@rebasepro/ui 0.4.0 → 0.6.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.
- package/README.md +124 -66
- package/dist/components/Autocomplete.d.ts +2 -2
- package/dist/components/BooleanSwitchWithLabel.d.ts +1 -1
- package/dist/components/Checkbox.d.ts +1 -1
- package/dist/components/Chip.d.ts +1 -1
- package/dist/components/CircularProgress.d.ts +2 -1
- package/dist/components/CircularProgressCenter.d.ts +2 -1
- package/dist/components/Collapse.d.ts +1 -1
- package/dist/components/ColorPicker.d.ts +2 -1
- package/dist/components/DebouncedTextField.d.ts +2 -1
- package/dist/components/Dialog.d.ts +2 -1
- package/dist/components/DialogActions.d.ts +1 -1
- package/dist/components/DialogContent.d.ts +1 -1
- package/dist/components/DialogTitle.d.ts +1 -1
- package/dist/components/ErrorBoundary.d.ts +1 -1
- package/dist/components/ExpandablePanel.d.ts +1 -1
- package/dist/components/FileUpload.d.ts +1 -1
- package/dist/components/InfoLabel.d.ts +1 -1
- package/dist/components/LoadingButton.d.ts +1 -1
- package/dist/components/Menu.d.ts +1 -1
- package/dist/components/Menubar.d.ts +16 -16
- package/dist/components/MultiSelect.d.ts +1 -1
- package/dist/components/Paper.d.ts +1 -1
- package/dist/components/Popover.d.ts +1 -1
- package/dist/components/ResizablePanels.d.ts +1 -1
- package/dist/components/SearchBar.d.ts +1 -1
- package/dist/components/Select.d.ts +2 -2
- package/dist/components/Separator.d.ts +1 -1
- package/dist/components/Skeleton.d.ts +2 -1
- package/dist/components/Table.d.ts +5 -5
- package/dist/components/Tabs.d.ts +2 -2
- package/dist/components/TextField.d.ts +2 -2
- package/dist/components/ToggleButtonGroup.d.ts +1 -1
- package/dist/components/Tooltip.d.ts +1 -1
- package/dist/components/Typography.d.ts +1 -1
- package/dist/components/VirtualTable/VirtualTableHeaderRow.d.ts +2 -1
- package/dist/components/common/SelectInputLabel.d.ts +1 -1
- package/dist/hooks/PortalContainerContext.d.ts +1 -1
- package/dist/icons/GitHubIcon.d.ts +2 -1
- package/dist/icons/HandleIcon.d.ts +1 -1
- package/dist/icons/index.d.ts +1 -1
- package/dist/index.es.js +5678 -8795
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +6796 -9398
- package/dist/index.umd.js.map +1 -1
- package/dist/src/index.css +126 -0
- package/package.json +35 -35
- package/src/components/BooleanSwitchWithLabel.tsx +4 -3
- package/src/components/DebouncedTextField.tsx +11 -9
- package/src/components/Dialog.tsx +6 -3
- package/src/components/ErrorBoundary.tsx +4 -2
- package/src/components/Table.tsx +21 -13
- package/src/components/TextField.tsx +3 -3
- package/src/components/Tooltip.tsx +2 -1
- package/src/components/VirtualTable/VirtualTable.performance.test.tsx +1 -1
- package/src/components/VirtualTable/VirtualTable.tsx +1 -2
- package/src/components/VirtualTable/VirtualTableHeader.tsx +34 -21
- package/src/hooks/useDebounceCallback.tsx +1 -1
- package/src/icons/Icon.tsx +1 -1
- package/src/icons/icon_keys.ts +1 -1
- package/src/icons/index.ts +1 -1
- package/src/scripts/fixIconSizes.cjs +7 -7
- package/src/scripts/fixIconSizesToConst.cjs +2 -2
- package/src/scripts/generateIconKeys.ts +7 -7
- package/src/scripts/saveIconFiles.ts +1 -1
- package/src/util/debounce.ts +2 -3
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
@theme {
|
|
2
|
+
/* Font Families */
|
|
3
|
+
--font-sans: 'Rubik', 'Roboto', 'Helvetica', 'Arial', sans-serif;
|
|
4
|
+
--font-headers: 'Rubik', 'Roboto', 'Helvetica', 'Arial', sans-serif;
|
|
5
|
+
--font-mono: 'JetBrains Mono', 'Space Mono', 'Lucida Console', monospace;
|
|
6
|
+
|
|
7
|
+
/* Colors */
|
|
8
|
+
--color-primary: #0070F4;
|
|
9
|
+
--color-primary-light: oklch(from var(--color-primary) calc(l + 0.15) c h);
|
|
10
|
+
--color-primary-dark: oklch(from var(--color-primary) calc(l - 0.15) c h);
|
|
11
|
+
--color-secondary: #FF5B79;
|
|
12
|
+
--color-secondary-light: oklch(from var(--color-secondary) calc(l + 0.15) c h);
|
|
13
|
+
--color-secondary-dark: oklch(from var(--color-secondary) calc(l - 0.15) c h);
|
|
14
|
+
|
|
15
|
+
--color-primary-bg: oklch(from var(--color-primary) l c h / 0.1);
|
|
16
|
+
--color-secondary-bg: oklch(from var(--color-secondary) l c h / 0.1);
|
|
17
|
+
|
|
18
|
+
/* Field Colors */
|
|
19
|
+
--color-field-disabled: rgb(224 224 226);
|
|
20
|
+
--color-field-disabled-dark: rgb(35 35 37);
|
|
21
|
+
|
|
22
|
+
/* Text Colors — using opaque values so icon strokes render solidly */
|
|
23
|
+
--color-text-primary: #212121;
|
|
24
|
+
--color-text-secondary: #757575;
|
|
25
|
+
--color-text-disabled: #9e9e9e;
|
|
26
|
+
--color-text-primary-dark: #ffffff;
|
|
27
|
+
--color-text-secondary-dark: #a0a0a9;
|
|
28
|
+
--color-text-disabled-dark: #757580;
|
|
29
|
+
|
|
30
|
+
/* Surface Colors */
|
|
31
|
+
--color-surface-50: #fafafa;
|
|
32
|
+
--color-surface-100: #f5f5f5;
|
|
33
|
+
--color-surface-200: #e5e5e5;
|
|
34
|
+
--color-surface-300: #d4d4d4;
|
|
35
|
+
--color-surface-400: #a3a3a3;
|
|
36
|
+
--color-surface-500: #737373;
|
|
37
|
+
--color-surface-600: #404040;
|
|
38
|
+
--color-surface-700: #262626;
|
|
39
|
+
--color-surface-800: #111111;
|
|
40
|
+
--color-surface-900: #0a0a0a;
|
|
41
|
+
--color-surface-950: #000000;
|
|
42
|
+
|
|
43
|
+
/* Surface Accent Colors */
|
|
44
|
+
--color-surface-accent-50: #f8fafc;
|
|
45
|
+
--color-surface-accent-100: #f1f5f9;
|
|
46
|
+
--color-surface-accent-200: #e2e8f0;
|
|
47
|
+
--color-surface-accent-300: #cbd5e1;
|
|
48
|
+
--color-surface-accent-400: #94a3b8;
|
|
49
|
+
--color-surface-accent-500: #64748b;
|
|
50
|
+
--color-surface-accent-600: #475569;
|
|
51
|
+
--color-surface-accent-700: #334155;
|
|
52
|
+
--color-surface-accent-800: #1e293b;
|
|
53
|
+
--color-surface-accent-900: #172033;
|
|
54
|
+
--color-surface-accent-950: #0f172a;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/* Chrome, Safari and Opera */
|
|
58
|
+
.no-scrollbar::-webkit-scrollbar {
|
|
59
|
+
display: none;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.no-scrollbar {
|
|
63
|
+
-ms-overflow-style: none; /* IE and Edge */
|
|
64
|
+
scrollbar-width: none; /* Firefox */
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.typography-h1 {
|
|
68
|
+
@apply text-6xl font-headers font-light tracking-tight;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.typography-h2 {
|
|
72
|
+
@apply text-5xl font-headers font-light tracking-tight;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.typography-h3 {
|
|
76
|
+
@apply text-4xl font-headers font-normal tracking-tight;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.typography-h4 {
|
|
80
|
+
@apply text-3xl font-headers font-normal tracking-tight;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.typography-h5 {
|
|
84
|
+
@apply text-2xl font-headers font-normal;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.typography-h6 {
|
|
88
|
+
@apply text-xl font-headers font-normal;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.typography-subtitle1 {
|
|
92
|
+
@apply text-lg font-headers font-medium;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.typography-subtitle2 {
|
|
96
|
+
@apply text-base font-headers font-medium;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.typography-body1 {
|
|
100
|
+
@apply text-base;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.typography-body2 {
|
|
104
|
+
@apply text-sm;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.typography-caption {
|
|
108
|
+
@apply text-xs;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.typography-label {
|
|
112
|
+
@apply text-sm font-medium;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.typography-inherit {
|
|
116
|
+
@apply text-inherit;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.typography-button {
|
|
120
|
+
@apply text-sm font-semibold;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
:focus-visible {
|
|
124
|
+
@apply outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-0 focus-visible:ring-offset-transparent
|
|
125
|
+
}
|
|
126
|
+
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rebasepro/ui",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.6.0",
|
|
5
5
|
"description": "Awesome Firebase/Firestore-based headless open-source CMS",
|
|
6
6
|
"funding": {
|
|
7
7
|
"url": "https://github.com/sponsors/rebaseco"
|
|
@@ -46,32 +46,31 @@
|
|
|
46
46
|
"@dnd-kit/core": "^6.3.1",
|
|
47
47
|
"@dnd-kit/sortable": "^10.0.0",
|
|
48
48
|
"@dnd-kit/utilities": "^3.2.2",
|
|
49
|
-
"@radix-ui/react-checkbox": "^1.3.
|
|
50
|
-
"@radix-ui/react-collapsible": "^1.1.
|
|
51
|
-
"@radix-ui/react-dialog": "^1.1.
|
|
52
|
-
"@radix-ui/react-dropdown-menu": "^2.1.
|
|
53
|
-
"@radix-ui/react-label": "^2.1.
|
|
54
|
-
"@radix-ui/react-menubar": "^1.1.
|
|
55
|
-
"@radix-ui/react-popover": "^1.1.
|
|
56
|
-
"@radix-ui/react-portal": "^1.1.
|
|
57
|
-
"@radix-ui/react-radio-group": "^1.
|
|
58
|
-
"@radix-ui/react-select": "^2.
|
|
59
|
-
"@radix-ui/react-separator": "^1.1.
|
|
60
|
-
"@radix-ui/react-slider": "^1.
|
|
61
|
-
"@radix-ui/react-slot": "^1.2.
|
|
62
|
-
"@radix-ui/react-tabs": "^1.1.
|
|
63
|
-
"@radix-ui/react-tooltip": "^1.2.
|
|
64
|
-
"@radix-ui/react-visually-hidden": "^1.2.
|
|
49
|
+
"@radix-ui/react-checkbox": "^1.3.4",
|
|
50
|
+
"@radix-ui/react-collapsible": "^1.1.13",
|
|
51
|
+
"@radix-ui/react-dialog": "^1.1.16",
|
|
52
|
+
"@radix-ui/react-dropdown-menu": "^2.1.17",
|
|
53
|
+
"@radix-ui/react-label": "^2.1.9",
|
|
54
|
+
"@radix-ui/react-menubar": "^1.1.17",
|
|
55
|
+
"@radix-ui/react-popover": "^1.1.16",
|
|
56
|
+
"@radix-ui/react-portal": "^1.1.11",
|
|
57
|
+
"@radix-ui/react-radio-group": "^1.4.0",
|
|
58
|
+
"@radix-ui/react-select": "^2.3.0",
|
|
59
|
+
"@radix-ui/react-separator": "^1.1.9",
|
|
60
|
+
"@radix-ui/react-slider": "^1.4.0",
|
|
61
|
+
"@radix-ui/react-slot": "^1.2.5",
|
|
62
|
+
"@radix-ui/react-tabs": "^1.1.14",
|
|
63
|
+
"@radix-ui/react-tooltip": "^1.2.9",
|
|
64
|
+
"@radix-ui/react-visually-hidden": "^1.2.5",
|
|
65
65
|
"clsx": "^2.1.1",
|
|
66
66
|
"cmdk": "^1.1.1",
|
|
67
67
|
"fast-equals": "6.0.0",
|
|
68
|
-
"lucide-react": "1.
|
|
69
|
-
"markdown-it": "^14.
|
|
70
|
-
"react-
|
|
71
|
-
"react-dropzone": "^14.4.1",
|
|
68
|
+
"lucide-react": "1.18.0",
|
|
69
|
+
"markdown-it": "^14.2.0",
|
|
70
|
+
"react-dropzone": "^15.0.0",
|
|
72
71
|
"react-use-measure": "^2.1.7",
|
|
73
72
|
"react-window": "^1.8.11",
|
|
74
|
-
"tailwind-merge": "^
|
|
73
|
+
"tailwind-merge": "^3.6.0"
|
|
75
74
|
},
|
|
76
75
|
"peerDependencies": {
|
|
77
76
|
"react": ">=19.0.0",
|
|
@@ -82,24 +81,25 @@
|
|
|
82
81
|
"@testing-library/jest-dom": "^6.9.1",
|
|
83
82
|
"@testing-library/react": "^16.3.2",
|
|
84
83
|
"@testing-library/user-event": "^14.6.1",
|
|
85
|
-
"@types/jest": "^
|
|
84
|
+
"@types/jest": "^30.0.0",
|
|
86
85
|
"@types/markdown-it": "^14.1.2",
|
|
87
|
-
"@types/node": "^
|
|
86
|
+
"@types/node": "^25.9.3",
|
|
88
87
|
"@types/object-hash": "^3.0.6",
|
|
89
|
-
"@types/react": "^19.2.
|
|
88
|
+
"@types/react": "^19.2.17",
|
|
90
89
|
"@types/react-dom": "^19.2.3",
|
|
91
90
|
"@types/react-measure": "^2.0.12",
|
|
92
|
-
"@
|
|
91
|
+
"@types/react-window": "^1.8.8",
|
|
92
|
+
"@vitejs/plugin-react": "^6.0.2",
|
|
93
93
|
"babel-plugin-react-compiler": "beta",
|
|
94
|
-
"cross-env": "^
|
|
95
|
-
"jest": "^
|
|
94
|
+
"cross-env": "^10.1.0",
|
|
95
|
+
"jest": "^30.4.2",
|
|
96
96
|
"rollup-preserve-directives": "^1.1.3",
|
|
97
|
-
"tailwindcss": "^4.3.
|
|
98
|
-
"ts-jest": "^29.4.
|
|
99
|
-
"tsd": "^0.
|
|
100
|
-
"typescript": "^
|
|
101
|
-
"vite": "^
|
|
102
|
-
"vite-plugin-static-copy": "^
|
|
97
|
+
"tailwindcss": "^4.3.1",
|
|
98
|
+
"ts-jest": "^29.4.11",
|
|
99
|
+
"tsd": "^0.33.0",
|
|
100
|
+
"typescript": "^6.0.3",
|
|
101
|
+
"vite": "^8.0.16",
|
|
102
|
+
"vite-plugin-static-copy": "^4.1.1"
|
|
103
103
|
},
|
|
104
104
|
"files": [
|
|
105
105
|
"dist",
|
|
@@ -112,7 +112,7 @@
|
|
|
112
112
|
},
|
|
113
113
|
"scripts": {
|
|
114
114
|
"watch": "vite build --watch",
|
|
115
|
-
"build": "rm -rf dist || true && vite build && tsc --emitDeclarationOnly -p tsconfig.prod.json",
|
|
115
|
+
"build": "rm -rf dist || true && vite build && cp dist/src/index.css dist/index.css 2>/dev/null; tsc --emitDeclarationOnly -p tsconfig.prod.json",
|
|
116
116
|
"test:lint": "eslint \"src/**\" --quiet",
|
|
117
117
|
"test": "jest --passWithNoTests",
|
|
118
118
|
"clean": "rm -rf dist && find ./src -name '*.js' -type f | xargs rm -f",
|
|
@@ -86,9 +86,10 @@ export const BooleanSwitchWithLabel = function BooleanSwitchWithLabel({
|
|
|
86
86
|
)}
|
|
87
87
|
onClick={disabled ? undefined : (e) => {
|
|
88
88
|
if (props.allowIndeterminate) {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
else
|
|
89
|
+
const onChange = onValueChange as ((newValue: boolean | null) => void) | undefined;
|
|
90
|
+
if (value === null || value === undefined) onChange?.(true)
|
|
91
|
+
else if (value) onChange?.(false)
|
|
92
|
+
else onChange?.(null);
|
|
92
93
|
} else {
|
|
93
94
|
onValueChange?.(!value);
|
|
94
95
|
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import React, {
|
|
2
|
+
import React, { useCallback, useEffect, useRef, useState } from "react";
|
|
3
3
|
import { TextField, TextFieldProps } from "./index";
|
|
4
4
|
|
|
5
|
+
type TextFieldChangeEvent = React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>;
|
|
6
|
+
|
|
5
7
|
export function DebouncedTextField<T extends string | number>(props: TextFieldProps<T>) {
|
|
6
8
|
|
|
7
9
|
const [internalValue, setInternalValue] = useState<T | string>(props.value ?? "");
|
|
@@ -24,7 +26,7 @@ export function DebouncedTextField<T extends string | number>(props: TextFieldPr
|
|
|
24
26
|
};
|
|
25
27
|
}, []);
|
|
26
28
|
|
|
27
|
-
const flushChange = useCallback((value: T | string, event?:
|
|
29
|
+
const flushChange = useCallback((value: T | string, event?: TextFieldChangeEvent) => {
|
|
28
30
|
if (timerRef.current) {
|
|
29
31
|
clearTimeout(timerRef.current);
|
|
30
32
|
timerRef.current = undefined;
|
|
@@ -38,12 +40,12 @@ export function DebouncedTextField<T extends string | number>(props: TextFieldPr
|
|
|
38
40
|
value: value,
|
|
39
41
|
name: props.name
|
|
40
42
|
}
|
|
41
|
-
};
|
|
42
|
-
props.onChange(e
|
|
43
|
+
} as TextFieldChangeEvent;
|
|
44
|
+
props.onChange(e);
|
|
43
45
|
}
|
|
44
46
|
}, [props.value, props.onChange, props.name]);
|
|
45
47
|
|
|
46
|
-
const internalOnChange = useCallback((event:
|
|
48
|
+
const internalOnChange = useCallback((event: TextFieldChangeEvent) => {
|
|
47
49
|
const newValue = event.target.value;
|
|
48
50
|
setInternalValue(newValue);
|
|
49
51
|
|
|
@@ -58,17 +60,17 @@ export function DebouncedTextField<T extends string | number>(props: TextFieldPr
|
|
|
58
60
|
};
|
|
59
61
|
|
|
60
62
|
timerRef.current = setTimeout(() => {
|
|
61
|
-
flushChange(newValue, eventCopy as
|
|
63
|
+
flushChange(newValue, eventCopy as TextFieldChangeEvent);
|
|
62
64
|
}, 150);
|
|
63
65
|
}, [flushChange]);
|
|
64
66
|
|
|
65
|
-
const internalOnBlur = useCallback((event: React.FocusEvent<
|
|
66
|
-
flushChange(internalValue, event as
|
|
67
|
+
const internalOnBlur = useCallback((event: React.FocusEvent<HTMLInputElement>) => {
|
|
68
|
+
flushChange(internalValue, event as TextFieldChangeEvent);
|
|
67
69
|
props.onBlur?.(event);
|
|
68
70
|
}, [internalValue, flushChange, props.onBlur]);
|
|
69
71
|
|
|
70
72
|
return <TextField {...props}
|
|
71
73
|
onChange={internalOnChange}
|
|
72
74
|
onBlur={internalOnBlur}
|
|
73
|
-
value={internalValue as T}
|
|
75
|
+
value={internalValue as T}/>;
|
|
74
76
|
}
|
|
@@ -26,6 +26,7 @@ export type DialogProps = {
|
|
|
26
26
|
*/
|
|
27
27
|
disableInitialFocus?: boolean;
|
|
28
28
|
portalContainer?: HTMLElement | null;
|
|
29
|
+
"aria-describedby"?: string;
|
|
29
30
|
};
|
|
30
31
|
|
|
31
32
|
const widthClasses = {
|
|
@@ -60,7 +61,8 @@ export const Dialog = ({
|
|
|
60
61
|
onPointerDownOutside,
|
|
61
62
|
onInteractOutside,
|
|
62
63
|
disableInitialFocus = true,
|
|
63
|
-
portalContainer
|
|
64
|
+
portalContainer,
|
|
65
|
+
"aria-describedby": ariaDescribedby
|
|
64
66
|
}: DialogProps) => {
|
|
65
67
|
const [displayed, setDisplayed] = useState(false);
|
|
66
68
|
|
|
@@ -92,7 +94,7 @@ export const Dialog = ({
|
|
|
92
94
|
<div className={cls("fixed inset-0 z-50", containerClassName)}>
|
|
93
95
|
|
|
94
96
|
<DialogPrimitive.Overlay
|
|
95
|
-
className={cls("fixed inset-0 transition-opacity ease-in-out duration-200 bg-black dark:bg-
|
|
97
|
+
className={cls("fixed inset-0 transition-opacity ease-in-out duration-200 bg-black/50 dark:bg-black/60 backdrop-blur-sm",
|
|
96
98
|
displayed && open ? "opacity-100" : "opacity-0",
|
|
97
99
|
"z-50 fixed top-0 left-0 w-full h-full flex justify-center items-center"
|
|
98
100
|
)}
|
|
@@ -111,7 +113,8 @@ export const Dialog = ({
|
|
|
111
113
|
}}
|
|
112
114
|
onPointerDownOutside={onPointerDownOutside}
|
|
113
115
|
onInteractOutside={onInteractOutside}
|
|
114
|
-
|
|
116
|
+
aria-describedby={ariaDescribedby}
|
|
117
|
+
className={cls("relative h-full outline-none flex justify-center items-center z-60 opacity-100 transition-all duration-200 ease-in-out")}
|
|
115
118
|
>
|
|
116
119
|
<div
|
|
117
120
|
className={cls(paperMixin,
|
|
@@ -48,7 +48,8 @@ interface ErrorBoundaryState {
|
|
|
48
48
|
export class ErrorBoundary extends React.Component<PropsWithChildren<ErrorBoundaryProps>, ErrorBoundaryState> {
|
|
49
49
|
constructor(props: PropsWithChildren<ErrorBoundaryProps>) {
|
|
50
50
|
super(props);
|
|
51
|
-
this.state = { error: null,
|
|
51
|
+
this.state = { error: null,
|
|
52
|
+
showDetails: false };
|
|
52
53
|
}
|
|
53
54
|
|
|
54
55
|
static getDerivedStateFromError(error: Error) {
|
|
@@ -60,7 +61,8 @@ export class ErrorBoundary extends React.Component<PropsWithChildren<ErrorBounda
|
|
|
60
61
|
}
|
|
61
62
|
|
|
62
63
|
private handleReset = () => {
|
|
63
|
-
this.setState({ error: null,
|
|
64
|
+
this.setState({ error: null,
|
|
65
|
+
showDetails: false });
|
|
64
66
|
this.props.onReset?.();
|
|
65
67
|
};
|
|
66
68
|
|
package/src/components/Table.tsx
CHANGED
|
@@ -137,6 +137,14 @@ export const TableCell = React.memo(({
|
|
|
137
137
|
);
|
|
138
138
|
});
|
|
139
139
|
|
|
140
|
+
interface ReactFiber {
|
|
141
|
+
return: ReactFiber;
|
|
142
|
+
type: string | Function;
|
|
143
|
+
elementType?: {
|
|
144
|
+
name?: string;
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
140
148
|
// This is highly experimental and might break in the future
|
|
141
149
|
function getParentName(element: HTMLElement | null): string | undefined {
|
|
142
150
|
if (element) {
|
|
@@ -146,19 +154,19 @@ function getParentName(element: HTMLElement | null): string | undefined {
|
|
|
146
154
|
key.startsWith("__reactInternalInstance$")
|
|
147
155
|
);
|
|
148
156
|
});
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
157
|
+
if (key) {
|
|
158
|
+
const domFiber = (element as unknown as Record<string, ReactFiber>)[key];
|
|
159
|
+
const getComponentFiber = (fiber: ReactFiber): ReactFiber => {
|
|
160
|
+
let parentFiber = fiber.return;
|
|
161
|
+
while (parentFiber && typeof parentFiber.type === "string") {
|
|
162
|
+
parentFiber = parentFiber.return;
|
|
163
|
+
}
|
|
164
|
+
return parentFiber;
|
|
165
|
+
};
|
|
166
|
+
let fiber = getComponentFiber(domFiber);
|
|
167
|
+
fiber = getComponentFiber(fiber);
|
|
168
|
+
return fiber?.elementType?.name;
|
|
169
|
+
}
|
|
162
170
|
}
|
|
163
171
|
return undefined;
|
|
164
172
|
}
|
|
@@ -53,7 +53,7 @@ export type TextFieldProps<T extends string | number> = {
|
|
|
53
53
|
* @default 1
|
|
54
54
|
*/
|
|
55
55
|
minRows?: number | string;
|
|
56
|
-
} & Omit<React.InputHTMLAttributes<HTMLInputElement>, "size">;
|
|
56
|
+
} & Omit<React.InputHTMLAttributes<HTMLInputElement>, "size" | "value">;
|
|
57
57
|
|
|
58
58
|
export const TextField = forwardRef<HTMLDivElement, TextFieldProps<string | number>>(
|
|
59
59
|
<T extends string | number>(
|
|
@@ -92,8 +92,8 @@ export const TextField = forwardRef<HTMLDivElement, TextFieldProps<string | numb
|
|
|
92
92
|
const hasValue = value !== undefined && value !== null && value !== "";
|
|
93
93
|
|
|
94
94
|
useEffect(() => {
|
|
95
|
-
|
|
96
|
-
if (
|
|
95
|
+
const element = inputRef && "current" in inputRef ? inputRef.current : null;
|
|
96
|
+
if (element && document.activeElement === element) {
|
|
97
97
|
setFocused(true);
|
|
98
98
|
}
|
|
99
99
|
}, []);
|
|
@@ -65,7 +65,8 @@ export const Tooltip = ({
|
|
|
65
65
|
|
|
66
66
|
return (
|
|
67
67
|
<TooltipPrimitive.Provider delayDuration={delayDuration}>
|
|
68
|
-
<TooltipPrimitive.Root {...(open !== undefined ? { open,
|
|
68
|
+
<TooltipPrimitive.Root {...(open !== undefined ? { open,
|
|
69
|
+
onOpenChange } : {})} defaultOpen={defaultOpen}>
|
|
69
70
|
{trigger}
|
|
70
71
|
<TooltipPrimitive.Portal container={finalContainer}>
|
|
71
72
|
<TooltipPrimitive.Content
|
|
@@ -9,7 +9,7 @@ import { VirtualTableProps } from "./VirtualTableProps";
|
|
|
9
9
|
// Mock VirtualTableRow to track renders
|
|
10
10
|
const renderCallback = jest.fn();
|
|
11
11
|
jest.mock("./VirtualTableRow", () => {
|
|
12
|
-
|
|
12
|
+
|
|
13
13
|
const React = require("react");
|
|
14
14
|
// Ensure it's memoized as in the real component, otherwise it would re-render anyway if parent renders
|
|
15
15
|
// But wait, if parent passes new props, it re-renders.
|
|
@@ -4,7 +4,6 @@ import React, { createContext, forwardRef, RefObject, useCallback, useEffect, us
|
|
|
4
4
|
|
|
5
5
|
import { deepEqual as equal } from "fast-equals"
|
|
6
6
|
|
|
7
|
-
// @ts-expect-error -- react-window does not ship type declarations and @types/react-window is not installed
|
|
8
7
|
import { FixedSizeList as List } from "react-window";
|
|
9
8
|
import useMeasure from "react-use-measure";
|
|
10
9
|
|
|
@@ -320,7 +319,7 @@ export const VirtualTable = React.memo<VirtualTableProps<Record<string, unknown>
|
|
|
320
319
|
}, [checkFilterCombination, currentSort, onFilterUpdate, sortByProperty]);
|
|
321
320
|
|
|
322
321
|
const empty = !loading && (data?.length ?? 0) === 0;
|
|
323
|
-
const customView = error
|
|
322
|
+
const customView = (error && (data?.length ?? 0) === 0)
|
|
324
323
|
? <CenteredView maxWidth={"2xl"}
|
|
325
324
|
className="flex flex-col gap-2">
|
|
326
325
|
|
|
@@ -110,10 +110,21 @@ export const VirtualTableHeader = React.memo<VirtualTableHeaderProps<Record<stri
|
|
|
110
110
|
<div
|
|
111
111
|
className={`flex items-center justify-${column.headerAlign} flex-row`}>
|
|
112
112
|
|
|
113
|
-
{column.icon
|
|
113
|
+
{column.icon && (
|
|
114
|
+
<div className="flex-shrink-0 flex items-center justify-center mr-1">
|
|
115
|
+
{React.isValidElement(column.icon) ? (
|
|
116
|
+
React.cloneElement(column.icon as React.ReactElement<any>, {
|
|
117
|
+
size: 14,
|
|
118
|
+
className: cls((column.icon as React.ReactElement<any>).props?.className, "flex-shrink-0")
|
|
119
|
+
})
|
|
120
|
+
) : (
|
|
121
|
+
column.icon
|
|
122
|
+
)}
|
|
123
|
+
</div>
|
|
124
|
+
)}
|
|
114
125
|
|
|
115
126
|
<div
|
|
116
|
-
className="truncate -webkit-box w-full
|
|
127
|
+
className="truncate -webkit-box w-full mr-1 overflow-hidden"
|
|
117
128
|
style={{
|
|
118
129
|
WebkitBoxOrient: "vertical",
|
|
119
130
|
WebkitLineClamp: 2,
|
|
@@ -130,27 +141,29 @@ export const VirtualTableHeader = React.memo<VirtualTableHeaderProps<Record<stri
|
|
|
130
141
|
<AdditionalHeaderWidget onHover={onHover || openFilter}/>}
|
|
131
142
|
|
|
132
143
|
{column.sortable && (sort || hovered || openFilter) &&
|
|
133
|
-
<
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
144
|
+
<div className="flex-shrink-0">
|
|
145
|
+
<Badge color="secondary"
|
|
146
|
+
invisible={!sort}>
|
|
147
|
+
<IconButton
|
|
148
|
+
size={"small"}
|
|
149
|
+
className={onHover || openFilter ? "bg-white dark:bg-surface-900" : undefined}
|
|
150
|
+
onClick={() => {
|
|
151
|
+
onColumnSort(column.key as Extract<keyof M, string>);
|
|
152
|
+
}}
|
|
153
|
+
>
|
|
154
|
+
{!sort &&
|
|
155
|
+
<ArrowUpIcon size={14} className="flex-shrink-0" />}
|
|
156
|
+
{sort === "asc" &&
|
|
157
|
+
<ArrowUpIcon size={14} className="flex-shrink-0" />}
|
|
158
|
+
{sort === "desc" &&
|
|
159
|
+
<ArrowUpIcon size={14} className="flex-shrink-0 rotate-180" />}
|
|
160
|
+
</IconButton>
|
|
161
|
+
</Badge>
|
|
162
|
+
</div>
|
|
150
163
|
}
|
|
151
164
|
</>
|
|
152
165
|
|
|
153
|
-
{column.filter && createFilterField && <div>
|
|
166
|
+
{column.filter && createFilterField && <div className="flex-shrink-0">
|
|
154
167
|
<Badge color="secondary"
|
|
155
168
|
invisible={!filter}>
|
|
156
169
|
|
|
@@ -164,7 +177,7 @@ export const VirtualTableHeader = React.memo<VirtualTableHeaderProps<Record<stri
|
|
|
164
177
|
className={onHover || openFilter ? "bg-white dark:bg-surface-900" : undefined}
|
|
165
178
|
size={"small"}
|
|
166
179
|
onClick={handleSettingsClick}>
|
|
167
|
-
<FilterIcon size={
|
|
180
|
+
<FilterIcon size={14} className="flex-shrink-0"/>
|
|
168
181
|
</IconButton>}
|
|
169
182
|
>
|
|
170
183
|
<FilterForm column={column}
|
package/src/icons/Icon.tsx
CHANGED
package/src/icons/icon_keys.ts
CHANGED
package/src/icons/index.ts
CHANGED
|
@@ -20,7 +20,7 @@ const SIZE_MAP = {
|
|
|
20
20
|
smallest: 16,
|
|
21
21
|
small: 20,
|
|
22
22
|
medium: 24,
|
|
23
|
-
large: 28
|
|
23
|
+
large: 28
|
|
24
24
|
};
|
|
25
25
|
|
|
26
26
|
const files = process.argv.slice(2);
|
|
@@ -46,21 +46,21 @@ for (const filePath of files) {
|
|
|
46
46
|
// We replace size={"small"} -> size={20} only when it appears on a lucide icon line
|
|
47
47
|
const patterns = [
|
|
48
48
|
`size={"${sizeStr}"}`,
|
|
49
|
-
`size="${sizeStr}"
|
|
49
|
+
`size="${sizeStr}"`
|
|
50
50
|
];
|
|
51
|
-
|
|
51
|
+
|
|
52
52
|
for (const pattern of patterns) {
|
|
53
53
|
const replacement = `size={${sizeNum}}`;
|
|
54
|
-
|
|
54
|
+
|
|
55
55
|
// Process line by line to check context
|
|
56
56
|
const lines = modified.split("\n");
|
|
57
57
|
for (let i = 0; i < lines.length; i++) {
|
|
58
58
|
const line = lines[i];
|
|
59
59
|
if (!line.includes(pattern)) continue;
|
|
60
|
-
|
|
60
|
+
|
|
61
61
|
// Skip if this is IconButton, CircularProgress, or other non-lucide components
|
|
62
62
|
if (/\<(IconButton|CircularProgress|Icon |AIIcon|FileIcon|IconForView|CollectionSize)/.test(line)) continue;
|
|
63
|
-
|
|
63
|
+
|
|
64
64
|
// Check if this line contains a lucide-style icon component (PascalCase ending with Icon)
|
|
65
65
|
if (/\<[A-Z][a-zA-Z0-9]+Icon/.test(line)) {
|
|
66
66
|
const newLine = line.replace(pattern, replacement);
|
|
@@ -80,7 +80,7 @@ for (const filePath of files) {
|
|
|
80
80
|
// Add iconSize import — but actually we're using raw numbers, not iconSize references
|
|
81
81
|
// So no import needed. The migration from previous session already set up the constants.
|
|
82
82
|
}
|
|
83
|
-
|
|
83
|
+
|
|
84
84
|
fs.writeFileSync(filePath, modified, "utf8");
|
|
85
85
|
console.log(`✅ ${path.relative(process.cwd(), filePath)}: ${fileReplacements} replacement(s)`);
|
|
86
86
|
totalReplacements += fileReplacements;
|
|
@@ -16,7 +16,7 @@ const SIZE_MAP = {
|
|
|
16
16
|
16: "iconSize.smallest",
|
|
17
17
|
20: "iconSize.small",
|
|
18
18
|
24: "iconSize.medium",
|
|
19
|
-
28: "iconSize.large"
|
|
19
|
+
28: "iconSize.large"
|
|
20
20
|
};
|
|
21
21
|
|
|
22
22
|
// Regex to match lucide icon components with raw numeric sizes
|
|
@@ -71,7 +71,7 @@ for (const filePath of files) {
|
|
|
71
71
|
if (!content.includes("iconSize")) {
|
|
72
72
|
// This shouldn't happen since we just added it, but safety check
|
|
73
73
|
}
|
|
74
|
-
|
|
74
|
+
|
|
75
75
|
if (content.includes("iconSize") && !content.match(/import\s.*iconSize/)) {
|
|
76
76
|
// Try to add to existing @rebasepro/ui import
|
|
77
77
|
const uiImportRe = /import\s*\{([^}]+)\}\s*from\s*["']@rebasepro\/ui["']/;
|