@nice2dev/icons 1.2.2 → 1.3.0

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 (74) hide show
  1. package/CHANGELOG.md +499 -0
  2. package/README.md +146 -134
  3. package/dist/cjs/NiceIconPicker.js +17 -19
  4. package/dist/cjs/createIcon.js +10 -2
  5. package/dist/cjs/iconParts.js +49 -0
  6. package/dist/cjs/icons/accessibility.js +12 -4
  7. package/dist/cjs/icons/actions.js +67 -25
  8. package/dist/cjs/icons/business.js +58 -28
  9. package/dist/cjs/icons/ecommerce.js +0 -20
  10. package/dist/cjs/icons/education.js +22 -9
  11. package/dist/cjs/icons/files.js +70 -56
  12. package/dist/cjs/icons/fun.js +39 -25
  13. package/dist/cjs/icons/gaming.js +29 -4
  14. package/dist/cjs/icons/health.js +11 -4
  15. package/dist/cjs/icons/index.js +55 -5
  16. package/dist/cjs/icons/input.js +120 -0
  17. package/dist/cjs/icons/legal.js +4 -9
  18. package/dist/cjs/icons/media.js +26 -11
  19. package/dist/cjs/icons/navigation.js +4 -2
  20. package/dist/cjs/icons/omniverk.js +19 -6
  21. package/dist/cjs/icons/realestate.js +10 -4
  22. package/dist/cjs/icons/saas.js +15 -4
  23. package/dist/cjs/icons/science.js +6 -3
  24. package/dist/cjs/icons/shapes.js +66 -31
  25. package/dist/cjs/icons/sports.js +6 -2
  26. package/dist/cjs/icons/ui.js +62 -20
  27. package/dist/cjs/icons/weather.js +18 -8
  28. package/dist/cjs/index.js +55 -5
  29. package/dist/cjs/nicetodev-icons.css +1 -1
  30. package/dist/cjs/resolver.js +46 -1
  31. package/dist/cjs/rsc.js +1 -1
  32. package/dist/esm/NiceIconPicker.js +19 -21
  33. package/dist/esm/createIcon.js +10 -2
  34. package/dist/esm/iconParts.js +46 -0
  35. package/dist/esm/icons/accessibility.js +12 -4
  36. package/dist/esm/icons/actions.js +67 -26
  37. package/dist/esm/icons/business.js +58 -29
  38. package/dist/esm/icons/ecommerce.js +1 -19
  39. package/dist/esm/icons/education.js +22 -10
  40. package/dist/esm/icons/files.js +70 -56
  41. package/dist/esm/icons/fun.js +37 -25
  42. package/dist/esm/icons/gaming.js +29 -5
  43. package/dist/esm/icons/health.js +11 -4
  44. package/dist/esm/icons/index.js +9 -8
  45. package/dist/esm/icons/input.js +75 -0
  46. package/dist/esm/icons/legal.js +4 -9
  47. package/dist/esm/icons/media.js +26 -11
  48. package/dist/esm/icons/navigation.js +4 -2
  49. package/dist/esm/icons/omniverk.js +19 -6
  50. package/dist/esm/icons/realestate.js +10 -4
  51. package/dist/esm/icons/saas.js +15 -4
  52. package/dist/esm/icons/science.js +6 -3
  53. package/dist/esm/icons/shapes.js +66 -31
  54. package/dist/esm/icons/sports.js +6 -2
  55. package/dist/esm/icons/ui.js +62 -21
  56. package/dist/esm/icons/weather.js +18 -8
  57. package/dist/esm/index.js +9 -8
  58. package/dist/esm/nicetodev-icons.css +1 -1
  59. package/dist/esm/resolver.js +46 -1
  60. package/dist/esm/rsc.js +1 -1
  61. package/dist/types/iconParts.d.ts +19 -0
  62. package/dist/types/icons/actions.d.ts +1 -0
  63. package/dist/types/icons/business.d.ts +2 -0
  64. package/dist/types/icons/ecommerce.d.ts +0 -2
  65. package/dist/types/icons/education.d.ts +1 -0
  66. package/dist/types/icons/files.d.ts +2 -1
  67. package/dist/types/icons/fun.d.ts +6 -1
  68. package/dist/types/icons/gaming.d.ts +2 -0
  69. package/dist/types/icons/index.d.ts +9 -8
  70. package/dist/types/icons/input.d.ts +53 -0
  71. package/dist/types/icons/omniverk.d.ts +0 -2
  72. package/dist/types/icons/ui.d.ts +1 -0
  73. package/dist/types/types.d.ts +6 -0
  74. package/package.json +3 -3
package/README.md CHANGED
@@ -81,12 +81,7 @@ function App() {
81
81
  <NtdSearch size="md" animation="pulse" />
82
82
 
83
83
  {/* Duotone heart with custom colors */}
84
- <NtdHeart
85
- variant="duotone"
86
- primaryColor="#e74c3c"
87
- secondaryColor="#fca5a5"
88
- animation="heartbeat"
89
- />
84
+ <NtdHeart variant="duotone" primaryColor="#e74c3c" secondaryColor="#fca5a5" animation="heartbeat" />
90
85
  </>
91
86
  );
92
87
  }
