Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
582349a
refactor(tiny-transition): "lagged state" interpretion of transition
hi-ogawa Oct 23, 2023
c8014d1
chore: add example
hi-ogawa Oct 23, 2023
6f787af
chore: test "appear: false"
hi-ogawa Oct 23, 2023
39677b0
refactor: add TransitionV2
hi-ogawa Oct 23, 2023
916079e
refactor: minor rename
hi-ogawa Oct 23, 2023
f7ae2e0
refactor: remove options.appear from core
hi-ogawa Oct 23, 2023
41ed536
refactor: rename
hi-ogawa Oct 23, 2023
ea6d16c
fix: fix dts build
hi-ogawa Oct 23, 2023
3abea8a
chore: TransitionV2 example
hi-ogawa Oct 23, 2023
ba36112
refactor: minor
hi-ogawa Oct 23, 2023
8171dc6
refactor: move callback logic to core
hi-ogawa Oct 23, 2023
b88fae7
refactor: support callbacks in useTransitionManager
hi-ogawa Oct 23, 2023
83978ae
chore: comment
hi-ogawa Oct 23, 2023
75df7ac
refactor: simplify "appear"
hi-ogawa Oct 23, 2023
82bc706
fix: tweak force style
hi-ogawa Oct 23, 2023
8ca8678
chore: more example
hi-ogawa Oct 23, 2023
39276b5
chore: "TransitionV2 as Transition" in examples
hi-ogawa Oct 23, 2023
6caa08c
chore: comment
hi-ogawa Oct 24, 2023
a4c4b45
chore: solid example
hi-ogawa Oct 24, 2023
e4a6bce
chore: replace old
hi-ogawa Oct 24, 2023
07294f6
chore: rename
hi-ogawa Oct 24, 2023
6ce6578
chore: comment
hi-ogawa Oct 24, 2023
31140dc
chore: lint
hi-ogawa Oct 24, 2023
ae682a6
chore: remove old
hi-ogawa Oct 25, 2023
4c9020c
chore: changeset
hi-ogawa Oct 25, 2023
9c29907
chore: release
hi-ogawa Oct 25, 2023
66fc3b4
chore: readme
hi-ogawa Oct 25, 2023
e9c8afd
chore: add (broken) solidjs slide example
hi-ogawa Oct 25, 2023
2c29e0c
fix: workaround solidjs `ref/style/class` ordering
hi-ogawa Oct 25, 2023
5d48181
fix: fix solidjs popover
hi-ogawa Oct 25, 2023
216d979
chore: readme
hi-ogawa Oct 25, 2023
64dcd36
chore: release
hi-ogawa Oct 25, 2023
f967800
chore: comment
hi-ogawa Oct 25, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/ten-terms-brake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@hiogawa/tiny-transition": patch
---

refactor: "lagged boolean state" interpretation of transition
52 changes: 30 additions & 22 deletions packages/app-solid/src/components/transition.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,51 +8,59 @@ import {
type JSX,
Show,
createEffect,
createMemo,
createSignal,
onCleanup,
untrack,
} from "solid-js";

