tailmix 0.4.7 → 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.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +8 -0
  3. data/README.md +46 -205
  4. data/app/javascript/tailmix/runtime/action_dispatcher.js +132 -0
  5. data/app/javascript/tailmix/runtime/component.js +130 -0
  6. data/app/javascript/tailmix/runtime/index.js +130 -0
  7. data/app/javascript/tailmix/runtime/plugins.js +35 -0
  8. data/app/javascript/tailmix/runtime/updater.js +140 -0
  9. data/docs/01_getting_started.md +0 -81
  10. data/examples/_modal_component.arb +17 -25
  11. data/examples/modal_component.rb +31 -155
  12. data/lib/tailmix/definition/builders/action_builder.rb +39 -0
  13. data/lib/tailmix/definition/{contexts → builders}/actions/element_builder.rb +1 -1
  14. data/lib/tailmix/definition/{contexts → builders}/attribute_builder.rb +1 -1
  15. data/lib/tailmix/definition/builders/component_builder.rb +81 -0
  16. data/lib/tailmix/definition/{contexts → builders}/dimension_builder.rb +2 -2
  17. data/lib/tailmix/definition/builders/element_builder.rb +83 -0
  18. data/lib/tailmix/definition/builders/reactor_builder.rb +43 -0
  19. data/lib/tailmix/definition/builders/rule_builder.rb +58 -0
  20. data/lib/tailmix/definition/builders/state_builder.rb +21 -0
  21. data/lib/tailmix/definition/{contexts → builders}/variant_builder.rb +17 -2
  22. data/lib/tailmix/definition/context_builder.rb +17 -12
  23. data/lib/tailmix/definition/payload_proxy.rb +16 -0
  24. data/lib/tailmix/definition/result.rb +17 -19
  25. data/lib/tailmix/dev/docs.rb +3 -9
  26. data/lib/tailmix/dev/tools.rb +0 -5
  27. data/lib/tailmix/dsl.rb +3 -5
  28. data/lib/tailmix/engine.rb +11 -1
  29. data/lib/tailmix/html/attributes.rb +22 -12
  30. data/lib/tailmix/middleware/registry_cleaner.rb +17 -0
  31. data/lib/tailmix/registry.rb +37 -0
  32. data/lib/tailmix/runtime/action_proxy.rb +29 -0
  33. data/lib/tailmix/runtime/attribute_builder.rb +102 -0
  34. data/lib/tailmix/runtime/attribute_cache.rb +23 -0
  35. data/lib/tailmix/runtime/context.rb +51 -62
  36. data/lib/tailmix/runtime/facade_builder.rb +8 -5
  37. data/lib/tailmix/runtime/state.rb +36 -0
  38. data/lib/tailmix/runtime/state_proxy.rb +34 -0
  39. data/lib/tailmix/runtime.rb +0 -1
  40. data/lib/tailmix/version.rb +1 -1
  41. data/lib/tailmix/view_helpers.rb +49 -0
  42. data/lib/tailmix.rb +4 -2
  43. metadata +26 -20
  44. data/app/javascript/tailmix/finder.js +0 -15
  45. data/app/javascript/tailmix/index.js +0 -7
  46. data/app/javascript/tailmix/mutator.js +0 -28
  47. data/app/javascript/tailmix/runner.js +0 -7
  48. data/app/javascript/tailmix/stimulus_adapter.js +0 -37
  49. data/docs/02_dsl_reference.md +0 -266
  50. data/docs/03_advanced_usage.md +0 -88
  51. data/docs/04_client_side_bridge.md +0 -119
  52. data/docs/05_cookbook.md +0 -249
  53. data/lib/tailmix/definition/contexts/action_builder.rb +0 -31
  54. data/lib/tailmix/definition/contexts/element_builder.rb +0 -55
  55. data/lib/tailmix/definition/contexts/stimulus_builder.rb +0 -101
  56. data/lib/tailmix/dev/stimulus_generator.rb +0 -124
  57. data/lib/tailmix/runtime/stimulus/compiler.rb +0 -59
