@firecms/ui 3.2.0 → 3.3.0-canary.102f274

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 (59) hide show
  1. package/dist/components/Autocomplete.d.ts +1 -1
  2. package/dist/components/BooleanSwitchWithLabel.d.ts +2 -1
  3. package/dist/components/Checkbox.d.ts +1 -1
  4. package/dist/components/Chip.d.ts +1 -1
  5. package/dist/components/CircularProgress.d.ts +2 -1
  6. package/dist/components/Collapse.d.ts +1 -1
  7. package/dist/components/ColorPicker.d.ts +2 -1
  8. package/dist/components/DebouncedTextField.d.ts +2 -1
  9. package/dist/components/Dialog.d.ts +1 -1
  10. package/dist/components/DialogActions.d.ts +1 -1
  11. package/dist/components/DialogContent.d.ts +1 -1
  12. package/dist/components/DialogTitle.d.ts +1 -1
  13. package/dist/components/ExpandablePanel.d.ts +1 -1
  14. package/dist/components/FileUpload.d.ts +1 -1
  15. package/dist/components/InfoLabel.d.ts +1 -1
  16. package/dist/components/LoadingButton.d.ts +1 -1
  17. package/dist/components/Menu.d.ts +1 -1
  18. package/dist/components/Menubar.d.ts +16 -16
  19. package/dist/components/MultiSelect.d.ts +1 -1
  20. package/dist/components/Paper.d.ts +1 -1
  21. package/dist/components/Popover.d.ts +1 -1
  22. package/dist/components/ResizablePanels.d.ts +16 -0
  23. package/dist/components/SearchBar.d.ts +1 -1
  24. package/dist/components/SearchableSelect.d.ts +48 -0
  25. package/dist/components/Select.d.ts +1 -1
  26. package/dist/components/Separator.d.ts +1 -1
  27. package/dist/components/Skeleton.d.ts +2 -1
  28. package/dist/components/Table.d.ts +5 -5
  29. package/dist/components/Tabs.d.ts +9 -2
  30. package/dist/components/ToggleButtonGroup.d.ts +1 -1
  31. package/dist/components/Tooltip.d.ts +18 -2
  32. package/dist/components/Typography.d.ts +1 -1
  33. package/dist/components/common/SelectInputLabel.d.ts +1 -1
  34. package/dist/components/index.d.ts +2 -0
  35. package/dist/hooks/PortalContainerContext.d.ts +1 -1
  36. package/dist/icons/FirestoreIcon.d.ts +7 -0
  37. package/dist/icons/GitHubIcon.d.ts +2 -1
  38. package/dist/icons/HandleIcon.d.ts +1 -1
  39. package/dist/icons/components/DatabaseIcon.d.ts +6 -0
  40. package/dist/icons/index.d.ts +2 -0
  41. package/dist/index.es.js +1376 -476
  42. package/dist/index.es.js.map +1 -1
  43. package/dist/index.umd.js +1378 -478
  44. package/dist/index.umd.js.map +1 -1
  45. package/package.json +3 -3
  46. package/src/components/BooleanSwitchWithLabel.tsx +4 -0
  47. package/src/components/Button.tsx +2 -1
  48. package/src/components/Chip.tsx +4 -3
  49. package/src/components/MultiSelect.tsx +23 -4
  50. package/src/components/ResizablePanels.tsx +181 -0
  51. package/src/components/SearchableSelect.tsx +335 -0
  52. package/src/components/Skeleton.tsx +4 -2
  53. package/src/components/Tabs.tsx +44 -16
  54. package/src/components/TextareaAutosize.tsx +90 -215
  55. package/src/components/Tooltip.tsx +7 -6
  56. package/src/components/index.tsx +2 -0
  57. package/src/icons/FirestoreIcon.tsx +47 -0
  58. package/src/icons/components/DatabaseIcon.tsx +10 -0
  59. 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` : "100%",
18
- height: height ? `${height}px` : "12px"
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
  }
@@ -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 <TabsPrimitive.Root value={value} onValueChange={onValueChange} className={cls("flex flex-row items-center min-w-0", className)}>
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", defaultBorderMixin
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-10 items-center justify-center rounded-md bg-surface-50 p-1 text-surface-600 dark:bg-surface-900 dark:text-surface-400",
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", defaultBorderMixin
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
- "inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-white transition-all",
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 * as ReactDOM from "react-dom";
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<HTMLInputElement>(null);
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
- const input = inputRef.current!;
74
- if (typeof window === "undefined") {
75
- return {
76
- outerHeightStyle: 0
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 containerWindow = window;
81
- const computedStyle = containerWindow.getComputedStyle(input);
82
-
83
- // If input's width is shrunk and it's not visible, don't sync height.
84
- if (computedStyle.width === "0px") {
85
- return {
86
- outerHeightStyle: 0
87
- };
88
- }
79
+ const lineHeight = singleRowScrollH - paddingY;
89
80
 
90
- const sizeReferenceElement = sizeRef?.current ?? shadowRef.current!;
91
- const inputShallow = shadowRef.current!;
81
+ let targetHeight = scrollH; // includes padding
92
82
 
93
- sizeReferenceElement.style.width = computedStyle.width;
94
- inputShallow.value = input.value || props.placeholder || "x";
95
- if (inputShallow.value.slice(-1) === "\n") {
96
- // Certain fonts which overflow the line height will cause the textarea
97
- // to report a different scrollHeight depending on whether the last line
98
- // is empty. Make it non-empty to avoid this issue.
99
- inputShallow.value += " ";
83
+ if (minRows) {
84
+ targetHeight = Math.max(
85
+ Number(minRows) * lineHeight + paddingY,
86
+ targetHeight
87
+ );
100
88
  }
101
89
 
102
- const boxSizing = computedStyle.boxSizing;
103
- const padding =
104
- getStyleValue(computedStyle.paddingBottom) + getStyleValue(computedStyle.paddingTop);
105
- const border =
106
- getStyleValue(computedStyle.borderBottomWidth) + getStyleValue(computedStyle.borderTopWidth);
107
- const minHeight = getStyleValue(computedStyle.minHeight);
108
-
109
- // The height of the inner content
110
- const innerHeight = sizeReferenceElement.scrollHeight;
111
-
112
- // Measure height of a textarea with a single row
113
- inputShallow.value = "x";
114
- const singleRowHeight = sizeReferenceElement.scrollHeight;
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
- outerHeight = Math.min(Number(maxRows) * singleRowHeight, outerHeight);
93
+ targetHeight = Math.min(
94
+ Number(maxRows) * lineHeight + paddingY,
95
+ targetHeight
96
+ );
124
97
  }
125
- outerHeight = Math.max(outerHeight, singleRowHeight, minHeight);
126
98
 
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);
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 overflow = Math.abs(outerHeight - innerHeight) <= 1;
105
+ const shouldScroll =
106
+ Math.abs(unclampedHeight - targetHeight) > 1;
131
107
 
132
- return {
133
- outerHeightStyle,
134
- overflow
135
- };
136
- }, [maxRows, minRows, props.placeholder]);
137
-
138
- const updateState = React.useCallback((prevState: State, newState: State) => {
139
- const {
140
- outerHeightStyle,
141
- overflow
142
- } = newState;
143
- // Need a large enough difference to update the height.
144
- // This prevents infinite rendering loop.
145
- if (
146
- renders.current < 20 &&
147
- ((outerHeightStyle > 0 &&
148
- Math.abs((prevState.outerHeightStyle || 0) - outerHeightStyle) > 1) ||
149
- prevState.overflow !== overflow)
150
- ) {
151
- renders.current += 1;
152
- return {
153
- overflow,
154
- outerHeightStyle
155
- };
156
- }
157
- if (process.env.NODE_ENV !== "production") {
158
- if (renders.current === 20) {
159
- console.error(
160
- [
161
- "MUI: Too many re-renders. The layout is unstable.",
162
- "TextareaAutosize limits the number of renders to prevent an infinite loop."
163
- ].join("\n")
164
- );
165
- }
166
- }
167
- return prevState;
168
- }, []);
169
-
170
- const syncHeight = React.useCallback(() => {
171
- const newState = getUpdatedState();
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(newState);
178
- }
179
-
180
- setState((prevState) => {
181
- return updateState(prevState, newState);
182
- });
183
- }, [getUpdatedState, onResize, updateState]);
184
-
185
- const syncHeightWithFlushSync = React.useCallback(() => {
186
- const newState = getUpdatedState();
187
-
188
- if (isEmpty(newState)) {
189
- return;
112
+ onResize({ outerHeightStyle: finalHeight, overflow: !shouldScroll });
190
113
  }
114
+ }, [maxRows, minRows, ignoreBoxSizing, onResize]);
191
115
 
192
- // In React 18, state updates in a ResizeObserver's callback are happening after the paint which causes flickering
193
- // when doing some visual updates in it. Using flushSync ensures that the dom will be painted after the states updates happen
194
- // Related issue - https://github.com/facebook/react/issues/24331
195
- ReactDOM.flushSync(() => {
196
- setState((prevState) => {
197
- return updateState(prevState, newState);
198
- });
199
- });
200
- }, [getUpdatedState, updateState]);
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
- syncHeightWithFlushSync();
125
+ syncHeight();
211
126
  }
212
127
  });
213
- let resizeObserver: ResizeObserver;
214
128
 
215
129
  const input = inputRef.current!;
216
- const containerWindow = window;
217
- if (typeof window === "undefined") {
218
- return;
219
- }
130
+ if (typeof window === "undefined") return;
220
131
 
221
- containerWindow.addEventListener("resize", handleResize);
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
- containerWindow.removeEventListener("resize", handleResize);
231
- if (resizeObserver) {
232
- resizeObserver.disconnect();
233
- }
142
+ window.removeEventListener("resize", handleResize);
143
+ resizeObserver?.disconnect();
234
144
  };
235
- }, [syncHeightWithFlushSync]);
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
- <React.Fragment>
259
- <textarea
260
- value={value}
261
- onChange={handleChange}
262
- className={props.className}
263
- ref={handleRef}
264
- onFocus={onFocus}
265
- onBlur={onBlur}
266
- // Apply the rows prop to get a "correct" first SSR paint
267
- rows={minRows as number}
268
- style={{
269
- height: state.outerHeightStyle,
270
- // Need a large enough difference to allow scrolling.
271
- // This prevents infinite rendering loop.
272
- overflow: state.overflow ? "hidden" : undefined,
273
- ...style,
274
- }}
275
- onScroll={onScroll}
276
- {...other}
277
- />
278
- <textarea
279
- aria-hidden
280
- className={cls(props.className, props.shadowClassName)}
281
- readOnly
282
- ref={shadowRef}
283
- tabIndex={-1}
284
- style={{
285
- padding: 0,
286
- ...styles.shadow,
287
- ...style,
288
- }}
289
- />
290
- </React.Fragment>
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
- }: TooltipProps) => {
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
 
@@ -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";
@@ -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";