@mirror-physics/fractal-ui 0.2.0-next.1 → 0.2.0-next.10

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/dist/index.d.ts CHANGED
@@ -180,8 +180,10 @@ interface SortableTableProps<T> {
180
180
  sortDirection?: SortDirection;
181
181
  onSortChange?: (key: string | null, direction: SortDirection) => void;
182
182
  highlightRow?: (row: T, index: number) => boolean;
183
+ /** Invoked when a row is selected. Enables pointer and keyboard row activation. */
184
+ onRowClick?: (row: T, index: number) => void;
183
185
  }
184
- declare function SortableTable<T>({ columns, data, emptyMessage, header, size, className, defaultSortKey, defaultSortDirection, sortKey: controlledKey, sortDirection: controlledDirection, onSortChange, highlightRow, }: SortableTableProps<T>): React.JSX.Element;
186
+ declare function SortableTable<T>({ columns, data, emptyMessage, header, size, className, defaultSortKey, defaultSortDirection, sortKey: controlledKey, sortDirection: controlledDirection, onSortChange, highlightRow, onRowClick, }: SortableTableProps<T>): React.JSX.Element;
185
187
 
186
188
  interface TabItem {
187
189
  id: string;
@@ -231,6 +233,35 @@ interface SidebarNavItemProps extends React.ButtonHTMLAttributes<HTMLButtonEleme
231
233
  declare const SidebarNavItem: React.ForwardRefExoticComponent<SidebarNavItemProps & React.RefAttributes<HTMLButtonElement>>;
232
234
  declare const SidebarFooter: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLDivElement> & React.RefAttributes<HTMLDivElement>>;
233
235
 
236
+ interface SidePanelProps {
237
+ open: boolean;
238
+ onClose: () => void;
239
+ title: React.ReactNode;
240
+ description?: React.ReactNode;
241
+ children: React.ReactNode;
242
+ footer?: React.ReactNode;
243
+ /** Panel width: sm (420px), md (540px), or lg (680px). */
244
+ size?: 'sm' | 'md' | 'lg';
245
+ className?: string;
246
+ }
247
+ declare function SidePanel({ open, onClose, title, description, children, footer, size, className }: SidePanelProps): React.ReactPortal | null;
248
+
249
+ interface TextareaProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {
250
+ }
251
+ declare const Textarea: React.ForwardRefExoticComponent<TextareaProps & React.RefAttributes<HTMLTextAreaElement>>;
252
+
253
+ interface FileDropzoneProps {
254
+ files: File[];
255
+ onFilesChange: (files: File[]) => void;
256
+ accept?: string;
257
+ multiple?: boolean;
258
+ disabled?: boolean;
259
+ title?: React.ReactNode;
260
+ description?: React.ReactNode;
261
+ className?: string;
262
+ }
263
+ declare function FileDropzone({ files, onFilesChange, accept, multiple, disabled, title, description, className, }: FileDropzoneProps): React.JSX.Element;
264
+
234
265
  /**
235
266
  * Register an Escape-key dismiss handler with the global priority stack.
236
267
  *
@@ -242,4 +273,4 @@ declare function useEscapeDismiss(priority: number, handler: () => void, enabled
242
273
 
243
274
  declare function cn(...inputs: ClassValue[]): string;
244
275
 
245
- export { Alert, AlertDescription, AlertTitle, Button, type ButtonColor, type ButtonProps, type ButtonSize, type ButtonType, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Chip, type ChipProps, type ChipTone, DataTable, type DataTableColumn, type DataTableProps, Disclosure, type DisclosureProps, Dropdown, type DropdownItem, type DropdownProps, FeatureCard, type FeatureCardProps, FolderCard, type FolderCardProps, Input, type InputProps, Label, type LabelProps, Link, type LinkProps, type LinkSize, type LinkTheme, Modal, type ModalProps, PromptCard, type PromptCardProps, type PromptCardTone, PromptGrid, type PromptGridProps, Sidebar, SidebarBrand, SidebarFooter, SidebarHeader, SidebarNav, SidebarNavItem, type SidebarNavItemProps, type SidebarProps, SidebarSection, type SortDirection, SortableTable, type SortableTableColumn, type SortableTableProps, type TabItem, Tabs, type TabsProps, Toggle, type ToggleProps, cn, useEscapeDismiss };
276
+ export { Alert, AlertDescription, AlertTitle, Button, type ButtonColor, type ButtonProps, type ButtonSize, type ButtonType, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Chip, type ChipProps, type ChipTone, DataTable, type DataTableColumn, type DataTableProps, Disclosure, type DisclosureProps, Dropdown, type DropdownItem, type DropdownProps, FeatureCard, type FeatureCardProps, FileDropzone, type FileDropzoneProps, FolderCard, type FolderCardProps, Input, type InputProps, Label, type LabelProps, Link, type LinkProps, type LinkSize, type LinkTheme, Modal, type ModalProps, PromptCard, type PromptCardProps, type PromptCardTone, PromptGrid, type PromptGridProps, SidePanel, type SidePanelProps, Sidebar, SidebarBrand, SidebarFooter, SidebarHeader, SidebarNav, SidebarNavItem, type SidebarNavItemProps, type SidebarProps, SidebarSection, type SortDirection, SortableTable, type SortableTableColumn, type SortableTableProps, type TabItem, Tabs, type TabsProps, Textarea, type TextareaProps, Toggle, type ToggleProps, cn, useEscapeDismiss };
package/dist/index.js CHANGED
@@ -50,7 +50,7 @@ var colorPalette = {
50
50
  var sizeConfig = {
51
51
  xs: { height: "28px", padding: "4px 8px", iconOnly: "28px", fontSize: "0.75rem" },
52
52
  sm: { height: "32px", padding: "6px 12px", iconOnly: "32px", fontSize: "0.75rem" },
53
- md: { height: "38px", padding: "8px 16px", iconOnly: "38px", fontSize: "0.875rem" },
53
+ md: { height: "40px", padding: "8px 16px", iconOnly: "40px", fontSize: "0.875rem" },
54
54
  lg: { height: "44px", padding: "10px 20px", iconOnly: "44px", fontSize: "1rem" },
55
55
  xl: { height: "56px", padding: "14px 28px", iconOnly: "56px", fontSize: "1rem" }
56
56
  };
@@ -680,7 +680,8 @@ function SortableTable({
680
680
  sortKey: controlledKey,
681
681
  sortDirection: controlledDirection,
682
682
  onSortChange,
683
- highlightRow
683
+ highlightRow,
684
+ onRowClick
684
685
  }) {
685
686
  const [internalKey, setInternalKey] = useState3(defaultSortKey ?? null);
686
687
  const [internalDirection, setInternalDirection] = useState3(defaultSortDirection);
@@ -781,8 +782,18 @@ function SortableTable({
781
782
  {
782
783
  className: cn(
783
784
  "fractal-sortable-table-row",
784
- highlighted && "fractal-sortable-table-row--highlighted"
785
+ highlighted && "fractal-sortable-table-row--highlighted",
786
+ onRowClick && "fractal-sortable-table-row--interactive"
785
787
  ),
788
+ onClick: onRowClick ? () => onRowClick(row, rowIndex) : void 0,
789
+ onKeyDown: onRowClick ? (event) => {
790
+ if (event.key === "Enter" || event.key === " ") {
791
+ event.preventDefault();
792
+ onRowClick(row, rowIndex);
793
+ }
794
+ } : void 0,
795
+ role: onRowClick ? "button" : void 0,
796
+ tabIndex: onRowClick ? 0 : void 0,
786
797
  children: columns.map((col) => /* @__PURE__ */ jsx12(
787
798
  "td",
788
799
  {
@@ -1027,6 +1038,141 @@ var SidebarFooter = React10.forwardRef(
1027
1038
  ({ className, ...props }, ref) => /* @__PURE__ */ jsx15("div", { ref, className: cn("fractal-sidebar__footer", className), ...props })
1028
1039
  );
1029
1040
  SidebarFooter.displayName = "SidebarFooter";
1041
+
1042
+ // src/components/SidePanel/SidePanel.tsx
1043
+ import { createPortal as createPortal2 } from "react-dom";
1044
+ import { Close } from "@mirror-physics/fractal-icons";
1045
+ import { jsx as jsx16, jsxs as jsxs13 } from "react/jsx-runtime";
1046
+ function SidePanel({ open, onClose, title, description, children, footer, size = "md", className }) {
1047
+ useEscapeDismiss(80, onClose, open);
1048
+ if (!open) return null;
1049
+ return createPortal2(
1050
+ /* @__PURE__ */ jsx16("div", { className: "fractal-side-panel-overlay", onMouseDown: onClose, children: /* @__PURE__ */ jsxs13(
1051
+ "aside",
1052
+ {
1053
+ className: cn("fractal-side-panel", `fractal-side-panel--${size}`, className),
1054
+ "aria-modal": "true",
1055
+ "aria-label": typeof title === "string" ? title : "Side panel",
1056
+ role: "dialog",
1057
+ onMouseDown: (event) => event.stopPropagation(),
1058
+ children: [
1059
+ /* @__PURE__ */ jsxs13("header", { className: "fractal-side-panel__header", children: [
1060
+ /* @__PURE__ */ jsxs13("div", { className: "fractal-side-panel__heading", children: [
1061
+ /* @__PURE__ */ jsx16("h2", { className: "fractal-side-panel__title", children: title }),
1062
+ description && /* @__PURE__ */ jsx16("p", { className: "fractal-side-panel__description", children: description })
1063
+ ] }),
1064
+ /* @__PURE__ */ jsx16("button", { type: "button", className: "fractal-side-panel__close", "aria-label": "Close panel", onClick: onClose, children: /* @__PURE__ */ jsx16(Close, { size: 18 }) })
1065
+ ] }),
1066
+ /* @__PURE__ */ jsx16("div", { className: "fractal-side-panel__body", children }),
1067
+ footer && /* @__PURE__ */ jsx16("footer", { className: "fractal-side-panel__footer", children: footer })
1068
+ ]
1069
+ }
1070
+ ) }),
1071
+ document.body
1072
+ );
1073
+ }
1074
+
1075
+ // src/components/Textarea/Textarea.tsx
1076
+ import * as React11 from "react";
1077
+ import { jsx as jsx17 } from "react/jsx-runtime";
1078
+ var Textarea = React11.forwardRef(
1079
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx17("textarea", { ref, className: cn("fractal-textarea", className), ...props })
1080
+ );
1081
+ Textarea.displayName = "Textarea";
1082
+
1083
+ // src/components/FileDropzone/FileDropzone.tsx
1084
+ import * as React12 from "react";
1085
+ import { Close as Close2, FileArrowUp } from "@mirror-physics/fractal-icons";
1086
+ import { jsx as jsx18, jsxs as jsxs14 } from "react/jsx-runtime";
1087
+ function FileDropzone({
1088
+ files,
1089
+ onFilesChange,
1090
+ accept,
1091
+ multiple = true,
1092
+ disabled = false,
1093
+ title = "Drop files here or click to browse",
1094
+ description = "Files stay attached to this record.",
1095
+ className
1096
+ }) {
1097
+ const inputId = React12.useId();
1098
+ const inputRef = React12.useRef(null);
1099
+ const [isDragging, setIsDragging] = React12.useState(false);
1100
+ const addFiles = (nextFiles) => {
1101
+ if (disabled) return;
1102
+ const additions = Array.from(nextFiles);
1103
+ if (additions.length === 0) return;
1104
+ onFilesChange(multiple ? [...files, ...additions] : additions.slice(0, 1));
1105
+ };
1106
+ return /* @__PURE__ */ jsxs14("div", { className: cn("fractal-file-dropzone", className), children: [
1107
+ /* @__PURE__ */ jsx18(
1108
+ "input",
1109
+ {
1110
+ ref: inputRef,
1111
+ id: inputId,
1112
+ className: "fractal-file-dropzone__input",
1113
+ type: "file",
1114
+ accept,
1115
+ multiple,
1116
+ disabled,
1117
+ onChange: (event) => {
1118
+ if (event.target.files) addFiles(event.target.files);
1119
+ event.target.value = "";
1120
+ }
1121
+ }
1122
+ ),
1123
+ /* @__PURE__ */ jsxs14(
1124
+ "button",
1125
+ {
1126
+ className: "fractal-file-dropzone__target",
1127
+ "data-dragging": isDragging || void 0,
1128
+ type: "button",
1129
+ disabled,
1130
+ onClick: () => inputRef.current?.click(),
1131
+ onDragEnter: (event) => {
1132
+ event.preventDefault();
1133
+ if (!disabled) setIsDragging(true);
1134
+ },
1135
+ onDragOver: (event) => event.preventDefault(),
1136
+ onDragLeave: (event) => {
1137
+ event.preventDefault();
1138
+ setIsDragging(false);
1139
+ },
1140
+ onDrop: (event) => {
1141
+ event.preventDefault();
1142
+ setIsDragging(false);
1143
+ addFiles(event.dataTransfer.files);
1144
+ },
1145
+ children: [
1146
+ /* @__PURE__ */ jsx18(FileArrowUp, { "aria-hidden": "true", size: 20 }),
1147
+ /* @__PURE__ */ jsxs14("span", { className: "fractal-file-dropzone__copy", children: [
1148
+ /* @__PURE__ */ jsx18("span", { className: "fractal-file-dropzone__title", children: title }),
1149
+ description && /* @__PURE__ */ jsx18("span", { className: "fractal-file-dropzone__description", children: description })
1150
+ ] })
1151
+ ]
1152
+ }
1153
+ ),
1154
+ files.length > 0 && /* @__PURE__ */ jsx18("ul", { className: "fractal-file-dropzone__files", "aria-label": "Attached files", children: files.map((file, index) => /* @__PURE__ */ jsxs14("li", { className: "fractal-file-dropzone__file", children: [
1155
+ /* @__PURE__ */ jsx18("span", { className: "fractal-file-dropzone__file-name", children: file.name }),
1156
+ /* @__PURE__ */ jsx18("span", { className: "fractal-file-dropzone__file-meta", children: formatFileSize(file.size) }),
1157
+ /* @__PURE__ */ jsx18(
1158
+ "button",
1159
+ {
1160
+ className: "fractal-file-dropzone__remove",
1161
+ type: "button",
1162
+ "aria-label": `Remove ${file.name}`,
1163
+ disabled,
1164
+ onClick: () => onFilesChange(files.filter((_, fileIndex) => fileIndex !== index)),
1165
+ children: /* @__PURE__ */ jsx18(Close2, { "aria-hidden": "true", size: 14 })
1166
+ }
1167
+ )
1168
+ ] }, `${file.name}-${file.size}-${index}`)) })
1169
+ ] });
1170
+ }
1171
+ function formatFileSize(bytes) {
1172
+ if (bytes < 1024) return `${bytes} B`;
1173
+ if (bytes < 1024 * 1024) return `${Math.round(bytes / 1024)} KB`;
1174
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
1175
+ }
1030
1176
  export {
1031
1177
  Alert,
1032
1178
  AlertDescription,
@@ -1043,6 +1189,7 @@ export {
1043
1189
  Disclosure,
1044
1190
  Dropdown,
1045
1191
  FeatureCard,
1192
+ FileDropzone,
1046
1193
  FolderCard,
1047
1194
  Input,
1048
1195
  Label,
@@ -1050,6 +1197,7 @@ export {
1050
1197
  Modal,
1051
1198
  PromptCard,
1052
1199
  PromptGrid,
1200
+ SidePanel,
1053
1201
  Sidebar,
1054
1202
  SidebarBrand,
1055
1203
  SidebarFooter,
@@ -1059,6 +1207,7 @@ export {
1059
1207
  SidebarSection,
1060
1208
  SortableTable,
1061
1209
  Tabs,
1210
+ Textarea,
1062
1211
  Toggle,
1063
1212
  cn,
1064
1213
  useEscapeDismiss