@wallarm-org/design-system 0.66.3 → 0.67.1

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 (39) hide show
  1. package/dist/components/FilterInput/FilterInputContext/types.d.ts +6 -0
  2. package/dist/components/FilterInput/FilterInputContext/useFilterInputContextValue.d.ts +2 -0
  3. package/dist/components/FilterInput/FilterInputContext/useFilterInputContextValue.js +4 -0
  4. package/dist/components/FilterInput/FilterInputErrors/parseFilterInputErrors.js +11 -2
  5. package/dist/components/FilterInput/FilterInputField/ChipsWithGaps.js +4 -2
  6. package/dist/components/FilterInput/FilterInputField/FilterInputChip/FilterInputChip.d.ts +7 -1
  7. package/dist/components/FilterInput/FilterInputField/FilterInputChip/FilterInputChip.js +44 -6
  8. package/dist/components/FilterInput/FilterInputField/FilterInputChip/PairSeparator.d.ts +3 -0
  9. package/dist/components/FilterInput/FilterInputField/FilterInputChip/PairSeparator.js +16 -0
  10. package/dist/components/FilterInput/FilterInputField/FilterInputChip/context/EditingContext.d.ts +2 -0
  11. package/dist/components/FilterInput/FilterInputField/FilterInputChip/index.d.ts +3 -2
  12. package/dist/components/FilterInput/FilterInputField/FilterInputChip/index.js +3 -2
  13. package/dist/components/FilterInput/FilterInputField/FilterInputChip/segmentVariant.d.ts +2 -0
  14. package/dist/components/FilterInput/FilterInputField/FilterInputChip/segmentVariant.js +2 -1
  15. package/dist/components/FilterInput/FilterInputField/FilterInputField.js +3 -1
  16. package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/lib/deriveAutocompleteValues.d.ts +8 -1
  17. package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/lib/deriveAutocompleteValues.js +22 -3
  18. package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useAutocompleteState.d.ts +10 -0
  19. package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useAutocompleteState.js +6 -0
  20. package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useChipEditing.d.ts +2 -0
  21. package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useChipEditing.js +31 -0
  22. package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useFilterInputAutocomplete.d.ts +2 -0
  23. package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useFilterInputAutocomplete.js +14 -3
  24. package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useMenuFlow/types.d.ts +9 -0
  25. package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useMenuFlow/useOperatorFlow.d.ts +1 -1
  26. package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useMenuFlow/useOperatorFlow.js +22 -7
  27. package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useMenuFlow/useValueFlow.d.ts +1 -1
  28. package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useMenuFlow/useValueFlow.js +77 -13
  29. package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useResetState.d.ts +3 -1
  30. package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useResetState.js +5 -1
  31. package/dist/components/FilterInput/hooks/useFilterInputExpression/buildChips.js +26 -1
  32. package/dist/components/FilterInput/hooks/useFilterInputExpression/expression.d.ts +9 -3
  33. package/dist/components/FilterInput/hooks/useFilterInputExpression/expression.js +75 -9
  34. package/dist/components/FilterInput/hooks/useFilterInputExpression/useFilterInputExpression.d.ts +1 -1
  35. package/dist/components/FilterInput/hooks/useFilterInputExpression/useFilterInputExpression.js +99 -71
  36. package/dist/components/FilterInput/lib/constants.js +2 -2
  37. package/dist/components/FilterInput/types.d.ts +25 -1
  38. package/dist/metadata/components.json +8 -2
  39. package/package.json +1 -1
@@ -1,4 +1,4 @@
1
- import { useCallback, useEffect, useMemo, useState } from "react";
1
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
2
2
  import { SEGMENT_VARIANT } from "../../FilterInputField/FilterInputChip/index.js";
3
3
  import { CONNECTOR_ID_PATTERN, chipIdToConditionIndex, validateValueForField } from "../../lib/index.js";
4
4
  import { applyExternalErrors } from "./applyExternalErrors.js";
@@ -94,13 +94,24 @@ const addConnectorIfNeeded = (connectors, newConditionsLength, editingChipId, at
94
94
  };
