kosui 0.2.0 → 0.4.0

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.
Files changed (238) hide show
  1. package/README.md +34 -3
  2. package/dist/alert/alert.svelte +16 -157
  3. package/dist/alert/alert.svelte.d.ts +3 -26
  4. package/dist/alert/index.d.ts +1 -1
  5. package/dist/alert/index.js +1 -1
  6. package/dist/autocomplete/autocomplete.svelte +126 -12
  7. package/dist/autocomplete/autocomplete.svelte.d.ts +30 -6
  8. package/dist/autocomplete/index.d.ts +1 -5
  9. package/dist/autocomplete/index.js +1 -5
  10. package/dist/avatar/avatar.svelte +16 -10
  11. package/dist/badge/badge.svelte +31 -20
  12. package/dist/badge/badge.svelte.d.ts +8 -4
  13. package/dist/badge/index.d.ts +1 -1
  14. package/dist/base.d.ts +4 -8
  15. package/dist/base.js +14 -28
  16. package/dist/box/box.svelte +2 -14
  17. package/dist/box/box.svelte.d.ts +0 -2
  18. package/dist/breadcrumbs/breadcrumbs.svelte +38 -19
  19. package/dist/breadcrumbs/breadcrumbs.svelte.d.ts +6 -4
  20. package/dist/breadcrumbs/index.d.ts +1 -1
  21. package/dist/breadcrumbs/index.js +1 -1
  22. package/dist/button/button.svelte +96 -31
  23. package/dist/button/button.svelte.d.ts +11 -5
  24. package/dist/button/index.d.ts +1 -1
  25. package/dist/button/index.js +1 -1
  26. package/dist/chip/chip.svelte +62 -14
  27. package/dist/chip/chip.svelte.d.ts +2 -1
  28. package/dist/command/action.svelte.js +13 -18
  29. package/dist/command/command-palette.svelte +163 -0
  30. package/dist/command/command-palette.svelte.d.ts +11 -0
  31. package/dist/command/index.d.ts +1 -8
  32. package/dist/command/index.js +1 -8
  33. package/dist/common/divider.svelte +10 -5
  34. package/dist/common/divider.svelte.d.ts +3 -3
  35. package/dist/common/index.d.ts +0 -2
  36. package/dist/common/index.js +0 -2
  37. package/dist/dialog/dialog-provider.svelte +70 -0
  38. package/dist/dialog/dialog-provider.svelte.d.ts +7 -0
  39. package/dist/dialog/dialog.svelte +33 -35
  40. package/dist/dialog/dialog.svelte.d.ts +4 -8
  41. package/dist/dialog/dialogs.svelte.d.ts +49 -0
  42. package/dist/dialog/dialogs.svelte.js +73 -0
  43. package/dist/dialog/index.d.ts +2 -3
  44. package/dist/dialog/index.js +2 -3
  45. package/dist/fab/fab.svelte +30 -10
  46. package/dist/fab/fab.svelte.d.ts +3 -2
  47. package/dist/index.d.ts +0 -1
  48. package/dist/index.js +0 -1
  49. package/dist/input/field.d.ts +8 -0
  50. package/dist/input/field.js +7 -0
  51. package/dist/input/index.d.ts +2 -1
  52. package/dist/input/index.js +2 -1
  53. package/dist/input/input.svelte +21 -66
  54. package/dist/input/input.svelte.d.ts +6 -16
  55. package/dist/link/link.svelte +33 -42
  56. package/dist/link/link.svelte.d.ts +4 -3
  57. package/dist/markdown/index.d.ts +2 -25
  58. package/dist/markdown/index.js +0 -24
  59. package/dist/markdown/markdown-tokens.svelte +166 -6
  60. package/dist/markdown/markdown-tokens.svelte.d.ts +2 -0
  61. package/dist/markdown/markdown.svelte +62 -173
  62. package/dist/markdown/markdown.svelte.d.ts +7 -27
  63. package/dist/markdown/parse.d.ts +5 -0
  64. package/dist/markdown/parse.js +30 -0
  65. package/dist/markdown/types.d.ts +28 -0
  66. package/dist/markdown/types.js +1 -0
  67. package/dist/menu/index.d.ts +3 -6
  68. package/dist/menu/index.js +3 -6
  69. package/dist/menu/menu-actions.svelte +44 -38
  70. package/dist/menu/menu-context.d.ts +4 -0
  71. package/dist/menu/menu-context.js +2 -0
  72. package/dist/menu/menu-group.svelte +40 -0
  73. package/dist/menu/menu-group.svelte.d.ts +11 -0
  74. package/dist/menu/menu-item.svelte +31 -54
  75. package/dist/menu/menu-item.svelte.d.ts +7 -5
  76. package/dist/menu/menu.svelte +160 -15
  77. package/dist/menu/menu.svelte.d.ts +15 -4
  78. package/dist/merge-props.d.ts +1 -1
  79. package/dist/merge-props.js +10 -5
  80. package/dist/modal/modal.svelte +60 -83
  81. package/dist/modal/modal.svelte.d.ts +5 -4
  82. package/dist/popover/index.d.ts +1 -1
  83. package/dist/popover/index.js +1 -1
  84. package/dist/popover/popover.svelte +58 -111
  85. package/dist/popover/popover.svelte.d.ts +6 -8
  86. package/dist/progress/circular-progress.svelte +64 -30
  87. package/dist/progress/circular-progress.svelte.d.ts +7 -8
  88. package/dist/shortcut/index.d.ts +2 -2
  89. package/dist/shortcut/index.js +1 -1
  90. package/dist/shortcut/shortcut-hint.svelte +36 -0
  91. package/dist/shortcut/shortcut-hint.svelte.d.ts +9 -0
  92. package/dist/shortcut/shortcut.d.ts +19 -14
  93. package/dist/shortcut/shortcut.js +105 -52
  94. package/dist/skeleton/skeleton.svelte +13 -8
  95. package/dist/skeleton/skeleton.svelte.d.ts +2 -5
  96. package/dist/slider/index.d.ts +1 -8
  97. package/dist/slider/index.js +1 -8
  98. package/dist/slider/slider.svelte +180 -211
  99. package/dist/slider/slider.svelte.d.ts +11 -32
  100. package/dist/switch/index.d.ts +1 -2
  101. package/dist/switch/index.js +1 -2
  102. package/dist/switch/switch.svelte +36 -60
  103. package/dist/switch/switch.svelte.d.ts +6 -17
  104. package/dist/textarea/index.d.ts +1 -1
  105. package/dist/textarea/index.js +1 -1
  106. package/dist/textarea/textarea.svelte +23 -63
  107. package/dist/textarea/textarea.svelte.d.ts +8 -18
  108. package/dist/theme.css +191 -0
  109. package/dist/toggle/index.d.ts +0 -1
  110. package/dist/toggle/index.js +0 -1
  111. package/dist/toggle/toggle-button.svelte +72 -38
  112. package/dist/toggle/toggle-button.svelte.d.ts +4 -4
  113. package/dist/toggle/toggle-context.d.ts +10 -0
  114. package/dist/toggle/toggle-context.js +2 -0
  115. package/dist/toggle/toggle-group.svelte +41 -22
  116. package/dist/toggle/toggle-group.svelte.d.ts +7 -4
  117. package/dist/tooltip/tooltip.svelte +67 -26
  118. package/dist/tooltip/tooltip.svelte.d.ts +10 -4
  119. package/dist/utils.d.ts +9 -3
  120. package/dist/utils.js +10 -0
  121. package/package.json +6 -5
  122. package/dist/autocomplete/autocomplete-content.svelte +0 -47
  123. package/dist/autocomplete/autocomplete-content.svelte.d.ts +0 -10
  124. package/dist/autocomplete/autocomplete-context.svelte.d.ts +0 -12
  125. package/dist/autocomplete/autocomplete-context.svelte.js +0 -34
  126. package/dist/autocomplete/autocomplete-input.svelte +0 -36
  127. package/dist/autocomplete/autocomplete-input.svelte.d.ts +0 -5
  128. package/dist/autocomplete/autocomplete-item.svelte +0 -68
  129. package/dist/autocomplete/autocomplete-item.svelte.d.ts +0 -11
  130. package/dist/base.css +0 -71
  131. package/dist/bindable.svelte.d.ts +0 -7
  132. package/dist/bindable.svelte.js +0 -25
  133. package/dist/command/command-content.svelte +0 -15
  134. package/dist/command/command-content.svelte.d.ts +0 -8
  135. package/dist/command/command-context.svelte.d.ts +0 -5
  136. package/dist/command/command-context.svelte.js +0 -13
  137. package/dist/command/command-input.svelte +0 -44
  138. package/dist/command/command-input.svelte.d.ts +0 -7
  139. package/dist/command/command-item.svelte +0 -66
  140. package/dist/command/command-item.svelte.d.ts +0 -11
  141. package/dist/command/command-search.svelte +0 -28
  142. package/dist/command/command-search.svelte.d.ts +0 -5
  143. package/dist/command/command.svelte +0 -19
  144. package/dist/command/command.svelte.d.ts +0 -8
  145. package/dist/command/commander.svelte +0 -107
  146. package/dist/command/commander.svelte.d.ts +0 -9
  147. package/dist/common/header.svelte +0 -35
  148. package/dist/common/header.svelte.d.ts +0 -10
  149. package/dist/common/item-context.svelte.d.ts +0 -12
  150. package/dist/common/item-context.svelte.js +0 -77
  151. package/dist/dialog/dialog-button.svelte +0 -25
  152. package/dist/dialog/dialog-button.svelte.d.ts +0 -32
  153. package/dist/dialog/dialoguer-context.svelte.d.ts +0 -53
  154. package/dist/dialog/dialoguer-context.svelte.js +0 -85
  155. package/dist/dialog/dialoguer.svelte +0 -65
  156. package/dist/dialog/dialoguer.svelte.d.ts +0 -7
  157. package/dist/markdown/markdown-blockquote.svelte +0 -14
  158. package/dist/markdown/markdown-blockquote.svelte.d.ts +0 -7
  159. package/dist/markdown/markdown-br.svelte +0 -12
  160. package/dist/markdown/markdown-br.svelte.d.ts +0 -7
  161. package/dist/markdown/markdown-code.svelte +0 -14
  162. package/dist/markdown/markdown-code.svelte.d.ts +0 -7
  163. package/dist/markdown/markdown-codespan.svelte +0 -15
  164. package/dist/markdown/markdown-codespan.svelte.d.ts +0 -7
  165. package/dist/markdown/markdown-context.svelte.d.ts +0 -26
  166. package/dist/markdown/markdown-context.svelte.js +0 -31
  167. package/dist/markdown/markdown-def.svelte +0 -15
  168. package/dist/markdown/markdown-def.svelte.d.ts +0 -7
  169. package/dist/markdown/markdown-del.svelte +0 -14
  170. package/dist/markdown/markdown-del.svelte.d.ts +0 -7
  171. package/dist/markdown/markdown-em.svelte +0 -14
  172. package/dist/markdown/markdown-em.svelte.d.ts +0 -7
  173. package/dist/markdown/markdown-escape.svelte +0 -8
  174. package/dist/markdown/markdown-escape.svelte.d.ts +0 -5
  175. package/dist/markdown/markdown-heading.svelte +0 -16
  176. package/dist/markdown/markdown-heading.svelte.d.ts +0 -7
  177. package/dist/markdown/markdown-hr.svelte +0 -12
  178. package/dist/markdown/markdown-hr.svelte.d.ts +0 -7
  179. package/dist/markdown/markdown-html.svelte +0 -9
  180. package/dist/markdown/markdown-html.svelte.d.ts +0 -5
  181. package/dist/markdown/markdown-image.svelte +0 -12
  182. package/dist/markdown/markdown-image.svelte.d.ts +0 -7
  183. package/dist/markdown/markdown-link.svelte +0 -17
  184. package/dist/markdown/markdown-link.svelte.d.ts +0 -7
  185. package/dist/markdown/markdown-list-item.svelte +0 -14
  186. package/dist/markdown/markdown-list-item.svelte.d.ts +0 -7
  187. package/dist/markdown/markdown-list.svelte +0 -19
  188. package/dist/markdown/markdown-list.svelte.d.ts +0 -7
  189. package/dist/markdown/markdown-paragraph.svelte +0 -16
  190. package/dist/markdown/markdown-paragraph.svelte.d.ts +0 -7
  191. package/dist/markdown/markdown-space.svelte +0 -8
  192. package/dist/markdown/markdown-space.svelte.d.ts +0 -5
  193. package/dist/markdown/markdown-strong.svelte +0 -15
  194. package/dist/markdown/markdown-strong.svelte.d.ts +0 -7
  195. package/dist/markdown/markdown-table-cell.svelte +0 -16
  196. package/dist/markdown/markdown-table-cell.svelte.d.ts +0 -7
  197. package/dist/markdown/markdown-table.svelte +0 -36
  198. package/dist/markdown/markdown-table.svelte.d.ts +0 -7
  199. package/dist/markdown/markdown-text.svelte +0 -8
  200. package/dist/markdown/markdown-text.svelte.d.ts +0 -5
  201. package/dist/markdown/markdown-token.svelte +0 -25
  202. package/dist/markdown/markdown-token.svelte.d.ts +0 -7
  203. package/dist/menu/menu-content.svelte +0 -47
  204. package/dist/menu/menu-content.svelte.d.ts +0 -10
  205. package/dist/menu/menu-context.svelte.d.ts +0 -18
  206. package/dist/menu/menu-context.svelte.js +0 -118
  207. package/dist/menu/menu-trigger-button.svelte +0 -31
  208. package/dist/menu/menu-trigger-button.svelte.d.ts +0 -5
  209. package/dist/menu/menu-trigger.svelte +0 -42
  210. package/dist/menu/menu-trigger.svelte.d.ts +0 -10
  211. package/dist/ordered-html-elements.d.ts +0 -7
  212. package/dist/ordered-html-elements.js +0 -29
  213. package/dist/portal/index.d.ts +0 -2
  214. package/dist/portal/index.js +0 -2
  215. package/dist/portal/portal.svelte +0 -15
  216. package/dist/portal/portal.svelte.d.ts +0 -8
  217. package/dist/portal/proxy.svelte +0 -13
  218. package/dist/portal/proxy.svelte.d.ts +0 -7
  219. package/dist/shortcut/shortcut-badge.svelte +0 -41
  220. package/dist/shortcut/shortcut-badge.svelte.d.ts +0 -11
  221. package/dist/slider/slider-context.svelte.d.ts +0 -50
  222. package/dist/slider/slider-context.svelte.js +0 -216
  223. package/dist/slider/slider-handle-label-arrow.svelte +0 -18
  224. package/dist/slider/slider-handle-label-arrow.svelte.d.ts +0 -6
  225. package/dist/slider/slider-handle-label.svelte +0 -35
  226. package/dist/slider/slider-handle-label.svelte.d.ts +0 -6
  227. package/dist/slider/slider-handle.svelte +0 -54
  228. package/dist/slider/slider-handle.svelte.d.ts +0 -6
  229. package/dist/slider/slider-mark-dot.svelte +0 -25
  230. package/dist/slider/slider-mark-dot.svelte.d.ts +0 -6
  231. package/dist/slider/slider-mark-label.svelte +0 -37
  232. package/dist/slider/slider-mark-label.svelte.d.ts +0 -9
  233. package/dist/slider/slider-mark.svelte +0 -33
  234. package/dist/slider/slider-mark.svelte.d.ts +0 -9
  235. package/dist/switch/switch-thumb.svelte +0 -52
  236. package/dist/switch/switch-thumb.svelte.d.ts +0 -7
  237. package/dist/toggle/toggle-context.svelte.d.ts +0 -9
  238. package/dist/toggle/toggle-context.svelte.js +0 -32
