@nice2dev/icons 1.3.3 → 1.3.5

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 (60) hide show
  1. package/CHANGELOG.md +75 -0
  2. package/README.md +43 -4
  3. package/dist/cjs/NiceIconStacker.js +120 -0
  4. package/dist/cjs/client.js +3 -0
  5. package/dist/cjs/createIcon.js +25 -4
  6. package/dist/cjs/figmaExport.js +1 -0
  7. package/dist/cjs/iconParts.js +6 -5
  8. package/dist/cjs/icons/actions.js +36 -7
  9. package/dist/cjs/icons/ai.js +3 -0
  10. package/dist/cjs/icons/business.js +8 -7
  11. package/dist/cjs/icons/education.js +18 -14
  12. package/dist/cjs/icons/files.js +34 -15
  13. package/dist/cjs/icons/index.js +3 -2
  14. package/dist/cjs/icons/media.js +41 -15
  15. package/dist/cjs/icons/navigation.js +8 -6
  16. package/dist/cjs/icons/realestate.js +5 -2
  17. package/dist/cjs/icons/shapes.js +18 -14
  18. package/dist/cjs/icons/sustainability.js +2 -11
  19. package/dist/cjs/icons/travel.js +15 -14
  20. package/dist/cjs/icons/ui.js +47 -6
  21. package/dist/cjs/icons/weather.js +3 -0
  22. package/dist/cjs/index.js +6 -2
  23. package/dist/cjs/nicetodev-icons.css +1 -1
  24. package/dist/cjs/stacking.js +100 -0
  25. package/dist/esm/NiceIconStacker.js +117 -0
  26. package/dist/esm/client.js +1 -0
  27. package/dist/esm/createIcon.js +26 -5
  28. package/dist/esm/figmaExport.js +1 -0
  29. package/dist/esm/iconParts.js +6 -5
  30. package/dist/esm/icons/actions.js +36 -7
  31. package/dist/esm/icons/ai.js +3 -1
  32. package/dist/esm/icons/business.js +9 -8
  33. package/dist/esm/icons/education.js +18 -14
  34. package/dist/esm/icons/files.js +34 -15
  35. package/dist/esm/icons/index.js +5 -5
  36. package/dist/esm/icons/media.js +41 -15
  37. package/dist/esm/icons/navigation.js +8 -6
  38. package/dist/esm/icons/realestate.js +5 -2
  39. package/dist/esm/icons/shapes.js +19 -14
  40. package/dist/esm/icons/sustainability.js +2 -10
  41. package/dist/esm/icons/travel.js +15 -14
  42. package/dist/esm/icons/ui.js +47 -7
  43. package/dist/esm/icons/weather.js +3 -1
  44. package/dist/esm/index.js +6 -5
  45. package/dist/esm/nicetodev-icons.css +1 -1
  46. package/dist/esm/stacking.js +92 -0
  47. package/dist/types/NiceIconStacker.d.ts +50 -0
  48. package/dist/types/client.d.ts +2 -0
  49. package/dist/types/createIcon.d.ts +5 -0
  50. package/dist/types/iconParts.d.ts +2 -1
  51. package/dist/types/icons/ai.d.ts +2 -0
  52. package/dist/types/icons/index.d.ts +5 -5
  53. package/dist/types/icons/shapes.d.ts +0 -1
  54. package/dist/types/icons/sustainability.d.ts +0 -1
  55. package/dist/types/icons/ui.d.ts +1 -0
  56. package/dist/types/icons/weather.d.ts +2 -0
  57. package/dist/types/index.d.ts +3 -1
  58. package/dist/types/stacking.d.ts +41 -0
  59. package/dist/types/types.d.ts +31 -3
  60. package/package.json +2 -2
package/CHANGELOG.md CHANGED
@@ -1,5 +1,80 @@
1
1
  # @nice2dev/icons — Changelog
2
2
 
3
+ ## 1.3.5
4
+
5
+ ### Patch Changes
6
+
7
+ - 2b49cd7: Icon browser feedback, round 36:
8
+
9
+ - `NtdMonitor`: neck and base twice the stroke width in every variant but `outline`; the base
10
+ 30% wider. Vivid's stand is secondary (it had a fixed `#3B82F6`).
11
+ - `NtdPieChart`: the larger part is a proper three-quarter sector from the centre (it was a segment
12
+ cut off by a chord). The two sectors have a gap between them; duotone uses fills only.
13
+ - `NtdCopy`:
14
+ - the front, filled sheet at the top right;
15
+ - the back sheet's line stops 1 short of it at any stroke width;
16
+ - `sharp` draws the back sheet with square corners;
17
+ - duotone uses fills only.
18
+ - `NtdStar4` is now a deprecated alias of `NtdSparkle`, and `NtdHydropower` of `NtdDroplet`. Both
19
+ are duplicates; the old names still work.
20
+
21
+ - 16d420b: Stack icons by nesting them, and build stacks in NiceIconStacker:
22
+
23
+ - An icon draws its children in its own SVG:
24
+ `<NtdSquare><NtdStar stackAlign="center">1</NtdStar></NtdSquare>`.
25
+ - **Child icons** sit where `stackAlign` says: `center`, an edge or a corner. `stackSize` sets
26
+ their share of the parent (default 0.6 centred, 0.5 elsewhere).
27
+ - **Text children** are centred on the icon's visual centre. `NtdStar`, `NtdHeart`, `NtdShield`
28
+ and `NtdTriangle` have their own centre (`textCenter` in `createIcon`). `textColor` and
29
+ `textSize` override the text's colour and size.
30
+ - **Colours** follow the parent: a filled parent cuts centred and edge content out (the parent's
31
+ accent); a corner badge keeps the parent's colours on an accent-coloured gap. Colours given on
32
+ a child win.
33
+ - `NiceIconStacker`:
34
+ - lists the base icon and the icons stacked in it, each with its position, size and text;
35
+ - adds the `current` icon, removes one or clears the stack;
36
+ - previews the stack at three sizes and writes its JSX (`niceIconStackCode`).
37
+ - New translation keys `iconStacker.*` (en, pl, de, fr, es, uk).
38
+
39
+ ## 1.3.4
40
+
41
+ ### Patch Changes
42
+
43
+ - 15f3a18: Icon browser feedback, round 34:
44
+
45
+ - Twice the stroke width in every variant but `outline`:
46
+ - `NtdMusic`: the stems move left so they still end flush with the note heads.
47
+ - `NtdClock`: the hands.
48
+ - `NtdShare`: the links.
49
+ - `NtdRepeat`: shafts and heads, redrawn tighter so they stay in the box; the shafts stop
50
+ short of the heads' points, so nothing sticks out in `sharp`.
51
+ - `NtdRefresh`: smaller heads, each pointing at the start of the other arrow's tail with a
52
+ clear gap.
53
+ - `NtdWifi`: the inner arc sits further from the dot, the arcs closer together.
54
+ - `NtdList`: dots a quarter smaller.
55
+ - Colours:
56
+ - `NtdHeart`: vivid outlines the split heart in the accent colour instead of an accent seam.
57
+ - `NtdBellRing`: the ring marks are primary when filled, accent in vivid.
58
+ - `NtdBriefcase`: the clasp is in the accent colour with a primary lock square.
59
+ - `NtdSmartphone`: filled, a primary frame round an accent screen with a primary button.
60
+ - Shapes:
61
+ - `NtdBook`: a notch in the right edge beside the pages, leaving a narrow strip of cover.
62
+ - `NtdNotebook`: a wider, shorter cover.
63
+ - `NtdAward`: the ribbon's inside between the two earlier sizes.
64
+ - `NtdApartment`: a thin primary sill under the door when filled.
65
+ - `NtdBus`: a front view with a big windscreen, the driver, headlights and rectangular wheels.
66
+ - `NtdBrush`: redrawn as a paintbrush — handle, ferrule and pointed bristles.
67
+ - Hardcoded colours (`#3B82F6`, white) are gone from `NtdShare` and `NtdRepeat`.
68
+
69
+ - 4fae5a4: `NtdHourglass` and the `turn` animation:
70
+
71
+ - `NtdHourglass`: two plates and a curved glass, the sand running from the top bulb through a
72
+ thread onto a pile in the bottom one. `NiceIcon` resolves it as `hourglass`.
73
+ - `animation="turn"` works on every icon: it turns the icon upside down, holds, turns it back
74
+ and holds again. On `NtdHourglass` the sand pours during the holds, in step with the turns.
75
+ - `animationDuration` now also sets the `--ntd-anim-duration` CSS variable, so parts an icon
76
+ animates inside itself (the hourglass sand) keep the same duration.
77
+
3
78
  ## 1.3.3
4
79
 
5
80
  ### Patch Changes
package/README.md CHANGED
@@ -193,10 +193,49 @@ Apply an animation by name:
193
193
 
194
194
  Each animation maps to a CSS class `.ntd-anim-{name}` defined in `animations.css` (auto-imported).
195
195
 
196
+ `turn` turns any icon upside down, holds, turns it back and holds again. On `NtdHourglass` the
197
+ sand pours while it holds, in step with the turns at any `animationDuration`:
198
+
199
+ ```tsx
200
+ <NtdHourglass animation="turn" />
201
+ <NtdHourglass animation="turn" animationDuration="6s" />
202
+ ```
203
+
196
204
  See [Full Animation Catalog](#full-animation-catalog) for the complete list.
197
205
 
198
206
  ---
199
207
 
208
+ ### Stacking icons
209
+
210
+ Nest icons to stack them in one SVG. A child icon sits where its `stackAlign` says, and text is
211
+ centred on the icon:
212
+
213
+ ```tsx
214
+ <NtdSquare variant="solid">
215
+ <NtdStar stackAlign="center">1</NtdStar>
216
+ </NtdSquare>
217
+
218
+ <NtdFolder>
219
+ <NtdPlus stackAlign="top-right" />
220
+ </NtdFolder>
221
+
222
+ <NtdCircle>42</NtdCircle>
223
+ ```
224
+
225
+ - **`stackAlign`:** `center` (default), `top`, `bottom`, `left`, `right`, `top-left`,
226
+ `top-right`, `bottom-left`, `bottom-right`.
227
+ - **`stackSize`:** the child's share of its parent. Default `0.6` centred, `0.5` elsewhere.
228
+ - **Colours** follow the parent unless the child sets its own:
229
+ - In the centre or at an edge, a filled parent cuts the content out. The child takes the
230
+ parent's accent and its text the parent's primary. On `outline` / `wireframe` everything stays
231
+ in primary.
232
+ - A corner badge keeps the parent's colours and sits on an accent-coloured gap.
233
+ - **Text:** `textColor` and `textSize` override the text's colour and size.
234
+ - **Builder:** `NiceIconStacker` builds a stack interactively and writes the JSX
235
+ (`niceIconStackCode`).
236
+
237
+ ---
238
+
200
239
  ### Gradient Fills
201
240
 
202
241
  Apply gradient fills using the `gradient` prop with one of 12 built-in presets:
@@ -406,9 +445,9 @@ Properties passed to every path renderer (pre-resolved from user props):
406
445
 
407
446
  `NtdSearch` · `NtdEdit` · `NtdTrash` · `NtdSave` · `NtdDownload` · `NtdUpload` · `NtdShare` · `NtdCopy` · `NtdClipboard` · `NtdPlus` · `NtdMinus` · `NtdCheck` · `NtdFilter` · `NtdSort` · `NtdPrint` · `NtdZoomIn` · `NtdZoomOut` · `NtdUndo` · `NtdRedo` · `NtdLink` · `NtdSettings` · `NtdLogIn` · `NtdLogOut` · `NtdLock` · `NtdUnlock`
408
447
 
409
- ### UI & Status (26 icons)
448
+ ### UI & Status (27 icons)
410
449
 
411
- `NtdHome` · `NtdUser` · `NtdUsers` · `NtdHeart` · `NtdStar` · `NtdBell` · `NtdEye` · `NtdEyeOff` · `NtdInfo` · `NtdWarning` · `NtdError` · `NtdSuccess` · `NtdHelp` · `NtdClock` · `NtdCalendar` · `NtdTag` · `NtdBookmark` · `NtdFlag` · `NtdThumbUp` · `NtdThumbDown` · `NtdShield` · `NtdGlobe` · `NtdMap` · `NtdPin` · `NtdSun` · `NtdMoon`
450
+ `NtdHome` · `NtdUser` · `NtdUsers` · `NtdHeart` · `NtdStar` · `NtdBell` · `NtdEye` · `NtdEyeOff` · `NtdInfo` · `NtdWarning` · `NtdError` · `NtdSuccess` · `NtdHelp` · `NtdClock` · `NtdHourglass` · `NtdCalendar` · `NtdTag` · `NtdBookmark` · `NtdFlag` · `NtdThumbUp` · `NtdThumbDown` · `NtdShield` · `NtdGlobe` · `NtdMap` · `NtdPin` · `NtdSun` · `NtdMoon`
412
451
 
413
452
  ### Media & Communication (19 icons)
414
453
 
@@ -466,9 +505,9 @@ Properties passed to every path renderer (pre-resolved from user props):
466
505
 
467
506
  ## Full Animation Catalog
468
507
 
469
- ### Core Transforms (13)
508
+ ### Core Transforms (14)
470
509
 
471
- `spin` · `spin-reverse` · `pulse` · `bounce` · `shake` · `float` · `flip` · `glow` · `wiggle` · `rubber` · `swing` · `tada` · `heartbeat`
510
+ `spin` · `spin-reverse` · `pulse` · `bounce` · `shake` · `float` · `flip` · `glow` · `wiggle` · `rubber` · `swing` · `tada` · `heartbeat` · `turn`
472
511
 
473
512
  ### Entrance Animations (22)
474
513
 
@@ -0,0 +1,120 @@
1
+ 'use strict';
2
+
3
+ var jsxRuntime = require('react/jsx-runtime');
4
+ var uiCore = require('@nice2dev/ui-core');
5
+ var React = require('react');
6
+ var actions = require('./icons/actions.js');
7
+ var stacking = require('./stacking.js');
8
+
9
+ const ALIGNS = [
10
+ 'center',
11
+ 'top',
12
+ 'bottom',
13
+ 'left',
14
+ 'right',
15
+ 'top-left',
16
+ 'top-right',
17
+ 'bottom-left',
18
+ 'bottom-right',
19
+ ];
20
+ const PREVIEW_SIZES = [96, 48, 24];
21
+ const NO_LAYERS = [];
22
+ /** The stack as JSX, the base wrapping its layers. */
23
+ function niceIconStackCode(layers) {
24
+ const [base, ...rest] = layers;
25
+ if (!base) {
26
+ return '';
27
+ }
28
+ const baseAttrs = base.variant ? ` variant="${base.variant}"` : '';
29
+ if (rest.length === 0) {
30
+ return base.text
31
+ ? `<${base.icon}${baseAttrs}>${base.text}</${base.icon}>`
32
+ : `<${base.icon}${baseAttrs} />`;
33
+ }
34
+ const lines = rest.map((l) => {
35
+ var _a;
36
+ const attrs = [
37
+ `stackAlign="${(_a = l.align) !== null && _a !== void 0 ? _a : 'center'}"`,
38
+ l.size !== undefined ? `stackSize={${l.size}}` : '',
39
+ l.variant && l.variant !== base.variant ? `variant="${l.variant}"` : '',
40
+ ]
41
+ .filter(Boolean)
42
+ .join(' ');
43
+ return l.text ? ` <${l.icon} ${attrs}>${l.text}</${l.icon}>` : ` <${l.icon} ${attrs} />`;
44
+ });
45
+ return [
46
+ `<${base.icon}${baseAttrs}>`,
47
+ ...(base.text ? [` ${base.text}`] : []),
48
+ ...lines,
49
+ `</${base.icon}>`,
50
+ ].join('\n');
51
+ }
52
+ /**
53
+ * NiceIconStacker (1.3.5) — builds an icon stack: the base icon and the icons stacked in it, each
54
+ * with its position, size and text, shown live at three sizes and as the JSX that draws it. Icons
55
+ * come in through `current` (the "add" button) and leave one by one or all at once.
56
+ */
57
+ function NiceIconStacker({ icons, layers: layersProp, defaultLayers, onLayersChange, current, previewProps, className, }) {
58
+ var _a;
59
+ const { t } = uiCore.useNiceTranslation();
60
+ const [layers, setLayers] = uiCore.useControllableState({
61
+ value: layersProp,
62
+ defaultValue: defaultLayers,
63
+ fallbackValue: NO_LAYERS,
64
+ onChange: onLayersChange,
65
+ componentName: 'NiceIconStacker',
66
+ });
67
+ const nextId = React.useRef(0);
68
+ const [copied, setCopied] = React.useState(false);
69
+ const resolve = React.useCallback((name) => (typeof icons === 'function' ? icons(name) : icons[name]), [icons]);
70
+ const [base, ...rest] = layers;
71
+ const code = React.useMemo(() => niceIconStackCode(layers), [layers]);
72
+ const update = (id, patch) => setLayers((prev) => prev.map((l) => (l.id === id ? { ...l, ...patch } : l)));
73
+ const add = () => {
74
+ if (!current) {
75
+ return;
76
+ }
77
+ nextId.current += 1;
78
+ const layer = {
79
+ id: `${current.icon}-${nextId.current}`,
80
+ icon: current.icon,
81
+ variant: current.variant,
82
+ };
83
+ setLayers((prev) => [...prev, layer]);
84
+ };
85
+ const remove = (id) => setLayers((prev) => prev.filter((l) => l.id !== id));
86
+ const clear = () => setLayers(NO_LAYERS);
87
+ const copy = () => {
88
+ var _a;
89
+ void ((_a = navigator.clipboard) === null || _a === void 0 ? void 0 : _a.writeText(code).then(() => {
90
+ setCopied(true);
91
+ window.setTimeout(() => setCopied(false), 1500);
92
+ }));
93
+ };
94
+ const renderStack = (size) => {
95
+ var _a;
96
+ const Base = base ? resolve(base.icon) : undefined;
97
+ if (!base || !Base) {
98
+ return null;
99
+ }
100
+ return (jsxRuntime.jsxs(Base, { ...previewProps, variant: (_a = base.variant) !== null && _a !== void 0 ? _a : previewProps === null || previewProps === void 0 ? void 0 : previewProps.variant, size: size, title: base.icon, children: [base.text, rest.map((l) => {
101
+ var _a;
102
+ const Layer = resolve(l.icon);
103
+ return Layer ? (jsxRuntime.jsx(Layer, { stackAlign: (_a = l.align) !== null && _a !== void 0 ? _a : 'center', stackSize: l.size, variant: l.variant !== base.variant ? l.variant : undefined, children: l.text }, l.id)) : null;
104
+ })] }));
105
+ };
106
+ return (jsxRuntime.jsxs("div", { className: ['nice-icon-stacker', className].filter(Boolean).join(' '), children: [jsxRuntime.jsxs("div", { className: "nice-icon-stacker__toolbar", children: [jsxRuntime.jsxs("button", { type: "button", onClick: add, disabled: !current, children: [jsxRuntime.jsx(actions.NtdPlus, { size: 14 }), ' ', t('iconStacker.add', 'Add {icon} to the stack').replace('{icon}', (_a = current === null || current === void 0 ? void 0 : current.icon) !== null && _a !== void 0 ? _a : '')] }), jsxRuntime.jsxs("button", { type: "button", onClick: clear, disabled: layers.length === 0, children: [jsxRuntime.jsx(actions.NtdTrash, { size: 14 }), " ", t('iconStacker.clear', 'Clear the stack')] })] }), layers.length === 0 ? (jsxRuntime.jsx("p", { className: "nice-icon-stacker__empty", children: t('iconStacker.empty', 'The stack is empty. Add an icon to start one.') })) : (jsxRuntime.jsxs("div", { className: "nice-icon-stacker__body", children: [jsxRuntime.jsx("div", { className: "nice-icon-stacker__preview", "aria-label": t('iconStacker.preview', 'Preview'), role: "group", children: PREVIEW_SIZES.map((s) => (jsxRuntime.jsxs("span", { className: "nice-icon-stacker__preview-cell", children: [renderStack(s), jsxRuntime.jsxs("small", { children: [s, "px"] })] }, s))) }), jsxRuntime.jsx("ol", { className: "nice-icon-stacker__layers", "aria-label": t('iconStacker.layers', 'Layers'), children: layers.map((l, i) => {
107
+ var _a, _b, _c;
108
+ const Icon = resolve(l.icon);
109
+ const isBase = i === 0;
110
+ const align = (_a = l.align) !== null && _a !== void 0 ? _a : 'center';
111
+ const share = (_b = l.size) !== null && _b !== void 0 ? _b : stacking.defaultStackSize(align);
112
+ return (jsxRuntime.jsxs("li", { className: "nice-icon-stacker__layer", children: [jsxRuntime.jsx("span", { className: "nice-icon-stacker__layer-icon", children: Icon ? jsxRuntime.jsx(Icon, { size: 24, variant: l.variant }) : null }), jsxRuntime.jsxs("span", { className: "nice-icon-stacker__layer-name", children: [l.icon, isBase && (jsxRuntime.jsx("span", { className: "nice-icon-stacker__badge", children: t('iconStacker.base', 'Base') }))] }), !isBase && (jsxRuntime.jsxs("label", { className: "nice-icon-stacker__field", children: [jsxRuntime.jsx("span", { children: t('iconStacker.align', 'Position') }), jsxRuntime.jsx("select", { value: align, onChange: (e) => update(l.id, { align: e.target.value }), children: ALIGNS.map((a) => (jsxRuntime.jsx("option", { value: a, children: a }, a))) })] })), !isBase && (jsxRuntime.jsxs("label", { className: "nice-icon-stacker__field", children: [jsxRuntime.jsx("span", { children: t('iconStacker.size', 'Size') }), jsxRuntime.jsx("input", { type: "range", min: 0.2, max: 1, step: 0.05, value: share, onChange: (e) => update(l.id, { size: Number(e.target.value) }) }), jsxRuntime.jsxs("output", { children: [Math.round(share * 100), "%"] })] })), jsxRuntime.jsxs("label", { className: "nice-icon-stacker__field", children: [jsxRuntime.jsx("span", { children: t('iconStacker.text', 'Text') }), jsxRuntime.jsx("input", { type: "text", value: (_c = l.text) !== null && _c !== void 0 ? _c : '', maxLength: 4, onChange: (e) => update(l.id, { text: e.target.value || undefined }) })] }), jsxRuntime.jsx("button", { type: "button", className: "nice-icon-stacker__remove", onClick: () => remove(l.id), "aria-label": t('iconStacker.remove', 'Remove {icon}').replace('{icon}', l.icon), title: t('iconStacker.remove', 'Remove {icon}').replace('{icon}', l.icon), children: jsxRuntime.jsx(actions.NtdTrash, { size: 14 }) })] }, l.id));
113
+ }) }), jsxRuntime.jsxs("div", { className: "nice-icon-stacker__code", children: [jsxRuntime.jsx("pre", { children: code }), jsxRuntime.jsxs("button", { type: "button", onClick: copy, children: [copied ? jsxRuntime.jsx(actions.NtdCheck, { size: 14 }) : jsxRuntime.jsx(actions.NtdCopy, { size: 14 }), ' ', copied ? t('iconStacker.copied', 'Copied') : t('iconStacker.copy', 'Copy the code')] })] })] }))] }));
114
+ }
115
+ if (process.env.NODE_ENV !== 'production') {
116
+ NiceIconStacker.displayName = 'NiceIconStacker';
117
+ }
118
+
119
+ exports.NiceIconStacker = NiceIconStacker;
120
+ exports.niceIconStackCode = niceIconStackCode;
@@ -2,6 +2,7 @@
2
2
 
3
3
  var animationControls = require('./animationControls.js');
4
4
  var NiceIconPicker = require('./NiceIconPicker.js');
5
+ var NiceIconStacker = require('./NiceIconStacker.js');
5
6
  var NtdIconMorph = require('./NtdIconMorph.js');
6
7
  var NtdIconBadge = require('./NtdIconBadge.js');
7
8
  var smilAnimations = require('./smilAnimations.js');
@@ -18,6 +19,8 @@ exports.createHoverAnimation = animationControls.createHoverAnimation;
18
19
  exports.createVisibilityAnimation = animationControls.createVisibilityAnimation;
19
20
  exports.useAnimationSequence = animationControls.useAnimationSequence;
20
21
  exports.NiceIconPicker = NiceIconPicker.NiceIconPicker;
22
+ exports.NiceIconStacker = NiceIconStacker.NiceIconStacker;
23
+ exports.niceIconStackCode = NiceIconStacker.niceIconStackCode;
21
24
  exports.NtdIconMorph = NtdIconMorph.NtdIconMorph;
22
25
  exports.NtdMorphPair = NtdIconMorph.NtdMorphPair;
23
26
  exports.NtdPathMorph = NtdIconMorph.NtdPathMorph;
@@ -4,6 +4,7 @@ var jsxRuntime = require('react/jsx-runtime');
4
4
  var React = require('react');
5
5
  var sharpen = require('./sharpen.js');
6
6
  var smilAnimations = require('./smilAnimations.js');
7
+ var stacking = require('./stacking.js');
7
8
  var types = require('./types.js');
8
9
 
9
10
  /* ── Dark mode detection hook ───────────────────────── */
@@ -27,6 +28,7 @@ function subscribeColorScheme(callback) {
27
28
  function useColorScheme() {
28
29
  return React.useSyncExternalStore(subscribeColorScheme, getColorScheme, () => 'light');
29
30
  }
31
+ const BOX_CENTER = [12, 12];
30
32
  /**
31
33
  * Factory function that creates a React icon component from SVG path renderers.
32
34
  *
@@ -113,10 +115,12 @@ function renderCutoutShape(shape, fill, maskId) {
113
115
  }
114
116
  }
115
117
  function createIcon(options) {
116
- const { displayName, viewBox = '0 0 24 24', paths } = options;
118
+ const { displayName, viewBox = '0 0 24 24', paths, textCenter = BOX_CENTER } = options;
117
119
  const Icon = React.forwardRef(({ size = 'md', variant = 'solid', cutoutShape = 'rounded-square', color, lightColor, darkColor, primaryColor: propPrimaryColor = 'currentColor', secondaryColor, accentColor, primaryOpacity = 1, secondaryOpacity = 0.4, accentOpacity = 1, strokeWidth = 2, strokeLinecap: propLinecap, strokeLinejoin: propLinejoin, animation, animationDuration, smilAnimation, smilDuration = 1, smilRepeatCount = 'indefinite', smilEasing = 'ease-in-out', smilDelay = 0, gradient, gradientType = 'linear', gradientAngle = 135, gradientAnimation,
118
120
  // Transform props
119
- rotate, flipX, flipY, scale, transition, transformOrigin = 'center center', title, className, style, ...rest }, ref) => {
121
+ rotate, flipX, flipY, scale, transition, transformOrigin = 'center center',
122
+ // stacking (1.3.5): the parent icon reads a child's own align / size from its props
123
+ children, stackAlign: _stackAlign, stackSize: _stackSize, textColor, textSize, title, className, style, ...rest }, ref) => {
120
124
  var _a, _b, _c, _d, _e, _f, _g;
121
125
  const resolvedSize = typeof size === 'number' ? size : ((_a = types.SIZE_MAP[size]) !== null && _a !== void 0 ? _a : 24);
122
126
  const gradId = React.useId();
@@ -189,7 +193,10 @@ function createIcon(options) {
189
193
  ? `transform ${transition}`
190
194
  : undefined;
191
195
  const animStyle = {
192
- ...(animationDuration ? { animationDuration } : {}),
196
+ // the variable carries the duration to the parts an icon animates inside itself
197
+ ...(animationDuration
198
+ ? { animationDuration, '--ntd-anim-duration': animationDuration }
199
+ : {}),
193
200
  ...(transformString ? { transform: transformString } : {}),
194
201
  ...(transformString ? { transformOrigin } : {}),
195
202
  ...(transitionValue ? { transition: transitionValue } : {}),
@@ -222,7 +229,19 @@ function createIcon(options) {
222
229
  : variant === 'wireframe'
223
230
  ? sharpen.wireTree(renderer(renderProps), effectivePrimary)
224
231
  : renderer(renderProps);
225
- const iconContent = variant === 'mini' ? (jsxRuntime.jsx("g", { transform: `translate(${center.x} ${center.y}) scale(${sharpen.MINI_SCALE}) translate(${-center.x} ${-center.y})`, children: rendered })) : (rendered);
232
+ // Children stacked in the icon (1.3.5) — drawn as they are, not re-drawn as the variant.
233
+ const stacked = stacking.renderStackChildren(children, {
234
+ variant,
235
+ primaryColor: effectivePrimary,
236
+ accentColor: resolvedAccent,
237
+ tileColor: gradient ? effectivePrimary : resolvedSecondary,
238
+ strokeWidth,
239
+ textCenter,
240
+ textColor,
241
+ textSize,
242
+ });
243
+ const drawn = stacked ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [rendered, stacked] })) : (rendered);
244
+ const iconContent = variant === 'mini' ? (jsxRuntime.jsx("g", { transform: `translate(${center.x} ${center.y}) scale(${sharpen.MINI_SCALE}) translate(${-center.x} ${-center.y})`, children: drawn })) : (drawn);
226
245
  return (jsxRuntime.jsxs("svg", { ref: ref, xmlns: "http://www.w3.org/2000/svg", viewBox: viewBox, width: resolvedSize, height: resolvedSize, fill: "none", strokeLinecap: strokeLinecap, strokeLinejoin: strokeLinejoin, className: classes, style: animStyle, role: title ? 'img' : 'presentation', "aria-hidden": title ? undefined : true, "aria-label": title, ...rest, children: [title && jsxRuntime.jsx("title", { children: title }), gradient && (jsxRuntime.jsx("defs", { children: renderGradientDef(gradId, gradient, gradientType, gradientAngle, gradientAnimation) })), is3d && jsxRuntime.jsx("defs", { children: render3dFilterDef(filterId) }), isCutout && (jsxRuntime.jsx("defs", { children: jsxRuntime.jsxs("mask", { id: maskId, children: [jsxRuntime.jsx("rect", { x: "0", y: "0", width: "24", height: "24", fill: "white" }), jsxRuntime.jsx("g", { transform: "translate(12,12) scale(0.8) translate(-12,-12)", children: paths.solid({
227
246
  ...renderProps,
228
247
  primaryColor: 'black',
@@ -235,6 +254,8 @@ function createIcon(options) {
235
254
  }) })] }) })), 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)] }));
236
255
  });
237
256
  Icon.displayName = displayName;
257
+ // lets a parent icon tell this one among its children (stacking, 1.3.5)
258
+ Icon[stacking.NTD_ICON_MARK] = true;
238
259
  return Icon;
239
260
  }
240
261
 
@@ -106,6 +106,7 @@ function exportIconToSvgString(svgElement, options = {}) {
106
106
  clone.style.animation = '';
107
107
  clone.style.animationName = '';
108
108
  clone.style.animationDuration = '';
109
+ clone.style.removeProperty('--ntd-anim-duration');
109
110
  // Remove SMIL animation elements
110
111
  clone.querySelectorAll('animate, animateTransform, animateMotion').forEach((el) => el.remove());
111
112
  }
@@ -35,17 +35,18 @@ const BELL_CLAPPER_LINE = 'M9 19a3 3 0 006 0';
35
35
  const BELL_RINGS = 'M2 4l1.5 1.5M22 4l-1.5 1.5';
36
36
  /**
37
37
  * NtdBell and NtdBellRing, so the two share one bell; `ringing` adds the ring marks. Duotone and
38
- * vivid are fills only: the bell primary, the clapper (and the marks) secondary.
38
+ * vivid are fills only: the bell primary, the clapper secondary. The marks are primary on the solid
39
+ * bell, secondary in duotone and in the accent colour in vivid.
39
40
  */
40
41
  function bellIcon(displayName, ringing) {
41
- const parts = ({ primaryColor, secondaryColor, strokeWidth }, detail) => (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("path", { d: BELL, fill: primaryColor }), jsxRuntime.jsx("path", { d: BELL_CLAPPER, fill: detail }), ringing && jsxRuntime.jsx("path", { d: BELL_RINGS, stroke: secondaryColor, strokeWidth: strokeWidth })] }));
42
+ const parts = ({ primaryColor, strokeWidth }, detail, rings, ringsOpacity = 1) => (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("path", { d: BELL, fill: primaryColor }), jsxRuntime.jsx("path", { d: BELL_CLAPPER, fill: detail }), ringing && (jsxRuntime.jsx("path", { d: BELL_RINGS, stroke: rings, strokeWidth: strokeWidth, opacity: ringsOpacity }))] }));
42
43
  return createIcon.createIcon({
43
44
  displayName,
44
45
  paths: {
45
- solid: (props) => parts(props, props.primaryColor),
46
+ solid: (props) => parts(props, props.primaryColor, props.primaryColor),
46
47
  outline: ({ primaryColor, strokeWidth }) => (jsxRuntime.jsx("path", { d: `${BELL}${BELL_CLAPPER_LINE}${ringing ? BELL_RINGS : ''}`, stroke: primaryColor, strokeWidth: strokeWidth, fill: "none" })),
47
- duotone: (props) => parts(props, props.secondaryColor),
48
- vivid: (props) => parts(props, props.secondaryColor),
48
+ duotone: (props) => parts(props, props.secondaryColor, props.secondaryColor),
49
+ vivid: (props) => parts(props, props.secondaryColor, props.accentColor, props.accentOpacity),
49
50
  },
50
51
  });