@@ -0,0 +1,130 @@
1
+ import {Component} from './component';
2
+ import {PluginManager} from './plugins';
3
+
4
+ /**
5
+ * The Tailmix global object that manages the lifecycle of components and plugins.
6
+ * It provides methods for starting the application, hydration of components,
7
+ */
8
+ const Tailmix = {
9
+ _namedInstances: {},
10
+ _components: new Map(),
11
+ _definitions: {},
12
+ _pluginManager: new PluginManager(),
13
+ _observer: null,
14
+
15
+ /**
16
+ * Starts the Tailmix application by loading component definitions and initializing components.
17
+ */
18
+ start() {
19
+ this.loadDefinitions();
20
+ this._namedInstances = {};
21
+ this._components.clear();
22
+
23
+ this.hydrate(document.body);
24
+ this.observeChanges();
25
+ },
26
+
27
+ /**
28
+ * Hydrate components within the specified root element.
29
+ * @param rootElement
30
+ */
31
+ hydrate(rootElement) {
32
+ const componentElements = rootElement.querySelectorAll('[data-tailmix-component]');
33
+ componentElements.forEach(element => {
34
+ if (this._components.has(element)) return;
35
+
36
+ const componentName = element.dataset.tailmixComponent;
37
+ const definition = this._definitions[componentName];
38
+ if (!definition) { /* ... */
39
+ return;
40
+ }
41
+
42
+ const component = new Component(element, definition, this._pluginManager);
43
+ this._components.set(element, component);
44
+
45
+ const componentId = element.dataset.tailmixId;
46
+ if (componentId) {
47
+ this._namedInstances[componentId] = component;
48
+ }
49
+ });
50
+
51
+ // External trigger binding
52
+ const triggerElements = rootElement.querySelectorAll('[data-tailmix-trigger-for]');
53
+ triggerElements.forEach(element => {
54
+ const targetId = element.dataset.tailmixTriggerFor;
55
+ const targetComponent = this._namedInstances[targetId];
56
+ if (targetComponent) {
57
+ targetComponent.dispatcher.bindAction(element);
58
+ }
59
+ });
60
+ },
61
+
62
+ /**
63
+ * Loads and parses the component definitions from a script tag with the attribute `data-tailmix-definitions`.
64
+ * If a valid JSON content is found, it assigns it to the `definitions` variable.
65
+ * Logs an error message to the console in case of parsing failure.
66
+ *
67
+ * @return {void} Does not return a value.
68
+ */
69
+ loadDefinitions() {
70
+ const definitionsTag = document.querySelector('script[data-tailmix-definitions]');
71
+ if (definitionsTag) {
72
+ try {
73
+ this._definitions = JSON.parse(definitionsTag.textContent);
74
+ } catch (e) {
75
+ console.error("Tailmix: Failed to parse component definitions.", e);
76
+ }
77
+ }
78
+ },
79
+
80
+ /**
81
+ * Retrieves a component associated with the nearest ancestor element
82
+ * that has the `data-tailmix-component` attribute.
83
+ *
84
+ * @param {Element} element - The DOM element from which the component search begins.
85
+ * @return {*} The component associated with the identified ancestor element, or `undefined` if no component is found.
86
+ */
87
+ getComponent(element) {
88
+ const root = element.closest('[data-tailmix-component]');
89
+ return root ? this._components.get(root) : undefined;
90
+ },
91
+
92
+ registerPlugin(plugin) {
93
+ this._pluginManager.register(plugin);
94
+ },
95
+
96
+ /**
97
+ * Observes changes in the DOM and rehydrates components when they are added or modified.
98
+ */
99
+ observeChanges() {
100
+ if (this._observer) this._observer.disconnect();
101
+
102
+ this._observer = new MutationObserver(mutations => {
103
+ for (const mutation of mutations) {
104
+ if (mutation.type === 'childList') {
105
+ mutation.addedNodes.forEach(node => {
106
+ if (node.nodeType === Node.ELEMENT_NODE) {
107
+ // If the component itself was added
108
+ if (node.matches('[data-tailmix-component]')) {
109
+ this.hydrate(node);
110
+ }
111
+ // If a parent was added, which may contain components
112
+ this.hydrate(node);
113
+ }
114
+ });
115
+ }
116
+ }
117
+ });
118
+
119
+ this._observer.observe(document.body, {childList: true, subtree: true});
120
+ }
121
+ };
122
+
123
+ // Initialize Tailmix on page load
124
+ document.addEventListener("turbo:load", () => {
125
+ console.log("Tailmix starting...");
126
+ Tailmix.start();
127
+ });
128
+
129
+
130
+ export default Tailmix;
@@ -0,0 +1,35 @@
1
+ class PluginManager {
2
+ constructor() {
3
+ this.plugins = [];
4
+ }
5
+
6
+ /**
7
+ * Registers a plugin with the plugin manager.
8
+ * @param plugin
9
+ */
10
+ register(plugin) {
11
+ if (!plugin.name) {
12
+ console.error("Tailmix Plugin Error: a plugin must have a name.", plugin);
13
+ return;
14
+ }
15
+ this.plugins.push(plugin);
16
+ }
17
+
18
+ /**
19
+ * Connects a component to all registered plugins.
20
+ * @param component
21
+ */
22
+ connect(component) {
23
+ this.plugins.forEach(plugin => {
24
+ if (typeof plugin.connect === 'function') {
25
+ const pluginConfig = component.definition.plugins?.[plugin.name];
26
+ if (pluginConfig) {
27
+ plugin.connect(component.api, pluginConfig);
28
+ }
29
+ }
30
+ });
31
+ }
32
+ }
33
+
34
+ export {PluginManager};
35
+
@@ -0,0 +1,140 @@
1
+ export class Updater {
2
+ constructor(component) {
3
+ this.component = component;
4
+ this.definition = component.definition;
5
+ }
6
+
7
+ run(newState, oldState) {
8
+ for (const elementName in this.definition.elements) {
9
+ const elementNode = this.component.elements[elementName];
10
+ const elementDef = this.definition.elements[elementName];
11
+
12
+ if (!elementNode || !elementDef) continue;
13
+
14
+ this.updateElement(elementNode, elementDef, newState, oldState);
15
+ }
16
+ }
17
+
18
+ updateElement(elementNode, elementDef, newState, oldState) {
19
+ this.updateClasses(elementNode, elementDef, newState);
20
+ this.updateAttributes(elementNode, elementDef, newState);
21
+ this.updateContent(elementNode, elementDef, newState);
22
+ }
23
+
24
+ updateClasses(elementNode, elementDef, newState) {
25
+ const targetClasses = this.calculateTargetClasses(elementDef, newState);
26
+ const currentClasses = new Set(elementNode.classList);
27
+
28
+ // We compare current classes with target classes and apply the difference.
29
+ targetClasses.forEach(cls => {
30
+ if (!currentClasses.has(cls)) {
31
+ elementNode.classList.add(cls);
32
+ }
33
+ });
34
+
35
+ currentClasses.forEach(cls => {
36
+ if (!targetClasses.has(cls)) {
37
+ // We avoid removing base classes that were originally in HTML.
38
+ // (This is a simple heuristic; it can be improved if base classes are in the definition)
39
+ const isBaseClass = !this.isVariantClass(elementDef, cls);
40
+ if (!targetClasses.has(cls) && !isBaseClass) {
41
+ elementNode.classList.remove(cls);
42
+ }
43
+ }
44
+ });
45
+ }
46
+
47
+ updateAttributes(elementNode, elementDef, newState) {
48
+ if (elementDef.attribute_bindings) {
49
+ for (const attrName in elementDef.attribute_bindings) {
50
+ if (["text", "html"].includes(attrName)) continue;
51
+
52
+ const stateKey = elementDef.attribute_bindings[attrName];
53
+ const newValue = newState[stateKey];
54
+
55
+ // We update the attribute only if it has changed.
56
+ if (elementNode.getAttribute(attrName) !== newValue) {
57
+ if (newValue === null || newValue === undefined) {
58
+ elementNode.removeAttribute(attrName);
59
+ } else {
60
+ elementNode.setAttribute(attrName, newValue);
61
+ }
62
+ }
63
+ }
64
+ }
65
+ }
66
+
67
+ updateContent(elementNode, elementDef, newState) {
68
+ const bindings = elementDef.attribute_bindings;
69
+ if (!bindings) return;
70
+
71
+ // Обработка `bind :text`
72
+ const textStateKey = bindings.text;
73
+ if (textStateKey !== undefined) {
74
+ const newText = newState[textStateKey] || '';
75
+ if (elementNode.textContent !== newText) {
76
+ elementNode.textContent = newText;
77
+ }
78
+ }
79
+
80
+ // Обработка `bind :html`
81
+ const htmlStateKey = bindings.html;
82
+ if (htmlStateKey !== undefined) {
83
+ const newHtml = newState[htmlStateKey] || '';
84
+ if (elementNode.innerHTML !== newHtml) {
85
+ elementNode.innerHTML = newHtml;
86
+ }
87
+ }
88
+ }
89
+
90
+ calculateTargetClasses(elementDef, state) {
91
+ const classes = new Set();
92
+
93
+ // 1. We add base classes (if any are in the definition).
94
+ // (We skip this for now, as they are already in the HTML)
95
+
96
+ // 2. We apply classes from active variants (dimensions).
97
+ if (elementDef.dimensions) {
98
+ for (const dimName in elementDef.dimensions) {
99
+ const dimDef = elementDef.dimensions[dimName];
100
+ const stateValue = state[dimName] !== undefined ? state[dimName] : dimDef.default;
101
+
102
+ const variantDef = dimDef.variants?.[stateValue];
103
+ if (variantDef?.classes) {
104
+ variantDef.classes.forEach(cls => classes.add(cls));
105
+ }
106
+ }
107
+ }
108
+
109
+ // 3. We apply classes from active compound variants.
110
+ if (elementDef.compound_variants) {
111
+ elementDef.compound_variants.forEach(cv => {
112
+ const conditions = cv.on;
113
+ const modifications = cv.modifications;
114
+
115
+ const isMatch = Object.entries(conditions).every(([key, value]) => {
116
+ return state[key] === value;
117
+ });
118
+
119
+ if (isMatch && modifications.classes) {
120
+ modifications.classes.forEach(cls => classes.add(cls));
121
+ }
122
+ });
123
+ }
124
+
125
+ return classes;
126
+ }
127
+
128
+ isVariantClass(elementDef, className) {
129
+ if (elementDef.dimensions) {
130
+ for (const dimName in elementDef.dimensions) {
131
+ const dim = elementDef.dimensions[dimName];
132
+ for (const variantName in dim.variants) {
133
+ if (dim.variants[variantName].classes?.includes(className)) return true;
134
+ }
135
+ }
136
+ }
137
+ // ... a check can also be added for compound_variants
138
+ return false;
139
+ }
140
+ }
@@ -34,87 +34,6 @@ This command will add tailmix to your importmap.rb and ensure its JavaScript is
34
34
  Let's create a simple BadgeComponent to see Tailmix in action.