@@ -1,11 +1,13 @@
1
- import { type Variants } from "../base.js";
2
- import type { ClassName, ElementRef } from "../utils.js";
1
+ import { type ColorProp } from "../base.js";
2
+ import { type ClassName } from "../utils.js";
3
3
  import type { Snippet } from "svelte";
4
4
  import type { HTMLButtonAttributes } from "svelte/elements";
5
5
  export type MenuItemProps = {
6
- onSelect?: (el: HTMLElement) => void;
7
- children: Snippet<[]>;
8
- } & ElementRef & ClassName & Variants & Omit<HTMLButtonAttributes, "type" | "value">;
6
+ onSelect?: () => void;
7
+ checked?: boolean;
8
+ el?: HTMLButtonElement;
9
+ children: Snippet;
10
+ } & ClassName & ColorProp & Omit<HTMLButtonAttributes, "type" | "value" | "role" | "class">;
9
11
  declare const MenuItem: import("svelte").Component<MenuItemProps, {}, "el">;
10
12
  type MenuItem = ReturnType<typeof MenuItem>;
11
13
  export default MenuItem;
@@ -1,29 +1,174 @@
1
1
  <script module lang="ts">
2
- import { MenuContext, setMenuContext } from "./index.js";
3
- import type { ElementRef } from "../utils.js";
2
+ import type { ShapeProp } from "../base.js";
3
+ import { baseClasses } from "../base.js";
4
+ import { mergeProps } from "../merge-props.js";
5
+ import {
6
+ Popover,
7
+ type PopoverPlacement,
8
+ type PopoverProps,
9
+ } from "../popover/index.js";
10
+ import { Shortcut } from "../shortcut/index.js";
11
+ import { cn, TypingBuffer } from "../utils.js";
4
12
  import type { Snippet } from "svelte";
