@genarou/blazir-icons 1.2.4 → 1.2.11

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.
Files changed (72) hide show
  1. package/dist/Icon.svelte +206 -60
  2. package/dist/Icon.svelte.d.ts +1 -0
  3. package/dist/IconBase.svelte +63 -51
  4. package/dist/IconBase.svelte.d.ts +1 -1
  5. package/dist/icons/Ai.svelte +18 -0
  6. package/dist/icons/Ai.svelte.d.ts +4 -0
  7. package/dist/icons/Cart.svelte +28 -0
  8. package/dist/icons/Cart.svelte.d.ts +4 -0
  9. package/dist/icons/Category.svelte +57 -0
  10. package/dist/icons/Category.svelte.d.ts +4 -0
  11. package/dist/icons/Chat.svelte +18 -0
  12. package/dist/icons/Chat.svelte.d.ts +4 -0
  13. package/dist/icons/CheckList.svelte +17 -7
  14. package/dist/icons/CircleExclamation.svelte +17 -9
  15. package/dist/icons/CircleExclamationOutlined.svelte +28 -0
  16. package/dist/icons/CircleExclamationOutlined.svelte.d.ts +4 -0
  17. package/dist/icons/CircleInfo.svelte +16 -9
  18. package/dist/icons/CircleInfoOutlined.svelte +28 -0
  19. package/dist/icons/CircleInfoOutlined.svelte.d.ts +4 -0
  20. package/dist/icons/CircleQuestion.svelte +11 -10
  21. package/dist/icons/CircleQuestionOutlined.svelte +32 -0
  22. package/dist/icons/CircleQuestionOutlined.svelte.d.ts +4 -0
  23. package/dist/icons/Contact.svelte +18 -0
  24. package/dist/icons/Contact.svelte.d.ts +4 -0
  25. package/dist/icons/Dashboard.svelte +3 -8
  26. package/dist/icons/DashboardOutlined.svelte +23 -0
  27. package/dist/icons/DashboardOutlined.svelte.d.ts +4 -0
  28. package/dist/icons/Download.svelte +5 -12
  29. package/dist/icons/Edit.svelte +28 -0
  30. package/dist/icons/Edit.svelte.d.ts +4 -0
  31. package/dist/icons/EditOutline.svelte +16 -14
  32. package/dist/icons/EmailAnimated.svelte +34 -29
  33. package/dist/icons/ErrorO.svelte +17 -8
  34. package/dist/icons/Excel.svelte +6 -31
  35. package/dist/icons/Filter.svelte +18 -0
  36. package/dist/icons/Filter.svelte.d.ts +4 -0
  37. package/dist/icons/Form.svelte +23 -0
  38. package/dist/icons/Form.svelte.d.ts +4 -0
  39. package/dist/icons/Image.svelte +9 -14
  40. package/dist/icons/Notes.svelte +21 -5
  41. package/dist/icons/Pay.svelte +19 -0
  42. package/dist/icons/Pay.svelte.d.ts +4 -0
  43. package/dist/icons/Paypal.svelte +21 -0
  44. package/dist/icons/Paypal.svelte.d.ts +4 -0
  45. package/dist/icons/Png.svelte +40 -3
  46. package/dist/icons/PowerPoint.svelte +18 -0
  47. package/dist/icons/PowerPoint.svelte.d.ts +4 -0
  48. package/dist/icons/Print.svelte +47 -0
  49. package/dist/icons/Print.svelte.d.ts +4 -0
  50. package/dist/icons/Profit.svelte +28 -0
  51. package/dist/icons/Profit.svelte.d.ts +4 -0
  52. package/dist/icons/Send.svelte +18 -0
  53. package/dist/icons/Send.svelte.d.ts +4 -0
  54. package/dist/icons/Team.svelte +20 -5
  55. package/dist/icons/Word.svelte +9 -3
  56. package/dist/icons/Xml.svelte +22 -3
  57. package/dist/icons/registry.d.ts +20 -7
  58. package/dist/icons/registry.js +39 -11
  59. package/dist/icons-api.d.ts +33 -47
  60. package/dist/icons-api.js +28 -46
  61. package/dist/index.d.ts +0 -116
  62. package/dist/index.js +0 -119
  63. package/dist/types.d.ts +5 -1
  64. package/package.json +1 -1
  65. package/dist/icons/LoadingSquares.svelte +0 -128
  66. package/dist/icons/LoadingSquares.svelte.d.ts +0 -7
  67. package/dist/icons/MainComponent.svelte +0 -18
  68. package/dist/icons/MainComponent.svelte.d.ts +0 -4
  69. package/dist/icons/Powerpoint.svelte +0 -13
  70. package/dist/icons/Powerpoint.svelte.d.ts +0 -4
  71. package/dist/icons/Search.svelte +0 -13
  72. package/dist/icons/Search.svelte.d.ts +0 -4