51
52
  }
@@ -104,22 +104,51 @@ const NtdUpload = /* @__PURE__ */ createIcon.createIcon({
104
104
  vivid: ({ primaryColor, secondaryColor, strokeWidth }) => (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("path", { d: TRAY, stroke: secondaryColor, strokeWidth: strokeWidth * 2 }), jsxRuntime.jsx("path", { d: "M17 8l-5-5-5 5M12 5v10", stroke: primaryColor, strokeWidth: strokeWidth * 2 })] })),
105
105
  },
106
106
  });
107
+ // NtdShare: three nodes joined by two links, the links twice the stroke width in every variant but
108
+ // `outline`. Duotone and vivid, fills only: the nodes primary, the links secondary (vivid had a
109
+ // fixed #3B82F6 node and white links that vanished on a light page).
110
+ const SHARE_NODES = 'M18 2a3 3 0 110 6 3 3 0 110-6zM6 9a3 3 0 110 6 3 3 0 110-6zM18 16a3 3 0 110 6 3 3 0 110-6z';
111
+ const SHARE_LINKS = 'M8.59 13.51L15.42 17.49M15.41 6.51L8.59 10.49';
112
+ function shareFilled(nodes, links, strokeWidth) {
113
+ return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("path", { d: SHARE_LINKS, stroke: links, strokeWidth: strokeWidth * 2 }), jsxRuntime.jsx("path", { d: SHARE_NODES, fill: nodes })] }));
114
+ }
107
115
  const NtdShare = /* @__PURE__ */ createIcon.createIcon({
108
116
  displayName: 'NtdShare',
109
117
  paths: {
110
- solid: ({ primaryColor, strokeWidth }) => (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("circle", { cx: "18", cy: "5", r: "3", fill: primaryColor }), jsxRuntime.jsx("circle", { cx: "6", cy: "12", r: "3", fill: primaryColor }), jsxRuntime.jsx("circle", { cx: "18", cy: "19", r: "3", fill: primaryColor }), jsxRuntime.jsx("line", { x1: "8.59", y1: "13.51", x2: "15.42", y2: "17.49", stroke: primaryColor, strokeWidth: strokeWidth }), jsxRuntime.jsx("line", { x1: "15.41", y1: "6.51", x2: "8.59", y2: "10.49", stroke: primaryColor, strokeWidth: strokeWidth })] })),
118
+ solid: ({ primaryColor, strokeWidth }) => shareFilled(primaryColor, primaryColor, strokeWidth),
111
119
  outline: ({ primaryColor, strokeWidth }) => (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("circle", { cx: "18", cy: "5", r: "2.5", stroke: primaryColor, strokeWidth: strokeWidth * 0.75, fill: "none" }), jsxRuntime.jsx("circle", { cx: "6", cy: "12", r: "2.5", stroke: primaryColor, strokeWidth: strokeWidth * 0.75, fill: "none" }), jsxRuntime.jsx("circle", { cx: "18", cy: "19", r: "2.5", stroke: primaryColor, strokeWidth: strokeWidth * 0.75, fill: "none" }), jsxRuntime.jsx("line", { x1: "8.59", y1: "13.51", x2: "15.42", y2: "17.49", stroke: primaryColor, strokeWidth: strokeWidth * 0.75 }), jsxRuntime.jsx("line", { x1: "15.41", y1: "6.51", x2: "8.59", y2: "10.49", stroke: primaryColor, strokeWidth: strokeWidth * 0.75 })] })),
