@payfit/unity-components 2.1.4 → 2.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 (35) hide show
  1. package/dist/esm/components/avatar/Avatar.context.d.ts +2 -1
  2. package/dist/esm/components/avatar/Avatar.context.js +13 -11
  3. package/dist/esm/components/avatar/Avatar.d.ts +126 -0
  4. package/dist/esm/components/avatar/Avatar.js +34 -20
  5. package/dist/esm/components/avatar/Avatar.variants.d.ts +39 -0
  6. package/dist/esm/components/avatar/Avatar.variants.js +22 -4
  7. package/dist/esm/components/avatar/parts/AvatarFallback.d.ts +52 -0
  8. package/dist/esm/components/avatar/parts/AvatarIcon.d.ts +31 -0
  9. package/dist/esm/components/avatar/parts/AvatarIcon.js +40 -0
  10. package/dist/esm/components/button/Button.js +8 -7
  11. package/dist/esm/components/dialog/Dialog.js +32 -31
  12. package/dist/esm/components/dialog/test-utils.d.ts +7 -1
  13. package/dist/esm/components/dialog/test-utils.js +39 -28
  14. package/dist/esm/components/inline-field-group/InlineFieldGroup.context.d.ts +23 -0
  15. package/dist/esm/components/inline-field-group/InlineFieldGroup.context.js +6 -0
  16. package/dist/esm/components/inline-field-group/InlineFieldGroup.d.ts +119 -0
  17. package/dist/esm/components/inline-field-group/InlineFieldGroup.js +185 -0
  18. package/dist/esm/components/inline-field-group/hooks/useInlineFieldGroupMode.d.ts +46 -0
  19. package/dist/esm/components/inline-field-group/hooks/useInlineFieldGroupMode.js +27 -0
  20. package/dist/esm/components/inline-field-group/parts/InlineFieldGroupEditView.d.ts +64 -0
  21. package/dist/esm/components/inline-field-group/parts/InlineFieldGroupEditView.js +56 -0
  22. package/dist/esm/components/inline-field-group/parts/InlineFieldGroupHeader.d.ts +95 -0
  23. package/dist/esm/components/inline-field-group/parts/InlineFieldGroupHeader.js +106 -0
  24. package/dist/esm/components/inline-field-group/parts/InlineFieldGroupReadView.d.ts +56 -0
  25. package/dist/esm/components/inline-field-group/parts/InlineFieldGroupReadView.js +28 -0
  26. package/dist/esm/components/side-panel/parts/SidePanelFooter.js +19 -10
  27. package/dist/esm/hooks/tanstack-form-context.d.ts +1 -1
  28. package/dist/esm/hooks/use-tanstack-form.d.ts +32 -8
  29. package/dist/esm/hooks/use-tanstack-form.js +71 -48
  30. package/dist/esm/index.d.ts +1 -0
  31. package/dist/esm/index.js +445 -443
  32. package/i18n/en-GB.json +6 -0
  33. package/i18n/es-ES.json +6 -0
  34. package/i18n/fr-FR.json +6 -0
  35. package/package.json +21 -21
@@ -1,4 +1,4 @@
1
- import { jsx as o, jsxs as c } from "react/jsx-runtime";
1
+ import { jsx as o, jsxs as p } from "react/jsx-runtime";
2
2
  import { Children as l, isValidElement as i } from "react";
3
3
  import { uyTv as f } from "@payfit/unity-themes";
4
4
  import { useId as w } from "react-aria";
@@ -28,7 +28,7 @@ const T = f({
28
28
  "uy:data-[entering]:animate-slide-up-fade-in uy:data-[exiting]:animate-slide-down-fade-out",
29
29
  "uy:md:[animation-delay:100ms] uy:md:data-[entering]:animate-zoom-in uy:md:data-[exiting]:animate-zoom-out"
30
30
  ],
31
- content: ["uy:group/dialog", "uy:outline-none uy:relative"],
31
+ content: ["uy:group/dialog", "uy:outline-none"],
32
32
  dismissIcon: ["uy:absolute uy:right-200 uy:top-200 uy:z-20"]
33
33
  },