package/dist/Icon.svelte CHANGED
@@ -9,94 +9,233 @@
9
9
  import type { IconProps } from "./types";
10
10
  import { coerceSize } from "./utils/defaults";
11
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
+
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
+ // ───────────────────────────────────────────────────────────────────────────
12
119
  interface DynamicIconProps extends Omit<IconProps, "children"> {
13
120
  name: IconName;
14
121
  preset?: IconPreset;
15
122
  variant?: IconVariant;
123
+ skeleton?: boolean;
16
124
  }
17
125
 
18
126
  const props: DynamicIconProps = $props();
127
+ const inputProps = $derived(() => ({ skeleton: false, ...props }));
19
128
 
20
- let internalHovered = $state(false);
21
- const isHovered = $derived(
22
- internalHovered || (props.parentHoverContext?.hovered ?? false)
23
- );
24
-
25
- const Comp = $derived(iconRegistry[props.name]);
26
-
27
- // Preset y variant (opcionales)
129
+ const Comp = $derived.by(() => iconRegistry[inputProps().name]);
28
130
  const presetProps = $derived(() =>
29
- props.preset ? iconPresets[props.preset] : {}
131
+ inputProps().preset ? iconPresets[inputProps().preset!] : {}
30
132
  );
31
133
  const variantProps = $derived(() =>
32
- props.variant ? iconVariants[props.variant] : {}
134
+ inputProps().variant ? iconVariants[inputProps().variant!] : {}
33
135
  );
136
+ const mergedProps = $derived(() => ({
137
+ ...presetProps(),
138
+ ...variantProps(),
139
+ ...inputProps(),
140
+ }));
34
141
 
