@xleddyl/nuxt-cms 0.1.23 → 0.1.24

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 (69) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/module.mjs +16 -3
  3. package/dist/runtime/app/components/cms/BlocksField.vue +2 -9
  4. package/dist/runtime/app/components/cms/Button.vue +4 -2
  5. package/dist/runtime/app/components/cms/ConfirmModal.vue +5 -14
  6. package/dist/runtime/app/components/cms/Drawer.d.vue.ts +30 -0
  7. package/dist/runtime/app/components/cms/Drawer.vue +65 -0
  8. package/dist/runtime/app/components/cms/Drawer.vue.d.ts +30 -0
  9. package/dist/runtime/app/components/cms/DropdownMenu.d.vue.ts +0 -3
  10. package/dist/runtime/app/components/cms/DropdownMenu.vue +2 -7
  11. package/dist/runtime/app/components/cms/DropdownMenu.vue.d.ts +0 -3
  12. package/dist/runtime/app/components/cms/EmptyState.d.vue.ts +1 -0
  13. package/dist/runtime/app/components/cms/EmptyState.vue +6 -7
  14. package/dist/runtime/app/components/cms/EmptyState.vue.d.ts +1 -0
  15. package/dist/runtime/app/components/cms/EntryDrawer.d.vue.ts +21 -0
  16. package/dist/runtime/app/components/cms/EntryDrawer.vue +193 -0
  17. package/dist/runtime/app/components/cms/EntryDrawer.vue.d.ts +21 -0
  18. package/dist/runtime/app/components/cms/EntryForm.vue +19 -7
  19. package/dist/runtime/app/components/cms/FieldInput.d.vue.ts +1 -0
  20. package/dist/runtime/app/components/cms/FieldInput.vue +13 -27
  21. package/dist/runtime/app/components/cms/FieldInput.vue.d.ts +1 -0
  22. package/dist/runtime/app/components/cms/Form.vue +1 -1
  23. package/dist/runtime/app/components/cms/FormField.d.vue.ts +4 -5
  24. package/dist/runtime/app/components/cms/FormField.vue +9 -7
  25. package/dist/runtime/app/components/cms/FormField.vue.d.ts +4 -5
  26. package/dist/runtime/app/components/cms/JsonField.vue +3 -4
  27. package/dist/runtime/app/components/cms/LocaleSwitch.d.vue.ts +14 -0
  28. package/dist/runtime/app/components/cms/LocaleSwitch.vue +29 -0
  29. package/dist/runtime/app/components/cms/LocaleSwitch.vue.d.ts +14 -0
  30. package/dist/runtime/app/components/cms/MediaField.vue +7 -17
  31. package/dist/runtime/app/components/cms/MediaGallery.vue +48 -78
  32. package/dist/runtime/app/components/cms/MediaThumb.d.vue.ts +6 -0
  33. package/dist/runtime/app/components/cms/MediaThumb.vue +40 -0
  34. package/dist/runtime/app/components/cms/MediaThumb.vue.d.ts +6 -0
  35. package/dist/runtime/app/components/cms/Modal.d.vue.ts +1 -4
  36. package/dist/runtime/app/components/cms/Modal.vue +8 -7
  37. package/dist/runtime/app/components/cms/Modal.vue.d.ts +1 -4
  38. package/dist/runtime/app/components/cms/PageHeader.d.vue.ts +1 -1
  39. package/dist/runtime/app/components/cms/PageHeader.vue +6 -6
  40. package/dist/runtime/app/components/cms/PageHeader.vue.d.ts +1 -1
  41. package/dist/runtime/app/components/cms/RelationField.vue +0 -4
  42. package/dist/runtime/app/components/cms/SelectMenu.vue +23 -3
  43. package/dist/runtime/app/components/cms/SlugField.vue +1 -2
  44. package/dist/runtime/app/components/cms/Spinner.d.vue.ts +3 -0
  45. package/dist/runtime/app/components/cms/Spinner.vue +5 -0
  46. package/dist/runtime/app/components/cms/Spinner.vue.d.ts +3 -0
  47. package/dist/runtime/app/components/cms/StatusBadge.d.vue.ts +6 -0
  48. package/dist/runtime/app/components/cms/StatusBadge.vue +11 -0
  49. package/dist/runtime/app/components/cms/StatusBadge.vue.d.ts +6 -0
  50. package/dist/runtime/app/components/cms/Table.d.vue.ts +3 -6
  51. package/dist/runtime/app/components/cms/Table.vue +72 -27
  52. package/dist/runtime/app/components/cms/Table.vue.d.ts +3 -6
  53. package/dist/runtime/app/components/cms/Toaster.vue +4 -4
  54. package/dist/runtime/app/components/cms/TranslatableField.d.vue.ts +1 -0
  55. package/dist/runtime/app/components/cms/TranslatableField.vue +7 -44
  56. package/dist/runtime/app/components/cms/TranslatableField.vue.d.ts +1 -0
  57. package/dist/runtime/app/layouts/cms-admin.vue +10 -20
  58. package/dist/runtime/app/pages/admin-collection.vue +130 -103
  59. package/dist/runtime/app/pages/admin-entry.vue +6 -24
  60. package/dist/runtime/app/pages/admin-login.vue +12 -31
  61. package/dist/runtime/app/pages/admin-media.vue +8 -2
  62. package/dist/runtime/app/utils/ui.d.ts +0 -7
  63. package/dist/runtime/app/utils/ui.js +0 -5
  64. package/dist/runtime/assets/main.css +1 -1
  65. package/dist/runtime/server/api/collection.get.js +8 -3
  66. package/dist/runtime/server/utils/relations.d.ts +1 -0
  67. package/dist/runtime/server/utils/relations.js +25 -0
  68. package/dist/runtime/shared/index.d.ts +10 -3
  69. package/package.json +1 -1
