@rebasepro/ui 0.17.3 → 0.18.1

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 (127) hide show
  1. package/README.md +4 -0
  2. package/dist/components/MultiSelect.d.ts +10 -0
  3. package/dist/index.es.js +2 -1
  4. package/dist/index.es.js.map +1 -1
  5. package/package.json +32 -23
  6. package/src/components/Alert.tsx +0 -118
  7. package/src/components/Autocomplete.tsx +0 -93
  8. package/src/components/Avatar.tsx +0 -73
  9. package/src/components/Badge.tsx +0 -44
  10. package/src/components/BooleanSwitch.tsx +0 -116
  11. package/src/components/BooleanSwitchWithLabel.tsx +0 -120
  12. package/src/components/Button.tsx +0 -110
  13. package/src/components/Card.tsx +0 -43
  14. package/src/components/CenteredView.tsx +0 -35
  15. package/src/components/Checkbox.tsx +0 -129
  16. package/src/components/Chip.tsx +0 -131
  17. package/src/components/CircularProgress.tsx +0 -53
  18. package/src/components/CircularProgressCenter.tsx +0 -27
  19. package/src/components/Collapse.tsx +0 -66
  20. package/src/components/ColorPicker.tsx +0 -140
  21. package/src/components/Container.tsx +0 -51
  22. package/src/components/DateTimeField.tsx +0 -363
  23. package/src/components/DebouncedTextField.tsx +0 -76
  24. package/src/components/Dialog.tsx +0 -159
  25. package/src/components/DialogActions.tsx +0 -29
  26. package/src/components/DialogContent.tsx +0 -27
  27. package/src/components/DialogTitle.tsx +0 -41
  28. package/src/components/ErrorBoundary.tsx +0 -236
  29. package/src/components/ExpandablePanel.tsx +0 -130
  30. package/src/components/FileUpload.tsx +0 -103
  31. package/src/components/FilterChip.tsx +0 -89
  32. package/src/components/IconButton.tsx +0 -101
  33. package/src/components/InfoLabel.tsx +0 -23
  34. package/src/components/InputLabel.tsx +0 -40
  35. package/src/components/Label.tsx +0 -32
  36. package/src/components/LoadingButton.tsx +0 -32
  37. package/src/components/Markdown.tsx +0 -55
  38. package/src/components/Menu.tsx +0 -114
  39. package/src/components/Menubar.tsx +0 -355
  40. package/src/components/MultiSelect.tsx +0 -433
  41. package/src/components/Paper.tsx +0 -23
  42. package/src/components/Popover.tsx +0 -162
  43. package/src/components/RadioGroup.tsx +0 -74
  44. package/src/components/ResizablePanels.tsx +0 -189
  45. package/src/components/SearchBar.tsx +0 -136
  46. package/src/components/Select.tsx +0 -326
  47. package/src/components/Separator.tsx +0 -28
  48. package/src/components/Sheet.tsx +0 -135
  49. package/src/components/Skeleton.tsx +0 -54
  50. package/src/components/Slider.tsx +0 -110
  51. package/src/components/Table.tsx +0 -178
  52. package/src/components/Tabs.tsx +0 -185
  53. package/src/components/TextField.tsx +0 -270
  54. package/src/components/TextareaAutosize.tsx +0 -368
  55. package/src/components/ToggleButtonGroup.tsx +0 -69
  56. package/src/components/Tooltip.tsx +0 -153
  57. package/src/components/Typography.tsx +0 -132
  58. package/src/components/VirtualTable/VirtualTable.performance.test.tsx +0 -90
  59. package/src/components/VirtualTable/VirtualTable.tsx +0 -700
  60. package/src/components/VirtualTable/VirtualTableCell.tsx +0 -59
  61. package/src/components/VirtualTable/VirtualTableHeader.tsx +0 -327
  62. package/src/components/VirtualTable/VirtualTableHeaderRow.tsx +0 -250
  63. package/src/components/VirtualTable/VirtualTableProps.tsx +0 -339
  64. package/src/components/VirtualTable/VirtualTableRow.tsx +0 -51
  65. package/src/components/VirtualTable/fields/VirtualTableDateField.tsx +0 -44
  66. package/src/components/VirtualTable/fields/VirtualTableInput.tsx +0 -89
  67. package/src/components/VirtualTable/fields/VirtualTableNumberInput.tsx +0 -71
  68. package/src/components/VirtualTable/fields/VirtualTableSelect.tsx +0 -107
  69. package/src/components/VirtualTable/fields/VirtualTableSwitch.tsx +0 -28
  70. package/src/components/VirtualTable/index.tsx +0 -10
  71. package/src/components/VirtualTable/selection/SelectionContext.tsx +0 -36
  72. package/src/components/VirtualTable/selection/SelectionStore.ts +0 -54
  73. package/src/components/VirtualTable/types.tsx +0 -47
  74. package/src/components/common/SelectInputLabel.tsx +0 -23
  75. package/src/components/index.tsx +0 -62
  76. package/src/hooks/PortalContainerContext.tsx +0 -50
  77. package/src/hooks/index.ts +0 -7
  78. package/src/hooks/useDebounceCallback.tsx +0 -21
  79. package/src/hooks/useDebounceValue.tsx +0 -20
  80. package/src/hooks/useDebouncedCallback.ts +0 -39
  81. package/src/hooks/useInjectStyles.tsx +0 -31
  82. package/src/hooks/useOutsideAlerter.tsx +0 -46
  83. package/src/icons/GitHubIcon.tsx +0 -21
  84. package/src/icons/HandleIcon.tsx +0 -23
  85. package/src/icons/Icon.tsx +0 -33
  86. package/src/icons/LucideIconByName.tsx +0 -117
  87. package/src/icons/cool_icon_keys.ts +0 -12
  88. package/src/icons/icon_keys.ts +0 -1936
  89. package/src/icons/index.ts +0 -179
  90. package/src/index.css +0 -169
  91. package/src/index.ts +0 -47
  92. package/src/scripts/fixIconSizes.cjs +0 -92
  93. package/src/scripts/fixIconSizesToConst.cjs +0 -106
  94. package/src/scripts/generateIconKeys.ts +0 -41
  95. package/src/scripts/generateIcons.ts +0 -4
  96. package/src/scripts/saveIconFiles.ts +0 -89
  97. package/src/styles.ts +0 -141
  98. package/src/theme.css +0 -168
  99. package/src/util/chip_colors.ts +0 -242
  100. package/src/util/cls.ts +0 -8
  101. package/src/util/debounce.ts +0 -28
  102. package/src/util/hash.ts +0 -11
  103. package/src/util/index.ts +0 -4
  104. package/src/util/key_to_icon_component.ts +0 -11
  105. package/src/util/lazy_chunk.ts +0 -101
  106. package/src/views/CardView.tsx +0 -281
  107. package/src/views/CollectionView/CollectionCardView.tsx +0 -265
  108. package/src/views/CollectionView/CollectionKanbanView.tsx +0 -246
  109. package/src/views/CollectionView/CollectionListView.tsx +0 -240
  110. package/src/views/CollectionView/CollectionTableView.tsx +0 -229
  111. package/src/views/CollectionView/CollectionView.tsx +0 -450
  112. package/src/views/CollectionView/CollectionViewToolbar.tsx +0 -242
  113. package/src/views/CollectionView/CollectionViewTypes.ts +0 -181
  114. package/src/views/CollectionView/DefaultCellRenderer.tsx +0 -404
  115. package/src/views/CollectionView/index.ts +0 -39
  116. package/src/views/CollectionView/resolveTitleProperty.ts +0 -52
  117. package/src/views/CollectionView/utils.ts +0 -14
  118. package/src/views/Kanban/Board.tsx +0 -384
  119. package/src/views/Kanban/BoardColumn.tsx +0 -138
  120. package/src/views/Kanban/BoardColumnTitle.tsx +0 -47
  121. package/src/views/Kanban/BoardSortableList.tsx +0 -182
  122. package/src/views/Kanban/board_types.ts +0 -80
  123. package/src/views/Kanban/index.tsx +0 -5
  124. package/src/views/Kanban/placement.ts +0 -62
  125. package/src/views/KanbanView.tsx +0 -32
  126. package/src/views/ListView.tsx +0 -296
  127. package/src/views/index.ts +0 -5
