@firecms/ui 3.0.1 → 3.1.0-canary.768c91f

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/README.md +9 -7
  2. package/dist/components/Card.d.ts +1 -1
  3. package/dist/components/ColorPicker.d.ts +30 -0
  4. package/dist/components/DateTimeField.d.ts +7 -0
  5. package/dist/components/Dialog.d.ts +2 -1
  6. package/dist/components/FileUpload.d.ts +1 -1
  7. package/dist/components/Menu.d.ts +2 -1
  8. package/dist/components/Menubar.d.ts +2 -1
  9. package/dist/components/MultiSelect.d.ts +2 -1
  10. package/dist/components/SearchBar.d.ts +11 -1
  11. package/dist/components/Select.d.ts +2 -1
  12. package/dist/components/Sheet.d.ts +1 -0
  13. package/dist/components/ToggleButtonGroup.d.ts +30 -0
  14. package/dist/components/index.d.ts +2 -0
  15. package/dist/hooks/PortalContainerContext.d.ts +31 -0
  16. package/dist/hooks/index.d.ts +1 -0
  17. package/dist/hooks/useOutsideAlerter.d.ts +1 -1
  18. package/dist/index.css +57 -6
  19. package/dist/index.es.js +1604 -936
  20. package/dist/index.es.js.map +1 -1
  21. package/dist/index.umd.js +1604 -936
  22. package/dist/index.umd.js.map +1 -1
  23. package/dist/styles.d.ts +11 -11
  24. package/package.json +7 -7
  25. package/src/components/BooleanSwitch.tsx +3 -3
  26. package/src/components/Button.tsx +5 -5
  27. package/src/components/Card.tsx +7 -7
  28. package/src/components/Checkbox.tsx +1 -1
  29. package/src/components/ColorPicker.tsx +134 -0
  30. package/src/components/DateTimeField.tsx +123 -34
  31. package/src/components/DebouncedTextField.tsx +3 -3
  32. package/src/components/Dialog.tsx +25 -16
  33. package/src/components/DialogActions.tsx +1 -1
  34. package/src/components/ExpandablePanel.tsx +1 -1
  35. package/src/components/FileUpload.tsx +25 -24
  36. package/src/components/IconButton.tsx +3 -2
  37. package/src/components/Menu.tsx +44 -30
  38. package/src/components/Menubar.tsx +14 -3
  39. package/src/components/MultiSelect.tsx +91 -74
  40. package/src/components/Popover.tsx +11 -3
  41. package/src/components/SearchBar.tsx +37 -19
  42. package/src/components/Select.tsx +86 -73
  43. package/src/components/Separator.tsx +2 -2
  44. package/src/components/Sheet.tsx +12 -3
  45. package/src/components/Slider.tsx +4 -4
  46. package/src/components/Table.tsx +1 -1
  47. package/src/components/Tabs.tsx +14 -17
  48. package/src/components/TextField.tsx +19 -8
  49. package/src/components/ToggleButtonGroup.tsx +67 -0
  50. package/src/components/Tooltip.tsx +9 -2
  51. package/src/components/index.tsx +2 -0
  52. package/src/hooks/PortalContainerContext.tsx +48 -0
  53. package/src/hooks/index.ts +1 -0
  54. package/src/hooks/useInjectStyles.tsx +12 -3
  55. package/src/hooks/useOutsideAlerter.tsx +1 -1
  56. package/src/index.css +57 -6
  57. package/src/styles.ts +11 -11
  58. package/src/util/cls.ts +1 -1
  59. package/tailwind.config.js +2 -3
