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