@michaelthielemann/kestrel 4.1.3 → 5.0.2

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 (342) hide show
  1. package/README.md +26 -201
  2. package/dist/boot.d.ts +45 -0
  3. package/dist/boot.js +284 -0
  4. package/dist/cast.d.ts +3 -0
  5. package/dist/cast.js +6 -0
  6. package/dist/catalogue.d.ts +11 -0
  7. package/dist/catalogue.js +1 -0
  8. package/dist/cli.d.ts +2 -0
  9. package/dist/cli.js +55 -0
  10. package/dist/context.d.ts +62 -0
  11. package/dist/context.js +61 -0
  12. package/dist/dataflow.d.ts +3 -0
  13. package/dist/dataflow.js +65 -0
  14. package/dist/defineConfig.d.ts +162 -0
  15. package/dist/defineConfig.js +42 -0
  16. package/dist/defineContract.d.ts +12 -0
  17. package/dist/defineContract.js +16 -0
  18. package/dist/defineModule.d.ts +75 -0
  19. package/dist/defineModule.js +9 -0
  20. package/dist/definePipeline.d.ts +7 -0
  21. package/dist/definePipeline.js +11 -0
  22. package/dist/errors.d.ts +26 -0
  23. package/dist/errors.js +48 -0
  24. package/dist/index.d.ts +19 -0
  25. package/dist/index.js +14 -0
  26. package/dist/load.d.ts +7 -0
  27. package/dist/load.js +79 -0
  28. package/dist/logger.d.ts +18 -0
  29. package/dist/logger.js +36 -0
  30. package/dist/registry.d.ts +14 -0
  31. package/dist/registry.js +87 -0
  32. package/dist/result.d.ts +19 -0
  33. package/dist/result.js +19 -0
  34. package/dist/runner.d.ts +29 -0
  35. package/dist/runner.js +133 -0
  36. package/dist/schema.d.ts +7 -0
  37. package/dist/schema.js +142 -0
  38. package/dist/sort.d.ts +2 -0
  39. package/dist/sort.js +50 -0
  40. package/dist/testing/runPipeline.d.ts +9 -0
  41. package/dist/testing/runPipeline.js +22 -0
  42. package/dist/triggers/allowlist.d.ts +9 -0
  43. package/dist/triggers/allowlist.js +49 -0
  44. package/dist/triggers/cron.d.ts +10 -0
  45. package/dist/triggers/cron.js +44 -0
  46. package/dist/triggers/cronMatch.d.ts +10 -0
  47. package/dist/triggers/cronMatch.js +68 -0
  48. package/dist/triggers/http.d.ts +59 -0
  49. package/dist/triggers/http.js +300 -0
  50. package/package.json +25 -151
  51. package/NOTICE +0 -12
  52. package/kestrel.config.ts +0 -28
  53. package/layers/access/nuxt.config.ts +0 -5
  54. package/layers/access/server/middleware/00.ip-allowlist.ts +0 -52
  55. package/layers/access/server/middleware/access-guard.ts +0 -32
  56. package/layers/access/server/types.d.ts +0 -7
  57. package/layers/admin/app/components/AdminAccount.vue +0 -212
  58. package/layers/admin/app/components/AdminNav.vue +0 -44
  59. package/layers/admin/app/components/BlockFields.vue +0 -67
  60. package/layers/admin/app/components/BlockPreview.vue +0 -413
  61. package/layers/admin/app/components/BlockTree.vue +0 -433
  62. package/layers/admin/app/components/BlocksBody.vue +0 -163
  63. package/layers/admin/app/components/CollectionDeleteDialog.vue +0 -38
  64. package/layers/admin/app/components/CollectionEditor.vue +0 -324
  65. package/layers/admin/app/components/CollectionList.vue +0 -429
  66. package/layers/admin/app/components/CollectionListBulkBar.vue +0 -62
  67. package/layers/admin/app/components/CollectionListColumnsPanel.vue +0 -37
  68. package/layers/admin/app/components/CollectionListFilterPanel.vue +0 -145
  69. package/layers/admin/app/components/CollectionListPager.vue +0 -70
  70. package/layers/admin/app/components/CollectionListTable.vue +0 -347
  71. package/layers/admin/app/components/EditorStatus.vue +0 -211
  72. package/layers/admin/app/components/EditorUnsupported.vue +0 -28
  73. package/layers/admin/app/components/FieldsBody.vue +0 -23
  74. package/layers/admin/app/components/LocaleBar.vue +0 -107
  75. package/layers/admin/app/components/PageFields.vue +0 -129
  76. package/layers/admin/app/components/PageFieldsPane.vue +0 -39
  77. package/layers/admin/app/components/SeoFields.vue +0 -210
  78. package/layers/admin/app/components/SingletonEditor.vue +0 -93
  79. package/layers/admin/app/composables/useAuth.ts +0 -68
  80. package/layers/admin/app/composables/useBlockTree.ts +0 -95
  81. package/layers/admin/app/composables/useBlocks.ts +0 -7
  82. package/layers/admin/app/composables/useCachedResource.ts +0 -35
  83. package/layers/admin/app/composables/useCollectionOps.ts +0 -102
  84. package/layers/admin/app/composables/useCollections.ts +0 -6
  85. package/layers/admin/app/composables/useContentLocales.ts +0 -23
  86. package/layers/admin/app/composables/useEditForm.ts +0 -402
  87. package/layers/admin/app/composables/useLinkResolver.ts +0 -40
  88. package/layers/admin/app/composables/useListBatchActions.ts +0 -78
  89. package/layers/admin/app/composables/useListColumns.ts +0 -53
  90. package/layers/admin/app/composables/useListFilterDraft.ts +0 -129
  91. package/layers/admin/app/composables/useListRows.ts +0 -75
  92. package/layers/admin/app/composables/useListSelection.ts +0 -28
  93. package/layers/admin/app/composables/useListUrlState.ts +0 -53
  94. package/layers/admin/app/composables/usePendingPublishPoll.ts +0 -15
  95. package/layers/admin/app/composables/usePreviewViewport.ts +0 -30
  96. package/layers/admin/app/composables/usePublishStatus.ts +0 -107
  97. package/layers/admin/app/composables/useRailCollapsed.ts +0 -14
  98. package/layers/admin/app/composables/useTheme.ts +0 -36
  99. package/layers/admin/app/composables/useToolbarPanel.ts +0 -38
  100. package/layers/admin/app/composables/useUnsavedGuard.ts +0 -22
  101. package/layers/admin/app/layouts/admin.vue +0 -266
  102. package/layers/admin/app/middleware/admin-auth.ts +0 -4
  103. package/layers/admin/app/pages/admin/[collection]/[id].vue +0 -203
  104. package/layers/admin/app/pages/admin/[collection]/index.vue +0 -62
  105. package/layers/admin/app/pages/admin/index.vue +0 -118
  106. package/layers/admin/app/pages/admin/login.vue +0 -68
  107. package/layers/admin/app/pages/admin/media.vue +0 -28
  108. package/layers/admin/app/pages/admin/references.vue +0 -146
  109. package/layers/admin/app/plugins/admin-style-guard.client.ts +0 -76
  110. package/layers/admin/app/plugins/reauth.client.ts +0 -20
  111. package/layers/admin/app/utils/admin-style-guard.ts +0 -20
  112. package/layers/admin/app/utils/block-tree.ts +0 -214
  113. package/layers/admin/app/utils/collection-ops.ts +0 -69
  114. package/layers/admin/app/utils/dead-refs.ts +0 -30
  115. package/layers/admin/app/utils/edit-form.ts +0 -267
  116. package/layers/admin/app/utils/editor-expose.ts +0 -36
  117. package/layers/admin/app/utils/editor-form-context.ts +0 -82
  118. package/layers/admin/app/utils/editor-registry.ts +0 -20
  119. package/layers/admin/app/utils/field-keys.ts +0 -11
  120. package/layers/admin/app/utils/list-cell.ts +0 -46
  121. package/layers/admin/app/utils/list-columns.ts +0 -142
  122. package/layers/admin/app/utils/list-query.ts +0 -96
  123. package/layers/admin/app/utils/nav-active.ts +0 -9
  124. package/layers/admin/app/utils/populate-blocks.ts +0 -288
  125. package/layers/admin/app/utils/preview-channel.ts +0 -62
  126. package/layers/admin/app/utils/preview-viewport.ts +0 -80
  127. package/layers/admin/app/utils/reauth.ts +0 -67
  128. package/layers/admin/app/utils/record-title.ts +0 -15
  129. package/layers/admin/app/utils/register-builtin-editors.ts +0 -9
  130. package/layers/admin/app/utils/safe-redirect.ts +0 -12
  131. package/layers/admin/nuxt.config.ts +0 -20
  132. package/layers/auth/nuxt.config.ts +0 -1
  133. package/layers/auth/server/plugins/01.register-auth-pipelines.ts +0 -7
  134. package/layers/collections/nuxt.config.ts +0 -1
  135. package/layers/collections/server/plugins/01.register.ts +0 -29
  136. package/layers/collections/server/plugins/02.register-relation-populate.ts +0 -18
  137. package/layers/core/app/composables/layouts.ts +0 -5
  138. package/layers/core/app/utils/field-factories.ts +0 -129
  139. package/layers/core/app/utils/layouts.ts +0 -38
  140. package/layers/core/modules/auto-discovery/extract-block.ts +0 -179
  141. package/layers/core/modules/auto-discovery/index.ts +0 -112
  142. package/layers/core/modules/auto-discovery/package-registry.ts +0 -19
  143. package/layers/core/modules/auto-discovery/scan.ts +0 -170
  144. package/layers/core/modules/auto-discovery/virtual.d.ts +0 -20
  145. package/layers/core/modules/component-namespace/index.ts +0 -44
  146. package/layers/core/modules/component-namespace/shared.ts +0 -24
  147. package/layers/core/modules/kestrel/app-shell.ts +0 -55
  148. package/layers/core/modules/kestrel/auto-imports.ts +0 -44
  149. package/layers/core/modules/kestrel/index.ts +0 -144
  150. package/layers/core/modules/plugin-order/index.ts +0 -69
  151. package/layers/core/modules/plugin-order/plugin-order.ts +0 -273
  152. package/layers/core/nuxt.config.ts +0 -10
  153. package/layers/core/server/api/[...path].ts +0 -98
  154. package/layers/core/server/plugins/00.config.ts +0 -12
  155. package/layers/core/server/plugins/00.migrate.ts +0 -12
  156. package/layers/core/server/plugins/01.register-introspection-pipeline.ts +0 -5
  157. package/layers/core/server/plugins/01.register-openapi-pipeline.ts +0 -5
  158. package/layers/core/server/plugins/01.register-tooling-pipelines.ts +0 -5
  159. package/layers/core/server/plugins/02.schema-sync.ts +0 -58
  160. package/layers/core/server/plugins/04.outbox-worker.ts +0 -31
  161. package/layers/core/server/plugins/05.reindex-refs.ts +0 -5
  162. package/layers/core/server/routes/__kestrel/dashboard.get.ts +0 -29
  163. package/layers/core/server/tasks/db/migrate-module.ts +0 -83
  164. package/layers/core/server/tasks/db/migrate-revisions.ts +0 -20
  165. package/layers/core/server/tasks/db/migrate.ts +0 -50
  166. package/layers/core/server/utils/server-config.ts +0 -81
  167. package/layers/fields/nuxt.config.ts +0 -1
  168. package/layers/fields/server/plugins/01.register-blocks-pipeline.ts +0 -7
  169. package/layers/fields/server/plugins/01.register-field-populate.ts +0 -11
  170. package/layers/media/app/components/KestrelImg.vue +0 -94
  171. package/layers/media/app/components/MediaConflictDialog.vue +0 -35
  172. package/layers/media/app/components/MediaDeleteDialog.vue +0 -52
  173. package/layers/media/app/components/MediaGrid.vue +0 -113
  174. package/layers/media/app/components/MediaImage.vue +0 -70
  175. package/layers/media/app/components/MediaLibrary.vue +0 -456
  176. package/layers/media/app/components/MediaNewFolderDialog.vue +0 -32
  177. package/layers/media/app/components/MediaPathBar.vue +0 -163
  178. package/layers/media/app/components/MediaPicker.vue +0 -19
  179. package/layers/media/app/components/MediaRenameDialog.vue +0 -32
  180. package/layers/media/app/components/MediaTable.vue +0 -75
  181. package/layers/media/app/components/MediaThumb.vue +0 -25
  182. package/layers/media/app/components/MediaToolbar.vue +0 -90
  183. package/layers/media/app/components/MediaUploadDialog.vue +0 -87
  184. package/layers/media/app/components/MediaViewer.vue +0 -137
  185. package/layers/media/app/components/field/Media.vue +0 -263
  186. package/layers/media/app/composables/useBlurUp.ts +0 -55
  187. package/layers/media/app/composables/useMediaClipboard.ts +0 -14
  188. package/layers/media/app/composables/useMediaContextMenu.ts +0 -56
  189. package/layers/media/app/composables/useMediaDnd.ts +0 -56
  190. package/layers/media/app/composables/useMediaLibrary.ts +0 -152
  191. package/layers/media/app/composables/useMediaOps.ts +0 -50
  192. package/layers/media/app/composables/useMediaRelocate.ts +0 -39
  193. package/layers/media/app/composables/useMediaResolver.ts +0 -50
  194. package/layers/media/app/composables/useMediaUpload.ts +0 -93
  195. package/layers/media/app/composables/useMediaVariant.ts +0 -34
  196. package/layers/media/app/plugins/field-media.client.ts +0 -6
  197. package/layers/media/app/utils/ai-disclosure.ts +0 -19
  198. package/layers/media/app/utils/dnd.ts +0 -69
  199. package/layers/media/app/utils/library.ts +0 -81
  200. package/layers/media/app/utils/ops.ts +0 -60
  201. package/layers/media/app/utils/picture-model.ts +0 -125
  202. package/layers/media/app/utils/thumbhash.ts +0 -19
  203. package/layers/media/app/utils/variant-name.ts +0 -20
  204. package/layers/media/nuxt.config.ts +0 -7
  205. package/layers/media/server/middleware/ondemand-variants.ts +0 -38
  206. package/layers/media/server/plugins/01.register-media-pipelines.ts +0 -7
  207. package/layers/media/server/plugins/02.register-media.ts +0 -22
  208. package/layers/media/server/plugins/04.variant-capture.ts +0 -27
  209. package/layers/media/server/plugins/05.media-cleanup.ts +0 -5
  210. package/layers/media/server/tasks/media/backfill.ts +0 -17
  211. package/layers/public/app/app.vue +0 -5
  212. package/layers/public/app/components/BlockRenderer.vue +0 -56
  213. package/layers/public/app/components/KestrelLink.vue +0 -26
  214. package/layers/public/app/components/KestrelPreviewBridge.vue +0 -133
  215. package/layers/public/app/composables/public-page.ts +0 -24
  216. package/layers/public/app/error.vue +0 -12
  217. package/layers/public/app/layouts/default.vue +0 -5
  218. package/layers/public/app/pages/[...slug].vue +0 -239
  219. package/layers/public/app/pages/__kestrel/preview.vue +0 -46
  220. package/layers/public/app/utils/block-edit-context.ts +0 -16
  221. package/layers/public/app/utils/json-ld.ts +0 -139
  222. package/layers/public/app/utils/link-href.ts +0 -46
  223. package/layers/public/app/utils/page-head.ts +0 -84
  224. package/layers/public/app/utils/page-layout.ts +0 -18
  225. package/layers/public/app/utils/preview-protocol.ts +0 -117
  226. package/layers/public/app/utils/public-route.ts +0 -46
  227. package/layers/public/app/utils/site-head.ts +0 -43
  228. package/layers/public/modules/deploy-output/index.ts +0 -80
  229. package/layers/public/modules/prerender-routes/discover.ts +0 -37
  230. package/layers/public/modules/prerender-routes/index.ts +0 -88
  231. package/layers/public/modules/prerender-routes/routes.ts +0 -16
  232. package/layers/public/modules/prune-media/index.ts +0 -48
  233. package/layers/public/modules/prune-media/prune-media.ts +0 -81
  234. package/layers/public/nuxt.config.ts +0 -14
  235. package/layers/public/server/delivery-live/serve.ts +0 -111
  236. package/layers/public/server/middleware/delivery-live-catchall.ts +0 -7
  237. package/layers/public/server/pipelines/index.ts +0 -23
  238. package/layers/public/server/plugins/00.ensure-snapshot-triggers.ts +0 -14
  239. package/layers/public/server/plugins/01.register-public-pipelines.ts +0 -7
  240. package/layers/public/server/plugins/02.register-links.ts +0 -16
  241. package/layers/public/server/plugins/03.redirects.ts +0 -55
  242. package/layers/public/server/plugins/05.plan-publish.ts +0 -5
  243. package/layers/public/server/plugins/zz.publish.ts +0 -127
  244. package/layers/public/server/routes/llms-full.txt.get.ts +0 -99
  245. package/layers/public/server/routes/llms.txt.get.ts +0 -66
  246. package/layers/public/server/routes/redirects.json.get.ts +0 -58
  247. package/layers/public/server/routes/robots.txt.get.ts +0 -11
  248. package/layers/public/server/routes/sitemap.xml.get.ts +0 -79
  249. package/layers/public/server/tasks/publish/run.ts +0 -32
  250. package/layers/public/server/utils/publish/render-live.ts +0 -26
  251. package/layers/ui/app/assets/scss/_base.scss +0 -16
  252. package/layers/ui/app/assets/scss/_datepicker.scss +0 -82
  253. package/layers/ui/app/assets/scss/_mixins.scss +0 -50
  254. package/layers/ui/app/assets/scss/_reset.scss +0 -65
  255. package/layers/ui/app/assets/scss/_tokens.scss +0 -183
  256. package/layers/ui/app/assets/scss/main.scss +0 -4
  257. package/layers/ui/app/components/field/Boolean.vue +0 -30
  258. package/layers/ui/app/components/field/Choice.vue +0 -73
  259. package/layers/ui/app/components/field/Datetime.vue +0 -98
  260. package/layers/ui/app/components/field/DeadRefNote.vue +0 -28
  261. package/layers/ui/app/components/field/Json.vue +0 -62
  262. package/layers/ui/app/components/field/Layout.vue +0 -138
  263. package/layers/ui/app/components/field/Link.vue +0 -112
  264. package/layers/ui/app/components/field/LinkInternalPicker.vue +0 -95
  265. package/layers/ui/app/components/field/Number.vue +0 -30
  266. package/layers/ui/app/components/field/Relation.vue +0 -49
  267. package/layers/ui/app/components/field/Renderer.vue +0 -30
  268. package/layers/ui/app/components/field/Repeater.vue +0 -460
  269. package/layers/ui/app/components/field/Richtext.vue +0 -26
  270. package/layers/ui/app/components/field/Slug.vue +0 -65
  271. package/layers/ui/app/components/field/Text.vue +0 -35
  272. package/layers/ui/app/components/field/Unsupported.vue +0 -34
  273. package/layers/ui/app/components/ui/Alert.vue +0 -50
  274. package/layers/ui/app/components/ui/Brand.vue +0 -22
  275. package/layers/ui/app/components/ui/Button.vue +0 -116
  276. package/layers/ui/app/components/ui/ButtonGroup.vue +0 -96
  277. package/layers/ui/app/components/ui/Checkbox.vue +0 -53
  278. package/layers/ui/app/components/ui/CheckboxGroup.vue +0 -51
  279. package/layers/ui/app/components/ui/Combobox.vue +0 -345
  280. package/layers/ui/app/components/ui/DatePicker.vue +0 -92
  281. package/layers/ui/app/components/ui/DateRangePicker.vue +0 -100
  282. package/layers/ui/app/components/ui/Dialog.vue +0 -107
  283. package/layers/ui/app/components/ui/EmptyState.vue +0 -46
  284. package/layers/ui/app/components/ui/Field.vue +0 -58
  285. package/layers/ui/app/components/ui/Fieldset.vue +0 -65
  286. package/layers/ui/app/components/ui/Icon.vue +0 -44
  287. package/layers/ui/app/components/ui/Menu.vue +0 -54
  288. package/layers/ui/app/components/ui/NumberInput.vue +0 -175
  289. package/layers/ui/app/components/ui/Richtext.vue +0 -141
  290. package/layers/ui/app/components/ui/RichtextToolbar.vue +0 -251
  291. package/layers/ui/app/components/ui/Select.vue +0 -52
  292. package/layers/ui/app/components/ui/TextInput.vue +0 -111
  293. package/layers/ui/app/components/ui/Textarea.vue +0 -39
  294. package/layers/ui/app/components/ui/TimeInput.vue +0 -18
  295. package/layers/ui/app/components/ui/Toasts.vue +0 -103
  296. package/layers/ui/app/components/ui/Tooltip.vue +0 -45
  297. package/layers/ui/app/composables/useAdminLang.ts +0 -8
  298. package/layers/ui/app/composables/useDragReorder.ts +0 -41
  299. package/layers/ui/app/composables/useEchoGuard.ts +0 -28
  300. package/layers/ui/app/composables/useLinkField.ts +0 -70
  301. package/layers/ui/app/composables/useRecordOptions.ts +0 -84
  302. package/layers/ui/app/composables/useRepeater.ts +0 -99
  303. package/layers/ui/app/composables/useT.ts +0 -32
  304. package/layers/ui/app/composables/useToast.ts +0 -46
  305. package/layers/ui/app/i18n/de.ts +0 -432
  306. package/layers/ui/app/i18n/en.ts +0 -434
  307. package/layers/ui/app/utils/boolean-field.ts +0 -10
  308. package/layers/ui/app/utils/field-component.ts +0 -22
  309. package/layers/ui/app/utils/field-empty.ts +0 -21
  310. package/layers/ui/app/utils/field-registry.ts +0 -36
  311. package/layers/ui/app/utils/humanize.ts +0 -14
  312. package/layers/ui/app/utils/icons.ts +0 -143
  313. package/layers/ui/app/utils/iso-date.ts +0 -18
  314. package/layers/ui/app/utils/localized.ts +0 -13
  315. package/layers/ui/app/utils/record-context.ts +0 -15
  316. package/layers/ui/app/utils/reorder.ts +0 -8
  317. package/layers/ui/app/utils/richtext-preserve-class.ts +0 -31
  318. package/layers/ui/app/utils/useFieldA11y.ts +0 -40
  319. package/layers/ui/nuxt.config.ts +0 -9
  320. package/nuxt.config.ts +0 -108
  321. package/scripts/consumer-template-ci.mjs +0 -160
  322. package/scripts/copy-create-payload.mjs +0 -49
  323. package/scripts/coverage-all.mjs +0 -163
  324. package/scripts/dashboard.mjs +0 -89
  325. package/scripts/hash-password.mjs +0 -29
  326. package/scripts/kestrel.mjs +0 -240
  327. package/scripts/lib/cli.mjs +0 -129
  328. package/scripts/lib/coverage-model.mjs +0 -51
  329. package/scripts/lib/password.mjs +0 -19
  330. package/scripts/lib/scaffold.mjs +0 -205
  331. package/scripts/port-graph.mjs +0 -350
  332. package/scripts/typecheck.mjs +0 -62
  333. package/templates/starter/README.md +0 -65
  334. package/templates/starter/_env.example +0 -17
  335. package/templates/starter/_gitignore +0 -26
  336. package/templates/starter/_package.json +0 -22
  337. package/templates/starter/app/app.vue +0 -7
  338. package/templates/starter/app/blocks/Prose.vue +0 -13
  339. package/templates/starter/app/layouts/default.vue +0 -6
  340. package/templates/starter/nuxt.config.ts +0 -23
  341. package/templates/starter/pnpm-workspace.yaml +0 -8
  342. package/templates/starter/tsconfig.json +0 -3
