@dskripchenko/laravel-admin 1.26.0 → 1.27.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.
- package/dist/api/bootstrap.d.ts +18 -17
- package/dist/api/client.d.ts +12 -11
- package/dist/api/envelope.d.ts +3 -3
- package/dist/api/errors.d.ts +7 -7
- package/dist/components/ScreenPage.vue.d.ts +1 -1
- package/dist/components/auth/LoginForm.vue.d.ts +2 -2
- package/dist/components/auth/LoginPage.vue.d.ts +5 -5
- package/dist/components/auth/index.d.ts +1 -1
- package/dist/components/createComponentRegistry.d.ts +6 -5
- package/dist/components/dashboard/ChartWidget.vue.d.ts +2 -2
- package/dist/components/dashboard/GaugeWidget.vue.d.ts +2 -2
- package/dist/components/dashboard/HeatmapWidget.vue.d.ts +2 -2
- package/dist/components/dashboard/IframeWidget.vue.d.ts +4 -3
- package/dist/components/dashboard/RecentTableWidget.vue.d.ts +4 -3
- package/dist/components/dashboard/StatWidget.vue.d.ts +8 -7
- package/dist/components/dashboard/TableWidget.vue.d.ts +3 -2
- package/dist/components/dashboard/WidgetConfigDialog.vue.d.ts +4 -4
- package/dist/components/dashboard/WidgetRenderer.vue.d.ts +1 -1
- package/dist/components/dashboard/builtin.d.ts +5 -4
- package/dist/components/dashboard/index.d.ts +1 -1
- package/dist/components/dashboard/registry.d.ts +5 -5
- package/dist/components/fields/BuilderField.vue.d.ts +1 -1
- package/dist/components/fields/DateField.vue.d.ts +3 -3
- package/dist/components/fields/GeneratedField.vue.d.ts +3 -3
- package/dist/components/fields/SelectField.vue.d.ts +3 -2
- package/dist/components/fields/TagsField.vue.d.ts +6 -6
- package/dist/components/fields/TranslatableField.vue.d.ts +2 -2
- package/dist/components/fields/WysiwygField.vue.d.ts +2 -2
- package/dist/components/fields/wysiwyg/quill/QuillField.vue.d.ts +3 -3
- package/dist/components/fields/wysiwyg/quill/index.d.ts +3 -3
- package/dist/components/fields/wysiwyg/tinymce/TinymceField.vue.d.ts +6 -5
- package/dist/components/fields/wysiwyg/tinymce/index.d.ts +3 -3
- package/dist/components/import/ImportWizardPage.vue.d.ts +9 -9
- package/dist/components/infolist/BadgeEntry.vue.d.ts +4 -4
- package/dist/components/infolist/InfolistRenderer.vue.d.ts +1 -1
- package/dist/components/infolist/TextEntry.vue.d.ts +3 -3
- package/dist/components/infolist/builtin.d.ts +6 -6
- package/dist/components/infolist/index.d.ts +1 -1
- package/dist/components/infolist/recordContext.d.ts +3 -3
- package/dist/components/infolist/registry.d.ts +7 -7
- package/dist/components/layouts/ColumnsLayout.vue.d.ts +2 -2
- package/dist/components/layouts/RowsLayout.vue.d.ts +1 -1
- package/dist/components/layouts/TabsLayout.vue.d.ts +12 -11
- package/dist/components/notifications/index.d.ts +5 -5
- package/dist/components/profile/ProfilePage.vue.d.ts +3 -3
- package/dist/components/profile/TwoFactorSetup.vue.d.ts +1 -1
- package/dist/components/render/builtin.d.ts +2 -2
- package/dist/components/render/formState.d.ts +18 -15
- package/dist/components/render/index.d.ts +1 -1
- package/dist/components/render/registry.d.ts +9 -9
- package/dist/components/resource/AdminFilterToolbar.vue.d.ts +3 -3
- package/dist/components/resource/AuditTimeline.vue.d.ts +4 -4
- package/dist/components/resource/FilterEditor.d.ts +4 -3
- package/dist/components/resource/InlineEditCell.vue.d.ts +4 -4
- package/dist/components/resource/ResourceFormPage.vue.d.ts +4 -4
- package/dist/components/resource/ResourceIndexPage.vue.d.ts +5 -5
- package/dist/components/resource/ResourceViewPage.vue.d.ts +7 -7
- package/dist/components/resource/cellFormat.d.ts +14 -12
- package/dist/components/resource/index.d.ts +1 -1
- package/dist/components/resource/statusLabel.d.ts +14 -13
- package/dist/components/shell/AdminShell.vue.d.ts +7 -7
- package/dist/components/shell/AdminSidebar.vue.d.ts +7 -7
- package/dist/components/shell/AdminSidebarNode.vue.d.ts +2 -2
- package/dist/components/shell/AdminTopBar.vue.d.ts +2 -2
- package/dist/components/shell/BrandLogo.vue.d.ts +3 -3
- package/dist/components/shell/iconRegistry.d.ts +10 -9
- package/dist/components/shell/index.d.ts +2 -2
- package/dist/composables/useAppReady.d.ts +12 -11
- package/dist/composables/useBrand.d.ts +4 -3
- package/dist/createAdminApp.d.ts +34 -33
- package/dist/formState-DeRZBwWH.js.map +1 -1
- package/dist/formState-yUTD6wAR.cjs.map +1 -1
- package/dist/i18n-BCRWWhHE.cjs.map +1 -1
- package/dist/i18n-DXndsABq.js.map +1 -1
- package/dist/index.cjs +4 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +12 -10
- package/dist/index.mjs +3789 -3774
- package/dist/index.mjs.map +1 -1
- package/dist/quill.cjs.map +1 -1
- package/dist/quill.mjs.map +1 -1
- package/dist/router/builder.d.ts +15 -14
- package/dist/router/guards.d.ts +28 -27
- package/dist/router/index.d.ts +20 -18
- package/dist/stores/auth.d.ts +10 -10
- package/dist/stores/dashboard.d.ts +33 -32
- package/dist/stores/i18n.d.ts +5 -4
- package/dist/stores/index.d.ts +2 -2
- package/dist/stores/locale.d.ts +7 -6
- package/dist/stores/manifest.d.ts +64 -61
- package/dist/stores/menu.d.ts +58 -57
- package/dist/stores/navigation.d.ts +5 -5
- package/dist/stores/notifications.d.ts +5 -4
- package/dist/stores/registry.d.ts +7 -7
- package/dist/stores/resourceForm.d.ts +12 -10
- package/dist/stores/resourceIndex.d.ts +12 -10
- package/dist/stores/screen.d.ts +15 -12
- package/dist/stores/theme.d.ts +4 -3
- package/dist/stores/toast.d.ts +3 -2
- package/dist/style.css +1 -1
- package/dist/tinymce.cjs.map +1 -1
- package/dist/tinymce.mjs.map +1 -1
- package/dist/types/bootstrap.d.ts +9 -9
- package/package.json +1 -1
package/dist/quill.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"quill.cjs","sources":["../resources/ts/components/fields/wysiwyg/quill/QuillField.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * QuillField — wrapper
|
|
1
|
+
{"version":3,"file":"quill.cjs","sources":["../resources/ts/components/fields/wysiwyg/quill/QuillField.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * QuillField — the wrapper around @vueup/vue-quill, for the admin's field\n * registry.\n *\n * Wiring it into a host project, after createAdminApp:\n *\n * import { registerField } from '@dskripchenko/laravel-admin'\n * import { QuillField } from '@dskripchenko/laravel-admin/quill'\n * // the peer dependencies `@vueup/vue-quill` and `quill` must be installed\n * registerField('wysiwyg', QuillField)\n *\n * When a peer dependency is missing at build time, vite's bundler fails with\n * \"Failed to resolve\". That is expected: the /quill subpath is deliberately\n * optional, so import it only once the peers are in place.\n */\nimport { computed } from 'vue'\n// @ts-expect-error — an optional peer dependency, whose types exist only once\n// the host has installed `@vueup/vue-quill`. The core's vite build marks the\n// package as external, so the import survives to runtime and resolves in the\n// host.\nimport { QuillEditor, type Delta } from '@vueup/vue-quill'\nimport { useFormState } from '../../../render/formState'\n\n/*\n * The host imports the CSS themes — snow and bubble — in its own entry. The\n * core does not, or its vite build would fail resolving the paths inside\n * node_modules: the peer dependency is optional and is not installed in the\n * core.\n *\n * // demo/resources/js/admin.js\n * import '@vueup/vue-quill/dist/vue-quill.snow.css'\n * import '@vueup/vue-quill/dist/vue-quill.bubble.css'\n */\n\ninterface Props {\n name: string\n label?: string | null\n help?: string | null\n required?: boolean\n placeholder?: string | null\n /**\n * Quill's toolbar configuration. It takes `'essential' | 'minimal' | 'full'`\n * or a raw array of groups.\n */\n toolbar?: string | unknown[]\n /** 'snow' — the default toolbar; 'bubble' — inline; 'core' — no UI at all. */\n theme?: 'snow' | 'bubble' | 'core'\n disabled?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n label: null,\n help: null,\n placeholder: null,\n required: false,\n toolbar: 'full',\n theme: 'snow',\n disabled: false,\n})\n\nconst form = useFormState()\nconst value = computed<string>(() => (form.getField(props.name) as string | undefined) ?? '')\nconst errorMsg = computed<string | undefined>(() => form.errors[props.name]?.[0])\n\nfunction onUpdate(next: string | Delta): void {\n // QuillEditor's v-model:content gives an HTML string by default, since\n // contentType is 'html'. A Delta object is supported too, just in case.\n if (typeof next === 'string') {\n form.setField(props.name, next)\n } else {\n form.setField(props.name, JSON.stringify(next))\n }\n}\n</script>\n\n<template>\n <div :class=\"['admin-field', { 'admin-field--invalid': errorMsg !== undefined }]\">\n <label v-if=\"label\" class=\"admin-field__label\">\n <span>{{ label }}</span>\n <span v-if=\"required\" class=\"admin-field__required\" aria-hidden=\"true\">*</span>\n </label>\n <div class=\"admin-field__control admin-field__control--quill\">\n <QuillEditor\n :content=\"value\"\n :toolbar=\"toolbar\"\n :theme=\"theme\"\n :placeholder=\"placeholder ?? undefined\"\n :read-only=\"disabled\"\n content-type=\"html\"\n @update:content=\"onUpdate\"\n />\n </div>\n <p v-if=\"errorMsg\" class=\"admin-field__error\">{{ errorMsg }}</p>\n <p v-else-if=\"help\" class=\"admin-field__help\">{{ help }}</p>\n </div>\n</template>\n\n<style>\n/* Quill takes its min-height from CSS, which removes the jolt on initialization. */\n.admin-field__control--quill .ql-container {\n min-height: 200px;\n font-size: 14px;\n font-family: inherit;\n}\n.admin-field__control--quill .ql-toolbar.ql-snow,\n.admin-field__control--quill .ql-container.ql-snow {\n border-color: var(--uid-border-subtle);\n}\n.admin-field__control--quill .ql-toolbar.ql-snow {\n border-radius: var(--uid-radius-md) var(--uid-radius-md) 0 0;\n background: var(--uid-surface-base);\n}\n.admin-field__control--quill .ql-container.ql-snow {\n border-radius: 0 0 var(--uid-radius-md) var(--uid-radius-md);\n background: var(--uid-surface-raised);\n color: var(--uid-text-primary);\n}\n</style>\n"],"names":["props","__props","form","useFormState","value","computed","errorMsg","onUpdate","next","_createElementBlock","_normalizeClass","_openBlock","_hoisted_1","_createElementVNode","_hoisted_2","_hoisted_3","_createVNode","_unref","QuillEditor","_hoisted_4","_toDisplayString","_hoisted_5"],"mappings":"+oBAmDA,MAAMA,EAAQC,EAURC,EAAOC,EAAAA,aAAA,EACPC,EAAQC,EAAAA,SAAiB,IAAOH,EAAK,SAASF,EAAM,IAAI,GAA4B,EAAE,EACtFM,EAAWD,EAAAA,SAA6B,IAAMH,EAAK,OAAOF,EAAM,IAAI,IAAI,CAAC,CAAC,EAEhF,SAASO,EAASC,EAA4B,CAGxC,OAAOA,GAAS,SAClBN,EAAK,SAASF,EAAM,KAAMQ,CAAI,EAE9BN,EAAK,SAASF,EAAM,KAAM,KAAK,UAAUQ,CAAI,CAAC,CAElD,6BAIEC,EAAAA,mBAkBM,MAAA,CAlBA,MAAKC,EAAAA,eAAA,CAAA,cAAA,CAAA,uBAA4CJ,EAAA,QAAa,OAAS,CAAA,CAAA,GAC9DL,EAAA,OAAbU,EAAAA,UAAA,EAAAF,EAAAA,mBAGQ,QAHRG,EAGQ,CAFNC,EAAAA,mBAAwB,8BAAfZ,EAAA,KAAK,EAAA,CAAA,EACFA,EAAA,wBAAZQ,qBAA+E,OAA/EK,EAAuE,GAAC,8DAE1ED,EAAAA,mBAUM,MAVNE,EAUM,CATJC,cAQEC,EAAAA,MAAAC,EAAAA,WAAA,EAAA,CAPC,QAASd,EAAA,MACT,QAASH,EAAA,QACT,MAAOA,EAAA,MACP,YAAaA,EAAA,aAAe,OAC5B,YAAWA,EAAA,SACZ,eAAa,OACZ,mBAAgBM,CAAA,oEAGZD,EAAA,qBAATG,EAAAA,mBAAgE,IAAhEU,EAAgEC,EAAAA,gBAAfd,EAAA,KAAQ,EAAA,CAAA,GAC3CL,EAAA,oBAAdQ,EAAAA,mBAA4D,IAA5DY,EAA4DD,EAAAA,gBAAXnB,EAAA,IAAI,EAAA,CAAA"}
|
package/dist/quill.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"quill.mjs","sources":["../resources/ts/components/fields/wysiwyg/quill/QuillField.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * QuillField — wrapper
|
|
1
|
+
{"version":3,"file":"quill.mjs","sources":["../resources/ts/components/fields/wysiwyg/quill/QuillField.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * QuillField — the wrapper around @vueup/vue-quill, for the admin's field\n * registry.\n *\n * Wiring it into a host project, after createAdminApp:\n *\n * import { registerField } from '@dskripchenko/laravel-admin'\n * import { QuillField } from '@dskripchenko/laravel-admin/quill'\n * // the peer dependencies `@vueup/vue-quill` and `quill` must be installed\n * registerField('wysiwyg', QuillField)\n *\n * When a peer dependency is missing at build time, vite's bundler fails with\n * \"Failed to resolve\". That is expected: the /quill subpath is deliberately\n * optional, so import it only once the peers are in place.\n */\nimport { computed } from 'vue'\n// @ts-expect-error — an optional peer dependency, whose types exist only once\n// the host has installed `@vueup/vue-quill`. The core's vite build marks the\n// package as external, so the import survives to runtime and resolves in the\n// host.\nimport { QuillEditor, type Delta } from '@vueup/vue-quill'\nimport { useFormState } from '../../../render/formState'\n\n/*\n * The host imports the CSS themes — snow and bubble — in its own entry. The\n * core does not, or its vite build would fail resolving the paths inside\n * node_modules: the peer dependency is optional and is not installed in the\n * core.\n *\n * // demo/resources/js/admin.js\n * import '@vueup/vue-quill/dist/vue-quill.snow.css'\n * import '@vueup/vue-quill/dist/vue-quill.bubble.css'\n */\n\ninterface Props {\n name: string\n label?: string | null\n help?: string | null\n required?: boolean\n placeholder?: string | null\n /**\n * Quill's toolbar configuration. It takes `'essential' | 'minimal' | 'full'`\n * or a raw array of groups.\n */\n toolbar?: string | unknown[]\n /** 'snow' — the default toolbar; 'bubble' — inline; 'core' — no UI at all. */\n theme?: 'snow' | 'bubble' | 'core'\n disabled?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n label: null,\n help: null,\n placeholder: null,\n required: false,\n toolbar: 'full',\n theme: 'snow',\n disabled: false,\n})\n\nconst form = useFormState()\nconst value = computed<string>(() => (form.getField(props.name) as string | undefined) ?? '')\nconst errorMsg = computed<string | undefined>(() => form.errors[props.name]?.[0])\n\nfunction onUpdate(next: string | Delta): void {\n // QuillEditor's v-model:content gives an HTML string by default, since\n // contentType is 'html'. A Delta object is supported too, just in case.\n if (typeof next === 'string') {\n form.setField(props.name, next)\n } else {\n form.setField(props.name, JSON.stringify(next))\n }\n}\n</script>\n\n<template>\n <div :class=\"['admin-field', { 'admin-field--invalid': errorMsg !== undefined }]\">\n <label v-if=\"label\" class=\"admin-field__label\">\n <span>{{ label }}</span>\n <span v-if=\"required\" class=\"admin-field__required\" aria-hidden=\"true\">*</span>\n </label>\n <div class=\"admin-field__control admin-field__control--quill\">\n <QuillEditor\n :content=\"value\"\n :toolbar=\"toolbar\"\n :theme=\"theme\"\n :placeholder=\"placeholder ?? undefined\"\n :read-only=\"disabled\"\n content-type=\"html\"\n @update:content=\"onUpdate\"\n />\n </div>\n <p v-if=\"errorMsg\" class=\"admin-field__error\">{{ errorMsg }}</p>\n <p v-else-if=\"help\" class=\"admin-field__help\">{{ help }}</p>\n </div>\n</template>\n\n<style>\n/* Quill takes its min-height from CSS, which removes the jolt on initialization. */\n.admin-field__control--quill .ql-container {\n min-height: 200px;\n font-size: 14px;\n font-family: inherit;\n}\n.admin-field__control--quill .ql-toolbar.ql-snow,\n.admin-field__control--quill .ql-container.ql-snow {\n border-color: var(--uid-border-subtle);\n}\n.admin-field__control--quill .ql-toolbar.ql-snow {\n border-radius: var(--uid-radius-md) var(--uid-radius-md) 0 0;\n background: var(--uid-surface-base);\n}\n.admin-field__control--quill .ql-container.ql-snow {\n border-radius: 0 0 var(--uid-radius-md) var(--uid-radius-md);\n background: var(--uid-surface-raised);\n color: var(--uid-text-primary);\n}\n</style>\n"],"names":["props","__props","form","useFormState","value","computed","errorMsg","onUpdate","next","_createElementBlock","_normalizeClass","_openBlock","_hoisted_1","_createElementVNode","_hoisted_2","_hoisted_3","_createVNode","_unref","QuillEditor","_hoisted_4","_toDisplayString","_hoisted_5"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmDA,UAAMA,IAAQC,GAURC,IAAOC,EAAA,GACPC,IAAQC,EAAiB,MAAOH,EAAK,SAASF,EAAM,IAAI,KAA4B,EAAE,GACtFM,IAAWD,EAA6B,MAAMH,EAAK,OAAOF,EAAM,IAAI,IAAI,CAAC,CAAC;AAEhF,aAASO,EAASC,GAA4B;AAG5C,MAAI,OAAOA,KAAS,WAClBN,EAAK,SAASF,EAAM,MAAMQ,CAAI,IAE9BN,EAAK,SAASF,EAAM,MAAM,KAAK,UAAUQ,CAAI,CAAC;AAAA,IAElD;2BAIEC,EAkBM,OAAA;AAAA,MAlBA,OAAKC,EAAA,CAAA,eAAA,EAAA,wBAA4CJ,EAAA,UAAa,QAAS,CAAA;AAAA,IAAA;MAC9DL,EAAA,SAAbU,EAAA,GAAAF,EAGQ,SAHRG,GAGQ;AAAA,QAFNC,EAAwB,gBAAfZ,EAAA,KAAK,GAAA,CAAA;AAAA,QACFA,EAAA,iBAAZQ,EAA+E,QAA/EK,GAAuE,GAAC;;MAE1ED,EAUM,OAVNE,GAUM;AAAA,QATJC,EAQEC,EAAAC,CAAA,GAAA;AAAA,UAPC,SAASd,EAAA;AAAA,UACT,SAASH,EAAA;AAAA,UACT,OAAOA,EAAA;AAAA,UACP,aAAaA,EAAA,eAAe;AAAA,UAC5B,aAAWA,EAAA;AAAA,UACZ,gBAAa;AAAA,UACZ,oBAAgBM;AAAA,QAAA;;MAGZD,EAAA,cAATG,EAAgE,KAAhEU,GAAgEC,EAAfd,EAAA,KAAQ,GAAA,CAAA,KAC3CL,EAAA,aAAdQ,EAA4D,KAA5DY,GAA4DD,EAAXnB,EAAA,IAAI,GAAA,CAAA;;;;"}
|
package/dist/router/builder.d.ts
CHANGED
|
@@ -1,33 +1,34 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* buildRoutesFromManifest
|
|
2
|
+
* buildRoutesFromManifest turns an AdminManifest into an array of
|
|
3
|
+
* RouteRecordRaw.
|
|
3
4
|
*
|
|
4
|
-
*
|
|
5
|
+
* The mapping:
|
|
5
6
|
* - resource{slug} → /r/{slug} (index)
|
|
6
7
|
* /r/{slug}/create (create)
|
|
7
8
|
* /r/{slug}/:id/edit (edit)
|
|
8
|
-
* /r/{slug}/:id (view,
|
|
9
|
+
* /r/{slug}/:id (view, optional)
|
|
9
10
|
* - screen{slug} → /screens/{slug}
|
|
10
11
|
* - settings{slug} → /settings/{slug}
|
|
11
12
|
* - dashboard{slug} → /dashboard/{slug}
|
|
12
13
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
14
|
+
* The components come from a resolver, so the host project decides what to
|
|
15
|
+
* draw and the library carries no renderers of its own.
|
|
15
16
|
*/
|
|
16
17
|
import type { RouteRecordRaw } from 'vue-router';
|
|
17
18
|
import type { Component } from 'vue';
|
|
18
19
|
import type { AdminManifest } from '../stores/manifest';
|
|
19
|
-
/** Vue
|
|
20
|
+
/** A Vue component, or an async loader for code splitting. */
|
|
20
21
|
export type AdminRouteComponent = Component | (() => Promise<Component>);
|
|
21
22
|
export interface RouteMeta {
|
|
22
|
-
/**
|
|
23
|
+
/** Requires a logged-in user. */
|
|
23
24
|
requiresAuth?: boolean;
|
|
24
|
-
/**
|
|
25
|
+
/** The permissions required to enter, with ANY semantics: one match is enough. */
|
|
25
26
|
permissions?: string[];
|
|
26
|
-
/**
|
|
27
|
+
/** The page's title, rendered into <title> by router.afterEach. */
|
|
27
28
|
title?: string;
|
|
28
|
-
/**
|
|
29
|
+
/** The admin route's kind, for the breadcrumbs and the active state in the UI. */
|
|
29
30
|
kind?: 'resource' | 'screen' | 'settings' | 'dashboard' | 'system' | 'auth';
|
|
30
|
-
/**
|
|
31
|
+
/** The entity's slug from the manifest, so the component can find its meta. */
|
|
31
32
|
slug?: string;
|
|
32
33
|
}
|
|
33
34
|
declare module 'vue-router' {
|
|
@@ -35,10 +36,10 @@ declare module 'vue-router' {
|
|
|
35
36
|
}
|
|
36
37
|
}
|
|
37
38
|
/**
|
|
38
|
-
*
|
|
39
|
+
* The component resolver of the dynamic routes.
|
|
39
40
|
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
41
|
+
* The host project supplies a Vue component for each role, and the library
|
|
42
|
+
* depends on no particular views.
|
|
42
43
|
*/
|
|
43
44
|
export interface RouteComponentResolver {
|
|
44
45
|
resourceIndex: AdminRouteComponent;
|
package/dist/router/guards.d.ts
CHANGED
|
@@ -1,56 +1,57 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* The router guards: authentication and permissions.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* 1.
|
|
6
|
-
*
|
|
7
|
-
* 2.
|
|
8
|
-
*
|
|
9
|
-
* 3.
|
|
10
|
-
*
|
|
4
|
+
* The logic:
|
|
5
|
+
* 1. When route.meta.requiresAuth is set and nobody is logged in, go to
|
|
6
|
+
* /login with ?redirect=...
|
|
7
|
+
* 2. When the user has a pendingChallenge (2FA), every protected route
|
|
8
|
+
* redirects to /login, where the challenge form lives.
|
|
9
|
+
* 3. When route.meta.permissions is set, hasAnyPermission() decides; none of
|
|
10
|
+
* them means /forbidden (or the route named 'admin.forbidden').
|
|
11
11
|
*
|
|
12
|
-
*
|
|
13
|
-
* permissions
|
|
14
|
-
*
|
|
12
|
+
* The guards use useAuthStore but never touch the client directly: everything
|
|
13
|
+
* goes through the permissions, isAuthenticated and pendingChallenge state.
|
|
14
|
+
* The stores must be hydrated before the router is set up.
|
|
15
15
|
*/
|
|
16
16
|
import type { RouteLocationNormalized, RouteLocationRaw } from 'vue-router';
|
|
17
|
-
/**
|
|
17
|
+
/** What a guard returns: true to pass through, or a redirect target. */
|
|
18
18
|
type GuardResult = boolean | RouteLocationRaw;
|
|
19
19
|
/**
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* `this: undefined
|
|
20
|
+
* A plain three-argument function, which is what router.beforeEach accepts. We
|
|
21
|
+
* do not use NavigationGuardWithThis<undefined> because it demands
|
|
22
|
+
* `this: undefined`, which is awkward when calling it directly from tests.
|
|
23
23
|
*/
|
|
24
24
|
type SimpleGuard = (to: RouteLocationNormalized, from: RouteLocationNormalized, next?: unknown) => GuardResult;
|
|
25
25
|
export interface AuthGuardOptions {
|
|
26
|
-
/**
|
|
26
|
+
/** The name of the login route; 'admin.login' by default. */
|
|
27
27
|
loginRouteName?: string;
|
|
28
|
-
/**
|
|
28
|
+
/** The name of the 403 page's route; 'admin.forbidden' by default. */
|
|
29
29
|
forbiddenRouteName?: string;
|
|
30
30
|
/**
|
|
31
|
-
*
|
|
32
|
-
*
|
|
31
|
+
* The name of the query parameter that carries the page to return to after
|
|
32
|
+
* the login; 'redirect' by default.
|
|
33
33
|
*/
|
|
34
34
|
redirectQueryKey?: string;
|
|
35
35
|
}
|
|
36
36
|
/**
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
37
|
+
* Creates the beforeEach guard. Pinia must be the active instance by the time
|
|
38
|
+
* the guard runs: router.beforeEach fires on every navigation, the first one
|
|
39
|
+
* included, so Pinia has to be installed already.
|
|
40
40
|
*/
|
|
41
41
|
export declare function createAuthGuard(opts?: AuthGuardOptions): SimpleGuard;
|
|
42
42
|
export interface TitleGuardOptions {
|
|
43
|
-
/**
|
|
43
|
+
/** The title template: {title} is meta.title, {brand} is the brand's name. */
|
|
44
44
|
template?: string;
|
|
45
|
-
/**
|
|
45
|
+
/** The brand's name. */
|
|
46
46
|
brand?: string;
|
|
47
|
-
/**
|
|
47
|
+
/** The title to use when meta carries none. */
|
|
48
48
|
fallback?: string;
|
|
49
49
|
}
|
|
50
50
|
/**
|
|
51
|
-
*
|
|
51
|
+
* Creates the afterEach hook that updates document.title.
|
|
52
52
|
*
|
|
53
|
-
*
|
|
53
|
+
* The default template is '{title} · {brand}' when both are there, and
|
|
54
|
+
* whichever one is present otherwise.
|
|
54
55
|
*/
|
|
55
56
|
export declare function createTitleGuard(opts?: TitleGuardOptions): (to: RouteLocationNormalized, from?: RouteLocationNormalized, failure?: unknown) => void;
|
|
56
57
|
export {};
|
package/dist/router/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* createAdminRouter —
|
|
2
|
+
* createAdminRouter — the factory of the admin router.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Usage:
|
|
5
5
|
*
|
|
6
6
|
* const router = createAdminRouter({
|
|
7
7
|
* base: '/admin',
|
|
@@ -11,52 +11,54 @@
|
|
|
11
11
|
* await router.replaceManifestRoutes(manifestStore.manifest)
|
|
12
12
|
* app.use(router)
|
|
13
13
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
14
|
+
* The library deliberately does not pick createWebHistory for the host: the
|
|
15
|
+
* host passes the history object itself (createWebHistory or
|
|
16
|
+
* createWebHashHistory).
|
|
16
17
|
*/
|
|
17
18
|
import { type Router, type RouteRecordRaw, type RouterHistory } from 'vue-router';
|
|
18
19
|
import type { AdminManifest } from '../stores/manifest';
|
|
19
20
|
import { type RouteComponentResolver, type AdminRouteComponent } from './builder';
|
|
20
21
|
import { type AuthGuardOptions, type TitleGuardOptions } from './guards';
|
|
21
22
|
export interface AdminRouterOptions {
|
|
22
|
-
/**
|
|
23
|
+
/** The admin panel's base URL; '/admin' by default. */
|
|
23
24
|
base?: string;
|
|
24
|
-
/**
|
|
25
|
+
/** The history implementation; createWebHistory(base) by default. */
|
|
25
26
|
history?: RouterHistory;
|
|
26
|
-
/**
|
|
27
|
+
/** The component resolver for the resource, screen, settings and dashboard routes. */
|
|
27
28
|
components: RouteComponentResolver & {
|
|
28
29
|
/** Login. */
|
|
29
30
|
login: AdminRouteComponent;
|
|
30
|
-
/**
|
|
31
|
+
/** The home page, or the overview dashboard. */
|
|
31
32
|
home: AdminRouteComponent;
|
|
32
33
|
/** 403 forbidden. */
|
|
33
34
|
forbidden: AdminRouteComponent;
|
|
34
35
|
/** 404 not found. */
|
|
35
36
|
notFound: AdminRouteComponent;
|
|
36
|
-
/**
|
|
37
|
+
/** The profile page. */
|
|
37
38
|
profile?: AdminRouteComponent;
|
|
38
|
-
/**
|
|
39
|
+
/** The notifications page. */
|
|
39
40
|
notifications?: AdminRouteComponent;
|
|
40
41
|
/** Forgot password. */
|
|
41
42
|
forgotPassword?: AdminRouteComponent;
|
|
42
|
-
/**
|
|
43
|
+
/** Password reset, by the token and email from the query. */
|
|
43
44
|
resetPassword?: AdminRouteComponent;
|
|
44
45
|
};
|
|
45
|
-
/**
|
|
46
|
+
/** Extra routes added on top of the dynamic ones. */
|
|
46
47
|
extraRoutes?: RouteRecordRaw[];
|
|
47
|
-
/**
|
|
48
|
+
/** The auth guard's options. */
|
|
48
49
|
authGuard?: AuthGuardOptions;
|
|
49
|
-
/**
|
|
50
|
+
/** The title guard's options. */
|
|
50
51
|
titleGuard?: TitleGuardOptions;
|
|
51
52
|
}
|
|
52
53
|
/**
|
|
53
|
-
*
|
|
54
|
+
* A Router extended with the ability to rebuild the dynamic routes.
|
|
54
55
|
*/
|
|
55
56
|
export interface AdminRouter extends Router {
|
|
56
57
|
/**
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
58
|
+
* Rebuilds the dynamic routes from the manifest: the old
|
|
59
|
+
* admin.resource.* / admin.screen.* / admin.settings.* / admin.dashboard.*
|
|
60
|
+
* are removed and replaced with the current ones. Useful when the manifest
|
|
61
|
+
* is reloaded.
|
|
60
62
|
*/
|
|
61
63
|
replaceManifestRoutes(manifest: AdminManifest | null): void;
|
|
62
64
|
}
|
package/dist/stores/auth.d.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* The auth store: the current user, the permissions, the login/logout/2FA flow.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* - `*` —
|
|
6
|
-
* - `admin.users.*` —
|
|
4
|
+
* Wildcards in permissions:
|
|
5
|
+
* - `*` — full access to everything.
|
|
6
|
+
* - `admin.users.*` — access to every admin.users.{view,create,update,delete}.
|
|
7
7
|
*
|
|
8
|
-
* 2FA flow:
|
|
9
|
-
* 1. login()
|
|
10
|
-
* errorKey: 'two_factor_required'
|
|
11
|
-
* `pendingChallenge`.
|
|
12
|
-
* 2. UI
|
|
13
|
-
* twoFactorRecovery(recovery_code)
|
|
8
|
+
* The 2FA flow:
|
|
9
|
+
* 1. login() with valid credentials and 2FA enabled returns success: false
|
|
10
|
+
* plus errorKey: 'two_factor_required' and a challenge_token. The state
|
|
11
|
+
* moves to `pendingChallenge`.
|
|
12
|
+
* 2. The UI shows the 2FA form. twoFactorChallenge(code) or
|
|
13
|
+
* twoFactorRecovery(recovery_code) completes the login.
|
|
14
14
|
*/
|
|
15
15
|
import type { AdminUser, AdminBootstrap } from '../types/bootstrap';
|
|
16
16
|
export interface PendingChallenge {
|
|
@@ -1,26 +1,27 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* The dashboard store: the edit mode, the working copy of the layout, and
|
|
3
|
+
* save/load.
|
|
3
4
|
*
|
|
4
|
-
*
|
|
5
|
+
* The backend contract:
|
|
5
6
|
* GET /api/admin/dashboard/get?key={dashboard_slug} → { layout: WidgetItem[] | null }
|
|
6
7
|
* POST /api/admin/dashboard/save body { key, widgets[] }
|
|
7
8
|
* POST /api/admin/dashboard/reset body { key }
|
|
8
9
|
*
|
|
9
|
-
* WidgetItem
|
|
10
|
+
* A WidgetItem is a per-user override:
|
|
10
11
|
* { slug, size, position, hidden, config?, type? }
|
|
11
12
|
*
|
|
12
|
-
* `slug`
|
|
13
|
-
*
|
|
14
|
-
*
|
|
13
|
+
* `slug` is either the backend's Widget::slug(), for the built-in ones, or
|
|
14
|
+
* user-generated, for the widgets added through "Add widget" — where the host
|
|
15
|
+
* saves the configuration in `config` and the type in `type`.
|
|
15
16
|
*/
|
|
16
17
|
export interface WidgetLayoutItem {
|
|
17
18
|
slug: string;
|
|
18
19
|
size?: number;
|
|
19
20
|
position?: number;
|
|
20
21
|
hidden?: boolean;
|
|
21
|
-
/**
|
|
22
|
+
/** The widget's type: set for the user-added ones, synced with Widget for the backend ones. */
|
|
22
23
|
type?: string;
|
|
23
|
-
/**
|
|
24
|
+
/** A title override plus the per-type configuration. */
|
|
24
25
|
config?: Record<string, unknown>;
|
|
25
26
|
}
|
|
26
27
|
export declare const useDashboardStore: import("pinia").StoreDefinition<"admin-dashboard", Pick<{
|
|
@@ -31,10 +32,10 @@ export declare const useDashboardStore: import("pinia").StoreDefinition<"admin-d
|
|
|
31
32
|
size?: number | undefined;
|
|
32
33
|
position?: number | undefined;
|
|
33
34
|
hidden?: boolean
|
|
34
|
-
/**
|
|
35
|
+
/** The widget's type: set for the user-added ones, synced with Widget for the backend ones. */
|
|
35
36
|
| undefined;
|
|
36
37
|
type?: string
|
|
37
|
-
/**
|
|
38
|
+
/** A title override plus the per-type configuration. */
|
|
38
39
|
| undefined;
|
|
39
40
|
config?: Record<string, unknown> | undefined;
|
|
40
41
|
}[], WidgetLayoutItem[] | {
|
|
@@ -42,10 +43,10 @@ export declare const useDashboardStore: import("pinia").StoreDefinition<"admin-d
|
|
|
42
43
|
size?: number | undefined;
|
|
43
44
|
position?: number | undefined;
|
|
44
45
|
hidden?: boolean
|
|
45
|
-
/**
|
|
46
|
+
/** The widget's type: set for the user-added ones, synced with Widget for the backend ones. */
|
|
46
47
|
| undefined;
|
|
47
48
|
type?: string
|
|
48
|
-
/**
|
|
49
|
+
/** A title override plus the per-type configuration. */
|
|
49
50
|
| undefined;
|
|
50
51
|
config?: Record<string, unknown> | undefined;
|
|
51
52
|
}[]>;
|
|
@@ -56,10 +57,10 @@ export declare const useDashboardStore: import("pinia").StoreDefinition<"admin-d
|
|
|
56
57
|
size?: number | undefined;
|
|
57
58
|
position?: number | undefined;
|
|
58
59
|
hidden?: boolean
|
|
59
|
-
/**
|
|
60
|
+
/** The widget's type: set for the user-added ones, synced with Widget for the backend ones. */
|
|
60
61
|
| undefined;
|
|
61
62
|
type?: string
|
|
62
|
-
/**
|
|
63
|
+
/** A title override plus the per-type configuration. */
|
|
63
64
|
| undefined;
|
|
64
65
|
config?: Record<string, unknown> | undefined;
|
|
65
66
|
}[], WidgetLayoutItem[] | {
|
|
@@ -67,10 +68,10 @@ export declare const useDashboardStore: import("pinia").StoreDefinition<"admin-d
|
|
|
67
68
|
size?: number | undefined;
|
|
68
69
|
position?: number | undefined;
|
|
69
70
|
hidden?: boolean
|
|
70
|
-
/**
|
|
71
|
+
/** The widget's type: set for the user-added ones, synced with Widget for the backend ones. */
|
|
71
72
|
| undefined;
|
|
72
73
|
type?: string
|
|
73
|
-
/**
|
|
74
|
+
/** A title override plus the per-type configuration. */
|
|
74
75
|
| undefined;
|
|
75
76
|
config?: Record<string, unknown> | undefined;
|
|
76
77
|
}[]>;
|
|
@@ -98,10 +99,10 @@ export declare const useDashboardStore: import("pinia").StoreDefinition<"admin-d
|
|
|
98
99
|
size?: number | undefined;
|
|
99
100
|
position?: number | undefined;
|
|
100
101
|
hidden?: boolean
|
|
101
|
-
/**
|
|
102
|
+
/** The widget's type: set for the user-added ones, synced with Widget for the backend ones. */
|
|
102
103
|
| undefined;
|
|
103
104
|
type?: string
|
|
104
|
-
/**
|
|
105
|
+
/** A title override plus the per-type configuration. */
|
|
105
106
|
| undefined;
|
|
106
107
|
config?: Record<string, unknown> | undefined;
|
|
107
108
|
}[], WidgetLayoutItem[] | {
|
|
@@ -109,10 +110,10 @@ export declare const useDashboardStore: import("pinia").StoreDefinition<"admin-d
|
|
|
109
110
|
size?: number | undefined;
|
|
110
111
|
position?: number | undefined;
|
|
111
112
|
hidden?: boolean
|
|
112
|
-
/**
|
|
113
|
+
/** The widget's type: set for the user-added ones, synced with Widget for the backend ones. */
|
|
113
114
|
| undefined;
|
|
114
115
|
type?: string
|
|
115
|
-
/**
|
|
116
|
+
/** A title override plus the per-type configuration. */
|
|
116
117
|
| undefined;
|
|
117
118
|
config?: Record<string, unknown> | undefined;
|
|
118
119
|
}[]>;
|
|
@@ -123,10 +124,10 @@ export declare const useDashboardStore: import("pinia").StoreDefinition<"admin-d
|
|
|
123
124
|
size?: number | undefined;
|
|
124
125
|
position?: number | undefined;
|
|
125
126
|
hidden?: boolean
|
|
126
|
-
/**
|
|
127
|
+
/** The widget's type: set for the user-added ones, synced with Widget for the backend ones. */
|
|
127
128
|
| undefined;
|
|
128
129
|
type?: string
|
|
129
|
-
/**
|
|
130
|
+
/** A title override plus the per-type configuration. */
|
|
130
131
|
| undefined;
|
|
131
132
|
config?: Record<string, unknown> | undefined;
|
|
132
133
|
}[], WidgetLayoutItem[] | {
|
|
@@ -134,10 +135,10 @@ export declare const useDashboardStore: import("pinia").StoreDefinition<"admin-d
|
|
|
134
135
|
size?: number | undefined;
|
|
135
136
|
position?: number | undefined;
|
|
136
137
|
hidden?: boolean
|
|
137
|
-
/**
|
|
138
|
+
/** The widget's type: set for the user-added ones, synced with Widget for the backend ones. */
|
|
138
139
|
| undefined;
|
|
139
140
|
type?: string
|
|
140
|
-
/**
|
|
141
|
+
/** A title override plus the per-type configuration. */
|
|
141
142
|
| undefined;
|
|
142
143
|
config?: Record<string, unknown> | undefined;
|
|
143
144
|
}[]>;
|
|
@@ -165,10 +166,10 @@ export declare const useDashboardStore: import("pinia").StoreDefinition<"admin-d
|
|
|
165
166
|
size?: number | undefined;
|
|
166
167
|
position?: number | undefined;
|
|
167
168
|
hidden?: boolean
|
|
168
|
-
/**
|
|
169
|
+
/** The widget's type: set for the user-added ones, synced with Widget for the backend ones. */
|
|
169
170
|
| undefined;
|
|
170
171
|
type?: string
|
|
171
|
-
/**
|
|
172
|
+
/** A title override plus the per-type configuration. */
|
|
172
173
|
| undefined;
|
|
173
174
|
config?: Record<string, unknown> | undefined;
|
|
174
175
|
}[], WidgetLayoutItem[] | {
|
|
@@ -176,10 +177,10 @@ export declare const useDashboardStore: import("pinia").StoreDefinition<"admin-d
|
|
|
176
177
|
size?: number | undefined;
|
|
177
178
|
position?: number | undefined;
|
|
178
179
|
hidden?: boolean
|
|
179
|
-
/**
|
|
180
|
+
/** The widget's type: set for the user-added ones, synced with Widget for the backend ones. */
|
|
180
181
|
| undefined;
|
|
181
182
|
type?: string
|
|
182
|
-
/**
|
|
183
|
+
/** A title override plus the per-type configuration. */
|
|
183
184
|
| undefined;
|
|
184
185
|
config?: Record<string, unknown> | undefined;
|
|
185
186
|
}[]>;
|
|
@@ -190,10 +191,10 @@ export declare const useDashboardStore: import("pinia").StoreDefinition<"admin-d
|
|
|
190
191
|
size?: number | undefined;
|
|
191
192
|
position?: number | undefined;
|
|
192
193
|
hidden?: boolean
|
|
193
|
-
/**
|
|
194
|
+
/** The widget's type: set for the user-added ones, synced with Widget for the backend ones. */
|
|
194
195
|
| undefined;
|
|
195
196
|
type?: string
|
|
196
|
-
/**
|
|
197
|
+
/** A title override plus the per-type configuration. */
|
|
197
198
|
| undefined;
|
|
198
199
|
config?: Record<string, unknown> | undefined;
|
|
199
200
|
}[], WidgetLayoutItem[] | {
|
|
@@ -201,10 +202,10 @@ export declare const useDashboardStore: import("pinia").StoreDefinition<"admin-d
|
|
|
201
202
|
size?: number | undefined;
|
|
202
203
|
position?: number | undefined;
|
|
203
204
|
hidden?: boolean
|
|
204
|
-
/**
|
|
205
|
+
/** The widget's type: set for the user-added ones, synced with Widget for the backend ones. */
|
|
205
206
|
| undefined;
|
|
206
207
|
type?: string
|
|
207
|
-
/**
|
|
208
|
+
/** A title override plus the per-type configuration. */
|
|
208
209
|
| undefined;
|
|
209
210
|
config?: Record<string, unknown> | undefined;
|
|
210
211
|
}[]>;
|
package/dist/stores/i18n.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { AdminBootstrap } from '../types/bootstrap';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
* Pinia (unit
|
|
3
|
+
* A standalone wrapper around store.tr() for the components: it does not fall
|
|
4
|
+
* over without an active Pinia (unit tests, SSR edges) and returns the source
|
|
5
|
+
* string instead.
|
|
5
6
|
*/
|
|
6
7
|
export declare function trSafe(text: string): string;
|
|
7
8
|
export declare const useI18nStore: import("pinia").StoreDefinition<"admin-i18n", Pick<{
|
|
@@ -33,7 +34,7 @@ export declare const useI18nStore: import("pinia").StoreDefinition<"admin-i18n",
|
|
|
33
34
|
keys: import("vue").ComputedRef<string[]>;
|
|
34
35
|
}, "hydrate" | "setMessages" | "t" | "tr" | "has">>;
|
|
35
36
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
37
|
+
* A convenience: the global t() for non-Vue contexts — utilities, services. In
|
|
38
|
+
* a Vue component prefer `const { t } = useI18nStore()`.
|
|
38
39
|
*/
|
|
39
40
|
export declare function tRaw(key: string, replace?: Record<string, string | number>): string;
|
package/dist/stores/index.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Public API of admin stores.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Usage:
|
|
5
5
|
*
|
|
6
6
|
* import { setAdminClient, useAuthStore, useThemeStore } from '@dskripchenko/laravel-admin'
|
|
7
7
|
*
|
|
8
8
|
* const client = createAdminClient({...})
|
|
9
|
-
* setAdminClient(client) //
|
|
9
|
+
* setAdminClient(client) // REQUIRED before the stores are used
|
|
10
10
|
* const auth = useAuthStore()
|
|
11
11
|
* const theme = useThemeStore()
|
|
12
12
|
*/
|
package/dist/stores/locale.d.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* The locale store: the current locale, its persistence and its synchronization
|
|
3
|
+
* with AdminClient.
|
|
3
4
|
*
|
|
4
|
-
*
|
|
5
|
-
* - `<html lang="...">`
|
|
6
|
-
* - AdminClient.setLocale()
|
|
7
|
-
* X-Admin-Locale header
|
|
8
|
-
* - POST /system/setLocale
|
|
5
|
+
* Changing it has three effects:
|
|
6
|
+
* - `<html lang="...">` is updated
|
|
7
|
+
* - AdminClient.setLocale() is called, so every later request carries the
|
|
8
|
+
* right X-Admin-Locale header
|
|
9
|
+
* - POST /system/setLocale persists it into user.locale and a cookie
|
|
9
10
|
*/
|
|
10
11
|
import type { AdminBootstrap } from '../types/bootstrap';
|
|
11
12
|
export declare const useLocaleStore: import("pinia").StoreDefinition<"admin-locale", Pick<{
|