@finsweet/webflow-apps-utils 1.0.54 → 1.0.56
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.
|
@@ -101,8 +101,8 @@
|
|
|
101
101
|
const handleGlobalKeyDown = (event: KeyboardEvent) => {
|
|
102
102
|
if (event.key === 'Escape' && closeOnEscape && isOpen) {
|
|
103
103
|
event.preventDefault();
|
|
104
|
+
event.stopPropagation();
|
|
104
105
|
closeDropdown();
|
|
105
|
-
// Remove focus to prevent focus ring after closing
|
|
106
106
|
if (document.activeElement instanceof HTMLElement) {
|
|
107
107
|
document.activeElement.blur();
|
|
108
108
|
}
|
|
@@ -334,7 +334,7 @@
|
|
|
334
334
|
if (!dropdownItems || !target) return instances;
|
|
335
335
|
|
|
336
336
|
if (closeOnClickOutside) {
|
|
337
|
-
document?.addEventListener('click', dismissTooltip);
|
|
337
|
+
document?.addEventListener('click', dismissTooltip, true);
|
|
338
338
|
}
|
|
339
339
|
instances.push(setupDropdown(target, dropdownItems));
|
|
340
340
|
|
|
@@ -347,7 +347,7 @@
|
|
|
347
347
|
const cleanupDropdownInstances = (instances: DropdownInstance[]) => {
|
|
348
348
|
instances.forEach((instance) => instance.cleanup());
|
|
349
349
|
if (closeOnClickOutside) {
|
|
350
|
-
document?.removeEventListener('click', dismissTooltip);
|
|
350
|
+
document?.removeEventListener('click', dismissTooltip, true);
|
|
351
351
|
}
|
|
352
352
|
};
|
|
353
353
|
|
|
@@ -386,35 +386,40 @@
|
|
|
386
386
|
const showDropdown = () => {
|
|
387
387
|
if (disabled) return;
|
|
388
388
|
|
|
389
|
+
// Show the dropdown FIRST so elements are focusable before calling focus()
|
|
389
390
|
tooltip.setAttribute('aria-hidden', 'false');
|
|
391
|
+
tooltip.style.display = 'flex';
|
|
392
|
+
isOpen = true;
|
|
393
|
+
isFocused = true;
|
|
390
394
|
update();
|
|
391
395
|
|
|
392
396
|
const selectedItemButton = tooltip.querySelector(
|
|
393
397
|
`.dropdown-item[aria-selected="true"]`
|
|
394
398
|
) as HTMLElement;
|
|
395
399
|
const firstItemButton = tooltip.querySelector('.dropdown-item') as HTMLElement;
|
|
396
|
-
const itemToFocus = selectedItemButton || firstItemButton;
|
|
397
400
|
|
|
398
401
|
const searchInput = tooltip?.querySelector<HTMLInputElement>('input[type="text"]');
|
|
399
|
-
if (
|
|
402
|
+
if (selectedItemButton) {
|
|
403
|
+
selectedItemButton.focus();
|
|
404
|
+
if (lastHoveredItem) {
|
|
405
|
+
lastHoveredItem.classList.remove('hover-state');
|
|
406
|
+
lastHoveredItem.setAttribute('tabindex', '-1');
|
|
407
|
+
}
|
|
408
|
+
lastHoveredItem = selectedItemButton;
|
|
409
|
+
lastHoveredItem.classList.add('hover-state');
|
|
410
|
+
lastHoveredItem.setAttribute('tabindex', '0');
|
|
411
|
+
} else if (searchInput && enableSearch) {
|
|
400
412
|
searchInput.focus();
|
|
401
|
-
} else {
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
lastHoveredItem.classList.remove('hover-state');
|
|
407
|
-
lastHoveredItem.setAttribute('tabindex', '-1');
|
|
408
|
-
}
|
|
409
|
-
lastHoveredItem = itemToFocus;
|
|
410
|
-
lastHoveredItem.classList.add('hover-state');
|
|
411
|
-
lastHoveredItem.setAttribute('tabindex', '0');
|
|
413
|
+
} else if (firstItemButton) {
|
|
414
|
+
firstItemButton.focus();
|
|
415
|
+
if (lastHoveredItem) {
|
|
416
|
+
lastHoveredItem.classList.remove('hover-state');
|
|
417
|
+
lastHoveredItem.setAttribute('tabindex', '-1');
|
|
412
418
|
}
|
|
419
|
+
lastHoveredItem = firstItemButton;
|
|
420
|
+
lastHoveredItem.classList.add('hover-state');
|
|
421
|
+
lastHoveredItem.setAttribute('tabindex', '0');
|
|
413
422
|
}
|
|
414
|
-
|
|
415
|
-
tooltip.style.display = 'flex';
|
|
416
|
-
isOpen = true;
|
|
417
|
-
isFocused = true;
|
|
418
423
|
};
|
|
419
424
|
|
|
420
425
|
const options: EventOption[] = [['click', showDropdown]].filter(Boolean) as EventOption[];
|
|
@@ -477,10 +482,13 @@
|
|
|
477
482
|
</script>
|
|
478
483
|
|
|
479
484
|
{#snippet selectWrapper()}
|
|
485
|
+
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
486
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
480
487
|
<div
|
|
481
488
|
class="dropdown-wrapper {className}"
|
|
482
489
|
bind:this={dropdownWrapper}
|
|
483
490
|
style="{hide ? 'display:none;' : ''} width: {width};"
|
|
491
|
+
onclick={(e) => e.stopPropagation()}
|
|
484
492
|
>
|
|
485
493
|
<div
|
|
486
494
|
class="dropdown"
|
|
@@ -509,12 +517,14 @@
|
|
|
509
517
|
</div>
|
|
510
518
|
</div>
|
|
511
519
|
|
|
520
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
512
521
|
<div
|
|
513
522
|
tabindex={disabled || isOpen ? -1 : 0}
|
|
514
523
|
class="dropdown-list"
|
|
515
524
|
class:has-footer={footer}
|
|
516
525
|
role="listbox"
|
|
517
526
|
style="width:{dropdownWidth};"
|
|
527
|
+
onclick={(e) => e.stopPropagation()}
|
|
518
528
|
onkeydown={(e) => {
|
|
519
529
|
e.stopPropagation();
|
|
520
530
|
e.preventDefault();
|
|
@@ -569,6 +579,9 @@
|
|
|
569
579
|
onkeydown={(e) => {
|
|
570
580
|
e.stopPropagation();
|
|
571
581
|
e.preventDefault();
|
|
582
|
+
if (e.key === 'Escape' && closeOnEscape) {
|
|
583
|
+
closeDropdown();
|
|
584
|
+
}
|
|
572
585
|
}}
|
|
573
586
|
onmouseenter={handleMouseEnter}
|
|
574
587
|
aria-hidden={!isOpen}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import SelectInModalStory from './SelectInModalStory.svelte';
|
|
2
|
+
const meta = {
|
|
3
|
+
title: 'Ui/Select/InModal',
|
|
4
|
+
component: SelectInModalStory,
|
|
5
|
+
parameters: {
|
|
6
|
+
layout: 'centered',
|
|
7
|
+
docs: {
|
|
8
|
+
description: {
|
|
9
|
+
component: 'Testing Modal with Select component to ensure proper event handling'
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
tags: ['autodocs']
|
|
14
|
+
};
|
|
15
|
+
export default meta;
|
|
16
|
+
export const Default = {
|
|
17
|
+
args: {}
|
|
18
|
+
};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Button } from '../button';
|
|
3
|
+
import Modal from '../modal/Modal.svelte';
|
|
4
|
+
import Select from './Select.svelte';
|
|
5
|
+
import type { SelectOption } from './types';
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
initialOpen?: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
let { initialOpen = false }: Props = $props();
|
|
12
|
+
|
|
13
|
+
let modalOpen = $state(initialOpen);
|
|
14
|
+
let selectedValue = $state<string | null>(null);
|
|
15
|
+
|
|
16
|
+
const options: SelectOption[] = [
|
|
17
|
+
{ label: 'Option 1', value: 'option1' },
|
|
18
|
+
{ label: 'Option 2', value: 'option2' },
|
|
19
|
+
{ label: 'Option 3', value: 'option3' },
|
|
20
|
+
{ label: 'Option 4', value: 'option4' },
|
|
21
|
+
{ label: 'Option 5', value: 'option5' }
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
const handleOpenModal = () => {
|
|
25
|
+
modalOpen = true;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const handleCloseModal = () => {
|
|
29
|
+
console.log('Modal closing');
|
|
30
|
+
modalOpen = false;
|
|
31
|
+
};
|
|
32
|
+
</script>
|
|
33
|
+
|
|
34
|
+
<div style="padding: 20px;">
|
|
35
|
+
<Button onclick={handleOpenModal}>Open Modal with Select</Button>
|
|
36
|
+
|
|
37
|
+
<Modal
|
|
38
|
+
open={modalOpen}
|
|
39
|
+
onOpenChange={(open) => {
|
|
40
|
+
console.log('Modal onOpenChange:', open);
|
|
41
|
+
modalOpen = open;
|
|
42
|
+
}}
|
|
43
|
+
title="Select Example Inside Modal"
|
|
44
|
+
showFooter={false}
|
|
45
|
+
width="400px"
|
|
46
|
+
closeOnOverlayClick={false}
|
|
47
|
+
closeOnEscape={false}
|
|
48
|
+
>
|
|
49
|
+
<div style="padding: 20px; display: flex; flex-direction: column; gap: 16px;">
|
|
50
|
+
<p style="color: var(--text1); margin: 0;">Test the following behaviors:</p>
|
|
51
|
+
<ul style="color: var(--text2); margin: 0; padding-left: 20px;">
|
|
52
|
+
<li>Click outside the modal (overlay) - modal stays open (disabled)</li>
|
|
53
|
+
<li>Press Escape with select closed - modal stays open (disabled)</li>
|
|
54
|
+
<li>Press Escape with select open - should close select only</li>
|
|
55
|
+
<li>Click outside select but inside modal - should close select only</li>
|
|
56
|
+
<li>Use "Close Modal" button to close the modal</li>
|
|
57
|
+
</ul>
|
|
58
|
+
|
|
59
|
+
<Select
|
|
60
|
+
{options}
|
|
61
|
+
bind:selected={selectedValue}
|
|
62
|
+
defaultText="Choose an option"
|
|
63
|
+
width="100%"
|
|
64
|
+
dropdownWidth="100%"
|
|
65
|
+
/>
|
|
66
|
+
|
|
67
|
+
{#if selectedValue}
|
|
68
|
+
<p style="color: var(--text1); margin: 0;">
|
|
69
|
+
Selected: {selectedValue}
|
|
70
|
+
</p>
|
|
71
|
+
{/if}
|
|
72
|
+
|
|
73
|
+
<Button onclick={handleCloseModal} variant="secondary">Close Modal</Button>
|
|
74
|
+
</div>
|
|
75
|
+
</Modal>
|
|
76
|
+
</div>
|