export function Transition(
// prettier-ignore
props: {
show: boolean;
appear?: boolean;
} & TransitionClassProps &
TransitionCallbackProps &
Pick<JSX.HTMLElementTags["div"], "class" | "children" | "style">
} & TransitionClassProps
& TransitionCallbackProps
& Pick<JSX.HTMLElementTags["div"], "class" | "children" | "style">
) {
const callbackOptions = createMemo(() =>
convertClassPropsToCallbackProps(props.class, props)
const manager = untrack(
() =>
new TransitionManager(
props.appear ? false : props.show,
convertClassPropsToCallbackProps(props.class, props)
)
);

const manager = new TransitionManager(
untrack(() => ({
defaultEntered: props.show && !props.appear,
...callbackOptions(),
}))
);

const [shouldRender, setShouldRender] = createSignal(manager.shouldRender());
const [state, setState] = createSignal(manager.state);

createEffect(() => {
const unsubscribe = manager.subscribe(() => {
setShouldRender(manager.shouldRender());
setState(manager.state);
});
onCleanup(() => unsubscribe());
});

createEffect(() => {
manager.show(props.show ?? false);
manager.set(props.show);
});

createEffect(() => {
Object.assign(manager.options, callbackOptions());
});
let workaroundNoAppear = manager.state && !props.appear;

return (
<Show when={shouldRender()}>
<div ref={(el) => manager.setElement(el)} style={props.style}>
<Show when={state()}>
<div
// TODO: solidjs runs `ref` before assigning `class` which breaks transition?
// for some reason, `style` works fine and this is required for popover currently.
// class={props.class}
style={props.style}
ref={(el) => {
manager.ref(el);
// since we cannot use `class` as explained above,
// we workaround `show=true appear=false` by manual callback calls on mount
if (workaroundNoAppear) {
workaroundNoAppear = false;
manager.callbacks?.onEntered?.(el);
}
}}
>
{props.children}
</div>
</Show>
Expand Down
32 changes: 21 additions & 11 deletions packages/app-solid/src/stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,28 +23,38 @@ export function StoryForm() {
}

export function StoryTransition() {
const [show, setShow] = createSignal(false);
const [show, setShow] = createSignal(true);

return (
<div class="flex flex-col gap-3 p-2 border w-sm">
<h1 class="text-xl">Transition</h1>
<h1 class="text-xl">Transition / Slide</h1>
<button
class="antd-btn antd-btn-primary p-1"
onClick={() => setShow(!show())}
>
Toggle ({show() ? "on" : "off"})
{show() ? "Hide" : "Show"}
</button>
<div class="h-[80px] flex items-center justify-center relative">
<div class="text-lg">Hello</div>
<div class="border h-[100px] overflow-hidden relative">
<Transition
appear
show={show()}
class="absolute top-2 right-2 inline-block duration-500 transform"
enterFrom="translate-y-[-200%]"
enterTo="translate-y-0"
leaveFrom="translate-y-0"
leaveTo="translate-y-[-200%]"
>
<span class="border px-2 py-1">top/right</span>
</Transition>
<Transition
show={show()}
class="absolute inset-0 antd-body flex items-center justify-center duration-1000"
enterFrom="opacity-0"
enterTo="opacity-80"
leaveFrom="opacity-80"
leaveTo="opacity-0"
class="absolute bottom-2 left-2 inline-block duration-500 transform"
enterFrom="translate-x-[-200%]"
enterTo="translate-x-0"
leaveFrom="translate-x-0"
leaveTo="translate-x-[-200%]"
>
<div class="antd-spin w-8 h-8"></div>
<span class="border px-2 py-1">bottom/left (appear = false)</span>
</Transition>
</div>
</div>
Expand Down
40 changes: 36 additions & 4 deletions packages/app/src/components/stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ import { useTinyForm } from "@hiogawa/tiny-form/dist/react";
import { useTinyProgress } from "@hiogawa/tiny-progress/dist/react";
import { useTinyStoreStorage } from "@hiogawa/tiny-store/dist/react";
import { TOAST_POSITIONS, type ToastPosition } from "@hiogawa/tiny-toast";
import { Transition } from "@hiogawa/tiny-transition/dist/react";
import {
Transition,
useTransitionManager,
} from "@hiogawa/tiny-transition/dist/react";
import { ANTD_VARS } from "@hiogawa/unocss-preset-antd";
import { none, objectKeys, objectPickBy, range } from "@hiogawa/utils";
import { Debug, toSetSetState, useDelay } from "@hiogawa/utils-react";
Expand Down Expand Up @@ -484,6 +487,7 @@ export function StoryModal() {

export function StorySlide() {
const [show, setShow] = React.useState(true);
const manager = useTransitionManager(show, { appear: true });

return (
<div className="flex flex-col items-center gap-3 m-2">
Expand All @@ -505,7 +509,7 @@ export function StorySlide() {
leaveFrom="translate-y-0"
leaveTo="translate-y-[-200%]"
>
<span className="border px-2 py-1">hello from top/right</span>
<span className="border px-2 py-1">top/right</span>
</Transition>
<Transition
appear
Expand All @@ -516,8 +520,32 @@ export function StorySlide() {
leaveFrom="translate-x-0"
leaveTo="translate-x-[-200%]"
>
<span className="border px-2 py-1">hello from bottom/left</span>
<span className="border px-2 py-1">bottom/left</span>
</Transition>
<Transition
show={show}
className="absolute top-2 left-2 inline-block duration-1500 transform"
enterFrom="translate-y-[-200%]"
enterTo="translate-y-0"
leaveFrom="translate-y-0"
leaveTo="translate-y-[-200%]"
>
<span className="border px-2 py-1">top/left (appear = false)</span>
</Transition>
{manager.state && (
<div
ref={manager.ref}
className={cls(
"absolute bottom-2 right-2 inline-block duration-1000 transform",
manager.state === "enterFrom" && "translate-y-[200%]",
manager.state === "enterTo" && "translate-y-0",
manager.state === "leaveFrom" && "translate-y-0",
manager.state === "leaveTo" && "translate-y-[200%]"
)}
>
<span className="border px-2 py-1">bottom/right</span>
</div>
)}
</div>
</section>
</div>
Expand Down Expand Up @@ -545,7 +573,11 @@ export function StoryCollapse() {
className="duration-500 overflow-hidden"
{...getCollapseProps()}
>
<div className="pt-3">Collapsable Div</div>
<div className="pt-3 flex flex-col gap-3">
<div>Collapsable Div</div>
<div>Collapsable Div</div>
<div>Collapsable Div</div>
</div>
</Transition>
</div>
</section>
Expand Down
10 changes: 4 additions & 6 deletions packages/tiny-toast/src/preact/ui.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,32 +79,30 @@ function ToastAnimation({
const transition = slideScaleCollapseTransition({
position: item.data.position,
});
const manager = new TransitionManager({
defaultEntered: false,
return new TransitionManager(false, {
onEnterFrom: transition.enterFrom,
onEnterTo: transition.enterTo,
onEntered: transition.entered,
onLeaveFrom: transition.leaveFrom,
onLeaveTo: transition.leaveTo,
onLeft: () => toast.remove(item.id),
});
return manager;
});

useSubscribe(manager.subscribe);

useEffect(() => {
manager.show(item.step < TOAST_STEP.DISMISS);
manager.set(item.step < TOAST_STEP.DISMISS);
}, [item.step]);

if (!manager.shouldRender()) {
if (!manager.state) {
return null;
}

return h(
"div",
{
ref: manager.setElement,
ref: manager.ref,
style: istyle({
pointerEvents: "auto",
}),
Expand Down
16 changes: 7 additions & 9 deletions packages/tiny-toast/src/tiny-react/ui.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,35 +65,33 @@ function ToastAnimation({
const transition = slideScaleCollapseTransition({
position: "top-center",
});
const manager = new TransitionManager({
defaultEntered: false,
return new TransitionManager(false, {
onEnterFrom: transition.enterFrom,
onEnterTo: transition.enterTo,
onEntered: transition.entered,
onLeaveFrom: transition.leaveFrom,
onLeaveTo: transition.leaveTo,
onLeft: () => toast.remove(item.id),
});
return manager;
});

const shouldRender = useSyncExternalStore(
const state = useSyncExternalStore(
manager.subscribe,
manager.shouldRender,
manager.shouldRender
() => manager.state,
() => manager.state
);

useEffect(() => {
manager.show(item.step < TOAST_STEP.DISMISS);
manager.set(item.step < TOAST_STEP.DISMISS);
}, [item.step]);

if (!shouldRender) {
if (!state) {
return h(Fragment, {});
}

return h.div(
{
ref: manager.setElement,
ref: manager.ref,
},
h.div(
{
Expand Down
16 changes: 14 additions & 2 deletions packages/tiny-transition/README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,21 @@
# tiny-transition

Rendering framework agnostic transition/animation utility.
Simple framework-agnostic transition-based animation utility.
API is similar to [headlessui's `Transition`](https://headlessui.com/react/transition).

## examples

- https://unocss-preset-antd-hiro18181.vercel.app/Slide
- https://unocss-preset-antd-hiro18181.vercel.app/Collapse
- https://unocss-preset-antd-hiro18181.vercel.app/Toast
- https://unocss-preset-antd-hiro18181.vercel.app/Modal
- https://unocss-preset-antd-hiro18181.vercel.app/Popover
- https://unocss-preset-antd-solidjs-hiro18181.vercel.app/Transition
- https://unocss-preset-antd-solidjs-hiro18181.vercel.app/Drawer
- https://unocss-preset-antd-solidjs-hiro18181.vercel.app/Popover

## limitation compared to `@headlessui/react`

- always `remount`
- no `Transition.Child`
- can workaround by setting same animation duration for all components
- this can be mostly workaround by setting same animation duration for all components
4 changes: 1 addition & 3 deletions packages/tiny-transition/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@hiogawa/tiny-transition",
"version": "0.0.1-pre.3",
"version": "0.0.1-pre.5",
"homepage": "https://github.com/hi-ogawa/unocss-preset-antd/tree/main/packages/tiny-transition",
"repository": {
"type": "git",
Expand Down Expand Up @@ -33,8 +33,6 @@
"release": "pnpm publish --no-git-checks --access public"
},
"devDependencies": {
"@hiogawa/utils": "1.6.1-pre.7",
"@hiogawa/utils-react": "^1.3.1-pre.0",
"@types/react": "^18.2.14",
"react": "^18.2.0"
},
Expand Down
3 changes: 1 addition & 2 deletions packages/tiny-transition/src/class.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { TransitionCallbackProps } from "./core";

// helper to use css class as a callback
// helper to use css class for callbacks

export const TRANSITION_CLASS_TYPES = [
"enter",
Expand All @@ -20,7 +20,6 @@ export function convertClassPropsToCallbackProps(
base: string | undefined,
props: TransitionClassProps & TransitionCallbackProps
): TransitionCallbackProps {
// TODO: render `onEntered` classes already during ssr when `show=true appear=false`?
const cl = {
base: splitClass(base ?? ""),
enter: splitClass(props.enter ?? ""),
Expand Down
Loading