Skip to content

Commit

Permalink
better types, align closer to motion/react for children rendering, al…
Browse files Browse the repository at this point in the history
…ign reorder list with svelte, and cleaner ref
  • Loading branch information
JonathonRP committed Feb 5, 2025
1 parent 2ddf8da commit 81e9b89
Show file tree
Hide file tree
Showing 24 changed files with 519 additions and 448 deletions.
8 changes: 8 additions & 0 deletions .changeset/pre.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"mode": "pre",
"tag": "next",
"initialVersions": {
"motion-start": "0.1.0"
},
"changesets": []
}
244 changes: 166 additions & 78 deletions bun.lock

Large diffs are not rendered by default.

32 changes: 16 additions & 16 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,38 +36,38 @@
"bits-ui": "^0.22.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"lucide-svelte": "^0.473.0",
"mode-watcher": "^0.5.0",
"lucide-svelte": "^0.474.0",
"mode-watcher": "^0.5.1",
"nanoid": "^5.0.9",
"tailwind-merge": "^2.6.0",
"tailwind-merge": "^3.0.1",
"tailwind-variants": "^0.3.1"
},
"devDependencies": {
"@biomejs/biome": "^1.9.4",
"@changesets/cli": "^2.27.11",
"@changesets/cli": "^2.27.12",
"@emotion/is-prop-valid": "^1.3.1",
"@sveltejs/adapter-auto": "^4.0.0",
"@sveltejs/kit": "^2.16.1",
"@sveltejs/package": "^2.3.9",
"@sveltejs/kit": "^2.17.1",
"@sveltejs/package": "^2.3.10",
"@sveltejs/vite-plugin-svelte": "^5.0.3",
"@sveltejs/vite-plugin-svelte-inspector": "^4.0.1",
"@tailwindcss/typography": "^0.5.16",
"@tailwindcss/vite": "^4.0.0",
"@tailwindcss/vite": "^4.0.3",
"@tsconfig/svelte": "^5.0.4",
"@types/node": "^20.17.14",
"@vitest/ui": "^2.1.8",
"@types/node": "^20.17.17",
"@vitest/ui": "^2.1.9",
"csstype": "^3.1.3",
"publint": "^0.3.2",
"runed": "^0.23.1",
"sv": "^0.6.14",
"publint": "^0.3.3",
"runed": "^0.23.2",
"sv": "^0.6.18",
"svelte-check": "^4.1.4",
"tailwindcss": "^4.0.0",
"tailwindcss": "^4.0.3",
"typescript": "^5.7.3",
"vite": "^6.0.11",
"vitest": "^2.1.8"
"vite": "^6.1.0",
"vitest": "^2.1.9"
},
"peerDependencies": {
"svelte": "^5.19.2"
"svelte": "^5.19.7"
},
"engines": {
"bun": ">=1.0.0",
Expand Down
2 changes: 0 additions & 2 deletions src/lib/components/motion/Dock/Dock.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
"mx-auto w-max mt-8 h-[58px] p-2 flex gap-2 rounded-2xl border supports-backdrop-blur:bg-white/10 supports-backdrop-blur:dark:bg-black/10 backdrop-blur-md",
);
let dockElement: HTMLDivElement;
let mouseX = Number.POSITIVE_INFINITY;
function handleMouseMove(e: MouseEvent) {
mouseX = e.pageX;
Expand All @@ -39,7 +38,6 @@

<!-- svelte-ignore a11y-no-static-element-interactions -->
<motion.div
bind:ref={dockElement}
onmousemove={(e) => handleMouseMove(e)}
onmouseleave={handleMouseLeave}
class={dockClass}
Expand Down
6 changes: 3 additions & 3 deletions src/lib/components/motion/Dock/DockIcon.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@
let className: string | undefined = "";
export { className as class };
let iconElement: HTMLDivElement;
let iconElement: { current: HTMLDivElement | null } = { current: null };
let distanceCalc = useTransform(mint, (val: number) => {
const bounds = iconElement?.getBoundingClientRect() ?? {
const bounds = iconElement.current?.getBoundingClientRect() ?? {
x: 0,
width: 0,
};
Expand All @@ -44,6 +44,6 @@
);
</script>

<motion.div style={{ width: width }} bind:ref={iconElement} class={iconClass}>
<motion.div style={{ width }} ref={iconElement} class={iconClass}>
<slot></slot>
</motion.div>
8 changes: 5 additions & 3 deletions src/lib/components/motion/ReorderList.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,12 @@
<Reorder.Group
class="list-none p-0 m-0 font-medium text-2xl relative w-[300px]"
axis="y"
onReorder={(newItems) => (initialItems = newItems)}
onReorder={(newItems) => {
initialItems = newItems;
}}
values={initialItems}
>
{#each initialItems as item (item)}
{#snippet children(item)}
<Reorder.Item
class="p-0 m-0 font-medium text-2xl rounded-[5px] mb-[10px] w-full py-[15px] px-[18px] bg-white flex justify-between items-center shrink-0 cursor-grab"
value={item}
Expand All @@ -65,6 +67,6 @@
>
<span>{item}</span>
</Reorder.Item>
{/each}
{/snippet}
</Reorder.Group>
</Box>
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,15 @@ Copyright (c) 2018 Framer B.V. -->
let presentChildren = pendingPresentChildren;
// $: presentChildren = pendingPresentChildren;
let diffedChildren = new SvelteMap<string | number, { key: number }>();
let exiting = new SvelteSet<"" | number>();
let diffedChildren = new Map<string | number, { key: number }>();
let exiting = new Set<"" | number>();
const updateChildLookup = (
children: { key: number }[],
allChild: Map<string | number, { key: number }>,
) => {
children.forEach((child) => {
const key = getChildKey(child);
untrack(() => allChild.set(key, child));
allChild.set(key, child);
});
};
$: updateChildLookup(pendingPresentChildren, diffedChildren);
Expand Down Expand Up @@ -111,10 +111,8 @@ Copyright (c) 2018 Framer B.V. -->
const insertionIndex = presentKeys.indexOf(key);
const onExit = () => {
untrack(() => {
diffedChildren.delete(key);
exiting.delete(key);
});
diffedChildren.delete(key);
exiting.delete(key);
// Remove this child from the present children
const removeIndex = presentChildren.findIndex(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Copyright (c) 2018 Framer B.V. -->
<script module lang="ts">
import { SvelteMap } from "svelte/reactivity";
function newChildrenMap(): Map<string | number, boolean> {
return new SvelteMap<string | number, boolean>();
return new Map<string | number, boolean>();
}
</script>

Expand Down Expand Up @@ -33,36 +33,41 @@ Copyright (c) 2018 Framer B.V. -->
const id = useId();
const memoExitComplete = $derived((childId: string | number) => {
untrack(() => presenceChildren.set(childId, true));
presenceChildren.set(childId, true);
for (const isComplete of presenceChildren.values()) {
if (!isComplete) return;
}
onExitComplete && onExitComplete();
});
const presenceProps = $derived(() => ({
id,
initial,
isPresent,
custom,
onExitComplete: memoExitComplete,
register: (childId: string | number) => {
presenceChildren.set(childId, false);
return () => presenceChildren.delete(childId);
},
}));
const presenceProps = $derived(
(
presence: boolean | number,
onExitComplete: typeof memoExitComplete,
) => ({
id,
initial,
isPresent,
custom,
onExitComplete,
register: (childId: string | number) => {
presenceChildren.set(childId, false);
return () => presenceChildren.delete(childId);
},
}),
);
// FIX: may need to go back to using .current api
const context = $derived.by(() => {
let presence = $state({ current: useContext(PresenceContext) });
return presence;
});
// this is getting called too much?..
$effect(() => {
memoExitComplete;
if (presenceAffectsLayout) {
context.current = presenceProps();
context.current = presenceProps(Math.random(), memoExitComplete);
}
});
Expand All @@ -72,17 +77,13 @@ Copyright (c) 2018 Framer B.V. -->
$effect.pre(() => {
// $inspect.trace();
isPresent;
memoExitComplete;
context.current = presenceProps();
context.current = presenceProps(isPresent, memoExitComplete);
});
// $inspect(isPresent);
$effect(() => {
keyset(isPresent);
tick().then(() => {
!isPresent && !presenceChildren.size && onExitComplete?.();
});
!isPresent && !presenceChildren.size && onExitComplete?.();
});
PresenceContext.Provider = context.current;
Expand Down
34 changes: 19 additions & 15 deletions src/lib/motion-start/components/Reorder/Group.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Copyright (c) 2018 Framer B.V. -->
ReorderContext as ReorderContextProps,
} from "./types";
import { checkReorder } from "./utils/check-reorder";
import type { PropsWithChildren } from "../../utils/types";
type Props<V> = {
/**
Expand Down Expand Up @@ -77,19 +78,21 @@ Copyright (c) 2018 Framer B.V. -->
axis = "y",
onReorder,
values,
ref = $bindable(),
ref: externalRef = $bindable(),
...props
}: Props<V> &
Omit<HTMLMotionProps<any>, "values" | "children"> & {
Omit<HTMLMotionProps<any>, "children"> &
PropsWithChildren<{}, [(typeof values)[number]]> & {
ref?: Ref<SvelteHTMLElements[typeof as]>;
} & { children?: Snippet } = $props();
motion.group = motion[as as keyof typeof motion] as Component<
Omit<HTMLMotionProps<any>, "children"> & {
ref?: Ref<SvelteHTMLElements[typeof as]>;
} & {
children?: Snippet;
}
} = $props();
const ReorderGroup = motion[as as keyof typeof motion] as Component<
PropsWithChildren<
HTMLMotionProps<any> & {
ref?: Ref<SvelteHTMLElements[typeof as]>;
},
[(typeof values)[number]]
>
>;
const order: ItemData<V>[] = [];
let isReordering = $state(false);
Expand Down Expand Up @@ -124,14 +127,15 @@ Copyright (c) 2018 Framer B.V. -->
},
});
$effect(() => {
if (!isReordering) return;
$effect.pre(() => {
isReordering = false;
});
ReorderContext.Provider = context;
</script>

<motion.group {...props} bind:ref ignoreStrict>
{@render children?.()}
</motion.group>
<ReorderGroup {...props} bind:ref={externalRef} ignoreStrict>
{#each values as value, indx (indx)}
{@render children?.(value)}
{/each}
</ReorderGroup>
27 changes: 14 additions & 13 deletions src/lib/motion-start/components/Reorder/Item.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Copyright (c) 2018 Framer B.V. -->
import type { Ref } from "../../utils/safe-react-types";
import type { Box } from "../../projection/geometry/types";
import type { PanInfo } from "../../gestures/pan/PanSession";
import type { PropsWithChildren } from "../../utils/types";
type Props<V> = {
/**
Expand Down Expand Up @@ -56,19 +57,19 @@ Copyright (c) 2018 Framer B.V. -->
as = "li",
onDrag,
layout = true,
ref = $bindable(),
ref: externalRef = $bindable(),
...props
}: Props<V> &
Omit<HTMLMotionProps<any>, "children"> & {
HTMLMotionProps<any> & {
ref?: Ref<SvelteHTMLElements[typeof as]>;
} & { children?: Snippet } = $props();
motion.groupItem = motion[as as keyof typeof motion] as Component<
Omit<HTMLMotionProps<any>, "children"> & {
ref?: Ref<SvelteHTMLElements[typeof as]>;
} & {
children?: Snippet;
}
} & PropsWithChildren<{}> = $props();
const ReorderItem = motion[as as keyof typeof motion] as Component<
PropsWithChildren<
HTMLMotionProps<any> & {
ref?: Ref<SvelteHTMLElements[typeof as]>;
}
>
>;
const context = useContext(ReorderContext);
Expand All @@ -91,7 +92,7 @@ Copyright (c) 2018 Framer B.V. -->
const { axis, registerItem, updateOrder } = $derived(context);
</script>

<motion.groupItem
<ReorderItem
drag={axis}
{...props}
dragSnapToOrigin
Expand All @@ -111,8 +112,8 @@ Copyright (c) 2018 Framer B.V. -->
onDrag && onDrag(event, gesturePoint);
}}
onLayoutMeasure={(measured: Box) => registerItem?.(value, measured)}
bind:ref
bind:ref={externalRef}
ignoreStrict
>
{@render children?.()}
</motion.groupItem>
</ReorderItem>
2 changes: 1 addition & 1 deletion src/lib/motion-start/components/Reorder/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ import Item from './Item.svelte';
export const Reorder = {
Group,
Item,
};
} as { Group: typeof Group; Item: typeof Item };
11 changes: 5 additions & 6 deletions src/lib/motion-start/context/index.svelte.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,25 @@ import { fromStore, toStore, writable, type Writable } from 'svelte/store';

export class Context<T> {
readonly #key: symbol;
#state = $state<RefObject<T>>({ current: null! });
#state = $state<RefObject<T>>({ current: null });

public constructor(initial: T) {
this.#key = Symbol(nanoid());
this.#state = { current: initial };
}

#create(): T {
return setContext(this.#key, this.#state).current;
return setContext(this.#key, this.#state).current!;
}

#get(): T {
return getContext<RefObject<T>>(this.#key).current;
return getContext<RefObject<T>>(this.#key).current!;
}

pipe(...transformers: ((arg?: T) => T)[]) {
// do not change order, hard coded and very specific order for a reason.
return [this.#create, this.#get, ...transformers].reduce<T>(
return [this.#get, ...transformers].reduce<T>(
(value, fn) => fn.call(this, value),
this.#state.current
(!hasContext(this.#key) && this.#create()) || this.#state.current!
);
}

Expand Down
Loading

0 comments on commit 81e9b89

Please sign in to comment.