stimulus_plumbers 0.4.5 → 0.4.8
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 +7 -0
- data/README.md +16 -0
- data/docs/component/button.md +1 -1
- data/docs/component/calendar.md +11 -4
- data/docs/component/card.md +14 -12
- data/docs/component/combobox.md +2 -2
- data/docs/component/form.md +32 -6
- data/docs/component/list.md +2 -2
- data/docs/component/plumber.md +22 -0
- data/docs/component/theme.md +15 -2
- data/docs/component/timeline.md +2 -2
- data/docs/guide.md +27 -0
- data/lib/generators/stimulus_plumbers/install/install_generator.rb +29 -0
- data/lib/stimulus_plumbers/components/button.rb +1 -1
- data/lib/stimulus_plumbers/components/card.rb +1 -1
- data/lib/stimulus_plumbers/components/combobox.rb +1 -1
- data/lib/stimulus_plumbers/components/link.rb +1 -1
- data/lib/stimulus_plumbers/components/list/item.rb +1 -1
- data/lib/stimulus_plumbers/components/timeline/event.rb +1 -1
- data/lib/stimulus_plumbers/engine.rb +4 -0
- data/lib/stimulus_plumbers/generators/css_entrypoint.rb +74 -0
- data/lib/stimulus_plumbers/generators/tokens_directive.rb +20 -0
- data/lib/stimulus_plumbers/plumber/slots.rb +10 -3
- data/lib/stimulus_plumbers/version.rb +1 -1
- data/lib/tasks/stimulus_plumbers.rake +18 -0
- data/vendor/ARIA.md +88 -0
- data/vendor/controller/docs/calendar.md +215 -0
- data/vendor/controller/docs/clipboard.md +61 -0
- data/vendor/controller/docs/combobox.md +242 -0
- data/vendor/controller/docs/dismisser.md +39 -0
- data/vendor/controller/docs/flipper.md +46 -0
- data/vendor/controller/docs/input-clearable.md +46 -0
- data/vendor/controller/docs/input-formatter.md +90 -0
- data/vendor/controller/docs/modal.md +60 -0
- data/vendor/controller/docs/panner.md +24 -0
- data/vendor/controller/docs/popover.md +113 -0
- data/vendor/controller/docs/timeline.md +84 -0
- data/vendor/controller/guide.md +16 -0
- metadata +20 -2
- /data/vendor/{controllers.manifest.json → controller/manifest.json} +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b138245a7a9711414d5c7bf052dbd55d42006f5e785626fd94bce96ffab6281d
|
|
4
|
+
data.tar.gz: e2e15e550f5678eecbfc7310628759590212fae21aa42c7eab60d7c049f2a47f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 624a411ff21cb32deebdde9a3c68c0d0c43fed7ade306cc7299fdaadca7604a3053a2f9251df1608af393fc0129bffd75b6e244b9fd9e0477a4df72f859e0ddb
|
|
7
|
+
data.tar.gz: 8176752b32e5e730e7c8a47ba5aa44d96b591a38c44d0a71ad6d2d147404227b02aa581b00db36711da357cda872b96260187b62749e74f652acb2bd1a26a970
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [conventional commits](https://www.conventionalcommits.org/) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
---
|
|
6
|
+
## [0.4.5](https://github.com/ryancyq/stimulus-plumbers/compare/stimulus-plumbers-rails/v0.4.4..stimulus-plumbers-rails/v0.4.5) - 2026-07-01
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
- mcp executable ([#151](https://github.com/ryancyq/stimulus-plumbers/issues/151)) - ([5e772ce](https://github.com/ryancyq/stimulus-plumbers/commit/5e772ce2d8ceb7c517c2e7fdd44d3438b1e38139)) - Ryan Chang
|
|
11
|
+
|
|
5
12
|
---
|
|
6
13
|
## [0.4.4](https://github.com/ryancyq/stimulus-plumbers/compare/stimulus-plumbers-rails/v0.4.3..stimulus-plumbers-rails/v0.4.4) - 2026-06-30
|
|
7
14
|
|
data/README.md
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
Rails helpers for accessible, WCAG 2.1 AA compliant UI components built on [Stimulus](https://stimulus.hotwired.dev/). Pairs with the [`@stimulus-plumbers/controllers`](../stimulus-plumbers) npm package.
|
|
8
8
|
|
|
9
|
+
See [docs/guide.md](docs/guide.md) for a quick guide to building forms and views with this gem.
|
|
10
|
+
|
|
9
11
|
## Requirements
|
|
10
12
|
|
|
11
13
|
- Ruby >= 3.0
|
|
@@ -40,6 +42,20 @@ Or use the form builder globally:
|
|
|
40
42
|
config.action_view.default_form_builder = StimulusPlumbers::Form::Builder
|
|
41
43
|
```
|
|
42
44
|
|
|
45
|
+
Run the install generator once to inject the `tokens.css` import into your CSS entry file:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
bin/rails generate stimulus_plumbers:install
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
The generator checks these files in order:
|
|
52
|
+
|
|
53
|
+
- `app/assets/stylesheets/application.tailwind.css`
|
|
54
|
+
- `app/assets/stylesheets/application.css`
|
|
55
|
+
- `app/javascript/entrypoints/application.css`
|
|
56
|
+
|
|
57
|
+
Override the detected file with `STIMULUS_PLUMBERS_CSS_FILE=/path/to/entry.css`. After the initial install, the import stays current automatically — the engine hooks `stimulus_plumbers:install` as a prerequisite of `assets:precompile`.
|
|
58
|
+
|
|
43
59
|
## Components
|
|
44
60
|
|
|
45
61
|
| Component | Helper(s) | Docs |
|
data/docs/component/button.md
CHANGED
|
@@ -107,7 +107,7 @@ Icons are rendered **inside** the `<button>`, before or after the label `<span>`
|
|
|
107
107
|
|
|
108
108
|
### Icon only
|
|
109
109
|
|
|
110
|
-
When no text is provided, no `<span>` is rendered
|
|
110
|
+
When no text is provided, no `<span>` is rendered, and the button becomes square (or a circle for `type: :fab` / `:fab_outline`):
|
|
111
111
|
|
|
112
112
|
```html
|
|
113
113
|
<button type="button" aria-label="Add" class="[theme classes]">
|
data/docs/component/calendar.md
CHANGED
|
@@ -94,10 +94,17 @@ Individual view helpers render a single view (used for Turbo Frame responses). T
|
|
|
94
94
|
<%= sp_calendar_turbo_decade(date: @date, today: @today) %>
|
|
95
95
|
```
|
|
96
96
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
|
100
|
-
|
|
|
97
|
+
**`sp_calendar_turbo_month`** — extra option:
|
|
98
|
+
|
|
99
|
+
| Option | Description |
|
|
100
|
+
| ---------------- | ------------------------------------------------------------------ |
|
|
101
|
+
| `weekday_format` | `:short` (default) \| `:long` \| `:narrow` — weekday header format |
|
|
102
|
+
|
|
103
|
+
**`sp_calendar_turbo_year`** — extra option:
|
|
104
|
+
|
|
105
|
+
| Option | Description |
|
|
106
|
+
| -------------- | ---------------------------------------------------------------- |
|
|
107
|
+
| `month_format` | `:short` (default) \| `:long` \| `:narrow` — month button format |
|
|
101
108
|
|
|
102
109
|
**Common options** (all helpers and `sp_calendar_turbo`):
|
|
103
110
|
|
data/docs/component/card.md
CHANGED
|
@@ -63,10 +63,12 @@ Rails helper for rendering a themed card with optional icon, title, body, and ac
|
|
|
63
63
|
<!-- body -->
|
|
64
64
|
<div class="[card_body theme classes]">Your account is active.</div>
|
|
65
65
|
|
|
66
|
-
<!-- action -->
|
|
67
|
-
<
|
|
68
|
-
|
|
69
|
-
|
|
66
|
+
<!-- action: a ghost/tertiary sp_button, with card_action classes merged directly onto it (no wrapping div) -->
|
|
67
|
+
<a
|
|
68
|
+
href="/settings"
|
|
69
|
+
class="[button ghost/tertiary + card_action theme classes]"
|
|
70
|
+
><span>Manage</span></a
|
|
71
|
+
>
|
|
70
72
|
</div>
|
|
71
73
|
```
|
|
72
74
|
|
|
@@ -74,14 +76,14 @@ Rails helper for rendering a themed card with optional icon, title, body, and ac
|
|
|
74
76
|
|
|
75
77
|
## Theme keys
|
|
76
78
|
|
|
77
|
-
| Key | Element
|
|
78
|
-
| ------------- |
|
|
79
|
-
| `card` | Outer `<div>`
|
|
80
|
-
| `card_header` | Header wrapper `<div>` (icon+title)
|
|
81
|
-
| `card_icon` | Icon inside the header
|
|
82
|
-
| `card_title` | Title element (`h2` etc.)
|
|
83
|
-
| `card_body` | Body `<div>`
|
|
84
|
-
| `card_action` |
|
|
79
|
+
| Key | Element | Variants |
|
|
80
|
+
| ------------- | ------------------------------------------------------------- | --------------------------------------------- |
|
|
81
|
+
| `card` | Outer `<div>` | `variant: :primary\|:secondary\|:tertiary\|…` |
|
|
82
|
+
| `card_header` | Header wrapper `<div>` (icon+title) | — |
|
|
83
|
+
| `card_icon` | Icon inside the header | — |
|
|
84
|
+
| `card_title` | Title element (`h2` etc.) | — |
|
|
85
|
+
| `card_body` | Body `<div>` | — |
|
|
86
|
+
| `card_action` | Merged onto the action `<a>`/`<button>` (no wrapping element) | — |
|
|
85
87
|
|
|
86
88
|
---
|
|
87
89
|
|
data/docs/component/combobox.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Rails helpers that render fully-wired combobox components. Each variant shares the same wrapper structure (`input-combobox` + `input-formatter`) and differs only in its popover body.
|
|
4
4
|
|
|
5
|
-
See [docs/component/combobox.md](
|
|
5
|
+
See [docs/component/combobox.md](../../../stimulus-plumbers/docs/component/combobox.md) in the JS package for the underlying controller API.
|
|
6
6
|
|
|
7
7
|
## Helpers
|
|
8
8
|
|
|
@@ -127,7 +127,7 @@ iOS-style drum/scroll-wheel time picker.
|
|
|
127
127
|
|
|
128
128
|
## Form Builder
|
|
129
129
|
|
|
130
|
-
`StimulusPlumbers::Form::Builder` exposes combobox-backed fields through purpose-specific methods rather than a single generic helper. See [
|
|
130
|
+
`StimulusPlumbers::Form::Builder` exposes combobox-backed fields through purpose-specific methods rather than a single generic helper. See [form.md](form.md) for the full API.
|
|
131
131
|
|
|
132
132
|
```erb
|
|
133
133
|
<%= form_with model: @user, builder: StimulusPlumbers::Form::Builder do |f| %>
|
data/docs/component/form.md
CHANGED
|
@@ -118,9 +118,34 @@ Three methods render a complete, accessible field:
|
|
|
118
118
|
|
|
119
119
|
Use `f.time_field` for a plain `<input type="time">`.
|
|
120
120
|
|
|
121
|
-
**Select** (`as: :select`) — read-only listbox backed by `combobox-dropdown`.
|
|
121
|
+
**Select** (`as: :select`) — read-only listbox backed by `combobox-dropdown`.
|
|
122
122
|
|
|
123
|
-
|
|
123
|
+
| Option | Values | Default | Description |
|
|
124
|
+
| --------------- | -------------- | ------- | ------------------------------------------------------------- |
|
|
125
|
+
| `choices` | Array | `[]` | `[label, value]` pairs, or a flat array of strings |
|
|
126
|
+
| `include_blank` | Boolean/String | `nil` | Prepends a blank option; String overrides its label |
|
|
127
|
+
| `prompt` | Boolean/String | `nil` | Prepends a disabled placeholder option |
|
|
128
|
+
| `selected` | Value | `nil` | Pre-selected value; defaults to the attribute's current value |
|
|
129
|
+
|
|
130
|
+
Use `f.select` for a native `<select>`.
|
|
131
|
+
|
|
132
|
+
**Search** (`as: :search`) — editable typeahead backed by `combobox-dropdown`.
|
|
133
|
+
|
|
134
|
+
| Option | Values | Default | Description |
|
|
135
|
+
| ----------- | ------- | ------- | ----------------------------------------------------------------------------------- |
|
|
136
|
+
| `choices` | Array | `[]` | Client-side options to filter; omit when using `url:` |
|
|
137
|
+
| `url` | String | `nil` | Server-side endpoint, receives `?q=<query>`, returns `<li role="option">` fragments |
|
|
138
|
+
| `clearable` | Boolean | `false` | Adds a clear button that resets the input |
|
|
139
|
+
|
|
140
|
+
Use `f.search_field` for a native `<input type="search">`.
|
|
141
|
+
|
|
142
|
+
**Password** (`as: :password`) — reveal-toggle wrapper backed by `input-formatter`.
|
|
143
|
+
|
|
144
|
+
| Option | Values | Default | Description |
|
|
145
|
+
| ------------ | ------- | ------- | ------------------------------------------- |
|
|
146
|
+
| `revealable` | Boolean | `false` | Adds a show/hide toggle button on the input |
|
|
147
|
+
|
|
148
|
+
Use `f.password_field` for a plain `<input type="password">` (also accepts `revealable:`).
|
|
124
149
|
|
|
125
150
|
---
|
|
126
151
|
|
|
@@ -169,9 +194,9 @@ Renders a `<fieldset>` / `<legend>` for accessible grouping, or a single checkbo
|
|
|
169
194
|
|
|
170
195
|
**Card / button behaviour:**
|
|
171
196
|
|
|
172
|
-
- **Checkbox card** — input visible on right; card border
|
|
173
|
-
- **Radio card** — input
|
|
174
|
-
- **Radio button** — input
|
|
197
|
+
- **Checkbox card** — input visible on the right; the card border highlights when checked
|
|
198
|
+
- **Radio card** — input visually hidden; the whole card is the clickable target and highlights when selected
|
|
199
|
+
- **Radio button** — input visually hidden; renders as an inline pill that highlights when selected
|
|
175
200
|
|
|
176
201
|
---
|
|
177
202
|
|
|
@@ -185,7 +210,8 @@ Renders a `<fieldset>` / `<legend>` for accessible grouping, or a single checkbo
|
|
|
185
210
|
<input
|
|
186
211
|
id="user_email"
|
|
187
212
|
type="email"
|
|
188
|
-
aria-describedby="user_email_hint"
|
|
213
|
+
aria-describedby="user_email_hint user_email_error"
|
|
214
|
+
aria-invalid="true"
|
|
189
215
|
required
|
|
190
216
|
aria-required="true"
|
|
191
217
|
/>
|
data/docs/component/list.md
CHANGED
|
@@ -39,7 +39,7 @@ Rails helper for rendering an accessible list of links, buttons, or grouped sect
|
|
|
39
39
|
| `role:` | `"list"` | ARIA role on the `<ul>` — use `"menu"` for interactive menus |
|
|
40
40
|
| `**html_options` | — | Forwarded to the `<ul>` |
|
|
41
41
|
|
|
42
|
-
### `list.section(title:, description:, **html_options)`
|
|
42
|
+
### `list.section(title:, description:, **html_options, &block)`
|
|
43
43
|
|
|
44
44
|
Renders a `<li>` with an optional heading/description and a nested `<ul>`.
|
|
45
45
|
|
|
@@ -60,7 +60,7 @@ Renders a `<li>` containing an `<a>` (when `url:` present) or `<button>`.
|
|
|
60
60
|
| `target:` | `nil` | Forwarded to the `<a>` (e.g. `"_blank"`) |
|
|
61
61
|
| `**html_options` | — | Forwarded to the inner `<a>` or `<button>` |
|
|
62
62
|
|
|
63
|
-
When `target: "_blank"` is set, `icon_trailing: "external-link"` is added automatically.
|
|
63
|
+
When `url:` is present and `target: "_blank"` is set, `icon_trailing: "external-link"` is added automatically.
|
|
64
64
|
|
|
65
65
|
### Item slot methods (yielded as `item`)
|
|
66
66
|
|
data/docs/component/plumber.md
CHANGED
|
@@ -103,6 +103,28 @@ end
|
|
|
103
103
|
|
|
104
104
|
---
|
|
105
105
|
|
|
106
|
+
## Plumber::Slots
|
|
107
|
+
|
|
108
|
+
Base class for a component's slot DSL (e.g. `renderer.with_title(value)`, `renderer.with_content { ... }`). Used by `Card`, `Button`, `Link`, `List::Item`, `Timeline::Event`, and `Combobox::Builder`.
|
|
109
|
+
|
|
110
|
+
```ruby
|
|
111
|
+
class MySlots < StimulusPlumbers::Plumber::Slots
|
|
112
|
+
slot :icon, :title # flat slots: with_icon(value) / with_title(value)
|
|
113
|
+
slot :header, by: HeaderSlots # nested slots: with_header { |h| h.with_x(...) }
|
|
114
|
+
end
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Always construct with the view `template` — required for a slot's multi-line block form (`with_content do ... end`) to render correctly:
|
|
118
|
+
|
|
119
|
+
```ruby
|
|
120
|
+
slots = MySlots.new(template)
|
|
121
|
+
yield slots if block_given?
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Slots needing custom validation (e.g. a required keyword) define `with_*` manually instead of using the `slot` DSL — see `Card::Slots#with_action`.
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
106
128
|
## Plumber::Options::Html
|
|
107
129
|
|
|
108
130
|
Mixin for safely deep-merging HTML attribute hashes. Handles three concerns that a plain `Hash#merge` gets wrong:
|
data/docs/component/theme.md
CHANGED
|
@@ -4,14 +4,27 @@ Stimulus Plumbers uses a theme system to apply presentational CSS classes to for
|
|
|
4
4
|
|
|
5
5
|
## Configuration
|
|
6
6
|
|
|
7
|
+
Adding the `stimulus_plumbers_tailwind` gem registers and activates the Tailwind theme automatically — no config needed. To switch to a custom theme instead:
|
|
8
|
+
|
|
7
9
|
```ruby
|
|
8
10
|
# config/initializers/stimulus_plumbers.rb
|
|
9
11
|
StimulusPlumbers.configure do |config|
|
|
10
|
-
config.theme.use(
|
|
11
|
-
# config.theme.use(MyCustomTheme.new)
|
|
12
|
+
config.theme.use(MyCustomTheme.new)
|
|
12
13
|
end
|
|
13
14
|
```
|
|
14
15
|
|
|
16
|
+
## Design tokens
|
|
17
|
+
|
|
18
|
+
- Component classes reference CSS custom properties (e.g. `--sp-color-primary`) defined in `stimulus_plumbers/tokens.css`
|
|
19
|
+
- Run `bin/rails generate stimulus_plumbers:install` once to import it — see the [README](../../README.md#installation)
|
|
20
|
+
- Override tokens with your own `:root` block after the generated import — the cascade applies your values without forking the file
|
|
21
|
+
|
|
22
|
+
```css
|
|
23
|
+
:root {
|
|
24
|
+
--sp-color-primary: oklch(60% 0.18 260);
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
15
28
|
## Theme keys
|
|
16
29
|
|
|
17
30
|
Each row lists the theme key, the component that uses it, and the variants it accepts.
|
data/docs/component/timeline.md
CHANGED
|
@@ -132,7 +132,7 @@ Rails helper for rendering a themed timeline as an ordered list with optional in
|
|
|
132
132
|
|
|
133
133
|
## ARIA
|
|
134
134
|
|
|
135
|
-
- See [ARIA.md](
|
|
135
|
+
- See [ARIA.md](../../../ARIA.md) for WCAG 2.1 AA criteria and disclosure widget patterns.
|
|
136
136
|
- The trigger button uses `aria-expanded` (managed by the JS controller) and `aria-controls` pointing to the detail `id`.
|
|
137
137
|
- Indicator elements carry `aria-hidden="true"` to hide decorative markup from screen readers.
|
|
138
|
-
- For JS controller targets, values, and actions, see the [stimulus-plumbers JS controller doc](
|
|
138
|
+
- For JS controller targets, values, and actions, see the [stimulus-plumbers JS controller doc](../../../stimulus-plumbers/docs/component/timeline.md).
|
data/docs/guide.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Guide
|
|
2
|
+
|
|
3
|
+
## Building forms
|
|
4
|
+
|
|
5
|
+
Use `StimulusPlumbers::Form::Builder` (set `config.action_view.default_form_builder`, or pass
|
|
6
|
+
`builder:` to `form_with`). Two levels:
|
|
7
|
+
|
|
8
|
+
- **Level 2 — recommended.** Full accessible field (label + input + hint + error):
|
|
9
|
+
`f.field(attr, as:)`, `f.collection_field(attr, as:, collection:, ...)`, `f.choice(attr, as:)`.
|
|
10
|
+
See [docs/component/form.md](component/form.md) for valid `as:` values per builder method and
|
|
11
|
+
which ones are backed by a Stimulus controller (date/time/select/search pickers).
|
|
12
|
+
- **Level 1.** Native helper overrides (`f.text_field`, `f.select`, `f.check_box`, ...) render
|
|
13
|
+
only the themed input element — use when you control the surrounding markup.
|
|
14
|
+
|
|
15
|
+
Submit with `f.submit` (themed button).
|
|
16
|
+
|
|
17
|
+
## Building views
|
|
18
|
+
|
|
19
|
+
Render components with `sp_*` helpers (`sp_button`, `sp_button_group`, `sp_card`, `sp_list`,
|
|
20
|
+
`sp_link`, `sp_avatar`, `sp_divider`, `sp_icon`, `sp_popover`, ...) — see the
|
|
21
|
+
[Components table](../README.md#components) for the full helper list, and each component's
|
|
22
|
+
`docs/component/<name>.md` for its keyword options, slots, and themed params.
|
|
23
|
+
|
|
24
|
+
## Stimulus integration
|
|
25
|
+
|
|
26
|
+
Most display components are pure markup; interactive ones (combobox, popover, calendar) emit their
|
|
27
|
+
`data-controller` attributes automatically — no manual wiring needed in Rails views.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators"
|
|
4
|
+
require "stimulus_plumbers/generators/css_entrypoint"
|
|
5
|
+
require "stimulus_plumbers/generators/tokens_directive"
|
|
6
|
+
|
|
7
|
+
module StimulusPlumbers
|
|
8
|
+
module Generators
|
|
9
|
+
class InstallGenerator < Rails::Generators::Base
|
|
10
|
+
include StimulusPlumbers::Generators::CssEntrypoint
|
|
11
|
+
|
|
12
|
+
STIMULUS_PLUMBERS_CSS_FILE = CssEntrypoint::STIMULUS_PLUMBERS_CSS_FILE
|
|
13
|
+
CSS_CANDIDATES = %w[
|
|
14
|
+
app/assets/stylesheets/application.tailwind.css
|
|
15
|
+
app/assets/stylesheets/application.css
|
|
16
|
+
app/javascript/entrypoints/application.css
|
|
17
|
+
].freeze
|
|
18
|
+
|
|
19
|
+
def install
|
|
20
|
+
css_file = entry_css_file(candidates: CSS_CANDIDATES, env_var: STIMULUS_PLUMBERS_CSS_FILE)
|
|
21
|
+
unless css_file
|
|
22
|
+
return warn_entry_css_not_found(candidates: CSS_CANDIDATES, env_var: STIMULUS_PLUMBERS_CSS_FILE, label: "CSS")
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
apply_edit(css_file, TokensDirective.directive, stale_pattern: TokensDirective.stale_pattern)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -4,7 +4,7 @@ module StimulusPlumbers
|
|
|
4
4
|
module Components
|
|
5
5
|
class Button < Plumber::Base
|
|
6
6
|
def render(content = nil, icon_leading: nil, icon_trailing: nil, **kwargs, &block)
|
|
7
|
-
slots = Button::Slots.new
|
|
7
|
+
slots = Button::Slots.new(template)
|
|
8
8
|
slots.with_icon_leading(icon_leading) if icon_leading
|
|
9
9
|
slots.with_icon_trailing(icon_trailing) if icon_trailing
|
|
10
10
|
|
|
@@ -4,7 +4,7 @@ module StimulusPlumbers
|
|
|
4
4
|
module Components
|
|
5
5
|
class Card < Plumber::Base
|
|
6
6
|
def render(variant: :tertiary, title_tag: :h2, **kwargs, &block)
|
|
7
|
-
slots = Card::Slots.new
|
|
7
|
+
slots = Card::Slots.new(template)
|
|
8
8
|
yield slots if block_given?
|
|
9
9
|
|
|
10
10
|
html_options = merge_html_options(theme.resolve(:card, variant: variant), kwargs)
|
|
@@ -6,7 +6,7 @@ module StimulusPlumbers
|
|
|
6
6
|
def render(content = nil, url:, icon_leading: nil, icon_trailing: nil, **kwargs, &block)
|
|
7
7
|
icon_trailing ||= "external-link" if kwargs[:target] == "_blank"
|
|
8
8
|
|
|
9
|
-
slots = Link::Slots.new
|
|
9
|
+
slots = Link::Slots.new(template)
|
|
10
10
|
slots.with_icon_leading(icon_leading) if icon_leading
|
|
11
11
|
slots.with_icon_trailing(icon_trailing) if icon_trailing
|
|
12
12
|
|
|
@@ -5,7 +5,7 @@ module StimulusPlumbers
|
|
|
5
5
|
class List
|
|
6
6
|
class Item < Plumber::Base
|
|
7
7
|
def render(content = nil, **kwargs, &block)
|
|
8
|
-
slots = List::Item::Slots.new
|
|
8
|
+
slots = List::Item::Slots.new(template)
|
|
9
9
|
slots.with_title(content) if content
|
|
10
10
|
slots.with_icon_trailing("external-link") if kwargs[:url].present? && kwargs[:target] == "_blank"
|
|
11
11
|
yield slots if block_given?
|
|
@@ -13,7 +13,7 @@ module StimulusPlumbers
|
|
|
13
13
|
def render(datetime: nil, id: nil, interactive: false, orientation: :vertical, **kwargs, &block)
|
|
14
14
|
@detail_id = self.class.detail_id_for(id || template.sp_dom_id)
|
|
15
15
|
|
|
16
|
-
slots = Timeline::Event::Slots.new
|
|
16
|
+
slots = Timeline::Event::Slots.new(template)
|
|
17
17
|
yield slots if block_given?
|
|
18
18
|
|
|
19
19
|
validate_slots!(slots, datetime: datetime)
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StimulusPlumbers
|
|
4
|
+
module Generators
|
|
5
|
+
module CssEntrypoint
|
|
6
|
+
STIMULUS_PLUMBERS_CSS_FILE = "STIMULUS_PLUMBERS_CSS_FILE"
|
|
7
|
+
|
|
8
|
+
def entry_css_file(candidates:, env_var:, fallback_env_var: nil)
|
|
9
|
+
[env_var, fallback_env_var].compact.each do |var|
|
|
10
|
+
next unless ENV[var]
|
|
11
|
+
|
|
12
|
+
path = File.expand_path(ENV.fetch(var, nil))
|
|
13
|
+
return path if File.exist?(path)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
candidates
|
|
17
|
+
.map { |c| File.join(destination_root, c) }
|
|
18
|
+
.find { |f| File.exist?(f) }
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def warn_entry_css_not_found(candidates:, env_var:, label:, fallback_env_var: nil)
|
|
22
|
+
tried = candidates.map { |c| File.join(destination_root, c) }
|
|
23
|
+
[env_var, fallback_env_var].compact.each do |var|
|
|
24
|
+
tried.unshift(File.expand_path(ENV[var])) if ENV[var]
|
|
25
|
+
end
|
|
26
|
+
say "Could not find a #{label} entry file. Tried: #{tried.join(", ")}. " \
|
|
27
|
+
"Set #{env_var}=/path/to/entry.css and re-run.",
|
|
28
|
+
:red
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def apply_edit(css_file, directive, stale_pattern:, anchor_pattern: nil)
|
|
32
|
+
content = File.read(css_file)
|
|
33
|
+
new_content, status = content_edit(content, directive, stale_pattern: stale_pattern, anchor_pattern: anchor_pattern)
|
|
34
|
+
|
|
35
|
+
if new_content
|
|
36
|
+
File.write(css_file, new_content)
|
|
37
|
+
say_status status, relative_to_destination(css_file), :green
|
|
38
|
+
else
|
|
39
|
+
say_status :identical, relative_to_destination(css_file)
|
|
40
|
+
end
|
|
41
|
+
rescue Errno::EROFS, Errno::EACCES => e
|
|
42
|
+
say "Could not update #{relative_to_destination(css_file)}: #{e.message}. Skipping.", :yellow
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# `stale_pattern:` is required — every call site needs stale-import detection,
|
|
46
|
+
# so making it optional would leave an untested "anchor without stale" state.
|
|
47
|
+
def content_edit(content, directive, stale_pattern:, anchor_pattern: nil)
|
|
48
|
+
return nil if content.include?(directive)
|
|
49
|
+
|
|
50
|
+
if (existing = content.match(stale_pattern))
|
|
51
|
+
return [content.sub(existing[0], directive), :update]
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
anchor_pattern ? insert_near_anchor(content, directive, anchor_pattern) : prepend(content, directive)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def prepend(content, directive)
|
|
58
|
+
["#{directive}\n#{content}", :prepend]
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def insert_near_anchor(content, directive, anchor_pattern)
|
|
62
|
+
if (anchor = content.match(anchor_pattern))
|
|
63
|
+
[content.sub(anchor[0], "#{anchor[0]}\n#{directive}"), :insert]
|
|
64
|
+
else
|
|
65
|
+
["#{content.rstrip}\n#{directive}\n", :append]
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def relative_to_destination(path)
|
|
70
|
+
path.delete_prefix("#{destination_root}/")
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StimulusPlumbers
|
|
4
|
+
module Generators
|
|
5
|
+
module TokensDirective
|
|
6
|
+
GEM_ROOT = File.expand_path("../../..", __dir__)
|
|
7
|
+
TOKENS_CSS_REL_PATH = "app/assets/stylesheets/stimulus_plumbers/tokens.css"
|
|
8
|
+
|
|
9
|
+
module_function
|
|
10
|
+
|
|
11
|
+
def directive
|
|
12
|
+
%(@import "#{GEM_ROOT}/#{TOKENS_CSS_REL_PATH}";)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def stale_pattern
|
|
16
|
+
%r{@import "[^"]*#{Regexp.escape(TOKENS_CSS_REL_PATH)}";}
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -11,8 +11,9 @@ module StimulusPlumbers
|
|
|
11
11
|
end
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
-
def initialize
|
|
14
|
+
def initialize(template = nil)
|
|
15
15
|
@slots = {}
|
|
16
|
+
@template = template
|
|
16
17
|
end
|
|
17
18
|
|
|
18
19
|
def resolve(name)
|
|
@@ -20,7 +21,7 @@ module StimulusPlumbers
|
|
|
20
21
|
return unless entry
|
|
21
22
|
|
|
22
23
|
value = entry[:value]
|
|
23
|
-
value = value
|
|
24
|
+
value = capture_block(value) if value.is_a?(Proc)
|
|
24
25
|
block_given? ? yield(value) : value
|
|
25
26
|
end
|
|
26
27
|
|
|
@@ -38,6 +39,12 @@ module StimulusPlumbers
|
|
|
38
39
|
|
|
39
40
|
private
|
|
40
41
|
|
|
42
|
+
# Runs the slot block through the view so its ERB output is returned,
|
|
43
|
+
# not written to whatever buffer happens to be active.
|
|
44
|
+
def capture_block(block)
|
|
45
|
+
@template ? @template.capture(&block) : block.call
|
|
46
|
+
end
|
|
47
|
+
|
|
41
48
|
def set_slot(name, value, options = {})
|
|
42
49
|
@slots[name] = { value: value, options: options }
|
|
43
50
|
end
|
|
@@ -54,7 +61,7 @@ module StimulusPlumbers
|
|
|
54
61
|
|
|
55
62
|
def define_by_slot(name, by)
|
|
56
63
|
define_method(:"with_#{name}") do |&block|
|
|
57
|
-
sub = by.new
|
|
64
|
+
sub = by.new(@template)
|
|
58
65
|
block&.call(sub)
|
|
59
66
|
set_slot(name, sub)
|
|
60
67
|
nil
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
namespace :stimulus_plumbers do
|
|
4
|
+
desc "Add the tokens.css @import into the CSS entry file"
|
|
5
|
+
task(:install) do
|
|
6
|
+
unless ENV["STIMULUS_PLUMBERS_SKIP_INSTALL"]
|
|
7
|
+
require "generators/stimulus_plumbers/install/install_generator"
|
|
8
|
+
StimulusPlumbers::Generators::InstallGenerator.new(
|
|
9
|
+
[], {}, { destination_root: Rails.root.to_s }
|
|
10
|
+
).invoke_all
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
Rake::Task["stimulus_plumbers:install"].enhance(["environment"]) unless ENV["STIMULUS_PLUMBERS_SKIP_INSTALL"]
|
|
16
|
+
|
|
17
|
+
# Hooks into "assets:precompile" as a prerequisite; Rake merges rather than overwrites, so order-independent.
|
|
18
|
+
task "assets:precompile" => ["stimulus_plumbers:install"]
|