13
+ import { createAttachmentKey, type Attachment } from "svelte/attachments";
14
+ import { setMenuContext } from "./menu-context.js";
15
+
16
+ export type MenuTriggerProps = {
17
+ [attachment: symbol]: Attachment<HTMLElement>;
18
+ id: string;
19
+ "aria-haspopup": "menu";
20
+ "aria-expanded": boolean;
21
+ "aria-controls"?: string;
22
+ onclick: (event: MouseEvent) => void;
23
+ onkeydown: (event: KeyboardEvent) => void;
24
+ };
5
25
 
6
26
  export type MenuProps = {
7
- open?: boolean;
8
- children: Snippet;
9
- } & ElementRef;
27
+ trigger: Snippet<[MenuTriggerProps]>;
28
+ placement?: PopoverPlacement;
29
+ } & ShapeProp &
30
+ Omit<PopoverProps, "placement" | "anchorEl" | "onKeydownWhileOpen">;
31
+
32
+ type Start = "first" | "last" | "menu";
10
33
  </script>
11
34
 
12
35
  <script lang="ts">
13
36
  let {
14
37
  open = $bindable(false),
15
- children,
16
38
  el = $bindable(),
39
+ trigger,
40
+ placement = "bottom-start",
41
+ shape = "xs",
42
+ class: className,
43
+ children,
44
+ ...restProps
17
45
  }: MenuProps = $props();
