@nice2dev/icons 1.3.5 → 1.3.6
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 +34 -0
- package/README.md +20 -3
- package/dist/cjs/NiceIconStacker.js +122 -58
- package/dist/cjs/client.js +5 -1
- package/dist/cjs/createIcon.js +1 -1
- package/dist/cjs/iconStack.js +111 -0
- package/dist/cjs/index.js +5 -1
- package/dist/cjs/nicetodev-icons.css +1 -1
- package/dist/cjs/stacking.js +27 -10
- package/dist/esm/NiceIconStacker.js +122 -58
- package/dist/esm/client.js +2 -1
- package/dist/esm/createIcon.js +2 -2
- package/dist/esm/iconParts.js +1 -1
- package/dist/esm/iconStack.js +104 -0
- package/dist/esm/icons/business.js +1 -1
- package/dist/esm/icons/files.js +2 -2
- package/dist/esm/icons/media.js +1 -1
- package/dist/esm/index.js +2 -1
- package/dist/esm/nicetodev-icons.css +1 -1
- package/dist/esm/smilAnimations.js +1 -1
- package/dist/esm/stacking.js +27 -11
- package/dist/types/NiceIconStacker.d.ts +18 -28
- package/dist/types/client.d.ts +4 -2
- package/dist/types/iconStack.d.ts +77 -0
- package/dist/types/index.d.ts +4 -2
- package/dist/types/stacking.d.ts +11 -3
- package/dist/types/types.d.ts +13 -4
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,39 @@
|
|
|
1
1
|
# @nice2dev/icons — Changelog
|
|
2
2
|
|
|
3
|
+
## 1.3.6
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 4bdd01c: NiceIconStacker builds stacks nested any number of levels deep:
|
|
8
|
+
|
|
9
|
+
- **Nesting:** a layer sits in its `parent` (a new optional field). A layer without one sits in
|
|
10
|
+
the base, as in 1.3.5.
|
|
11
|
+
- **Where icons go:** "Add" puts the current icon into the selected layer and then selects it, so
|
|
12
|
+
adding again nests one level deeper (`<NtdBitcoin><NtdStar><NtdSparkle>1</NtdSparkle></NtdStar></NtdBitcoin>`).
|
|
13
|
+
Select an outer layer to put icons side by side.
|
|
14
|
+
- **Display:**
|
|
15
|
+
- layers are indented by depth;
|
|
16
|
+
- text is the innermost level, offered under the deepest icons;
|
|
17
|
+
- the colours flip at every level.
|
|
18
|
+
- **Removing:** removing a layer moves what sat in it up into its parent.
|
|
19
|
+
- **Handle:** a ref handle (`NiceIconStackerHandle`: `add`, `clear`) lets a parent run "add" from a
|
|
20
|
+
button of its own.
|
|
21
|
+
- **Translations:** new keys `iconStacker.addInto` and `iconStacker.target`.
|
|
22
|
+
- **Smaller text:** the default size of stacked text is three quarters of the 1.3.5 size (9, 7.5,
|
|
23
|
+
6, 5 by length).
|
|
24
|
+
- **`stackRotation`:** turns a stacked icon about its own centre.
|
|
25
|
+
- **NiceIconStacker:** every layer can turn and take its own primary, secondary and accent colours
|
|
26
|
+
(with "auto" to follow its parent), and its text a size and colour of its own.
|
|
27
|
+
- **Bound text:**
|
|
28
|
+
- in the stacker, text can be bound to a value ("Bind to"): the code writes `{count}`, and the
|
|
29
|
+
text stays a sample;
|
|
30
|
+
- the new `NiceIconStack` draws a stack kept as data with live values (`values={{ count }}`).
|
|
31
|
+
- **`renderIconStack`** draws a stack as one icon tree.
|
|
32
|
+
- **`half-` positions:** `stackAlign` takes every position again with `half-` (`half-top`,
|
|
33
|
+
`half-top-right`, `half-bottom`, …). The icon sits halfway between the centre and that edge or
|
|
34
|
+
corner, inside the parent, so it is cut out like centred content (only full corners are
|
|
35
|
+
badges).
|
|
36
|
+
|
|
3
37
|
## 1.3.5
|
|
4
38
|
|
|
5
39
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -220,19 +220,36 @@ centred on the icon:
|
|
|
220
220
|
</NtdFolder>
|
|
221
221
|
|
|
222
222
|
<NtdCircle>42</NtdCircle>
|
|
223
|
+
|
|
224
|
+
<NtdBitcoin>
|
|
225
|
+
<NtdStar>
|
|
226
|
+
<NtdSparkle>1</NtdSparkle>
|
|
227
|
+
</NtdStar>
|
|
228
|
+
</NtdBitcoin>
|
|
223
229
|
```
|
|
224
230
|
|
|
231
|
+
Stacks nest any number of levels deep, with the colours flipping at every level.
|
|
232
|
+
|
|
225
233
|
- **`stackAlign`:** `center` (default), `top`, `bottom`, `left`, `right`, `top-left`,
|
|
226
|
-
`top-right`, `bottom-left`, `bottom-right
|
|
234
|
+
`top-right`, `bottom-left`, `bottom-right`, plus each of them with `half-` (`half-top`,
|
|
235
|
+
`half-bottom-right`, …), halfway from the centre to that edge or corner.
|
|
227
236
|
- **`stackSize`:** the child's share of its parent. Default `0.6` centred, `0.5` elsewhere.
|
|
228
237
|
- **Colours** follow the parent unless the child sets its own:
|
|
229
238
|
- In the centre or at an edge, a filled parent cuts the content out. The child takes the
|
|
230
239
|
parent's accent and its text the parent's primary. On `outline` / `wireframe` everything stays
|
|
231
240
|
in primary.
|
|
232
241
|
- A corner badge keeps the parent's colours and sits on an accent-coloured gap.
|
|
233
|
-
-
|
|
242
|
+
- **`stackRotation`:** turns a stacked icon about its own centre, in degrees clockwise. Its
|
|
243
|
+
contents and text turn with it.
|
|
244
|
+
- **Text:** the default size is 9, 7.5, 6 or 5 in the 24-unit box (by length). `textColor` and
|
|
245
|
+
`textSize` override the text's colour and size.
|
|
246
|
+
- **Data:** `NiceIconStack` draws a stack kept as data (NiceIconStacker's layers), its bound text
|
|
247
|
+
filled from `values`. This works with live numbers, for example
|
|
248
|
+
`<NiceIconStack layers={stack} icons={icons} values={{ count }} />`.
|
|
234
249
|
- **Builder:** `NiceIconStacker` builds a stack interactively and writes the JSX
|
|
235
|
-
(`niceIconStackCode`).
|
|
250
|
+
(`niceIconStackCode`). Each icon you add goes into the selected layer, which is the newest one,
|
|
251
|
+
so adding nests deeper. Select an outer layer to put icons side by side. Text is the innermost
|
|
252
|
+
level.
|
|
236
253
|
|
|
237
254
|
---
|
|
238
255
|
|
|
@@ -4,6 +4,8 @@ var jsxRuntime = require('react/jsx-runtime');
|
|
|
4
4
|
var uiCore = require('@nice2dev/ui-core');
|
|
5
5
|
var React = require('react');
|
|
6
6
|
var actions = require('./icons/actions.js');
|
|
7
|
+
var navigation = require('./icons/navigation.js');
|
|
8
|
+
var iconStack = require('./iconStack.js');
|
|
7
9
|
var stacking = require('./stacking.js');
|
|
8
10
|
|
|
9
11
|
const ALIGNS = [
|
|
@@ -16,46 +18,82 @@ const ALIGNS = [
|
|
|
16
18
|
'top-right',
|
|
17
19
|
'bottom-left',
|
|
18
20
|
'bottom-right',
|
|
21
|
+
'half-top',
|
|
22
|
+
'half-bottom',
|
|
23
|
+
'half-left',
|
|
24
|
+
'half-right',
|
|
25
|
+
'half-top-left',
|
|
26
|
+
'half-top-right',
|
|
27
|
+
'half-bottom-left',
|
|
28
|
+
'half-bottom-right',
|
|
19
29
|
];
|
|
20
30
|
const PREVIEW_SIZES = [96, 48, 24];
|
|
21
31
|
const NO_LAYERS = [];
|
|
22
|
-
/**
|
|
23
|
-
|
|
24
|
-
|
|
32
|
+
/** What a colour field shows while the colour is unset (follows the parent). */
|
|
33
|
+
const AUTO_SWATCH = '#808080';
|
|
34
|
+
/** The layers in tree order (each followed by what sits in it), with their depth. */
|
|
35
|
+
function walk(layers) {
|
|
36
|
+
const out = [];
|
|
37
|
+
const visit = (layer, depth) => {
|
|
38
|
+
out.push({ layer, depth });
|
|
39
|
+
for (const child of iconStack.stackChildrenOf(layers, layer.id)) {
|
|
40
|
+
visit(child, depth + 1);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
if (layers[0]) {
|
|
44
|
+
visit(layers[0], 0);
|
|
45
|
+
}
|
|
46
|
+
return out;
|
|
47
|
+
}
|
|
48
|
+
/** Removes a layer; what sat in it moves up into its parent (a removed base hands over to its first child). */
|
|
49
|
+
function removeLayer(layers, id) {
|
|
50
|
+
const base = layers[0];
|
|
25
51
|
if (!base) {
|
|
26
|
-
return
|
|
52
|
+
return [];
|
|
27
53
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
54
|
+
if (id !== base.id) {
|
|
55
|
+
const gone = layers.find((l) => l.id === id);
|
|
56
|
+
const up = gone ? iconStack.stackParentId(layers, gone) : undefined;
|
|
57
|
+
return layers
|
|
58
|
+
.filter((l) => l.id !== id)
|
|
59
|
+
.map((l) => (l.parent === id ? { ...l, parent: up === base.id ? undefined : up } : l));
|
|
33
60
|
}
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
61
|
+
const [heir, ...siblings] = iconStack.stackChildrenOf(layers, base.id);
|
|
62
|
+
if (!heir) {
|
|
63
|
+
return [];
|
|
64
|
+
}
|
|
65
|
+
const newBase = {
|
|
66
|
+
...heir,
|
|
67
|
+
parent: undefined,
|
|
68
|
+
align: undefined,
|
|
69
|
+
size: undefined,
|
|
70
|
+
rotation: undefined,
|
|
71
|
+
};
|
|
45
72
|
return [
|
|
46
|
-
|
|
47
|
-
...
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
73
|
+
newBase,
|
|
74
|
+
...layers
|
|
75
|
+
.filter((l) => l.id !== base.id && l.id !== heir.id)
|
|
76
|
+
.map((l) => siblings.includes(l)
|
|
77
|
+
? { ...l, parent: heir.id }
|
|
78
|
+
: l.parent === heir.id
|
|
79
|
+
? { ...l, parent: undefined }
|
|
80
|
+
: l),
|
|
81
|
+
];
|
|
82
|
+
}
|
|
83
|
+
/** A colour of the layer's own, or "auto" — following its parent — until one is picked. */
|
|
84
|
+
function ColorField({ label, value, onChange, autoLabel, resetLabel }) {
|
|
85
|
+
return (jsxRuntime.jsxs("span", { className: "nice-icon-stacker__color", children: [jsxRuntime.jsxs("label", { className: "nice-icon-stacker__field", children: [jsxRuntime.jsx("span", { children: label }), jsxRuntime.jsx("input", { type: "color", value: value !== null && value !== void 0 ? value : AUTO_SWATCH, onChange: (e) => onChange(e.target.value) })] }), value ? (jsxRuntime.jsx("button", { type: "button", className: "nice-icon-stacker__reset", onClick: () => onChange(undefined), "aria-label": resetLabel, title: resetLabel, children: jsxRuntime.jsx(navigation.NtdClose, { size: 12 }) })) : (jsxRuntime.jsx("small", { className: "nice-icon-stacker__auto", children: autoLabel }))] }));
|
|
51
86
|
}
|
|
52
87
|
/**
|
|
53
88
|
* NiceIconStacker (1.3.5) — builds an icon stack: the base icon and the icons stacked in it, each
|
|
54
|
-
* with its position, size and
|
|
55
|
-
*
|
|
89
|
+
* with its position, size, turn and colours, nested any number of levels deep (1.3.6) with text
|
|
90
|
+
* innermost — its size, colour and the value it is bound to — shown live at three sizes and as the
|
|
91
|
+
* JSX that draws it. The "add" button puts `current` into the selected layer and selects it, so
|
|
92
|
+
* adding again goes one level deeper; select an outer layer to add side by side. Layers leave one
|
|
93
|
+
* by one or all at once. `NiceIconStack` draws a stack built here from data.
|
|
56
94
|
*/
|
|
57
|
-
function NiceIconStacker({ icons, layers: layersProp, defaultLayers, onLayersChange, current, previewProps, className,
|
|
58
|
-
var _a;
|
|
95
|
+
const NiceIconStacker = React.forwardRef(function NiceIconStacker({ icons, layers: layersProp, defaultLayers, onLayersChange, current, previewProps, className }, ref) {
|
|
96
|
+
var _a, _b, _c, _d;
|
|
59
97
|
const { t } = uiCore.useNiceTranslation();
|
|
60
98
|
const [layers, setLayers] = uiCore.useControllableState({
|
|
61
99
|
value: layersProp,
|
|
@@ -65,25 +103,45 @@ function NiceIconStacker({ icons, layers: layersProp, defaultLayers, onLayersCha
|
|
|
65
103
|
componentName: 'NiceIconStacker',
|
|
66
104
|
});
|
|
67
105
|
const nextId = React.useRef(0);
|
|
106
|
+
const targetGroup = React.useId();
|
|
68
107
|
const [copied, setCopied] = React.useState(false);
|
|
108
|
+
// The layer the next icon goes into — the newest by default, so adding nests deeper.
|
|
109
|
+
const [selected, setSelected] = React.useState(undefined);
|
|
110
|
+
const base = layers[0];
|
|
111
|
+
const target = (_b = (_a = layers.find((l) => l.id === selected)) !== null && _a !== void 0 ? _a : layers[layers.length - 1]) !== null && _b !== void 0 ? _b : undefined;
|
|
69
112
|
const resolve = React.useCallback((name) => (typeof icons === 'function' ? icons(name) : icons[name]), [icons]);
|
|
70
|
-
const
|
|
71
|
-
const code = React.useMemo(() => niceIconStackCode(layers), [layers]);
|
|
113
|
+
const tree = React.useMemo(() => walk(layers), [layers]);
|
|
114
|
+
const code = React.useMemo(() => iconStack.niceIconStackCode(layers), [layers]);
|
|
72
115
|
const update = (id, patch) => setLayers((prev) => prev.map((l) => (l.id === id ? { ...l, ...patch } : l)));
|
|
73
116
|
const add = () => {
|
|
74
117
|
if (!current) {
|
|
75
118
|
return;
|
|
76
119
|
}
|
|
77
120
|
nextId.current += 1;
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
121
|
+
const id = `${current.icon}-${Date.now().toString(36)}-${nextId.current}`;
|
|
122
|
+
setLayers((prev) => {
|
|
123
|
+
const into = prev.find((l) => l.id === (target === null || target === void 0 ? void 0 : target.id));
|
|
124
|
+
const layer = {
|
|
125
|
+
id,
|
|
126
|
+
icon: current.icon,
|
|
127
|
+
variant: current.variant,
|
|
128
|
+
...(into && into !== prev[0] ? { parent: into.id } : {}),
|
|
129
|
+
};
|
|
130
|
+
return [...prev, layer];
|
|
131
|
+
});
|
|
132
|
+
setSelected(id);
|
|
133
|
+
};
|
|
134
|
+
const remove = (id) => {
|
|
135
|
+
setLayers((prev) => removeLayer(prev, id));
|
|
136
|
+
if (selected === id) {
|
|
137
|
+
setSelected(undefined);
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
const clear = () => {
|
|
141
|
+
setLayers(NO_LAYERS);
|
|
142
|
+
setSelected(undefined);
|
|
84
143
|
};
|
|
85
|
-
|
|
86
|
-
const clear = () => setLayers(NO_LAYERS);
|
|
144
|
+
React.useImperativeHandle(ref, () => ({ add, clear }));
|
|
87
145
|
const copy = () => {
|
|
88
146
|
var _a;
|
|
89
147
|
void ((_a = navigator.clipboard) === null || _a === void 0 ? void 0 : _a.writeText(code).then(() => {
|
|
@@ -91,30 +149,36 @@ function NiceIconStacker({ icons, layers: layersProp, defaultLayers, onLayersCha
|
|
|
91
149
|
window.setTimeout(() => setCopied(false), 1500);
|
|
92
150
|
}));
|
|
93
151
|
};
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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;
|
|
152
|
+
const autoLabel = t('iconStacker.auto', 'auto');
|
|
153
|
+
const resetLabel = t('iconStacker.reset', 'Back to auto');
|
|
154
|
+
const addLabel = base && target
|
|
155
|
+
? t('iconStacker.addInto', 'Add {icon} into {target}')
|
|
156
|
+
.replace('{icon}', (_c = current === null || current === void 0 ? void 0 : current.icon) !== null && _c !== void 0 ? _c : '')
|
|
157
|
+
.replace('{target}', target.icon)
|
|
158
|
+
: t('iconStacker.add', 'Add {icon} to the stack').replace('{icon}', (_d = current === null || current === void 0 ? void 0 : current.icon) !== null && _d !== void 0 ? _d : '');
|
|
159
|
+
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 }), " ", addLabel] }), jsxRuntime.jsxs("button", { type: "button", onClick: clear, disabled: layers.length === 0, children: [jsxRuntime.jsx(actions.NtdTrash, { size: 14 }), " ", t('iconStacker.clear', 'Clear the stack')] })] }), !base ? (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: [iconStack.renderIconStack(layers, icons, {
|
|
160
|
+
baseProps: { ...previewProps, size: s, title: base.icon },
|
|
161
|
+
}), jsxRuntime.jsxs("small", { children: [s, "px"] })] }, s))) }), jsxRuntime.jsx("ol", { className: "nice-icon-stacker__layers", "aria-label": t('iconStacker.layers', 'Layers'), children: tree.map(({ layer: l, depth }) => {
|
|
162
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
108
163
|
const Icon = resolve(l.icon);
|
|
109
|
-
const isBase =
|
|
164
|
+
const isBase = l === base;
|
|
110
165
|
const align = (_a = l.align) !== null && _a !== void 0 ? _a : 'center';
|
|
111
166
|
const share = (_b = l.size) !== null && _b !== void 0 ? _b : stacking.defaultStackSize(align);
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
167
|
+
const innermost = iconStack.stackChildrenOf(layers, l.id).length === 0;
|
|
168
|
+
const textSize = (_c = l.textSize) !== null && _c !== void 0 ? _c : stacking.defaultTextSize(((_d = l.text) !== null && _d !== void 0 ? _d : '').length || 1);
|
|
169
|
+
const bindingInvalid = !!l.textBinding && !iconStack.isValidTextBinding(l.textBinding);
|
|
170
|
+
const depthStyle = {
|
|
171
|
+
['--nice-icon-stacker-depth']: String(depth),
|
|
172
|
+
};
|
|
173
|
+
const label = (key, fallback) => t(key, fallback).replace('{icon}', l.icon);
|
|
174
|
+
return (jsxRuntime.jsxs("li", { className: "nice-icon-stacker__layer", style: depthStyle, children: [jsxRuntime.jsxs("div", { className: "nice-icon-stacker__layer-row", children: [jsxRuntime.jsx("input", { type: "radio", name: targetGroup, checked: (target === null || target === void 0 ? void 0 : target.id) === l.id, onChange: () => setSelected(l.id), "aria-label": label('iconStacker.target', 'Add the next icon into {icon}'), title: label('iconStacker.target', 'Add the next icon into {icon}') }), 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.jsx("button", { type: "button", className: "nice-icon-stacker__remove", onClick: () => remove(l.id), "aria-label": label('iconStacker.remove', 'Remove {icon}'), title: label('iconStacker.remove', 'Remove {icon}'), children: jsxRuntime.jsx(actions.NtdTrash, { size: 14 }) })] }), jsxRuntime.jsxs("details", { className: "nice-icon-stacker__more", children: [jsxRuntime.jsx("summary", { children: t('iconStacker.more', 'More') }), jsxRuntime.jsxs("div", { className: "nice-icon-stacker__more-body", children: [!isBase && (jsxRuntime.jsxs("label", { className: "nice-icon-stacker__field", children: [jsxRuntime.jsx("span", { children: t('iconStacker.rotation', 'Turn') }), jsxRuntime.jsx("input", { type: "range", min: -180, max: 180, step: 15, value: (_e = l.rotation) !== null && _e !== void 0 ? _e : 0, onChange: (e) => update(l.id, { rotation: Number(e.target.value) || undefined }) }), jsxRuntime.jsxs("output", { children: [(_f = l.rotation) !== null && _f !== void 0 ? _f : 0, "\u00B0"] })] })), jsxRuntime.jsx(ColorField, { label: t('iconStacker.primary', 'Primary'), value: l.primaryColor, onChange: (v) => update(l.id, { primaryColor: v }), autoLabel: autoLabel, resetLabel: resetLabel }), jsxRuntime.jsx(ColorField, { label: t('iconStacker.secondary', 'Secondary'), value: l.secondaryColor, onChange: (v) => update(l.id, { secondaryColor: v }), autoLabel: autoLabel, resetLabel: resetLabel }), jsxRuntime.jsx(ColorField, { label: t('iconStacker.accent', 'Accent'), value: l.accentColor, onChange: (v) => update(l.id, { accentColor: v }), autoLabel: autoLabel, resetLabel: resetLabel })] })] }), (innermost || l.text || l.textBinding) && (jsxRuntime.jsxs("div", { className: "nice-icon-stacker__text", children: [jsxRuntime.jsxs("label", { className: "nice-icon-stacker__field", children: [jsxRuntime.jsx("span", { children: t('iconStacker.text', 'Text') }), jsxRuntime.jsx("input", { type: "text", value: (_g = l.text) !== null && _g !== void 0 ? _g : '', maxLength: 4, onChange: (e) => update(l.id, { text: e.target.value || undefined }) })] }), jsxRuntime.jsxs("label", { className: "nice-icon-stacker__field", children: [jsxRuntime.jsx("span", { children: t('iconStacker.textSize', 'Text size') }), jsxRuntime.jsx("input", { type: "range", min: 3, max: 14, step: 0.5, value: textSize, onChange: (e) => update(l.id, { textSize: Number(e.target.value) }) }), jsxRuntime.jsx("output", { children: textSize }), l.textSize !== undefined && (jsxRuntime.jsx("button", { type: "button", className: "nice-icon-stacker__reset", onClick: () => update(l.id, { textSize: undefined }), "aria-label": resetLabel, title: resetLabel, children: jsxRuntime.jsx(navigation.NtdClose, { size: 12 }) }))] }), jsxRuntime.jsx(ColorField, { label: t('iconStacker.textColor', 'Text colour'), value: l.textColor, onChange: (v) => update(l.id, { textColor: v }), autoLabel: autoLabel, resetLabel: resetLabel }), jsxRuntime.jsxs("label", { className: "nice-icon-stacker__field", children: [jsxRuntime.jsx("span", { children: t('iconStacker.bind', 'Bind to') }), jsxRuntime.jsx("input", { type: "text", value: (_h = l.textBinding) !== null && _h !== void 0 ? _h : '', "aria-invalid": bindingInvalid || undefined, title: t('iconStacker.bindHint', 'The name of the value the text shows, e.g. count — the text above is a sample'), onChange: (e) => update(l.id, { textBinding: e.target.value.trim() || undefined }) })] })] }))] }, l.id));
|
|
175
|
+
}) }), 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
|
|
176
|
+
? t('iconStacker.copied', 'Copied')
|
|
177
|
+
: t('iconStacker.copy', 'Copy the code')] })] })] }))] }));
|
|
178
|
+
});
|
|
115
179
|
if (process.env.NODE_ENV !== 'production') {
|
|
116
180
|
NiceIconStacker.displayName = 'NiceIconStacker';
|
|
117
181
|
}
|
|
118
182
|
|
|
183
|
+
exports.niceIconStackCode = iconStack.niceIconStackCode;
|
|
119
184
|
exports.NiceIconStacker = NiceIconStacker;
|
|
120
|
-
exports.niceIconStackCode = niceIconStackCode;
|
package/dist/cjs/client.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
var animationControls = require('./animationControls.js');
|
|
4
4
|
var NiceIconPicker = require('./NiceIconPicker.js');
|
|
5
5
|
var NiceIconStacker = require('./NiceIconStacker.js');
|
|
6
|
+
var iconStack = require('./iconStack.js');
|
|
6
7
|
var NtdIconMorph = require('./NtdIconMorph.js');
|
|
7
8
|
var NtdIconBadge = require('./NtdIconBadge.js');
|
|
8
9
|
var smilAnimations = require('./smilAnimations.js');
|
|
@@ -20,7 +21,10 @@ exports.createVisibilityAnimation = animationControls.createVisibilityAnimation;
|
|
|
20
21
|
exports.useAnimationSequence = animationControls.useAnimationSequence;
|
|
21
22
|
exports.NiceIconPicker = NiceIconPicker.NiceIconPicker;
|
|
22
23
|
exports.NiceIconStacker = NiceIconStacker.NiceIconStacker;
|
|
23
|
-
exports.
|
|
24
|
+
exports.NiceIconStack = iconStack.NiceIconStack;
|
|
25
|
+
exports.isValidTextBinding = iconStack.isValidTextBinding;
|
|
26
|
+
exports.niceIconStackCode = iconStack.niceIconStackCode;
|
|
27
|
+
exports.renderIconStack = iconStack.renderIconStack;
|
|
24
28
|
exports.NtdIconMorph = NtdIconMorph.NtdIconMorph;
|
|
25
29
|
exports.NtdMorphPair = NtdIconMorph.NtdMorphPair;
|
|
26
30
|
exports.NtdPathMorph = NtdIconMorph.NtdPathMorph;
|
package/dist/cjs/createIcon.js
CHANGED
|
@@ -120,7 +120,7 @@ function createIcon(options) {
|
|
|
120
120
|
// Transform props
|
|
121
121
|
rotate, flipX, flipY, scale, transition, transformOrigin = 'center center',
|
|
122
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) => {
|
|
123
|
+
children, stackAlign: _stackAlign, stackSize: _stackSize, stackRotation: _stackRotation, textColor, textSize, title, className, style, ...rest }, ref) => {
|
|
124
124
|
var _a, _b, _c, _d, _e, _f, _g;
|
|
125
125
|
const resolvedSize = typeof size === 'number' ? size : ((_a = types.SIZE_MAP[size]) !== null && _a !== void 0 ? _a : 24);
|
|
126
126
|
const gradId = React.useId();
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
4
|
+
var React = require('react');
|
|
5
|
+
|
|
6
|
+
/** A name `textBinding` can take: a JavaScript identifier. */
|
|
7
|
+
function isValidTextBinding(name) {
|
|
8
|
+
return !!name && /^[A-Za-z_$][\w$]*$/.test(name);
|
|
9
|
+
}
|
|
10
|
+
/** The id of the layer `layer` sits in, `undefined` for the base. */
|
|
11
|
+
function stackParentId(layers, layer) {
|
|
12
|
+
var _a, _b;
|
|
13
|
+
return layer === layers[0] ? undefined : ((_a = layer.parent) !== null && _a !== void 0 ? _a : (_b = layers[0]) === null || _b === void 0 ? void 0 : _b.id);
|
|
14
|
+
}
|
|
15
|
+
/** The layers that sit directly in layer `id`, in order. */
|
|
16
|
+
function stackChildrenOf(layers, id) {
|
|
17
|
+
return layers.filter((l) => l !== layers[0] && stackParentId(layers, l) === id);
|
|
18
|
+
}
|
|
19
|
+
const COLOR_PROPS = ['primaryColor', 'secondaryColor', 'accentColor', 'textColor'];
|
|
20
|
+
/** The stack as JSX: every layer wrapping the layers that sit in it, text innermost. */
|
|
21
|
+
function niceIconStackCode(layers) {
|
|
22
|
+
const base = layers[0];
|
|
23
|
+
if (!base) {
|
|
24
|
+
return '';
|
|
25
|
+
}
|
|
26
|
+
const bound = layers.filter((l) => isValidTextBinding(l.textBinding));
|
|
27
|
+
const write = (layer, parent, indent) => {
|
|
28
|
+
var _a;
|
|
29
|
+
const attrs = [
|
|
30
|
+
parent ? `stackAlign="${(_a = layer.align) !== null && _a !== void 0 ? _a : 'center'}"` : '',
|
|
31
|
+
parent && layer.size !== undefined ? `stackSize={${layer.size}}` : '',
|
|
32
|
+
parent && layer.rotation ? `stackRotation={${layer.rotation}}` : '',
|
|
33
|
+
layer.variant && layer.variant !== (parent === null || parent === void 0 ? void 0 : parent.variant) ? `variant="${layer.variant}"` : '',
|
|
34
|
+
...COLOR_PROPS.map((p) => (layer[p] ? `${p}="${layer[p]}"` : '')),
|
|
35
|
+
layer.textSize !== undefined ? `textSize={${layer.textSize}}` : '',
|
|
36
|
+
]
|
|
37
|
+
.filter(Boolean)
|
|
38
|
+
.join(' ');
|
|
39
|
+
const open = `${layer.icon}${attrs ? ` ${attrs}` : ''}`;
|
|
40
|
+
const text = isValidTextBinding(layer.textBinding) ? `{${layer.textBinding}}` : layer.text;
|
|
41
|
+
const inner = stackChildrenOf(layers, layer.id);
|
|
42
|
+
if (inner.length === 0) {
|
|
43
|
+
return text ? `${indent}<${open}>${text}</${layer.icon}>` : `${indent}<${open} />`;
|
|
44
|
+
}
|
|
45
|
+
return [
|
|
46
|
+
`${indent}<${open}>`,
|
|
47
|
+
...(text ? [`${indent} ${text}`] : []),
|
|
48
|
+
...inner.map((child) => write(child, layer, `${indent} `)),
|
|
49
|
+
`${indent}</${layer.icon}>`,
|
|
50
|
+
].join('\n');
|
|
51
|
+
};
|
|
52
|
+
const notes = bound.map((l) => `// {${l.textBinding}}: bound text${l.text ? `, e.g. "${l.text}"` : ''}`);
|
|
53
|
+
return [...notes, write(base, undefined, '')].join('\n');
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* The stack drawn as icons — the base wrapping the layers that sit in it, text innermost (1.3.6).
|
|
57
|
+
* One SVG, as nesting the icons by hand would give.
|
|
58
|
+
*/
|
|
59
|
+
function renderIconStack(layers, icons, { baseProps, values } = {}) {
|
|
60
|
+
const resolve = (name) => (typeof icons === 'function' ? icons(name) : icons[name]);
|
|
61
|
+
const draw = (layer, parent) => {
|
|
62
|
+
var _a, _b;
|
|
63
|
+
const Icon = resolve(layer.icon);
|
|
64
|
+
if (!Icon) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
const bound = isValidTextBinding(layer.textBinding) ? values === null || values === void 0 ? void 0 : values[layer.textBinding] : undefined;
|
|
68
|
+
const text = bound !== null && bound !== void 0 ? bound : layer.text;
|
|
69
|
+
const own = {
|
|
70
|
+
primaryColor: layer.primaryColor,
|
|
71
|
+
secondaryColor: layer.secondaryColor,
|
|
72
|
+
accentColor: layer.accentColor,
|
|
73
|
+
textColor: layer.textColor,
|
|
74
|
+
textSize: layer.textSize,
|
|
75
|
+
};
|
|
76
|
+
const inner = stackChildrenOf(layers, layer.id).map((child) => draw(child, layer));
|
|
77
|
+
if (!parent) {
|
|
78
|
+
const props = { ...baseProps };
|
|
79
|
+
for (const [k, v] of Object.entries(own)) {
|
|
80
|
+
if (v !== undefined) {
|
|
81
|
+
props[k] = v;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return (React.createElement(Icon, { ...props, key: layer.id, variant: (_a = layer.variant) !== null && _a !== void 0 ? _a : baseProps === null || baseProps === void 0 ? void 0 : baseProps.variant },
|
|
85
|
+
inner,
|
|
86
|
+
text));
|
|
87
|
+
}
|
|
88
|
+
return (React.createElement(Icon, { ...own, key: layer.id, stackAlign: (_b = layer.align) !== null && _b !== void 0 ? _b : 'center', stackSize: layer.size, stackRotation: layer.rotation, variant: layer.variant !== parent.variant ? layer.variant : undefined },
|
|
89
|
+
inner,
|
|
90
|
+
text));
|
|
91
|
+
};
|
|
92
|
+
return layers[0] ? draw(layers[0], undefined) : null;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* NiceIconStack (1.3.6) — draws a stack kept as data (NiceIconStacker's layers) as one icon, its
|
|
96
|
+
* bound text filled from `values`: `<NiceIconStack layers={stack} icons={icons} values={{ count }} />`.
|
|
97
|
+
* The rest of the props go to the base icon.
|
|
98
|
+
*/
|
|
99
|
+
function NiceIconStack({ layers, icons, values, ...baseProps }) {
|
|
100
|
+
return jsxRuntime.jsx(jsxRuntime.Fragment, { children: renderIconStack(layers, icons, { baseProps, values }) });
|
|
101
|
+
}
|
|
102
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
103
|
+
NiceIconStack.displayName = 'NiceIconStack';
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
exports.NiceIconStack = NiceIconStack;
|
|
107
|
+
exports.isValidTextBinding = isValidTextBinding;
|
|
108
|
+
exports.niceIconStackCode = niceIconStackCode;
|
|
109
|
+
exports.renderIconStack = renderIconStack;
|
|
110
|
+
exports.stackChildrenOf = stackChildrenOf;
|
|
111
|
+
exports.stackParentId = stackParentId;
|
package/dist/cjs/index.js
CHANGED
|
@@ -5,6 +5,7 @@ var createIcon = require('./createIcon.js');
|
|
|
5
5
|
var smilAnimations = require('./smilAnimations.js');
|
|
6
6
|
var NiceIconPicker = require('./NiceIconPicker.js');
|
|
7
7
|
var NiceIconStacker = require('./NiceIconStacker.js');
|
|
8
|
+
var iconStack = require('./iconStack.js');
|
|
8
9
|
var animationControls = require('./animationControls.js');
|
|
9
10
|
var figmaExport = require('./figmaExport.js');
|
|
10
11
|
var navigation = require('./icons/navigation.js');
|
|
@@ -70,7 +71,10 @@ exports.generateSmilAnimation = smilAnimations.generateSmilAnimation;
|
|
|
70
71
|
exports.isSmilSupported = smilAnimations.isSmilSupported;
|
|
71
72
|
exports.NiceIconPicker = NiceIconPicker.NiceIconPicker;
|
|
72
73
|
exports.NiceIconStacker = NiceIconStacker.NiceIconStacker;
|
|
73
|
-
exports.
|
|
74
|
+
exports.NiceIconStack = iconStack.NiceIconStack;
|
|
75
|
+
exports.isValidTextBinding = iconStack.isValidTextBinding;
|
|
76
|
+
exports.niceIconStackCode = iconStack.niceIconStackCode;
|
|
77
|
+
exports.renderIconStack = iconStack.renderIconStack;
|
|
74
78
|
exports.AnimationController = animationControls.AnimationController;
|
|
75
79
|
exports.InteractiveIcon = animationControls.InteractiveIcon;
|
|
76
80
|
exports.NiceAnimationController = animationControls.AnimationController;
|