@happyvertical/smrt-ui 0.51.38 → 0.52.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (212) hide show
  1. package/AGENTS.md +14 -2
  2. package/README.md +257 -5
  3. package/dist/actions/__tests__/swipe-dismiss.test.js +69 -0
  4. package/dist/actions/swipe-dismiss.d.ts +42 -0
  5. package/dist/actions/swipe-dismiss.d.ts.map +1 -0
  6. package/dist/actions/swipe-dismiss.js +69 -0
  7. package/dist/components/calendar/Calendar.svelte +5 -0
  8. package/dist/components/calendar/Calendar.svelte.d.ts.map +1 -1
  9. package/dist/components/calendar/CalendarView.svelte +1250 -0
  10. package/dist/components/calendar/CalendarView.svelte.d.ts +66 -0
  11. package/dist/components/calendar/CalendarView.svelte.d.ts.map +1 -0
  12. package/dist/components/calendar/DayView.svelte +5 -0
  13. package/dist/components/calendar/DayView.svelte.d.ts.map +1 -1
  14. package/dist/components/calendar/__tests__/CalendarView.test.js +226 -0
  15. package/dist/components/calendar/__tests__/calendar-model.test.js +138 -0
  16. package/dist/components/calendar/calendar-model.d.ts +178 -0
  17. package/dist/components/calendar/calendar-model.d.ts.map +1 -0
  18. package/dist/components/calendar/calendar-model.js +362 -0
  19. package/dist/components/calendar/index.d.ts +12 -1
  20. package/dist/components/calendar/index.d.ts.map +1 -1
  21. package/dist/components/calendar/index.js +11 -1
  22. package/dist/components/data/CollectionList.svelte +23 -5
  23. package/dist/components/data/CollectionList.svelte.d.ts +8 -2
  24. package/dist/components/data/CollectionList.svelte.d.ts.map +1 -1
  25. package/dist/components/data/DataTable.svelte +118 -40
  26. package/dist/components/data/DataTable.svelte.d.ts.map +1 -1
  27. package/dist/components/data/DataTableController.d.ts +4 -0
  28. package/dist/components/data/DataTableController.d.ts.map +1 -1
  29. package/dist/components/data/DataTableController.js +11 -4
  30. package/dist/components/data/ListSortSelect.svelte +121 -0
  31. package/dist/components/data/ListSortSelect.svelte.d.ts +22 -0
  32. package/dist/components/data/ListSortSelect.svelte.d.ts.map +1 -0
  33. package/dist/components/data/SortableHeader.svelte +169 -0
  34. package/dist/components/data/SortableHeader.svelte.d.ts +30 -0
  35. package/dist/components/data/SortableHeader.svelte.d.ts.map +1 -0
  36. package/dist/components/data/__tests__/CollectionList.test.js +37 -0
  37. package/dist/components/data/__tests__/DataTable.test.js +76 -0
  38. package/dist/components/data/__tests__/DataTableController.test.js +35 -0
  39. package/dist/components/data/__tests__/SortableHeader.test.js +101 -0
  40. package/dist/components/data/__tests__/link-step-surface.test.js +172 -0
  41. package/dist/components/data/__tests__/list-sort-state.svelte.test.js +71 -0
  42. package/dist/components/data/__tests__/list-sort.test.js +91 -0
  43. package/dist/components/data/__tests__/surface-navigation.test.js +121 -0
  44. package/dist/components/data/data-surface.d.ts +1 -0
  45. package/dist/components/data/data-surface.d.ts.map +1 -1
  46. package/dist/components/data/data-surface.js +1 -0
  47. package/dist/components/data/index.d.ts +6 -0
  48. package/dist/components/data/index.d.ts.map +1 -1
  49. package/dist/components/data/index.js +6 -0
  50. package/dist/components/data/link-surface.d.ts +79 -0
  51. package/dist/components/data/link-surface.d.ts.map +1 -0
  52. package/dist/components/data/link-surface.js +155 -0
  53. package/dist/components/data/list-sort-state.svelte.d.ts +39 -0
  54. package/dist/components/data/list-sort-state.svelte.d.ts.map +1 -0
  55. package/dist/components/data/list-sort-state.svelte.js +47 -0
  56. package/dist/components/data/list-sort.d.ts +94 -0
  57. package/dist/components/data/list-sort.d.ts.map +1 -0
  58. package/dist/components/data/list-sort.js +182 -0
  59. package/dist/components/data/step-surface.d.ts +73 -0
  60. package/dist/components/data/step-surface.d.ts.map +1 -0
  61. package/dist/components/data/step-surface.js +177 -0
  62. package/dist/components/data/surface-navigation.d.ts +49 -0
  63. package/dist/components/data/surface-navigation.d.ts.map +1 -0
  64. package/dist/components/data/surface-navigation.js +100 -0
  65. package/dist/components/data/types.d.ts +19 -0
  66. package/dist/components/data/types.d.ts.map +1 -1
  67. package/dist/components/feedback/PhoneSheet.svelte +224 -0
  68. package/dist/components/feedback/PhoneSheet.svelte.d.ts +35 -0
  69. package/dist/components/feedback/PhoneSheet.svelte.d.ts.map +1 -0
  70. package/dist/components/feedback/WorkingStrip.svelte +426 -0
  71. package/dist/components/feedback/WorkingStrip.svelte.d.ts +37 -0
  72. package/dist/components/feedback/WorkingStrip.svelte.d.ts.map +1 -0
  73. package/dist/components/feedback/__tests__/PhoneSheet.test.js +101 -0
  74. package/dist/components/feedback/__tests__/WorkingStrip.test.js +172 -0
  75. package/dist/components/feedback/index.d.ts +4 -0
  76. package/dist/components/feedback/index.d.ts.map +1 -1
  77. package/dist/components/feedback/index.js +5 -0
  78. package/dist/components/feedback/working-status.d.ts +34 -0
  79. package/dist/components/feedback/working-status.d.ts.map +1 -0
  80. package/dist/components/feedback/working-status.js +5 -0
  81. package/dist/components/forms/Checkbox.svelte +26 -2
  82. package/dist/components/forms/Combobox.svelte +68 -12
  83. package/dist/components/forms/Combobox.svelte.d.ts +6 -0
  84. package/dist/components/forms/Combobox.svelte.d.ts.map +1 -1
  85. package/dist/components/forms/DictationButton.svelte +128 -0
  86. package/dist/components/forms/DictationButton.svelte.d.ts +19 -0
  87. package/dist/components/forms/DictationButton.svelte.d.ts.map +1 -0
  88. package/dist/components/forms/DictationStatus.svelte +164 -0
  89. package/dist/components/forms/DictationStatus.svelte.d.ts +11 -0
  90. package/dist/components/forms/DictationStatus.svelte.d.ts.map +1 -0
  91. package/dist/components/forms/Fieldset.svelte +5 -1
  92. package/dist/components/forms/Fieldset.svelte.d.ts +2 -0
  93. package/dist/components/forms/Fieldset.svelte.d.ts.map +1 -1
  94. package/dist/components/forms/FormActionBar.svelte +84 -0
  95. package/dist/components/forms/FormActionBar.svelte.d.ts +23 -0
  96. package/dist/components/forms/FormActionBar.svelte.d.ts.map +1 -0
  97. package/dist/components/forms/Input.svelte +9 -0
  98. package/dist/components/forms/Radio.svelte +15 -4
  99. package/dist/components/forms/SearchInput.svelte +207 -0
  100. package/dist/components/forms/SearchInput.svelte.d.ts +22 -0
  101. package/dist/components/forms/SearchInput.svelte.d.ts.map +1 -0
  102. package/dist/components/forms/Select.svelte +9 -0
  103. package/dist/components/forms/StagedControlReview.svelte +21 -12
  104. package/dist/components/forms/StagedControlReview.svelte.d.ts.map +1 -1
  105. package/dist/components/forms/Switch.svelte +11 -5
  106. package/dist/components/forms/TagsInput.svelte +8 -1
  107. package/dist/components/forms/Textarea.svelte +8 -0
  108. package/dist/components/forms/__tests__/Combobox.test.js +87 -0
  109. package/dist/components/forms/__tests__/ControlHitArea.test.js +73 -0
  110. package/dist/components/forms/__tests__/DictationButton.test.js +176 -0
  111. package/dist/components/forms/__tests__/Fieldset.test.js +37 -0
  112. package/dist/components/forms/__tests__/FormActionBar.test.js +22 -0
  113. package/dist/components/forms/__tests__/SearchInput.test.js +92 -0
  114. package/dist/components/forms/__tests__/StagedControlReview.test.js +37 -3
  115. package/dist/components/forms/__tests__/combobox-labels.fixture.svelte +19 -0
  116. package/dist/components/forms/__tests__/combobox-labels.fixture.svelte.d.ts +11 -0
  117. package/dist/components/forms/__tests__/combobox-labels.fixture.svelte.d.ts.map +1 -0
  118. package/dist/components/forms/__tests__/composite-registration.fixture.svelte +29 -0
  119. package/dist/components/forms/__tests__/composite-registration.fixture.svelte.d.ts +8 -0
  120. package/dist/components/forms/__tests__/composite-registration.fixture.svelte.d.ts.map +1 -0
  121. package/dist/components/forms/__tests__/control-interaction.test.js +19 -1
  122. package/dist/components/forms/__tests__/control-proposals.test.js +63 -0
  123. package/dist/components/forms/__tests__/dictation-recording.test.js +582 -0
  124. package/dist/components/forms/__tests__/dictation.test.js +486 -0
  125. package/dist/components/forms/__tests__/long-press.test.js +175 -0
  126. package/dist/components/forms/__tests__/place-picker.fixture.svelte +33 -0
  127. package/dist/components/forms/__tests__/place-picker.fixture.svelte.d.ts +12 -0
  128. package/dist/components/forms/__tests__/place-picker.fixture.svelte.d.ts.map +1 -0
  129. package/dist/components/forms/audio-capture.d.ts +69 -0
  130. package/dist/components/forms/audio-capture.d.ts.map +1 -0
  131. package/dist/components/forms/audio-capture.js +193 -0
  132. package/dist/components/forms/control-interaction.d.ts +6 -0
  133. package/dist/components/forms/control-interaction.d.ts.map +1 -1
  134. package/dist/components/forms/control-interaction.js +12 -1
  135. package/dist/components/forms/control-proposals.d.ts +55 -0
  136. package/dist/components/forms/control-proposals.d.ts.map +1 -0
  137. package/dist/components/forms/control-proposals.js +188 -0
  138. package/dist/components/forms/dictation-transcribe.d.ts +36 -0
  139. package/dist/components/forms/dictation-transcribe.d.ts.map +1 -0
  140. package/dist/components/forms/dictation-transcribe.js +65 -0
  141. package/dist/components/forms/dictation.svelte.d.ts +200 -0
  142. package/dist/components/forms/dictation.svelte.d.ts.map +1 -0
  143. package/dist/components/forms/dictation.svelte.js +686 -0
  144. package/dist/components/forms/index.d.ts +14 -1
  145. package/dist/components/forms/index.d.ts.map +1 -1
  146. package/dist/components/forms/index.js +14 -1
  147. package/dist/components/forms/insert-text.d.ts +12 -0
  148. package/dist/components/forms/insert-text.d.ts.map +1 -0
  149. package/dist/components/forms/insert-text.js +38 -0
  150. package/dist/components/forms/long-press.d.ts +88 -0
  151. package/dist/components/forms/long-press.d.ts.map +1 -0
  152. package/dist/components/forms/long-press.js +213 -0
  153. package/dist/components/forms/ready-beep.d.ts +24 -0
  154. package/dist/components/forms/ready-beep.d.ts.map +1 -0
  155. package/dist/components/forms/ready-beep.js +82 -0
  156. package/dist/components/layout/PageHeader.svelte +151 -17
  157. package/dist/components/layout/PageHeader.svelte.d.ts +47 -7
  158. package/dist/components/layout/PageHeader.svelte.d.ts.map +1 -1
  159. package/dist/components/layout/__tests__/PageHeader.test.js +87 -1
  160. package/dist/components/layout/index.d.ts +1 -0
  161. package/dist/components/layout/index.d.ts.map +1 -1
  162. package/dist/components/layout/index.js +1 -0
  163. package/dist/components/layout/page-header-context.d.ts +25 -0
  164. package/dist/components/layout/page-header-context.d.ts.map +1 -0
  165. package/dist/components/layout/page-header-context.js +16 -0
  166. package/dist/components/nav/Tabs.svelte +241 -0
  167. package/dist/components/nav/Tabs.svelte.d.ts +15 -0
  168. package/dist/components/nav/Tabs.svelte.d.ts.map +1 -1
  169. package/dist/components/nav/__tests__/LinkTabs.test.js +117 -0
  170. package/dist/components/nav/index.d.ts +1 -0
  171. package/dist/components/nav/index.d.ts.map +1 -1
  172. package/dist/components/nav/index.js +1 -0
  173. package/dist/components/nav/tabs-overflow.d.ts +12 -0
  174. package/dist/components/nav/tabs-overflow.d.ts.map +1 -0
  175. package/dist/components/nav/tabs-overflow.js +17 -0
  176. package/dist/components/nav/types.d.ts +8 -0
  177. package/dist/components/nav/types.d.ts.map +1 -1
  178. package/dist/components/ui/Button.svelte +7 -0
  179. package/dist/components/ui/Card.svelte +14 -5
  180. package/dist/components/ui/Dropdown.svelte +55 -1
  181. package/dist/components/ui/Dropdown.svelte.d.ts +8 -0
  182. package/dist/components/ui/Dropdown.svelte.d.ts.map +1 -1
  183. package/dist/components/ui/IconToggle.svelte +139 -0
  184. package/dist/components/ui/IconToggle.svelte.d.ts +32 -0
  185. package/dist/components/ui/IconToggle.svelte.d.ts.map +1 -0
  186. package/dist/components/ui/IconToggleGroup.svelte +56 -0
  187. package/dist/components/ui/IconToggleGroup.svelte.d.ts +21 -0
  188. package/dist/components/ui/IconToggleGroup.svelte.d.ts.map +1 -0
  189. package/dist/components/ui/Pagination.svelte +1 -1
  190. package/dist/components/ui/Popover.svelte +17 -1
  191. package/dist/components/ui/Popover.svelte.d.ts +8 -0
  192. package/dist/components/ui/Popover.svelte.d.ts.map +1 -1
  193. package/dist/components/ui/__tests__/IconToggle.test.js +59 -0
  194. package/dist/components/ui/__tests__/IconToggleGroup.test.js +30 -0
  195. package/dist/components/ui/__tests__/Popover.test.js +44 -0
  196. package/dist/components/ui/__tests__/dropdown-popover-icon.test.js +125 -0
  197. package/dist/components/ui/__tests__/phone-touch-targets.test.js +39 -0
  198. package/dist/components/ui/index.d.ts +2 -0
  199. package/dist/components/ui/index.d.ts.map +1 -1
  200. package/dist/components/ui/index.js +2 -0
  201. package/dist/i18n/strings.ui.d.ts +41 -0
  202. package/dist/i18n/strings.ui.d.ts.map +1 -1
  203. package/dist/i18n/strings.ui.js +46 -0
  204. package/dist/svelte/__tests__/playground.test.js +1 -0
  205. package/dist/svelte/playground/CalendarPreview.svelte +140 -0
  206. package/dist/svelte/playground/CalendarPreview.svelte.d.ts +4 -0
  207. package/dist/svelte/playground/CalendarPreview.svelte.d.ts.map +1 -0
  208. package/dist/svelte/playground.d.ts.map +1 -1
  209. package/dist/svelte/playground.js +10 -0
  210. package/dist/types-generic.d.ts +2 -0
  211. package/dist/types-generic.d.ts.map +1 -1
  212. package/package.json +3 -3
