turbo_boost-elements 0.0.9 → 0.0.11

Sign up to get free protection for your applications and to get access to all the features.
@@ -44,10 +44,9 @@ function focusTrixEditorElement (element) {
44
44
  }
45
45
 
46
46
  function shouldEnhanceFocus (element) {
47
- return (
48
- element.closest('turbo-boost-toggle-target') &&
49
- element.tagName.match(/^input|textarea|trix-editor$/i)
50
- )
47
+ if (!element.tagName.match(/^input|textarea|trix-editor$/i)) return false
48
+ const toggleTargetElement = element.closest('turbo-boost-toggle-target')
49
+ return toggleTargetElement && toggleTargetElement.focusSelector
51
50
  }
52
51
 
53
52
  function enhanceFocus (element) {
@@ -68,7 +68,7 @@ export default class ToggleTargetElement extends ToggleElement {
68
68
  this.innerHTML = ''
69
69
  try {
70
70
  this.expanded = false
71
- this.currentTriggerElement.hideDevtool()
71
+ this.triggerElement.hideDevtool()
72
72
  } catch {}
73
73
  }
74
74
 
@@ -86,28 +86,30 @@ export default class ToggleTargetElement extends ToggleElement {
86
86
 
87
87
  get collapseSelector () {
88
88
  return (
89
- this.currentTriggerElement.collapseSelector ||
89
+ this.triggerElement.collapseSelector ||
90
90
  this.getAttribute('collapse-selector')
91
91
  )
92
92
  }
93
93
 
94
- focus () {
95
- clearTimeout(this.focusTimeout)
96
- this.focusTimeout = setTimeout(() => {
97
- if (this.focusElement) this.focusElement.focus()
98
- }, 50)
94
+ applyFocus () {
95
+ if (this.focusElement) this.focusElement.focus()
99
96
  }
100
97
 
101
98
  get focusSelector () {
102
- if (this.currentTriggerElement && this.currentTriggerElement.focusSelector)
103
- return this.currentTriggerElement.focusSelector
104
- return this.getAttribute('focus-selector')
99
+ let value = this.getAttribute('focus-selector')
100
+ if (this.triggerElement)
101
+ value = this.triggerElement.getAttribute('focus-selector') || value
102
+ return value
105
103
  }
106
104
 
107
105
  get focusElement () {
108
106
  return this.querySelector(this.focusSelector)
109
107
  }
110
108
 
109
+ get triggerElement () {
110
+ return document.getElementById(this.labeledBy)
111
+ }
112
+
111
113
  get labeledBy () {
112
114
  return this.getAttribute('aria-labeledby')
113
115
  }
@@ -119,10 +121,10 @@ export default class ToggleTargetElement extends ToggleElement {
119
121
  }
120
122
 
121
123
  get expanded () {
122
- return this.currentTriggerElement.expanded
124
+ return this.triggerElement.expanded
123
125
  }
124
126
 
125
127
  set expanded (value) {
126
- return (this.currentTriggerElement.expanded = value)
128
+ return (this.triggerElement.expanded = value)
127
129
  }
128
130
  }
@@ -2,6 +2,7 @@
2
2
  import {
3
3
  appendHTML,
4
4
  addHighlight,
5
+ attempt,
5
6
  coordinates,
6
7
  removeHighlight
7
8
  } from '../../../utils/dom'
@@ -34,43 +35,57 @@ export default class Devtool {
34
35
  this.targetElement = triggerElement.targetElement // SEE: app/javascript/elements/toggle_target_element.js
35
36
  this.morphElement = triggerElement.morphElement
36
37
 
37
- document.addEventListener('turbo-boost:devtool-enable', event => {
38
- const { name } = event.detail
39
- if (name === this.name) {
40
- addHighlight(this.triggerElement, {
41
- outline: '3px dashed blueviolet',
42
- outlineOffset: '2px'
43
- })
44
- }
45
- })
46
-
47
- document.addEventListener('turbo-boost:devtool-disable', event => {
48
- const { name } = event.detail
49
- if (name === this.name) removeHighlight(this.triggerElement)
50
- })
51
-
52
38
  let hideTimeout
53
39
  const debouncedHide = () => {
54
40
  clearTimeout(hideTimeout)
55
41
  hideTimeout = setTimeout(this.hide({ active: false }), 25)
56
42
  }
57
43
 
58
- addEventListener('click', event => {
44
+ this.eventListeners['turbo-boost:devtool-enable'] = event => {
45
+ // LeaderLine.positionByWindowResize = false
46
+ const { name } = event.detail
47
+ if (name !== this.name) return
48
+
49
+ addHighlight(this.triggerElement, {
50
+ outline: '3px dashed blueviolet',
51
+ outlineOffset: '2px'
52
+ })
53
+
54
+ this.hide({ active: false })
55
+ if (this.active) this.show()
56
+ }
57
+
58
+ this.eventListeners['turbo-boost:devtool-disable'] = event => {
59
+ const { name } = event.detail
60
+ if (name === this.name) removeHighlight(this.triggerElement)
61
+ }
62
+
63
+ this.eventListeners['click'] = event => {
59
64
  if (event.target.closest('turbo-boost-devtool-tooltip')) return
60
65
  debouncedHide()
66
+ }
67
+
68
+ this.eventListeners['turbo:load'] = debouncedHide
69
+ this.eventListeners['turbo-frame:load'] = debouncedHide
70
+ this.eventListeners[TurboBoost.Commands.events.finish] = debouncedHide
71
+
72
+ this.registerEventListeners()
73
+ }
74
+
75
+ registerEventListeners () {
76
+ Object.entries(this.eventListeners).forEach(([type, listener]) => {
77
+ addEventListener(type, listener)
61
78
  })
79
+ }
62
80
 
63
- addEventListener('resize', () => {
64
- if (this.active) {
65
- this.hide({ active: false })
66
- this.show()
67
- }
81
+ unregisterEventListeners () {
82
+ Object.entries(this.eventListeners).forEach(([type, listener]) => {
83
+ removeEventListener(type, listener)
68
84
  })
85
+ }
69
86
 
70
- addEventListener('turbo:load', debouncedHide)
71
- addEventListener('turbo-frame:load', debouncedHide)
72
- addEventListener(TurboBoost.Commands.events.success, debouncedHide)
73
- addEventListener(TurboBoost.Commands.events.finish, debouncedHide)
87
+ get eventListeners () {
88
+ return this._eventListeners || (this._eventListeners = {})
74
89
  }
75
90
 
76
91
  get enabled () {
@@ -88,8 +103,10 @@ export default class Devtool {
88
103
 
89
104
  show () {
90
105
  if (!this.enabled) return
106
+
91
107
  if (this.active) return
92
108
  this.active = true
109
+
93
110
  this.hide({ active: true })
94
111
 
95
112
  addHighlight(this.targetElement, {
@@ -102,9 +119,12 @@ export default class Devtool {
102
119
  outlineOffset: '3px'
103
120
  })
104
121
 
105
- const morphTooltip = this.createMorphTooltip()
106
- const targetTooltip = this.createTargetTooltip()
107
- this.createTriggerTooltip(targetTooltip, morphTooltip)
122
+ this.renderingTooltip = this.createRenderingTooltip()
123
+ this.targetTooltip = this.createTargetTooltip()
124
+ this.triggerTooltip = this.createTriggerTooltip(
125
+ this.targetTooltip,
126
+ this.renderingTooltip
127
+ )
108
128
 
109
129
  document
110
130
  .querySelectorAll('.leader-line')
@@ -140,10 +160,15 @@ export default class Devtool {
140
160
  }
141
161
 
142
162
  hide ({ active: active = false }) {
143
- document.querySelectorAll('.leader-line').forEach(el => el.remove())
144
163
  document
145
164
  .querySelectorAll('turbo-boost-devtool-tooltip')
146
- .forEach(el => el.remove())
165
+ .forEach(tooltip => {
166
+ attempt(() => tooltip.line.remove())
167
+ attempt(() => tooltip.drag.remove())
168
+ attempt(() => tooltip.lineToRendering.remove())
169
+ attempt(() => tooltip.lineToTarget.remove())
170
+ attempt(() => tooltip.remove())
171
+ })
147
172
 
148
173
  document.querySelectorAll('[data-turbo-boost-highlight]').forEach(el => {
149
174
  if (!el.tagName.match(/turbo-boost-toggle-trigger/i)) removeHighlight(el)
@@ -152,10 +177,15 @@ export default class Devtool {
152
177
  this.active = active
153
178
  }
154
179
 
155
- createMorphTooltip () {
180
+ createRenderingTooltip () {
181
+ if (!this.triggerElement.renders)
182
+ return console.debug(
183
+ `Unable to create the rendering tooltip! The trigger element must set the 'renders' attribute.`
184
+ )
185
+
156
186
  if (!this.triggerElement.morphs)
157
187
  return console.debug(
158
- `Unable to create the morph tooltip! No element matches the DOM id: '${this.triggerElement.morphs}'`
188
+ `Unable to create the rendering tooltip! The trigger element specified the 'morphs' attrbiute but no element matches the DOM id: '${this.triggerElement.morphs}'`
159
189
  )
160
190
 
161
191
  const title = `
@@ -245,7 +275,7 @@ export default class Devtool {
245
275
  return tooltip
246
276
  }
247
277
 
248
- createTriggerTooltip (targetTooltip, morphTooltip) {
278
+ createTriggerTooltip (targetTooltip, renderingTooltip) {
249
279
  if (!this.triggerElement) return
250
280
  const title = `
251
281
  <svg xmlns="http://www.w3.org/2000/svg" style="display:inline;" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"></polygon></svg>
@@ -305,16 +335,16 @@ export default class Devtool {
305
335
  }
306
336
  }
307
337
 
308
- if (morphTooltip) {
309
- tooltip.lineToRendering = new LeaderLine(tooltip, morphTooltip, {
338
+ if (renderingTooltip) {
339
+ tooltip.lineToRendering = new LeaderLine(tooltip, renderingTooltip, {
310
340
  ...this.leaderLineOptions,
311
341
  color: 'blueviolet',
312
342
  middleLabel: 'renders & morphs',
313
343
  size: 2.1
314
344
  })
315
345
 
316
- morphTooltip.drag.onMove = () => {
317
- morphTooltip.line.position()
346
+ renderingTooltip.drag.onMove = () => {
347
+ renderingTooltip.line.position()
318
348
  if (tooltip.lineToTarget) tooltip.lineToTarget.position()
319
349
  tooltip.lineToRendering.position()
320
350
  }
@@ -32,6 +32,7 @@ export default class ToggleTriggerElement extends ToggleElement {
32
32
  removeEventListener(beforeInvokeEvent, this.beforeInvokeHandler)
33
33
 
34
34
  this.devtool.hide({ active: false })
35
+ this.devtool.unregisterEventListeners()
35
36
  delete this.devtool
36
37
  }
37
38
 
@@ -45,6 +46,8 @@ export default class ToggleTriggerElement extends ToggleElement {
45
46
 
46
47
  addEventListener('turbo-boost:devtools-stop', () => {
47
48
  this.removeEventListener('mouseenter', mouseenter)
49
+ this.devtool.hide({ active: false })
50
+ this.devtool.unregisterEventListeners()
48
51
  delete this.devtool
49
52
  })
50
53
 
@@ -58,7 +61,6 @@ export default class ToggleTriggerElement extends ToggleElement {
58
61
  }
59
62
 
60
63
  onCommandStart (event) {
61
- this.targetElement.currentTriggerElement = this
62
64
  this.targetElement.setAttribute('aria-labeledby', this.id)
63
65
  this.targetElement.collapseMatches()
64
66
  this.targetElement.busy = true
@@ -83,13 +85,13 @@ export default class ToggleTriggerElement extends ToggleElement {
83
85
  this.busy = false
84
86
  this.targetElement.busy = false
85
87
  this.morphToggleElements.forEach(el => (el.busy = false))
86
- this.expanded = !this.expanded
87
88
  }, delay - 10)
88
89
 
89
90
  // runs after the morph is executed
90
91
  setTimeout(() => {
91
- if (this.expanded) this.targetElement.focus()
92
- }, delay + 10)
92
+ this.targetElement.setAttribute('aria-labeledby', this.id)
93
+ this.targetElement.applyFocus()
94
+ }, delay + 100)
93
95
  }
94
96
 
95
97
  // a list of views shared between the trigger and target
@@ -144,7 +146,10 @@ export default class ToggleTriggerElement extends ToggleElement {
144
146
  }
145
147
 
146
148
  get focusSelector () {
147
- return this.getAttribute('focus-selector')
149
+ return (
150
+ this.getAttribute('focus-selector') ||
151
+ this.targetElement.getAttribute('focus-selector')
152
+ )
148
153
  }
149
154
 
150
155
  // indicates if the toggle state should be remembered across requests
@@ -62,3 +62,9 @@ export function coordinates (element) {
62
62
  const bottom = top + height
63
63
  return { top, left, right, bottom, width, height }
64
64
  }
65
+
66
+ export function attempt (callback) {
67
+ try {
68
+ callback()
69
+ } catch {}
70
+ }
@@ -22,12 +22,13 @@ class TurboBoost::Elements::TagBuilders::ToggleTagsBuilder < TurboBoost::Element
22
22
  focus_selector: nil, # CSS selector for the element to focus when the target is expanded
23
23
  method: :toggle, # method to inovke (:show, :hide, :toggle)
24
24
  disabled: false, # disable the trigger
25
+ expanded: false, # override to force expansion
25
26
  remember: false, # remember ephemeral UI state between requests
26
27
  **kwargs, # generic support for additional element attributes like `class` etc.
27
28
  &block # a Ruby block that emits this trigger's content
28
29
  )
29
30
  kwargs = kwargs.with_indifferent_access
30
- kwargs[:id] ||= "#{controls}-toggle-trigger-#{SecureRandom.hex(6)}"
31
+ kwargs[:id] ||= "#{controls}-toggle-trigger"
31
32
 
32
33
  # command
33
34
  kwargs[:data] ||= {}
@@ -36,7 +37,7 @@ class TurboBoost::Elements::TagBuilders::ToggleTagsBuilder < TurboBoost::Element
36
37
  # aria
37
38
  kwargs[:aria] ||= {}
38
39
  kwargs[:aria][:controls] = controls # toggle target
39
- kwargs[:aria][:expanded] = target_expanded?(controls)
40
+ kwargs[:aria][:expanded] = expanded || target_expanded?(controls)
40
41
  kwargs[:aria][:atomic] ||= true
41
42
  kwargs[:aria][:relevant] ||= "all"
42
43
 
@@ -57,8 +58,8 @@ class TurboBoost::Elements::TagBuilders::ToggleTagsBuilder < TurboBoost::Element
57
58
  id, # REQUIRED, the `dom_id` for the element
58
59
  collapse_on: [], # list of events that will collapse this target
59
60
  collapse_selector: nil, # CSS selector for other matching targets to collapse when this target is expanded
60
- expanded: false, # override to force expansion
61
61
  focus_selector: nil, # CSS selector for the element to focus when this target is expanded
62
+ expanded: false, # override to force expansion
62
63
  **kwargs, # generic support for additional element attributes like `class` etc.
63
64
  &block # a Ruby block that emits this target's content
64
65
  )
@@ -2,6 +2,6 @@
2
2
 
3
3
  module TurboBoost
4
4
  module Elements
5
- VERSION = "0.0.9"
5
+ VERSION = "0.0.11"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: turbo_boost-elements
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.9
4
+ version: 0.0.11
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nate Hopkins (hopsoft)
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-01-28 00:00:00.000000000 Z
11
+ date: 2023-02-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails