@geotab/zenith 3.13.0-beta.1 → 3.13.1-beta.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 (40) hide show
  1. package/README.md +3 -1
  2. package/dist/commonHelpers/hooks/usePillSize.js +2 -2
  3. package/dist/commonStyles/popupMixin.less +2 -2
  4. package/dist/controlledPopup/controlledPopup.d.ts +4 -1
  5. package/dist/controlledPopup/controlledPopup.js +2 -2
  6. package/dist/filters/components/filtersContainer.js +64 -7
  7. package/dist/filters/components/filtersSearch.d.ts +7 -2
  8. package/dist/filters/components/filtersSearch.js +36 -72
  9. package/dist/filters/components/filtersSearchList.js +18 -35
  10. package/dist/filters/filters.d.ts +2 -0
  11. package/dist/filters/filters.js +66 -180
  12. package/dist/filters/filtersHelper.js +1 -1
  13. package/dist/groupsFilterRaw/groupsFilterRaw.js +7 -1
  14. package/dist/index.css +193 -113
  15. package/dist/inputAdornments/inputAdornments.d.ts +2 -1
  16. package/dist/inputAdornments/inputAdornments.js +2 -2
  17. package/dist/searchInputRaw/searchInputRaw.js +6 -2
  18. package/dist/searchInputRaw/types.d.ts +4 -0
  19. package/dist/utils/localization/translations/en.json +1 -0
  20. package/dist/utils/positioningUtils/calculatePosition.js +3 -1
  21. package/dist/utils/positioningUtils/normalizeVertically.js +1 -1
  22. package/esm/commonHelpers/hooks/usePillSize.js +2 -2
  23. package/esm/controlledPopup/controlledPopup.d.ts +4 -1
  24. package/esm/controlledPopup/controlledPopup.js +2 -2
  25. package/esm/filters/components/filtersContainer.js +65 -8
  26. package/esm/filters/components/filtersSearch.d.ts +7 -2
  27. package/esm/filters/components/filtersSearch.js +36 -49
  28. package/esm/filters/components/filtersSearchList.js +18 -35
  29. package/esm/filters/filters.d.ts +2 -0
  30. package/esm/filters/filters.js +67 -181
  31. package/esm/filters/filtersHelper.js +1 -1
  32. package/esm/groupsFilterRaw/groupsFilterRaw.js +7 -1
  33. package/esm/inputAdornments/inputAdornments.d.ts +2 -1
  34. package/esm/inputAdornments/inputAdornments.js +2 -2
  35. package/esm/searchInputRaw/searchInputRaw.js +6 -2
  36. package/esm/searchInputRaw/types.d.ts +4 -0
  37. package/esm/utils/localization/translations/en.json +1 -0
  38. package/esm/utils/positioningUtils/calculatePosition.js +3 -1
  39. package/esm/utils/positioningUtils/normalizeVertically.js +1 -1
  40. package/package.json +1 -1
