@nice2dev/icons 1.1.5 → 1.1.7

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 (125) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/dist/cjs/NiceDesktopIconExporter.js +0 -1
  3. package/dist/cjs/NiceFaviconGenerator.js +0 -1
  4. package/dist/cjs/NiceIconEditor.js +0 -1
  5. package/dist/cjs/NiceIconPicker.js +0 -1
  6. package/dist/cjs/NiceIconPreview.js +0 -1
  7. package/dist/cjs/NiceIconSetCreator.js +0 -1
  8. package/dist/cjs/NtdIconBadge.js +0 -1
  9. package/dist/cjs/NtdIconMorph.js +0 -1
  10. package/dist/cjs/NtdStateMorph.js +0 -1
  11. package/dist/cjs/advancedIconSearch.js +0 -1
  12. package/dist/cjs/animationControls.js +0 -1
  13. package/dist/cjs/client.js +33 -0
  14. package/dist/cjs/createIcon.js +0 -1
  15. package/dist/cjs/design-tokens.js +459 -0
  16. package/dist/cjs/figmaExport.js +0 -1
  17. package/dist/cjs/figmaSync.js +0 -1
  18. package/dist/cjs/headless-ui.js +430 -0
  19. package/dist/cjs/iconAnalytics.js +0 -1
  20. package/dist/cjs/iconMetadata.js +0 -1
  21. package/dist/cjs/iconStyles.js +0 -1
  22. package/dist/cjs/icons/accessibility.js +0 -1
  23. package/dist/cjs/icons/actions.js +0 -1
  24. package/dist/cjs/icons/ai.js +0 -1
  25. package/dist/cjs/icons/brand.js +0 -1
  26. package/dist/cjs/icons/business.js +0 -1
  27. package/dist/cjs/icons/devtech.js +0 -1
  28. package/dist/cjs/icons/ecommerce.js +0 -1
  29. package/dist/cjs/icons/education.js +0 -1
  30. package/dist/cjs/icons/files.js +0 -1
  31. package/dist/cjs/icons/fintech.js +0 -1
  32. package/dist/cjs/icons/food.js +0 -1
  33. package/dist/cjs/icons/fun.js +0 -1
  34. package/dist/cjs/icons/gaming.js +0 -1
  35. package/dist/cjs/icons/health.js +0 -1
  36. package/dist/cjs/icons/index.js +0 -1
  37. package/dist/cjs/icons/legal.js +0 -1
  38. package/dist/cjs/icons/media.js +0 -1
  39. package/dist/cjs/icons/navigation.js +0 -1
  40. package/dist/cjs/icons/omniverk.js +0 -1
  41. package/dist/cjs/icons/realestate.js +0 -1
  42. package/dist/cjs/icons/saas.js +0 -1
  43. package/dist/cjs/icons/science.js +0 -1
  44. package/dist/cjs/icons/shapes.js +0 -1
  45. package/dist/cjs/icons/smarthome.js +0 -1
  46. package/dist/cjs/icons/social.js +0 -1
  47. package/dist/cjs/icons/sports.js +0 -1
  48. package/dist/cjs/icons/sustainability.js +0 -1
  49. package/dist/cjs/icons/travel.js +0 -1
  50. package/dist/cjs/icons/ui.js +0 -1
  51. package/dist/cjs/icons/weather.js +0 -1
  52. package/dist/cjs/index.js +0 -1
  53. package/dist/cjs/lottieIntegration.js +0 -1
  54. package/dist/cjs/particleEffects.js +0 -1
  55. package/dist/cjs/resolver.js +0 -1
  56. package/dist/cjs/rsc.js +209 -0
  57. package/dist/cjs/sharpen.js +0 -1
  58. package/dist/cjs/smilAnimations.js +0 -1
  59. package/dist/cjs/tailwind-plugin.js +0 -1
  60. package/dist/cjs/types.js +0 -1
  61. package/dist/cjs/utilities.js +401 -0
  62. package/dist/cjs/variantProps.js +0 -1
  63. package/dist/esm/NiceDesktopIconExporter.js +0 -1
  64. package/dist/esm/NiceFaviconGenerator.js +0 -1
  65. package/dist/esm/NiceIconEditor.js +0 -1
  66. package/dist/esm/NiceIconPicker.js +0 -1
  67. package/dist/esm/NiceIconPreview.js +0 -1
  68. package/dist/esm/NiceIconSetCreator.js +0 -1
  69. package/dist/esm/NtdIconBadge.js +0 -1
  70. package/dist/esm/NtdIconMorph.js +0 -1
  71. package/dist/esm/NtdStateMorph.js +0 -1
  72. package/dist/esm/advancedIconSearch.js +0 -1
  73. package/dist/esm/animationControls.js +0 -1
  74. package/dist/esm/client.js +5 -0
  75. package/dist/esm/createIcon.js +0 -1
  76. package/dist/esm/design-tokens.js +446 -0
  77. package/dist/esm/figmaExport.js +0 -1
  78. package/dist/esm/figmaSync.js +0 -1
  79. package/dist/esm/headless-ui.js +413 -0
  80. package/dist/esm/iconAnalytics.js +0 -1
  81. package/dist/esm/iconMetadata.js +0 -1
  82. package/dist/esm/iconStyles.js +0 -1
  83. package/dist/esm/icons/accessibility.js +0 -1
  84. package/dist/esm/icons/actions.js +0 -1
  85. package/dist/esm/icons/ai.js +0 -1
  86. package/dist/esm/icons/brand.js +0 -1
  87. package/dist/esm/icons/business.js +0 -1
  88. package/dist/esm/icons/devtech.js +0 -1
  89. package/dist/esm/icons/ecommerce.js +0 -1
  90. package/dist/esm/icons/education.js +0 -1
  91. package/dist/esm/icons/files.js +0 -1
  92. package/dist/esm/icons/fintech.js +0 -1
  93. package/dist/esm/icons/food.js +0 -1
  94. package/dist/esm/icons/fun.js +0 -1
  95. package/dist/esm/icons/gaming.js +0 -1
  96. package/dist/esm/icons/health.js +0 -1
  97. package/dist/esm/icons/index.js +0 -1
  98. package/dist/esm/icons/legal.js +0 -1
  99. package/dist/esm/icons/media.js +0 -1
  100. package/dist/esm/icons/navigation.js +0 -1
  101. package/dist/esm/icons/omniverk.js +0 -1
  102. package/dist/esm/icons/realestate.js +0 -1
  103. package/dist/esm/icons/saas.js +0 -1
  104. package/dist/esm/icons/science.js +0 -1
  105. package/dist/esm/icons/shapes.js +0 -1
  106. package/dist/esm/icons/smarthome.js +0 -1
  107. package/dist/esm/icons/social.js +0 -1
  108. package/dist/esm/icons/sports.js +0 -1
  109. package/dist/esm/icons/sustainability.js +0 -1
  110. package/dist/esm/icons/travel.js +0 -1
  111. package/dist/esm/icons/ui.js +0 -1
  112. package/dist/esm/icons/weather.js +0 -1
  113. package/dist/esm/index.js +0 -1
  114. package/dist/esm/lottieIntegration.js +0 -1
  115. package/dist/esm/particleEffects.js +0 -1
  116. package/dist/esm/resolver.js +0 -1
  117. package/dist/esm/rsc.js +201 -0
  118. package/dist/esm/sharpen.js +0 -1
  119. package/dist/esm/smilAnimations.js +0 -1
  120. package/dist/esm/tailwind-plugin.js +0 -1
  121. package/dist/esm/types.js +0 -1
  122. package/dist/esm/utilities.js +384 -0
  123. package/dist/esm/variantProps.js +0 -1
  124. package/dist/types/headless-ui.d.ts +4 -4
  125. package/package.json +2 -2
