@lovince/icons 1.0.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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Lovince Tech
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,155 @@
1
+ # @lovince/icons
2
+
3
+ An icon system for interfaces that move — 24×24 geometry, variants derived from
4
+ one shape, and state transitions that read as a single object changing rather
5
+ than two icons swapping.
6
+
7
+ ```bash
8
+ bun add @lovince/icons
9
+ ```
10
+
11
+ No runtime dependencies. Vue is an optional peer, needed only for the Vue layer.
12
+
13
+ ## Not another icon collection
14
+
15
+ Competing on icon count is a losing game. This competes on what generic sets
16
+ don't do:
17
+
18
+ | | |
19
+ |---|---|
20
+ | **One geometry, three variants** | Outline, duotone and filled are the same paths at different fill strengths — so a variant change is an animatable value, not a swap |
21
+ | **Transitions, not swaps** | Menu ⇄ Close, Plus ⇄ Close, and a tick that draws on. Continuity is the point |
22
+ | **Hit area separated from icon size** | An 18px icon still gets a 44px target. The most common icon-button mistake, invisible until someone taps it |
23
+ | **Journey step states** | `○ → ◉ → ✓` as one shape, for onboarding progress |
24
+ | **Onboarding vocabulary** | `welcome`, `discover`, `click`, `swipe`, `drag`, `scroll`, `tip`, `shortcut`, `newFeature` |
25
+ | **Optical sizing** | Stroke lightens at small and large sizes so weight reads constant |
26
+ | **Accessibility by construction** | Decorative by default; a `label` switches it to `role="img"` |
27
+ | **Reduced motion** | Every icon reaches its final state; it just gets there instantly |
28
+
29
+ ## Works in any framework
30
+
31
+ The core is framework-free. Vue components are one adapter, not the only door.
32
+
33
+ ### Plain JavaScript, React, Svelte, Angular, Astro
34
+
35
+ ```ts
36
+ import { renderIcon, createIcon, defineIconElement } from '@lovince/icons'
37
+
38
+ renderIcon('arrowRight', { size: 20 }) // SVG markup as a string
39
+ createIcon('home', { size: 24 }) // a live SVGElement
40
+ ```
41
+
42
+ Or use the custom element, which every framework renders natively:
43
+
44
+ ```ts
45
+ import { defineIconElement } from '@lovince/icons'
46
+ defineIconElement()
47
+ ```
48
+
49
+ ```html
50
+ <lv-icon name="arrow-right" size="20"></lv-icon>
51
+ <lv-icon name="close" label="Dismiss"></lv-icon>
52
+ ```
53
+
54
+ Names accept kebab-case in markup and camelCase in code.
55
+
56
+ ### Vue
57
+
58
+ ```vue
59
+ <script setup lang="ts">
60
+ import { LvIcon, LvIconButton, LvJourneyStep } from '@lovince/icons/vue'
61
+ import '@lovince/icons/style.css'
62
+ </script>
63
+
64
+ <template>
65
+ <LvIcon name="arrowRight" :size="20" />
66
+ <LvIconButton icon="close" label="Dismiss" context="toolbar" />
67
+ <LvJourneyStep state="current" />
68
+ </template>
69
+ ```
70
+
71
+ The stylesheet is only needed for the animated components.
72
+
73
+ ## Props
74
+
75
+ ### `LvIcon`
76
+
77
+ | Prop | Type | Default | |
78
+ |---|---|---|---|
79
+ | `name` | `IconName` | — | Unknown names render nothing rather than breaking layout |
80
+ | `size` | `number \| string` | `24` | |
81
+ | `strokeWidth` | `number \| string` | `2` | |
82
+ | `absoluteStrokeWidth` | `boolean` | `false` | Keep rendered thickness constant as size changes |
83
+ | `fill` | `number \| boolean` | `0` | `0` outline, `1` solid, between is duotone |
84
+ | `rotate` | `number` | `0` | About the canvas centre |
85
+ | `flip` | `'horizontal' \| 'vertical' \| 'both'` | — | |
86
+ | `label` | `string` | — | Omit for decorative icons |
87
+
88
+ ### `LvIconButton`
89
+
90
+ | Prop | Type | Default | |
91
+ |---|---|---|---|
92
+ | `icon` | `IconName` | — | |
93
+ | `label` | `string` | — | **Required** — an icon-only control has no visible name |
94
+ | `context` | `IconContext` | `'button'` | `toolbar` `button` `navigation` `hero` `status` `inline` |
95
+ | `hitArea` | `number` | from context | Never below 44px |
96
+ | `variant` | `IconVariant` | `'outline'` | |
97
+ | `optical` | `boolean` | `true` | Adjust stroke for the rendered size |
98
+
99
+ ### Animated
100
+
101
+ `LvMenuToClose` and `LvPlusToClose` take `open`; `LvCheckmark` takes `checked`
102
+ and `circle`; `LvArrowMotion` takes `direction` and `travel`; `LvLoading` takes
103
+ `duration`. All accept `size`, `strokeWidth` and `label`.
104
+
105
+ `LvArrowMotion` responds to the **parent's** hover and focus, so the whole
106
+ control moves as one object:
107
+
108
+ ```vue
109
+ <button class="lv-hover">
110
+ Continue <LvArrowMotion direction="right" />
111
+ </button>
112
+ ```
113
+
114
+ ## Icon set
115
+
116
+ 52 icons across five groups — `navigation`, `journey`, `status`, `onboarding`,
117
+ `interface`.
118
+
119
+ ```ts
120
+ import { iconNames, iconGroups, isIconName } from '@lovince/icons'
121
+ ```
122
+
123
+ ## Motion tokens
124
+
125
+ ```ts
126
+ import { DURATION, EASE, prefersReducedMotion } from '@lovince/icons'
127
+
128
+ DURATION // { micro: 0.18, state: 0.28, morph: 0.36 }
129
+ ```
130
+
131
+ Shared with [`@lovince/product-journey`](https://www.npmjs.com/package/@lovince/product-journey)
132
+ so icon motion matches journey motion.
133
+
134
+ ## Design rules
135
+
136
+ ```
137
+ 24 × 24 canvas
138
+ 2px centred stroke
139
+ round caps and joins
140
+ legible at 16px
141
+ no gradients, no mixed weights
142
+ ```
143
+
144
+ ## Size
145
+
146
+ ```
147
+ index.js 1.7 KB gzip framework-free core
148
+ vue.js 2.3 KB gzip Vue components
149
+ paths.js 2.0 KB gzip geometry only
150
+ style.css 2.0 KB gzip animated components only
151
+ ```
152
+
153
+ ## License
154
+
155
+ MIT
@@ -0,0 +1,25 @@
1
+ import { IconName } from './paths';
2
+ import { IconFlip } from './core/props';
3
+ type __VLS_Props = {
4
+ /** Name from the registry. An unknown name renders nothing rather than breaking layout. */
5
+ name: IconName;
6
+ size?: number | string;
7
+ strokeWidth?: number | string;
8
+ /** Keep the stroke's rendered thickness constant as size changes. */
9
+ absoluteStrokeWidth?: boolean;
10
+ /** 0 outline, 1 solid, anything between is a partial wash. */
11
+ fill?: number | boolean;
12
+ rotate?: number | string;
13
+ flip?: IconFlip;
14
+ /** Accessible name. Omit for decorative icons that sit beside visible text. */
15
+ label?: string;
16
+ };
17
+ declare const __VLS_export: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
18
+ fill: number | boolean;
19
+ size: number | string;
20
+ rotate: number | string;
21
+ strokeWidth: number | string;
22
+ absoluteStrokeWidth: boolean;
23
+ }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
24
+ declare const _default: typeof __VLS_export;
25
+ export default _default;
@@ -0,0 +1,22 @@
1
+ import { IconContext, IconVariant } from './core/variants';
2
+ import { IconName } from './paths';
3
+ type __VLS_Props = {
4
+ icon: IconName;
5
+ /** Required: an icon-only control has no visible text to name it. */
6
+ label: string;
7
+ context?: IconContext;
8
+ size?: number;
9
+ /** Interactive target, independent of the icon's visual size. */
10
+ hitArea?: number;
11
+ variant?: IconVariant;
12
+ disabled?: boolean;
13
+ optical?: boolean;
14
+ };
15
+ declare const __VLS_export: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
16
+ disabled: boolean;
17
+ context: IconContext;
18
+ variant: IconVariant;
19
+ optical: boolean;
20
+ }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
21
+ declare const _default: typeof __VLS_export;
22
+ export default _default;
@@ -0,0 +1,14 @@
1
+ export type StepState = 'pending' | 'current' | 'complete' | 'skipped';
2
+ type __VLS_Props = {
3
+ state?: StepState;
4
+ size?: number | string;
5
+ strokeWidth?: number | string;
6
+ label?: string;
7
+ };
8
+ declare const __VLS_export: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
9
+ size: number | string;
10
+ strokeWidth: number | string;
11
+ state: StepState;
12
+ }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
13
+ declare const _default: typeof __VLS_export;
14
+ export default _default;
@@ -0,0 +1,20 @@
1
+ type __VLS_Props = {
2
+ direction?: 'right' | 'left' | 'up' | 'down';
3
+ /** Nudge distance in canvas units when the parent is hovered. */
4
+ travel?: number;
5
+ size?: number | string;
6
+ strokeWidth?: number | string;
7
+ absoluteStrokeWidth?: boolean;
8
+ label?: string;
9
+ duration?: number;
10
+ };
11
+ declare const __VLS_export: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
12
+ size: number | string;
13
+ strokeWidth: number | string;
14
+ absoluteStrokeWidth: boolean;
15
+ duration: number;
16
+ direction: "right" | "left" | "up" | "down";
17
+ travel: number;
18
+ }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
19
+ declare const _default: typeof __VLS_export;
20
+ export default _default;
@@ -0,0 +1,20 @@
1
+ type __VLS_Props = {
2
+ /** Draws the tick when true; resets when false so it can replay. */
3
+ checked?: boolean;
4
+ circle?: boolean;
5
+ size?: number | string;
6
+ strokeWidth?: number | string;
7
+ absoluteStrokeWidth?: boolean;
8
+ label?: string;
9
+ duration?: number;
10
+ };
11
+ declare const __VLS_export: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
12
+ circle: boolean;
13
+ size: number | string;
14
+ strokeWidth: number | string;
15
+ absoluteStrokeWidth: boolean;
16
+ duration: number;
17
+ checked: boolean;
18
+ }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
19
+ declare const _default: typeof __VLS_export;
20
+ export default _default;
@@ -0,0 +1,17 @@
1
+ type __VLS_Props = {
2
+ size?: number | string;
3
+ strokeWidth?: number | string;
4
+ absoluteStrokeWidth?: boolean;
5
+ /** Seconds per revolution. */
6
+ duration?: number;
7
+ label?: string;
8
+ };
9
+ declare const __VLS_export: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
10
+ size: number | string;
11
+ label: string;
12
+ strokeWidth: number | string;
13
+ absoluteStrokeWidth: boolean;
14
+ duration: number;
15
+ }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
16
+ declare const _default: typeof __VLS_export;
17
+ export default _default;
@@ -0,0 +1,17 @@
1
+ type __VLS_Props = {
2
+ open?: boolean;
3
+ size?: number | string;
4
+ strokeWidth?: number | string;
5
+ absoluteStrokeWidth?: boolean;
6
+ label?: string;
7
+ duration?: number;
8
+ };
9
+ declare const __VLS_export: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
10
+ size: number | string;
11
+ strokeWidth: number | string;
12
+ absoluteStrokeWidth: boolean;
13
+ open: boolean;
14
+ duration: number;
15
+ }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
16
+ declare const _default: typeof __VLS_export;
17
+ export default _default;
@@ -0,0 +1,17 @@
1
+ type __VLS_Props = {
2
+ open?: boolean;
3
+ size?: number | string;
4
+ strokeWidth?: number | string;
5
+ absoluteStrokeWidth?: boolean;
6
+ label?: string;
7
+ duration?: number;
8
+ };
9
+ declare const __VLS_export: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
10
+ size: number | string;
11
+ strokeWidth: number | string;
12
+ absoluteStrokeWidth: boolean;
13
+ open: boolean;
14
+ duration: number;
15
+ }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
16
+ declare const _default: typeof __VLS_export;
17
+ export default _default;
@@ -0,0 +1,17 @@
1
+ /** Shared with @lovince/product-journey so icon motion matches journey motion. */
2
+ export declare const DURATION: {
3
+ readonly micro: 0.18;
4
+ readonly state: 0.28;
5
+ readonly morph: 0.36;
6
+ };
7
+ export declare const EASE: {
8
+ readonly enter: "cubic-bezier(0.33, 1, 0.68, 1)";
9
+ readonly morph: "cubic-bezier(0.65, 0, 0.35, 1)";
10
+ };
11
+ export declare const prefersReducedMotion: () => boolean;
12
+ /**
13
+ * Motion is a progressive enhancement: with reduced motion the icon still
14
+ * reaches its final state, it just gets there instantly.
15
+ */
16
+ export declare const resolveDuration: (seconds: number, reduced?: boolean) => number;
17
+ export declare const cssDuration: (seconds: number, reduced?: boolean) => string;
@@ -0,0 +1,26 @@
1
+ export declare const CANVAS = 24;
2
+ export declare const DEFAULT_SIZE = 24;
3
+ export declare const DEFAULT_STROKE = 2;
4
+ export type IconFlip = 'horizontal' | 'vertical' | 'both';
5
+ export interface IconGeometry {
6
+ readonly size: number;
7
+ readonly strokeWidth: number;
8
+ readonly transform: string | undefined;
9
+ }
10
+ /**
11
+ * Optical sizing. A 2px stroke drawn on a 24px canvas becomes 4px when the icon
12
+ * is rendered at 48px, which reads as a heavier icon rather than a bigger one.
13
+ * `absoluteStrokeWidth` rescales the stroke so its rendered thickness stays put.
14
+ */
15
+ export declare const resolveStroke: (strokeWidth: number | string | undefined, size: number, absolute: boolean) => number;
16
+ /** Rotation and flipping happen about the canvas centre, never the origin. */
17
+ export declare const resolveTransform: (rotate: number | string | undefined, flip: IconFlip | undefined) => string | undefined;
18
+ export declare const resolveGeometry: (options: {
19
+ size?: number | string;
20
+ strokeWidth?: number | string;
21
+ absoluteStrokeWidth?: boolean;
22
+ rotate?: number | string;
23
+ flip?: IconFlip;
24
+ }) => IconGeometry;
25
+ /** 0 draws outline only, 1 fills solid; anything between is a partial wash. */
26
+ export declare const resolveFill: (fill: number | boolean | undefined) => number;
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Outline, filled and duotone are the *same* geometry at different fill
3
+ * strengths — not three drawings that can drift apart. That is what makes a
4
+ * transition between them a single animated value rather than an icon swap.
5
+ */
6
+ export type IconVariant = 'outline' | 'filled' | 'duotone';
7
+ export declare const VARIANT_FILL: Readonly<Record<IconVariant, number>>;
8
+ export declare const resolveVariantFill: (variant: IconVariant | undefined) => number;
9
+ /**
10
+ * Context presets. The same icon needs a different treatment in a dense toolbar
11
+ * than in a hero, and encoding that here stops every call site inventing its
12
+ * own numbers.
13
+ */
14
+ export type IconContext = 'toolbar' | 'button' | 'navigation' | 'hero' | 'status' | 'inline';
15
+ export interface ContextPreset {
16
+ readonly size: number;
17
+ readonly strokeWidth: number;
18
+ /** Minimum interactive target, independent of the icon's visual size. */
19
+ readonly hitArea: number;
20
+ }
21
+ export declare const CONTEXT: Readonly<Record<IconContext, ContextPreset>>;
22
+ export declare const resolveContext: (context: IconContext | undefined) => ContextPreset | null;
23
+ /**
24
+ * Optical sizing. Scaling one path to 16px thickens it relative to its detail,
25
+ * so small sizes get a slightly lighter stroke and large sizes a lighter one
26
+ * again — the icon reads as the same weight at every size.
27
+ */
28
+ export declare const opticalStroke: (size: number, base?: number) => number;
29
+ export type IconState = 'default' | 'hover' | 'active' | 'selected' | 'disabled';
30
+ /** States differ by fill and opacity, so a change is animatable, not a swap. */
31
+ export declare const STATE_FILL: Readonly<Record<IconState, number>>;
32
+ export declare const STATE_OPACITY: Readonly<Record<IconState, number>>;
@@ -0,0 +1,8 @@
1
+ export declare class LvIconElement extends HTMLElement {
2
+ static get observedAttributes(): readonly string[];
3
+ connectedCallback(): void;
4
+ attributeChangedCallback(): void;
5
+ private render;
6
+ }
7
+ /** Idempotent, so calling it from several entry points is safe. */
8
+ export declare const defineIconElement: (tag?: string) => void;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Framework-free entry point. Nothing here imports Vue, so it works in React,
3
+ * Svelte, Angular, Astro or plain HTML. The Vue components live at
4
+ * `@lovince/icons/vue`; the custom element works everywhere.
5
+ */
6
+ export { paths, iconNames, iconGroups, isIconName, type IconName } from './paths';
7
+ export { renderIcon, createIcon, iconDataUri, type RenderOptions } from './render';
8
+ export { LvIconElement, defineIconElement } from './element';
9
+ export { CANVAS, DEFAULT_SIZE, DEFAULT_STROKE, resolveGeometry, resolveStroke, resolveTransform, resolveFill, type IconFlip, type IconGeometry, } from './core/props';
10
+ export { CONTEXT, VARIANT_FILL, STATE_FILL, STATE_OPACITY, opticalStroke, resolveContext, resolveVariantFill, type ContextPreset, type IconContext, type IconState, type IconVariant, } from './core/variants';
11
+ export { DURATION, EASE, cssDuration, prefersReducedMotion, resolveDuration, } from './core/motion';
package/dist/index.js ADDED
@@ -0,0 +1,84 @@
1
+ import { iconGroups as e, iconNames as t, isIconName as n, paths as r } from "./paths.js";
2
+ import { _ as i, a, c as o, d as s, f as c, g as l, h as u, i as d, l as f, m as p, n as m, o as h, p as g, r as _, s as v, t as y, u as b, v as x, y as S } from "./motion-72rOKpWt.js";
3
+ //#region src/render.ts
4
+ var C = "http://www.w3.org/2000/svg", w = (e) => e.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;"), T = (e, t) => {
5
+ let n = i(t), a = l(t.fill), o = t.label == null || t.label.length === 0, s = {
6
+ xmlns: C,
7
+ class: t.class ?? "lv-icon",
8
+ width: String(n.size),
9
+ height: String(n.size),
10
+ viewBox: "0 0 24 24",
11
+ fill: "none",
12
+ stroke: "currentColor",
13
+ "stroke-width": String(n.strokeWidth),
14
+ "stroke-linecap": "round",
15
+ "stroke-linejoin": "round"
16
+ };
17
+ return o ? s["aria-hidden"] = "true" : (s.role = "img", s["aria-label"] = t.label), {
18
+ attrs: s,
19
+ shapes: r[e] ?? [],
20
+ transform: n.transform,
21
+ fill: a
22
+ };
23
+ }, E = (e, t = {}) => {
24
+ let { attrs: n, shapes: r, transform: i, fill: a } = T(e, t), o = Object.entries(n).map(([e, t]) => `${e}="${w(t)}"`).join(" "), s = a > 0 ? ` fill="currentColor" fill-opacity="${a}"` : "", c = r.map((e) => `<path d="${w(e)}"${s}/>`).join("");
25
+ return `<svg ${o}>${i === void 0 ? c : `<g transform="${w(i)}">${c}</g>`}</svg>`;
26
+ }, D = (e, t = {}) => {
27
+ if (typeof document > "u") return null;
28
+ let { attrs: n, shapes: r, transform: i, fill: a } = T(e, t), o = document.createElementNS(C, "svg");
29
+ for (let [e, t] of Object.entries(n)) o.setAttribute(e, t);
30
+ let s = i === void 0 ? o : (() => {
31
+ let e = document.createElementNS(C, "g");
32
+ return e.setAttribute("transform", i), o.appendChild(e), e;
33
+ })();
34
+ for (let e of r) {
35
+ let t = document.createElementNS(C, "path");
36
+ t.setAttribute("d", e), a > 0 && (t.setAttribute("fill", "currentColor"), t.setAttribute("fill-opacity", String(a))), s.appendChild(t);
37
+ }
38
+ return o;
39
+ }, O = (e, t = {}) => `data:image/svg+xml,${encodeURIComponent(E(e, t))}`, k = [
40
+ "name",
41
+ "size",
42
+ "stroke-width",
43
+ "absolute-stroke-width",
44
+ "fill",
45
+ "rotate",
46
+ "flip",
47
+ "label"
48
+ ], A = (e) => e.replace(/-([a-z])/g, (e, t) => t.toUpperCase()), j = (e) => {
49
+ if (e == null) return null;
50
+ let t = A(e.trim());
51
+ return n(t) ? t : null;
52
+ }, M = (e, t) => {
53
+ if (e == null) return t;
54
+ let n = Number.parseFloat(e);
55
+ return Number.isFinite(n) ? n : t;
56
+ }, N = class extends HTMLElement {
57
+ static get observedAttributes() {
58
+ return k;
59
+ }
60
+ connectedCallback() {
61
+ this.render();
62
+ }
63
+ attributeChangedCallback() {
64
+ this.isConnected && this.render();
65
+ }
66
+ render() {
67
+ let e = j(this.getAttribute("name"));
68
+ if (this.replaceChildren(), e === null) return;
69
+ let t = this.getAttribute("flip"), n = D(e, {
70
+ size: M(this.getAttribute("size"), 24),
71
+ strokeWidth: M(this.getAttribute("stroke-width"), 2),
72
+ absoluteStrokeWidth: this.hasAttribute("absolute-stroke-width"),
73
+ fill: M(this.getAttribute("fill"), 0),
74
+ rotate: M(this.getAttribute("rotate"), 0),
75
+ ...t === "horizontal" || t === "vertical" || t === "both" ? { flip: t } : {},
76
+ ...this.getAttribute("label") == null ? {} : { label: this.getAttribute("label") }
77
+ });
78
+ n !== null && this.appendChild(n);
79
+ }
80
+ }, P = (e = "lv-icon") => {
81
+ typeof window > "u" || typeof customElements > "u" || customElements.get(e) === void 0 && customElements.define(e, N);
82
+ };
83
+ //#endregion
84
+ export { g as CANVAS, h as CONTEXT, p as DEFAULT_SIZE, u as DEFAULT_STROKE, y as DURATION, m as EASE, N as LvIconElement, v as STATE_FILL, o as STATE_OPACITY, f as VARIANT_FILL, D as createIcon, _ as cssDuration, P as defineIconElement, O as iconDataUri, e as iconGroups, t as iconNames, n as isIconName, b as opticalStroke, r as paths, d as prefersReducedMotion, E as renderIcon, s as resolveContext, a as resolveDuration, l as resolveFill, i as resolveGeometry, x as resolveStroke, S as resolveTransform, c as resolveVariantFill };
@@ -0,0 +1,85 @@
1
+ //#region src/core/props.ts
2
+ var e = 24, t = 24, n = 2, r = (e, t) => {
3
+ if (typeof e == "number") return Number.isFinite(e) ? e : t;
4
+ if (typeof e == "string") {
5
+ let n = Number.parseFloat(e);
6
+ return Number.isFinite(n) ? n : t;
7
+ }
8
+ return t;
9
+ }, i = (e, t, n) => {
10
+ let i = r(e, 2);
11
+ return !n || t <= 0 ? i : i * 24 / t;
12
+ }, a = (e) => (r(e, 0) % 360 + 360) % 360, o = (e) => e === "horizontal" ? [-1, 1] : e === "vertical" ? [1, -1] : e === "both" ? [-1, -1] : [1, 1], s = (e, t) => {
13
+ let n = a(e), [r, i] = o(t);
14
+ if (n === 0 && r === 1 && i === 1) return;
15
+ let s = ["translate(12 12)"];
16
+ return n !== 0 && s.push(`rotate(${n})`), (r !== 1 || i !== 1) && s.push(`scale(${r} ${i})`), s.push("translate(-12 -12)"), s.join(" ");
17
+ }, c = (e) => {
18
+ let t = r(e.size, 24);
19
+ return {
20
+ size: t,
21
+ strokeWidth: i(e.strokeWidth, t, e.absoluteStrokeWidth === !0),
22
+ transform: s(e.rotate, e.flip)
23
+ };
24
+ }, l = (e) => {
25
+ if (e === !0) return 1;
26
+ if (e === void 0 || e === !1) return 0;
27
+ let t = r(e, 0);
28
+ return t < 0 ? 0 : t > 1 ? 1 : t;
29
+ }, u = {
30
+ outline: 0,
31
+ duotone: .18,
32
+ filled: 1
33
+ }, d = (e) => u[e ?? "outline"] ?? 0, f = {
34
+ inline: {
35
+ size: 16,
36
+ strokeWidth: 2,
37
+ hitArea: 0
38
+ },
39
+ toolbar: {
40
+ size: 20,
41
+ strokeWidth: 2,
42
+ hitArea: 44
43
+ },
44
+ button: {
45
+ size: 18,
46
+ strokeWidth: 2,
47
+ hitArea: 44
48
+ },
49
+ navigation: {
50
+ size: 22,
51
+ strokeWidth: 1.9,
52
+ hitArea: 48
53
+ },
54
+ status: {
55
+ size: 20,
56
+ strokeWidth: 2,
57
+ hitArea: 0
58
+ },
59
+ hero: {
60
+ size: 40,
61
+ strokeWidth: 1.6,
62
+ hitArea: 0
63
+ }
64
+ }, p = (e) => e === void 0 ? null : f[e] ?? null, m = (e, t = 2) => e <= 16 ? t * .9 : e <= 20 ? t * .95 : e <= 28 ? t : e <= 40 ? t * .85 : t * .75, h = {
65
+ default: 0,
66
+ hover: .12,
67
+ active: 1,
68
+ selected: 1,
69
+ disabled: 0
70
+ }, g = {
71
+ default: 1,
72
+ hover: 1,
73
+ active: 1,
74
+ selected: 1,
75
+ disabled: .38
76
+ }, _ = {
77
+ micro: .18,
78
+ state: .28,
79
+ morph: .36
80
+ }, v = {
81
+ enter: "cubic-bezier(0.33, 1, 0.68, 1)",
82
+ morph: "cubic-bezier(0.65, 0, 0.35, 1)"
83
+ }, y = () => typeof window > "u" || typeof window.matchMedia != "function" ? !1 : window.matchMedia("(prefers-reduced-motion: reduce)").matches === !0, b = (e, t = y()) => t ? 0 : e, x = (e, t) => `${b(e, t)}s`;
84
+ //#endregion
85
+ export { c as _, b as a, g as c, p as d, d as f, l as g, n as h, y as i, u as l, t as m, v as n, f as o, e as p, x as r, h as s, _ as t, m as u, i as v, s as y };