@@ -1,92 +0,0 @@
1
- <script setup lang="ts">
2
- import { computed } from 'vue'
3
- import UiIcon from './Icon.vue'
4
- import {
5
- DatePickerRoot, DatePickerField, DatePickerInput, DatePickerTrigger,
6
- DatePickerContent, DatePickerArrow, DatePickerCalendar,
7
- DatePickerHeader, DatePickerPrev, DatePickerHeading, DatePickerNext,
8
- DatePickerGrid, DatePickerGridHead, DatePickerGridBody, DatePickerGridRow,
9
- DatePickerHeadCell, DatePickerCell, DatePickerCellTrigger,
10
- } from 'reka-ui'
11
- import type { DateValue } from '@internationalized/date'
12
- import { isoToDate, dateToIso, type IsoPrecision } from '../../utils/iso-date'
13
-
14
- const { t } = useT()
15
-
16
- const props = withDefaults(
17
- defineProps<{
18
- precision?: IsoPrecision
19
- disabled?: boolean
20
- locale?: string
21
- // a11y: the segmented control has no single labelable <input>, so the field name is carried as an
22
- // aria-label on the role=group field, with the error/hint + invalid/required state wired alongside.
23
- ariaLabel?: string
24
- describedby?: string
25
- invalid?: boolean
26
- required?: boolean
27
- }>(),
28
- { precision: 'datetime', disabled: false },
29
- )
30
- const model = defineModel<string | null>()
31
-
32
- // Match the native inputs, which format to the browser locale. Overridable for a future admin-locale setting.
33
- const displayLocale = computed(() => props.locale ?? (typeof navigator !== 'undefined' ? navigator.language : 'en'))
34
- const granularity = computed(() => (props.precision === 'date' ? 'day' : 'minute'))
35
- const value = computed<DateValue | undefined>({
36
- get: () => isoToDate(model.value, props.precision),
37
- set: (v) => { model.value = dateToIso(v) },
38
- })
39
- </script>
40
-
41
- <template>
42
- <DatePickerRoot v-model="value" :granularity="granularity" :disabled="disabled" :locale="displayLocale">
43
- <DatePickerField
44
- v-slot="{ segments }"
45
- class="ui-datepicker__field"
46
- role="group"
47
- :aria-label="ariaLabel"
48
- :aria-describedby="describedby"
49
- :aria-invalid="invalid || undefined"
50
- :aria-required="required || undefined"
51
- >
52
- <DatePickerInput
53
- v-for="item in segments"
54
- :key="item.part"
55
- :part="item.part"
56
- class="ui-datepicker__segment"
57
- >{{ item.value }}</DatePickerInput>
58
- <DatePickerTrigger class="ui-datepicker__trigger" :aria-label="t('datePicker.openCalendar')"><UiIcon name="calendar" :size="16" /></DatePickerTrigger>
59
- </DatePickerField>
60
-
61
- <DatePickerContent class="ui-datepicker__content" :side-offset="6">
62
- <DatePickerArrow class="ui-datepicker__arrow" />
63
- <DatePickerCalendar v-slot="{ weekDays, grid }" class="ui-datepicker__calendar">
64
- <DatePickerHeader class="ui-datepicker__header">
65
- <DatePickerPrev class="ui-datepicker__nav" :aria-label="t('datePicker.previousMonth')">‹</DatePickerPrev>
66
- <DatePickerHeading class="ui-datepicker__heading" />
67
- <DatePickerNext class="ui-datepicker__nav" :aria-label="t('datePicker.nextMonth')">›</DatePickerNext>
68
- </DatePickerHeader>
69
- <DatePickerGrid v-for="month in grid" :key="month.value.toString()" class="ui-datepicker__grid">
70
- <DatePickerGridHead>
71
- <DatePickerGridRow class="ui-datepicker__row">
72
- <DatePickerHeadCell v-for="day in weekDays" :key="day" class="ui-datepicker__weekday">{{ day }}</DatePickerHeadCell>
73
- </DatePickerGridRow>
74
- </DatePickerGridHead>
75
- <DatePickerGridBody>
76
- <DatePickerGridRow v-for="(weekDates, i) in month.rows" :key="`w${i}`" class="ui-datepicker__row">
77
- <DatePickerCell v-for="weekDate in weekDates" :key="weekDate.toString()" :date="weekDate">
78
- <DatePickerCellTrigger :day="weekDate" :month="month.value" class="ui-datepicker__cell" />
79
- </DatePickerCell>
80
- </DatePickerGridRow>
81
- </DatePickerGridBody>
82
- </DatePickerGrid>
83
- </DatePickerCalendar>
84
- </DatePickerContent>
85
- </DatePickerRoot>
86
- </template>
87
-
88
- <style lang="scss">
89
- // Shared, global (non-scoped) datepicker chrome — the calendar is teleported to <body>.
90
- @use '../../assets/scss/datepicker';
91
- @include datepicker.ui-datepicker;
92
- </style>
@@ -1,100 +0,0 @@
1
- <script setup lang="ts">
2
- import { computed } from 'vue'
3
- import UiIcon from './Icon.vue'
4
- import {
5
- DateRangePickerRoot, DateRangePickerField, DateRangePickerInput, DateRangePickerTrigger,
6
- DateRangePickerContent, DateRangePickerArrow, DateRangePickerCalendar,
7
- DateRangePickerHeader, DateRangePickerPrev, DateRangePickerHeading, DateRangePickerNext,
8
- DateRangePickerGrid, DateRangePickerGridHead, DateRangePickerGridBody, DateRangePickerGridRow,
9
- DateRangePickerHeadCell, DateRangePickerCell, DateRangePickerCellTrigger,
10
- } from 'reka-ui'
11
- import type { DateValue } from '@internationalized/date'
12
- import { isoToDate, dateToIso, type IsoPrecision } from '../../utils/iso-date'
13
-
14
- const { t } = useT()
15
- const props = withDefaults(
16
- defineProps<{
17
- precision?: IsoPrecision
18
- disabled?: boolean
19
- locale?: string
20
- // a11y: see DatePicker — the field name rides as an aria-label on the role=group field.
21
- ariaLabel?: string
22
- describedby?: string
23
- invalid?: boolean
24
- required?: boolean
25
- }>(),
26
- { precision: 'datetime', disabled: false },
27
- )
28
- const model = defineModel<{ start: string; end: string } | null>()
29
-
30
- const displayLocale = computed(() => props.locale ?? (typeof navigator !== 'undefined' ? navigator.language : 'en'))
31
- const granularity = computed(() => (props.precision === 'date' ? 'day' : 'minute'))
32
- const value = computed<{ start: DateValue | undefined; end: DateValue | undefined }>({
33
- get: () => ({
34
- start: isoToDate(model.value?.start, props.precision),
35
- end: isoToDate(model.value?.end, props.precision),
36
- }),
37
- set: (v) => {
38
- const start = dateToIso(v.start)
39
- const end = dateToIso(v.end)
40
- // Preserve a partial selection (Reka commits start before end) as empty strings.
41
- model.value = start || end ? { start: start ?? '', end: end ?? '' } : null
42
- },
43
- })
44
- </script>
45
-
46
- <template>
47
- <DateRangePickerRoot v-model="value" :granularity="granularity" :disabled="disabled" :locale="displayLocale">
48
- <DateRangePickerField
49
- v-slot="{ segments }"
50
- class="ui-datepicker__field"
51
- role="group"
52
- :aria-label="ariaLabel"
53
- :aria-describedby="describedby"
54
- :aria-invalid="invalid || undefined"
55
- :aria-required="required || undefined"
56
- >
57
- <template v-for="item in segments.start" :key="`s-${item.part}`">
58
- <DateRangePickerInput :part="item.part" type="start" class="ui-datepicker__segment">{{ item.value }}</DateRangePickerInput>
59
- </template>
60
- <span class="ui-datepicker__sep" aria-hidden="true">–</span>
61
- <template v-for="item in segments.end" :key="`e-${item.part}`">
62
- <DateRangePickerInput :part="item.part" type="end" class="ui-datepicker__segment">{{ item.value }}</DateRangePickerInput>
63
- </template>
64
- <DateRangePickerTrigger class="ui-datepicker__trigger" :aria-label="t('dateRange.openCalendar')"><UiIcon name="calendar" :size="16" /></DateRangePickerTrigger>
65
- </DateRangePickerField>
66
-
67
- <DateRangePickerContent class="ui-datepicker__content" :side-offset="6">
68
- <DateRangePickerArrow class="ui-datepicker__arrow" />
69
- <DateRangePickerCalendar v-slot="{ weekDays, grid }" class="ui-datepicker__calendar">
70
- <DateRangePickerHeader class="ui-datepicker__header">
71
- <DateRangePickerPrev class="ui-datepicker__nav" :aria-label="t('dateRange.previousMonth')">‹</DateRangePickerPrev>
72
- <DateRangePickerHeading class="ui-datepicker__heading" />
73
- <DateRangePickerNext class="ui-datepicker__nav" :aria-label="t('dateRange.nextMonth')">›</DateRangePickerNext>
74
- </DateRangePickerHeader>
75
- <DateRangePickerGrid v-for="month in grid" :key="month.value.toString()" class="ui-datepicker__grid">
76
- <DateRangePickerGridHead>
77
- <DateRangePickerGridRow class="ui-datepicker__row">
78
- <DateRangePickerHeadCell v-for="day in weekDays" :key="day" class="ui-datepicker__weekday">{{ day }}</DateRangePickerHeadCell>
79
- </DateRangePickerGridRow>
80
- </DateRangePickerGridHead>
81
- <DateRangePickerGridBody>
82
- <DateRangePickerGridRow v-for="(weekDates, i) in month.rows" :key="`w${i}`" class="ui-datepicker__row">
83
- <DateRangePickerCell v-for="weekDate in weekDates" :key="weekDate.toString()" :date="weekDate">
84
- <DateRangePickerCellTrigger :day="weekDate" :month="month.value" class="ui-datepicker__cell" />
85
- </DateRangePickerCell>
86
- </DateRangePickerGridRow>
87
- </DateRangePickerGridBody>
88
- </DateRangePickerGrid>
89
- </DateRangePickerCalendar>
90
- </DateRangePickerContent>
91
- </DateRangePickerRoot>
92
- </template>
93
-
94
- <style lang="scss">
95
- // Shares the global (non-scoped) datepicker chrome — the calendar is teleported to <body>, so a
96
- // `scoped` block would silently drop these styles. Included explicitly rather than borrowing them
97
- // from DatePicker being present on the page.
98
- @use '../../assets/scss/datepicker';
99
- @include datepicker.ui-datepicker;
100
- </style>
@@ -1,107 +0,0 @@
1
- <script setup lang="ts">
2
- import { DialogRoot, DialogOverlay, DialogContent, DialogTitle, DialogDescription, DialogClose } from 'reka-ui'
3
- import UiIcon from './Icon.vue'
4
-
5
- const props = defineProps<{ open: boolean; title: string; description?: string; size?: 'md' | 'lg' | 'xl' }>()
6
- const emit = defineEmits<{ 'update:open': [boolean] }>()
7
- const { t } = useT()
8
-
9
- // Rendering in place means dialog inputs are form-associated with any ancestor form (the record
10
- // editor): Enter in a text-entry input would implicitly submit it — saving the half-edited record
11
- // from inside a picker. Swallow that default here; Enter on buttons (and click-like inputs) keeps
12
- // its activation behavior, and a dialog can still opt into Enter via its own @keydown.enter handler.
13
- const CLICK_INPUT_TYPES = new Set(['button', 'submit', 'reset', 'checkbox', 'radio', 'file'])
14
- function onKeydown(e: KeyboardEvent) {
15
- if (e.key !== 'Enter' || e.isComposing) return
16
- const el = e.target
17
- if (el instanceof HTMLInputElement && !CLICK_INPUT_TYPES.has(el.type)) e.preventDefault()
18
- }
19
- </script>
20
-
21
- <template>
22
- <!-- Reka Dialog, rendered IN PLACE (no DialogPortal). Reka provides the focus trap, scroll lock,
23
- Escape/outside-click dismissal, ARIA wiring and focus restore; rendering in place (rather than
24
- teleporting) keeps the dialog synchronously DOM-testable and needs no force-mount hack. The
25
- admin's flat layouts don't need a portal to escape clipping — add DialogPortal here if that
26
- ever changes. -->
27
- <DialogRoot :open="props.open" @update:open="emit('update:open', $event)">
28
- <DialogOverlay class="ui-dialog__overlay" />
29
- <DialogContent
30
- class="ui-dialog__content"
31
- :class="`ui-dialog__content--${size ?? 'md'}`"
32
- v-bind="description ? {} : { 'aria-describedby': undefined }"
33
- @keydown="onKeydown"
34
- >
35
- <header class="ui-dialog__header">
36
- <DialogTitle class="ui-dialog__title">{{ title }}</DialogTitle>
37
- <DialogClose data-test="dialog-close" class="ui-dialog__close" :aria-label="t('common.close')"><UiIcon name="x" :size="16" /></DialogClose>
38
- </header>
39
- <DialogDescription v-if="description" class="ui-dialog__desc">{{ description }}</DialogDescription>
40
- <div class="ui-dialog__body"><slot /></div>
41
- <footer v-if="$slots.footer" class="ui-dialog__footer"><slot name="footer" /></footer>
42
- </DialogContent>
43
- </DialogRoot>
44
- </template>
45
-
46
- <style lang="scss" scoped>
47
- .ui-dialog__overlay {
48
- position: fixed;
49
- inset: 0;
50
- z-index: var(--z-dialog);
51
- background: var(--color-scrim);
52
- }
53
- .ui-dialog__content {
54
- position: fixed;
55
- top: 50%;
56
- left: 50%;
57
- transform: translate(-50%, -50%);
58
- z-index: var(--z-dialog);
59
- max-height: calc(100svh - var(--space-6));
60
- overflow: auto;
61
- background: var(--color-surface);
62
- border: 1px solid var(--color-border);
63
- border-radius: var(--radius-lg);
64
- box-shadow: var(--shadow-lg);
65
- padding: var(--space-4);
66
-
67
- &:focus,
68
- &:focus-visible { outline: none; }
69
- }
70
- .ui-dialog__content--md { width: min(32rem, calc(100vw - 2rem)); }
71
- .ui-dialog__content--lg { width: min(64rem, calc(100vw - 2rem)); }
72
- .ui-dialog__content--xl { width: min(80rem, calc(100vw - 2rem)); }
73
- .ui-dialog__header {
74
- display: flex;
75
- align-items: center;
76
- justify-content: space-between;
77
- gap: var(--space-2);
78
- margin-bottom: var(--space-3);
79
- }
80
- .ui-dialog__title {
81
- font-size: var(--text-lg);
82
- font-weight: var(--weight-bold);
83
- margin: 0;
84
- }
85
- .ui-dialog__desc {
86
- margin: 0 0 var(--space-3);
87
- color: var(--color-text-muted);
88
- }
89
- .ui-dialog__close {
90
- background: none;
91
- border: 0;
92
- font-size: var(--text-xl);
93
- line-height: 1;
94
- cursor: pointer;
95
- color: var(--color-text-muted);
96
- border-radius: var(--radius-sm);
97
-
98
- &:hover { background: var(--color-hover); color: var(--color-text); }
99
- &:focus-visible { outline: 2px solid var(--color-focus); outline-offset: -2px; }
100
- }
101
- .ui-dialog__footer {
102
- display: flex;
103
- justify-content: flex-end;
104
- gap: var(--space-2);
105
- margin-top: var(--space-4);
106
- }
107
- </style>
@@ -1,46 +0,0 @@
1
- <script setup lang="ts">
2
- import type { IconName } from '../../utils/icons'
3
-
4
- defineProps<{ icon?: IconName; title: string; description?: string }>()
5
- </script>
6
-
7
- <template>
8
- <div class="ui-empty">
9
- <KestrelUiIcon v-if="icon" :name="icon" size="1.5rem" class="ui-empty__icon" />
10
- <p class="ui-empty__title">{{ title }}</p>
11
- <p v-if="description" class="ui-empty__desc">{{ description }}</p>
12
- <div v-if="$slots.action" class="ui-empty__action"><slot name="action" /></div>
13
- </div>
14
- </template>
15
-
16
- <style lang="scss">
17
- .ui-empty {
18
- display: flex;
19
- flex-direction: column;
20
- align-items: center;
21
- justify-content: center;
22
- gap: var(--space-2);
23
- padding: var(--space-6) var(--space-4);
24
- text-align: center;
25
- border: 1px solid var(--color-border);
26
- border-radius: var(--radius-lg);
27
- background: var(--color-surface);
28
-
29
- &__icon {
30
- margin-bottom: var(--space-1);
31
- color: var(--color-text-subtle);
32
- }
33
- &__title {
34
- font-weight: var(--weight-medium);
35
- color: var(--color-text);
36
- }
37
- &__desc {
38
- font-size: var(--text-sm);
39
- color: var(--color-text-muted);
40
- max-width: 40ch;
41
- }
42
- &__action {
43
- margin-top: var(--space-2);
44
- }
45
- }
46
- </style>
@@ -1,58 +0,0 @@
1
- <script setup lang="ts">
2
- import { computed } from 'vue'
3
- import { useFieldA11y } from '../../utils/useFieldA11y'
4
-
5
- const props = defineProps<{
6
- label?: string
7
- hint?: string
8
- error?: string | null
9
- required?: boolean
10
- id?: string
11
- }>()
12
-
13
- const { fieldId, hintId, errId, describedby, ariaInvalid } = useFieldA11y(props)
14
-
15
- const slotProps = computed(() => ({
16
- id: fieldId.value,
17
- 'aria-invalid': ariaInvalid.value,
18
- 'aria-describedby': describedby.value,
19
- required: props.required,
20
- }))
21
- </script>
22
-
23
- <template>
24
- <div class="ui-field" :data-invalid="error ? 'true' : undefined">
25
- <!-- eslint-disable-next-line vuejs-accessibility/label-has-for -- the matching control is the default slot's content, invisible to static analysis across the slot boundary -->
26
- <label v-if="label" :for="fieldId" class="ui-field__label">
27
- {{ label }}<span v-if="required" class="ui-field__required" aria-hidden="true">*</span>
28
- </label>
29
- <slot v-bind="slotProps" />
30
- <p v-if="hint" :id="hintId" class="ui-field__hint">{{ hint }}</p>
31
- <p v-if="error" :id="errId" class="ui-field__error" role="alert">{{ error }}</p>
32
- </div>
33
- </template>
34
-
35
- <style lang="scss">
36
- .ui-field {
37
- display: flex;
38
- flex-direction: column;
39
- gap: var(--space-1);
40
-
41
- &__label {
42
- font-size: var(--text-sm);
43
- font-weight: var(--weight-medium);
44
- }
45
- &__required {
46
- color: var(--color-danger);
47
- margin-inline-start: var(--space-1);
48
- }
49
- &__hint {
50
- font-size: var(--text-sm);
51
- color: var(--color-text-muted);
52
- }
53
- &__error {
54
- font-size: var(--text-sm);
55
- color: var(--color-danger);
56
- }
57
- }
58
- </style>
@@ -1,65 +0,0 @@
1
- <script setup lang="ts">
2
- import { computed } from 'vue'
3
- import { useFieldA11y } from '../../utils/useFieldA11y'
4
-
5
- const props = defineProps<{
6
- label?: string
7
- hint?: string
8
- error?: string | null
9
- required?: boolean
10
- id?: string
11
- }>()
12
-
13
- // No `fieldId`: a group has no single control to point a label `for` at (see below).
14
- const { hintId, errId, describedby, ariaInvalid, ariaRequired } = useFieldA11y(props)
15
-
16
- // No `id` — a group has no single control to point a label `for` at; the
17
- // <legend> labels the group natively. Consumers spread these onto the group root.
18
- const slotProps = computed(() => ({
19
- 'aria-invalid': ariaInvalid.value,
20
- 'aria-describedby': describedby.value,
21
- 'aria-required': ariaRequired.value,
22
- required: props.required,
23
- }))
24
- </script>
25
-
26
- <template>
27
- <fieldset class="ui-fieldset" :data-invalid="error ? 'true' : undefined">
28
- <legend v-if="label" class="ui-fieldset__legend">
29
- {{ label }}<span v-if="required" class="ui-fieldset__required" aria-hidden="true">*</span>
30
- </legend>
31
- <slot v-bind="slotProps" />
32
- <p v-if="hint" :id="hintId" class="ui-fieldset__hint">{{ hint }}</p>
33
- <p v-if="error" :id="errId" class="ui-fieldset__error" role="alert">{{ error }}</p>
34
- </fieldset>
35
- </template>
36
-
37
- <style lang="scss">
38
- .ui-fieldset {
39
- display: flex;
40
- flex-direction: column;
41
- gap: var(--space-1);
42
- min-inline-size: 0;
43
- margin: 0;
44
- padding: 0;
45
- border: 0;
46
-
47
- &__legend {
48
- padding: 0;
49
- font-size: var(--text-sm);
50
- font-weight: var(--weight-medium);
51
- }
52
- &__required {
53
- color: var(--color-danger);
54
- margin-inline-start: var(--space-1);
55
- }
56
- &__hint {
57
- font-size: var(--text-sm);
58
- color: var(--color-text-muted);
59
- }
60
- &__error {
61
- font-size: var(--text-sm);
62
- color: var(--color-danger);
63
- }
64
- }
65
- </style>
@@ -1,44 +0,0 @@
1
- <script setup lang="ts">
2
- import { computed, useAttrs } from 'vue'
3
- import { isIconName, resolveIconBody, type IconSpec } from '../../utils/icons'
4
-
5
- const props = withDefaults(defineProps<{ name: IconSpec; size?: string | number; label?: string }>(), { size: '1em' })
6
- const attrs = useAttrs()
7
-
8
- const dim = computed(() => (typeof props.size === 'number' ? `${props.size}px` : props.size))
9
- const body = computed(() => resolveIconBody(props.name))
10
- // `data-icon` exposes the resolved registry key (or `custom` for raw SVG) for styling/tests.
11
- const dataIcon = computed(() => (isIconName(props.name) ? props.name : 'custom'))
12
- // A caller can give the icon an accessible name via fallthrough attrs (role/aria-label) instead of the
13
- // `label` prop — either way it must not stay `aria-hidden`, or the name is invisible to AT.
14
- const hasA11yName = computed(() => props.label != null || attrs.role != null || attrs['aria-label'] != null || attrs['aria-labelledby'] != null)
15
- </script>
16
-
17
- <template>
18
- <!-- v-html body is either a static registry icon or developer-supplied SVG allowlist-sanitised by
19
- resolveIconBody → sanitizeIconSvg (no script-capable elements/attributes survive). -->
20
- <!-- eslint-disable-next-line vue/no-v-html -- sanitized via resolveIconBody -> sanitizeIconSvg (allowlist-only tags/attrs) -->
21
- <svg v-html="body"
22
- class="ui-icon"
23
- :data-icon="dataIcon"
24
- :width="dim"
25
- :height="dim"
26
- viewBox="0 0 24 24"
27
- fill="none"
28
- stroke="currentColor"
29
- stroke-width="2"
30
- stroke-linecap="round"
31
- stroke-linejoin="round"
32
- :role="label ? 'img' : undefined"
33
- :aria-label="label || undefined"
34
- :aria-hidden="hasA11yName ? undefined : 'true'"
35
- />
36
- </template>
37
-
38
- <style lang="scss">
39
- .ui-icon {
40
- display: inline-block;
41
- vertical-align: middle;
42
- flex-shrink: 0;
43
- }
44
- </style>
@@ -1,54 +0,0 @@
1
- <script setup lang="ts">
2
- import { ContextMenuRoot, ContextMenuTrigger, ContextMenuPortal, ContextMenuContent, ContextMenuItem } from 'reka-ui'
3
-
4
- export interface MenuItem { label: string; value: string; danger?: boolean; disabled?: boolean }
5
- defineProps<{ items: MenuItem[] }>()
6
- const emit = defineEmits<{ select: [string] }>()
7
- </script>
8
-
9
- <template>
10
- <ContextMenuRoot>
11
- <ContextMenuTrigger as-child>
12
- <slot />
13
- </ContextMenuTrigger>
14
- <ContextMenuPortal>
15
- <ContextMenuContent class="ui-menu" :collision-padding="8">
16
- <ContextMenuItem
17
- v-for="it in items"
18
- :key="it.value"
19
- :disabled="it.disabled"
20
- class="ui-menu__item"
21
- :class="{ 'ui-menu__item--danger': it.danger }"
22
- @select="emit('select', it.value)"
23
- >{{ it.label }}</ContextMenuItem>
24
- </ContextMenuContent>
25
- </ContextMenuPortal>
26
- </ContextMenuRoot>
27
- </template>
28
-
29
- <style lang="scss">
30
- // Not scoped: Reka teleports the menu content to <body>, where a scoped data-v selector would no
31
- // longer match — so the surface background/border silently dropped out (Image: a backgroundless,
32
- // hard-to-read context menu). The other portaled overlays (Combobox/DatePicker/Select) are global too.
33
- .ui-menu {
34
- min-width: 11rem;
35
- background: var(--color-surface);
36
- border: 1px solid var(--color-border);
37
- border-radius: var(--radius-md);
38
- box-shadow: var(--shadow-md);
39
- padding: var(--space-1);
40
- z-index: var(--z-dropdown);
41
- }
42
- .ui-menu__item {
43
- display: block;
44
- width: 100%;
45
- padding: var(--space-1) var(--space-3);
46
- border-radius: var(--radius-sm);
47
- font-size: var(--text-sm);
48
- cursor: pointer;
49
- user-select: none;
50
- &[data-highlighted] { background: var(--color-hover); outline: none; }
51
- &[data-disabled] { color: var(--color-text-muted); cursor: default; }
52
- &--danger { color: var(--color-danger); }
53
- }
54
- </style>