@rebasepro/ui 0.17.3 → 0.18.0

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,339 +0,0 @@
1
-
2
-
3
- import React from "react";
4
- import type { FilterFormFieldProps } from "./VirtualTableHeader";
5
-
6
- /** One sort key: the column, and which way it runs. */
7
- export type VirtualTableSortKey = [string, "asc" | "desc"];
8
-
9
- export type OnRowClickParams<T extends Record<string, unknown>> = {
10
- rowData: T;
11
- rowIndex: number;
12
- event: React.SyntheticEvent
13
- };
14
-
15
- /**
16
- * @see Table
17
- * @group Components
18
- */
19
- export interface VirtualTableProps<T extends Record<string, unknown>> {
20
-
21
- /**
22
- * Array of arbitrary data
23
- */
24
- data?: T[];
25
-
26
- /**
27
- * Properties displayed in this collection. If this property is not set
28
- * every property is displayed, you can filter
29
- */
30
- columns: VirtualTableColumn[];
31
-
32
- /**
33
- * Custom cell renderer
34
- * The renderer receives props `{ cellData, columns, column, columnIndex, rowData, rowIndex, container, isScrolling }`
35
- */
36
- cellRenderer: (props: CellRendererParams<T>) => React.ReactNode;
37
-
38
- /**
39
- * Set this callback if you want to support some combinations
40
- * of filter combinations only.
41
- * @param filterValues
42
- * @param sortBy
43
- */
44
- checkFilterCombination?: (filterValues: VirtualTableFilterValues<Extract<keyof T, string>>,
45
- sortBy?: VirtualTableSortKey[]) => boolean;
46
-
47
- /**
48
- * A callback function when scrolling the table to near the end
49
- */
50
- onEndReached?: () => void;
51
-
52
- /**
53
- * Offset in pixels where the onEndReached callback is triggered
54
- */
55
- endOffset?: number;
56
-
57
- /**
58
- * When the pagination should be reset. E.g. the filters or sorting
59
- * has been reset.
60
- */
61
- onResetPagination?: () => void;
62
-
63
- /**
64
- * Callback when a row is clicked
65
- */
66
- onRowClick?: (props: OnRowClickParams<T>) => void;
67
-
68
- /**
69
- * Callback when a column is resized
70
- */
71
- onColumnResize?: (params: OnVirtualTableColumnResizeParams) => void;
72
- onColumnResizeEnd?: (params: OnVirtualTableColumnResizeParams) => void;
73
-
74
- /**
75
- * Size of the table
76
- */
77
- rowHeight?: number,
78
- headerHeight?: number,
79
-
80
- /**
81
- * In case this table should have some filters set by default
82
- */
83
- filter?: VirtualTableFilterValues<string>;
84
-
85
- /**
86
- * Callback used when filters are updated
87
- * @param filter
88
- */
89
- onFilterUpdate?: (filter?: VirtualTableFilterValues<string> | undefined) => void;
90
-
91
- /**
92
- * Callback when the table is scrolled
93
- * @param props
94
- */
95
- onScroll?: (props: {
96
- scrollDirection: "forward" | "backward",
97
- scrollOffset: number,
98
- scrollUpdateWasRequested: boolean
99
- }) => void;
100
-
101
- /**
102
- * Sort applied to this collection: the keys in order of significance, the
103
- * second breaking ties on the first.
104
- */
105
- sortBy?: VirtualTableSortKey[];
106
-
107
- /**
108
- * Callback used when sorting is updated
109
- * @param sortBy
110
- */
111
- onSortByUpdate?: (sortBy?: VirtualTableSortKey[]) => void;
112
-
113
- /**
114
- * If there is an error loading data you can pass it here, so it gets
115
- * displayed instead of the content
116
- */
117
- error?: Error;
118
-
119
- /**
120
- * Message displayed when there is no data
121
- */
122
- emptyComponent?: React.ReactNode;
123
-
124
- /**
125
- * Is the table in a loading state
126
- */
127
- loading?: boolean;
128
-
129
- /**
130
- * Should apply a different style when hovering
131
- */
132
- hoverRow?: boolean;
133
-
134
- /**
135
- * Apply a custom class name to the row
136
- * @param rowData
137
- */
138
- rowClassName?: (rowData: T) => string | undefined;
139
-
140
- /**
141
- * Callback to create a filter field, displayed in the header as a dropdown
142
- * @param props
143
- */
144
- createFilterField?: (props: FilterFormFieldProps<unknown>) => React.ReactNode;
145
-
146
- /**
147
- * Class name applied to the table
148
- */
149
- className?: string;
150
-
151
- /**
152
- * Style applied to the table
153
- */
154
- style?: React.CSSProperties;
155
-
156
- /**
157
- * Component rendered at the end of the table, after scroll
158
- */
159
- endAdornment?: React.ReactNode;
160
-
161
- /**
162
- * If adding this callback, a button to add a new column is displayed.
163
- * @param column
164
- */
165
- AddColumnComponent?: React.ComponentType;
166
-
167
- /**
168
- * Initial scroll position
169
- */
170
- initialScroll?: number;
171
-
172
- /**
173
- * Callback when columns are reordered via drag-and-drop.
174
- * @param columns The new column order
175
- */
176
- onColumnsOrderChange?: (columns: VirtualTableColumn[]) => void;
177
-
178
- /**
179
- * Extra data passed to the cell renderer
180
- */
181
- extraData?: unknown;
182
-
183
- }
184
-
185
- export type CellRendererParams<T = unknown> = {
186
- column: VirtualTableColumn;
187
- columns: VirtualTableColumn[];
188
- columnIndex: number;
189
- rowData?: T;
190
- rowIndex: number;
191
- width: number;
192
- isScrolling?: boolean;
193
- // Sortable props for dnd-kit integration
194
- sortableNodeRef?: (node: HTMLElement | null) => void;
195
- sortableStyle?: React.CSSProperties;
196
- sortableAttributes?: Record<string, unknown>;
197
- isDragging?: boolean;
198
- isDraggable?: boolean;
199
- frozen?: boolean;
200
- extraData?: unknown;
201
- };
202
-
203
- /**
204
- * @see Table
205
- * @group Components
206
- */
207
- export interface VirtualTableColumn<CustomProps = unknown> {
208
-
209
- /**
210
- * Data key for the cell value, could be "a.b.c"
211
- */
212
- key: string;
213
-
214
- /**
215
- * The width of the column, gutter width is not included
216
- */
217
- width: number;
218
-
219
- /**
220
- * Label displayed in the header
221
- */
222
- title?: string;
223
-
224
- /**
225
- * This column is frozen to the left
226
- */
227
- frozen?: boolean;
228
-
229
- /**
230
- * How is the
231
- */
232
- headerAlign?: "left" | "center" | "right";
233
-
234
- /**
235
- * Icon displayed in the header
236
- */
237
- icon?: React.ReactNode;
238
-
239
- /**
240
- *
241
- */
242
- filter?: boolean;
243
-
244
- /**
245
- * Alignment of the column cell
246
- */
247
-
248
- align?: "left" | "right" | "center";
249
-
250
- /**
251
- * Whether the column is sortable, defaults to false
252
- */
253
- sortable?: boolean;
254
-
255
- /**
256
- * Can it be resized
257
- */
258
- resizable?: boolean;
259
-
260
- /**
261
- * Custom props passed to the cell renderer
262
- */
263
- custom?: CustomProps;
264
-
265
- /**
266
- * Additional children to be rendered in the header when hovering
267
- */
268
- AdditionalHeaderWidget?: (props: { onHover: boolean }) => React.ReactNode;
269
- }
270
-
271
- /**
272
- * @see Table
273
- * @group Collection components
274
- */
275
- export type OnVirtualTableColumnResizeParams = {
276
- width: number,
277
- key: string,
278
- column: VirtualTableColumn
279
- };
280
-
281
- /**
282
- * The filter operators this table can express.
283
- *
284
- * A deliberate copy of `WhereFilterOp` in `@rebasepro/types`, not an import:
285
- * `@rebasepro/ui` has no dependency on the core types and is meant to stay
286
- * usable on its own. The cost is that two published packages export the same
287
- * name, so the two must be kept identical by hand —
288
- * `packages/types/test/filter-operators-duplication.test.ts` fails if they
289
- * diverge, because a table that cannot express an operator the query layer
290
- * supports is a silent gap: the filter simply is not offered.
291
- *
292
- * @see Table
293
- * @group Components
294
- */
295
- export type WhereFilterOp =
296
- | "<"
297
- | "<="
298
- | "=="
299
- | "!="
300
- | ">="
301
- | ">"
302
- | "array-contains"
303
- | "in"
304
- | "not-in"
305
- | "array-contains-any"
306
- | "like"
307
- | "ilike"
308
- | "not-like"
309
- | "not-ilike"
310
- | "is-null"
311
- | "is-not-null";
312
-
313
- export type FilterValues<Key extends string> =
314
- Partial<Record<Key, [WhereFilterOp, unknown] | [WhereFilterOp, unknown][]>>;
315
-
316
- /**
317
- * @see Table
318
- * @group Components
319
- */
320
- export type VirtualTableSort = "asc" | "desc" | undefined;
321
-
322
- /**
323
- * @see Table
324
- * @group Components
325
- */
326
- export type VirtualTableFilterValues<Key extends string> = FilterValues<Key>;
327
-
328
- /**
329
- * Filter conditions in a `Query.where()` clause, named by {@link WhereFilterOp}.
330
- *
331
- * The list is not repeated here: this comment used to name eight operators when
332
- * the type had sixteen, having been written before the SQL pattern and null
333
- * checks (`like`, `ilike`, `not-like`, `not-ilike`, `is-null`, `is-not-null`)
334
- * were added. A prose copy of a union drifts from it silently.
335
- *
336
- * @see Table
337
- * @group Models
338
- */
339
- export type VirtualTableWhereFilterOp = WhereFilterOp;
@@ -1,51 +0,0 @@
1
- import React, { useCallback } from "react";
2
-
3
- import { deepEqual as equal } from "fast-equals"
4
-
5
- import { VirtualTableRowProps } from "./types";
6
- import { cls } from "../../util/cls";
7
- export const VirtualTableRow = React.memo<VirtualTableRowProps<Record<string, unknown>>>(
8
- function VirtualTableRow<T extends Record<string, unknown>>({
9
- rowData,
10
- rowIndex,
11
- children,
12
- onRowClick,
13
- rowHeight,
14
- style,
15
- hoverRow,
16
- rowClassName
17
- }: VirtualTableRowProps<T>) {
18
-
19
- const onClick = useCallback((event: React.SyntheticEvent) => {
20
- if (onRowClick)
21
- onRowClick({
22
- rowData,
23
- rowIndex,
24
- event
25
- })
26
- }, [onRowClick, rowData, rowIndex]);
27
-
28
- return (
29
- <div
30
- className={cls(
31
- "flex min-w-full text-sm border-b border-surface-200/60 dark:border-surface-700/60 dark:bg-surface-800",
32
- rowClassName ? rowClassName(rowData) : "",
33
- {
34
- "hover:!bg-primary/[0.03] dark:hover:!bg-primary/5": hoverRow,
35
- "cursor-pointer ": onRowClick
36
- }
37
- )}
38
- onClick={onClick}
39
- style={{
40
- ...(style),
41
- height: rowHeight,
42
- width: "fit-content"
43
- }}
44
- >
45
- {children}
46
- </div>
47
- );
48
-
49
- },
50
- equal
51
- );
@@ -1,44 +0,0 @@
1
- import React from "react";
2
- import { cls } from "../../../util";
3
- import { focusedDisabled } from "../../../styles";
4
- import { DateTimeField } from "../../DateTimeField";
5
-
6
- export function VirtualTableDateField(props: {
7
- name?: string;
8
- error?: Error;
9
- mode?: "date" | "date_time";
10
- timezone?: string;
11
- internalValue: Date | undefined | null;
12
- updateValue: (newValue: (Date | null)) => void;
13
- focused: boolean;
14
- disabled: boolean;
15
- small?: boolean;
16
- locale?: string;
17
- onBlur?: React.FocusEventHandler<HTMLInputElement | HTMLTextAreaElement>;
18
- }) {
19
- const { disabled, error, mode, timezone, internalValue, updateValue, small = false, locale } = props;
20
-
21
- return (
22
- <DateTimeField
23
- value={internalValue ?? undefined}
24
- onChange={(dateValue) => updateValue(dateValue ?? null)}
25
- // Same omission as `VirtualTableInput`: destructured and forwarded
26
- // nowhere, so a disabled date cell was still editable. `error` went
27
- // the same way — it drives `DateTimeField`'s invalid styling, and a
28
- // prop a caller passes should reach something rather than sit in a
29
- // destructure.
30
- disabled={disabled}
31
- error={Boolean(error)}
32
- invisible={true}
33
- // Without this the field falls back to DateTimeField's own default of
34
- // `size="large"` (min-h-64px), which overflows a table cell and makes
35
- // the value collide with the cell border.
36
- size={small ? "small" : "medium"}
37
- inputClassName={cls("w-full h-full", focusedDisabled)}
38
- className={cls("w-full h-full", focusedDisabled)}
39
- mode={mode}
40
- timezone={timezone}
41
- locale={locale}
42
- />
43
- );
44
- }
@@ -1,89 +0,0 @@
1
- import React, { useEffect, useRef, useState } from "react";
2
- import { useDebouncedCallback } from "../../../hooks/useDebouncedCallback";
3
- import { focusedDisabled } from "../../../styles";
4
- import { TextareaAutosize } from "../../TextareaAutosize";
5
-
6
- export function VirtualTableInput(props: {
7
- error?: Error;
8
- value: string;
9
- multiline?: boolean;
10
- focused: boolean;
11
- disabled: boolean;
12
- updateValue: (newValue: (string | null)) => void;
13
- onBlur?: () => void;
14
- }) {
15
- const ref = React.useRef<HTMLTextAreaElement>(null);
16
- const { disabled, value, multiline = false, updateValue, focused } = props;
17
- const prevValue = useRef<string | null>(value);
18
- const [internalValue, setInternalValue] = useState<typeof value>(value);
19
- const focusedState = useRef<boolean>(false);
20
-
21
- useEffect(() => {
22
- if (prevValue.current !== value && value !== internalValue)
23
- setInternalValue(value);
24
- prevValue.current = value;
25
- }, [value]);
26
-
27
- const doUpdate = React.useCallback(() => {
28
- const emptyInitialValue = !value;
29
- if (emptyInitialValue && !internalValue) return;
30
- if (internalValue !== value && internalValue !== prevValue.current) {
31
- prevValue.current = internalValue;
32
- updateValue(internalValue);
33
- }
34
- }, [internalValue, updateValue, value]);
35
-
36
- useDebouncedCallback(internalValue, doUpdate, !focused, 400);
37
-
38
- useEffect(() => {
39
- if (ref.current && focused && !focusedState.current) {
40
- focusedState.current = true;
41
- ref.current.focus({ preventScroll: true });
42
- ref.current.selectionStart = ref.current.value.length;
43
- ref.current.selectionEnd = ref.current.value.length;
44
- } else {
45
- focusedState.current = focused;
46
- }
47
- }, [focused, ref]);
48
-
49
- return (
50
- <TextareaAutosize
51
- className={focusedDisabled}
52
- ref={ref}
53
- // Destructured above and passed to nothing, so a property carrying
54
- // `admin: { disabled: true }` stayed typeable in the table and the
55
- // debounced write fired on blur. `readOnly` as well as `disabled`:
56
- // a disabled textarea is skipped by keyboard navigation, and a cell
57
- // you cannot tab through is worse than one you cannot edit.
58
- disabled={disabled}
59
- readOnly={disabled}
60
- style={{
61
- padding: 0,
62
- margin: 0,
63
- width: "100%",
64
- color: "unset",
65
- fontWeight: "unset",
66
- fontSize: "unset",
67
- fontFamily: "unset",
68
- background: "unset",
69
- border: "unset",
70
- resize: "none",
71
- outline: "none"
72
- }}
73
- value={internalValue ?? ""}
74
- onChange={(evt) => {
75
- const newValue = evt.target.value as string;
76
- if (multiline || !newValue.endsWith("\n"))
77
- setInternalValue(newValue);
78
- }}
79
- onFocus={() => {
80
- focusedState.current = true;
81
- }}
82
- onBlur={() => {
83
- focusedState.current = false;
84
- doUpdate();
85
- if (props.onBlur) props.onBlur();
86
- }}
87
- />
88
- );
89
- }
@@ -1,71 +0,0 @@
1
- import React, { useEffect, useRef, useState } from "react";
2
- import { useDebouncedCallback } from "../../../hooks/useDebouncedCallback";
3
- import { cls } from "../../../util";
4
- import { focusedDisabled } from "../../../styles";
5
- import { TextField } from "../../TextField";
6
-
7
- export function VirtualTableNumberInput(props: {
8
- error?: Error;
9
- value: number;
10
- align?: "right" | "left" | "center";
11
- updateValue: (newValue: (number | null)) => void;
12
- focused: boolean;
13
- disabled: boolean;
14
- }) {
15
- const { align = "left", value, updateValue, focused } = props;
16
- const propStringValue = (value && typeof value === "number") ? value.toString() : "";
17
- const [internalValue, setInternalValue] = useState<string | null>(propStringValue);
18
- const prevValue = useRef<number | null>(value);
19
-
20
- useEffect(() => {
21
- if (prevValue.current !== value && String(value) !== internalValue)
22
- setInternalValue(value ? value.toString() : null);
23
- prevValue.current = value;
24
- }, [value]);
25
-
26
- const doUpdate = React.useCallback(() => {
27
- if (internalValue !== propStringValue) {
28
- if (internalValue !== undefined && internalValue !== null) {
29
- const numberValue = parseFloat(internalValue);
30
- if (isNaN(numberValue)) return;
31
- updateValue(numberValue);
32
- } else {
33
- updateValue(null);
34
- }
35
- }
36
- }, [internalValue, value]);
37
-
38
- useDebouncedCallback(internalValue, doUpdate, !focused, 400);
39
-
40
- useEffect(() => {
41
- if (!focused && propStringValue !== internalValue)
42
- setInternalValue(value !== undefined && value !== null ? value.toString() : null);
43
- }, [value, focused]);
44
-
45
- const inputRef = React.useRef<HTMLInputElement>(null);
46
-
47
- useEffect(() => {
48
- if (inputRef.current && focused) {
49
- inputRef.current.focus({ preventScroll: true });
50
- }
51
- }, [focused, inputRef]);
52
-
53
- const regexp = /^-?[0-9]+[,.]?[0-9]*$/;
54
-
55
- return (
56
- <TextField
57
- inputRef={inputRef}
58
- invisible={true}
59
- size="small"
60
- className="w-full"
61
- inputClassName={cls("p-0 m-0 bg-transparent border-none outline-hidden font-normal leading-normal text-unset", focusedDisabled)}
62
- inputStyle={{ textAlign: align }}
63
- value={internalValue ?? ""}
64
- onChange={(evt) => {
65
- const newValue = evt.target.value.replace(",", ".");
66
- if (newValue.length === 0) setInternalValue(null);
67
- if (regexp.test(newValue) || newValue.startsWith("-")) setInternalValue(newValue);
68
- }}
69
- />
70
- );
71
- }
@@ -1,107 +0,0 @@
1
- import React, { useCallback, useEffect, useRef } from "react";
2
- import { Select, SelectItem } from "../../Select";
3
- import { MultiSelect, MultiSelectItem } from "../../MultiSelect";
4
- import { Chip } from "../../Chip";
5
-
6
- export interface VirtualTableSelectOption {
7
- value: string | number;
8
- label: string;
9
- color?: string;
10
- }
11
-
12
- export function VirtualTableSelect(props: {
13
- options: VirtualTableSelectOption[];
14
- error?: Error;
15
- multiple?: boolean;
16
- disabled: boolean;
17
- small?: boolean;
18
- value: string | number | string[] | number[] | undefined;
19
- updateValue: (newValue: (string | number | string[] | number[] | null)) => void;
20
- focused: boolean;
21
- onBlur?: React.FocusEventHandler<HTMLInputElement | HTMLTextAreaElement>;
22
- }) {
23
- const { options, value, disabled, small = false, focused, updateValue, multiple = false } = props;
24
- const validValue = (Array.isArray(value) && multiple) || (!Array.isArray(value) && !multiple);
25
- const ref = useRef<HTMLButtonElement>(null);
26
-
27
- useEffect(() => {
28
- if (ref.current && focused) {
29
- ref.current?.focus({ preventScroll: true });
30
- }
31
- }, [focused, ref]);
32
-
33
- const onChange = useCallback((updatedValue: string | string[]) => {
34
- if (multiple) {
35
- const isNumber = options.some(o => typeof o.value === "number");
36
- const newValue = (updatedValue as string[]).map((v) => {
37
- const opt = options.find(o => String(o.value) === v);
38
- return typeof opt?.value === "number" ? parseFloat(v) : v;
39
- });
40
- if (isNumber) {
41
- updateValue(newValue as number[]);
42
- } else {
43
- updateValue(newValue as string[]);
44
- }
45
- } else {
46
- const opt = options.find(o => String(o.value) === updatedValue);
47
- updateValue(typeof opt?.value === "number" ? parseFloat(updatedValue as string) : (updatedValue as string || null));
48
- }
49
- }, [multiple, updateValue, options]);
50
-
51
- const renderValue = (val?: string | number) => {
52
- const option = options.find(o => o.value === val);
53
- return (
54
- <Chip size={small ? "small" : "medium"}>
55
- {option ? option.label : String(val)}
56
- </Chip>
57
- );
58
- };
59
-
60
- const handleOpenChange = useCallback((open: boolean) => {
61
- if (!open && ref.current) {
62
- setTimeout(() => {
63
- ref.current?.focus({ preventScroll: true });
64
- }, 0);
65
- }
66
- }, []);
67
-
68
- return multiple ? (
69
- <MultiSelect
70
- inputRef={ref}
71
- className="w-full h-full p-0 bg-transparent outline-none"
72
- position={"item-aligned"}
73
- disabled={disabled}
74
- includeClear={false}
75
- useChips={false}
76
- value={validValue ? (value as (string | number)[]).map(v => String(v)) : []}
77
- onValueChange={onChange}
78
- onOpenChange={handleOpenChange}
79
- >
80
- {options.map((opt) => (
81
- <MultiSelectItem key={String(opt.value)} value={String(opt.value)}>
82
- <Chip size={small ? "small" : "medium"}>{opt.label}</Chip>
83
- </MultiSelectItem>
84
- ))}
85
- </MultiSelect>
86
- ) : (
87
- <Select
88
- inputRef={ref}
89
- fullWidth
90
- className="w-full h-full p-0 bg-transparent outline-none [&_button]:ring-0 [&_button]:ring-offset-0"
91
- inputClassName="ring-0 ring-offset-0 focus:ring-0 focus-visible:ring-0 outline-none focus:outline-none focus-visible:outline-none focus-visible:ring-offset-0"
92
- position={"item-aligned"}
93
- disabled={disabled}
94
- padding={false}
95
- value={validValue ? String(value) : ""}
96
- onValueChange={onChange}
97
- onOpenChange={handleOpenChange}
98
- renderValue={renderValue}
99
- >
100
- {options.map((opt) => (
101
- <SelectItem key={String(opt.value)} value={String(opt.value)}>
102
- <Chip size={small ? "small" : "medium"}>{opt.label}</Chip>
103
- </SelectItem>
104
- ))}
105
- </Select>
106
- );
107
- }