@@ -1,270 +0,0 @@
1
- "use client";
2
- import React, { ForwardedRef, forwardRef, useEffect, useId, useRef } from "react";
3
-
4
- import {
5
- fieldBackgroundDisabledMixin,
6
- fieldBackgroundHoverMixin,
7
- fieldBackgroundInvisibleMixin,
8
- fieldBackgroundMixin,
9
- focusedInvisibleMixin
10
- } from "../styles";
11
- import { InputLabel } from "./InputLabel";
12
- import { cls } from "../util";
13
-
14
- export type InputType =
15
- | "text"
16
- | "number"
17
- | "phone"
18
- | "email"
19
- | "password"
20
- | "search"
21
- | "url"
22
- | "date"
23
- | "time"
24
- | "datetime-local"
25
- | "month"
26
- | "week"
27
- | "color";
28
-
29
- export type TextFieldProps<T extends string | number> = {
30
- type?: InputType;
31
- value?: T;
32
- onChange?: (event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => void;
33
- label?: React.ReactNode;
34
- multiline?: boolean;
35
- disabled?: boolean;
36
- invisible?: boolean;
37
- error?: boolean;
38
- endAdornment?: React.ReactNode;
39
- autoFocus?: boolean;
40
- placeholder?: string;
41
- size?: "smallest" | "small" | "medium" | "large";
42
- className?: string;
43
- style?: React.CSSProperties;
44
- inputClassName?: string;
45
- inputStyle?: React.CSSProperties;
46
- inputRef?: React.ForwardedRef<any>;
47
- /**
48
- * Maximum number of rows to display.
49
- */
50
- maxRows?: number | string;
51
- /**
52
- * Minimum number of rows to display.
53
- * @default 1
54
- */
55
- minRows?: number | string;
56
- } & Omit<React.InputHTMLAttributes<HTMLInputElement>, "size" | "value">;
57
-
58
- export const TextField = forwardRef<HTMLDivElement, TextFieldProps<string | number>>(
59
- <T extends string | number>(
60
- {
61
- value,
62
- onChange,
63
- label,
64
- type = "text",
65
- multiline = false,
66
- invisible,
67
- maxRows,
68
- minRows,
69
- disabled,
70
- error,
71
- endAdornment,
72
- autoFocus,
73
- placeholder,
74
- size = "large",
75
- className,
76
- style,
77
- inputClassName,
78
- inputStyle,
79
- inputRef: inputRefProp,
80
- ...inputProps
81
- }: TextFieldProps<T>,
82
- ref: ForwardedRef<HTMLDivElement>
83
- ) => {
84
-
85
- const fallbackRef = useRef(null);
86
- const inputRef = inputRefProp ?? fallbackRef;
87
- const autoId = useId();
88
- const inputId = inputProps.id ?? autoId;
89
- const labelId = `${inputId}-label`;
90
-
91
- const [focused, setFocused] = React.useState(false);
92
- const hasValue = value !== undefined && value !== null && value !== "";
93
-
94
- useEffect(() => {
95
- const element = inputRef && "current" in inputRef ? inputRef.current : null;
96
- if (element && document.activeElement === element) {
97
- setFocused(true);
98
- }
99
- }, []);
100
-
101
- useEffect(() => {
102
- if (type !== "number") return;
103
- const handleWheel = (event: any) => {
104
- if (event.target instanceof HTMLElement) event.target.blur();
105
- };
106
-
107
- const element = "current" in inputRef ? inputRef.current : inputRef;
108
-
109
- element?.addEventListener("wheel", handleWheel);
110
-
111
- return () => {
112
- element?.removeEventListener("wheel", handleWheel);
113
- };
114
- }, [inputRef, type]);
115
-
116
- const input = multiline ? (
117
- <textarea
118
- {...(inputProps as React.TextareaHTMLAttributes<HTMLTextAreaElement>)}
119
- ref={inputRef}
120
- id={inputId}
121
- // Falls back to the caller's own value rather than to `undefined`:
122
- // this sits after the spread, so hardcoding `undefined` silently
123
- // erased an `aria-labelledby` a consumer had passed, and the
124
- // unlabelled field it was meant to name stayed unnamed.
125
- aria-labelledby={label ? labelId : inputProps["aria-labelledby"]}
126
- aria-invalid={error || undefined}
127
- aria-disabled={disabled || undefined}
128
- placeholder={focused || hasValue || !label ? placeholder : undefined}
129
- autoFocus={autoFocus}
130
- rows={typeof minRows === "string" ? parseInt(minRows) : (minRows ?? 3)}
131
- value={value ?? ""}
132
- onChange={onChange}
133
- onFocus={() => setFocused(true)}
134
- onBlur={() => setFocused(false)}
135
- style={inputStyle}
136
- className={cls(
137
- invisible ? focusedInvisibleMixin : "",
138
- "rounded-lg resize-none w-full outline-none text-base bg-transparent min-h-[64px] px-3",
139
- label ? "pt-8 pb-2" : "py-2",
140
- disabled && "outline-none opacity-50 text-surface-accent-600 dark:text-surface-accent-500",
141
- inputClassName
142
- )}
143
- />
144
- ) : (
145
- <input
146
- {...inputProps}
147
- ref={inputRef}
148
- id={inputId}
149
- // See the textarea branch: preserve a caller-supplied
150
- // `aria-labelledby` instead of overwriting it with `undefined`.
151
- aria-labelledby={label ? labelId : inputProps["aria-labelledby"]}
152
- aria-invalid={error || undefined}
153
- aria-disabled={disabled || undefined}
154
- disabled={disabled}
155
- style={inputStyle}
156
- className={cls(
157
- "w-full outline-none bg-transparent leading-normal px-3",
158
- "rounded-lg",
159
- "focused:text-text-primary focused:dark:text-text-primary-dark",
160
- invisible ? focusedInvisibleMixin : "",
161
- disabled ? fieldBackgroundDisabledMixin : fieldBackgroundHoverMixin,
162
- {
163
- "min-h-[28px]": size === "smallest",
164
- "min-h-[32px]": size === "small",
165
- "min-h-[40px]": size === "medium",
166
- "min-h-[48px]": size === "large"
167
- },
168
- /* A shrunk label occupies roughly the first 16px of the
169
- box (`top-[-1px]`, translated down 2px, ~15px tall at
170
- scale-75). `pt-4` put the value's first line at exactly
171
- 16px, so label and content shared a row: a labelled
172
- `small` field with a placeholder rendered "Name" on top
173
- of "e.g. Analytics Pipeline". `large` was never affected
174
- — it reserves `pt-8`. */
175
- label
176
- // Labelled fields sit exactly 16px above the unlabelled
177
- // scale (44/48/56/64 against 28/32/40/48). smallest,
178
- // small and medium all used to share `pt-5 pb-1.5`, so
179
- // they rendered at an identical 50px and `size` looked
180
- // inert whenever a label was present.
181
- ? { smallest: "pt-4 pb-1", small: "pt-5 pb-1", medium: "pt-6 pb-2", large: "pt-8 pb-2" }[size]
182
- : size === "smallest"
183
- ? "py-0.5"
184
- : size === "small"
185
- ? "py-1"
186
- : "py-2",
187
- endAdornment ? "pr-12" : "pr-3",
188
- disabled &&
189
- "outline-none opacity-65 dark:opacity-60 text-surface-accent-800 dark:text-white",
190
- inputClassName
191
- )}
192
- placeholder={focused || hasValue || !label ? placeholder : undefined}
193
- autoFocus={autoFocus}
194
- onFocus={() => setFocused(true)}
195
- onBlur={() => setFocused(false)}
196
- type={type}
197
- value={type === "number" && Number.isNaN(value) ? "" : value ?? ""}
198
- onChange={onChange}
199
- />
200
- );
201
-
202
- return (
203
- <div
204
- ref={ref}
205
- className={cls(
206
- "rounded-lg relative max-w-full",
207
- invisible ? fieldBackgroundInvisibleMixin : fieldBackgroundMixin,
208
- disabled ? fieldBackgroundDisabledMixin : fieldBackgroundHoverMixin,
209
- error ? "border border-red-500 dark:border-red-600" : "",
210
- {
211
- "min-h-[28px]": size === "smallest",
212
- "min-h-[32px]": size === "small",
213
- "min-h-[40px]": size === "medium",
214
- "min-h-[48px]": size === "large"
215
- },
216
- className
217
- )}
218
- style={style}
219
- >
220
- {label && (
221
- <InputLabel
222
- id={labelId}
223
- htmlFor={inputId}
224
- className={cls(
225
- "pointer-events-none absolute",
226
- size === "large" ? "top-1" : "top-[-1px]",
227
- !error
228
- ? focused
229
- ? "text-primary dark:text-primary"
230
- : "text-text-secondary dark:text-text-secondary-dark"
231
- : "text-red-500 dark:text-red-600",
232
- // A disabled field's LABEL is the one part that must
233
- // stay readable — it is what tells you which field
234
- // you cannot edit. `opacity-50` halved the alpha of
235
- // an already-muted #737373, landing near 2.2:1 over
236
- // the field fill; the label was effectively gone.
237
- //
238
- // The value below it keeps its own `opacity-50`,
239
- // which is the correct signal: the CONTENT is
240
- // inactive. Naming the field is not content.
241
- disabled ? "text-text-disabled dark:text-text-disabled-dark" : ""
242
- )}
243
- shrink={hasValue || focused}
244
- >
245
- {label}
246
- </InputLabel>
247
- )}
248
-
249
- {input}
250
-
251
- {endAdornment && (
252
- <div
253
- className={cls(
254
- "flex flex-row justify-center items-center absolute h-full right-0 top-0",
255
- {
256
- "mr-4": size === "large",
257
- "mr-3": size === "medium",
258
- "mr-2": size === "small" || size === "smallest"
259
- }
260
- )}
261
- >
262
- {endAdornment}
263
- </div>
264
- )}
265
- </div>
266
- );
267
- }
268
- );
269
-
270
- TextField.displayName = "TextField";
@@ -1,368 +0,0 @@
1
- "use client";
2
- import * as React from "react";
3
- import { useLayoutEffect } from "react";
4
- import * as ReactDOM from "react-dom";
5
- import { cls, debounce } from "../util";
6
-
7
- type State = {
8
- outerHeightStyle: number;
9
- overflow?: boolean | undefined;
10
- };
11
-
12
- function getStyleValue(value: string) {
13
- return parseInt(value, 10) || 0;
14
- }
15
-
16
- const styles: {
17
- shadow: React.CSSProperties;
18
- } = {
19
- shadow: {
20
- // Visibility needed to hide the extra text area on iPads
21
- visibility: "hidden",
22
- // Remove from the content flow
23
- position: "absolute",
24
- // Ignore the scrollbar width
25
- overflow: "hidden",
26
- height: 0,
27
- top: 0,
28
- left: 0,
29
- // Create a new layer, increase the isolation of the computed values
30
- transform: "translateZ(0)"
31
- }
32
- };
33
-
34
- function isEmpty(obj: State) {
35
- return (
36
- obj === undefined ||
37
- obj === null ||
38
- Object.keys(obj).length === 0 ||
39
- (obj.outerHeightStyle === 0 && !obj.overflow)
40
- );
41
- }
42
-
43
- export const TextareaAutosize = React.forwardRef(function TextareaAutosize(
44
- props: TextareaAutosizeProps,
45
- ref: React.ForwardedRef<Element>
46
- ) {
47
- const {
48
- onChange,
49
- onScroll,
50
- onResize,
51
- maxRows,
52
- minRows = 1,
53
- style,
54
- value,
55
- onFocus,
56
- onBlur,
57
- sizeRef,
58
- ignoreBoxSizing,
59
- ...other
60
- } = props;
61
-
62
- const { current: isControlled } = React.useRef(value != null);
63
- const inputRef = React.useRef<HTMLInputElement>(null);
64
- const handleRef = useForkRef(ref, inputRef);
65
- const shadowRef = React.useRef<HTMLTextAreaElement>(null);
66
- const renders = React.useRef(0);
67
- const [state, setState] = React.useState<State>({
68
- outerHeightStyle: 0
69
- });
70
-
71
- const getUpdatedState = React.useCallback(() => {
72
-
73
- const input = inputRef.current!;
74
- if (typeof window === "undefined") {
75
- return {
76
- outerHeightStyle: 0
77
- };
78
- }
79
-
80
- const containerWindow = window;
81
- const computedStyle = containerWindow.getComputedStyle(input);
82
-
83
- // If input's width is shrunk and it's not visible, don't sync height.
84
- if (computedStyle.width === "0px") {
85
- return {
86
- outerHeightStyle: 0
87
- };
88
- }
89
-
90
- const sizeReferenceElement = sizeRef?.current ?? shadowRef.current!;
91
- const inputShallow = shadowRef.current!;
92
-
93
- sizeReferenceElement.style.width = computedStyle.width;
94
- inputShallow.value = input.value || props.placeholder || "x";
95
- if (inputShallow.value.slice(-1) === "\n") {
96
- // Certain fonts which overflow the line height will cause the textarea
97
- // to report a different scrollHeight depending on whether the last line
98
- // is empty. Make it non-empty to avoid this issue.
99
- inputShallow.value += " ";
100
- }
101
-
102
- const boxSizing = computedStyle.boxSizing;
103
- const padding =
104
- getStyleValue(computedStyle.paddingBottom) + getStyleValue(computedStyle.paddingTop);
105
- const border =
106
- getStyleValue(computedStyle.borderBottomWidth) + getStyleValue(computedStyle.borderTopWidth);
107
- const minHeight = getStyleValue(computedStyle.minHeight);
108
-
109
- // The height of the inner content
110
- const innerHeight = sizeReferenceElement.scrollHeight;
111
-
112
- // Measure height of a textarea with a single row
113
- inputShallow.value = "x";
114
- const singleRowHeight = sizeReferenceElement.scrollHeight;
115
-
116
- // The height of the outer content
117
- let outerHeight = innerHeight;
118
-
119
- if (minRows) {
120
- outerHeight = Math.max(Number(minRows) * singleRowHeight, outerHeight);
121
- }
122
- if (maxRows) {
123
- outerHeight = Math.min(Number(maxRows) * singleRowHeight, outerHeight);
124
- }
125
- outerHeight = Math.max(outerHeight, singleRowHeight, minHeight);
126
-
127
- // Take the box sizing into account for applying this value as a style.
128
- const outerHeightStyle = outerHeight + (!ignoreBoxSizing && boxSizing === "border-box" ? padding + border : 0);
129
-
130
- const overflow = Math.abs(outerHeight - innerHeight) <= 1;
131
-
132
- return {
133
- outerHeightStyle,
134
- overflow
135
- };
136
- }, [maxRows, minRows, props.placeholder]);
137
-
138
- const updateState = React.useCallback((prevState: State, newState: State) => {
139
- const {
140
- outerHeightStyle,
141
- overflow
142
- } = newState;
143
- // Need a large enough difference to update the height.
144
- // This prevents infinite rendering loop.
145
- if (
146
- renders.current < 20 &&
147
- ((outerHeightStyle > 0 &&
148
- Math.abs((prevState.outerHeightStyle || 0) - outerHeightStyle) > 1) ||
149
- prevState.overflow !== overflow)
150
- ) {
151
- renders.current += 1;
152
- return {
153
- overflow,
154
- outerHeightStyle
155
- };
156
- }
157
- if (process.env.NODE_ENV !== "production") {
158
- if (renders.current === 20) {
159
- console.error(
160
- [
161
- "MUI: Too many re-renders. The layout is unstable.",
162
- "TextareaAutosize limits the number of renders to prevent an infinite loop."
163
- ].join("\n")
164
- );
165
- }
166
- }
167
- return prevState;
168
- }, []);
169
-
170
- const syncHeight = React.useCallback(() => {
171
- const newState = getUpdatedState();
172
-
173
- if (isEmpty(newState)) {
174
- return;
175
- }
176
- if (onResize) {
177
- onResize(newState);
178
- }
179
-
180
- setState((prevState) => {
181
- return updateState(prevState, newState);
182
- });
183
- }, [getUpdatedState, onResize, updateState]);
184
-
185
- const syncHeightWithFlushSync = React.useCallback(() => {
186
- const newState = getUpdatedState();
187
-
188
- if (isEmpty(newState)) {
189
- return;
190
- }
191
-
192
- // In React 18, state updates in a ResizeObserver's callback are happening after the paint which causes flickering
193
- // when doing some visual updates in it. Using flushSync ensures that the dom will be painted after the states updates happen
194
- // Related issue - https://github.com/facebook/react/issues/24331
195
- ReactDOM.flushSync(() => {
196
- setState((prevState) => {
197
- return updateState(prevState, newState);
198
- });
199
- });
200
- }, [getUpdatedState, updateState]);
201
-
202
- React.useEffect(() => {
203
- const handleResize = debounce(() => {
204
- renders.current = 0;
205
-
206
- // If the TextareaAutosize component is replaced by Suspense with a fallback, the last
207
- // ResizeObserver's handler that runs because of the change in the layout is trying to
208
- // access a dom node that is no longer there (as the fallback component is being shown instead).
209
- if (inputRef.current) {
210
- syncHeightWithFlushSync();
211
- }
212
- });
213
- let resizeObserver: ResizeObserver;
214
-
215
- const input = inputRef.current!;
216
- const containerWindow = window;
217
- if (typeof window === "undefined") {
218
- return;
219
- }
220
-
221
- containerWindow.addEventListener("resize", handleResize);
222
-
223
- if (typeof ResizeObserver !== "undefined") {
224
- resizeObserver = new ResizeObserver(handleResize);
225
- resizeObserver.observe(input);
226
- }
227
-
228
- return () => {
229
- handleResize.clear();
230
- containerWindow.removeEventListener("resize", handleResize);
231
- if (resizeObserver) {
232
- resizeObserver.disconnect();
233
- }
234
- };
235
- }, [syncHeightWithFlushSync]);
236
-
237
- useLayoutEffect(() => {
238
- syncHeight();
239
- });
240
-
241
- React.useEffect(() => {
242
- renders.current = 0;
243
- }, [value]);
244
-
245
- const handleChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => {
246
- renders.current = 0;
247
-
248
- if (!isControlled) {
249
- syncHeight();
250
- }
251
-
252
- if (onChange) {
253
- onChange(event);
254
- }
255
- };
256
-
257
- return (
258
- <React.Fragment>
259
- <textarea
260
- value={value}
261
- onChange={handleChange}
262
- className={props.className}
263
- ref={handleRef as React.Ref<HTMLTextAreaElement>}
264
- onFocus={onFocus}
265
- onBlur={onBlur}
266
- // Apply the rows prop to get a "correct" first SSR paint
267
- rows={minRows as number}
268
- style={{
269
- height: state.outerHeightStyle,
270
- // Need a large enough difference to allow scrolling.
271
- // This prevents infinite rendering loop.
272
- overflow: state.overflow ? "hidden" : undefined,
273
- ...style
274
- }}
275
- onScroll={onScroll}
276
- {...other}
277
- />
278
- <textarea
279
- aria-hidden
280
- className={cls(props.className, props.shadowClassName)}
281
- readOnly
282
- ref={shadowRef}
283
- tabIndex={-1}
284
- style={{
285
- padding: 0,
286
- ...styles.shadow,
287
- ...style
288
- }}
289
- />
290
- </React.Fragment>
291
- );
292
- }) as React.FC<TextareaAutosizeProps & { ref?: React.ForwardedRef<Element> }>;
293
-
294
- export type TextareaAutosizeProps = Omit<React.InputHTMLAttributes<HTMLTextAreaElement>, "onResize"> & {
295
-
296
- className?: string;
297
-
298
- shadowClassName?: string;
299
-
300
- /**
301
- * Maximum number of rows to display.
302
- */
303
- maxRows?: number | string;
304
- /**
305
- * Minimum number of rows to display.
306
- * @default 1
307
- */
308
- minRows?: number | string;
309
- /**
310
- * @ignore
311
- */
312
- onChange?: (event: React.ChangeEvent<HTMLTextAreaElement>) => void;
313
- /**
314
- * @ignore
315
- */
316
- placeholder?: string;
317
- /**
318
- * @ignore
319
- */
320
- style?: object;
321
- /**
322
- * @ignore
323
- */
324
- value?: string[] | number | string;
325
-
326
- sizeRef?: React.RefObject<HTMLDivElement>;
327
-
328
- onScroll?: (event: React.UIEvent<HTMLTextAreaElement>) => void;
329
-
330
- onResize?: (state: State) => void;
331
-
332
- autoFocus?: boolean;
333
-
334
- ignoreBoxSizing?: boolean;
335
- }
336
-
337
- function useForkRef<Instance>(
338
- ...refs: Array<React.Ref<Instance> | undefined>
339
- ): React.RefCallback<Instance> | null {
340
- /**
341
- * This will create a new function if the refs passed to this hook change and are all defined.
342
- * This means react will call the old forkRef with `null` and the new forkRef
343
- * with the ref. Cleanup naturally emerges from this behavior.
344
- */
345
- return React.useMemo(() => {
346
- if (refs.every((ref) => ref == null)) {
347
- return null;
348
- }
349
-
350
- return (instance) => {
351
- refs.forEach((ref) => {
352
- setRef(ref, instance);
353
- });
354
- };
355
-
356
- }, refs);
357
- }
358
-
359
- function setRef<T>(
360
- ref: React.MutableRefObject<T | null> | ((instance: T | null) => void) | null | undefined,
361
- value: T | null
362
- ): void {
363
- if (typeof ref === "function") {
364
- ref(value);
365
- } else if (ref) {
366
- ref.current = value;
367
- }
368
- }
@@ -1,69 +0,0 @@
1
- import React from "react";
2
- import { cls } from "../util";
3
-
4
- export type ToggleButtonOption<T extends string = string> = {
5
- value: T;
6
- label: string;
7
- icon?: React.ReactNode;
8
- disabled?: boolean;
9
- }
10
-
11
- export type ToggleButtonGroupProps<T extends string = string> = {
12
- /**
13
- * Currently selected value
14
- */
15
- value: T;
16
- /**
17
- * Callback when value changes
18
- */
19
- onValueChange: (value: T) => void;
20
- /**
21
- * Options to display
22
- */
23
- options: ToggleButtonOption<T>[];
24
- /**
25
- * Additional class names for the container
26
- */
27
- className?: string;
28
- }
29
-
30
- /**
31
- * A toggle button group component for selecting one option from a set.
32
- * Displays options as buttons in a horizontal row with active state styling.
33
- */
34
- export function ToggleButtonGroup<T extends string = string>({
35
- value,
36
- onValueChange,
37
- options,
38
- className
39
- }: ToggleButtonGroupProps<T>) {
40
- return (
41
- <div role="group" aria-label="Toggle options" className={cls("inline-flex flex-row bg-surface-100 dark:bg-surface-900 rounded-lg p-1 gap-1", className)}>
42
- {options.map((option) => (
43
- <button
44
- key={option.value}
45
- type="button"
46
- onClick={(e) => {
47
- e.stopPropagation();
48
- if (!option.disabled) {
49
- onValueChange(option.value);
50
- }
51
- }}
52
- disabled={option.disabled}
53
- aria-pressed={value === option.value}
54
- aria-disabled={option.disabled || undefined}
55
- className={cls(
56
- "flex flex-row items-center justify-center gap-2 py-3 px-4 rounded-md transition-colors duration-150 focus:outline-none focus-visible:ring-2 focus-visible:ring-primary/50",
57
- value === option.value
58
- ? "bg-white dark:bg-surface-700 text-primary dark:text-primary-300 shadow-sm"
59
- : "text-text-secondary dark:text-text-secondary-dark hover:bg-surface-200 dark:hover:bg-surface-800",
60
- option.disabled && "opacity-50 cursor-not-allowed"
61
- )}
62
- >
63
- {option.icon}
64
- <span className="text-sm font-medium">{option.label}</span>
65
- </button>
66
- ))}
67
- </div>
68
- );
69
- }