@xleddyl/nuxt-cms 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/module.mjs +16 -5
  3. package/dist/runtime/app/components/cms/Drawer.vue +5 -9
  4. package/dist/runtime/app/components/cms/EntryDrawer.vue +11 -3
  5. package/dist/runtime/app/components/cms/EntryForm.vue +0 -1
  6. package/dist/runtime/app/components/cms/Input.d.vue.ts +0 -1
  7. package/dist/runtime/app/components/cms/Input.vue +7 -4
  8. package/dist/runtime/app/components/cms/Input.vue.d.ts +0 -1
  9. package/dist/runtime/app/components/cms/LocaleSwitch.vue +1 -1
  10. package/dist/runtime/app/components/cms/MediaFolderPicker.d.vue.ts +0 -1
  11. package/dist/runtime/app/components/cms/MediaFolderPicker.vue +1 -3
  12. package/dist/runtime/app/components/cms/MediaFolderPicker.vue.d.ts +0 -1
  13. package/dist/runtime/app/components/cms/Modal.vue +5 -9
  14. package/dist/runtime/app/components/cms/PasswordInput.vue +9 -5
  15. package/dist/runtime/app/components/cms/RelationField.vue +7 -2
  16. package/dist/runtime/app/components/cms/SelectMenu.d.vue.ts +0 -1
  17. package/dist/runtime/app/components/cms/SelectMenu.vue +1 -6
  18. package/dist/runtime/app/components/cms/SelectMenu.vue.d.ts +0 -1
  19. package/dist/runtime/app/components/cms/SettingsEntry.vue +1 -8
  20. package/dist/runtime/app/components/cms/SettingsModal.vue +5 -10
  21. package/dist/runtime/app/components/cms/SettingsUsers.vue +8 -4
  22. package/dist/runtime/app/components/cms/Table.d.vue.ts +0 -7
  23. package/dist/runtime/app/components/cms/Table.vue +7 -49
  24. package/dist/runtime/app/components/cms/Table.vue.d.ts +0 -7
  25. package/dist/runtime/app/components/cms/Textarea.d.vue.ts +0 -1
  26. package/dist/runtime/app/components/cms/Textarea.vue +1 -2
  27. package/dist/runtime/app/components/cms/Textarea.vue.d.ts +0 -1
  28. package/dist/runtime/app/composables/cms-entry-disabled.js +1 -1
  29. package/dist/runtime/app/composables/cms-media-keys.d.ts +1 -0
  30. package/dist/runtime/app/composables/cms-media-keys.js +5 -0
  31. package/dist/runtime/app/composables/cms-media-library.js +3 -0
  32. package/dist/runtime/app/composables/cms-media-uploader.d.ts +0 -1
  33. package/dist/runtime/app/composables/cms-media-uploader.js +1 -1
  34. package/dist/runtime/app/composables/cms-overlay.d.ts +4 -0
  35. package/dist/runtime/app/composables/cms-overlay.js +23 -0
  36. package/dist/runtime/app/composables/cms-settings.d.ts +0 -1
  37. package/dist/runtime/app/composables/cms-settings.js +1 -1
  38. package/dist/runtime/app/pages/admin-entry.vue +1 -0
  39. package/dist/runtime/assets/main.css +1 -1
  40. package/dist/runtime/generate.js +1 -1
  41. package/dist/runtime/server/api/collection.get.js +1 -1
  42. package/dist/runtime/server/api/media.delete.js +2 -1
  43. package/dist/runtime/server/api/media.post.js +2 -1
  44. package/dist/runtime/server/api/media.put.js +2 -1
  45. package/dist/runtime/server/api/users.post.js +13 -9
  46. package/dist/runtime/server/routes/auth/login.post.js +2 -2
  47. package/dist/runtime/server/utils/graphql-depth.js +12 -5
  48. package/dist/runtime/server/utils/graphql.js +2 -2
  49. package/dist/runtime/server/utils/media-folders.js +10 -2
  50. package/dist/runtime/server/utils/media.d.ts +1 -0
  51. package/dist/runtime/server/utils/media.js +4 -1
  52. package/dist/runtime/server/utils/migrate.js +1 -1
  53. package/dist/runtime/server/utils/require-admin.d.ts +0 -2
  54. package/dist/runtime/server/utils/require-admin.js +0 -1
  55. package/dist/runtime/shared/layout.d.ts +0 -1
  56. package/dist/runtime/shared/layout.js +0 -3
  57. package/package.json +1 -1
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@xleddyl/nuxt-cms",
3
3
  "configKey": "cms",
