diff --git a/packages/ui/src/components/text-shimmer-core.test.ts b/packages/ui/src/components/text-shimmer-core.test.ts new file mode 100644 index 00000000..f9be206a --- /dev/null +++ b/packages/ui/src/components/text-shimmer-core.test.ts @@ -0,0 +1,57 @@ +import { describe, expect, test } from "bun:test" +import { buildTextShimmerStyle } from "./text-shimmer-core" +import { TextShimmerConfig } from "./text-shimmer" +import { TextShimmerV2Config } from "../v2/components/text-shimmer-v2" + +describe("buildTextShimmerStyle", () => { + test("swap/index vars are mapped", () => { + expect(buildTextShimmerStyle("--text-shimmer-swap", + "--text-shimmer-index", 220, 5)).toEqual({ + "--text-shimmer-swap": "220ms", + "--text-shimmer-index": "5", + }) + }) + test("swap/index vars are mapped for v2", () => { + expect(buildTextShimmerStyle("--_swap", "--_index", 220, 0)).toEqual({ + "--_swap": "220ms", + "--_index": "0", + }) + }) + test("helper keys match config", () => { + expect(Object.keys(buildTextShimmerStyle(TextShimmerConfig.swapVar, + TextShimmerConfig.indexVar, 220, 1))).toEqual( + [TextShimmerConfig.swapVar, TextShimmerConfig.indexVar] + ) + expect(Object.keys(buildTextShimmerStyle(TextShimmerV2Config.swapVar, + TextShimmerV2Config.indexVar, 220, 1))).toEqual( + [TextShimmerV2Config.swapVar, TextShimmerV2Config.indexVar] + ) + }) +}) + +describe("Configurations of shimmers", () => { + test("original selectors of v1 is kept", () => { + expect(TextShimmerConfig).toEqual({ + component: "text-shimmer", + charSlot: "text-shimmer-char", + baseSlot: "text-shimmer-char-base", + shimmerSlot: "text-shimmer-char-shimmer", + swapVar: "--text-shimmer-swap", + indexVar: "--text-shimmer-index", + }) + }) + test("original selectos of v2 is kept", () => { + expect(TextShimmerV2Config).toEqual({ + component: "text-shimmer-v2", + charSlot: "text-shimmer-v2-char", + baseSlot: "text-shimmer-v2-base", + shimmerSlot: "text-shimmer-v2-shimmer", + swapVar: "--_swap", + indexVar: "--_index", + }) + }) + test("v1 & v2 are different", () => { + expect(TextShimmerConfig.component).not.toBe(TextShimmerV2Config.component) + expect(TextShimmerConfig.charSlot).not.toBe(TextShimmerV2Config.charSlot) + }) +}) diff --git a/packages/ui/src/components/text-shimmer-core.tsx b/packages/ui/src/components/text-shimmer-core.tsx new file mode 100644 index 00000000..b475258d --- /dev/null +++ b/packages/ui/src/components/text-shimmer-core.tsx @@ -0,0 +1,74 @@ +import { createEffect, createMemo, createSignal, onCleanup, type JSX, type ValidComponent } from "solid-js" +import { Dynamic } from "solid-js/web" + +export type TextShimmerCoreProps = { + text: string + class?: string + as?: T + active?: boolean + offset?: number + component: string + charSlot: string + baseSlot: string + shimmerSlot: string + swapVar: string + indexVar: string +} + +export const TextShimmerCore = ( + props: TextShimmerCoreProps +) => { + const text = createMemo(() => props.text ?? "") + const active = createMemo(() => props.active ?? true) + const offset = createMemo(() => props.offset ?? 0) + const [run, setRun] = createSignal(active()) + const swap = 220 + let timer: ReturnType | undefined + + createEffect(() => { + if (timer) { + clearTimeout(timer) + timer = undefined + } + + if (active()) { + setRun(true) + return + } + + timer = setTimeout(() => { + timer = undefined + setRun(false) + }, swap) + }) + + onCleanup(() => { + if (!timer) return + clearTimeout(timer) + }) + + return ( + + + + + + + ) +} + +export function buildTextShimmerStyle(swapVar: string, indexVar: string, + swap: number, offset: number) { + return {[swapVar]: `${swap}ms`, [indexVar]: `${offset}` } as JSX.CSSProperties +} diff --git a/packages/ui/src/components/text-shimmer.tsx b/packages/ui/src/components/text-shimmer.tsx index 3ab077d9..6b2789a9 100644 --- a/packages/ui/src/components/text-shimmer.tsx +++ b/packages/ui/src/components/text-shimmer.tsx @@ -1,5 +1,14 @@ -import { createEffect, createMemo, createSignal, onCleanup, type ValidComponent } from "solid-js" -import { Dynamic } from "solid-js/web" +import type { ValidComponent } from "solid-js" +import { TextShimmerCore } from "./text-shimmer-core" + +export const TextShimmerConfig = { + component: "text-shimmer", + charSlot: "text-shimmer-char", + baseSlot: "text-shimmer-char-base", + shimmerSlot: "text-shimmer-char-shimmer", + swapVar: "--text-shimmer-swap", + indexVar: "--text-shimmer-index" +} as const export const TextShimmer = (props: { text: string @@ -8,55 +17,5 @@ export const TextShimmer = (props: { active?: boolean offset?: number }) => { - const text = createMemo(() => props.text ?? "") - const active = createMemo(() => props.active ?? true) - const offset = createMemo(() => props.offset ?? 0) - const [run, setRun] = createSignal(active()) - const swap = 220 - let timer: ReturnType | undefined - - createEffect(() => { - if (timer) { - clearTimeout(timer) - timer = undefined - } - - if (active()) { - setRun(true) - return - } - - timer = setTimeout(() => { - timer = undefined - setRun(false) - }, swap) - }) - - onCleanup(() => { - if (!timer) return - clearTimeout(timer) - }) - - return ( - - - - - - - ) + return } diff --git a/packages/ui/src/v2/components/text-shimmer-v2.tsx b/packages/ui/src/v2/components/text-shimmer-v2.tsx index d1860477..184c8a51 100644 --- a/packages/ui/src/v2/components/text-shimmer-v2.tsx +++ b/packages/ui/src/v2/components/text-shimmer-v2.tsx @@ -1,7 +1,16 @@ -import { createEffect, createMemo, createSignal, onCleanup, type ValidComponent } from "solid-js" -import { Dynamic } from "solid-js/web" +import type { ValidComponent } from "solid-js" +import { TextShimmerCore } from "../../components/text-shimmer-core" import "./text-shimmer-v2.css" +export const TextShimmerV2Config = { + component: "text-shimmer-v2", + charSlot: "text-shimmer-v2-char", + baseSlot: "text-shimmer-v2-base", + shimmerSlot: "text-shimmer-v2-shimmer", + swapVar: "--_swap", + indexVar: "--_index" +} as const + export const TextShimmerV2 = (props: { text: string class?: string @@ -9,55 +18,5 @@ export const TextShimmerV2 = (props: { active?: boolean offset?: number }) => { - const text = createMemo(() => props.text ?? "") - const active = createMemo(() => props.active ?? true) - const offset = createMemo(() => props.offset ?? 0) - const [run, setRun] = createSignal(active()) - const swap = 220 - let timer: ReturnType | undefined - - createEffect(() => { - if (timer) { - clearTimeout(timer) - timer = undefined - } - - if (active()) { - setRun(true) - return - } - - timer = setTimeout(() => { - timer = undefined - setRun(false) - }, swap) - }) - - onCleanup(() => { - if (!timer) return - clearTimeout(timer) - }) - - return ( - - - - - - - ) + return }