@chelone/vue 0.1.1 → 0.3.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 (44) hide show
  1. package/dist/admin/admin-shell.vue.js +1 -1
  2. package/dist/admin/admin.vue.js +3 -2
  3. package/dist/array/use-array-input.d.ts +4 -3
  4. package/dist/choices/use-choices.d.ts +8 -2
  5. package/dist/choices/use-choices.js +3 -7
  6. package/dist/components/admin/auth/auth-callback.vue.js +1 -1
  7. package/dist/components/admin/auth/login-form.vue.js +1 -1
  8. package/dist/components/admin/auth/login-with-email.vue.js +1 -1
  9. package/dist/components/admin/buttons/delete-button.vue.js +21 -1
  10. package/dist/components/admin/buttons/dual-approval-button.vue.d.ts +19 -15
  11. package/dist/components/admin/buttons/dual-approval-button.vue.js +3 -1
  12. package/dist/components/admin/buttons/sort-button.vue.d.ts +2 -1
  13. package/dist/components/admin/buttons/status-transition-button.vue.d.ts +19 -14
  14. package/dist/components/admin/buttons/status-transition-button.vue.js +3 -1
  15. package/dist/components/admin/feedback/loading-indicator.vue.js +1 -1
  16. package/dist/components/admin/feedback/spinner.vue.js +1 -1
  17. package/dist/components/admin/fields/file-field.vue.js +3 -3
  18. package/dist/components/admin/fields/image-field.vue.js +3 -3
  19. package/dist/components/admin/inputs/choice-input-props.d.ts +2 -2
  20. package/dist/components/admin/inputs/use-choice-input.d.ts +2 -1
  21. package/dist/components/admin/list/calendar-header.vue.js +2 -2
  22. package/dist/components/admin/list/count-spinner.vue.js +1 -1
  23. package/dist/components/admin/list/data-table-col.vue.d.ts +2 -2
  24. package/dist/components/admin/list/data-table-number-col.vue.d.ts +2 -1
  25. package/dist/components/admin/list/filter-form.vue.js +2 -10
  26. package/dist/components/admin/list/tree-list.vue.d.ts +1 -1
  27. package/dist/components/admin/list/tree-list.vue.js +3 -9
  28. package/dist/components/admin/supabase/forgot-password-form.vue.js +1 -1
  29. package/dist/components/admin/supabase/set-password-form.vue.js +1 -1
  30. package/dist/components/maps-osm/osm-feature-operator.vue.js +1 -1
  31. package/dist/components/ui/spinner.vue.js +1 -29
  32. package/dist/components/ui/spinner2.vue.js +29 -1
  33. package/dist/controllers/use-controller-for-id.d.ts +6 -2
  34. package/dist/controllers/use-controller-for-id.js +2 -1
  35. package/dist/data/create-chelone-hooks.d.ts +20 -0
  36. package/dist/data/mutations.d.ts +20 -0
  37. package/dist/form/use-input.d.ts +4 -3
  38. package/dist/label/use-get-record-representation.d.ts +2 -1
  39. package/dist/label/use-get-record-representation.js +1 -13
  40. package/dist/record/use-field-value.d.ts +7 -6
  41. package/dist/record/use-field-value.js +3 -20
  42. package/package.json +4 -4
  43. package/dist/components/admin/fields/get-path.d.ts +0 -7
  44. package/dist/components/admin/fields/get-path.js +0 -14
@@ -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,5 +1,5 @@
1
1
  import { defineComponent, provide, useSlots, onScopeDispose, onBeforeUpdate, watchEffect, openBlock, createElementBlock, Fragment, renderSlot, createVNode, createSlots, withCtx, normalizeProps, guardReactiveProps } from "vue";
2
- import { joinBasename } from "@chelone/core";
2
+ import { createI18n, englishMessages, joinBasename } from "@chelone/core";
3
3
  import { BasenameKey } from "../router/adapter.js";
4
4
  import { QueryClient, VUE_QUERY_CLIENT } from "@tanstack/vue-query";
5
5
  import { useRouter, START_LOCATION } from "vue-router";
@@ -44,7 +44,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
44
44
  dataProvider: props.dataProvider,
45
45
  queryClient,
46
46
  authProvider: props.authProvider,
47
- i18n: props.i18n,
47
+ // Built once, and only when the app brings no i18n of its own.
48
+ i18n: props.i18n ?? createI18n({ locale: "en", messages: { en: englishMessages } }),
48
49
  store: props.store,
49
50
  notifications: props.notifications,
50
51
  // Thread the zod registry so `useForm({ resource })` auto-resolves the
@@ -18,11 +18,11 @@
18
18
  * array field's error/meta ride a separate {@link useSubscribable} over the field
19
19
  * binding, projected through `computed`.
20
20
  */
21
- import type { ArrayBindingUpdateOptions, ArrayRow, SourceScope, TranslatableMessage } from "@chelone/core";
21
+ import type { ArrayBindingUpdateOptions, ArrayRow, SourcePath, SourceScope, TranslatableMessage } from "@chelone/core";
22
22
  import { type ComputedRef, type Ref } from "vue";