112
- duotone: ({ primaryColor, secondaryColor, secondaryOpacity, strokeWidth }) => (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("circle", { cx: "18", cy: "5", r: "3", fill: secondaryColor, opacity: secondaryOpacity }), jsxRuntime.jsx("circle", { cx: "6", cy: "12", r: "3", fill: secondaryColor, opacity: secondaryOpacity }), jsxRuntime.jsx("circle", { cx: "18", cy: "19", r: "3", fill: secondaryColor, opacity: secondaryOpacity }), jsxRuntime.jsx("circle", { cx: "18", cy: "5", r: "2.5", stroke: primaryColor, strokeWidth: strokeWidth * 0.75, fill: "none" }), jsxRuntime.jsx("circle", { cx: "6", cy: "12", r: "2.5", stroke: primaryColor, strokeWidth: strokeWidth * 0.75, fill: "none" }), jsxRuntime.jsx("circle", { cx: "18", cy: "19", r: "2.5", stroke: primaryColor, strokeWidth: strokeWidth * 0.75, fill: "none" }), jsxRuntime.jsx("line", { x1: "8.59", y1: "13.51", x2: "15.42", y2: "17.49", stroke: primaryColor, strokeWidth: strokeWidth * 0.75 }), jsxRuntime.jsx("line", { x1: "15.41", y1: "6.51", x2: "8.59", y2: "10.49", stroke: primaryColor, strokeWidth: strokeWidth * 0.75 })] })),
113
- vivid: ({ primaryColor, secondaryColor }) => (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("circle", { cx: "18", cy: "5", r: "3", fill: primaryColor }), jsxRuntime.jsx("circle", { cx: "18", cy: "5", r: "3", fill: "white", opacity: "0.08" }), jsxRuntime.jsx("circle", { cx: "6", cy: "12", r: "3", fill: secondaryColor }), jsxRuntime.jsx("circle", { cx: "18", cy: "19", r: "3", fill: "#3B82F6" }), jsxRuntime.jsx("line", { x1: "8.59", y1: "13.51", x2: "15.42", y2: "17.49", stroke: "white", strokeWidth: "1" }), jsxRuntime.jsx("line", { x1: "15.41", y1: "6.51", x2: "8.59", y2: "10.49", stroke: "white", strokeWidth: "1" })] })),
120
+ duotone: ({ primaryColor, secondaryColor, strokeWidth }) => shareFilled(primaryColor, secondaryColor, strokeWidth),
121
+ vivid: ({ primaryColor, secondaryColor, strokeWidth }) => shareFilled(primaryColor, secondaryColor, strokeWidth),
114
122
  },