35
35
 
36
36
  #### 1. Define the Component Class
37
-
38
- Create a new file in `app/components/badge_component.rb`:
39
-
40
- ```ruby
41
- # app/components/badge_component.rb
42
- class BadgeComponent
43
- include Tailmix
44
- attr_reader :ui, :text
45
-
46
- def initialize(text, color: :gray)
47
- @ui = tailmix(color: color)
48
- @text = text
49
- end
50
-
51
- tailmix do
52
- element :badge, "inline-flex items-center px-2.5 py-0.5 text-xs font-medium rounded-full" do
53
- dimension :color, default: :gray do
54
- variant :gray, "bg-gray-100 text-gray-600"
55
- variant :success, "bg-green-100 text-green-700"
56
- variant :danger, "bg-red-100 text-red-700"
57
- end
58
- end
59
- end
60
- end
61
- ```
62
-
63
- #### 2. Use it in a View
64
-
65
- Now, you can use this component in any ERB view:
66
-
67
- ```html
68
- <%# Create two instances of our badge with different variants %>
69
- <% success_badge = BadgeComponent.new("Active", color: :success) %>
70
- <% danger_badge = BadgeComponent.new("Inactive", color: :danger) %>
71
-
72
- <span <%= tag.attributes **success_badge.ui.badge %>>
73
- <%= success_badge.text %>
74
- </span>
75
-
76
- <span <%= tag.attributes **danger_badge.ui.badge %>>
77
- <%= danger_badge.text %>
78
- </span>
79
- ```
80
-
81
- #### View Usage .arb (Ruby Arbre)
82
-
83
- ```ruby
84
- # app/views/components/_badge.arb
85
- success_badge = BadgeComponent.new("Active", color: :success)
86
- danger_badge = BadgeComponent.new("Inactive", color: :danger)
87
-
88
- span success_badge.ui.badge do
89
- success_badge.text
90
- end
91
-
92
- span danger_badge.ui.badge do
93
- danger_badge.text
94
- end
95
- ```
96
-
97
- #### 3. The Resulting HTML
98
-
99
- This will produce the following clean and semantic HTML:
100
-
101
- ```html
102
- <span class="inline-flex ... bg-green-100 text-green-700" data-tailmix-badge="color:success">
103
- Active
104
- </span>
105
-
106
- <span class="inline-flex ... bg-red-100 text-red-700" data-tailmix-badge="color:danger">
107
- Inactive
108
- </span>
109
- ```
110
-
111
- Notice the `data-tailmix-badge` attribute, which serves as both a stable selector and a state indicator for your component.
112
-
113
- ## Next Steps
114
-
115
- - You've successfully created your first component! To learn more about the power of Tailmix, check out these documents:
116
37
 
