@nqmcreative/ui 0.4.0 → 0.4.1

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 (132) hide show
  1. package/README.md +16 -18
  2. package/dist/core/calendar.d.ts +1 -1
  3. package/dist/core/calendar.js +1 -1
  4. package/dist/core/date.d.ts +3 -3
  5. package/dist/core/date.js +1 -1
  6. package/dist/core/files.d.ts +2 -3
  7. package/dist/core/files.js +2 -3
  8. package/dist/core/index.d.ts +1 -3
  9. package/dist/core/index.js +1 -3
  10. package/dist/core/password.d.ts +1 -1
  11. package/dist/core/password.js +1 -1
  12. package/dist/styles/matte/Alert.svelte +1 -3
  13. package/dist/styles/matte/Breadcrumb.svelte +1 -4
  14. package/dist/styles/matte/Calendar.svelte +6 -9
  15. package/dist/styles/matte/ColorInput.svelte +1 -4
  16. package/dist/styles/matte/Combobox.svelte +3 -6
  17. package/dist/styles/matte/CommandPalette.svelte +3 -6
  18. package/dist/styles/matte/ConfirmDialog.svelte +2 -5
  19. package/dist/styles/matte/ContextMenu.svelte +1 -4
  20. package/dist/styles/matte/CurrencyInput.svelte +1 -1
  21. package/dist/styles/matte/CurrencyInput.svelte.d.ts +1 -1
  22. package/dist/styles/matte/DatePicker.svelte +9 -9
  23. package/dist/styles/matte/DatePicker.svelte.d.ts +3 -0
  24. package/dist/styles/matte/Drawer.svelte +1 -3
  25. package/dist/styles/matte/Dropdown.svelte +2 -5
  26. package/dist/styles/matte/Dropdown.svelte.d.ts +1 -1
  27. package/dist/styles/matte/Dropzone.svelte +9 -12
  28. package/dist/styles/matte/Dropzone.svelte.d.ts +1 -1
  29. package/dist/styles/matte/FileInput.svelte +11 -14
  30. package/dist/styles/matte/FileInput.svelte.d.ts +2 -2
  31. package/dist/styles/matte/Footer.svelte +1 -1
  32. package/dist/styles/matte/Footer.svelte.d.ts +1 -1
  33. package/dist/styles/matte/Modal.svelte +1 -3
  34. package/dist/styles/matte/MultiSelect.svelte +5 -8
  35. package/dist/styles/matte/Navbar.svelte +3 -9
  36. package/dist/styles/matte/Pagination.svelte +3 -6
  37. package/dist/styles/matte/PinInput.svelte +1 -4
  38. package/dist/styles/matte/SearchInput.svelte +1 -4
  39. package/dist/styles/matte/Sidebar.svelte +3 -6
  40. package/dist/styles/matte/Spinner.svelte +2 -5
  41. package/dist/styles/matte/Spinner.svelte.d.ts +1 -1
  42. package/dist/styles/matte/Table.svelte +5 -8
  43. package/dist/styles/matte/TagsInput.svelte +5 -8
  44. package/dist/styles/matte/ThemeToggle.svelte +5 -8
  45. package/dist/styles/matte/TimeInput.svelte +2 -5
  46. package/dist/styles/matte/Toaster.svelte +2 -5
  47. package/dist/styles/matte/index.d.ts +2 -3
  48. package/dist/styles/matte/index.js +1 -3
  49. package/dist/styles/paper/Alert.svelte +1 -3
  50. package/dist/styles/paper/Breadcrumb.svelte +1 -4
  51. package/dist/styles/paper/Calendar.svelte +6 -9
  52. package/dist/styles/paper/ColorInput.svelte +1 -4
  53. package/dist/styles/paper/Combobox.svelte +3 -6
  54. package/dist/styles/paper/CommandPalette.svelte +3 -6
  55. package/dist/styles/paper/ConfirmDialog.svelte +2 -5
  56. package/dist/styles/paper/ContextMenu.svelte +1 -4
  57. package/dist/styles/paper/CurrencyInput.svelte +1 -1
  58. package/dist/styles/paper/CurrencyInput.svelte.d.ts +1 -1
  59. package/dist/styles/paper/DatePicker.svelte +9 -9
  60. package/dist/styles/paper/DatePicker.svelte.d.ts +3 -0
  61. package/dist/styles/paper/Drawer.svelte +1 -3
  62. package/dist/styles/paper/Dropdown.svelte +2 -5
  63. package/dist/styles/paper/Dropdown.svelte.d.ts +1 -1
  64. package/dist/styles/paper/Dropzone.svelte +9 -12
  65. package/dist/styles/paper/Dropzone.svelte.d.ts +1 -1
  66. package/dist/styles/paper/FileInput.svelte +11 -14
  67. package/dist/styles/paper/FileInput.svelte.d.ts +2 -2
  68. package/dist/styles/paper/Footer.svelte +1 -1
  69. package/dist/styles/paper/Footer.svelte.d.ts +1 -1
  70. package/dist/styles/paper/Modal.svelte +1 -3
  71. package/dist/styles/paper/MultiSelect.svelte +5 -8
  72. package/dist/styles/paper/Navbar.svelte +3 -9
  73. package/dist/styles/paper/Pagination.svelte +3 -6
  74. package/dist/styles/paper/PinInput.svelte +1 -4
  75. package/dist/styles/paper/SearchInput.svelte +1 -4
  76. package/dist/styles/paper/Sidebar.svelte +3 -6
  77. package/dist/styles/paper/Spinner.svelte +2 -5
  78. package/dist/styles/paper/Spinner.svelte.d.ts +1 -1
  79. package/dist/styles/paper/Table.svelte +5 -8
  80. package/dist/styles/paper/TagsInput.svelte +5 -8
  81. package/dist/styles/paper/ThemeToggle.svelte +5 -8
  82. package/dist/styles/paper/TimeInput.svelte +2 -5
  83. package/dist/styles/paper/Toaster.svelte +3 -6
  84. package/dist/styles/paper/index.d.ts +2 -3
  85. package/dist/styles/paper/index.js +1 -3
  86. package/dist/styles/sprout/Alert.svelte +1 -3
  87. package/dist/styles/sprout/Breadcrumb.svelte +1 -4
  88. package/dist/styles/sprout/Calendar.svelte +6 -9
  89. package/dist/styles/sprout/ColorInput.svelte +1 -4
  90. package/dist/styles/sprout/Combobox.svelte +3 -6
  91. package/dist/styles/sprout/CommandPalette.svelte +3 -6
  92. package/dist/styles/sprout/ConfirmDialog.svelte +2 -5
  93. package/dist/styles/sprout/ContextMenu.svelte +1 -4
  94. package/dist/styles/sprout/CurrencyInput.svelte +1 -1
  95. package/dist/styles/sprout/CurrencyInput.svelte.d.ts +1 -1
  96. package/dist/styles/sprout/DatePicker.svelte +9 -9
  97. package/dist/styles/sprout/DatePicker.svelte.d.ts +3 -0
  98. package/dist/styles/sprout/Drawer.svelte +1 -3
  99. package/dist/styles/sprout/Dropdown.svelte +2 -5
  100. package/dist/styles/sprout/Dropdown.svelte.d.ts +1 -1
  101. package/dist/styles/sprout/Dropzone.svelte +9 -12
  102. package/dist/styles/sprout/Dropzone.svelte.d.ts +1 -1
  103. package/dist/styles/sprout/FileInput.svelte +11 -14
  104. package/dist/styles/sprout/FileInput.svelte.d.ts +2 -2
  105. package/dist/styles/sprout/Footer.svelte +1 -1
  106. package/dist/styles/sprout/Footer.svelte.d.ts +1 -1
  107. package/dist/styles/sprout/Modal.svelte +1 -3
  108. package/dist/styles/sprout/MultiSelect.svelte +5 -8
  109. package/dist/styles/sprout/Navbar.svelte +3 -9
  110. package/dist/styles/sprout/Pagination.svelte +3 -6
  111. package/dist/styles/sprout/PinInput.svelte +1 -4
  112. package/dist/styles/sprout/SearchInput.svelte +1 -4
  113. package/dist/styles/sprout/Sidebar.svelte +3 -6
  114. package/dist/styles/sprout/Spinner.svelte +2 -5
  115. package/dist/styles/sprout/Spinner.svelte.d.ts +1 -1
  116. package/dist/styles/sprout/Table.svelte +5 -8
  117. package/dist/styles/sprout/TagsInput.svelte +5 -8
  118. package/dist/styles/sprout/ThemeToggle.svelte +5 -8
  119. package/dist/styles/sprout/TimeInput.svelte +2 -5
  120. package/dist/styles/sprout/Toaster.svelte +3 -6
  121. package/dist/styles/sprout/index.d.ts +2 -3
  122. package/dist/styles/sprout/index.js +1 -3
  123. package/package.json +2 -19
  124. package/registry.json +3 -44
  125. package/dist/core/locale.svelte.d.ts +0 -101
  126. package/dist/core/locale.svelte.js +0 -167
  127. package/dist/styles/matte/LocaleProvider.svelte +0 -24
  128. package/dist/styles/matte/LocaleProvider.svelte.d.ts +0 -10
  129. package/dist/styles/paper/LocaleProvider.svelte +0 -24
  130. package/dist/styles/paper/LocaleProvider.svelte.d.ts +0 -10
  131. package/dist/styles/sprout/LocaleProvider.svelte +0 -24
  132. package/dist/styles/sprout/LocaleProvider.svelte.d.ts +0 -10
