@genarou/blazir-icons 1.2.8 → 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.
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.by(() => 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,4 +1,3 @@
1
- <!-- src/lib/IconBase.svelte -->
2
1
  <script lang="ts">
3
2
  import { type IconEffectOptions, iconEffects } from "./effects.js";
4
3
  import type { IconMode, IconProps } from "./types.js";
@@ -13,16 +12,9 @@
13
12
  normalizeClass,
14
13
  } from "./utils/defaults.js";
15
14
 
16
- // ───────────────────────────────────────────────────────────────────────────
17
- // Helpers
18
- // ───────────────────────────────────────────────────────────────────────────
19
- function uid(): string {
20
- return Math.random().toString(36).slice(2);
21
- }
22
-
23
- // Normaliza duración en ms: acepta number, '200ms' o '0.2s'
24
15
  const DEFAULT_MS = 180;
25
16
  const DEFAULT_EASING = "cubic-bezier(.2,.8,.2,1)";
17
+
26
18
  function normalizeMs(v?: number | string): number {
27
19
  if (v == null) return DEFAULT_MS;
28
20
  if (typeof v === "number") return v;
@@ -33,42 +25,31 @@
33
25
  return Number.isFinite(n) ? n : DEFAULT_MS;
34
26
  }
35
27
 
36
- // ───────────────────────────────────────────────────────────────────────────
37
- // Props
38
- // ───────────────────────────────────────────────────────────────────────────
39
- const props: IconProps & { mode?: IconMode } = $props();
28
+ function uid(): string {
29
+ return Math.random().toString(36).slice(2);
30
+ }
40
31
 
41
- // ── Interacción ────────────────────────────────────────────────────────────
32
+ const props: IconProps & { mode?: IconMode } = $props();
42
33
  let internalHovered = $state(false);
43
34
  const isHovered = $derived(
44
35
  internalHovered || (props.parentHoverContext?.hovered ?? false)
45
36
  );
46
37
 
47
- // ── Color por defecto dark-first con fallbacks ─────────────────────────────
48
- // 1) --icon-fg (si existe)
49
- // 2) --ui-muted-fg (definido distinto en claro/oscuro)
50
- // 3) currentColor (último fallback)
51
38
  const DEFAULT_ICON_COLOR = "var(--icon-fg, var(--ui-muted-fg, currentColor))";
52
-
53
39
  const effectiveColor = $derived(
54
40
  (isHovered && props.hoverColor ? props.hoverColor : props.color) ??
55
41
  DEFAULT_ICON_COLOR
56
42
  );
57
43
 
58
- // ── Modo / clases / defaults compartidos ───────────────────────────────────
59
44
  const mode = $derived(props.mode ?? "solid");
60
45
  const klass = $derived(normalizeClass(props));
61
46
  const common = $derived(commonDefaults(props));
62
47
 
63
- // Pasar color efectivo al pipeline visual
64
48
  const propsWithEffectiveColor = $derived({ ...props, color: effectiveColor });
65
49
  const visual = $derived(modeDefaults(mode, propsWithEffectiveColor));
66
-
67
- // ── Animaciones (clases + timing inline) ───────────────────────────────────
68
50
  const animationClasses = $derived(getAnimationClasses(props).join(" "));
69
51
  const timingStyle = $derived(getAnimationStyle(props));
70
52
 
71
- // ── Transform sólo via CSS (no colisionar con transform del <g>) ──────────
72
53
  const cssTransformOnly = $derived(
73
54
  combineTransforms({
74
55
  ...props,
@@ -78,7 +59,6 @@
78
59
  })
79
60
  );
80
61
 
81
- // ── Duración de spin como custom prop ──────────────────────────────────────
82
62
  const spinDuration = $derived(
83
63
  props.spin
84
64
  ? props.spin === true
@@ -89,7 +69,6 @@
89
69
  : null
90
70
  );
91
71
 
92
- // ── Transición global (color/fill/stroke/transform/opacity) ───────────────
93
72
  const hoverMs = $derived(
94
73
  normalizeMs(props.transitionMs ?? props.animationDuration)
95
74
  );
