@nice2dev/icons 1.2.1 → 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.
- package/CHANGELOG.md +538 -0
- package/README.md +146 -134
- package/dist/cjs/NiceDesktopIconExporter.js +7 -3
- package/dist/cjs/NiceFaviconGenerator.js +6 -2
- package/dist/cjs/NiceIconPicker.js +36 -28
- package/dist/cjs/NiceIconPreview.js +3 -1
- package/dist/cjs/NiceIconSetCreator.js +7 -2
- package/dist/cjs/NtdIconBadge.js +18 -3
- package/dist/cjs/NtdIconMorph.js +3 -1
- package/dist/cjs/createIcon.js +10 -2
- package/dist/cjs/design-tokens.js +2 -0
- package/dist/cjs/iconMetadata.js +4 -0
- package/dist/cjs/iconParts.js +49 -0
- package/dist/cjs/icons/accessibility.js +12 -4
- package/dist/cjs/icons/actions.js +89 -25
- package/dist/cjs/icons/business.js +58 -28
- package/dist/cjs/icons/ecommerce.js +0 -20
- package/dist/cjs/icons/education.js +22 -9
- package/dist/cjs/icons/files.js +70 -56
- package/dist/cjs/icons/fun.js +39 -25
- package/dist/cjs/icons/gaming.js +29 -4
- package/dist/cjs/icons/health.js +11 -4
- package/dist/cjs/icons/index.js +63 -5
- package/dist/cjs/icons/input.js +120 -0
- package/dist/cjs/icons/legal.js +4 -9
- package/dist/cjs/icons/media.js +26 -11
- package/dist/cjs/icons/navigation.js +4 -2
- package/dist/cjs/icons/omniverk.js +19 -6
- package/dist/cjs/icons/realestate.js +10 -4
- package/dist/cjs/icons/saas.js +15 -4
- package/dist/cjs/icons/science.js +6 -3
- package/dist/cjs/icons/shapes.js +133 -31
- package/dist/cjs/icons/sports.js +6 -2
- package/dist/cjs/icons/ui.js +62 -20
- package/dist/cjs/icons/weather.js +18 -8
- package/dist/cjs/index.js +63 -5
- package/dist/cjs/nicetodev-icons.css +1 -1
- package/dist/cjs/resolver.js +55 -1
- package/dist/cjs/rsc.js +1 -1
- package/dist/cjs/utilities.js +4 -2
- package/dist/esm/NiceDesktopIconExporter.js +7 -3
- package/dist/esm/NiceFaviconGenerator.js +6 -2
- package/dist/esm/NiceIconPicker.js +37 -29
- package/dist/esm/NiceIconPreview.js +3 -1
- package/dist/esm/NiceIconSetCreator.js +7 -2
- package/dist/esm/NtdIconBadge.js +18 -3
- package/dist/esm/NtdIconMorph.js +3 -1
- package/dist/esm/createIcon.js +10 -2
- package/dist/esm/design-tokens.js +2 -0
- package/dist/esm/iconMetadata.js +4 -0
- package/dist/esm/iconParts.js +46 -0
- package/dist/esm/icons/accessibility.js +12 -4
- package/dist/esm/icons/actions.js +87 -26
- package/dist/esm/icons/business.js +58 -29
- package/dist/esm/icons/ecommerce.js +1 -19
- package/dist/esm/icons/education.js +22 -10
- package/dist/esm/icons/files.js +70 -56
- package/dist/esm/icons/fun.js +37 -25
- package/dist/esm/icons/gaming.js +29 -5
- package/dist/esm/icons/health.js +11 -4
- package/dist/esm/icons/index.js +10 -9
- package/dist/esm/icons/input.js +75 -0
- package/dist/esm/icons/legal.js +4 -9
- package/dist/esm/icons/media.js +26 -11
- package/dist/esm/icons/navigation.js +4 -2
- package/dist/esm/icons/omniverk.js +19 -6
- package/dist/esm/icons/realestate.js +10 -4
- package/dist/esm/icons/saas.js +15 -4
- package/dist/esm/icons/science.js +6 -3
- package/dist/esm/icons/shapes.js +128 -32
- package/dist/esm/icons/sports.js +6 -2
- package/dist/esm/icons/ui.js +62 -21
- package/dist/esm/icons/weather.js +18 -8
- package/dist/esm/index.js +10 -9
- package/dist/esm/nicetodev-icons.css +1 -1
- package/dist/esm/resolver.js +55 -1
- package/dist/esm/rsc.js +1 -1
- package/dist/esm/utilities.js +4 -2
- package/dist/types/design-tokens.d.ts +2 -0
- package/dist/types/iconParts.d.ts +19 -0
- package/dist/types/icons/actions.d.ts +3 -0
- package/dist/types/icons/business.d.ts +2 -0
- package/dist/types/icons/ecommerce.d.ts +0 -2
- package/dist/types/icons/education.d.ts +1 -0
- package/dist/types/icons/files.d.ts +2 -1
- package/dist/types/icons/fun.d.ts +6 -1
- package/dist/types/icons/gaming.d.ts +2 -0
- package/dist/types/icons/index.d.ts +10 -9
- package/dist/types/icons/input.d.ts +53 -0
- package/dist/types/icons/omniverk.d.ts +0 -2
- package/dist/types/icons/shapes.d.ts +6 -0
- package/dist/types/icons/ui.d.ts +1 -0
- package/dist/types/types.d.ts +6 -0
- package/package.json +3 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var jsxRuntime = require('react/jsx-runtime');
|
|
4
|
+
var uiCore = require('@nice2dev/ui-core');
|
|
4
5
|
var React = require('react');
|
|
5
6
|
var actions = require('./icons/actions.js');
|
|
6
7
|
|
|
@@ -225,6 +226,7 @@ function useFaviconGenerator(icon, fill = '#1e1e1e', initialConfig) {
|
|
|
225
226
|
* Favicon generator component for creating all favicon variants.
|
|
226
227
|
*/
|
|
227
228
|
function NiceFaviconGenerator({ icon, fill = '#1e1e1e', manifestConfig, onGenerate, className = '', }) {
|
|
229
|
+
const { t } = uiCore.useNiceTranslation();
|
|
228
230
|
const { config, setConfig, assets, isGenerating, generate, downloadAll, downloadSingle, getManifestJson, getHtmlTags, } = useFaviconGenerator(icon, fill, manifestConfig);
|
|
229
231
|
const [activeTab, setActiveTab] = React.useState('preview');
|
|
230
232
|
const handleGenerate = async () => {
|
|
@@ -233,10 +235,12 @@ function NiceFaviconGenerator({ icon, fill = '#1e1e1e', manifestConfig, onGenera
|
|
|
233
235
|
onGenerate(assets, getManifestJson());
|
|
234
236
|
}
|
|
235
237
|
};
|
|
236
|
-
return (jsxRuntime.jsxs("div", { className: `nice-favicon-generator ${className}`, style: styles.container, children: [jsxRuntime.jsxs("div", { style: styles.section, children: [jsxRuntime.jsx("h3", { style: styles.sectionTitle, children:
|
|
238
|
+
return (jsxRuntime.jsxs("div", { className: `nice-favicon-generator ${className}`, style: styles.container, children: [jsxRuntime.jsxs("div", { style: styles.section, children: [jsxRuntime.jsx("h3", { style: styles.sectionTitle, children: t('faviconGenerator.manifestConfiguration', 'Manifest Configuration') }), jsxRuntime.jsxs("div", { style: styles.formGrid, children: [jsxRuntime.jsxs("div", { style: styles.formGroup, children: [jsxRuntime.jsx("label", { style: styles.label, children: t('faviconGenerator.appName', 'App Name') }), jsxRuntime.jsx("input", { type: "text", value: config.name, onChange: (e) => setConfig((c) => ({ ...c, name: e.target.value })), style: styles.input })] }), jsxRuntime.jsxs("div", { style: styles.formGroup, children: [jsxRuntime.jsx("label", { style: styles.label, children: t('faviconGenerator.shortName', 'Short Name') }), jsxRuntime.jsx("input", { type: "text", value: config.shortName, onChange: (e) => setConfig((c) => ({ ...c, shortName: e.target.value })), style: styles.input })] }), jsxRuntime.jsxs("div", { style: styles.formGroup, children: [jsxRuntime.jsx("label", { style: styles.label, children: t('faviconGenerator.themeColor', 'Theme Color') }), jsxRuntime.jsx("input", { type: "color", value: config.themeColor, onChange: (e) => setConfig((c) => ({ ...c, themeColor: e.target.value })), style: styles.colorInput })] }), jsxRuntime.jsxs("div", { style: styles.formGroup, children: [jsxRuntime.jsx("label", { style: styles.label, children: t('faviconGenerator.backgroundColor', 'Background Color') }), jsxRuntime.jsx("input", { type: "color", value: config.backgroundColor, onChange: (e) => setConfig((c) => ({ ...c, backgroundColor: e.target.value })), style: styles.colorInput })] }), jsxRuntime.jsxs("div", { style: styles.formGroup, children: [jsxRuntime.jsx("label", { style: styles.label, children: t('faviconGenerator.displayMode', 'Display Mode') }), jsxRuntime.jsxs("select", { value: config.display, onChange: (e) => setConfig((c) => ({ ...c, display: e.target.value })), style: styles.input, children: [jsxRuntime.jsx("option", { value: "standalone", children: t('faviconGenerator.standalone', 'Standalone') }), jsxRuntime.jsx("option", { value: "fullscreen", children: t('faviconGenerator.fullscreen', 'Fullscreen') }), jsxRuntime.jsx("option", { value: "minimal-ui", children: t('faviconGenerator.minimalUi', 'Minimal UI') }), jsxRuntime.jsx("option", { value: "browser", children: t('faviconGenerator.browser', 'Browser') })] })] }), jsxRuntime.jsxs("div", { style: styles.formGroup, children: [jsxRuntime.jsx("label", { style: styles.label, children: t('faviconGenerator.startUrl', 'Start URL') }), jsxRuntime.jsx("input", { type: "text", value: config.startUrl, onChange: (e) => setConfig((c) => ({ ...c, startUrl: e.target.value })), style: styles.input })] })] })] }), jsxRuntime.jsxs("div", { style: styles.actions, children: [jsxRuntime.jsx("button", { onClick: handleGenerate, disabled: isGenerating, style: {
|
|
237
239
|
...styles.button,
|
|
238
240
|
...(isGenerating ? styles.buttonDisabled : styles.buttonPrimary),
|
|
239
|
-
}, children: isGenerating
|
|
241
|
+
}, children: isGenerating
|
|
242
|
+
? t('faviconGenerator.generating', 'Generating...')
|
|
243
|
+
: t('faviconGenerator.generateFavicons', 'Generate Favicons') }), assets.length > 0 && (jsxRuntime.jsx("button", { onClick: downloadAll, style: { ...styles.button, ...styles.buttonSecondary }, children: t('faviconGenerator.downloadAll', 'Download All') }))] }), assets.length > 0 && (jsxRuntime.jsxs("div", { style: styles.section, children: [jsxRuntime.jsxs("div", { style: styles.tabs, children: [jsxRuntime.jsx("button", { style: { ...styles.tab, ...(activeTab === 'preview' ? styles.tabActive : {}) }, onClick: () => setActiveTab('preview'), children: t('faviconGenerator.preview', 'Preview') }), jsxRuntime.jsx("button", { style: { ...styles.tab, ...(activeTab === 'manifest' ? styles.tabActive : {}) }, onClick: () => setActiveTab('manifest'), children: "manifest.json" }), jsxRuntime.jsx("button", { style: { ...styles.tab, ...(activeTab === 'html' ? styles.tabActive : {}) }, onClick: () => setActiveTab('html'), children: t('faviconGenerator.htmlTags', 'HTML Tags') })] }), activeTab === 'preview' && (jsxRuntime.jsx("div", { style: styles.assetGrid, children: assets.map((asset) => (jsxRuntime.jsxs("div", { style: styles.assetCard, children: [jsxRuntime.jsx("div", { style: styles.assetPreview, children: jsxRuntime.jsx("img", { src: asset.dataUrl, alt: asset.filename, style: { maxWidth: '64px', maxHeight: '64px' } }) }), jsxRuntime.jsxs("div", { style: styles.assetInfo, children: [jsxRuntime.jsx("span", { style: styles.assetName, children: asset.filename }), jsxRuntime.jsxs("span", { style: styles.assetSize, children: [asset.size, "x", asset.size] }), jsxRuntime.jsx("span", { style: styles.assetPurpose, children: asset.purpose })] }), jsxRuntime.jsx("button", { onClick: () => downloadSingle(asset), style: styles.downloadBtn, "aria-label": t('faviconGenerator.download', 'Download {filename}').replace('{filename}', String(asset.filename)), children: jsxRuntime.jsx(actions.NtdDownload, { size: 14, variant: "outline" }) })] }, asset.filename))) })), activeTab === 'manifest' && jsxRuntime.jsx("pre", { style: styles.codeBlock, children: getManifestJson() }), activeTab === 'html' && jsxRuntime.jsx("pre", { style: styles.codeBlock, children: getHtmlTags() })] })), jsxRuntime.jsxs("div", { style: styles.reference, children: [jsxRuntime.jsx("h4", { style: styles.referenceTitle, children: t('faviconGenerator.faviconSizesReference', 'Favicon Sizes Reference') }), jsxRuntime.jsxs("table", { style: styles.table, children: [jsxRuntime.jsx("thead", { children: jsxRuntime.jsxs("tr", { children: [jsxRuntime.jsx("th", { style: styles.th, children: t('faviconGenerator.file', 'File') }), jsxRuntime.jsx("th", { style: styles.th, children: t('faviconGenerator.size', 'Size') }), jsxRuntime.jsx("th", { style: styles.th, children: t('faviconGenerator.purpose', 'Purpose') })] }) }), jsxRuntime.jsx("tbody", { children: FAVICON_SIZES.map((f) => (jsxRuntime.jsxs("tr", { children: [jsxRuntime.jsxs("td", { style: styles.td, children: [f.name, ".", f.format] }), jsxRuntime.jsxs("td", { style: styles.td, children: [f.size, "x", f.size] }), jsxRuntime.jsx("td", { style: styles.td, children: f.purpose })] }, f.name))) })] })] })] }));
|
|
240
244
|
}
|
|
241
245
|
/* ══════════════════════════════════════════════════════════════════════════════
|
|
242
246
|
Styles
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var jsxRuntime = require('react/jsx-runtime');
|
|
4
|
+
var uiCore = require('@nice2dev/ui-core');
|
|
4
5
|
var React = require('react');
|
|
5
6
|
var actions = require('./icons/actions.js');
|
|
6
7
|
var files = require('./icons/files.js');
|
|
@@ -71,8 +72,11 @@ function saveToStorage(key, value) {
|
|
|
71
72
|
* }
|
|
72
73
|
* ```
|
|
73
74
|
*/
|
|
74
|
-
const NiceIconPicker = /* @__PURE__ */ React.forwardRef(function NiceIconPicker({ icons, value = null, values = [], onChange, onChangeMulti, multiSelect = false, maxSelections, placeholder
|
|
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) {
|
|
75
76
|
var _a;
|
|
77
|
+
const tablistRef = React.useRef(null);
|
|
78
|
+
uiCore.useRovingFocus(tablistRef, { orientation: 'horizontal', wrap: true, activateOnMove: true });
|
|
79
|
+
const { t } = uiCore.useNiceTranslation();
|
|
76
80
|
const [isOpen, setIsOpen] = React.useState(false);
|
|
77
81
|
const [search, setSearch] = React.useState('');
|
|
78
82
|
const [activeCategory, setActiveCategory] = React.useState(null);
|
|
@@ -223,30 +227,26 @@ const NiceIconPicker = /* @__PURE__ */ React.forwardRef(function NiceIconPicker(
|
|
|
223
227
|
document.addEventListener('mousedown', onClickOutside);
|
|
224
228
|
return () => document.removeEventListener('mousedown', onClickOutside);
|
|
225
229
|
}, [isOpen]);
|
|
226
|
-
/* ───
|
|
230
|
+
/* ─── Reset on close ─── */
|
|
227
231
|
React.useEffect(() => {
|
|
228
|
-
if (isOpen) {
|
|
229
|
-
requestAnimationFrame(() => { var _a; return (_a = searchInputRef.current) === null || _a === void 0 ? void 0 : _a.focus(); });
|
|
230
|
-
}
|
|
231
|
-
else {
|
|
232
|
+
if (!isOpen) {
|
|
232
233
|
setSearch('');
|
|
233
234
|
setActiveCategory(null);
|
|
234
235
|
setVisibleRange({ start: 0, end: virtualizedItemCount });
|
|
235
236
|
}
|
|
236
237
|
}, [isOpen, virtualizedItemCount]);
|
|
237
|
-
/* ─── Keyboard ─── */
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
}, [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' });
|
|
250
250
|
/* ─── Virtualization scroll handler ─── */
|
|
251
251
|
React.useEffect(() => {
|
|
252
252
|
if (!virtualized || !isOpen || !gridWrapRef.current) {
|
|
@@ -388,19 +388,27 @@ const NiceIconPicker = /* @__PURE__ */ React.forwardRef(function NiceIconPicker(
|
|
|
388
388
|
if (selectedEntries.length === 1) {
|
|
389
389
|
return readableName(selectedEntries[0].name);
|
|
390
390
|
}
|
|
391
|
-
return
|
|
391
|
+
return t('iconPicker.iconsSelected', '{count} icons selected').replace('{count}', String(selectedEntries.length));
|
|
392
392
|
}
|
|
393
|
-
return selectedEntry
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
393
|
+
return selectedEntry
|
|
394
|
+
? readableName(selectedEntry.name)
|
|
395
|
+
: (placeholder !== null && placeholder !== void 0 ? placeholder : t('iconPicker.selectIcon', 'Select icon…'));
|
|
396
|
+
}, [multiSelect, selectedEntries, selectedEntry, placeholder, t]);
|
|
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__'
|
|
398
|
+
? t('iconPicker.noFavoritesYetClickThe', 'No favorites yet. Click the star icon to add.')
|
|
397
399
|
: activeCategory === '__history__'
|
|
398
|
-
? 'No recent icons.'
|
|
399
|
-
: '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 && !(activeCategory === null || activeCategory === void 0 ? void 0 : activeCategory.startsWith('__')) && (jsxRuntime.jsx("div", { className: "ntd-picker-category-label", children: category })), jsxRuntime.jsx("div", { className: "ntd-picker-grid", children: entries.map((entry) => {
|
|
400
|
+
? t('iconPicker.noRecentIcons', 'No recent icons.')
|
|
401
|
+
: 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 && !(activeCategory === null || activeCategory === void 0 ? void 0 : activeCategory.startsWith('__')) && (jsxRuntime.jsx("div", { className: "ntd-picker-category-label", children: category })), jsxRuntime.jsx("div", { className: "ntd-picker-grid", children: entries.map((entry) => {
|
|
400
402
|
const isSelected = selectedValues.includes(entry.name);
|
|
401
403
|
const isFavorite = favorites.includes(entry.name);
|
|
402
|
-
return (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": readableName(entry.name), role: "option", "aria-selected": isSelected, title: readableName(entry.name), draggable: enableDragAndDrop, onDragStart: enableDragAndDrop ? (e) => handleDragStart(entry, e) : undefined, onDragEnd: enableDragAndDrop ? (e) => handleDragEnd(entry, e) : undefined, children: [jsxRuntime.jsx(entry.Component, { size: gridIconSize, variant: variant }), multiSelect && isSelected && (jsxRuntime.jsx("span", { className: "ntd-picker-cell-check", children: jsxRuntime.jsx(CheckIcon, {}) })), showFavorites && (jsxRuntime.jsx("button", { type: "button", className: `ntd-picker-cell-fav${isFavorite ? ' ntd-picker-cell-fav--active' : ''}`, onClick: (e) => handleFavoriteClick(e, entry.name), title: isFavorite
|
|
403
|
-
|
|
404
|
+
return (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": readableName(entry.name), role: "option", "aria-selected": isSelected, title: readableName(entry.name), draggable: enableDragAndDrop, onDragStart: enableDragAndDrop ? (e) => handleDragStart(entry, e) : undefined, onDragEnd: enableDragAndDrop ? (e) => handleDragEnd(entry, e) : undefined, children: [jsxRuntime.jsx(entry.Component, { size: gridIconSize, variant: variant }), multiSelect && isSelected && (jsxRuntime.jsx("span", { className: "ntd-picker-cell-check", children: jsxRuntime.jsx(CheckIcon, {}) })), showFavorites && (jsxRuntime.jsx("button", { type: "button", className: `ntd-picker-cell-fav${isFavorite ? ' ntd-picker-cell-fav--active' : ''}`, onClick: (e) => handleFavoriteClick(e, entry.name), title: isFavorite
|
|
405
|
+
? t('iconPicker.removeFromFavorites', 'Remove from favorites')
|
|
406
|
+
: t('iconPicker.addToFavorites', 'Add to favorites'), children: jsxRuntime.jsx(StarIcon, { filled: isFavorite }) }))] }, entry.name));
|
|
407
|
+
}) })] }, category))), virtualized && visibleRange.end < filtered.length && (jsxRuntime.jsx("div", { style: { height: Math.ceil((filtered.length - visibleRange.end) / 6) * 48 } }))] })) }), jsxRuntime.jsxs("div", { className: "ntd-picker-footer", children: [jsxRuntime.jsxs("span", { className: "ntd-picker-count", children: [(filtered.length === 1
|
|
408
|
+
? t('iconPicker.iconCountOne', '{count} icon')
|
|
409
|
+
: 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 && selectedValues.length > 0 && (jsxRuntime.jsx("button", { type: "button", className: "ntd-picker-clear", onClick: handleClear, children: multiSelect
|
|
410
|
+
? t('iconPicker.clearAll', 'Clear all')
|
|
411
|
+
: t('iconPicker.clearSelection', 'Clear selection') })), multiSelect && (jsxRuntime.jsx("button", { type: "button", className: "ntd-picker-done", onClick: () => setIsOpen(false), children: t('iconPicker.done', 'Done') }))] })] }))] }));
|
|
404
412
|
});
|
|
405
413
|
|
|
406
414
|
exports.NiceIconPicker = NiceIconPicker;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var jsxRuntime = require('react/jsx-runtime');
|
|
4
|
+
var uiCore = require('@nice2dev/ui-core');
|
|
4
5
|
var React = require('react');
|
|
5
6
|
|
|
6
7
|
/* ══════════════════════════════════════════════════════════════════════════════
|
|
@@ -66,6 +67,7 @@ const animationCSS = `
|
|
|
66
67
|
* Icon preview component with multiple modes and sizes.
|
|
67
68
|
*/
|
|
68
69
|
function NiceIconPreview({ icon: Icon, iconName = 'Icon', mode = 'both', layout = 'grid', sizes = DEFAULT_SIZES, showAnimation = true, animations = DEFAULT_ANIMATIONS, fill, className = '', }) {
|
|
70
|
+
const { t } = uiCore.useNiceTranslation();
|
|
69
71
|
const [selectedAnimation, setSelectedAnimation] = React.useState(null);
|
|
70
72
|
// Inject animation styles
|
|
71
73
|
const styleSheet = React.useMemo(() => jsxRuntime.jsx("style", { dangerouslySetInnerHTML: { __html: animationCSS } }), []);
|
|
@@ -77,7 +79,7 @@ function NiceIconPreview({ icon: Icon, iconName = 'Icon', mode = 'both', layout
|
|
|
77
79
|
background: BACKGROUND_COLORS[bgMode],
|
|
78
80
|
}, children: jsxRuntime.jsx(Icon, { size: size, fill: iconFill, className: selectedAnimation || undefined }) }));
|
|
79
81
|
};
|
|
80
|
-
return (jsxRuntime.jsxs("div", { className: `nice-icon-preview ${className}`, style: styles.container, children: [styleSheet, jsxRuntime.jsxs("div", { style: styles.header, children: [jsxRuntime.jsx("h3", { style: styles.title, children: iconName }), showAnimation && (jsxRuntime.jsxs("div", { style: styles.animationControls, children: [jsxRuntime.jsx("span", { style: styles.animationLabel, children:
|
|
82
|
+
return (jsxRuntime.jsxs("div", { className: `nice-icon-preview ${className}`, style: styles.container, children: [styleSheet, jsxRuntime.jsxs("div", { style: styles.header, children: [jsxRuntime.jsx("h3", { style: styles.title, children: iconName }), showAnimation && (jsxRuntime.jsxs("div", { style: styles.animationControls, children: [jsxRuntime.jsx("span", { style: styles.animationLabel, children: t('iconPreview.animation', 'Animation:') }), jsxRuntime.jsxs("select", { value: selectedAnimation || '', onChange: (e) => setSelectedAnimation(e.target.value || null), style: styles.select, children: [jsxRuntime.jsx("option", { value: "", children: t('iconPreview.none', 'None') }), animations.map((a) => (jsxRuntime.jsx("option", { value: a.className, children: a.name }, a.className)))] })] }))] }), layout === 'grid' && (jsxRuntime.jsxs("div", { style: styles.gridContainer, children: [(mode === 'light' || mode === 'both') && (jsxRuntime.jsxs("div", { style: styles.section, children: [jsxRuntime.jsx("h4", { style: styles.sectionTitle, children: t('iconPreview.lightMode', 'Light Mode') }), jsxRuntime.jsx("div", { style: styles.sizeGrid, children: sizes.map((s) => (jsxRuntime.jsxs("div", { style: styles.sizeItem, children: [renderIcon(s.size, 'light', fill), jsxRuntime.jsx("span", { style: styles.sizeLabel, children: s.label })] }, s.size))) })] })), (mode === 'dark' || mode === 'both') && (jsxRuntime.jsxs("div", { style: styles.section, children: [jsxRuntime.jsx("h4", { style: styles.sectionTitle, children: t('iconPreview.darkMode', 'Dark Mode') }), jsxRuntime.jsx("div", { style: styles.sizeGrid, children: sizes.map((s) => (jsxRuntime.jsxs("div", { style: styles.sizeItem, children: [renderIcon(s.size, 'dark', fill), jsxRuntime.jsx("span", { style: styles.sizeLabel, children: s.label })] }, s.size))) })] })), mode === 'gradient' && (jsxRuntime.jsxs("div", { style: styles.section, children: [jsxRuntime.jsx("h4", { style: styles.sectionTitle, children: t('iconPreview.gradientBackground', 'Gradient Background') }), jsxRuntime.jsx("div", { style: styles.sizeGrid, children: sizes.map((s) => (jsxRuntime.jsxs("div", { style: styles.sizeItem, children: [renderIcon(s.size, 'gradient', '#ffffff'), jsxRuntime.jsx("span", { style: styles.sizeLabel, children: s.label })] }, s.size))) })] }))] })), layout === 'list' && (jsxRuntime.jsx("div", { style: styles.listContainer, children: sizes.map((s) => (jsxRuntime.jsxs("div", { style: styles.listRow, children: [jsxRuntime.jsx("span", { style: styles.listLabel, children: s.label }), renderIcon(s.size, 'light', fill), renderIcon(s.size, 'dark', fill)] }, s.size))) })), layout === 'comparison' && (jsxRuntime.jsxs("div", { style: styles.comparisonContainer, children: [jsxRuntime.jsxs("div", { style: styles.comparisonSide, children: [jsxRuntime.jsx("h4", { style: styles.sectionTitle, children: t('iconPreview.light', 'Light') }), jsxRuntime.jsx("div", { style: { ...styles.comparisonBox, background: BACKGROUND_COLORS.light }, children: jsxRuntime.jsx(Icon, { size: 96, fill: fill || '#1e1e1e', className: selectedAnimation || undefined }) })] }), jsxRuntime.jsxs("div", { style: styles.comparisonSide, children: [jsxRuntime.jsx("h4", { style: styles.sectionTitle, children: t('iconPreview.dark', 'Dark') }), jsxRuntime.jsx("div", { style: { ...styles.comparisonBox, background: BACKGROUND_COLORS.dark }, children: jsxRuntime.jsx(Icon, { size: 96, fill: fill || '#ffffff', className: selectedAnimation || undefined }) })] })] }))] }));
|
|
81
83
|
}
|
|
82
84
|
/* ══════════════════════════════════════════════════════════════════════════════
|
|
83
85
|
Styles
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var jsxRuntime = require('react/jsx-runtime');
|
|
4
|
+
var uiCore = require('@nice2dev/ui-core');
|
|
4
5
|
var React = require('react');
|
|
5
6
|
var actions = require('./icons/actions.js');
|
|
6
7
|
|
|
@@ -10,6 +11,7 @@ var actions = require('./icons/actions.js');
|
|
|
10
11
|
const DEFAULT_CONFIG = {
|
|
11
12
|
name: 'MyIcons',
|
|
12
13
|
packageName: '@myproject/icons',
|
|
14
|
+
// audit-allow: i18n — the generated package.json's default description, not UI copy
|
|
13
15
|
description: 'Custom icon set',
|
|
14
16
|
version: '1.0.0',
|
|
15
17
|
author: '',
|
|
@@ -287,6 +289,7 @@ function useIconSetCreator(options) {
|
|
|
287
289
|
* Icon Set Creator component for building custom icon packages.
|
|
288
290
|
*/
|
|
289
291
|
function NiceIconSetCreator({ icons, initialSelection, defaultConfig, onSelectionChange: _onSelectionChange, onExport, className = '', }) {
|
|
292
|
+
const { t } = uiCore.useNiceTranslation();
|
|
290
293
|
const creator = useIconSetCreator({ icons, initialSelection, defaultConfig });
|
|
291
294
|
const [searchQuery, setSearchQuery] = React.useState('');
|
|
292
295
|
const [categoryFilter, setCategoryFilter] = React.useState('all');
|
|
@@ -340,7 +343,9 @@ function NiceIconSetCreator({ icons, initialSelection, defaultConfig, onSelectio
|
|
|
340
343
|
URL.revokeObjectURL(url);
|
|
341
344
|
});
|
|
342
345
|
}, [creator]);
|
|
343
|
-
return (jsxRuntime.jsxs("div", { className: `nice-icon-set-creator ${className}`, style: styles.container, children: [jsxRuntime.jsxs("div", { style: styles.header, children: [jsxRuntime.jsx("h2", { style: styles.title, children:
|
|
346
|
+
return (jsxRuntime.jsxs("div", { className: `nice-icon-set-creator ${className}`, style: styles.container, children: [jsxRuntime.jsxs("div", { style: styles.header, children: [jsxRuntime.jsx("h2", { style: styles.title, children: t('iconSetCreator.iconSetCreator', 'Icon Set Creator') }), jsxRuntime.jsx("div", { style: styles.stats, children: t('iconSetCreator.iconsSelected', '{selectedCount} / {items} icons selected')
|
|
347
|
+
.replace('{selectedCount}', String(creator.selectedCount))
|
|
348
|
+
.replace('{items}', String(creator.items.length)) })] }), jsxRuntime.jsxs("div", { style: styles.content, children: [jsxRuntime.jsxs("div", { style: styles.configPanel, children: [jsxRuntime.jsx("h3", { style: styles.sectionTitle, children: t('iconSetCreator.configuration', 'Configuration') }), jsxRuntime.jsxs("div", { style: styles.field, children: [jsxRuntime.jsx("label", { style: styles.fieldLabel, children: t('iconSetCreator.setName', 'Set Name') }), jsxRuntime.jsx("input", { type: "text", value: creator.config.name, onChange: (e) => creator.updateConfig({ name: e.target.value }), style: styles.input, placeholder: "MyIcons" })] }), jsxRuntime.jsxs("div", { style: styles.field, children: [jsxRuntime.jsx("label", { style: styles.fieldLabel, children: t('iconSetCreator.packageName', 'Package Name') }), jsxRuntime.jsx("input", { type: "text", value: creator.config.packageName, onChange: (e) => creator.updateConfig({ packageName: e.target.value }), style: styles.input, placeholder: "@myproject/icons" })] }), jsxRuntime.jsxs("div", { style: styles.field, children: [jsxRuntime.jsx("label", { style: styles.fieldLabel, children: t('iconSetCreator.description', 'Description') }), jsxRuntime.jsx("input", { type: "text", value: creator.config.description, onChange: (e) => creator.updateConfig({ description: e.target.value }), style: styles.input })] }), jsxRuntime.jsxs("div", { style: styles.fieldRow, children: [jsxRuntime.jsxs("div", { style: styles.field, children: [jsxRuntime.jsx("label", { style: styles.fieldLabel, children: t('iconSetCreator.version', 'Version') }), jsxRuntime.jsx("input", { type: "text", value: creator.config.version, onChange: (e) => creator.updateConfig({ version: e.target.value }), style: styles.input })] }), jsxRuntime.jsxs("div", { style: styles.field, children: [jsxRuntime.jsx("label", { style: styles.fieldLabel, children: t('iconSetCreator.license', 'License') }), jsxRuntime.jsx("input", { type: "text", value: creator.config.license, onChange: (e) => creator.updateConfig({ license: e.target.value }), style: styles.input })] })] }), jsxRuntime.jsxs("div", { style: styles.field, children: [jsxRuntime.jsx("label", { style: styles.fieldLabel, children: t('iconSetCreator.exportFormat', 'Export Format') }), jsxRuntime.jsxs("select", { value: creator.config.format, onChange: (e) => creator.updateConfig({ format: e.target.value }), style: styles.select, children: [jsxRuntime.jsx("option", { value: "esm", children: t('iconSetCreator.esModules', 'ES Modules') }), jsxRuntime.jsx("option", { value: "cjs", children: "CommonJS" }), jsxRuntime.jsx("option", { value: "both", children: t('iconSetCreator.both', 'Both') })] })] }), jsxRuntime.jsxs("div", { style: styles.checkboxGroup, children: [jsxRuntime.jsxs("label", { style: styles.checkbox, children: [jsxRuntime.jsx("input", { type: "checkbox", checked: creator.config.generateTypes, onChange: (e) => creator.updateConfig({ generateTypes: e.target.checked }) }), t('iconSetCreator.generateTypescriptTypes', 'Generate TypeScript types')] }), jsxRuntime.jsxs("label", { style: styles.checkbox, children: [jsxRuntime.jsx("input", { type: "checkbox", checked: creator.config.treeShaking, onChange: (e) => creator.updateConfig({ treeShaking: e.target.checked }) }), t('iconSetCreator.treeShakingFriendly', 'Tree-shaking friendly')] }), jsxRuntime.jsxs("label", { style: styles.checkbox, children: [jsxRuntime.jsx("input", { type: "checkbox", checked: creator.config.includeMetadata, onChange: (e) => creator.updateConfig({ includeMetadata: e.target.checked }) }), t('iconSetCreator.includeMetadata', 'Include metadata')] })] }), jsxRuntime.jsxs("div", { style: styles.exportActions, children: [jsxRuntime.jsx("button", { onClick: handleExport, style: styles.primaryButton, children: t('iconSetCreator.previewExport', 'Preview Export') }), jsxRuntime.jsx("button", { onClick: handleDownloadZip, style: styles.secondaryButton, children: t('iconSetCreator.downloadFiles', 'Download Files') })] })] }), jsxRuntime.jsxs("div", { style: styles.selectionPanel, children: [jsxRuntime.jsxs("div", { style: styles.selectionHeader, children: [jsxRuntime.jsx("input", { type: "text", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), placeholder: t('iconSetCreator.searchIcons', 'Search icons...'), style: styles.searchInput }), jsxRuntime.jsxs("select", { value: categoryFilter, onChange: (e) => setCategoryFilter(e.target.value), style: styles.categorySelect, children: [jsxRuntime.jsx("option", { value: "all", children: t('iconSetCreator.allCategories', 'All Categories') }), categories.map((cat) => (jsxRuntime.jsx("option", { value: cat, children: cat }, cat)))] })] }), jsxRuntime.jsxs("div", { style: styles.selectionActions, children: [jsxRuntime.jsx("button", { onClick: creator.selectAll, style: styles.smallButton, children: t('iconSetCreator.selectAll', 'Select All') }), jsxRuntime.jsx("button", { onClick: creator.deselectAll, style: styles.smallButton, children: t('iconSetCreator.deselectAll', 'Deselect All') })] }), jsxRuntime.jsx("div", { style: styles.iconGrid, children: filteredItems.map((item) => (jsxRuntime.jsxs("div", { role: "checkbox", "aria-checked": item.included, "aria-label": item.customName, tabIndex: 0, onClick: () => creator.toggleIcon(item.originalName), onKeyDown: (e) => {
|
|
344
349
|
if (e.key === 'Enter' || e.key === ' ') {
|
|
345
350
|
e.preventDefault();
|
|
346
351
|
creator.toggleIcon(item.originalName);
|
|
@@ -349,7 +354,7 @@ function NiceIconSetCreator({ icons, initialSelection, defaultConfig, onSelectio
|
|
|
349
354
|
...styles.iconItem,
|
|
350
355
|
borderColor: item.included ? '#3b82f6' : '#e5e7eb',
|
|
351
356
|
backgroundColor: item.included ? '#eff6ff' : '#fff',
|
|
352
|
-
}, children: [jsxRuntime.jsx(item.icon, { size: 32 }), jsxRuntime.jsx("span", { style: styles.iconName, children: item.customName }), item.included && (jsxRuntime.jsx("span", { style: styles.checkmark, children: jsxRuntime.jsx(actions.NtdCheck, { size: 14 }) }))] }, item.originalName))) })] }), previewOutput && (jsxRuntime.jsxs("div", { style: styles.previewPanel, children: [jsxRuntime.jsx("h3", { style: styles.sectionTitle, children:
|
|
357
|
+
}, children: [jsxRuntime.jsx(item.icon, { size: 32 }), jsxRuntime.jsx("span", { style: styles.iconName, children: item.customName }), item.included && (jsxRuntime.jsx("span", { style: styles.checkmark, children: jsxRuntime.jsx(actions.NtdCheck, { size: 14 }) }))] }, item.originalName))) })] }), previewOutput && (jsxRuntime.jsxs("div", { style: styles.previewPanel, children: [jsxRuntime.jsx("h3", { style: styles.sectionTitle, children: t('iconSetCreator.exportPreview', 'Export Preview') }), previewOutput.map((file) => (jsxRuntime.jsxs("div", { style: styles.filePreview, children: [jsxRuntime.jsx("div", { style: styles.fileName, children: file.filename }), jsxRuntime.jsxs("pre", { style: styles.codePreview, children: [file.content.slice(0, 1000), file.content.length > 1000 && '...'] })] }, file.filename)))] }))] })] }));
|
|
353
358
|
}
|
|
354
359
|
/* ══════════════════════════════════════════════════════════════════════════════
|
|
355
360
|
Styles
|
package/dist/cjs/NtdIconBadge.js
CHANGED
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var jsxRuntime = require('react/jsx-runtime');
|
|
4
|
+
var uiCore = require('@nice2dev/ui-core');
|
|
4
5
|
var React = require('react');
|
|
5
6
|
var types = require('./types.js');
|
|
6
7
|
|
|
8
|
+
/** The status in words for the accessible name — English defaults of `ntdIconBadge.statusValue.*`. */
|
|
9
|
+
const STATUS_WORDS = {
|
|
10
|
+
online: 'online',
|
|
11
|
+
offline: 'offline',
|
|
12
|
+
busy: 'busy',
|
|
13
|
+
away: 'away',
|
|
14
|
+
dnd: 'do not disturb',
|
|
15
|
+
invisible: 'invisible',
|
|
16
|
+
};
|
|
7
17
|
/* ────────────────────────────────────────────────────────────────────────────
|
|
8
18
|
CONSTANTS
|
|
9
19
|
───────────────────────────────────────────────────────────────────────────── */
|
|
@@ -190,6 +200,7 @@ function injectStyles() {
|
|
|
190
200
|
const NtdBadge = /* @__PURE__ */ Object.assign(
|
|
191
201
|
/* @__PURE__ */ React.forwardRef(({ icon: Icon, count = 0, maxCount = 99, showZero = false, position = 'top-right', variant = 'standard', badgeColor = '#ef4444', textColor = '#ffffff', animated = true, pulse = false, iconProps = {}, className, style, 'aria-label': ariaLabel, }, ref) => {
|
|
192
202
|
var _a, _b;
|
|
203
|
+
const { t } = uiCore.useNiceTranslation();
|
|
193
204
|
const [isEntering, setIsEntering] = React.useState(false);
|
|
194
205
|
const [countChanging, setCountChanging] = React.useState(false);
|
|
195
206
|
const prevCountRef = React.useRef(count);
|
|
@@ -213,6 +224,7 @@ const NtdBadge = /* @__PURE__ */ Object.assign(
|
|
|
213
224
|
const timer = setTimeout(() => setIsEntering(false), 300);
|
|
214
225
|
return () => clearTimeout(timer);
|
|
215
226
|
}
|
|
227
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps -- the enter animation plays once, when the badge mounts
|
|
216
228
|
}, []);
|
|
217
229
|
const shouldShowBadge = count > 0 || showZero;
|
|
218
230
|
const displayCount = count > maxCount ? `${maxCount}+` : count.toString();
|
|
@@ -233,7 +245,9 @@ const NtdBadge = /* @__PURE__ */ Object.assign(
|
|
|
233
245
|
width: resolvedSize,
|
|
234
246
|
height: resolvedSize,
|
|
235
247
|
...style,
|
|
236
|
-
}, "aria-label": ariaLabel !== null && ariaLabel !== void 0 ? ariaLabel : (shouldShowBadge
|
|
248
|
+
}, "aria-label": ariaLabel !== null && ariaLabel !== void 0 ? ariaLabel : (shouldShowBadge
|
|
249
|
+
? t('ntdIconBadge.notifications', '{count} notifications').replace('{count}', String(count))
|
|
250
|
+
: undefined), children: [jsxRuntime.jsx(Icon, { ...iconProps }), shouldShowBadge && (jsxRuntime.jsx("span", { className: badgeClasses, style: {
|
|
237
251
|
...POSITION_STYLES[position],
|
|
238
252
|
backgroundColor: badgeColor,
|
|
239
253
|
color: textColor,
|
|
@@ -259,7 +273,8 @@ const NtdBadge = /* @__PURE__ */ Object.assign(
|
|
|
259
273
|
*/
|
|
260
274
|
const NtdStatusDot = /* @__PURE__ */ Object.assign(
|
|
261
275
|
/* @__PURE__ */ React.forwardRef(({ icon: Icon, status, statusColor, position = 'bottom-right', dotSize = 'md', animated = true, pulse = false, iconProps = {}, className, style, }, ref) => {
|
|
262
|
-
var _a, _b;
|
|
276
|
+
var _a, _b, _c;
|
|
277
|
+
const { t } = uiCore.useNiceTranslation();
|
|
263
278
|
React.useEffect(() => {
|
|
264
279
|
injectStyles();
|
|
265
280
|
}, []);
|
|
@@ -279,7 +294,7 @@ const NtdStatusDot = /* @__PURE__ */ Object.assign(
|
|
|
279
294
|
width: resolvedIconSize,
|
|
280
295
|
height: resolvedIconSize,
|
|
281
296
|
...style,
|
|
282
|
-
}, role: "status", "aria-label":
|
|
297
|
+
}, role: "status", "aria-label": t('ntdIconBadge.status', 'Status: {status}').replace('{status}', t('ntdIconBadge.statusValue.' + status, (_c = STATUS_WORDS[status]) !== null && _c !== void 0 ? _c : status)), children: [jsxRuntime.jsx(Icon, { ...iconProps }), jsxRuntime.jsx("span", { className: dotClasses, style: {
|
|
283
298
|
...POSITION_STYLES[position],
|
|
284
299
|
width: resolvedDotSize,
|
|
285
300
|
height: resolvedDotSize,
|
package/dist/cjs/NtdIconMorph.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var jsxRuntime = require('react/jsx-runtime');
|
|
4
|
+
var uiCore = require('@nice2dev/ui-core');
|
|
4
5
|
var React = require('react');
|
|
5
6
|
var actions = require('./icons/actions.js');
|
|
6
7
|
var media = require('./icons/media.js');
|
|
@@ -264,6 +265,7 @@ const NtdIconMorph = /* @__PURE__ */ Object.assign(
|
|
|
264
265
|
const NtdPathMorph = /* @__PURE__ */ Object.assign(
|
|
265
266
|
/* @__PURE__ */ React.forwardRef(({ pair, morphed: controlledMorphed, trigger = 'none', duration = 300, easing = 'ease-in-out', size = 'md', color = 'currentColor', strokeWidth = 2, filled = false, onMorphStart, onMorphEnd, className, style, 'aria-label': ariaLabel, }, ref) => {
|
|
266
267
|
var _a, _b;
|
|
268
|
+
const { t } = uiCore.useNiceTranslation();
|
|
267
269
|
const [internalMorphed, setInternalMorphed] = React.useState(false);
|
|
268
270
|
const morphed = controlledMorphed !== null && controlledMorphed !== void 0 ? controlledMorphed : internalMorphed;
|
|
269
271
|
const [isAnimating, setIsAnimating] = React.useState(false);
|
|
@@ -315,7 +317,7 @@ const NtdPathMorph = /* @__PURE__ */ Object.assign(
|
|
|
315
317
|
return (jsxRuntime.jsx("svg", { ref: ref, xmlns: "http://www.w3.org/2000/svg", viewBox: pathData.viewBox, width: resolvedSize, height: resolvedSize, className: `ntd-path-morph ${className !== null && className !== void 0 ? className : ''}`, style: {
|
|
316
318
|
cursor: trigger !== 'none' ? 'pointer' : undefined,
|
|
317
319
|
...style,
|
|
318
|
-
}, onClick: handleClick, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, role: trigger !== 'none' ? 'button' : 'img', "aria-label": ariaLabel !== null && ariaLabel !== void 0 ? ariaLabel :
|
|
320
|
+
}, onClick: handleClick, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, role: trigger !== 'none' ? 'button' : 'img', "aria-label": ariaLabel !== null && ariaLabel !== void 0 ? ariaLabel : t('ntdIconMorph.icon', '{pair} icon').replace('{pair}', String(pair)), "aria-pressed": trigger !== 'none' ? morphed : undefined, children: jsxRuntime.jsx("path", { d: currentPath, fill: filled ? color : 'none', stroke: filled ? 'none' : color, strokeWidth: filled ? 0 : strokeWidth, strokeLinecap: "round", strokeLinejoin: "round", style: {
|
|
319
321
|
transition: `d ${duration}ms ${resolvedEasing}, fill ${duration}ms ${resolvedEasing}, stroke ${duration}ms ${resolvedEasing}`,
|
|
320
322
|
} }) }));
|
|
321
323
|
}), { displayName: 'NtdPathMorph' });
|
package/dist/cjs/createIcon.js
CHANGED
|
@@ -85,7 +85,11 @@ function renderGradientDef(id, preset, type, angle, animation) {
|
|
|
85
85
|
}
|
|
86
86
|
/* ── 3D filter helper ───────────────────────────────── */
|
|
87
87
|
function render3dFilterDef(filterId) {
|
|
88
|
-
return (
|
|
88
|
+
return (
|
|
89
|
+
// The region is the icon's box plus a margin for the shadow, not a share of the shape's
|
|
90
|
+
// bounding box: a lone horizontal or vertical line (NtdMinus) has a zero-height box, and a
|
|
91
|
+
// filter region of 140% of zero rendered nothing.
|
|
92
|
+
jsxRuntime.jsxs("filter", { id: filterId, filterUnits: "userSpaceOnUse", x: "-4", y: "-4", width: "32", height: "32", children: [jsxRuntime.jsx("feDropShadow", { dx: "0", dy: "1.5", stdDeviation: "1", floodOpacity: "0.35" }), jsxRuntime.jsxs("feComponentTransfer", { in: "SourceGraphic", result: "highlight", children: [jsxRuntime.jsx("feFuncR", { type: "linear", slope: "1.2", intercept: "0.1" }), jsxRuntime.jsx("feFuncG", { type: "linear", slope: "1.2", intercept: "0.1" }), jsxRuntime.jsx("feFuncB", { type: "linear", slope: "1.2", intercept: "0.1" })] }), jsxRuntime.jsx("feComposite", { operator: "in", in: "highlight", in2: "SourceGraphic", result: "highlightMask" }), jsxRuntime.jsx("feMerge", { children: jsxRuntime.jsx("feMergeNode", { in: "SourceGraphic" }) })] }));
|
|
89
93
|
}
|
|
90
94
|
/* ── Cutout shape helper ──────────────────────────── */
|
|
91
95
|
function renderCutoutShape(shape, fill, maskId) {
|
|
@@ -116,6 +120,8 @@ function createIcon(options) {
|
|
|
116
120
|
var _a, _b, _c, _d, _e, _f, _g;
|
|
117
121
|
const resolvedSize = typeof size === 'number' ? size : ((_a = types.SIZE_MAP[size]) !== null && _a !== void 0 ? _a : 24);
|
|
118
122
|
const gradId = React.useId();
|
|
123
|
+
// the prefix of the ids of the clip paths the renderers draw (IconRenderProps.uid)
|
|
124
|
+
const uid = React.useId();
|
|
119
125
|
const center = { x: 12, y: 12 }; // Default center for 24x24 viewBox
|
|
120
126
|
// Use dark/light mode detection when both colors are specified
|
|
121
127
|
const colorScheme = useColorScheme();
|
|
@@ -144,6 +150,7 @@ function createIcon(options) {
|
|
|
144
150
|
strokeWidth: effectiveStrokeWidth,
|
|
145
151
|
strokeLinecap,
|
|
146
152
|
strokeLinejoin,
|
|
153
|
+
uid,
|
|
147
154
|
};
|
|
148
155
|
// Resolve renderer with fallback chain. `wireframe` has no authored layer
|
|
149
156
|
// at all — it is a mechanical re-draw of the full `solid` construction, so
|
|
@@ -221,10 +228,11 @@ function createIcon(options) {
|
|
|
221
228
|
variant: 'solid',
|
|
222
229
|
primaryColor: 'black',
|
|
223
230
|
secondaryColor: 'black',
|
|
224
|
-
accentColor: '
|
|
231
|
+
accentColor: 'white',
|
|
225
232
|
primaryOpacity: 1,
|
|
226
233
|
secondaryOpacity: 1,
|
|
227
234
|
accentOpacity: 1,
|
|
235
|
+
uid: `${uid}m`,
|
|
228
236
|
}) })] }) })), smilAnimation ? (needsSmilScaleWrapper ? (jsxRuntime.jsxs("g", { transform: `translate(${center.x}, ${center.y})`, filter: is3d ? `url(#${filterId})` : undefined, children: [jsxRuntime.jsx("g", { transform: `translate(-${center.x}, -${center.y})`, children: iconContent }), smilAnimationElements] })) : (jsxRuntime.jsxs("g", { filter: is3d ? `url(#${filterId})` : undefined, children: [iconContent, smilAnimationElements] }))) : is3d ? (jsxRuntime.jsx("g", { filter: `url(#${filterId})`, children: iconContent })) : (iconContent)] }));
|
|
229
237
|
});
|
|
230
238
|
Icon.displayName = displayName;
|
|
@@ -29,6 +29,8 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
29
29
|
* @version 1.0.0
|
|
30
30
|
* @since 2026-03
|
|
31
31
|
* @license MIT
|
|
32
|
+
*
|
|
33
|
+
* audit-allow: i18n-file — token descriptions written into Style Dictionary output, not UI copy.
|
|
32
34
|
*/
|
|
33
35
|
/* ────────────────────────────────────────────────────────────────────────────
|
|
34
36
|
DEFAULT TOKEN VALUES
|
package/dist/cjs/iconMetadata.js
CHANGED
|
@@ -17,6 +17,8 @@
|
|
|
17
17
|
/* ────────────────────────────────────────────────────────────────────────────
|
|
18
18
|
CATEGORY METADATA
|
|
19
19
|
───────────────────────────────────────────────────────────────────────────── */
|
|
20
|
+
// audit-allow: i18n — English data for the host's icon browser and searchIcons(); no control of
|
|
21
|
+
// this package shows it.
|
|
20
22
|
const CATEGORY_INFO = {
|
|
21
23
|
actions: {
|
|
22
24
|
label: 'Actions',
|
|
@@ -364,6 +366,7 @@ function getSearchSuggestions(partialQuery, limit = 5) {
|
|
|
364
366
|
This will be populated when icons are imported
|
|
365
367
|
───────────────────────────────────────────────────────────────────────────── */
|
|
366
368
|
// Example metadata definitions (actual ones will be in icon files)
|
|
369
|
+
// audit-allow: i18n — English search metadata: searchIcons() matches these words.
|
|
367
370
|
const UI_ICON_METADATA = {
|
|
368
371
|
NtdHome: {
|
|
369
372
|
category: 'ui',
|
|
@@ -430,6 +433,7 @@ const UI_ICON_METADATA = {
|
|
|
430
433
|
related: ['NtdBellOff', 'NtdAlarm'],
|
|
431
434
|
},
|
|
432
435
|
};
|
|
436
|
+
// audit-allow: i18n — English search metadata: searchIcons() matches these words.
|
|
433
437
|
const ACTION_ICON_METADATA = {
|
|
434
438
|
NtdPlay: {
|
|
435
439
|
category: 'actions',
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
4
|
+
var createIcon = require('./createIcon.js');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* `content` drawn only inside `shape` — for accent lines that read as a cut through a shape (a
|
|
8
|
+
* pencil's band, a calendar's header line, an envelope's flap): clipped, they meet the shape's
|
|
9
|
+
* edges flush in every variant, where their round caps used to stick out of it (and `sharp` /
|
|
10
|
+
* `rounded` re-cap every stroke, so caps alone cannot do it).
|
|
11
|
+
*
|
|
12
|
+
* `uid` is the icon's `IconRenderProps.uid`; `name` tells the clips of one icon apart. Without a
|
|
13
|
+
* `uid` — a renderer called outside createIcon — the content is drawn as it is.
|
|
14
|
+
*/
|
|
15
|
+
function within(uid, name, shape, content) {
|
|
16
|
+
if (!uid) {
|
|
17
|
+
return content;
|
|
18
|
+
}
|
|
19
|
+
const id = `${uid}${name}`;
|
|
20
|
+
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("clipPath", { id: id, children: shape }), jsxRuntime.jsx("g", { clipPath: `url(#${id})`, children: content })] }));
|
|
21
|
+
}
|
|
22
|
+
/** The page of every paper icon: 16 × 20, its top-right corner folded over 8. */
|
|
23
|
+
const PAPER = 'M12 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V10z';
|
|
24
|
+
const PAPER_FOLD = 'M12 2v6a2 2 0 002 2h6z';
|
|
25
|
+
const PAPER_FOLD_LINE = 'M12 2v8h8';
|
|
26
|
+
const PAPER_SHARP = 'M12 2H4v20h16V10z';
|
|
27
|
+
const PAPER_FOLD_SHARP = 'M12 2v8h8z';
|
|
28
|
+
/**
|
|
29
|
+
* A paper icon — NtdFile, and every document drawn on the same page (NtdFileText, NtdInvoice,
|
|
30
|
+
* NtdContract, …), so their pages and folds match. The fold is secondary when the page is filled;
|
|
31
|
+
* `text` is the document's writing, a path of strokes kept below the fold (y ≥ 11, x 7.5–16.5),
|
|
32
|
+
* drawn in the accent colour on a filled page and in primary on a stroked one.
|
|
33
|
+
*/
|
|
34
|
+
function paperIcon(displayName, text = '') {
|
|
35
|
+
return createIcon.createIcon({
|
|
36
|
+
displayName,
|
|
37
|
+
paths: {
|
|
38
|
+
solid: ({ primaryColor, secondaryColor, accentColor, accentOpacity }) => (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("path", { d: PAPER, fill: primaryColor }), jsxRuntime.jsx("path", { d: PAPER_FOLD, fill: secondaryColor }), text && (jsxRuntime.jsx("path", { d: text, stroke: accentColor, strokeOpacity: accentOpacity, strokeWidth: "1.5", strokeLinecap: "round", fill: "none" }))] })),
|
|
39
|
+
// square at every corner, the fold included — the derived `sharp` keeps a path's arcs round
|
|
40
|
+
sharp: ({ primaryColor, secondaryColor, accentColor, accentOpacity }) => (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("path", { d: PAPER_SHARP, fill: primaryColor }), jsxRuntime.jsx("path", { d: PAPER_FOLD_SHARP, fill: secondaryColor }), text && (jsxRuntime.jsx("path", { d: text, stroke: accentColor, strokeOpacity: accentOpacity, strokeWidth: "1.5", fill: "none" }))] })),
|
|
41
|
+
outline: ({ primaryColor, strokeWidth }) => (jsxRuntime.jsx("path", { d: `${PAPER}${PAPER_FOLD_LINE}${text}`, stroke: primaryColor, strokeWidth: strokeWidth, fill: "none" })),
|
|
42
|
+
duotone: ({ primaryColor, secondaryColor, secondaryOpacity, strokeWidth }) => (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("path", { d: PAPER, fill: secondaryColor, opacity: secondaryOpacity }), jsxRuntime.jsx("path", { d: PAPER_FOLD_SHARP, fill: primaryColor, opacity: secondaryOpacity + 0.1 }), jsxRuntime.jsx("path", { d: `${PAPER}${PAPER_FOLD_LINE}${text}`, stroke: primaryColor, strokeWidth: strokeWidth, fill: "none" })] })),
|
|
43
|
+
vivid: ({ primaryColor, secondaryColor }) => (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("path", { d: PAPER, fill: primaryColor }), jsxRuntime.jsx("path", { d: PAPER, fill: "white", opacity: "0.1" }), jsxRuntime.jsx("path", { d: PAPER_FOLD, fill: secondaryColor }), text && (jsxRuntime.jsx("path", { d: text, stroke: "white", strokeOpacity: "0.9", strokeWidth: "1.5", strokeLinecap: "round", fill: "none" }))] })),
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
exports.paperIcon = paperIcon;
|
|
49
|
+
exports.within = within;
|
|
@@ -15,13 +15,21 @@ const NtdAccessibility = /* @__PURE__ */ createIcon.createIcon({
|
|
|
15
15
|
vivid: ({ primaryColor, secondaryColor }) => (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("circle", { cx: "12", cy: "4", r: "2", fill: primaryColor }), jsxRuntime.jsx("circle", { cx: "12", cy: "4", r: "2", fill: "white", opacity: "0.08" }), jsxRuntime.jsx("path", { d: "M12 8c-4 0-6 1-6 2v2h3v8h2v-5h2v5h2v-8h3v-2c0-1-2-2-6-2z", fill: secondaryColor })] })),
|
|
16
16
|
},
|
|
17
17
|
});
|
|
18
|
+
// NtdWheelchair: the rider faces right, leaning back a little (it faced left and sat bolt
|
|
19
|
+
// upright), the arm forward on the rest, the leg out to the footrest, in a wheel that is a full
|
|
20
|
+
// 210° arc around the seat (it was a small ring under it).
|
|
21
|
+
const WC_TORSO = 'M8.2 7.4l2.2 6';
|
|
22
|
+
const WC_ARM = 'M9 9.4h5.6';
|
|
23
|
+
const WC_LEG = 'M10.4 13.4h5l2.5 6.4H21';
|
|
24
|
+
const WC_WHEEL = 'M5.6 10.95A5.6 5.6 0 1 0 13.25 18.6';
|
|
18
25
|
const NtdWheelchair = /* @__PURE__ */ createIcon.createIcon({
|
|
19
26
|
displayName: 'NtdWheelchair',
|
|
20
27
|
paths: {
|
|
21
|
-
solid: ({ primaryColor }) => (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("circle", { cx: "
|
|
22
|
-
outline: ({ primaryColor, strokeWidth }) => (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("circle", { cx: "
|
|
23
|
-
|
|
24
|
-
|
|
28
|
+
solid: ({ primaryColor, strokeWidth }) => (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("circle", { cx: "8.6", cy: "3.4", r: "2.2", fill: primaryColor }), jsxRuntime.jsxs("g", { stroke: primaryColor, fill: "none", children: [jsxRuntime.jsx("path", { d: WC_TORSO, strokeWidth: strokeWidth * 1.4 }), jsxRuntime.jsx("path", { d: WC_ARM, strokeWidth: strokeWidth * 1.1 }), jsxRuntime.jsx("path", { d: WC_LEG, strokeWidth: strokeWidth * 1.3 }), jsxRuntime.jsx("path", { d: WC_WHEEL, strokeWidth: strokeWidth * 1.1 })] })] })),
|
|
29
|
+
outline: ({ primaryColor, strokeWidth }) => (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("circle", { cx: "8.6", cy: "3.4", r: "1.8", stroke: primaryColor, strokeWidth: strokeWidth }), jsxRuntime.jsx("path", { d: `${WC_TORSO}${WC_ARM}${WC_LEG}${WC_WHEEL}`, stroke: primaryColor, strokeWidth: strokeWidth, fill: "none" })] })),
|
|
30
|
+
// fills only: the rider primary, the wheel secondary
|
|
31
|
+
duotone: ({ primaryColor, secondaryColor, strokeWidth }) => (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("path", { d: WC_WHEEL, stroke: secondaryColor, strokeWidth: strokeWidth * 1.1, fill: "none" }), jsxRuntime.jsx("circle", { cx: "8.6", cy: "3.4", r: "2.2", fill: primaryColor }), jsxRuntime.jsxs("g", { stroke: primaryColor, fill: "none", children: [jsxRuntime.jsx("path", { d: WC_TORSO, strokeWidth: strokeWidth * 1.4 }), jsxRuntime.jsx("path", { d: WC_ARM, strokeWidth: strokeWidth * 1.1 }), jsxRuntime.jsx("path", { d: WC_LEG, strokeWidth: strokeWidth * 1.3 })] })] })),
|
|
32
|
+
vivid: ({ primaryColor, secondaryColor, strokeWidth }) => (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("path", { d: WC_WHEEL, stroke: secondaryColor, strokeWidth: strokeWidth * 1.1, fill: "none" }), jsxRuntime.jsx("circle", { cx: "8.6", cy: "3.4", r: "2.2", fill: primaryColor }), jsxRuntime.jsxs("g", { stroke: primaryColor, fill: "none", children: [jsxRuntime.jsx("path", { d: WC_TORSO, strokeWidth: strokeWidth * 1.4 }), jsxRuntime.jsx("path", { d: WC_ARM, strokeWidth: strokeWidth * 1.1 }), jsxRuntime.jsx("path", { d: WC_LEG, strokeWidth: strokeWidth * 1.3 })] })] })),
|
|
25
33
|
},
|
|
26
34
|
});
|
|
27
35
|
const NtdBlind = /* @__PURE__ */ createIcon.createIcon({
|