@nice2dev/icons 1.3.8 → 1.3.10
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/CHANGELOG.md +99 -0
- package/dist/cjs/NiceIconPicker.js +310 -92
- package/dist/cjs/NiceIconStacker.js +137 -120
- package/dist/cjs/NiceIconValue.js +44 -0
- package/dist/cjs/client.js +11 -0
- package/dist/cjs/iconHtml.js +104 -0
- package/dist/cjs/iconStack.js +74 -1
- package/dist/cjs/iconValue.js +473 -0
- package/dist/cjs/icons/actions.js +39 -8
- package/dist/cjs/icons/files.js +23 -2
- package/dist/cjs/icons/fintech.js +12 -5
- package/dist/cjs/icons/index.js +1 -0
- package/dist/cjs/icons/media.js +7 -4
- package/dist/cjs/icons/realestate.js +8 -7
- package/dist/cjs/icons/shapes.js +55 -4
- package/dist/cjs/index.js +12 -0
- package/dist/cjs/nicetodev-icons.css +1 -1
- package/dist/esm/NiceIconPicker.js +313 -95
- package/dist/esm/NiceIconStacker.js +140 -123
- package/dist/esm/NiceIconValue.js +41 -0
- package/dist/esm/client.js +4 -1
- package/dist/esm/iconHtml.js +100 -0
- package/dist/esm/iconStack.js +72 -2
- package/dist/esm/iconValue.js +470 -0
- package/dist/esm/icons/actions.js +39 -9
- package/dist/esm/icons/files.js +23 -2
- package/dist/esm/icons/fintech.js +12 -5
- package/dist/esm/icons/index.js +1 -1
- package/dist/esm/icons/media.js +8 -5
- package/dist/esm/icons/realestate.js +8 -7
- package/dist/esm/icons/shapes.js +55 -4
- package/dist/esm/index.js +5 -2
- package/dist/esm/nicetodev-icons.css +1 -1
- package/dist/types/NiceIconPicker.d.ts +72 -20
- package/dist/types/NiceIconStacker.d.ts +30 -9
- package/dist/types/NiceIconValue.d.ts +40 -0
- package/dist/types/client.d.ts +8 -2
- package/dist/types/iconHtml.d.ts +22 -0
- package/dist/types/iconStack.d.ts +18 -0
- package/dist/types/iconValue.d.ts +22 -0
- package/dist/types/icons/actions.d.ts +1 -0
- package/dist/types/icons/index.d.ts +1 -1
- package/dist/types/index.d.ts +8 -2
- package/package.json +2 -2
|
@@ -1,19 +1,46 @@
|
|
|
1
1
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
2
|
-
import { useRovingFocus, useNiceTranslation, useOverlayFocus } from '@nice2dev/ui-core';
|
|
3
|
-
import { forwardRef, useRef, useState, useMemo, useCallback, useImperativeHandle, useEffect } from 'react';
|
|
2
|
+
import { useRovingFocus, useNiceTranslation, useControllableState, useOverlayFocus } from '@nice2dev/ui-core';
|
|
3
|
+
import { forwardRef, useRef, useState, useId, useMemo, useCallback, useImperativeHandle, useEffect, useLayoutEffect } from 'react';
|
|
4
|
+
import { scopeIconHtmlIds, iconHtmlAttributes } from './iconHtml.js';
|
|
4
5
|
import { NtdSearch, NtdCheck } from './icons/actions.js';
|
|
5
6
|
import { NtdGrid } from './icons/files.js';
|
|
6
7
|
import { NtdInbox } from './icons/media.js';
|
|
7
|
-
import { NtdChevronDown } from './icons/navigation.js';
|
|
8
|
+
import { NtdArrowLeft, NtdChevronDown } from './icons/navigation.js';
|
|
8
9
|
import { NtdStar, NtdClock } from './icons/ui.js';
|
|
10
|
+
import { iconDisplayName, renderIconStack } from './iconStack.js';
|
|
11
|
+
import { parseIconValue, formatIconValue } from './iconValue.js';
|
|
12
|
+
import { NiceIconStacker } from './NiceIconStacker.js';
|
|
9
13
|
|
|
10
14
|
const MAX_HISTORY_ITEMS = 20;
|
|
15
|
+
/** `useLayoutEffect` in the browser, `useEffect` on the server (no warning there). */
|
|
16
|
+
const useIsoLayoutEffect = typeof window === 'undefined' ? useEffect : useLayoutEffect;
|
|
11
17
|
/* ─── Small icons: the package's own (until 1.2.0 these were pasted SVGs) ─── */
|
|
12
18
|
const Chevron = () => jsx(NtdChevronDown, { size: 12, variant: "outline" });
|
|
13
19
|
const SearchIcon = () => jsx(NtdSearch, { size: 14, variant: "outline" });
|
|
14
20
|
const StarIcon = ({ filled }) => (jsx(NtdStar, { size: 12, variant: filled ? 'solid' : 'outline' }));
|
|
15
21
|
const ClockIcon = () => jsx(NtdClock, { size: 12, variant: "outline" });
|
|
16
22
|
const CheckIcon = () => jsx(NtdCheck, { size: 10, variant: "outline" });
|
|
23
|
+
const NO_NAMES = [];
|
|
24
|
+
const NO_LAYERS = [];
|
|
25
|
+
/** The accent the picker draws with: the page's background, white without a theme. */
|
|
26
|
+
const PAGE_ACCENT = 'var(--nice-bg-primary, white)';
|
|
27
|
+
/** An entry's name as shown: its label, else the export name without `Ntd`. */
|
|
28
|
+
const entryLabel = (entry) => { var _a; return (_a = entry.label) !== null && _a !== void 0 ? _a : iconDisplayName(entry.name); };
|
|
29
|
+
/** The part of the window an element's popup can show in: the window, cut by every ancestor that clips or scrolls. */
|
|
30
|
+
function visibleBounds(el) {
|
|
31
|
+
const bounds = { top: 0, bottom: window.innerHeight, left: 0, right: window.innerWidth };
|
|
32
|
+
for (let p = el.parentElement; p && p !== document.body; p = p.parentElement) {
|
|
33
|
+
const style = window.getComputedStyle(p);
|
|
34
|
+
if (style.overflowX !== 'visible' || style.overflowY !== 'visible') {
|
|
35
|
+
const r = p.getBoundingClientRect();
|
|
36
|
+
bounds.top = Math.max(bounds.top, r.top);
|
|
37
|
+
bounds.bottom = Math.min(bounds.bottom, r.bottom);
|
|
38
|
+
bounds.left = Math.max(bounds.left, r.left);
|
|
39
|
+
bounds.right = Math.min(bounds.right, r.right);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return bounds;
|
|
43
|
+
}
|
|
17
44
|
/* ─── localStorage helpers ─── */
|
|
18
45
|
function loadFromStorage(key, fallback) {
|
|
19
46
|
if (typeof window === 'undefined') {
|
|
@@ -44,40 +71,43 @@ function saveToStorage(key, value) {
|
|
|
44
71
|
/**
|
|
45
72
|
* A fully-featured icon picker dropdown component.
|
|
46
73
|
*
|
|
47
|
-
* Provides a trigger button that opens a searchable, categorized icon grid.
|
|
48
|
-
*
|
|
49
|
-
*
|
|
74
|
+
* Provides a trigger button that opens a searchable, categorized icon grid. The value binds both
|
|
75
|
+
* ways — `value` / `defaultValue` + `onChange` (1.3.9) — as the icon's export name, a stack as
|
|
76
|
+
* short code (`Folder(Star@tr)`), JSON or HTML (`valueFormat`). With `stackable` the panel gets
|
|
77
|
+
* an "Overlays" tab where icons are stacked on the chosen one. Names show without `Ntd`; the
|
|
78
|
+
* trigger shows the icon alone or the icon and its name (`displayMode`). Keyboard: arrows in the
|
|
79
|
+
* grid, Escape to close.
|
|
50
80
|
*
|
|
51
81
|
* **Styling:** Uses CSS variables for theming (see `NiceIconPicker.css`).
|
|
52
82
|
* Override `--ntd-picker-bg`, `--ntd-picker-accent`, etc. on a parent element.
|
|
53
83
|
*
|
|
54
84
|
* @example
|
|
55
85
|
* ```tsx
|
|
56
|
-
* import { NiceIconPicker } from '@
|
|
57
|
-
*
|
|
58
|
-
* function IconSelectField({ icons }) {
|
|
59
|
-
* const [selected, setSelected] = useState<string | null>(null);
|
|
86
|
+
* import { NiceIconPicker } from '@nice2dev/icons';
|
|
60
87
|
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
* value={selected}
|
|
65
|
-
* onChange={(name) => setSelected(name)}
|
|
66
|
-
* variant="solid"
|
|
67
|
-
* placeholder="Pick icon..."
|
|
68
|
-
* />
|
|
69
|
-
* );
|
|
88
|
+
* function IconField({ icons, value, onChange }) {
|
|
89
|
+
* // stores `NtdFolder`, or `Folder(Star@tr)` with a star stacked in its corner
|
|
90
|
+
* return <NiceIconPicker icons={icons} value={value} onChange={onChange} stackable />;
|
|
70
91
|
* }
|
|
71
92
|
* ```
|
|
72
93
|
*/
|
|
73
|
-
const NiceIconPicker = /* @__PURE__ */ forwardRef(function NiceIconPicker({ icons, value
|
|
94
|
+
const NiceIconPicker = /* @__PURE__ */ forwardRef(function NiceIconPicker({ icons, value: valueProp, defaultValue, values: valuesProp, defaultValues, onChange, onChangeMulti, valueFormat: valueFormatProp, stackable = false, displayMode = 'icon-name', optionDisplayMode = 'icon', name, 'aria-label': ariaLabel, id, testId, 'data-testid': dataTestId, multiSelect = false, maxSelections, placeholder, variant = 'solid', iconProps, gridIconSize = 'md', triggerIconSize = 'sm', categories: categoriesProp, className, disabled = false, clearable = true, virtualized = false, virtualizedItemCount = 100, showHistory = false, showFavorites = false, storageKeyPrefix = 'ntd-icon-picker', enableDragAndDrop = false, onDragStart, onDragEnd, }, ref) {
|
|
74
95
|
var _a;
|
|
75
96
|
const tablistRef = useRef(null);
|
|
76
97
|
useRovingFocus(tablistRef, { orientation: 'horizontal', wrap: true, activateOnMove: true });
|
|
98
|
+
const viewsRef = useRef(null);
|
|
99
|
+
useRovingFocus(viewsRef, { orientation: 'horizontal', activateOnMove: true });
|
|
77
100
|
const { t } = useNiceTranslation();
|
|
78
101
|
const [isOpen, setIsOpen] = useState(false);
|
|
79
102
|
const [search, setSearch] = useState('');
|
|
80
103
|
const [activeCategory, setActiveCategory] = useState(null);
|
|
104
|
+
// the panel's view: the icon grid or the stack; `picking` = the grid chooses an icon to stack
|
|
105
|
+
const [view, setView] = useState('icons');
|
|
106
|
+
const [picking, setPicking] = useState(null);
|
|
107
|
+
const panelId = useId();
|
|
108
|
+
const valueFormat = valueFormatProp !== null && valueFormatProp !== void 0 ? valueFormatProp : 'name';
|
|
109
|
+
const canStack = stackable && !multiSelect;
|
|
110
|
+
const drawProps = useMemo(() => ({ accentColor: PAGE_ACCENT, ...iconProps, variant }), [iconProps, variant]);
|
|
81
111
|
// Virtualization state
|
|
82
112
|
const [visibleRange, setVisibleRange] = useState({ start: 0, end: virtualizedItemCount });
|
|
83
113
|
const gridWrapRef = useRef(null);
|
|
@@ -86,35 +116,125 @@ const NiceIconPicker = /* @__PURE__ */ forwardRef(function NiceIconPicker({ icon
|
|
|
86
116
|
const historyKey = `${storageKeyPrefix}-history`;
|
|
87
117
|
const [favorites, setFavorites] = useState(() => loadFromStorage(favoritesKey, []));
|
|
88
118
|
const [history, setHistory] = useState(() => loadFromStorage(historyKey, []));
|
|
89
|
-
//
|
|
90
|
-
const [
|
|
91
|
-
|
|
119
|
+
// The value binds both ways (1.3.9): controlled by `value`, or kept here from `defaultValue`.
|
|
120
|
+
const [value, setValue] = useControllableState({
|
|
121
|
+
value: valueProp,
|
|
122
|
+
defaultValue,
|
|
123
|
+
fallbackValue: null,
|
|
124
|
+
componentName: 'NiceIconPicker',
|
|
125
|
+
});
|
|
126
|
+
const [values, setValues] = useControllableState({
|
|
127
|
+
value: valuesProp,
|
|
128
|
+
defaultValue: defaultValues,
|
|
129
|
+
fallbackValue: NO_NAMES,
|
|
130
|
+
componentName: 'NiceIconPicker',
|
|
131
|
+
});
|
|
92
132
|
const rootRef = useRef(null);
|
|
93
133
|
const searchInputRef = useRef(null);
|
|
134
|
+
const stackerRef = useRef(null);
|
|
135
|
+
const serializerRef = useRef(null);
|
|
136
|
+
/* ─── Icons by name: exact, or without the Ntd a stored value may lack ─── */
|
|
137
|
+
const byName = useMemo(() => new Map(icons.map((e) => [e.name, e])), [icons]);
|
|
138
|
+
const entryOf = useCallback((n) => { var _a; return (n ? ((_a = byName.get(n)) !== null && _a !== void 0 ? _a : byName.get(`Ntd${n}`)) : undefined); }, [byName]);
|
|
139
|
+
const labelOf = useCallback((n) => {
|
|
140
|
+
const entry = entryOf(n);
|
|
141
|
+
return entry ? entryLabel(entry) : iconDisplayName(n);
|
|
142
|
+
}, [entryOf]);
|
|
143
|
+
const resolve = useCallback((n) => { var _a; return (_a = entryOf(n)) === null || _a === void 0 ? void 0 : _a.Component; }, [entryOf]);
|
|
144
|
+
/* ─── The single value as a stack — a plain icon is a stack of one ─── */
|
|
145
|
+
// The last value emitted with its layers: read back, the text would number the layers anew
|
|
146
|
+
// (it keeps no ids) and the stacker would lose the layer it has selected.
|
|
147
|
+
const [emitted, setEmitted] = useState(null);
|
|
148
|
+
const model = useMemo(() => {
|
|
149
|
+
if (!value) {
|
|
150
|
+
return NO_LAYERS;
|
|
151
|
+
}
|
|
152
|
+
if ((emitted === null || emitted === void 0 ? void 0 : emitted.value) === value) {
|
|
153
|
+
return emitted.layers;
|
|
154
|
+
}
|
|
155
|
+
// a name as is first: an older value may hold one of the code's signs
|
|
156
|
+
const exact = value.includes('<') ? undefined : entryOf(value);
|
|
157
|
+
if (exact) {
|
|
158
|
+
return [{ id: '1', icon: exact.name }];
|
|
159
|
+
}
|
|
160
|
+
const layers = parseIconValue(value);
|
|
161
|
+
if (!layers || !entryOf(layers[0].icon)) {
|
|
162
|
+
return NO_LAYERS;
|
|
163
|
+
}
|
|
164
|
+
// the code names icons without `Ntd`; the stacker and the grid know them by entry name
|
|
165
|
+
return layers.map((l) => {
|
|
166
|
+
const entry = entryOf(l.icon);
|
|
167
|
+
return entry && entry.name !== l.icon ? { ...l, icon: entry.name } : l;
|
|
168
|
+
});
|
|
169
|
+
}, [value, emitted, entryOf]);
|
|
170
|
+
// A stack waiting to be drawn into HTML: shown at once, emitted once drawn (below).
|
|
171
|
+
const [pending, setPending] = useState(null);
|
|
172
|
+
const shown = pending !== null && pending !== void 0 ? pending : model;
|
|
173
|
+
const base = shown[0];
|
|
174
|
+
/** The name a stack's code gives an icon: without `Ntd`, unless that is another icon's name. */
|
|
175
|
+
const shortName = useCallback((n) => {
|
|
176
|
+
const short = iconDisplayName(n);
|
|
177
|
+
return short !== n && !byName.has(short) ? short : n;
|
|
178
|
+
}, [byName]);
|
|
179
|
+
const onChangeRef = useRef(onChange);
|
|
180
|
+
onChangeRef.current = onChange;
|
|
181
|
+
const emit = useCallback((next, layers) => {
|
|
182
|
+
var _a, _b, _c;
|
|
183
|
+
setEmitted(next === null ? null : { value: next, layers });
|
|
184
|
+
setValue(next);
|
|
185
|
+
(_a = onChangeRef.current) === null || _a === void 0 ? void 0 : _a.call(onChangeRef, next, (_c = entryOf((_b = layers[0]) === null || _b === void 0 ? void 0 : _b.icon)) !== null && _c !== void 0 ? _c : null);
|
|
186
|
+
}, [setValue, entryOf]);
|
|
187
|
+
/** Sets the single value to a stack: written as `valueFormat` holds it, HTML once drawn. */
|
|
188
|
+
const commit = useCallback((layers) => {
|
|
189
|
+
if (layers.length === 0) {
|
|
190
|
+
setPending(null);
|
|
191
|
+
emit(null, NO_LAYERS);
|
|
192
|
+
}
|
|
193
|
+
else if (valueFormat === 'html') {
|
|
194
|
+
setPending(layers);
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
emit(formatIconValue(layers, valueFormat, { shortName }), layers);
|
|
198
|
+
}
|
|
199
|
+
}, [valueFormat, emit, shortName]);
|
|
200
|
+
// The HTML is the icon as drawn: the hidden copy below renders the pending stack, and its
|
|
201
|
+
// markup — ids renamed after the drawing — is the new value, emitted before the paint.
|
|
202
|
+
useIsoLayoutEffect(() => {
|
|
203
|
+
var _a;
|
|
204
|
+
if (!pending) {
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
const svg = (_a = serializerRef.current) === null || _a === void 0 ? void 0 : _a.querySelector('svg');
|
|
208
|
+
setPending(null);
|
|
209
|
+
if (svg) {
|
|
210
|
+
emit(scopeIconHtmlIds(svg.outerHTML), pending);
|
|
211
|
+
}
|
|
212
|
+
}, [pending, emit]);
|
|
213
|
+
const selectedValues = useMemo(() => (multiSelect ? values : base ? [base.icon] : NO_NAMES), [multiSelect, values, base]);
|
|
94
214
|
/* ─── Favorites helpers ─── */
|
|
95
|
-
const addFavorite = useCallback((
|
|
215
|
+
const addFavorite = useCallback((favorite) => {
|
|
96
216
|
setFavorites((prev) => {
|
|
97
|
-
if (prev.includes(
|
|
217
|
+
if (prev.includes(favorite)) {
|
|
98
218
|
return prev;
|
|
99
219
|
}
|
|
100
|
-
const next = [...prev,
|
|
220
|
+
const next = [...prev, favorite];
|
|
101
221
|
saveToStorage(favoritesKey, next);
|
|
102
222
|
return next;
|
|
103
223
|
});
|
|
104
224
|
}, [favoritesKey]);
|
|
105
|
-
const removeFavorite = useCallback((
|
|
225
|
+
const removeFavorite = useCallback((favorite) => {
|
|
106
226
|
setFavorites((prev) => {
|
|
107
|
-
const next = prev.filter((n) => n !==
|
|
227
|
+
const next = prev.filter((n) => n !== favorite);
|
|
108
228
|
saveToStorage(favoritesKey, next);
|
|
109
229
|
return next;
|
|
110
230
|
});
|
|
111
231
|
}, [favoritesKey]);
|
|
112
|
-
const toggleFavorite = useCallback((
|
|
113
|
-
if (favorites.includes(
|
|
114
|
-
removeFavorite(
|
|
232
|
+
const toggleFavorite = useCallback((favorite) => {
|
|
233
|
+
if (favorites.includes(favorite)) {
|
|
234
|
+
removeFavorite(favorite);
|
|
115
235
|
}
|
|
116
236
|
else {
|
|
117
|
-
addFavorite(
|
|
237
|
+
addFavorite(favorite);
|
|
118
238
|
}
|
|
119
239
|
}, [favorites, addFavorite, removeFavorite]);
|
|
120
240
|
const clearFavorites = useCallback(() => {
|
|
@@ -122,10 +242,10 @@ const NiceIconPicker = /* @__PURE__ */ forwardRef(function NiceIconPicker({ icon
|
|
|
122
242
|
saveToStorage(favoritesKey, []);
|
|
123
243
|
}, [favoritesKey]);
|
|
124
244
|
/* ─── History helpers ─── */
|
|
125
|
-
const addToHistory = useCallback((
|
|
245
|
+
const addToHistory = useCallback((used) => {
|
|
126
246
|
setHistory((prev) => {
|
|
127
|
-
const filtered = prev.filter((n) => n !==
|
|
128
|
-
const next = [
|
|
247
|
+
const filtered = prev.filter((n) => n !== used);
|
|
248
|
+
const next = [used, ...filtered].slice(0, MAX_HISTORY_ITEMS);
|
|
129
249
|
saveToStorage(historyKey, next);
|
|
130
250
|
return next;
|
|
131
251
|
});
|
|
@@ -162,12 +282,14 @@ const NiceIconPicker = /* @__PURE__ */ forwardRef(function NiceIconPicker({ icon
|
|
|
162
282
|
}
|
|
163
283
|
return result;
|
|
164
284
|
}, [icons, categoriesProp]);
|
|
165
|
-
/* ─── Filter icons ─── */
|
|
285
|
+
/* ─── Filter icons: by the name shown, the export name or the category ─── */
|
|
166
286
|
const filtered = useMemo(() => {
|
|
167
287
|
let items = icons;
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
items = items.filter((i) => i.
|
|
288
|
+
const q = search.trim().toLowerCase();
|
|
289
|
+
if (q) {
|
|
290
|
+
items = items.filter((i) => entryLabel(i).toLowerCase().includes(q) ||
|
|
291
|
+
i.name.toLowerCase().includes(q) ||
|
|
292
|
+
i.category.toLowerCase().includes(q));
|
|
171
293
|
}
|
|
172
294
|
if (activeCategory === '__favorites__') {
|
|
173
295
|
items = items.filter((i) => favorites.includes(i.name));
|
|
@@ -205,13 +327,8 @@ const NiceIconPicker = /* @__PURE__ */ forwardRef(function NiceIconPicker({ icon
|
|
|
205
327
|
}
|
|
206
328
|
return map;
|
|
207
329
|
}, [filtered, virtualized, virtualizedFiltered]);
|
|
208
|
-
/* ─── Selected entries ─── */
|
|
209
|
-
const selectedEntries = useMemo(() =>
|
|
210
|
-
return selectedValues
|
|
211
|
-
.map((v) => icons.find((i) => i.name === v))
|
|
212
|
-
.filter((e) => e !== undefined);
|
|
213
|
-
}, [icons, selectedValues]);
|
|
214
|
-
const selectedEntry = (_a = selectedEntries[0]) !== null && _a !== void 0 ? _a : null;
|
|
330
|
+
/* ─── Selected entries (multi-select) ─── */
|
|
331
|
+
const selectedEntries = useMemo(() => values.map((v) => entryOf(v)).filter((e) => e !== undefined), [values, entryOf]);
|
|
215
332
|
/* ─── Click outside ─── */
|
|
216
333
|
useEffect(() => {
|
|
217
334
|
if (!isOpen) {
|
|
@@ -230,6 +347,8 @@ const NiceIconPicker = /* @__PURE__ */ forwardRef(function NiceIconPicker({ icon
|
|
|
230
347
|
if (!isOpen) {
|
|
231
348
|
setSearch('');
|
|
232
349
|
setActiveCategory(null);
|
|
350
|
+
setView('icons');
|
|
351
|
+
setPicking(null);
|
|
233
352
|
setVisibleRange({ start: 0, end: virtualizedItemCount });
|
|
234
353
|
}
|
|
235
354
|
}, [isOpen, virtualizedItemCount]);
|
|
@@ -237,14 +356,27 @@ const NiceIconPicker = /* @__PURE__ */ forwardRef(function NiceIconPicker({ icon
|
|
|
237
356
|
// The panel is a dialog on the overlay stack: focus on the search field when it opens, Escape
|
|
238
357
|
// / B close only it (a document listener before) and focus returns to the trigger. The icon
|
|
239
358
|
// grid is an APG listbox moving in two dimensions — the cells were reachable by Tab only.
|
|
359
|
+
// While the grid picks an icon to stack, Escape goes back to the stack instead (1.3.9).
|
|
240
360
|
const dropdownRef = useRef(null);
|
|
241
361
|
useOverlayFocus(dropdownRef, {
|
|
242
362
|
open: isOpen,
|
|
243
363
|
modal: false,
|
|
244
364
|
initialFocus: searchInputRef,
|
|
245
|
-
onClose: () => setIsOpen(false),
|
|
365
|
+
onClose: () => (picking ? setPicking(null) : setIsOpen(false)),
|
|
246
366
|
});
|
|
247
367
|
useRovingFocus(gridWrapRef, { orientation: 'grid', itemSelector: '.ntd-picker-cell' });
|
|
368
|
+
// into the grid to pick an icon to stack: focus the search; back: the layer it went into
|
|
369
|
+
const wasPicking = useRef(false);
|
|
370
|
+
useEffect(() => {
|
|
371
|
+
var _a, _b, _c;
|
|
372
|
+
if (picking) {
|
|
373
|
+
(_a = searchInputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
374
|
+
}
|
|
375
|
+
else if (wasPicking.current) {
|
|
376
|
+
(_c = (_b = dropdownRef.current) === null || _b === void 0 ? void 0 : _b.querySelector('.nice-icon-stacker__layer--selected .nice-icon-stacker__layer-select')) === null || _c === void 0 ? void 0 : _c.focus();
|
|
377
|
+
}
|
|
378
|
+
wasPicking.current = !!picking;
|
|
379
|
+
}, [picking]);
|
|
248
380
|
/* ─── Virtualization scroll handler ─── */
|
|
249
381
|
useEffect(() => {
|
|
250
382
|
if (!virtualized || !isOpen || !gridWrapRef.current) {
|
|
@@ -272,63 +404,83 @@ const NiceIconPicker = /* @__PURE__ */ forwardRef(function NiceIconPicker({ icon
|
|
|
272
404
|
}, [virtualized, isOpen, filtered.length]);
|
|
273
405
|
/* ─── Handlers ─── */
|
|
274
406
|
const handleSelect = useCallback((entry) => {
|
|
407
|
+
var _a;
|
|
408
|
+
// an icon to stack: into the layer the stacker has selected, then back to the stack
|
|
409
|
+
if (picking) {
|
|
410
|
+
(_a = stackerRef.current) === null || _a === void 0 ? void 0 : _a.add({ icon: entry.name });
|
|
411
|
+
setPicking(null);
|
|
412
|
+
setSearch('');
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
275
415
|
// Add to history
|
|
276
416
|
if (showHistory) {
|
|
277
417
|
addToHistory(entry.name);
|
|
278
418
|
}
|
|
279
419
|
if (multiSelect) {
|
|
280
|
-
const isSelected =
|
|
420
|
+
const isSelected = values.includes(entry.name);
|
|
281
421
|
let newValues;
|
|
282
422
|
if (isSelected) {
|
|
283
|
-
newValues =
|
|
423
|
+
newValues = values.filter((v) => v !== entry.name);
|
|
284
424
|
}
|
|
285
425
|
else {
|
|
286
|
-
if (maxSelections &&
|
|
426
|
+
if (maxSelections && values.length >= maxSelections) {
|
|
287
427
|
return; // Max selections reached
|
|
288
428
|
}
|
|
289
|
-
newValues = [...
|
|
429
|
+
newValues = [...values, entry.name];
|
|
290
430
|
}
|
|
431
|
+
setValues(newValues);
|
|
291
432
|
if (onChangeMulti) {
|
|
292
433
|
const entries = newValues
|
|
293
|
-
.map((v) =>
|
|
434
|
+
.map((v) => entryOf(v))
|
|
294
435
|
.filter((e) => e !== undefined);
|
|
295
436
|
onChangeMulti(newValues, entries);
|
|
296
437
|
}
|
|
297
|
-
setInternalValues(newValues);
|
|
298
438
|
// Don't close in multi-select mode
|
|
299
439
|
}
|
|
300
440
|
else {
|
|
301
|
-
|
|
441
|
+
// the HTML records the variant it was drawn in; swapping the icon keeps its stack
|
|
442
|
+
const own = valueFormat === 'html' ? { variant } : {};
|
|
443
|
+
commit(canStack && base
|
|
444
|
+
? [{ ...base, icon: entry.name, ...own }, ...shown.slice(1)]
|
|
445
|
+
: [{ id: '1', icon: entry.name, ...own }]);
|
|
302
446
|
setIsOpen(false);
|
|
303
447
|
}
|
|
304
448
|
}, [
|
|
449
|
+
picking,
|
|
450
|
+
variant,
|
|
305
451
|
multiSelect,
|
|
306
|
-
|
|
452
|
+
values,
|
|
453
|
+
setValues,
|
|
307
454
|
maxSelections,
|
|
308
|
-
|
|
309
|
-
onChange,
|
|
455
|
+
entryOf,
|
|
310
456
|
onChangeMulti,
|
|
311
457
|
showHistory,
|
|
312
458
|
addToHistory,
|
|
459
|
+
valueFormat,
|
|
460
|
+
canStack,
|
|
461
|
+
base,
|
|
462
|
+
shown,
|
|
463
|
+
commit,
|
|
313
464
|
]);
|
|
314
465
|
const handleClear = useCallback((e) => {
|
|
315
466
|
e.stopPropagation();
|
|
316
467
|
if (multiSelect) {
|
|
468
|
+
setValues(NO_NAMES);
|
|
317
469
|
onChangeMulti === null || onChangeMulti === void 0 ? void 0 : onChangeMulti([], []);
|
|
318
|
-
setInternalValues([]);
|
|
319
470
|
}
|
|
320
471
|
else {
|
|
321
|
-
|
|
472
|
+
commit(NO_LAYERS);
|
|
473
|
+
setView('icons');
|
|
322
474
|
}
|
|
323
|
-
}, [multiSelect,
|
|
475
|
+
}, [multiSelect, setValues, onChangeMulti, commit]);
|
|
324
476
|
const handleToggle = useCallback(() => {
|
|
325
477
|
if (!disabled) {
|
|
326
478
|
setIsOpen((o) => !o);
|
|
327
479
|
}
|
|
328
480
|
}, [disabled]);
|
|
329
|
-
const handleFavoriteClick = useCallback((e,
|
|
481
|
+
const handleFavoriteClick = useCallback((e, favorite) => {
|
|
330
482
|
e.stopPropagation();
|
|
331
|
-
toggleFavorite(
|
|
483
|
+
toggleFavorite(favorite);
|
|
332
484
|
}, [toggleFavorite]);
|
|
333
485
|
/* ─── Drag & Drop handlers ─── */
|
|
334
486
|
const handleDragStart = useCallback((entry, e) => {
|
|
@@ -336,8 +488,7 @@ const NiceIconPicker = /* @__PURE__ */ forwardRef(function NiceIconPicker({ icon
|
|
|
336
488
|
return;
|
|
337
489
|
}
|
|
338
490
|
// Set various data formats for maximum compatibility
|
|
339
|
-
const
|
|
340
|
-
const readName = readableName(name);
|
|
491
|
+
const readName = entryLabel(entry);
|
|
341
492
|
// Plain text - icon name
|
|
342
493
|
e.dataTransfer.setData('text/plain', readName);
|
|
343
494
|
// JSON data with full icon info
|
|
@@ -349,14 +500,9 @@ const NiceIconPicker = /* @__PURE__ */ forwardRef(function NiceIconPicker({ icon
|
|
|
349
500
|
};
|
|
350
501
|
e.dataTransfer.setData('application/json', JSON.stringify(iconData));
|
|
351
502
|
e.dataTransfer.setData('text/x-ntd-icon', JSON.stringify(iconData));
|
|
352
|
-
//
|
|
503
|
+
// The cell's own drawing as an image
|
|
353
504
|
try {
|
|
354
|
-
const
|
|
355
|
-
tempContainer.style.position = 'absolute';
|
|
356
|
-
tempContainer.style.left = '-9999px';
|
|
357
|
-
document.body.appendChild(tempContainer);
|
|
358
|
-
// Use dynamic import workaround - render to string
|
|
359
|
-
const svgElement = document.querySelector(`[data-name="${readName}"] svg`);
|
|
505
|
+
const svgElement = e.currentTarget.querySelector('svg');
|
|
360
506
|
if (svgElement) {
|
|
361
507
|
const svgClone = svgElement.cloneNode(true);
|
|
362
508
|
svgClone.setAttribute('width', '64');
|
|
@@ -369,7 +515,6 @@ const NiceIconPicker = /* @__PURE__ */ forwardRef(function NiceIconPicker({ icon
|
|
|
369
515
|
img.src = URL.createObjectURL(svgBlob);
|
|
370
516
|
e.dataTransfer.setDragImage(img, 32, 32);
|
|
371
517
|
}
|
|
372
|
-
document.body.removeChild(tempContainer);
|
|
373
518
|
}
|
|
374
519
|
catch (_a) {
|
|
375
520
|
// Fallback - use default drag image
|
|
@@ -380,33 +525,106 @@ const NiceIconPicker = /* @__PURE__ */ forwardRef(function NiceIconPicker({ icon
|
|
|
380
525
|
const handleDragEnd = useCallback((entry, e) => {
|
|
381
526
|
onDragEnd === null || onDragEnd === void 0 ? void 0 : onDragEnd(entry, e);
|
|
382
527
|
}, [onDragEnd]);
|
|
383
|
-
const readableName = (name) => name.replace(/^Ntd/, '');
|
|
384
528
|
const triggerLabel = useMemo(() => {
|
|
385
|
-
if (multiSelect
|
|
529
|
+
if (multiSelect) {
|
|
386
530
|
if (selectedEntries.length === 1) {
|
|
387
|
-
return
|
|
531
|
+
return entryLabel(selectedEntries[0]);
|
|
532
|
+
}
|
|
533
|
+
if (selectedEntries.length > 1) {
|
|
534
|
+
return t('iconPicker.iconsSelected', '{count} icons selected').replace('{count}', String(selectedEntries.length));
|
|
388
535
|
}
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
}, [multiSelect, selectedEntries,
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
536
|
+
}
|
|
537
|
+
else if (base) {
|
|
538
|
+
return labelOf(base.icon);
|
|
539
|
+
}
|
|
540
|
+
return placeholder !== null && placeholder !== void 0 ? placeholder : t('iconPicker.selectIcon', 'Select icon…');
|
|
541
|
+
}, [multiSelect, selectedEntries, base, labelOf, placeholder, t]);
|
|
542
|
+
const hasSelection = multiSelect ? selectedEntries.length > 0 : !!base;
|
|
543
|
+
const iconOnly = displayMode === 'icon';
|
|
544
|
+
// the field's label first, then what is chosen — the icon-only trigger has no text of its own
|
|
545
|
+
const triggerName = ariaLabel
|
|
546
|
+
? `${ariaLabel}: ${triggerLabel}`
|
|
547
|
+
: iconOnly
|
|
548
|
+
? triggerLabel
|
|
549
|
+
: undefined;
|
|
550
|
+
const overlays = Math.max(0, shown.length - 1);
|
|
551
|
+
const showGrid = view === 'icons' || !!picking;
|
|
552
|
+
// Where the panel opens (1.3.9): above the trigger when there is more room there, against its
|
|
553
|
+
// right edge near the window's, never taller than the room it has — the stack view is tall.
|
|
554
|
+
// The room is what the window and every clipping or scrolling ancestor leave visible.
|
|
555
|
+
const [placement, setPlacement] = useState({
|
|
556
|
+
up: false,
|
|
557
|
+
end: false,
|
|
558
|
+
});
|
|
559
|
+
useIsoLayoutEffect(() => {
|
|
560
|
+
const root = rootRef.current;
|
|
561
|
+
if (!isOpen || !root || typeof window === 'undefined') {
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
const r = root.getBoundingClientRect();
|
|
565
|
+
const bounds = visibleBounds(root);
|
|
566
|
+
const want = showGrid ? 460 : 620;
|
|
567
|
+
const below = bounds.bottom - r.bottom - 12;
|
|
568
|
+
const above = r.top - bounds.top - 12;
|
|
569
|
+
const up = below < want && above > below;
|
|
570
|
+
const room = up ? above : below;
|
|
571
|
+
setPlacement({
|
|
572
|
+
up,
|
|
573
|
+
end: r.left + 380 > bounds.right - 8 && r.right - bounds.left >= 380,
|
|
574
|
+
maxHeight: room < want ? Math.max(240, Math.floor(room)) : undefined,
|
|
575
|
+
});
|
|
576
|
+
}, [isOpen, showGrid]);
|
|
577
|
+
const pickingInto = picking ? shown.find((l) => l.id === picking.target) : undefined;
|
|
578
|
+
const SingleMulti = (_a = selectedEntries[0]) === null || _a === void 0 ? void 0 : _a.Component;
|
|
579
|
+
return (jsxs("div", { ref: rootRef, id: id, "data-testid": dataTestId !== null && dataTestId !== void 0 ? dataTestId : testId, className: `ntd-picker${isOpen ? ' ntd-picker--open' : ''}${disabled ? ' ntd-picker--disabled' : ''}${multiSelect ? ' ntd-picker--multi' : ''}${iconOnly ? ' ntd-picker--icon-only' : ''}${className ? ` ${className}` : ''}`, children: [jsxs("button", { type: "button", className: "ntd-picker-trigger", onClick: handleToggle, disabled: disabled, "aria-haspopup": "dialog", "aria-expanded": isOpen, "aria-label": triggerName, title: iconOnly ? triggerLabel : undefined, children: [jsx("span", { className: "ntd-picker-trigger-icon", children: !multiSelect && base ? (renderIconStack(shown, resolve, {
|
|
580
|
+
baseProps: { ...drawProps, size: triggerIconSize },
|
|
581
|
+
})) : multiSelect && selectedEntries.length > 1 ? (jsx("span", { className: "ntd-picker-multi-badge", children: selectedEntries.length })) : SingleMulti ? (jsx(SingleMulti, { ...drawProps, size: triggerIconSize })) : (jsx(NtdGrid, { size: 16, variant: "outline" })) }), !iconOnly && (jsxs(Fragment, { children: [jsx("span", { className: `ntd-picker-trigger-label${hasSelection ? '' : ' ntd-picker-trigger-placeholder'}`, children: triggerLabel }), jsx("span", { className: "ntd-picker-trigger-chevron", children: jsx(Chevron, {}) })] }))] }), name &&
|
|
582
|
+
(multiSelect ? (values.map((v) => jsx("input", { type: "hidden", name: name, value: v }, v))) : (jsx("input", { type: "hidden", name: name, value: value !== null && value !== void 0 ? value : '' }))), pending && (jsx("span", { ref: serializerRef, className: "ntd-picker-serializer", hidden: true, children: renderIconStack(pending, resolve, {
|
|
583
|
+
baseProps: {
|
|
584
|
+
...drawProps,
|
|
585
|
+
width: '1em',
|
|
586
|
+
height: '1em',
|
|
587
|
+
title: labelOf(pending[0].icon),
|
|
588
|
+
...iconHtmlAttributes(pending),
|
|
589
|
+
},
|
|
590
|
+
}) })), isOpen && (jsxs("div", { ref: dropdownRef, className: `ntd-picker-dropdown${showGrid ? '' : ' ntd-picker-dropdown--stack'}${placement.up ? ' ntd-picker-dropdown--up' : ''}${placement.end ? ' ntd-picker-dropdown--end' : ''}`, style: placement.maxHeight ? { maxHeight: placement.maxHeight } : undefined, role: "dialog", "aria-label": t('iconPicker.selectIcon', 'Select icon…'), children: [canStack &&
|
|
591
|
+
(picking ? (jsxs("div", { className: "ntd-picker-banner", children: [jsx("button", { type: "button", className: "ntd-picker-back", onClick: () => setPicking(null), "aria-label": t('iconPicker.back', 'Back'), title: t('iconPicker.back', 'Back'), children: jsx(NtdArrowLeft, { size: 14, variant: "outline" }) }), jsx("span", { children: t('iconPicker.pickOverlay', 'Pick an icon to put on {icon}').replace('{icon}', pickingInto ? labelOf(pickingInto.icon) : base ? labelOf(base.icon) : '') })] })) : (jsxs("div", { className: "ntd-picker-views", ref: viewsRef, role: "tablist", "aria-label": t('iconPicker.selectIcon', 'Select icon…'), children: [jsx("button", { type: "button", role: "tab", id: `${panelId}-icons`, "aria-selected": view === 'icons', "aria-controls": `${panelId}-panel`, className: `ntd-picker-view${view === 'icons' ? ' ntd-picker-view--active' : ''}`, onClick: () => setView('icons'), children: t('iconPicker.tabIcon', 'Icon') }), jsxs("button", { type: "button", role: "tab", id: `${panelId}-stack`, "aria-selected": view === 'stack', "aria-controls": `${panelId}-panel`, className: `ntd-picker-view${view === 'stack' ? ' ntd-picker-view--active' : ''}`, onClick: () => setView('stack'), disabled: !base, title: base ? undefined : t('iconPicker.stackNeedsIcon', 'Pick an icon first'), children: [t('iconPicker.tabStack', 'Overlays'), overlays > 0 && jsx("span", { className: "ntd-picker-tab-count", children: overlays })] })] }))), jsxs("div", { className: "ntd-picker-panel", id: `${panelId}-panel`, ...(canStack && !picking
|
|
592
|
+
? {
|
|
593
|
+
role: 'tabpanel',
|
|
594
|
+
'aria-labelledby': `${panelId}-${view}`,
|
|
595
|
+
}
|
|
596
|
+
: {}), children: [showGrid && (jsxs(Fragment, { children: [jsxs("div", { className: "ntd-picker-search", children: [jsx("span", { className: "ntd-picker-search-icon", children: jsx(SearchIcon, {}) }), jsx("input", { ref: searchInputRef, type: "text", placeholder: t('iconPicker.searchIcons', 'Search icons…'), value: search, onChange: (e) => setSearch(e.target.value), "aria-label": t('iconPicker.searchIcons2', 'Search icons') })] }), (allCategories.length > 1 || showFavorites || showHistory) && (jsxs("div", { className: "ntd-picker-tabs", ref: tablistRef, role: "tablist", children: [jsx("button", { type: "button", className: `ntd-picker-tab${activeCategory === null ? ' ntd-picker-tab--active' : ''}`, onClick: () => setActiveCategory(null), role: "tab", "aria-selected": activeCategory === null, children: t('iconPicker.all', 'All') }), showFavorites && (jsxs("button", { type: "button", className: `ntd-picker-tab ntd-picker-tab-special${activeCategory === '__favorites__' ? ' ntd-picker-tab--active' : ''}`, onClick: () => setActiveCategory('__favorites__'), role: "tab", "aria-selected": activeCategory === '__favorites__', title: t('iconPicker.favorites', 'Favorites'), children: [jsx(StarIcon, { filled: true }), " ", t('iconPicker.favorites', 'Favorites'), favorites.length > 0 && (jsx("span", { className: "ntd-picker-tab-count", children: favorites.length }))] })), showHistory && history.length > 0 && (jsxs("button", { type: "button", className: `ntd-picker-tab ntd-picker-tab-special${activeCategory === '__history__' ? ' ntd-picker-tab--active' : ''}`, onClick: () => setActiveCategory('__history__'), role: "tab", "aria-selected": activeCategory === '__history__', title: t('iconPicker.recentlyUsed', 'Recently used'), children: [jsx(ClockIcon, {}), " ", t('iconPicker.recent', 'Recent')] })), allCategories.map((cat) => (jsx("button", { type: "button", className: `ntd-picker-tab${activeCategory === cat ? ' ntd-picker-tab--active' : ''}`, onClick: () => setActiveCategory(cat), role: "tab", "aria-selected": activeCategory === cat, children: cat }, cat)))] })), jsx("div", { className: "ntd-picker-grid-wrap", ref: gridWrapRef, role: "listbox", "aria-label": t('iconPicker.icons', 'Icons'), "aria-multiselectable": multiSelect || undefined, children: filtered.length === 0 ? (jsxs("div", { className: "ntd-picker-empty", children: [jsx("div", { className: "ntd-picker-empty-icon", children: jsx(NtdInbox, { size: 28, variant: "outline" }) }), activeCategory === '__favorites__'
|
|
597
|
+
? t('iconPicker.noFavoritesYetClickThe', 'No favorites yet. Click the star icon to add.')
|
|
598
|
+
: activeCategory === '__history__'
|
|
599
|
+
? t('iconPicker.noRecentIcons', 'No recent icons.')
|
|
600
|
+
: t('iconPicker.noIconsFound', 'No icons found')] })) : (jsxs(Fragment, { children: [virtualized && visibleRange.start > 0 && (jsx("div", { style: { height: Math.floor(visibleRange.start / 6) * 48 } })), Array.from(grouped.entries()).map(([category, entries]) => (jsxs("div", { children: [!activeCategory && (jsx("div", { className: "ntd-picker-category-label", children: category })), jsx("div", { className: `ntd-picker-grid${optionDisplayMode === 'icon-name' ? ' ntd-picker-grid--names' : ''}`, children: entries.map((entry) => {
|
|
601
|
+
const isSelected = !picking && selectedValues.includes(entry.name);
|
|
602
|
+
const isFavorite = favorites.includes(entry.name);
|
|
603
|
+
const label = entryLabel(entry);
|
|
604
|
+
const cell = (jsxs("button", { type: "button", className: `ntd-picker-cell${isSelected ? ' ntd-picker-cell--selected' : ''}${multiSelect ? ' ntd-picker-cell--multi-select' : ''}${enableDragAndDrop ? ' ntd-picker-cell--draggable' : ''}`, onClick: () => handleSelect(entry), "data-name": label, role: "option", "aria-selected": isSelected, "aria-label": label, title: label, draggable: enableDragAndDrop, onDragStart: enableDragAndDrop
|
|
605
|
+
? (e) => handleDragStart(entry, e)
|
|
606
|
+
: undefined, onDragEnd: enableDragAndDrop ? (e) => handleDragEnd(entry, e) : undefined, children: [jsx(entry.Component, { ...drawProps, size: gridIconSize }), optionDisplayMode === 'icon-name' && (jsx("span", { className: "ntd-picker-cell-name", children: label })), multiSelect && isSelected && (jsx("span", { className: "ntd-picker-cell-check", children: jsx(CheckIcon, {}) }))] }, entry.name));
|
|
607
|
+
if (!showFavorites) {
|
|
608
|
+
return cell;
|
|
609
|
+
}
|
|
610
|
+
// the star beside the cell, not in it — a button in a button is
|
|
611
|
+
// invalid HTML (1.3.9)
|
|
612
|
+
const favLabel = isFavorite
|
|
613
|
+
? t('iconPicker.removeFromFavorites', 'Remove from favorites')
|
|
614
|
+
: t('iconPicker.addToFavorites', 'Add to favorites');
|
|
615
|
+
return (jsxs("div", { className: "ntd-picker-cell-wrap", children: [cell, jsx("button", { type: "button", tabIndex: -1, className: `ntd-picker-cell-fav${isFavorite ? ' ntd-picker-cell-fav--active' : ''}`, onClick: (e) => handleFavoriteClick(e, entry.name), "aria-label": `${favLabel}: ${label}`, title: favLabel, children: jsx(StarIcon, { filled: isFavorite }) })] }, entry.name));
|
|
616
|
+
}) })] }, category))), virtualized && visibleRange.end < filtered.length && (jsx("div", { style: {
|
|
617
|
+
height: Math.ceil((filtered.length - visibleRange.end) / 6) * 48,
|
|
618
|
+
} }))] })) })] })), canStack && view === 'stack' && base && (jsx("div", { className: "ntd-picker-stack", hidden: !!picking, children: jsx(NiceIconStacker, { ref: stackerRef, icons: resolve, layers: shown, onLayersChange: commit, onAddClick: (target) => {
|
|
619
|
+
var _a;
|
|
620
|
+
setSearch('');
|
|
621
|
+
setActiveCategory(null);
|
|
622
|
+
setPicking({ target: (_a = target === null || target === void 0 ? void 0 : target.id) !== null && _a !== void 0 ? _a : base.id });
|
|
623
|
+
}, previewProps: drawProps, iconLabel: labelOf, showCode: false, keepBase: true }) }))] }), !picking && (jsxs("div", { className: "ntd-picker-footer", children: [jsx("span", { className: "ntd-picker-count", children: showGrid && (jsxs(Fragment, { children: [(filtered.length === 1
|
|
624
|
+
? t('iconPicker.iconCountOne', '{count} icon')
|
|
625
|
+
: t('iconPicker.iconCountMany', '{count} icons')).replace('{count}', String(filtered.length)), multiSelect && selectedValues.length > 0 && (jsxs(Fragment, { children: [' ', t('iconPicker.selected', '• {selectedValues} selected').replace('{selectedValues}', String(selectedValues.length))] })), maxSelections && multiSelect && (jsxs(Fragment, { children: [' ', t('iconPicker.max', '(max {maxSelections})').replace('{maxSelections}', String(maxSelections))] }))] })) }), clearable && hasSelection && (jsx("button", { type: "button", className: "ntd-picker-clear", onClick: handleClear, children: multiSelect
|
|
408
626
|
? t('iconPicker.clearAll', 'Clear all')
|
|
409
|
-
: t('iconPicker.clearSelection', 'Clear selection') })), multiSelect && (jsx("button", { type: "button", className: "ntd-picker-done", onClick: () => setIsOpen(false), children: t('iconPicker.done', 'Done') }))] })] }))] }));
|
|
627
|
+
: t('iconPicker.clearSelection', 'Clear selection') })), (multiSelect || view === 'stack') && (jsx("button", { type: "button", className: "ntd-picker-done", onClick: () => setIsOpen(false), children: t('iconPicker.done', 'Done') }))] }))] }))] }));
|
|
410
628
|
});
|
|
411
629
|
|
|
412
630
|
export { NiceIconPicker };
|