@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.
Files changed (153) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +1 -1
  3. package/dist/cjs/NiceDesktopIconExporter.js +688 -599
  4. package/dist/cjs/NiceFaviconGenerator.js +603 -417
  5. package/dist/cjs/NiceIconPicker.js +788 -508
  6. package/dist/cjs/NiceIconPreview.js +246 -188
  7. package/dist/cjs/NiceIconSetCreator.js +683 -552
  8. package/dist/cjs/NiceIconStacker.js +547 -171
  9. package/dist/cjs/NiceIconValue.js +27 -35
  10. package/dist/cjs/NtdIconBadge.js +384 -360
  11. package/dist/cjs/NtdIconMorph.js +419 -354
  12. package/dist/cjs/NtdStateMorph.js +150 -147
  13. package/dist/cjs/advancedIconSearch.js +373 -510
  14. package/dist/cjs/animationControls.js +518 -642
  15. package/dist/cjs/animationSpeed.js +69 -0
  16. package/dist/cjs/client.js +12 -15
  17. package/dist/cjs/createIcon.js +374 -252
  18. package/dist/cjs/design-tokens.js +324 -390
  19. package/dist/cjs/figmaExport.js +162 -257
  20. package/dist/cjs/figmaSync.js +234 -316
  21. package/dist/cjs/headless-ui.js +240 -368
  22. package/dist/cjs/iconAnalytics.js +337 -393
  23. package/dist/cjs/iconHtml.js +64 -86
  24. package/dist/cjs/iconMetadata.js +392 -472
  25. package/dist/cjs/iconParts.js +120 -90
  26. package/dist/cjs/iconStack.js +136 -155
  27. package/dist/cjs/iconStyles.js +1670 -509
  28. package/dist/cjs/iconValue.js +359 -430
  29. package/dist/cjs/icons/accessibility.js +1119 -134
  30. package/dist/cjs/icons/actions.js +1290 -330
  31. package/dist/cjs/icons/ai.js +1424 -144
  32. package/dist/cjs/icons/brand.js +613 -184
  33. package/dist/cjs/icons/business.js +938 -192
  34. package/dist/cjs/icons/devtech.js +1656 -227
  35. package/dist/cjs/icons/ecommerce.js +802 -111
  36. package/dist/cjs/icons/education.js +1345 -199
  37. package/dist/cjs/icons/files.js +1597 -328
  38. package/dist/cjs/icons/fintech.js +793 -104
  39. package/dist/cjs/icons/food.js +1028 -156
  40. package/dist/cjs/icons/fun.js +804 -109
  41. package/dist/cjs/icons/gaming.js +916 -170
  42. package/dist/cjs/icons/health.js +623 -112
  43. package/dist/cjs/icons/index.js +32 -42
  44. package/dist/cjs/icons/input.js +282 -83
  45. package/dist/cjs/icons/legal.js +1065 -122
  46. package/dist/cjs/icons/media.js +1061 -267
  47. package/dist/cjs/icons/navigation.js +407 -123
  48. package/dist/cjs/icons/omniverk.js +523 -93
  49. package/dist/cjs/icons/realestate.js +1520 -193
  50. package/dist/cjs/icons/saas.js +867 -98
  51. package/dist/cjs/icons/science.js +1344 -202
  52. package/dist/cjs/icons/shapes.js +1316 -308
  53. package/dist/cjs/icons/smarthome.js +786 -90
  54. package/dist/cjs/icons/social.js +591 -59
  55. package/dist/cjs/icons/sports.js +1133 -133
  56. package/dist/cjs/icons/sustainability.js +1211 -139
  57. package/dist/cjs/icons/travel.js +1470 -148
  58. package/dist/cjs/icons/ui.js +1508 -315
  59. package/dist/cjs/icons/weather.js +1160 -173
  60. package/dist/cjs/index.js +559 -568
  61. package/dist/cjs/lottieIntegration.js +261 -266
  62. package/dist/cjs/nicetodev-icons.css +1 -1
  63. package/dist/cjs/particleEffects.js +270 -251
  64. package/dist/cjs/resolver.js +229 -284
  65. package/dist/cjs/rsc.js +175 -193
  66. package/dist/cjs/sharpen.js +73 -130
  67. package/dist/cjs/smilAnimations.js +380 -165
  68. package/dist/cjs/stacking.js +87 -122
  69. package/dist/cjs/tailwind-plugin.js +280 -320
  70. package/dist/cjs/types.js +46 -72
  71. package/dist/cjs/utilities.js +163 -324
  72. package/dist/cjs/variantProps.js +12 -52
  73. package/dist/esm/NiceDesktopIconExporter.js +671 -598
  74. package/dist/esm/NiceFaviconGenerator.js +583 -416
  75. package/dist/esm/NiceIconPicker.js +789 -507
  76. package/dist/esm/NiceIconPreview.js +247 -187
  77. package/dist/esm/NiceIconSetCreator.js +685 -551
  78. package/dist/esm/NiceIconStacker.js +549 -170
  79. package/dist/esm/NiceIconValue.js +29 -34
  80. package/dist/esm/NtdIconBadge.js +388 -359
  81. package/dist/esm/NtdIconMorph.js +423 -353
  82. package/dist/esm/NtdStateMorph.js +152 -146
  83. package/dist/esm/advancedIconSearch.js +391 -509
  84. package/dist/esm/animationControls.js +527 -641
  85. package/dist/esm/animationSpeed.js +69 -0
  86. package/dist/esm/client.js +48 -10
  87. package/dist/esm/createIcon.js +375 -251
  88. package/dist/esm/design-tokens.js +334 -387
  89. package/dist/esm/figmaExport.js +170 -256
  90. package/dist/esm/figmaSync.js +237 -315
  91. package/dist/esm/headless-ui.js +254 -365
  92. package/dist/esm/iconAnalytics.js +342 -392
  93. package/dist/esm/iconHtml.js +67 -85
  94. package/dist/esm/iconMetadata.js +404 -471
  95. package/dist/esm/iconParts.js +125 -89
  96. package/dist/esm/iconStack.js +145 -154
  97. package/dist/esm/iconStyles.js +1681 -508
  98. package/dist/esm/iconValue.js +361 -429
  99. package/dist/esm/icons/accessibility.js +1133 -133
  100. package/dist/esm/icons/actions.js +1320 -329
  101. package/dist/esm/icons/ai.js +1441 -143
  102. package/dist/esm/icons/brand.js +616 -179
  103. package/dist/esm/icons/business.js +957 -191
  104. package/dist/esm/icons/devtech.js +1687 -226
  105. package/dist/esm/icons/ecommerce.js +816 -110
  106. package/dist/esm/icons/education.js +1364 -198
  107. package/dist/esm/icons/files.js +1628 -327
  108. package/dist/esm/icons/fintech.js +807 -103
  109. package/dist/esm/icons/food.js +1043 -153
  110. package/dist/esm/icons/fun.js +819 -108
  111. package/dist/esm/icons/gaming.js +933 -169
  112. package/dist/esm/icons/health.js +636 -111
  113. package/dist/esm/icons/index.js +589 -32
  114. package/dist/esm/icons/input.js +326 -82
  115. package/dist/esm/icons/legal.js +1079 -121
  116. package/dist/esm/icons/media.js +1086 -266
  117. package/dist/esm/icons/navigation.js +421 -122
  118. package/dist/esm/icons/omniverk.js +534 -91
  119. package/dist/esm/icons/realestate.js +1544 -198
  120. package/dist/esm/icons/saas.js +880 -97
  121. package/dist/esm/icons/science.js +1370 -208
  122. package/dist/esm/icons/shapes.js +1343 -307
  123. package/dist/esm/icons/smarthome.js +799 -89
  124. package/dist/esm/icons/social.js +599 -58
  125. package/dist/esm/icons/sports.js +1148 -132
  126. package/dist/esm/icons/sustainability.js +1226 -138
  127. package/dist/esm/icons/travel.js +1486 -147
  128. package/dist/esm/icons/ui.js +1540 -314
  129. package/dist/esm/icons/weather.js +1181 -172
  130. package/dist/esm/index.js +749 -58
  131. package/dist/esm/lottieIntegration.js +264 -265
  132. package/dist/esm/nicetodev-icons.css +1 -1
  133. package/dist/esm/particleEffects.js +271 -250
  134. package/dist/esm/resolver.js +231 -283
  135. package/dist/esm/rsc.js +182 -192
  136. package/dist/esm/sharpen.js +78 -129
  137. package/dist/esm/smilAnimations.js +385 -164
  138. package/dist/esm/stacking.js +99 -121
  139. package/dist/esm/tailwind-plugin.js +282 -319
  140. package/dist/esm/types.js +50 -71
  141. package/dist/esm/utilities.js +177 -321
  142. package/dist/esm/variantProps.js +23 -52
  143. package/dist/types/animationSpeed.d.ts +19 -0
  144. package/dist/types/createIcon.d.ts +6 -0
  145. package/dist/types/iconStyles.d.ts +6 -1
  146. package/dist/types/icons/brand.d.ts +0 -4
  147. package/dist/types/icons/food.d.ts +0 -3
  148. package/dist/types/icons/index.d.ts +3 -3
  149. package/dist/types/icons/omniverk.d.ts +0 -3
  150. package/dist/types/icons/science.d.ts +1 -1
  151. package/dist/types/smilAnimations.d.ts +4 -2
  152. package/dist/types/types.d.ts +7 -0
  153. package/package.json +15 -22