package/AGENTS.md CHANGED
@@ -46,11 +46,11 @@ components are exempt — they *are* the primitives.
46
46
  | `.` | barrel re-exporting the primitives, registry, and theme system |
47
47
  | `./playground` | shared-host previews for the complete foundation catalog; rendered inside the active preset and color scheme |
48
48
  | `./ui` | `Button`, `Card`, `Badge`, `Avatar`, `Chip`, `Skeleton`, `Tooltip`, `Dropdown`/`Menu`, `Popover`, `Disclosure`, `Accordion`, `Tree`, `Pagination`, … |
49
- | `./feedback` | `Alert`, `ToastViewport`, `Modal`, `Drawer`/`Sheet`, `ConfirmDialog`, `LoadingOverlay`, `Progress`, `Meter`, `Spinner` |
49
+ | `./feedback` | `Alert`, `ToastViewport`, `Modal`, `Drawer`/`Sheet`, `ConfirmDialog`, `LoadingOverlay`, `Progress`, `Meter`, `Spinner`, phone surfaces `PhoneSheet` / `WorkingStrip` and the `swipeDismiss` action |
50
50
  | `./data` | `CollectionToolbar`, `CollectionList`/`ContentList`, `DataTable` and their types — a **component barrel**: it re-exports `.svelte` files, so a plain Node `import()` of it throws `ERR_UNKNOWN_FILE_EXTENSION` |
