@firecms/ui 3.2.0-canary.9c3d298 → 3.3.0-canary.040c21c
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/dist/components/Autocomplete.d.ts +1 -1
- package/dist/components/BooleanSwitchWithLabel.d.ts +2 -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/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 +1 -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/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 +16 -0
- package/dist/components/SearchBar.d.ts +1 -1
- package/dist/components/SearchableSelect.d.ts +48 -0
- package/dist/components/Select.d.ts +1 -1
- 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 +9 -2
- package/dist/components/ToggleButtonGroup.d.ts +1 -1
- package/dist/components/Tooltip.d.ts +18 -2
- package/dist/components/Typography.d.ts +1 -1
- package/dist/components/common/SelectInputLabel.d.ts +1 -1
- package/dist/components/index.d.ts +2 -0
- package/dist/hooks/PortalContainerContext.d.ts +1 -1
- package/dist/icons/FirestoreIcon.d.ts +7 -0
- package/dist/icons/GitHubIcon.d.ts +2 -1
- package/dist/icons/HandleIcon.d.ts +1 -1
- package/dist/icons/components/DatabaseIcon.d.ts +6 -0
- package/dist/icons/index.d.ts +2 -0
- package/dist/index.es.js +1376 -476
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +1378 -478
- package/dist/index.umd.js.map +1 -1
- package/package.json +3 -3
- package/src/components/BooleanSwitchWithLabel.tsx +4 -0
- package/src/components/Button.tsx +2 -1
- package/src/components/Chip.tsx +4 -3
- package/src/components/MultiSelect.tsx +23 -4
- package/src/components/ResizablePanels.tsx +181 -0
- package/src/components/SearchableSelect.tsx +335 -0
- package/src/components/Skeleton.tsx +4 -2
- package/src/components/Tabs.tsx +44 -16
- package/src/components/TextareaAutosize.tsx +90 -215
- package/src/components/Tooltip.tsx +7 -6
- package/src/components/index.tsx +2 -0
- package/src/icons/FirestoreIcon.tsx +47 -0
- package/src/icons/components/DatabaseIcon.tsx +10 -0
- package/src/icons/index.ts +2 -0
|
@@ -14,8 +14,8 @@ export function Skeleton({
|
|
|
14
14
|
}: SkeletonProps) {
|
|
15
15
|
return <span
|
|
16
16
|
style={{
|
|
17
|
-
width: width ? `${width}px` :
|
|
18
|
-
height: height ? `${height}px` :
|
|
17
|
+
width: width !== undefined ? `${width}px` : undefined,
|
|
18
|
+
height: height !== undefined ? `${height}px` : undefined
|
|
19
19
|
}}
|
|
20
20
|
className={
|
|
21
21
|
cls(
|
|
@@ -23,6 +23,8 @@ export function Skeleton({
|
|
|
23
23
|
"bg-surface-accent-200 dark:bg-surface-accent-800 rounded-md",
|
|
24
24
|
"animate-pulse",
|
|
25
25
|
"max-w-full max-h-full",
|
|
26
|
+
width === undefined ? "w-full" : "",
|
|
27
|
+
height === undefined ? "h-3" : "",
|
|
26
28
|
className)
|
|
27
29
|
}/>;
|
|
28
30
|
}
|
package/src/components/Tabs.tsx
CHANGED
|
@@ -1,16 +1,24 @@
|
|
|
1
|
-
import React, { useRef, useState, useEffect } from "react";
|
|
1
|
+
import React, { createContext, useContext, useRef, useState, useEffect } from "react";
|
|
2
2
|
import * as TabsPrimitive from "@radix-ui/react-tabs";
|
|
3
3
|
import { cls } from "../util";
|
|
4
4
|
import { defaultBorderMixin } from "../styles";
|
|
5
5
|
import { IconButton } from "./IconButton";
|
|
6
6
|
import { ChevronLeftIcon, ChevronRightIcon } from "../icons";
|
|
7
7
|
|
|
8
|
+
type TabsMode = "primary" | "secondary";
|
|
9
|
+
const TabsModeContext = createContext<TabsMode>("primary");
|
|
10
|
+
|
|
8
11
|
export type TabsProps = {
|
|
9
12
|
value: string,
|
|
10
13
|
children: React.ReactNode,
|
|
11
14
|
innerClassName?: string,
|
|
12
15
|
className?: string,
|
|
13
|
-
onValueChange: (value: string) => void
|
|
16
|
+
onValueChange: (value: string) => void,
|
|
17
|
+
/**
|
|
18
|
+
* "primary" renders the default pill-style tabs.
|
|
19
|
+
* "secondary" renders underline-style tabs suitable for inner/nested panels.
|
|
20
|
+
*/
|
|
21
|
+
mode?: TabsMode
|
|
14
22
|
};
|
|
15
23
|
|
|
16
24
|
export function Tabs({
|
|
@@ -18,7 +26,8 @@ export function Tabs({
|
|
|
18
26
|
onValueChange,
|
|
19
27
|
className,
|
|
20
28
|
innerClassName,
|
|
21
|
-
children
|
|
29
|
+
children,
|
|
30
|
+
mode = "primary"
|
|
22
31
|
}: TabsProps) {
|
|
23
32
|
const scrollContainerRef = useRef<HTMLDivElement>(null);
|
|
24
33
|
const [showLeftScroll, setShowLeftScroll] = useState(false);
|
|
@@ -67,7 +76,8 @@ export function Tabs({
|
|
|
67
76
|
}
|
|
68
77
|
};
|
|
69
78
|
|
|
70
|
-
return <
|
|
79
|
+
return <TabsModeContext.Provider value={mode}>
|
|
80
|
+
<TabsPrimitive.Root value={value} onValueChange={onValueChange} className={cls("flex flex-row items-center min-w-0", className)}>
|
|
71
81
|
{isScrollable && (
|
|
72
82
|
<button
|
|
73
83
|
type="button"
|
|
@@ -78,7 +88,9 @@ export function Tabs({
|
|
|
78
88
|
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-surface-400",
|
|
79
89
|
"disabled:pointer-events-none disabled:opacity-0",
|
|
80
90
|
"text-surface-600 dark:text-surface-400 hover:bg-surface-200 dark:hover:bg-surface-800",
|
|
81
|
-
"mr-1 bg-surface-50 dark:bg-surface-900 border",
|
|
91
|
+
mode === "primary" && "mr-1 bg-surface-50 dark:bg-surface-900 border",
|
|
92
|
+
mode === "primary" && defaultBorderMixin,
|
|
93
|
+
mode === "secondary" && "mr-1"
|
|
82
94
|
)}
|
|
83
95
|
>
|
|
84
96
|
<ChevronLeftIcon size="small" />
|
|
@@ -91,10 +103,10 @@ export function Tabs({
|
|
|
91
103
|
style={{ scrollbarWidth: "none", msOverflowStyle: "none" }}
|
|
92
104
|
>
|
|
93
105
|
<TabsPrimitive.List className={cls(
|
|
94
|
-
"border",
|
|
95
|
-
defaultBorderMixin,
|
|
96
|
-
"gap-2",
|
|
97
|
-
"inline-flex h-
|
|
106
|
+
mode === "primary" && "border",
|
|
107
|
+
mode === "primary" && defaultBorderMixin,
|
|
108
|
+
mode === "primary" && "gap-2 inline-flex h-10 items-center justify-center rounded-md bg-surface-50 p-1 text-surface-600 dark:bg-surface-900 dark:text-surface-400",
|
|
109
|
+
mode === "secondary" && "gap-1 inline-flex h-9 items-center text-surface-500 dark:text-surface-400",
|
|
98
110
|
innerClassName)
|
|
99
111
|
}>
|
|
100
112
|
{children}
|
|
@@ -110,13 +122,16 @@ export function Tabs({
|
|
|
110
122
|
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-surface-400",
|
|
111
123
|
"disabled:pointer-events-none disabled:opacity-0",
|
|
112
124
|
"text-surface-600 dark:text-surface-400 hover:bg-surface-200 dark:hover:bg-surface-800",
|
|
113
|
-
"ml-1 bg-surface-50 dark:bg-surface-900 border",
|
|
125
|
+
mode === "primary" && "ml-1 bg-surface-50 dark:bg-surface-900 border",
|
|
126
|
+
mode === "primary" && defaultBorderMixin,
|
|
127
|
+
mode === "secondary" && "ml-1"
|
|
114
128
|
)}
|
|
115
129
|
>
|
|
116
130
|
<ChevronRightIcon size="small" />
|
|
117
131
|
</button>
|
|
118
132
|
)}
|
|
119
133
|
</TabsPrimitive.Root>
|
|
134
|
+
</TabsModeContext.Provider>
|
|
120
135
|
}
|
|
121
136
|
|
|
122
137
|
export type TabProps = {
|
|
@@ -134,15 +149,28 @@ export function Tab({
|
|
|
134
149
|
children,
|
|
135
150
|
disabled
|
|
136
151
|
}: TabProps) {
|
|
152
|
+
const mode = useContext(TabsModeContext);
|
|
153
|
+
|
|
154
|
+
const primaryClasses = cls(
|
|
155
|
+
"inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-white transition-all",
|
|
156
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-surface-400 focus-visible:ring-offset-2",
|
|
157
|
+
"disabled:pointer-events-none disabled:opacity-50",
|
|
158
|
+
"data-[state=active]:bg-white data-[state=active]:text-surface-900 dark:data-[state=active]:bg-surface-950 dark:data-[state=active]:text-surface-50",
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
const secondaryClasses = cls(
|
|
162
|
+
"inline-flex items-center justify-center whitespace-nowrap px-3 py-1.5 text-sm font-medium transition-all",
|
|
163
|
+
"border-b-2 border-transparent -mb-px",
|
|
164
|
+
"focus-visible:outline-none",
|
|
165
|
+
"disabled:pointer-events-none disabled:opacity-50",
|
|
166
|
+
"hover:text-surface-700 dark:hover:text-surface-300",
|
|
167
|
+
"data-[state=active]:border-b-primary data-[state=active]:text-primary dark:data-[state=active]:border-b-primary dark:data-[state=active]:text-primary-dark",
|
|
168
|
+
);
|
|
169
|
+
|
|
137
170
|
return <TabsPrimitive.Trigger value={value}
|
|
138
171
|
disabled={disabled}
|
|
139
172
|
className={cls(
|
|
140
|
-
|
|
141
|
-
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-surface-400 focus-visible:ring-offset-2",
|
|
142
|
-
"disabled:pointer-events-none disabled:opacity-50",
|
|
143
|
-
"data-[state=active]:bg-white data-[state=active]:text-surface-900 dark:data-[state=active]:bg-surface-950 dark:data-[state=active]:text-surface-50",
|
|
144
|
-
// "data-[state=active]:border",
|
|
145
|
-
// defaultBorderMixin,
|
|
173
|
+
mode === "secondary" ? secondaryClasses : primaryClasses,
|
|
146
174
|
className,
|
|
147
175
|
innerClassName)}>
|
|
148
176
|
{children}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
import { useLayoutEffect } from "react";
|
|
4
|
-
import
|
|
5
|
-
import { cls, debounce } from "../util";
|
|
4
|
+
import { debounce } from "../util";
|
|
6
5
|
|
|
7
6
|
type State = {
|
|
8
7
|
outerHeightStyle: number;
|
|
@@ -13,33 +12,6 @@ function getStyleValue(value: string) {
|
|
|
13
12
|
return parseInt(value, 10) || 0;
|
|
14
13
|
}
|
|
15
14
|
|
|
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
15
|
export const TextareaAutosize = React.forwardRef(function TextareaAutosize(
|
|
44
16
|
props: TextareaAutosizeProps,
|
|
45
17
|
ref: React.ForwardedRef<Element>
|
|
@@ -60,166 +32,106 @@ export const TextareaAutosize = React.forwardRef(function TextareaAutosize(
|
|
|
60
32
|
} = props;
|
|
61
33
|
|
|
62
34
|
const { current: isControlled } = React.useRef(value != null);
|
|
63
|
-
const inputRef = React.useRef<
|
|
35
|
+
const inputRef = React.useRef<HTMLTextAreaElement>(null);
|
|
64
36
|
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
37
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
38
|
+
const syncHeight = React.useCallback(() => {
|
|
39
|
+
const el = inputRef.current;
|
|
40
|
+
if (!el || typeof window === "undefined") return;
|
|
41
|
+
if (el.offsetWidth === 0) return;
|
|
42
|
+
|
|
43
|
+
const cs = window.getComputedStyle(el);
|
|
44
|
+
const paddingY =
|
|
45
|
+
getStyleValue(cs.paddingTop) + getStyleValue(cs.paddingBottom);
|
|
46
|
+
const borderY =
|
|
47
|
+
getStyleValue(cs.borderTopWidth) + getStyleValue(cs.borderBottomWidth);
|
|
48
|
+
const boxSizing = cs.boxSizing;
|
|
49
|
+
|
|
50
|
+
// ── measure by temporarily collapsing the real element ──
|
|
51
|
+
const prevHeight = el.style.height;
|
|
52
|
+
const prevOverflow = el.style.overflowY;
|
|
53
|
+
el.style.overflowY = "hidden";
|
|
54
|
+
el.style.height = "0px";
|
|
55
|
+
|
|
56
|
+
// scrollHeight = content + padding (always, regardless of box-sizing)
|
|
57
|
+
const scrollH = el.scrollHeight;
|
|
58
|
+
|
|
59
|
+
// Measure single-row height for minRows / maxRows
|
|
60
|
+
// Save cursor position — directly setting el.value resets it.
|
|
61
|
+
const selStart = el.selectionStart;
|
|
62
|
+
const selEnd = el.selectionEnd;
|
|
63
|
+
|
|
64
|
+
const prevValue = el.value;
|
|
65
|
+
el.value = "x";
|
|
66
|
+
const singleRowScrollH = el.scrollHeight;
|
|
67
|
+
el.value = prevValue;
|
|
68
|
+
|
|
69
|
+
// Restore immediately — all of this happens before paint (useLayoutEffect)
|
|
70
|
+
el.style.height = prevHeight;
|
|
71
|
+
el.style.overflowY = prevOverflow;
|
|
72
|
+
|
|
73
|
+
// Restore cursor position after value manipulation
|
|
74
|
+
if (document.activeElement === el) {
|
|
75
|
+
el.selectionStart = selStart;
|
|
76
|
+
el.selectionEnd = selEnd;
|
|
78
77
|
}
|
|
79
78
|
|
|
80
|
-
const
|
|
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
|
-
}
|
|
79
|
+
const lineHeight = singleRowScrollH - paddingY;
|
|
89
80
|
|
|
90
|
-
|
|
91
|
-
const inputShallow = shadowRef.current!;
|
|
81
|
+
let targetHeight = scrollH; // includes padding
|
|
92
82
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
// is empty. Make it non-empty to avoid this issue.
|
|
99
|
-
inputShallow.value += " ";
|
|
83
|
+
if (minRows) {
|
|
84
|
+
targetHeight = Math.max(
|
|
85
|
+
Number(minRows) * lineHeight + paddingY,
|
|
86
|
+
targetHeight
|
|
87
|
+
);
|
|
100
88
|
}
|
|
101
89
|
|
|
102
|
-
const
|
|
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;
|
|
90
|
+
const unclampedHeight = targetHeight;
|
|
115
91
|
|
|
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
92
|
if (maxRows) {
|
|
123
|
-
|
|
93
|
+
targetHeight = Math.min(
|
|
94
|
+
Number(maxRows) * lineHeight + paddingY,
|
|
95
|
+
targetHeight
|
|
96
|
+
);
|
|
124
97
|
}
|
|
125
|
-
outerHeight = Math.max(outerHeight, singleRowHeight, minHeight);
|
|
126
98
|
|
|
127
|
-
//
|
|
128
|
-
|
|
99
|
+
// For border-box, height CSS prop = content + padding + border.
|
|
100
|
+
// scrollHeight already includes padding, so only add border.
|
|
101
|
+
const extra =
|
|
102
|
+
!ignoreBoxSizing && boxSizing === "border-box" ? borderY : 0;
|
|
103
|
+
const finalHeight = Math.ceil(targetHeight + extra);
|
|
129
104
|
|
|
130
|
-
const
|
|
105
|
+
const shouldScroll =
|
|
106
|
+
Math.abs(unclampedHeight - targetHeight) > 1;
|
|
131
107
|
|
|
132
|
-
|
|
133
|
-
|
|
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();
|
|
108
|
+
el.style.height = `${finalHeight}px`;
|
|
109
|
+
el.style.overflowY = shouldScroll ? "auto" : "hidden";
|
|
172
110
|
|
|
173
|
-
if (isEmpty(newState)) {
|
|
174
|
-
return;
|
|
175
|
-
}
|
|
176
111
|
if (onResize) {
|
|
177
|
-
onResize(
|
|
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;
|
|
112
|
+
onResize({ outerHeightStyle: finalHeight, overflow: !shouldScroll });
|
|
190
113
|
}
|
|
114
|
+
}, [maxRows, minRows, ignoreBoxSizing, onResize]);
|
|
191
115
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
setState((prevState) => {
|
|
197
|
-
return updateState(prevState, newState);
|
|
198
|
-
});
|
|
199
|
-
});
|
|
200
|
-
}, [getUpdatedState, updateState]);
|
|
116
|
+
// ── sync on every layout ──
|
|
117
|
+
useLayoutEffect(() => {
|
|
118
|
+
syncHeight();
|
|
119
|
+
});
|
|
201
120
|
|
|
121
|
+
// ── sync on window resize / element resize ──
|
|
202
122
|
React.useEffect(() => {
|
|
203
123
|
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
124
|
if (inputRef.current) {
|
|
210
|
-
|
|
125
|
+
syncHeight();
|
|
211
126
|
}
|
|
212
127
|
});
|
|
213
|
-
let resizeObserver: ResizeObserver;
|
|
214
128
|
|
|
215
129
|
const input = inputRef.current!;
|
|
216
|
-
|
|
217
|
-
if (typeof window === "undefined") {
|
|
218
|
-
return;
|
|
219
|
-
}
|
|
130
|
+
if (typeof window === "undefined") return;
|
|
220
131
|
|
|
221
|
-
|
|
132
|
+
window.addEventListener("resize", handleResize);
|
|
222
133
|
|
|
134
|
+
let resizeObserver: ResizeObserver | undefined;
|
|
223
135
|
if (typeof ResizeObserver !== "undefined") {
|
|
224
136
|
resizeObserver = new ResizeObserver(handleResize);
|
|
225
137
|
resizeObserver.observe(input);
|
|
@@ -227,67 +139,35 @@ export const TextareaAutosize = React.forwardRef(function TextareaAutosize(
|
|
|
227
139
|
|
|
228
140
|
return () => {
|
|
229
141
|
handleResize.clear();
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
resizeObserver.disconnect();
|
|
233
|
-
}
|
|
142
|
+
window.removeEventListener("resize", handleResize);
|
|
143
|
+
resizeObserver?.disconnect();
|
|
234
144
|
};
|
|
235
|
-
}, [
|
|
236
|
-
|
|
237
|
-
useLayoutEffect(() => {
|
|
238
|
-
syncHeight();
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
React.useEffect(() => {
|
|
242
|
-
renders.current = 0;
|
|
243
|
-
}, [value]);
|
|
145
|
+
}, [syncHeight]);
|
|
244
146
|
|
|
245
147
|
const handleChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => {
|
|
246
|
-
renders.current = 0;
|
|
247
|
-
|
|
248
148
|
if (!isControlled) {
|
|
249
149
|
syncHeight();
|
|
250
150
|
}
|
|
251
|
-
|
|
252
151
|
if (onChange) {
|
|
253
152
|
onChange(event);
|
|
254
153
|
}
|
|
255
154
|
};
|
|
256
155
|
|
|
257
156
|
return (
|
|
258
|
-
<
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
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>
|
|
157
|
+
<textarea
|
|
158
|
+
value={value}
|
|
159
|
+
onChange={handleChange}
|
|
160
|
+
className={props.className}
|
|
161
|
+
ref={handleRef}
|
|
162
|
+
onFocus={onFocus}
|
|
163
|
+
onBlur={onBlur}
|
|
164
|
+
rows={minRows as number}
|
|
165
|
+
style={{
|
|
166
|
+
...style,
|
|
167
|
+
}}
|
|
168
|
+
onScroll={onScroll}
|
|
169
|
+
{...other}
|
|
170
|
+
/>
|
|
291
171
|
);
|
|
292
172
|
}) as React.FC<TextareaAutosizeProps & { ref?: React.ForwardedRef<Element> }>;
|
|
293
173
|
|
|
@@ -337,11 +217,6 @@ export type TextareaAutosizeProps = Omit<React.InputHTMLAttributes<HTMLTextAreaE
|
|
|
337
217
|
function useForkRef<Instance>(
|
|
338
218
|
...refs: Array<React.Ref<Instance> | undefined>
|
|
339
219
|
): 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
220
|
return React.useMemo(() => {
|
|
346
221
|
if (refs.every((ref) => ref == null)) {
|
|
347
222
|
return null;
|
|
@@ -22,9 +22,9 @@ export type TooltipProps = {
|
|
|
22
22
|
className?: string,
|
|
23
23
|
container?: HTMLElement,
|
|
24
24
|
style?: React.CSSProperties;
|
|
25
|
-
}
|
|
25
|
+
} & Omit<React.HTMLAttributes<HTMLDivElement>, "title">;
|
|
26
26
|
|
|
27
|
-
export const Tooltip = ({
|
|
27
|
+
export const Tooltip = React.forwardRef<HTMLDivElement, TooltipProps>(({
|
|
28
28
|
open,
|
|
29
29
|
defaultOpen,
|
|
30
30
|
side = "bottom",
|
|
@@ -39,8 +39,9 @@ export const Tooltip = ({
|
|
|
39
39
|
asChild = false,
|
|
40
40
|
container,
|
|
41
41
|
className,
|
|
42
|
-
style
|
|
43
|
-
|
|
42
|
+
style,
|
|
43
|
+
...props
|
|
44
|
+
}, ref) => {
|
|
44
45
|
|
|
45
46
|
useInjectStyles("Tooltip", styles);
|
|
46
47
|
|
|
@@ -58,7 +59,7 @@ export const Tooltip = ({
|
|
|
58
59
|
{children}
|
|
59
60
|
</TooltipPrimitive.Trigger>
|
|
60
61
|
: <TooltipPrimitive.Trigger asChild={true}>
|
|
61
|
-
<div style={style} className={className}>
|
|
62
|
+
<div style={style} className={className} ref={ref} {...props}>
|
|
62
63
|
{children}
|
|
63
64
|
</div>
|
|
64
65
|
</TooltipPrimitive.Trigger>;
|
|
@@ -83,7 +84,7 @@ export const Tooltip = ({
|
|
|
83
84
|
</TooltipPrimitive.Root>
|
|
84
85
|
</TooltipPrimitive.Provider>
|
|
85
86
|
);
|
|
86
|
-
};
|
|
87
|
+
});
|
|
87
88
|
|
|
88
89
|
const styles = `
|
|
89
90
|
|
package/src/components/index.tsx
CHANGED
|
@@ -30,7 +30,9 @@ export * from "./Menubar";
|
|
|
30
30
|
export * from "./MultiSelect";
|
|
31
31
|
export * from "./Paper";
|
|
32
32
|
export * from "./RadioGroup";
|
|
33
|
+
export * from "./ResizablePanels";
|
|
33
34
|
export * from "./SearchBar";
|
|
35
|
+
export * from "./SearchableSelect";
|
|
34
36
|
export * from "./Select";
|
|
35
37
|
export * from "./Separator";
|
|
36
38
|
export * from "./Slider";
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
import { IconProps } from "./Icon";
|
|
4
|
+
|
|
5
|
+
const sizeMap: Record<string, number> = {
|
|
6
|
+
smallest: 16,
|
|
7
|
+
small: 20,
|
|
8
|
+
medium: 24,
|
|
9
|
+
large: 28,
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Firebase Firestore flame icon (monochrome, uses currentColor).
|
|
14
|
+
* @group Icons
|
|
15
|
+
*/
|
|
16
|
+
export function FirestoreIcon(props: IconProps) {
|
|
17
|
+
const s = typeof props.size === "number"
|
|
18
|
+
? props.size
|
|
19
|
+
: sizeMap[props.size ?? "medium"] ?? 24;
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<svg
|
|
23
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
24
|
+
className={props.className}
|
|
25
|
+
fill={"currentColor"}
|
|
26
|
+
width={s}
|
|
27
|
+
height={s}
|
|
28
|
+
viewBox="0 0 73 91"
|
|
29
|
+
>
|
|
30
|
+
<path
|
|
31
|
+
d="M22.575 87.933A52.16 52.16 0 0034.787 90.513c5.84.204 11.395-1.004 16.359-3.298a70.68 70.68 0 01-15.948-10.013c-2.98 4.778-7.393 8.548-12.623 10.731z"
|
|
32
|
+
opacity=".7"
|
|
33
|
+
/>
|
|
34
|
+
<path
|
|
35
|
+
d="M35.2 77.205c-10.505-9.714-16.878-23.776-16.339-39.2.018-.499.045-1.001.075-1.5a39.51 39.51 0 00-5.866-.855 38.77 38.77 0 00-8.34.997A53.07 53.07 0 00.022 53.236c-.544 15.58 8.884 29.191 22.553 34.697 5.23-2.18 9.642-5.948 12.625-10.728z"
|
|
36
|
+
opacity=".6"
|
|
37
|
+
/>
|
|
38
|
+
<path
|
|
39
|
+
d="M35.2 77.205a31.63 31.63 0 004.096-13.428c.452-12.985-8.278-24.155-20.36-27.273-.03.5-.058 1.002-.076 1.502-.536 15.421 5.835 29.483 16.34 39.199z"
|
|
40
|
+
opacity=".7"
|
|
41
|
+
/>
|
|
42
|
+
<path
|
|
43
|
+
d="M37.944 0a73.99 73.99 0 00-15.603 21.156 72.82 72.82 0 00-3.41 15.349c12.082 3.117 20.812 14.288 20.36 27.275a31.58 31.58 0 01-4.098 13.425 70.76 70.76 0 0015.948 10.013c11.951-5.523 20.43-17.41 20.919-31.467.318-9.11-3.181-17.228-8.126-24.081C58.711 24.424 37.944 0 37.944 0z"
|
|
44
|
+
/>
|
|
45
|
+
</svg>
|
|
46
|
+
);
|
|
47
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Icon, IconProps } from "../Icon";
|
|
3
|
+
/**
|
|
4
|
+
* @group Icons
|
|
5
|
+
*/
|
|
6
|
+
export const DatabaseIcon = React.forwardRef<HTMLSpanElement, IconProps>((props, ref) => {
|
|
7
|
+
return <Icon {...props} iconKey={"database"} ref={ref}/>
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
DatabaseIcon.displayName = "DatabaseIcon";
|
package/src/icons/index.ts
CHANGED
|
@@ -3,6 +3,7 @@ export * from "./cool_icon_keys";
|
|
|
3
3
|
export * from "./Icon";
|
|
4
4
|
export * from "./GitHubIcon";
|
|
5
5
|
export * from "./HandleIcon";
|
|
6
|
+
export * from "./FirestoreIcon";
|
|
6
7
|
export * from "./components/_10kIcon";
|
|
7
8
|
export * from "./components/_10mpIcon";
|
|
8
9
|
export * from "./components/_11mpIcon";
|
|
@@ -522,6 +523,7 @@ export * from "./components/DataThresholdingIcon";
|
|
|
522
523
|
export * from "./components/DataUsageIcon";
|
|
523
524
|
export * from "./components/DatasetIcon";
|
|
524
525
|
export * from "./components/DatasetLinkedIcon";
|
|
526
|
+
export * from "./components/DatabaseIcon";
|
|
525
527
|
export * from "./components/DateRangeIcon";
|
|
526
528
|
export * from "./components/DeblurIcon";
|
|
527
529
|
export * from "./components/DeckIcon";
|