stimulus_plumbers_tailwind 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.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +9 -0
  3. data/README.md +12 -15
  4. data/app/assets/stylesheets/stimulus_plumbers/tailwind/animations.css +18 -0
  5. data/docs/architecture.md +50 -0
  6. data/docs/guide.md +23 -0
  7. data/lib/generators/stimulus_plumbers/tailwind/install/install_generator.rb +61 -0
  8. data/lib/stimulus_plumbers/tailwind/engine.rb +18 -0
  9. data/lib/stimulus_plumbers/tailwind/generators/animations_directive.rb +32 -0
  10. data/lib/stimulus_plumbers/tailwind/generators/sources_directive.rb +29 -0
  11. data/lib/stimulus_plumbers/tailwind/version.rb +1 -1
  12. data/lib/stimulus_plumbers/themes/tailwind/button.rb +1 -1
  13. data/lib/stimulus_plumbers/themes/tailwind/card.rb +1 -1
  14. data/lib/stimulus_plumbers/themes/tailwind/checklist.rb +65 -0
  15. data/lib/stimulus_plumbers/themes/tailwind/combobox.rb +1 -1
  16. data/lib/stimulus_plumbers/themes/tailwind/control.rb +2 -0
  17. data/lib/stimulus_plumbers/themes/tailwind/form/input.rb +18 -15
  18. data/lib/stimulus_plumbers/themes/tailwind/icon.rb +10 -3
  19. data/lib/stimulus_plumbers/themes/tailwind/icons/customs/progress-ring.svg +12 -0
  20. data/lib/stimulus_plumbers/themes/tailwind/indicator.rb +49 -0
  21. data/lib/stimulus_plumbers/themes/tailwind/link.rb +1 -1
  22. data/lib/stimulus_plumbers/themes/tailwind/list.rb +1 -1
  23. data/lib/stimulus_plumbers/themes/tailwind/ordered_list.rb +66 -0
  24. data/lib/stimulus_plumbers/themes/tailwind/progress.rb +45 -0
  25. data/lib/stimulus_plumbers/themes/tailwind/timeline/group.rb +14 -5
  26. data/lib/stimulus_plumbers/themes/tailwind/timeline.rb +7 -9
  27. data/lib/stimulus_plumbers/themes/tailwind_theme.rb +8 -0
  28. data/lib/stimulus_plumbers_tailwind.rb +1 -1
  29. data/lib/tasks/stimulus_plumbers_tailwind.rake +15 -9
  30. metadata +13 -3
  31. data/lib/generators/stimulus_plumbers_tailwind/install/install_generator.rb +0 -78
  32. data/lib/stimulus_plumbers_tailwind/engine.rb +0 -15
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ff605a8bd2767e0f3eff46c53f3e644041ace037687a66042b3d2eb6bf3cf7f8
4
- data.tar.gz: 9cc8d80f88fb2ddc41c8dbd2e7a5cc9507a48987211501abf457f0318e4fb2e2
3
+ metadata.gz: e7a0a4078d4282a62d21ebdb421fd18c446fe69031dac6ed8f9be663c91957d2
4
+ data.tar.gz: 234ae70da55a34c9be1c655fa40c5d7e9536d6c99c7a75f5f5724df662fc63d8
5
5
  SHA512:
6
- metadata.gz: 8d74cf1b92f2ef08e823e60415b83ef8f737bfac9bfac2203d52e0383c9152958150137cdd52c23ca9c08d3fdea58282b88e135f8bfff962c2f187e4c6137cdb
7
- data.tar.gz: 48b048561bfe8ea18473122b340ce3a801dc9fb08b316c851b9d72d0b1460b580247e705fd0c272aed2f0f7196281d50162fbe4c40de4b5ae256bfe37f800f5a
6
+ metadata.gz: c608f0a0f0513903e3f6224c8750935deee7ccf736791eee048a54dcb421a5a345c7efec685046bd2027a75756ce2b3e3801fdffdc570f71aca9c05e3e86e92a
7
+ data.tar.gz: c1db6f8eb3034534ed893ada8507866529bfe654fd059fb26f22d17ee49951fbd7b4b218f5f121eb47ad4959cdda40185bc3b560b1aa2f80fb1c806d6877a154
data/CHANGELOG.md CHANGED
@@ -2,6 +2,15 @@
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.8](https://github.com/ryancyq/stimulus-plumbers/compare/stimulus-plumbers-tailwind/v0.4.5..stimulus-plumbers-tailwind/v0.4.8) - 2026-07-03
7
+
8
+ ### Bug Fixes
9
+
10
+ - tailwind install generator ([#154](https://github.com/ryancyq/stimulus-plumbers/issues/154)) - ([db4bda0](https://github.com/ryancyq/stimulus-plumbers/commit/db4bda0b899a463e0608229927c65326233dd3a4)) - Ryan Chang
11
+ - tailwind theme registration + activation (rails only) ([#157](https://github.com/ryancyq/stimulus-plumbers/issues/157)) - ([5998854](https://github.com/ryancyq/stimulus-plumbers/commit/5998854ed2a6ca9c67aa225f3c397e4f7bca7a51)) - Ryan Chang
12
+ - mcp doc update + new tools validations ([#162](https://github.com/ryancyq/stimulus-plumbers/issues/162)) - ([beed1bb](https://github.com/ryancyq/stimulus-plumbers/commit/beed1bb14cbfcb8ec6f757145dc6a461d357c060)) - Ryan Chang
13
+
5
14
  ---
6
15
  ## [0.4.4](https://github.com/ryancyq/stimulus-plumbers/compare/stimulus-plumbers-tailwind/v0.4.3..stimulus-plumbers-tailwind/v0.4.4) - 2026-06-30
7
16
 
data/README.md CHANGED
@@ -6,10 +6,12 @@
6
6
 
7
7
  Tailwind CSS v4 theme for [`stimulus_plumbers`](../stimulus-plumbers-rails). Extends the base theme with utility classes for all components.
8
8
 
9
+ See [docs/guide.md](docs/guide.md) for a quick setup/theming guide.
10
+
9
11
  ## Requirements
10
12
 
11
13
  - Ruby >= 3.0
12
- - `stimulus_plumbers` >= 0.2.9
14
+ - `stimulus_plumbers` >= 0.3.1
13
15
  - Tailwind CSS v4 in your build toolchain
14
16
 
15
17
  ## Installation
@@ -23,7 +25,7 @@ gem "stimulus_plumbers_tailwind"
23
25
  bundle install
24
26
  ```
25
27
 
26
- Activate the theme in an initializer or `config/application.rb`:
28
+ In a Rails app, the theme registers and activates automatically — no config needed. Outside Rails, it only registers; activate it explicitly:
27
29
 
28
30
  ```ruby
29
31
  StimulusPlumbers.configure do |config|
@@ -31,21 +33,13 @@ StimulusPlumbers.configure do |config|
31
33
  end
32
34
  ```
33
35
 
34
- Run the install generator once to inject the required `@source` directive into your Tailwind CSS entry file:
36
+ Run the install generator once to inject the required `@source` directive (and the `stimulus_plumbers` tokens.css + `animations.css` imports) into your Tailwind CSS entry file — see [docs/guide.md](docs/guide.md) for file-detection/override details:
35
37
 
36
38
  ```bash
37
- bin/rails generate stimulus_plumbers_tailwind:install
39
+ bin/rails generate stimulus_plumbers:tailwind:install
38
40
  ```
39
41
 
40
- The generator checks these files in order:
41
-
42
- - `app/assets/stylesheets/application.tailwind.css`
43
- - `app/assets/stylesheets/application.css`
44
- - `app/javascript/entrypoints/application.css`
45
-
46
- Override the detected file for both the generator and rake task with `TAILWIND_CSS_FILE=/path/to/entry.css`.
47
-
48
- After the initial install, the `@source` path is kept current automatically — no manual re-run needed after `bundle update`. The engine hooks `stimulus_plumbers_tailwind:install` as a prerequisite of:
42
+ After that, the `@source` path is kept current automatically — no manual re-run needed after `bundle update`. The engine hooks `stimulus_plumbers:tailwind:install` as a prerequisite of:
49
43
 
50
44
  - `assets:precompile` (Sprockets and Propshaft both define this task)
51
45
  - `tailwindcss:build` (when `tailwindcss-rails` is present)
@@ -53,7 +47,7 @@ After the initial install, the `@source` path is kept current automatically —
53
47
  To trigger an update manually without a full compile (useful for debugging or after `bundle update`):
54
48
 
55
49
  ```bash
56
- bin/rails stimulus_plumbers_tailwind:install
50
+ bin/rails stimulus_plumbers:tailwind:install
57
51
  ```
58
52
 
59
53
  ## Theming
@@ -69,9 +63,12 @@ bin/rails stimulus_plumbers_tailwind:install
69
63
  | `Tailwind::Combobox` | Combobox (date, time, dropdown, typeahead) |
70
64
  | `Tailwind::Form` | Form fields, labels, errors |
71
65
  | `Tailwind::Icon` | Icon (SVG rendering, icon registry) |
66
+ | `Tailwind::Indicator` | Indicator (dot, pulse, badge) |
72
67
  | `Tailwind::Layout` | Layout primitives |
73
68
  | `Tailwind::Link` | Link |
74
69
  | `Tailwind::List` | List |
70
+ | `Tailwind::OrderedList` | Reorderable list (drag handle, item styling) |
71
+ | `Tailwind::Progress` | Progress bar, progress ring, meter |
75
72
  | `Tailwind::Timeline` | Timeline |
76
73
 
77
74
  Custom themes can subclass `TailwindTheme` to override individual methods, or subclass `StimulusPlumbers::Themes::Base` directly.
@@ -92,7 +89,7 @@ Icons render as inline `<svg>` with `size-6` by default. Override with `class:`:
92
89
  <%= sp_icon "arrow-left", class: "size-4 text-gray-500" %>
93
90
  ```
94
91
 
95
- **Built-in aliases:** `"close"` → `"x-mark"`, `"calendar"` → `"calendar-days"`.
92
+ **Built-in aliases:** `"close"` → `"x-mark"`, `"download"` → `"arrow-down-tray"`, `"book"` → `"book-open"`, `"edit"` → `"pencil"`, `"email"` → `"envelope"`, `"calendar"` → `"calendar-days"`, `"external-link"` → `"arrow-top-right-on-square"`, `"reveal"` → `"eye"`, `"grip-vertical"` → `"bars-3"`.
96
93
 
97
94
  **Optional `heroicons` gem:** add `gem "heroicons"` to your `Gemfile` to load icons from the gem instead of the bundled files. The theme detects it automatically.
98
95
 
@@ -0,0 +1,18 @@
1
+ @theme {
2
+ --animate-progress-slide: sp-progress-slide 1.5s ease-in-out infinite;
3
+ }
4
+
5
+ @keyframes sp-progress-slide {
6
+ 0% { transform: translateX(-100%); }
7
+ 100% { transform: translateX(300%); }
8
+ }
9
+
10
+ /* Ring-only; core's transition covers width, not stroke-dashoffset. */
11
+ [data-progress-target="fill"] {
12
+ transition: stroke-dashoffset 0.3s ease;
13
+ }
14
+
15
+ /* Track color — kept out of the SVG so consumers can retheme it via CSS. */
16
+ svg[role="progressbar"] .track {
17
+ stroke: var(--sp-color-muted);
18
+ }
@@ -0,0 +1,50 @@
1
+ # Tailwind Theme Architecture
2
+
3
+ ## Module Layout
4
+
5
+ `TailwindTheme` extends `Base` and is split into concern modules under `StimulusPlumbers::Themes::Tailwind::*`. Each module provides CSS class resolution for one component family. The `Icon` module also owns the icon registry (`icons/`).
6
+
7
+ ## Constant Ownership Rules
8
+
9
+ - **No local aliases** — never re-export another module's constant (e.g. `MY_CONST = Other::CONST`). Reference the constant directly at the call site.
10
+ - **No prefix redundancy** — name constants after what they represent, not where they live. `Button::VARIANTS` not `Button::BUTTON_VARIANTS`; `Card::VARIANTS` not `Card::CARD_VARIANTS`.
11
+ - **Remove unused constants** — if a constant has no call sites in `schema.rb`, delete it.
12
+
13
+ ## Shared Interactive Foundation — `Control::BASE`
14
+
15
+ `Control::BASE` holds CSS shared by all focusable, disableable controls: font weight, transition, focus ring tokens, `disabled:*` states. Components include it via array splat and add their own ring color variable on top.
16
+
17
+ ```ruby
18
+ # Composition — correct
19
+ [*Control::BASE, *Button::BASE, "extra-class"].freeze
20
+
21
+ # Duplication — wrong
22
+ ["font-medium", "transition-colors", "extra-class"].freeze
23
+ ```
24
+
25
+ `Control::BASE` is always included first; never duplicate its classes in component constants.
26
+
27
+ ## Icon-Only Square Pattern
28
+
29
+ The core gem wraps button/link text in `<span>`. Icon-only renders no `<span>`. The theme detects this via CSS:
30
+
31
+ - `Button::LAYOUT` carries `[&:not(:has(>span))]:aspect-square [&:not(:has(>span))]:px-0`
32
+ - `Link::BUTTON` carries the same two classes
33
+ - `fab`/`fab_outline` types become circles via this mechanism
34
+
35
+ **Do not change the Ruby renderer's `<span>` wrapping contract without updating these theme constants.**
36
+
37
+ ## Card Style Pattern
38
+
39
+ Button, Link, Checkbox, and Radio all share a unified card style via the `--card-ring` CSS variable.
40
+
41
+ - `Card::VARIANTS` maps `:default/:success/:destructive/:warning/:info` to `--sp-color-*` values
42
+ - All card-type components reference `Card::VARIANTS.fetch(variant, Card::VARIANTS[:default])`
43
+ - **Button card** (`type: :card`) — `Button::BASE + Button::CARD + Button::TYPES[:card]`; `size:` ignored
44
+ - **Link card** — `Link::CARD` composes `Control::BASE + Card::BASE + Button::CARD`
45
+ - **Checkbox card** — input visible; label uses `has-[:checked]:border-(--card-ring)`
46
+ - **Radio card/button** — input is `hidden peer`; label uses `peer-checked:border-(--card-ring)`
47
+
48
+ ## Floating Field Key Ownership
49
+
50
+ Floating-label theme keys (`form_field_floating`, `form_field_floating_group`, `form_field_floating_label`) and their constants (`FLOATING_INPUT_*`, `FLOATING_GROUP_TYPES`, `FLOATING_LABEL_*`) live in `Form::Field` (`form/field.rb`), **not** `Form::Input`. Floating is a field-layout concern, not an input-element concern.
data/docs/guide.md ADDED
@@ -0,0 +1,23 @@
1
+ # Guide
2
+
3
+ Adding this gem registers and activates the Tailwind theme automatically in Rails — no config
4
+ needed. Outside Rails, activate it explicitly with `config.theme.use(:tailwind)`.
5
+
6
+ Run the install generator once to wire the required `@source` directive and the `animations.css`
7
+ import (registers the indeterminate progress bar's slide keyframe via `@theme`) into your Tailwind
8
+ CSS entry file:
9
+
10
+ ```bash
11
+ bin/rails generate stimulus_plumbers:tailwind:install
12
+ ```
13
+
14
+ It re-runs automatically on `assets:precompile`/`tailwindcss:build` after that. File detection and
15
+ the `STIMULUS_PLUMBERS_CSS_ENTRY` override are shared with the core gem — see
16
+ [stimulus-plumbers-rails/docs/guide.md#css-entry-file-detection](../../stimulus-plumbers-rails/docs/guide.md#css-entry-file-detection).
17
+
18
+ Icons: pass a kebab-case name to `sp_icon` or any `icon_leading:`/`icon_trailing:` option (append
19
+ `/solid` for the filled variant) — see [README.md](../README.md#icons) for aliases and the
20
+ optional `heroicons` gem.
21
+
22
+ To implement a custom theme instead of/alongside Tailwind, see
23
+ [stimulus-plumbers-rails/docs/component/theme.md](../../stimulus-plumbers-rails/docs/component/theme.md).
@@ -0,0 +1,61 @@
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
+ require "stimulus_plumbers/tailwind/generators/animations_directive"
7
+ require "stimulus_plumbers/tailwind/generators/sources_directive"
8
+
9
+ module StimulusPlumbers
10
+ module Tailwind
11
+ module Generators
12
+ class InstallGenerator < Rails::Generators::Base
13
+ include StimulusPlumbers::Generators::CssEntrypoint
14
+
15
+ def install
16
+ css_file = entry_css_file(**css_file_lookup_options)
17
+ return warn_entry_css_not_found(label: "Tailwind CSS", **css_file_lookup_options) unless css_file
18
+
19
+ apply_tokens_directive(css_file)
20
+ apply_animations_directive(css_file)
21
+ apply_sources_directive(css_file)
22
+ end
23
+
24
+ private
25
+
26
+ def css_file_lookup_options
27
+ {
28
+ candidates: StimulusPlumbers::Generators::CssEntrypoint::ENTRY_CANDIDATES,
29
+ env_var: StimulusPlumbers::Generators::CssEntrypoint::STIMULUS_PLUMBERS_CSS_ENTRY
30
+ }
31
+ end
32
+
33
+ def apply_tokens_directive(css_file)
34
+ apply_edit(
35
+ css_file,
36
+ StimulusPlumbers::Generators::TokensDirective.directive(from: File.dirname(css_file)),
37
+ stale_pattern: StimulusPlumbers::Generators::TokensDirective.stale_pattern
38
+ )
39
+ end
40
+
41
+ def apply_animations_directive(css_file)
42
+ apply_edit(
43
+ css_file,
44
+ AnimationsDirective.directive(from: File.dirname(css_file)),
45
+ anchor_pattern: AnimationsDirective.anchor_pattern,
46
+ stale_pattern: AnimationsDirective.stale_pattern
47
+ )
48
+ end
49
+
50
+ def apply_sources_directive(css_file)
51
+ apply_edit(
52
+ css_file,
53
+ SourcesDirective.directive(from: File.dirname(css_file)),
54
+ anchor_pattern: SourcesDirective.anchor_pattern,
55
+ stale_pattern: SourcesDirective.stale_pattern
56
+ )
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StimulusPlumbers
4
+ module Tailwind
5
+ class Engine < ::Rails::Engine
6
+ initializer "stimulus_plumbers_tailwind.register_theme" do
7
+ StimulusPlumbers.configure do |c|
8
+ c.theme.register(:tailwind, StimulusPlumbers::Themes::TailwindTheme)
9
+ c.theme.use(:tailwind)
10
+ end
11
+ end
12
+
13
+ rake_tasks do
14
+ load File.join(__dir__, "../../tasks/stimulus_plumbers_tailwind.rake")
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pathname"
4
+
5
+ module StimulusPlumbers
6
+ module Tailwind
7
+ module Generators
8
+ module AnimationsDirective
9
+ GEM_ROOT = File.expand_path("../../../..", __dir__)
10
+ ANIMATIONS_CSS_REL_PATH = "app/assets/stylesheets/stimulus_plumbers/tailwind/animations.css"
11
+
12
+ module_function
13
+
14
+ def directive(from:)
15
+ path = Pathname.new(GEM_ROOT).join(ANIMATIONS_CSS_REL_PATH)
16
+ rel = path.relative_path_from(Pathname.new(from))
17
+ %(@import "#{rel}";)
18
+ end
19
+
20
+ # Anchors right after the core tokens.css import so the two asset
21
+ # imports stay grouped, ahead of the tailwindcss import and @source lines.
22
+ def anchor_pattern
23
+ %r{@import "[^"]*tokens\.css";}
24
+ end
25
+
26
+ def stale_pattern
27
+ %r{@import "[^"]*#{Regexp.escape(ANIMATIONS_CSS_REL_PATH)}";}
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pathname"
4
+
5
+ module StimulusPlumbers
6
+ module Tailwind
7
+ module Generators
8
+ module SourcesDirective
9
+ GEM_NAME = "stimulus_plumbers_tailwind"
10
+ LIB_DIR = File.expand_path("../../..", __dir__)
11
+
12
+ module_function
13
+
14
+ def directive(from:)
15
+ rel = Pathname.new(LIB_DIR).relative_path_from(Pathname.new(from))
16
+ %(@source "#{rel}/**/*.rb";)
17
+ end
18
+
19
+ def anchor_pattern
20
+ %r{@import "tailwindcss"[^;]*;}
21
+ end
22
+
23
+ def stale_pattern
24
+ %r{@source "[^"]*#{Regexp.escape(GEM_NAME)}[^"]*";}
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module StimulusPlumbers
4
4
  module Tailwind
5
- VERSION = "0.4.5"
5
+ VERSION = "0.4.9"
6
6
  end
7
7
  end
@@ -147,7 +147,7 @@ module StimulusPlumbers
147
147
  end
148
148
 
149
149
  def button_icon_classes
150
- { classes: klasses("size-(--sp-icon-size-sm)", "stroke-current") }
150
+ { classes: klasses("stroke-current") }
151
151
  end
152
152
  end
153
153
  end
@@ -26,7 +26,7 @@ module StimulusPlumbers
26
26
  ].freeze
27
27
 
28
28
  ICON_BASE = %w[
29
- size-(--sp-space-6) shrink-0 stroke-current text-(--sp-color-muted-fg)
29
+ shrink-0 stroke-current text-(--sp-color-muted-fg)
30
30
  ].freeze
31
31
 
32
32
  TITLE_BASE = %w[
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StimulusPlumbers
4
+ module Themes
5
+ module Tailwind
6
+ module Checklist
7
+ WRAPPER_BASE = %w[flex flex-col gap-(--sp-space-1)].freeze
8
+
9
+ ITEM_BASE = [
10
+ *Control::BASE,
11
+ "group/checklist-item flex items-center gap-(--sp-space-2) w-full text-start",
12
+ "px-(--sp-space-2) py-(--sp-space-1)",
13
+ "rounded-(--sp-radius-sm) text-(length:--sp-text-sm)",
14
+ "cursor-pointer select-none text-(--sp-color-fg)",
15
+ "hover:bg-(--sp-color-muted)",
16
+ "has-disabled:cursor-default has-disabled:hover:bg-transparent"
17
+ ].freeze
18
+
19
+ INPUT_BASE = [
20
+ *Control::ACCENT,
21
+ "size-(--sp-control-size) rounded-(--sp-radius-sm) shrink-0",
22
+ "border border-(--sp-color-border) bg-(--sp-color-muted)",
23
+ "focus:ring-(length:--sp-focus-ring-width) focus:ring-(--sp-focus-ring-color) focus:outline-none",
24
+ "disabled:opacity-50 disabled:cursor-not-allowed cursor-pointer"
25
+ ].freeze
26
+
27
+ CONTENT_BASE = %w[flex flex-col flex-1 min-w-0].freeze
28
+
29
+ TITLE_BASE = %w[
30
+ text-(length:--sp-text-sm) font-medium text-(--sp-color-fg)
31
+ group-has-checked/checklist-item:line-through
32
+ group-has-checked/checklist-item:text-(--sp-color-muted-fg)
33
+ ].freeze
34
+
35
+ DESCRIPTION_BASE = %w[text-(length:--sp-text-xs) text-(--sp-color-muted-fg)].freeze
36
+
37
+ private
38
+
39
+ def checklist_classes
40
+ { classes: klasses(*WRAPPER_BASE) }
41
+ end
42
+
43
+ def checklist_item_classes
44
+ { classes: klasses(*ITEM_BASE) }
45
+ end
46
+
47
+ def checklist_item_input_classes
48
+ { classes: klasses(*INPUT_BASE) }
49
+ end
50
+
51
+ def checklist_item_content_classes
52
+ { classes: klasses(*CONTENT_BASE) }
53
+ end
54
+
55
+ def checklist_item_title_classes
56
+ { classes: klasses(*TITLE_BASE) }
57
+ end
58
+
59
+ def checklist_item_description_classes
60
+ { classes: klasses(*DESCRIPTION_BASE) }
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -112,7 +112,7 @@ module StimulusPlumbers
112
112
  end
113
113
 
114
114
  def combobox_typeahead_loading_icon_classes
115
- { classes: klasses("size-(--sp-icon-size-md)", "animate-spin") }
115
+ { classes: klasses("animate-spin") }
116
116
  end
117
117
 
118
118
  def combobox_typeahead_empty_classes
@@ -11,6 +11,8 @@ module StimulusPlumbers
11
11
  focus-visible:ring-offset-(length:--sp-focus-ring-offset)
12
12
  disabled:pointer-events-none disabled:opacity-50
13
13
  ].freeze
14
+
15
+ ACCENT = %w[[accent-color:var(--sp-color-primary)] cursor-pointer].freeze
14
16
  end
15
17
  end
16
18
  end
@@ -80,21 +80,24 @@ module StimulusPlumbers
80
80
 
81
81
  # ── Choice inputs ─────────────────────────────────────────────────────
82
82
  CHECKBOX_TYPES = {
83
- default: %w[
84
- size-(--sp-control-size) rounded-(--sp-radius-sm) shrink-0
85
- border border-(--sp-color-border) bg-(--sp-color-muted)
86
- focus:ring-(length:--sp-focus-ring-width) focus:ring-(--sp-focus-ring-color) focus:outline-none
87
- disabled:opacity-50 disabled:cursor-not-allowed cursor-pointer
83
+ default: [
84
+ *Control::ACCENT,
85
+ "size-(--sp-control-size) rounded-(--sp-radius-sm) shrink-0",
86
+ "border border-(--sp-color-border) bg-(--sp-color-muted)",
87
+ "focus:ring-(length:--sp-focus-ring-width) focus:ring-(--sp-focus-ring-color) focus:outline-none",
88
+ "disabled:opacity-50 disabled:cursor-not-allowed cursor-pointer"
88
89
  ].freeze,
89
- button: %w[
90
- size-(--sp-control-size) rounded-(--sp-radius-sm) shrink-0
91
- border border-(--sp-color-border) bg-(--sp-color-muted)
92
- focus:ring-(length:--sp-focus-ring-width) focus:ring-(--sp-focus-ring-color) focus:outline-none
93
- disabled:opacity-50 disabled:cursor-not-allowed cursor-pointer
90
+ button: [
91
+ *Control::ACCENT,
92
+ "size-(--sp-control-size) rounded-(--sp-radius-sm) shrink-0",
93
+ "border border-(--sp-color-border) bg-(--sp-color-muted)",
94
+ "focus:ring-(length:--sp-focus-ring-width) focus:ring-(--sp-focus-ring-color) focus:outline-none",
95
+ "disabled:opacity-50 disabled:cursor-not-allowed cursor-pointer"
94
96
  ].freeze,
95
97
  card: %w[
96
98
  size-(--sp-control-size) rounded-(--sp-radius-sm) shrink-0
97
99
  border border-(--sp-color-border) bg-(--sp-color-muted)
100
+ [accent-color:var(--card-ring)] cursor-pointer
98
101
  checked:border-(--card-ring)
99
102
  focus:ring-(length:--sp-focus-ring-width) focus:ring-(--card-ring) focus:outline-none
100
103
  disabled:opacity-50 disabled:cursor-not-allowed cursor-pointer
@@ -102,11 +105,11 @@ module StimulusPlumbers
102
105
  }.freeze
103
106
 
104
107
  RADIO_TYPES = {
105
- default: %w[
106
- size-(--sp-control-size) rounded-full shrink-0
107
- [accent-color:var(--sp-color-primary)] cursor-pointer
108
- focus:ring-(length:--sp-focus-ring-width) focus:ring-(--sp-focus-ring-color) focus:outline-none
109
- disabled:opacity-50 disabled:cursor-not-allowed
108
+ default: [
109
+ *Control::ACCENT,
110
+ "size-(--sp-control-size) rounded-full shrink-0",
111
+ "focus:ring-(length:--sp-focus-ring-width) focus:ring-(--sp-focus-ring-color) focus:outline-none",
112
+ "disabled:opacity-50 disabled:cursor-not-allowed"
110
113
  ].freeze,
111
114
  button: %w[hidden].freeze,
112
115
  card: %w[hidden].freeze
@@ -16,7 +16,8 @@ module StimulusPlumbers
16
16
  "email" => "envelope",
17
17
  "calendar" => "calendar-days",
18
18
  "external-link" => "arrow-top-right-on-square",
19
- "reveal" => "eye"
19
+ "reveal" => "eye",
20
+ "grip-vertical" => "bars-3"
20
21
  }.freeze
21
22
 
22
23
  ICONS = StimulusPlumbers::Themes::Icons::Registry.new(
@@ -24,14 +25,20 @@ module StimulusPlumbers
24
25
  aliases: ALIASES
25
26
  )
26
27
 
28
+ SIZES = {
29
+ sm: "size-(--sp-icon-size-sm)",
30
+ md: "size-(--sp-icon-size-md)",
31
+ lg: "size-(--sp-icon-size-lg)"
32
+ }.freeze
33
+
27
34
  def icons
28
35
  ICONS
29
36
  end
30
37
 
31
38
  private
32
39
 
33
- def icon_classes
34
- { classes: "size-(--sp-icon-size-lg)" }
40
+ def icon_classes(size: :lg)
41
+ { classes: SIZES.fetch(size, SIZES[:lg]) }
35
42
  end
36
43
  end
37
44
  end
@@ -0,0 +1,12 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 44 44" width="44" height="44" fill="none">
2
+ <circle cx="22" cy="22" r="18" stroke-width="3" class="track"></circle>
3
+ <circle
4
+ cx="22"
5
+ cy="22"
6
+ r="18"
7
+ stroke="currentColor"
8
+ stroke-width="3"
9
+ stroke-linecap="round"
10
+ data-progress-target="fill"
11
+ ></circle>
12
+ </svg>
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StimulusPlumbers
4
+ module Themes
5
+ module Tailwind
6
+ module Indicator
7
+ VARIANTS = {
8
+ primary: "bg-(--sp-color-primary)",
9
+ secondary: "bg-(--sp-color-secondary)",
10
+ tertiary: "bg-(--sp-color-muted-fg)",
11
+ success: "bg-(--sp-color-success)",
12
+ destructive: "bg-(--sp-color-destructive)",
13
+ warning: "bg-(--sp-color-warning)",
14
+ info: "bg-(--sp-color-info)"
15
+ }.freeze
16
+
17
+ DOT = %w[inline-flex size-2.5 rounded-full].freeze
18
+ BADGE = %w[
19
+ inline-flex items-center justify-center
20
+ min-w-5 h-5 px-1
21
+ rounded-full text-(length:--sp-text-xs) font-medium text-white
22
+ ].freeze
23
+
24
+ # Establishes the positioning context the pulse ring overlays onto — no margin needed.
25
+ WRAPPER = %w[relative inline-flex items-center justify-center].freeze
26
+
27
+ PULSE_RING = %w[
28
+ absolute inset-0 rounded-full
29
+ bg-(--sp-color-indicator) opacity-75
30
+ animate-ping motion-reduce:animate-none
31
+ ].freeze
32
+
33
+ private
34
+
35
+ def indicator_classes(type: :dot, variant: nil)
36
+ { classes: klasses(*(type.to_sym == :badge ? BADGE : DOT), VARIANTS.fetch(variant, nil)) }
37
+ end
38
+
39
+ def indicator_wrapper_classes
40
+ { classes: klasses(*WRAPPER) }
41
+ end
42
+
43
+ def indicator_pulse_classes
44
+ { classes: klasses(*PULSE_RING) }
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -79,7 +79,7 @@ module StimulusPlumbers
79
79
  end
80
80
 
81
81
  def link_icon_classes
82
- { classes: klasses("size-(--sp-icon-size-sm)", "stroke-current") }
82
+ { classes: klasses("stroke-current") }
83
83
  end
84
84
  end
85
85
  end
@@ -64,7 +64,7 @@ module StimulusPlumbers
64
64
  end
65
65
 
66
66
  def list_item_icon_classes
67
- { classes: klasses("size-(--sp-icon-size-sm)", "stroke-current") }
67
+ { classes: klasses("stroke-current") }
68
68
  end
69
69
 
70
70
  def list_item_content_classes
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StimulusPlumbers
4
+ module Themes
5
+ module Tailwind
6
+ module OrderedList
7
+ # ── Item ──────────────────────────────────────────────────────────────
8
+ ITEM_BASE = [
9
+ *Control::BASE,
10
+ "flex items-center gap-(--sp-space-2) w-full",
11
+ "px-(--sp-space-2) py-(--sp-space-1)",
12
+ "rounded-(--sp-radius-sm) text-(length:--sp-text-sm)",
13
+ "text-(--sp-color-fg)",
14
+ "focus-visible:ring-(--sp-color-primary-ring)",
15
+ "aria-[current]:bg-(--sp-color-primary)/10 aria-[current]:text-(--sp-color-primary)"
16
+ ].freeze
17
+
18
+ HANDLE_BASE = %w[
19
+ flex items-center justify-center shrink-0
20
+ stroke-current
21
+ text-(--sp-color-muted-fg)
22
+ cursor-grab active:cursor-grabbing
23
+ touch-none
24
+ ].freeze
25
+
26
+ CONTENT_BASE = %w[
27
+ flex flex-col flex-1 min-w-0
28
+ ].freeze
29
+
30
+ TITLE_BASE = %w[
31
+ text-(length:--sp-text-sm) font-medium text-(--sp-color-fg)
32
+ ].freeze
33
+
34
+ DESCRIPTION_BASE = %w[
35
+ text-(length:--sp-text-xs) text-(--sp-color-muted-fg)
36
+ ].freeze
37
+
38
+ private
39
+
40
+ def ordered_list_classes
41
+ { classes: klasses("flex flex-col gap-(--sp-space-1) py-(--sp-space-1)") }
42
+ end
43
+
44
+ def ordered_list_item_classes
45
+ { classes: klasses(*ITEM_BASE) }
46
+ end
47
+
48
+ def ordered_list_item_handle_classes
49
+ { classes: klasses(*HANDLE_BASE) }
50
+ end
51
+
52
+ def ordered_list_item_content_classes
53
+ { classes: klasses(*CONTENT_BASE) }
54
+ end
55
+
56
+ def ordered_list_item_title_classes
57
+ { classes: klasses(*TITLE_BASE) }
58
+ end
59
+
60
+ def ordered_list_item_description_classes
61
+ { classes: klasses(*DESCRIPTION_BASE) }
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StimulusPlumbers
4
+ module Themes
5
+ module Tailwind
6
+ module Progress
7
+ BAR = %w[
8
+ relative w-full h-2 overflow-hidden rounded-full
9
+ bg-(--sp-color-muted)
10
+ ].freeze
11
+
12
+ BAR_FILL = %w[
13
+ h-full rounded-full bg-(--sp-color-primary)
14
+ [.sp-progress-indeterminate_&]:animate-progress-slide
15
+ [.sp-progress-indeterminate_&]:motion-reduce:animate-none
16
+ ].freeze
17
+
18
+ # Circle stroke/fill live in icons/customs/progress-ring.svg — @source never scans
19
+ # .svg files, so Tailwind classes there are inert.
20
+ # Indeterminate class and utility land on this same svg, so this needs the compound
21
+ # [&.foo] form, not BAR_FILL's [.foo_&] descendant form.
22
+ RING = %w[
23
+ -rotate-90 text-(--sp-color-primary)
24
+ [&.sp-progress-indeterminate]:animate-spin
25
+ ].freeze
26
+
27
+ METER = %w[
28
+ w-full h-2 rounded-full
29
+ [&::-webkit-meter-bar]:rounded-full [&::-webkit-meter-bar]:bg-(--sp-color-muted)
30
+ [&::-webkit-meter-optimum-value]:bg-(--sp-color-success)
31
+ [&::-webkit-meter-suboptimum-value]:bg-(--sp-color-warning)
32
+ [&::-webkit-meter-even-less-good-value]:bg-(--sp-color-destructive)
33
+ [&::-moz-meter-bar]:bg-(--sp-color-success)
34
+ ].freeze
35
+
36
+ private
37
+
38
+ def progress_bar_classes = { classes: klasses(*BAR) }
39
+ def progress_bar_fill_classes = { classes: klasses(*BAR_FILL) }
40
+ def progress_ring_classes = { classes: klasses(*RING) }
41
+ def progress_meter_classes = { classes: klasses(*METER) }
42
+ end
43
+ end
44
+ end
45
+ end
@@ -5,22 +5,31 @@ module StimulusPlumbers
5
5
  module Tailwind
6
6
  module Timeline
7
7
  module Group
8
- WRAPPER = %w[space-y-4].freeze
9
- SECTION = %w[
8
+ WRAPPER = %w[flex flex-col gap-y-4].freeze
9
+ SECTION = %w[
10
10
  p-4
11
11
  bg-(--sp-color-bg-muted)
12
12
  border border-(--sp-color-border)
13
13
  rounded-lg
14
14
  ].freeze
15
- DATE = %w[text-base font-semibold text-(--sp-color-fg)].freeze
16
- LIST = %w[mt-3 divide-y divide-(--sp-color-border)].freeze
15
+ DATE = %w[text-base font-semibold text-(--sp-color-fg)].freeze
16
+ LIST_VERTICAL = %w[
17
+ pt-3 flex flex-col
18
+ divide-y divide-(--sp-color-border)
19
+ [&>li]:py-2 [&>li]:first:pt-0 [&>li]:last:pb-0
20
+ ].freeze
21
+ LIST_HORIZONTAL = %w[pt-3 flex gap-x-4].freeze
17
22
 
18
23
  private
19
24
 
20
25
  def timeline_group_classes = { classes: klasses(WRAPPER) }
21
26
  def timeline_group_section_classes = { classes: klasses(SECTION) }
22
27
  def timeline_group_section_date_classes = { classes: klasses(DATE) }
23
- def timeline_group_section_list_classes = { classes: klasses(LIST) }
28
+
29
+ def timeline_group_section_list_classes(orientation: :vertical)
30
+ list = orientation.to_sym == :horizontal ? LIST_HORIZONTAL : LIST_VERTICAL
31
+ { classes: klasses(list) }
32
+ end
24
33
  end
25
34
  end
26
35
  end
@@ -18,7 +18,6 @@ module StimulusPlumbers
18
18
  mt-1 flex shrink-0 size-6 items-center justify-center rounded-full z-10
19
19
  bg-(--sp-color-bg) ring-4 ring-(--sp-color-bg)
20
20
  ].freeze
21
- ITEM_INDICATOR_DOT_MARK = %w[size-3 rounded-full bg-(--sp-color-indicator)].freeze
22
21
 
23
22
  ITEM_INDICATOR_ICON = %w[
24
23
  mt-1 flex shrink-0 size-6 items-center justify-center rounded-full z-10
@@ -26,7 +25,7 @@ module StimulusPlumbers
26
25
  ring-4 ring-(--sp-color-bg)
27
26
  ].freeze
28
27
 
29
- ITEM_INDICATOR_ICON_SLOT = %w[size-(--sp-icon-size-sm) stroke-current].freeze
28
+ ITEM_INDICATOR_ICON_SLOT = %w[stroke-current].freeze
30
29
 
31
30
  ITEM_INDICATOR_DOT_HORIZONTAL = %w[
32
31
  z-10 flex shrink-0 size-6 items-center justify-center rounded-full
@@ -50,12 +49,12 @@ module StimulusPlumbers
50
49
  ].freeze
51
50
  ITEM_TITLE = %w[mb-1 text-base font-semibold text-(--sp-color-fg)].freeze
52
51
  ITEM_HEADING = %w[mb-1].freeze
53
- ITEM_TRIGGER = %w[
54
- w-full text-left text-base font-semibold
55
- text-(--sp-color-fg)
56
- hover:text-(--sp-color-primary)
57
- focus-visible:outline-none focus-visible:ring-2
58
- focus-visible:ring-(--sp-color-primary) focus-visible:rounded-sm
52
+ ITEM_TRIGGER = [
53
+ *Control::BASE,
54
+ "w-full text-left text-base font-semibold",
55
+ "text-(--sp-color-fg)",
56
+ "hover:text-(--sp-color-primary)",
57
+ "focus-visible:ring-(--sp-color-primary-ring) focus-visible:rounded-sm"
59
58
  ].freeze
60
59
  ITEM_DESCRIPTION = %w[text-sm text-(--sp-color-muted-fg)].freeze
61
60
  ITEM_DETAIL = %w[mt-2 text-sm text-(--sp-color-muted-fg)].freeze
@@ -93,7 +92,6 @@ module StimulusPlumbers
93
92
  def timeline_item_description_classes = { classes: klasses(ITEM_DESCRIPTION) }
94
93
  def timeline_item_detail_classes = { classes: klasses(ITEM_DETAIL) }
95
94
  def timeline_item_actions_classes = { classes: klasses(ITEM_ACTIONS) }
96
- def timeline_item_indicator_dot_classes = { classes: klasses(ITEM_INDICATOR_DOT_MARK) }
97
95
  def timeline_item_connector_classes = { classes: klasses(ITEM_CONNECTOR_HORIZONTAL) }
98
96
  def timeline_item_content_classes = { classes: klasses(ITEM_CONTENT_HORIZONTAL) }
99
97
  def timeline_track_line_classes = { classes: klasses(TRACK_LINE_VERTICAL) }
@@ -1,7 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "tailwind/control"
4
+ require_relative "tailwind/checklist"
4
5
  require_relative "tailwind/list"
6
+ require_relative "tailwind/ordered_list"
5
7
  require_relative "tailwind/avatar"
6
8
  require_relative "tailwind/button"
7
9
  require_relative "tailwind/button/group"
@@ -12,14 +14,18 @@ require_relative "tailwind/form"
12
14
  require_relative "tailwind/form/field"
13
15
  require_relative "tailwind/form/input"
14
16
  require_relative "tailwind/icon"
17
+ require_relative "tailwind/indicator"
15
18
  require_relative "tailwind/layout"
16
19
  require_relative "tailwind/link"
20
+ require_relative "tailwind/progress"
17
21
  require_relative "tailwind/timeline"
18
22
 
19
23
  module StimulusPlumbers
20
24
  module Themes
21
25
  class TailwindTheme < Base
26
+ include Tailwind::Checklist
22
27
  include Tailwind::List
28
+ include Tailwind::OrderedList
23
29
  include Tailwind::Combobox
24
30
  include Tailwind::Avatar
25
31
  include Tailwind::Button
@@ -30,8 +36,10 @@ module StimulusPlumbers
30
36
  include Tailwind::Form::Field
31
37
  include Tailwind::Form::Input
32
38
  include Tailwind::Icon
39
+ include Tailwind::Indicator
33
40
  include Tailwind::Layout
34
41
  include Tailwind::Link
42
+ include Tailwind::Progress
35
43
  include Tailwind::Timeline
36
44
  include Tailwind::Timeline::Group
37
45
 
@@ -6,7 +6,7 @@ require_relative "stimulus_plumbers/tailwind/version"
6
6
  require_relative "stimulus_plumbers/themes/tailwind_theme"
7
7
 
8
8
  if defined?(Rails::Engine)
9
- require_relative "stimulus_plumbers_tailwind/engine"
9
+ require_relative "stimulus_plumbers/tailwind/engine"
10
10
  else
11
11
  StimulusPlumbers.configure do |c|
12
12
  c.theme.register(:tailwind, StimulusPlumbers::Themes::TailwindTheme)
@@ -1,15 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- namespace :stimulus_plumbers_tailwind do
4
- desc "Add @source directive for stimulus_plumbers_tailwind into Tailwind CSS entry file"
5
- task install: :environment do
6
- require "generators/stimulus_plumbers_tailwind/install/install_generator"
7
- StimulusPlumbersTailwind::Generators::InstallGenerator.new(
8
- [], {}, { destination_root: Rails.root.to_s }
9
- ).invoke_all
3
+ namespace :stimulus_plumbers do
4
+ namespace :tailwind do
5
+ desc "Add @source directive for stimulus_plumbers_tailwind into Tailwind CSS entry file"
6
+ task(:install) do
7
+ unless ENV["STIMULUS_PLUMBERS_SKIP_INSTALL"]
8
+ require "generators/stimulus_plumbers/tailwind/install/install_generator"
9
+ StimulusPlumbers::Tailwind::Generators::InstallGenerator.new(
10
+ [], {}, { destination_root: Rails.root.to_s }
11
+ ).invoke_all
12
+ end
13
+ end
10
14
  end
11
15
  end
12
16
 
13
- Rake::Task["assets:precompile"].enhance(["stimulus_plumbers_tailwind:install"]) if Rake::Task.task_defined?("assets:precompile")
17
+ Rake::Task["stimulus_plumbers:tailwind:install"].enhance(["environment"]) unless ENV["STIMULUS_PLUMBERS_SKIP_INSTALL"]
14
18
 
15
- Rake::Task["tailwindcss:build"].enhance(["stimulus_plumbers_tailwind:install"]) if Rake::Task.task_defined?("tailwindcss:build")
19
+ # Hooks into these tasks as a prerequisite; Rake merges rather than overwrites, so order-independent.
20
+ task "assets:precompile" => ["stimulus_plumbers:tailwind:install"]
21
+ task "tailwindcss:build" => ["stimulus_plumbers:tailwind:install"]
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stimulus_plumbers_tailwind
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.5
4
+ version: 0.4.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryan Chang
@@ -33,13 +33,20 @@ files:
33
33
  - CHANGELOG.md
34
34
  - LICENSE-DEPENDENCIES
35
35
  - README.md
36
- - lib/generators/stimulus_plumbers_tailwind/install/install_generator.rb
36
+ - app/assets/stylesheets/stimulus_plumbers/tailwind/animations.css
37
+ - docs/architecture.md
38
+ - docs/guide.md
39
+ - lib/generators/stimulus_plumbers/tailwind/install/install_generator.rb
40
+ - lib/stimulus_plumbers/tailwind/engine.rb
41
+ - lib/stimulus_plumbers/tailwind/generators/animations_directive.rb
42
+ - lib/stimulus_plumbers/tailwind/generators/sources_directive.rb
37
43
  - lib/stimulus_plumbers/tailwind/version.rb
38
44
  - lib/stimulus_plumbers/themes/tailwind/avatar.rb
39
45
  - lib/stimulus_plumbers/themes/tailwind/button.rb
40
46
  - lib/stimulus_plumbers/themes/tailwind/button/group.rb
41
47
  - lib/stimulus_plumbers/themes/tailwind/calendar.rb
42
48
  - lib/stimulus_plumbers/themes/tailwind/card.rb
49
+ - lib/stimulus_plumbers/themes/tailwind/checklist.rb
43
50
  - lib/stimulus_plumbers/themes/tailwind/combobox.rb
44
51
  - lib/stimulus_plumbers/themes/tailwind/control.rb
45
52
  - lib/stimulus_plumbers/themes/tailwind/form.rb
@@ -47,6 +54,7 @@ files:
47
54
  - lib/stimulus_plumbers/themes/tailwind/form/input.rb
48
55
  - lib/stimulus_plumbers/themes/tailwind/icon.rb
49
56
  - lib/stimulus_plumbers/themes/tailwind/icons/custom.rb
57
+ - lib/stimulus_plumbers/themes/tailwind/icons/customs/progress-ring.svg
50
58
  - lib/stimulus_plumbers/themes/tailwind/icons/customs/spinner.svg
51
59
  - lib/stimulus_plumbers/themes/tailwind/icons/heroicon.rb
52
60
  - lib/stimulus_plumbers/themes/tailwind/icons/heroicons/outline/academic-cap.svg
@@ -697,14 +705,16 @@ files:
697
705
  - lib/stimulus_plumbers/themes/tailwind/icons/heroicons/solid/wrench.svg
698
706
  - lib/stimulus_plumbers/themes/tailwind/icons/heroicons/solid/x-circle.svg
699
707
  - lib/stimulus_plumbers/themes/tailwind/icons/heroicons/solid/x-mark.svg
708
+ - lib/stimulus_plumbers/themes/tailwind/indicator.rb
700
709
  - lib/stimulus_plumbers/themes/tailwind/layout.rb
701
710
  - lib/stimulus_plumbers/themes/tailwind/link.rb
702
711
  - lib/stimulus_plumbers/themes/tailwind/list.rb
712
+ - lib/stimulus_plumbers/themes/tailwind/ordered_list.rb
713
+ - lib/stimulus_plumbers/themes/tailwind/progress.rb
703
714
  - lib/stimulus_plumbers/themes/tailwind/timeline.rb
704
715
  - lib/stimulus_plumbers/themes/tailwind/timeline/group.rb
705
716
  - lib/stimulus_plumbers/themes/tailwind_theme.rb
706
717
  - lib/stimulus_plumbers_tailwind.rb
707
- - lib/stimulus_plumbers_tailwind/engine.rb
708
718
  - lib/tasks/stimulus_plumbers_tailwind.rake
709
719
  homepage: https://github.com/ryancyq/stimulus-plumbers
710
720
  licenses:
@@ -1,78 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "rails/generators"
4
-
5
- module StimulusPlumbersTailwind
6
- module Generators
7
- class InstallGenerator < Rails::Generators::Base
8
- GEM_NAME = "stimulus_plumbers_tailwind"
9
- TAILWIND_CSS_FILE = "TAILWIND_CSS_FILE"
10
- CSS_CANDIDATES = %w[
11
- app/assets/stylesheets/application.tailwind.css
12
- app/assets/stylesheets/application.css
13
- app/javascript/entrypoints/application.css
14
- ].freeze
15
-
16
- def install
17
- css_file = entry_css_file
18
- return warn_entry_css_not_found unless css_file
19
-
20
- apply_edit(css_file)
21
- end
22
-
23
- private
24
-
25
- def warn_entry_css_not_found
26
- tried = CSS_CANDIDATES.map { |c| File.join(destination_root, c) }
27
- tried.unshift(File.expand_path(ENV[TAILWIND_CSS_FILE])) if ENV[TAILWIND_CSS_FILE]
28
- say "Could not find a Tailwind CSS entry file. Tried: #{tried.join(", ")}. " \
29
- "Set #{TAILWIND_CSS_FILE}=/path/to/entry.css and re-run.",
30
- :red
31
- end
32
-
33
- def apply_edit(css_file)
34
- content = File.read(css_file)
35
- new_content, status = content_edit(content, source_directive)
36
-
37
- if new_content
38
- File.write(css_file, new_content)
39
- say_status status, relative_to_destination(css_file), :green
40
- else
41
- say_status :identical, relative_to_destination(css_file)
42
- end
43
- end
44
-
45
- def content_edit(content, source_line)
46
- if content.include?(source_line)
47
- nil
48
- elsif (existing = content.match(%r{@source "[^"]*#{Regexp.escape(GEM_NAME)}[^"]*";}))
49
- [content.sub(existing[0], source_line), :update]
50
- elsif (import_line = content.match(%r{@import "tailwindcss"[^;]*;}))
51
- [content.sub(import_line[0], "#{import_line[0]}\n#{source_line}"), :insert]
52
- else
53
- ["#{content.rstrip}\n#{source_line}\n", :append]
54
- end
55
- end
56
-
57
- def source_directive
58
- spec = Gem.loaded_specs[GEM_NAME]
59
- %(@source "#{spec.gem_dir}/lib/**/*.rb";)
60
- end
61
-
62
- def entry_css_file
63
- if ENV[TAILWIND_CSS_FILE]
64
- path = File.expand_path(ENV[TAILWIND_CSS_FILE])
65
- return path if File.exist?(path)
66
- end
67
-
68
- CSS_CANDIDATES
69
- .map { |c| File.join(destination_root, c) }
70
- .find { |f| File.exist?(f) }
71
- end
72
-
73
- def relative_to_destination(path)
74
- path.delete_prefix("#{destination_root}/")
75
- end
76
- end
77
- end
78
- end
@@ -1,15 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module StimulusPlumbersTailwind
4
- class Engine < ::Rails::Engine
5
- initializer "stimulus_plumbers_tailwind.register_theme" do
6
- StimulusPlumbers.configure do |c|
7
- c.theme.register(:tailwind, StimulusPlumbers::Themes::TailwindTheme)
8
- end
9
- end
10
-
11
- rake_tasks do
12
- load File.join(__dir__, "../tasks/stimulus_plumbers_tailwind.rake")
13
- end
14
- end
15
- end