stimulus_plumbers 0.4.5 → 0.4.9
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 +23 -0
- data/README.md +15 -1
- data/app/assets/javascripts/stimulus-plumbers/controllers.manifest.json +331 -20
- data/app/assets/javascripts/stimulus-plumbers/index.es.js +493 -226
- 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 +2 -1
- data/docs/component/calendar.md +18 -20
- data/docs/component/card.md +14 -12
- data/docs/component/checklist.md +153 -0
- data/docs/component/combobox.md +2 -2
- data/docs/component/form.md +32 -6
- data/docs/component/icon.md +7 -5
- data/docs/component/indicator.md +55 -0
- data/docs/component/list.md +4 -5
- data/docs/component/ordered_list.md +118 -0
- data/docs/component/plumber.md +43 -0
- data/docs/component/popover.md +1 -2
- data/docs/component/progress.md +65 -0
- data/docs/component/theme.md +15 -2
- data/docs/component/timeline.md +49 -14
- data/docs/guide.md +39 -0
- data/lib/generators/stimulus_plumbers/install/install_generator.rb +33 -0
- data/lib/stimulus_plumbers/components/button.rb +2 -1
- data/lib/stimulus_plumbers/components/card.rb +1 -1
- 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/combobox.rb +1 -1
- 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 +2 -1
- data/lib/stimulus_plumbers/components/list/item.rb +1 -1
- 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 +3 -2
- data/lib/stimulus_plumbers/components/timeline/group.rb +5 -3
- data/lib/stimulus_plumbers/engine.rb +4 -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/generators/css_entrypoint.rb +80 -0
- data/lib/stimulus_plumbers/generators/tokens_directive.rb +24 -0
- 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/plumber/slots.rb +10 -3
- 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 +39 -3
- data/lib/stimulus_plumbers/version.rb +1 -1
- data/lib/stimulus_plumbers.rb +10 -0
- data/lib/tasks/stimulus_plumbers.rake +18 -0
- data/vendor/ARIA.md +108 -0
- data/vendor/component/manifest.json +212 -0
- data/vendor/controller/docs/calendar.md +212 -0
- data/vendor/controller/docs/checklist.md +45 -0
- data/vendor/controller/docs/clipboard.md +61 -0
- data/vendor/controller/docs/combobox.md +212 -0
- data/vendor/controller/docs/dismisser.md +39 -0
- data/vendor/controller/docs/flipper.md +46 -0
- data/vendor/controller/docs/input-clearable.md +43 -0
- data/vendor/controller/docs/input-formatter.md +89 -0
- data/vendor/controller/docs/modal.md +57 -0
- data/vendor/controller/docs/panner.md +24 -0
- data/vendor/controller/docs/popover.md +111 -0
- data/vendor/controller/docs/progress.md +82 -0
- data/vendor/controller/docs/reorderable.md +82 -0
- data/vendor/controller/docs/timeline.md +84 -0
- data/vendor/controller/docs/visibility.md +31 -0
- data/vendor/controller/guide.md +16 -0
- data/vendor/{controllers.manifest.json → controller/manifest.json} +331 -20
- metadata +44 -2
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# input-formatter
|
|
2
|
+
|
|
3
|
+
Formats, masks, and reveals values written to an input element. Handles password reveal toggling and structured display formatting for credit cards, phone numbers, currencies, dates, and times.
|
|
4
|
+
|
|
5
|
+
> When co-located with `input-combobox`, see [combobox.md](combobox.md) for the full event flow and wiring.
|
|
6
|
+
|
|
7
|
+
## Targets
|
|
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 |
|
|
13
|
+
|
|
14
|
+
## Values
|
|
15
|
+
|
|
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()` |
|
|
21
|
+
|
|
22
|
+
## Methods
|
|
23
|
+
|
|
24
|
+
| Method | Wired via | Description |
|
|
25
|
+
| ----------------- | ------------------------ | ---------------------------------------------------------------------------------------------- |
|
|
26
|
+
| `format(value)` | — | Programmatic API — normalises, formats/masks, writes to `input` target, dispatches `formatted` |
|
|
27
|
+
| `toggle()` | `data-action` | Action — flips `revealedValue`; no-op unless `format` is `"password"` or formatter is maskable |
|
|
28
|
+
| `onChange(event)` | `input-combobox:changed` | Event adapter — extracts `event.detail.value`, calls `format(value)` |
|
|
29
|
+
| `onPaste(event)` | `clipboard:pasted` | Event adapter — normalises and validates pasted text, calls `format(value)` |
|
|
30
|
+
|
|
31
|
+
## Dispatches
|
|
32
|
+
|
|
33
|
+
| Event | Detail | When |
|
|
34
|
+
| --------------------------- | ----------- | --------------------------------------- |
|
|
35
|
+
| `input-formatter:formatted` | `{ value }` | After every write to the `input` target |
|
|
36
|
+
|
|
37
|
+
## Formatters
|
|
38
|
+
|
|
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 |
|
|
48
|
+
|
|
49
|
+
Custom formatters can be registered at runtime:
|
|
50
|
+
|
|
51
|
+
```js
|
|
52
|
+
import { Formatter } from '@stimulus-plumbers/controllers';
|
|
53
|
+
|
|
54
|
+
Formatter.register('iban', {
|
|
55
|
+
normalize: (raw) => raw.replace(/\s/g, '').toUpperCase(),
|
|
56
|
+
validate: (value) => /^[A-Z]{2}\d{2}[A-Z0-9]+$/.test(value),
|
|
57
|
+
format: (value) => value.replace(/(.{4})/g, '$1 ').trim(),
|
|
58
|
+
});
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Examples
|
|
62
|
+
|
|
63
|
+
### Password reveal
|
|
64
|
+
|
|
65
|
+
```html
|
|
66
|
+
<div data-controller="input-formatter" data-input-formatter-format-value="password">
|
|
67
|
+
<input type="password" data-input-formatter-target="input" />
|
|
68
|
+
<button
|
|
69
|
+
type="button"
|
|
70
|
+
aria-label="Show password"
|
|
71
|
+
aria-pressed="false"
|
|
72
|
+
data-input-formatter-target="toggle"
|
|
73
|
+
data-action="click->input-formatter#toggle"
|
|
74
|
+
></button>
|
|
75
|
+
</div>
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Credit card formatting
|
|
79
|
+
|
|
80
|
+
```html
|
|
81
|
+
<div data-controller="input-formatter" data-input-formatter-format-value="creditCard">
|
|
82
|
+
<input type="text" data-input-formatter-target="input" />
|
|
83
|
+
</div>
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Accessibility
|
|
87
|
+
|
|
88
|
+
- Password `type` attribute switches between `"password"` (masked) and `"text"` (revealed), so screen readers announce the field type correctly.
|
|
89
|
+
- See [ARIA.md's Password Reveal pattern](../../../ARIA.md) for the `toggle` button's `aria-label`/`aria-pressed` requirements.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# modal
|
|
2
|
+
|
|
3
|
+
Implements the [WAI-ARIA Dialog (Modal) pattern](https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/). Supports both native `<dialog>` elements and custom overlay implementations.
|
|
4
|
+
|
|
5
|
+
## Targets
|
|
6
|
+
|
|
7
|
+
| Target | Description |
|
|
8
|
+
| --------- | ----------------------------------------------------------- |
|
|
9
|
+
| `modal` | The dialog element (required) |
|
|
10
|
+
| `overlay` | Optional wrapper shown/hidden around `modal` in custom mode |
|
|
11
|
+
|
|
12
|
+
## Methods
|
|
13
|
+
|
|
14
|
+
| Method | Wired via | Description |
|
|
15
|
+
| -------------- | ------------------------- | ------------------------------------------------------------------------------ |
|
|
16
|
+
| `open(event)` | `data-action` | Action — opens the modal, traps focus inside |
|
|
17
|
+
| `close(event)` | `data-action`, Escape key | Action — closes the modal, restores focus to trigger |
|
|
18
|
+
| `dismissed()` | Dismisser plumber | Plumber callback — called on click-outside (custom mode only); calls `close()` |
|
|
19
|
+
|
|
20
|
+
## Native `<dialog>` usage
|
|
21
|
+
|
|
22
|
+
```html
|
|
23
|
+
<div data-controller="modal">
|
|
24
|
+
<button data-action="modal#open">Open</button>
|
|
25
|
+
|
|
26
|
+
<dialog data-modal-target="modal" aria-labelledby="modal-title" aria-modal="true">
|
|
27
|
+
<h2 id="modal-title">Confirm action</h2>
|
|
28
|
+
<p>Are you sure?</p>
|
|
29
|
+
<button data-action="modal#close">Cancel</button>
|
|
30
|
+
<button>Confirm</button>
|
|
31
|
+
</dialog>
|
|
32
|
+
</div>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The browser handles backdrop rendering. Clicking outside the dialog closes it via `onBackdropClick` (an internal handler, not part of the public API).
|
|
36
|
+
|
|
37
|
+
## Custom overlay usage
|
|
38
|
+
|
|
39
|
+
```html
|
|
40
|
+
<div data-controller="modal">
|
|
41
|
+
<button data-action="modal#open">Open</button>
|
|
42
|
+
|
|
43
|
+
<div data-modal-target="overlay" hidden>
|
|
44
|
+
<div data-modal-target="modal" role="dialog" aria-modal="true" aria-labelledby="modal-title">
|
|
45
|
+
<h2 id="modal-title">Title</h2>
|
|
46
|
+
<button data-action="modal#close">Close</button>
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Focus is trapped inside `modal` target using `FocusTrap`. Escape key and click-outside (via `dismissed()`) both close the modal.
|
|
53
|
+
|
|
54
|
+
## Accessibility
|
|
55
|
+
|
|
56
|
+
- `body` scroll is locked while open (custom mode)
|
|
57
|
+
- See [ARIA.md's Modal pattern](../../../ARIA.md) for focus management and the status-announcement contract
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# panner
|
|
2
|
+
|
|
3
|
+
Keeps a content element within the viewport by applying a CSS `translate` transform when the window resizes. Backed by the `Shifter` plumber.
|
|
4
|
+
|
|
5
|
+
## Targets
|
|
6
|
+
|
|
7
|
+
| Target | Description |
|
|
8
|
+
| --------- | -------------------------------------------------------------------------- |
|
|
9
|
+
| `content` | Element to keep in-viewport. Defaults to the controller element if absent. |
|
|
10
|
+
|
|
11
|
+
## Example
|
|
12
|
+
|
|
13
|
+
```html
|
|
14
|
+
<div data-controller="panner">
|
|
15
|
+
<div data-panner-target="content">
|
|
16
|
+
<!-- content that must stay visible when the window resizes -->
|
|
17
|
+
</div>
|
|
18
|
+
</div>
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Notes
|
|
22
|
+
|
|
23
|
+
- Shifting is skipped when the `content` target is hidden.
|
|
24
|
+
- See [docs/plumber/shifter.md](../plumber/shifter.md) for the full options API (`boundaries`, `events`, `respectMotion`).
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# popover
|
|
2
|
+
|
|
3
|
+
Shows and hides a panel with optional remote loading. Owns visibility, `aria-expanded`, outside-click dismissal, and focus management. Backed by the `Visibility`, `Dismisser`, and `ContentLoader` plumbers.
|
|
4
|
+
|
|
5
|
+
This controller is also the visibility/dismissal layer for the combobox family — `input-combobox` runs alongside it and delegates open/close to `popover` (see [combobox docs](./combobox.md)).
|
|
6
|
+
|
|
7
|
+
## Targets
|
|
8
|
+
|
|
9
|
+
| Target | Description |
|
|
10
|
+
| ---------- | -------------------------------------------------------------------------- |
|
|
11
|
+
| `trigger` | The activator element — opens/toggles the panel and tracks `aria-expanded` |
|
|
12
|
+
| `panel` | The element to show/hide (and load remote content into) |
|
|
13
|
+
| `template` | Optional `<template>` or element whose HTML is used as initial content |
|
|
14
|
+
| `loader` | Optional element shown during remote load |
|
|
15
|
+
|
|
16
|
+
## Values
|
|
17
|
+
|
|
18
|
+
| Value | Type | Default | Description |
|
|
19
|
+
| --------------- | ------- | ---------------- | ---------------------------------------------------- |
|
|
20
|
+
| `url` | String | — | Remote URL to fetch content from |
|
|
21
|
+
| `reload` | String | `"never"` | When to reload: `"never"` \| `"always"` \| `"stale"` |
|
|
22
|
+
| `staleAfter` | Number | `3600` | Seconds after which content is considered stale |
|
|
23
|
+
| `loadedAt` | String | — | ISO timestamp of last load (set automatically) |
|
|
24
|
+
| `closeOnSelect` | Boolean | `true` | Whether a `#closeOnSelect` call dismisses the panel |
|
|
25
|
+
| `announceOpen` | String | `"Panel opened"` | Screen-reader announcement on panel show |
|
|
26
|
+
| `announceClose` | String | `"Panel closed"` | Screen-reader announcement on panel hide |
|
|
27
|
+
|
|
28
|
+
## Methods
|
|
29
|
+
|
|
30
|
+
| Method | Wired via | Description |
|
|
31
|
+
| ---------------------------- | --------------------- | ----------------------------------------------------------------------------------------------------------- |
|
|
32
|
+
| `open()` | `data-action` | Action — shows the panel |
|
|
33
|
+
| `close()` | `data-action` | Action — hides the panel (used by Esc, close buttons, outside-click) |
|
|
34
|
+
| `toggle()` | `data-action` | Action — shows when hidden, hides when visible |
|
|
35
|
+
| `closeOnSelect()` | `data-action` | Action — hides the panel only when `closeOnSelect` value is `true`; used by selection events |
|
|
36
|
+
| `shown()` | Visibility plumber | Plumber callback — triggers `load()`, then moves focus into the panel |
|
|
37
|
+
| `hidden()` | Visibility plumber | Plumber callback — returns focus to the `trigger` |
|
|
38
|
+
| `dismissed()` | Dismisser plumber | Plumber callback — closes the panel on outside click |
|
|
39
|
+
| `canLoad()` | ContentLoader plumber | Plumber callback (gate) — returns `false` for `<turbo-frame>` panels (sets `src` instead); `true` otherwise |
|
|
40
|
+
| `contentLoading()` | ContentLoader plumber | Plumber callback — shows the `loader` target while fetching |
|
|
41
|
+
| `contentLoaded({ content })` | ContentLoader plumber | Plumber callback — inserts fetched content into the `panel` target, hides `loader` |
|
|
42
|
+
| `contentLoader()` | ContentLoader plumber | Plumber callback — returns static content from `template` target (if no URL) |
|
|
43
|
+
|
|
44
|
+
## Examples
|
|
45
|
+
|
|
46
|
+
### Static popover
|
|
47
|
+
|
|
48
|
+
```html
|
|
49
|
+
<div data-controller="popover">
|
|
50
|
+
<button
|
|
51
|
+
data-action="click->popover#toggle keydown.esc->popover#close"
|
|
52
|
+
data-popover-target="trigger"
|
|
53
|
+
aria-haspopup="dialog"
|
|
54
|
+
aria-expanded="false"
|
|
55
|
+
aria-controls="opts"
|
|
56
|
+
>
|
|
57
|
+
Open
|
|
58
|
+
</button>
|
|
59
|
+
|
|
60
|
+
<div id="opts" data-popover-target="panel" hidden role="dialog" aria-label="Options">
|
|
61
|
+
<p>Popover content</p>
|
|
62
|
+
<button data-action="click->popover#close">Close</button>
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Remote content (fetch on show)
|
|
68
|
+
|
|
69
|
+
```html
|
|
70
|
+
<div
|
|
71
|
+
data-controller="popover"
|
|
72
|
+
data-popover-url-value="/help/tooltip"
|
|
73
|
+
data-popover-reload-value="stale"
|
|
74
|
+
data-popover-stale-after-value="300"
|
|
75
|
+
>
|
|
76
|
+
<button data-action="click->popover#toggle" data-popover-target="trigger">Help</button>
|
|
77
|
+
|
|
78
|
+
<div data-popover-target="panel" hidden>
|
|
79
|
+
<div data-popover-target="loader" hidden>Loading…</div>
|
|
80
|
+
<div data-popover-target="template"></div>
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Turbo Frame (lazy load)
|
|
86
|
+
|
|
87
|
+
When the `panel` target is a `<turbo-frame>`, `canLoad()` sets its `src` attribute on show rather than fetching HTML directly.
|
|
88
|
+
|
|
89
|
+
```html
|
|
90
|
+
<div data-controller="popover" data-popover-url-value="/preview/123">
|
|
91
|
+
<button data-action="click->popover#toggle" data-popover-target="trigger">Preview</button>
|
|
92
|
+
<turbo-frame data-popover-target="panel" hidden></turbo-frame>
|
|
93
|
+
</div>
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Keep panel open on select
|
|
97
|
+
|
|
98
|
+
Selection events (from combobox sub-controllers, menu items, etc.) can be wired to `closeOnSelect`. Set `data-popover-close-on-select-value="false"` to keep the panel open after a selection — useful for multi-step pickers (date ranges, multi-select).
|
|
99
|
+
|
|
100
|
+
```html
|
|
101
|
+
<div data-controller="popover" data-popover-close-on-select-value="false">
|
|
102
|
+
...
|
|
103
|
+
<ul data-action="my-list:selected->popover#closeOnSelect">
|
|
104
|
+
…
|
|
105
|
+
</ul>
|
|
106
|
+
</div>
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Accessibility
|
|
110
|
+
|
|
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,84 @@
|
|
|
1
|
+
# Timeline
|
|
2
|
+
|
|
3
|
+
Manages expandable/collapsible timeline event items with keyboard navigation and optional client-side date formatting.
|
|
4
|
+
|
|
5
|
+
## Stimulus Identifier
|
|
6
|
+
|
|
7
|
+
`timeline`
|
|
8
|
+
|
|
9
|
+
## Targets
|
|
10
|
+
|
|
11
|
+
| Name | Element | Purpose |
|
|
12
|
+
| --------- | ----------------------------- | --------------------------------------------------------------------------------------- |
|
|
13
|
+
| `trigger` | `<button>` inside each `<h3>` | Controls expand/collapse of its associated detail |
|
|
14
|
+
| `detail` | Collapsible region | Content shown/hidden via `aria-controls` reference |
|
|
15
|
+
| `time` | `<time datetime="…">` | Empty `<time>` elements whose text content is set from `datetime` via `dateFormatValue` |
|
|
16
|
+
|
|
17
|
+
## Values
|
|
18
|
+
|
|
19
|
+
| Name | Type | Default | Purpose |
|
|
20
|
+
| ------------ | ------ | ------- | ------------------------------------------------------------------------------------------------------- |
|
|
21
|
+
| `dateFormat` | Object | `{}` | `Intl.DateTimeFormat` options applied to empty `<time datetime>` elements on connect. No-op when empty. |
|
|
22
|
+
|
|
23
|
+
## Actions
|
|
24
|
+
|
|
25
|
+
| Name | Purpose |
|
|
26
|
+
| ---------- | ------------------------------------------- |
|
|
27
|
+
| `toggle` | Expands if collapsed, collapses if expanded |
|
|
28
|
+
| `expand` | Expands the item |
|
|
29
|
+
| `collapse` | Collapses the item |
|
|
30
|
+
|
|
31
|
+
## Keyboard
|
|
32
|
+
|
|
33
|
+
| Key | Behaviour |
|
|
34
|
+
| ----------- | ------------------------------ |
|
|
35
|
+
| `ArrowDown` | Focus next trigger (wraps) |
|
|
36
|
+
| `ArrowUp` | Focus previous trigger (wraps) |
|
|
37
|
+
| `Home` | Focus first trigger |
|
|
38
|
+
| `End` | Focus last trigger |
|
|
39
|
+
|
|
40
|
+
## Dispatches
|
|
41
|
+
|
|
42
|
+
| Event | Detail | When |
|
|
43
|
+
| -------------------- | --------------------- | --------------------------------- |
|
|
44
|
+
| `timeline:expand` | `{ trigger, detail }` | When an item begins expanding |
|
|
45
|
+
| `timeline:expanded` | `{ trigger, detail }` | After an item finishes expanding |
|
|
46
|
+
| `timeline:collapse` | `{ trigger, detail }` | When an item begins collapsing |
|
|
47
|
+
| `timeline:collapsed` | `{ trigger, detail }` | After an item finishes collapsing |
|
|
48
|
+
|
|
49
|
+
## Example HTML
|
|
50
|
+
|
|
51
|
+
```html
|
|
52
|
+
<!-- Static timeline with server-rendered date text -->
|
|
53
|
+
<ol data-controller="timeline">
|
|
54
|
+
<li>
|
|
55
|
+
<time datetime="2024-01-15">January 2024</time>
|
|
56
|
+
<h3>Event title</h3>
|
|
57
|
+
</li>
|
|
58
|
+
</ol>
|
|
59
|
+
|
|
60
|
+
<!-- Static timeline with client-side date formatting -->
|
|
61
|
+
<ol data-controller="timeline" data-timeline-date-format-value='{"month":"long","year":"numeric","day":"numeric"}'>
|
|
62
|
+
<li>
|
|
63
|
+
<time datetime="2024-01-15"></time>
|
|
64
|
+
<h3>Event title</h3>
|
|
65
|
+
</li>
|
|
66
|
+
</ol>
|
|
67
|
+
|
|
68
|
+
<!-- Interactive timeline with expandable details -->
|
|
69
|
+
<ol data-controller="timeline">
|
|
70
|
+
<li>
|
|
71
|
+
<h3>
|
|
72
|
+
<button
|
|
73
|
+
data-timeline-target="trigger"
|
|
74
|
+
data-action="timeline#toggle"
|
|
75
|
+
aria-expanded="false"
|
|
76
|
+
aria-controls="detail-1"
|
|
77
|
+
>
|
|
78
|
+
Event title
|
|
79
|
+
</button>
|
|
80
|
+
</h3>
|
|
81
|
+
<div id="detail-1" data-timeline-target="detail" hidden>Detail content</div>
|
|
82
|
+
</li>
|
|
83
|
+
</ol>
|
|
84
|
+
```
|
|
@@ -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.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Guide
|
|
2
|
+
|
|
3
|
+
For a non-Rails / plain JS consumer of `@stimulus-plumbers/controllers`. Rails apps get this wired
|
|
4
|
+
automatically via the `stimulus_plumbers` gem's `sp_*` helpers — skip this guide for Rails.
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
npm install @stimulus-plumbers/controllers
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
Import and register each controller you use with your Stimulus application — see
|
|
11
|
+
[README.md](../README.md#setup) for the full import + `application.register(...)` list and the
|
|
12
|
+
[Controllers table](../README.md#controllers) for identifiers and their docs.
|
|
13
|
+
|
|
14
|
+
Interactive components (combobox, popover, calendar) expect their `data-controller` attributes to
|
|
15
|
+
already be present in the rendered HTML — Rails apps get these from `sp_*` helpers; a plain-JS setup
|
|
16
|
+
must add them manually per each controller's doc.
|