Skip to content

Commit

Permalink
refactor: 💡 more changes
Browse files Browse the repository at this point in the history
more changes

BREAKING CHANGE: 🧨 all

✅ Closes: none
  • Loading branch information
JonathonRP committed Dec 10, 2024
1 parent 44a4c33 commit c0a55b5
Show file tree
Hide file tree
Showing 29 changed files with 531 additions and 408 deletions.
Binary file modified bun.lockb
Binary file not shown.
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,25 +48,25 @@
"@changesets/cli": "^2.27.10",
"@emotion/is-prop-valid": "^1.3.1",
"@sveltejs/adapter-auto": "^3.3.1",
"@sveltejs/kit": "^2.9.0",
"@sveltejs/kit": "^2.9.1",
"@sveltejs/package": "^2.3.7",
"@sveltejs/vite-plugin-svelte": "^5.0.1",
"@tailwindcss/typography": "^0.5.15",
"@tailwindcss/vite": "^4.0.0-beta.5",
"@tailwindcss/vite": "^4.0.0-beta.6",
"@tsconfig/svelte": "^5.0.4",
"@types/node": "^20.17.9",
"@vitest/ui": "latest",
"csstype": "^3.1.3",
"publint": "^0.2.12",
"sv": "^0.5.11",
"svelte-check": "^4.1.1",
"tailwindcss": "^4.0.0-beta.5",
"tailwindcss": "^4.0.0-beta.6",
"typescript": "^5.7.2",
"vite": "latest",
"vitest": "latest"
},
"peerDependencies": {
"svelte": "^5.7.1"
"svelte": "^5.10.0"
},
"engines": {
"bun": ">=1.0.0",
Expand Down
2 changes: 1 addition & 1 deletion src/lib/components/motion/CyclingThroughStates.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import Box from "../Box.svelte";
// Cycling through states
import { motion, useCycle } from "$lib/motion-start";
import { motion, useCycle, useMotionValue } from "$lib/motion-start";
let stateText = "State 1";
// In React
// const [animate, cycle] = useCycle(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,6 @@ export class MainThreadAnimation<T extends string | number> extends BaseAnimatio
}

onPostResolved() {
console.log('post resolve');
const { autoplay = true } = this.options;

this.play();
Expand Down Expand Up @@ -379,7 +378,6 @@ export class MainThreadAnimation<T extends string | number> extends BaseAnimatio
}

