@dbcdk/react-components 0.0.95 → 0.0.97
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/alert/Alert.d.ts +13 -0
- package/dist/components/forms/input/Input.d.ts +2 -1
- package/dist/components/forms/input-container/InputContainer.d.ts +3 -1
- package/dist/components/forms/text-area/Textarea.d.ts +1 -1
- package/dist/components/stack/Stack.d.ts +11 -3
- package/dist/components/table/Table.d.ts +1 -1
- package/dist/components/table/Table.types.d.ts +3 -0
- package/dist/components/table/components/TableHeader.d.ts +2 -1
- package/dist/index.cjs +12750 -0
- package/dist/index.css +7149 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +12641 -78
- package/dist/tanstack.cjs +2674 -0
- package/dist/tanstack.css +1267 -0
- package/dist/tanstack.js +2650 -3
- package/dist/themes/dbc.css +3 -0
- package/dist/themes/forfatterweb.css +2 -0
- package/package.json +11 -10
- package/dist/assets/logo.js +0 -2
- package/dist/components/__stories__/_data/table.d.ts +0 -15
- package/dist/components/__stories__/_data/table.js +0 -55
- package/dist/components/__stories__/_data/tabs.d.ts +0 -9
- package/dist/components/__stories__/_data/tabs.js +0 -31
- package/dist/components/__stories__/story-components/Colors.d.ts +0 -11
- package/dist/components/__stories__/story-components/Colors.js +0 -96
- package/dist/components/__stories__/story-components/Colors.module.css +0 -27
- package/dist/components/__stories__/story-components/ComponentSizes.d.ts +0 -2
- package/dist/components/__stories__/story-components/ComponentSizes.js +0 -26
- package/dist/components/__stories__/story-components/Elevation.d.ts +0 -2
- package/dist/components/__stories__/story-components/Elevation.js +0 -49
- package/dist/components/__stories__/story-components/Flex.d.ts +0 -2
- package/dist/components/__stories__/story-components/Flex.js +0 -177
- package/dist/components/__stories__/story-components/Flex.module.css +0 -317
- package/dist/components/__stories__/story-components/Spacing.d.ts +0 -6
- package/dist/components/__stories__/story-components/Spacing.js +0 -76
- package/dist/components/__stories__/story-components/Spacing.module.css +0 -154
- package/dist/components/accordion/Accordion.js +0 -70
- package/dist/components/accordion/Accordion.module.css +0 -28
- package/dist/components/accordion/components/AccordionRow.js +0 -53
- package/dist/components/accordion/components/AccordionRow.module.css +0 -90
- package/dist/components/app-header/AppHeader.js +0 -5
- package/dist/components/app-header/AppHeader.module.css +0 -74
- package/dist/components/attribute-chip/AttributeChip.js +0 -5
- package/dist/components/attribute-chip/AttributeChip.module.css +0 -65
- package/dist/components/avatar/Avatar.js +0 -48
- package/dist/components/avatar/Avatar.module.css +0 -91
- package/dist/components/breadcrumbs/Breadcrumbs.js +0 -6
- package/dist/components/breadcrumbs/Breadcrumbs.module.css +0 -80
- package/dist/components/button/Button.js +0 -81
- package/dist/components/button/Button.module.css +0 -249
- package/dist/components/button-select/ButtonSelect.js +0 -7
- package/dist/components/button-select/ButtonSelect.module.css +0 -40
- package/dist/components/card/Card.js +0 -71
- package/dist/components/card/Card.module.css +0 -160
- package/dist/components/card/components/CardMeta.js +0 -26
- package/dist/components/card/components/CardMeta.module.css +0 -55
- package/dist/components/card-container/CardContainer.js +0 -6
- package/dist/components/card-container/CardContainer.module.css +0 -61
- package/dist/components/chip/Chip.js +0 -31
- package/dist/components/chip/Chip.module.css +0 -236
- package/dist/components/circle/Circle.js +0 -5
- package/dist/components/circle/Circle.module.css +0 -128
- package/dist/components/clear-button/ClearButton.js +0 -13
- package/dist/components/clear-button/ClearButton.module.css +0 -26
- package/dist/components/code-block/CodeBlock.js +0 -58
- package/dist/components/code-block/CodeBlock.module.css +0 -124
- package/dist/components/copy-button/CopyButton.js +0 -78
- package/dist/components/copy-button/CopyButton.module.css +0 -22
- package/dist/components/datetime-picker/DateTimePicker.js +0 -403
- package/dist/components/datetime-picker/DateTimePicker.module.css +0 -155
- package/dist/components/datetime-picker/dateTimeHelpers.js +0 -248
- package/dist/components/divider/Divider.js +0 -12
- package/dist/components/filter-field/FilterField.js +0 -191
- package/dist/components/filter-field/FilterField.module.css +0 -379
- package/dist/components/filtering/chip-multi-toggle/ChipMultiToggle.js +0 -52
- package/dist/components/filtering/chip-multi-toggle/ChipMultiToggle.module.css +0 -59
- package/dist/components/forms/checkbox/Checkbox.js +0 -26
- package/dist/components/forms/checkbox/Checkbox.module.css +0 -99
- package/dist/components/forms/checkbox-group/CheckboxGroup.js +0 -75
- package/dist/components/forms/checkbox-group/CheckboxGroup.module.css +0 -115
- package/dist/components/forms/form-select/FormSelect.js +0 -86
- package/dist/components/forms/form-select/FormSelect.module.css +0 -236
- package/dist/components/forms/input/Input.js +0 -77
- package/dist/components/forms/input/Input.module.css +0 -468
- package/dist/components/forms/input-container/InputContainer.js +0 -15
- package/dist/components/forms/input-container/InputContainer.module.css +0 -56
- package/dist/components/forms/multi-select/MultiSelect.js +0 -122
- package/dist/components/forms/radio-buttons/RadioButton.js +0 -26
- package/dist/components/forms/radio-buttons/RadioButtonGroup.js +0 -19
- package/dist/components/forms/radio-buttons/RadioButtons.module.css +0 -118
- package/dist/components/forms/select/Select.js +0 -185
- package/dist/components/forms/select/Select.module.css +0 -32
- package/dist/components/forms/text-area/Textarea.js +0 -47
- package/dist/components/forms/text-area/Textarea.module.css +0 -70
- package/dist/components/forms/typeahead/Typeahead.js +0 -668
- package/dist/components/forms/typeahead/Typeahead.module.css +0 -38
- package/dist/components/grid/Grid.js +0 -23
- package/dist/components/grid/Grid.module.css +0 -35
- package/dist/components/headline/CollapsibleHeadline.js +0 -29
- package/dist/components/headline/Headline.js +0 -26
- package/dist/components/headline/Headline.module.css +0 -165
- package/dist/components/hyperlink/Hyperlink.js +0 -40
- package/dist/components/hyperlink/Hyperlink.module.css +0 -107
- package/dist/components/icon/Icon.js +0 -14
- package/dist/components/icon/Icon.module.css +0 -36
- package/dist/components/interval-select/IntervalSelect.js +0 -99
- package/dist/components/json-viewer/HighlightedText.js +0 -6
- package/dist/components/json-viewer/JsonNode.js +0 -30
- package/dist/components/json-viewer/JsonViewer.js +0 -68
- package/dist/components/json-viewer/JsonViewer.module.css +0 -346
- package/dist/components/json-viewer/types.js +0 -1
- package/dist/components/json-viewer/useClipboardStatus.js +0 -11
- package/dist/components/json-viewer/utils.js +0 -125
- package/dist/components/menu/Menu.js +0 -165
- package/dist/components/menu/Menu.module.css +0 -211
- package/dist/components/meta-bar/MetaBar.js +0 -9
- package/dist/components/meta-bar/MetaBar.module.css +0 -27
- package/dist/components/nav-bar/NavBar.js +0 -29
- package/dist/components/nav-bar/NavBar.module.css +0 -200
- package/dist/components/overlay/fade-overlay/FadeOverlay.js +0 -8
- package/dist/components/overlay/fade-overlay/FadeOverlay.module.css +0 -54
- package/dist/components/overlay/modal/Modal.js +0 -115
- package/dist/components/overlay/modal/Modal.module.css +0 -109
- package/dist/components/overlay/modal/provider/ModalProvider.js +0 -73
- package/dist/components/overlay/side-panel/SidePanel.js +0 -83
- package/dist/components/overlay/side-panel/SidePanel.module.css +0 -177
- package/dist/components/overlay/side-panel/useSidePanel.js +0 -11
- package/dist/components/overlay/tooltip/Tooltip.js +0 -17
- package/dist/components/overlay/tooltip/Tooltip.module.css +0 -104
- package/dist/components/overlay/tooltip/TooltipProvider.js +0 -255
- package/dist/components/overlay/tooltip/useTooltipTrigger.js +0 -118
- package/dist/components/page/Page.js +0 -11
- package/dist/components/page/Page.module.css +0 -76
- package/dist/components/page-layout/PageLayout.js +0 -76
- package/dist/components/page-layout/PageLayout.module.css +0 -236
- package/dist/components/page-layout/components/layout-footer/LayoutFooter.js +0 -27
- package/dist/components/page-layout/components/layout-footer/LayoutFooter.module.css +0 -89
- package/dist/components/page-layout/components/page-layout-hero/PageLayoutHero.js +0 -14
- package/dist/components/page-layout/components/page-layout-hero/PageLayoutHero.module.css +0 -84
- package/dist/components/pagination/Pagination.js +0 -56
- package/dist/components/pagination/Pagination.module.css +0 -25
- package/dist/components/panel/Panel.js +0 -7
- package/dist/components/panel/Panel.module.css +0 -29
- package/dist/components/popover/Popover.js +0 -257
- package/dist/components/popover/Popover.module.css +0 -54
- package/dist/components/search-box/SearchBox.js +0 -170
- package/dist/components/search-box/SearchBox.module.css +0 -21
- package/dist/components/segmented-progress-bar/SegmentedProgressBar.js +0 -48
- package/dist/components/segmented-progress-bar/SegmentedProgressBar.module.css +0 -167
- package/dist/components/sidebar/Sidebar.js +0 -6
- package/dist/components/sidebar/components/SidebarItem.js +0 -8
- package/dist/components/sidebar/components/SidebarItem.module.css +0 -0
- package/dist/components/sidebar/components/expandable-sidebar-item/ExpandableSidebarItem.js +0 -63
- package/dist/components/sidebar/components/expandable-sidebar-item/ExpandableSidebarItem.module.css +0 -29
- package/dist/components/sidebar/components/sidebar-container/SidebarContainer.js +0 -153
- package/dist/components/sidebar/components/sidebar-container/SidebarContainer.module.css +0 -260
- package/dist/components/sidebar/components/sidebar-item-content/SidebarItemContent.js +0 -18
- package/dist/components/sidebar/components/sidebar-item-content/SidebarItemContent.module.css +0 -106
- package/dist/components/sidebar/components/sidebar-items/SidebarItems.js +0 -26
- package/dist/components/sidebar/components/sidebar-items/SidebarItems.module.css +0 -20
- package/dist/components/sidebar/components/sidenav-filteirng/SidenavFiltering.js +0 -30
- package/dist/components/sidebar/providers/SidebarProvider.js +0 -229
- package/dist/components/skeleton-loader/SkeletonLoader.js +0 -73
- package/dist/components/skeleton-loader/skeleton-loader-item/SkeletonLoaderItem.js +0 -13
- package/dist/components/skeleton-loader/skeleton-loader-item/SkeletonLoaderItem.module.css +0 -51
- package/dist/components/split-button/SplitButton.js +0 -10
- package/dist/components/split-button/SplitButton.module.css +0 -32
- package/dist/components/split-pane/SplitPane.js +0 -107
- package/dist/components/split-pane/SplitPane.module.css +0 -111
- package/dist/components/split-pane/provider/SplitPaneContext.js +0 -124
- package/dist/components/stack/Stack.js +0 -19
- package/dist/components/state-page/StatePage.js +0 -20
- package/dist/components/state-page/StatePage.module.css +0 -9
- package/dist/components/state-page/empty.js +0 -2
- package/dist/components/state-page/error.js +0 -2
- package/dist/components/state-page/notFound.js +0 -2
- package/dist/components/sticky-footer-layout/StickyFooterLayout.js +0 -64
- package/dist/components/table/Table.js +0 -50
- package/dist/components/table/Table.module.css +0 -536
- package/dist/components/table/Table.types.js +0 -1
- package/dist/components/table/TanstackTable.js +0 -111
- package/dist/components/table/components/TableBody.js +0 -10
- package/dist/components/table/components/TableHeader.js +0 -7
- package/dist/components/table/components/TableHeaderCell.js +0 -24
- package/dist/components/table/components/TableLoadingBody.js +0 -26
- package/dist/components/table/components/TablePagination.js +0 -1
- package/dist/components/table/components/TableRow.js +0 -54
- package/dist/components/table/components/TableSelectionCell.js +0 -16
- package/dist/components/table/components/column-resizer/ColumnResizer.js +0 -5
- package/dist/components/table/components/column-resizer/ColumnResizer.module.css +0 -22
- package/dist/components/table/components/empty-state/EmptyState.js +0 -23
- package/dist/components/table/components/empty-state/EmptyState.module.css +0 -4
- package/dist/components/table/components/table-settings/TableSettings.js +0 -63
- package/dist/components/table/hooks/useTableRowInteractions.js +0 -30
- package/dist/components/table/table.classes.js +0 -23
- package/dist/components/table/table.utils.js +0 -47
- package/dist/components/table/tanstackTable.utils.js +0 -175
- package/dist/components/tabs/Tabs.js +0 -125
- package/dist/components/tabs/Tabs.module.css +0 -204
- package/dist/components/theme-button/ThemeButton.js +0 -23
- package/dist/components/toast/Toast.js +0 -20
- package/dist/components/toast/Toast.module.css +0 -161
- package/dist/components/toast/provider/ToastProvider.js +0 -70
- package/dist/components/user-display/UserDisplay.js +0 -6
- package/dist/components/user-display/UserDisplay.module.css +0 -25
- package/dist/constants/severity.js +0 -24
- package/dist/constants/severity.types.js +0 -1
- package/dist/constants/sizes.js +0 -7
- package/dist/hooks/useDeviceSize.js +0 -32
- package/dist/hooks/useListNavigation.js +0 -234
- package/dist/hooks/usePagination.js +0 -140
- package/dist/hooks/useSorting.js +0 -118
- package/dist/hooks/useTableData.js +0 -45
- package/dist/hooks/useTableSelection.js +0 -164
- package/dist/hooks/useTableSettings.js +0 -71
- package/dist/hooks/useTheme.js +0 -66
- package/dist/hooks/useTimeDuration.js +0 -68
- package/dist/hooks/useViewportFill.js +0 -77
- package/dist/styles/animation.js +0 -5
- package/dist/styles/themes/types.js +0 -1
- package/dist/types/a11y-props.types.js +0 -1
- package/dist/types/sizes.types.js +0 -1
- package/dist/utils/arrays/nested-filtering.js +0 -48
- package/dist/utils/date/formatDate.js +0 -51
- package/dist/utils/localStorage.utils.js +0 -78
- package/dist/utils/text/get-highlighted-segments.js +0 -46
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
3
|
-
const EMPTY_IDS = new Set();
|
|
4
|
-
function safeParseIds(raw) {
|
|
5
|
-
if (!raw)
|
|
6
|
-
return null;
|
|
7
|
-
try {
|
|
8
|
-
const parsed = JSON.parse(raw);
|
|
9
|
-
if (!Array.isArray(parsed))
|
|
10
|
-
return null;
|
|
11
|
-
return parsed.filter((v) => typeof v === 'string' || typeof v === 'number');
|
|
12
|
-
}
|
|
13
|
-
catch {
|
|
14
|
-
return null;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
function serializeIds(ids) {
|
|
18
|
-
return JSON.stringify(Array.from(ids));
|
|
19
|
-
}
|
|
20
|
-
function areSetsEqual(a, b) {
|
|
21
|
-
if (a.size !== b.size)
|
|
22
|
-
return false;
|
|
23
|
-
for (const value of a) {
|
|
24
|
-
if (!b.has(value))
|
|
25
|
-
return false;
|
|
26
|
-
}
|
|
27
|
-
return true;
|
|
28
|
-
}
|
|
29
|
-
export function useTableSelection({ storageKey, items, getId, initialSelectedIds, onSelectionChange, selectionMode = 'single', pruneToItems = false, storage = 'session', }) {
|
|
30
|
-
const resolvedInitialSelectedIds = initialSelectedIds !== null && initialSelectedIds !== void 0 ? initialSelectedIds : EMPTY_IDS;
|
|
31
|
-
const [selectedIds, setSelectedIds] = useState(resolvedInitialSelectedIds);
|
|
32
|
-
const [hydrated, setHydrated] = useState(false);
|
|
33
|
-
const lastWrittenRef = useRef(null);
|
|
34
|
-
const itemsById = useMemo(() => {
|
|
35
|
-
const map = new Map();
|
|
36
|
-
for (const item of items) {
|
|
37
|
-
map.set(getId(item), item);
|
|
38
|
-
}
|
|
39
|
-
return map;
|
|
40
|
-
}, [items, getId]);
|
|
41
|
-
useEffect(() => {
|
|
42
|
-
if (typeof window === 'undefined')
|
|
43
|
-
return;
|
|
44
|
-
const storageApi = storage === 'local' ? window.localStorage : window.sessionStorage;
|
|
45
|
-
if (!storageKey) {
|
|
46
|
-
setSelectedIds(prev => areSetsEqual(prev, resolvedInitialSelectedIds) ? prev : new Set(resolvedInitialSelectedIds));
|
|
47
|
-
setHydrated(true);
|
|
48
|
-
lastWrittenRef.current = null;
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
const parsed = safeParseIds(storageApi.getItem(storageKey));
|
|
52
|
-
const next = new Set(parsed !== null && parsed !== void 0 ? parsed : Array.from(resolvedInitialSelectedIds));
|
|
53
|
-
setSelectedIds(prev => (areSetsEqual(prev, next) ? prev : next));
|
|
54
|
-
lastWrittenRef.current = serializeIds(next);
|
|
55
|
-
setHydrated(true);
|
|
56
|
-
}, [storage, storageKey, resolvedInitialSelectedIds]);
|
|
57
|
-
useEffect(() => {
|
|
58
|
-
if (!pruneToItems)
|
|
59
|
-
return;
|
|
60
|
-
const visibleIds = new Set(Array.from(itemsById.keys()));
|
|
61
|
-
setSelectedIds(prev => {
|
|
62
|
-
if (prev.size === 0)
|
|
63
|
-
return prev;
|
|
64
|
-
if (visibleIds.size === 0)
|
|
65
|
-
return prev;
|
|
66
|
-
const next = new Set();
|
|
67
|
-
for (const id of prev) {
|
|
68
|
-
if (visibleIds.has(id))
|
|
69
|
-
next.add(id);
|
|
70
|
-
}
|
|
71
|
-
return areSetsEqual(prev, next) ? prev : next;
|
|
72
|
-
});
|
|
73
|
-
}, [pruneToItems, itemsById]);
|
|
74
|
-
const selectedItemMap = useMemo(() => {
|
|
75
|
-
const map = new Map();
|
|
76
|
-
for (const id of selectedIds) {
|
|
77
|
-
const item = itemsById.get(id);
|
|
78
|
-
if (item !== undefined)
|
|
79
|
-
map.set(id, item);
|
|
80
|
-
}
|
|
81
|
-
return map;
|
|
82
|
-
}, [selectedIds, itemsById]);
|
|
83
|
-
const selectedItems = useMemo(() => Array.from(selectedItemMap.values()), [selectedItemMap]);
|
|
84
|
-
const allSelected = useMemo(() => {
|
|
85
|
-
if (items.length === 0)
|
|
86
|
-
return false;
|
|
87
|
-
return items.every(item => selectedIds.has(getId(item)));
|
|
88
|
-
}, [items, selectedIds, getId]);
|
|
89
|
-
const anySelected = useMemo(() => selectedIds.size > 0, [selectedIds]);
|
|
90
|
-
useEffect(() => {
|
|
91
|
-
if (!hydrated)
|
|
92
|
-
return;
|
|
93
|
-
if (typeof window === 'undefined')
|
|
94
|
-
return;
|
|
95
|
-
if (storageKey) {
|
|
96
|
-
const storageApi = storage === 'local' ? window.localStorage : window.sessionStorage;
|
|
97
|
-
const nextStr = serializeIds(selectedIds);
|
|
98
|
-
if (lastWrittenRef.current !== nextStr) {
|
|
99
|
-
storageApi.setItem(storageKey, nextStr);
|
|
100
|
-
lastWrittenRef.current = nextStr;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
onSelectionChange === null || onSelectionChange === void 0 ? void 0 : onSelectionChange({ selectedIds, selectedItems });
|
|
104
|
-
}, [hydrated, onSelectionChange, selectedIds, selectedItems, storage, storageKey]);
|
|
105
|
-
const toggleId = useCallback((id, selected) => {
|
|
106
|
-
setSelectedIds(prev => {
|
|
107
|
-
const next = new Set(prev);
|
|
108
|
-
const isSelected = next.has(id);
|
|
109
|
-
const shouldSelect = selected === undefined ? !isSelected : selected;
|
|
110
|
-
if (selectionMode === 'single') {
|
|
111
|
-
next.clear();
|
|
112
|
-
if (shouldSelect)
|
|
113
|
-
next.add(id);
|
|
114
|
-
return areSetsEqual(prev, next) ? prev : next;
|
|
115
|
-
}
|
|
116
|
-
if (shouldSelect)
|
|
117
|
-
next.add(id);
|
|
118
|
-
else
|
|
119
|
-
next.delete(id);
|
|
120
|
-
return areSetsEqual(prev, next) ? prev : next;
|
|
121
|
-
});
|
|
122
|
-
}, [selectionMode]);
|
|
123
|
-
const toggleItem = useCallback((item) => {
|
|
124
|
-
toggleId(getId(item));
|
|
125
|
-
}, [toggleId, getId]);
|
|
126
|
-
const selectOnly = useCallback((id) => {
|
|
127
|
-
setSelectedIds(prev => {
|
|
128
|
-
const next = new Set([id]);
|
|
129
|
-
return areSetsEqual(prev, next) ? prev : next;
|
|
130
|
-
});
|
|
131
|
-
}, []);
|
|
132
|
-
const clearSelection = useCallback(() => {
|
|
133
|
-
setSelectedIds(prev => (prev.size === 0 ? prev : new Set()));
|
|
134
|
-
}, []);
|
|
135
|
-
const toggleAll = useCallback((selected) => {
|
|
136
|
-
if (!selected) {
|
|
137
|
-
clearSelection();
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
140
|
-
if (selectionMode === 'single') {
|
|
141
|
-
const first = items[0];
|
|
142
|
-
const next = first ? new Set([getId(first)]) : new Set();
|
|
143
|
-
setSelectedIds(prev => (areSetsEqual(prev, next) ? prev : next));
|
|
144
|
-
return;
|
|
145
|
-
}
|
|
146
|
-
const next = new Set();
|
|
147
|
-
for (const item of items) {
|
|
148
|
-
next.add(getId(item));
|
|
149
|
-
}
|
|
150
|
-
setSelectedIds(prev => (areSetsEqual(prev, next) ? prev : next));
|
|
151
|
-
}, [clearSelection, getId, items, selectionMode]);
|
|
152
|
-
return {
|
|
153
|
-
selectedIds,
|
|
154
|
-
selectedItems,
|
|
155
|
-
selectedItemMap,
|
|
156
|
-
toggleItem,
|
|
157
|
-
toggleId,
|
|
158
|
-
selectOnly,
|
|
159
|
-
clearSelection,
|
|
160
|
-
allSelected,
|
|
161
|
-
anySelected,
|
|
162
|
-
toggleAll,
|
|
163
|
-
};
|
|
164
|
-
}
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
3
|
-
import { readLocalStorage, writeLocalStorage } from '../utils/localStorage.utils';
|
|
4
|
-
function getDefaultVisibleIds(tableColumns) {
|
|
5
|
-
var _a;
|
|
6
|
-
return ((_a = tableColumns === null || tableColumns === void 0 ? void 0 : tableColumns.filter(c => { var _a; return !((_a = c.meta) === null || _a === void 0 ? void 0 : _a.hidden); }).map(c => c.id).filter(Boolean)) !== null && _a !== void 0 ? _a : []);
|
|
7
|
-
}
|
|
8
|
-
function mergeDefaults(stored, defaults) {
|
|
9
|
-
const viewMode = (stored === null || stored === void 0 ? void 0 : stored.viewMode) === 'compact' || (stored === null || stored === void 0 ? void 0 : stored.viewMode) === 'wrapped'
|
|
10
|
-
? stored.viewMode
|
|
11
|
-
: defaults.viewMode;
|
|
12
|
-
const visibleColumnIds = Array.isArray(stored === null || stored === void 0 ? void 0 : stored.visibleColumnIds) && stored.visibleColumnIds.length > 0
|
|
13
|
-
? stored.visibleColumnIds
|
|
14
|
-
: defaults.visibleColumnIds;
|
|
15
|
-
return { viewMode, visibleColumnIds };
|
|
16
|
-
}
|
|
17
|
-
export const localStorageTableSettingsStorage = {
|
|
18
|
-
get: key => {
|
|
19
|
-
const v = readLocalStorage(key);
|
|
20
|
-
return v && typeof v === 'object' ? v : undefined;
|
|
21
|
-
},
|
|
22
|
-
set: (key, next) => {
|
|
23
|
-
writeLocalStorage(key, next);
|
|
24
|
-
},
|
|
25
|
-
};
|
|
26
|
-
export function useTableSettings({ storageKey, tableColumns, defaultViewMode = 'compact', defaultVisibleColumnIds, storage = localStorageTableSettingsStorage, }) {
|
|
27
|
-
const defaults = useMemo(() => {
|
|
28
|
-
return {
|
|
29
|
-
viewMode: defaultViewMode,
|
|
30
|
-
visibleColumnIds: defaultVisibleColumnIds !== null && defaultVisibleColumnIds !== void 0 ? defaultVisibleColumnIds : getDefaultVisibleIds(tableColumns),
|
|
31
|
-
};
|
|
32
|
-
}, [defaultViewMode, defaultVisibleColumnIds, tableColumns]);
|
|
33
|
-
const [state, setState] = useState(defaults);
|
|
34
|
-
useEffect(() => {
|
|
35
|
-
const stored = storage.get(storageKey);
|
|
36
|
-
const next = mergeDefaults(stored, defaults);
|
|
37
|
-
setState(next);
|
|
38
|
-
}, [storageKey, storage, defaults]);
|
|
39
|
-
const persist = useCallback((next) => storage.set(storageKey, next), [storage, storageKey]);
|
|
40
|
-
const setViewMode = useCallback((mode) => {
|
|
41
|
-
setState(prev => {
|
|
42
|
-
if (prev.viewMode === mode)
|
|
43
|
-
return prev;
|
|
44
|
-
const next = { ...prev, viewMode: mode };
|
|
45
|
-
persist(next);
|
|
46
|
-
return next;
|
|
47
|
-
});
|
|
48
|
-
}, [persist]);
|
|
49
|
-
const toggleViewMode = useCallback(() => {
|
|
50
|
-
setState(prev => {
|
|
51
|
-
const nextMode = prev.viewMode === 'wrapped' ? 'compact' : 'wrapped';
|
|
52
|
-
const next = { ...prev, viewMode: nextMode };
|
|
53
|
-
persist(next);
|
|
54
|
-
return next;
|
|
55
|
-
});
|
|
56
|
-
}, [persist]);
|
|
57
|
-
const setVisibleColumnIds = useCallback((ids) => {
|
|
58
|
-
setState(prev => {
|
|
59
|
-
const next = { ...prev, visibleColumnIds: ids };
|
|
60
|
-
persist(next);
|
|
61
|
-
return next;
|
|
62
|
-
});
|
|
63
|
-
}, [persist]);
|
|
64
|
-
return {
|
|
65
|
-
viewMode: state.viewMode,
|
|
66
|
-
toggleViewMode,
|
|
67
|
-
setViewMode,
|
|
68
|
-
visibleColumnIds: state.visibleColumnIds,
|
|
69
|
-
setVisibleColumnIds,
|
|
70
|
-
};
|
|
71
|
-
}
|
package/dist/hooks/useTheme.js
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
import { useCallback, useEffect, useState } from 'react';
|
|
3
|
-
const THEME_VARIANTS = ['light', 'dark', 'system'];
|
|
4
|
-
const STORAGE_KEY = 'dbc_theme';
|
|
5
|
-
function isThemeVariant(x) {
|
|
6
|
-
return !!x && THEME_VARIANTS.includes(x);
|
|
7
|
-
}
|
|
8
|
-
function getCookie(name) {
|
|
9
|
-
const match = document.cookie.match(new RegExp(`(?:^|; )${name}=([^;]*)`));
|
|
10
|
-
return match ? decodeURIComponent(match[1]) : null;
|
|
11
|
-
}
|
|
12
|
-
function persistTheme(id) {
|
|
13
|
-
try {
|
|
14
|
-
localStorage.setItem(STORAGE_KEY, id);
|
|
15
|
-
}
|
|
16
|
-
catch {
|
|
17
|
-
console.error('Failed to access localStorage');
|
|
18
|
-
}
|
|
19
|
-
try {
|
|
20
|
-
document.cookie = `${STORAGE_KEY}=${encodeURIComponent(id)}; Path=/; Max-Age=${60 * 60 * 24 * 365}`;
|
|
21
|
-
}
|
|
22
|
-
catch {
|
|
23
|
-
console.error('Failed to set theme cookie');
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
function getTheme() {
|
|
27
|
-
return document.documentElement.dataset.theme;
|
|
28
|
-
}
|
|
29
|
-
function applyTheme(id) {
|
|
30
|
-
document.documentElement.dataset.theme = id;
|
|
31
|
-
}
|
|
32
|
-
export function useTheme(initialTheme = 'system') {
|
|
33
|
-
const [theme, setTheme] = useState(null);
|
|
34
|
-
useEffect(() => {
|
|
35
|
-
const themeFromDataAttributes = getTheme();
|
|
36
|
-
let resolved = isThemeVariant(themeFromDataAttributes)
|
|
37
|
-
? themeFromDataAttributes
|
|
38
|
-
: initialTheme;
|
|
39
|
-
// Prefer cookie (SSR + client consistency)
|
|
40
|
-
const fromCookie = getCookie(STORAGE_KEY);
|
|
41
|
-
if (isThemeVariant(fromCookie)) {
|
|
42
|
-
resolved = fromCookie;
|
|
43
|
-
}
|
|
44
|
-
else {
|
|
45
|
-
// fallback to localStorage
|
|
46
|
-
try {
|
|
47
|
-
const fromStorage = localStorage.getItem(STORAGE_KEY);
|
|
48
|
-
if (isThemeVariant(fromStorage))
|
|
49
|
-
resolved = fromStorage;
|
|
50
|
-
}
|
|
51
|
-
catch {
|
|
52
|
-
console.error('Failed to access localStorage');
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
applyTheme(resolved);
|
|
56
|
-
setTheme(resolved);
|
|
57
|
-
persistTheme(resolved);
|
|
58
|
-
}, [initialTheme]);
|
|
59
|
-
const switchTheme = useCallback((id) => {
|
|
60
|
-
applyTheme(id);
|
|
61
|
-
setTheme(id);
|
|
62
|
-
persistTheme(id);
|
|
63
|
-
return id;
|
|
64
|
-
}, []);
|
|
65
|
-
return { theme, switchTheme };
|
|
66
|
-
}
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
import { useEffect, useMemo, useState } from 'react';
|
|
3
|
-
function defaultDuration(ms) {
|
|
4
|
-
if (!Number.isFinite(ms) || ms < 0)
|
|
5
|
-
return '—';
|
|
6
|
-
const sec = Math.floor(ms / 1000) % 60;
|
|
7
|
-
const min = Math.floor(ms / (1000 * 60)) % 60;
|
|
8
|
-
const hr = Math.floor(ms / (1000 * 60 * 60));
|
|
9
|
-
if (hr > 0) {
|
|
10
|
-
return `${hr}t ${min}m ${sec}s`;
|
|
11
|
-
}
|
|
12
|
-
if (min > 0) {
|
|
13
|
-
return `${min}m ${sec}s`;
|
|
14
|
-
}
|
|
15
|
-
return `${sec}s`;
|
|
16
|
-
}
|
|
17
|
-
export function useTimeDuration({ start, end, dateFormat = {
|
|
18
|
-
year: '2-digit',
|
|
19
|
-
month: '2-digit',
|
|
20
|
-
day: '2-digit',
|
|
21
|
-
hour: '2-digit',
|
|
22
|
-
minute: '2-digit',
|
|
23
|
-
second: '2-digit',
|
|
24
|
-
}, fallback = '—', liveUpdate = false, formatDuration = defaultDuration, }) {
|
|
25
|
-
const [hydrated, setHydrated] = useState(false);
|
|
26
|
-
const [tick, setTick] = useState(0);
|
|
27
|
-
useEffect(() => setHydrated(true), []);
|
|
28
|
-
useEffect(() => {
|
|
29
|
-
if (!liveUpdate)
|
|
30
|
-
return;
|
|
31
|
-
if (end && end.getTime() <= Date.now())
|
|
32
|
-
return;
|
|
33
|
-
const timer = setInterval(() => {
|
|
34
|
-
if (end && end.getTime() <= Date.now()) {
|
|
35
|
-
clearInterval(timer);
|
|
36
|
-
}
|
|
37
|
-
setTick(tick => tick + 1);
|
|
38
|
-
}, 1000);
|
|
39
|
-
return () => clearInterval(timer);
|
|
40
|
-
}, [liveUpdate, end]);
|
|
41
|
-
const started = useMemo(() => {
|
|
42
|
-
if (!start)
|
|
43
|
-
return fallback;
|
|
44
|
-
if (!hydrated)
|
|
45
|
-
return fallback;
|
|
46
|
-
return new Intl.DateTimeFormat('da-DK', dateFormat).format(start);
|
|
47
|
-
}, [start, hydrated, fallback, dateFormat]);
|
|
48
|
-
const ended = useMemo(() => {
|
|
49
|
-
if (!end)
|
|
50
|
-
return fallback;
|
|
51
|
-
if (!hydrated)
|
|
52
|
-
return fallback;
|
|
53
|
-
return new Intl.DateTimeFormat('da-DK', dateFormat).format(end);
|
|
54
|
-
}, [end, hydrated, fallback, dateFormat]);
|
|
55
|
-
const duration = useMemo(() => {
|
|
56
|
-
if (!start || !hydrated)
|
|
57
|
-
return fallback;
|
|
58
|
-
const now = Date.now();
|
|
59
|
-
if (!end) {
|
|
60
|
-
return formatDuration(now - start.getTime());
|
|
61
|
-
}
|
|
62
|
-
if (end.getTime() > now) {
|
|
63
|
-
return formatDuration(end.getTime() - now);
|
|
64
|
-
}
|
|
65
|
-
return formatDuration(end.getTime() - start.getTime());
|
|
66
|
-
}, [start, end, fallback, hydrated, formatDuration, tick]);
|
|
67
|
-
return { started, ended, isFinished: !!start && !!end, duration, hydrated };
|
|
68
|
-
}
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, } from 'react';
|
|
3
|
-
const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect;
|
|
4
|
-
export function useViewportFill(ref, { bottomOffset = 0, min = 120, includeMarginTop = false, watchRef } = {}) {
|
|
5
|
-
const [maxHeight, setMaxHeight] = useState(min);
|
|
6
|
-
const raf = useRef(null);
|
|
7
|
-
const measure = useCallback(() => {
|
|
8
|
-
if (typeof window === 'undefined' || !ref.current)
|
|
9
|
-
return;
|
|
10
|
-
const el = ref.current;
|
|
11
|
-
const rect = el.getBoundingClientRect();
|
|
12
|
-
let top = rect.top;
|
|
13
|
-
if (includeMarginTop) {
|
|
14
|
-
const marginTop = parseFloat(window.getComputedStyle(el).marginTop || '0') || 0;
|
|
15
|
-
top -= marginTop;
|
|
16
|
-
}
|
|
17
|
-
const next = Math.max(min, Math.floor(window.innerHeight - bottomOffset - top));
|
|
18
|
-
setMaxHeight(prev => (prev === next ? prev : next));
|
|
19
|
-
}, [ref, bottomOffset, min, includeMarginTop]);
|
|
20
|
-
const scheduleMeasure = useCallback(() => {
|
|
21
|
-
if (typeof window === 'undefined')
|
|
22
|
-
return;
|
|
23
|
-
if (raf.current != null) {
|
|
24
|
-
cancelAnimationFrame(raf.current);
|
|
25
|
-
}
|
|
26
|
-
raf.current = window.requestAnimationFrame(() => {
|
|
27
|
-
raf.current = null;
|
|
28
|
-
measure();
|
|
29
|
-
});
|
|
30
|
-
}, [measure]);
|
|
31
|
-
useIsomorphicLayoutEffect(() => {
|
|
32
|
-
measure();
|
|
33
|
-
}, [measure]);
|
|
34
|
-
useEffect(() => {
|
|
35
|
-
var _a;
|
|
36
|
-
if (typeof window === 'undefined' || !ref.current)
|
|
37
|
-
return;
|
|
38
|
-
const target = ref.current;
|
|
39
|
-
const extra = (_a = watchRef === null || watchRef === void 0 ? void 0 : watchRef.current) !== null && _a !== void 0 ? _a : null;
|
|
40
|
-
const parent = target.parentElement;
|
|
41
|
-
const onResize = () => scheduleMeasure();
|
|
42
|
-
const onTransitionOrAnimationEnd = () => scheduleMeasure();
|
|
43
|
-
window.addEventListener('resize', onResize);
|
|
44
|
-
document.addEventListener('transitionend', onTransitionOrAnimationEnd, true);
|
|
45
|
-
document.addEventListener('animationend', onTransitionOrAnimationEnd, true);
|
|
46
|
-
let resizeObserver = null;
|
|
47
|
-
if ('ResizeObserver' in window) {
|
|
48
|
-
resizeObserver = new ResizeObserver(() => scheduleMeasure());
|
|
49
|
-
resizeObserver.observe(target);
|
|
50
|
-
if (parent && parent !== target) {
|
|
51
|
-
resizeObserver.observe(parent);
|
|
52
|
-
}
|
|
53
|
-
if (extra && extra !== target && extra !== parent) {
|
|
54
|
-
resizeObserver.observe(extra);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
return () => {
|
|
58
|
-
window.removeEventListener('resize', onResize);
|
|
59
|
-
document.removeEventListener('transitionend', onTransitionOrAnimationEnd, true);
|
|
60
|
-
document.removeEventListener('animationend', onTransitionOrAnimationEnd, true);
|
|
61
|
-
resizeObserver === null || resizeObserver === void 0 ? void 0 : resizeObserver.disconnect();
|
|
62
|
-
if (raf.current != null) {
|
|
63
|
-
cancelAnimationFrame(raf.current);
|
|
64
|
-
raf.current = null;
|
|
65
|
-
}
|
|
66
|
-
};
|
|
67
|
-
}, [ref, watchRef, scheduleMeasure]);
|
|
68
|
-
const style = useMemo(() => ({
|
|
69
|
-
maxHeight,
|
|
70
|
-
overflow: 'auto',
|
|
71
|
-
}), [maxHeight]);
|
|
72
|
-
return {
|
|
73
|
-
maxHeight,
|
|
74
|
-
style,
|
|
75
|
-
recompute: measure,
|
|
76
|
-
};
|
|
77
|
-
}
|
package/dist/styles/animation.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
const normalize = (s) => s.normalize('NFKC').toLowerCase().trim();
|
|
2
|
-
const toTerms = (q) => (Array.isArray(q) ? q : q.split(/\s+/)).map(normalize).filter(Boolean);
|
|
3
|
-
const valueToSearchable = (value) => typeof value === 'string'
|
|
4
|
-
? value
|
|
5
|
-
: Array.isArray(value) || typeof value === 'object'
|
|
6
|
-
? JSON.stringify(value)
|
|
7
|
-
: value == null
|
|
8
|
-
? ''
|
|
9
|
-
: String(value);
|
|
10
|
-
const itemMatches = (item, keys, terms, mode) => {
|
|
11
|
-
if (terms.length === 0)
|
|
12
|
-
return true;
|
|
13
|
-
const haystack = keys
|
|
14
|
-
.map(k => valueToSearchable(item[k]))
|
|
15
|
-
.join(' ')
|
|
16
|
-
.toString();
|
|
17
|
-
const h = normalize(haystack);
|
|
18
|
-
const hit = (t) => h.includes(t);
|
|
19
|
-
return mode === 'AND' ? terms.every(hit) : terms.some(hit);
|
|
20
|
-
};
|
|
21
|
-
export function nestedFiltering(items, opts) {
|
|
22
|
-
const { keys, query, includeParents = true, childrenKey, mode = 'AND' } = opts;
|
|
23
|
-
const ck = (childrenKey !== null && childrenKey !== void 0 ? childrenKey : 'children');
|
|
24
|
-
const terms = toTerms(query);
|
|
25
|
-
const searchNested = (list) => {
|
|
26
|
-
const out = [];
|
|
27
|
-
for (const item of list) {
|
|
28
|
-
const children = item[ck] || [];
|
|
29
|
-
const filteredChildren = searchNested(children);
|
|
30
|
-
const matchedSelf = itemMatches(item, keys, terms, mode);
|
|
31
|
-
if (matchedSelf) {
|
|
32
|
-
const clone = { ...item, [ck]: filteredChildren };
|
|
33
|
-
out.push(clone);
|
|
34
|
-
}
|
|
35
|
-
else if (filteredChildren.length) {
|
|
36
|
-
if (includeParents) {
|
|
37
|
-
const clone = { ...item, [ck]: filteredChildren };
|
|
38
|
-
out.push(clone);
|
|
39
|
-
}
|
|
40
|
-
else {
|
|
41
|
-
out.push(...filteredChildren);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
return out;
|
|
46
|
-
};
|
|
47
|
-
return searchNested(items);
|
|
48
|
-
}
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Formats a Date as "dd/mm/yyyy" or "dd/mm/yyyy hh:mm:ss".
|
|
3
|
-
* Optionally returns user-friendly Danish labels like "I dag" or "I går".
|
|
4
|
-
*
|
|
5
|
-
* Examples:
|
|
6
|
-
* - formatDate(new Date(), { userFriendlyLabel: true }) => "I dag"
|
|
7
|
-
* - formatDate(new Date(), { userFriendlyLabel: true, showTime: true }) => "I dag, kl. 14:30"
|
|
8
|
-
* - formatDate(date, { showTime: true }) => "10.03.2026 14:30"
|
|
9
|
-
*
|
|
10
|
-
* @param date - The Date or date-parsable value to format
|
|
11
|
-
* @param options.showTime - If true, append time "hh:mm" or "hh:mm:ss"
|
|
12
|
-
* @param options.showSeconds - If true, append seconds when showTime is enabled
|
|
13
|
-
* @param options.userFriendlyLabel - If true, use "I dag" / "I går" when applicable
|
|
14
|
-
* @returns A formatted string
|
|
15
|
-
*/
|
|
16
|
-
export function formatDate(date, options = {}) {
|
|
17
|
-
const d = date instanceof Date ? date : new Date(date);
|
|
18
|
-
if (isNaN(d.getTime()))
|
|
19
|
-
return '';
|
|
20
|
-
const { showTime = false, showSeconds = false, userFriendlyLabel = false } = options;
|
|
21
|
-
const pad = (n) => n.toString().padStart(2, '0');
|
|
22
|
-
const formatTime = () => {
|
|
23
|
-
const hours = pad(d.getHours());
|
|
24
|
-
const minutes = pad(d.getMinutes());
|
|
25
|
-
if (!showSeconds)
|
|
26
|
-
return `${hours}:${minutes}`;
|
|
27
|
-
const seconds = pad(d.getSeconds());
|
|
28
|
-
return `${hours}:${minutes}:${seconds}`;
|
|
29
|
-
};
|
|
30
|
-
const isSameCalendarDay = (a, b) => a.getDate() === b.getDate() &&
|
|
31
|
-
a.getMonth() === b.getMonth() &&
|
|
32
|
-
a.getFullYear() === b.getFullYear();
|
|
33
|
-
if (userFriendlyLabel) {
|
|
34
|
-
const now = new Date();
|
|
35
|
-
const yesterday = new Date(now);
|
|
36
|
-
yesterday.setDate(now.getDate() - 1);
|
|
37
|
-
if (isSameCalendarDay(d, now)) {
|
|
38
|
-
return showTime ? `I dag, kl. ${formatTime()}` : 'I dag';
|
|
39
|
-
}
|
|
40
|
-
if (isSameCalendarDay(d, yesterday)) {
|
|
41
|
-
return showTime ? `I går, kl. ${formatTime()}` : 'I går';
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
const day = pad(d.getDate());
|
|
45
|
-
const month = pad(d.getMonth() + 1);
|
|
46
|
-
const year = d.getFullYear();
|
|
47
|
-
const base = `${day}.${month}.${year}`;
|
|
48
|
-
if (!showTime)
|
|
49
|
-
return base;
|
|
50
|
-
return `${base} ${formatTime()}`;
|
|
51
|
-
}
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
function isBrowser() {
|
|
2
|
-
return typeof window !== 'undefined' && typeof window.localStorage !== 'undefined';
|
|
3
|
-
}
|
|
4
|
-
/**
|
|
5
|
-
* Safely read from localStorage.
|
|
6
|
-
* - Returns undefined if not in browser
|
|
7
|
-
* - Returns parsed JSON if value is JSON
|
|
8
|
-
* - Returns plain string if not JSON
|
|
9
|
-
* - Never throws
|
|
10
|
-
*/
|
|
11
|
-
export function readLocalStorage(key) {
|
|
12
|
-
if (!isBrowser())
|
|
13
|
-
return undefined;
|
|
14
|
-
try {
|
|
15
|
-
const raw = window.localStorage.getItem(key);
|
|
16
|
-
if (raw == null)
|
|
17
|
-
return undefined;
|
|
18
|
-
// Try parse once
|
|
19
|
-
try {
|
|
20
|
-
const parsed = JSON.parse(raw);
|
|
21
|
-
// Handle double-stringified JSON
|
|
22
|
-
if (typeof parsed === 'string') {
|
|
23
|
-
try {
|
|
24
|
-
return JSON.parse(parsed);
|
|
25
|
-
}
|
|
26
|
-
catch {
|
|
27
|
-
return parsed;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
return parsed;
|
|
31
|
-
}
|
|
32
|
-
catch {
|
|
33
|
-
// Not JSON — return as plain string
|
|
34
|
-
return raw;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
catch {
|
|
38
|
-
return undefined;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* Safely write to localStorage.
|
|
43
|
-
* - Automatically JSON.stringifies objects/arrays
|
|
44
|
-
* - Stores plain strings as-is
|
|
45
|
-
* - Never throws
|
|
46
|
-
*/
|
|
47
|
-
export function writeLocalStorage(key, value) {
|
|
48
|
-
if (!isBrowser())
|
|
49
|
-
return;
|
|
50
|
-
try {
|
|
51
|
-
if (value === undefined) {
|
|
52
|
-
window.localStorage.removeItem(key);
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
if (typeof value === 'string') {
|
|
56
|
-
window.localStorage.setItem(key, value);
|
|
57
|
-
}
|
|
58
|
-
else {
|
|
59
|
-
window.localStorage.setItem(key, JSON.stringify(value));
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
catch {
|
|
63
|
-
// ignore quota errors etc.
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* Remove key safely
|
|
68
|
-
*/
|
|
69
|
-
export function removeLocalStorage(key) {
|
|
70
|
-
if (!isBrowser())
|
|
71
|
-
return;
|
|
72
|
-
try {
|
|
73
|
-
window.localStorage.removeItem(key);
|
|
74
|
-
}
|
|
75
|
-
catch {
|
|
76
|
-
// ignore
|
|
77
|
-
}
|
|
78
|
-
}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
function normalizeTerms(query) {
|
|
2
|
-
const terms = Array.isArray(query) ? query : [query];
|
|
3
|
-
return [...new Set(terms.map(term => term.trim().toLowerCase()).filter(Boolean))].sort((a, b) => b.length - a.length);
|
|
4
|
-
}
|
|
5
|
-
export function getHighlightedSegments(text, query) {
|
|
6
|
-
const terms = normalizeTerms(query);
|
|
7
|
-
if (!text || terms.length === 0)
|
|
8
|
-
return [{ text, matched: false }];
|
|
9
|
-
const lower = text.toLowerCase();
|
|
10
|
-
const ranges = [];
|
|
11
|
-
for (const term of terms) {
|
|
12
|
-
let startIndex = 0;
|
|
13
|
-
while (startIndex < lower.length) {
|
|
14
|
-
const matchIndex = lower.indexOf(term, startIndex);
|
|
15
|
-
if (matchIndex === -1)
|
|
16
|
-
break;
|
|
17
|
-
ranges.push({ start: matchIndex, end: matchIndex + term.length });
|
|
18
|
-
startIndex = matchIndex + term.length;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
if (ranges.length === 0)
|
|
22
|
-
return [{ text, matched: false }];
|
|
23
|
-
ranges.sort((a, b) => a.start - b.start || a.end - b.end);
|
|
24
|
-
const mergedRanges = [];
|
|
25
|
-
for (const range of ranges) {
|
|
26
|
-
const previous = mergedRanges[mergedRanges.length - 1];
|
|
27
|
-
if (!previous || range.start > previous.end) {
|
|
28
|
-
mergedRanges.push({ ...range });
|
|
29
|
-
continue;
|
|
30
|
-
}
|
|
31
|
-
previous.end = Math.max(previous.end, range.end);
|
|
32
|
-
}
|
|
33
|
-
const segments = [];
|
|
34
|
-
let cursor = 0;
|
|
35
|
-
for (const range of mergedRanges) {
|
|
36
|
-
if (range.start > cursor) {
|
|
37
|
-
segments.push({ text: text.slice(cursor, range.start), matched: false });
|
|
38
|
-
}
|
|
39
|
-
segments.push({ text: text.slice(range.start, range.end), matched: true });
|
|
40
|
-
cursor = range.end;
|
|
41
|
-
}
|
|
42
|
-
if (cursor < text.length) {
|
|
43
|
-
segments.push({ text: text.slice(cursor), matched: false });
|
|
44
|
-
}
|
|
45
|
-
return segments.length > 0 ? segments : [{ text, matched: false }];
|
|
46
|
-
}
|