117
- - DSL Reference: For a deep dive into every available DSL method.
118
38
 
119
- - Cookbook: For practical, real-world examples of common UI components.
120
39
 
@@ -1,36 +1,28 @@
1
1
  # Arbre view:
2
2
 
3
- modal = SuperModalComponent.new(size: :lg, open: true)
3
+ modal_component = ModalComponent.new(open: false, id: :user_profile_modal)
4
+ ui = modal_component.ui
4
5
 
5
- # modal.lock!
6
- ui = modal.ui
6
+ button "Open Modal Outer", tailmix_trigger_for(:user_profile_modal, :toggle_open)
7
7
 
8
- div ui.base do
9
- div ui.overlay
10
- div ui.open do
11
- "open"
12
- end
8
+ div ui.container.component do
9
+ button "Open Modal", ui.open_button
13
10
 
14
- div ui.panel do
15
- button ui.close_button do
16
- span "Close"
17
- end
11
+ div ui.base do
12
+ div ui.overlay
18
13
 
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..."
14
+ div ui.panel do
15
+ div ui.title do
16
+ h3 "Modal Title"
17
+ button ui.close_button do
18
+ text_node "✖"
32
19
  end
33
20
  end
21
+
22
+ div ui.body do
23
+ para "This is the main content of the modal. It's powered by the new Tailmix Runtime!"
24
+ end
34
25
  end
