stimulus_plumbers 0.4.8 → 0.4.10
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +39 -0
- data/README.md +7 -9
- data/app/assets/javascripts/stimulus-plumbers/controllers.manifest.json +367 -23
- data/app/assets/javascripts/stimulus-plumbers/index.es.js +678 -233
- data/app/assets/javascripts/stimulus-plumbers/index.es.js.map +1 -1
- data/app/assets/javascripts/stimulus-plumbers/index.umd.js +1 -1
- data/app/assets/javascripts/stimulus-plumbers/index.umd.js.map +1 -1
- data/app/assets/stylesheets/stimulus_plumbers/tokens.css +58 -53
- data/docs/architecture.md +0 -2
- data/docs/component/avatar.md +1 -0
- data/docs/component/button.md +1 -0
- data/docs/component/calendar.md +7 -16
- data/docs/component/checklist.md +153 -0
- data/docs/component/form.md +27 -5
- data/docs/component/icon.md +7 -5
- data/docs/component/indicator.md +55 -0
- data/docs/component/list.md +2 -3
- data/docs/component/ordered_list.md +118 -0
- data/docs/component/plumber.md +21 -0
- data/docs/component/popover.md +1 -2
- data/docs/component/progress.md +65 -0
- data/docs/component/timeline.md +47 -12
- data/docs/guide.md +21 -0
- data/lib/generators/stimulus_plumbers/install/install_generator.rb +21 -11
- data/lib/stimulus_plumbers/components/button.rb +1 -0
- data/lib/stimulus_plumbers/components/checklist/item/slots.rb +13 -0
- data/lib/stimulus_plumbers/components/checklist/item.rb +64 -0
- data/lib/stimulus_plumbers/components/checklist.rb +50 -0
- data/lib/stimulus_plumbers/components/combobox/typeahead.rb +1 -0
- data/lib/stimulus_plumbers/components/icon.rb +2 -2
- data/lib/stimulus_plumbers/components/indicator.rb +30 -0
- data/lib/stimulus_plumbers/components/link.rb +1 -0
- data/lib/stimulus_plumbers/components/ordered_list/item/slots.rb +13 -0
- data/lib/stimulus_plumbers/components/ordered_list/item.rb +129 -0
- data/lib/stimulus_plumbers/components/ordered_list.rb +24 -0
- data/lib/stimulus_plumbers/components/progress_bar.rb +48 -0
- data/lib/stimulus_plumbers/components/progress_meter.rb +37 -0
- data/lib/stimulus_plumbers/components/progress_ring.rb +40 -0
- data/lib/stimulus_plumbers/components/timeline/event.rb +2 -1
- data/lib/stimulus_plumbers/components/timeline/group.rb +5 -3
- data/lib/stimulus_plumbers/engine.rb +0 -4
- data/lib/stimulus_plumbers/form/builder.rb +4 -0
- data/lib/stimulus_plumbers/form/field.rb +1 -1
- data/lib/stimulus_plumbers/form/fields/inputs/code.rb +86 -0
- data/lib/stimulus_plumbers/form/fields/inputs/credit_card.rb +98 -0
- data/lib/stimulus_plumbers/form/fields/inputs/password.rb +1 -0
- data/lib/stimulus_plumbers/form/fields/inputs/search.rb +1 -0
- data/lib/stimulus_plumbers/form/fields/renderer.rb +2 -0
- data/lib/stimulus_plumbers/generators/css_entrypoint.rb +37 -9
- data/lib/stimulus_plumbers/generators/tokens_directive.rb +9 -5
- data/lib/stimulus_plumbers/helpers/avatar_helper.rb +1 -7
- data/lib/stimulus_plumbers/helpers/button_helper.rb +1 -7
- data/lib/stimulus_plumbers/helpers/calendar_helper.rb +2 -9
- data/lib/stimulus_plumbers/helpers/calendar_turbo_helper.rb +3 -6
- data/lib/stimulus_plumbers/helpers/checklist_helper.rb +11 -0
- data/lib/stimulus_plumbers/helpers/indicator_helper.rb +11 -0
- data/lib/stimulus_plumbers/helpers/ordered_list_helper.rb +11 -0
- data/lib/stimulus_plumbers/helpers/popover_helper.rb +1 -7
- data/lib/stimulus_plumbers/helpers/progress_helper.rb +21 -0
- data/lib/stimulus_plumbers/helpers.rb +8 -0
- data/lib/stimulus_plumbers/themes/base.rb +4 -0
- data/lib/stimulus_plumbers/themes/schema/icon.rb +21 -4
- data/lib/stimulus_plumbers/themes/schema/indicator/ranges.rb +14 -0
- data/lib/stimulus_plumbers/themes/schema.rb +82 -25
- data/lib/stimulus_plumbers/version.rb +1 -1
- data/lib/stimulus_plumbers.rb +10 -0
- data/vendor/ARIA.md +29 -2
- data/vendor/component/manifest.json +215 -0
- data/vendor/controller/docs/calendar.md +4 -7
- data/vendor/controller/docs/checklist.md +45 -0
- data/vendor/controller/docs/combobox.md +1 -31
- data/vendor/controller/docs/input-clearable.md +1 -4
- data/vendor/controller/docs/input-formatter.md +88 -23
- data/vendor/controller/docs/modal.md +1 -4
- data/vendor/controller/docs/popover.md +1 -3
- data/vendor/controller/docs/progress.md +82 -0
- data/vendor/controller/docs/reorderable.md +82 -0
- data/vendor/controller/docs/visibility.md +31 -0
- data/vendor/controller/manifest.json +367 -23
- metadata +27 -1
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Checklist
|
|
2
|
+
|
|
3
|
+
Master "select all" toggle for a group of native `<input type="checkbox">` checklist items. Aggregates their `.checked` state onto a master checkbox's `checked`/`indeterminate` properties and toggles them all at once. See [stimulus-plumbers-rails's docs/component/checklist.md](../../../stimulus-plumbers-rails/docs/component/checklist.md) for the Rails render options.
|
|
4
|
+
|
|
5
|
+
## Stimulus Identifier
|
|
6
|
+
|
|
7
|
+
`checklist`
|
|
8
|
+
|
|
9
|
+
## Targets
|
|
10
|
+
|
|
11
|
+
| Name | Purpose |
|
|
12
|
+
| -------- | --------------------------------------------------------------------- |
|
|
13
|
+
| `master` | The "select all" `<input type="checkbox">` — receives aggregate state |
|
|
14
|
+
| `item` | Each checklist item `<input type="checkbox">` |
|
|
15
|
+
|
|
16
|
+
## Actions
|
|
17
|
+
|
|
18
|
+
| Name | Purpose |
|
|
19
|
+
| ----------- | -------------------------------------------------------------------------------------------------------------------------------- |
|
|
20
|
+
| `onChange` | Wired to `change` on the wrapper — event adapter: if the event came from the master, calls `toggleAll`; always calls `recompute` |
|
|
21
|
+
| `toggleAll` | Programmatic API — sets every enabled item's `.checked` to the given value |
|
|
22
|
+
| `recompute` | Programmatic API — writes the master's `.checked`/`.indeterminate` from the enabled items' aggregate state |
|
|
23
|
+
|
|
24
|
+
## Example HTML
|
|
25
|
+
|
|
26
|
+
```html
|
|
27
|
+
<div data-controller="checklist" data-action="change->checklist#onChange">
|
|
28
|
+
<label>
|
|
29
|
+
<input type="checkbox" data-checklist-target="master" />
|
|
30
|
+
Select all
|
|
31
|
+
</label>
|
|
32
|
+
|
|
33
|
+
<label>
|
|
34
|
+
<input type="checkbox" data-checklist-target="item" checked />
|
|
35
|
+
Buy milk
|
|
36
|
+
</label>
|
|
37
|
+
|
|
38
|
+
<label>
|
|
39
|
+
<input type="checkbox" data-checklist-target="item" />
|
|
40
|
+
Walk the dog
|
|
41
|
+
</label>
|
|
42
|
+
</div>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Disabled items (`disabled` attribute) are excluded from both aggregation and bulk toggling — the controller filters them out via `enabledItems()`. `indeterminate` is a JS-only property with no HTML attribute; it is set on connect and after every change, never rendered server-side.
|
|
@@ -53,37 +53,7 @@ Owns the trigger input, popover visibility, and hidden value. Always co-located
|
|
|
53
53
|
|
|
54
54
|
## input-formatter
|
|
55
55
|
|
|
56
|
-
Formats and displays values. Always co-located with `input-combobox`.
|
|
57
|
-
|
|
58
|
-
**Targets**
|
|
59
|
-
|
|
60
|
-
| Target | Element | Description |
|
|
61
|
-
| -------- | ------------------------ | ------------------------------------------------------------------------- |
|
|
62
|
-
| `input` | `<input>` or any element | Write destination — sets `.value` for `<input>`, `.textContent` otherwise |
|
|
63
|
-
| `toggle` | `<button>` | Reveal/conceal button (maskable/password types only) |
|
|
64
|
-
|
|
65
|
-
**Values**
|
|
66
|
-
|
|
67
|
-
| Value | Type | Default | Description |
|
|
68
|
-
| ---------- | ------- | --------- | --------------------------------------------------------- |
|
|
69
|
-
| `format` | String | `"plain"` | `plain` \| `password` \| `creditCard` \| `date` \| `time` |
|
|
70
|
-
| `options` | Object | `{}` | Formatter options (e.g. `{ format: "h12" }` for time) |
|
|
71
|
-
| `revealed` | Boolean | `false` | Whether a masked value is currently revealed |
|
|
72
|
-
|
|
73
|
-
**Methods**
|
|
74
|
-
|
|
75
|
-
| Method | Wired via | Description |
|
|
76
|
-
| ----------------- | ------------------------ | -------------------------------------------------------------------------------- |
|
|
77
|
-
| `format(value)` | — | Programmatic API — normalises, formats, writes to target, dispatches `formatted` |
|
|
78
|
-
| `onChange(event)` | `input-combobox:changed` | Event adapter — extracts `event.detail.value`, calls `format(value)` |
|
|
79
|
-
| `onPaste(event)` | `clipboard:pasted` | Event adapter — normalises and validates pasted text, calls `format(value)` |
|
|
80
|
-
| `toggle()` | `data-action` | Action — flips `revealedValue` (maskable / password types only) |
|
|
81
|
-
|
|
82
|
-
**Dispatches**
|
|
83
|
-
|
|
84
|
-
| Event | Detail | When |
|
|
85
|
-
| --------------------------- | ----------- | --------------------------------------- |
|
|
86
|
-
| `input-formatter:formatted` | `{ value }` | After every write to the `input` target |
|
|
56
|
+
Formats and displays values. Always co-located with `input-combobox`. See [input-formatter.md](input-formatter.md) for targets, values, methods, and dispatches.
|
|
87
57
|
|
|
88
58
|
---
|
|
89
59
|
|
|
@@ -34,10 +34,7 @@ Shows a clear button when an input has a value, hides it when empty, and clears
|
|
|
34
34
|
## Accessibility
|
|
35
35
|
|
|
36
36
|
- The clear button must carry `aria-label="Clear search"` — the controller never overrides it.
|
|
37
|
-
-
|
|
38
|
-
- After clearing, focus returns to the input so the user can type immediately (WCAG 2.4.3 Focus Order).
|
|
39
|
-
- Pressing Escape inside the input clears it when the field has a value; the event's default is prevented to avoid closing parent overlays unintentionally.
|
|
40
|
-
- No `aria-live` region is needed — clearing is user-initiated and the button's disappearance is self-explanatory.
|
|
37
|
+
- See [ARIA.md's Input Clearable pattern](../../../ARIA.md) for the clear-button tab-order, Escape-key, and focus-return contract.
|
|
41
38
|
|
|
42
39
|
## Notes
|
|
43
40
|
|
|
@@ -6,18 +6,20 @@ Formats, masks, and reveals values written to an input element. Handles password
|
|
|
6
6
|
|
|
7
7
|
## Targets
|
|
8
8
|
|
|
9
|
-
| Target | Element | Description
|
|
10
|
-
| -------- | ------------------------ |
|
|
11
|
-
| `input` | `<input>` or any element | Write destination — sets `.value` for `<input>`, `.textContent` otherwise
|
|
12
|
-
| `toggle` | `<button>` | Reveal/conceal button; hidden at connect when the formatter is not maskable
|
|
9
|
+
| Target | Element | Description |
|
|
10
|
+
| -------- | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
11
|
+
| `input` | `<input>` or any element | Write destination — sets `.value` for `<input>`, `.textContent` otherwise |
|
|
12
|
+
| `toggle` | `<button>` | Reveal/conceal button; hidden at connect when the formatter is not maskable |
|
|
13
|
+
| `cell` | `<div>` (any element) | Optional character cells; when present, the canonical value is painted one character per cell, or (grouped mode — cell count equals `groups.length`) one group-sized slice per cell — see [character-cells](../plumber/character-cells.md) |
|
|
13
14
|
|
|
14
15
|
## Values
|
|
15
16
|
|
|
16
|
-
| Value | Type | Default | Description
|
|
17
|
-
| ---------- | ------- | --------- |
|
|
18
|
-
| `format` | String | `"plain"` | Formatter type — see [Formatters](#formatters) for valid identifiers
|
|
19
|
-
| `options` | Object | `{}` | Formatter-specific options (e.g. `{ locale: "en-US" }` for currency)
|
|
20
|
-
| `revealed` | Boolean | `false` | Whether a masked value is currently revealed; managed by `toggle()`
|
|
17
|
+
| Value | Type | Default | Description |
|
|
18
|
+
| ---------- | ------- | --------- | --------------------------------------------------------------------------------- |
|
|
19
|
+
| `format` | String | `"plain"` | Formatter type — see [Formatters](#formatters) for valid identifiers |
|
|
20
|
+
| `options` | Object | `{}` | Formatter-specific options (e.g. `{ locale: "en-US" }` for currency) |
|
|
21
|
+
| `revealed` | Boolean | `false` | Whether a masked value is currently revealed; managed by `toggle()` |
|
|
22
|
+
| `groups` | Array | `[]` | Cell group widths (e.g. `[4,4,4,4]`); overrides the formatter's own grouping hint |
|
|
21
23
|
|
|
22
24
|
## Methods
|
|
23
25
|
|
|
@@ -27,24 +29,29 @@ Formats, masks, and reveals values written to an input element. Handles password
|
|
|
27
29
|
| `toggle()` | `data-action` | Action — flips `revealedValue`; no-op unless `format` is `"password"` or formatter is maskable |
|
|
28
30
|
| `onChange(event)` | `input-combobox:changed` | Event adapter — extracts `event.detail.value`, calls `format(value)` |
|
|
29
31
|
| `onPaste(event)` | `clipboard:pasted` | Event adapter — normalises and validates pasted text, calls `format(value)` |
|
|
32
|
+
| `onInput(event)` | `input` DOM event | Event adapter — re-formats from the input's current value |
|
|
33
|
+
| `onFocus(event)` | `focus` DOM event | Event adapter — redraws cells so the caret cell appears |
|
|
34
|
+
| `onBlur(event)` | `blur` DOM event | Event adapter — redraws cells so the caret cell clears |
|
|
30
35
|
|
|
31
36
|
## Dispatches
|
|
32
37
|
|
|
33
|
-
| Event | Detail | When
|
|
34
|
-
| --------------------------- | ----------- |
|
|
35
|
-
| `input-formatter:formatted` | `{ value }` | After every write to the `input` target
|
|
38
|
+
| Event | Detail | When |
|
|
39
|
+
| --------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
40
|
+
| `input-formatter:formatted` | `{ value }` | After every write to the `input` target |
|
|
41
|
+
| `input-formatter:filled` | `{ value }` | When a valid, cell-capable formatter's value reaches its expected cell length — auto-submit hook. Fires once per fill, on the transition into "full"; not on connect with an already-full value. |
|
|
36
42
|
|
|
37
43
|
## Formatters
|
|
38
44
|
|
|
39
|
-
| `type` | Description
|
|
40
|
-
| -------------- |
|
|
41
|
-
| `"plain"` | No-op — passes the value through unchanged
|
|
42
|
-
| `"password"` | Switches `input[type]` between `"password"` and `"text"` on reveal
|
|
43
|
-
| `"creditCard"` | Groups digits as `#### #### #### ####`
|
|
44
|
-
| `"phone"` | Formats as a local phone number
|
|
45
|
-
| `"currency"` | Locale-aware thousands separator and decimal places
|
|
46
|
-
| `"date"` | Locale-aware date string
|
|
47
|
-
| `"time"` | Locale-aware time string
|
|
45
|
+
| `type` | Description |
|
|
46
|
+
| -------------- | ------------------------------------------------------------------------------------------------------------------ |
|
|
47
|
+
| `"plain"` | No-op — passes the value through unchanged |
|
|
48
|
+
| `"password"` | Switches `input[type]` between `"password"` and `"text"` on reveal |
|
|
49
|
+
| `"creditCard"` | Groups digits as `#### #### #### ####` |
|
|
50
|
+
| `"phone"` | Formats as a local phone number |
|
|
51
|
+
| `"currency"` | Locale-aware thousands separator and decimal places |
|
|
52
|
+
| `"date"` | Locale-aware date string |
|
|
53
|
+
| `"time"` | Locale-aware time string |
|
|
54
|
+
| `"code"` | Charset-filtered fixed-length codes (OTP, PIN); options `{ charset: "digits"\|"letters"\|"alphanumeric", length }` |
|
|
48
55
|
|
|
49
56
|
Custom formatters can be registered at runtime:
|
|
50
57
|
|
|
@@ -83,8 +90,66 @@ Formatter.register('iban', {
|
|
|
83
90
|
</div>
|
|
84
91
|
```
|
|
85
92
|
|
|
93
|
+
### One-time code entry (cells)
|
|
94
|
+
|
|
95
|
+
```html
|
|
96
|
+
<div
|
|
97
|
+
data-controller="input-formatter"
|
|
98
|
+
data-input-formatter-format-value="code"
|
|
99
|
+
data-input-formatter-options-value='{"charset":"digits","length":6}'
|
|
100
|
+
>
|
|
101
|
+
<label for="otp" class="sr-only">Verification code</label>
|
|
102
|
+
<div class="cells-row">
|
|
103
|
+
<div data-input-formatter-target="cell"></div>
|
|
104
|
+
<div data-input-formatter-target="cell"></div>
|
|
105
|
+
<div data-input-formatter-target="cell"></div>
|
|
106
|
+
<div data-input-formatter-target="cell"></div>
|
|
107
|
+
<div data-input-formatter-target="cell"></div>
|
|
108
|
+
<div data-input-formatter-target="cell"></div>
|
|
109
|
+
</div>
|
|
110
|
+
<input
|
|
111
|
+
id="otp"
|
|
112
|
+
data-input-formatter-target="input"
|
|
113
|
+
autocomplete="one-time-code"
|
|
114
|
+
inputmode="numeric"
|
|
115
|
+
maxlength="6"
|
|
116
|
+
data-action="input->input-formatter#onInput focus->input-formatter#onFocus blur->input-formatter#onBlur"
|
|
117
|
+
/>
|
|
118
|
+
</div>
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Cells are decoration; style them via `[data-filled]`, `[data-caret]`, `[data-group-end]`, `[data-inactive]`. See [ARIA.md's Code Input pattern](../../../ARIA.md) for the overlay/labeling requirements.
|
|
122
|
+
|
|
123
|
+
### Credit card formatting (grouped cells)
|
|
124
|
+
|
|
125
|
+
Rendering exactly `groups.length` cells (instead of one cell per digit) puts the plumber into
|
|
126
|
+
grouped mode: each cell displays its whole group of digits. Separators between cells (e.g. a
|
|
127
|
+
dash) are authored HTML, never inserted by the plumber — see
|
|
128
|
+
[character-cells](../plumber/character-cells.md#grouped-mode).
|
|
129
|
+
|
|
130
|
+
```html
|
|
131
|
+
<div data-controller="input-formatter" data-input-formatter-format-value="creditCard">
|
|
132
|
+
<div class="cells-row">
|
|
133
|
+
<span data-input-formatter-target="cell"></span>
|
|
134
|
+
<span aria-hidden="true">-</span>
|
|
135
|
+
<span data-input-formatter-target="cell"></span>
|
|
136
|
+
<span aria-hidden="true">-</span>
|
|
137
|
+
<span data-input-formatter-target="cell"></span>
|
|
138
|
+
<span aria-hidden="true">-</span>
|
|
139
|
+
<span data-input-formatter-target="cell"></span>
|
|
140
|
+
</div>
|
|
141
|
+
<input
|
|
142
|
+
data-input-formatter-target="input"
|
|
143
|
+
autocomplete="cc-number"
|
|
144
|
+
inputmode="numeric"
|
|
145
|
+
maxlength="16"
|
|
146
|
+
data-action="input->input-formatter#onInput focus->input-formatter#onFocus blur->input-formatter#onBlur"
|
|
147
|
+
/>
|
|
148
|
+
</div>
|
|
149
|
+
```
|
|
150
|
+
|
|
86
151
|
## Accessibility
|
|
87
152
|
|
|
88
|
-
- The `toggle` button must carry `aria-label` describing the action: `"Show password"` / `"Hide password"`.
|
|
89
|
-
- `aria-pressed` is managed automatically — initialised to `"false"`, flipped to `"true"` when the value is revealed.
|
|
90
153
|
- Password `type` attribute switches between `"password"` (masked) and `"text"` (revealed), so screen readers announce the field type correctly.
|
|
154
|
+
- See [ARIA.md's Password Reveal pattern](../../../ARIA.md) for the `toggle` button's `aria-label`/`aria-pressed` requirements.
|
|
155
|
+
- Cell display: see [ARIA.md's Code Input pattern](../../../ARIA.md).
|
|
@@ -53,8 +53,5 @@ Focus is trapped inside `modal` target using `FocusTrap`. Escape key and click-o
|
|
|
53
53
|
|
|
54
54
|
## Accessibility
|
|
55
55
|
|
|
56
|
-
- Focus moves into the modal on open; returns to the trigger on close
|
|
57
|
-
- Focus is trapped — Tab/Shift+Tab cycle within the modal
|
|
58
|
-
- Escape closes the modal
|
|
59
56
|
- `body` scroll is locked while open (custom mode)
|
|
60
|
-
-
|
|
57
|
+
- See [ARIA.md's Modal pattern](../../../ARIA.md) for focus management and the status-announcement contract
|
|
@@ -108,6 +108,4 @@ Selection events (from combobox sub-controllers, menu items, etc.) can be wired
|
|
|
108
108
|
|
|
109
109
|
## Accessibility
|
|
110
110
|
|
|
111
|
-
|
|
112
|
-
- `panel` should have an appropriate `role` (`dialog`, `tooltip`, `listbox`, etc.).
|
|
113
|
-
- Esc and outside-click both close the panel; focus returns to the `trigger` on close.
|
|
111
|
+
See [ARIA.md's Popover pattern](../../../ARIA.md) for role, `aria-haspopup`/`aria-expanded`, and focus-return requirements.
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Progress
|
|
2
|
+
|
|
3
|
+
Value-driven progress indicator supporting three render variants: a linear bar, an SVG ring, and a native `<meter>`.
|
|
4
|
+
|
|
5
|
+
## Stimulus Identifier
|
|
6
|
+
|
|
7
|
+
`progress`
|
|
8
|
+
|
|
9
|
+
## Targets
|
|
10
|
+
|
|
11
|
+
| Name | Element | Purpose |
|
|
12
|
+
| ------- | --------------------------------- | ----------------------------------------------------------------------------------- |
|
|
13
|
+
| `fill` | `<div>` (bar) / `<circle>` (ring) | Element whose `width` (bar) or `stroke-dasharray`/`stroke-dashoffset` (ring) is set |
|
|
14
|
+
| `meter` | `<meter>` | Present only for `variant: "meter"` — native element, attributes synced directly |
|
|
15
|
+
|
|
16
|
+
## Values
|
|
17
|
+
|
|
18
|
+
| Name | Type | Default | Purpose |
|
|
19
|
+
| ----------------------- | ------- | ------- | ------------------------------------------------------------------------- |
|
|
20
|
+
| `variant` | String | `"bar"` | `"bar"` \| `"ring"` \| `"meter"` |
|
|
21
|
+
| `current` | Number | `0` | Current value |
|
|
22
|
+
| `min` | Number | `0` | Range minimum |
|
|
23
|
+
| `max` | Number | `100` | Range maximum |
|
|
24
|
+
| `optimum` | Number | — | Meter-only; maps to native `<meter optimum>` |
|
|
25
|
+
| `low` | Number | — | Meter-only; maps to native `<meter low>` |
|
|
26
|
+
| `high` | Number | — | Meter-only; maps to native `<meter high>` |
|
|
27
|
+
| `indeterminate` | Boolean | `false` | Suppresses `aria-valuenow`; toggles the `sp-progress-indeterminate` class |
|
|
28
|
+
| `indeterminateFraction` | Number | `0.25` | Static bar width / ring arc fraction rendered while indeterminate |
|
|
29
|
+
|
|
30
|
+
## Methods
|
|
31
|
+
|
|
32
|
+
| Method | Wired via | Purpose |
|
|
33
|
+
| ---------------------------- | ----------------------- | --------------------------------------------------------------------------------------------------------- |
|
|
34
|
+
| `setValue(value)` | — | Programmatic API — clamps to `[min, max]`, updates `currentValue`, dispatches `progress:changed` |
|
|
35
|
+
| `currentValueChanged(value)` | Stimulus value callback | Recalculates fill/meter attrs whenever `current` changes (covers `setValue()` and direct attribute edits) |
|
|
36
|
+
|
|
37
|
+
## Dispatches
|
|
38
|
+
|
|
39
|
+
| Event | Detail | When |
|
|
40
|
+
| ------------------ | --------------------- | ------------------------------------ |
|
|
41
|
+
| `progress:changed` | `{ value, min, max }` | After `setValue()` updates the value |
|
|
42
|
+
|
|
43
|
+
## Example HTML
|
|
44
|
+
|
|
45
|
+
```html
|
|
46
|
+
<!-- Bar -->
|
|
47
|
+
<div
|
|
48
|
+
role="progressbar"
|
|
49
|
+
data-controller="progress"
|
|
50
|
+
data-progress-current-value="30"
|
|
51
|
+
data-progress-min-value="0"
|
|
52
|
+
data-progress-max-value="100"
|
|
53
|
+
>
|
|
54
|
+
<div data-progress-target="fill"></div>
|
|
55
|
+
</div>
|
|
56
|
+
|
|
57
|
+
<!-- Ring -->
|
|
58
|
+
<svg
|
|
59
|
+
role="progressbar"
|
|
60
|
+
data-controller="progress"
|
|
61
|
+
data-progress-variant-value="ring"
|
|
62
|
+
data-progress-current-value="25"
|
|
63
|
+
data-progress-max-value="100"
|
|
64
|
+
>
|
|
65
|
+
<circle data-progress-target="fill" r="40"></circle>
|
|
66
|
+
</svg>
|
|
67
|
+
|
|
68
|
+
<!-- Meter -->
|
|
69
|
+
<meter
|
|
70
|
+
data-controller="progress"
|
|
71
|
+
data-progress-variant-value="meter"
|
|
72
|
+
data-progress-target="meter"
|
|
73
|
+
data-progress-current-value="40"
|
|
74
|
+
data-progress-min-value="0"
|
|
75
|
+
data-progress-max-value="100"
|
|
76
|
+
></meter>
|
|
77
|
+
|
|
78
|
+
<!-- Indeterminate -->
|
|
79
|
+
<div role="progressbar" data-controller="progress" data-progress-indeterminate-value="true">
|
|
80
|
+
<div data-progress-target="fill"></div>
|
|
81
|
+
</div>
|
|
82
|
+
```
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Reorderable
|
|
2
|
+
|
|
3
|
+
Reorders a vertical or horizontal list of items via pointer drag (a dedicated handle) or keyboard (`Alt+Arrow`, axis depends on `orientation`). No third-party drag library — built on the native Pointer Events API, backed by the [`Reorderable` plumber](../plumber/reorderable.md). Plain Arrow/Home/End keys move focus only, via [`RovingTabIndex`](../utility/accessibility.md).
|
|
4
|
+
|
|
5
|
+
## Stimulus Identifier
|
|
6
|
+
|
|
7
|
+
`reorderable`
|
|
8
|
+
|
|
9
|
+
## Targets
|
|
10
|
+
|
|
11
|
+
| Name | Element | Purpose |
|
|
12
|
+
| --------- | ------------------------------------------- | ---------------------------------------------------------------------------------- |
|
|
13
|
+
| `item` | Each reorderable row (`<li>`, `<tr>`, etc.) | Must have a stable `id` to appear in the `reorderable:reordered` event's `itemIds` |
|
|
14
|
+
| `handle` | Drag grip within each `item` | The only pointer-drag surface — wire `pointerdown`/`pointermove`/`pointerup` to it |
|
|
15
|
+
| `trigger` | The `<a>`/`<button>` inside an item, if any | Neutralized (via `aria-disabled`/`tabindex`) while `editingValue` is `true` |
|
|
16
|
+
|
|
17
|
+
## Values
|
|
18
|
+
|
|
19
|
+
| Name | Type | Default | Purpose |
|
|
20
|
+
| ------------- | ------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
21
|
+
| `moveKey` | String | `"Alt"` | Modifier key that, combined with the move keys below on a focused item, moves it. One of `Alt`, `Control`, `Shift`, `Meta`. |
|
|
22
|
+
| `editing` | Boolean | `false` | While `true`, pointer drag and `Alt+Arrow` move are active and every `trigger` target is neutralized. While `false`, drag/keyboard-move are inert no-ops and every `trigger` behaves normally. Plain Arrow/Home/End focus movement is unaffected either way. |
|
|
23
|
+
| `orientation` | String | `"vertical"` | Reorder axis: `"vertical"` or `"horizontal"`. Also passed through to [`RovingTabIndex`](../utility/accessibility.md). |
|
|
24
|
+
|
|
25
|
+
## Actions
|
|
26
|
+
|
|
27
|
+
| Name | Purpose |
|
|
28
|
+
| --------------- | ---------------------------------------------------------------------------------------------------------- |
|
|
29
|
+
| `onPointerDown` | Wire to `pointerdown` on the `handle` target — starts a drag (no-op unless `editingValue`) |
|
|
30
|
+
| `onPointerMove` | Wire to `pointermove` on the `handle` target — live-reorders while dragging (no-op unless `editingValue`) |
|
|
31
|
+
| `onPointerUp` | Wire to `pointerup` on the `handle` target — ends the drag (no-op unless `editingValue`) |
|
|
32
|
+
| `toggleEditing` | Flips `editingValue` — wire to e.g. `click->reorderable#toggleEditing` on an app-provided Edit/Done button |
|
|
33
|
+
| `enterEditing` | Sets `editingValue` to `true` explicitly |
|
|
34
|
+
| `exitEditing` | Sets `editingValue` to `false` explicitly |
|
|
35
|
+
|
|
36
|
+
## Keyboard
|
|
37
|
+
|
|
38
|
+
| Key | Behaviour |
|
|
39
|
+
| ------------------------------------------------------------------------------------ | ---------------------------------------------------------------------- |
|
|
40
|
+
| `ArrowDown`/`ArrowUp` (vertical) or `ArrowLeft`/`ArrowRight` (horizontal) | Focus next/previous item (wraps) — unchanged `RovingTabIndex` behavior |
|
|
41
|
+
| `Home` / `End` | Focus first/last item |
|
|
42
|
+
| `Alt+ArrowDown` (vertical) / `Alt+ArrowRight` (horizontal, `Left` under `dir="rtl"`) | Move the focused item forward one position, keeps focus on it |
|
|
43
|
+
| `Alt+ArrowUp` (vertical) / `Alt+ArrowLeft` (horizontal, `Right` under `dir="rtl"`) | Move the focused item back one position, keeps focus on it |
|
|
44
|
+
|
|
45
|
+
## Dispatches
|
|
46
|
+
|
|
47
|
+
| Event | Detail | When |
|
|
48
|
+
| ----------------------- | ----------------------- | ------------------------------------------------------------------------------------------------- |
|
|
49
|
+
| `reorderable:reordered` | `{ itemIds: string[] }` | After a drag ends or a keyboard move completes. Items without an `id` are omitted from `itemIds`. |
|
|
50
|
+
|
|
51
|
+
## Example HTML
|
|
52
|
+
|
|
53
|
+
```html
|
|
54
|
+
<ul data-controller="reorderable">
|
|
55
|
+
<li id="row-1" data-reorderable-target="item" tabindex="0">
|
|
56
|
+
<span
|
|
57
|
+
data-reorderable-target="handle"
|
|
58
|
+
data-action="pointerdown->reorderable#onPointerDown pointermove->reorderable#onPointerMove pointerup->reorderable#onPointerUp"
|
|
59
|
+
>
|
|
60
|
+
::
|
|
61
|
+
</span>
|
|
62
|
+
First item
|
|
63
|
+
</li>
|
|
64
|
+
<li id="row-2" data-reorderable-target="item" tabindex="-1">
|
|
65
|
+
<span
|
|
66
|
+
data-reorderable-target="handle"
|
|
67
|
+
data-action="pointerdown->reorderable#onPointerDown pointermove->reorderable#onPointerMove pointerup->reorderable#onPointerUp"
|
|
68
|
+
>
|
|
69
|
+
::
|
|
70
|
+
</span>
|
|
71
|
+
Second item
|
|
72
|
+
</li>
|
|
73
|
+
</ul>
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Notes
|
|
77
|
+
|
|
78
|
+
- Persistence is not built in — listen for `reorderable:reordered` and send `event.detail.itemIds` to your backend.
|
|
79
|
+
- Single-list only. Grid orientation and cross-list drag are not supported.
|
|
80
|
+
- The dragged/moved item is repositioned live in the DOM — there is no placeholder or ghost element.
|
|
81
|
+
- See [ARIA.md's Reorderable pattern](../../../ARIA.md) for the drag/keyboard-move focus-and-announcement contract and the `trigger` neutralization rules.
|
|
82
|
+
- Pointer clicks on a `trigger` are not blocked by JS. Apps/themes must add their own CSS rule to block them while editing, e.g. `[data-reorderable-editing-value="true"] [data-reorderable-target="trigger"] { pointer-events: none }` — keeps the controller content-agnostic about link/button internals.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# visibility
|
|
2
|
+
|
|
3
|
+
Shows/hides a `content` element, keeping it within the viewport and dismissing it on click-outside. Backed by the `Visibility`, `Shifter`, and `Dismisser` plumbers.
|
|
4
|
+
|
|
5
|
+
## Targets
|
|
6
|
+
|
|
7
|
+
| Target | Description |
|
|
8
|
+
| --------- | --------------------------------------------------------------------------------- |
|
|
9
|
+
| `content` | Element to show/hide. Optional — `toggle()`/`dismissed()` are no-ops when absent. |
|
|
10
|
+
|
|
11
|
+
## Methods
|
|
12
|
+
|
|
13
|
+
| Method | Wired via | Description |
|
|
14
|
+
| ------------- | ----------------- | -------------------------------------------------------------------------- |
|
|
15
|
+
| `toggle()` | `data-action` | Shows `content` if hidden (shifting it into viewport), hides it if visible |
|
|
16
|
+
| `dismissed()` | Dismisser plumber | Plumber callback — hides `content` on click-outside |
|
|
17
|
+
|
|
18
|
+
## Example
|
|
19
|
+
|
|
20
|
+
```html
|
|
21
|
+
<div data-controller="visibility">
|
|
22
|
+
<button data-action="visibility#toggle">Toggle</button>
|
|
23
|
+
<div data-visibility-target="content" hidden>Content</div>
|
|
24
|
+
</div>
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Notes
|
|
28
|
+
|
|
29
|
+
- See [docs/plumber/visibility.md](../plumber/visibility.md) for the show/hide API and dispatched events.
|
|
30
|
+
- See [docs/plumber/shifter.md](../plumber/shifter.md) for the viewport-shifting behavior applied to `content` on show.
|
|
31
|
+
- See [docs/component/dismisser.md](dismisser.md) for click-outside-to-dismiss behavior.
|