package/README.md CHANGED
@@ -49,8 +49,10 @@ Markdown reference for each component (used by AI tooling and IRIS) lives in [`d
49
49
 
50
50
  ## Change log
51
51
 
52
- ### 3.13.0
52
+ ### 3.13.1
53
53
 
54
+ - Redesign `Filters` bar (Phases 1–3): updated chip colors and mobile sizing, replace "All Filters" text chip with a compact "+" icon chip with active-filter badge, and reorder inline save/clear icon chips by filter state
55
+ - Add `searchPlaceholder` prop to `Filters` for customizing the search input placeholder
54
56
  - Add `prefix` prop to `Summary` component for mixed-color value styling
55
57
  - Add logical alignment support to `Tooltip` for correct RTL popup direction
56
58
  - Remove hardcoded 400px minimum height from `Table` component
@@ -52,7 +52,7 @@ const usePillSize = ({ size, iconPosition, onClose, hasContent, initialTitle })
52
52
  }
53
53
  }, []);
54
54
  (0, useResize_1.useResize)(() => {
55
- if (isFixedSize || (hasIcon && !isClosable) || (!hasIcon && isClosable)) {
55
+ if (isFixedSize || !hasIcon || !isClosable) {
56
56
  return;
57
57
  }
58
58
  hidePillContent();
@@ -61,7 +61,7 @@ const usePillSize = ({ size, iconPosition, onClose, hasContent, initialTitle })
61
61
  if (initialTitle) {
62
62
  return;
63
63
  }
64
- if ((hasIcon && !isClosable) || (!hasIcon && isClosable)) {
64
+ if (!hasIcon || !isClosable) {
65
65
  return;
66
66
  }
67
67
  hidePillContent();
@@ -7,9 +7,9 @@
7
7
  }
8
8
 
9
9
  .zen-popup-body.zen-popup-body {
10
- overflow-block: auto;
10
+ overflow-y: auto;
11
11
 
12
12
  &--hidden {
13
- overflow-block: hidden;
13
+ overflow-y: hidden;
14
14
  }
15
15
  }
@@ -3,6 +3,7 @@ import { TTrapFocus } from "../absolute/absolute";
3
3
  import "./controlledPopup.less";
4
4
  import { IZenComponentProps } from "../commonHelpers/zenComponent";
5
5
  import { TAlignment as TAlignmentAbsolute } from "../absolute/absolute";
6
+ import { IAlignment } from "../utils/positioningUtils/alignments";
6
7
  /**
7
8
  * @deprecated - please use {@link TAlignmentAbsolute|TAlignment} interface from the absolute component
8
9
  */
@@ -28,5 +29,7 @@ export interface IControlledPopup extends IZenComponentProps {
28
29
  preventAttributesAutoSet?: boolean;
29
30
  /** Whether to focus the first focusable element when popup opens. Default: true */
30
31
  focusOnOpen?: boolean;
32
+ /** Override the default alignment calculation functions. */
33
+ alignmentsFn?: IAlignment;
31
34
  }
32
- export declare const ControlledPopup: ({ isOpen, id, paddingX, paddingY, triggerRef, className, children, alignment, inline, onOpenChange, useTrapFocusWithTrigger, shouldHoldScroll, ariaLabel, ariaLabelledby, recalculateOnScroll, closeOnScroll, preventAttributesAutoSet, focusOnOpen, role }: IControlledPopup) => import("react/jsx-runtime").JSX.Element;
35
+ export declare const ControlledPopup: ({ isOpen, id, paddingX, paddingY, triggerRef, className, children, alignment, inline, onOpenChange, useTrapFocusWithTrigger, shouldHoldScroll, ariaLabel, ariaLabelledby, recalculateOnScroll, closeOnScroll, preventAttributesAutoSet, focusOnOpen, alignmentsFn, role }: IControlledPopup) => import("react/jsx-runtime").JSX.Element;
@@ -7,7 +7,7 @@ const absolute_1 = require("../absolute/absolute");
7
7
  const classNames_1 = require("../commonHelpers/classNames/classNames");
8
8
  const useClientReady_1 = require("../commonHelpers/hooks/useClientReady");
9
9
  const useRTLAlignment_1 = require("../utils/localization/useRTLAlignment");
10
- const ControlledPopup = ({ isOpen, id, paddingX, paddingY, triggerRef, className, children, alignment, inline, onOpenChange, useTrapFocusWithTrigger, shouldHoldScroll, ariaLabel, ariaLabelledby, recalculateOnScroll, closeOnScroll, preventAttributesAutoSet, focusOnOpen, role = "dialog" }) => {
10
+ const ControlledPopup = ({ isOpen, id, paddingX, paddingY, triggerRef, className, children, alignment, inline, onOpenChange, useTrapFocusWithTrigger, shouldHoldScroll, ariaLabel, ariaLabelledby, recalculateOnScroll, closeOnScroll, preventAttributesAutoSet, focusOnOpen, alignmentsFn, role = "dialog" }) => {
11
11
  const autoId = (0, react_1.useId)();
12
12
  const popupId = id || autoId;
13
13
  const isClientReady = (0, useClientReady_1.useClientReady)();
@@ -26,6 +26,6 @@ const ControlledPopup = ({ isOpen, id, paddingX, paddingY, triggerRef, className
26
26
  triggerRef.current.setAttribute("aria-expanded", isOpen ? "true" : "false");
27
27
  triggerRef.current.setAttribute("aria-controls", popupId);
28
28
  }, [triggerRef, isOpen, popupId, preventAttributesAutoSet, isClientReady]);
29
- return ((0, jsx_runtime_1.jsx)(absolute_1.Absolute, Object.assign({ triggerRef: triggerRef, alignment: rtlAlignment, id: popupId, isOpen: isOpen, className: (0, classNames_1.classNames)(["zen-popup", className ? className : ""]), paddingX: paddingX, paddingY: paddingY, inline: inline, onOpenChange: onOpenChange, useTrapFocusWithTrigger: useTrapFocusWithTrigger, shouldHoldScroll: shouldHoldScroll }, (role !== false && typeof role === "string" ? { role } : {}), (role !== false && ariaLabel ? { ariaLabel } : {}), (role !== false && ariaLabelledby ? { ariaLabelledby } : {}), { recalculateOnScroll: recalculateOnScroll, closeOnScroll: closeOnScroll, focusOnOpen: focusOnOpen, children: children })));
29
+ return ((0, jsx_runtime_1.jsx)(absolute_1.Absolute, Object.assign({ triggerRef: triggerRef, alignment: rtlAlignment, id: popupId, isOpen: isOpen, className: (0, classNames_1.classNames)(["zen-popup", className ? className : ""]), paddingX: paddingX, paddingY: paddingY, inline: inline, onOpenChange: onOpenChange, useTrapFocusWithTrigger: useTrapFocusWithTrigger, shouldHoldScroll: shouldHoldScroll }, (role !== false && typeof role === "string" ? { role } : {}), (role !== false && ariaLabel ? { ariaLabel } : {}), (role !== false && ariaLabelledby ? { ariaLabelledby } : {}), { recalculateOnScroll: recalculateOnScroll, closeOnScroll: closeOnScroll, focusOnOpen: focusOnOpen, alignmentsFn: alignmentsFn, children: children })));
30
30
  };
31
31
  exports.ControlledPopup = ControlledPopup;
@@ -70,15 +70,45 @@ injectString("zh-Hans", "Clear", "\u6E05\u9664");
70
70
  injectString("zh-TW", "Clear", "\u6E05\u9664");
71
71
  injectString("ro-RO", "Clear", "\u0218terge\u021Bi");
72
72
  injectString("ar", "Clear", "\u0645\u0633\u062D");
73
+ injectString("cs", "Save", "Ulo\u017Eit");
74
+ injectString("da-DK", "Save", "Spare");
75
+ injectString("de", "Save", "Speichern");
76
+ injectString("en", "Save", "Save");
77
+ injectString("es", "Save", "Guardar");
78
+ injectString("fi-FI", "Save", "S\xE4\xE4st\xE4\xE4");
79
+ injectString("fr", "Save", "Enregistrer");
80
+ injectString("fr-FR", "Save", "Enregistrer");
81
+ injectString("hu-HU", "Save", "Ment\xE9s");
82
+ injectString("id", "Save", "Simpan");
83
+ injectString("it", "Save", "Salvare");
84
+ injectString("ja", "Save", "\u4FDD\u5B58");
85
+ injectString("ko-KR", "Save", "\uC800\uC7A5");
86
+ injectString("ms", "Save", "Simpan");
87
+ injectString("nb-NO", "Save", "Lagre");
88
+ injectString("nl", "Save", "Opslaan");
89
+ injectString("pl", "Save", "Zapisz");
90
+ injectString("pt-BR", "Save", "Salvar");
91
+ injectString("pt-PT", "Save", "Guardar");
92
+ injectString("sk-SK", "Save", "Ulo\u017Ei\u0165");
93
+ injectString("sv", "Save", "Spara");
94
+ injectString("th", "Save", "\u0E1A\u0E31\u0E19\u0E17\u0E36\u0E01");
95
+ injectString("tr", "Save", "Kaydet");
96
+ injectString("zh-Hans", "Save", "\u4FDD\u5B58");
97
+ injectString("zh-TW", "Save", "\u5132\u5B58");
98
+ injectString("ro-RO", "Save", "Salva\u021Bi");
99
+ injectString("ar", "Save", "\u062D\u0641\u0638");
73
100
  const react_2 = __importDefault(require("react"));
74
101
  const iconFilter_1 = require("../../icons/iconFilter");
75
102
  const filtersHelper_1 = require("../filtersHelper");
76
103
  const filtersSavedChipComponent_1 = require("./filtersSavedChipComponent");
77
- const iconClose_1 = require("../../icons/iconClose");
104
+ const iconCloseCircle_1 = require("../../icons/iconCloseCircle");
105
+ const iconDisk_1 = require("../../icons/iconDisk");
78
106
  const chipStatusProvider_1 = require("../../chip/chipStatusProvider");
79
107
  const useLanguage_1 = require("../../utils/localization/useLanguage");
80
108
  const useMobile_1 = require("../../commonHelpers/hooks/useMobile");
81
109
  const keyboardHelpers_1 = require("../../utils/keyboardHelpers");
110
+ const filtersSavedItemsProvider_1 = require("./filtersSavedItemsProvider");
111
+ const filtersSaveModal_1 = require("./filtersSaveModal");
82
112
  const FiltersContainer = ({
83
113
  className,
84
114
  isPinned = false,
@@ -94,6 +124,11 @@ const FiltersContainer = ({
94
124
  translate
95
125
  } = (0, useLanguage_1.useLanguage)();
96
126
  const isMobile = (0, useMobile_1.useMobile)();
127
+ const {
128
+ onSave
129
+ } = (0, filtersSavedItemsProvider_1.useFiltersSavedItems)();
130
+ const [saveModalOpen, setSaveModalOpen] = (0, react_1.useState)(false);
131
+ const saveChipRef = (0, react_1.useRef)(null);
97
132
  const filtersChipContainerRef = react_2.default.useRef(null);
98
133
  const numberOfChangedFilters = (0, react_1.useMemo)(() => {
99
134
  const suppressedFilters = new Set();
@@ -116,6 +151,7 @@ const FiltersContainer = ({
116
151
  title: translate("All Filters"),
117
152
  dataRole: "dialog",
118
153
  triggerRef: filtersChipRef,
154
+ className: "zen-filters-container__all-filters-chip",
119
155
  children: "+"
120
156
  }), [numberOfChangedFilters, chipStatus, filtersClickHandler, translate, filtersChipRef]);
121
157
  const childrenSignature = (0, react_1.useMemo)(() => {
@@ -173,19 +209,40 @@ const FiltersContainer = ({
173
209
  active: false,
174
210
  status: isPinned ? "accent" : undefined,
175
211
  onChange: handleRemovalClick,
176
- icon: iconClose_1.IconClose
212
+ icon: iconCloseCircle_1.IconCloseCircle
177
213
  }) : null, [isPinned, handleRemovalClick, translate, numberOfChangedFilters]);
214
+ const handleSaveChipClick = (0, react_1.useCallback)(() => {
215
+ setSaveModalOpen(true);
216
+ }, []);
217
+ const chipSaveComponent = (0, react_1.useMemo)(() => onSave ? (0, jsx_runtime_1.jsx)(chip_1.Chip, {
218
+ title: translate("Save"),
219
+ onClick: handleSaveChipClick,
220
+ icon: iconDisk_1.IconDisk,
221
+ isOpen: saveModalOpen,
222
+ triggerRef: saveChipRef,
223
+ className: "zen-filters-container__save-chip"
224
+ }) : null, [onSave, translate, handleSaveChipClick, saveModalOpen]);
225
+ // Order:
226
+ // Default: [filters] [+]
227
+ // Selected: [filters] [+badge] [save] [clear]
228
+ // w/ Saved: [filters] [+] [save]
229
+ const showInlineSave = Boolean(onSave && (numberOfChangedFilters > 0 || hasSavedFilters));
178
230
  const memoizedFiltersElements = (0, react_1.useMemo)(() => (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, {
179
- children: [memoizedChildren, hasSavedFilters ? (0, jsx_runtime_1.jsx)(filtersSavedChipComponent_1.FiltersSavedChipComponent, {}) : null, filtersChipComponent, chipRemovalComponent]
180
- }), [chipRemovalComponent, filtersChipComponent, hasSavedFilters, memoizedChildren]);
181
- return (0, jsx_runtime_1.jsx)("div", {
231
+ children: [memoizedChildren, hasSavedFilters ? (0, jsx_runtime_1.jsx)(filtersSavedChipComponent_1.FiltersSavedChipComponent, {}) : null, filtersChipComponent, showInlineSave ? chipSaveComponent : null, chipRemovalComponent]
232
+ }), [chipRemovalComponent, chipSaveComponent, filtersChipComponent, hasSavedFilters, memoizedChildren, showInlineSave]);
233
+ return (0, jsx_runtime_1.jsxs)("div", {
182
234
  className: (0, classNames_1.classNames)(["zen-filters-container", isMobile ? "zen-filters-container--mobile" : "", className || ""]),
183
235
  onKeyDown: handleKeyDown,
184
236
  ref: filtersChipContainerRef,
185
- children: isMobile ? (0, jsx_runtime_1.jsx)("div", {
237
+ children: [isMobile ? (0, jsx_runtime_1.jsx)("div", {
186
238
  className: "zen-filters-container__mobile-scroll-wrapper",
187
239
  children: memoizedFiltersElements
188
- }) : memoizedFiltersElements
240
+ }) : memoizedFiltersElements, onSave ? (0, jsx_runtime_1.jsx)(filtersSaveModal_1.FiltersSaveModal, {
241
+ prioritize: isMobile,
242
+ isOpen: saveModalOpen,
243
+ onClose: setSaveModalOpen,
244
+ onSave: onSave
245
+ }) : null]
189
246
  });
190
247
  };
191
248
  exports.FiltersContainer = FiltersContainer;
@@ -8,19 +8,24 @@ interface IFiltersSearch {
8
8
  onChange: (newValue: string) => void;
9
9
  onSearchKeyDown?: (e: React.KeyboardEvent<HTMLInputElement>) => void;
10
10
  onOpenPopup?: () => void;
11
+ /** Called when the clear (X) button inside the search input is clicked */
12
+ onClear?: () => void;
11
13
  searchSelection?: ISearchItem[];
12
- handleRemove?: (id: ISearchItem) => void;
13
14
  className?: string;
14
15
  ref?: RefObject<HTMLInputElement | null>;
16
+ /** Ref forwarded to the root wrapper div — used to anchor the search popup */
17
+ containerRef?: RefObject<HTMLDivElement | null>;
15
18
  ariaLabel?: string;
16
19
  role?: string | undefined;
17
20
  ariaAutocomplete?: "list" | "none" | "inline" | "both";
18
21
  ariaControls?: string;
19
22
  ariaHaspopup?: boolean | "dialog" | "menu" | "grid" | "true" | "false" | "listbox" | "tree";
20
23
  ariaExpanded?: boolean | "true" | "false";
24
+ /** When true, renders only the search icon button (input lives in the popup) */
25
+ isMobile?: boolean;
21
26
  }
22
27
  export declare const FiltersSearch: {
23
- ({ id, name, value, onChange, onSearchKeyDown, onOpenPopup, searchSelection, handleRemove, className, ariaLabel, role, ariaAutocomplete, ariaControls, ariaHaspopup, ariaExpanded, ref }: IFiltersSearch): import("react/jsx-runtime").JSX.Element;
28
+ ({ id, name, value, onChange, onSearchKeyDown, onOpenPopup, onClear, searchSelection, className, containerRef, ariaLabel, role, ariaAutocomplete, ariaControls, ariaHaspopup, ariaExpanded, isMobile, ref }: IFiltersSearch): import("react/jsx-runtime").JSX.Element;
24
29
  displayName: string;
25
30
  };
26
31
  export {};
@@ -1,89 +1,53 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
25
2
  Object.defineProperty(exports, "__esModule", { value: true });
26
3
  exports.FiltersSearch = void 0;
27
4
  const jsx_runtime_1 = require("react/jsx-runtime");
28
- const react_1 = __importStar(require("react"));
5
+ const react_1 = require("react");
29
6
  const classNames_1 = require("../../commonHelpers/classNames/classNames");
30
- const filtersSearchList_1 = require("./filtersSearchList");
31
7
  const searchInputRaw_1 = require("../../searchInputRaw/searchInputRaw");
32
8
  const pill_1 = require("../../pill/pill");
33
- const useMobile_1 = require("../../commonHelpers/hooks/useMobile");
34
- const FiltersSearch = ({ id, name, value, onChange, onSearchKeyDown, onOpenPopup, searchSelection, handleRemove, className, ariaLabel, role, ariaAutocomplete, ariaControls, ariaHaspopup, ariaExpanded, ref }) => {
35
- const isWithPills = Boolean(searchSelection && handleRemove);
36
- const divider = "_divider_";
37
- const isMobile = (0, useMobile_1.useMobile)();
38
- const memoizedPillsArray = react_1.default.useMemo(() => {
39
- if (!isWithPills || !searchSelection || searchSelection.length === 0) {
40
- return [];
41
- }
42
- const pillsArray = [];
43
- searchSelection.forEach(item => {
44
- pillsArray.push({
45
- id: `${item.type || filtersSearchList_1.NO_TYPED_SEARCH_ITEM}${divider}${item.id}`,
46
- name: item.name
47
- });
48
- });
49
- return pillsArray;
50
- }, [isWithPills, searchSelection]);
51
- const onRemove = (0, react_1.useCallback)((removedId) => {
52
- const [type, itemId] = removedId.split(divider);
53
- const item = searchSelection === null || searchSelection === void 0 ? void 0 : searchSelection.find(el => el.id === itemId);
54
- if (item) {
55
- handleRemove === null || handleRemove === void 0 ? void 0 : handleRemove(Object.assign(Object.assign({}, item), { type: type === filtersSearchList_1.NO_TYPED_SEARCH_ITEM ? undefined : type }));
56
- }
57
- }, [handleRemove, searchSelection]);
58
- const handleSearchClick = (0, react_1.useCallback)(e => {
59
- var _a, _b, _c;
60
- if (((_a = e.target) === null || _a === void 0 ? void 0 : _a.dataset.name) === "IconClose" ||
61
- e.target.classList.contains("zen-pill-non-actionable__close-button") ||
62
- ((_b = e.target) === null || _b === void 0 ? void 0 : _b.parentElement.classList.contains("zen-pill-non-actionable__close-button")) ||
63
- ((_c = e.target) === null || _c === void 0 ? void 0 : _c.parentElement.dataset.name) === "IconClose") {
9
+ const iconSearch_1 = require("../../icons/iconSearch");
10
+ const FiltersSearch = ({ id, name, value, onChange, onSearchKeyDown, onOpenPopup, onClear, searchSelection, className, containerRef, ariaLabel, role, ariaAutocomplete, ariaControls, ariaHaspopup, ariaExpanded, isMobile, ref }) => {
11
+ var _a;
12
+ const selectionCount = (_a = searchSelection === null || searchSelection === void 0 ? void 0 : searchSelection.length) !== null && _a !== void 0 ? _a : 0;
13
+ const handleSearchClick = (0, react_1.useCallback)((e) => {
14
+ var _a, _b;
15
+ const target = e.target;
16
+ if (target.classList.contains("zen-search-input__close-button") ||
17
+ ((_a = target.parentElement) === null || _a === void 0 ? void 0 : _a.classList.contains("zen-search-input__close-button")) ||
18
+ target.dataset.name === "IconClose" ||
19
+ ((_b = target.parentElement) === null || _b === void 0 ? void 0 : _b.dataset.name) === "IconClose") {
64
20
  return;
65
21
  }
66
22
  onOpenPopup === null || onOpenPopup === void 0 ? void 0 : onOpenPopup();
67
23
  }, [onOpenPopup]);
24
+ const selectionLabel = selectionCount > 99 ? "+99" : String(selectionCount);
68
25
  const selectedPills = (0, react_1.useMemo)(() => {
69
- if (memoizedPillsArray.length === 0) {
26
+ if (selectionCount === 0) {
70
27
  return [];
71
28
  }
72
- if (isMobile) {
73
- return memoizedPillsArray.length
74
- ? [
75
- (0, jsx_runtime_1.jsx)(pill_1.Pill, { type: "info", className: "zen-filters-search__selected-item zen-filters-search__selected-item--more", children: `+${memoizedPillsArray.length}` }, `${memoizedPillsArray.length}_${memoizedPillsArray.map(pill => pill.name).join("_")}`)
76
- ]
77
- : [];
78
- }
79
- const pillsForRender = [
80
- { id: memoizedPillsArray[0].id, name: memoizedPillsArray[0].name },
81
- memoizedPillsArray.length > 1 ? { id: "more_pills", name: `+${memoizedPillsArray.length - 1}` } : undefined
82
- ].filter(Boolean);
83
- return pillsForRender.map((pill, ind) => ((0, jsx_runtime_1.jsx)(pill_1.Pill, { type: "info", onClose: ind === 0 ? onRemove.bind(null, pill.id) : undefined, className: (0, classNames_1.classNames)(["zen-filters-search__selected-item", ind !== 0 ? "zen-filters-search__selected-item--more" : ""]), children: pill.name }, `${pill.id}_${pill.name}`)));
84
- }, [memoizedPillsArray, onRemove, isMobile]);
85
- const searchComponent = (0, react_1.useMemo)(() => ((0, jsx_runtime_1.jsx)(searchInputRaw_1.SearchInputRaw, { id: id, ref: ref, placeholder: name, onChange: onChange, onKeyDown: onSearchKeyDown, value: value, role: role, ariaLabel: ariaLabel, ariaExpanded: ariaExpanded === undefined ? undefined : ariaExpanded === "true" || ariaExpanded === true, ariaHaspopup: ariaHaspopup, ariaControls: ariaControls, ariaAutocomplete: ariaAutocomplete, className: (0, classNames_1.classNames)(["zen-filters-search__search-input", "zen-filters__search", className || ""]) })), [id, ref, name, onChange, onSearchKeyDown, value, className, role, ariaLabel, ariaExpanded, ariaHaspopup, ariaControls, ariaAutocomplete]);
86
- return ((0, jsx_runtime_1.jsxs)("div", { className: (0, classNames_1.classNames)(["zen-filters-search", className || "", isMobile ? "zen-filters-search--mobile" : ""]), onClick: handleSearchClick, children: [searchComponent, selectedPills.length > 0 && (0, jsx_runtime_1.jsx)("div", { className: "zen-filters-search__selected", children: selectedPills })] }));
29
+ return [
30
+ (0, jsx_runtime_1.jsx)(pill_1.Pill, { type: "info", className: "zen-filters-search__selected-item zen-filters-search__selected-item--more", children: selectionLabel }, selectionCount)
31
+ ];
32
+ }, [selectionCount, selectionLabel]);
33
+ const searchComponent = (0, react_1.useMemo)(() => ((0, jsx_runtime_1.jsx)(searchInputRaw_1.SearchInputRaw, { id: id, ref: ref, placeholder: name, onChange: onChange, onClear: onClear, onKeyDown: onSearchKeyDown, value: value, role: role, ariaLabel: ariaLabel, ariaExpanded: ariaExpanded === undefined ? undefined : ariaExpanded === "true" || ariaExpanded === true, ariaHaspopup: ariaHaspopup, ariaControls: ariaControls, ariaAutocomplete: ariaAutocomplete, className: (0, classNames_1.classNames)(["zen-filters-search__search-input", "zen-filters__search", className || ""]) })), [
34
+ id,
35
+ ref,
36
+ name,
37
+ onChange,
38
+ onClear,
39
+ onSearchKeyDown,
40
+ value,
41
+ className,
42
+ role,
43
+ ariaLabel,
44
+ ariaExpanded,
45
+ ariaHaspopup,
46
+ ariaControls,
47
+ ariaAutocomplete
48
+ ]);
49
+ const mobileTrigger = (0, react_1.useMemo)(() => ((0, jsx_runtime_1.jsx)("button", { type: "button", className: "zen-filters-search__mobile-trigger", onClick: handleSearchClick, "aria-haspopup": ariaHaspopup, "aria-controls": ariaControls, "aria-expanded": ariaExpanded === undefined ? undefined : ariaExpanded === "true" || ariaExpanded === true, "aria-label": ariaLabel || name, children: (0, jsx_runtime_1.jsx)(iconSearch_1.IconSearch, { className: "zen-filters-search__mobile-trigger-icon", size: "large" }) })), [handleSearchClick, ariaHaspopup, ariaControls, ariaExpanded, ariaLabel, name]);
50
+ return ((0, jsx_runtime_1.jsxs)("div", { ref: containerRef, className: (0, classNames_1.classNames)(["zen-filters-search", isMobile ? "zen-filters-search--mobile" : "", className || ""]), onClick: isMobile ? undefined : handleSearchClick, children: [isMobile ? mobileTrigger : searchComponent, selectedPills.length > 0 && (0, jsx_runtime_1.jsx)("div", { className: "zen-filters-search__selected", children: selectedPills })] }));
87
51
  };
88
52
  exports.FiltersSearch = FiltersSearch;
89
53
  exports.FiltersSearch.displayName = "FiltersSearch";
@@ -118,33 +118,6 @@ injectString("zh-Hans", "Currently selected", "Currently selected");
118
118
  injectString("zh-TW", "Currently selected", "Currently selected");
119
119
  injectString("ro-RO", "Currently selected", "Selec\u021Bie curent\u0103");
120
120
  injectString("ar", "Currently selected", "\u0627\u0644\u0645\u062D\u062F\u062F \u062D\u0627\u0644\u064A\u0627\u064B");
121
- injectString("cs", "View history", "View history");
122
- injectString("da-DK", "View history", "View history");
123
- injectString("de", "View history", "View history");
124
- injectString("en", "View history", "View history");
125
- injectString("es", "View history", "View history");
126
- injectString("fi-FI", "View history", "View history");
127
- injectString("fr", "View history", "View history");
128
- injectString("fr-FR", "View history", "View history");
129
- injectString("hu-HU", "View history", "View history");
130
- injectString("id", "View history", "View history");
131
- injectString("it", "View history", "View history");
132
- injectString("ja", "View history", "View history");
133
- injectString("ko-KR", "View history", "View history");
134
- injectString("ms", "View history", "View history");
135
- injectString("nb-NO", "View history", "View history");
136
- injectString("nl", "View history", "View history");
137
- injectString("pl", "View history", "View history");
138
- injectString("pt-BR", "View history", "View history");
139
- injectString("pt-PT", "View history", "Ver hist\xF3rico");
140
- injectString("sk-SK", "View history", "View history");
141
- injectString("sv", "View history", "View history");
142
- injectString("th", "View history", "View history");
143
- injectString("tr", "View history", "View history");
144
- injectString("zh-Hans", "View history", "View history");
145
- injectString("zh-TW", "View history", "View history");
146
- injectString("ro-RO", "View history", "Vede\u021Bi istoricul");
147
- injectString("ar", "View history", "\u0639\u0631\u0636 \u0627\u0644\u0633\u062C\u0644");
148
121
  injectString("cs", "Clear", "Vymazat");
149
122
  injectString("da-DK", "Clear", "Ryd");
150
123
  injectString("de", "Clear", "L\xF6schen");
@@ -393,15 +366,25 @@ const FiltersSearchList = ({
393
366
  var _a;
394
367
  setRemovedItems(prev => [...prev, itemId]);
395
368
  onRemoveRecentSearchItem && onRemoveRecentSearchItem([itemId]);
369
+ const remaining = recentItems.filter(item => item.id !== itemId && !removedItems.includes(item.id));
370
+ if (remaining.length === 0) {
371
+ changeMode("");
372
+ return;
373
+ }
396
374
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
397
375
  const firstItem = (_a = searchListRef.current) === null || _a === void 0 ? void 0 : _a.querySelector(focusableSelector_1.FOCUSABLE_SELECTOR);
398
376
  firstItem === null || firstItem === void 0 ? void 0 : firstItem.focus();
399
- }, [onRemoveRecentSearchItem, searchListRef]);
377
+ }, [onRemoveRecentSearchItem, searchListRef, recentItems, removedItems, changeMode]);
400
378
  const handleRemoveAll = (0, react_1.useCallback)(() => {
401
379
  setRemovedItems(recentItems.map(item => item.id));
402
380
  onRemoveRecentSearchItem && onRemoveRecentSearchItem(recentItems.map(item => item.id));
403
381
  changeMode(getCurrentMode());
404
382
  }, [recentItems, onRemoveRecentSearchItem, changeMode, getCurrentMode]);
383
+ const handleViewAll = (0, react_1.useCallback)(() => {
384
+ setRemovedItems(recentItems.map(item => item.id));
385
+ onRemoveRecentSearchItem && onRemoveRecentSearchItem(recentItems.map(item => item.id));
386
+ changeMode("");
387
+ }, [recentItems, onRemoveRecentSearchItem, changeMode]);
405
388
  const memoizedSearchItemClick = (0, react_1.useCallback)((itemId, itemType) => () => {
406
389
  const currentSearchItem = onlyOneType && !itemType ? searchItems.find(item => item.id === itemId) : searchItems.find(item => item.id === itemId && item.type === itemType);
407
390
  currentSearchItem && handleSearchItemClick && handleSearchItemClick(currentSearchItem);
@@ -530,10 +513,10 @@ const FiltersSearchList = ({
530
513
  children: [(0, jsx_runtime_1.jsx)("div", {
531
514
  className: "zen-filters-search-list__header-title",
532
515
  children: currentMode === "recent" ? translate("Recent searches") : translate("Currently selected")
533
- }), currentMode === "recent" && recentItems.length > resultsToShow.length && (0, jsx_runtime_1.jsx)(button_1.Button, {
516
+ }), currentMode === "recent" && (0, jsx_runtime_1.jsx)(button_1.Button, {
534
517
  type: "tertiary",
535
- onClick: changeMode.bind(null, "history"),
536
- children: translate("View history")
518
+ onClick: handleViewAll,
519
+ children: translate("Clear all")
537
520
  }), currentMode === "selected" && resultsToShow.length > 0 && (0, jsx_runtime_1.jsx)(button_1.Button, {
538
521
  type: "tertiary",
539
522
  onClick: memoizedClearSelectedItems,
@@ -578,17 +561,17 @@ const FiltersSearchList = ({
578
561
  type: "tertiary",
579
562
  onClick: handleRemoveAll,
580
563
  children: translate("Clear all")
581
- }) : null, currentMode === "recent" && recentItems.length > resultsToShow.length ? (0, jsx_runtime_1.jsx)(button_1.Button, {
564
+ }) : null, currentMode === "recent" ? (0, jsx_runtime_1.jsx)(button_1.Button, {
582
565
  type: "tertiary",
583
- onClick: changeMode.bind(null, "history"),
584
- children: translate("View history")
566
+ onClick: handleViewAll,
567
+ children: translate("Clear all")
585
568
  }) : null, currentMode === "selected" && resultsToShow.length > 0 && (searchSelection || []).length > 0 ? (0, jsx_runtime_1.jsx)(button_1.Button, {
586
569
  type: "tertiary",
587
570
  onClick: memoizedClearSelectedItemsForApplyMode,
588
571
  children: translate("Clear")
589
572
  }) : null]
590
573
  });
591
- }, [changeMode, currentMode, handleRemoveAll, applyButtonHandler, memoizedClearSelectedItemsForApplyMode, recentItems.length, resultsToShow.length, translate, isInitialSearchSelectionState, searchSelection]);
574
+ }, [currentMode, handleRemoveAll, handleViewAll, applyButtonHandler, memoizedClearSelectedItemsForApplyMode, resultsToShow.length, translate, isInitialSearchSelectionState, searchSelection]);
592
575
  const searchHeaderContent = applyButtonHandler ? getHeaderTitles() : getSearchHeaderContent();
593
576
  return (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, {
594
577
  children: [(0, jsx_runtime_1.jsxs)("div", {
@@ -40,6 +40,8 @@ export interface IFiltersSearchTypesItem {
40
40
  export interface IFiltersBase extends IZenComponentProps {
41
41
  searchTypes?: IFiltersSearchTypesItem[] | IFiltersSearchTypesItem;
42
42
  onToggleView?: (isOpen: boolean) => void;
43
+ /** Placeholder text for the search input. Defaults to "Find in page" when empty or undefined. */
44
+ searchPlaceholder?: string;
43
45
  }
44
46
  export interface IFiltersWithKeyword {
45
47
  keyword: string;