package/README.md CHANGED
@@ -67,16 +67,18 @@ export default {
67
67
  Finally, you need to define your primary and secondary colors in your `index.css` file:
68
68
 
69
69
  ```css
70
- @import "@firecms/ui/index.css";
70
+ @import 'tailwindcss';
71
+ @import "@firecms/ui/index.css" layer(base);
71
72
 
72
- @tailwind base;
73
- @tailwind components;
74
- @tailwind utilities;
73
+ @custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *));
74
+
75
+ @source "../index.html";
76
+ @source "./**/*.{js,ts,jsx,tsx}";
77
+ @source "../node_modules/@firecms/**/*.{js,ts,jsx,tsx}";
75
78
 
76
79
  :root {
77
- --fcms-primary: #0070F4;
78
- --fcms-primary-bg: #0061e610;
79
- --fcms-secondary: #FF5B79;
80
+ --color-primary: #0070F4;
81
+ --color-secondary: #FF5B79;
80
82
  }
81
83
  ```
82
84
 
@@ -2,7 +2,7 @@ import React from "react";
2
2
  type CardProps = {
3
3
  children: React.ReactNode;
4
4
  style?: React.CSSProperties;
5
- onClick?: () => void;
5
+ onClick?: (e?: React.MouseEvent) => void;
6
6
  className?: string;
7
7
  };
8
8
  declare const Card: React.ForwardRefExoticComponent<CardProps & React.RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,30 @@
1
+ import { ChipColorKey } from "./Chip";
2
+ export interface ColorPickerProps {
3
+ /**
4
+ * Currently selected color key
5
+ */
6
+ value?: ChipColorKey;
7
+ /**
8
+ * Callback when color selection changes. Passes undefined when "Auto" is selected.
9
+ */
10
+ onChange: (colorKey: ChipColorKey | undefined) => void;
11
+ /**
12
+ * Size of the color swatches
13
+ */
14
+ size?: "small" | "medium";
15
+ /**
16
+ * Whether to show the "Auto" option that clears the selection
17
+ */
18
+ allowClear?: boolean;
19
+ /**
20
+ * Whether the picker is disabled
21
+ */
22
+ disabled?: boolean;
23
+ }
24
+ /**
25
+ * A color picker component that displays a grid of predefined CHIP_COLORS.
26
+ * Used for selecting colors for enum values, tags, and other chip-based UI elements.
27
+ *
28
+ * @group Form components
29
+ */
30
+ export declare function ColorPicker({ value, onChange, size, allowClear, disabled }: ColorPickerProps): import("react/jsx-runtime").JSX.Element;
@@ -13,5 +13,12 @@ export type DateTimeFieldProps = {
13
13
  inputClassName?: string;
14
14
  invisible?: boolean;
15
15
  locale?: string;
16
+ /**
17
+ * IANA timezone string (e.g., "America/New_York", "Europe/London").
18
+ * Used to display and input dates in the specified timezone.
19
+ * The value passed to onChange will always be in UTC.
20
+ * If not provided, uses the user's local timezone.
21
+ */
22
+ timezone?: string;
16
23
  };
17
24
  export declare const DateTimeField: React.FC<DateTimeFieldProps>;
@@ -19,6 +19,7 @@ export type DialogProps = {
19
19
  * If `true`, the dialog will not focus the first focusable element when opened.
20
20
  */
21
21
  disableInitialFocus?: boolean;
22
+ portalContainer?: HTMLElement | null;
22
23
  };
23
24
  declare const widthClasses: {
24
25
  xs: string;
@@ -34,5 +35,5 @@ declare const widthClasses: {
34
35
  "7xl": string;
35
36
  full: string;
36
37
  };
37
- export declare const Dialog: ({ open, onOpenChange, children, className, containerClassName, fullWidth, fullHeight, fullScreen, scrollable, maxWidth, modal, onOpenAutoFocus, onEscapeKeyDown, onPointerDownOutside, onInteractOutside, disableInitialFocus }: DialogProps) => import("react/jsx-runtime").JSX.Element;
38
+ export declare const Dialog: ({ open, onOpenChange, children, className, containerClassName, fullWidth, fullHeight, fullScreen, scrollable, maxWidth, modal, onOpenAutoFocus, onEscapeKeyDown, onPointerDownOutside, onInteractOutside, disableInitialFocus, portalContainer }: DialogProps) => import("react/jsx-runtime").JSX.Element;
38
39
  export {};
@@ -18,6 +18,6 @@ export type FileUploadProps = {
18
18
  title?: React.ReactNode;
19
19
  uploadDescription?: React.ReactNode;
20
20
  preventDropOnDocument?: boolean;
21
- size?: "medium" | "large";
21
+ size?: "small" | "medium" | "large";
22
22
  };
23
23
  export declare function FileUpload({ accept, onFilesAdded, onFilesRejected, maxSize, disabled, maxFiles, title, uploadDescription, children, preventDropOnDocument, size }: React.PropsWithChildren<FileUploadProps>): import("react/jsx-runtime").JSX.Element;
@@ -16,7 +16,8 @@ export { Menu };
16
16
  export type MenuItemProps = {
17
17
  children: React.ReactNode;
18
18
  dense?: boolean;
19
+ disabled?: boolean;
19
20
  onClick?: (event: React.MouseEvent) => void;
20
21
  className?: string;
21
22
  };
22
- export declare const MenuItem: React.MemoExoticComponent<({ children, dense, onClick, className }: MenuItemProps) => import("react/jsx-runtime").JSX.Element>;
23
+ export declare const MenuItem: React.MemoExoticComponent<({ children, dense, disabled, onClick, className }: MenuItemProps) => import("react/jsx-runtime").JSX.Element>;
@@ -12,8 +12,9 @@ export declare function MenubarTrigger({ children, onSelect, className }: {
12
12
  onSelect?: (event: React.SyntheticEvent) => void;
13
13
  className?: string;
14
14
  }): import("react/jsx-runtime").JSX.Element;
15
- export declare function MenubarPortal({ children, }: {
15
+ export declare function MenubarPortal({ children, portalContainer, }: {
16
16
  children: React.ReactNode;
17
+ portalContainer?: HTMLElement | null;
17
18
  }): import("react/jsx-runtime").JSX.Element;
18
19
  export declare function MenubarContent({ children, className, align, sideOffset, alignOffset, onSelect, ...rest }: {
19
20
  children: React.ReactNode;
@@ -31,11 +31,12 @@ interface MultiSelectProps<T extends MultiSelectValue = string> {
31
31
  multiple?: boolean;
32
32
  includeSelectAll?: boolean;
33
33
  includeClear?: boolean;
34
- inputRef?: React.RefObject<HTMLButtonElement>;
34
+ inputRef?: React.RefObject<HTMLButtonElement | null>;
35
35
  padding?: boolean;
36
36
  invisible?: boolean;
37
37
  children: React.ReactNode;
38
38
  renderValues?: (values: T[]) => React.ReactNode;
39
+ portalContainer?: HTMLElement | null;
39
40
  }
40
41
  export declare const MultiSelect: React.ForwardRefExoticComponent<MultiSelectProps<string> & React.RefAttributes<HTMLButtonElement>>;
41
42
  export interface MultiSelectItemProps<T extends MultiSelectValue = string> {
@@ -4,6 +4,16 @@ interface SearchBarProps {
4
4
  onTextSearch?: (searchString?: string) => void;
5
5
  placeholder?: string;
6
6
  expandable?: boolean;
7
+ /**
8
+ * Size of the search bar.
9
+ * - "small": 32px height (matches TextField small)
10
+ * - "medium": 44px height (matches TextField medium)
11
+ * @default "medium"
12
+ */
13
+ size?: "small" | "medium";
14
+ /**
15
+ * @deprecated Use size="medium" or size="small" instead. This prop will be removed in a future version.
16
+ */
7
17
  large?: boolean;
8
18
  innerClassName?: string;
9
19
  className?: string;
@@ -12,5 +22,5 @@ interface SearchBarProps {
12
22
  loading?: boolean;
13
23
  inputRef?: React.Ref<HTMLInputElement>;
14
24
  }
15
- export declare function SearchBar({ onClick, onTextSearch, placeholder, expandable, large, innerClassName, className, autoFocus, disabled, loading, inputRef }: SearchBarProps): import("react/jsx-runtime").JSX.Element;
25
+ export declare function SearchBar({ onClick, onTextSearch, placeholder, expandable, size, large, innerClassName, className, autoFocus, disabled, loading, inputRef }: SearchBarProps): import("react/jsx-runtime").JSX.Element;
16
26
  export {};
@@ -20,11 +20,12 @@ export type SelectProps<T extends SelectValue = string> = {
20
20
  error?: boolean;
21
21
  position?: "item-aligned" | "popper";
22
22
  endAdornment?: React.ReactNode;
23
- inputRef?: React.RefObject<HTMLButtonElement>;
23
+ inputRef?: React.RefObject<HTMLButtonElement | null>;
24
24
  padding?: boolean;
25
25
  invisible?: boolean;
26
26
  children?: React.ReactNode;
27
27
  dataType?: "string" | "number" | "boolean";
28
+ portalContainer?: HTMLElement | null;
28
29
  };
29
30
  export declare const Select: React.ForwardRefExoticComponent<SelectProps<string> & React.RefAttributes<HTMLDivElement>>;
30
31
  export type SelectItemProps<T extends SelectValue = string> = {
@@ -13,6 +13,7 @@ interface SheetProps {
13
13
  style?: React.CSSProperties;
14
14
  overlayClassName?: string;
15
15
  overlayStyle?: React.CSSProperties;
16
+ portalContainer?: HTMLElement | null;
16
17
  }
17
18
  export declare const Sheet: React.FC<SheetProps>;
18
19
  export {};
@@ -0,0 +1,30 @@
1
+ import React from "react";
2
+ export type ToggleButtonOption<T extends string = string> = {
3
+ value: T;
4
+ label: string;
5
+ icon?: React.ReactNode;
6
+ disabled?: boolean;
7
+ };
8
+ export type ToggleButtonGroupProps<T extends string = string> = {
9
+ /**
10
+ * Currently selected value
11
+ */
12
+ value: T;
13
+ /**
14
+ * Callback when value changes
15
+ */
16
+ onValueChange: (value: T) => void;
17
+ /**
18
+ * Options to display
19
+ */
20
+ options: ToggleButtonOption<T>[];
21
+ /**
22
+ * Additional class names for the container
23
+ */
24
+ className?: string;
25
+ };
26
+ /**
27
+ * A toggle button group component for selecting one option from a set.
28
+ * Displays options as buttons in a horizontal row with active state styling.
29
+ */
30
+ export declare function ToggleButtonGroup<T extends string = string>({ value, onValueChange, options, className }: ToggleButtonGroupProps<T>): import("react/jsx-runtime").JSX.Element;
@@ -11,6 +11,7 @@ export * from "./Collapse";
11
11
  export * from "./CircularProgress";
12
12
  export * from "./Checkbox";
13
13
  export * from "./Chip";
14
+ export * from "./ColorPicker";
14
15
  export * from "./DateTimeField";
15
16
  export * from "./Dialog";
16
17
  export * from "./DialogActions";
@@ -44,3 +45,4 @@ export * from "./Popover";
44
45
  export * from "./Badge";
45
46
  export * from "./DebouncedTextField";
46
47
  export * from "./Skeleton";
48
+ export * from "./ToggleButtonGroup";
@@ -0,0 +1,31 @@
1
+ import React from "react";
2
+ export interface PortalContainerContextType {
3
+ container: HTMLElement | null;
4
+ }
5
+ export interface PortalContainerProviderProps {
6
+ container: HTMLElement | null;
7
+ children: React.ReactNode;
8
+ }
9
+ /**
10
+ * Provider component that sets the portal container for all descendants.
11
+ * This can be used at any level of the tree to specify where portals should be attached.
12
+ *
13
+ * @example
14
+ * ```tsx
15
+ * const containerRef = useRef<HTMLDivElement>(null);
16
+ *
17
+ * <div ref={containerRef}>
18
+ * <PortalContainerProvider container={containerRef.current}>
19
+ * <YourComponents />
20
+ * </PortalContainerProvider>
21
+ * </div>
22
+ * ```
23
+ */
24
+ export declare function PortalContainerProvider({ container, children }: PortalContainerProviderProps): import("react/jsx-runtime").JSX.Element;
25
+ /**
26
+ * Hook to access the portal container from context.
27
+ * Returns null if no provider is found in the tree.
28
+ *
29
+ * @returns The portal container element or null
30
+ */
31
+ export declare function usePortalContainer(): HTMLElement | null;
@@ -2,3 +2,4 @@ export * from "./useInjectStyles";
2
2
  export * from "./useOutsideAlerter";
3
3
  export * from "./useDebounceValue";
4
4
  export * from "./useIconStyles";
5
+ export * from "./PortalContainerContext";
@@ -2,4 +2,4 @@ import { RefObject } from "react";
2
2
  /**
3
3
  * Hook that alerts clicks outside the passed ref
4
4
  */
5
- export declare function useOutsideAlerter(ref: RefObject<HTMLElement>, onOutsideClick: () => void, active?: boolean): void;
5
+ export declare function useOutsideAlerter(ref: RefObject<HTMLElement | null>, onOutsideClick: () => void, active?: boolean): void;
package/dist/index.css CHANGED
@@ -1,3 +1,59 @@
1
+ @theme {
2
+ /* Font Families */
3
+ --font-sans: 'Rubik', 'Roboto', 'Helvetica', 'Arial', sans-serif;
4
+ --font-headers: 'Rubik', 'Roboto', 'Helvetica', 'Arial', sans-serif;
5
+ --font-mono: 'JetBrains Mono', 'Space Mono', 'Lucida Console', monospace;
6
+
7
+ /* Colors */
8
+ --color-primary: #0070F4;
9
+ --color-primary-light: oklch(from var(--color-primary) calc(l + 0.15) c h);
10
+ --color-primary-dark: oklch(from var(--color-primary) calc(l - 0.15) c h);
11
+ --color-secondary: #FF5B79;
12
+ --color-secondary-light: oklch(from var(--color-secondary) calc(l + 0.15) c h);
13
+ --color-secondary-dark: oklch(from var(--color-secondary) calc(l - 0.15) c h);
14
+
15
+ --color-primary-bg: oklch(from var(--color-primary) l c h / 0.1);
16
+ --color-secondary-bg: oklch(from var(--color-secondary) l c h / 0.1);
17
+
18
+ /* Field Colors */
19
+ --color-field-disabled: rgb(224 224 226);
20
+ --color-field-disabled-dark: rgb(35 35 37);
21
+
22
+ /* Text Colors */
23
+ --color-text-primary: rgba(0, 0, 0, 0.87);
24
+ --color-text-secondary: rgba(0, 0, 0, 0.52);
25
+ --color-text-disabled: rgba(0, 0, 0, 0.38);
26
+ --color-text-primary-dark: #ffffff;
27
+ --color-text-secondary-dark: rgba(255, 255, 255, 0.6);
28
+ --color-text-disabled-dark: rgba(255, 255, 255, 0.48);
29
+
30
+ /* Surface Colors */
31
+ --color-surface-50: #f8f8fc;
32
+ --color-surface-100: #e7e7eb;
33
+ --color-surface-200: #cfcfd6;
34
+ --color-surface-300: #b7b7bf;
35
+ --color-surface-400: #a0a0a9;
36
+ --color-surface-500: #87878f;
37
+ --color-surface-600: #6b6b74;
38
+ --color-surface-700: #454552;
39
+ --color-surface-800: #292934;
40
+ --color-surface-900: #18181c;
41
+ --color-surface-950: #101013;
42
+
43
+ /* Surface Accent Colors */
44
+ --color-surface-accent-50: #f8fafc;
45
+ --color-surface-accent-100: #f1f5f9;
46
+ --color-surface-accent-200: #e2e8f0;
47
+ --color-surface-accent-300: #cbd5e1;
48
+ --color-surface-accent-400: #94a3b8;
49
+ --color-surface-accent-500: #64748b;
50
+ --color-surface-accent-600: #475569;
51
+ --color-surface-accent-700: #334155;
52
+ --color-surface-accent-800: #1e293b;
53
+ --color-surface-accent-900: #0f172a;
54
+ --color-surface-accent-950: #020617;
55
+ }
56
+
1
57
  /* Chrome, Safari and Opera */
2
58
  .no-scrollbar::-webkit-scrollbar {
3
59
  display: none;
@@ -63,12 +119,7 @@
63
119
  @apply text-sm font-semibold uppercase;
64
120
  }
65
121
 
66
-
67
122
  :focus-visible {
68
- @apply outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-opacity-75 focus-visible:ring-offset-2 focus-visible:ring-offset-transparent
69
- }
70
-
71
- a {
72
- @apply text-blue-600 dark:text-blue-400 dark:hover:text-blue-600 hover:text-blue-800
123
+ @apply outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 focus-visible:ring-offset-transparent
73
124
  }
74
125