@flikk/ui 1.0.0-beta.22 → 1.0.0-beta.24
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/components/ai/PromptInput/PromptInput.js +139 -7
- package/dist/components/ai/PromptInput/VoiceRecorder.js +84 -0
- package/dist/components/charts/AreaChart/AreaChart.types.d.ts +2 -2
- package/dist/components/charts/BarChart/BarChart.types.d.ts +2 -2
- package/dist/components/charts/ComboChart/ComboChart.types.d.ts +2 -2
- package/dist/components/charts/LineChart/LineChart.types.d.ts +2 -2
- package/dist/components/charts/ScatterPlot/ScatterPlot.types.d.ts +2 -2
- package/dist/components/charts/StackedBarChart/StackedBarChart.types.d.ts +2 -2
- package/dist/components/core/Accordion/AccordionTrigger.js +28 -1
- package/dist/components/core/Alert/Alert.js +2 -1
- package/dist/components/core/AlertDialog/AlertDialog.js +1 -1
- package/dist/components/core/Badge/Badge.js +6 -17
- package/dist/components/core/Breadcrumbs/Breadcrumbs.js +17 -28
- package/dist/components/core/Calendar/Calendar.js +1 -1
- package/dist/components/core/Carousel/CarouselBody.js +6 -1
- package/dist/components/core/ContextMenu/ContextMenuBody.js +6 -3
- package/dist/components/core/ContextMenu/ContextMenuSubBody.js +6 -3
- package/dist/components/core/DragDrop/DragDrop.js +170 -17
- package/dist/components/core/DragDrop/DragDrop.types.d.ts +6 -0
- package/dist/components/core/Drawer/Drawer.d.ts +1 -1
- package/dist/components/core/Drawer/Drawer.js +2 -0
- package/dist/components/core/Drawer/Drawer.types.d.ts +2 -0
- package/dist/components/core/Drawer/DrawerTitle.d.ts +1 -1
- package/dist/components/core/Drawer/DrawerTitle.js +2 -2
- package/dist/components/core/Dropdown/DropdownMenu.js +2 -30
- package/dist/components/core/Masonry/Masonry.js +7 -2
- package/dist/components/core/Modal/Modal.d.ts +1 -1
- package/dist/components/core/Modal/Modal.js +3 -3
- package/dist/components/core/Modal/Modal.types.d.ts +2 -0
- package/dist/components/core/Modal/ModalTitle.d.ts +1 -1
- package/dist/components/core/Modal/ModalTitle.js +2 -2
- package/dist/components/core/NavItem/NavItem.js +21 -6
- package/dist/components/core/NavItem/NavItem.theme.js +2 -2
- package/dist/components/core/Pagination/Pagination.js +1 -1
- package/dist/components/core/Popover/PopoverBody.js +3 -27
- package/dist/components/core/Popover/PopoverTrigger.js +1 -1
- package/dist/components/core/Progress/Progress.js +4 -4
- package/dist/components/core/Rating/Rating.js +1 -1
- package/dist/components/core/ScrollArea/ScrollArea.js +3 -2
- package/dist/components/core/Segmented/Segmented.js +6 -6
- package/dist/components/core/Segmented/SegmentedItem.js +28 -2
- package/dist/components/core/Sidebar/Sidebar.d.ts +1 -0
- package/dist/components/core/Sidebar/Sidebar.js +5 -3
- package/dist/components/core/Sidebar/Sidebar.types.d.ts +7 -1
- package/dist/components/core/Sidebar/SidebarContent.js +1 -1
- package/dist/components/core/Sidebar/SidebarFooter.js +1 -1
- package/dist/components/core/Sidebar/SidebarHeader.js +5 -2
- package/dist/components/core/Sidebar/SidebarLogo.d.ts +3 -0
- package/dist/components/core/Sidebar/SidebarLogo.js +17 -0
- package/dist/components/core/Sidebar/SidebarNavGroup.js +2 -1
- package/dist/components/core/Sidebar/SidebarToggle.js +2 -4
- package/dist/components/core/Sidebar/SidebarUserProfile.js +3 -6
- package/dist/components/core/Sidebar/index.d.ts +2 -1
- package/dist/components/core/Skeleton/Skeleton.js +2 -2
- package/dist/components/core/Sortable/Sortable.d.ts +1 -1
- package/dist/components/core/Sortable/Sortable.js +20 -4
- package/dist/components/core/Sortable/Sortable.types.d.ts +2 -0
- package/dist/components/core/Tabs/Tabs.js +6 -4
- package/dist/components/core/Tabs/Tabs.types.d.ts +2 -0
- package/dist/components/core/Tabs/TabsBody.js +3 -3
- package/dist/components/core/Tabs/TabsList.js +19 -21
- package/dist/components/core/Tabs/TabsTrigger.js +47 -28
- package/dist/components/core/Toast/Toast.js +1 -1
- package/dist/components/core/Toast/ToastProvider.js +3 -2
- package/dist/components/core/Tooltip/Tooltip.d.ts +1 -1
- package/dist/components/core/Tooltip/Tooltip.js +26 -35
- package/dist/components/core/Tooltip/Tooltip.types.d.ts +5 -1
- package/dist/components/core/Tree/Tree.js +178 -8
- package/dist/components/core/index.js +1 -0
- package/dist/components/data-display/GanttChart/GanttSplitter.js +15 -1
- package/dist/components/data-display/Table/Table.js +2 -2
- package/dist/components/data-display/Table/Table.types.d.ts +2 -0
- package/dist/components/data-display/Table/TableFilter.js +21 -18
- package/dist/components/effects/CustomCursor/CustomCursor.js +3 -7
- package/dist/components/effects/Overlay/Overlay.js +4 -9
- package/dist/components/effects/PageTransition/PageTransition.js +17 -6
- package/dist/components/effects/SpotlightBorder/SpotlightBorder.js +3 -4
- package/dist/components/forms/Checkbox/Checkbox.js +11 -1
- package/dist/components/forms/Checkbox/Checkbox.theme.js +4 -1
- package/dist/components/forms/Checkbox/Checkbox.types.d.ts +7 -5
- package/dist/components/forms/Checkbox/CheckboxGroup.js +9 -4
- package/dist/components/forms/ColorPicker/ColorPicker.js +7 -4
- package/dist/components/forms/ColorPicker/ColorPicker.types.d.ts +2 -2
- package/dist/components/forms/ColorPicker/ColorPickerBody.js +5 -3
- package/dist/components/forms/ColorPicker/ColorPickerEyeDropper.js +3 -3
- package/dist/components/forms/ColorPicker/ColorPickerGradient.js +8 -8
- package/dist/components/forms/ColorPicker/ColorPickerInput.js +1 -1
- package/dist/components/forms/ColorPicker/index.d.ts +1 -1
- package/dist/components/forms/Combobox/Combobox.js +20 -45
- package/dist/components/forms/Combobox/Combobox.types.d.ts +4 -2
- package/dist/components/forms/CronInput/CronInput.js +10 -4
- package/dist/components/forms/CronInput/CronInput.types.d.ts +12 -1
- package/dist/components/forms/DatePicker/DatePicker.js +2 -2
- package/dist/components/forms/DatePicker/DatePicker.types.d.ts +4 -2
- package/dist/components/forms/DatePicker/DatePickerBody.js +1 -29
- package/dist/components/forms/DatePicker/DatePickerTrigger.js +2 -2
- package/dist/components/forms/DateRangePicker/DateRangePicker.js +3 -3
- package/dist/components/forms/DateRangePicker/DateRangePicker.types.d.ts +4 -2
- package/dist/components/forms/DateRangePicker/DateRangePickerBody.js +1 -28
- package/dist/components/forms/DateRangePicker/DateRangePickerTrigger.js +2 -2
- package/dist/components/forms/FileUpload/FileUpload.js +99 -10
- package/dist/components/forms/FileUpload/FileUpload.types.d.ts +15 -0
- package/dist/components/forms/FormLabel/FormLabel.js +2 -2
- package/dist/components/forms/Input/Input.js +5 -3
- package/dist/components/forms/Input/Input.types.d.ts +2 -0
- package/dist/components/forms/InputAddress/InputAddress.d.ts +1 -1
- package/dist/components/forms/InputAddress/InputAddress.js +8 -9
- package/dist/components/forms/InputCounter/InputCounter.js +25 -18
- package/dist/components/forms/InputCounter/InputCounter.types.d.ts +8 -1
- package/dist/components/forms/InputCreditCard/InputCreditCard.d.ts +1 -1
- package/dist/components/forms/InputCreditCard/InputCreditCard.js +27 -9
- package/dist/components/forms/InputCreditCard/InputCreditCard.types.d.ts +5 -0
- package/dist/components/forms/InputOTP/InputOTP.js +26 -17
- package/dist/components/forms/InputOTP/InputOTP.types.d.ts +21 -4
- package/dist/components/forms/InputTag/InputTag.js +12 -39
- package/dist/components/forms/InputTag/InputTag.types.d.ts +2 -2
- package/dist/components/forms/Mention/Mention.js +13 -6
- package/dist/components/forms/Mention/Mention.types.d.ts +9 -0
- package/dist/components/forms/Radio/Radio.js +12 -2
- package/dist/components/forms/Radio/Radio.theme.js +5 -1
- package/dist/components/forms/Radio/Radio.types.d.ts +7 -5
- package/dist/components/forms/Radio/RadioGroup.js +8 -3
- package/dist/components/forms/RichTextEditor/RichTextEditor.js +21 -21
- package/dist/components/forms/RichTextEditor/RichTextEditor.types.d.ts +2 -2
- package/dist/components/forms/Select/Select.d.ts +2 -0
- package/dist/components/forms/Select/Select.js +26 -56
- package/dist/components/forms/Select/Select.types.d.ts +14 -0
- package/dist/components/forms/Signature/Signature.js +1 -1
- package/dist/components/forms/Signature/Signature.types.d.ts +3 -3
- package/dist/components/forms/Slider/Slider.js +18 -4
- package/dist/components/forms/Slider/Slider.types.d.ts +2 -2
- package/dist/components/forms/Switch/Switch.js +12 -19
- package/dist/components/forms/Switch/Switch.theme.js +12 -2
- package/dist/components/forms/Switch/Switch.types.d.ts +20 -2
- package/dist/components/forms/Textarea/Textarea.js +8 -3
- package/dist/components/forms/Textarea/Textarea.types.d.ts +2 -0
- package/dist/components/forms/TimePicker/TimePicker.js +1 -1
- package/dist/components/forms/TimePicker/TimePicker.types.d.ts +6 -2
- package/dist/components/forms/TimePicker/TimePickerContent.js +88 -30
- package/dist/components/forms/TimePicker/TimePickerTrigger.js +2 -2
- package/dist/components/forms/index.js +86 -1
- package/dist/components/layout/FormLayout/FormLayout.js +87 -1
- package/dist/components/layout/index.js +4 -0
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/useClickOutside.d.ts +10 -4
- package/dist/hooks/useClickOutside.js +44 -24
- package/dist/hooks/useDarkMode.d.ts +1 -0
- package/dist/hooks/useDarkMode.js +42 -0
- package/dist/hooks/useFormStateMachine.d.ts +1 -1
- package/dist/hooks/useNetworkStatus.js +1 -1
- package/dist/hooks/useOverlay.js +4 -0
- package/dist/hooks/useScaleBackground.js +5 -2
- package/dist/hooks/useSelectPortal.js +14 -5
- package/dist/index.d.ts +0 -2
- package/dist/index.js +174 -223
- package/dist/shadcn-compat.css +80 -0
- package/dist/styles.css +1 -1
- package/dist/utils/stateMachine.d.ts +4 -4
- package/package.json +12 -10
- package/tailwind.preset.cjs +32 -3
- /package/{src/theme-plugin.css → dist/theme.css} +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx } from 'react/jsx-runtime';
|
|
2
|
-
import { useRef, useCallback } from 'react';
|
|
2
|
+
import { useRef, useEffect, useCallback } from 'react';
|
|
3
3
|
import { cn } from '../../../utils/cn.js';
|
|
4
4
|
import { useGanttContext } from './GanttChart.js';
|
|
5
5
|
|
|
@@ -10,6 +10,18 @@ const GanttSplitter = () => {
|
|
|
10
10
|
const { theme, tablePanelWidth, setTablePanelWidth } = useGanttContext();
|
|
11
11
|
const startX = useRef(0);
|
|
12
12
|
const startWidth = useRef(0);
|
|
13
|
+
// Track active pointer listeners so they can be cleaned up on unmount during a drag
|
|
14
|
+
const activeListenersRef = useRef({ move: null, up: null });
|
|
15
|
+
// Cleanup on unmount — removes any lingering listeners if component unmounts during a drag
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
return () => {
|
|
18
|
+
const { move, up } = activeListenersRef.current;
|
|
19
|
+
if (move)
|
|
20
|
+
document.removeEventListener("pointermove", move);
|
|
21
|
+
if (up)
|
|
22
|
+
document.removeEventListener("pointerup", up);
|
|
23
|
+
};
|
|
24
|
+
}, []);
|
|
13
25
|
const onPointerDown = useCallback((e) => {
|
|
14
26
|
e.preventDefault();
|
|
15
27
|
startX.current = e.clientX;
|
|
@@ -20,9 +32,11 @@ const GanttSplitter = () => {
|
|
|
20
32
|
setTablePanelWidth(next);
|
|
21
33
|
};
|
|
22
34
|
const onPointerUp = () => {
|
|
35
|
+
activeListenersRef.current = { move: null, up: null };
|
|
23
36
|
document.removeEventListener("pointermove", onPointerMove);
|
|
24
37
|
document.removeEventListener("pointerup", onPointerUp);
|
|
25
38
|
};
|
|
39
|
+
activeListenersRef.current = { move: onPointerMove, up: onPointerUp };
|
|
26
40
|
document.addEventListener("pointermove", onPointerMove);
|
|
27
41
|
document.addEventListener("pointerup", onPointerUp);
|
|
28
42
|
}, [tablePanelWidth, setTablePanelWidth]);
|
|
@@ -38,7 +38,7 @@ const TableContext = createContext({
|
|
|
38
38
|
*/
|
|
39
39
|
function TableComponent(props) {
|
|
40
40
|
var _a;
|
|
41
|
-
const { data, columns, rowKey, variant = "default", theme: themeOverrides, className = "", freezeHeader = false, freezeFirstColumn = false, freezeLastColumn = false, sortable = false, sortConfig = [], onSortChange, filterable = false, filterConfig = [], onFilterChange, filterLogic: controlledFilterLogic, enableBuiltInFilter = false, expandable = false, expandedRowRender, expandedRows = [], onExpandedRowsChange, selectable = false, selectionType = "checkbox", selectedRows = [], onSelectionChange, bulkActions = [], showColumnManager = false, visibleColumns: initialVisibleColumns, onVisibleColumnsChange, onColumnReorder, pagination, children, } = props;
|
|
41
|
+
const { data, columns, rowKey, variant = "default", theme: themeOverrides, className = "", freezeHeader = false, freezeFirstColumn = false, freezeLastColumn = false, sortable = false, sortConfig = [], onSortChange, filterable = false, filterConfig = [], onFilterChange, filterLogic: controlledFilterLogic, enableBuiltInFilter = false, expandable = false, expandedRowRender, expandedRows = [], onExpandedRowsChange, selectable = false, selectionType = "checkbox", selectedRows = [], onSelectionChange, bulkActions = [], showColumnManager = false, visibleColumns: initialVisibleColumns, onVisibleColumnsChange, onColumnReorder, pagination, children, caption, } = props;
|
|
42
42
|
// Column management hook
|
|
43
43
|
const { visibleColumns, columnOrder, visibleColumnsList, handleToggleColumn, handleColumnReorder, handleResetToDefault, } = useTableColumns({
|
|
44
44
|
columns,
|
|
@@ -148,7 +148,7 @@ function TableComponent(props) {
|
|
|
148
148
|
selectedRows,
|
|
149
149
|
onSelectionChange,
|
|
150
150
|
]);
|
|
151
|
-
return (jsx(TableContext.Provider, { value: contextValue, children: jsxs("div", { className: "relative", children: [!isDeclarativeAPI && columns && ((filterable && onFilterChange) || showColumnManager) && (jsxs("div", { className: theme.columnManagerContainer, children: [filterable && onFilterChange && (jsx(TableFilter, { columns: columns, filterConfig: filterConfig, onFilterChange: onFilterChange, filterLogic: filterLogic, onFilterLogicChange: handleFilterLogicChange, data: data })), showColumnManager && (jsx(TableColumnManager, { columns: columns, visibleColumns: visibleColumns, columnOrder: columnOrder, onToggleColumn: handleToggleColumn, onResetToDefault: handleResetToDefault, onColumnReorder: onColumnReorder ? handleColumnReorder : undefined }))] })), !isDeclarativeAPI && selectable && selectedRows.length > 0 && (jsx(TableSelectionHeader, { selectedCount: selectedRows.length, totalCount: paginatedData.length, selectedRows: selectedRows, bulkActions: bulkActions, onClearSelection: handleClearSelection })), jsx("div", { className: "overflow-x-auto", ref: scrollContainerRef, children:
|
|
151
|
+
return (jsx(TableContext.Provider, { value: contextValue, children: jsxs("div", { className: "relative", children: [!isDeclarativeAPI && columns && ((filterable && onFilterChange) || showColumnManager) && (jsxs("div", { className: theme.columnManagerContainer, children: [filterable && onFilterChange && (jsx(TableFilter, { columns: columns, filterConfig: filterConfig, onFilterChange: onFilterChange, filterLogic: filterLogic, onFilterLogicChange: handleFilterLogicChange, data: data })), showColumnManager && (jsx(TableColumnManager, { columns: columns, visibleColumns: visibleColumns, columnOrder: columnOrder, onToggleColumn: handleToggleColumn, onResetToDefault: handleResetToDefault, onColumnReorder: onColumnReorder ? handleColumnReorder : undefined }))] })), !isDeclarativeAPI && selectable && selectedRows.length > 0 && (jsx(TableSelectionHeader, { selectedCount: selectedRows.length, totalCount: paginatedData.length, selectedRows: selectedRows, bulkActions: bulkActions, onClearSelection: handleClearSelection })), jsx("div", { className: "overflow-x-auto", ref: scrollContainerRef, children: jsxs("table", { className: cn(baseClasses, variantClasses, className), role: "table", "aria-label": isDeclarativeAPI ? "Data table" : `Data table with ${paginatedData.length} rows`, "aria-rowcount": pagination ? pagination.totalItems : data === null || data === void 0 ? void 0 : data.length, "aria-colcount": visibleColumnsList.length, children: [caption && (jsx("caption", { className: "sr-only", children: caption })), isDeclarativeAPI ? (children) : (jsxs(Fragment, { children: [jsx(TableHeader, { columns: visibleColumnsList, sortConfig: sortConfig, onSortChange: onSortChange, filterConfig: filterConfig, onFilterChange: onFilterChange, freezeHeader: freezeHeader, freezeFirstColumn: freezeFirstColumn, freezeLastColumn: freezeLastColumn, expandable: expandable, expandedRowRender: expandedRowRender, selectable: selectable, selectionType: selectionType, selectedRows: selectedRows, onSelectionChange: onSelectionChange, totalRowCount: paginatedData.length, theme: theme, data: paginatedData, rowKey: rowKey }), jsx(TableBody, { data: paginatedData, columns: visibleColumnsList, rowKey: rowKey, expandedRowRender: expandedRowRender, expandedRows: expandedRows, onExpandedRowsChange: onExpandedRowsChange, selectable: selectable, selectionType: selectionType, selectedRows: selectedRows, onSelectionChange: onSelectionChange, freezeFirstColumn: freezeFirstColumn, freezeLastColumn: freezeLastColumn, theme: theme })] }))] }) })] }) }));
|
|
152
152
|
}
|
|
153
153
|
TableComponent.displayName = "Table";
|
|
154
154
|
const Table = TableComponent;
|
|
@@ -193,6 +193,8 @@ export interface TableProps<T = unknown> {
|
|
|
193
193
|
totalItems: number;
|
|
194
194
|
};
|
|
195
195
|
children?: ReactNode;
|
|
196
|
+
/** Optional accessible caption for the table, rendered as a visually-hidden <caption> element */
|
|
197
|
+
caption?: string;
|
|
196
198
|
}
|
|
197
199
|
export interface TableHeaderProps {
|
|
198
200
|
children?: ReactNode;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
-
import { useState, useCallback } from 'react';
|
|
3
|
-
import { inferFilterType, getDefaultOperator, isUnaryOperator,
|
|
2
|
+
import { useRef, useState, useCallback } from 'react';
|
|
3
|
+
import { inferFilterType, getDefaultOperator, isUnaryOperator, isBetweenOperator, getOperatorsForFilterType } from './Table.filterUtils.js';
|
|
4
4
|
import { Button } from '../../core/Button/Button.js';
|
|
5
5
|
import { Input } from '../../forms/Input/Input.js';
|
|
6
6
|
import '../../forms/Input/Input.theme.js';
|
|
@@ -57,6 +57,7 @@ function FilterValueInput({ filter, column, filterType, onValueChange, }) {
|
|
|
57
57
|
}
|
|
58
58
|
function TableFilter({ columns, filterConfig, onFilterChange, filterLogic: controlledLogic, onFilterLogicChange, data, className, }) {
|
|
59
59
|
const filterableColumns = columns.filter((col) => col.filterable);
|
|
60
|
+
const filterIdRef = useRef(0);
|
|
60
61
|
// Internal filter logic state
|
|
61
62
|
const [internalLogic, setInternalLogic] = useState("and");
|
|
62
63
|
const filterLogic = controlledLogic !== null && controlledLogic !== void 0 ? controlledLogic : internalLogic;
|
|
@@ -87,7 +88,7 @@ function TableFilter({ columns, filterConfig, onFilterChange, filterLogic: contr
|
|
|
87
88
|
const resolvedType = inferFilterType(defaultColumn, data);
|
|
88
89
|
return [
|
|
89
90
|
{
|
|
90
|
-
id: `filter-${
|
|
91
|
+
id: `filter-${++filterIdRef.current}`,
|
|
91
92
|
columnId: defaultColumn.id,
|
|
92
93
|
operator: getDefaultOperator(resolvedType),
|
|
93
94
|
value: "",
|
|
@@ -107,7 +108,7 @@ function TableFilter({ columns, filterConfig, onFilterChange, filterLogic: contr
|
|
|
107
108
|
return "text";
|
|
108
109
|
return inferFilterType(column, data);
|
|
109
110
|
};
|
|
110
|
-
const syncFilters = (filters) => {
|
|
111
|
+
const syncFilters = useCallback((filters) => {
|
|
111
112
|
const newFilterConfig = filters
|
|
112
113
|
.filter((f) => {
|
|
113
114
|
if (!f.columnId)
|
|
@@ -131,8 +132,8 @@ function TableFilter({ columns, filterConfig, onFilterChange, filterLogic: contr
|
|
|
131
132
|
operator: f.operator,
|
|
132
133
|
}));
|
|
133
134
|
onFilterChange(newFilterConfig);
|
|
134
|
-
};
|
|
135
|
-
const handleAddFilter = () => {
|
|
135
|
+
}, [onFilterChange]);
|
|
136
|
+
const handleAddFilter = useCallback(() => {
|
|
136
137
|
const defaultColumn = filterableColumns[0];
|
|
137
138
|
if (!defaultColumn)
|
|
138
139
|
return;
|
|
@@ -141,21 +142,22 @@ function TableFilter({ columns, filterConfig, onFilterChange, filterLogic: contr
|
|
|
141
142
|
setActiveFilters((prev) => [
|
|
142
143
|
...prev,
|
|
143
144
|
{
|
|
144
|
-
id: `filter-${
|
|
145
|
+
id: `filter-${++filterIdRef.current}`,
|
|
145
146
|
columnId: defaultColumn.id,
|
|
146
147
|
operator: defaultOp,
|
|
147
148
|
value: "",
|
|
148
149
|
},
|
|
149
150
|
]);
|
|
150
|
-
|
|
151
|
-
|
|
151
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
152
|
+
}, [filterableColumns, onFilterChange]);
|
|
153
|
+
const handleRemoveFilter = useCallback((filterId) => {
|
|
152
154
|
setActiveFilters((prev) => {
|
|
153
155
|
const updated = prev.filter((f) => f.id !== filterId);
|
|
154
156
|
syncFilters(updated);
|
|
155
157
|
return updated;
|
|
156
158
|
});
|
|
157
|
-
};
|
|
158
|
-
const handleFilterColumnChange = (filterId, columnId) => {
|
|
159
|
+
}, [syncFilters]);
|
|
160
|
+
const handleFilterColumnChange = useCallback((filterId, columnId) => {
|
|
159
161
|
setActiveFilters((prev) => {
|
|
160
162
|
const updated = prev.map((f) => {
|
|
161
163
|
if (f.id !== filterId)
|
|
@@ -168,8 +170,9 @@ function TableFilter({ columns, filterConfig, onFilterChange, filterLogic: contr
|
|
|
168
170
|
syncFilters(updated);
|
|
169
171
|
return updated;
|
|
170
172
|
});
|
|
171
|
-
|
|
172
|
-
|
|
173
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
174
|
+
}, [filterableColumns, data, syncFilters]);
|
|
175
|
+
const handleFilterOperatorChange = useCallback((filterId, operator) => {
|
|
173
176
|
setActiveFilters((prev) => {
|
|
174
177
|
const updated = prev.map((f) => {
|
|
175
178
|
if (f.id !== filterId)
|
|
@@ -188,18 +191,18 @@ function TableFilter({ columns, filterConfig, onFilterChange, filterLogic: contr
|
|
|
188
191
|
syncFilters(updated);
|
|
189
192
|
return updated;
|
|
190
193
|
});
|
|
191
|
-
};
|
|
192
|
-
const handleFilterValueChange = (filterId, value) => {
|
|
194
|
+
}, [syncFilters]);
|
|
195
|
+
const handleFilterValueChange = useCallback((filterId, value) => {
|
|
193
196
|
setActiveFilters((prev) => {
|
|
194
197
|
const updated = prev.map((f) => f.id === filterId ? { ...f, value } : f);
|
|
195
198
|
syncFilters(updated);
|
|
196
199
|
return updated;
|
|
197
200
|
});
|
|
198
|
-
};
|
|
199
|
-
const handleClearAll = () => {
|
|
201
|
+
}, [syncFilters]);
|
|
202
|
+
const handleClearAll = useCallback(() => {
|
|
200
203
|
setActiveFilters([]);
|
|
201
204
|
onFilterChange([]);
|
|
202
|
-
};
|
|
205
|
+
}, [onFilterChange]);
|
|
203
206
|
const appliedFilterCount = activeFilters.filter((f) => {
|
|
204
207
|
if (!f.columnId)
|
|
205
208
|
return false;
|
|
@@ -204,13 +204,9 @@ const CustomCursorFollow = React.forwardRef(({ children, sideOffset = 15, align
|
|
|
204
204
|
const { cursorPos, isActive, cursorRef } = useCustomCursor();
|
|
205
205
|
const cursorFollowRef = React.useRef(null);
|
|
206
206
|
const shouldReduceMotion = useReducedMotion();
|
|
207
|
-
// Select random color for user variant if not provided (stable across renders)
|
|
208
|
-
const
|
|
209
|
-
|
|
210
|
-
return color;
|
|
211
|
-
const randomIndex = Math.floor(Math.random() * userVariantColors.length);
|
|
212
|
-
return userVariantColors[randomIndex];
|
|
213
|
-
}, [color]);
|
|
207
|
+
// Select random color for user variant if not provided (stable across renders via ref)
|
|
208
|
+
const randomColorRef = React.useRef(userVariantColors[Math.floor(Math.random() * userVariantColors.length)]);
|
|
209
|
+
const resolvedColor = color !== null && color !== void 0 ? color : randomColorRef.current;
|
|
214
210
|
React.useImperativeHandle(ref, () => cursorFollowRef.current);
|
|
215
211
|
const x = useMotionValue(0);
|
|
216
212
|
const y = useMotionValue(0);
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
2
|
-
import React__default, { useRef, useCallback, useEffect
|
|
2
|
+
import React__default, { useRef, useCallback, useEffect } from 'react';
|
|
3
3
|
import { createPortal } from 'react-dom';
|
|
4
4
|
import { useReducedMotion, AnimatePresence, motion } from 'motion/react';
|
|
5
5
|
import { cn } from '../../../utils/cn.js';
|
|
6
6
|
import { useOverlay, lockScroll, unlockScroll } from '../../../hooks/useOverlay.js';
|
|
7
7
|
import { useFocusTrap } from '../../../hooks/useFocusTrap.js';
|
|
8
8
|
import { useIsClient } from '../../../hooks/useIsClient.js';
|
|
9
|
+
import { useDarkMode } from '../../../hooks/useDarkMode.js';
|
|
9
10
|
import { overlayTheme, overlayPositionVariants } from './Overlay.theme.js';
|
|
10
11
|
|
|
11
12
|
/**
|
|
@@ -138,14 +139,8 @@ const Overlay = React__default.forwardRef(({ isOpen, onClose, closeOnOverlayClic
|
|
|
138
139
|
};
|
|
139
140
|
}
|
|
140
141
|
}
|
|
141
|
-
// Detect dark mode
|
|
142
|
-
const isDarkMode =
|
|
143
|
-
if (!isClient)
|
|
144
|
-
return false;
|
|
145
|
-
return (document.documentElement.classList.contains("dark") ||
|
|
146
|
-
document.body.classList.contains("dark") ||
|
|
147
|
-
document.querySelector(".dark") !== null);
|
|
148
|
-
}, [isClient]);
|
|
142
|
+
// Detect dark mode reactively via a shared MutationObserver-backed hook (SSR-safe)
|
|
143
|
+
const isDarkMode = useDarkMode();
|
|
149
144
|
// Determine portal target
|
|
150
145
|
const target = portalTarget !== undefined
|
|
151
146
|
? portalTarget
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
2
|
-
import { useState, useEffect } from 'react';
|
|
2
|
+
import { useState, useRef, useEffect } from 'react';
|
|
3
3
|
import { motion } from 'motion/react';
|
|
4
4
|
import { cn } from '../../../utils/cn.js';
|
|
5
5
|
import { buildCurtainVariants, buildLogoParallaxVariants, buildSplitVariants } from './PageTransition.animations.js';
|
|
@@ -7,7 +7,14 @@ import { buildCurtainVariants, buildLogoParallaxVariants, buildSplitVariants } f
|
|
|
7
7
|
const PageTransition = ({ children, logo, type = "slide", direction = "up", duration = 0.8, delay = 0.8, color = "bg-[var(--color-primary)]", className, parallax = true, onStart, onComplete, }) => {
|
|
8
8
|
const [phase, setPhase] = useState("covered");
|
|
9
9
|
const [completedPanels, setCompletedPanels] = useState(0);
|
|
10
|
+
// Stable refs for callbacks to avoid stale closures without re-running the effect
|
|
11
|
+
const onStartRef = useRef(onStart);
|
|
12
|
+
const onCompleteRef = useRef(onComplete);
|
|
13
|
+
useEffect(() => { onStartRef.current = onStart; }, [onStart]);
|
|
14
|
+
useEffect(() => { onCompleteRef.current = onComplete; }, [onComplete]);
|
|
10
15
|
useEffect(() => {
|
|
16
|
+
if (typeof window === "undefined" || typeof document === "undefined")
|
|
17
|
+
return;
|
|
11
18
|
let cancelled = false;
|
|
12
19
|
const loadPromise = new Promise((resolve) => {
|
|
13
20
|
if (document.readyState === "complete") {
|
|
@@ -24,8 +31,9 @@ const PageTransition = ({ children, logo, type = "slide", direction = "up", dura
|
|
|
24
31
|
if (!cancelled) {
|
|
25
32
|
setPhase("exit");
|
|
26
33
|
setTimeout(() => {
|
|
34
|
+
var _a;
|
|
27
35
|
if (!cancelled)
|
|
28
|
-
|
|
36
|
+
(_a = onStartRef.current) === null || _a === void 0 ? void 0 : _a.call(onStartRef);
|
|
29
37
|
}, 250);
|
|
30
38
|
}
|
|
31
39
|
});
|
|
@@ -34,10 +42,11 @@ const PageTransition = ({ children, logo, type = "slide", direction = "up", dura
|
|
|
34
42
|
};
|
|
35
43
|
}, [delay]);
|
|
36
44
|
useEffect(() => {
|
|
45
|
+
var _a;
|
|
37
46
|
if (type === "split" && completedPanels === 2) {
|
|
38
|
-
|
|
47
|
+
(_a = onCompleteRef.current) === null || _a === void 0 ? void 0 : _a.call(onCompleteRef);
|
|
39
48
|
}
|
|
40
|
-
}, [completedPanels, type
|
|
49
|
+
}, [completedPanels, type]);
|
|
41
50
|
if (type === "split") {
|
|
42
51
|
const axis = direction === "up" || direction === "down" ? "h" : "v";
|
|
43
52
|
const splitVars = buildSplitVariants(axis, duration);
|
|
@@ -58,13 +67,15 @@ const PageTransition = ({ children, logo, type = "slide", direction = "up", dura
|
|
|
58
67
|
if (type === "slide" && parallax) {
|
|
59
68
|
const logoVariants = buildLogoParallaxVariants(direction, duration);
|
|
60
69
|
return (jsxs(Fragment, { children: [children, jsx(motion.div, { className: cn("fixed inset-0 z-[9999] flex items-center justify-center", color, className), style: { willChange: "transform" }, variants: curtainVariants, initial: "covered", animate: phase, onAnimationComplete: (def) => {
|
|
70
|
+
var _a;
|
|
61
71
|
if (def === "exit")
|
|
62
|
-
|
|
72
|
+
(_a = onCompleteRef.current) === null || _a === void 0 ? void 0 : _a.call(onCompleteRef);
|
|
63
73
|
}, children: logo && (jsx(motion.div, { className: "relative z-10 select-none", variants: logoVariants, initial: "covered", animate: phase, children: logo })) })] }));
|
|
64
74
|
}
|
|
65
75
|
return (jsxs(Fragment, { children: [children, jsx(motion.div, { className: cn("fixed inset-0 z-[9999] flex items-center justify-center", color, className), style: { willChange: "transform" }, variants: curtainVariants, initial: "covered", animate: phase, onAnimationComplete: (def) => {
|
|
76
|
+
var _a;
|
|
66
77
|
if (def === "exit")
|
|
67
|
-
|
|
78
|
+
(_a = onCompleteRef.current) === null || _a === void 0 ? void 0 : _a.call(onCompleteRef);
|
|
68
79
|
}, children: logo && jsx("div", { className: "relative z-10 select-none", children: logo }) })] }));
|
|
69
80
|
};
|
|
70
81
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx } from 'react/jsx-runtime';
|
|
2
2
|
import { useRef, useEffect } from 'react';
|
|
3
|
+
import { useReducedMotion } from 'motion/react';
|
|
3
4
|
import * as THREE from 'three';
|
|
4
5
|
import { cn } from '../../../utils/cn.js';
|
|
5
6
|
import { variationMap } from './SpotlightBorder.types.js';
|
|
@@ -21,12 +22,10 @@ import { fragmentShader, vertexShader } from './SpotlightBorder.shaders.js';
|
|
|
21
22
|
* ```
|
|
22
23
|
*/
|
|
23
24
|
const SpotlightBorder = ({ children, variation = "rounded-rect", rounded, borderWidth = 2, color = "#ffffff", spotlightSize = 0.4, spotlightEdge = 0.5, glowExtent = 20, glowIntensity = 0.6, tiltIntensity = 5, shouldAnimate = true, className, style, ...props }) => {
|
|
24
|
-
var _a;
|
|
25
25
|
const outerRef = useRef(null);
|
|
26
26
|
const containerRef = useRef(null);
|
|
27
|
-
//
|
|
28
|
-
const prefersReducedMotion =
|
|
29
|
-
((_a = window.matchMedia) === null || _a === void 0 ? void 0 : _a.call(window, "(prefers-reduced-motion: reduce)").matches);
|
|
27
|
+
// useReducedMotion is SSR-safe (returns null on server, actual value after hydration)
|
|
28
|
+
const prefersReducedMotion = useReducedMotion();
|
|
30
29
|
const isAnimating = shouldAnimate && !prefersReducedMotion;
|
|
31
30
|
// Refs to store THREE.js objects across effects
|
|
32
31
|
const rendererRef = useRef(null);
|
|
@@ -22,6 +22,7 @@ const CheckboxComponent = React__default.forwardRef(({ id: idProp, name: namePro
|
|
|
22
22
|
const size = (_c = groupContext === null || groupContext === void 0 ? void 0 : groupContext.size) !== null && _c !== void 0 ? _c : sizeProp;
|
|
23
23
|
const state = stateProp === "disabled" ? "disabled" : ((_d = groupContext === null || groupContext === void 0 ? void 0 : groupContext.state) !== null && _d !== void 0 ? _d : stateProp);
|
|
24
24
|
const isDisabled = state === "disabled";
|
|
25
|
+
const isInvalid = state === "invalid";
|
|
25
26
|
// Determine checked state
|
|
26
27
|
const isInGroup = groupContext !== undefined;
|
|
27
28
|
const [internalChecked, setInternalChecked] = useState(false);
|
|
@@ -42,6 +43,15 @@ const CheckboxComponent = React__default.forwardRef(({ id: idProp, name: namePro
|
|
|
42
43
|
inputRef.current.indeterminate = indeterminate;
|
|
43
44
|
}
|
|
44
45
|
}, [indeterminate]);
|
|
46
|
+
// Dev-only warning when no accessible name is provided
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
49
|
+
if (!label && !restProps['aria-label'] && !restProps['aria-labelledby']) {
|
|
50
|
+
console.warn('[Checkbox]: No accessible name provided. Add a `label` prop, `aria-label`, or `aria-labelledby`.');
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
54
|
+
}, []);
|
|
45
55
|
const handleChange = (e) => {
|
|
46
56
|
if (isDisabled)
|
|
47
57
|
return;
|
|
@@ -59,7 +69,7 @@ const CheckboxComponent = React__default.forwardRef(({ id: idProp, name: namePro
|
|
|
59
69
|
ref(node);
|
|
60
70
|
else if (ref)
|
|
61
71
|
ref.current = node;
|
|
62
|
-
}, type: "checkbox", id: id, name: name, value: value, checked: isChecked, onChange: handleChange, disabled: isDisabled, "aria-describedby": description ? `${id}-description` : undefined, "data-state": isDisabled ? "disabled" : "default", className: cn(checkboxTheme.inputStyle, checkboxTheme.sizes[size], indeterminate && "bg-neutral-200") }), jsxs(AnimatePresence, { mode: "wait", children: [indeterminate && (jsx(motion.svg, { viewBox: "0 0 16 16", fill: "none", className: cn("pointer-events-none col-start-1 row-start-1 self-center justify-self-center text-neutral-600", size === "sm" ? "size-3" : "size-4"), children: jsx(motion.path, { d: "M3.5 8 L12.5 8", stroke: "currentColor", strokeWidth: 2.5, strokeLinecap: "round", variants: shouldReduceMotion ? reducedMotionVariants : indeterminateVariants, initial: "unchecked", animate: "checked", exit: "unchecked" }) }, "indeterminate")), isChecked && !indeterminate && (jsx(motion.svg, { viewBox: "0 0 16 16", fill: "none", className: cn("pointer-events-none col-start-1 row-start-1 self-center justify-self-center text-white", size === "sm" ? "size-3" : "size-4"), children: jsx(motion.path, { d: "M3.5 8.5 L6.5 11.5 L12.5 4.5", stroke: "currentColor", strokeWidth: 2.5, strokeLinecap: "round", strokeLinejoin: "round", variants: shouldReduceMotion ? reducedMotionVariants : checkmarkVariants, initial: "unchecked", animate: "checked", exit: "unchecked" }) }, "checkmark"))] })] }), (label || description) && (jsxs("label", { htmlFor: id, className: cn("cursor-pointer", isDisabled && "cursor-not-allowed"), children: [label && (typeof label === 'string' ? (jsx("span", { className: cn(formLabelTheme.baseStyle, formLabelTheme.stateStyles[state], "mb-0"), children: label })) : (label)), description && (typeof description === 'string' ? (jsx("div", { id: `${id}-description`, className: checkboxTheme.descriptionStyle, children: description })) : (description))] }))] }));
|
|
72
|
+
}, type: "checkbox", id: id, name: name, value: value, checked: isChecked, onChange: handleChange, disabled: isDisabled, "aria-describedby": description ? `${id}-description` : undefined, "aria-invalid": isInvalid || undefined, "data-state": isDisabled ? "disabled" : isInvalid ? "invalid" : "default", className: cn(checkboxTheme.inputStyle, checkboxTheme.sizes[size], indeterminate && "bg-neutral-200") }), jsxs(AnimatePresence, { mode: "wait", children: [indeterminate && (jsx(motion.svg, { viewBox: "0 0 16 16", fill: "none", className: cn("pointer-events-none col-start-1 row-start-1 self-center justify-self-center text-neutral-600", size === "sm" ? "size-3" : size === "lg" ? "size-5" : "size-4"), children: jsx(motion.path, { d: "M3.5 8 L12.5 8", stroke: "currentColor", strokeWidth: 2.5, strokeLinecap: "round", variants: shouldReduceMotion ? reducedMotionVariants : indeterminateVariants, initial: "unchecked", animate: "checked", exit: "unchecked" }) }, "indeterminate")), isChecked && !indeterminate && (jsx(motion.svg, { viewBox: "0 0 16 16", fill: "none", className: cn("pointer-events-none col-start-1 row-start-1 self-center justify-self-center text-white", size === "sm" ? "size-3" : size === "lg" ? "size-5" : "size-4"), children: jsx(motion.path, { d: "M3.5 8.5 L6.5 11.5 L12.5 4.5", stroke: "currentColor", strokeWidth: 2.5, strokeLinecap: "round", strokeLinejoin: "round", variants: shouldReduceMotion ? reducedMotionVariants : checkmarkVariants, initial: "unchecked", animate: "checked", exit: "unchecked" }) }, "checkmark"))] })] }), (label || description) && (jsxs("label", { htmlFor: id, className: cn("cursor-pointer", isDisabled && "cursor-not-allowed"), children: [label && (typeof label === 'string' ? (jsx("span", { className: cn(formLabelTheme.baseStyle, formLabelTheme.stateStyles[state], "mb-0"), children: label })) : (label)), description && (typeof description === 'string' ? (jsx("div", { id: `${id}-description`, className: checkboxTheme.descriptionStyle, children: description })) : (description))] }))] }));
|
|
63
73
|
});
|
|
64
74
|
CheckboxComponent.displayName = "Checkbox";
|
|
65
75
|
const Checkbox = Object.assign(CheckboxComponent, {
|
|
@@ -24,12 +24,15 @@ const checkboxTheme = {
|
|
|
24
24
|
"data-[state=disabled]:border-[var(--color-border)] data-[state=disabled]:bg-[var(--color-background-disabled)] data-[state=disabled]:cursor-not-allowed " +
|
|
25
25
|
// Disabled state - dark
|
|
26
26
|
"dark:disabled:bg-[var(--color-neutral-800)] dark:disabled:border-[var(--color-neutral-600)] " +
|
|
27
|
-
"dark:data-[state=disabled]:bg-[var(--color-neutral-700)] dark:data-[state=disabled]:border-[var(--color-neutral-600)]"
|
|
27
|
+
"dark:data-[state=disabled]:bg-[var(--color-neutral-700)] dark:data-[state=disabled]:border-[var(--color-neutral-600)] " +
|
|
28
|
+
// Invalid state
|
|
29
|
+
"data-[state=invalid]:border-[var(--color-danger)] data-[state=invalid]:focus-visible:outline-[var(--color-danger)]",
|
|
28
30
|
// Description text - light and dark
|
|
29
31
|
descriptionStyle: "text-sm mt-0.5 " + "text-[var(--color-text-secondary)]/80 " + " ",
|
|
30
32
|
sizes: {
|
|
31
33
|
sm: "size-5 mt-0.25",
|
|
32
34
|
md: "size-6",
|
|
35
|
+
lg: "size-7",
|
|
33
36
|
},
|
|
34
37
|
// Group styles
|
|
35
38
|
groupStyle: "flex flex-col gap-1.5",
|
|
@@ -2,11 +2,11 @@ import React from 'react';
|
|
|
2
2
|
/**
|
|
3
3
|
* State variants for the checkbox component
|
|
4
4
|
*/
|
|
5
|
-
export type CheckboxState = 'default' | 'disabled';
|
|
5
|
+
export type CheckboxState = 'default' | 'disabled' | 'invalid';
|
|
6
6
|
/**
|
|
7
7
|
* Size variants for the checkbox component
|
|
8
8
|
*/
|
|
9
|
-
export type CheckboxSize = 'sm' | 'md';
|
|
9
|
+
export type CheckboxSize = 'sm' | 'md' | 'lg';
|
|
10
10
|
/**
|
|
11
11
|
* Orientation for Checkbox.Group layout
|
|
12
12
|
*/
|
|
@@ -14,11 +14,9 @@ export type CheckboxGroupOrientation = 'vertical' | 'horizontal';
|
|
|
14
14
|
/**
|
|
15
15
|
* Checkbox component props
|
|
16
16
|
*/
|
|
17
|
-
export interface CheckboxProps extends Omit<React.
|
|
17
|
+
export interface CheckboxProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'onChange' | 'size'> {
|
|
18
18
|
id?: string;
|
|
19
|
-
name?: string;
|
|
20
19
|
value: string;
|
|
21
|
-
checked?: boolean;
|
|
22
20
|
indeterminate?: boolean;
|
|
23
21
|
onChange?: (checked: boolean, value: string) => void;
|
|
24
22
|
label?: React.ReactNode;
|
|
@@ -40,7 +38,11 @@ export interface CheckboxGroupProps extends Omit<React.HTMLAttributes<HTMLFieldS
|
|
|
40
38
|
orientation?: CheckboxGroupOrientation;
|
|
41
39
|
label?: React.ReactNode;
|
|
42
40
|
description?: React.ReactNode;
|
|
41
|
+
/**
|
|
42
|
+
* @deprecated Use `state='invalid'` with `errorMessage` instead.
|
|
43
|
+
*/
|
|
43
44
|
error?: React.ReactNode;
|
|
45
|
+
errorMessage?: React.ReactNode;
|
|
44
46
|
required?: boolean;
|
|
45
47
|
className?: string;
|
|
46
48
|
}
|
|
@@ -5,7 +5,12 @@ import { checkboxTheme } from './Checkbox.theme.js';
|
|
|
5
5
|
import { formLabelTheme } from '../FormLabel/FormLabel.theme.js';
|
|
6
6
|
import { CheckboxGroupContext } from './CheckboxContext.js';
|
|
7
7
|
|
|
8
|
-
const CheckboxGroup = React__default.forwardRef(({ value: controlledValue, defaultValue, onChange, name, size = "md", state = "default", orientation = "vertical", label, description, error, required, className, children, ...props }, ref) => {
|
|
8
|
+
const CheckboxGroup = React__default.forwardRef(({ value: controlledValue, defaultValue, onChange, name, size = "md", state: stateProp = "default", orientation = "vertical", label, description, error, errorMessage, required, className, children, ...props }, ref) => {
|
|
9
|
+
// Prefer errorMessage; fall back to deprecated error prop
|
|
10
|
+
const resolvedError = errorMessage !== null && errorMessage !== void 0 ? errorMessage : error;
|
|
11
|
+
// If error (deprecated) is provided and state not explicitly set to something else,
|
|
12
|
+
// implicitly treat the group as invalid
|
|
13
|
+
const state = resolvedError && stateProp === "default" ? "invalid" : stateProp;
|
|
9
14
|
const autoId = useId();
|
|
10
15
|
const groupName = name !== null && name !== void 0 ? name : `checkbox-group-${autoId}`;
|
|
11
16
|
const labelId = `${autoId}-label`;
|
|
@@ -31,9 +36,9 @@ const CheckboxGroup = React__default.forwardRef(({ value: controlledValue, defau
|
|
|
31
36
|
size,
|
|
32
37
|
state,
|
|
33
38
|
}), [groupName, currentValue, size, state]);
|
|
34
|
-
const labelState =
|
|
35
|
-
return (jsx(CheckboxGroupContext.Provider, { value: contextValue, children: jsxs("fieldset", { ref: ref, role: "group", "aria-labelledby": label ? labelId : undefined, "aria-describedby": description ? descriptionId : undefined, "aria-required": required || undefined, "aria-invalid":
|
|
36
|
-
checkboxTheme.groupHorizontalStyle), children: children }),
|
|
39
|
+
const labelState = state === "invalid" ? "invalid" : state === "disabled" ? "disabled" : "default";
|
|
40
|
+
return (jsx(CheckboxGroupContext.Provider, { value: contextValue, children: jsxs("fieldset", { ref: ref, role: "group", "aria-labelledby": label ? labelId : undefined, "aria-describedby": description ? descriptionId : undefined, "aria-required": required || undefined, "aria-invalid": resolvedError ? true : undefined, "aria-errormessage": resolvedError ? errorId : undefined, className: cn("border-0 p-0 m-0", checkboxTheme.groupStyle, className), ...props, children: [label && (typeof label === 'string' ? (jsxs("legend", { id: labelId, className: cn(formLabelTheme.baseStyle, formLabelTheme.stateStyles[labelState]), children: [label, required && (jsx("span", { className: formLabelTheme.requiredIndicatorStyle, children: "*" }))] })) : (label)), description && (typeof description === 'string' ? (jsx("p", { id: descriptionId, className: checkboxTheme.groupDescriptionStyle, children: description })) : (description)), jsx("div", { className: cn(checkboxTheme.groupItemsStyle, orientation === "horizontal" &&
|
|
41
|
+
checkboxTheme.groupHorizontalStyle), children: children }), resolvedError && (typeof resolvedError === 'string' ? (jsx("p", { id: errorId, role: "alert", className: checkboxTheme.groupErrorStyle, children: resolvedError })) : (resolvedError))] }) }));
|
|
37
42
|
});
|
|
38
43
|
CheckboxGroup.displayName = "Checkbox.Group";
|
|
39
44
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
2
|
-
import React__default, { useMemo, useState, useCallback,
|
|
2
|
+
import React__default, { useRef, useMemo, useState, useCallback, useEffect } from 'react';
|
|
3
3
|
import { cn } from '../../../utils/cn.js';
|
|
4
4
|
import { FormLabel } from '../FormLabel/FormLabel.js';
|
|
5
5
|
import { colorPickerTheme } from './ColorPicker.theme.js';
|
|
@@ -31,7 +31,10 @@ const DEFAULT_GRADIENT = {
|
|
|
31
31
|
* ColorPicker Root Component
|
|
32
32
|
* Follows Flikkui compound component pattern (similar to Accordion, Tabs, Popover)
|
|
33
33
|
*/
|
|
34
|
-
const ColorPickerRoot = React__default.forwardRef(({ value, defaultValue = '#000000', onChange, format: initialFormat = 'hex', showAlpha = true, showEyeDropper = true, presetColors, savedColors: savedColorsProp, onSavedColorsChange, defaultMode = 'solid', mode: modeProp, onModeChange, gradientValue: gradientProp, onGradientChange, size = 'md', state = 'default', disabled = false, label, helperText, required = false, theme, className, children, ...props }, ref) => {
|
|
34
|
+
const ColorPickerRoot = React__default.forwardRef(({ value, defaultValue = '#000000', onChange, format: initialFormat = 'hex', showAlpha = true, showEyeDropper = true, presetColors, savedColors: savedColorsProp, onSavedColorsChange, defaultMode = 'solid', mode: modeProp, onModeChange, gradientValue: gradientProp, onGradientChange, size = 'md', state = 'default', disabled = false, label, helperText, required = false, theme, className, id, children, ...props }, ref) => {
|
|
35
|
+
const generatedId = React__default.useId();
|
|
36
|
+
const componentId = id || generatedId;
|
|
37
|
+
const stopIdRef = useRef(0);
|
|
35
38
|
// Parse initial color value
|
|
36
39
|
const initialColor = useMemo(() => {
|
|
37
40
|
const parsed = parseColor(value || defaultValue);
|
|
@@ -71,7 +74,7 @@ const ColorPickerRoot = React__default.forwardRef(({ value, defaultValue = '#000
|
|
|
71
74
|
}, [gradient, setGradient]);
|
|
72
75
|
const addGradientStop = useCallback((position) => {
|
|
73
76
|
const newStop = {
|
|
74
|
-
id: `stop-${
|
|
77
|
+
id: `stop-${++stopIdRef.current}`,
|
|
75
78
|
color: formatHex(color.h, color.s, color.l),
|
|
76
79
|
position: Math.round(position),
|
|
77
80
|
};
|
|
@@ -252,7 +255,7 @@ const ColorPickerRoot = React__default.forwardRef(({ value, defaultValue = '#000
|
|
|
252
255
|
addSavedColor,
|
|
253
256
|
removeSavedColor,
|
|
254
257
|
]);
|
|
255
|
-
return (jsx(ColorPickerContext.Provider, { value: contextValue, children: jsxs("div", { ref: ref, className: cn(mergedTheme.baseStyle, className), ...props, children: [label && (jsx(FormLabel, { htmlFor:
|
|
258
|
+
return (jsx(ColorPickerContext.Provider, { value: contextValue, children: jsxs("div", { ref: ref, className: cn(mergedTheme.baseStyle, className), ...props, children: [label && (typeof label === 'string' ? (jsx(FormLabel, { htmlFor: componentId, required: required, state: state, children: label })) : (label)), children, helperText && (typeof helperText === 'string' ? (jsx("div", { id: `${componentId}-helper`, className: cn('mt-1.5 text-sm text-[var(--color-text-secondary)]', state === 'invalid' && 'text-[var(--color-danger)]'), children: helperText })) : (helperText))] }) }));
|
|
256
259
|
});
|
|
257
260
|
ColorPickerRoot.displayName = 'ColorPicker';
|
|
258
261
|
/**
|
|
@@ -83,11 +83,11 @@ export interface ColorPickerProps extends Omit<React.HTMLAttributes<HTMLDivEleme
|
|
|
83
83
|
/**
|
|
84
84
|
* Label for the color picker
|
|
85
85
|
*/
|
|
86
|
-
label?:
|
|
86
|
+
label?: React.ReactNode;
|
|
87
87
|
/**
|
|
88
88
|
* Helper text displayed below the picker
|
|
89
89
|
*/
|
|
90
|
-
helperText?:
|
|
90
|
+
helperText?: React.ReactNode;
|
|
91
91
|
/**
|
|
92
92
|
* Whether the picker is required
|
|
93
93
|
* @default false
|
|
@@ -66,9 +66,11 @@ const ColorPickerBody = ({ children, className, portal = true, animation = true,
|
|
|
66
66
|
damping: 30,
|
|
67
67
|
}, "data-state": isOpen ? 'open' : 'closed', ...domProps, children: children })) }));
|
|
68
68
|
// Use portal if specified (same as Dropdown)
|
|
69
|
-
|
|
70
|
-
?
|
|
71
|
-
:
|
|
69
|
+
if (portal) {
|
|
70
|
+
const target = isClient ? document.body : null;
|
|
71
|
+
return target ? createPortal(content, target) : null;
|
|
72
|
+
}
|
|
73
|
+
return content;
|
|
72
74
|
};
|
|
73
75
|
ColorPickerBody.displayName = 'ColorPicker.Body';
|
|
74
76
|
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import { jsx } from 'react/jsx-runtime';
|
|
2
2
|
import { EyeDropperIcon } from '@heroicons/react/20/solid';
|
|
3
|
-
import { useContext } from 'react';
|
|
3
|
+
import { useContext, useMemo } from 'react';
|
|
4
4
|
import { cn } from '../../../utils/cn.js';
|
|
5
5
|
import { Button } from '../../core/Button/Button.js';
|
|
6
6
|
import { ColorPickerContext } from './ColorPickerContext.js';
|
|
7
7
|
import { parseColor } from './colorUtils.js';
|
|
8
8
|
|
|
9
|
-
// Check if browser supports EyeDropper API
|
|
10
|
-
const supportsEyeDropper = typeof window !== 'undefined' && 'EyeDropper' in window;
|
|
11
9
|
/**
|
|
12
10
|
* ColorPickerEyeDropper - Eyedropper tool to pick colors from screen
|
|
13
11
|
* Uses browser EyeDropper API (if available)
|
|
14
12
|
*/
|
|
15
13
|
const ColorPickerEyeDropper = ({ className, ...props }) => {
|
|
16
14
|
const context = useContext(ColorPickerContext);
|
|
15
|
+
// Check if browser supports EyeDropper API (inside component to avoid SSR module-caching issues)
|
|
16
|
+
const supportsEyeDropper = useMemo(() => typeof window !== 'undefined' && 'EyeDropper' in window, []);
|
|
17
17
|
if (!context) {
|
|
18
18
|
throw new Error('ColorPickerEyeDropper must be used within a ColorPicker');
|
|
19
19
|
}
|
|
@@ -113,14 +113,14 @@ const ColorPickerGradient = ({ className, ...props }) => {
|
|
|
113
113
|
setDragStopId(null);
|
|
114
114
|
}, []);
|
|
115
115
|
useEffect(() => {
|
|
116
|
-
if (dragStopId)
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
}
|
|
116
|
+
if (!dragStopId)
|
|
117
|
+
return; // no listeners to add; no cleanup needed
|
|
118
|
+
window.addEventListener('pointermove', handlePointerMove);
|
|
119
|
+
window.addEventListener('pointerup', handlePointerUp);
|
|
120
|
+
return () => {
|
|
121
|
+
window.removeEventListener('pointermove', handlePointerMove);
|
|
122
|
+
window.removeEventListener('pointerup', handlePointerUp);
|
|
123
|
+
};
|
|
124
124
|
}, [dragStopId, handlePointerMove, handlePointerUp]);
|
|
125
125
|
// --- Selected stop color input editing ---
|
|
126
126
|
const [colorInput, setColorInput] = useState((_c = selectedStop === null || selectedStop === void 0 ? void 0 : selectedStop.color) !== null && _c !== void 0 ? _c : '');
|
|
@@ -20,7 +20,7 @@ const ColorPickerInput = ({ className, ...props }) => {
|
|
|
20
20
|
if (!isInvalid) {
|
|
21
21
|
setInputValue(getFormattedValue());
|
|
22
22
|
}
|
|
23
|
-
}, [color, format]);
|
|
23
|
+
}, [color, format, getFormattedValue, isInvalid]);
|
|
24
24
|
const handleCopy = async () => {
|
|
25
25
|
try {
|
|
26
26
|
await navigator.clipboard.writeText(getFormattedValue());
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Compound component following Flikkui patterns
|
|
4
4
|
*/
|
|
5
5
|
export { ColorPicker } from './ColorPicker';
|
|
6
|
-
export { useColorPicker
|
|
6
|
+
export { useColorPicker } from './ColorPickerContext';
|
|
7
7
|
export { ColorPickerTrigger } from './ColorPickerTrigger';
|
|
8
8
|
export { ColorPickerBody } from './ColorPickerBody';
|
|
9
9
|
export { ColorPicker2DCanvas } from './ColorPicker2DCanvas';
|