@wallarm-org/design-system 1.7.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/FilterInput/FilterInputField/ChipsWithGaps.js +2 -0
- package/dist/components/FilterInput/FilterInputField/FilterInputChip/FilterInputChip.d.ts +7 -0
- package/dist/components/FilterInput/FilterInputField/FilterInputChip/FilterInputChip.js +18 -2
- package/dist/components/FilterInput/FilterInputField/FilterInputChip/Segment.d.ts +6 -0
- package/dist/components/FilterInput/FilterInputField/FilterInputChip/Segment.js +10 -3
- package/dist/components/FilterInput/FilterInputField/FilterInputChip/applyEditCaret.d.ts +1 -0
- package/dist/components/FilterInput/FilterInputField/FilterInputChip/applyEditCaret.js +10 -0
- package/dist/components/FilterInput/FilterInputField/FilterInputChip/index.d.ts +1 -0
- package/dist/components/FilterInput/FilterInputField/FilterInputChip/index.js +1 -0
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useFilterInputAutocomplete.js +4 -1
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useFocusManagement.js +16 -5
- package/dist/components/FilterInput/hooks/useFilterInputExpression/buildChips.js +6 -0
- package/dist/components/FilterInput/types.d.ts +16 -0
- package/dist/metadata/components.json +1104 -627
- package/dist/theme/Pixel.stories.tsx +8 -0
- package/package.json +1 -1
|
@@ -30,6 +30,8 @@ const ChipsWithGaps = ({ chips, hideLeadingGap, hideTrailingGap, onChipClick, on
|
|
|
30
30
|
valueSeparator: chip.valueSeparator,
|
|
31
31
|
errorValueIndices: chip.errorValueIndices,
|
|
32
32
|
disabled: chip.disabled,
|
|
33
|
+
valueMaxWidth: chip.valueMaxWidth,
|
|
34
|
+
caretMode: chip.caretMode,
|
|
33
35
|
pair: chip.pair,
|
|
34
36
|
onRemove: chip.disabled ? void 0 : ()=>onChipRemove(chip.id),
|
|
35
37
|
onSegmentClick: chip.disabled ? void 0 : (segment, anchorEl)=>onChipClick(chip.id, segment, anchorEl),
|
|
@@ -20,6 +20,13 @@ export interface FilterInputChipProps extends Omit<HTMLAttributes<HTMLDivElement
|
|
|
20
20
|
building?: boolean;
|
|
21
21
|
/** When true, the chip cannot be edited or removed (dimmed appearance) */
|
|
22
22
|
disabled?: boolean;
|
|
23
|
+
/** Caps the applied value segment at this width (px), truncating with an
|
|
24
|
+
* ellipsis; lifts the chip's default max-width so it grows to fit. Defaults
|
|
25
|
+
* to 500; a paired chip caps its whole width there instead (AS-1064). */
|
|
26
|
+
valueMaxWidth?: number;
|
|
27
|
+
/** Caret placement on value inline-edit: `'end'` (default) puts it at the
|
|
28
|
+
* tail — better for long values; pass explicitly to keep the API visible (AS-1064). */
|
|
29
|
+
caretMode?: 'end';
|
|
23
30
|
/** Second paired triplet (two-step fields). The paired attribute is fixed. */
|
|
24
31
|
pair?: FilterInputChipData['pair'];
|
|
25
32
|
onRemove?: () => void;
|
|
@@ -9,7 +9,7 @@ import { FilterInputRemoveButton } from "./FilterInputRemoveButton.js";
|
|
|
9
9
|
import { PairSeparator } from "./PairSeparator.js";
|
|
10
10
|
import { Segment } from "./Segment.js";
|
|
11
11
|
import { SEGMENT_VARIANT } from "./segmentVariant.js";
|
|
12
|
-
const FilterInputChip = ({ ref, chipId, attribute, attributeDescription, operator, value, error = false, valueParts, valueSeparator, errorValueIndices, building = false, disabled = false, pair, onRemove, onSegmentClick, onPairSegmentClick, className, ...props })=>{
|
|
12
|
+
const FilterInputChip = ({ ref, chipId, attribute, attributeDescription, operator, value, error = false, valueParts, valueSeparator, errorValueIndices, building = false, disabled = false, valueMaxWidth = 500, caretMode = 'end', pair, onRemove, onSegmentClick, onPairSegmentClick, className, style, ...props })=>{
|
|
13
13
|
const interactive = !disabled;
|
|
14
14
|
const internalRef = useRef(null);
|
|
15
15
|
const editing = useEditingContext();
|
|
@@ -74,7 +74,14 @@ const FilterInputChip = ({ ref, chipId, attribute, attributeDescription, operato
|
|
|
74
74
|
interactive,
|
|
75
75
|
disabled,
|
|
76
76
|
building
|
|
77
|
-
}), pair ?
|
|
77
|
+
}), pair ? void 0 : 'max-w-[320px]', className),
|
|
78
|
+
style: pair ? {
|
|
79
|
+
maxWidth: valueMaxWidth,
|
|
80
|
+
...style
|
|
81
|
+
} : {
|
|
82
|
+
maxWidth: 'none',
|
|
83
|
+
...style
|
|
84
|
+
},
|
|
78
85
|
"data-slot": "filter-input-condition-chip",
|
|
79
86
|
...building && {
|
|
80
87
|
'data-building': ''
|
|
@@ -104,10 +111,17 @@ const FilterInputChip = ({ ref, chipId, attribute, attributeDescription, operato
|
|
|
104
111
|
(value || baseActiveSegment === SEGMENT_VARIANT.value || !pair && effectiveError === SEGMENT_VARIANT.value) && /*#__PURE__*/ jsx(Segment, {
|
|
105
112
|
variant: SEGMENT_VARIANT.value,
|
|
106
113
|
className: value || pair ? pair ? 'max-w-[90px] shrink-0' : 'min-w-0' : emptyValueHitTarget,
|
|
114
|
+
...valueMaxWidth && value && !pair && {
|
|
115
|
+
style: {
|
|
116
|
+
maxWidth: valueMaxWidth
|
|
117
|
+
}
|
|
118
|
+
},
|
|
107
119
|
error: baseActiveSegment !== SEGMENT_VARIANT.value && (true === effectiveError || effectiveError === SEGMENT_VARIANT.value),
|
|
108
120
|
valueParts: valueParts,
|
|
109
121
|
valueSeparator: valueSeparator,
|
|
110
122
|
errorValueIndices: errorValueIndices,
|
|
123
|
+
caretMode: caretMode,
|
|
124
|
+
valueMaxWidth: valueMaxWidth,
|
|
111
125
|
onClick: interactive ? (e)=>handleSegmentClick(SEGMENT_VARIANT.value, e) : void 0,
|
|
112
126
|
onMouseDown: interactive && building ? handleSegmentMouseDown : void 0,
|
|
113
127
|
...segmentEditProps(SEGMENT_VARIANT.value),
|
|
@@ -133,6 +147,8 @@ const FilterInputChip = ({ ref, chipId, attribute, attributeDescription, operato
|
|
|
133
147
|
variant: SEGMENT_VARIANT.value,
|
|
134
148
|
className: pair.value ? 'min-w-0' : emptyValueHitTarget,
|
|
135
149
|
error: pairActiveSegment !== SEGMENT_VARIANT.value && (true === effectivePairError || effectivePairError === SEGMENT_VARIANT.value),
|
|
150
|
+
caretMode: caretMode,
|
|
151
|
+
valueMaxWidth: valueMaxWidth,
|
|
136
152
|
onClick: interactive ? (e)=>handlePairSegmentClick('value', e) : void 0,
|
|
137
153
|
...segmentEditProps(SEGMENT_VARIANT.value, 1),
|
|
138
154
|
children: pair.value ?? ''
|
|
@@ -18,5 +18,11 @@ export type SegmentProps = HTMLAttributes<HTMLDivElement> & {
|
|
|
18
18
|
valueSeparator?: string;
|
|
19
19
|
/** Indices of invalid values in valueParts */
|
|
20
20
|
errorValueIndices?: number[];
|
|
21
|
+
/** `'end'` places the caret at the end on edit; omit for default select-all (AS-1064). */
|
|
22
|
+
caretMode?: 'end';
|
|
23
|
+
/** Caps the edit input's width (px) to match the applied value cap, so the
|
|
24
|
+
* segment keeps the same width entering/leaving edit instead of snapping to
|
|
25
|
+
* the default MAX_INPUT_WIDTH. Value variant only (AS-1064). */
|
|
26
|
+
valueMaxWidth?: number;
|
|
21
27
|
};
|
|
22
28
|
export declare const Segment: FC<SegmentProps>;
|
|
@@ -2,12 +2,13 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { useCallback, useContext, useEffect, useRef } from "react";
|
|
3
3
|
import { cn } from "../../../../utils/cn.js";
|
|
4
4
|
import { FilterInputContext } from "../../FilterInputContext/FilterInputContext.js";
|
|
5
|
+
import { applyEditCaret } from "./applyEditCaret.js";
|
|
5
6
|
import { editingValueMinWidth, segmentContainer, segmentTextVariants } from "./classes.js";
|
|
6
7
|
import { CHAR_WIDTH_PX } from "./constants.js";
|
|
7
8
|
import { MultiValueSegment } from "./MultiValueSegment.js";
|
|
8
9
|
import { useSizerWidth } from "./model/useSizerWidth.js";
|
|
9
10
|
import { SEGMENT_VARIANT } from "./segmentVariant.js";
|
|
10
|
-
const Segment = ({ ref, variant, children, className, error, editing, editText, onEditChange, onEditKeyDown, onEditBlur, valueParts, valueSeparator = ', ', errorValueIndices, ...props })=>{
|
|
11
|
+
const Segment = ({ ref, variant, children, className, error, editing, editText, onEditChange, onEditKeyDown, onEditBlur, valueParts, valueSeparator = ', ', errorValueIndices, caretMode, valueMaxWidth, ...props })=>{
|
|
11
12
|
const textRef = useRef(null);
|
|
12
13
|
const inputRef = useRef(null);
|
|
13
14
|
const sizerRef = useRef(null);
|
|
@@ -28,7 +29,7 @@ const Segment = ({ ref, variant, children, className, error, editing, editText,
|
|
|
28
29
|
const outerFrame = requestAnimationFrame(()=>{
|
|
29
30
|
innerFrame = requestAnimationFrame(()=>{
|
|
30
31
|
inputRef.current?.focus();
|
|
31
|
-
inputRef.current
|
|
32
|
+
applyEditCaret(inputRef.current);
|
|
32
33
|
});
|
|
33
34
|
});
|
|
34
35
|
return ()=>{
|
|
@@ -40,7 +41,10 @@ const Segment = ({ ref, variant, children, className, error, editing, editText,
|
|
|
40
41
|
]);
|
|
41
42
|
const inputWidth = useSizerWidth({
|
|
42
43
|
sizerRef,
|
|
43
|
-
text: editText ?? ''
|
|
44
|
+
text: editText ?? '',
|
|
45
|
+
...variant === SEGMENT_VARIANT.value && valueMaxWidth && {
|
|
46
|
+
maxWidth: valueMaxWidth
|
|
47
|
+
}
|
|
44
48
|
});
|
|
45
49
|
const setInputRef = useCallback((node)=>{
|
|
46
50
|
inputRef.current = node;
|
|
@@ -76,6 +80,9 @@ const Segment = ({ ref, variant, children, className, error, editing, editText,
|
|
|
76
80
|
onKeyDown: onEditKeyDown,
|
|
77
81
|
onBlur: onEditBlur,
|
|
78
82
|
"aria-label": `Filter ${variant}`,
|
|
83
|
+
...'end' === caretMode && {
|
|
84
|
+
'data-caret-mode': 'end'
|
|
85
|
+
},
|
|
79
86
|
className: cn(segmentTextVariants({
|
|
80
87
|
variant,
|
|
81
88
|
error
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const applyEditCaret: (input: HTMLInputElement | null) => void;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { type ConnectorVariant, FilterInputConnectorChip, type FilterInputConnectorChipProps, } from '../FilterInputConnectorChip';
|
|
2
|
+
export { applyEditCaret } from './applyEditCaret';
|
|
2
3
|
export { type EditingContextValue, EditingProvider, useEditingContext, } from './context/EditingContext';
|
|
3
4
|
export { type ChipSegment, FilterInputChip, type FilterInputChipProps, type PairSegment, } from './FilterInputChip';
|
|
4
5
|
export { FilterInputRemoveButton } from './FilterInputRemoveButton';
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { FilterInputConnectorChip } from "../FilterInputConnectorChip/index.js";
|
|
2
|
+
export { applyEditCaret } from "./applyEditCaret.js";
|
|
2
3
|
export { EditingProvider, useEditingContext } from "./context/EditingContext.js";
|
|
3
4
|
export { FilterInputChip } from "./FilterInputChip.js";
|
|
4
5
|
export { FilterInputRemoveButton } from "./FilterInputRemoveButton.js";
|
package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useFilterInputAutocomplete.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useCallback } from "react";
|
|
2
2
|
import { SEGMENT_VARIANT } from "../../FilterInputField/FilterInputChip/index.js";
|
|
3
3
|
import { useDateRange } from "../../FilterInputMenu/FilterInputDateValueMenu/hooks.js";
|
|
4
|
+
import { isMultiSelectOperator } from "../../lib/index.js";
|
|
4
5
|
import { deriveAutocompleteValues } from "./lib/index.js";
|
|
5
6
|
import { useAutocompleteState } from "./useAutocompleteState.js";
|
|
6
7
|
import { useBlurCommit } from "./useBlurCommit.js";
|
|
@@ -218,6 +219,8 @@ const useFilterInputAutocomplete = ({ fields, conditions, chips, upsertCondition
|
|
|
218
219
|
buildingBase,
|
|
219
220
|
editingSide: editing.editingSide
|
|
220
221
|
});
|
|
222
|
+
const isMultiValueValueEdit = null != editing.editingChipId && editing.editingSegment === SEGMENT_VARIANT.value && null != selectedOperator && isMultiSelectOperator(selectedOperator);
|
|
223
|
+
const liveSegmentFilterText = isMultiValueValueEdit && null != buildingMultiValue && '' === editing.segmentMenuFilterText ? buildingMultiValue : editing.segmentFilterText;
|
|
221
224
|
return {
|
|
222
225
|
inputText,
|
|
223
226
|
menuState,
|
|
@@ -260,7 +263,7 @@ const useFilterInputAutocomplete = ({ fields, conditions, chips, upsertCondition
|
|
|
260
263
|
editingChipId: editing.editingChipId,
|
|
261
264
|
editingSegment: editing.editingSegment,
|
|
262
265
|
editingSide: editing.editingSide,
|
|
263
|
-
segmentFilterText:
|
|
266
|
+
segmentFilterText: liveSegmentFilterText,
|
|
264
267
|
segmentMenuFilterText: editing.segmentMenuFilterText,
|
|
265
268
|
handleSegmentFilterChange,
|
|
266
269
|
cancelSegmentEdit,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useCallback, useEffect, useMemo, useRef } from "react";
|
|
2
|
-
import { SEGMENT_VARIANT } from "../../FilterInputField/FilterInputChip/index.js";
|
|
2
|
+
import { SEGMENT_VARIANT, applyEditCaret } from "../../FilterInputField/FilterInputChip/index.js";
|
|
3
3
|
import { isMenuRelated, nextBuildingMenu } from "../../lib/index.js";
|
|
4
4
|
const useFocusManagement = ({ menuState, isFocused, conditionsLength, inputText, selectedField, selectedOperator, containerRef, inputRef, editingSegment, segmentAttributeInputRef, segmentOperatorInputRef, segmentValueInputRef, blurCommitRef, commitBuildingOnBlur, hasIncompleteBuilding, setIsFocused, setMenuState, resetMenuAnchor, resetState })=>{
|
|
5
5
|
const handleFocus = useCallback((e)=>{
|
|
@@ -12,12 +12,19 @@ const useFocusManagement = ({ menuState, isFocused, conditionsLength, inputText,
|
|
|
12
12
|
const handleBlur = useCallback((e)=>{
|
|
13
13
|
if (handlingBlurRef.current) return;
|
|
14
14
|
const related = e.relatedTarget;
|
|
15
|
-
if (containerRef.current?.contains(related)) return;
|
|
16
15
|
if (isMenuRelated(related)) return;
|
|
16
|
+
if (null != related && (related === segmentAttributeInputRef.current || related === segmentOperatorInputRef.current || related === segmentValueInputRef.current)) return;
|
|
17
17
|
handlingBlurRef.current = true;
|
|
18
18
|
try {
|
|
19
|
+
const focusStaysInside = !!containerRef.current?.contains(related);
|
|
20
|
+
if ((null != editingSegment || !focusStaysInside) && blurCommitRef.current?.()) {
|
|
21
|
+
setIsFocused(false);
|
|
22
|
+
related?.focus();
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
if (focusStaysInside) return;
|
|
19
26
|
setIsFocused(false);
|
|
20
|
-
const committed =
|
|
27
|
+
const committed = commitBuildingOnBlur();
|
|
21
28
|
if (!committed) if (hasIncompleteBuilding()) setMenuState('closed');
|
|
22
29
|
else resetState();
|
|
23
30
|
related?.focus();
|
|
@@ -33,7 +40,11 @@ const useFocusManagement = ({ menuState, isFocused, conditionsLength, inputText,
|
|
|
33
40
|
resetState,
|
|
34
41
|
setIsFocused,
|
|
35
42
|
setMenuState,
|
|
36
|
-
inputRef
|
|
43
|
+
inputRef,
|
|
44
|
+
editingSegment,
|
|
45
|
+
segmentAttributeInputRef,
|
|
46
|
+
segmentOperatorInputRef,
|
|
47
|
+
segmentValueInputRef
|
|
37
48
|
]);
|
|
38
49
|
const prevFocusedRef = useRef(false);
|
|
39
50
|
useEffect(()=>{
|
|
@@ -87,7 +98,7 @@ const useFocusManagement = ({ menuState, isFocused, conditionsLength, inputText,
|
|
|
87
98
|
if (!dest) return;
|
|
88
99
|
if (document.activeElement === dest) return;
|
|
89
100
|
dest.focus();
|
|
90
|
-
if (editingSegment) dest
|
|
101
|
+
if (editingSegment) applyEditCaret(dest);
|
|
91
102
|
});
|
|
92
103
|
});
|
|
93
104
|
return ()=>{
|
|
@@ -39,6 +39,12 @@ const buildBaseChip = (i, condition, field)=>({
|
|
|
39
39
|
...field?.description && {
|
|
40
40
|
attributeDescription: field.description
|
|
41
41
|
},
|
|
42
|
+
...field?.valueMaxWidth && {
|
|
43
|
+
valueMaxWidth: field.valueMaxWidth
|
|
44
|
+
},
|
|
45
|
+
...field?.caretMode && {
|
|
46
|
+
caretMode: field.caretMode
|
|
47
|
+
},
|
|
42
48
|
operator: condition.operator ? getOperatorLabel(condition.operator, field?.type || DEFAULT_FIELD_TYPE) : void 0,
|
|
43
49
|
...condition.disabled && {
|
|
44
50
|
disabled: true
|
|
@@ -38,6 +38,10 @@ export interface FilterInputChipData {
|
|
|
38
38
|
errorValueIndices?: number[];
|
|
39
39
|
/** When true, the chip cannot be edited or removed */
|
|
40
40
|
disabled?: boolean;
|
|
41
|
+
/** Applied value-segment width cap in px (see FieldMetadata.valueMaxWidth). */
|
|
42
|
+
valueMaxWidth?: number;
|
|
43
|
+
/** Caret placement on value inline-edit (see FieldMetadata.caretMode). */
|
|
44
|
+
caretMode?: 'end';
|
|
41
45
|
/** Second paired triplet (display) for two-step fields. */
|
|
42
46
|
pair?: {
|
|
43
47
|
attribute: string;
|
|
@@ -199,6 +203,18 @@ export interface FieldMetadata {
|
|
|
199
203
|
* set it while an async options fetch is in flight.
|
|
200
204
|
*/
|
|
201
205
|
loadingOptions?: boolean;
|
|
206
|
+
/**
|
|
207
|
+
* Caps the applied value segment at this width in px: a longer value truncates
|
|
208
|
+
* with a trailing ellipsis while the full value is preserved for matching. When
|
|
209
|
+
* set, the chip's default max-width is lifted so it grows to fit the wider value.
|
|
210
|
+
* Omit to keep the chip's default cap. (AS-1064: long parameter-path expressions.)
|
|
211
|
+
*/
|
|
212
|
+
valueMaxWidth?: number;
|
|
213
|
+
/**
|
|
214
|
+
* `'end'` places the caret at the end of the value on inline-edit (natural for
|
|
215
|
+
* long values); omit to select the whole value so typing replaces it. (AS-1064)
|
|
216
|
+
*/
|
|
217
|
+
caretMode?: 'end';
|
|
202
218
|
}
|
|
203
219
|
/**
|
|
204
220
|
* A labeled group of filter fields for the field-selection menu, referenced by
|