@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
@@ -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
  import { edge } from './lift.js';
13
12
 
14
13
  export type AlertVariant = 'soft' | 'outline' | 'accent';
@@ -37,7 +36,6 @@
37
36
  ...rest
38
37
  }: Props = $props();
39
38
 
40
- const t = useLocale();
41
39
  let open = $state(true);
42
40
 
43
41
  const variants = $derived({
@@ -83,7 +81,7 @@
83
81
  <button
84
82
  type="button"
85
83
  onclick={dismiss}
86
- aria-label={t.current.dismiss}
84
+ aria-label="Dismiss"
87
85
  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"
88
86
  >
89
87
  <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
  import { edge, soft } from './lift.js';
10
9
 
@@ -52,8 +51,6 @@
52
51
  class: className = ''
53
52
  }: Props = $props();
54
53
 
55
- const t = useLocale();
56
-
57
54
  let open = $state(false);
58
55
  let query = $state('');
59
56
  let wrapper: HTMLElement | null = $state(null);
@@ -162,7 +159,7 @@
162
159
  aria-controls="{id ?? 'combobox'}-listbox"
163
160
  aria-activedescendant={open && flat[active] ? `${id ?? 'combobox'}-opt-${active}` : undefined}
164
161
  aria-invalid={invalid ? 'true' : undefined}
165
- placeholder={selected ? selected.label : (placeholder ?? t.current.comboboxPlaceholder)}
162
+ placeholder={selected ? selected.label : (placeholder ?? 'Search…')}
166
163
  value={open ? query : (selected?.label ?? '')}
167
164
  oninput={(event) => {
168
165
  query = event.currentTarget.value;
@@ -180,7 +177,7 @@
180
177
  <button
181
178
  type="button"
182
179
  onclick={clear}
183
- aria-label={t.current.clear}
180
+ aria-label="Clear selection"
184
181
  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"
185
182
  >
186
183
  <svg class="size-3.5" viewBox="0 0 14 14" fill="none" aria-hidden="true">
@@ -218,7 +215,7 @@
218
215
  >
219
216
  {#if flat.length === 0}
220
217
  <p class="px-3 py-6 text-center text-sm text-text-muted">
221
- {emptyText ?? t.current.noMatches}
218
+ {emptyText ?? 'No matches'}
222
219
  </p>
223
220
  {:else}
224
221
  {#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';
@@ -62,8 +61,6 @@
62
61
  class: className = ''
63
62
  }: Props = $props();
64
63
 
65
- const t = useLocale();
66
-
67
64
  let dialog: HTMLDialogElement | null = $state(null);
68
65
  let inputEl: HTMLInputElement | null = $state(null);
69
66
  let listEl: HTMLElement | null = $state(null);
@@ -183,8 +180,8 @@
183
180
  oninput={() => cursor.reset()}
184
181
  type="text"
185
182
  autocomplete="off"
186
- aria-label={placeholder ?? t.current.commandPlaceholder}
187
- placeholder={placeholder ?? t.current.commandPlaceholder}
183
+ aria-label={placeholder ?? 'Type a command or search…'}
184
+ placeholder={placeholder ?? 'Type a command or search…'}
188
185
  class="h-12 w-full min-w-0 bg-transparent font-sans text-sm text-text placeholder:text-text-muted focus:outline-none"
189
186
  />
190
187
  <Kbd>esc</Kbd>
@@ -198,7 +195,7 @@
198
195
  >
199
196
  {#if flat.length === 0}
200
197
  <p class="px-4 py-10 text-center font-sans text-sm text-text-muted">
201
- {emptyText ?? t.current.noResults}
198
+ {emptyText ?? 'No results'}
202
199
  </p>
203
200
  {:else}
204
201
  {#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
  import { soft } from './lift.js';
8
7
 
9
8
  interface Props {
@@ -28,8 +27,6 @@
28
27
  menuClass = ''
29
28
  }: Props = $props();
30
29
 
31
- const t = useLocale();
32
-
33
30
  let menuEl: HTMLElement | null = $state(null);
34
31
  /**
35
32
  * A 0×0 element parked at the pointer, so the same `anchored` action that
@@ -80,7 +77,7 @@
80
77
  if ((event.target as HTMLElement).closest('[role="menuitem"]:not([disabled])')) open = false;
81
78
  }}
82
79
  role="menu"
83
- aria-label={label ?? t.current.menu}
80
+ aria-label={label ?? 'Menu'}
84
81
  tabindex="-1"
85
82
  class="z-50 flex max-h-[min(24rem,80vh)] min-w-48 flex-col overflow-y-auto rounded-2xl border border-hairline bg-bg py-1 font-sans {soft} {menuClass}"
86
83
  >
@@ -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
  import { edge, soft } from './lift.js';
@@ -16,6 +15,8 @@
16
15
  max?: string;
17
16
  isDisabled?: (date: Date) => boolean;
18
17
  weekStart?: 0 | 1;
18
+ /** Order of the date parts the field accepts and prints. */
19
+ format?: DateFormat;
19
20
  tone?: Tone;
20
21
  invalid?: boolean;
21
22
  disabled?: boolean;
@@ -34,6 +35,7 @@
34
35
  max,
35
36
  isDisabled,
36
37
  weekStart = 1,
38
+ format = 'dmy',
37
39
  tone = 'brand',
38
40
  invalid = false,
39
41
  disabled = false,
@@ -45,8 +47,6 @@
45
47
  class: className = ''
46
48
  }: Props = $props();
47
49
 
48
- const t = useLocale();
49
-
50
50
  let open = $state(false);
51
51
  let text = $state('');
52
52
  let editing = $state(false);
@@ -54,8 +54,8 @@
54
54
  let inputEl: HTMLInputElement | null = $state(null);
55
55
 
56
56
  /** While typing the field shows the raw text; otherwise the formatted value. */
57
- const display = $derived(editing ? text : formatISO(value, t.current.dateFormat));
58
- const hint = $derived(placeholder ?? dateHint(t.current.dateFormat));
57
+ const display = $derived(editing ? text : formatISO(value, format));
58
+ const hint = $derived(placeholder ?? dateHint(format));
59
59
 
60
60
  function commit() {
61
61
  editing = false;
@@ -66,7 +66,7 @@
66
66
  }
67
67
  return;
68
68
  }
69
- const parsed = parseFormatted(text, t.current.dateFormat);
69
+ const parsed = parseFormatted(text, format);
70
70
  if (parsed && parsed !== value) {
71
71
  value = parsed;
72
72
  onchange?.(parsed);
@@ -133,7 +133,7 @@
133
133
  onchange?.('');
134
134
  inputEl?.focus();
135
135
  }}
136
- aria-label={t.current.clear}
136
+ aria-label="Clear selection"
137
137
  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"
138
138
  >
139
139
  <svg class="size-3.5" viewBox="0 0 14 14" fill="none" aria-hidden="true">
@@ -151,7 +151,7 @@
151
151
  type="button"
152
152
  {disabled}
153
153
  onclick={() => (open = !open)}
154
- aria-label={t.current.selectDate}
154
+ aria-label="Select a date"
155
155
  aria-expanded={open}
156
156
  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"
157
157
  >
@@ -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
  import { float } from './lift.js';
8
7
 
@@ -40,7 +39,6 @@
40
39
  ...rest
41
40
  }: Props = $props();
42
41
 
43
- const t = useLocale();
44
42
  let dialog: HTMLDialogElement | null = $state(null);
45
43
 
46
44
  const horizontal = $derived(side === 'left' || side === 'right');
@@ -110,7 +108,7 @@
110
108
  <button
111
109
  type="button"
112
110
  onclick={close}
113
- aria-label={t.current.close}
111
+ aria-label="Close"
114
112
  class="-m-1 shrink-0 rounded-xl 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"
115
113
  >
116
114
  <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
  import { soft } from './lift.js';
8
7
 
9
8
  interface Props {
@@ -18,7 +17,7 @@
18
17
  matchWidth?: boolean;
19
18
  /** Close as soon as an item inside is activated. Default true. */
20
19
  closeOnSelect?: boolean;
21
- /** Accessible name for the menu. Defaults to the locale's `menu`. */
20
+ /** Accessible name for the menu. Defaults to `Menu`. */
22
21
  label?: string;
23
22
  class?: string;
24
23
  /** Classes for the wrapper around the trigger. */
@@ -38,8 +37,6 @@
38
37
  triggerClass = ''
39
38
  }: Props = $props();
40
39
 
41
- const t = useLocale();
42
-
43
40
  let anchorEl: HTMLElement | null = $state(null);
44
41
  let menuEl: HTMLElement | null = $state(null);
45
42
 
@@ -86,7 +83,7 @@
86
83
  onkeydown={onMenuKeydown}
87
84
  onclick={onMenuClick}
88
85
  role="menu"
89
- aria-label={label ?? t.current.menu}
86
+ aria-label={label ?? 'Menu'}
90
87
  tabindex="-1"
91
88
  class="z-50 flex max-h-[min(24rem,80vh)] min-w-48 flex-col overflow-y-auto rounded-2xl border border-hairline bg-bg py-1 font-sans {soft} {className}"
92
89
  >
@@ -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
  import { edge } from './lift.js';
7
6
 
@@ -17,7 +16,7 @@
17
16
  maxFiles?: number;
18
17
  disabled?: boolean;
19
18
  tone?: Tone;
20
- /** Headline inside the zone. Defaults to the locale's `dropFiles`. */
19
+ /** Headline inside the zone. Defaults to `Drop files here`. */
21
20
  label?: string;
22
21
  hint?: string;
23
22
  /** Hide the built-in file list to render your own. */
@@ -46,15 +45,13 @@
46
45
  class: className = ''
47
46
  }: Props = $props();
48
47
 
49
- const t = useLocale();
50
-
51
48
  let input: HTMLInputElement | null = $state(null);
52
49
  let dragging = $state(false);
53
50
  let rejected = $state<RejectedFile[]>([]);
54
51
  /** Nested elements fire dragleave too, so count depth instead of toggling. */
55
52
  let depth = 0;
56
53
 
57
- const formatSize = (bytes: number) => toReadableSize(bytes, t.current.byteUnits);
54
+ const formatSize = (bytes: number) => toReadableSize(bytes);
58
55
 
59
56
  const defaultHint = $derived.by(() => {
60
57
  if (hint) return hint;
@@ -97,9 +94,9 @@
97
94
  }
98
95
 
99
96
  const reasons = $derived({
100
- type: t.current.rejectedType,
101
- size: t.current.rejectedSize,
102
- count: t.current.rejectedCount
97
+ type: 'wrong file type',
98
+ size: 'too large',
99
+ count: 'over the file limit'
103
100
  });
104
101
  </script>
105
102
 
@@ -147,10 +144,10 @@
147
144
  {/if}
148
145
  </span>
149
146
  <span class="flex flex-col gap-1">
150
- <span class="text-sm font-semibold text-text">{label ?? t.current.dropFiles}</span>
147
+ <span class="text-sm font-semibold text-text">{label ?? 'Drop files here'}</span>
151
148
  <span class="text-[13px] text-text-secondary">
152
- {t.current.or}
153
- <span class="underline underline-offset-2">{t.current.browse}</span>
149
+ or
150
+ <span class="underline underline-offset-2">browse</span>
154
151
  </span>
155
152
  {#if defaultHint}
156
153
  <span class="text-xs text-text-muted">{defaultHint}</span>
@@ -192,7 +189,7 @@
192
189
  <button
193
190
  type="button"
194
191
  onclick={() => remove(i)}
195
- aria-label="{t.current.remove} {file.name}"
192
+ aria-label="Remove {file.name}"
196
193
  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"
197
194
  >
198
195
  <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. */
@@ -1,6 +1,5 @@
1
1
  <script lang="ts">
2
2
  import { formatSize as toReadableSize, sortFiles, type RejectedFile } from '../../core/files.js';
3
- import { useLocale } from '../../core/locale.svelte.js';
4
3
  import { toneFocusWithinBorder, type Tone } from '../../core/tones.js';
5
4
  import { edge } from './lift.js';
6
5
  import type { InputSize } from './Input.svelte';
@@ -19,9 +18,9 @@
19
18
  size?: InputSize;
20
19
  tone?: Tone;
21
20
  invalid?: boolean;
22
- /** Text on the button. Defaults to the locale's `chooseFile`. */
21
+ /** Text on the button. Defaults to `Choose file`. */
23
22
  label?: string;
24
- /** Shown while nothing is chosen. Defaults to the locale's `noFile`. */
23
+ /** Shown while nothing is chosen. Defaults to `No file chosen`. */
25
24
  placeholder?: string;
26
25
  /** Show the list of held files below the row. */
27
26
  showList?: boolean;
@@ -51,18 +50,16 @@
51
50
  class: className = ''
52
51
  }: Props = $props();
53
52
 
54
- const t = useLocale();
55
-
56
53
  let input: HTMLInputElement | null = $state(null);
57
54
  let rejected = $state<RejectedFile[]>([]);
58
55
 
59
- const formatSize = (bytes: number) => toReadableSize(bytes, t.current.byteUnits);
56
+ const formatSize = (bytes: number) => toReadableSize(bytes);
60
57
 
61
58
  /** One file reads as its name and weight; several read as a count. */
62
59
  const summary = $derived.by(() => {
63
- if (files.length === 0) return placeholder ?? t.current.noFile;
60
+ if (files.length === 0) return placeholder ?? 'No file chosen';
64
61
  if (files.length === 1) return `${files[0].name} · ${formatSize(files[0].size)}`;
65
- return `${files.length} ${t.current.selected}`;
62
+ return `${files.length} $selected`;
66
63
  });
67
64
 
68
65
  function add(incoming: FileList | File[] | null) {
@@ -91,9 +88,9 @@
91
88
  }
92
89
 
93
90
  const reasons: Record<RejectedFile['reason'], string> = $derived({
94
- type: t.current.rejectedType,
95
- size: t.current.rejectedSize,
96
- count: t.current.rejectedCount
91
+ type: 'wrong file type',
92
+ size: 'too large',
93
+ count: 'over the file limit'
97
94
  });
98
95
 
99
96
  const sizes: Record<InputSize, string> = {
@@ -125,7 +122,7 @@
125
122
  class="shrink-0 rounded-lg border border-hairline-strong bg-bg-alt font-sans font-medium text-text-secondary transition-colors duration-150 hover:bg-bg-inset hover:text-text focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-current
126
123
  {buttons[size]}"
127
124
  >
128
- {label ?? t.current.chooseFile}
125
+ {label ?? 'Choose file'}
129
126
  </button>
130
127
 
131
128
  <span
@@ -138,7 +135,7 @@
138
135
  <button
139
136
  type="button"
140
137
  onclick={clear}
141
- aria-label={t.current.remove}
138
+ aria-label="Remove"
142
139
  class="mr-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"
143
140
  >
144
141
  <svg class="size-3.5" viewBox="0 0 14 14" fill="none" aria-hidden="true">
@@ -186,7 +183,7 @@
186
183
  <button
187
184
  type="button"
188
185
  onclick={() => (files = files.filter((_, at) => at !== i))}
189
- aria-label="{t.current.remove} {file.name}"
186
+ aria-label="Remove {file.name}"
190
187
  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"
191
188
  >
192
189
  <svg class="size-3.5" viewBox="0 0 14 14" fill="none" aria-hidden="true">
@@ -15,9 +15,9 @@ interface Props {
15
15
  size?: InputSize;
16
16
  tone?: Tone;
17
17
  invalid?: boolean;
18
- /** Text on the button. Defaults to the locale's `chooseFile`. */
18
+ /** Text on the button. Defaults to `Choose file`. */
19
19
  label?: string;
20
- /** Shown while nothing is chosen. Defaults to the locale's `noFile`. */
20
+ /** Shown while nothing is chosen. Defaults to `No file chosen`. */
21
21
  placeholder?: string;
22
22
  /** Show the list of held files below the row. */
23
23
  showList?: boolean;
@@ -22,7 +22,7 @@
22
22
  columns?: FooterColumn[];
23
23
  /** Logo, wordmark and a line of copy on the left. */
24
24
  brand?: Snippet;
25
- /** Right side of the bottom bar — social links, a locale picker. */
25
+ /** Right side of the bottom bar — social links, a language picker. */
26
26
  bottom?: Snippet;
27
27
  /** Left side of the bottom bar. Ignored when `bottom` covers both. */
28
28
  copyright?: string;
@@ -15,7 +15,7 @@ interface Props {
15
15
  columns?: FooterColumn[];
16
16
  /** Logo, wordmark and a line of copy on the left. */
17
17
  brand?: Snippet;
18
- /** Right side of the bottom bar — social links, a locale picker. */
18
+ /** Right side of the bottom bar — social links, a language picker. */
19
19
  bottom?: Snippet;
20
20
  /** Left side of the bottom bar. Ignored when `bottom` covers both. */
21
21
  copyright?: string;