@@ -1,453 +1,446 @@
1
- 'use strict';
2
-
3
- var jsxRuntime = require('react/jsx-runtime');
4
- var uiCore = require('@nice2dev/ui-core');
5
- var React = require('react');
6
- var iconHtml = require('./iconHtml.js');
7
- var actions = require('./icons/actions.js');
8
- var files = require('./icons/files.js');
9
- var media = require('./icons/media.js');
10
- var navigation = require('./icons/navigation.js');
11
- var ui = require('./icons/ui.js');
12
- var iconStack = require('./iconStack.js');
13
- var iconValue = require('./iconValue.js');
14
- var NiceIconStacker = require('./NiceIconStacker.js');
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
- /** `useLayoutEffect` in the browser, `useEffect` on the server (no warning there). */
18
- const useIsoLayoutEffect = typeof window === 'undefined' ? React.useEffect : React.useLayoutEffect;
19
- /* ─── Small icons: the package's own (until 1.2.0 these were pasted SVGs) ─── */
20
- const Chevron = () => jsxRuntime.jsx(navigation.NtdChevronDown, { size: 12, variant: "outline" });
21
- const SearchIcon = () => jsxRuntime.jsx(actions.NtdSearch, { size: 14, variant: "outline" });
22
- const StarIcon = ({ filled }) => (jsxRuntime.jsx(ui.NtdStar, { size: 12, variant: filled ? 'solid' : 'outline' }));
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
- /** The accent the picker draws with: the page's background, white without a theme. */
28
- const PAGE_ACCENT = 'var(--nice-bg-primary, white)';
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
- const bounds = { top: 0, bottom: window.innerHeight, left: 0, right: window.innerWidth };
34
- for (let p = el.parentElement; p && p !== document.body; p = p.parentElement) {
35
- const style = window.getComputedStyle(p);
36
- if (style.overflowX !== 'visible' || style.overflowY !== 'visible') {
37
- const r = p.getBoundingClientRect();
38
- bounds.top = Math.max(bounds.top, r.top);
39
- bounds.bottom = Math.min(bounds.bottom, r.bottom);
40
- bounds.left = Math.max(bounds.left, r.left);
41
- bounds.right = Math.min(bounds.right, r.right);
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
- return bounds;
38
+ }
39
+ return bounds;
45
40
  }
