poetry-core 0.0.2
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 +7 -0
- data/CHANGELOG.md +3 -0
- data/DESIGN.md +145 -0
- data/LICENSE.txt +21 -0
- data/README.md +30 -0
- data/THIRD_PARTY_NOTICES.md +507 -0
- data/app/components/poetry/core/box/component.rb +82 -0
- data/app/components/poetry/core/component.rb +390 -0
- data/app/components/poetry/core/concerns/agent_tools.rb +418 -0
- data/app/components/poetry/core/concerns/declared_attributes.rb +267 -0
- data/app/components/poetry/core/concerns/introspection.rb +533 -0
- data/app/components/poetry/core/concerns/options.rb +262 -0
- data/app/components/poetry/core/concerns/parts.rb +148 -0
- data/app/components/poetry/core/concerns/stimulus.rb +363 -0
- data/app/components/poetry/core/concerns/styles.rb +323 -0
- data/app/components/poetry/core/style.rb +150 -0
- data/app/components/poetry/core/wrapper/component.rb +90 -0
- data/app/javascript/poetry/core/accordion_controller.js +165 -0
- data/app/javascript/poetry/core/action_bar_controller.js +109 -0
- data/app/javascript/poetry/core/autocomplete_controller.js +220 -0
- data/app/javascript/poetry/core/calendar_controller.js +406 -0
- data/app/javascript/poetry/core/carousel_controller.js +167 -0
- data/app/javascript/poetry/core/checkbox_group_controller.js +97 -0
- data/app/javascript/poetry/core/checked_controller.js +208 -0
- data/app/javascript/poetry/core/clipboard_text_controller.js +111 -0
- data/app/javascript/poetry/core/combobox_controller.js +1095 -0
- data/app/javascript/poetry/core/command_controller.js +510 -0
- data/app/javascript/poetry/core/context_menu_controller.js +204 -0
- data/app/javascript/poetry/core/date_field_controller.js +584 -0
- data/app/javascript/poetry/core/date_picker_controller.js +111 -0
- data/app/javascript/poetry/core/deferred_controller.js +111 -0
- data/app/javascript/poetry/core/dialog_controller.js +206 -0
- data/app/javascript/poetry/core/dismissable_controller.js +182 -0
- data/app/javascript/poetry/core/drawer_controller.js +365 -0
- data/app/javascript/poetry/core/file_input_controller.js +191 -0
- data/app/javascript/poetry/core/focus_scope_controller.js +162 -0
- data/app/javascript/poetry/core/helpers/announce.js +228 -0
- data/app/javascript/poetry/core/helpers/breakpoint.js +28 -0
- data/app/javascript/poetry/core/helpers/collection.js +18 -0
- data/app/javascript/poetry/core/helpers/direction.js +15 -0
- data/app/javascript/poetry/core/helpers/escape.js +38 -0
- data/app/javascript/poetry/core/helpers/filter_rank.js +104 -0
- data/app/javascript/poetry/core/helpers/focus_guards.js +46 -0
- data/app/javascript/poetry/core/helpers/hotkey.js +43 -0
- data/app/javascript/poetry/core/helpers/id_integrity.js +65 -0
- data/app/javascript/poetry/core/helpers/incomplete_date.js +328 -0
- data/app/javascript/poetry/core/helpers/mask.js +283 -0
- data/app/javascript/poetry/core/helpers/portal.js +217 -0
- data/app/javascript/poetry/core/helpers/presence.js +210 -0
- data/app/javascript/poetry/core/helpers/registration_guard.js +76 -0
- data/app/javascript/poetry/core/helpers/scroll_lock.js +67 -0
- data/app/javascript/poetry/core/helpers/scroller_geometry.js +422 -0
- data/app/javascript/poetry/core/helpers/state.js +80 -0
- data/app/javascript/poetry/core/helpers/tabbable.js +56 -0
- data/app/javascript/poetry/core/helpers/turbo_cache.js +21 -0
- data/app/javascript/poetry/core/helpers/typeahead.js +85 -0
- data/app/javascript/poetry/core/hotkey_controller.js +55 -0
- data/app/javascript/poetry/core/hover_card_controller.js +431 -0
- data/app/javascript/poetry/core/index.js +218 -0
- data/app/javascript/poetry/core/mask_controller.js +500 -0
- data/app/javascript/poetry/core/menu_controller.js +1050 -0
- data/app/javascript/poetry/core/menubar_controller.js +329 -0
- data/app/javascript/poetry/core/message_scroller_controller.js +930 -0
- data/app/javascript/poetry/core/navigation_menu_controller.js +458 -0
- data/app/javascript/poetry/core/number_field_controller.js +404 -0
- data/app/javascript/poetry/core/optimistic_form_controller.js +56 -0
- data/app/javascript/poetry/core/otp_controller.js +219 -0
- data/app/javascript/poetry/core/popover_controller.js +300 -0
- data/app/javascript/poetry/core/popper_controller.js +439 -0
- data/app/javascript/poetry/core/pressed_controller.js +74 -0
- data/app/javascript/poetry/core/questionnaire_controller.js +468 -0
- data/app/javascript/poetry/core/radio_group_controller.js +197 -0
- data/app/javascript/poetry/core/resizable_controller.js +202 -0
- data/app/javascript/poetry/core/roving_focus_controller.js +215 -0
- data/app/javascript/poetry/core/scroll_spy_controller.js +84 -0
- data/app/javascript/poetry/core/search_field_controller.js +95 -0
- data/app/javascript/poetry/core/select_controller.js +924 -0
- data/app/javascript/poetry/core/sensitive_input_controller.js +195 -0
- data/app/javascript/poetry/core/sheet_controller.js +24 -0
- data/app/javascript/poetry/core/sidebar_controller.js +272 -0
- data/app/javascript/poetry/core/slider_controller.js +419 -0
- data/app/javascript/poetry/core/state_controller.js +82 -0
- data/app/javascript/poetry/core/table_selection_controller.js +153 -0
- data/app/javascript/poetry/core/tabs_controller.js +151 -0
- data/app/javascript/poetry/core/tag_group_controller.js +134 -0
- data/app/javascript/poetry/core/toast_controller.js +228 -0
- data/app/javascript/poetry/core/toast_trigger_controller.js +26 -0
- data/app/javascript/poetry/core/toaster_controller.js +197 -0
- data/app/javascript/poetry/core/toggle_group_controller.js +206 -0
- data/app/javascript/poetry/core/tooltip_controller.js +541 -0
- data/app/javascript/poetry/core/tree_controller.js +265 -0
- data/app/javascript/poetry/core/vendor/floating_ui_core.js +1043 -0
- data/app/javascript/poetry/core/vendor/floating_ui_dom.js +794 -0
- data/app/javascript/poetry/core/vendor/floating_ui_utils.js +141 -0
- data/app/javascript/poetry/core/vendor/floating_ui_utils_dom.js +170 -0
- data/app/views/poetry/core/preview.html.erb +13 -0
- data/config/component_registry.yml +20 -0
- data/config/controllers_manifest.json +1528 -0
- data/config/importmap.rb +11 -0
- data/config/state_vocabulary.json +31 -0
- data/lib/active_model/type/list.rb +33 -0
- data/lib/active_model/type/symbol.rb +36 -0
- data/lib/poetry/core/check/stable_identity.rb +104 -0
- data/lib/poetry/core/check.rb +1445 -0
- data/lib/poetry/core/config.rb +286 -0
- data/lib/poetry/core/contrib/wrapped_helper.rb +68 -0
- data/lib/poetry/core/css/bem_merger.rb +45 -0
- data/lib/poetry/core/css/bem_reference.rb +87 -0
- data/lib/poetry/core/css/override_scan.rb +110 -0
- data/lib/poetry/core/css/resolver.rb +161 -0
- data/lib/poetry/core/css/safelist.rb +41 -0
- data/lib/poetry/core/css/tailwind_merger.rb +102 -0
- data/lib/poetry/core/css/template_classes.rb +102 -0
- data/lib/poetry/core/css/theme_coverage.rb +72 -0
- data/lib/poetry/core/css/var_coverage.rb +75 -0
- data/lib/poetry/core/css/verifier.rb +106 -0
- data/lib/poetry/core/design_lint.rb +720 -0
- data/lib/poetry/core/design_md/import.rb +250 -0
- data/lib/poetry/core/design_md.rb +461 -0
- data/lib/poetry/core/engine.rb +71 -0
- data/lib/poetry/core/errors.rb +53 -0
- data/lib/poetry/core/html/attributes.rb +667 -0
- data/lib/poetry/core/icons.rb +157 -0
- data/lib/poetry/core/llms_text.rb +354 -0
- data/lib/poetry/core/page_architectures.rb +304 -0
- data/lib/poetry/core/part_contract.rb +285 -0
- data/lib/poetry/core/preview/abstract.rb +121 -0
- data/lib/poetry/core/preview/base.rb +227 -0
- data/lib/poetry/core/preview/sidecarable.rb +136 -0
- data/lib/poetry/core/preview/template.rb +186 -0
- data/lib/poetry/core/recipe_items.rb +82 -0
- data/lib/poetry/core/registry.rb +363 -0
- data/lib/poetry/core/registry_address.rb +115 -0
- data/lib/poetry/core/registry_client.rb +217 -0
- data/lib/poetry/core/registry_installer.rb +234 -0
- data/lib/poetry/core/registry_items.rb +184 -0
- data/lib/poetry/core/skill_text.rb +288 -0
- data/lib/poetry/core/stable_id.rb +110 -0
- data/lib/poetry/core/stimulus/builder.rb +395 -0
- data/lib/poetry/core/stimulus/declarations.rb +444 -0
- data/lib/poetry/core/stimulus/manifest.rb +68 -0
- data/lib/poetry/core/stimulus/merger.rb +131 -0
- data/lib/poetry/core/stimulus_contract.rb +263 -0
- data/lib/poetry/core/tag_helper.rb +10 -0
- data/lib/poetry/core/template_compile.rb +74 -0
- data/lib/poetry/core/token_import.rb +317 -0
- data/lib/poetry/core/tokens/color.rb +215 -0
- data/lib/poetry/core/tokens/contrast_gate.rb +120 -0
- data/lib/poetry/core/tokens/generator.rb +211 -0
- data/lib/poetry/core/tokens.rb +117 -0
- data/lib/poetry/core/version.rb +9 -0
- data/lib/poetry/core.rb +52 -0
- data/lib/poetry-core.rb +5 -0
- data/tokens/aliases.css +145 -0
- data/tokens/tailwind-theme.css +47 -0
- data/tokens/tokens.css +78 -0
- data/tokens/tokens.dtcg.json +779 -0
- data/vendor/floating-ui/LICENSE +20 -0
- data/vendor/floating-ui/VENDORED_VERSIONS +15 -0
- data/vendor/shadcn-tailwind/LICENSE +21 -0
- data/vendor/shadcn-tailwind/VENDORED_COMMIT +1 -0
- data/vendor/shadcn-tailwind/tailwind.css +629 -0
- data/vendor/tw-animate-css/LICENSE +21 -0
- data/vendor/tw-animate-css/VENDORED_VERSION +1 -0
- data/vendor/tw-animate-css/tw-animate.css +1 -0
- metadata +267 -0
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Poetry
|
|
4
|
+
module Core
|
|
5
|
+
module Stimulus
|
|
6
|
+
# Builder class for constructing Stimulus controller HTML attributes in a Ruby-friendly way.
|
|
7
|
+
#
|
|
8
|
+
# This class provides a clean API for adding Stimulus data attributes to HTML elements
|
|
9
|
+
# without manually constructing attribute strings. It handles:
|
|
10
|
+
# - Controller registration
|
|
11
|
+
# - Values (data passed to controllers)
|
|
12
|
+
# - CSS class references (for Stimulus classes API)
|
|
13
|
+
# - Outlets (connections to other controllers)
|
|
14
|
+
# - Actions (event listeners)
|
|
15
|
+
# - Custom parameters
|
|
16
|
+
#
|
|
17
|
+
# @example Basic usage
|
|
18
|
+
# builder = Poetry::Core::Stimulus::Builder.new("dropdown", html_attributes)
|
|
19
|
+
# builder.register_controller
|
|
20
|
+
# builder.with_value(:open, false)
|
|
21
|
+
# builder.with_action(:toggle, on: :click)
|
|
22
|
+
# # Produces: data-controller="dropdown"
|
|
23
|
+
# # data-dropdown-open-value="false"
|
|
24
|
+
# # data-action="click->dropdown#toggle"
|
|
25
|
+
#
|
|
26
|
+
# @example With initialization options
|
|
27
|
+
# builder = Poetry::Core::Stimulus::Builder.new("menu", html_attributes,
|
|
28
|
+
# values: { visible: true },
|
|
29
|
+
# actions: { show: :mouseenter, hide: :mouseleave },
|
|
30
|
+
# classes: { active: "bg-blue-500" }
|
|
31
|
+
# )
|
|
32
|
+
#
|
|
33
|
+
class Builder
|
|
34
|
+
# Event aliases for common event combinations
|
|
35
|
+
# @example Using hover_in alias
|
|
36
|
+
# with_action(:show, on: :hover_in)
|
|
37
|
+
# # Expands to both mouseenter and focus events
|
|
38
|
+
EVENT_ALIASES = {
|
|
39
|
+
hover_in: %w[mouseenter focus].freeze,
|
|
40
|
+
hover_out: %w[mouseleave blur].freeze
|
|
41
|
+
}.freeze
|
|
42
|
+
|
|
43
|
+
# @return [String] The formatted Stimulus controller identifier
|
|
44
|
+
attr_reader :identifier
|
|
45
|
+
|
|
46
|
+
# @return [Object] The HTML attributes object that will be modified
|
|
47
|
+
attr_reader :html_attributes
|
|
48
|
+
|
|
49
|
+
# Creates a new Stimulus builder instance
|
|
50
|
+
#
|
|
51
|
+
# @param identifier [String, Symbol, Array] The Stimulus controller identifier(s)
|
|
52
|
+
# @param html_attributes [Object] An object that responds to merge_stimulus! methods
|
|
53
|
+
# @param options [Hash] Optional configurations
|
|
54
|
+
# @option options [Hash] :values Key-value pairs to register as Stimulus values
|
|
55
|
+
# @option options [Hash] :classes Key-value pairs to register as Stimulus classes
|
|
56
|
+
# @option options [Hash] :outlets Key-value pairs to register as Stimulus outlets
|
|
57
|
+
# @option options [Hash] :params Key-value pairs to register as custom params
|
|
58
|
+
# @option options [Hash] :actions Method-event pairs to register as Stimulus actions
|
|
59
|
+
#
|
|
60
|
+
# @example Single controller
|
|
61
|
+
# Builder.new("dropdown", html_attributes)
|
|
62
|
+
#
|
|
63
|
+
# @example Multiple controllers (namespaced)
|
|
64
|
+
# Builder.new([:admin, :dropdown], html_attributes)
|
|
65
|
+
#
|
|
66
|
+
# @example With options
|
|
67
|
+
# Builder.new("modal", html_attributes,
|
|
68
|
+
# values: { open: false, size: "large" },
|
|
69
|
+
# actions: { toggle: :click, close: { on: :keydown, at: :window } }
|
|
70
|
+
# )
|
|
71
|
+
def initialize(identifier, html_attributes, options = {})
|
|
72
|
+
@identifier = format_identifier identifier
|
|
73
|
+
# nil for host-app controllers; poetry-namespaced identifiers are
|
|
74
|
+
# validated against the committed controllers manifest.
|
|
75
|
+
@definition = Manifest.definition(@identifier)
|
|
76
|
+
@html_attributes = html_attributes
|
|
77
|
+
register_values options[:values]
|
|
78
|
+
register_classes options[:classes]
|
|
79
|
+
register_outlets options[:outlets]
|
|
80
|
+
register_params options[:params]
|
|
81
|
+
register_actions options[:actions]
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Registers this controller in the data-controller attribute
|
|
85
|
+
#
|
|
86
|
+
# @return [void]
|
|
87
|
+
#
|
|
88
|
+
# @example
|
|
89
|
+
# builder.register_controller
|
|
90
|
+
# # Adds: data-controller="dropdown"
|
|
91
|
+
def register_controller
|
|
92
|
+
html_attributes.merge_stimulus_controllers!(identifier)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Returns the attribute name for a Stimulus target
|
|
96
|
+
#
|
|
97
|
+
# @return [Symbol] The target attribute name (e.g., :dropdown_target)
|
|
98
|
+
#
|
|
99
|
+
# @example
|
|
100
|
+
# builder.target_attribute_name
|
|
101
|
+
# # => :dropdown_target
|
|
102
|
+
def target_attribute_name
|
|
103
|
+
key :target
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Adds a Stimulus value to the HTML attributes
|
|
107
|
+
#
|
|
108
|
+
# Values are used to pass data from HTML to Stimulus controllers.
|
|
109
|
+
#
|
|
110
|
+
# @param name [String, Symbol] The name of the value
|
|
111
|
+
# @param value [Object] The value to set (will be JSON-encoded if necessary)
|
|
112
|
+
# @return [void]
|
|
113
|
+
#
|
|
114
|
+
# @example
|
|
115
|
+
# builder.with_value(:open, false)
|
|
116
|
+
# # Adds: data-dropdown-open-value="false"
|
|
117
|
+
def with_value(name, value)
|
|
118
|
+
validate! camelize(name), @definition&.fetch("values", {})&.keys, "value"
|
|
119
|
+
html_attributes.merge_stimulus! key(name, :value) => value
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Adds a Stimulus target attribute to the HTML attributes
|
|
123
|
+
#
|
|
124
|
+
# @param name [String, Symbol] The target name (snake_case camelizes)
|
|
125
|
+
# @return [void]
|
|
126
|
+
#
|
|
127
|
+
# @example
|
|
128
|
+
# builder.with_target(:dialog)
|
|
129
|
+
# # Adds: data-dropdown-target="dialog"
|
|
130
|
+
def with_target(name)
|
|
131
|
+
html_attributes.merge_stimulus! target_attribute_name => target(name)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Validates and returns the JS target name
|
|
135
|
+
#
|
|
136
|
+
# @param name [String, Symbol] The target name
|
|
137
|
+
# @return [String] The camelCase target name
|
|
138
|
+
def target(name)
|
|
139
|
+
camelize(name).tap { |js_name| validate! js_name, @definition&.fetch("targets", []), "target" }
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# Adds a Stimulus class reference to the HTML attributes
|
|
143
|
+
#
|
|
144
|
+
# Classes are used to reference CSS classes that the controller can toggle.
|
|
145
|
+
#
|
|
146
|
+
# @param name [String, Symbol] The name of the class reference
|
|
147
|
+
# @param value [String] The CSS class name(s)
|
|
148
|
+
# @return [void]
|
|
149
|
+
#
|
|
150
|
+
# @example
|
|
151
|
+
# builder.with_class(:active, "bg-blue-500 text-white")
|
|
152
|
+
# # Adds: data-dropdown-active-class="bg-blue-500 text-white"
|
|
153
|
+
def with_class(name, value)
|
|
154
|
+
html_attributes.merge_stimulus! key(name, :class) => value
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Adds a Stimulus outlet reference to the HTML attributes
|
|
158
|
+
#
|
|
159
|
+
# Outlets allow one controller to reference and interact with other controllers.
|
|
160
|
+
#
|
|
161
|
+
# @param name [String, Symbol] The name of the outlet
|
|
162
|
+
# @param value [String] The outlet selector
|
|
163
|
+
# @return [void]
|
|
164
|
+
#
|
|
165
|
+
# @example
|
|
166
|
+
# builder.with_outlet(:modal, ".modal-controller")
|
|
167
|
+
# # Adds: data-dropdown-modal-outlet=".modal-controller"
|
|
168
|
+
def with_outlet(name, value)
|
|
169
|
+
html_attributes.merge_stimulus! key(format_identifier(name), :outlet) => value
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# Adds an action parameter attribute (`data-<identifier>-<name>-param`),
|
|
173
|
+
# which the controller reads from `event.params`.
|
|
174
|
+
#
|
|
175
|
+
# @param name [String, Symbol] The parameter name
|
|
176
|
+
# @param value [Object] The parameter value
|
|
177
|
+
# @return [void]
|
|
178
|
+
#
|
|
179
|
+
# @example
|
|
180
|
+
# builder.with_param(:size, "large")
|
|
181
|
+
# # Adds: data-dropdown-size-param="large"
|
|
182
|
+
def with_param(name, value)
|
|
183
|
+
html_attributes.merge_stimulus! param_attribute_name(name) => value
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
# Returns the attribute name for a parameter
|
|
187
|
+
#
|
|
188
|
+
# @param name [String, Symbol] The parameter name
|
|
189
|
+
# @return [Symbol] The parameter attribute name
|
|
190
|
+
#
|
|
191
|
+
# @example
|
|
192
|
+
# builder.param_attribute_name(:size)
|
|
193
|
+
# # => :dropdown_size_param
|
|
194
|
+
def param_attribute_name(name)
|
|
195
|
+
key name, :param
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# Adds a Stimulus action (event listener) to the HTML attributes
|
|
199
|
+
#
|
|
200
|
+
# @param method [String, Symbol] The controller method to call
|
|
201
|
+
# @param on [Symbol, String, Array<Symbol>, nil] The event(s) to listen for
|
|
202
|
+
# @param at [Symbol, String, nil] The event target (e.g., :window, :document)
|
|
203
|
+
# @return [void]
|
|
204
|
+
#
|
|
205
|
+
# @example Basic action
|
|
206
|
+
# builder.with_action(:toggle, on: :click)
|
|
207
|
+
# # Adds: data-action="click->dropdown#toggle"
|
|
208
|
+
#
|
|
209
|
+
# @example Multiple events using alias
|
|
210
|
+
# builder.with_action(:show, on: :hover_in)
|
|
211
|
+
# # Adds: data-action="mouseenter->dropdown#show focus->dropdown#show"
|
|
212
|
+
#
|
|
213
|
+
# @example Global event
|
|
214
|
+
# builder.with_action(:close, on: :keydown, at: :window)
|
|
215
|
+
# # Adds: data-action="keydown@window->dropdown#close"
|
|
216
|
+
def with_action(method, on: nil, at: nil)
|
|
217
|
+
html_attributes.merge_stimulus_actions! action(method, on: on, at: at)
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
# Builds a Stimulus action descriptor string without adding it to the
|
|
221
|
+
# attributes ({#with_action} adds it).
|
|
222
|
+
#
|
|
223
|
+
# @param method [String, Symbol] The controller method to call
|
|
224
|
+
# @param on [Symbol, String, Array<Symbol>, nil] The event(s) to listen for
|
|
225
|
+
# @param at [Symbol, String, nil] The event target (e.g., :window, :document)
|
|
226
|
+
# @return [String] The formatted action string
|
|
227
|
+
#
|
|
228
|
+
# @example Simple action
|
|
229
|
+
# builder.action(:toggle)
|
|
230
|
+
# # => "dropdown#toggle"
|
|
231
|
+
#
|
|
232
|
+
# @example With event
|
|
233
|
+
# builder.action(:toggle, on: :click)
|
|
234
|
+
# # => "click->dropdown#toggle"
|
|
235
|
+
#
|
|
236
|
+
# @example With event target
|
|
237
|
+
# builder.action(:close, on: :keydown, at: :window)
|
|
238
|
+
# # => "keydown@window->dropdown#close"
|
|
239
|
+
#
|
|
240
|
+
# @example Multiple events
|
|
241
|
+
# builder.action(:show, on: [:mouseenter, :focus])
|
|
242
|
+
# # => "mouseenter->dropdown#show focus->dropdown#show"
|
|
243
|
+
def action(method, on: nil, at: nil)
|
|
244
|
+
js_method = camelize(method)
|
|
245
|
+
validate! js_method, @definition&.fetch("methods", []), "action method"
|
|
246
|
+
controller_method = "#{identifier}##{js_method}"
|
|
247
|
+
|
|
248
|
+
if on.nil?
|
|
249
|
+
controller_method
|
|
250
|
+
else
|
|
251
|
+
events = Array(on).map { |event| EVENT_ALIASES.fetch(event, event) }.flatten
|
|
252
|
+
|
|
253
|
+
controller_actions = events.each_with_object([]) do |event, actions|
|
|
254
|
+
if event.nil?
|
|
255
|
+
actions << controller_method
|
|
256
|
+
else
|
|
257
|
+
event = "#{event}@#{at}" if at.present?
|
|
258
|
+
actions << "#{event}->#{controller_method}"
|
|
259
|
+
end
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
controller_actions.join " "
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
# Builds a custom Stimulus event name
|
|
267
|
+
#
|
|
268
|
+
# @param event [String, Symbol] The event name
|
|
269
|
+
# @return [String] The namespaced event name
|
|
270
|
+
#
|
|
271
|
+
# @example
|
|
272
|
+
# builder.event(:opened)
|
|
273
|
+
# # => "dropdown:opened"
|
|
274
|
+
#
|
|
275
|
+
# @example Usage in JavaScript
|
|
276
|
+
# // Dispatch custom event: this.dispatch("opened")
|
|
277
|
+
# // Listen in HTML: data-action="dropdown:opened->other#handleOpened"
|
|
278
|
+
def event(event)
|
|
279
|
+
"#{identifier}:#{event}"
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
private
|
|
283
|
+
|
|
284
|
+
delegate :format_identifier, to: :class
|
|
285
|
+
|
|
286
|
+
# Ruby snake_case -> Stimulus camelCase (the JS-side name).
|
|
287
|
+
def camelize(name)
|
|
288
|
+
name.to_s.gsub(/_([a-z\d])/) { Regexp.last_match(1).upcase }
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
# No-op for host-app controllers (no definition); raises with the
|
|
292
|
+
# known list for poetry controllers - the message IS the fix
|
|
293
|
+
# (agent-teachable failures).
|
|
294
|
+
def validate!(js_name, known, kind)
|
|
295
|
+
return if known.nil? || known.include?(js_name)
|
|
296
|
+
|
|
297
|
+
raise Manifest::UnknownName,
|
|
298
|
+
"unknown #{kind} #{js_name.inspect} for #{identifier} - known #{kind}s: #{known.sort.join(", ")}"
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
# Builds an attribute key from parts
|
|
302
|
+
#
|
|
303
|
+
# @param parts [Array] The parts to join
|
|
304
|
+
# @return [Symbol] The formatted key
|
|
305
|
+
#
|
|
306
|
+
# @example
|
|
307
|
+
# key(:open, :value)
|
|
308
|
+
# # => :dropdown_open_value
|
|
309
|
+
def key(*parts)
|
|
310
|
+
key = parts.map(&:to_s)
|
|
311
|
+
key.unshift identifier
|
|
312
|
+
key.join("_").tr("-", "_").to_sym
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
# Registers multiple Stimulus values from a hash
|
|
316
|
+
#
|
|
317
|
+
# @param values [Hash, nil] Hash of name-value pairs
|
|
318
|
+
# @return [void]
|
|
319
|
+
def register_values(values)
|
|
320
|
+
return if values.blank?
|
|
321
|
+
|
|
322
|
+
values.each { |name, value| with_value name, value }
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
# Registers multiple Stimulus classes from a hash
|
|
326
|
+
#
|
|
327
|
+
# @param classes [Hash, nil] Hash of name-class pairs
|
|
328
|
+
# @return [void]
|
|
329
|
+
def register_classes(classes)
|
|
330
|
+
return if classes.blank?
|
|
331
|
+
|
|
332
|
+
classes.each { |name, value| with_class name, value }
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
# Registers multiple Stimulus outlets from a hash
|
|
336
|
+
#
|
|
337
|
+
# @param outlets [Hash, nil] Hash of name-outlet pairs
|
|
338
|
+
# @return [void]
|
|
339
|
+
def register_outlets(outlets)
|
|
340
|
+
return if outlets.blank?
|
|
341
|
+
|
|
342
|
+
outlets.each { |name, value| with_outlet name, value }
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
# Registers multiple custom params from a hash
|
|
346
|
+
#
|
|
347
|
+
# @param params [Hash, nil] Hash of name-param pairs
|
|
348
|
+
# @return [void]
|
|
349
|
+
def register_params(params)
|
|
350
|
+
return if params.blank?
|
|
351
|
+
|
|
352
|
+
params.each { |name, value| with_param name, value }
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
# Registers multiple Stimulus actions from a hash
|
|
356
|
+
#
|
|
357
|
+
# @param actions [Hash, nil] Hash of method-event pairs
|
|
358
|
+
# Values can be symbols, arrays, or hashes with :on and :at keys
|
|
359
|
+
# @return [void]
|
|
360
|
+
#
|
|
361
|
+
# @example
|
|
362
|
+
# register_actions(toggle: :click, close: { on: :keydown, at: :window })
|
|
363
|
+
def register_actions(actions)
|
|
364
|
+
return if actions.blank?
|
|
365
|
+
|
|
366
|
+
actions.each do |method, value|
|
|
367
|
+
if value.is_a?(Hash)
|
|
368
|
+
with_action method, **value
|
|
369
|
+
else
|
|
370
|
+
with_action method, on: value
|
|
371
|
+
end
|
|
372
|
+
end
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
class << self
|
|
376
|
+
# Formats an identifier by converting underscores to dashes
|
|
377
|
+
#
|
|
378
|
+
# @param identifier [String, Symbol, Array] The identifier(s) to format
|
|
379
|
+
# @return [String] The formatted identifier
|
|
380
|
+
#
|
|
381
|
+
# @example Single identifier
|
|
382
|
+
# format_identifier(:my_controller)
|
|
383
|
+
# # => "my-controller"
|
|
384
|
+
#
|
|
385
|
+
# @example Multiple identifiers (namespaced)
|
|
386
|
+
# format_identifier([:admin, :dropdown])
|
|
387
|
+
# # => "admin--dropdown"
|
|
388
|
+
def format_identifier(identifier)
|
|
389
|
+
Array(identifier).map { |i| i.to_s.tr("_", "-") }.join("--")
|
|
390
|
+
end
|
|
391
|
+
end
|
|
392
|
+
end
|
|
393
|
+
end
|
|
394
|
+
end
|
|
395
|
+
end
|