@@ -0,0 +1,201 @@
1
+ import { jsx, jsxs } from 'react/jsx-runtime';
2
+ import { sharpenTree, roundTree, wireTree, MINI_SCALE } from './sharpen.js';
3
+ import { SIZE_MAP, SEMANTIC_COLOR_DEFAULTS, GRADIENT_PRESETS } from './types.js';
4
+
5
+ /* ────────────────────────────────────────────────────────────────────────────
6
+ RSC-COMPATIBLE ICON FACTORY
7
+ ───────────────────────────────────────────────────────────────────────────── */
8
+ /**
9
+ * Creates a React Server Component compatible icon.
10
+ *
11
+ * Unlike the standard `createIcon`, this version:
12
+ * - Does NOT use React hooks (useId, useSyncExternalStore, etc.)
13
+ * - Does NOT support animations
14
+ * - Does NOT support automatic dark/light mode detection
15
+ * - IS fully streamable and SSR-safe
16
+ *
17
+ * @param options - Icon configuration
18
+ * @returns A React function component (not using forwardRef)
19
+ *
20
+ * @example
21
+ * ```tsx
22
+ * export const NtdHomeRSC = createIconRSC({
23
+ * displayName: 'NtdHomeRSC',
24
+ * paths: {
25
+ * solid: ({ primaryColor }) => <path d="..." fill={primaryColor} />,
26
+ * outline: ({ primaryColor, strokeWidth }) => <path d="..." stroke={primaryColor} />,
27
+ * duotone: ({ primaryColor, secondaryColor }) => <path d="..." />,
28
+ * },
29
+ * });
30
+ * ```
31
+ */
32
+ function createIconRSC(options) {
33
+ const { displayName, viewBox = '0 0 24 24', paths } = options;
34
+ function IconRSC(props) {
35
+ var _a;
36
+ const { size = 'md', variant = 'solid', cutoutShape = 'rounded-square', primaryColor: primaryColorProp, secondaryColor: secondaryColorProp, accentColor: accentColorProp, secondaryOpacity = 0.35, strokeWidth = 1.5, gradient, gradientDirection = 135, color, rotate, flipX, flipY, scale, className, style, 'aria-label': ariaLabel, 'aria-hidden': ariaHidden, title, role = ariaLabel ? 'img' : 'presentation', 'data-testid': testId, } = props;
37
+ // Resolve size
38
+ const resolvedSize = typeof size === 'number' ? size : SIZE_MAP[size] || SIZE_MAP.md;
39
+ // Resolve colors with semantic color support
40
+ let primaryColor = primaryColorProp !== null && primaryColorProp !== void 0 ? primaryColorProp : 'currentColor';
41
+ const secondaryColor = secondaryColorProp !== null && secondaryColorProp !== void 0 ? secondaryColorProp : 'currentColor';
42
+ const accentColor = accentColorProp !== null && accentColorProp !== void 0 ? accentColorProp : primaryColor;
43
+ if (color && SEMANTIC_COLOR_DEFAULTS[color]) {
44
+ if (!primaryColorProp) {
45
+ primaryColor = SEMANTIC_COLOR_DEFAULTS[color];
46
+ }
47
+ }
48
+ // Resolve variant with fallback. `wireframe` always starts from `solid`
49
+ // (the full construction) — it is re-drawn mechanically, not authored.
50
+ const isCutout = variant === 'cutout' || variant === 'flat';
51
+ const pathRenderer = isCutout || variant === 'wireframe' ? paths.solid : ((_a = paths[variant]) !== null && _a !== void 0 ? _a : paths.solid);
52
+ // Build render props
53
+ const renderProps = {
54
+ primaryColor,
55
+ secondaryColor,
56
+ accentColor,
57
+ secondaryOpacity,
58
+ strokeWidth,
59
+ };
60
+ // Build transforms
61
+ const transforms = [];
62
+ if (rotate) {
63
+ transforms.push(`rotate(${rotate}deg)`);
64
+ }
65
+ if (flipX) {
66
+ transforms.push('scaleX(-1)');
67
+ }
68
+ if (flipY) {
69
+ transforms.push('scaleY(-1)');
70
+ }
71
+ if (scale && scale !== 1) {
72
+ transforms.push(`scale(${scale})`);
73
+ }
74
+ // Build gradient defs if needed
75
+ let gradientDefs = null;
76
+ let gradientId = null;
77
+ if (gradient && GRADIENT_PRESETS[gradient]) {
78
+ const colors = GRADIENT_PRESETS[gradient];
79
+ // Generate a deterministic ID based on displayName and gradient
80
+ gradientId = `${displayName}-${gradient}-grad`;
81
+ const x1 = Math.round(50 - 50 * Math.cos((gradientDirection * Math.PI) / 180));
82
+ const y1 = Math.round(50 - 50 * Math.sin((gradientDirection * Math.PI) / 180));
83
+ const x2 = Math.round(50 + 50 * Math.cos((gradientDirection * Math.PI) / 180));
84
+ const y2 = Math.round(50 + 50 * Math.sin((gradientDirection * Math.PI) / 180));
85
+ gradientDefs = (jsx("defs", { children: jsx("linearGradient", { id: gradientId, x1: `${x1}%`, y1: `${y1}%`, x2: `${x2}%`, y2: `${y2}%`, children: colors.map((color, i) => (jsx("stop", { offset: `${(i / (colors.length - 1)) * 100}%`, stopColor: color }, i))) }) }));
86
+ // Override primary color with gradient reference
87
+ renderProps.primaryColor = `url(#${gradientId})`;
88
+ }
89
+ // Final styles
90
+ const finalStyle = {
91
+ width: resolvedSize,
92
+ height: resolvedSize,
93
+ ...style,
94
+ ...(transforms.length > 0 && { transform: transforms.join(' ') }),
95
+ };
96
+ // Cutout mask ID (deterministic for SSR)
97
+ const cutoutMaskId = isCutout ? `${displayName}-cutout-mask` : null;
98
+ // Cutout shape renderer
99
+ const cutoutBg = isCutout
100
+ ? (() => {
101
+ const fill = renderProps.primaryColor !== 'currentColor'
102
+ ? renderProps.secondaryColor
103
+ : renderProps.primaryColor;
104
+ const mask = `url(#${cutoutMaskId})`;
105
+ switch (cutoutShape) {
106
+ case 'circle':
107
+ return jsx("circle", { cx: "12", cy: "12", r: "11", fill: fill, mask: mask });
108
+ case 'square':
109
+ return jsx("rect", { x: "1", y: "1", width: "22", height: "22", fill: fill, mask: mask });
110
+ case 'hexagon':
111
+ return (jsx("polygon", { points: "12,1 22.39,6.5 22.39,17.5 12,23 1.61,17.5 1.61,6.5", fill: fill, mask: mask }));
112
+ case 'diamond':
113
+ return jsx("polygon", { points: "12,1 23,12 12,23 1,12", fill: fill, mask: mask });
114
+ case 'shield':
115
+ return (jsx("path", { d: "M12 1L3 5v6c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V5L12 1z", fill: fill, mask: mask }));
116
+ case 'squircle':
117
+ return (jsx("path", { d: "M12 1C5.5 1 1 5.5 1 8.5v7C1 18.5 5.5 23 12 23s11-4.5 11-7.5v-7C23 5.5 18.5 1 12 1z", fill: fill, mask: mask }));
118
+ default:
119
+ return jsx("rect", { x: "1", y: "1", width: "22", height: "22", rx: "4", fill: fill, mask: mask });
120
+ }
121
+ })()
122
+ : null;
123
+ // Mirrors the client renderer in createIcon.tsx: `sharp` / `rounded` /
124
+ // `wireframe` / `mini` all reuse `paths.solid`, so each needs its own pass
125
+ // to be distinguishable.
126
+ const rendered = isCutout
127
+ ? cutoutBg
128
+ : variant === 'sharp'
129
+ ? sharpenTree(pathRenderer(renderProps))
130
+ : variant === 'rounded'
131
+ ? roundTree(pathRenderer(renderProps))
132
+ : variant === 'wireframe'
133
+ ? wireTree(pathRenderer(renderProps), renderProps.primaryColor)
134
+ : pathRenderer(renderProps);
135
+ const iconContent = variant === 'mini' ? (jsx("g", { transform: `translate(12 12) scale(${MINI_SCALE}) translate(-12 -12)`, children: rendered })) : (rendered);
136
+ return (jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: viewBox, width: resolvedSize, height: resolvedSize, fill: "none", className: className, style: finalStyle, role: role, "aria-label": ariaLabel, "aria-hidden": ariaHidden !== null && ariaHidden !== void 0 ? ariaHidden : !ariaLabel, "data-testid": testId, children: [title && jsx("title", { children: title }), gradientDefs, isCutout && (jsx("defs", { children: jsxs("mask", { id: cutoutMaskId, children: [jsx("rect", { x: "0", y: "0", width: "24", height: "24", fill: "white" }), jsx("g", { transform: "translate(12,12) scale(0.8) translate(-12,-12)", children: paths.solid({
137
+ ...renderProps,
138
+ primaryColor: 'black',
139
+ secondaryColor: 'black',
140
+ accentColor: 'black',
141
+ }) })] }) })), iconContent] }));
142
+ }
143
+ IconRSC.displayName = displayName;
144
+ return IconRSC;
145
+ }
146
+ /* ────────────────────────────────────────────────────────────────────────────
147
+ STREAMING-FRIENDLY UTILITIES
148
+ ───────────────────────────────────────────────────────────────────────────── */
149
+ /**
150
+ * Renders an icon to a static SVG string (for streaming/SSR).
151
+ * Useful for generating static assets or email templates.
152
+ */
153
+ function renderIconToString(Icon, props = {}) {
154
+ var _a;
155
+ try {
156
+ // Import react-dom/server for SSR rendering
157
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
158
+ const { renderToStaticMarkup } = require('react-dom/server');
159
+ const element = Icon(props);
160
+ return renderToStaticMarkup(element);
161
+ }
162
+ catch (_b) {
163
+ // Fallback: construct SVG manually
164
+ const { size = 'md', primaryColor = 'currentColor', variant: _variant = 'solid', className = '', } = props;
165
+ const sizeValue = (_a = SIZE_MAP[size]) !== null && _a !== void 0 ? _a : (typeof size === 'number' ? size : 24);
166
+ return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="${sizeValue}" height="${sizeValue}" class="${className}" fill="${primaryColor}"><path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5" fill="${primaryColor}"></path></svg>`;
167
+ }
168
+ }
169
+ /**
170
+ * Type guard to check if a component is an RSC-compatible icon
171
+ */
172
+ function isRSCIcon(component) {
173
+ return (typeof component === 'function' &&
174
+ 'displayName' in component &&
175
+ typeof component.displayName === 'string' &&
176
+ component.displayName.endsWith('RSC'));
177
+ }
178
+ /* ────────────────────────────────────────────────────────────────────────────
179
+ RSC ICON WRAPPER
180
+ ───────────────────────────────────────────────────────────────────────────── */
181
+ /**
182
+ * Creates an RSC version from an existing icon's path config.
183
+ * This allows creating RSC-compatible versions from standard icons.
184
+ *
185
+ * @example
186
+ * ```tsx
187
+ * import { NtdHome } from '@nice2dev/icons';
188
+ * import { wrapAsRSC } from '@nice2dev/icons/rsc';
189
+ *
190
+ * // Note: This requires access to the original path config
191
+ * const NtdHomeRSC = wrapAsRSC(NtdHome);
192
+ * ```
193
+ */
194
+ function wrapAsRSC(originalPaths, displayName) {
195
+ return createIconRSC({
196
+ displayName: `${displayName}RSC`,
197
+ paths: originalPaths,
198
+ });
199
+ }
200
+
201
+ export { GRADIENT_PRESETS, SEMANTIC_COLOR_DEFAULTS, SIZE_MAP, createIconRSC, isRSCIcon, renderIconToString, wrapAsRSC };
@@ -139,4 +139,3 @@ function wireTree(node, color) {
139
139
  }