115
123
  });
124
+ // NtdCopy: the front sheet at the top right (it was at the bottom right), the back one showing at
125
+ // the bottom left as a line that stops 1 short of the front — closer than before. The gap is
126
+ // worked out from the stroke width, so it stays the same at any weight; `sharp` draws the back
127
+ // sheet with square corners (the derived one kept their arcs). Duotone, fills only: the back sheet
128
+ // secondary under the primary front one; vivid: the back line secondary.
129
+ const COPY_FRONT = { x: 8.5, y: 3, width: 12.5, height: 12.5, rx: 2.5 };
130
+ const COPY_BACK = { x: 3, y: 8.5, width: 12.5, height: 12.5, rx: 2.5 };
131
+ /** What shows of the back sheet: its edges outside the front one, `gap` short of it. */
132
+ function copyBack(gap, square = false) {
133
+ const top = COPY_FRONT.x - gap;
134
+ const right = COPY_FRONT.y + COPY_FRONT.height + gap;
135
+ return square
136
+ ? `M${top} 8.5H3V21h12.5V${right}`
137
+ : `M${top} 8.5H5.5a2.5 2.5 0 00-2.5 2.5v7.5a2.5 2.5 0 002.5 2.5h7.5a2.5 2.5 0 002.5-2.5V${right}`;
138
+ }
139
+ /** The front sheet filled and the back one as a line 1 short of it (past its round cap). */
140
+ function copyFilled(front, back, strokeWidth, square = false) {
141
+ return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("rect", { ...COPY_FRONT, fill: front }), jsxRuntime.jsx("path", { d: copyBack(1 + strokeWidth / 2, square), stroke: back, strokeWidth: strokeWidth, fill: "none" })] }));
142
+ }
116
143
  const NtdCopy = /* @__PURE__ */ createIcon.createIcon({
117
144
  displayName: 'NtdCopy',
118
145
  paths: {
119
- solid: ({ primaryColor, strokeWidth }) => (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("rect", { x: "9", y: "9", width: "12", height: "12", rx: "2.5", fill: primaryColor }), jsxRuntime.jsx("path", { d: "M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1", stroke: primaryColor, strokeWidth: strokeWidth })] })),
120
- outline: ({ primaryColor, strokeWidth }) => (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("rect", { x: "9", y: "9", width: "12", height: "12", rx: "2.5", stroke: primaryColor, strokeWidth: strokeWidth, fill: "none" }), jsxRuntime.jsx("path", { d: "M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1", stroke: primaryColor, strokeWidth: strokeWidth })] })),
121
- duotone: ({ primaryColor, secondaryColor, secondaryOpacity, strokeWidth }) => (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("rect", { x: "2", y: "2", width: "13", height: "13", rx: "2.5", fill: secondaryColor, opacity: secondaryOpacity }), jsxRuntime.jsx("rect", { x: "9", y: "9", width: "12", height: "12", rx: "2.5", stroke: primaryColor, strokeWidth: strokeWidth, fill: "none" }), jsxRuntime.jsx("path", { d: "M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1", stroke: primaryColor, strokeWidth: strokeWidth })] })),
122
- vivid: ({ primaryColor, secondaryColor }) => (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("rect", { x: "9", y: "9", width: "12", height: "12", rx: "2.5", fill: primaryColor }), jsxRuntime.jsx("rect", { x: "9", y: "9", width: "12", height: "12", rx: "2.5", fill: "white", opacity: "0.1" }), jsxRuntime.jsx("path", { d: "M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1", stroke: secondaryColor, strokeWidth: "1.8" })] })),
146
+ solid: ({ primaryColor, strokeWidth }) => copyFilled(primaryColor, primaryColor, strokeWidth),
147
+ sharp: ({ primaryColor, strokeWidth }) => copyFilled(primaryColor, primaryColor, strokeWidth, true),
148
+ // both sheets stroked: the back line stops 1 short of the front sheet's line
149
+ outline: ({ primaryColor, strokeWidth }) => (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("rect", { ...COPY_FRONT, stroke: primaryColor, strokeWidth: strokeWidth, fill: "none" }), jsxRuntime.jsx("path", { d: copyBack(1 + strokeWidth), stroke: primaryColor, strokeWidth: strokeWidth, fill: "none" })] })),
150
+ duotone: ({ primaryColor, secondaryColor }) => (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("rect", { ...COPY_BACK, fill: secondaryColor }), jsxRuntime.jsx("rect", { ...COPY_FRONT, fill: primaryColor })] })),
151
+ vivid: ({ primaryColor, secondaryColor, strokeWidth }) => copyFilled(primaryColor, secondaryColor, strokeWidth),
123
152
  },
