@unocss/preset-icons 0.44.3 → 0.44.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/dist/browser.cjs +19 -0
- package/dist/browser.d.ts +9 -0
- package/dist/browser.mjs +14 -0
- package/dist/chunks/cdn.cjs +440 -0
- package/dist/chunks/cdn.mjs +437 -0
- package/dist/core.cjs +114 -0
- package/dist/core.d.ts +82 -0
- package/dist/core.mjs +110 -0
- package/dist/index.cjs +20 -139
- package/dist/index.d.ts +7 -80
- package/dist/index.mjs +18 -137
- package/package.json +12 -2
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
import { $fetch } from 'ohmyfetch';
|
|
2
|
+
|
|
3
|
+
const defaults = Object.freeze({
|
|
4
|
+
inline: false,
|
|
5
|
+
width: null,
|
|
6
|
+
height: null,
|
|
7
|
+
hAlign: "center",
|
|
8
|
+
vAlign: "middle",
|
|
9
|
+
slice: false,
|
|
10
|
+
hFlip: false,
|
|
11
|
+
vFlip: false,
|
|
12
|
+
rotate: 0
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
const iconDefaults = Object.freeze({
|
|
16
|
+
left: 0,
|
|
17
|
+
top: 0,
|
|
18
|
+
width: 16,
|
|
19
|
+
height: 16,
|
|
20
|
+
rotate: 0,
|
|
21
|
+
vFlip: false,
|
|
22
|
+
hFlip: false
|
|
23
|
+
});
|
|
24
|
+
function fullIcon(data) {
|
|
25
|
+
return { ...iconDefaults, ...data };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function mergeIconData(icon, alias) {
|
|
29
|
+
const result = { ...icon };
|
|
30
|
+
for (const key in iconDefaults) {
|
|
31
|
+
const prop = key;
|
|
32
|
+
if (alias[prop] !== void 0) {
|
|
33
|
+
const value = alias[prop];
|
|
34
|
+
if (result[prop] === void 0) {
|
|
35
|
+
result[prop] = value;
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
switch (prop) {
|
|
39
|
+
case "rotate":
|
|
40
|
+
result[prop] = (result[prop] + value) % 4;
|
|
41
|
+
break;
|
|
42
|
+
case "hFlip":
|
|
43
|
+
case "vFlip":
|
|
44
|
+
result[prop] = value !== result[prop];
|
|
45
|
+
break;
|
|
46
|
+
default:
|
|
47
|
+
result[prop] = value;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return result;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function getIconData(data, name, full = false) {
|
|
55
|
+
function getIcon(name2, iteration) {
|
|
56
|
+
if (data.icons[name2] !== void 0) {
|
|
57
|
+
return Object.assign({}, data.icons[name2]);
|
|
58
|
+
}
|
|
59
|
+
if (iteration > 5) {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
const aliases = data.aliases;
|
|
63
|
+
if (aliases && aliases[name2] !== void 0) {
|
|
64
|
+
const item = aliases[name2];
|
|
65
|
+
const result2 = getIcon(item.parent, iteration + 1);
|
|
66
|
+
if (result2) {
|
|
67
|
+
return mergeIconData(result2, item);
|
|
68
|
+
}
|
|
69
|
+
return result2;
|
|
70
|
+
}
|
|
71
|
+
const chars = data.chars;
|
|
72
|
+
if (!iteration && chars && chars[name2] !== void 0) {
|
|
73
|
+
return getIcon(chars[name2], iteration + 1);
|
|
74
|
+
}
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
const result = getIcon(name, 0);
|
|
78
|
+
if (result) {
|
|
79
|
+
for (const key in iconDefaults) {
|
|
80
|
+
if (result[key] === void 0 && data[key] !== void 0) {
|
|
81
|
+
result[key] = data[key];
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return result && full ? fullIcon(result) : result;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const unitsSplit = /(-?[0-9.]*[0-9]+[0-9.]*)/g;
|
|
89
|
+
const unitsTest = /^-?[0-9.]*[0-9]+[0-9.]*$/g;
|
|
90
|
+
function calculateSize(size, ratio, precision) {
|
|
91
|
+
if (ratio === 1) {
|
|
92
|
+
return size;
|
|
93
|
+
}
|
|
94
|
+
precision = precision === void 0 ? 100 : precision;
|
|
95
|
+
if (typeof size === "number") {
|
|
96
|
+
return Math.ceil(size * ratio * precision) / precision;
|
|
97
|
+
}
|
|
98
|
+
if (typeof size !== "string") {
|
|
99
|
+
return size;
|
|
100
|
+
}
|
|
101
|
+
const oldParts = size.split(unitsSplit);
|
|
102
|
+
if (oldParts === null || !oldParts.length) {
|
|
103
|
+
return size;
|
|
104
|
+
}
|
|
105
|
+
const newParts = [];
|
|
106
|
+
let code = oldParts.shift();
|
|
107
|
+
let isNumber = unitsTest.test(code);
|
|
108
|
+
while (true) {
|
|
109
|
+
if (isNumber) {
|
|
110
|
+
const num = parseFloat(code);
|
|
111
|
+
if (isNaN(num)) {
|
|
112
|
+
newParts.push(code);
|
|
113
|
+
} else {
|
|
114
|
+
newParts.push(Math.ceil(num * ratio * precision) / precision);
|
|
115
|
+
}
|
|
116
|
+
} else {
|
|
117
|
+
newParts.push(code);
|
|
118
|
+
}
|
|
119
|
+
code = oldParts.shift();
|
|
120
|
+
if (code === void 0) {
|
|
121
|
+
return newParts.join("");
|
|
122
|
+
}
|
|
123
|
+
isNumber = !isNumber;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function preserveAspectRatio(props) {
|
|
128
|
+
let result = "";
|
|
129
|
+
switch (props.hAlign) {
|
|
130
|
+
case "left":
|
|
131
|
+
result += "xMin";
|
|
132
|
+
break;
|
|
133
|
+
case "right":
|
|
134
|
+
result += "xMax";
|
|
135
|
+
break;
|
|
136
|
+
default:
|
|
137
|
+
result += "xMid";
|
|
138
|
+
}
|
|
139
|
+
switch (props.vAlign) {
|
|
140
|
+
case "top":
|
|
141
|
+
result += "YMin";
|
|
142
|
+
break;
|
|
143
|
+
case "bottom":
|
|
144
|
+
result += "YMax";
|
|
145
|
+
break;
|
|
146
|
+
default:
|
|
147
|
+
result += "YMid";
|
|
148
|
+
}
|
|
149
|
+
result += props.slice ? " slice" : " meet";
|
|
150
|
+
return result;
|
|
151
|
+
}
|
|
152
|
+
function iconToSVG(icon, customisations) {
|
|
153
|
+
const box = {
|
|
154
|
+
left: icon.left,
|
|
155
|
+
top: icon.top,
|
|
156
|
+
width: icon.width,
|
|
157
|
+
height: icon.height
|
|
158
|
+
};
|
|
159
|
+
let body = icon.body;
|
|
160
|
+
[icon, customisations].forEach((props) => {
|
|
161
|
+
const transformations = [];
|
|
162
|
+
const hFlip = props.hFlip;
|
|
163
|
+
const vFlip = props.vFlip;
|
|
164
|
+
let rotation = props.rotate;
|
|
165
|
+
if (hFlip) {
|
|
166
|
+
if (vFlip) {
|
|
167
|
+
rotation += 2;
|
|
168
|
+
} else {
|
|
169
|
+
transformations.push("translate(" + (box.width + box.left).toString() + " " + (0 - box.top).toString() + ")");
|
|
170
|
+
transformations.push("scale(-1 1)");
|
|
171
|
+
box.top = box.left = 0;
|
|
172
|
+
}
|
|
173
|
+
} else if (vFlip) {
|
|
174
|
+
transformations.push("translate(" + (0 - box.left).toString() + " " + (box.height + box.top).toString() + ")");
|
|
175
|
+
transformations.push("scale(1 -1)");
|
|
176
|
+
box.top = box.left = 0;
|
|
177
|
+
}
|
|
178
|
+
let tempValue;
|
|
179
|
+
if (rotation < 0) {
|
|
180
|
+
rotation -= Math.floor(rotation / 4) * 4;
|
|
181
|
+
}
|
|
182
|
+
rotation = rotation % 4;
|
|
183
|
+
switch (rotation) {
|
|
184
|
+
case 1:
|
|
185
|
+
tempValue = box.height / 2 + box.top;
|
|
186
|
+
transformations.unshift("rotate(90 " + tempValue.toString() + " " + tempValue.toString() + ")");
|
|
187
|
+
break;
|
|
188
|
+
case 2:
|
|
189
|
+
transformations.unshift("rotate(180 " + (box.width / 2 + box.left).toString() + " " + (box.height / 2 + box.top).toString() + ")");
|
|
190
|
+
break;
|
|
191
|
+
case 3:
|
|
192
|
+
tempValue = box.width / 2 + box.left;
|
|
193
|
+
transformations.unshift("rotate(-90 " + tempValue.toString() + " " + tempValue.toString() + ")");
|
|
194
|
+
break;
|
|
195
|
+
}
|
|
196
|
+
if (rotation % 2 === 1) {
|
|
197
|
+
if (box.left !== 0 || box.top !== 0) {
|
|
198
|
+
tempValue = box.left;
|
|
199
|
+
box.left = box.top;
|
|
200
|
+
box.top = tempValue;
|
|
201
|
+
}
|
|
202
|
+
if (box.width !== box.height) {
|
|
203
|
+
tempValue = box.width;
|
|
204
|
+
box.width = box.height;
|
|
205
|
+
box.height = tempValue;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
if (transformations.length) {
|
|
209
|
+
body = '<g transform="' + transformations.join(" ") + '">' + body + "</g>";
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
let width, height;
|
|
213
|
+
if (customisations.width === null && customisations.height === null) {
|
|
214
|
+
height = "1em";
|
|
215
|
+
width = calculateSize(height, box.width / box.height);
|
|
216
|
+
} else if (customisations.width !== null && customisations.height !== null) {
|
|
217
|
+
width = customisations.width;
|
|
218
|
+
height = customisations.height;
|
|
219
|
+
} else if (customisations.height !== null) {
|
|
220
|
+
height = customisations.height;
|
|
221
|
+
width = calculateSize(height, box.width / box.height);
|
|
222
|
+
} else {
|
|
223
|
+
width = customisations.width;
|
|
224
|
+
height = calculateSize(width, box.height / box.width);
|
|
225
|
+
}
|
|
226
|
+
if (width === "auto") {
|
|
227
|
+
width = box.width;
|
|
228
|
+
}
|
|
229
|
+
if (height === "auto") {
|
|
230
|
+
height = box.height;
|
|
231
|
+
}
|
|
232
|
+
width = typeof width === "string" ? width : width.toString() + "";
|
|
233
|
+
height = typeof height === "string" ? height : height.toString() + "";
|
|
234
|
+
const result = {
|
|
235
|
+
attributes: {
|
|
236
|
+
width,
|
|
237
|
+
height,
|
|
238
|
+
preserveAspectRatio: preserveAspectRatio(customisations),
|
|
239
|
+
viewBox: box.left.toString() + " " + box.top.toString() + " " + box.width.toString() + " " + box.height.toString()
|
|
240
|
+
},
|
|
241
|
+
body
|
|
242
|
+
};
|
|
243
|
+
if (customisations.inline) {
|
|
244
|
+
result.inline = true;
|
|
245
|
+
}
|
|
246
|
+
return result;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function trimSVG(str) {
|
|
250
|
+
return str.replace(/(["';{}><])\s*\n\s*/g, "$1").replace(/\s*\n\s*/g, " ").replace(/\s+"/g, '"').replace(/="\s+/g, '="').trim();
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const svgWidthRegex = /width\s*=\s*["'](\w+)["']/;
|
|
254
|
+
const svgHeightRegex = /height\s*=\s*["'](\w+)["']/;
|
|
255
|
+
function configureSvgSize(svg, props, scale) {
|
|
256
|
+
const svgNode = svg.slice(0, svg.indexOf(">"));
|
|
257
|
+
let result = svgWidthRegex.exec(svgNode);
|
|
258
|
+
const w = result != null;
|
|
259
|
+
if (typeof props.width === "undefined" || props.width === null) {
|
|
260
|
+
if (typeof scale === "number") {
|
|
261
|
+
props.width = `${scale}em`;
|
|
262
|
+
} else if (result) {
|
|
263
|
+
props.width = result[1];
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
result = svgHeightRegex.exec(svgNode);
|
|
267
|
+
const h = result != null;
|
|
268
|
+
if (typeof props.height === "undefined" || props.height === null) {
|
|
269
|
+
if (typeof scale === "number") {
|
|
270
|
+
props.height = `${scale}em`;
|
|
271
|
+
} else if (result) {
|
|
272
|
+
props.height = result[1];
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
return [w, h];
|
|
276
|
+
}
|
|
277
|
+
async function mergeIconProps(svg, collection, icon, options, propsProvider, afterCustomizations) {
|
|
278
|
+
const { scale, addXmlNs = false } = options ?? {};
|
|
279
|
+
const { additionalProps = {}, iconCustomizer } = options?.customizations ?? {};
|
|
280
|
+
const props = await propsProvider?.() ?? {};
|
|
281
|
+
await iconCustomizer?.(collection, icon, props);
|
|
282
|
+
Object.keys(additionalProps).forEach((p) => {
|
|
283
|
+
const v = additionalProps[p];
|
|
284
|
+
if (v !== void 0 && v !== null)
|
|
285
|
+
props[p] = v;
|
|
286
|
+
});
|
|
287
|
+
afterCustomizations?.(props);
|
|
288
|
+
const [widthOnSvg, heightOnSvg] = configureSvgSize(svg, props, scale);
|
|
289
|
+
if (addXmlNs) {
|
|
290
|
+
if (!svg.includes(" xmlns=") && !props["xmlns"]) {
|
|
291
|
+
props["xmlns"] = "http://www.w3.org/2000/svg";
|
|
292
|
+
}
|
|
293
|
+
if (!svg.includes(" xmlns:xlink=") && svg.includes("xlink:") && !props["xmlns:xlink"]) {
|
|
294
|
+
props["xmlns:xlink"] = "http://www.w3.org/1999/xlink";
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
const propsToAdd = Object.keys(props).map((p) => p === "width" && widthOnSvg || p === "height" && heightOnSvg ? null : `${p}="${props[p]}"`).filter((p) => p != null);
|
|
298
|
+
if (propsToAdd.length) {
|
|
299
|
+
svg = svg.replace("<svg ", `<svg ${propsToAdd.join(" ")} `);
|
|
300
|
+
}
|
|
301
|
+
if (options) {
|
|
302
|
+
const { defaultStyle, defaultClass } = options;
|
|
303
|
+
if (defaultClass && !svg.includes(" class=")) {
|
|
304
|
+
svg = svg.replace("<svg ", `<svg class="${defaultClass}" `);
|
|
305
|
+
}
|
|
306
|
+
if (defaultStyle && !svg.includes(" style=")) {
|
|
307
|
+
svg = svg.replace("<svg ", `<svg style="${defaultStyle}" `);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
const usedProps = options?.usedProps;
|
|
311
|
+
if (usedProps) {
|
|
312
|
+
Object.keys(additionalProps).forEach((p) => {
|
|
313
|
+
const v = props[p];
|
|
314
|
+
if (v !== void 0 && v !== null)
|
|
315
|
+
usedProps[p] = v;
|
|
316
|
+
});
|
|
317
|
+
if (typeof props.width !== "undefined" && props.width !== null) {
|
|
318
|
+
usedProps.width = props.width;
|
|
319
|
+
}
|
|
320
|
+
if (typeof props.height !== "undefined" && props.height !== null) {
|
|
321
|
+
usedProps.height = props.height;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
return svg;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
async function getCustomIcon(custom, collection, icon, options) {
|
|
328
|
+
let result;
|
|
329
|
+
if (typeof custom === "function") {
|
|
330
|
+
result = await custom(icon);
|
|
331
|
+
} else {
|
|
332
|
+
const inline = custom[icon];
|
|
333
|
+
result = typeof inline === "function" ? await inline() : inline;
|
|
334
|
+
}
|
|
335
|
+
if (result) {
|
|
336
|
+
const cleanupIdx = result.indexOf("<svg");
|
|
337
|
+
if (cleanupIdx > 0)
|
|
338
|
+
result = result.slice(cleanupIdx);
|
|
339
|
+
const { transform } = options?.customizations ?? {};
|
|
340
|
+
result = typeof transform === "function" ? await transform(result, collection, icon) : result;
|
|
341
|
+
if (!result.startsWith("<svg")) {
|
|
342
|
+
console.warn(`Custom icon "${icon}" in "${collection}" is not a valid SVG`);
|
|
343
|
+
return result;
|
|
344
|
+
}
|
|
345
|
+
return await mergeIconProps(options?.customizations?.trimCustomSvg === true ? trimSVG(result) : result, collection, icon, options, void 0);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
async function searchForIcon(iconSet, collection, ids, options) {
|
|
350
|
+
let iconData;
|
|
351
|
+
const { customize } = options?.customizations ?? {};
|
|
352
|
+
for (const id of ids) {
|
|
353
|
+
iconData = getIconData(iconSet, id, true);
|
|
354
|
+
if (iconData) {
|
|
355
|
+
let defaultCustomizations = { ...defaults };
|
|
356
|
+
if (typeof customize === "function")
|
|
357
|
+
defaultCustomizations = customize(defaultCustomizations);
|
|
358
|
+
const {
|
|
359
|
+
attributes: { width, height, ...restAttributes },
|
|
360
|
+
body
|
|
361
|
+
} = iconToSVG(iconData, defaultCustomizations);
|
|
362
|
+
const scale = options?.scale;
|
|
363
|
+
return await mergeIconProps(`<svg >${body}</svg>`, collection, id, options, () => {
|
|
364
|
+
return { ...restAttributes };
|
|
365
|
+
}, (props) => {
|
|
366
|
+
if (typeof props.width === "undefined" || props.width === null) {
|
|
367
|
+
if (typeof scale === "number") {
|
|
368
|
+
props.width = `${scale}em`;
|
|
369
|
+
} else {
|
|
370
|
+
props.width = width;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
if (typeof props.height === "undefined" || props.height === null) {
|
|
374
|
+
if (typeof scale === "number") {
|
|
375
|
+
props.height = `${scale}em`;
|
|
376
|
+
} else {
|
|
377
|
+
props.height = height;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
const loadIcon = async (collection, icon, options) => {
|
|
386
|
+
const custom = options?.customCollections?.[collection];
|
|
387
|
+
if (custom) {
|
|
388
|
+
if (typeof custom === "function") {
|
|
389
|
+
const result = await custom(icon);
|
|
390
|
+
if (result) {
|
|
391
|
+
if (typeof result === "string") {
|
|
392
|
+
return await getCustomIcon(() => result, collection, icon, options);
|
|
393
|
+
}
|
|
394
|
+
if ("icons" in result) {
|
|
395
|
+
const ids = [
|
|
396
|
+
icon,
|
|
397
|
+
icon.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase(),
|
|
398
|
+
icon.replace(/([a-z])(\d+)/g, "$1-$2")
|
|
399
|
+
];
|
|
400
|
+
return await searchForIcon(result, collection, ids, options);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
} else {
|
|
404
|
+
return await getCustomIcon(custom, collection, icon, options);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
return void 0;
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
const supportedCollection = ["material-symbols", "ic", "mdi", "ph", "ri", "carbon", "bi", "tabler", "ion", "uil", "teenyicons", "clarity", "iconoir", "majesticons", "zondicons", "ant-design", "bx", "bxs", "gg", "cil", "lucide", "pixelarticons", "system-uicons", "ci", "akar-icons", "typcn", "radix-icons", "ep", "mdi-light", "fe", "eos-icons", "line-md", "charm", "prime", "heroicons-outline", "heroicons-solid", "uiw", "uim", "uit", "uis", "maki", "gridicons", "mi", "quill", "gala", "fluent", "icon-park-outline", "icon-park", "vscode-icons", "jam", "codicon", "pepicons", "bytesize", "ei", "fa6-solid", "fa6-regular", "octicon", "ooui", "nimbus", "openmoji", "twemoji", "noto", "noto-v1", "emojione", "emojione-monotone", "emojione-v1", "fxemoji", "bxl", "logos", "simple-icons", "cib", "fa6-brands", "arcticons", "file-icons", "brandico", "entypo-social", "cryptocurrency", "flag", "circle-flags", "flagpack", "cif", "gis", "map", "geo", "fad", "academicons", "wi", "healthicons", "medical-icon", "la", "eva", "dashicons", "flat-color-icons", "entypo", "foundation", "raphael", "icons8", "iwwa", "fa-solid", "fa-regular", "fa-brands", "fa", "fontisto", "icomoon-free", "ps", "subway", "oi", "wpf", "simple-line-icons", "et", "el", "vaadin", "grommet-icons", "whh", "si-glyph", "zmdi", "ls", "bpmn", "flat-ui", "vs", "topcoat", "il", "websymbol", "fontelico", "feather", "mono-icons"];
|
|
411
|
+
function createCDNLoader(cdnBase) {
|
|
412
|
+
const cache = /* @__PURE__ */ new Map();
|
|
413
|
+
function fetchCollection(name) {
|
|
414
|
+
if (!supportedCollection.includes(name))
|
|
415
|
+
return void 0;
|
|
416
|
+
if (!cache.has(name))
|
|
417
|
+
cache.set(name, $fetch(`${cdnBase}@iconify-json/${name}/icons.json`));
|
|
418
|
+
return cache.get(name);
|
|
419
|
+
}
|
|
420
|
+
return async (collection, icon, options) => {
|
|
421
|
+
let result = await loadIcon(collection, icon, options);
|
|
422
|
+
if (result)
|
|
423
|
+
return result;
|
|
424
|
+
const iconSet = await fetchCollection(collection);
|
|
425
|
+
if (iconSet) {
|
|
426
|
+
const ids = [
|
|
427
|
+
icon,
|
|
428
|
+
icon.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase(),
|
|
429
|
+
icon.replace(/([a-z])(\d+)/g, "$1-$2")
|
|
430
|
+
];
|
|
431
|
+
result = await searchForIcon(iconSet, collection, ids, options);
|
|
432
|
+
}
|
|
433
|
+
return result;
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
export { createCDNLoader as c, loadIcon as l };
|
package/dist/core.cjs
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
const core = require('@unocss/core');
|
|
6
|
+
|
|
7
|
+
function encodeSvgForCss(svg) {
|
|
8
|
+
let useSvg = svg.startsWith("<svg>") ? svg.replace("<svg>", "<svg >") : svg;
|
|
9
|
+
if (!useSvg.includes(" xmlns:xlink=") && useSvg.includes(" xlink:")) {
|
|
10
|
+
useSvg = useSvg.replace("<svg ", '<svg xmlns:xlink="http://www.w3.org/1999/xlink" ');
|
|
11
|
+
}
|
|
12
|
+
if (!useSvg.includes(" xmlns=")) {
|
|
13
|
+
useSvg = useSvg.replace("<svg ", '<svg xmlns="http://www.w3.org/2000/svg" ');
|
|
14
|
+
}
|
|
15
|
+
return useSvg.replace(/"/g, "'").replace(/%/g, "%25").replace(/#/g, "%23").replace(/{/g, "%7B").replace(/}/g, "%7D").replace(/</g, "%3C").replace(/>/g, "%3E");
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const COLLECTION_NAME_PARTS_MAX = 3;
|
|
19
|
+
function createPresetIcons(lookupIconLoader) {
|
|
20
|
+
return function presetIcons(options = {}) {
|
|
21
|
+
const {
|
|
22
|
+
scale = 1,
|
|
23
|
+
mode = "auto",
|
|
24
|
+
prefix = "i-",
|
|
25
|
+
warn = false,
|
|
26
|
+
collections: customCollections,
|
|
27
|
+
extraProperties = {},
|
|
28
|
+
customizations = {},
|
|
29
|
+
autoInstall = false,
|
|
30
|
+
layer = "icons",
|
|
31
|
+
unit
|
|
32
|
+
} = options;
|
|
33
|
+
const loaderOptions = {
|
|
34
|
+
addXmlNs: true,
|
|
35
|
+
scale,
|
|
36
|
+
customCollections,
|
|
37
|
+
autoInstall,
|
|
38
|
+
warn: void 0,
|
|
39
|
+
customizations: {
|
|
40
|
+
...customizations,
|
|
41
|
+
additionalProps: { ...extraProperties },
|
|
42
|
+
trimCustomSvg: true,
|
|
43
|
+
async iconCustomizer(collection, icon, props) {
|
|
44
|
+
await customizations.iconCustomizer?.(collection, icon, props);
|
|
45
|
+
if (unit) {
|
|
46
|
+
if (!props.width)
|
|
47
|
+
props.width = `${scale}${unit}`;
|
|
48
|
+
if (!props.height)
|
|
49
|
+
props.height = `${scale}${unit}`;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
let iconLoader;
|
|
55
|
+
return {
|
|
56
|
+
name: "@unocss/preset-icons",
|
|
57
|
+
enforce: "pre",
|
|
58
|
+
options,
|
|
59
|
+
layers: { icons: -30 },
|
|
60
|
+
rules: [[
|
|
61
|
+
/^([a-z0-9:-]+)(?:\?(mask|bg|auto))?$/,
|
|
62
|
+
async ([full, body, _mode = mode]) => {
|
|
63
|
+
let collection = "";
|
|
64
|
+
let name = "";
|
|
65
|
+
let svg;
|
|
66
|
+
iconLoader = iconLoader || await lookupIconLoader(options);
|
|
67
|
+
const usedProps = {};
|
|
68
|
+
if (body.includes(":")) {
|
|
69
|
+
[collection, name] = body.split(":");
|
|
70
|
+
svg = await iconLoader(collection, name, { ...loaderOptions, usedProps });
|
|
71
|
+
} else {
|
|
72
|
+
const parts = body.split(/-/g);
|
|
73
|
+
for (let i = COLLECTION_NAME_PARTS_MAX; i >= 1; i--) {
|
|
74
|
+
collection = parts.slice(0, i).join("-");
|
|
75
|
+
name = parts.slice(i).join("-");
|
|
76
|
+
svg = await iconLoader(collection, name, { ...loaderOptions, usedProps });
|
|
77
|
+
if (svg)
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (!svg) {
|
|
82
|
+
if (warn)
|
|
83
|
+
core.warnOnce(`failed to load icon "${full}"`);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const url = `url("data:image/svg+xml;utf8,${encodeSvgForCss(svg)}")`;
|
|
87
|
+
if (_mode === "auto")
|
|
88
|
+
_mode = svg.includes("currentColor") ? "mask" : "bg";
|
|
89
|
+
if (_mode === "mask") {
|
|
90
|
+
return {
|
|
91
|
+
"--un-icon": url,
|
|
92
|
+
"mask": "var(--un-icon) no-repeat",
|
|
93
|
+
"mask-size": "100% 100%",
|
|
94
|
+
"-webkit-mask": "var(--un-icon) no-repeat",
|
|
95
|
+
"-webkit-mask-size": "100% 100%",
|
|
96
|
+
"background-color": "currentColor",
|
|
97
|
+
...usedProps
|
|
98
|
+
};
|
|
99
|
+
} else {
|
|
100
|
+
return {
|
|
101
|
+
"background": `${url} no-repeat`,
|
|
102
|
+
"background-size": "100% 100%",
|
|
103
|
+
"background-color": "transparent",
|
|
104
|
+
...usedProps
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
{ layer, prefix }
|
|
109
|
+
]]
|
|
110
|
+
};
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
exports.createPresetIcons = createPresetIcons;
|
package/dist/core.d.ts
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { Awaitable, Preset } from '@unocss/core';
|
|
2
|
+
import { CustomIconLoader, InlineCollection, IconCustomizations, UniversalIconLoader } from '@iconify/utils/lib/loader/types';
|
|
3
|
+
import { IconifyJSON } from '@iconify/types';
|
|
4
|
+
|
|
5
|
+
interface IconsOptions {
|
|
6
|
+
/**
|
|
7
|
+
* Scale related to the current font size (1em).
|
|
8
|
+
*
|
|
9
|
+
* @default 1
|
|
10
|
+
*/
|
|
11
|
+
scale?: number;
|
|
12
|
+
/**
|
|
13
|
+
* Mode of generated CSS icons.
|
|
14
|
+
*
|
|
15
|
+
* - `mask` - use background color and the `mask` property for monochrome icons
|
|
16
|
+
* - `background-img` - use background image for the icons, colors are static
|
|
17
|
+
* - `auto` - smartly decide mode between `mask` and `background-img` per icon based on its style
|
|
18
|
+
*
|
|
19
|
+
* @default 'auto'
|
|
20
|
+
* @see https://antfu.me/posts/icons-in-pure-css
|
|
21
|
+
*/
|
|
22
|
+
mode?: 'mask' | 'background-img' | 'auto';
|
|
23
|
+
/**
|
|
24
|
+
* Class prefix for matching icon rules.
|
|
25
|
+
*
|
|
26
|
+
* @default `i-`
|
|
27
|
+
*/
|
|
28
|
+
prefix?: string;
|
|
29
|
+
/**
|
|
30
|
+
* Extra CSS properties applied to the generated CSS
|
|
31
|
+
*
|
|
32
|
+
* @default {}
|
|
33
|
+
*/
|
|
34
|
+
extraProperties?: Record<string, string>;
|
|
35
|
+
/**
|
|
36
|
+
* Emit warning when missing icons are matched
|
|
37
|
+
*
|
|
38
|
+
* @default false
|
|
39
|
+
*/
|
|
40
|
+
warn?: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* In Node.js environment, the preset will search for the installed iconify dataset automatically.
|
|
43
|
+
* When using in the browser, this options is provided to provide dataset with custom loading mechanism.
|
|
44
|
+
*/
|
|
45
|
+
collections?: Record<string, (() => Awaitable<IconifyJSON>) | undefined | CustomIconLoader | InlineCollection>;
|
|
46
|
+
/**
|
|
47
|
+
* Rule layer
|
|
48
|
+
*
|
|
49
|
+
* @default 'icons'
|
|
50
|
+
*/
|
|
51
|
+
layer?: string;
|
|
52
|
+
/**
|
|
53
|
+
* Custom icon customizations.
|
|
54
|
+
*/
|
|
55
|
+
customizations?: Omit<IconCustomizations, 'additionalProps' | 'trimCustomSvg'>;
|
|
56
|
+
/**
|
|
57
|
+
* Auto install icon sources package when the usages is detected
|
|
58
|
+
*
|
|
59
|
+
* **WARNING**: only on `node` environment, on `browser` this option will be ignored.
|
|
60
|
+
*
|
|
61
|
+
* @default false
|
|
62
|
+
*/
|
|
63
|
+
autoInstall?: boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Custom icon unit.
|
|
66
|
+
*
|
|
67
|
+
* @default `em`
|
|
68
|
+
*/
|
|
69
|
+
unit?: string;
|
|
70
|
+
/**
|
|
71
|
+
* Load icons from CDN. Should starts with `https://` and ends with `/`
|
|
72
|
+
*
|
|
73
|
+
* Recommends:
|
|
74
|
+
* - https://esm.sh/
|
|
75
|
+
* - https://cdn.skypack.dev/
|
|
76
|
+
*/
|
|
77
|
+
cdn?: string;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
declare function createPresetIcons(lookupIconLoader: (options: IconsOptions) => Promise<UniversalIconLoader>): (options?: IconsOptions) => Preset;
|
|
81
|
+
|
|
82
|
+
export { IconsOptions, createPresetIcons };
|