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