@genarou/blazir-icons 1.1.17 → 1.2.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/dist/Icon.svelte +108 -66
- package/dist/Icon.svelte.d.ts +6 -3
- package/dist/IconBase.svelte +110 -59
- package/dist/IconBase.svelte.d.ts +1 -1
- package/dist/{actions.d.ts → effects.d.ts} +9 -7
- package/dist/effects.js +128 -0
- package/dist/{styles/animations.css → icon-animation.css} +19 -52
- package/dist/icons/Alternate.svelte +7 -4
- package/dist/icons/AnimatedArrowLeft.svelte +7 -7
- package/dist/icons/Aws.svelte +7 -6
- package/dist/icons/Bag.svelte +8 -4
- package/dist/icons/Bank.svelte +13 -0
- package/dist/icons/Bank.svelte.d.ts +4 -0
- package/dist/icons/Bell.svelte +7 -4
- package/dist/icons/Blaze.svelte +3 -9
- package/dist/icons/Book.svelte +7 -4
- package/dist/icons/Box.svelte +3 -9
- package/dist/icons/BoxAdd.svelte +8 -5
- package/dist/icons/Building.svelte +3 -5
- package/dist/icons/Buy.svelte +8 -5
- package/dist/icons/Calendar.svelte +7 -6
- package/dist/icons/CalendarEdit.svelte +7 -5
- package/dist/icons/CalendarPlus.svelte +7 -5
- package/dist/icons/CategoryAdd.svelte +10 -7
- package/dist/icons/CategorySearch.svelte +7 -4
- package/dist/icons/Chart.svelte +7 -4
- package/dist/icons/ChartDoc.svelte +8 -5
- package/dist/icons/Check.svelte +8 -5
- package/dist/icons/CheckList.svelte +8 -6
- package/dist/icons/CheckO.svelte +4 -15
- package/dist/icons/ChevronDown.svelte +8 -6
- package/dist/icons/ChevronUpDown.svelte +7 -5
- package/dist/icons/CircleCheck.svelte +9 -6
- package/dist/icons/CircleExclamation.svelte +6 -15
- package/dist/icons/CircleInfo.svelte +6 -11
- package/dist/icons/CircleQuestion.svelte +8 -15
- package/dist/icons/Close.svelte +5 -11
- package/dist/icons/Copy.svelte +5 -11
- package/dist/icons/CostIcon.svelte +7 -4
- package/dist/icons/Csv.svelte +7 -9
- package/dist/icons/Dashboard.svelte +12 -4
- package/dist/icons/Db.svelte +13 -0
- package/dist/icons/Db.svelte.d.ts +4 -0
- package/dist/icons/Download.svelte +11 -4
- package/dist/icons/DownloadAnimated.svelte +5 -9
- package/dist/icons/EditOutline.svelte +17 -12
- package/dist/icons/Email.svelte +7 -4
- package/dist/icons/EmailAnimated.svelte +25 -35
- package/dist/icons/Enterprise.svelte +9 -5
- package/dist/icons/Error.svelte +7 -4
- package/dist/icons/ErrorO.svelte +7 -5
- package/dist/icons/Excel.svelte +23 -25
- package/dist/icons/ExcelAnimated.svelte +11 -4
- package/dist/icons/Exchange.svelte +8 -5
- package/dist/icons/Eye.svelte +10 -9
- package/dist/icons/EyeOff.svelte +52 -67
- package/dist/icons/Facebook.svelte +3 -10
- package/dist/icons/Favorites.svelte +3 -10
- package/dist/icons/File.svelte +10 -6
- package/dist/icons/FileUploadAnimated.svelte +59 -47
- package/dist/icons/FilterOutline.svelte +12 -9
- package/dist/icons/Fingerprint.svelte +13 -0
- package/dist/icons/Fingerprint.svelte.d.ts +4 -0
- package/dist/icons/FormatListGroup.svelte +8 -5
- package/dist/icons/Golang.svelte +2 -10
- package/dist/icons/Google.svelte +3 -10
- package/dist/icons/Group.svelte +7 -4
- package/dist/icons/Hamburguer.svelte +2 -21
- package/dist/icons/HandShake.svelte +8 -5
- package/dist/icons/Height.svelte +7 -5
- package/dist/icons/Home.svelte +2 -13
- package/dist/icons/Image.svelte +14 -12
- package/dist/icons/ImageAnimated.svelte +29 -36
- package/dist/icons/Key.svelte +7 -4
- package/dist/icons/List.svelte +7 -4
- package/dist/icons/ListDots.svelte +7 -4
- package/dist/icons/LoadingDots.svelte +18 -13
- package/dist/icons/LoadingSquares.svelte +24 -23
- package/dist/icons/LoadingSquares.svelte.d.ts +0 -1
- package/dist/icons/Location.svelte +6 -4
- package/dist/icons/LocationAnimated.svelte +28 -9
- package/dist/icons/Lock.svelte +2 -8
- package/dist/icons/LockOpen.svelte +3 -9
- package/dist/icons/Logout.svelte +8 -17
- package/dist/icons/MagnifiyingGlass.svelte +7 -4
- package/dist/icons/MainComponent.svelte +7 -5
- package/dist/icons/Measure.svelte +7 -4
- package/dist/icons/Money.svelte +3 -6
- package/dist/icons/Notes.svelte +2 -7
- package/dist/icons/ObjectGroup.svelte +1 -26
- package/dist/icons/Pdf.svelte +12 -48
- package/dist/icons/Phone.svelte +1 -13
- package/dist/icons/Plus.svelte +2 -39
- package/dist/icons/Png.svelte +2 -13
- package/dist/icons/PointSale.svelte +4 -40
- package/dist/icons/Powerpoint.svelte +1 -13
- package/dist/icons/Product.svelte +2 -38
- package/dist/icons/Project.svelte +2 -39
- package/dist/icons/RegularSpinner.svelte +13 -49
- package/dist/icons/RegularSpinner.svelte.d.ts +2 -0
- package/dist/icons/Reset.svelte +3 -6
- package/dist/icons/RightArrow.svelte +3 -43
- package/dist/icons/SafeSolid.svelte +5 -40
- package/dist/icons/Scan.svelte +3 -39
- package/dist/icons/Search.svelte +3 -39
- package/dist/icons/Security.svelte +4 -41
- package/dist/icons/Server.svelte +13 -0
- package/dist/icons/Server.svelte.d.ts +4 -0
- package/dist/icons/Settings.svelte +3 -38
- package/dist/icons/Shield.svelte +1 -13
- package/dist/icons/SquareChart.svelte +5 -15
- package/dist/icons/Star.svelte +1 -13
- package/dist/icons/Supervisor.svelte +3 -38
- package/dist/icons/Swap.svelte +3 -43
- package/dist/icons/Table.svelte +3 -42
- package/dist/icons/Team.svelte +2 -41
- package/dist/icons/Trash.svelte +4 -36
- package/dist/icons/TrashOutline.svelte +4 -26
- package/dist/icons/Truck.svelte +3 -29
- package/dist/icons/TruckReturn.svelte +12 -38
- package/dist/icons/UpArrow.svelte +6 -5
- package/dist/icons/UpDownArrow.svelte +7 -14
- package/dist/icons/Upload.svelte +2 -5
- package/dist/icons/UploadAnimated.svelte +11 -16
- package/dist/icons/UploadLoader.svelte +1 -4
- package/dist/icons/User.svelte +3 -5
- package/dist/icons/UserTie.svelte +3 -5
- package/dist/icons/Wallet.svelte +3 -4
- package/dist/icons/Warehouse.svelte +3 -6
- package/dist/icons/Warning.svelte +2 -5
- package/dist/icons/Word.svelte +1 -13
- package/dist/icons/Xml.svelte +1 -13
- package/dist/icons/Zip.svelte +4 -9
- package/dist/icons/registry.d.ts +4 -1
- package/dist/icons/registry.js +8 -0
- package/dist/icons-api.d.ts +285 -141
- package/dist/icons-api.js +112 -145
- package/dist/index.d.ts +9 -9
- package/dist/index.js +27 -19
- package/dist/presets.d.ts +107 -0
- package/dist/presets.js +89 -0
- package/dist/styles.d.ts +2 -0
- package/dist/types.d.ts +37 -45
- package/dist/utils/defaults.d.ts +7 -1
- package/dist/utils/defaults.js +77 -17
- package/package.json +4 -1
- package/dist/DynamicIcon.svelte +0 -45
- package/dist/DynamicIcon.svelte.d.ts +0 -7
- package/dist/actions.js +0 -150
- package/dist/auto-inject.d.ts +0 -1
- package/dist/auto-inject.js +0 -8
- package/dist/effects/icon-effects.d.ts +0 -5
- package/dist/effects/icon-effects.js +0 -222
- package/dist/icons/icon-bundle.d.ts +0 -104
- package/dist/icons/icon-bundle.js +0 -203
- package/dist/icons/icons-effects.css +0 -26
- package/dist/icons/injectEffects.d.ts +0 -2
- package/dist/icons/injectEffects.js +0 -32
- package/dist/styles/css-injection.d.ts +0 -2
- package/dist/styles/css-injection.js +0 -5
- package/dist/styles/hybrid-inject.d.ts +0 -12
- package/dist/styles/hybrid-inject.js +0 -225
package/dist/Icon.svelte
CHANGED
|
@@ -1,80 +1,122 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { iconRegistry, type IconName } from "./icons/registry";
|
|
3
|
-
import
|
|
3
|
+
import {
|
|
4
|
+
iconPresets,
|
|
5
|
+
iconVariants,
|
|
6
|
+
type IconPreset,
|
|
7
|
+
type IconVariant,
|
|
8
|
+
} from "./presets";
|
|
9
|
+
import type { IconProps } from "./types";
|
|
4
10
|
import { coerceSize } from "./utils/defaults";
|
|
5
11
|
|
|
6
|
-
|
|
7
|
-
name
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
strokeWidth,
|
|
12
|
-
fill,
|
|
13
|
-
class: classAttr = "",
|
|
14
|
-
className = "",
|
|
15
|
-
ariaLabel,
|
|
16
|
-
title = "",
|
|
17
|
-
rotate = 0,
|
|
18
|
-
flipH = false,
|
|
19
|
-
flipV = false,
|
|
20
|
-
spin = false,
|
|
21
|
-
nonScalingStroke = false,
|
|
22
|
-
preserveAspectRatio = "xMidYMid meet",
|
|
23
|
-
decorative = false,
|
|
24
|
-
titleId = "",
|
|
25
|
-
testId,
|
|
26
|
-
attrs = {},
|
|
27
|
-
animationDuration,
|
|
28
|
-
animationDelay,
|
|
29
|
-
animationEasing,
|
|
30
|
-
...rest
|
|
31
|
-
}: { name: IconName } & Partial<IconProps> = $props();
|
|
12
|
+
interface DynamicIconProps extends Omit<IconProps, "children"> {
|
|
13
|
+
name: IconName;
|
|
14
|
+
preset?: IconPreset;
|
|
15
|
+
variant?: IconVariant;
|
|
16
|
+
}
|
|
32
17
|
|
|
33
|
-
|
|
34
|
-
const Comp: IconComponent | undefined = $derived(iconRegistry?.[name]);
|
|
18
|
+
const props: DynamicIconProps = $props();
|
|
35
19
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
color !== undefined
|
|
40
|
-
? { color, stroke: stroke ?? "none", fill: fill ?? color }
|
|
41
|
-
: { stroke, fill }
|
|
20
|
+
let internalHovered = $state(false);
|
|
21
|
+
const isHovered = $derived(
|
|
22
|
+
internalHovered || (props.parentHoverContext?.hovered ?? false)
|
|
42
23
|
);
|
|
43
24
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
attrs,
|
|
63
|
-
animationDuration:
|
|
64
|
-
typeof animationDuration === "number"
|
|
65
|
-
? `${animationDuration}ms`
|
|
66
|
-
: animationDuration,
|
|
67
|
-
animationDelay:
|
|
68
|
-
typeof animationDelay === "number"
|
|
69
|
-
? `${animationDelay}ms`
|
|
70
|
-
: animationDelay,
|
|
71
|
-
animationEasing,
|
|
72
|
-
...rest,
|
|
25
|
+
const Comp = $derived(iconRegistry[props.name]);
|
|
26
|
+
|
|
27
|
+
// Preset y variant (opcionales)
|
|
28
|
+
const presetProps = $derived(() =>
|
|
29
|
+
props.preset ? iconPresets[props.preset] : {}
|
|
30
|
+
);
|
|
31
|
+
const variantProps = $derived(() =>
|
|
32
|
+
props.variant ? iconVariants[props.variant] : {}
|
|
33
|
+
);
|
|
34
|
+
|
|
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;
|
|
73
43
|
});
|
|
44
|
+
|
|
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;
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
...rest,
|
|
73
|
+
size: coerceSize(size, 24),
|
|
74
|
+
color: effectiveColor,
|
|
75
|
+
animationDuration: normalizeAnimationValue(animationDuration),
|
|
76
|
+
animationDelay: normalizeAnimationValue(animationDelay),
|
|
77
|
+
};
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
function handleMouseEnter() {
|
|
81
|
+
internalHovered = true;
|
|
82
|
+
}
|
|
83
|
+
function handleMouseLeave() {
|
|
84
|
+
internalHovered = false;
|
|
85
|
+
}
|
|
74
86
|
</script>
|
|
75
87
|
|
|
76
88
|
{#if Comp}
|
|
77
|
-
<
|
|
89
|
+
<span
|
|
90
|
+
class="bz-icon-wrapper"
|
|
91
|
+
role="presentation"
|
|
92
|
+
onmouseenter={handleMouseEnter}
|
|
93
|
+
onmouseleave={handleMouseLeave}
|
|
94
|
+
>
|
|
95
|
+
<Comp {...iconProps} />
|
|
96
|
+
</span>
|
|
78
97
|
{:else}
|
|
79
|
-
<span class="
|
|
98
|
+
<span class="bz-icon-error" data-icon={props.name}>
|
|
99
|
+
⚠️ Icon not found: {props.name}
|
|
100
|
+
</span>
|
|
80
101
|
{/if}
|
|
102
|
+
|
|
103
|
+
<style>
|
|
104
|
+
.bz-icon-wrapper {
|
|
105
|
+
display: inline-flex;
|
|
106
|
+
align-items: center;
|
|
107
|
+
justify-content: center;
|
|
108
|
+
line-height: 1;
|
|
109
|
+
}
|
|
110
|
+
.bz-icon-error {
|
|
111
|
+
display: inline-block;
|
|
112
|
+
width: 1em;
|
|
113
|
+
height: 1em;
|
|
114
|
+
background: var(--danger-color, #ef4444);
|
|
115
|
+
color: var(--danger-foreground, white);
|
|
116
|
+
text-align: center;
|
|
117
|
+
line-height: 1;
|
|
118
|
+
font-size: 0.75rem;
|
|
119
|
+
border-radius: var(--ui-radius, 0.25rem);
|
|
120
|
+
font-weight: 600;
|
|
121
|
+
}
|
|
122
|
+
</style>
|
package/dist/Icon.svelte.d.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { type IconName } from "./icons/registry";
|
|
2
|
+
import { type IconPreset, type IconVariant } from "./presets";
|
|
2
3
|
import type { IconProps } from "./types";
|
|
3
|
-
|
|
4
|
+
interface DynamicIconProps extends Omit<IconProps, "children"> {
|
|
4
5
|
name: IconName;
|
|
5
|
-
|
|
6
|
-
|
|
6
|
+
preset?: IconPreset;
|
|
7
|
+
variant?: IconVariant;
|
|
8
|
+
}
|
|
9
|
+
declare const Icon: import("svelte").Component<DynamicIconProps, {}, "">;
|
|
7
10
|
type Icon = ReturnType<typeof Icon>;
|
|
8
11
|
export default Icon;
|
package/dist/IconBase.svelte
CHANGED
|
@@ -1,31 +1,55 @@
|
|
|
1
|
-
<!-- 📁 src/lib/IconBase.svelte -->
|
|
2
1
|
<script lang="ts">
|
|
3
|
-
import
|
|
2
|
+
import { IconEffectOptions, iconEffects } from "./effects.js";
|
|
3
|
+
import type { IconMode, IconProps } from "./types";
|
|
4
4
|
import {
|
|
5
5
|
combineTransforms,
|
|
6
6
|
getAnimationClasses,
|
|
7
7
|
getAnimationStyle,
|
|
8
|
-
} from "./utils/animations
|
|
8
|
+
} from "./utils/animations";
|
|
9
9
|
import {
|
|
10
10
|
commonDefaults,
|
|
11
11
|
modeDefaults,
|
|
12
12
|
normalizeClass,
|
|
13
|
-
} from "./utils/defaults
|
|
13
|
+
} from "./utils/defaults";
|
|
14
|
+
|
|
15
|
+
// id simple para <title>
|
|
16
|
+
function uid(): string {
|
|
17
|
+
return Math.random().toString(36).slice(2);
|
|
18
|
+
}
|
|
14
19
|
|
|
15
|
-
// Props del icono + modo visual
|
|
16
20
|
const props: IconProps & { mode?: IconMode } = $props();
|
|
17
21
|
|
|
18
|
-
//
|
|
22
|
+
// ── Interacción ────────────────────────────────────────────────────────────
|
|
23
|
+
let internalHovered = $state(false);
|
|
24
|
+
const isHovered = $derived(
|
|
25
|
+
internalHovered || (props.parentHoverContext?.hovered ?? false)
|
|
26
|
+
);
|
|
27
|
+
|
|
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
|
+
const DEFAULT_ICON_COLOR = "var(--icon-fg, var(--ui-muted-fg, currentColor))";
|
|
33
|
+
|
|
34
|
+
const effectiveColor = $derived(
|
|
35
|
+
(isHovered && props.hoverColor ? props.hoverColor : props.color) ??
|
|
36
|
+
DEFAULT_ICON_COLOR
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
// ── Modo / clases / defaults compartidos ───────────────────────────────────
|
|
19
40
|
const mode = $derived(props.mode ?? "solid");
|
|
20
41
|
const klass = $derived(normalizeClass(props));
|
|
21
|
-
const common = $derived(commonDefaults(props));
|
|
22
|
-
const visual = $derived(modeDefaults(mode, props)); // lo que ya tienes
|
|
42
|
+
const common = $derived(commonDefaults(props));
|
|
23
43
|
|
|
44
|
+
// Pasar color efectivo al pipeline visual
|
|
45
|
+
const propsWithEffectiveColor = $derived({ ...props, color: effectiveColor });
|
|
46
|
+
const visual = $derived(modeDefaults(mode, propsWithEffectiveColor));
|
|
47
|
+
|
|
48
|
+
// ── Animaciones (clases + timing inline) ───────────────────────────────────
|
|
24
49
|
const animationClasses = $derived(getAnimationClasses(props).join(" "));
|
|
25
|
-
const timingStyle = $derived(getAnimationStyle(props));
|
|
50
|
+
const timingStyle = $derived(getAnimationStyle(props));
|
|
26
51
|
|
|
27
|
-
//
|
|
28
|
-
// Quitamos rotate/flip del combine para no duplicar (rotate/flip van en <g transform>).
|
|
52
|
+
// ── Transform sólo CSS (no colisionar con transform del <g>) ──────────────
|
|
29
53
|
const cssTransformOnly = $derived(
|
|
30
54
|
combineTransforms({
|
|
31
55
|
...props,
|
|
@@ -35,74 +59,80 @@
|
|
|
35
59
|
})
|
|
36
60
|
);
|
|
37
61
|
|
|
38
|
-
//
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
62
|
+
// ── Duración de spin como custom prop ──────────────────────────────────────
|
|
63
|
+
const spinDuration = $derived(
|
|
64
|
+
props.spin
|
|
65
|
+
? props.spin === true
|
|
66
|
+
? "1s"
|
|
67
|
+
: typeof props.spin === "number"
|
|
68
|
+
? `${props.spin}ms`
|
|
69
|
+
: props.spin
|
|
70
|
+
: null
|
|
46
71
|
);
|
|
47
72
|
|
|
48
|
-
//
|
|
49
|
-
const style = $derived(
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
73
|
+
// ── style final (string) ───────────────────────────────────────────────────
|
|
74
|
+
const style = $derived(() => {
|
|
75
|
+
const parts: string[] = [];
|
|
76
|
+
if (props.style)
|
|
77
|
+
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};`);
|
|
81
|
+
if (timingStyle) parts.push(timingStyle);
|
|
82
|
+
// 💡 clave: color por defecto dark-first
|
|
83
|
+
if (effectiveColor) parts.push(`color: ${effectiveColor};`);
|
|
84
|
+
return parts.join(" ");
|
|
85
|
+
});
|
|
57
86
|
|
|
58
|
-
// A11y
|
|
87
|
+
// ── A11y ───────────────────────────────────────────────────────────────────
|
|
59
88
|
const ariaHidden = $derived(props.decorative ? "true" : undefined);
|
|
89
|
+
const computedTitleId = $derived(
|
|
90
|
+
props.title ? (props.titleId ?? `bz-icon-title-${uid()}`) : undefined
|
|
91
|
+
);
|
|
60
92
|
const ariaLabel = $derived(
|
|
61
93
|
props.decorative ? undefined : props.ariaLabel || undefined
|
|
62
94
|
);
|
|
63
95
|
const ariaLabelledby = $derived(
|
|
64
|
-
!props.decorative && props.title ?
|
|
96
|
+
!props.decorative && props.title ? computedTitleId : undefined
|
|
65
97
|
);
|
|
66
98
|
|
|
67
|
-
//
|
|
99
|
+
// ── Tamaño final ───────────────────────────────────────────────────────────
|
|
68
100
|
const finalSize = $derived(
|
|
69
101
|
typeof common.size === "number" ? `${common.size}px` : common.size
|
|
70
102
|
);
|
|
71
103
|
|
|
72
|
-
//
|
|
104
|
+
// ── Attrs seguros (no permitir width/height externos) ──────────────────────
|
|
73
105
|
const safeAttrs = $derived(() => {
|
|
74
|
-
const a = { ...(props.attrs ?? {}) }
|
|
75
|
-
delete a.width;
|
|
76
|
-
delete a.height;
|
|
106
|
+
const a = { ...(props.attrs ?? {}) };
|
|
107
|
+
delete (a as any).width;
|
|
108
|
+
delete (a as any).height;
|
|
77
109
|
return a;
|
|
78
110
|
});
|
|
79
111
|
|
|
112
|
+
// ── Clase final ────────────────────────────────────────────────────────────
|
|
80
113
|
const finalClass = $derived(
|
|
81
114
|
`${klass} ${animationClasses} ${props.chevronState ? "bz-icon-chevron" : ""}`.trim()
|
|
82
115
|
);
|
|
83
116
|
|
|
84
|
-
//
|
|
117
|
+
// ── Centro del viewBox para rot/flip exactos en SVG ────────────────────────
|
|
85
118
|
function getViewBoxCenter(vb: string) {
|
|
86
119
|
const parts = vb.trim().split(/\s+/).map(Number);
|
|
87
120
|
const [minX, minY, w, h] = parts.length === 4 ? parts : [0, 0, 24, 24];
|
|
88
121
|
return { cx: minX + w / 2, cy: minY + h / 2 };
|
|
89
122
|
}
|
|
90
|
-
|
|
91
|
-
const
|
|
123
|
+
const center = $derived(getViewBoxCenter(common.viewBox));
|
|
124
|
+
const cx = $derived(center.cx);
|
|
125
|
+
const cy = $derived(center.cy);
|
|
92
126
|
|
|
93
127
|
const svgTransform = $derived(() => {
|
|
94
128
|
const cmds: string[] = [];
|
|
95
|
-
|
|
96
|
-
// rotate
|
|
97
129
|
if (props.rotate != null) {
|
|
98
130
|
const r =
|
|
99
131
|
typeof props.rotate === "number"
|
|
100
132
|
? props.rotate
|
|
101
133
|
: parseFloat(String(props.rotate));
|
|
102
|
-
if (!
|
|
134
|
+
if (!isNaN(r)) cmds.push(`rotate(${r} ${cx} ${cy})`);
|
|
103
135
|
}
|
|
104
|
-
|
|
105
|
-
// flips
|
|
106
136
|
if (props.flipH || props.flipV) {
|
|
107
137
|
const sx = props.flipH ? -1 : 1;
|
|
108
138
|
const sy = props.flipV ? -1 : 1;
|
|
@@ -110,27 +140,46 @@
|
|
|
110
140
|
`translate(${cx} ${cy}) scale(${sx} ${sy}) translate(${-cx} ${-cy})`
|
|
111
141
|
);
|
|
112
142
|
}
|
|
113
|
-
|
|
114
143
|
return cmds.join(" ");
|
|
115
144
|
});
|
|
116
145
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
146
|
+
// ── Hover handlers ─────────────────────────────────────────────────────────
|
|
147
|
+
function handleMouseEnter() {
|
|
148
|
+
internalHovered = true;
|
|
149
|
+
}
|
|
150
|
+
function handleMouseLeave() {
|
|
151
|
+
internalHovered = false;
|
|
152
|
+
}
|
|
121
153
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
154
|
+
// ── Efectos declarativos (props.effects > attrs.* retrocompat) ─────────────
|
|
155
|
+
let svgRef: SVGSVGElement | null = $state(null);
|
|
156
|
+
|
|
157
|
+
$effect(() => {
|
|
158
|
+
if (!svgRef) return;
|
|
159
|
+
|
|
160
|
+
const effectsOpts: IconEffectOptions = { ...(props.effects ?? {}) };
|
|
161
|
+
const a = props.attrs ?? {};
|
|
162
|
+
if (a.spinOnHover) effectsOpts.spinOnHover = true;
|
|
163
|
+
if (a.bounceOnHover) effectsOpts.bounceOnHover = true;
|
|
164
|
+
if (a.wiggleOnHover) effectsOpts.wiggleOnHover = true;
|
|
165
|
+
if (a.slideOnHover) effectsOpts.slideOnHover = a.slideOnHover;
|
|
166
|
+
if (a.morphOnHover) effectsOpts.morphOnHover = a.morphOnHover;
|
|
167
|
+
if (a.elasticOnClick) effectsOpts.elasticOnClick = true;
|
|
168
|
+
if (a.heartbeatOnActive) effectsOpts.heartbeatOnActive = true;
|
|
169
|
+
if (a.hoverScale) effectsOpts.hoverScale = a.hoverScale;
|
|
170
|
+
if (a.pressScale) effectsOpts.pressScale = a.pressScale;
|
|
171
|
+
|
|
172
|
+
if (Object.keys(effectsOpts).length > 0) {
|
|
173
|
+
const controller = iconEffects(svgRef, effectsOpts);
|
|
174
|
+
return () => controller.destroy();
|
|
126
175
|
}
|
|
127
|
-
return v;
|
|
128
176
|
});
|
|
129
177
|
</script>
|
|
130
178
|
|
|
131
179
|
<svg
|
|
180
|
+
bind:this={svgRef}
|
|
132
181
|
xmlns="http://www.w3.org/2000/svg"
|
|
133
|
-
{...safeAttrs}
|
|
182
|
+
{...safeAttrs()}
|
|
134
183
|
width={finalSize}
|
|
135
184
|
height={finalSize}
|
|
136
185
|
viewBox={common.viewBox}
|
|
@@ -141,16 +190,18 @@
|
|
|
141
190
|
aria-label={ariaLabel}
|
|
142
191
|
aria-labelledby={ariaLabelledby}
|
|
143
192
|
focusable="false"
|
|
144
|
-
{style}
|
|
145
|
-
fill={
|
|
146
|
-
stroke={
|
|
147
|
-
stroke-width={
|
|
193
|
+
style={style()}
|
|
194
|
+
fill={visual.fill}
|
|
195
|
+
stroke={visual.stroke}
|
|
196
|
+
stroke-width={visual.strokeWidth}
|
|
148
197
|
stroke-linecap={props.strokeLinecap}
|
|
149
198
|
stroke-linejoin={props.strokeLinejoin}
|
|
150
199
|
vector-effect={props.nonScalingStroke ? "non-scaling-stroke" : undefined}
|
|
151
200
|
data-testid={props.testId}
|
|
201
|
+
onmouseenter={handleMouseEnter}
|
|
202
|
+
onmouseleave={handleMouseLeave}
|
|
152
203
|
>
|
|
153
|
-
{#if props.title}<title id={
|
|
204
|
+
{#if props.title}<title id={computedTitleId}>{props.title}</title>{/if}
|
|
154
205
|
<g transform={svgTransform()}>
|
|
155
206
|
{@render props.children?.()}
|
|
156
207
|
</g>
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
export type IconEffectOptions = {
|
|
2
|
-
hoverColor?: string;
|
|
3
|
-
activeColor?: string;
|
|
4
|
-
hoverScale?: number;
|
|
5
|
-
pressScale?: number;
|
|
6
|
-
rotateOnHover?: number;
|
|
7
2
|
spinOnHover?: boolean;
|
|
8
3
|
bounceOnHover?: boolean;
|
|
4
|
+
wiggleOnHover?: boolean;
|
|
5
|
+
slideOnHover?: "up" | "down" | "left" | "right";
|
|
6
|
+
morphOnHover?: "play" | "pause" | "menu" | "close" | "arrow" | "check";
|
|
7
|
+
elasticOnClick?: boolean;
|
|
8
|
+
heartbeatOnActive?: boolean;
|
|
9
9
|
pulse?: boolean;
|
|
10
|
-
|
|
10
|
+
hoverScale?: number;
|
|
11
|
+
pressScale?: number;
|
|
12
|
+
rotateOnHover?: number;
|
|
11
13
|
transitionMs?: number;
|
|
12
14
|
easing?: string;
|
|
13
15
|
cursor?: string;
|
|
@@ -15,7 +17,7 @@ export type IconEffectOptions = {
|
|
|
15
17
|
focusRing?: boolean;
|
|
16
18
|
kbFocusAttr?: string;
|
|
17
19
|
};
|
|
18
|
-
export declare function iconEffects(node:
|
|
20
|
+
export declare function iconEffects(node: Element, opts?: IconEffectOptions): {
|
|
19
21
|
update(next?: IconEffectOptions): void;
|
|
20
22
|
destroy(): void;
|
|
21
23
|
};
|
package/dist/effects.js
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
function getPrefersReducedMotion() {
|
|
2
|
+
if (typeof window === "undefined")
|
|
3
|
+
return false;
|
|
4
|
+
return (window.matchMedia?.("(prefers-reduced-motion: reduce)").matches ?? false);
|
|
5
|
+
}
|
|
6
|
+
export function iconEffects(node, opts = {}) {
|
|
7
|
+
const prefersReducedMotion = getPrefersReducedMotion();
|
|
8
|
+
let hovering = false;
|
|
9
|
+
let pressing = false;
|
|
10
|
+
let rafId = 0;
|
|
11
|
+
// Setup inicial
|
|
12
|
+
if (opts.tooltip)
|
|
13
|
+
node.setAttribute("title", opts.tooltip);
|
|
14
|
+
if (opts.cursor && node instanceof HTMLElement)
|
|
15
|
+
node.style.cursor = opts.cursor;
|
|
16
|
+
const transitionMs = opts.transitionMs ?? 160;
|
|
17
|
+
const easing = opts.easing ?? "cubic-bezier(.2,.8,.2,1)";
|
|
18
|
+
if (!prefersReducedMotion &&
|
|
19
|
+
(node instanceof HTMLElement || node instanceof SVGElement)) {
|
|
20
|
+
node.style.transition = `transform ${transitionMs}ms ${easing}`;
|
|
21
|
+
}
|
|
22
|
+
if (opts.pulse && !prefersReducedMotion) {
|
|
23
|
+
node.classList.add("bz-icon-pulse");
|
|
24
|
+
}
|
|
25
|
+
function applyAnimations() {
|
|
26
|
+
cancelAnimationFrame(rafId);
|
|
27
|
+
rafId = requestAnimationFrame(() => {
|
|
28
|
+
if (prefersReducedMotion)
|
|
29
|
+
return;
|
|
30
|
+
const parts = [];
|
|
31
|
+
// Escala
|
|
32
|
+
const scale = hovering
|
|
33
|
+
? pressing && opts.pressScale
|
|
34
|
+
? opts.pressScale
|
|
35
|
+
: opts.hoverScale ?? 1
|
|
36
|
+
: 1;
|
|
37
|
+
if (scale !== 1)
|
|
38
|
+
parts.push(`scale(${scale})`);
|
|
39
|
+
// Rotación
|
|
40
|
+
if (hovering && opts.rotateOnHover)
|
|
41
|
+
parts.push(`rotate(${opts.rotateOnHover}deg)`);
|
|
42
|
+
// Slide
|
|
43
|
+
if (hovering && opts.slideOnHover) {
|
|
44
|
+
const slideMap = {
|
|
45
|
+
up: "translateY(-4px)",
|
|
46
|
+
down: "translateY(4px)",
|
|
47
|
+
left: "translateX(-4px)",
|
|
48
|
+
right: "translateX(4px)",
|
|
49
|
+
};
|
|
50
|
+
parts.push(slideMap[opts.slideOnHover]);
|
|
51
|
+
}
|
|
52
|
+
if (node instanceof HTMLElement || node instanceof SVGElement) {
|
|
53
|
+
node.style.transform = parts.join(" ");
|
|
54
|
+
}
|
|
55
|
+
// Clases de animación CSS
|
|
56
|
+
node.classList.toggle("bz-icon-spin", !!(hovering && opts.spinOnHover));
|
|
57
|
+
node.classList.toggle("bz-icon-bounce", !!(hovering && opts.bounceOnHover));
|
|
58
|
+
node.classList.toggle("bz-icon-wiggle", !!(hovering && opts.wiggleOnHover));
|
|
59
|
+
node.classList.toggle("bz-icon-heartbeat", !!(pressing && opts.heartbeatOnActive));
|
|
60
|
+
node.classList.toggle("bz-icon-elastic", !!(pressing && opts.elasticOnClick));
|
|
61
|
+
if (opts.morphOnHover) {
|
|
62
|
+
node.classList.toggle(`bz-icon-morph-${opts.morphOnHover}`, hovering);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
// Handlers
|
|
67
|
+
function onEnter() {
|
|
68
|
+
hovering = true;
|
|
69
|
+
applyAnimations();
|
|
70
|
+
}
|
|
71
|
+
function onLeave() {
|
|
72
|
+
hovering = false;
|
|
73
|
+
pressing = false;
|
|
74
|
+
applyAnimations();
|
|
75
|
+
}
|
|
76
|
+
function onDown() {
|
|
77
|
+
pressing = true;
|
|
78
|
+
applyAnimations();
|
|
79
|
+
}
|
|
80
|
+
function onUp() {
|
|
81
|
+
pressing = false;
|
|
82
|
+
applyAnimations();
|
|
83
|
+
}
|
|
84
|
+
// Nota: eventos de teclado SIN { passive: true }
|
|
85
|
+
function onKeydown(e) {
|
|
86
|
+
if (opts.focusRing !== false && e.key === "Tab") {
|
|
87
|
+
node.setAttribute(opts.kbFocusAttr ?? "data-bz-kb-focus", "true");
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function onMousedown() {
|
|
91
|
+
if (opts.focusRing !== false) {
|
|
92
|
+
node.removeAttribute(opts.kbFocusAttr ?? "data-bz-kb-focus");
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// Listeners
|
|
96
|
+
node.addEventListener("pointerenter", onEnter, { passive: true });
|
|
97
|
+
node.addEventListener("pointerleave", onLeave, { passive: true });
|
|
98
|
+
node.addEventListener("pointerdown", onDown, { passive: true });
|
|
99
|
+
node.addEventListener("pointerup", onUp, { passive: true });
|
|
100
|
+
node.addEventListener("keydown", onKeydown); // ← sin passive
|
|
101
|
+
node.addEventListener("mousedown", onMousedown, { passive: true });
|
|
102
|
+
return {
|
|
103
|
+
update(next) {
|
|
104
|
+
if (!next)
|
|
105
|
+
return;
|
|
106
|
+
if (next.tooltip !== undefined) {
|
|
107
|
+
if (next.tooltip)
|
|
108
|
+
node.setAttribute("title", next.tooltip);
|
|
109
|
+
else
|
|
110
|
+
node.removeAttribute("title");
|
|
111
|
+
}
|
|
112
|
+
if (next.cursor !== undefined && node instanceof HTMLElement) {
|
|
113
|
+
node.style.cursor = next.cursor || "";
|
|
114
|
+
}
|
|
115
|
+
Object.assign(opts, next);
|
|
116
|
+
applyAnimations();
|
|
117
|
+
},
|
|
118
|
+
destroy() {
|
|
119
|
+
cancelAnimationFrame(rafId);
|
|
120
|
+
node.removeEventListener("pointerenter", onEnter);
|
|
121
|
+
node.removeEventListener("pointerleave", onLeave);
|
|
122
|
+
node.removeEventListener("pointerdown", onDown);
|
|
123
|
+
node.removeEventListener("pointerup", onUp);
|
|
124
|
+
node.removeEventListener("keydown", onKeydown);
|
|
125
|
+
node.removeEventListener("mousedown", onMousedown);
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
}
|