@genarou/blazir-icons 1.2.11 → 1.2.15
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/Icon.svelte +196 -208
- package/dist/Icon.svelte.d.ts +15 -5
- package/dist/IconBase.svelte +254 -82
- package/dist/effects.d.ts +2 -0
- package/dist/effects.js +187 -95
- package/dist/global.d.ts +8 -0
- package/dist/icons/EmailAnimated.svelte +7 -32
- package/dist/icons/FileUploadAnimated.svelte +53 -68
- package/dist/icons/Heart.svelte +22 -0
- package/dist/icons/Heart.svelte.d.ts +4 -0
- package/dist/icons/ImageAnimated.svelte +38 -41
- package/dist/icons/Moon.svelte +19 -0
- package/dist/icons/Moon.svelte.d.ts +4 -0
- package/dist/icons/Png.svelte +17 -37
- package/dist/icons/Rocket.svelte +19 -0
- package/dist/icons/Rocket.svelte.d.ts +4 -0
- package/dist/icons/Share.svelte +19 -0
- package/dist/icons/Share.svelte.d.ts +4 -0
- package/dist/icons/Sun.svelte +19 -0
- package/dist/icons/Sun.svelte.d.ts +4 -0
- package/dist/icons/Timer.svelte +19 -0
- package/dist/icons/Timer.svelte.d.ts +4 -0
- package/dist/icons/World.svelte +19 -0
- package/dist/icons/World.svelte.d.ts +4 -0
- package/dist/icons/Xml.svelte +16 -18
- package/dist/icons/registry.d.ts +7 -0
- package/dist/icons/registry.js +14 -0
- package/dist/icons-api.d.ts +8 -1
- package/dist/icons-api.js +7 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/types/svelte-events.d.ts +9 -0
- package/dist/types.d.ts +1 -0
- package/dist/utils/debounce.d.ts +20 -0
- package/dist/utils/debounce.js +88 -0
- package/package.json +7 -2
package/dist/Icon.svelte
CHANGED
|
@@ -1,251 +1,206 @@
|
|
|
1
|
+
<!-- DynamicIcon.svelte -->
|
|
1
2
|
<script lang="ts">
|
|
3
|
+
import { coerceSize } from ".";
|
|
2
4
|
import { iconRegistry, type IconName } from "./icons/registry";
|
|
3
|
-
import {
|
|
4
|
-
iconPresets,
|
|
5
|
-
iconVariants,
|
|
6
|
-
type IconPreset,
|
|
7
|
-
type IconVariant,
|
|
8
|
-
} from "./presets";
|
|
5
|
+
import { iconPresets, iconVariants } from "./presets";
|
|
9
6
|
import type { IconProps } from "./types";
|
|
10
|
-
import { coerceSize } from "./utils/defaults";
|
|
11
|
-
|
|
12
|
-
// ───────────────────────────────────────────────────────────────────────────
|
|
13
|
-
// POOLS (1 IO por root scrolleable, 1 RO global)
|
|
14
|
-
// ───────────────────────────────────────────────────────────────────────────
|
|
15
|
-
type RootKey = Element | undefined;
|
|
16
|
-
type Entry = {
|
|
17
|
-
el: HTMLElement;
|
|
18
|
-
onViewportChange?: (v: boolean) => void;
|
|
19
|
-
onSizeChange?: (w: number, h: number) => void;
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
class IntersectionPool {
|
|
23
|
-
private pools = new Map<
|
|
24
|
-
RootKey,
|
|
25
|
-
{ io: IntersectionObserver; entries: Set<Entry> }
|
|
26
|
-
>();
|
|
27
|
-
|
|
28
|
-
register(root: RootKey, entry: Entry) {
|
|
29
|
-
let pool = this.pools.get(root);
|
|
30
|
-
if (!pool) {
|
|
31
|
-
const io = new IntersectionObserver(
|
|
32
|
-
(entries) => {
|
|
33
|
-
const p = this.pools.get(root);
|
|
34
|
-
if (!p) return;
|
|
35
|
-
for (const e of entries) {
|
|
36
|
-
const tgt = e.target as HTMLElement;
|
|
37
|
-
for (const item of p.entries) {
|
|
38
|
-
if (item.el === tgt) {
|
|
39
|
-
item.onViewportChange?.(e.isIntersecting);
|
|
40
|
-
break;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
},
|
|
45
|
-
{ root: root as Element | undefined, rootMargin: "200px" }
|
|
46
|
-
);
|
|
47
|
-
pool = { io, entries: new Set() };
|
|
48
|
-
this.pools.set(root, pool);
|
|
49
|
-
}
|
|
50
7
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
if (!p) return;
|
|
57
|
-
p.io.unobserve(entry.el);
|
|
58
|
-
p.entries.delete(entry);
|
|
59
|
-
if (p.entries.size === 0) {
|
|
60
|
-
p.io.disconnect();
|
|
61
|
-
this.pools.delete(root);
|
|
62
|
-
}
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
}
|
|
8
|
+
// Bridge de actions externas (no importamos lazy aquí)
|
|
9
|
+
type ActionFn<T = HTMLElement, P = any> = (
|
|
10
|
+
node: T,
|
|
11
|
+
params?: P
|
|
12
|
+
) => void | { update?: (p?: P) => void; destroy?: () => void };
|
|
66
13
|
|
|
67
|
-
|
|
68
|
-
private ro: ResizeObserver | null = null;
|
|
69
|
-
private entries = new Set<Entry>();
|
|
70
|
-
|
|
71
|
-
private ensure() {
|
|
72
|
-
if (this.ro || typeof ResizeObserver === "undefined") return;
|
|
73
|
-
this.ro = new ResizeObserver((ros) => {
|
|
74
|
-
for (const r of ros) {
|
|
75
|
-
const tgt = r.target as HTMLElement;
|
|
76
|
-
for (const item of this.entries) {
|
|
77
|
-
if (item.el === tgt) {
|
|
78
|
-
item.onSizeChange?.(tgt.offsetWidth, tgt.offsetHeight);
|
|
79
|
-
break;
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
});
|
|
84
|
-
}
|
|
14
|
+
type ActionEntry<T = HTMLElement> = [ActionFn<T, any>, any?];
|
|
85
15
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
}
|
|
98
|
-
};
|
|
99
|
-
}
|
|
16
|
+
type IconPresetName = keyof typeof iconPresets;
|
|
17
|
+
type IconVariantName = keyof typeof iconVariants;
|
|
18
|
+
|
|
19
|
+
interface DynamicIconProps extends Omit<IconProps, "children"> {
|
|
20
|
+
name?: IconName;
|
|
21
|
+
preset?: IconPresetName;
|
|
22
|
+
variant?: IconVariantName;
|
|
23
|
+
class?: string;
|
|
24
|
+
style?: string;
|
|
25
|
+
/** Pasas aquí tus actions: p.ej. actions={[[lazyClass, opts]]} */
|
|
26
|
+
actions?: ActionEntry[];
|
|
100
27
|
}
|
|
101
28
|
|
|
102
|
-
const
|
|
103
|
-
const RO_POOL = new ResizePool();
|
|
29
|
+
const p: DynamicIconProps = $props();
|
|
104
30
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
while (p) {
|
|
108
|
-
const s = getComputedStyle(p);
|
|
109
|
-
if (/(auto|scroll)/.test(`${s.overflow}${s.overflowX}${s.overflowY}`))
|
|
110
|
-
return p;
|
|
111
|
-
p = p.parentElement;
|
|
112
|
-
}
|
|
113
|
-
return undefined;
|
|
114
|
-
}
|
|
31
|
+
// 🔥 OPTIMIZACIÓN: Cache para props combinadas
|
|
32
|
+
const propsCache = new Map<string, any>();
|
|
115
33
|
|
|
116
|
-
//
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
34
|
+
// 🔥 OPTIMIZACIÓN: Derivados en lugar de effects + states
|
|
35
|
+
const iconName = $derived(p.name ?? null);
|
|
36
|
+
const Comp = $derived(iconName ? iconRegistry[iconName] : null);
|
|
37
|
+
|
|
38
|
+
// 🔥 OPTIMIZACIÓN: Preset y variant como derivados
|
|
39
|
+
const presetProps = $derived(() => {
|
|
40
|
+
const key = p.preset as IconPresetName | undefined;
|
|
41
|
+
return key ? iconPresets[key] : {};
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const variantProps = $derived(() => {
|
|
45
|
+
const key = p.variant as IconVariantName | undefined;
|
|
46
|
+
return key ? iconVariants[key] : {};
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// 🔥 OPTIMIZACIÓN: Helper para normalizar ms
|
|
50
|
+
function toMs(v: unknown): string | undefined {
|
|
51
|
+
if (v == null) return undefined;
|
|
52
|
+
return typeof v === "number" ? `${v}ms` : String(v);
|
|
124
53
|
}
|
|
125
54
|
|
|
126
|
-
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
const Comp = $derived.by(() => iconRegistry[inputProps().name]);
|
|
130
|
-
const presetProps = $derived(() =>
|
|
131
|
-
inputProps().preset ? iconPresets[inputProps().preset!] : {}
|
|
132
|
-
);
|
|
133
|
-
const variantProps = $derived(() =>
|
|
134
|
-
inputProps().variant ? iconVariants[inputProps().variant!] : {}
|
|
135
|
-
);
|
|
136
|
-
const mergedProps = $derived(() => ({
|
|
137
|
-
...presetProps(),
|
|
138
|
-
...variantProps(),
|
|
139
|
-
...inputProps(),
|
|
140
|
-
}));
|
|
141
|
-
|
|
142
|
-
// 🔹 No pasar `skeleton` al hijo
|
|
143
|
-
const childProps = $derived(() => {
|
|
144
|
-
const { skeleton, animationDuration, animationDelay, ...rest } =
|
|
145
|
-
mergedProps();
|
|
146
|
-
|
|
147
|
-
const toMsString = (v?: string | number) =>
|
|
148
|
-
v === undefined ? undefined : typeof v === "number" ? `${v}ms` : v;
|
|
55
|
+
// 🔥 OPTIMIZACIÓN: Child props con memoización
|
|
56
|
+
const child = $derived(() => {
|
|
57
|
+
const cacheKey = `${p.preset || ""}-${p.variant || ""}-${p.name || ""}`;
|
|
149
58
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
59
|
+
// Check cache primero
|
|
60
|
+
const cached = propsCache.get(cacheKey);
|
|
61
|
+
if (cached && cached.timestamp > Date.now() - 1000) {
|
|
62
|
+
// Cache válido por 1s
|
|
63
|
+
return { ...cached.props, ...p };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const merged = {
|
|
67
|
+
...presetProps(),
|
|
68
|
+
...variantProps(),
|
|
69
|
+
...p,
|
|
154
70
|
};
|
|
71
|
+
|
|
72
|
+
// Extraer y eliminar propiedades que no van al hijo
|
|
73
|
+
const { class: _class, style: _style, actions: _actions, ...rest } = merged;
|
|
74
|
+
|
|
75
|
+
// Normalizar animationDuration y animationDelay
|
|
76
|
+
const normalized: any = { ...rest };
|
|
77
|
+
|
|
78
|
+
if (normalized.animationDuration !== undefined) {
|
|
79
|
+
normalized.animationDuration = toMs(normalized.animationDuration);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (normalized.animationDelay !== undefined) {
|
|
83
|
+
normalized.animationDelay = toMs(normalized.animationDelay);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const result = normalized as IconProps;
|
|
87
|
+
|
|
88
|
+
// Guardar en cache
|
|
89
|
+
propsCache.set(cacheKey, {
|
|
90
|
+
props: result,
|
|
91
|
+
timestamp: Date.now(),
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// Limpiar cache viejo (mantener solo últimos 50)
|
|
95
|
+
if (propsCache.size > 50) {
|
|
96
|
+
const firstKey = propsCache.keys().next().value;
|
|
97
|
+
if (firstKey !== undefined) {
|
|
98
|
+
propsCache.delete(firstKey);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return result;
|
|
155
103
|
});
|
|
156
104
|
|
|
105
|
+
// 🔥 OPTIMIZACIÓN: Box size como derivado
|
|
157
106
|
const boxSize = $derived(() => {
|
|
158
|
-
const
|
|
107
|
+
const rawSize =
|
|
108
|
+
(p as any).size ??
|
|
109
|
+
(presetProps() as any).size ??
|
|
110
|
+
(variantProps() as any).size;
|
|
111
|
+
const s = coerceSize(rawSize, 24);
|
|
159
112
|
return typeof s === "number" ? `${s}px` : s;
|
|
160
113
|
});
|
|
161
114
|
|
|
162
|
-
//
|
|
163
|
-
|
|
164
|
-
// ───────────────────────────────────────────────────────────────────────────
|
|
165
|
-
let hostEl: HTMLElement | null = $state(null);
|
|
166
|
-
let isVisible = $state(false);
|
|
167
|
-
let inViewport = false,
|
|
168
|
-
hasSize = false;
|
|
169
|
-
let unregIO: (() => void) | null = null,
|
|
170
|
-
unregRO: (() => void) | null = null;
|
|
171
|
-
|
|
172
|
-
function tryMount() {
|
|
173
|
-
if (!isVisible && inViewport && hasSize) {
|
|
174
|
-
isVisible = true;
|
|
175
|
-
unregIO?.();
|
|
176
|
-
unregIO = null;
|
|
177
|
-
unregRO?.();
|
|
178
|
-
unregRO = null;
|
|
179
|
-
}
|
|
180
|
-
}
|
|
115
|
+
// 🔥 OPTIMIZACIÓN: Host class como derivado
|
|
116
|
+
const hostClass = $derived(`bz-icon-wrapper ${p.class ?? ""}`.trim());
|
|
181
117
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
onViewportChange: (v) => {
|
|
196
|
-
inViewport = v;
|
|
197
|
-
tryMount();
|
|
198
|
-
},
|
|
199
|
-
});
|
|
118
|
+
// 🔥 OPTIMIZACIÓN: Host style como derivado
|
|
119
|
+
const hostStyle = $derived(() => {
|
|
120
|
+
const size = boxSize();
|
|
121
|
+
return (
|
|
122
|
+
`--bz-icon-size:${size};` +
|
|
123
|
+
`width:${size};height:${size};` +
|
|
124
|
+
`inline-size:${size};block-size:${size};` +
|
|
125
|
+
`${p.style ?? ""}`
|
|
126
|
+
);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// Key para forzar remonte y reiniciar animaciones
|
|
130
|
+
let mountKey = $state(0);
|
|
200
131
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
hasSize = w > 0 && h > 0;
|
|
206
|
-
tryMount();
|
|
207
|
-
},
|
|
208
|
-
});
|
|
209
|
-
} else {
|
|
210
|
-
hasSize = hostEl.offsetWidth > 0 && hostEl.offsetHeight > 0;
|
|
211
|
-
tryMount();
|
|
132
|
+
// 🔥 OPTIMIZACIÓN: Bridge de actions más eficiente
|
|
133
|
+
function applyActions(node: HTMLElement, entries: ActionEntry[] = []) {
|
|
134
|
+
if (!entries || entries.length === 0) {
|
|
135
|
+
return { update() {}, destroy() {} };
|
|
212
136
|
}
|
|
213
137
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
138
|
+
let handles = entries
|
|
139
|
+
.map(([fn, params]) => fn?.(node, params))
|
|
140
|
+
.filter(Boolean);
|
|
141
|
+
|
|
142
|
+
return {
|
|
143
|
+
update(next: ActionEntry[] = []) {
|
|
144
|
+
// Cleanup anterior
|
|
145
|
+
handles.forEach((h) => h?.destroy?.());
|
|
146
|
+
|
|
147
|
+
// Aplicar nuevos
|
|
148
|
+
handles = next
|
|
149
|
+
.map(([fn, params]) => fn?.(node, params))
|
|
150
|
+
.filter(Boolean);
|
|
151
|
+
},
|
|
152
|
+
destroy() {
|
|
153
|
+
handles.forEach((h) => h?.destroy?.());
|
|
154
|
+
handles = [];
|
|
155
|
+
},
|
|
219
156
|
};
|
|
220
|
-
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Reanima al recibir el evento del action
|
|
160
|
+
function onLazyMount() {
|
|
161
|
+
mountKey++;
|
|
162
|
+
}
|
|
221
163
|
</script>
|
|
222
164
|
|
|
223
165
|
{#if Comp}
|
|
224
166
|
<span
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
167
|
+
class={hostClass}
|
|
168
|
+
style={hostStyle()}
|
|
169
|
+
use:applyActions={p.actions ?? []}
|
|
170
|
+
onlazyMount={onLazyMount}
|
|
229
171
|
>
|
|
230
|
-
{#
|
|
231
|
-
<Comp {...
|
|
232
|
-
{
|
|
233
|
-
<span class="bz-icon-skeleton" aria-hidden="true"></span>
|
|
234
|
-
{/if}
|
|
172
|
+
{#key mountKey}
|
|
173
|
+
<Comp {...child()} />
|
|
174
|
+
{/key}
|
|
235
175
|
</span>
|
|
236
|
-
{:else}
|
|
237
|
-
<span class="bz-icon-error" data-icon={
|
|
238
|
-
⚠️ Icon not found: {
|
|
176
|
+
{:else if iconName}
|
|
177
|
+
<span class="bz-icon-error" data-icon={iconName}>
|
|
178
|
+
⚠️ Icon not found: {iconName}
|
|
239
179
|
</span>
|
|
180
|
+
{:else}
|
|
181
|
+
<span class="bz-icon-skeleton" aria-hidden="true"></span>
|
|
240
182
|
{/if}
|
|
241
183
|
|
|
242
184
|
<style>
|
|
243
185
|
.bz-icon-wrapper {
|
|
186
|
+
cursor: pointer;
|
|
244
187
|
display: inline-flex;
|
|
245
188
|
align-items: center;
|
|
246
189
|
justify-content: center;
|
|
247
|
-
|
|
190
|
+
position: relative;
|
|
191
|
+
pointer-events: auto;
|
|
192
|
+
contain: layout style paint;
|
|
193
|
+
content-visibility: auto;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
.bz-icon-wrapper :global(svg) {
|
|
197
|
+
pointer-events: bounding-box !important;
|
|
248
198
|
}
|
|
199
|
+
|
|
200
|
+
.bz-icon-wrapper :global(svg *) {
|
|
201
|
+
pointer-events: none !important;
|
|
202
|
+
}
|
|
203
|
+
|
|
249
204
|
.bz-icon-skeleton {
|
|
250
205
|
width: 100%;
|
|
251
206
|
height: 100%;
|
|
@@ -265,4 +220,37 @@
|
|
|
265
220
|
border-radius: var(--ui-radius, 0.25rem);
|
|
266
221
|
font-weight: 600;
|
|
267
222
|
}
|
|
223
|
+
.is-entering {
|
|
224
|
+
opacity: 0.001;
|
|
225
|
+
transform: translateY(4px);
|
|
226
|
+
animation: ic-enter 160ms ease-out forwards;
|
|
227
|
+
}
|
|
228
|
+
.is-leaving {
|
|
229
|
+
opacity: 1;
|
|
230
|
+
transform: none;
|
|
231
|
+
animation: ic-leave 160ms ease-in forwards;
|
|
232
|
+
}
|
|
233
|
+
.is-mounted {
|
|
234
|
+
opacity: 1;
|
|
235
|
+
transform: none;
|
|
236
|
+
}
|
|
237
|
+
@keyframes ic-enter {
|
|
238
|
+
to {
|
|
239
|
+
opacity: 1;
|
|
240
|
+
transform: none;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
@keyframes ic-leave {
|
|
244
|
+
to {
|
|
245
|
+
opacity: 0.001;
|
|
246
|
+
transform: translateY(4px);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
@media (prefers-reduced-motion: reduce) {
|
|
250
|
+
.is-entering,
|
|
251
|
+
.is-leaving {
|
|
252
|
+
animation: none;
|
|
253
|
+
transform: none;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
268
256
|
</style>
|
package/dist/Icon.svelte.d.ts
CHANGED
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
import { type IconName } from "./icons/registry";
|
|
2
|
-
import {
|
|
2
|
+
import { iconPresets, iconVariants } from "./presets";
|
|
3
3
|
import type { IconProps } from "./types";
|
|
4
|
+
type ActionFn<T = HTMLElement, P = any> = (node: T, params?: P) => void | {
|
|
5
|
+
update?: (p?: P) => void;
|
|
6
|
+
destroy?: () => void;
|
|
7
|
+
};
|
|
8
|
+
type ActionEntry<T = HTMLElement> = [ActionFn<T, any>, any?];
|
|
9
|
+
type IconPresetName = keyof typeof iconPresets;
|
|
10
|
+
type IconVariantName = keyof typeof iconVariants;
|
|
4
11
|
interface DynamicIconProps extends Omit<IconProps, "children"> {
|
|
5
|
-
name
|
|
6
|
-
preset?:
|
|
7
|
-
variant?:
|
|
8
|
-
|
|
12
|
+
name?: IconName;
|
|
13
|
+
preset?: IconPresetName;
|
|
14
|
+
variant?: IconVariantName;
|
|
15
|
+
class?: string;
|
|
16
|
+
style?: string;
|
|
17
|
+
/** Pasas aquí tus actions: p.ej. actions={[[lazyClass, opts]]} */
|
|
18
|
+
actions?: ActionEntry[];
|
|
9
19
|
}
|
|
10
20
|
declare const Icon: import("svelte").Component<DynamicIconProps, {}, "">;
|
|
11
21
|
type Icon = ReturnType<typeof Icon>;
|