@@ -1,6 +1,5 @@
1
1
  <script lang="ts">
2
2
  import type { HTMLInputAttributes } from 'svelte/elements';
3
- import { useLocale } from '../../core/locale.svelte.js';
4
3
  import { clampTime, formatTime, parseTime, stepTime } from '../../core/time.js';
5
4
  import type { Tone } from '../../core/tones.js';
6
5
  import Input, { type InputSize } from './Input.svelte';
@@ -38,8 +37,6 @@
38
37
  ...rest
39
38
  }: Props = $props();
40
39
 
41
- const t = useLocale();
42
-
43
40
  // Typed text and the bound value are separate until the field settles:
44
41
  // `9` is a valid thing to be halfway through typing and an invalid time.
45
42
  let text = $state('');
@@ -106,7 +103,7 @@
106
103
  class={stepper}
107
104
  {disabled}
108
105
  onclick={() => nudge(1)}
109
- aria-label={t.current.increase}
106
+ aria-label="Increase"
110
107
  >
111
108
  <svg class="size-3" viewBox="0 0 12 12" fill="none" aria-hidden="true">
112
109
  <path d="m3 7.5 3-3 3 3" stroke="currentColor" stroke-width="1.5" />
@@ -117,7 +114,7 @@
117
114
  class={stepper}
