@sigx/lynx-icons 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +132 -0
- package/dist/Icon.d.ts +29 -0
- package/dist/defineIconSet-BIFjZq08.js +28 -0
- package/dist/defineIconSet-BIFjZq08.js.map +1 -0
- package/dist/defineIconSet.d.ts +27 -0
- package/dist/defineIconSet.js +2 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +50 -0
- package/dist/index.js.map +1 -0
- package/dist/registry.d.ts +8 -0
- package/dist/stubs/codepoints.d.ts +9 -0
- package/dist/stubs/codepoints.js +6 -0
- package/dist/stubs/codepoints.js.map +1 -0
- package/dist/stubs/codepoints.ts +10 -0
- package/dist/stubs/font-face.css +6 -0
- package/dist/stubs/font-face.css.d.ts +7 -0
- package/dist/stubs/svgs.d.ts +7 -0
- package/dist/stubs/svgs.js +6 -0
- package/dist/stubs/svgs.js.map +1 -0
- package/dist/stubs/svgs.ts +8 -0
- package/dist/types.d.ts +78 -0
- package/package.json +72 -0
- package/signalx-module.json +14 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025-2026 Andreas Ekdahl
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# @sigx/lynx-icons
|
|
2
|
+
|
|
3
|
+
`<Icon set name />` for sigx-lynx, with build-time tree-shaking so only the glyphs you actually render ship in the bundle. Pairs with adapter packages — [`@sigx/lynx-icons-fa-free`](../lynx-icons-fa-free) for Font Awesome Free, [`@sigx/lynx-icons-lucide`](../lynx-icons-lucide) for Lucide — and is wired by [`@sigx/lynx-plugin`](../lynx-plugin)'s icons slice.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @sigx/lynx-icons @sigx/lynx-icons-fa-free
|
|
9
|
+
pnpm add @fortawesome/fontawesome-free @fortawesome/free-solid-svg-icons
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
```ts
|
|
13
|
+
// signalx.config.ts
|
|
14
|
+
import { defineLynxConfig } from '@sigx/lynx-cli/config';
|
|
15
|
+
|
|
16
|
+
export default defineLynxConfig({
|
|
17
|
+
iconSets: [
|
|
18
|
+
{ id: 'fa', source: '@sigx/lynx-icons-fa-free', styles: ['solid'] },
|
|
19
|
+
],
|
|
20
|
+
});
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
`pnpm dev` / `sigx build` does the rest — `@sigx/lynx-plugin` scans your `.tsx` for `<Icon set= name=>` usages, asks the adapter for each glyph, and writes generated codepoint / SVG maps into `node_modules/.cache/sigx-lynx-icons/`. Unreferenced glyphs are never imported.
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
```tsx
|
|
28
|
+
import { Icon } from '@sigx/lynx-icons';
|
|
29
|
+
|
|
30
|
+
<Icon set="fa" name="user" />
|
|
31
|
+
<Icon set="fa" name="house" size={20} color="#0D9488" />
|
|
32
|
+
<Icon set="lucide" name="search" size={16} />
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
| Prop | Type | Default | Notes |
|
|
36
|
+
| --- | --- | --- | --- |
|
|
37
|
+
| `set` | `string` | — | Must match an `iconSets[].id` in `signalx.config.ts` (or a `defineIconSet({ id, … })` call at runtime). |
|
|
38
|
+
| `name` | `string` | — | Glyph name in the set's canonical kebab-case (`chevron-right`, not `chevronRight` / `ChevronRight`). |
|
|
39
|
+
| `size` | `number` | `16` | Both width and height, in CSS px. |
|
|
40
|
+
| `color` | `string` | `'currentColor'` | Substituted into the SVG template at render time (data: URIs don't reliably inherit `currentColor`). |
|
|
41
|
+
| `class` | `string` | — | Forwarded to the host element (`<text>` for font mode, `<image>` for SVG mode, `<view>` for missing glyphs). |
|
|
42
|
+
|
|
43
|
+
The component renders `<text style={{ fontFamily: setId, … }}>{codepoint}</text>` when a codepoint is available (font mode), or an `<image>` wrapping an SVG data URI otherwise (SVG mode). When neither is registered, an empty `<view>` of the same size renders so layout doesn't jump.
|
|
44
|
+
|
|
45
|
+
## Dynamic names
|
|
46
|
+
|
|
47
|
+
The build-time scanner only sees literal `name="…"` strings. Computed names — `<Icon name={state.icon} />` — fall through to the missing-glyph placeholder unless you tell the plugin which glyphs to include.
|
|
48
|
+
|
|
49
|
+
**Known list — enumerate the names**:
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
iconSets: [
|
|
53
|
+
{
|
|
54
|
+
id: 'fa',
|
|
55
|
+
source: '@sigx/lynx-icons-fa-free',
|
|
56
|
+
styles: ['solid'],
|
|
57
|
+
include: ['user', 'house', 'gear'], // force-include for dynamic <Icon name={…}>
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**Unknown list (JSON-driven UIs, server-driven content, etc.) — `include: ['*']`**:
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
iconSets: [
|
|
66
|
+
{
|
|
67
|
+
id: 'fa',
|
|
68
|
+
source: '@sigx/lynx-icons-fa-free',
|
|
69
|
+
styles: ['solid'],
|
|
70
|
+
include: ['*'], // ship the full FA-solid catalog
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Trade-off: shipping a full set noticeably grows the bundle (the showcase goes from ~336 kB → ~2.65 MB when FA solid's ~1 900 glyphs are bundled). Build output prints the exact glyph count so you can audit it:
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
[@sigx/lynx-plugin] icons: fa bundling 1956 glyphs (include: ['*'])
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Use it only on the sets that genuinely need dynamic names — mix per set with the tree-shaken default for everything else. Coming in v1.1: font mode swaps the SVG-per-glyph payload for a single subsetted TTF, which is dramatically smaller for full-catalog scenarios.
|
|
82
|
+
|
|
83
|
+
## Custom sets
|
|
84
|
+
|
|
85
|
+
For one-off in-app icons that don't warrant a full adapter package, use `defineIconSet`:
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
import { defineIconSet } from '@sigx/lynx-icons';
|
|
89
|
+
|
|
90
|
+
defineIconSet({
|
|
91
|
+
id: 'brand',
|
|
92
|
+
glyphs: {
|
|
93
|
+
logo: { svg: { svg: '<svg viewBox="0 0 24 24" fill="__COLOR__"><path d="M3 12L12 3l9 9-9 9z"/></svg>' } },
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// Anywhere:
|
|
98
|
+
<Icon set="brand" name="logo" />
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
`__COLOR__` placeholders in the SVG string get replaced with the user-supplied `color` (or `currentColor`) at render time.
|
|
102
|
+
|
|
103
|
+
## Writing your own adapter
|
|
104
|
+
|
|
105
|
+
Adapter packages are plain Node modules with a default export matching this contract:
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
import type { IconAdapter } from '@sigx/lynx-icons';
|
|
109
|
+
|
|
110
|
+
const adapter: IconAdapter = {
|
|
111
|
+
styles: ['solid'],
|
|
112
|
+
getGlyph(style, name) {
|
|
113
|
+
// Return { codepoint?, svg } or null
|
|
114
|
+
},
|
|
115
|
+
getFontPath(style) {
|
|
116
|
+
// Absolute path to a TTF for font-mode subsetting, or null.
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
export default adapter;
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
The plugin dynamically `import()`s the adapter from the consumer's `node_modules` at build start, then calls `getGlyph` for every scanned `<Icon set= name=>` usage. `getFontPath` is reserved for the v1.1 font-subsetting pipeline and can return `null` for SVG-only sets.
|
|
123
|
+
|
|
124
|
+
## v1 limitations
|
|
125
|
+
|
|
126
|
+
- **Font mode** (build-time TTF subsetting + base64-inlined `@font-face`) ships in v1.1. v1 renders every glyph as an inline SVG `<image>`. The plumbing for `mode: 'font'` is in the schema but currently no-ops.
|
|
127
|
+
- **HMR** for newly-added icon names requires a `pnpm dev` restart. The v1 scanner is a one-shot regex pass at plugin start; v1.1 replaces it with a real SWC-AST Rspack loader.
|
|
128
|
+
- **FA Pro** and **Iconify** adapters are v1.1 follow-ups.
|
|
129
|
+
|
|
130
|
+
## Reference app
|
|
131
|
+
|
|
132
|
+
`examples/showcase/src/screens/Settings.tsx` shows fa-solid, fa-brands, and lucide icons rendered side by side. The generated cache lives at `examples/showcase/node_modules/.cache/sigx-lynx-icons/` after `pnpm build`.
|
package/dist/Icon.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type Define } from '@sigx/lynx';
|
|
2
|
+
import '@sigx/lynx-icons/__font-face.css';
|
|
3
|
+
export type IconProps = Define.Prop<'set', string, true> & Define.Prop<'name', string, true> & Define.Prop<'size', number, false> & Define.Prop<'color', string, false> & Define.Prop<'class', string, false>;
|
|
4
|
+
export declare function sanitizeColor(color: string): string;
|
|
5
|
+
export declare function inlineSvg(template: string, color: string): string;
|
|
6
|
+
/**
|
|
7
|
+
* Render a glyph from a registered icon set.
|
|
8
|
+
*
|
|
9
|
+
* Sets are declared in `signalx.config.ts` via `iconSets: [...]` (build-time,
|
|
10
|
+
* tree-shaken) or via `defineIconSet({ id, glyphs })` (runtime, ad-hoc).
|
|
11
|
+
*
|
|
12
|
+
* Resolution order: **SVG first**, then codepoint, then missing placeholder.
|
|
13
|
+
*
|
|
14
|
+
* - SVG-mode sets render with Lynx's native `<svg content={...}>` element
|
|
15
|
+
* (the engine parses the inline XML; no JSX children, no data: URIs).
|
|
16
|
+
* - Font-mode sets fall back to a single character inside a `<text>` element
|
|
17
|
+
* with a matching `font-family`. The plugin only emits codepoints when the
|
|
18
|
+
* matching `@font-face` has also been registered (v1.1+); v1 always hits
|
|
19
|
+
* the SVG branch.
|
|
20
|
+
* - Missing glyphs render an empty `<view>` of the same size so layout
|
|
21
|
+
* doesn't jump.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```tsx
|
|
25
|
+
* <Icon set="fa" name="user" size={20} color="#333" />
|
|
26
|
+
* <Icon set="lucide" name="search" size={16} />
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export declare const Icon: import("@sigx/runtime-core").ComponentFactory<IconProps, void, {}>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
//#region src/registry.ts
|
|
2
|
+
var e = {}, t = {};
|
|
3
|
+
function n(n) {
|
|
4
|
+
let r = {}, i = {};
|
|
5
|
+
for (let [e, t] of Object.entries(n.glyphs)) t.codepoint !== void 0 && (r[e] = t.codepoint), t.svg && (i[e] = t.svg);
|
|
6
|
+
Object.keys(r).length > 0 && (e[n.id] = r), Object.keys(i).length > 0 && (t[n.id] = i);
|
|
7
|
+
}
|
|
8
|
+
function r(t, n, r) {
|
|
9
|
+
return t[n]?.[r] ?? e[n]?.[r];
|
|
10
|
+
}
|
|
11
|
+
function i(e, n, r) {
|
|
12
|
+
return e[n]?.[r] ?? t[n]?.[r];
|
|
13
|
+
}
|
|
14
|
+
function a(e, t, n, a) {
|
|
15
|
+
let o = i(t, n, a);
|
|
16
|
+
if (o) return { svg: o };
|
|
17
|
+
let s = r(e, n, a);
|
|
18
|
+
if (s !== void 0) return { codepoint: s };
|
|
19
|
+
}
|
|
20
|
+
//#endregion
|
|
21
|
+
//#region src/defineIconSet.ts
|
|
22
|
+
function o(e) {
|
|
23
|
+
return n(e), e;
|
|
24
|
+
}
|
|
25
|
+
//#endregion
|
|
26
|
+
export { a as n, o as t };
|
|
27
|
+
|
|
28
|
+
//# sourceMappingURL=defineIconSet-BIFjZq08.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"defineIconSet-BIFjZq08.js","names":[],"sources":["../src/registry.ts","../src/defineIconSet.ts"],"sourcesContent":["import type { CodepointMap, GlyphSvg, IconSetDef, SvgMap } from './types';\n\nconst runtimeCodepoints: CodepointMap = {};\nconst runtimeSvgs: SvgMap = {};\n\nexport function registerIconSet(def: IconSetDef): void {\n const codepoints: Record<string, number> = {};\n const svgs: Record<string, GlyphSvg> = {};\n for (const [name, g] of Object.entries(def.glyphs)) {\n if (g.codepoint !== undefined) codepoints[name] = g.codepoint;\n if (g.svg) svgs[name] = g.svg;\n }\n if (Object.keys(codepoints).length > 0) runtimeCodepoints[def.id] = codepoints;\n if (Object.keys(svgs).length > 0) runtimeSvgs[def.id] = svgs;\n}\n\nexport function lookupCodepoint(\n buildTime: CodepointMap,\n set: string,\n name: string,\n): number | undefined {\n return buildTime[set]?.[name] ?? runtimeCodepoints[set]?.[name];\n}\n\nexport function lookupSvg(\n buildTime: SvgMap,\n set: string,\n name: string,\n): GlyphSvg | undefined {\n return buildTime[set]?.[name] ?? runtimeSvgs[set]?.[name];\n}\n\nexport function lookupGlyph(\n buildTimeCodepoints: CodepointMap,\n buildTimeSvgs: SvgMap,\n set: string,\n name: string,\n): { codepoint?: number; svg?: GlyphSvg } | undefined {\n const svg = lookupSvg(buildTimeSvgs, set, name);\n if (svg) return { svg };\n\n const codepoint = lookupCodepoint(buildTimeCodepoints, set, name);\n if (codepoint !== undefined) return { codepoint };\n\n return undefined;\n}\n","import { registerIconSet } from './registry';\nimport type { IconSetDef } from './types';\n\n/**\n * Register a custom icon set at module load time.\n *\n * Build-time sets declared in `signalx.config.ts` are auto-detected and\n * tree-shaken; `defineIconSet` is the escape hatch for ad-hoc sets defined\n * directly in app code (e.g. a small private set used in one screen).\n *\n * @example\n * ```ts\n * defineIconSet({\n * id: 'brand',\n * glyphs: {\n * logo: {\n * svg: {\n * svg: '<svg viewBox=\"0 0 24 24\" fill=\"__COLOR__\"><path d=\"M3 12L12 3l9 9-9 9z\"/></svg>',\n * },\n * },\n * },\n * });\n * ```\n *\n * The inner `svg` value is raw SVG markup. `__COLOR__` placeholders in that\n * markup get substituted with the user's `color` prop at render time.\n */\nexport function defineIconSet(def: IconSetDef): IconSetDef {\n registerIconSet(def);\n return def;\n}\n\nexport type { IconSetDef } from './types';\n"],"mappings":";AAEA,IAAM,IAAkC,EAAE,EACpC,IAAsB,EAAE;AAE9B,SAAgB,EAAgB,GAAuB;CACnD,IAAM,IAAqC,EAAE,EACvC,IAAiC,EAAE;CACzC,KAAK,IAAM,CAAC,GAAM,MAAM,OAAO,QAAQ,EAAI,OAAO,EAE9C,AADI,EAAE,cAAc,KAAA,MAAW,EAAW,KAAQ,EAAE,YAChD,EAAE,QAAK,EAAK,KAAQ,EAAE;CAG9B,AADI,OAAO,KAAK,EAAW,CAAC,SAAS,MAAG,EAAkB,EAAI,MAAM,IAChE,OAAO,KAAK,EAAK,CAAC,SAAS,MAAG,EAAY,EAAI,MAAM;;AAG5D,SAAgB,EACZ,GACA,GACA,GACkB;CAClB,OAAO,EAAU,KAAO,MAAS,EAAkB,KAAO;;AAG9D,SAAgB,EACZ,GACA,GACA,GACoB;CACpB,OAAO,EAAU,KAAO,MAAS,EAAY,KAAO;;AAGxD,SAAgB,EACZ,GACA,GACA,GACA,GACkD;CAClD,IAAM,IAAM,EAAU,GAAe,GAAK,EAAK;CAC/C,IAAI,GAAK,OAAO,EAAE,QAAK;CAEvB,IAAM,IAAY,EAAgB,GAAqB,GAAK,EAAK;CACjE,IAAI,MAAc,KAAA,GAAW,OAAO,EAAE,cAAW;;;;ACfrD,SAAgB,EAAc,GAA6B;CAEvD,OADA,EAAgB,EAAI,EACb"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { IconSetDef } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Register a custom icon set at module load time.
|
|
4
|
+
*
|
|
5
|
+
* Build-time sets declared in `signalx.config.ts` are auto-detected and
|
|
6
|
+
* tree-shaken; `defineIconSet` is the escape hatch for ad-hoc sets defined
|
|
7
|
+
* directly in app code (e.g. a small private set used in one screen).
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* defineIconSet({
|
|
12
|
+
* id: 'brand',
|
|
13
|
+
* glyphs: {
|
|
14
|
+
* logo: {
|
|
15
|
+
* svg: {
|
|
16
|
+
* svg: '<svg viewBox="0 0 24 24" fill="__COLOR__"><path d="M3 12L12 3l9 9-9 9z"/></svg>',
|
|
17
|
+
* },
|
|
18
|
+
* },
|
|
19
|
+
* },
|
|
20
|
+
* });
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* The inner `svg` value is raw SVG markup. `__COLOR__` placeholders in that
|
|
24
|
+
* markup get substituted with the user's `color` prop at render time.
|
|
25
|
+
*/
|
|
26
|
+
export declare function defineIconSet(def: IconSetDef): IconSetDef;
|
|
27
|
+
export type { IconSetDef } from './types';
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { n as e, t } from "./defineIconSet-BIFjZq08.js";
|
|
2
|
+
import { component as n } from "@sigx/lynx";
|
|
3
|
+
import { codepoints as r } from "@sigx/lynx-icons/__codepoints";
|
|
4
|
+
import { svgs as i } from "@sigx/lynx-icons/__svgs";
|
|
5
|
+
import "@sigx/lynx-icons/__font-face.css";
|
|
6
|
+
import { jsx as a } from "@sigx/lynx/jsx-runtime";
|
|
7
|
+
//#region src/Icon.tsx
|
|
8
|
+
var o = /^(?:currentColor|[a-zA-Z]+|#[0-9a-fA-F]{3,8}|(?:rgb|rgba|hsl|hsla)\(\s*[\d.,%\s]+\))$/;
|
|
9
|
+
function s(e) {
|
|
10
|
+
return o.test(e) ? e : "currentColor";
|
|
11
|
+
}
|
|
12
|
+
function c(e, t) {
|
|
13
|
+
return e.replace(/__COLOR__/g, s(t));
|
|
14
|
+
}
|
|
15
|
+
var l = n(({ props: t }) => () => {
|
|
16
|
+
let n = t.size ?? 16, o = t.set, s = t.name, l = {
|
|
17
|
+
width: n,
|
|
18
|
+
height: n
|
|
19
|
+
}, u = e(r, i, o, s);
|
|
20
|
+
if (u?.svg) {
|
|
21
|
+
let e = t.color ?? "currentColor";
|
|
22
|
+
return /* @__PURE__ */ a("svg", {
|
|
23
|
+
content: c(u.svg.svg, e),
|
|
24
|
+
class: t.class,
|
|
25
|
+
style: l
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
if (u?.codepoint !== void 0) {
|
|
29
|
+
let e = {
|
|
30
|
+
fontFamily: o,
|
|
31
|
+
fontSize: n,
|
|
32
|
+
lineHeight: `${n}px`,
|
|
33
|
+
width: n,
|
|
34
|
+
height: n
|
|
35
|
+
};
|
|
36
|
+
return t.color && (e.color = t.color), /* @__PURE__ */ a("text", {
|
|
37
|
+
class: t.class,
|
|
38
|
+
style: e,
|
|
39
|
+
children: String.fromCodePoint(u.codepoint)
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
return /* @__PURE__ */ a("view", {
|
|
43
|
+
class: t.class,
|
|
44
|
+
style: l
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
//#endregion
|
|
48
|
+
export { l as Icon, t as defineIconSet };
|
|
49
|
+
|
|
50
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/Icon.tsx"],"sourcesContent":["import { component, type Define } from '@sigx/lynx';\nimport { codepoints } from '@sigx/lynx-icons/__codepoints';\nimport { svgs } from '@sigx/lynx-icons/__svgs';\nimport '@sigx/lynx-icons/__font-face.css';\nimport { lookupGlyph } from './registry';\n\nexport type IconProps =\n & Define.Prop<'set', string, true>\n & Define.Prop<'name', string, true>\n & Define.Prop<'size', number, false>\n & Define.Prop<'color', string, false>\n & Define.Prop<'class', string, false>;\n\n/**\n * Match a conservative subset of valid CSS color formats:\n * - `currentColor` and named colors (`red`, `dodgerblue`, `transparent` …)\n * - `#rgb` / `#rgba` / `#rrggbb` / `#rrggbbaa`\n * - `rgb(...)` / `rgba(...)` / `hsl(...)` / `hsla(...)` with digits, dots,\n * commas, percent signs, and whitespace inside the parens\n *\n * Anything else (quotes, angle brackets, ampersands, arbitrary HTML) falls\n * back to `currentColor`. The `color` prop ends up substituted directly into\n * the SVG markup that we hand to Lynx's `<svg content={…}>` parser, so a\n * value like `red\" stroke=\"…` would break out of the `fill=\"\"` attribute.\n * Allow-list, not escape-list — keeps the surface area provably small.\n */\nconst SAFE_COLOR_RE =\n /^(?:currentColor|[a-zA-Z]+|#[0-9a-fA-F]{3,8}|(?:rgb|rgba|hsl|hsla)\\(\\s*[\\d.,%\\s]+\\))$/;\n\nexport function sanitizeColor(color: string): string {\n return SAFE_COLOR_RE.test(color) ? color : 'currentColor';\n}\n\nexport function inlineSvg(template: string, color: string): string {\n return template.replace(/__COLOR__/g, sanitizeColor(color));\n}\n\n/**\n * Render a glyph from a registered icon set.\n *\n * Sets are declared in `signalx.config.ts` via `iconSets: [...]` (build-time,\n * tree-shaken) or via `defineIconSet({ id, glyphs })` (runtime, ad-hoc).\n *\n * Resolution order: **SVG first**, then codepoint, then missing placeholder.\n *\n * - SVG-mode sets render with Lynx's native `<svg content={...}>` element\n * (the engine parses the inline XML; no JSX children, no data: URIs).\n * - Font-mode sets fall back to a single character inside a `<text>` element\n * with a matching `font-family`. The plugin only emits codepoints when the\n * matching `@font-face` has also been registered (v1.1+); v1 always hits\n * the SVG branch.\n * - Missing glyphs render an empty `<view>` of the same size so layout\n * doesn't jump.\n *\n * @example\n * ```tsx\n * <Icon set=\"fa\" name=\"user\" size={20} color=\"#333\" />\n * <Icon set=\"lucide\" name=\"search\" size={16} />\n * ```\n */\nexport const Icon = component<IconProps>(({ props }) => {\n return () => {\n const size = props.size ?? 16;\n const set = props.set;\n const name = props.name;\n const sizeStyle = { width: size, height: size } as const;\n\n const glyph = lookupGlyph(codepoints, svgs, set, name);\n if (glyph?.svg) {\n const color = props.color ?? 'currentColor';\n return (\n <svg\n content={inlineSvg(glyph.svg.svg, color)}\n class={props.class}\n style={sizeStyle}\n />\n );\n }\n\n if (glyph?.codepoint !== undefined) {\n const textStyle: Record<string, string | number> = {\n fontFamily: set,\n fontSize: size,\n lineHeight: `${size}px`,\n width: size,\n height: size,\n };\n if (props.color) textStyle.color = props.color;\n return (\n <text class={props.class} style={textStyle}>\n {String.fromCodePoint(glyph.codepoint)}\n </text>\n );\n }\n\n return <view class={props.class} style={sizeStyle} />;\n };\n});\n"],"mappings":";;;;;;;AA0BA,IAAM,IACF;AAEJ,SAAgB,EAAc,GAAuB;CACjD,OAAO,EAAc,KAAK,EAAM,GAAG,IAAQ;;AAG/C,SAAgB,EAAU,GAAkB,GAAuB;CAC/D,OAAO,EAAS,QAAQ,cAAc,EAAc,EAAM,CAAC;;AA0B/D,IAAa,IAAO,GAAsB,EAAE,qBAC3B;CACT,IAAM,IAAO,EAAM,QAAQ,IACrB,IAAM,EAAM,KACZ,IAAO,EAAM,MACb,IAAY;EAAE,OAAO;EAAM,QAAQ;EAAM,EAEzC,IAAQ,EAAY,GAAY,GAAM,GAAK,EAAK;CACtD,IAAI,GAAO,KAAK;EACZ,IAAM,IAAQ,EAAM,SAAS;EAC7B,OACI,kBAAC,OAAD;GACI,SAAS,EAAU,EAAM,IAAI,KAAK,EAAM;GACxC,OAAO,EAAM;GACb,OAAO;GACT,CAAA;;CAIV,IAAI,GAAO,cAAc,KAAA,GAAW;EAChC,IAAM,IAA6C;GAC/C,YAAY;GACZ,UAAU;GACV,YAAY,GAAG,EAAK;GACpB,OAAO;GACP,QAAQ;GACX;EAED,OADI,EAAM,UAAO,EAAU,QAAQ,EAAM,QAErC,kBAAC,QAAD;GAAM,OAAO,EAAM;GAAO,OAAO;aAC5B,OAAO,cAAc,EAAM,UAAU;GACnC,CAAA;;CAIf,OAAO,kBAAC,QAAD;EAAM,OAAO,EAAM;EAAO,OAAO;EAAa,CAAA;EAE3D"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { CodepointMap, GlyphSvg, IconSetDef, SvgMap } from './types';
|
|
2
|
+
export declare function registerIconSet(def: IconSetDef): void;
|
|
3
|
+
export declare function lookupCodepoint(buildTime: CodepointMap, set: string, name: string): number | undefined;
|
|
4
|
+
export declare function lookupSvg(buildTime: SvgMap, set: string, name: string): GlyphSvg | undefined;
|
|
5
|
+
export declare function lookupGlyph(buildTimeCodepoints: CodepointMap, buildTimeSvgs: SvgMap, set: string, name: string): {
|
|
6
|
+
codepoint?: number;
|
|
7
|
+
svg?: GlyphSvg;
|
|
8
|
+
} | undefined;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { CodepointMap } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Empty stub. The @sigx/lynx-plugin icons slice replaces this module via an
|
|
4
|
+
* Rspack alias at build time, populating it with the codepoints used by the
|
|
5
|
+
* app's `<Icon>` calls. Without the plugin, no built-in sets are registered
|
|
6
|
+
* and all icons fall back to `defineIconSet`-registered sets (if any) or the
|
|
7
|
+
* missing-glyph placeholder.
|
|
8
|
+
*/
|
|
9
|
+
export declare const codepoints: CodepointMap;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codepoints.js","names":[],"sources":["../../src/stubs/codepoints.ts"],"sourcesContent":["import type { CodepointMap } from '../types';\n\n/**\n * Empty stub. The @sigx/lynx-plugin icons slice replaces this module via an\n * Rspack alias at build time, populating it with the codepoints used by the\n * app's `<Icon>` calls. Without the plugin, no built-in sets are registered\n * and all icons fall back to `defineIconSet`-registered sets (if any) or the\n * missing-glyph placeholder.\n */\nexport const codepoints: CodepointMap = {};\n"],"mappings":";AASA,IAAa,IAA2B,EAAE"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { CodepointMap } from '../types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Empty stub. The @sigx/lynx-plugin icons slice replaces this module via an
|
|
5
|
+
* Rspack alias at build time, populating it with the codepoints used by the
|
|
6
|
+
* app's `<Icon>` calls. Without the plugin, no built-in sets are registered
|
|
7
|
+
* and all icons fall back to `defineIconSet`-registered sets (if any) or the
|
|
8
|
+
* missing-glyph placeholder.
|
|
9
|
+
*/
|
|
10
|
+
export const codepoints: CodepointMap = {};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { SvgMap } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Empty stub. The @sigx/lynx-plugin icons slice replaces this module via an
|
|
4
|
+
* Rspack alias at build time, populating it with the SVG path data for the
|
|
5
|
+
* icons used by the app's `<Icon>` calls.
|
|
6
|
+
*/
|
|
7
|
+
export declare const svgs: SvgMap;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"svgs.js","names":[],"sources":["../../src/stubs/svgs.ts"],"sourcesContent":["import type { SvgMap } from '../types';\n\n/**\n * Empty stub. The @sigx/lynx-plugin icons slice replaces this module via an\n * Rspack alias at build time, populating it with the SVG path data for the\n * icons used by the app's `<Icon>` calls.\n */\nexport const svgs: SvgMap = {};\n"],"mappings":";AAOA,IAAa,IAAe,EAAE"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { SvgMap } from '../types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Empty stub. The @sigx/lynx-plugin icons slice replaces this module via an
|
|
5
|
+
* Rspack alias at build time, populating it with the SVG path data for the
|
|
6
|
+
* icons used by the app's `<Icon>` calls.
|
|
7
|
+
*/
|
|
8
|
+
export const svgs: SvgMap = {};
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-glyph vector data. Used by SVG-mode rendering.
|
|
3
|
+
*
|
|
4
|
+
* `svg` is a complete `<svg …>…</svg>` string with `__COLOR__` placeholders
|
|
5
|
+
* wherever the user-supplied `color` should be substituted. This lets each
|
|
6
|
+
* adapter ship the native shape of its icons (FA = filled paths, lucide =
|
|
7
|
+
* stroked paths) without the core component needing to know per-adapter
|
|
8
|
+
* styling.
|
|
9
|
+
*
|
|
10
|
+
* @example FA solid: `<svg viewBox="0 0 448 512" fill="__COLOR__"><path d="..."/></svg>`
|
|
11
|
+
* @example lucide: `<svg viewBox="0 0 24 24" fill="none" stroke="__COLOR__" stroke-width="2" ...><path .../><circle .../></svg>`
|
|
12
|
+
*/
|
|
13
|
+
export interface GlyphSvg {
|
|
14
|
+
svg: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Build-time virtual module shape: glyph name → unicode codepoint, keyed by set id.
|
|
18
|
+
* Populated by the @sigx/lynx-plugin icons slice; the stub ships an empty record
|
|
19
|
+
* so the core package builds standalone.
|
|
20
|
+
*/
|
|
21
|
+
export type CodepointMap = Record<string, Record<string, number>>;
|
|
22
|
+
/**
|
|
23
|
+
* Build-time virtual module shape: glyph name → SVG data, keyed by set id.
|
|
24
|
+
* Populated by the @sigx/lynx-plugin icons slice.
|
|
25
|
+
*/
|
|
26
|
+
export type SvgMap = Record<string, Record<string, GlyphSvg>>;
|
|
27
|
+
/**
|
|
28
|
+
* Runtime-registered icon set, for ad-hoc / app-local sets created via
|
|
29
|
+
* `defineIconSet`. Build-time sets bypass this and live in the virtual modules.
|
|
30
|
+
*/
|
|
31
|
+
export interface IconSetDef {
|
|
32
|
+
id: string;
|
|
33
|
+
glyphs: Record<string, {
|
|
34
|
+
codepoint?: number;
|
|
35
|
+
svg?: GlyphSvg;
|
|
36
|
+
}>;
|
|
37
|
+
fontFamily?: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Glyph data exposed by an adapter package at build time.
|
|
41
|
+
* `codepoint` enables font-mode subsetting; `svg` enables SVG-mode rendering.
|
|
42
|
+
* Adapters should populate both when possible (FA does), or only `svg` for
|
|
43
|
+
* SVG-only sets (e.g. lucide).
|
|
44
|
+
*
|
|
45
|
+
* `svg` is a complete `<svg>…</svg>` string with `__COLOR__` placeholders
|
|
46
|
+
* for the user's color; see {@link GlyphSvg} for the rendering contract.
|
|
47
|
+
*/
|
|
48
|
+
export interface GlyphData {
|
|
49
|
+
codepoint?: number;
|
|
50
|
+
svg: string;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Build-time adapter contract. Implemented by packages like
|
|
54
|
+
* `@sigx/lynx-icons-fa-free` and consumed by `@sigx/lynx-plugin`'s icons slice
|
|
55
|
+
* to resolve glyph data for every `<Icon set="..." name="..." />` usage.
|
|
56
|
+
*
|
|
57
|
+
* Adapters should expose a default export of this shape from their main entry.
|
|
58
|
+
*/
|
|
59
|
+
export interface IconAdapter {
|
|
60
|
+
/** Style variants the adapter supports. e.g. ['solid', 'regular', 'brands'] for FA, [''] (single empty style) for lucide. */
|
|
61
|
+
styles: string[];
|
|
62
|
+
/** Resolve a glyph by style + name. Return `null` when the glyph is unknown. */
|
|
63
|
+
getGlyph(style: string, name: string): GlyphData | null;
|
|
64
|
+
/**
|
|
65
|
+
* Absolute filesystem path to the source TTF for `style`, or null when the
|
|
66
|
+
* adapter only supports SVG mode. The plugin reads this file and runs it
|
|
67
|
+
* through `subset-font` to produce per-app subsets.
|
|
68
|
+
*/
|
|
69
|
+
getFontPath(style: string): string | null;
|
|
70
|
+
/**
|
|
71
|
+
* Enumerate every glyph name the adapter can resolve for `style`, in
|
|
72
|
+
* the same kebab-case form `getGlyph` accepts. Used by the plugin when
|
|
73
|
+
* a project sets `iconSets[].include: ['*']` (e.g. JSON-driven UIs where
|
|
74
|
+
* the names aren't known at build time) — the plugin pulls the full
|
|
75
|
+
* catalog into the bundle instead of relying on the JSX scanner.
|
|
76
|
+
*/
|
|
77
|
+
listGlyphs(style: string): string[];
|
|
78
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sigx/lynx-icons",
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "Icon component and registry for sigx-lynx — works with adapter packages like @sigx/lynx-icons-fa-free and @sigx/lynx-icons-lucide.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"default": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./__codepoints": {
|
|
15
|
+
"types": "./dist/stubs/codepoints.d.ts",
|
|
16
|
+
"import": "./dist/stubs/codepoints.js",
|
|
17
|
+
"default": "./dist/stubs/codepoints.js"
|
|
18
|
+
},
|
|
19
|
+
"./__svgs": {
|
|
20
|
+
"types": "./dist/stubs/svgs.d.ts",
|
|
21
|
+
"import": "./dist/stubs/svgs.js",
|
|
22
|
+
"default": "./dist/stubs/svgs.js"
|
|
23
|
+
},
|
|
24
|
+
"./__font-face.css": {
|
|
25
|
+
"types": "./dist/stubs/font-face.css.d.ts",
|
|
26
|
+
"default": "./dist/stubs/font-face.css"
|
|
27
|
+
},
|
|
28
|
+
"./signalx-module.json": "./signalx-module.json",
|
|
29
|
+
"./package.json": "./package.json"
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"dist",
|
|
33
|
+
"signalx-module.json",
|
|
34
|
+
"README.md",
|
|
35
|
+
"LICENSE"
|
|
36
|
+
],
|
|
37
|
+
"peerDependencies": {
|
|
38
|
+
"@sigx/lynx": "^0.4.0"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@sigx/vite": "^0.4.3",
|
|
42
|
+
"typescript": "^6.0.3",
|
|
43
|
+
"@sigx/lynx": "^0.4.0"
|
|
44
|
+
},
|
|
45
|
+
"author": "Andreas Ekdahl",
|
|
46
|
+
"license": "MIT",
|
|
47
|
+
"repository": {
|
|
48
|
+
"type": "git",
|
|
49
|
+
"url": "git+https://github.com/signalxjs/lynx.git",
|
|
50
|
+
"directory": "packages/lynx-icons"
|
|
51
|
+
},
|
|
52
|
+
"homepage": "https://github.com/signalxjs/lynx/tree/main/packages/lynx-icons",
|
|
53
|
+
"bugs": {
|
|
54
|
+
"url": "https://github.com/signalxjs/lynx/issues"
|
|
55
|
+
},
|
|
56
|
+
"publishConfig": {
|
|
57
|
+
"access": "public"
|
|
58
|
+
},
|
|
59
|
+
"keywords": [
|
|
60
|
+
"signalx",
|
|
61
|
+
"sigx",
|
|
62
|
+
"lynx",
|
|
63
|
+
"icon",
|
|
64
|
+
"icons",
|
|
65
|
+
"font-awesome",
|
|
66
|
+
"lucide"
|
|
67
|
+
],
|
|
68
|
+
"scripts": {
|
|
69
|
+
"build": "vite build && tsgo --emitDeclarationOnly && node ../../scripts/copy-assets.mjs src/stubs dist/stubs",
|
|
70
|
+
"dev": "vite build --watch"
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Icons",
|
|
3
|
+
"package": "@sigx/lynx-icons",
|
|
4
|
+
"description": "Registers Lynx's <svg> element (via XElement/SVG + ServalSVG) so <Icon> can render in SVG mode.",
|
|
5
|
+
"platforms": ["android", "ios"],
|
|
6
|
+
"android": {
|
|
7
|
+
"dependencies": ["org.lynxsdk.lynx:xelement-svg:3.7.0"]
|
|
8
|
+
},
|
|
9
|
+
"ios": {
|
|
10
|
+
"pods": {
|
|
11
|
+
"XElement/SVG": "3.7.0"
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|