quince 0.2.0 → 0.4.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/Gemfile.lock +2 -2
- data/README.md +2 -2
- data/lib/quince/attributes_by_element.rb +9 -3
- data/lib/quince/callback.js.erb +31 -0
- data/lib/quince/callback.rb +48 -0
- data/lib/quince/component.rb +31 -10
- data/lib/quince/html_tag_components.rb +22 -19
- data/lib/quince/serialiser.rb +2 -89
- data/lib/quince/singleton_methods.rb +24 -5
- data/lib/quince/version.rb +1 -1
- data/scripts.js +81 -22
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7821a6a97b508f9ad109b4ae7e59d9ccd774b94b2e451a9809eed7fa941e5cba
|
4
|
+
data.tar.gz: 5d5b6137d4089497408776c24152239fb99ac4977d5c31778cfa9bfa21518ee4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cb40eef0228b05bb3a7a05b7c32ec1b3bf47cd261d25a164f7df018e2dc94a5e720565ca7d2fd58edbb39899a481cbf7aacc50f01c97cd73bd815ca7dc95938b
|
7
|
+
data.tar.gz: a6fb31c6d7835f380670ee3be74952786653bf3a3b5f7712c0ac4dd9526b8e2f9564c127d71fe16c12cebbdccf0cd4df9cbcd8a3d243c327323572baa7ebde4a
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
quince (0.
|
4
|
+
quince (0.4.1)
|
5
5
|
oj (~> 3.13)
|
6
6
|
typed_struct (>= 0.1.4)
|
7
7
|
|
@@ -9,7 +9,7 @@ GEM
|
|
9
9
|
remote: https://rubygems.org/
|
10
10
|
specs:
|
11
11
|
diff-lcs (1.4.4)
|
12
|
-
oj (3.13.
|
12
|
+
oj (3.13.7)
|
13
13
|
rake (13.0.6)
|
14
14
|
rbs (1.6.2)
|
15
15
|
rspec (3.10.0)
|
data/README.md
CHANGED
@@ -92,8 +92,8 @@ class Counter < Quince::Component
|
|
92
92
|
def render
|
93
93
|
div(
|
94
94
|
h2("count is #{state.val}"),
|
95
|
-
button(onclick:
|
96
|
-
button(onclick:
|
95
|
+
button(onclick: callback(:increment)) { "++" },
|
96
|
+
button(onclick: callback(:decrement)) { "--" }
|
97
97
|
)
|
98
98
|
end
|
99
99
|
end
|
@@ -11,7 +11,7 @@ module Quince
|
|
11
11
|
t = Quince::Types
|
12
12
|
opt_string_sym = Rbs("#{t::OptionalString} | Symbol")
|
13
13
|
opt_bool = t::OptionalBoolean
|
14
|
-
|
14
|
+
opt_callback = Rbs("Quince::Callback::Interface | Quince::Types::Undefined")
|
15
15
|
value = opt_string_sym # for now
|
16
16
|
|
17
17
|
ATTRIBUTES_BY_ELEMENT = {
|
@@ -468,8 +468,14 @@ module Quince
|
|
468
468
|
}.freeze
|
469
469
|
|
470
470
|
DOM_EVENTS = {
|
471
|
-
onclick:
|
472
|
-
onsubmit:
|
471
|
+
onclick: opt_callback,
|
472
|
+
onsubmit: opt_callback,
|
473
|
+
onblur: opt_callback,
|
474
|
+
onchange: opt_callback,
|
475
|
+
onsearch: opt_callback,
|
476
|
+
onkeyup: opt_callback,
|
477
|
+
onselect: opt_callback,
|
478
|
+
onscroll: opt_callback,
|
473
479
|
}.freeze
|
474
480
|
end
|
475
481
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
const p = this.dataset[`quOn<%= key.to_s[2..-1] %>State`];
|
2
|
+
<% if value.debugger %>debugger;<% end %>
|
3
|
+
<% if value.if %>
|
4
|
+
if (<%= value.if %>) {
|
5
|
+
<% end %>
|
6
|
+
<% if value.debounce_ms %>
|
7
|
+
if (!window[`<%= fn_name %>`]) window[`<%= fn_name %>`] = Q.d((p) => {
|
8
|
+
<% end %>
|
9
|
+
Q.c(
|
10
|
+
`<%= endpoint %>`,
|
11
|
+
JSON.stringify(
|
12
|
+
{component: p, event: `<%= key.to_s[2..-1] %>`,stateContainer: `<%= state_container %>`,
|
13
|
+
<% if value.take_form_values %>
|
14
|
+
params: Q.f(this),
|
15
|
+
<% end %>
|
16
|
+
<% if rerender %>
|
17
|
+
rerender: <%= rerender.to_json %>,
|
18
|
+
<% end %>}),
|
19
|
+
`<%= rerender&.dig(:selector)&.to_s || selector %>`,
|
20
|
+
<% if mode = rerender&.dig(:mode) %>`<%= mode.to_s %>`<% end %>
|
21
|
+
);
|
22
|
+
<% unless push_params_state == "null" %>Q.ps(<%= push_params_state %>);<% end %>
|
23
|
+
<% if value.debounce_ms&.positive? %>
|
24
|
+
}, <%= value.debounce_ms %>); window[`<%= fn_name %>`](p)
|
25
|
+
<% end %>
|
26
|
+
<% if value.if %>
|
27
|
+
};
|
28
|
+
<% end %>
|
29
|
+
<% if value.prevent_default %>
|
30
|
+
;return false;
|
31
|
+
<% end %>
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Quince
|
2
|
+
class Callback
|
3
|
+
module ComponentHelpers
|
4
|
+
protected
|
5
|
+
|
6
|
+
DEFAULT_CALLBACK_OPTIONS = {
|
7
|
+
prevent_default: false,
|
8
|
+
take_form_values: false,
|
9
|
+
debounce_ms: nil,
|
10
|
+
if: nil,
|
11
|
+
debugger: false,
|
12
|
+
rerender: nil,
|
13
|
+
push_params_state: nil,
|
14
|
+
}.freeze
|
15
|
+
|
16
|
+
def callback(method_name, **opts)
|
17
|
+
unless self.class.instance_variable_get(:@exposed_actions).member?(method_name)
|
18
|
+
raise "The action you called is not exposed"
|
19
|
+
end
|
20
|
+
|
21
|
+
opts = DEFAULT_CALLBACK_OPTIONS.merge opts
|
22
|
+
|
23
|
+
Callback.new(self, method_name, **opts)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
module Interface
|
28
|
+
attr_reader(
|
29
|
+
:receiver,
|
30
|
+
:method_name,
|
31
|
+
*ComponentHelpers::DEFAULT_CALLBACK_OPTIONS.keys,
|
32
|
+
)
|
33
|
+
end
|
34
|
+
|
35
|
+
include Interface
|
36
|
+
|
37
|
+
def initialize(
|
38
|
+
receiver,
|
39
|
+
method_name,
|
40
|
+
**opts
|
41
|
+
)
|
42
|
+
@receiver, @method_name = receiver, method_name
|
43
|
+
ComponentHelpers::DEFAULT_CALLBACK_OPTIONS.each_key do |opt|
|
44
|
+
instance_variable_set :"@#{opt}", opts.fetch(opt)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/quince/component.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require_relative "callback"
|
2
|
+
|
1
3
|
module Quince
|
2
4
|
class Component
|
3
5
|
class << self
|
@@ -8,7 +10,8 @@ module Quince
|
|
8
10
|
def Props(**kw)
|
9
11
|
self.const_set "Props", TypedStruct.new(
|
10
12
|
{ default: Quince::Types::Undefined },
|
11
|
-
Quince::Component::
|
13
|
+
Quince::Component::PARENT_SELECTOR_ATTR => String,
|
14
|
+
Quince::Component::SELF_SELECTOR => String,
|
12
15
|
**kw,
|
13
16
|
)
|
14
17
|
end
|
@@ -21,16 +24,24 @@ module Quince
|
|
21
24
|
self.const_set "State", st
|
22
25
|
end
|
23
26
|
|
24
|
-
def exposed(action,
|
27
|
+
def exposed(action, method: :POST)
|
25
28
|
@exposed_actions ||= Set.new
|
26
29
|
@exposed_actions.add action
|
27
30
|
route = "/api/#{self.name}/#{action}"
|
28
31
|
Quince.middleware.create_route_handler(
|
29
|
-
verb:
|
32
|
+
verb: method,
|
30
33
|
route: route,
|
31
34
|
) do |params|
|
32
|
-
instance = Quince::Serialiser.deserialise
|
35
|
+
instance = Quince::Serialiser.deserialise(CGI.unescapeHTML(params[:component]))
|
33
36
|
Quince::Component.class_variable_set :@@params, params
|
37
|
+
render_with = if params[:rerender]
|
38
|
+
instance.instance_variable_set :@state_container, params[:stateContainer]
|
39
|
+
params[:rerender][:method].to_sym
|
40
|
+
else
|
41
|
+
:render
|
42
|
+
end
|
43
|
+
instance.instance_variable_set :@render_with, render_with
|
44
|
+
instance.instance_variable_set :@callback_event, params[:event]
|
34
45
|
if @exposed_actions.member? action
|
35
46
|
instance.send action
|
36
47
|
instance
|
@@ -47,7 +58,8 @@ module Quince
|
|
47
58
|
id = SecureRandom.alphanumeric 6
|
48
59
|
instance.instance_variable_set :@__id, id
|
49
60
|
instance.instance_variable_set :@props, initialize_props(self, id, **props)
|
50
|
-
|
61
|
+
kids = block_children ? block_children.call : children
|
62
|
+
instance.instance_variable_set(:@children, kids)
|
51
63
|
instance.send :initialize
|
52
64
|
end
|
53
65
|
end
|
@@ -55,10 +67,14 @@ module Quince
|
|
55
67
|
private
|
56
68
|
|
57
69
|
def initialize_props(const, id, **props)
|
58
|
-
|
70
|
+
if const.const_defined?("Props")
|
71
|
+
const::Props.new(PARENT_SELECTOR_ATTR => id, **props, SELF_SELECTOR => id)
|
72
|
+
end
|
59
73
|
end
|
60
74
|
end
|
61
75
|
|
76
|
+
include Callback::ComponentHelpers
|
77
|
+
|
62
78
|
# set default
|
63
79
|
@@params = {}
|
64
80
|
|
@@ -71,7 +87,7 @@ module Quince
|
|
71
87
|
protected
|
72
88
|
|
73
89
|
def to(route, via: :POST)
|
74
|
-
self.class.exposed route,
|
90
|
+
self.class.exposed route, method: via
|
75
91
|
end
|
76
92
|
|
77
93
|
def params
|
@@ -82,10 +98,15 @@ module Quince
|
|
82
98
|
|
83
99
|
attr_reader :__id
|
84
100
|
|
85
|
-
|
101
|
+
PARENT_SELECTOR_ATTR = :"data-quid-parent"
|
102
|
+
SELF_SELECTOR = :"data-quid"
|
103
|
+
|
104
|
+
def html_parent_selector
|
105
|
+
"[#{PARENT_SELECTOR_ATTR}='#{__id}']".freeze
|
106
|
+
end
|
86
107
|
|
87
|
-
def
|
88
|
-
"[#{
|
108
|
+
def html_self_selector
|
109
|
+
"[#{SELF_SELECTOR}='#{props[SELF_SELECTOR]}']".freeze
|
89
110
|
end
|
90
111
|
end
|
91
112
|
end
|
@@ -1,9 +1,13 @@
|
|
1
1
|
require "oj"
|
2
2
|
require_relative "attributes_by_element"
|
3
3
|
require_relative "serialiser"
|
4
|
+
require "erb"
|
4
5
|
|
5
6
|
module Quince
|
6
7
|
module HtmlTagComponents
|
8
|
+
CALLBACK_TEMPLATE = File.read(File.join(__dir__, "callback.js.erb")).delete!("\n").freeze
|
9
|
+
CALLBACK_ERB_INSTANCE = ERB.new(CALLBACK_TEMPLATE)
|
10
|
+
|
7
11
|
def self.define_html_tag_component(const_name, attrs, self_closing: false)
|
8
12
|
klass = Class.new(Quince::Component) do
|
9
13
|
Props(
|
@@ -17,9 +21,12 @@ module Quince
|
|
17
21
|
props.each_pair.map { |k, v| to_html_attr(k, v) }.compact.join(" ")
|
18
22
|
end
|
19
23
|
result = "<#{tag_name}"
|
20
|
-
|
21
|
-
|
22
|
-
|
24
|
+
if !attrs.empty?
|
25
|
+
result << " #{attrs}>"
|
26
|
+
return result if self_closing?
|
27
|
+
elsif attrs.empty? && self_closing?
|
28
|
+
return result << ">"
|
29
|
+
end
|
23
30
|
|
24
31
|
result << Quince.to_html(children)
|
25
32
|
result << "</#{tag_name}>"
|
@@ -33,27 +40,23 @@ module Quince
|
|
33
40
|
attrib = case value
|
34
41
|
when String, Integer, Float, Symbol
|
35
42
|
value.to_s
|
36
|
-
when
|
37
|
-
owner = value.owner
|
43
|
+
when Callback::Interface
|
38
44
|
receiver = value.receiver
|
39
|
-
|
40
|
-
|
45
|
+
owner = receiver.class.name
|
46
|
+
name = value.method_name
|
47
|
+
endpoint = "/api/#{owner}/#{name}"
|
48
|
+
selector = receiver.send :html_parent_selector
|
41
49
|
internal = Quince::Serialiser.serialise receiver
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
when :onsubmit
|
49
|
-
CGI.escape_html(
|
50
|
-
"const p = #{payload}; callRemoteEndpoint( `/api/#{owner}/#{name}`, JSON.stringify({...p, params: getFormValues(this)}), `#{selector}`); return false"
|
51
|
-
)
|
52
|
-
end
|
50
|
+
fn_name = "_Q_#{key}_#{receiver.send(:__id)}"
|
51
|
+
rerender = value.rerender
|
52
|
+
state_container = html_self_selector
|
53
|
+
push_params_state = value.push_params_state.to_json
|
54
|
+
code = CALLBACK_ERB_INSTANCE.result(binding)
|
55
|
+
return %Q{#{key}="#{CGI.escape_html(code)}" data-qu-#{key}-state="#{CGI.escapeHTML(internal)}"}
|
53
56
|
when true
|
54
57
|
return key
|
55
58
|
when false, nil, Quince::Types::Undefined
|
56
|
-
return
|
59
|
+
return nil
|
57
60
|
else
|
58
61
|
raise "prop type not yet implemented #{value}"
|
59
62
|
end
|
data/lib/quince/serialiser.rb
CHANGED
@@ -2,98 +2,11 @@ module Quince
|
|
2
2
|
class Serialiser
|
3
3
|
class << self
|
4
4
|
def serialise(obj)
|
5
|
-
|
6
|
-
when Quince::Component
|
7
|
-
{
|
8
|
-
id: serialise(obj.send(:__id)),
|
9
|
-
props: serialise(obj.props),
|
10
|
-
state: serialise(obj.state),
|
11
|
-
children: serialise(obj.children),
|
12
|
-
html_element_selector: serialise(obj.send(:html_element_selector)),
|
13
|
-
}
|
14
|
-
when Array
|
15
|
-
obj.map { |e| serialise e }
|
16
|
-
when TypedStruct, Struct, OpenStruct, Hash
|
17
|
-
result = obj.each_pair.each_with_object({}) do |(k, v), ob|
|
18
|
-
case v
|
19
|
-
when Undefined
|
20
|
-
next
|
21
|
-
else
|
22
|
-
ob[k] = serialise(v)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
if result.empty? && !obj.is_a?(Hash)
|
26
|
-
obj = nil
|
27
|
-
nil
|
28
|
-
else
|
29
|
-
result
|
30
|
-
end
|
31
|
-
when Proc
|
32
|
-
obj = obj.call # is there a more efficient way of doing this?
|
33
|
-
serialise(obj)
|
34
|
-
when String
|
35
|
-
res = obj.gsub "\n", '\n'
|
36
|
-
res.gsub! ?", '\"'
|
37
|
-
res
|
38
|
-
else
|
39
|
-
obj
|
40
|
-
end
|
41
|
-
|
42
|
-
{ t: obj.class&.name, v: val }
|
5
|
+
Oj.dump(obj)
|
43
6
|
end
|
44
7
|
|
45
8
|
def deserialise(json)
|
46
|
-
|
47
|
-
when "String", "Integer", "Float", "NilClass", "TrueClass", "FalseClass"
|
48
|
-
json[:v]
|
49
|
-
when "Symbol"
|
50
|
-
json[:v].to_sym
|
51
|
-
when "Array"
|
52
|
-
json[:v].map { |e| deserialise e }
|
53
|
-
when "Hash"
|
54
|
-
transform_hash json[:v]
|
55
|
-
when "OpenStruct"
|
56
|
-
OpenStruct.new(**transform_hash(props))
|
57
|
-
when nil
|
58
|
-
nil
|
59
|
-
else
|
60
|
-
klass = Object.const_get(json[:t])
|
61
|
-
if klass < TypedStruct
|
62
|
-
transform_hash_for_struct(json[:v]) || {}
|
63
|
-
elsif klass < Quince::Component
|
64
|
-
instance = klass.allocate
|
65
|
-
val = json[:v]
|
66
|
-
id = deserialise val[:id]
|
67
|
-
|
68
|
-
instance.instance_variable_set :@__id, id
|
69
|
-
instance.instance_variable_set(
|
70
|
-
:@props,
|
71
|
-
klass.send(
|
72
|
-
:initialize_props,
|
73
|
-
klass,
|
74
|
-
id,
|
75
|
-
**(deserialise(val[:props]) || {}),
|
76
|
-
),
|
77
|
-
)
|
78
|
-
st = deserialise(val[:state])
|
79
|
-
instance.instance_variable_set :@state, klass::State.new(**st) if st
|
80
|
-
instance.instance_variable_set :@children, deserialise(val[:children])
|
81
|
-
instance
|
82
|
-
else
|
83
|
-
klass = Object.const_get(json[:t])
|
84
|
-
klass.new(deserialise(json[:v]))
|
85
|
-
end
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
private
|
90
|
-
|
91
|
-
def transform_hash(hsh)
|
92
|
-
hsh.transform_values! { |v| deserialise v }
|
93
|
-
end
|
94
|
-
|
95
|
-
def transform_hash_for_struct(hsh)
|
96
|
-
hsh.to_h { |k, v| [k.to_sym, deserialise(v)] }
|
9
|
+
Oj.load(json)
|
97
10
|
end
|
98
11
|
end
|
99
12
|
end
|
@@ -16,6 +16,7 @@ module Quince
|
|
16
16
|
) do |params|
|
17
17
|
component = component.create if component.instance_of? Class
|
18
18
|
Quince::Component.class_variable_set :@@params, params
|
19
|
+
component.instance_variable_set :@render_with, :render
|
19
20
|
component
|
20
21
|
end
|
21
22
|
end
|
@@ -25,7 +26,7 @@ module Quince
|
|
25
26
|
def define_constructor(const, constructor_name = const.to_s)
|
26
27
|
HtmlTagComponents.instance_eval do
|
27
28
|
define_method(constructor_name) do |*children, **props, &block_children|
|
28
|
-
new_props = { **props, Quince::Component::
|
29
|
+
new_props = { **props, Quince::Component::PARENT_SELECTOR_ATTR => __id }
|
29
30
|
const.create(*children, **new_props, &block_children)
|
30
31
|
end
|
31
32
|
end
|
@@ -44,12 +45,30 @@ module Quince
|
|
44
45
|
output = to_html(output.call)
|
45
46
|
when NilClass
|
46
47
|
output = ""
|
47
|
-
|
48
|
+
when Component
|
48
49
|
tmp = output
|
49
|
-
|
50
|
-
|
51
|
-
|
50
|
+
render_with = output.instance_variable_get(:@render_with) || :render
|
51
|
+
output = output.send render_with
|
52
|
+
case render_with
|
53
|
+
when :render
|
54
|
+
if output.is_a?(Array)
|
55
|
+
raise "#render in #{tmp.class} should not return multiple elements. Consider wrapping it in a div"
|
56
|
+
end
|
57
|
+
else
|
58
|
+
internal = Quince::Serialiser.serialise tmp
|
59
|
+
updated_state = CGI.escapeHTML(internal).to_json
|
60
|
+
selector = tmp.instance_variable_get :@state_container
|
61
|
+
event = tmp.instance_variable_get :@callback_event
|
62
|
+
|
63
|
+
scr = to_html(HtmlTagComponents::Script.create(<<~JS, type: "text/javascript"))
|
64
|
+
var stateContainer = document.querySelector(`#{selector}`);
|
65
|
+
stateContainer.dataset.quOn#{event}State = #{updated_state};
|
66
|
+
JS
|
67
|
+
|
68
|
+
output += (output.is_a?(String) ? scr : [scr])
|
52
69
|
end
|
70
|
+
else
|
71
|
+
raise "don't know how to render #{output.class} (#{output.inspect})"
|
53
72
|
end
|
54
73
|
end
|
55
74
|
|
data/lib/quince/version.rb
CHANGED
data/scripts.js
CHANGED
@@ -1,24 +1,83 @@
|
|
1
|
-
const
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
1
|
+
const Q = {
|
2
|
+
c: (endpoint, payload, selector, mode = "replace") => {
|
3
|
+
return fetch(
|
4
|
+
endpoint,
|
5
|
+
{
|
6
|
+
method: `POST`,
|
7
|
+
headers: {
|
8
|
+
"Content-Type": `application/json;charset=utf-8`
|
9
|
+
},
|
10
|
+
body: payload,
|
11
|
+
}
|
12
|
+
).then(resp => resp.text()).then(html => {
|
13
|
+
const element = document.querySelector(selector);
|
14
|
+
if (!element) {
|
15
|
+
throw `element not found for ${selector}`;
|
16
|
+
}
|
16
17
|
|
17
|
-
|
18
|
-
|
19
|
-
|
18
|
+
switch (mode) {
|
19
|
+
case "append_diff":
|
20
|
+
const tmpElem = document.createElement(element.nodeName);
|
21
|
+
tmpElem.innerHTML = html;
|
22
|
+
const newNodes = Array.from(tmpElem.childNodes);
|
23
|
+
const script = newNodes.pop();
|
24
|
+
const existingChildren = element.childNodes;
|
25
|
+
// This comparison doesn't currently work because of each node's unique id (data-quid).
|
26
|
+
// maybe it would be possible to use regex replace to on the raw html, but it could also
|
27
|
+
// be overkill
|
28
|
+
// let c = 0;
|
29
|
+
// for (; c < existingChildren.length; c++) {
|
30
|
+
// if (existingChildren[c].isEqualNode(newNodes[c]))
|
31
|
+
// continue;
|
32
|
+
// else
|
33
|
+
// break;
|
34
|
+
// }
|
35
|
+
// for the time being, we can just assume that we can just take the extra items
|
36
|
+
let c = existingChildren.length;
|
37
|
+
for (const node of newNodes.slice(c)) {
|
38
|
+
element.appendChild(node);
|
39
|
+
}
|
40
|
+
|
41
|
+
const newScript = document.createElement("script");
|
42
|
+
newScript.dataset.quid = script.dataset.quid;
|
43
|
+
newScript.innerHTML = script.innerHTML;
|
44
|
+
document.head.appendChild(newScript);
|
45
|
+
break;
|
46
|
+
case "replace":
|
47
|
+
element.outerHTML = html;
|
48
|
+
break;
|
49
|
+
default:
|
50
|
+
throw `mode ${mode} is not valid`;
|
51
|
+
}
|
52
|
+
})
|
53
|
+
},
|
54
|
+
f: (elem) => {
|
55
|
+
let form = elem.localName === "form" ? elem : elem.form;
|
56
|
+
if (!form) {
|
57
|
+
throw `element ${elem} should belong to a form`;
|
58
|
+
}
|
59
|
+
const fd = new FormData(form);
|
60
|
+
return Object.fromEntries(fd.entries());
|
61
|
+
},
|
62
|
+
d: (func, wait_ms) => {
|
63
|
+
let timer = null;
|
20
64
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
65
|
+
return (...args) => {
|
66
|
+
clearTimeout(timer);
|
67
|
+
return new Promise((resolve) => {
|
68
|
+
timer = setTimeout(
|
69
|
+
() => resolve(func(...args)),
|
70
|
+
wait_ms,
|
71
|
+
);
|
72
|
+
});
|
73
|
+
};
|
74
|
+
},
|
75
|
+
ps: (stateObj) => {
|
76
|
+
const base = location.origin + location.pathname;
|
77
|
+
const url = new URL(base);
|
78
|
+
for (const p in stateObj) {
|
79
|
+
url.searchParams.append(p, stateObj[p]);
|
80
|
+
};
|
81
|
+
window.history.pushState({}, document.title, url);
|
82
|
+
}
|
83
|
+
};
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: quince
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joseph Johansen
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-09-
|
11
|
+
date: 2021-09-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: typed_struct
|
@@ -57,6 +57,8 @@ files:
|
|
57
57
|
- bin/setup
|
58
58
|
- lib/quince.rb
|
59
59
|
- lib/quince/attributes_by_element.rb
|
60
|
+
- lib/quince/callback.js.erb
|
61
|
+
- lib/quince/callback.rb
|
60
62
|
- lib/quince/component.rb
|
61
63
|
- lib/quince/html_tag_components.rb
|
62
64
|
- lib/quince/serialiser.rb
|