@wallarm-org/design-system 0.16.4 → 0.16.5
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/FilterInput/FilterInputField/FilterInputChip/ChipSearchInput.d.ts +8 -0
- package/dist/components/FilterInput/FilterInputField/FilterInputChip/ChipSearchInput.js +48 -0
- package/dist/components/FilterInput/FilterInputField/FilterInputChip/FilterInputChip.d.ts +2 -1
- package/dist/components/FilterInput/FilterInputField/FilterInputChip/FilterInputChip.js +22 -12
- package/dist/components/FilterInput/FilterInputField/FilterInputChip/MultiValueSegment.d.ts +9 -0
- package/dist/components/FilterInput/FilterInputField/FilterInputChip/MultiValueSegment.js +32 -0
- package/dist/components/FilterInput/FilterInputField/FilterInputChip/Segment.d.ts +7 -1
- package/dist/components/FilterInput/FilterInputField/FilterInputChip/Segment.js +20 -14
- package/dist/components/FilterInput/FilterInputField/FilterInputChip/classes.d.ts +1 -1
- package/dist/components/FilterInput/FilterInputField/FilterInputChip/classes.js +12 -5
- package/dist/components/FilterInput/FilterInputField/FilterInputChip/constants.d.ts +6 -0
- package/dist/components/FilterInput/FilterInputField/FilterInputChip/constants.js +4 -0
- package/dist/components/FilterInput/FilterInputField/FilterInputChip/{EditingContext.d.ts → context/EditingContext.d.ts} +1 -1
- package/dist/components/FilterInput/FilterInputField/FilterInputChip/index.d.ts +2 -2
- package/dist/components/FilterInput/FilterInputField/FilterInputChip/index.js +1 -1
- package/dist/components/FilterInput/FilterInputField/FilterInputChip/model/useSizerWidth.d.ts +16 -0
- package/dist/components/FilterInput/FilterInputField/FilterInputChip/model/useSizerWidth.js +14 -0
- package/dist/components/FilterInput/FilterInputField/FilterInputField.js +8 -16
- package/dist/components/FilterInput/FilterInputField/classes.d.ts +0 -2
- package/dist/components/FilterInput/FilterInputField/classes.js +3 -4
- package/dist/components/FilterInput/FilterInputMenu/FilterInputMenu.js +14 -20
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useChipEditing.d.ts +3 -1
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useChipEditing.js +20 -8
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useFilterInputAutocomplete.d.ts +1 -1
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useFilterInputAutocomplete.js +31 -1
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useFocusManagement.d.ts +3 -1
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useFocusManagement.js +3 -2
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useMenuFlow.d.ts +6 -2
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useMenuFlow.js +13 -5
- package/dist/components/FilterInput/hooks/useFilterInputExpression/buildChips.js +83 -77
- package/dist/components/FilterInput/hooks/useFilterInputExpression/useFilterInputExpression.d.ts +1 -1
- package/dist/components/FilterInput/hooks/useFilterInputExpression/useFilterInputExpression.js +61 -48
- package/dist/components/FilterInput/lib/index.d.ts +1 -0
- package/dist/components/FilterInput/lib/index.js +2 -1
- package/dist/components/FilterInput/lib/menuFilterText.d.ts +9 -0
- package/dist/components/FilterInput/lib/menuFilterText.js +13 -0
- package/dist/components/FilterInput/types.d.ts +2 -1
- package/dist/metadata/components.json +2 -2
- package/package.json +1 -1
- package/dist/components/FilterInput/FilterInputField/FilterInputChip/OperatorSegment.d.ts +0 -7
- package/dist/components/FilterInput/FilterInputField/FilterInputChip/OperatorSegment.js +0 -23
- package/dist/components/FilterInput/FilterInputField/FilterInputChip/ValueSegment.d.ts +0 -18
- package/dist/components/FilterInput/FilterInputField/FilterInputChip/ValueSegment.js +0 -47
- /package/dist/components/FilterInput/FilterInputField/FilterInputChip/{EditingContext.js → context/EditingContext.js} +0 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { FC } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Inline search input rendered inside a building chip.
|
|
4
|
+
* Reads input state from FilterInputContext.
|
|
5
|
+
* Uses a hidden sizer span to measure width (same approach as Segment).
|
|
6
|
+
* Renders nothing when used outside of FilterInput (e.g. in Storybook or tests).
|
|
7
|
+
*/
|
|
8
|
+
export declare const ChipSearchInput: FC;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useContext, useRef } from "react";
|
|
3
|
+
import { cn } from "../../../../utils/cn.js";
|
|
4
|
+
import { FilterInputContext } from "../../FilterInputContext/FilterInputContext.js";
|
|
5
|
+
import { segmentTextVariants } from "./classes.js";
|
|
6
|
+
import { useSizerWidth } from "./model/useSizerWidth.js";
|
|
7
|
+
const ChipSearchInput = ()=>{
|
|
8
|
+
const ctx = useContext(FilterInputContext);
|
|
9
|
+
const sizerRef = useRef(null);
|
|
10
|
+
const inputText = ctx?.inputText ?? '';
|
|
11
|
+
const inputWidth = useSizerWidth({
|
|
12
|
+
sizerRef,
|
|
13
|
+
text: inputText
|
|
14
|
+
});
|
|
15
|
+
if (!ctx) return null;
|
|
16
|
+
const { inputRef, error, menuOpen, onInputChange, onInputKeyDown, onInputClick } = ctx;
|
|
17
|
+
return /*#__PURE__*/ jsxs(Fragment, {
|
|
18
|
+
children: [
|
|
19
|
+
/*#__PURE__*/ jsx("input", {
|
|
20
|
+
ref: inputRef,
|
|
21
|
+
type: "text",
|
|
22
|
+
role: "combobox",
|
|
23
|
+
"aria-expanded": menuOpen,
|
|
24
|
+
"aria-invalid": error,
|
|
25
|
+
"aria-label": "Filter value",
|
|
26
|
+
"aria-autocomplete": "list",
|
|
27
|
+
value: inputText,
|
|
28
|
+
onChange: onInputChange,
|
|
29
|
+
onKeyDown: onInputKeyDown,
|
|
30
|
+
onClick: onInputClick,
|
|
31
|
+
style: {
|
|
32
|
+
width: `${inputWidth}px`
|
|
33
|
+
},
|
|
34
|
+
className: "h-22 border-none bg-transparent p-0 text-sm shadow-none outline-none ring-0"
|
|
35
|
+
}),
|
|
36
|
+
/*#__PURE__*/ jsx("span", {
|
|
37
|
+
ref: sizerRef,
|
|
38
|
+
className: cn(segmentTextVariants({
|
|
39
|
+
variant: 'value'
|
|
40
|
+
}), 'invisible absolute whitespace-pre'),
|
|
41
|
+
"aria-hidden": true,
|
|
42
|
+
children: inputText || ' '
|
|
43
|
+
})
|
|
44
|
+
]
|
|
45
|
+
});
|
|
46
|
+
};
|
|
47
|
+
ChipSearchInput.displayName = 'ChipSearchInput';
|
|
48
|
+
export { ChipSearchInput };
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import type { FC, HTMLAttributes } from 'react';
|
|
1
|
+
import type { FC, HTMLAttributes, Ref } from 'react';
|
|
2
2
|
import type { ChipErrorSegment } from '../../types';
|
|
3
3
|
export type ChipSegment = 'attribute' | 'operator' | 'value';
|
|
4
4
|
export interface FilterInputChipProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
|
|
5
|
+
ref?: Ref<HTMLDivElement>;
|
|
5
6
|
chipId?: string;
|
|
6
7
|
attribute: string;
|
|
7
8
|
operator?: string;
|
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useCallback, useRef } from "react";
|
|
3
3
|
import { cn } from "../../../../utils/cn.js";
|
|
4
|
+
import { ChipSearchInput } from "./ChipSearchInput.js";
|
|
4
5
|
import { chipVariants } from "./classes.js";
|
|
5
|
-
import { useEditingContext } from "./EditingContext.js";
|
|
6
|
+
import { useEditingContext } from "./context/EditingContext.js";
|
|
6
7
|
import { FilterInputRemoveButton } from "./FilterInputRemoveButton.js";
|
|
7
|
-
import { OperatorSegment } from "./OperatorSegment.js";
|
|
8
8
|
import { Segment } from "./Segment.js";
|
|
9
|
-
|
|
10
|
-
const FilterInputChip = ({ chipId, attribute, operator, value, error = false, valueParts, valueSeparator, errorValueIndices, building = false, onRemove, onSegmentClick, className, ...props })=>{
|
|
9
|
+
const FilterInputChip = ({ ref, chipId, attribute, operator, value, error = false, valueParts, valueSeparator, errorValueIndices, building = false, onRemove, onSegmentClick, className, ...props })=>{
|
|
11
10
|
const interactive = !building;
|
|
12
11
|
const hasError = !!error;
|
|
13
|
-
const
|
|
12
|
+
const internalRef = useRef(null);
|
|
14
13
|
const editing = useEditingContext();
|
|
15
14
|
const isEditingThisChip = null != editing && null != chipId && editing.editingChipId === chipId;
|
|
16
15
|
const activeSegment = isEditingThisChip ? editing.editingSegment : null;
|
|
@@ -18,7 +17,7 @@ const FilterInputChip = ({ chipId, attribute, operator, value, error = false, va
|
|
|
18
17
|
if (!onSegmentClick) return;
|
|
19
18
|
if (activeSegment === segment) return;
|
|
20
19
|
e.stopPropagation();
|
|
21
|
-
const anchorEl = 'attribute' === segment ?
|
|
20
|
+
const anchorEl = 'attribute' === segment ? internalRef.current : e.currentTarget;
|
|
22
21
|
if (!anchorEl) return;
|
|
23
22
|
onSegmentClick(segment, anchorEl.getBoundingClientRect());
|
|
24
23
|
}, [
|
|
@@ -32,8 +31,15 @@ const FilterInputChip = ({ chipId, attribute, operator, value, error = false, va
|
|
|
32
31
|
onEditKeyDown: editing.onSegmentEditKeyDown,
|
|
33
32
|
onEditBlur: editing.onSegmentEditBlur
|
|
34
33
|
} : {};
|
|
34
|
+
const setRefs = useCallback((node)=>{
|
|
35
|
+
internalRef.current = node;
|
|
36
|
+
if ('function' == typeof ref) ref(node);
|
|
37
|
+
else if (ref) ref.current = node;
|
|
38
|
+
}, [
|
|
39
|
+
ref
|
|
40
|
+
]);
|
|
35
41
|
return /*#__PURE__*/ jsxs("div", {
|
|
36
|
-
ref:
|
|
42
|
+
ref: setRefs,
|
|
37
43
|
className: cn(chipVariants({
|
|
38
44
|
error: hasError,
|
|
39
45
|
interactive
|
|
@@ -49,21 +55,25 @@ const FilterInputChip = ({ chipId, attribute, operator, value, error = false, va
|
|
|
49
55
|
...segmentEditProps('attribute'),
|
|
50
56
|
children: attribute
|
|
51
57
|
}),
|
|
52
|
-
operator && /*#__PURE__*/ jsx(
|
|
58
|
+
(operator || 'operator' === activeSegment) && /*#__PURE__*/ jsx(Segment, {
|
|
59
|
+
variant: "operator",
|
|
53
60
|
className: "shrink-0",
|
|
54
61
|
onClick: interactive ? (e)=>handleSegmentClick('operator', e) : void 0,
|
|
55
|
-
|
|
62
|
+
...segmentEditProps('operator'),
|
|
63
|
+
children: operator ?? ''
|
|
56
64
|
}),
|
|
57
|
-
value && /*#__PURE__*/ jsx(
|
|
58
|
-
|
|
65
|
+
(value || 'value' === activeSegment) && /*#__PURE__*/ jsx(Segment, {
|
|
66
|
+
variant: "value",
|
|
67
|
+
className: "shrink-0",
|
|
59
68
|
error: 'value' !== activeSegment && (true === error || 'value' === error),
|
|
60
69
|
valueParts: valueParts,
|
|
61
70
|
valueSeparator: valueSeparator,
|
|
62
71
|
errorValueIndices: errorValueIndices,
|
|
63
72
|
onClick: interactive ? (e)=>handleSegmentClick('value', e) : void 0,
|
|
64
73
|
...segmentEditProps('value'),
|
|
65
|
-
children: value
|
|
74
|
+
children: value ?? ''
|
|
66
75
|
}),
|
|
76
|
+
building && /*#__PURE__*/ jsx(ChipSearchInput, {}),
|
|
67
77
|
onRemove && /*#__PURE__*/ jsx(FilterInputRemoveButton, {
|
|
68
78
|
error: hasError,
|
|
69
79
|
onRemove: onRemove
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { FC, HTMLAttributes } from 'react';
|
|
2
|
+
interface MultiValueSegmentProps extends HTMLAttributes<HTMLDivElement> {
|
|
3
|
+
valueParts: string[];
|
|
4
|
+
valueSeparator: string;
|
|
5
|
+
errorValueIndices: number[];
|
|
6
|
+
}
|
|
7
|
+
/** Value segment with per-part error highlighting for multi-value chips. */
|
|
8
|
+
export declare const MultiValueSegment: FC<MultiValueSegmentProps>;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../../../../utils/cn.js";
|
|
3
|
+
import { segmentContainer, segmentTextVariants } from "./classes.js";
|
|
4
|
+
const MultiValueSegment = ({ valueParts, valueSeparator, errorValueIndices, className, ...props })=>{
|
|
5
|
+
const isInteractive = !!props.onClick;
|
|
6
|
+
return /*#__PURE__*/ jsx("div", {
|
|
7
|
+
className: cn(segmentContainer, className),
|
|
8
|
+
"data-slot": "segment-value",
|
|
9
|
+
...isInteractive && {
|
|
10
|
+
role: 'button',
|
|
11
|
+
'aria-label': 'Edit filter value'
|
|
12
|
+
},
|
|
13
|
+
...props,
|
|
14
|
+
children: /*#__PURE__*/ jsx("p", {
|
|
15
|
+
className: segmentTextVariants({
|
|
16
|
+
variant: 'value'
|
|
17
|
+
}),
|
|
18
|
+
children: valueParts.map((part, idx)=>/*#__PURE__*/ jsxs("span", {
|
|
19
|
+
className: errorValueIndices.includes(idx) ? segmentTextVariants({
|
|
20
|
+
variant: 'value',
|
|
21
|
+
error: true
|
|
22
|
+
}) : void 0,
|
|
23
|
+
children: [
|
|
24
|
+
part,
|
|
25
|
+
idx < valueParts.length - 1 && valueSeparator
|
|
26
|
+
]
|
|
27
|
+
}, idx))
|
|
28
|
+
})
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
MultiValueSegment.displayName = 'MultiValueSegment';
|
|
32
|
+
export { MultiValueSegment };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { FC, FocusEvent, HTMLAttributes, KeyboardEvent } from 'react';
|
|
2
2
|
type SegmentVariant = 'attribute' | 'operator' | 'value';
|
|
3
|
-
type SegmentProps = HTMLAttributes<HTMLDivElement> & {
|
|
3
|
+
export type SegmentProps = HTMLAttributes<HTMLDivElement> & {
|
|
4
4
|
variant: SegmentVariant;
|
|
5
5
|
children: string;
|
|
6
6
|
error?: boolean;
|
|
@@ -9,6 +9,12 @@ type SegmentProps = HTMLAttributes<HTMLDivElement> & {
|
|
|
9
9
|
onEditChange?: (text: string) => void;
|
|
10
10
|
onEditKeyDown?: (e: KeyboardEvent<HTMLInputElement>) => void;
|
|
11
11
|
onEditBlur?: (e: FocusEvent<HTMLInputElement>) => void;
|
|
12
|
+
/** Individual display parts for multi-value chips (value variant only) */
|
|
13
|
+
valueParts?: string[];
|
|
14
|
+
/** Separator between parts (default: ", ") */
|
|
15
|
+
valueSeparator?: string;
|
|
16
|
+
/** Indices of invalid values in valueParts */
|
|
17
|
+
errorValueIndices?: number[];
|
|
12
18
|
};
|
|
13
19
|
export declare const Segment: FC<SegmentProps>;
|
|
14
20
|
export {};
|
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect, useRef
|
|
2
|
+
import { useEffect, useRef } from "react";
|
|
3
3
|
import { cn } from "../../../../utils/cn.js";
|
|
4
4
|
import { segmentContainer, segmentTextVariants } from "./classes.js";
|
|
5
|
-
|
|
5
|
+
import { CHAR_WIDTH_PX } from "./constants.js";
|
|
6
|
+
import { MultiValueSegment } from "./MultiValueSegment.js";
|
|
7
|
+
import { useSizerWidth } from "./model/useSizerWidth.js";
|
|
8
|
+
const Segment = ({ variant, children, className, error, editing, editText, onEditChange, onEditKeyDown, onEditBlur, valueParts, valueSeparator = ', ', errorValueIndices, ...props })=>{
|
|
6
9
|
const textRef = useRef(null);
|
|
7
10
|
const inputRef = useRef(null);
|
|
8
11
|
const sizerRef = useRef(null);
|
|
9
12
|
const lastTextWidthRef = useRef(0);
|
|
10
|
-
const [inputWidth, setInputWidth] = useState(void 0);
|
|
11
13
|
useEffect(()=>{
|
|
12
14
|
if (editing) return;
|
|
13
|
-
const width = textRef.current?.getBoundingClientRect().width ??
|
|
15
|
+
const width = textRef.current?.getBoundingClientRect().width ?? children.length * CHAR_WIDTH_PX;
|
|
14
16
|
lastTextWidthRef.current = width;
|
|
15
17
|
}, [
|
|
16
18
|
editing,
|
|
@@ -33,15 +35,19 @@ const Segment = ({ variant, children, className, error, editing, editText, onEdi
|
|
|
33
35
|
}, [
|
|
34
36
|
editing
|
|
35
37
|
]);
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}, [
|
|
41
|
-
editing,
|
|
42
|
-
editText
|
|
43
|
-
]);
|
|
38
|
+
const inputWidth = useSizerWidth({
|
|
39
|
+
sizerRef,
|
|
40
|
+
text: editText ?? ''
|
|
41
|
+
});
|
|
44
42
|
const isInteractive = !!props.onClick;
|
|
43
|
+
const hasMultiValueErrors = 'value' === variant && !!valueParts && !!errorValueIndices && errorValueIndices.length > 0 && !editing;
|
|
44
|
+
if (hasMultiValueErrors) return /*#__PURE__*/ jsx(MultiValueSegment, {
|
|
45
|
+
valueParts: valueParts,
|
|
46
|
+
valueSeparator: valueSeparator,
|
|
47
|
+
errorValueIndices: errorValueIndices,
|
|
48
|
+
className: className,
|
|
49
|
+
...props
|
|
50
|
+
});
|
|
45
51
|
return /*#__PURE__*/ jsx("div", {
|
|
46
52
|
className: cn(segmentContainer, className),
|
|
47
53
|
"data-slot": `segment-${variant}`,
|
|
@@ -63,9 +69,9 @@ const Segment = ({ variant, children, className, error, editing, editText, onEdi
|
|
|
63
69
|
className: cn(segmentTextVariants({
|
|
64
70
|
variant,
|
|
65
71
|
error
|
|
66
|
-
}), 'bg-transparent
|
|
72
|
+
}), 'bg-transparent outline-none p-0 m-0'),
|
|
67
73
|
style: {
|
|
68
|
-
width: `${inputWidth
|
|
74
|
+
width: `${inputWidth}px`
|
|
69
75
|
}
|
|
70
76
|
}),
|
|
71
77
|
/*#__PURE__*/ jsx("span", {
|
|
@@ -4,7 +4,7 @@ export declare const chipVariants: (props?: ({
|
|
|
4
4
|
interactive?: boolean | null | undefined;
|
|
5
5
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
6
6
|
/** Segment container */
|
|
7
|
-
export declare const segmentContainer = "flex flex-col justify-center overflow-hidden
|
|
7
|
+
export declare const segmentContainer = "flex flex-col justify-center overflow-hidden leading-none";
|
|
8
8
|
/** Segment text styles by variant */
|
|
9
9
|
export declare const segmentTextVariants: (props?: ({
|
|
10
10
|
variant?: "operator" | "value" | "attribute" | null | undefined;
|
|
@@ -1,21 +1,28 @@
|
|
|
1
1
|
import { cva } from "class-variance-authority";
|
|
2
|
-
const chipVariants = cva('group/chip relative flex items-center justify-center
|
|
2
|
+
const chipVariants = cva('h-22 group/chip relative flex items-center justify-center px-5 py-0 border border-solid rounded-8 gap-4', {
|
|
3
3
|
variants: {
|
|
4
4
|
error: {
|
|
5
5
|
true: 'bg-bg-light-danger border-border-danger',
|
|
6
6
|
false: 'bg-badge-badge-bg border-border-primary'
|
|
7
7
|
},
|
|
8
8
|
interactive: {
|
|
9
|
-
true: 'cursor-pointer
|
|
10
|
-
false: '
|
|
9
|
+
true: 'cursor-pointer',
|
|
10
|
+
false: ''
|
|
11
11
|
}
|
|
12
12
|
},
|
|
13
|
+
compoundVariants: [
|
|
14
|
+
{
|
|
15
|
+
interactive: false,
|
|
16
|
+
error: false,
|
|
17
|
+
className: 'border-border-strong-primary'
|
|
18
|
+
}
|
|
19
|
+
],
|
|
13
20
|
defaultVariants: {
|
|
14
21
|
error: false,
|
|
15
22
|
interactive: false
|
|
16
23
|
}
|
|
17
24
|
});
|
|
18
|
-
const segmentContainer = 'flex flex-col justify-center overflow-hidden
|
|
25
|
+
const segmentContainer = 'flex flex-col justify-center overflow-hidden leading-none';
|
|
19
26
|
const segmentTextVariants = cva('truncate text-sm', {
|
|
20
27
|
variants: {
|
|
21
28
|
variant: {
|
|
@@ -49,7 +56,7 @@ const segmentTextVariants = cva('truncate text-sm', {
|
|
|
49
56
|
error: false
|
|
50
57
|
}
|
|
51
58
|
});
|
|
52
|
-
const removeButtonVariants = cva('absolute -right-
|
|
59
|
+
const removeButtonVariants = cva('absolute -right-[13px] top-[-1px] bottom-[-1px] flex items-center justify-center p-0 cursor-pointer w-[18px] border border-solid border-l-0 rounded-r-8 opacity-0 group-hover/chip:opacity-100 focus:opacity-100 transition-opacity', {
|
|
53
60
|
variants: {
|
|
54
61
|
error: {
|
|
55
62
|
true: 'border-border-danger bg-bg-light-danger text-text-danger',
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** Minimum input width in px */
|
|
2
|
+
export declare const MIN_INPUT_WIDTH = 4;
|
|
3
|
+
/** Extra pixels added to measured width to prevent text clipping */
|
|
4
|
+
export declare const WIDTH_OFFSET = 1;
|
|
5
|
+
/** Approximate width of a single character in px (text-sm fallback) */
|
|
6
|
+
export declare const CHAR_WIDTH_PX = 8;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type FC, type FocusEvent, type KeyboardEvent, type ReactNode } from 'react';
|
|
2
|
-
import type { ChipSegment } from '
|
|
2
|
+
import type { ChipSegment } from '../FilterInputChip';
|
|
3
3
|
export interface EditingContextValue {
|
|
4
4
|
editingChipId: string | null;
|
|
5
5
|
editingSegment: ChipSegment | null;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { type ConnectorVariant, FilterInputConnectorChip, type FilterInputConnectorChipProps, } from '../FilterInputConnectorChip';
|
|
2
|
-
export { type EditingContextValue, EditingProvider, useEditingContext } from './EditingContext';
|
|
2
|
+
export { type EditingContextValue, EditingProvider, useEditingContext, } from './context/EditingContext';
|
|
3
3
|
export { type ChipSegment, FilterInputChip, type FilterInputChipProps } from './FilterInputChip';
|
|
4
4
|
export { FilterInputRemoveButton } from './FilterInputRemoveButton';
|
|
5
|
-
export { Segment } from './Segment';
|
|
5
|
+
export { Segment, type SegmentProps } from './Segment';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { FilterInputConnectorChip } from "../FilterInputConnectorChip/index.js";
|
|
2
|
-
import { EditingProvider, useEditingContext } from "./EditingContext.js";
|
|
2
|
+
import { EditingProvider, useEditingContext } from "./context/EditingContext.js";
|
|
3
3
|
import { FilterInputChip } from "./FilterInputChip.js";
|
|
4
4
|
import { FilterInputRemoveButton } from "./FilterInputRemoveButton.js";
|
|
5
5
|
import { Segment } from "./Segment.js";
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { RefObject } from 'react';
|
|
2
|
+
interface UseSizerWidthOptions {
|
|
3
|
+
/** Ref to the hidden sizer span */
|
|
4
|
+
sizerRef: RefObject<HTMLSpanElement | null>;
|
|
5
|
+
/** Current text to measure */
|
|
6
|
+
text: string;
|
|
7
|
+
/** Minimum width floor (defaults to MIN_INPUT_WIDTH) */
|
|
8
|
+
minWidth?: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Measures input width from a hidden sizer span.
|
|
12
|
+
* Falls back to character count * CHAR_WIDTH_PX when the DOM element is unavailable.
|
|
13
|
+
* Returns the measured width + WIDTH_OFFSET to prevent text clipping.
|
|
14
|
+
*/
|
|
15
|
+
export declare const useSizerWidth: ({ sizerRef, text, minWidth, }: UseSizerWidthOptions) => number;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
import { CHAR_WIDTH_PX, MIN_INPUT_WIDTH, WIDTH_OFFSET } from "../constants.js";
|
|
3
|
+
const useSizerWidth = ({ sizerRef, text, minWidth = MIN_INPUT_WIDTH })=>{
|
|
4
|
+
const [width, setWidth] = useState(minWidth);
|
|
5
|
+
useEffect(()=>{
|
|
6
|
+
const sizerWidth = sizerRef.current?.getBoundingClientRect().width ?? text.length * CHAR_WIDTH_PX;
|
|
7
|
+
setWidth(Math.max(minWidth, sizerWidth));
|
|
8
|
+
}, [
|
|
9
|
+
text,
|
|
10
|
+
minWidth
|
|
11
|
+
]);
|
|
12
|
+
return width + WIDTH_OFFSET;
|
|
13
|
+
};
|
|
14
|
+
export { useSizerWidth };
|
|
@@ -6,8 +6,8 @@ import { ScrollArea, ScrollAreaScrollbar, ScrollAreaViewport } from "../../Scrol
|
|
|
6
6
|
import { useFilterInputContext } from "../FilterInputContext/index.js";
|
|
7
7
|
import { isMenuRelated } from "../lib/index.js";
|
|
8
8
|
import { ChipsWithGaps, TrailingGap } from "./ChipsWithGaps.js";
|
|
9
|
-
import { ACTIONS_PADDING, COLLAPSED_MAX_HEIGHT,
|
|
10
|
-
import { EditingProvider } from "./FilterInputChip/EditingContext.js";
|
|
9
|
+
import { ACTIONS_PADDING, COLLAPSED_MAX_HEIGHT, filterInputContainerVariants, filterInputInnerVariants } from "./classes.js";
|
|
10
|
+
import { EditingProvider } from "./FilterInputChip/context/EditingContext.js";
|
|
11
11
|
import { FilterInputChip } from "./FilterInputChip/FilterInputChip.js";
|
|
12
12
|
import { FilterInputFieldActions } from "./FilterInputFieldActions.js";
|
|
13
13
|
import { FilterInputSearch } from "./FilterInputSearch.js";
|
|
@@ -104,21 +104,13 @@ const FilterInputField = ({ className, ...props })=>{
|
|
|
104
104
|
hideTrailingGap: hideTrailingGap,
|
|
105
105
|
...chipsGapProps
|
|
106
106
|
}),
|
|
107
|
-
buildingChipData ? /*#__PURE__*/
|
|
107
|
+
buildingChipData ? /*#__PURE__*/ jsx(FilterInputChip, {
|
|
108
108
|
ref: buildingChipRef,
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
operator: buildingChipData.operator,
|
|
115
|
-
value: buildingChipData.value,
|
|
116
|
-
className: "border-none"
|
|
117
|
-
}),
|
|
118
|
-
/*#__PURE__*/ jsx(FilterInputSearch, {
|
|
119
|
-
hasContent: true
|
|
120
|
-
})
|
|
121
|
-
]
|
|
109
|
+
building: true,
|
|
110
|
+
attribute: buildingChipData.attribute ?? '',
|
|
111
|
+
operator: buildingChipData.operator,
|
|
112
|
+
value: buildingChipData.value,
|
|
113
|
+
className: "mx-4"
|
|
122
114
|
}) : /*#__PURE__*/ jsx(FilterInputSearch, {
|
|
123
115
|
hasContent: hasContent
|
|
124
116
|
}),
|
|
@@ -11,8 +11,6 @@ export declare const filterInputContainerVariants: (props?: ({
|
|
|
11
11
|
export declare const filterInputInnerVariants: (props?: ({
|
|
12
12
|
hasContent?: boolean | null | undefined;
|
|
13
13
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
14
|
-
/** Wrapper that visually groups the building chip and the filter input */
|
|
15
|
-
export declare const buildingChipWrapperClass = "flex items-center min-w-0 h-24 rounded-8 border border-solid border-border-strong-primary bg-badge-badge-bg ml-8";
|
|
16
14
|
/** Native input element inside the query bar */
|
|
17
15
|
export declare const filterInputInputVariants: (props?: ({
|
|
18
16
|
hasContent?: boolean | null | undefined;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { cva } from "class-variance-authority";
|
|
2
2
|
const VISIBLE_ROWS = 3;
|
|
3
|
-
const CHIP_ROW_HEIGHT =
|
|
3
|
+
const CHIP_ROW_HEIGHT = 22;
|
|
4
4
|
const ROW_GAP = 4;
|
|
5
5
|
const PADDING_Y = 16;
|
|
6
6
|
const EDGE_GAP = 8;
|
|
@@ -38,11 +38,10 @@ const filterInputInnerVariants = cva('flex min-h-[40px] w-full cursor-text flex-
|
|
|
38
38
|
hasContent: false
|
|
39
39
|
}
|
|
40
40
|
});
|
|
41
|
-
const buildingChipWrapperClass = 'flex items-center min-w-0 h-24 rounded-8 border border-solid border-border-strong-primary bg-badge-badge-bg ml-8';
|
|
42
41
|
const filterInputInputVariants = cva('h-24 border-none bg-transparent p-0 text-sm shadow-none outline-none ring-0', {
|
|
43
42
|
variants: {
|
|
44
43
|
hasContent: {
|
|
45
|
-
true: '
|
|
44
|
+
true: 'ml-4',
|
|
46
45
|
false: 'flex-1'
|
|
47
46
|
}
|
|
48
47
|
},
|
|
@@ -50,4 +49,4 @@ const filterInputInputVariants = cva('h-24 border-none bg-transparent p-0 text-s
|
|
|
50
49
|
hasContent: false
|
|
51
50
|
}
|
|
52
51
|
});
|
|
53
|
-
export { ACTIONS_PADDING, COLLAPSED_MAX_HEIGHT,
|
|
52
|
+
export { ACTIONS_PADDING, COLLAPSED_MAX_HEIGHT, filterInputContainerVariants, filterInputInnerVariants, filterInputInputVariants };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { getFieldValues, isBetweenOperator, isMultiSelectOperator } from "../lib/index.js";
|
|
2
|
+
import { getFieldValues, getValueFilterText, isBetweenOperator, isMultiSelectOperator } from "../lib/index.js";
|
|
3
3
|
import { FilterInputDateValueMenu } from "./FilterInputDateValueMenu/index.js";
|
|
4
4
|
import { FilterInputFieldMenu } from "./FilterInputFieldMenu/index.js";
|
|
5
5
|
import { FilterInputOperatorMenu } from "./FilterInputOperatorMenu.js";
|
|
@@ -7,19 +7,13 @@ import { FilterInputValueMenu } from "./FilterInputValueMenu/index.js";
|
|
|
7
7
|
const FilterInputMenu = ({ fields, autocomplete })=>{
|
|
8
8
|
const { inputText, menuState, selectedField, selectedOperator, menuPositioning, editingMultiValues, editingSingleValue, editingDateRange, inputRef, menuRef, handleFieldSelect, handleOperatorSelect, handleValueSelect, handleMultiCommit, handleRangeSelect, handleMenuClose, handleMenuDiscard, handleBuildingValueChange, segmentMenuFilterText, editingSegment, blurCommitRef } = autocomplete;
|
|
9
9
|
const fieldFilterText = 'attribute' === editingSegment ? segmentMenuFilterText : inputText;
|
|
10
|
-
const operatorFilterText = editingSegment ?
|
|
10
|
+
const operatorFilterText = 'operator' === editingSegment ? segmentMenuFilterText : inputText;
|
|
11
11
|
const selectedFieldValues = selectedField ? getFieldValues(selectedField) : [];
|
|
12
|
-
const valueFilterText = (
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
if (selectedFieldValues.length > 0) {
|
|
18
|
-
const isKnownValue = selectedFieldValues.some((v)=>v.label.toLowerCase() === lastToken.toLowerCase() || String(v.value).toLowerCase() === lastToken.toLowerCase());
|
|
19
|
-
if (isKnownValue) return '';
|
|
20
|
-
}
|
|
21
|
-
return lastToken;
|
|
22
|
-
})();
|
|
12
|
+
const valueFilterText = getValueFilterText(editingSegment, inputText, segmentMenuFilterText, selectedOperator, selectedFieldValues);
|
|
13
|
+
const showOperatorMenu = !!selectedField;
|
|
14
|
+
const showValueMenu = !!selectedField && !!selectedOperator;
|
|
15
|
+
const isDateField = selectedField?.type === 'date';
|
|
16
|
+
const hasValueOptions = selectedFieldValues.length > 0;
|
|
23
17
|
return /*#__PURE__*/ jsxs(Fragment, {
|
|
24
18
|
children: [
|
|
25
19
|
/*#__PURE__*/ jsx(FilterInputFieldMenu, {
|
|
@@ -27,29 +21,29 @@ const FilterInputMenu = ({ fields, autocomplete })=>{
|
|
|
27
21
|
filterText: fieldFilterText,
|
|
28
22
|
open: 'field' === menuState,
|
|
29
23
|
onSelect: handleFieldSelect,
|
|
30
|
-
onOpenChange:
|
|
24
|
+
onOpenChange: handleMenuClose,
|
|
31
25
|
onEscape: handleMenuDiscard,
|
|
32
26
|
positioning: menuPositioning,
|
|
33
27
|
inputRef: inputRef,
|
|
34
28
|
menuRef: menuRef
|
|
35
29
|
}),
|
|
36
|
-
|
|
30
|
+
showOperatorMenu && /*#__PURE__*/ jsx(FilterInputOperatorMenu, {
|
|
37
31
|
fieldType: selectedField.type,
|
|
38
32
|
operators: selectedField.operators,
|
|
39
33
|
open: 'operator' === menuState,
|
|
40
34
|
onSelect: handleOperatorSelect,
|
|
41
|
-
onOpenChange:
|
|
35
|
+
onOpenChange: handleMenuClose,
|
|
42
36
|
onEscape: handleMenuDiscard,
|
|
43
37
|
positioning: menuPositioning,
|
|
44
38
|
inputRef: inputRef,
|
|
45
39
|
menuRef: menuRef,
|
|
46
40
|
filterText: operatorFilterText
|
|
47
41
|
}),
|
|
48
|
-
|
|
42
|
+
showValueMenu && (isDateField ? /*#__PURE__*/ jsx(FilterInputDateValueMenu, {
|
|
49
43
|
open: 'value' === menuState,
|
|
50
44
|
onSelect: handleValueSelect,
|
|
51
45
|
onRangeSelect: handleRangeSelect,
|
|
52
|
-
onOpenChange:
|
|
46
|
+
onOpenChange: handleMenuClose,
|
|
53
47
|
onEscape: handleMenuDiscard,
|
|
54
48
|
positioning: menuPositioning,
|
|
55
49
|
range: isBetweenOperator(selectedOperator),
|
|
@@ -57,12 +51,12 @@ const FilterInputMenu = ({ fields, autocomplete })=>{
|
|
|
57
51
|
initialValue: null != editingSingleValue ? String(editingSingleValue) : void 0,
|
|
58
52
|
initialRangeValue: editingDateRange,
|
|
59
53
|
filterText: valueFilterText
|
|
60
|
-
}) :
|
|
54
|
+
}) : hasValueOptions && /*#__PURE__*/ jsx(FilterInputValueMenu, {
|
|
61
55
|
values: selectedFieldValues,
|
|
62
56
|
open: 'value' === menuState,
|
|
63
57
|
onSelect: handleValueSelect,
|
|
64
58
|
onCommit: handleMultiCommit,
|
|
65
|
-
onOpenChange:
|
|
59
|
+
onOpenChange: handleMenuClose,
|
|
66
60
|
onEscape: handleMenuDiscard,
|
|
67
61
|
multiSelect: isMultiSelectOperator(selectedOperator),
|
|
68
62
|
initialValues: editingMultiValues,
|
|
@@ -10,15 +10,17 @@ interface UseChipEditingOptions {
|
|
|
10
10
|
setSelectedField: (field: FieldMetadata | null) => void;
|
|
11
11
|
setSelectedOperator: (op: FilterOperator | null) => void;
|
|
12
12
|
setMenuState: (state: MenuState) => void;
|
|
13
|
+
upsertCondition: (field: FieldMetadata, operator: FilterOperator | undefined, val: string | number | boolean | null | Array<string | number | boolean>, editingChipId?: string | null) => void;
|
|
13
14
|
}
|
|
14
15
|
/**
|
|
15
16
|
* Manages editing of existing filter chips.
|
|
16
17
|
* Handles chip click → open appropriate menu based on segment,
|
|
17
18
|
* then advances through the full flow (field → operator → value).
|
|
18
19
|
*/
|
|
19
|
-
export declare const useChipEditing: ({ conditions, chips, fields, containerRef, setMenuOffset, setSelectedField, setSelectedOperator, setMenuState, }: UseChipEditingOptions) => {
|
|
20
|
+
export declare const useChipEditing: ({ conditions, chips, fields, containerRef, setMenuOffset, setSelectedField, setSelectedOperator, setMenuState, upsertCondition, }: UseChipEditingOptions) => {
|
|
20
21
|
editingChipId: string | null;
|
|
21
22
|
editingSegment: ChipSegment | null;
|
|
23
|
+
setEditingSegment: import("react").Dispatch<import("react").SetStateAction<ChipSegment | null>>;
|
|
22
24
|
segmentFilterText: string;
|
|
23
25
|
segmentMenuFilterText: string;
|
|
24
26
|
setSegmentFilterText: (text: string) => void;
|