tosijs-ui 1.5.8 → 1.5.10
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/dist/icons.d.ts +0 -2
- package/dist/icons.js +68 -69
- package/dist/iife.js +29 -29
- package/dist/iife.js.map +4 -4
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/llms.txt +2 -2
- package/package.json +1 -1
package/dist/icons.d.ts
CHANGED
|
@@ -8,8 +8,6 @@ export interface IconRule {
|
|
|
8
8
|
prefix: string | RegExp;
|
|
9
9
|
apply: (baseName: string, match: RegExpMatchArray | string, parts: ElementPart[]) => Element | string | null;
|
|
10
10
|
}
|
|
11
|
-
/** Returns icon name safe for suffix concatenation (appends _ if name ends in digit) */
|
|
12
|
-
export declare const safeIconSuffix: (name: string) => string;
|
|
13
11
|
export declare const iconRules: IconRule[];
|
|
14
12
|
export declare const icons: SVGIconMap;
|
|
15
13
|
export declare class SvgIcon extends WebComponent {
|
package/dist/icons.js
CHANGED
|
@@ -7,26 +7,6 @@
|
|
|
7
7
|
<tosi-icon title="tosi-platform" icon="tosiPlatform" style="--tosi-icon-size: 128px"></tosi-icon>
|
|
8
8
|
</div>
|
|
9
9
|
|
|
10
|
-
A library that provides `ElementCreator` functions that produce SVG icons. It leverages `tosijs`'s
|
|
11
|
-
`svgElements` proxy and is intended to address all the key use-cases for SVG icons in web
|
|
12
|
-
applications along with being very easy to extend and maintain.
|
|
13
|
-
|
|
14
|
-
> ### Supported Use Cases
|
|
15
|
-
> - inline SVGs that can be styled by CSS (for buttons, etc.)
|
|
16
|
-
> - allows both stroked and filled icons (unlike font-based systems)
|
|
17
|
-
> - support for color icons (without requiring multiple glyphs perfectly aligned)
|
|
18
|
-
> - icons can be rendered as data urls, e.g. to insert into CSS… (the little `owl` logo rendered under blockquotes is an example)
|
|
19
|
-
> - icon composition: stack, transform, and style icons via naming conventions (e.g. `lock50s75o$shield`)
|
|
20
|
-
> - extensible rules system for custom prefixes and overlays (e.g. `unLock`, `checkFile`)
|
|
21
|
-
> - icon redirects eliminate redundant SVG data (e.g. `chevronDown` → `chevronRight90r`)
|
|
22
|
-
|
|
23
|
-
### Nice Features
|
|
24
|
-
> - no build process magic needed (your icons are "just javascript", no special CSS files needed, no magic glyph mappings). Adding new, or overriding existing, icons is trivial.
|
|
25
|
-
> - icons are just regular SVG, not a specialized subset.
|
|
26
|
-
> - highly optimized and compressible (the code is comparable in size to what you get with a compressed font built from the same icons, except icon fonts don't support strokes, gradients, etc.)
|
|
27
|
-
|
|
28
|
-
## icons
|
|
29
|
-
|
|
30
10
|
`icons` is a proxy that generates an `ElementCreator` for a given icon on demand,
|
|
31
11
|
e.g. `icons.chevronDown()` produces an `<svg>` element containing a downward-pointing chevron
|
|
32
12
|
icon with the class `icon-chevron-down`.
|
|
@@ -351,7 +331,7 @@ that, for example, treat all colored icons inside buttons the same way.
|
|
|
351
331
|
<tosi-icon icon="lock50s75o_10y$shield_brandColorS" size=128></tosi-icon>
|
|
352
332
|
<tosi-icon icon="unLock_brandColorS" size=128></tosi-icon>
|
|
353
333
|
<tosi-icon icon="checkFile" size=128></tosi-icon>
|
|
354
|
-
<tosi-icon icon="
|
|
334
|
+
<tosi-icon icon="spin120Loader40s_30x_brandColorS$cloud" size=128></tosi-icon>
|
|
355
335
|
|
|
356
336
|
### Why?
|
|
357
337
|
|
|
@@ -371,6 +351,12 @@ variations on every icon?
|
|
|
371
351
|
<tosi-icon icon="pin0f_brandColorS" size=64></tosi-icon>
|
|
372
352
|
<tosi-icon icon="unPin_brandColorS" size=64></tosi-icon>
|
|
373
353
|
|
|
354
|
+
And now we can just create icon languages…
|
|
355
|
+
|
|
356
|
+
<tosi-icon icon="checkUsers" size=64></tosi-icon>
|
|
357
|
+
<tosi-icon icon="searchMap" size=64></tosi-icon>
|
|
358
|
+
<tosi-icon icon="cancelHeart" size=64></tosi-icon>
|
|
359
|
+
|
|
374
360
|
### Icon modifier suffixes
|
|
375
361
|
|
|
376
362
|
The suffix system is inspired by tosijs's CSS variable math, where
|
|
@@ -460,6 +446,33 @@ through the full pipeline), an **Element** (used directly), or **null**
|
|
|
460
446
|
},
|
|
461
447
|
})
|
|
462
448
|
|
|
449
|
+
### Building an icon vocabulary
|
|
450
|
+
|
|
451
|
+
You don't need to spell out the DSL every time. The real power is in
|
|
452
|
+
defining named patterns that become your application's icon language.
|
|
453
|
+
For example, a `remove` prefix that overlays a trash icon:
|
|
454
|
+
|
|
455
|
+
iconRules.push({
|
|
456
|
+
prefix: 'remove',
|
|
457
|
+
apply: (baseName) =>
|
|
458
|
+
`trash75o_actionColorS$${baseName}50o`,
|
|
459
|
+
})
|
|
460
|
+
|
|
461
|
+
Now `removeUser`, `removeFile`, `removeProject` all just work — and
|
|
462
|
+
they're consistent. If you redesign the trash icon or change
|
|
463
|
+
`--action-color`, every "remove" icon updates automatically.
|
|
464
|
+
|
|
465
|
+
You can also use `defineIcons` for one-off named compositions:
|
|
466
|
+
|
|
467
|
+
defineIcons({
|
|
468
|
+
cloudSync: 'spin120Loader40s_30x$cloud',
|
|
469
|
+
secureShield: 'lock50s75o_10y$shield',
|
|
470
|
+
newFile: 'plus75o60s25x25y$file',
|
|
471
|
+
})
|
|
472
|
+
|
|
473
|
+
Then just use `icons.cloudSync()` or `<tosi-icon icon="cloudSync">` —
|
|
474
|
+
the composition is invisible to consumers of the icon.
|
|
475
|
+
|
|
463
476
|
### Composites and `svg2DataUrl`
|
|
464
477
|
|
|
465
478
|
Composed icons (stacked, overlay rules) are wrapped in a `<span>`, not
|
|
@@ -473,35 +486,6 @@ with `svg2DataUrl`.
|
|
|
473
486
|
If you ask for an icon that isn't defined, the `icons` proxy will print a warning to console
|
|
474
487
|
and render a `square` (in fact, `icons.square()`) as a fallback.
|
|
475
488
|
|
|
476
|
-
## Why?
|
|
477
|
-
|
|
478
|
-
My evolution has been:
|
|
479
|
-
|
|
480
|
-
1. Using Icomoon.io, which I still think is a solid choice for managing custom icon fonts
|
|
481
|
-
2. Processing Icomoon selection.json files into icon-data and then generating SVGs dynamically
|
|
482
|
-
from the data
|
|
483
|
-
3. Ingesting SVGs directly, with a little cleanup
|
|
484
|
-
|
|
485
|
-
The goal is always to have a single source of truth for icons, no magic or convoluted tooling, and
|
|
486
|
-
be able to quickly and easily add and replace icons, distribute them with components, and
|
|
487
|
-
have no mess or fuss.
|
|
488
|
-
|
|
489
|
-
1. Works well, but…
|
|
490
|
-
- color icons are flaky,
|
|
491
|
-
- doesn't play well with others,
|
|
492
|
-
- can't really distribute the icons with your components.
|
|
493
|
-
- difficult to use icons in CSS `content`
|
|
494
|
-
- impossible to use icons in CSS backgrounds
|
|
495
|
-
2. This is `icons.ts` until just now! Solves all the above, but…
|
|
496
|
-
- no fancy SVG effects, like gradients (goodness knows I experimented with converting CSS gradients to SVG gradients) and, most
|
|
497
|
-
- **strokes** need to be converted to outlines
|
|
498
|
-
- outlined strokes can't be styled the way strokes can
|
|
499
|
-
- blocks use of popular icon libraries
|
|
500
|
-
3. This is how everyone else works, except…
|
|
501
|
-
- no build magic needed: `defineIcons({ myIcon: '<svg....>', ... })`
|
|
502
|
-
- if you want build magic, `icons.js` has no dependencies, finds icons and creates an `icon-data.ts` file.
|
|
503
|
-
- smaller icon files, even though I'm now including more icons (including *all the current* feathericons)
|
|
504
|
-
|
|
505
489
|
## Icon Sources
|
|
506
490
|
|
|
507
491
|
Many of these icons are sourced from [Feather Icons](https://github.com/feathericons/feather), but
|
|
@@ -514,6 +498,12 @@ The remaining icons I have created myself using the excellent but sometimes flaw
|
|
|
514
498
|
[Amadine](https://apps.apple.com/us/app/amadine-vector-design-art/id1339198386?mt=12)
|
|
515
499
|
and before that [Graphic](https://apps.apple.com/us/app/graphic/id404705039?mt=12).
|
|
516
500
|
|
|
501
|
+
### Building icon data
|
|
502
|
+
|
|
503
|
+
Use [make-icon-data](/?make-icon-data.js) to generate icon data from SVG files.
|
|
504
|
+
It supports directional folder conventions to auto-generate rotated and flipped
|
|
505
|
+
variants, eliminating redundant SVGs.
|
|
506
|
+
|
|
517
507
|
### Feather Icons Copyright Notice
|
|
518
508
|
|
|
519
509
|
The MIT License (MIT)
|
|
@@ -573,8 +563,8 @@ export const svg2DataUrl = (icon, fill, stroke, strokeWidth) => {
|
|
|
573
563
|
const text = encodeURIComponent(svg.outerHTML);
|
|
574
564
|
return `url(data:image/svg+xml;charset=UTF-8,${text})`;
|
|
575
565
|
};
|
|
576
|
-
|
|
577
|
-
|
|
566
|
+
// Appends _ to names ending in digits to prevent suffix ambiguity
|
|
567
|
+
const safeIconSuffix = (name) => /\d$/.test(name) ? name + '_' : name;
|
|
578
568
|
export const iconRules = [
|
|
579
569
|
{
|
|
580
570
|
prefix: /^spin(_?\d+)/,
|
|
@@ -582,40 +572,38 @@ export const iconRules = [
|
|
|
582
572
|
const dps = match[1].replace('_', '-');
|
|
583
573
|
const duration = 360 / Math.abs(parseFloat(dps));
|
|
584
574
|
const direction = dps.startsWith('-') ? 'reverse' : 'normal';
|
|
585
|
-
//
|
|
586
|
-
const
|
|
587
|
-
const iconName = parsed ? parsed.baseName : baseName;
|
|
588
|
-
const icon = resolveIcon(iconName, []);
|
|
575
|
+
// Resolve baseName with suffixes intact — they apply to the icon
|
|
576
|
+
const icon = resolveIcon(baseName, []);
|
|
589
577
|
const el = icon;
|
|
590
578
|
if (el.animate) {
|
|
591
|
-
el.
|
|
579
|
+
const base = el.style.transform || '';
|
|
580
|
+
el.animate([
|
|
581
|
+
{ transform: `${base} rotate(0deg)` },
|
|
582
|
+
{ transform: `${base} rotate(360deg)` },
|
|
583
|
+
], {
|
|
592
584
|
duration: duration * 1000,
|
|
593
585
|
iterations: Infinity,
|
|
594
586
|
direction,
|
|
595
587
|
});
|
|
596
588
|
}
|
|
597
|
-
|
|
598
|
-
if (parsed) {
|
|
599
|
-
Object.assign(wrapper.style, parsed.style);
|
|
600
|
-
}
|
|
601
|
-
return wrapper;
|
|
589
|
+
return wrapIcon(baseName, parts, icon);
|
|
602
590
|
},
|
|
603
591
|
},
|
|
604
592
|
{
|
|
605
593
|
prefix: 'un',
|
|
606
|
-
apply: (baseName) => `slash25o$${
|
|
594
|
+
apply: (baseName) => `slash25o$${baseName}75s75o`,
|
|
607
595
|
},
|
|
608
596
|
{
|
|
609
597
|
prefix: 'check',
|
|
610
|
-
apply: (baseName) => `check75o_00aa00S$${
|
|
598
|
+
apply: (baseName) => `check75o_00aa00S$${baseName}75s50o`,
|
|
611
599
|
},
|
|
612
600
|
{
|
|
613
601
|
prefix: 'cancel',
|
|
614
|
-
apply: (baseName) => `x75o_cc0000S$${
|
|
602
|
+
apply: (baseName) => `x75o_cc0000S$${baseName}75s50o`,
|
|
615
603
|
},
|
|
616
604
|
{
|
|
617
605
|
prefix: 'search',
|
|
618
|
-
apply: (baseName) => `search80s30x30y$${
|
|
606
|
+
apply: (baseName) => `search80s30x30y$${baseName}50o`,
|
|
619
607
|
},
|
|
620
608
|
];
|
|
621
609
|
function makeIcon(spec, parts) {
|
|
@@ -699,7 +687,7 @@ function composeIcon(prop, parts) {
|
|
|
699
687
|
// Only apply if baseName can actually resolve to an icon
|
|
700
688
|
if (!canResolve(baseName))
|
|
701
689
|
continue;
|
|
702
|
-
const result = rule.apply(baseName, match, parts);
|
|
690
|
+
const result = rule.apply(safeIconSuffix(baseName), match, parts);
|
|
703
691
|
if (typeof result === 'string')
|
|
704
692
|
return resolveIcon(result, parts);
|
|
705
693
|
if (result)
|
|
@@ -728,8 +716,17 @@ function parseStyleSuffixes(name) {
|
|
|
728
716
|
if (!baseName)
|
|
729
717
|
return null;
|
|
730
718
|
const data = iconData;
|
|
731
|
-
if
|
|
732
|
-
|
|
719
|
+
// Accept if baseName is in iconData, or matches a composition rule prefix
|
|
720
|
+
if (!data[baseName]) {
|
|
721
|
+
const matchesRule = iconRules.some((rule) => {
|
|
722
|
+
if (rule.prefix instanceof RegExp) {
|
|
723
|
+
return rule.prefix.test(baseName);
|
|
724
|
+
}
|
|
725
|
+
return baseName.startsWith(rule.prefix) && baseName.length > rule.prefix.length;
|
|
726
|
+
});
|
|
727
|
+
if (!matchesRule)
|
|
728
|
+
return null;
|
|
729
|
+
}
|
|
733
730
|
const style = {};
|
|
734
731
|
const suffixes = match[0].match(/_?\d+[osxyr]|[01]f|_[a-zA-Z0-9]+[FS]|\d+W/g);
|
|
735
732
|
if (!suffixes)
|
|
@@ -787,6 +784,8 @@ function parseStyleSuffixes(name) {
|
|
|
787
784
|
return { baseName, style };
|
|
788
785
|
}
|
|
789
786
|
function resolveIcon(prop, parts) {
|
|
787
|
+
if (prop.endsWith('_'))
|
|
788
|
+
prop = prop.slice(0, -1);
|
|
790
789
|
const data = iconData;
|
|
791
790
|
// Direct match or redirect chain
|
|
792
791
|
let name = prop;
|