@csedl/hotwire-svelte-helpers 0.1.0 → 0.1.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.
- package/.idea/dictionaries/project.xml +7 -0
- package/README.md +34 -29
- package/index.js +8 -15
- package/package.json +8 -2
- package/src/lib/config.js +74 -0
- package/src/{floating-ui-functions.js → lib/floating-ui-functions.js} +1 -1
- package/src/{utils.js → lib/utils.js} +18 -18
- package/src/{dropdown-controller.js → stimulus/dropdown-controller.js} +4 -2
- package/src/{move-panels-controller.js → stimulus/move-panels-controller.js} +2 -2
- package/src/{tooltip-controller.js → stimulus/tooltip-controller.js} +3 -3
- package/src/svelte/cleanMount.js +80 -0
- package/src/config.js +0 -114
package/README.md
CHANGED
|
@@ -1,44 +1,49 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Hotwire Svelte Helpers
|
|
2
|
+
|
|
3
|
+
is the successor of the `@csedl/stimulus-dropdown` package
|
|
2
4
|
|
|
3
5
|
Dropdown with stimulus, based on floating-UI.
|
|
4
6
|
|
|
5
7
|
**Links:**
|
|
6
|
-
- [Online Demo App](https://
|
|
7
|
-
- [Ruby Gem: csedl-stimulus-
|
|
8
|
+
- [Online Demo App](https://hotwire-svelte-helpers.sedlmair.ch/)
|
|
9
|
+
- [Ruby Gem: csedl-stimulus-dropdownSvelte](https://gitlab.com/sedl/csedl-stimulus-dropdown)
|
|
8
10
|
- [How we are building a Rails App](https://dev.to/chmich/setup-vite-svelte-inertia-stimulus-bootstrap-foundation-on-rails-7-overview-1bk1)
|
|
9
11
|
|
|
10
12
|
## Import and config
|
|
11
13
|
|
|
12
14
|
```javascript
|
|
13
|
-
import {
|
|
14
|
-
|
|
15
|
+
import {HotwireSvelteHelpers} from "@csedl/hotwire-svelte-helpers"
|
|
16
|
+
HotwireSvelteHelpers.debug = true
|
|
17
|
+
HotwireSvelteHelpers.initializeOverlays()
|
|
15
18
|
```
|
|
16
19
|
|
|
17
20
|
All configurations and their defaults are as follows:
|
|
18
21
|
|
|
19
22
|
```javascript
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
23
|
+
HotwireSvelteHelpers.initializeOverlays({
|
|
24
|
+
debug: false,
|
|
25
|
+
closeButtonSelector: '.close-button',
|
|
26
|
+
dropdownContentSelector: '.content',
|
|
27
|
+
// when a data-src attribute is added to the panel, the content-tag is replaced by the result of the xhr-response
|
|
28
|
+
tooltipContentSelector: '.content',
|
|
29
|
+
// same as dropdownContentSelector
|
|
30
|
+
addArrow: true,
|
|
31
|
+
// add element with id #arrow within the panel, on opening
|
|
32
|
+
persistTooltipOnClick: false
|
|
33
|
+
// clicking on the tooltip-label causes the tooltip-panel to persist open
|
|
34
|
+
// this may mostly be helpful for development, so you may make it environment-dependent
|
|
35
|
+
})
|
|
31
36
|
```
|
|
32
37
|
|
|
33
38
|
## Example
|
|
34
39
|
|
|
35
|
-
There is a [online example app](https://
|
|
40
|
+
There is a [online example app](https://hotwire-svelte-helpers.sedlmair.ch/)
|
|
36
41
|
|
|
37
42
|
```html
|
|
38
43
|
<div data-controller="csedl-dropdown" data-panel-id="dropdown-panel-3h5k7l4">
|
|
39
44
|
Button
|
|
40
45
|
</div>
|
|
41
|
-
<div id="dropdown-panel-3h5k7l4" class="hide
|
|
46
|
+
<div id="dropdown-panel-3h5k7l4" class="hide dropdownSvelte-panel-example-class">
|
|
42
47
|
... any content
|
|
43
48
|
</div>
|
|
44
49
|
```
|
|
@@ -53,35 +58,35 @@ There is a [online example app](https://stimulus-dropdown.sedlmair.ch/)
|
|
|
53
58
|
|
|
54
59
|
## Close on click outside
|
|
55
60
|
|
|
56
|
-
When a
|
|
61
|
+
When a dropdownSvelte is open, it closes when clicking outside a panel.
|
|
57
62
|
|
|
58
63
|
This behaviour can be stopped on:
|
|
59
64
|
|
|
60
|
-
- The clicked element or its parent elements has the `data-
|
|
65
|
+
- The clicked element or its parent elements has the `data-dropdownSvelte-persist` (not: `data-dropdownSvelte-persist="false"`) attribute.
|
|
61
66
|
- the event has the attribute `event.detail.dataDropdownPersist` set to true.
|
|
62
67
|
|
|
63
68
|
## Flexibility
|
|
64
69
|
|
|
65
|
-
The functions of this package are intended to give flexibility on various ways building a
|
|
70
|
+
The functions of this package are intended to give flexibility on various ways building a dropdownSvelte.
|
|
66
71
|
|
|
67
72
|
- Stimulus Controller with Rails Helper
|
|
68
73
|
- Example: [Stimulus controller within this package](https://gitlab.com/sedl/csedl-stimulus-dropdown-js/-/blob/main/src/dropdown-controller.js?ref_type=heads)
|
|
69
74
|
- Stimulus with Svelte component
|
|
70
75
|
- Example: [Stimulus controller on example app](https://gitlab.com/sedl/stimulusfloatingdropdown/-/blob/main/app/frontend/javascript/svelte-dropdown-controller.js?ref_type=heads)
|
|
71
76
|
|
|
72
|
-
**Important:** When creating or initializing the
|
|
77
|
+
**Important:** When creating or initializing the dropdownSvelte,
|
|
73
78
|
always call the initialize function before attaching a listener to the panel's close event.
|
|
74
79
|
The initialize function adds a `close` event listener to the panel that executes the `onPanelClose` function.
|
|
75
80
|
If your custom close function destroys the panel, this has to be done after the `onPanelClose` is fired by the `close` event.
|
|
76
81
|
|
|
77
82
|
## Requirements
|
|
78
83
|
|
|
79
|
-
- The class `
|
|
84
|
+
- The class `dropdownSvelte-panel-example-class` must be set to `position: absolute;`.
|
|
80
85
|
- The class `hide` must be set to `display: none;`.
|
|
81
86
|
|
|
82
87
|
## Options
|
|
83
88
|
|
|
84
|
-
- If there is an element with ID `arrow` inside the
|
|
89
|
+
- If there is an element with ID `arrow` inside the dropdownSvelte panel, it is treated as described on [floating-ui](https://floating-ui.com/docs/arrow).
|
|
85
90
|
- The `data-placement` attribute on the panel can be used to control positioning, see [floating-ui/placements](https://floating-ui.com/docs/tutorial#placements).
|
|
86
91
|
|
|
87
92
|
## Events
|
|
@@ -109,12 +114,12 @@ Event Triggers on the panel element:
|
|
|
109
114
|
If the panels are rendered to a different location than the button (see z-index on [rails-app](https://gitlab.com/sedl/stimulusfloatingdropdown)), within a scrollable (e.g.) container, the button would scroll away from the panel. For such cases, add this both data-attributes to the scrollable element:
|
|
110
115
|
|
|
111
116
|
```html
|
|
112
|
-
<div data-controller="csedl-place-
|
|
117
|
+
<div data-controller="csedl-place-dropdownSvelte-panels" data-on="scroll" data-run-after="500" style="overflow: scroll;">
|
|
113
118
|
...
|
|
114
119
|
</div>
|
|
115
120
|
```
|
|
116
121
|
|
|
117
|
-
Now, on scrolling, it searches for all
|
|
122
|
+
Now, on scrolling, it searches for all dropdownSvelte-buttons (by class-name `has-open-panel`) and triggers the `place-panel` event there.
|
|
118
123
|
|
|
119
124
|
**Options**
|
|
120
125
|
|
|
@@ -151,15 +156,15 @@ makes a tooltip.
|
|
|
151
156
|
|
|
152
157
|
It adds the class `tooltip-is-visible` to the tooltip label while the tooltip is visible.
|
|
153
158
|
|
|
154
|
-
`data-src` attribute is working similar to
|
|
159
|
+
`data-src` attribute is working similar to dropdownSvelte
|
|
155
160
|
|
|
156
161
|
## Rails Helpers
|
|
157
162
|
|
|
158
163
|
There is a corresponding rails gem, on [GitLab](https://gitlab.com/sedl/csedl-stimulus-dropdown)
|
|
159
164
|
|
|
160
|
-
## Stimulus Usage in stimulus-
|
|
165
|
+
## Stimulus Usage in stimulus-dropdownSvelte
|
|
161
166
|
|
|
162
|
-
This package uses Stimulus unconventionally to initialize and toggle external
|
|
167
|
+
This package uses Stimulus unconventionally to initialize and toggle external dropdownSvelte or tooltip panels
|
|
163
168
|
(via data-panel-id) rather than managing child elements within the controller’s scope,
|
|
164
169
|
as is typical in Stimulus documentation.
|
|
165
170
|
|
package/index.js
CHANGED
|
@@ -1,19 +1,12 @@
|
|
|
1
1
|
|
|
2
|
-
import
|
|
2
|
+
import HotwireSvelteHelpers from "./src/lib/config.js";
|
|
3
|
+
import { cleanMount, unmountAllDetached } from './src/svelte/cleanMount';
|
|
4
|
+
import {initializeDropdown, onPanelOpen, getOrSetPanelId, debugLog} from './src/lib/utils.js'
|
|
3
5
|
|
|
4
|
-
import { Application } from "@hotwired/stimulus"
|
|
5
|
-
window.Stimulus = Application.start()
|
|
6
6
|
|
|
7
|
-
// initialize dropdowns
|
|
8
|
-
import dc from './src/dropdown-controller'
|
|
9
|
-
Stimulus.register('csedl-dropdown', dc)
|
|
10
7
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
import ppc from './src/move-panels-controller'
|
|
17
|
-
Stimulus.register('csedl-move-panels', ppc)
|
|
18
|
-
|
|
19
|
-
export { StimulusDropdown }
|
|
8
|
+
export {
|
|
9
|
+
HotwireSvelteHelpers,
|
|
10
|
+
cleanMount, unmountAllDetached,
|
|
11
|
+
initializeDropdown, onPanelOpen, getOrSetPanelId, debugLog
|
|
12
|
+
}
|
package/package.json
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@csedl/hotwire-svelte-helpers",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Hotwire + Svelte helpers for Rails: Stimulus floating dropdowns/toolips + Svelte global panels/modals + Rails form error mapping + Turbo-friendly utilities. Build together with the rubygem svelte-on-rails and its npm-package.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"test": "echo \"Error: no test specified\" && exit 1"
|
|
8
8
|
},
|
|
9
|
+
"exports": {
|
|
10
|
+
".": [
|
|
11
|
+
"./index.js",
|
|
12
|
+
"./src/utils.js"
|
|
13
|
+
]
|
|
14
|
+
},
|
|
9
15
|
"repository": {
|
|
10
16
|
"type": "git",
|
|
11
17
|
"url": "git+https://gitlab.com/sedl/csedl-hotwire-svelte-helpers"
|
|
@@ -20,7 +26,7 @@
|
|
|
20
26
|
"bugs": {
|
|
21
27
|
"url": "https://gitlab.com/sedl/csedl-stimulus-dropdown-js/-/issues"
|
|
22
28
|
},
|
|
23
|
-
"homepage": "https://gitlab.com/sedl/csedl-
|
|
29
|
+
"homepage": "https://gitlab.com/sedl/csedl-hotwire-svelte-helpers",
|
|
24
30
|
"dependencies": {
|
|
25
31
|
"@floating-ui/dom": ">=1.5.1",
|
|
26
32
|
"@hotwired/stimulus": ">=3.2.1"
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import {debugLog} from "./utils.js";
|
|
2
|
+
import { Application } from "@hotwired/stimulus"
|
|
3
|
+
import dc from '../stimulus/dropdown-controller'
|
|
4
|
+
import ttc from '../stimulus/tooltip-controller'
|
|
5
|
+
import ppc from '../stimulus/move-panels-controller'
|
|
6
|
+
|
|
7
|
+
// DEFAULTS
|
|
8
|
+
let _debug = false;
|
|
9
|
+
|
|
10
|
+
let _close_on_click_outside_listener_added = false;
|
|
11
|
+
|
|
12
|
+
const HotwireSvelteHelpers = {
|
|
13
|
+
|
|
14
|
+
// debug
|
|
15
|
+
|
|
16
|
+
get debug() {
|
|
17
|
+
return _debug;
|
|
18
|
+
},
|
|
19
|
+
set debug(value) {
|
|
20
|
+
if (typeof value !== "boolean") {
|
|
21
|
+
throw new Error("Debug value must be true or false");
|
|
22
|
+
}
|
|
23
|
+
_debug = value;
|
|
24
|
+
debugLog('debugging active')
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
overlays: {
|
|
28
|
+
closeButtonSelector: '.close-button',
|
|
29
|
+
dropdownContentSelector: '.content',
|
|
30
|
+
tooltipContentSelector: '.content',
|
|
31
|
+
addArrow: true,
|
|
32
|
+
persistTooltipOnClick: false,
|
|
33
|
+
closeOnClickOutsideListenerAdded: false,
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
initializeOverlays(options = {}) {
|
|
37
|
+
const overlays = this.overlays;
|
|
38
|
+
|
|
39
|
+
if (options.closeButtonSelector !== undefined) {
|
|
40
|
+
overlays.closeButtonSelector = options.closeButtonSelector;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (options.dropdownContentSelector !== undefined) {
|
|
44
|
+
overlays.dropdownContentSelector = options.dropdownContentSelector;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (options.tooltipContentSelector !== undefined) {
|
|
48
|
+
overlays.tooltipContentSelector = options.tooltipContentSelector;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (options.addArrow !== undefined) {
|
|
52
|
+
overlays.addArrow = options.addArrow;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (options.persistTooltipOnClick !== undefined) {
|
|
56
|
+
overlays.persistTooltipOnClick = options.persistTooltipOnClick;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
window.Stimulus = Application.start()
|
|
60
|
+
|
|
61
|
+
Stimulus.register('csedl-dropdown', dc)
|
|
62
|
+
Stimulus.register('csedl-tooltip', ttc)
|
|
63
|
+
Stimulus.register('csedl-move-panels', ppc)
|
|
64
|
+
|
|
65
|
+
debugLog('Overlays active, driven by Stimulus')
|
|
66
|
+
|
|
67
|
+
return overlays;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
window.HotwireSvelteHelpers = HotwireSvelteHelpers;
|
|
73
|
+
|
|
74
|
+
export default HotwireSvelteHelpers;
|
|
@@ -10,7 +10,7 @@ export function onPanelOpen(event, button) {
|
|
|
10
10
|
closeOnOutsideClick(event)
|
|
11
11
|
|
|
12
12
|
// add arrow
|
|
13
|
-
if (window.
|
|
13
|
+
if (window.HotwireSvelteHelpers.overlays.addArrow) {
|
|
14
14
|
if (!panel.querySelector(':scope > #arrow')) {
|
|
15
15
|
const arrowTag = document.createElement('div');
|
|
16
16
|
arrowTag.id = 'arrow';
|
|
@@ -28,7 +28,7 @@ export function onPanelOpen(event, button) {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
// Set status attribute
|
|
31
|
-
panel.setAttribute('data-stimulus-
|
|
31
|
+
panel.setAttribute('data-stimulus-overlay-panel-status', 'open');
|
|
32
32
|
button.classList.add('has-open-panel');
|
|
33
33
|
|
|
34
34
|
// Dispatch events
|
|
@@ -39,8 +39,8 @@ export function onPanelOpen(event, button) {
|
|
|
39
39
|
button.dispatchEvent(buttonOpenEvent);
|
|
40
40
|
|
|
41
41
|
// Add listener for closing on outside click
|
|
42
|
-
if (!window.
|
|
43
|
-
window.
|
|
42
|
+
if (!window.HotwireSvelteHelpers.overlays.closeOnClickOutsideListenerAdded) {
|
|
43
|
+
window.HotwireSvelteHelpers.overlays.closeOnClickOutsideListenerAdded = true;
|
|
44
44
|
window.addEventListener('click', closeOnOutsideClick);
|
|
45
45
|
debugLog('Listener for close panels on click outside added');
|
|
46
46
|
}
|
|
@@ -58,7 +58,7 @@ export function onPanelOpen(event, button) {
|
|
|
58
58
|
} else {
|
|
59
59
|
const button = document.querySelector(`[data-panel-id="${panel.id}"]`);
|
|
60
60
|
const ctrl = button.getAttribute('data-controller');
|
|
61
|
-
const config = window.
|
|
61
|
+
const config = window.HotwireSvelteHelpers.overlays;
|
|
62
62
|
const contentSelector = config.dropdownContentSelector;
|
|
63
63
|
|
|
64
64
|
if (ctrl === 'csedl-dropdown' && contentSelector) {
|
|
@@ -92,13 +92,13 @@ export function onPanelClose(button) {
|
|
|
92
92
|
|
|
93
93
|
// Update status attributes
|
|
94
94
|
button.classList.remove('has-open-panel');
|
|
95
|
-
panel.setAttribute('data-stimulus-
|
|
95
|
+
panel.setAttribute('data-stimulus-overlay-panel-status', 'closed');
|
|
96
96
|
|
|
97
97
|
// Remove outside click listener if no panels are open
|
|
98
|
-
const openPanels = document.querySelectorAll("[data-stimulus-
|
|
98
|
+
const openPanels = document.querySelectorAll("[data-stimulus-overlay-panel-status='open']");
|
|
99
99
|
debugLog(`panel closed, still open: ${openPanels.length}`);
|
|
100
|
-
if (window.
|
|
101
|
-
window.
|
|
100
|
+
if (window.HotwireSvelteHelpers.closeOnClickOutsideListenerAdded && openPanels.length === 0) {
|
|
101
|
+
window.HotwireSvelteHelpers.closeOnClickOutsideListenerAdded = false;
|
|
102
102
|
window.removeEventListener('click', closeOnOutsideClick);
|
|
103
103
|
debugLog('Listener for close panels on click outside removed');
|
|
104
104
|
}
|
|
@@ -114,10 +114,10 @@ export function closeOnOutsideClick(ev) {
|
|
|
114
114
|
let btn = ev.target;
|
|
115
115
|
let parentPanelIds = [];
|
|
116
116
|
|
|
117
|
-
const persistElement = ev.target.closest('[data-
|
|
117
|
+
const persistElement = ev.target.closest('[data-overlay-persist]');
|
|
118
118
|
if (persistElement) {
|
|
119
|
-
if (persistElement.getAttribute('data-
|
|
120
|
-
debugLog('closing panel prevented because the target element has attribute "data-
|
|
119
|
+
if (persistElement.getAttribute('data-overlay-persist') !== 'false') {
|
|
120
|
+
debugLog('closing panel prevented because the target element has attribute "data-overlay-persist"');
|
|
121
121
|
return false;
|
|
122
122
|
}
|
|
123
123
|
}
|
|
@@ -125,7 +125,7 @@ export function closeOnOutsideClick(ev) {
|
|
|
125
125
|
debugLog('closeOnOutsideClick called, event.target:', ev.target);
|
|
126
126
|
|
|
127
127
|
while (true) {
|
|
128
|
-
const parentPanel = btn.closest("[data-stimulus-
|
|
128
|
+
const parentPanel = btn.closest("[data-stimulus-overlay-panel-status='open']");
|
|
129
129
|
if (parentPanel) {
|
|
130
130
|
parentPanelIds.push(parentPanel.id);
|
|
131
131
|
btn = document.querySelector(`[data-panel-id="${parentPanel.id}"]`);
|
|
@@ -134,7 +134,7 @@ export function closeOnOutsideClick(ev) {
|
|
|
134
134
|
}
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
-
const openPanels = document.querySelectorAll("[data-stimulus-
|
|
137
|
+
const openPanels = document.querySelectorAll("[data-stimulus-overlay-panel-status='open']");
|
|
138
138
|
for (const panel of openPanels) {
|
|
139
139
|
if (!parentPanelIds.includes(panel.id)) {
|
|
140
140
|
const ev = new Event('close');
|
|
@@ -157,7 +157,7 @@ export function initializeDropdown(button) {
|
|
|
157
157
|
}
|
|
158
158
|
|
|
159
159
|
// Add close button functionality
|
|
160
|
-
const selector = window.
|
|
160
|
+
const selector = window.HotwireSvelteHelpers.overlays.closeButtonSelector;
|
|
161
161
|
const closeButtons = panel.querySelectorAll(selector);
|
|
162
162
|
for (const btn of closeButtons) {
|
|
163
163
|
btn.addEventListener('click', () => {
|
|
@@ -179,13 +179,13 @@ export function initializeDropdown(button) {
|
|
|
179
179
|
// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
|
180
180
|
|
|
181
181
|
export function debugLog(message, object) {
|
|
182
|
-
if (window.
|
|
182
|
+
if (window.HotwireSvelteHelpers.debug) {
|
|
183
183
|
|
|
184
184
|
if (object) {
|
|
185
|
-
console.log(`[
|
|
185
|
+
console.log(`[HSH] ${message}`, object);
|
|
186
186
|
|
|
187
187
|
} else {
|
|
188
|
-
console.log(`[
|
|
188
|
+
console.log(`[HSH] ${message}`);
|
|
189
189
|
}
|
|
190
190
|
|
|
191
191
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {Controller} from "@hotwired/stimulus"
|
|
2
|
-
import {initializeDropdown, onPanelOpen} from "
|
|
3
|
-
import {debugLog} from "
|
|
2
|
+
import {initializeDropdown, onPanelOpen} from "../lib/utils.js";
|
|
3
|
+
import {debugLog} from "../lib/utils.js";
|
|
4
4
|
|
|
5
5
|
export default class extends Controller {
|
|
6
6
|
|
|
@@ -10,6 +10,8 @@ export default class extends Controller {
|
|
|
10
10
|
|
|
11
11
|
this.element.addEventListener('click', (e) => this.toggle(e))
|
|
12
12
|
|
|
13
|
+
debugLog('dropdown connected')
|
|
14
|
+
|
|
13
15
|
}
|
|
14
16
|
|
|
15
17
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {Controller} from "@hotwired/stimulus"
|
|
2
|
-
import {positionAllPanels, getAllOpenPanels} from "
|
|
3
|
-
import {debugLog} from "
|
|
2
|
+
import {positionAllPanels, getAllOpenPanels} from "../lib/floating-ui-functions";
|
|
3
|
+
import {debugLog} from "../lib/utils.js";
|
|
4
4
|
|
|
5
5
|
export default class extends Controller {
|
|
6
6
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {Controller} from "@hotwired/stimulus"
|
|
2
|
-
import {positionPanelByButton} from "
|
|
3
|
-
import {debugLog, onPanelOpen} from "
|
|
2
|
+
import {positionPanelByButton} from "../lib/floating-ui-functions.js";
|
|
3
|
+
import {debugLog, onPanelOpen} from "../lib/utils.js";
|
|
4
4
|
|
|
5
5
|
export default class extends Controller {
|
|
6
6
|
|
|
@@ -17,7 +17,7 @@ export default class extends Controller {
|
|
|
17
17
|
}
|
|
18
18
|
this.element.addEventListener('mouseenter', (e) => this.start_opening(e, panel_id, delay_sec * 1000))
|
|
19
19
|
this.element.addEventListener('mouseleave', (e) => this.close(e, panel_id))
|
|
20
|
-
if (window.
|
|
20
|
+
if (window.HotwireSvelteHelpers.persistTooltipOnClick) {
|
|
21
21
|
this.element.addEventListener('click', (e) => this.toggleByClick(e, panel_id))
|
|
22
22
|
}
|
|
23
23
|
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { mount, unmount } from 'svelte';
|
|
2
|
+
import { debugLog } from '../lib/utils.js';
|
|
3
|
+
|
|
4
|
+
const mountedByTarget = new WeakMap();
|
|
5
|
+
const trackedTargets = new Set();
|
|
6
|
+
|
|
7
|
+
function getTargetRecordList(target) {
|
|
8
|
+
let records = mountedByTarget.get(target);
|
|
9
|
+
|
|
10
|
+
if (!records) {
|
|
11
|
+
records = [];
|
|
12
|
+
mountedByTarget.set(target, records);
|
|
13
|
+
trackedTargets.add(target);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return records;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function cleanMount(component, options = {}) {
|
|
20
|
+
|
|
21
|
+
if (!options.target) {
|
|
22
|
+
throw new Error('[HSH] cleanMount: target is required');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (!component) {
|
|
26
|
+
throw new Error('[HSH] cleanMount: component is required');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const instance = mount(component, options);
|
|
30
|
+
|
|
31
|
+
const records = getTargetRecordList(options.target);
|
|
32
|
+
records.push({
|
|
33
|
+
target: options.target,
|
|
34
|
+
component: component,
|
|
35
|
+
instance: instance
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
debugLog(`cleanMount: mounted, stored: ${records.length} components`, instance)
|
|
39
|
+
|
|
40
|
+
return instance;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function unmountAllDetached() {
|
|
44
|
+
let unmountedCount = 0;
|
|
45
|
+
|
|
46
|
+
for (const target of Array.from(trackedTargets)) {
|
|
47
|
+
const records = mountedByTarget.get(target);
|
|
48
|
+
|
|
49
|
+
if (!records || records.length === 0) {
|
|
50
|
+
trackedTargets.delete(target);
|
|
51
|
+
mountedByTarget.delete(target);
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (target.isConnected) {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
for (const record of records) {
|
|
60
|
+
try {
|
|
61
|
+
unmount(record.instance);
|
|
62
|
+
unmountedCount++;
|
|
63
|
+
} catch (error) {
|
|
64
|
+
console.error('[HSH] unmountAllDetached: failed to unmount component', error);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
records.length = 0;
|
|
69
|
+
trackedTargets.delete(target);
|
|
70
|
+
mountedByTarget.delete(target);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (unmountedCount === 0) {
|
|
74
|
+
debugLog('unmountAllDetached: nothing to unmount')
|
|
75
|
+
} else {
|
|
76
|
+
debugLog(`unmountAllDetached: unmounted ${unmountedCount} components`)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return unmountedCount;
|
|
80
|
+
}
|
package/src/config.js
DELETED
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
import {debugLog} from "./utils.js";
|
|
2
|
-
|
|
3
|
-
// DEFAULTS
|
|
4
|
-
let _debug = false;
|
|
5
|
-
let _close_button_selector = '.close-button';
|
|
6
|
-
let _dropdown_content_selector = '.content';
|
|
7
|
-
let _tooltip_content_selector = '.content';
|
|
8
|
-
let _add_arrow = true;
|
|
9
|
-
let _persist_tooltip_on_click = false;
|
|
10
|
-
|
|
11
|
-
let _close_on_click_outside_listener_added = false;
|
|
12
|
-
|
|
13
|
-
const StimulusDropdown = {
|
|
14
|
-
|
|
15
|
-
// debug
|
|
16
|
-
|
|
17
|
-
get debug() {
|
|
18
|
-
return _debug;
|
|
19
|
-
},
|
|
20
|
-
set debug(value) {
|
|
21
|
-
if (typeof value !== "boolean") {
|
|
22
|
-
throw new Error("Debug value must be true or false");
|
|
23
|
-
}
|
|
24
|
-
_debug = value;
|
|
25
|
-
debugLog('debugging active')
|
|
26
|
-
},
|
|
27
|
-
|
|
28
|
-
// close button selector
|
|
29
|
-
|
|
30
|
-
get closeButtonSelector() {
|
|
31
|
-
return _close_button_selector;
|
|
32
|
-
},
|
|
33
|
-
set closeButtonSelector(string) {
|
|
34
|
-
if (typeof string !== "string") {
|
|
35
|
-
throw new Error("Close Button Selector must be a string");
|
|
36
|
-
}
|
|
37
|
-
_close_button_selector = string;
|
|
38
|
-
debugLog(`close button selector is: «${string}»`);
|
|
39
|
-
},
|
|
40
|
-
|
|
41
|
-
// content selector
|
|
42
|
-
|
|
43
|
-
get dropdownContentSelector() {
|
|
44
|
-
return _dropdown_content_selector;
|
|
45
|
-
},
|
|
46
|
-
set dropdownContentSelector(string) {
|
|
47
|
-
if (typeof string !== "string") {
|
|
48
|
-
throw new Error("Content Selector must be a string");
|
|
49
|
-
}
|
|
50
|
-
_dropdown_content_selector = string;
|
|
51
|
-
debugLog(`dropdown content selector is: «${string}»`);
|
|
52
|
-
},
|
|
53
|
-
|
|
54
|
-
// tooltip content selector
|
|
55
|
-
|
|
56
|
-
get tooltipContentSelector() {
|
|
57
|
-
return _tooltip_content_selector;
|
|
58
|
-
},
|
|
59
|
-
set tooltipContentSelector(string) {
|
|
60
|
-
if (typeof string !== "string") {
|
|
61
|
-
throw new Error("Tooltip Content Selector must be a string");
|
|
62
|
-
}
|
|
63
|
-
_tooltip_content_selector = string;
|
|
64
|
-
debugLog(`tooltip content selector is: «${string}»`);
|
|
65
|
-
},
|
|
66
|
-
|
|
67
|
-
// add arrow
|
|
68
|
-
|
|
69
|
-
get addArrow() {
|
|
70
|
-
return _add_arrow;
|
|
71
|
-
},
|
|
72
|
-
set addArrow(boolean) {
|
|
73
|
-
if (typeof boolean !== "boolean") {
|
|
74
|
-
throw new Error("Tooltip Content Selector must be a string");
|
|
75
|
-
}
|
|
76
|
-
_add_arrow = boolean;
|
|
77
|
-
debugLog(`add-arrow is set to: «${boolean}»`);
|
|
78
|
-
},
|
|
79
|
-
|
|
80
|
-
// add arrow
|
|
81
|
-
|
|
82
|
-
get persistTooltipOnClick() {
|
|
83
|
-
return _persist_tooltip_on_click;
|
|
84
|
-
},
|
|
85
|
-
set persistTooltipOnClick(boolean) {
|
|
86
|
-
if (typeof boolean !== "boolean") {
|
|
87
|
-
throw new Error("Tooltip Content Selector must be a string");
|
|
88
|
-
}
|
|
89
|
-
_persist_tooltip_on_click = boolean;
|
|
90
|
-
debugLog(`Persist tooltip on click is set to: «${boolean}»`);
|
|
91
|
-
},
|
|
92
|
-
|
|
93
|
-
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
94
|
-
// INTERNAL USE
|
|
95
|
-
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
96
|
-
|
|
97
|
-
// closeOnClickOutsideListenerAdded
|
|
98
|
-
|
|
99
|
-
get closeOnClickOutsideListenerAdded() {
|
|
100
|
-
return _close_on_click_outside_listener_added;
|
|
101
|
-
},
|
|
102
|
-
set closeOnClickOutsideListenerAdded(bool) {
|
|
103
|
-
if (typeof bool !== "boolean") {
|
|
104
|
-
throw new Error("closeOnClickOutsideListenerAdded must be a boolean");
|
|
105
|
-
}
|
|
106
|
-
_close_on_click_outside_listener_added = bool;
|
|
107
|
-
debugLog(`closeOnClickOutsideListenerAdded is: «${bool}»`);
|
|
108
|
-
},
|
|
109
|
-
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
window.StimulusDropdown = StimulusDropdown;
|
|
113
|
-
|
|
114
|
-
export default StimulusDropdown;
|