@rebasepro/ui 0.1.2 → 0.2.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": "@rebasepro/ui",
3
3
  "type": "module",
4
- "version": "0.1.2",
4
+ "version": "0.2.3",
5
5
  "description": "Awesome Firebase/Firestore-based headless open-source CMS",
6
6
  "funding": {
7
7
  "url": "https://github.com/sponsors/rebaseco"
@@ -34,7 +34,7 @@
34
34
  "exports": {
35
35
  ".": {
36
36
  "types": "./dist/index.d.ts",
37
- "development": "./src/index.ts",
37
+ "development": "./dist/index.es.js",
38
38
  "import": "./dist/index.es.js",
39
39
  "require": "./dist/index.umd.js"
40
40
  },
@@ -53,10 +53,12 @@
53
53
  "@radix-ui/react-label": "^2.1.8",
54
54
  "@radix-ui/react-menubar": "^1.1.16",
55
55
  "@radix-ui/react-popover": "^1.1.15",
56
+ "@radix-ui/react-portal": "^1.1.10",
56
57
  "@radix-ui/react-radio-group": "^1.3.8",
57
58
  "@radix-ui/react-select": "^2.2.6",
58
59
  "@radix-ui/react-separator": "^1.1.8",
59
60
  "@radix-ui/react-slider": "^1.3.6",
61
+ "@radix-ui/react-slot": "^1.2.4",
60
62
  "@radix-ui/react-tabs": "^1.1.13",
61
63
  "@radix-ui/react-tooltip": "^1.2.8",
62
64
  "@radix-ui/react-visually-hidden": "^1.2.4",
@@ -64,41 +66,41 @@
64
66
  "cmdk": "^1.1.1",
65
67
  "fast-equals": "6.0.0",
66
68
  "lucide-react": "1.14.0",
67
- "markdown-it": "^14.1.0",
68
- "react-dropzone": "^14.3.8",
69
+ "markdown-it": "^14.1.1",
70
+ "react-compiler-runtime": "1.0.0",
71
+ "react-dropzone": "^14.4.1",
69
72
  "react-use-measure": "^2.1.7",
70
73
  "react-window": "^1.8.11",
71
- "tailwind-merge": "^2.6.0",
72
- "@rebasepro/types": "0.1.2"
74
+ "tailwind-merge": "^2.6.1",
75
+ "@rebasepro/types": "0.2.3"
73
76
  },
74
77
  "peerDependencies": {
75
78
  "react": ">=19.0.0",
76
79
  "react-dom": ">=19.0.0"
77
80
  },
78
81
  "devDependencies": {
79
- "@jest/globals": "^30.2.0",
82
+ "@jest/globals": "^30.4.1",
80
83
  "@testing-library/jest-dom": "^6.9.1",
81
- "@testing-library/react": "^16.3.0",
84
+ "@testing-library/react": "^16.3.2",
82
85
  "@testing-library/user-event": "^14.6.1",
83
86
  "@types/jest": "^29.5.14",
84
- "@types/node": "^20.17.14",
87
+ "@types/markdown-it": "^14.1.2",
88
+ "@types/node": "^20.19.41",
85
89
  "@types/object-hash": "^3.0.6",
86
- "@types/react": "^19.0.8",
87
- "@types/react-dom": "^19.0.3",
90
+ "@types/react": "^19.2.15",
91
+ "@types/react-dom": "^19.2.3",
88
92
  "@types/react-measure": "^2.0.12",
89
- "@vitejs/plugin-react": "^4.3.4",
93
+ "@vitejs/plugin-react": "^4.7.0",
90
94
  "babel-plugin-react-compiler": "beta",
91
95
  "cross-env": "^7.0.3",
92
- "eslint-plugin-react-compiler": "^19.1.0-rc.2",
93
96
  "jest": "^29.7.0",
94
- "npm-run-all": "^4.1.5",
95
97
  "rollup-preserve-directives": "^1.1.3",
96
- "tailwindcss": "^4.1.3",
97
- "ts-jest": "^29.4.5",
98
+ "tailwindcss": "^4.3.0",
99
+ "ts-jest": "^29.4.10",
98
100
  "tsd": "^0.31.2",
99
101
  "typescript": "^5.9.3",
100
- "vite": "^7.2.4",
101
- "vite-plugin-static-copy": "^3.1.4"
102
+ "vite": "^7.3.3",
103
+ "vite-plugin-static-copy": "^3.4.0"
102
104
  },
103
105
  "files": [
104
106
  "dist",
@@ -29,7 +29,7 @@ export const BooleanSwitch = React.forwardRef(function BooleanSwitch({
29
29
  role="switch"
30
30
  aria-checked={allowIndeterminate && (value === null || value === undefined) ? "mixed" : !!value}
31
31
  aria-disabled={disabled || undefined}
32
- ref={ref}
32
+ ref={ref as any}
33
33
  tabIndex={disabled ? -1 : undefined}
34
34
  onClick={disabled
35
35
  ? (e) => e.preventDefault()
@@ -88,7 +88,7 @@ export const BooleanSwitchWithLabel = function BooleanSwitchWithLabel({
88
88
  if (props.allowIndeterminate) {
89
89
  if (value === null || value === undefined) onValueChange?.(true)
90
90
  else if (value) onValueChange?.(false)
91
- else onValueChange?.(null as unknown as boolean);
91
+ else onValueChange?.(null as unknown as boolean); // SAFETY: null represents indeterminate state when allowIndeterminate is true
92
92
  } else {
93
93
  onValueChange?.(!value);
94
94
  }
@@ -83,7 +83,7 @@ const ButtonInner = React.memo(React.forwardRef<
83
83
  if (Component) {
84
84
  return (
85
85
  <Component
86
- ref={ref}
86
+ ref={ref as any}
87
87
  onClick={props.onClick}
88
88
  className={cls(startIcon ? "pl-3" : "", baseClasses, buttonClasses, sizeClasses, iconColorClass, className)}
89
89
  {...props}>
@@ -94,7 +94,7 @@ const ButtonInner = React.memo(React.forwardRef<
94
94
  }
95
95
 
96
96
  return (
97
- <button ref={ref as React.Ref<HTMLButtonElement>}
97
+ <button ref={ref as any}
98
98
  type={props.type ?? "button"}
99
99
  onClick={props.onClick}
100
100
  className={cls(startIcon ? "pl-3" : "", baseClasses, buttonClasses, sizeClasses, iconColorClass, className)}
@@ -59,12 +59,46 @@ export function Chip({
59
59
 
60
60
  // Resolve theme-aware colors
61
61
  const dark = isDarkMode();
62
- const bgColor = usedColorScheme
63
- ? (dark && usedColorScheme.darkColor ? usedColorScheme.darkColor : usedColorScheme.color)
64
- : undefined;
65
- const textColor = usedColorScheme
66
- ? (dark && usedColorScheme.darkText ? usedColorScheme.darkText : usedColorScheme.text)
67
- : undefined;
62
+
63
+ // Helper to generate rgba from hex or standard colors
64
+ const getRgba = (hex: string, alpha: number): string => {
65
+ if (!hex || !hex.startsWith("#")) return hex;
66
+ let color = hex.slice(1);
67
+ if (color.length === 3) {
68
+ color = color[0] + color[0] + color[1] + color[1] + color[2] + color[2];
69
+ }
70
+ const r = parseInt(color.slice(0, 2), 16);
71
+ const g = parseInt(color.slice(2, 4), 16);
72
+ const b = parseInt(color.slice(4, 6), 16);
73
+ return `rgba(${r}, ${g}, ${b}, ${alpha})`;
74
+ };
75
+
76
+ let textColor = "";
77
+ let bgColor = "";
78
+ let border = "";
79
+
80
+ if (error) {
81
+ textColor = dark ? "#f87171" : "#dc2626";
82
+ } else if (usedColorScheme) {
83
+ textColor = dark && usedColorScheme.darkText ? usedColorScheme.darkText : usedColorScheme.text;
84
+ } else {
85
+ textColor = dark ? "#d4d4d4" : "#404040";
86
+ }
87
+
88
+ if (outlined) {
89
+ bgColor = getRgba(textColor, dark ? 0.12 : 0.06);
90
+ border = `1px solid ${getRgba(textColor, dark ? 0.25 : 0.18)}`;
91
+ } else {
92
+ if (error) {
93
+ bgColor = dark ? "rgba(220, 38, 38, 0.2)" : "rgba(239, 68, 68, 0.15)";
94
+ border = `1px solid ${dark ? "rgba(220, 38, 38, 0.4)" : "rgba(239, 68, 68, 0.3)"}`;
95
+ } else if (usedColorScheme) {
96
+ bgColor = dark && usedColorScheme.darkColor ? usedColorScheme.darkColor : usedColorScheme.color;
97
+ } else {
98
+ bgColor = dark ? "#1f1f1f" : "#f4f4f5";
99
+ border = `1px solid ${dark ? "#2e2e30" : "#e4e4e7"}`;
100
+ }
101
+ }
68
102
 
69
103
  return (
70
104
  <div
@@ -73,13 +107,12 @@ export function Chip({
73
107
  "items-center",
74
108
  onClick ? "cursor-pointer hover:bg-surface-accent-300 dark:hover:bg-surface-accent-700" : "",
75
109
  sizeClassNames[size],
76
- error || !usedColorScheme ? "bg-surface-accent-200 dark:bg-surface-accent-800 text-surface-accent-800 dark:text-white" : "",
77
- error ? "text-red-500 dark:text-red-400" : "",
78
110
  className)}
79
111
  onClick={onClick}
80
112
  style={{
81
- backgroundColor: error ? undefined : bgColor,
82
- color: error ? undefined : textColor,
113
+ backgroundColor: bgColor,
114
+ color: textColor,
115
+ border: border || undefined,
83
116
  overflow: "hidden",
84
117
  ...style
85
118
  }}
@@ -0,0 +1,84 @@
1
+ import React from "react";
2
+ import { cls } from "../util";
3
+
4
+ export interface FilterChipProps {
5
+ /**
6
+ * The text label displayed on the chip.
7
+ */
8
+ children: React.ReactNode;
9
+ /**
10
+ * Whether the chip is currently in an active/selected state.
11
+ */
12
+ active?: boolean;
13
+ /**
14
+ * Callback when the chip is clicked.
15
+ */
16
+ onClick?: () => void;
17
+ /**
18
+ * Optional icon rendered before the label.
19
+ */
20
+ icon?: React.ReactNode;
21
+ /**
22
+ * Size variant.
23
+ * @default "medium"
24
+ */
25
+ size?: "small" | "medium";
26
+ /**
27
+ * Additional class names.
28
+ */
29
+ className?: string;
30
+ /**
31
+ * Whether the chip is disabled.
32
+ */
33
+ disabled?: boolean;
34
+ }
35
+
36
+ const sizeClasses = {
37
+ small: "px-2 py-0.5 text-xs",
38
+ medium: "px-2.5 py-1 text-xs"
39
+ };
40
+
41
+ /**
42
+ * A toggle chip used for filter presets and similar multi-select controls.
43
+ *
44
+ * Uses an inset box-shadow for the active ring instead of `border` so the
45
+ * chip size stays stable across states and the ring cannot be clipped by
46
+ * parent `overflow-hidden` containers.
47
+ *
48
+ * @group Interactive components
49
+ */
50
+ export function FilterChip({
51
+ children,
52
+ active = false,
53
+ onClick,
54
+ icon,
55
+ size = "medium",
56
+ className,
57
+ disabled = false
58
+ }: FilterChipProps) {
59
+ return (
60
+ <button
61
+ type="button"
62
+ onClick={onClick}
63
+ disabled={disabled}
64
+ className={cls(
65
+ "inline-flex items-center gap-1 rounded-full",
66
+ "font-medium whitespace-nowrap select-none shrink-0",
67
+ "transition-all duration-150",
68
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/50",
69
+ sizeClasses[size],
70
+ active
71
+ ? "bg-primary/12 text-primary dark:bg-primary/20 dark:text-primary shadow-[inset_0_0_0_1.5px_var(--color-primary)]"
72
+ : cls(
73
+ "bg-surface-accent-100 text-text-secondary dark:bg-surface-accent-800 dark:text-text-secondary-dark",
74
+ !disabled && "cursor-pointer hover:bg-surface-accent-200 dark:hover:bg-surface-accent-700"
75
+ ),
76
+ disabled && "opacity-50 cursor-not-allowed",
77
+ className
78
+ )}
79
+ >
80
+ {icon}
81
+ {children}
82
+ </button>
83
+ );
84
+ }
@@ -2,7 +2,6 @@
2
2
  import React, { useMemo } from "react";
3
3
  import { deepEqual as equal } from "fast-equals"
4
4
 
5
- // @ts-ignore
6
5
  import MarkdownIt from "markdown-it";
7
6
  import { cls } from "../util";
8
7
 
@@ -35,7 +34,7 @@ const proseWeightOverrides = [
35
34
  "prose-a:font-normal"
36
35
  ].join(" ");
37
36
 
38
- const md = new MarkdownIt({ html: true });
37
+ const md = new MarkdownIt({ html: false });
39
38
  /**
40
39
  * @group Preview components
41
40
  */
@@ -95,7 +95,7 @@ export function SearchBar({
95
95
  </div>
96
96
  <input
97
97
  value={searchText ?? ""}
98
- ref={inputRef}
98
+ ref={inputRef as any}
99
99
  onClick={onClick}
100
100
  placeholder={placeholder}
101
101
  aria-label={placeholder}
@@ -104,7 +104,7 @@ export const Select = forwardRef<HTMLDivElement, SelectProps>(({
104
104
  name,
105
105
  value: typedValue
106
106
  }
107
- } as unknown as ChangeEvent<HTMLSelectElement>;
107
+ } as ChangeEvent<HTMLSelectElement>;
108
108
  onChange(event);
109
109
  }
110
110
  }, [onChange, onValueChange, name, dataType]);
@@ -82,7 +82,8 @@ export const TextField = forwardRef<HTMLDivElement, TextFieldProps<string | numb
82
82
  ref: ForwardedRef<HTMLDivElement>
83
83
  ) => {
84
84
 
85
- const inputRef = inputRefProp ?? useRef(null);
85
+ const fallbackRef = useRef(null);
86
+ const inputRef = inputRefProp ?? fallbackRef;
86
87
  const autoId = useId();
87
88
  const inputId = inputProps.id ?? autoId;
88
89
  const labelId = `${inputId}-label`;
@@ -260,7 +260,7 @@ export const TextareaAutosize = React.forwardRef(function TextareaAutosize(
260
260
  value={value}
261
261
  onChange={handleChange}
262
262
  className={props.className}
263
- ref={handleRef}
263
+ ref={handleRef as any}
264
264
  onFocus={onFocus}
265
265
  onBlur={onBlur}
266
266
  // Apply the rows prop to get a "correct" first SSR paint
@@ -279,7 +279,7 @@ export const TextareaAutosize = React.forwardRef(function TextareaAutosize(
279
279
  aria-hidden
280
280
  className={cls(props.className, props.shadowClassName)}
281
281
  readOnly
282
- ref={shadowRef}
282
+ ref={shadowRef as any}
283
283
  tabIndex={-1}
284
284
  style={{
285
285
  padding: 0,
@@ -384,8 +384,9 @@ export const VirtualTable = React.memo<VirtualTableProps<any>>(
384
384
  className,
385
385
  draggingColumnId && "overflow-hidden"
386
386
  )}>
387
+ {/* SAFETY: T extends Record<string, any> is structurally assignable to Record<string, unknown> */}
387
388
  <VirtualListContext.Provider
388
- value={virtualListController as unknown as VirtualTableContextProps<Record<string, unknown>>}>
389
+ value={virtualListController as VirtualTableContextProps<Record<string, unknown>>}>
389
390
 
390
391
  <MemoizedList
391
392
  outerRef={tableRef}
@@ -182,7 +182,7 @@ export const VirtualTableHeader = React.memo<VirtualTableHeaderProps<any>>(
182
182
  </div>}
183
183
 
184
184
  {column.resizable && <div
185
- ref={resizeHandleRef}
185
+ ref={resizeHandleRef as any}
186
186
  data-no-dnd="true"
187
187
  className={cls(
188
188
  "absolute h-full w-[6px] top-0 right-0 cursor-col-resize",
@@ -22,6 +22,7 @@ export * from "./DialogContent";
22
22
  export * from "./DialogTitle";
23
23
  export * from "./ExpandablePanel";
24
24
  export * from "./FileUpload";
25
+ export * from "./FilterChip";
25
26
  export * from "./IconButton";
26
27
  export * from "./InputLabel";
27
28
  export * from "./InfoLabel";
@@ -51,3 +52,10 @@ export * from "./DebouncedTextField";
51
52
  export * from "./Skeleton";
52
53
  export * from "./ToggleButtonGroup";
53
54
  export * from "./VirtualTable";
55
+
56
+ // Re-exported Radix primitives — consume these from @rebasepro/ui instead of
57
+ // adding direct @radix-ui/* deps to every sub-package.
58
+ import * as Portal from "@radix-ui/react-portal";
59
+ import * as PopoverPrimitive from "@radix-ui/react-popover";
60
+ export { Portal, PopoverPrimitive };
61
+ export { Slot } from "@radix-ui/react-slot";
@@ -3,4 +3,127 @@ export * from "./cool_icon_keys";
3
3
  export * from "./Icon";
4
4
  export * from "./GitHubIcon";
5
5
  export * from "./HandleIcon";
6
+
7
+ // Re-export types and the full icons map from lucide-react.
6
8
  export type { LucideProps, LucideIcon } from "lucide-react";
9
+ export { icons as lucideIcons } from "lucide-react";
10
+
11
+ // Re-export individual icon components used across the monorepo.
12
+ // Sub-packages import these from @rebasepro/ui instead of declaring a direct
13
+ // lucide-react dependency. When a new icon is needed, add it here.
14
+ export {
15
+ AlertCircleIcon,
16
+ AlertTriangleIcon,
17
+ AlignLeftIcon,
18
+ AppWindow,
19
+ ArrowDownToLineIcon,
20
+ ArrowLeftIcon,
21
+ ArrowRightFromLineIcon,
22
+ ArrowRightIcon,
23
+ ArrowRightToLineIcon,
24
+ ArrowUpToLineIcon,
25
+ BoldIcon,
26
+ BookOpenIcon,
27
+ CalendarIcon,
28
+ CheckCircleIcon,
29
+ CheckIcon,
30
+ CheckSquareIcon,
31
+ ChevronDownIcon,
32
+ ChevronLeftIcon,
33
+ ChevronRightIcon,
34
+ ChevronUpIcon,
35
+ ChevronsLeftIcon,
36
+ ChevronsRightIcon,
37
+ ChevronsUpDownIcon,
38
+ CircleIcon,
39
+ CircleUserIcon,
40
+ CodeIcon,
41
+ ColumnsIcon,
42
+ CopyIcon,
43
+ DatabaseIcon,
44
+ DownloadIcon,
45
+ ExternalLinkIcon,
46
+ EyeIcon,
47
+ EyeOffIcon,
48
+ FileIcon,
49
+ FileSearchIcon,
50
+ FileTextIcon,
51
+ FilterIcon,
52
+ FilterXIcon,
53
+ FlagIcon,
54
+ FolderIcon,
55
+ FolderPlusIcon,
56
+ FolderUpIcon,
57
+ FunctionSquareIcon,
58
+ GitBranchIcon,
59
+ GlobeIcon,
60
+ HashIcon,
61
+ Heading1Icon,
62
+ Heading2Icon,
63
+ Heading3Icon,
64
+ HelpCircleIcon,
65
+ HistoryIcon,
66
+ HomeIcon,
67
+ ImageIcon,
68
+ ImageOffIcon,
69
+ InfoIcon,
70
+ ItalicIcon,
71
+ KanbanIcon,
72
+ KeyIcon,
73
+ KeyRoundIcon,
74
+ LanguagesIcon,
75
+ LayoutGridIcon,
76
+ LinkIcon,
77
+ ListIcon,
78
+ ListOrderedIcon,
79
+ LoaderIcon,
80
+ LogOutIcon,
81
+ MailIcon,
82
+ Maximize2Icon,
83
+ MenuIcon,
84
+ MinusCircleIcon,
85
+ MinusIcon,
86
+ MoonIcon,
87
+ MoreVerticalIcon,
88
+ Music2Icon,
89
+ PanelLeftIcon,
90
+ PauseIcon,
91
+ PencilIcon,
92
+ PhoneIcon,
93
+ PlayIcon,
94
+ PlusIcon,
95
+ QuoteIcon,
96
+ RefreshCcwIcon,
97
+ RefreshCwIcon,
98
+ RepeatIcon,
99
+ Rows3Icon,
100
+ SaveIcon,
101
+ SearchIcon,
102
+ SendIcon,
103
+ SettingsIcon,
104
+ ShieldIcon,
105
+ ShoppingCartIcon,
106
+ SlidersHorizontalIcon,
107
+ SquareIcon,
108
+ StarIcon,
109
+ StrikethroughIcon,
110
+ SunIcon,
111
+ SunMoonIcon,
112
+ TableIcon,
113
+ TagIcon,
114
+ TerminalIcon,
115
+ TextIcon,
116
+ Trash2Icon,
117
+ TypeIcon,
118
+ UnderlineIcon,
119
+ UndoIcon,
120
+ UploadCloudIcon,
121
+ UploadIcon,
122
+ UserCheckIcon,
123
+ UserIcon,
124
+ VideoIcon,
125
+ VoteIcon,
126
+ Wand2Icon,
127
+ XCircleIcon,
128
+ XIcon,
129
+ } from "lucide-react";