18
46
 
19
- setMenuContext(
20
- new MenuContext(
21
- () => open,
22
- (value) => (open = value),
23
- () => el,
24
- (value) => (el = value),
25
- ),
26
- );
47
+ const id = $props.id();
48
+ const anchor = createAttachmentKey();
49
+ const buffer = new TypingBuffer();
50
+ let anchorEl: HTMLElement | undefined = $state();
51
+ let start: Start = "menu";
52
+
53
+ function show(from: Start) {
54
+ start = from;
55
+ open = true;
56
+ }
57
+
58
+ function close() {
59
+ open = false;
60
+ anchorEl?.focus();
61
+ }
62
+
63
+ setMenuContext({ close });
64
+
65
+ $effect(() => {
66
+ if (open && el) queueMicrotask(focusStart);
67
+ });
68
+
69
+ function items(): HTMLElement[] {
70
+ return el
71
+ ? [
72
+ ...el.querySelectorAll<HTMLElement>(
73
+ '[role^="menuitem"]:not(:disabled)',
74
+ ),
75
+ ]
76
+ : [];
77
+ }
78
+
79
+ function focusStart() {
80
+ const list = items();
81
+ const target =
82
+ start === "first" ? list[0] : start === "last" ? list.at(-1) : el;
83
+ (target ?? el)?.focus();
84
+ }
85
+
86
+ function typeahead(char: string, list: HTMLElement[], index: number) {
87
+ const typed = buffer.append(char).toLowerCase();
88
+ const prefix = [...typed].every((c) => c === typed[0]) ? typed[0] : typed;
89
+ const ordered = [...list.slice(index + 1), ...list.slice(0, index + 1)];
90
+ return ordered.find((item) =>
91
+ item.textContent?.trim().toLowerCase().startsWith(prefix),
92
+ );
93
+ }
94
+
95
+ function onkeydown(event: KeyboardEvent) {
96
+ if (
97
+ Shortcut.TAB_FORWARD.matches(event) ||
98
+ Shortcut.TAB_BACKWARD.matches(event)
99
+ ) {
100
+ open = false;
101
+ return;
102
+ }
103
+ const list = items();
104
+ const index = list.indexOf(document.activeElement as HTMLElement);
105
+ let target: HTMLElement | undefined;
106
+ if (Shortcut.ARROW_DOWN.matches(event)) {
107
+ target = list[(index + 1) % list.length];
108
+ } else if (Shortcut.ARROW_UP.matches(event)) {
109
+ target = index <= 0 ? list.at(-1) : list[index - 1];
110
+ } else if (Shortcut.HOME.matches(event)) {
111
+ target = list[0];
112
+ } else if (Shortcut.END.matches(event)) {
113
+ target = list.at(-1);
114
+ } else if (Shortcut.isChar(event) && event.key !== " ") {
115
+ target = typeahead(event.key, list, index);
116
+ } else if (
117
+ !Shortcut.ENTER.matches(event) &&
118
+ !Shortcut.SPACE.matches(event)
119
+ ) {
120
+ return;
121
+ }
122
+ target?.focus();
123
+ if (target) event.preventDefault();
124
+ event.stopPropagation();
125
+ }
126
+
127
+ function onpointerleave() {
128
+ if (el?.contains(document.activeElement)) el.focus();
129
+ }
130
+
131
+ const triggerProps: MenuTriggerProps = $derived({
132
+ [anchor]: (node) => {
133
+ anchorEl = node;
134
+ },
135
+ id: `${id}-trigger`,
136
+ "aria-haspopup": "menu",
137
+ "aria-expanded": open,
138
+ "aria-controls": open ? `${id}-menu` : undefined,
139
+ onclick: (event) => {
140
+ if (open) open = false;
141
+ else show(event.detail === 0 ? "first" : "menu");
142
+ },
143
+ onkeydown: (event) => {
144
+ if (Shortcut.ARROW_DOWN.matches(event)) show("first");
145
+ else if (Shortcut.ARROW_UP.matches(event)) show("last");
146
+ else return;
147
+ event.preventDefault();
148
+ },
149
+ });
27
150
  </script>
