@noya-app/noya-designsystem 0.1.41 → 0.1.43
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 +16 -0
- package/dist/index.css +1 -1
- package/dist/index.d.mts +268 -147
- package/dist/index.d.ts +268 -147
- package/dist/index.js +7618 -6908
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +6161 -5487
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -5
- package/src/__tests__/combobox.test.ts +578 -0
- package/src/components/ActivityIndicator.tsx +4 -4
- package/src/components/AnimatePresence.tsx +5 -5
- package/src/components/Avatar.tsx +5 -2
- package/src/components/BaseToolbar.tsx +61 -0
- package/src/components/Button.tsx +1 -1
- package/src/components/Checkbox.tsx +6 -5
- package/src/components/Combobox.tsx +327 -337
- package/src/components/ComboboxMenu.tsx +88 -0
- package/src/components/Dialog.tsx +10 -6
- package/src/components/DimensionInput.tsx +4 -4
- package/src/components/FillPreviewBackground.tsx +51 -44
- package/src/components/FloatingWindow.tsx +5 -2
- package/src/components/GradientPicker.tsx +14 -14
- package/src/components/IconButton.tsx +6 -12
- package/src/components/Icons.tsx +3 -3
- package/src/components/InputField.tsx +145 -160
- package/src/components/Label.tsx +4 -4
- package/src/components/LabeledElementView.tsx +35 -41
- package/src/components/ListView.tsx +49 -39
- package/src/components/Message.tsx +75 -0
- package/src/components/Progress.tsx +2 -2
- package/src/components/ScrollArea.tsx +7 -5
- package/src/components/SegmentedControl.tsx +171 -0
- package/src/components/SelectMenu.tsx +61 -10
- package/src/components/Slider.tsx +5 -2
- package/src/components/Sortable.tsx +17 -27
- package/src/components/Spacer.tsx +28 -9
- package/src/components/Switch.tsx +8 -8
- package/src/components/TextArea.tsx +59 -12
- package/src/components/Toolbar.tsx +129 -0
- package/src/components/Tooltip.tsx +10 -7
- package/src/components/WorkspaceLayout.tsx +145 -152
- package/src/components/internal/Menu.tsx +5 -4
- package/src/contexts/DesignSystemConfiguration.tsx +15 -24
- package/src/contexts/DialogContext.tsx +137 -49
- package/src/contexts/ImageDataContext.tsx +6 -12
- package/src/index.css +1 -3
- package/src/index.tsx +12 -3
- package/src/utils/combobox.ts +369 -0
- package/tailwind.config.ts +1 -2
- package/src/components/RadioGroup.tsx +0 -100
- package/src/utils/completions.ts +0 -21
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { DropdownChevronIcon } from "@noya-app/noya-icons";
|
|
2
2
|
import { assignRef, memoGeneric } from "@noya-app/react-utils";
|
|
3
3
|
import { Property } from "csstype";
|
|
4
4
|
import React, {
|
|
5
|
-
Children,
|
|
6
5
|
createContext,
|
|
7
6
|
FocusEventHandler,
|
|
8
7
|
ForwardedRef,
|
|
9
8
|
forwardRef,
|
|
10
|
-
isValidElement,
|
|
11
9
|
LabelHTMLAttributes,
|
|
12
10
|
memo,
|
|
13
11
|
ReactNode,
|
|
@@ -36,7 +34,6 @@ type InputFieldContextValue = {
|
|
|
36
34
|
labelPosition: LabelPosition;
|
|
37
35
|
labelSize?: number;
|
|
38
36
|
buttonSize?: number;
|
|
39
|
-
hasDropdown: boolean;
|
|
40
37
|
/** @default medium */
|
|
41
38
|
size: InputFieldSize;
|
|
42
39
|
isFocused: boolean;
|
|
@@ -48,23 +45,64 @@ type InputFieldContextValue = {
|
|
|
48
45
|
id?: string;
|
|
49
46
|
};
|
|
50
47
|
|
|
48
|
+
const getLabelPosition = ({
|
|
49
|
+
labelPosition,
|
|
50
|
+
size,
|
|
51
|
+
buttonSize,
|
|
52
|
+
}: {
|
|
53
|
+
labelPosition: LabelPosition;
|
|
54
|
+
size: InputFieldSize;
|
|
55
|
+
buttonSize?: number;
|
|
56
|
+
}) => ({
|
|
57
|
+
left:
|
|
58
|
+
labelPosition === "start"
|
|
59
|
+
? size === "large"
|
|
60
|
+
? "6px"
|
|
61
|
+
: size === "medium"
|
|
62
|
+
? "2px"
|
|
63
|
+
: "0px"
|
|
64
|
+
: "",
|
|
65
|
+
right:
|
|
66
|
+
labelPosition === "end"
|
|
67
|
+
? size === "large"
|
|
68
|
+
? `${6 + (buttonSize ?? 0)}px`
|
|
69
|
+
: size === "medium"
|
|
70
|
+
? `${2 + (buttonSize ?? 0)}px`
|
|
71
|
+
: `${buttonSize ?? 0}px`
|
|
72
|
+
: "",
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const getInputFieldSpacing = ({
|
|
76
|
+
labelPosition,
|
|
77
|
+
buttonSize,
|
|
78
|
+
labelSize,
|
|
79
|
+
}: {
|
|
80
|
+
labelPosition: LabelPosition;
|
|
81
|
+
size?: InputFieldSize;
|
|
82
|
+
buttonSize?: number;
|
|
83
|
+
labelSize?: number;
|
|
84
|
+
}) => ({
|
|
85
|
+
paddingLeft: `${labelSize && labelPosition === "start" ? 6 + labelSize : 6}px`,
|
|
86
|
+
paddingRight: `${labelPosition === "end" ? 6 + (labelSize ?? 0) + (buttonSize ? buttonSize + 6 : 0) : 6}px`,
|
|
87
|
+
});
|
|
88
|
+
|
|
51
89
|
const InputFieldContext = createContext<InputFieldContextValue>({
|
|
52
90
|
labelPosition: "end",
|
|
53
91
|
labelSize: undefined,
|
|
54
92
|
buttonSize: undefined,
|
|
55
|
-
hasDropdown: false,
|
|
56
93
|
size: "medium",
|
|
57
94
|
isFocused: false,
|
|
58
95
|
onFocusChange: () => {},
|
|
59
96
|
});
|
|
60
97
|
|
|
98
|
+
const noop = () => {};
|
|
99
|
+
|
|
61
100
|
/* ----------------------------------------------------------------------------
|
|
62
101
|
* Label
|
|
63
102
|
* ------------------------------------------------------------------------- */
|
|
64
103
|
|
|
65
104
|
interface LabelContainerProps {
|
|
66
105
|
labelPosition: LabelPosition;
|
|
67
|
-
hasDropdown: boolean;
|
|
68
106
|
size: InputFieldSize;
|
|
69
107
|
className?: string;
|
|
70
108
|
children?: React.ReactNode;
|
|
@@ -75,7 +113,6 @@ interface LabelContainerProps {
|
|
|
75
113
|
const LabelContainer = forwardRef(function LabelContainer(
|
|
76
114
|
{
|
|
77
115
|
labelPosition: $labelPosition,
|
|
78
|
-
hasDropdown: $hasDropdown,
|
|
79
116
|
size: $size,
|
|
80
117
|
className,
|
|
81
118
|
style,
|
|
@@ -84,27 +121,15 @@ const LabelContainer = forwardRef(function LabelContainer(
|
|
|
84
121
|
ref: ForwardedRef<HTMLLabelElement>
|
|
85
122
|
) {
|
|
86
123
|
const { buttonSize } = useContext(InputFieldContext);
|
|
124
|
+
const spacingStyles = getLabelPosition({
|
|
125
|
+
labelPosition: $labelPosition,
|
|
126
|
+
size: $size,
|
|
127
|
+
buttonSize,
|
|
128
|
+
});
|
|
129
|
+
|
|
87
130
|
const memoizedStyles = useMemo(
|
|
88
|
-
() => ({
|
|
89
|
-
|
|
90
|
-
$labelPosition === "start"
|
|
91
|
-
? $size === "large"
|
|
92
|
-
? "6px"
|
|
93
|
-
: $size === "medium"
|
|
94
|
-
? "2px"
|
|
95
|
-
: "0px"
|
|
96
|
-
: "",
|
|
97
|
-
right:
|
|
98
|
-
$labelPosition === "end"
|
|
99
|
-
? $size === "large"
|
|
100
|
-
? `${6 + (buttonSize ?? 0)}px`
|
|
101
|
-
: $size === "medium"
|
|
102
|
-
? `${2 + (buttonSize ?? 0)}px`
|
|
103
|
-
: `${buttonSize ?? 0}px`
|
|
104
|
-
: "",
|
|
105
|
-
...style,
|
|
106
|
-
}),
|
|
107
|
-
[$labelPosition, $size, buttonSize, style]
|
|
131
|
+
() => ({ ...spacingStyles, ...style }),
|
|
132
|
+
[spacingStyles, style]
|
|
108
133
|
);
|
|
109
134
|
|
|
110
135
|
return (
|
|
@@ -114,8 +139,7 @@ const LabelContainer = forwardRef(function LabelContainer(
|
|
|
114
139
|
"absolute top-0 bottom-0 flex items-center font-sans text-label font-bold uppercase text-text-disabled select-none z-label pointer-events-none",
|
|
115
140
|
$labelPosition === "start"
|
|
116
141
|
? `justify-start pl-1.5`
|
|
117
|
-
: $labelPosition === "end" &&
|
|
118
|
-
`justify-end ${$hasDropdown ? "pr-4" : "pr-1.5"}`,
|
|
142
|
+
: $labelPosition === "end" && "justify-end pr-1.5",
|
|
119
143
|
className
|
|
120
144
|
)}
|
|
121
145
|
style={memoizedStyles}
|
|
@@ -132,7 +156,7 @@ const InputFieldLabel = memo(
|
|
|
132
156
|
props: LabelHTMLAttributes<HTMLLabelElement>,
|
|
133
157
|
forwardedRef: ForwardedRef<HTMLLabelElement>
|
|
134
158
|
) {
|
|
135
|
-
const { labelPosition,
|
|
159
|
+
const { labelPosition, setLabelWidth, size, id } =
|
|
136
160
|
useContext(InputFieldContext);
|
|
137
161
|
|
|
138
162
|
const ref = useRef<HTMLLabelElement | null>(null);
|
|
@@ -155,7 +179,6 @@ const InputFieldLabel = memo(
|
|
|
155
179
|
}}
|
|
156
180
|
size={size}
|
|
157
181
|
labelPosition={labelPosition}
|
|
158
|
-
hasDropdown={hasDropdown}
|
|
159
182
|
htmlFor={id}
|
|
160
183
|
{...props}
|
|
161
184
|
/>
|
|
@@ -188,28 +211,18 @@ interface InputFieldDropdownProps<T extends string> {
|
|
|
188
211
|
const InputFieldDropdownMenu = memoGeneric(function InputFieldDropdownMenu<
|
|
189
212
|
T extends string,
|
|
190
213
|
>({ id, items, onSelect, children }: InputFieldDropdownProps<T>) {
|
|
191
|
-
const { size } = useContext(InputFieldContext);
|
|
192
|
-
|
|
193
|
-
const contentStyle = useMemo(
|
|
194
|
-
() => ({
|
|
195
|
-
margin: "-4px 0",
|
|
196
|
-
minHeight: size === "small" ? "15px" : undefined,
|
|
197
|
-
}),
|
|
198
|
-
[size]
|
|
199
|
-
);
|
|
200
|
-
|
|
201
214
|
return (
|
|
202
215
|
<DropdownContainer>
|
|
203
|
-
<DropdownMenu<T>
|
|
216
|
+
<DropdownMenu<T>
|
|
217
|
+
items={items}
|
|
218
|
+
onSelect={onSelect}
|
|
219
|
+
align="end"
|
|
220
|
+
sideOffset={8}
|
|
221
|
+
>
|
|
204
222
|
{children || (
|
|
205
|
-
<
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
className="flex flex-1"
|
|
209
|
-
contentStyle={contentStyle}
|
|
210
|
-
>
|
|
211
|
-
<CaretDownIcon />
|
|
212
|
-
</Button>
|
|
223
|
+
<InputFieldButton variant="floating" id={id}>
|
|
224
|
+
<DropdownChevronIcon />
|
|
225
|
+
</InputFieldButton>
|
|
213
226
|
)}
|
|
214
227
|
</DropdownMenu>
|
|
215
228
|
</DropdownContainer>
|
|
@@ -220,35 +233,23 @@ const InputFieldDropdownMenu = memoGeneric(function InputFieldDropdownMenu<
|
|
|
220
233
|
* Button
|
|
221
234
|
* ------------------------------------------------------------------------- */
|
|
222
235
|
|
|
223
|
-
const ButtonContainer = forwardRef<
|
|
224
|
-
HTMLSpanElement,
|
|
225
|
-
{ $size: InputFieldSize; className?: string; children?: React.ReactNode }
|
|
226
|
-
>(({ $size, className, ...props }, ref) => (
|
|
227
|
-
<span
|
|
228
|
-
ref={ref}
|
|
229
|
-
className={cx(
|
|
230
|
-
"absolute",
|
|
231
|
-
$size === "large"
|
|
232
|
-
? "right-[9px] top-2.5"
|
|
233
|
-
: $size === "medium"
|
|
234
|
-
? "right-1 top-1"
|
|
235
|
-
: $size === "small"
|
|
236
|
-
? "right-0.5 top-0.5"
|
|
237
|
-
: "",
|
|
238
|
-
"z-label",
|
|
239
|
-
className
|
|
240
|
-
)}
|
|
241
|
-
{...props}
|
|
242
|
-
/>
|
|
243
|
-
));
|
|
244
|
-
|
|
245
236
|
/** Inserts a Button at the end of the input. Should not be used with an InputField.Label */
|
|
246
237
|
const InputFieldButton = memo(
|
|
247
238
|
forwardRef(function InputFieldButton(
|
|
248
|
-
props: React.ButtonHTMLAttributes<HTMLButtonElement
|
|
239
|
+
props: React.ButtonHTMLAttributes<HTMLButtonElement> & {
|
|
240
|
+
buttonClassName?: string;
|
|
241
|
+
variant?: "floating" | "normal";
|
|
242
|
+
},
|
|
249
243
|
forwardedRef: ForwardedRef<HTMLButtonElement>
|
|
250
244
|
) {
|
|
251
|
-
const {
|
|
245
|
+
const {
|
|
246
|
+
children,
|
|
247
|
+
onClick,
|
|
248
|
+
className,
|
|
249
|
+
buttonClassName,
|
|
250
|
+
variant = "normal",
|
|
251
|
+
...rest
|
|
252
|
+
} = props;
|
|
252
253
|
const { size, inputRef, setButtonWidth } = useContext(InputFieldContext);
|
|
253
254
|
|
|
254
255
|
const ref = useRef<HTMLButtonElement | null>(null);
|
|
@@ -286,7 +287,21 @@ const InputFieldButton = memo(
|
|
|
286
287
|
);
|
|
287
288
|
|
|
288
289
|
return (
|
|
289
|
-
<
|
|
290
|
+
<span
|
|
291
|
+
className={cx(
|
|
292
|
+
"absolute",
|
|
293
|
+
size === "large"
|
|
294
|
+
? "right-[9px] top-2.5"
|
|
295
|
+
: size === "medium"
|
|
296
|
+
? "right-1 top-1"
|
|
297
|
+
: size === "small"
|
|
298
|
+
? "right-0.5 top-0.5"
|
|
299
|
+
: "",
|
|
300
|
+
"z-label",
|
|
301
|
+
variant === "floating" && "right-0",
|
|
302
|
+
className
|
|
303
|
+
)}
|
|
304
|
+
>
|
|
290
305
|
<Button
|
|
291
306
|
ref={(element) => {
|
|
292
307
|
ref.current = element;
|
|
@@ -297,11 +312,15 @@ const InputFieldButton = memo(
|
|
|
297
312
|
onPointerDown={handlePointerDown}
|
|
298
313
|
tabIndex={-1}
|
|
299
314
|
size={size === "medium" ? "normal" : size}
|
|
315
|
+
className={cx(
|
|
316
|
+
buttonClassName,
|
|
317
|
+
variant === "floating" && "bg-transparent shadow-none !px-0.5"
|
|
318
|
+
)}
|
|
300
319
|
{...rest}
|
|
301
320
|
>
|
|
302
321
|
{children}
|
|
303
322
|
</Button>
|
|
304
|
-
</
|
|
323
|
+
</span>
|
|
305
324
|
);
|
|
306
325
|
})
|
|
307
326
|
);
|
|
@@ -322,12 +341,8 @@ type InputFieldVariant = "normal" | "bare";
|
|
|
322
341
|
type InputElementProps = {
|
|
323
342
|
disabled?: boolean;
|
|
324
343
|
readOnly?: boolean;
|
|
325
|
-
$labelPosition: LabelPosition;
|
|
326
|
-
$labelSize?: number;
|
|
327
|
-
$hasDropdown: boolean;
|
|
328
344
|
$textAlign?: Property.TextAlign;
|
|
329
345
|
$variant?: InputFieldVariant;
|
|
330
|
-
$size: InputFieldSize;
|
|
331
346
|
children?: React.ReactNode;
|
|
332
347
|
className?: string;
|
|
333
348
|
style?: React.CSSProperties;
|
|
@@ -341,45 +356,30 @@ const InputElement = forwardRef(
|
|
|
341
356
|
{
|
|
342
357
|
disabled,
|
|
343
358
|
readOnly,
|
|
344
|
-
$labelPosition,
|
|
345
|
-
$labelSize,
|
|
346
|
-
$hasDropdown,
|
|
347
359
|
$textAlign,
|
|
348
360
|
$variant = "normal",
|
|
349
|
-
$size,
|
|
350
361
|
className,
|
|
351
362
|
children,
|
|
352
363
|
value,
|
|
353
364
|
onSubmit,
|
|
354
365
|
as = "input",
|
|
366
|
+
style,
|
|
355
367
|
...props
|
|
356
368
|
}: InputElementProps,
|
|
357
369
|
ref: ForwardedRef<any>
|
|
358
370
|
) => {
|
|
359
|
-
const
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
: ($size === "large" ? 10 : $size === "medium" ? 6 : 1) +
|
|
369
|
-
($labelSize && $labelPosition === "end" ? 6 + $labelSize : 0) +
|
|
370
|
-
($hasDropdown ? 11 : 0);
|
|
371
|
-
|
|
372
|
-
const memoizedStyles = useMemo(
|
|
373
|
-
() => ({
|
|
374
|
-
paddingLeft: `${paddingLeft}px`,
|
|
375
|
-
paddingRight: `${paddingRight}px`,
|
|
376
|
-
...props.style,
|
|
377
|
-
}),
|
|
378
|
-
[paddingLeft, paddingRight, props.style]
|
|
379
|
-
);
|
|
371
|
+
const { buttonSize, labelSize, labelPosition, size } =
|
|
372
|
+
useContext(InputFieldContext);
|
|
373
|
+
|
|
374
|
+
const spacingStyles = getInputFieldSpacing({
|
|
375
|
+
labelPosition,
|
|
376
|
+
size,
|
|
377
|
+
buttonSize,
|
|
378
|
+
labelSize,
|
|
379
|
+
});
|
|
380
380
|
|
|
381
381
|
const inputClassName = cx(
|
|
382
|
-
"flex w-0 flex-1 relative border-0 outline-none min-w-0 self-stretch rounded bg-input-background focus:ring-2 focus:ring-primary placeholder:text-text-disabled",
|
|
382
|
+
"flex w-0 flex-1 relative border-0 outline-none min-w-0 self-stretch rounded bg-input-background focus:ring-2 focus:ring-primary placeholder:text-text-disabled transition-all",
|
|
383
383
|
readOnly
|
|
384
384
|
? "text-text-muted"
|
|
385
385
|
: disabled
|
|
@@ -387,11 +387,11 @@ const InputElement = forwardRef(
|
|
|
387
387
|
: "text-text",
|
|
388
388
|
"font-sans font-normal",
|
|
389
389
|
$textAlign && `text-${$textAlign}`,
|
|
390
|
-
|
|
390
|
+
size === "small"
|
|
391
391
|
? "text-[11px] leading-[19px] py-[1px]"
|
|
392
|
-
:
|
|
392
|
+
: size === "large"
|
|
393
393
|
? "py-2.5 text-heading5"
|
|
394
|
-
:
|
|
394
|
+
: size === "medium"
|
|
395
395
|
? "py-1 text-heading5"
|
|
396
396
|
: $variant === "bare" && "py-1 -m-1",
|
|
397
397
|
className
|
|
@@ -403,7 +403,7 @@ const InputElement = forwardRef(
|
|
|
403
403
|
ref={ref}
|
|
404
404
|
{...props}
|
|
405
405
|
className={inputClassName}
|
|
406
|
-
style={
|
|
406
|
+
style={{ ...spacingStyles, ...style }}
|
|
407
407
|
>
|
|
408
408
|
{children}
|
|
409
409
|
</span>
|
|
@@ -417,7 +417,7 @@ const InputElement = forwardRef(
|
|
|
417
417
|
onSubmit={onSubmit ?? (() => {})}
|
|
418
418
|
{...props}
|
|
419
419
|
className={inputClassName}
|
|
420
|
-
style={
|
|
420
|
+
style={{ ...spacingStyles, ...style }}
|
|
421
421
|
/>
|
|
422
422
|
);
|
|
423
423
|
}
|
|
@@ -434,16 +434,7 @@ const InputFieldInput = forwardRef(function InputFieldInput(
|
|
|
434
434
|
{ textAlign, variant, ...rest }: InputFieldInputProps,
|
|
435
435
|
forwardedRef: ForwardedRef<HTMLInputElement>
|
|
436
436
|
) {
|
|
437
|
-
const {
|
|
438
|
-
labelPosition,
|
|
439
|
-
labelSize,
|
|
440
|
-
hasDropdown,
|
|
441
|
-
buttonSize,
|
|
442
|
-
size,
|
|
443
|
-
onFocusChange,
|
|
444
|
-
setInputRef,
|
|
445
|
-
id,
|
|
446
|
-
} = useContext(InputFieldContext);
|
|
437
|
+
const { onFocusChange, setInputRef, id } = useContext(InputFieldContext);
|
|
447
438
|
|
|
448
439
|
const handleFocusChange = useCallback(
|
|
449
440
|
(isFocused: boolean) => {
|
|
@@ -459,10 +450,6 @@ const InputFieldInput = forwardRef(function InputFieldInput(
|
|
|
459
450
|
return (
|
|
460
451
|
<InputElement
|
|
461
452
|
ref={forwardedRef}
|
|
462
|
-
$labelPosition={labelPosition}
|
|
463
|
-
$labelSize={labelSize || buttonSize}
|
|
464
|
-
$hasDropdown={hasDropdown}
|
|
465
|
-
$size={size}
|
|
466
453
|
$variant={variant}
|
|
467
454
|
$textAlign={textAlign}
|
|
468
455
|
onFocusChange={handleFocusChange}
|
|
@@ -472,17 +459,14 @@ const InputFieldInput = forwardRef(function InputFieldInput(
|
|
|
472
459
|
);
|
|
473
460
|
});
|
|
474
461
|
|
|
475
|
-
const InputFieldTypeahead = (props: {
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
462
|
+
const InputFieldTypeahead = (props: {
|
|
463
|
+
prefix: string;
|
|
464
|
+
value: string;
|
|
465
|
+
isFocused: boolean;
|
|
466
|
+
}) => {
|
|
479
467
|
return (
|
|
480
468
|
<InputElement
|
|
481
469
|
as="span"
|
|
482
|
-
$labelPosition={labelPosition}
|
|
483
|
-
$labelSize={labelSize}
|
|
484
|
-
$hasDropdown={hasDropdown}
|
|
485
|
-
$size={size}
|
|
486
470
|
style={useMemo(
|
|
487
471
|
() => ({
|
|
488
472
|
position: "absolute",
|
|
@@ -498,9 +482,11 @@ const InputFieldTypeahead = (props: { prefix: string; value: string }) => {
|
|
|
498
482
|
)}
|
|
499
483
|
>
|
|
500
484
|
<span style={{ opacity: 0 }}>{props.prefix}</span>
|
|
501
|
-
|
|
502
|
-
{
|
|
503
|
-
|
|
485
|
+
{props.isFocused && (
|
|
486
|
+
<span style={{ opacity: 0.5 }}>
|
|
487
|
+
{props.value.slice(props.prefix.length)}
|
|
488
|
+
</span>
|
|
489
|
+
)}
|
|
504
490
|
</InputElement>
|
|
505
491
|
);
|
|
506
492
|
};
|
|
@@ -511,13 +497,14 @@ const InputFieldTypeahead = (props: { prefix: string; value: string }) => {
|
|
|
511
497
|
|
|
512
498
|
type InputFieldNumberInputProps = Omit<
|
|
513
499
|
TextInputProps,
|
|
514
|
-
"value" | "onChange" | "onKeyDown" | "onSubmit"
|
|
500
|
+
"value" | "onChange" | "onKeyDown" | "onSubmit" | "readOnly"
|
|
515
501
|
> & {
|
|
516
502
|
value: number | undefined;
|
|
517
503
|
onNudge?: (value: number) => void;
|
|
518
504
|
variant?: "bare";
|
|
519
505
|
onFocus?: FocusEventHandler;
|
|
520
506
|
onBlur?: FocusEventHandler;
|
|
507
|
+
readOnly?: boolean;
|
|
521
508
|
} & (
|
|
522
509
|
| {
|
|
523
510
|
onChange: (value: number) => void;
|
|
@@ -532,7 +519,7 @@ function parseNumber(value: string) {
|
|
|
532
519
|
}
|
|
533
520
|
|
|
534
521
|
function InputFieldNumberInput(props: InputFieldNumberInputProps) {
|
|
535
|
-
const { value, placeholder, onNudge, onBlur, ...rest } = props;
|
|
522
|
+
const { value, placeholder, onNudge, onBlur, readOnly, ...rest } = props;
|
|
536
523
|
const onSubmit = "onSubmit" in props ? props.onSubmit : undefined;
|
|
537
524
|
const onChange = "onChange" in props ? props.onChange : undefined;
|
|
538
525
|
|
|
@@ -609,11 +596,12 @@ function InputFieldNumberInput(props: InputFieldNumberInputProps) {
|
|
|
609
596
|
: String(value)
|
|
610
597
|
}
|
|
611
598
|
placeholder={placeholder}
|
|
612
|
-
onKeyDown={handleKeyDown}
|
|
599
|
+
onKeyDown={!readOnly ? handleKeyDown : undefined}
|
|
613
600
|
onBlur={handleBlur}
|
|
601
|
+
{...(readOnly ? { readOnly: true } : {})}
|
|
614
602
|
{...("onChange" in props
|
|
615
|
-
? { onChange: handleChange }
|
|
616
|
-
: { onSubmit: handleSubmit })}
|
|
603
|
+
? { onChange: readOnly ? noop : handleChange }
|
|
604
|
+
: { onSubmit: readOnly ? noop : handleSubmit })}
|
|
617
605
|
/>
|
|
618
606
|
);
|
|
619
607
|
}
|
|
@@ -644,16 +632,19 @@ const RootContainer = forwardRef<
|
|
|
644
632
|
|
|
645
633
|
interface InputFieldRootProps {
|
|
646
634
|
id?: string;
|
|
647
|
-
flex?: string;
|
|
648
635
|
children?: ReactNode;
|
|
649
636
|
width?: number;
|
|
650
637
|
/** @default end */
|
|
651
638
|
labelPosition?: LabelPosition;
|
|
652
639
|
labelSize?: number;
|
|
653
|
-
hasDropdown?: boolean;
|
|
654
640
|
/** @default medium */
|
|
655
641
|
size?: InputFieldSize;
|
|
656
642
|
renderPopoverContent?: (options: { width: number }) => ReactNode;
|
|
643
|
+
/**
|
|
644
|
+
* if there is a popover, this is the offset from the trigger
|
|
645
|
+
* @default 3
|
|
646
|
+
*/
|
|
647
|
+
sideOffset?: number;
|
|
657
648
|
onFocusChange?: (isFocused: boolean) => void;
|
|
658
649
|
style?: React.CSSProperties;
|
|
659
650
|
className?: string;
|
|
@@ -661,10 +652,10 @@ interface InputFieldRootProps {
|
|
|
661
652
|
|
|
662
653
|
function InputFieldRoot({
|
|
663
654
|
id,
|
|
664
|
-
flex,
|
|
665
655
|
children,
|
|
666
656
|
width,
|
|
667
657
|
labelPosition = "end",
|
|
658
|
+
sideOffset = 3,
|
|
668
659
|
labelSize,
|
|
669
660
|
size = "medium",
|
|
670
661
|
renderPopoverContent,
|
|
@@ -672,13 +663,8 @@ function InputFieldRoot({
|
|
|
672
663
|
style,
|
|
673
664
|
className,
|
|
674
665
|
}: InputFieldRootProps) {
|
|
675
|
-
const childrenArray = Children.toArray(children);
|
|
676
666
|
const randomId = useId();
|
|
677
667
|
|
|
678
|
-
const hasDropdown = childrenArray.some(
|
|
679
|
-
(child) => isValidElement(child) && child.type === InputFieldDropdownMenu
|
|
680
|
-
);
|
|
681
|
-
|
|
682
668
|
const [isFocused, setIsFocused] = React.useState(false);
|
|
683
669
|
|
|
684
670
|
const [measuredLabelSize, setMeasuredLabelSize] = React.useState<number>();
|
|
@@ -710,7 +696,6 @@ function InputFieldRoot({
|
|
|
710
696
|
labelPosition,
|
|
711
697
|
labelSize: measuredLabelSize ?? labelSize,
|
|
712
698
|
buttonSize: measuredButtonSize,
|
|
713
|
-
hasDropdown,
|
|
714
699
|
size,
|
|
715
700
|
isFocused,
|
|
716
701
|
onFocusChange: handleFocusChange,
|
|
@@ -725,7 +710,6 @@ function InputFieldRoot({
|
|
|
725
710
|
measuredLabelSize,
|
|
726
711
|
labelSize,
|
|
727
712
|
measuredButtonSize,
|
|
728
|
-
hasDropdown,
|
|
729
713
|
size,
|
|
730
714
|
isFocused,
|
|
731
715
|
handleFocusChange,
|
|
@@ -742,7 +726,7 @@ function InputFieldRoot({
|
|
|
742
726
|
);
|
|
743
727
|
|
|
744
728
|
const measuredWidthObject = useMemo(
|
|
745
|
-
() => (measuredWidth ? { width: measuredWidth +
|
|
729
|
+
() => (measuredWidth ? { width: measuredWidth + 4 } : undefined),
|
|
746
730
|
[measuredWidth]
|
|
747
731
|
);
|
|
748
732
|
|
|
@@ -752,7 +736,7 @@ function InputFieldRoot({
|
|
|
752
736
|
<Popover
|
|
753
737
|
open={true}
|
|
754
738
|
trigger={rootElement}
|
|
755
|
-
sideOffset={
|
|
739
|
+
sideOffset={sideOffset}
|
|
756
740
|
showArrow={false}
|
|
757
741
|
onOpenAutoFocus={(event) => {
|
|
758
742
|
event.preventDefault();
|
|
@@ -795,19 +779,20 @@ const PrimitiveInputField = ({
|
|
|
795
779
|
const { labelSize, labelPosition, buttonSize } =
|
|
796
780
|
useContext(InputFieldContext);
|
|
797
781
|
|
|
782
|
+
const spacingStyles = getInputFieldSpacing({
|
|
783
|
+
labelPosition,
|
|
784
|
+
buttonSize,
|
|
785
|
+
labelSize,
|
|
786
|
+
});
|
|
798
787
|
const memoizedStyles = useMemo(
|
|
799
|
-
() => ({
|
|
800
|
-
|
|
801
|
-
paddingRight: `${labelPosition === "end" ? 6 + (labelSize ?? 0) + (buttonSize ?? 0) : 6}px`,
|
|
802
|
-
...style,
|
|
803
|
-
}),
|
|
804
|
-
[labelSize, labelPosition, buttonSize, style]
|
|
788
|
+
() => ({ ...spacingStyles, ...style }),
|
|
789
|
+
[spacingStyles, style]
|
|
805
790
|
);
|
|
806
791
|
return (
|
|
807
792
|
<input
|
|
808
793
|
style={memoizedStyles}
|
|
809
794
|
className={cx(
|
|
810
|
-
"flex w-0 flex-1 relative border-none outline-none min-w-0 self-stretch rounded py-1 bg-input-background select-all pointer-events-[all] focus:ring-2 focus:ring-primary font-sans font-normal text-heading5 placeholder:text-text-disabled focus:z-interactable",
|
|
795
|
+
"flex w-0 flex-1 relative border-none outline-none min-w-0 self-stretch rounded py-1 bg-input-background select-all pointer-events-[all] focus:ring-2 focus:ring-primary font-sans font-normal text-heading5 placeholder:text-text-disabled focus:z-interactable transition-all",
|
|
811
796
|
readOnly
|
|
812
797
|
? "text-text-muted"
|
|
813
798
|
: disabled
|
package/src/components/Label.tsx
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as React from "react";
|
|
2
2
|
import { cx } from "../utils/classNames";
|
|
3
3
|
|
|
4
|
-
interface LabelProps extends LabelHTMLAttributes<HTMLLabelElement> {}
|
|
4
|
+
interface LabelProps extends React.LabelHTMLAttributes<HTMLLabelElement> {}
|
|
5
5
|
|
|
6
|
-
export const Label = memo(
|
|
7
|
-
forwardRef<HTMLLabelElement, LabelProps>(function Label(
|
|
6
|
+
export const Label = React.memo(
|
|
7
|
+
React.forwardRef<HTMLLabelElement, LabelProps>(function Label(
|
|
8
8
|
{ className, ...props },
|
|
9
9
|
ref
|
|
10
10
|
) {
|