atagen

malice

public
Star0
Watch0
9090eb8cdf5f6e853fd8a08b68b8d5fcc31b062a
2
ssh://brewery@pls.lobotomise.me:33/atagen/malice.git

Blame: web/src/lib/motion.ts

d07bca5da4aeatagen2026-10-26 03:48 UTC1
import { cubicOut } from 'svelte/easing';
d07bca5da4aeatagen2026-10-26 03:48 UTC2
import { fade, slide, type TransitionConfig } from 'svelte/transition';
d07bca5da4aeatagen2026-10-26 03:48 UTC3
d07bca5da4aeatagen2026-10-26 03:48 UTC4
const entering = new Set<string>();
d07bca5da4aeatagen2026-10-26 03:48 UTC5
const leaving = new Set<string>();
d07bca5da4aeatagen2026-10-26 03:48 UTC6
let busyUntil = 0;
d07bca5da4aeatagen2026-10-26 03:48 UTC7
d07bca5da4aeatagen2026-10-26 03:48 UTC8
export function motionDuration(ms: number): number {
d07bca5da4aeatagen2026-10-26 03:48 UTC9
  return typeof window !== 'undefined' && window.matchMedia('(prefers-reduced-motion: reduce)').matches ? 0 : ms;
d07bca5da4aeatagen2026-10-26 03:48 UTC10
}
d07bca5da4aeatagen2026-10-26 03:48 UTC11
d07bca5da4aeatagen2026-10-26 03:48 UTC12
export function motionActive(): boolean {
d07bca5da4aeatagen2026-10-26 03:48 UTC13
  return performance.now() < busyUntil;
d07bca5da4aeatagen2026-10-26 03:48 UTC14
}
d07bca5da4aeatagen2026-10-26 03:48 UTC15
d07bca5da4aeatagen2026-10-26 03:48 UTC16
function noteBusy(ms: number): void {
d07bca5da4aeatagen2026-10-26 03:48 UTC17
  busyUntil = Math.max(busyUntil, performance.now() + ms + 60);
d07bca5da4aeatagen2026-10-26 03:48 UTC18
}
d07bca5da4aeatagen2026-10-26 03:48 UTC19
d07bca5da4aeatagen2026-10-26 03:48 UTC20
export function setRowMotion(enter: Iterable<string>, leave: Iterable<string>): void {
d07bca5da4aeatagen2026-10-26 03:48 UTC21
  entering.clear();
d07bca5da4aeatagen2026-10-26 03:48 UTC22
  leaving.clear();
d07bca5da4aeatagen2026-10-26 03:48 UTC23
  for (const key of enter) entering.add(key);
d07bca5da4aeatagen2026-10-26 03:48 UTC24
  for (const key of leave) leaving.add(key);
d07bca5da4aeatagen2026-10-26 03:48 UTC25
}
d07bca5da4aeatagen2026-10-26 03:48 UTC26
d07bca5da4aeatagen2026-10-26 03:48 UTC27
function rowKey(node: Element): string {
d07bca5da4aeatagen2026-10-26 03:48 UTC28
  const { item, row } = (node as HTMLElement).dataset;
d07bca5da4aeatagen2026-10-26 03:48 UTC29
  return item ?? row ?? '';
d07bca5da4aeatagen2026-10-26 03:48 UTC30
}
d07bca5da4aeatagen2026-10-26 03:48 UTC31
d07bca5da4aeatagen2026-10-26 03:48 UTC32
function reveal(node: Element, ms: number, axis: 'x' | 'y' = 'y'): TransitionConfig {
d07bca5da4aeatagen2026-10-26 03:48 UTC33
  const duration = motionDuration(ms);
d07bca5da4aeatagen2026-10-26 03:48 UTC34
  if (duration === 0) return { duration: 0 };
d07bca5da4aeatagen2026-10-26 03:48 UTC35
  noteBusy(duration);
d07bca5da4aeatagen2026-10-26 03:48 UTC36
  const base = slide(node, { duration, easing: cubicOut, axis });
20fe182f3204atagen2026-10-26 05:50 UTC37
  return { duration, easing: cubicOut, css: (t, u) => `${base.css?.(t, u) ?? ''};opacity: ${t};` };
d07bca5da4aeatagen2026-10-26 03:48 UTC38
}
d07bca5da4aeatagen2026-10-26 03:48 UTC39
d07bca5da4aeatagen2026-10-26 03:48 UTC40
export function rowIn(node: Element): TransitionConfig {
d07bca5da4aeatagen2026-10-26 03:48 UTC41
  return entering.has(rowKey(node)) ? reveal(node, 200) : { duration: 0 };
d07bca5da4aeatagen2026-10-26 03:48 UTC42
}
d07bca5da4aeatagen2026-10-26 03:48 UTC43
d07bca5da4aeatagen2026-10-26 03:48 UTC44
export function rowOut(node: Element): TransitionConfig {
d07bca5da4aeatagen2026-10-26 03:48 UTC45
  return leaving.has(rowKey(node)) ? reveal(node, 160) : { duration: 0 };
d07bca5da4aeatagen2026-10-26 03:48 UTC46
}
d07bca5da4aeatagen2026-10-26 03:48 UTC47
d07bca5da4aeatagen2026-10-26 03:48 UTC48
export function expand(node: Element, params: { axis?: 'x' | 'y' } = {}): TransitionConfig {
d07bca5da4aeatagen2026-10-26 03:48 UTC49
  return reveal(node, 170, params.axis);
d07bca5da4aeatagen2026-10-26 03:48 UTC50
}
d07bca5da4aeatagen2026-10-26 03:48 UTC51
d07bca5da4aeatagen2026-10-26 03:48 UTC52
export function dissolve(node: Element): TransitionConfig {
d07bca5da4aeatagen2026-10-26 03:48 UTC53
  const duration = motionDuration(150);
d07bca5da4aeatagen2026-10-26 03:48 UTC54
  if (duration > 0) noteBusy(duration);
d07bca5da4aeatagen2026-10-26 03:48 UTC55
  return fade(node, { duration });
d07bca5da4aeatagen2026-10-26 03:48 UTC56
}
d07bca5da4aeatagen2026-10-26 03:48 UTC57
d07bca5da4aeatagen2026-10-26 03:48 UTC58
export function settleHeight(node: HTMLElement, from: number): void {
d07bca5da4aeatagen2026-10-26 03:48 UTC59
  const duration = motionDuration(180);
d07bca5da4aeatagen2026-10-26 03:48 UTC60
  const to = node.offsetHeight;
d07bca5da4aeatagen2026-10-26 03:48 UTC61
  if (duration === 0 || Math.abs(to - from) < 2 || node.getAnimations({ subtree: true }).length > 0) return;
d07bca5da4aeatagen2026-10-26 03:48 UTC62
  noteBusy(duration);
d07bca5da4aeatagen2026-10-26 03:48 UTC63
  node.animate(
d07bca5da4aeatagen2026-10-26 03:48 UTC64
    [{ height: `${from}px`, overflow: 'hidden' }, { height: `${to}px`, overflow: 'hidden' }],
d07bca5da4aeatagen2026-10-26 03:48 UTC65
    { duration, easing: 'cubic-bezier(0.2, 0.8, 0.2, 1)' }
d07bca5da4aeatagen2026-10-26 03:48 UTC66
  );
d07bca5da4aeatagen2026-10-26 03:48 UTC67
}