35
26
  end
36
27
  end
28
+
@@ -8,186 +8,62 @@ class ModalComponent
8
8
  attr_reader :ui
9
9
 
10
10
  tailmix do
11
- element :base, "fixed inset-0 z-50 flex items-center justify-center" do
12
- dimension :open, default: true do
13
- variant true, "visible opacity-100"
14
- variant false, "invisible opacity-0"
15
- end
16
- stimulus.controller("modal").action_payload(:toggle, as: :toggle_data)
17
- end
18
-
19
- element :overlay, "fixed inset-0 bg-black/50 transition-opacity" do
20
- stimulus.context("modal").action(:click, :close)
21
- end
11
+ plugin :auto_focus, on: :open_button, delay: 100
12
+ state :open, default: false, toggle: true
22
13
 
23
- element :panel, "relative bg-white rounded-lg shadow-xl transition-transform transform" do
24
- dimension :size, default: :md do
25
- variant :sm, "w-full max-w-sm p-4" do
26
- classes "dark:text-slate-400", group: :dark_mode
27
- classes "one two"
28
- end
29
- variant :md, "w-full max-w-md p-6"
30
- variant :lg, "w-full max-w-lg p-8"
31
- end
32
- stimulus.context("modal").target("panel")
14
+ element :container do
33
15
  end
34
16
 
35
- element :title, "text-lg font-semibold text-gray-900"
36
- element :body, "mt-2 text-sm text-gray-600"
37
- element :close_button, "absolute top-2 right-2 p-1 text-gray-400 rounded-full hover:bg-gray-100 hover:text-gray-600" do
38
- stimulus.context("modal").action(:click, :close)
17
+ element :open_button do
18
+ # We attach the `click` event to our auto-generated action.
19
+ on :click, :toggle_open
39
20
  end
40
21
 
41
- element :footer, "mt-4 pt-4 border-t flex justify-end"
42
- 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
43
- stimulus.controller("form-submission")
44
- .action(:click, :submit)
45
- .action_payload(:enter_pending_state, as: :pending_data)
22
+ element :base do
23
+ dimension :open do
24
+ variant true, "fixed inset-0 z-50 flex items-center justify-center visible opacity-100 transition-opacity"
25
+ variant false, "invisible opacity-0"
26
+ end
46
27
  end
47
28
 
48
- element :spinner, "absolute inset-0 flex items-center justify-center hidden"
49
-
50
- action :toggle, method: :toggle do
51
- element :overlay do
52
- classes "hidden"
53
- end
54
- element :panel do
55
- classes "hidden"
29
+ element :overlay do
30
+ dimension :open do
31
+ variant true, "fixed inset-0 bg-black/50"
32
+ variant false, "hidden"
56
33
  end
34
+ on :click, :toggle_open
57
35
  end
58
36
 
59
- action :lock, method: :add do
60
- element :close_button do
61
- classes "hidden"
62
- end
63
- element :panel do
64
- data locked: true, reason: "processing"
37
+ element :panel, "relative bg-white rounded-lg shadow-xl" do
38
+ dimension :open do
39
+ variant true, "block"
40
+ variant false, "hidden"
65
41
  end