35
- // Merge: preset < variant < props explícitas
36
- const mergedProps = $derived({ ...presetProps, ...variantProps, ...props });
37
-
38
- // Color efectivo con fallback
39
- const effectiveColor = $derived(() => {
40
- const baseColor = mergedProps.color || "currentColor";
41
- if (isHovered && mergedProps.hoverColor) return mergedProps.hoverColor;
42
- return baseColor;
43
- });
142
+ // 🔹 No pasar `skeleton` al hijo
143
+ const childProps = $derived(() => {
144
+ const { skeleton, animationDuration, animationDelay, ...rest } =
145
+ mergedProps();
44
146
 
45
- // Normalizar durations (string ms)
46
- const normalizeAnimationValue = (
47
- value?: number | string
48
- ): string | undefined =>
49
- value === undefined
50
- ? undefined
51
- : typeof value === "number"
52
- ? `${value}ms`
53
- : value;
54
-
55
- // Props finales para el Icono concreto
56
- const iconProps = $derived(() => {
57
- const {
58
- name,
59
- preset,
60
- variant,
61
- size,
62
- color,
63
- hoverColor,
64
- activeColor,
65
- parentHoverContext,
66
- animationDuration,
67
- animationDelay,
68
- ...rest
69
- } = mergedProps;
147
+ const toMsString = (v?: string | number) =>
148
+ v === undefined ? undefined : typeof v === "number" ? `${v}ms` : v;
70
149
 
71
150
  return {
72
151
  ...rest,
73
- size: coerceSize(size, 24),
74
- color: effectiveColor,
75
- animationDuration: normalizeAnimationValue(animationDuration),
76
- animationDelay: normalizeAnimationValue(animationDelay),
152
+ animationDuration: toMsString(animationDuration),
153
+ animationDelay: toMsString(animationDelay),
77
154
  };
78
155
  });
79
156
 
80
- function handleMouseEnter() {
81
- internalHovered = true;
82
- }
83
- function handleMouseLeave() {
84
- internalHovered = false;
157
+ const boxSize = $derived(() => {
158
+ const s = coerceSize(mergedProps().size, 24);
159
+ return typeof s === "number" ? `${s}px` : s;
160
+ });
161
+
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;
171
+
172
+ function tryMount() {
173
+ if (!isVisible && inViewport && hasSize) {
174
+ isVisible = true;
175
+ unregIO?.();
176
+ unregIO = null;
177
+ unregRO?.();
178
+ unregRO = null;
179
+ }
85
180
  }
181
+
182
+ $effect(() => {
183
+ if (
184
+ typeof window === "undefined" ||
185
+ typeof IntersectionObserver === "undefined"
186
+ ) {
187
+ isVisible = true;
188
+ return;
189
+ }
190
+ if (!hostEl || isVisible) return;
191
+
192
+ const root = getScrollRoot(hostEl) ?? undefined;
193
+ unregIO = IO_POOL.register(root, {
194
+ el: hostEl,
195
+ onViewportChange: (v) => {
196
+ inViewport = v;
197
+ tryMount();
198
+ },
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
+ };
220
+ });
86
221
  </script>
87
222
 
88
223
  {#if Comp}
89
224
  <span
225
+ bind:this={hostEl}
90
226
  class="bz-icon-wrapper"
91
- role="presentation"
92
- onmouseenter={handleMouseEnter}
93
- onmouseleave={handleMouseLeave}
227
+ style={`inline-size:${boxSize};block-size:${boxSize};`}
228
+ data-mounted={isVisible ? "true" : "false"}
94
229
  >
95
- <Comp {...iconProps} />
230
+ {#if isVisible}
231
+ <Comp {...childProps()} />
232
+ {:else if mergedProps().skeleton}
233
+ <span class="bz-icon-skeleton" aria-hidden="true"></span>
234
+ {/if}
96
235
  </span>
97
236
  {:else}
98
- <span class="bz-icon-error" data-icon={props.name}>
99
- ⚠️ Icon not found: {props.name}
237
+ <span class="bz-icon-error" data-icon={inputProps().name}>
238
+ ⚠️ Icon not found: {inputProps().name}
100
239
  </span>
101
240
  {/if}
102
241
 
@@ -107,12 +246,19 @@
107
246
  justify-content: center;
108
247
  line-height: 1;
109
248
  }
249
+ .bz-icon-skeleton {
250
+ width: 100%;
251
+ height: 100%;
252
+ border-radius: var(--ui-radius, 0.25rem);
253
+ background: color-mix(in oklab, currentColor 12%, transparent);
254
+ opacity: 0.5;
255
+ }
110
256
  .bz-icon-error {
111
257
  display: inline-block;
112
258
  width: 1em;
113
259
  height: 1em;
114
260
  background: var(--danger-color, #ef4444);
115
- color: var(--danger-foreground, white);
261
+ color: var(--danger-foreground, #fff);
116
262
  text-align: center;
117
263
  line-height: 1;
118
264
  font-size: 0.75rem;
@@ -5,6 +5,7 @@ interface DynamicIconProps extends Omit<IconProps, "children"> {
5
5
  name: IconName;
6
6
  preset?: IconPreset;
7
7
  variant?: IconVariant;
8
+ skeleton?: boolean;
8
9
  }
9
10
  declare const Icon: import("svelte").Component<DynamicIconProps, {}, "">;
10
11
  type Icon = ReturnType<typeof Icon>;
@@ -1,55 +1,55 @@
1
1
  <script lang="ts">
2
2
  import { type IconEffectOptions, iconEffects } from "./effects.js";
3
- import type { IconMode, IconProps } from "./types";
3
+ import type { IconMode, IconProps } from "./types.js";
4
4
  import {
5
5
  combineTransforms,
6
6
  getAnimationClasses,
7
7
  getAnimationStyle,
8
- } from "./utils/animations";
8
+ } from "./utils/animations.js";
9
9
  import {
10
10
  commonDefaults,
11
11
  modeDefaults,
12
12
  normalizeClass,
13
- } from "./utils/defaults";
13
+ } from "./utils/defaults.js";
14
+
15
+ const DEFAULT_MS = 180;
16
+ const DEFAULT_EASING = "cubic-bezier(.2,.8,.2,1)";
17
+
18
+ function normalizeMs(v?: number | string): number {
19
+ if (v == null) return DEFAULT_MS;
20
+ if (typeof v === "number") return v;
21
+ const s = String(v).trim();
22
+ if (s.endsWith("ms")) return parseFloat(s);
23
+ if (s.endsWith("s")) return parseFloat(s) * 1000;
24
+ const n = parseFloat(s);
25
+ return Number.isFinite(n) ? n : DEFAULT_MS;
26
+ }
14
27
 
15
- // id simple para <title>
16
28
  function uid(): string {
17
29
  return Math.random().toString(36).slice(2);
18
30
  }
19
31
 
20
32
  const props: IconProps & { mode?: IconMode } = $props();
21
-
22
- // ── Interacción ────────────────────────────────────────────────────────────
23
33
  let internalHovered = $state(false);
24
34
  const isHovered = $derived(
25
35
  internalHovered || (props.parentHoverContext?.hovered ?? false)
26
36
  );
27
37
 
28
- // ── Color por defecto dark-first con fallbacks ─────────────────────────────
29
- // 1) --icon-fg (si existe)
30
- // 2) --ui-muted-fg (ya lo defines distinto en claro/oscuro)
31
- // 3) currentColor (último fallback)
32
38
  const DEFAULT_ICON_COLOR = "var(--icon-fg, var(--ui-muted-fg, currentColor))";
33
-
34
39
  const effectiveColor = $derived(
35
40
  (isHovered && props.hoverColor ? props.hoverColor : props.color) ??
36
41
  DEFAULT_ICON_COLOR
37
42
  );
38
43
 
39
- // ── Modo / clases / defaults compartidos ───────────────────────────────────
40
44
  const mode = $derived(props.mode ?? "solid");
41
45
  const klass = $derived(normalizeClass(props));
42
46
  const common = $derived(commonDefaults(props));
43
47
 
44
- // Pasar color efectivo al pipeline visual
45
48
  const propsWithEffectiveColor = $derived({ ...props, color: effectiveColor });
46
49
  const visual = $derived(modeDefaults(mode, propsWithEffectiveColor));
47
-
48
- // ── Animaciones (clases + timing inline) ───────────────────────────────────
49
50
  const animationClasses = $derived(getAnimationClasses(props).join(" "));
50
51
  const timingStyle = $derived(getAnimationStyle(props));
51
52
 
52
- // ── Transform sólo CSS (no colisionar con transform del <g>) ──────────────
53
53
  const cssTransformOnly = $derived(
54
54
  combineTransforms({
55
55
  ...props,
@@ -59,7 +59,6 @@
59
59
  })
60
60
  );
61
61
 
62
- // ── Duración de spin como custom prop ──────────────────────────────────────
63
62
  const spinDuration = $derived(
64
63
  props.spin
65
64
  ? props.spin === true
@@ -70,21 +69,29 @@
70
69
  : null
71
70
  );
72
71
 
73
- // ── style final (string) ───────────────────────────────────────────────────
72
+ const hoverMs = $derived(
73
+ normalizeMs(props.transitionMs ?? props.animationDuration)
74
+ );
75
+ const hoverEase = $derived(
76
+ props.transitionEasing ?? props.animationEasing ?? DEFAULT_EASING
77
+ );
78
+
74
79
  const style = $derived(() => {
75
80
  const parts: string[] = [];
76
81
  if (props.style)
77
82
  parts.push(props.style.endsWith(";") ? props.style : `${props.style};`);
78
- if (cssTransformOnly) parts.push(`transform: ${cssTransformOnly};`);
79
- parts.push("transform-origin: center;");
80
- if (spinDuration) parts.push(`--bz-spin: ${spinDuration};`);
83
+ if (cssTransformOnly) parts.push(`transform:${cssTransformOnly};`);
84
+ parts.push("transform-origin:center;");
85
+ if (spinDuration) parts.push(`--bz-spin:${spinDuration};`);
81
86
  if (timingStyle) parts.push(timingStyle);
82
- // 💡 clave: color por defecto dark-first
83
- if (effectiveColor) parts.push(`color: ${effectiveColor};`);
87
+ if (effectiveColor) parts.push(`color:${effectiveColor};`);
88
+ parts.push(
89
+ `transition:color ${hoverMs}ms ${hoverEase},fill ${hoverMs}ms ${hoverEase},` +
90
+ `stroke ${hoverMs}ms ${hoverEase},transform ${hoverMs}ms ${hoverEase},opacity ${hoverMs}ms ${hoverEase};`
91
+ );
84
92
  return parts.join(" ");
85
93
  });
86
94
 
87
- // ── A11y ───────────────────────────────────────────────────────────────────
88
95
  const ariaHidden = $derived(props.decorative ? "true" : undefined);
89
96
  const computedTitleId = $derived(
90
97
  props.title ? (props.titleId ?? `bz-icon-title-${uid()}`) : undefined
@@ -96,12 +103,9 @@
96
103
  !props.decorative && props.title ? computedTitleId : undefined
97
104
  );
98
105
 
99
- // ── Tamaño final ───────────────────────────────────────────────────────────
100
106
  const finalSize = $derived(
101
107
  typeof common.size === "number" ? `${common.size}px` : common.size
102
108
  );
103
-
104
- // ── Attrs seguros (no permitir width/height externos) ──────────────────────
105
109
  const safeAttrs = $derived(() => {
106
110
  const a = { ...(props.attrs ?? {}) };
107
111
  delete (a as any).width;
@@ -109,21 +113,15 @@
109
113
  return a;
110
114
  });
111
115
 
112
- // ── Clase final ────────────────────────────────────────────────────────────
113
116
  const finalClass = $derived(
114
- `${klass} ${animationClasses} ${props.chevronState ? "bz-icon-chevron" : ""}`.trim()
117
+ `bz-icon ${klass} ${animationClasses} ${props.chevronState ? "bz-icon-chevron" : ""}`.trim()
115
118
  );
116
119
 
117
- // ── Centro del viewBox para rot/flip exactos en SVG ────────────────────────
118
120
  function getViewBoxCenter(vb: string) {
119
- const parts = vb.trim().split(/\s+/).map(Number);
120
- const [minX, minY, w, h] = parts.length === 4 ? parts : [0, 0, 24, 24];
121
+ const [minX, minY, w, h] = vb.trim().split(/\s+/).map(Number);
121
122
  return { cx: minX + w / 2, cy: minY + h / 2 };
122
123
  }
123
124
  const center = $derived(getViewBoxCenter(common.viewBox));
124
- const cx = $derived(center.cx);
125
- const cy = $derived(center.cy);
126
-
127
125
  const svgTransform = $derived(() => {
128
126
  const cmds: string[] = [];
129
127
  if (props.rotate != null) {
@@ -131,32 +129,21 @@
131
129
  typeof props.rotate === "number"
132
130
  ? props.rotate
133
131
  : parseFloat(String(props.rotate));
134
- if (!isNaN(r)) cmds.push(`rotate(${r} ${cx} ${cy})`);
132
+ if (!isNaN(r)) cmds.push(`rotate(${r} ${center.cx} ${center.cy})`);
135
133
  }
136
134
  if (props.flipH || props.flipV) {
137
- const sx = props.flipH ? -1 : 1;
138
- const sy = props.flipV ? -1 : 1;
135
+ const sx = props.flipH ? -1 : 1,
136
+ sy = props.flipV ? -1 : 1;
139
137
  cmds.push(
140
- `translate(${cx} ${cy}) scale(${sx} ${sy}) translate(${-cx} ${-cy})`
138
+ `translate(${center.cx} ${center.cy}) scale(${sx} ${sy}) translate(${-center.cx} ${-center.cy})`
141
139
  );
142
140
  }
143
141
  return cmds.join(" ");
144
142
  });
145
143
 
146
- // ── Hover handlers ─────────────────────────────────────────────────────────
147
- function handleMouseEnter() {
148
- internalHovered = true;
149
- }
150
- function handleMouseLeave() {
151
- internalHovered = false;
152
- }
153
-
154
- // ── Efectos declarativos (props.effects > attrs.* retrocompat) ─────────────
155
144
  let svgRef: SVGSVGElement | null = $state(null);
156
-
157
145
  $effect(() => {
158
146
  if (!svgRef) return;
159
-
160
147
  const effectsOpts: IconEffectOptions = { ...(props.effects ?? {}) };
161
148
  const a = props.attrs ?? {};
162
149
  if (a.spinOnHover) effectsOpts.spinOnHover = true;
@@ -168,12 +155,18 @@
168
155
  if (a.heartbeatOnActive) effectsOpts.heartbeatOnActive = true;
169
156
  if (a.hoverScale) effectsOpts.hoverScale = a.hoverScale;
170
157
  if (a.pressScale) effectsOpts.pressScale = a.pressScale;
171
-
172
158
  if (Object.keys(effectsOpts).length > 0) {
173
159
  const controller = iconEffects(svgRef, effectsOpts);
174
160
  return () => controller.destroy();
175
161
  }
176
162
  });
163
+
164
+ function handleMouseEnter() {
165
+ internalHovered = true;
166
+ }
167
+ function handleMouseLeave() {
168
+ internalHovered = false;
169
+ }
177
170
  </script>
178
171
 
179
172
  <svg
@@ -206,3 +199,22 @@
206
199
  {@render props.children?.()}
207
200
  </g>
208
201
  </svg>
202
+
203
+ <style>
204
+ @media (prefers-reduced-motion: no-preference) {
205
+ .bz-icon {
206
+ animation: bzIconIn 0.18s cubic-bezier(0.2, 0.8, 0.2, 1) both;
207
+ will-change: opacity, transform;
208
+ }
209
+ @keyframes bzIconIn {
210
+ from {
211
+ opacity: 0;
212
+ transform: translateY(2px) scale(0.985);
213
+ }
214
+ to {
215
+ opacity: 1;
216
+ transform: none;
217
+ }
218
+ }
219
+ }
220
+ </style>
@@ -1,4 +1,4 @@
1
- import type { IconMode, IconProps } from "./types";
1
+ import type { IconMode, IconProps } from "./types.js";
2
2
  type $$ComponentProps = IconProps & {
3
3
  mode?: IconMode;
4
4
  };
@@ -0,0 +1,18 @@
1
+ <script lang="ts">
2
+ import IconBase from "../IconBase.svelte";
3
+ import type { IconProps } from "../types";
4
+
5
+ const props: IconProps = $props();
6
+ </script>
7
+
8
+ <IconBase
9
+ {...props}
10
+ mode="solid"
11
+ viewBox="0 0 24 24"
12
+ ariaLabel={props.ariaLabel ?? "File settings icon"}
13
+ title={props.title ?? ""}
14
+ >
15
+ <path
16
+ d="m20.467 8.694l.246-.566a4.36 4.36 0 0 1 2.22-2.25l.759-.339a.53.53 0 0 0 0-.963l-.717-.319a4.37 4.37 0 0 1-2.251-2.326l-.253-.611a.506.506 0 0 0-.942 0l-.253.61a4.37 4.37 0 0 1-2.25 2.327l-.718.32a.53.53 0 0 0 0 .962l.76.338a4.36 4.36 0 0 1 2.219 2.251l.246.566c.18.414.753.414.934 0M5.8 16h2.154l.6-1.5h2.892l.6 1.5H14.2L11 8H9zm4.2-5.115l.646 1.615H9.354zM15 16V8h2v8zM3 3a1 1 0 0 0-1 1v16a1 1 0 0 0 1 1h18a1 1 0 0 0 1-1v-9h-2v8H4V5h10V3z"
17
+ />
18
+ </IconBase>
@@ -0,0 +1,4 @@
1
+ import type { IconProps } from "../types";
2
+ declare const Ai: import("svelte").Component<IconProps, {}, "">;
3
+ type Ai = ReturnType<typeof Ai>;
4
+ export default Ai;
@@ -0,0 +1,28 @@
1
+ <!-- src/lib/icons/Cart.svelte -->
2
+ <script lang="ts">
3
+ import IconBase from "../IconBase.svelte";
4
+ import type { IconProps } from "../types";
5
+
6
+ const props: IconProps = $props();
7
+ const ariaLabel = props.ariaLabel ?? "Cart";
8
+ const title = props.title ?? "";
9
+ </script>
10
+
11
+ <IconBase
12
+ {...props}
13
+ mode={props.mode ?? "solid"}
14
+ viewBox="0 0 24 24"
15
+ {ariaLabel}
16
+ {title}
17
+ >
18
+ <!-- IconBase controla color/hover/animaciones/transición; aquí solo paths -->
19
+ <g fill="none">
20
+ <path
21
+ d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z"
22
+ />
23
+ <path
24
+ fill="currentColor"
25
+ d="M9 20a1 1 0 1 1 0 2a1 1 0 0 1 0-2m7 0a1 1 0 1 1 0 2a1 1 0 0 1 0-2M2.2 2.9a1 1 0 0 1 1.295-.269l.105.07l1.708 1.28a2 2 0 0 1 .653.848l.06.171h12.846a2 2 0 0 1 1.998 2.1l-.013.148l-.457 3.655a5 5 0 0 1-4.32 4.34l-.226.023l-7.313.61l.26 1.124H17.5a1 1 0 0 1 .117 1.993L17.5 19H8.796a2 2 0 0 1-1.906-1.393l-.043-.157l-2.74-11.87L2.4 4.3a1 1 0 0 1-.2-1.4"
26
+ />
27
+ </g>
28
+ </IconBase>
@@ -0,0 +1,4 @@
1
+ import type { IconProps } from "../types";
2
+ declare const Cart: import("svelte").Component<IconProps, {}, "">;
3
+ type Cart = ReturnType<typeof Cart>;
4
+ export default Cart;
@@ -0,0 +1,57 @@
1
+ <!-- src/lib/icons/GridAlt.svelte -->
2
+ <script lang="ts">
3
+ import IconBase from "../IconBase.svelte";
4
+ import type { IconProps } from "../types";
5
+ const props: IconProps = $props();
6
+ </script>
7
+
8
+ <IconBase {...props} mode="outline" viewBox="0 0 24 24">
9
+ <!-- Cuadro superior derecho (romboado) -->
10
+ <rect
11
+ x="13"
12
+ y="3"
13
+ width="8"
14
+ height="8"
15
+ rx="2"
16
+ ry="2"
17
+ transform="rotate(45 17 7)"
18
+ fill="none"
19
+ stroke="currentColor"
20
+ />
21
+
22
+ <!-- Cuadro superior izquierdo -->
23
+ <rect
24
+ x="3"
25
+ y="3"
26
+ width="8"
27
+ height="8"
28
+ rx="2"
29
+ ry="2"
30
+ fill="none"
31
+ stroke="currentColor"
32
+ />
33
+
34
+ <!-- Cuadro inferior derecho -->
35
+ <rect
36
+ x="13"
37
+ y="13"
38
+ width="8"
39
+ height="8"
40
+ rx="2"
41
+ ry="2"
42
+ fill="none"
43
+ stroke="currentColor"
44
+ />
45
+
46
+ <!-- Cuadro inferior izquierdo -->
47
+ <rect
48
+ x="3"
49
+ y="13"
50
+ width="8"
51
+ height="8"
52
+ rx="2"
53
+ ry="2"
54
+ fill="none"
55
+ stroke="currentColor"
56
+ />
57
+ </IconBase>
@@ -0,0 +1,4 @@
1
+ import type { IconProps } from "../types";
2
+ declare const Category: import("svelte").Component<IconProps, {}, "">;
3
+ type Category = ReturnType<typeof Category>;
4
+ export default Category;