118
115
  {disabled}
119
116
  onclick={() => nudge(-1)}
120
- aria-label={t.current.decrease}
117
+ aria-label="Decrease"
121
118
  >
122
119
  <svg class="size-3" viewBox="0 0 12 12" fill="none" aria-hidden="true">
123
120
  <path d="m3 4.5 3 3 3-3" stroke="currentColor" stroke-width="1.5" />
@@ -6,7 +6,6 @@
6
6
  import { toneBorderSoft, toneSurface, toneText } from '../../core/tones.js';
7
7
  import { iconMd } from './icon.js';
8
8
  import Spinner from './Spinner.svelte';
9
- import { useLocale } from '../../core/locale.svelte.js';
10
9
 
11
10
  export type ToasterPosition =
12
11
  'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right';
@@ -18,8 +17,6 @@
18
17
 
19
18
  let { position = 'bottom-right', class: className = '' }: Props = $props();
20
19
 
21
- const t = useLocale();
22
-
23
20
  const positions: Record<ToasterPosition, string> = {
24
21
  'top-left': 'top-4 left-4 items-start',
25
22
  'top-center': 'top-4 left-1/2 -translate-x-1/2 items-center',
@@ -41,7 +38,7 @@
41
38
  <div
42
39
  use:portal
43
40
  aria-live="polite"
44
- aria-label={t.current.notifications}
41
+ aria-label="Notifications"
45
42
  class="pointer-events-none fixed z-[100] flex w-[min(24rem,calc(100vw-2rem))] flex-col gap-3 {positions[
46
43
  position
47
44
  ]} {fromTop ? '' : 'flex-col-reverse'} {className}"
@@ -101,7 +98,7 @@
101
98
  <button
102
99
  type="button"
103
100
  onclick={() => toast.dismiss(item.id)}
104
- aria-label={t.current.dismiss}
101
+ aria-label="Dismiss"
105
102
  class="-m-1 shrink-0 p-1 text-text-muted transition-colors duration-150 hover:text-text focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-current"
106
103
  >
107
104
  <svg class="size-3.5" viewBox="0 0 14 14" fill="none" aria-hidden="true">
@@ -5,9 +5,8 @@
5
5
  * `@nqmcreative/ui/core`, re-exported below so this one import covers
6
6
  * the whole style.
7
7
  */
8
- export { TONES, focusRing, peerFocusRing, toneBorder, toneBorderLeft, toneBorderSoft, toneFill, toneFocusBorder, toneFocusWithinBorder, toneHoverBorder, toneHoverText, tonePeerChecked, tonePeerFocus, toneRing, toneSoft, toneSoftHover, toneSolid, toneSolidHover, toneSurface, toneText, anchored, clickOutside, focusTrap, focusable, navigateList, portal, toast, locale, setLocale, provideLocale, useLocale, enUS, idID, LocaleStore, applyTheme, storedTheme, toISO, fromISO, today, isSameDay, addDays, addMonths, monthGrid, weekdayNames, monthLabel, isOutOfRange, formatISO, parseFormatted } from '../../core/index.js';
9
- export type { Tone, Placement, AnchorOptions, ClickOutsideOptions, FocusTrapOptions, Toast, ToastOptions, ToastAction, Locale, Theme, DateFormat, TableSort, SortDirection, RejectedFile } from '../../core/index.js';
10
- export { default as LocaleProvider } from './LocaleProvider.svelte';
8
+ export { TONES, focusRing, peerFocusRing, toneBorder, toneBorderLeft, toneBorderSoft, toneFill, toneFocusBorder, toneFocusWithinBorder, toneHoverBorder, toneHoverText, tonePeerChecked, tonePeerFocus, toneRing, toneSoft, toneSoftHover, toneSolid, toneSolidHover, toneSurface, toneText, anchored, clickOutside, focusTrap, focusable, navigateList, portal, toast, applyTheme, storedTheme, toISO, fromISO, today, isSameDay, addDays, addMonths, monthGrid, weekdayNames, monthLabel, isOutOfRange, formatISO, parseFormatted } from '../../core/index.js';
9
+ export type { Tone, Placement, AnchorOptions, ClickOutsideOptions, FocusTrapOptions, Toast, ToastOptions, ToastAction, Theme, DateFormat, TableSort, SortDirection, RejectedFile } from '../../core/index.js';
11
10
  export { default as Button } from './Button.svelte';
12
11
  export type { ButtonVariant, ButtonSize } from './Button.svelte';
13
12
  export { default as Link } from './Link.svelte';
@@ -6,9 +6,7 @@
6
6
  * the whole style.
7
7
  */
8
8
  /* --- shared behaviour --- */
