@genarou/blazir-icons 1.2.11 → 1.2.13

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 CHANGED
@@ -1,251 +1,145 @@
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
7
 
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
- };
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 };
21
13
 
22
- class IntersectionPool {
23
- private pools = new Map<
24
- RootKey,
25
- { io: IntersectionObserver; entries: Set<Entry> }
26
- >();
14
+ type ActionEntry<T = HTMLElement> = [ActionFn<T, any>, any?];
27
15
 
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
- }
16
+ type IconPresetName = keyof typeof iconPresets;
17
+ type IconVariantName = keyof typeof iconVariants;
50
18
 
51
- pool.entries.add(entry);
52
- pool.io.observe(entry.el);
53
-
54
- return () => {
55
- const p = this.pools.get(root);
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
- }
66
-
67
- class ResizePool {
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
- }
85
-
86
- register(entry: Entry) {
87
- this.ensure();
88
- this.entries.add(entry);
89
- this.ro?.observe(entry.el);
90
- entry.onSizeChange?.(entry.el.offsetWidth, entry.el.offsetHeight); // inicial
91
- return () => {
92
- this.ro?.unobserve(entry.el);
93
- this.entries.delete(entry);
94
- if (this.entries.size === 0) {
95
- this.ro?.disconnect();
96
- this.ro = null;
97
- }
98
- };
99
- }
100
- }
101
-
102
- const IO_POOL = new IntersectionPool();
103
- const RO_POOL = new ResizePool();
104
-
105
- function getScrollRoot(el: HTMLElement | null): Element | undefined {
106
- let p: HTMLElement | null = el?.parentElement ?? null;
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
- }
115
-
116
- // ───────────────────────────────────────────────────────────────────────────
117
- // PROPS
118
- // ───────────────────────────────────────────────────────────────────────────
119
19
  interface DynamicIconProps extends Omit<IconProps, "children"> {
120
- name: IconName;
121
- preset?: IconPreset;
122
- variant?: IconVariant;
123
- skeleton?: boolean;
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[];
124
27
  }
125
28
 
126
- const props: DynamicIconProps = $props();
127
- const inputProps = $derived(() => ({ skeleton: false, ...props }));
29
+ const p: DynamicIconProps = $props();
128
30
 
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
- }));
31
+ let iconName = $state<IconName | null>(null);
32
+ let Comp = $state<any>(null);
141
33
 
