@noahbaumgartner/svelte-ui 0.0.5 → 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.
Files changed (214) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +20 -5
  3. package/dist/components/accordion/Accordion.svelte +16 -6
  4. package/dist/components/accordion/Accordion.svelte.d.ts +3 -1
  5. package/dist/components/accordion/AccordionItem.svelte +106 -67
  6. package/dist/components/accordion/AccordionItem.svelte.d.ts +5 -1
  7. package/dist/components/alert-dialog/AlertDialog.svelte +108 -76
  8. package/dist/components/alert-dialog/AlertDialog.svelte.d.ts +5 -2
  9. package/dist/components/avatar/Avatar.svelte +71 -49
  10. package/dist/components/avatar/Avatar.svelte.d.ts +5 -2
  11. package/dist/components/avatar-group/AvatarGroup.svelte +72 -45
  12. package/dist/components/avatar-group/AvatarGroup.svelte.d.ts +5 -1
  13. package/dist/components/badge/Badge.svelte +50 -28
  14. package/dist/components/badge/Badge.svelte.d.ts +6 -4
  15. package/dist/components/breadcrumb/Breadcrumb.svelte +21 -17
  16. package/dist/components/breadcrumb/Breadcrumb.svelte.d.ts +3 -1
  17. package/dist/components/breadcrumb/BreadcrumbEllipsis.svelte +17 -6
  18. package/dist/components/breadcrumb/BreadcrumbEllipsis.svelte.d.ts +5 -2
  19. package/dist/components/breadcrumb/BreadcrumbItem.svelte +75 -36
  20. package/dist/components/breadcrumb/BreadcrumbItem.svelte.d.ts +8 -5
  21. package/dist/components/breadcrumb/BreadcrumbSeparator.svelte +32 -9
  22. package/dist/components/breadcrumb/BreadcrumbSeparator.svelte.d.ts +10 -17
  23. package/dist/components/button/Button.svelte +206 -168
  24. package/dist/components/button/Button.svelte.d.ts +9 -5
  25. package/dist/components/button-group/ButtonGroup.svelte +52 -55
  26. package/dist/components/button-group/ButtonGroup.svelte.d.ts +3 -1
  27. package/dist/components/calendar/Calendar.svelte +232 -144
  28. package/dist/components/calendar/Calendar.svelte.d.ts +19 -2
  29. package/dist/components/calendar/dates.d.ts +4 -1
  30. package/dist/components/calendar/dates.js +8 -2
  31. package/dist/components/card/Card.svelte +48 -32
  32. package/dist/components/card/Card.svelte.d.ts +3 -1
  33. package/dist/components/card/CardAction.svelte +11 -7
  34. package/dist/components/card/CardAction.svelte.d.ts +3 -1
  35. package/dist/components/card/CardContent.svelte +8 -4
  36. package/dist/components/card/CardContent.svelte.d.ts +3 -1
  37. package/dist/components/card/CardDescription.svelte +11 -7
  38. package/dist/components/card/CardDescription.svelte.d.ts +3 -1
  39. package/dist/components/card/CardFooter.svelte +15 -11
  40. package/dist/components/card/CardFooter.svelte.d.ts +3 -1
  41. package/dist/components/card/CardHeader.svelte +15 -11
  42. package/dist/components/card/CardHeader.svelte.d.ts +3 -1
  43. package/dist/components/card/CardTitle.svelte +11 -7
  44. package/dist/components/card/CardTitle.svelte.d.ts +3 -1
  45. package/dist/components/checkbox/Checkbox.svelte +152 -61
  46. package/dist/components/checkbox/Checkbox.svelte.d.ts +5 -2
  47. package/dist/components/color-picker/ColorPicker.svelte +131 -78
  48. package/dist/components/color-picker/ColorPicker.svelte.d.ts +10 -1
  49. package/dist/components/context-menu/ContextMenu.svelte +78 -61
  50. package/dist/components/context-menu/ContextMenu.svelte.d.ts +5 -2
  51. package/dist/components/context-menu/ContextMenuInfo.svelte +91 -0
  52. package/dist/components/context-menu/ContextMenuInfo.svelte.d.ts +19 -0
  53. package/dist/components/context-menu/ContextMenuItem.svelte +102 -57
  54. package/dist/components/context-menu/ContextMenuItem.svelte.d.ts +10 -5
  55. package/dist/components/context-menu/ContextMenuLabel.svelte +28 -0
  56. package/dist/components/context-menu/ContextMenuLabel.svelte.d.ts +10 -0
  57. package/dist/components/context-menu/ContextMenuSeparator.svelte +29 -0
  58. package/dist/components/context-menu/ContextMenuSeparator.svelte.d.ts +8 -0
  59. package/dist/components/dialog/Dialog.svelte +149 -108
  60. package/dist/components/dialog/Dialog.svelte.d.ts +7 -2
  61. package/dist/components/drawer/Drawer.svelte +240 -189
  62. package/dist/components/drawer/Drawer.svelte.d.ts +7 -2
  63. package/dist/components/empty-state/EmptyState.svelte +67 -47
  64. package/dist/components/empty-state/EmptyState.svelte.d.ts +7 -4
  65. package/dist/components/entry/Entry.svelte +53 -42
  66. package/dist/components/entry/Entry.svelte.d.ts +2 -1
  67. package/dist/components/field/Field.svelte +66 -45
  68. package/dist/components/field/Field.svelte.d.ts +2 -1
  69. package/dist/components/field/FieldContent.svelte +11 -8
  70. package/dist/components/field/FieldContent.svelte.d.ts +2 -1
  71. package/dist/components/field/FieldDescription.svelte +18 -15
  72. package/dist/components/field/FieldDescription.svelte.d.ts +2 -1
  73. package/dist/components/field/FieldError.svelte +22 -13
  74. package/dist/components/field/FieldError.svelte.d.ts +2 -1
  75. package/dist/components/field/FieldGroup.svelte +15 -12
  76. package/dist/components/field/FieldGroup.svelte.d.ts +2 -1
  77. package/dist/components/field/FieldLegend.svelte +23 -14
  78. package/dist/components/field/FieldLegend.svelte.d.ts +2 -1
  79. package/dist/components/field/FieldSeparator.svelte +12 -9
  80. package/dist/components/field/FieldSeparator.svelte.d.ts +2 -1
  81. package/dist/components/field/FieldSet.svelte +18 -15
  82. package/dist/components/field/FieldSet.svelte.d.ts +2 -1
  83. package/dist/components/footer/Footer.svelte +63 -47
  84. package/dist/components/footer/Footer.svelte.d.ts +2 -1
  85. package/dist/components/footer/FooterColumn.svelte +17 -14
  86. package/dist/components/footer/FooterColumn.svelte.d.ts +2 -1
  87. package/dist/components/image/Image.svelte +61 -0
  88. package/dist/components/image/Image.svelte.d.ts +18 -0
  89. package/dist/components/input/Input.svelte +307 -208
  90. package/dist/components/input/Input.svelte.d.ts +12 -1
  91. package/dist/components/input-group/InputGroup.svelte +101 -61
  92. package/dist/components/input-group/InputGroup.svelte.d.ts +2 -1
  93. package/dist/components/input-group/InputGroupAddon.svelte +58 -49
  94. package/dist/components/kbd/Kbd.svelte +39 -30
  95. package/dist/components/kbd/Kbd.svelte.d.ts +2 -1
  96. package/dist/components/kbd/KbdGroup.svelte +15 -12
  97. package/dist/components/kbd/KbdGroup.svelte.d.ts +2 -1
  98. package/dist/components/label/Label.svelte +47 -28
  99. package/dist/components/label/Label.svelte.d.ts +2 -1
  100. package/dist/components/link/Link.svelte +71 -17
  101. package/dist/components/link/Link.svelte.d.ts +2 -1
  102. package/dist/components/mesh-gradient/MeshGradient.svelte +33 -22
  103. package/dist/components/mesh-gradient/MeshGradient.svelte.d.ts +2 -3
  104. package/dist/components/navbar/Navbar.svelte +137 -99
  105. package/dist/components/navbar/Navbar.svelte.d.ts +4 -2
  106. package/dist/components/navigation-menu/NavigationMenu.svelte +37 -20
  107. package/dist/components/navigation-menu/NavigationMenu.svelte.d.ts +4 -2
  108. package/dist/components/navigation-menu/NavigationMenuItem.svelte +131 -89
  109. package/dist/components/navigation-menu/NavigationMenuItem.svelte.d.ts +4 -2
  110. package/dist/components/navigation-menu/NavigationMenuLink.svelte +90 -51
  111. package/dist/components/navigation-menu/NavigationMenuLink.svelte.d.ts +7 -5
  112. package/dist/components/number-stepper/NumberStepper.svelte +39 -20
  113. package/dist/components/number-stepper/NumberStepper.svelte.d.ts +6 -1
  114. package/dist/components/popover/Popover.svelte +85 -65
  115. package/dist/components/popover/Popover.svelte.d.ts +5 -2
  116. package/dist/components/preview-card/PreviewCard.svelte +116 -98
  117. package/dist/components/preview-card/PreviewCard.svelte.d.ts +9 -6
  118. package/dist/components/progress/Progress.svelte +61 -38
  119. package/dist/components/progress/Progress.svelte.d.ts +2 -1
  120. package/dist/components/radio-group/RadioGroup.svelte +24 -19
  121. package/dist/components/radio-group/RadioGroup.svelte.d.ts +2 -1
  122. package/dist/components/radio-group/RadioGroupItem.svelte +101 -54
  123. package/dist/components/radio-group/RadioGroupItem.svelte.d.ts +2 -1
  124. package/dist/components/section/Section.svelte +114 -0
  125. package/dist/components/section/Section.svelte.d.ts +22 -0
  126. package/dist/components/select/Select.svelte +216 -133
  127. package/dist/components/select/Select.svelte.d.ts +11 -2
  128. package/dist/components/separator/Separator.svelte +21 -10
  129. package/dist/components/separator/Separator.svelte.d.ts +2 -1
  130. package/dist/components/sidebar/Sidebar.svelte +142 -108
  131. package/dist/components/sidebar/Sidebar.svelte.d.ts +6 -1
  132. package/dist/components/sidebar/SidebarContent.svelte +15 -12
  133. package/dist/components/sidebar/SidebarContent.svelte.d.ts +2 -1
  134. package/dist/components/sidebar/SidebarFooter.svelte +10 -7
  135. package/dist/components/sidebar/SidebarFooter.svelte.d.ts +2 -1
  136. package/dist/components/sidebar/SidebarGroup.svelte +35 -25
  137. package/dist/components/sidebar/SidebarGroup.svelte.d.ts +2 -1
  138. package/dist/components/sidebar/SidebarHeader.svelte +21 -16
  139. package/dist/components/sidebar/SidebarHeader.svelte.d.ts +6 -1
  140. package/dist/components/sidebar/SidebarInset.svelte +11 -8
  141. package/dist/components/sidebar/SidebarInset.svelte.d.ts +2 -1
  142. package/dist/components/sidebar/SidebarMenu.svelte +13 -10
  143. package/dist/components/sidebar/SidebarMenu.svelte.d.ts +2 -1
  144. package/dist/components/sidebar/SidebarMenuItem.svelte +195 -114
  145. package/dist/components/sidebar/SidebarMenuItem.svelte.d.ts +11 -5
  146. package/dist/components/sidebar/SidebarProvider.svelte +18 -9
  147. package/dist/components/sidebar/SidebarProvider.svelte.d.ts +2 -1
  148. package/dist/components/sidebar/SidebarSeparator.svelte +6 -1
  149. package/dist/components/sidebar/SidebarSeparator.svelte.d.ts +6 -17
  150. package/dist/components/sidebar/SidebarTrigger.svelte +17 -5
  151. package/dist/components/sidebar/SidebarTrigger.svelte.d.ts +4 -2
  152. package/dist/components/skeleton/Skeleton.svelte +26 -16
  153. package/dist/components/skeleton/Skeleton.svelte.d.ts +4 -2
  154. package/dist/components/spinner/Spinner.svelte +71 -46
  155. package/dist/components/spinner/Spinner.svelte.d.ts +2 -1
  156. package/dist/components/switch/Switch.svelte +126 -65
  157. package/dist/components/switch/Switch.svelte.d.ts +4 -2
  158. package/dist/components/table/Table.svelte +135 -76
  159. package/dist/components/table/Table.svelte.d.ts +12 -2
  160. package/dist/components/tabs/Tabs.svelte +173 -116
  161. package/dist/components/tabs/Tabs.svelte.d.ts +10 -8
  162. package/dist/components/textarea/Textarea.svelte +76 -42
  163. package/dist/components/textarea/Textarea.svelte.d.ts +4 -1
  164. package/dist/components/time-picker/TimePicker.svelte +214 -85
  165. package/dist/components/time-picker/TimePicker.svelte.d.ts +18 -1
  166. package/dist/components/toaster/Toaster.svelte +132 -70
  167. package/dist/components/toaster/Toaster.svelte.d.ts +18 -2
  168. package/dist/components/toaster/toast.svelte.d.ts +3 -3
  169. package/dist/components/toggle/Toggle.svelte +25 -4
  170. package/dist/components/toggle/Toggle.svelte.d.ts +1 -1
  171. package/dist/components/tooltip/Tooltip.svelte +87 -58
  172. package/dist/components/tooltip/Tooltip.svelte.d.ts +4 -2
  173. package/dist/icon.d.ts +12 -0
  174. package/dist/icon.js +1 -0
  175. package/dist/index.d.ts +6 -0
  176. package/dist/index.js +5 -0
  177. package/dist/internal/icons/ArrowDown.svelte +14 -0
  178. package/dist/internal/icons/ArrowDown.svelte.d.ts +4 -0
  179. package/dist/internal/icons/ArrowUp.svelte +14 -0
  180. package/dist/internal/icons/ArrowUp.svelte.d.ts +4 -0
  181. package/dist/internal/icons/Calendar.svelte +16 -0
  182. package/dist/internal/icons/Calendar.svelte.d.ts +4 -0
  183. package/dist/internal/icons/Check.svelte +8 -0
  184. package/dist/internal/icons/Check.svelte.d.ts +4 -0
  185. package/dist/internal/icons/ChevronDown.svelte +8 -0
  186. package/dist/internal/icons/ChevronDown.svelte.d.ts +4 -0
  187. package/dist/internal/icons/ChevronLeft.svelte +8 -0
  188. package/dist/internal/icons/ChevronLeft.svelte.d.ts +4 -0
  189. package/dist/internal/icons/ChevronRight.svelte +8 -0
  190. package/dist/internal/icons/ChevronRight.svelte.d.ts +4 -0
  191. package/dist/internal/icons/ChevronUp.svelte +8 -0
  192. package/dist/internal/icons/ChevronUp.svelte.d.ts +4 -0
  193. package/dist/internal/icons/ChevronsUpDown.svelte +14 -0
  194. package/dist/internal/icons/ChevronsUpDown.svelte.d.ts +4 -0
  195. package/dist/internal/icons/Clock.svelte +14 -0
  196. package/dist/internal/icons/Clock.svelte.d.ts +4 -0
  197. package/dist/internal/icons/Ellipsis.svelte +15 -0
  198. package/dist/internal/icons/Ellipsis.svelte.d.ts +4 -0
  199. package/dist/internal/icons/Icon.svelte +33 -0
  200. package/dist/internal/icons/Icon.svelte.d.ts +8 -0
  201. package/dist/internal/icons/Minus.svelte +8 -0
  202. package/dist/internal/icons/Minus.svelte.d.ts +4 -0
  203. package/dist/internal/icons/PanelLeft.svelte +14 -0
  204. package/dist/internal/icons/PanelLeft.svelte.d.ts +4 -0
  205. package/dist/internal/icons/Plus.svelte +14 -0
  206. package/dist/internal/icons/Plus.svelte.d.ts +4 -0
  207. package/dist/internal/icons/User.svelte +14 -0
  208. package/dist/internal/icons/User.svelte.d.ts +4 -0
  209. package/dist/internal/icons/X.svelte +14 -0
  210. package/dist/internal/icons/X.svelte.d.ts +4 -0
  211. package/dist/styles/tokens.css +39 -41
  212. package/package.json +5 -10
  213. package/dist/icons.d.ts +0 -2
  214. package/dist/icons.js +0 -4
