@rkosafo/cai.components 0.0.3 → 0.0.6
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/README.md +8 -8
- package/dist/baseEditor/index.svelte +32 -32
- package/dist/builders/filters/FilterBuilder.svelte +638 -638
- package/dist/forms/FormCheckbox/FormCheckbox.svelte +53 -53
- package/dist/forms/FormDatepicker/FormDatepicker.svelte +159 -159
- package/dist/forms/FormInput/FormInput.svelte +87 -87
- package/dist/forms/FormRadio/FormRadio.svelte +53 -53
- package/dist/forms/FormSelect/FormSelect.svelte +86 -86
- package/dist/forms/FormTextarea/FormTextarea.svelte +77 -77
- package/dist/forms/checkbox/Checkbox.svelte +82 -82
- package/dist/forms/checkbox/CheckboxButton.svelte +92 -92
- package/dist/forms/datepicker/Datepicker.svelte +706 -706
- package/dist/forms/form/Form.svelte +69 -69
- package/dist/forms/input/Input.svelte +363 -363
- package/dist/forms/label/Label.svelte +38 -38
- package/dist/forms/radio/Radio.svelte +48 -48
- package/dist/forms/radio/RadioButton.svelte +22 -22
- package/dist/forms/select/Select.svelte +50 -50
- package/dist/forms/textarea/Textarea.svelte +165 -165
- package/dist/layout/TF/Content/Content.svelte +28 -28
- package/dist/layout/TF/Header/Header.svelte +159 -159
- package/dist/layout/TF/Sidebar/Sidebar.svelte +74 -74
- package/dist/layout/TF/Wrapper/Wrapper.svelte +17 -17
- package/dist/themes/ThemeProvider.svelte +20 -20
- package/dist/typography/heading/Heading.svelte +35 -35
- package/dist/ui/accordion/Accordion.svelte +49 -49
- package/dist/ui/accordion/AccordionItem.svelte +173 -173
- package/dist/ui/alert/Alert.svelte +83 -83
- package/dist/ui/alertDialog/AlertDialog.svelte +40 -40
- package/dist/ui/avatar/Avatar.svelte +77 -77
- package/dist/ui/buttons/Button.svelte +102 -102
- package/dist/ui/buttons/GradientButton.svelte +59 -59
- package/dist/ui/datatable/Datatable.svelte +516 -516
- package/dist/ui/drawer/Drawer.svelte +280 -280
- package/dist/ui/dropdown/Dropdown.svelte +36 -36
- package/dist/ui/dropdown/DropdownDivider.svelte +11 -11
- package/dist/ui/dropdown/DropdownGroup.svelte +14 -14
- package/dist/ui/dropdown/DropdownHeader.svelte +14 -14
- package/dist/ui/dropdown/DropdownItem.svelte +52 -52
- package/dist/ui/footer/Footer.svelte +15 -15
- package/dist/ui/footer/FooterBrand.svelte +37 -37
- package/dist/ui/footer/FooterCopyright.svelte +45 -45
- package/dist/ui/footer/FooterIcon.svelte +22 -22
- package/dist/ui/footer/FooterLink.svelte +33 -33
- package/dist/ui/footer/FooterLinkGroup.svelte +13 -13
- package/dist/ui/indicator/Indicator.svelte +42 -42
- package/dist/ui/modal/Modal.svelte +265 -265
- package/dist/ui/notificationList/NotificationList.svelte +123 -123
- package/dist/ui/pageLoader/PageLoader.svelte +10 -10
- package/dist/ui/paginate/Paginate.svelte +96 -96
- package/dist/ui/tab/Tab.svelte +65 -65
- package/dist/ui/table/Table.svelte +385 -385
- package/dist/ui/tableLoader/TableLoader.svelte +24 -24
- package/dist/ui/toolbar/Toolbar.svelte +59 -59
- package/dist/ui/toolbar/ToolbarButton.svelte +56 -56
- package/dist/ui/toolbar/ToolbarGroup.svelte +43 -43
- package/dist/utils/Popper.svelte +257 -257
- package/dist/utils/closeButton/CloseButton.svelte +88 -88
- package/dist/utils/singleSelection.svelte.js +48 -48
- package/dist/youtube/index.svelte +12 -12
- package/package.json +7 -3
package/dist/utils/Popper.svelte
CHANGED
|
@@ -1,257 +1,257 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import type { Coords, Middleware, Placement, Strategy } from '@floating-ui/dom';
|
|
3
|
-
import * as dom from '@floating-ui/dom';
|
|
4
|
-
// import Arrow from './Arrow.svelte';
|
|
5
|
-
import { fade } from 'svelte/transition';
|
|
6
|
-
import { sineIn } from 'svelte/easing';
|
|
7
|
-
import clsx from 'clsx';
|
|
8
|
-
import type { ParamsType, PopperProps, TriggeredToggleEvent } from '../types/index.js';
|
|
9
|
-
|
|
10
|
-
let {
|
|
11
|
-
triggeredBy,
|
|
12
|
-
triggerDelay = 200,
|
|
13
|
-
trigger = 'click',
|
|
14
|
-
placement = 'top',
|
|
15
|
-
offset = 8,
|
|
16
|
-
arrow = false,
|
|
17
|
-
yOnly = false,
|
|
18
|
-
strategy = 'absolute',
|
|
19
|
-
reference,
|
|
20
|
-
middlewares = [dom.flip(), dom.shift()],
|
|
21
|
-
onbeforetoggle: _onbeforetoggle,
|
|
22
|
-
ontoggle: _ontoggle,
|
|
23
|
-
class: className = '',
|
|
24
|
-
arrowClass = '',
|
|
25
|
-
isOpen = $bindable(false),
|
|
26
|
-
transitionParams,
|
|
27
|
-
transition = fade,
|
|
28
|
-
children,
|
|
29
|
-
...restProps
|
|
30
|
-
}: PopperProps = $props();
|
|
31
|
-
|
|
32
|
-
let focusable: boolean = true;
|
|
33
|
-
let clickable: boolean = $derived(trigger === 'click');
|
|
34
|
-
let hoverable: boolean = $derived(trigger === 'hover');
|
|
35
|
-
|
|
36
|
-
let popover: HTMLElement | null = $state(null);
|
|
37
|
-
let invoker: HTMLElement | null = null;
|
|
38
|
-
let referenceElement: HTMLElement | null = null;
|
|
39
|
-
let triggerEls: HTMLElement[] = [];
|
|
40
|
-
let arrowParams: { placement: Placement; cords: Partial<Coords>; strategy: Strategy } = $state({
|
|
41
|
-
placement,
|
|
42
|
-
cords: { x: 0, y: 0 },
|
|
43
|
-
strategy
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
const paramsDefault = { duration: 100, easing: sineIn };
|
|
47
|
-
const paramsOptions = $derived(transitionParams ?? paramsDefault);
|
|
48
|
-
|
|
49
|
-
const px = (n: number | undefined) => (n ? `${n}px` : '');
|
|
50
|
-
|
|
51
|
-
function updatePopoverPosition() {
|
|
52
|
-
if (!invoker || !popover) {
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const arrowEl: HTMLElement | null = popover.querySelector('.popover-arrow');
|
|
57
|
-
|
|
58
|
-
let middleware: Middleware[] = [...middlewares, dom.offset(offset)];
|
|
59
|
-
if (arrowEl) middleware.push(dom.arrow({ element: arrowEl }));
|
|
60
|
-
|
|
61
|
-
return dom
|
|
62
|
-
.computePosition(referenceElement ?? invoker, popover, { placement, middleware, strategy })
|
|
63
|
-
.then(({ x, y, middlewareData: { arrow }, placement: pl, strategy }) => {
|
|
64
|
-
if (popover) {
|
|
65
|
-
Object.assign(popover.style, {
|
|
66
|
-
position: strategy,
|
|
67
|
-
left: yOnly ? '0' : px(x),
|
|
68
|
-
top: px(y)
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
if (arrow && arrowEl) {
|
|
72
|
-
arrowParams = { placement: pl, cords: { x: arrow.x, y: arrow.y }, strategy };
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
let isTriggered: boolean = false;
|
|
79
|
-
|
|
80
|
-
async function open_popover(ev: Event) {
|
|
81
|
-
// throttle
|
|
82
|
-
isTriggered = true;
|
|
83
|
-
await new Promise((resolve) => setTimeout(resolve, triggerDelay));
|
|
84
|
-
if (!isTriggered) {
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
ev.preventDefault();
|
|
89
|
-
|
|
90
|
-
if (ev.target !== invoker && triggerEls.includes(ev.target as HTMLElement)) {
|
|
91
|
-
invoker = ev.target as HTMLElement;
|
|
92
|
-
// if (invoker) invoker.popoverTargetElement = popover;
|
|
93
|
-
isOpen = false;
|
|
94
|
-
await new Promise((resolve) => setTimeout(resolve, triggerDelay));
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
if (ev.type === 'mousedown') {
|
|
98
|
-
isOpen = !isOpen;
|
|
99
|
-
} else {
|
|
100
|
-
isOpen = true;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
async function close_popover(ev: Event) {
|
|
105
|
-
// For click triggers, don't close on focusout events from inside the popover
|
|
106
|
-
if (trigger === 'click' && ev.type === 'focusout') {
|
|
107
|
-
const relatedTarget = (ev as FocusEvent).relatedTarget as HTMLElement;
|
|
108
|
-
|
|
109
|
-
// If focus is moving to somewhere inside the popover, don't close
|
|
110
|
-
if (popover && relatedTarget && popover.contains(relatedTarget)) {
|
|
111
|
-
return;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// If focus is moving to nowhere (like when clicking), don't close for click triggers
|
|
115
|
-
if (!relatedTarget) {
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
isTriggered = false;
|
|
121
|
-
await new Promise((resolve) => setTimeout(resolve, triggerDelay));
|
|
122
|
-
if (isTriggered) {
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// if popover has focus don't close when leaving the invoker
|
|
127
|
-
if (ev?.type === 'mouseleave' && popover?.contains(popover.ownerDocument.activeElement)) {
|
|
128
|
-
return;
|
|
129
|
-
}
|
|
130
|
-
if (ev?.type === 'focusout' && popover?.contains(popover.ownerDocument.activeElement)) {
|
|
131
|
-
return;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
isOpen = false;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
let autoUpdateDestroy = () => {};
|
|
138
|
-
|
|
139
|
-
function on_before_toggle(ev: ToggleEvent) {
|
|
140
|
-
if (!invoker || !popover) return;
|
|
141
|
-
|
|
142
|
-
(ev as TriggeredToggleEvent).trigger = invoker;
|
|
143
|
-
_onbeforetoggle?.(ev as TriggeredToggleEvent);
|
|
144
|
-
|
|
145
|
-
// Floating UI instance when it's closed we need to keep a autoUpdate destroy function
|
|
146
|
-
|
|
147
|
-
if (ev.newState === 'open') {
|
|
148
|
-
autoUpdateDestroy = dom.autoUpdate(
|
|
149
|
-
referenceElement ?? invoker,
|
|
150
|
-
popover,
|
|
151
|
-
updatePopoverPosition
|
|
152
|
-
);
|
|
153
|
-
popover.ownerDocument.addEventListener('click', closeOnClickOutside);
|
|
154
|
-
popover.ownerDocument.addEventListener('keydown', closeOnEscape);
|
|
155
|
-
} else {
|
|
156
|
-
autoUpdateDestroy();
|
|
157
|
-
popover.ownerDocument.removeEventListener('click', closeOnClickOutside);
|
|
158
|
-
popover.ownerDocument.removeEventListener('keydown', closeOnEscape);
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
function on_toggle(ev: ToggleEvent) {
|
|
163
|
-
if (!invoker) return;
|
|
164
|
-
|
|
165
|
-
// Update isOpen value when popover state changes through other means
|
|
166
|
-
isOpen = ev.newState === 'open';
|
|
167
|
-
|
|
168
|
-
(ev as TriggeredToggleEvent).trigger = invoker;
|
|
169
|
-
_ontoggle?.(ev as TriggeredToggleEvent);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
function set_triggers(node: HTMLElement) {
|
|
173
|
-
const events: [string, any, boolean][] = [
|
|
174
|
-
['focusin', open_popover, focusable],
|
|
175
|
-
['focusout', close_popover, focusable],
|
|
176
|
-
['mousedown', open_popover, clickable],
|
|
177
|
-
['mouseenter', open_popover, hoverable],
|
|
178
|
-
['mouseleave', close_popover, hoverable]
|
|
179
|
-
];
|
|
180
|
-
|
|
181
|
-
if (triggeredBy)
|
|
182
|
-
triggerEls = [...node.ownerDocument.querySelectorAll<HTMLElement>(triggeredBy)];
|
|
183
|
-
else if (node.previousElementSibling) triggerEls = [node.previousElementSibling as HTMLElement];
|
|
184
|
-
else if (node.parentElement) triggerEls = [node.parentElement];
|
|
185
|
-
|
|
186
|
-
if (!triggerEls.length) {
|
|
187
|
-
console.error('No triggers found.', triggeredBy);
|
|
188
|
-
return;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
if (reference) referenceElement = node.ownerDocument.querySelector<HTMLElement>(reference);
|
|
192
|
-
invoker = triggerEls[0];
|
|
193
|
-
|
|
194
|
-
triggerEls.forEach((element: HTMLElement) => {
|
|
195
|
-
if (element.tabIndex < 0) element.tabIndex = 0; // trigger must be focusable
|
|
196
|
-
for (const [name, handler, cond] of events) if (cond) element.addEventListener(name, handler);
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
$effect(() => {
|
|
200
|
-
return () => {
|
|
201
|
-
triggerEls.forEach((element: HTMLElement) => {
|
|
202
|
-
for (const [name, handler, cond] of events)
|
|
203
|
-
if (cond) element.removeEventListener(name, handler);
|
|
204
|
-
});
|
|
205
|
-
};
|
|
206
|
-
});
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
function closeOnEscape(event: KeyboardEvent) {
|
|
210
|
-
if (event.key === 'Escape') {
|
|
211
|
-
isOpen = false;
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
function closeOnClickOutside(event: MouseEvent) {
|
|
216
|
-
if (!popover) {
|
|
217
|
-
return;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
const clickPath = event.composedPath();
|
|
221
|
-
|
|
222
|
-
const isClickInsidePopover = clickPath.includes(popover);
|
|
223
|
-
const isClickOnTrigger = triggerEls.some((el) => clickPath.includes(el));
|
|
224
|
-
|
|
225
|
-
// Only close if click is outside both popover and trigger elements
|
|
226
|
-
if (!isClickInsidePopover && !isClickOnTrigger) {
|
|
227
|
-
close_popover(event);
|
|
228
|
-
isOpen = false;
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
</script>
|
|
232
|
-
|
|
233
|
-
<div use:set_triggers hidden></div>
|
|
234
|
-
|
|
235
|
-
{#if isOpen}
|
|
236
|
-
<div
|
|
237
|
-
popover="manual"
|
|
238
|
-
role="tooltip"
|
|
239
|
-
bind:this={popover}
|
|
240
|
-
class:overflow-visible={true}
|
|
241
|
-
onfocusout={close_popover}
|
|
242
|
-
onmouseleave={hoverable ? close_popover : undefined}
|
|
243
|
-
onmouseenter={hoverable ? open_popover : undefined}
|
|
244
|
-
onbeforetoggle={on_before_toggle}
|
|
245
|
-
ontoggle={on_toggle}
|
|
246
|
-
class={clsx(className)}
|
|
247
|
-
transition:transition={paramsOptions as ParamsType}
|
|
248
|
-
onintrostart={() => popover?.showPopover()}
|
|
249
|
-
onoutroend={() => popover?.hidePopover()}
|
|
250
|
-
{...restProps}
|
|
251
|
-
>
|
|
252
|
-
{@render children()}
|
|
253
|
-
<!-- {#if arrow}
|
|
254
|
-
<Arrow {...arrowParams} class={arrowClass} />
|
|
255
|
-
{/if} -->
|
|
256
|
-
</div>
|
|
257
|
-
{/if}
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Coords, Middleware, Placement, Strategy } from '@floating-ui/dom';
|
|
3
|
+
import * as dom from '@floating-ui/dom';
|
|
4
|
+
// import Arrow from './Arrow.svelte';
|
|
5
|
+
import { fade } from 'svelte/transition';
|
|
6
|
+
import { sineIn } from 'svelte/easing';
|
|
7
|
+
import clsx from 'clsx';
|
|
8
|
+
import type { ParamsType, PopperProps, TriggeredToggleEvent } from '../types/index.js';
|
|
9
|
+
|
|
10
|
+
let {
|
|
11
|
+
triggeredBy,
|
|
12
|
+
triggerDelay = 200,
|
|
13
|
+
trigger = 'click',
|
|
14
|
+
placement = 'top',
|
|
15
|
+
offset = 8,
|
|
16
|
+
arrow = false,
|
|
17
|
+
yOnly = false,
|
|
18
|
+
strategy = 'absolute',
|
|
19
|
+
reference,
|
|
20
|
+
middlewares = [dom.flip(), dom.shift()],
|
|
21
|
+
onbeforetoggle: _onbeforetoggle,
|
|
22
|
+
ontoggle: _ontoggle,
|
|
23
|
+
class: className = '',
|
|
24
|
+
arrowClass = '',
|
|
25
|
+
isOpen = $bindable(false),
|
|
26
|
+
transitionParams,
|
|
27
|
+
transition = fade,
|
|
28
|
+
children,
|
|
29
|
+
...restProps
|
|
30
|
+
}: PopperProps = $props();
|
|
31
|
+
|
|
32
|
+
let focusable: boolean = true;
|
|
33
|
+
let clickable: boolean = $derived(trigger === 'click');
|
|
34
|
+
let hoverable: boolean = $derived(trigger === 'hover');
|
|
35
|
+
|
|
36
|
+
let popover: HTMLElement | null = $state(null);
|
|
37
|
+
let invoker: HTMLElement | null = null;
|
|
38
|
+
let referenceElement: HTMLElement | null = null;
|
|
39
|
+
let triggerEls: HTMLElement[] = [];
|
|
40
|
+
let arrowParams: { placement: Placement; cords: Partial<Coords>; strategy: Strategy } = $state({
|
|
41
|
+
placement,
|
|
42
|
+
cords: { x: 0, y: 0 },
|
|
43
|
+
strategy
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const paramsDefault = { duration: 100, easing: sineIn };
|
|
47
|
+
const paramsOptions = $derived(transitionParams ?? paramsDefault);
|
|
48
|
+
|
|
49
|
+
const px = (n: number | undefined) => (n ? `${n}px` : '');
|
|
50
|
+
|
|
51
|
+
function updatePopoverPosition() {
|
|
52
|
+
if (!invoker || !popover) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const arrowEl: HTMLElement | null = popover.querySelector('.popover-arrow');
|
|
57
|
+
|
|
58
|
+
let middleware: Middleware[] = [...middlewares, dom.offset(offset)];
|
|
59
|
+
if (arrowEl) middleware.push(dom.arrow({ element: arrowEl }));
|
|
60
|
+
|
|
61
|
+
return dom
|
|
62
|
+
.computePosition(referenceElement ?? invoker, popover, { placement, middleware, strategy })
|
|
63
|
+
.then(({ x, y, middlewareData: { arrow }, placement: pl, strategy }) => {
|
|
64
|
+
if (popover) {
|
|
65
|
+
Object.assign(popover.style, {
|
|
66
|
+
position: strategy,
|
|
67
|
+
left: yOnly ? '0' : px(x),
|
|
68
|
+
top: px(y)
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
if (arrow && arrowEl) {
|
|
72
|
+
arrowParams = { placement: pl, cords: { x: arrow.x, y: arrow.y }, strategy };
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
let isTriggered: boolean = false;
|
|
79
|
+
|
|
80
|
+
async function open_popover(ev: Event) {
|
|
81
|
+
// throttle
|
|
82
|
+
isTriggered = true;
|
|
83
|
+
await new Promise((resolve) => setTimeout(resolve, triggerDelay));
|
|
84
|
+
if (!isTriggered) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
ev.preventDefault();
|
|
89
|
+
|
|
90
|
+
if (ev.target !== invoker && triggerEls.includes(ev.target as HTMLElement)) {
|
|
91
|
+
invoker = ev.target as HTMLElement;
|
|
92
|
+
// if (invoker) invoker.popoverTargetElement = popover;
|
|
93
|
+
isOpen = false;
|
|
94
|
+
await new Promise((resolve) => setTimeout(resolve, triggerDelay));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (ev.type === 'mousedown') {
|
|
98
|
+
isOpen = !isOpen;
|
|
99
|
+
} else {
|
|
100
|
+
isOpen = true;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function close_popover(ev: Event) {
|
|
105
|
+
// For click triggers, don't close on focusout events from inside the popover
|
|
106
|
+
if (trigger === 'click' && ev.type === 'focusout') {
|
|
107
|
+
const relatedTarget = (ev as FocusEvent).relatedTarget as HTMLElement;
|
|
108
|
+
|
|
109
|
+
// If focus is moving to somewhere inside the popover, don't close
|
|
110
|
+
if (popover && relatedTarget && popover.contains(relatedTarget)) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// If focus is moving to nowhere (like when clicking), don't close for click triggers
|
|
115
|
+
if (!relatedTarget) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
isTriggered = false;
|
|
121
|
+
await new Promise((resolve) => setTimeout(resolve, triggerDelay));
|
|
122
|
+
if (isTriggered) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// if popover has focus don't close when leaving the invoker
|
|
127
|
+
if (ev?.type === 'mouseleave' && popover?.contains(popover.ownerDocument.activeElement)) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
if (ev?.type === 'focusout' && popover?.contains(popover.ownerDocument.activeElement)) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
isOpen = false;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
let autoUpdateDestroy = () => {};
|
|
138
|
+
|
|
139
|
+
function on_before_toggle(ev: ToggleEvent) {
|
|
140
|
+
if (!invoker || !popover) return;
|
|
141
|
+
|
|
142
|
+
(ev as TriggeredToggleEvent).trigger = invoker;
|
|
143
|
+
_onbeforetoggle?.(ev as TriggeredToggleEvent);
|
|
144
|
+
|
|
145
|
+
// Floating UI instance when it's closed we need to keep a autoUpdate destroy function
|
|
146
|
+
|
|
147
|
+
if (ev.newState === 'open') {
|
|
148
|
+
autoUpdateDestroy = dom.autoUpdate(
|
|
149
|
+
referenceElement ?? invoker,
|
|
150
|
+
popover,
|
|
151
|
+
updatePopoverPosition
|
|
152
|
+
);
|
|
153
|
+
popover.ownerDocument.addEventListener('click', closeOnClickOutside);
|
|
154
|
+
popover.ownerDocument.addEventListener('keydown', closeOnEscape);
|
|
155
|
+
} else {
|
|
156
|
+
autoUpdateDestroy();
|
|
157
|
+
popover.ownerDocument.removeEventListener('click', closeOnClickOutside);
|
|
158
|
+
popover.ownerDocument.removeEventListener('keydown', closeOnEscape);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function on_toggle(ev: ToggleEvent) {
|
|
163
|
+
if (!invoker) return;
|
|
164
|
+
|
|
165
|
+
// Update isOpen value when popover state changes through other means
|
|
166
|
+
isOpen = ev.newState === 'open';
|
|
167
|
+
|
|
168
|
+
(ev as TriggeredToggleEvent).trigger = invoker;
|
|
169
|
+
_ontoggle?.(ev as TriggeredToggleEvent);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function set_triggers(node: HTMLElement) {
|
|
173
|
+
const events: [string, any, boolean][] = [
|
|
174
|
+
['focusin', open_popover, focusable],
|
|
175
|
+
['focusout', close_popover, focusable],
|
|
176
|
+
['mousedown', open_popover, clickable],
|
|
177
|
+
['mouseenter', open_popover, hoverable],
|
|
178
|
+
['mouseleave', close_popover, hoverable]
|
|
179
|
+
];
|
|
180
|
+
|
|
181
|
+
if (triggeredBy)
|
|
182
|
+
triggerEls = [...node.ownerDocument.querySelectorAll<HTMLElement>(triggeredBy)];
|
|
183
|
+
else if (node.previousElementSibling) triggerEls = [node.previousElementSibling as HTMLElement];
|
|
184
|
+
else if (node.parentElement) triggerEls = [node.parentElement];
|
|
185
|
+
|
|
186
|
+
if (!triggerEls.length) {
|
|
187
|
+
console.error('No triggers found.', triggeredBy);
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (reference) referenceElement = node.ownerDocument.querySelector<HTMLElement>(reference);
|
|
192
|
+
invoker = triggerEls[0];
|
|
193
|
+
|
|
194
|
+
triggerEls.forEach((element: HTMLElement) => {
|
|
195
|
+
if (element.tabIndex < 0) element.tabIndex = 0; // trigger must be focusable
|
|
196
|
+
for (const [name, handler, cond] of events) if (cond) element.addEventListener(name, handler);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
$effect(() => {
|
|
200
|
+
return () => {
|
|
201
|
+
triggerEls.forEach((element: HTMLElement) => {
|
|
202
|
+
for (const [name, handler, cond] of events)
|
|
203
|
+
if (cond) element.removeEventListener(name, handler);
|
|
204
|
+
});
|
|
205
|
+
};
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function closeOnEscape(event: KeyboardEvent) {
|
|
210
|
+
if (event.key === 'Escape') {
|
|
211
|
+
isOpen = false;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function closeOnClickOutside(event: MouseEvent) {
|
|
216
|
+
if (!popover) {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const clickPath = event.composedPath();
|
|
221
|
+
|
|
222
|
+
const isClickInsidePopover = clickPath.includes(popover);
|
|
223
|
+
const isClickOnTrigger = triggerEls.some((el) => clickPath.includes(el));
|
|
224
|
+
|
|
225
|
+
// Only close if click is outside both popover and trigger elements
|
|
226
|
+
if (!isClickInsidePopover && !isClickOnTrigger) {
|
|
227
|
+
close_popover(event);
|
|
228
|
+
isOpen = false;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
</script>
|
|
232
|
+
|
|
233
|
+
<div use:set_triggers hidden></div>
|
|
234
|
+
|
|
235
|
+
{#if isOpen}
|
|
236
|
+
<div
|
|
237
|
+
popover="manual"
|
|
238
|
+
role="tooltip"
|
|
239
|
+
bind:this={popover}
|
|
240
|
+
class:overflow-visible={true}
|
|
241
|
+
onfocusout={close_popover}
|
|
242
|
+
onmouseleave={hoverable ? close_popover : undefined}
|
|
243
|
+
onmouseenter={hoverable ? open_popover : undefined}
|
|
244
|
+
onbeforetoggle={on_before_toggle}
|
|
245
|
+
ontoggle={on_toggle}
|
|
246
|
+
class={clsx(className)}
|
|
247
|
+
transition:transition={paramsOptions as ParamsType}
|
|
248
|
+
onintrostart={() => popover?.showPopover()}
|
|
249
|
+
onoutroend={() => popover?.hidePopover()}
|
|
250
|
+
{...restProps}
|
|
251
|
+
>
|
|
252
|
+
{@render children()}
|
|
253
|
+
<!-- {#if arrow}
|
|
254
|
+
<Arrow {...arrowParams} class={arrowClass} />
|
|
255
|
+
{/if} -->
|
|
256
|
+
</div>
|
|
257
|
+
{/if}
|