9
- export { TONES, focusRing, peerFocusRing, toneBorder, toneBorderLeft, toneBorderSoft, toneFill, toneFocusBorder, toneFocusWithinBorder, toneHoverBorder, toneHoverText, tonePeerChecked, tonePeerFocus, toneRing, toneSoft, toneSoftHover, toneSolid, toneSolidHover, toneSurface, toneText, anchored, clickOutside, focusTrap, focusable, navigateList, portal, toast, locale, setLocale, provideLocale, useLocale, enUS, idID, LocaleStore, applyTheme, storedTheme, toISO, fromISO, today, isSameDay, addDays, addMonths, monthGrid, weekdayNames, monthLabel, isOutOfRange, formatISO, parseFormatted } from '../../core/index.js';
10
- /* --- locale --- */
11
- export { default as LocaleProvider } from './LocaleProvider.svelte';
9
+ export { TONES, focusRing, peerFocusRing, toneBorder, toneBorderLeft, toneBorderSoft, toneFill, toneFocusBorder, toneFocusWithinBorder, toneHoverBorder, toneHoverText, tonePeerChecked, tonePeerFocus, toneRing, toneSoft, toneSoftHover, toneSolid, toneSolidHover, toneSurface, toneText, anchored, clickOutside, focusTrap, focusable, navigateList, portal, toast, applyTheme, storedTheme, toISO, fromISO, today, isSameDay, addDays, addMonths, monthGrid, weekdayNames, monthLabel, isOutOfRange, formatISO, parseFormatted } from '../../core/index.js';
12
10
  /* --- actions --- */
13
11
  export { default as Button } from './Button.svelte';
14
12
  export { default as Link } from './Link.svelte';
@@ -8,7 +8,6 @@
8
8
  toneText,
9
9
  type Tone
10
10
  } from '../../core/tones.js';
11
- import { useLocale } from '../../core/locale.svelte.js';
12
11
 
13
12
  export type AlertVariant = 'soft' | 'outline' | 'accent';
14
13
 
@@ -36,7 +35,6 @@
36
35
  ...rest
37
36
  }: Props = $props();
38
37
 
39
- const t = useLocale();
40
38
  let open = $state(true);
41
39
 
42
40
  const variants = $derived({
@@ -82,7 +80,7 @@
82
80
  <button
83
81
  type="button"
84
82
  onclick={dismiss}
85
- aria-label={t.current.dismiss}
83
+ aria-label="Dismiss"
86
84
  class="-m-1 shrink-0 rounded p-1 text-text-muted transition-colors duration-150 hover:bg-bg/60 hover:text-text focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-current"
87
85
  >
88
86
  <svg class="size-3.5" viewBox="0 0 14 14" fill="none" aria-hidden="true">
@@ -2,7 +2,6 @@
2
2
  import type { Snippet } from 'svelte';
3
3
  import type { HTMLAttributes } from 'svelte/elements';
4
4
  import { toneHoverText, type Tone } from '../../core/tones.js';
5
- import { useLocale } from '../../core/locale.svelte.js';
6
5
  import { iconMd } from './icon.js';
7
6
 
8
7
  export interface BreadcrumbItem {
@@ -19,11 +18,9 @@
19
18
  }
20
19
 
21
20
  let { items, separator = '/', tone = 'brand', class: className = '', ...rest }: Props = $props();
22
-
23
- const t = useLocale();
24
21
  </script>
25
22
 
26
- <nav aria-label={t.current.breadcrumb} class="font-sans text-sm {className}" {...rest}>
23
+ <nav aria-label="Breadcrumb" class="font-sans text-sm {className}" {...rest}>
27
24
  <ol class="flex flex-wrap items-center gap-1.5">
28
25
  {#each items as item, i (item.label)}
29
26
  <li class="flex items-center gap-1.5">
@@ -11,7 +11,6 @@
11
11
  weekdayNames
12
12
  } from '../../core/date.js';
13
13
  import { calendarKeyMove } from '../../core/calendar.js';
14
- import { useLocale } from '../../core/locale.svelte.js';
15
14
  import {
16
15
  focusRing,
17
16
  toneFill,
@@ -53,8 +52,6 @@
53
52
  class: className = ''
54
53
  }: Props = $props();
55
54
 
56
- const t = useLocale();
57
-
58
55
  const selected = $derived(fromISO(value));
59
56
  const now = today();
60
57
 
@@ -71,7 +68,7 @@
71
68
  });
72
69
 
73
70
  const days = $derived(monthGrid(view, weekStart));
74
- const weekdays = $derived(weekdayNames(t.current.dateLocale, weekStart));
71
+ const weekdays = $derived(weekdayNames(undefined, weekStart));
75
72
 
76
73
  function blocked(date: Date) {
77
74
  return isOutOfRange(date, min, max) || (isDisabled?.(date) ?? false);
@@ -119,7 +116,7 @@
119
116
  <button
120
117
  type="button"
121
118
  onclick={() => show(addMonths(view, -1))}
122
- aria-label={t.current.previousMonth}
119
+ aria-label="Previous month"
123
120
  class="{arrow} {focusRing} {toneRing[tone]}"
124
121
  >
125
122
  <svg class="size-4" viewBox="0 0 16 16" fill="none" aria-hidden="true">
@@ -128,13 +125,13 @@
128
125
  </button>
129
126
 
130
127
  <span aria-live="polite" class="text-sm font-semibold text-text">
131
- {monthLabel(view, t.current.dateLocale)}
128
+ {monthLabel(view)}
132
129
  </span>
133
130
 
134
131
  <button
135
132
  type="button"
136
133
  onclick={() => show(addMonths(view, 1))}
137
- aria-label={t.current.nextMonth}
134
+ aria-label="Next month"
138
135
  class="{arrow} {focusRing} {toneRing[tone]}"
139
136
  >
140
137
  <svg class="size-4" viewBox="0 0 16 16" fill="none" aria-hidden="true">
@@ -154,7 +151,7 @@
154
151
  bind:this={grid}
155
152
  role="grid"
156
153
  tabindex="-1"
157
- aria-label={t.current.selectDate}
154
+ aria-label="Select a date"
158
155
  onkeydown={onKeydown}
159
156
  class="grid grid-cols-7 gap-0.5 focus:outline-none"
160
157
  >
@@ -196,7 +193,7 @@
196
193
  tone
197
194
  ]} hover:underline hover:underline-offset-2 {focusRing} {toneRing[tone]}"
