reflex_behaviors 0.0.9 → 0.0.10
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 +4 -4
- data/README.md +23 -19
- data/app/assets/builds/reflex_behaviors.js +35 -13
- data/app/assets/builds/reflex_behaviors.js.map +3 -3
- data/app/helpers/reflex_behaviors/application_helper.rb +1 -19
- data/app/javascript/devtools/elements/tooltip_element.js +16 -4
- data/app/javascript/devtools/toggle.js +67 -51
- data/app/javascript/elements/reflex_element.js +3 -3
- data/app/javascript/elements/toggle_target_element.js +81 -1
- data/app/javascript/elements/toggle_trigger_element.js +72 -51
- data/app/reflexes/reflex_behaviors/application_reflex.rb +16 -9
- data/app/reflexes/reflex_behaviors/toggle_reflex.rb +3 -3
- data/lib/reflex_behaviors/engine.rb +1 -0
- data/lib/reflex_behaviors/tag_builders/base_tag_builder.rb +15 -0
- data/lib/reflex_behaviors/tag_builders/toggle_tags_builder.rb +57 -20
- data/lib/reflex_behaviors/version.rb +1 -1
- data/package.json +1 -1
- data/tags +854 -809
- data/yarn.lock +140 -140
- metadata +2 -2
@@ -1,12 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
require_relative "../../../lib/reflex_behaviors/tag_builders"
|
4
4
|
|
5
5
|
module ReflexBehaviors::ApplicationHelper
|
6
|
-
def idomatic_partial_path(partial_path)
|
7
|
-
partial_path.to_s.gsub("/_", "/").split(".").first
|
8
|
-
end
|
9
|
-
|
10
6
|
def current_partial_path
|
11
7
|
path = nil
|
12
8
|
prefix = "app/views/"
|
@@ -20,15 +16,6 @@ module ReflexBehaviors::ApplicationHelper
|
|
20
16
|
path[(path.index(prefix) + prefix.length), path.rindex("/")]
|
21
17
|
end
|
22
18
|
|
23
|
-
def reflex_render(**kwargs)
|
24
|
-
kwargs[:partial] = idomatic_partial_path(kwargs[:partial])
|
25
|
-
kwargs[:assigns] ||= {}
|
26
|
-
kwargs[:assigns].each { |key, val| kwargs[:assigns][key] = transportable_value(val) }
|
27
|
-
kwargs[:locals] ||= {}
|
28
|
-
kwargs[:locals].each { |key, val| kwargs[:locals][key] = transportable_value(val) }
|
29
|
-
kwargs.to_json
|
30
|
-
end
|
31
|
-
|
32
19
|
def method_missing(name, ...)
|
33
20
|
prefixes = %w[toggle_]
|
34
21
|
prefixes.each do |prefix|
|
@@ -52,9 +39,4 @@ module ReflexBehaviors::ApplicationHelper
|
|
52
39
|
def toggle_tag_builder
|
53
40
|
@toggle_tag_builder ||= ReflexBehaviors::TagBuilders::ToggleTagsBuilder.new(self)
|
54
41
|
end
|
55
|
-
|
56
|
-
def transportable_value(value)
|
57
|
-
return value.to_s unless value.respond_to?(:to_sgid_param)
|
58
|
-
value.try(:persisted?) ? value.to_sgid_param : nil
|
59
|
-
end
|
60
42
|
end
|
@@ -22,6 +22,7 @@ export default class TooltipElement extends HTMLElement {
|
|
22
22
|
<style>${this.stylesheet}</style>
|
23
23
|
<div>
|
24
24
|
<slot name="title"></slot>
|
25
|
+
<slot name="subtitle"></slot>
|
25
26
|
<slot name="content-top"></slot>
|
26
27
|
<slot name="content"></slot>
|
27
28
|
<slot name="content-bottom"></slot>
|
@@ -52,18 +53,29 @@ export default class TooltipElement extends HTMLElement {
|
|
52
53
|
opacity: 0.9;
|
53
54
|
outline-offset: 1px;
|
54
55
|
outline: dashed 3px ${this.color};
|
55
|
-
padding:
|
56
|
+
padding: 12px;
|
56
57
|
position: relative;
|
57
58
|
white-space: nowrap;
|
58
59
|
}
|
59
60
|
|
60
61
|
slot[name="title"] {
|
61
|
-
border-bottom: dotted 1px ${this.color};
|
62
62
|
color: ${this.color};
|
63
|
-
display:
|
63
|
+
display: block;
|
64
64
|
font-weight: bold;
|
65
|
+
width: 100%;
|
66
|
+
}
|
67
|
+
|
68
|
+
slot[name="subtitle"] {
|
69
|
+
border-bottom: dotted 1px ${this.color};
|
70
|
+
border-top: dotted 1px ${this.color};
|
71
|
+
color: ${this.color};
|
72
|
+
display: block;
|
73
|
+
font-size: 0.8rem;
|
74
|
+
font-weight: lighter;
|
65
75
|
margin-bottom: 8px;
|
66
|
-
|
76
|
+
margin-top: 4px;
|
77
|
+
padding-bottom: 4px;
|
78
|
+
padding-top: 4px;
|
67
79
|
width: 100%;
|
68
80
|
}
|
69
81
|
|
@@ -12,29 +12,31 @@ document.addEventListener('reflex-behaviors:devtools-start', () =>
|
|
12
12
|
supervisor.register('toggle', 'toggles<small>(trigger/target)</small>')
|
13
13
|
)
|
14
14
|
|
15
|
-
function appendTooltip (title, content, options = {}) {
|
15
|
+
function appendTooltip (title, subtitle, content, options = {}) {
|
16
16
|
let { backgroundColor, color, position } = options
|
17
17
|
color = color || 'white'
|
18
18
|
position = position || 'top'
|
19
19
|
return appendHTML(`
|
20
20
|
<reflex-behaviors-devools-tooltip position="${position}" background-color="${backgroundColor}" color="${color}">
|
21
21
|
<div slot='title'>${title}</div>
|
22
|
+
<div slot='subtitle'>${subtitle}</div>
|
22
23
|
${content}
|
23
24
|
</reflex-behaviors-devools-tooltip>
|
24
25
|
`)
|
25
26
|
}
|
26
27
|
|
27
28
|
export default class ToggleDevtool {
|
28
|
-
constructor (
|
29
|
+
constructor (triggerElement) {
|
29
30
|
this.name = 'toggle'
|
30
|
-
this.reflex =
|
31
|
-
this.
|
32
|
-
this.
|
31
|
+
this.reflex = triggerElement.dataset.turboReflex
|
32
|
+
this.triggerElement = triggerElement // SEE: app/javascript/elements/toggle_trigger_element.js
|
33
|
+
this.targetElement = triggerElement.targetElement // SEE: app/javascript/elements/toggle_target_element.js
|
34
|
+
this.morphElement = triggerElement.morphElement
|
33
35
|
|
34
36
|
document.addEventListener('reflex-behaviors:devtool-enable', event => {
|
35
37
|
const { name } = event.detail
|
36
38
|
if (name === this.name) {
|
37
|
-
addHighlight(this.
|
39
|
+
addHighlight(this.triggerElement, {
|
38
40
|
outline: '3px dashed blueviolet',
|
39
41
|
outlineOffset: '2px'
|
40
42
|
})
|
@@ -43,7 +45,7 @@ export default class ToggleDevtool {
|
|
43
45
|
|
44
46
|
document.addEventListener('reflex-behaviors:devtool-disable', event => {
|
45
47
|
const { name } = event.detail
|
46
|
-
if (name === this.name) removeHighlight(this.
|
48
|
+
if (name === this.name) removeHighlight(this.triggerElement)
|
47
49
|
})
|
48
50
|
|
49
51
|
let hideTimeout
|
@@ -73,45 +75,47 @@ export default class ToggleDevtool {
|
|
73
75
|
activeToggle = this
|
74
76
|
this.hide()
|
75
77
|
|
76
|
-
addHighlight(this.
|
78
|
+
addHighlight(this.targetElement, {
|
77
79
|
outline: '3px dashed darkcyan',
|
78
80
|
outlineOffset: '-2px'
|
79
81
|
})
|
80
82
|
|
81
|
-
addHighlight(this.
|
83
|
+
addHighlight(this.triggerElement.morphElement, {
|
82
84
|
outline: '3px dashed chocolate',
|
83
85
|
outlineOffset: '3px'
|
84
86
|
})
|
85
87
|
|
86
|
-
const
|
88
|
+
const morphTooltip = this.createMorphTooltip()
|
87
89
|
const targetTooltip = this.createTargetTooltip()
|
88
|
-
this.createTriggerTooltip(targetTooltip,
|
90
|
+
this.createTriggerTooltip(targetTooltip, morphTooltip)
|
89
91
|
|
90
92
|
document
|
91
93
|
.querySelectorAll('.leader-line')
|
92
94
|
.forEach(el => (el.style.zIndex = 100000))
|
93
95
|
|
94
96
|
const data = {
|
95
|
-
|
96
|
-
partial: this.
|
97
|
-
id: this.
|
98
|
-
status: this.
|
97
|
+
morph: {
|
98
|
+
partial: this.triggerElement.renders,
|
99
|
+
id: this.triggerElement.morphs,
|
100
|
+
status: this.morphElement ? 'OK' : 'Not Found'
|
99
101
|
},
|
100
102
|
trigger: { partial: null, id: null, status: 'Not Found' },
|
101
103
|
target: { partial: null, id: null, status: 'Not Found' }
|
102
104
|
}
|
103
105
|
|
104
|
-
if (this.
|
106
|
+
if (this.triggerElement) {
|
105
107
|
data.trigger = {
|
106
|
-
partial: this.
|
107
|
-
id: this.
|
108
|
+
partial: this.triggerElement.partial,
|
109
|
+
id: this.triggerElement.id,
|
108
110
|
status: 'OK'
|
109
111
|
}
|
112
|
+
data.target.id = this.triggerElement.controls
|
113
|
+
}
|
110
114
|
|
111
|
-
if (this.
|
115
|
+
if (this.targetElement)
|
112
116
|
data.target = {
|
113
|
-
partial: this.
|
114
|
-
id: this.
|
117
|
+
partial: this.targetElement.partial,
|
118
|
+
id: this.targetElement.id,
|
115
119
|
status: 'OK'
|
116
120
|
}
|
117
121
|
|
@@ -133,21 +137,24 @@ export default class ToggleDevtool {
|
|
133
137
|
if (clearActiveToggle) activeToggle = null
|
134
138
|
}
|
135
139
|
|
136
|
-
|
137
|
-
if (!this.
|
140
|
+
createMorphTooltip () {
|
141
|
+
if (!this.triggerElement.morphs)
|
138
142
|
return console.debug(
|
139
|
-
`Unable to create the
|
143
|
+
`Unable to create the morph tooltip! No element matches the DOM id: '${this.triggerElement.morphs}'`
|
140
144
|
)
|
141
145
|
|
142
|
-
const title =
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
+
const title = 'PARTIAL'
|
147
|
+
const subtitle = `
|
148
|
+
id: ${this.triggerElement.morphs || 'unknown'}<br>
|
149
|
+
partial: ${this.triggerElement.renders || 'unknown'}
|
150
|
+
`
|
151
|
+
const content = '<div slot="content"></div>'
|
152
|
+
const tooltip = appendTooltip(title, subtitle, content, {
|
146
153
|
backgroundColor: 'lightyellow',
|
147
154
|
color: 'chocolate'
|
148
155
|
})
|
149
156
|
|
150
|
-
const coords = coordinates(this.
|
157
|
+
const coords = coordinates(this.morphElement)
|
151
158
|
const top = Math.ceil(
|
152
159
|
coords.top + coords.height / 2 - tooltip.offsetHeight / 2
|
153
160
|
)
|
@@ -155,7 +162,7 @@ export default class ToggleDevtool {
|
|
155
162
|
tooltip.style.top = `${top}px`
|
156
163
|
tooltip.style.left = `${left}px`
|
157
164
|
|
158
|
-
tooltip.line = new LeaderLine(tooltip, this.
|
165
|
+
tooltip.line = new LeaderLine(tooltip, this.morphElement, {
|
159
166
|
...this.leaderLineOptions,
|
160
167
|
color: 'chocolate'
|
161
168
|
})
|
@@ -165,34 +172,38 @@ export default class ToggleDevtool {
|
|
165
172
|
}
|
166
173
|
|
167
174
|
createTargetTooltip () {
|
168
|
-
if (!this.
|
175
|
+
if (!this.targetElement)
|
169
176
|
return console.debug(
|
170
|
-
`Unable to create the target tooltip! No element matches the DOM id: '${this.
|
177
|
+
`Unable to create the target tooltip! No element matches the DOM id: '${this.triggerElement.controls}'`
|
171
178
|
)
|
172
179
|
|
173
|
-
const title =
|
174
|
-
const
|
180
|
+
const title = 'TARGET'
|
181
|
+
const subtitle = `
|
182
|
+
id: ${this.targetElement.id}<br>
|
183
|
+
labeled by: ${this.targetElement.labeledBy}
|
184
|
+
`
|
185
|
+
const content = this.targetElement.viewStack
|
175
186
|
.reverse()
|
176
187
|
.map((view, index) => {
|
177
|
-
return this.
|
188
|
+
return this.triggerElement.sharedViews.includes(view)
|
178
189
|
? `<div slot="content-top">${index + 1}. ${view}</div>`
|
179
190
|
: `<div slot="content-bottom">${index + 1}. ${view}</div>`
|
180
191
|
}, this)
|
181
192
|
.join('')
|
182
193
|
|
183
|
-
const tooltip = appendTooltip(title, content, {
|
194
|
+
const tooltip = appendTooltip(title, subtitle, content, {
|
184
195
|
backgroundColor: 'lightcyan',
|
185
196
|
color: 'darkcyan',
|
186
197
|
position: 'bottom'
|
187
198
|
})
|
188
199
|
|
189
|
-
const coords = coordinates(this.
|
200
|
+
const coords = coordinates(this.targetElement)
|
190
201
|
const top = Math.ceil(coords.top + tooltip.offsetHeight)
|
191
202
|
const left = Math.ceil(coords.left + coords.width + tooltip.offsetWidth / 3)
|
192
203
|
tooltip.style.top = `${top}px`
|
193
204
|
tooltip.style.left = `${left}px`
|
194
205
|
|
195
|
-
tooltip.line = new LeaderLine(tooltip, this.
|
206
|
+
tooltip.line = new LeaderLine(tooltip, this.targetElement, {
|
196
207
|
...this.leaderLineOptions,
|
197
208
|
color: 'darkcyan'
|
198
209
|
})
|
@@ -201,30 +212,34 @@ export default class ToggleDevtool {
|
|
201
212
|
return tooltip
|
202
213
|
}
|
203
214
|
|
204
|
-
createTriggerTooltip (targetTooltip,
|
205
|
-
if (!this.
|
206
|
-
const title =
|
207
|
-
const
|
215
|
+
createTriggerTooltip (targetTooltip, morphTooltip) {
|
216
|
+
if (!this.triggerElement) return
|
217
|
+
const title = 'TRIGGER'
|
218
|
+
const subtitle = `
|
219
|
+
id: ${this.triggerElement.id}<br>
|
220
|
+
controls: ${this.triggerElement.controls}
|
221
|
+
`
|
222
|
+
const content = this.triggerElement.viewStack
|
208
223
|
.reverse()
|
209
224
|
.map((view, index) => {
|
210
|
-
return this.
|
225
|
+
return this.triggerElement.sharedViews.includes(view)
|
211
226
|
? `<div slot="content-top">${index + 1}. ${view}</div>`
|
212
227
|
: `<div slot="content-bottom">${index + 1}. ${view}</div>`
|
213
228
|
}, this)
|
214
229
|
.join('')
|
215
230
|
|
216
|
-
const tooltip = appendTooltip(title, content, {
|
231
|
+
const tooltip = appendTooltip(title, subtitle, content, {
|
217
232
|
backgroundColor: 'lavender',
|
218
233
|
color: 'blueviolet'
|
219
234
|
})
|
220
235
|
|
221
|
-
const coords = coordinates(this.
|
236
|
+
const coords = coordinates(this.triggerElement)
|
222
237
|
const top = Math.ceil(coords.top - tooltip.offsetHeight * 2)
|
223
238
|
const left = Math.ceil(coords.left + coords.width + tooltip.offsetWidth / 3)
|
224
239
|
tooltip.style.top = `${top}px`
|
225
240
|
tooltip.style.left = `${left}px`
|
226
241
|
|
227
|
-
tooltip.line = new LeaderLine(this.
|
242
|
+
tooltip.line = new LeaderLine(this.triggerElement, tooltip, {
|
228
243
|
...this.leaderLineOptions,
|
229
244
|
color: 'blueviolet'
|
230
245
|
})
|
@@ -244,16 +259,16 @@ export default class ToggleDevtool {
|
|
244
259
|
}
|
245
260
|
}
|
246
261
|
|
247
|
-
if (
|
248
|
-
tooltip.lineToRendering = new LeaderLine(tooltip,
|
262
|
+
if (morphTooltip) {
|
263
|
+
tooltip.lineToRendering = new LeaderLine(tooltip, morphTooltip, {
|
249
264
|
...this.leaderLineOptions,
|
250
265
|
color: 'blueviolet',
|
251
|
-
middleLabel: 'renders',
|
266
|
+
middleLabel: 'renders and morphs',
|
252
267
|
size: 2.1
|
253
268
|
})
|
254
269
|
|
255
|
-
|
256
|
-
|
270
|
+
morphTooltip.drag.onMove = () => {
|
271
|
+
morphTooltip.line.position()
|
257
272
|
if (tooltip.lineToTarget) tooltip.lineToTarget.position()
|
258
273
|
tooltip.lineToRendering.position()
|
259
274
|
}
|
@@ -261,6 +276,7 @@ export default class ToggleDevtool {
|
|
261
276
|
|
262
277
|
tooltip.drag = new PlainDraggable(tooltip)
|
263
278
|
tooltip.drag.onMove = () => {
|
279
|
+
console.log('nate', tooltip.line)
|
264
280
|
tooltip.line.position()
|
265
281
|
if (tooltip.lineToTarget) tooltip.lineToTarget.position()
|
266
282
|
if (tooltip.lineToRendering) tooltip.lineToRendering.position()
|
@@ -8,7 +8,6 @@ export default class ReflexElement extends HTMLElement {
|
|
8
8
|
|
9
9
|
connectedCallback () {
|
10
10
|
this.ensureId()
|
11
|
-
this.dataset.elementOrigin = 'hopsoft/reflex_behaviors'
|
12
11
|
}
|
13
12
|
|
14
13
|
ensureId () {
|
@@ -27,8 +26,9 @@ export default class ReflexElement extends HTMLElement {
|
|
27
26
|
}
|
28
27
|
|
29
28
|
get viewStack () {
|
30
|
-
|
31
|
-
|
29
|
+
const value = this.getAttribute('view-stack')
|
30
|
+
if (!value) return []
|
31
|
+
return JSON.parse(value)
|
32
32
|
}
|
33
33
|
|
34
34
|
get partial () {
|
@@ -1,3 +1,83 @@
|
|
1
1
|
import ReflexElement from './reflex_element'
|
2
2
|
|
3
|
-
export default class ToggleTargetElement extends ReflexElement {
|
3
|
+
export default class ToggleTargetElement extends ReflexElement {
|
4
|
+
connectedCallback () {
|
5
|
+
super.connectedCallback()
|
6
|
+
|
7
|
+
this.addEventListener('mouseenter', () =>
|
8
|
+
clearTimeout(this.collapseTimeout)
|
9
|
+
)
|
10
|
+
|
11
|
+
this.collapseOn.forEach(name =>
|
12
|
+
this.addEventListener(name, () => this.collapse())
|
13
|
+
)
|
14
|
+
}
|
15
|
+
|
16
|
+
// TODO: get cached content working properly
|
17
|
+
// perhaps use a mechanic other than morph
|
18
|
+
|
19
|
+
cacheHTML () {
|
20
|
+
// this.cachedHTML = this.innerHTML
|
21
|
+
}
|
22
|
+
|
23
|
+
renderCachedHTML () {
|
24
|
+
// if (!this.cachedHTML) return
|
25
|
+
// this.innerHTML = this.cachedHTML
|
26
|
+
}
|
27
|
+
|
28
|
+
collapse () {
|
29
|
+
clearTimeout(this.collapseTimeout)
|
30
|
+
this.collapseTimeout = setTimeout(() => {
|
31
|
+
this.innerHTML = ''
|
32
|
+
try {
|
33
|
+
this.currentTriggerElement.expanded = false
|
34
|
+
this.currentTriggerElement.hideDevtool()
|
35
|
+
} catch {}
|
36
|
+
}, 250)
|
37
|
+
}
|
38
|
+
|
39
|
+
collapseMatches () {
|
40
|
+
document.querySelectorAll(this.collapseSelector).forEach(el => {
|
41
|
+
if (el === this) return
|
42
|
+
if (el.collapse) el.collapse()
|
43
|
+
})
|
44
|
+
}
|
45
|
+
|
46
|
+
get collapseSelector () {
|
47
|
+
if (
|
48
|
+
this.currentTriggerElement &&
|
49
|
+
this.currentTriggerElement.collapseSelector
|
50
|
+
)
|
51
|
+
return this.currentTriggerElement.collapseSelector
|
52
|
+
return this.getAttribute('collapse-selector')
|
53
|
+
}
|
54
|
+
|
55
|
+
focus () {
|
56
|
+
clearTimeout(this.focusTimeout)
|
57
|
+
this.focusTimeout = setTimeout(() => {
|
58
|
+
if (!this.focusElement) return
|
59
|
+
this.focusElement.focus()
|
60
|
+
this.focusElement.scrollIntoView({ block: 'center', behavior: 'smooth' })
|
61
|
+
}, 50)
|
62
|
+
}
|
63
|
+
|
64
|
+
get focusSelector () {
|
65
|
+
if (this.currentTriggerElement && this.currentTriggerElement.focusSelector)
|
66
|
+
return this.currentTriggerElement.focusSelector
|
67
|
+
return this.getAttribute('focus-selector')
|
68
|
+
}
|
69
|
+
|
70
|
+
get focusElement () {
|
71
|
+
return this.querySelector(this.focusSelector)
|
72
|
+
}
|
73
|
+
|
74
|
+
get labeledBy () {
|
75
|
+
return this.getAttribute('aria-labeledby')
|
76
|
+
}
|
77
|
+
|
78
|
+
get collapseOn () {
|
79
|
+
const value = this.getAttribute('collapse-on')
|
80
|
+
if (!value) return []
|
81
|
+
return JSON.parse(value)
|
82
|
+
}
|
83
|
+
}
|
@@ -5,14 +5,38 @@ import ToggleDevtool from '../devtools/toggle'
|
|
5
5
|
export default class ToggleTriggerElement extends ReflexElement {
|
6
6
|
connectedCallback () {
|
7
7
|
super.connectedCallback()
|
8
|
+
|
9
|
+
if (this.targetElement) {
|
10
|
+
this.targetElement.setAttribute('aria-labeledby', this.id)
|
11
|
+
}
|
12
|
+
|
13
|
+
this.addEventListener(TurboReflex.events.start, () => {
|
14
|
+
this.busy = true
|
15
|
+
this.targetElement.currentTriggerElement = this
|
16
|
+
this.targetElement.renderCachedHTML()
|
17
|
+
})
|
18
|
+
|
19
|
+
this.addEventListener(TurboReflex.events.success, () => {
|
20
|
+
this.busy = false
|
21
|
+
this.targetElement.focus()
|
22
|
+
this.targetElement.collapseMatches()
|
23
|
+
this.targetElement.cacheHTML()
|
24
|
+
})
|
25
|
+
|
26
|
+
this.addEventListener(TurboReflex.events.finish, () => (this.busy = false))
|
27
|
+
|
28
|
+
this.initializeDevtool()
|
29
|
+
}
|
30
|
+
|
31
|
+
initializeDevtool () {
|
8
32
|
const mouseenter = () => this.devtool.show()
|
9
33
|
|
10
|
-
|
34
|
+
addEventListener('reflex-behaviors:devtools-start', () => {
|
11
35
|
this.devtool = new ToggleDevtool(this)
|
12
36
|
this.addEventListener('mouseenter', mouseenter)
|
13
37
|
})
|
14
38
|
|
15
|
-
|
39
|
+
addEventListener('reflex-behaviors:devtools-stop', () => {
|
16
40
|
this.removeEventListener('mouseenter', mouseenter)
|
17
41
|
delete this.devtool
|
18
42
|
})
|
@@ -20,79 +44,76 @@ export default class ToggleTriggerElement extends ReflexElement {
|
|
20
44
|
if (DevtoolSupervisor.started) DevtoolSupervisor.restart()
|
21
45
|
}
|
22
46
|
|
23
|
-
|
24
|
-
|
25
|
-
this.target.remove()
|
26
|
-
this.setAttribute('aria-expanded', false)
|
27
|
-
} catch (error) {
|
28
|
-
console.error('Failed to collapse toggle-trigger target!', error)
|
29
|
-
}
|
47
|
+
hideDevtool () {
|
48
|
+
if (this.devtool) this.devtool.hide(true)
|
30
49
|
}
|
31
50
|
|
51
|
+
// a list of views shared between the trigger and target
|
32
52
|
get sharedViews () {
|
33
|
-
if (!this.
|
34
|
-
if (!this.
|
53
|
+
if (!this.targetElement) return []
|
54
|
+
if (!this.targetElement.viewStack) return []
|
35
55
|
const reducer = (memo, view) => {
|
36
|
-
if (this.
|
56
|
+
if (this.targetElement.viewStack.includes(view)) memo.push(view)
|
37
57
|
return memo
|
38
58
|
}
|
39
59
|
return this.viewStack.reduce(reducer.bind(this), [])
|
40
60
|
}
|
41
61
|
|
42
|
-
|
43
|
-
|
44
|
-
return
|
62
|
+
// the partial to render
|
63
|
+
get renders () {
|
64
|
+
return this.getAttribute('renders')
|
65
|
+
}
|
66
|
+
|
67
|
+
// the renderered partial's top wrapping dom_id
|
68
|
+
get morphs () {
|
69
|
+
return this.getAttribute('morphs')
|
45
70
|
}
|
46
71
|
|
47
|
-
|
48
|
-
|
72
|
+
// the morph element
|
73
|
+
get morphElement () {
|
74
|
+
if (!this.morphs) return null
|
75
|
+
return document.getElementById(this.morphs)
|
49
76
|
}
|
50
77
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
78
|
+
// the target's dom_id
|
79
|
+
get controls () {
|
80
|
+
return this.getAttribute('aria-controls')
|
81
|
+
}
|
82
|
+
|
83
|
+
// the target element
|
84
|
+
get targetElement () {
|
85
|
+
if (!this.controls) return null
|
86
|
+
return document.getElementById(this.controls)
|
87
|
+
}
|
88
|
+
|
89
|
+
get collapseSelector () {
|
90
|
+
return this.getAttribute('collapse-selector')
|
55
91
|
}
|
56
92
|
|
93
|
+
get focusSelector () {
|
94
|
+
return this.getAttribute('focus-selector')
|
95
|
+
}
|
96
|
+
|
97
|
+
// indicates if the target is expanded
|
57
98
|
get expanded () {
|
58
99
|
return this.getAttribute('aria-expanded') === 'true'
|
59
100
|
}
|
60
101
|
|
61
|
-
|
62
|
-
|
102
|
+
set expanded (value) {
|
103
|
+
this.setAttribute('aria-expanded', !!value)
|
63
104
|
}
|
64
105
|
|
65
|
-
|
66
|
-
|
106
|
+
// indicates if the target is expanded
|
107
|
+
get collapsed () {
|
108
|
+
return !this.expanded
|
67
109
|
}
|
68
110
|
|
69
|
-
|
70
|
-
|
111
|
+
// indicates if an rpc call is active/busy
|
112
|
+
get busy () {
|
113
|
+
return this.getAttribute('busy') === 'true'
|
71
114
|
}
|
72
115
|
|
73
|
-
set
|
74
|
-
this.setAttribute('
|
116
|
+
set busy (value) {
|
117
|
+
this.setAttribute('busy', !!value)
|
75
118
|
}
|
76
119
|
}
|
77
|
-
|
78
|
-
addEventListener(
|
79
|
-
TurboReflex.events.start,
|
80
|
-
event => (event.target.active = true)
|
81
|
-
)
|
82
|
-
addEventListener(
|
83
|
-
TurboReflex.events.success,
|
84
|
-
event => (event.target.active = false)
|
85
|
-
)
|
86
|
-
addEventListener(
|
87
|
-
TurboReflex.events.finish,
|
88
|
-
event => (event.target.active = false)
|
89
|
-
)
|
90
|
-
|
91
|
-
addEventListener('click', event => {
|
92
|
-
if (event.target.tagName.match(/reflex-behaviors-devtool/i)) return
|
93
|
-
setTimeout(() => {
|
94
|
-
const selector =
|
95
|
-
'toggle-trigger[aria-controls][aria-expanded="true"][data-auto-collapse="true"]'
|
96
|
-
document.querySelectorAll(selector).forEach(trigger => trigger.collapse())
|
97
|
-
})
|
98
|
-
})
|
@@ -21,19 +21,22 @@ class ReflexBehaviors::ApplicationReflex < TurboReflex::Base
|
|
21
21
|
protected
|
22
22
|
|
23
23
|
def render_payload
|
24
|
-
return {} if element.
|
25
|
-
@render_payload ||=
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
24
|
+
return {} if element.renders.blank?
|
25
|
+
@render_payload ||= {partial: idomatic_partial_path(element.renders)}.tap do |payload|
|
26
|
+
if element.assigns.present?
|
27
|
+
payload[:assigns] = JSON.parse(element.assigns)
|
28
|
+
payload[:assigns].each { |key, value| payload[:assigns][key] = hydrate_value(value) }
|
29
|
+
end
|
30
|
+
if element.locals.present?
|
31
|
+
payload[:locals] = JSON.parse(element.locals)
|
32
|
+
payload[:locals].each { |key, value| payload[:locals][key] = hydrate_value(value) }
|
33
|
+
end
|
34
|
+
end.deep_symbolize_keys
|
32
35
|
end
|
33
36
|
|
34
37
|
private
|
35
38
|
|
36
|
-
def
|
39
|
+
def hydrate_value(value)
|
37
40
|
hydrated = begin
|
38
41
|
GlobalID::Locator.locate_signed(value)
|
39
42
|
rescue
|
@@ -41,4 +44,8 @@ class ReflexBehaviors::ApplicationReflex < TurboReflex::Base
|
|
41
44
|
end
|
42
45
|
hydrated.blank? ? nil : hydrated
|
43
46
|
end
|
47
|
+
|
48
|
+
def idomatic_partial_path(partial_path)
|
49
|
+
partial_path.to_s.gsub("/_", "/").split(".").first
|
50
|
+
end
|
44
51
|
end
|