turbo_boost-elements 0.0.9 → 0.0.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.
@@ -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