play() {
console.log('play', this.isStopped);
if (!this.resolver.isScheduled) {
this.resolver.resume();
}
Expand Down Expand Up @@ -439,7 +437,6 @@ export class MainThreadAnimation<T extends string | number> extends BaseAnimatio
* animation.stop is returned as a reference from a useEffect.
*/
stop = () => {
console.log(new Error().stack?.toString());
this.resolver.cancel();
this.isStopped = true;
if (this.state === 'idle') return;
Expand Down
278 changes: 140 additions & 138 deletions src/lib/motion-start/components/AnimatePresence/AnimatePresence.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ Copyright (c) 2018 Framer B.V. -->
import { fromStore } from "svelte/store";
import { SvelteMap, SvelteSet } from "svelte/reactivity";
import { untrack, type Snippet } from "svelte";
import { type ComponentKey, getChildKey } from "./utils.js";
import { invariant } from "../../utils/errors.js";
type Props = AnimatePresenceProps<ConditionalGeneric<T>> & {
isCustom?: boolean;
children?: Snippet<[ConditionalGeneric<T>]>;
children?: Snippet<[ConditionalGeneric<T> | { key: number }]>;
};
let {
Expand All @@ -29,161 +31,161 @@ Copyright (c) 2018 Framer B.V. -->
children,
}: Props = $props();
invariant(!exitBeforeEnter, "Replace exitBeforeEnter with mode='wait'");
let _list = $state(list !== undefined ? list : show ? [{ key: 1 }] : []);
const presentChildren = $derived(_list);
const presentKeys = $derived(presentChildren.map(getChildKey));
let isInitialRender = $state(true);
let pendingPresentChildren = $state(presentChildren);
let diffedChildren = $state(presentChildren);
let renderedChildren = $state(presentChildren);
let exitComplete = new SvelteMap<ComponentKey, boolean>();
// $inspect(presentChildren);
$effect(() => {
console.log("effect");
list;
isInitialRender = false;
pendingPresentChildren = presentChildren;
/**
* Update complete status of exiting children.
*/
for (let i = 0; i < renderedChildren.length; i++) {
const key = getChildKey(renderedChildren[i]);
if (!presentKeys.includes(key)) {
if (exitComplete.get(key) !== true) {
exitComplete.set(key, false);
}
} else {
exitComplete.delete(key);
}
}
});
const exitingChildren = $state<(ConditionalGeneric<T> | { key: number })[]>(
[],
);
if (presentChildren !== diffedChildren) {
let nextChildren = [...presentChildren];
/**
* Loop through all the currently rendered components and decide which
* are exiting.
*/
for (let i = 0; i < renderedChildren.length; i++) {
const child = renderedChildren[i];
const key = getChildKey(child);
if (!presentKeys.includes(key)) {
nextChildren.splice(i, 0, child);
exitingChildren.push(child);
}
}
/**
* If we're in "wait" mode, and we have exiting children, we want to
* only render these until they've all exited.
*/
if (mode === "wait" && exitingChildren.length) {
nextChildren = exitingChildren;
}
renderedChildren = nextChildren;
diffedChildren = presentChildren;
/**
* Early return to ensure once we've set state with the latest diffed
* children, we can immediately re-render.
*/
// return
}
if (
process.env.NODE_ENV !== "production" &&
mode === "wait" &&
renderedChildren.length > 1
) {
console.warn(
`You're attempting to animate multiple children within AnimatePresence, but its mode is set to "wait". This will lead to odd visual behaviour.`,
);
}
const layoutContext = fromStore(
useContext(LayoutGroupContext, isCustom),
).current;
const forceRender = $derived(() => {
layoutContext.forceRender?.();
_list = [..._list];
});
</script>

function getChildKey(child: { key: number }) {
return child.key || "";
}
{#each renderedChildren.map((child) => {
const key = getChildKey(child);
const isPresent = presentChildren === renderedChildren || presentKeys.includes(key);
return { key, isPresent, item: child, onExit() {
console.log("exit");
if (exitComplete.has(key)) {
exitComplete.set(key, true);
} else {
return;
}

let isInitialRender = $state(true);
const filteredChildren = $derived(_list);
let presentChildren = $state(filteredChildren);
let allChildren = new SvelteMap<string | number, { key: number }>();
let exiting = new SvelteSet<"" | number>();
const updateChildLookup = (
children: { key: number }[],
allChild: Map<string | number, { key: number }>,
) => {
children.forEach((child) => {
const key = getChildKey(child);
allChild.set(key, child);
});
};
$effect(() => {
updateChildLookup(filteredChildren, allChildren);
});
let isEveryExitComplete = true;
exitComplete.forEach((isExitComplete) => {
if (!isExitComplete) isEveryExitComplete = false;
});

let childrenToRender: {
present: boolean;
item: any;
key: any;
onExit: undefined | (() => void);
}[] = $state([
...filteredChildren.map((v) => ({
present: true,
item: v,
key: v.key,
onExit: undefined,
})),
]);
$effect.pre(() => {
if (!isInitialRender) {
// If this is a subsequent render, deal with entering and exiting children
childrenToRender = [
...filteredChildren.map((v) => ({
present: true,
item: v,
key: v.key,
onExit: undefined,
})),
];
// Diff the keys of the currently-present and target children to update our
// exiting list.
const presentKeys = untrack(() => presentChildren).map(getChildKey);
const targetKeys = untrack(() => filteredChildren).map(getChildKey);
// Diff the present children with our target children and mark those that are exiting
const numPresent = presentKeys.length;
for (let i = 0; i < numPresent; i++) {
const key = presentKeys[i];
if (targetKeys.indexOf(key) === -1) {
exiting.add(key);
} else {
// In case this key has re-entered, remove from the exiting list
exiting.delete(key);
}
}
if (isEveryExitComplete) {
forceRender?.();
renderedChildren = pendingPresentChildren;

// If we currently have exiting children, and we're deferring rendering incoming children
// until after all current children have exiting, empty the childrenToRender array
if (exitBeforeEnter && exiting.size) {
childrenToRender = [];
onExitComplete && onExitComplete();
}
// Loop through all currently exiting components and clone them to overwrite `animate`
// with any `exit` prop they might have defined.
exiting.forEach((key) => {
// If this component is actually entering again, early return
if (targetKeys.indexOf(key) !== -1) return;
const child = allChildren.get(key);
if (!child) return;
const insertionIndex = presentKeys.indexOf(key);
const onExit = () => {
allChildren.delete(key);
exiting.delete(key);
// Remove this child from the present children
const removeIndex = presentChildren.findIndex(
(presentChild) => presentChild.key === key,
);
if (removeIndex < 0) {
return;
}
untrack(() => presentChildren).splice(removeIndex, 1);
// Defer re-rendering until all exiting children have indeed left
if (!exiting.size) {
presentChildren = [...filteredChildren];
forceRender?.();
onExitComplete && onExitComplete();
}
};
untrack(() => childrenToRender).splice(insertionIndex, 0, {
present: false,
item: child,
key: getChildKey(child),
onExit,
});
});
// Add `MotionContext` even to children that don't need it to ensure we're rendering
// the same tree between renders
/*
childrenToRender = childrenToRender.map((child) => {
const key = child.key as string | number;
return exiting.has(key) ? (
child
) : (
<PresenceChild
key={getChildKey(child)}
isPresent
presenceAffectsLayout={presenceAffectsLayout}
>
{child}
</PresenceChild>
);
});
*/
presentChildren = untrack(() => childrenToRender);
} else {
isInitialRender = false;
}
});
</script>

{#each childrenToRender as child (getChildKey(child))}
// allChildren.delete(key);
// exiting.delete(key);

// // Remove this child from the present children
// const removeIndex = presentChildren.findIndex(
// (presentChild) => presentChild.key === key,
// );

// if (removeIndex < 0) {
// return;
// }
// untrack(() => presentChildren).splice(removeIndex, 1);

// // Defer re-rendering until all exiting children have indeed left
// if (!exiting.size) {
// presentChildren = [...filteredChildren];
// forceRender?.();
// onExitComplete && onExitComplete();
// }
// };

// untrack(() => childrenToRender).splice(insertionIndex, 0, {
// present: false,
// item: child,
// key: getChildKey(child),
// onExit,
// });
} };
}) as child (getChildKey(child))}
<PresenceChild
isPresent={child.present}
initial={initial ? undefined : false}
custom={child.onExit ? custom : undefined}
isPresent={child.isPresent}
initial={!isInitialRender || initial ? undefined : false}
custom={child.isPresent ? custom : undefined}
{presenceAffectsLayout}
{mode}
onExitComplete={child.onExit}
onExitComplete={child.isPresent ? undefined : child.onExit}
{isCustom}
>
{@render children?.(child.item)}
Expand Down
Loading

0 comments on commit c0a55b5

Please sign in to comment.