@@ -97,29 +76,22 @@
97
76
  props.transitionEasing ?? props.animationEasing ?? DEFAULT_EASING
98
77
  );
99
78
 
100
- // ── style final (string) ───────────────────────────────────────────────────
101
79
  const style = $derived(() => {
102
80
  const parts: string[] = [];
103
81
  if (props.style)
104
82
  parts.push(props.style.endsWith(";") ? props.style : `${props.style};`);
105
- if (cssTransformOnly) parts.push(`transform: ${cssTransformOnly};`);
106
- parts.push("transform-origin: center;");
107
- 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};`);
108
86
  if (timingStyle) parts.push(timingStyle);
109
- // Color base controlado por CSS var → permite hover suave
110
- if (effectiveColor) parts.push(`color: ${effectiveColor};`);
111
- // Transición universal y suave
87
+ if (effectiveColor) parts.push(`color:${effectiveColor};`);
112
88
  parts.push(
113
- `transition: color ${hoverMs}ms ${hoverEase},` +
114
- ` fill ${hoverMs}ms ${hoverEase},` +
115
- ` stroke ${hoverMs}ms ${hoverEase},` +
116
- ` transform ${hoverMs}ms ${hoverEase},` +
117
- ` opacity ${hoverMs}ms ${hoverEase};`
89
+ `transition:color ${hoverMs}ms ${hoverEase},fill ${hoverMs}ms ${hoverEase},` +
90
+ `stroke ${hoverMs}ms ${hoverEase},transform ${hoverMs}ms ${hoverEase},opacity ${hoverMs}ms ${hoverEase};`
118
91
  );
119
92
  return parts.join(" ");
120
93
  });
121
94
 
122
- // ── A11y ───────────────────────────────────────────────────────────────────
123
95
  const ariaHidden = $derived(props.decorative ? "true" : undefined);
124
96
  const computedTitleId = $derived(
125
97
  props.title ? (props.titleId ?? `bz-icon-title-${uid()}`) : undefined
@@ -131,12 +103,9 @@
131
103
  !props.decorative && props.title ? computedTitleId : undefined
132
104
  );
133
105
 
134
- // ── Tamaño final ───────────────────────────────────────────────────────────
135
106
  const finalSize = $derived(
136
107
  typeof common.size === "number" ? `${common.size}px` : common.size
137
108
  );
138
-
139
- // ── Attrs seguros (no permitir width/height externos) ──────────────────────
140
109
  const safeAttrs = $derived(() => {
141
110
  const a = { ...(props.attrs ?? {}) };
142
111
  delete (a as any).width;
@@ -144,21 +113,15 @@
144
113
  return a;
145
114
  });
146
115
 
147
- // ── Clase final ────────────────────────────────────────────────────────────
148
116
  const finalClass = $derived(
149
- `${klass} ${animationClasses} ${props.chevronState ? "bz-icon-chevron" : ""}`.trim()
117
+ `bz-icon ${klass} ${animationClasses} ${props.chevronState ? "bz-icon-chevron" : ""}`.trim()
150
118
  );
151
119
 
152
- // ── Centro del viewBox para rot/flip exactos en SVG ────────────────────────
153
120
  function getViewBoxCenter(vb: string) {
154
- const parts = vb.trim().split(/\s+/).map(Number);
155
- 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);
156
122
  return { cx: minX + w / 2, cy: minY + h / 2 };
157
123
  }
158
124
  const center = $derived(getViewBoxCenter(common.viewBox));
159
- const cx = $derived(center.cx);
160
- const cy = $derived(center.cy);
161
-
162
125
  const svgTransform = $derived(() => {
163
126
  const cmds: string[] = [];
164
127
  if (props.rotate != null) {
@@ -166,32 +129,21 @@
166
129
  typeof props.rotate === "number"
167
130
  ? props.rotate
168
131
  : parseFloat(String(props.rotate));
169
- if (!isNaN(r)) cmds.push(`rotate(${r} ${cx} ${cy})`);
132
+ if (!isNaN(r)) cmds.push(`rotate(${r} ${center.cx} ${center.cy})`);
170
133
  }
171
134
  if (props.flipH || props.flipV) {
172
- const sx = props.flipH ? -1 : 1;
173
- const sy = props.flipV ? -1 : 1;
135
+ const sx = props.flipH ? -1 : 1,
136
+ sy = props.flipV ? -1 : 1;
174
137
  cmds.push(
175
- `translate(${cx} ${cy}) scale(${sx} ${sy}) translate(${-cx} ${-cy})`
138
+ `translate(${center.cx} ${center.cy}) scale(${sx} ${sy}) translate(${-center.cx} ${-center.cy})`
176
139
  );
177
140
  }
178
141
  return cmds.join(" ");
179
142
  });
180
143
 
181
- // ── Hover handlers ─────────────────────────────────────────────────────────
182
- function handleMouseEnter() {
183
- internalHovered = true;
184
- }
185
- function handleMouseLeave() {
186
- internalHovered = false;
187
- }
188
-
189
- // ── Efectos declarativos (props.effects > attrs.* retrocompat) ─────────────
190
144
  let svgRef: SVGSVGElement | null = $state(null);
191
-
192
145
  $effect(() => {
193
146
  if (!svgRef) return;
194
-
195
147
  const effectsOpts: IconEffectOptions = { ...(props.effects ?? {}) };
196
148
  const a = props.attrs ?? {};
197
149
  if (a.spinOnHover) effectsOpts.spinOnHover = true;
@@ -203,13 +155,18 @@
203
155
  if (a.heartbeatOnActive) effectsOpts.heartbeatOnActive = true;
204
156
  if (a.hoverScale) effectsOpts.hoverScale = a.hoverScale;
205
157
  if (a.pressScale) effectsOpts.pressScale = a.pressScale;
206
-
207
- // Importante: IconBase ya define transition en style(); iconEffects no la pisa.
208
158
  if (Object.keys(effectsOpts).length > 0) {
209
159
  const controller = iconEffects(svgRef, effectsOpts);
210
160
  return () => controller.destroy();
211
161
  }
212
162
  });
163
+
164
+ function handleMouseEnter() {
165
+ internalHovered = true;
166
+ }
167
+ function handleMouseLeave() {
168
+ internalHovered = false;
169
+ }
213
170
  </script>
214
171
 
215
172
  <svg
@@ -242,3 +199,22 @@
242
199
  {@render props.children?.()}
243
200
  </g>
244
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,28 +1,18 @@
1
- <!-- src/lib/icons/GearSpark.svelte -->
2
1
  <script lang="ts">
3
2
  import IconBase from "../IconBase.svelte";
4
3
  import type { IconProps } from "../types";
5
4
 
6
5
  const props: IconProps = $props();
7
- const ariaLabel = props.ariaLabel ?? "Gear Spark";
8
- const title = props.title ?? "";
9
6
  </script>
10
7
 
11
8
  <IconBase
12
9
  {...props}
13
- mode={props.mode ?? "solid"}
10
+ mode="solid"
14
11
  viewBox="0 0 24 24"
15
- {ariaLabel}
16
- {title}
12
+ ariaLabel={props.ariaLabel ?? "File settings icon"}
13
+ title={props.title ?? ""}
17
14
  >
18
- <!-- IconBase maneja color, hover, animaciones y transición -->
19
- <g fill="none">
20
- <path
21
- d="m12.594 23.258l-.012.002l-.071.035l-.02.004l-.014-.004l-.071-.036q-.016-.004-.024.006l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.016-.018m.264-.113l-.014.002l-.184.093l-.01.01l-.003.011l.018.43l.005.012l.008.008l.201.092q.019.005.029-.008l.004-.014l-.034-.614q-.005-.019-.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.003-.011l.018-.43l-.003-.012l-.01-.01z"
22
- />
23
- <path
24
- fill="currentColor"
25
- d="M9.107 5.448c.598-1.75 3.016-1.803 3.725-.159l.06.16l.807 2.36a4 4 0 0 0 2.276 2.411l.217.081l2.36.806c1.75.598 1.803 3.016.16 3.725l-.16.06l-2.36.807a4 4 0 0 0-2.412 2.276l-.081.216l-.806 2.361c-.598 1.75-3.016 1.803-3.724.16l-.062-.16l-.806-2.36a4 4 0 0 0-2.276-2.412l-.216-.081l-2.36-.806c-1.751-.598-1.804-3.016-.16-3.724l.16-.062l2.36-.806A4 4 0 0 0 8.22 8.025l.081-.216zM19 2a1 1 0 0 1 .898.56l.048.117l.35 1.026l1.027.35a1 1 0 0 1 .118 1.845l-.118.048l-1.026.35l-.35 1.027a1 1 0 0 1-1.845.117l-.048-.117l-.35-1.026l-1.027-.35a1 1 0 0 1-.118-1.845l.118-.048l1.026-.35l.35-1.027A1 1 0 0 1 19 2"
26
- />
27
- </g>
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
+ />
28
18
  </IconBase>
@@ -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 ?? "Chat bubble icon"}
13
+ title={props.title ?? ""}
14
+ >
15
+ <path
16
+ d="M9 22a1 1 0 0 1-1-1v-3H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2h-6.1l-3.7 3.71c-.2.19-.45.29-.7.29zm8-11V9h-2v2zm-4 0V9h-2v2zm-4 0V9H7v2z"
17
+ />
18
+ </IconBase>
@@ -0,0 +1,4 @@
1
+ import type { IconProps } from "../types";
2
+ declare const Chat: import("svelte").Component<IconProps, {}, "">;
3
+ type Chat = ReturnType<typeof Chat>;
4
+ export default Chat;
@@ -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 ?? "Control panel icon"}
13
+ title={props.title ?? ""}
14
+ >
15
+ <path
16
+ d="M4 2a1 1 0 0 0-1 1v2h2V4h14v16H5v-1H3v2a1 1 0 0 0 1 1h16a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1zm5 14a3 3 0 1 1 6 0zm3-4a2 2 0 1 1 0-4a2 2 0 0 1 0 4M6 9V7H2v2zm0 2v2H2v-2zm0 6v-2H2v2z"
17
+ />
18
+ </IconBase>
@@ -0,0 +1,4 @@
1
+ import type { IconProps } from "../types";
2
+ declare const Contact: import("svelte").Component<IconProps, {}, "">;
3
+ type Contact = ReturnType<typeof Contact>;
4
+ export default Contact;
@@ -1,23 +1,18 @@
1
- <!-- src/lib/icons/TableChart.svelte -->
2
1
  <script lang="ts">
3
2
  import IconBase from "../IconBase.svelte";
4
3
  import type { IconProps } from "../types";
4
+
5
5
  const props: IconProps = $props();
6
6
  </script>
7
7
 
8
8
  <IconBase
9
9
  {...props}
10
- mode="outline"
10
+ mode="solid"
11
11
  viewBox="0 0 24 24"
12
- ariaLabel={props.ariaLabel ?? "Table chart"}
12
+ ariaLabel={props.ariaLabel ?? "Grid view icon"}
13
13
  title={props.title ?? ""}
14
- strokeWidth={props.strokeWidth ?? 1.5}
15
- strokeLinecap={props.strokeLinecap ?? "round"}
16
- strokeLinejoin={props.strokeLinejoin ?? "round"}
17
14
  >
18
- <!-- Sin fill: que herede del SVG (outline) -->
19
15
  <path
20
16
  d="M4 13h6c.55 0 1-.45 1-1V4c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1v8c0 .55.45 1 1 1m0 8h6c.55 0 1-.45 1-1v-4c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1v4c0 .55.45 1 1 1m10 0h6c.55 0 1-.45 1-1v-8c0-.55-.45-1-1-1h-6c-.55 0-1 .45-1 1v8c0 .55.45 1 1 1M13 4v4c0 .55.45 1 1 1h6c.55 0 1-.45 1-1V4c0-.55-.45-1-1-1h-6c-.55 0-1 .45-1 1"
21
- fill="none"
22
17
  />
23
18
  </IconBase>