@noya-app/noya-designsystem 0.1.48 → 0.1.50
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 +19 -0
- package/dist/index.css +1 -1
- package/dist/index.d.mts +347 -173
- package/dist/index.d.ts +347 -173
- package/dist/index.js +3602 -2811
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +4761 -3993
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -5
- package/src/__tests__/validateDropIndicator.test.ts +4 -4
- package/src/components/ActionMenu.tsx +15 -4
- package/src/components/Avatar.tsx +2 -2
- package/src/components/Banner.tsx +29 -0
- package/src/components/BaseToolbar.tsx +2 -2
- package/src/components/Button.tsx +2 -2
- package/src/components/Checkbox.tsx +2 -2
- package/src/components/Chip.tsx +15 -14
- package/src/components/Collection.tsx +25 -2
- package/src/components/Combobox.tsx +22 -23
- package/src/components/ComboboxMenu.tsx +1 -1
- package/src/components/Dialog.tsx +2 -2
- 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 +131 -113
- package/src/components/GridView.tsx +36 -18
- package/src/components/InputField.tsx +116 -171
- package/src/components/Label.tsx +14 -73
- package/src/components/LabeledField.tsx +13 -2
- package/src/components/List.tsx +106 -47
- package/src/components/ListView.tsx +52 -31
- package/src/components/MediaThumbnail.tsx +14 -6
- package/src/components/Message.tsx +8 -8
- package/src/components/NoyaLogo.tsx +41 -0
- package/src/components/Popover.tsx +9 -6
- package/src/components/Section.tsx +172 -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 +186 -47
- package/src/components/UserPointer.tsx +1 -1
- package/src/components/ai-assistant/AIAssistantLayout.tsx +102 -0
- package/src/components/connected-users-menu/ConnectedUsersMenuLayout.tsx +71 -0
- package/src/components/file-explorer/FileExplorerLayout.tsx +71 -0
- package/src/components/internal/Menu.tsx +20 -10
- package/src/components/internal/SelectItem.tsx +3 -2
- package/src/components/pipeline/PipelineResultLayout.tsx +32 -0
- package/src/index.css +118 -112
- package/src/index.tsx +9 -0
- package/src/theme/index.ts +2 -0
- package/src/utils/classNames.ts +76 -3
- package/src/utils/inputs.ts +21 -0
- package/src/utils/moveTreeItem.ts +99 -0
- package/tailwind.config.ts +82 -75
|
@@ -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}
|
package/src/components/Label.tsx
CHANGED
|
@@ -1,72 +1,9 @@
|
|
|
1
|
-
import React, {
|
|
2
|
-
|
|
3
|
-
LabelHTMLAttributes,
|
|
4
|
-
forwardRef,
|
|
5
|
-
memo,
|
|
6
|
-
useMemo,
|
|
7
|
-
} from "react";
|
|
8
|
-
import { cx } from "../utils/classNames";
|
|
9
|
-
import { InputFieldSize } from "./InputField";
|
|
1
|
+
import React, { LabelHTMLAttributes, forwardRef, memo } from "react";
|
|
2
|
+
import { mergeConflictingClassNames } from "../utils/classNames";
|
|
10
3
|
|
|
11
4
|
export type LabelPosition = "inset" | "start" | "end" | "above";
|
|
12
5
|
|
|
13
|
-
export interface LabelProps extends LabelHTMLAttributes<HTMLLabelElement> {
|
|
14
|
-
/** @default start */
|
|
15
|
-
position?: Exclude<LabelPosition, "inset">;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export type InsetLabelProps = {
|
|
19
|
-
/** @default medium */
|
|
20
|
-
size?: InputFieldSize;
|
|
21
|
-
/** when used with an InputField.Root and label position is inset, it will add padding to the right of the label to account for the button */
|
|
22
|
-
buttonWidth?: number;
|
|
23
|
-
} & LabelHTMLAttributes<HTMLLabelElement>;
|
|
24
|
-
|
|
25
|
-
// apply to <label
|
|
26
|
-
export const insetLabelTextStyles = `font-sans text-label uppercase text-text-disabled leading-[19px] font-bold text-[60%] truncate pointer-events-none text-right`;
|
|
27
|
-
|
|
28
|
-
// InsetLabelContainer
|
|
29
|
-
export const insetLabelStyles = `flex items-center absolute top-0 bottom-0 z-label`;
|
|
30
|
-
const getInsetLabelPosition = ({
|
|
31
|
-
buttonWidth,
|
|
32
|
-
}: {
|
|
33
|
-
buttonWidth?: number;
|
|
34
|
-
}): CSSProperties => ({
|
|
35
|
-
right: `${6 + (buttonWidth ?? 0)}px`,
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
export const InsetLabel = memo(
|
|
39
|
-
forwardRef<HTMLLabelElement, InsetLabelProps>(function InsetLabel(
|
|
40
|
-
{ children, buttonWidth, size = "medium", className, style, ...props },
|
|
41
|
-
ref
|
|
42
|
-
) {
|
|
43
|
-
const styles = useMemo(
|
|
44
|
-
() => ({
|
|
45
|
-
...getInsetLabelPosition({
|
|
46
|
-
buttonWidth,
|
|
47
|
-
}),
|
|
48
|
-
...style,
|
|
49
|
-
}),
|
|
50
|
-
[buttonWidth, style]
|
|
51
|
-
);
|
|
52
|
-
|
|
53
|
-
return (
|
|
54
|
-
<label
|
|
55
|
-
ref={ref}
|
|
56
|
-
style={styles}
|
|
57
|
-
className={cx(
|
|
58
|
-
insetLabelStyles,
|
|
59
|
-
insetLabelTextStyles,
|
|
60
|
-
size === "small" ? "min-h-[19px]" : "min-h-[27px]",
|
|
61
|
-
className
|
|
62
|
-
)}
|
|
63
|
-
{...props}
|
|
64
|
-
>
|
|
65
|
-
{children}
|
|
66
|
-
</label>
|
|
67
|
-
);
|
|
68
|
-
})
|
|
69
|
-
);
|
|
6
|
+
export interface LabelProps extends LabelHTMLAttributes<HTMLLabelElement> {}
|
|
70
7
|
|
|
71
8
|
const labelStyles = "flex items-center select-none z-label relative";
|
|
72
9
|
const labelTextStyles =
|
|
@@ -74,18 +11,22 @@ const labelTextStyles =
|
|
|
74
11
|
|
|
75
12
|
export const Label = memo(
|
|
76
13
|
forwardRef<HTMLLabelElement, LabelProps>(function Label(
|
|
77
|
-
{ className,
|
|
14
|
+
{ className, children, ...props },
|
|
78
15
|
ref
|
|
79
16
|
) {
|
|
80
17
|
return (
|
|
81
18
|
<label
|
|
82
19
|
ref={ref}
|
|
83
|
-
className={
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
20
|
+
className={mergeConflictingClassNames(
|
|
21
|
+
[
|
|
22
|
+
labelStyles,
|
|
23
|
+
labelTextStyles,
|
|
24
|
+
"text-left min-h-input-height",
|
|
25
|
+
className,
|
|
26
|
+
],
|
|
27
|
+
{
|
|
28
|
+
categories: ["color", "font"],
|
|
29
|
+
}
|
|
89
30
|
)}
|
|
90
31
|
{...props}
|
|
91
32
|
>
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
LabelContext,
|
|
5
5
|
LabelPositionContext,
|
|
6
6
|
LabelPositionContextValue,
|
|
7
|
+
useLabelPosition,
|
|
7
8
|
useLabelWidth,
|
|
8
9
|
} from "../hooks/useLabel";
|
|
9
10
|
import { cx } from "../utils/classNames";
|
|
@@ -23,7 +24,7 @@ const labelPositionInset: LabelPositionContextValue = {
|
|
|
23
24
|
export const LabeledField = memo(function LabeledField({
|
|
24
25
|
children,
|
|
25
26
|
label,
|
|
26
|
-
labelPosition
|
|
27
|
+
labelPosition: labelPositionProp,
|
|
27
28
|
fieldId,
|
|
28
29
|
minWidth = 100,
|
|
29
30
|
className,
|
|
@@ -44,6 +45,10 @@ export const LabeledField = memo(function LabeledField({
|
|
|
44
45
|
const labelWidthFromContext = useLabelWidth();
|
|
45
46
|
const labelWidth = labelWidthFromContext ?? minWidth;
|
|
46
47
|
|
|
48
|
+
const labelPositionFromContext = useLabelPosition();
|
|
49
|
+
const labelPosition =
|
|
50
|
+
labelPositionProp ?? labelPositionFromContext ?? "start";
|
|
51
|
+
|
|
47
52
|
const labeledFieldClasses = useMemo(
|
|
48
53
|
() => labeledFieldStyles(labelPosition),
|
|
49
54
|
[labelPosition]
|
|
@@ -91,7 +96,13 @@ export const LabeledField = memo(function LabeledField({
|
|
|
91
96
|
<div className={labeledFieldClasses}>
|
|
92
97
|
<Label
|
|
93
98
|
htmlFor={fieldId}
|
|
94
|
-
position
|
|
99
|
+
// we do this so presence avatars are aligned to the right when position is above
|
|
100
|
+
// and when start/end position with textarea it aligns label to the top of the field
|
|
101
|
+
className={
|
|
102
|
+
labelPosition === "start" || labelPosition === "end"
|
|
103
|
+
? "self-start"
|
|
104
|
+
: undefined
|
|
105
|
+
}
|
|
95
106
|
style={labelStyle}
|
|
96
107
|
{...props}
|
|
97
108
|
>
|