@firecms/ui 3.2.0 → 3.3.0-canary.451aa49
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/BooleanSwitchWithLabel.d.ts +2 -1
- package/dist/components/Chip.d.ts +1 -1
- package/dist/components/ResizablePanels.d.ts +16 -0
- package/dist/components/SearchableSelect.d.ts +48 -0
- package/dist/components/Tabs.d.ts +8 -1
- package/dist/components/Tooltip.d.ts +18 -2
- package/dist/components/index.d.ts +2 -0
- package/dist/icons/FirestoreIcon.d.ts +6 -0
- package/dist/icons/components/DatabaseIcon.d.ts +6 -0
- package/dist/icons/index.d.ts +2 -0
- package/dist/index.es.js +1370 -476
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +1372 -478
- package/dist/index.umd.js.map +1 -1
- package/package.json +2 -2
- 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 +77 -212
- 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,96 @@ 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
|
-
|
|
73
|
-
const input = inputRef.current!;
|
|
74
|
-
if (typeof window === "undefined") {
|
|
75
|
-
return {
|
|
76
|
-
outerHeightStyle: 0
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
37
|
|
|
80
|
-
|
|
81
|
-
const
|
|
38
|
+
const syncHeight = React.useCallback(() => {
|
|
39
|
+
const el = inputRef.current;
|
|
40
|
+
if (!el || typeof window === "undefined") return;
|
|
41
|
+
if (el.offsetWidth === 0) return;
|
|
82
42
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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;
|
|
89
49
|
|
|
90
|
-
|
|
91
|
-
const
|
|
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";
|
|
92
55
|
|
|
93
|
-
|
|
94
|
-
|
|
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
|
-
}
|
|
56
|
+
// scrollHeight = content + padding (always, regardless of box-sizing)
|
|
57
|
+
const scrollH = el.scrollHeight;
|
|
101
58
|
|
|
102
|
-
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
const minHeight = getStyleValue(computedStyle.minHeight);
|
|
59
|
+
// Measure single-row height for minRows / maxRows
|
|
60
|
+
const prevValue = el.value;
|
|
61
|
+
el.value = "x";
|
|
62
|
+
const singleRowScrollH = el.scrollHeight;
|
|
63
|
+
el.value = prevValue;
|
|
108
64
|
|
|
109
|
-
//
|
|
110
|
-
|
|
65
|
+
// Restore immediately — all of this happens before paint (useLayoutEffect)
|
|
66
|
+
el.style.height = prevHeight;
|
|
67
|
+
el.style.overflowY = prevOverflow;
|
|
111
68
|
|
|
112
|
-
|
|
113
|
-
inputShallow.value = "x";
|
|
114
|
-
const singleRowHeight = sizeReferenceElement.scrollHeight;
|
|
69
|
+
const lineHeight = singleRowScrollH - paddingY;
|
|
115
70
|
|
|
116
|
-
|
|
117
|
-
let outerHeight = innerHeight;
|
|
71
|
+
let targetHeight = scrollH; // includes padding
|
|
118
72
|
|
|
119
73
|
if (minRows) {
|
|
120
|
-
|
|
74
|
+
targetHeight = Math.max(
|
|
75
|
+
Number(minRows) * lineHeight + paddingY,
|
|
76
|
+
targetHeight
|
|
77
|
+
);
|
|
121
78
|
}
|
|
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
79
|
|
|
130
|
-
const
|
|
80
|
+
const unclampedHeight = targetHeight;
|
|
131
81
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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
|
-
}
|
|
82
|
+
if (maxRows) {
|
|
83
|
+
targetHeight = Math.min(
|
|
84
|
+
Number(maxRows) * lineHeight + paddingY,
|
|
85
|
+
targetHeight
|
|
86
|
+
);
|
|
166
87
|
}
|
|
167
|
-
return prevState;
|
|
168
|
-
}, []);
|
|
169
88
|
|
|
170
|
-
|
|
171
|
-
|
|
89
|
+
// For border-box, height CSS prop = content + padding + border.
|
|
90
|
+
// scrollHeight already includes padding, so only add border.
|
|
91
|
+
const extra =
|
|
92
|
+
!ignoreBoxSizing && boxSizing === "border-box" ? borderY : 0;
|
|
93
|
+
const finalHeight = Math.ceil(targetHeight + extra);
|
|
172
94
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
}
|
|
176
|
-
if (onResize) {
|
|
177
|
-
onResize(newState);
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
setState((prevState) => {
|
|
181
|
-
return updateState(prevState, newState);
|
|
182
|
-
});
|
|
183
|
-
}, [getUpdatedState, onResize, updateState]);
|
|
95
|
+
const shouldScroll =
|
|
96
|
+
Math.abs(unclampedHeight - targetHeight) > 1;
|
|
184
97
|
|
|
185
|
-
|
|
186
|
-
|
|
98
|
+
el.style.height = `${finalHeight}px`;
|
|
99
|
+
el.style.overflowY = shouldScroll ? "auto" : "hidden";
|
|
187
100
|
|
|
188
|
-
if (
|
|
189
|
-
|
|
101
|
+
if (onResize) {
|
|
102
|
+
onResize({ outerHeightStyle: finalHeight, overflow: !shouldScroll });
|
|
190
103
|
}
|
|
104
|
+
}, [maxRows, minRows, ignoreBoxSizing, onResize]);
|
|
191
105
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
setState((prevState) => {
|
|
197
|
-
return updateState(prevState, newState);
|
|
198
|
-
});
|
|
199
|
-
});
|
|
200
|
-
}, [getUpdatedState, updateState]);
|
|
106
|
+
// ── sync on every layout ──
|
|
107
|
+
useLayoutEffect(() => {
|
|
108
|
+
syncHeight();
|
|
109
|
+
});
|
|
201
110
|
|
|
111
|
+
// ── sync on window resize / element resize ──
|
|
202
112
|
React.useEffect(() => {
|
|
203
113
|
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
114
|
if (inputRef.current) {
|
|
210
|
-
|
|
115
|
+
syncHeight();
|
|
211
116
|
}
|
|
212
117
|
});
|
|
213
|
-
let resizeObserver: ResizeObserver;
|
|
214
118
|
|
|
215
119
|
const input = inputRef.current!;
|
|
216
|
-
|
|
217
|
-
if (typeof window === "undefined") {
|
|
218
|
-
return;
|
|
219
|
-
}
|
|
120
|
+
if (typeof window === "undefined") return;
|
|
220
121
|
|
|
221
|
-
|
|
122
|
+
window.addEventListener("resize", handleResize);
|
|
222
123
|
|
|
124
|
+
let resizeObserver: ResizeObserver | undefined;
|
|
223
125
|
if (typeof ResizeObserver !== "undefined") {
|
|
224
126
|
resizeObserver = new ResizeObserver(handleResize);
|
|
225
127
|
resizeObserver.observe(input);
|
|
@@ -227,67 +129,35 @@ export const TextareaAutosize = React.forwardRef(function TextareaAutosize(
|
|
|
227
129
|
|
|
228
130
|
return () => {
|
|
229
131
|
handleResize.clear();
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
resizeObserver.disconnect();
|
|
233
|
-
}
|
|
132
|
+
window.removeEventListener("resize", handleResize);
|
|
133
|
+
resizeObserver?.disconnect();
|
|
234
134
|
};
|
|
235
|
-
}, [
|
|
236
|
-
|
|
237
|
-
useLayoutEffect(() => {
|
|
238
|
-
syncHeight();
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
React.useEffect(() => {
|
|
242
|
-
renders.current = 0;
|
|
243
|
-
}, [value]);
|
|
135
|
+
}, [syncHeight]);
|
|
244
136
|
|
|
245
137
|
const handleChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => {
|
|
246
|
-
renders.current = 0;
|
|
247
|
-
|
|
248
138
|
if (!isControlled) {
|
|
249
139
|
syncHeight();
|
|
250
140
|
}
|
|
251
|
-
|
|
252
141
|
if (onChange) {
|
|
253
142
|
onChange(event);
|
|
254
143
|
}
|
|
255
144
|
};
|
|
256
145
|
|
|
257
146
|
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>
|
|
147
|
+
<textarea
|
|
148
|
+
value={value}
|
|
149
|
+
onChange={handleChange}
|
|
150
|
+
className={props.className}
|
|
151
|
+
ref={handleRef}
|
|
152
|
+
onFocus={onFocus}
|
|
153
|
+
onBlur={onBlur}
|
|
154
|
+
rows={minRows as number}
|
|
155
|
+
style={{
|
|
156
|
+
...style,
|
|
157
|
+
}}
|
|
158
|
+
onScroll={onScroll}
|
|
159
|
+
{...other}
|
|
160
|
+
/>
|
|
291
161
|
);
|
|
292
162
|
}) as React.FC<TextareaAutosizeProps & { ref?: React.ForwardedRef<Element> }>;
|
|
293
163
|
|
|
@@ -337,11 +207,6 @@ export type TextareaAutosizeProps = Omit<React.InputHTMLAttributes<HTMLTextAreaE
|
|
|
337
207
|
function useForkRef<Instance>(
|
|
338
208
|
...refs: Array<React.Ref<Instance> | undefined>
|
|
339
209
|
): 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
210
|
return React.useMemo(() => {
|
|
346
211
|
if (refs.every((ref) => ref == null)) {
|
|
347
212
|
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";
|