Skip to content

Commit 2cbab88

Browse files
chore: Extract a "scroller" Twig component
1 parent 5733d53 commit 2cbab88

4 files changed

Lines changed: 147 additions & 186 deletions

File tree

src/views/collections/followers/edit.html.twig

Lines changed: 11 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -80,12 +80,7 @@
8080
{% if form.streams %}
8181
<div
8282
class="streams-selector flow flow--smaller"
83-
data-controller="selection filter scroller"
84-
data-scroller-vertical-value="true"
85-
data-action="
86-
resize@window->scroller#refresh
87-
filter:filtered->scroller#refresh
88-
"
83+
data-controller="selection filter"
8984
>
9085
{% if form.streams | length > 10 %}
9186
<input
@@ -102,27 +97,14 @@
10297
>
10398
{% endif %}
10499

105-
<div class="scroller scroller--vertical" data-scroller-target="viewport">
106-
<button
107-
type="button"
108-
class="button--icon scroller__button scroller__button--previous"
109-
title="{{ t('Display the previous streams') }}"
110-
data-scroller-target="previous"
111-
data-action="scroller#previous"
112-
disabled
113-
>
114-
{{ icon('arrow-left', 'rotate90') }}
115-
116-
<span class="sr-only">
117-
{{ t('Display the previous streams') }}
118-
</span>
119-
</button>
120-
121-
<div
122-
class="scroller__strip scroller__strip--vertical streams-selector__options stack stack--gap-small"
123-
data-scroller-target="strip"
124-
data-action="scroll->scroller#refresh"
125-
>
100+
{% embed 'components/scroller.html.twig' with {
101+
previous_label: t('Display the previous streams'),
102+
next_label: t('Display the next streams'),
103+
strip_class: 'streams-selector__options stack stack--gap-small',
104+
vertical: true,
105+
actions: 'filter:filtered@document->scroller#refresh',
106+
} %}
107+
{% block strip %}
126108
{% for stream in form.streams %}
127109
<div
128110
class="streams-selector__option"
@@ -156,23 +138,8 @@
156138
</label>
157139
</div>
158140
{% endfor %}
159-
</div>
160-
161-
<button
162-
type="button"
163-
class="button--icon scroller__button scroller__button--next"
164-
title="{{ t('Display the next streams') }}"
165-
data-scroller-target="next"
166-
data-action="scroller#next"
167-
disabled
168-
>
169-
{{ icon('arrow-right', 'rotate90') }}
170-
171-
<span class="sr-only">
172-
{{ t('Display the next streams') }}
173-
</span>
174-
</button>
175-
</div>
141+
{% endblock %}
142+
{% endembed %}
176143

