feat(ExpanableWrapper): add onResize prop and trigger it in ResizeObserver
This commit is contained in:
@@ -3,6 +3,7 @@
|
|||||||
Animated wrapper for content that can be expanded and collapsed.
|
Animated wrapper for content that can be expanded and collapsed.
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { debounce } from '$shared/lib/utils';
|
||||||
import { cn } from '$shared/shadcn/utils/shadcn-utils';
|
import { cn } from '$shared/shadcn/utils/shadcn-utils';
|
||||||
import type { Snippet } from 'svelte';
|
import type { Snippet } from 'svelte';
|
||||||
import { cubicOut } from 'svelte/easing';
|
import { cubicOut } from 'svelte/easing';
|
||||||
@@ -38,6 +39,10 @@ interface Props extends HTMLAttributes<HTMLDivElement> {
|
|||||||
* Optional badge to render
|
* Optional badge to render
|
||||||
*/
|
*/
|
||||||
badge?: Snippet<[{ expanded?: boolean; disabled?: boolean }]>;
|
badge?: Snippet<[{ expanded?: boolean; disabled?: boolean }]>;
|
||||||
|
/**
|
||||||
|
* Callback for when the element's size changes
|
||||||
|
*/
|
||||||
|
onResize?: (rect: DOMRectReadOnly) => void;
|
||||||
/**
|
/**
|
||||||
* Rotation animation direction
|
* Rotation animation direction
|
||||||
* @default 'clockwise'
|
* @default 'clockwise'
|
||||||
@@ -56,6 +61,7 @@ let {
|
|||||||
visibleContent,
|
visibleContent,
|
||||||
hiddenContent,
|
hiddenContent,
|
||||||
badge,
|
badge,
|
||||||
|
onResize,
|
||||||
rotation = 'clockwise',
|
rotation = 'clockwise',
|
||||||
class: className = '',
|
class: className = '',
|
||||||
containerClassName = '',
|
containerClassName = '',
|
||||||
@@ -64,7 +70,7 @@ let {
|
|||||||
|
|
||||||
let timeoutId = $state<ReturnType<typeof setTimeout> | null>(null);
|
let timeoutId = $state<ReturnType<typeof setTimeout> | null>(null);
|
||||||
|
|
||||||
export const xSpring = new Spring(0, {
|
const xSpring = new Spring(0, {
|
||||||
stiffness: 0.14, // Lower is slower
|
stiffness: 0.14, // Lower is slower
|
||||||
damping: 0.5, // Settle
|
damping: 0.5, // Settle
|
||||||
});
|
});
|
||||||
@@ -79,7 +85,7 @@ const scaleSpring = new Spring(1, {
|
|||||||
damping: 0.65,
|
damping: 0.65,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const rotateSpring = new Spring(0, {
|
const rotateSpring = new Spring(0, {
|
||||||
stiffness: 0.12,
|
stiffness: 0.12,
|
||||||
damping: 0.55,
|
damping: 0.55,
|
||||||
});
|
});
|
||||||
@@ -107,6 +113,9 @@ function handleKeyDown(e: KeyboardEvent) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create debounced recize callback
|
||||||
|
const debouncedResize = debounce((entry: ResizeObserverEntry) => onResize?.(entry.contentRect), 50);
|
||||||
|
|
||||||
// Elevation and scale on activation
|
// Elevation and scale on activation
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if (expanded && !disabled) {
|
if (expanded && !disabled) {
|
||||||
@@ -149,6 +158,21 @@ $effect(() => {
|
|||||||
expanded = false;
|
expanded = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Use an effect to watch the element's actual physical size
|
||||||
|
$effect(() => {
|
||||||
|
if (!element) return;
|
||||||
|
|
||||||
|
const observer = new ResizeObserver(entries => {
|
||||||
|
const entry = entries[0];
|
||||||
|
if (entry) {
|
||||||
|
debouncedResize(entry);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
observer.observe(element);
|
||||||
|
return () => observer.disconnect();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
@@ -158,7 +182,7 @@ $effect(() => {
|
|||||||
role="button"
|
role="button"
|
||||||
tabindex={0}
|
tabindex={0}
|
||||||
class={cn(
|
class={cn(
|
||||||
'will-change-transform duration-300',
|
'will-change-[transform, width, height] duration-300',
|
||||||
disabled ? 'pointer-events-none' : 'pointer-events-auto',
|
disabled ? 'pointer-events-none' : 'pointer-events-auto',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user