34
34
  variants: {
@@ -62,7 +62,7 @@ function B({
62
62
  size: n = "md",
63
63
  ...d
64
64
  }) {
65
- const m = z(), u = w(), { overlay: y, wrapper: s, content: g, dismissIcon: p } = T({ size: n });
65
+ const m = z(), u = w(), { overlay: y, wrapper: s, content: g, dismissIcon: c } = T({ size: n });
66
66
  return /* @__PURE__ */ o(
67
67
  x,
68
68
  {
@@ -70,34 +70,35 @@ function B({
70
70
  isOpen: t,
71
71
  onOpenChange: a,
72
72
  className: y(),
73
- children: /* @__PURE__ */ c(v, { className: s(), "data-unity-dialog": !0, "data-dd-privacy": "allow", children: [
74
- r && /* @__PURE__ */ o(
75
- I,
76
- {
77
- icon: "CloseOutlined",
78
- color: "content.neutral.low",
79
- title: m.formatMessage({
80
- id: "unity:component:common:close:label"
81
- }),
82
- className: p(),
83
- slot: "close",
84
- size: "large"
85
- }
86
- ),
87
- /* @__PURE__ */ o(h.Provider, { value: { "aria-describedby": u }, children: /* @__PURE__ */ o(
88
- b,
89
- {
90
- role: j(e) ? "alertdialog" : "dialog",
91
- "aria-modal": "true",
92
- className: g(),
93
- "aria-label": d["aria-label"],
94
- "aria-describedby": u,
95
- "data-unity-slot": "dialog",
96
- "data-unity-size": n,
97
- children: e
98
- }
99
- ) })
100
- ] })
73
+ children: /* @__PURE__ */ o(v, { className: s(), "data-unity-dialog": !0, "data-dd-privacy": "allow", children: /* @__PURE__ */ o(h.Provider, { value: { "aria-describedby": u }, children: /* @__PURE__ */ p(
74
+ b,
75
+ {
76
+ role: j(e) ? "alertdialog" : "dialog",
77
+ "aria-modal": "true",
78
+ className: g(),
79
+ "aria-label": d["aria-label"],
80
+ "aria-describedby": u,
81
+ "data-unity-slot": "dialog",
82
+ "data-unity-size": n,
83
+ children: [
84
+ r && /* @__PURE__ */ o(
85
+ I,
86
+ {
87
+ icon: "CloseOutlined",
88
+ color: "content.neutral.low",
89
+ title: m.formatMessage({
90
+ id: "unity:component:common:close:label"
91
+ }),
92
+ className: c(),
93
+ slot: "close",
94
+ size: "large",
95
+ asElement: "button"
96
+ }
97
+ ),
98
+ e
99
+ ]
100
+ }
101
+ ) }) })
101
102
  }
102
103
  );
103
104
  }
@@ -1,12 +1,14 @@
1
+ import { IntlShape } from 'react-intl';
1
2
  import { PlayCtx } from '../../types/testing.js';
2
3
  /**
3
4
  * Factory function returning a set of helpers to test Unity Dialog components.
4
5
  * It wires Storybook's `Play` context with convenient utilities to query,
5
6
  * assert, and interact with dialogs rendered on the canvas.
6
7
  * @param context The Storybook play context used to group steps and actions.
8
+ * @param globalIntl The global intl instance to use for localization.
7
9
  * @returns An object of helpers: `findDialog`, `closeDialog`, `assertDialogIsClosed`, `assertElementExistsInDialog`, and `triggerPrimaryAction`.
8
10
  */
9
- export declare const getTestingUtilsDialog: (context: PlayCtx) => {
11
+ export declare const getTestingUtilsDialog: (context: PlayCtx, globalIntl: IntlShape) => {
10
12
  findDialog: ({ title }: {
11
13
  title: string;
12
14
  }) => Promise<HTMLElement>;
@@ -14,6 +16,10 @@ export declare const getTestingUtilsDialog: (context: PlayCtx) => {
14
16
  title: string;
15
17
  closeButtonLabel: string;
16
18
  }) => Promise<void>;
19
+ closeDialogWithDismissButton: ({ title, dismissButtonLabel, }: {
20
+ title: string;
21
+ dismissButtonLabel?: string;
22
+ }) => Promise<void>;
17
23
  assertDialogIsClosed: (dialog: HTMLElement) => Promise<void>;
18
24
  assertElementExistsInDialog: ({ title, content, primaryActionLabel, secondaryActionLabel, }: {
19
25
  title: string;
@@ -1,31 +1,42 @@
1
- import { userEvent as c, within as o, expect as n, waitFor as r, screen as w } from "storybook/test";
2
- const f = (i) => {
3
- const s = async ({ title: a }) => await r(() => {
4
- const t = w.queryByRole("dialog", { name: a }) ?? w.queryByRole("alertdialog", { name: a });
1
+ import { userEvent as r, within as s, expect as n, waitFor as w, screen as y } from "storybook/test";
2
+ const p = (e, m) => {
3
+ const o = async ({ title: a }) => await w(() => {
4
+ const t = y.queryByRole("dialog", { name: a }) ?? y.queryByRole("alertdialog", { name: a });
5
5
  if (!t)
6
6
  throw new Error(`Dialog with title: "${a}" not found in the page`);
7
7
  return t;
8
- }), y = async (a, t) => {
9
- await c.click(
10
- o(a).getByRole("button", {
8
+ }), c = async (a, t) => {
9
+ await r.click(
10
+ s(a).getByRole("button", {
11
11
  name: t
12
12
  }),
13
13
  { delay: 100 }
14
14
  );
15
15
  };
16
16
  return {
17
- findDialog: s,
17
+ findDialog: o,
18
18
  closeDialog: async ({
19
19
  title: a,
20
20
  closeButtonLabel: t
21
21
  }) => {
22
- await i.step("Close dialog", async () => {
23
- const e = await s({ title: a });
24
- await y(e, t);
22
+ await e.step("Close dialog", async () => {
23
+ const i = await o({ title: a });
24
+ await c(i, t);
25
+ });
26
+ },
27
+ closeDialogWithDismissButton: async ({
28
+ title: a,
29
+ dismissButtonLabel: t = m.formatMessage({
30
+ id: "unity:component:common:close:label"
31
+ })
32
+ }) => {
33
+ await e.step("Close dialog", async () => {
34
+ const i = await o({ title: a });
35
+ await c(i, t);
25
36
  });
26
37
  },
27
38
  assertDialogIsClosed: async (a) => {
28
- await r(
39
+ await w(
29
40
  async () => {
30
41
  await n(a).not.toBeInTheDocument();
31
42
  },
@@ -35,24 +46,24 @@ const f = (i) => {
35
46
  assertElementExistsInDialog: async ({
36
47
  title: a,
37
48
  content: t,
38
- primaryActionLabel: e,
39
- secondaryActionLabel: g
49
+ primaryActionLabel: i,
50
+ secondaryActionLabel: l
40
51
  }) => {
41
- const l = await s({ title: a });
42
- t && await i.step(`Check if "${t}" is in the dialog`, async () => {
43
- await n(o(l).getByText(t)).toBeInTheDocument();
44
- }), e && await i.step(
45
- `Check if "${e}" is in the dialog`,
52
+ const g = await o({ title: a });
53
+ t && await e.step(`Check if "${t}" is in the dialog`, async () => {
54
+ await n(s(g).getByText(t)).toBeInTheDocument();
55
+ }), i && await e.step(
56
+ `Check if "${i}" is in the dialog`,
46
57
  async () => {
47
58
  await n(
48
- o(l).getByRole("button", { name: e })
59
+ s(g).getByRole("button", { name: i })
49
60
  ).toBeInTheDocument();
50
61
  }
51
- ), g && await i.step(
52
- `Check if "${g}" is in the dialog`,
62
+ ), l && await e.step(
63
+ `Check if "${l}" is in the dialog`,
53
64
  async () => {
54
65
  await n(
55
- o(l).getByRole("button", { name: g })
66
+ s(g).getByRole("button", { name: l })
56
67
  ).toBeInTheDocument();
57
68
  }
58
69
  );
@@ -61,12 +72,12 @@ const f = (i) => {
61
72
  title: a,
62
73
  primaryActionLabel: t
63
74
  }) => {
64
- const e = await s({ title: a });
65
- await i.step(
75
+ const i = await o({ title: a });
76
+ await e.step(
66
77
  `Trigger the primary action "${t}" of the dialog`,
67
78
  async () => {
68
- await c.click(
69
- o(e).getByRole("button", { name: t })
79
+ await r.click(
80
+ s(i).getByRole("button", { name: t })
70
81
  );
71
82
  }
72
83
  );
@@ -74,5 +85,5 @@ const f = (i) => {
74
85
  };
75
86
  };
76
87
  export {
77
- f as getTestingUtilsDialog
88
+ p as getTestingUtilsDialog
78
89
  };
@@ -0,0 +1,23 @@
1
+ import { RefObject } from 'react';
2
+ export type InlineFieldGroupMode = 'read' | 'edit';
3
+ export interface InlineFieldGroupContextValue {
4
+ /** Current mode: read or edit */
5
+ mode: InlineFieldGroupMode;
6
+ /** Switch to edit mode */
7
+ enterEditMode: () => void;
8
+ /** Exit edit mode (cancel or save) */
9
+ exitEditMode: () => void;
10
+ /** Unique ID for the field group (for aria attributes) */
11
+ groupId: string;
12
+ /** ID for the header title (for aria-labelledby) */
13
+ headerId: string;
14
+ /** ID for the edit view container (for aria-controls) */
15
+ editViewId: string;
16
+ /** Whether the component is in a loading state */
17
+ isLoading?: boolean;
18
+ /** Ref to the edit button for focus management */
19
+ editButtonRef?: RefObject<HTMLButtonElement>;
20
+ /** Ref to the edit view container for focus management */
21
+ editViewRef?: RefObject<HTMLFieldSetElement>;
22
+ }
23
+ export declare const InlineFieldGroupContext: import('react').Context<InlineFieldGroupContextValue | undefined>;
@@ -0,0 +1,6 @@
1
+ import { createContext as e } from "react";
2
+ const o = e(void 0);
3
+ o.displayName = "InlineFieldGroupContext";
4
+ export {
5
+ o as InlineFieldGroupContext
6
+ };
@@ -0,0 +1,119 @@
1
+ import { default as React, PropsWithChildren } from 'react';
2
+ import { InlineFieldGroupMode } from './InlineFieldGroup.context.js';
3
+ /**
4
+ * Imperative handle for InlineFieldGroup component.
5
+ * Provides methods for programmatic control when needed for advanced use cases.
6
+ */
7
+ export interface InlineFieldGroupHandle {
8
+ /**
9
+ * Focuses the first invalid field in the edit view.
10
+ * Useful for custom validation error handling.
11
+ */
12
+ focusFirstInvalidField: () => void;
13
+ /**
14
+ * Exits edit mode programmatically.
15
+ */
16
+ exitEditMode: () => void;
17
+ /**
18
+ * Enters edit mode programmatically.
19
+ */
20
+ enterEditMode: () => void;
21
+ }
22
+ export interface InlineFieldGroupProps extends PropsWithChildren {
23
+ /**
24
+ * Controlled mode value. When provided, the component operates in controlled mode.
25
+ */
26
+ mode?: InlineFieldGroupMode;
27
+ /**
28
+ * Default mode value for uncontrolled mode.
29
+ * @default 'read'
30
+ */
31
+ defaultMode?: InlineFieldGroupMode;
32
+ /**
33
+ * Callback fired when mode changes.
34
+ */
35
+ onModeChange?: (mode: InlineFieldGroupMode) => void;
36
+ /**
37
+ * Callback to intercept and potentially prevent mode changes.
38
+ * Return `false` to prevent the mode transition.
39
+ * Return `true` or `undefined` to allow the transition.
40
+ */
41
+ shouldModeChange?: (fromMode: InlineFieldGroupMode, toMode: InlineFieldGroupMode) => boolean | undefined;
42
+ /**
43
+ * Whether the component is in a loading state (e.g., during async save).
44
+ */
45
+ isLoading?: boolean;
46
+ /**
47
+ * Optional className for custom styling
48
+ */
49
+ className?: string;
50
+ /**
51
+ * Optional aria-label for the form element.
52
+ * If not provided, aria-labelledby will reference the header title.
53
+ */
54
+ 'aria-label'?: string;
55
+ /**
56
+ * Success message to announce when save succeeds.
57
+ * If not provided, no success announcement is made.
58
+ */
59
+ successMessage?: string;
60
+ /**
61
+ * Error message to announce when save or validation fails.
62
+ * If not provided, generic validation errors are announced.
63
+ */
64
+ errorMessage?: string;
65
+ }
66
+ /**
67
+ * InlineFieldGroup enables group-level inline editing with read/edit mode switching.
68
+ * It integrates with TanStack Form for validation and state management, providing
69
+ * a complete pattern for displaying data in read mode and editing it in edit mode.
70
+ * The component handles the full edit lifecycle: mode transitions, form submission,
71
+ * validation error handling, and accessibility announcements.
72
+ * @example
73
+ * ```tsx
74
+ * import { useTanstackUnityForm } from '@payfit/unity-components'
75
+ *
76
+ * function ContactForm() {
77
+ * const form = useTanstackUnityForm({
78
+ * defaultValues: { email: 'user@example.com', phone: '+1234567890' },
79
+ * onSubmit: async ({ value }) => {
80
+ * await saveContact(value)
81
+ * }
82
+ * })
83
+ *
84
+ * return (
85
+ * <form.AppForm>
86
+ * <form.InlineFieldGroup successMessage="Contact saved!">
87
+ * <form.InlineFieldGroupHeader title="Contact Information" />
88
+ * <form.InlineFieldGroupReadView>
89
+ * <DefinitionList>
90
+ * <DefinitionItem term="Email" description={form.state.values.email} />
91
+ * </DefinitionList>
92
+ * </form.InlineFieldGroupReadView>
93
+ * <form.InlineFieldGroupEditView legend="Edit contact">
94
+ * <form.AppField name="email">
95
+ * {field => <field.TextField label="Email" />}
96
+ * </form.AppField>
97
+ * </form.InlineFieldGroupEditView>
98
+ * </form.InlineFieldGroup>
99
+ * </form.AppForm>
100
+ * )
101
+ * }
102
+ * ```
103
+ * @remarks
104
+ * - The component automatically exits edit mode on successful form submission
105
+ * - Press Escape to cancel editing and reset form values
106
+ * - Focus moves to the first form field when entering edit mode
107
+ * - Focus returns to the edit button when exiting edit mode
108
+ * - Focus is retained under a scope when in edit mode, to prevent users for leaving unfinished changes
109
+ * - Use `shouldModeChange` to intercept and conditionally prevent mode transitions
110
+ * - Use `successMessage` and `errorMessage` for accessible announcements via live regions
111
+ * @see {@link InlineFieldGroupProps} for all available props
112
+ * @see {@link InlineFieldGroupHandle} for imperative handle methods
113
+ * @see {@link InlineFieldGroupHeader} for the header component with action buttons
114
+ * @see {@link InlineFieldGroupReadView} for read mode content
115
+ * @see {@link InlineFieldGroupEditView} for edit mode form fields
116
+ * @see Source code in {@link https://github.com/PayFit/hr-apps/tree/master/libs/shared/unity/components/src/components/inline-field-group GitHub}
117
+ * @see Developer docs in {@link https://unity-components.payfit.io/?path=/docs/forms-reference-inlinefieldgroup--docs unity-components.payfit.io}
118
+ */
119
+ export declare const InlineFieldGroup: React.ForwardRefExoticComponent<InlineFieldGroupProps & React.RefAttributes<InlineFieldGroupHandle>>;
@@ -0,0 +1,185 @@
1
+ import { jsx as x, jsxs as z } from "react/jsx-runtime";
2
+ import { forwardRef as J, useRef as l, useState as V, useEffect as r, useCallback as P, useImperativeHandle as Q, useMemo as W } from "react";
3
+ import { uyTv as X } from "@payfit/unity-themes";
4
+ import { useStore as Y } from "@tanstack/react-form";
5
+ import { useId as Z, useKeyboard as L, FocusScope as ee } from "react-aria";
6
+ import { useIntl as te } from "react-intl";
7
+ import { useFormContext as ie } from "../../hooks/tanstack-form-context.js";
8
+ import { useInlineFieldGroupMode as re } from "./hooks/useInlineFieldGroupMode.js";
9
+ import { InlineFieldGroupContext as oe } from "./InlineFieldGroup.context.js";
10
+ const se = X({
11
+ slots: {
12
+ form: "uy:flex uy:flex-col uy:gap-300"
13
+ }
14
+ }), D = (a) => {
15
+ if (!a.current) return;
16
+ const c = a.current.querySelector(
17
+ '[aria-invalid="true"]'
18
+ );
19
+ c && c.focus();
20
+ }, k = 5e3, ne = J(
21
+ ({
22
+ children: a,
23
+ mode: c,
24
+ defaultMode: j,
25
+ onModeChange: q,
26
+ shouldModeChange: m,
27
+ isLoading: N,
28
+ className: K,
29
+ "aria-label": _,
30
+ successMessage: g,
31
+ errorMessage: d
32
+ }, O) => {
33
+ const s = Z(), A = `unity-InlineFieldGroup-${s}__header`, C = `unity-InlineFieldGroup-${s}__edit-view`, E = l(null), n = l(null), M = l(!0), [G, R] = V(""), [h, f] = V(""), p = te(), u = ie(), { mode: t, enterEditMode: b, exitEditMode: o } = re({
34
+ mode: c,
35
+ defaultMode: j,
36
+ onModeChange: q,
37
+ shouldModeChange: m
38
+ }), { isSubmitting: i, isValid: v, isSubmitSuccessful: S, submissionAttempts: y } = Y(u.store, (e) => ({
39
+ isSubmitting: e.isSubmitting,
40
+ isValid: e.isValid,
41
+ isSubmitSuccessful: e.isSubmitSuccessful,
42
+ submissionAttempts: e.submissionAttempts
43
+ })), F = l(i), T = l(y);
44
+ r(() => {
45
+ i && !F.current && (R(""), f(""));
46
+ }, [i]), r(() => {
47
+ if (y > T.current && !v) {
48
+ const I = d ?? p.formatMessage({
49
+ id: "unity:component:inline-field-group:validation-error",
50
+ defaultMessage: "Please fix the errors before saving."
51
+ });
52
+ f(I), D(n);
53
+ }
54
+ }, [y, v, d, p]), r(() => {
55
+ F.current && !i && S && (g && R(g), o());
56
+ }, [i, S, g, o]), r(() => {
57
+ if (F.current && !i && !S && v) {
58
+ const I = d ?? p.formatMessage({
59
+ id: "unity:component:inline-field-group:save-error",
60
+ defaultMessage: "An error occurred while saving. Please try again."
61
+ });
62
+ f(I);
63
+ }
64
+ }, [i, S, v, d, p]), r(() => {
65
+ F.current = i, T.current = y;
66
+ });
67
+ const $ = P(
68
+ (e) => {
69
+ e.preventDefault(), e.stopPropagation(), u.handleSubmit();
70
+ },
71
+ [u]
72
+ ), w = P(() => {
73
+ m !== void 0 && !m(t, "read") || (u.reset(), o());
74
+ }, [u, t, o, m]);
75
+ Q(
76
+ O,
77
+ () => ({
78
+ focusFirstInvalidField: () => {
79
+ D(n);
80
+ },
81
+ exitEditMode: o,
82
+ enterEditMode: b
83
+ }),
84
+ [o, b]
85
+ ), r(() => {
86
+ if (M.current) {
87
+ M.current = !1;
88
+ return;
89
+ }
90
+ if (t === "edit") {
91
+ if (n.current) {
92
+ const e = n.current.querySelectorAll(
93
+ 'input, select, textarea, [tabindex]:not([tabindex="-1"])'
94
+ );
95
+ e.length > 0 && e[0].focus();
96
+ }
97
+ } else
98
+ E.current && E.current.focus();
99
+ }, [t]);
100
+ const { keyboardProps: B } = L({
101
+ onKeyDown: (e) => {
102
+ e.key === "Escape" && t === "edit" && (e.preventDefault(), w());
103
+ }
104
+ });
105
+ r(() => {
106
+ if (G) {
107
+ const e = setTimeout(() => {
108
+ R("");
109
+ }, k);
110
+ return () => {
111
+ clearTimeout(e);
112
+ };
113
+ }
114
+ }, [G]), r(() => {
115
+ if (h) {
116
+ const e = setTimeout(() => {
117
+ f("");
118
+ }, k);
119
+ return () => {
120
+ clearTimeout(e);
121
+ };
122
+ }
123
+ }, [h]);
124
+ const H = W(
125
+ () => ({
126
+ mode: t,
127
+ enterEditMode: b,
128
+ exitEditMode: w,
129
+ groupId: s,
130
+ headerId: A,
131
+ editViewId: C,
132
+ isLoading: N,
133
+ editButtonRef: E,
134
+ editViewRef: n
135
+ }),
136
+ [
137
+ t,
138
+ b,
139
+ w,
140
+ s,
141
+ A,
142
+ C,
143
+ N
144
+ ]
145
+ ), { form: U } = se();
146
+ return /* @__PURE__ */ x(oe.Provider, { value: H, children: /* @__PURE__ */ x(
147
+ "form",
148
+ {
149
+ ...B,
150
+ id: s,
151
+ className: U({ className: K }),
152
+ onSubmit: $,
153
+ "aria-label": _,
154
+ "aria-labelledby": _ ? void 0 : A,
155
+ children: /* @__PURE__ */ z(ee, { contain: t === "edit", children: [
156
+ a,
157
+ /* @__PURE__ */ x(
158
+ "div",
159
+ {
160
+ role: "status",
161
+ "aria-live": "polite",
162
+ "aria-atomic": "true",
163
+ className: "uy:sr-only",
164
+ children: G
165
+ }
166
+ ),
167
+ /* @__PURE__ */ x(
168
+ "div",
169
+ {
170
+ role: "alert",
171
+ "aria-live": "assertive",
172
+ "aria-atomic": "true",
173
+ className: "uy:sr-only",
174
+ children: h
175
+ }
176
+ )
177
+ ] })
178
+ }
179
+ ) });
180
+ }
181
+ );
182
+ ne.displayName = "InlineFieldGroup";
183
+ export {
184
+ ne as InlineFieldGroup
185
+ };
@@ -0,0 +1,46 @@
1
+ import { InlineFieldGroupMode } from '../InlineFieldGroup.context.js';
2
+ export interface UseInlineFieldGroupModeProps {
3
+ /**
4
+ * Controlled mode value. When provided, the component operates in controlled mode.
5
+ */
6
+ mode?: InlineFieldGroupMode;
7
+ /**
8
+ * Default mode value for uncontrolled mode.
9
+ * @default 'read'
10
+ */
11
+ defaultMode?: InlineFieldGroupMode;
12
+ /**
13
+ * Callback fired when mode changes.
14
+ */
15
+ onModeChange?: (mode: InlineFieldGroupMode) => void;
16
+ /**
17
+ * Callback to intercept and potentially prevent mode changes.
18
+ * Return `false` to prevent the mode transition.
19
+ * Return `true` or `undefined` to allow the transition.
20
+ */
21
+ shouldModeChange?: (fromMode: InlineFieldGroupMode, toMode: InlineFieldGroupMode) => boolean | undefined;
22
+ }
23
+ export interface UseInlineFieldGroupModeReturn {
24
+ /** Current mode value */
25
+ mode: InlineFieldGroupMode;
26
+ /** Switch to edit mode */
27
+ enterEditMode: () => void;
28
+ /** Exit edit mode (return to read mode) */
29
+ exitEditMode: () => void;
30
+ /** Check if component is in controlled mode */
31
+ isControlled: boolean;
32
+ }
33
+ /**
34
+ * Hook to manage the mode state (read/edit) for InlineFieldGroup.
35
+ * Supports both controlled and uncontrolled modes, following the pattern
36
+ * established by NavGroup.tsx
37
+ * @param props - Configuration for the mode hook
38
+ * @param props.mode - Controlled mode value. When provided, the component operates in controlled mode.
39
+ * @param props.defaultMode - Default mode value for uncontrolled mode.
40
+ * @param props.onModeChange - Callback fired when mode changes.
41
+ * @param props.shouldModeChange - Callback to intercept and potentially prevent mode changes.
42
+ * Return `false` to prevent the mode transition.
43
+ * Return `true` or `undefined` to allow the transition.
44
+ * @returns Mode state and handlers
45
+ */
46
+ export declare function useInlineFieldGroupMode({ mode: controlledMode, defaultMode, onModeChange, shouldModeChange, }: UseInlineFieldGroupModeProps): UseInlineFieldGroupModeReturn;
@@ -0,0 +1,27 @@
1
+ import { useState as m, useCallback as i } from "react";
2
+ function x({
3
+ mode: d,
4
+ defaultMode: c = "read",
5
+ onModeChange: s,
6
+ shouldModeChange: o
7
+ }) {
8
+ const [l, u] = m(c), t = d !== void 0, n = t ? d : l, e = i(
9
+ (r) => {
10
+ o && o(n, r) === !1 || (t || u(r), s?.(r));
11
+ },
12
+ [t, n, s, o]
13
+ ), a = i(() => {
14
+ e("edit");
15
+ }, [e]), f = i(() => {
16
+ e("read");
17
+ }, [e]);
18
+ return {
19
+ mode: n,
20
+ enterEditMode: a,
21
+ exitEditMode: f,
22
+ isControlled: t
23
+ };
24
+ }
25
+ export {
26
+ x as useInlineFieldGroupMode
27
+ };