@giddaa-housing/ui 1.1.0 → 1.2.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.
@@ -1,39 +1,49 @@
1
1
  import { t as ComponentSize } from "./size-context-D4mYvcg8.js";
2
+ import { ChartConfig } from "./chart.js";
2
3
  import * as React from "react";
4
+ import { Pie, PieChart as PieChart$1 } from "recharts";
3
5
  //#region src/pie-chart.d.ts
4
6
  /**
5
- * Pie / donut charts are composed from recharts plot primitives directly —
6
- * recharts identifies `PieChart`, `Pie`, and `Cell` by component identity, so
7
- * they cannot be wrapped. This module supplies the giddaa-specific pieces that
8
- * would otherwise be copy-pasted: per-size layout numbers, a categorical
9
- * palette, the donut center label, and the rich legend (dot · name · value ·
10
- * percentage). Pair them with the shared frame from `./chart`.
7
+ * Pie / donut charts are composable wrappers over the recharts pie primitives.
8
+ * recharts 3 registers graphical items through context when they render, so the
9
+ * primitives CAN be wrapped in our own components. Each wrapper bakes in the
10
+ * giddaa defaults (square plot box, size-scaled radii, mark specs) and forwards
11
+ * every recharts prop, so anything can be overridden in place or replaced with
12
+ * the raw recharts element.
11
13
  *
12
- * A donut keeps an open center for a total label (`innerRadius` > 0); a pie
13
- * fills the circle (`innerRadius={0}`). Both read the same layout.
14
+ * A donut keeps an open center for a total label (`type="donut"`, the default);
15
+ * a pie fills the circle (`type="pie"`). Segment colours are assigned per `Cell`
16
+ * from `PIE_CHART_PALETTE` in slot order — a fourth+ category folds into "Other"
17
+ * (the palette has three validated categorical slots on purpose).
14
18
  *
15
19
  * @example
16
- * const chart = usePieChartLayout({ size: "md", type: "donut" });
17
20
  * <ChartCard size="md">
21
+ * <ChartHeader>
22
+ * <ChartTitle>Portfolio mix</ChartTitle>
23
+ * </ChartHeader>
18
24
  * <div className="flex items-center gap-4">
19
- * <ChartContainer config={config} {...chart.container}>
20
- * <PieChart>
21
- * <Pie data={data} dataKey="value" nameKey="label" {...chart.pie}>
22
- * {data.map((d) => <Cell key={d.label} fill={d.color} />)}
23
- * <Label content={<PieCenterLabel total="₦1.1M" caption="Total spend" />} />
24
- * </Pie>
25
- * </PieChart>
26
- * </ChartContainer>
25
+ * <PieChart config={config}>
26
+ * <PieChartSeries data={data} dataKey="value" nameKey="label" type="donut">
27
+ * {data.map((d, i) => <Cell key={d.label} fill={PIE_CHART_PALETTE[i]} />)}
28
+ * <Label content={<PieCenterLabel total="₦1.1M" caption="Total spend" />} />
29
+ * </PieChartSeries>
30
+ * </PieChart>
27
31
  * <PieChartLegend items={legendItems} />
28
32
  * </div>
29
33
  * </ChartCard>
30
34
  */
31
35
  /** Pie sizes line up with the three shared `ComponentSize` values. */
32
36
  type PieChartSize = ComponentSize;
