@noya-app/noya-designsystem 0.1.40 → 0.1.42

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.
Files changed (52) hide show
  1. package/.turbo/turbo-build.log +10 -10
  2. package/CHANGELOG.md +16 -0
  3. package/dist/index.css +1 -1
  4. package/dist/index.d.mts +221 -124
  5. package/dist/index.d.ts +221 -124
  6. package/dist/index.js +7464 -6801
  7. package/dist/index.js.map +1 -1
  8. package/dist/index.mjs +7867 -7246
  9. package/dist/index.mjs.map +1 -1
  10. package/package.json +5 -6
  11. package/src/__tests__/combobox.test.ts +578 -0
  12. package/src/components/ActivityIndicator.tsx +4 -4
  13. package/src/components/AnimatePresence.tsx +5 -5
  14. package/src/components/Avatar.tsx +5 -2
  15. package/src/components/BaseToolbar.tsx +61 -0
  16. package/src/components/Button.tsx +1 -1
  17. package/src/components/Checkbox.tsx +7 -6
  18. package/src/components/Combobox.tsx +220 -324
  19. package/src/components/ComboboxMenu.tsx +88 -0
  20. package/src/components/Dialog.tsx +10 -6
  21. package/src/components/DimensionInput.tsx +4 -4
  22. package/src/components/FillPreviewBackground.tsx +51 -44
  23. package/src/components/FloatingWindow.tsx +5 -2
  24. package/src/components/GradientPicker.tsx +14 -14
  25. package/src/components/IconButton.tsx +6 -12
  26. package/src/components/Icons.tsx +3 -3
  27. package/src/components/InputField.tsx +145 -160
  28. package/src/components/Label.tsx +4 -4
  29. package/src/components/LabeledElementView.tsx +35 -41
  30. package/src/components/ListView.tsx +49 -39
  31. package/src/components/Message.tsx +75 -0
  32. package/src/components/Progress.tsx +2 -2
  33. package/src/components/ScrollArea.tsx +7 -5
  34. package/src/components/SegmentedControl.tsx +171 -0
  35. package/src/components/SelectMenu.tsx +61 -10
  36. package/src/components/Slider.tsx +49 -14
  37. package/src/components/Sortable.tsx +17 -27
  38. package/src/components/Spacer.tsx +28 -9
  39. package/src/components/Switch.tsx +8 -8
  40. package/src/components/TextArea.tsx +23 -13
  41. package/src/components/Toolbar.tsx +134 -0
  42. package/src/components/Tooltip.tsx +10 -7
  43. package/src/components/internal/Menu.tsx +5 -4
  44. package/src/contexts/DesignSystemConfiguration.tsx +15 -24
  45. package/src/contexts/DialogContext.tsx +137 -49
  46. package/src/contexts/ImageDataContext.tsx +6 -12
  47. package/src/index.css +4 -6
  48. package/src/index.tsx +12 -3
  49. package/src/utils/combobox.ts +369 -0
  50. package/tailwind.config.ts +2 -3
  51. package/src/components/RadioGroup.tsx +0 -100
  52. package/src/utils/completions.ts +0 -21
@@ -6,7 +6,12 @@ import {
6
6
  import { memoGeneric } from "@noya-app/react-utils";
7
7
  import * as Select from "@radix-ui/react-select";
8
8
  import { type SelectProps } from "@radix-ui/react-select";
9
- import React, { CSSProperties, FocusEventHandler, useMemo } from "react";
9
+ import React, {
10
+ CSSProperties,
11
+ FocusEventHandler,
12
+ useMemo,
13
+ useState,
14
+ } from "react";
10
15
  import { cx } from "../utils/classNames";
11
16
  import { Button } from "./Button";
12
17
  import { renderIcon } from "./Icons";
@@ -26,14 +31,15 @@ type Props<T extends string> = {
26
31
  label?: React.ReactNode;
27
32
  onFocus?: FocusEventHandler;
28
33
  onBlur?: FocusEventHandler;
29
- } & Pick<SelectProps, "open">;
34
+ } & Pick<SelectProps, "open" | "onOpenChange">;
30
35
 