28
151
 
29
- {@render children()}
152
+ {@render trigger(triggerProps)}
153
+
154
+ <Popover
155
+ bind:open
156
+ bind:el
157
+ {anchorEl}
158
+ {placement}
159
+ id="{id}-menu"
160
+ role="menu"
161
+ aria-labelledby="{id}-trigger"
162
+ tabindex={-1}
163
+ class={cn(
164
+ baseClasses({ shape }),
165
+ "max-h-[min(60vh,32rem)] min-w-40 overflow-y-auto bg-m3-surface-container p-1 text-m3-on-surface shadow-md shadow-m3-shadow/20 outline-none",
166
+ className,
167
+ )}
168
+ {...mergeProps(restProps, {
169
+ onkeydown,
170
+ onpointerleave,
171
+ })}
172
+ >
173
+ {@render children()}
174
+ </Popover>
@@ -1,9 +1,20 @@
1
- import type { ElementRef } from "../utils.js";
1
+ import type { ShapeProp } from "../base.js";
2
+ import { type PopoverPlacement, type PopoverProps } from "../popover/index.js";
2
3
  import type { Snippet } from "svelte";
4
+ import { type Attachment } from "svelte/attachments";
5
+ export type MenuTriggerProps = {
6
+ [attachment: symbol]: Attachment<HTMLElement>;
7
+ id: string;
8
+ "aria-haspopup": "menu";
9
+ "aria-expanded": boolean;
10
+ "aria-controls"?: string;
11
+ onclick: (event: MouseEvent) => void;
12
+ onkeydown: (event: KeyboardEvent) => void;
13
+ };
3
14
  export type MenuProps = {
4
- open?: boolean;
5
- children: Snippet;
6
- } & ElementRef;
15
+ trigger: Snippet<[MenuTriggerProps]>;
16
+ placement?: PopoverPlacement;
17
+ } & ShapeProp & Omit<PopoverProps, "placement" | "anchorEl" | "onKeydownWhileOpen">;
7
18
  declare const Menu: import("svelte").Component<MenuProps, {}, "el" | "open">;
