@inkra/react 0.0.0-stage → 0.2.0
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/LICENSE +21 -0
- package/README.md +316 -2
- package/THIRD_PARTY_NOTICES.md +39 -0
- package/Yozai-OFL.txt +94 -0
- package/dist/Button.d.ts +36 -0
- package/dist/Button.js +89 -0
- package/dist/PencilIcon.d.ts +22 -0
- package/dist/PencilIcon.js +210 -0
- package/dist/PencilLine.d.ts +10 -0
- package/dist/PencilLine.js +91 -0
- package/dist/PencilSurface.d.ts +19 -0
- package/dist/PencilSurface.js +19 -0
- package/dist/PencilText.d.ts +16 -0
- package/dist/PencilText.js +338 -0
- package/dist/accordion/Accordion.d.ts +27 -0
- package/dist/accordion/Accordion.js +40 -0
- package/dist/accordion/index.d.ts +2 -0
- package/dist/accordion/index.js +1 -0
- package/dist/alert-dialog/index.d.ts +64 -0
- package/dist/alert-dialog/index.js +109 -0
- package/dist/assets/ui-hand-yozai.woff2 +0 -0
- package/dist/audio-player/index.d.ts +60 -0
- package/dist/audio-player/index.js +101 -0
- package/dist/breadcrumb/index.d.ts +16 -0
- package/dist/breadcrumb/index.js +25 -0
- package/dist/calendar/index.d.ts +21 -0
- package/dist/calendar/index.js +41 -0
- package/dist/carousel/index.d.ts +51 -0
- package/dist/carousel/index.js +290 -0
- package/dist/choice-grid/index.d.ts +38 -0
- package/dist/choice-grid/index.js +85 -0
- package/dist/color-picker/color.d.ts +15 -0
- package/dist/color-picker/color.js +55 -0
- package/dist/color-picker/index.d.ts +31 -0
- package/dist/color-picker/index.js +69 -0
- package/dist/command/index.d.ts +23 -0
- package/dist/command/index.js +39 -0
- package/dist/content/index.d.ts +106 -0
- package/dist/content/index.js +80 -0
- package/dist/context-menu/index.d.ts +76 -0
- package/dist/context-menu/index.js +240 -0
- package/dist/data-table/index.d.ts +47 -0
- package/dist/data-table/index.js +91 -0
- package/dist/date-picker/index.d.ts +50 -0
- package/dist/date-picker/index.js +180 -0
- package/dist/day-agenda/index.d.ts +43 -0
- package/dist/day-agenda/index.js +47 -0
- package/dist/day-agenda/layout.d.ts +20 -0
- package/dist/day-agenda/layout.js +60 -0
- package/dist/display/ProgressRing.d.ts +6 -0
- package/dist/display/ProgressRing.js +146 -0
- package/dist/display/index.d.ts +59 -0
- package/dist/display/index.js +75 -0
- package/dist/dockable-panel/index.d.ts +41 -0
- package/dist/dockable-panel/index.js +253 -0
- package/dist/dockable-panel/model.d.ts +21 -0
- package/dist/dockable-panel/model.js +61 -0
- package/dist/drawer/index.d.ts +63 -0
- package/dist/drawer/index.js +86 -0
- package/dist/dropdown-menu/index.d.ts +34 -0
- package/dist/dropdown-menu/index.js +40 -0
- package/dist/editable/index.d.ts +53 -0
- package/dist/editable/index.js +162 -0
- package/dist/empty/index.d.ts +21 -0
- package/dist/empty/index.js +23 -0
- package/dist/flip/index.d.ts +49 -0
- package/dist/flip/index.js +138 -0
- package/dist/form/EditablePencilText.d.ts +13 -0
- package/dist/form/EditablePencilText.js +323 -0
- package/dist/form/Field.d.ts +13 -0
- package/dist/form/Field.js +26 -0
- package/dist/form/Input.d.ts +27 -0
- package/dist/form/Input.js +97 -0
- package/dist/form/InputAction.d.ts +7 -0
- package/dist/form/InputAction.js +24 -0
- package/dist/form/Textarea.d.ts +17 -0
- package/dist/form/Textarea.js +102 -0
- package/dist/form/context.d.ts +12 -0
- package/dist/form/context.js +10 -0
- package/dist/form/controlProps.d.ts +23 -0
- package/dist/form/controlProps.js +20 -0
- package/dist/form/index.d.ts +7 -0
- package/dist/form/index.js +3 -0
- package/dist/form/types.d.ts +6 -0
- package/dist/form/types.js +1 -0
- package/dist/heatmap/index.d.ts +59 -0
- package/dist/heatmap/index.js +248 -0
- package/dist/hotkey/index.d.ts +37 -0
- package/dist/hotkey/index.js +130 -0
- package/dist/hotkey/model.d.ts +27 -0
- package/dist/hotkey/model.js +97 -0
- package/dist/hover-card/index.d.ts +33 -0
- package/dist/hover-card/index.js +141 -0
- package/dist/image-cropper/index.d.ts +82 -0
- package/dist/image-cropper/index.js +261 -0
- package/dist/image-cropper/model.d.ts +42 -0
- package/dist/image-cropper/model.js +94 -0
- package/dist/image-viewer/index.d.ts +79 -0
- package/dist/image-viewer/index.js +220 -0
- package/dist/image-viewer/model.d.ts +9 -0
- package/dist/image-viewer/model.js +18 -0
- package/dist/images/layers-2x.png +0 -0
- package/dist/images/layers.png +0 -0
- package/dist/images/marker-icon.png +0 -0
- package/dist/index.d.ts +75 -0
- package/dist/index.js +69 -0
- package/dist/input-number/index.d.ts +16 -0
- package/dist/input-number/index.js +77 -0
- package/dist/internal/InputActionContext.d.ts +1 -0
- package/dist/internal/InputActionContext.js +3 -0
- package/dist/internal/cache.d.ts +14 -0
- package/dist/internal/cache.js +54 -0
- package/dist/internal/line.d.ts +4 -0
- package/dist/internal/line.js +21 -0
- package/dist/internal/paper.d.ts +1 -0
- package/dist/internal/paper.js +65 -0
- package/dist/internal/random.d.ts +3 -0
- package/dist/internal/random.js +30 -0
- package/dist/internal/recovery.d.ts +1 -0
- package/dist/internal/recovery.js +52 -0
- package/dist/internal/renderTokens.d.ts +10 -0
- package/dist/internal/renderTokens.js +13 -0
- package/dist/internal/stroke.d.ts +24 -0
- package/dist/internal/stroke.js +144 -0
- package/dist/internal/surfaceRenderer.d.ts +41 -0
- package/dist/internal/surfaceRenderer.js +178 -0
- package/dist/internal/textQueue.d.ts +1 -0
- package/dist/internal/textQueue.js +37 -0
- package/dist/internal/textRenderer.d.ts +28 -0
- package/dist/internal/textRenderer.js +239 -0
- package/dist/internal/travelIcons.d.ts +24 -0
- package/dist/internal/travelIcons.js +14 -0
- package/dist/internal/useBrowserLayoutEffect.d.ts +2 -0
- package/dist/internal/useBrowserLayoutEffect.js +3 -0
- package/dist/internal/usePencilSurface.d.ts +3 -0
- package/dist/internal/usePencilSurface.js +116 -0
- package/dist/internal/useSelectionDrawing.d.ts +2 -0
- package/dist/internal/useSelectionDrawing.js +90 -0
- package/dist/internal/wash.d.ts +16 -0
- package/dist/internal/wash.js +149 -0
- package/dist/journey-map/MapScale.d.ts +4 -0
- package/dist/journey-map/MapScale.js +54 -0
- package/dist/journey-map/index.d.ts +39 -0
- package/dist/journey-map/index.js +197 -0
- package/dist/journey-map/terrain.d.ts +8 -0
- package/dist/journey-map/terrain.js +127 -0
- package/dist/keypad/index.d.ts +23 -0
- package/dist/keypad/index.js +63 -0
- package/dist/keypad/model.d.ts +20 -0
- package/dist/keypad/model.js +33 -0
- package/dist/layout/index.d.ts +63 -0
- package/dist/layout/index.js +62 -0
- package/dist/log/index.d.ts +45 -0
- package/dist/log/index.js +159 -0
- package/dist/log/model.d.ts +16 -0
- package/dist/log/model.js +13 -0
- package/dist/node-graph/index.d.ts +123 -0
- package/dist/node-graph/index.js +304 -0
- package/dist/number-ticker/index.d.ts +29 -0
- package/dist/number-ticker/index.js +132 -0
- package/dist/number-ticker/model.d.ts +8 -0
- package/dist/number-ticker/model.js +34 -0
- package/dist/overflow-list/index.d.ts +39 -0
- package/dist/overflow-list/index.js +230 -0
- package/dist/overflow-list/layout.d.ts +19 -0
- package/dist/overflow-list/layout.js +44 -0
- package/dist/overlay/Overlay.d.ts +65 -0
- package/dist/overlay/Overlay.js +77 -0
- package/dist/overlay/Popover.d.ts +43 -0
- package/dist/overlay/Popover.js +45 -0
- package/dist/overlay/index.d.ts +4 -0
- package/dist/overlay/index.js +2 -0
- package/dist/overlay/themeBridge.d.ts +8 -0
- package/dist/overlay/themeBridge.js +98 -0
- package/dist/pagination/index.d.ts +23 -0
- package/dist/pagination/index.js +41 -0
- package/dist/pagination/model.d.ts +4 -0
- package/dist/pagination/model.js +42 -0
- package/dist/pan-zoom/index.d.ts +71 -0
- package/dist/pan-zoom/index.js +175 -0
- package/dist/pencil.d.ts +10 -0
- package/dist/pencil.js +39 -0
- package/dist/radar/index.d.ts +54 -0
- package/dist/radar/index.js +220 -0
- package/dist/range-slider/index.d.ts +16 -0
- package/dist/range-slider/index.js +22 -0
- package/dist/rating/index.d.ts +28 -0
- package/dist/rating/index.js +127 -0
- package/dist/resizable/index.d.ts +32 -0
- package/dist/resizable/index.js +50 -0
- package/dist/scroll-area/index.d.ts +36 -0
- package/dist/scroll-area/index.js +44 -0
- package/dist/segmented-progress/SegmentedProgress.d.ts +17 -0
- package/dist/segmented-progress/SegmentedProgress.js +42 -0
- package/dist/select/MultipleSelect.d.ts +44 -0
- package/dist/select/MultipleSelect.js +124 -0
- package/dist/select/SearchableSelect.d.ts +45 -0
- package/dist/select/SearchableSelect.js +50 -0
- package/dist/select/Select.d.ts +109 -0
- package/dist/select/Select.js +205 -0
- package/dist/select/index.d.ts +4 -0
- package/dist/select/index.js +2 -0
- package/dist/selection/Checkbox.d.ts +9 -0
- package/dist/selection/Checkbox.js +37 -0
- package/dist/selection/RadioGroup.d.ts +23 -0
- package/dist/selection/RadioGroup.js +82 -0
- package/dist/selection/Switch.d.ts +8 -0
- package/dist/selection/Switch.js +36 -0
- package/dist/selection/index.d.ts +6 -0
- package/dist/selection/index.js +3 -0
- package/dist/selection/useNativeChecked.d.ts +13 -0
- package/dist/selection/useNativeChecked.js +27 -0
- package/dist/slider/index.d.ts +11 -0
- package/dist/slider/index.js +45 -0
- package/dist/slot/index.d.ts +24 -0
- package/dist/slot/index.js +29 -0
- package/dist/sortable/index.d.ts +50 -0
- package/dist/sortable/index.js +278 -0
- package/dist/sortable/model.d.ts +30 -0
- package/dist/sortable/model.js +64 -0
- package/dist/sparkline/index.d.ts +19 -0
- package/dist/sparkline/index.js +240 -0
- package/dist/sparkline/model.d.ts +16 -0
- package/dist/sparkline/model.js +28 -0
- package/dist/steps/index.d.ts +16 -0
- package/dist/steps/index.js +37 -0
- package/dist/styles.css +8226 -0
- package/dist/tabs/index.d.ts +22 -0
- package/dist/tabs/index.js +42 -0
- package/dist/tag-input/index.d.ts +69 -0
- package/dist/tag-input/index.js +325 -0
- package/dist/tag-input/model.d.ts +14 -0
- package/dist/tag-input/model.js +38 -0
- package/dist/time-picker/index.d.ts +15 -0
- package/dist/time-picker/index.js +168 -0
- package/dist/timeline/index.d.ts +14 -0
- package/dist/timeline/index.js +16 -0
- package/dist/toast/index.d.ts +27 -0
- package/dist/toast/index.js +36 -0
- package/dist/toggle-group/index.d.ts +17 -0
- package/dist/toggle-group/index.js +26 -0
- package/dist/tokens.css +356 -0
- package/dist/tooltip/Tooltip.d.ts +24 -0
- package/dist/tooltip/Tooltip.js +32 -0
- package/dist/tooltip/index.d.ts +2 -0
- package/dist/tooltip/index.js +1 -0
- package/dist/transfer/index.d.ts +64 -0
- package/dist/transfer/index.js +174 -0
- package/dist/transfer/model.d.ts +12 -0
- package/dist/transfer/model.js +33 -0
- package/dist/tree/index.d.ts +39 -0
- package/dist/tree/index.js +36 -0
- package/dist/tree-select/index.d.ts +59 -0
- package/dist/tree-select/index.js +142 -0
- package/dist/tree-select/model.d.ts +31 -0
- package/dist/tree-select/model.js +135 -0
- package/dist/typewriter/index.d.ts +21 -0
- package/dist/typewriter/index.js +101 -0
- package/dist/typewriter/model.d.ts +7 -0
- package/dist/typewriter/model.js +45 -0
- package/dist/upload/index.d.ts +59 -0
- package/dist/upload/index.js +230 -0
- package/dist/virtual-list/index.d.ts +44 -0
- package/dist/virtual-list/index.js +128 -0
- package/dist/virtual-list/model.d.ts +7 -0
- package/dist/virtual-list/model.js +27 -0
- package/package.json +119 -4
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import * as Primitive from '@radix-ui/react-tabs';
|
|
2
|
+
import { type ReactNode } from 'react';
|
|
3
|
+
export interface TabsProps extends Primitive.TabsProps {
|
|
4
|
+
size?: 'sm' | 'md' | 'lg';
|
|
5
|
+
animated?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export interface TabsListProps extends Primitive.TabsListProps {
|
|
8
|
+
align?: 'start' | 'center' | 'end';
|
|
9
|
+
fullWidth?: boolean;
|
|
10
|
+
divided?: boolean;
|
|
11
|
+
}
|
|
12
|
+
export interface TabsTriggerProps extends Omit<Primitive.TabsTriggerProps, 'asChild'> {
|
|
13
|
+
startIcon?: ReactNode;
|
|
14
|
+
endIcon?: ReactNode;
|
|
15
|
+
}
|
|
16
|
+
export type TabsContentProps = Primitive.TabsContentProps;
|
|
17
|
+
export declare const Tabs: {
|
|
18
|
+
Root: import("react").ForwardRefExoticComponent<TabsProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
19
|
+
List: import("react").ForwardRefExoticComponent<TabsListProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
20
|
+
Trigger: import("react").ForwardRefExoticComponent<TabsTriggerProps & import("react").RefAttributes<HTMLButtonElement>>;
|
|
21
|
+
Content: import("react").ForwardRefExoticComponent<Primitive.TabsContentProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
22
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import * as Primitive from '@radix-ui/react-tabs';
|
|
3
|
+
import { createContext, forwardRef, useContext, useRef, useState } from 'react';
|
|
4
|
+
import { PencilSurface } from '../PencilSurface.js';
|
|
5
|
+
import { PencilText } from '../PencilText.js';
|
|
6
|
+
import { PencilLine } from '../PencilLine.js';
|
|
7
|
+
import { useSelectionDrawing } from '../internal/useSelectionDrawing.js';
|
|
8
|
+
const ValueContext = createContext(undefined);
|
|
9
|
+
const AnimatedContext = createContext(true);
|
|
10
|
+
const OrientationContext = createContext('horizontal');
|
|
11
|
+
const DividedContext = createContext(false);
|
|
12
|
+
const Root = forwardRef(function TabsRoot({ value, defaultValue, onValueChange, className, size = 'md', animated = true, orientation = 'horizontal', ...props }, ref) {
|
|
13
|
+
const [internal, setInternal] = useState(defaultValue);
|
|
14
|
+
const current = value === undefined ? internal : value;
|
|
15
|
+
const lastChange = useRef(current);
|
|
16
|
+
lastChange.current = current;
|
|
17
|
+
return _jsx(ValueContext.Provider, { value: current, children: _jsx(AnimatedContext.Provider, { value: animated, children: _jsx(OrientationContext.Provider, { value: orientation, children: _jsx(Primitive.Root, { ...props, orientation: orientation, ref: ref, "data-size": size, className: ['inkra-tabs', className].filter(Boolean).join(' '), value: current ?? '', onValueChange: next => {
|
|
18
|
+
// Pointer focus and pointer down can request the same tab before React renders.
|
|
19
|
+
if (lastChange.current === next)
|
|
20
|
+
return;
|
|
21
|
+
lastChange.current = next;
|
|
22
|
+
if (value === undefined)
|
|
23
|
+
setInternal(next);
|
|
24
|
+
onValueChange?.(next);
|
|
25
|
+
} }) }) }) });
|
|
26
|
+
});
|
|
27
|
+
const List = forwardRef(function TabsList({ className, align = 'start', fullWidth = false, divided = false, ...props }, ref) {
|
|
28
|
+
return _jsx(DividedContext.Provider, { value: divided, children: _jsx(Primitive.List, { ...props, ref: ref, "data-align": align, "data-full-width": fullWidth || undefined, "data-divided": divided || undefined, className: ['inkra-tabs__list', className].filter(Boolean).join(' ') }) });
|
|
29
|
+
});
|
|
30
|
+
const Trigger = forwardRef(function TabsTrigger({ value, className, children, startIcon, endIcon, ...props }, ref) {
|
|
31
|
+
const current = useContext(ValueContext);
|
|
32
|
+
const divided = useContext(DividedContext);
|
|
33
|
+
const orientation = useContext(OrientationContext);
|
|
34
|
+
const surface = useRef(null);
|
|
35
|
+
useSelectionDrawing(surface, current === value, useContext(AnimatedContext));
|
|
36
|
+
return _jsxs(Primitive.Trigger, { ...props, value: value, ref: ref, className: ['inkra-tabs__trigger', className].filter(Boolean).join(' '), children: [divided && _jsx(PencilLine, { className: "inkra-tabs__divider", orientation: orientation === 'horizontal' ? 'vertical' : 'horizontal', seed: `tab-divider-${value}` }), _jsx(PencilSurface, { as: "span", "aria-hidden": "true", className: "inkra-tabs__selection", variant: "selection", seed: `tab-${value}`, ref: surface }), _jsx(PencilSurface, { as: "span", "aria-hidden": "true", className: "inkra-tabs__focus-ring", variant: "outline", seed: `tab-${value}-focus` }), startIcon && _jsx("span", { className: "inkra-tabs__icon", "aria-hidden": "true", children: startIcon }), _jsx("span", { className: "inkra-tabs__label", children: typeof children === 'string' ? _jsx(PencilText, { children: children }) : children }), endIcon && _jsx("span", { className: "inkra-tabs__icon", "aria-hidden": "true", children: endIcon })] });
|
|
37
|
+
});
|
|
38
|
+
const Content = forwardRef(function TabsContent({ className, value, ...props }, ref) {
|
|
39
|
+
const current = useContext(ValueContext);
|
|
40
|
+
return _jsx(Primitive.Content, { ...props, value: value, ref: ref, hidden: current !== value, className: ['inkra-tabs__content', className].filter(Boolean).join(' ') });
|
|
41
|
+
});
|
|
42
|
+
export const Tabs = { Root, List, Trigger, Content };
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { HTMLAttributes, InputHTMLAttributes } from 'react';
|
|
2
|
+
import type { FieldControlSize, FieldControlStateProps } from '../form/types.js';
|
|
3
|
+
import { createDefaultTag, defaultTagInputDelimiters, defaultTagValueKey, hasTagValue, normalizeTagInputValue, splitTagInputValue } from './model.js';
|
|
4
|
+
import type { TagInputIdentity } from './model.js';
|
|
5
|
+
export interface TagInputTag extends TagInputIdentity {
|
|
6
|
+
label?: string;
|
|
7
|
+
}
|
|
8
|
+
export type TagInputVariant = 'underline' | 'outline' | 'soft';
|
|
9
|
+
export type TagInputCommitReason = 'enter' | 'delimiter' | 'paste';
|
|
10
|
+
export type TagInputChangeReason = 'add' | 'remove' | 'clear';
|
|
11
|
+
export type TagInputValidationCode = 'duplicate' | 'max-tags' | 'invalid';
|
|
12
|
+
export type TagInputValidationResult = boolean | string | null | undefined;
|
|
13
|
+
export interface TagInputChangeDetails {
|
|
14
|
+
reason: TagInputChangeReason;
|
|
15
|
+
tags: readonly TagInputTag[];
|
|
16
|
+
inputReason?: TagInputCommitReason;
|
|
17
|
+
}
|
|
18
|
+
export interface TagInputDraftChangeDetails {
|
|
19
|
+
reason: 'input' | 'commit' | 'clear';
|
|
20
|
+
}
|
|
21
|
+
export interface TagInputValidationContext {
|
|
22
|
+
tags: readonly TagInputTag[];
|
|
23
|
+
reason: TagInputCommitReason;
|
|
24
|
+
}
|
|
25
|
+
export interface TagInputValidationDetails extends TagInputValidationContext {
|
|
26
|
+
candidate: string;
|
|
27
|
+
code: TagInputValidationCode;
|
|
28
|
+
}
|
|
29
|
+
export interface TagInputLabels {
|
|
30
|
+
tagList: string;
|
|
31
|
+
removeTag: (tag: TagInputTag) => string;
|
|
32
|
+
clearAll: string;
|
|
33
|
+
duplicate: (candidate: string) => string;
|
|
34
|
+
maxTags: (maximum: number) => string;
|
|
35
|
+
invalid: (candidate: string) => string;
|
|
36
|
+
}
|
|
37
|
+
export type TagInputClassNames = Partial<Record<'root' | 'control' | 'list' | 'tag' | 'input' | 'clear' | 'message', string>>;
|
|
38
|
+
export interface TagInputCreateContext extends TagInputValidationContext {
|
|
39
|
+
}
|
|
40
|
+
export interface TagInputProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children' | 'defaultValue' | 'onChange'>, FieldControlStateProps {
|
|
41
|
+
value?: readonly TagInputTag[];
|
|
42
|
+
defaultValue?: readonly TagInputTag[];
|
|
43
|
+
onValueChange?: (value: readonly TagInputTag[], details: TagInputChangeDetails) => void;
|
|
44
|
+
inputValue?: string;
|
|
45
|
+
defaultInputValue?: string;
|
|
46
|
+
onInputValueChange?: (value: string, details: TagInputDraftChangeDetails) => void;
|
|
47
|
+
createTag?: (candidate: string, context: TagInputCreateContext) => TagInputTag | null | undefined;
|
|
48
|
+
validateTag?: (candidate: string, context: TagInputValidationContext) => TagInputValidationResult;
|
|
49
|
+
onValidationError?: (message: string, details: TagInputValidationDetails) => void;
|
|
50
|
+
validationMessage?: string;
|
|
51
|
+
getTagValueKey?: (value: string) => string;
|
|
52
|
+
delimiters?: readonly string[];
|
|
53
|
+
maxTags?: number;
|
|
54
|
+
disabled?: boolean;
|
|
55
|
+
readOnly?: boolean;
|
|
56
|
+
required?: boolean;
|
|
57
|
+
name?: string;
|
|
58
|
+
form?: string;
|
|
59
|
+
placeholder?: string;
|
|
60
|
+
clearable?: boolean;
|
|
61
|
+
fullWidth?: boolean;
|
|
62
|
+
variant?: TagInputVariant;
|
|
63
|
+
labels?: Partial<TagInputLabels>;
|
|
64
|
+
inputProps?: Omit<InputHTMLAttributes<HTMLInputElement>, 'defaultValue' | 'disabled' | 'form' | 'id' | 'name' | 'placeholder' | 'readOnly' | 'required' | 'size' | 'value'>;
|
|
65
|
+
classNames?: TagInputClassNames;
|
|
66
|
+
}
|
|
67
|
+
export declare const TagInput: import("react").ForwardRefExoticComponent<TagInputProps & import("react").RefAttributes<HTMLInputElement>>;
|
|
68
|
+
export { createDefaultTag, defaultTagInputDelimiters, defaultTagValueKey, hasTagValue, normalizeTagInputValue, splitTagInputValue, };
|
|
69
|
+
export type { FieldControlSize, TagInputIdentity };
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef, useEffect, useId, useImperativeHandle, useMemo, useRef, useState, } from 'react';
|
|
3
|
+
import { PencilIcon } from '../PencilIcon.js';
|
|
4
|
+
import { PencilLine } from '../PencilLine.js';
|
|
5
|
+
import { PencilSurface } from '../PencilSurface.js';
|
|
6
|
+
import { PencilText } from '../PencilText.js';
|
|
7
|
+
import { useResolvedControlProps } from '../form/controlProps.js';
|
|
8
|
+
import { createDefaultTag, defaultTagInputDelimiters, defaultTagValueKey, hasTagValue, normalizeTagInputValue, splitTagInputValue, } from './model.js';
|
|
9
|
+
const removePaths = [{ d: 'M18 6 6 18' }, { d: 'm6 6 12 12' }];
|
|
10
|
+
const clearPaths = [{ d: 'M4 7h16' }, { d: 'M9 7V4h6v3' }, { d: 'm7 7 1 13h8l1-13' }];
|
|
11
|
+
const classes = (...values) => values.filter(Boolean).join(' ');
|
|
12
|
+
const defaultLabels = {
|
|
13
|
+
tagList: 'Selected tags',
|
|
14
|
+
removeTag: tag => `Remove ${tag.label ?? tag.value}`,
|
|
15
|
+
clearAll: 'Clear all tags',
|
|
16
|
+
duplicate: candidate => `${candidate} is already added.`,
|
|
17
|
+
maxTags: maximum => `You can add up to ${maximum} tags.`,
|
|
18
|
+
invalid: candidate => `${candidate} is not a valid tag.`,
|
|
19
|
+
};
|
|
20
|
+
function joinAriaIds(...values) {
|
|
21
|
+
const ids = values.flatMap(value => value?.split(/\s+/).filter(Boolean) ?? []);
|
|
22
|
+
return [...new Set(ids)].join(' ') || undefined;
|
|
23
|
+
}
|
|
24
|
+
function resolvedLabels(labels) {
|
|
25
|
+
return {
|
|
26
|
+
tagList: labels?.tagList ?? defaultLabels.tagList,
|
|
27
|
+
removeTag: labels?.removeTag ?? defaultLabels.removeTag,
|
|
28
|
+
clearAll: labels?.clearAll ?? defaultLabels.clearAll,
|
|
29
|
+
duplicate: labels?.duplicate ?? defaultLabels.duplicate,
|
|
30
|
+
maxTags: labels?.maxTags ?? defaultLabels.maxTags,
|
|
31
|
+
invalid: labels?.invalid ?? defaultLabels.invalid,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function validationText(result, fallback) {
|
|
35
|
+
if (typeof result === 'string')
|
|
36
|
+
return result || fallback;
|
|
37
|
+
if (result === false)
|
|
38
|
+
return fallback;
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
export const TagInput = forwardRef(function TagInput({ 'aria-describedby': ariaDescribedBy, 'aria-errormessage': ariaErrorMessage, 'aria-invalid': ariaInvalid, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, className, classNames = {}, clearable = true, createTag, defaultInputValue = '', defaultValue = [], delimiters = defaultTagInputDelimiters, disabled, form, fullWidth = true, getTagValueKey = defaultTagValueKey, id, inputProps = {}, inputValue, invalid = false, labels, maxTags, name, onClick, onInputValueChange, onValidationError, onValueChange, placeholder, readOnly, required, seed, size = 'md', validationMessage, validateTag, value, variant = 'outline', ...rootProps }, forwardedRef) {
|
|
42
|
+
const generatedId = useId();
|
|
43
|
+
const inputRef = useRef(null);
|
|
44
|
+
const removeRefs = useRef(new Map());
|
|
45
|
+
const composingRef = useRef(false);
|
|
46
|
+
const defaultTagsRef = useRef(defaultValue);
|
|
47
|
+
const defaultDraftRef = useRef(defaultInputValue);
|
|
48
|
+
const [uncontrolledTags, setUncontrolledTags] = useState(() => [...defaultValue]);
|
|
49
|
+
const [uncontrolledDraft, setUncontrolledDraft] = useState(defaultInputValue);
|
|
50
|
+
const [localValidationMessage, setLocalValidationMessage] = useState();
|
|
51
|
+
const [focusWithin, setFocusWithin] = useState(false);
|
|
52
|
+
const [pendingFocus, setPendingFocus] = useState(null);
|
|
53
|
+
const tagsControlled = value !== undefined;
|
|
54
|
+
const draftControlled = inputValue !== undefined;
|
|
55
|
+
const currentTags = tagsControlled ? value : uncontrolledTags;
|
|
56
|
+
const currentDraft = draftControlled ? inputValue : uncontrolledDraft;
|
|
57
|
+
const copy = useMemo(() => resolvedLabels(labels), [labels]);
|
|
58
|
+
const activeValidationMessage = validationMessage ?? localValidationMessage;
|
|
59
|
+
const messageId = activeValidationMessage ? `inkra-tag-input-${generatedId}-message` : undefined;
|
|
60
|
+
const state = useResolvedControlProps({
|
|
61
|
+
id,
|
|
62
|
+
required,
|
|
63
|
+
disabled,
|
|
64
|
+
readOnly,
|
|
65
|
+
invalid: invalid || Boolean(activeValidationMessage),
|
|
66
|
+
'aria-describedby': joinAriaIds(ariaDescribedBy, messageId),
|
|
67
|
+
'aria-errormessage': activeValidationMessage ? messageId : ariaErrorMessage,
|
|
68
|
+
'aria-invalid': ariaInvalid,
|
|
69
|
+
});
|
|
70
|
+
const resolvedSeed = seed ?? `inkra-tag-input-${state.id ?? generatedId}`;
|
|
71
|
+
const limit = typeof maxTags === 'number' && Number.isFinite(maxTags)
|
|
72
|
+
? Math.max(0, Math.floor(maxTags))
|
|
73
|
+
: Number.POSITIVE_INFINITY;
|
|
74
|
+
const atLimit = currentTags.length >= limit;
|
|
75
|
+
const canEdit = !state.disabled && !state.readOnly;
|
|
76
|
+
const surfaceVariant = state.invalid
|
|
77
|
+
? 'danger'
|
|
78
|
+
: variant === 'soft'
|
|
79
|
+
? 'quiet'
|
|
80
|
+
: variant === 'underline'
|
|
81
|
+
? 'ghost'
|
|
82
|
+
: 'outline';
|
|
83
|
+
defaultTagsRef.current = defaultValue;
|
|
84
|
+
defaultDraftRef.current = defaultInputValue;
|
|
85
|
+
useImperativeHandle(forwardedRef, () => inputRef.current);
|
|
86
|
+
useEffect(() => {
|
|
87
|
+
if (!pendingFocus)
|
|
88
|
+
return;
|
|
89
|
+
const target = 'input' in pendingFocus
|
|
90
|
+
? inputRef.current
|
|
91
|
+
: removeRefs.current.get(pendingFocus.tagId) ?? inputRef.current;
|
|
92
|
+
target?.focus({ preventScroll: true });
|
|
93
|
+
setPendingFocus(null);
|
|
94
|
+
}, [currentTags, pendingFocus]);
|
|
95
|
+
useEffect(() => {
|
|
96
|
+
const input = inputRef.current;
|
|
97
|
+
const ownerForm = input?.form;
|
|
98
|
+
if (!input || !ownerForm || (tagsControlled && draftControlled))
|
|
99
|
+
return;
|
|
100
|
+
let timer;
|
|
101
|
+
const reset = (event) => {
|
|
102
|
+
clearTimeout(timer);
|
|
103
|
+
timer = setTimeout(() => {
|
|
104
|
+
if (event.defaultPrevented)
|
|
105
|
+
return;
|
|
106
|
+
if (!tagsControlled)
|
|
107
|
+
setUncontrolledTags([...defaultTagsRef.current]);
|
|
108
|
+
if (!draftControlled)
|
|
109
|
+
setUncontrolledDraft(defaultDraftRef.current);
|
|
110
|
+
setLocalValidationMessage(undefined);
|
|
111
|
+
}, 0);
|
|
112
|
+
};
|
|
113
|
+
ownerForm.addEventListener('reset', reset);
|
|
114
|
+
return () => {
|
|
115
|
+
clearTimeout(timer);
|
|
116
|
+
ownerForm.removeEventListener('reset', reset);
|
|
117
|
+
};
|
|
118
|
+
}, [draftControlled, form, tagsControlled]);
|
|
119
|
+
const updateDraft = (next, details) => {
|
|
120
|
+
if (!draftControlled)
|
|
121
|
+
setUncontrolledDraft(next);
|
|
122
|
+
onInputValueChange?.(next, details);
|
|
123
|
+
};
|
|
124
|
+
const commitTags = (next, details) => {
|
|
125
|
+
if (!tagsControlled)
|
|
126
|
+
setUncontrolledTags([...next]);
|
|
127
|
+
onValueChange?.(next, details);
|
|
128
|
+
};
|
|
129
|
+
const reportValidation = (candidate, reason, code, message, tags) => {
|
|
130
|
+
setLocalValidationMessage(message);
|
|
131
|
+
onValidationError?.(message, { candidate, code, reason, tags });
|
|
132
|
+
};
|
|
133
|
+
const commitCandidates = (candidates, reason) => {
|
|
134
|
+
if (!canEdit)
|
|
135
|
+
return false;
|
|
136
|
+
const working = [...currentTags];
|
|
137
|
+
const added = [];
|
|
138
|
+
let rejected;
|
|
139
|
+
for (const rawCandidate of candidates) {
|
|
140
|
+
const candidate = normalizeTagInputValue(rawCandidate);
|
|
141
|
+
if (!candidate)
|
|
142
|
+
continue;
|
|
143
|
+
if (working.length >= limit) {
|
|
144
|
+
rejected = { candidate, code: 'max-tags', message: copy.maxTags(limit) };
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
if (hasTagValue(working, candidate, getTagValueKey)) {
|
|
148
|
+
rejected = { candidate, code: 'duplicate', message: copy.duplicate(candidate) };
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
const context = { tags: working, reason };
|
|
152
|
+
const message = validationText(validateTag?.(candidate, context), copy.invalid(candidate));
|
|
153
|
+
if (message) {
|
|
154
|
+
rejected = { candidate, code: 'invalid', message };
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
const tag = createTag ? createTag(candidate, context) : createDefaultTag(candidate);
|
|
158
|
+
if (!tag || !tag.id || !normalizeTagInputValue(tag.value)) {
|
|
159
|
+
rejected = { candidate, code: 'invalid', message: copy.invalid(candidate) };
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
if (working.some(current => current.id === tag.id) || hasTagValue(working, tag.value, getTagValueKey)) {
|
|
163
|
+
rejected = { candidate, code: 'duplicate', message: copy.duplicate(candidate) };
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
working.push(tag);
|
|
167
|
+
added.push(tag);
|
|
168
|
+
}
|
|
169
|
+
if (added.length > 0)
|
|
170
|
+
commitTags(working, { reason: 'add', tags: added, inputReason: reason });
|
|
171
|
+
if (rejected) {
|
|
172
|
+
updateDraft(rejected.candidate, { reason: 'commit' });
|
|
173
|
+
reportValidation(rejected.candidate, reason, rejected.code, rejected.message, working);
|
|
174
|
+
inputRef.current?.focus({ preventScroll: true });
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
setLocalValidationMessage(undefined);
|
|
178
|
+
updateDraft('', { reason: 'commit' });
|
|
179
|
+
inputRef.current?.focus({ preventScroll: true });
|
|
180
|
+
return added.length > 0;
|
|
181
|
+
};
|
|
182
|
+
const commitDraft = (reason) => {
|
|
183
|
+
const parsed = splitTagInputValue(currentDraft, delimiters);
|
|
184
|
+
return commitCandidates(parsed.parts, reason);
|
|
185
|
+
};
|
|
186
|
+
const removeTag = (index, focus = null) => {
|
|
187
|
+
if (!canEdit)
|
|
188
|
+
return;
|
|
189
|
+
const tag = currentTags[index];
|
|
190
|
+
if (!tag)
|
|
191
|
+
return;
|
|
192
|
+
const next = currentTags.filter((_, tagIndex) => tagIndex !== index);
|
|
193
|
+
const nextFocus = focus ?? (currentTags[index + 1]
|
|
194
|
+
? { tagId: currentTags[index + 1].id }
|
|
195
|
+
: currentTags[index - 1]
|
|
196
|
+
? { tagId: currentTags[index - 1].id }
|
|
197
|
+
: { input: true });
|
|
198
|
+
setLocalValidationMessage(undefined);
|
|
199
|
+
commitTags(next, { reason: 'remove', tags: [tag] });
|
|
200
|
+
setPendingFocus(nextFocus);
|
|
201
|
+
};
|
|
202
|
+
const clearAll = () => {
|
|
203
|
+
if (!canEdit || currentTags.length === 0)
|
|
204
|
+
return;
|
|
205
|
+
const removed = [...currentTags];
|
|
206
|
+
setLocalValidationMessage(undefined);
|
|
207
|
+
commitTags([], { reason: 'clear', tags: removed });
|
|
208
|
+
updateDraft('', { reason: 'clear' });
|
|
209
|
+
setPendingFocus({ input: true });
|
|
210
|
+
};
|
|
211
|
+
const focusRemoveButton = (index) => {
|
|
212
|
+
const tag = currentTags[index];
|
|
213
|
+
if (tag)
|
|
214
|
+
removeRefs.current.get(tag.id)?.focus({ preventScroll: true });
|
|
215
|
+
};
|
|
216
|
+
const handleInputChange = (event) => {
|
|
217
|
+
inputProps.onChange?.(event);
|
|
218
|
+
if (event.defaultPrevented)
|
|
219
|
+
return;
|
|
220
|
+
setLocalValidationMessage(undefined);
|
|
221
|
+
updateDraft(event.currentTarget.value, { reason: 'input' });
|
|
222
|
+
};
|
|
223
|
+
const handleInputPaste = (event) => {
|
|
224
|
+
inputProps.onPaste?.(event);
|
|
225
|
+
if (event.defaultPrevented || !canEdit || composingRef.current)
|
|
226
|
+
return;
|
|
227
|
+
const pasted = event.clipboardData.getData('text');
|
|
228
|
+
const start = event.currentTarget.selectionStart ?? currentDraft.length;
|
|
229
|
+
const end = event.currentTarget.selectionEnd ?? start;
|
|
230
|
+
const combined = `${currentDraft.slice(0, start)}${pasted}${currentDraft.slice(end)}`;
|
|
231
|
+
const parsed = splitTagInputValue(combined, delimiters);
|
|
232
|
+
if (!parsed.hasDelimiter)
|
|
233
|
+
return;
|
|
234
|
+
event.preventDefault();
|
|
235
|
+
commitCandidates(parsed.parts, 'paste');
|
|
236
|
+
};
|
|
237
|
+
const handleInputKeyDown = (event) => {
|
|
238
|
+
inputProps.onKeyDown?.(event);
|
|
239
|
+
if (event.defaultPrevented || !canEdit)
|
|
240
|
+
return;
|
|
241
|
+
const nativeEvent = event.nativeEvent;
|
|
242
|
+
if (composingRef.current || nativeEvent.isComposing || nativeEvent.keyCode === 229)
|
|
243
|
+
return;
|
|
244
|
+
const direction = getComputedStyle(event.currentTarget).direction;
|
|
245
|
+
const previousKey = direction === 'rtl' ? 'ArrowRight' : 'ArrowLeft';
|
|
246
|
+
if (event.key === previousKey && event.currentTarget.selectionStart === 0 && event.currentTarget.selectionEnd === 0) {
|
|
247
|
+
if (currentTags.length > 0 && !state.readOnly) {
|
|
248
|
+
event.preventDefault();
|
|
249
|
+
focusRemoveButton(currentTags.length - 1);
|
|
250
|
+
}
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
if (event.key === 'Backspace' && currentDraft.length === 0 && currentTags.length > 0) {
|
|
254
|
+
event.preventDefault();
|
|
255
|
+
removeTag(currentTags.length - 1, { input: true });
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
if (event.key === 'Enter') {
|
|
259
|
+
event.preventDefault();
|
|
260
|
+
commitDraft('enter');
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
if (delimiters.includes(event.key)) {
|
|
264
|
+
event.preventDefault();
|
|
265
|
+
commitDraft('delimiter');
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
const handleTagKeyDown = (event, index) => {
|
|
269
|
+
if (event.defaultPrevented || !canEdit)
|
|
270
|
+
return;
|
|
271
|
+
const direction = getComputedStyle(event.currentTarget).direction;
|
|
272
|
+
const previousKey = direction === 'rtl' ? 'ArrowRight' : 'ArrowLeft';
|
|
273
|
+
const nextKey = direction === 'rtl' ? 'ArrowLeft' : 'ArrowRight';
|
|
274
|
+
if (event.key === previousKey) {
|
|
275
|
+
event.preventDefault();
|
|
276
|
+
focusRemoveButton(Math.max(0, index - 1));
|
|
277
|
+
}
|
|
278
|
+
else if (event.key === nextKey) {
|
|
279
|
+
event.preventDefault();
|
|
280
|
+
if (index >= currentTags.length - 1)
|
|
281
|
+
inputRef.current?.focus({ preventScroll: true });
|
|
282
|
+
else
|
|
283
|
+
focusRemoveButton(index + 1);
|
|
284
|
+
}
|
|
285
|
+
else if (event.key === 'Home') {
|
|
286
|
+
event.preventDefault();
|
|
287
|
+
focusRemoveButton(0);
|
|
288
|
+
}
|
|
289
|
+
else if (event.key === 'End') {
|
|
290
|
+
event.preventDefault();
|
|
291
|
+
inputRef.current?.focus({ preventScroll: true });
|
|
292
|
+
}
|
|
293
|
+
else if (event.key === 'Backspace' || event.key === 'Delete') {
|
|
294
|
+
event.preventDefault();
|
|
295
|
+
removeTag(index);
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
return (_jsxs("div", { ...rootProps, className: classes('inkra-field-control-hit', 'inkra-tag-input', className, classNames.root), "data-disabled": state.disabled || undefined, "data-focus-within": focusWithin || undefined, "data-full-width": fullWidth, "data-invalid": state.invalid || undefined, "data-max-reached": atLimit || undefined, "data-readonly": state.readOnly || undefined, "data-size": size, "data-variant": variant, onBlurCapture: event => {
|
|
299
|
+
if (!event.currentTarget.contains(event.relatedTarget))
|
|
300
|
+
setFocusWithin(false);
|
|
301
|
+
rootProps.onBlurCapture?.(event);
|
|
302
|
+
}, onClick: event => {
|
|
303
|
+
onClick?.(event);
|
|
304
|
+
if (event.defaultPrevented || state.disabled)
|
|
305
|
+
return;
|
|
306
|
+
const target = event.target;
|
|
307
|
+
if (!target.closest('button, input'))
|
|
308
|
+
inputRef.current?.focus({ preventScroll: true });
|
|
309
|
+
}, onFocusCapture: event => {
|
|
310
|
+
setFocusWithin(true);
|
|
311
|
+
rootProps.onFocusCapture?.(event);
|
|
312
|
+
}, children: [_jsxs(PencilSurface, { as: "div", className: classes('inkra-field-control', 'inkra-tag-input__control', classNames.control), radius: 8, seed: resolvedSeed, variant: surfaceVariant, children: [_jsx("ul", { "aria-label": copy.tagList, className: classes('inkra-tag-input__list', classNames.list), children: currentTags.map((tag, index) => (_jsxs(PencilSurface, { as: "li", className: classes('inkra-tag-input__tag', classNames.tag), clipContent: true, "data-tag-id": tag.id, radius: 6, seed: `${resolvedSeed}-tag-${tag.id}`, variant: "quiet", children: [_jsx("span", { className: "inkra-tag-input__tag-label", children: tag.label ?? tag.value }), !state.readOnly ? (_jsx("button", { "aria-label": copy.removeTag(tag), className: "inkra-tag-input__remove", disabled: state.disabled, onClick: () => removeTag(index), onKeyDown: event => handleTagKeyDown(event, index), ref: node => {
|
|
313
|
+
if (node)
|
|
314
|
+
removeRefs.current.set(tag.id, node);
|
|
315
|
+
else
|
|
316
|
+
removeRefs.current.delete(tag.id);
|
|
317
|
+
}, title: copy.removeTag(tag), type: "button", children: _jsx(PencilIcon, { decorative: true, paths: removePaths, seed: `${resolvedSeed}-remove-${tag.id}`, size: 18, sourceSize: 24 }) })) : null] }, `${tag.id}-${index}`))) }), _jsx("input", { ...inputProps, "aria-describedby": state.describedBy, "aria-errormessage": state.errorMessage, "aria-invalid": state.ariaInvalid, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-readonly": state.readOnly || undefined, "aria-required": state.required || undefined, autoComplete: inputProps.autoComplete ?? 'off', className: classes('inkra-input', 'inkra-tag-input__input', inputProps.className, classNames.input), disabled: state.disabled, form: form, id: state.id, onChange: handleInputChange, onCompositionEnd: event => {
|
|
318
|
+
composingRef.current = false;
|
|
319
|
+
inputProps.onCompositionEnd?.(event);
|
|
320
|
+
}, onCompositionStart: event => {
|
|
321
|
+
composingRef.current = true;
|
|
322
|
+
inputProps.onCompositionStart?.(event);
|
|
323
|
+
}, onKeyDown: handleInputKeyDown, onPaste: handleInputPaste, placeholder: placeholder, readOnly: state.readOnly, ref: inputRef, required: state.required && currentTags.length === 0, spellCheck: inputProps.spellCheck ?? false, type: "text", value: currentDraft }), clearable && currentTags.length > 0 && !state.readOnly ? (_jsx("button", { "aria-label": copy.clearAll, className: classes('inkra-tag-input__clear', classNames.clear), disabled: state.disabled, onClick: clearAll, onPointerDown: (event) => event.preventDefault(), title: copy.clearAll, type: "button", children: _jsx(PencilIcon, { decorative: true, paths: clearPaths, seed: `${resolvedSeed}-clear`, size: 20, sourceSize: 24 }) })) : null, variant === 'underline' ? (_jsx(PencilLine, { className: "inkra-tag-input__underline", seed: `${resolvedSeed}-underline`, weight: (focusWithin ? 1.5 : 1) + (state.invalid ? 0.1 : 0) })) : null] }), name ? currentTags.map((tag, index) => (_jsx("input", { disabled: state.disabled, form: form, name: name, type: "hidden", value: tag.value }, `${tag.id}-${index}`))) : null, activeValidationMessage ? (_jsx(PencilText, { as: "span", className: classes('inkra-tag-input__message', classNames.message), id: messageId, role: "alert", seed: `${resolvedSeed}-message`, children: activeValidationMessage })) : null] }));
|
|
324
|
+
});
|
|
325
|
+
export { createDefaultTag, defaultTagInputDelimiters, defaultTagValueKey, hasTagValue, normalizeTagInputValue, splitTagInputValue, };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface TagInputIdentity {
|
|
2
|
+
id: string;
|
|
3
|
+
value: string;
|
|
4
|
+
}
|
|
5
|
+
export interface SplitTagInputResult {
|
|
6
|
+
parts: string[];
|
|
7
|
+
hasDelimiter: boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare const defaultTagInputDelimiters: readonly [",", ";", "\n"];
|
|
10
|
+
export declare function normalizeTagInputValue(value: string): string;
|
|
11
|
+
export declare function defaultTagValueKey(value: string): string;
|
|
12
|
+
export declare function splitTagInputValue(value: string, delimiters?: readonly string[]): SplitTagInputResult;
|
|
13
|
+
export declare function hasTagValue(tags: readonly TagInputIdentity[], value: string, getTagValueKey?: (value: string) => string): boolean;
|
|
14
|
+
export declare function createDefaultTag(value: string): TagInputIdentity;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export const defaultTagInputDelimiters = [',', ';', '\n'];
|
|
2
|
+
export function normalizeTagInputValue(value) {
|
|
3
|
+
return value.trim();
|
|
4
|
+
}
|
|
5
|
+
export function defaultTagValueKey(value) {
|
|
6
|
+
return normalizeTagInputValue(value).toLocaleLowerCase();
|
|
7
|
+
}
|
|
8
|
+
export function splitTagInputValue(value, delimiters = defaultTagInputDelimiters) {
|
|
9
|
+
const activeDelimiters = [...new Set(delimiters.filter(Boolean))]
|
|
10
|
+
.sort((left, right) => right.length - left.length);
|
|
11
|
+
if (activeDelimiters.length === 0)
|
|
12
|
+
return { parts: [value], hasDelimiter: false };
|
|
13
|
+
const parts = [];
|
|
14
|
+
let start = 0;
|
|
15
|
+
let index = 0;
|
|
16
|
+
let hasDelimiter = false;
|
|
17
|
+
while (index < value.length) {
|
|
18
|
+
const delimiter = activeDelimiters.find(candidate => value.startsWith(candidate, index));
|
|
19
|
+
if (!delimiter) {
|
|
20
|
+
index += 1;
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
parts.push(value.slice(start, index));
|
|
24
|
+
index += delimiter.length;
|
|
25
|
+
start = index;
|
|
26
|
+
hasDelimiter = true;
|
|
27
|
+
}
|
|
28
|
+
parts.push(value.slice(start));
|
|
29
|
+
return { parts, hasDelimiter };
|
|
30
|
+
}
|
|
31
|
+
export function hasTagValue(tags, value, getTagValueKey = defaultTagValueKey) {
|
|
32
|
+
const candidateKey = getTagValueKey(value);
|
|
33
|
+
return tags.some(tag => getTagValueKey(tag.value) === candidateKey);
|
|
34
|
+
}
|
|
35
|
+
export function createDefaultTag(value) {
|
|
36
|
+
const normalized = normalizeTagInputValue(value);
|
|
37
|
+
return { id: normalized, value: normalized };
|
|
38
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { MouseEvent, ReactNode } from 'react';
|
|
2
|
+
import type { InputProps, InputValueChangeDetails } from '../form/Input.js';
|
|
3
|
+
export type TimePickerHourCycle = 'h12' | 'h24';
|
|
4
|
+
export type TimePickerChangeDetails = InputValueChangeDetails;
|
|
5
|
+
export interface TimePickerProps extends Omit<InputProps, 'clearable' | 'clearButtonLabel' | 'onClear' | 'onValueChange' | 'type'> {
|
|
6
|
+
hourCycle?: TimePickerHourCycle;
|
|
7
|
+
locale?: string;
|
|
8
|
+
showSeconds?: boolean;
|
|
9
|
+
clearable?: boolean;
|
|
10
|
+
clearButtonLabel?: string;
|
|
11
|
+
onClear?: (event: MouseEvent<HTMLButtonElement>) => void;
|
|
12
|
+
onValueChange?: (value: string, details: TimePickerChangeDetails) => void;
|
|
13
|
+
startIcon?: ReactNode;
|
|
14
|
+
}
|
|
15
|
+
export declare const TimePicker: import("react").ForwardRefExoticComponent<TimePickerProps & import("react").RefAttributes<HTMLInputElement>>;
|