From 5e4bbe1ff4e1476e9573c61ae97129fec0ff1ec5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20D=C3=B6rbandt?= Date: Thu, 21 Mar 2024 16:57:13 +0100 Subject: [PATCH 1/4] feat(toast): add Toast.useToastTime --- packages/core/src/toast/index.tsx | 3 + .../core/src/toast/toast-progress-fill.tsx | 37 +------- packages/core/src/toast/use-toast-time.ts | 84 +++++++++++++++++++ 3 files changed, 91 insertions(+), 33 deletions(-) create mode 100644 packages/core/src/toast/use-toast-time.ts diff --git a/packages/core/src/toast/index.tsx b/packages/core/src/toast/index.tsx index b2df9d161..1b9ceb6a8 100644 --- a/packages/core/src/toast/index.tsx +++ b/packages/core/src/toast/index.tsx @@ -45,6 +45,7 @@ import { type ToastPromiseState, type ToastSwipeDirection, } from "./types"; +import { type ToastTime, useToastTime } from "./use-toast-time"; export type { ToastCloseButtonOptions, @@ -66,6 +67,7 @@ export type { ToastRootOptions, ToastRootProps, ToastSwipeDirection, + ToastTime, ToastTitleOptions, ToastTitleProps, }; @@ -79,4 +81,5 @@ export { Region, Root, Title, + useToastTime, }; diff --git a/packages/core/src/toast/toast-progress-fill.tsx b/packages/core/src/toast/toast-progress-fill.tsx index d87ca03d6..eb0a2b93c 100644 --- a/packages/core/src/toast/toast-progress-fill.tsx +++ b/packages/core/src/toast/toast-progress-fill.tsx @@ -1,15 +1,8 @@ import { OverrideComponentProps } from "@kobalte/utils"; -import { - JSX, - createEffect, - createSignal, - onCleanup, - splitProps, -} from "solid-js"; +import { JSX, splitProps } from "solid-js"; import { AsChildProp, Polymorphic } from "../polymorphic"; -import { useToastContext } from "./toast-context"; -import { useToastRegionContext } from "./toast-region-context"; +import { useToastTime } from "./use-toast-time"; export interface ToastProgressFillOptions extends AsChildProp { /** The HTML styles attribute (object form only). */ @@ -24,32 +17,10 @@ export interface ToastProgressFillProps * Used to visually show the fill of `Toast.ProgressTrack`. */ export function ToastProgressFill(props: ToastProgressFillProps) { - const rootContext = useToastRegionContext(); - const context = useToastContext(); - const [local, others] = splitProps(props, ["style"]); - const [lifeTime, setLifeTime] = createSignal(100); - let totalElapsedTime = 0; - - createEffect(() => { - if (rootContext.isPaused() || context.isPersistent()) { - return; - } - - const intervalId = setInterval(() => { - const elapsedTime = - new Date().getTime() - context.closeTimerStartTime() + totalElapsedTime; - - const life = Math.trunc(100 - (elapsedTime / context.duration()) * 100); - setLifeTime(life < 0 ? 0 : life); - }); - - onCleanup(() => { - totalElapsedTime += new Date().getTime() - context.closeTimerStartTime(); - clearInterval(intervalId); - }); - }); + const { remainingFraction } = useToastTime(); + const lifeTime = () => Math.trunc(remainingFraction() * 100); return ( ; + /** + * The fraction of this toast's duration it has been open, not accounting for paused time. + * Range: `0` to `1`, increasing over time. + */ + elapsedFraction: Accessor; + /** + * The time this toast will been open before it is closed in milliseconds, not accounting for paused time. + * Range: `0` to `duration` of the toast, decreasing over time. + */ + remainingTime: Accessor; + /** + * The fraction of this toast's duration it will been open before it is closed, not accounting for paused time. + * Range: `0` to `1`, decreasing over time. + */ + remainingFraction: Accessor; +} + +/** + * Compute times and fractions regarding this toast's elapsed and remaining time open. + * + * @example + * + * ```js + * const { + * elapsedTime, + * elapsedFraction, + * remainingTime, + * remainingFraction, + * } = useToastTime(); + * ``` + */ +export function useToastTime(): ToastTime { + const rootContext = useToastRegionContext(); + const context = useToastContext(); + + const [elapsedTime, setElapsedTime] = createSignal(0); + const remainingTime = () => { + const duration = context.duration(); + return clamp(duration - elapsedTime(), 0, duration); + }; + const elapsedFraction = () => clamp(elapsedTime() / context.duration(), 0, 1); + const remainingFraction = () => 1 - elapsedFraction(); + + const timeSinceStart = () => + new Date().getTime() - context.closeTimerStartTime(); + + let totalElapsedTime = 0; + + createEffect(() => { + if (rootContext.isPaused() || context.isPersistent()) { + return; + } + + const intervalId = setInterval(() => { + setElapsedTime(timeSinceStart() + totalElapsedTime); + }); + + onCleanup(() => { + totalElapsedTime += timeSinceStart(); + clearInterval(intervalId); + }); + }); + + return { + elapsedTime, + elapsedFraction, + remainingTime, + remainingFraction, + }; +} From fb2f537db002a0324b7b4e63849a84de4ca35e08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20D=C3=B6rbandt?= Date: Thu, 21 Mar 2024 16:57:41 +0100 Subject: [PATCH 2/4] docs(toast): add API reference for Toast.useToastTime --- apps/docs/src/routes/docs/core/components/toast.mdx | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/apps/docs/src/routes/docs/core/components/toast.mdx b/apps/docs/src/routes/docs/core/components/toast.mdx index db350f57e..954e668b0 100644 --- a/apps/docs/src/routes/docs/core/components/toast.mdx +++ b/apps/docs/src/routes/docs/core/components/toast.mdx @@ -613,6 +613,17 @@ Inside your application, use add your custom region: | --kb-toast-swipe-end-x | The offset end position of the toast after horizontally swiping. | | --kb-toast-swipe-end-y | The offset end position of the toast after vertically swiping. | +### Toast.useToastTime + +The `Toast.useToastTime` primitive returns the following properties. All of the properties ignore paused time. + +| Name | Description | +| :---------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| elapsedTime | `Accessor`
The time this toast has been open in milliseconds.
Range: `0` to `duration` of the toast, increasing over time. | +| elapsedFraction | `Accessor`
The fraction of this toast's duration it has been open.
Range: `0` to `1`, increasing over time. | +| remainingTime | `Accessor`
The time this toast will been open before it is closed in milliseconds.
Range: `0` to `duration` of the toast, decreasing over time. | +| remainingFraction | `Accessor`
The fraction of this toast's duration it will been open before it is closed.
Range: `0` to `1`, decreasing over time. | + ## Rendered elements | Component | Default rendered element | From 70546214ad423e528735f4dff4d959e9292180de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20D=C3=B6rbandt?= Date: Thu, 21 Mar 2024 16:58:34 +0100 Subject: [PATCH 3/4] docs(toast): add example for Toast.useToastTime --- apps/docs/src/examples/toast.tsx | 21 +++++++++++++ .../src/routes/docs/core/components/toast.mdx | 30 ++++++++++++++++++- 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/apps/docs/src/examples/toast.tsx b/apps/docs/src/examples/toast.tsx index 21082ba40..58652b66f 100644 --- a/apps/docs/src/examples/toast.tsx +++ b/apps/docs/src/examples/toast.tsx @@ -114,3 +114,24 @@ export function MultipleRegionsExample() { ); } + +function CustomProgress() { + const { remainingFraction } = Toast.useToastTime(); + return ; +} + +export function CustomProgressExample() { + const showToast = () => { + toaster.show((props) => ( + + + + )); + }; + + return ( + + ); +} diff --git a/apps/docs/src/routes/docs/core/components/toast.mdx b/apps/docs/src/routes/docs/core/components/toast.mdx index 954e668b0..cd66ff5cf 100644 --- a/apps/docs/src/routes/docs/core/components/toast.mdx +++ b/apps/docs/src/routes/docs/core/components/toast.mdx @@ -1,5 +1,9 @@ import { Preview, TabsSnippets, Callout, Kbd } from "../../../../components"; -import { BasicExample, MultipleRegionsExample } from "../../../../examples/toast"; +import { + BasicExample, + CustomProgressExample, + MultipleRegionsExample, +} from "../../../../examples/toast"; # Toast @@ -554,6 +558,30 @@ Inside your application, use add your custom region: +### The `useToastTime` primitive + +`Toast.useToastTime` allows components to access the remaining time usually displayed by `Toast.ProgressFillTrack`. +It should be used within `Toast.Root`, e. g. in a component that replaces `Toast.ProgressTrack` and `Toast.ProgressFill`. + +```tsx +import { Toast, toaster } from "@kobalte/core"; + +function CustomProgress() { + const { remainingFraction } = Toast.useToastTime(); + return ; +} + +toaster.show(props => ( + + + +)); +``` + + + + + ## API reference ### toaster From b21625e8858392c334793087eb0363f97ee1ce3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20D=C3=B6rbandt?= Date: Thu, 21 Mar 2024 17:57:39 +0100 Subject: [PATCH 4/4] docs(toast): show other values in Toast.useToastTime example --- apps/docs/src/examples/toast.tsx | 34 ++++++++++++++--- .../src/routes/docs/core/components/toast.mdx | 38 ++++++++++++++----- 2 files changed, 57 insertions(+), 15 deletions(-) diff --git a/apps/docs/src/examples/toast.tsx b/apps/docs/src/examples/toast.tsx index 58652b66f..6fd7c1e9a 100644 --- a/apps/docs/src/examples/toast.tsx +++ b/apps/docs/src/examples/toast.tsx @@ -1,4 +1,5 @@ import { Toast, toaster } from "@kobalte/core"; +import { Accessor } from "solid-js"; import { CrossIcon } from "../components"; import style from "./toast.module.css"; @@ -115,16 +116,39 @@ export function MultipleRegionsExample() { ); } -function CustomProgress() { - const { remainingFraction } = Toast.useToastTime(); - return ; +function ToastTimeExampleToastBody() { + const { remainingTime, remainingFraction, elapsedTime, elapsedFraction } = + Toast.useToastTime(); + const format = (time: Accessor, fraction: Accessor) => ( + <> + {(time() / 1000).toFixed(1)}s ({Math.round(fraction() * 100)}%) + + ); + return ( + <> +
+
+ + Remaining time: {format(remainingTime, remainingFraction)} + + + Elapsed time: {format(elapsedTime, elapsedFraction)} + +
+ + + +
+ + + ); } -export function CustomProgressExample() { +export function ToastTimeExample() { const showToast = () => { toaster.show((props) => ( - + )); }; diff --git a/apps/docs/src/routes/docs/core/components/toast.mdx b/apps/docs/src/routes/docs/core/components/toast.mdx index cd66ff5cf..f609257a1 100644 --- a/apps/docs/src/routes/docs/core/components/toast.mdx +++ b/apps/docs/src/routes/docs/core/components/toast.mdx @@ -1,9 +1,5 @@ import { Preview, TabsSnippets, Callout, Kbd } from "../../../../components"; -import { - BasicExample, - CustomProgressExample, - MultipleRegionsExample, -} from "../../../../examples/toast"; +import { BasicExample, MultipleRegionsExample, ToastTimeExample } from "../../../../examples/toast"; # Toast @@ -566,20 +562,42 @@ It should be used within `Toast.Root`, e. g. in a component that replaces `Toast ```tsx import { Toast, toaster } from "@kobalte/core"; -function CustomProgress() { - const { remainingFraction } = Toast.useToastTime(); - return ; +function ToastBody() { + const { remainingTime, remainingFraction, elapsedTime, elapsedFraction } = Toast.useToastTime(); + const format = (time: Accessor, fraction: Accessor) => ( + <> + {(time() / 1000).toFixed(1)}s ({Math.round(fraction() * 100)}%) + + ); + return ( + <> +
+
+ + Remaining time: {format(remainingTime, remainingFraction)} + + + Elapsed time: {format(elapsedTime, elapsedFraction)} + +
+ + + +
+ + + ); } toaster.show(props => ( - + )); ``` - + ## API reference