@pallasoft/ps-input-components 3.2.0 → 3.3.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.
@@ -0,0 +1,26 @@
1
+ import { type ReactNode } from 'react';
2
+ import type { PsGroupedSwitcherAllOption } from '../interfaces/ps-grouped-switcher-all-option';
3
+ import type { PsGroupedSwitcherGroup } from '../interfaces/ps-grouped-switcher-group';
4
+ export interface PsGroupedSwitcherProps<T> {
5
+ allOption?: PsGroupedSwitcherAllOption;
6
+ ariaLabel: string;
7
+ className?: string;
8
+ emptyText: string;
9
+ getItemKey: (item: T) => string;
10
+ getItemSearchText: (item: T) => string;
11
+ groups: PsGroupedSwitcherGroup<T>[];
12
+ onSelect: (key: string) => void;
13
+ onTogglePin?: (key: string) => void;
14
+ pinLabel?: string;
15
+ pinnedKeys?: string[];
16
+ pinnedLabel?: string;
17
+ recentKeys?: string[];
18
+ recentLabel?: string;
19
+ renderItem: (item: T) => ReactNode;
20
+ renderSectionLabel?: (key: string, label: string) => ReactNode;
21
+ renderSelected: (item: T | null) => ReactNode;
22
+ searchPlaceholder: string;
23
+ selectedKey: string;
24
+ unpinLabel?: string;
25
+ }
26
+ export declare const PsGroupedSwitcher: <T>({ allOption, ariaLabel, className, emptyText, getItemKey, getItemSearchText, groups, onSelect, onTogglePin, pinLabel, pinnedKeys, pinnedLabel, recentKeys, recentLabel, renderItem, renderSectionLabel, renderSelected, searchPlaceholder, selectedKey, unpinLabel, }: PsGroupedSwitcherProps<T>) => import("react").JSX.Element;
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsGroupedSwitcher = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const react_1 = require("react");
6
+ const ps_core_components_1 = require("@pallasoft/ps-core-components");
7
+ const ps_design_system_1 = require("@pallasoft/ps-design-system");
8
+ const ps_icons_1 = require("@pallasoft/ps-icons");
9
+ const react_aria_components_1 = require("react-aria-components");
10
+ const ps_grouped_switcher_config_1 = require("../constants/ps-grouped-switcher-config");
11
+ const ps_grouped_switcher_service_1 = require("../services/ps-grouped-switcher-service");
12
+ const PsGroupedSwitcher = ({ allOption, ariaLabel, className, emptyText, getItemKey, getItemSearchText, groups, onSelect, onTogglePin, pinLabel = 'Pin', pinnedKeys = [], pinnedLabel = 'Pinned', recentKeys = [], recentLabel = 'Recent', renderItem, renderSectionLabel, renderSelected, searchPlaceholder, selectedKey, unpinLabel = 'Unpin', }) => {
13
+ const [isOpen, setIsOpen] = (0, react_1.useState)(false);
14
+ const [query, setQuery] = (0, react_1.useState)('');
15
+ const zIndex = (0, ps_core_components_1.usePsLayerZIndex)(ps_design_system_1.PallasoftZIndex.Dropdown);
16
+ const items = groups.flatMap((group) => group.items);
17
+ const selected = items.find((item) => getItemKey(item) === selectedKey);
18
+ const sections = ps_grouped_switcher_service_1.PsGroupedSwitcherService.sections({
19
+ getItemKey,
20
+ getItemSearchText,
21
+ groups,
22
+ pinnedKeys,
23
+ pinnedLabel,
24
+ query,
25
+ recentKeys,
26
+ recentLabel,
27
+ });
28
+ const showAll = allOption !== undefined && query.trim() === '';
29
+ const isAllSelected = selectedKey === '' || selected === undefined;
30
+ const choose = (key) => {
31
+ onSelect(key);
32
+ setIsOpen(false);
33
+ setQuery('');
34
+ };
35
+ const onSelectionChange = (selection) => {
36
+ const ids = selection === 'all' ? [] : Array.from(selection);
37
+ if (ids.length === 0) {
38
+ choose(selectedKey);
39
+ return;
40
+ }
41
+ const text = String(ids[0]);
42
+ choose(text === ps_grouped_switcher_config_1.PsGroupedSwitcherConfig.allKey ? '' : ps_grouped_switcher_service_1.PsGroupedSwitcherService.itemKey(text));
43
+ };
44
+ return ((0, jsx_runtime_1.jsxs)(react_aria_components_1.DialogTrigger, { isOpen: isOpen, onOpenChange: (next) => {
45
+ setIsOpen(next);
46
+ if (!next)
47
+ setQuery('');
48
+ }, children: [(0, jsx_runtime_1.jsxs)(react_aria_components_1.Button, { "aria-label": ariaLabel, className: ps_design_system_1.PallasoftClassNameService.merge('ps-input-control ps-focus-ring relative flex w-full min-w-0 items-center pr-10 text-left text-sm font-semibold', className), children: [(0, jsx_runtime_1.jsx)("span", { className: 'min-w-0 flex-1', children: renderSelected(isAllSelected ? null : selected) }), (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'pointer-events-none absolute right-3 top-1/2 h-4 w-4 -translate-y-1/2 text-primary', iconKey: ps_icons_1.PsIconKey.Chevron })] }), (0, jsx_runtime_1.jsx)(react_aria_components_1.Popover, { className: 'ps-floating-panel w-[max(var(--trigger-width),min(32rem,calc(100vw-2rem)))] min-w-[var(--trigger-width)] overflow-hidden p-0 outline-none', offset: 6, placement: 'bottom start', style: { zIndex }, children: (0, jsx_runtime_1.jsx)(react_aria_components_1.Dialog, { "aria-label": ariaLabel, className: 'flex max-h-[min(34rem,75vh)] flex-col outline-none', children: (0, jsx_runtime_1.jsxs)(react_aria_components_1.Autocomplete, { filter: () => true, inputValue: query, onInputChange: setQuery, children: [(0, jsx_runtime_1.jsxs)(react_aria_components_1.SearchField, { "aria-label": searchPlaceholder, autoFocus: true, className: 'relative block border-b border-line p-2', children: [(0, jsx_runtime_1.jsx)(react_aria_components_1.Input, { className: 'ps-input-control ps-focus-ring w-full pl-10 text-sm font-semibold', placeholder: searchPlaceholder }), (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'pointer-events-none absolute left-5 top-1/2 h-4 w-4 -translate-y-1/2 text-ink/45', iconKey: ps_icons_1.PsIconKey.Search })] }), (0, jsx_runtime_1.jsxs)(react_aria_components_1.ListBox, { "aria-label": ariaLabel, className: 'ps-scrollbar-hidden min-h-0 flex-1 overflow-y-auto p-1.5 outline-none', onSelectionChange: onSelectionChange, renderEmptyState: () => ((0, jsx_runtime_1.jsx)("div", { className: 'px-3 py-6 text-center text-sm text-ink/55', children: emptyText })), selectedKeys: [
49
+ isAllSelected
50
+ ? ps_grouped_switcher_config_1.PsGroupedSwitcherConfig.allKey
51
+ : ps_grouped_switcher_service_1.PsGroupedSwitcherService.selectedOptionId(sections, selectedKey, getItemKey),
52
+ ], selectionMode: 'single', children: [showAll ? ((0, jsx_runtime_1.jsx)(react_aria_components_1.ListBoxSection, { children: (0, jsx_runtime_1.jsx)(react_aria_components_1.ListBoxItem, { className: 'ps-floating-option min-h-12 text-ink', id: ps_grouped_switcher_config_1.PsGroupedSwitcherConfig.allKey, textValue: 'all', children: allOption.content }) })) : null, sections.map((section) => ((0, jsx_runtime_1.jsxs)(react_aria_components_1.ListBoxSection, { id: section.key, children: [(0, jsx_runtime_1.jsx)(react_aria_components_1.Header, { className: 'px-3 pb-1 pt-3 text-xs font-semibold tracking-wide text-ink/50 uppercase', children: renderSectionLabel === undefined
53
+ ? section.label
54
+ : renderSectionLabel(section.key, section.label) }), section.items.map((item) => {
55
+ const key = getItemKey(item);
56
+ const isPinned = pinnedKeys.includes(key);
57
+ return ((0, jsx_runtime_1.jsxs)(react_aria_components_1.ListBoxItem, { className: 'ps-floating-option group min-h-14 text-ink', id: ps_grouped_switcher_service_1.PsGroupedSwitcherService.optionId(section.key, key), textValue: getItemSearchText(item), children: [(0, jsx_runtime_1.jsx)("span", { className: 'min-w-0 flex-1', children: renderItem(item) }), onTogglePin === undefined ? null : ((0, jsx_runtime_1.jsx)(react_aria_components_1.Button, { "aria-label": isPinned ? unpinLabel : pinLabel, className: ps_design_system_1.PallasoftClassNameService.merge('ps-focus-ring flex h-9 w-9 shrink-0 items-center justify-center rounded-ps-control text-ink/35 transition-opacity hover:text-primary focus-visible:opacity-100 group-hover:opacity-100 group-focus-within:opacity-100', isPinned ? 'opacity-100' : 'opacity-0'), onPress: () => onTogglePin(key), children: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: ps_design_system_1.PallasoftClassNameService.merge('h-4 w-4', isPinned ? 'text-primary' : ''), iconKey: ps_icons_1.PsIconKey.Star }) }))] }, key));
58
+ })] }, section.key)))] })] }) }) })] }));
59
+ };
60
+ exports.PsGroupedSwitcher = PsGroupedSwitcher;
@@ -0,0 +1,6 @@
1
+ export declare const PsGroupedSwitcherConfig: {
2
+ readonly allKey: "all";
3
+ readonly maxRecent: 3;
4
+ readonly pinnedSection: "pinned";
5
+ readonly recentSection: "recent";
6
+ };
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsGroupedSwitcherConfig = void 0;
4
+ exports.PsGroupedSwitcherConfig = {
5
+ allKey: 'all',
6
+ maxRecent: 3,
7
+ pinnedSection: 'pinned',
8
+ recentSection: 'recent',
9
+ };
@@ -0,0 +1,56 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from 'react';
3
+ import { usePsLayerZIndex } from '@pallasoft/ps-core-components';
4
+ import { PallasoftClassNameService, PallasoftZIndex } from '@pallasoft/ps-design-system';
5
+ import { PsIcon, PsIconKey } from '@pallasoft/ps-icons';
6
+ import { Autocomplete, Button, Dialog, DialogTrigger, Header, Input, ListBox, ListBoxItem, ListBoxSection, Popover, SearchField, } from 'react-aria-components';
7
+ import { PsGroupedSwitcherConfig } from '../constants/ps-grouped-switcher-config';
8
+ import { PsGroupedSwitcherService } from '../services/ps-grouped-switcher-service';
9
+ export const PsGroupedSwitcher = ({ allOption, ariaLabel, className, emptyText, getItemKey, getItemSearchText, groups, onSelect, onTogglePin, pinLabel = 'Pin', pinnedKeys = [], pinnedLabel = 'Pinned', recentKeys = [], recentLabel = 'Recent', renderItem, renderSectionLabel, renderSelected, searchPlaceholder, selectedKey, unpinLabel = 'Unpin', }) => {
10
+ const [isOpen, setIsOpen] = useState(false);
11
+ const [query, setQuery] = useState('');
12
+ const zIndex = usePsLayerZIndex(PallasoftZIndex.Dropdown);
13
+ const items = groups.flatMap((group) => group.items);
14
+ const selected = items.find((item) => getItemKey(item) === selectedKey);
15
+ const sections = PsGroupedSwitcherService.sections({
16
+ getItemKey,
17
+ getItemSearchText,
18
+ groups,
19
+ pinnedKeys,
20
+ pinnedLabel,
21
+ query,
22
+ recentKeys,
23
+ recentLabel,
24
+ });
25
+ const showAll = allOption !== undefined && query.trim() === '';
26
+ const isAllSelected = selectedKey === '' || selected === undefined;
27
+ const choose = (key) => {
28
+ onSelect(key);
29
+ setIsOpen(false);
30
+ setQuery('');
31
+ };
32
+ const onSelectionChange = (selection) => {
33
+ const ids = selection === 'all' ? [] : Array.from(selection);
34
+ if (ids.length === 0) {
35
+ choose(selectedKey);
36
+ return;
37
+ }
38
+ const text = String(ids[0]);
39
+ choose(text === PsGroupedSwitcherConfig.allKey ? '' : PsGroupedSwitcherService.itemKey(text));
40
+ };
41
+ return (_jsxs(DialogTrigger, { isOpen: isOpen, onOpenChange: (next) => {
42
+ setIsOpen(next);
43
+ if (!next)
44
+ setQuery('');
45
+ }, children: [_jsxs(Button, { "aria-label": ariaLabel, className: PallasoftClassNameService.merge('ps-input-control ps-focus-ring relative flex w-full min-w-0 items-center pr-10 text-left text-sm font-semibold', className), children: [_jsx("span", { className: 'min-w-0 flex-1', children: renderSelected(isAllSelected ? null : selected) }), _jsx(PsIcon, { "aria-hidden": 'true', className: 'pointer-events-none absolute right-3 top-1/2 h-4 w-4 -translate-y-1/2 text-primary', iconKey: PsIconKey.Chevron })] }), _jsx(Popover, { className: 'ps-floating-panel w-[max(var(--trigger-width),min(32rem,calc(100vw-2rem)))] min-w-[var(--trigger-width)] overflow-hidden p-0 outline-none', offset: 6, placement: 'bottom start', style: { zIndex }, children: _jsx(Dialog, { "aria-label": ariaLabel, className: 'flex max-h-[min(34rem,75vh)] flex-col outline-none', children: _jsxs(Autocomplete, { filter: () => true, inputValue: query, onInputChange: setQuery, children: [_jsxs(SearchField, { "aria-label": searchPlaceholder, autoFocus: true, className: 'relative block border-b border-line p-2', children: [_jsx(Input, { className: 'ps-input-control ps-focus-ring w-full pl-10 text-sm font-semibold', placeholder: searchPlaceholder }), _jsx(PsIcon, { "aria-hidden": 'true', className: 'pointer-events-none absolute left-5 top-1/2 h-4 w-4 -translate-y-1/2 text-ink/45', iconKey: PsIconKey.Search })] }), _jsxs(ListBox, { "aria-label": ariaLabel, className: 'ps-scrollbar-hidden min-h-0 flex-1 overflow-y-auto p-1.5 outline-none', onSelectionChange: onSelectionChange, renderEmptyState: () => (_jsx("div", { className: 'px-3 py-6 text-center text-sm text-ink/55', children: emptyText })), selectedKeys: [
46
+ isAllSelected
47
+ ? PsGroupedSwitcherConfig.allKey
48
+ : PsGroupedSwitcherService.selectedOptionId(sections, selectedKey, getItemKey),
49
+ ], selectionMode: 'single', children: [showAll ? (_jsx(ListBoxSection, { children: _jsx(ListBoxItem, { className: 'ps-floating-option min-h-12 text-ink', id: PsGroupedSwitcherConfig.allKey, textValue: 'all', children: allOption.content }) })) : null, sections.map((section) => (_jsxs(ListBoxSection, { id: section.key, children: [_jsx(Header, { className: 'px-3 pb-1 pt-3 text-xs font-semibold tracking-wide text-ink/50 uppercase', children: renderSectionLabel === undefined
50
+ ? section.label
51
+ : renderSectionLabel(section.key, section.label) }), section.items.map((item) => {
52
+ const key = getItemKey(item);
53
+ const isPinned = pinnedKeys.includes(key);
54
+ return (_jsxs(ListBoxItem, { className: 'ps-floating-option group min-h-14 text-ink', id: PsGroupedSwitcherService.optionId(section.key, key), textValue: getItemSearchText(item), children: [_jsx("span", { className: 'min-w-0 flex-1', children: renderItem(item) }), onTogglePin === undefined ? null : (_jsx(Button, { "aria-label": isPinned ? unpinLabel : pinLabel, className: PallasoftClassNameService.merge('ps-focus-ring flex h-9 w-9 shrink-0 items-center justify-center rounded-ps-control text-ink/35 transition-opacity hover:text-primary focus-visible:opacity-100 group-hover:opacity-100 group-focus-within:opacity-100', isPinned ? 'opacity-100' : 'opacity-0'), onPress: () => onTogglePin(key), children: _jsx(PsIcon, { "aria-hidden": 'true', className: PallasoftClassNameService.merge('h-4 w-4', isPinned ? 'text-primary' : ''), iconKey: PsIconKey.Star }) }))] }, key));
55
+ })] }, section.key)))] })] }) }) })] }));
56
+ };
@@ -0,0 +1,6 @@
1
+ export const PsGroupedSwitcherConfig = {
2
+ allKey: 'all',
3
+ maxRecent: 3,
4
+ pinnedSection: 'pinned',
5
+ recentSection: 'recent',
6
+ };
package/dist/esm/index.js CHANGED
@@ -56,3 +56,10 @@ export * from './interfaces/ps-checkbox-matrix-row';
56
56
  export * from './services/ps-checkbox-matrix-service';
