@wallarm-org/design-system 0.26.0 → 0.27.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.
Files changed (59) hide show
  1. package/dist/components/Badge/index.d.ts +1 -0
  2. package/dist/components/FilterInput/FilterInputErrors/parseFilterInputErrors.js +11 -0
  3. package/dist/components/FilterInput/FilterInputMenu/FilterInputMenu.d.ts +2 -1
  4. package/dist/components/FilterInput/FilterInputMenu/FilterInputMenu.js +18 -5
  5. package/dist/components/FilterInput/FilterInputMenu/FilterInputValueMenu/FilterInputValueMenu.d.ts +5 -1
  6. package/dist/components/FilterInput/FilterInputMenu/FilterInputValueMenu/FilterInputValueMenu.js +13 -84
  7. package/dist/components/FilterInput/FilterInputMenu/FilterInputValueMenu/ValueMenuFooter.d.ts +6 -0
  8. package/dist/components/FilterInput/FilterInputMenu/FilterInputValueMenu/ValueMenuFooter.js +72 -0
  9. package/dist/components/FilterInput/FilterInputMenu/FilterInputValueMenu/ValueMenuItem.js +6 -9
  10. package/dist/components/FilterInput/FilterInputMenu/FilterInputValueMenu/useValueMenuDisplayValues.d.ts +31 -0
  11. package/dist/components/FilterInput/FilterInputMenu/FilterInputValueMenu/useValueMenuDisplayValues.js +39 -0
  12. package/dist/components/FilterInput/FilterInputMenu/FilterInputValueMenu/useValueMenuState.d.ts +6 -1
  13. package/dist/components/FilterInput/FilterInputMenu/FilterInputValueMenu/useValueMenuState.js +5 -3
  14. package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useChipEditing.d.ts +1 -0
  15. package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useChipEditing.js +5 -0
  16. package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useFilterInputAutocomplete.d.ts +1 -0
  17. package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useFilterInputAutocomplete.js +8 -3
  18. package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useInputHandlers.js +7 -2
  19. package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useMenuFlow.d.ts +2 -0
  20. package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useMenuFlow.js +8 -0
  21. package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/valueCommitHelpers.js +13 -2
  22. package/dist/components/FilterInput/index.d.ts +1 -1
  23. package/dist/components/FilterInput/index.js +2 -2
  24. package/dist/components/FilterInput/lib/applyAcceptChar.d.ts +7 -0
  25. package/dist/components/FilterInput/lib/applyAcceptChar.js +5 -0
  26. package/dist/components/FilterInput/lib/fields.d.ts +8 -2
  27. package/dist/components/FilterInput/lib/fields.js +2 -2
  28. package/dist/components/FilterInput/lib/index.d.ts +3 -2
  29. package/dist/components/FilterInput/lib/index.js +4 -3
  30. package/dist/components/FilterInput/lib/menuFilterText.d.ts +19 -4
  31. package/dist/components/FilterInput/lib/menuFilterText.js +11 -8
  32. package/dist/components/FilterInput/lib/statusCode/createStatusCodeInputFilter.d.ts +6 -0
  33. package/dist/components/FilterInput/lib/statusCode/createStatusCodeInputFilter.js +2 -0
  34. package/dist/components/FilterInput/lib/statusCode/createStatusCodeNormalizer.d.ts +10 -0
  35. package/dist/components/FilterInput/lib/statusCode/createStatusCodeNormalizer.js +9 -0
  36. package/dist/components/FilterInput/lib/statusCode/createStatusCodeSuggestions.d.ts +16 -0
  37. package/dist/components/FilterInput/lib/statusCode/createStatusCodeSuggestions.js +21 -0
  38. package/dist/components/FilterInput/lib/statusCode/createStatusCodeValidator.d.ts +19 -0
  39. package/dist/components/FilterInput/lib/statusCode/createStatusCodeValidator.js +8 -0
  40. package/dist/components/FilterInput/lib/statusCode/index.d.ts +5 -0
  41. package/dist/components/FilterInput/lib/statusCode/index.js +5 -0
  42. package/dist/components/FilterInput/lib/statusCode/utils/canonicalize.d.ts +20 -0
  43. package/dist/components/FilterInput/lib/statusCode/utils/canonicalize.js +19 -0
  44. package/dist/components/FilterInput/lib/statusCode/utils/constants.d.ts +14 -0
  45. package/dist/components/FilterInput/lib/statusCode/utils/constants.js +17 -0
  46. package/dist/components/FilterInput/lib/statusCode/utils/index.d.ts +5 -0
  47. package/dist/components/FilterInput/lib/statusCode/utils/index.js +5 -0
  48. package/dist/components/FilterInput/lib/statusCode/utils/makeMask.d.ts +7 -0
  49. package/dist/components/FilterInput/lib/statusCode/utils/makeMask.js +14 -0
  50. package/dist/components/FilterInput/lib/statusCode/utils/maskRoots.d.ts +4 -0
  51. package/dist/components/FilterInput/lib/statusCode/utils/maskRoots.js +3 -0
  52. package/dist/components/FilterInput/lib/statusCode/utils/types.d.ts +8 -0
  53. package/dist/components/FilterInput/lib/statusCode/utils/types.js +0 -0
  54. package/dist/components/FilterInput/stories/mockStatusCodes.js +2 -0
  55. package/dist/components/FilterInput/types.d.ts +32 -2
  56. package/dist/metadata/components.json +2 -2
  57. package/package.json +1 -1
  58. package/dist/components/FilterInput/lib/statusCodeSuggestions.d.ts +0 -10
  59. package/dist/components/FilterInput/lib/statusCodeSuggestions.js +0 -45