124
153
  });
125
154
  /** NtdClipboard's two lines of text, high on the board (they sat below its middle). */
@@ -132,6 +132,8 @@ const NtdSparkle = /* @__PURE__ */ createIcon.createIcon({
132
132
  vivid: ({ primaryColor, secondaryColor: _secondaryColor }) => (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("path", { d: "M12 2l2.4 7.2L22 12l-7.6 2.8L12 22l-2.4-7.2L2 12l7.6-2.8L12 2z", fill: primaryColor }), jsxRuntime.jsx("path", { d: "M12 2l2.4 7.2L22 12l-7.6 2.8L12 22l-2.4-7.2L2 12l7.6-2.8L12 2z", fill: "white", opacity: "0.1" })] })),
133
133
  },
134
134
  });
135
+ /** @deprecated The same four-pointed star as {@link NtdSparkle} — use that. */
136
+ const NtdStar4 = NtdSparkle;
135
137
  const NtdMagicWand = /* @__PURE__ */ createIcon.createIcon({
136
138
  displayName: 'NtdMagicWand',
137
139
  paths: {
@@ -184,6 +186,7 @@ exports.NtdNeuralNetwork = NtdNeuralNetwork;
184
186
  exports.NtdPrompt = NtdPrompt;
185
187
  exports.NtdRobot = NtdRobot;
186
188
  exports.NtdSparkle = NtdSparkle;
189
+ exports.NtdStar4 = NtdStar4;
187
190
  exports.NtdTensor = NtdTensor;
188
191
  exports.NtdTraining = NtdTraining;
189
192
  exports.NtdVoiceAssistant = NtdVoiceAssistant;
@@ -5,21 +5,22 @@ var createIcon = require('../createIcon.js');
5
5
  var iconParts = require('../iconParts.js');
6
6
 
7
7
  // The band sits halfway down the case and is a cut through it: kept inside the case it meets the
8
- // sides flush (its round caps stuck out of both). A clasp hangs from its middle: low and wide,
9
- // secondary on the filled case (the accent colour in vivid, its third colour).
8
+ // sides flush (its round caps stuck out of both). A clasp hangs from its middle: low and wide, in
9
+ // the accent colour like the band on the filled case (secondary in duotone), with a primary lock
10
+ // square in it where it had a slot showing the band through.
10
11
  const BRIEFCASE_CASE = { x: 2, y: 7, width: 20, height: 14, rx: 2 };
11
12
  const BRIEFCASE_HANDLE = 'M16 7V5a2 2 0 00-2-2h-4a2 2 0 00-2 2v2';
12
- /** The clasp over the band, hanging down from it, with its slot (even-odd). */
13
+ /** The clasp over the band, hanging down from it. */
13
14
  const BRIEFCASE_CLASP = 'M10.6 11.3h2.8a.8.8 0 01.8.8v2.6a.8.8 0 01-.8.8h-2.8a.8.8 0 01-.8-.8v-2.6a.8.8 0 01.8-.8z';
14
- const BRIEFCASE_SLOT = 'M11.2 13.2h1.6v.9h-1.6z';
15
+ const BRIEFCASE_LOCK = { x: 11.2, y: 12.6, width: 1.6, height: 1.6 };
15
16
  const NtdBriefcase = /* @__PURE__ */ createIcon.createIcon({
16
17
  displayName: 'NtdBriefcase',
17
18
  paths: {
18
- solid: (props) => (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("rect", { ...BRIEFCASE_CASE, fill: props.primaryColor }), jsxRuntime.jsx("path", { d: BRIEFCASE_HANDLE, stroke: props.primaryColor, strokeWidth: props.strokeWidth, fill: "none" }), iconParts.within(props.uid, 'band', jsxRuntime.jsx("rect", { ...BRIEFCASE_CASE }), jsxRuntime.jsx("line", { x1: "2", y1: "12.5", x2: "22", y2: "12.5", stroke: props.accentColor, strokeWidth: "1.5", opacity: props.accentOpacity })), jsxRuntime.jsx("path", { d: `${BRIEFCASE_CLASP}${BRIEFCASE_SLOT}`, fillRule: "evenodd", fill: iconParts.detailColor(props) })] })),
19
+ solid: (props) => (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("rect", { ...BRIEFCASE_CASE, fill: props.primaryColor }), jsxRuntime.jsx("path", { d: BRIEFCASE_HANDLE, stroke: props.primaryColor, strokeWidth: props.strokeWidth, fill: "none" }), iconParts.within(props.uid, 'band', jsxRuntime.jsx("rect", { ...BRIEFCASE_CASE }), jsxRuntime.jsx("line", { x1: "2", y1: "12.5", x2: "22", y2: "12.5", stroke: props.accentColor, strokeWidth: "1.5", opacity: props.accentOpacity })), jsxRuntime.jsx("path", { d: BRIEFCASE_CLASP, fill: props.accentColor, opacity: props.accentOpacity }), jsxRuntime.jsx("rect", { ...BRIEFCASE_LOCK, fill: props.primaryColor })] })),
19
20
  outline: ({ primaryColor, strokeWidth }) => (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("rect", { ...BRIEFCASE_CASE, stroke: primaryColor, strokeWidth: strokeWidth, fill: "none" }), jsxRuntime.jsx("path", { d: BRIEFCASE_HANDLE, stroke: primaryColor, strokeWidth: strokeWidth, fill: "none" }), jsxRuntime.jsx("line", { x1: "2", y1: "12.5", x2: "22", y2: "12.5", stroke: primaryColor, strokeWidth: "1.5" }), jsxRuntime.jsx("path", { d: BRIEFCASE_CLASP, fill: primaryColor })] })),
