@firecms/ui 3.0.1 → 3.1.0-canary.02232f4

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 (77) hide show
  1. package/README.md +9 -7
  2. package/dist/components/BooleanSwitchWithLabel.d.ts +2 -1
  3. package/dist/components/Card.d.ts +1 -1
  4. package/dist/components/Chip.d.ts +1 -1
  5. package/dist/components/ColorPicker.d.ts +30 -0
  6. package/dist/components/DateTimeField.d.ts +7 -0
  7. package/dist/components/Dialog.d.ts +2 -1
  8. package/dist/components/FileUpload.d.ts +1 -1
  9. package/dist/components/Menu.d.ts +2 -1
  10. package/dist/components/Menubar.d.ts +2 -1
  11. package/dist/components/MultiSelect.d.ts +2 -1
  12. package/dist/components/ResizablePanels.d.ts +16 -0
  13. package/dist/components/SearchBar.d.ts +11 -1
  14. package/dist/components/SearchableSelect.d.ts +48 -0
  15. package/dist/components/Select.d.ts +2 -1
  16. package/dist/components/Sheet.d.ts +1 -0
  17. package/dist/components/Tabs.d.ts +8 -1
  18. package/dist/components/ToggleButtonGroup.d.ts +30 -0
  19. package/dist/components/Tooltip.d.ts +18 -2
  20. package/dist/components/index.d.ts +4 -0
  21. package/dist/hooks/PortalContainerContext.d.ts +31 -0
  22. package/dist/hooks/index.d.ts +1 -0
  23. package/dist/hooks/useOutsideAlerter.d.ts +1 -1
  24. package/dist/icons/FirestoreIcon.d.ts +6 -0
  25. package/dist/icons/components/DatabaseIcon.d.ts +6 -0
  26. package/dist/icons/index.d.ts +2 -0
  27. package/dist/index.css +57 -6
  28. package/dist/index.es.js +2846 -1165
  29. package/dist/index.es.js.map +1 -1
  30. package/dist/index.umd.js +2846 -1165
  31. package/dist/index.umd.js.map +1 -1
  32. package/dist/styles.d.ts +11 -11
  33. package/package.json +7 -7
  34. package/src/components/BooleanSwitch.tsx +3 -3
  35. package/src/components/BooleanSwitchWithLabel.tsx +4 -0
  36. package/src/components/Button.tsx +6 -5
  37. package/src/components/Card.tsx +7 -7
  38. package/src/components/Checkbox.tsx +1 -1
  39. package/src/components/Chip.tsx +4 -3
  40. package/src/components/ColorPicker.tsx +134 -0
  41. package/src/components/DateTimeField.tsx +129 -35
  42. package/src/components/DebouncedTextField.tsx +3 -3
  43. package/src/components/Dialog.tsx +25 -16
  44. package/src/components/DialogActions.tsx +1 -1
  45. package/src/components/ExpandablePanel.tsx +1 -1
  46. package/src/components/FileUpload.tsx +25 -24
  47. package/src/components/IconButton.tsx +3 -2
  48. package/src/components/Menu.tsx +44 -30
  49. package/src/components/Menubar.tsx +14 -3
  50. package/src/components/MultiSelect.tsx +113 -77
  51. package/src/components/Popover.tsx +11 -3
  52. package/src/components/ResizablePanels.tsx +181 -0
  53. package/src/components/SearchBar.tsx +37 -19
  54. package/src/components/SearchableSelect.tsx +335 -0
  55. package/src/components/Select.tsx +86 -73
  56. package/src/components/Separator.tsx +2 -2
  57. package/src/components/Sheet.tsx +12 -3
  58. package/src/components/Skeleton.tsx +4 -2
  59. package/src/components/Slider.tsx +4 -4
  60. package/src/components/Table.tsx +1 -1
  61. package/src/components/Tabs.tsx +150 -37
  62. package/src/components/TextField.tsx +19 -8
  63. package/src/components/TextareaAutosize.tsx +77 -212
  64. package/src/components/ToggleButtonGroup.tsx +67 -0
  65. package/src/components/Tooltip.tsx +16 -8
  66. package/src/components/index.tsx +4 -0
  67. package/src/hooks/PortalContainerContext.tsx +48 -0
  68. package/src/hooks/index.ts +1 -0
  69. package/src/hooks/useInjectStyles.tsx +12 -3
  70. package/src/hooks/useOutsideAlerter.tsx +1 -1
  71. package/src/icons/FirestoreIcon.tsx +47 -0
  72. package/src/icons/components/DatabaseIcon.tsx +10 -0
  73. package/src/icons/index.ts +2 -0
  74. package/src/index.css +57 -6
  75. package/src/styles.ts +11 -11
  76. package/src/util/cls.ts +1 -1
  77. package/tailwind.config.js +2 -3