66
42
  end
67
43
 
68
- action :enter_pending_state, method: :add do
69
- element :confirm_button do
70
- classes "opacity-75 cursor-not-allowed"
71
- end
72
- element :spinner do
73
- classes "flex"
74
- end
44
+ element :close_button, "absolute top-2 right-2 p-1 text-gray-500 rounded-full cursor-pointer" do
45
+ on :click, :toggle_open
75
46
  end
76
- end
77
47
 
78
- def initialize(size: :md, open: false)
79
- @ui = tailmix(size: size, open: open)
48
+ element :title, "text-lg font-semibold text-gray-900 p-4 border-b"
49
+ element :body, "p-4 text-gray-900"
80
50
  end
81
51
 
82
- def lock!
83
- @ui.action(:lock).apply!
52
+ def initialize(open: false, id: nil)
53
+ @ui = tailmix(open: open, id: id)
84
54
  end
85
55
  end
86
56
 
87
- puts "-" * 100
88
- # puts ModalComponent.dev.docs
89
- # puts ""
90
- # puts "Scaffolds:"
91
- # puts ""
92
- # puts ModalComponent.dev.stimulus.scaffold
93
- # puts ""
94
-
95
- # >>>
96
- #
97
- # == Tailmix Docs for ModalComponent ==
98
- # Signature: `initialize(open: true, size: :md)`
99
- #
100
- # Dimensions:
101
- # - open (default: true)
102
- # - true:
103
- # - classes : "visible opacity-100"
104
- # - false:
105
- # - classes : "invisible opacity-0"
106
- # - size (default: :md)
107
- # - :sm:
108
- # - classes : "w-full max-w-sm p-4"
109
- # - classes (group: :dark_mode): "dark:text-slate-400"
110
- # - classes : "one two"
111
- # - :md:
112
- # - classes : "w-full max-w-md p-6"
113
- # - :lg:
114
- # - classes : "w-full max-w-lg p-8"
115
- #
116
- # Actions:
117
- # - :toggle
118
- # - :lock
119
- # - :enter_pending_state
120
- #
121
- # Stimulus:
122
- # - on `modal` controller:
123
- # - Targets: panel
124
- # - Actions: close
125
- #
126
- # Stimulus:
127
- # - on `form-submission` controller:
128
- # - Actions: submit
129
- #
130
- # Scaffolds:
131
- #
132
- # // Generated by Tailmix for the "modal" controller
133
- # // Path: app/javascript/controllers/modal_controller.js
134
- # import { Controller } from "@hotwired/stimulus"
135
- # import Tailmix from "tailmix"
136
- #
137
- # export default class extends Controller {
138
- # static targets = ['panel']
139
- # static values = { toggleData: Object }
140
- #
141
- # connect() {
142
- # console.log("modal controller connected to", this.element);
143
- # }
144
- # toggle(event) {
145
- # if (event) event.preventDefault();
146
- # Tailmix.run({ config: this.toggleDataValue, controller: this });
147
- # }
148
- #
149
- # close() {
150
- # console.log('modal#close fired');
151
- # }
152
- # }
153
- # ------------------------------------------------------------
154
- #
155
- # // Generated by Tailmix for the "form-submission" controller
156
- # // Path: 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 targets = []
162
- # static values = { pendingData: Object }
163
- #
164
- # connect() {
165
- # console.log("form-submission controller connected to", this.element);
166
- # }
167
- # enterPendingState(event) {
168
- # if (event) event.preventDefault();
169
- # Tailmix.run({ config: this.pendingDataValue, controller: this });
170
- # }
171
- #
172
- # submit() {
173
- # console.log('form-submission#submit fired');
174
- # }
175
- # }
176
- # ------------------------------------------------------------
177
-
178
-
179
-
180
-
181
- modal = ModalComponent.new(size: :lg, open: true)
182
- # modal.lock!
183
- ui = modal.ui
184
-
185
57
 
58
+ modal = ModalComponent.new(open: false, id: :user_profile_modal)
59
+ ui = modal.ui
186
60
 
187
61
 
188
62
  # puts "Definition:"
189
63
  # puts JSON.pretty_generate(stringify_keys(ModalComponent.tailmix_definition.to_h))
190
- # ui.action(:lock).apply!
64
+ puts "-" * 100
65
+ puts ModalComponent.dev.docs
66
+ puts "-" * 100
191
67
 
192
68
  ModalComponent.dev.elements.each do |element_name|
193
69
  element = ui.send(element_name)