4
- "version": "0.2.0",
4
+ "version": "0.2.1",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "unknown"
package/dist/module.mjs CHANGED
@@ -380,7 +380,7 @@ function validateConfig(config, i18n) {
380
380
  errors.push(`${fat}: relation target '${field.to}' must be a collection`);
381
381
  if (!entry.table && target.table)
382
382
  errors.push(
383
- `${fat}: relation target '${field.to}' uses a custom table \u2014 derived relations can only target derived entries`
383
+ `${fat}: relation target '${field.to}' uses a custom table: derived relations can only target derived entries`
384
384
  );
385
385
  }
386
386
  if (field.cardinality && !["many-to-one", "one-to-one", "many-to-many"].includes(field.cardinality)) {
@@ -835,7 +835,12 @@ ${lines.join(
835
835
  function pageTypesTs(name, entry) {
836
836
  const auto = `${typeName(name)}Auto`;
837
837
  const lines = pageRoutes(entry).map((route) => {
838
- const keys = ["id", "path", "updatedAt", ...Object.keys(pageFields(entry, route.path))];
838
+ const keys = [
839
+ "id",
840
+ "path",
841
+ "updatedAt",
842
+ ...Object.entries(pageFields(entry, route.path)).filter(([, field]) => !isPrivateField(field)).map(([key]) => key)
843
+ ];
839
844
  return ` ${JSON.stringify(route.path)}: Pick<${auto}, ${quotedKeys(keys)}>`;
840
845
  });
841
846
  return [
@@ -1027,7 +1032,7 @@ async function loadCmsConfig(nuxt, resolver, configPathOption, i18n, logger) {
1027
1032
  cmsConfig = expandMobileMedia(await jiti.import(configPath, { default: true }));
1028
1033
  } else {
1029
1034
  logger.warn(
1030
- `[nuxt-cms] Config file not found: ${configPath}. Using an empty registry \u2014 create a ${configPathOption}.ts with defineCmsConfig().`
1035
+ `[nuxt-cms] Config file not found: ${configPath}. Using an empty registry. Create a ${configPathOption}.ts with defineCmsConfig().`
1031
1036
  );
1032
1037
  nuxt.options.alias["#cms-config"] = resolver.resolve("./runtime/shared/empty-config");
1033
1038
  }
@@ -1288,7 +1293,13 @@ const module$1 = defineNuxtModule({
1288
1293
  ` dialect: '${dialect}',`,
1289
1294
  ` schema: '${toPosix(schemaTemplate.dst)}',`,
1290
1295
  ` out: '${toPosix(relativeMigrationsDir)}',`,
1291
- driver === "postgres" ? ` dbCredentials: { url: process.env.NUXT_CMS_DATABASE_URL ?? '${databaseUrl}' },` : driver === "libsql" ? ` dbCredentials: { url: process.env.NUXT_CMS_DATABASE_URL ?? '${databaseUrl || `file:${toPosix(resolvedDbPath)}`}', authToken: (process.env.NUXT_CMS_DATABASE_AUTH_TOKEN ?? '${databaseAuthToken}') || undefined },` : driver === "d1" ? `` : ` dbCredentials: { url: '${toPosix(resolvedDbPath)}' },`,
1296
+ driver === "postgres" ? ` dbCredentials: { url: process.env.NUXT_CMS_DATABASE_URL ?? ${JSON.stringify(
1297
+ databaseUrl
1298
+ )} },` : driver === "libsql" ? ` dbCredentials: { url: process.env.NUXT_CMS_DATABASE_URL ?? ${JSON.stringify(
1299
+ databaseUrl || `file:${toPosix(resolvedDbPath)}`
1300
+ )}, authToken: (process.env.NUXT_CMS_DATABASE_AUTH_TOKEN ?? ${JSON.stringify(
1301
+ databaseAuthToken
1302
+ )}) || undefined },` : driver === "d1" ? `` : ` dbCredentials: { url: ${JSON.stringify(toPosix(resolvedDbPath))} },`,
1292
1303
  `}`,
1293
1304
  ``
1294
1305
  ].join("\n")
@@ -1305,7 +1316,7 @@ const module$1 = defineNuxtModule({
1305
1316
  const migrations = await collectMigrations(migrationsDir);
1306
1317
  if (!migrations && !nuxt.options.dev && Object.keys(cmsConfig).length) {
1307
1318
  logger.warn(
1308
- `[nuxt-cms] No migrations found at ${migrationsDir}. The CMS tables will not be created \u2014 run the dev server once to generate them and commit ${toPosix(
1319
+ `[nuxt-cms] No migrations found at ${migrationsDir}. The CMS tables will not be created. Run the dev server once to generate them and commit ${toPosix(
1309
1320
  relativeMigrationsDir
1310
1321
  )}.`
1311
1322
  );
@@ -35,6 +35,7 @@
35
35
 
36
36
  <script setup>
37
37
  import { onBeforeUnmount, watch } from "#imports";
38
+ import { useCmsOverlay } from "../../composables/cms-overlay";
38
39
  defineProps({
39
40
  title: { type: String, required: false }
40
41
  });
@@ -43,23 +44,18 @@ const emit = defineEmits(["close"]);
43
44
  function close() {
44
45
  emit("close");
45
46
  }
46
- function onKeydown(event) {
47
- if (event.key === "Escape") close();
48
- }
47
+ const overlay = useCmsOverlay(close);
49
48
  watch(open, (value) => {
50
49
  if (import.meta.server) return;
51
50
  if (value) {
52
- document.addEventListener("keydown", onKeydown);
51
+ overlay.activate();
53
52
  document.body.style.overflow = "hidden";
54
53
  } else {
55
- document.removeEventListener("keydown", onKeydown);
54
+ overlay.deactivate();
56
55
  document.body.style.overflow = "";
57
56
  }
58
57
  });
59
58
  onBeforeUnmount(() => {
60
- if (import.meta.client) {
61
- document.removeEventListener("keydown", onKeydown);
62
- document.body.style.overflow = "";
63
- }
59
+ if (import.meta.client) document.body.style.overflow = "";
64
60
  });
65
61
  </script>
@@ -121,16 +121,23 @@ function revertStatus() {
121
121
  if (!drafts.value) return;
122
122
  formState.value.status = JSON.parse(snapshot.value).status ?? null;
123
123
  }
124
+ let loadRequest = 0;
124
125
  async function load() {
125
- if (props.entryId === null) {
126
+ const request = ++loadRequest;
127
+ const id = props.entryId;
128
+ if (id === null) {
129
+ loading.value = false;
126
130
  setState(emptyState());
127
131
  return;
128
132
  }
133
+ const isStale = () => request !== loadRequest || props.entryId !== id || !open.value;
129
134
  loading.value = true;
130
135
  try {
131
- const entry = await cmsApi(`${endpoint.value}/${props.entryId}`);
136
+ const entry = await cmsApi(`${endpoint.value}/${id}`);
137
+ if (isStale()) return;
132
138
  setState(pickFields(entry));
133
139
  } catch (error) {
140
+ if (isStale()) return;
134
141
  toast.add({
135
142
  title: "Could not load entry",
136
143
  description: errorMessage(error),
@@ -138,7 +145,7 @@ async function load() {
138
145
  });
139
146
  open.value = false;
140
147
  } finally {
141
- loading.value = false;
148
+ if (request === loadRequest) loading.value = false;
142
149
  }
143
150
  }
144
151
  watch(
@@ -165,6 +172,7 @@ async function save() {
165
172
  emit("saved");
166
173
  open.value = false;
167
174
  } catch (error) {
175
+ revertStatus();
168
176
  toast.add({
169
177
  title: "Save failed",
170
178
  description: errorMessage(error),
@@ -125,7 +125,6 @@ watch(tabList, (tabs) => {
125
125
  if (!tabs.some((tab) => tab.id === activeTab.value)) activeTab.value = tabs[0]?.id ?? "";
126
126
  });
127
127
  const settings = useCmsSettingsState();
128
- const fields = computed(() => props.fields);
129
128
  const layout = computed(
130
129
  () => props.entryName ? entryFormLayout(props.entryName, settings.value, props.fields) : resolveFormLayout(props.fields, void 0)
131
130
  );
@@ -2,7 +2,6 @@ type __VLS_Props = {
2
2
  type?: string;
3
3
  size?: 'sm' | 'md' | 'lg';
4
4
  icon?: string;
5
- color?: 'error';
6
5
  };
7
6
  type __VLS_ModelProps = {
8
7
  modelValue?: string | number | null;
@@ -1,5 +1,9 @@
1
1
  <template>
2
- <div class="cms-input-wrap" :class="[$attrs.class, { 'has-lead': icon }]">
2
+ <div
3
+ class="cms-input-wrap"
4
+ :class="[$attrs.class, { 'has-lead': icon }]"
5
+ :style="$attrs.style"
6
+ >
3
7
  <span v-if="icon" class="cms-input-lead">
4
8
  <CmsIcon :name="icon" class="size-4" />
5
9
  </span>
@@ -7,7 +11,7 @@
7
11
  v-bind="inputAttrs"
8
12
  :type="type ?? 'text'"
9
13
  :value="model ?? ''"
10
- :class="['cms-field', `cms-field-${size ?? 'md'}`, { 'is-error': color === 'error' }]"
14
+ :class="['cms-field', `cms-field-${size ?? 'md'}`]"
11
15
  @input="onInput"
12
16
  />
13
17
  <span v-if="$slots.trailing" class="cms-input-trail">
@@ -22,8 +26,7 @@ defineOptions({ inheritAttrs: false });
22
26
  defineProps({
23
27
  type: { type: String, required: false },
24
28
  size: { type: String, required: false },
25
- icon: { type: String, required: false },
26
- color: { type: String, required: false }
29
+ icon: { type: String, required: false }
27
30
  });
28
31
  const [model, modifiers] = defineModel({ type: [String, Number, null] });
29
32
  const attrs = useAttrs();
@@ -2,7 +2,6 @@ type __VLS_Props = {
2
2
  type?: string;
3
3
  size?: 'sm' | 'md' | 'lg';
4
4
  icon?: string;
5
- color?: 'error';
6
5
  };
7
6
  type __VLS_ModelProps = {
8
7
  modelValue?: string | number | null;
@@ -6,7 +6,7 @@
6
6
  type="button"
7
7
  role="tab"
8
8
  :aria-selected="model === locale"
9
- :title="filled(locale) ? locale : `${locale} \u2014 empty`"
9
+ :title="filled(locale) ? locale : `${locale}: empty`"
10
10
  class="cms-locale-option"
11
11
  :class="{ 'is-active': model === locale, 'is-empty': !filled(locale) }"
12
12
  @click="model = locale"
@@ -1,7 +1,6 @@
1
1
  type __VLS_Props = {
2
2
  folders: string[];
3
3
  exclude?: string | null;
4
- disabled?: (string | null)[];
5
4
  };
6
5
  type __VLS_ModelProps = {
7
6
  modelValue?: string | null;
@@ -50,8 +50,7 @@ import { computed, ref } from "#imports";
50
50
  import { isWithinMediaFolder, mediaFolderDepth, mediaFolderName } from "#nuxt-cms";
51
51
  const props = defineProps({
52
52
  folders: { type: Array, required: true },
53
- exclude: { type: [String, null], required: false },
54
- disabled: { type: Array, required: false }
53
+ exclude: { type: [String, null], required: false }
55
54
  });
56
55
  const model = defineModel({ type: [String, null], ...{ default: null } });
57
56
  const query = ref("");
@@ -60,7 +59,6 @@ const visible = computed(() => {
60
59
  return term ? props.folders.filter((folder) => folder.includes(term)) : props.folders;
61
60
  });
62
61
  function isDisabled(folder) {
63
- if (props.disabled?.includes(folder)) return true;
64
62
  return !!props.exclude && !!folder && isWithinMediaFolder(folder, props.exclude);
65
63
  }
66
64
  </script>
@@ -1,7 +1,6 @@
1
1
  type __VLS_Props = {
2
2
  folders: string[];
3
3
  exclude?: string | null;
4
- disabled?: (string | null)[];
5
4
  };
6
5
  type __VLS_ModelProps = {
7
6
  modelValue?: string | null;
@@ -27,7 +27,8 @@
27
27
  </template>
28
28
 
29
29
  <script setup>
30
- import { onBeforeUnmount, watch } from "#imports";
30
+ import { watch } from "#imports";
31
+ import { useCmsOverlay } from "../../composables/cms-overlay";
31
32
  defineProps({
32
33
  title: { type: String, required: false },
33
34
  size: { type: String, required: false }
@@ -37,19 +38,14 @@ const emit = defineEmits(["after:leave"]);
37
38
  function close() {
38
39
  open.value = false;
39
40
  }
40
- function onKeydown(event) {
41
- if (event.key === "Escape") close();
42
- }
41
+ const overlay = useCmsOverlay(close);
43
42
  watch(open, (value, previous) => {
44
43
  if (import.meta.server) return;
45
44
  if (value) {
46
- document.addEventListener("keydown", onKeydown);
45
+ overlay.activate();
47
46
  } else {
48
- document.removeEventListener("keydown", onKeydown);
47
+ overlay.deactivate();
49
48
  if (previous) emit("after:leave");
50
49
  }
51
50
  });
52
- onBeforeUnmount(() => {
53
- if (import.meta.client) document.removeEventListener("keydown", onKeydown);
54
- });
55
51
  </script>
@@ -67,11 +67,15 @@ function regenerate() {
67
67
  }
68
68
  async function copyPassword() {
69
69
  if (!model.value) return;
70
- await navigator.clipboard.writeText(model.value);
71
- copied.value = true;
72
- clearTimeout(copiedTimer);
73
- copiedTimer = setTimeout(() => {
70
+ try {
71
+ await navigator.clipboard.writeText(model.value);
72
+ copied.value = true;
73
+ clearTimeout(copiedTimer);
74
+ copiedTimer = setTimeout(() => {
75
+ copied.value = false;
76
+ }, 1600);
77
+ } catch {
74
78
  copied.value = false;
75
- }, 1600);
79
+ }
76
80
  }
77
81
  </script>
@@ -22,7 +22,7 @@
22
22
 
23
23
  <script setup>
24
24
  import { isTranslatableField } from "#nuxt-cms";
25
- import { computed, onMounted, ref, watch } from "#imports";
25
+ import { computed, onBeforeUnmount, onMounted, ref, watch } from "#imports";
26
26
  import cmsConfig from "#cms-config";
27
27
  import { useCmsRuntime } from "../../composables/cms-runtime";
28
28
  import { useCmsToast } from "../../composables/cms-toast";
@@ -40,22 +40,26 @@ const fetched = ref([]);
40
40
  const selectedRows = ref(/* @__PURE__ */ new Map());
41
41
  const loading = ref(true);
42
42
  const searchTerm = ref("");
43
+ let fetchRequest = 0;
43
44
  async function fetchOptions() {
45
+ const request = ++fetchRequest;
44
46
  loading.value = true;
45
47
  try {
46
48
  const term = searchTerm.value.trim();
47
49
  const page = await $fetch(endpoint, {
48
50
  query: { light: "true", limit: 25, ...term ? { search: term } : {} }
49
51
  });
52
+ if (request !== fetchRequest) return;
50
53
  fetched.value = page.items;
51
54
  } catch (err) {
55
+ if (request !== fetchRequest) return;
52
56
  toast.add({
53
57
  title: "Loading failed",
54
58
  description: errorMessage(err),
55
59
  color: "error"
56
60
  });
57
61
  } finally {
58
- loading.value = false;
62
+ if (request === fetchRequest) loading.value = false;
59
63
  }
60
64
  }
61
65
  let searchTimer;
@@ -63,6 +67,7 @@ watch(searchTerm, () => {
63
67
  clearTimeout(searchTimer);
64
68
  searchTimer = setTimeout(() => void fetchOptions(), 300);
65
69
  });
70
+ onBeforeUnmount(() => clearTimeout(searchTimer));
66
71
  const selectedIds = computed(() => {
67
72
  if (props.multiple) return model.value ?? [];
68
73
  return model.value ? [model.value] : [];
@@ -7,7 +7,6 @@ type __VLS_Props = {
7
7
  multiple?: boolean;
8
8
  ignoreFilter?: boolean;
9
9
  loading?: boolean;
10
- size?: string;
11
10
  placeholder?: string;
12
11
  };
13
12
  type __VLS_ModelProps = {
@@ -1,10 +1,6 @@
1
1
  <template>
2
2
  <div ref="root" class="relative w-full">
3
- <div
4
- class="cms-field flex flex-wrap items-center gap-1"
5
- :class="`cms-field-${size ?? 'md'}`"
6
- @click="focusInput"
7
- >
3
+ <div class="cms-field cms-field-md flex flex-wrap items-center gap-1" @click="focusInput">
8
4
  <template v-if="multiple">
9
5
  <span v-for="value in modelArray" :key="value" class="cms-tag">
10
6
  {{ labelFor(value) }}
@@ -70,7 +66,6 @@ const props = defineProps({
70
66
  multiple: { type: Boolean, required: false },
71
67
  ignoreFilter: { type: Boolean, required: false },
72
68
  loading: { type: Boolean, required: false },
73
- size: { type: String, required: false },
74
69
  placeholder: { type: String, required: false }
75
70
  });
76
71
  const model = defineModel({ type: [String, Array, null] });
@@ -7,7 +7,6 @@ type __VLS_Props = {
7
7
  multiple?: boolean;
8
8
  ignoreFilter?: boolean;
9
9
  loading?: boolean;
10
- size?: string;
11
10
  placeholder?: string;
12
11
  };
13
12
  type __VLS_ModelProps = {
@@ -55,7 +55,7 @@
55
55
  </template>
56
56
 
57
57
  <script setup>
58
- import { computed, ref, watch } from "#imports";
58
+ import { computed, ref } from "#imports";
59
59
  import cmsConfig from "#cms-config";
60
60
  import {
61
61
  entryFields,
@@ -89,13 +89,6 @@ const parts = computed(() => [
89
89
  { id: "layout", label: "Form layout" }
90
90
  ]);
91
91
  const activePart = ref(parts.value[0].id);
92
- watch(
93
- () => props.name,
94
- () => {
95
- activePart.value = parts.value[0].id;
96
- status.value = "idle";
97
- }
98
- );
99
92
  const columns = computed(() => entryListColumns(props.name, settings.value));
100
93
  const layout = computed(() => entryFormLayout(props.name, settings.value, fields.value));
101
94
  const isCustom = computed(() => {
@@ -132,10 +132,11 @@
132
132
  </template>
133
133
 
134
134
  <script setup>
135
- import { computed, onBeforeUnmount, ref, watch } from "#imports";
135
+ import { computed, ref, watch } from "#imports";
136
136
  import cmsConfig from "#cms-config";
137
137
  import { CMS_THEMES, setCmsTheme } from "../../composables/cms-theme";
138
138
  import { useCmsAccount } from "../../composables/cms-account";
139
+ import { useCmsOverlay } from "../../composables/cms-overlay";
139
140
  const { isSuperAdmin } = useCmsAccount();
140
141
  const open = defineModel("open", { type: Boolean, ...{ default: false } });
141
142
  const theme = defineModel("theme", { type: String, ...{ required: true } });
@@ -165,15 +166,9 @@ function previewHalves(value) {
165
166
  function close() {
166
167
  open.value = false;
167
168
  }
168
- function onKeydown(event) {
169
- if (event.key === "Escape") close();
170
- }
169
+ const overlay = useCmsOverlay(close);
171
170
  watch(open, (value) => {
172
- if (import.meta.server) return;
173
- if (value) document.addEventListener("keydown", onKeydown);
174
- else document.removeEventListener("keydown", onKeydown);
175
- });
176
- onBeforeUnmount(() => {
177
- if (import.meta.client) document.removeEventListener("keydown", onKeydown);
171
+ if (value) overlay.activate();
172
+ else overlay.deactivate();
178
173
  });
179
174
  </script>
@@ -224,11 +224,15 @@ async function create() {
224
224
  }
225
225
  async function copyCredentials() {
226
226
  if (!created.value) return;
227
- await navigator.clipboard.writeText(
228
- `Email: ${created.value.email}
227
+ try {
228
+ await navigator.clipboard.writeText(
229
+ `Email: ${created.value.email}
229
230
  Password: ${created.value.password}`
230
- );
231
- copied.value = true;
231
+ );
232
+ copied.value = true;
233
+ } catch {
234
+ copied.value = false;
235
+ }
232
236
  }
233
237
  const resetting = ref(null);
234
238
  const reset = ref({ password: "" });
@@ -4,7 +4,6 @@ interface Column {
4
4
  accessorKey?: string;
5
5
  accessorFn?: (row: Row) => unknown;
6
6
  header?: string;
7
- reorderable?: boolean;
8
7
  sortable?: boolean;
9
8
  }
10
9
  interface TableSort {
@@ -15,11 +14,9 @@ type __VLS_Props = {
15
14
  data: Row[];
16
15
  columns: Column[];
17
16
  selectable?: boolean;
18
- rowKey?: string;
19
17
  };
20
18
  type __VLS_ModelProps = {
21
19
  'selected'?: string[];
22
- 'columnVisibility'?: Record<string, boolean>;
23
20
  'sort'?: TableSort | null;
24
21
  };
25
22
  type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
@@ -35,17 +32,13 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
35
32
  select: (event: Event, row: {
36
33
  original: Row;
37
34
  }) => any;
38
- reorder: (from: string, to: string) => any;
39
35
  "update:selected": (value: string[]) => any;
40
- "update:columnVisibility": (value: Record<string, boolean>) => any;
41
36
  "update:sort": (value: TableSort | null) => any;
42
37
  }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
43
38
  onSelect?: ((event: Event, row: {
44
39
  original: Row;
45
40
  }) => any) | undefined;
46
- onReorder?: ((from: string, to: string) => any) | undefined;
47
41
  "onUpdate:selected"?: ((value: string[]) => any) | undefined;
48
- "onUpdate:columnVisibility"?: ((value: Record<string, boolean>) => any) | undefined;
49
42
  "onUpdate:sort"?: ((value: TableSort | null) => any) | undefined;
50
43
  }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
51
44
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
@@ -13,22 +13,14 @@
13
13
  />
14
14
  </th>
15
15
  <th
16
- v-for="column in visibleColumns"
16
+ v-for="column in columns"
17
17
  :key="columnId(column)"
18
18
  :class="{
19
- 'is-dragging': draggingId === columnId(column),
20
- 'is-drop-target': dropTargetId === columnId(column),
21
19
  'is-sortable': column.sortable,
22
20
  'is-sorted': sort?.key === columnId(column)
23
21
  }"
24
- :draggable="column.reorderable || void 0"
25
22
  :aria-sort="ariaSort(column)"
26
23
  @click="toggleSort(column)"
27
- @dragstart="onDragStart($event, column)"
28
- @dragover="onDragOver($event, column)"
29
- @dragleave="onDragLeave(column)"
30
- @drop="onDrop($event, column)"
31
- @dragend="resetDrag"
32
24
  >
33
25
  <span class="cms-th-label">
34
26
  {{ column.header }}
@@ -44,7 +36,7 @@
44
36
  <tbody>
45
37
  <tr
46
38
  v-for="(row, index) in data"
47
- :key="index"
39
+ :key="keyOf(row)"
48
40
  class="__clickable"
49
41
  :class="{ 'is-selected': isSelected(row) }"
50
42
  :style="{ '--cms-row-delay': `${Math.min(index, 14) * 24}ms` }"
@@ -57,7 +49,7 @@
57
49
  @toggle="toggleRow(row)"
58
50
  />
59
51
  </td>
60
- <td v-for="column in visibleColumns" :key="columnId(column)">
52
+ <td v-for="column in columns" :key="columnId(column)">
61
53
  <slot :name="`${columnId(column)}-cell`" :row="{ original: row }">
62
54
  {{ cellValue(column, row) }}
63
55
  </slot>
@@ -70,16 +62,15 @@
70
62
  </template>
71
63
 
72
64
  <script setup>
73
- import { computed, ref } from "#imports";
65
+ import { computed } from "#imports";
74
66
  const props = defineProps({
75
67
  data: { type: Array, required: true },
76
68
  columns: { type: Array, required: true },
77
- selectable: { type: Boolean, required: false },
78
- rowKey: { type: String, required: false }
69
+ selectable: { type: Boolean, required: false }
79
70
  });
80
71
  const selected = defineModel("selected", { type: Array, ...{ default: () => [] } });
81
72
  function keyOf(row) {
82
- return String(row[props.rowKey ?? "id"]);
73
+ return String(row.id);
83
74
  }
84
75
  function isSelected(row) {
85
76
  return selected.value.includes(keyOf(row));
@@ -95,9 +86,8 @@ const someSelected = computed(() => !allSelected.value && selected.value.length
95
86
  function toggleAll() {
96
87
  selected.value = allSelected.value ? [] : props.data.map(keyOf);
97
88
  }
98
- const visibility = defineModel("columnVisibility", { type: Object, ...{ default: () => ({}) } });
99
89
  const sort = defineModel("sort", { type: [Object, null], ...{ default: null } });
100
- const emit = defineEmits(["select", "reorder"]);
90
+ const emit = defineEmits(["select"]);
101
91
  function columnId(column) {
102
92
  return column.id ?? column.accessorKey ?? "";
103
93
  }
@@ -117,38 +107,6 @@ function ariaSort(column) {
117
107
  if (sort.value?.key !== columnId(column)) return "none";
118
108
  return sort.value.order === "asc" ? "ascending" : "descending";
119
109
  }
120
- const draggingId = ref(null);
121
- const dropTargetId = ref(null);
122
- function resetDrag() {
123
- draggingId.value = null;
124
- dropTargetId.value = null;
125
- }
126
- function onDragStart(event, column) {
127
- if (!column.reorderable) return;
128
- draggingId.value = columnId(column);
129
- event.dataTransfer?.setData("text/plain", columnId(column));
130
- if (event.dataTransfer) event.dataTransfer.effectAllowed = "move";
131
- }
132
- function onDragOver(event, column) {
133
- if (!draggingId.value || !column.reorderable) return;
134
- event.preventDefault();
135
- if (event.dataTransfer) event.dataTransfer.dropEffect = "move";
136
- dropTargetId.value = columnId(column);
137
- }
138
- function onDragLeave(column) {
139
- if (dropTargetId.value === columnId(column)) dropTargetId.value = null;
140
- }
141
- function onDrop(event, column) {
142
- if (!draggingId.value || !column.reorderable) return;
143
- event.preventDefault();
144
- const from = draggingId.value;
145
- const to = columnId(column);
146
- resetDrag();
147
- if (from !== to) emit("reorder", from, to);
148
- }
149
- const visibleColumns = computed(
150
- () => props.columns.filter((column) => visibility.value[columnId(column)] !== false)
151
- );
152
110
  function cellValue(column, row) {
153
111
  if (column.accessorFn) return column.accessorFn(row);
154
112
  if (column.accessorKey) return row[column.accessorKey];
@@ -4,7 +4,6 @@ interface Column {
4
4
  accessorKey?: string;
5
5
  accessorFn?: (row: Row) => unknown;
6
6
  header?: string;
7
- reorderable?: boolean;
8
7
  sortable?: boolean;
9
8
  }
10
9
  interface TableSort {
@@ -15,11 +14,9 @@ type __VLS_Props = {
15
14
  data: Row[];
16
15
  columns: Column[];
17
16
  selectable?: boolean;
18
- rowKey?: string;
19
17
  };
20
18
  type __VLS_ModelProps = {
21
19
  'selected'?: string[];
22
- 'columnVisibility'?: Record<string, boolean>;
23
20
  'sort'?: TableSort | null;
24
21
  };
25
22
  type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
@@ -35,17 +32,13 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
35
32
  select: (event: Event, row: {
36
33
  original: Row;
37
34
  }) => any;
38
- reorder: (from: string, to: string) => any;
39
35
  "update:selected": (value: string[]) => any;
40
- "update:columnVisibility": (value: Record<string, boolean>) => any;
41
36
  "update:sort": (value: TableSort | null) => any;
42
37
  }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
43
38
  onSelect?: ((event: Event, row: {
44
39
  original: Row;
45
40
  }) => any) | undefined;
46
- onReorder?: ((from: string, to: string) => any) | undefined;
47
41
  "onUpdate:selected"?: ((value: string[]) => any) | undefined;
48
- "onUpdate:columnVisibility"?: ((value: Record<string, boolean>) => any) | undefined;
49
42
  "onUpdate:sort"?: ((value: TableSort | null) => any) | undefined;
50
43
  }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
51
44
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
@@ -1,6 +1,5 @@
1
1
  type __VLS_Props = {
2
2
  rows?: number;
3
- size?: 'sm' | 'md' | 'lg';
4
3
  color?: 'error';
5
4
  };
6
5
  type __VLS_ModelProps = {