37
+ /**
38
+ * Categorical segment palette — the theme-aware `chart-1`…`chart-3` tokens.
39
+ * Assign colours in this fixed slot order, never cycled: a fourth category folds
40
+ * into "Other". Override per segment with a `Cell` `fill`.
41
+ */
42
+ declare const PIE_CHART_PALETTE: readonly ["var(--color-chart-1)", "var(--color-chart-2)", "var(--color-chart-3)"];
33
43
  type PieChartLayout = {
34
44
  /** Square plot box in px (width = height); width is NOT parent-driven here. */
35
45
  size: number;
36
- /** Donut hole radius in px. Pass `0` to the `Pie` for a filled pie. */
46
+ /** Donut hole radius in px. `type="pie"` collapses it to 0. */
37
47
  innerRadius: number;
38
48
  /** Outer ring radius in px. */
39
49
  outerRadius: number;
@@ -47,46 +57,38 @@ type PieChartLayout = {
47
57
  captionFontSize: number;
48
58
  };
49
59
  declare const PIE_CHART_LAYOUT: Record<PieChartSize, PieChartLayout>;
60
+ type PieChartProps = Omit<React.ComponentProps<typeof PieChart$1>, "width" | "height"> & {
61
+ /** Series labels/icons for the shared tooltip and legend (see `ChartConfig`). */
62
+ config?: ChartConfig;
63
+ /** Plot scale; inherited from the nearest `SizeProvider` when omitted. */
64
+ size?: PieChartSize;
65
+ /** Applied to the square plot box, e.g. to override its dimensions. */
66
+ className?: string;
67
+ };
50
68
  /**
51
- * Categorical palette for pie/donut segments, ordered by share. Giddaa has no
52
- * `chart-1`…`chart-N` tokens yet, so these map to the brand scales: forest
53
- * brass moss clay stone. Assign by index, or override per `Cell`.
54
- */
55
- declare const PIE_CHART_PALETTE: readonly ["var(--color-forest-700)", "var(--color-brass-500)", "var(--color-moss-600)", "var(--color-clay-500)", "var(--color-stone-500)"];
56
- /**
57
- * Resolve the pie-chart layout for an explicit or inherited component size and
58
- * chart `type`. The returned object groups the giddaa defaults by the recharts
59
- * component they belong to — spread `container` onto `ChartContainer` and `pie`
60
- * onto `Pie`, then pass the data-specific props (`data`, `dataKey`, `nameKey`)
61
- * directly afterwards. `type="pie"` collapses the donut hole (`innerRadius` 0).
69
+ * Plot root wraps the shared `ChartContainer` (tooltip/legend theming,
70
+ * responsive box) around the recharts `PieChart` in a fixed square box from
71
+ * `PIE_CHART_LAYOUT` (unlike the cartesian roots, a pie's width is not
72
+ * parent-driven). `size` flows to the child primitives through `SizeProvider`.
62
73
  */
63
- declare function usePieChartLayout({ size, type }?: {
64
- size?: PieChartSize;
74
+ declare function PieChart({ config, size, className, children, ...props }: PieChartProps): React.JSX.Element;
75
+ type PieChartSeriesProps = React.ComponentProps<typeof Pie> & {
76
+ /** `donut` keeps an open center (default); `pie` fills the circle. */
65
77
  type?: "donut" | "pie";
66
- }): {
67
- container: {
68
- className: string;
69
- style: {
70
- width: number;
71
- height: number;
72
- };
73
- };
74
- pie: {
75
- innerRadius: number;
76
- outerRadius: number;
77
- paddingAngle: number;
78
- cornerRadius: number;
79
- strokeWidth: number;
80
- };
81
- label: {
82
- totalFontSize: number;
83
- captionFontSize: number;
84
- };
78
+ /** Explicit mark scale; otherwise inherited from the chart's `size`. */
79
+ size?: PieChartSize;
85
80
  };
81
+ /**
82
+ * One data series — recharts `Pie` with the giddaa mark spec: size-scaled
83
+ * radii, a small inter-segment gap, and no stroke. Pass `Cell` children for
84
+ * per-segment colours and a `Label` for the donut center. Each numeric prop is
85
+ * defaulted from the layout and still wins when passed directly.
86
+ */
87
+ declare function PieChartSeries({ type, size, innerRadius, outerRadius, paddingAngle, cornerRadius, strokeWidth, ...props }: PieChartSeriesProps): React.JSX.Element;
86
88
  /**
87
89
  * Center label for a donut — the hero total over a short caption. Pass it to
88
90
  * recharts as `<Label content={<PieCenterLabel total=… caption=… />} />` inside
89
- * the `Pie`; recharts injects the `viewBox` carrying the polar center (cx/cy).
91
+ * the `PieChartSeries`; recharts injects the `viewBox` carrying the polar center.
90
92
  */
91
93
  type PieCenterLabelProps = {
92
94
  /** Hero figure — pre-formatted, e.g. "₦1.1M". */
@@ -121,5 +123,34 @@ declare function PieChartLegend({ items, size, className, ...props }: React.Comp
121
123
  items: PieLegendItem[];
122
124
  size?: PieChartSize;
123
125
  }): React.JSX.Element;
126
+ /**
127
+ * @deprecated Compose the `<PieChart>`/`<PieChartSeries>` primitives instead —
128
+ * see the module example. This layout hook predates recharts 3 wrapping support
129
+ * and will be removed in the next major. It returns prop bags to spread onto raw
130
+ * recharts elements.
131
+ */
132
+ declare function usePieChartLayout({ size, type }?: {
133
+ size?: PieChartSize;
134
+ type?: "donut" | "pie";
135
+ }): {
136
+ container: {
137
+ className: string;
138
+ style: {
139
+ width: number;
140
+ height: number;
141
+ };
142
+ };
143
+ pie: {
144
+ innerRadius: number;
145
+ outerRadius: number;
146
+ paddingAngle: number;
147
+ cornerRadius: number;
148
+ strokeWidth: number;
149
+ };
150
+ label: {
151
+ totalFontSize: number;
152
+ captionFontSize: number;
153
+ };
154
+ };
124
155
  //#endregion
125
- export { PIE_CHART_LAYOUT, PIE_CHART_PALETTE, PieCenterLabel, type PieCenterLabelProps, type PieChartLayout, PieChartLegend, type PieChartSize, type PieLegendItem, usePieChartLayout };
156
+ export { PIE_CHART_LAYOUT, PIE_CHART_PALETTE, PieCenterLabel, type PieCenterLabelProps, PieChart, type PieChartLayout, PieChartLegend, type PieChartProps, PieChartSeries, type PieChartSeriesProps, type PieChartSize, type PieLegendItem, usePieChartLayout };
package/dist/pie-chart.js CHANGED
@@ -1,9 +1,21 @@
1
1
  "use client";
2
2
  import { t as cn } from "./cn-BI_4DMBf.js";
3
- import { useComponentSize } from "./size-context.js";
3
+ import { SizeProvider, useComponentSize } from "./size-context.js";
4
+ import { ChartContainer } from "./chart.js";
4
5
  import { jsx, jsxs } from "react/jsx-runtime";
5
6
  import { cva } from "class-variance-authority";
7
+ import { Pie, PieChart as PieChart$1 } from "recharts";
6
8
  //#region src/pie-chart.tsx
9
+ /**
10
+ * Categorical segment palette — the theme-aware `chart-1`…`chart-3` tokens.
11
+ * Assign colours in this fixed slot order, never cycled: a fourth category folds
12
+ * into "Other". Override per segment with a `Cell` `fill`.
13
+ */
14
+ const PIE_CHART_PALETTE = [
15
+ "var(--color-chart-1)",
16
+ "var(--color-chart-2)",
17
+ "var(--color-chart-3)"
18
+ ];
7
19
  const PIE_CHART_LAYOUT = {
8
20
  sm: {
9
21
  size: 132,
@@ -34,52 +46,53 @@ const PIE_CHART_LAYOUT = {
34
46
  }
35
47
  };
36
48
  /**
37
- * Categorical palette for pie/donut segments, ordered by share. Giddaa has no
38
- * `chart-1`…`chart-N` tokens yet, so these map to the brand scales: forest
39
- * brass moss clay stone. Assign by index, or override per `Cell`.
40
- */
41
- const PIE_CHART_PALETTE = [
42
- "var(--color-forest-700)",
43
- "var(--color-brass-500)",
44
- "var(--color-moss-600)",
45
- "var(--color-clay-500)",
46
- "var(--color-stone-500)"
47
- ];
48
- /**
49
- * Resolve the pie-chart layout for an explicit or inherited component size and
50
- * chart `type`. The returned object groups the giddaa defaults by the recharts
51
- * component they belong to — spread `container` onto `ChartContainer` and `pie`
52
- * onto `Pie`, then pass the data-specific props (`data`, `dataKey`, `nameKey`)
53
- * directly afterwards. `type="pie"` collapses the donut hole (`innerRadius` 0).
49
+ * Plot root wraps the shared `ChartContainer` (tooltip/legend theming,
50
+ * responsive box) around the recharts `PieChart` in a fixed square box from
51
+ * `PIE_CHART_LAYOUT` (unlike the cartesian roots, a pie's width is not
52
+ * parent-driven). `size` flows to the child primitives through `SizeProvider`.
54
53
  */
55
- function usePieChartLayout({ size, type = "donut" } = {}) {
56
- const layout = PIE_CHART_LAYOUT[useComponentSize(size)];
57
- return {
58
- container: {
59
- className: "aspect-square shrink-0",
54
+ function PieChart({ config = {}, size, className, children, ...props }) {
55
+ const resolvedSize = useComponentSize(size);
56
+ const layout = PIE_CHART_LAYOUT[resolvedSize];
57
+ return /* @__PURE__ */ jsx(SizeProvider, {
58
+ size: resolvedSize,
59
+ children: /* @__PURE__ */ jsx(ChartContainer, {
60
+ config,
61
+ className: cn("aspect-square shrink-0", className),
60
62
  style: {
61
63
  width: layout.size,
62
64
  height: layout.size
63
- }
64
- },
65
- pie: {
66
- innerRadius: type === "pie" ? 0 : layout.innerRadius,
67
- outerRadius: layout.outerRadius,
68
- paddingAngle: type === "pie" ? 0 : layout.paddingAngle,
69
- cornerRadius: layout.cornerRadius,
70
- strokeWidth: 0
71
- },
72
- label: {
73
- totalFontSize: layout.totalFontSize,
74
- captionFontSize: layout.captionFontSize
75
- }
76
- };
65
+ },
66
+ children: /* @__PURE__ */ jsx(PieChart$1, {
67
+ accessibilityLayer: true,
68
+ ...props,
69
+ children
70
+ })
71
+ })
72
+ });
73
+ }
74
+ /**
75
+ * One data series — recharts `Pie` with the giddaa mark spec: size-scaled
76
+ * radii, a small inter-segment gap, and no stroke. Pass `Cell` children for
77
+ * per-segment colours and a `Label` for the donut center. Each numeric prop is
78
+ * defaulted from the layout and still wins when passed directly.
79
+ */
80
+ function PieChartSeries({ type = "donut", size, innerRadius, outerRadius, paddingAngle, cornerRadius, strokeWidth, ...props }) {
81
+ const layout = PIE_CHART_LAYOUT[useComponentSize(size)];
82
+ return /* @__PURE__ */ jsx(Pie, {
83
+ innerRadius: innerRadius ?? (type === "pie" ? 0 : layout.innerRadius),
84
+ outerRadius: outerRadius ?? layout.outerRadius,
85
+ paddingAngle: paddingAngle ?? (type === "pie" ? 0 : layout.paddingAngle),
86
+ cornerRadius: cornerRadius ?? layout.cornerRadius,
87
+ strokeWidth: strokeWidth ?? 0,
88
+ ...props
89
+ });
77
90
  }
78
91
  function PieCenterLabel({ total, caption, size, viewBox }) {
79
- const { label } = usePieChartLayout({ size });
92
+ const layout = PIE_CHART_LAYOUT[useComponentSize(size)];
80
93
  if (!viewBox || viewBox.cx == null || viewBox.cy == null) return null;
81
94
  const { cx, cy } = viewBox;
82
- const captionY = cy + label.totalFontSize * .72;
95
+ const captionY = cy + layout.totalFontSize * .72;
83
96
  return /* @__PURE__ */ jsxs("text", {
84
97
  x: cx,
85
98
  y: cy,
@@ -87,15 +100,15 @@ function PieCenterLabel({ total, caption, size, viewBox }) {
87
100
  dominantBaseline: "central",
88
101
  children: [/* @__PURE__ */ jsx("tspan", {
89
102
  x: cx,
90
- y: caption == null ? cy : cy - label.captionFontSize * .5,
103
+ y: caption == null ? cy : cy - layout.captionFontSize * .5,
91
104
  className: "fill-fg-primary font-extrabold tabular-nums",
92
- style: { fontSize: label.totalFontSize },
105
+ style: { fontSize: layout.totalFontSize },
93
106
  children: total
94
107
  }), caption != null && /* @__PURE__ */ jsx("tspan", {
95
108
  x: cx,
96
109
  y: captionY,
97
110
  className: "fill-fg-secondary font-medium",
98
- style: { fontSize: label.captionFontSize },
111
+ style: { fontSize: layout.captionFontSize },
99
112
  children: caption
100
113
  })]
101
114
  });
@@ -145,5 +158,34 @@ function PieChartLegend({ items, size, className, ...props }) {
145
158
  }, item.label))
146
159
  });
147
160
  }
