reflex_behaviors 0.0.3 → 0.0.4
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 +12 -7
- data/README.md +1 -1
- data/app/assets/builds/reflex_behaviors.js +133 -10
- data/app/assets/builds/reflex_behaviors.js.map +4 -4
- data/app/javascript/devtools/dom.js +34 -0
- data/app/javascript/devtools/elements/devtool_element.js +62 -0
- data/app/javascript/devtools/elements/supervisor_element.js +113 -0
- data/app/javascript/devtools/elements/tooltip_element.js +102 -0
- data/app/javascript/devtools/index.js +3 -115
- data/app/javascript/devtools/supervisor.js +83 -0
- data/app/javascript/devtools/toggle.js +150 -0
- data/app/javascript/elements/reflex_element.js +12 -0
- data/app/javascript/elements/toggle_trigger_element.js +41 -42
- data/app/javascript/index.js +1 -5
- data/lib/reflex_behaviors/engine.rb +0 -1
- data/lib/reflex_behaviors/version.rb +1 -1
- data/package.json +1 -1
- data/tags +1069 -853
- data/yarn.lock +138 -138
- metadata +8 -7
- data/app/assets/config/reflex_behaviors_manifest.js +0 -1
- data/app/assets/stylesheets/devtools.css +0 -51
- data/app/assets/stylesheets/reflex_behaviors.css +0 -4
- data/app/assets/stylesheets/toggle.css +0 -56
- data/app/assets/stylesheets/tooltip.css +0 -18
@@ -0,0 +1,34 @@
|
|
1
|
+
export function template (html) {
|
2
|
+
let template = document.createElement('template')
|
3
|
+
template.innerHTML = html
|
4
|
+
return template
|
5
|
+
}
|
6
|
+
|
7
|
+
export function appendHTML (html, parent) {
|
8
|
+
parent = parent || document.body
|
9
|
+
return parent.appendChild(template(html).content.cloneNode(true))
|
10
|
+
}
|
11
|
+
|
12
|
+
export function addHighlight (element, options = {}) {
|
13
|
+
if (!element) return
|
14
|
+
let { color, offset, width } = options
|
15
|
+
color = color || 'red'
|
16
|
+
offset = offset || '0px'
|
17
|
+
width = width || '3px'
|
18
|
+
const { outline, outlineOffset } = element.style
|
19
|
+
element.originalStyles = element.originalStyles || {
|
20
|
+
outline,
|
21
|
+
outlineOffset
|
22
|
+
}
|
23
|
+
element.style.outline = `dotted ${width} ${color}`
|
24
|
+
element.style.outlineOffset = offset
|
25
|
+
}
|
26
|
+
|
27
|
+
export function removeHighlight (element) {
|
28
|
+
if (element && element.originalStyles) {
|
29
|
+
for (const [key, value] of Object.entries(element.originalStyles)) {
|
30
|
+
value ? (element.style[key] = value) : (element.style[key] = '')
|
31
|
+
}
|
32
|
+
delete element.originalStyles
|
33
|
+
}
|
34
|
+
}
|
@@ -0,0 +1,62 @@
|
|
1
|
+
export default class DevtoolElement extends HTMLElement {
|
2
|
+
constructor () {
|
3
|
+
super()
|
4
|
+
this.attachShadow({ mode: 'open' })
|
5
|
+
this.shadowRoot.innerHTML = this.html
|
6
|
+
this.labelElement.addEventListener('click', event => {
|
7
|
+
event.preventDefault()
|
8
|
+
this.toggle()
|
9
|
+
})
|
10
|
+
this.checkboxElement.addEventListener('change', event =>
|
11
|
+
this.dispatchEvent(new CustomEvent('change', { bubbles: true }))
|
12
|
+
)
|
13
|
+
}
|
14
|
+
|
15
|
+
toggle () {
|
16
|
+
this.checked ? this.uncheck() : this.check()
|
17
|
+
}
|
18
|
+
|
19
|
+
check () {
|
20
|
+
this.checkboxElement.checked = true
|
21
|
+
this.dispatchEvent(new CustomEvent('change', { bubbles: true }))
|
22
|
+
}
|
23
|
+
|
24
|
+
uncheck () {
|
25
|
+
this.checkboxElement.checked = false
|
26
|
+
this.dispatchEvent(new CustomEvent('change', { bubbles: true }))
|
27
|
+
}
|
28
|
+
|
29
|
+
get name () {
|
30
|
+
return this.getAttribute('name')
|
31
|
+
}
|
32
|
+
|
33
|
+
get checked () {
|
34
|
+
return this.checkboxElement.checked
|
35
|
+
}
|
36
|
+
|
37
|
+
get checkboxElement () {
|
38
|
+
return this.shadowRoot.querySelector('input')
|
39
|
+
}
|
40
|
+
|
41
|
+
get labelElement () {
|
42
|
+
return this.shadowRoot.querySelector('label')
|
43
|
+
}
|
44
|
+
|
45
|
+
get html () {
|
46
|
+
return `
|
47
|
+
<style>${this.stylesheet}</style>
|
48
|
+
<div>
|
49
|
+
<input name="checkbox" type="checkbox">
|
50
|
+
<label for="checkbox"><slot name="label"></slot></label>
|
51
|
+
</div>
|
52
|
+
`
|
53
|
+
}
|
54
|
+
|
55
|
+
get stylesheet () {
|
56
|
+
return `
|
57
|
+
div {
|
58
|
+
display: flex;
|
59
|
+
}
|
60
|
+
`
|
61
|
+
}
|
62
|
+
}
|
@@ -0,0 +1,113 @@
|
|
1
|
+
import { appendHTML } from '../dom'
|
2
|
+
|
3
|
+
export default class SupervisorElement extends HTMLElement {
|
4
|
+
constructor () {
|
5
|
+
super()
|
6
|
+
this.enabledDevtools = {}
|
7
|
+
this.attachShadow({ mode: 'open' })
|
8
|
+
this.shadowRoot.innerHTML = this.html
|
9
|
+
this.shadowRoot
|
10
|
+
.querySelector('button[data-role="closer"]')
|
11
|
+
.addEventListener('click', () => this.close())
|
12
|
+
|
13
|
+
this.addEventListener('change', event => {
|
14
|
+
const devtoolElement = event.target
|
15
|
+
const { checked, name } = devtoolElement
|
16
|
+
checked ? this.enableDevtool(name) : this.disableDevtool(name)
|
17
|
+
})
|
18
|
+
}
|
19
|
+
|
20
|
+
enableDevtool (name) {
|
21
|
+
if (this.enabledDevtools[name]) return
|
22
|
+
this.enabledDevtools[name] = true
|
23
|
+
this.dispatchEvent(
|
24
|
+
new CustomEvent('reflex-behaviors:devtool-enable', {
|
25
|
+
bubbles: true,
|
26
|
+
detail: { name: name }
|
27
|
+
})
|
28
|
+
)
|
29
|
+
}
|
30
|
+
|
31
|
+
disableDevtool (name) {
|
32
|
+
if (!this.enabledDevtools[name]) return
|
33
|
+
delete this.enabledDevtools[name]
|
34
|
+
this.dispatchEvent(
|
35
|
+
new CustomEvent('reflex-behaviors:devtool-disable', {
|
36
|
+
bubbles: true,
|
37
|
+
detail: { name: name }
|
38
|
+
})
|
39
|
+
)
|
40
|
+
}
|
41
|
+
|
42
|
+
close () {
|
43
|
+
this.devtoolElements.forEach(el => {
|
44
|
+
if (el.checked) el.uncheck()
|
45
|
+
})
|
46
|
+
this.remove()
|
47
|
+
}
|
48
|
+
|
49
|
+
get devtoolElements () {
|
50
|
+
return this.querySelectorAll('[slot="devtool"]')
|
51
|
+
}
|
52
|
+
|
53
|
+
get closeElement () {
|
54
|
+
return this.querySelector('button[data-role="closer"]')
|
55
|
+
}
|
56
|
+
|
57
|
+
get html () {
|
58
|
+
return `
|
59
|
+
<style>${this.stylesheet}</style>
|
60
|
+
<div data-role="container">
|
61
|
+
<strong>ReflexBehaviors</strong>
|
62
|
+
<slot name="devtool"></slot>
|
63
|
+
<button data-role='closer'>X</button>
|
64
|
+
</div>
|
65
|
+
`
|
66
|
+
}
|
67
|
+
|
68
|
+
get stylesheet () {
|
69
|
+
return `
|
70
|
+
:host {
|
71
|
+
background-color: ghostwhite;
|
72
|
+
border-radius: 10px;
|
73
|
+
outline: solid 1px gainsboro;
|
74
|
+
bottom: 20px;
|
75
|
+
display: block;
|
76
|
+
filter: drop-shadow(0 4px 3px rgba(0,0,0,.07));
|
77
|
+
left: 50%;
|
78
|
+
padding: 5px 10px;
|
79
|
+
position: fixed;
|
80
|
+
transform: translateX(-50%);
|
81
|
+
z-index: 10000;
|
82
|
+
}
|
83
|
+
|
84
|
+
:host, :host * {
|
85
|
+
-webkit-user-select: none;
|
86
|
+
user-select: none;
|
87
|
+
}
|
88
|
+
|
89
|
+
strong {
|
90
|
+
color: silver;
|
91
|
+
font-weight: 600;
|
92
|
+
}
|
93
|
+
|
94
|
+
div[data-role="container"] {
|
95
|
+
display: flex;
|
96
|
+
gap: 0 5px;
|
97
|
+
}
|
98
|
+
|
99
|
+
button[data-role="closer"] {
|
100
|
+
border: none;
|
101
|
+
background-color: gainsboro;
|
102
|
+
border-radius: 50%;
|
103
|
+
color: white;
|
104
|
+
font-size: 12px;
|
105
|
+
height: 18px;
|
106
|
+
line-height: 18px;
|
107
|
+
margin: 0 -5px 0 10px;
|
108
|
+
padding: 0 3px;
|
109
|
+
width: 18px;
|
110
|
+
}
|
111
|
+
`
|
112
|
+
}
|
113
|
+
}
|
@@ -0,0 +1,102 @@
|
|
1
|
+
export default class TooltipElement extends HTMLElement {
|
2
|
+
constructor () {
|
3
|
+
super()
|
4
|
+
this.attachShadow({ mode: 'open' })
|
5
|
+
this.shadowRoot.innerHTML = this.html
|
6
|
+
}
|
7
|
+
|
8
|
+
get color () {
|
9
|
+
return this.getAttribute('color') || 'darkslategray'
|
10
|
+
}
|
11
|
+
|
12
|
+
get emphasisColor () {
|
13
|
+
return this.getAttribute('emphasis-color') || 'black'
|
14
|
+
}
|
15
|
+
|
16
|
+
get backgroundColor () {
|
17
|
+
return this.getAttribute('background-color') || 'gainsboro'
|
18
|
+
}
|
19
|
+
|
20
|
+
get position () {
|
21
|
+
return this.getAttribute('position') || 'top'
|
22
|
+
}
|
23
|
+
|
24
|
+
get cssArrow () {
|
25
|
+
switch (this.position) {
|
26
|
+
case 'bottom':
|
27
|
+
return `transparent transparent ${this.emphasisColor} transparent`
|
28
|
+
default:
|
29
|
+
return `${this.emphasisColor} transparent transparent transparent;`
|
30
|
+
}
|
31
|
+
}
|
32
|
+
|
33
|
+
get html () {
|
34
|
+
return `
|
35
|
+
<style>${this.stylesheet}</style>
|
36
|
+
<div role="tooltip">
|
37
|
+
<slot name="title"></slot>
|
38
|
+
<hr>
|
39
|
+
<slot name="normal"></slot>
|
40
|
+
<slot name="emphasis"></slot>
|
41
|
+
</div>
|
42
|
+
`
|
43
|
+
}
|
44
|
+
|
45
|
+
get stylesheet () {
|
46
|
+
return `
|
47
|
+
:host {
|
48
|
+
display: block;
|
49
|
+
position: absolute;
|
50
|
+
z-index: 10000;
|
51
|
+
}
|
52
|
+
|
53
|
+
* {
|
54
|
+
color: ${this.color}
|
55
|
+
}
|
56
|
+
|
57
|
+
[role="tooltip"] {
|
58
|
+
background-color: ${this.backgroundColor};
|
59
|
+
border-radius: 5px;
|
60
|
+
filter: drop-shadow(3px 3px 3px rgba(0,0,0,0.3));
|
61
|
+
font-family: monospace;
|
62
|
+
left: 50px;
|
63
|
+
min-height: 30px;
|
64
|
+
min-width: 100px;
|
65
|
+
opacity: 0.9;
|
66
|
+
outline: solid 2px ${this.emphasisColor};
|
67
|
+
padding: 8px 12px 8px 12px;
|
68
|
+
white-space: nowrap;
|
69
|
+
}
|
70
|
+
|
71
|
+
[role="tooltip"]::after {
|
72
|
+
border-color: ${this.cssArrow};
|
73
|
+
border-style: solid;
|
74
|
+
border-width: 5px;
|
75
|
+
content: "";
|
76
|
+
margin-left: -5px;
|
77
|
+
position: absolute;
|
78
|
+
top: ${this.position === 'bottom' ? '-10px' : '100%'};
|
79
|
+
}
|
80
|
+
|
81
|
+
slot[name="title"] {
|
82
|
+
color: ${this.emphasisColor};
|
83
|
+
font-weight: bold;
|
84
|
+
}
|
85
|
+
|
86
|
+
slot[name="emphasis"] {
|
87
|
+
color: ${this.emphasisColor};
|
88
|
+
font-weight: normal;
|
89
|
+
opacity: 0.7;
|
90
|
+
}
|
91
|
+
|
92
|
+
hr {
|
93
|
+
background-color: ${this.emphasisColor};
|
94
|
+
border: none;
|
95
|
+
height: 1px;
|
96
|
+
margin-bottom: 4px;
|
97
|
+
margin-top: 4px;
|
98
|
+
opacity: 0.3;
|
99
|
+
}
|
100
|
+
`
|
101
|
+
}
|
102
|
+
}
|
@@ -1,117 +1,5 @@
|
|
1
|
-
|
2
|
-
// - [ ] audit css class names and refine
|
3
|
-
// - [ ] extract stylesheet and host on cdn
|
4
|
-
// - [ ] ensure tooltips don't overlap or run off screen
|
5
|
-
// - [ ] add ability to remember start/stop in local storage
|
6
|
-
// - [ ] isolate individual tools and register them (i.e. plugin framework)
|
7
|
-
// will probably have behaviors register any devtools they support
|
8
|
-
//
|
9
|
-
// Tool markup example
|
10
|
-
//
|
11
|
-
// <div name="TOOL_NAME" class="devtool">
|
12
|
-
// <input name="TOOL_NAME-checkbox" value="TOOL_NAME" type="checkbox">
|
13
|
-
// <label for="TOOL_NAME-checkbox">TOOL_LABEL</label>
|
14
|
-
// </div>
|
1
|
+
import supervisor from './supervisor'
|
15
2
|
|
16
|
-
|
3
|
+
const { restart, start, stop } = supervisor
|
17
4
|
|
18
|
-
|
19
|
-
setTimeout(() => {
|
20
|
-
document
|
21
|
-
.querySelectorAll('.reflex-behaviors-tooltip')
|
22
|
-
.forEach(tooltip => tooltip.remove())
|
23
|
-
}, 300)
|
24
|
-
})
|
25
|
-
|
26
|
-
function tooltip (reflexElement, title, body, cssClass, position = 'top') {
|
27
|
-
const el = document.createElement('div')
|
28
|
-
el.classList.add('reflex-behaviors-tooltip', cssClass)
|
29
|
-
el.innerHTML = `<strong>${title}</strong><hr>${body}`
|
30
|
-
document.body.appendChild(el)
|
31
|
-
|
32
|
-
const coords = reflexElement.coordinates
|
33
|
-
|
34
|
-
if (position === 'top') {
|
35
|
-
el.style.top = `${Math.ceil(coords.top - el.offsetHeight - 5)}px`
|
36
|
-
el.style.left = `${Math.ceil(coords.left + 4)}px`
|
37
|
-
}
|
38
|
-
|
39
|
-
if (position === 'bottom') {
|
40
|
-
el.style.top = `${Math.ceil(coords.top + coords.height + 5)}px`
|
41
|
-
el.style.left = `${Math.ceil(coords.left + 4)}px`
|
42
|
-
}
|
43
|
-
}
|
44
|
-
|
45
|
-
function enabled () {
|
46
|
-
const tools = document.body.dataset.devtools || ''
|
47
|
-
return tools.trim().split(' ')
|
48
|
-
}
|
49
|
-
|
50
|
-
function isEnabled (tool) {
|
51
|
-
return enabled().includes(tool)
|
52
|
-
}
|
53
|
-
|
54
|
-
function enable (tool) {
|
55
|
-
if (isEnabled(tool)) return
|
56
|
-
const list = enabled()
|
57
|
-
list.push(tool)
|
58
|
-
document.body.dataset.devtools = list.join(' ').trim()
|
59
|
-
}
|
60
|
-
|
61
|
-
function disable (tool) {
|
62
|
-
const list = enabled()
|
63
|
-
const index = list.indexOf(tool)
|
64
|
-
if (index < 0) return
|
65
|
-
list.splice(index, 1)
|
66
|
-
document.body.dataset.devtools = list.join(' ').trim()
|
67
|
-
}
|
68
|
-
|
69
|
-
function stop () {
|
70
|
-
if (!tray) return
|
71
|
-
tray
|
72
|
-
.querySelectorAll('.devtool')
|
73
|
-
.forEach(devtool => disable(devtool.getAttribute('name')))
|
74
|
-
tray.remove()
|
75
|
-
tray = null
|
76
|
-
}
|
77
|
-
|
78
|
-
function start () {
|
79
|
-
stop()
|
80
|
-
tray = document.createElement('div')
|
81
|
-
tray.id = 'reflex-behaviors-devtools'
|
82
|
-
tray.innerHTML = `
|
83
|
-
<strong>DEVTOOLS</strong>
|
84
|
-
<div name="toggle" class="devtool">
|
85
|
-
<input name="toggle-checkbox" value="toggle" type="checkbox">
|
86
|
-
<label for="toggle-checkbox">toggles<small>(trigger/target)</small></label>
|
87
|
-
</div>
|
88
|
-
<button data-action='close'>X</button>
|
89
|
-
`
|
90
|
-
document.body.appendChild(tray)
|
91
|
-
tray
|
92
|
-
.querySelector('button[data-action=close]')
|
93
|
-
.addEventListener('click', () => stop())
|
94
|
-
tray.querySelectorAll('.devtool').forEach(devtool => {
|
95
|
-
devtool.querySelector('input').addEventListener('change', event => {
|
96
|
-
event.target.checked
|
97
|
-
? enable(event.target.value)
|
98
|
-
: disable(event.target.value)
|
99
|
-
})
|
100
|
-
devtool.addEventListener('click', event => {
|
101
|
-
if (event.target.tagName.match(/input/i)) return
|
102
|
-
event.target
|
103
|
-
.closest('.devtool')
|
104
|
-
.querySelector('input')
|
105
|
-
.click()
|
106
|
-
})
|
107
|
-
})
|
108
|
-
}
|
109
|
-
|
110
|
-
export default {
|
111
|
-
disable,
|
112
|
-
enable,
|
113
|
-
isEnabled,
|
114
|
-
start,
|
115
|
-
stop,
|
116
|
-
tooltip
|
117
|
-
}
|
5
|
+
export default { restart, start, stop }
|
@@ -0,0 +1,83 @@
|
|
1
|
+
import { appendHTML } from './dom'
|
2
|
+
import DevtoolElement from './elements/devtool_element'
|
3
|
+
import SupervisorElement from './elements/supervisor_element'
|
4
|
+
import TooltipElement from './elements/tooltip_element'
|
5
|
+
|
6
|
+
customElements.define('reflex-behaviors-devtool', DevtoolElement)
|
7
|
+
customElements.define('reflex-behaviors-devtool-supervisor', SupervisorElement)
|
8
|
+
customElements.define('reflex-behaviors-devools-tooltip', TooltipElement)
|
9
|
+
|
10
|
+
let supervisorElement
|
11
|
+
|
12
|
+
function stop () {
|
13
|
+
if (!supervisorElement) return
|
14
|
+
supervisorElement.close()
|
15
|
+
supervisorElement.dispatchEvent(
|
16
|
+
new CustomEvent('reflex-behaviors:devtools-stop', {
|
17
|
+
bubbles: true
|
18
|
+
})
|
19
|
+
)
|
20
|
+
supervisorElement = null
|
21
|
+
}
|
22
|
+
|
23
|
+
function start () {
|
24
|
+
appendHTML(
|
25
|
+
'<reflex-behaviors-devtool-supervisor></reflex-behaviors-devtool-supervisor>'
|
26
|
+
)
|
27
|
+
supervisorElement = document.body.querySelector(
|
28
|
+
'reflex-behaviors-devtool-supervisor'
|
29
|
+
)
|
30
|
+
supervisorElement.dispatchEvent(
|
31
|
+
new CustomEvent('reflex-behaviors:devtools-start', {
|
32
|
+
bubbles: true
|
33
|
+
})
|
34
|
+
)
|
35
|
+
}
|
36
|
+
|
37
|
+
function restart () {
|
38
|
+
const enabledList = supervisorElement
|
39
|
+
? Object.keys(supervisorElement.enabledDevtools)
|
40
|
+
: []
|
41
|
+
|
42
|
+
stop()
|
43
|
+
start()
|
44
|
+
|
45
|
+
supervisorElement.devtoolElements.forEach(el => {
|
46
|
+
if (enabledList.includes(el.name)) el.check()
|
47
|
+
})
|
48
|
+
}
|
49
|
+
|
50
|
+
let restartTimeout
|
51
|
+
function debouncedRestart () {
|
52
|
+
clearTimeout(restartTimeout)
|
53
|
+
restartTimeout = setTimeout(restart, 25)
|
54
|
+
}
|
55
|
+
addEventListener('turbo:load', debouncedRestart)
|
56
|
+
addEventListener('turbo-frame:load', debouncedRestart)
|
57
|
+
addEventListener('turbo-reflex:success', debouncedRestart)
|
58
|
+
addEventListener('turbo-reflex:finish', debouncedRestart)
|
59
|
+
|
60
|
+
function register (name, label) {
|
61
|
+
if (!supervisorElement) return
|
62
|
+
return appendHTML(
|
63
|
+
`
|
64
|
+
<reflex-behaviors-devtool name="${name}" slot="devtool">
|
65
|
+
<span slot="label">${label}</span>
|
66
|
+
</reflex-behaviors-devtool>
|
67
|
+
`,
|
68
|
+
supervisorElement
|
69
|
+
)
|
70
|
+
}
|
71
|
+
|
72
|
+
function enabled (name) {
|
73
|
+
if (!supervisorElement) return false
|
74
|
+
return supervisorElement.enabledDevtools[name]
|
75
|
+
}
|
76
|
+
|
77
|
+
export default {
|
78
|
+
enabled,
|
79
|
+
register,
|
80
|
+
restart: debouncedRestart,
|
81
|
+
start,
|
82
|
+
stop
|
83
|
+
}
|
@@ -0,0 +1,150 @@
|
|
1
|
+
import { appendHTML, addHighlight, removeHighlight } from './dom'
|
2
|
+
import supervisor from './supervisor'
|
3
|
+
|
4
|
+
document.addEventListener('reflex-behaviors:devtools-start', () =>
|
5
|
+
supervisor.register('toggle', 'toggles<small>(trigger/target)</small>')
|
6
|
+
)
|
7
|
+
|
8
|
+
const triggerTooltipId = 'toggle-trigger-tooltip'
|
9
|
+
const targetTooltipId = 'toggle-target-tooltip'
|
10
|
+
|
11
|
+
addEventListener('click', () => setTimeout(removeTooltips))
|
12
|
+
|
13
|
+
function removeTooltips () {
|
14
|
+
const ids = [triggerTooltipId, targetTooltipId]
|
15
|
+
ids.forEach(id => {
|
16
|
+
const el = document.getElementById(id)
|
17
|
+
if (el) el.remove()
|
18
|
+
})
|
19
|
+
}
|
20
|
+
|
21
|
+
function appendTooltip (id, title, content, options = {}) {
|
22
|
+
let { backgroundColor, color, emphaisColor, position } = options
|
23
|
+
color = color || 'white'
|
24
|
+
position = position || 'top'
|
25
|
+
|
26
|
+
appendHTML(`
|
27
|
+
<reflex-behaviors-devools-tooltip id="${id}" position="${position}" background-color="${backgroundColor}" color="${color}" emphasis-color="${emphaisColor}">
|
28
|
+
<div slot='title'>${title}</div>
|
29
|
+
${content}
|
30
|
+
</reflex-behaviors-devools-tooltip>
|
31
|
+
`)
|
32
|
+
return document.getElementById(id)
|
33
|
+
}
|
34
|
+
|
35
|
+
export default class ToggleDevtool {
|
36
|
+
constructor (trigger) {
|
37
|
+
this.name = 'toggle'
|
38
|
+
this.reflex = trigger.dataset.turboReflex
|
39
|
+
this.trigger = trigger
|
40
|
+
this.target = trigger.target
|
41
|
+
this.renderingPartial = trigger.renderingPartial
|
42
|
+
this.renderingElement = trigger.renderingElement
|
43
|
+
this.renderingElementId = this.renderingElement
|
44
|
+
? this.renderingElement.id
|
45
|
+
: null
|
46
|
+
|
47
|
+
document.addEventListener('reflex-behaviors:devtool-enable', event => {
|
48
|
+
const { name } = event.detail
|
49
|
+
if (name === this.name)
|
50
|
+
addHighlight(this.trigger, { color: 'red', offset: '2px' })
|
51
|
+
})
|
52
|
+
|
53
|
+
document.addEventListener('reflex-behaviors:devtool-disable', event => {
|
54
|
+
const { name } = event.detail
|
55
|
+
if (name === this.name) removeHighlight(this.trigger)
|
56
|
+
})
|
57
|
+
}
|
58
|
+
|
59
|
+
get enabled () {
|
60
|
+
return supervisor.enabled(this.name)
|
61
|
+
}
|
62
|
+
|
63
|
+
show () {
|
64
|
+
if (!this.enabled) return
|
65
|
+
this.hide()
|
66
|
+
this.createTriggerTooltip()
|
67
|
+
this.createTargetTooltip()
|
68
|
+
addHighlight(this.target, { color: 'blue', offset: '-2px' })
|
69
|
+
addHighlight(this.renderingElement, {
|
70
|
+
color: 'turquoise',
|
71
|
+
offset: '4px',
|
72
|
+
width: '4px'
|
73
|
+
})
|
74
|
+
|
75
|
+
console.table({
|
76
|
+
trigger: { id: this.trigger.id, partial: this.trigger.partial },
|
77
|
+
target: { id: this.target.id, partial: this.target.partial },
|
78
|
+
[this.reflex]: {
|
79
|
+
id: this.renderingElementId,
|
80
|
+
partial: this.renderingPartial
|
81
|
+
}
|
82
|
+
})
|
83
|
+
}
|
84
|
+
|
85
|
+
hide () {
|
86
|
+
this.destroyTriggerTooltip()
|
87
|
+
this.destroyTargetTooltip()
|
88
|
+
removeHighlight(this.target)
|
89
|
+
removeHighlight(this.renderingElement)
|
90
|
+
}
|
91
|
+
|
92
|
+
createTriggerTooltip () {
|
93
|
+
const title = `TRIGGER (targets: ${this.trigger.controls})`
|
94
|
+
const content = this.trigger.viewStack
|
95
|
+
.map(view => {
|
96
|
+
return this.trigger.sharedViews.includes(view)
|
97
|
+
? `<div slot="emphasis">${view}</div>`
|
98
|
+
: `<div slot="normal">${view}</div>`
|
99
|
+
}, this)
|
100
|
+
.join('')
|
101
|
+
|
102
|
+
this.triggerTooltip = appendTooltip(triggerTooltipId, title, content, {
|
103
|
+
backgroundColor: 'pink',
|
104
|
+
emphaisColor: 'darkred'
|
105
|
+
})
|
106
|
+
|
107
|
+
const coords = this.trigger.coordinates
|
108
|
+
const top = Math.ceil(coords.top - this.triggerTooltip.offsetHeight - 5)
|
109
|
+
const left = Math.ceil(coords.left)
|
110
|
+
this.triggerTooltip.style.top = `${top}px`
|
111
|
+
this.triggerTooltip.style.left = `${left}px`
|
112
|
+
}
|
113
|
+
|
114
|
+
destroyTriggerTooltip () {
|
115
|
+
if (!this.triggerTooltip) return
|
116
|
+
this.triggerTooltip.remove()
|
117
|
+
delete this.triggerTooltip
|
118
|
+
}
|
119
|
+
|
120
|
+
createTargetTooltip () {
|
121
|
+
if (!this.target) return
|
122
|
+
|
123
|
+
const title = `TARGET (id: ${this.target.id})`
|
124
|
+
const content = this.target.viewStack
|
125
|
+
.map(view => {
|
126
|
+
return this.trigger.sharedViews.includes(view)
|
127
|
+
? `<div slot="emphasis">${view}</div>`
|
128
|
+
: `<div slot="normal">${view}</div>`
|
129
|
+
}, this)
|
130
|
+
.join('')
|
131
|
+
|
132
|
+
this.targetTooltip = appendTooltip(targetTooltipId, title, content, {
|
133
|
+
backgroundColor: 'lightskyblue',
|
134
|
+
emphaisColor: 'blue',
|
135
|
+
position: 'bottom'
|
136
|
+
})
|
137
|
+
|
138
|
+
const coords = this.target.coordinates
|
139
|
+
const top = Math.ceil(coords.top + coords.height + 4)
|
140
|
+
const left = Math.ceil(coords.left)
|
141
|
+
this.targetTooltip.style.top = `${top}px`
|
142
|
+
this.targetTooltip.style.left = `${left}px`
|
143
|
+
}
|
144
|
+
|
145
|
+
destroyTargetTooltip () {
|
146
|
+
if (!this.targetTooltip) return
|
147
|
+
this.targetTooltip.remove()
|
148
|
+
delete this.targetTooltip
|
149
|
+
}
|
150
|
+
}
|
@@ -1,6 +1,14 @@
|
|
1
1
|
export default class ReflexElement extends HTMLElement {
|
2
|
+
constructor () {
|
3
|
+
super()
|
4
|
+
this.devtool = 'unknown'
|
5
|
+
this.attachShadow({ mode: 'open' })
|
6
|
+
this.shadowRoot.innerHTML = '<slot></slot>'
|
7
|
+
}
|
8
|
+
|
2
9
|
connectedCallback () {
|
3
10
|
this.ensureId()
|
11
|
+
this.dataset.elementOrigin = 'hopsoft/reflex_behaviors'
|
4
12
|
}
|
5
13
|
|
6
14
|
ensureId () {
|
@@ -23,6 +31,10 @@ export default class ReflexElement extends HTMLElement {
|
|
23
31
|
return JSON.parse(this.dataset.viewStack)
|
24
32
|
}
|
25
33
|
|
34
|
+
get partial () {
|
35
|
+
return this.viewStack[0]
|
36
|
+
}
|
37
|
+
|
26
38
|
get coordinates () {
|
27
39
|
const rect = this.getBoundingClientRect()
|
28
40
|
return {
|