23
- export interface UseArrayInputOptions extends ArrayBindingUpdateOptions {
23
+ export interface UseArrayInputOptions<TRecord = unknown> extends ArrayBindingUpdateOptions {
24
24
  /** The array field path RELATIVE to the ambient source scope. */
25
- source: string;
25
+ source: SourcePath<TRecord>;
26
26
  }
27
27
  /** The array field's own reactive error state (min-length validators, etc.). */
28
28
  export interface ArrayFieldState {
@@ -52,4 +52,5 @@ export interface UseArrayInputResult {
52
52
  clear(): void;
53
53
  fieldState: ArrayFieldState;
54
54
  }
55
+ export declare function useArrayInput<TRecord>(options: UseArrayInputOptions<TRecord>): UseArrayInputResult;
55
56
  export declare function useArrayInput(options: UseArrayInputOptions): UseArrayInputResult;
@@ -15,7 +15,7 @@
15
15
  */
16
16
  /** An erased choice record — choices carry arbitrary shapes (`{id, name, ...}`). */
17
17
  export type ChoiceRecord = Record<string, unknown>;
18
- /** The display path (dotted) or a formatter over the whole choice. */
18
+ /** The display path (dot or bracket) or a formatter over the whole choice. */
19
19
  export type OptionText = string | ((choice: ChoiceRecord) => string);
20
20
  export interface UseChoicesOptions {
21
21
  /** The display path or a formatter. Default `'name'`. */
@@ -36,7 +36,13 @@ export interface UseChoicesResult {
36
36
  /** The disabled flag: `get(choice, disableValue)`. */
37
37
  getDisableValue(choice: ChoiceRecord): unknown;
38
38
  }
39
- /** Reads a dotted path off a record (`get(choice, 'a.b')`), else `undefined`. */
39
+ /**
40
+ * Read a choice path off a record with core's one path grammar
41
+ * ({@link getAtPath}): dot members (`author.name`), bracket indices
42
+ * (`tags[0].label`) and numeric-dot indices all resolve, and an own flat key
43
+ * equal to the whole path wins. An empty path reads nothing, where core's
44
+ * reader returns the record itself. Public in the package barrel.
45
+ */
40
46
  export declare function getChoicePath(record: ChoiceRecord, path: string): unknown;
41
47
  /**
42
48
  * The choice accessors (ra's `useChoices`): text (string path or formatter,
@@ -1,12 +1,8 @@
1
+ import { getAtPath } from "@chelone/core";
1
2
  import { useTranslate } from "../i18n/use-translate.js";
2
3
  function getChoicePath(record, path) {
3
- if (path in record) return record[path];
4
- let current = record;
5
- for (const key of path.split(".")) {
6
- if (current == null || typeof current !== "object") return void 0;
7
- current = current[key];
8
- }
9
- return current;
4
+ if (path === "") return void 0;
5
+ return getAtPath(record, path);
10
6
  }
11
7
  function useChoices(options = {}) {
12
8
  const translate = useTranslate();
@@ -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";
@@ -3,6 +3,8 @@ import { removeRecordsFromCache } from "@chelone/core";
3
3
  import { useCheloneContext } from "../../../context.js";
4
4
  import { useUndoable } from "../../../data/undoable.js";
5
5
  import { useTranslate } from "../../../i18n/use-translate.js";
6
+ import { humanize, pluralize } from "../../../label/inflection.js";
7
+ import { useGetRecordRepresentation } from "../../../label/use-get-record-representation.js";
6
8
  import { RecordContextKey } from "../../../record/record-context.js";
7
9
  import { useResourceContext } from "../../../resource-context.js";
8
10
  import { useMenuAccess } from "../layout/use-menu-access.js";
@@ -56,6 +58,23 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
56
58
  }))
57
59
  );
58
60
  const show = computed(() => !isAccessPending.value && canAccess.value);
61
+ const getRecordRepresentation = useGetRecordRepresentation(resource);
62
+ const confirmOptions = computed(() => {
63
+ const representation = getRecordRepresentation(record.value);
64
+ return {
65
+ name: translate(`resources.${resource}.forcedCaseName`, {
66
+ smart_count: 1,
67
+ _: humanize(
68
+ translate(`resources.${resource}.name`, {
69
+ smart_count: 1,
70
+ _: pluralize(resource, 1)
71
+ })
72
+ )
73
+ }),
74
+ recordRepresentation: typeof representation === "string" ? representation : "",
75
+ id: record.value?.id
76
+ };
77
+ });
59
78
  const label = computed(
60
79
  () => translate("ra.action.delete", { _: props.label ?? "Delete" })
61
80
  );
@@ -123,9 +142,10 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
123
142
  title: __props.confirmTitle,
124
143
  content: __props.confirmContent,
125
144
  "confirm-color": __props.confirmColor,
145
+ "translate-options": confirmOptions.value,
126
146
  onConfirm,
127
147
  onClose: onCancel
128
- }, null, 8, ["is-open", "loading", "title", "content", "confirm-color"])) : createCommentVNode("", true)
148
+ }, null, 8, ["is-open", "loading", "title", "content", "confirm-color", "translate-options"])) : createCommentVNode("", true)
129
149
  ], 64);
130
150
  };
131
151
  }
@@ -1,12 +1,12 @@
1
- import type { LooseRecord, MutationMode } from "@chelone/core";
1
+ import type { MutationMode, SourceKey } from "@chelone/core";
2
2
  /** Props for {@link DualApprovalButton}. */
3
- export interface DualApprovalButtonProps {
3
+ export interface DualApprovalButtonProps<TRecord extends Record<string, unknown> = Record<string, unknown>> {
4
4
  /** Approvals needed. Floored, minimum 1. Default `2`. */
5
5
  required?: number;
6
- /** Record field holding the approver list. Default `"approvers"`. */
7
- approverSource?: string;
8
- /** Record field the status is written to. Default `"status"`. */
9
- statusSource?: string;
6
+ /** Record field holding the approver list, a top-level key. Default `"approvers"`. */
7
+ approverSource?: SourceKey<TRecord>;
8
+ /** Record field the status is written to, a top-level key. Default `"status"`. */
9
+ statusSource?: SourceKey<TRecord>;
10
10
  /** Status written while approvals are short of `required`. Default `"pending"`. */
11
11
  pendingValue?: unknown;
12
12
  /** Status written once approvals reach `required`. Default `"approved"`. */
@@ -14,18 +14,22 @@ export interface DualApprovalButtonProps {
14
14
  /** Override the ambient `<Resource>`. */
15
15
  resource?: string;
16
16
  /** Override the ambient record. */
17
- record?: LooseRecord;
17
+ record?: TRecord;
18
18
  /** How the update is dispatched. Default `"pessimistic"`. */
19
19
  mutationMode?: MutationMode;
20
20
  /** Extra CSS class appended to the button. */
21
21
  class?: string;
22
22
  }
23
- declare const _default: import("vue").DefineComponent<DualApprovalButtonProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<DualApprovalButtonProps> & Readonly<{}>, {
24
- required: number;
25
- mutationMode: MutationMode;
26
- approverSource: string;
27
- statusSource: string;
28
- pendingValue: "pending";
29
- approvedValue: "approved";
30
- }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
23
+ declare const _default: <TRecord extends Record<string, unknown> = Record<string, unknown>>(__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<{
24
+ props: __VLS_PrettifyLocal<Pick<Partial<{}> & Omit<{} & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, never>, never> & DualApprovalButtonProps<TRecord> & Partial<{}>> & import("vue").PublicProps;
25
+ expose(exposed: import("vue").ShallowUnwrapRef<{}>): void;
26
+ attrs: any;
27
+ slots: {};
28
+ emit: {};
29
+ }>) => import("vue").VNode & {
30
+ __ctx?: Awaited<typeof __VLS_setup>;
31
+ };
31
32
  export default _default;
33
+ type __VLS_PrettifyLocal<T> = {
34
+ [K in keyof T]: T[K];
35
+ } & {};
@@ -36,7 +36,9 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
36
36
  const props = __props;
37
37
  const resource = useResourceContext(props);
38
38
  const injectedRecord = inject(RecordContextKey, null);
39
- const record = computed(() => props.record ?? injectedRecord?.value);
39
+ const record = computed(
40
+ () => props.record ?? injectedRecord?.value
41
+ );
40
42
  const translate = useTranslate();
41
43
  const notifications = useCheloneNotifications();
42
44
  const { identity } = useOptionalIdentity();
@@ -1,7 +1,8 @@
1
+ import type { SourceProp } from "@chelone/core";
1
2
  /** Props for {@link SortButton}. */
2
3
  export interface SortButtonProps {
3
4
  /** The sortable field names offered in the dropdown. */
4
- fields: string[];
5
+ fields: SourceProp[];
5
6
  /** Override the auto-translated button label. */
6
7
  label?: string;
7
8
  /** Extra CSS class appended to the trigger. */
@@ -1,8 +1,8 @@
1
- import type { LooseRecord, MutationMode } from "@chelone/core";
1
+ import type { MutationMode, SourceKey } from "@chelone/core";
2
2
  /** Props for {@link StatusTransitionButton}. */
3
- export interface StatusTransitionButtonProps {
4
- /** Record field holding the status. Default `"status"`. */
5
- source?: string;
3
+ export interface StatusTransitionButtonProps<TRecord extends Record<string, unknown> = Record<string, unknown>> {
4
+ /** Record field holding the status, a top-level key. Default `"status"`. */
5
+ source?: SourceKey<TRecord>;
6
6
  /** Next states by current state. */
7
7
  transitions: Record<string, readonly string[]>;
8
8
  /** Predicates keyed `"<from>-><to>"`. A transition with no guard is allowed. */
@@ -12,7 +12,7 @@ export interface StatusTransitionButtonProps {
12
12
  /** Override the ambient `<Resource>`. */
13
13
  resource?: string;
14
14
  /** Override the ambient record. */
15
- record?: LooseRecord;
15
+ record?: TRecord;
16
16
  /** Ask for confirmation before moving the record. Default `false`. */
17
17
  confirm?: boolean;
18
18
  /** How the update is dispatched. Default `"pessimistic"`. */
@@ -20,13 +20,18 @@ export interface StatusTransitionButtonProps {
20
20
  /** Extra CSS class appended to the trigger. */
21
21
  class?: string;
22
22
  }
23
- declare const _default: import("vue").DefineComponent<StatusTransitionButtonProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
24
- transition: (from: string, to: string, record: LooseRecord) => any;
25
- }, string, import("vue").PublicProps, Readonly<StatusTransitionButtonProps> & Readonly<{
26
- onTransition?: ((from: string, to: string, record: LooseRecord) => any) | undefined;
27
- }>, {
28
- source: string;
29
- confirm: boolean;
30
- mutationMode: MutationMode;
31
- }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
23
+ declare const _default: <TRecord extends Record<string, unknown> = Record<string, unknown>>(__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<{
24
+ props: __VLS_PrettifyLocal<Pick<Partial<{}> & Omit<{
25
+ readonly onTransition?: ((from: string, to: string, record: TRecord) => any) | undefined;
26
+ } & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, never>, "onTransition"> & StatusTransitionButtonProps<TRecord> & Partial<{}>> & import("vue").PublicProps;
27
+ expose(exposed: import("vue").ShallowUnwrapRef<{}>): void;
28
+ attrs: any;
29
+ slots: {};
30
+ emit: (event: "transition", from: string, to: string, record: TRecord) => void;
31
+ }>) => import("vue").VNode & {
32
+ __ctx?: Awaited<typeof __VLS_setup>;
33
+ };
32
34
  export default _default;
35
+ type __VLS_PrettifyLocal<T> = {
36
+ [K in keyof T]: T[K];
37
+ } & {};
@@ -37,7 +37,9 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
37
37
  const emit = __emit;
38
38
  const resource = useResourceContext(props);
39
39
  const injectedRecord = inject(RecordContextKey, null);
40
- const record = computed(() => props.record ?? injectedRecord?.value);
40
+ const record = computed(
41
+ () => props.record ?? injectedRecord?.value
42
+ );
41
43
  const translate = useTranslate();
42
44
  const notifications = useCheloneNotifications();
43
45
  const { canAccess, isPending: isAccessPending } = useMenuAccess(
@@ -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,5 +1,5 @@
1
1
  import { defineComponent, computed, openBlock, createElementBlock, normalizeClass, unref, toDisplayString, Fragment, renderList, createElementVNode, createCommentVNode } from "vue";
2
- import { getPath } from "./get-path.js";
2
+ import { getAtPath } from "@chelone/core";
3
3
  import { cn } from "../../../lib/utils.js";
4
4
  import { useFieldValue } from "../../../record/use-field-value.js";
5
5
  import { useTranslate } from "../../../i18n/use-translate.js";
@@ -46,8 +46,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
46
46
  const links = computed(() => {
47
47
  if (!Array.isArray(sourceValue.value)) return [];
48
48
  return sourceValue.value.map((file, index) => {
49
- const fileTitleValue = props.title ? getPath(file, props.title) ?? props.title : props.title;
50
- const srcValue = props.src ? getPath(file, props.src) ?? props.title : props.title;
49
+ const fileTitleValue = props.title ? getAtPath(file, props.title) ?? props.title : props.title;
50
+ const srcValue = props.src ? getAtPath(file, props.src) ?? props.title : props.title;
51
51
  return { href: srcValue, title: fileTitleValue, key: srcValue ?? index };
52
52
  });
53
53
  });
@@ -1,5 +1,5 @@
1
1
  import { defineComponent, computed, openBlock, createElementBlock, normalizeClass, unref, toDisplayString, createElementVNode, Fragment, renderList, createCommentVNode } from "vue";
2
- import { getPath } from "./get-path.js";
2
+ import { getAtPath } from "@chelone/core";
3
3
  import { cn } from "../../../lib/utils.js";
4
4
  import { useFieldValue } from "../../../record/use-field-value.js";
5
5
  import { useTranslate } from "../../../i18n/use-translate.js";
@@ -39,8 +39,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
39
39
  const items = computed(() => {
40
40
  if (!Array.isArray(value.value)) return [];
41
41
  return value.value.map((file, index) => {
42
- const fileTitleValue = props.title ? getPath(file, props.title) ?? props.title : props.title;
43
- const srcValue = props.src ? getPath(file, props.src) ?? props.title : props.title;
42
+ const fileTitleValue = props.title ? getAtPath(file, props.title) ?? props.title : props.title;
43
+ const srcValue = props.src ? getAtPath(file, props.src) ?? props.title : props.title;
44
44
  return { src: srcValue, title: fileTitleValue, key: srcValue ?? index };
45
45
  });
46
46
  });
@@ -6,12 +6,12 @@
6
6
  * input nested under a reference wrapper inherits `source` from the ambient
7
7
  * choices context; standalone, it must be provided (each input throws otherwise).
8
8
  */
9
- import type { Validator } from "@chelone/core";
9
+ import type { SourceProp, Validator } from "@chelone/core";
10
10
  import type { ChoiceRecord, OptionText } from "../../../choices/use-choices.js";
11
11
  /** The choice-accessor + form-binding props every choice input accepts. */
12
12
  export interface ChoiceInputBaseProps {
13
13
  /** The FK/field path. Optional under a reference wrapper (inherited from context). */
14
- source?: string;
14
+ source?: SourceProp | undefined;
15
15
  /** Inline choices (`{ id, name }[]` or `string[]`). Omitted under a reference wrapper. */
16
16
  choices?: readonly (string | ChoiceRecord)[];
17
17
  /** The display path (dotted) or a formatter over the whole choice. Default `name`. */
@@ -21,6 +21,7 @@
21
21
  * / `resource` / `isFromReference` are stable per context, so they are read once
22
22
  * (parity with the twin's `useState` capture of the reference options).
23
23
  */
24
+ import type { SourceProp } from "@chelone/core";
24
25
  import { type ComputedRef } from "vue";
25
26
  import { type ChoiceRecord, type OptionText } from "../../../choices/use-choices.js";
26
27
  /** Options for {@link useChoiceInput}. */
@@ -30,7 +31,7 @@ export interface UseChoiceInputOptions {
30
31
  /** Inline choices — synthesized into a static context, ignoring any ambient one. */
31
32
  choices?: readonly (string | ChoiceRecord)[];
32
33
  /** The FK/field path (falls back to the ambient reference context's source). */
33
- source?: string;
34
+ source?: SourceProp | undefined;
34
35
  /** The display path or a formatter. */
35
36
  optionText?: OptionText;
36
37
  /** The value path. Default `id` (via `useChoices`). */
@@ -57,7 +57,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
57
57
  createVNode(_sfc_main$1, {
58
58
  variant: "ghost",
59
59
  size: "icon",
60
- "aria-label": unref(translate)("ra.navigation.previous", { _: "Previous" }),
60
+ "aria-label": unref(translate)("ra.calendar.previous", { _: "Previous" }),
61
61
  onClick: _cache[0] || (_cache[0] = ($event) => emit("navigate", "previous"))
62
62
  }, {
63
63
  default: withCtx(() => [
@@ -78,7 +78,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
78
78
  createVNode(_sfc_main$1, {
79
79
  variant: "ghost",
80
80
  size: "icon",
81
- "aria-label": unref(translate)("ra.navigation.next", { _: "Next" }),
81
+ "aria-label": unref(translate)("ra.calendar.next", { _: "Next" }),
82
82
  onClick: _cache[2] || (_cache[2] = ($event) => emit("navigate", "next"))
83
83
  }, {
84
84
  default: withCtx(() => [
@@ -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",
@@ -1,7 +1,7 @@
1
- import type { LooseRecord } from "@chelone/core";
1
+ import type { LooseRecord, SourceProp } from "@chelone/core";
2
2
  /** Props for {@link DataTableCol}. */
3
3
  export interface DataTableColProps {
4
- source?: string;
4
+ source?: SourceProp;
5
5
  label?: string;
6
6
  /** Applied to both the header and the body cells. */
7
7
  class?: string;
@@ -1,7 +1,8 @@
1
+ import type { SourceProp } from "@chelone/core";
1
2
  import type { DataTableColProps } from "./data-table-col.vue.js";
2
3
  /** Props for {@link DataTableNumberCol}. */
3
4
  export interface DataTableNumberColProps extends Omit<DataTableColProps, "source"> {
4
- source: string;
5
+ source: SourceProp;
5
6
  /** BCP 47 locale(s) for `Intl.NumberFormat`. */
6
7
  locales?: string | string[];
7
8
  /** `Intl.NumberFormatOptions` (currency, percent, fraction digits…). */
@@ -1,5 +1,5 @@
1
1
  import { defineComponent, watch, openBlock, createElementBlock, withModifiers, Fragment, renderList, createBlock, resolveDynamicComponent, mergeProps, unref, createVNode, createCommentVNode, renderSlot } from "vue";
2
- import { mergeFilterValues, getFilterFormValues, filterFormReflectShape } from "@chelone/core";
2
+ import { mergeFilterValues, getFilterFormValues, filterFormReflectShape, getAtPath } from "@chelone/core";
3
3
  import { useTranslate } from "../../../i18n/use-translate.js";
4
4
  import { useForm } from "../../../form/use-form.js";
5
5
  import { useListContext } from "../../../list/list-context.js";
@@ -36,16 +36,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
36
36
  }
37
37
  return false;
38
38
  }
39
- function valueAt(record, source) {
40
- let current = record;
41
- for (const key of source.split(".")) {
42
- if (current == null || typeof current !== "object") return void 0;
43
- current = current[key];
44
- }
45
- return current;
46
- }
47
39
  function isShown(entry) {
48
- return !!entry.alwaysOn || !!list.displayedFilters?.[entry.source] || !isEmptyValue(valueAt(list.filter, entry.source));
40
+ return !!entry.alwaysOn || !!list.displayedFilters?.[entry.source] || !isEmptyValue(getAtPath(list.filter, entry.source));
49
41
  }
50
42
  const { form, values, reset } = useForm({
51
43
  resource,
@@ -18,7 +18,7 @@ import { type LooseRecord } from "@chelone/core";
18
18
  import type { Component } from "vue";
19
19
  /** Props for {@link TreeList}. */
20
20
  export interface TreeListProps {
21
- /** The field holding the parent's id; a dotted path reads a nested value. */
21
+ /** The field holding the parent's id; a dot or bracket path reads a nested value. */
22
22
  parentSource: string;
23
23
  /** The string field shown as the row title; the record representation otherwise. */
24
24
  titleSource?: string;
@@ -1,5 +1,5 @@
1
1
  import { defineComponent, getCurrentInstance, computed, ref, provide, unref, openBlock, createElementBlock, Fragment, renderSlot, createBlock, normalizeClass, renderList } from "vue";
2
- import { buildRecordTree } from "@chelone/core";
2
+ import { buildRecordTree, getAtPath } from "@chelone/core";
3
3
  import { useResourceDefinition } from "../../../admin/resource-registry.js";
4
4
  import { useGetRecordRepresentation } from "../../../label/use-get-record-representation.js";
5
5
  import { cn } from "../../../lib/utils.js";
@@ -58,22 +58,16 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
58
58
  const linkTo = (record) => resource && type.value ? createPath({ resource, id: record.id, type: type.value }) : void 0;
59
59
  function titleFor(record) {
60
60
  if (props.titleSource) {
61
- const value = readPath(record, props.titleSource);
61
+ const value = getAtPath(record, props.titleSource);
62
62
  if (typeof value === "string") return value;
63
63
  }
64
64
  return getRepresentation(record);
65
65
  }
66
66
  function iconFor(record) {
67
67
  if (!props.iconSource || !props.iconMap) return void 0;
68
- const value = readPath(record, props.iconSource);
68
+ const value = getAtPath(record, props.iconSource);
69
69
  return value == null ? void 0 : props.iconMap[String(value)];
70
70
  }
71
- function readPath(record, path) {
72
- return path.split(".").reduce(
73
- (value, key) => value == null ? void 0 : value[key],
74
- record
75
- );
76
- }
77
71
  provide(TreeListKey, {
78
72
  isExpanded,
79
73
  toggle(id) {
@@ -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 = {
@@ -6,7 +6,7 @@ import { useTranslate } from "../../i18n/use-translate.js";
6
6
  import { useNotify } from "../../notification-hooks.js";
7
7
  import { useMapsConfig } from "../maps/use-map.js";
8
8
  import _sfc_main$1 from "../ui/button.vue.js";
9
- import _sfc_main$2 from "../ui/spinner.vue.js";
9
+ import _sfc_main$2 from "../ui/spinner2.vue.js";
10
10
  import { fetchOsmFeatures } from "./osm-features.js";
11
11
  import { subtractMask, unionAll } from "./osm-geometry-ops.js";
12
12
  const _sfc_main = /* @__PURE__ */ defineComponent({
@@ -1,32 +1,4 @@
1
- import { defineComponent, computed, openBlock, createElementBlock, normalizeClass, createElementVNode } from "vue";
2
- import { cn } from "../../lib/utils.js";
3
- const _sfc_main = /* @__PURE__ */ defineComponent({
4
- __name: "spinner",
5
- props: {
6
- class: {}
7
- },
8
- setup(__props) {
9
- const props = __props;
10
- const classes = computed(() => cn("size-4 animate-spin", props.class));
11
- return (_ctx, _cache) => {
12
- return openBlock(), createElementBlock("svg", {
13
- role: "status",
14
- "aria-label": "Loading",
15
- class: normalizeClass(classes.value),
16
- "data-slot": "spinner",
17
- viewBox: "0 0 24 24",
18
- fill: "none",
19
- stroke: "currentColor",
20
- "stroke-width": "2",
21
- "stroke-linecap": "round",
22
- "stroke-linejoin": "round",
23
- "aria-hidden": "false"
24
- }, [..._cache[0] || (_cache[0] = [
25
- createElementVNode("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" }, null, -1)
26
- ])], 2);
27
- };
28
- }
29
- });
1
+ import _sfc_main from "./spinner2.vue.js";
30
2
  export {
31
3
  _sfc_main as default
32
4
  };
@@ -1,4 +1,32 @@
1
- import _sfc_main from "./spinner.vue.js";
1
+ import { defineComponent, computed, openBlock, createElementBlock, normalizeClass, createElementVNode } from "vue";
2
+ import { cn } from "../../lib/utils.js";
3
+ const _sfc_main = /* @__PURE__ */ defineComponent({
4
+ __name: "spinner",
5
+ props: {
6
+ class: {}
7
+ },
8
+ setup(__props) {
9
+ const props = __props;
10
+ const classes = computed(() => cn("size-4 animate-spin", props.class));
11
+ return (_ctx, _cache) => {
12
+ return openBlock(), createElementBlock("svg", {
13
+ role: "status",
14
+ "aria-label": "Loading",
15
+ class: normalizeClass(classes.value),
16
+ "data-slot": "spinner",
17
+ viewBox: "0 0 24 24",
18
+ fill: "none",
19
+ stroke: "currentColor",
20
+ "stroke-width": "2",
21
+ "stroke-linecap": "round",
22
+ "stroke-linejoin": "round",
23
+ "aria-hidden": "false"
24
+ }, [..._cache[0] || (_cache[0] = [
25
+ createElementVNode("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" }, null, -1)
26
+ ])], 2);
27
+ };
28
+ }
29
+ });
2
30
  export {
3
31
  _sfc_main as default
4
32
  };
@@ -5,7 +5,7 @@
5
5
  * the 4R release: the old controller's observer detaches the moment the id
6
6
  * moves on, and the last scope disposal releases the final one.
7
7
  */
8
- import type { Identifier, MicroStore } from "@chelone/core";
8
+ import { type Identifier, type MicroStore } from "@chelone/core";
9
9
  import { type Ref } from "vue";
10
10
  import type { RecordValue } from "../record/record-context.js";
11
11
  export type SourceId = Identifier | undefined;
@@ -13,7 +13,11 @@ export interface BoundController<TController, TState> {
13
13
  controller: TController;
14
14
  state: Readonly<Ref<TState>>;
15
15
  }
16
- /** The foreign key `source` holds on the record; anything but a string or number reads as no key. */
16
+ /**
17
+ * The foreign key `source` holds on the record, read through core's
18
+ * {@link getAtPath} so `source="author.id"` resolves the way the field
19
+ * composables read it. Anything but a string or number reads as no key.
20
+ */
17
21
  export declare function foreignKeyOf(record: RecordValue, source: string): SourceId;
18
22
  /**
19
23
  * Builds `create(id)` inside a scope of its own and keeps it bound to `id`:
@@ -1,7 +1,8 @@
1
+ import { getAtPath } from "@chelone/core";
1
2
  import { shallowRef, watch, onScopeDispose, effectScope } from "vue";
2
3
  import { useMicroStore } from "../store/use-micro-store.js";
3
4
  function foreignKeyOf(record, source) {
4
- const value = record?.[source];
5
+ const value = getAtPath(record, source);
5
6
  return typeof value === "string" || typeof value === "number" ? value : void 0;
6
7
  }
7
8
  function useControllerForId(id, create) {
@@ -26,6 +26,7 @@ export declare function createCheloneHooks<TResources extends ResourceMap>(): {
26
26
  previous?: undefined;
27
27
  key?: undefined;
28
28
  manySnapshot?: undefined;
29
+ rollbackLists?: undefined;
29
30
  } | {
30
31
  previous: import("@chelone/core").RecordOf<TResources, TName> | undefined;
31
32
  key: readonly [string, "getOne", {
@@ -36,10 +37,12 @@ export declare function createCheloneHooks<TResources extends ResourceMap>(): {
36
37
  meta: {} | null;
37
38
  }];
38
39
  manySnapshot: [readonly unknown[], unknown][];
40
+ rollbackLists: () => void;
39
41
  }, Omit<import("@tanstack/query-core").MutationObserverIdleResult<import("@chelone/core").RecordOf<TResources, TName>, unknown, import("@chelone/core").UpdateParams<import("@chelone/core").RecordOf<TResources, TName>>, {
40
42
  previous?: undefined;
41
43
  key?: undefined;
42
44
  manySnapshot?: undefined;
45
+ rollbackLists?: undefined;
43
46
  } | {
44
47
  previous: import("@chelone/core").RecordOf<TResources, TName> | undefined;
45
48
  key: readonly [string, "getOne", {
@@ -50,10 +53,12 @@ export declare function createCheloneHooks<TResources extends ResourceMap>(): {
50
53
  meta: {} | null;
51
54
  }];
52
55
  manySnapshot: [readonly unknown[], unknown][];
56
+ rollbackLists: () => void;
53
57
  }>, "reset" | "mutate"> | Omit<import("@tanstack/query-core").MutationObserverLoadingResult<import("@chelone/core").RecordOf<TResources, TName>, unknown, import("@chelone/core").UpdateParams<import("@chelone/core").RecordOf<TResources, TName>>, {
54
58
  previous?: undefined;
55
59
  key?: undefined;
56
60
  manySnapshot?: undefined;
61
+ rollbackLists?: undefined;
57
62
  } | {
58
63
  previous: import("@chelone/core").RecordOf<TResources, TName> | undefined;
59
64
  key: readonly [string, "getOne", {
@@ -64,10 +69,12 @@ export declare function createCheloneHooks<TResources extends ResourceMap>(): {
64
69
  meta: {} | null;
65
70
  }];
66
71
  manySnapshot: [readonly unknown[], unknown][];
72
+ rollbackLists: () => void;
67
73
  }>, "reset" | "mutate"> | Omit<import("@tanstack/query-core").MutationObserverErrorResult<import("@chelone/core").RecordOf<TResources, TName>, unknown, import("@chelone/core").UpdateParams<import("@chelone/core").RecordOf<TResources, TName>>, {
68
74
  previous?: undefined;
69
75
  key?: undefined;
70
76
  manySnapshot?: undefined;
77
+ rollbackLists?: undefined;
71
78
  } | {
72
79
  previous: import("@chelone/core").RecordOf<TResources, TName> | undefined;
73
80
  key: readonly [string, "getOne", {
@@ -78,10 +85,12 @@ export declare function createCheloneHooks<TResources extends ResourceMap>(): {
78
85
  meta: {} | null;
79
86
  }];
80
87
  manySnapshot: [readonly unknown[], unknown][];
88
+ rollbackLists: () => void;
81
89
  }>, "reset" | "mutate"> | Omit<import("@tanstack/query-core").MutationObserverSuccessResult<import("@chelone/core").RecordOf<TResources, TName>, unknown, import("@chelone/core").UpdateParams<import("@chelone/core").RecordOf<TResources, TName>>, {
82
90
  previous?: undefined;
83
91
  key?: undefined;
84
92
  manySnapshot?: undefined;
93
+ rollbackLists?: undefined;
85
94
  } | {
86
95
  previous: import("@chelone/core").RecordOf<TResources, TName> | undefined;
87
96
  key: readonly [string, "getOne", {
@@ -92,10 +101,12 @@ export declare function createCheloneHooks<TResources extends ResourceMap>(): {
92
101
  meta: {} | null;
93
102
  }];
94
103
  manySnapshot: [readonly unknown[], unknown][];
104
+ rollbackLists: () => void;
95
105
  }>, "reset" | "mutate">> | (Omit<import("@tanstack/vue-query").UseMutationReturnType<import("@chelone/core").RecordOf<TResources, TName>, unknown, import("@chelone/core").UpdateParams<import("@chelone/core").RecordOf<TResources, TName>>, {
96
106
  previous?: undefined;
97
107
  key?: undefined;
98
108
  manySnapshot?: undefined;
109
+ rollbackLists?: undefined;
99
110
  } | {
100
111
  previous: import("@chelone/core").RecordOf<TResources, TName> | undefined;
101
112
  key: readonly [string, "getOne", {
@@ -106,10 +117,12 @@ export declare function createCheloneHooks<TResources extends ResourceMap>(): {
106
117
  meta: {} | null;
107
118
  }];
108
119
  manySnapshot: [readonly unknown[], unknown][];
120
+ rollbackLists: () => void;
109
121
  }, Omit<import("@tanstack/query-core").MutationObserverIdleResult<import("@chelone/core").RecordOf<TResources, TName>, unknown, import("@chelone/core").UpdateParams<import("@chelone/core").RecordOf<TResources, TName>>, {
110
122
  previous?: undefined;
111
123
  key?: undefined;
112
124
  manySnapshot?: undefined;
125
+ rollbackLists?: undefined;
113
126
  } | {
114
127
  previous: import("@chelone/core").RecordOf<TResources, TName> | undefined;
115
128
  key: readonly [string, "getOne", {
@@ -120,10 +133,12 @@ export declare function createCheloneHooks<TResources extends ResourceMap>(): {
120
133
  meta: {} | null;
121
134
  }];
122
135
  manySnapshot: [readonly unknown[], unknown][];
136
+ rollbackLists: () => void;
123
137
  }>, "reset" | "mutate"> | Omit<import("@tanstack/query-core").MutationObserverLoadingResult<import("@chelone/core").RecordOf<TResources, TName>, unknown, import("@chelone/core").UpdateParams<import("@chelone/core").RecordOf<TResources, TName>>, {
124
138
  previous?: undefined;
125
139
  key?: undefined;
126
140
  manySnapshot?: undefined;
141
+ rollbackLists?: undefined;
127
142
  } | {
128
143
  previous: import("@chelone/core").RecordOf<TResources, TName> | undefined;
129
144
  key: readonly [string, "getOne", {
@@ -134,10 +149,12 @@ export declare function createCheloneHooks<TResources extends ResourceMap>(): {
134
149
  meta: {} | null;
135
150
  }];
136
151
  manySnapshot: [readonly unknown[], unknown][];
152
+ rollbackLists: () => void;
137
153
  }>, "reset" | "mutate"> | Omit<import("@tanstack/query-core").MutationObserverErrorResult<import("@chelone/core").RecordOf<TResources, TName>, unknown, import("@chelone/core").UpdateParams<import("@chelone/core").RecordOf<TResources, TName>>, {
138
154
  previous?: undefined;
139
155
  key?: undefined;
140
156
  manySnapshot?: undefined;
157
+ rollbackLists?: undefined;
141
158
  } | {
142
159
  previous: import("@chelone/core").RecordOf<TResources, TName> | undefined;
143
160
  key: readonly [string, "getOne", {
@@ -148,10 +165,12 @@ export declare function createCheloneHooks<TResources extends ResourceMap>(): {
148
165
  meta: {} | null;
149
166
  }];
150
167
  manySnapshot: [readonly unknown[], unknown][];
168
+ rollbackLists: () => void;
151
169
  }>, "reset" | "mutate"> | Omit<import("@tanstack/query-core").MutationObserverSuccessResult<import("@chelone/core").RecordOf<TResources, TName>, unknown, import("@chelone/core").UpdateParams<import("@chelone/core").RecordOf<TResources, TName>>, {
152
170
  previous?: undefined;
153
171
  key?: undefined;
154
172
  manySnapshot?: undefined;
173
+ rollbackLists?: undefined;
155
174
  } | {
156
175
  previous: import("@chelone/core").RecordOf<TResources, TName> | undefined;
157
176
  key: readonly [string, "getOne", {
@@ -162,6 +181,7 @@ export declare function createCheloneHooks<TResources extends ResourceMap>(): {
162
181
  meta: {} | null;
163
182
  }];
164
183
  manySnapshot: [readonly unknown[], unknown][];
184
+ rollbackLists: () => void;
165
185
  }>, "reset" | "mutate">>, "mutate" | "mutateAsync"> & {
166
186
  mutate: (params: import("@chelone/core").UpdateParams<import("@chelone/core").RecordOf<TResources, TName>>, callbacks?: import("@chelone/core").UndoableCallbacks<import("@chelone/core").RecordOf<TResources, TName>, import("@chelone/core").UpdateParams<import("@chelone/core").RecordOf<TResources, TName>>> | undefined) => void;
167
187
  mutateAsync: (params: import("@chelone/core").UpdateParams<import("@chelone/core").RecordOf<TResources, TName>>) => Promise<void>;
@@ -42,6 +42,7 @@ export declare function useUpdate<TResources extends ResourceMap = ResourceMap,
42
42
  previous?: undefined;
43
43
  key?: undefined;
44
44
  manySnapshot?: undefined;
45
+ rollbackLists?: undefined;
45
46
  } | {
46
47
  previous: RecordOf<TResources, TName> | undefined;
47
48
  key: readonly [string, "getOne", {
@@ -52,10 +53,12 @@ export declare function useUpdate<TResources extends ResourceMap = ResourceMap,
52
53
  meta: {} | null;
53
54
  }];
54
55
  manySnapshot: [readonly unknown[], unknown][];
56
+ rollbackLists: () => void;
55
57
  }, Omit<import("@tanstack/query-core").MutationObserverIdleResult<RecordOf<TResources, TName>, unknown, UpdateParams<RecordOf<TResources, TName>>, {
56
58
  previous?: undefined;
57
59
  key?: undefined;
58
60
  manySnapshot?: undefined;
61
+ rollbackLists?: undefined;
59
62
  } | {
60
63
  previous: RecordOf<TResources, TName> | undefined;
61
64
  key: readonly [string, "getOne", {
@@ -66,10 +69,12 @@ export declare function useUpdate<TResources extends ResourceMap = ResourceMap,
66
69
  meta: {} | null;
67
70
  }];
68
71
  manySnapshot: [readonly unknown[], unknown][];
72
+ rollbackLists: () => void;
69
73
  }>, "reset" | "mutate"> | Omit<import("@tanstack/query-core").MutationObserverLoadingResult<RecordOf<TResources, TName>, unknown, UpdateParams<RecordOf<TResources, TName>>, {
70
74
  previous?: undefined;
71
75
  key?: undefined;
72
76
  manySnapshot?: undefined;
77
+ rollbackLists?: undefined;
73
78
  } | {
74
79
  previous: RecordOf<TResources, TName> | undefined;
75
80
  key: readonly [string, "getOne", {
@@ -80,10 +85,12 @@ export declare function useUpdate<TResources extends ResourceMap = ResourceMap,
80
85
  meta: {} | null;
81
86
  }];
82
87
  manySnapshot: [readonly unknown[], unknown][];
88
+ rollbackLists: () => void;
83
89
  }>, "reset" | "mutate"> | Omit<import("@tanstack/query-core").MutationObserverErrorResult<RecordOf<TResources, TName>, unknown, UpdateParams<RecordOf<TResources, TName>>, {
84
90
  previous?: undefined;
85
91
  key?: undefined;
86
92
  manySnapshot?: undefined;
93
+ rollbackLists?: undefined;
87
94
  } | {
88
95
  previous: RecordOf<TResources, TName> | undefined;
89
96
  key: readonly [string, "getOne", {
@@ -94,10 +101,12 @@ export declare function useUpdate<TResources extends ResourceMap = ResourceMap,
94
101
  meta: {} | null;
95
102
  }];
96
103
  manySnapshot: [readonly unknown[], unknown][];
104
+ rollbackLists: () => void;
97
105
  }>, "reset" | "mutate"> | Omit<import("@tanstack/query-core").MutationObserverSuccessResult<RecordOf<TResources, TName>, unknown, UpdateParams<RecordOf<TResources, TName>>, {
98
106
  previous?: undefined;
99
107
  key?: undefined;
100
108
  manySnapshot?: undefined;
109
+ rollbackLists?: undefined;
101
110
  } | {
102
111
  previous: RecordOf<TResources, TName> | undefined;
103
112
  key: readonly [string, "getOne", {
@@ -108,10 +117,12 @@ export declare function useUpdate<TResources extends ResourceMap = ResourceMap,
108
117
  meta: {} | null;
109
118
  }];
110
119
  manySnapshot: [readonly unknown[], unknown][];
120
+ rollbackLists: () => void;
111
121
  }>, "reset" | "mutate">> | Undoable<import("@tanstack/vue-query").UseMutationReturnType<RecordOf<TResources, TName>, unknown, UpdateParams<RecordOf<TResources, TName>>, {
112
122
  previous?: undefined;
113
123
  key?: undefined;
114
124
  manySnapshot?: undefined;
125
+ rollbackLists?: undefined;
115
126
  } | {
116
127
  previous: RecordOf<TResources, TName> | undefined;
117
128
  key: readonly [string, "getOne", {
@@ -122,10 +133,12 @@ export declare function useUpdate<TResources extends ResourceMap = ResourceMap,
122
133
  meta: {} | null;
123
134
  }];
124
135
  manySnapshot: [readonly unknown[], unknown][];
136
+ rollbackLists: () => void;
125
137
  }, Omit<import("@tanstack/query-core").MutationObserverIdleResult<RecordOf<TResources, TName>, unknown, UpdateParams<RecordOf<TResources, TName>>, {
126
138
  previous?: undefined;
127
139
  key?: undefined;
128
140
  manySnapshot?: undefined;
141
+ rollbackLists?: undefined;
129
142
  } | {
130
143
  previous: RecordOf<TResources, TName> | undefined;
131
144
  key: readonly [string, "getOne", {
@@ -136,10 +149,12 @@ export declare function useUpdate<TResources extends ResourceMap = ResourceMap,
136
149
  meta: {} | null;
137
150
  }];
138
151
  manySnapshot: [readonly unknown[], unknown][];
152
+ rollbackLists: () => void;
139
153
  }>, "reset" | "mutate"> | Omit<import("@tanstack/query-core").MutationObserverLoadingResult<RecordOf<TResources, TName>, unknown, UpdateParams<RecordOf<TResources, TName>>, {
140
154
  previous?: undefined;
141
155
  key?: undefined;
142
156
  manySnapshot?: undefined;
157
+ rollbackLists?: undefined;
143
158
  } | {
144
159
  previous: RecordOf<TResources, TName> | undefined;
145
160
  key: readonly [string, "getOne", {
@@ -150,10 +165,12 @@ export declare function useUpdate<TResources extends ResourceMap = ResourceMap,
150
165
  meta: {} | null;
151
166
  }];
152
167
  manySnapshot: [readonly unknown[], unknown][];
168
+ rollbackLists: () => void;
153
169
  }>, "reset" | "mutate"> | Omit<import("@tanstack/query-core").MutationObserverErrorResult<RecordOf<TResources, TName>, unknown, UpdateParams<RecordOf<TResources, TName>>, {
154
170
  previous?: undefined;
155
171
  key?: undefined;
156
172
  manySnapshot?: undefined;
173
+ rollbackLists?: undefined;
157
174
  } | {
158
175
  previous: RecordOf<TResources, TName> | undefined;
159
176
  key: readonly [string, "getOne", {
@@ -164,10 +181,12 @@ export declare function useUpdate<TResources extends ResourceMap = ResourceMap,
164
181
  meta: {} | null;
165
182
  }];
166
183
  manySnapshot: [readonly unknown[], unknown][];
184
+ rollbackLists: () => void;
167
185
  }>, "reset" | "mutate"> | Omit<import("@tanstack/query-core").MutationObserverSuccessResult<RecordOf<TResources, TName>, unknown, UpdateParams<RecordOf<TResources, TName>>, {
168
186
  previous?: undefined;
169
187
  key?: undefined;
170
188
  manySnapshot?: undefined;
189
+ rollbackLists?: undefined;
171
190
  } | {
172
191
  previous: RecordOf<TResources, TName> | undefined;
173
192
  key: readonly [string, "getOne", {
@@ -178,6 +197,7 @@ export declare function useUpdate<TResources extends ResourceMap = ResourceMap,
178
197
  meta: {} | null;
179
198
  }];
180
199
  manySnapshot: [readonly unknown[], unknown][];
200
+ rollbackLists: () => void;
181
201
  }>, "reset" | "mutate">>, UpdateParams<RecordOf<TResources, TName>>, RecordOf<TResources, TName>>;
182
202
  /**
183
203
  * Delete-one mutation for `resource`. Returns vue-query's mutation object; call
@@ -17,11 +17,11 @@
17
17
  * server field errors (mapped onto the form's `onSubmit` channel) both surface
18
18
  * here once the field is registered.
19
19
  */
20
- import type { TranslatableMessage, Validator } from "@chelone/core";
20
+ import type { SourcePath, TranslatableMessage, Validator } from "@chelone/core";
21
21
  import { type ComputedRef } from "vue";
22
- export interface UseInputOptions {
22
+ export interface UseInputOptions<TRecord = unknown> {
23
23
  /** The field path RELATIVE to the ambient source scope (iterator rows compose it). */
24
- source: string;
24
+ source: SourcePath<TRecord>;
25
25
  /**
26
26
  * Input-level default (ra precedence): seeds ONLY into a hole (an untouched
27
27
  * field whose form value is `undefined`); an existing record value wins.
@@ -57,4 +57,5 @@ export interface UseInputReturn {
57
57
  /** Whether a required validator is present (or `isRequired` was forced). */
58
58
  isRequired: boolean;
59
59
  }
60
+ export declare function useInput<TRecord>(options: UseInputOptions<TRecord>): UseInputReturn;
60
61
  export declare function useInput(options: UseInputOptions): UseInputReturn;
@@ -12,7 +12,8 @@
12
12
  *
13
13
  * The resource comes from the argument or the ambient `<Resource>` name. The
14
14
  * registry is optional (a form/field used without `<Admin>` in a unit test):
15
- * with no registry, only the default chain applies.
15
+ * with no registry, only the default chain applies. The string-path read is
16
+ * core's `getAtPath`, the same reader `useFieldValue` uses.
16
17
  */
17
18
  /** The resolver `useGetRecordRepresentation` returns. */
18
19
  export type GetRecordRepresentation = (record: unknown) => string;
@@ -1,19 +1,7 @@
1
+ import { getAtPath } from "@chelone/core";
1
2
  import { inject } from "vue";
2
3
  import { ResourceRegistryKey } from "../admin/resource-registry.js";
3
4
  import { useResourceContext } from "../resource-context.js";
4
- function getAtPath(record, source) {
5
- if (source === "") return record;
6
- let current = record;
7
- const pattern = /\[([^\]]*)\]|[^.[\]]+/g;
8
- let match = pattern.exec(source);
9
- while (match !== null) {
10
- if (current == null || typeof current !== "object") return void 0;
11
- const key = match[1] !== void 0 ? match[1] : match[0];
12
- current = current[key];
13
- match = pattern.exec(source);
14
- }
15
- return current;
16
- }
17
5
  function isPresent(value) {
18
6
  return value != null && value !== "";
19
7
  }
@@ -5,13 +5,14 @@
5
5
  * clean-room, zero-lodash). Returns a `ComputedRef` so a field re-renders when
6
6
  * the ambient record resolves (React re-renders instead; Vue tracks the ref).
7
7
  *
8
- * Path semantics match `lodash/get`: dot members (`author.name`), bracket
9
- * indices (`tags[1].label`), and numeric-dot indices (`tags.0.label`) resolve;
10
- * `defaultValue` replaces ONLY a resolved `undefined`, never a present `null` /
11
- * `0` / `false` / `""`. Reads from the RecordContext (NOT a source scope) on
12
- * purpose — a field tracks the record, not the form's source prefix.
8
+ * Path semantics match `lodash/get` through core's `getAtPath`: dot members
9
+ * (`author.name`), bracket indices (`tags[1].label`), numeric-dot indices
10
+ * (`tags.0.label`) and a literal `"a.b"` key all resolve; `defaultValue`
11
+ * replaces ONLY a resolved `undefined`, never a present `null` / `0` / `false`
12
+ * / `""`. Reads from the RecordContext (NOT a source scope) on purpose — a
13
+ * field tracks the record, not the form's source prefix.
13
14
  */
14
- import type { LooseRecord } from "@chelone/core";
15
+ import { type LooseRecord } from "@chelone/core";
15
16
  import { type ComputedRef } from "vue";
16
17
  import { type RecordValue } from "./record-context.js";
17
18
  export interface UseFieldValueOptions<RecordType = LooseRecord> {
@@ -1,30 +1,13 @@
1
+ import { getAtPath } from "@chelone/core";
1
2
  import { inject, computed } from "vue";
2
3
  import { RecordContextKey } from "./record-context.js";
3
- function toPathSegments(source) {
4
- if (source === "") return [];
5
- const segments = [];
6
- const pattern = /\[([^\]]*)\]|[^.[\]]+/g;
7
- let match = pattern.exec(source);
8
- while (match !== null) {
9
- segments.push(match[1] !== void 0 ? match[1] : match[0]);
10
- match = pattern.exec(source);
11
- }
12
- return segments;
13
- }
14
- function getAtPath(record, source, defaultValue) {
15
- let current = record;
16
- for (const key of toPathSegments(source)) {
17
- if (current == null || typeof current !== "object") return defaultValue;
18
- current = current[key];
19
- }
20
- return current === void 0 ? defaultValue : current;
21
- }
22
4
  function useFieldValue(options) {
23
5
  const { source, defaultValue } = options;
24
6
  const injected = inject(RecordContextKey, null);
25
7
  return computed(() => {
26
8
  const record = options.record !== void 0 ? options.record : injected?.value;
27
- return getAtPath(record, source, defaultValue);
9
+ const value = getAtPath(record, source);
10
+ return value === void 0 ? defaultValue : value;
28
11
  });
29
12
  }
30
13
  export {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chelone/vue",
3
- "version": "0.1.1",
3
+ "version": "0.3.0",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -57,7 +57,7 @@
57
57
  "dependencies": {
58
58
  "cronstrue": "^3.24.0",
59
59
  "libphonenumber-js": "^1.13.8",
60
- "@chelone/core": "0.1.1"
60
+ "@chelone/core": "0.3.0"
61
61
  },
62
62
  "peerDependencies": {
63
63
  "@guolao/vue-monaco-editor": "^1.6.0",
@@ -76,7 +76,7 @@
76
76
  "terra-draw-maplibre-gl-adapter": "^1.5.0",
77
77
  "vue": "^3.4.0",
78
78
  "vue-router": "^4.0.0",
79
- "@chelone/supabase": "0.1.1"
79
+ "@chelone/supabase": "0.3.0"
80
80
  },
81
81
  "peerDependenciesMeta": {
82
82
  "@chelone/supabase": {
@@ -150,7 +150,7 @@
150
150
  "vue-router": "^4.0.0",
151
151
  "vue-tsc": "^2.0.0",
152
152
  "zod": "^3.24.0",
153
- "@chelone/supabase": "0.1.1"
153
+ "@chelone/supabase": "0.3.0"
154
154
  },
155
155
  "scripts": {
156
156
  "build": "vite build && vue-tsc -p tsconfig.build.json && node ../../scripts/strip-dts-css-imports.mjs dist && node ../../scripts/dts-extensions.mjs dist",
@@ -1,7 +0,0 @@
1
- /**
2
- * Read a value at a dot path off `record`. Clean-room stand-in for
3
- * `lodash/get(record, path)` (chelone stays zero-lodash) — shared by the
4
- * per-item title/src lookups in `<FileField>` / `<ImageField>`'s array branch.
5
- * Mirrors the identical helper in the React twins.
6
- */
7
- export declare function getPath(record: unknown, path: string): unknown;
@@ -1,14 +0,0 @@
1
- function getPath(record, path) {
2
- if (record != null && typeof record === "object" && path in record) {
3
- return record[path];
4
- }
5
- let current = record;
6
- for (const key of path.split(".")) {
7
- if (current == null || typeof current !== "object") return void 0;
8
- current = current[key];
9
- }
10
- return current;
11
- }
12
- export {
13
- getPath
14
- };