@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.
Files changed (44) hide show
  1. package/dist/components/FilterInput/FilterInputField/FilterInputChip/ChipSearchInput.d.ts +8 -0
  2. package/dist/components/FilterInput/FilterInputField/FilterInputChip/ChipSearchInput.js +48 -0
  3. package/dist/components/FilterInput/FilterInputField/FilterInputChip/FilterInputChip.d.ts +2 -1
  4. package/dist/components/FilterInput/FilterInputField/FilterInputChip/FilterInputChip.js +22 -12
  5. package/dist/components/FilterInput/FilterInputField/FilterInputChip/MultiValueSegment.d.ts +9 -0
  6. package/dist/components/FilterInput/FilterInputField/FilterInputChip/MultiValueSegment.js +32 -0
  7. package/dist/components/FilterInput/FilterInputField/FilterInputChip/Segment.d.ts +7 -1
  8. package/dist/components/FilterInput/FilterInputField/FilterInputChip/Segment.js +20 -14
  9. package/dist/components/FilterInput/FilterInputField/FilterInputChip/classes.d.ts +1 -1
  10. package/dist/components/FilterInput/FilterInputField/FilterInputChip/classes.js +12 -5
  11. package/dist/components/FilterInput/FilterInputField/FilterInputChip/constants.d.ts +6 -0
  12. package/dist/components/FilterInput/FilterInputField/FilterInputChip/constants.js +4 -0
  13. package/dist/components/FilterInput/FilterInputField/FilterInputChip/{EditingContext.d.ts → context/EditingContext.d.ts} +1 -1
  14. package/dist/components/FilterInput/FilterInputField/FilterInputChip/index.d.ts +2 -2
  15. package/dist/components/FilterInput/FilterInputField/FilterInputChip/index.js +1 -1
  16. package/dist/components/FilterInput/FilterInputField/FilterInputChip/model/useSizerWidth.d.ts +16 -0
  17. package/dist/components/FilterInput/FilterInputField/FilterInputChip/model/useSizerWidth.js +14 -0
  18. package/dist/components/FilterInput/FilterInputField/FilterInputField.js +8 -16
  19. package/dist/components/FilterInput/FilterInputField/classes.d.ts +0 -2
  20. package/dist/components/FilterInput/FilterInputField/classes.js +3 -4
  21. package/dist/components/FilterInput/FilterInputMenu/FilterInputMenu.js +14 -20
  22. package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useChipEditing.d.ts +3 -1
  23. package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useChipEditing.js +20 -8
  24. package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useFilterInputAutocomplete.d.ts +1 -1
  25. package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useFilterInputAutocomplete.js +31 -1
  26. package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useFocusManagement.d.ts +3 -1
  27. package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useFocusManagement.js +3 -2
  28. package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useMenuFlow.d.ts +6 -2
  29. package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useMenuFlow.js +13 -5
  30. package/dist/components/FilterInput/hooks/useFilterInputExpression/buildChips.js +83 -77
  31. package/dist/components/FilterInput/hooks/useFilterInputExpression/useFilterInputExpression.d.ts +1 -1
  32. package/dist/components/FilterInput/hooks/useFilterInputExpression/useFilterInputExpression.js +61 -48
  33. package/dist/components/FilterInput/lib/index.d.ts +1 -0
  34. package/dist/components/FilterInput/lib/index.js +2 -1
  35. package/dist/components/FilterInput/lib/menuFilterText.d.ts +9 -0
  36. package/dist/components/FilterInput/lib/menuFilterText.js +13 -0
  37. package/dist/components/FilterInput/types.d.ts +2 -1
  38. package/dist/metadata/components.json +2 -2
  39. package/package.json +1 -1
  40. package/dist/components/FilterInput/FilterInputField/FilterInputChip/OperatorSegment.d.ts +0 -7
  41. package/dist/components/FilterInput/FilterInputField/FilterInputChip/OperatorSegment.js +0 -23
  42. package/dist/components/FilterInput/FilterInputField/FilterInputChip/ValueSegment.d.ts +0 -18
  43. package/dist/components/FilterInput/FilterInputField/FilterInputChip/ValueSegment.js +0 -47
  44. /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
- import { ValueSegment } from "./ValueSegment.js";
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 chipRef = useRef(null);
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 ? chipRef.current : e.currentTarget;
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: chipRef,
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(OperatorSegment, {
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
- children: operator
62
+ ...segmentEditProps('operator'),
63
+ children: operator ?? ''
56
64
  }),