198
195
  >
199
- {t.current.today}
196
+ Today
200
197
  </button>
201
198
  {/if}
202
199
  </div>
@@ -1,7 +1,6 @@
1
1
  <script lang="ts">
2
2
  import type { HTMLInputAttributes } from 'svelte/elements';
3
3
  import { normaliseHex } from '../../core/color.js';
4
- import { useLocale } from '../../core/locale.svelte.js';
5
4
  import type { Tone } from '../../core/tones.js';
6
5
  import Input, { type InputSize } from './Input.svelte';
7
6
 
@@ -27,8 +26,6 @@
27
26
  ...rest
28
27
  }: Props = $props();
29
28
 
30
- const t = useLocale();
31
-
32
29
  // The text can be mid-typing (`#a2`) while the swatch needs a real colour,
33
30
  // so the swatch follows the last value that parsed rather than the field.
34
31
  let text = $state('');
@@ -84,7 +81,7 @@
84
81
  type="color"
85
82
  value={resolved}
86
83
  {disabled}
87
- aria-label={t.current.pickColour}
84
+ aria-label="Pick a colour"
88
85
  oninput={(event) => choose(event.currentTarget.value)}
89
86
  class="absolute inset-0 cursor-pointer opacity-0 disabled:cursor-not-allowed"
90
87
  />
@@ -4,7 +4,6 @@
4
4
  import { clickOutside, portal } from '../../core/actions/dismissable.js';
5
5
  import { ListCursor, groupItems, matchQuery, revealIndex } from '../../core/list.svelte.js';
6
6
  import { toneFocusWithinBorder, toneSoft, toneText, type Tone } from '../../core/tones.js';
7
- import { useLocale } from '../../core/locale.svelte.js';
8
7
  import { iconMd } from './icon.js';
9
8
 
10
9
  export interface ComboboxOption {
@@ -51,8 +50,6 @@
51
50
  class: className = ''
52
51
  }: Props = $props();
53
52
 
54
- const t = useLocale();
55
-
56
53
  let open = $state(false);
57
54
  let query = $state('');
58
55
  let wrapper: HTMLElement | null = $state(null);
@@ -161,7 +158,7 @@
161
158
  aria-controls="{id ?? 'combobox'}-listbox"
162
159
  aria-activedescendant={open && flat[active] ? `${id ?? 'combobox'}-opt-${active}` : undefined}
163
160
  aria-invalid={invalid ? 'true' : undefined}
164
- placeholder={selected ? selected.label : (placeholder ?? t.current.comboboxPlaceholder)}
161
+ placeholder={selected ? selected.label : (placeholder ?? 'Search…')}
165
162
  value={open ? query : (selected?.label ?? '')}
