tailmix 0.1.0 → 0.4.5

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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +3 -0
  3. data/README.md +147 -85
  4. data/app/javascript/tailmix/finder.js +12 -0
  5. data/app/javascript/tailmix/index.js +7 -0
  6. data/app/javascript/tailmix/mutator.js +28 -0
  7. data/app/javascript/tailmix/runner.js +7 -0
  8. data/app/javascript/tailmix/stimulus_adapter.js +37 -0
  9. data/examples/_modal_component.arb +36 -0
  10. data/examples/modal_component.rb +180 -0
  11. data/lib/generators/tailmix/install_generator.rb +19 -0
  12. data/lib/tailmix/definition/context_builder.rb +39 -0
  13. data/lib/tailmix/definition/contexts/action_builder.rb +31 -0
  14. data/lib/tailmix/definition/contexts/actions/element_builder.rb +30 -0
  15. data/lib/tailmix/definition/contexts/attribute_builder.rb +21 -0
  16. data/lib/tailmix/definition/contexts/dimension_builder.rb +16 -0
  17. data/lib/tailmix/definition/contexts/element_builder.rb +41 -0
  18. data/lib/tailmix/definition/contexts/stimulus_builder.rb +101 -0
  19. data/lib/tailmix/definition/merger.rb +93 -0
  20. data/lib/tailmix/definition/result.rb +31 -0
  21. data/lib/tailmix/definition.rb +11 -0
  22. data/lib/tailmix/dev/docs.rb +82 -0
  23. data/lib/tailmix/dev/stimulus_generator.rb +124 -0
  24. data/lib/tailmix/dev/tools.rb +26 -0
  25. data/lib/tailmix/engine.rb +17 -0
  26. data/lib/tailmix/html/attributes.rb +71 -0
  27. data/lib/tailmix/html/class_list.rb +79 -0
  28. data/lib/tailmix/html/data_map.rb +95 -0
  29. data/lib/tailmix/html/stimulus_builder.rb +65 -0
  30. data/lib/tailmix/runtime/action.rb +51 -0
  31. data/lib/tailmix/runtime/context.rb +66 -0
  32. data/lib/tailmix/runtime/facade_builder.rb +23 -0
  33. data/lib/tailmix/runtime/stimulus/compiler.rb +59 -0
  34. data/lib/tailmix/runtime.rb +14 -0
  35. data/lib/tailmix/version.rb +1 -1
  36. data/lib/tailmix.rb +48 -18
  37. metadata +33 -10
  38. data/examples/status_badge_component.rb +0 -44
  39. data/lib/tailmix/dimension.rb +0 -18
  40. data/lib/tailmix/element.rb +0 -24
  41. data/lib/tailmix/manager.rb +0 -58
  42. data/lib/tailmix/part.rb +0 -39
  43. data/lib/tailmix/resolver.rb +0 -28
  44. data/lib/tailmix/schema.rb +0 -18
  45. data/lib/tailmix/utils.rb +0 -15
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7c517006187e0dda710f20903df6e442b7d6fd844384213b7e031c2c7681342b
4
- data.tar.gz: 833dbd51ffbe9a84d9c7ac09057b1d6f7867495d1fc09ffd7339d2addd9ec4dd
3
+ metadata.gz: 6092dfded3153a41a28c7ca05e13fdbd52836c86b92576606aaafaabba030d12
4
+ data.tar.gz: 2abf62eb8efb4a6fa6e7bc42da9a55e7e09610c9ad4a688af9030fb5a94c2d26
5
5
  SHA512:
6
- metadata.gz: c63e70ec35d7894f0600aca24b6b48d5b4690606e278d87da574cdcd2a7521bfa5cc0b4297123cefbf014ca56c3143feab7bcb52a23b5e2073115395d1174d15
7
- data.tar.gz: e8eb45f22300c45e63aa0c47298c33ed8454f8940400ab945e99558e74e22a4c7adad5f5a30f6c45eed23648cf8a8384274255b496bf36edee7c82435451cee8
6
+ metadata.gz: ba691f44a9ae33dab521b918f89858cbd2d4d08ae867330845ff403764a24ae63c87eafc1a20b8b73b5a1b9c1c4762673398f0b4c79427186bb42e96e4d48f40
7
+ data.tar.gz: b7bb32552b876c9c59d3c2e82450e3200fe4fe3aa4267c4b6ef94f1151836bbfa10f74a10ac80f7044a0fe2fefd69cf8f8d0a0717d9749abe8dc8b1f99e2d42e
data/.rubocop.yml CHANGED
@@ -1,3 +1,6 @@
1
+ inherit_gem:
2
+ rubocop-rails-omakase: rubocop.yml
3
+
1
4
  AllCops:
2
5
  TargetRubyVersion: 3.1
3
6
 
data/README.md CHANGED
@@ -1,148 +1,210 @@
1
- # tailmix
1
+ # Tailmix
2
+
3
+ **Tailmix** is a powerful, declarative, and interactive CSS class manager for building maintainable UI components in Ruby. It's designed to work seamlessly with utility-first CSS frameworks like **Tailwind CSS**, allowing you to co-locate your style logic with your component's code—in a clean, structured, and highly reusable way.
2
4
 
3
5
  [![Gem Version](https://badge.fury.io/rb/tailmix.svg)](https://badge.fury.io/rb/tailmix)
4
6
  [![Build Status](https://github.com/alexander-s-f/tailmix/actions/workflows/main.yml/badge.svg)](https://github.com/alexander-s-f/tailmix/actions/workflows/main.yml)
5
7
 
6
- **Tailmix** is a powerful, declarative, and interactive class manager for building maintainable UI components in Ruby. It's designed to work seamlessly with utility-first CSS frameworks like **Tailwind CSS**, allowing you to co-locate your style logic with your component's code in a clean, structured, and highly reusable way.
7
-
8
- Inspired by modern frontend tools like CVA (Class Variance Authority), `tailmix` brings a robust styling engine to your server-side components (like those built with Arbre, ViewComponent, or Phlex).
8
+ Inspired by modern frontend tools like CVA (Class Variance Authority), Tailmix brings a robust styling engine to your server-side components (built with Arbre, ViewComponent, Phlex, etc.).
9
9
 
10
10
  ## Philosophy
11
11
 
12
12
  * **Co-location & Isolation:** Define all style variants for a component directly within its class. No more hunting for styles in separate files. Each component is fully self-contained.
13
- * **Declarative First:** A beautiful DSL to *declare* your component's visual appearance based on variants like `state`, `size`, etc.
14
- * **Imperative Power:** A rich runtime API to dynamically and imperatively `add`, `remove`, or `toggle` classes, perfect for server-side updates via Hotwire/Turbo.
13
+ * **Declarative First:** A beautiful DSL to declaratively describe your component's appearance based on variants like state, size, etc.
14
+ * **Imperative Power:** A rich runtime API to dynamically add, remove, or toggle classes, perfect for server-side updates via Hotwire/Turbo.
15
15
  * **Framework-Agnostic:** Written in pure Ruby with zero dependencies, ready to be used in any project.
16
16
 
17
17
  ## Installation
18
18
 
19
- Add this line to your application's Gemfile:
19
+ Add the gem to your Gemfile:
20
20
 
21
21
  ```ruby
22
22
  gem 'tailmix'
23
+ ````
24
+
25
+ Or install it from the command line:
26
+
27
+ ```bash
28
+ bundle add tailmix
23
29
  ```
24
30
 
25
- And then execute: `$ bundle install`
31
+ Next, run the installer to set up the JavaScript assets:
32
+
33
+ ```bash
34
+ bin/rails g tailmix:install
35
+ ```
26
36
 
27
- Or install it yourself as: `$ gem install tailmix`
37
+ ## Core Concepts
28
38
 
29
- ## The DSL: A Detailed Breakdown
39
+ You define your component's appearance using a simple `tailmix do ... end` DSL inside your class.
30
40
 
31
- You define a component's style "schema" using the `tailmix` DSL within your class.
41
+ - `element :name, "base classes"`: Defines a logical part of your component (e.g., `:wrapper`, `:panel`, `:icon`).
42
+ - `dimension :name, default: :value`: Defines a variant or "dimension" (e.g., `size` or `color`).
43
+ - `option :value, "classes"`: Defines the classes for a specific variant option.
44
+ - `action :name, method: :add | :toggle | :remove`: Defines a named set of UI mutations that can be applied on the server (`.apply!`) or passed to the client (`action_payload`).
45
+ - `stimulus`: A powerful nested DSL for declaratively describing Stimulus `data-*` attributes.
32
46
 
33
- * `element :name, "base classes" do ... end`: Defines a "part" of your component. Every component has at least one element.
34
- * `dimension_name do ... end` (e.g., `state do`, `size do`): Defines a variant "dimension". The name can be anything you choose.
35
- * `option :name, "classes", default: true`: Defines a specific option within a dimension and its corresponding CSS classes. One option per dimension can be marked as the default.
47
+ ## Usage Example
36
48
 
37
- ### Full Example of the DSL
49
+ Let's build a complex `ModalComponent` from scratch.
50
+ #### 1. Define the Component (`app/components/modal_component.rb`)
51
+
52
+ This is a plain Ruby class that contains all the style and behavior logic.
38
53
 
39
54
  ```ruby
40
- class MyButtonComponent
55
+ class ModalComponent
41
56
  include Tailmix
57
+ attr_reader :ui
42
58
 
43
59
  tailmix do
44
- # Define the main element and its base classes
45
- element :button, "inline-flex items-center font-medium rounded-md" do
46
- # Define the 'size' dimension
47
- size do
48
- option :sm, "px-2.5 py-1.5 text-xs", default: true
49
- option :md, "px-3 py-2 text-sm"
50
- option :lg, "px-4 py-2 text-base"
60
+ element :base, "fixed inset-0 z-50 flex items-center justify-center" do
61
+ dimension :open, default: false do
62
+ option true, "visible opacity-100"
63
+ option false, "invisible opacity-0"
51
64
  end
65
+ stimulus.controller("modal")
66
+ end
52
67
 
53
- # Define the 'intent' dimension
54
- intent do
55
- option :primary, "bg-blue-600 text-white hover:bg-blue-700", default: true
56
- option :secondary, "bg-gray-200 text-gray-800 hover:bg-gray-300"
57
- option :danger, "bg-red-600 text-white hover:bg-red-700"
68
+ element :overlay, "fixed inset-0 bg-black/50 transition-opacity" do
69
+ stimulus.context("modal").action(click: :close)
70
+ end
71
+
72
+ element :panel, "relative bg-white rounded-lg shadow-xl" do
73
+ dimension :size, default: :md do
74
+ option :sm, "w-full max-w-sm p-4"
75
+ option :md, "w-full max-w-md p-6"
58
76
  end
59
77
  end
60
78
 
61
- # Define another element, like an icon
62
- element :icon, "inline-block" do
63
- size do
64
- option :sm, "h-4 w-4"
65
- option :md, "h-5 w-5", default: true
66
- option :lg, "h-6 w-6"
79
+ element :close_button, "absolute top-2 right-2 p-1 text-gray-400" do
80
+ stimulus.context("modal").action(click: :close)
81
+ end
82
+
83
+ element :confirm_button, "px-4 py-2 bg-blue-600 text-white rounded-md" do
84
+ stimulus.controller("form-submission")
85
+ .action(:click, :show_pending_state)
86
+ .action_payload(:show_pending_state, as: :pending_data)
87
+ end
88
+
89
+ action :show_pending_state, method: :add do
90
+ element :confirm_button do
91
+ classes "is-loading"
92
+ data pending: true
67
93
  end
68
94
  end
69
95
  end
70
- # ...
96
+
97
+ def initialize(open: false, size: :md)
98
+ @ui = tailmix(open: open, size: size)
99
+ end
71
100
  end
72
101
  ```
73
102
 
74
- ## Usage
103
+ #### 2. Use in a View (Arbre, ERB, etc.)
104
+
105
+ Thanks to Tailmix's design, you can pass `ui` objects directly to many rendering helpers.
75
106
 
76
- ### 1. Initialization
107
+ ##### Arbre
77
108
 
78
- Inside your component, call the `tailmix` helper to create an interactive style manager. You can pass initial variants to it.
109
+ The API is seamless. The `ui` object behaves like an attributes hash automatically.
110
+
111
+ Ruby
79
112
 
80
113
  ```ruby
81
- class MyButtonComponent
82
- # ... (tailmix DSL from above)
114
+ # app/views/components/my_modal.arb
115
+ # 1. Instantiate the component with the desired variants
116
+ modal = ModalComponent.new(open: true, size: :sm)
83
117
 
84
- attr_reader :classes
118
+ # 2. Render by passing the ui objects directly to Arbre's tag helpers
119
+ div modal.ui.base do
120
+ div modal.ui.overlay
85
121
 
86
- def initialize(intent: :primary, size: :md)
87
- # The `tailmix` helper creates and returns the manager object
88
- @classes = tailmix(intent: intent, size: size)
89
- end
90
-
91
- def render
92
- # The manager's methods map to your elements.
93
- # Ruby's `to_s` is called implicitly when rendering.
94
- "<button class='#{@classes.button}'>
95
- <span class='#{@classes.icon}'></span>
96
- Click me
97
- </button>"
122
+ div modal.ui.panel do
123
+ # ... your content ...
124
+ button modal.ui.confirm_button, "Confirm"
98
125
  end
99
126
  end
127
+ ```
128
+
129
+ ##### ERB / Rails Tag Helpers
130
+
131
+ In ERB, the idiomatic way to pass a hash-like object as attributes is with the double splat (`**`) operator.
100
132
 
101
- # Renders a medium primary button by default
102
- button = MyButtonComponent.new
103
- button.render
133
+ Фрагмент кода
104
134
 
105
- # Renders a small danger button
106
- button = MyButtonComponent.new(intent: :danger, size: :sm)
107
- button.render
108
135
  ```
136
+ <%# app/views/pages/home.html.erb %>
137
+ <% modal = ModalComponent.new(open: true, size: :sm) %>
109
138
 
110
- ### 2. Dynamic & Imperative Usage
139
+ <%= tag.div **modal.ui.base do %>
140
+ <%= tag.div **modal.ui.overlay %>
111
141
 
112
- This is where `tailmix` truly shines. The `@classes` object is a live manager that you can modify. This is perfect for server-side re-rendering with Hotwire/Turbo.
142
+ <%= tag.div **modal.ui.panel do %>
143
+ <%# ... your content ... %>
144
+ <%= tag.button "Confirm", **modal.ui.confirm_button %>
145
+ <% end %>
146
+ <% end %>
147
+ ```
113
148
 
114
- ```ruby
115
- class MyButtonComponent
116
- # ...
149
+ #### 3. Bring it to Life with Stimulus
150
+
151
+ The `action_payload` helper makes it easy to connect server-side definitions with client-side behavior.
152
+
153
+ JavaScript
154
+
155
+ ```js
156
+ // app/javascript/controllers/form_submission_controller.js
157
+ import { Controller } from "@hotwired/stimulus"
158
+ import Tailmix from "tailmix"
159
+
160
+ export default class extends Controller {
161
+ static values = { pendingData: Object }
117
162
 
118
- # A method that might be called during a Turbo Stream update
119
- def set_loading_state!
120
- # The `combine` method updates the declarative state
121
- @classes.combine(intent: :secondary)
163
+ showPendingState(event) {
164
+ event.preventDefault()
122
165
 
123
- # The imperative API allows for fine-grained control
124
- @classes.button.add("cursor-wait opacity-75")
125
- @classes.icon.add("animate-spin")
126
- end
166
+ // Instantly apply UI changes from the payload
167
+ Tailmix.run({
168
+ config: this.pendingDataValue,
169
+ controller: this
170
+ });
171
+
172
+ // ... then submit the form or send an AJAX request
173
+ }
174
+ }
175
+ ```
127
176
 
128
- def remove_loading_state!
129
- @classes.combine(intent: :primary) # Revert to original intent
130
- @classes.button.remove("cursor-wait opacity-75")
131
- @classes.icon.remove("animate-spin")
132
- end
133
- end
177
+ ## Developer Tools
178
+
179
+ Tailmix comes with built-in introspection tools to improve your development experience. Access them via the `.dev` namespace on your component class.
134
180
 
135
- button = MyButtonComponent.new(intent: :primary)
136
- button.set_loading_state!
137
- button.render # Renders the button in a loading state
181
+ #### Component Documentation
138
182
 
139
- button.remove_loading_state!
140
- button.render # Renders the button back in its primary state
183
+ Get a cheat sheet of all available `dimensions` and `actions` right in your console.
184
+
185
+ ```ruby
186
+ puts ModalComponent.dev.docs
141
187
  ```
142
188
 
143
- ### 3. The Bridge to JavaScript (Stimulus)
189
+ #### Stimulus Controller Generator
190
+
191
+ Tailmix can analyze your component and scaffold a perfect boilerplate Stimulus controller with all targets, values, and action methods.
192
+
193
+ ```ruby
194
+ puts ModalComponent.dev.stimulus.scaffold("modal")
195
+ ```
144
196
 
145
- While `tailmix` is a server-side library, it enables clean integration with JavaScript controllers like Stimulus by providing the "source of truth" for classes. You can create a helper to export variants to `data-` attributes, keeping your JS free of hardcoded style strings.
197
+ ## Configuration
198
+
199
+ You can configure Tailmix by creating an initializer:
200
+
201
+ ```ruby
202
+ # config/initializers/tailmix.rb
203
+ Tailmix.configure do |config|
204
+ # The attribute used by the universal JS selector.
205
+ config.element_selector_attribute = "data-tm-el"
206
+ end
207
+ ```
146
208
 
147
209
  ## Contributing
148
210
 
@@ -0,0 +1,12 @@
1
+ const SELECTOR_ATTRIBUTE = "data-tm-el";
2
+
3
+ /**
4
+ * find element [data-tm-el="..."]
5
+ * @param {HTMLElement} rootElement
6
+ * @param {string} name
7
+ * @returns {HTMLElement|null}
8
+ */
9
+ export function findElement(rootElement, name) {
10
+ const selector = `[${SELECTOR_ATTRIBUTE}="${name}"]`;
11
+ return rootElement.querySelector(selector);
12
+ }
@@ -0,0 +1,7 @@
1
+ import { runFromStimulus } from './stimulus_adapter';
2
+
3
+ const Tailmix = {
4
+ run: runFromStimulus
5
+ };
6
+
7
+ export default Tailmix;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * @param {HTMLElement} element
3
+ * @param {object} command - { field: "classes", method: "toggle", payload: "hidden" }.
4
+ */
5
+ export function applyCommand(element, command) {
6
+ const { field, method, payload } = command;
7
+
8
+ if (field === 'classes') {
9
+ payload.split(' ').forEach(klass => {
10
+ if (klass) element.classList[method](klass);
11
+ });
12
+ } else if (field === 'data') {
13
+ for (const key in payload) {
14
+ const attributeName = `data-${key.replace(/_/g, '-')}`;
15
+ const value = payload[key];
16
+
17
+ if (method === 'remove') {
18
+ element.removeAttribute(attributeName);
19
+ } else if (method === 'toggle') {
20
+ element.hasAttribute(attributeName)
21
+ ? element.removeAttribute(attributeName)
22
+ : element.setAttribute(attributeName, value);
23
+ } else { // 'add'
24
+ element.setAttribute(attributeName, value);
25
+ }
26
+ }
27
+ }
28
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * TailmixRunner
3
+ *
4
+ */
5
+ export default class TailmixRunner {
6
+
7
+ }
@@ -0,0 +1,37 @@
1
+ import { findElement as findElementByAttribute } from './finder';
2
+ import { applyCommand } from './mutator';
3
+
4
+ /**
5
+ * @param {object} params
6
+ * @param {object} params.config
7
+ * @param {StimulusController} params.controller
8
+ */
9
+ export function runFromStimulus({ config, controller }) {
10
+ if (!config?.mutations) {
11
+ console.error("Invalid Tailmix config:", config);
12
+ return;
13
+ }
14
+
15
+ for (const elementName in config.mutations) {
16
+ const targetElement = findElement(elementName, controller);
17
+
18
+ if (!targetElement) {
19
+ console.warn(`Tailmix: Element "${elementName}" not found.`);
20
+ continue;
21
+ }
22
+
23
+ const commands = config.mutations[elementName];
24
+
25
+ for (const command of commands) {
26
+ applyCommand(targetElement, command);
27
+ }
28
+ }
29
+ }
30
+
31
+ function findElement(name, controller) {
32
+ const targetName = `${name}Target`;
33
+ if (controller[targetName]) {
34
+ return controller[targetName];
35
+ }
36
+ return findElementByAttribute(controller.element, name);
37
+ }
@@ -0,0 +1,36 @@
1
+ # Arbre view:
2
+
3
+ modal = SuperModalComponent.new(size: :lg, open: true)
4
+
5
+ # modal.lock!
6
+ ui = modal.ui
7
+
8
+ div ui.base do
9
+ div ui.overlay
10
+ div ui.open do
11
+ "open"
12
+ end
13
+
14
+ div ui.panel do
15
+ button ui.close_button do
16
+ span "Close"
17
+ end
18
+
19
+ h3 ui.title do
20
+ "Payment Successful"
21
+ end
22
+
23
+ div ui.body do
24
+ "Your payment has been successfully submitted. We’ve sent you an email with all of the details of your order."
25
+ end
26
+
27
+ div ui.footer do
28
+ button ui.confirm_button do
29
+ span "Confirm Purchase"
30
+ div ui.spinner do
31
+ span "Loading..."
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,180 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../lib/tailmix"
4
+
5
+ class ModalComponent
6
+ include Tailmix
7
+ attr_reader :ui
8
+
9
+ tailmix do
10
+ element :base, "fixed inset-0 z-50 flex items-center justify-center" do
11
+ dimension :open, default: true do
12
+ option true, "visible opacity-100"
13
+ option false, "invisible opacity-0"
14
+ end
15
+ stimulus.controller("modal").action_payload(:toggle, as: :toggle_data)
16
+ end
17
+
18
+ element :overlay, "fixed inset-0 bg-black/50 transition-opacity" do
19
+ stimulus.context("modal").action(:click, :close)
20
+ end
21
+
22
+ element :panel, "relative bg-white rounded-lg shadow-xl transition-transform transform" do
23
+ dimension :size, default: :md do
24
+ option :sm, "w-full max-w-sm p-4"
25
+ option :md, "w-full max-w-md p-6"
26
+ option :lg, "w-full max-w-lg p-8"
27
+ end
28
+ stimulus.context("modal").target("panel")
29
+ end
30
+
31
+ element :title, "text-lg font-semibold text-gray-900"
32
+ element :body, "mt-2 text-sm text-gray-600"
33
+ element :close_button, "absolute top-2 right-2 p-1 text-gray-400 rounded-full hover:bg-gray-100 hover:text-gray-600" do
34
+ stimulus.context("modal").action(:click, :close)
35
+ end
36
+
37
+ element :footer, "mt-4 pt-4 border-t flex justify-end"
38
+ element :confirm_button, "relative inline-flex items-center px-4 py-2 bg-blue-600 text-white font-semibold rounded-md hover:bg-blue-700" do
39
+ stimulus.controller("form-submission")
40
+ .action(:click, :submit)
41
+ .action_payload(:enter_pending_state, as: :pending_data)
42
+ end
43
+
44
+ element :spinner, "absolute inset-0 flex items-center justify-center hidden"
45
+
46
+ action :toggle, method: :toggle do
47
+ element :overlay do
48
+ classes "hidden"
49
+ end
50
+ element :panel do
51
+ classes "hidden"
52
+ end
53
+ end
54
+
55
+ action :lock, method: :add do
56
+ element :close_button do
57
+ classes "hidden"
58
+ end
59
+ element :panel do
60
+ data locked: true, reason: "processing"
61
+ end
62
+ end
63
+
64
+ action :enter_pending_state, method: :add do
65
+ element :confirm_button do
66
+ classes "opacity-75 cursor-not-allowed"
67
+ end
68
+ element :spinner do
69
+ classes "flex"
70
+ end
71
+ end
72
+ end
73
+
74
+ def initialize(size: :md, open: false)
75
+ @ui = tailmix(size: size, open: open)
76
+ end
77
+
78
+ def lock!
79
+ @ui.action(:lock).apply!
80
+ end
81
+ end
82
+
83
+ puts "-" * 100
84
+ puts ModalComponent.dev.docs
85
+ puts ""
86
+ puts "Scaffolds:"
87
+ puts ""
88
+ puts ModalComponent.dev.stimulus.scaffold
89
+ puts ""
90
+
91
+ # >>>
92
+ #
93
+ # == Tailmix Docs for ModalComponent ==
94
+ # Signature: `initialize(open: true, size: :md)`
95
+ #
96
+ # Dimensions:
97
+ # - open (default: true)
98
+ # - true: "visible opacity-100"
99
+ # - false: "invisible opacity-0"
100
+ # - size (default: :md)
101
+ # - :sm: "w-full max-w-sm p-4"
102
+ # - :md: "w-full max-w-md p-6"
103
+ # - :lg: "w-full max-w-lg p-8"
104
+ #
105
+ # Actions:
106
+ # - :toggle
107
+ # - :lock
108
+ # - :enter_pending_state
109
+ #
110
+ # Stimulus:
111
+ # - on `modal` controller:
112
+ # - Targets: panel
113
+ # - Actions: close
114
+ #
115
+ # Stimulus:
116
+ # - on `form-submission` controller:
117
+ # - Actions: submit
118
+ #
119
+ # Scaffolds:
120
+ #
121
+ # // Generated by Tailmix for the "modal" controller
122
+ # // Path: app/javascript/controllers/modal_controller.js
123
+ # import { Controller } from "@hotwired/stimulus"
124
+ # import Tailmix from "tailmix"
125
+ #
126
+ # export default class extends Controller {
127
+ # static targets = ['panel']
128
+ # static values = { toggleData: Object }
129
+ #
130
+ # connect() {
131
+ # console.log("modal controller connected to", this.element);
132
+ # }
133
+ # toggle(event) {
134
+ # if (event) event.preventDefault();
135
+ # Tailmix.run({ config: this.toggleDataValue, controller: this });
136
+ # }
137
+ #
138
+ # close() {
139
+ # console.log('modal#close fired');
140
+ # }
141
+ # }
142
+ # ------------------------------------------------------------
143
+ #
144
+ # // Generated by Tailmix for the "form-submission" controller
145
+ # // Path: app/javascript/controllers/form-submission_controller.js
146
+ # import { Controller } from "@hotwired/stimulus"
147
+ # import Tailmix from "tailmix"
148
+ #
149
+ # export default class extends Controller {
150
+ # static targets = []
151
+ # static values = { pendingData: Object }
152
+ #
153
+ # connect() {
154
+ # console.log("form-submission controller connected to", this.element);
155
+ # }
156
+ # enterPendingState(event) {
157
+ # if (event) event.preventDefault();
158
+ # Tailmix.run({ config: this.pendingDataValue, controller: this });
159
+ # }
160
+ #
161
+ # submit() {
162
+ # console.log('form-submission#submit fired');
163
+ # }
164
+ # }
165
+ # ------------------------------------------------------------
166
+
167
+
168
+
169
+
170
+ # modal = ModalComponent.new(size: :lg, open: true)
171
+ # modal.lock!
172
+ # ui = modal.ui
173
+
174
+ # puts "Definition:"
175
+ # pp ModalComponent.tailmix_definition
176
+ # pp ModalComponent.tailmix_definition.to_h
177
+ # ui.overlay.each do |key, value|
178
+ # puts "#{key} :-> #{value.inspect.to_s[0, 75]}..."
179
+ # end
180
+ # ui.action(:lock).apply!
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+
5
+ module Tailmix
6
+ module Generators
7
+ class InstallGenerator < Rails::Generators::Base
8
+ def add_javascript
9
+ say "Pinning Tailmix JavaScript", :green
10
+ append_to_file "config/importmap.rb", <<~RUBY
11
+ pin "tailmix", to: "tailmix/index.js"
12
+ RUBY
13
+
14
+ say "Adding Tailmix to asset manifest", :green
15
+ append_to_file "app/assets/config/manifest.js", "\n//= link tailmix/index.js.js\n"
16
+ end
17
+ end
18
+ end
19
+ end