@wallarm-org/design-system 1.7.0 → 1.8.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 +5 -0
- package/dist/components/FilterInput/FilterInputField/FilterInputChip/FilterInputChip.js +12 -1
- 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,11 @@ 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 (AS-1064). */
|
|
25
|
+
valueMaxWidth?: number;
|
|
26
|
+
/** `'end'` places the caret at the end on value inline-edit; omit to select all (AS-1064). */
|
|
27
|
+
caretMode?: 'end';
|
|
23
28
|
/** Second paired triplet (two-step fields). The paired attribute is fixed. */
|
|
24
29
|
pair?: FilterInputChipData['pair'];
|
|
25
30
|
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, caretMode, pair, onRemove, onSegmentClick, onPairSegmentClick, className, style, ...props })=>{
|
|
13
13
|
const interactive = !disabled;
|
|
14
14
|
const internalRef = useRef(null);
|
|
15
15
|
const editing = useEditingContext();
|
|
@@ -75,6 +75,10 @@ const FilterInputChip = ({ ref, chipId, attribute, attributeDescription, operato
|
|
|
75
75
|
disabled,
|
|
76
76
|
building
|
|
77
77
|
}), pair ? 'max-w-[380px]' : 'max-w-[320px]', className),
|
|
78
|
+
style: valueMaxWidth && !pair ? {
|
|
79
|
+
maxWidth: 'none',
|
|
80
|
+
...style
|
|
81
|
+
} : style,
|
|
78
82
|
"data-slot": "filter-input-condition-chip",
|
|
79
83
|
...building && {
|
|
80
84
|
'data-building': ''
|
|
@@ -104,10 +108,17 @@ const FilterInputChip = ({ ref, chipId, attribute, attributeDescription, operato
|
|
|
104
108
|
(value || baseActiveSegment === SEGMENT_VARIANT.value || !pair && effectiveError === SEGMENT_VARIANT.value) && /*#__PURE__*/ jsx(Segment, {
|
|
105
109
|
variant: SEGMENT_VARIANT.value,
|
|
106
110
|
className: value || pair ? pair ? 'max-w-[90px] shrink-0' : 'min-w-0' : emptyValueHitTarget,
|
|
111
|
+
...valueMaxWidth && value && !pair && {
|
|
112
|
+
style: {
|
|
113
|
+
maxWidth: valueMaxWidth
|
|
114
|
+
}
|
|
115
|
+
},
|
|
107
116
|
error: baseActiveSegment !== SEGMENT_VARIANT.value && (true === effectiveError || effectiveError === SEGMENT_VARIANT.value),
|
|
108
117
|
valueParts: valueParts,
|
|
109
118
|
valueSeparator: valueSeparator,
|
|
110
119
|
errorValueIndices: errorValueIndices,
|
|
120
|
+
caretMode: caretMode,
|
|
121
|
+
valueMaxWidth: valueMaxWidth,
|
|
111
122
|
onClick: interactive ? (e)=>handleSegmentClick(SEGMENT_VARIANT.value, e) : void 0,
|
|
112
123
|
onMouseDown: interactive && building ? handleSegmentMouseDown : void 0,
|
|
113
124
|
...segmentEditProps(SEGMENT_VARIANT.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
|