@payfit/unity-components 2.7.2 → 2.8.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.
@@ -27,6 +27,7 @@ type UnityButtonProps = AriaButtonProps & ButtonBase & {
27
27
  * @default false
28
28
  */
29
29
  isLoading?: boolean;
30
+ truncateLabelLength?: number;
30
31
  };
31
32
  type UnionButtonProps = (UnityButtonProps & {
32
33
  variant: 'primary';
@@ -1,68 +1,78 @@
1
- import { jsx as m, jsxs as y } from "react/jsx-runtime";
2
- import { forwardRef as h, useMemo as x } from "react";
3
- import { uyMerge as l } from "@payfit/unity-themes";
4
- import { Button as b } from "react-aria-components";
1
+ import { jsx as s, jsxs as h } from "react/jsx-runtime";
2
+ import { forwardRef as b, useMemo as w } from "react";
3
+ import { uyMerge as m } from "@payfit/unity-themes";
4
+ import { Button as x } from "react-aria-components";
5
5
  import { Icon as v } from "../icon/Icon.js";
6
6
  import { Spinner as B } from "../spinner/Spinner.js";
7
- import { buttonGhost as w, buttonOutlined as N, buttonFilled as j } from "./Button.variants.js";
8
- const I = (e, n) => e ? /* @__PURE__ */ m(B, { size: "small", color: "inherit", label: "Loading..." }) : n ? /* @__PURE__ */ m(v, { src: n, size: 20, color: "inherit", role: "presentation" }) : null, M = h(
7
+ import { buttonGhost as N, buttonOutlined as j, buttonFilled as I } from "./Button.variants.js";
8
+ const M = (e, r) => e ? /* @__PURE__ */ s(B, { size: "small", color: "inherit", label: "Loading..." }) : r ? /* @__PURE__ */ s(v, { src: r, size: 20, color: "inherit", role: "presentation" }) : null, $ = b(
9
9
  ({
10
10
  variant: e,
11
- children: n,
11
+ children: r,
12
12
  color: o = "primary",
13
13
  size: c = "default",
14
- isDisabled: s = !1,
14
+ isDisabled: a = !1,
15
15
  isLoading: t = !1,
16
- prefixIcon: u = void 0,
17
- ...i
18
- }, p) => {
19
- const f = x(() => {
20
- const a = { size: c, isDisabled: s, isLoading: t };
16
+ prefixIcon: i = void 0,
17
+ truncateLabelLength: u,
18
+ ...p
19
+ }, y) => {
20
+ const f = w(() => {
21
+ const l = { size: c, isDisabled: a, isLoading: t };
21
22
  switch (e) {
22
23
  case "primary": {
23
- const r = {
24
- ...a,
24
+ const n = {
25
+ ...l,
25
26
  color: o
26
27
  };
27
- return l(j(r));
28
+ return m(I(n));
28
29
  }
29
30
  case "secondary": {
30
- const r = {
31
- ...a,
31
+ const n = {
32
+ ...l,
32
33
  color: o
33
34
  };
34
- return l(N(r));
35
+ return m(j(n));
35
36
  }
36
37
  case "ghost": {
37
- const r = {
38
- ...a,
38
+ const n = {
39
+ ...l,
39
40
  color: o
40
41
  };
41
- return l(w(r));
42
+ return m(N(n));
42
43
  }
43
44
  }
44
- }, [e, o, c, s, t]), d = {
45
+ }, [e, o, c, a, t]), d = {
45
46
  ...t && { "data-loading": t }
46
47
  };
47
- return /* @__PURE__ */ m(
48
- b,
48
+ return /* @__PURE__ */ s(
49
+ x,
49
50
  {
50
51
  "data-dd-privacy": "allow",
51
- ...i,
52
- ref: p,
53
- isDisabled: s || t,
52
+ ...p,
53
+ ref: y,
54
+ isDisabled: a || t,
54
55
  isPending: t,
55
56
  className: f,
56
57
  ...d,
57
- children: /* @__PURE__ */ y("span", { className: "uy:inline-flex uy:gap-50 uy:items-center", children: [
58
- I(t, u),
59
- n
58
+ children: /* @__PURE__ */ h("span", { className: "uy:inline-flex uy:gap-50 uy:items-center", children: [
59
+ M(t, i),
60
+ u && u > 0 ? /* @__PURE__ */ s(
61
+ "span",
62
+ {
63
+ style: {
64
+ "--uy-button-label-truncation-length": `${u}ch`
65
+ },
66
+ className: "uy:truncate uy:min-w-0 uy:w-(--uy-button-label-truncation-length)",
67
+ children: r
68
+ }
69
+ ) : r
60
70
  ] })
61
71
  }
62
72
  );
63
73
  }
64
74
  );
65
- M.displayName = "Button";
75
+ $.displayName = "Button";
66
76
  export {
67
- M as Button
77
+ $ as Button
68
78
  };
@@ -0,0 +1,41 @@
1
+ import { FC } from 'react';
2
+ export interface FunnelBackButtonProps {
3
+ /**
4
+ * Label for the back button.
5
+ * Shown as text on desktop, used as aria-label on mobile.
6
+ */
7
+ label: string;
8
+ /**
9
+ * Callback when back button is pressed.
10
+ */
11
+ onPress: () => void;
12
+ /**
13
+ * Maximum length of the label to truncate.
14
+ * Defaults to 10 characters.
15
+ */
16
+ truncateLabelLength?: number;
17
+ }
18
+ /**
19
+ * Back button component for use in FunnelTopBar leading slot.
20
+ * Automatically adapts between desktop (text + icon) and mobile (icon only) layouts.
21
+ * @example
22
+ * ```tsx
23
+ * import { FunnelTopBar, FunnelBackButton } from '@payfit/unity-components'
24
+ *
25
+ * function MyFunnel() {
26
+ * return (
27
+ * <FunnelTopBar
28
+ * leading={
29
+ * <FunnelBackButton
30
+ * label="Back to dashboard"
31
+ * onPress={handleBack}
32
+ * />
33
+ * }
34
+ * title="Step 2"
35
+ * progressValue={50}
36
+ * />
37
+ * )
38
+ * }
39
+ * ```
40
+ */
41
+ export declare const FunnelBackButton: FC<FunnelBackButtonProps>;
@@ -0,0 +1,35 @@
1
+ import { jsx as n } from "react/jsx-runtime";
2
+ import { useBreakpointListener as i } from "../../../hooks/use-breakpoint-listener.js";
3
+ import { Button as s } from "../../button/Button.js";
4
+ import { IconButton as c } from "../../icon-button/IconButton.js";
5
+ const u = ({
6
+ label: t,
7
+ onPress: o,
8
+ truncateLabelLength: r
9
+ }) => {
10
+ const e = i();
11
+ return !["xs", "sm"].includes(e) ? /* @__PURE__ */ n(
12
+ s,
13
+ {
14
+ variant: "ghost",
15
+ color: "neutral",
16
+ prefixIcon: "ArrowLeftOutlined",
17
+ onPress: o,
18
+ truncateLabelLength: r,
19
+ children: t
20
+ }
21
+ ) : /* @__PURE__ */ n(
22
+ c,
23
+ {
24
+ variant: "ghost",
25
+ color: "neutral",
26
+ icon: "ArrowLeftOutlined",
27
+ label: t,
28
+ onClick: o
29
+ }
30
+ );
31
+ };
32
+ u.displayName = "FunnelBackButton";
33
+ export {
34
+ u as FunnelBackButton
35
+ };
@@ -39,10 +39,50 @@ export declare const funnelTopBar: import('tailwind-variants').TVReturnType<{
39
39
  export interface FunnelTopBarProps extends VariantProps<typeof funnelTopBar> {
40
40
  /** The title text to display in the funnel top bar */
41
41
  title: string;
42
- /** Label for the back button. */
43
- backButtonLabel: string;
44
- /** Callback function to be called when the back button is pressed */
45
- onBackButtonPress: () => void;
42
+ /**
43
+ * Custom content to display in the leading area (left side).
44
+ * Typically a logo on first step or a back button on subsequent steps.
45
+ * @example
46
+ * ```tsx
47
+ * // With FunnelBackButton
48
+ * <FunnelTopBar
49
+ * leading={<FunnelBackButton label="Back" onPress={handleBack} />}
50
+ * title="My Funnel"
51
+ * />
52
+ *
53
+ * // With logo
54
+ * <FunnelTopBar
55
+ * leading={<PayFitBrand color="original" />}
56
+ * title="Get Started"
57
+ * />
58
+ * ```
59
+ */
60
+ leading?: React.ReactNode;
61
+ /**
62
+ * Label for the back button.
63
+ * @deprecated Use `leading` with <FunnelBackButton> instead. Will be removed in v2.0.
64
+ * @example
65
+ * ```tsx
66
+ * // Old way (deprecated)
67
+ * <FunnelTopBar
68
+ * backButtonLabel="Back"
69
+ * onBackButtonPress={handleBack}
70
+ * title="My Funnel"
71
+ * />
72
+ *
73
+ * // New way (recommended)
74
+ * <FunnelTopBar
75
+ * leading={<FunnelBackButton label="Back" onPress={handleBack} />}
76
+ * title="My Funnel"
77
+ * />
78
+ * ```
79
+ */
80
+ backButtonLabel?: string;
81
+ /**
82
+ * Callback function to be called when the back button is pressed.
83
+ * @deprecated Use `leading` with <FunnelBackButton> instead. Will be removed in v2.0.
84
+ */
85
+ onBackButtonPress?: () => void;
46
86
  /** Optional actions slot to display in the top bar */
47
87
  actions?: React.ReactNode;
48
88
  /** Optional flag to show or hide the progress indicator. Defaults to false */
@@ -1,115 +1,110 @@
1
- import { jsxs as o, jsx as e } from "react/jsx-runtime";
2
- import { forwardRef as x, useMemo as I } from "react";
3
- import { uyTv as N } from "@payfit/unity-themes";
4
- import { useIntl as w } from "react-intl";
5
- import { useBreakpointListener as M } from "../../../hooks/use-breakpoint-listener.js";
6
- import { Button as P } from "../../button/Button.js";
7
- import { IconButton as T } from "../../icon-button/IconButton.js";
8
- import { useFunnelLayoutContext as B } from "../FunnelLayout.context.js";
9
- import { FunnelProgressBar as F } from "./FunnelProgressBar.js";
10
- const A = N({
1
+ import { jsx as e, jsxs as l } from "react/jsx-runtime";
2
+ import { forwardRef as w, useMemo as y } from "react";
3
+ import { uyTv as B } from "@payfit/unity-themes";
4
+ import { useIntl as F } from "react-intl";
5
+ import { useFunnelLayoutContext as I } from "../FunnelLayout.context.js";
6
+ import { FunnelBackButton as T } from "./FunnelBackButton.js";
7
+ import { FunnelProgressBar as M } from "./FunnelProgressBar.js";
8
+ var c = {};
9
+ const P = B({
11
10
  slots: {
12
- base: ["uy:bg-surface-neutral uy:shadow-raising"],
11
+ base: ["uy:bg-surface-neutral uy:shadow-raising uy:min-h-800"],
13
12
  titleWrapper: [
14
- "uy:px-200 uy:py-150 uy:md:px-500",
15
- "uy:grid uy:grid-cols-[minmax(var(--uy-spacing-500),max-content)_1fr_minmax(var(--uy-spacing-500),max-content)] uy:items-center"
13
+ "uy:px-200 uy:py-150 uy:md:px-500 uy:min-h-[calc(var(--uy-spacing-800)-4px)]",
14
+ "uy:flex uy:justify-between uy:items-center"
16
15
  ],
17
16
  title: [
18
- "uy:text-content-neutral uy:typography-body-strong uy:sm:typography-h4 uy:text-center"
17
+ "uy:text-content-neutral uy:typography-body-strong uy:sm:typography-h4 uy:flex-1 uy:sm:flex-auto uy:text-center uy:col-2"
19
18
  ]
20
19
  }
21
- }), L = x(
20
+ }), _ = w(
22
21
  ({
23
- actions: r,
24
- backButtonLabel: i,
25
- currentTaskDescription: s,
26
- onBackButtonPress: l,
27
- progressAnnouncement: u,
28
- progressValue: t,
29
- showProgress: c = !0,
30
- title: m,
31
- ...p
32
- }, y) => {
33
- const a = w(), { ids: n } = B(), { base: f, titleWrapper: g, title: h } = A(), v = M(), b = !["xs", "sm"].includes(v), d = I(
22
+ actions: s,
23
+ backButtonLabel: t,
24
+ currentTaskDescription: u,
25
+ leading: r,
26
+ onBackButtonPress: o,
27
+ progressAnnouncement: d,
28
+ progressValue: n,
29
+ showProgress: m = !0,
30
+ title: f,
31
+ ...v
32
+ }, g) => {
33
+ const i = F(), { ids: a } = I(), { base: h, titleWrapper: b, title: x } = P(), N = y(() => r !== void 0 ? r : t && o ? (c.NODE_ENV === "development" && console.warn(
34
+ "FunnelTopBar: backButtonLabel and onBackButtonPress are deprecated. Use leading={<FunnelBackButton />} instead."
35
+ ), /* @__PURE__ */ e(
36
+ T,
37
+ {
38
+ label: t,
39
+ onPress: o
40
+ }
41
+ )) : null, [r, t, o]);
42
+ c.NODE_ENV === "development" && r !== void 0 && (t || o) && console.warn(
43
+ "FunnelTopBar: Both leading and deprecated props (backButtonLabel/onBackButtonPress) provided. leading takes precedence."
44
+ );
45
+ const p = y(
34
46
  () => ({
35
- navigationDescription: a.formatMessage({
47
+ navigationDescription: i.formatMessage({
36
48
  id: "unity:layout:funnel:top-bar:navigation-desc",
37
49
  defaultMessage: "Process navigation"
38
50
  }),
39
- progressAnnouncement: u ?? a.formatMessage(
51
+ progressAnnouncement: d ?? i.formatMessage(
40
52
  {
41
53
  id: "unity:layout:funnel:top-bar:progress-announcement",
42
- defaultMessage: t !== void 0 ? `Process is ${t}% complete` : "Progress status unavailable"
54
+ defaultMessage: n !== void 0 ? `Process is ${n}% complete` : "Progress status unavailable"
43
55
  },
44
56
  {
45
- progress: t
57
+ progress: n
46
58
  }
47
59
  )
48
60
  }),
49
- [a, u, t]
61
+ [i, d, n]
50
62
  );
51
- return /* @__PURE__ */ o(
63
+ return /* @__PURE__ */ l(
52
64
  "header",
53
65
  {
54
- ref: y,
55
- id: n.topbarId,
56
- className: f(),
66
+ ref: g,
67
+ id: a.topbarId,
68
+ className: h(),
57
69
  role: "banner",
58
- "aria-labelledby": n.funnelTitleId,
59
- "aria-describedby": n.funnelStatusId,
70
+ "aria-labelledby": a.funnelTitleId,
71
+ "aria-describedby": a.funnelStatusId,
60
72
  "data-dd-privacy": "allow",
61
- ...p,
73
+ ...v,
62
74
  children: [
63
- /* @__PURE__ */ o(
75
+ /* @__PURE__ */ l(
64
76
  "nav",
65
77
  {
66
- className: g(),
67
- "aria-label": d.navigationDescription,
78
+ className: b(),
79
+ "aria-label": p.navigationDescription,
68
80
  children: [
69
- b ? /* @__PURE__ */ e(
70
- P,
71
- {
72
- variant: "ghost",
73
- color: "neutral",
74
- prefixIcon: "ArrowLeftOutlined",
75
- onPress: l,
76
- children: i
77
- }
78
- ) : /* @__PURE__ */ e(
79
- T,
80
- {
81
- variant: "ghost",
82
- color: "neutral",
83
- icon: "ArrowLeftOutlined",
84
- label: i,
85
- onClick: l
86
- }
87
- ),
88
- /* @__PURE__ */ o("h1", { className: h(), id: n.funnelTitleId, children: [
89
- m,
90
- s && /* @__PURE__ */ e("span", { className: "uy:sr-only", children: s })
81
+ /* @__PURE__ */ e("div", { className: "uy:grow-0 uy:basis-[100px]", children: N }),
82
+ /* @__PURE__ */ l("h1", { className: x(), id: a.funnelTitleId, children: [
83
+ f,
84
+ u && /* @__PURE__ */ e("span", { className: "uy:sr-only", children: u })
91
85
  ] }),
92
86
  /* @__PURE__ */ e(
93
87
  "div",
94
88
  {
95
- "aria-hidden": !r,
96
- role: r ? "group" : "presentation",
97
- children: r
89
+ className: "uy:grow-0 uy:basis-[100px]",
90
+ "aria-hidden": !s,
91
+ role: s ? "group" : "presentation",
92
+ children: s
98
93
  }
99
94
  )
100
95
  ]
101
96
  }
102
97
  ),
103
- c && /* @__PURE__ */ e("div", { "aria-hidden": "true", children: /* @__PURE__ */ e(F, { value: t ?? 0 }) }),
98
+ m && /* @__PURE__ */ e("div", { "aria-hidden": "true", children: /* @__PURE__ */ e(M, { value: n ?? 0 }) }),
104
99
  /* @__PURE__ */ e(
105
100
  "div",
106
101
  {
107
- id: n.funnelStatusId,
102
+ id: a.funnelStatusId,
108
103
  role: "status",
109
104
  "aria-live": "polite",
110
105
  "aria-atomic": "true",
111
106
  className: "uy:sr-only",
112
- children: d.progressAnnouncement
107
+ children: p.progressAnnouncement
113
108
  }
114
109
  )
115
110
  ]
@@ -117,8 +112,8 @@ const A = N({
117
112
  );
118
113
  }
119
114
  );
120
- L.displayName = "FunnelTopBar";
115
+ _.displayName = "FunnelTopBar";
121
116
  export {
122
- L as FunnelTopBar,
123
- A as funnelTopBar
117
+ _ as FunnelTopBar,
118
+ P as funnelTopBar
124
119
  };
@@ -83,6 +83,7 @@ export * from './components/funnel-layout/parts/FunnelPage.js';
83
83
  export * from './components/funnel-layout/parts/FunnelPageAction.js';
84
84
  export * from './components/funnel-layout/parts/FunnelPageActions.js';
85
85
  export * from './components/funnel-layout/parts/FunnelPageContent.js';
86
+ export * from './components/funnel-layout/parts/FunnelBackButton.js';
86
87
  export * from './components/funnel-layout/parts/FunnelPageFooter.js';
87
88
  export * from './components/funnel-layout/parts/FunnelPageHeader.js';
88
89
  export * from './components/funnel-layout/parts/FunnelSidebar.js';