46
- /* ─── localStorage helpers ─── */
47
41
  function loadFromStorage(key, fallback) {
48
- if (typeof window === 'undefined') {
49
- return fallback;
50
- }
51
- try {
52
- const stored = localStorage.getItem(key);
53
- return stored ? JSON.parse(stored) : fallback;
54
- }
55
- catch (_a) {
56
- return fallback;
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
- if (typeof window === 'undefined') {
61
- return;
62
- }
63
- try {
64
- localStorage.setItem(key, JSON.stringify(value));
65
- }
66
- catch (_a) {
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
- NiceIconPicker Component
72
- ═══════════════════════════════════════════════════════ */
73
- /**
74
- * A fully-featured icon picker dropdown component.
75
- *
76
- * Provides a trigger button that opens a searchable, categorized icon grid. The value binds both
77
- * ways — `value` / `defaultValue` + `onChange` (1.3.9) — as the icon's export name, a stack as
78
- * short code (`Folder(Star@tr)`), JSON or HTML (`valueFormat`). With `stackable` the panel gets
79
- * an "Overlays" tab where icons are stacked on the chosen one. Names show without `Ntd`; the
80
- * trigger shows the icon alone or the icon and its name (`displayMode`). Keyboard: arrows in the
81
- * grid, Escape to close.
82
- *
83
- * **Styling:** Uses CSS variables for theming (see `NiceIconPicker.css`).
84
- * Override `--ntd-picker-bg`, `--ntd-picker-accent`, etc. on a parent element.
85
- *
86
- * @example
87
- * ```tsx
88
- * import { NiceIconPicker } from '@nice2dev/icons';
89
- *
90
- * function IconField({ icons, value, onChange }) {
91
- * // stores `NtdFolder`, or `Folder(Star@tr)` with a star stacked in its corner
92
- * return <NiceIconPicker icons={icons} value={value} onChange={onChange} stackable />;
93
- * }
94
- * ```
95
- */
96
- const NiceIconPicker = /* @__PURE__ */ React.forwardRef(function NiceIconPicker({ icons, value: valueProp, defaultValue, values: valuesProp, defaultValues, onChange, onChangeMulti, valueFormat: valueFormatProp, stackable = false, displayMode = 'icon-name', optionDisplayMode = 'icon', name, 'aria-label': ariaLabel, id, testId, 'data-testid': dataTestId, multiSelect = false, maxSelections, placeholder, variant = 'solid', iconProps, gridIconSize = 'md', triggerIconSize = 'sm', categories: categoriesProp, className, disabled = false, clearable = true, virtualized = false, virtualizedItemCount = 100, showHistory = false, showFavorites = false, storageKeyPrefix = 'ntd-icon-picker', enableDragAndDrop = false, onDragStart, onDragEnd, }, ref) {
97
- var _a;
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: 'horizontal', wrap: true, activateOnMove: true });
100
+ uiCore.useRovingFocus(tablistRef, { orientation: "horizontal", wrap: true, activateOnMove: true });
100
101
  const viewsRef = React.useRef(null);
101
- uiCore.useRovingFocus(viewsRef, { orientation: 'horizontal', activateOnMove: true });
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
- // the panel's view: the icon grid or the stack; `picking` = the grid chooses an icon to stack
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 !== null && valueFormatProp !== void 0 ? valueFormatProp : 'name';
110
+ const valueFormat = valueFormatProp ?? "name";
111
111
  const canStack = stackable && !multiSelect;
112
- const drawProps = React.useMemo(() => ({ accentColor: PAGE_ACCENT, ...iconProps, variant }), [iconProps, variant]);
113
- // Virtualization state
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
- value: valueProp,
124
- defaultValue,
125
- fallbackValue: null,
126
- componentName: 'NiceIconPicker',
123
+ value: valueProp,
124
+ defaultValue,
125
+ fallbackValue: null,
126
+ componentName: "NiceIconPicker"
127
127
  });
128
128
  const [values, setValues] = uiCore.useControllableState({
129
- value: valuesProp,
130
- defaultValue: defaultValues,
131
- fallbackValue: NO_NAMES,
132
- componentName: 'NiceIconPicker',
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((n) => { var _a; return (n ? ((_a = byName.get(n)) !== null && _a !== void 0 ? _a : byName.get(`Ntd${n}`)) : undefined); }, [byName]);
141
- const labelOf = React.useCallback((n) => {
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
- }, [entryOf]);
145
- const resolve = React.useCallback((n) => { var _a; return (_a = entryOf(n)) === null || _a === void 0 ? void 0 : _a.Component; }, [entryOf]);
146
- /* ─── The single value as a stack — a plain icon is a stack of one ─── */
147
- // The last value emitted with its layers: read back, the text would number the layers anew
148
- // (it keeps no ids) and the stacker would lose the layer it has selected.
149
- const [emitted, setEmitted] = React.useState(null);
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
- if (!value) {
152
- return NO_LAYERS;
153
- }
154
- if ((emitted === null || emitted === void 0 ? void 0 : emitted.value) === value) {
155
- return emitted.layers;
156
- }
157
- // a name as is first: an older value may hold one of the code's signs
158
- const exact = value.includes('<') ? undefined : entryOf(value);
159
- if (exact) {
160
- return [{ id: '1', icon: exact.name }];
161
- }
162
- const layers = iconValue.parseIconValue(value);
163
- if (!layers || !entryOf(layers[0].icon)) {
164
- return NO_LAYERS;
165
- }
166
- // the code names icons without `Ntd`; the stacker and the grid know them by entry name
167
- return layers.map((l) => {
168
- const entry = entryOf(l.icon);
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 !== null && pending !== void 0 ? pending : model;
178
+ const shown = pending ?? model;
175
179
  const base = shown[0];
176
- /** The name a stack's code gives an icon: without `Ntd`, unless that is another icon's name. */
177
- const shortName = React.useCallback((n) => {
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
- }, [byName]);
184
+ },
185
+ [byName]
186
+ );
181
187
  const onChangeRef = React.useRef(onChange);
182
188
  onChangeRef.current = onChange;
183
- const emit = React.useCallback((next, layers) => {
184
- var _a, _b, _c;
189
+ const emit = React.useCallback(
190
+ (next, layers) => {
185
191
  setEmitted(next === null ? null : { value: next, layers });
186
192
  setValue(next);
187
- (_a = onChangeRef.current) === null || _a === void 0 ? void 0 : _a.call(onChangeRef, next, (_c = entryOf((_b = layers[0]) === null || _b === void 0 ? void 0 : _b.icon)) !== null && _c !== void 0 ? _c : null);
188
- }, [setValue, entryOf]);
189
- /** Sets the single value to a stack: written as `valueFormat` holds it, HTML once drawn. */
190
- const commit = React.useCallback((layers) => {
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
- setPending(null);
193
- emit(null, NO_LAYERS);
194
- }
195
- else if (valueFormat === 'html') {
196
- setPending(layers);
197
- }
198
- else {
199
- emit(iconValue.formatIconValue(layers, valueFormat, { shortName }), layers);
200
- }
201
- }, [valueFormat, emit, shortName]);
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
- var _a;
206
- if (!pending) {
207
- return;
208
- }
209
- const svg = (_a = serializerRef.current) === null || _a === void 0 ? void 0 : _a.querySelector('svg');
210
- setPending(null);
211
- if (svg) {
212
- emit(iconHtml.scopeIconHtmlIds(svg.outerHTML), pending);
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(() => (multiSelect ? values : base ? [base.icon] : NO_NAMES), [multiSelect, values, base]);
216
- /* ─── Favorites helpers ─── */
217
- const addFavorite = React.useCallback((favorite) => {
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
- if (prev.includes(favorite)) {
220
- return prev;
221
- }
222
- const next = [...prev, favorite];
223
- saveToStorage(favoritesKey, next);
224
- return next;
227
+ if (prev.includes(favorite)) {
228
+ return prev;
229
+ }
230
+ const next = [...prev, favorite];
231
+ saveToStorage(favoritesKey, next);
232
+ return next;
225
233
  });
226
- }, [favoritesKey]);
227
- const removeFavorite = React.useCallback((favorite) => {
234
+ },
235
+ [favoritesKey]
236
+ );
237
+ const removeFavorite = React.useCallback(
238
+ (favorite) => {
228
239
  setFavorites((prev) => {
229
- const next = prev.filter((n) => n !== favorite);
230
- saveToStorage(favoritesKey, next);
231
- return next;
240
+ const next = prev.filter((n) => n !== favorite);
241
+ saveToStorage(favoritesKey, next);
242
+ return next;
232
243
  });
233
- }, [favoritesKey]);
234
- const toggleFavorite = React.useCallback((favorite) => {
244
+ },
245
+ [favoritesKey]
246
+ );
247
+ const toggleFavorite = React.useCallback(
248
+ (favorite) => {
235
249
  if (favorites.includes(favorite)) {
236
- removeFavorite(favorite);
237
- }
238
- else {
239
- addFavorite(favorite);
250
+ removeFavorite(favorite);
251
+ } else {
252
+ addFavorite(favorite);
240
253
  }
241
- }, [favorites, addFavorite, removeFavorite]);
254
+ },
255
+ [favorites, addFavorite, removeFavorite]
256
+ );
242
257
  const clearFavorites = React.useCallback(() => {
243
- setFavorites([]);
244
- saveToStorage(favoritesKey, []);
258
+ setFavorites([]);
259
+ saveToStorage(favoritesKey, []);
245
260
  }, [favoritesKey]);
246
- /* ─── History helpers ─── */
247
- const addToHistory = React.useCallback((used) => {
261
+ const addToHistory = React.useCallback(
262
+ (used) => {
248
263
  setHistory((prev) => {
249
- const filtered = prev.filter((n) => n !== used);
250
- const next = [used, ...filtered].slice(0, MAX_HISTORY_ITEMS);
251
- saveToStorage(historyKey, next);
252
- return next;
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
- }, [historyKey]);
269
+ },
270
+ [historyKey]
271
+ );
255
272
  const clearHistory = React.useCallback(() => {
256
- setHistory([]);
257
- saveToStorage(historyKey, []);
273
+ setHistory([]);
274
+ saveToStorage(historyKey, []);
258
275
  }, [historyKey]);
259
- /* ─── Imperative handle ─── */
260
276
  React.useImperativeHandle(ref, () => ({
261
- open: () => setIsOpen(true),
262
- close: () => setIsOpen(false),
263
- toggle: () => setIsOpen((o) => !o),
264
- getFavorites: () => favorites,
265
- addFavorite,
266
- removeFavorite,
267
- toggleFavorite,
268
- clearFavorites,
269
- getHistory: () => history,
270
- clearHistory,
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
- if (categoriesProp) {
275
- return categoriesProp;
276
- }
277
- const seen = new Set();
278
- const result = [];
279
- for (const icon of icons) {
280
- if (!seen.has(icon.category)) {
281
- seen.add(icon.category);
282
- result.push(icon.category);
283
- }
284
- }
285
- return result;
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
- let items = icons;
290
- const q = search.trim().toLowerCase();
291
- if (q) {
292
- items = items.filter((i) => entryLabel(i).toLowerCase().includes(q) ||
293
- i.name.toLowerCase().includes(q) ||
294
- i.category.toLowerCase().includes(q));
295
- }
296
- if (activeCategory === '__favorites__') {
297
- items = items.filter((i) => favorites.includes(i.name));
298
- }
299
- else if (activeCategory === '__history__') {
300
- const historySet = new Set(history);
301
- items = items.filter((i) => historySet.has(i.name));
302
- // Sort by history order
303
- items.sort((a, b) => history.indexOf(a.name) - history.indexOf(b.name));
304
- }
305
- else if (activeCategory) {
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
- if (!virtualized) {
313
- return filtered;
314
- }
315
- return filtered.slice(visibleRange.start, visibleRange.end);
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
- const items = virtualized ? virtualizedFiltered : filtered;
320
- const map = new Map();
321
- for (const icon of items) {
322
- const list = map.get(icon.category);
323
- if (list) {
324
- list.push(icon);
325
- }
326
- else {
327
- map.set(icon.category, [icon]);
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
- /* ─── Selected entries (multi-select) ─── */
333
- const selectedEntries = React.useMemo(() => values.map((v) => entryOf(v)).filter((e) => e !== undefined), [values, entryOf]);
334
- /* ─── Click outside ─── */
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
- if (!isOpen) {
337
- return;
338
- }
339
- function onClickOutside(e) {
340
- if (rootRef.current && !rootRef.current.contains(e.target)) {
341
- setIsOpen(false);
342
- }
343
- }
344
- document.addEventListener('mousedown', onClickOutside);
345
- return () => document.removeEventListener('mousedown', onClickOutside);
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
- if (!isOpen) {
350
- setSearch('');
351
- setActiveCategory(null);
352
- setView('icons');
353
- setPicking(null);
354
- setVisibleRange({ start: 0, end: virtualizedItemCount });
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
- open: isOpen,
365
- modal: false,
366
- initialFocus: searchInputRef,
367
- onClose: () => (picking ? setPicking(null) : setIsOpen(false)),
367
+ open: isOpen,
368
+ modal: false,
369
+ initialFocus: searchInputRef,
370
+ onClose: () => picking ? setPicking(null) : setIsOpen(false)
368
371
  });
369
- uiCore.useRovingFocus(gridWrapRef, { orientation: 'grid', itemSelector: '.ntd-picker-cell' });
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
- var _a, _b, _c;
374
- if (picking) {
375
- (_a = searchInputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
376
- }
377
- else if (wasPicking.current) {
378
- (_c = (_b = dropdownRef.current) === null || _b === void 0 ? void 0 : _b.querySelector('.nice-icon-stacker__layer--selected .nice-icon-stacker__layer-select')) === null || _c === void 0 ? void 0 : _c.focus();
379
- }
380
- wasPicking.current = !!picking;
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
- if (!virtualized || !isOpen || !gridWrapRef.current) {
385
- return;
386
- }
387
- const handleScroll = () => {
388
- const el = gridWrapRef.current;
389
- if (!el) {
390
- return;
391
- }
392
- const scrollTop = el.scrollTop;
393
- const itemHeight = 48; // Approximate cell height
394
- const itemsPerRow = 6; // Approximate items per row
395
- const rowHeight = itemHeight;
396
- const visibleRows = Math.ceil(el.clientHeight / rowHeight);
397
- const startRow = Math.floor(scrollTop / rowHeight);
398
- const buffer = 2; // Extra rows to render
399
- const start = Math.max(0, (startRow - buffer) * itemsPerRow);
400
- const end = Math.min(filtered.length, (startRow + visibleRows + buffer) * itemsPerRow);
401
- setVisibleRange({ start, end });
402
- };
403
- const el = gridWrapRef.current;
404
- el.addEventListener('scroll', handleScroll, { passive: true });
405
- return () => el.removeEventListener('scroll', handleScroll);
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
- /* ─── Handlers ─── */
408
- const handleSelect = React.useCallback((entry) => {
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
- (_a = stackerRef.current) === null || _a === void 0 ? void 0 : _a.add({ icon: entry.name });
413
- setPicking(null);
414
- setSearch('');
415
- return;
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
- addToHistory(entry.name);
417
+ addToHistory(entry.name);
420
418
  }
421
419
  if (multiSelect) {
422
- const isSelected = values.includes(entry.name);
423
- let newValues;
424
- if (isSelected) {
425
- newValues = values.filter((v) => v !== entry.name);
426
- }
427
- else {
428
- if (maxSelections && values.length >= maxSelections) {
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
- setValues(newValues);
434
- if (onChangeMulti) {
435
- const entries = newValues
436
- .map((v) => entryOf(v))
437
- .filter((e) => e !== undefined);
438
- onChangeMulti(newValues, entries);
439
- }
440
- // Don't close in multi-select mode
441
- }
442
- else {
443
- // the HTML records the variant it was drawn in; swapping the icon keeps its stack
444
- const own = valueFormat === 'html' ? { variant } : {};
445
- commit(canStack && base
446
- ? [{ ...base, icon: entry.name, ...own }, ...shown.slice(1)]
447
- : [{ id: '1', icon: entry.name, ...own }]);
448
- setIsOpen(false);
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
- const handleClear = React.useCallback((e) => {
458
+ commit
459
+ ]
460
+ );
461
+ const handleClear = React.useCallback(
462
+ (e) => {
468
463
  e.stopPropagation();
469
464
  if (multiSelect) {
470
- setValues(NO_NAMES);
471
- onChangeMulti === null || onChangeMulti === void 0 ? void 0 : onChangeMulti([], []);
472
- }
473
- else {
474
- commit(NO_LAYERS);
475
- setView('icons');
476
- }
477
- }, [multiSelect, setValues, onChangeMulti, commit]);
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
- if (!disabled) {
480
- setIsOpen((o) => !o);
481
- }
475
+ if (!disabled) {
476
+ setIsOpen((o) => !o);
477
+ }
482
478
  }, [disabled]);
483
- const handleFavoriteClick = React.useCallback((e, favorite) => {
479
+ const handleFavoriteClick = React.useCallback(
480
+ (e, favorite) => {
484
481
  e.stopPropagation();
485
482
  toggleFavorite(favorite);
486
- }, [toggleFavorite]);
487
- /* ─── Drag & Drop handlers ─── */
488
- const handleDragStart = React.useCallback((entry, e) => {
483
+ },
484
+ [toggleFavorite]
485
+ );
486
+ const handleDragStart = React.useCallback(
487
+ (entry, e) => {
489
488
  if (!enableDragAndDrop) {
490
- return;
489
+ return;
491
490
  }
492
- // Set various data formats for maximum compatibility
493
491
  const readName = entryLabel(entry);
494
- // Plain text - icon name
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
- name: entry.name,
499
- readableName: readName,
500
- category: entry.category,
501
- variant,
494
+ name: entry.name,
495
+ readableName: readName,
496
+ category: entry.category,
497
+ variant
502
498
  };
503
- e.dataTransfer.setData('application/json', JSON.stringify(iconData));
504
- e.dataTransfer.setData('text/x-ntd-icon', JSON.stringify(iconData));
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
- const svgElement = e.currentTarget.querySelector('svg');
508
- if (svgElement) {
509
- const svgClone = svgElement.cloneNode(true);
510
- svgClone.setAttribute('width', '64');
511
- svgClone.setAttribute('height', '64');
512
- const svgString = new XMLSerializer().serializeToString(svgClone);
513
- const svgBlob = new Blob([svgString], { type: 'image/svg+xml' });
514
- e.dataTransfer.setData('text/html', `<img src="data:image/svg+xml;base64,${btoa(svgString)}" alt="${readName}" />`);
515
- // Create drag image
516
- const img = new Image(64, 64);
517
- img.src = URL.createObjectURL(svgBlob);
518
- e.dataTransfer.setDragImage(img, 32, 32);
519
- }
520
- }
521
- catch (_a) {
522
- // Fallback - use default drag image
523
- }
524
- e.dataTransfer.effectAllowed = 'copy';
525
- onDragStart === null || onDragStart === void 0 ? void 0 : onDragStart(entry, e);
526
- }, [enableDragAndDrop, variant, onDragStart]);
527
- const handleDragEnd = React.useCallback((entry, e) => {
528
- onDragEnd === null || onDragEnd === void 0 ? void 0 : onDragEnd(entry, e);
529
- }, [onDragEnd]);
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
- 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('{count}', String(selectedEntries.length));
537
- }
538
- }
539
- else if (base) {
540
- return labelOf(base.icon);
541
- }
542
- return placeholder !== null && placeholder !== void 0 ? placeholder : t('iconPicker.selectIcon', 'Select icon…');
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 === 'icon';
546
- // the field's label first, then what is chosen — the icon-only trigger has no text of its own
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 === 'icons' || !!picking;
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
- up: false,
559
- end: false,
552
+ up: false,
553
+ end: false
560
554
  });
561
555
  useIsoLayoutEffect(() => {
562
- const root = rootRef.current;
563
- if (!isOpen || !root || typeof window === 'undefined') {
564
- return;
565
- }
566
- const r = root.getBoundingClientRect();
567
- const bounds = visibleBounds(root);
568
- const want = showGrid ? 460 : 620;
569
- const below = bounds.bottom - r.bottom - 12;
570
- const above = r.top - bounds.top - 12;
571
- const up = below < want && above > below;
572
- const room = up ? above : below;
573
- setPlacement({
574
- up,
575
- end: r.left + 380 > bounds.right - 8 && r.right - bounds.left >= 380,
576
- maxHeight: room < want ? Math.max(240, Math.floor(room)) : undefined,
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) : undefined;
580
- const SingleMulti = (_a = selectedEntries[0]) === null || _a === void 0 ? void 0 : _a.Component;
581
- return (jsxRuntime.jsxs("div", { ref: rootRef, id: id, "data-testid": dataTestId !== null && dataTestId !== void 0 ? dataTestId : testId, className: `ntd-picker${isOpen ? ' ntd-picker--open' : ''}${disabled ? ' ntd-picker--disabled' : ''}${multiSelect ? ' ntd-picker--multi' : ''}${iconOnly ? ' ntd-picker--icon-only' : ''}${className ? ` ${className}` : ''}`, children: [jsxRuntime.jsxs("button", { type: "button", className: "ntd-picker-trigger", onClick: handleToggle, disabled: disabled, "aria-haspopup": "dialog", "aria-expanded": isOpen, "aria-label": triggerName, title: iconOnly ? triggerLabel : undefined, children: [jsxRuntime.jsx("span", { className: "ntd-picker-trigger-icon", children: !multiSelect && base ? (iconStack.renderIconStack(shown, resolve, {
582
- baseProps: { ...drawProps, size: triggerIconSize },
583
- })) : multiSelect && selectedEntries.length > 1 ? (jsxRuntime.jsx("span", { className: "ntd-picker-multi-badge", children: selectedEntries.length })) : SingleMulti ? (jsxRuntime.jsx(SingleMulti, { ...drawProps, size: triggerIconSize })) : (jsxRuntime.jsx(files.NtdGrid, { size: 16, variant: "outline" })) }), !iconOnly && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("span", { className: `ntd-picker-trigger-label${hasSelection ? '' : ' ntd-picker-trigger-placeholder'}`, children: triggerLabel }), jsxRuntime.jsx("span", { className: "ntd-picker-trigger-chevron", children: jsxRuntime.jsx(Chevron, {}) })] }))] }), name &&
584
- (multiSelect ? (values.map((v) => jsxRuntime.jsx("input", { type: "hidden", name: name, value: v }, v))) : (jsxRuntime.jsx("input", { type: "hidden", name: name, value: value !== null && value !== void 0 ? value : '' }))), pending && (jsxRuntime.jsx("span", { ref: serializerRef, className: "ntd-picker-serializer", hidden: true, children: iconStack.renderIconStack(pending, resolve, {
585
- baseProps: {
586
- ...drawProps,
587
- width: '1em',
588
- height: '1em',
589
- title: labelOf(pending[0].icon),
590
- ...iconHtml.iconHtmlAttributes(pending),
591
- },
592
- }) })), isOpen && (jsxRuntime.jsxs("div", { ref: dropdownRef, className: `ntd-picker-dropdown${showGrid ? '' : ' ntd-picker-dropdown--stack'}${placement.up ? ' ntd-picker-dropdown--up' : ''}${placement.end ? ' ntd-picker-dropdown--end' : ''}`, style: placement.maxHeight ? { maxHeight: placement.maxHeight } : undefined, role: "dialog", "aria-label": t('iconPicker.selectIcon', 'Select icon…'), children: [canStack &&
593
- (picking ? (jsxRuntime.jsxs("div", { className: "ntd-picker-banner", children: [jsxRuntime.jsx("button", { type: "button", className: "ntd-picker-back", onClick: () => setPicking(null), "aria-label": t('iconPicker.back', 'Back'), title: t('iconPicker.back', 'Back'), children: jsxRuntime.jsx(navigation.NtdArrowLeft, { size: 14, variant: "outline" }) }), jsxRuntime.jsx("span", { children: t('iconPicker.pickOverlay', 'Pick an icon to put on {icon}').replace('{icon}', pickingInto ? labelOf(pickingInto.icon) : base ? labelOf(base.icon) : '') })] })) : (jsxRuntime.jsxs("div", { className: "ntd-picker-views", ref: viewsRef, role: "tablist", "aria-label": t('iconPicker.selectIcon', 'Select icon…'), children: [jsxRuntime.jsx("button", { type: "button", role: "tab", id: `${panelId}-icons`, "aria-selected": view === 'icons', "aria-controls": `${panelId}-panel`, className: `ntd-picker-view${view === 'icons' ? ' ntd-picker-view--active' : ''}`, onClick: () => setView('icons'), children: t('iconPicker.tabIcon', 'Icon') }), jsxRuntime.jsxs("button", { type: "button", role: "tab", id: `${panelId}-stack`, "aria-selected": view === 'stack', "aria-controls": `${panelId}-panel`, className: `ntd-picker-view${view === 'stack' ? ' ntd-picker-view--active' : ''}`, onClick: () => setView('stack'), disabled: !base, title: base ? undefined : t('iconPicker.stackNeedsIcon', 'Pick an icon first'), children: [t('iconPicker.tabStack', 'Overlays'), overlays > 0 && jsxRuntime.jsx("span", { className: "ntd-picker-tab-count", children: overlays })] })] }))), jsxRuntime.jsxs("div", { className: "ntd-picker-panel", id: `${panelId}-panel`, ...(canStack && !picking
594
- ? {
595
- role: 'tabpanel',
596
- 'aria-labelledby': `${panelId}-${view}`,
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
- : {}), children: [showGrid && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("div", { className: "ntd-picker-search", children: [jsxRuntime.jsx("span", { className: "ntd-picker-search-icon", children: jsxRuntime.jsx(SearchIcon, {}) }), jsxRuntime.jsx("input", { ref: searchInputRef, type: "text", placeholder: t('iconPicker.searchIcons', 'Search icons…'), value: search, onChange: (e) => setSearch(e.target.value), "aria-label": t('iconPicker.searchIcons2', 'Search icons') })] }), (allCategories.length > 1 || showFavorites || showHistory) && (jsxRuntime.jsxs("div", { className: "ntd-picker-tabs", ref: tablistRef, role: "tablist", children: [jsxRuntime.jsx("button", { type: "button", className: `ntd-picker-tab${activeCategory === null ? ' ntd-picker-tab--active' : ''}`, onClick: () => setActiveCategory(null), role: "tab", "aria-selected": activeCategory === null, children: t('iconPicker.all', 'All') }), showFavorites && (jsxRuntime.jsxs("button", { type: "button", className: `ntd-picker-tab ntd-picker-tab-special${activeCategory === '__favorites__' ? ' ntd-picker-tab--active' : ''}`, onClick: () => setActiveCategory('__favorites__'), role: "tab", "aria-selected": activeCategory === '__favorites__', title: t('iconPicker.favorites', 'Favorites'), children: [jsxRuntime.jsx(StarIcon, { filled: true }), " ", t('iconPicker.favorites', 'Favorites'), favorites.length > 0 && (jsxRuntime.jsx("span", { className: "ntd-picker-tab-count", children: favorites.length }))] })), showHistory && history.length > 0 && (jsxRuntime.jsxs("button", { type: "button", className: `ntd-picker-tab ntd-picker-tab-special${activeCategory === '__history__' ? ' ntd-picker-tab--active' : ''}`, onClick: () => setActiveCategory('__history__'), role: "tab", "aria-selected": activeCategory === '__history__', title: t('iconPicker.recentlyUsed', 'Recently used'), children: [jsxRuntime.jsx(ClockIcon, {}), " ", t('iconPicker.recent', 'Recent')] })), allCategories.map((cat) => (jsxRuntime.jsx("button", { type: "button", className: `ntd-picker-tab${activeCategory === cat ? ' ntd-picker-tab--active' : ''}`, onClick: () => setActiveCategory(cat), role: "tab", "aria-selected": activeCategory === cat, children: cat }, cat)))] })), jsxRuntime.jsx("div", { className: "ntd-picker-grid-wrap", ref: gridWrapRef, role: "listbox", "aria-label": t('iconPicker.icons', 'Icons'), "aria-multiselectable": multiSelect || undefined, children: filtered.length === 0 ? (jsxRuntime.jsxs("div", { className: "ntd-picker-empty", children: [jsxRuntime.jsx("div", { className: "ntd-picker-empty-icon", children: jsxRuntime.jsx(media.NtdInbox, { size: 28, variant: "outline" }) }), activeCategory === '__favorites__'
599
- ? t('iconPicker.noFavoritesYetClickThe', 'No favorites yet. Click the star icon to add.')
600
- : activeCategory === '__history__'
601
- ? t('iconPicker.noRecentIcons', 'No recent icons.')
602
- : t('iconPicker.noIconsFound', 'No icons found')] })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [virtualized && visibleRange.start > 0 && (jsxRuntime.jsx("div", { style: { height: Math.floor(visibleRange.start / 6) * 48 } })), Array.from(grouped.entries()).map(([category, entries]) => (jsxRuntime.jsxs("div", { children: [!activeCategory && (jsxRuntime.jsx("div", { className: "ntd-picker-category-label", children: category })), jsxRuntime.jsx("div", { className: `ntd-picker-grid${optionDisplayMode === 'icon-name' ? ' ntd-picker-grid--names' : ''}`, children: entries.map((entry) => {
603
- const isSelected = !picking && selectedValues.includes(entry.name);
604
- const isFavorite = favorites.includes(entry.name);
605
- const label = entryLabel(entry);
606
- const cell = (jsxRuntime.jsxs("button", { type: "button", className: `ntd-picker-cell${isSelected ? ' ntd-picker-cell--selected' : ''}${multiSelect ? ' ntd-picker-cell--multi-select' : ''}${enableDragAndDrop ? ' ntd-picker-cell--draggable' : ''}`, onClick: () => handleSelect(entry), "data-name": label, role: "option", "aria-selected": isSelected, "aria-label": label, title: label, draggable: enableDragAndDrop, onDragStart: enableDragAndDrop
607
- ? (e) => handleDragStart(entry, e)
608
- : undefined, onDragEnd: enableDragAndDrop ? (e) => handleDragEnd(entry, e) : undefined, children: [jsxRuntime.jsx(entry.Component, { ...drawProps, size: gridIconSize }), optionDisplayMode === 'icon-name' && (jsxRuntime.jsx("span", { className: "ntd-picker-cell-name", children: label })), multiSelect && isSelected && (jsxRuntime.jsx("span", { className: "ntd-picker-cell-check", children: jsxRuntime.jsx(CheckIcon, {}) }))] }, entry.name));
609
- if (!showFavorites) {
610
- return cell;
611
- }
612
- // the star beside the cell, not in it — a button in a button is
613
- // invalid HTML (1.3.9)
614
- const favLabel = isFavorite
615
- ? t('iconPicker.removeFromFavorites', 'Remove from favorites')
616
- : t('iconPicker.addToFavorites', 'Add to favorites');
617
- return (jsxRuntime.jsxs("div", { className: "ntd-picker-cell-wrap", children: [cell, jsxRuntime.jsx("button", { type: "button", tabIndex: -1, className: `ntd-picker-cell-fav${isFavorite ? ' ntd-picker-cell-fav--active' : ''}`, onClick: (e) => handleFavoriteClick(e, entry.name), "aria-label": `${favLabel}: ${label}`, title: favLabel, children: jsxRuntime.jsx(StarIcon, { filled: isFavorite }) })] }, entry.name));
618
- }) })] }, category))), virtualized && visibleRange.end < filtered.length && (jsxRuntime.jsx("div", { style: {
619
- height: Math.ceil((filtered.length - visibleRange.end) / 6) * 48,
620
- } }))] })) })] })), canStack && view === 'stack' && base && (jsxRuntime.jsx("div", { className: "ntd-picker-stack", hidden: !!picking, children: jsxRuntime.jsx(NiceIconStacker.NiceIconStacker, { ref: stackerRef, icons: resolve, layers: shown, onLayersChange: commit, onAddClick: (target) => {
621
- var _a;
622
- setSearch('');
623
- setActiveCategory(null);
624
- setPicking({ target: (_a = target === null || target === void 0 ? void 0 : target.id) !== null && _a !== void 0 ? _a : base.id });
625
- }, previewProps: drawProps, iconLabel: labelOf, showCode: false, keepBase: true }) }))] }), !picking && (jsxRuntime.jsxs("div", { className: "ntd-picker-footer", children: [jsxRuntime.jsx("span", { className: "ntd-picker-count", children: showGrid && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [(filtered.length === 1
626
- ? t('iconPicker.iconCountOne', '{count} icon')
627
- : t('iconPicker.iconCountMany', '{count} icons')).replace('{count}', String(filtered.length)), multiSelect && selectedValues.length > 0 && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [' ', t('iconPicker.selected', '• {selectedValues} selected').replace('{selectedValues}', String(selectedValues.length))] })), maxSelections && multiSelect && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [' ', t('iconPicker.max', '(max {maxSelections})').replace('{maxSelections}', String(maxSelections))] }))] })) }), clearable && hasSelection && (jsxRuntime.jsx("button", { type: "button", className: "ntd-picker-clear", onClick: handleClear, children: multiSelect
628
- ? t('iconPicker.clearAll', 'Clear all')
629
- : t('iconPicker.clearSelection', 'Clear selection') })), (multiSelect || view === 'stack') && (jsxRuntime.jsx("button", { type: "button", className: "ntd-picker-done", onClick: () => setIsOpen(false), children: t('iconPicker.done', 'Done') }))] }))] }))] }));
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;