51
51
  | `./data-surface` | **Svelte-free**: registry contracts and shared protocol limits (`DATA_SURFACE_MAX_REQUEST_BYTES`, `DATA_SURFACE_IDENTIFIER_MAX_LENGTH`, …). Server adapters must import these from here, not from `./data` — a server entry that reaches the barrel breaks the `smrt` CLI's plain-Node `.smrt/register.js` load (issue #2924) |
52
52
  | `./layout` | `Container`, `Grid`, `Header`, `Footer`, `PageHeader`, `EmptyState`, … |
53
- | `./calendar` | `Calendar`, `DayView` |
53
+ | `./calendar` | `CalendarView` (time-zone-aware month grid + phone agenda) and its Svelte-free date model (`toEntries`, `layoutMonth`, `dateKeyInZone`, …); deprecated `Calendar`, `DayView` |
54
54
  | `./chat` | `MessageBubble`, `ReactionPicker`, `TypingIndicator` |
55
55
  | `./forms` | Provider-free fields, choice controls, sliders/ranges, combobox/listbox/multiselect/tags, date/time/file controls, plus the transport-neutral control interaction registry |
56
56
  | `./i18n` | i18n **client**: `useI18n`, `<Trans>`, `defineMessages`, `renderTemplate` (no `smrt-languages` import — the server resolver stays in `smrt-svelte/i18n/server`) |
@@ -149,7 +149,19 @@ other text pairing clears WCAG AA in both schemes.
149
149
 
150
150
  ## Gotchas
151
151
 
152
+ - **Calendar dates are `YYYY-MM-DD` keys in the view's `timeZone`.** Project
153
+ instants into the zone once (`dateKeyInZone`) and do all grid/span math on
154
+ keys; never `new Date(y, m, d)` / `getDay()` in the browser zone. Months are
155
+ 1-12 in the public API (`CalendarMonth`), unlike `Date#getMonth`.
156
+
152
157
  - **i18n split**: the client (here) is dependency-free; the Node-only server
153
158
  resolver (`buildI18nSnapshot`, → `@happyvertical/smrt-languages`) stays in
154
159
  `@happyvertical/smrt-svelte/i18n/server`. Package string catalogs register via
155
160
  `defineMessages` imported from `@happyvertical/smrt-ui/i18n`.