@@ -1,5 +1,5 @@
1
1
  import { FilterInput } from "./FilterInput.js";
2
2
  import { FilterInputChip } from "./FilterInputField/index.js";
3
3
  import { FilterInputFieldMenu, FilterInputOperatorMenu, FilterInputValueMenu } from "./FilterInputMenu/index.js";
4
- import { createStatusCodeSuggestions, isFilterParseError, parseExpression, serializeExpression } from "./lib/index.js";
5
- export { FilterInput, FilterInputChip, FilterInputFieldMenu, FilterInputOperatorMenu, FilterInputValueMenu, createStatusCodeSuggestions, isFilterParseError, parseExpression, serializeExpression };
4
+ import { createStatusCodeInputFilter, createStatusCodeNormalizer, createStatusCodeSuggestions, createStatusCodeValidator, isFilterParseError, parseExpression, serializeExpression } from "./lib/index.js";
5
+ export { FilterInput, FilterInputChip, FilterInputFieldMenu, FilterInputOperatorMenu, FilterInputValueMenu, createStatusCodeInputFilter, createStatusCodeNormalizer, createStatusCodeSuggestions, createStatusCodeValidator, isFilterParseError, parseExpression, serializeExpression };
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Strip characters from `text` that fail the field's `acceptChar` predicate.
3
+ * Commas and spaces always pass through so multi-select delimiters keep
4
+ * working. Returns the original string unchanged when no filtering was
5
+ * needed, so callers can cheaply compare identity to detect a no-op.
6
+ */
7
+ export declare const applyAcceptChar: (text: string, acceptChar: (c: string) => boolean) => string;
@@ -0,0 +1,5 @@
1
+ const applyAcceptChar = (text, acceptChar)=>{
2
+ const filtered = Array.from(text).filter((c)=>',' === c || ' ' === c || acceptChar(c)).join('');
3
+ return filtered === text ? text : filtered;
4
+ };
5
+ export { applyAcceptChar };
@@ -1,9 +1,15 @@
1
1
  import type { FieldMetadata, FieldValueOption } from '../types';
2
2
  /**
3
3
  * Get value options for a field.
4
- * Priority: `getSuggestions(inputText)` > `values` > `options` (converted to `{value, label}`).
4
+ * Priority: `getSuggestions(inputText, context)` > `values` > `options` (converted to `{value, label}`).
5
+ *
6
+ * `context.selectedValues` is forwarded to `getSuggestions` so helpers can
7
+ * include the currently-committed values in their output (e.g. to preserve a
8
+ * badge style on a concrete code once suggestions have narrowed to masks).
5
9
  */
