opal-vite 0.3.8 → 0.3.11
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/opal/opal_vite/concerns/v1/functional_component.rb +245 -0
- data/opal/opal_vite/concerns/v1.rb +1 -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: c5628cb3dc5134851617af485e740c1bea4450cd413afabcd998331337ea2441
|
|
4
|
+
data.tar.gz: 54dce35513cf4eae90c8365877d9147ecbd6dcd0f6ebfd3cc58dc333a94df403
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f5173decf97b9880ed22b43b722951cb50f7c88ced4894929c926130f8edb99a322e663457ec324c376213c82c64da84032ab91aacb6e73e4a1e35ae96567c9e
|
|
7
|
+
data.tar.gz: 270053e539217d025395db5beb7eed0e4788176e68a849c251388a5c52be06c37db4af6b8f20e6ef60337865307d272bcdf91a8b4139ce125a53fbc6b8949cc2
|
data/lib/opal/vite/version.rb
CHANGED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
# backtick_javascript: true
|
|
2
|
+
|
|
3
|
+
module OpalVite
|
|
4
|
+
module Concerns
|
|
5
|
+
module V1
|
|
6
|
+
# FunctionalComponent - Create React functional components with hooks in Ruby
|
|
7
|
+
#
|
|
8
|
+
# This module provides a DSL to create React functional components
|
|
9
|
+
# while hiding backtick JavaScript internally.
|
|
10
|
+
#
|
|
11
|
+
# @example Basic usage with useState
|
|
12
|
+
# class CounterComponent
|
|
13
|
+
# extend FunctionalComponent
|
|
14
|
+
# extend ReactHelpers
|
|
15
|
+
#
|
|
16
|
+
# def self.to_n
|
|
17
|
+
# create_component do |hooks|
|
|
18
|
+
# count, set_count = hooks.use_state(0)
|
|
19
|
+
#
|
|
20
|
+
# div({ className: 'counter' },
|
|
21
|
+
# button({ onClick: set_count.with { |c| c - 1 } }, '-'),
|
|
22
|
+
# span(nil, count),
|
|
23
|
+
# button({ onClick: set_count.with { |c| c + 1 } }, '+')
|
|
24
|
+
# )
|
|
25
|
+
# end
|
|
26
|
+
# end
|
|
27
|
+
# end
|
|
28
|
+
#
|
|
29
|
+
module FunctionalComponent
|
|
30
|
+
# Create a React functional component with hooks support
|
|
31
|
+
#
|
|
32
|
+
# @yield [hooks] Block that receives hooks helper and returns React element
|
|
33
|
+
# @yieldparam hooks [Hooks] Hooks helper object
|
|
34
|
+
# @return [Native] JavaScript function component
|
|
35
|
+
def create_component(&block)
|
|
36
|
+
r = react
|
|
37
|
+
hooks_class = Hooks
|
|
38
|
+
|
|
39
|
+
`(function(React, HooksClass) {
|
|
40
|
+
return function(props) {
|
|
41
|
+
var hooks = HooksClass.$new(React, props);
|
|
42
|
+
var element = #{block.call(`hooks`)};
|
|
43
|
+
return element;
|
|
44
|
+
};
|
|
45
|
+
})(#{r}, #{hooks_class})`
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Hooks helper class - provides React hooks as Ruby methods
|
|
49
|
+
class Hooks
|
|
50
|
+
def initialize(react, props)
|
|
51
|
+
@react = react
|
|
52
|
+
@props = Native(props) if props
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
attr_reader :props
|
|
56
|
+
|
|
57
|
+
# React useState hook
|
|
58
|
+
#
|
|
59
|
+
# @param initial_value [Object] Initial state value
|
|
60
|
+
# @return [Array] [current_value, setter] pair
|
|
61
|
+
#
|
|
62
|
+
# @example
|
|
63
|
+
# count, set_count = hooks.use_state(0)
|
|
64
|
+
# set_count.call(5) # Set to specific value
|
|
65
|
+
# set_count.with { |c| c + 1 } # Functional update
|
|
66
|
+
def use_state(initial_value)
|
|
67
|
+
react = @react
|
|
68
|
+
result = `#{react}.useState(#{initial_value})`
|
|
69
|
+
current = `#{result}[0]`
|
|
70
|
+
setter_fn = `#{result}[1]`
|
|
71
|
+
|
|
72
|
+
setter = StateSetter.new(setter_fn)
|
|
73
|
+
[current, setter]
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# React useEffect hook
|
|
77
|
+
#
|
|
78
|
+
# @param dependencies [Array, nil] Dependency array (nil = run every render, [] = run once)
|
|
79
|
+
# @yield Effect callback
|
|
80
|
+
# @yieldreturn [Proc, nil] Optional cleanup function
|
|
81
|
+
#
|
|
82
|
+
# @example Run on mount only
|
|
83
|
+
# hooks.use_effect([]) do
|
|
84
|
+
# console_log("Mounted!")
|
|
85
|
+
# -> { console_log("Unmounted!") }
|
|
86
|
+
# end
|
|
87
|
+
#
|
|
88
|
+
# @example Run when count changes
|
|
89
|
+
# hooks.use_effect([count]) do
|
|
90
|
+
# console_log("Count changed to", count)
|
|
91
|
+
# end
|
|
92
|
+
def use_effect(dependencies = nil, &block)
|
|
93
|
+
react = @react
|
|
94
|
+
|
|
95
|
+
effect_fn = `function() {
|
|
96
|
+
var result = #{block.call};
|
|
97
|
+
if (result && typeof result.$call === 'function') {
|
|
98
|
+
return function() { result.$call(); };
|
|
99
|
+
}
|
|
100
|
+
return result;
|
|
101
|
+
}`
|
|
102
|
+
|
|
103
|
+
if dependencies.nil?
|
|
104
|
+
`#{react}.useEffect(#{effect_fn})`
|
|
105
|
+
else
|
|
106
|
+
`#{react}.useEffect(#{effect_fn}, #{dependencies.to_a})`
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# React useMemo hook
|
|
111
|
+
#
|
|
112
|
+
# @param dependencies [Array] Dependency array
|
|
113
|
+
# @yield Factory function
|
|
114
|
+
# @return [Object] Memoized value
|
|
115
|
+
#
|
|
116
|
+
# @example
|
|
117
|
+
# expensive_value = hooks.use_memo([input]) do
|
|
118
|
+
# compute_expensive(input)
|
|
119
|
+
# end
|
|
120
|
+
def use_memo(dependencies, &block)
|
|
121
|
+
react = @react
|
|
122
|
+
factory_fn = `function() { return #{block.call}; }`
|
|
123
|
+
`#{react}.useMemo(#{factory_fn}, #{dependencies.to_a})`
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# React useCallback hook
|
|
127
|
+
#
|
|
128
|
+
# @param dependencies [Array] Dependency array
|
|
129
|
+
# @yield Callback function
|
|
130
|
+
# @return [Native] Memoized callback
|
|
131
|
+
#
|
|
132
|
+
# @example
|
|
133
|
+
# handle_click = hooks.use_callback([item_id]) do
|
|
134
|
+
# on_item_click(item_id)
|
|
135
|
+
# end
|
|
136
|
+
def use_callback(dependencies, &block)
|
|
137
|
+
react = @react
|
|
138
|
+
callback_fn = `function() { return #{block.call}; }`
|
|
139
|
+
`#{react}.useCallback(#{callback_fn}, #{dependencies.to_a})`
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# React useRef hook
|
|
143
|
+
#
|
|
144
|
+
# @param initial_value [Object] Initial ref value
|
|
145
|
+
# @return [Native] Ref object with .current property
|
|
146
|
+
#
|
|
147
|
+
# @example
|
|
148
|
+
# input_ref = hooks.use_ref(nil)
|
|
149
|
+
# # In render: input({ ref: input_ref })
|
|
150
|
+
# # Later: input_ref.current.focus()
|
|
151
|
+
def use_ref(initial_value = nil)
|
|
152
|
+
react = @react
|
|
153
|
+
Native(`#{react}.useRef(#{initial_value})`)
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# React useReducer hook
|
|
157
|
+
#
|
|
158
|
+
# @param reducer [Proc] Reducer function (state, action) -> new_state
|
|
159
|
+
# @param initial_state [Object] Initial state
|
|
160
|
+
# @return [Array] [state, dispatch] pair
|
|
161
|
+
#
|
|
162
|
+
# @example
|
|
163
|
+
# reducer = ->(state, action) {
|
|
164
|
+
# case action[:type]
|
|
165
|
+
# when 'increment' then { count: state[:count] + 1 }
|
|
166
|
+
# when 'decrement' then { count: state[:count] - 1 }
|
|
167
|
+
# else state
|
|
168
|
+
# end
|
|
169
|
+
# }
|
|
170
|
+
# state, dispatch = hooks.use_reducer(reducer, { count: 0 })
|
|
171
|
+
# dispatch.call({ type: 'increment' })
|
|
172
|
+
def use_reducer(reducer, initial_state)
|
|
173
|
+
react = @react
|
|
174
|
+
|
|
175
|
+
js_reducer = `function(state, action) {
|
|
176
|
+
return #{reducer.call(`state`, `action`)};
|
|
177
|
+
}`
|
|
178
|
+
|
|
179
|
+
result = `#{react}.useReducer(#{js_reducer}, #{initial_state.to_n})`
|
|
180
|
+
state = Native(`#{result}[0]`)
|
|
181
|
+
dispatch_fn = `#{result}[1]`
|
|
182
|
+
|
|
183
|
+
dispatch = ->(action) { `#{dispatch_fn}(#{action.to_n})` }
|
|
184
|
+
[state, dispatch]
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
# React useContext hook
|
|
188
|
+
#
|
|
189
|
+
# @param context [Native] React context object
|
|
190
|
+
# @return [Object] Current context value
|
|
191
|
+
def use_context(context)
|
|
192
|
+
react = @react
|
|
193
|
+
`#{react}.useContext(#{context})`
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# State setter wrapper - provides functional update support
|
|
198
|
+
class StateSetter
|
|
199
|
+
def initialize(setter_fn)
|
|
200
|
+
@setter_fn = setter_fn
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
# Set state to a specific value
|
|
204
|
+
#
|
|
205
|
+
# @param value [Object] New state value
|
|
206
|
+
def call(value)
|
|
207
|
+
`#{@setter_fn}(#{value})`
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# Functional state update
|
|
211
|
+
# Returns a callback function for use in onClick etc.
|
|
212
|
+
#
|
|
213
|
+
# @yield [current] Block receives current value and returns new value
|
|
214
|
+
# @return [Native] JavaScript callback function
|
|
215
|
+
#
|
|
216
|
+
# @example
|
|
217
|
+
# button({ onClick: set_count.with { |c| c + 1 } }, '+')
|
|
218
|
+
def with(&block)
|
|
219
|
+
setter_fn = @setter_fn
|
|
220
|
+
`function() {
|
|
221
|
+
#{setter_fn}(function(current) {
|
|
222
|
+
return #{block.call(`current`)};
|
|
223
|
+
});
|
|
224
|
+
}`
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
# Create a callback that sets to a specific value
|
|
228
|
+
#
|
|
229
|
+
# @param value [Object] Value to set
|
|
230
|
+
# @return [Native] JavaScript callback function
|
|
231
|
+
#
|
|
232
|
+
# @example
|
|
233
|
+
# button({ onClick: set_count.to(0) }, 'Reset')
|
|
234
|
+
def to(value)
|
|
235
|
+
setter_fn = @setter_fn
|
|
236
|
+
`function() { #{setter_fn}(#{value}); }`
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
# Alias for easy access
|
|
245
|
+
FunctionalComponent = OpalVite::Concerns::V1::FunctionalComponent
|
|
@@ -6,6 +6,7 @@ require 'opal_vite/concerns/v1/storable'
|
|
|
6
6
|
require 'opal_vite/concerns/v1/stimulus_helpers'
|
|
7
7
|
require 'opal_vite/concerns/v1/vue_helpers'
|
|
8
8
|
require 'opal_vite/concerns/v1/react_helpers'
|
|
9
|
+
require 'opal_vite/concerns/v1/functional_component'
|
|
9
10
|
require 'opal_vite/concerns/v1/uri_helpers'
|
|
10
11
|
require 'opal_vite/concerns/v1/base64_helpers'
|
|
11
12
|
require 'opal_vite/concerns/v1/debug_helpers'
|
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.3.
|
|
4
|
+
version: 0.3.11
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- stofu1234
|
|
@@ -124,6 +124,7 @@ files:
|
|
|
124
124
|
- opal/opal_vite/concerns/v1/base64_helpers.rb
|
|
125
125
|
- opal/opal_vite/concerns/v1/debug_helpers.rb
|
|
126
126
|
- opal/opal_vite/concerns/v1/dom_helpers.rb
|
|
127
|
+
- opal/opal_vite/concerns/v1/functional_component.rb
|
|
127
128
|
- opal/opal_vite/concerns/v1/js_proxy_ex.rb
|
|
128
129
|
- opal/opal_vite/concerns/v1/react_helpers.rb
|
|
129
130
|
- opal/opal_vite/concerns/v1/stimulus_helpers.rb
|