161
+
162
+ - **Consumers type-checking a local checkout** hit "two different types with
163
+ this name" on `Snippet` when two Svelte copies meet (svelte2tsx's
164
+ `/// <reference types="svelte" />` in compiled sources ignores `paths`). Keep
165
+ every Svelte export's `types` condition first and pointing at built
166
+ `*.svelte.d.ts`; the consumer recipe is
167
+ [docs/development/svelte-consumer-linking.md](../../docs/development/svelte-consumer-linking.md).
package/README.md CHANGED
@@ -29,15 +29,67 @@ pnpm add @happyvertical/smrt-ui
29
29
 
30
30
  | Area | Components |
31
31
  | --- | --- |
32
- | Fields | `Form`, `Field`/`FormGroup`, `Fieldset`, `InputGroup`, `ErrorSummary` |
33
- | Text and structured input | `Input`, `Textarea`, `Select`, `Combobox`, `Listbox`, `MultiSelect`, `TagsInput` |
32
+ | Fields | `Form`, `Field`/`FormGroup`, `Fieldset`, `InputGroup`, `ErrorSummary`, `FormActionBar` |
33
+ | Text and structured input | `Input`, `Textarea`, `Select`, `Combobox`, `Listbox`, `MultiSelect`, `TagsInput`, `SearchInput` |
34
34
  | Choices | `Checkbox`, `RadioGroup`/`Radio`, `Switch`, `Toggle`, `ToggleButton`, `SegmentedControl` |
35
35
  | Values and files | `Slider`, `RangeSlider`, `DatePicker`, `TimePicker`, `FilePicker` |
36
36
  | Actions and display | `Button`, `Dropdown`/`Menu`, `Badge`, `Chip`, `Avatar`, `Card`, `Skeleton`, `Tooltip`, `Tree` |
37
- | Disclosure and overlays | `Popover`, `Disclosure`, `Accordion`/`AccordionItem`, `Modal`, `Drawer`/`Sheet`, `ConfirmDialog` |
38
- | Feedback | `Alert`, `ToastViewport`, `Progress`, `Meter`, `Spinner`, `LoadingOverlay` |
37
+ | Disclosure and overlays | `Popover`, `Disclosure`, `Accordion`/`AccordionItem`, `Modal`, `Drawer`/`Sheet`, `PhoneSheet`, `ConfirmDialog` |
38
+ | Feedback | `Alert`, `ToastViewport`, `Progress`, `Meter`, `Spinner`, `LoadingOverlay`, `WorkingStrip` |
39
39
  | Collections | `CollectionToolbar`, `CollectionList`/`ContentList`, `DataTable`, `Pagination` |
40
40
  | Layout and navigation | `Container`, `Grid`, `Header`, `Footer`, `PageHeader`, `EmptyState`, `Tabs`, `FilterChips` |
