@firecms/ui 3.0.0-beta.2-pre.4 → 3.0.0-beta.2

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@firecms/ui",
3
3
  "type": "module",
4
- "version": "3.0.0-beta.2-pre.4",
4
+ "version": "3.0.0-beta.2",
5
5
  "description": "Awesome Firebase/Firestore-based headless open-source CMS",
6
6
  "funding": {
7
7
  "url": "https://github.com/sponsors/firecmsco"
@@ -47,14 +47,15 @@
47
47
  "@radix-ui/react-portal": "^1.0.4",
48
48
  "@radix-ui/react-scroll-area": "^1.0.5",
49
49
  "@radix-ui/react-select": "^1.2.2",
50
+ "@radix-ui/react-separator": "^1.0.3",
50
51
  "@radix-ui/react-switch": "^1.0.3",
51
52
  "@radix-ui/react-tabs": "^1.0.4",
52
53
  "@radix-ui/react-tooltip": "^1.0.7",
53
54
  "cmdk": "^0.2.1",
54
- "react-datepicker": "^4.24.0",
55
+ "react-datepicker": "^4.25.0",
55
56
  "react-dropzone": "^14.2.3",
56
57
  "react-fast-compare": "^3.2.2",
57
- "tailwind-merge": "^1.14.0"
58
+ "tailwind-merge": "^2.2.1"
58
59
  },
59
60
  "peerDependencies": {
60
61
  "react": "^18.2.0",
@@ -93,15 +94,15 @@
93
94
  "npm-run-all": "^4.1.5",
94
95
  "ts-jest": "^29.1.2",
95
96
  "ts-node": "^10.9.2",
96
- "tsd": "^0.28.1",
97
+ "tsd": "^0.30.4",
97
98
  "typescript": "^5.3.3",
98
- "vite": "^4.5.2"
99
+ "vite": "^5.1.1"
99
100
  },
100
101
  "files": [
101
102
  "dist",
102
103
  "src"
103
104
  ],
104
- "gitHead": "a947c08aada0e374aec7dff2b0dc9e540e170834",
105
+ "gitHead": "9c3561bc09311f2339bb6fa224c88a62d3b19617",
105
106
  "publishConfig": {
106
107
  "access": "public"
107
108
  }
@@ -1,13 +1,14 @@
1
1
  import React from "react";
2
2
 
3
- import { focusedMixin } from "../styles";
4
- import { cn } from "../util";
3
+ import {focusedMixin} from "../styles";
4
+ import {cn} from "../util";
5
5
 
6
6
  export type ButtonProps<P extends React.ElementType> =
7
7
  Omit<(P extends "button" ? React.ButtonHTMLAttributes<HTMLButtonElement> : React.ComponentProps<P>), "onClick">
8
8
  & {
9
9
  variant?: "filled" | "outlined" | "text";
10
10
  disabled?: boolean;
11
+ color?: "primary" | "secondary" | "text" | "error";
11
12
  size?: "small" | "medium" | "large";
12
13
  startIcon?: React.ReactNode;
13
14
  fullWidth?: boolean;
@@ -15,32 +16,57 @@ export type ButtonProps<P extends React.ElementType> =
15
16
  onClick?: React.MouseEventHandler<any>
16
17
  };
17
18
 