57
- value && /*#__PURE__*/ jsx(ValueSegment, {
58
- className: "min-w-0 max-w-[400px]",
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, useState } from "react";
2
+ import { useEffect, useRef } from "react";
3
3
  import { cn } from "../../../../utils/cn.js";
4
4
  import { segmentContainer, segmentTextVariants } from "./classes.js";
5
- const Segment = ({ variant, children, className, error, editing, editText, onEditChange, onEditKeyDown, onEditBlur, ...props })=>{
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 ?? 8 * children.length;
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
- useEffect(()=>{
37
- if (!editing) return void setInputWidth(void 0);
38
- const sizerWidth = sizerRef.current?.getBoundingClientRect().width ?? (editText?.length ?? 0) * 8;
39
- setInputWidth(Math.max(20, lastTextWidthRef.current, sizerWidth));
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 border-none outline-none p-0 m-0'),
72
+ }), 'bg-transparent outline-none p-0 m-0'),
67
73
  style: {
68
- width: `${inputWidth ?? Math.max(20, lastTextWidthRef.current)}px`
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 p-2 leading-none";
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 pl-4 pr-0 py-0 border border-solid rounded-8', {
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 px-4 h-24',
10
- false: 'h-[22px]'
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 p-2 leading-none';
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-12 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', {
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;
@@ -0,0 +1,4 @@
1
+ const MIN_INPUT_WIDTH = 4;
2
+ const WIDTH_OFFSET = 1;
3
+ const CHAR_WIDTH_PX = 8;
4
+ export { CHAR_WIDTH_PX, MIN_INPUT_WIDTH, WIDTH_OFFSET };
@@ -1,5 +1,5 @@
1
1
  import { type FC, type FocusEvent, type KeyboardEvent, type ReactNode } from 'react';
2
- import type { ChipSegment } from './FilterInputChip';
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, buildingChipWrapperClass, filterInputContainerVariants, filterInputInnerVariants } from "./classes.js";
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__*/ jsxs("div", {
107
+ buildingChipData ? /*#__PURE__*/ jsx(FilterInputChip, {
108
108
  ref: buildingChipRef,
109
- className: buildingChipWrapperClass,
110
- children: [
111
- /*#__PURE__*/ jsx(FilterInputChip, {
112
- building: true,
113
- attribute: buildingChipData.attribute ?? '',
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 = 24;
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: 'mx-4',
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, buildingChipWrapperClass, filterInputContainerVariants, filterInputInnerVariants, filterInputInputVariants };
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 ? '' : inputText;
10
+ const operatorFilterText = 'operator' === editingSegment ? segmentMenuFilterText : inputText;
11
11
  const selectedFieldValues = selectedField ? getFieldValues(selectedField) : [];
12
- const valueFilterText = (()=>{
13
- if ('value' !== editingSegment) return inputText;
14
- if (!isMultiSelectOperator(selectedOperator)) return segmentMenuFilterText;
15
- const lastToken = segmentMenuFilterText.split(',').pop()?.trim() ?? '';
16
- if (!lastToken) return '';
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: ()=>handleMenuClose(),
24
+ onOpenChange: handleMenuClose,
31
25
  onEscape: handleMenuDiscard,
32
26
  positioning: menuPositioning,
33
27
  inputRef: inputRef,
34
28
  menuRef: menuRef
35
29
  }),
36
- selectedField && /*#__PURE__*/ jsx(FilterInputOperatorMenu, {
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: ()=>handleMenuClose(),
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
- selectedField && selectedOperator && ('date' === selectedField.type ? /*#__PURE__*/ jsx(FilterInputDateValueMenu, {
42
+ showValueMenu && (isDateField ? /*#__PURE__*/ jsx(FilterInputDateValueMenu, {
49
43
  open: 'value' === menuState,
50
44
  onSelect: handleValueSelect,
51
45
  onRangeSelect: handleRangeSelect,
52
- onOpenChange: ()=>handleMenuClose(),
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
- }) : selectedFieldValues.length > 0 && /*#__PURE__*/ jsx(FilterInputValueMenu, {
54
+ }) : hasValueOptions && /*#__PURE__*/ jsx(FilterInputValueMenu, {
61
55
  values: selectedFieldValues,
62
56
  open: 'value' === menuState,
63
57
  onSelect: handleValueSelect,
64
58
  onCommit: handleMultiCommit,
65
- onOpenChange: ()=>handleMenuClose(),
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;