@@ -3,6 +3,7 @@ import React, { useEffect, useState } from "react";
3
3
  import * as DialogPrimitive from "@radix-ui/react-dialog";
4
4
  import { paperMixin } from "../styles";
5
5
  import { cls } from "../util";
6
+ import { usePortalContainer } from "../hooks/PortalContainerContext";
6
7
 
7
8
  export type DialogProps = {
8
9
  open?: boolean;
@@ -24,21 +25,22 @@ export type DialogProps = {
24
25
  * If `true`, the dialog will not focus the first focusable element when opened.
25
26
  */
26
27
  disableInitialFocus?: boolean;
28
+ portalContainer?: HTMLElement | null;
27
29
  };
28
30
 
29
31
  const widthClasses = {
30
- xs: "max-w-xs min-w-xs w-xs",
31
- sm: "max-w-sm min-w-sm w-sm",
32
- md: "max-w-md min-w-md w-md",
33
- lg: "max-w-lg min-w-lg w-lg",
34
- xl: "max-w-xl min-w-xl w-xl",
35
- "2xl": "max-w-2xl min-w-2xl w-2xl",
36
- "3xl": "max-w-3xl min-w-3xl w-3xl",
37
- "4xl": "max-w-4xl min-w-4xl w-4xl",
38
- "5xl": "max-w-5xl min-w-5xl w-5xl",
39
- "6xl": "max-w-6xl min-w-6xl w-6xl",
40
- "7xl": "max-w-7xl min-w-7xl w-7xl",
41
- full: "max-w-full min-w-full w-full"
32
+ xs: "max-w-xs w-xs",
33
+ sm: "max-w-sm w-sm",
34
+ md: "max-w-md w-md",
35
+ lg: "max-w-lg w-lg",
36
+ xl: "max-w-xl w-xl",
37
+ "2xl": "max-w-2xl w-2xl",
38
+ "3xl": "max-w-3xl w-3xl",
39
+ "4xl": "max-w-4xl w-4xl",
40
+ "5xl": "max-w-5xl w-5xl",
41
+ "6xl": "max-w-6xl w-6xl",
42
+ "7xl": "max-w-7xl w-7xl",
43
+ full: "max-w-full w-full"
42
44
  };
43
45
 
44
46
  export const Dialog = ({
@@ -57,15 +59,22 @@ export const Dialog = ({
57
59
  onEscapeKeyDown,
58
60
  onPointerDownOutside,
59
61
  onInteractOutside,
60
- disableInitialFocus = true
62
+ disableInitialFocus = true,
63
+ portalContainer
61
64
  }: DialogProps) => {
62
65
  const [displayed, setDisplayed] = useState(false);
63
66
 
67
+ // Get the portal container from context
68
+ const contextContainer = usePortalContainer();
69
+
70
+ // Prioritize manual prop, fallback to context container
71
+ const finalContainer = (portalContainer ?? contextContainer ?? undefined) as HTMLElement | undefined;
72
+
64
73
  useEffect(() => {
65
74
  if (!open) {
66
75
  const timeout = setTimeout(() => {
67
76
  setDisplayed(false);
68
- }, 150);
77
+ }, 100);
69
78
  return () => clearTimeout(timeout);
70
79
  } else {
71
80
  setDisplayed(true);
@@ -78,12 +87,12 @@ export const Dialog = ({
78
87
  <DialogPrimitive.Root open={displayed || open}
79
88
  modal={modal}
80
89
  onOpenChange={onOpenChange}>
81
- <DialogPrimitive.Portal>
90
+ <DialogPrimitive.Portal container={finalContainer}>
82
91
 
83
92
  <div className={cls("fixed inset-0 z-30", containerClassName)}>
84
93
 
85
94
  <DialogPrimitive.Overlay
86
- className={cls("fixed inset-0 transition-opacity z-20 ease-in-out duration-200 bg-black bg-opacity-50 dark:bg-opacity-60 backdrop-blur-sm ",
95
+ className={cls("fixed inset-0 transition-opacity z-20 ease-in-out duration-200 bg-black dark:bg-opacity-60 dark:bg-black/60 bg-opacity-50 bg-black/50 dark: bg-black/60 backdrop-blur-sm ",
87
96
  displayed && open ? "opacity-100" : "opacity-0",
88
97
  "z-20 fixed top-0 left-0 w-full h-full flex justify-center items-center"
89
98
  )}
@@ -19,7 +19,7 @@ export function DialogActions({
19
19
  defaultBorderMixin,
20
20
  "pt-2 pb-4 px-4 border-t flex flex-row items-center justify-end bottom-0 right-0 left-0 text-right z-2 gap-2",
21
21
  position,
22
- "bg-white bg-opacity-60 dark:bg-surface-900 dark:bg-opacity-60",
22
+ "bg-white bg-opacity-60 bg-white/60 dark:bg-surface-900 dark:bg-opacity-60 dark:bg-surface-900/60",
23
23
  translucent ? "backdrop-blur-sm" : "",
24
24
  className)}>
25
25
  {children}
@@ -95,7 +95,7 @@ export function ExpandablePanel({
95
95
  <div
96
96
  className={cls(
97
97
  "rounded-t flex items-center justify-between w-full min-h-[52px]",
98
- "hover:bg-surface-accent-200 hover:bg-opacity-40 dark:hover:bg-surface-800 dark:hover:bg-opacity-40",
98
+ "hover:bg-surface-accent-200 hover:bg-opacity-40 hover:bg-surface-accent-200/40 dark:hover:bg-surface-800 dark:hover:bg-opacity-40 dark:hover:bg-surface-800/40",
99
99
  invisible ? "border-b px-2" : "p-4",
100
100
  open ? "py-6" : "py-4",
101
101
  "transition-all duration-200",
@@ -28,22 +28,22 @@ export type FileUploadProps = {
28
28
  title?: React.ReactNode;
29
29
  uploadDescription?: React.ReactNode;
30
30
  preventDropOnDocument?: boolean;
31
- size?: "medium" | "large";
31
+ size?: "small" | "medium" | "large";
32
32
  };
33
33
 
34
34
  export function FileUpload({
35
- accept,
36
- onFilesAdded,
37
- onFilesRejected,
38
- maxSize,
39
- disabled,
40
- maxFiles,
41
- title,
42
- uploadDescription,
43
- children,
44
- preventDropOnDocument = true,
45
- size
46
- }: React.PropsWithChildren<FileUploadProps>) {
35
+ accept,
36
+ onFilesAdded,
37
+ onFilesRejected,
38
+ maxSize,
39
+ disabled,
40
+ maxFiles,
41
+ title,
42
+ uploadDescription,
43
+ children,
44
+ preventDropOnDocument = true,
45
+ size
46
+ }: React.PropsWithChildren<FileUploadProps>) {
47
47
 
48
48
  const {
49
49
  getRootProps,
@@ -52,15 +52,15 @@ export function FileUpload({
52
52
  isDragAccept,
53
53
  isDragReject
54
54
  } = useDropzone({
55
- accept,
56
- noDragEventsBubbling: true,
57
- maxSize,
58
- onDrop: onFilesAdded,
59
- onDropRejected: onFilesRejected,
60
- disabled,
61
- maxFiles,
62
- preventDropOnDocument
63
- }
55
+ accept,
56
+ noDragEventsBubbling: true,
57
+ maxSize,
58
+ onDrop: onFilesAdded,
59
+ onDropRejected: onFilesRejected,
60
+ disabled,
61
+ maxFiles,
62
+ preventDropOnDocument
63
+ }
64
64
  );
65
65
  return <div
66
66
  {...getRootProps()}
@@ -71,6 +71,7 @@ export function FileUpload({
71
71
  {
72
72
  "h-44": size === "large",
73
73
  "h-28": size === "medium",
74
+ "h-16": size === "small",
74
75
  "cursor-pointer": !disabled,
75
76
  [fieldBackgroundHoverMixin]: !isDragActive,
76
77
  "transition-colors duration-200 ease-[cubic-bezier(0,0,0.2,1)] border-red-500": isDragReject,
@@ -89,8 +90,8 @@ export function FileUpload({
89
90
  <div
90
91
  className="flex-grow h-28 box-border flex flex-col items-center justify-center text-center">
91
92
  <Typography align={"center"}
92
- variant={"label"}
93
- className={"flex flex-row gap-2 justify-center"}>
93
+ variant={"label"}
94
+ className={"flex flex-row gap-2 justify-center"}>
94
95
  {uploadDescription}
95
96
  </Typography>
96
97
  </div>
@@ -14,7 +14,7 @@ export type IconButtonProps<C extends React.ElementType> =
14
14
  onClick?: React.MouseEventHandler<any>
15
15
  }
16
16
 
17
- const buttonClasses = "hover:bg-surface-accent-200 hover:bg-opacity-75 dark:hover:bg-surface-accent-800 hover:scale-105 transition-transform";
17
+ const buttonClasses = "hover:bg-surface-accent-200 hover:bg-opacity-75 hover:bg-surface-accent-200/75 dark:hover:bg-surface-accent-800 hover:scale-105 transition-transform";
18
18
  const baseClasses = "inline-flex items-center justify-center p-2 text-sm font-medium focus:outline-none transition-colors ease-in-out duration-150";
19
19
  const colorClasses = "text-surface-accent-600 visited:text-surface-accent-600 dark:text-surface-accent-300 dark:visited:text-surface-300";
20
20
  const sizeClasses = {
@@ -40,7 +40,7 @@ const IconButtonInner = <C extends React.ElementType = "button">({
40
40
  ...props
41
41
  }: IconButtonProps<C>, ref: React.ForwardedRef<HTMLButtonElement>) => {
42
42
 
43
- const bgClasses = variant === "ghost" ? "bg-transparent" : "bg-surface-accent-200 bg-opacity-50 dark:bg-surface-950 dark:bg-opacity-50";
43
+ const bgClasses = variant === "ghost" ? "bg-transparent" : "bg-surface-accent-200 bg-opacity-50 bg-surface-accent-200/50 dark:bg-surface-950 dark:bg-opacity-50 dark:bg-surface-950/50";
44
44
  const Component: React.ElementType<any> = component || "button";
45
45
  return (
46
46
  <Component
@@ -50,6 +50,7 @@ const IconButtonInner = <C extends React.ElementType = "button">({
50
50
  className={cls(
51
51
  disabled ? "opacity-50 pointer-events-none" : "cursor-pointer",
52
52
  toggled ? "outline outline-2 outline-primary" : "",
53
+ "text-inherit dark:text-inherit",
53
54
  colorClasses,
54
55
  bgClasses,
55
56
  baseClasses,
@@ -3,6 +3,7 @@ import React from "react";
3
3
  import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
4
4
  import { focusedDisabled, paperMixin } from "../styles";
5
5
  import { cls } from "../util";
6
+ import { usePortalContainer } from "../hooks/PortalContainerContext";
6
7
 
7
8
  export type MenuProps = {
8
9
  children: React.ReactNode;
@@ -33,28 +34,36 @@ const Menu = React.forwardRef<
33
34
  onOpenChange,
34
35
  portalContainer,
35
36
  sideOffset = 4,
36
- className
37
- }, ref) => (
38
- <DropdownMenu.Root
39
- open={open}
40
- defaultOpen={defaultOpen}
41
- onOpenChange={onOpenChange}>
42
- <DropdownMenu.Trigger
43
- ref={ref}
44
- asChild>
45
- {trigger}
46
- </DropdownMenu.Trigger>
47
- <DropdownMenu.Portal container={portalContainer}>
48
- <DropdownMenu.Content
49
- side={side}
50
- sideOffset={sideOffset}
51
- align={align}
52
- className={cls(paperMixin, focusedDisabled, "py-2 z-30", className)}>
53
- {children}
54
- </DropdownMenu.Content>
55
- </DropdownMenu.Portal>
56
- </DropdownMenu.Root>
57
- ))
37
+ className
38
+ }, ref) => {
39
+ // Get the portal container from context
40
+ const contextContainer = usePortalContainer();
41
+
42
+ // Prioritize manual prop, fallback to context container
43
+ const finalContainer = (portalContainer ?? contextContainer ?? undefined) as HTMLElement | undefined;
44
+
45
+ return (
46
+ <DropdownMenu.Root
47
+ open={open}
48
+ defaultOpen={defaultOpen}
49
+ onOpenChange={onOpenChange}>
50
+ <DropdownMenu.Trigger
51
+ ref={ref}
52
+ asChild>
53
+ {trigger}
54
+ </DropdownMenu.Trigger>
55
+ <DropdownMenu.Portal container={finalContainer}>
56
+ <DropdownMenu.Content
57
+ side={side}
58
+ sideOffset={sideOffset}
59
+ align={align}
60
+ className={cls(paperMixin, focusedDisabled, "py-2 z-30", className)}>
61
+ {children}
62
+ </DropdownMenu.Content>
63
+ </DropdownMenu.Portal>
64
+ </DropdownMenu.Root>
65
+ );
66
+ })
58
67
  Menu.displayName = "Menu"
59
68
 
60
69
  export { Menu }
@@ -62,20 +71,24 @@ export { Menu }
62
71
  export type MenuItemProps = {
63
72
  children: React.ReactNode;
64
73
  dense?: boolean;
74
+ disabled?: boolean;
65
75
  onClick?: (event: React.MouseEvent) => void;
66
76
  className?: string;
67
77
  };
68
78
 
69
79
  export const MenuItem = React.memo(({
70
- children,
71
- dense = false, // Default value is false if not provided
72
- onClick,
73
- className
74
- }: MenuItemProps) => {
80
+ children,
81
+ dense = false, // Default value is false if not provided
82
+ disabled = false,
83
+ onClick,
84
+ className
85
+ }: MenuItemProps) => {
75
86
  // Dynamically adjusting the class based on the "dense" prop
76
87
  const classNames = cls(
77
- onClick && "cursor-pointer",
78
- "rounded-md text-sm font-medium text-surface-accent-700 dark:text-surface-accent-300 hover:bg-surface-accent-100 dark:hover:bg-surface-accent-900 flex items-center gap-4",
88
+ onClick && !disabled && "cursor-pointer",
89
+ disabled && "opacity-50 cursor-not-allowed",
90
+ "rounded-md text-sm font-medium text-surface-accent-700 dark:text-surface-accent-300 flex items-center gap-4",
91
+ !disabled && "hover:bg-surface-accent-100 dark:hover:bg-surface-accent-900",
79
92
  dense ? "px-4 py-1.5" : "px-4 py-2",
80
93
  className
81
94
  );
@@ -83,7 +96,8 @@ export const MenuItem = React.memo(({
83
96
  return (
84
97
  <DropdownMenu.Item
85
98
  className={classNames}
86
- onClick={onClick}>
99
+ disabled={disabled}
100
+ onClick={disabled ? undefined : onClick}>
87
101
  {children}
88
102
  </DropdownMenu.Item>
89
103
  );
@@ -3,6 +3,7 @@ import * as React from "react";
3
3
  import * as MenubarPrimitive from "@radix-ui/react-menubar";
4
4
  import { cls } from "../util";
5
5
  import { CheckIcon, ChevronRightIcon } from "../icons";
6
+ import { usePortalContainer } from "../hooks/PortalContainerContext";
6
7
 
7
8
  export function Menubar({
8
9
  children,
@@ -44,7 +45,7 @@ export function MenubarTrigger({
44
45
  return (
45
46
  <MenubarPrimitive.Trigger
46
47
  onSelect={onSelect}
47
- className={cls("py-2 px-3 outline-none select-none font-medium leading-none rounded text-text-primary dark:text-text-primary-dark text-[13px] flex items-center justify-between gap-[2px] data-[highlighted]:bg-surface-accent-100 data-[highlighted]:dark:bg-surface-800 data-[state=open]:bg-surface-accent-100 data-[state=open]:dark:bg-surface-800 hover:bg-surface-accent-200 hover:bg-opacity-75 dark:hover:bg-surface-accent-800",
48
+ className={cls("py-2 px-3 outline-none select-none font-medium leading-none rounded text-text-primary dark:text-text-primary-dark text-[13px] flex items-center justify-between gap-[2px] data-[highlighted]:bg-surface-accent-100 data-[highlighted]:dark:bg-surface-800 data-[state=open]:bg-surface-accent-100 data-[state=open]:dark:bg-surface-800 hover:bg-surface-accent-200 hover:bg-opacity-75 hover:bg-surface-accent-200/75 dark:hover:bg-surface-accent-800",
48
49
  className)}>
49
50
  {children}
50
51
  </MenubarPrimitive.Trigger>
@@ -53,9 +54,19 @@ export function MenubarTrigger({
53
54
 
54
55
  export function MenubarPortal({
55
56
  children,
56
- }: { children: React.ReactNode }) {
57
+ portalContainer,
58
+ }: {
59
+ children: React.ReactNode;
60
+ portalContainer?: HTMLElement | null;
61
+ }) {
62
+ // Get the portal container from context
63
+ const contextContainer = usePortalContainer();
64
+
65
+ // Prioritize manual prop, fallback to context container
66
+ const finalContainer = (portalContainer ?? contextContainer ?? undefined) as HTMLElement | undefined;
67
+
57
68
  return (
58
- <MenubarPrimitive.Portal>
69
+ <MenubarPrimitive.Portal container={finalContainer}>
59
70
  {children}
60
71
  </MenubarPrimitive.Portal>
61
72
  )