@@ -1,20 +1,35 @@
1
1
  <script lang="ts">
2
2
  import type { Snippet } from 'svelte';
3
- import type { LucideIcon } from '@lucide/svelte';
4
- import Check from '@lucide/svelte/icons/check';
3
+ import type { HTMLButtonAttributes } from 'svelte/elements';
4
+ import type { IconComponent } from '../../icon.js';
5
+ import Check from '../../internal/icons/Check.svelte';
5
6
  import { getMenuContext } from './context.js';
6
7
 
7
- type Props = {
8
- /** Any Lucide icon (import from `svelte-ui/icons`). */
9
- icon?: LucideIcon;
8
+ type Props = Omit<HTMLButtonAttributes, 'children' | 'onclick' | 'role' | 'type'> & {
9
+ /** The button element. Bindable. */
10
+ ref?: HTMLButtonElement | null;
11
+ /** Any icon component, e.g. from `@lucide/svelte`. */
12
+ icon?: IconComponent;
10
13
  /** Makes the item a radio option; `true` marks it as the current choice. */
11
14
  selected?: boolean;
12
15
  disabled?: boolean;
13
16
  onclick?: () => void;
17
+ /** Shown at the end of the item, e.g. a keyboard shortcut. */
18
+ trailing?: Snippet;
14
19
  children: Snippet;
15
20
  };
16
21
 
17
- let { icon: Icon, selected, disabled = false, onclick, children }: Props = $props();
22
+ let {
23
+ ref = $bindable(null),
24
+ icon: Icon,
25
+ selected,
26
+ disabled = false,
27
+ onclick,
28
+ trailing,
29
+ class: className,
30
+ children,
31
+ ...rest
32
+ }: Props = $props();
18
33
 
19
34
  const menu = getMenuContext();
20
35
 
@@ -25,16 +40,19 @@
25
40
  </script>
26
41
 
27
42
  <button
43
+ {...rest}
44
+ bind:this={ref}
28
45
  type="button"
29
46
  tabindex="-1"
30
47
  role={selected === undefined ? 'menuitem' : 'menuitemradio'}
31
48
  aria-checked={selected}
32
49
  {disabled}
33
- class={['context-menu-item', { 'context-menu-item--selected': selected }]}
50
+ class={['context-menu-item', { 'context-menu-item--selected': selected }, className]}
34
51
  onclick={handleClick}
35
52
  >
36
53
  {#if Icon}<Icon class="context-menu-item-icon" aria-hidden="true" />{/if}
37
54
  <span class="context-menu-item-label">{@render children()}</span>
55
+ {@render trailing?.()}
38
56
  {#if selected !== undefined}
39
57
  <span class="context-menu-item-check">
40
58
  {#if selected}<Check aria-hidden="true" />{/if}
@@ -43,63 +61,90 @@
43
61
  </button>
44
62
 
45
63
  <style>
46
- .context-menu-item {
47
- display: flex;
48
- align-items: center;
49
- gap: 10px;
50
- width: 100%;
51
- padding: 8px 10px;
52
- background-color: transparent;
53
- border: none;
54
- border-radius: 6px;
55
- font: inherit;
56
- font-size: 13px;
57
- color: var(--color-text);
58
- text-align: left;
59
- outline: none;
60
- cursor: pointer;
61
- transition: background-color 150ms ease;
62
- }
64
+ @layer svelte-ui {
65
+ .context-menu-item {
66
+ display: flex;
67
+ align-items: center;
68
+ gap: 10px;
69
+ width: 100%;
70
+ padding: 8px 10px;
71
+ background-color: transparent;
72
+ border: none;
73
+ border-radius: 6px;
74
+ font: inherit;
75
+ font-size: 13px;
76
+ color: var(--color-text);
77
+ text-align: left;
78
+ outline: none;
79
+ cursor: pointer;
80
+ transition: background-color 150ms ease;
81
+ }
63
82
 
64
- .context-menu-item:hover:not(:disabled),
65
- .context-menu-item:focus-visible {
66
- background-color: var(--color-surface);
67
- }
83
+ .context-menu-item:hover:not(:disabled),
84
+ .context-menu-item:focus-visible {
85
+ background-color: var(--color-surface);
86
+ }
68
87
 
69
- .context-menu-item:disabled {
70
- cursor: not-allowed;
71
- opacity: 0.5;
72
- }
88
+ .context-menu-item:disabled {
89
+ cursor: not-allowed;
90
+ opacity: 0.5;
91
+ }
73
92
 
74
- .context-menu-item--selected {
75
- background-color: var(--color-surface);
76
- font-weight: 600;
77
- }
93
+ .context-menu-item--selected {
94
+ background-color: var(--color-surface);
95
+ font-weight: 600;
96
+ }
78
97
 
79
- .context-menu-item--selected:hover,
80
- .context-menu-item--selected:focus-visible {
81
- background-color: var(--color-surface-hover);
82
- }
98
+ .context-menu-item--selected:hover,
99
+ .context-menu-item--selected:focus-visible {
100
+ background-color: var(--color-surface-hover);
101
+ }
83
102
 
84
- .context-menu-item :global(.context-menu-item-icon) {
85
- width: 16px;
86
- height: 16px;
87
- flex-shrink: 0;
88
- }
103
+ .context-menu-item :global(.context-menu-item-icon) {
104
+ width: 16px;
105
+ height: 16px;
106
+ flex-shrink: 0;
107
+ }
89
108
 
90
- .context-menu-item-label {
91
- flex-grow: 1;
92
- }
109
+ .context-menu-item-label {
110
+ flex-grow: 1;
111
+ }
93
112
 
94
- .context-menu-item-check {
95
- display: flex;
96
- width: 14px;
97
- height: 14px;
98
- flex-shrink: 0;
99
- }
113
+ .context-menu-item-check {
114
+ display: flex;
115
+ width: 14px;
116
+ height: 14px;
117
+ flex-shrink: 0;
118
+ }
119
+
120
+ .context-menu-item-check :global(svg) {
121
+ width: 14px;
122
+ height: 14px;
123
+ }
124
+
125
+ @media (pointer: coarse) {
126
+ .context-menu-item {
127
+ min-height: 44px;
128
+ }
129
+ }
130
+
131
+ @media (forced-colors: active) {
132
+ .context-menu-item:hover:not(:disabled),
133
+ .context-menu-item:focus-visible {
134
+ outline: 2px solid Highlight;
135
+ outline-offset: -2px;
136
+ }
137
+
138
+ .context-menu-item:disabled {
139
+ color: GrayText;
140
+ opacity: 1;
141
+ }
142
+ }
100
143
 
101
- .context-menu-item-check :global(svg) {
102
- width: 14px;
103
- height: 14px;
144
+ @media (prefers-reduced-motion: reduce) {
145
+ .context-menu-item {
146
+ transition: none;
147
+ }
148
+ }
104
149
  }
105
150
  </style>
@@ -1,14 +1,19 @@
1
1
  import type { Snippet } from 'svelte';
2
- import type { LucideIcon } from '@lucide/svelte';
3
- type Props = {
4
- /** Any Lucide icon (import from `svelte-ui/icons`). */
5
- icon?: LucideIcon;
2
+ import type { HTMLButtonAttributes } from 'svelte/elements';
3
+ import type { IconComponent } from '../../icon.js';
4
+ type Props = Omit<HTMLButtonAttributes, 'children' | 'onclick' | 'role' | 'type'> & {
5
+ /** The button element. Bindable. */
6
+ ref?: HTMLButtonElement | null;
7
+ /** Any icon component, e.g. from `@lucide/svelte`. */
8
+ icon?: IconComponent;
6
9
  /** Makes the item a radio option; `true` marks it as the current choice. */
7
10
  selected?: boolean;
8
11
  disabled?: boolean;
9
12
  onclick?: () => void;
13
+ /** Shown at the end of the item, e.g. a keyboard shortcut. */
14
+ trailing?: Snippet;
10
15
  children: Snippet;
11
16
  };
12
- declare const ContextMenuItem: import("svelte").Component<Props, {}, "">;
17
+ declare const ContextMenuItem: import("svelte").Component<Props, {}, "ref">;
13
18
  type ContextMenuItem = ReturnType<typeof ContextMenuItem>;
14
19
  export default ContextMenuItem;
@@ -0,0 +1,28 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import type { HTMLAttributes } from 'svelte/elements';
4
+
5
+ type Props = Omit<HTMLAttributes<HTMLDivElement>, 'children' | 'role'> & {
6
+ /** The label element. Bindable. */
7
+ ref?: HTMLDivElement | null;
8
+ children: Snippet;
9
+ };
10
+
11
+ let { ref = $bindable(null), class: className, children, ...rest }: Props = $props();
12
+ </script>
13
+
14
+ <div {...rest} bind:this={ref} role="presentation" class={['context-menu-label', className]}>
15
+ {@render children()}
16
+ </div>
17
+
18
+ <style>
19
+ @layer svelte-ui {
20
+ .context-menu-label {
21
+ padding: 6px 10px 4px;
22
+ font-size: 11px;
23
+ font-weight: 500;
24
+ letter-spacing: 0.02em;
25
+ color: var(--color-text-muted);
26
+ }
27
+ }
28
+ </style>
@@ -0,0 +1,10 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { HTMLAttributes } from 'svelte/elements';
3
+ type Props = Omit<HTMLAttributes<HTMLDivElement>, 'children' | 'role'> & {
4
+ /** The label element. Bindable. */
5
+ ref?: HTMLDivElement | null;
6
+ children: Snippet;
7
+ };
8
+ declare const ContextMenuLabel: import("svelte").Component<Props, {}, "ref">;
9
+ type ContextMenuLabel = ReturnType<typeof ContextMenuLabel>;
10
+ export default ContextMenuLabel;
@@ -0,0 +1,29 @@
1
+ <script lang="ts">
2
+ import type { HTMLAttributes } from 'svelte/elements';
3
+
4
+ type Props = Omit<HTMLAttributes<HTMLDivElement>, 'children' | 'role'> & {
5
+ /** The separator element. Bindable. */
6
+ ref?: HTMLDivElement | null;
7
+ };
8
+
9
+ let { ref = $bindable(null), class: className, ...rest }: Props = $props();
10
+ </script>
11
+
12
+ <div {...rest} bind:this={ref} role="separator" class={['context-menu-separator', className]}></div>
13
+
14
+ <style>
15
+ @layer svelte-ui {
16
+ .context-menu-separator {
17
+ height: 1px;
18
+ margin: 2px 0;
19
+ flex-shrink: 0;
20
+ background-color: var(--color-border);
21
+ }
22
+
23
+ @media (forced-colors: active) {
24
+ .context-menu-separator {
25
+ background-color: CanvasText;
26
+ }
27
+ }
28
+ }
29
+ </style>
@@ -0,0 +1,8 @@
1
+ import type { HTMLAttributes } from 'svelte/elements';
2
+ type Props = Omit<HTMLAttributes<HTMLDivElement>, 'children' | 'role'> & {
3
+ /** The separator element. Bindable. */
4
+ ref?: HTMLDivElement | null;
5
+ };
6
+ declare const ContextMenuSeparator: import("svelte").Component<Props, {}, "ref">;
7
+ type ContextMenuSeparator = ReturnType<typeof ContextMenuSeparator>;
8
+ export default ContextMenuSeparator;
@@ -7,11 +7,17 @@
7
7
 
8
8
  <script lang="ts">
9
9
  import type { Snippet } from 'svelte';
10
- import { X } from '@lucide/svelte';
10
+ import type { HTMLDialogAttributes } from 'svelte/elements';
11
+ import X from '../../internal/icons/X.svelte';
11
12
  import Button from '../button/Button.svelte';
12
13
  import Spinner from '../spinner/Spinner.svelte';
13
14
 
14
- type Props = {
15
+ type Props = Omit<
16
+ HTMLDialogAttributes,
17
+ 'open' | 'title' | 'children' | 'aria-labelledby' | 'aria-describedby'
18
+ > & {
19
+ /** The dialog element. Bindable. */
20
+ ref?: HTMLDialogElement | null;
15
21
  /** Whether the dialog is open. Bindable. Escape, the close button and a click on the backdrop close it. */
16
22
  open?: boolean;
17
23
  title: string;
@@ -24,20 +30,25 @@
24
30
  actions?: Snippet<[close: () => void]>;
25
31
  /** Grays out the content and actions, makes them inert and shows a centered Spinner. The close button keeps working. */
26
32
  loading?: boolean;
33
+ /** Accessible name of the close button. */
34
+ closeLabel?: string;
27
35
  };
28
36
 
29
37
  let {
38
+ ref = $bindable(null),
30
39
  open = $bindable(false),
31
40
  title,
32
41
  description,
33
42
  trigger,
34
43
  children,
35
44
  actions,
36
- loading = false
45
+ loading = false,
46
+ closeLabel = 'Close',
47
+ class: className,
48
+ ...rest
37
49
  }: Props = $props();
38
50
 
39
51
  const id = $props.id();
40
- let dialogEl: HTMLDialogElement;
41
52
 
42
53
  const triggerProps: DialogTriggerProps = {
43
54
  onclick: () => (open = true),
@@ -49,10 +60,11 @@
49
60
  }
50
61
 
51
62
  $effect(() => {
52
- if (open && !dialogEl.open) {
53
- dialogEl.showModal();
54
- } else if (!open && dialogEl.open) {
55
- dialogEl.close();
63
+ if (!ref) return;
64
+ if (open && !ref.open) {
65
+ ref.showModal();
66
+ } else if (!open && ref.open) {
67
+ ref.close();
56
68
  }
57
69
  });
58
70
  </script>
@@ -60,11 +72,12 @@
60
72
  {@render trigger?.(triggerProps)}
61
73
 
62
74
  <dialog
63
- bind:this={dialogEl}
75
+ {...rest}
76
+ bind:this={ref}
64
77
  aria-labelledby="{id}-title"
65
78
  aria-describedby={description ? `${id}-description` : undefined}
66
79
  aria-busy={loading || undefined}
67
- class="dialog"
80
+ class={['dialog', className]}
68
81
  onkeydown={(event) => {
69
82
  if (event.key !== 'Escape') return;
70
83
  event.preventDefault();
@@ -77,8 +90,8 @@
77
90
  onclose={() => (open = false)}
78
91
  onclick={(event) => {
79
92
  // Clicks on the ::backdrop target the <dialog> itself, outside its box
80
- if (event.target !== dialogEl) return;
81
- const rect = dialogEl.getBoundingClientRect();
93
+ if (event.target !== ref || !ref) return;
94
+ const rect = ref.getBoundingClientRect();
82
95
  const inside =
83
96
  event.clientX >= rect.left &&
84
97
  event.clientX <= rect.right &&
@@ -105,121 +118,149 @@
105
118
  <Spinner class="dialog-spinner" size="lg" />
106
119
  {/if}
107
120
  <!-- Last in the DOM so the content, not the close button, gets the initial focus -->
108
- <Button class="dialog-close" variant="ghost" size="sm" icon={X} label="Close" onclick={close} />
121
+ <Button
122
+ class="dialog-close"
123
+ variant="ghost"
124
+ size="sm"
125
+ icon={X}
126
+ label={closeLabel}
127
+ onclick={close}
128
+ />
109
129
  </dialog>
110
130
 
111
131
  <style>
112
- /* Locks page scrolling while open; the gutter keeps the layout from shifting */
113
- :global(html:has(.dialog[open])) {
114
- overflow: hidden;
115
- scrollbar-gutter: stable;
116
- }
132
+ @layer svelte-ui {
133
+ /* Locks page scrolling while open; the gutter keeps the layout from shifting */
134
+ :global(html:has(.dialog[open])) {
135
+ overflow: hidden;
136
+ scrollbar-gutter: stable;
137
+ }
117
138
 
118
- .dialog {
119
- box-sizing: border-box;
120
- width: calc(100% - 32px);
121
- max-width: 480px;
122
- max-height: calc(100% - 32px);
123
- flex-direction: column;
124
- padding: 0;
125
- overflow: hidden;
126
- background-color: var(--color-bg);
127
- color: var(--color-text);
128
- border: 1px solid var(--color-border);
129
- border-radius: 14px;
130
- box-shadow: 0 16px 48px rgba(0, 0, 0, 0.16);
131
- outline: none;
132
- opacity: 0;
133
- scale: 0.95;
134
- transition:
135
- opacity 140ms ease,
136
- scale 140ms ease,
137
- display 140ms allow-discrete,
138
- overlay 140ms allow-discrete;
139
- }
139
+ .dialog {
140
+ box-sizing: border-box;
141
+ width: calc(100% - 32px);
142
+ max-width: 480px;
143
+ max-height: calc(100% - 32px);
144
+ flex-direction: column;
145
+ padding: 0;
146
+ overflow: hidden;
147
+ background-color: var(--color-bg);
148
+ color: var(--color-text);
149
+ border: 1px solid var(--color-border);
150
+ border-radius: 14px;
151
+ box-shadow: 0 16px 48px rgba(0, 0, 0, 0.16);
152
+ outline: none;
153
+ opacity: 0;
154
+ scale: 0.95;
155
+ transition:
156
+ opacity 140ms ease,
157
+ scale 140ms ease,
158
+ display 140ms allow-discrete,
159
+ overlay 140ms allow-discrete;
160
+ }
140
161
 
141
- .dialog[open] {
142
- display: flex;
143
- opacity: 1;
144
- scale: 1;
145
- }
162
+ .dialog[open] {
163
+ display: flex;
164
+ opacity: 1;
165
+ scale: 1;
166
+ }
146
167
 
147
- .dialog::backdrop {
148
- background-color: rgba(0, 0, 0, 0);
149
- transition:
150
- background-color 140ms ease,
151
- display 140ms allow-discrete,
152
- overlay 140ms allow-discrete;
153
- }
168
+ .dialog::backdrop {
169
+ background-color: rgba(0, 0, 0, 0);
170
+ transition:
171
+ background-color 140ms ease,
172
+ display 140ms allow-discrete,
173
+ overlay 140ms allow-discrete;
174
+ }
154
175
 
155
- .dialog[open]::backdrop {
156
- background-color: rgba(0, 0, 0, 0.4);
157
- }
176
+ .dialog[open]::backdrop {
177
+ background-color: rgba(0, 0, 0, 0.4);
178
+ }
158
179
 
159
- @starting-style {
160
- .dialog[open] {
161
- opacity: 0;
162
- scale: 0.95;
180
+ @starting-style {
181
+ .dialog[open] {
182
+ opacity: 0;
183
+ scale: 0.95;
184
+ }
185
+
186
+ .dialog[open]::backdrop {
187
+ background-color: rgba(0, 0, 0, 0);
188
+ }
163
189
  }
164
190
 
165
- .dialog[open]::backdrop {
166
- background-color: rgba(0, 0, 0, 0);
191
+ .dialog-content,
192
+ .dialog-actions {
193
+ transition: opacity 140ms ease;
167
194
  }
168
- }
169
195
 
170
- .dialog-content,
171
- .dialog-actions {
172
- transition: opacity 140ms ease;
173
- }
196
+ .dialog-content {
197
+ min-height: 0;
198
+ padding: 20px;
199
+ overflow-y: auto;
200
+ }
174
201
 
175
- .dialog-content {
176
- min-height: 0;
177
- padding: 20px;
178
- overflow-y: auto;
179
- }
202
+ .dialog-title {
203
+ margin: 0;
204
+ padding-right: 32px;
205
+ font-size: 15px;
206
+ font-weight: 600;
207
+ }
180
208
 
181
- .dialog-title {
182
- margin: 0;
183
- padding-right: 32px;
184
- font-size: 15px;
185
- font-weight: 600;
186
- }
209
+ .dialog-description {
210
+ margin: 6px 0 0;
211
+ font-size: 13px;
212
+ line-height: 1.5;
213
+ color: var(--color-text-muted);
214
+ }
187
215
 
188
- .dialog-description {
189
- margin: 6px 0 0;
190
- font-size: 13px;
191
- line-height: 1.5;
192
- color: var(--color-text-muted);
193
- }
216
+ .dialog-body {
217
+ margin-top: 16px;
218
+ font-size: 13px;
219
+ }
194
220
 
195
- .dialog-body {
196
- margin-top: 16px;
197
- font-size: 13px;
198
- }
221
+ .dialog-actions {
222
+ display: flex;
223
+ flex-wrap: wrap;
224
+ justify-content: flex-end;
225
+ gap: 4px;
226
+ padding: 16px;
227
+ border-top: 1px solid var(--color-border);
228
+ }
199
229
 
200
- .dialog-actions {
201
- display: flex;
202
- flex-wrap: wrap;
203
- justify-content: flex-end;
204
- gap: 4px;
205
- padding: 16px;
206
- border-top: 1px solid var(--color-border);
207
- }
230
+ @media (prefers-reduced-motion: reduce) {
231
+ .dialog,
232
+ .dialog::backdrop,
233
+ .dialog-content,
234
+ .dialog-actions {
235
+ transition: none;
236
+ }
237
+ }
208
238
 
209
- .dialog-dimmed {
210
- opacity: 0.4;
211
- user-select: none;
212
- }
239
+ @media (forced-colors: active) {
240
+ .dialog {
241
+ border-color: CanvasText;
242
+ }
213
243
 
214
- .dialog :global(.dialog-spinner) {
215
- position: absolute;
216
- inset: 0;
217
- margin: auto;
218
- }
244
+ .dialog-actions {
245
+ border-top-color: CanvasText;
246
+ }
247
+ }
219
248
 
220
- .dialog :global(.dialog-close) {
221
- position: absolute;
222
- top: 14px;
223
- right: 14px;
249
+ .dialog-dimmed {
250
+ opacity: 0.4;
251
+ user-select: none;
252
+ }
253
+
254
+ .dialog :global(.dialog-spinner) {
255
+ position: absolute;
256
+ inset: 0;
257
+ margin: auto;
258
+ }
259
+
260
+ .dialog :global(.dialog-close) {
261
+ position: absolute;
262
+ top: 14px;
263
+ right: 14px;
264
+ }
224
265
  }
225
266
  </style>
@@ -3,7 +3,10 @@ export type DialogTriggerProps = {
3
3
  'aria-haspopup': 'dialog';
4
4
  };
5
5
  import type { Snippet } from 'svelte';
6
- type Props = {
6
+ import type { HTMLDialogAttributes } from 'svelte/elements';
7
+ type Props = Omit<HTMLDialogAttributes, 'open' | 'title' | 'children' | 'aria-labelledby' | 'aria-describedby'> & {
8
+ /** The dialog element. Bindable. */
9
+ ref?: HTMLDialogElement | null;
7
10
  /** Whether the dialog is open. Bindable. Escape, the close button and a click on the backdrop close it. */
8
11
  open?: boolean;
9
12
  title: string;
@@ -16,7 +19,9 @@ type Props = {
16
19
  actions?: Snippet<[close: () => void]>;
17
20
  /** Grays out the content and actions, makes them inert and shows a centered Spinner. The close button keeps working. */
18
21
  loading?: boolean;
22
+ /** Accessible name of the close button. */
23
+ closeLabel?: string;
19
24
  };
20
- declare const Dialog: import("svelte").Component<Props, {}, "open">;
25
+ declare const Dialog: import("svelte").Component<Props, {}, "ref" | "open">;
21
26
  type Dialog = ReturnType<typeof Dialog>;
22
27
  export default Dialog;