@@ -100,29 +95,29 @@ function App() {
100
95
 
101
96
  Every icon component accepts these props (extends `SVGProps<SVGSVGElement>`):
102
97
 
103
- | Prop | Type | Default | Description |
104
- |------|------|---------|-------------|
105
- | `size` | `IconSize` | `'md'` | Named preset or pixel number |
106
- | `variant` | `IconVariant` | `'solid'` | Rendering variant |
107
- | `primaryColor` | `string` | `'currentColor'` | Main icon color |
108
- | `secondaryColor` | `string` | `primaryColor` | Secondary color (duotone background) |
109
- | `accentColor` | `string` | `'#ffffff'` | Accent color for detail elements |
110
- | `primaryOpacity` | `number` | `1` | Opacity of primary elements (0–1) |
111
- | `secondaryOpacity` | `number` | `0.4` | Opacity of secondary elements (0–1) |
112
- | `accentOpacity` | `number` | `1` | Opacity of accent elements (0–1) |
113
- | `strokeWidth` | `number` | `2` | Base stroke width for outline/duotone |
114
- | `strokeLinecap` | `'round' \| 'square' \| 'butt'` | `'round'` | Stroke line cap style |
115
- | `strokeLinejoin` | `'round' \| 'miter' \| 'bevel'` | `'round'` | Stroke line join style |
116
- | `animation` | `IconAnimation` | `undefined` | CSS animation name |
117
- | `animationDuration` | `string` | CSS default | Duration override (e.g. `'0.5s'`) |
118
- | `gradient` | `GradientPreset` | `undefined` | Apply a gradient fill preset |
119
- | `gradientType` | `'linear' \| 'radial'` | `'linear'` | Gradient shape |
120
- | `gradientAngle` | `number` | `135` | Angle for linear gradients (degrees) |
121
- | `gradientAnimation` | `'shift' \| 'spin' \| 'pulse'` | `undefined` | Animate the gradient |
122
- | `title` | `string` | `undefined` | Accessible title for screen readers |
123
- | `className` | `string` | `undefined` | Custom CSS class |
124
- | `style` | `CSSProperties` | `undefined` | Inline styles |
125
- | `ref` | `Ref<SVGSVGElement>` | — | Forward ref to the `<svg>` element |
98
+ | Prop | Type | Default | Description |
99
+ | ------------------- | ------------------------------- | ---------------- | ------------------------------------- |
100
+ | `size` | `IconSize` | `'md'` | Named preset or pixel number |
101
+ | `variant` | `IconVariant` | `'solid'` | Rendering variant |
102
+ | `primaryColor` | `string` | `'currentColor'` | Main icon color |
103
+ | `secondaryColor` | `string` | `primaryColor` | Secondary color (duotone background) |
104
+ | `accentColor` | `string` | `'#ffffff'` | Accent color for detail elements |
105
+ | `primaryOpacity` | `number` | `1` | Opacity of primary elements (0–1) |
106
+ | `secondaryOpacity` | `number` | `0.4` | Opacity of secondary elements (0–1) |
107
+ | `accentOpacity` | `number` | `1` | Opacity of accent elements (0–1) |
108
+ | `strokeWidth` | `number` | `2` | Base stroke width for outline/duotone |
109
+ | `strokeLinecap` | `'round' \| 'square' \| 'butt'` | `'round'` | Stroke line cap style |
110
+ | `strokeLinejoin` | `'round' \| 'miter' \| 'bevel'` | `'round'` | Stroke line join style |
111
+ | `animation` | `IconAnimation` | `undefined` | CSS animation name |
112
+ | `animationDuration` | `string` | CSS default | Duration override (e.g. `'0.5s'`) |
113
+ | `gradient` | `GradientPreset` | `undefined` | Apply a gradient fill preset |
114
+ | `gradientType` | `'linear' \| 'radial'` | `'linear'` | Gradient shape |
115
+ | `gradientAngle` | `number` | `135` | Angle for linear gradients (degrees) |
116
+ | `gradientAnimation` | `'shift' \| 'spin' \| 'pulse'` | `undefined` | Animate the gradient |
117
+ | `title` | `string` | `undefined` | Accessible title for screen readers |
118
+ | `className` | `string` | `undefined` | Custom CSS class |
119
+ | `style` | `CSSProperties` | `undefined` | Inline styles |
120
+ | `ref` | `Ref<SVGSVGElement>` | — | Forward ref to the `<svg>` element |
126
121
 
127
122
  Plus all standard `SVGProps<SVGSVGElement>` attributes (`onClick`, `id`, `data-*`, etc.).
128
123
 
@@ -135,7 +130,7 @@ type IconSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | 'xxl' | 'xxxl' | numb
135
130
  ```
136
131
 
137
132
  | Preset | Pixels |
138
- |--------|--------|
133
+ | ------ | ------ |
139
134
  | `xs` | 12 |
140
135
  | `sm` | 16 |
141
136
  | `md` | 24 |
@@ -157,12 +152,12 @@ The pixel map is exported as `SIZE_MAP: Record<string, number>`.
157
152
  type IconVariant = 'solid' | 'outline' | 'duotone' | 'flat';
158
153
  ```
159
154
 
160
- | Variant | Description |
161
- |---------|-------------|
162
- | `solid` | Filled shapes using `fill` |
163
- | `outline` | Stroked paths with `fill="none"` |
164
- | `duotone` | Background shape at `secondaryOpacity` + foreground stroke |
165
- | `flat` | Colored badge background (`secondaryColor`, rounded rect) with solid content on top |
155
+ | Variant | Description |
156
+ | --------- | ----------------------------------------------------------------------------------- |
157
+ | `solid` | Filled shapes using `fill` |
158
+ | `outline` | Stroked paths with `fill="none"` |
159
+ | `duotone` | Background shape at `secondaryOpacity` + foreground stroke |
160
+ | `flat` | Colored badge background (`secondaryColor`, rounded rect) with solid content on top |
166
161
 
167
162
  ```tsx
168
163
  <NtdHome variant="outline" primaryColor="#000" strokeWidth={1.5} />
@@ -175,10 +170,10 @@ type IconVariant = 'solid' | 'outline' | 'duotone' | 'flat';
175
170
  ```tsx
176
171
  <NtdHeart
177
172
  variant="duotone"
178
- primaryColor="#e74c3c" // foreground stroke
179
- secondaryColor="#fca5a5" // background fill
180
- secondaryOpacity={0.3} // background opacity
181
- accentColor="#fff" // small details
173
+ primaryColor="#e74c3c" // foreground stroke
174
+ secondaryColor="#fca5a5" // background fill
175
+ secondaryOpacity={0.3} // background opacity
176
+ accentColor="#fff" // small details
182
177
  />
183
178
  ```
184
179
 
@@ -209,36 +204,36 @@ Apply gradient fills using the `gradient` prop with one of 12 built-in presets:
209
204
  ```tsx
210
205
  <NtdHeart gradient="sunset" />
211
206
  <NtdStar gradient="ocean" gradientType="radial" />
212
- <NtdZap gradient="neon" gradientAnimation="spin" />
207
+ <NtdLightning gradient="neon" gradientAnimation="spin" />
213
208
  <NtdGlobe gradient="aurora" gradientAngle={45} />
214
209
  ```
215
210
 
216
211
  #### Available Presets (`GradientPreset`)
217
212
 
218
- | Preset | Colors |
219
- |--------|--------|
220
- | `sunset` | `#ff6b6b` → `#feca57` → `#ff9ff3` |
221
- | `ocean` | `#0652DD` → `#1B9CFC` → `#38ada9` |
222
- | `forest` | `#0a3d0a` → `#1e8449` → `#58D68D` |
223
- | `fire` | `#e74c3c` → `#f39c12` → `#f1c40f` |
224
- | `ice` | `#74b9ff` → `#a29bfe` → `#dfe6e9` |
225
- | `neon` | `#6c5ce7` → `#fd79a8` → `#00cec9` |
226
- | `gold` | `#f9ca24` → `#f0932b` → `#e17055` |
227
- | `purple-haze` | `#6c5ce7` → `#a55eea` → `#e056a0` |
228
- | `aurora` | `#00b894` → `#00cec9` → `#0984e3` → `#6c5ce7` |
229
- | `midnight` | `#0c0c1d` → `#1e3a5f` → `#2c3e50` |
230
- | `candy` | `#ff6b81` → `#ffa502` → `#2ed573` |
231
- | `steel` | `#636e72` → `#b2bec3` → `#dfe6e9` |
213
+ | Preset | Colors |
214
+ | ------------- | --------------------------------------------------- |
215
+ | `sunset` | `#ff6b6b` → `#feca57` → `#ff9ff3` |
216
+ | `ocean` | `#0652DD` → `#1B9CFC` → `#38ada9` |
217
+ | `forest` | `#0a3d0a` → `#1e8449` → `#58D68D` |
218
+ | `fire` | `#e74c3c` → `#f39c12` → `#f1c40f` |
219
+ | `ice` | `#74b9ff` → `#a29bfe` → `#dfe6e9` |
220
+ | `neon` | `#6c5ce7` → `#fd79a8` → `#00cec9` |
221
+ | `gold` | `#f9ca24` → `#f0932b` → `#e17055` |
222
+ | `purple-haze` | `#6c5ce7` → `#a55eea` → `#e056a0` |
223
+ | `aurora` | `#00b894` → `#00cec9` → `#0984e3` → `#6c5ce7` |
224
+ | `midnight` | `#0c0c1d` → `#1e3a5f` → `#2c3e50` |
225
+ | `candy` | `#ff6b81` → `#ffa502` → `#2ed573` |
226
+ | `steel` | `#636e72` → `#b2bec3` → `#dfe6e9` |
232
227
 
233
228
  Color stops are exported as `GRADIENT_PRESETS: Record<GradientPreset, readonly string[]>`.
234
229
 
235
230
  #### Gradient Animations (`GradientAnimation`)
236
231
 
237
- | Animation | Effect |
238
- |-----------|--------|
239
- | `shift` | Slides gradient colors horizontally |
240
- | `spin` | Rotates the gradient around the icon center |
241
- | `pulse` | Pulsates between stop colors |
232
+ | Animation | Effect |
233
+ | --------- | ------------------------------------------- |
234
+ | `shift` | Slides gradient colors horizontally |
235
+ | `spin` | Rotates the gradient around the icon center |
236
+ | `pulse` | Pulsates between stop colors |
242
237
 
243
238
  Animations use SVG SMIL (`<animate>` / `<animateTransform>`) — no JavaScript runtime, works in all modern browsers.
244
239
 
@@ -265,9 +260,9 @@ import { NiceIconPicker, type IconPickerEntry } from '@nice2dev/icons';
265
260
  import { NtdHome, NtdSearch, NtdHeart } from '@nice2dev/icons';
266
261
 
267
262
  const icons: IconPickerEntry[] = [
268
- { name: 'NtdHome', Component: NtdHome, category: 'UI' },
269
- { name: 'NtdSearch', Component: NtdSearch, category: 'Actions' },
270
- { name: 'NtdHeart', Component: NtdHeart, category: 'UI' },
263
+ { name: 'NtdHome', Component: NtdHome, category: 'UI' },
264
+ { name: 'NtdSearch', Component: NtdSearch, category: 'Actions' },
265
+ { name: 'NtdHeart', Component: NtdHeart, category: 'UI' },
271
266
  ];
272
267
 
273
268
  function App() {
@@ -298,26 +293,26 @@ interface IconPickerEntry {
298
293
 
299
294
  ### NiceIconPickerProps
300
295
 
301
- | Prop | Type | Default | Description |
302
- |------|------|---------|-------------|
303
- | `icons` | `IconPickerEntry[]` | **required** | Full icon registry |
304
- | `value` | `string \| null` | `null` | Currently selected icon name |
305
- | `onChange` | `(name: string \| null, entry: IconPickerEntry \| null) => void` | — | Selection callback |
306
- | `placeholder` | `string` | `'Select icon…'` | Placeholder text |
307
- | `variant` | `IconVariant` | `'solid'` | Variant for grid preview |
308
- | `gridIconSize` | `IconSize \| number` | `'md'` | Icon size in the grid |
309
- | `triggerIconSize` | `IconSize \| number` | `'sm'` | Icon size in trigger button |
310
- | `categories` | `string[]` | auto-detected | Ordered category tab names |
311
- | `className` | `string` | — | Additional CSS class |
312
- | `disabled` | `boolean` | `false` | Disable the picker |
313
- | `clearable` | `boolean` | `true` | Show "Clear selection" button |
296
+ | Prop | Type | Default | Description |
297
+ | ----------------- | ---------------------------------------------------------------- | ------------------ | ----------------------------- |
298
+ | `icons` | `IconPickerEntry[]` | **required** | Full icon registry |
299
+ | `value` | `string \| null` | `null` | Currently selected icon name |
300
+ | `onChange` | `(name: string \| null, entry: IconPickerEntry \| null) => void` | — | Selection callback |
301
+ | `placeholder` | `string` | `'Select icon…'` | Placeholder text |
302
+ | `variant` | `IconVariant` | `'solid'` | Variant for grid preview |
303
+ | `gridIconSize` | `IconSize \| number` | `'md'` | Icon size in the grid |
304
+ | `triggerIconSize` | `IconSize \| number` | `'sm'` | Icon size in trigger button |
305
+ | `categories` | `string[]` | auto-detected | Ordered category tab names |
306
+ | `className` | `string` | — | Additional CSS class |
307
+ | `disabled` | `boolean` | `false` | Disable the picker |
308
+ | `clearable` | `boolean` | `true` | Show "Clear selection" button |
314
309
 
315
310
  ### NiceIconPickerHandle (imperative ref)
316
311
 
317
312
  ```tsx
318
313
  const ref = useRef<NiceIconPickerHandle>(null);
319
- ref.current?.open(); // programmatically open
320
- ref.current?.close(); // programmatically close
314
+ ref.current?.open(); // programmatically open
315
+ ref.current?.close(); // programmatically close
321
316
  ref.current?.toggle(); // toggle open/closed
322
317
  ```
323
318
 
@@ -340,11 +335,9 @@ import { createIcon } from '@nice2dev/icons';
340
335
 
341
336
  export const NtdMyIcon = createIcon({
342
337
  displayName: 'NtdMyIcon',
343
- viewBox: '0 0 24 24', // optional, defaults to '0 0 24 24'
338
+ viewBox: '0 0 24 24', // optional, defaults to '0 0 24 24'
344
339
  paths: {
345
- solid: ({ primaryColor }) => (
346
- <path d="M12 2l10 20H2z" fill={primaryColor} />
347
- ),
340
+ solid: ({ primaryColor }) => <path d="M12 2l10 20H2z" fill={primaryColor} />,
348
341
  outline: ({ primaryColor, strokeWidth }) => (
349
342
  <path d="M12 2l10 20H2z" stroke={primaryColor} strokeWidth={strokeWidth} fill="none" />
350
343
  ),
@@ -364,19 +357,19 @@ export const NtdMyIcon = createIcon({
364
357
  });
365
358
 
366
359
  // Usage — exactly like any built-in icon:
367
- <NtdMyIcon size="lg" variant="duotone" primaryColor="#8b5cf6" animation="pulse" />
360
+ <NtdMyIcon size="lg" variant="duotone" primaryColor="#8b5cf6" animation="pulse" />;
368
361
  ```
369
362
 
370
363
  ### CreateIconOptions
371
364
 
372
- | Field | Type | Default | Description |
373
- |-------|------|---------|-------------|
374
- | `displayName` | `string` | **required** | React display name |
375
- | `viewBox` | `string` | `'0 0 24 24'` | SVG viewBox |
376
- | `paths.solid` | `IconPathRenderer` | **required** | Solid variant renderer |
377
- | `paths.outline` | `IconPathRenderer` | **required** | Outline variant renderer |
378
- | `paths.duotone` | `IconPathRenderer` | **required** | Duotone variant renderer |
379
- | `paths.flat` | `IconPathRenderer` | **required** | Flat (badge) variant renderer |
365
+ | Field | Type | Default | Description |
366
+ | --------------- | ------------------ | ------------- | ----------------------------- |
367
+ | `displayName` | `string` | **required** | React display name |
368
+ | `viewBox` | `string` | `'0 0 24 24'` | SVG viewBox |
369
+ | `paths.solid` | `IconPathRenderer` | **required** | Solid variant renderer |
370
+ | `paths.outline` | `IconPathRenderer` | **required** | Outline variant renderer |
371
+ | `paths.duotone` | `IconPathRenderer` | **required** | Duotone variant renderer |
372
+ | `paths.flat` | `IconPathRenderer` | **required** | Flat (badge) variant renderer |
380
373
 
381
374
  ### IconPathRenderer
382
375
 
@@ -388,69 +381,85 @@ type IconPathRenderer = (props: IconRenderProps) => React.ReactNode;
388
381
 
389
382
  Properties passed to every path renderer (pre-resolved from user props):
390
383
 
391
- | Prop | Type | Description |
392
- |------|------|-------------|
393
- | `variant` | `IconVariant` | Active variant |
394
- | `primaryColor` | `string` | Resolved primary color |
395
- | `secondaryColor` | `string` | Resolved secondary color |
396
- | `accentColor` | `string` | Resolved accent color |
397
- | `primaryOpacity` | `number` | Primary opacity |
398
- | `secondaryOpacity` | `number` | Secondary opacity |
399
- | `accentOpacity` | `number` | Accent opacity |
400
- | `strokeWidth` | `number` | Stroke width |
401
- | `strokeLinecap` | `'round' \| 'square' \| 'butt'` | Stroke line cap |
402
- | `strokeLinejoin` | `'round' \| 'miter' \| 'bevel'` | Stroke line join |
384
+ | Prop | Type | Description |
385
+ | ------------------ | ------------------------------- | ------------------------ |
386
+ | `variant` | `IconVariant` | Active variant |
387
+ | `primaryColor` | `string` | Resolved primary color |
388
+ | `secondaryColor` | `string` | Resolved secondary color |
389
+ | `accentColor` | `string` | Resolved accent color |
390
+ | `primaryOpacity` | `number` | Primary opacity |
391
+ | `secondaryOpacity` | `number` | Secondary opacity |
392
+ | `accentOpacity` | `number` | Accent opacity |
393
+ | `strokeWidth` | `number` | Stroke width |
394
+ | `strokeLinecap` | `'round' \| 'square' \| 'butt'` | Stroke line cap |
395
+ | `strokeLinejoin` | `'round' \| 'miter' \| 'bevel'` | Stroke line join |
403
396
 
404
397
  ---
405
398
 
406
399
  ## Full Icon Catalog
407
400
 
408
401
  ### Navigation (14 icons)
402
+
409
403
  `NtdArrowUp` · `NtdArrowDown` · `NtdArrowLeft` · `NtdArrowRight` · `NtdChevronUp` · `NtdChevronDown` · `NtdChevronLeft` · `NtdChevronRight` · `NtdMenu` · `NtdClose` · `NtdMoreHorizontal` · `NtdMoreVertical` · `NtdExternalLink` · `NtdRefresh`
410
404
 
411
405
  ### Actions (25 icons)
406
+
412
407
  `NtdSearch` · `NtdEdit` · `NtdTrash` · `NtdSave` · `NtdDownload` · `NtdUpload` · `NtdShare` · `NtdCopy` · `NtdClipboard` · `NtdPlus` · `NtdMinus` · `NtdCheck` · `NtdFilter` · `NtdSort` · `NtdPrint` · `NtdZoomIn` · `NtdZoomOut` · `NtdUndo` · `NtdRedo` · `NtdLink` · `NtdSettings` · `NtdLogIn` · `NtdLogOut` · `NtdLock` · `NtdUnlock`
413
408
 
414
409
  ### UI & Status (26 icons)
410
+
415
411
  `NtdHome` · `NtdUser` · `NtdUsers` · `NtdHeart` · `NtdStar` · `NtdBell` · `NtdEye` · `NtdEyeOff` · `NtdInfo` · `NtdWarning` · `NtdError` · `NtdSuccess` · `NtdHelp` · `NtdClock` · `NtdCalendar` · `NtdTag` · `NtdBookmark` · `NtdFlag` · `NtdThumbUp` · `NtdThumbDown` · `NtdShield` · `NtdGlobe` · `NtdMap` · `NtdPin` · `NtdSun` · `NtdMoon`
416
412
 
417
413
  ### Media & Communication (19 icons)
414
+
418
415
  `NtdPlay` · `NtdPause` · `NtdStop` · `NtdSkipForward` · `NtdSkipBack` · `NtdVolume` · `NtdVolumeMute` · `NtdImage` · `NtdCamera` · `NtdVideo` · `NtdMicrophone` · `NtdMusic` · `NtdMail` · `NtdPhone` · `NtdChat` · `NtdSend` · `NtdNotification` · `NtdWifi` · `NtdBluetooth`
419
416
 
420
- ### Files & Misc (31 icons)
421
- `NtdFile` · `NtdFileText` · `NtdFolder` · `NtdFolderOpen` · `NtdArchive` · `NtdDatabase` · `NtdCloud` · `NtdCloudUpload` · `NtdCloudDownload` · `NtdTerminal` · `NtdCode` · `NtdGit` · `NtdPackage` · `NtdLayers` · `NtdGrid` · `NtdList` · `NtdTable` · `NtdSmartphone` · `NtdMonitor` · `NtdCpu` · `NtdZap` · `NtdKey` · `NtdAward` · `NtdCoffee` · `NtdCart` · `NtdCreditCard` · `NtdPieChart` · `NtdBarChart` · `NtdActivity` · `NtdTarget` · `NtdTruck`
417
+ ### Files & Misc (30 icons)
418
+
419
+ `NtdFile` · `NtdFileText` · `NtdFolder` · `NtdFolderOpen` · `NtdArchive` · `NtdDatabase` · `NtdCloud` · `NtdCloudUpload` · `NtdCloudDownload` · `NtdTerminal` · `NtdCode` · `NtdGit` · `NtdPackage` · `NtdLayers` · `NtdGrid` · `NtdList` · `NtdTable` · `NtdSmartphone` · `NtdMonitor` · `NtdCpu` · `NtdKey` · `NtdAward` · `NtdCoffee` · `NtdCart` · `NtdCreditCard` · `NtdPieChart` · `NtdBarChart` · `NtdActivity` · `NtdTarget` · `NtdTruck`
422
420
 
423
421
  ### Fun & Emoji (13 icons)
424
- `NtdDumpsterFire` · `NtdRocket` · `NtdGamepad` · `NtdPuzzle` · `NtdPartyPopper` · `NtdTrophy` · `NtdCrown` · `NtdDice` · `NtdSmiley` · `NtdSad` · `NtdSkull` · `NtdGhost` · `NtdLightning`
422
+
423
+ `NtdDumpsterFire` · `NtdRocket` · `NtdGamepadOldschool` · `NtdPuzzle` · `NtdPartyPopper` · `NtdTrophy` · `NtdCrown` · `NtdDice` · `NtdSmile` · `NtdSad` · `NtdSkull` · `NtdGhost` · `NtdLightning`
425
424
 
426
425
  ### Weather & Nature (20 icons)
426
+
427
427
  `NtdSunrise` · `NtdSunset` · `NtdRain` · `NtdSnow` · `NtdThunderstorm` · `NtdWind` · `NtdDroplet` · `NtdThermometer` · `NtdUmbrella` · `NtdLeaf` · `NtdFlower` · `NtdTree` · `NtdMountain` · `NtdWave` · `NtdFire` · `NtdSnowflake` · `NtdCompass` · `NtdAnchor` · `NtdFeather` · `NtdBug`
428
428
 
429
429
  ### Arrows & Shapes (22 icons)
430
+
430
431
  `NtdArrowUpCircle` · `NtdArrowDownCircle` · `NtdArrowLeftCircle` · `NtdArrowRightCircle` · `NtdSquare` · `NtdCircle` · `NtdTriangle` · `NtdHexagon` · `NtdOctagon` · `NtdDiamond` · `NtdPentagon` · `NtdCross` · `NtdStar4` · `NtdCornerUpLeft` · `NtdCornerUpRight` · `NtdCornerDownLeft` · `NtdCornerDownRight` · `NtdMaximize` · `NtdMinimize` · `NtdMove` · `NtdRepeat` · `NtdShuffle`
431
432
 
432
433
  ### Business & Finance (16 icons)
434
+
433
435
  `NtdBriefcase` · `NtdDollar` · `NtdWallet` · `NtdReceipt` · `NtdBank` · `NtdTrendUp` · `NtdTrendDown` · `NtdPercent` · `NtdInvoice` · `NtdStore` · `NtdBarcode` · `NtdCalculator` · `NtdPresentation` · `NtdClipboardCheck` · `NtdPiggyBank` · `NtdCoins`
434
436
 
435
437
  ### Social & Communication (8 icons)
438
+
436
439
  `NtdAtSign` · `NtdPhoneOff` · `NtdBubble` · `NtdThumbsUp` · `NtdThumbsDown` · `NtdBellRing` · `NtdMegaphone` · `NtdQrCode`
437
440
 
438
441
  ### Dev & Tech (9 icons)
442
+
439
443
  `NtdBrackets` · `NtdServer` · `NtdGitBranch` · `NtdGitCommit` · `NtdGitMerge` · `NtdBattery` · `NtdSlash` · `NtdHash` · `NtdBugDev`
440
444
 
441
445
  ### Food & Health (13 icons)
446
+
442
447
  `NtdApple` · `NtdPizza` · `NtdGlass` · `NtdHeartPulse` · `NtdPill` · `NtdSyringe` · `NtdStethoscope` · `NtdWeight` · `NtdBandaid` · `NtdCake` · `NtdEgg` · `NtdHamburger` · `NtdDna`
443
448
 
444
449
  ### Brand & Logo (14 icons)
450
+
445
451
  `NtdNiceToDev` · `NtdNiceMonogram` · `NtdNiceOrbit` · `NtdNiceSpark` · `NtdNiceShield` · `NtdNiceBadge` · `NtdNiceCore` · `NtdNiceCrest` · `NtdNiceHelix` · `NtdNicePortal` · `NtdNicePulseMark` · `NtdNiceFlagship` · `NtdOmniVerk` · `NtdOmniVerkLogo`
446
452
 
447
453
  ### Brand Studio (25 icons)
454
+
448
455
  `NtdStudioGrid` · `NtdStudioBlueprint` · `NtdStudioCursor` · `NtdStudioPrototype` · `NtdStudioComponent` · `NtdStudioToken` · `NtdStudioPalette` · `NtdStudioBrush` · `NtdStudioPenTool` · `NtdStudioLayerStack` · `NtdStudioSpline` · `NtdStudioNodes` · `NtdStudioFlow` · `NtdStudioRelease` · `NtdStudioRocketMark` · `NtdStudioAIBrain` · `NtdStudioNeural` · `NtdStudioChipmark` · `NtdStudioTerminalMark` · `NtdStudioForge` · `NtdStudioWireframe` · `NtdStudioPersona` · `NtdStudioBenchmark` · `NtdStudioStudio` · `NtdStudioBeacon`
449
456
 
450
457
  ### Brand Lockups (4 icons)
458
+
451
459
  `NtdNiceLockupHorizontal` · `NtdNiceLockupStacked` · `NtdNiceWordmark` · `NtdNiceSymbolWordmark`
452
460
 
453
461
  ### Brand Enterprise (18 icons)
462
+
454
463
  `NtdEnterpriseRoadmap` · `NtdEnterpriseBacklog` · `NtdEnterpriseKanban` · `NtdEnterpriseSprint` · `NtdEnterpriseMilestone` · `NtdEnterprisePipeline` · `NtdEnterpriseDeploy` · `NtdEnterpriseMonitor` · `NtdEnterpriseAlert` · `NtdEnterpriseSla` · `NtdEnterpriseKpi` · `NtdEnterpriseRevenue` · `NtdEnterpriseRisk` · `NtdEnterpriseCompliance` · `NtdEnterpriseGateway` · `NtdEnterpriseDataMesh` · `NtdEnterpriseFeatureFlag` · `NtdEnterpriseWorkspace`
455
464
 
456
465
  ---
@@ -458,27 +467,35 @@ Properties passed to every path renderer (pre-resolved from user props):
458
467
  ## Full Animation Catalog
459
468
 
460
469
  ### Core Transforms (13)
470
+
461
471
  `spin` · `spin-reverse` · `pulse` · `bounce` · `shake` · `float` · `flip` · `glow` · `wiggle` · `rubber` · `swing` · `tada` · `heartbeat`
462
472
 
463
473
  ### Entrance Animations (22)
474
+
464
475
  `fade-in` · `slide-up` · `slide-down` · `slide-left` · `slide-right` · `drop` · `rise` · `roll-in` · `materialize` · `vortex` · `meteor` · `scale-in` · `signature` · `draw-sequence` · `stroke-order` · `glow-trace` · `seal-stamp` · `reveal` · `typewriter` · `unfold` · `assemble` · `draw` · `draw-from-dot`
465
476
 
466
477
  ### Exit Animations (3)
478
+
467
479
  `scale-out` · `fold` · `dissolve`
468
480
 
469
481
  ### Organic / Natural (10)
482
+
470
483
  `breathe` · `drift` · `orbit` · `sway` · `tilt` · `rock` · `pendulum` · `levitate` · `flap` · `skate`
471
484
 
472
485
  ### Energetic / Impact (10)
486
+
473
487
  `jello` · `pop` · `squeeze` · `crush` · `stretch` · `stomp` · `snap` · `twist` · `dash` · `surge`
474
488
 
475
489
  ### Effects / Digital (8)
490
+
476
491
  `flash` · `blink` · `flicker` · `vibrate` · `tremble` · `glitch` · `neon` · `earthquake`
477
492
 
478
493
  ### Complex Transforms (7)
494
+
479
495
  `wobble` · `spiral` · `warp` · `morph` · `zoom-pulse` · `ripple` · `radar`
480
496
 
481
497
  ### Dramatic (6)
498
+
482
499
  `ping` · `implode` · `explode` · `scatter` · `rotate-x` · `draw-loop`
483
500
 
484
501
  ---
@@ -539,6 +556,7 @@ npm run build-storybook # static build to storybook-static/
539
556
  ```
540
557
 
541
558
  Stories cover:
559
+
542
560
  - **Components/Icon** — all props, variants, sizes, colors, animations
543
561
  - **Components/NiceIconPicker** — controlled picker, disabled, outline variant
544
562
  - **Components/Animations** — full gallery of 65+ animations
@@ -569,11 +587,13 @@ dist/
569
587
  ```
570
588
 
571
589
  Entry points in `package.json`:
590
+
572
591
  - `main` → `dist/cjs/index.js`
573
592
  - `module` → `dist/esm/index.js`
574
593
  - `types` → `dist/types/index.d.ts`
575
594
 
576
595
  ---
596
+
577
597
  ## React Server Components (RSC)
578
598
 
579
599
  Use icons in Server Components without "use client":
@@ -585,7 +605,7 @@ import { createIconRSC } from '@nice2dev/icons/rsc';
585
605
  // Create RSC-compatible icon
586
606
  const HomeIcon = createIconRSC({
587
607
  displayName: 'HomeIcon',
588
- paths: { /* ... */ }
608
+ paths: {/* ... */},
589
609
  });
590
610
 
591
611
  export default function Page() {
@@ -595,11 +615,11 @@ export default function Page() {
595
615
 
596
616
  ### RSC vs Client Exports
597
617
 
598
- | Import Path | Use Case |
599
- |-------------|----------|
600
- | `@nice2dev/icons` | Standard usage (includes animations, color scheme detection) |
601
- | `@nice2dev/icons/rsc` | Server Components (no hooks, streaming-friendly) |
602
- | `@nice2dev/icons/client` | Client-only features ("use client" directive) |
618
+ | Import Path | Use Case |
619
+ | ------------------------ | ------------------------------------------------------------ |
620
+ | `@nice2dev/icons` | Standard usage (includes animations, color scheme detection) |
621
+ | `@nice2dev/icons/rsc` | Server Components (no hooks, streaming-friendly) |
622
+ | `@nice2dev/icons/client` | Client-only features ("use client" directive) |
603
623
 
604
624
  ### RSC Utilities
605
625
 
@@ -662,11 +682,11 @@ const figmaTokens = toFigmaTokens(tokens);
662
682
 
663
683
  ### Default Token Values
664
684
 
665
- | Category | Tokens |
666
- |----------|--------|
667
- | Sizes | `xs` (12px), `sm` (16px), `md` (24px), `lg` (32px), `xl` (48px), `2xl` (64px) |
668
- | Colors | `primary`, `secondary`, `accent`, `success`, `warning`, `error`, `info`, `muted` |
669
- | Strokes | `thin` (1), `normal` (1.5), `medium` (2), `thick` (2.5), `bold` (3) |
685
+ | Category | Tokens |
686
+ | -------- | -------------------------------------------------------------------------------- |
687
+ | Sizes | `xs` (12px), `sm` (16px), `md` (24px), `lg` (32px), `xl` (48px), `2xl` (64px) |
688
+ | Colors | `primary`, `secondary`, `accent`, `success`, `warning`, `error`, `info`, `muted` |
689
+ | Strokes | `thin` (1), `normal` (1.5), `medium` (2), `thick` (2.5), `bold` (3) |
670
690
 
671
691
  ---
672
692
 
@@ -676,12 +696,7 @@ Seamless integration with Radix UI, Headless UI, and shadcn/ui:
676
696
 
677
697
  ```tsx
678
698
  import { NtdSettings, NtdChevronDown } from '@nice2dev/icons';
679
- import {
680
- forwardRefIcon,
681
- createRadixIcon,
682
- withRadixState,
683
- shadcnIconClasses,
684
- } from '@nice2dev/icons/headless-ui';
699
+ import { forwardRefIcon, createRadixIcon, withRadixState, shadcnIconClasses } from '@nice2dev/icons/headless-ui';
685
700
 
686
701
  // Create ref-compatible icon for Radix asChild pattern
687
702
  const SettingsIcon = forwardRefIcon(NtdSettings);
@@ -698,7 +713,7 @@ const ChevronIcon = withRadixState(NtdChevronDown);
698
713
  <Button>
699
714
  <SettingsIcon className={shadcnIconClasses.buttonLeft} />
700
715
  Settings
701
- </Button>
716
+ </Button>;
702
717
  ```
703
718
 
704
719
  ### shadcn/ui Class Presets
@@ -715,7 +730,7 @@ shadcnIconClasses = {
715
730
  dropdown: 'mr-2 h-4 w-4',
716
731
  spinner: 'h-4 w-4 animate-spin',
717
732
  // ...
718
- }
733
+ };
719
734
  ```
720
735
 
721
736
  ### Dynamic Class Generation
@@ -747,29 +762,25 @@ import {
747
762
  createAccessibleIconProps,
748
763
  isIconComponent,
749
764
  isValidIconName,
750
- useBatchIcons
765
+ useBatchIcons,
751
766
  } from '@nice2dev/icons/utilities';
752
767
 
753
768
  // Dynamic icon loading with suspense support
754
769
  function DynamicIcon({ name }: { name: string }) {
755
770
  const { icon: Icon, loading, error } = useIcon(name);
756
-
771
+
757
772
  if (loading) return <span>Loading...</span>;
758
773
  if (error) return <span>Icon not found</span>;
759
774
  if (!Icon) return null;
760
-
775
+
761
776
  return <Icon className="w-6 h-6" />;
762
777
  }
763
778
 
764
779
  // Respect user's reduced motion preference
765
780
  function AnimatedIcon() {
766
781
  const prefersReducedMotion = useReducedMotion();
767
-
768
- return (
769
- <NtdSpinner
770
- className={prefersReducedMotion ? '' : 'animate-spin'}
771
- />
772
- );
782
+
783
+ return <NtdSpinner className={prefersReducedMotion ? '' : 'animate-spin'} />;
773
784
  }
774
785
 
775
786
  // Preload icons for better performance
@@ -821,6 +832,7 @@ npx @nice2dev/icons info NtdHeart
821
832
  ```
822
833
 
823
834
  ---
835
+
824
836
  ## License
825
837
 
826
838
  MIT © NiceToDev
@@ -74,6 +74,8 @@ function saveToStorage(key, value) {
74
74
  */
75
75
  const NiceIconPicker = /* @__PURE__ */ React.forwardRef(function NiceIconPicker({ icons, value = null, values = [], onChange, onChangeMulti, multiSelect = false, maxSelections, placeholder, variant = 'solid', gridIconSize = 'md', triggerIconSize = 'sm', categories: categoriesProp, className, disabled = false, clearable = true, virtualized = false, virtualizedItemCount = 100, showHistory = false, showFavorites = false, storageKeyPrefix = 'ntd-icon-picker', enableDragAndDrop = false, onDragStart, onDragEnd, }, ref) {
76
76
  var _a;
77
+ const tablistRef = React.useRef(null);
78
+ uiCore.useRovingFocus(tablistRef, { orientation: 'horizontal', wrap: true, activateOnMove: true });
77
79
  const { t } = uiCore.useNiceTranslation();
78
80
  const [isOpen, setIsOpen] = React.useState(false);
79
81
  const [search, setSearch] = React.useState('');
@@ -225,30 +227,26 @@ const NiceIconPicker = /* @__PURE__ */ React.forwardRef(function NiceIconPicker(
225
227
  document.addEventListener('mousedown', onClickOutside);
226
228
  return () => document.removeEventListener('mousedown', onClickOutside);
227
229
  }, [isOpen]);
228
- /* ─── Focus search on open ─── */
230
+ /* ─── Reset on close ─── */
229
231
  React.useEffect(() => {
230
- if (isOpen) {
231
- requestAnimationFrame(() => { var _a; return (_a = searchInputRef.current) === null || _a === void 0 ? void 0 : _a.focus(); });
232
- }
233
- else {
232
+ if (!isOpen) {
234
233
  setSearch('');
235
234
  setActiveCategory(null);
236
235
  setVisibleRange({ start: 0, end: virtualizedItemCount });
237
236
  }
238
237
  }, [isOpen, virtualizedItemCount]);
239
- /* ─── Keyboard ─── */
240
- React.useEffect(() => {
241
- if (!isOpen) {
242
- return;
243
- }
244
- function onKeyDown(e) {
245
- if (e.key === 'Escape') {
246
- setIsOpen(false);
247
- }
248
- }
249
- document.addEventListener('keydown', onKeyDown);
250
- return () => document.removeEventListener('keydown', onKeyDown);
251
- }, [isOpen]);
238
+ /* ─── Keyboard and pad (1.3.0) ─── */
239
+ // The panel is a dialog on the overlay stack: focus on the search field when it opens, Escape
240
+ // / B close only it (a document listener before) and focus returns to the trigger. The icon
241
+ // grid is an APG listbox moving in two dimensions — the cells were reachable by Tab only.
242
+ const dropdownRef = React.useRef(null);
243
+ uiCore.useOverlayFocus(dropdownRef, {
244
+ open: isOpen,
245
+ modal: false,
246
+ initialFocus: searchInputRef,
247
+ onClose: () => setIsOpen(false),
248
+ });
249
+ uiCore.useRovingFocus(gridWrapRef, { orientation: 'grid', itemSelector: '.ntd-picker-cell' });
252
250
  /* ─── Virtualization scroll handler ─── */
253
251
  React.useEffect(() => {
254
252
  if (!virtualized || !isOpen || !gridWrapRef.current) {
@@ -396,7 +394,7 @@ const NiceIconPicker = /* @__PURE__ */ React.forwardRef(function NiceIconPicker(
396
394
  ? readableName(selectedEntry.name)
397
395
  : (placeholder !== null && placeholder !== void 0 ? placeholder : t('iconPicker.selectIcon', 'Select icon…'));
398
396
  }, [multiSelect, selectedEntries, selectedEntry, placeholder, t]);
399
- return (jsxRuntime.jsxs("div", { ref: rootRef, className: `ntd-picker${isOpen ? ' ntd-picker--open' : ''}${disabled ? ' ntd-picker--disabled' : ''}${multiSelect ? ' ntd-picker--multi' : ''}${className ? ` ${className}` : ''}`, children: [jsxRuntime.jsxs("button", { type: "button", className: "ntd-picker-trigger", onClick: handleToggle, disabled: disabled, "aria-haspopup": "listbox", "aria-expanded": isOpen, children: [jsxRuntime.jsx("span", { className: "ntd-picker-trigger-icon", children: selectedEntry ? (jsxRuntime.jsx(selectedEntry.Component, { size: triggerIconSize, variant: variant })) : multiSelect && selectedEntries.length > 1 ? (jsxRuntime.jsx("span", { className: "ntd-picker-multi-badge", children: selectedEntries.length })) : (jsxRuntime.jsx(files.NtdGrid, { size: 16, variant: "outline" })) }), jsxRuntime.jsx("span", { className: `ntd-picker-trigger-label${!selectedEntry && selectedEntries.length === 0 ? ' ntd-picker-trigger-placeholder' : ''}`, children: triggerLabel }), jsxRuntime.jsx("span", { className: "ntd-picker-trigger-chevron", children: jsxRuntime.jsx(Chevron, {}) })] }), isOpen && (jsxRuntime.jsxs("div", { className: "ntd-picker-dropdown", role: "listbox", 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", 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, 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__'
397
+ return (jsxRuntime.jsxs("div", { ref: rootRef, className: `ntd-picker${isOpen ? ' ntd-picker--open' : ''}${disabled ? ' ntd-picker--disabled' : ''}${multiSelect ? ' ntd-picker--multi' : ''}${className ? ` ${className}` : ''}`, children: [jsxRuntime.jsxs("button", { type: "button", className: "ntd-picker-trigger", onClick: handleToggle, disabled: disabled, "aria-haspopup": "listbox", "aria-expanded": isOpen, children: [jsxRuntime.jsx("span", { className: "ntd-picker-trigger-icon", children: selectedEntry ? (jsxRuntime.jsx(selectedEntry.Component, { size: triggerIconSize, variant: variant })) : multiSelect && selectedEntries.length > 1 ? (jsxRuntime.jsx("span", { className: "ntd-picker-multi-badge", children: selectedEntries.length })) : (jsxRuntime.jsx(files.NtdGrid, { size: 16, variant: "outline" })) }), jsxRuntime.jsx("span", { className: `ntd-picker-trigger-label${!selectedEntry && selectedEntries.length === 0 ? ' ntd-picker-trigger-placeholder' : ''}`, children: triggerLabel }), jsxRuntime.jsx("span", { className: "ntd-picker-trigger-chevron", children: jsxRuntime.jsx(Chevron, {}) })] }), isOpen && (jsxRuntime.jsxs("div", { ref: dropdownRef, className: "ntd-picker-dropdown", role: "dialog", "aria-label": t('iconPicker.selectIcon', 'Select icon…'), 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__'
400
398
  ? t('iconPicker.noFavoritesYetClickThe', 'No favorites yet. Click the star icon to add.')
401
399
  : activeCategory === '__history__'
402
400
  ? t('iconPicker.noRecentIcons', 'No recent icons.')