opal-vite 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/opal/vite/version.rb +1 -1
- data/lib/opal-vite.rb +10 -2
- data/opal/opal_vite/concerns/react_helpers.rb +315 -0
- data/opal/opal_vite/concerns/stimulus_helpers.rb +427 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a747844bb38869490030ea9a7649cdd6f5b665cf2a60da55f3cc12f479aa556b
|
|
4
|
+
data.tar.gz: 8e0de9d6b0b124f5ee0cb83cfd242199785c6a7ecdfb1f2e3ba0f7253bb5c663
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d12af9028e4ca9537a4af9043a74fc3dd3cf043a0ed928ad199a5bf36625e2d3a0d392d7e45ac8f49832edb5c409664334ddb60571f794511156a4d50332644c
|
|
7
|
+
data.tar.gz: '083ab7d7c102a315c9253491790014b6a5af41996bf81190d4a3f2c56bce9e503a3d204a0013866d2f9a956d311b9136bc2f5b86a317350d9f9585fe9ffc0299'
|
data/lib/opal/vite/version.rb
CHANGED
data/lib/opal-vite.rb
CHANGED
|
@@ -21,9 +21,17 @@ module Opal
|
|
|
21
21
|
yield(config) if block_given?
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
+
# Returns the path to the opal/ directory in this gem
|
|
25
|
+
# Contains built-in concerns like StimulusHelpers
|
|
26
|
+
def opal_lib_path
|
|
27
|
+
File.expand_path('../../opal', __dir__)
|
|
28
|
+
end
|
|
29
|
+
|
|
24
30
|
# CLI entry point for compilation
|
|
25
|
-
|
|
26
|
-
|
|
31
|
+
# @param file_path [String] The path to the Ruby file to compile
|
|
32
|
+
# @param include_concerns [Boolean] Whether to include built-in concerns
|
|
33
|
+
def compile_for_vite(file_path, include_concerns: true)
|
|
34
|
+
compiler = Compiler.new(include_concerns: include_concerns)
|
|
27
35
|
result = compiler.compile_file(file_path)
|
|
28
36
|
|
|
29
37
|
# Output JSON to stdout for the Vite plugin to consume
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
# backtick_javascript: true
|
|
2
|
+
|
|
3
|
+
# ReactHelpers - DSL helpers for React applications with Opal
|
|
4
|
+
# Reduces backtick JavaScript usage in React components
|
|
5
|
+
module ReactHelpers
|
|
6
|
+
# ===================
|
|
7
|
+
# React Access
|
|
8
|
+
# ===================
|
|
9
|
+
|
|
10
|
+
# Get React from window
|
|
11
|
+
def react
|
|
12
|
+
Native(`window.React`)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Get ReactDOM from window
|
|
16
|
+
def react_dom
|
|
17
|
+
Native(`window.ReactDOM`)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# ===================
|
|
21
|
+
# Window/Global Access
|
|
22
|
+
# ===================
|
|
23
|
+
|
|
24
|
+
# Get a property from window
|
|
25
|
+
def window_get(key)
|
|
26
|
+
`window[#{key}]`
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Set a property on window
|
|
30
|
+
def window_set(key, value)
|
|
31
|
+
`window[#{key}] = #{value}`
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Delete a property from window
|
|
35
|
+
def window_delete(key)
|
|
36
|
+
`delete window[#{key}]`
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# ===================
|
|
40
|
+
# Console
|
|
41
|
+
# ===================
|
|
42
|
+
|
|
43
|
+
# Console log
|
|
44
|
+
def console_log(*args)
|
|
45
|
+
`console.log(...#{args})`
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Console warn
|
|
49
|
+
def console_warn(*args)
|
|
50
|
+
`console.warn(...#{args})`
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Console error
|
|
54
|
+
def console_error(*args)
|
|
55
|
+
`console.error(...#{args})`
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# ===================
|
|
59
|
+
# Alerts/Dialogs
|
|
60
|
+
# ===================
|
|
61
|
+
|
|
62
|
+
# Show alert dialog
|
|
63
|
+
def alert_message(message)
|
|
64
|
+
`alert(#{message})`
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Show confirm dialog
|
|
68
|
+
def confirm_message(message)
|
|
69
|
+
`confirm(#{message})`
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Show prompt dialog
|
|
73
|
+
def prompt_message(message, default_value = '')
|
|
74
|
+
`prompt(#{message}, #{default_value})`
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# ===================
|
|
78
|
+
# DOM Events
|
|
79
|
+
# ===================
|
|
80
|
+
|
|
81
|
+
# Execute block when DOM is ready
|
|
82
|
+
def on_dom_ready(&block)
|
|
83
|
+
`document.addEventListener('DOMContentLoaded', #{block})`
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Add event listener to window
|
|
87
|
+
def on_window_event(event_name, &block)
|
|
88
|
+
`window.addEventListener(#{event_name}, #{block})`
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Remove event listener from window
|
|
92
|
+
def off_window_event(event_name, handler)
|
|
93
|
+
`window.removeEventListener(#{event_name}, #{handler})`
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# ===================
|
|
97
|
+
# DOM Query
|
|
98
|
+
# ===================
|
|
99
|
+
|
|
100
|
+
# Query single element
|
|
101
|
+
def query(selector)
|
|
102
|
+
`document.querySelector(#{selector})`
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Query all elements
|
|
106
|
+
def query_all(selector)
|
|
107
|
+
`Array.from(document.querySelectorAll(#{selector}))`
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Get element by ID
|
|
111
|
+
def get_element_by_id(id)
|
|
112
|
+
`document.getElementById(#{id})`
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# ===================
|
|
116
|
+
# DOM Manipulation
|
|
117
|
+
# ===================
|
|
118
|
+
|
|
119
|
+
# Create element
|
|
120
|
+
def create_element(tag)
|
|
121
|
+
`document.createElement(#{tag})`
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# Set innerHTML
|
|
125
|
+
def set_html(element, html)
|
|
126
|
+
`#{element}.innerHTML = #{html}`
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Set textContent
|
|
130
|
+
def set_text(element, text)
|
|
131
|
+
`#{element}.textContent = #{text}`
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Add class to element
|
|
135
|
+
def add_class(element, *classes)
|
|
136
|
+
`#{element}.classList.add(...#{classes})`
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Remove class from element
|
|
140
|
+
def remove_class(element, *classes)
|
|
141
|
+
`#{element}.classList.remove(...#{classes})`
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# ===================
|
|
145
|
+
# React Element Creation Helpers
|
|
146
|
+
# ===================
|
|
147
|
+
|
|
148
|
+
# Create React element (shorthand)
|
|
149
|
+
def el(type, props = nil, *children)
|
|
150
|
+
react.createElement(type, props, *children)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# Create div element
|
|
154
|
+
def div(props = nil, *children, &block)
|
|
155
|
+
if block_given?
|
|
156
|
+
react.createElement('div', props, block.call)
|
|
157
|
+
else
|
|
158
|
+
react.createElement('div', props, *children)
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# Create span element
|
|
163
|
+
def span(props = nil, *children, &block)
|
|
164
|
+
if block_given?
|
|
165
|
+
react.createElement('span', props, block.call)
|
|
166
|
+
else
|
|
167
|
+
react.createElement('span', props, *children)
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# Create button element
|
|
172
|
+
def button(props = nil, *children, &block)
|
|
173
|
+
if block_given?
|
|
174
|
+
react.createElement('button', props, block.call)
|
|
175
|
+
else
|
|
176
|
+
react.createElement('button', props, *children)
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# Create p element
|
|
181
|
+
def paragraph(props = nil, *children, &block)
|
|
182
|
+
if block_given?
|
|
183
|
+
react.createElement('p', props, block.call)
|
|
184
|
+
else
|
|
185
|
+
react.createElement('p', props, *children)
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# Create heading elements
|
|
190
|
+
def h1(props = nil, *children)
|
|
191
|
+
react.createElement('h1', props, *children)
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def h2(props = nil, *children)
|
|
195
|
+
react.createElement('h2', props, *children)
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def h3(props = nil, *children)
|
|
199
|
+
react.createElement('h3', props, *children)
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
# ===================
|
|
203
|
+
# Timing
|
|
204
|
+
# ===================
|
|
205
|
+
|
|
206
|
+
# Set timeout
|
|
207
|
+
def set_timeout(delay_ms, &block)
|
|
208
|
+
`setTimeout(#{block}, #{delay_ms})`
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# Set interval
|
|
212
|
+
def set_interval(interval_ms, &block)
|
|
213
|
+
`setInterval(#{block}, #{interval_ms})`
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
# Clear timeout
|
|
217
|
+
def clear_timeout(timeout_id)
|
|
218
|
+
`clearTimeout(#{timeout_id})`
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
# Clear interval
|
|
222
|
+
def clear_interval(interval_id)
|
|
223
|
+
`clearInterval(#{interval_id})`
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
# ===================
|
|
227
|
+
# LocalStorage
|
|
228
|
+
# ===================
|
|
229
|
+
|
|
230
|
+
# Get from localStorage
|
|
231
|
+
def storage_get(key)
|
|
232
|
+
`localStorage.getItem(#{key})`
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
# Set to localStorage
|
|
236
|
+
def storage_set(key, value)
|
|
237
|
+
`localStorage.setItem(#{key}, #{value})`
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
# Remove from localStorage
|
|
241
|
+
def storage_remove(key)
|
|
242
|
+
`localStorage.removeItem(#{key})`
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
# ===================
|
|
246
|
+
# Fetch API
|
|
247
|
+
# ===================
|
|
248
|
+
|
|
249
|
+
# Fetch with promise (returns Native promise)
|
|
250
|
+
def fetch_url(url, options = nil)
|
|
251
|
+
if options
|
|
252
|
+
Native(`fetch(#{url}, #{options.to_n})`)
|
|
253
|
+
else
|
|
254
|
+
Native(`fetch(#{url})`)
|
|
255
|
+
end
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
# ===================
|
|
259
|
+
# JSON
|
|
260
|
+
# ===================
|
|
261
|
+
|
|
262
|
+
# Parse JSON string
|
|
263
|
+
def parse_json(json_string)
|
|
264
|
+
`JSON.parse(#{json_string})`
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
# Stringify to JSON
|
|
268
|
+
def to_json(object)
|
|
269
|
+
`JSON.stringify(#{object})`
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
# ===================
|
|
273
|
+
# Type Conversion
|
|
274
|
+
# ===================
|
|
275
|
+
|
|
276
|
+
# Parse string to integer (wrapper for JavaScript parseInt)
|
|
277
|
+
# @param value [String, Number] Value to parse
|
|
278
|
+
# @param radix [Integer] Radix (default: 10)
|
|
279
|
+
# @return [Integer, NaN] Parsed integer
|
|
280
|
+
def parse_int(value, radix = 10)
|
|
281
|
+
`parseInt(#{value}, #{radix})`
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
# Parse string to float (wrapper for JavaScript parseFloat)
|
|
285
|
+
# @param value [String, Number] Value to parse
|
|
286
|
+
# @return [Float, NaN] Parsed float
|
|
287
|
+
def parse_float(value)
|
|
288
|
+
`parseFloat(#{value})`
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
# Check if value is NaN
|
|
292
|
+
# @param value [Number] Value to check
|
|
293
|
+
# @return [Boolean] true if NaN
|
|
294
|
+
def is_nan?(value)
|
|
295
|
+
`Number.isNaN(#{value})`
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
# Parse integer with default value (returns default if NaN)
|
|
299
|
+
# @param value [String, Number] Value to parse
|
|
300
|
+
# @param default_value [Integer] Default value if parsing fails
|
|
301
|
+
# @return [Integer] Parsed integer or default
|
|
302
|
+
def parse_int_or(value, default_value = 0)
|
|
303
|
+
result = parse_int(value)
|
|
304
|
+
is_nan?(result) ? default_value : result
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
# Parse float with default value (returns default if NaN)
|
|
308
|
+
# @param value [String, Number] Value to parse
|
|
309
|
+
# @param default_value [Float] Default value if parsing fails
|
|
310
|
+
# @return [Float] Parsed float or default
|
|
311
|
+
def parse_float_or(value, default_value = 0.0)
|
|
312
|
+
result = parse_float(value)
|
|
313
|
+
is_nan?(result) ? default_value : result
|
|
314
|
+
end
|
|
315
|
+
end
|
|
@@ -339,6 +339,391 @@ module OpalVite
|
|
|
339
339
|
`#{array}[#{index}]`
|
|
340
340
|
end
|
|
341
341
|
|
|
342
|
+
# ===== LocalStorage Methods =====
|
|
343
|
+
|
|
344
|
+
# Get item from localStorage
|
|
345
|
+
# @param key [String] Storage key
|
|
346
|
+
# @return [String, nil] Stored value or nil
|
|
347
|
+
def storage_get(key)
|
|
348
|
+
`localStorage.getItem(#{key})`
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
# Set item in localStorage
|
|
352
|
+
# @param key [String] Storage key
|
|
353
|
+
# @param value [String] Value to store
|
|
354
|
+
def storage_set(key, value)
|
|
355
|
+
`localStorage.setItem(#{key}, #{value})`
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
# Remove item from localStorage
|
|
359
|
+
# @param key [String] Storage key
|
|
360
|
+
def storage_remove(key)
|
|
361
|
+
`localStorage.removeItem(#{key})`
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
# Get JSON-parsed value from localStorage
|
|
365
|
+
# @param key [String] Storage key
|
|
366
|
+
# @param default [Object] Default value if key doesn't exist
|
|
367
|
+
# @return [Object] Parsed value or default
|
|
368
|
+
def storage_get_json(key, default = nil)
|
|
369
|
+
stored = storage_get(key)
|
|
370
|
+
return default if `#{stored} === null`
|
|
371
|
+
`JSON.parse(#{stored})`
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
# Set JSON-stringified value in localStorage
|
|
375
|
+
# @param key [String] Storage key
|
|
376
|
+
# @param value [Object] Value to store (will be JSON-stringified)
|
|
377
|
+
def storage_set_json(key, value)
|
|
378
|
+
`localStorage.setItem(#{key}, JSON.stringify(#{value.to_n}))`
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
# ===== Event Methods =====
|
|
382
|
+
|
|
383
|
+
# Dispatch a custom event on window
|
|
384
|
+
# @param name [String] Event name
|
|
385
|
+
# @param detail [Hash] Event detail data
|
|
386
|
+
def dispatch_window_event(name, detail = {})
|
|
387
|
+
native_detail = detail.is_a?(Hash) ? detail.to_n : detail
|
|
388
|
+
`
|
|
389
|
+
const event = new CustomEvent(#{name}, { detail: #{native_detail} });
|
|
390
|
+
window.dispatchEvent(event);
|
|
391
|
+
`
|
|
392
|
+
end
|
|
393
|
+
|
|
394
|
+
# Dispatch a custom event on the controller element
|
|
395
|
+
# @param name [String] Event name
|
|
396
|
+
# @param detail [Hash] Event detail data
|
|
397
|
+
def dispatch_event(name, detail = {})
|
|
398
|
+
native_detail = detail.is_a?(Hash) ? detail.to_n : detail
|
|
399
|
+
`
|
|
400
|
+
const event = new CustomEvent(#{name}, { detail: #{native_detail} });
|
|
401
|
+
this.element.dispatchEvent(event);
|
|
402
|
+
`
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
# Add window event listener
|
|
406
|
+
# @param name [String] Event name
|
|
407
|
+
# @yield Block to execute when event fires
|
|
408
|
+
def on_window_event(name, &block)
|
|
409
|
+
`window.addEventListener(#{name}, #{block})`
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
# Remove window event listener
|
|
413
|
+
# @param name [String] Event name
|
|
414
|
+
# @param handler [Native] Handler function to remove
|
|
415
|
+
def off_window_event(name, handler)
|
|
416
|
+
`window.removeEventListener(#{name}, #{handler})`
|
|
417
|
+
end
|
|
418
|
+
|
|
419
|
+
# Add document event listener
|
|
420
|
+
# @param name [String] Event name
|
|
421
|
+
# @yield Block to execute when event fires
|
|
422
|
+
def on_document_event(name, &block)
|
|
423
|
+
`document.addEventListener(#{name}, #{block})`
|
|
424
|
+
end
|
|
425
|
+
|
|
426
|
+
# Add event listener when DOM is ready
|
|
427
|
+
# @yield Block to execute when DOM is ready
|
|
428
|
+
def on_dom_ready(&block)
|
|
429
|
+
`document.addEventListener('DOMContentLoaded', #{block})`
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
# Add event listener to any element
|
|
433
|
+
# @param element [Native] DOM element
|
|
434
|
+
# @param name [String] Event name
|
|
435
|
+
# @yield Block to execute when event fires
|
|
436
|
+
def on_element_event(element, name, &block)
|
|
437
|
+
`#{element}.addEventListener(#{name}, #{block})`
|
|
438
|
+
end
|
|
439
|
+
|
|
440
|
+
# Remove event listener from element
|
|
441
|
+
# @param element [Native] DOM element
|
|
442
|
+
# @param name [String] Event name
|
|
443
|
+
# @param handler [Native] Handler function to remove
|
|
444
|
+
def off_element_event(element, name, handler)
|
|
445
|
+
`#{element}.removeEventListener(#{name}, #{handler})`
|
|
446
|
+
end
|
|
447
|
+
|
|
448
|
+
# Add event listener to controller's element (this.element)
|
|
449
|
+
# @param name [String] Event name
|
|
450
|
+
# @yield Block to execute when event fires
|
|
451
|
+
def on_controller_event(name, &block)
|
|
452
|
+
`
|
|
453
|
+
const handler = #{block};
|
|
454
|
+
this.element.addEventListener(#{name}, function(e) {
|
|
455
|
+
handler(e);
|
|
456
|
+
});
|
|
457
|
+
`
|
|
458
|
+
end
|
|
459
|
+
|
|
460
|
+
# Get the current event's target element
|
|
461
|
+
# @return [Native] Event target element
|
|
462
|
+
def event_target
|
|
463
|
+
`event.currentTarget`
|
|
464
|
+
end
|
|
465
|
+
|
|
466
|
+
# Get data attribute from current event target
|
|
467
|
+
# @param attr [String] Data attribute name (without 'data-' prefix)
|
|
468
|
+
# @return [String, nil] Attribute value
|
|
469
|
+
def event_data(attr)
|
|
470
|
+
`event.currentTarget.getAttribute('data-' + #{attr})`
|
|
471
|
+
end
|
|
472
|
+
|
|
473
|
+
# Get integer data attribute from current event target
|
|
474
|
+
# @param attr [String] Data attribute name (without 'data-' prefix)
|
|
475
|
+
# @return [Integer, nil] Parsed integer value
|
|
476
|
+
def event_data_int(attr)
|
|
477
|
+
parse_int(event_data(attr))
|
|
478
|
+
end
|
|
479
|
+
|
|
480
|
+
# Prevent default event behavior
|
|
481
|
+
def prevent_default
|
|
482
|
+
`event.preventDefault()`
|
|
483
|
+
end
|
|
484
|
+
|
|
485
|
+
# Get event key (for keyboard events)
|
|
486
|
+
# @return [String] Key name
|
|
487
|
+
def event_key
|
|
488
|
+
`event.key`
|
|
489
|
+
end
|
|
490
|
+
|
|
491
|
+
# ===== Element Methods =====
|
|
492
|
+
|
|
493
|
+
# Add class to element
|
|
494
|
+
# @param element [Native] DOM element
|
|
495
|
+
# @param class_name [String] CSS class to add
|
|
496
|
+
def add_class(element, class_name)
|
|
497
|
+
`#{element}.classList.add(#{class_name})`
|
|
498
|
+
end
|
|
499
|
+
|
|
500
|
+
# Remove class from element
|
|
501
|
+
# @param element [Native] DOM element
|
|
502
|
+
# @param class_name [String] CSS class to remove
|
|
503
|
+
def remove_class(element, class_name)
|
|
504
|
+
`#{element}.classList.remove(#{class_name})`
|
|
505
|
+
end
|
|
506
|
+
|
|
507
|
+
# Toggle class on element
|
|
508
|
+
# @param element [Native] DOM element
|
|
509
|
+
# @param class_name [String] CSS class to toggle
|
|
510
|
+
def toggle_class(element, class_name)
|
|
511
|
+
`#{element}.classList.toggle(#{class_name})`
|
|
512
|
+
end
|
|
513
|
+
|
|
514
|
+
# Check if element has class
|
|
515
|
+
# @param element [Native] DOM element
|
|
516
|
+
# @param class_name [String] CSS class to check
|
|
517
|
+
# @return [Boolean]
|
|
518
|
+
def has_class?(element, class_name)
|
|
519
|
+
`#{element}.classList.contains(#{class_name})`
|
|
520
|
+
end
|
|
521
|
+
|
|
522
|
+
# Set element attribute
|
|
523
|
+
# @param element [Native] DOM element
|
|
524
|
+
# @param attr [String] Attribute name
|
|
525
|
+
# @param value [String] Attribute value
|
|
526
|
+
def set_attr(element, attr, value)
|
|
527
|
+
`#{element}.setAttribute(#{attr}, #{value})`
|
|
528
|
+
end
|
|
529
|
+
|
|
530
|
+
# Get element attribute
|
|
531
|
+
# @param element [Native] DOM element
|
|
532
|
+
# @param attr [String] Attribute name
|
|
533
|
+
# @return [String, nil] Attribute value
|
|
534
|
+
def get_attr(element, attr)
|
|
535
|
+
`#{element}.getAttribute(#{attr})`
|
|
536
|
+
end
|
|
537
|
+
|
|
538
|
+
# Remove element attribute
|
|
539
|
+
# @param element [Native] DOM element
|
|
540
|
+
# @param attr [String] Attribute name
|
|
541
|
+
def remove_attr(element, attr)
|
|
542
|
+
`#{element}.removeAttribute(#{attr})`
|
|
543
|
+
end
|
|
544
|
+
|
|
545
|
+
# Set element style
|
|
546
|
+
# @param element [Native] DOM element
|
|
547
|
+
# @param property [String] CSS property
|
|
548
|
+
# @param value [String] CSS value
|
|
549
|
+
def set_style(element, property, value)
|
|
550
|
+
`#{element}.style[#{property}] = #{value}`
|
|
551
|
+
end
|
|
552
|
+
|
|
553
|
+
# Set element innerHTML
|
|
554
|
+
# @param element [Native] DOM element
|
|
555
|
+
# @param html [String] HTML content
|
|
556
|
+
def set_html(element, html)
|
|
557
|
+
`#{element}.innerHTML = #{html}`
|
|
558
|
+
end
|
|
559
|
+
|
|
560
|
+
# Set element textContent
|
|
561
|
+
# @param element [Native] DOM element
|
|
562
|
+
# @param text [String] Text content
|
|
563
|
+
def set_text(element, text)
|
|
564
|
+
`#{element}.textContent = #{text}`
|
|
565
|
+
end
|
|
566
|
+
|
|
567
|
+
# Get element value (for inputs)
|
|
568
|
+
# @param element [Native] DOM element
|
|
569
|
+
# @return [String] Element value
|
|
570
|
+
def get_value(element)
|
|
571
|
+
`#{element}.value`
|
|
572
|
+
end
|
|
573
|
+
|
|
574
|
+
# Set element value (for inputs)
|
|
575
|
+
# @param element [Native] DOM element
|
|
576
|
+
# @param value [String] Value to set
|
|
577
|
+
def set_value(element, value)
|
|
578
|
+
`#{element}.value = #{value}`
|
|
579
|
+
end
|
|
580
|
+
|
|
581
|
+
# Focus element
|
|
582
|
+
# @param element [Native] DOM element
|
|
583
|
+
def focus(element)
|
|
584
|
+
`#{element}.focus()`
|
|
585
|
+
end
|
|
586
|
+
|
|
587
|
+
# Check if element has attribute
|
|
588
|
+
# @param element [Native] DOM element
|
|
589
|
+
# @param attr [String] Attribute name
|
|
590
|
+
# @return [Boolean]
|
|
591
|
+
def has_attr?(element, attr)
|
|
592
|
+
`#{element}.hasAttribute(#{attr})`
|
|
593
|
+
end
|
|
594
|
+
|
|
595
|
+
# ===== DOM Creation Methods =====
|
|
596
|
+
|
|
597
|
+
# Create a new DOM element
|
|
598
|
+
# @param tag [String] HTML tag name
|
|
599
|
+
# @return [Native] Created element
|
|
600
|
+
def create_element(tag)
|
|
601
|
+
`document.createElement(#{tag})`
|
|
602
|
+
end
|
|
603
|
+
|
|
604
|
+
# Append child to element
|
|
605
|
+
# @param parent [Native] Parent element
|
|
606
|
+
# @param child [Native] Child element to append
|
|
607
|
+
def append_child(parent, child)
|
|
608
|
+
`#{parent}.appendChild(#{child})`
|
|
609
|
+
end
|
|
610
|
+
|
|
611
|
+
# Remove element from DOM
|
|
612
|
+
# @param element [Native] Element to remove
|
|
613
|
+
def remove_element(element)
|
|
614
|
+
`#{element}.remove()`
|
|
615
|
+
end
|
|
616
|
+
|
|
617
|
+
# Get next element sibling
|
|
618
|
+
# @param element [Native] DOM element
|
|
619
|
+
# @return [Native, nil] Next sibling element
|
|
620
|
+
def next_sibling(element)
|
|
621
|
+
`#{element}.nextElementSibling`
|
|
622
|
+
end
|
|
623
|
+
|
|
624
|
+
# Get previous element sibling
|
|
625
|
+
# @param element [Native] DOM element
|
|
626
|
+
# @return [Native, nil] Previous sibling element
|
|
627
|
+
def prev_sibling(element)
|
|
628
|
+
`#{element}.previousElementSibling`
|
|
629
|
+
end
|
|
630
|
+
|
|
631
|
+
# Get parent element
|
|
632
|
+
# @param element [Native] DOM element
|
|
633
|
+
# @return [Native, nil] Parent element
|
|
634
|
+
def parent(element)
|
|
635
|
+
`#{element}.parentElement`
|
|
636
|
+
end
|
|
637
|
+
|
|
638
|
+
# ===== DOM Query Methods =====
|
|
639
|
+
|
|
640
|
+
# Query selector on document
|
|
641
|
+
# @param selector [String] CSS selector
|
|
642
|
+
# @return [Native, nil] Element or nil
|
|
643
|
+
def query(selector)
|
|
644
|
+
`document.querySelector(#{selector})`
|
|
645
|
+
end
|
|
646
|
+
|
|
647
|
+
# Query selector all on document
|
|
648
|
+
# @param selector [String] CSS selector
|
|
649
|
+
# @return [Array] Array of elements
|
|
650
|
+
def query_all(selector)
|
|
651
|
+
`Array.from(document.querySelectorAll(#{selector}))`
|
|
652
|
+
end
|
|
653
|
+
|
|
654
|
+
# Query selector on controller element
|
|
655
|
+
# @param selector [String] CSS selector
|
|
656
|
+
# @return [Native, nil] Element or nil
|
|
657
|
+
def query_element(selector)
|
|
658
|
+
`this.element.querySelector(#{selector})`
|
|
659
|
+
end
|
|
660
|
+
|
|
661
|
+
# Query selector all on controller element
|
|
662
|
+
# @param selector [String] CSS selector
|
|
663
|
+
# @return [Array] Array of elements
|
|
664
|
+
def query_all_element(selector)
|
|
665
|
+
`Array.from(this.element.querySelectorAll(#{selector}))`
|
|
666
|
+
end
|
|
667
|
+
|
|
668
|
+
# ===== Document Methods =====
|
|
669
|
+
|
|
670
|
+
# Get document root element (html)
|
|
671
|
+
# @return [Native] HTML element
|
|
672
|
+
def doc_root
|
|
673
|
+
`document.documentElement`
|
|
674
|
+
end
|
|
675
|
+
|
|
676
|
+
# Set attribute on document root
|
|
677
|
+
# @param attr [String] Attribute name
|
|
678
|
+
# @param value [String] Attribute value
|
|
679
|
+
def set_root_attr(attr, value)
|
|
680
|
+
`document.documentElement.setAttribute(#{attr}, #{value})`
|
|
681
|
+
end
|
|
682
|
+
|
|
683
|
+
# Get attribute from document root
|
|
684
|
+
# @param attr [String] Attribute name
|
|
685
|
+
# @return [String, nil] Attribute value
|
|
686
|
+
def get_root_attr(attr)
|
|
687
|
+
`document.documentElement.getAttribute(#{attr})`
|
|
688
|
+
end
|
|
689
|
+
|
|
690
|
+
# ===== Template Methods =====
|
|
691
|
+
|
|
692
|
+
# Clone a template target's content
|
|
693
|
+
# @param name [Symbol, String] Template target name
|
|
694
|
+
# @return [Native] Cloned content
|
|
695
|
+
def clone_template(name)
|
|
696
|
+
method_name = "#{camelize(name, false)}Target"
|
|
697
|
+
`this[#{method_name}].content.cloneNode(true)`
|
|
698
|
+
end
|
|
699
|
+
|
|
700
|
+
# Get first element child from cloned template
|
|
701
|
+
# @param clone [Native] Cloned template content
|
|
702
|
+
# @return [Native, nil] First element child
|
|
703
|
+
def template_first_child(clone)
|
|
704
|
+
`#{clone}.firstElementChild`
|
|
705
|
+
end
|
|
706
|
+
|
|
707
|
+
# ===== Stimulus Controller Element Methods =====
|
|
708
|
+
|
|
709
|
+
# Add class to controller element
|
|
710
|
+
# @param class_name [String] CSS class to add
|
|
711
|
+
def element_add_class(class_name)
|
|
712
|
+
`this.element.classList.add(#{class_name})`
|
|
713
|
+
end
|
|
714
|
+
|
|
715
|
+
# Remove class from controller element
|
|
716
|
+
# @param class_name [String] CSS class to remove
|
|
717
|
+
def element_remove_class(class_name)
|
|
718
|
+
`this.element.classList.remove(#{class_name})`
|
|
719
|
+
end
|
|
720
|
+
|
|
721
|
+
# Toggle class on controller element
|
|
722
|
+
# @param class_name [String] CSS class to toggle
|
|
723
|
+
def element_toggle_class(class_name)
|
|
724
|
+
`this.element.classList.toggle(#{class_name})`
|
|
725
|
+
end
|
|
726
|
+
|
|
342
727
|
# ===== Utility Methods =====
|
|
343
728
|
|
|
344
729
|
# Generate a unique ID based on timestamp
|
|
@@ -363,6 +748,48 @@ module OpalVite
|
|
|
363
748
|
`this[#{method_name}].blur()`
|
|
364
749
|
end
|
|
365
750
|
|
|
751
|
+
# ===== Type Conversion Methods =====
|
|
752
|
+
|
|
753
|
+
# Parse string to integer (wrapper for JavaScript parseInt)
|
|
754
|
+
# @param value [String, Number] Value to parse
|
|
755
|
+
# @param radix [Integer] Radix (default: 10)
|
|
756
|
+
# @return [Integer, NaN] Parsed integer
|
|
757
|
+
def parse_int(value, radix = 10)
|
|
758
|
+
`parseInt(#{value}, #{radix})`
|
|
759
|
+
end
|
|
760
|
+
|
|
761
|
+
# Parse string to float (wrapper for JavaScript parseFloat)
|
|
762
|
+
# @param value [String, Number] Value to parse
|
|
763
|
+
# @return [Float, NaN] Parsed float
|
|
764
|
+
def parse_float(value)
|
|
765
|
+
`parseFloat(#{value})`
|
|
766
|
+
end
|
|
767
|
+
|
|
768
|
+
# Check if value is NaN
|
|
769
|
+
# @param value [Number] Value to check
|
|
770
|
+
# @return [Boolean] true if NaN
|
|
771
|
+
def is_nan?(value)
|
|
772
|
+
`Number.isNaN(#{value})`
|
|
773
|
+
end
|
|
774
|
+
|
|
775
|
+
# Parse integer with default value (returns default if NaN)
|
|
776
|
+
# @param value [String, Number] Value to parse
|
|
777
|
+
# @param default_value [Integer] Default value if parsing fails
|
|
778
|
+
# @return [Integer] Parsed integer or default
|
|
779
|
+
def parse_int_or(value, default_value = 0)
|
|
780
|
+
result = parse_int(value)
|
|
781
|
+
is_nan?(result) ? default_value : result
|
|
782
|
+
end
|
|
783
|
+
|
|
784
|
+
# Parse float with default value (returns default if NaN)
|
|
785
|
+
# @param value [String, Number] Value to parse
|
|
786
|
+
# @param default_value [Float] Default value if parsing fails
|
|
787
|
+
# @return [Float] Parsed float or default
|
|
788
|
+
def parse_float_or(value, default_value = 0.0)
|
|
789
|
+
result = parse_float(value)
|
|
790
|
+
is_nan?(result) ? default_value : result
|
|
791
|
+
end
|
|
792
|
+
|
|
366
793
|
private
|
|
367
794
|
|
|
368
795
|
# Convert snake_case to camelCase
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: opal-vite
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- stofu1234
|
|
@@ -114,6 +114,7 @@ files:
|
|
|
114
114
|
- opal/opal_vite/concerns.rb
|
|
115
115
|
- opal/opal_vite/concerns/dom_helpers.rb
|
|
116
116
|
- opal/opal_vite/concerns/js_proxy_ex.rb
|
|
117
|
+
- opal/opal_vite/concerns/react_helpers.rb
|
|
117
118
|
- opal/opal_vite/concerns/stimulus_helpers.rb
|
|
118
119
|
- opal/opal_vite/concerns/storable.rb
|
|
119
120
|
- opal/opal_vite/concerns/toastable.rb
|