weasel-icons 0.1.0 → 0.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.
Files changed (104) hide show
  1. package/README.md +13 -4
  2. package/THIRD_PARTY_NOTICES.md +14 -4
  3. package/dist/icons/alert/alert-icon.d.ts +6 -0
  4. package/dist/icons/alert/alert-icon.js +22 -0
  5. package/dist/icons/alert/alert-rig.d.ts +12 -0
  6. package/dist/icons/alert/alert-rig.js +25 -0
  7. package/dist/icons/badge-check/badge-check-icon.d.ts +5 -0
  8. package/dist/icons/badge-check/badge-check-icon.js +33 -0
  9. package/dist/icons/badge-check/badge-check-rig.d.ts +14 -0
  10. package/dist/icons/badge-check/badge-check-rig.js +36 -0
  11. package/dist/icons/bell/bell-icon.js +1 -1
  12. package/dist/icons/bulb/bulb-icon.d.ts +5 -0
  13. package/dist/icons/bulb/bulb-icon.js +25 -0
  14. package/dist/icons/bulb/bulb-rig.d.ts +13 -0
  15. package/dist/icons/bulb/bulb-rig.js +37 -0
  16. package/dist/icons/check/check-icon.d.ts +5 -0
  17. package/dist/icons/check/check-icon.js +26 -0
  18. package/dist/icons/check/check-rig.d.ts +6 -0
  19. package/dist/icons/check/check-rig.js +38 -0
  20. package/dist/icons/clapperboard/clapperboard-icon.d.ts +5 -0
  21. package/dist/icons/clapperboard/clapperboard-icon.js +18 -0
  22. package/dist/icons/clapperboard/clapperboard-rig.d.ts +12 -0
  23. package/dist/icons/clapperboard/clapperboard-rig.js +25 -0
  24. package/dist/icons/close/close-icon.d.ts +5 -0
  25. package/dist/icons/close/close-icon.js +27 -0
  26. package/dist/icons/close/close-rig.d.ts +10 -0
  27. package/dist/icons/close/close-rig.js +31 -0
  28. package/dist/icons/copy/copy-icon.d.ts +5 -0
  29. package/dist/icons/copy/copy-icon.js +31 -0
  30. package/dist/icons/copy/copy-rig.d.ts +13 -0
  31. package/dist/icons/copy/copy-rig.js +44 -0
  32. package/dist/icons/cursor/cursor-icon.d.ts +5 -0
  33. package/dist/icons/cursor/cursor-icon.js +32 -0
  34. package/dist/icons/cursor/cursor-rig.d.ts +21 -0
  35. package/dist/icons/cursor/cursor-rig.js +63 -0
  36. package/dist/icons/delete/delete-icon.d.ts +5 -0
  37. package/dist/icons/delete/delete-icon.js +25 -0
  38. package/dist/icons/delete/delete-rig.d.ts +15 -0
  39. package/dist/icons/delete/delete-rig.js +33 -0
  40. package/dist/icons/dog/dog-icon.js +1 -1
  41. package/dist/icons/download/download-icon.js +1 -1
  42. package/dist/icons/eye/eye-icon.d.ts +5 -0
  43. package/dist/icons/eye/eye-icon.js +27 -0
  44. package/dist/icons/eye/eye-rig.d.ts +9 -0
  45. package/dist/icons/eye/eye-rig.js +37 -0
  46. package/dist/icons/heart/heart-icon.js +1 -1
  47. package/dist/icons/heart/heart-rig.js +1 -1
  48. package/dist/icons/link/link-icon.d.ts +5 -0
  49. package/dist/icons/link/link-icon.js +30 -0
  50. package/dist/icons/link/link-rig.d.ts +13 -0
  51. package/dist/icons/link/link-rig.js +37 -0
  52. package/dist/icons/monkey/monkey-icon.js +1 -1
  53. package/dist/icons/plug/plug-icon.d.ts +5 -0
  54. package/dist/icons/plug/plug-icon.js +21 -0
  55. package/dist/icons/plug/plug-rig.d.ts +16 -0
  56. package/dist/icons/plug/plug-rig.js +29 -0
  57. package/dist/icons/refresh/refresh-icon.d.ts +5 -0
  58. package/dist/icons/refresh/refresh-icon.js +40 -0
  59. package/dist/icons/refresh/refresh-rig.d.ts +11 -0
  60. package/dist/icons/refresh/refresh-rig.js +30 -0
  61. package/dist/icons/replace/replace-icon.d.ts +5 -0
  62. package/dist/icons/replace/replace-icon.js +25 -0
  63. package/dist/icons/replace/replace-rig.d.ts +14 -0
  64. package/dist/icons/replace/replace-rig.js +24 -0
  65. package/dist/icons/rocket/rocket-icon.js +1 -1
  66. package/dist/icons/search/search-icon.js +1 -1
  67. package/dist/icons/shield/shield-icon.d.ts +5 -0
  68. package/dist/icons/shield/shield-icon.js +25 -0
  69. package/dist/icons/shield/shield-rig.d.ts +23 -0
  70. package/dist/icons/shield/shield-rig.js +42 -0
  71. package/dist/icons/ship/ship-icon.js +1 -1
  72. package/dist/icons/sparkle/sparkle-icon.d.ts +5 -0
  73. package/dist/icons/sparkle/sparkle-icon.js +27 -0
  74. package/dist/icons/sparkle/sparkle-rig.d.ts +8 -0
  75. package/dist/icons/sparkle/sparkle-rig.js +6 -0
  76. package/dist/icons/sparkles/sparkles-icon.d.ts +5 -0
  77. package/dist/icons/sparkles/sparkles-icon.js +27 -0
  78. package/dist/icons/sparkles/sparkles-rig.d.ts +14 -0
  79. package/dist/icons/sparkles/sparkles-rig.js +24 -0
  80. package/dist/icons/star/star-icon.d.ts +5 -0
  81. package/dist/icons/star/star-icon.js +38 -0
  82. package/dist/icons/star/star-rig.d.ts +20 -0
  83. package/dist/icons/star/star-rig.js +61 -0
  84. package/dist/icons/swap/swap-icon.d.ts +5 -0
  85. package/dist/icons/swap/swap-icon.js +26 -0
  86. package/dist/icons/swap/swap-rig.d.ts +10 -0
  87. package/dist/icons/swap/swap-rig.js +24 -0
  88. package/dist/icons/wave/wave-icon.d.ts +5 -0
  89. package/dist/icons/wave/wave-icon.js +18 -0
  90. package/dist/icons/wave/wave-rig.d.ts +8 -0
  91. package/dist/icons/wave/wave-rig.js +39 -0
  92. package/dist/index.d.ts +21 -2
  93. package/dist/index.js +20 -1
  94. package/dist/shared/icon-colors.d.ts +23 -4
  95. package/dist/shared/icon-types.d.ts +1 -0
  96. package/dist/shared/sparkle-motion.d.ts +12 -0
  97. package/dist/shared/sparkle-motion.js +38 -0
  98. package/dist/shared/use-icon-motion.d.ts +4 -0
  99. package/dist/shared/use-icon-motion.js +26 -0
  100. package/package.json +104 -9
  101. package/dist/icons/leaf/leaf-icon.d.ts +0 -6
  102. package/dist/icons/leaf/leaf-icon.js +0 -29
  103. package/dist/icons/leaf/leaf-rig.d.ts +0 -25
  104. package/dist/icons/leaf/leaf-rig.js +0 -201
