@chelone/vue 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/README.md +1 -0
  2. package/dist/admin/admin-shell.vue.js +1 -1
  3. package/dist/components/admin/auth/auth-callback.vue.js +1 -1
  4. package/dist/components/admin/auth/login-form.vue.js +1 -1
  5. package/dist/components/admin/auth/login-with-email.vue.js +1 -1
  6. package/dist/components/admin/feedback/loading-indicator.vue.js +1 -1
  7. package/dist/components/admin/feedback/spinner.vue.js +1 -1
  8. package/dist/components/admin/fields/api-key-field.vue.d.ts +1 -1
  9. package/dist/components/admin/fields/api-key-field.vue.js +2 -12
  10. package/dist/components/admin/fields/select-field.vue.d.ts +17 -6
  11. package/dist/components/admin/fields/select-field.vue.js +1 -1
  12. package/dist/components/admin/inputs/checkbox-group-input.vue.d.ts +14 -7
  13. package/dist/components/admin/inputs/checkbox-group-input.vue.js +2 -2
  14. package/dist/components/admin/inputs/datagrid-input.vue.d.ts +20 -5
  15. package/dist/components/admin/inputs/datagrid-input.vue.js +7 -3
  16. package/dist/components/admin/inputs/radio-button-group-input.vue.d.ts +14 -7
  17. package/dist/components/admin/inputs/radio-button-group-input.vue.js +2 -2
  18. package/dist/components/admin/list/count-spinner.vue.js +1 -1
  19. package/dist/components/admin/supabase/forgot-password-form.vue.js +1 -1
  20. package/dist/components/admin/supabase/set-password-form.vue.js +1 -1
  21. package/dist/components/index.d.ts +0 -2
  22. package/dist/components/kanban/kanban-board.vue.js +260 -0
  23. package/dist/components/kanban/kanban-draggable-card.vue.d.ts +18 -0
  24. package/dist/components/kanban/kanban-draggable-card.vue.js +32 -0
  25. package/dist/components/kanban/kanban-draggable-card2.vue.js +4 -0
  26. package/dist/components/kanban/kanban-drop-area.vue.d.ts +15 -0
  27. package/dist/components/kanban/kanban-drop-area.vue.js +29 -0
  28. package/dist/components/kanban/kanban-drop-area2.vue.js +4 -0
  29. package/dist/components/maps-osm/osm-feature-operator.vue.js +1 -1
  30. package/dist/components/ui/chart/chart-container.vue.d.ts +18 -0
  31. package/dist/components/ui/chart/chart-container.vue.js +40 -0
  32. package/dist/components/ui/chart/chart-container2.vue.js +4 -0
  33. package/dist/components/ui/chart/chart-legend-content.vue.d.ts +9 -0
  34. package/dist/components/ui/chart/chart-legend-content.vue.js +56 -0
  35. package/dist/components/ui/chart/chart-legend-content2.vue.js +4 -0
  36. package/dist/components/ui/chart/chart-style.vue.d.ts +7 -0
  37. package/dist/components/ui/chart/chart-style.vue.js +43 -0
  38. package/dist/components/ui/chart/chart-style2.vue.js +4 -0
  39. package/dist/components/ui/chart/chart-tooltip-content.vue.d.ts +7 -0
  40. package/dist/components/ui/chart/chart-tooltip-content.vue.js +122 -0
  41. package/dist/components/ui/chart/chart-tooltip-content2.vue.js +4 -0
  42. package/dist/components/ui/chart/index.d.ts +11 -0
  43. package/dist/components/ui/chart/utils.d.ts +81 -0
  44. package/dist/components/ui/chart/utils.js +52 -0
  45. package/dist/components/ui/index.d.ts +1 -0
  46. package/dist/components/ui/spinner.vue.js +1 -29
  47. package/dist/components/ui/spinner2.vue.js +29 -1
  48. package/dist/create-resource-components.d.ts +142 -18
  49. package/dist/data/create-chelone-hooks.d.ts +2 -1
  50. package/dist/data/queries.d.ts +6 -3
  51. package/dist/data/queries.js +8 -4
  52. package/dist/index.js +398 -387
  53. package/dist/kanban.d.ts +1 -0
  54. package/dist/kanban.js +4 -0
  55. package/package.json +18 -4
  56. package/dist/components/admin/list/kanban-board.vue.js +0 -275
  57. /package/dist/components/{admin/list → kanban}/kanban-board.vue.d.ts +0 -0
  58. /package/dist/components/{admin/list → kanban}/kanban-board2.vue.js +0 -0
package/README.md CHANGED
@@ -13,6 +13,7 @@ Each entry below needs the peer dependencies listed beside it, and only that ent
13
13
  | Entry | Optional peers |
14
14
  | --- | --- |
15
15
  | `@chelone/vue/monaco` | `monaco-editor`, `@guolao/vue-monaco-editor` |
16
+ | `@chelone/vue/kanban` | `@dnd-kit/vue`, `@dnd-kit/dom` (needs Vue 3.5) |
16
17
  | `@chelone/vue/mdx-editor` | `@milkdown/crepe`, `@milkdown/kit` |
17
18
  | `@chelone/vue/csv-import` | `papaparse` |
18
19
  | `@chelone/vue/maps` | `maplibre-gl` |
@@ -4,7 +4,7 @@ import { useRoute, useRouter, START_LOCATION } from "vue-router";
4
4
  import _sfc_main$6 from "../components/admin/layout/layout.vue.js";
5
5
  import _sfc_main$1 from "../components/admin/layout/theme-provider.vue.js";
6
6
  import _sfc_main$4 from "../components/admin/auth/login-page.vue.js";
7
- import _sfc_main$3 from "../components/ui/spinner.vue.js";
7
+ import _sfc_main$3 from "../components/ui/spinner2.vue.js";
8
8
  import { useBasename } from "../router/adapter.js";
9
9
  import { useWatchDataErrors } from "../auth/permissions-hooks.js";
10
10
  import { useAuthGate } from "./auth-gate.js";
@@ -1,6 +1,6 @@
1
1
  import { defineComponent, computed, unref, openBlock, createBlock, createElementBlock, createVNode } from "vue";
2
2
  import { useHandleAuthCallback } from "../../../admin/auth-gate.js";
3
- import _sfc_main$2 from "../../ui/spinner.vue.js";
3
+ import _sfc_main$2 from "../../ui/spinner2.vue.js";
4
4
  import _sfc_main$1 from "./auth-error.vue.js";
5
5
  const _hoisted_1 = {
6
6
  key: 1,
@@ -6,7 +6,7 @@ import { useTranslate } from "../../../i18n/use-translate.js";
6
6
  import { cn } from "../../../lib/utils.js";
7
7
  import { useLogin } from "../../../auth/auth-hooks.js";
8
8
  import _sfc_main$2 from "../../ui/button.vue.js";
9
- import _sfc_main$3 from "../../ui/spinner.vue.js";
9
+ import _sfc_main$3 from "../../ui/spinner2.vue.js";
10
10
  import _sfc_main$1 from "../inputs/text-input.vue.js";
11
11
  const _sfc_main = /* @__PURE__ */ defineComponent({
12
12
  __name: "login-form",
@@ -6,7 +6,7 @@ import { useForm } from "../../../form/use-form.js";
6
6
  import { useTranslate } from "../../../i18n/use-translate.js";
7
7
  import { cn } from "../../../lib/utils.js";
8
8
  import _sfc_main$3 from "../../ui/button.vue.js";
9
- import _sfc_main$4 from "../../ui/spinner.vue.js";
9
+ import _sfc_main$4 from "../../ui/spinner2.vue.js";
10
10
  import _sfc_main$2 from "../inputs/password-input.vue.js";
11
11
  import _sfc_main$1 from "../inputs/text-input.vue.js";
12
12
  import { LogInIcon } from "./icons.js";
@@ -2,7 +2,7 @@ import { defineComponent, computed, openBlock, createElementBlock, normalizeClas
2
2
  import { useLoading, useRefresh } from "../../../data/use-refresh.js";
3
3
  import { useTranslate } from "../../../i18n/use-translate.js";
4
4
  import { cn } from "../../../lib/utils.js";
5
- import _sfc_main$2 from "../../ui/spinner.vue.js";
5
+ import _sfc_main$2 from "../../ui/spinner2.vue.js";
6
6
  import { RotateCwIcon } from "../buttons/icons.js";
7
7
  import _sfc_main$1 from "../common/icon-button-with-tooltip.vue.js";
8
8
  const _sfc_main = /* @__PURE__ */ defineComponent({
@@ -1,6 +1,6 @@
1
1
  import { defineComponent, computed, openBlock, createBlock, normalizeClass, createCommentVNode } from "vue";
2
2
  import { cn } from "../../../lib/utils.js";
3
- import _sfc_main$1 from "../../ui/spinner.vue.js";
3
+ import _sfc_main$1 from "../../ui/spinner2.vue.js";
4
4
  const _sfc_main = /* @__PURE__ */ defineComponent({
5
5
  __name: "spinner",
6
6
  props: {
@@ -1,4 +1,4 @@
1
- import type { LooseRecord, SourceKey, SourceProp } from "@chelone/core";
1
+ import { type LooseRecord, type SourceKey, type SourceProp } from "@chelone/core";
2
2
  /** Props for {@link ApiKeyField}. */
3
3
  export interface ApiKeyFieldProps<RecordType extends LooseRecord = LooseRecord> {
4
4
  source: SourceProp;
@@ -3,6 +3,7 @@ import { cn } from "../../../lib/utils.js";
3
3
  import _sfc_main$1 from "../../ui/button.vue.js";
4
4
  import { RecordContextKey } from "../../../record/record-context.js";
5
5
  import { useFieldValue } from "../../../record/use-field-value.js";
6
+ import { formatRelativeTime } from "@chelone/core";
6
7
  const _hoisted_1 = { class: "inline-flex items-center gap-2" };
7
8
  const _hoisted_2 = {
8
9
  "data-api-key": "",
@@ -82,17 +83,6 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
82
83
  if (!props.lastUsedSource || !rec) return void 0;
83
84
  return rec[props.lastUsedSource];
84
85
  });
85
- function formatRelative(iso) {
86
- if (!iso) return "Never";
87
- const delta = (new Date(iso).getTime() - Date.now()) / 1e3;
88
- const rtf = new Intl.RelativeTimeFormat(void 0, { numeric: "auto" });
89
- const abs = Math.abs(delta);
90
- if (abs < 60) return rtf.format(Math.round(delta), "second");
91
- if (abs < 3600) return rtf.format(Math.round(delta / 60), "minute");
92
- if (abs < 86400) return rtf.format(Math.round(delta / 3600), "hour");
93
- if (abs < 86400 * 30) return rtf.format(Math.round(delta / 86400), "day");
94
- return rtf.format(Math.round(delta / (86400 * 30)), "month");
95
- }
96
86
  async function handleCopy() {
97
87
  try {
98
88
  await navigator.clipboard.writeText(key.value ?? "");
@@ -182,7 +172,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
182
172
  }, toDisplayString(s), 1);
183
173
  }), 128))
184
174
  ])) : createCommentVNode("", true),
185
- __props.lastUsedSource ? (openBlock(), createElementBlock("span", _hoisted_7, "Last used: " + toDisplayString(formatRelative(lastUsed.value)), 1)) : createCommentVNode("", true)
175
+ __props.lastUsedSource ? (openBlock(), createElementBlock("span", _hoisted_7, "Last used: " + toDisplayString(lastUsed.value ? unref(formatRelativeTime)(new Date(lastUsed.value), /* @__PURE__ */ new Date()) : "Never"), 1)) : createCommentVNode("", true)
186
176
  ], 2)) : createCommentVNode("", true);
187
177
  };
188
178
  }
@@ -1,20 +1,31 @@
1
- import type { LooseRecord, SourceProp } from "@chelone/core";
1
+ import type { LooseRecord, SourcePath, SourceProp } from "@chelone/core";
2
2
  import type { ChoiceRecord, OptionText } from "../../../choices/use-choices.js";
3
3
  /** Props for {@link SelectField}. */
4
- export interface SelectFieldProps {
4
+ export interface SelectFieldProps<TChoice extends ChoiceRecord = ChoiceRecord> {
5
5
  source: SourceProp;
6
6
  record?: LooseRecord;
7
7
  defaultValue?: unknown;
8
8
  empty?: string;
9
9
  /** The list of choices to map the value against. */
10
- choices?: readonly ChoiceRecord[];
10
+ choices?: readonly TChoice[];
11
11
  /** The value path on a choice. Default `'id'`. */
12
- optionValue?: string;
12
+ optionValue?: SourcePath<TChoice>;
13
13
  /** The display path or a formatter. Default `'name'`. */
14
- optionText?: OptionText;
14
+ optionText?: OptionText<TChoice>;
15
15
  /** Translate a string-path text through the catalog. Default `true`. */
16
16
  translateChoice?: boolean;
17
17
  class?: string;
18
18
  }
19
- declare const _default: import("vue").DefineComponent<SelectFieldProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<SelectFieldProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
19
+ declare const _default: <TChoice extends ChoiceRecord = ChoiceRecord>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_expose?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
20
+ props: __VLS_PrettifyLocal<Pick<Partial<{}> & Omit<{} & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, never>, never> & SelectFieldProps<TChoice> & Partial<{}>> & import("vue").PublicProps;
21
+ expose(exposed: import("vue").ShallowUnwrapRef<{}>): void;
22
+ attrs: any;
23
+ slots: {};
24
+ emit: {};
25
+ }>) => import("vue").VNode & {
26
+ __ctx?: Awaited<typeof __VLS_setup>;
27
+ };
20
28
  export default _default;
29
+ type __VLS_PrettifyLocal<T> = {
30
+ [K in keyof T]: T[K];
31
+ } & {};
@@ -11,7 +11,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
11
11
  empty: {},
12
12
  choices: {},
13
13
  optionValue: {},
14
- optionText: {},
14
+ optionText: { type: Function },
15
15
  translateChoice: { type: Boolean },
16
16
  class: {}
17
17
  },
@@ -1,13 +1,20 @@
1
+ import type { ChoiceRecord } from "../../../choices/use-choices.js";
1
2
  import type { ChoiceInputBaseProps } from "./choice-input-props.js";
2
3
  /** Props for {@link CheckboxGroupInput}. */
3
- export interface CheckboxGroupInputProps extends ChoiceInputBaseProps {
4
+ export interface CheckboxGroupInputProps<TChoice extends ChoiceRecord = ChoiceRecord> extends ChoiceInputBaseProps<TChoice> {
4
5
  /** Lay the checkboxes out horizontally instead of stacked. */
5
6
  row?: boolean;
6
7
  }
7
- declare const _default: import("vue").DefineComponent<CheckboxGroupInputProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<CheckboxGroupInputProps> & Readonly<{}>, {
8
- label: string | false;
9
- optionValue: string;
10
- row: boolean;
11
- disableValue: string;
12
- }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
8
+ declare const _default: <TChoice extends ChoiceRecord = ChoiceRecord>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_expose?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
9
+ props: __VLS_PrettifyLocal<Pick<Partial<{}> & Omit<{} & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, never>, never> & CheckboxGroupInputProps<TChoice> & Partial<{}>> & import("vue").PublicProps;
10
+ expose(exposed: import("vue").ShallowUnwrapRef<{}>): void;
11
+ attrs: any;
12
+ slots: {};
13
+ emit: {};
14
+ }>) => import("vue").VNode & {
15
+ __ctx?: Awaited<typeof __VLS_setup>;
16
+ };
13
17
  export default _default;
18
+ type __VLS_PrettifyLocal<T> = {
19
+ [K in keyof T]: T[K];
20
+ } & {};
@@ -19,8 +19,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
19
19
  source: {},
20
20
  choices: {},
21
21
  optionText: {},
22
- optionValue: { default: "id" },
23
- disableValue: { default: "disabled" },
22
+ optionValue: {},
23
+ disableValue: {},
24
24
  translateChoice: { type: Boolean },
25
25
  label: { type: [String, Boolean], default: void 0 },
26
26
  helperText: {},
@@ -1,10 +1,25 @@
1
+ import type { ChoiceRecord } from "../../../choices/use-choices.js";
1
2
  import type { ChoiceInputBaseProps } from "./choice-input-props.js";
2
- /** Props for {@link DatagridInput}. */
3
- export interface DatagridInputProps extends ChoiceInputBaseProps {
3
+ /**
4
+ * Props for {@link DatagridInput}.
5
+ *
6
+ * `disableValue` comes in through the shared base and is unused: a datagrid row
7
+ * is never disabled. The React twin has no `disableValue` prop at all.
8
+ */
9
+ export interface DatagridInputProps<TChoice extends ChoiceRecord = ChoiceRecord> extends ChoiceInputBaseProps<TChoice> {
4
10
  /** Column header for the choice column. Default empty. */
5
11
  optionLabel?: string;
6
12
  }
7
- declare const _default: import("vue").DefineComponent<DatagridInputProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<DatagridInputProps> & Readonly<{}>, {
8
- optionValue: string;
9
- }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
13
+ declare const _default: <TChoice extends ChoiceRecord = ChoiceRecord>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_expose?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
14
+ props: __VLS_PrettifyLocal<Pick<Partial<{}> & Omit<{} & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, never>, never> & DatagridInputProps<TChoice> & Partial<{}>> & import("vue").PublicProps;
15
+ expose(exposed: import("vue").ShallowUnwrapRef<{}>): void;
16
+ attrs: any;
17
+ slots: {};
18
+ emit: {};
19
+ }>) => import("vue").VNode & {
20
+ __ctx?: Awaited<typeof __VLS_setup>;
21
+ };
10
22
  export default _default;
23
+ type __VLS_PrettifyLocal<T> = {
24
+ [K in keyof T]: T[K];
25
+ } & {};
@@ -12,14 +12,14 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
12
12
  optionLabel: {},
13
13
  source: {},
14
14
  choices: {},
15
- optionText: {},
16
- optionValue: { default: "id" },
15
+ optionText: { type: Function },
16
+ optionValue: {},
17
17
  disableValue: {},
18
18
  translateChoice: { type: Boolean },
19
19
  label: { type: [String, Boolean] },
20
20
  helperText: {},
21
21
  defaultValue: {},
22
- validate: {},
22
+ validate: { type: [Function, Array] },
23
23
  disabled: { type: Boolean },
24
24
  readonly: { type: Boolean },
25
25
  class: {}
@@ -32,6 +32,10 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
32
32
  componentName: "DatagridInput",
33
33
  choices: props.choices,
34
34
  source: props.source,
35
+ // The React twin defaults `optionText` to `"name"` in its own destructuring,
36
+ // so a `<DatagridInput>` under a reference wrapper labels the `name` field
37
+ // rather than the record representation. The literal has to be cast: a
38
+ // `SourcePath<TChoice>` is deferred while `TChoice` is open.
35
39
  optionText: props.optionText ?? "name",
36
40
  optionValue: props.optionValue,
37
41
  disableValue: props.disableValue,
@@ -1,13 +1,20 @@
1
+ import type { ChoiceRecord } from "../../../choices/use-choices.js";
1
2
  import type { ChoiceInputBaseProps } from "./choice-input-props.js";
2
3
  /** Props for {@link RadioButtonGroupInput}. */
3
- export interface RadioButtonGroupInputProps extends ChoiceInputBaseProps {
4
+ export interface RadioButtonGroupInputProps<TChoice extends ChoiceRecord = ChoiceRecord> extends ChoiceInputBaseProps<TChoice> {
4
5
  /** Lay the radios out horizontally instead of stacked. */
5
6
  row?: boolean;
6
7
  }
7
- declare const _default: import("vue").DefineComponent<RadioButtonGroupInputProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<RadioButtonGroupInputProps> & Readonly<{}>, {
8
- label: string | false;
9
- optionValue: string;
10
- row: boolean;
11
- disableValue: string;
12
- }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
8
+ declare const _default: <TChoice extends ChoiceRecord = ChoiceRecord>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_expose?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
9
+ props: __VLS_PrettifyLocal<Pick<Partial<{}> & Omit<{} & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, never>, never> & RadioButtonGroupInputProps<TChoice> & Partial<{}>> & import("vue").PublicProps;
10
+ expose(exposed: import("vue").ShallowUnwrapRef<{}>): void;
11
+ attrs: any;
12
+ slots: {};
13
+ emit: {};
14
+ }>) => import("vue").VNode & {
15
+ __ctx?: Awaited<typeof __VLS_setup>;
16
+ };
13
17
  export default _default;
18
+ type __VLS_PrettifyLocal<T> = {
19
+ [K in keyof T]: T[K];
20
+ } & {};
@@ -19,8 +19,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
19
19
  source: {},
20
20
  choices: {},
21
21
  optionText: {},
22
- optionValue: { default: "id" },
23
- disableValue: { default: "disabled" },
22
+ optionValue: {},
23
+ disableValue: {},
24
24
  translateChoice: { type: Boolean },
25
25
  label: { type: [String, Boolean], default: void 0 },
26
26
  helperText: {},
@@ -1,5 +1,5 @@
1
1
  import { defineComponent, unref, openBlock, createBlock, normalizeClass, createCommentVNode } from "vue";
2
- import _sfc_main$1 from "../../ui/spinner.vue.js";
2
+ import _sfc_main$1 from "../../ui/spinner2.vue.js";
3
3
  import { useTimeout } from "../feedback/use-timeout.js";
4
4
  const _sfc_main = /* @__PURE__ */ defineComponent({
5
5
  __name: "count-spinner",
@@ -6,7 +6,7 @@ import { useForm } from "../../../form/use-form.js";
6
6
  import { useTranslate } from "../../../i18n/use-translate.js";
7
7
  import { cn } from "../../../lib/utils.js";
8
8
  import _sfc_main$2 from "../../ui/button.vue.js";
9
- import _sfc_main$3 from "../../ui/spinner.vue.js";
9
+ import _sfc_main$3 from "../../ui/spinner2.vue.js";
10
10
  import _sfc_main$1 from "../inputs/text-input.vue.js";
11
11
  import { useResetPassword } from "./use-password-recovery.js";
12
12
  const _sfc_main = /* @__PURE__ */ defineComponent({
@@ -6,7 +6,7 @@ import { useForm } from "../../../form/use-form.js";
6
6
  import { useTranslate } from "../../../i18n/use-translate.js";
7
7
  import { cn } from "../../../lib/utils.js";
8
8
  import _sfc_main$2 from "../../ui/button.vue.js";
9
- import _sfc_main$3 from "../../ui/spinner.vue.js";
9
+ import _sfc_main$3 from "../../ui/spinner2.vue.js";
10
10
  import _sfc_main$1 from "../inputs/password-input.vue.js";
11
11
  import { useSupabaseUrlTokens, useSetPassword } from "./use-password-recovery.js";
12
12
  const _hoisted_1 = {
@@ -326,8 +326,6 @@ export type { FilterLiveSearchProps } from "./admin/list/filter-live-search.vue.
326
326
  export { default as FilterLiveSearch } from "./admin/list/filter-live-search.vue.js";
327
327
  export type { InfinitePaginationProps } from "./admin/list/infinite-pagination.vue.js";
328
328
  export { default as InfinitePagination } from "./admin/list/infinite-pagination.vue.js";
329
- export type { KanbanBoardProps } from "./admin/list/kanban-board.vue.js";
330
- export { default as KanbanBoard } from "./admin/list/kanban-board.vue.js";
331
329
  export type { ListActionsProps } from "./admin/list/list-actions.vue.js";
332
330
  export { default as ListActions } from "./admin/list/list-actions.vue.js";
333
331
  export type { ListNoResultsProps } from "./admin/list/list-no-results.vue.js";
@@ -0,0 +1,260 @@
1
+ import { defineComponent, computed, getCurrentInstance, openBlock, createBlock, unref, withCtx, createElementVNode, mergeProps, createElementBlock, Fragment, renderList, normalizeClass, toDisplayString, createVNode, createCommentVNode, renderSlot, withModifiers, createTextVNode } from "vue";
2
+ import { buildKanbanBuckets, planKanbanMove, getAtPath } from "@chelone/core";
3
+ import { PointerSensor } from "@dnd-kit/dom";
4
+ import { DragDropProvider } from "@dnd-kit/vue";
5
+ import { useResourceDefinition } from "../../admin/resource-registry.js";
6
+ import { useCheloneNotifications } from "../../context.js";
7
+ import { useUpdate } from "../../data/mutations.js";
8
+ import { useTranslate } from "../../i18n/use-translate.js";
9
+ import { useGetRecordRepresentation } from "../../label/use-get-record-representation.js";
10
+ import { cn } from "../../lib/utils.js";
11
+ import { useListContext } from "../../list/list-context.js";
12
+ import { useResourceContext } from "../../resource-context.js";
13
+ import _sfc_main$5 from "../ui/button.vue.js";
14
+ import { useMenuAccess } from "../admin/layout/use-menu-access.js";
15
+ import { ArrowRightLeftIcon } from "../admin/list/list-icons.js";
16
+ import _sfc_main$2 from "./kanban-draggable-card.vue.js";
17
+ import _sfc_main$1 from "./kanban-drop-area.vue.js";
18
+ import { useNavigate, useCreatePath } from "../../router/nav-composables.js";
19
+ import _sfc_main$3 from "../ui/dropdown-menu/dropdown-menu.vue.js";
20
+ import _sfc_main$4 from "../ui/dropdown-menu/dropdown-menu-trigger.vue.js";
21
+ import _sfc_main$6 from "../ui/dropdown-menu/dropdown-menu-content.vue.js";
22
+ import _sfc_main$7 from "../ui/dropdown-menu/dropdown-menu-item.vue.js";
23
+ const _hoisted_1 = ["aria-label", "data-column-id"];
24
+ const _hoisted_2 = { class: "flex items-center justify-between px-1 text-sm font-medium" };
25
+ const _hoisted_3 = {
26
+ "data-slot": "kanban-column-count",
27
+ class: "text-muted-foreground"
28
+ };
29
+ const _hoisted_4 = {
30
+ key: 0,
31
+ "data-slot": "kanban-empty",
32
+ class: "px-1 py-2 text-sm text-muted-foreground"
33
+ };
34
+ const _hoisted_5 = { class: "flex min-w-0 flex-1 flex-col gap-1" };
35
+ const _hoisted_6 = {
36
+ "data-slot": "kanban-card-title",
37
+ class: "font-medium"
38
+ };
39
+ const _hoisted_7 = {
40
+ key: 0,
41
+ "data-slot": "kanban-card-description",
42
+ class: "text-muted-foreground"
43
+ };
44
+ const _sfc_main = /* @__PURE__ */ defineComponent({
45
+ ...{ inheritAttrs: false },
46
+ __name: "kanban-board",
47
+ props: {
48
+ groupSource: {},
49
+ columns: {},
50
+ titleSource: { default: void 0 },
51
+ descriptionSource: { default: void 0 },
52
+ linkType: { type: [String, Boolean], default: void 0 },
53
+ class: {}
54
+ },
55
+ emits: ["select-card"],
56
+ setup(__props, { emit: __emit }) {
57
+ const sensors = [PointerSensor];
58
+ const props = __props;
59
+ const emit = __emit;
60
+ const list = useListContext();
61
+ const resource = useResourceContext();
62
+ const translate = useTranslate();
63
+ const getRepresentation = useGetRecordRepresentation(resource);
64
+ const navigate = useNavigate();
65
+ const createPath = useCreatePath();
66
+ const definition = useResourceDefinition(() => ({ resource }));
67
+ const notifications = useCheloneNotifications();
68
+ const { canAccess, isPending: isAccessPending } = useMenuAccess(
69
+ computed(() => ({
70
+ action: "edit",
71
+ resource: resource ?? ""
72
+ }))
73
+ );
74
+ const update = useUpdate(resource ?? "", { mutationMode: "optimistic" });
75
+ const draggable = computed(
76
+ () => !!resource && canAccess.value && !isAccessPending.value
77
+ );
78
+ const instance = getCurrentInstance();
79
+ const hasListener = () => {
80
+ const listeners = instance?.vnode.props;
81
+ return !!(listeners?.onSelectCard || listeners?.onSelectCardOnce);
82
+ };
83
+ const buckets = computed(
84
+ () => buildKanbanBuckets(list.data ?? [], props.groupSource, props.columns)
85
+ );
86
+ function move(record, targetColumnId) {
87
+ const plan = planKanbanMove(
88
+ record,
89
+ props.groupSource,
90
+ props.columns,
91
+ targetColumnId
92
+ );
93
+ if (!plan) return;
94
+ update.mutateAsync(plan).catch(() => {
95
+ notifications?.notify("chelone.notification.http_error", {
96
+ type: "error"
97
+ });
98
+ });
99
+ }
100
+ const type = computed(
101
+ () => props.linkType ?? (definition.value.hasEdit ? "edit" : definition.value.hasShow ? "show" : false)
102
+ );
103
+ const linkTo = (record) => resource && type.value ? createPath({ resource, id: record.id, type: type.value }) : void 0;
104
+ const selectable = computed(
105
+ () => hasListener() || !!resource && type.value !== false
106
+ );
107
+ function select(record) {
108
+ if (!selectable.value) return;
109
+ if (hasListener()) return emit("select-card", record);
110
+ const to = linkTo(record);
111
+ if (to) navigate(to);
112
+ }
113
+ function onCardKeydown(event, record) {
114
+ if (event.target !== event.currentTarget) return;
115
+ if (event.key !== "Enter" && event.key !== " ") return;
116
+ event.preventDefault();
117
+ select(record);
118
+ }
119
+ function onDragEnd(event) {
120
+ const { source, target } = event.operation;
121
+ if (event.canceled || !source || !target || !draggable.value) return;
122
+ const record = buckets.value.flatMap((bucket) => bucket.records).find((candidate) => String(candidate.id) === String(source.id));
123
+ if (record) move(record, String(target.id));
124
+ }
125
+ const isEmpty = (value) => value == null || value === "";
126
+ const textOf = (record, source) => source ? getAtPath(record, source) : void 0;
127
+ const titleOf = (record) => {
128
+ const value = textOf(record, props.titleSource);
129
+ return value == null ? getRepresentation(record) : String(value);
130
+ };
131
+ const othersOf = (columnId) => props.columns.filter((other) => other.id !== columnId);
132
+ const moveLabel = (record) => translate("chelone.kanban_board.move", {
133
+ title: titleOf(record),
134
+ _: "Move {title}"
135
+ });
136
+ const emptyLabel = computed(
137
+ () => translate("chelone.kanban_board.empty", { _: "No items" })
138
+ );
139
+ return (_ctx, _cache) => {
140
+ return openBlock(), createBlock(unref(DragDropProvider), {
141
+ sensors,
142
+ onDragEnd
143
+ }, {
144
+ default: withCtx(() => [
145
+ createElementVNode("div", mergeProps(_ctx.$attrs, {
146
+ "data-slot": "kanban-board",
147
+ class: unref(cn)("flex items-start gap-4 overflow-x-auto pb-2", props.class)
148
+ }), [
149
+ (openBlock(true), createElementBlock(Fragment, null, renderList(buckets.value, ({ column, records }) => {
150
+ return openBlock(), createElementBlock("div", {
151
+ key: column.id,
152
+ role: "group",
153
+ "aria-label": column.label,
154
+ "data-slot": "kanban-column",
155
+ "data-column-id": column.id,
156
+ class: normalizeClass(
157
+ unref(cn)(
158
+ "flex w-72 shrink-0 flex-col gap-2 rounded-lg bg-muted/50 p-2",
159
+ column.className
160
+ )
161
+ )
162
+ }, [
163
+ createElementVNode("div", _hoisted_2, [
164
+ createElementVNode("span", null, toDisplayString(column.label), 1),
165
+ createElementVNode("span", _hoisted_3, toDisplayString(records.length), 1)
166
+ ]),
167
+ createVNode(_sfc_main$1, {
168
+ "column-id": column.id,
169
+ "data-slot": "kanban-column-cards",
170
+ class: "flex min-h-16 flex-col gap-2 rounded-md data-[over]:bg-accent"
171
+ }, {
172
+ default: withCtx(() => [
173
+ records.length === 0 ? (openBlock(), createElementBlock("p", _hoisted_4, toDisplayString(emptyLabel.value), 1)) : createCommentVNode("", true),
174
+ (openBlock(true), createElementBlock(Fragment, null, renderList(records, (record) => {
175
+ return openBlock(), createBlock(_sfc_main$2, {
176
+ key: record.id,
177
+ id: String(record.id),
178
+ draggable: draggable.value,
179
+ role: "button",
180
+ tabindex: "0",
181
+ "data-slot": "kanban-card",
182
+ "data-record-id": String(record.id),
183
+ class: "flex cursor-pointer items-start gap-2 rounded-md border bg-card p-3 text-sm shadow-xs data-[dragging]:opacity-50",
184
+ onClick: ($event) => select(record),
185
+ onKeydown: ($event) => onCardKeydown($event, record)
186
+ }, {
187
+ default: withCtx(() => [
188
+ createElementVNode("div", _hoisted_5, [
189
+ _ctx.$slots.card ? renderSlot(_ctx.$slots, "card", {
190
+ key: 0,
191
+ record
192
+ }) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [
193
+ createElementVNode("span", _hoisted_6, toDisplayString(titleOf(record)), 1),
194
+ !isEmpty(textOf(record, props.descriptionSource)) ? (openBlock(), createElementBlock("span", _hoisted_7, toDisplayString(String(textOf(record, props.descriptionSource))), 1)) : createCommentVNode("", true)
195
+ ], 64))
196
+ ]),
197
+ draggable.value ? (openBlock(), createElementBlock("div", {
198
+ key: 0,
199
+ onClick: _cache[0] || (_cache[0] = withModifiers(() => {
200
+ }, ["stop"]))
201
+ }, [
202
+ createVNode(unref(_sfc_main$3), null, {
203
+ default: withCtx(() => [
204
+ createVNode(unref(_sfc_main$4), null, {
205
+ default: withCtx(() => [
206
+ createVNode(_sfc_main$5, {
207
+ type: "button",
208
+ variant: "ghost",
209
+ size: "icon",
210
+ "data-slot": "kanban-card-move",
211
+ class: "size-6",
212
+ "aria-label": moveLabel(record)
213
+ }, {
214
+ default: withCtx(() => [
215
+ createVNode(unref(ArrowRightLeftIcon))
216
+ ]),
217
+ _: 1
218
+ }, 8, ["aria-label"])
219
+ ]),
220
+ _: 2
221
+ }, 1024),
222
+ createVNode(unref(_sfc_main$6), { align: "end" }, {
223
+ default: withCtx(() => [
224
+ (openBlock(true), createElementBlock(Fragment, null, renderList(othersOf(column.id), (other) => {
225
+ return openBlock(), createBlock(unref(_sfc_main$7), {
226
+ key: other.id,
227
+ onSelect: ($event) => move(record, other.id)
228
+ }, {
229
+ default: withCtx(() => [
230
+ createTextVNode(toDisplayString(other.label), 1)
231
+ ]),
232
+ _: 2
233
+ }, 1032, ["onSelect"]);
234
+ }), 128))
235
+ ]),
236
+ _: 2
237
+ }, 1024)
238
+ ]),
239
+ _: 2
240
+ }, 1024)
241
+ ])) : createCommentVNode("", true)
242
+ ]),
243
+ _: 2
244
+ }, 1032, ["id", "draggable", "data-record-id", "onClick", "onKeydown"]);
245
+ }), 128))
246
+ ]),
247
+ _: 2
248
+ }, 1032, ["column-id"])
249
+ ], 10, _hoisted_1);
250
+ }), 128))
251
+ ], 16)
252
+ ]),
253
+ _: 3
254
+ });
255
+ };
256
+ }
257
+ });
258
+ export {
259
+ _sfc_main as default
260
+ };
@@ -0,0 +1,18 @@
1
+ type __VLS_Props = {
2
+ /** The record id; the drag's source id. */
3
+ id: string;
4
+ /** False when the user may not edit: the card is no drag source then. */
5
+ draggable: boolean;
6
+ };
7
+ declare var __VLS_1: {};
8
+ type __VLS_Slots = {} & {
9
+ default?: (props: typeof __VLS_1) => any;
10
+ };
11
+ declare const __VLS_component: 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>;
12
+ declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
13
+ export default _default;
14
+ type __VLS_WithSlots<T, S> = T & {
15
+ new (): {
16
+ $slots: S;
17
+ };
18
+ };