140
140
 
141
141
  export { MINI_SCALE, WIREFRAME_STROKE, roundTree, sharpenTree, wireTree };
142
- //# sourceMappingURL=sharpen.js.map
@@ -175,4 +175,3 @@ const SMIL_ANIMATION_TYPES = [
175
175
  ];
176
176
 
177
177
  export { SMIL_ANIMATION_TYPES, SmilAnimationWrapper, createSmilAnimationDef, generateSmilAnimation, isSmilSupported };
178
- //# sourceMappingURL=smilAnimations.js.map
@@ -340,4 +340,3 @@ const ntdIconPreset = {
340
340
  };
341
341
 
342
342
  export { ntdIconPlugin, ntdIconPreset };
343
- //# sourceMappingURL=tailwind-plugin.js.map
package/dist/esm/types.js CHANGED
@@ -77,4 +77,3 @@ const SIZE_MAP = {
77
77
  };
78
78
 
79
79
  export { GRADIENT_PRESETS, SEMANTIC_COLOR_DEFAULTS, SEMANTIC_COLOR_VARS, SIZE_MAP };
80
- //# sourceMappingURL=types.js.map
@@ -0,0 +1,384 @@
1
+ import { useEffect, useState, useCallback, createElement, useSyncExternalStore } from 'react';
2
+
3
+ /**
4
+ * @file utilities.ts
5
+ * @description Utility hooks and functions for @nice2dev/icons
6
+ *
7
+ * Provides:
8
+ * - useIcon hook for dynamic icon loading
9
+ * - useReducedMotion hook for accessibility
10
+ * - preloadIcons for performance optimization
11
+ * - Icon type guards and helpers
12
+ *
13
+ * @example
14
+ * ```tsx
15
+ * import { useIcon, useReducedMotion, preloadIcons } from '@nice2dev/icons/utilities';
16
+ *
17
+ * // Dynamic icon loading
18
+ * const { Icon, isLoading } = useIcon('NtdHome');
19
+ *
20
+ * // Respect user preference for reduced motion
21
+ * const prefersReducedMotion = useReducedMotion();
22
+ *
23
+ * // Preload icons for better performance
24
+ * await preloadIcons(['NtdHome', 'NtdSearch', 'NtdUser']);
25
+ * ```
26
+ *
27
+ * @version 1.0.0
28
+ * @since 2026-03
29
+ * @license MIT
30
+ */
31
+ /* ────────────────────────────────────────────────────────────────────────────
32
+ ICON REGISTRY
33
+ ───────────────────────────────────────────────────────────────────────────── */
34
+ /**
35
+ * Global icon cache for dynamic loading
36
+ */
37
+ const iconRegistry = new Map();
38
+ /**
39
+ * Registers an icon in the global registry
40
+ */
41
+ function registerIcon(name, component) {
42
+ iconRegistry.set(name, {
43
+ component,
44
+ loadedAt: Date.now(),
45
+ });
46
+ }
47
+ /**
48
+ * Gets an icon from the registry
49
+ */
50
+ function getIconFromRegistry(name) {
51
+ var _a;
52
+ return (_a = iconRegistry.get(name)) === null || _a === void 0 ? void 0 : _a.component;
53
+ }
54
+ /**
55
+ * Clears the icon registry
56
+ */
57
+ function clearIconRegistry() {
58
+ iconRegistry.clear();
59
+ }
60
+ /**
61
+ * Gets all registered icon names
62
+ */
63
+ function getRegisteredIconNames() {
64
+ return Array.from(iconRegistry.keys());
65
+ }
66
+ /* ────────────────────────────────────────────────────────────────────────────
67
+ USE ICON HOOK
68
+ ───────────────────────────────────────────────────────────────────────────── */
69
+ /**
70
+ * Hook for dynamically loading icons by name.
71
+ *
72
+ * @param iconName - Name of the icon to load (e.g., 'NtdHome')
73
+ * @param options - Loading options
74
+ * @returns Icon component, loading state, and error
75
+ *
76
+ * @example
77
+ * ```tsx
78
+ * function DynamicIcon({ name }: { name: string }) {
79
+ * const { Icon, isLoading, error } = useIcon(name);
80
+ *
81
+ * if (isLoading) return <Spinner />;
82
+ * if (error || !Icon) return <FallbackIcon />;
83
+ *
84
+ * return <Icon size="md" />;
85
+ * }
86
+ * ```
87
+ */
88
+ function useIcon(iconName, options = {}) {
89
+ const { fallback, cache = true } = options;
90
+ const [state, setState] = useState({
91
+ Icon: iconName ? getIconFromRegistry(iconName) || null : null,
92
+ isLoading: false,
93
+ error: null,
94
+ });
95
+ useEffect(() => {
96
+ if (!iconName) {
97
+ setState({ Icon: null, isLoading: false, error: null });
98
+ return;
99
+ }
100
+ // Check cache first
101
+ const cached = getIconFromRegistry(iconName);
102
+ if (cached) {
103
+ setState({ Icon: cached, isLoading: false, error: null });
104
+ return;
105
+ }
106
+ // Start loading
107
+ setState((prev) => ({ ...prev, isLoading: true, error: null }));
108
+ // Dynamic import (assumes icons are exported individually)
109
+ const loadIcon = async () => {
110
+ try {
111
+ // Try to import from the icons module
112
+ const module = await import('./icons/index.js');
113
+ const IconComponent = module[iconName];
114
+ if (IconComponent) {
115
+ if (cache) {
116
+ registerIcon(iconName, IconComponent);
117
+ }
118
+ setState({ Icon: IconComponent, isLoading: false, error: null });
119
+ }
120
+ else {
121
+ throw new Error(`Icon "${iconName}" not found`);
122
+ }
123
+ }
124
+ catch (err) {
125
+ setState({
126
+ Icon: fallback || null,
127
+ isLoading: false,
128
+ error: err instanceof Error ? err : new Error('Failed to load icon'),
129
+ });
130
+ }
131
+ };
132
+ loadIcon();
133
+ }, [iconName, cache, fallback]);
134
+ return state;
135
+ }
136
+ /* ────────────────────────────────────────────────────────────────────────────
137
+ USE REDUCED MOTION
138
+ ───────────────────────────────────────────────────────────────────────────── */
139
+ /**
140
+ * Subscribes to prefers-reduced-motion media query.
141
+ */
142
+ function subscribeToReducedMotion(callback) {
143
+ if (typeof window === 'undefined') {
144
+ return () => { };
145
+ }
146
+ const mediaQuery = window.matchMedia('(prefers-reduced-motion: reduce)');
147
+ const handler = () => callback();
148
+ mediaQuery.addEventListener('change', handler);
149
+ return () => mediaQuery.removeEventListener('change', handler);
150
+ }
151
+ /**
152
+ * Gets current reduced motion preference.
153
+ */
154
+ function getReducedMotionSnapshot() {
155
+ if (typeof window === 'undefined') {
156
+ return false;
157
+ }
158
+ return window.matchMedia('(prefers-reduced-motion: reduce)').matches;
159
+ }
160
+ /**
161
+ * Server snapshot for SSR (assumes no reduced motion preference).
162
+ */
163
+ function getReducedMotionServerSnapshot() {
164
+ return false;
165
+ }
166
+ /**
167
+ * Hook that returns true if user prefers reduced motion.
168
+ * Uses useSyncExternalStore for SSR compatibility.
169
+ *
170
+ * @returns Whether the user prefers reduced motion
171
+ *
172
+ * @example
173
+ * ```tsx
174
+ * function AnimatedIcon() {
175
+ * const prefersReducedMotion = useReducedMotion();
176
+ *
177
+ * return (
178
+ * <NtdHeart
179
+ * animation={prefersReducedMotion ? undefined : 'pulse'}
180
+ * />
181
+ * );
182
+ * }
183
+ * ```
184
+ */
185
+ function useReducedMotion() {
186
+ return useSyncExternalStore(subscribeToReducedMotion, getReducedMotionSnapshot, getReducedMotionServerSnapshot);
187
+ }
188
+ /* ────────────────────────────────────────────────────────────────────────────
189
+ PRELOAD ICONS
190
+ ───────────────────────────────────────────────────────────────────────────── */
191
+ /**
192
+ * Preloads icons into the registry for instant access.
193
+ *
194
+ * @param iconNames - Array of icon names to preload
195
+ * @returns Promise that resolves when all icons are loaded
196
+ *
197
+ * @example
198
+ * ```tsx
199
+ * // Preload critical icons at app startup
200
+ * useEffect(() => {
201
+ * preloadIcons(['NtdHome', 'NtdSearch', 'NtdUser', 'NtdMenu']);
202
+ * }, []);
203
+ * ```
204
+ */
205
+ async function preloadIcons(iconNames) {
206
+ const results = new Map();
207
+ try {
208
+ const module = await import('./icons/index.js');
209
+ for (const name of iconNames) {
210
+ const IconComponent = module[name];
211
+ if (IconComponent) {
212
+ registerIcon(name, IconComponent);
213
+ results.set(name, IconComponent);
214
+ }
215
+ }
216
+ }
217
+ catch (error) {
218
+ console.warn('Failed to preload icons:', error);
219
+ }
220
+ return results;
221
+ }
222
+ /**
223
+ * Creates a preloader component that preloads icons on mount.
224
+ *
225
+ * @param iconNames - Icons to preload
226
+ * @returns React component that preloads icons
227
+ *
228
+ * @example
229
+ * ```tsx
230
+ * const IconPreloader = createIconPreloader(['NtdHome', 'NtdSearch']);
231
+ *
232
+ * function App() {
233
+ * return (
234
+ * <>
235
+ * <IconPreloader />
236
+ * <MainContent />
237
+ * </>
238
+ * );
239
+ * }
240
+ * ```
241
+ */
242
+ function createIconPreloader(iconNames) {
243
+ return function IconPreloader() {
244
+ useEffect(() => {
245
+ preloadIcons(iconNames);
246
+ }, []);
247
+ return null;
248
+ };
249
+ }
250
+ /* ────────────────────────────────────────────────────────────────────────────
251
+ ICON PROPS HELPERS
252
+ ───────────────────────────────────────────────────────────────────────────── */
253
+ /**
254
+ * Creates props with animation disabled if user prefers reduced motion.
255
+ *
256
+ * @param props - Original icon props
257
+ * @param prefersReducedMotion - Whether user prefers reduced motion
258
+ * @returns Props with animation conditionally disabled
259
+ */
260
+ function withReducedMotionProps(props, prefersReducedMotion) {
261
+ if (!prefersReducedMotion) {
262
+ return props;
263
+ }
264
+ return {
265
+ ...props,
266
+ animation: undefined,
267
+ animationDuration: undefined,
268
+ gradientAnimation: undefined,
269
+ };
270
+ }
271
+ /**
272
+ * Creates accessible icon props based on context.
273
+ *
274
+ * @param options - Accessibility options
275
+ * @returns Icon props for accessibility
276
+ */
277
+ function createAccessibleIconProps(options) {
278
+ const { label, isDecorative = !label, role } = options;
279
+ if (isDecorative) {
280
+ return {
281
+ 'aria-hidden': true,
282
+ role: 'presentation',
283
+ };
284
+ }
285
+ return {
286
+ 'aria-label': label,
287
+ role: role || 'img',
288
+ title: label,
289
+ };
290
+ }
291
+ /* ────────────────────────────────────────────────────────────────────────────
292
+ ICON TYPE GUARDS
293
+ ───────────────────────────────────────────────────────────────────────────── */
294
+ /**
295
+ * Checks if a value is a valid icon component.
296
+ * Handles both regular components and forwardRef components.
297
+ */
298
+ function isIconComponent(value) {
299
+ if (value == null) {
300
+ return false;
301
+ }
302
+ // ForwardRef components are objects with $$typeof, regular components are functions
303
+ const isValidComponent = typeof value === 'function' || (typeof value === 'object' && '$$typeof' in value);
304
+ if (!isValidComponent) {
305
+ return false;
306
+ }
307
+ const displayName = value.displayName;
308
+ return typeof displayName === 'string' && displayName.startsWith('Ntd');
309
+ }
310
+ /**
311
+ * Checks if an icon name is valid.
312
+ */
313
+ function isValidIconName(name) {
314
+ return /^Ntd[A-Z][a-zA-Z0-9]*$/.test(name);
315
+ }
316
+ /**
317
+ * Creates a function that renders multiple icons efficiently.
318
+ *
319
+ * @param configs - Array of icon configurations
320
+ * @returns Function that renders all configured icons
321
+ *
322
+ * @example
323
+ * ```tsx
324
+ * const renderNavIcons = createBatchIconRenderer([
325
+ * { name: 'NtdHome', props: { size: 'md' } },
326
+ * { name: 'NtdSearch', props: { size: 'md' } },
327
+ * { name: 'NtdUser', props: { size: 'md' } },
328
+ * ]);
329
+ *
330
+ * // In component
331
+ * const icons = renderNavIcons();
332
+ * ```
333
+ */
334
+ function useBatchIcons(configs) {
335
+ const [icons, setIcons] = useState(new Map());
336
+ const [isLoading, setIsLoading] = useState(true);
337
+ useEffect(() => {
338
+ const loadIcons = async () => {
339
+ setIsLoading(true);
340
+ const names = configs.map((c) => c.name);
341
+ const loadedIcons = await preloadIcons(names);
342
+ setIcons(loadedIcons);
343
+ setIsLoading(false);
344
+ };
345
+ loadIcons();
346
+ }, [configs.map((c) => c.name).join(',')]);
347
+ const renderIcon = useCallback((name, additionalProps) => {
348
+ const Icon = icons.get(name);
349
+ const config = configs.find((c) => c.name === name);
350
+ if (!Icon) {
351
+ return null;
352
+ }
353
+ const mergedProps = {
354
+ ...config === null || config === void 0 ? void 0 : config.props,
355
+ ...additionalProps,
356
+ };
357
+ return createElement(Icon, mergedProps);
358
+ }, [icons, configs]);
359
+ return {
360
+ renderIcon,
361
+ isLoading,
362
+ icons,
363
+ };
364
+ }
365
+ /* ────────────────────────────────────────────────────────────────────────────
366
+ EXPORTS
367
+ ───────────────────────────────────────────────────────────────────────────── */
368
+ var utilities = {
369
+ useIcon,
370
+ useReducedMotion,
371
+ preloadIcons,
372
+ createIconPreloader,
373
+ registerIcon,
374
+ getIconFromRegistry,
375
+ clearIconRegistry,
376
+ getRegisteredIconNames,
377
+ withReducedMotionProps,
378
+ createAccessibleIconProps,
379
+ isIconComponent,
380
+ isValidIconName,
381
+ useBatchIcons,
382
+ };
383
+
384
+ export { clearIconRegistry, createAccessibleIconProps, createIconPreloader, utilities as default, getIconFromRegistry, getRegisteredIconNames, isIconComponent, isValidIconName, preloadIcons, registerIcon, useBatchIcons, useIcon, useReducedMotion, withReducedMotionProps };
@@ -73,4 +73,3 @@ function toIconProps(props) {
73
73
  }