177144
<p class="text--center text--secondary" data-filter-target="empty" hidden>
178145
{{ t('No stream matches your search.') }}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
{#
2+
# A scrollable strip with previous/next buttons, to be used with {% embed %}:
3+
#
4+
# {% embed 'components/scroller.html.twig' with {
5+
# previous_label: t('Display the previous views'),
6+
# next_label: t('Display the next views'),
7+
# strip_class: 'views__strip',
8+
# strip_label: t('Views'),
9+
# } %}
10+
# {% block strip %}
11+
# …
12+
# {% endblock %}
13+
# {% endembed %}
14+
#
15+
# The component declares the `scroller` Stimulus controller itself, and
16+
# refreshes it on the events that may resize the strip (window resize, Turbo
17+
# morphing). `actions` binds extra site-specific actions on it: as the
18+
# controller lives on the component, the events dispatched by the ancestors
19+
# must be listened at the document level (e.g. `filter:filtered@document->scroller#refresh`).
20+
#
21+
# `class` and `strip_class` add classes to the viewport and to the strip.
22+
# `strip_label` exposes the strip as a labelled group. `navigable` moves the
23+
# focus with the arrow keys: the items must then declare
24+
# `data-scroller-target="item"` and handle a roving tabindex (cf.
25+
# scroller_controller.js).
26+
#}
27+
{% types {
28+
previous_label: 'string',
29+
next_label: 'string',
30+
class: 'string',
31+
strip_class: 'string',
32+
strip_label: 'string',
33+
vertical: 'boolean',
34+
navigable: 'boolean',
35+
actions: 'string',
36+
} %}
37+
38+
{% set class = class ?? '' %}
39+
{% set strip_class = strip_class ?? '' %}
40+
{% set strip_label = strip_label ?? '' %}
41+
{% set vertical = vertical ?? false %}
42+
{% set navigable = navigable ?? false %}
43+
{% set actions = actions ?? '' %}
44+
45+
<div
46+
class="scroller {{ vertical ? 'scroller--vertical' }} {{ class }}"
47+
data-controller="scroller"
48+
{% if vertical %}
49+
data-scroller-vertical-value="true"
50+
{% endif %}
51+
data-scroller-target="viewport"
52+
data-action="
53+
resize@window->scroller#refresh
54+
turbo:morph@document->scroller#refresh
55+
{{ actions }}
56+
"
57+
>
58+
<button
59+
type="button"
60+
class="button--icon scroller__button scroller__button--previous"
61+
title="{{ previous_label }}"
62+
data-scroller-target="previous"
63+
data-action="scroller#previous"
64+
disabled
65+
>
66+
{{ icon('arrow-left', vertical ? 'rotate90' : '') }}
67+
68+
<span class="sr-only">
69+
{{ previous_label }}
70+
</span>
71+
</button>
72+
73+
<div
74+
class="scroller__strip {{ vertical ? 'scroller__strip--vertical' }} {{ strip_class }}"
75+
{% if strip_label %}
76+
role="group"
77+
aria-label="{{ strip_label }}"
78+
{% endif %}
79+
data-scroller-target="strip"
80+
data-action="
81+
{{ navigable ? 'keydown->scroller#navigate' }}
82+
scroll->scroller#refresh
83+
"
84+
>
85+
{% block strip %}{% endblock %}
86+
</div>
87+
88+
<button
89+
type="button"
90+
class="button--icon scroller__button scroller__button--next"
91+
title="{{ next_label }}"
92+
data-scroller-target="next"
93+
data-action="scroller#next"
94+
disabled
95+
>
96+
{{ icon('arrow-right', vertical ? 'rotate90' : '') }}
97+
98+
<span class="sr-only">
99+
{{ next_label }}
100+
</span>
101+
</button>
102+
</div>

src/views/streams/_views.html.twig

Lines changed: 23 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,13 @@
11
<div class="flow flow--smaller">
2-
<div
3-
class="views"
4-
data-controller="scroller"
5-
data-action="
6-
resize@window->scroller#refresh
7-
turbo:morph@document->scroller#refresh
8-
"
9-
>
10-
<div class="scroller views__scroller" data-scroller-target="viewport">
11-
<button
12-
type="button"
13-
class="button--icon scroller__button scroller__button--previous"
14-
title="{{ t('Display the previous views') }}"
15-
data-scroller-target="previous"
16-
data-action="scroller#previous"
17-
disabled
18-
>
19-
{{ icon('arrow-left') }}
20-
21-
<span class="sr-only">
22-
{{ t('Display the previous views') }}
23-
</span>
24-
</button>
25-
26-
<div
27-
class="scroller__strip views__strip"
28-
role="group"
29-
aria-label="{{ t('Views') }}"
30-
data-scroller-target="strip"
31-
data-action="scroll->scroller#refresh"
32-
>
2+
<div class="views">
3+
{% embed 'components/scroller.html.twig' with {
4+
previous_label: t('Display the previous views'),
5+
next_label: t('Display the next views'),
6+
class: 'views__scroller',
7+
strip_class: 'views__strip',
8+
strip_label: t('Views'),
9+
} %}
10+
{% block strip %}
3311
{% for view in stream.views({ context_user: app.user }) %}
3412
<a
3513
id="stream-view-{{ view.routeId }}"
@@ -55,23 +33,8 @@
5533
{{ icon('plus') }}
5634
{{ t('New view') }}
5735
</button>
58-
</div>
59-
60-
<button
61-
type="button"
62-
class="button--icon scroller__button scroller__button--next"
63-
title="{{ t('Display the next views') }}"
64-
data-scroller-target="next"
65-
data-action="scroller#next"
66-
disabled
67-
>
68-
{{ icon('arrow-right') }}
69-
70-
<span class="sr-only">
71-
{{ t('Display the next views') }}
72-
</span>
73-
</button>
74-
</div>
36+
{% endblock %}
37+
{% endembed %}
7538

7639
{% embed 'components/popup.html.twig' with {
7740
position: 'right',
@@ -133,15 +96,7 @@
13396
<span class="col--extend text--bold">{{ t('View filters') }}</span>
13497
</summary>
13598

136-
<div
137-
class="filters__section flow"
138-
data-controller="scroller"
139-
data-action="
140-
resize@window->scroller#refresh
141-
turbo:morph@document->scroller#refresh
142-
details:opened@document->scroller#reveal
143-
"
144-
>
99+
<div class="filters__section flow">
145100
{% set max_count_per_day = stream_view.maxCountPerDay() %}
146101

147102
{# Number of days displayed per month, used to cap the width of each
@@ -153,32 +108,15 @@
153108
{% set count_days_per_month = count_days_per_month | merge({ (month_key): count + 1 }) %}
154109
{% endfor %}
155110

156-
<div class="scroller" data-scroller-target="viewport">
157-
<button
158-
type="button"
159-
class="button--icon scroller__button scroller__button--previous"
160-
title="{{ t('Display the previous days') }}"
161-
data-scroller-target="previous"
162-
data-action="scroller#previous"
163-
disabled
164-
>
165-
{{ icon('arrow-left') }}
166-
167-
<span class="sr-only">
168-
{{ t('Display the previous days') }}
169-
</span>
170-
</button>
171-
172-
<div
173-
class="scroller__strip day-picker"
174-
role="group"
175-
aria-label="{{ t('Date') }}"
176-
data-scroller-target="strip"
177-
data-action="
178-
keydown->scroller#navigate
179-
scroll->scroller#refresh
180-
"
181-
>
111+
{% embed 'components/scroller.html.twig' with {
112+
previous_label: t('Display the previous days'),
113+
next_label: t('Display the next days'),
114+
strip_class: 'day-picker',
115+
strip_label: t('Date'),
116+
navigable: true,
117+
actions: 'details:opened@document->scroller#reveal',
118+
} %}
119+
{% block strip %}
182120
{% for day in stream_view.period %}
183121
{% set day_value = day.format('Y-m-d') %}
184122
{% set is_month_first_day = loop.first or day.format('Y-m') != stream_view.period[loop.index0 - 1].format('Y-m') %}
@@ -248,23 +186,8 @@
248186
</span>
249187
</button>
250188
{% endfor %}
251-
</div>
252-
253-
<button
254-
type="button"
255-
class="button--icon scroller__button scroller__button--next"
256-
title="{{ t('Display the next days') }}"
257-
data-scroller-target="next"
258-
data-action="scroller#next"
259-
disabled
260-
>
261-
{{ icon('arrow-right') }}
262-
263-
<span class="sr-only">
264-
{{ t('Display the next days') }}
265-
</span>
266-
</button>
267-
</div>
189+
{% endblock %}
190+
{% endembed %}
268191

269192
<div
270193
class="stack stack--gap-small"

0 commit comments

Comments
 (0)