95
95
  const useFilterInputExpression = ({ fields, value, onChange, error, externalErrors })=>{
96
96
  const [state, setState] = useState(EMPTY_STATE);
97
+ const stateRef = useRef(state);
98
+ const applyState = useCallback((next, emit)=>{
99
+ stateRef.current = next;
100
+ setState(next);
101
+ if (emit) onChange?.(buildExpression(next.conditions, next.connectors, fields));
102
+ }, [
103
+ onChange,
104
+ fields
105
+ ]);
97
106
  useEffect(()=>{
98
107
  if (void 0 !== value) {
99
- const result = expressionToConditions(value);
100
- setState({
108
+ const result = expressionToConditions(value, fields);
109
+ const next = {
101
110
  conditions: revalidateConditions(result.conditions, fields),
102
111
  connectors: result.connectors
103
- });
112
+ };
113
+ stateRef.current = next;
114
+ setState(next);
104
115
  }
105
116
  }, [
106
117
  value,
@@ -113,100 +124,117 @@ const useFilterInputExpression = ({ fields, value, onChange, error, externalErro
113
124
  error,
114
125
  externalErrors
115
126
  ]);
116
- const upsertCondition = useCallback((field, operator, val, editingChipId, atIndex, error, dateOrigin)=>{
117
- const condition = buildCondition(field, operator, val, error, dateOrigin);
118
- setState((prev)=>{
119
- const newConditions = applyCondition(prev.conditions, condition, editingChipId, atIndex);
120
- const newConnectors = addConnectorIfNeeded(prev.connectors, newConditions.length, editingChipId, atIndex, prev.conditions.length);
121
- const next = {
122
- conditions: newConditions,
123
- connectors: newConnectors
127
+ const upsertCondition = useCallback((field, operator, val, editingChipId, atIndex, error, dateOrigin, side)=>{
128
+ const prev = stateRef.current;
129
+ if (1 === side) {
130
+ const idx = null != editingChipId ? chipIdToConditionIndex(editingChipId) : prev.conditions.length - 1;
131
+ if (null == idx || idx < 0 || idx >= prev.conditions.length) return;
132
+ const base = prev.conditions[idx];
133
+ const updated = [
134
+ ...prev.conditions
135
+ ];
136
+ updated[idx] = {
137
+ ...base,
138
+ pair: {
139
+ ...operator && {
140
+ operator
141
+ },
142
+ value: val,
143
+ ...error && {
144
+ error
145
+ },
146
+ ...dateOrigin && {
147
+ dateOrigin
148
+ }
149
+ }
124
150
  };
125
- onChange?.(buildExpression(next.conditions, next.connectors));
126
- return next;
127
- });
151
+ applyState({
152
+ conditions: updated,
153
+ connectors: prev.connectors
154
+ }, true);
155
+ return;
156
+ }
157
+ const condition = buildCondition(field, operator, val, error, dateOrigin);
158
+ const newConditions = applyCondition(prev.conditions, condition, editingChipId, atIndex);
159
+ const newConnectors = addConnectorIfNeeded(prev.connectors, newConditions.length, editingChipId, atIndex, prev.conditions.length);
160
+ applyState({
161
+ conditions: newConditions,
162
+ connectors: newConnectors
163
+ }, true);
128
164
  }, [
129
- onChange
165
+ applyState
130
166
  ]);
131
167
  const removeCondition = useCallback((chipId)=>{
132
168
  const idx = chipIdToConditionIndex(chipId);
133
169
  if (null === idx) return;
134
- setState((prev)=>{
135
- if (prev.conditions[idx]?.disabled) return prev;
136
- const newConditions = prev.conditions.filter((_, i)=>i !== idx);
137
- const newConnectors = removeConnectorAtConditionIndex(prev.connectors, idx);
138
- const next = {
139
- conditions: newConditions,
140
- connectors: newConnectors
141
- };
142
- onChange?.(buildExpression(next.conditions, next.connectors));
143
- return next;
144
- });
170
+ const prev = stateRef.current;
171
+ if (prev.conditions[idx]?.disabled) return;
172
+ const newConditions = prev.conditions.filter((_, i)=>i !== idx);
173
+ const newConnectors = removeConnectorAtConditionIndex(prev.connectors, idx);
174
+ applyState({
175
+ conditions: newConditions,
176
+ connectors: newConnectors
177
+ }, true);
145
178
  }, [
146
- onChange
179
+ applyState
147
180
  ]);
148
181
  const removeConditionAtIndex = useCallback((idx)=>{
149
- setState((prev)=>{
150
- if (idx < 0 || idx >= prev.conditions.length) return prev;
151
- if (prev.conditions[idx]?.disabled) return prev;
152
- const newConditions = prev.conditions.filter((_, i)=>i !== idx);
153
- const newConnectors = removeConnectorAtConditionIndex(prev.connectors, idx);
154
- const next = {
155
- conditions: newConditions,
156
- connectors: newConnectors
157
- };
158
- onChange?.(buildExpression(next.conditions, next.connectors));
159
- return next;
160
- });
182
+ const prev = stateRef.current;
183
+ if (idx < 0 || idx >= prev.conditions.length) return;
184
+ if (prev.conditions[idx]?.disabled) return;
185
+ const newConditions = prev.conditions.filter((_, i)=>i !== idx);
186
+ const newConnectors = removeConnectorAtConditionIndex(prev.connectors, idx);
187
+ applyState({
188
+ conditions: newConditions,
189
+ connectors: newConnectors
190
+ }, true);
161
191
  }, [
162
- onChange
192
+ applyState
163
193
  ]);
164
194
  const clearAll = useCallback(()=>{
165
- setState((prev)=>{
166
- const disabledConditions = prev.conditions.filter((c)=>c.disabled);
167
- if (0 === disabledConditions.length) {
168
- onChange?.(null);
169
- return EMPTY_STATE;
170
- }
171
- const next = {
172
- conditions: disabledConditions,
173
- connectors: []
174
- };
175
- onChange?.(buildExpression(next.conditions, next.connectors));
176
- return next;
177
- });
195
+ const prev = stateRef.current;
196
+ const disabledConditions = prev.conditions.filter((c)=>c.disabled);
197
+ if (0 === disabledConditions.length) {
198
+ applyState(EMPTY_STATE, false);
199
+ onChange?.(null);
200
+ return;
201
+ }
202
+ applyState({
203
+ conditions: disabledConditions,
204
+ connectors: []
205
+ }, true);
178
206
  }, [
207
+ applyState,
179
208
  onChange
180
209
  ]);
181
210
  const replaceExpression = useCallback((expr)=>{
182
- const result = expressionToConditions(expr);
183
- setState({
211
+ const result = expressionToConditions(expr, fields);
212
+ applyState({
184
213
  conditions: result.conditions,
185
214
  connectors: result.connectors
186
- });
215
+ }, false);
187
216
  onChange?.(expr);
188
217
  }, [
189
- onChange
218
+ applyState,
219
+ onChange,
220
+ fields
190
221
  ]);
191
222
  const setConnectorValue = useCallback((connectorId, value)=>{
192
223
  const match = connectorId.match(CONNECTOR_ID_PATTERN);
193
224
  if (!match) return;
194
225
  const condIdx = Number(match[1]);
195
226
  const connectorIdx = condIdx - 1;
196
- setState((prev)=>{
197
- const updated = [
198
- ...prev.connectors
199
- ];
200
- if (connectorIdx >= 0 && connectorIdx < updated.length) updated[connectorIdx] = value;
201
- const next = {
202
- conditions: prev.conditions,
203
- connectors: updated
204
- };
205
- onChange?.(buildExpression(next.conditions, next.connectors));
206
- return next;
207
- });
227
+ const prev = stateRef.current;
228
+ const updated = [
229
+ ...prev.connectors
230
+ ];
231
+ if (connectorIdx >= 0 && connectorIdx < updated.length) updated[connectorIdx] = value;
232
+ applyState({
233
+ conditions: prev.conditions,
234
+ connectors: updated
235
+ }, true);
208
236
  }, [
209
- onChange
237
+ applyState
210
238
  ]);
211
239
  return {
212
240
  conditions: state.conditions,
@@ -28,8 +28,8 @@ const OPERATOR_SYMBOLS = {
28
28
  not_like: '!~',
29
29
  in: 'IN',
30
30
  not_in: 'NOT IN',
31
- is_null: '= null',
32
- is_not_null: '!= null',
31
+ is_null: '!= null',
32
+ is_not_null: '= null',
33
33
  between: '<>'
34
34
  };
35
35
  const OPERATOR_LABELS = {
@@ -14,7 +14,10 @@ export type ChipErrorSegment = boolean | 'attribute' | 'value';
14
14
  * Re-declared in several option interfaces — exported here to keep the source of
15
15
  * truth single-rooted (changes to the signature reach all consumers).
16
16
  */
17
- export type UpsertCondition = (field: FieldMetadata, operator: FilterOperator | undefined, val: string | number | boolean | null | Array<string | number | boolean>, editingChipId?: string | null, atIndex?: number, error?: ChipErrorSegment, dateOrigin?: 'relative' | 'absolute') => void;
17
+ export type UpsertCondition = (field: FieldMetadata, operator: FilterOperator | undefined, val: string | number | boolean | null | Array<string | number | boolean>, editingChipId?: string | null, atIndex?: number, error?: ChipErrorSegment, dateOrigin?: 'relative' | 'absolute',
18
+ /** When 1, write the paired (second) triplet onto the target condition instead
19
+ * of replacing the base triplet. Defaults to 0 (base triplet). */
20
+ side?: 0 | 1) => void;
18
21
  export interface FilterInputChipData {
19
22
  id: string;
20
23
  variant: FilterInputChipVariant;
@@ -30,6 +33,13 @@ export interface FilterInputChipData {
30
33
  errorValueIndices?: number[];
31
34
  /** When true, the chip cannot be edited or removed */
32
35
  disabled?: boolean;
36
+ /** Second paired triplet (display) for two-step fields. */
37
+ pair?: {
38
+ attribute: string;
39
+ operator?: string;
40
+ value?: string;
41
+ error?: ChipErrorSegment;
42
+ };
33
43
  }
34
44
  /**
35
45
  * Field Type for filter attributes
@@ -120,6 +130,13 @@ export interface FieldMetadata {
120
130
  * emitting the query. Display in the chip is unaffected.
121
131
  */
122
132
  serializeValue?: (value: string | number | boolean) => string | number | boolean;
133
+ /**
134
+ * When set, this field is a two-step paired field: the chip holds a second
135
+ * attribute/operator/value triplet. The paired segment is a full field
136
+ * definition (label, type, operators, values, validate, …). Nesting is one
137
+ * level only — a pairedField's own `pairedField` is ignored.
138
+ */
139
+ pairedField?: FieldMetadata;
123
140
  }
124
141
  /**
125
142
  * Expression Tree Types
@@ -139,6 +156,13 @@ export interface Condition {
139
156
  dateOrigin?: 'relative' | 'absolute';
140
157
  /** When true, the condition cannot be edited or removed */
141
158
  disabled?: boolean;
159
+ /** Second paired triplet. Present only for fields with `pairedField`. */
160
+ pair?: {
161
+ operator?: FilterOperator;
162
+ value: string | number | boolean | null | Array<string | number | boolean>;
163
+ error?: ChipErrorSegment;
164
+ dateOrigin?: 'relative' | 'absolute';
165
+ };
142
166
  }
143
167
  /**
144
168
  * Group Node - Represents a grouped expression with AND/OR logic
@@ -1,6 +1,6 @@
1
1
  {
2
- "version": "0.66.2",
3
- "generatedAt": "2026-06-23T09:25:43.087Z",
2
+ "version": "0.67.0",
3
+ "generatedAt": "2026-06-24T17:08:52.641Z",
4
4
  "components": [
5
5
  {
6
6
  "name": "Accordion",
@@ -29593,6 +29593,12 @@
29593
29593
  "required": false,
29594
29594
  "description": "When true, the chip cannot be edited or removed (dimmed appearance)"
29595
29595
  },
29596
+ {
29597
+ "name": "pair",
29598
+ "type": "complex",
29599
+ "required": false,
29600
+ "description": "Second paired triplet (two-step fields). The paired attribute is fixed."
29601
+ },
29596
29602
  {
29597
29603
  "name": "defaultChecked",
29598
29604
  "type": "boolean | undefined",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wallarm-org/design-system",
3
- "version": "0.66.3",
3
+ "version": "0.67.1",
4
4
  "description": "Core design system library with React components and Storybook documentation",
5
5
  "publishConfig": {
6
6
  "access": "public",