@flikk/ui 1.0.0-beta.6 → 1.0.0-beta.8
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 +5 -124
- package/dist/components/core/Alert/Alert.js +1 -0
- package/dist/components/core/Alert/Alert.theme.js +3 -7
- package/dist/components/core/Alert/Alert.types.d.ts +2 -1
- package/dist/components/core/Badge/Badge.theme.js +13 -8
- package/dist/components/core/Badge/Badge.types.d.ts +2 -1
- package/dist/components/core/Button/Button.theme.js +36 -30
- package/dist/components/core/Button/Button.types.d.ts +2 -1
- package/dist/components/core/Calendar/Calendar.theme.js +3 -3
- package/dist/components/core/CommandPalette/CommandPalette.theme.js +1 -1
- package/dist/components/core/MenuItem/MenuItem.theme.js +2 -2
- package/dist/components/core/OfflineIndicator/OfflineIndicator.theme.js +1 -1
- package/dist/components/core/Popover/PopoverContent.js +2 -0
- package/dist/components/core/Progress/Progress.theme.js +9 -6
- package/dist/components/core/Progress/Progress.types.d.ts +2 -1
- package/dist/components/core/Spinner/Spinner.theme.js +3 -5
- package/dist/components/core/Spinner/Spinner.types.d.ts +2 -1
- package/dist/components/data-display/KPI/KPI.theme.js +3 -4
- package/dist/components/data-display/KPI/KPI.types.d.ts +2 -1
- package/dist/components/data-display/Metric/Metric.theme.js +4 -0
- package/dist/components/data-display/Metric/Metric.types.d.ts +2 -1
- package/dist/components/data-display/Table/TableFilter.js +1 -1
- package/dist/components/data-display/Timeline/Timeline.theme.js +2 -2
- package/dist/components/effects/CustomCursor/CustomCursor.theme.js +2 -0
- package/dist/components/effects/CustomCursor/CustomCursor.types.d.ts +2 -1
- package/dist/components/forms/Checkbox/Checkbox.d.ts +3 -4
- package/dist/components/forms/Checkbox/Checkbox.js +33 -9
- package/dist/components/forms/Checkbox/Checkbox.theme.js +6 -0
- package/dist/components/forms/Checkbox/Checkbox.types.d.ts +28 -2
- package/dist/components/forms/Checkbox/CheckboxContext.d.ts +14 -0
- package/dist/components/forms/Checkbox/CheckboxContext.js +12 -0
- package/dist/components/forms/Checkbox/CheckboxGroup.d.ts +3 -0
- package/dist/components/forms/Checkbox/CheckboxGroup.js +40 -0
- package/dist/components/forms/Checkbox/index.d.ts +1 -1
- package/dist/components/forms/FileUpload/FileUpload.js +0 -78
- package/dist/components/forms/Radio/Radio.d.ts +3 -4
- package/dist/components/forms/Radio/Radio.js +21 -4
- package/dist/components/forms/Radio/Radio.theme.js +6 -0
- package/dist/components/forms/Radio/Radio.types.d.ts +30 -4
- package/dist/components/forms/Radio/RadioContext.d.ts +14 -0
- package/dist/components/forms/Radio/RadioContext.js +12 -0
- package/dist/components/forms/Radio/RadioGroup.d.ts +3 -0
- package/dist/components/forms/Radio/RadioGroup.js +36 -0
- package/dist/components/forms/Radio/index.d.ts +1 -1
- package/dist/components/forms/TimePicker/TimePickerContent.js +1 -78
- package/dist/components/forms/index.d.ts +2 -2
- package/dist/components/forms/index.js +0 -78
- package/dist/components/layout/FormLayout/FormLayoutBody.js +1 -79
- package/dist/hooks/useSelectPortal.d.ts +3 -2
- package/dist/hooks/useSelectPortal.js +44 -19
- package/dist/index.js +191 -197
- package/dist/styles.css +1 -1
- package/dist/utils/colorUtils.d.ts +23 -0
- package/dist/utils/colorUtils.js +45 -0
- package/dist/utils/index.d.ts +1 -0
- package/package.json +1 -1
- package/src/styles/theme.css +29 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import { SemanticColor } from '../../../utils/colorUtils';
|
|
2
3
|
export type MetricSize = 'sm' | 'md' | 'lg';
|
|
3
|
-
export type MetricColor =
|
|
4
|
+
export type MetricColor = SemanticColor;
|
|
4
5
|
export type MetricVariant = 'filled' | 'glass';
|
|
5
6
|
export type TrendDirection = 'up' | 'down';
|
|
6
7
|
export interface MetricProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
@@ -206,7 +206,7 @@ function TableFilter({ columns, filterConfig, onFilterChange, filterLogic: contr
|
|
|
206
206
|
return true;
|
|
207
207
|
return f.value != null && f.value !== "";
|
|
208
208
|
}).length;
|
|
209
|
-
return (jsxs(Popover, { placement: "bottom-end", offset: 8, closeOnClickOutside: false, children: [jsx(Popover.Trigger, { children: jsxs(Button, { color: "neutral", variant: "outline", "aria-label": "Filter table data", size: "sm", children: [jsx(FunnelIcon, { className: "size-4", "aria-hidden": "true" }), "Filters", appliedFilterCount > 0 && (jsx("span", { className: "ml-1 inline-flex items-center justify-center size-5 rounded-full bg-[var(--color-primary)] text-
|
|
209
|
+
return (jsxs(Popover, { placement: "bottom-end", offset: 8, closeOnClickOutside: false, children: [jsx(Popover.Trigger, { children: jsxs(Button, { color: "neutral", variant: "outline", "aria-label": "Filter table data", size: "sm", children: [jsx(FunnelIcon, { className: "size-4", "aria-hidden": "true" }), "Filters", appliedFilterCount > 0 && (jsx("span", { className: "ml-1 inline-flex items-center justify-center size-5 rounded-full bg-[var(--color-primary)] text-[var(--color-primary-contrast)] text-xs font-medium", children: appliedFilterCount }))] }) }), jsx(Popover.Content, { className: "p-0 max-w-none", children: jsxs("div", { className: cn("w-[500px] p-3", className), role: "dialog", "aria-labelledby": "table-filter-title", "aria-modal": "true", children: [jsxs("div", { className: "flex items-center justify-between mb-3", children: [jsx("div", { id: "table-filter-title", className: "text-xs uppercase font-bold text-[var(--color-text-placeholder)]", children: "Filters" }), activeFilters.length > 1 && (jsxs("div", { className: "flex items-center gap-0.5 rounded-md bg-[var(--color-background-secondary)] p-0.5", children: [jsx("button", { type: "button", className: cn("px-2.5 py-0.5 text-xs font-medium rounded transition-colors", filterLogic === "and"
|
|
210
210
|
? "bg-[var(--color-background)] text-[var(--color-text-primary)] shadow-sm"
|
|
211
211
|
: "text-[var(--color-text-muted)] hover:text-[var(--color-text-secondary)]"), onClick: () => handleSetLogic("and"), children: "AND" }), jsx("button", { type: "button", className: cn("px-2.5 py-0.5 text-xs font-medium rounded transition-colors", filterLogic === "or"
|
|
212
212
|
? "bg-[var(--color-background)] text-[var(--color-text-primary)] shadow-sm"
|
|
@@ -19,8 +19,8 @@ const timelineTheme = {
|
|
|
19
19
|
markerStyle: "relative z-10 flex items-center justify-center shrink-0 rounded-full border-2 transition-colors duration-200",
|
|
20
20
|
// Marker status-specific styles
|
|
21
21
|
markerStatuses: {
|
|
22
|
-
completed: "w-8 h-8 bg-[var(--color-success)] border-[var(--color-success)] text-
|
|
23
|
-
active: "w-8 h-8 bg-[var(--color-primary)] border-[var(--color-primary)] text-
|
|
22
|
+
completed: "w-8 h-8 bg-[var(--color-success)] border-[var(--color-success)] text-[var(--color-success-contrast)]",
|
|
23
|
+
active: "w-8 h-8 bg-[var(--color-primary)] border-[var(--color-primary)] text-[var(--color-primary-contrast)] ring-4 ring-[var(--color-primary-100)]",
|
|
24
24
|
pending: "w-8 h-8 bg-[var(--color-background)] border-[var(--color-border)]",
|
|
25
25
|
},
|
|
26
26
|
// Content area - vertical
|
|
@@ -13,6 +13,7 @@ const customCursorTheme = {
|
|
|
13
13
|
neutral: "bg-[var(--color-background-secondary)]",
|
|
14
14
|
dark: "bg-[var(--color-text-primary)]",
|
|
15
15
|
primary: "bg-[var(--color-primary)]",
|
|
16
|
+
secondary: "bg-[var(--color-secondary)]",
|
|
16
17
|
success: "bg-[var(--color-success)]",
|
|
17
18
|
warning: "bg-[var(--color-warning)]",
|
|
18
19
|
danger: "bg-[var(--color-danger)]",
|
|
@@ -26,6 +27,7 @@ const customCursorTheme = {
|
|
|
26
27
|
neutral: "text-[var(--color-background-secondary)]",
|
|
27
28
|
dark: "text-[var(--color-text-primary)]",
|
|
28
29
|
primary: "text-[var(--color-primary)]",
|
|
30
|
+
secondary: "text-[var(--color-secondary)]",
|
|
29
31
|
success: "text-[var(--color-success)]",
|
|
30
32
|
warning: "text-[var(--color-warning)]",
|
|
31
33
|
danger: "text-[var(--color-danger)]",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import type { SpringOptions } from 'motion/react';
|
|
3
|
+
import { SemanticColor } from '../../../utils/colorUtils';
|
|
3
4
|
/**
|
|
4
5
|
* Alignment positions for CustomCursorFollow relative to the main cursor
|
|
5
6
|
*/
|
|
@@ -11,7 +12,7 @@ export type CustomCursorSize = 'sm' | 'md' | 'lg';
|
|
|
11
12
|
/**
|
|
12
13
|
* Color variants for the custom cursor
|
|
13
14
|
*/
|
|
14
|
-
export type CustomCursorColor =
|
|
15
|
+
export type CustomCursorColor = SemanticColor | 'dark';
|
|
15
16
|
/**
|
|
16
17
|
* Visual variants for the custom cursor
|
|
17
18
|
*/
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { CheckboxProps } from "./Checkbox.types";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
export declare const Checkbox: React.ForwardRefExoticComponent<CheckboxProps & React.RefAttributes<HTMLInputElement>>;
|
|
3
|
+
export declare const Checkbox: React.ForwardRefExoticComponent<CheckboxProps & React.RefAttributes<HTMLInputElement>> & {
|
|
4
|
+
Group: React.ForwardRefExoticComponent<import("./Checkbox.types").CheckboxGroupProps & React.RefAttributes<HTMLFieldSetElement>>;
|
|
5
|
+
};
|
|
@@ -1,31 +1,52 @@
|
|
|
1
1
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
-
import React__default, { useState, useRef, useEffect } from 'react';
|
|
2
|
+
import React__default, { useId, useState, useRef, useEffect } from 'react';
|
|
3
3
|
import { checkboxTheme } from './Checkbox.theme.js';
|
|
4
4
|
import { formLabelTheme } from '../FormLabel/FormLabel.theme.js';
|
|
5
5
|
import { MinusIcon, CheckIcon } from '@heroicons/react/24/outline';
|
|
6
6
|
import { cn } from '../../../utils/cn.js';
|
|
7
|
+
import { useCheckboxGroupContext } from './CheckboxContext.js';
|
|
8
|
+
import { CheckboxGroup } from './CheckboxGroup.js';
|
|
7
9
|
|
|
8
10
|
/**
|
|
9
11
|
* Checkbox component for selecting one or more options
|
|
10
12
|
*/
|
|
11
|
-
const
|
|
13
|
+
const CheckboxComponent = React__default.forwardRef(({ id: idProp, name: nameProp, value, checked: controlledChecked, indeterminate = false, onChange, label, description, state: stateProp = "default", size: sizeProp = "md", className, ...restProps }, ref) => {
|
|
14
|
+
var _a, _b, _c, _d;
|
|
15
|
+
const autoId = useId();
|
|
16
|
+
const groupContext = useCheckboxGroupContext();
|
|
17
|
+
// Resolve props from group context or individual props
|
|
18
|
+
const id = idProp !== null && idProp !== void 0 ? idProp : autoId;
|
|
19
|
+
const name = (_b = (_a = groupContext === null || groupContext === void 0 ? void 0 : groupContext.name) !== null && _a !== void 0 ? _a : nameProp) !== null && _b !== void 0 ? _b : autoId;
|
|
20
|
+
const size = (_c = groupContext === null || groupContext === void 0 ? void 0 : groupContext.size) !== null && _c !== void 0 ? _c : sizeProp;
|
|
21
|
+
const state = stateProp === "disabled" ? "disabled" : ((_d = groupContext === null || groupContext === void 0 ? void 0 : groupContext.state) !== null && _d !== void 0 ? _d : stateProp);
|
|
22
|
+
const isDisabled = state === "disabled";
|
|
23
|
+
// Determine checked state
|
|
24
|
+
const isInGroup = groupContext !== undefined;
|
|
12
25
|
const [internalChecked, setInternalChecked] = useState(false);
|
|
26
|
+
let isChecked;
|
|
27
|
+
if (isInGroup) {
|
|
28
|
+
isChecked = groupContext.value.includes(value);
|
|
29
|
+
}
|
|
30
|
+
else if (controlledChecked !== undefined) {
|
|
31
|
+
isChecked = controlledChecked;
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
isChecked = internalChecked;
|
|
35
|
+
}
|
|
13
36
|
const inputRef = useRef(null);
|
|
14
|
-
// Determine if component is controlled or uncontrolled
|
|
15
|
-
const isControlled = controlledChecked !== undefined;
|
|
16
|
-
const isChecked = isControlled ? controlledChecked : internalChecked;
|
|
17
37
|
// Set indeterminate property on the input element
|
|
18
38
|
useEffect(() => {
|
|
19
39
|
if (inputRef.current) {
|
|
20
40
|
inputRef.current.indeterminate = indeterminate;
|
|
21
41
|
}
|
|
22
42
|
}, [indeterminate]);
|
|
23
|
-
// Derive state flags
|
|
24
|
-
const isDisabled = state === "disabled";
|
|
25
43
|
const handleChange = (e) => {
|
|
26
44
|
if (isDisabled)
|
|
27
45
|
return;
|
|
28
|
-
if (
|
|
46
|
+
if (isInGroup) {
|
|
47
|
+
groupContext.onChange(value, e.target.checked);
|
|
48
|
+
}
|
|
49
|
+
else if (controlledChecked === undefined) {
|
|
29
50
|
setInternalChecked(e.target.checked);
|
|
30
51
|
}
|
|
31
52
|
onChange === null || onChange === void 0 ? void 0 : onChange(e.target.checked, value);
|
|
@@ -38,6 +59,9 @@ const Checkbox = React__default.forwardRef(({ id, name, value, checked: controll
|
|
|
38
59
|
ref.current = node;
|
|
39
60
|
}, 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") }), indeterminate && (jsx(MinusIcon, { className: cn("pointer-events-none col-start-1 row-start-1 self-center justify-self-center opacity-100 stroke-neutral-600 transition-opacity duration-200", size === "sm" ? "size-3" : "size-4"), strokeWidth: 3 })), isChecked && !indeterminate && (jsx(CheckIcon, { className: cn("pointer-events-none col-start-1 row-start-1 self-center justify-self-center opacity-100 stroke-white transition-opacity duration-200", size === "sm" ? "size-3" : "size-4"), strokeWidth: 3 }))] }), (label || description) && (jsxs("label", { htmlFor: id, className: cn("cursor-pointer", isDisabled && "cursor-not-allowed"), children: [label && (jsx("span", { className: cn(formLabelTheme.baseStyle, formLabelTheme.stateStyles[state], "mb-0"), children: label })), description && (jsx("div", { id: `${id}-description`, className: checkboxTheme.descriptionStyle, children: description }))] }))] }));
|
|
40
61
|
});
|
|
41
|
-
|
|
62
|
+
CheckboxComponent.displayName = "Checkbox";
|
|
63
|
+
const Checkbox = Object.assign(CheckboxComponent, {
|
|
64
|
+
Group: CheckboxGroup,
|
|
65
|
+
});
|
|
42
66
|
|
|
43
67
|
export { Checkbox };
|
|
@@ -35,6 +35,12 @@ const checkboxTheme = {
|
|
|
35
35
|
sm: "size-5 mt-0.25",
|
|
36
36
|
md: "size-6",
|
|
37
37
|
},
|
|
38
|
+
// Group styles
|
|
39
|
+
groupStyle: "flex flex-col gap-1.5",
|
|
40
|
+
groupHorizontalStyle: "flex-row flex-wrap gap-x-6 gap-y-3",
|
|
41
|
+
groupDescriptionStyle: "text-sm text-[var(--color-text-secondary)]/80 -mt-0.5 mb-1",
|
|
42
|
+
groupErrorStyle: "text-sm text-[var(--color-danger)] mt-1.5",
|
|
43
|
+
groupItemsStyle: "flex flex-col gap-3",
|
|
38
44
|
};
|
|
39
45
|
|
|
40
46
|
export { checkboxTheme };
|
|
@@ -7,12 +7,16 @@ export type CheckboxState = 'default' | 'disabled';
|
|
|
7
7
|
* Size variants for the checkbox component
|
|
8
8
|
*/
|
|
9
9
|
export type CheckboxSize = 'sm' | 'md';
|
|
10
|
+
/**
|
|
11
|
+
* Orientation for Checkbox.Group layout
|
|
12
|
+
*/
|
|
13
|
+
export type CheckboxGroupOrientation = 'vertical' | 'horizontal';
|
|
10
14
|
/**
|
|
11
15
|
* Checkbox component props
|
|
12
16
|
*/
|
|
13
17
|
export interface CheckboxProps extends Omit<React.HTMLAttributes<HTMLInputElement>, 'onChange' | 'size'> {
|
|
14
|
-
id
|
|
15
|
-
name
|
|
18
|
+
id?: string;
|
|
19
|
+
name?: string;
|
|
16
20
|
value: string;
|
|
17
21
|
checked?: boolean;
|
|
18
22
|
indeterminate?: boolean;
|
|
@@ -23,6 +27,23 @@ export interface CheckboxProps extends Omit<React.HTMLAttributes<HTMLInputElemen
|
|
|
23
27
|
size?: CheckboxSize;
|
|
24
28
|
className?: string;
|
|
25
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* Checkbox.Group component props
|
|
32
|
+
*/
|
|
33
|
+
export interface CheckboxGroupProps extends Omit<React.HTMLAttributes<HTMLFieldSetElement>, 'onChange'> {
|
|
34
|
+
value?: string[];
|
|
35
|
+
defaultValue?: string[];
|
|
36
|
+
onChange?: (value: string[]) => void;
|
|
37
|
+
name?: string;
|
|
38
|
+
size?: CheckboxSize;
|
|
39
|
+
state?: CheckboxState;
|
|
40
|
+
orientation?: CheckboxGroupOrientation;
|
|
41
|
+
label?: string;
|
|
42
|
+
description?: string;
|
|
43
|
+
error?: string;
|
|
44
|
+
required?: boolean;
|
|
45
|
+
className?: string;
|
|
46
|
+
}
|
|
26
47
|
/**
|
|
27
48
|
* Checkbox theme configuration
|
|
28
49
|
*/
|
|
@@ -32,4 +53,9 @@ export interface CheckboxTheme {
|
|
|
32
53
|
inputStyle: string;
|
|
33
54
|
descriptionStyle: string;
|
|
34
55
|
sizes: Record<CheckboxSize, string>;
|
|
56
|
+
groupStyle?: string;
|
|
57
|
+
groupHorizontalStyle?: string;
|
|
58
|
+
groupDescriptionStyle?: string;
|
|
59
|
+
groupErrorStyle?: string;
|
|
60
|
+
groupItemsStyle?: string;
|
|
35
61
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { CheckboxSize, CheckboxState } from "./Checkbox.types";
|
|
2
|
+
export interface CheckboxGroupContextValue {
|
|
3
|
+
name: string;
|
|
4
|
+
value: string[];
|
|
5
|
+
onChange: (itemValue: string, checked: boolean) => void;
|
|
6
|
+
size: CheckboxSize;
|
|
7
|
+
state: CheckboxState;
|
|
8
|
+
}
|
|
9
|
+
export declare const CheckboxGroupContext: import("react").Context<CheckboxGroupContextValue | undefined>;
|
|
10
|
+
/**
|
|
11
|
+
* Returns the group context, or undefined when used outside a Checkbox.Group.
|
|
12
|
+
* This allows standalone Checkbox usage without a Group wrapper.
|
|
13
|
+
*/
|
|
14
|
+
export declare function useCheckboxGroupContext(): CheckboxGroupContextValue | undefined;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { createContext, useContext } from 'react';
|
|
2
|
+
|
|
3
|
+
const CheckboxGroupContext = createContext(undefined);
|
|
4
|
+
/**
|
|
5
|
+
* Returns the group context, or undefined when used outside a Checkbox.Group.
|
|
6
|
+
* This allows standalone Checkbox usage without a Group wrapper.
|
|
7
|
+
*/
|
|
8
|
+
function useCheckboxGroupContext() {
|
|
9
|
+
return useContext(CheckboxGroupContext);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export { CheckboxGroupContext, useCheckboxGroupContext };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
2
|
+
import React__default, { useId, useState, useMemo } from 'react';
|
|
3
|
+
import { cn } from '../../../utils/cn.js';
|
|
4
|
+
import { checkboxTheme } from './Checkbox.theme.js';
|
|
5
|
+
import { formLabelTheme } from '../FormLabel/FormLabel.theme.js';
|
|
6
|
+
import { CheckboxGroupContext } from './CheckboxContext.js';
|
|
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) => {
|
|
9
|
+
const autoId = useId();
|
|
10
|
+
const groupName = name !== null && name !== void 0 ? name : `checkbox-group-${autoId}`;
|
|
11
|
+
const labelId = `${autoId}-label`;
|
|
12
|
+
const descriptionId = `${autoId}-desc`;
|
|
13
|
+
const errorId = `${autoId}-error`;
|
|
14
|
+
// Controlled vs uncontrolled
|
|
15
|
+
const isControlled = controlledValue !== undefined;
|
|
16
|
+
const [internalValue, setInternalValue] = useState(defaultValue !== null && defaultValue !== void 0 ? defaultValue : []);
|
|
17
|
+
const currentValue = isControlled ? controlledValue : internalValue;
|
|
18
|
+
const handleChange = (itemValue, checked) => {
|
|
19
|
+
const next = checked
|
|
20
|
+
? [...currentValue, itemValue]
|
|
21
|
+
: currentValue.filter((v) => v !== itemValue);
|
|
22
|
+
if (!isControlled) {
|
|
23
|
+
setInternalValue(next);
|
|
24
|
+
}
|
|
25
|
+
onChange === null || onChange === void 0 ? void 0 : onChange(next);
|
|
26
|
+
};
|
|
27
|
+
const contextValue = useMemo(() => ({
|
|
28
|
+
name: groupName,
|
|
29
|
+
value: currentValue,
|
|
30
|
+
onChange: handleChange,
|
|
31
|
+
size,
|
|
32
|
+
state,
|
|
33
|
+
}), [groupName, currentValue, size, state]);
|
|
34
|
+
const labelState = error ? "invalid" : state === "disabled" ? "disabled" : "default";
|
|
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": error ? true : undefined, "aria-errormessage": error ? errorId : undefined, className: cn("border-0 p-0 m-0", checkboxTheme.groupStyle, className), ...props, children: [label && (jsxs("legend", { id: labelId, className: cn(formLabelTheme.baseStyle, formLabelTheme.stateStyles[labelState]), children: [label, required && (jsx("span", { className: formLabelTheme.requiredIndicatorStyle, children: "*" }))] })), description && (jsx("p", { id: descriptionId, className: checkboxTheme.groupDescriptionStyle, children: description })), jsx("div", { className: cn(checkboxTheme.groupItemsStyle, orientation === "horizontal" &&
|
|
36
|
+
checkboxTheme.groupHorizontalStyle), children: children }), error && (jsx("p", { id: errorId, role: "alert", className: checkboxTheme.groupErrorStyle, children: error }))] }) }));
|
|
37
|
+
});
|
|
38
|
+
CheckboxGroup.displayName = "Checkbox.Group";
|
|
39
|
+
|
|
40
|
+
export { CheckboxGroup };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { Checkbox } from './Checkbox';
|
|
2
|
-
export type { CheckboxProps, CheckboxSize, CheckboxState } from './Checkbox.types';
|
|
2
|
+
export type { CheckboxProps, CheckboxSize, CheckboxState, CheckboxGroupProps, CheckboxGroupOrientation } from './Checkbox.types';
|
|
3
3
|
export { checkboxTheme } from './Checkbox.theme';
|
|
@@ -3,85 +3,7 @@ import React__default, { useMemo, useRef, useState, useCallback } from 'react';
|
|
|
3
3
|
import { fileUploadTheme } from './FileUpload.theme.js';
|
|
4
4
|
import { cn } from '../../../utils/cn.js';
|
|
5
5
|
import { CloudArrowUpIcon, DocumentIcon, XMarkIcon } from '@heroicons/react/24/outline';
|
|
6
|
-
import '../../core/Accordion/Accordion.js';
|
|
7
|
-
import '../../core/AlertDialog/AlertDialog.js';
|
|
8
|
-
import '../../core/AspectRatio/AspectRatio.js';
|
|
9
|
-
import '../../core/Avatar/Avatar.js';
|
|
10
|
-
import '../../core/AvatarGroup/AvatarGroup.js';
|
|
11
|
-
import '../../core/Badge/Badge.js';
|
|
12
|
-
import '../../core/Breadcrumbs/Breadcrumbs.js';
|
|
13
|
-
import '../../core/Breadcrumbs/Breadcrumbs.theme.js';
|
|
14
6
|
import { Button } from '../../core/Button/Button.js';
|
|
15
|
-
import '../../core/ButtonGroup/ButtonGroup.js';
|
|
16
|
-
import '../../core/ButtonGroup/ButtonGroupSeparator.js';
|
|
17
|
-
import '../../core/ButtonGroup/ButtonGroupText.js';
|
|
18
|
-
import '../../core/Calendar/Calendar.js';
|
|
19
|
-
import '../../core/Calendar/CalendarMini/CalendarMini.js';
|
|
20
|
-
import '../../core/Card/Card.js';
|
|
21
|
-
import '../../core/CardStack/CardStack.js';
|
|
22
|
-
import '../../core/Carousel/Carousel.js';
|
|
23
|
-
import '../../core/CommandPalette/CommandPalette.js';
|
|
24
|
-
import '../../core/Separator/Separator.js';
|
|
25
|
-
import '../../core/Drawer/Drawer.js';
|
|
26
|
-
import '../../core/Dropdown/Dropdown.js';
|
|
27
|
-
import '../../core/Dropdown/DropdownTrigger.js';
|
|
28
|
-
import '../../core/Dropdown/DropdownMenu.js';
|
|
29
|
-
import '../../core/Dropdown/DropdownItem.js';
|
|
30
|
-
import '../../core/Dropdown/DropdownSection.js';
|
|
31
|
-
import '../../core/Dropdown/DropdownSeparator.js';
|
|
32
|
-
import '../../core/Dropdown/Dropdown.theme.js';
|
|
33
|
-
import '../../core/Kbd/Kbd.js';
|
|
34
|
-
import '../../core/MenuItem/MenuItem.js';
|
|
35
|
-
import '../../core/Link/Link.js';
|
|
36
|
-
import '../../core/Loader/Loader.js';
|
|
37
|
-
import '../../core/Modal/Modal.js';
|
|
38
|
-
import '../../core/ModalStack/ModalStack.js';
|
|
39
|
-
import '../../core/PageHeading/PageHeading.js';
|
|
40
|
-
import '../../core/Pagination/Pagination.js';
|
|
41
|
-
import '../../core/Popover/Popover.js';
|
|
42
|
-
import '../../core/Popover/PopoverContext.js';
|
|
43
|
-
import '../../core/Progress/Progress.js';
|
|
44
|
-
import '../../core/Rating/Rating.js';
|
|
45
|
-
import '../../core/ScrollArea/ScrollArea.js';
|
|
46
|
-
import '../../core/ScrollArea/smooth/SmoothScrollEngine.js';
|
|
47
|
-
import '../../core/Segmented/Segmented.js';
|
|
48
|
-
import '../../core/Skeleton/Skeleton.js';
|
|
49
|
-
import '../../core/SlidingNumber/SlidingNumber.js';
|
|
50
|
-
import '../../core/Tabs/Tabs.js';
|
|
51
|
-
import '../../core/Tabs/TabsList.js';
|
|
52
|
-
import '../../core/Tabs/TabsTrigger.js';
|
|
53
|
-
import '../../core/Tabs/TabsContent.js';
|
|
54
|
-
import '../../core/Tabs/TabsContext.js';
|
|
55
|
-
import '../../core/Tooltip/Tooltip.js';
|
|
56
|
-
import '../../core/Tooltip/Tooltip.animations.js';
|
|
57
|
-
import '../../core/Tree/Tree.js';
|
|
58
|
-
import '../../core/Tag/Tag.js';
|
|
59
|
-
import '../../core/Alert/Alert.js';
|
|
60
|
-
import '../../core/Toast/Toast.js';
|
|
61
|
-
import '../../core/Toast/ToastProvider.js';
|
|
62
|
-
import '@heroicons/react/24/solid';
|
|
63
|
-
import '../../core/Spinner/Spinner.js';
|
|
64
|
-
import '../../core/Message/Message.js';
|
|
65
|
-
import '../../core/Message/TypeWriter.js';
|
|
66
|
-
import '../../core/Empty/Empty.js';
|
|
67
|
-
import '../../core/Masonry/Masonry.js';
|
|
68
|
-
import '../../core/DragDrop/DragDrop.js';
|
|
69
|
-
import '../../core/Sortable/Sortable.js';
|
|
70
|
-
import '../../core/NavItem/NavItem.js';
|
|
71
|
-
import '../../core/Sidebar/Sidebar.js';
|
|
72
|
-
import '../../core/Sidebar/SidebarHeader.js';
|
|
73
|
-
import '../../core/Sidebar/SidebarContent.js';
|
|
74
|
-
import '../../core/Sidebar/SidebarFooter.js';
|
|
75
|
-
import '../../core/Sidebar/SidebarNav.js';
|
|
76
|
-
import '../../core/Sidebar/SidebarNavGroup.js';
|
|
77
|
-
import 'motion/react';
|
|
78
|
-
import '../../core/Sidebar/SidebarToggle.js';
|
|
79
|
-
import '../../core/Sidebar/SidebarContext.js';
|
|
80
|
-
import '../../core/OfflineIndicator/OfflineIndicator.js';
|
|
81
|
-
import '../../core/ContextMenu/ContextMenu.js';
|
|
82
|
-
import '../../core/ContextMenu/ContextMenu.theme.js';
|
|
83
|
-
import '../../core/Pill/Pill.js';
|
|
84
|
-
import '../../core/SocialIcon/SocialIcon.js';
|
|
85
7
|
|
|
86
8
|
/**
|
|
87
9
|
* FileUpload component - Drag-and-drop file uploader with previews
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { RadioProps } from "./Radio.types";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
export declare const Radio: React.ForwardRefExoticComponent<RadioProps & React.RefAttributes<HTMLInputElement>>;
|
|
3
|
+
export declare const Radio: React.ForwardRefExoticComponent<RadioProps & React.RefAttributes<HTMLInputElement>> & {
|
|
4
|
+
Group: React.ForwardRefExoticComponent<import("./Radio.types").RadioGroupProps & React.RefAttributes<HTMLFieldSetElement>>;
|
|
5
|
+
};
|
|
@@ -1,18 +1,34 @@
|
|
|
1
1
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
-
import React__default from 'react';
|
|
2
|
+
import React__default, { useId } from 'react';
|
|
3
3
|
import { radioTheme } from './Radio.theme.js';
|
|
4
4
|
import { formLabelTheme } from '../FormLabel/FormLabel.theme.js';
|
|
5
5
|
import { cn } from '../../../utils/cn.js';
|
|
6
|
+
import { useRadioGroupContext } from './RadioContext.js';
|
|
7
|
+
import { RadioGroup } from './RadioGroup.js';
|
|
6
8
|
|
|
7
9
|
/**
|
|
8
10
|
* Radio component for selecting one option from a group
|
|
9
11
|
*/
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
+
const RadioComponent = React__default.forwardRef(({ id: idProp, name: nameProp, value, checked: checkedProp, defaultChecked, onChange, label, description, state: stateProp = "default", size: sizeProp = "md", className, ...restProps }, ref) => {
|
|
13
|
+
var _a, _b, _c, _d;
|
|
14
|
+
const autoId = useId();
|
|
15
|
+
const groupContext = useRadioGroupContext();
|
|
16
|
+
// Resolve props from group context or individual props
|
|
17
|
+
const id = idProp !== null && idProp !== void 0 ? idProp : autoId;
|
|
18
|
+
const name = (_b = (_a = groupContext === null || groupContext === void 0 ? void 0 : groupContext.name) !== null && _a !== void 0 ? _a : nameProp) !== null && _b !== void 0 ? _b : autoId;
|
|
19
|
+
const size = (_c = groupContext === null || groupContext === void 0 ? void 0 : groupContext.size) !== null && _c !== void 0 ? _c : sizeProp;
|
|
20
|
+
const state = stateProp === "disabled" ? "disabled" : ((_d = groupContext === null || groupContext === void 0 ? void 0 : groupContext.state) !== null && _d !== void 0 ? _d : stateProp);
|
|
12
21
|
const isDisabled = state === "disabled";
|
|
22
|
+
// Determine checked state
|
|
23
|
+
const isInGroup = groupContext !== undefined;
|
|
24
|
+
const checked = isInGroup ? groupContext.value === value : checkedProp;
|
|
25
|
+
const isControlled = checked !== undefined;
|
|
13
26
|
const handleChange = (e) => {
|
|
14
27
|
if (isDisabled)
|
|
15
28
|
return;
|
|
29
|
+
if (isInGroup) {
|
|
30
|
+
groupContext.onChange(value);
|
|
31
|
+
}
|
|
16
32
|
onChange === null || onChange === void 0 ? void 0 : onChange(e.target.checked, value);
|
|
17
33
|
};
|
|
18
34
|
const inputProps = isControlled
|
|
@@ -20,6 +36,7 @@ const Radio = React__default.forwardRef(({ id, name, value, checked, defaultChec
|
|
|
20
36
|
: { defaultChecked };
|
|
21
37
|
return (jsxs("div", { className: cn(radioTheme.baseStyle, className), ...restProps, children: [jsxs("div", { className: cn(radioTheme.inputContainerStyle, radioTheme.sizes[size]), children: [jsx("input", { ref: ref, type: "radio", id: id, name: name, value: value, ...inputProps, onChange: handleChange, disabled: isDisabled, "aria-describedby": description ? `${id}-description` : undefined, "data-state": isDisabled ? "disabled" : "default", className: cn("peer", radioTheme.inputStyle, radioTheme.sizes[size]) }), jsx("div", { className: cn(radioTheme.dotStyle, size === "sm" ? "size-2.5 top-0.25" : "size-3") })] }), (label || description) && (jsxs("label", { htmlFor: id, className: cn("cursor-pointer", isDisabled && "cursor-not-allowed"), children: [label && (jsx("span", { className: cn(formLabelTheme.baseStyle, formLabelTheme.stateStyles[state], "mb-0"), children: label })), description && (jsx("div", { id: `${id}-description`, className: radioTheme.descriptionStyle, children: description }))] }))] }));
|
|
22
38
|
});
|
|
23
|
-
|
|
39
|
+
RadioComponent.displayName = "Radio";
|
|
40
|
+
const Radio = Object.assign(RadioComponent, { Group: RadioGroup });
|
|
24
41
|
|
|
25
42
|
export { Radio };
|
|
@@ -34,6 +34,12 @@ const radioTheme = {
|
|
|
34
34
|
sm: "size-5 mt-0.25",
|
|
35
35
|
md: "size-6",
|
|
36
36
|
},
|
|
37
|
+
// Group styles
|
|
38
|
+
groupStyle: "flex flex-col gap-1.5",
|
|
39
|
+
groupHorizontalStyle: "flex-row flex-wrap gap-x-6 gap-y-3",
|
|
40
|
+
groupDescriptionStyle: "text-sm text-[var(--color-text-secondary)]/80 -mt-0.5 mb-1",
|
|
41
|
+
groupErrorStyle: "text-sm text-[var(--color-danger)] mt-1.5",
|
|
42
|
+
groupItemsStyle: "flex flex-col gap-3",
|
|
37
43
|
};
|
|
38
44
|
|
|
39
45
|
export { radioTheme };
|
|
@@ -7,21 +7,42 @@ export type RadioState = 'default' | 'disabled';
|
|
|
7
7
|
* Size variants for the radio component
|
|
8
8
|
*/
|
|
9
9
|
export type RadioSize = 'sm' | 'md';
|
|
10
|
+
/**
|
|
11
|
+
* Orientation for Radio.Group layout
|
|
12
|
+
*/
|
|
13
|
+
export type RadioGroupOrientation = 'vertical' | 'horizontal';
|
|
10
14
|
/**
|
|
11
15
|
* Radio component props
|
|
12
16
|
*/
|
|
13
17
|
export interface RadioProps extends Omit<React.HTMLAttributes<HTMLInputElement>, 'onChange' | 'size'> {
|
|
14
|
-
id
|
|
15
|
-
name
|
|
16
|
-
value: string;
|
|
18
|
+
id?: string;
|
|
19
|
+
name?: string;
|
|
20
|
+
value: string | number;
|
|
17
21
|
checked?: boolean;
|
|
18
|
-
onChange?: (checked: boolean, value: string) => void;
|
|
22
|
+
onChange?: (checked: boolean, value: string | number) => void;
|
|
19
23
|
label?: string;
|
|
20
24
|
description?: string;
|
|
21
25
|
state?: RadioState;
|
|
22
26
|
size?: RadioSize;
|
|
23
27
|
className?: string;
|
|
24
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* Radio.Group component props
|
|
31
|
+
*/
|
|
32
|
+
export interface RadioGroupProps extends Omit<React.HTMLAttributes<HTMLFieldSetElement>, 'onChange'> {
|
|
33
|
+
value?: string | number;
|
|
34
|
+
defaultValue?: string | number;
|
|
35
|
+
onChange?: (value: string | number) => void;
|
|
36
|
+
name?: string;
|
|
37
|
+
size?: RadioSize;
|
|
38
|
+
state?: RadioState;
|
|
39
|
+
orientation?: RadioGroupOrientation;
|
|
40
|
+
label?: string;
|
|
41
|
+
description?: string;
|
|
42
|
+
error?: string;
|
|
43
|
+
required?: boolean;
|
|
44
|
+
className?: string;
|
|
45
|
+
}
|
|
25
46
|
/**
|
|
26
47
|
* Radio theme configuration
|
|
27
48
|
*/
|
|
@@ -32,4 +53,9 @@ export interface RadioTheme {
|
|
|
32
53
|
dotStyle: string;
|
|
33
54
|
descriptionStyle: string;
|
|
34
55
|
sizes: Record<RadioSize, string>;
|
|
56
|
+
groupStyle?: string;
|
|
57
|
+
groupHorizontalStyle?: string;
|
|
58
|
+
groupDescriptionStyle?: string;
|
|
59
|
+
groupErrorStyle?: string;
|
|
60
|
+
groupItemsStyle?: string;
|
|
35
61
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { RadioSize, RadioState } from "./Radio.types";
|
|
2
|
+
export interface RadioGroupContextValue {
|
|
3
|
+
name: string;
|
|
4
|
+
value: string | number | undefined;
|
|
5
|
+
onChange: (value: string | number) => void;
|
|
6
|
+
size: RadioSize;
|
|
7
|
+
state: RadioState;
|
|
8
|
+
}
|
|
9
|
+
export declare const RadioGroupContext: import("react").Context<RadioGroupContextValue | undefined>;
|
|
10
|
+
/**
|
|
11
|
+
* Returns the group context, or undefined when used outside a Radio.Group.
|
|
12
|
+
* This allows standalone Radio usage without a Group wrapper.
|
|
13
|
+
*/
|
|
14
|
+
export declare function useRadioGroupContext(): RadioGroupContextValue | undefined;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { createContext, useContext } from 'react';
|
|
2
|
+
|
|
3
|
+
const RadioGroupContext = createContext(undefined);
|
|
4
|
+
/**
|
|
5
|
+
* Returns the group context, or undefined when used outside a Radio.Group.
|
|
6
|
+
* This allows standalone Radio usage without a Group wrapper.
|
|
7
|
+
*/
|
|
8
|
+
function useRadioGroupContext() {
|
|
9
|
+
return useContext(RadioGroupContext);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export { RadioGroupContext, useRadioGroupContext };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
2
|
+
import React__default, { useId, useState, useMemo } from 'react';
|
|
3
|
+
import { cn } from '../../../utils/cn.js';
|
|
4
|
+
import { radioTheme } from './Radio.theme.js';
|
|
5
|
+
import { formLabelTheme } from '../FormLabel/FormLabel.theme.js';
|
|
6
|
+
import { RadioGroupContext } from './RadioContext.js';
|
|
7
|
+
|
|
8
|
+
const RadioGroup = React__default.forwardRef(({ value: controlledValue, defaultValue, onChange, name, size = "md", state = "default", orientation = "vertical", label, description, error, required, className, children, ...props }, ref) => {
|
|
9
|
+
const autoId = useId();
|
|
10
|
+
const groupName = name !== null && name !== void 0 ? name : `radio-group-${autoId}`;
|
|
11
|
+
const labelId = `${autoId}-label`;
|
|
12
|
+
const descriptionId = `${autoId}-desc`;
|
|
13
|
+
const errorId = `${autoId}-error`;
|
|
14
|
+
// Controlled vs uncontrolled
|
|
15
|
+
const isControlled = controlledValue !== undefined;
|
|
16
|
+
const [internalValue, setInternalValue] = useState(defaultValue);
|
|
17
|
+
const currentValue = isControlled ? controlledValue : internalValue;
|
|
18
|
+
const handleChange = (itemValue) => {
|
|
19
|
+
if (!isControlled) {
|
|
20
|
+
setInternalValue(itemValue);
|
|
21
|
+
}
|
|
22
|
+
onChange === null || onChange === void 0 ? void 0 : onChange(itemValue);
|
|
23
|
+
};
|
|
24
|
+
const contextValue = useMemo(() => ({
|
|
25
|
+
name: groupName,
|
|
26
|
+
value: currentValue,
|
|
27
|
+
onChange: handleChange,
|
|
28
|
+
size,
|
|
29
|
+
state,
|
|
30
|
+
}), [groupName, currentValue, size, state]);
|
|
31
|
+
const labelState = error ? "invalid" : state === "disabled" ? "disabled" : "default";
|
|
32
|
+
return (jsx(RadioGroupContext.Provider, { value: contextValue, children: jsxs("fieldset", { ref: ref, role: "radiogroup", "aria-labelledby": label ? labelId : undefined, "aria-describedby": description ? descriptionId : undefined, "aria-required": required || undefined, "aria-invalid": error ? true : undefined, "aria-errormessage": error ? errorId : undefined, className: cn("border-0 p-0 m-0", radioTheme.groupStyle, className), ...props, children: [label && (jsxs("legend", { id: labelId, className: cn(formLabelTheme.baseStyle, formLabelTheme.stateStyles[labelState]), children: [label, required && (jsx("span", { className: formLabelTheme.requiredIndicatorStyle, children: "*" }))] })), description && (jsx("p", { id: descriptionId, className: radioTheme.groupDescriptionStyle, children: description })), jsx("div", { className: cn(radioTheme.groupItemsStyle, orientation === "horizontal" && radioTheme.groupHorizontalStyle), children: children }), error && (jsx("p", { id: errorId, role: "alert", className: radioTheme.groupErrorStyle, children: error }))] }) }));
|
|
33
|
+
});
|
|
34
|
+
RadioGroup.displayName = "Radio.Group";
|
|
35
|
+
|
|
36
|
+
export { RadioGroup };
|