stimeo-ui 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +184 -0
- package/dist/controllers/announcer_controller.js.map +1 -1
- package/dist/controllers/aspect_ratio_controller.d.ts +3 -5
- package/dist/controllers/aspect_ratio_controller.js +19 -11
- package/dist/controllers/aspect_ratio_controller.js.map +1 -1
- package/dist/controllers/avatar_controller.d.ts +36 -15
- package/dist/controllers/avatar_controller.js +195 -40
- package/dist/controllers/avatar_controller.js.map +1 -1
- package/dist/controllers/breadcrumb_controller.js +5 -1
- package/dist/controllers/breadcrumb_controller.js.map +1 -1
- package/dist/controllers/carousel_controller.d.ts +12 -3
- package/dist/controllers/carousel_controller.js +90 -10
- package/dist/controllers/carousel_controller.js.map +1 -1
- package/dist/controllers/checkbox_controller.d.ts +31 -5
- package/dist/controllers/checkbox_controller.js +136 -25
- package/dist/controllers/checkbox_controller.js.map +1 -1
- package/dist/controllers/clipboard_controller.js +8 -3
- package/dist/controllers/clipboard_controller.js.map +1 -1
- package/dist/controllers/collapsible_controller.d.ts +1 -1
- package/dist/controllers/collapsible_controller.js +4 -1
- package/dist/controllers/collapsible_controller.js.map +1 -1
- package/dist/controllers/color_picker_controller.d.ts +9 -3
- package/dist/controllers/color_picker_controller.js +41 -11
- package/dist/controllers/color_picker_controller.js.map +1 -1
- package/dist/controllers/combobox_controller.js.map +1 -1
- package/dist/controllers/command_palette_controller.js.map +1 -1
- package/dist/controllers/context_menu_controller.d.ts +1 -1
- package/dist/controllers/context_menu_controller.js +2 -2
- package/dist/controllers/context_menu_controller.js.map +1 -1
- package/dist/controllers/countdown_controller.d.ts +5 -3
- package/dist/controllers/countdown_controller.js +5 -1
- package/dist/controllers/countdown_controller.js.map +1 -1
- package/dist/controllers/date_range_picker_controller.d.ts +23 -4
- package/dist/controllers/date_range_picker_controller.js +162 -31
- package/dist/controllers/date_range_picker_controller.js.map +1 -1
- package/dist/controllers/direct_upload_controller.d.ts +1 -1
- package/dist/controllers/direct_upload_controller.js +3 -3
- package/dist/controllers/direct_upload_controller.js.map +1 -1
- package/dist/controllers/empty_state_controller.d.ts +27 -7
- package/dist/controllers/empty_state_controller.js +107 -16
- package/dist/controllers/empty_state_controller.js.map +1 -1
- package/dist/controllers/file_dropzone_controller.d.ts +7 -1
- package/dist/controllers/file_dropzone_controller.js +26 -3
- package/dist/controllers/file_dropzone_controller.js.map +1 -1
- package/dist/controllers/flash_controller.d.ts +25 -5
- package/dist/controllers/flash_controller.js +161 -21
- package/dist/controllers/flash_controller.js.map +1 -1
- package/dist/controllers/focus_controller.d.ts +2 -3
- package/dist/controllers/focus_controller.js.map +1 -1
- package/dist/controllers/form_validation_controller.js +8 -2
- package/dist/controllers/form_validation_controller.js.map +1 -1
- package/dist/controllers/frame_loading_controller.d.ts +19 -2
- package/dist/controllers/frame_loading_controller.js +94 -22
- package/dist/controllers/frame_loading_controller.js.map +1 -1
- package/dist/controllers/highlight_controller.d.ts +8 -4
- package/dist/controllers/highlight_controller.js +38 -1
- package/dist/controllers/highlight_controller.js.map +1 -1
- package/dist/controllers/hover_card_controller.d.ts +2 -2
- package/dist/controllers/hover_card_controller.js.map +1 -1
- package/dist/controllers/idle_controller.d.ts +5 -4
- package/dist/controllers/idle_controller.js +39 -6
- package/dist/controllers/idle_controller.js.map +1 -1
- package/dist/controllers/listbox_controller.js.map +1 -1
- package/dist/controllers/local_time_controller.js +2 -0
- package/dist/controllers/local_time_controller.js.map +1 -1
- package/dist/controllers/masonry_controller.d.ts +1 -1
- package/dist/controllers/masonry_controller.js +1 -1
- package/dist/controllers/masonry_controller.js.map +1 -1
- package/dist/controllers/menubar_controller.js +5 -3
- package/dist/controllers/menubar_controller.js.map +1 -1
- package/dist/controllers/meter_controller.js +3 -1
- package/dist/controllers/meter_controller.js.map +1 -1
- package/dist/controllers/multi_select_controller.d.ts +46 -12
- package/dist/controllers/multi_select_controller.js +460 -151
- package/dist/controllers/multi_select_controller.js.map +1 -1
- package/dist/controllers/network_status_controller.d.ts +15 -13
- package/dist/controllers/network_status_controller.js +1 -3
- package/dist/controllers/network_status_controller.js.map +1 -1
- package/dist/controllers/number_input_controller.d.ts +29 -3
- package/dist/controllers/number_input_controller.js +455 -64
- package/dist/controllers/number_input_controller.js.map +1 -1
- package/dist/controllers/overflow_menu_controller.js +5 -1
- package/dist/controllers/overflow_menu_controller.js.map +1 -1
- package/dist/controllers/pagination_controller.js +38 -1
- package/dist/controllers/pagination_controller.js.map +1 -1
- package/dist/controllers/password_strength_controller.d.ts +4 -4
- package/dist/controllers/password_strength_controller.js +21 -3
- package/dist/controllers/password_strength_controller.js.map +1 -1
- package/dist/controllers/persist_controller.d.ts +4 -4
- package/dist/controllers/persist_controller.js +24 -5
- package/dist/controllers/persist_controller.js.map +1 -1
- package/dist/controllers/pointer_drag_controller.js +10 -0
- package/dist/controllers/pointer_drag_controller.js.map +1 -1
- package/dist/controllers/popover_controller.d.ts +2 -2
- package/dist/controllers/popover_controller.js.map +1 -1
- package/dist/controllers/portal_controller.d.ts +1 -2
- package/dist/controllers/portal_controller.js +10 -0
- package/dist/controllers/portal_controller.js.map +1 -1
- package/dist/controllers/progress_controller.d.ts +1 -1
- package/dist/controllers/progress_controller.js +7 -3
- package/dist/controllers/progress_controller.js.map +1 -1
- package/dist/controllers/radio_group_controller.d.ts +41 -15
- package/dist/controllers/radio_group_controller.js +540 -56
- package/dist/controllers/radio_group_controller.js.map +1 -1
- package/dist/controllers/range_slider_controller.d.ts +23 -0
- package/dist/controllers/range_slider_controller.js +385 -94
- package/dist/controllers/range_slider_controller.js.map +1 -1
- package/dist/controllers/rating_controller.d.ts +36 -31
- package/dist/controllers/rating_controller.js +274 -89
- package/dist/controllers/rating_controller.js.map +1 -1
- package/dist/controllers/relative_time_controller.js +2 -0
- package/dist/controllers/relative_time_controller.js.map +1 -1
- package/dist/controllers/resizable_controller.js +33 -0
- package/dist/controllers/resizable_controller.js.map +1 -1
- package/dist/controllers/roving_controller.d.ts +4 -0
- package/dist/controllers/roving_controller.js +60 -5
- package/dist/controllers/roving_controller.js.map +1 -1
- package/dist/controllers/scroll_area_controller.d.ts +7 -1
- package/dist/controllers/scroll_area_controller.js +155 -23
- package/dist/controllers/scroll_area_controller.js.map +1 -1
- package/dist/controllers/scroll_visibility_controller.js +33 -0
- package/dist/controllers/scroll_visibility_controller.js.map +1 -1
- package/dist/controllers/separator_controller.js +13 -17
- package/dist/controllers/separator_controller.js.map +1 -1
- package/dist/controllers/skeleton_controller.d.ts +2 -2
- package/dist/controllers/skeleton_controller.js +71 -3
- package/dist/controllers/skeleton_controller.js.map +1 -1
- package/dist/controllers/slider_controller.d.ts +17 -1
- package/dist/controllers/slider_controller.js +325 -48
- package/dist/controllers/slider_controller.js.map +1 -1
- package/dist/controllers/spinner_controller.d.ts +15 -10
- package/dist/controllers/spinner_controller.js +18 -3
- package/dist/controllers/spinner_controller.js.map +1 -1
- package/dist/controllers/step_indicator_controller.d.ts +1 -1
- package/dist/controllers/step_indicator_controller.js +3 -1
- package/dist/controllers/step_indicator_controller.js.map +1 -1
- package/dist/controllers/stepper_controller.js +2 -0
- package/dist/controllers/stepper_controller.js.map +1 -1
- package/dist/controllers/switch_controller.d.ts +12 -8
- package/dist/controllers/switch_controller.js +162 -18
- package/dist/controllers/switch_controller.js.map +1 -1
- package/dist/controllers/tags_input_controller.d.ts +41 -11
- package/dist/controllers/tags_input_controller.js +356 -120
- package/dist/controllers/tags_input_controller.js.map +1 -1
- package/dist/controllers/textarea_autosize_controller.js +1 -1
- package/dist/controllers/textarea_autosize_controller.js.map +1 -1
- package/dist/controllers/time_picker_controller.d.ts +42 -10
- package/dist/controllers/time_picker_controller.js +296 -104
- package/dist/controllers/time_picker_controller.js.map +1 -1
- package/dist/controllers/toggle_group_controller.d.ts +39 -13
- package/dist/controllers/toggle_group_controller.js +378 -55
- package/dist/controllers/toggle_group_controller.js.map +1 -1
- package/dist/controllers/toolbar_controller.js +5 -3
- package/dist/controllers/toolbar_controller.js.map +1 -1
- package/dist/controllers/tooltip_controller.d.ts +2 -2
- package/dist/controllers/tooltip_controller.js.map +1 -1
- package/dist/controllers/transition_controller.d.ts +2 -3
- package/dist/controllers/transition_controller.js.map +1 -1
- package/dist/controllers/tree_view_controller.d.ts +1 -2
- package/dist/controllers/tree_view_controller.js +24 -4
- package/dist/controllers/tree_view_controller.js.map +1 -1
- package/dist/index.js +3811 -1048
- package/dist/index.js.map +1 -1
- package/dist/inspector/cli.d.ts +92 -4
- package/dist/inspector/cli.js +174 -6
- package/dist/inspector/cli.js.map +1 -1
- package/dist/inspector/cli_bin.js +234 -8
- package/dist/inspector/cli_bin.js.map +1 -1
- package/dist/inspector/examples.json +24 -24
- package/dist/inspector/manifest.json +573 -59
- package/dist/positioning/index.d.ts +2 -2
- package/dist/positioning/index.js.map +1 -1
- package/package.json +3 -3
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
},
|
|
20
20
|
"stimeo--aspect-ratio": {
|
|
21
21
|
"file": "examples/aspect_ratio/_demo.html.erb",
|
|
22
|
-
"source": "<%# Markup for the aspect-ratio demo.\n The library only supplies the ratio Value as the --stimeo
|
|
22
|
+
"source": "<%# Markup for the aspect-ratio demo.\n The library only supplies the ratio Value as the --stimeo--aspect-ratio custom\n property; demo.css does the actual ratio rendering\n (aspect-ratio: var(--stimeo--aspect-ratio)). %>\n<div class=\"aspect-ratio-demo\">\n <figure class=\"aspect-ratio-demo__item\">\n <div class=\"ratio-box\" data-controller=\"stimeo--aspect-ratio\"\n data-stimeo--aspect-ratio-ratio-value=\"16/9\">\n <div class=\"ratio-box__content\">16 / 9</div>\n </div>\n <figcaption><%= t(\"components.aspect_ratio.demo.wide\") %></figcaption>\n </figure>\n\n <figure class=\"aspect-ratio-demo__item\">\n <div class=\"ratio-box\" data-controller=\"stimeo--aspect-ratio\"\n data-stimeo--aspect-ratio-ratio-value=\"1/1\">\n <div class=\"ratio-box__content\">1 / 1</div>\n </div>\n <figcaption><%= t(\"components.aspect_ratio.demo.square\") %></figcaption>\n </figure>\n\n <figure class=\"aspect-ratio-demo__item\">\n <div class=\"ratio-box\" data-controller=\"stimeo--aspect-ratio\"\n data-stimeo--aspect-ratio-ratio-value=\"3/4\">\n <div class=\"ratio-box__content\">3 / 4</div>\n </div>\n <figcaption><%= t(\"components.aspect_ratio.demo.portrait\") %></figcaption>\n </figure>\n</div>\n"
|
|
23
23
|
},
|
|
24
24
|
"stimeo--auto-submit": {
|
|
25
25
|
"file": "examples/auto_submit/_demo.html.erb",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
},
|
|
28
28
|
"stimeo--avatar": {
|
|
29
29
|
"file": "examples/avatar/_demo.html.erb",
|
|
30
|
-
"source": "<%# Markup for the avatar demo.\n The accessible name is owned solely by the container (role=\"img\" + aria-label);\n the inner <img> and fallback use aria-hidden=\"true\" to avoid double announcement.\n The library only
|
|
30
|
+
"source": "<%# Markup for the avatar demo.\n The accessible name is owned solely by the container (role=\"img\" + aria-label);\n the inner <img> and fallback use aria-hidden=\"true\" to avoid double announcement.\n The library only tracks the source and its load state, exposing empty / loading /\n loaded / error on data-state; shape, color, and size are drawn by demo.css. %>\n<div class=\"avatar-demo\">\n <%# 1. An avatar that loads successfully. The src is a self-contained data-URI SVG that\n draws a person silhouette (a picture, not letters) so the \"loaded image\" state reads\n clearly different from the initials fallback below. %>\n <figure class=\"avatar-demo__item\">\n <span\n class=\"avatar\"\n data-controller=\"stimeo--avatar\"\n role=\"img\"\n aria-label=\"<%= t(\"components.avatar.demo.name_ok\") %>\"\n data-stimeo--avatar-src-value=\"data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='96'%20height='96'%3E%3Cdefs%3E%3ClinearGradient%20id='g'%20x1='0'%20y1='0'%20x2='0'%20y2='1'%3E%3Cstop%20offset='0'%20stop-color='%236366f1'/%3E%3Cstop%20offset='1'%20stop-color='%234338ca'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect%20width='96'%20height='96'%20fill='url(%23g)'/%3E%3Ccircle%20cx='48'%20cy='38'%20r='17'%20fill='%23eef2ff'/%3E%3Cpath%20d='M20%2084%20a28%2028%200%200%201%2056%200%20Z'%20fill='%23eef2ff'/%3E%3C/svg%3E\">\n <img\n class=\"avatar__image\"\n alt=\"\"\n aria-hidden=\"true\"\n data-stimeo--avatar-target=\"image\"\n data-action=\"load->stimeo--avatar#onLoad error->stimeo--avatar#onError\">\n <span\n class=\"avatar__fallback\"\n aria-hidden=\"true\"\n hidden\n data-stimeo--avatar-target=\"fallback\"\n >AU</span>\n </span>\n <figcaption><%= t(\"components.avatar.demo.caption_ok\") %></figcaption>\n </figure>\n\n <%# 2. An avatar that fails to load (nonexistent path → fallback initials). The browser\n logs an expected 404 for the missing image — that is the point: it triggers the error\n → fallback path the library handles. %>\n <figure class=\"avatar-demo__item\">\n <span\n class=\"avatar\"\n data-controller=\"stimeo--avatar\"\n role=\"img\"\n aria-label=\"<%= t(\"components.avatar.demo.name_fallback\") %>\"\n data-stimeo--avatar-src-value=\"/this-image-does-not-exist.jpg\">\n <img\n class=\"avatar__image\"\n alt=\"\"\n aria-hidden=\"true\"\n data-stimeo--avatar-target=\"image\"\n data-action=\"load->stimeo--avatar#onLoad error->stimeo--avatar#onError\">\n <span\n class=\"avatar__fallback\"\n aria-hidden=\"true\"\n hidden\n data-stimeo--avatar-target=\"fallback\"\n >JD</span>\n </span>\n <figcaption><%= t(\"components.avatar.demo.caption_fallback\") %></figcaption>\n </figure>\n\n <%# 3. An avatar with no image target at all. Both targets are optional, so a\n fallback-only avatar is valid and settles in the empty state. %>\n <figure class=\"avatar-demo__item\">\n <span\n class=\"avatar\"\n data-controller=\"stimeo--avatar\"\n role=\"img\"\n aria-label=\"<%= t(\"components.avatar.demo.name_empty\") %>\">\n <span\n class=\"avatar__fallback\"\n aria-hidden=\"true\"\n data-stimeo--avatar-target=\"fallback\"\n >GU</span>\n </span>\n <figcaption><%= t(\"components.avatar.demo.caption_empty\") %></figcaption>\n </figure>\n</div>\n"
|
|
31
31
|
},
|
|
32
32
|
"stimeo--breadcrumb": {
|
|
33
33
|
"file": "examples/breadcrumb/_demo.html.erb",
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
},
|
|
64
64
|
"stimeo--color-picker": {
|
|
65
65
|
"file": "examples/color_picker/_demo.html.erb",
|
|
66
|
-
"source": "<%# Markup for the color-picker demo.\n The library manages each channel's (hue / saturation / lightness) role=\"slider\"\n value, composes them into a color two-way-synced with the hex input, and updates\n --stimeo
|
|
66
|
+
"source": "<%# Markup for the color-picker demo.\n The library manages each channel's (hue / saturation / lightness) role=\"slider\"\n value, composes them into a color two-way-synced with the hex input, and updates\n --stimeo--color for the preview. The 2D palette is decomposed into independent 1D\n sliders for accessibility. Thumb position and look are the consumer's CSS. %>\n<div\n class=\"color-picker\"\n data-controller=\"stimeo--color-picker\"\n data-stimeo--color-picker-value-value=\"#3366cc\">\n <div\n class=\"color-picker__preview\"\n data-stimeo--color-picker-target=\"preview\"\n aria-hidden=\"true\"></div>\n\n <div class=\"color-picker__channels\">\n <% { hue: [t(\"components.color_picker.demo.hue\"), 360, 210],\n saturation: [t(\"components.color_picker.demo.saturation\"), 100, 60],\n lightness: [t(\"components.color_picker.demo.lightness\"), 100, 50] }\n .each do |channel, (label, max, now)| %>\n <div class=\"color-picker__channel\">\n <span class=\"color-picker__channel-label\"><%= label %></span>\n <div\n class=\"color-picker__track color-picker__track--<%= channel %>\"\n role=\"slider\"\n aria-label=\"<%= label %>\"\n data-channel=\"<%= channel %>\"\n tabindex=\"0\"\n aria-valuemin=\"0\"\n aria-valuemax=\"<%= max %>\"\n aria-valuenow=\"<%= now %>\"\n data-stimeo--color-picker-target=\"slider\"\n data-action=\"\n keydown->stimeo--color-picker#onKeydown\n pointerdown->stimeo--color-picker#onPointerDown\">\n <span class=\"color-picker__thumb\" aria-hidden=\"true\"></span>\n </div>\n </div>\n <% end %>\n </div>\n\n <label class=\"color-picker__hex\">\n <span><%= t(\"components.color_picker.demo.hex\") %></span>\n <input\n type=\"text\"\n inputmode=\"text\"\n aria-label=\"<%= t(\"components.color_picker.demo.hex\") %>\"\n value=\"#3366cc\"\n data-stimeo--color-picker-target=\"hex\"\n data-action=\"change->stimeo--color-picker#onHexInput\" />\n </label>\n\n <input type=\"hidden\" name=\"brand_color\" data-stimeo--color-picker-target=\"field\" />\n</div>\n"
|
|
67
67
|
},
|
|
68
68
|
"stimeo--combobox": {
|
|
69
69
|
"file": "examples/combobox/_demo.html.erb",
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
},
|
|
84
84
|
"stimeo--context-menu": {
|
|
85
85
|
"file": "examples/context_menu/_demo.html.erb",
|
|
86
|
-
"source": "<%# Markup for the context_menu demo.\n A right-click on the region (or the ContextMenu key / Shift+F10) opens a\n role=\"menu\" at the pointer position. The interaction model is the same as a Menu\n Button. The click coordinate is reflected on the menu as\n --stimeo
|
|
86
|
+
"source": "<%# Markup for the context_menu demo.\n A right-click on the region (or the ContextMenu key / Shift+F10) opens a\n role=\"menu\" at the pointer position. The interaction model is the same as a Menu\n Button. The click coordinate is reflected on the menu as\n --stimeo--context-menu-x / -y, and the Playground CSS uses it for top/left\n placement (works even without the positioning module). %>\n<div class=\"context-menu\" data-controller=\"stimeo--context-menu\">\n <div\n class=\"context-menu__region\"\n data-stimeo--context-menu-target=\"region\"\n tabindex=\"0\"\n aria-haspopup=\"menu\"\n aria-controls=\"context-menu-list\"\n data-action=\"\n contextmenu->stimeo--context-menu#open\n keydown->stimeo--context-menu#onRegionKeydown\">\n <%= t(\"components.context_menu.demo.region\") %>\n </div>\n\n <ul\n class=\"context-menu__list\"\n id=\"context-menu-list\"\n role=\"menu\"\n aria-label=\"<%= t(\"components.context_menu.demo.label\") %>\"\n data-stimeo--context-menu-target=\"menu\"\n hidden>\n <li role=\"none\">\n <button type=\"button\" class=\"demo-menuitem\" role=\"menuitem\" tabindex=\"-1\"\n data-stimeo--context-menu-target=\"item\"\n data-action=\"\n click->stimeo--context-menu#activate\n keydown->stimeo--context-menu#onItemKeydown\">\n <%= t(\"components.context_menu.demo.copy\") %>\n </button>\n </li>\n <li role=\"none\">\n <button type=\"button\" class=\"demo-menuitem\" role=\"menuitem\" tabindex=\"-1\"\n data-stimeo--context-menu-target=\"item\"\n data-action=\"\n click->stimeo--context-menu#activate\n keydown->stimeo--context-menu#onItemKeydown\">\n <%= t(\"components.context_menu.demo.paste\") %>\n </button>\n </li>\n <li role=\"none\">\n <button type=\"button\" class=\"demo-menuitem\" role=\"menuitem\" tabindex=\"-1\"\n data-stimeo--context-menu-target=\"item\"\n data-action=\"\n click->stimeo--context-menu#activate\n keydown->stimeo--context-menu#onItemKeydown\">\n <%= t(\"components.context_menu.demo.delete\") %>\n </button>\n </li>\n </ul>\n</div>\n"
|
|
87
87
|
},
|
|
88
88
|
"stimeo--count-up": {
|
|
89
89
|
"file": "examples/count_up/_demo.html.erb",
|
|
@@ -91,7 +91,7 @@
|
|
|
91
91
|
},
|
|
92
92
|
"stimeo--countdown": {
|
|
93
93
|
"file": "examples/countdown/_demo.html.erb",
|
|
94
|
-
"source": "<%# Markup for the countdown (timer) demo.\n Fills the days/hours/minutes/seconds slots with the time remaining until the\n deadline, updating every interval (default 1000ms). role=\"timer\" + aria-live=\"off\"\n avoids per-second announcements;
|
|
94
|
+
"source": "<%# Markup for the countdown (timer) demo.\n Fills the days/hours/minutes/seconds slots with the time remaining until the\n deadline, updating every interval (default 1000ms). role=\"timer\" + aria-live=\"off\"\n avoids per-second announcements; the status slot carries the completion text\n visually and the shared announcer reads it out, so it is said exactly once.\n The deadline is computed in ERB to always be in the future.\n The pause/resume/reset buttons sit outside the timer (a role=\"timer\" live region\n should not wrap unrelated controls), so they drive it through the countdown:* events\n the controller listens for (see demo.js) instead of a Stimulus data-action, which\n only binds within the controller's own element.\n This demo behaves as a *duration* timer (a fixed length counted from \"now\"): the\n deadline is now + the duration below, and Reset re-anchors it to now + the same\n duration so the clock returns to the full time. The library only counts down to\n whatever deadline it is given; demo.js owns that duration→deadline policy and exposes\n the length via data-countdown-duration-ms. %>\n<% countdown_duration = 1.hour %>\n<div class=\"countdown-demo\" data-countdown-duration-ms=\"<%= countdown_duration.to_i * 1000 %>\">\n <div\n class=\"countdown\"\n data-controller=\"stimeo--countdown\"\n role=\"timer\"\n aria-live=\"off\"\n data-stimeo--countdown-deadline-value=\"<%= countdown_duration.from_now.iso8601 %>\"\n data-stimeo--countdown-complete-label-value=\"<%= t('components.countdown.demo.complete') %>\"\n data-stimeo--countdown-announce-text-value=\"<%= t('components.countdown.demo.complete') %>\"\n data-action=\"\n countdown:pause->stimeo--countdown#pause\n countdown:resume->stimeo--countdown#resume\n countdown:reset->stimeo--countdown#reset\">\n <span class=\"countdown__unit\">\n <span class=\"countdown__value\" data-stimeo--countdown-target=\"days\">0</span>\n <span class=\"countdown__label\"><%= t(\"components.countdown.demo.days\") %></span>\n </span>\n <span class=\"countdown__unit\">\n <span class=\"countdown__value\" data-stimeo--countdown-target=\"hours\">00</span>\n <span class=\"countdown__label\"><%= t(\"components.countdown.demo.hours\") %></span>\n </span>\n <span class=\"countdown__unit\">\n <span class=\"countdown__value\" data-stimeo--countdown-target=\"minutes\">00</span>\n <span class=\"countdown__label\"><%= t(\"components.countdown.demo.minutes\") %></span>\n </span>\n <span class=\"countdown__unit\">\n <span class=\"countdown__value\" data-stimeo--countdown-target=\"seconds\">00</span>\n <span class=\"countdown__label\"><%= t(\"components.countdown.demo.seconds\") %></span>\n </span>\n <%# Visual only: the announcer says this once, so a live region here would repeat it. %>\n <span class=\"countdown__status\" data-stimeo--countdown-target=\"status\"></span>\n </div>\n\n <div class=\"countdown-demo__controls\">\n <button class=\"demo-trigger\" type=\"button\" data-countdown-pause>\n <%= t(\"components.countdown.demo.pause\") %>\n </button>\n <button class=\"demo-trigger\" type=\"button\" data-countdown-resume>\n <%= t(\"components.countdown.demo.resume\") %>\n </button>\n <button class=\"demo-trigger\" type=\"button\" data-countdown-reset>\n <%= t(\"components.countdown.demo.reset\") %>\n </button>\n </div>\n\n <%# Shared announcer: reaching zero is the transition worth reading; the ticking\n numbers in between are state the reader can query. %>\n <div data-controller=\"stimeo--announcer\">\n <div class=\"visually-hidden\"\n data-stimeo--announcer-target=\"polite\" aria-live=\"polite\" aria-atomic=\"true\"></div>\n </div>\n</div>\n"
|
|
95
95
|
},
|
|
96
96
|
"stimeo--currency-input": {
|
|
97
97
|
"file": "examples/currency_input/_demo.html.erb",
|
|
@@ -103,7 +103,7 @@
|
|
|
103
103
|
},
|
|
104
104
|
"stimeo--date-range-picker": {
|
|
105
105
|
"file": "examples/date_range_picker/_demo.html.erb",
|
|
106
|
-
"source": "<%# Markup for the date-range-picker (range-selection calendar) demo.\n stimeo--date-range-picker derives from calendar and handles two-point start/end\n selection, a provisional range preview mid-selection, marking in-range cells,\n auto-swapping when start > end, applying presets, and grid keyboard movement\n (roving). Range ends are shown with aria-selected and the inside with data-in-range;\n placement and styling live in demo.css. %>\n<div class=\"drp demo-card\" data-controller=\"stimeo--date-range-picker\">\n <div class=\"drp__header\">\n <button class=\"drp__nav\" type=\"button\"\n data-action=\"click->stimeo--date-range-picker#prev\"\n aria-label=\"<%= t(\"components.date_range_picker.demo.prev_month\") %>\">‹</button>\n <span class=\"drp__label\" id=\"drp-month\" aria-live=\"polite\"\n data-stimeo--date-range-picker-target=\"monthLabel\"></span>\n <button class=\"drp__nav\" type=\"button\"\n data-action=\"click->stimeo--date-range-picker#next\"\n aria-label=\"<%= t(\"components.date_range_picker.demo.next_month\") %>\">›</button>\n </div>\n\n <div class=\"drp__weekdays\" aria-hidden=\"true\">\n <% t(\"components.date_range_picker.demo.weekdays\").each do |day| %>\n <span class=\"drp__weekday\"><%= day %></span>\n <% end %>\n </div>\n\n <div class=\"drp__grid\" role=\"grid\" aria-labelledby=\"drp-month\"\n data-stimeo--date-range-picker-target=\"grid\">\n <% 6.times do %>\n <div class=\"drp__row\" role=\"row\">\n <% 7.times do %>\n <button class=\"drp__cell\" type=\"button\" role=\"gridcell\" tabindex=\"-1\"\n data-stimeo--date-range-picker-target=\"cell\"\n data-action=\"click->stimeo--date-range-picker#selectDate\n mouseenter->stimeo--date-range-picker#previewTo\n focus->stimeo--date-range-picker#previewTo\n keydown->stimeo--date-range-picker#onKeydown\"></button>\n <% end %>\n </div>\n <% end %>\n </div>\n\n <div class=\"drp__presets\" role=\"group\"\n aria-label=\"<%= t(\"components.date_range_picker.demo.presets_label\") %>\">\n <button class=\"drp__preset\" type=\"button\" data-range=\"today\"\n data-action=\"click->stimeo--date-range-picker#applyPreset\">\n <%= t(\"components.date_range_picker.demo.preset_today\") %>\n </button>\n <button class=\"drp__preset\" type=\"button\" data-range=\"last7\"\n data-action=\"click->stimeo--date-range-picker#applyPreset\">\n <%= t(\"components.date_range_picker.demo.preset_last7\") %>\n </button>\n <button class=\"drp__preset\" type=\"button\" data-range=\"thisMonth\"\n data-action=\"click->stimeo--date-range-picker#applyPreset\">\n <%= t(\"components.date_range_picker.demo.preset_this_month\") %>\n </button>\n </div>\n\n <span class=\"drp__status\" role=\"status\" aria-live=\"polite\"\n data-stimeo--date-range-picker-target=\"status\"></span>\n\n <input type=\"hidden\" name=\"start_date\" data-stimeo--date-range-picker-target=\"startField\" />\n <input type=\"hidden\" name=\"end_date\" data-stimeo--date-range-picker-target=\"endField\" />\n</div>\n"
|
|
106
|
+
"source": "<%# Markup for the date-range-picker (range-selection calendar) demo.\n stimeo--date-range-picker derives from calendar and handles two-point start/end\n selection, a provisional range preview mid-selection, marking in-range cells,\n auto-swapping when start > end, applying presets, and grid keyboard movement\n (roving). Range ends are shown with aria-selected and the inside with data-in-range;\n placement and styling live in demo.css.\n\n Two unavailable days are declared as data rather than written onto cells. The\n 42 cells are reused for a different date every month, so availability has to\n be keyed by the date; the controller derives each cell's aria-disabled from\n this array on every paint. A range may still span such a day — it just cannot\n end on one. %>\n<% blackout = [3.days.from_now, 4.days.from_now].map { |day| day.strftime(\"%Y-%m-%d\") } %>\n<div class=\"drp demo-card\" data-controller=\"stimeo--date-range-picker\"\n data-stimeo--date-range-picker-disabled-dates-value=\"<%= blackout.to_json %>\">\n <div class=\"drp__header\">\n <button class=\"drp__nav\" type=\"button\"\n data-action=\"click->stimeo--date-range-picker#prev\"\n aria-label=\"<%= t(\"components.date_range_picker.demo.prev_month\") %>\">‹</button>\n <span class=\"drp__label\" id=\"drp-month\" aria-live=\"polite\"\n data-stimeo--date-range-picker-target=\"monthLabel\"></span>\n <button class=\"drp__nav\" type=\"button\"\n data-action=\"click->stimeo--date-range-picker#next\"\n aria-label=\"<%= t(\"components.date_range_picker.demo.next_month\") %>\">›</button>\n </div>\n\n <div class=\"drp__weekdays\" aria-hidden=\"true\">\n <% t(\"components.date_range_picker.demo.weekdays\").each do |day| %>\n <span class=\"drp__weekday\"><%= day %></span>\n <% end %>\n </div>\n\n <div class=\"drp__grid\" role=\"grid\" aria-labelledby=\"drp-month\"\n data-stimeo--date-range-picker-target=\"grid\">\n <% 6.times do %>\n <div class=\"drp__row\" role=\"row\">\n <% 7.times do %>\n <button class=\"drp__cell\" type=\"button\" role=\"gridcell\" tabindex=\"-1\"\n data-stimeo--date-range-picker-target=\"cell\"\n data-action=\"click->stimeo--date-range-picker#selectDate\n mouseenter->stimeo--date-range-picker#previewTo\n focus->stimeo--date-range-picker#previewTo\n keydown->stimeo--date-range-picker#onKeydown\"></button>\n <% end %>\n </div>\n <% end %>\n </div>\n\n <div class=\"drp__presets\" role=\"group\"\n aria-label=\"<%= t(\"components.date_range_picker.demo.presets_label\") %>\">\n <button class=\"drp__preset\" type=\"button\" data-range=\"today\"\n data-action=\"click->stimeo--date-range-picker#applyPreset\">\n <%= t(\"components.date_range_picker.demo.preset_today\") %>\n </button>\n <button class=\"drp__preset\" type=\"button\" data-range=\"last7\"\n data-action=\"click->stimeo--date-range-picker#applyPreset\">\n <%= t(\"components.date_range_picker.demo.preset_last7\") %>\n </button>\n <button class=\"drp__preset\" type=\"button\" data-range=\"thisMonth\"\n data-action=\"click->stimeo--date-range-picker#applyPreset\">\n <%= t(\"components.date_range_picker.demo.preset_this_month\") %>\n </button>\n </div>\n\n <span class=\"drp__status\" role=\"status\" aria-live=\"polite\"\n data-stimeo--date-range-picker-target=\"status\"></span>\n\n <%# Says on screen why two days are struck out. The dates move with today, so\n without this the exclusion reads as a glitch rather than a demonstration. %>\n <p class=\"drp__note\"><%= t(\"components.date_range_picker.demo.blackout_note\") %></p>\n\n <input type=\"hidden\" name=\"start_date\" data-stimeo--date-range-picker-target=\"startField\" />\n <input type=\"hidden\" name=\"end_date\" data-stimeo--date-range-picker-target=\"endField\" />\n</div>\n"
|
|
107
107
|
},
|
|
108
108
|
"stimeo--dialog": {
|
|
109
109
|
"file": "examples/dialog/_demo.html.erb",
|
|
@@ -111,7 +111,7 @@
|
|
|
111
111
|
},
|
|
112
112
|
"stimeo--direct-upload": {
|
|
113
113
|
"file": "examples/direct_upload/_demo.html.erb",
|
|
114
|
-
"source": "<%# Direct upload demo: there is no server, so demo.js fires the ActiveStorage\n direct-upload:* events to drive the rows. The library clones the row template per\n file, updates aria-valuenow / data-upload-state / the --stimeo
|
|
114
|
+
"source": "<%# Direct upload demo: there is no server, so demo.js fires the ActiveStorage\n direct-upload:* events to drive the rows. The library clones the row template per\n file, updates aria-valuenow / data-upload-state / the --stimeo--upload-progress\n var, and announces completion into the status live region. This demo styles the\n bars; the sr-only status carries the announcement. %>\n<div class=\"direct-upload-demo\">\n <button type=\"button\" class=\"demo-trigger\" data-direct-upload-start>\n <%= t(\"components.direct_upload.demo.start\") %>\n </button>\n <div\n class=\"direct-upload\"\n data-controller=\"stimeo--direct-upload\"\n data-stimeo--direct-upload-done-label-value=\"<%= t('components.direct_upload.demo.done') %>\"\n data-stimeo--direct-upload-error-label-value=\"<%= t('components.direct_upload.demo.error') %>\">\n <div class=\"direct-upload__list\" data-stimeo--direct-upload-target=\"list\"></div>\n <template data-stimeo--direct-upload-target=\"row\">\n <div class=\"direct-upload__row\" role=\"progressbar\" aria-valuemin=\"0\" aria-valuemax=\"100\">\n <span class=\"direct-upload__name\" data-field=\"name\"></span>\n <span class=\"direct-upload__track\"><span class=\"direct-upload__bar\"></span></span>\n <span class=\"direct-upload__percent\" data-field=\"percent\"></span>\n </div>\n </template>\n <span class=\"visually-hidden\" data-stimeo--direct-upload-target=\"status\"\n aria-live=\"polite\"></span>\n </div>\n</div>\n"
|
|
115
115
|
},
|
|
116
116
|
"stimeo--dirty-form": {
|
|
117
117
|
"file": "examples/dirty_form/_demo.html.erb",
|
|
@@ -135,7 +135,7 @@
|
|
|
135
135
|
},
|
|
136
136
|
"stimeo--empty-state": {
|
|
137
137
|
"file": "examples/empty_state/_demo.html.erb",
|
|
138
|
-
"source": "<%# Empty-state demo: add/remove items and the empty placeholder toggles itself. The\n controller observes the list with a MutationObserver, toggles hidden on the\n list / empty targets at the 0 <-> 1+ boundary, and reflects data-empty / data-count.\n demo.js adds/removes <li> (standing in for Turbo Stream rows). This demo only styles\n it; the empty placeholder's copy is owned here. %>\n<div\n class=\"empty-state-demo\"\n data-controller=\"stimeo--empty-state\"\n data-stimeo--empty-state-announce-text-value=\"<%=\n t(\"components.empty_state.demo.announce_empty\") %>\"\n data-stimeo--empty-state-announce-filled-text-value=\"<%=\n t(\"components.empty_state.demo.announce_filled\") %>\">\n <div class=\"empty-state-demo__bar\">\n <button\n type=\"button\"\n class=\"demo-trigger\"\n data-empty-state-add\n data-item-label=\"<%= t(\"components.empty_state.demo.item\") %>\">\n <%= t(\"components.empty_state.demo.add\") %>\n </button>\n <button type=\"button\" class=\"demo-trigger\" data-empty-state-clear>\n <%= t(\"components.empty_state.demo.clear\") %>\n </button>\n </div>\n <ul class=\"empty-state-demo__list\" data-stimeo--empty-state-target=\"list\">\n <li><%= t(\"components.empty_state.demo.item\") %> 1</li>\n <li><%= t(\"components.empty_state.demo.item\") %> 2</li>\n </ul>\n <p class=\"empty-state-demo__empty\" data-stimeo--empty-state-target=\"empty\" hidden>\n <%= t(\"components.empty_state.demo.empty\") %>\n </p>\n</div>\n"
|
|
138
|
+
"source": "<%# Empty-state demo: add/remove items and the empty placeholder toggles itself. The\n controller observes the list with a MutationObserver, toggles hidden on the\n list / empty targets at the 0 <-> 1+ boundary, and reflects data-empty / data-count.\n demo.js adds/removes <li> (standing in for Turbo Stream rows). This demo only styles\n it; the empty placeholder's copy is owned here, and the crossing is read out through\n the shared announcer at the bottom. %>\n<div\n class=\"empty-state-demo\"\n data-controller=\"stimeo--empty-state\"\n data-stimeo--empty-state-announce-text-value=\"<%=\n t(\"components.empty_state.demo.announce_empty\") %>\"\n data-stimeo--empty-state-announce-filled-text-value=\"<%=\n t(\"components.empty_state.demo.announce_filled\") %>\">\n <div class=\"empty-state-demo__bar\">\n <button\n type=\"button\"\n class=\"demo-trigger\"\n data-empty-state-add\n data-item-label=\"<%= t(\"components.empty_state.demo.item\") %>\">\n <%= t(\"components.empty_state.demo.add\") %>\n </button>\n <button type=\"button\" class=\"demo-trigger\" data-empty-state-clear>\n <%= t(\"components.empty_state.demo.clear\") %>\n </button>\n </div>\n <ul class=\"empty-state-demo__list\" data-stimeo--empty-state-target=\"list\">\n <li><%= t(\"components.empty_state.demo.item\") %> 1</li>\n <li><%= t(\"components.empty_state.demo.item\") %> 2</li>\n </ul>\n <p class=\"empty-state-demo__empty\" data-stimeo--empty-state-target=\"empty\" hidden>\n <%= t(\"components.empty_state.demo.empty\") %>\n </p>\n\n <%# Shared announcer: the crossing is read out without moving focus. A placeholder\n that only becomes visible at the moment of the change is not reliably read, so the\n wording goes to a region that stands in the page independently of it. %>\n <div data-controller=\"stimeo--announcer\">\n <div class=\"visually-hidden\"\n data-stimeo--announcer-target=\"polite\" aria-live=\"polite\" aria-atomic=\"true\"></div>\n </div>\n</div>\n"
|
|
139
139
|
},
|
|
140
140
|
"stimeo--file-dropzone": {
|
|
141
141
|
"file": "examples/file_dropzone/_demo.html.erb",
|
|
@@ -163,7 +163,7 @@
|
|
|
163
163
|
},
|
|
164
164
|
"stimeo--frame-loading": {
|
|
165
165
|
"file": "examples/frame_loading/_demo.html.erb",
|
|
166
|
-
"source": "<%# Frame-loading demo: the controller subscribes to a turbo-frame's fetch lifecycle\n and toggles aria-busy / data-frame-loading, the skeleton, content inert, and focus.\n This catalog has no Turbo backend, so demo.js fires the turbo:before-fetch-request →\n turbo:frame-load pair to reproduce a frame fetch. The library only toggles state and\n hidden; demo.css owns the skeleton bars and the dimmed content. %>\n<div class=\"frame-demo\">\n <button type=\"button\" class=\"demo-trigger\" data-frame-demo-reload>\n <%= t(\"components.frame_loading.demo.reload\") %>\n </button>\n <div\n class=\"frame-demo__frame\"\n data-controller=\"stimeo--frame-loading\"\n data-stimeo--frame-loading-min-duration-value=\"600\">\n <div class=\"frame-demo__skeleton\" data-stimeo--frame-loading-target=\"skeleton\" hidden>\n <span class=\"visually-hidden\"><%= t(\"components.frame_loading.demo.loading\") %></span>\n <span class=\"frame-demo__bar\"></span>\n <span class=\"frame-demo__bar\"></span>\n <span class=\"frame-demo__bar\"></span>\n </div>\n <div class=\"frame-demo__content\" data-stimeo--frame-loading-target=\"content\">\n <p><%= t(\"components.frame_loading.demo.content\") %></p>\n <button\n type=\"button\"\n class=\"demo-trigger\"\n data-frame-demo-action\n data-result-template=\"<%= t(\"components.frame_loading.demo.action_result\") %>\">\n <%= t(\"components.frame_loading.demo.action\") %>\n </button>\n <span class=\"frame-demo__result\" data-frame-demo-result role=\"status\"></span>\n </div>\n </div>\n</div>\n"
|
|
166
|
+
"source": "<%# Frame-loading demo: the controller subscribes to a turbo-frame's fetch lifecycle\n and toggles aria-busy / data-frame-loading, the skeleton, content inert, and focus.\n This catalog has no Turbo backend, so demo.js fires the turbo:before-fetch-request →\n turbo:frame-load pair to reproduce a frame fetch. The library only toggles state and\n hidden; demo.css owns the skeleton bars and the dimmed content. %>\n<div class=\"frame-demo\">\n <button type=\"button\" class=\"demo-trigger\" data-frame-demo-reload>\n <%= t(\"components.frame_loading.demo.reload\") %>\n </button>\n <div\n class=\"frame-demo__frame\"\n data-controller=\"stimeo--frame-loading\"\n data-stimeo--frame-loading-min-duration-value=\"600\"\n data-stimeo--frame-loading-announce-text-value=\"<%=\n t(\"components.frame_loading.demo.announce_loading\") %>\"\n data-stimeo--frame-loading-announce-ready-text-value=\"<%=\n t(\"components.frame_loading.demo.announce_ready\") %>\">\n <div class=\"frame-demo__skeleton\" data-stimeo--frame-loading-target=\"skeleton\" hidden>\n <span class=\"visually-hidden\"><%= t(\"components.frame_loading.demo.loading\") %></span>\n <span class=\"frame-demo__bar\"></span>\n <span class=\"frame-demo__bar\"></span>\n <span class=\"frame-demo__bar\"></span>\n </div>\n <div class=\"frame-demo__content\" data-stimeo--frame-loading-target=\"content\">\n <p><%= t(\"components.frame_loading.demo.content\") %></p>\n <button\n type=\"button\"\n class=\"demo-trigger\"\n data-frame-demo-action\n data-result-template=\"<%= t(\"components.frame_loading.demo.action_result\") %>\">\n <%= t(\"components.frame_loading.demo.action\") %>\n </button>\n <span class=\"frame-demo__result\" data-frame-demo-result role=\"status\"></span>\n </div>\n </div>\n\n <%# Shared announcer: the frame's own skeleton is revealed at the moment of the\n change, which is not reliably read, so the loading / ready wording goes here. %>\n <div data-controller=\"stimeo--announcer\">\n <div class=\"visually-hidden\"\n data-stimeo--announcer-target=\"polite\" aria-live=\"polite\" aria-atomic=\"true\"></div>\n </div>\n</div>\n"
|
|
167
167
|
},
|
|
168
168
|
"stimeo--highlight": {
|
|
169
169
|
"file": "examples/highlight/_demo.html.erb",
|
|
@@ -203,7 +203,7 @@
|
|
|
203
203
|
},
|
|
204
204
|
"stimeo--masonry": {
|
|
205
205
|
"file": "examples/masonry/_demo.html.erb",
|
|
206
|
-
"source": "<%# Markup for the masonry (tiled grid) demo.\n The library computes the column count from the container width and min-column-width,\n and updates --stimeo
|
|
206
|
+
"source": "<%# Markup for the masonry (tiled grid) demo.\n The library computes the column count from the container width and min-column-width,\n and updates --stimeo--masonry-columns and each item's data-column. The look (CSS\n multi-column) is the consumer's CSS; reading/focus order stays in DOM order. %>\n<div\n class=\"masonry-demo\"\n data-controller=\"stimeo--masonry\"\n data-stimeo--masonry-min-column-width-value=\"180\"\n data-stimeo--masonry-gap-value=\"12\">\n <div class=\"masonry-demo__item\" data-stimeo--masonry-target=\"item\">\n <h3 class=\"masonry-demo__title\"><%= t(\"components.masonry.demo.cards.short.title\") %></h3>\n <p><%= t(\"components.masonry.demo.cards.short.body\") %></p>\n </div>\n <div class=\"masonry-demo__item\" data-stimeo--masonry-target=\"item\">\n <h3 class=\"masonry-demo__title\"><%= t(\"components.masonry.demo.cards.tall.title\") %></h3>\n <p><%= t(\"components.masonry.demo.cards.tall.body\") %></p>\n <p><%= t(\"components.masonry.demo.cards.tall.body\") %></p>\n </div>\n <div class=\"masonry-demo__item\" data-stimeo--masonry-target=\"item\">\n <h3 class=\"masonry-demo__title\"><%= t(\"components.masonry.demo.cards.medium.title\") %></h3>\n <p><%= t(\"components.masonry.demo.cards.medium.body\") %></p>\n </div>\n <div class=\"masonry-demo__item\" data-stimeo--masonry-target=\"item\">\n <h3 class=\"masonry-demo__title\"><%= t(\"components.masonry.demo.cards.short.title\") %></h3>\n <p><%= t(\"components.masonry.demo.cards.short.body\") %></p>\n </div>\n <div class=\"masonry-demo__item\" data-stimeo--masonry-target=\"item\">\n <h3 class=\"masonry-demo__title\"><%= t(\"components.masonry.demo.cards.tall.title\") %></h3>\n <p><%= t(\"components.masonry.demo.cards.tall.body\") %></p>\n <p><%= t(\"components.masonry.demo.cards.medium.body\") %></p>\n </div>\n <div class=\"masonry-demo__item\" data-stimeo--masonry-target=\"item\">\n <h3 class=\"masonry-demo__title\"><%= t(\"components.masonry.demo.cards.medium.title\") %></h3>\n <p><%= t(\"components.masonry.demo.cards.medium.body\") %></p>\n </div>\n</div>\n<p\n class=\"masonry-demo__status\"\n data-masonry-status\n data-template=\"<%= t(\"components.masonry.demo.status_template\") %>\"\n aria-live=\"polite\"></p>\n"
|
|
207
207
|
},
|
|
208
208
|
"stimeo--menu": {
|
|
209
209
|
"file": "examples/menu/_demo.html.erb",
|
|
@@ -215,11 +215,11 @@
|
|
|
215
215
|
},
|
|
216
216
|
"stimeo--meter": {
|
|
217
217
|
"file": "examples/meter/_demo.html.erb",
|
|
218
|
-
"source": "<%# Markup for the meter demo.\n Syncs role=\"meter\" and aria-value*, and classifies data-state into low/medium/high\n via the low/high thresholds. demo.css applies color based on data-state.\n aria-valuetext is generated by the library from the {percent}/{state} template.\n Like a progressbar, a meter must not contain interactive children, so the preset\n buttons sit beside it and update it through the meter:set CustomEvent the\n controller listens for (see demo.js) rather than a Stimulus data-action, which\n only binds within the controller's own element. %>\n<div class=\"meter-demo\">\n <div\n class=\"meter\"\n data-controller=\"stimeo--meter\"\n role=\"meter\"\n aria-label=\"<%= t('components.meter.demo.label') %>\"\n aria-valuemin=\"0\"\n aria-valuemax=\"100\"\n aria-valuenow=\"72\"\n data-stimeo--meter-value-value=\"72\"\n data-stimeo--meter-low-value=\"50\"\n data-stimeo--meter-high-value=\"80\"\n data-stimeo--meter-value-text-value=\"<%= t('components.meter.demo.value_text') %>\"\n data-action=\"meter:set->stimeo--meter#setValue\">\n <div class=\"meter__bar\" data-stimeo--meter-target=\"bar\"></div>\n </div>\n\n <div class=\"meter-demo__controls\" role=\"group\"\n aria-label=\"<%= t('components.meter.demo.controls_label') %>\">\n <% [20, 65, 90].each do |amount| %>\n <button\n class=\"demo-trigger\"\n type=\"button\"\n data-meter-amount=\"<%= amount %>\">\n <%= amount %>\n </button>\n <% end %>\n </div>\n</div>\n"
|
|
218
|
+
"source": "<%# Markup for the meter demo.\n Syncs role=\"meter\" and aria-value*, and classifies data-state into low/medium/high\n via the low/high thresholds. demo.css applies color based on data-state.\n aria-valuetext is generated by the library from the {percent}/{state} template.\n Like a progressbar, a meter must not contain interactive children, so the preset\n buttons sit beside it and update it through the meter:set CustomEvent the\n controller listens for (see demo.js) rather than a Stimulus data-action, which\n only binds within the controller's own element. %>\n<div class=\"meter-demo\">\n <div\n class=\"meter\"\n data-controller=\"stimeo--meter\"\n role=\"meter\"\n aria-label=\"<%= t('components.meter.demo.label') %>\"\n aria-valuemin=\"0\"\n aria-valuemax=\"100\"\n aria-valuenow=\"72\"\n data-stimeo--meter-value-value=\"72\"\n data-stimeo--meter-low-value=\"50\"\n data-stimeo--meter-high-value=\"80\"\n data-stimeo--meter-value-text-value=\"<%= t('components.meter.demo.value_text') %>\"\n data-stimeo--meter-announce-text-value=\"<%= t('components.meter.demo.announce_state') %>\"\n data-action=\"meter:set->stimeo--meter#setValue\">\n <div class=\"meter__bar\" data-stimeo--meter-target=\"bar\"></div>\n </div>\n\n <div class=\"meter-demo__controls\" role=\"group\"\n aria-label=\"<%= t('components.meter.demo.controls_label') %>\">\n <% [20, 65, 90].each do |amount| %>\n <button\n class=\"demo-trigger\"\n type=\"button\"\n data-meter-amount=\"<%= amount %>\">\n <%= amount %>\n </button>\n <% end %>\n </div>\n\n <%# Shared announcer: reading every value would be unusable, so only a move into\n another segment is read out. %>\n <div data-controller=\"stimeo--announcer\">\n <div class=\"visually-hidden\"\n data-stimeo--announcer-target=\"polite\" aria-live=\"polite\" aria-atomic=\"true\"></div>\n </div>\n</div>\n"
|
|
219
219
|
},
|
|
220
220
|
"stimeo--multi-select": {
|
|
221
221
|
"file": "examples/multi_select/_demo.html.erb",
|
|
222
|
-
"source": "<%# Markup for the multi-select (multiple-selection combobox) demo.\n Filter candidates as you type, select several, and show the selected ones as chips.\n Focus stays on the input; the active candidate is shown via aria-activedescendant\n and selection via aria-selected. The library handles filtering, multi-select\n toggling, chip create/remove, roving,
|
|
222
|
+
"source": "<%# Markup for the multi-select (multiple-selection combobox) demo.\n Filter candidates as you type, select several, and show the selected ones as chips.\n Focus stays on the input; the active candidate is shown via aria-activedescendant\n and selection via aria-selected. The library handles filtering, multi-select\n toggling, chip create/remove, roving, shared announcements, and dismiss. Placement\n lives in demo.css. %>\n<% announce_selected = t(\"components.multi_select.demo.announce_selected\") %>\n<% announce_removed = t(\"components.multi_select.demo.announce_removed\") %>\n<% remove_label = t(\"components.multi_select.demo.remove_label\") %>\n<div\n class=\"multi-select\"\n data-controller=\"stimeo--multi-select\"\n data-stimeo--multi-select-announce-text-value=\"<%= announce_selected %>\"\n data-stimeo--multi-select-announce-removed-text-value=\"<%= announce_removed %>\">\n <span id=\"multi-select-label\" class=\"multi-select__label\">\n <%= t(\"components.multi_select.demo.label\") %>\n </span>\n\n <ul\n class=\"multi-select__tags\"\n aria-label=\"<%= t(\"components.multi_select.demo.selected_label\") %>\"\n data-stimeo--multi-select-target=\"tags\"></ul>\n\n <input\n type=\"text\"\n class=\"multi-select__input\"\n role=\"combobox\"\n aria-expanded=\"false\"\n aria-autocomplete=\"list\"\n aria-controls=\"multi-select-list\"\n aria-labelledby=\"multi-select-label\"\n placeholder=\"<%= t(\"components.multi_select.demo.placeholder\") %>\"\n data-stimeo--multi-select-target=\"input\"\n data-action=\"input->stimeo--multi-select#filter\n keydown->stimeo--multi-select#onKeydown\n focus->stimeo--multi-select#open\" />\n\n <ul\n id=\"multi-select-list\"\n class=\"multi-select__list\"\n role=\"listbox\"\n aria-multiselectable=\"true\"\n aria-label=\"<%= t(\"components.multi_select.demo.options_label\") %>\"\n hidden\n data-stimeo--multi-select-target=\"list\">\n <% %w[apple banana cherry grape orange melon].each_with_index do |fruit, index| %>\n <li\n id=\"multi-select-opt-<%= index %>\"\n class=\"multi-select__option\"\n role=\"option\"\n aria-selected=\"false\"\n data-value=\"<%= fruit %>\"\n data-stimeo--multi-select-target=\"option\"\n data-action=\"click->stimeo--multi-select#toggleOption\">\n <%= t(\"components.multi_select.demo.options.#{fruit}\") %>\n </li>\n <% end %>\n </ul>\n\n <template data-stimeo--multi-select-target=\"tagTemplate\">\n <li class=\"multi-select__tag\" data-stimeo--multi-select-target=\"tag\">\n <span data-stimeo--multi-select-target=\"label\"></span>\n <%# Removal is handled by a delegated listener on the tags container, so it\n works instantly without waiting on Stimulus wiring a data-action onto the\n dynamically-added button. %>\n <button\n type=\"button\"\n class=\"multi-select__remove\"\n tabindex=\"-1\"\n aria-label=\"<%= remove_label %>\"\n data-stimeo--multi-select-target=\"remove\">×</button>\n </li>\n </template>\n\n <%# Selection announcements go to the shared announcer that lives in the page. %>\n <div data-controller=\"stimeo--announcer\">\n <div class=\"visually-hidden\" data-stimeo--announcer-target=\"polite\"\n aria-live=\"polite\" aria-atomic=\"true\"></div>\n </div>\n</div>\n"
|
|
223
223
|
},
|
|
224
224
|
"stimeo--navigation-menu": {
|
|
225
225
|
"file": "examples/navigation_menu/_demo.html.erb",
|
|
@@ -231,7 +231,7 @@
|
|
|
231
231
|
},
|
|
232
232
|
"stimeo--network-status": {
|
|
233
233
|
"file": "examples/network_status/_demo.html.erb",
|
|
234
|
-
"source": "<%# Markup for the network-status (offline-detection banner) demo.\n The library checks navigator.onLine initially and subscribes to the window's\n online/offline events, toggling the corresponding banner's hidden.
|
|
234
|
+
"source": "<%# Markup for the network-status (offline-detection banner) demo.\n The library checks navigator.onLine initially and subscribes to the window's\n online/offline events, toggling the corresponding banner's hidden. The banners are\n the visual half only: a region revealed at the moment of the change is not reliably\n read, so the wording goes to the shared announcer instead — assertively when the\n connection drops, politely when it comes back. Since real online/offline is\n environment-dependent, the demo's demo.js fakes the window events to reproduce the\n behavior. %>\n<div class=\"network-demo\">\n <div class=\"network-demo__controls\">\n <button class=\"demo-trigger\" type=\"button\" data-network-demo=\"offline\">\n <%= t(\"components.network_status.demo.go_offline\") %>\n </button>\n <button class=\"demo-trigger\" type=\"button\" data-network-demo=\"online\">\n <%= t(\"components.network_status.demo.go_online\") %>\n </button>\n </div>\n\n <div data-controller=\"stimeo--network-status\"\n data-stimeo--network-status-online-auto-hide-value=\"3000\"\n data-stimeo--network-status-announce-text-value=\"<%=\n t(\"components.network_status.demo.offline_message\") %>\"\n data-stimeo--network-status-announce-online-text-value=\"<%=\n t(\"components.network_status.demo.online_message\") %>\">\n <div\n class=\"network-banner network-banner--offline\"\n hidden\n data-stimeo--network-status-target=\"offline\">\n <%= t(\"components.network_status.demo.offline_message\") %>\n </div>\n <div\n class=\"network-banner network-banner--online\"\n hidden\n data-stimeo--network-status-target=\"online\">\n <%= t(\"components.network_status.demo.online_message\") %>\n </div>\n </div>\n\n <%# Shared announcer: losing the connection interrupts (assertive), regaining it\n waits its turn (polite). %>\n <div data-controller=\"stimeo--announcer\">\n <div class=\"visually-hidden\"\n data-stimeo--announcer-target=\"polite\" aria-live=\"polite\" aria-atomic=\"true\"></div>\n <div class=\"visually-hidden\"\n data-stimeo--announcer-target=\"assertive\" aria-live=\"assertive\" aria-atomic=\"true\"></div>\n </div>\n\n <p class=\"network-demo__note\"><%= t(\"components.network_status.demo.note\") %></p>\n</div>\n"
|
|
235
235
|
},
|
|
236
236
|
"stimeo--number-input": {
|
|
237
237
|
"file": "examples/number_input/_demo.html.erb",
|
|
@@ -263,7 +263,7 @@
|
|
|
263
263
|
},
|
|
264
264
|
"stimeo--password-strength": {
|
|
265
265
|
"file": "examples/password_strength/_demo.html.erb",
|
|
266
|
-
"source": "<%# Markup for the password-strength demo.\n The controller scores the field with a lightweight zero-dependency heuristic and\n drives the meter (aria-valuenow), the data-strength token, and the 0–1 fill on the\n --stimeo
|
|
266
|
+
"source": "<%# Markup for the password-strength demo.\n The controller scores the field with a lightweight zero-dependency heuristic and\n drives the meter (aria-valuenow), the data-strength token, and the 0–1 fill on the\n --stimeo--password-strength custom property. The input sits inside the controller so\n its data-action input->...#evaluate binds directly. Level labels are localized via\n the levels value; demo.css colors the bar by the locale-independent data-strength band,\n so the look never depends on the (translated) label text. The library writes no CSS. %>\n<% levels = t(\"components.password_strength.demo.levels\").to_json %>\n<div\n class=\"password-strength\"\n data-controller=\"stimeo--password-strength\"\n data-stimeo--password-strength-levels-value=\"<%= levels %>\">\n <label class=\"password-strength__label\" for=\"password-strength-demo-input\">\n <%= t(\"components.password_strength.demo.label\") %>\n </label>\n <input\n class=\"password-strength__input\"\n id=\"password-strength-demo-input\"\n type=\"password\"\n autocomplete=\"new-password\"\n aria-describedby=\"password-strength-demo-readout\"\n data-stimeo--password-strength-target=\"input\"\n data-action=\"input->stimeo--password-strength#evaluate\">\n <div\n class=\"password-strength__meter\"\n role=\"meter\"\n aria-valuemin=\"0\"\n aria-valuemax=\"4\"\n aria-valuenow=\"0\"\n aria-label=\"<%= t('components.password_strength.demo.meter_label') %>\"\n data-stimeo--password-strength-target=\"meter\">\n <span class=\"password-strength__bar\"></span>\n </div>\n <span\n class=\"password-strength__readout\"\n id=\"password-strength-demo-readout\"\n aria-live=\"polite\"\n data-stimeo--password-strength-target=\"label\"></span>\n</div>\n<p class=\"password-strength__hint\"><%= t(\"components.password_strength.demo.hint\") %></p>\n"
|
|
267
267
|
},
|
|
268
268
|
"stimeo--persist": {
|
|
269
269
|
"file": "examples/persist/_demo.html.erb",
|
|
@@ -291,19 +291,19 @@
|
|
|
291
291
|
},
|
|
292
292
|
"stimeo--progress": {
|
|
293
293
|
"file": "examples/progress/_demo.html.erb",
|
|
294
|
-
"source": "<%# Markup for the progress (progress bar) demo.\n The library syncs role=\"progressbar\" and aria-value*, and exposes the ratio as\n --stimeo
|
|
294
|
+
"source": "<%# Markup for the progress (progress bar) demo.\n The library syncs role=\"progressbar\" and aria-value*, and exposes the ratio as\n --stimeo--progress-ratio (0–1). The bar's width and color live in demo.css.\n The control buttons sit outside the progressbar (a progressbar must not contain\n interactive children), so they can't drive it through a Stimulus data-action —\n that only binds inside the controller's own element. Instead they dispatch the\n progress:set CustomEvent the controller listens for (see demo.js), the same\n contract a consumer would use to update progress from elsewhere. %>\n<div class=\"progress-demo\">\n <div\n class=\"progress\"\n data-controller=\"stimeo--progress\"\n role=\"progressbar\"\n aria-label=\"<%= t('components.progress.demo.label') %>\"\n aria-valuemin=\"0\"\n aria-valuemax=\"100\"\n aria-valuenow=\"40\"\n data-stimeo--progress-value-value=\"40\"\n data-stimeo--progress-value-text-value=\"<%= t('components.progress.demo.value_text') %>\"\n data-stimeo--progress-announce-text-value=\"<%=\n t('components.progress.demo.announce_complete') %>\"\n data-action=\"progress:set->stimeo--progress#setValue\">\n <div class=\"progress__bar\" data-stimeo--progress-target=\"bar\"></div>\n </div>\n\n <div\n class=\"progress-demo__controls\"\n role=\"group\"\n aria-label=\"<%= t('components.progress.demo.controls_label') %>\">\n <% [0, 25, 50, 75, 100].each do |amount| %>\n <button\n class=\"demo-trigger\"\n type=\"button\"\n data-progress-amount=\"<%= amount %>\">\n <%= amount %>%\n </button>\n <% end %>\n </div>\n\n <%# Indeterminate example: drop aria-valuenow and let the consumer apply a looping\n animation via data-state=\"indeterminate\". %>\n <div\n class=\"progress progress--indeterminate\"\n data-controller=\"stimeo--progress\"\n role=\"progressbar\"\n aria-label=\"<%= t('components.progress.demo.indeterminate_label') %>\"\n aria-valuemin=\"0\"\n aria-valuemax=\"100\"\n data-stimeo--progress-indeterminate-value=\"true\">\n <div class=\"progress__bar\" data-stimeo--progress-target=\"bar\"></div>\n </div>\n\n <%# Shared announcer: aria-valuenow is state a reader can query, not a notification.\n Only reaching the end is news, so that is the one thing read out. %>\n <div data-controller=\"stimeo--announcer\">\n <div class=\"visually-hidden\"\n data-stimeo--announcer-target=\"polite\" aria-live=\"polite\" aria-atomic=\"true\"></div>\n </div>\n</div>\n"
|
|
295
295
|
},
|
|
296
296
|
"stimeo--radio-group": {
|
|
297
297
|
"file": "examples/radio_group/_demo.html.erb",
|
|
298
|
-
"source": "<%# Markup for the radio-group (APG Radio Group) demo.\n A custom card-style radio. Single selection is shown via aria-checked and the single\n tab stop via roving tabindex. Arrows move the selection (focus = selection). The\n selected value is reflected into a hidden field; the look is the consumer's CSS. %>\n<div class=\"radio-group-demo\" role=\"radiogroup\" aria-labelledby=\"rg-label\"\n data-controller=\"stimeo--radio-group\">\n <span class=\"radio-group-demo__legend\" id=\"rg-label\">\n <%= t(\"components.radio_group.demo.legend\") %>\n </span>\n <p class=\"radio-group-demo__hint\"><%= t(\"components.radio_group.demo.hint\") %></p>\n\n <div class=\"radio-group-demo__options\">\n <div class=\"radio-group-demo__option\" role=\"radio\" aria-checked=\"false\" tabindex=\"0\"\n data-value=\"basic\" data-stimeo--radio-group-target=\"radio\"
|
|
298
|
+
"source": "<%# Markup for the radio-group (APG Radio Group) demo.\n A custom card-style radio. Single selection is shown via aria-checked and the single\n tab stop via roving tabindex. Arrows move the selection (focus = selection). The group\n delegates click and keydown, so the radios carry no data-action of their own. The\n selected value is reflected into a hidden field; the look is the consumer's CSS. %>\n<div class=\"radio-group-demo\" role=\"radiogroup\" aria-labelledby=\"rg-label\"\n data-controller=\"stimeo--radio-group\">\n <span class=\"radio-group-demo__legend\" id=\"rg-label\">\n <%= t(\"components.radio_group.demo.legend\") %>\n </span>\n <p class=\"radio-group-demo__hint\"><%= t(\"components.radio_group.demo.hint\") %></p>\n\n <div class=\"radio-group-demo__options\">\n <div class=\"radio-group-demo__option\" role=\"radio\" aria-checked=\"false\" tabindex=\"0\"\n data-value=\"basic\" data-stimeo--radio-group-target=\"radio\">\n <span class=\"radio-group-demo__name\"><%= t(\"components.radio_group.demo.basic\") %></span>\n <span class=\"radio-group-demo__desc\"><%= t(\"components.radio_group.demo.basic_desc\") %></span>\n </div>\n <div class=\"radio-group-demo__option\" role=\"radio\" aria-checked=\"false\" tabindex=\"-1\"\n data-value=\"pro\" data-stimeo--radio-group-target=\"radio\">\n <span class=\"radio-group-demo__name\"><%= t(\"components.radio_group.demo.pro\") %></span>\n <span class=\"radio-group-demo__desc\"><%= t(\"components.radio_group.demo.pro_desc\") %></span>\n </div>\n <div class=\"radio-group-demo__option\" role=\"radio\" aria-checked=\"false\" tabindex=\"-1\"\n data-value=\"max\" data-stimeo--radio-group-target=\"radio\">\n <span class=\"radio-group-demo__name\"><%= t(\"components.radio_group.demo.max\") %></span>\n <span class=\"radio-group-demo__desc\"><%= t(\"components.radio_group.demo.max_desc\") %></span>\n </div>\n </div>\n\n <input type=\"hidden\" name=\"plan\" value=\"\" data-stimeo--radio-group-target=\"field\" />\n\n <p class=\"radio-group-demo__status\" aria-live=\"polite\">\n <%= t(\"components.radio_group.demo.selected\") %>\n <span data-radio-value><%= t(\"components.radio_group.demo.none\") %></span>\n </p>\n</div>\n"
|
|
299
299
|
},
|
|
300
300
|
"stimeo--range-slider": {
|
|
301
301
|
"file": "examples/range_slider/_demo.html.erb",
|
|
302
|
-
"source": "<%# Markup for the range-slider (APG Slider, multi-thumb) demo.\n stimeo--range-slider handles the two thumbs' (start / end) mutual constraint\n (lower ≤ upper), keyboard / drag operation, and dynamic updates of each thumb's\n aria-valuemin/max/now, and exposes the range as the root CSS variables\n --stimeo
|
|
302
|
+
"source": "<%# Markup for the range-slider (APG Slider, multi-thumb) demo.\n stimeo--range-slider handles the two thumbs' (start / end) mutual constraint\n (lower ≤ upper), keyboard / drag operation, and dynamic updates of each thumb's\n aria-valuemin/max/now, and exposes the range as the root CSS variables\n --stimeo--range-slider-start / --stimeo--range-slider-end (0..1). The thumb\n and range placement is the Playground's CSS using those variables. %>\n<div\n class=\"range-slider\"\n data-controller=\"stimeo--range-slider\"\n data-stimeo--range-slider-min-value=\"0\"\n data-stimeo--range-slider-max-value=\"100\"\n data-stimeo--range-slider-step-value=\"1\"\n data-stimeo--range-slider-start-value=\"20\"\n data-stimeo--range-slider-end-value=\"80\">\n <div class=\"range-slider__track\" data-stimeo--range-slider-target=\"track\"\n data-action=\"pointerdown->stimeo--range-slider#onPointerDown\">\n <div class=\"range-slider__range\" aria-hidden=\"true\"></div>\n <div class=\"range-slider__thumb\" role=\"slider\" tabindex=\"0\"\n aria-label=\"<%= t(\"components.range_slider.demo.min_label\") %>\"\n data-stimeo--range-slider-target=\"startThumb\"\n data-action=\"keydown->stimeo--range-slider#onKeydown\"></div>\n <div class=\"range-slider__thumb\" role=\"slider\" tabindex=\"0\"\n aria-label=\"<%= t(\"components.range_slider.demo.max_label\") %>\"\n data-stimeo--range-slider-target=\"endThumb\"\n data-action=\"keydown->stimeo--range-slider#onKeydown\"></div>\n </div>\n <output class=\"range-slider__output\" data-range-slider-output aria-live=\"polite\">20 – 80</output>\n</div>\n"
|
|
303
303
|
},
|
|
304
304
|
"stimeo--rating": {
|
|
305
305
|
"file": "examples/rating/_demo.html.erb",
|
|
306
|
-
"source": "<%# Markup for the rating (APG Radio Group, ordinal scale) demo.\n Each symbol is role=\"radio\" with an aria-label (\"3 stars\", etc.). Selection is\n aria-checked; the currently filled range is data-rating-hover (the selected value or\n a hover preview).
|
|
306
|
+
"source": "<%# Markup for the rating (APG Radio Group, ordinal scale) demo.\n Each symbol is role=\"radio\" with an aria-label (\"3 stars\", etc.). Selection is\n aria-checked; the currently filled range is data-rating-hover (the selected value or\n a hover/focus preview). DOM order defines values 1..N. The star look is the consumer's CSS. %>\n<div class=\"rating-demo-wrap\">\n <p class=\"rating-demo__hint\"><%= t(\"components.rating.demo.hint\") %></p>\n <div class=\"rating-demo\" role=\"radiogroup\" aria-label=\"<%= t('components.rating.demo.label') %>\"\n data-controller=\"stimeo--rating\"\n data-stimeo--rating-value-value=\"0\">\n <% (1..5).each do |n| %>\n <span class=\"rating-demo__star\" role=\"radio\" aria-checked=\"false\"\n aria-label=\"<%= t('components.rating.demo.star', count: n) %>\"\n tabindex=\"<%= n == 1 ? 0 : -1 %>\"\n data-stimeo--rating-target=\"symbol\"\n data-action=\"click->stimeo--rating#select\n mouseenter->stimeo--rating#preview\n mouseleave->stimeo--rating#endPreview\n focus->stimeo--rating#preview\n blur->stimeo--rating#endPreview\n keydown->stimeo--rating#onKeydown\">\n <svg class=\"rating-demo__icon\" viewBox=\"0 0 24 24\" aria-hidden=\"true\">\n <path d=\"M12 2l2.9 6.26 6.86.6-5.2 4.52 1.56 6.72L12 17.1 5.88 20.6l1.56-6.72-5.2-4.52\n 6.86-.6z\"></path>\n </svg>\n </span>\n <% end %>\n\n <input type=\"hidden\" name=\"rating\" value=\"0\" data-stimeo--rating-target=\"field\" />\n\n <%# Subscribe to committed and reconciled values to update this instance's status. %>\n <span class=\"rating-demo__status\" aria-live=\"polite\"\n data-empty-text=\"<%= t('components.rating.demo.empty') %>\"\n data-rated-template=\"<%= t('components.rating.demo.rated') %>\"><%= t(\n 'components.rating.demo.empty'\n ) %></span>\n </div>\n</div>\n"
|
|
307
307
|
},
|
|
308
308
|
"stimeo--read-more": {
|
|
309
309
|
"file": "examples/read_more/_demo.html.erb",
|
|
@@ -331,7 +331,7 @@
|
|
|
331
331
|
},
|
|
332
332
|
"stimeo--scroll-area": {
|
|
333
333
|
"file": "examples/scroll_area/_demo.html.erb",
|
|
334
|
-
"source": "<%# Markup for the scroll-area demo.\n The consumer (this markup) puts aria-label on the viewport. On overflow the library\n adds tabindex to the viewport and (only when it has an accessible name) role=\"region\",\n and exposes data-scroll (start/middle/end) and --stimeo
|
|
334
|
+
"source": "<%# Markup for the scroll-area demo.\n The consumer (this markup) puts aria-label on the viewport. On overflow the library\n adds tabindex to the viewport and (only when it has an accessible name) role=\"region\",\n and exposes data-scroll (start/middle/end) and --stimeo--scroll-progress.\n Drawing the scroll shadow is demo.css's job, reading data-scroll. %>\n<div class=\"scroll-area\" data-controller=\"stimeo--scroll-area\"\n data-stimeo--scroll-area-orientation-value=\"vertical\">\n <div class=\"scroll-area__viewport\"\n data-stimeo--scroll-area-target=\"viewport\"\n aria-label=\"<%= t(\"components.scroll_area.demo.label\") %>\">\n <p><%= t(\"components.scroll_area.demo.intro\") %></p>\n <% 12.times do |i| %>\n <p class=\"scroll-area__line\"><%= t(\"components.scroll_area.demo.line\", number: i + 1) %></p>\n <% end %>\n </div>\n</div>\n"
|
|
335
335
|
},
|
|
336
336
|
"stimeo--scroll-restore": {
|
|
337
337
|
"file": "examples/scroll_restore/_demo.html.erb",
|
|
@@ -355,7 +355,7 @@
|
|
|
355
355
|
},
|
|
356
356
|
"stimeo--skeleton": {
|
|
357
357
|
"file": "examples/skeleton/_demo.html.erb",
|
|
358
|
-
"source": "<%# Markup for the skeleton (skeleton toggle control) demo.\n Initially the skeleton is shown, the real content hidden, and aria-busy=\"true\". On\n ready it swaps and sets aria-busy=\"false\". The skeleton is decorative\n (aria-hidden=\"true\"). A minimum display time prevents flicker.\n The Load / Reset buttons sit outside the busy card (so aria-busy stays scoped to the\n loading region, not the controls), so they drive it through events the controller\n listens for (see demo.js): Load fires content:ready — the same signal a consumer\n dispatches when their real content arrives — and Reset returns it to loading. A\n Stimulus data-action on the buttons would not bind, since actions only wire up\n within the controller's own element. %>\n<div class=\"skeleton-demo\">\n <div class=\"skeleton-demo__controls\">\n <button\n class=\"demo-trigger\"\n type=\"button\"\n data-skeleton-load>\n <%= t(\"components.skeleton.demo.load\") %>\n </button>\n <button\n class=\"demo-trigger\"\n type=\"button\"\n data-skeleton-reset>\n <%= t(\"components.skeleton.demo.reset\") %>\n </button>\n </div>\n\n <div\n class=\"skeleton-card\"\n data-controller=\"stimeo--skeleton\"\n aria-busy=\"true\"\n data-stimeo--skeleton-min-duration-value=\"400\"\n data-action=\"content:ready->stimeo--skeleton#ready skeleton:reset->stimeo--skeleton#reset\">\n <div\n class=\"skeleton\"\n aria-hidden=\"true\"\n data-stimeo--skeleton-target=\"placeholder\">\n <span class=\"skeleton__line skeleton__line--title\"></span>\n <span class=\"skeleton__line\"></span>\n <span class=\"skeleton__line skeleton__line--short\"></span>\n </div>\n\n <div class=\"skeleton-content\" hidden data-stimeo--skeleton-target=\"content\">\n <h3 class=\"skeleton-content__title\"><%= t(\"components.skeleton.demo.title\") %></h3>\n <p><%= t(\"components.skeleton.demo.body\") %></p>\n </div>\n </div>\n</div>\n"
|
|
358
|
+
"source": "<%# Markup for the skeleton (skeleton toggle control) demo.\n Initially the skeleton is shown, the real content hidden, and aria-busy=\"true\". On\n ready it swaps and sets aria-busy=\"false\". The skeleton is decorative\n (aria-hidden=\"true\"). A minimum display time prevents flicker.\n The Load / Reset buttons sit outside the busy card (so aria-busy stays scoped to the\n loading region, not the controls), so they drive it through events the controller\n listens for (see demo.js): Load fires content:ready — the same signal a consumer\n dispatches when their real content arrives — and Reset returns it to loading. A\n Stimulus data-action on the buttons would not bind, since actions only wire up\n within the controller's own element. %>\n<div class=\"skeleton-demo\">\n <div class=\"skeleton-demo__controls\">\n <button\n class=\"demo-trigger\"\n type=\"button\"\n data-skeleton-load>\n <%= t(\"components.skeleton.demo.load\") %>\n </button>\n <button\n class=\"demo-trigger\"\n type=\"button\"\n data-skeleton-reset>\n <%= t(\"components.skeleton.demo.reset\") %>\n </button>\n </div>\n\n <div\n class=\"skeleton-card\"\n data-controller=\"stimeo--skeleton\"\n aria-busy=\"true\"\n data-stimeo--skeleton-min-duration-value=\"400\"\n data-stimeo--skeleton-announce-ready-text-value=\"<%=\n t(\"components.skeleton.demo.announce_ready\") %>\"\n data-action=\"content:ready->stimeo--skeleton#ready skeleton:reset->stimeo--skeleton#reset\">\n <div\n class=\"skeleton\"\n aria-hidden=\"true\"\n data-stimeo--skeleton-target=\"placeholder\">\n <span class=\"skeleton__line skeleton__line--title\"></span>\n <span class=\"skeleton__line\"></span>\n <span class=\"skeleton__line skeleton__line--short\"></span>\n </div>\n\n <div class=\"skeleton-content\" hidden data-stimeo--skeleton-target=\"content\">\n <h3 class=\"skeleton-content__title\"><%= t(\"components.skeleton.demo.title\") %></h3>\n <p><%= t(\"components.skeleton.demo.body\") %></p>\n </div>\n </div>\n\n <%# Shared announcer: aria-busy going false is state, not a notification, so the\n arrival of the content is read out through a region that stands in the page\n independently of the card. %>\n <div data-controller=\"stimeo--announcer\">\n <div class=\"visually-hidden\"\n data-stimeo--announcer-target=\"polite\" aria-live=\"polite\" aria-atomic=\"true\"></div>\n </div>\n</div>\n"
|
|
359
359
|
},
|
|
360
360
|
"stimeo--slider": {
|
|
361
361
|
"file": "examples/slider/_demo.html.erb",
|
|
@@ -371,11 +371,11 @@
|
|
|
371
371
|
},
|
|
372
372
|
"stimeo--spinner": {
|
|
373
373
|
"file": "examples/spinner/_demo.html.erb",
|
|
374
|
-
"source": "<%# Markup for the spinner (loading indicator) demo.\n The indicator
|
|
374
|
+
"source": "<%# Markup for the spinner (loading indicator) demo.\n The indicator is the visual half — its text plus a spinner marked aria-hidden=\"true\" —\n and the controlled region reflects aria-busy. An indicator revealed at the moment of\n the change is not reliably read, so the loading / ready wording goes to the shared\n announcer and is said exactly once. A show delay and a minimum display time suppress\n flicker. The Start/Stop buttons call the controller's methods directly. %>\n<div\n class=\"spinner-demo\"\n data-controller=\"stimeo--spinner\"\n data-stimeo--spinner-delay-value=\"150\"\n data-stimeo--spinner-min-duration-value=\"600\"\n data-stimeo--spinner-announce-text-value=\"<%= t(\"components.spinner.demo.loading\") %>\"\n data-stimeo--spinner-announce-ready-text-value=\"<%=\n t(\"components.spinner.demo.announce_ready\") %>\">\n <div class=\"spinner-demo__controls\">\n <button\n class=\"demo-trigger\"\n type=\"button\"\n data-action=\"click->stimeo--spinner#start\">\n <%= t(\"components.spinner.demo.start\") %>\n </button>\n <button\n class=\"demo-trigger\"\n type=\"button\"\n data-action=\"click->stimeo--spinner#stop\">\n <%= t(\"components.spinner.demo.stop\") %>\n </button>\n </div>\n\n <p class=\"spinner-demo__hint\"><%= t(\"components.spinner.demo.hint\") %></p>\n\n <%# Visual only: the announcer says this once, so a live region here would repeat it. %>\n <div\n class=\"spinner\"\n hidden\n data-stimeo--spinner-target=\"indicator\">\n <span class=\"spinner__icon\" aria-hidden=\"true\"></span>\n <span data-stimeo--spinner-target=\"message\"><%= t(\"components.spinner.demo.loading\") %></span>\n </div>\n\n <div\n class=\"spinner-demo__region\"\n aria-busy=\"false\"\n data-stimeo--spinner-target=\"region\">\n <%= t(\"components.spinner.demo.content\") %>\n </div>\n\n <%# Shared announcer: loading ↔ ready is the transition worth reading; aria-busy is\n state the reader can query, not a notification. %>\n <div data-controller=\"stimeo--announcer\">\n <div class=\"visually-hidden\"\n data-stimeo--announcer-target=\"polite\" aria-live=\"polite\" aria-atomic=\"true\"></div>\n </div>\n</div>\n"
|
|
375
375
|
},
|
|
376
376
|
"stimeo--step-indicator": {
|
|
377
377
|
"file": "examples/step_indicator/_demo.html.erb",
|
|
378
|
-
"source": "<%# Markup for the step-indicator (read-only progress) demo.\n The library holds the current step number, sets data-state on each li, sets\n aria-current=\"step\" on the current li, and updates the progress CSS variable\n (--stimeo
|
|
378
|
+
"source": "<%# Markup for the step-indicator (read-only progress) demo.\n The library holds the current step number, sets data-state on each li, sets\n aria-current=\"step\" on the current li, and updates the progress CSS variable\n (--stimeo--step-indicator-ratio). The steps themselves aren't interactive, so the\n current step is updated via the external step:set event (fired by demo.js from the\n prev/next buttons). %>\n<div class=\"step-indicator-demo\">\n <%# --steps lets the CSS put the track between the first and last dot centres:\n the library's ratio is \"how far along the range\", and the range runs from\n centre to centre, not edge to edge. %>\n <ol\n class=\"step-indicator\"\n style=\"--steps: 4\"\n data-controller=\"stimeo--step-indicator\"\n aria-label=\"<%= t(\"components.step_indicator.demo.label\") %>\"\n data-stimeo--step-indicator-current-value=\"1\"\n data-action=\"step:set->stimeo--step-indicator#setCurrent\">\n <% %w[cart shipping payment confirm].each_with_index do |step, index| %>\n <li\n class=\"step-indicator__step\"\n data-stimeo--step-indicator-target=\"step\"\n <%= \"data-state=\\\"complete\\\"\".html_safe if index < 1 %>\n <%= \"data-state=\\\"current\\\" aria-current=\\\"step\\\"\".html_safe if index == 1 %>\n <%= \"data-state=\\\"upcoming\\\"\".html_safe if index > 1 %>>\n <span class=\"step-indicator__dot\"></span>\n <span class=\"step-indicator__label\"><%= t(\n \"components.step_indicator.demo.steps.#{step}\"\n ) %></span>\n </li>\n <% end %>\n </ol>\n\n <div class=\"step-indicator__controls\">\n <button type=\"button\" class=\"demo-trigger\" data-step-indicator-prev>\n <%= t(\"components.step_indicator.demo.back\") %>\n </button>\n <button type=\"button\" class=\"demo-trigger\" data-step-indicator-next>\n <%= t(\"components.step_indicator.demo.advance\") %>\n </button>\n </div>\n</div>\n"
|
|
379
379
|
},
|
|
380
380
|
"stimeo--stepper": {
|
|
381
381
|
"file": "examples/stepper/_demo.html.erb",
|
|
@@ -403,7 +403,7 @@
|
|
|
403
403
|
},
|
|
404
404
|
"stimeo--tags-input": {
|
|
405
405
|
"file": "examples/tags_input/_demo.html.erb",
|
|
406
|
-
"source": "<%# Markup for the tags-input demo.\n Enter or a comma (delimiter) turns the input value into a tag, blocking duplicates /\n empties / over-limit. Tag remove buttons move via roving (left/right keys), and\n Backspace on an empty input deletes the previous tag. The library handles tag\n create/remove, hidden-field sync, focus handoff, and
|
|
406
|
+
"source": "<%# Markup for the tags-input demo.\n Enter or a comma (delimiter) turns the input value into a tag, blocking duplicates /\n empties / over-limit. Tag remove buttons move via roving (left/right keys), and\n Backspace on an empty input deletes the previous tag. The library handles tag\n create/remove, hidden-field sync, focus handoff, and shared announcements. %>\n<% announce_added = t(\"components.tags_input.demo.announce_added\") %>\n<% announce_removed = t(\"components.tags_input.demo.announce_removed\") %>\n<% remove_label = t(\"components.tags_input.demo.remove_label\") %>\n<div class=\"tags-input\" data-controller=\"stimeo--tags-input\"\n data-stimeo--tags-input-delimiter-value=\",\"\n data-stimeo--tags-input-name-value=\"frameworks[]\"\n data-stimeo--tags-input-announce-text-value=\"<%= announce_added %>\"\n data-stimeo--tags-input-announce-removed-text-value=\"<%= announce_removed %>\">\n <ul\n class=\"tags-input__tags\"\n role=\"list\"\n aria-label=\"<%= t(\"components.tags_input.demo.tags_label\") %>\"\n data-stimeo--tags-input-target=\"tags\"></ul>\n\n <input\n type=\"text\"\n class=\"tags-input__input\"\n aria-label=\"<%= t(\"components.tags_input.demo.input_label\") %>\"\n aria-describedby=\"tags-input-help\"\n data-stimeo--tags-input-target=\"input\"\n data-action=\"keydown->stimeo--tags-input#onKeydown\" />\n\n <span id=\"tags-input-help\" class=\"tags-input__help\">\n <%= t(\"components.tags_input.demo.help\") %>\n </span>\n\n <%# The library creates and syncs the submit hidden inputs as name=\"frameworks[]\". %>\n <div hidden data-stimeo--tags-input-target=\"fields\"></div>\n\n <template data-stimeo--tags-input-target=\"tagTemplate\">\n <li class=\"tags-input__tag\" role=\"listitem\" data-stimeo--tags-input-target=\"tag\">\n <span data-stimeo--tags-input-target=\"label\"></span>\n <%# Removal is handled by a delegated listener on the tags container, so it\n works instantly without waiting on Stimulus wiring a data-action onto the\n dynamically-added button. %>\n <button\n type=\"button\"\n class=\"tags-input__remove\"\n tabindex=\"-1\"\n aria-label=\"<%= remove_label %>\"\n data-stimeo--tags-input-target=\"remove\">×</button>\n </li>\n </template>\n\n <%# Tag announcements go to the shared announcer that lives in the page. %>\n <div data-controller=\"stimeo--announcer\">\n <div class=\"visually-hidden\" data-stimeo--announcer-target=\"polite\"\n aria-live=\"polite\" aria-atomic=\"true\"></div>\n </div>\n</div>\n"
|
|
407
407
|
},
|
|
408
408
|
"stimeo--textarea-autosize": {
|
|
409
409
|
"file": "examples/textarea_autosize/_demo.html.erb",
|
|
@@ -423,7 +423,7 @@
|
|
|
423
423
|
},
|
|
424
424
|
"stimeo--toggle-group": {
|
|
425
425
|
"file": "examples/toggle_group/_demo.html.erb",
|
|
426
|
-
"source": "<%# Markup for the toggle-group
|
|
426
|
+
"source": "<%# Markup for the toggle-group demo: multiple selection, single selection, and\n unavailable items. The group delegates click and keydown, so the items carry\n no data-action of their own. %>\n<div class=\"toggle-group-demo-suite\">\n <section class=\"toggle-group-demo__example\">\n <h3 class=\"toggle-group-demo__heading\" id=\"toggle-group-multiple-heading\">\n <%= t(\"components.toggle_group.demo.multiple_heading\") %>\n </h3>\n <div class=\"toggle-group-demo\" role=\"group\"\n aria-labelledby=\"toggle-group-multiple-heading\"\n data-controller=\"stimeo--toggle-group\">\n <button type=\"button\" class=\"toggle-group-demo__item\" aria-pressed=\"false\" tabindex=\"0\"\n aria-label=\"<%= t('components.toggle_group.demo.bold') %>\" data-value=\"bold\"\n data-stimeo--toggle-group-target=\"item\">\n <svg class=\"demo-icon\" viewBox=\"0 0 24 24\" fill=\"none\"\n stroke=\"currentColor\" stroke-width=\"2\"\n stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M6 4h8a4 4 0 0 1 0 8H6z\"></path>\n <path d=\"M6 12h9a4 4 0 0 1 0 8H6z\"></path>\n </svg>\n </button>\n <button type=\"button\" class=\"toggle-group-demo__item\" aria-pressed=\"false\" tabindex=\"-1\"\n aria-label=\"<%= t('components.toggle_group.demo.italic') %>\" data-value=\"italic\"\n data-stimeo--toggle-group-target=\"item\">\n <svg class=\"demo-icon\" viewBox=\"0 0 24 24\" fill=\"none\"\n stroke=\"currentColor\" stroke-width=\"2\"\n stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\n <line x1=\"19\" y1=\"4\" x2=\"10\" y2=\"4\"></line>\n <line x1=\"14\" y1=\"20\" x2=\"5\" y2=\"20\"></line>\n <line x1=\"15\" y1=\"4\" x2=\"9\" y2=\"20\"></line>\n </svg>\n </button>\n <button type=\"button\" class=\"toggle-group-demo__item\" aria-pressed=\"false\" tabindex=\"-1\"\n aria-label=\"<%= t('components.toggle_group.demo.underline') %>\" data-value=\"underline\"\n data-stimeo--toggle-group-target=\"item\">\n <svg class=\"demo-icon\" viewBox=\"0 0 24 24\" fill=\"none\"\n stroke=\"currentColor\" stroke-width=\"2\"\n stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M6 3v7a6 6 0 0 0 12 0V3\"></path>\n <line x1=\"4\" y1=\"21\" x2=\"20\" y2=\"21\"></line>\n </svg>\n </button>\n </div>\n </section>\n\n <section class=\"toggle-group-demo__example\">\n <h3 class=\"toggle-group-demo__heading\" id=\"toggle-group-single-heading\">\n <%= t(\"components.toggle_group.demo.single_heading\") %>\n </h3>\n <div class=\"toggle-group-demo\" role=\"group\"\n aria-labelledby=\"toggle-group-single-heading\"\n data-controller=\"stimeo--toggle-group\"\n data-stimeo--toggle-group-mode-value=\"single\">\n <button type=\"button\" class=\"toggle-group-demo__item\" aria-pressed=\"true\" tabindex=\"0\"\n aria-label=\"<%= t('components.toggle_group.demo.align_left') %>\" data-value=\"left\"\n data-stimeo--toggle-group-target=\"item\">\n <svg class=\"demo-icon\" viewBox=\"0 0 24 24\" fill=\"none\"\n stroke=\"currentColor\" stroke-width=\"2\"\n stroke-linecap=\"round\" aria-hidden=\"true\">\n <path d=\"M4 6h16M4 10h11M4 14h16M4 18h9\"></path>\n </svg>\n </button>\n <button type=\"button\" class=\"toggle-group-demo__item\" aria-pressed=\"false\" tabindex=\"-1\"\n aria-label=\"<%= t('components.toggle_group.demo.align_center') %>\" data-value=\"center\"\n data-stimeo--toggle-group-target=\"item\">\n <svg class=\"demo-icon\" viewBox=\"0 0 24 24\" fill=\"none\"\n stroke=\"currentColor\" stroke-width=\"2\"\n stroke-linecap=\"round\" aria-hidden=\"true\">\n <path d=\"M4 6h16M6 10h12M4 14h16M7 18h10\"></path>\n </svg>\n </button>\n <button type=\"button\" class=\"toggle-group-demo__item\" aria-pressed=\"false\" tabindex=\"-1\"\n aria-label=\"<%= t('components.toggle_group.demo.align_right') %>\" data-value=\"right\"\n data-stimeo--toggle-group-target=\"item\">\n <svg class=\"demo-icon\" viewBox=\"0 0 24 24\" fill=\"none\"\n stroke=\"currentColor\" stroke-width=\"2\"\n stroke-linecap=\"round\" aria-hidden=\"true\">\n <path d=\"M4 6h16M9 10h11M4 14h16M11 18h9\"></path>\n </svg>\n </button>\n </div>\n </section>\n\n <section class=\"toggle-group-demo__example\">\n <h3 class=\"toggle-group-demo__heading\" id=\"toggle-group-disabled-heading\">\n <%= t(\"components.toggle_group.demo.disabled_heading\") %>\n </h3>\n <div class=\"toggle-group-demo\" role=\"group\"\n aria-labelledby=\"toggle-group-disabled-heading\"\n data-controller=\"stimeo--toggle-group\">\n <button type=\"button\" class=\"toggle-group-demo__item toggle-group-demo__item--wide\"\n aria-pressed=\"false\" tabindex=\"0\" data-value=\"available\"\n data-stimeo--toggle-group-target=\"item\">\n <%= t(\"components.toggle_group.demo.available\") %>\n </button>\n <button type=\"button\" class=\"toggle-group-demo__item toggle-group-demo__item--wide\"\n aria-pressed=\"false\" tabindex=\"-1\" data-value=\"native-disabled\" disabled\n data-stimeo--toggle-group-target=\"item\">\n <%= t(\"components.toggle_group.demo.native_disabled\") %>\n </button>\n <button type=\"button\" class=\"toggle-group-demo__item toggle-group-demo__item--wide\"\n aria-pressed=\"false\" tabindex=\"-1\" data-value=\"aria-disabled\" aria-disabled=\"true\"\n data-stimeo--toggle-group-target=\"item\">\n <%= t(\"components.toggle_group.demo.aria_disabled\") %>\n </button>\n </div>\n </section>\n</div>\n"
|
|
427
427
|
},
|
|
428
428
|
"stimeo--toolbar": {
|
|
429
429
|
"file": "examples/toolbar/_demo.html.erb",
|