@@ -1,9 +1,6 @@
1
1
  type __VLS_Props = {
2
2
  title?: string;
3
- ui?: {
4
- content?: string;
5
- title?: string;
6
- };
3
+ size?: 'sm' | 'lg';
7
4
  };
8
5
  type __VLS_ModelProps = {
9
6
  'open'?: boolean;
@@ -1,5 +1,5 @@
1
1
  type __VLS_Props = {
2
- kicker: string;
2
+ kicker?: string;
3
3
  title: string;
4
4
  };
5
5
  declare var __VLS_1: {}, __VLS_3: {};
@@ -1,11 +1,11 @@
1
1
  <template>
2
- <header class="flex items-end justify-between gap-4">
3
- <div class="flex flex-col gap-2">
4
- <div class="cms-kicker">
2
+ <header class="cms-page-header">
3
+ <div class="cms-page-heading">
4
+ <div v-if="kicker" class="cms-kicker">
5
5
  {{ kicker }}
6
6
  </div>
7
- <div class="flex items-center gap-3">
8
- <h1 class="cms-display text-[32px]/none font-medium text-(--ui-text-highlighted)">
7
+ <div class="cms-actions">
8
+ <h1 class="cms-title cms-title-md">
9
9
  {{ title }}
10
10
  </h1>
11
11
  <slot name="badge" />
@@ -17,7 +17,7 @@
17
17
 
18
18
  <script setup>
19
19
  defineProps({
20
- kicker: { type: String, required: true },
20
+ kicker: { type: String, required: false },
21
21
  title: { type: String, required: true }
22
22
  });
23
23
  </script>
@@ -1,5 +1,5 @@
1
1
  type __VLS_Props = {
2
- kicker: string;
2
+ kicker?: string;
3
3
  title: string;
4
4
  };
5
5
  declare var __VLS_1: {}, __VLS_3: {};
@@ -8,8 +8,6 @@
8
8
  :items="options"
9
9
  value-key="value"
10
10
  :loading="loading"
11
- size="lg"
12
- class="w-full"
13
11
  />
14
12
  <CmsSelectMenu
15
13
  v-else
@@ -19,8 +17,6 @@
19
17
  :items="singleItems"
20
18
  value-key="value"
21
19
  :loading="loading"
22
- size="lg"
23
- class="w-full"
24
20
  />
25
21
  </template>
26
22
 
@@ -1,6 +1,10 @@
1
1
  <template>
2
2
  <div ref="root" class="relative w-full">
3
- <div class="cms-field cms-field-lg flex flex-wrap items-center gap-1" @click="focusInput">
3
+ <div
4
+ class="cms-field flex flex-wrap items-center gap-1"
5
+ :class="`cms-field-${size ?? 'md'}`"
6
+ @click="focusInput"
7
+ >
4
8
  <template v-if="multiple">
5
9
  <span v-for="value in modelArray" :key="value" class="cms-tag">
6
10
  {{ labelFor(value) }}
@@ -15,7 +19,7 @@
15
19
  </template>
16
20
  <input
17
21
  ref="input"
18
- class="min-w-16 flex-1 bg-transparent text-(--ui-text-highlighted) outline-none"
22
+ class="cms-selectmenu-input"
19
23
  :value="displayValue"
20
24
  :placeholder="placeholder"
21
25
  @input="onInput"
@@ -24,8 +28,19 @@
24
28
  <CmsIcon
25
29
  v-if="loading"
26
30
  name="arrow-path"
27
- class="ml-auto size-4 shrink-0 animate-spin text-(--ui-text-dimmed)"
31
+ class="cms-selectmenu-affix ml-auto size-4 animate-spin"
28
32
  />
33
+ <button
34
+ v-else
35
+ type="button"
36
+ tabindex="-1"
37
+ aria-label="Toggle options"
38
+ class="cms-select-chevron cms-selectmenu-affix ml-auto"
39
+ :class="{ 'is-open': open }"
40
+ @click.stop="toggleOpen"
41
+ >
42
+ <CmsIcon name="chevron-down" class="size-4" />
43
+ </button>
29
44
  </div>
30
45
  <div v-if="open" class="cms-selectmenu-panel">
31
46
  <button
@@ -108,6 +123,11 @@ function isSelected(value) {
108
123
  }
109
124
  function focusInput() {
110
125
  input.value?.focus();
126
+ open.value = true;
127
+ }
128
+ function toggleOpen() {
129
+ if (open.value) open.value = false;
130
+ else focusInput();
111
131
  }
112
132
  function toggle(value) {
113
133
  const next = modelArray.value.includes(value) ? modelArray.value.filter((entry) => entry !== value) : [...modelArray.value, value];
@@ -1,8 +1,7 @@
1
1
  <template>
2
2
  <CmsInput
3
3
  :model-value="model ?? ''"
4
- size="lg"
5
- class="w-full font-mono"
4
+ class="font-mono"
6
5
  @update:model-value="
7
6
  (v) => {
8
7
  model = v || null;
@@ -0,0 +1,3 @@
1
+ declare const __VLS_export: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
2
+ declare const _default: typeof __VLS_export;
3
+ export default _default;
@@ -0,0 +1,5 @@
1
+ <template>
2
+ <div class="cms-spinner">
3
+ <CmsIcon name="arrow-path" class="size-6 animate-spin" />
4
+ </div>
5
+ </template>
@@ -0,0 +1,3 @@
1
+ declare const __VLS_export: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
2
+ declare const _default: typeof __VLS_export;
3
+ export default _default;
@@ -0,0 +1,6 @@
1
+ type __VLS_Props = {
2
+ published?: boolean;
3
+ };
4
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
5
+ declare const _default: typeof __VLS_export;
6
+ export default _default;
@@ -0,0 +1,11 @@
1
+ <template>
2
+ <span class="cms-badge" :class="published ? 'is-published' : 'is-draft'">
3
+ {{ published ? "Published" : "Draft" }}
4
+ </span>
5
+ </template>
6
+
7
+ <script setup>
8
+ defineProps({
9
+ published: { type: Boolean, required: false }
10
+ });
11
+ </script>
@@ -0,0 +1,6 @@
1
+ type __VLS_Props = {
2
+ published?: boolean;
3
+ };
4
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
5
+ declare const _default: typeof __VLS_export;
6
+ export default _default;
@@ -4,16 +4,11 @@ interface Column {
4
4
  accessorKey?: string;
5
5
  accessorFn?: (row: Row) => unknown;
6
6
  header?: string;
7
+ reorderable?: boolean;
7
8
  }
8
9
  type __VLS_Props = {
9
10
  data: Row[];
10
11
  columns: Column[];
11
- ui?: {
12
- thead?: string;
13
- th?: string;
14
- td?: string;
15
- tr?: string;
16
- };
17
12
  };
18
13
  type __VLS_ModelProps = {
19
14
  'columnVisibility'?: Record<string, boolean>;
@@ -31,11 +26,13 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
31
26
  select: (event: Event, row: {
32
27
  original: Row;
33
28
  }) => any;
29
+ reorder: (from: string, to: string) => any;
34
30
  "update:columnVisibility": (value: Record<string, boolean>) => any;
35
31
  }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
36
32
  onSelect?: ((event: Event, row: {
37
33
  original: Row;
38
34
  }) => any) | undefined;
35
+ onReorder?: ((from: string, to: string) => any) | undefined;
39
36
  "onUpdate:columnVisibility"?: ((value: Record<string, boolean>) => any) | undefined;
40
37
  }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
41
38
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
@@ -1,41 +1,86 @@
1
1
  <template>
2
- <table class="cms-table">
3
- <thead :class="ui?.thead">
4
- <tr>
5
- <th v-for="column in visibleColumns" :key="columnId(column)" :class="ui?.th">
6
- {{ column.header }}
7
- </th>
8
- </tr>
9
- </thead>
10
- <tbody>
11
- <tr
12
- v-for="(row, index) in data"
13
- :key="index"
14
- :class="ui?.tr"
15
- @click="emit('select', $event, { original: row })"
16
- >
17
- <td v-for="column in visibleColumns" :key="columnId(column)" :class="ui?.td">
18
- <slot :name="`${columnId(column)}-cell`" :row="{ original: row }">
19
- {{ cellValue(column, row) }}
20
- </slot>
21
- </td>
22
- </tr>
23
- </tbody>
24
- </table>
2
+ <div class="cms-card cms-table-shell">
3
+ <div class="cms-table-scroll">
4
+ <table class="cms-table">
5
+ <thead>
6
+ <tr>
7
+ <th
8
+ v-for="column in visibleColumns"
9
+ :key="columnId(column)"
10
+ :class="{
11
+ 'is-dragging': draggingId === columnId(column),
12
+ 'is-drop-target': dropTargetId === columnId(column)
13
+ }"
14
+ :draggable="column.reorderable || void 0"
15
+ @dragstart="onDragStart($event, column)"
16
+ @dragover="onDragOver($event, column)"
17
+ @dragleave="onDragLeave(column)"
18
+ @drop="onDrop($event, column)"
19
+ @dragend="resetDrag"
20
+ >
21
+ {{ column.header }}
22
+ </th>
23
+ </tr>
24
+ </thead>
25
+ <tbody>
26
+ <tr
27
+ v-for="(row, index) in data"
28
+ :key="index"
29
+ class="__clickable"
30
+ @click="emit('select', $event, { original: row })"
31
+ >
32
+ <td v-for="column in visibleColumns" :key="columnId(column)">
33
+ <slot :name="`${columnId(column)}-cell`" :row="{ original: row }">
34
+ {{ cellValue(column, row) }}
35
+ </slot>
36
+ </td>
37
+ </tr>
38
+ </tbody>
39
+ </table>
40
+ </div>
41
+ </div>
25
42
  </template>
26
43
 
27
44
  <script setup>
28
- import { computed } from "#imports";
45
+ import { computed, ref } from "#imports";
29
46
  const props = defineProps({
30
47
  data: { type: Array, required: true },
31
- columns: { type: Array, required: true },
32
- ui: { type: Object, required: false }
48
+ columns: { type: Array, required: true }
33
49
  });
34
50
  const visibility = defineModel("columnVisibility", { type: Object, ...{ default: () => ({}) } });
35
- const emit = defineEmits(["select"]);
51
+ const emit = defineEmits(["select", "reorder"]);
36
52
  function columnId(column) {
37
53
  return column.id ?? column.accessorKey ?? "";
38
54
  }
55
+ const draggingId = ref(null);
56
+ const dropTargetId = ref(null);
57
+ function resetDrag() {
58
+ draggingId.value = null;
59
+ dropTargetId.value = null;
60
+ }
61
+ function onDragStart(event, column) {
62
+ if (!column.reorderable) return;
63
+ draggingId.value = columnId(column);
64
+ event.dataTransfer?.setData("text/plain", columnId(column));
65
+ if (event.dataTransfer) event.dataTransfer.effectAllowed = "move";
66
+ }
67
+ function onDragOver(event, column) {
68
+ if (!draggingId.value || !column.reorderable) return;
69
+ event.preventDefault();
70
+ if (event.dataTransfer) event.dataTransfer.dropEffect = "move";
71
+ dropTargetId.value = columnId(column);
72
+ }
73
+ function onDragLeave(column) {
74
+ if (dropTargetId.value === columnId(column)) dropTargetId.value = null;
75
+ }
76
+ function onDrop(event, column) {
77
+ if (!draggingId.value || !column.reorderable) return;
78
+ event.preventDefault();
79
+ const from = draggingId.value;
80
+ const to = columnId(column);
81
+ resetDrag();
82
+ if (from !== to) emit("reorder", from, to);
83
+ }
39
84
  const visibleColumns = computed(
40
85
  () => props.columns.filter((column) => visibility.value[columnId(column)] !== false)
41
86
  );
@@ -4,16 +4,11 @@ interface Column {
4
4
  accessorKey?: string;
5
5
  accessorFn?: (row: Row) => unknown;
6
6
  header?: string;
7
+ reorderable?: boolean;
7
8
  }
8
9
  type __VLS_Props = {
9
10
  data: Row[];
10
11
  columns: Column[];
11
- ui?: {
12
- thead?: string;
13
- th?: string;
14
- td?: string;
15
- tr?: string;
16
- };
17
12
  };
18
13
  type __VLS_ModelProps = {
19
14
  'columnVisibility'?: Record<string, boolean>;
@@ -31,11 +26,13 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
31
26
  select: (event: Event, row: {
32
27
  original: Row;
33
28
  }) => any;
29
+ reorder: (from: string, to: string) => any;
34
30
  "update:columnVisibility": (value: Record<string, boolean>) => any;
35
31
  }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
36
32
  onSelect?: ((event: Event, row: {
37
33
  original: Row;
38
34
  }) => any) | undefined;
35
+ onReorder?: ((from: string, to: string) => any) | undefined;
39
36
  "onUpdate:columnVisibility"?: ((value: Record<string, boolean>) => any) | undefined;
40
37
  }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