41
+ | Calendar | `CalendarView` (deprecated: `Calendar`, `DayView`) |
42
+
43
+ ### Phone surfaces
44
+
45
+ - `PhoneSheet` (`/feedback`) is the phone replacement for drawers and centered
46
+ modals: `variant="page"` covers the area, `variant="sheet"` rises from the
47
+ bottom and closes on a swipe down its header. It is non-modal, closes on its
48
+ button and Escape, returns focus to the opener, and can stay mounted while
49
+ closed (hidden and inert) so its content keeps state. It is
50
+ `position: absolute; inset: 0` — render it in AdminShell's `overlays`.
51
+ - `WorkingStrip` (`/feedback`) shows a transport-neutral `WorkingStatus`
52
+ (`idle` · `working` · `paused` · `waiting` · `done` · `failed` ·
53
+ `cancelled`, a `label` for the current step and an optional `goal`) as a
54
+ strip above a phone bottom bar, a floating pill (`variant="floating"`, the
55
+ host positions it), or a line at the top of a pane. Optional controls:
56
+ reopen (`onopen`), Pause/Continue (`onpause`/`onresume`; Escape pauses),
57
+ Review while waiting (`onreview`), Stop, and Close once finished
58
+ (`ondismiss`); all 44px. Done uses the success colors, failed the error
59
+ colors, always with an icon and text. Step changes are announced politely
60
+ (throttled by `announceIntervalMs`), `waiting` and `failed` assertively; it
61
+ never takes focus.
62
+ - `FormActionBar` (`/forms`) groups a form's actions (primary last). On phones
63
+ it is fixed to the bottom, carries `data-form-action-bar` so AdminShell hides
64
+ its phone bottom bar, and hides while `:root[data-keyboard-open]`.
65
+ - `swipeDismiss` / `swipeDismisses` (`/feedback`) are the touch action and pure
66
+ decision behind swipe-to-close.
67
+ - `Dictation` / `DictationButton` / `DictationStatus` (`/forms`) let people
68
+ speak into a text field (tap the microphone, or press and hold the field
69
+ with `longPress`). The speech `source` is normally smrt-svelte's
70
+ `createSttDictationSource()` (the browser's own speech recognition). Give
71
+ it a `transcribe` function too (`createHttpTranscriber('/your/route')`)
72
+ and, where the browser has no speech recognition (Firefox) or no speech
73
+ service behind it (Brave: `network` / `service-not-allowed`), it records
74
+ the message with `MediaRecorder` instead (WebM/Opus, MP4 on Safari; at most
75
+ `maxDurationMs` 2 minutes and `maxBytes` 10 MB), shows "Writing it down…"
76
+ while the route turns it into text, and puts the text at the cursor.
77
+ Keep the speech service's key on the server: the route takes the raw audio
78
+ body (`Content-Type` is the recording's type, `?language=&durationMs=`)
79
+ and answers `{ text }`; 413, 503 and 401/403 become "too long", "not set
80
+ up" and "not allowed" messages.
81
+
82
+ ### Link tabs
83
+
84
+ `Tabs` with an `href` on every tab renders a page's sections as navigation —
85
+ a `<nav>` of links, `aria-current="page"` on the active one, each tab its own
86
+ URL — instead of an ARIA tablist. `maxVisible` moves extra tabs into a "More"
87
+ menu while the active tab always stays in the row (`splitTabs` is the pure
88
+ rule), `badge` marks a tab needing attention (and dots "More" when a hidden
89
+ tab has one), and the row scrolls sideways on phones with the active tab
90
+ kept in view. It carries `data-shell-tabs`, so AdminShell keeps it sticky
91
+ under the phone top bar. Pair it with `useLinkSurface` (smrt-svelte) so
92
+ agents can switch tabs too.
41
93
 
42
94
  Use the focused subpaths (`/forms`, `/ui`, `/feedback`, `/data`,
43
95
  `/data-surface`, `/layout`, `/themes`) to keep imports explicit. The
@@ -45,6 +97,92 @@ Svelte-free `/data-surface` entry exposes the registry contracts and shared
45
97
  protocol limits for server adapters. The package root remains a compatibility
46
98
  barrel.
47
99
 
100
+ ### Touch targets for Checkbox, Radio and Switch
101
+
102
+ `Checkbox`, `Radio` and `Switch` have an invisible 44x44px hit area around the
103
+ visible control (about 18px for a box or mark, the 2.75rem track for a switch).
104
+ A transparent `::before` on the wrapping `<label>` makes it, so a click anywhere
105
+ in the area toggles the input, no layout shifts, and the focus ring stays on the
106
+ visible box. The box and the label text sit above every hit area, so adjacent
107
+ controls never steal each other's clicks.
108
+
109
+ The hit area paints above plain (unpositioned) content around it, so it is
110
+ constrained to where that cannot cost a neighbour a click:
111
+
112
+ - **Beside other content.** A `Checkbox` or `Radio` that is not the only element
113
+ in its parent (a row title or link next to it, a group's other options) keeps
114
+ its hit area in the box's own column: it grows vertically only, never over the
115
+ text or link beside it. Only a control standing alone gets the full 44x44.
116
+ - **Table cells.** Inside a `td`/`th` the area never leaves the cell. A
117
+ `Checkbox` that is the cell's only content uses the whole cell (clipped to its
118
+ edges). The cell gets `position: relative` at zero specificity (`:where()`), so
119
+ a sticky header or a pinned column keeps its own positioning.
120
+ - **Stacked controls.** Hit areas of tightly stacked controls overlap in the
121
+ gaps; the later control wins a gap, never a neighbour's box or text. Content
122
+ directly above or below a box, closer than 13px, is covered by its hit area:
123
+ give it room, or shrink the area with `--smrt-control-hit-size`.
124
+ - **Resize or turn off.** `--smrt-control-hit-size` (default `2.75rem`); `0px`
125
+ gives only the visible control. Pages never add their own padding hacks.
126
+ - `DataTable`'s built-in selection checkboxes and `CollectionList`'s select box
127
+ are still native inputs and do not use this yet.
128
+
129
+ ## Calendar
130
+
131
+ `CalendarView` (`@happyvertical/smrt-ui/calendar`) is a generic month grid
132
+ with a phone agenda:
133
+
134
+ ```svelte
135
+ <script lang="ts">
136
+ import { CalendarView } from '@happyvertical/smrt-ui/calendar';
137
+ import { goto } from '$app/navigation';
138
+ let { data } = $props(); // { year, month, items }
139
+ </script>
140
+
141
+ <CalendarView
142
+ items={data.items}
143
+ year={data.year}
144
+ month={data.month}
145
+ timeZone="America/Edmonton"
146
+ onNavigate={({ year, month }) => goto(`?y=${year}&m=${month}`, { keepFocus: true })}
147
+ />
148
+ ```
149
+
150
+ - **Items** are generic `CalendarItem`s: `{ id, title, start, end?, allDay?,
151
+ tone?, color?, label?, group?, href? }`. `start`/`end` take a `Date`, an ISO
152
+ date-time, or a `YYYY-MM-DD` date (all-day). Timed `end` is exclusive; an
153
+ all-day `YYYY-MM-DD` end is the inclusive last day, and an all-day end
154
+ instant at local midnight is exclusive. `tone` maps to the theme's color
155
+ roles; without it, `group` picks a stable tone. Items with `href` render as
156
+ links, others as buttons; both call `onItemSelect`.
157
+ - **Time zone and locale**: every day is computed in `timeZone` (IANA;
158
+ defaults to the browser's — pass it explicitly so server and client
159
+ agree). Month, weekday, date, and time text come from `Intl` in `locale`
160
+ (defaults to the i18n locale); `weekStartsOn` defaults to the locale's.
161
+ - **Modes**: `month` is a `role="grid"` month with roving focus (arrows,
162
+ Home/End, PageUp/PageDown cross months). All-day and multi-day items are
163
+ bands across the days they cover; a day with more than `maxPerDay` rows
164
+ shows "+N more", which opens the day in a panel under the grid, or follows
165
+ `dayHref` when given. `agenda` is a horizontally scrolling strip of the
166
+ month's days (44px targets, arrow keys) above a day-by-day list. `auto`
167
+ (default) uses the agenda below 48rem, matching AdminShell's phone
168
+ breakpoint. No modals.
169
+ - **URL state**: `year` + `month` (1-12) and `selectedDate` (`YYYY-MM-DD`) are
170
+ controlled when passed; changes are reported via `onNavigate` and
171
+ `onSelectDate`, so a page can keep them in its URL.
172
+ - The Svelte-free date model (`toEntries`, `layoutMonth`, `dateKeyInZone`,
173
+ `monthWeeks`, `shiftMonth`, …) is exported from the same subpath for
174
+ server-side range queries and tests.
175
+
176
+ **Migrating from `Calendar` / `DayView` (deprecated).** They compute days in
177
+ the browser zone, print English-only names, and hard-code game/meeting/event
178
+ emoji and routes. Map each `DayEventDetail` to a `CalendarItem` (`name` →
179
+ `title`, `type` → `group`/`label`, your own route → `href`), replace
180
+ `Calendar`'s `year`/`month` (0-indexed) with `CalendarView`'s `year`/`month`
181
+ (1-12) and `onMonthNavigate` with `onNavigate`, `baseUrl` with a `dayHref`
182
+ function that builds your day route from the `YYYY-MM-DD` key, and a `DayView`
183
+ page with `CalendarView` in agenda mode (`selectedDate` set to the day) or the
184
+ month grid's day panel. They will be removed in a future minor release.
185
+
48
186
  ### Currency display
49
187
 
50
188
  `CurrencyDisplay` accepts ISO 4217 codes as a public `string` prop so persisted
@@ -128,7 +266,8 @@ without coupling controls to a transport:
128
266
 
129
267
  Controls publish serializable metadata, constraints, options, sensitivity, and
130
268
  capabilities. Adapters can focus, reveal, highlight, explain, validate, and
131
- stage reviewable proposals. Agents cannot apply, discard, clear, or undo;
269
+ stage reviewable proposals. An agent never moves keyboard focus: its `focus`
270
+ reveals and highlights the control instead. Agents cannot apply, discard, clear, or undo;
132
271
  those value-changing actions require a trusted local gesture handled by the
133
272
  framework review surface. Secret/read-only controls reject agent mutations.
134
273
  Staging remains separate so proposals never change user state before review.
@@ -178,6 +317,51 @@ not await a same-control mutation through a captured registry reference: that
178
317
  call is indistinguishable from an independent caller in browser runtimes and,
179
318
  like any hook that never settles, can hold the ordered queue indefinitely.
180
319
 
320
+ ### Composite controls and form proposals
321
+
322
+ A composite field — a place picker, a slug field with its own preview, a
323
+ record picker — registers as ONE control with `useControlRegistration` (call
324
+ it during component init inside any `Form`, rich `Form`, or `FormScope`). Give
325
+ it a stable `controlId`, a plain `label`, and, when its value is not a plain
326
+ string/number/boolean, a `valueSchema` so adapters can describe it:
327
+
328
+ ```svelte
329
+ <script lang="ts">
330
+ import { useControlRegistration } from '@happyvertical/smrt-ui/forms';
331
+
332
+ let { value = $bindable(null) } = $props();
333
+
334
+ useControlRegistration(() => ({
335
+ controlId: 'place',
336
+ metadata: {
337
+ kind: 'custom',
338
+ label: 'Town location',
339
+ valueSchema: {
340
+ type: 'object',
341
+ properties: { name: { type: 'string' }, latitude: { type: 'number' }, longitude: { type: 'number' } },
342
+ },
343
+ },
344
+ getValue: () => value,
345
+ setValue: (next) => { value = next; },
346
+ }));
347
+ </script>
348
+ ```
349
+
350
+ Call `recordControlUserEdit(context, controlId, subject)` from the
351
+ composite's own user-event handlers so a staged proposal goes stale when the
352
+ person edits over it (native inputs bubble their events to the Form instead).
353
+ `focusControl`, `revealControl`, `highlightControl`, and `emitControlChange`
354
+ are the same DOM helpers the built-in primitives use for their `focus`,
355
+ `reveal`, and `highlight` handles.
356
+
357
+ `controlProposalInputSchema(registry, formId)` and
358
+ `stageControlProposals(registry, formId, values)` are the transport-neutral
359
+ halves of a "propose values for this form" tool: the schema has one optional
360
+ property per proposable control (never secret/sensitive, unwritable,
361
+ disabled, read-only, file, or password controls), and staging only ever
362
+ creates reviewable proposals. smrt-svelte's `<Form webmcp>` and `FormScope`
363
+ build their `*_stage_changes` tool from these.
364
+
181
365
  ## DataTable controller
182
366
 
183
367
  `DataTable` can share one headless `DataTableController` between rendered
@@ -437,6 +621,34 @@ registry.register({
437
621
  });
438
622
  ```
439
623
 
624
+ ### Navigation and step surfaces
625
+
626
+ Two ready-made surfaces cover interactions a view intent cannot reach on its
627
+ own (an intent only dispatches a registry command):
628
+
629
+ - `registerLinkSurface({ registry, surfaceId, label, description, links,
630
+ navigate })` mounts a menu, tab row, or section list. `state.links`
631
+ publishes each link's id, label, description, and group — never its href —
632
+ and the default `open` control resolves `{ target }` (an id, a label, or a
633
+ unique label prefix) to one of those links and calls `navigate(href)`.
634
+ Nothing an agent sends can become a URL.
635
+ Return the navigation's promise from `navigate`: the command answers at
636
+ once, and `whenSurfaceNavigationSettled(registry)` resolves once it
637
+ finished and the new page's surfaces registered (a bespoke surface that
638
+ navigates calls `trackSurfaceNavigation(registry, promise)`).
639
+ - `registerStepSurface({ registry, surfaceId, label, description, steps,
640
+ current, next, back, goTo, nextWrites, showNext })` mounts a wizard.
641
+ `state` carries the steps with their status, the current step, and
642
+ `nextWrites`. `next`/`back`/`go-to` run the page's own handlers (return
643
+ `false` to refuse, e.g. when validation fails). When the current step's
644
+ forward button saves or creates something (`nextWrites`), `next` never
645
+ presses it: it calls `showNext` to reveal and highlight the button and
646
+ publishes `awaitingPerson: true` until the flow moves on.
647
+
648
+ smrt-svelte's `useLinkSurface` / `useStepSurface` / `useListSurface`
649
+ (`@happyvertical/smrt-svelte/web`) bind these to a component's lifetime on
650
+ the Provider's registry.
651
+
440
652
  `inspect()` and command results are deterministic `{ version, descriptor,
441
653
  revision, state, selection }` envelopes; neither includes a timestamp, rows,
442
654
  functions, authority fields, tenant/principal data, SQL, or a transport handle.
@@ -529,6 +741,46 @@ hand-authored, and every text pairing clears WCAG AA.
529
741
  Run the shared playground to inspect the full catalog under every preset and
530
742
  light/dark scheme.
531
743
 
744
+ ### Card look tokens
745
+
746
+ `Card` and `CollectionList` rows read these custom properties, so an app can
747
+ change the card look once in its own theme layer instead of per page. Unset,
748
+ they keep the stock look.
749
+
750
+ | Property | Default | Used for |
751
+ | --- | --- | --- |
752
+ | `--smrt-card-border` | `1px solid var(--smrt-color-outline-variant)` | edge of `default` and `elevated` cards, CollectionList rows |
753
+ | `--smrt-card-background` | `var(--smrt-color-surface)` | card and row fill |
754
+ | `--smrt-card-shadow` | `none` | shadow of `default` cards (`elevated` keeps its elevation) |
755
+ | `--smrt-card-divider` | `1px solid var(--smrt-color-outline-variant)` | rule under a card header / above its footer |
756
+
757
+ The `outlined` variant always draws its outline. For borderless cards on a
758
+ `surface` page, pair `--smrt-card-border: none` with a tinted background such
759
+ as `var(--smrt-color-surface-variant)` so cards stay distinct in both schemes:
760
+
761
+ ```css
762
+ :root {
763
+ --smrt-card-border: none;
764
+ --smrt-card-background: var(--smrt-color-surface-variant);
765
+ }
766
+ ```
767
+
768
+ ### Component override hooks
769
+
770
+ Hooks an app sets once in its own theme layer instead of restyling a component's
771
+ classes. Unset, each keeps its stock look.
772
+
773
+ | Property | Component | Used for |
774
+ | --- | --- | --- |
775
+ | `--smrt-popover-panel-width` | `Popover` | the panel's width (never wider than the viewport minus 2rem) |
776
+ | `--smrt-popover-panel-padding` | `Popover` | the panel's padding |
777
+ | `--smrt-admin-shell-background` | `AdminShell` (smrt-svelte) | the page background behind the cards |
778
+ | `--smrt-admin-shell-scrollbar-track` / `-thumb` / `-thumb-hover` | `AdminShell` (smrt-svelte) | the themed thin scrollbars inside the shell |
779
+ | `--smrt-shell-title-font-family` | `ShellTitle` (smrt-svelte) | the workspace name's font family |
780
+
781
+ `IconToggle` takes a `tone` prop (a CSS colour such as `var(--status-draft)`) for
782
+ a toggle that is neutral until pressed and then wears its colour.
783
+
532
784
  ## Development
533
785
 
534
786
  ```bash
@@ -0,0 +1,69 @@
1
+ import { describe, expect, it, vi } from 'vitest';
2
+ import { swipeDismiss, swipeDismisses } from '../swipe-dismiss.js';
3
+ describe('swipeDismisses', () => {
4
+ it('dismisses a drawer on a clear swipe left only', () => {
5
+ expect(swipeDismisses(-80, 10, 'left')).toBe(true);
6
+ expect(swipeDismisses(-40, 0, 'left')).toBe(false);
7
+ expect(swipeDismisses(-80, 70, 'left')).toBe(false);
8
+ expect(swipeDismisses(80, 0, 'left')).toBe(false);
9
+ });
10
+ it('dismisses a right-hand drawer on a clear swipe right only', () => {
11
+ expect(swipeDismisses(80, 5, 'right')).toBe(true);
12
+ expect(swipeDismisses(-80, 5, 'right')).toBe(false);
13
+ });
14
+ it('dismisses a sheet on a clear swipe down only', () => {
15
+ expect(swipeDismisses(5, 90, 'down')).toBe(true);
16
+ expect(swipeDismisses(0, -90, 'down')).toBe(false);
17
+ expect(swipeDismisses(80, 90, 'down')).toBe(false);
18
+ });
19
+ it('honours a custom threshold', () => {
20
+ expect(swipeDismisses(0, 40, 'down', 32)).toBe(true);
21
+ expect(swipeDismisses(0, 40, 'down')).toBe(false);
22
+ });
23
+ });
24
+ function touch(type, x, y) {
25
+ const point = { clientX: x, clientY: y };
26
+ const event = new Event(type);
27
+ Object.defineProperty(event, 'touches', {
28
+ value: type === 'touchend' ? [] : [point],
29
+ });
30
+ Object.defineProperty(event, 'changedTouches', { value: [point] });
31
+ return event;
32
+ }
33
+ describe('swipeDismiss action', () => {
34
+ it('calls onDismiss after a swipe in its direction', () => {
35
+ const node = document.createElement('div');
36
+ const onDismiss = vi.fn();
37
+ const action = swipeDismiss(node, { direction: 'down', onDismiss });
38
+ node.dispatchEvent(touch('touchstart', 10, 10));
39
+ node.dispatchEvent(touch('touchend', 12, 100));
40
+ expect(onDismiss).toHaveBeenCalledTimes(1);
41
+ action.destroy();
42
+ });
43
+ it('ignores swipes while disabled and after update re-enables them', () => {
44
+ const node = document.createElement('div');
45
+ const onDismiss = vi.fn();
46
+ const action = swipeDismiss(node, {
47
+ direction: 'left',
48
+ onDismiss,
49
+ enabled: false,
50
+ });
51
+ node.dispatchEvent(touch('touchstart', 100, 10));
52
+ node.dispatchEvent(touch('touchend', 10, 10));
53
+ expect(onDismiss).not.toHaveBeenCalled();
54
+ action.update({ direction: 'left', onDismiss, enabled: true });
55
+ node.dispatchEvent(touch('touchstart', 100, 10));
56
+ node.dispatchEvent(touch('touchend', 10, 10));
57
+ expect(onDismiss).toHaveBeenCalledTimes(1);
58
+ action.destroy();
59
+ });
60
+ it('forgets a cancelled touch', () => {
61
+ const node = document.createElement('div');
62
+ const onDismiss = vi.fn();
63
+ swipeDismiss(node, { direction: 'down', onDismiss });
64
+ node.dispatchEvent(touch('touchstart', 10, 10));
65
+ node.dispatchEvent(new Event('touchcancel'));
66
+ node.dispatchEvent(touch('touchend', 10, 200));
67
+ expect(onDismiss).not.toHaveBeenCalled();
68
+ });
69
+ });
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Swipe-to-dismiss for phone surfaces: a drawer closes on a swipe left, a
3
+ * bottom sheet on a swipe down.
4
+ *
5
+ * `swipeDismisses` is the pure decision (unit-testable, DOM-free);
6
+ * `swipeDismiss` is the Svelte action that feeds it touch events. Touch only:
7
+ * mouse drags keep selecting text. Never bind a left-edge swipe to anything —
8
+ * mobile browsers use it for Back.
9
+ */
10
+ /** The direction a surface is swiped to dismiss it. */
11
+ export type SwipeDirection = 'left' | 'right' | 'down';
12
+ /** Minimum travel (px) that counts as a dismissing swipe. */
13
+ export declare const SWIPE_DISMISS_THRESHOLD = 64;
14
+ /**
15
+ * Whether a drag of (`dx`, `dy`) px dismisses a surface swiped in `direction`.
16
+ * The drag must travel at least `threshold` px and be clearly more along the
17
+ * dismiss axis than across it (1.5x), so a vertical scroll never closes a
18
+ * drawer and a sideways pan never closes a sheet.
19
+ */
20
+ export declare function swipeDismisses(dx: number, dy: number, direction: SwipeDirection, threshold?: number): boolean;
21
+ /** Options for the {@link swipeDismiss} action. */
22
+ export interface SwipeDismissOptions {
23
+ /** Direction that dismisses the surface. */
24
+ direction: SwipeDirection;
25
+ /** Called once a swipe in `direction` passes the threshold. */
26
+ onDismiss: () => void;
27
+ /** Set `false` to ignore swipes (e.g. only enable on phones). */
28
+ enabled?: boolean;
29
+ /** Minimum travel in px (default {@link SWIPE_DISMISS_THRESHOLD}). */
30
+ threshold?: number;
31
+ }
32
+ /**
33
+ * Svelte action: dismiss a drawer or sheet with a touch swipe.
34
+ *
35
+ * @example
36
+ * <div use:swipeDismiss={{ direction: 'down', onDismiss: close }}>…</div>
37
+ */
38
+ export declare function swipeDismiss(node: HTMLElement, initial: SwipeDismissOptions): {
39
+ update: (next: SwipeDismissOptions) => void;
40
+ destroy: () => void;
41
+ };
42
+ //# sourceMappingURL=swipe-dismiss.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"swipe-dismiss.d.ts","sourceRoot":"","sources":["../../src/actions/swipe-dismiss.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,uDAAuD;AACvD,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;AAEvD,6DAA6D;AAC7D,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAE1C;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,MAAM,EACV,SAAS,EAAE,cAAc,EACzB,SAAS,SAA0B,GAClC,OAAO,CAQT;AAED,mDAAmD;AACnD,MAAM,WAAW,mBAAmB;IAClC,4CAA4C;IAC5C,SAAS,EAAE,cAAc,CAAC;IAC1B,+DAA+D;IAC/D,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,iEAAiE;IACjE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,sEAAsE;IACtE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,WAAW,EACjB,OAAO,EAAE,mBAAmB,GAC3B;IACD,MAAM,EAAE,CAAC,IAAI,EAAE,mBAAmB,KAAK,IAAI,CAAC;IAC5C,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CAsCA"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Swipe-to-dismiss for phone surfaces: a drawer closes on a swipe left, a
3
+ * bottom sheet on a swipe down.
4
+ *
5
+ * `swipeDismisses` is the pure decision (unit-testable, DOM-free);
6
+ * `swipeDismiss` is the Svelte action that feeds it touch events. Touch only:
7
+ * mouse drags keep selecting text. Never bind a left-edge swipe to anything —
8
+ * mobile browsers use it for Back.
9
+ */
10
+ /** Minimum travel (px) that counts as a dismissing swipe. */
11
+ export const SWIPE_DISMISS_THRESHOLD = 64;
12
+ /**
13
+ * Whether a drag of (`dx`, `dy`) px dismisses a surface swiped in `direction`.
14
+ * The drag must travel at least `threshold` px and be clearly more along the
15
+ * dismiss axis than across it (1.5x), so a vertical scroll never closes a
16
+ * drawer and a sideways pan never closes a sheet.
17
+ */
18
+ export function swipeDismisses(dx, dy, direction, threshold = SWIPE_DISMISS_THRESHOLD) {
19
+ if (direction === 'left') {
20
+ return -dx >= threshold && Math.abs(dx) > Math.abs(dy) * 1.5;
21
+ }
22
+ if (direction === 'right') {
23
+ return dx >= threshold && Math.abs(dx) > Math.abs(dy) * 1.5;
24
+ }
25
+ return dy >= threshold && Math.abs(dy) > Math.abs(dx) * 1.5;
26
+ }
27
+ /**
28
+ * Svelte action: dismiss a drawer or sheet with a touch swipe.
29
+ *
30
+ * @example
31
+ * <div use:swipeDismiss={{ direction: 'down', onDismiss: close }}>…</div>
32
+ */
33
+ export function swipeDismiss(node, initial) {
34
+ let options = initial;
35
+ let start = null;
36
+ function onStart(event) {
37
+ if (options.enabled === false || event.touches.length !== 1)
38
+ return;
39
+ const touch = event.touches[0];
40
+ start = { x: touch.clientX, y: touch.clientY };
41
+ }
42
+ function onEnd(event) {
43
+ if (!start)
44
+ return;
45
+ const touch = event.changedTouches[0];
46
+ const dx = touch.clientX - start.x;
47
+ const dy = touch.clientY - start.y;
48
+ start = null;
49
+ if (swipeDismisses(dx, dy, options.direction, options.threshold)) {
50
+ options.onDismiss();
51
+ }
52
+ }
53
+ function onCancel() {
54
+ start = null;
55
+ }
56
+ node.addEventListener('touchstart', onStart, { passive: true });
57
+ node.addEventListener('touchend', onEnd);
58
+ node.addEventListener('touchcancel', onCancel);
59
+ return {
60
+ update(next) {
61
+ options = next;
62
+ },
63
+ destroy() {
64
+ node.removeEventListener('touchstart', onStart);
65
+ node.removeEventListener('touchend', onEnd);
66
+ node.removeEventListener('touchcancel', onCancel);
67
+ },
68
+ };
69
+ }
@@ -2,6 +2,11 @@
2
2
  /**
3
3
  * Calendar Component
4
4
  * Month view calendar with event indicators and day navigation
5
+ *
6
+ * @deprecated Use `CalendarView`. This component computes days in the
7
+ * browser's zone, prints English-only names, hard-codes game/meeting/event
8
+ * emoji and `/events/y/m/d` links, and has no phone agenda. See the
9
+ * smrt-ui README, "Calendar", for the migration.
5
10
  */
6
11
 
7
12
  import { M } from '../../i18n/strings.ui.js';
@@ -1 +1 @@
1
- {"version":3,"file":"Calendar.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/calendar/Calendar.svelte.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAkB,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGzE,MAAM,WAAW,KAAK;IACpB,2EAA2E;IAC3E,MAAM,CAAC,EAAE,aAAa,EAAE,GAAG,IAAI,CAAC;IAChC,6DAA6D;IAC7D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mEAAmE;IACnE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yDAAyD;IACzD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kDAAkD;IAClD,eAAe,CAAC,EAAE,CAChB,SAAS,EAAE,MAAM,GAAG,MAAM,EAC1B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,KACV,IAAI,CAAC;IACV,2CAA2C;IAC3C,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,KAAK,IAAI,CAAC;IAC1D,4CAA4C;IAC5C,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;CAC3B;AAqQD,QAAA,MAAM,QAAQ,2CAAwC,CAAC;AACvD,KAAK,QAAQ,GAAG,UAAU,CAAC,OAAO,QAAQ,CAAC,CAAC;AAC5C,eAAe,QAAQ,CAAC"}
1
+ {"version":3,"file":"Calendar.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/calendar/Calendar.svelte.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAkB,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGzE,MAAM,WAAW,KAAK;IACpB,2EAA2E;IAC3E,MAAM,CAAC,EAAE,aAAa,EAAE,GAAG,IAAI,CAAC;IAChC,6DAA6D;IAC7D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mEAAmE;IACnE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yDAAyD;IACzD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kDAAkD;IAClD,eAAe,CAAC,EAAE,CAChB,SAAS,EAAE,MAAM,GAAG,MAAM,EAC1B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,KACV,IAAI,CAAC;IACV,2CAA2C;IAC3C,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,KAAK,IAAI,CAAC;IAC1D,4CAA4C;IAC5C,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;CAC3B;AAqQD,QAAA,MAAM,QAAQ,2CAAwC,CAAC;AACvD,KAAK,QAAQ,GAAG,UAAU,CAAC,OAAO,QAAQ,CAAC,CAAC;AAC5C,eAAe,QAAQ,CAAC"}