6
- export declare const getFieldValues: (field: FieldMetadata, inputText?: string) => FieldValueOption[];
10
+ export declare const getFieldValues: (field: FieldMetadata, inputText?: string, context?: {
11
+ selectedValues?: Array<string | number | boolean>;
12
+ }) => FieldValueOption[];
7
13
  /**
8
14
  * Check whether a field has a source of value suggestions — dynamic callback, static
9
15
  * `values`, or `options`. Used to decide whether to render a value dropdown at all.
@@ -1,5 +1,5 @@
1
- const getFieldValues = (field, inputText = '')=>{
2
- if (field.getSuggestions) return field.getSuggestions(inputText);
1
+ const getFieldValues = (field, inputText = '', context)=>{
2
+ if (field.getSuggestions) return field.getSuggestions(inputText, context);
3
3
  const fromValues = field.values ?? [];
4
4
  if (fromValues.length > 0) return fromValues;
5
5
  return field.options?.map((s)=>({
@@ -1,12 +1,13 @@
1
1
  export type { DatePreset } from '../FilterInputMenu/FilterInputDateValueMenu/constants';
2
2
  export { DATE_PRESETS, formatDateForChip, getDateDisplayLabel, isDatePreset, } from '../FilterInputMenu/FilterInputDateValueMenu/constants';
3
+ export { applyAcceptChar } from './applyAcceptChar';
3
4
  export { chipIdToConditionIndex, findChipSplitIndex } from './conditions';
4
5
  export { CONNECTOR_ID_PATTERN, NO_VALUE_OPERATORS, OPERATOR_LABELS, OPERATOR_LABELS_BY_TYPE, OPERATOR_SYMBOLS, OPERATORS_BY_TYPE, QUERY_BAR_SELECTOR, VARIANT_LABELS, } from './constants';
5
6
  export { buildContainerAnchoredRect, isMenuRelated } from './dom';
6
7
  export { getFieldValues, hasFieldValues, hasStaticAllowlist } from './fields';
7
8
  export { filterAndSort } from './filterSort';
8
- export { getValueFilterText } from './menuFilterText';
9
+ export { getCurrentValueTokenText, getValueFilterText } from './menuFilterText';
9
10
  export { getOperatorFromLabel, getOperatorLabel, isBetweenOperator, isMultiSelectOperator, isNoValueOperator, } from './operators';
10
11
  export { type FilterParseError, isFilterParseError, parseExpression } from './parseExpression';
11
12
  export { serializeExpression } from './serializeExpression';
12
- export { createStatusCodeSuggestions, type StatusCodeSuggestionsOptions, } from './statusCodeSuggestions';
13
+ export { createStatusCodeInputFilter, createStatusCodeNormalizer, createStatusCodeSuggestions, createStatusCodeValidator, type StatusCodeSuggestionsOptions, } from './statusCode';
@@ -1,12 +1,13 @@
1
1
  import { DATE_PRESETS, formatDateForChip, getDateDisplayLabel, isDatePreset } from "../FilterInputMenu/FilterInputDateValueMenu/constants.js";
2
+ import { applyAcceptChar } from "./applyAcceptChar.js";
2
3
  import { chipIdToConditionIndex, findChipSplitIndex } from "./conditions.js";
3
4
  import { CONNECTOR_ID_PATTERN, NO_VALUE_OPERATORS, OPERATORS_BY_TYPE, OPERATOR_LABELS, OPERATOR_LABELS_BY_TYPE, OPERATOR_SYMBOLS, QUERY_BAR_SELECTOR, VARIANT_LABELS } from "./constants.js";
4
5
  import { buildContainerAnchoredRect, isMenuRelated } from "./dom.js";
5
6
  import { getFieldValues, hasFieldValues, hasStaticAllowlist } from "./fields.js";
6
7
  import { filterAndSort } from "./filterSort.js";
7
- import { getValueFilterText } from "./menuFilterText.js";
8
+ import { getCurrentValueTokenText, getValueFilterText } from "./menuFilterText.js";
8
9
  import { getOperatorFromLabel, getOperatorLabel, isBetweenOperator, isMultiSelectOperator, isNoValueOperator } from "./operators.js";
9
10
  import { isFilterParseError, parseExpression } from "./parseExpression/index.js";
10
11
  import { serializeExpression } from "./serializeExpression.js";
11
- import { createStatusCodeSuggestions } from "./statusCodeSuggestions.js";
12
- export { CONNECTOR_ID_PATTERN, DATE_PRESETS, NO_VALUE_OPERATORS, OPERATORS_BY_TYPE, OPERATOR_LABELS, OPERATOR_LABELS_BY_TYPE, OPERATOR_SYMBOLS, QUERY_BAR_SELECTOR, VARIANT_LABELS, buildContainerAnchoredRect, chipIdToConditionIndex, createStatusCodeSuggestions, filterAndSort, findChipSplitIndex, formatDateForChip, getDateDisplayLabel, getFieldValues, getOperatorFromLabel, getOperatorLabel, getValueFilterText, hasFieldValues, hasStaticAllowlist, isBetweenOperator, isDatePreset, isFilterParseError, isMenuRelated, isMultiSelectOperator, isNoValueOperator, parseExpression, serializeExpression };
12
+ import { createStatusCodeInputFilter, createStatusCodeNormalizer, createStatusCodeSuggestions, createStatusCodeValidator } from "./statusCode/index.js";
13
+ export { CONNECTOR_ID_PATTERN, DATE_PRESETS, NO_VALUE_OPERATORS, OPERATORS_BY_TYPE, OPERATOR_LABELS, OPERATOR_LABELS_BY_TYPE, OPERATOR_SYMBOLS, QUERY_BAR_SELECTOR, VARIANT_LABELS, applyAcceptChar, buildContainerAnchoredRect, chipIdToConditionIndex, createStatusCodeInputFilter, createStatusCodeNormalizer, createStatusCodeSuggestions, createStatusCodeValidator, filterAndSort, findChipSplitIndex, formatDateForChip, getCurrentValueTokenText, getDateDisplayLabel, getFieldValues, getOperatorFromLabel, getOperatorLabel, getValueFilterText, hasFieldValues, hasStaticAllowlist, isBetweenOperator, isDatePreset, isFilterParseError, isMenuRelated, isMultiSelectOperator, isNoValueOperator, parseExpression, serializeExpression };
@@ -1,9 +1,24 @@
1
1
  import type { FieldValueOption, FilterOperator } from '../types';
2
+ /**
3
+ * Derive the "current value token" — the substring the user is actively typing.
4
+ *
5
+ * For multi-select operators (like `in` / `any of`), the field input may contain
6
+ * a comma-separated list such as `"2XX, 3XX, 4"`. Only the last token (`"4"`)
7
+ * represents the value currently being composed; prior tokens are already
8
+ * committed as checked items. Callers use this to feed both `getSuggestions`
9
+ * and the dropdown's filter/sort so the active token drives the menu.
10
+ *
11
+ * For single-value operators the raw input is returned unchanged.
12
+ */
13
+ export declare const getCurrentValueTokenText: (editingSegment: string | null, inputText: string, segmentMenuFilterText: string, selectedOperator: FilterOperator | null) => string;
2
14
  /**
3
15
  * Derive the filter text for the value menu.
4
16
  *
5
- * For multi-select operators, extracts the last comma-separated token
6
- * (the one the user is currently typing) and uses it for filtering.
7
- * If that token already matches a known value, returns '' to show all options.
17
+ * For multi-select operators, uses the current value token (the one the user
18
+ * is actively typing). If that token already matches a known value, returns
19
+ * `''` so the dropdown shows all options the user just finished a selection
20
+ * and is about to start the next one.
21
+ *
22
+ * For single-value operators, returns the token as-is.
8
23
  */