@@ -0,0 +1,5 @@
1
+ import type { StatefulIconProps } from "../../shared/icon-types.js";
2
+ import { type SwapState } from "./swap-rig.js";
3
+ export { swapStates, type SwapState } from "./swap-rig.js";
4
+ export type SwapIconProps = StatefulIconProps<SwapState>;
5
+ export declare const SwapIcon: import("react").NamedExoticComponent<SwapIconProps>;
@@ -0,0 +1,26 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { memo, useCallback, useRef } from 'react';
4
+ import { motion, useTransform } from 'motion/react';
5
+ import { usePoseWeight, useBehaviorClock, useActionProgress } from "../../shared/use-icon-motion.js";
6
+ import { useMotionPreference } from "../../shared/use-motion-preference.js";
7
+ import { SWAP_LEFT, SWAP_RIGHT, sampleSwap } from "./swap-rig.js";
8
+ export { swapStates } from "./swap-rig.js";
9
+ const ORIGIN = { originX: '0px', originY: '0px', transformBox: 'view-box' };
10
+ export const SwapIcon = memo(function SwapIcon({ state = 'idle', size = 24, color = 'currentColor', strokeWidth = 1.65, speed = 1, alive = false, className, }) {
11
+ const reduced = useMotionPreference(), element = useRef(null);
12
+ const pace = Number.isFinite(speed) ? Math.min(4, Math.max(0.1, speed)) : 1;
13
+ const idle = usePoseWeight(state === 'idle', pace, reduced), swapping = usePoseWeight(state === 'swapping', pace, reduced), swapped = usePoseWeight(state === 'swapped', pace, reduced), error = usePoseWeight(state === 'error', pace, reduced);
14
+ const performs = alive && !reduced && state !== 'idle', life = usePoseWeight(performs, pace, reduced), time = useBehaviorClock(element, performs, pace, life);
15
+ const first = useRef(state !== 'swapping');
16
+ const canRestart = useCallback(() => {
17
+ const invisible = first.current || swapping.get() < 0.001;
18
+ first.current = false;
19
+ return invisible;
20
+ }, [swapping]);
21
+ const entry = useActionProgress(state === 'swapping', pace, reduced, 1.1, canRestart);
22
+ const errorEntry = useActionProgress(state === 'error', pace, reduced, 1.15);
23
+ const rig = useTransform(() => sampleSwap([idle.get(), swapping.get(), swapped.get(), error.get()], time.get(), life.get(), entry.get(), errorEntry.get()));
24
+ const body = useTransform(rig, (r) => r.body), left = useTransform(rig, (r) => r.left), right = useTransform(rig, (r) => r.right);
25
+ return (_jsx("svg", { ref: element, className: className, width: size, height: size, viewBox: "0 0 24 24", xmlns: "http://www.w3.org/2000/svg", fill: "none", stroke: color, strokeWidth: strokeWidth, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", focusable: "false", pointerEvents: "none", style: { display: 'block', overflow: 'visible' }, children: _jsxs(motion.g, { "data-layer": "swap-body", transform: body, style: ORIGIN, children: [_jsx(motion.path, { "data-layer": "swap-left", "data-part": "left-arrow", d: SWAP_LEFT, transform: left, style: ORIGIN }), _jsx(motion.path, { "data-layer": "swap-right", "data-part": "right-arrow", d: SWAP_RIGHT, transform: right, style: ORIGIN })] }) }));
26
+ });
@@ -0,0 +1,10 @@
1
+ export declare const swapStates: readonly ["idle", "swapping", "swapped", "error"];
2
+ export type SwapState = (typeof swapStates)[number];
3
+ /** Lucide arrow-left-right: complete arrows, with their heads attached. */
4
+ export declare const SWAP_LEFT = "M8 3 4 7l4 4M4 7h16";
5
+ export declare const SWAP_RIGHT = "m16 21 4-4-4-4M20 17H4";
6
+ export declare function sampleSwap(weights: readonly number[], time?: number, alive?: number, entry?: number, errorEntry?: number): {
7
+ body: string;
8
+ left: string;
9
+ right: string;
10
+ };
@@ -0,0 +1,24 @@
1
+ export const swapStates = ['idle', 'swapping', 'swapped', 'error'];
2
+ /** Lucide arrow-left-right: complete arrows, with their heads attached. */
3
+ export const SWAP_LEFT = 'M8 3 4 7l4 4M4 7h16';
4
+ export const SWAP_RIGHT = 'm16 21 4-4-4-4M20 17H4';
5
+ export function sampleSwap(weights, time = 0, alive = 0, entry = 1, errorEntry = 1) {
6
+ const clamp = (n) => Math.max(0, Math.min(1, n));
7
+ const [, swapping, swapped, error] = weights.map(clamp), life = clamp(alive);
8
+ const t = time % 3.2;
9
+ const travel = Math.max(entry >= 1 ? 0 : behaviorBeat(clamp(entry) * 1.1, 0, 0.38, 0.1, 0.62), life * behaviorBeat(t, 0.12, 0.38, 0.1, 0.62), life * behaviorBeat(t, 1.72, 0.38, 0.1, 0.62));
10
+ // Attempt → jam → recoil → rest. Error feedback is not gated by living mode.
11
+ const rejection = (seconds) => 1.7 * behaviorBeat(seconds, 0.06, 0.2, 0.05, 0.25) -
12
+ 0.9 * behaviorBeat(seconds, 0.5, 0.12, 0.02, 0.46);
13
+ const entryRefusal = errorEntry >= 1 ? 0 : rejection(clamp(errorEntry) * 1.15);
14
+ const refuse = error * ((1 - life) * entryRefusal + life * rejection(t));
15
+ const shift = -swapping * travel * 1.45 + error * 0.65 + refuse;
16
+ const spread = swapped * (0.5 + life * behaviorBeat(t, 0.2, 0.3, 0.1, 0.6) * 0.25);
17
+ const tilt = error * 8 + refuse * 5;
18
+ return {
19
+ body: `translate(12px, 12px) rotate(${180 * swapped}deg) scale(0.82) translate(-12px, -12px)`,
20
+ left: `translate(${shift}px, ${-spread}px) translate(12px, 7px) rotate(${tilt}deg) translate(-12px, -7px)`,
21
+ right: `translate(${-shift}px, ${spread}px) translate(12px, 17px) rotate(${-tilt}deg) translate(-12px, -17px)`,
22
+ };
23
+ }
24
+ import { behaviorBeat } from "../../shared/behavior-curves.js";
@@ -0,0 +1,5 @@
1
+ import type { StatefulIconProps } from "../../shared/icon-types.js";
2
+ import { type WaveState } from "./wave-rig.js";
3
+ export { waveStates, type WaveState } from "./wave-rig.js";
4
+ export type WaveIconProps = StatefulIconProps<WaveState>;
5
+ export declare const WaveIcon: import("react").NamedExoticComponent<WaveIconProps>;
@@ -0,0 +1,18 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { memo, useRef } from 'react';
4
+ import { motion, useTransform } from 'motion/react';
5
+ import { usePoseWeight, useBehaviorClock } from "../../shared/use-icon-motion.js";
6
+ import { useMotionPreference } from "../../shared/use-motion-preference.js";
7
+ import { sampleWave } from "./wave-rig.js";
8
+ export { waveStates } from "./wave-rig.js";
9
+ export const WaveIcon = memo(function WaveIcon({ state = 'idle', size = 24, color = 'currentColor', strokeWidth = 1.65, speed = 1, alive = false, className, }) {
10
+ const reduced = useMotionPreference(), element = useRef(null);
11
+ const pace = Number.isFinite(speed) ? Math.min(4, Math.max(0.1, speed)) : 1;
12
+ const idle = usePoseWeight(state === 'idle', pace, reduced), flowing = usePoseWeight(state === 'flowing', pace, reduced), energetic = usePoseWeight(state === 'energetic', pace, reduced), flat = usePoseWeight(state === 'flat', pace, reduced);
13
+ const performs = alive && !reduced && (state === 'flowing' || state === 'energetic');
14
+ const life = usePoseWeight(performs, pace, reduced), time = useBehaviorClock(element, performs, pace, life);
15
+ const rig = useTransform(() => sampleWave([idle.get(), flowing.get(), energetic.get(), flat.get()], time.get(), life.get()));
16
+ const path = useTransform(rig, (r) => r.path);
17
+ return (_jsx("svg", { ref: element, className: className, width: size, height: size, viewBox: "0 0 24 24", xmlns: "http://www.w3.org/2000/svg", fill: "none", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", focusable: "false", pointerEvents: "none", style: { display: 'block', overflow: 'visible' }, children: _jsx(motion.path, { "data-layer": "wave-line", d: path, stroke: color, strokeWidth: strokeWidth }) }));
18
+ });
@@ -0,0 +1,8 @@
1
+ export declare const waveStates: readonly ["idle", "flowing", "energetic", "flat"];
2
+ export type WaveState = (typeof waveStates)[number];
3
+ /** Hermite segments preserve exact slope correspondence as the wave travels.
4
+ * Life blends positions, not accumulated phase, so switching it off cannot rewind.
5
+ */
6
+ export declare function sampleWave(weights: readonly number[], time?: number, alive?: number): {
7
+ path: string;
8
+ };
@@ -0,0 +1,39 @@
1
+ export const waveStates = ['idle', 'flowing', 'energetic', 'flat'];
2
+ /** Hermite segments preserve exact slope correspondence as the wave travels.
3
+ * Life blends positions, not accumulated phase, so switching it off cannot rewind.
4
+ */
5
+ export function sampleWave(weights, time = 0, alive = 0) {
6
+ const clamp = (n) => Math.max(0, Math.min(1, n));
7
+ const [idle, flowing, energetic] = weights.map(clamp), life = clamp(alive);
8
+ const k = (Math.PI * 4) / 18;
9
+ function point(x) {
10
+ const u = x - 3;
11
+ const flowPhase = k * u + 0.4, energyPhase = k * u;
12
+ const y = idle * 3.6 * Math.sin(k * u) +
13
+ flowing * 3.6 * ((1 - life) * Math.sin(flowPhase) + life * Math.sin(flowPhase - time * 2.1)) +
14
+ energetic *
15
+ (5 * ((1 - life) * Math.sin(energyPhase) + life * Math.sin(energyPhase - time * 3.6)) +
16
+ 0.35 *
17
+ ((1 - life) * Math.sin(energyPhase * 2) +
18
+ life * Math.sin(energyPhase * 2 - time * 7.2)));
19
+ const slope = idle * 3.6 * k * Math.cos(k * u) +
20
+ flowing *
21
+ 3.6 *
22
+ k *
23
+ ((1 - life) * Math.cos(flowPhase) + life * Math.cos(flowPhase - time * 2.1)) +
24
+ energetic *
25
+ (5 * k * ((1 - life) * Math.cos(energyPhase) + life * Math.cos(energyPhase - time * 3.6)) +
26
+ 0.7 *
27
+ k *
28
+ ((1 - life) * Math.cos(energyPhase * 2) +
29
+ life * Math.cos(energyPhase * 2 - time * 7.2)));
30
+ return { y: 12 - y, slope: -slope };
31
+ }
32
+ const step = 18 / 24;
33
+ let path = `M3 ${point(3).y}`;
34
+ for (let i = 0; i < 24; i++) {
35
+ const x = 3 + i * step, a = point(x), b = point(x + step);
36
+ path += `C${x + step / 3} ${a.y + (a.slope * step) / 3} ${x + (step * 2) / 3} ${b.y - (b.slope * step) / 3} ${x + step} ${b.y}`;
37
+ }
38
+ return { path };
39
+ }
package/dist/index.d.ts CHANGED
@@ -1,6 +1,15 @@
1
+ export { RefreshIcon, refreshStates, type RefreshState, type RefreshIconProps, } from "./icons/refresh/refresh-icon.js";
2
+ export { CopyIcon, copyStates, type CopyState, type CopyIconProps } from "./icons/copy/copy-icon.js";
3
+ export { EyeIcon, eyeStates, type EyeState, type EyeIconProps } from "./icons/eye/eye-icon.js";
4
+ export { LinkIcon, linkStates, type LinkState, type LinkIconProps } from "./icons/link/link-icon.js";
5
+ export { CheckIcon, checkStates, type CheckState, type CheckIconProps, } from "./icons/check/check-icon.js";
6
+ export { CloseIcon, closeStates, type CloseState, type CloseIconProps, } from "./icons/close/close-icon.js";
7
+ export { SparkleIcon, sparkleStates, type SparkleState, type SparkleIconProps, } from "./icons/sparkle/sparkle-icon.js";
8
+ export { SparklesIcon, sparklesStates, type SparklesState, type SparklesIconProps, } from "./icons/sparkles/sparkles-icon.js";
9
+ export { StarIcon, starStates, type StarState, type StarIconProps } from "./icons/star/star-icon.js";
10
+ export { WaveIcon, waveStates, type WaveState, type WaveIconProps } from "./icons/wave/wave-icon.js";
1
11
  export { DogIcon, dogStates, type DogState, type DogIconProps } from "./icons/dog/dog-icon.js";
2
12
  export { HeartIcon, heartStates, type HeartIconProps, type HeartState, } from "./icons/heart/heart-icon.js";
3
- export { LeafIcon, leafStates, type LeafState, type LeafIconProps } from "./icons/leaf/leaf-icon.js";
4
13
  export { MonkeyIcon, monkeyStates, type MonkeyState, type MonkeyIconProps, } from "./icons/monkey/monkey-icon.js";
5
14
  export { RocketIcon, rocketStates, type RocketState, type RocketIconProps, } from "./icons/rocket/rocket-icon.js";
6
15
  export { ShipIcon, shipStates, type ShipState, type ShipIconProps } from "./icons/ship/ship-icon.js";
@@ -8,4 +17,14 @@ export { SearchIcon, searchStates, type SearchState, type SearchIconProps, } fro
8
17
  export { BellIcon, bellStates, type BellState, type BellIconProps } from "./icons/bell/bell-icon.js";
9
18
  export { DownloadIcon, downloadStates, type DownloadState, type DownloadIconProps, } from "./icons/download/download-icon.js";
10
19
  export type { StatefulIconProps } from "./shared/icon-types.js";
11
- export type { DogColors, MonkeyColors, HeartColors, ShipColors, LeafColors, } from "./shared/icon-colors.js";
20
+ export { ShieldIcon, shieldStates, type ShieldState, type ShieldIconProps, } from "./icons/shield/shield-icon.js";
21
+ export { CursorIcon, cursorStates, type CursorState, type CursorIconProps, } from "./icons/cursor/cursor-icon.js";
22
+ export { ClapperboardIcon, clapperboardStates, type ClapperboardState, type ClapperboardIconProps, } from "./icons/clapperboard/clapperboard-icon.js";
23
+ export { PlugIcon, plugStates, type PlugState, type PlugIconProps } from "./icons/plug/plug-icon.js";
24
+ export { BulbIcon, bulbStates, type BulbState, type BulbIconProps } from "./icons/bulb/bulb-icon.js";
25
+ export { AlertIcon, alertStates, type AlertState, type AlertIconProps, } from "./icons/alert/alert-icon.js";
26
+ export { BadgeCheckIcon, badgeCheckStates, type BadgeCheckState, type BadgeCheckIconProps, } from "./icons/badge-check/badge-check-icon.js";
27
+ export { ReplaceIcon, replaceStates, type ReplaceState, type ReplaceIconProps, } from "./icons/replace/replace-icon.js";
28
+ export { DeleteIcon, deleteStates, type DeleteState, type DeleteIconProps, } from "./icons/delete/delete-icon.js";
29
+ export { SwapIcon, swapStates, type SwapState, type SwapIconProps } from "./icons/swap/swap-icon.js";
30
+ export type { DogColors, MonkeyColors, HeartColors, ShipColors, AlertColors, } from "./shared/icon-colors.js";
package/dist/index.js CHANGED
@@ -1,10 +1,29 @@
1
1
  'use client';
2
+ export { RefreshIcon, refreshStates, } from "./icons/refresh/refresh-icon.js";
3
+ export { CopyIcon, copyStates } from "./icons/copy/copy-icon.js";
4
+ export { EyeIcon, eyeStates } from "./icons/eye/eye-icon.js";
5
+ export { LinkIcon, linkStates } from "./icons/link/link-icon.js";
6
+ export { CheckIcon, checkStates, } from "./icons/check/check-icon.js";
7
+ export { CloseIcon, closeStates, } from "./icons/close/close-icon.js";
8
+ export { SparkleIcon, sparkleStates, } from "./icons/sparkle/sparkle-icon.js";
9
+ export { SparklesIcon, sparklesStates, } from "./icons/sparkles/sparkles-icon.js";
10
+ export { StarIcon, starStates } from "./icons/star/star-icon.js";
11
+ export { WaveIcon, waveStates } from "./icons/wave/wave-icon.js";
2
12
  export { DogIcon, dogStates } from "./icons/dog/dog-icon.js";
3
13
  export { HeartIcon, heartStates, } from "./icons/heart/heart-icon.js";
4
- export { LeafIcon, leafStates } from "./icons/leaf/leaf-icon.js";
5
14
  export { MonkeyIcon, monkeyStates, } from "./icons/monkey/monkey-icon.js";
6
15
  export { RocketIcon, rocketStates, } from "./icons/rocket/rocket-icon.js";
7
16
  export { ShipIcon, shipStates } from "./icons/ship/ship-icon.js";
8
17
  export { SearchIcon, searchStates, } from "./icons/search/search-icon.js";
9
18
  export { BellIcon, bellStates } from "./icons/bell/bell-icon.js";
10
19
  export { DownloadIcon, downloadStates, } from "./icons/download/download-icon.js";
20
+ export { ShieldIcon, shieldStates, } from "./icons/shield/shield-icon.js";
21
+ export { CursorIcon, cursorStates, } from "./icons/cursor/cursor-icon.js";
22
+ export { ClapperboardIcon, clapperboardStates, } from "./icons/clapperboard/clapperboard-icon.js";
23
+ export { PlugIcon, plugStates } from "./icons/plug/plug-icon.js";
24
+ export { BulbIcon, bulbStates } from "./icons/bulb/bulb-icon.js";
25
+ export { AlertIcon, alertStates, } from "./icons/alert/alert-icon.js";
26
+ export { BadgeCheckIcon, badgeCheckStates, } from "./icons/badge-check/badge-check-icon.js";
27
+ export { ReplaceIcon, replaceStates, } from "./icons/replace/replace-icon.js";
28
+ export { DeleteIcon, deleteStates, } from "./icons/delete/delete-icon.js";
29
+ export { SwapIcon, swapStates } from "./icons/swap/swap-icon.js";
@@ -17,18 +17,37 @@ export interface ShipColors {
17
17
  windows?: string;
18
18
  water?: string;
19
19
  }
20
- export interface LeafColors {
21
- blade?: string;
22
- stem?: string;
20
+ export interface AlertColors {
21
+ /** Critical punctuation; defaults to red, independent of the triangle outline. */
22
+ critical?: string;
23
23
  }
24
24
  export interface IconColors {
25
+ refresh: never;
26
+ copy: never;
27
+ eye: never;
28
+ link: never;
29
+ check: never;
30
+ close: never;
31
+ sparkles: never;
32
+ sparkle: never;
33
+ star: never;
34
+ wave: never;
25
35
  dog: DogColors;
26
36
  monkey: MonkeyColors;
27
37
  heart: HeartColors;
28
38
  ship: ShipColors;
29
- leaf: LeafColors;
30
39
  rocket: never;
31
40
  search: never;
32
41
  bell: never;
33
42
  download: never;
43
+ shield: never;
44
+ cursor: never;
45
+ clapperboard: never;
46
+ plug: never;
47
+ bulb: never;
48
+ alert: AlertColors;
49
+ 'badge-check': never;
50
+ replace: never;
51
+ delete: never;
52
+ swap: never;
34
53
  }
@@ -5,6 +5,7 @@ export interface StatefulIconProps<State extends string, Colors = never> {
5
5
  accentColor?: string;
6
6
  /** Optional, icon-specific detail colours. Unset parts inherit their defaults. */
7
7
  colors?: Colors;
8
+ /** Outline width in SVG units. Defaults to 1.65. */
8
9
  strokeWidth?: number;
9
10
  speed?: number;
10
11
  /** Keeps the icon performing after its state transition has settled. */
@@ -0,0 +1,12 @@
1
+ export declare const SPARKLE_BLINK_DURATION = 2.4;
2
+ export declare function sampleTwinkle(time: number, alive: number, entry: number, start: number, repeats: readonly number[]): {
3
+ dark: number;
4
+ tilt: number;
5
+ };
6
+ export declare function sampleSparkleCore(weights: readonly number[], time: number, alive: number, entry: number, baseScale: number): {
7
+ core: string;
8
+ coreOpacity: number;
9
+ tint: number;
10
+ };
11
+ /** Lucide's ISC-licensed rounded four-point contour, shared by the two compositions. */
12
+ export declare const SPARKLE_CORE = "M11.017 2.814a1 1 0 0 1 1.966 0l1.051 5.558a2 2 0 0 0 1.594 1.594l5.558 1.051a1 1 0 0 1 0 1.966l-5.558 1.051a2 2 0 0 0-1.594 1.594l-1.051 5.558a1 1 0 0 1-1.966 0l-1.051-5.558a2 2 0 0 0-1.594-1.594l-5.558-1.051a1 1 0 0 1 0-1.966l5.558-1.051a2 2 0 0 0 1.594-1.594z";
@@ -0,0 +1,38 @@
1
+ import { behaviorBeat } from "./behavior-curves.js";
2
+ const clamp = (n) => Math.max(0, Math.min(1, n));
3
+ export const SPARKLE_BLINK_DURATION = 2.4;
4
+ /** Soft disappearance and recovery; no dark hold, flash or scale-pop. */
5
+ function twinkle(time, start) {
6
+ const dark = clamp(behaviorBeat(time, start, 0.48, 0, 0.68));
7
+ return {
8
+ dark,
9
+ tilt: dark * Math.sin(((time - start) / 1.16) * Math.PI * 2) * 4,
10
+ };
11
+ }
12
+ export function sampleTwinkle(time, alive, entry, start, repeats) {
13
+ const first = twinkle(clamp(entry) * SPARKLE_BLINK_DURATION, start);
14
+ let recurring = { dark: 0, tilt: 0 };
15
+ if (alive > 0)
16
+ for (const at of repeats) {
17
+ const beat = twinkle(((time % 6.4) + 6.4) % 6.4, at);
18
+ if (beat.dark > 0)
19
+ recurring = { dark: beat.dark * clamp(alive), tilt: beat.tilt * clamp(alive) };
20
+ }
21
+ // Smooth union keeps overlapping entry/live fades bounded, without max() handoff kinks.
22
+ return {
23
+ dark: 1 - (1 - first.dark) * (1 - recurring.dark),
24
+ tilt: first.tilt * (1 - recurring.dark * 0.5) + recurring.tilt * (1 - first.dark * 0.5),
25
+ };
26
+ }
27
+ export function sampleSparkleCore(weights, time, alive, entry, baseScale) {
28
+ const [, shimmering, blink, dimmed] = weights.map(clamp), life = clamp(alive);
29
+ const beat = sampleTwinkle(time, life, entry, 0.05, [0.2, 3.2]);
30
+ const scale = baseScale - dimmed * 0.09 + shimmering * 0.025;
31
+ return {
32
+ core: `translate(12px, 12px) rotate(${shimmering * (-5 + life * Math.sin(time * 1.5) * 4) + blink * beat.tilt}deg) scale(${scale}) translate(-12px, -12px)`,
33
+ coreOpacity: clamp(1 - blink * beat.dark * 0.9),
34
+ tint: 0,
35
+ };
36
+ }
37
+ /** Lucide's ISC-licensed rounded four-point contour, shared by the two compositions. */
38
+ export const SPARKLE_CORE = 'M11.017 2.814a1 1 0 0 1 1.966 0l1.051 5.558a2 2 0 0 0 1.594 1.594l5.558 1.051a1 1 0 0 1 0 1.966l-5.558 1.051a2 2 0 0 0-1.594 1.594l-1.051 5.558a1 1 0 0 1-1.966 0l-1.051-5.558a2 2 0 0 0-1.594-1.594l-5.558-1.051a1 1 0 0 1 0-1.966l5.558-1.051a2 2 0 0 0 1.594-1.594z';
@@ -1,6 +1,10 @@
1
1
  import { type RefObject } from 'react';
2
2
  import { type MotionValue } from 'motion/react';
3
3
  export declare function usePoseWeight(active: boolean, pace: number, reduced: boolean): MotionValue<number>;
4
+ /** Finite action playhead: preserve interrupted progress; restart only once invisible.
5
+ * Motion owns scheduling, not React state or another recurring frame loop.
6
+ */
7
+ export declare function useActionProgress(active: boolean, pace: number, reduced: boolean, duration: number, canRestart?: () => boolean): MotionValue<number>;
4
8
  /** Own the source subscription so changing speed disposes the previous spring.
5
9
  * Motion's source-following spring cleanup currently removes the subscription
6
10
  * without stopping the old animation when options change. A numeric spring has
@@ -24,6 +24,32 @@ export function usePoseWeight(active, pace, reduced) {
24
24
  }, [active, pace, reduced, value, spring]);
25
25
  return value;
26
26
  }
27
+ /** Finite action playhead: preserve interrupted progress; restart only once invisible.
28
+ * Motion owns scheduling, not React state or another recurring frame loop.
29
+ */
30
+ export function useActionProgress(active, pace, reduced, duration, canRestart) {
31
+ const progress = useMotionValue(1);
32
+ const wasActive = useRef(false);
33
+ useEffect(() => {
34
+ const entered = active && !wasActive.current;
35
+ wasActive.current = active;
36
+ if (reduced) {
37
+ progress.jump(1);
38
+ return;
39
+ }
40
+ if (entered && progress.get() === 1 && (canRestart?.() ?? true))
41
+ progress.jump(0);
42
+ const remaining = 1 - progress.get();
43
+ if (remaining <= 0)
44
+ return;
45
+ const animation = animate(progress, 1, {
46
+ duration: (duration * remaining) / pace,
47
+ ease: 'linear',
48
+ });
49
+ return () => animation.stop();
50
+ }, [active, pace, reduced, duration, progress, canRestart]);
51
+ return progress;
52
+ }
27
53
  /** Own the source subscription so changing speed disposes the previous spring.
28
54
  * Motion's source-following spring cleanup currently removes the subscription
29
55
  * without stopping the old animation when options change. A numeric spring has
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "weasel-icons",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Stateful animated React SVG icons. Icons that overreact beautifully.",
5
5
  "license": "MIT",
6
6
  "author": "Vinod Murugan",
@@ -39,11 +39,6 @@
39
39
  "import": "./dist/icons/rocket/rocket-icon.js",
40
40
  "default": "./dist/icons/rocket/rocket-icon.js"
41
41
  },
42
- "./leaf": {
43
- "types": "./dist/icons/leaf/leaf-icon.d.ts",
44
- "import": "./dist/icons/leaf/leaf-icon.js",
45
- "default": "./dist/icons/leaf/leaf-icon.js"
46
- },
47
42
  "./search": {
48
43
  "types": "./dist/icons/search/search-icon.d.ts",
49
44
  "import": "./dist/icons/search/search-icon.js",
@@ -59,7 +54,107 @@
59
54
  "import": "./dist/icons/download/download-icon.js",
60
55
  "default": "./dist/icons/download/download-icon.js"
61
56
  },
62
- "./package.json": "./package.json"
57
+ "./shield": {
58
+ "types": "./dist/icons/shield/shield-icon.d.ts",
59
+ "import": "./dist/icons/shield/shield-icon.js",
60
+ "default": "./dist/icons/shield/shield-icon.js"
61
+ },
62
+ "./cursor": {
63
+ "types": "./dist/icons/cursor/cursor-icon.d.ts",
64
+ "import": "./dist/icons/cursor/cursor-icon.js",
65
+ "default": "./dist/icons/cursor/cursor-icon.js"
66
+ },
67
+ "./clapperboard": {
68
+ "types": "./dist/icons/clapperboard/clapperboard-icon.d.ts",
69
+ "import": "./dist/icons/clapperboard/clapperboard-icon.js",
70
+ "default": "./dist/icons/clapperboard/clapperboard-icon.js"
71
+ },
72
+ "./plug": {
73
+ "types": "./dist/icons/plug/plug-icon.d.ts",
74
+ "import": "./dist/icons/plug/plug-icon.js",
75
+ "default": "./dist/icons/plug/plug-icon.js"
76
+ },
77
+ "./bulb": {
78
+ "types": "./dist/icons/bulb/bulb-icon.d.ts",
79
+ "import": "./dist/icons/bulb/bulb-icon.js",
80
+ "default": "./dist/icons/bulb/bulb-icon.js"
81
+ },
82
+ "./alert": {
83
+ "types": "./dist/icons/alert/alert-icon.d.ts",
84
+ "import": "./dist/icons/alert/alert-icon.js",
85
+ "default": "./dist/icons/alert/alert-icon.js"
86
+ },
87
+ "./badge-check": {
88
+ "types": "./dist/icons/badge-check/badge-check-icon.d.ts",
89
+ "import": "./dist/icons/badge-check/badge-check-icon.js",
90
+ "default": "./dist/icons/badge-check/badge-check-icon.js"
91
+ },
92
+ "./replace": {
93
+ "types": "./dist/icons/replace/replace-icon.d.ts",
94
+ "import": "./dist/icons/replace/replace-icon.js",
95
+ "default": "./dist/icons/replace/replace-icon.js"
96
+ },
97
+ "./delete": {
98
+ "types": "./dist/icons/delete/delete-icon.d.ts",
99
+ "import": "./dist/icons/delete/delete-icon.js",
100
+ "default": "./dist/icons/delete/delete-icon.js"
101
+ },
102
+ "./swap": {
103
+ "types": "./dist/icons/swap/swap-icon.d.ts",
104
+ "import": "./dist/icons/swap/swap-icon.js",
105
+ "default": "./dist/icons/swap/swap-icon.js"
106
+ },
107
+ "./sparkles": {
108
+ "types": "./dist/icons/sparkles/sparkles-icon.d.ts",
109
+ "import": "./dist/icons/sparkles/sparkles-icon.js",
110
+ "default": "./dist/icons/sparkles/sparkles-icon.js"
111
+ },
112
+ "./sparkle": {
113
+ "types": "./dist/icons/sparkle/sparkle-icon.d.ts",
114
+ "import": "./dist/icons/sparkle/sparkle-icon.js",
115
+ "default": "./dist/icons/sparkle/sparkle-icon.js"
116
+ },
117
+ "./star": {
118
+ "types": "./dist/icons/star/star-icon.d.ts",
119
+ "import": "./dist/icons/star/star-icon.js",
120
+ "default": "./dist/icons/star/star-icon.js"
121
+ },
122
+ "./wave": {
123
+ "types": "./dist/icons/wave/wave-icon.d.ts",
124
+ "import": "./dist/icons/wave/wave-icon.js",
125
+ "default": "./dist/icons/wave/wave-icon.js"
126
+ },
127
+ "./package.json": "./package.json",
128
+ "./refresh": {
129
+ "types": "./dist/icons/refresh/refresh-icon.d.ts",
130
+ "import": "./dist/icons/refresh/refresh-icon.js",
131
+ "default": "./dist/icons/refresh/refresh-icon.js"
132
+ },
133
+ "./copy": {
134
+ "types": "./dist/icons/copy/copy-icon.d.ts",
135
+ "import": "./dist/icons/copy/copy-icon.js",
136
+ "default": "./dist/icons/copy/copy-icon.js"
137
+ },
138
+ "./eye": {
139
+ "types": "./dist/icons/eye/eye-icon.d.ts",
140
+ "import": "./dist/icons/eye/eye-icon.js",
141
+ "default": "./dist/icons/eye/eye-icon.js"
142
+ },
143
+ "./link": {
144
+ "types": "./dist/icons/link/link-icon.d.ts",
145
+ "import": "./dist/icons/link/link-icon.js",
146
+ "default": "./dist/icons/link/link-icon.js"
147
+ },
148
+ "./check": {
149
+ "types": "./dist/icons/check/check-icon.d.ts",
150
+ "import": "./dist/icons/check/check-icon.js",
151
+ "default": "./dist/icons/check/check-icon.js"
152
+ },
153
+ "./close": {
154
+ "types": "./dist/icons/close/close-icon.d.ts",
155
+ "import": "./dist/icons/close/close-icon.js",
156
+ "default": "./dist/icons/close/close-icon.js"
157
+ }
63
158
  },
64
159
  "files": [
65
160
  "dist",
@@ -68,8 +163,8 @@
68
163
  "THIRD_PARTY_NOTICES.md"
69
164
  ],
70
165
  "peerDependencies": {
71
- "react": "^19.0.0",
72
- "react-dom": "^19.0.0"
166
+ "react": "^18.3.0 || ^19.0.0",
167
+ "react-dom": "^18.3.0 || ^19.0.0"
73
168
  },
74
169
  "dependencies": {
75
170
  "motion": "^13.2.0"
@@ -1,6 +0,0 @@
1
- import type { LeafColors } from "../../shared/icon-colors.js";
2
- import type { StatefulIconProps } from "../../shared/icon-types.js";
3
- import { type LeafState } from "./leaf-rig.js";
4
- export { leafStates, type LeafState } from "./leaf-rig.js";
5
- export type LeafIconProps = StatefulIconProps<LeafState, LeafColors>;
6
- export declare const LeafIcon: import("react").NamedExoticComponent<LeafIconProps>;
@@ -1,29 +0,0 @@
1
- 'use client';
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { memo, useRef } from 'react';
4
- import { motion, useTransform } from 'motion/react';
5
- import { usePoseWeight, useBehaviorClock } from "../../shared/use-icon-motion.js";
6
- import { useMotionPreference } from "../../shared/use-motion-preference.js";
7
- import { sampleLeaf } from "./leaf-rig.js";
8
- export { leafStates } from "./leaf-rig.js";
9
- const WINDS = ['M2.5 7.2C4.1 6.1 5.8 6.1 7.2 6.5', 'M3 10.3C4 9.7 4.6 9.6 5.3 9.7'];
10
- const LeafWind = memo(function LeafWind({ rig, index, color, width, }) {
11
- const opacity = useTransform(rig, (r) => r.wind[index].opacity);
12
- const offset = useTransform(rig, (r) => r.wind[index].offset);
13
- return (_jsx(motion.path, { "data-part": `wind-${index}`, d: WINDS[index], stroke: color, strokeWidth: width * 0.65, opacity: opacity, strokeDasharray: "3 8", strokeDashoffset: offset }));
14
- });
15
- export const LeafIcon = memo(function LeafIcon({ state = 'resting', size = 24, color = 'currentColor', accentColor = 'currentColor', strokeWidth = 1.8, speed = 1, alive = false, className, colors, }) {
16
- const reduced = useMotionPreference(), element = useRef(null);
17
- const pace = Number.isFinite(speed) ? Math.min(4, Math.max(0.1, speed)) : 1;
18
- const resting = usePoseWeight(state === 'resting', pace, reduced);
19
- const growing = usePoseWeight(state === 'growing', pace, reduced);
20
- const breezy = usePoseWeight(state === 'breezy', pace, reduced);
21
- const wilted = usePoseWeight(state === 'wilted', pace, reduced);
22
- const performs = alive && !reduced;
23
- const life = usePoseWeight(performs, pace, reduced);
24
- const time = useBehaviorClock(element, performs, pace, life);
25
- const rig = useTransform(() => sampleLeaf([resting.get(), growing.get(), breezy.get(), wilted.get()], time.get(), life.get()));
26
- const blade = useTransform(rig, (r) => r.blade), stem = useTransform(rig, (r) => r.stem);
27
- const tint = useTransform(rig, (r) => r.tint), glint = useTransform(rig, (r) => r.glint);
28
- return (_jsxs("svg", { ref: element, className: className, width: size, height: size, viewBox: "0 0 24 24", xmlns: "http://www.w3.org/2000/svg", fill: "none", overflow: "visible", preserveAspectRatio: "xMidYMid meet", "aria-hidden": "true", focusable: "false", pointerEvents: "none", shapeRendering: "geometricPrecision", strokeLinecap: "round", strokeLinejoin: "round", style: { display: 'block', overflow: 'visible' }, children: [_jsxs("g", { "data-layer": "leaf-wind", children: [_jsx(LeafWind, { rig: rig, index: 0, color: accentColor, width: strokeWidth }), _jsx(LeafWind, { rig: rig, index: 1, color: accentColor, width: strokeWidth })] }), _jsxs("g", { "data-layer": "leaf-body", children: [_jsx(motion.path, { "data-part": "blade", d: blade, stroke: colors?.blade ?? color, strokeWidth: strokeWidth, fill: colors?.blade ?? accentColor, fillOpacity: tint }), _jsx(motion.path, { "data-part": "stem", d: stem, stroke: colors?.stem ?? colors?.blade ?? color, strokeWidth: strokeWidth })] }), _jsx(motion.path, { "data-layer": "leaf-glint", d: "M4 3v2M3 4h2", opacity: glint, stroke: accentColor, strokeWidth: strokeWidth * 0.6 })] }));
29
- });
@@ -1,25 +0,0 @@
1
- /** Lucide-informed blade: one closed contour and one connected petiole/midrib. */
2
- export declare const leafStates: readonly ["resting", "growing", "breezy", "wilted"];
3
- export type LeafState = (typeof leafStates)[number];
4
- export type LeafWeights = readonly [number, number, number, number];
5
- type Point = readonly [number, number];
6
- export declare const LEAF_SCENE_SECONDS = 5.2;
7
- export declare const leafBehaviors: {
8
- readonly resting: readonly ["a quiet tip nod", "catch a little light", "soft blade flex"];
9
- readonly growing: readonly ["open toward the light", "stretch, then unfurl the tip", "a small proud lift"];
10
- readonly breezy: readonly ["lean into a passing gust", "tip flutters after the blade", "longer breeze, soft recovery"];
11
- readonly wilted: readonly ["petiole gives a little", "edges curl inward", "a tired tip settles"];
12
- };
13
- export declare function sampleLeaf(weights: LeafWeights, time?: number, alive?: number): {
14
- blade: string;
15
- stem: string;
16
- bladePoints: Point[];
17
- stemPoints: Point[];
18
- tint: number;
19
- wind: {
20
- opacity: number;
21
- offset: number;
22
- }[];
23
- glint: number;
24
- };
25
- export {};