57
57
  export * from './components/ps-rating-input';
58
58
  export * from './constants/ps-rating-input-size';
59
+ export * from './components/ps-grouped-switcher';
60
+ export * from './constants/ps-grouped-switcher-config';
61
+ export * from './interfaces/ps-grouped-switcher-all-option';
62
+ export * from './interfaces/ps-grouped-switcher-group';
63
+ export * from './interfaces/ps-grouped-switcher-section';
64
+ export * from './interfaces/ps-grouped-switcher-sections-input';
65
+ export * from './services/ps-grouped-switcher-service';
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,60 @@
1
+ import { PsGroupedSwitcherConfig } from '../constants/ps-grouped-switcher-config';
2
+ export class PsGroupedSwitcherService {
3
+ static optionId(sectionKey, itemKey) {
4
+ return `${sectionKey}:${itemKey}`;
5
+ }
6
+ static selectedOptionId(sections, selectedKey, getItemKey) {
7
+ const section = sections.find((candidate) => candidate.items.some((item) => getItemKey(item) === selectedKey));
8
+ return section === undefined ? '' : PsGroupedSwitcherService.optionId(section.key, selectedKey);
9
+ }
10
+ static itemKey(optionId) {
11
+ return optionId.slice(optionId.indexOf(':') + 1);
12
+ }
13
+ static sections(input) {
14
+ const needle = input.query.trim().toLowerCase();
15
+ const words = needle.split(/\s+/).filter(Boolean);
16
+ const all = input.groups.flatMap((group) => group.items);
17
+ const byKey = new Map(all.map((item) => [input.getItemKey(item), item]));
18
+ const pick = (keys) => keys.flatMap((key) => {
19
+ const item = byKey.get(key);
20
+ return item === undefined ? [] : [item];
21
+ });
22
+ const pinned = pick(input.pinnedKeys);
23
+ const recent = pick(input.recentKeys.filter((key) => !input.pinnedKeys.includes(key))).slice(0, PsGroupedSwitcherConfig.maxRecent);
24
+ const groups = input.groups
25
+ .map((group) => ({
26
+ items: needle === ''
27
+ ? group.items
28
+ : group.items.filter((item) => {
29
+ const haystack = `${group.label} ${input.getItemSearchText(item)}`.toLowerCase();
30
+ return words.every((word) => haystack.includes(word));
31
+ }),
32
+ key: group.key,
33
+ label: group.label,
34
+ }))
35
+ .filter((group) => group.items.length > 0);
36
+ if (needle !== '')
37
+ return groups;
38
+ return [
39
+ ...(pinned.length > 0
40
+ ? [
41
+ {
42
+ items: pinned,
43
+ key: PsGroupedSwitcherConfig.pinnedSection,
44
+ label: input.pinnedLabel,
45
+ },
46
+ ]
47
+ : []),
48
+ ...(recent.length > 0
49
+ ? [
50
+ {
51
+ items: recent,
52
+ key: PsGroupedSwitcherConfig.recentSection,
53
+ label: input.recentLabel,
54
+ },
55
+ ]
56
+ : []),
57
+ ...groups,
58
+ ];
59
+ }
60
+ }
package/dist/index.d.ts CHANGED
@@ -56,3 +56,10 @@ export * from './interfaces/ps-checkbox-matrix-row';
56
56
  export * from './services/ps-checkbox-matrix-service';