41
38
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
@@ -11,18 +11,18 @@
11
11
  'is-error': toast.color === 'error'
12
12
  }"
13
13
  >
14
- <div class="flex items-start gap-3">
14
+ <div class="cms-toast-row">
15
15
  <div class="min-w-0 flex-1">
16
- <p class="text-sm font-medium text-(--ui-text-highlighted)">
16
+ <p class="cms-toast-title">
17
17
  {{ toast.title }}
18
18
  </p>
19
- <p v-if="toast.description" class="mt-0.5 text-sm text-(--ui-text-muted)">
19
+ <p v-if="toast.description" class="cms-toast-description">
20
20
  {{ toast.description }}
21
21
  </p>
22
22
  </div>
23
23
  <button
24
24
  type="button"
25
- class="shrink-0 text-(--ui-text-dimmed) hover:text-(--ui-text)"
25
+ class="cms-toast-dismiss"
26
26
  aria-label="Dismiss"
27
27
  @click="remove(toast.id)"
28
28
  >
@@ -1,6 +1,7 @@
1
1
  import type { FieldConfig } from '#nuxt-cms';
2
2
  type __VLS_Props = {
3
3
  field: FieldConfig;
4
+ locale?: string;
4
5
  };
5
6
  type __VLS_ModelProps = {
6
7
  modelValue: Record<string, string> | null;
@@ -1,53 +1,19 @@
1
1
  <template>
2
- <div class="flex flex-col gap-2">
3
- <div v-if="i18n.locales.length > 1" class="flex items-center gap-1">
4
- <CmsButton
5
- v-for="locale in i18n.locales"
6
- :key="locale"
7
- size="xs"
8
- :variant="active === locale ? 'soft' : 'ghost'"
9
- color="neutral"
10
- class="font-mono uppercase"
11
- @click="
12
- () => {
13
- active = locale;
14
- }
15
- "
16
- >
17
- {{ locale }}
18
- <span
19
- class="size-1.5 rounded-full"
20
- :class="hasContent(locale) ? 'bg-(--cms-fern)' : 'bg-(--cms-line)'"
21
- />
22
- </CmsButton>
23
- <span
24
- v-if="props.field.required && active === i18n.defaultLocale"
25
- class="ml-auto font-mono text-sm text-(--ui-text-muted)"
26
- >
27
- {{ i18n.defaultLocale }} *
28
- </span>
29
- </div>
30
- <CmsRichTextField v-if="props.field.type === 'richtext'" v-model="current" />
31
- <CmsTextarea
32
- v-else-if="props.field.textarea"
33
- v-model="current"
34
- :rows="4"
35
- size="lg"
36
- class="w-full"
37
- />
38
- <CmsInput v-else v-model="current" size="lg" class="w-full" />
39
- </div>
2
+ <CmsRichTextField v-if="props.field.type === 'richtext'" v-model="current" />
3
+ <CmsTextarea v-else-if="props.field.textarea" v-model="current" :rows="8" />
4
+ <CmsInput v-else v-model="current" />
40
5
  </template>
41
6
 
42
7
  <script setup>
43
- import { computed, ref } from "#imports";
8
+ import { computed } from "#imports";
44
9
  import { useCmsRuntime } from "../../composables/cms-runtime";
45
10
  const props = defineProps({
46
- field: { type: Object, required: true }
11
+ field: { type: Object, required: true },
12
+ locale: { type: String, required: false }
47
13
  });
48
14
  const model = defineModel({ type: [Object, null], ...{ required: true } });
49
15
  const { i18n } = useCmsRuntime();
50
- const active = ref(i18n.defaultLocale);
16
+ const active = computed(() => props.locale ?? i18n.defaultLocale);
51
17
  const current = computed({
52
18
  get: () => model.value?.[active.value] ?? "",
53
19
  set: (value) => {
@@ -58,7 +24,4 @@ const current = computed({
58
24
  model.value = Object.keys(next).length ? next : null;
59
25
  }
60
26
  });
61
- function hasContent(locale) {
62
- return !!model.value?.[locale]?.trim();
63
- }
64
27
  </script>
@@ -1,6 +1,7 @@
1
1
  import type { FieldConfig } from '#nuxt-cms';
2
2
  type __VLS_Props = {
3
3
  field: FieldConfig;
4
+ locale?: string;
4
5
  };
5
6
  type __VLS_ModelProps = {
6
7
  modelValue: Record<string, string> | null;
@@ -1,18 +1,12 @@
1
1
  <template>
2
2
  <div class="cms-scope cms-canvas cms-grain min-h-screen">
3
- <div class="flex min-h-screen">
4
- <aside
5
- class="sticky top-0 flex h-screen w-64 shrink-0 flex-col gap-7 border-r border-(--cms-line) px-4 py-7"
6
- >
7
- <div class="px-3">
8
- <div class="cms-display text-[22px] font-semibold text-(--ui-text-highlighted)">
9
- nuxt<span class="text-(--cms-fern)">·</span>cms
10
- </div>
11
- </div>
3
+ <div class="cms-shell">
4
+ <aside class="cms-sidebar">
5
+ <div class="cms-sidebar-brand">nuxt<span class="cms-accent">·</span>cms</div>
12
6
 
13
- <nav class="flex flex-1 flex-col gap-6 overflow-y-auto">
14
- <div v-for="group in groups" :key="group.title" class="flex flex-col gap-1">
15
- <div class="cms-kicker mb-2 px-3">
7
+ <nav class="cms-sidebar-nav">
8
+ <div v-for="group in groups" :key="group.title" class="cms-sidebar-group">
9
+ <div class="cms-kicker">
16
10
  {{ group.title }}
17
11
  </div>
18
12
  <NuxtLink
@@ -22,16 +16,13 @@
22
16
  class="cms-navlink"
23
17
  :class="{ 'is-active': route.path === link.to }"
24
18
  >
25
- <CmsIcon
26
- :name="group.icon"
27
- class="cms-navlink-icon size-4 shrink-0 text-(--ui-text-dimmed)"
28
- />
19
+ <CmsIcon :name="group.icon" class="cms-navlink-icon size-4 shrink-0" />
29
20
  <span class="truncate">{{ link.label }}</span>
30
21
  </NuxtLink>
31
22
  </div>
32
23
  </nav>
33
24
 
34
- <div class="flex flex-col gap-3 border-t border-(--cms-line) px-3 pt-5">
25
+ <div class="cms-sidebar-footer">
35
26
  <CmsButton
36
27
  label="Sign out"
37
28
  icon="arrow-right-on-rectangle"
@@ -40,14 +31,13 @@
40
31
  variant="ghost"
41
32
  color="neutral"
42
33
  size="xs"
43
- class="rounded-full"
44
34
  @click="logout"
45
35
  />
46
36
  </div>
47
37
  </aside>
48
38
 
49
- <main class="min-w-0 flex-1 px-10 py-10">
50
- <div class="mx-auto w-full max-w-5xl">
39
+ <main class="cms-main">
40
+ <div class="cms-main-inner">
51
41
  <slot />
52
42
  </div>
53
43
  </main>