74
74
 
75
75
  export { isCutoutProps, isDuotoneProps, isFlatProps, isMiniProps, isOutlineProps, isRoundedProps, isSharpProps, isSolidProps, isThreeDProps, isVividProps, toIconProps };
76
- //# sourceMappingURL=variantProps.js.map
@@ -107,11 +107,11 @@ export declare const SHADCN_ICON_SIZES: {
107
107
  export declare const SHADCN_COLOR_TOKENS: {
108
108
  readonly foreground: "hsl(var(--foreground))";
109
109
  readonly mutedForeground: "hsl(var(--muted-foreground))";
110
- readonly primary: "hsl(var(--primary))";
110
+ readonly primary: "hsl(var(--nice-color-primary))";
111
111
  readonly primaryForeground: "hsl(var(--primary-foreground))";
112
112
  readonly secondary: "hsl(var(--secondary))";
113
113
  readonly secondaryForeground: "hsl(var(--secondary-foreground))";
114
- readonly accent: "hsl(var(--accent))";
114
+ readonly accent: "hsl(var(--nice-color-accent))";
115
115
  readonly accentForeground: "hsl(var(--accent-foreground))";
116
116
  readonly destructive: "hsl(var(--destructive))";
117
117
  readonly destructiveForeground: "hsl(var(--destructive-foreground))";
@@ -337,11 +337,11 @@ declare const _default: {
337
337
  SHADCN_COLOR_TOKENS: {
338
338
  readonly foreground: "hsl(var(--foreground))";
339
339
  readonly mutedForeground: "hsl(var(--muted-foreground))";
340
- readonly primary: "hsl(var(--primary))";
340
+ readonly primary: "hsl(var(--nice-color-primary))";
341
341
  readonly primaryForeground: "hsl(var(--primary-foreground))";
342
342
  readonly secondary: "hsl(var(--secondary))";
343
343
  readonly secondaryForeground: "hsl(var(--secondary-foreground))";
344
- readonly accent: "hsl(var(--accent))";
344
+ readonly accent: "hsl(var(--nice-color-accent))";
345
345
  readonly accentForeground: "hsl(var(--accent-foreground))";
346
346
  readonly destructive: "hsl(var(--destructive))";
347
347
  readonly destructiveForeground: "hsl(var(--destructive-foreground))";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nice2dev/icons",
3
- "version": "1.1.5",
3
+ "version": "1.1.7",
4
4
  "description": "Beautiful, lightweight, multi-color SVG icon library for React with animations",
5
5
  "author": "NiceToDev <contact@nicetodev.com>",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -52,7 +52,7 @@
52
52
  "import": "./dist/esm/utilities.js",
53
53
  "require": "./dist/cjs/utilities.js"
54
54
  },
55
- "./styles.css": "./dist/animations.css"
55
+ "./styles.css": "./dist/esm/nicetodev-icons.css"
56
56
  },
57
57
  "bin": {
58
58
  "ntd-icons": "./bin/ntd-icons.mjs"