Skip to content

Commit

Permalink
feat(tiny-toast): support all toast positions (`bottom/top * left/cen…
Browse files Browse the repository at this point in the history
…ter/right`) (#96)
  • Loading branch information
hi-ogawa committed Oct 25, 2023
1 parent 0c008b7 commit 59d9b6b
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 48 deletions.
20 changes: 14 additions & 6 deletions packages/tiny-toast/src/common.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import { tinyassert } from "@hiogawa/utils";
import { cls, styleAssign } from "./utils";

// TODO: support all 6 positions
export const TOAST_POSITIONS = ["bottom-left", "top-center"] as const;
export const TOAST_POSITIONS = [
"bottom-left",
"bottom-center",
"bottom-right",
"top-left",
"top-center",
"top-right",
] as const;

export type ToastPosition = (typeof TOAST_POSITIONS)[number];

Expand Down Expand Up @@ -40,6 +46,10 @@ export function slideScaleCollapseTransition({
// steps
// - slide in + scale up + uncollapse
// - slide out + scale down + collapse
const isBottom =
position === "bottom-left" ||
position === "bottom-center" ||
position === "bottom-right";
return {
enterFrom: (el: HTMLElement) => {
tinyassert(el.firstElementChild instanceof HTMLElement);
Expand All @@ -52,8 +62,7 @@ export function slideScaleCollapseTransition({
opacity: "0.5",
transform: cls(
"scale(0.5)",
position === "bottom-left" && "translateY(200%)",
position === "top-center" && "translateY(-200%)"
isBottom ? "translateY(200%)" : "translateY(-200%)"
),
});
},
Expand Down Expand Up @@ -98,8 +107,7 @@ export function slideScaleCollapseTransition({
opacity: "0",
transform: cls(
"scale(0)",
position === "bottom-left" && "translateY(150%)",
position === "top-center" && "translateY(-150%)"
isBottom ? "translateY(150%)" : "translateY(-150%)"
),
});
},
Expand Down
14 changes: 9 additions & 5 deletions packages/tiny-toast/src/tiny-react/api.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,19 @@ export class TinyReactToastManager extends ToastManager<TinyToastData> {
duration: 4000,
};

render() {
const el = document.createElement("div");
el.setAttribute("data-tiny-toast", "");
document.body.appendChild(el);
render(userEl?: HTMLElement) {
const el = userEl ?? document.createElement("div");
if (!userEl) {
el.setAttribute("data-tiny-toast", "");
document.body.appendChild(el);
}
const root = createRoot(el);
root.render(h(ToastContainer, { toast: this }));
return () => {
root.unmount();
el.remove();
if (!userEl) {
el.remove();
}
};
}

Expand Down
50 changes: 42 additions & 8 deletions packages/tiny-toast/src/tiny-react/dev.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createRoot, h } from "@hiogawa/tiny-react";
import { createRoot, h, useCallback } from "@hiogawa/tiny-react";
import { tinyassert } from "@hiogawa/utils";
import { TOAST_POSITIONS } from "../common";
import { TinyReactToastManager } from "./api";
Expand Down Expand Up @@ -76,20 +76,20 @@ function Buttons() {
function Root() {
return h.div(
{
className: "flex p-3",
className: "flex flex-col gap-3 p-3 h-full mx-auto w-full max-w-5xl",
},
h.div(
{ className: "flex flex-col gap-3 border p-3 h-full" },
{ className: "flex flex-col gap-3 border p-3" },
h.div(
{
className: "flex flex-col gap-2",
},
h.span({ className: "text-colorTextSecondary text-sm" }, "Show toast"),
h.span({ className: "text-colorTextSecondary text-sm" }, "Toast type"),
h(Buttons, {})
),
h.label(
{
className: "flex flex-col gap-2",
className: "flex flex-col gap-1.5",
},
h.span({ className: "text-colorTextSecondary text-sm" }, "Position"),
h.select(
Expand All @@ -107,15 +107,49 @@ function Root() {
},
TOAST_POSITIONS.map((value) => h.option({ key: value, value }, value))
)
),
h.label(
{
className: "flex flex-col gap-1.5",
},
h.span(
{ className: "text-colorTextSecondary text-sm" },
"Duration (ms)"
),
h.select(
{
className: "antd-input p-1",
ref(el) {
if (el) {
el.value = String(toast.defaultOptions.duration);
}
},
oninput: (e) => {
toast.defaultOptions.duration = parseFloat(e.currentTarget.value);
},
},
["2000", "4000", "8000", "Infinity"].map((value) =>
h.option({ key: value, value }, value)
)
)
)
),
h.div(
{
className: "flex-1 flex flex-col border",
},
h.div({
// force "position: absolute" in ToastContainer
className: "flex-1 relative overflow-hidden ![&>div]:absolute",
ref: useCallback((el) => {
toast.render(el);
}, []),
})
)
);
}

function main() {
toast.render();
// toast.defaultOptions.position = ""

const el = document.getElementById("root");
tinyassert(el);
el.textContent = "";
Expand Down
76 changes: 47 additions & 29 deletions packages/tiny-toast/src/tiny-react/ui.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
import { TransitionManager } from "@hiogawa/tiny-transition";
import { groupBy, includesGuard } from "@hiogawa/utils";
import {
TOAST_POSITIONS,
TOAST_TYPE_ICONS,
TOAST_TYPE_ICON_COLORS,
slideScaleCollapseTransition,
Expand Down Expand Up @@ -36,38 +37,53 @@ export function ToastContainer({ toast }: { toast: TinyReactToastManager }) {
toast.pause(false);
},
},
h.div(
{
style: istyle({
position: "absolute",
bottom: "0.5rem",
left: "0.75rem",
display: "flex",
flexDirection: "column",
}),
},
itemsByPosition
.get("bottom-left")
?.map((item) => h(ToastAnimation, { key: item.id, toast, item }))
),
h.div(
{
style: istyle({
position: "absolute",
top: "0.5rem",
width: "100%",
display: "flex",
flexDirection: "column-reverse",
alignItems: "center",
}),
},
itemsByPosition
.get("top-center")
?.map((item) => h(ToastAnimation, { key: item.id, toast, item }))
)
TOAST_POSITIONS.map((position) => {
const items = itemsByPosition.get(position);
if (!items) {
return h(Fragment, { key: position });
}
const [y, x] = position.split("-") as ["top", "center"];
return h.div(
{
key: position,
style:
CONTAINER_POSITION_STYLES.base +
CONTAINER_POSITION_STYLES[x] +
CONTAINER_POSITION_STYLES[y],
},
items.map((item) => h(ToastAnimation, { key: item.id, toast, item }))
);
})
);
}

const CONTAINER_POSITION_STYLES = {
base: istyle({
display: "flex",
position: "absolute",
}),
bottom: istyle({
flexDirection: "column",
bottom: "0.5rem",
}),
top: istyle({
flexDirection: "column-reverse",
top: "0.5rem",
}),
left: istyle({
left: "0.75rem",
alignItems: "flex-start",
}),
right: istyle({
right: "0.75rem",
alignItems: "flex-end",
}),
center: istyle({
alignItems: "center",
width: "100%",
}),
};

function ToastAnimation({
toast,
item,
Expand Down Expand Up @@ -132,6 +148,8 @@ function ToastItemComponent({
className: item.data.className,
style:
istyle({
color: "rgba(0, 0, 0, 0.88)",
background: "white",
display: "flex",
alignItems: "center",
padding: "10px 10px",
Expand Down

0 comments on commit 59d9b6b

Please sign in to comment.