9
- export declare const getValueFilterText: (editingSegment: string | null, inputText: string, segmentMenuFilterText: string, selectedOperator: FilterOperator | null, fieldValues: FieldValueOption[]) => string;
24
+ export declare const getValueFilterText: (currentTokenText: string, selectedOperator: FilterOperator | null, fieldValues: FieldValueOption[]) => string;
@@ -1,13 +1,16 @@
1
1
  import { isMultiSelectOperator } from "./operators.js";
2
- const getValueFilterText = (editingSegment, inputText, segmentMenuFilterText, selectedOperator, fieldValues)=>{
3
- if ('value' !== editingSegment) return inputText;
4
- if (!isMultiSelectOperator(selectedOperator)) return segmentMenuFilterText;
5
- const lastToken = segmentMenuFilterText.split(',').pop()?.trim() ?? '';
6
- if (!lastToken) return '';
2
+ const getCurrentValueTokenText = (editingSegment, inputText, segmentMenuFilterText, selectedOperator)=>{
3
+ const raw = 'value' === editingSegment ? segmentMenuFilterText : inputText;
4
+ if (!isMultiSelectOperator(selectedOperator)) return raw;
5
+ return raw.split(',').pop()?.trim() ?? '';
6
+ };
7
+ const getValueFilterText = (currentTokenText, selectedOperator, fieldValues)=>{
8
+ if (!isMultiSelectOperator(selectedOperator)) return currentTokenText;
9
+ if (!currentTokenText) return '';
7
10
  if (fieldValues.length > 0) {
8
- const isKnownValue = fieldValues.some((v)=>v.label.toLowerCase() === lastToken.toLowerCase() || String(v.value).toLowerCase() === lastToken.toLowerCase());
11
+ const isKnownValue = fieldValues.some((v)=>v.label.toLowerCase() === currentTokenText.toLowerCase() || String(v.value).toLowerCase() === currentTokenText.toLowerCase());
9
12
  if (isKnownValue) return '';
10
13
  }
11
- return lastToken;
14
+ return currentTokenText;
12
15
  };
13
- export { getValueFilterText };
16
+ export { getCurrentValueTokenText, getValueFilterText };
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Per-character input filter for the HTTP status code field. Accepts only
3
+ * digits and the `X` mask marker (case-insensitive). Meant to be wired to
4
+ * `FieldMetadata.acceptChar` so typing in the value segment is constrained.
5
+ */
6
+ export declare const createStatusCodeInputFilter: () => ((char: string) => boolean);
@@ -0,0 +1,2 @@
1
+ const createStatusCodeInputFilter = ()=>(c)=>/^[0-9xX]$/.test(c);
2
+ export { createStatusCodeInputFilter };
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Commit-time normalizer for the HTTP status code field. Pads partial input
3
+ * to three characters and uppercases the mask marker:
4
+ * - "2" → "2XX"
5
+ * - "22" → "22X"
6
+ * - "2x" → "2XX"
7
+ * - "222", "4XX", "40X" → unchanged (already 3 chars)
8
+ * Leaves anything unrecognised alone so `validate` can still flag it.
9
+ */
10
+ export declare const createStatusCodeNormalizer: () => ((value: string | number | boolean) => string | number | boolean);
@@ -0,0 +1,9 @@
1
+ const createStatusCodeNormalizer = ()=>(value)=>{
2
+ if ('string' != typeof value && 'number' != typeof value) return value;
3
+ const s = String(value).toUpperCase();
4
+ if (!/^\d[0-9X]{0,2}$/.test(s)) return value;
5
+ if (1 === s.length) return `${s}XX`;
6
+ if (2 === s.length) return `${s}X`;
7
+ return s;
8
+ };
9
+ export { createStatusCodeNormalizer };
@@ -0,0 +1,16 @@
1
+ import type { FieldValueOption } from '../../types';
2
+ import { type StatusCodeSuggestionsOptions } from './utils';
3
+ /**
4
+ * Build the `getSuggestions` callback for an HTTP status code field. The
5
+ * returned function:
6
+ * - reads the active `maskRoots` from `options.codes`
7
+ * - on empty input, offers every available class mask
8
+ * - on partial input (`"3"`, `"3X"`, `"30"`, …), expands it via
9
+ * `canonicalizeStatusCodeInput` and offers the single matching mask
10
+ * - merges any `context.selectedValues` (values already committed to the
11
+ * chip) in front of the input-driven output so they always appear with
12
+ * their canonical badge styling
13
+ */
14
+ export declare const createStatusCodeSuggestions: (options?: StatusCodeSuggestionsOptions) => ((inputText: string, context?: {
15
+ selectedValues?: Array<string | number | boolean>;
16
+ }) => FieldValueOption[]);
@@ -0,0 +1,21 @@
1
+ import { canonicalizeStatusCodeInput, getMaskRoots, makeMask } from "./utils/index.js";
2
+ const createStatusCodeSuggestions = (options)=>{
3
+ const maskRoots = getMaskRoots(options?.codes);
4
+ const resolveSelected = (selectedValues)=>(selectedValues ?? []).map((v)=>String(v)).filter((s)=>/^\d[\dX]{0,2}$/.test(s) && maskRoots.includes(s.charAt(0))).map((s)=>makeMask(s));
5
+ return (inputText, context)=>{
6
+ const norm = inputText.trim();
7
+ const selected = resolveSelected(context?.selectedValues);
8
+ const seen = new Set(selected.map((o)=>String(o.value)));
9
+ const merge = (primary)=>[
10
+ ...selected,
11
+ ...primary.filter((o)=>!seen.has(String(o.value)))
12
+ ];
13
+ if (0 === norm.length) return merge(maskRoots.map((d)=>makeMask(`${d}XX`)));
14
+ const mask = canonicalizeStatusCodeInput(norm, maskRoots);
15
+ if (!mask) return selected;
16
+ return merge([
17
+ makeMask(mask)
18
+ ]);
19
+ };
20
+ };
21
+ export { createStatusCodeSuggestions };
@@ -0,0 +1,19 @@
1
+ import { type StatusCodeSuggestionsOptions } from './utils';
2
+ /**
3
+ * Build a validator for the HTTP status code field. Accepts only 3-character
4
+ * values whose leading digit is a valid HTTP class (`[1..5]`) and whose
5
+ * remaining two characters form one of:
6
+ * - `XX` (mask like `4XX`)
7
+ * - `dX` (narrowed mask like `40X`)
8
+ * - `dd` (concrete code like `401`)
9
+ *
10
+ * Returns `true` when the value is invalid, matching the
11
+ * `FieldMetadata.validate` contract.
12
+ *
13
+ * The `options` argument is accepted for API symmetry with
14
+ * `createStatusCodeSuggestions` (same wiring on the field), but validity is
15
+ * bound to the static `[1..5]` HTTP class range rather than `codes` — the
16
+ * backend's available-data list doesn't narrow what the user may legitimately
17
+ * type.
18
+ */
19
+ export declare const createStatusCodeValidator: (_options?: StatusCodeSuggestionsOptions) => ((value: string | number | boolean) => boolean);
@@ -0,0 +1,8 @@
1
+ import { STATUS_CODE_LENGTH, VALID_MASK_ROOTS } from "./utils/index.js";
2
+ const createStatusCodeValidator = (_options)=>(value)=>{
3
+ const s = String(value);
4
+ if (s.length !== STATUS_CODE_LENGTH) return true;
5
+ if (!VALID_MASK_ROOTS.has(s.charAt(0))) return true;
6
+ return !/^(XX|\dX|\d\d)$/.test(s.slice(1));
7
+ };
8
+ export { createStatusCodeValidator };
@@ -0,0 +1,5 @@
1
+ export { createStatusCodeInputFilter } from './createStatusCodeInputFilter';
2
+ export { createStatusCodeNormalizer } from './createStatusCodeNormalizer';
3
+ export { createStatusCodeSuggestions } from './createStatusCodeSuggestions';
4
+ export { createStatusCodeValidator } from './createStatusCodeValidator';
5
+ export type { StatusCodeSuggestionsOptions } from './utils';
@@ -0,0 +1,5 @@
1
+ import { createStatusCodeInputFilter } from "./createStatusCodeInputFilter.js";
2
+ import { createStatusCodeNormalizer } from "./createStatusCodeNormalizer.js";
3
+ import { createStatusCodeSuggestions } from "./createStatusCodeSuggestions.js";
4
+ import { createStatusCodeValidator } from "./createStatusCodeValidator.js";
5
+ export { createStatusCodeInputFilter, createStatusCodeNormalizer, createStatusCodeSuggestions, createStatusCodeValidator };
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Normalize the user-typed input (up to 3 chars, digits + X, case-insensitive)
3
+ * into the canonical mask it represents. Returns `null` if the shape isn't a
4
+ * valid status-code fragment.
5
+ *
6
+ * Accepted inputs:
7
+ * "3" → "3XX" (single digit pads to full class mask)
8
+ * "3X" → "3XX" (partial mask)
9
+ * "3XX" → "3XX" (full mask)
10
+ * "30" → "30X" (two digits pad to narrowed mask)
11
+ * "30X" → "30X" (full narrowed mask)
12
+ * "301" → "301" (concrete code)
13
+ *
14
+ * Rejected:
15
+ * "3X0" (digit after placeholder)
16
+ * "X30" (starts with placeholder)
17
+ * "6..." (leading digit not in `maskRoots`)
18
+ * "ab..." (non-digit, non-X)
19
+ */
20
+ export declare const canonicalizeStatusCodeInput: (input: string, maskRoots: string[]) => string | null;
@@ -0,0 +1,19 @@
1
+ import { MASK_PLACEHOLDER, STATUS_CODE_LENGTH } from "./constants.js";
2
+ const canonicalizeStatusCodeInput = (input, maskRoots)=>{
3
+ const s = input.toUpperCase();
4
+ if (0 === s.length || s.length > STATUS_CODE_LENGTH) return null;
5
+ if (!/^[\dX]+$/.test(s)) return null;
6
+ const d1 = s.charAt(0);
7
+ if (d1 === MASK_PLACEHOLDER || !maskRoots.includes(d1)) return null;
8
+ if (1 === s.length) return `${d1}XX`;
9
+ const c2 = s.charAt(1);
10
+ if (c2 === MASK_PLACEHOLDER) {
11
+ if (3 === s.length && s.charAt(2) !== MASK_PLACEHOLDER) return null;
12
+ return `${d1}XX`;
13
+ }
14
+ if (2 === s.length) return `${d1}${c2}X`;
15
+ const c3 = s.charAt(2);
16
+ if (c3 === MASK_PLACEHOLDER) return `${d1}${c2}X`;
17
+ return `${d1}${c2}${c3}`;
18
+ };
19
+ export { canonicalizeStatusCodeInput };
@@ -0,0 +1,14 @@
1
+ import type { BadgeColor } from '../../../../Badge';
2
+ /** The five valid HTTP status-code classes, used both for suggestion-building
3
+ * and validation. Backed by the standard HTTP spec, independent of what the
4
+ * backend happens to carry. */
5
+ export declare const VALID_MASK_ROOTS: Set<string>;
6
+ /** Badge color per HTTP class, keyed by the leading digit. Derived from the
7
+ * AS-877 Figma designs: informational greys, success greens, redirect blues,
8
+ * client-error ambers, server-error reds. */
9
+ export declare const HTTP_CLASS_BADGE_COLOR: Record<string, BadgeColor>;
10
+ /** Fixed length of a status-code value (mask or concrete). Exposed as a
11
+ * constant so downstream helpers stay in sync. */
12
+ export declare const STATUS_CODE_LENGTH = 3;
13
+ /** Placeholder character used inside masks (e.g. the `X` in `4XX`). */
14
+ export declare const MASK_PLACEHOLDER = "X";
@@ -0,0 +1,17 @@
1
+ const VALID_MASK_ROOTS = new Set([
2
+ '1',
3
+ '2',
4
+ '3',
5
+ '4',
6
+ '5'
7
+ ]);
8
+ const HTTP_CLASS_BADGE_COLOR = {
9
+ 1: 'slate',
10
+ 2: 'green',
11
+ 3: 'blue',
12
+ 4: 'amber',
13
+ 5: 'red'
14
+ };
15
+ const STATUS_CODE_LENGTH = 3;
16
+ const MASK_PLACEHOLDER = 'X';
17
+ export { HTTP_CLASS_BADGE_COLOR, MASK_PLACEHOLDER, STATUS_CODE_LENGTH, VALID_MASK_ROOTS };
@@ -0,0 +1,5 @@
1
+ export { canonicalizeStatusCodeInput } from './canonicalize';
2
+ export { HTTP_CLASS_BADGE_COLOR, MASK_PLACEHOLDER, STATUS_CODE_LENGTH, VALID_MASK_ROOTS, } from './constants';
3
+ export { makeMask } from './makeMask';
4
+ export { getMaskRoots } from './maskRoots';
5
+ export type { StatusCodeSuggestionsOptions } from './types';
@@ -0,0 +1,5 @@
1
+ import { canonicalizeStatusCodeInput } from "./canonicalize.js";
2
+ import { HTTP_CLASS_BADGE_COLOR, MASK_PLACEHOLDER, STATUS_CODE_LENGTH, VALID_MASK_ROOTS } from "./constants.js";
3
+ import { makeMask } from "./makeMask.js";
4
+ import { getMaskRoots } from "./maskRoots.js";
5
+ export { HTTP_CLASS_BADGE_COLOR, MASK_PLACEHOLDER, STATUS_CODE_LENGTH, VALID_MASK_ROOTS, canonicalizeStatusCodeInput, getMaskRoots, makeMask };
@@ -0,0 +1,7 @@
1
+ import type { FieldValueOption } from '../../../types';
2
+ /** Build a `FieldValueOption` for a status-code mask or concrete code. Badge
3
+ * color is derived from the leading digit via `HTTP_CLASS_BADGE_COLOR`.
4
+ * Throws if the leading digit has no color mapping — this is unreachable in
5
+ * practice because callers gate on `maskRoots`, but the check is kept as a
6
+ * safety net so a future refactor can't silently emit a colorless pill. */
7
+ export declare const makeMask: (label: string) => FieldValueOption;
@@ -0,0 +1,14 @@
1
+ import { HTTP_CLASS_BADGE_COLOR } from "./constants.js";
2
+ const makeMask = (label)=>{
3
+ const color = HTTP_CLASS_BADGE_COLOR[label.charAt(0)];
4
+ if (!color) throw new Error(`statusCode: no badge color for mask "${label}"`);
5
+ return {
6
+ value: label,
7
+ label,
8
+ badge: {
9
+ color,
10
+ text: label
11
+ }
12
+ };
13
+ };
14
+ export { makeMask };
@@ -0,0 +1,4 @@
1
+ /** Extract the HTTP class digits (`1`..`5`) that actually appear as single-
2
+ * character entries in the backend `codes` list. These become the "available"
3
+ * class masks the helper is willing to emit. */
4
+ export declare const getMaskRoots: (codes: string[] | undefined) => string[];
@@ -0,0 +1,3 @@
1
+ import { VALID_MASK_ROOTS } from "./constants.js";
2
+ const getMaskRoots = (codes)=>(codes ?? []).filter((c)=>1 === c.length && VALID_MASK_ROOTS.has(c));
3
+ export { getMaskRoots };
@@ -0,0 +1,8 @@
1
+ export interface StatusCodeSuggestionsOptions {
2
+ /**
3
+ * Raw backend status-code list. The helper keeps only 1-char entries in
4
+ * `[1..5]` as mask roots; everything else is ignored. When omitted or
5
+ * empty, the helper returns `[]` for every input.
6
+ */
7
+ codes?: string[];
8
+ }
@@ -1,8 +1,10 @@
1
1
  const MOCK_STATUS_CODES = [
2
+ '1',
2
3
  '2',
3
4
  '3',
4
5
  '4',
5
6
  '5',
7
+ '101',
6
8
  '200',
7
9
  '201',
8
10
  '301',
@@ -1,4 +1,5 @@
1
1
  import type { ReactNode } from 'react';
2
+ import type { BadgeColor } from '../Badge';
2
3
  /**
3
4
  * FilterInput Chip Variant Types
4
5
  */
@@ -39,7 +40,7 @@ export interface FieldValueOption {
39
40
  value: string | number | boolean;
40
41
  label: string;
41
42
  badge?: {
42
- color: string;
43
+ color: BadgeColor;
43
44
  text: string;
44
45
  };
45
46
  }
@@ -66,8 +67,37 @@ export interface FieldMetadata {
66
67
  * input text. When provided, takes precedence over `values` and `options`.
67
68
  * The returned list is still post-filtered by `filterAndSort`, so prefix/includes
68
69
  * matching still applies — return items that contain the input text.
70
+ *
71
+ * The optional `context.selectedValues` carries any values already committed
72
+ * to the chip (in single- or multi-select form) so the helper can include
73
+ * them in its output with their canonical presentation (e.g. a badge color)
74
+ * even when they fall outside the current input-driven suggestions.
75
+ */
76
+ getSuggestions?: (inputText: string, context?: {
77
+ selectedValues?: Array<string | number | boolean>;
78
+ }) => FieldValueOption[];
79
+ /**
80
+ * Optional freeform-value validator. When provided, it runs in place of the
81
+ * static-allowlist check — return `true` to mark the value invalid. Useful
82
+ * for fields that accept arbitrary input but still have format rules
83
+ * (e.g. HTTP status code must be 3 chars, first digit in [1..5]).
84
+ */
85
+ validate?: (value: string | number | boolean) => boolean;
86
+ /**
87
+ * Optional per-character input filter. When the user is entering a value
88
+ * for this field (building or inline-editing the value segment), any
89
+ * character for which this returns `false` is stripped from the input
90
+ * on change. Separators that the filter should preserve (comma, space)
91
+ * are allowed through unconditionally.
92
+ */
93
+ acceptChar?: (char: string) => boolean;
94
+ /**
95
+ * Optional value normalizer. Runs on commit (after Enter / blur) before
96
+ * `validate`, so consumers can auto-complete partial input — e.g. a status
97
+ * code helper can turn "2" into "2XX" or "22" into "22X". Multi-select
98
+ * commits apply this per token.
69
99
  */
70
- getSuggestions?: (inputText: string) => FieldValueOption[];
100
+ normalize?: (value: string | number | boolean) => string | number | boolean;
71
101
  }
72
102
  /**
73
103
  * Expression Tree Types
@@ -1,6 +1,6 @@
1
1
  {
2
- "version": "0.25.1",
3
- "generatedAt": "2026-04-20T10:37:05.301Z",
2
+ "version": "0.26.0",
3
+ "generatedAt": "2026-04-20T13:27:55.627Z",
4
4
  "components": [
5
5
  {
6
6
  "name": "Alert",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wallarm-org/design-system",
3
- "version": "0.26.0",
3
+ "version": "0.27.0",
4
4
  "description": "Core design system library with React components and Storybook documentation",
5
5
  "publishConfig": {
6
6
  "access": "public",
@@ -1,10 +0,0 @@
1
- import type { FieldValueOption } from '../types';
2
- export interface StatusCodeSuggestionsOptions {
3
- /**
4
- * Raw backend status-code list. The helper keeps only 1-char entries in
5
- * `[1..5]` as mask roots; everything else is ignored. When omitted or empty,
6
- * the helper returns `[]` for every input.
7
- */
8
- codes?: string[];
9
- }
10
- export declare const createStatusCodeSuggestions: (options?: StatusCodeSuggestionsOptions) => ((inputText: string) => FieldValueOption[]);
@@ -1,45 +0,0 @@
1
- const VALID_MASK_ROOTS = new Set([
2
- '1',
3
- '2',
4
- '3',
5
- '4',
6
- '5'
7
- ]);
8
- const HTTP_CLASS_BADGE_COLOR = {
9
- 1: 'var(--color-bg-light-success)',
10
- 2: 'var(--color-bg-success)',
11
- 3: 'var(--color-bg-info)',
12
- 4: 'var(--color-bg-warning)',
13
- 5: 'var(--color-bg-danger)'
14
- };
15
- const createStatusCodeSuggestions = (options)=>{
16
- const maskRoots = (options?.codes ?? []).filter((c)=>1 === c.length && VALID_MASK_ROOTS.has(c));
17
- const makeMask = (label)=>{
18
- const color = HTTP_CLASS_BADGE_COLOR[label.charAt(0)];
19
- if (!color) throw new Error(`statusCodeSuggestions: no badge color for mask "${label}"`);
20
- return {
21
- value: label,
22
- label,
23
- badge: {
24
- color,
25
- text: label
26
- }
27
- };
28
- };
29
- return (inputText)=>{
30
- const norm = inputText.trim();
31
- if (norm.length > 3) return [];
32
- if (norm.length > 0 && !/^\d+$/.test(norm)) return [];
33
- if (0 === norm.length) return maskRoots.map((d)=>makeMask(`${d}XX`));
34
- const d1 = norm.charAt(0);
35
- if (!maskRoots.includes(d1)) return [];
36
- if (1 === norm.length) return [
37
- makeMask(`${norm}XX`)
38
- ];
39
- if (2 === norm.length) return [
40
- makeMask(`${norm}X`)
41
- ];
42
- return [];
43
- };
44
- };
45
- export { createStatusCodeSuggestions };