20
21
  // fills only: the case and the handle primary, the band and the clasp secondary
21
- duotone: ({ primaryColor, secondaryColor, strokeWidth, uid }) => (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("rect", { ...BRIEFCASE_CASE, fill: primaryColor }), jsxRuntime.jsx("path", { d: BRIEFCASE_HANDLE, stroke: primaryColor, strokeWidth: strokeWidth, fill: "none" }), iconParts.within(uid, 'band', jsxRuntime.jsx("rect", { ...BRIEFCASE_CASE }), jsxRuntime.jsx("line", { x1: "2", y1: "12.5", x2: "22", y2: "12.5", stroke: secondaryColor, strokeWidth: "1.5" })), jsxRuntime.jsx("path", { d: `${BRIEFCASE_CLASP}${BRIEFCASE_SLOT}`, fillRule: "evenodd", fill: secondaryColor })] })),
22
- vivid: ({ primaryColor, secondaryColor, accentColor, accentOpacity, strokeWidth, uid }) => (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("rect", { ...BRIEFCASE_CASE, fill: primaryColor }), jsxRuntime.jsx("path", { d: BRIEFCASE_HANDLE, stroke: secondaryColor, strokeWidth: strokeWidth, fill: "none" }), iconParts.within(uid, 'band', jsxRuntime.jsx("rect", { ...BRIEFCASE_CASE }), jsxRuntime.jsx("line", { x1: "2", y1: "12.5", x2: "22", y2: "12.5", stroke: secondaryColor, strokeWidth: "1.5" })), jsxRuntime.jsx("path", { d: `${BRIEFCASE_CLASP}${BRIEFCASE_SLOT}`, fillRule: "evenodd", fill: accentColor, opacity: accentOpacity })] })),
22
+ duotone: ({ primaryColor, secondaryColor, strokeWidth, uid }) => (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("rect", { ...BRIEFCASE_CASE, fill: primaryColor }), jsxRuntime.jsx("path", { d: BRIEFCASE_HANDLE, stroke: primaryColor, strokeWidth: strokeWidth, fill: "none" }), iconParts.within(uid, 'band', jsxRuntime.jsx("rect", { ...BRIEFCASE_CASE }), jsxRuntime.jsx("line", { x1: "2", y1: "12.5", x2: "22", y2: "12.5", stroke: secondaryColor, strokeWidth: "1.5" })), jsxRuntime.jsx("path", { d: BRIEFCASE_CLASP, fill: secondaryColor }), jsxRuntime.jsx("rect", { ...BRIEFCASE_LOCK, fill: primaryColor })] })),
23
+ vivid: ({ primaryColor, secondaryColor, accentColor, accentOpacity, strokeWidth, uid }) => (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("rect", { ...BRIEFCASE_CASE, fill: primaryColor }), jsxRuntime.jsx("path", { d: BRIEFCASE_HANDLE, stroke: secondaryColor, strokeWidth: strokeWidth, fill: "none" }), iconParts.within(uid, 'band', jsxRuntime.jsx("rect", { ...BRIEFCASE_CASE }), jsxRuntime.jsx("line", { x1: "2", y1: "12.5", x2: "22", y2: "12.5", stroke: secondaryColor, strokeWidth: "1.5" })), jsxRuntime.jsx("path", { d: BRIEFCASE_CLASP, fill: accentColor, opacity: accentOpacity }), jsxRuntime.jsx("rect", { ...BRIEFCASE_LOCK, fill: primaryColor })] })),
23
24
  },