166
163
  oninput={(event) => {
167
164
  query = event.currentTarget.value;
@@ -179,7 +176,7 @@
179
176
  <button
180
177
  type="button"
181
178
  onclick={clear}
182
- aria-label={t.current.clear}
179
+ aria-label="Clear selection"
183
180
  class="shrink-0 rounded p-1 text-text-muted transition-colors duration-150 hover:bg-bg-inset hover:text-text focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-current"
184
181
  >
185
182
  <svg class="size-3.5" viewBox="0 0 14 14" fill="none" aria-hidden="true">
@@ -217,7 +214,7 @@
217
214
  >
218
215
  {#if flat.length === 0}
219
216
  <p class="px-3 py-6 text-center text-sm text-text-muted">
220
- {emptyText ?? t.current.noMatches}
217
+ {emptyText ?? 'No matches'}
221
218
  </p>
222
219
  {:else}
223
220
  {#each groups as [group, items] (group)}
@@ -23,7 +23,6 @@
23
23
  import { focusTrap } from '../../core/actions/dismissable.js';
24
24
  import { syncDialog } from '../../core/dialog.svelte.js';
25
25
  import { ListCursor, groupItems, matchQuery, revealIndex } from '../../core/list.svelte.js';
26
- import { useLocale } from '../../core/locale.svelte.js';
27
26
  import { toneSoft, type Tone } from '../../core/tones.js';
28
27
  import { isTypingTarget } from '../../core/trigger.js';
29
28
  import { iconMd } from './icon.js';
@@ -61,8 +60,6 @@
61
60
  class: className = ''
62
61
  }: Props = $props();
63
62
 
64
- const t = useLocale();
65
-
66
63
  let dialog: HTMLDialogElement | null = $state(null);
67
64
  let inputEl: HTMLInputElement | null = $state(null);
68
65
  let listEl: HTMLElement | null = $state(null);
@@ -182,8 +179,8 @@
182
179
  oninput={() => cursor.reset()}
183
180
  type="text"
184
181
  autocomplete="off"
185
- aria-label={placeholder ?? t.current.commandPlaceholder}
186
- placeholder={placeholder ?? t.current.commandPlaceholder}
182
+ aria-label={placeholder ?? 'Type a command or search…'}
183
+ placeholder={placeholder ?? 'Type a command or search…'}
187
184
  class="h-12 w-full min-w-0 bg-transparent font-sans text-sm text-text placeholder:text-text-muted focus:outline-none"
188
185
  />
189
186
  <Kbd>esc</Kbd>
@@ -197,7 +194,7 @@
197
194
  >
198
195
  {#if flat.length === 0}
199
196
  <p class="px-4 py-10 text-center font-sans text-sm text-text-muted">
200
- {emptyText ?? t.current.noResults}
197
+ {emptyText ?? 'No results'}
201
198
  </p>
202
199
  {:else}
203
200
  {#each groups as [group, groupItems] (group)}
@@ -2,7 +2,6 @@
2
2
  import type { Snippet } from 'svelte';
3
3
  import Button from './Button.svelte';
4
4
  import Modal from './Modal.svelte';
5
- import { useLocale } from '../../core/locale.svelte.js';
6
5
  import { toneSoft, type Tone } from '../../core/tones.js';
7
6
 
8
7
  interface Props {
@@ -37,8 +36,6 @@
37
36
  children
38
37
  }: Props = $props();
39
38
 
40
- const t = useLocale();
41
-
42
39
  async function confirm() {
43
40
  try {
44
41
  loading = true;
@@ -88,10 +85,10 @@
88
85
 
89
86
  {#snippet footer()}
90
87
  <Button variant="outline" tone="neutral" onclick={cancel} disabled={loading}>
91
- {cancelLabel ?? t.current.cancel}
88
+ {cancelLabel ?? 'Cancel'}
92
89
  </Button>
93
90
  <Button {tone} onclick={confirm} {loading}>
94
- {confirmLabel ?? t.current.confirm}
91
+ {confirmLabel ?? 'Confirm'}
95
92
  </Button>
96
93
  {/snippet}
97
94
  </Modal>
@@ -3,7 +3,6 @@
3
3
  import { anchored } from '../../core/actions/anchor.js';
4
4
  import { clickOutside, focusTrap, navigateList, portal } from '../../core/actions/dismissable.js';
5
5
  import { menuItems } from '../../core/trigger.js';
6
- import { useLocale } from '../../core/locale.svelte.js';
7
6
 
8
7
  interface Props {
9
8
  /** The region that responds to a right-click. */
@@ -27,8 +26,6 @@
27
26
  menuClass = ''
28
27
  }: Props = $props();
29
28
 
30
- const t = useLocale();
31
-
32
29
  let menuEl: HTMLElement | null = $state(null);
33
30
  /**
34
31
  * A 0×0 element parked at the pointer, so the same `anchored` action that
@@ -79,7 +76,7 @@
79
76
  if ((event.target as HTMLElement).closest('[role="menuitem"]:not([disabled])')) open = false;
80
77
  }}
81
78
  role="menu"
82
- aria-label={label ?? t.current.menu}
79
+ aria-label={label ?? 'Menu'}
83
80
  tabindex="-1"
84
81
  class="z-50 flex max-h-[min(24rem,80vh)] min-w-48 flex-col overflow-y-auto rounded-lg border border-hairline bg-bg py-1 font-sans shadow-lg {menuClass}"
85
82
  >
@@ -20,7 +20,7 @@
20
20
  precision?: number;
21
21
  /** Leading mark — `Rp`, `$`, `€`. */
22
22
  currency?: string;
23
- /** Trailing mark instead, for the locales that write it that way. */
23
+ /** Trailing mark instead, for the regions that write it that way. */
24
24
  unit?: string;
25
25
  size?: InputSize;
26
26
  tone?: Tone;
@@ -14,7 +14,7 @@ interface Props extends Omit<HTMLInputAttributes, 'size' | 'prefix' | 'suffix' |
14
14
  precision?: number;
15
15
  /** Leading mark — `Rp`, `$`, `€`. */
16
16
  currency?: string;
17
- /** Trailing mark instead, for the locales that write it that way. */
17
+ /** Trailing mark instead, for the regions that write it that way. */
18
18
  unit?: string;
19
19
  size?: InputSize;
20
20
  tone?: Tone;
@@ -2,8 +2,7 @@
2
2
  import { anchored } from '../../core/actions/anchor.js';
3
3
  import { clickOutside, portal } from '../../core/actions/dismissable.js';
4
4
  import { dateHint } from '../../core/calendar.js';
5
- import { formatISO, parseFormatted } from '../../core/date.js';
6
- import { useLocale } from '../../core/locale.svelte.js';
5
+ import { formatISO, parseFormatted, type DateFormat } from '../../core/date.js';
7
6
  import { toneFocusWithinBorder, type Tone } from '../../core/tones.js';
8
7
  import Calendar from './Calendar.svelte';
9
8
 
@@ -15,6 +14,8 @@
15
14
  max?: string;
16
15
  isDisabled?: (date: Date) => boolean;
17
16
  weekStart?: 0 | 1;
17
+ /** Order of the date parts the field accepts and prints. */
18
+ format?: DateFormat;
18
19
  tone?: Tone;
19
20
  invalid?: boolean;
20
21
  disabled?: boolean;
@@ -33,6 +34,7 @@
33
34
  max,
34
35
  isDisabled,
35
36
  weekStart = 1,
37
+ format = 'dmy',
36
38
  tone = 'brand',
37
39
  invalid = false,
38
40
  disabled = false,
@@ -44,8 +46,6 @@
44
46
  class: className = ''
45
47
  }: Props = $props();
46
48
 
47
- const t = useLocale();
48
-
49
49
  let open = $state(false);
50
50
  let text = $state('');
51
51
  let editing = $state(false);
@@ -53,8 +53,8 @@
53
53
  let inputEl: HTMLInputElement | null = $state(null);
54
54
 
55
55
  /** While typing the field shows the raw text; otherwise the formatted value. */
56
- const display = $derived(editing ? text : formatISO(value, t.current.dateFormat));
57
- const hint = $derived(placeholder ?? dateHint(t.current.dateFormat));
56
+ const display = $derived(editing ? text : formatISO(value, format));
57
+ const hint = $derived(placeholder ?? dateHint(format));
58
58
 
59
59
  function commit() {
60
60
  editing = false;
@@ -65,7 +65,7 @@
65
65
  }
66
66
  return;
67
67
  }
68
- const parsed = parseFormatted(text, t.current.dateFormat);
68
+ const parsed = parseFormatted(text, format);
69
69
  if (parsed && parsed !== value) {
70
70
  value = parsed;
71
71
  onchange?.(parsed);
@@ -132,7 +132,7 @@
132
132
  onchange?.('');
133
133
  inputEl?.focus();
134
134
  }}
135
- aria-label={t.current.clear}
135
+ aria-label="Clear selection"
136
136
  class="shrink-0 rounded p-1 text-text-muted transition-colors duration-150 hover:bg-bg-inset hover:text-text focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-current"
137
137
  >
138
138
  <svg class="size-3.5" viewBox="0 0 14 14" fill="none" aria-hidden="true">
@@ -150,7 +150,7 @@
150
150
  type="button"
151
151
  {disabled}
152
152
  onclick={() => (open = !open)}
153
- aria-label={t.current.selectDate}
153
+ aria-label="Select a date"
154
154
  aria-expanded={open}
155
155
  class="shrink-0 rounded p-1.5 text-text-muted transition-colors duration-150 hover:bg-bg-inset hover:text-text focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-current"
156
156
  >
@@ -1,3 +1,4 @@
1
+ import { type DateFormat } from '../../core/date.js';
1
2
  import { type Tone } from '../../core/tones.js';
2
3
  interface Props {
3
4
  /** Bindable `YYYY-MM-DD` — the same shape as `<input type="date">`. */
@@ -7,6 +8,8 @@ interface Props {
7
8
  max?: string;
8
9
  isDisabled?: (date: Date) => boolean;
9
10
  weekStart?: 0 | 1;
11
+ /** Order of the date parts the field accepts and prints. */
12
+ format?: DateFormat;
10
13
  tone?: Tone;
11
14
  invalid?: boolean;
12
15
  disabled?: boolean;
@@ -2,7 +2,6 @@
2
2
  import type { Snippet } from 'svelte';
3
3
  import { iconLg } from './icon.js';
4
4
  import type { HTMLDialogAttributes } from 'svelte/elements';
5
- import { useLocale } from '../../core/locale.svelte.js';
6
5
  import { isBackdropClick, syncDialog } from '../../core/dialog.svelte.js';
7
6
 
8
7
  export type DrawerSide = 'left' | 'right' | 'top' | 'bottom';
@@ -39,7 +38,6 @@
39
38
  ...rest
40
39
  }: Props = $props();
41
40
 
42
- const t = useLocale();
43
41
  let dialog: HTMLDialogElement | null = $state(null);
44
42
 
45
43
  const horizontal = $derived(side === 'left' || side === 'right');
@@ -109,7 +107,7 @@
109
107
  <button
110
108
  type="button"
111
109
  onclick={close}
112
- aria-label={t.current.close}
110
+ aria-label="Close"
113
111
  class="-m-1 shrink-0 rounded-md p-1 text-text-muted transition-colors duration-150 hover:bg-bg-inset hover:text-text focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-brand"
114
112
  >
115
113
  <svg class="size-4" viewBox="0 0 14 14" fill="none" aria-hidden="true">
@@ -3,7 +3,6 @@
3
3
  import { anchored, type Placement } from '../../core/actions/anchor.js';
4
4
  import { clickOutside, focusTrap, navigateList, portal } from '../../core/actions/dismissable.js';
5
5
  import { describeTrigger, menuItems, popupTrigger } from '../../core/trigger.js';
6
- import { useLocale } from '../../core/locale.svelte.js';
7
6
 
8
7
  interface Props {
9
8
  /** The control that opens the menu — put a `Button` in here. */
@@ -17,7 +16,7 @@
17
16
  matchWidth?: boolean;
18
17
  /** Close as soon as an item inside is activated. Default true. */
19
18
  closeOnSelect?: boolean;
20
- /** Accessible name for the menu. Defaults to the locale's `menu`. */
19
+ /** Accessible name for the menu. Defaults to `Menu`. */
21
20
  label?: string;
22
21
  class?: string;
23
22
  /** Classes for the wrapper around the trigger. */
@@ -37,8 +36,6 @@
37
36
  triggerClass = ''
38
37
  }: Props = $props();
39
38
 
40
- const t = useLocale();
41
-
42
39
  let anchorEl: HTMLElement | null = $state(null);
43
40
  let menuEl: HTMLElement | null = $state(null);
44
41
 
@@ -85,7 +82,7 @@
85
82
  onkeydown={onMenuKeydown}
86
83
  onclick={onMenuClick}
87
84
  role="menu"
88
- aria-label={label ?? t.current.menu}
85
+ aria-label={label ?? 'Menu'}
89
86
  tabindex="-1"
90
87
  class="z-50 flex max-h-[min(24rem,80vh)] min-w-48 flex-col overflow-y-auto rounded-lg border border-hairline bg-bg py-1 font-sans shadow-lg {className}"
91
88
  >
@@ -12,7 +12,7 @@ interface Props {
12
12
  matchWidth?: boolean;
13
13
  /** Close as soon as an item inside is activated. Default true. */
14
14
  closeOnSelect?: boolean;
15
- /** Accessible name for the menu. Defaults to the locale's `menu`. */
15
+ /** Accessible name for the menu. Defaults to `Menu`. */
16
16
  label?: string;
17
17
  class?: string;
18
18
  /** Classes for the wrapper around the trigger. */
@@ -1,7 +1,6 @@
1
1
  <script lang="ts">
2
2
  import type { Snippet } from 'svelte';
3
3
  import { toneBorder, toneSoft, toneSurface, toneText, type Tone } from '../../core/tones.js';
4
- import { useLocale } from '../../core/locale.svelte.js';
5
4
  import { formatSize as toReadableSize, sortFiles, type RejectedFile } from '../../core/files.js';
6
5
 
7
6
  interface Props {
@@ -16,7 +15,7 @@
16
15
  maxFiles?: number;
17
16
  disabled?: boolean;
18
17
  tone?: Tone;
19
- /** Headline inside the zone. Defaults to the locale's `dropFiles`. */
18
+ /** Headline inside the zone. Defaults to `Drop files here`. */
20
19
  label?: string;
21
20
  hint?: string;
22
21
  /** Hide the built-in file list to render your own. */
@@ -45,15 +44,13 @@
45
44
  class: className = ''
46
45
  }: Props = $props();
47
46
 
48
- const t = useLocale();
49
-
50
47
  let input: HTMLInputElement | null = $state(null);
51
48
  let dragging = $state(false);
52
49
  let rejected = $state<RejectedFile[]>([]);
53
50
  /** Nested elements fire dragleave too, so count depth instead of toggling. */
54
51
  let depth = 0;
55
52
 
56
- const formatSize = (bytes: number) => toReadableSize(bytes, t.current.byteUnits);
53
+ const formatSize = (bytes: number) => toReadableSize(bytes);
57
54
 
58
55
  const defaultHint = $derived.by(() => {
59
56
  if (hint) return hint;
@@ -96,9 +93,9 @@
96
93
  }
97
94
 
98
95
  const reasons = $derived({
99
- type: t.current.rejectedType,
100
- size: t.current.rejectedSize,
101
- count: t.current.rejectedCount
96
+ type: 'wrong file type',
97
+ size: 'too large',
98
+ count: 'over the file limit'
102
99
  });
103
100
  </script>
104
101
 
@@ -146,10 +143,10 @@
146
143
  {/if}
147
144
  </span>
148
145
  <span class="flex flex-col gap-1">
149
- <span class="text-sm font-semibold text-text">{label ?? t.current.dropFiles}</span>
146
+ <span class="text-sm font-semibold text-text">{label ?? 'Drop files here'}</span>
150
147
  <span class="text-[13px] text-text-secondary">
151
- {t.current.or}
152
- <span class="underline underline-offset-2">{t.current.browse}</span>
148
+ or
149
+ <span class="underline underline-offset-2">browse</span>
153
150
  </span>
154
151
  {#if defaultHint}
155
152
  <span class="text-xs text-text-muted">{defaultHint}</span>
@@ -191,7 +188,7 @@
191
188
  <button
192
189
  type="button"
193
190
  onclick={() => remove(i)}
194
- aria-label="{t.current.remove} {file.name}"
191
+ aria-label="Remove {file.name}"
195
192
  class="-m-1 shrink-0 rounded p-1 text-text-muted transition-colors duration-150 hover:bg-bg-inset hover:text-danger focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-danger"
196
193
  >
197
194
  <svg class="size-3.5" viewBox="0 0 14 14" fill="none" aria-hidden="true">
@@ -13,7 +13,7 @@ interface Props {
13
13
  maxFiles?: number;
14
14
  disabled?: boolean;
15
15
  tone?: Tone;
16
- /** Headline inside the zone. Defaults to the locale's `dropFiles`. */
16
+ /** Headline inside the zone. Defaults to `Drop files here`. */
17
17
  label?: string;
18
18
  hint?: string;
19
19
  /** Hide the built-in file list to render your own. */