@14ch/svelte-ui 0.0.63 → 0.0.65
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/dist/components/ConfirmDialog.svelte +18 -1
- package/dist/components/ConfirmDialog.svelte.d.ts +5 -0
- package/dist/components/Dialog.svelte +13 -1
- package/dist/components/Dialog.svelte.d.ts +4 -0
- package/dist/components/Drawer.svelte +13 -1
- package/dist/components/Drawer.svelte.d.ts +4 -0
- package/dist/components/Modal.svelte +41 -13
- package/dist/components/Modal.svelte.d.ts +5 -1
- package/dist/components/Popup.svelte +1 -1
- package/dist/index.d.ts +1 -1
- package/package.json +1 -1
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import Button from './Button.svelte';
|
|
6
6
|
import { convertToHtml } from '../utils/formatText';
|
|
7
7
|
import type { SvelteComponent } from 'svelte';
|
|
8
|
+
import type { CloseReason } from '../types/propOptions';
|
|
8
9
|
|
|
9
10
|
// =========================================================================
|
|
10
11
|
// Props, States & Constants
|
|
@@ -35,7 +36,11 @@
|
|
|
35
36
|
|
|
36
37
|
// イベントハンドラー
|
|
37
38
|
onSubmit?: () => void;
|
|
39
|
+
/** Called on cancel, including dismissal via Escape or a backdrop click. */
|
|
38
40
|
onCancel?: () => void;
|
|
41
|
+
onOpen?: () => void;
|
|
42
|
+
/** Called after the close animation completes, with the reason it was closed. */
|
|
43
|
+
onClose?: (reason: CloseReason) => void;
|
|
39
44
|
};
|
|
40
45
|
|
|
41
46
|
let {
|
|
@@ -60,7 +65,9 @@
|
|
|
60
65
|
|
|
61
66
|
// イベントハンドラー
|
|
62
67
|
onSubmit = () => {}, // No params for type inference
|
|
63
|
-
onCancel = () => {} // No params for type inference
|
|
68
|
+
onCancel = () => {}, // No params for type inference
|
|
69
|
+
onOpen = () => {},
|
|
70
|
+
onClose = () => {}
|
|
64
71
|
}: ConfirmDialogProps = $props();
|
|
65
72
|
|
|
66
73
|
let dialogRef: SvelteComponent | undefined = $state();
|
|
@@ -78,6 +85,14 @@
|
|
|
78
85
|
close();
|
|
79
86
|
};
|
|
80
87
|
|
|
88
|
+
const handleClose = (reason: CloseReason): void => {
|
|
89
|
+
// Escape / 背景クリックでの離脱もキャンセルとして扱う
|
|
90
|
+
if (reason !== 'explicit') {
|
|
91
|
+
onCancel();
|
|
92
|
+
}
|
|
93
|
+
onClose(reason);
|
|
94
|
+
};
|
|
95
|
+
|
|
81
96
|
/** Opens the dialog. */
|
|
82
97
|
export const open = (): void => {
|
|
83
98
|
isOpen = true;
|
|
@@ -102,6 +117,8 @@
|
|
|
102
117
|
{scrollable}
|
|
103
118
|
{closeIfClickOutside}
|
|
104
119
|
{focusFirstOnOpen}
|
|
120
|
+
{onOpen}
|
|
121
|
+
onClose={handleClose}
|
|
105
122
|
id={id ? `${id}-dialog` : undefined}
|
|
106
123
|
>
|
|
107
124
|
<div class="confirm-dialog-message">
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { CloseReason } from '../types/propOptions';
|
|
1
2
|
export type ConfirmDialogProps = {
|
|
2
3
|
title?: string;
|
|
3
4
|
description?: string;
|
|
@@ -15,7 +16,11 @@ export type ConfirmDialogProps = {
|
|
|
15
16
|
closeIfClickOutside?: boolean;
|
|
16
17
|
focusFirstOnOpen?: boolean;
|
|
17
18
|
onSubmit?: () => void;
|
|
19
|
+
/** Called on cancel, including dismissal via Escape or a backdrop click. */
|
|
18
20
|
onCancel?: () => void;
|
|
21
|
+
onOpen?: () => void;
|
|
22
|
+
/** Called after the close animation completes, with the reason it was closed. */
|
|
23
|
+
onClose?: (reason: CloseReason) => void;
|
|
19
24
|
};
|
|
20
25
|
declare const ConfirmDialog: import("svelte").Component<ConfirmDialogProps, {
|
|
21
26
|
/** Opens the dialog. */ open: () => void;
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
* This prevents state synchronization bugs and ensures consistent behavior.
|
|
15
15
|
*/
|
|
16
16
|
import type { Snippet } from 'svelte';
|
|
17
|
+
import type { CloseReason } from '../types/propOptions';
|
|
17
18
|
import Modal from './Modal.svelte';
|
|
18
19
|
import { getStyleFromNumber } from '../utils/style';
|
|
19
20
|
|
|
@@ -56,6 +57,11 @@
|
|
|
56
57
|
// ARIA/アクセシビリティ
|
|
57
58
|
ariaLabel?: string;
|
|
58
59
|
ariaDescribedby?: string;
|
|
60
|
+
|
|
61
|
+
// イベントハンドラー
|
|
62
|
+
onOpen?: () => void;
|
|
63
|
+
/** Called after the close animation completes, with the reason it was closed. */
|
|
64
|
+
onClose?: (reason: CloseReason) => void;
|
|
59
65
|
};
|
|
60
66
|
|
|
61
67
|
let {
|
|
@@ -85,7 +91,11 @@
|
|
|
85
91
|
|
|
86
92
|
// ARIA/アクセシビリティ
|
|
87
93
|
ariaLabel,
|
|
88
|
-
ariaDescribedby
|
|
94
|
+
ariaDescribedby,
|
|
95
|
+
|
|
96
|
+
// イベントハンドラー
|
|
97
|
+
onOpen = () => {}, // No params for type inference
|
|
98
|
+
onClose = () => {}
|
|
89
99
|
}: DialogProps = $props();
|
|
90
100
|
|
|
91
101
|
let modalRef: Modal;
|
|
@@ -145,6 +155,8 @@
|
|
|
145
155
|
{closeIfClickOutside}
|
|
146
156
|
{restoreFocus}
|
|
147
157
|
{focusFirstOnOpen}
|
|
158
|
+
{onOpen}
|
|
159
|
+
{onClose}
|
|
148
160
|
componentType="Dialog"
|
|
149
161
|
{ariaLabel}
|
|
150
162
|
{ariaLabelledby}
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* This prevents state synchronization bugs and ensures consistent behavior.
|
|
12
12
|
*/
|
|
13
13
|
import type { Snippet } from 'svelte';
|
|
14
|
+
import type { CloseReason } from '../types/propOptions';
|
|
14
15
|
export type DialogProps = {
|
|
15
16
|
header?: Snippet;
|
|
16
17
|
children?: Snippet;
|
|
@@ -36,6 +37,9 @@ export type DialogProps = {
|
|
|
36
37
|
focusFirstOnOpen?: boolean;
|
|
37
38
|
ariaLabel?: string;
|
|
38
39
|
ariaDescribedby?: string;
|
|
40
|
+
onOpen?: () => void;
|
|
41
|
+
/** Called after the close animation completes, with the reason it was closed. */
|
|
42
|
+
onClose?: (reason: CloseReason) => void;
|
|
39
43
|
};
|
|
40
44
|
declare const Dialog: import("svelte").Component<DialogProps, {
|
|
41
45
|
/** Opens the dialog. */ open: () => void;
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
* This prevents state synchronization bugs and ensures consistent behavior.
|
|
15
15
|
*/
|
|
16
16
|
import type { Snippet } from 'svelte';
|
|
17
|
+
import type { CloseReason } from '../types/propOptions';
|
|
17
18
|
import Modal from './Modal.svelte';
|
|
18
19
|
import { getStyleFromNumber } from '../utils/style';
|
|
19
20
|
|
|
@@ -58,6 +59,11 @@
|
|
|
58
59
|
// ARIA/アクセシビリティ
|
|
59
60
|
ariaLabel?: string;
|
|
60
61
|
ariaDescribedby?: string;
|
|
62
|
+
|
|
63
|
+
// イベントハンドラー
|
|
64
|
+
onOpen?: () => void;
|
|
65
|
+
/** Called after the close animation completes, with the reason it was closed. */
|
|
66
|
+
onClose?: (reason: CloseReason) => void;
|
|
61
67
|
};
|
|
62
68
|
|
|
63
69
|
let {
|
|
@@ -88,7 +94,11 @@
|
|
|
88
94
|
|
|
89
95
|
// ARIA/アクセシビリティ
|
|
90
96
|
ariaLabel = 'Drawer',
|
|
91
|
-
ariaDescribedby
|
|
97
|
+
ariaDescribedby,
|
|
98
|
+
|
|
99
|
+
// イベントハンドラー
|
|
100
|
+
onOpen = () => {}, // No params for type inference
|
|
101
|
+
onClose = () => {}
|
|
92
102
|
}: DrawerProps = $props();
|
|
93
103
|
|
|
94
104
|
let modalRef: Modal;
|
|
@@ -159,6 +169,8 @@
|
|
|
159
169
|
{closeIfClickOutside}
|
|
160
170
|
{restoreFocus}
|
|
161
171
|
{focusFirstOnOpen}
|
|
172
|
+
{onOpen}
|
|
173
|
+
{onClose}
|
|
162
174
|
componentType="Drawer"
|
|
163
175
|
{ariaLabel}
|
|
164
176
|
{ariaLabelledby}
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* This prevents state synchronization bugs and ensures consistent behavior.
|
|
12
12
|
*/
|
|
13
13
|
import type { Snippet } from 'svelte';
|
|
14
|
+
import type { CloseReason } from '../types/propOptions';
|
|
14
15
|
export type DrawerProps = {
|
|
15
16
|
header?: Snippet;
|
|
16
17
|
children?: Snippet;
|
|
@@ -38,6 +39,9 @@ export type DrawerProps = {
|
|
|
38
39
|
focusFirstOnOpen?: boolean;
|
|
39
40
|
ariaLabel?: string;
|
|
40
41
|
ariaDescribedby?: string;
|
|
42
|
+
onOpen?: () => void;
|
|
43
|
+
/** Called after the close animation completes, with the reason it was closed. */
|
|
44
|
+
onClose?: (reason: CloseReason) => void;
|
|
41
45
|
};
|
|
42
46
|
declare const Drawer: import("svelte").Component<DrawerProps, {
|
|
43
47
|
/** Opens the drawer. */ open: () => void;
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* This prevents state synchronization bugs and ensures consistent behavior.
|
|
14
14
|
*/
|
|
15
15
|
import type { Snippet } from 'svelte';
|
|
16
|
+
import type { CloseReason } from '../types/propOptions';
|
|
16
17
|
import { announceOpenClose } from '../utils/accessibility';
|
|
17
18
|
|
|
18
19
|
// =========================================================================
|
|
@@ -47,6 +48,11 @@
|
|
|
47
48
|
ariaLabel?: string;
|
|
48
49
|
ariaLabelledby?: string;
|
|
49
50
|
ariaDescribedby?: string;
|
|
51
|
+
|
|
52
|
+
// イベントハンドラー
|
|
53
|
+
onOpen?: () => void;
|
|
54
|
+
/** Called after the close animation completes, with the reason it was closed. */
|
|
55
|
+
onClose?: (reason: CloseReason) => void;
|
|
50
56
|
};
|
|
51
57
|
|
|
52
58
|
let {
|
|
@@ -72,12 +78,19 @@
|
|
|
72
78
|
// ARIA/アクセシビリティ
|
|
73
79
|
ariaLabel,
|
|
74
80
|
ariaLabelledby,
|
|
75
|
-
ariaDescribedby
|
|
81
|
+
ariaDescribedby,
|
|
82
|
+
|
|
83
|
+
// イベントハンドラー
|
|
84
|
+
onOpen = () => {}, // No params for type inference
|
|
85
|
+
onClose = () => {}
|
|
76
86
|
}: ModalProps = $props();
|
|
77
87
|
|
|
78
88
|
let dialogRef: HTMLDialogElement;
|
|
79
89
|
let containerRef: HTMLDivElement;
|
|
80
90
|
let previousActiveElement: HTMLElement | null = null;
|
|
91
|
+
// isOpen は bind でも変わるため、実際に開いているかは内部で持つ
|
|
92
|
+
let isVisible = false;
|
|
93
|
+
let pendingCloseReason: CloseReason | null = null;
|
|
81
94
|
|
|
82
95
|
// =========================================================================
|
|
83
96
|
// Effects
|
|
@@ -89,7 +102,7 @@
|
|
|
89
102
|
if (!closeIfClickOutside) return;
|
|
90
103
|
if (!containerRef || !event.target) return;
|
|
91
104
|
if (!containerRef.contains(event.target as Node)) {
|
|
92
|
-
close();
|
|
105
|
+
close(undefined, 'outside');
|
|
93
106
|
}
|
|
94
107
|
};
|
|
95
108
|
|
|
@@ -103,18 +116,20 @@
|
|
|
103
116
|
});
|
|
104
117
|
|
|
105
118
|
$effect(() => {
|
|
106
|
-
if (!
|
|
119
|
+
if (!dialogRef) return;
|
|
107
120
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
121
|
+
// showModal() した dialog は最前面の1つだけに cancel が配送されるため、
|
|
122
|
+
// Escape で多重の Modal がまとめて閉じることを防げる。ネイティブの即時
|
|
123
|
+
// クローズは抑止し、fade-out アニメーション付きの close() を使う。
|
|
124
|
+
const handleCancel = (event: Event) => {
|
|
125
|
+
event.preventDefault();
|
|
126
|
+
close(undefined, 'escape');
|
|
112
127
|
};
|
|
113
128
|
|
|
114
|
-
|
|
129
|
+
dialogRef.addEventListener('cancel', handleCancel);
|
|
115
130
|
|
|
116
131
|
return () => {
|
|
117
|
-
|
|
132
|
+
dialogRef.removeEventListener('cancel', handleCancel);
|
|
118
133
|
};
|
|
119
134
|
});
|
|
120
135
|
|
|
@@ -170,11 +185,14 @@
|
|
|
170
185
|
// =========================================================================
|
|
171
186
|
/** Opens the modal. */
|
|
172
187
|
export const open = (title?: string): void => {
|
|
173
|
-
if (!dialogRef) return;
|
|
188
|
+
if (!dialogRef || isVisible) return;
|
|
174
189
|
|
|
190
|
+
isVisible = true;
|
|
175
191
|
isOpen = true;
|
|
176
192
|
previousActiveElement = document.activeElement as HTMLElement;
|
|
177
193
|
|
|
194
|
+
// 閉じるアニメーションの途中で開き直した場合は close を取り消す
|
|
195
|
+
pendingCloseReason = null;
|
|
178
196
|
dialogRef.classList.add('fade-in');
|
|
179
197
|
dialogRef.removeEventListener('animationend', closeEnd);
|
|
180
198
|
dialogRef.showModal();
|
|
@@ -184,14 +202,17 @@
|
|
|
184
202
|
dialogRef?.focus();
|
|
185
203
|
}
|
|
186
204
|
announceOpenClose(componentType, true, title || ariaLabel || '');
|
|
205
|
+
onOpen?.();
|
|
187
206
|
}, 0);
|
|
188
207
|
};
|
|
189
208
|
|
|
190
209
|
/** Closes the modal. */
|
|
191
|
-
export const close = (title?: string): void => {
|
|
192
|
-
if (!dialogRef) return;
|
|
210
|
+
export const close = (title?: string, reason: CloseReason = 'explicit'): void => {
|
|
211
|
+
if (!dialogRef || !isVisible) return;
|
|
193
212
|
|
|
213
|
+
isVisible = false;
|
|
194
214
|
isOpen = false;
|
|
215
|
+
pendingCloseReason = reason;
|
|
195
216
|
dialogRef.classList.add('fade-out');
|
|
196
217
|
dialogRef.addEventListener('animationend', closeEnd, { once: true });
|
|
197
218
|
|
|
@@ -209,11 +230,18 @@
|
|
|
209
230
|
previousActiveElement.focus();
|
|
210
231
|
}
|
|
211
232
|
previousActiveElement = null;
|
|
233
|
+
|
|
234
|
+
// アニメーション完了後に onClose を呼ぶ
|
|
235
|
+
if (pendingCloseReason) {
|
|
236
|
+
const reason = pendingCloseReason;
|
|
237
|
+
pendingCloseReason = null;
|
|
238
|
+
onClose?.(reason);
|
|
239
|
+
}
|
|
212
240
|
};
|
|
213
241
|
|
|
214
242
|
/** Toggles between open and closed. */
|
|
215
243
|
export const toggle = (title?: string): void => {
|
|
216
|
-
if (
|
|
244
|
+
if (isVisible) {
|
|
217
245
|
close(title);
|
|
218
246
|
} else {
|
|
219
247
|
open(title);
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* This prevents state synchronization bugs and ensures consistent behavior.
|
|
12
12
|
*/
|
|
13
13
|
import type { Snippet } from 'svelte';
|
|
14
|
+
import type { CloseReason } from '../types/propOptions';
|
|
14
15
|
export type ModalProps = {
|
|
15
16
|
children?: Snippet;
|
|
16
17
|
/** Internal label used for accessibility announcements. @default 'Modal' */
|
|
@@ -29,10 +30,13 @@ export type ModalProps = {
|
|
|
29
30
|
ariaLabel?: string;
|
|
30
31
|
ariaLabelledby?: string;
|
|
31
32
|
ariaDescribedby?: string;
|
|
33
|
+
onOpen?: () => void;
|
|
34
|
+
/** Called after the close animation completes, with the reason it was closed. */
|
|
35
|
+
onClose?: (reason: CloseReason) => void;
|
|
32
36
|
};
|
|
33
37
|
declare const Modal: import("svelte").Component<ModalProps, {
|
|
34
38
|
/** Opens the modal. */ open: (title?: string) => void;
|
|
35
|
-
/** Closes the modal. */ close: (title?: string) => void;
|
|
39
|
+
/** Closes the modal. */ close: (title?: string, reason?: CloseReason) => void;
|
|
36
40
|
/** @internal Completes the close animation. Called automatically. */ closeEnd: () => void;
|
|
37
41
|
/** Toggles between open and closed. */ toggle: (title?: string) => void;
|
|
38
42
|
}, "isOpen">;
|
package/dist/index.d.ts
CHANGED
|
@@ -85,7 +85,7 @@ export type { NavProps } from './components/Nav.svelte';
|
|
|
85
85
|
export type { NavItemProps, NavItemSelectedVariant } from './components/NavItem.svelte';
|
|
86
86
|
export type { TabProps } from './components/Tab.svelte';
|
|
87
87
|
export type { TextareaProps } from './components/Textarea.svelte';
|
|
88
|
-
export type { PopupPosition, SnackbarPosition, FabPosition, ButtonVariant, ButtonSize, IconPosition, ComponentSize, SnackbarType, SnackbarVariant, BadgeVariant, DatepickerMode, FocusStyle, NavVariant, ChildrenVariant, StepNavOrientation } from './types/propOptions';
|
|
88
|
+
export type { PopupPosition, SnackbarPosition, FabPosition, ButtonVariant, ButtonSize, IconPosition, ComponentSize, SnackbarType, SnackbarVariant, BadgeVariant, DatepickerMode, FocusStyle, NavVariant, ChildrenVariant, StepNavOrientation, CloseReason } from './types/propOptions';
|
|
89
89
|
export type { MenuItem } from './types/menuItem';
|
|
90
90
|
export type { StepItem } from './types/stepItem';
|
|
91
91
|
export type { SegmentedControlItem } from './types/segmentedControlItem';
|