142
- // 🔹 No pasar `skeleton` al hijo
143
- const childProps = $derived(() => {
144
- const { skeleton, animationDuration, animationDelay, ...rest } =
145
- mergedProps();
34
+ let presetProps = $state<Partial<IconProps>>({});
35
+ let variantProps = $state<Partial<IconProps>>({});
146
36
 
147
- const toMsString = (v?: string | number) =>
148
- v === undefined ? undefined : typeof v === "number" ? `${v}ms` : v;
37
+ let child = $state<IconProps>({} as IconProps);
38
+ let boxSize = $state<string>("1em");
39
+ let hostClass = $state<string>("bz-icon-wrapper");
40
+ let hostStyle = $state<string>("");
149
41
 
150
- return {
151
- ...rest,
152
- animationDuration: toMsString(animationDuration),
153
- animationDelay: toMsString(animationDelay),
154
- };
42
+ // Key para forzar remonte y reiniciar animaciones
43
+ let mountKey = $state(0);
44
+
45
+ // name -> componente
46
+ $effect(() => {
47
+ iconName = p.name ?? null;
48
+ Comp = iconName ? iconRegistry[iconName] : null;
155
49
  });
156
50
 
157
- const boxSize = $derived(() => {
158
- const s = coerceSize(mergedProps().size, 24);
159
- return typeof s === "number" ? `${s}px` : s;
51
+ // preset/variant -> props parciales
52
+ $effect(() => {
53
+ const key = p.preset as IconPresetName | undefined;
54
+ presetProps = key ? iconPresets[key] : {};
55
+ });
56
+ $effect(() => {
57
+ const key = p.variant as IconVariantName | undefined;
58
+ variantProps = key ? iconVariants[key] : {};
160
59
  });
161
60
 
162
- // ───────────────────────────────────────────────────────────────────────────
163
- // Lazy-mount con pooling
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;
61
+ // Limpia class/style/actions del host antes de pasar al hijo
62
+ $effect(() => {
63
+ const {
64
+ class: _class,
65
+ style: _style,
66
+ actions: _actions,
67
+ ...restRaw
68
+ } = { ...presetProps, ...variantProps, ...p } as Record<string, unknown>;
69
+
70
+ const toMs = (v: unknown) =>
71
+ v == null ? undefined : typeof v === "number" ? `${v}ms` : (v as string);
72
+
73
+ child = {
74
+ ...(restRaw as IconProps),
75
+ animationDuration: toMs((restRaw as any).animationDuration),
76
+ animationDelay: toMs((restRaw as any).animationDelay),
77
+ } as IconProps;
78
+ });
171
79
 
172
- function tryMount() {
173
- if (!isVisible && inViewport && hasSize) {
174
- isVisible = true;
175
- unregIO?.();
176
- unregIO = null;
177
- unregRO?.();
178
- unregRO = null;
179
- }
180
- }
80
+ // Tamaño del wrapper
81
+ $effect(() => {
82
+ const rawSize =
83
+ (p as any).size ??
84
+ (presetProps as any).size ??
85
+ (variantProps as any).size;
86
+ const s = coerceSize(rawSize, 24);
87
+ boxSize = typeof s === "number" ? `${s}px` : s;
88
+ });
181
89
 
90
+ // Host attrs
182
91
  $effect(() => {
183
- if (
184
- typeof window === "undefined" ||
185
- typeof IntersectionObserver === "undefined"
186
- ) {
187
- isVisible = true;
188
- return;
189
- }
190
- if (!hostEl || isVisible) return;
92
+ hostClass = `bz-icon-wrapper ${p.class ?? ""}`.trim();
93
+ hostStyle =
94
+ `--bz-icon-size:${boxSize};` +
95
+ `width:${boxSize};height:${boxSize};` +
96
+ `inline-size:${boxSize};block-size:${boxSize};` +
97
+ `${p.style ?? ""}`;
98
+ });
191
99
 
192
- const root = getScrollRoot(hostEl) ?? undefined;
193
- unregIO = IO_POOL.register(root, {
194
- el: hostEl,
195
- onViewportChange: (v) => {
196
- inViewport = v;
197
- tryMount();
100
+ // Bridge para aplicar actions externas
101
+ function applyActions(node: HTMLElement, entries: ActionEntry[] = []) {
102
+ let handles =
103
+ entries?.map(([fn, params]) => fn?.(node, params)).filter(Boolean) ?? [];
104
+ return {
105
+ update(next: ActionEntry[] = []) {
106
+ handles.forEach((h) => h?.destroy?.());
107
+ handles =
108
+ next?.map(([fn, params]) => fn?.(node, params)).filter(Boolean) ?? [];
109
+ },
110
+ destroy() {
111
+ handles.forEach((h) => h?.destroy?.());
112
+ handles = [];
198
113
  },
199
- });
200
-
201
- if (typeof ResizeObserver !== "undefined") {
202
- unregRO = RO_POOL.register({
203
- el: hostEl,
204
- onSizeChange: (w, h) => {
205
- hasSize = w > 0 && h > 0;
206
- tryMount();
207
- },
208
- });
209
- } else {
210
- hasSize = hostEl.offsetWidth > 0 && hostEl.offsetHeight > 0;
211
- tryMount();
212
- }
213
-
214
- return () => {
215
- unregIO?.();
216
- unregIO = null;
217
- unregRO?.();
218
- unregRO = null;
219
114
  };
220
- });
115
+ }
116
+
117
+ // Reanima al recibir el evento del action (asegúrate de que el action emita "lazyMount")
118
+ function onLazyMount() {
119
+ mountKey++;
120
+ }
221
121
  </script>
222
122
 
223
123
  {#if Comp}
224
124
  <span
225
- bind:this={hostEl}
226
- class="bz-icon-wrapper"
227
- style={`inline-size:${boxSize};block-size:${boxSize};`}
228
- data-mounted={isVisible ? "true" : "false"}
125
+ class={hostClass}
126
+ style={hostStyle}
127
+ use:applyActions={p.actions ?? []}
128
+ onlazyMount={onLazyMount}
229
129
  >
230
- {#if isVisible}
231
- <Comp {...childProps()} />
232
- {:else if mergedProps().skeleton}
233
- <span class="bz-icon-skeleton" aria-hidden="true"></span>
234
- {/if}
130
+ {#key mountKey}
131
+ <Comp {...child} />
132
+ {/key}
235
133
  </span>
236
- {:else}
237
- <span class="bz-icon-error" data-icon={inputProps().name}>
238
- ⚠️ Icon not found: {inputProps().name}
134
+ {:else if iconName}
135
+ <span class="bz-icon-error" data-icon={iconName}>
136
+ ⚠️ Icon not found: {iconName}
239
137
  </span>
138
+ {:else}
139
+ <span class="bz-icon-skeleton" aria-hidden="true"></span>
240
140
  {/if}
241
141
 
242
142
  <style>
243
- .bz-icon-wrapper {
244
- display: inline-flex;
245
- align-items: center;
246
- justify-content: center;
247
- line-height: 1;
248
- }
249
143
  .bz-icon-skeleton {
250
144
  width: 100%;
251
145
  height: 100%;
@@ -265,4 +159,37 @@
265
159
  border-radius: var(--ui-radius, 0.25rem);
266
160
  font-weight: 600;
267
161
  }
162
+ .is-entering {
163
+ opacity: 0.001;
164
+ transform: translateY(4px);
165
+ animation: ic-enter 160ms ease-out forwards;
166
+ }
167
+ .is-leaving {
168
+ opacity: 1;
169
+ transform: none;
170
+ animation: ic-leave 160ms ease-in forwards;
171
+ }
172
+ .is-mounted {
173
+ opacity: 1;
174
+ transform: none;
175
+ }
176
+ @keyframes ic-enter {
177
+ to {
178
+ opacity: 1;
179
+ transform: none;
180
+ }
181
+ }
182
+ @keyframes ic-leave {
183
+ to {
184
+ opacity: 0.001;
185
+ transform: translateY(4px);
186
+ }
187
+ }
188
+ @media (prefers-reduced-motion: reduce) {
189
+ .is-entering,
190
+ .is-leaving {
191
+ animation: none;
192
+ transform: none;
193
+ }
194
+ }
268
195
  </style>
@@ -1,11 +1,21 @@
1
1
  import { type IconName } from "./icons/registry";
2
- import { type IconPreset, type IconVariant } from "./presets";
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: IconName;
6
- preset?: IconPreset;
7
- variant?: IconVariant;
8
- skeleton?: boolean;
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>;
@@ -1,11 +1,9 @@
1
+ <!-- IconBase.svelte -->
1
2
  <script lang="ts">
2
3
  import { type IconEffectOptions, iconEffects } from "./effects.js";
4
+
3
5
  import type { IconMode, IconProps } from "./types.js";
4
- import {
5
- combineTransforms,
6
- getAnimationClasses,
7
- getAnimationStyle,
8
- } from "./utils/animations.js";
6
+ import { combineTransforms, getAnimationStyle } from "./utils/animations.js";
9
7
  import {
10
8
  commonDefaults,
11
9
  modeDefaults,
@@ -30,26 +28,31 @@
30
28
  }
31
29
 
32
30
  const props: IconProps & { mode?: IconMode } = $props();
31
+
32
+ // Hover (nativo + heredado)
33
33
  let internalHovered = $state(false);
34
34
  const isHovered = $derived(
35
35
  internalHovered || (props.parentHoverContext?.hovered ?? false)
36
36
  );
37
37
 
38
+ // Color efectivo
38
39
  const DEFAULT_ICON_COLOR = "var(--icon-fg, var(--ui-muted-fg, currentColor))";
39
40
  const effectiveColor = $derived(
40
41
  (isHovered && props.hoverColor ? props.hoverColor : props.color) ??
41
42
  DEFAULT_ICON_COLOR
42
43
  );
43
44
 
45
+ // Visuales / defaults
44
46
  const mode = $derived(props.mode ?? "solid");
45
47
  const klass = $derived(normalizeClass(props));
46
48
  const common = $derived(commonDefaults(props));
47
-
48
49
  const propsWithEffectiveColor = $derived({ ...props, color: effectiveColor });
49
50
  const visual = $derived(modeDefaults(mode, propsWithEffectiveColor));
50
- const animationClasses = $derived(getAnimationClasses(props).join(" "));
51
+
52
+ // Sólo para variables de timing (no clases de animación)
51
53
  const timingStyle = $derived(getAnimationStyle(props));
52
54
 
55
+ // Transforms declarativos (NO incluye rotate/flip; esos van en el <g> geometry)
53
56
  const cssTransformOnly = $derived(
54
57
  combineTransforms({
55
58
  ...props,
@@ -59,39 +62,75 @@
59
62
  })
60
63
  );
61
64
 
65
+ // Duraciones / easing
66
+ const hoverMs = $derived(
67
+ normalizeMs(props.transitionMs ?? props.animationDuration)
68
+ );
69
+ const hoverEase = $derived(
70
+ props.transitionEasing ?? props.animationEasing ?? DEFAULT_EASING
71
+ );
72
+
73
+ // Spin continuo → inline animation (sin clases)
62
74
  const spinDuration = $derived(
63
75
  props.spin
64
76
  ? props.spin === true
65
77
  ? "1s"
66
78
  : typeof props.spin === "number"
67
79
  ? `${props.spin}ms`
68
- : props.spin
80
+ : String(props.spin)
69
81
  : null
70
82
  );
71
83
 
72
- const hoverMs = $derived(
73
- normalizeMs(props.transitionMs ?? props.animationDuration)
74
- );
75
- const hoverEase = $derived(
76
- props.transitionEasing ?? props.animationEasing ?? DEFAULT_EASING
77
- );
84
+ // Pulse/Bounce/Wiggle/Heartbeat/Elastic continuos (boolean props) → inline animation
85
+ const wantsPulse = $derived(!!props.pulse);
86
+ const wantsBounce = $derived(!!props.bounce);
87
+ const wantsWiggle = $derived(!!props.wiggle);
88
+ const wantsHeartbeat = $derived(!!props.heartbeat);
89
+ const wantsElastic = $derived(!!props.elastic);
78
90
 
91
+ // Construye style inline (NO escribimos transform cuando haya capa FX)
79
92
  const style = $derived(() => {
80
93
  const parts: string[] = [];
94
+
95
+ // estilo previo del usuario
81
96
  if (props.style)
82
97
  parts.push(props.style.endsWith(";") ? props.style : `${props.style};`);
98
+
99
+ // color
100
+ if (effectiveColor) parts.push(`color:${effectiveColor};`);
101
+
102
+ // transform declarativo (translate/scale fuera de rotate/flip)
83
103
  if (cssTransformOnly) parts.push(`transform:${cssTransformOnly};`);
84
104
  parts.push("transform-origin:center;");
85
- if (spinDuration) parts.push(`--bz-spin:${spinDuration};`);
105
+
106
+ // timing variables
86
107
  if (timingStyle) parts.push(timingStyle);
87
- if (effectiveColor) parts.push(`color:${effectiveColor};`);
108
+
109
+ // transición en hover
88
110
  parts.push(
89
111
  `transition:color ${hoverMs}ms ${hoverEase},fill ${hoverMs}ms ${hoverEase},` +
90
112
  `stroke ${hoverMs}ms ${hoverEase},transform ${hoverMs}ms ${hoverEase},opacity ${hoverMs}ms ${hoverEase};`
91
113
  );
114
+
115
+ // Animaciones continuas inline (evitamos clases para no chocar)
116
+ const animations: string[] = [];
117
+ if (spinDuration)
118
+ animations.push(`__icon_spin ${spinDuration} linear infinite`);
119
+ if (wantsPulse)
120
+ animations.push(`bz-icon-pulse 1200ms cubic-bezier(.2,.8,.2,1) infinite`);
121
+ if (wantsBounce) animations.push(`bz-icon-bounce 400ms ease infinite`);
122
+ if (wantsWiggle)
123
+ animations.push(`bz-icon-wiggle 200ms ease-in-out infinite`);
124
+ if (wantsElastic)
125
+ animations.push(`bz-icon-elastic 300ms ease-out infinite`);
126
+ if (wantsHeartbeat)
127
+ animations.push(`bz-icon-heartbeat 1000ms ease-in-out infinite`);
128
+ if (animations.length) parts.push(`animation:${animations.join(",")};`);
129
+
92
130
  return parts.join(" ");
93
131
  });
94
132
 
133
+ // A11y
95
134
  const ariaHidden = $derived(props.decorative ? "true" : undefined);
96
135
  const computedTitleId = $derived(
97
136
  props.title ? (props.titleId ?? `bz-icon-title-${uid()}`) : undefined
@@ -103,9 +142,12 @@
103
142
  !props.decorative && props.title ? computedTitleId : undefined
104
143
  );
105
144
 
145
+ // Tamaño final
106
146
  const finalSize = $derived(
107
147
  typeof common.size === "number" ? `${common.size}px` : common.size
108
148
  );
149
+
150
+ // Atributos seguros
109
151
  const safeAttrs = $derived(() => {
110
152
  const a = { ...(props.attrs ?? {}) };
111
153
  delete (a as any).width;
@@ -113,10 +155,12 @@
113
155
  return a;
114
156
  });
115
157
 
158
+ // 👇 Importante: NO agregamos animationClasses aquí para evitar choques
116
159
  const finalClass = $derived(
117
- `bz-icon ${klass} ${animationClasses} ${props.chevronState ? "bz-icon-chevron" : ""}`.trim()
160
+ `bz-icon ${klass} ${props.chevronState ? "bz-icon-chevron" : ""}`.trim()
118
161
  );
119
162
 
163
+ // Rotate/Flip a nivel de GEOMETRÍA (en el <g>)
120
164
  function getViewBoxCenter(vb: string) {
121
165
  const [minX, minY, w, h] = vb.trim().split(/\s+/).map(Number);
122
166
  return { cx: minX + w / 2, cy: minY + h / 2 };
@@ -141,9 +185,13 @@
141
185
  return cmds.join(" ");
142
186
  });
143
187
 
188
+ // Capa FX: efectos programáticos (scale/slide/rotateOnHover/pressScale)
144
189
  let svgRef: SVGSVGElement | null = $state(null);
190
+ let fxLayer: SVGGElement | null = $state(null);
191
+
145
192
  $effect(() => {
146
- if (!svgRef) return;
193
+ if (!fxLayer) return;
194
+
147
195
  const effectsOpts: IconEffectOptions = { ...(props.effects ?? {}) };
148
196
  const a = props.attrs ?? {};
149
197
  if (a.spinOnHover) effectsOpts.spinOnHover = true;
@@ -155,10 +203,10 @@
155
203
  if (a.heartbeatOnActive) effectsOpts.heartbeatOnActive = true;
156
204
  if (a.hoverScale) effectsOpts.hoverScale = a.hoverScale;
157
205
  if (a.pressScale) effectsOpts.pressScale = a.pressScale;
158
- if (Object.keys(effectsOpts).length > 0) {
159
- const controller = iconEffects(svgRef, effectsOpts);
160
- return () => controller.destroy();
161
- }
206
+
207
+ // Aplica transforms sobre fxLayer (no sobre el <svg>)
208
+ const controller = iconEffects(fxLayer, effectsOpts);
209
+ return () => controller.destroy();
162
210
  });
163
211
 
164
212
  function handleMouseEnter() {
@@ -195,7 +243,9 @@
195
243
  onmouseleave={handleMouseLeave}
196
244
  >
197
245
  {#if props.title}<title id={computedTitleId}>{props.title}</title>{/if}
198
- <g transform={svgTransform()}>
246
+
247
+ <!-- 🎯 CAPA FX: aquí se aplican transformaciones programáticas (iconEffects) -->
248
+ <g bind:this={fxLayer} transform={svgTransform()}>
199
249
  {@render props.children?.()}
200
250
  </g>
201
251
  </svg>
@@ -217,4 +267,79 @@
217
267
  }
218
268
  }
219
269
  }
270
+
271
+ /* Keyframes usados por animaciones inline */
272
+ @keyframes __icon_spin {
273
+ to {
274
+ transform: rotate(360deg);
275
+ }
276
+ }
277
+ @keyframes bz-icon-bounce {
278
+ 0% {
279
+ transform: translateY(0);
280
+ }
281
+ 35% {
282
+ transform: translateY(-18%);
283
+ }
284
+ 70% {
285
+ transform: translateY(0);
286
+ }
287
+ }
288
+ @keyframes bz-icon-pulse {
289
+ 0%,
290
+ 100% {
291
+ transform: scale(1);
292
+ opacity: 1;
293
+ }
294
+ 50% {
295
+ transform: scale(1.04);
296
+ opacity: 0.92;
297
+ }
298
+ }
299
+ @keyframes bz-icon-wiggle {
300
+ 0% {
301
+ transform: rotate(0deg);
302
+ }
303
+ 25% {
304
+ transform: rotate(-3deg);
305
+ }
306
+ 75% {
307
+ transform: rotate(3deg);
308
+ }
309
+ 100% {
310
+ transform: rotate(0deg);
311
+ }
312
+ }
313
+ @keyframes bz-icon-elastic {
314
+ 0% {
315
+ transform: scale(1);
316
+ }
317
+ 30% {
318
+ transform: scale(0.8);
319
+ }
320
+ 60% {
321
+ transform: scale(1.15);
322
+ }
323
+ 100% {
324
+ transform: scale(1);
325
+ }
326
+ }
327
+ @keyframes bz-icon-heartbeat {
328
+ 0%,
329
+ 100% {
330
+ transform: scale(1);
331
+ }
332
+ 14% {
333
+ transform: scale(1.1);
334
+ }
335
+ 28% {
336
+ transform: scale(1);
337
+ }
338
+ 42% {
339
+ transform: scale(1.1);
340
+ }
341
+ 70% {
342
+ transform: scale(1);
343
+ }
344
+ }
220
345
  </style>
@@ -0,0 +1,9 @@
1
+ /// <reference types="svelte" />
2
+
3
+ // No exportes nada aquí: debe ser una augmentación global
4
+ declare namespace svelte.JSX {
5
+ interface HTMLAttributes<T> {
6
+ onlazyMount?: (e: CustomEvent<any>) => void;
7
+ onlazyUnmount?: (e: CustomEvent<any>) => void;
8
+ }
9
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@genarou/blazir-icons",
3
- "version": "1.2.11",
3
+ "version": "1.2.13",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "publishConfig": {
@@ -35,7 +35,8 @@
35
35
  "preview": "vite preview",
36
36
  "prepublishOnly": "npm run build",
37
37
  "clean": "rm -rf dist",
38
- "prebuild": "npm run clean"
38
+ "prebuild": "npm run clean",
39
+ "try": "npm run build && vite --config play/vite.config.js"
39
40
  },
40
41
  "devDependencies": {
41
42
  "@sveltejs/package": "^2.5.0",