@noya-app/noya-designsystem 0.1.24 → 0.1.26
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 +9 -9
- package/CHANGELOG.md +14 -0
- package/dist/index.d.mts +23 -3
- package/dist/index.d.ts +23 -3
- package/dist/index.js +347 -162
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +373 -193
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/components/Button.tsx +7 -0
- package/src/components/Chip.tsx +16 -11
- package/src/components/InputField.tsx +48 -8
- package/src/components/InputFieldWithCompletions.tsx +5 -3
- package/src/components/InspectorPrimitives.tsx +1 -1
- package/src/components/ListView.tsx +36 -8
- package/src/components/SelectMenu.tsx +40 -5
- package/src/components/Switch.tsx +3 -0
- package/src/components/Text.tsx +3 -0
- package/src/components/TextArea.tsx +82 -0
- package/src/index.tsx +1 -0
- package/src/theme/light.ts +26 -22
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@noya-app/noya-designsystem",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.26",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"module": "./dist/index.mjs",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"@dnd-kit/core": "3.1.1",
|
|
14
14
|
"@dnd-kit/modifiers": "3.0.0",
|
|
15
15
|
"@dnd-kit/sortable": "4.0.0",
|
|
16
|
-
"@noya-app/noya-colorpicker": "0.1.
|
|
16
|
+
"@noya-app/noya-colorpicker": "0.1.11",
|
|
17
17
|
"@noya-app/noya-utils": "0.1.2",
|
|
18
18
|
"@noya-app/noya-geometry": "0.1.4",
|
|
19
19
|
"@noya-app/noya-icons": "0.1.4",
|
|
@@ -137,6 +137,12 @@ export const ButtonElement = styled.button<{
|
|
|
137
137
|
boxShadow: "0 1px 2px rgba(0, 0, 0, 0.1)",
|
|
138
138
|
fontSize: "12px",
|
|
139
139
|
padding: "0px 4px",
|
|
140
|
+
"&:hover": {
|
|
141
|
+
opacity: 0.8,
|
|
142
|
+
},
|
|
143
|
+
"&:active": {
|
|
144
|
+
opacity: 0.9,
|
|
145
|
+
},
|
|
140
146
|
}),
|
|
141
147
|
display: "flex",
|
|
142
148
|
alignItems: "center",
|
|
@@ -186,6 +192,7 @@ export interface ButtonRootProps {
|
|
|
186
192
|
contentStyle?: CSSProperties;
|
|
187
193
|
defaultBackground?: string;
|
|
188
194
|
as?: React.ElementType;
|
|
195
|
+
htmlFor?: string;
|
|
189
196
|
href?: string;
|
|
190
197
|
target?: string;
|
|
191
198
|
rel?: string;
|
package/src/components/Chip.tsx
CHANGED
|
@@ -5,7 +5,7 @@ import { useDesignSystemTheme } from "../contexts/DesignSystemConfiguration";
|
|
|
5
5
|
import { useHover } from "../hooks/useHover";
|
|
6
6
|
|
|
7
7
|
type ChipColorScheme = "primary" | "secondary" | "error";
|
|
8
|
-
type ChipSize = "small" | "medium";
|
|
8
|
+
type ChipSize = "small" | "medium" | "large";
|
|
9
9
|
type ChipVariant = "solid" | "outlined" | "ghost";
|
|
10
10
|
|
|
11
11
|
const ChipElement = styled.span<{
|
|
@@ -24,9 +24,9 @@ const ChipElement = styled.span<{
|
|
|
24
24
|
|
|
25
25
|
const backgroundColor =
|
|
26
26
|
$colorScheme === "primary"
|
|
27
|
-
?
|
|
27
|
+
? theme.colors.primaryPastel
|
|
28
28
|
: $colorScheme === "secondary"
|
|
29
|
-
?
|
|
29
|
+
? theme.colors.secondaryPastel
|
|
30
30
|
: $colorScheme === "error"
|
|
31
31
|
? "rgb(255, 219, 219)"
|
|
32
32
|
: theme.colors.inputBackground;
|
|
@@ -49,15 +49,20 @@ const ChipElement = styled.span<{
|
|
|
49
49
|
// lineHeight: "1.4",
|
|
50
50
|
lineHeight: "15px",
|
|
51
51
|
whiteSpace: "pre",
|
|
52
|
-
...($size === "
|
|
52
|
+
...($size === "large"
|
|
53
53
|
? {
|
|
54
54
|
fontSize: "11px",
|
|
55
55
|
padding: "4px 8px",
|
|
56
56
|
}
|
|
57
|
-
:
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
57
|
+
: $size === "medium"
|
|
58
|
+
? {
|
|
59
|
+
fontSize: "10px",
|
|
60
|
+
padding: "2px 6px",
|
|
61
|
+
}
|
|
62
|
+
: {
|
|
63
|
+
fontSize: "9px",
|
|
64
|
+
padding: "0px 4px",
|
|
65
|
+
}),
|
|
61
66
|
...($monospace && {
|
|
62
67
|
fontFamily: theme.fonts.monospace,
|
|
63
68
|
}),
|
|
@@ -96,7 +101,7 @@ const DeleteElement = styled(Cross1Icon)<{
|
|
|
96
101
|
cursor: "pointer",
|
|
97
102
|
opacity: 0.5,
|
|
98
103
|
|
|
99
|
-
...(size === "
|
|
104
|
+
...(size === "large"
|
|
100
105
|
? {
|
|
101
106
|
marginLeft: "2px",
|
|
102
107
|
// top: '-1px',
|
|
@@ -124,7 +129,7 @@ const AddElement = styled(PlusIcon).withConfig({
|
|
|
124
129
|
cursor: "pointer",
|
|
125
130
|
opacity: 0.5,
|
|
126
131
|
|
|
127
|
-
...(size === "
|
|
132
|
+
...(size === "large"
|
|
128
133
|
? {
|
|
129
134
|
marginRight: "2px",
|
|
130
135
|
top: "-1px",
|
|
@@ -168,7 +173,7 @@ export const Chip = memo(
|
|
|
168
173
|
addable,
|
|
169
174
|
clickable,
|
|
170
175
|
style,
|
|
171
|
-
size = "
|
|
176
|
+
size = "large",
|
|
172
177
|
variant = "solid",
|
|
173
178
|
monospace = false,
|
|
174
179
|
tabIndex,
|
|
@@ -370,40 +370,46 @@ const InputFieldInput = forwardRef(function InputFieldInput(
|
|
|
370
370
|
);
|
|
371
371
|
});
|
|
372
372
|
|
|
373
|
-
const InputFieldTypeahead = (props: { value: string }) => {
|
|
373
|
+
const InputFieldTypeahead = (props: { prefix: string; value: string }) => {
|
|
374
374
|
const {
|
|
375
375
|
labelPosition,
|
|
376
376
|
labelSize,
|
|
377
377
|
hasDropdown,
|
|
378
378
|
hasLabel,
|
|
379
379
|
size,
|
|
380
|
-
onFocusChange,
|
|
380
|
+
// onFocusChange,
|
|
381
381
|
} = useContext(InputFieldContext);
|
|
382
382
|
|
|
383
383
|
return (
|
|
384
384
|
<InputElement
|
|
385
|
+
as="span"
|
|
385
386
|
$labelPosition={labelPosition}
|
|
386
387
|
$labelSize={labelSize}
|
|
387
388
|
$hasLabel={hasLabel}
|
|
388
389
|
$hasDropdown={hasDropdown}
|
|
389
390
|
$size={size}
|
|
390
|
-
onFocusChange={onFocusChange}
|
|
391
|
-
readOnly
|
|
392
|
-
placeholder={props.value}
|
|
391
|
+
// onFocusChange={onFocusChange}
|
|
392
|
+
// readOnly
|
|
393
|
+
// placeholder={props.value}
|
|
393
394
|
value=""
|
|
394
395
|
style={{
|
|
395
396
|
position: "absolute",
|
|
396
|
-
top:
|
|
397
|
+
top: 0,
|
|
397
398
|
left: 0,
|
|
398
399
|
right: 0,
|
|
399
|
-
bottom:
|
|
400
|
+
bottom: 0,
|
|
400
401
|
width: "100%",
|
|
401
402
|
height: "100%",
|
|
402
403
|
pointerEvents: "none",
|
|
403
404
|
background: "transparent",
|
|
404
405
|
boxShadow: "none",
|
|
405
406
|
}}
|
|
406
|
-
|
|
407
|
+
>
|
|
408
|
+
<span style={{ opacity: 0 }}>{props.prefix}</span>
|
|
409
|
+
<span style={{ opacity: 0.5 }}>
|
|
410
|
+
{props.value.slice(props.prefix.length)}
|
|
411
|
+
</span>
|
|
412
|
+
</InputElement>
|
|
407
413
|
);
|
|
408
414
|
};
|
|
409
415
|
|
|
@@ -662,6 +668,39 @@ function InputFieldRoot({
|
|
|
662
668
|
);
|
|
663
669
|
}
|
|
664
670
|
|
|
671
|
+
const PrimitiveInputField = styled.input(({ theme, readOnly, disabled }) => {
|
|
672
|
+
return {
|
|
673
|
+
// placeholder
|
|
674
|
+
"&::placeholder": {
|
|
675
|
+
color: theme.colors.textDisabled,
|
|
676
|
+
},
|
|
677
|
+
...theme.textStyles.small,
|
|
678
|
+
color: readOnly
|
|
679
|
+
? theme.colors.textMuted
|
|
680
|
+
: disabled
|
|
681
|
+
? theme.colors.textDisabled
|
|
682
|
+
: theme.colors.text,
|
|
683
|
+
width: "0px", // Reset intrinsic width
|
|
684
|
+
flex: "1 1 0px",
|
|
685
|
+
position: "relative",
|
|
686
|
+
border: "0",
|
|
687
|
+
outline: "none",
|
|
688
|
+
minWidth: "0",
|
|
689
|
+
alignSelf: "stretch",
|
|
690
|
+
borderRadius: "4px",
|
|
691
|
+
paddingTop: "4px",
|
|
692
|
+
paddingBottom: "4px",
|
|
693
|
+
paddingLeft: "6px",
|
|
694
|
+
paddingRight: "6px",
|
|
695
|
+
background: theme.colors.inputBackground,
|
|
696
|
+
"&:focus": {
|
|
697
|
+
boxShadow: `0 0 0 2px ${theme.colors.primary}`,
|
|
698
|
+
},
|
|
699
|
+
userSelect: "all",
|
|
700
|
+
pointerEvents: "all",
|
|
701
|
+
};
|
|
702
|
+
});
|
|
703
|
+
|
|
665
704
|
export namespace InputField {
|
|
666
705
|
export const Root = memo(InputFieldRoot);
|
|
667
706
|
export const Input = memo(InputFieldInput);
|
|
@@ -670,4 +709,5 @@ export namespace InputField {
|
|
|
670
709
|
export const DropdownMenu = InputFieldDropdownMenu;
|
|
671
710
|
export const Button = InputFieldButton;
|
|
672
711
|
export const Label = InputFieldLabel;
|
|
712
|
+
export const PrimitiveElement = PrimitiveInputField;
|
|
673
713
|
}
|
|
@@ -468,9 +468,11 @@ export const InputFieldWithCompletions = memo(
|
|
|
468
468
|
aria-expanded={isFocused && display !== "none"}
|
|
469
469
|
aria-controls="component-listbox"
|
|
470
470
|
/>
|
|
471
|
-
{filter &&
|
|
472
|
-
|
|
473
|
-
|
|
471
|
+
{filter &&
|
|
472
|
+
typeaheadValue &&
|
|
473
|
+
typeaheadValue.toLowerCase().startsWith(filter.toLowerCase()) && (
|
|
474
|
+
<InputField.Typeahead value={typeaheadValue} prefix={filter} />
|
|
475
|
+
)}
|
|
474
476
|
{(!isFocused || !hideChildrenWhenFocused) && children}
|
|
475
477
|
{loading && isFocused && (
|
|
476
478
|
<InputField.Label>
|
|
@@ -66,14 +66,15 @@ type ListRowContextValue = {
|
|
|
66
66
|
// The dragged item isn't passed a context value (doing so causes an infinite loop),
|
|
67
67
|
// so we pass this separately. Rows need the indentation to look correct when dragging,
|
|
68
68
|
// and the dragged row doesn't have a ListRowContextValue.
|
|
69
|
-
type
|
|
69
|
+
type ListViewDraggingContextValue = {
|
|
70
70
|
indentation: number;
|
|
71
71
|
isDragging?: boolean;
|
|
72
72
|
};
|
|
73
73
|
|
|
74
|
-
const
|
|
74
|
+
const ListViewDraggingContext = createContext<ListViewDraggingContextValue>({
|
|
75
75
|
indentation: 12,
|
|
76
76
|
});
|
|
77
|
+
(ListViewDraggingContext as any).displayName = "ListViewDraggingContext";
|
|
77
78
|
|
|
78
79
|
const ListRowContext = createContext<ListRowContextValue>({
|
|
79
80
|
marginType: "none",
|
|
@@ -88,6 +89,7 @@ const ListRowContext = createContext<ListRowContextValue>({
|
|
|
88
89
|
isSectionHeader: false,
|
|
89
90
|
colorScheme: "primary",
|
|
90
91
|
});
|
|
92
|
+
(ListRowContext as any).displayName = "ListRowContext";
|
|
91
93
|
|
|
92
94
|
/* ----------------------------------------------------------------------------
|
|
93
95
|
* RowTitle
|
|
@@ -333,6 +335,7 @@ interface ListViewRowProps<MenuItemType extends string = string> {
|
|
|
333
335
|
onMenuOpenChange?: (isOpen: boolean) => void;
|
|
334
336
|
onKeyDown?: (event: React.KeyboardEvent) => void;
|
|
335
337
|
style?: CSSProperties;
|
|
338
|
+
className?: string;
|
|
336
339
|
dragIndicatorStyle?:
|
|
337
340
|
| CSSProperties
|
|
338
341
|
| ((props: {
|
|
@@ -367,6 +370,7 @@ const ListViewRow = forwardRef(function ListViewRow<
|
|
|
367
370
|
onKeyDown,
|
|
368
371
|
style,
|
|
369
372
|
dragIndicatorStyle,
|
|
373
|
+
className,
|
|
370
374
|
}: ListViewRowProps<MenuItemType>,
|
|
371
375
|
forwardedRef: ForwardedRef<HTMLElement>
|
|
372
376
|
) {
|
|
@@ -386,7 +390,7 @@ const ListViewRow = forwardRef(function ListViewRow<
|
|
|
386
390
|
const { hoverProps } = useHover({
|
|
387
391
|
onHoverChange,
|
|
388
392
|
});
|
|
389
|
-
const { indentation, isDragging } = useContext(
|
|
393
|
+
const { indentation, isDragging } = useContext(ListViewDraggingContext);
|
|
390
394
|
|
|
391
395
|
const handlePress = useCallback(
|
|
392
396
|
(event: React.MouseEvent) => {
|
|
@@ -463,6 +467,7 @@ const ListViewRow = forwardRef(function ListViewRow<
|
|
|
463
467
|
{ [pressEventName]: handlePress }
|
|
464
468
|
)}
|
|
465
469
|
tabIndex={tabIndex}
|
|
470
|
+
className={className}
|
|
466
471
|
>
|
|
467
472
|
{relativeDropPosition && (
|
|
468
473
|
<ListViewDragIndicatorElement
|
|
@@ -524,6 +529,7 @@ const ListViewRow = forwardRef(function ListViewRow<
|
|
|
524
529
|
const RenderItemContext = createContext<(index: number) => ReactNode>(
|
|
525
530
|
() => null
|
|
526
531
|
);
|
|
532
|
+
(RenderItemContext as any).displayName = "RenderItemContext";
|
|
527
533
|
|
|
528
534
|
const VirtualizedListRow = memo(function VirtualizedListRow({
|
|
529
535
|
index,
|
|
@@ -761,8 +767,29 @@ const ListViewRootInner = forwardRef(function ListViewRootInner<T>(
|
|
|
761
767
|
[data, renderItem]
|
|
762
768
|
);
|
|
763
769
|
|
|
770
|
+
// TODO: Do we still use section header properties anywhere?
|
|
771
|
+
const defaultContextValue = useMemo(
|
|
772
|
+
(): ListRowContextValue => ({
|
|
773
|
+
colorScheme,
|
|
774
|
+
marginType: "none",
|
|
775
|
+
selectedPosition: "only",
|
|
776
|
+
sortable,
|
|
777
|
+
expandable,
|
|
778
|
+
divider,
|
|
779
|
+
pressEventName,
|
|
780
|
+
variant,
|
|
781
|
+
gap,
|
|
782
|
+
sectionHeaderVariant: "normal",
|
|
783
|
+
isSectionHeader: false,
|
|
784
|
+
}),
|
|
785
|
+
[colorScheme, sortable, expandable, divider, pressEventName, variant, gap]
|
|
786
|
+
);
|
|
787
|
+
|
|
764
788
|
const getItemContextValue = useCallback(
|
|
765
789
|
(i: number): ListRowContextValue | undefined => {
|
|
790
|
+
if (variant === "bare" || variant === "normal")
|
|
791
|
+
return defaultContextValue;
|
|
792
|
+
|
|
766
793
|
const current = renderChild(i);
|
|
767
794
|
|
|
768
795
|
if (!isValidElement(current)) return;
|
|
@@ -827,6 +854,8 @@ const ListViewRootInner = forwardRef(function ListViewRootInner<T>(
|
|
|
827
854
|
};
|
|
828
855
|
},
|
|
829
856
|
[
|
|
857
|
+
variant,
|
|
858
|
+
defaultContextValue,
|
|
830
859
|
renderChild,
|
|
831
860
|
data.length,
|
|
832
861
|
colorScheme,
|
|
@@ -834,7 +863,6 @@ const ListViewRootInner = forwardRef(function ListViewRootInner<T>(
|
|
|
834
863
|
expandable,
|
|
835
864
|
divider,
|
|
836
865
|
pressEventName,
|
|
837
|
-
variant,
|
|
838
866
|
sectionHeaderVariant,
|
|
839
867
|
gap,
|
|
840
868
|
]
|
|
@@ -848,9 +876,9 @@ const ListViewRootInner = forwardRef(function ListViewRootInner<T>(
|
|
|
848
876
|
const renderOverlay = useCallback(
|
|
849
877
|
(index: number) => (
|
|
850
878
|
<div style={{ opacity: 0.25 }}>
|
|
851
|
-
<
|
|
879
|
+
<ListViewDraggingContext.Provider value={draggingContextOverlayValue}>
|
|
852
880
|
{renderItem(data[index], index, { isDragging: true })}
|
|
853
|
-
</
|
|
881
|
+
</ListViewDraggingContext.Provider>
|
|
854
882
|
</div>
|
|
855
883
|
),
|
|
856
884
|
[draggingContextOverlayValue, renderItem, data]
|
|
@@ -916,7 +944,7 @@ const ListViewRootInner = forwardRef(function ListViewRootInner<T>(
|
|
|
916
944
|
const draggingContextValue = useMemo(() => ({ indentation }), [indentation]);
|
|
917
945
|
|
|
918
946
|
return (
|
|
919
|
-
<
|
|
947
|
+
<ListViewDraggingContext.Provider value={draggingContextValue}>
|
|
920
948
|
<RootContainer
|
|
921
949
|
id={id}
|
|
922
950
|
className={className}
|
|
@@ -945,7 +973,7 @@ const ListViewRootInner = forwardRef(function ListViewRootInner<T>(
|
|
|
945
973
|
)
|
|
946
974
|
)}
|
|
947
975
|
</RootContainer>
|
|
948
|
-
</
|
|
976
|
+
</ListViewDraggingContext.Provider>
|
|
949
977
|
);
|
|
950
978
|
});
|
|
951
979
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DropdownChevronIcon } from "@noya-app/noya-icons";
|
|
2
2
|
import * as Select from "@radix-ui/react-select";
|
|
3
|
-
import React, { memo } from "react";
|
|
3
|
+
import React, { CSSProperties, memo } from "react";
|
|
4
4
|
import { styled } from "styled-components";
|
|
5
5
|
import { Button } from "./Button";
|
|
6
6
|
import { renderIcon } from "./Icons";
|
|
@@ -15,6 +15,17 @@ type Props<T extends string> = {
|
|
|
15
15
|
value: T;
|
|
16
16
|
onSelect?: (value: T) => void;
|
|
17
17
|
placeholder?: string;
|
|
18
|
+
disabled?: boolean;
|
|
19
|
+
readOnly?: boolean;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const readOnlyStyle: CSSProperties = {
|
|
23
|
+
justifyContent: "flex-start",
|
|
24
|
+
textAlign: "left",
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const flexStyle: CSSProperties = {
|
|
28
|
+
flex: 1,
|
|
18
29
|
};
|
|
19
30
|
|
|
20
31
|
export const SelectMenu = memo(function SelectMenu<T extends string = string>({
|
|
@@ -25,6 +36,8 @@ export const SelectMenu = memo(function SelectMenu<T extends string = string>({
|
|
|
25
36
|
value,
|
|
26
37
|
onSelect,
|
|
27
38
|
placeholder,
|
|
39
|
+
disabled,
|
|
40
|
+
readOnly,
|
|
28
41
|
}: Props<T>) {
|
|
29
42
|
const selectedItem = menuItems.find(
|
|
30
43
|
(item): item is RegularMenuItem<T> =>
|
|
@@ -32,18 +45,40 @@ export const SelectMenu = memo(function SelectMenu<T extends string = string>({
|
|
|
32
45
|
);
|
|
33
46
|
const icon = selectedItem?.icon;
|
|
34
47
|
|
|
48
|
+
if (readOnly) {
|
|
49
|
+
return (
|
|
50
|
+
<Button
|
|
51
|
+
id={id}
|
|
52
|
+
style={style}
|
|
53
|
+
contentStyle={readOnlyStyle}
|
|
54
|
+
className={className}
|
|
55
|
+
disabled={disabled}
|
|
56
|
+
>
|
|
57
|
+
{icon && (
|
|
58
|
+
<>
|
|
59
|
+
{renderIcon(icon)}
|
|
60
|
+
<Spacer.Horizontal inline size={6} />
|
|
61
|
+
</>
|
|
62
|
+
)}
|
|
63
|
+
<span style={flexStyle}>{selectedItem?.title ?? value}</span>
|
|
64
|
+
</Button>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
35
68
|
return (
|
|
36
69
|
<Select.Root value={value} onValueChange={onSelect}>
|
|
37
70
|
<Select.SelectTrigger asChild>
|
|
38
|
-
<Button id={id} style={style} className={className}>
|
|
71
|
+
<Button id={id} style={style} className={className} disabled={disabled}>
|
|
39
72
|
{icon && (
|
|
40
73
|
<>
|
|
41
74
|
{renderIcon(icon)}
|
|
42
|
-
<Spacer.Horizontal size={
|
|
75
|
+
<Spacer.Horizontal inline size={6} />
|
|
43
76
|
</>
|
|
44
77
|
)}
|
|
45
|
-
<
|
|
46
|
-
|
|
78
|
+
<span style={flexStyle}>
|
|
79
|
+
<Select.Value placeholder={placeholder} />
|
|
80
|
+
</span>
|
|
81
|
+
<Spacer.Horizontal inline size={6} />
|
|
47
82
|
<DropdownChevronIcon />
|
|
48
83
|
</Button>
|
|
49
84
|
</Select.SelectTrigger>
|
|
@@ -45,17 +45,20 @@ interface Props {
|
|
|
45
45
|
value: boolean;
|
|
46
46
|
onChange: (value: boolean) => void;
|
|
47
47
|
colorScheme?: SwitchColorScheme;
|
|
48
|
+
disabled?: boolean;
|
|
48
49
|
}
|
|
49
50
|
|
|
50
51
|
export const Switch = function Switch({
|
|
51
52
|
value,
|
|
52
53
|
onChange,
|
|
53
54
|
colorScheme = "normal",
|
|
55
|
+
disabled,
|
|
54
56
|
}: Props) {
|
|
55
57
|
return (
|
|
56
58
|
<SwitchRoot
|
|
57
59
|
$colorScheme={colorScheme}
|
|
58
60
|
checked={value}
|
|
61
|
+
disabled={disabled}
|
|
59
62
|
onCheckedChange={(newValue) => {
|
|
60
63
|
onChange(newValue);
|
|
61
64
|
}}
|
package/src/components/Text.tsx
CHANGED
|
@@ -55,6 +55,7 @@ interface Props extends StyleProps {
|
|
|
55
55
|
as?: keyof ReactHTML;
|
|
56
56
|
href?: string;
|
|
57
57
|
className?: string;
|
|
58
|
+
style?: React.CSSProperties;
|
|
58
59
|
variant: keyof Theme["textStyles"];
|
|
59
60
|
breakpoints?: BreakpointCollection<StyleProps> | null | false;
|
|
60
61
|
color?: ThemeColorName;
|
|
@@ -89,6 +90,7 @@ export const Text = forwardRef(function Text(
|
|
|
89
90
|
tabIndex,
|
|
90
91
|
onClick,
|
|
91
92
|
onKeyDown,
|
|
93
|
+
style,
|
|
92
94
|
...rest
|
|
93
95
|
}: Props,
|
|
94
96
|
forwardedRef: ForwardedRef<HTMLElement>
|
|
@@ -100,6 +102,7 @@ export const Text = forwardRef(function Text(
|
|
|
100
102
|
ref={forwardedRef}
|
|
101
103
|
as={element}
|
|
102
104
|
className={className}
|
|
105
|
+
style={style}
|
|
103
106
|
tabIndex={tabIndex}
|
|
104
107
|
$variant={variant}
|
|
105
108
|
$breakpoints={breakpoints}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { assignRef } from "@noya-app/react-utils";
|
|
2
|
+
import React, { forwardRef, memo, useCallback, useEffect, useRef } from "react";
|
|
3
|
+
import styled from "styled-components";
|
|
4
|
+
|
|
5
|
+
const TextAreaElement = styled.textarea(({ theme }) => ({
|
|
6
|
+
...theme.textStyles.small,
|
|
7
|
+
color: theme.colors.text,
|
|
8
|
+
background: theme.colors.inputBackground,
|
|
9
|
+
width: "0px",
|
|
10
|
+
flex: "1 1 auto",
|
|
11
|
+
padding: "4px 6px",
|
|
12
|
+
border: "none",
|
|
13
|
+
outline: "none",
|
|
14
|
+
height: 100,
|
|
15
|
+
borderRadius: "4px",
|
|
16
|
+
"&::placeholder": {
|
|
17
|
+
color: theme.colors.textDisabled,
|
|
18
|
+
},
|
|
19
|
+
"&:focus": {
|
|
20
|
+
boxShadow: `0 0 0 2px ${theme.colors.primary}`,
|
|
21
|
+
},
|
|
22
|
+
// readonly
|
|
23
|
+
"&:read-only": {
|
|
24
|
+
color: theme.colors.textDisabled,
|
|
25
|
+
},
|
|
26
|
+
resize: "none",
|
|
27
|
+
}));
|
|
28
|
+
|
|
29
|
+
export const useAutoResize = (value: string) => {
|
|
30
|
+
const textareaRef = useRef<HTMLTextAreaElement | null>(null);
|
|
31
|
+
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
if (!textareaRef.current) return;
|
|
34
|
+
|
|
35
|
+
textareaRef.current.style.height = "auto"; // Reset the height
|
|
36
|
+
textareaRef.current.style.height = `${textareaRef.current.scrollHeight}px`;
|
|
37
|
+
}, [value]);
|
|
38
|
+
|
|
39
|
+
return textareaRef;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export const AutoResizingTextArea = memo(
|
|
43
|
+
forwardRef(function AutoResizingTextArea(
|
|
44
|
+
{
|
|
45
|
+
value,
|
|
46
|
+
onChangeText,
|
|
47
|
+
...rest
|
|
48
|
+
}: Omit<
|
|
49
|
+
React.ComponentProps<typeof TextAreaElement>,
|
|
50
|
+
"onChange" | "value"
|
|
51
|
+
> & {
|
|
52
|
+
onChangeText: (value: string) => void;
|
|
53
|
+
value?: string;
|
|
54
|
+
},
|
|
55
|
+
forwardedRef: React.ForwardedRef<HTMLTextAreaElement>
|
|
56
|
+
) {
|
|
57
|
+
const ref = useAutoResize(value || rest.placeholder || "");
|
|
58
|
+
|
|
59
|
+
const handleChange = useCallback(
|
|
60
|
+
(event: React.ChangeEvent<HTMLTextAreaElement>) =>
|
|
61
|
+
onChangeText(event.target.value),
|
|
62
|
+
[onChangeText]
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
const handleRef = useCallback(
|
|
66
|
+
(value: HTMLTextAreaElement | null) => {
|
|
67
|
+
ref.current = value;
|
|
68
|
+
assignRef(forwardedRef, value);
|
|
69
|
+
},
|
|
70
|
+
[ref, forwardedRef]
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
return (
|
|
74
|
+
<TextAreaElement
|
|
75
|
+
ref={handleRef}
|
|
76
|
+
{...rest}
|
|
77
|
+
onChange={handleChange}
|
|
78
|
+
value={value}
|
|
79
|
+
/>
|
|
80
|
+
);
|
|
81
|
+
})
|
|
82
|
+
);
|
package/src/index.tsx
CHANGED
|
@@ -49,6 +49,7 @@ export * from "./components/Spacer";
|
|
|
49
49
|
export * from "./components/Stack";
|
|
50
50
|
export * from "./components/Switch";
|
|
51
51
|
export * from "./components/Text";
|
|
52
|
+
export * from "./components/TextArea";
|
|
52
53
|
export * from "./components/Toast";
|
|
53
54
|
export * from "./components/Tooltip";
|
|
54
55
|
export * from "./components/TreeView";
|