57
57
  export * from './components/ps-rating-input';
58
58
  export * from './constants/ps-rating-input-size';
59
+ export * from './components/ps-grouped-switcher';
60
+ export * from './constants/ps-grouped-switcher-config';
61
+ export * from './interfaces/ps-grouped-switcher-all-option';
62
+ export * from './interfaces/ps-grouped-switcher-group';
63
+ export * from './interfaces/ps-grouped-switcher-section';
64
+ export * from './interfaces/ps-grouped-switcher-sections-input';
65
+ export * from './services/ps-grouped-switcher-service';
package/dist/index.js CHANGED
@@ -72,3 +72,10 @@ __exportStar(require("./interfaces/ps-checkbox-matrix-row"), exports);
72
72
  __exportStar(require("./services/ps-checkbox-matrix-service"), exports);
73
73
  __exportStar(require("./components/ps-rating-input"), exports);
74
74
  __exportStar(require("./constants/ps-rating-input-size"), exports);
75
+ __exportStar(require("./components/ps-grouped-switcher"), exports);
76
+ __exportStar(require("./constants/ps-grouped-switcher-config"), exports);
77
+ __exportStar(require("./interfaces/ps-grouped-switcher-all-option"), exports);
78
+ __exportStar(require("./interfaces/ps-grouped-switcher-group"), exports);
79
+ __exportStar(require("./interfaces/ps-grouped-switcher-section"), exports);
80
+ __exportStar(require("./interfaces/ps-grouped-switcher-sections-input"), exports);
81
+ __exportStar(require("./services/ps-grouped-switcher-service"), exports);
@@ -0,0 +1,4 @@
1
+ import type { ReactNode } from 'react';
2
+ export interface PsGroupedSwitcherAllOption {
3
+ content: ReactNode;
4
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,5 @@
1
+ export interface PsGroupedSwitcherGroup<T> {
2
+ items: T[];
3
+ key: string;
4
+ label: string;
5
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,5 @@
1
+ export interface PsGroupedSwitcherSection<T> {
2
+ items: T[];
3
+ key: string;
4
+ label: string;
5
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,11 @@
1
+ import type { PsGroupedSwitcherGroup } from './ps-grouped-switcher-group';
2
+ export interface PsGroupedSwitcherSectionsInput<T> {
3
+ getItemKey: (item: T) => string;
4
+ getItemSearchText: (item: T) => string;
5
+ groups: PsGroupedSwitcherGroup<T>[];
6
+ pinnedKeys: string[];
7
+ pinnedLabel: string;
8
+ query: string;
9
+ recentKeys: string[];
10
+ recentLabel: string;
11
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,8 @@
1
+ import type { PsGroupedSwitcherSection } from '../interfaces/ps-grouped-switcher-section';
2
+ import type { PsGroupedSwitcherSectionsInput } from '../interfaces/ps-grouped-switcher-sections-input';
3
+ export declare class PsGroupedSwitcherService {
4
+ static optionId(sectionKey: string, itemKey: string): string;
5
+ static selectedOptionId<T>(sections: PsGroupedSwitcherSection<T>[], selectedKey: string, getItemKey: (item: T) => string): string;
6
+ static itemKey(optionId: string): string;
7
+ static sections<T>(input: PsGroupedSwitcherSectionsInput<T>): PsGroupedSwitcherSection<T>[];
8
+ }
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsGroupedSwitcherService = void 0;
4
+ const ps_grouped_switcher_config_1 = require("../constants/ps-grouped-switcher-config");
5
+ class PsGroupedSwitcherService {
6
+ static optionId(sectionKey, itemKey) {
7
+ return `${sectionKey}:${itemKey}`;
8
+ }
9
+ static selectedOptionId(sections, selectedKey, getItemKey) {
10
+ const section = sections.find((candidate) => candidate.items.some((item) => getItemKey(item) === selectedKey));
11
+ return section === undefined ? '' : PsGroupedSwitcherService.optionId(section.key, selectedKey);
12
+ }
13
+ static itemKey(optionId) {
14
+ return optionId.slice(optionId.indexOf(':') + 1);
15
+ }
16
+ static sections(input) {
17
+ const needle = input.query.trim().toLowerCase();
18
+ const words = needle.split(/\s+/).filter(Boolean);
19
+ const all = input.groups.flatMap((group) => group.items);
20
+ const byKey = new Map(all.map((item) => [input.getItemKey(item), item]));
21
+ const pick = (keys) => keys.flatMap((key) => {
22
+ const item = byKey.get(key);
23
+ return item === undefined ? [] : [item];
24
+ });
25
+ const pinned = pick(input.pinnedKeys);
26
+ const recent = pick(input.recentKeys.filter((key) => !input.pinnedKeys.includes(key))).slice(0, ps_grouped_switcher_config_1.PsGroupedSwitcherConfig.maxRecent);
27
+ const groups = input.groups
28
+ .map((group) => ({
29
+ items: needle === ''
30
+ ? group.items
31
+ : group.items.filter((item) => {
32
+ const haystack = `${group.label} ${input.getItemSearchText(item)}`.toLowerCase();
33
+ return words.every((word) => haystack.includes(word));
34
+ }),
35
+ key: group.key,
36
+ label: group.label,
37
+ }))
38
+ .filter((group) => group.items.length > 0);
39
+ if (needle !== '')
40
+ return groups;
41
+ return [
42
+ ...(pinned.length > 0
43
+ ? [
44
+ {
45
+ items: pinned,
46
+ key: ps_grouped_switcher_config_1.PsGroupedSwitcherConfig.pinnedSection,
47
+ label: input.pinnedLabel,
48
+ },
49
+ ]
50
+ : []),
51
+ ...(recent.length > 0
52
+ ? [
53
+ {
54
+ items: recent,
55
+ key: ps_grouped_switcher_config_1.PsGroupedSwitcherConfig.recentSection,
56
+ label: input.recentLabel,
57
+ },
58
+ ]
59
+ : []),
60
+ ...groups,
61
+ ];
62
+ }
63
+ }
64
+ exports.PsGroupedSwitcherService = PsGroupedSwitcherService;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pallasoft/ps-input-components",
3
- "version": "3.2.0",
3
+ "version": "3.3.1",
4
4
  "private": false,
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/esm/index.js",