8
19
  type Menu = ReturnType<typeof Menu>;
9
20
  export default Menu;
@@ -4,7 +4,7 @@ export declare function interceptStopImmediatePropagation(event: Event, callback
4
4
  export declare function runEventHandlers(target: EventTarget, event: Event & {
5
5
  currentTarget: EventTarget & Element;
6
6
  }, ...handlers: EventHandler[]): void;
7
- type Props = Record<string, unknown>;
7
+ type Props = Record<PropertyKey, unknown>;
8
8
  type PropsUnion<T extends Props[]> = (T extends (infer U)[] ? U extends Props ? (k: {
9
9
  [K in keyof U]: U[K];
10
10
  }) => void : never : never) extends (k: infer I) => void ? I : never;
@@ -1,4 +1,4 @@
1
- import { twMerge } from "tailwind-merge";
1
+ import { cn } from "./utils.js";
2
2
  function isClass(value) {
3
3
  if (value === null || value === undefined) {
4
4
  return true;
@@ -9,10 +9,12 @@ function isClass(value) {
9
9
  if (Array.isArray(value)) {
10
10
  return value.every((item) => isClass(item));
11
11
  }
12
- return false;
12
+ return Object.getPrototypeOf(value) === Object.prototype;
13
13
  }
14
14
  function isHandlerKey(key) {
15
- return key.startsWith("on") && key[2].toLowerCase() === key[2];
15
+ return (typeof key === "string" &&
16
+ key.startsWith("on") &&
17
+ key[2].toLowerCase() === key[2]);
16
18
  }
17
19
  function isHandler(value) {
18
20
  return typeof value === "function";
@@ -48,10 +50,13 @@ function chainCallbacks(...callbacks) {
48
50
  }
49
51
  };
50
52
  }
53
+ function ownKeys(props) {
54
+ return [...Object.keys(props), ...Object.getOwnPropertySymbols(props)];
55
+ }
51
56
  export function mergeProps(...args) {
52
57
  const result = {};
53
58
  for (const props of args) {
54
- for (const key in props) {
59
+ for (const key of ownKeys(props)) {
55
60
  const prevVal = result[key];
56
61
  const currVal = props[key];
57
62
  if (currVal === undefined) {
@@ -70,7 +75,7 @@ export function mergeProps(...args) {
70
75
  continue;
71
76
  }
72
77
  if (key === "class" && isClass(prevVal) && isClass(currVal)) {
73
- result[key] = twMerge(prevVal, currVal);
78
+ result[key] = cn(prevVal, currVal);
74
79
  continue;
75
80
  }
76
81
  result[key] = currVal;
@@ -1,123 +1,100 @@
1
1
  <script module lang="ts">
2
2
  import { events } from "../index.js";
3
+ import { baseClasses, type ShapeProp } from "../base.js";
3
4
  import { mergeProps } from "../merge-props.js";
4
- import { Shortcut } from "../shortcut/index.js";
5
- import type { ClassName } from "../utils.js";
5
+ import { cn, type ClassName } from "../utils.js";
6
6
  import type { Snippet } from "svelte";
7
7
  import type { HTMLDialogAttributes } from "svelte/elements";
8
+ import { prefersReducedMotion } from "svelte/motion";
8
9
  import { scale } from "svelte/transition";
9
- import { twMerge } from "tailwind-merge";
10
10
 
11
11
  export type ModalProps = {
12
- ref?: HTMLDialogElement;
12
+ el?: HTMLDialogElement;
13
13
  onCancel?: () => void;
14
14
  children: Snippet;
15
15
  } & ClassName &
16
- HTMLDialogAttributes;
16
+ ShapeProp &
17
+ Omit<HTMLDialogAttributes, "class">;
18
+
19
+ type Layer = { el: HTMLDialogElement; opener?: HTMLElement };
20
+
21
+ const layers: Layer[] = [];
22
+
23
+ function focusedElement() {
24
+ const active = document.activeElement;
25
+ return active instanceof HTMLElement && active !== document.body
26
+ ? active
27
+ : undefined;
28
+ }
29
+
30
+ function closeLayer(layer: Layer) {
31
+ const index = layers.indexOf(layer);
32
+ layers.splice(index, 1);
33
+ const above = layers[index];
34
+ if (above) {
35
+ if (!above.opener?.isConnected || layer.el.contains(above.opener)) {
36
+ above.opener = layer.opener;
37
+ }
38
+ } else if (layer.opener?.isConnected) {
39
+ layer.opener.focus();
40
+ }
41
+ }
17
42
  </script>
18
43
 
19
44
  <script lang="ts">
20
45
  let {
21
- ref = $bindable(),
46
+ el = $bindable(),
22
47
  open = $bindable(),
23
48
  onCancel,
24
49
  children,
50
+ shape = "xl",
25
51
  class: className,
26
52
  ...restProps
27
53
  }: ModalProps = $props();
28
54
 
29
- function handleKeydown(event: KeyboardEvent) {
30
- if (Shortcut.ESCAPE.matches(event)) {
31
- open = false;
32
- onCancel?.();
33
- event.preventDefault();
34
- }
55
+ function isolateKeys(event: KeyboardEvent) {
35
56
  event.stopImmediatePropagation();
36
57
  }
37
58
 
38
- function handleClickOutside(event: MouseEvent) {
39
- if (ref && ref === event.target) {
40
- const rect = ref.getBoundingClientRect();
41
- if (
42
- event.clientY < rect.top ||
43
- event.clientY > rect.top + rect.height ||
44
- event.clientX < rect.left ||
45
- event.clientX > rect.left + rect.width
46
- ) {
47
- open = false;
48
- onCancel?.();
49
- event.preventDefault();
50
- event.stopImmediatePropagation();
51
- }
52
- }
59
+ function oncancel(event: Event) {
60
+ event.preventDefault();
61
+ open = false;
62
+ onCancel?.();
53
63
  }
54
64
 
55
65
  $effect(() => {
56
- if (ref) {
57
- ref.showModal();
58
- events.on("keydown", handleKeydown);
59
- events.on("mousedown", handleClickOutside);
66
+ if (!el) return;
67
+ const layer: Layer = { el, opener: focusedElement() };
68
+ layers.push(layer);
69
+ if (!el.open) el.showModal();
70
+ events.on("keydown", isolateKeys);
60
71
 
61
- return () => {
62
- events.remove("keydown", handleKeydown);
63
- events.remove("mousedown", handleClickOutside);
64
- };
65
- }
72
+ return () => {
73
+ events.remove("keydown", isolateKeys);
74
+ closeLayer(layer);
75
+ };
66
76
  });
67
77
  </script>
68
78
 
69
79
  {#if open}
70
80
  <dialog
71
- bind:this={ref}
72
- class={twMerge("m-auto rounded-lg p-5 shadow-lg", className)}
73
- transition:scale={{ duration: 150, start: 0.95 }}
81
+ bind:this={el}
82
+ closedby="any"
83
+ class={cn(
84
+ "m-auto bg-m3-surface-container-high p-6 text-m3-on-surface shadow-lg backdrop:bg-black/50 backdrop:backdrop-blur-[2px] data-closing:backdrop:bg-transparent motion-safe:backdrop:transition-colors starting:backdrop:bg-transparent",
85
+ baseClasses({ shape }),
86
+ className,
87
+ )}
88
+ transition:scale={{
89
+ duration: prefersReducedMotion.current ? 0 : 150,
90
+ start: 0.95,
91
+ }}
74
92
  {...mergeProps(restProps, {
75
- onintrostart: () => {
76
- if (ref) {
77
- ref.classList.remove("backdrop-out");
78
- ref.classList.add("backdrop-in");
79
- }
80
- },
81
- onoutrostart: () => {
82
- if (ref) {
83
- ref.classList.remove("backdrop-in");
84
- ref.classList.add("backdrop-out");
85
- }
86
- },
93
+ oncancel,
94
+ onintrostart: () => el?.removeAttribute("data-closing"),
95
+ onoutrostart: () => el?.setAttribute("data-closing", ""),
87
96
  })}
88
97
  >
89
98
  {@render children()}
90
99
  </dialog>
91
100
  {/if}
92
-
93
- <style>@layer components {
94
- .backdrop-in::backdrop {
95
- animation: backdrop-in 150ms forwards;
96
- }
97
-
98
- .backdrop-out::backdrop {
99
- animation: backdrop-out 150ms forwards;
100
- }
101
-
102
- @keyframes backdrop-in {
103
- from {
104
- background: rgba(0, 0, 0, 0);
105
- backdrop-filter: blur(0px);
106
- }
107
- to {
108
- background: rgba(0, 0, 0, 0.5);
109
- backdrop-filter: blur(2px);
110
- }
111
- }
112
-
113
- @keyframes backdrop-out {
114
- from {
115
- background: rgba(0, 0, 0, 0.5);
116
- backdrop-filter: blur(2px);
117
- }
118
- to {
119
- background: rgba(0, 0, 0, 0);
120
- backdrop-filter: blur(0px);
121
- }
122
- }
123
- }</style>
@@ -1,11 +1,12 @@
1
- import type { ClassName } from "../utils.js";
1
+ import { type ShapeProp } from "../base.js";
2
+ import { type ClassName } from "../utils.js";
2
3
  import type { Snippet } from "svelte";
3
4
  import type { HTMLDialogAttributes } from "svelte/elements";
4
5
  export type ModalProps = {
5
- ref?: HTMLDialogElement;
6
+ el?: HTMLDialogElement;
6
7
  onCancel?: () => void;
7
8
  children: Snippet;
8
- } & ClassName & HTMLDialogAttributes;
9
- declare const Modal: import("svelte").Component<ModalProps, {}, "open" | "ref">;
9
+ } & ClassName & ShapeProp & Omit<HTMLDialogAttributes, "class">;
10
+ declare const Modal: import("svelte").Component<ModalProps, {}, "el" | "open">;
10
11
  type Modal = ReturnType<typeof Modal>;
11
12
  export default Modal;
@@ -1 +1 @@
1
- export { default as Popover, type PopoverProps } from "./popover.svelte";
1
+ export { default as Popover, type PopoverPlacement, type PopoverProps, } from "./popover.svelte";
@@ -1 +1 @@
1
- export { default as Popover } from "./popover.svelte";
1
+ export { default as Popover, } from "./popover.svelte";