@noya-app/noya-designsystem 0.1.48 → 0.1.49
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/.turbo/turbo-build.log +10 -10
- package/CHANGELOG.md +8 -0
- package/dist/index.css +1 -1
- package/dist/index.d.mts +213 -164
- package/dist/index.d.ts +213 -164
- package/dist/index.js +3583 -3290
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +3617 -3329
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -2
- package/src/__tests__/validateDropIndicator.test.ts +4 -4
- package/src/components/ActionMenu.tsx +15 -4
- package/src/components/BaseToolbar.tsx +2 -2
- package/src/components/Checkbox.tsx +2 -2
- package/src/components/Chip.tsx +7 -6
- package/src/components/Collection.tsx +19 -1
- package/src/components/Combobox.tsx +22 -23
- package/src/components/ComboboxMenu.tsx +1 -1
- package/src/components/DimensionInput.tsx +14 -12
- package/src/components/EditableText.tsx +3 -1
- package/src/components/FillInputField.tsx +2 -2
- package/src/components/Grid.tsx +133 -113
- package/src/components/GridView.tsx +36 -18
- package/src/components/InputField.tsx +116 -171
- package/src/components/Label.tsx +4 -68
- package/src/components/LabeledField.tsx +7 -1
- package/src/components/List.tsx +103 -44
- package/src/components/ListView.tsx +42 -26
- package/src/components/MediaThumbnail.tsx +3 -3
- package/src/components/Message.tsx +8 -8
- package/src/components/NoyaLogo.tsx +41 -0
- package/src/components/SegmentedControl.tsx +1 -1
- package/src/components/SelectMenu.tsx +9 -4
- package/src/components/Slider.tsx +16 -7
- package/src/components/Sortable.tsx +62 -25
- package/src/components/internal/Menu.tsx +8 -3
- package/src/components/internal/SelectItem.tsx +3 -2
- package/src/index.css +7 -1
- package/src/index.tsx +2 -0
- package/src/theme/index.ts +2 -0
- package/src/utils/classNames.ts +51 -3
- package/src/utils/inputs.ts +21 -0
- package/tailwind.config.ts +7 -0
|
@@ -12,7 +12,7 @@ import React, {
|
|
|
12
12
|
useMemo,
|
|
13
13
|
} from "react";
|
|
14
14
|
import { useHover } from "../hooks/useHover";
|
|
15
|
-
import { cx } from "../utils/classNames";
|
|
15
|
+
import { cx, mergeConflictingClassNames } from "../utils/classNames";
|
|
16
16
|
import withSeparatorElements from "../utils/withSeparatorElements";
|
|
17
17
|
import { ActivityIndicator } from "./ActivityIndicator";
|
|
18
18
|
import { ContextMenu } from "./ContextMenu";
|
|
@@ -96,6 +96,8 @@ interface ItemProps<MenuItemType extends string = string> {
|
|
|
96
96
|
action?: ReactNode;
|
|
97
97
|
onKeyDown?: (event: React.KeyboardEvent) => void;
|
|
98
98
|
hovered?: boolean;
|
|
99
|
+
testShowInsideDropIndicator?: boolean;
|
|
100
|
+
role?: string;
|
|
99
101
|
}
|
|
100
102
|
|
|
101
103
|
const GridViewItem = forwardRefGeneric(function GridViewItem<
|
|
@@ -120,6 +122,8 @@ const GridViewItem = forwardRefGeneric(function GridViewItem<
|
|
|
120
122
|
action,
|
|
121
123
|
onKeyDown,
|
|
122
124
|
hovered = false,
|
|
125
|
+
testShowInsideDropIndicator: showInsideDropIndicator = false,
|
|
126
|
+
...props
|
|
123
127
|
}: ItemProps<MenuItemType>,
|
|
124
128
|
forwardedRef: ForwardedRef<HTMLDivElement>
|
|
125
129
|
) {
|
|
@@ -157,13 +161,16 @@ const GridViewItem = forwardRefGeneric(function GridViewItem<
|
|
|
157
161
|
|
|
158
162
|
let element = (
|
|
159
163
|
<div
|
|
164
|
+
{...props}
|
|
160
165
|
className={cx(
|
|
161
|
-
"flex flex-col relative
|
|
166
|
+
"flex flex-col relative rounded p-2 -m-2 text-text",
|
|
162
167
|
"cursor-pointer",
|
|
163
168
|
"active:opacity-70",
|
|
164
169
|
selected && "bg-primary-pastel",
|
|
170
|
+
hovered && "bg-list-view-hover-background",
|
|
165
171
|
disabled && "opacity-50",
|
|
166
|
-
|
|
172
|
+
showInsideDropIndicator && "ring-2 ring-primary ring-inset",
|
|
173
|
+
"focus:ring-2 focus:ring-primary focus:ring-inset focus:outline-none"
|
|
167
174
|
)}
|
|
168
175
|
id={id}
|
|
169
176
|
ref={forwardedRef}
|
|
@@ -177,8 +184,7 @@ const GridViewItem = forwardRefGeneric(function GridViewItem<
|
|
|
177
184
|
<div
|
|
178
185
|
className={cx(
|
|
179
186
|
"flex items-center justify-center rounded",
|
|
180
|
-
bordered ? "border border-divider" : "border border-transparent"
|
|
181
|
-
selected ? "bg-primary-pastel" : "bg-input-background text-text"
|
|
187
|
+
bordered ? "border border-divider" : "border border-transparent"
|
|
182
188
|
)}
|
|
183
189
|
style={style}
|
|
184
190
|
>
|
|
@@ -267,6 +273,9 @@ interface GridViewRootProps extends Partial<GridViewContextValue> {
|
|
|
267
273
|
onDragEnter?: React.DragEventHandler<HTMLDivElement>;
|
|
268
274
|
onDragLeave?: React.DragEventHandler<HTMLDivElement>;
|
|
269
275
|
onDrop?: React.DragEventHandler<HTMLDivElement>;
|
|
276
|
+
role?: string;
|
|
277
|
+
renderEmptyState?: () => ReactNode;
|
|
278
|
+
contentClassName?: string;
|
|
270
279
|
}
|
|
271
280
|
|
|
272
281
|
const GridViewRoot = forwardRef(function GridViewRoot(
|
|
@@ -280,6 +289,7 @@ const GridViewRoot = forwardRef(function GridViewRoot(
|
|
|
280
289
|
bordered = false,
|
|
281
290
|
disabled = false,
|
|
282
291
|
className,
|
|
292
|
+
contentClassName,
|
|
283
293
|
...props
|
|
284
294
|
}: GridViewRootProps,
|
|
285
295
|
forwardedRef: ForwardedRef<HTMLDivElement>
|
|
@@ -305,25 +315,29 @@ const GridViewRoot = forwardRef(function GridViewRoot(
|
|
|
305
315
|
[bordered, disabled, gap, minColumnWidth, textPosition]
|
|
306
316
|
);
|
|
307
317
|
|
|
318
|
+
const mergedClassName = useMemo(() => {
|
|
319
|
+
const baseClassName = cx(
|
|
320
|
+
"flex flex-col -mx-3",
|
|
321
|
+
scrollable ? "basis-0 min-h-0" : "flex-none",
|
|
322
|
+
className
|
|
323
|
+
);
|
|
324
|
+
|
|
325
|
+
return mergeConflictingClassNames([baseClassName, className], {
|
|
326
|
+
categories: ["flex"],
|
|
327
|
+
});
|
|
328
|
+
}, [className, scrollable]);
|
|
329
|
+
|
|
330
|
+
const content = <div className={cx("p-3", contentClassName)}>{children}</div>;
|
|
331
|
+
|
|
308
332
|
return (
|
|
309
333
|
<GridViewContext.Provider value={contextValue}>
|
|
310
334
|
<div
|
|
311
335
|
onClick={handleClick}
|
|
312
|
-
className={
|
|
313
|
-
"flex flex-col -mx-3 -mb-1",
|
|
314
|
-
scrollable ? "basis-0 min-h-0" : "flex-none",
|
|
315
|
-
className
|
|
316
|
-
)}
|
|
336
|
+
className={mergedClassName}
|
|
317
337
|
{...props}
|
|
318
338
|
ref={forwardedRef}
|
|
319
339
|
>
|
|
320
|
-
{scrollable ?
|
|
321
|
-
<ScrollArea>
|
|
322
|
-
<div className="p-3">{children}</div>
|
|
323
|
-
</ScrollArea>
|
|
324
|
-
) : (
|
|
325
|
-
<div className="p-3">{children}</div>
|
|
326
|
-
)}
|
|
340
|
+
{scrollable ? <ScrollArea>{content}</ScrollArea> : content}
|
|
327
341
|
</div>
|
|
328
342
|
</GridViewContext.Provider>
|
|
329
343
|
);
|
|
@@ -350,7 +364,11 @@ function GridViewSection({
|
|
|
350
364
|
return isEmpty && renderEmptyState ? (
|
|
351
365
|
renderEmptyState()
|
|
352
366
|
) : (
|
|
353
|
-
<div
|
|
367
|
+
<div
|
|
368
|
+
role="presentation"
|
|
369
|
+
className={cx("grid text-text", className)}
|
|
370
|
+
style={styles}
|
|
371
|
+
>
|
|
354
372
|
{children}
|
|
355
373
|
</div>
|
|
356
374
|
);
|
|
@@ -6,7 +6,6 @@ import React, {
|
|
|
6
6
|
FocusEventHandler,
|
|
7
7
|
ForwardedRef,
|
|
8
8
|
forwardRef,
|
|
9
|
-
LabelHTMLAttributes,
|
|
10
9
|
memo,
|
|
11
10
|
ReactNode,
|
|
12
11
|
useCallback,
|
|
@@ -16,45 +15,35 @@ import React, {
|
|
|
16
15
|
useState,
|
|
17
16
|
} from "react";
|
|
18
17
|
import { useLabel, useLabelPosition } from "../hooks/useLabel";
|
|
19
|
-
import { cx } from "../utils/classNames";
|
|
18
|
+
import { cx, mergeConflictingClassNames } from "../utils/classNames";
|
|
20
19
|
import handleNudge from "../utils/handleNudge";
|
|
20
|
+
import { getInsetEndStyles, InputSize, startBaseStyles } from "../utils/inputs";
|
|
21
21
|
import { Button, ButtonRootProps } from "./Button";
|
|
22
22
|
import { DropdownMenu } from "./DropdownMenu";
|
|
23
23
|
import { MenuItem } from "./internal/Menu";
|
|
24
24
|
import TextInput, { TextInputProps } from "./internal/TextInput";
|
|
25
|
-
import {
|
|
25
|
+
import { Label } from "./Label";
|
|
26
26
|
import { Popover } from "./Popover";
|
|
27
27
|
|
|
28
28
|
export type InputFieldSize = "small" | "medium" | "large";
|
|
29
29
|
|
|
30
30
|
type InputFieldContextValue = {
|
|
31
|
-
labelWidth?: number;
|
|
32
|
-
buttonWidth?: number;
|
|
33
|
-
startWidth?: number;
|
|
34
|
-
/** @default medium */
|
|
35
|
-
size: InputFieldSize;
|
|
36
31
|
isFocused: boolean;
|
|
37
32
|
onFocusChange: (isFocused: boolean) => void;
|
|
38
33
|
inputRef: ForwardedRef<HTMLInputElement>;
|
|
39
|
-
buttonRef: ForwardedRef<HTMLButtonElement>;
|
|
40
|
-
labelRef: ForwardedRef<HTMLLabelElement>;
|
|
41
34
|
startRef: ForwardedRef<HTMLSpanElement>;
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
};
|
|
35
|
+
endRef: ForwardedRef<HTMLSpanElement>;
|
|
36
|
+
endWidth?: number;
|
|
37
|
+
} & Pick<InputFieldRootProps, "endWidth" | "startWidth" | "size" | "id">;
|
|
46
38
|
|
|
47
39
|
const InputFieldContext = createContext<InputFieldContextValue>({
|
|
48
|
-
labelWidth: undefined,
|
|
49
|
-
buttonWidth: undefined,
|
|
50
40
|
startWidth: undefined,
|
|
51
41
|
size: "medium",
|
|
52
42
|
isFocused: false,
|
|
53
43
|
onFocusChange: () => {},
|
|
54
44
|
inputRef: null,
|
|
55
|
-
buttonRef: null,
|
|
56
|
-
labelRef: null,
|
|
57
45
|
startRef: null,
|
|
46
|
+
endRef: null,
|
|
58
47
|
});
|
|
59
48
|
|
|
60
49
|
export const useInputFieldContext = () => useContext(InputFieldContext);
|
|
@@ -62,68 +51,22 @@ export const useInputFieldContext = () => useContext(InputFieldContext);
|
|
|
62
51
|
const noop = () => {};
|
|
63
52
|
|
|
64
53
|
export const getFieldSpacing = ({
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
startWidth,
|
|
54
|
+
endWidth = 0,
|
|
55
|
+
startWidth = 0,
|
|
68
56
|
variant,
|
|
69
57
|
}: {
|
|
70
|
-
|
|
71
|
-
labelWidth?: number;
|
|
58
|
+
endWidth?: number;
|
|
72
59
|
startWidth?: number;
|
|
73
60
|
variant?: InputFieldVariant;
|
|
74
61
|
}) => ({
|
|
75
62
|
paddingLeft: `${variant === "bare" ? startWidth : startWidth ? startWidth + 12 : 6}px`,
|
|
76
|
-
paddingRight: `${variant === "bare" ?
|
|
63
|
+
paddingRight: `${variant === "bare" ? endWidth : endWidth ? endWidth + 12 : 6}px`,
|
|
77
64
|
});
|
|
78
65
|
|
|
79
|
-
/* ----------------------------------------------------------------------------
|
|
80
|
-
* Label
|
|
81
|
-
* ------------------------------------------------------------------------- */
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Inserts a Label at the end of the input, unless overridden by LabeledField.
|
|
85
|
-
*/
|
|
86
|
-
const InputFieldLabel = memo(
|
|
87
|
-
forwardRef(function InputFieldLabel(
|
|
88
|
-
props: LabelHTMLAttributes<HTMLLabelElement>,
|
|
89
|
-
forwardedRef: ForwardedRef<HTMLLabelElement>
|
|
90
|
-
) {
|
|
91
|
-
const { labelRef, size, buttonWidth, label, id, labelPosition } =
|
|
92
|
-
useInputFieldContext();
|
|
93
|
-
|
|
94
|
-
const ref = useMergeRefs(labelRef, forwardedRef);
|
|
95
|
-
|
|
96
|
-
if (labelPosition !== "inset") return null;
|
|
97
|
-
|
|
98
|
-
return (
|
|
99
|
-
<InsetLabel
|
|
100
|
-
ref={ref}
|
|
101
|
-
size={size}
|
|
102
|
-
buttonWidth={buttonWidth}
|
|
103
|
-
htmlFor={id}
|
|
104
|
-
{...props}
|
|
105
|
-
>
|
|
106
|
-
{label}
|
|
107
|
-
</InsetLabel>
|
|
108
|
-
);
|
|
109
|
-
})
|
|
110
|
-
);
|
|
111
|
-
|
|
112
66
|
/* ----------------------------------------------------------------------------
|
|
113
67
|
* Dropdown
|
|
114
68
|
* ------------------------------------------------------------------------- */
|
|
115
69
|
|
|
116
|
-
const DropdownContainer = forwardRef<
|
|
117
|
-
HTMLSpanElement,
|
|
118
|
-
{ className?: string; children?: React.ReactNode }
|
|
119
|
-
>(({ className, ...props }, ref) => (
|
|
120
|
-
<span
|
|
121
|
-
ref={ref}
|
|
122
|
-
className={cx("absolute inset-y-0 right-0 flex flex-col", className)}
|
|
123
|
-
{...props}
|
|
124
|
-
/>
|
|
125
|
-
));
|
|
126
|
-
|
|
127
70
|
interface InputFieldDropdownProps<T extends string> {
|
|
128
71
|
id?: string;
|
|
129
72
|
items: MenuItem<T>[];
|
|
@@ -135,20 +78,18 @@ const InputFieldDropdownMenu = memoGeneric(function InputFieldDropdownMenu<
|
|
|
135
78
|
T extends string,
|
|
136
79
|
>({ id, items, onSelect, children }: InputFieldDropdownProps<T>) {
|
|
137
80
|
return (
|
|
138
|
-
<
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
<
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
</DropdownMenu>
|
|
151
|
-
</DropdownContainer>
|
|
81
|
+
<DropdownMenu<T>
|
|
82
|
+
items={items}
|
|
83
|
+
onSelect={onSelect}
|
|
84
|
+
align="end"
|
|
85
|
+
sideOffset={8}
|
|
86
|
+
>
|
|
87
|
+
{children || (
|
|
88
|
+
<InputFieldButton variant="floating" id={id}>
|
|
89
|
+
<DropdownChevronIcon />
|
|
90
|
+
</InputFieldButton>
|
|
91
|
+
)}
|
|
92
|
+
</DropdownMenu>
|
|
152
93
|
);
|
|
153
94
|
});
|
|
154
95
|
|
|
@@ -161,8 +102,6 @@ const InputFieldButton = memo(
|
|
|
161
102
|
forwardRef(function InputFieldButton(
|
|
162
103
|
props: React.ButtonHTMLAttributes<HTMLButtonElement> &
|
|
163
104
|
ButtonRootProps & {
|
|
164
|
-
buttonClassName?: string;
|
|
165
|
-
buttonStyle?: React.CSSProperties;
|
|
166
105
|
variant?: "floating" | "normal";
|
|
167
106
|
},
|
|
168
107
|
forwardedRef: ForwardedRef<HTMLButtonElement>
|
|
@@ -172,15 +111,11 @@ const InputFieldButton = memo(
|
|
|
172
111
|
onClick,
|
|
173
112
|
className,
|
|
174
113
|
style,
|
|
175
|
-
buttonClassName,
|
|
176
|
-
buttonStyle,
|
|
177
114
|
variant = "normal",
|
|
178
115
|
as = "button",
|
|
179
116
|
...rest
|
|
180
117
|
} = props;
|
|
181
|
-
const { size, inputRef
|
|
182
|
-
|
|
183
|
-
const ref = useMergeRefs(buttonRef, forwardedRef);
|
|
118
|
+
const { size, inputRef } = useInputFieldContext();
|
|
184
119
|
|
|
185
120
|
const defaultHandleClick = useCallback(
|
|
186
121
|
(event: React.MouseEvent<HTMLButtonElement>) => {
|
|
@@ -204,41 +139,35 @@ const InputFieldButton = memo(
|
|
|
204
139
|
[]
|
|
205
140
|
);
|
|
206
141
|
|
|
142
|
+
const memoizedStyle = useMemo(
|
|
143
|
+
() => ({
|
|
144
|
+
...(variant === "floating" && {
|
|
145
|
+
paddingLeft: "0px",
|
|
146
|
+
paddingRight: "0px",
|
|
147
|
+
}),
|
|
148
|
+
...style,
|
|
149
|
+
}),
|
|
150
|
+
[style, variant]
|
|
151
|
+
);
|
|
152
|
+
|
|
207
153
|
return (
|
|
208
|
-
<
|
|
209
|
-
|
|
154
|
+
<Button
|
|
155
|
+
as={as}
|
|
156
|
+
style={memoizedStyle}
|
|
157
|
+
ref={forwardedRef}
|
|
158
|
+
variant="floating"
|
|
159
|
+
onClick={onClick ?? defaultHandleClick}
|
|
160
|
+
onPointerDown={handlePointerDown}
|
|
161
|
+
tabIndex={-1}
|
|
162
|
+
size={size === "medium" ? "normal" : size}
|
|
210
163
|
className={cx(
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
? "right-1 top-2.5"
|
|
214
|
-
: size === "medium"
|
|
215
|
-
? "right-1 top-1"
|
|
216
|
-
: size === "small"
|
|
217
|
-
? "right-0.5 top-0.5"
|
|
218
|
-
: "",
|
|
219
|
-
"z-label",
|
|
220
|
-
variant === "floating" && "right-0",
|
|
221
|
-
className
|
|
164
|
+
className,
|
|
165
|
+
variant === "floating" && "bg-transparent shadow-none"
|
|
222
166
|
)}
|
|
167
|
+
{...rest}
|
|
223
168
|
>
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
ref={ref}
|
|
227
|
-
variant="floating"
|
|
228
|
-
onClick={onClick ?? defaultHandleClick}
|
|
229
|
-
onPointerDown={handlePointerDown}
|
|
230
|
-
tabIndex={-1}
|
|
231
|
-
size={size === "medium" ? "normal" : size}
|
|
232
|
-
className={cx(
|
|
233
|
-
buttonClassName,
|
|
234
|
-
variant === "floating" && "bg-transparent shadow-none"
|
|
235
|
-
)}
|
|
236
|
-
style={buttonStyle}
|
|
237
|
-
{...rest}
|
|
238
|
-
>
|
|
239
|
-
{children}
|
|
240
|
-
</Button>
|
|
241
|
-
</span>
|
|
169
|
+
{children}
|
|
170
|
+
</Button>
|
|
242
171
|
);
|
|
243
172
|
})
|
|
244
173
|
);
|
|
@@ -286,14 +215,12 @@ const InputElement = forwardRef(
|
|
|
286
215
|
}: InputElementProps,
|
|
287
216
|
forwardedRef: ForwardedRef<any>
|
|
288
217
|
) => {
|
|
289
|
-
const {
|
|
290
|
-
useInputFieldContext();
|
|
218
|
+
const { endWidth, inputRef, size, id, startWidth } = useInputFieldContext();
|
|
291
219
|
|
|
292
220
|
const ref = useMergeRefs(inputRef, forwardedRef);
|
|
293
221
|
|
|
294
222
|
const spacingStyles = getFieldSpacing({
|
|
295
|
-
|
|
296
|
-
labelWidth,
|
|
223
|
+
endWidth,
|
|
297
224
|
variant: $variant,
|
|
298
225
|
startWidth,
|
|
299
226
|
});
|
|
@@ -317,12 +244,20 @@ const InputElement = forwardRef(
|
|
|
317
244
|
className
|
|
318
245
|
);
|
|
319
246
|
|
|
247
|
+
const mergedClassName = useMemo(
|
|
248
|
+
() =>
|
|
249
|
+
mergeConflictingClassNames(inputClassName, {
|
|
250
|
+
categories: ["text"],
|
|
251
|
+
}),
|
|
252
|
+
[inputClassName]
|
|
253
|
+
);
|
|
254
|
+
|
|
320
255
|
if (as === "span") {
|
|
321
256
|
return (
|
|
322
257
|
<span
|
|
323
258
|
ref={ref}
|
|
324
259
|
{...props}
|
|
325
|
-
className={
|
|
260
|
+
className={mergedClassName}
|
|
326
261
|
style={{ ...spacingStyles, ...style }}
|
|
327
262
|
>
|
|
328
263
|
{children}
|
|
@@ -337,7 +272,7 @@ const InputElement = forwardRef(
|
|
|
337
272
|
id={id}
|
|
338
273
|
onSubmit={onSubmit ?? (() => {})}
|
|
339
274
|
{...props}
|
|
340
|
-
className={
|
|
275
|
+
className={mergedClassName}
|
|
341
276
|
style={{ ...spacingStyles, ...style }}
|
|
342
277
|
/>
|
|
343
278
|
);
|
|
@@ -549,16 +484,17 @@ const RootContainer = forwardRef<
|
|
|
549
484
|
/>
|
|
550
485
|
));
|
|
551
486
|
|
|
552
|
-
const startStyles = "absolute left-1.5 inset-y-0 flex items-center";
|
|
553
|
-
|
|
554
487
|
interface InputFieldRootProps {
|
|
555
488
|
id?: string;
|
|
556
489
|
children?: ReactNode;
|
|
490
|
+
start?: ReactNode;
|
|
491
|
+
end?: ReactNode;
|
|
492
|
+
label?: ReactNode;
|
|
557
493
|
width?: number;
|
|
558
|
-
labelWidth?: number;
|
|
559
494
|
startWidth?: number;
|
|
495
|
+
endWidth?: number;
|
|
560
496
|
/** @default medium */
|
|
561
|
-
size?:
|
|
497
|
+
size?: InputSize;
|
|
562
498
|
renderPopoverContent?: (options: { width: number }) => ReactNode;
|
|
563
499
|
/**
|
|
564
500
|
* if there is a popover, this is the offset from the trigger
|
|
@@ -568,38 +504,39 @@ interface InputFieldRootProps {
|
|
|
568
504
|
onFocusChange?: (isFocused: boolean) => void;
|
|
569
505
|
style?: React.CSSProperties;
|
|
570
506
|
className?: string;
|
|
571
|
-
start?: ReactNode;
|
|
572
507
|
}
|
|
573
508
|
|
|
574
509
|
function InputFieldRoot({
|
|
575
|
-
id
|
|
510
|
+
id,
|
|
576
511
|
children,
|
|
577
512
|
width,
|
|
578
513
|
sideOffset = 3,
|
|
579
|
-
|
|
580
|
-
|
|
514
|
+
startWidth: startWidthProp,
|
|
515
|
+
endWidth: endWidthProp,
|
|
581
516
|
size = "medium",
|
|
582
517
|
renderPopoverContent,
|
|
583
518
|
onFocusChange,
|
|
584
519
|
style,
|
|
585
520
|
className,
|
|
586
521
|
start,
|
|
522
|
+
end,
|
|
523
|
+
label: labelProp,
|
|
587
524
|
}: InputFieldRootProps) {
|
|
588
|
-
const { fieldId: id, label } = useLabel({ fieldId: idProp });
|
|
589
525
|
const randomId = useId();
|
|
590
|
-
const labelPosition = useLabelPosition();
|
|
591
526
|
|
|
592
527
|
const [isFocused, setIsFocused] = React.useState(false);
|
|
593
528
|
|
|
594
529
|
const startRef = React.useRef<HTMLSpanElement>(null);
|
|
595
|
-
const labelRef = React.useRef<HTMLLabelElement>(null);
|
|
596
|
-
const buttonRef = React.useRef<HTMLButtonElement>(null);
|
|
597
530
|
const inputRef = React.useRef<HTMLInputElement>(null);
|
|
531
|
+
const endRef = React.useRef<HTMLSpanElement>(null);
|
|
598
532
|
|
|
599
|
-
const measuredLabelSize = useSize(labelRef, "width");
|
|
600
|
-
const measuredButtonSize = useSize(buttonRef, "width");
|
|
601
533
|
const measuredInputSize = useSize(inputRef, "width");
|
|
602
534
|
const measuredStartSize = useSize(startRef, "width");
|
|
535
|
+
const measuredEndSize = useSize(endRef, "width");
|
|
536
|
+
const labelPosition = useLabelPosition();
|
|
537
|
+
const { label } = useLabel({ fieldId: id });
|
|
538
|
+
|
|
539
|
+
const endStyles = getInsetEndStyles(size);
|
|
603
540
|
|
|
604
541
|
const handleFocusChange = useCallback(
|
|
605
542
|
(isFocused: boolean) => {
|
|
@@ -609,48 +546,49 @@ function InputFieldRoot({
|
|
|
609
546
|
[onFocusChange]
|
|
610
547
|
);
|
|
611
548
|
|
|
549
|
+
const startWidth = measuredStartSize?.width ?? startWidthProp;
|
|
550
|
+
const endWidth = measuredEndSize?.width ?? endWidthProp;
|
|
551
|
+
|
|
612
552
|
const contextValue = useMemo(
|
|
613
553
|
(): InputFieldContextValue => ({
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
startWidth: measuredStartSize?.width ?? startWidth,
|
|
554
|
+
startWidth,
|
|
555
|
+
endWidth,
|
|
617
556
|
inputRef,
|
|
618
|
-
buttonRef,
|
|
619
|
-
labelRef,
|
|
620
557
|
startRef,
|
|
558
|
+
endRef,
|
|
621
559
|
size,
|
|
622
560
|
isFocused,
|
|
623
561
|
onFocusChange: handleFocusChange,
|
|
624
562
|
id: id ?? randomId,
|
|
625
|
-
label,
|
|
626
|
-
labelPosition,
|
|
627
563
|
}),
|
|
628
|
-
[
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
label,
|
|
639
|
-
labelPosition,
|
|
640
|
-
measuredStartSize,
|
|
641
|
-
startWidth,
|
|
642
|
-
]
|
|
564
|
+
[startWidth, endWidth, size, isFocused, handleFocusChange, id, randomId]
|
|
565
|
+
);
|
|
566
|
+
|
|
567
|
+
const insetLabel = useMemo(
|
|
568
|
+
() => (
|
|
569
|
+
<Label htmlFor={id} className="!text-text-disabled">
|
|
570
|
+
{labelProp ?? label}
|
|
571
|
+
</Label>
|
|
572
|
+
),
|
|
573
|
+
[id, labelProp, label]
|
|
643
574
|
);
|
|
644
575
|
|
|
645
576
|
const rootElement = (
|
|
646
577
|
<RootContainer $width={width} style={style} className={className}>
|
|
647
578
|
{children}
|
|
648
579
|
{start && (
|
|
649
|
-
<span ref={startRef} className={
|
|
580
|
+
<span ref={startRef} className={startBaseStyles}>
|
|
650
581
|
{start}
|
|
651
582
|
</span>
|
|
652
583
|
)}
|
|
653
|
-
{label &&
|
|
584
|
+
{(end || label) && (
|
|
585
|
+
<span ref={endRef} className={cx(endStyles, label && end && "gap-1.5")}>
|
|
586
|
+
{labelProp
|
|
587
|
+
? insetLabel
|
|
588
|
+
: label && labelPosition === "inset" && insetLabel}
|
|
589
|
+
{end}
|
|
590
|
+
</span>
|
|
591
|
+
)}
|
|
654
592
|
</RootContainer>
|
|
655
593
|
);
|
|
656
594
|
|
|
@@ -706,11 +644,10 @@ const PrimitiveInputField = ({
|
|
|
706
644
|
React.InputHTMLAttributes<HTMLInputElement>,
|
|
707
645
|
HTMLInputElement
|
|
708
646
|
>) => {
|
|
709
|
-
const { id,
|
|
647
|
+
const { id, endWidth } = useInputFieldContext();
|
|
710
648
|
|
|
711
649
|
const spacingStyles = getFieldSpacing({
|
|
712
|
-
|
|
713
|
-
labelWidth,
|
|
650
|
+
endWidth,
|
|
714
651
|
});
|
|
715
652
|
const memoizedStyles = useMemo(
|
|
716
653
|
() => ({ ...spacingStyles, ...style }),
|
|
@@ -745,16 +682,20 @@ const InputFieldSubcomponents = {
|
|
|
745
682
|
NumberInput: memo(InputFieldNumberInput),
|
|
746
683
|
DropdownMenu: InputFieldDropdownMenu,
|
|
747
684
|
Button: InputFieldButton,
|
|
748
|
-
Label: InputFieldLabel,
|
|
749
685
|
PrimitiveElement: PrimitiveInputField,
|
|
750
686
|
} as const;
|
|
751
687
|
|
|
752
688
|
// Create the main InputField component
|
|
753
689
|
const InputFieldComponent = forwardRef<HTMLInputElement, InputFieldProps>(
|
|
754
|
-
function InputField(
|
|
690
|
+
function InputField(props: InputFieldProps, ref) {
|
|
755
691
|
const {
|
|
692
|
+
children,
|
|
756
693
|
width,
|
|
757
|
-
|
|
694
|
+
endWidth,
|
|
695
|
+
startWidth,
|
|
696
|
+
label,
|
|
697
|
+
start,
|
|
698
|
+
end,
|
|
758
699
|
size,
|
|
759
700
|
renderPopoverContent,
|
|
760
701
|
sideOffset,
|
|
@@ -767,7 +708,11 @@ const InputFieldComponent = forwardRef<HTMLInputElement, InputFieldProps>(
|
|
|
767
708
|
return (
|
|
768
709
|
<InputFieldSubcomponents.Root
|
|
769
710
|
width={width}
|
|
770
|
-
|
|
711
|
+
endWidth={endWidth}
|
|
712
|
+
startWidth={startWidth}
|
|
713
|
+
label={label}
|
|
714
|
+
start={start}
|
|
715
|
+
end={end}
|
|
771
716
|
size={size}
|
|
772
717
|
renderPopoverContent={renderPopoverContent}
|
|
773
718
|
sideOffset={sideOffset}
|