-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Restore Legacy animatepresence stack with conditional ✅
- Loading branch information
1 parent
6f8cdb6
commit 5714f68
Showing
11 changed files
with
363 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import Root from "./label.svelte"; | ||
|
||
export { | ||
Root, | ||
// | ||
Root as Label, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
<script lang="ts"> | ||
import { Label as LabelPrimitive } from "bits-ui"; | ||
import { cn } from "$lib/utils.js"; | ||
type $$Props = LabelPrimitive.Props; | ||
type $$Events = LabelPrimitive.Events; | ||
let className: $$Props["class"] = undefined; | ||
export { className as class }; | ||
</script> | ||
|
||
<LabelPrimitive.Root | ||
class={cn( | ||
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70", | ||
className | ||
)} | ||
{...$$restProps} | ||
on:mousedown | ||
> | ||
<slot /> | ||
</LabelPrimitive.Root> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import Root from "./switch.svelte"; | ||
|
||
export { | ||
Root, | ||
// | ||
Root as Switch, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
<script lang="ts"> | ||
import { Switch as SwitchPrimitive } from "bits-ui"; | ||
import { cn } from "$lib/utils.js"; | ||
type $$Props = SwitchPrimitive.Props; | ||
type $$Events = SwitchPrimitive.Events; | ||
let className: $$Props["class"] = undefined; | ||
export let checked: $$Props["checked"] = undefined; | ||
export { className as class }; | ||
</script> | ||
|
||
<SwitchPrimitive.Root | ||
bind:checked | ||
class={cn( | ||
"focus-visible:ring-ring focus-visible:ring-offset-background data-[state=checked]:bg-primary data-[state=unchecked]:bg-input peer inline-flex h-[24px] w-[44px] shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50", | ||
className | ||
)} | ||
{...$$restProps} | ||
on:click | ||
on:keydown | ||
> | ||
<SwitchPrimitive.Thumb | ||
class={cn( | ||
"bg-background pointer-events-none block h-5 w-5 rounded-full shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0" | ||
)} | ||
/> | ||
</SwitchPrimitive.Root> |
185 changes: 185 additions & 0 deletions
185
src/lib/motion-start/components/AnimatePresence/AnimatePresenceLegacy.svelte
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,185 @@ | ||
<!-- based on [email protected], | ||
Copyright (c) 2018 Framer B.V. --> | ||
|
||
<script lang="ts" generics="T extends {key:any}"> | ||
import type { ConditionalGeneric, AnimatePresenceProps } from "./index.js"; | ||
import { getContext } from "svelte"; | ||
import PresenceChild from "./PresenceChild/PresenceChild.svelte"; | ||
import { writable, type Writable } from "svelte/store"; | ||
//@ts-expect-error | ||
import { isSharedLayout, SharedLayoutContext } from "./legacies/SharedLayoutContext.js"; | ||
type $$Props = AnimatePresenceProps<ConditionalGeneric<T>>; | ||
export let list: $$Props["list"] = undefined, | ||
custom: $$Props["custom"] = undefined, | ||
initial: $$Props["initial"] = true, | ||
onExitComplete: $$Props["onExitComplete"] = undefined, | ||
exitBeforeEnter: $$Props["exitBeforeEnter"] = undefined, | ||
presenceAffectsLayout = true, | ||
show: $$Props["show"] = undefined, | ||
isCustom = false; | ||
let _list = list !== undefined ? list : show ? [{ key: 1 }] : []; | ||
$: _list = list !== undefined ? list : show ? [{ key: 1 }] : []; | ||
const layoutContext = | ||
getContext<Writable<any>>( | ||
SharedLayoutContext, | ||
) || SharedLayoutContext(isCustom); | ||
$: forceRender = () => { | ||
if (isSharedLayout($layoutContext)) { | ||
$layoutContext.forceUpdate(); | ||
} | ||
_list = [..._list]; | ||
}; | ||
function getChildKey(child: { key: number }) { | ||
return child.key || ""; | ||
} | ||
let isInitialRender = true; | ||
let filteredChildren = _list; | ||
$: filteredChildren = _list; | ||
let presentChildren = filteredChildren; | ||
let allChildren = 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); | ||
allChild.set(key, child); | ||
}); | ||
}; | ||
$: updateChildLookup(filteredChildren, allChildren); | ||
let childrenToRender: { | ||
present: boolean; | ||
item: any; | ||
key: any; | ||
onExit: undefined | (() => void); | ||
}[] = [ | ||
...filteredChildren.map((v) => ({ | ||
present: true, | ||
item: v, | ||
key: v.key, | ||
onExit: undefined, | ||
})), | ||
]; | ||
$: 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 = presentChildren.map(getChildKey); | ||
const targetKeys = 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 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 = []; | ||
} | ||
// 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; | ||
} | ||
presentChildren.splice(removeIndex, 1); | ||
// Defer re-rendering until all exiting children have indeed left | ||
if (!exiting.size) { | ||
presentChildren = [...filteredChildren]; | ||
forceRender(); | ||
onExitComplete && onExitComplete(); | ||
} | ||
}; | ||
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 = childrenToRender; | ||
} else { | ||
isInitialRender = false; | ||
} | ||
</script> | ||
|
||
{#each childrenToRender as child (getChildKey(child))} | ||
<PresenceChild | ||
mode="sync" | ||
isPresent={child.present} | ||
initial={initial ? undefined : false} | ||
custom={child.onExit ? custom : undefined} | ||
{presenceAffectsLayout} | ||
onExitComplete={child.onExit} | ||
{isCustom} | ||
> | ||
<slot item={child.item} /> | ||
</PresenceChild> | ||
{/each} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
27 changes: 27 additions & 0 deletions
27
src/lib/motion-start/components/AnimatePresence/legacies/SharedLayoutContext.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
|
||
//"$lib/motion-start/context/DOMcontext.js"; | ||
import { writable } from "svelte/store"; | ||
export const getDomContext = ( | ||
name, | ||
el | ||
) => { | ||
if (!el || !window) { | ||
return undefined; | ||
} | ||
let par = el; | ||
while ((par = par.parentNode)) { | ||
if (par.motionDomContext && par.motionDomContext.has(name)) { | ||
return par.motionDomContext.get(name); | ||
} | ||
} | ||
return undefined; | ||
}; | ||
import { createBatcher } from "./batcher"; | ||
function SharedLayoutContext(custom) { | ||
return (getDomContext('SharedLayout', custom)) || writable(createBatcher()); | ||
} | ||
function isSharedLayout(context){ | ||
return 'forceUpdate' in context; | ||
} | ||
|
||
export { SharedLayoutContext, isSharedLayout }; |
65 changes: 65 additions & 0 deletions
65
src/lib/motion-start/components/AnimatePresence/legacies/batcher.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
function createBatcher() { | ||
var queue = new Set(); | ||
return { | ||
add: (child) => queue.add(child), | ||
flush: (_a) => { | ||
var _b = _a === void 0 ? defaultHandler : _a, | ||
layoutReady = _b.layoutReady, | ||
parent = _b.parent; | ||
batchLayout((read, write) => { | ||
var order = Array.from(queue).sort(compareByDepth); | ||
var ancestors = parent ? collectProjectingAncestors(parent) : []; | ||
write(() => { | ||
var allElements = [...ancestors, ...order]; | ||
allElements.forEach((element) => element.resetTransform()); | ||
}); | ||
read(() => { | ||
|
||
order.forEach(updateLayoutMeasurement); | ||
}); | ||
write(() => { | ||
ancestors.forEach((element) => element.restoreTransform()); | ||
|
||
order.forEach(layoutReady); | ||
}); | ||
read(() => { | ||
/** | ||
* After all children have started animating, ensure any Entering components are set to Present. | ||
* If we add deferred animations (set up all animations and then start them in two loops) this | ||
* could be moved to the start loop. But it needs to happen after all the animations configs | ||
* are generated in AnimateSharedLayout as this relies on presence data | ||
*/ | ||
order.forEach((child) => { | ||
if (child.isPresent) child.presence = Presence.Present; | ||
}); | ||
}); | ||
write(() => { | ||
/** | ||
* Starting these animations will have queued jobs on the frame loop. In some situations, | ||
* like when removing an element, these will be processed too late after the DOM is manipulated, | ||
* leaving a flash of incorrectly-projected content. By manually flushing these jobs | ||
* we ensure there's no flash. | ||
*/ | ||
flushSync.preRender(); | ||
flushSync.render(); | ||
}); | ||
read(() => { | ||
/** | ||
* Schedule a callback at the end of the following frame to assign the latest projection | ||
* box to the prevViewportBox snapshot. Once global batching is in place this could be run | ||
* synchronously. But for now it ensures that if any nested `AnimateSharedLayout` top-level | ||
* child attempts to calculate its previous relative position against a prevViewportBox | ||
* it will be against its latest projection box instead, as the snapshot is useless beyond this | ||
* render. | ||
*/ | ||
|
||
sync.postRender(() => order.forEach(assignProjectionToSnapshot)); | ||
queue.clear(); | ||
}); | ||
}); | ||
// TODO: Need to find a layout-synchronous way of flushing this | ||
flushLayout(); | ||
}, | ||
}; | ||
} | ||
export { createBatcher }; |
Oops, something went wrong.