@noya-app/noya-designsystem 0.0.14 → 0.0.16
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/.eslintrc.js +11 -0
- package/.turbo/turbo-build.log +8 -8
- package/.turbo/turbo-lint.log +3 -0
- package/CHANGELOG.md +24 -0
- package/dist/index.d.mts +133 -111
- package/dist/index.d.ts +133 -111
- package/dist/index.js +7832 -646
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +7819 -601
- package/dist/index.mjs.map +1 -1
- package/package.json +8 -7
- package/src/components/Chip.tsx +101 -82
- package/src/components/Dialog.tsx +51 -26
- package/src/components/DraggableMenuButton.tsx +116 -0
- package/src/components/InputField.tsx +196 -132
- package/src/components/InputFieldWithCompletions.tsx +43 -6
- package/src/components/InspectorPrimitives.tsx +1 -1
- package/src/components/ListView.tsx +77 -38
- package/src/components/Select.tsx +2 -0
- package/src/components/Sortable.tsx +45 -36
- package/src/components/Stack.tsx +63 -62
- package/src/components/Switch.tsx +25 -25
- package/src/components/Text.tsx +3 -0
- package/src/contexts/DialogContext.tsx +3 -0
- package/src/index.tsx +3 -2
- package/src/theme/light.ts +1 -1
- package/src/utils/getGradientBackground.tsx +8 -10
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { CaretDownIcon } from
|
|
2
|
-
import { memoize } from
|
|
3
|
-
import {
|
|
1
|
+
import { CaretDownIcon } from "@noya-app/noya-icons";
|
|
2
|
+
import { memoize } from "@noya-app/noya-utils";
|
|
3
|
+
import { assignRef } from "@noya-app/react-utils";
|
|
4
|
+
import { Property } from "csstype";
|
|
4
5
|
import React, {
|
|
5
6
|
Children,
|
|
6
7
|
createContext,
|
|
@@ -14,19 +15,20 @@ import React, {
|
|
|
14
15
|
useEffect,
|
|
15
16
|
useLayoutEffect,
|
|
16
17
|
useMemo,
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
import
|
|
20
|
-
import
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
import
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
18
|
+
useRef,
|
|
19
|
+
} from "react";
|
|
20
|
+
import styled from "styled-components";
|
|
21
|
+
import handleNudge from "../utils/handleNudge";
|
|
22
|
+
import { Button } from "./Button";
|
|
23
|
+
import { DropdownMenu } from "./DropdownMenu";
|
|
24
|
+
import { MenuItem } from "./internal/Menu";
|
|
25
|
+
import TextInput, { TextInputProps } from "./internal/TextInput";
|
|
26
|
+
import { Popover } from "./Popover";
|
|
27
|
+
import { Stack } from "./Stack";
|
|
26
28
|
|
|
27
|
-
type LabelPosition =
|
|
29
|
+
type LabelPosition = "start" | "end";
|
|
28
30
|
|
|
29
|
-
export type InputFieldSize =
|
|
31
|
+
export type InputFieldSize = "small" | "medium" | "large";
|
|
30
32
|
|
|
31
33
|
type InputFieldContextValue = {
|
|
32
34
|
labelPosition: LabelPosition;
|
|
@@ -38,14 +40,15 @@ type InputFieldContextValue = {
|
|
|
38
40
|
onFocusChange: (isFocused: boolean) => void;
|
|
39
41
|
inputRef?: ForwardedRef<HTMLInputElement>;
|
|
40
42
|
setInputRef?: (ref: ForwardedRef<HTMLInputElement>) => void;
|
|
43
|
+
setLabelWidth?: (width: number) => void;
|
|
41
44
|
};
|
|
42
45
|
|
|
43
46
|
const InputFieldContext = createContext<InputFieldContextValue>({
|
|
44
|
-
labelPosition:
|
|
47
|
+
labelPosition: "end",
|
|
45
48
|
labelSize: 6,
|
|
46
49
|
hasLabel: false,
|
|
47
50
|
hasDropdown: false,
|
|
48
|
-
size:
|
|
51
|
+
size: "medium",
|
|
49
52
|
isFocused: false,
|
|
50
53
|
onFocusChange: () => {},
|
|
51
54
|
});
|
|
@@ -60,58 +63,79 @@ const LabelContainer = styled.label<{
|
|
|
60
63
|
hasDropdown: boolean;
|
|
61
64
|
}>(({ theme, labelPosition, hasDropdown, pointerEvents }) => ({
|
|
62
65
|
color: theme.colors.textDisabled,
|
|
63
|
-
position:
|
|
66
|
+
position: "absolute",
|
|
64
67
|
top: 0,
|
|
65
|
-
right: labelPosition ===
|
|
68
|
+
right: labelPosition === "end" ? 0 : undefined,
|
|
66
69
|
bottom: 0,
|
|
67
|
-
left: labelPosition ===
|
|
68
|
-
display:
|
|
69
|
-
alignItems:
|
|
70
|
+
left: labelPosition === "start" ? 0 : undefined,
|
|
71
|
+
display: "flex",
|
|
72
|
+
alignItems: "center",
|
|
70
73
|
pointerEvents,
|
|
71
|
-
fontWeight:
|
|
72
|
-
fontSize:
|
|
73
|
-
userSelect:
|
|
74
|
-
...(labelPosition ===
|
|
75
|
-
? { justifyContent:
|
|
74
|
+
fontWeight: "bold",
|
|
75
|
+
fontSize: "60%",
|
|
76
|
+
userSelect: "none",
|
|
77
|
+
...(labelPosition === "start"
|
|
78
|
+
? { justifyContent: "flex-start", paddingLeft: "6px" }
|
|
76
79
|
: {
|
|
77
|
-
justifyContent:
|
|
78
|
-
paddingRight: hasDropdown ?
|
|
80
|
+
justifyContent: "flex-end",
|
|
81
|
+
paddingRight: hasDropdown ? "16px" : "6px",
|
|
79
82
|
}),
|
|
80
83
|
}));
|
|
81
84
|
|
|
82
85
|
interface InputFieldLabelProps {
|
|
83
86
|
children?: ReactNode;
|
|
84
87
|
pointerEvents?: Property.PointerEvents;
|
|
88
|
+
style?: React.CSSProperties;
|
|
85
89
|
}
|
|
86
90
|
|
|
87
|
-
const InputFieldLabel = memo(
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
91
|
+
const InputFieldLabel = memo(
|
|
92
|
+
forwardRef(function InputFieldLabel(
|
|
93
|
+
{ children = false, pointerEvents = "none", style }: InputFieldLabelProps,
|
|
94
|
+
forwardedRef: ForwardedRef<HTMLLabelElement>
|
|
95
|
+
) {
|
|
96
|
+
const { labelPosition, hasDropdown, setLabelWidth } =
|
|
97
|
+
useContext(InputFieldContext);
|
|
92
98
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
99
|
+
const ref = useRef<HTMLLabelElement | null>(null);
|
|
100
|
+
|
|
101
|
+
useLayoutEffect(() => {
|
|
102
|
+
if (!setLabelWidth) return;
|
|
103
|
+
|
|
104
|
+
const width = ref.current?.getBoundingClientRect().width;
|
|
105
|
+
|
|
106
|
+
if (!width) return;
|
|
107
|
+
|
|
108
|
+
setLabelWidth(width);
|
|
109
|
+
}, [setLabelWidth]);
|
|
110
|
+
|
|
111
|
+
return (
|
|
112
|
+
<LabelContainer
|
|
113
|
+
ref={(element) => {
|
|
114
|
+
ref.current = element;
|
|
115
|
+
assignRef(forwardedRef, element);
|
|
116
|
+
}}
|
|
117
|
+
pointerEvents={pointerEvents}
|
|
118
|
+
labelPosition={labelPosition}
|
|
119
|
+
hasDropdown={hasDropdown}
|
|
120
|
+
style={style}
|
|
121
|
+
>
|
|
122
|
+
{children}
|
|
123
|
+
</LabelContainer>
|
|
124
|
+
);
|
|
125
|
+
})
|
|
126
|
+
);
|
|
103
127
|
|
|
104
128
|
/* ----------------------------------------------------------------------------
|
|
105
129
|
* Dropdown
|
|
106
130
|
* ------------------------------------------------------------------------- */
|
|
107
131
|
|
|
108
132
|
const DropdownContainer = styled.span(({ theme }) => ({
|
|
109
|
-
position:
|
|
133
|
+
position: "absolute",
|
|
110
134
|
top: 0,
|
|
111
135
|
right: 0,
|
|
112
136
|
bottom: 0,
|
|
113
|
-
display:
|
|
114
|
-
flexDirection:
|
|
137
|
+
display: "flex",
|
|
138
|
+
flexDirection: "column",
|
|
115
139
|
}));
|
|
116
140
|
|
|
117
141
|
interface InputFieldDropdownProps<T extends string> {
|
|
@@ -128,10 +152,10 @@ const InputFieldDropdownMenu = memo(function InputFieldDropdownMenu<
|
|
|
128
152
|
|
|
129
153
|
const contentStyle = useMemo(
|
|
130
154
|
() => ({
|
|
131
|
-
margin:
|
|
132
|
-
minHeight: size ===
|
|
155
|
+
margin: "-4px 0",
|
|
156
|
+
minHeight: size === "small" ? "15px" : undefined,
|
|
133
157
|
}),
|
|
134
|
-
[size]
|
|
158
|
+
[size]
|
|
135
159
|
);
|
|
136
160
|
|
|
137
161
|
return (
|
|
@@ -153,10 +177,10 @@ const InputFieldDropdownMenu = memo(function InputFieldDropdownMenu<
|
|
|
153
177
|
|
|
154
178
|
const ButtonContainer = styled.span<{ size: InputFieldSize }>(
|
|
155
179
|
({ theme, size }) => ({
|
|
156
|
-
position:
|
|
157
|
-
right: size ===
|
|
158
|
-
top: size ===
|
|
159
|
-
})
|
|
180
|
+
position: "absolute",
|
|
181
|
+
right: size === "large" ? "9px" : size === "medium" ? "4px" : "2px",
|
|
182
|
+
top: size === "large" ? "8px" : size === "medium" ? "4px" : "2px",
|
|
183
|
+
})
|
|
160
184
|
);
|
|
161
185
|
|
|
162
186
|
const InputFieldButton = memo(function InputFieldButton({
|
|
@@ -170,7 +194,7 @@ const InputFieldButton = memo(function InputFieldButton({
|
|
|
170
194
|
|
|
171
195
|
const defaultHandleClick = useCallback(
|
|
172
196
|
(event: React.MouseEvent) => {
|
|
173
|
-
if (inputRef && typeof inputRef !==
|
|
197
|
+
if (inputRef && typeof inputRef !== "function") {
|
|
174
198
|
inputRef.current?.focus();
|
|
175
199
|
// Select all text
|
|
176
200
|
inputRef.current?.setSelectionRange(0, inputRef.current.value.length);
|
|
@@ -179,7 +203,7 @@ const InputFieldButton = memo(function InputFieldButton({
|
|
|
179
203
|
event.preventDefault();
|
|
180
204
|
event.stopPropagation();
|
|
181
205
|
},
|
|
182
|
-
[inputRef]
|
|
206
|
+
[inputRef]
|
|
183
207
|
);
|
|
184
208
|
|
|
185
209
|
const handlePointerDown = useCallback((event: React.PointerEvent) => {
|
|
@@ -209,19 +233,19 @@ const createCrossSVGString = memoize(
|
|
|
209
233
|
(color: string) =>
|
|
210
234
|
`<svg width='15' height='15' viewBox='0 0 15 15' fill='${color}' xmlns='http://www.w3.org/2000/svg'>
|
|
211
235
|
<path d='M0.877075 7.49988C0.877075 3.84219 3.84222 0.877045 7.49991 0.877045C11.1576 0.877045 14.1227 3.84219 14.1227 7.49988C14.1227 11.1575 11.1576 14.1227 7.49991 14.1227C3.84222 14.1227 0.877075 11.1575 0.877075 7.49988ZM7.49991 1.82704C4.36689 1.82704 1.82708 4.36686 1.82708 7.49988C1.82708 10.6329 4.36689 13.1727 7.49991 13.1727C10.6329 13.1727 13.1727 10.6329 13.1727 7.49988C13.1727 4.36686 10.6329 1.82704 7.49991 1.82704ZM9.85358 5.14644C10.0488 5.3417 10.0488 5.65829 9.85358 5.85355L8.20713 7.49999L9.85358 9.14644C10.0488 9.3417 10.0488 9.65829 9.85358 9.85355C9.65832 10.0488 9.34173 10.0488 9.14647 9.85355L7.50002 8.2071L5.85358 9.85355C5.65832 10.0488 5.34173 10.0488 5.14647 9.85355C4.95121 9.65829 4.95121 9.3417 5.14647 9.14644L6.79292 7.49999L5.14647 5.85355C4.95121 5.65829 4.95121 5.3417 5.14647 5.14644C5.34173 4.95118 5.65832 4.95118 5.85358 5.14644L7.50002 6.79289L9.14647 5.14644C9.34173 4.95118 9.65832 4.95118 9.85358 5.14644Z' fill-rule='evenodd' clip-rule='evenodd'></path>
|
|
212
|
-
</svg
|
|
236
|
+
</svg>`
|
|
213
237
|
);
|
|
214
238
|
|
|
215
|
-
type InputFieldVariant =
|
|
239
|
+
type InputFieldVariant = "normal" | "bare";
|
|
216
240
|
|
|
217
241
|
const ignoredProps = new Set([
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
242
|
+
"labelPosition",
|
|
243
|
+
"labelSize",
|
|
244
|
+
"hasLabel",
|
|
245
|
+
"hasDropdown",
|
|
246
|
+
"textAlign",
|
|
247
|
+
"variant",
|
|
248
|
+
"onNudge",
|
|
225
249
|
]);
|
|
226
250
|
|
|
227
251
|
export const InputElement = styled(TextInput).withConfig({
|
|
@@ -246,76 +270,76 @@ export const InputElement = styled(TextInput).withConfig({
|
|
|
246
270
|
disabled,
|
|
247
271
|
hasLabel,
|
|
248
272
|
readOnly,
|
|
249
|
-
variant =
|
|
273
|
+
variant = "normal",
|
|
250
274
|
size,
|
|
251
275
|
}) => ({
|
|
252
276
|
// placeholder
|
|
253
|
-
|
|
277
|
+
"&::placeholder": {
|
|
254
278
|
color: theme.colors.textDisabled,
|
|
255
279
|
},
|
|
256
280
|
...theme.textStyles.small,
|
|
257
281
|
color: readOnly
|
|
258
282
|
? theme.colors.textMuted
|
|
259
283
|
: disabled
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
...(size ===
|
|
263
|
-
fontSize:
|
|
284
|
+
? theme.colors.textDisabled
|
|
285
|
+
: theme.colors.text,
|
|
286
|
+
...(size === "small" && {
|
|
287
|
+
fontSize: "11px",
|
|
264
288
|
}),
|
|
265
|
-
width:
|
|
266
|
-
flex:
|
|
267
|
-
position:
|
|
268
|
-
border:
|
|
269
|
-
outline:
|
|
270
|
-
minWidth:
|
|
271
|
-
textAlign: textAlign ??
|
|
272
|
-
alignSelf:
|
|
273
|
-
borderRadius:
|
|
274
|
-
paddingTop: size ===
|
|
289
|
+
width: "0px", // Reset intrinsic width
|
|
290
|
+
flex: "1 1 0px",
|
|
291
|
+
position: "relative",
|
|
292
|
+
border: "0",
|
|
293
|
+
outline: "none",
|
|
294
|
+
minWidth: "0",
|
|
295
|
+
textAlign: textAlign ?? "left",
|
|
296
|
+
alignSelf: "stretch",
|
|
297
|
+
borderRadius: "4px",
|
|
298
|
+
paddingTop: size === "large" ? "10px" : size === "medium" ? "4px" : "1px",
|
|
275
299
|
paddingBottom:
|
|
276
|
-
size ===
|
|
300
|
+
size === "large" ? "10px" : size === "medium" ? "4px" : "1px",
|
|
277
301
|
paddingLeft:
|
|
278
|
-
(size ===
|
|
279
|
-
(hasLabel && labelPosition ===
|
|
280
|
-
|
|
302
|
+
(size === "large" ? 10 : size === "medium" ? 6 : 4) +
|
|
303
|
+
(hasLabel && labelPosition === "start" ? 6 + labelSize : 0) +
|
|
304
|
+
"px",
|
|
281
305
|
paddingRight:
|
|
282
|
-
(size ===
|
|
283
|
-
(hasLabel && labelPosition ===
|
|
306
|
+
(size === "large" ? 10 : size === "medium" ? 6 : 4) +
|
|
307
|
+
(hasLabel && labelPosition === "end" ? 6 + labelSize : 0) +
|
|
284
308
|
(hasDropdown ? 11 : 0) +
|
|
285
|
-
|
|
309
|
+
"px",
|
|
286
310
|
background: theme.colors.inputBackground,
|
|
287
|
-
|
|
311
|
+
"&:focus": {
|
|
288
312
|
boxShadow: `0 0 0 2px ${theme.colors.primary}`,
|
|
289
313
|
},
|
|
290
|
-
...(variant ===
|
|
291
|
-
paddingTop:
|
|
292
|
-
paddingRight:
|
|
293
|
-
paddingBottom:
|
|
294
|
-
paddingLeft:
|
|
295
|
-
marginTop:
|
|
296
|
-
marginRight:
|
|
297
|
-
marginBottom:
|
|
298
|
-
marginLeft:
|
|
314
|
+
...(variant === "bare" && {
|
|
315
|
+
paddingTop: "4px",
|
|
316
|
+
paddingRight: "4px",
|
|
317
|
+
paddingBottom: "4px",
|
|
318
|
+
paddingLeft: "4px",
|
|
319
|
+
marginTop: "-4px",
|
|
320
|
+
marginRight: "-4px",
|
|
321
|
+
marginBottom: "-4px",
|
|
322
|
+
marginLeft: "-4px",
|
|
299
323
|
}),
|
|
300
324
|
|
|
301
325
|
'&[type="search"]::-webkit-search-cancel-button': {
|
|
302
|
-
appearance:
|
|
303
|
-
height:
|
|
304
|
-
width:
|
|
326
|
+
appearance: "none",
|
|
327
|
+
height: "15px",
|
|
328
|
+
width: "15px",
|
|
305
329
|
background: `url("data:image/svg+xml;utf8,${createCrossSVGString(
|
|
306
|
-
theme.colors.icon
|
|
330
|
+
theme.colors.icon
|
|
307
331
|
)}") no-repeat`,
|
|
308
332
|
},
|
|
309
|
-
})
|
|
333
|
+
})
|
|
310
334
|
);
|
|
311
335
|
|
|
312
336
|
const InputFieldInput = forwardRef(function InputFieldInput(
|
|
313
337
|
// onFocusChange should only be passed from the root, never directly
|
|
314
338
|
props: TextInputProps & {
|
|
315
339
|
textAlign?: Property.TextAlign;
|
|
316
|
-
variant?:
|
|
340
|
+
variant?: "bare";
|
|
317
341
|
},
|
|
318
|
-
forwardedRef: ForwardedRef<HTMLInputElement
|
|
342
|
+
forwardedRef: ForwardedRef<HTMLInputElement>
|
|
319
343
|
) {
|
|
320
344
|
const {
|
|
321
345
|
labelPosition,
|
|
@@ -331,7 +355,7 @@ const InputFieldInput = forwardRef(function InputFieldInput(
|
|
|
331
355
|
(isFocused: boolean) => {
|
|
332
356
|
onFocusChange(isFocused);
|
|
333
357
|
},
|
|
334
|
-
[onFocusChange]
|
|
358
|
+
[onFocusChange]
|
|
335
359
|
);
|
|
336
360
|
|
|
337
361
|
useLayoutEffect(() => {
|
|
@@ -352,17 +376,54 @@ const InputFieldInput = forwardRef(function InputFieldInput(
|
|
|
352
376
|
);
|
|
353
377
|
});
|
|
354
378
|
|
|
379
|
+
const InputFieldTypeahead = (props: { value: string }) => {
|
|
380
|
+
const {
|
|
381
|
+
labelPosition,
|
|
382
|
+
labelSize,
|
|
383
|
+
hasDropdown,
|
|
384
|
+
hasLabel,
|
|
385
|
+
size,
|
|
386
|
+
onFocusChange,
|
|
387
|
+
} = useContext(InputFieldContext);
|
|
388
|
+
|
|
389
|
+
return (
|
|
390
|
+
<InputElement
|
|
391
|
+
labelPosition={labelPosition}
|
|
392
|
+
labelSize={labelSize}
|
|
393
|
+
hasLabel={hasLabel}
|
|
394
|
+
hasDropdown={hasDropdown}
|
|
395
|
+
size={size}
|
|
396
|
+
onFocusChange={onFocusChange}
|
|
397
|
+
readOnly
|
|
398
|
+
placeholder={props.value}
|
|
399
|
+
value=""
|
|
400
|
+
style={{
|
|
401
|
+
position: "absolute",
|
|
402
|
+
top: -1,
|
|
403
|
+
left: 0,
|
|
404
|
+
right: 0,
|
|
405
|
+
bottom: -1,
|
|
406
|
+
width: "100%",
|
|
407
|
+
height: "100%",
|
|
408
|
+
pointerEvents: "none",
|
|
409
|
+
background: "transparent",
|
|
410
|
+
boxShadow: "none",
|
|
411
|
+
}}
|
|
412
|
+
/>
|
|
413
|
+
);
|
|
414
|
+
};
|
|
415
|
+
|
|
355
416
|
/* ----------------------------------------------------------------------------
|
|
356
417
|
* NumberInput
|
|
357
418
|
* ------------------------------------------------------------------------- */
|
|
358
419
|
|
|
359
420
|
type InputFieldNumberInputProps = Omit<
|
|
360
421
|
TextInputProps,
|
|
361
|
-
|
|
422
|
+
"value" | "onChange" | "onKeyDown" | "onSubmit"
|
|
362
423
|
> & {
|
|
363
424
|
value: number | undefined;
|
|
364
425
|
onNudge?: (value: number) => void;
|
|
365
|
-
variant?:
|
|
426
|
+
variant?: "bare";
|
|
366
427
|
} & (
|
|
367
428
|
| {
|
|
368
429
|
onChange: (value: number) => void;
|
|
@@ -378,8 +439,8 @@ function parseNumber(value: string) {
|
|
|
378
439
|
|
|
379
440
|
function InputFieldNumberInput(props: InputFieldNumberInputProps) {
|
|
380
441
|
const { value, placeholder, onNudge } = props;
|
|
381
|
-
const onSubmit =
|
|
382
|
-
const onChange =
|
|
442
|
+
const onSubmit = "onSubmit" in props ? props.onSubmit : undefined;
|
|
443
|
+
const onChange = "onChange" in props ? props.onChange : undefined;
|
|
383
444
|
|
|
384
445
|
const handleSubmit = useCallback(
|
|
385
446
|
(value: string) => {
|
|
@@ -389,7 +450,7 @@ function InputFieldNumberInput(props: InputFieldNumberInputProps) {
|
|
|
389
450
|
onSubmit?.(newValue);
|
|
390
451
|
}
|
|
391
452
|
},
|
|
392
|
-
[onSubmit]
|
|
453
|
+
[onSubmit]
|
|
393
454
|
);
|
|
394
455
|
|
|
395
456
|
const handleKeyDown = useCallback(
|
|
@@ -403,23 +464,23 @@ function InputFieldNumberInput(props: InputFieldNumberInputProps) {
|
|
|
403
464
|
event.preventDefault();
|
|
404
465
|
event.stopPropagation();
|
|
405
466
|
},
|
|
406
|
-
[onNudge]
|
|
467
|
+
[onNudge]
|
|
407
468
|
);
|
|
408
469
|
|
|
409
470
|
const handleChange = useCallback(
|
|
410
471
|
(value: string) => {
|
|
411
472
|
onChange?.(parseNumber(value));
|
|
412
473
|
},
|
|
413
|
-
[onChange]
|
|
474
|
+
[onChange]
|
|
414
475
|
);
|
|
415
476
|
|
|
416
477
|
return (
|
|
417
478
|
<InputFieldInput
|
|
418
479
|
{...props}
|
|
419
|
-
value={value === undefined ?
|
|
480
|
+
value={value === undefined ? "" : String(value)}
|
|
420
481
|
placeholder={placeholder}
|
|
421
482
|
onKeyDown={handleKeyDown}
|
|
422
|
-
{...(
|
|
483
|
+
{...("onChange" in props
|
|
423
484
|
? { onChange: handleChange }
|
|
424
485
|
: { onSubmit: handleSubmit })}
|
|
425
486
|
/>
|
|
@@ -432,12 +493,12 @@ function InputFieldNumberInput(props: InputFieldNumberInputProps) {
|
|
|
432
493
|
|
|
433
494
|
const RootContainer = styled.div<{ width?: number; flex?: string }>(
|
|
434
495
|
({ theme, flex, width }) => ({
|
|
435
|
-
flex: flex ??
|
|
436
|
-
display:
|
|
437
|
-
flexDirection:
|
|
438
|
-
position:
|
|
439
|
-
maxWidth: typeof width ===
|
|
440
|
-
})
|
|
496
|
+
flex: flex ?? "1",
|
|
497
|
+
display: "flex",
|
|
498
|
+
flexDirection: "row",
|
|
499
|
+
position: "relative",
|
|
500
|
+
maxWidth: typeof width === "number" ? `${width}px` : undefined,
|
|
501
|
+
})
|
|
441
502
|
);
|
|
442
503
|
|
|
443
504
|
interface InputFieldRootProps {
|
|
@@ -458,23 +519,24 @@ function InputFieldRoot({
|
|
|
458
519
|
flex,
|
|
459
520
|
children,
|
|
460
521
|
width,
|
|
461
|
-
labelPosition =
|
|
462
|
-
labelSize
|
|
463
|
-
size =
|
|
522
|
+
labelPosition = "end",
|
|
523
|
+
labelSize,
|
|
524
|
+
size = "medium",
|
|
464
525
|
renderPopoverContent,
|
|
465
526
|
onFocusChange,
|
|
466
527
|
}: InputFieldRootProps) {
|
|
467
528
|
const childrenArray = Children.toArray(children);
|
|
468
529
|
|
|
469
530
|
const hasDropdown = childrenArray.some(
|
|
470
|
-
(child) => isValidElement(child) && child.type === InputFieldDropdownMenu
|
|
531
|
+
(child) => isValidElement(child) && child.type === InputFieldDropdownMenu
|
|
471
532
|
);
|
|
472
533
|
const hasLabel = childrenArray.some(
|
|
473
|
-
(child) => isValidElement(child) && child.type === InputFieldLabel
|
|
534
|
+
(child) => isValidElement(child) && child.type === InputFieldLabel
|
|
474
535
|
);
|
|
475
536
|
|
|
476
537
|
const [isFocused, setIsFocused] = React.useState(false);
|
|
477
538
|
|
|
539
|
+
const [measuredLabelSize, setMeasuredLabelSize] = React.useState<number>();
|
|
478
540
|
const [measuredWidth, setMeasuredWidth] = React.useState<number>();
|
|
479
541
|
|
|
480
542
|
const handleFocusChange = useCallback(
|
|
@@ -482,18 +544,17 @@ function InputFieldRoot({
|
|
|
482
544
|
setIsFocused(isFocused);
|
|
483
545
|
onFocusChange?.(isFocused);
|
|
484
546
|
},
|
|
485
|
-
[onFocusChange]
|
|
547
|
+
[onFocusChange]
|
|
486
548
|
);
|
|
487
549
|
|
|
488
550
|
const [inputRef, setInputRef] =
|
|
489
551
|
React.useState<ForwardedRef<HTMLInputElement>>();
|
|
490
552
|
|
|
553
|
+
// eslint-disable-next-line @shopify/prefer-early-return
|
|
491
554
|
useEffect(() => {
|
|
492
|
-
if (inputRef && typeof inputRef !==
|
|
555
|
+
if (inputRef && typeof inputRef !== "function") {
|
|
493
556
|
setMeasuredWidth?.(
|
|
494
|
-
isFocused
|
|
495
|
-
? inputRef?.current?.getBoundingClientRect().width
|
|
496
|
-
: undefined,
|
|
557
|
+
isFocused ? inputRef?.current?.getBoundingClientRect().width : undefined
|
|
497
558
|
);
|
|
498
559
|
}
|
|
499
560
|
}, [inputRef, isFocused]);
|
|
@@ -501,7 +562,7 @@ function InputFieldRoot({
|
|
|
501
562
|
const contextValue = useMemo(
|
|
502
563
|
(): InputFieldContextValue => ({
|
|
503
564
|
labelPosition,
|
|
504
|
-
labelSize,
|
|
565
|
+
labelSize: measuredLabelSize ?? labelSize ?? 6,
|
|
505
566
|
hasDropdown,
|
|
506
567
|
hasLabel,
|
|
507
568
|
size,
|
|
@@ -509,9 +570,11 @@ function InputFieldRoot({
|
|
|
509
570
|
onFocusChange: handleFocusChange,
|
|
510
571
|
inputRef,
|
|
511
572
|
setInputRef,
|
|
573
|
+
setLabelWidth: setMeasuredLabelSize,
|
|
512
574
|
}),
|
|
513
575
|
[
|
|
514
576
|
labelPosition,
|
|
577
|
+
measuredLabelSize,
|
|
515
578
|
labelSize,
|
|
516
579
|
hasDropdown,
|
|
517
580
|
hasLabel,
|
|
@@ -519,7 +582,7 @@ function InputFieldRoot({
|
|
|
519
582
|
isFocused,
|
|
520
583
|
handleFocusChange,
|
|
521
584
|
inputRef,
|
|
522
|
-
]
|
|
585
|
+
]
|
|
523
586
|
);
|
|
524
587
|
|
|
525
588
|
const rootElement = (
|
|
@@ -561,6 +624,7 @@ function InputFieldRoot({
|
|
|
561
624
|
export namespace InputField {
|
|
562
625
|
export const Root = memo(InputFieldRoot);
|
|
563
626
|
export const Input = memo(InputFieldInput);
|
|
627
|
+
export const Typeahead = memo(InputFieldTypeahead);
|
|
564
628
|
export const NumberInput = memo(InputFieldNumberInput);
|
|
565
629
|
export const DropdownMenu = InputFieldDropdownMenu;
|
|
566
630
|
export const Button = InputFieldButton;
|