31
36
  const readOnlyStyle: CSSProperties = {
32
37
  justifyContent: "flex-start",
33
38
  textAlign: "left",
34
39
  };
35
40
 
36
- const labelStyles = `font-sans text-label uppercase text-text-disabled leading-[19px] font-bold text-[60%] pointer-events-none flex items-center absolute top-0 bottom-0 right-[1.3rem] z-label`;
41
+ export const labelTextStyles = `font-sans text-label uppercase text-text-disabled leading-[19px] font-bold text-[60%]`;
42
+ const labelStyles = `pointer-events-none flex items-center absolute top-0 bottom-0 right-[1.3rem] z-label`;
37
43
 
38
44
  const scrollButtonStyles = `
39
45
  flex
@@ -61,12 +67,21 @@ export const SelectMenu = memoGeneric(function SelectMenu<
61
67
  open,
62
68
  onFocus,
63
69
  onBlur,
70
+ onOpenChange,
64
71
  }: Props<T>) {
65
72
  const selectedItem = menuItems.find(
66
73
  (item): item is RegularMenuItem<T> =>
67
74
  typeof item !== "string" && item.value === value
68
75
  );
69
76
  const icon = selectedItem?.icon;
77
+ const [internalOpen, setInternalOpen] = useState(open);
78
+
79
+ const handleOpenChange = (open: boolean) => {
80
+ if (onOpenChange) {
81
+ onOpenChange(open);
82
+ }
83
+ setInternalOpen(open);
84
+ };
70
85
 
71
86
  const readOnlyButton = useMemo(
72
87
  () => (
@@ -95,7 +110,11 @@ export const SelectMenu = memoGeneric(function SelectMenu<
95
110
  <Button
96
111
  id={id}
97
112
  style={style}
98
- className={`${className ?? "w-full"} flex-1 focus:z-interactable relative`}
113
+ className={cx(
114
+ className ?? "w-full",
115
+ "flex-1 focus:z-interactable relative",
116
+ internalOpen && "ring-2 ring-primary"
117
+ )}
99
118
  disabled={disabled}
100
119
  >
101
120
  {icon && (
@@ -108,15 +127,31 @@ export const SelectMenu = memoGeneric(function SelectMenu<
108
127
  <Select.Value placeholder={placeholder} />
109
128
  </span>
110
129
  {label && (
111
- <label htmlFor={id} className={labelStyles}>
130
+ <label htmlFor={id} className={cx(labelStyles, labelTextStyles)}>
112
131
  {label}
113
132
  </label>
114
133
  )}
115
- <DropdownChevronIcon />
134
+ <DropdownChevronIcon
135
+ className={cx(
136
+ "transition-transform duration-100",
137
+ internalOpen ? "rotate-180" : "rotate-0"
138
+ )}
139
+ />
116
140
  </Button>
117
141
  </Select.SelectTrigger>
118
142
  ),
119
- [onFocus, onBlur, id, style, className, disabled, icon, placeholder, label]
143
+ [
144
+ onFocus,
145
+ onBlur,
146
+ id,
147
+ style,
148
+ className,
149
+ disabled,
150
+ icon,
151
+ placeholder,
152
+ label,
153
+ internalOpen,
154
+ ]
120
155
  );
121
156
 
122
157
  if (readOnly) {
@@ -133,12 +168,28 @@ export const SelectMenu = memoGeneric(function SelectMenu<
133
168
  }
134
169
 
135
170
  return (
136
- <Select.Root value={value} onValueChange={onSelect} open={open}>
171
+ <Select.Root
172
+ value={value}
173
+ onValueChange={onSelect}
174
+ open={open}
175
+ onOpenChange={handleOpenChange}
176
+ >
137
177
  {trigger}
138
178
  <Select.Portal>
139
- <Select.Content className={styles.contentStyle}>
179
+ <Select.Content
180
+ className={styles.contentStyle}
181
+ position="popper"
182
+ sideOffset={6}
183
+ collisionPadding={8}
184
+ align="end"
185
+ style={{
186
+ width: "var(--radix-select-trigger-width)",
187
+ }}
188
+ >
140
189
  <Select.ScrollUpButton className={scrollButtonStyles}>
141
- <ChevronUpIcon />
190
+ <ChevronUpIcon
191
+ className={cx("transition-transform duration-100")}
192
+ />
142
193
  </Select.ScrollUpButton>
143
194
  <Select.Viewport>
144
195
  {menuItems.map((menuItem) => {
@@ -1,5 +1,5 @@
1
1
  import * as RadixSlider from "@radix-ui/react-slider";
2
- import React, { FocusEventHandler, useCallback, useMemo } from "react";
2
+ import React, { FocusEventHandler, memo, useCallback, useMemo } from "react";
3
3
  import { cx } from "../utils/classNames";
4
4
 
5
5
  type ColorScheme = "primary" | "secondary";
@@ -12,15 +12,23 @@ interface Props {
12
12
  onValueChange: (value: number) => void;
13
13
  min: number;
14
14
  max: number;
15
+ step?: number;
15
16
  colorScheme?: ColorScheme;
16
17
  label?: React.ReactNode;
17
18
  onFocus?: FocusEventHandler;
18
19
  onBlur?: FocusEventHandler;
20
+ readOnly?: boolean;
19
21
  }
20
22
 
21
23
  const labelStyles = `font-sans text-label uppercase text-text-disabled leading-[19px] font-bold text-[60%] pointer-events-none flex items-center z-label`;
22
24
 
23
- export const Slider = function Slider({
25
+ const THUMB_WIDTH = 8; // Width of the thumb in pixels
26
+
27
+ const thumbStyle = {
28
+ width: THUMB_WIDTH,
29
+ };
30
+
31
+ export const Slider = memo(function Slider({
24
32
  id,
25
33
  style,
26
34
  className,
@@ -28,21 +36,18 @@ export const Slider = function Slider({
28
36
  onValueChange,
29
37
  min,
30
38
  max,
39
+ step,
31
40
  colorScheme,
32
41
  label,
33
42
  onFocus,
34
43
  onBlur,
44
+ readOnly = false,
35
45
  }: Props) {
36
46
  const arrayValue = useMemo(
37
47
  () => [Math.min(Math.max(value, min), max)],
38
48
  [value, min, max]
39
49
  );
40
50
 
41
- const percentage = useMemo(() => {
42
- const percent = ((arrayValue[0] - min) / (max - min)) * 100;
43
- return `${percent}%`;
44
- }, [arrayValue, min, max]);
45
-
46
51
  const handleValueChange = useCallback(
47
52
  (arrayValue: number[]) => {
48
53
  onValueChange(arrayValue[0]);
@@ -50,22 +55,50 @@ export const Slider = function Slider({
50
55
  [onValueChange]
51
56
  );
52
57
 
58
+ const percentage = useMemo(() => {
59
+ // Calculate the raw percentage (for label)
60
+ const rawPercent = (arrayValue[0] - min) / (max - min);
61
+ // Adjust the percentage to account for thumb width (for fill)
62
+ const adjustedPercent =
63
+ rawPercent * (100 - (THUMB_WIDTH * 100) / 300) + (THUMB_WIDTH * 50) / 300;
64
+ return {
65
+ raw: rawPercent * 100,
66
+ adjusted: adjustedPercent,
67
+ };
68
+ }, [arrayValue, min, max]);
69
+
70
+ const trackFillStyle = useMemo(
71
+ () => ({
72
+ clipPath: `inset(0 ${100 - percentage.adjusted}% 0 0)`,
73
+ }),
74
+ [percentage.adjusted]
75
+ );
76
+
77
+ const labelStyle = useMemo(
78
+ () => ({
79
+ clipPath: `inset(0 ${100 - percentage.raw}% 0 0)`,
80
+ }),
81
+ [percentage.raw]
82
+ );
83
+
53
84
  return (
54
85
  <RadixSlider.Root
55
86
  min={min}
56
87
  max={max}
88
+ step={step}
57
89
  id={id}
58
90
  value={arrayValue}
59
91
  onValueChange={handleValueChange}
60
92
  className={cx(
61
- "flex relative items-center select-none touch-none h-[27px]",
93
+ "flex relative items-center select-none touch-none h-[27px] rounded overflow-hidden w-full",
62
94
  className
63
95
  )}
64
96
  style={style}
65
97
  onFocus={onFocus}
66
98
  onBlur={onBlur}
99
+ disabled={readOnly}
67
100
  >
68
- <RadixSlider.Track className="bg-divider relative flex-grow h-full rounded overflow-hidden">
101
+ <RadixSlider.Track className="relative flex-grow h-full rounded overflow-hidden bg-input-background">
69
102
  {label && (
70
103
  <label
71
104
  htmlFor={id}
@@ -74,9 +107,10 @@ export const Slider = function Slider({
74
107
  {label}
75
108
  </label>
76
109
  )}
77
- <RadixSlider.Range
110
+ <div
111
+ style={trackFillStyle}
78
112
  className={cx(
79
- "absolute h-full rounded",
113
+ "absolute inset-0 w-full h-full rounded overflow-hidden",
80
114
  colorScheme === undefined && "bg-slider-border",
81
115
  colorScheme === "primary" && "bg-primary",
82
116
  colorScheme === "secondary" && "bg-secondary"
@@ -84,8 +118,9 @@ export const Slider = function Slider({
84
118
  />
85
119
  </RadixSlider.Track>
86
120
  <RadixSlider.Thumb
121
+ style={thumbStyle}
87
122
  className={cx(
88
- "block w-2 h-[27px] rounded border border-solid bg-slider-background focus:outline-none cursor-ew-resize",
123
+ "block h-[27px] rounded border border-solid bg-slider-thumb-background transition-colors focus:border-primary focus:border-2 outline-none",
89
124
  colorScheme === undefined && "border-slider-border",
90
125
  colorScheme === "primary" && "border-primary",
91
126
  colorScheme === "secondary" && "border-secondary"
@@ -98,11 +133,11 @@ export const Slider = function Slider({
98
133
  labelStyles,
99
134
  "absolute top-0 bottom-0 left-2 right-2 text-white overflow-hidden flex justify-end"
100
135
  )}
101
- style={{ clipPath: `inset(0 ${100 - parseFloat(percentage)}% 0 0)` }}
136
+ style={labelStyle}
102
137
  >
103
138
  {label}
104
139
  </label>
105
140
  )}
106
141
  </RadixSlider.Root>
107
142
  );
108
- };
143
+ });
@@ -16,17 +16,7 @@ import {
16
16
  verticalListSortingStrategy,
17
17
  } from "@dnd-kit/sortable";
18
18
  import { memoGeneric } from "@noya-app/react-utils";
19
- import React, {
20
- createContext,
21
- ReactNode,
22
- Ref,
23
- useCallback,
24
- useContext,
25
- useEffect,
26
- useMemo,
27
- useRef,
28
- useState,
29
- } from "react";
19
+ import * as React from "react";
30
20
  import { createPortal } from "react-dom";
31
21
 
32
22
  export type RelativeDropPosition = "above" | "below" | "inside";
@@ -60,7 +50,7 @@ const defaultAcceptsDrop: DropValidator = (
60
50
  return true;
61
51
  };
62
52
 
63
- const SortableItemContext = createContext<{
53
+ const SortableItemContext = React.createContext<{
64
54
  keys: string[];
65
55
  position: Translate;
66
56
  acceptsDrop: DropValidator;
@@ -119,7 +109,7 @@ interface ItemProps<T> {
119
109
  disabled?: boolean;
120
110
  children: (
121
111
  props: {
122
- ref: Ref<T>;
112
+ ref: React.Ref<T>;
123
113
  relativeDropPosition?: RelativeDropPosition;
124
114
  [key: string]: any;
125
115
  } & UseSortableReturnType["attributes"]
@@ -132,7 +122,7 @@ function SortableItem<T extends HTMLElement>({
132
122
  children,
133
123
  }: ItemProps<T>) {
134
124
  const { keys, position, acceptsDrop, setActivatorEvent, axis } =
135
- useContext(SortableItemContext);
125
+ React.useContext(SortableItemContext);
136
126
  const sortable = useSortable({ id, disabled });
137
127
 
138
128
  const {
@@ -156,7 +146,7 @@ function SortableItem<T extends HTMLElement>({
156
146
  const offsetLeft = eventX + position.x;
157
147
  const offsetTop = eventY + position.y;
158
148
 
159
- const ref = useCallback((node: T) => setNodeRef(node), [setNodeRef]);
149
+ const ref = React.useCallback((node: T) => setNodeRef(node), [setNodeRef]);
160
150
 
161
151
  return children({
162
152
  ref,
@@ -183,8 +173,8 @@ function SortableItem<T extends HTMLElement>({
183
173
 
184
174
  interface RootProps {
185
175
  keys: string[];
186
- children: ReactNode;
187
- renderOverlay?: (index: number) => ReactNode;
176
+ children: React.ReactNode;
177
+ renderOverlay?: (index: number) => React.ReactNode;
188
178
  onMoveItem?: (
189
179
  sourceIndex: number,
190
180
  destinationIndex: number,
@@ -210,27 +200,27 @@ function SortableRoot({
210
200
  })
211
201
  );
212
202
 
213
- const [activeIndex, setActiveIndex] = useState<number | undefined>();
214
- const activatorEvent = useRef<PointerEvent | null>(null);
203
+ const [activeIndex, setActiveIndex] = React.useState<number | undefined>();
204
+ const activatorEvent = React.useRef<PointerEvent | null>(null);
215
205
 
216
- const setActivatorEvent = useCallback((event: PointerEvent) => {
206
+ const setActivatorEvent = React.useCallback((event: PointerEvent) => {
217
207
  activatorEvent.current = event;
218
208
  }, []);
219
209
 
220
- const [position, setPosition] = useState<Translate>({ x: 0, y: 0 });
210
+ const [position, setPosition] = React.useState<Translate>({ x: 0, y: 0 });
221
211
 
222
- const handleDragStart = useCallback(
212
+ const handleDragStart = React.useCallback(
223
213
  (event: DragStartEvent) => {
224
214
  setActiveIndex(keys.indexOf(event.active.id));
225
215
  },
226
216
  [keys]
227
217
  );
228
218
 
229
- const handleDragMove = useCallback((event: DragMoveEvent) => {
219
+ const handleDragMove = React.useCallback((event: DragMoveEvent) => {
230
220
  setPosition({ ...event.delta });
231
221
  }, []);
232
222
 
233
- const handleDragEnd = useCallback(
223
+ const handleDragEnd = React.useCallback(
234
224
  (event: DragEndEvent) => {
235
225
  const { active, over } = event;
236
226
 
@@ -263,15 +253,15 @@ function SortableRoot({
263
253
  [acceptsDrop, axis, keys, onMoveItem, position.x, position.y]
264
254
  );
265
255
 
266
- const [mounted, setMounted] = useState(false);
256
+ const [mounted, setMounted] = React.useState(false);
267
257
 
268
- useEffect(() => {
258
+ React.useEffect(() => {
269
259
  setMounted(true);
270
260
  }, []);
271
261
 
272
262
  return (
273
263
  <SortableItemContext.Provider
274
- value={useMemo(
264
+ value={React.useMemo(
275
265
  () => ({
276
266
  keys,
277
267
  acceptsDrop,
@@ -1,5 +1,6 @@
1
1
  /* eslint-disable react/jsx-pascal-case */
2
- import React, { ForwardedRef, forwardRef } from "react";
2
+ import type { ForwardedRef } from "react";
3
+ import * as React from "react";
3
4
 
4
5
  interface Props {
5
6
  size?: number | string;
@@ -10,12 +11,21 @@ interface Props {
10
11
  * Vertical
11
12
  * ------------------------------------------------------------------------- */
12
13
 
13
- const SpacerVertical = forwardRef<HTMLSpanElement, Props>(
14
+ const SpacerVertical = React.forwardRef<HTMLSpanElement, Props>(
14
15
  ({ size, inline, ...props }, ref: ForwardedRef<HTMLSpanElement>) => {
15
16
  return (
16
- <span className={`${inline ? "inline-block" : "block"} ${size === undefined ? "flex flex-1" : ""}`} style={size ? {
17
- minHeight: `${size}px`,
18
- } : undefined} {...props} ref={ref} />
17
+ <span
18
+ className={`${inline ? "inline-block" : "block"} ${size === undefined ? "flex flex-1" : ""}`}
19
+ style={
20
+ size
21
+ ? {
22
+ minHeight: `${size}px`,
23
+ }
24
+ : undefined
25
+ }
26
+ {...props}
27
+ ref={ref}
28
+ />
19
29
  );
20
30
  }
21
31
  );
@@ -24,12 +34,21 @@ const SpacerVertical = forwardRef<HTMLSpanElement, Props>(
24
34
  * Horizontal
25
35
  * ------------------------------------------------------------------------- */
26
36
 
27
- const SpacerHorizontal = forwardRef<HTMLSpanElement, Props>(
37
+ const SpacerHorizontal = React.forwardRef<HTMLSpanElement, Props>(
28
38
  ({ size, inline, ...props }, ref: ForwardedRef<HTMLSpanElement>) => {
29
39
  return (
30
- <span className={`${inline ? "inline-block" : "block"} ${size === undefined ? "flex flex-1" : ""}`} style={size ? {
31
- minWidth: `${size}px`,
32
- } : undefined} {...props} ref={ref} />
40
+ <span
41
+ className={`${inline ? "inline-block" : "block"} ${size === undefined ? "flex flex-1" : ""}`}
42
+ style={
43
+ size
44
+ ? {
45
+ minWidth: `${size}px`,
46
+ }
47
+ : undefined
48
+ }
49
+ {...props}
50
+ ref={ref}
51
+ />
33
52
  );
34
53
  }
35
54
  );
@@ -1,8 +1,8 @@
1
1
  import * as SwitchPrimitive from "@radix-ui/react-switch";
2
- import React, { CSSProperties, FocusEventHandler } from "react";
2
+ import * as React from "react";
3
3
  import { cx } from "../utils/classNames";
4
4
 
5
- type SwitchColorScheme = "normal" | "primary" | "secondary";
5
+ type SwitchColorScheme = "primary" | "secondary";
6
6
 
7
7
  interface Props {
8
8
  id?: string;
@@ -11,17 +11,17 @@ interface Props {
11
11
  /** @default normal */
12
12
  colorScheme?: SwitchColorScheme;
13
13
  disabled?: boolean;
14
- onFocus?: FocusEventHandler;
15
- onBlur?: FocusEventHandler;
14
+ onFocus?: React.FocusEventHandler;
15
+ onBlur?: React.FocusEventHandler;
16
16
  className?: string;
17
- style?: CSSProperties;
17
+ style?: React.CSSProperties;
18
18
  }
19
19
 
20
20
  export const Switch = function Switch({
21
21
  id,
22
22
  value,
23
23
  onChange,
24
- colorScheme = "normal",
24
+ colorScheme = "primary",
25
25
  disabled,
26
26
  onFocus,
27
27
  onBlur,
@@ -38,7 +38,7 @@ export const Switch = function Switch({
38
38
  onChange(newValue);
39
39
  }}
40
40
  className={cx(
41
- "all-unset w-8 h-[19px] bg-active-background rounded-full relative cursor-pointer [webkit-tap-highlight-color:rgba(0,0,0,0)] focus:ring-2 focus:ring-primary focus:ring-offset-[1px] outline-none data-[state=checked]:bg-primary",
41
+ "all-unset w-8 h-[19px] bg-active-background rounded-full relative cursor-pointer [webkit-tap-highlight-color:rgba(0,0,0,0)] focus:ring-2 focus:ring-primary focus:ring-offset-[1px] outline-none data-[state=checked]:bg-primary transition-all",
42
42
  colorScheme === "secondary" && "data-[state=checked]:bg-secondary",
43
43
  "focus:z-interactable",
44
44
  className
@@ -46,7 +46,7 @@ export const Switch = function Switch({
46
46
  onFocus={onFocus}
47
47
  onBlur={onBlur}
48
48
  >
49
- <SwitchPrimitive.Thumb className="block w-[15px] h-[15px] bg-white rounded-full transition-transform translate-x-[2px] data-[state=checked]:translate-x-[15px]" />
49
+ <SwitchPrimitive.Thumb className="block w-[15px] h-[15px] bg-background rounded-full transition-transform translate-x-[2px] data-[state=checked]:translate-x-[15px]" />
50
50
  </SwitchPrimitive.Root>
51
51
  );
52
52
  };
@@ -21,19 +21,26 @@ export const AutoResizingTextArea = memo(
21
21
  value,
22
22
  onChangeText,
23
23
  className,
24
+ end,
25
+ endClassName,
26
+ onChange,
24
27
  ...rest
25
28
  }: {
26
29
  onChangeText: (value: string) => void;
27
30
  value?: string;
31
+ end?: React.ReactNode;
32
+ endClassName?: string;
28
33
  } & React.TextareaHTMLAttributes<HTMLTextAreaElement>,
29
34
  forwardedRef: React.ForwardedRef<HTMLTextAreaElement>
30
35
  ) {
31
36
  const ref = useAutoResize(value || rest.placeholder || "");
32
37
 
33
38
  const handleChange = useCallback(
34
- (event: React.ChangeEvent<HTMLTextAreaElement>) =>
35
- onChangeText(event.target.value),
36
- [onChangeText]
39
+ (event: React.ChangeEvent<HTMLTextAreaElement>) => {
40
+ onChangeText(event.target.value);
41
+ onChange?.(event);
42
+ },
43
+ [onChangeText, onChange]
37
44
  );
38
45
 
39
46
  const handleRef = useCallback(
@@ -45,16 +52,19 @@ export const AutoResizingTextArea = memo(
45
52
  );
46
53
 
47
54
  return (
48
- <textarea
49
- className={cx(
50
- `font-sans text-heading5 font-normal text-text bg-input-background w-0 flex-1 py-1 px-1.5 border-none outline-none h-[100px] rounded-4 resize-none placeholder:text-text-disabled focus:ring-2 focus:ring-primary read-only:text-text-disabled focus:z-interactable `,
51
- className
52
- )}
53
- ref={handleRef}
54
- {...rest}
55
- onChange={handleChange}
56
- value={value}
57
- />
55
+ <div className="relative w-full h-full">
56
+ <textarea
57
+ className={cx(
58
+ `font-sans text-heading5 font-normal text-text bg-input-background flex-1 py-1 px-1.5 border-none outline-none min-h-24 w-full rounded resize-none placeholder:text-text-disabled focus:ring-2 focus:ring-primary read-only:text-text-disabled focus:z-interactable transition-all`,
59
+ className
60
+ )}
61
+ ref={handleRef}
62
+ {...rest}
63
+ onChange={handleChange}
64
+ value={value}
65
+ />
66
+ <div className={cx("absolute right-1 top-4", endClassName)}>{end}</div>
67
+ </div>
58
68
  );
59
69
  })
60
70
  );