24
25
  });
25
26
  // NtdDollar: a line glyph, twice the stroke width in every variant but `outline`; the stem runs
@@ -46,14 +46,17 @@ const NtdUniversity = /* @__PURE__ */ createIcon.createIcon({
46
46
  LEARNING TOOLS
47
47
  ───────────────────────────────────────────────────────────────────────────── */
48
48
  // NtdBook: a closed book, not a page — the rounded spine on the left, the block of pages showing
49
- // at the bottom between the front cover and the line of the back cover. Down the right edge it is
50
- // cover, then the pages set in 2.5 from that edge (the cover showing beside them), then cover
51
- // again; the outline marks where the pages end with a line. The cover is plain, so another glyph
52
- // can sit on it; NtdBookBookmark adds a ribbon.
53
- const BOOK_SILHOUETTE = 'M7 2h12a1 1 0 011 1v17a1 1 0 01-1 1H7a2.5 2.5 0 01-2.5-2.5v-14A2.5 2.5 0 017 2z';
54
- const BOOK_PAGES = 'M6.6 17.6h10.9v2.3H6.6a1.15 1.15 0 010-2.3z';
49
+ // at the bottom between the front cover and the back cover. Down the right edge it is cover, then
50
+ // a notch cut in 0.9 at the pages, then cover again: beside the pages only a narrow strip of cover
51
+ // is left (it was a 2.5 wide bar). The outline ends the pages with a line short of the edge, the back
52
+ // cover running on under the notch. The cover is plain, so another glyph can sit on it;
53
+ // NtdBookBookmark adds a ribbon.
54
+ const BOOK_SILHOUETTE = 'M7 2h12a1 1 0 011 1v14.2h-.9v2.1H20v.7a1 1 0 01-1 1H7a2.5 2.5 0 01-2.5-2.5v-14A2.5 2.5 0 017 2z';
55
+ const BOOK_PAGES = 'M6.6 17.2h11.4v2.1H6.6a1.05 1.05 0 010-2.1z';
55
56
  const BOOK_RIBBON = 'M9.5 2v6.5l1.75-1.3L13 8.5V2z';
56
- const BOOK_OUTLINE = 'M4.5 18.5v-14A2.5 2.5 0 017 2h12a1 1 0 011 1v17a1 1 0 01-1 1H7a2.5 2.5 0 010-5h13M17.5 16v5';
57
+ const BOOK_OUTLINE = 'M4.5 18.5v-14A2.5 2.5 0 017 2h12a1 1 0 011 1v13H7a2.5 2.5 0 000 5h13M18.7 16v5';
58
+ /** The inside of the outline, for duotone's fill under it. */
59
+ const BOOK_OUTLINE_FILL = 'M7 2h12a1 1 0 011 1v13h-1.3v5H7a2.5 2.5 0 01-2.5-2.5v-14A2.5 2.5 0 017 2z';
57
60
  /** The book, with the ribbon bookmark or without it. */
58
61
  function bookIcon(displayName, ribbon) {
59
62
  return createIcon.createIcon({
@@ -61,7 +64,7 @@ function bookIcon(displayName, ribbon) {
61
64
  paths: {
62
65
  solid: ({ primaryColor, accentColor, accentOpacity }) => (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("path", { d: BOOK_SILHOUETTE, fill: primaryColor }), jsxRuntime.jsxs("g", { fill: accentColor, opacity: accentOpacity, children: [jsxRuntime.jsx("path", { d: BOOK_PAGES }), ribbon && jsxRuntime.jsx("path", { d: BOOK_RIBBON })] })] })),
63
66
  outline: ({ primaryColor, strokeWidth }) => (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("path", { d: BOOK_OUTLINE, stroke: primaryColor, strokeWidth: strokeWidth, fill: "none" }), ribbon && (jsxRuntime.jsx("path", { d: "M9.5 2.5v6l1.75-1.3L13 8.5v-6", stroke: primaryColor, strokeWidth: strokeWidth * 0.75, fill: "none" }))] })),
64
- duotone: ({ primaryColor, secondaryColor, secondaryOpacity, strokeWidth }) => (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("path", { d: BOOK_SILHOUETTE, fill: secondaryColor, opacity: secondaryOpacity }), jsxRuntime.jsx("path", { d: BOOK_OUTLINE, stroke: primaryColor, strokeWidth: strokeWidth, fill: "none" }), ribbon && jsxRuntime.jsx("path", { d: BOOK_RIBBON, fill: primaryColor })] })),
67
+ duotone: ({ primaryColor, secondaryColor, secondaryOpacity, strokeWidth }) => (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("path", { d: BOOK_OUTLINE_FILL, fill: secondaryColor, opacity: secondaryOpacity }), jsxRuntime.jsx("path", { d: BOOK_OUTLINE, stroke: primaryColor, strokeWidth: strokeWidth, fill: "none" }), ribbon && jsxRuntime.jsx("path", { d: BOOK_RIBBON, fill: primaryColor })] })),
65
68
  vivid: ({ primaryColor, secondaryColor, accentColor, accentOpacity }) => (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("path", { d: BOOK_SILHOUETTE, fill: primaryColor }), jsxRuntime.jsx("path", { d: BOOK_PAGES, fill: accentColor, opacity: accentOpacity }), ribbon && jsxRuntime.jsx("path", { d: BOOK_RIBBON, fill: secondaryColor })] })),
66
69
  },
67
70
  });
@@ -78,12 +81,13 @@ const NtdOpenBook = /* @__PURE__ */ createIcon.createIcon({
78
81
  },
79
82
  });
80
83
  // NtdNotebook: a cover with short binder rings across its left edge (they stuck out further) and
81
- // three lines of writing — in the accent colour when filled, as the outline draws them. Duotone,
82
- // fills only: the cover primary, the rings and the writing secondary; vivid: the rings secondary,
83
- // the writing in the accent colour.
84
- const NOTEBOOK_COVER = { x: 5, y: 2, width: 15, height: 20, rx: 2 };
85
- const NOTEBOOK_RINGS = 'M3.5 6h3M3.5 10h3M3.5 14h3M3.5 18h3';
86
- const NOTEBOOK_TEXT = 'M9 7h7M9 11h7M9 15h4';
84
+ // three lines of writing — in the accent colour when filled, as the outline draws them. The cover
85
+ // is 16.5 × 19 (it was 15 × 20, tall and slim for an exercise book). Duotone, fills only: the
86
+ // cover primary, the rings and the writing secondary; vivid: the rings secondary, the writing in
87
+ // the accent colour.
88
+ const NOTEBOOK_COVER = { x: 4.5, y: 2.5, width: 16.5, height: 19, rx: 2 };
89
+ const NOTEBOOK_RINGS = 'M3 6h3M3 10h3M3 14h3M3 18h3';
90
+ const NOTEBOOK_TEXT = 'M9 7.5h8.5M9 11.5h8.5M9 15.5h4.5';
87
91
  const NtdNotebook = /* @__PURE__ */ createIcon.createIcon({
88
92
  displayName: 'NtdNotebook',
89
93
  paths: {