161
+ /**
162
+ * @deprecated Compose the `<PieChart>`/`<PieChartSeries>` primitives instead —
163
+ * see the module example. This layout hook predates recharts 3 wrapping support
164
+ * and will be removed in the next major. It returns prop bags to spread onto raw
165
+ * recharts elements.
166
+ */
167
+ function usePieChartLayout({ size, type = "donut" } = {}) {
168
+ const layout = PIE_CHART_LAYOUT[useComponentSize(size)];
169
+ return {
170
+ container: {
171
+ className: "aspect-square shrink-0",
172
+ style: {
173
+ width: layout.size,
174
+ height: layout.size
175
+ }
176
+ },
177
+ pie: {
178
+ innerRadius: type === "pie" ? 0 : layout.innerRadius,
179
+ outerRadius: layout.outerRadius,
180
+ paddingAngle: type === "pie" ? 0 : layout.paddingAngle,
181
+ cornerRadius: layout.cornerRadius,
182
+ strokeWidth: 0
183
+ },
184
+ label: {
185
+ totalFontSize: layout.totalFontSize,
186
+ captionFontSize: layout.captionFontSize
187
+ }
188
+ };
189
+ }
148
190
  //#endregion
149
- export { PIE_CHART_LAYOUT, PIE_CHART_PALETTE, PieCenterLabel, PieChartLegend, usePieChartLayout };
191
+ export { PIE_CHART_LAYOUT, PIE_CHART_PALETTE, PieCenterLabel, PieChart, PieChartLegend, PieChartSeries, usePieChartLayout };
@@ -0,0 +1,79 @@
1
+ import { useRender } from "@base-ui/react/use-render";
2
+ import { VariantProps } from "class-variance-authority";
3
+ import * as React from "react";
4
+ //#region src/purchase-option-card.d.ts
5
+ declare const purchaseOptionCardVariants: (props?: ({
6
+ selected?: boolean | null | undefined;
7
+ disabled?: boolean | null | undefined;
8
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
9
+ type PurchaseOptionCardProps = useRender.ComponentProps<"article"> & VariantProps<typeof purchaseOptionCardVariants>;
10
+ /**
11
+ * Root surface for a single purchase / mortgage option. Composable — assemble
12
+ * it from the header, grid, and footer parts so the actions can vary per use.
13
+ *
14
+ * `disabled` is propagated to descendants via a `data-disabled` group flag, so
15
+ * the header/grid text mutes automatically. Footer buttons are supplied by the
16
+ * consumer, so pass `disabled` to them too.
17
+ *
18
+ * <PurchaseOptionCard>
19
+ * <PurchaseOptionCardHeader>
20
+ * <PurchaseOptionCardMeta>
21
+ * <PurchaseOptionCardLabel>Mortgage Plan</PurchaseOptionCardLabel>
22
+ * <Badge variant="default" border={false} shape="round" size="sm">
23
+ * 20% Interest Rate
24
+ * </Badge>
25
+ * </PurchaseOptionCardMeta>
26
+ * <div>
27
+ * <PurchaseOptionCardAmount>₦1,200,000</PurchaseOptionCardAmount>
28
+ * <PurchaseOptionCardCadence>Per Month</PurchaseOptionCardCadence>
29
+ * </div>
30
+ * </PurchaseOptionCardHeader>
31
+ * <PurchaseOptionCardGrid>
32
+ * <PurchaseOptionCardItem label="Initial Deposit">₦5,000,000</PurchaseOptionCardItem>
33
+ * <PurchaseOptionCardItem label="Max Loan Amount">₦80,000,000</PurchaseOptionCardItem>
34
+ * <PurchaseOptionCardItem label="Max Payment Period" wide>20 Years</PurchaseOptionCardItem>
35
+ * </PurchaseOptionCardGrid>
36
+ * <PurchaseOptionCardFooter>
37
+ * <Button variant="primary-outline" size="md" className="flex-1">View Details</Button>
38
+ * <Button variant="primary" size="md" className="flex-1">Apply Now</Button>
39
+ * </PurchaseOptionCardFooter>
40
+ * </PurchaseOptionCard>
41
+ */
42
+ declare function PurchaseOptionCard({ className, disabled, render, selected, ...props }: PurchaseOptionCardProps): React.ReactElement<unknown, string | React.JSXElementConstructor<any>>;
43
+ /**
44
+ * Top section wrapper. Stacks the meta row and the headline figure, and draws
45
+ * the divider that separates the header from the grid.
46
+ */
47
+ declare function PurchaseOptionCardHeader({ className, render, ...props }: useRender.ComponentProps<"div">): React.ReactElement<unknown, string | React.JSXElementConstructor<any>>;
48
+ /**
49
+ * Row inside the header that balances the plan label against the interest-rate
50
+ * badge (or any other trailing content).
51
+ */
52
+ declare function PurchaseOptionCardMeta({ className, render, ...props }: useRender.ComponentProps<"div">): React.ReactElement<unknown, string | React.JSXElementConstructor<any>>;
53
+ /** Eyebrow label naming the plan type. */
54
+ declare function PurchaseOptionCardLabel({ className, render, ...props }: useRender.ComponentProps<"span">): React.ReactElement<unknown, string | React.JSXElementConstructor<any>>;
55
+ /** Headline figure — the plan's payment amount. */
56
+ declare function PurchaseOptionCardAmount({ className, render, ...props }: useRender.ComponentProps<"h3">): React.ReactElement<unknown, string | React.JSXElementConstructor<any>>;
57
+ /** Supporting cadence line beneath the headline figure. */
58
+ declare function PurchaseOptionCardCadence({ className, render, ...props }: useRender.ComponentProps<"p">): React.ReactElement<unknown, string | React.JSXElementConstructor<any>>;
59
+ /** Two-column grid holding the option's detail items. */
60
+ declare function PurchaseOptionCardGrid({ className, render, ...props }: useRender.ComponentProps<"div">): React.ReactElement<unknown, string | React.JSXElementConstructor<any>>;
61
+ type PurchaseOptionCardItemProps = useRender.ComponentProps<"div"> & {
62
+ /** Caption rendered beneath the value. */
63
+ label: React.ReactNode;
64
+ /** Span both grid columns. */
65
+ wide?: boolean;
66
+ };
67
+ /**
68
+ * A single labelled detail cell in the grid. The value is passed as `children`,
69
+ * the caption via `label`. Use `wide` to span the full row.
70
+ */
71
+ declare function PurchaseOptionCardItem({ children, className, label, render, wide, ...props }: PurchaseOptionCardItemProps): React.ReactElement<unknown, string | React.JSXElementConstructor<any>>;
72
+ /**
73
+ * Action row at the foot of the card. Wraps the consumer-supplied buttons and
74
+ * draws the divider above them. Give each button `className="flex-1"` to share
75
+ * the row evenly.
76
+ */
77
+ declare function PurchaseOptionCardFooter({ className, render, ...props }: useRender.ComponentProps<"div">): React.ReactElement<unknown, string | React.JSXElementConstructor<any>>;
78
+ //#endregion
79
+ export { PurchaseOptionCard, PurchaseOptionCardAmount, PurchaseOptionCardCadence, PurchaseOptionCardFooter, PurchaseOptionCardGrid, PurchaseOptionCardHeader, PurchaseOptionCardItem, type PurchaseOptionCardItemProps, PurchaseOptionCardLabel, PurchaseOptionCardMeta, type PurchaseOptionCardProps, purchaseOptionCardVariants };
@@ -0,0 +1,176 @@
1
+ import { t as cn } from "./cn-BI_4DMBf.js";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import { mergeProps } from "@base-ui/react/merge-props";
4
+ import { useRender } from "@base-ui/react/use-render";
5
+ import { cva } from "class-variance-authority";
6
+ //#region src/purchase-option-card.tsx
7
+ const purchaseOptionCardVariants = cva("group/purchase-option-card relative flex w-full min-w-0 flex-col gap-3.5 rounded-2xl border bg-surface-overlay px-6 py-5 shadow-1 transition-[background-color,border-color,box-shadow]", {
8
+ variants: {
9
+ selected: {
10
+ true: "border-line-focus bg-surface-brand-subtle inset-ring-1 inset-ring-line-focus",
11
+ false: "border-line-subtle hover:border-line hover:shadow-2"
12
+ },
13
+ disabled: {
14
+ true: "border-line-subtle bg-surface shadow-none hover:border-line-subtle hover:shadow-none",
15
+ false: ""
16
+ }
17
+ },
18
+ compoundVariants: [{
19
+ selected: true,
20
+ disabled: true,
21
+ class: "border-line-subtle bg-surface shadow-none inset-ring-0 hover:border-line-subtle hover:shadow-none"
22
+ }],
23
+ defaultVariants: {
24
+ selected: false,
25
+ disabled: false
26
+ }
27
+ });
28
+ /**
29
+ * Root surface for a single purchase / mortgage option. Composable — assemble
30
+ * it from the header, grid, and footer parts so the actions can vary per use.
31
+ *
32
+ * `disabled` is propagated to descendants via a `data-disabled` group flag, so
33
+ * the header/grid text mutes automatically. Footer buttons are supplied by the
34
+ * consumer, so pass `disabled` to them too.
35
+ *
36
+ * <PurchaseOptionCard>
37
+ * <PurchaseOptionCardHeader>
38
+ * <PurchaseOptionCardMeta>
39
+ * <PurchaseOptionCardLabel>Mortgage Plan</PurchaseOptionCardLabel>
40
+ * <Badge variant="default" border={false} shape="round" size="sm">
41
+ * 20% Interest Rate
42
+ * </Badge>
43
+ * </PurchaseOptionCardMeta>
44
+ * <div>
45
+ * <PurchaseOptionCardAmount>₦1,200,000</PurchaseOptionCardAmount>
46
+ * <PurchaseOptionCardCadence>Per Month</PurchaseOptionCardCadence>
47
+ * </div>
48
+ * </PurchaseOptionCardHeader>
49
+ * <PurchaseOptionCardGrid>
50
+ * <PurchaseOptionCardItem label="Initial Deposit">₦5,000,000</PurchaseOptionCardItem>
51
+ * <PurchaseOptionCardItem label="Max Loan Amount">₦80,000,000</PurchaseOptionCardItem>
52
+ * <PurchaseOptionCardItem label="Max Payment Period" wide>20 Years</PurchaseOptionCardItem>
53
+ * </PurchaseOptionCardGrid>
54
+ * <PurchaseOptionCardFooter>
55
+ * <Button variant="primary-outline" size="md" className="flex-1">View Details</Button>
56
+ * <Button variant="primary" size="md" className="flex-1">Apply Now</Button>
57
+ * </PurchaseOptionCardFooter>
58
+ * </PurchaseOptionCard>
59
+ */
60
+ function PurchaseOptionCard({ className, disabled = false, render, selected = false, ...props }) {
61
+ return useRender({
62
+ defaultTagName: "article",
63
+ props: mergeProps({
64
+ "data-disabled": disabled || void 0,
65
+ className: cn(purchaseOptionCardVariants({
66
+ selected: !disabled && selected,
67
+ disabled
68
+ }), className)
69
+ }, props),
70
+ render,
71
+ state: {
72
+ disabled,
73
+ selected,
74
+ slot: "purchase-option-card"
75
+ }
76
+ });
77
+ }
78
+ /**
79
+ * Top section wrapper. Stacks the meta row and the headline figure, and draws
80
+ * the divider that separates the header from the grid.
81
+ */
82
+ function PurchaseOptionCardHeader({ className, render, ...props }) {
83
+ return useRender({
84
+ defaultTagName: "div",
85
+ props: mergeProps({ className: cn("flex min-w-0 flex-col gap-3.5 border-line border-b pb-3.5", className) }, props),
86
+ render,
87
+ state: { slot: "purchase-option-card-header" }
88
+ });
89
+ }
90
+ /**
91
+ * Row inside the header that balances the plan label against the interest-rate
92
+ * badge (or any other trailing content).
93
+ */
94
+ function PurchaseOptionCardMeta({ className, render, ...props }) {
95
+ return useRender({
96
+ defaultTagName: "div",
97
+ props: mergeProps({ className: cn("flex items-start justify-between gap-4", className) }, props),
98
+ render,
99
+ state: { slot: "purchase-option-card-meta" }
100
+ });
101
+ }
102
+ /** Eyebrow label naming the plan type. */
103
+ function PurchaseOptionCardLabel({ className, render, ...props }) {
104
+ return useRender({
105
+ defaultTagName: "span",
106
+ props: mergeProps({ className: cn("text-gdt-capitalized font-semibold text-fg-secondary uppercase group-data-disabled/purchase-option-card:text-fg-caption-placeholder", className) }, props),
107
+ render,
108
+ state: { slot: "purchase-option-card-label" }
109
+ });
110
+ }
111
+ /** Headline figure — the plan's payment amount. */
112
+ function PurchaseOptionCardAmount({ className, render, ...props }) {
113
+ return useRender({
114
+ defaultTagName: "h3",
115
+ props: mergeProps({ className: cn("text-gdt-h3 font-extrabold text-fg-primary group-data-disabled/purchase-option-card:text-fg-caption-placeholder", className) }, props),
116
+ render,
117
+ state: { slot: "purchase-option-card-amount" }
118
+ });
119
+ }
120
+ /** Supporting cadence line beneath the headline figure. */
121
+ function PurchaseOptionCardCadence({ className, render, ...props }) {
122
+ return useRender({
123
+ defaultTagName: "p",
124
+ props: mergeProps({ className: cn("text-gdt-sm leading-none text-fg-secondary group-data-disabled/purchase-option-card:text-fg-caption-placeholder", className) }, props),
125
+ render,
126
+ state: { slot: "purchase-option-card-cadence" }
127
+ });
128
+ }
129
+ /** Two-column grid holding the option's detail items. */
130
+ function PurchaseOptionCardGrid({ className, render, ...props }) {
131
+ return useRender({
132
+ defaultTagName: "div",
133
+ props: mergeProps({ className: cn("grid grid-cols-2 gap-x-4 gap-y-6", className) }, props),
134
+ render,
135
+ state: { slot: "purchase-option-card-grid" }
136
+ });
137
+ }
138
+ /**
139
+ * A single labelled detail cell in the grid. The value is passed as `children`,
140
+ * the caption via `label`. Use `wide` to span the full row.
141
+ */
142
+ function PurchaseOptionCardItem({ children, className, label, render, wide = false, ...props }) {
143
+ return useRender({
144
+ defaultTagName: "div",
145
+ props: mergeProps({
146
+ children: /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("p", {
147
+ className: "text-gdt-sm font-semibold text-fg-primary group-data-disabled/purchase-option-card:text-fg-caption-placeholder",
148
+ children
149
+ }), /* @__PURE__ */ jsx("p", {
150
+ className: "text-gdt-xs text-fg-secondary group-data-disabled/purchase-option-card:text-fg-caption-placeholder",
151
+ children: label
152
+ })] }),
153
+ className: cn("flex flex-col gap-1", wide && "col-span-2", className)
154
+ }, props),
155
+ render,
156
+ state: {
157
+ slot: "purchase-option-card-item",
158
+ wide
159
+ }
160
+ });
161
+ }
162
+ /**
163
+ * Action row at the foot of the card. Wraps the consumer-supplied buttons and
164
+ * draws the divider above them. Give each button `className="flex-1"` to share
165
+ * the row evenly.
166
+ */
167
+ function PurchaseOptionCardFooter({ className, render, ...props }) {
168
+ return useRender({
169
+ defaultTagName: "div",
170
+ props: mergeProps({ className: cn("flex items-center gap-3 border-line border-t pt-3.5", className) }, props),
171
+ render,
172
+ state: { slot: "purchase-option-card-footer" }
173
+ });
174
+ }
175
+ //#endregion
176
+ export { PurchaseOptionCard, PurchaseOptionCardAmount, PurchaseOptionCardCadence, PurchaseOptionCardFooter, PurchaseOptionCardGrid, PurchaseOptionCardHeader, PurchaseOptionCardItem, PurchaseOptionCardLabel, PurchaseOptionCardMeta, purchaseOptionCardVariants };
@@ -13,7 +13,7 @@ function RadioGroup({ className, ...props }) {
13
13
  ...props
14
14
  });
