@firecms/ui 3.0.0-beta.2-pre.5 → 3.0.0-beta.3

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.5",
4
+ "version": "3.0.0-beta.3",
5
5
  "description": "Awesome Firebase/Firestore-based headless open-source CMS",
6
6
  "funding": {
7
7
  "url": "https://github.com/sponsors/firecmsco"
@@ -27,6 +27,14 @@
27
27
  "headless cms",
28
28
  "content manager"
29
29
  ],
30
+ "exports": {
31
+ ".": {
32
+ "import": "./dist/index.es.js",
33
+ "require": "./dist/index.umd.js",
34
+ "types": "./dist/src/index.d.ts"
35
+ },
36
+ "./package.json": "./package.json"
37
+ },
30
38
  "scripts": {
31
39
  "watch": "vite build --watch",
32
40
  "build": "vite build && tsc --emitDeclarationOnly",
@@ -47,6 +55,7 @@
47
55
  "@radix-ui/react-portal": "^1.0.4",
48
56
  "@radix-ui/react-scroll-area": "^1.0.5",
49
57
  "@radix-ui/react-select": "^1.2.2",
58
+ "@radix-ui/react-separator": "^1.0.3",
50
59
  "@radix-ui/react-switch": "^1.0.3",
51
60
  "@radix-ui/react-tabs": "^1.0.4",
52
61
  "@radix-ui/react-tooltip": "^1.0.7",
@@ -101,7 +110,7 @@
101
110
  "dist",
102
111
  "src"
103
112
  ],
104
- "gitHead": "5159646cfdb35e11cc510080bd3cff17104d0cb4",
113
+ "gitHead": "33cceff5d5bc2a7002cc5a3904b787e70bfb9c6e",
105
114
  "publishConfig": {
106
115
  "access": "public"
107
116
  }
@@ -29,7 +29,7 @@ const getColorClasses = (severity: string) => {
29
29
  case "warning":
30
30
  return "bg-amber-50 dark:bg-amber-800 dark:text-amber-100 text-amber-900";
31
31
  case "info":
32
- return "bg-blue-50 dark:bg-blue-800 dark:text-blue-100 text-blue-900";
32
+ return "bg-blue-100 dark:bg-blue-800 dark:text-blue-100 text-blue-900";
33
33
  case "success":
34
34
  return "bg-emerald-50 dark:bg-emerald-800 dark:text-emerald-100 text-emerald-900";
35
35
  case "base":
@@ -55,6 +55,7 @@ export const Alert: React.FC<AlertProps> = ({
55
55
  className={cn(
56
56
  getSizeClasses(size),
57
57
  "w-full",
58
+ "font-medium",
58
59
  "rounded-md flex items-center gap-2",
59
60
  classes,
60
61
  className)}>
@@ -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;
@@ -15,13 +15,14 @@ export type IconButtonProps<C extends React.ElementType> =
15
15
  }
16
16
 
17
17
  const buttonClasses =
18
- "hover:bg-slate-200 hover:bg-opacity-75 dark:hover:bg-slate-700 dark:hover:bg-opacity-75";
18
+ "hover:bg-slate-200 hover:bg-opacity-75 dark:hover:bg-gray-700 dark:hover:bg-opacity-50";
19
19
  const baseClasses =
20
20
  "inline-flex items-center justify-center p-2 text-sm font-medium focus:outline-none transition-colors ease-in-out duration-150";
21
21
  const colorClasses = "text-slate-600 visited:text-slate-600 dark:text-slate-300 dark:visited: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 = {
@@ -41,7 +42,7 @@ const IconButtonInner = <C extends React.ElementType = "button">({
41
42
  ...props
42
43
  }: IconButtonProps<C>, ref: React.ForwardedRef<HTMLButtonElement>) => {
43
44
 
44
- const bgClasses = variant === "ghost" ? "bg-transparent" : "bg-slate-50 dark:bg-slate-900";
45
+ const bgClasses = variant === "ghost" ? "bg-transparent" : "bg-slate-50 dark:bg-gray-950 dark:bg-opacity-50";
45
46
  const Component: React.ElementType<any> = component || "button";
46
47
  return (
47
48
  <Component
@@ -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-600 bg-gray-300 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-600 bg-gray-300 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
+ }
@@ -19,7 +19,7 @@ export function Tabs({
19
19
 
20
20
  return <TabsPrimitive.Root value={value} onValueChange={onValueChange}>
21
21
  <TabsPrimitive.List className={cn(
22
- "flex text-sm font-medium text-center text-slate-800 dark:text-slate-200",
22
+ "flex text-sm font-medium text-center text-slate-800 dark:text-slate-200 max-w-full overflow-auto no-scrollbar",
23
23
  className)
24
24
  }>
25
25
  {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,
@@ -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) => {