@nice2dev/icons 1.3.15 → 1.3.17
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 +26 -0
- package/README.md +1 -1
- package/dist/cjs/NiceDesktopIconExporter.js +688 -599
- package/dist/cjs/NiceFaviconGenerator.js +603 -417
- package/dist/cjs/NiceIconPicker.js +788 -508
- package/dist/cjs/NiceIconPreview.js +246 -188
- package/dist/cjs/NiceIconSetCreator.js +683 -552
- package/dist/cjs/NiceIconStacker.js +547 -171
- package/dist/cjs/NiceIconValue.js +27 -35
- package/dist/cjs/NtdIconBadge.js +384 -360
- package/dist/cjs/NtdIconMorph.js +419 -354
- package/dist/cjs/NtdStateMorph.js +150 -147
- package/dist/cjs/advancedIconSearch.js +373 -510
- package/dist/cjs/animationControls.js +518 -642
- package/dist/cjs/animationSpeed.js +69 -0
- package/dist/cjs/client.js +12 -15
- package/dist/cjs/createIcon.js +374 -252
- package/dist/cjs/design-tokens.js +324 -390
- package/dist/cjs/figmaExport.js +162 -257
- package/dist/cjs/figmaSync.js +234 -316
- package/dist/cjs/headless-ui.js +240 -368
- package/dist/cjs/iconAnalytics.js +337 -393
- package/dist/cjs/iconHtml.js +64 -86
- package/dist/cjs/iconMetadata.js +392 -472
- package/dist/cjs/iconParts.js +120 -90
- package/dist/cjs/iconStack.js +136 -155
- package/dist/cjs/iconStyles.js +1670 -509
- package/dist/cjs/iconValue.js +359 -430
- package/dist/cjs/icons/accessibility.js +1119 -134
- package/dist/cjs/icons/actions.js +1290 -330
- package/dist/cjs/icons/ai.js +1424 -144
- package/dist/cjs/icons/brand.js +613 -184
- package/dist/cjs/icons/business.js +938 -192
- package/dist/cjs/icons/devtech.js +1656 -227
- package/dist/cjs/icons/ecommerce.js +802 -111
- package/dist/cjs/icons/education.js +1345 -199
- package/dist/cjs/icons/files.js +1597 -328
- package/dist/cjs/icons/fintech.js +793 -104
- package/dist/cjs/icons/food.js +1028 -156
- package/dist/cjs/icons/fun.js +804 -109
- package/dist/cjs/icons/gaming.js +916 -170
- package/dist/cjs/icons/health.js +623 -112
- package/dist/cjs/icons/index.js +32 -42
- package/dist/cjs/icons/input.js +282 -83
- package/dist/cjs/icons/legal.js +1065 -122
- package/dist/cjs/icons/media.js +1061 -267
- package/dist/cjs/icons/navigation.js +407 -123
- package/dist/cjs/icons/omniverk.js +523 -93
- package/dist/cjs/icons/realestate.js +1520 -193
- package/dist/cjs/icons/saas.js +867 -98
- package/dist/cjs/icons/science.js +1344 -202
- package/dist/cjs/icons/shapes.js +1316 -308
- package/dist/cjs/icons/smarthome.js +786 -90
- package/dist/cjs/icons/social.js +591 -59
- package/dist/cjs/icons/sports.js +1133 -133
- package/dist/cjs/icons/sustainability.js +1211 -139
- package/dist/cjs/icons/travel.js +1470 -148
- package/dist/cjs/icons/ui.js +1508 -315
- package/dist/cjs/icons/weather.js +1160 -173
- package/dist/cjs/index.js +559 -568
- package/dist/cjs/lottieIntegration.js +261 -266
- package/dist/cjs/nicetodev-icons.css +1 -1
- package/dist/cjs/particleEffects.js +270 -251
- package/dist/cjs/resolver.js +229 -284
- package/dist/cjs/rsc.js +175 -193
- package/dist/cjs/sharpen.js +73 -130
- package/dist/cjs/smilAnimations.js +380 -165
- package/dist/cjs/stacking.js +87 -122
- package/dist/cjs/tailwind-plugin.js +280 -320
- package/dist/cjs/types.js +46 -72
- package/dist/cjs/utilities.js +163 -324
- package/dist/cjs/variantProps.js +12 -52
- package/dist/esm/NiceDesktopIconExporter.js +671 -598
- package/dist/esm/NiceFaviconGenerator.js +583 -416
- package/dist/esm/NiceIconPicker.js +789 -507
- package/dist/esm/NiceIconPreview.js +247 -187
- package/dist/esm/NiceIconSetCreator.js +685 -551
- package/dist/esm/NiceIconStacker.js +549 -170
- package/dist/esm/NiceIconValue.js +29 -34
- package/dist/esm/NtdIconBadge.js +388 -359
- package/dist/esm/NtdIconMorph.js +423 -353
- package/dist/esm/NtdStateMorph.js +152 -146
- package/dist/esm/advancedIconSearch.js +391 -509
- package/dist/esm/animationControls.js +527 -641
- package/dist/esm/animationSpeed.js +69 -0
- package/dist/esm/client.js +48 -10
- package/dist/esm/createIcon.js +375 -251
- package/dist/esm/design-tokens.js +334 -387
- package/dist/esm/figmaExport.js +170 -256
- package/dist/esm/figmaSync.js +237 -315
- package/dist/esm/headless-ui.js +254 -365
- package/dist/esm/iconAnalytics.js +342 -392
- package/dist/esm/iconHtml.js +67 -85
- package/dist/esm/iconMetadata.js +404 -471
- package/dist/esm/iconParts.js +125 -89
- package/dist/esm/iconStack.js +145 -154
- package/dist/esm/iconStyles.js +1681 -508
- package/dist/esm/iconValue.js +361 -429
- package/dist/esm/icons/accessibility.js +1133 -133
- package/dist/esm/icons/actions.js +1320 -329
- package/dist/esm/icons/ai.js +1441 -143
- package/dist/esm/icons/brand.js +616 -179
- package/dist/esm/icons/business.js +957 -191
- package/dist/esm/icons/devtech.js +1687 -226
- package/dist/esm/icons/ecommerce.js +816 -110
- package/dist/esm/icons/education.js +1364 -198
- package/dist/esm/icons/files.js +1628 -327
- package/dist/esm/icons/fintech.js +807 -103
- package/dist/esm/icons/food.js +1043 -153
- package/dist/esm/icons/fun.js +819 -108
- package/dist/esm/icons/gaming.js +933 -169
- package/dist/esm/icons/health.js +636 -111
- package/dist/esm/icons/index.js +589 -32
- package/dist/esm/icons/input.js +326 -82
- package/dist/esm/icons/legal.js +1079 -121
- package/dist/esm/icons/media.js +1086 -266
- package/dist/esm/icons/navigation.js +421 -122
- package/dist/esm/icons/omniverk.js +534 -91
- package/dist/esm/icons/realestate.js +1544 -198
- package/dist/esm/icons/saas.js +880 -97
- package/dist/esm/icons/science.js +1370 -208
- package/dist/esm/icons/shapes.js +1343 -307
- package/dist/esm/icons/smarthome.js +799 -89
- package/dist/esm/icons/social.js +599 -58
- package/dist/esm/icons/sports.js +1148 -132
- package/dist/esm/icons/sustainability.js +1226 -138
- package/dist/esm/icons/travel.js +1486 -147
- package/dist/esm/icons/ui.js +1540 -314
- package/dist/esm/icons/weather.js +1181 -172
- package/dist/esm/index.js +749 -58
- package/dist/esm/lottieIntegration.js +264 -265
- package/dist/esm/nicetodev-icons.css +1 -1
- package/dist/esm/particleEffects.js +271 -250
- package/dist/esm/resolver.js +231 -283
- package/dist/esm/rsc.js +182 -192
- package/dist/esm/sharpen.js +78 -129
- package/dist/esm/smilAnimations.js +385 -164
- package/dist/esm/stacking.js +99 -121
- package/dist/esm/tailwind-plugin.js +282 -319
- package/dist/esm/types.js +50 -71
- package/dist/esm/utilities.js +177 -321
- package/dist/esm/variantProps.js +23 -52
- package/dist/types/animationSpeed.d.ts +19 -0
- package/dist/types/createIcon.d.ts +6 -0
- package/dist/types/iconStyles.d.ts +6 -1
- package/dist/types/icons/brand.d.ts +0 -4
- package/dist/types/icons/food.d.ts +0 -3
- package/dist/types/icons/index.d.ts +3 -3
- package/dist/types/icons/omniverk.d.ts +0 -3
- package/dist/types/icons/science.d.ts +1 -1
- package/dist/types/smilAnimations.d.ts +4 -2
- package/dist/types/types.d.ts +7 -0
- package/package.json +15 -22
|
@@ -1,451 +1,444 @@
|
|
|
1
|
-
import { jsxs, jsx, Fragment } from
|
|
2
|
-
import { useRovingFocus, useNiceTranslation, useControllableState, useOverlayFocus } from
|
|
3
|
-
import { forwardRef, useRef, useState, useId, useMemo, useCallback, useImperativeHandle, useEffect, useLayoutEffect } from
|
|
4
|
-
import { scopeIconHtmlIds, iconHtmlAttributes } from
|
|
5
|
-
import { NtdSearch, NtdCheck } from
|
|
6
|
-
import { NtdGrid } from
|
|
7
|
-
import { NtdInbox } from
|
|
8
|
-
import { NtdArrowLeft, NtdChevronDown } from
|
|
9
|
-
import { NtdStar, NtdClock } from
|
|
10
|
-
import { iconDisplayName, renderIconStack } from
|
|
11
|
-
import { parseIconValue, formatIconValue } from
|
|
12
|
-
import { NiceIconStacker } from
|
|
13
|
-
|
|
1
|
+
import { jsxs, jsx, Fragment } from "react/jsx-runtime";
|
|
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";
|
|
5
|
+
import { NtdSearch, NtdCheck } from "./icons/actions.js";
|
|
6
|
+
import { NtdGrid } from "./icons/files.js";
|
|
7
|
+
import { NtdInbox } from "./icons/media.js";
|
|
8
|
+
import { NtdArrowLeft, NtdChevronDown } from "./icons/navigation.js";
|
|
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";
|
|
13
|
+
/* empty css */
|
|
14
14
|
const MAX_HISTORY_ITEMS = 20;
|
|
15
|
-
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
const ClockIcon = () => jsx(NtdClock, { size: 12, variant: "outline" });
|
|
22
|
-
const CheckIcon = () => jsx(NtdCheck, { size: 10, variant: "outline" });
|
|
15
|
+
const useIsoLayoutEffect = typeof window === "undefined" ? useEffect : useLayoutEffect;
|
|
16
|
+
const Chevron = () => /* @__PURE__ */ jsx(NtdChevronDown, { size: 12, variant: "outline" });
|
|
17
|
+
const SearchIcon = () => /* @__PURE__ */ jsx(NtdSearch, { size: 14, variant: "outline" });
|
|
18
|
+
const StarIcon = ({ filled }) => /* @__PURE__ */ jsx(NtdStar, { size: 12, variant: filled ? "solid" : "outline" });
|
|
19
|
+
const ClockIcon = () => /* @__PURE__ */ jsx(NtdClock, { size: 12, variant: "outline" });
|
|
20
|
+
const CheckIcon = () => /* @__PURE__ */ jsx(NtdCheck, { size: 10, variant: "outline" });
|
|
23
21
|
const NO_NAMES = [];
|
|
24
22
|
const NO_LAYERS = [];
|
|
25
|
-
|
|
26
|
-
const
|
|
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. */
|
|
23
|
+
const PAGE_ACCENT = "var(--nice-bg-primary, white)";
|
|
24
|
+
const entryLabel = (entry) => entry.label ?? iconDisplayName(entry.name);
|
|
30
25
|
function visibleBounds(el) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
26
|
+
const bounds = { top: 0, bottom: window.innerHeight, left: 0, right: window.innerWidth };
|
|
27
|
+
for (let p = el.parentElement; p && p !== document.body; p = p.parentElement) {
|
|
28
|
+
const style = window.getComputedStyle(p);
|
|
29
|
+
if (style.overflowX !== "visible" || style.overflowY !== "visible") {
|
|
30
|
+
const r = p.getBoundingClientRect();
|
|
31
|
+
bounds.top = Math.max(bounds.top, r.top);
|
|
32
|
+
bounds.bottom = Math.min(bounds.bottom, r.bottom);
|
|
33
|
+
bounds.left = Math.max(bounds.left, r.left);
|
|
34
|
+
bounds.right = Math.min(bounds.right, r.right);
|
|
41
35
|
}
|
|
42
|
-
|
|
36
|
+
}
|
|
37
|
+
return bounds;
|
|
43
38
|
}
|
|
44
|
-
/* ─── localStorage helpers ─── */
|
|
45
39
|
function loadFromStorage(key, fallback) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
40
|
+
if (typeof window === "undefined") {
|
|
41
|
+
return fallback;
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
const stored = localStorage.getItem(key);
|
|
45
|
+
return stored ? JSON.parse(stored) : fallback;
|
|
46
|
+
} catch {
|
|
47
|
+
return fallback;
|
|
48
|
+
}
|
|
56
49
|
}
|
|
57
50
|
function saveToStorage(key, value) {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
// Storage full or disabled
|
|
66
|
-
}
|
|
51
|
+
if (typeof window === "undefined") {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
localStorage.setItem(key, JSON.stringify(value));
|
|
56
|
+
} catch {
|
|
57
|
+
}
|
|
67
58
|
}
|
|
68
|
-
/*
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
59
|
+
const NiceIconPicker = /* @__PURE__ */ forwardRef(
|
|
60
|
+
function NiceIconPicker2({
|
|
61
|
+
icons,
|
|
62
|
+
value: valueProp,
|
|
63
|
+
defaultValue,
|
|
64
|
+
values: valuesProp,
|
|
65
|
+
defaultValues,
|
|
66
|
+
onChange,
|
|
67
|
+
onChangeMulti,
|
|
68
|
+
valueFormat: valueFormatProp,
|
|
69
|
+
stackable = false,
|
|
70
|
+
displayMode = "icon-name",
|
|
71
|
+
optionDisplayMode = "icon",
|
|
72
|
+
name,
|
|
73
|
+
"aria-label": ariaLabel,
|
|
74
|
+
id,
|
|
75
|
+
testId,
|
|
76
|
+
"data-testid": dataTestId,
|
|
77
|
+
multiSelect = false,
|
|
78
|
+
maxSelections,
|
|
79
|
+
placeholder,
|
|
80
|
+
variant = "solid",
|
|
81
|
+
iconProps,
|
|
82
|
+
gridIconSize = "md",
|
|
83
|
+
triggerIconSize = "sm",
|
|
84
|
+
categories: categoriesProp,
|
|
85
|
+
className,
|
|
86
|
+
disabled = false,
|
|
87
|
+
clearable = true,
|
|
88
|
+
virtualized = false,
|
|
89
|
+
virtualizedItemCount = 100,
|
|
90
|
+
showHistory = false,
|
|
91
|
+
showFavorites = false,
|
|
92
|
+
storageKeyPrefix = "ntd-icon-picker",
|
|
93
|
+
enableDragAndDrop = false,
|
|
94
|
+
onDragStart,
|
|
95
|
+
onDragEnd
|
|
96
|
+
}, ref) {
|
|
96
97
|
const tablistRef = useRef(null);
|
|
97
|
-
useRovingFocus(tablistRef, { orientation:
|
|
98
|
+
useRovingFocus(tablistRef, { orientation: "horizontal", wrap: true, activateOnMove: true });
|
|
98
99
|
const viewsRef = useRef(null);
|
|
99
|
-
useRovingFocus(viewsRef, { orientation:
|
|
100
|
+
useRovingFocus(viewsRef, { orientation: "horizontal", activateOnMove: true });
|
|
100
101
|
const { t } = useNiceTranslation();
|
|
101
102
|
const [isOpen, setIsOpen] = useState(false);
|
|
102
|
-
const [search, setSearch] = useState(
|
|
103
|
+
const [search, setSearch] = useState("");
|
|
103
104
|
const [activeCategory, setActiveCategory] = useState(null);
|
|
104
|
-
|
|
105
|
-
const [view, setView] = useState('icons');
|
|
105
|
+
const [view, setView] = useState("icons");
|
|
106
106
|
const [picking, setPicking] = useState(null);
|
|
107
107
|
const panelId = useId();
|
|
108
|
-
const valueFormat = valueFormatProp
|
|
108
|
+
const valueFormat = valueFormatProp ?? "name";
|
|
109
109
|
const canStack = stackable && !multiSelect;
|
|
110
|
-
const drawProps = useMemo(
|
|
111
|
-
|
|
110
|
+
const drawProps = useMemo(
|
|
111
|
+
() => ({ accentColor: PAGE_ACCENT, ...iconProps, variant }),
|
|
112
|
+
[iconProps, variant]
|
|
113
|
+
);
|
|
112
114
|
const [visibleRange, setVisibleRange] = useState({ start: 0, end: virtualizedItemCount });
|
|
113
115
|
const gridWrapRef = useRef(null);
|
|
114
|
-
// Favorites & History state
|
|
115
116
|
const favoritesKey = `${storageKeyPrefix}-favorites`;
|
|
116
117
|
const historyKey = `${storageKeyPrefix}-history`;
|
|
117
118
|
const [favorites, setFavorites] = useState(() => loadFromStorage(favoritesKey, []));
|
|
118
119
|
const [history, setHistory] = useState(() => loadFromStorage(historyKey, []));
|
|
119
|
-
// The value binds both ways (1.3.9): controlled by `value`, or kept here from `defaultValue`.
|
|
120
120
|
const [value, setValue] = useControllableState({
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
121
|
+
value: valueProp,
|
|
122
|
+
defaultValue,
|
|
123
|
+
fallbackValue: null,
|
|
124
|
+
componentName: "NiceIconPicker"
|
|
125
125
|
});
|
|
126
126
|
const [values, setValues] = useControllableState({
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
127
|
+
value: valuesProp,
|
|
128
|
+
defaultValue: defaultValues,
|
|
129
|
+
fallbackValue: NO_NAMES,
|
|
130
|
+
componentName: "NiceIconPicker"
|
|
131
131
|
});
|
|
132
132
|
const rootRef = useRef(null);
|
|
133
133
|
const searchInputRef = useRef(null);
|
|
134
134
|
const stackerRef = useRef(null);
|
|
135
135
|
const serializerRef = useRef(null);
|
|
136
|
-
/* ─── Icons by name: exact, or without the Ntd a stored value may lack ─── */
|
|
137
136
|
const byName = useMemo(() => new Map(icons.map((e) => [e.name, e])), [icons]);
|
|
138
|
-
const entryOf = useCallback(
|
|
139
|
-
|
|
137
|
+
const entryOf = useCallback(
|
|
138
|
+
(n) => n ? byName.get(n) ?? byName.get(`Ntd${n}`) : void 0,
|
|
139
|
+
[byName]
|
|
140
|
+
);
|
|
141
|
+
const labelOf = useCallback(
|
|
142
|
+
(n) => {
|
|
140
143
|
const entry = entryOf(n);
|
|
141
144
|
return entry ? entryLabel(entry) : iconDisplayName(n);
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
145
|
+
},
|
|
146
|
+
[entryOf]
|
|
147
|
+
);
|
|
148
|
+
const resolve = useCallback(
|
|
149
|
+
(n) => entryOf(n)?.Component,
|
|
150
|
+
[entryOf]
|
|
151
|
+
);
|
|
152
|
+
const [emitted, setEmitted] = useState(
|
|
153
|
+
null
|
|
154
|
+
);
|
|
148
155
|
const model = useMemo(() => {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
return
|
|
166
|
-
|
|
167
|
-
return entry && entry.name !== l.icon ? { ...l, icon: entry.name } : l;
|
|
168
|
-
});
|
|
156
|
+
if (!value) {
|
|
157
|
+
return NO_LAYERS;
|
|
158
|
+
}
|
|
159
|
+
if (emitted?.value === value) {
|
|
160
|
+
return emitted.layers;
|
|
161
|
+
}
|
|
162
|
+
const exact = value.includes("<") ? void 0 : entryOf(value);
|
|
163
|
+
if (exact) {
|
|
164
|
+
return [{ id: "1", icon: exact.name }];
|
|
165
|
+
}
|
|
166
|
+
const layers = parseIconValue(value);
|
|
167
|
+
if (!layers || !entryOf(layers[0].icon)) {
|
|
168
|
+
return NO_LAYERS;
|
|
169
|
+
}
|
|
170
|
+
return layers.map((l) => {
|
|
171
|
+
const entry = entryOf(l.icon);
|
|
172
|
+
return entry && entry.name !== l.icon ? { ...l, icon: entry.name } : l;
|
|
173
|
+
});
|
|
169
174
|
}, [value, emitted, entryOf]);
|
|
170
|
-
// A stack waiting to be drawn into HTML: shown at once, emitted once drawn (below).
|
|
171
175
|
const [pending, setPending] = useState(null);
|
|
172
|
-
const shown = pending
|
|
176
|
+
const shown = pending ?? model;
|
|
173
177
|
const base = shown[0];
|
|
174
|
-
|
|
175
|
-
|
|
178
|
+
const shortName = useCallback(
|
|
179
|
+
(n) => {
|
|
176
180
|
const short = iconDisplayName(n);
|
|
177
181
|
return short !== n && !byName.has(short) ? short : n;
|
|
178
|
-
|
|
182
|
+
},
|
|
183
|
+
[byName]
|
|
184
|
+
);
|
|
179
185
|
const onChangeRef = useRef(onChange);
|
|
180
186
|
onChangeRef.current = onChange;
|
|
181
|
-
const emit = useCallback(
|
|
182
|
-
|
|
187
|
+
const emit = useCallback(
|
|
188
|
+
(next, layers) => {
|
|
183
189
|
setEmitted(next === null ? null : { value: next, layers });
|
|
184
190
|
setValue(next);
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
191
|
+
onChangeRef.current?.(next, entryOf(layers[0]?.icon) ?? null);
|
|
192
|
+
},
|
|
193
|
+
[setValue, entryOf]
|
|
194
|
+
);
|
|
195
|
+
const commit = useCallback(
|
|
196
|
+
(layers) => {
|
|
189
197
|
if (layers.length === 0) {
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
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.
|
|
198
|
+
setPending(null);
|
|
199
|
+
emit(null, NO_LAYERS);
|
|
200
|
+
} else if (valueFormat === "html") {
|
|
201
|
+
setPending(layers);
|
|
202
|
+
} else {
|
|
203
|
+
emit(formatIconValue(layers, valueFormat, { shortName }), layers);
|
|
204
|
+
}
|
|
205
|
+
},
|
|
206
|
+
[valueFormat, emit, shortName]
|
|
207
|
+
);
|
|
202
208
|
useIsoLayoutEffect(() => {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
}
|
|
209
|
+
if (!pending) {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
const svg = serializerRef.current?.querySelector("svg");
|
|
213
|
+
setPending(null);
|
|
214
|
+
if (svg) {
|
|
215
|
+
emit(scopeIconHtmlIds(svg.outerHTML), pending);
|
|
216
|
+
}
|
|
212
217
|
}, [pending, emit]);
|
|
213
|
-
const selectedValues = useMemo(
|
|
214
|
-
|
|
215
|
-
|
|
218
|
+
const selectedValues = useMemo(
|
|
219
|
+
() => multiSelect ? values : base ? [base.icon] : NO_NAMES,
|
|
220
|
+
[multiSelect, values, base]
|
|
221
|
+
);
|
|
222
|
+
const addFavorite = useCallback(
|
|
223
|
+
(favorite) => {
|
|
216
224
|
setFavorites((prev) => {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
225
|
+
if (prev.includes(favorite)) {
|
|
226
|
+
return prev;
|
|
227
|
+
}
|
|
228
|
+
const next = [...prev, favorite];
|
|
229
|
+
saveToStorage(favoritesKey, next);
|
|
230
|
+
return next;
|
|
223
231
|
});
|
|
224
|
-
|
|
225
|
-
|
|
232
|
+
},
|
|
233
|
+
[favoritesKey]
|
|
234
|
+
);
|
|
235
|
+
const removeFavorite = useCallback(
|
|
236
|
+
(favorite) => {
|
|
226
237
|
setFavorites((prev) => {
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
238
|
+
const next = prev.filter((n) => n !== favorite);
|
|
239
|
+
saveToStorage(favoritesKey, next);
|
|
240
|
+
return next;
|
|
230
241
|
});
|
|
231
|
-
|
|
232
|
-
|
|
242
|
+
},
|
|
243
|
+
[favoritesKey]
|
|
244
|
+
);
|
|
245
|
+
const toggleFavorite = useCallback(
|
|
246
|
+
(favorite) => {
|
|
233
247
|
if (favorites.includes(favorite)) {
|
|
234
|
-
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
addFavorite(favorite);
|
|
248
|
+
removeFavorite(favorite);
|
|
249
|
+
} else {
|
|
250
|
+
addFavorite(favorite);
|
|
238
251
|
}
|
|
239
|
-
|
|
252
|
+
},
|
|
253
|
+
[favorites, addFavorite, removeFavorite]
|
|
254
|
+
);
|
|
240
255
|
const clearFavorites = useCallback(() => {
|
|
241
|
-
|
|
242
|
-
|
|
256
|
+
setFavorites([]);
|
|
257
|
+
saveToStorage(favoritesKey, []);
|
|
243
258
|
}, [favoritesKey]);
|
|
244
|
-
|
|
245
|
-
|
|
259
|
+
const addToHistory = useCallback(
|
|
260
|
+
(used) => {
|
|
246
261
|
setHistory((prev) => {
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
262
|
+
const filtered2 = prev.filter((n) => n !== used);
|
|
263
|
+
const next = [used, ...filtered2].slice(0, MAX_HISTORY_ITEMS);
|
|
264
|
+
saveToStorage(historyKey, next);
|
|
265
|
+
return next;
|
|
251
266
|
});
|
|
252
|
-
|
|
267
|
+
},
|
|
268
|
+
[historyKey]
|
|
269
|
+
);
|
|
253
270
|
const clearHistory = useCallback(() => {
|
|
254
|
-
|
|
255
|
-
|
|
271
|
+
setHistory([]);
|
|
272
|
+
saveToStorage(historyKey, []);
|
|
256
273
|
}, [historyKey]);
|
|
257
|
-
/* ─── Imperative handle ─── */
|
|
258
274
|
useImperativeHandle(ref, () => ({
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
275
|
+
open: () => setIsOpen(true),
|
|
276
|
+
close: () => setIsOpen(false),
|
|
277
|
+
toggle: () => setIsOpen((o) => !o),
|
|
278
|
+
getFavorites: () => favorites,
|
|
279
|
+
addFavorite,
|
|
280
|
+
removeFavorite,
|
|
281
|
+
toggleFavorite,
|
|
282
|
+
clearFavorites,
|
|
283
|
+
getHistory: () => history,
|
|
284
|
+
clearHistory
|
|
269
285
|
}));
|
|
270
|
-
/* ─── Derive categories ─── */
|
|
271
286
|
const allCategories = useMemo(() => {
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
287
|
+
if (categoriesProp) {
|
|
288
|
+
return categoriesProp;
|
|
289
|
+
}
|
|
290
|
+
const seen = /* @__PURE__ */ new Set();
|
|
291
|
+
const result = [];
|
|
292
|
+
for (const icon of icons) {
|
|
293
|
+
if (!seen.has(icon.category)) {
|
|
294
|
+
seen.add(icon.category);
|
|
295
|
+
result.push(icon.category);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
return result;
|
|
284
299
|
}, [icons, categoriesProp]);
|
|
285
|
-
/* ─── Filter icons: by the name shown, the export name or the category ─── */
|
|
286
300
|
const filtered = useMemo(() => {
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
items = items.filter((i) => i.category === activeCategory);
|
|
305
|
-
}
|
|
306
|
-
return items;
|
|
301
|
+
let items = icons;
|
|
302
|
+
const q = search.trim().toLowerCase();
|
|
303
|
+
if (q) {
|
|
304
|
+
items = items.filter(
|
|
305
|
+
(i) => entryLabel(i).toLowerCase().includes(q) || i.name.toLowerCase().includes(q) || i.category.toLowerCase().includes(q)
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
if (activeCategory === "__favorites__") {
|
|
309
|
+
items = items.filter((i) => favorites.includes(i.name));
|
|
310
|
+
} else if (activeCategory === "__history__") {
|
|
311
|
+
const historySet = new Set(history);
|
|
312
|
+
items = items.filter((i) => historySet.has(i.name));
|
|
313
|
+
items.sort((a, b) => history.indexOf(a.name) - history.indexOf(b.name));
|
|
314
|
+
} else if (activeCategory) {
|
|
315
|
+
items = items.filter((i) => i.category === activeCategory);
|
|
316
|
+
}
|
|
317
|
+
return items;
|
|
307
318
|
}, [icons, search, activeCategory, favorites, history]);
|
|
308
|
-
/* ─── Virtualized items ─── */
|
|
309
319
|
const virtualizedFiltered = useMemo(() => {
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
320
|
+
if (!virtualized) {
|
|
321
|
+
return filtered;
|
|
322
|
+
}
|
|
323
|
+
return filtered.slice(visibleRange.start, visibleRange.end);
|
|
314
324
|
}, [filtered, virtualized, visibleRange]);
|
|
315
|
-
/* ─── Group by category ─── */
|
|
316
325
|
const grouped = useMemo(() => {
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
return map;
|
|
326
|
+
const items = virtualized ? virtualizedFiltered : filtered;
|
|
327
|
+
const map = /* @__PURE__ */ new Map();
|
|
328
|
+
for (const icon of items) {
|
|
329
|
+
const list = map.get(icon.category);
|
|
330
|
+
if (list) {
|
|
331
|
+
list.push(icon);
|
|
332
|
+
} else {
|
|
333
|
+
map.set(icon.category, [icon]);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
return map;
|
|
329
337
|
}, [filtered, virtualized, virtualizedFiltered]);
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
338
|
+
const selectedEntries = useMemo(
|
|
339
|
+
() => values.map((v) => entryOf(v)).filter((e) => e !== void 0),
|
|
340
|
+
[values, entryOf]
|
|
341
|
+
);
|
|
333
342
|
useEffect(() => {
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
343
|
+
if (!isOpen) {
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
function onClickOutside(e) {
|
|
347
|
+
if (rootRef.current && !rootRef.current.contains(e.target)) {
|
|
348
|
+
setIsOpen(false);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
document.addEventListener("mousedown", onClickOutside);
|
|
352
|
+
return () => document.removeEventListener("mousedown", onClickOutside);
|
|
344
353
|
}, [isOpen]);
|
|
345
|
-
/* ─── Reset on close ─── */
|
|
346
354
|
useEffect(() => {
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
355
|
+
if (!isOpen) {
|
|
356
|
+
setSearch("");
|
|
357
|
+
setActiveCategory(null);
|
|
358
|
+
setView("icons");
|
|
359
|
+
setPicking(null);
|
|
360
|
+
setVisibleRange({ start: 0, end: virtualizedItemCount });
|
|
361
|
+
}
|
|
354
362
|
}, [isOpen, virtualizedItemCount]);
|
|
355
|
-
/* ─── Keyboard and pad (1.3.0) ─── */
|
|
356
|
-
// The panel is a dialog on the overlay stack: focus on the search field when it opens, Escape
|
|
357
|
-
// / B close only it (a document listener before) and focus returns to the trigger. The icon
|
|
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).
|
|
360
363
|
const dropdownRef = useRef(null);
|
|
361
364
|
useOverlayFocus(dropdownRef, {
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
365
|
+
open: isOpen,
|
|
366
|
+
modal: false,
|
|
367
|
+
initialFocus: searchInputRef,
|
|
368
|
+
onClose: () => picking ? setPicking(null) : setIsOpen(false)
|
|
366
369
|
});
|
|
367
|
-
useRovingFocus(gridWrapRef, { orientation:
|
|
368
|
-
// into the grid to pick an icon to stack: focus the search; back: the layer it went into
|
|
370
|
+
useRovingFocus(gridWrapRef, { orientation: "grid", itemSelector: ".ntd-picker-cell" });
|
|
369
371
|
const wasPicking = useRef(false);
|
|
370
372
|
useEffect(() => {
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
373
|
+
if (picking) {
|
|
374
|
+
searchInputRef.current?.focus();
|
|
375
|
+
} else if (wasPicking.current) {
|
|
376
|
+
dropdownRef.current?.querySelector(
|
|
377
|
+
".nice-icon-stacker__layer--selected .nice-icon-stacker__layer-select"
|
|
378
|
+
)?.focus();
|
|
379
|
+
}
|
|
380
|
+
wasPicking.current = !!picking;
|
|
379
381
|
}, [picking]);
|
|
380
|
-
/* ─── Virtualization scroll handler ─── */
|
|
381
382
|
useEffect(() => {
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
383
|
+
if (!virtualized || !isOpen || !gridWrapRef.current) {
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
const handleScroll = () => {
|
|
387
|
+
const el2 = gridWrapRef.current;
|
|
388
|
+
if (!el2) {
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
const scrollTop = el2.scrollTop;
|
|
392
|
+
const itemHeight = 48;
|
|
393
|
+
const itemsPerRow = 6;
|
|
394
|
+
const rowHeight = itemHeight;
|
|
395
|
+
const visibleRows = Math.ceil(el2.clientHeight / rowHeight);
|
|
396
|
+
const startRow = Math.floor(scrollTop / rowHeight);
|
|
397
|
+
const buffer = 2;
|
|
398
|
+
const start = Math.max(0, (startRow - buffer) * itemsPerRow);
|
|
399
|
+
const end = Math.min(filtered.length, (startRow + visibleRows + buffer) * itemsPerRow);
|
|
400
|
+
setVisibleRange({ start, end });
|
|
401
|
+
};
|
|
402
|
+
const el = gridWrapRef.current;
|
|
403
|
+
el.addEventListener("scroll", handleScroll, { passive: true });
|
|
404
|
+
return () => el.removeEventListener("scroll", handleScroll);
|
|
404
405
|
}, [virtualized, isOpen, filtered.length]);
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
var _a;
|
|
408
|
-
// an icon to stack: into the layer the stacker has selected, then back to the stack
|
|
406
|
+
const handleSelect = useCallback(
|
|
407
|
+
(entry) => {
|
|
409
408
|
if (picking) {
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
409
|
+
stackerRef.current?.add({ icon: entry.name });
|
|
410
|
+
setPicking(null);
|
|
411
|
+
setSearch("");
|
|
412
|
+
return;
|
|
414
413
|
}
|
|
415
|
-
// Add to history
|
|
416
414
|
if (showHistory) {
|
|
417
|
-
|
|
415
|
+
addToHistory(entry.name);
|
|
418
416
|
}
|
|
419
417
|
if (multiSelect) {
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
return; // Max selections reached
|
|
428
|
-
}
|
|
429
|
-
newValues = [...values, entry.name];
|
|
418
|
+
const isSelected = values.includes(entry.name);
|
|
419
|
+
let newValues;
|
|
420
|
+
if (isSelected) {
|
|
421
|
+
newValues = values.filter((v) => v !== entry.name);
|
|
422
|
+
} else {
|
|
423
|
+
if (maxSelections && values.length >= maxSelections) {
|
|
424
|
+
return;
|
|
430
425
|
}
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
}
|
|
448
|
-
}, [
|
|
426
|
+
newValues = [...values, entry.name];
|
|
427
|
+
}
|
|
428
|
+
setValues(newValues);
|
|
429
|
+
if (onChangeMulti) {
|
|
430
|
+
const entries = newValues.map((v) => entryOf(v)).filter((e) => e !== void 0);
|
|
431
|
+
onChangeMulti(newValues, entries);
|
|
432
|
+
}
|
|
433
|
+
} else {
|
|
434
|
+
const own = valueFormat === "html" ? { variant } : {};
|
|
435
|
+
commit(
|
|
436
|
+
canStack && base ? [{ ...base, icon: entry.name, ...own }, ...shown.slice(1)] : [{ id: "1", icon: entry.name, ...own }]
|
|
437
|
+
);
|
|
438
|
+
setIsOpen(false);
|
|
439
|
+
}
|
|
440
|
+
},
|
|
441
|
+
[
|
|
449
442
|
picking,
|
|
450
443
|
variant,
|
|
451
444
|
multiSelect,
|
|
@@ -460,171 +453,460 @@ const NiceIconPicker = /* @__PURE__ */ forwardRef(function NiceIconPicker({ icon
|
|
|
460
453
|
canStack,
|
|
461
454
|
base,
|
|
462
455
|
shown,
|
|
463
|
-
commit
|
|
464
|
-
|
|
465
|
-
|
|
456
|
+
commit
|
|
457
|
+
]
|
|
458
|
+
);
|
|
459
|
+
const handleClear = useCallback(
|
|
460
|
+
(e) => {
|
|
466
461
|
e.stopPropagation();
|
|
467
462
|
if (multiSelect) {
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
463
|
+
setValues(NO_NAMES);
|
|
464
|
+
onChangeMulti?.([], []);
|
|
465
|
+
} else {
|
|
466
|
+
commit(NO_LAYERS);
|
|
467
|
+
setView("icons");
|
|
468
|
+
}
|
|
469
|
+
},
|
|
470
|
+
[multiSelect, setValues, onChangeMulti, commit]
|
|
471
|
+
);
|
|
476
472
|
const handleToggle = useCallback(() => {
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
473
|
+
if (!disabled) {
|
|
474
|
+
setIsOpen((o) => !o);
|
|
475
|
+
}
|
|
480
476
|
}, [disabled]);
|
|
481
|
-
const handleFavoriteClick = useCallback(
|
|
477
|
+
const handleFavoriteClick = useCallback(
|
|
478
|
+
(e, favorite) => {
|
|
482
479
|
e.stopPropagation();
|
|
483
480
|
toggleFavorite(favorite);
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
481
|
+
},
|
|
482
|
+
[toggleFavorite]
|
|
483
|
+
);
|
|
484
|
+
const handleDragStart = useCallback(
|
|
485
|
+
(entry, e) => {
|
|
487
486
|
if (!enableDragAndDrop) {
|
|
488
|
-
|
|
487
|
+
return;
|
|
489
488
|
}
|
|
490
|
-
// Set various data formats for maximum compatibility
|
|
491
489
|
const readName = entryLabel(entry);
|
|
492
|
-
|
|
493
|
-
e.dataTransfer.setData('text/plain', readName);
|
|
494
|
-
// JSON data with full icon info
|
|
490
|
+
e.dataTransfer.setData("text/plain", readName);
|
|
495
491
|
const iconData = {
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
492
|
+
name: entry.name,
|
|
493
|
+
readableName: readName,
|
|
494
|
+
category: entry.category,
|
|
495
|
+
variant
|
|
500
496
|
};
|
|
501
|
-
e.dataTransfer.setData(
|
|
502
|
-
e.dataTransfer.setData(
|
|
503
|
-
// The cell's own drawing as an image
|
|
497
|
+
e.dataTransfer.setData("application/json", JSON.stringify(iconData));
|
|
498
|
+
e.dataTransfer.setData("text/x-ntd-icon", JSON.stringify(iconData));
|
|
504
499
|
try {
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
}
|
|
522
|
-
e.dataTransfer.effectAllowed =
|
|
523
|
-
onDragStart
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
500
|
+
const svgElement = e.currentTarget.querySelector("svg");
|
|
501
|
+
if (svgElement) {
|
|
502
|
+
const svgClone = svgElement.cloneNode(true);
|
|
503
|
+
svgClone.setAttribute("width", "64");
|
|
504
|
+
svgClone.setAttribute("height", "64");
|
|
505
|
+
const svgString = new XMLSerializer().serializeToString(svgClone);
|
|
506
|
+
const svgBlob = new Blob([svgString], { type: "image/svg+xml" });
|
|
507
|
+
e.dataTransfer.setData(
|
|
508
|
+
"text/html",
|
|
509
|
+
`<img src="data:image/svg+xml;base64,${btoa(svgString)}" alt="${readName}" />`
|
|
510
|
+
);
|
|
511
|
+
const img = new Image(64, 64);
|
|
512
|
+
img.src = URL.createObjectURL(svgBlob);
|
|
513
|
+
e.dataTransfer.setDragImage(img, 32, 32);
|
|
514
|
+
}
|
|
515
|
+
} catch {
|
|
516
|
+
}
|
|
517
|
+
e.dataTransfer.effectAllowed = "copy";
|
|
518
|
+
onDragStart?.(entry, e);
|
|
519
|
+
},
|
|
520
|
+
[enableDragAndDrop, variant, onDragStart]
|
|
521
|
+
);
|
|
522
|
+
const handleDragEnd = useCallback(
|
|
523
|
+
(entry, e) => {
|
|
524
|
+
onDragEnd?.(entry, e);
|
|
525
|
+
},
|
|
526
|
+
[onDragEnd]
|
|
527
|
+
);
|
|
528
528
|
const triggerLabel = useMemo(() => {
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
return
|
|
529
|
+
if (multiSelect) {
|
|
530
|
+
if (selectedEntries.length === 1) {
|
|
531
|
+
return entryLabel(selectedEntries[0]);
|
|
532
|
+
}
|
|
533
|
+
if (selectedEntries.length > 1) {
|
|
534
|
+
return t("iconPicker.iconsSelected", "{count} icons selected").replace(
|
|
535
|
+
"{count}",
|
|
536
|
+
String(selectedEntries.length)
|
|
537
|
+
);
|
|
538
|
+
}
|
|
539
|
+
} else if (base) {
|
|
540
|
+
return labelOf(base.icon);
|
|
541
|
+
}
|
|
542
|
+
return placeholder ?? t("iconPicker.selectIcon", "Select icon…");
|
|
541
543
|
}, [multiSelect, selectedEntries, base, labelOf, placeholder, t]);
|
|
542
544
|
const hasSelection = multiSelect ? selectedEntries.length > 0 : !!base;
|
|
543
|
-
const iconOnly = displayMode ===
|
|
544
|
-
|
|
545
|
-
const triggerName = ariaLabel
|
|
546
|
-
? `${ariaLabel}: ${triggerLabel}`
|
|
547
|
-
: iconOnly
|
|
548
|
-
? triggerLabel
|
|
549
|
-
: undefined;
|
|
545
|
+
const iconOnly = displayMode === "icon";
|
|
546
|
+
const triggerName = ariaLabel ? `${ariaLabel}: ${triggerLabel}` : iconOnly ? triggerLabel : void 0;
|
|
550
547
|
const overlays = Math.max(0, shown.length - 1);
|
|
551
|
-
const showGrid = view ===
|
|
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.
|
|
548
|
+
const showGrid = view === "icons" || !!picking;
|
|
555
549
|
const [placement, setPlacement] = useState({
|
|
556
|
-
|
|
557
|
-
|
|
550
|
+
up: false,
|
|
551
|
+
end: false
|
|
558
552
|
});
|
|
559
553
|
useIsoLayoutEffect(() => {
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
554
|
+
const root = rootRef.current;
|
|
555
|
+
if (!isOpen || !root || typeof window === "undefined") {
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
const r = root.getBoundingClientRect();
|
|
559
|
+
const bounds = visibleBounds(root);
|
|
560
|
+
const want = showGrid ? 460 : 620;
|
|
561
|
+
const below = bounds.bottom - r.bottom - 12;
|
|
562
|
+
const above = r.top - bounds.top - 12;
|
|
563
|
+
const up = below < want && above > below;
|
|
564
|
+
const room = up ? above : below;
|
|
565
|
+
setPlacement({
|
|
566
|
+
up,
|
|
567
|
+
end: r.left + 380 > bounds.right - 8 && r.right - bounds.left >= 380,
|
|
568
|
+
maxHeight: room < want ? Math.max(240, Math.floor(room)) : void 0
|
|
569
|
+
});
|
|
576
570
|
}, [isOpen, showGrid]);
|
|
577
|
-
const pickingInto = picking ? shown.find((l) => l.id === picking.target) :
|
|
578
|
-
const SingleMulti =
|
|
579
|
-
return
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
571
|
+
const pickingInto = picking ? shown.find((l) => l.id === picking.target) : void 0;
|
|
572
|
+
const SingleMulti = selectedEntries[0]?.Component;
|
|
573
|
+
return /* @__PURE__ */ jsxs(
|
|
574
|
+
"div",
|
|
575
|
+
{
|
|
576
|
+
ref: rootRef,
|
|
577
|
+
id,
|
|
578
|
+
"data-testid": dataTestId ?? testId,
|
|
579
|
+
className: `ntd-picker${isOpen ? " ntd-picker--open" : ""}${disabled ? " ntd-picker--disabled" : ""}${multiSelect ? " ntd-picker--multi" : ""}${iconOnly ? " ntd-picker--icon-only" : ""}${className ? ` ${className}` : ""}`,
|
|
580
|
+
children: [
|
|
581
|
+
/* @__PURE__ */ jsxs(
|
|
582
|
+
"button",
|
|
583
|
+
{
|
|
584
|
+
type: "button",
|
|
585
|
+
className: "ntd-picker-trigger",
|
|
586
|
+
onClick: handleToggle,
|
|
587
|
+
disabled,
|
|
588
|
+
"aria-haspopup": "dialog",
|
|
589
|
+
"aria-expanded": isOpen,
|
|
590
|
+
"aria-label": triggerName,
|
|
591
|
+
title: iconOnly ? triggerLabel : void 0,
|
|
592
|
+
children: [
|
|
593
|
+
/* @__PURE__ */ jsx("span", { className: "ntd-picker-trigger-icon", children: !multiSelect && base ? renderIconStack(shown, resolve, {
|
|
594
|
+
baseProps: { ...drawProps, size: triggerIconSize }
|
|
595
|
+
}) : multiSelect && selectedEntries.length > 1 ? /* @__PURE__ */ jsx("span", { className: "ntd-picker-multi-badge", children: selectedEntries.length }) : SingleMulti ? /* @__PURE__ */ jsx(SingleMulti, { ...drawProps, size: triggerIconSize }) : /* @__PURE__ */ jsx(NtdGrid, { size: 16, variant: "outline" }) }),
|
|
596
|
+
!iconOnly && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
597
|
+
/* @__PURE__ */ jsx(
|
|
598
|
+
"span",
|
|
599
|
+
{
|
|
600
|
+
className: `ntd-picker-trigger-label${hasSelection ? "" : " ntd-picker-trigger-placeholder"}`,
|
|
601
|
+
children: triggerLabel
|
|
602
|
+
}
|
|
603
|
+
),
|
|
604
|
+
/* @__PURE__ */ jsx("span", { className: "ntd-picker-trigger-chevron", children: /* @__PURE__ */ jsx(Chevron, {}) })
|
|
605
|
+
] })
|
|
606
|
+
]
|
|
607
|
+
}
|
|
608
|
+
),
|
|
609
|
+
name && (multiSelect ? values.map((v) => /* @__PURE__ */ jsx("input", { type: "hidden", name, value: v }, v)) : /* @__PURE__ */ jsx("input", { type: "hidden", name, value: value ?? "" })),
|
|
610
|
+
pending && /* @__PURE__ */ jsx("span", { ref: serializerRef, className: "ntd-picker-serializer", hidden: true, children: renderIconStack(pending, resolve, {
|
|
611
|
+
baseProps: {
|
|
612
|
+
...drawProps,
|
|
613
|
+
width: "1em",
|
|
614
|
+
height: "1em",
|
|
615
|
+
title: labelOf(pending[0].icon),
|
|
616
|
+
...iconHtmlAttributes(pending)
|
|
617
|
+
}
|
|
618
|
+
}) }),
|
|
619
|
+
isOpen && /* @__PURE__ */ jsxs(
|
|
620
|
+
"div",
|
|
621
|
+
{
|
|
622
|
+
ref: dropdownRef,
|
|
623
|
+
className: `ntd-picker-dropdown${showGrid ? "" : " ntd-picker-dropdown--stack"}${placement.up ? " ntd-picker-dropdown--up" : ""}${placement.end ? " ntd-picker-dropdown--end" : ""}`,
|
|
624
|
+
style: placement.maxHeight ? { maxHeight: placement.maxHeight } : void 0,
|
|
625
|
+
role: "dialog",
|
|
626
|
+
"aria-label": t("iconPicker.selectIcon", "Select icon…"),
|
|
627
|
+
children: [
|
|
628
|
+
canStack && (picking ? /* @__PURE__ */ jsxs("div", { className: "ntd-picker-banner", children: [
|
|
629
|
+
/* @__PURE__ */ jsx(
|
|
630
|
+
"button",
|
|
631
|
+
{
|
|
632
|
+
type: "button",
|
|
633
|
+
className: "ntd-picker-back",
|
|
634
|
+
onClick: () => setPicking(null),
|
|
635
|
+
"aria-label": t("iconPicker.back", "Back"),
|
|
636
|
+
title: t("iconPicker.back", "Back"),
|
|
637
|
+
children: /* @__PURE__ */ jsx(NtdArrowLeft, { size: 14, variant: "outline" })
|
|
638
|
+
}
|
|
639
|
+
),
|
|
640
|
+
/* @__PURE__ */ jsx("span", { children: t("iconPicker.pickOverlay", "Pick an icon to put on {icon}").replace(
|
|
641
|
+
"{icon}",
|
|
642
|
+
pickingInto ? labelOf(pickingInto.icon) : base ? labelOf(base.icon) : ""
|
|
643
|
+
) })
|
|
644
|
+
] }) : /* @__PURE__ */ jsxs(
|
|
645
|
+
"div",
|
|
646
|
+
{
|
|
647
|
+
className: "ntd-picker-views",
|
|
648
|
+
ref: viewsRef,
|
|
649
|
+
role: "tablist",
|
|
650
|
+
"aria-label": t("iconPicker.selectIcon", "Select icon…"),
|
|
651
|
+
children: [
|
|
652
|
+
/* @__PURE__ */ jsx(
|
|
653
|
+
"button",
|
|
654
|
+
{
|
|
655
|
+
type: "button",
|
|
656
|
+
role: "tab",
|
|
657
|
+
id: `${panelId}-icons`,
|
|
658
|
+
"aria-selected": view === "icons",
|
|
659
|
+
"aria-controls": `${panelId}-panel`,
|
|
660
|
+
className: `ntd-picker-view${view === "icons" ? " ntd-picker-view--active" : ""}`,
|
|
661
|
+
onClick: () => setView("icons"),
|
|
662
|
+
children: t("iconPicker.tabIcon", "Icon")
|
|
663
|
+
}
|
|
664
|
+
),
|
|
665
|
+
/* @__PURE__ */ jsxs(
|
|
666
|
+
"button",
|
|
667
|
+
{
|
|
668
|
+
type: "button",
|
|
669
|
+
role: "tab",
|
|
670
|
+
id: `${panelId}-stack`,
|
|
671
|
+
"aria-selected": view === "stack",
|
|
672
|
+
"aria-controls": `${panelId}-panel`,
|
|
673
|
+
className: `ntd-picker-view${view === "stack" ? " ntd-picker-view--active" : ""}`,
|
|
674
|
+
onClick: () => setView("stack"),
|
|
675
|
+
disabled: !base,
|
|
676
|
+
title: base ? void 0 : t("iconPicker.stackNeedsIcon", "Pick an icon first"),
|
|
677
|
+
children: [
|
|
678
|
+
t("iconPicker.tabStack", "Overlays"),
|
|
679
|
+
overlays > 0 && /* @__PURE__ */ jsx("span", { className: "ntd-picker-tab-count", children: overlays })
|
|
680
|
+
]
|
|
681
|
+
}
|
|
682
|
+
)
|
|
683
|
+
]
|
|
684
|
+
}
|
|
685
|
+
)),
|
|
686
|
+
/* @__PURE__ */ jsxs(
|
|
687
|
+
"div",
|
|
688
|
+
{
|
|
689
|
+
className: "ntd-picker-panel",
|
|
690
|
+
id: `${panelId}-panel`,
|
|
691
|
+
...canStack && !picking ? {
|
|
692
|
+
role: "tabpanel",
|
|
693
|
+
"aria-labelledby": `${panelId}-${view}`
|
|
694
|
+
} : {},
|
|
695
|
+
children: [
|
|
696
|
+
showGrid && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
697
|
+
/* @__PURE__ */ jsxs("div", { className: "ntd-picker-search", children: [
|
|
698
|
+
/* @__PURE__ */ jsx("span", { className: "ntd-picker-search-icon", children: /* @__PURE__ */ jsx(SearchIcon, {}) }),
|
|
699
|
+
/* @__PURE__ */ jsx(
|
|
700
|
+
"input",
|
|
701
|
+
{
|
|
702
|
+
ref: searchInputRef,
|
|
703
|
+
type: "text",
|
|
704
|
+
placeholder: t("iconPicker.searchIcons", "Search icons…"),
|
|
705
|
+
value: search,
|
|
706
|
+
onChange: (e) => setSearch(e.target.value),
|
|
707
|
+
"aria-label": t("iconPicker.searchIcons2", "Search icons")
|
|
595
708
|
}
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
709
|
+
)
|
|
710
|
+
] }),
|
|
711
|
+
(allCategories.length > 1 || showFavorites || showHistory) && /* @__PURE__ */ jsxs("div", { className: "ntd-picker-tabs", ref: tablistRef, role: "tablist", children: [
|
|
712
|
+
/* @__PURE__ */ jsx(
|
|
713
|
+
"button",
|
|
714
|
+
{
|
|
715
|
+
type: "button",
|
|
716
|
+
className: `ntd-picker-tab${activeCategory === null ? " ntd-picker-tab--active" : ""}`,
|
|
717
|
+
onClick: () => setActiveCategory(null),
|
|
718
|
+
role: "tab",
|
|
719
|
+
"aria-selected": activeCategory === null,
|
|
720
|
+
children: t("iconPicker.all", "All")
|
|
721
|
+
}
|
|
722
|
+
),
|
|
723
|
+
showFavorites && /* @__PURE__ */ jsxs(
|
|
724
|
+
"button",
|
|
725
|
+
{
|
|
726
|
+
type: "button",
|
|
727
|
+
className: `ntd-picker-tab ntd-picker-tab-special${activeCategory === "__favorites__" ? " ntd-picker-tab--active" : ""}`,
|
|
728
|
+
onClick: () => setActiveCategory("__favorites__"),
|
|
729
|
+
role: "tab",
|
|
730
|
+
"aria-selected": activeCategory === "__favorites__",
|
|
731
|
+
title: t("iconPicker.favorites", "Favorites"),
|
|
732
|
+
children: [
|
|
733
|
+
/* @__PURE__ */ jsx(StarIcon, { filled: true }),
|
|
734
|
+
" ",
|
|
735
|
+
t("iconPicker.favorites", "Favorites"),
|
|
736
|
+
favorites.length > 0 && /* @__PURE__ */ jsx("span", { className: "ntd-picker-tab-count", children: favorites.length })
|
|
737
|
+
]
|
|
738
|
+
}
|
|
739
|
+
),
|
|
740
|
+
showHistory && history.length > 0 && /* @__PURE__ */ jsxs(
|
|
741
|
+
"button",
|
|
742
|
+
{
|
|
743
|
+
type: "button",
|
|
744
|
+
className: `ntd-picker-tab ntd-picker-tab-special${activeCategory === "__history__" ? " ntd-picker-tab--active" : ""}`,
|
|
745
|
+
onClick: () => setActiveCategory("__history__"),
|
|
746
|
+
role: "tab",
|
|
747
|
+
"aria-selected": activeCategory === "__history__",
|
|
748
|
+
title: t("iconPicker.recentlyUsed", "Recently used"),
|
|
749
|
+
children: [
|
|
750
|
+
/* @__PURE__ */ jsx(ClockIcon, {}),
|
|
751
|
+
" ",
|
|
752
|
+
t("iconPicker.recent", "Recent")
|
|
753
|
+
]
|
|
754
|
+
}
|
|
755
|
+
),
|
|
756
|
+
allCategories.map((cat) => /* @__PURE__ */ jsx(
|
|
757
|
+
"button",
|
|
758
|
+
{
|
|
759
|
+
type: "button",
|
|
760
|
+
className: `ntd-picker-tab${activeCategory === cat ? " ntd-picker-tab--active" : ""}`,
|
|
761
|
+
onClick: () => setActiveCategory(cat),
|
|
762
|
+
role: "tab",
|
|
763
|
+
"aria-selected": activeCategory === cat,
|
|
764
|
+
children: cat
|
|
765
|
+
},
|
|
766
|
+
cat
|
|
767
|
+
))
|
|
768
|
+
] }),
|
|
769
|
+
/* @__PURE__ */ jsx(
|
|
770
|
+
"div",
|
|
771
|
+
{
|
|
772
|
+
className: "ntd-picker-grid-wrap",
|
|
773
|
+
ref: gridWrapRef,
|
|
774
|
+
role: "listbox",
|
|
775
|
+
"aria-label": t("iconPicker.icons", "Icons"),
|
|
776
|
+
"aria-multiselectable": multiSelect || void 0,
|
|
777
|
+
children: filtered.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "ntd-picker-empty", children: [
|
|
778
|
+
/* @__PURE__ */ jsx("div", { className: "ntd-picker-empty-icon", children: /* @__PURE__ */ jsx(NtdInbox, { size: 28, variant: "outline" }) }),
|
|
779
|
+
activeCategory === "__favorites__" ? t(
|
|
780
|
+
"iconPicker.noFavoritesYetClickThe",
|
|
781
|
+
"No favorites yet. Click the star icon to add."
|
|
782
|
+
) : activeCategory === "__history__" ? t("iconPicker.noRecentIcons", "No recent icons.") : t("iconPicker.noIconsFound", "No icons found")
|
|
783
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
784
|
+
virtualized && visibleRange.start > 0 && /* @__PURE__ */ jsx("div", { style: { height: Math.floor(visibleRange.start / 6) * 48 } }),
|
|
785
|
+
Array.from(grouped.entries()).map(([category, entries]) => /* @__PURE__ */ jsxs("div", { children: [
|
|
786
|
+
!activeCategory && /* @__PURE__ */ jsx("div", { className: "ntd-picker-category-label", children: category }),
|
|
787
|
+
/* @__PURE__ */ jsx(
|
|
788
|
+
"div",
|
|
789
|
+
{
|
|
790
|
+
className: `ntd-picker-grid${optionDisplayMode === "icon-name" ? " ntd-picker-grid--names" : ""}`,
|
|
791
|
+
children: entries.map((entry) => {
|
|
792
|
+
const isSelected = !picking && selectedValues.includes(entry.name);
|
|
793
|
+
const isFavorite = favorites.includes(entry.name);
|
|
794
|
+
const label = entryLabel(entry);
|
|
795
|
+
const cell = /* @__PURE__ */ jsxs(
|
|
796
|
+
"button",
|
|
797
|
+
{
|
|
798
|
+
type: "button",
|
|
799
|
+
className: `ntd-picker-cell${isSelected ? " ntd-picker-cell--selected" : ""}${multiSelect ? " ntd-picker-cell--multi-select" : ""}${enableDragAndDrop ? " ntd-picker-cell--draggable" : ""}`,
|
|
800
|
+
onClick: () => handleSelect(entry),
|
|
801
|
+
"data-name": label,
|
|
802
|
+
role: "option",
|
|
803
|
+
"aria-selected": isSelected,
|
|
804
|
+
"aria-label": label,
|
|
805
|
+
title: label,
|
|
806
|
+
draggable: enableDragAndDrop,
|
|
807
|
+
onDragStart: enableDragAndDrop ? (e) => handleDragStart(entry, e) : void 0,
|
|
808
|
+
onDragEnd: enableDragAndDrop ? (e) => handleDragEnd(entry, e) : void 0,
|
|
809
|
+
children: [
|
|
810
|
+
/* @__PURE__ */ jsx(entry.Component, { ...drawProps, size: gridIconSize }),
|
|
811
|
+
optionDisplayMode === "icon-name" && /* @__PURE__ */ jsx("span", { className: "ntd-picker-cell-name", children: label }),
|
|
812
|
+
multiSelect && isSelected && /* @__PURE__ */ jsx("span", { className: "ntd-picker-cell-check", children: /* @__PURE__ */ jsx(CheckIcon, {}) })
|
|
813
|
+
]
|
|
814
|
+
},
|
|
815
|
+
entry.name
|
|
816
|
+
);
|
|
817
|
+
if (!showFavorites) {
|
|
818
|
+
return cell;
|
|
819
|
+
}
|
|
820
|
+
const favLabel = isFavorite ? t("iconPicker.removeFromFavorites", "Remove from favorites") : t("iconPicker.addToFavorites", "Add to favorites");
|
|
821
|
+
return /* @__PURE__ */ jsxs("div", { className: "ntd-picker-cell-wrap", children: [
|
|
822
|
+
cell,
|
|
823
|
+
/* @__PURE__ */ jsx(
|
|
824
|
+
"button",
|
|
825
|
+
{
|
|
826
|
+
type: "button",
|
|
827
|
+
tabIndex: -1,
|
|
828
|
+
className: `ntd-picker-cell-fav${isFavorite ? " ntd-picker-cell-fav--active" : ""}`,
|
|
829
|
+
onClick: (e) => handleFavoriteClick(e, entry.name),
|
|
830
|
+
"aria-label": `${favLabel}: ${label}`,
|
|
831
|
+
title: favLabel,
|
|
832
|
+
children: /* @__PURE__ */ jsx(StarIcon, { filled: isFavorite })
|
|
833
|
+
}
|
|
834
|
+
)
|
|
835
|
+
] }, entry.name);
|
|
836
|
+
})
|
|
837
|
+
}
|
|
838
|
+
)
|
|
839
|
+
] }, category)),
|
|
840
|
+
virtualized && visibleRange.end < filtered.length && /* @__PURE__ */ jsx(
|
|
841
|
+
"div",
|
|
842
|
+
{
|
|
843
|
+
style: {
|
|
844
|
+
height: Math.ceil((filtered.length - visibleRange.end) / 6) * 48
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
)
|
|
848
|
+
] })
|
|
849
|
+
}
|
|
850
|
+
)
|
|
851
|
+
] }),
|
|
852
|
+
canStack && view === "stack" && base && /* @__PURE__ */ jsx("div", { className: "ntd-picker-stack", hidden: !!picking, children: /* @__PURE__ */ jsx(
|
|
853
|
+
NiceIconStacker,
|
|
854
|
+
{
|
|
855
|
+
ref: stackerRef,
|
|
856
|
+
icons: resolve,
|
|
857
|
+
layers: shown,
|
|
858
|
+
onLayersChange: commit,
|
|
859
|
+
onAddClick: (target) => {
|
|
860
|
+
setSearch("");
|
|
861
|
+
setActiveCategory(null);
|
|
862
|
+
setPicking({ target: target?.id ?? base.id });
|
|
863
|
+
},
|
|
864
|
+
previewProps: drawProps,
|
|
865
|
+
iconLabel: labelOf,
|
|
866
|
+
showCode: false,
|
|
867
|
+
keepBase: true
|
|
868
|
+
}
|
|
869
|
+
) })
|
|
870
|
+
]
|
|
871
|
+
}
|
|
872
|
+
),
|
|
873
|
+
!picking && /* @__PURE__ */ jsxs("div", { className: "ntd-picker-footer", children: [
|
|
874
|
+
/* @__PURE__ */ jsx("span", { className: "ntd-picker-count", children: showGrid && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
875
|
+
(filtered.length === 1 ? t("iconPicker.iconCountOne", "{count} icon") : t("iconPicker.iconCountMany", "{count} icons")).replace("{count}", String(filtered.length)),
|
|
876
|
+
multiSelect && selectedValues.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
877
|
+
" ",
|
|
878
|
+
t("iconPicker.selected", "• {selectedValues} selected").replace(
|
|
879
|
+
"{selectedValues}",
|
|
880
|
+
String(selectedValues.length)
|
|
881
|
+
)
|
|
882
|
+
] }),
|
|
883
|
+
maxSelections && multiSelect && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
884
|
+
" ",
|
|
885
|
+
t("iconPicker.max", "(max {maxSelections})").replace(
|
|
886
|
+
"{maxSelections}",
|
|
887
|
+
String(maxSelections)
|
|
888
|
+
)
|
|
889
|
+
] })
|
|
890
|
+
] }) }),
|
|
891
|
+
clearable && hasSelection && /* @__PURE__ */ jsx("button", { type: "button", className: "ntd-picker-clear", onClick: handleClear, children: multiSelect ? t("iconPicker.clearAll", "Clear all") : t("iconPicker.clearSelection", "Clear selection") }),
|
|
892
|
+
(multiSelect || view === "stack") && /* @__PURE__ */ jsx(
|
|
893
|
+
"button",
|
|
894
|
+
{
|
|
895
|
+
type: "button",
|
|
896
|
+
className: "ntd-picker-done",
|
|
897
|
+
onClick: () => setIsOpen(false),
|
|
898
|
+
children: t("iconPicker.done", "Done")
|
|
899
|
+
}
|
|
900
|
+
)
|
|
901
|
+
] })
|
|
902
|
+
]
|
|
903
|
+
}
|
|
904
|
+
)
|
|
905
|
+
]
|
|
906
|
+
}
|
|
907
|
+
);
|
|
908
|
+
}
|
|
909
|
+
);
|
|
910
|
+
export {
|
|
911
|
+
NiceIconPicker
|
|
912
|
+
};
|