15
15
  }
16
- const radioVariants = cva("group/radio-group-item peer relative flex aspect-square shrink-0 rounded-full border-2 bg-transparent text-transparent outline-none transition-[background-color,border-color,box-shadow,color] after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-line-focus focus-visible:shadow-[0px_0px_0px_2px_var(--color-line-focus)] aria-invalid:border-line-danger aria-invalid:ring-3 aria-invalid:ring-destructive/20 border-line-strong data-checked:border-action-primary data-checked:text-action-primary data-disabled:cursor-not-allowed data-disabled:border-line-subtle data-disabled:bg-surface data-disabled:text-fg-caption-placeholder size-5 [--radio-dot-size:0.75rem] group-data-[size=sm]/field:size-4 group-data-[size=sm]/field:[--radio-dot-size:0.625rem] group-data-[size=md]/field:size-5 group-data-[size=md]/field:[--radio-dot-size:0.75rem] group-data-[size=lg]/field:size-6 group-data-[size=lg]/field:[--radio-dot-size:0.875rem]", { variants: { size: {
16
+ const radioVariants = cva("group/radio-group-item peer relative flex aspect-square shrink-0 rounded-full border-2 bg-transparent text-transparent outline-none transition-[background-color,border-color,box-shadow,color] after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-line-focus focus-visible:shadow-[0px_0px_0px_2px_var(--color-line-focus)] aria-invalid:border-line-danger aria-invalid:ring-3 aria-invalid:ring-line-danger/20 aria-invalid:focus-visible:border-line-danger aria-invalid:focus-visible:shadow-[0px_0px_0px_2px_var(--color-line-danger)] border-line-strong data-checked:border-action-primary data-checked:text-action-primary data-disabled:cursor-not-allowed data-disabled:border-line-subtle data-disabled:bg-surface data-disabled:text-fg-caption-placeholder size-5 [--radio-dot-size:0.75rem] group-data-[size=sm]/field:size-4 group-data-[size=sm]/field:[--radio-dot-size:0.625rem] group-data-[size=md]/field:size-5 group-data-[size=md]/field:[--radio-dot-size:0.75rem] group-data-[size=lg]/field:size-6 group-data-[size=lg]/field:[--radio-dot-size:0.875rem]", { variants: { size: {
17
17
  sm: "size-4 [--radio-dot-size:0.625rem]",
18
18
  md: "size-5 [--radio-dot-size:0.75rem]",
19
19
  lg: "size-6 [--radio-dot-size:0.875rem]"
package/dist/select.js CHANGED
@@ -21,7 +21,7 @@ function Select({ multiple, children, size, ...props }) {
21
21
  })
22
22
  });
23
23
  }
24
- const selectTriggerVariants = cva("group/select-trigger flex w-fit min-w-0 items-center justify-between rounded-full border border-line bg-canvas text-left font-normal whitespace-nowrap text-fg-primary shadow-none outline-none transition-[background-color,border-color,border-radius,box-shadow,color] select-none hover:border-line-strong focus-visible:border-line-focus focus-visible:inset-ring-1 focus-visible:inset-ring-line-focus data-focused:border-line-focus data-focused:inset-ring-1 data-focused:inset-ring-line-focus data-popup-open:rounded-2xl data-popup-open:border-line-focus data-popup-open:inset-ring-1 data-popup-open:inset-ring-line-focus disabled:pointer-events-none disabled:cursor-not-allowed disabled:border-line-subtle disabled:bg-surface disabled:text-fg-caption-placeholder data-disabled:pointer-events-none data-disabled:cursor-not-allowed data-disabled:border-line-subtle data-disabled:bg-surface data-disabled:text-fg-caption-placeholder data-placeholder:text-fg-caption-placeholder aria-invalid:border-line-danger aria-invalid:text-fg-danger aria-invalid:shadow-none data-[invalid=true]:border-line-danger data-[invalid=true]:text-fg-danger *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:min-w-0 *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0", {
24
+ const selectTriggerVariants = cva("group/select-trigger flex w-fit min-w-0 items-center justify-between rounded-full border border-line bg-canvas text-left font-normal whitespace-nowrap text-fg-primary shadow-none outline-none transition-[background-color,border-color,border-radius,box-shadow,color] select-none hover:border-line-strong focus-visible:border-line-focus focus-visible:inset-ring-1 focus-visible:inset-ring-line-focus data-focused:border-line-focus data-focused:inset-ring-1 data-focused:inset-ring-line-focus data-popup-open:rounded-2xl data-popup-open:border-line-focus data-popup-open:inset-ring-1 data-popup-open:inset-ring-line-focus disabled:pointer-events-none disabled:cursor-not-allowed disabled:border-line-subtle disabled:bg-surface disabled:text-fg-caption-placeholder data-disabled:pointer-events-none data-disabled:cursor-not-allowed data-disabled:border-line-subtle data-disabled:bg-surface data-disabled:text-fg-caption-placeholder data-placeholder:text-fg-caption-placeholder aria-invalid:border-line-danger aria-invalid:text-fg-danger aria-invalid:shadow-none data-[invalid=true]:border-line-danger data-[invalid=true]:text-fg-danger data-[invalid=true]:focus-visible:border-line-danger data-[invalid=true]:focus-visible:inset-ring-line-danger data-[invalid=true]:data-focused:border-line-danger data-[invalid=true]:data-focused:inset-ring-line-danger data-[invalid=true]:data-popup-open:border-line-danger data-[invalid=true]:data-popup-open:inset-ring-line-danger *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:min-w-0 *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0", {
25
25
  variants: { size: {
26
26
  sm: "h-8 gap-2 px-3 text-gdt-xs [&_svg:not([class*='size-'])]:size-3.5",
27
27
  md: "h-10 gap-2 px-3.5 text-gdt-sm [&_svg:not([class*='size-'])]:size-4",