18
- export function Button<P extends React.ElementType>({
19
- children,
20
- className,
21
- variant = "filled",
22
- disabled = false,
23
- size = "medium",
24
- startIcon = null,
25
- fullWidth = false,
26
- component: Component,
27
- ...props
28
- }: ButtonProps<P>) {
19
+ const ButtonInner = React.forwardRef<
20
+ ButtonProps<React.ElementType<any>>
21
+ >(({
22
+ children,
23
+ className,
24
+ variant = "filled",
25
+ disabled = false,
26
+ size = "medium",
27
+ startIcon = null,
28
+ fullWidth = false,
29
+ component: Component,
30
+ color = "primary",
31
+ ...props
32
+ }: ButtonProps<any>, ref) => {
29
33
 
30
34
  const baseClasses =
31
- "h-fit rounded-md border uppercase inline-flex items-center justify-center p-2 px-4 text-sm font-medium focus:outline-none transition ease-in-out duration-150 gap-2";
35
+ "h-fit rounded-md uppercase inline-flex items-center justify-center p-2 px-4 text-sm font-medium focus:outline-none transition ease-in-out duration-150 gap-2";
32
36
 
33
- const buttonClasses = cn(
34
- {
35
- "w-full": fullWidth,
36
- "w-fit": !fullWidth,
37
- "border-transparent bg-primary hover:bg-primary-dark focus:ring-primary !text-white shadow hover:ring-1 hover:ring-primary": variant === "filled" && !disabled,
38
- "border-transparent !text-primary !hover:text-primary-dark hover:bg-primary hover:bg-primary-bg": variant === "text" && !disabled,
39
- "border-primary !text-primary hover:bg-primary-bg hover:border-primary-dark !hover:text-primary-dark focus:ring-primary hover:ring-1 hover:ring-primary": variant === "outlined" && !disabled,
40
- "border-primary-dark border-opacity-50 dark:border-primary dark:border-opacity-50 opacity-50 !text-primary-dark !dark:text-primary text-opacity-50 dark:text-opacity-50": variant === "outlined" && disabled,
41
- "border-transparent outline-none opacity-50 !text-slate-600 !dark:text-slate-500": (variant === "filled" || variant === "text") && disabled
42
- }
43
- );
37
+ // const buttonClasses = cn(
38
+ // {
39
+ // "w-full": fullWidth,
40
+ // "w-fit": !fullWidth,
41
+ // "border-transparent bg-primary hover:bg-primary-dark focus:ring-primary !text-white shadow hover:ring-1 hover:ring-primary": variant === "filled" && !disabled,
42
+ // "border-transparent !text-primary !hover:text-primary-dark hover:bg-primary hover:bg-primary-bg": variant === "text" && !disabled,
43
+ // "border-primary !text-primary hover:bg-primary-bg hover:border-primary-dark !hover:text-primary-dark focus:ring-primary hover:ring-1 hover:ring-primary": variant === "outlined" && !disabled,
44
+ // "border-primary-dark border-opacity-50 dark:border-primary dark:border-opacity-50 opacity-50 !text-primary-dark !dark:text-primary text-opacity-50 dark:text-opacity-50": variant === "outlined" && disabled,
45
+ // "border-transparent outline-none opacity-50 !text-slate-600 !dark:text-slate-500": (variant === "filled" || variant === "text") && disabled
46
+ // }
47
+ // );
48
+
49
+ const buttonClasses = cn({
50
+ "w-full": fullWidth,
51
+ "w-fit": !fullWidth,
52
+ // Filled Variants
53
+ "border border-transparent bg-primary hover:bg-primary-dark focus:ring-primary shadow hover:ring-1 hover:ring-primary text-white": variant === "filled" && color === "primary" && !disabled,
54
+ "border border-transparent bg-secondary hover:bg-secondary-dark focus:ring-secondary shadow hover:ring-1 hover:ring-secondary text-white": variant === "filled" && color === "secondary" && !disabled,
55
+ "border border-transparent bg-red-500 hover:bg-red-500 focus:ring-red-500 shadow hover:ring-1 hover:ring-red-600 text-white": variant === "filled" && color === "error" && !disabled,
56
+ "border border-transparent bg-slate-200 hover:bg-slate-300 focus:ring-slate-400 shadow hover:ring-1 hover:ring-slate-400 text-text-primary dark:text-text-primary-dark": variant === "filled" && color === "text" && !disabled,
57
+ // Text Variants
58
+ "border border-transparent text-primary hover:bg-slate-200 dark:hover:bg-gray-900": variant === "text" && color === "primary" && !disabled,
59
+ "border border-transparent text-secondary hover:bg-secondary-bg": variant === "text" && color === "secondary" && !disabled,
60
+ "border border-transparent text-red-500 hover:bg-red-500 hover:bg-opacity-10": variant === "text" && color === "error" && !disabled,
61
+ "border border-transparent text-text-primary dark:text-text-primary-dark hover:bg-slate-200 hover:dark:bg-gray-700": variant === "text" && color === "text" && !disabled,
62
+ // Outlined Variants
63
+ "border border-primary text-primary hover:bg-primary-bg": variant === "outlined" && color === "primary" && !disabled,
64
+ "border border-secondary text-secondary hover:bg-secondary-bg": variant === "outlined" && color === "secondary" && !disabled,
65
+ "border border-red-500 text-red-500 hover:bg-red-500": variant === "outlined" && color === "error" && !disabled,
66
+ "border border-slate-400 text-text-primary dark:text-text-primary-dark hover:bg-slate-200": variant === "outlined" && color === "text" && !disabled,
67
+ // Disabled states for all variants
68
+ "border border-transparent opacity-50": disabled
69
+ });
44
70
 
45
71
  const sizeClasses = cn(
46
72
  {
@@ -52,9 +78,11 @@ export function Button<P extends React.ElementType>({
52
78
 
53
79
  if (Component) {
54
80
  return (
55
- <Component onClick={props.onClick}
56
- className={cn(focusedMixin, startIcon ? "pl-3" : "", baseClasses, buttonClasses, sizeClasses, className)}
57
- {...(props as React.ComponentPropsWithRef<P>)}>
81
+ <Component
82
+ ref={ref}
83
+ onClick={props.onClick}
84
+ className={cn(focusedMixin, startIcon ? "pl-3" : "", baseClasses, buttonClasses, sizeClasses, className)}
85
+ {...(props as React.ComponentPropsWithRef<any>)}>
58
86
  {startIcon}
59
87
  {children}
60
88
  </Component>
@@ -62,7 +90,8 @@ export function Button<P extends React.ElementType>({
62
90
  }
63
91
 
64
92
  return (
65
- <button type={props.type ?? "button"}
93
+ <button ref={ref as any}
94
+ type={props.type ?? "button"}
66
95
  onClick={props.onClick}
67
96
  className={cn(focusedMixin, startIcon ? "pl-3" : "", baseClasses, buttonClasses, sizeClasses, className)}
68
97
  disabled={disabled}
@@ -71,4 +100,9 @@ export function Button<P extends React.ElementType>({
71
100
  {children}
72
101
  </button>
73
102
  );
74
- }
103
+
104
+ });
105
+
106
+ ButtonInner.displayName = "Button"
107
+
108
+ export const Button = ButtonInner as React.FC<ButtonProps<any>>;
@@ -88,6 +88,7 @@ export function ExpandablePanel({
88
88
  <Collapsible.Trigger
89
89
  className={cn(focusedMixin,
90
90
  "rounded flex items-center justify-between w-full min-h-[52px]",
91
+ "hover:bg-gray-100 dark:hover:bg-gray-800 dark:hover:bg-opacity-10",
91
92
  invisible ? "border-b px-2" : "p-4",
92
93
  invisible && defaultBorderMixin,
93
94
  asField && fieldBackgroundMixin,
@@ -5,7 +5,7 @@ import { cn } from "../util";
5
5
  export type IconButtonProps<C extends React.ElementType> =
6
6
  Omit<(C extends "button" ? React.ButtonHTMLAttributes<HTMLButtonElement> : React.ComponentProps<C>), "onClick">
7
7
  & {
8
- size?: "medium" | "small" | "large";
8
+ size?: "medium" | "small" | "smallest" | "large";
9
9
  variant?: "ghost" | "filled",
10
10
  shape?: "circular" | "square",
11
11
  disabled?: boolean;
@@ -22,6 +22,7 @@ const colorClasses = "text-slate-600 visited:text-slate-600 dark:text-slate-300
22
22
  const sizeClasses = {
23
23
  medium: "w-10 !h-10 min-w-10 min-h-10",
24
24
  small: "w-8 !h-8 min-w-8 min-h-8",
25
+ smallest: "w-6 !h-6 min-w-6 min-h-6",
25
26
  large: "w-12 !h-12 min-w-12 min-h-12"
26
27
  }
27
28
  const shapeClasses = {
@@ -56,7 +56,8 @@ export function Popover({
56
56
  {trigger}
57
57
  </PopoverPrimitive.Trigger>
58
58
  <PopoverPrimitive.Portal>
59
- <PopoverPrimitive.Content className={cn(paperMixin, "PopoverContent shadow z-40", className)}
59
+ <PopoverPrimitive.Content className={cn(paperMixin,
60
+ "PopoverContent shadow z-40", className)}
60
61
  side={side}
61
62
  sideOffset={sideOffset}
62
63
  align={align}
@@ -0,0 +1,22 @@
1
+ import * as SeparatorPrimitive from "@radix-ui/react-separator";
2
+
3
+ export function Separator({ orientation, decorative }: {
4
+ orientation: "horizontal" | "vertical",
5
+ decorative?: boolean
6
+ }) {
7
+ if (orientation === "horizontal")
8
+ return (
9
+ <SeparatorPrimitive.Root
10
+ decorative={decorative}
11
+ orientation="horizontal"
12
+ className="dark:bg-opacity-50 bg-opacity-50 dark:bg-gray-400 bg-gray-500 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px my-[8px]"/>
13
+ );
14
+ else
15
+ return (
16
+ <SeparatorPrimitive.Root
17
+ className="dark:bg-opacity-50 bg-opacity-50 dark:bg-gray-400 bg-gray-500 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px mx-[8px]"
18
+ decorative={decorative}
19
+ orientation="vertical"
20
+ />
21
+ );
22
+ }
@@ -13,7 +13,7 @@ export const Table = ({
13
13
  className,
14
14
  style
15
15
  }: TableProps) => (
16
- <table className={cn("w-full text-left text-slate-800 dark:text-slate-200 rounded-md overflow-x-auto",
16
+ <table className={cn("w-full text-left text-gray-800 dark:text-slate-200 rounded-md overflow-x-auto",
17
17
  className)}
18
18
  style={style}>
19
19
  {children}
@@ -29,7 +29,7 @@ export const TableBody = ({
29
29
  className
30
30
  }: TableBodyProps) => (
31
31
  <tbody
32
- className={cn("bg-white text-sm dark:bg-slate-800 divide-y divide-slate-200 dark:divide-slate-700", className)}>
32
+ className={cn("bg-white text-sm dark:bg-gray-800 divide-y divide-slate-200 dark:divide-gray-700", className)}>
33
33
  {children}
34
34
  </tbody>
35
35
  );
@@ -46,8 +46,8 @@ export const TableHeader = ({
46
46
  <thead>
47
47
  <tr className={cn(
48
48
  defaultBorderMixin,
49
- "text-sm font-medium text-slate-700 dark:text-slate-300",
50
- "bg-slate-50 border-b dark:bg-slate-900", className)}>
49
+ "text-sm font-medium text-gray-700 dark:text-slate-300",
50
+ "bg-slate-50 border-b dark:bg-gray-900", className)}>
51
51
  {children}
52
52
  </tr>
53
53
  </thead>
@@ -70,9 +70,9 @@ export const TableRow = ({
70
70
  onClick={onClick}
71
71
  style={style}
72
72
  className={cn(
73
- "divide-slate-100 dark:divide-slate-800",
74
- "bg-white dark:bg-slate-950",
75
- onClick ? "hover:bg-slate-100 dark:hover:bg-slate-800 cursor-pointer" : "",
73
+ "divide-slate-100 dark:divide-gray-800",
74
+ "bg-white dark:bg-gray-950",
75
+ onClick ? "hover:bg-slate-100 dark:hover:bg-gray-800 cursor-pointer" : "",
76
76
  className)}
77
77
  >
78
78
  {children}
@@ -1,4 +1,4 @@
1
- import React, { useCallback, useRef } from "react";
1
+ import React, { useCallback, useEffect, useRef } from "react";
2
2
 
3
3
  import { TextareaAutosize } from "./TextareaAutosize";
4
4
  import {
@@ -45,7 +45,7 @@ export type TextFieldProps<T extends string | number> = {
45
45
  style?: React.CSSProperties,
46
46
  inputClassName?: string,
47
47
  inputStyle?: React.CSSProperties,
48
- inputRef?: React.Ref<any>,
48
+ inputRef?: React.ForwardedRef<any>
49
49
  } & Omit<React.InputHTMLAttributes<HTMLInputElement>, "size">;
50
50
 
51
51
  export function TextField<T extends string | number>({
@@ -77,12 +77,26 @@ export function TextField<T extends string | number>({
77
77
  const [focused, setFocused] = React.useState(document.activeElement === inputRef.current);
78
78
  const hasValue = value !== undefined && value !== null && value !== "";
79
79
 
80
+ useEffect(() => {
81
+ if (type !== "number") return;
82
+ const handleWheel = (event: any) => {
83
+ event.preventDefault(); // Prevent scrolling the number input
84
+ };
85
+
86
+ // Current input element
87
+ const element = "current" in inputRef ? inputRef.current : inputRef;
88
+
89
+ // Add the event listener
90
+ element.addEventListener("wheel", handleWheel);
91
+
92
+ // Remove event listener on cleanup
93
+ return () => {
94
+ element.removeEventListener("wheel", handleWheel);
95
+ };
96
+ }, [inputRef, type]);
97
+
80
98
  const numberInputOnWheelPreventChange = useCallback((e: any) => {
81
- e.target.blur()
82
- e.stopPropagation()
83
- setTimeout(() => {
84
- e.target.focus()
85
- }, 0)
99
+ e.preventDefault()
86
100
  }, []);
87
101
 
88
102
  const input = multiline
@@ -159,7 +173,8 @@ export function TextField<T extends string | number>({
159
173
 
160
174
  {input}
161
175
 
162
- {endAdornment && <div className="flex flex-row justify-center items-center absolute h-full right-0 top-0 mr-4 ">{endAdornment}</div>}
176
+ {endAdornment && <div
177
+ className="flex flex-row justify-center items-center absolute h-full right-0 top-0 mr-4 ">{endAdornment}</div>}
163
178
 
164
179
  </div>
165
180
  );
@@ -8,7 +8,7 @@ export type TooltipProps = {
8
8
  onOpenChange?: (open: boolean) => void,
9
9
  side?: "top" | "bottom" | "left" | "right",
10
10
  sideOffset?: number,
11
- title?: string,
11
+ title?: string | React.ReactNode,
12
12
  delayDuration?: number;
13
13
  className?: string,
14
14
  tooltipClassName?: string,
@@ -89,7 +89,7 @@ export function Typography<C extends React.ElementType>(
89
89
  align !== "inherit" && `text-${align}`,
90
90
  gutterBottom && "mb-[0.35em]",
91
91
  noWrap && "truncate",
92
- paragraph && "mb-4",
92
+ paragraph && "mb-3",
93
93
  className
94
94
  );
95
95
 
@@ -25,8 +25,9 @@ export * from "./Markdown";
25
25
  export * from "./Menu";
26
26
  export * from "./MultiSelect";
27
27
  export * from "./Paper";
28
- export * from "./Select";
29
28
  export * from "./SearchBar";
29
+ export * from "./Select";
30
+ export * from "./Separator";
30
31
  export * from "./Sheet";
31
32
  export * from "./Spinner";
32
33
  export * from "./TextareaAutosize";
@@ -0,0 +1,10 @@
1
+ export function HandleIcon() {
2
+ return <svg width="24" height="24" viewBox="0 0 100 100" fill="none">
3
+ <circle cx="28" cy="50" r="9" fill={"currentColor"}/>
4
+ <circle cx="28" cy="21" r="9" fill={"currentColor"}/>
5
+ <circle cx="71" cy="21" r="9" fill={"currentColor"}/>
6
+ <circle cx="71" cy="50" r="9" fill={"currentColor"}/>
7
+ <circle cx="71" cy="78" r="9" fill={"currentColor"}/>
8
+ <circle cx="28" cy="78" r="9" fill={"currentColor"}/>
9
+ </svg>;
10
+ }
@@ -2,6 +2,7 @@ export * from "./icon_keys";
2
2
  export * from "./cool_icon_keys";
3
3
  export * from "./Icon";
4
4
  export * from "./GitHubIcon";
5
+ export * from "./HandleIcon";
5
6
  export * from "./components/_10kIcon";
6
7
  export * from "./components/_10mpIcon";
7
8
  export * from "./components/_11mpIcon";
@@ -18,7 +18,7 @@ export function saveIconFiles(iconKeys: string[]) {
18
18
  fs.mkdirSync(path.join(__dirname, "../icons/components"), { recursive: true });
19
19
 
20
20
  // create empty index file
21
- fs.writeFileSync(path.join(__dirname, "../icons/index.ts"), "export * from \"./icon_keys\";\nexport * from \"./cool_icon_keys\";\nexport * from \"./Icon\";\nexport * from \"./GitHubIcon\";\n");
21
+ fs.writeFileSync(path.join(__dirname, "../icons/index.ts"), "export * from \"./icon_keys\";\nexport * from \"./cool_icon_keys\";\nexport * from \"./Icon\";\nexport * from \"./GitHubIcon\";\nexport * from \"./HandleIcon\";\n");
22
22
 
23
23
  // for each key, generate a file with an Icon ts component
24
24
  iconKeys.forEach((key: string) => {