torch-glare 2.5.3 → 2.5.4
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.
- package/apps/lib/components/ActionButton.tsx +39 -6
- package/apps/lib/components/Badge.tsx +5 -2
- package/apps/lib/components/BadgeField.tsx +1 -1
- package/apps/lib/components/Button.tsx +4 -4
- package/apps/lib/components/Calendar.tsx +7 -17
- package/apps/lib/components/ColorPicker.tsx +1 -1
- package/apps/lib/components/DataViews/views/table-view.tsx +33 -4
- package/apps/lib/components/Drawer.tsx +18 -1
- package/apps/lib/components/FormRenderer/stepper.tsx +22 -16
- package/apps/lib/components/Input.tsx +19 -4
- package/apps/lib/components/Popover.tsx +93 -55
- package/apps/lib/components/SearchableSelect.tsx +12 -11
- package/apps/lib/components/SearchableTree.tsx +10 -11
- package/apps/lib/components/SearchableTreeDialog.tsx +10 -11
- package/apps/lib/components/SectionBlock.tsx +3 -1
- package/apps/lib/components/Select.tsx +35 -11
- package/apps/lib/components/SimpleSelect.tsx +2 -2
- package/apps/lib/components/SlideDatePicker.tsx +17 -4
- package/apps/lib/components/Stepper.tsx +329 -180
- package/apps/lib/components/Switch.tsx +2 -2
- package/apps/lib/components/Table.tsx +1 -1
- package/apps/lib/components/Textarea.tsx +29 -4
- package/apps/lib/components/TreeDropDown.tsx +18 -6
- package/apps/lib/registry.json +7 -16
- package/apps/lib/tsconfig.tsbuildinfo +1 -1
- package/docs/components/action-button.md +3 -1
- package/docs/components/stepper.md +119 -24
- package/docs/reference/tailwind-plugins.md +42 -0
- package/docs/tutorials/getting-started.md +32 -11
- package/package.json +1 -1
- package/apps/lib/components/FormStepper.tsx +0 -272
- package/docs/components/form-stepper.md +0 -250
|
@@ -4,12 +4,24 @@ import { Button } from "./Button";
|
|
|
4
4
|
import { cn } from "../utils/cn";
|
|
5
5
|
import { ButtonVariant, Themes } from "../utils/types";
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
/**
|
|
8
|
+
* The button that sits at the end of an input field — Figma `ActionButton` (Type=DropDown).
|
|
9
|
+
*
|
|
10
|
+
* Sizes are Figma's exactly: Xs 18, S 22, M 32, with `radius/sm` on the two small ones and
|
|
11
|
+
* `radius/lg` on M.
|
|
12
|
+
*
|
|
13
|
+
* The `!` on the radius is load-bearing. `twMerge` does not dedupe `rounded-radius-*` — they are
|
|
14
|
+
* custom scale keys, not values it recognises — so this radius and the one coming from `Button`'s
|
|
15
|
+
* own `size` variant both survive, and CSS source order picks the winner. Tailwind emits the scale
|
|
16
|
+
* in config order (sm before md before lg), so without `!` size S would render Button M's 6px
|
|
17
|
+
* instead of its own 4px.
|
|
18
|
+
*/
|
|
19
|
+
const buttonVariants = cva("", {
|
|
8
20
|
variants: {
|
|
9
21
|
size: {
|
|
10
|
-
XS: "h-[18px] w-[18px] text-[12px]",
|
|
11
|
-
S: "h-[22px] w-[22px] text-[12px]",
|
|
12
|
-
M: "h-[32px] w-[32px] text-[18px]",
|
|
22
|
+
XS: "h-[18px] w-[18px] text-[12px] !rounded-radius-sm",
|
|
23
|
+
S: "h-[22px] w-[22px] text-[12px] !rounded-radius-sm",
|
|
24
|
+
M: "h-[32px] w-[32px] text-[18px] !rounded-radius-lg",
|
|
13
25
|
},
|
|
14
26
|
},
|
|
15
27
|
defaultVariants: {
|
|
@@ -17,6 +29,23 @@ const buttonVariants = cva("!rounded-[4px]", {
|
|
|
17
29
|
},
|
|
18
30
|
});
|
|
19
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Default/hover/disabled colours as Figma binds them. Applied only when the caller passes no
|
|
34
|
+
* `variant`, so anything asking for a specific Button variant still gets it.
|
|
35
|
+
*
|
|
36
|
+
* `action-secondary` and `action-disabled` already match `Button`'s `PrimeStyle` equivalents; the
|
|
37
|
+
* real difference is hover, where `PrimeStyle` uses `button-hover` and the design uses
|
|
38
|
+
* `action-hover`.
|
|
39
|
+
*/
|
|
40
|
+
const actionTokens = [
|
|
41
|
+
"bg-background-presentation-action-secondary",
|
|
42
|
+
"text-content-presentation-action-light-primary",
|
|
43
|
+
"hover:bg-background-presentation-action-hover",
|
|
44
|
+
"hover:text-content-presentation-global-hover",
|
|
45
|
+
"disabled:bg-background-presentation-action-disabled",
|
|
46
|
+
"disabled:text-content-presentation-state-disabled",
|
|
47
|
+
];
|
|
48
|
+
|
|
20
49
|
interface Props
|
|
21
50
|
extends ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {
|
|
22
51
|
is_loading?: boolean;
|
|
@@ -29,7 +58,6 @@ interface Props
|
|
|
29
58
|
export const ActionButton = function ({
|
|
30
59
|
size,
|
|
31
60
|
asChild,
|
|
32
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- excluded from {...props} spread
|
|
33
61
|
as: Tag = "button",
|
|
34
62
|
className,
|
|
35
63
|
variant,
|
|
@@ -42,14 +70,19 @@ export const ActionButton = function ({
|
|
|
42
70
|
<Button
|
|
43
71
|
theme={theme}
|
|
44
72
|
asChild={asChild}
|
|
73
|
+
// Forwarded, not swallowed: the chevron inside a field renders `as="span"` because it is
|
|
74
|
+
// nested in a clickable trigger, and a <button> cannot contain a <button>.
|
|
75
|
+
as={Tag}
|
|
45
76
|
buttonType="icon"
|
|
46
|
-
type
|
|
77
|
+
// `type` is only meaningful on a real button; on a span it would be an invalid attribute.
|
|
78
|
+
type={Tag === "button" ? type : undefined}
|
|
47
79
|
size={size == "XS" ? "S" : size == "S" ? "M" : size == "M" ? "L" : "S"}
|
|
48
80
|
variant={variant}
|
|
49
81
|
className={cn(
|
|
50
82
|
buttonVariants({
|
|
51
83
|
size,
|
|
52
84
|
}),
|
|
85
|
+
!variant && actionTokens,
|
|
53
86
|
className,
|
|
54
87
|
)}
|
|
55
88
|
{...props}
|
|
@@ -118,7 +118,10 @@ const subtleCompoundVariants = [
|
|
|
118
118
|
export const badgeStyles = cva(
|
|
119
119
|
[
|
|
120
120
|
"inline-flex items-center justify-center w-fit",
|
|
121
|
-
|
|
121
|
+
// `radius/md` (6px), read off the Badge master component in Figma. Note the badge instances
|
|
122
|
+
// nested inside InnerLabelField render at `radius/lg` — that is an instance override, not the
|
|
123
|
+
// component's own value, and reading it as the spec is how this briefly shipped as 8px.
|
|
124
|
+
"rounded-radius-md",
|
|
122
125
|
"transition-all duration-200 ease-in-out",
|
|
123
126
|
"whitespace-nowrap",
|
|
124
127
|
"[&_i]:!leading-none",
|
|
@@ -215,7 +218,7 @@ export const Badge: React.FC<BadgeProps> = ({
|
|
|
215
218
|
}
|
|
216
219
|
}}
|
|
217
220
|
className={cn(
|
|
218
|
-
"rounded-
|
|
221
|
+
"rounded-radius-sm",
|
|
219
222
|
"flex items-center justify-center cursor-pointer",
|
|
220
223
|
"hover:bg-background-presentation-action-secondary",
|
|
221
224
|
"transition-colors duration-150",
|
|
@@ -222,7 +222,7 @@ export const BadgeField = forwardRef<HTMLInputElement, Props>(
|
|
|
222
222
|
fill="black"
|
|
223
223
|
/>
|
|
224
224
|
</svg>
|
|
225
|
-
<p className="text-black-1000 text-
|
|
225
|
+
<p className="text-black-1000 text-end text-[12px] font-[510] leading-[148%]">
|
|
226
226
|
{addLabel}
|
|
227
227
|
</p>
|
|
228
228
|
</div>
|
|
@@ -248,10 +248,10 @@ export const buttonVariants = cva(
|
|
|
248
248
|
],
|
|
249
249
|
},
|
|
250
250
|
size: {
|
|
251
|
-
S: "h-[22px] py-[2px] px-[6px] typography-body-small-medium rounded-
|
|
252
|
-
M: "h-[28px] py-[2px] px-[14px] typography-body-large-medium rounded-
|
|
253
|
-
L: "h-[34px] py-[5px] px-[22px] typography-body-large-medium rounded-
|
|
254
|
-
XL: "h-[40px] py-[8px] px-[30px] typography-headers-medium-medium rounded-
|
|
251
|
+
S: "h-[22px] py-[2px] px-[6px] typography-body-small-medium rounded-radius-sm [&_i]:text-[12px]",
|
|
252
|
+
M: "h-[28px] py-[2px] px-[14px] typography-body-large-medium rounded-radius-md [&_i]:text-[18px]",
|
|
253
|
+
L: "h-[34px] py-[5px] px-[22px] typography-body-large-medium rounded-radius-lg [&_i]:text-[20px]",
|
|
254
|
+
XL: "h-[40px] py-[8px] px-[30px] typography-headers-medium-medium rounded-radius-lg [&_i]:text-[22px]",
|
|
255
255
|
},
|
|
256
256
|
is_loading: {
|
|
257
257
|
true: "[&_i]:hidden",
|
|
@@ -4,7 +4,7 @@ import * as React from "react";
|
|
|
4
4
|
import { DayPicker } from "react-day-picker";
|
|
5
5
|
import "react-day-picker/style.css";
|
|
6
6
|
import { cn } from "../utils/cn";
|
|
7
|
-
import {
|
|
7
|
+
import { ActionButton } from "./ActionButton";
|
|
8
8
|
import { SimpleSelectValue, SimpleSelectItem } from "./SimpleSelect";
|
|
9
9
|
|
|
10
10
|
export type CalendarProps = React.ComponentProps<typeof DayPicker>;
|
|
@@ -30,24 +30,14 @@ const Calendar = ({
|
|
|
30
30
|
Nav: (e) => {
|
|
31
31
|
return (
|
|
32
32
|
<div className="w-full flex items-center justify-between absolute top-0 left-0 p-[6px]">
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
variant="PrimeStyle"
|
|
37
|
-
size="M"
|
|
38
|
-
className="w-[26px] h-[26px]"
|
|
39
|
-
>
|
|
33
|
+
{/* Popover navigation rather than an in-field button, so it keeps its own 26px box
|
|
34
|
+
and only adopts the shared action colours and radius. */}
|
|
35
|
+
<ActionButton onClick={e.onPreviousClick} size="S" className="w-[26px] h-[26px]">
|
|
40
36
|
<i className="ri-arrow-left-s-line"></i>
|
|
41
|
-
</
|
|
42
|
-
<
|
|
43
|
-
onClick={e.onNextClick}
|
|
44
|
-
buttonType="icon"
|
|
45
|
-
variant="PrimeStyle"
|
|
46
|
-
size="M"
|
|
47
|
-
className="w-[26px] h-[26px]"
|
|
48
|
-
>
|
|
37
|
+
</ActionButton>
|
|
38
|
+
<ActionButton onClick={e.onNextClick} size="S" className="w-[26px] h-[26px]">
|
|
49
39
|
<i className="ri-arrow-right-s-line"></i>
|
|
50
|
-
</
|
|
40
|
+
</ActionButton>
|
|
51
41
|
</div>
|
|
52
42
|
);
|
|
53
43
|
},
|
|
@@ -404,7 +404,7 @@ export const ColorPicker = forwardRef<HTMLElement, ColorPickerProps>(
|
|
|
404
404
|
}}
|
|
405
405
|
className={NUM_INPUT_CLS}
|
|
406
406
|
/>
|
|
407
|
-
<span className="
|
|
407
|
+
<span className="pe-1 typography-body-small-regular text-content-presentation-action-light-secondary">
|
|
408
408
|
%
|
|
409
409
|
</span>
|
|
410
410
|
</Group>
|
|
@@ -132,7 +132,18 @@ function TableViewImpl({
|
|
|
132
132
|
// pushes the whole component wide instead of scrolling within it.
|
|
133
133
|
className="min-w-0 flex-1 overflow-auto rounded-lg"
|
|
134
134
|
>
|
|
135
|
-
|
|
135
|
+
{/* One width for the table and the footer bar below it. Inside an `overflow-auto`
|
|
136
|
+
scroller a child's `w-full` resolves against the *visible* width, not the scrollable
|
|
137
|
+
width — so without this box the footer stopped at the fold whenever the columns
|
|
138
|
+
overflowed. `w-max` sizes to the table (the widest child); `min-w-full` keeps it
|
|
139
|
+
filling the scroller when the table is narrower. */}
|
|
140
|
+
<div className="w-max min-w-full">
|
|
141
|
+
{/* `overflow-visible` opts out of the primitive's `overflow-hidden` (tailwind-merge
|
|
142
|
+
lets ours win). It has to: a clipping table is its own scrollport, and the sticky
|
|
143
|
+
header would then resolve against a box that never scrolls. Safe here because the
|
|
144
|
+
scroller above is `min-w-0 overflow-auto`, so a wide table scrolls inside it rather
|
|
145
|
+
than pushing the layout. */}
|
|
146
|
+
<Table ref={tableRef} className="w-full overflow-visible">
|
|
136
147
|
{/* The header sticks to the scroller above by default — that lives on `TableHeader`
|
|
137
148
|
in the primitive. Two things this view adds:
|
|
138
149
|
|
|
@@ -228,7 +239,7 @@ function TableViewImpl({
|
|
|
228
239
|
>
|
|
229
240
|
{onRowMove && <GripCell />}
|
|
230
241
|
{selectable && (
|
|
231
|
-
<TableCell isDummy className="h-[40px] w-12" onClick={(e) => e.stopPropagation()}>
|
|
242
|
+
<TableCell isDummy className="min-h-[40px] w-12" onClick={(e) => e.stopPropagation()}>
|
|
232
243
|
{/* What `TableCheckbox` renders, inlined: its props are typed as button
|
|
233
244
|
attributes, so it cannot express a controlled checkbox. */}
|
|
234
245
|
<div className="flex items-center justify-center">
|
|
@@ -245,8 +256,20 @@ function TableViewImpl({
|
|
|
245
256
|
// `undefined` means "you paint it" — distinct from `null`, which is a
|
|
246
257
|
// deliberate blank.
|
|
247
258
|
const custom = renderCell?.({ field, row, id, index, fields: visibleFields });
|
|
259
|
+
// `fade={false}` and clip instead. The fade is a `mask-image` on every
|
|
260
|
+
// cell, close to free on its own but very expensive once a sticky header
|
|
261
|
+
// repaints the region each scroll frame: measured on this page,
|
|
262
|
+
// mask+sticky costs ~61ms/frame against ~29ms with the mask off and
|
|
263
|
+
// ~17ms with neither. `overflow-hidden` is passed back because
|
|
264
|
+
// `fade={false}` otherwise switches the cell to `overflow-visible`, and
|
|
265
|
+
// the text would spill into the next column.
|
|
248
266
|
return (
|
|
249
|
-
<TableCell
|
|
267
|
+
<TableCell
|
|
268
|
+
key={`${field.path}-${i}`}
|
|
269
|
+
className="min-h-[40px]"
|
|
270
|
+
fade={false}
|
|
271
|
+
childrenClassName="overflow-hidden"
|
|
272
|
+
>
|
|
250
273
|
{/* `isolate` confines the Badge's mix-blend-luminosity to a local
|
|
251
274
|
stacking context and `transform-gpu` promotes it to its own layer, so
|
|
252
275
|
the table's post-mount column reflow repaints cleanly instead of
|
|
@@ -287,6 +310,7 @@ function TableViewImpl({
|
|
|
287
310
|
{addRowLabel}
|
|
288
311
|
</TableEndAction>
|
|
289
312
|
)}
|
|
313
|
+
</div>
|
|
290
314
|
</div>
|
|
291
315
|
</div>
|
|
292
316
|
</div>
|
|
@@ -419,7 +443,12 @@ function SkeletonRows({
|
|
|
419
443
|
{grip && <TableCell isDummy className="h-[40px] w-8" />}
|
|
420
444
|
{selectable && <TableCell isDummy className="h-[40px] w-12" />}
|
|
421
445
|
{fields.map((field, i) => (
|
|
422
|
-
<TableCell
|
|
446
|
+
<TableCell
|
|
447
|
+
key={`${field.path}-${i}`}
|
|
448
|
+
className="min-h-[40px]"
|
|
449
|
+
fade={false}
|
|
450
|
+
childrenClassName="overflow-hidden"
|
|
451
|
+
>
|
|
423
452
|
{/* Widths cycle rather than repeat, so a column of bars reads as text that has not
|
|
424
453
|
arrived instead of as a grid. */}
|
|
425
454
|
<SkeletonBar className={["w-[70%]", "w-[45%]", "w-[85%]", "w-[60%]"][(row + i) % 4]} />
|
|
@@ -37,6 +37,7 @@ DrawerOverlay.displayName = DrawerPrimitive.Overlay.displayName;
|
|
|
37
37
|
interface DrawerContentProps extends React.ComponentPropsWithoutRef<
|
|
38
38
|
typeof DrawerPrimitive.Content
|
|
39
39
|
> {
|
|
40
|
+
/** @deprecated No effect here — the drag handle is `DrawerPanel`'s `showHandle`. */
|
|
40
41
|
showHandle?: boolean;
|
|
41
42
|
notch?: React.ReactNode;
|
|
42
43
|
notchSide?: "left" | "right";
|
|
@@ -87,7 +88,17 @@ const DrawerPanel = React.forwardRef<HTMLDivElement, DrawerPanelProps>(
|
|
|
87
88
|
// would render white-on-light = invisible).
|
|
88
89
|
data-theme="light"
|
|
89
90
|
className={cn(
|
|
90
|
-
|
|
91
|
+
// `flex-col` is load-bearing: this panel stacks header / body / footer, and its own
|
|
92
|
+
// `showHandle` centres the drag handle with `mx-auto`, which only centres in a column.
|
|
93
|
+
// It was dropped in 37fddd5 alongside the tray's — on the tray that was deliberate (FormDrawer
|
|
94
|
+
// puts the panel and its summary side by side), here it was not, and every consumer that puts
|
|
95
|
+
// a header/body/footer straight into a panel rendered them in a row.
|
|
96
|
+
//
|
|
97
|
+
// `min-w-0` for the same reason, one axis over: the tray is a row now, so width is its
|
|
98
|
+
// main axis, and a flex item's default `min-width: auto` pins it to its content — a wide
|
|
99
|
+
// form pushed the panel straight out past the tray. `min-h-0` alone covered the old
|
|
100
|
+
// column tray; the row needs both.
|
|
101
|
+
"flex flex-1 flex-col gap-2 rounded-t-[16px] p-1 bg-[#F0F0F0] min-h-0 min-w-0",
|
|
91
102
|
framed && "border border-[#D4D4D4] shadow-[inset_0_-4px_16px_rgba(0,0,0,0.1)]",
|
|
92
103
|
className,
|
|
93
104
|
)}
|
|
@@ -113,6 +124,12 @@ const DrawerContent = React.forwardRef<
|
|
|
113
124
|
framed: framedProp,
|
|
114
125
|
wrapperClassName,
|
|
115
126
|
trayClassName,
|
|
127
|
+
// Destructured only to keep it out of `...props`. The handle belongs to `DrawerPanel` now, so
|
|
128
|
+
// this does nothing here — but it was never pulled out either, so it was being spread onto the
|
|
129
|
+
// DOM node and React warned "does not recognize the `showHandle` prop on a DOM element" every
|
|
130
|
+
// time a drawer opened. Kept in the props type rather than removed: consumers still pass it.
|
|
131
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
132
|
+
showHandle,
|
|
116
133
|
...props
|
|
117
134
|
},
|
|
118
135
|
ref,
|
|
@@ -10,7 +10,16 @@ import { Button } from "../Button";
|
|
|
10
10
|
// stepper — the rail, the state, Back/Next — is chrome and lives here. The dependency only ever
|
|
11
11
|
// points this way: FormRenderer → FormBuilder, never back.
|
|
12
12
|
import { StepContext, type StepRegistry } from "../FormBuilder/context";
|
|
13
|
-
|
|
13
|
+
// Aliased: this module exports its own `Stepper` and `Step` (the wizard behaviour —
|
|
14
|
+
// `FormRenderer.Stepper` / `.Step`), which would otherwise collide with the presentational
|
|
15
|
+
// component of the same name.
|
|
16
|
+
import {
|
|
17
|
+
Stepper as StepperRail,
|
|
18
|
+
Step as StepPill,
|
|
19
|
+
StepIndicator,
|
|
20
|
+
StepLabel,
|
|
21
|
+
StepConnector,
|
|
22
|
+
} from "../Stepper";
|
|
14
23
|
|
|
15
24
|
// ─── Stepper state context ───────────────────────────────────────────────────
|
|
16
25
|
|
|
@@ -116,7 +125,7 @@ function StepperNav({ control }: { control: Control<FieldValues> }) {
|
|
|
116
125
|
// `min-w-0` so the rail can be squeezed: its grid track no longer grows to fit a long label
|
|
117
126
|
// (see form-renderer.tsx), so the column has to be allowed to shrink and let the labels
|
|
118
127
|
// truncate instead of spilling over the fields column.
|
|
119
|
-
<
|
|
128
|
+
<StepperRail activeStep={currentStep} orientation="vertical" className="min-w-0 shrink-0">
|
|
120
129
|
{titles.map((title, index) => {
|
|
121
130
|
// The step buttons ARE the navigation: click to move. Backward is free;
|
|
122
131
|
// clicking forward validates the steps in between (goToStep) and stops at
|
|
@@ -129,11 +138,11 @@ function StepperNav({ control }: { control: Control<FieldValues> }) {
|
|
|
129
138
|
: "default";
|
|
130
139
|
return (
|
|
131
140
|
<React.Fragment key={title}>
|
|
132
|
-
{/* The shrink/truncate pair is passed in rather than changed in `
|
|
141
|
+
{/* The shrink/truncate pair is passed in rather than changed in `Stepper`, so its
|
|
133
142
|
other consumers keep sizing to their labels. `shrink` beats the pill's own
|
|
134
143
|
`shrink-0` and `truncate` beats the label's `whitespace-nowrap` — both via
|
|
135
|
-
tailwind-merge, which `
|
|
136
|
-
<
|
|
144
|
+
tailwind-merge, which `Stepper` runs the caller's className through. */}
|
|
145
|
+
<StepPill
|
|
137
146
|
index={index}
|
|
138
147
|
type={type}
|
|
139
148
|
onClick={() => void goToStep(index)}
|
|
@@ -142,20 +151,17 @@ function StepperNav({ control }: { control: Control<FieldValues> }) {
|
|
|
142
151
|
// past the track instead of clipping inside it.
|
|
143
152
|
className="min-w-0 max-w-full shrink"
|
|
144
153
|
>
|
|
145
|
-
<
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
className="ms-[12.5px] mt-[2px] h-[16px] w-[3px] rounded-full bg-[#A0A0A0]"
|
|
153
|
-
/>
|
|
154
|
-
)}
|
|
154
|
+
<StepIndicator />
|
|
155
|
+
{/* `max-w-none` opts out of the component's 106px cap: the rail sizes to its own
|
|
156
|
+
grid track, so the label should truncate there rather than 106px early. */}
|
|
157
|
+
<StepLabel className="max-w-none truncate">{title}</StepLabel>
|
|
158
|
+
</StepPill>
|
|
159
|
+
{/* Connector between steps — the component centres it under the indicator per size. */}
|
|
160
|
+
{index < titles.length - 1 && <StepConnector />}
|
|
155
161
|
</React.Fragment>
|
|
156
162
|
);
|
|
157
163
|
})}
|
|
158
|
-
</
|
|
164
|
+
</StepperRail>
|
|
159
165
|
);
|
|
160
166
|
}
|
|
161
167
|
|
|
@@ -62,7 +62,16 @@ interface TrillingProps extends HTMLAttributes<HTMLDivElement> {
|
|
|
62
62
|
}
|
|
63
63
|
export const Trilling = ({ children, className, ...props }: TrillingProps) => {
|
|
64
64
|
return (
|
|
65
|
-
|
|
65
|
+
// `ps-1` mirrors the inner side of Figma's `EndField-Container` (`p-[4px]`), keeping the field's
|
|
66
|
+
// text clear of the end button instead of running straight into it.
|
|
67
|
+
//
|
|
68
|
+
// Logical, not physical: this slot flips to the left edge in RTL, so a `pl-1` would put the
|
|
69
|
+
// padding on the outside — measured as 0px of clearance and the button 8px from the edge
|
|
70
|
+
// instead of 4px. `padding-inline-start` follows the slot.
|
|
71
|
+
<div
|
|
72
|
+
{...props}
|
|
73
|
+
className={cn("flex items-center justify-center h-full gap-1 py-1 ps-1", className)}
|
|
74
|
+
>
|
|
66
75
|
{children}
|
|
67
76
|
</div>
|
|
68
77
|
);
|
|
@@ -135,7 +144,10 @@ input:-webkit-autofill:active {
|
|
|
135
144
|
|
|
136
145
|
export const GroupStyles = cva(
|
|
137
146
|
[
|
|
138
|
-
|
|
147
|
+
// 3px, not 4: Figma's `EndField-Container` is `p-[4px]` and its stroke sits *inside* that
|
|
148
|
+
// padding, so the end button lands 4px from the field's outer edge. CSS `border-box` adds the
|
|
149
|
+
// 1px border on top of the padding, so 4px here would render 5px. 1 + 3 = 4.
|
|
150
|
+
"flex w-full min-w-0 px-[3px] justify-center items-center",
|
|
139
151
|
"typography-body-small-regular",
|
|
140
152
|
"border",
|
|
141
153
|
"transition-all duration-200 ease-in-out",
|
|
@@ -180,14 +192,17 @@ export const GroupStyles = cva(
|
|
|
180
192
|
"[&_input]:text-white",
|
|
181
193
|
],
|
|
182
194
|
},
|
|
195
|
+
// Radius per Figma's `radius/*` collection: S is `radius/lg` (8px) and M is `radius/xl`
|
|
196
|
+
// (12px) — a deliberate jump, not consecutive steps. This `Group` is the shared field root,
|
|
197
|
+
// so it is also what gives `InputField` and `InnerLabelField` their corners.
|
|
183
198
|
size: {
|
|
184
199
|
S: [
|
|
185
200
|
"h-[30px] min-h-[30px]",
|
|
186
|
-
"rounded-
|
|
201
|
+
"rounded-radius-lg [&_input]:h-[30px] [&_div[data-role='icon']]:text-[16px]",
|
|
187
202
|
],
|
|
188
203
|
M: [
|
|
189
204
|
"h-[40px] min-h-[40px]",
|
|
190
|
-
"rounded-
|
|
205
|
+
"rounded-radius-xl [&_input]:h-[40px] [&_div[data-role='icon']]:text-[18px] [&_div[data-role='icon']]:px-[2px]",
|
|
191
206
|
],
|
|
192
207
|
},
|
|
193
208
|
error: {
|