From a993fb83f44e0e93e3a4342cca65892b8c43fbdc Mon Sep 17 00:00:00 2001 From: Segun Adebayo Date: Sat, 25 May 2024 21:49:48 +0100 Subject: [PATCH] chore(vanilla): add avatar example --- .changeset/tiny-coats-sniff.md | 5 +++ .xstate/avatar.js | 2 +- examples/vanilla-ts/index.html | 22 ++------- examples/vanilla-ts/pages/accordion.html | 31 +++++++++++++ examples/vanilla-ts/pages/accordion.ts | 9 ++++ examples/vanilla-ts/pages/avatar.html | 24 ++++++++++ examples/vanilla-ts/pages/avatar.ts | 9 ++++ examples/vanilla-ts/src/accordion.ts | 7 +-- examples/vanilla-ts/src/avatar.ts | 45 +++++++++++++++++++ examples/vanilla-ts/src/main.ts | 9 ---- .../machines/avatar/src/avatar.machine.ts | 14 +++--- 11 files changed, 140 insertions(+), 37 deletions(-) create mode 100644 .changeset/tiny-coats-sniff.md create mode 100644 examples/vanilla-ts/pages/accordion.html create mode 100644 examples/vanilla-ts/pages/accordion.ts create mode 100644 examples/vanilla-ts/pages/avatar.html create mode 100644 examples/vanilla-ts/pages/avatar.ts create mode 100644 examples/vanilla-ts/src/avatar.ts delete mode 100644 examples/vanilla-ts/src/main.ts diff --git a/.changeset/tiny-coats-sniff.md b/.changeset/tiny-coats-sniff.md new file mode 100644 index 0000000000..41d90fee70 --- /dev/null +++ b/.changeset/tiny-coats-sniff.md @@ -0,0 +1,5 @@ +--- +"@zag-js/avatar": patch +--- + +Improve image load check to use `naturalWidth|Height` instead of `currentSrc` diff --git a/.xstate/avatar.js b/.xstate/avatar.js index 8206eedfe6..520e66fe83 100644 --- a/.xstate/avatar.js +++ b/.xstate/avatar.js @@ -30,7 +30,7 @@ const fetchMachine = createMachine({ states: { loading: { activities: ["trackSrcChange"], - entry: ["checkImgStatus"], + entry: ["checkImageStatus"], on: { "IMG.LOADED": { target: "loaded", diff --git a/examples/vanilla-ts/index.html b/examples/vanilla-ts/index.html index c629ba4b05..59ebe90aad 100644 --- a/examples/vanilla-ts/index.html +++ b/examples/vanilla-ts/index.html @@ -7,25 +7,9 @@ Vite + TS -

Accordion

+

Vanilla + Zag

-
-
- -
First Content
-
- -
- -
Second Content
-
- -
- -
Third Content
-
-
- - + Accordion + Avatar diff --git a/examples/vanilla-ts/pages/accordion.html b/examples/vanilla-ts/pages/accordion.html new file mode 100644 index 0000000000..6b2642dea7 --- /dev/null +++ b/examples/vanilla-ts/pages/accordion.html @@ -0,0 +1,31 @@ + + + + + + + Vite + TS + + +

Accordion

+ +
+
+ +
First Content
+
+ +
+ +
Second Content
+
+ +
+ +
Third Content
+
+
+ + + + diff --git a/examples/vanilla-ts/pages/accordion.ts b/examples/vanilla-ts/pages/accordion.ts new file mode 100644 index 0000000000..bc27e5e0ba --- /dev/null +++ b/examples/vanilla-ts/pages/accordion.ts @@ -0,0 +1,9 @@ +import "@zag-js/shared/src/style.css" + +import { Accordion } from "../src/accordion" +import { nanoid } from "nanoid" + +document.querySelectorAll(".accordion").forEach((rootEl) => { + const accordion = new Accordion(rootEl, { id: nanoid(), multiple: true }) + accordion.init() +}) diff --git a/examples/vanilla-ts/pages/avatar.html b/examples/vanilla-ts/pages/avatar.html new file mode 100644 index 0000000000..6df126e2ab --- /dev/null +++ b/examples/vanilla-ts/pages/avatar.html @@ -0,0 +1,24 @@ + + + + + + + Vite + TS + + +

Avatar

+ +
+ PA + Naruto +
+ + + + diff --git a/examples/vanilla-ts/pages/avatar.ts b/examples/vanilla-ts/pages/avatar.ts new file mode 100644 index 0000000000..e9e4b7d26c --- /dev/null +++ b/examples/vanilla-ts/pages/avatar.ts @@ -0,0 +1,9 @@ +import "@zag-js/shared/src/style.css" + +import { Avatar } from "../src/avatar" +import { nanoid } from "nanoid" + +document.querySelectorAll(".avatar").forEach((rootEl) => { + const avatar = new Avatar(rootEl, { id: nanoid() }) + avatar.init() +}) diff --git a/examples/vanilla-ts/src/accordion.ts b/examples/vanilla-ts/src/accordion.ts index 4e3d52f720..1133d1cbf8 100644 --- a/examples/vanilla-ts/src/accordion.ts +++ b/examples/vanilla-ts/src/accordion.ts @@ -7,9 +7,7 @@ export class Accordion { service: ReturnType api: accordion.Api - constructor(root: string, context: accordion.Context) { - const rootEl = document.querySelector(root) - + constructor(rootEl: HTMLElement | null, context: accordion.Context) { if (!rootEl) throw new Error("Root element not found") this.rootEl = rootEl @@ -21,6 +19,9 @@ export class Accordion { init = () => { const { service } = this + + this.render() + this.service.subscribe(() => { this.api = accordion.connect(service.state, service.send, normalizeProps) this.render() diff --git a/examples/vanilla-ts/src/avatar.ts b/examples/vanilla-ts/src/avatar.ts new file mode 100644 index 0000000000..0f420c892b --- /dev/null +++ b/examples/vanilla-ts/src/avatar.ts @@ -0,0 +1,45 @@ +import * as avatar from "@zag-js/avatar" +import { normalizeProps } from "./normalize-props" +import { spreadProps } from "./spread-props" + +export class Avatar { + rootEl: HTMLElement + service: ReturnType + api: avatar.Api + + constructor(rootEl: HTMLElement | null, context: avatar.Context) { + if (!rootEl) throw new Error("Root element not found") + this.rootEl = rootEl + + this.service = avatar.machine(context) + this.api = avatar.connect(this.service.state, this.service.send, normalizeProps) + } + + private disposable = new Map() + + init = () => { + const { service } = this + this.render() + this.service.subscribe(() => { + this.api = avatar.connect(service.state, service.send, normalizeProps) + this.render() + }) + + this.service.start() + } + + destroy = () => { + this.service.stop() + } + + render = () => { + const rootEl = this.rootEl + this.disposable.set(rootEl, spreadProps(this.rootEl, this.api.rootProps)) + + const imageEl = rootEl.querySelector(".avatar-image") + if (imageEl) this.disposable.set(imageEl, spreadProps(imageEl, this.api.imageProps)) + + const fallbackEl = rootEl.querySelector(".avatar-fallback") + if (fallbackEl) this.disposable.set(fallbackEl, spreadProps(fallbackEl, this.api.fallbackProps)) + } +} diff --git a/examples/vanilla-ts/src/main.ts b/examples/vanilla-ts/src/main.ts deleted file mode 100644 index f4718dcd64..0000000000 --- a/examples/vanilla-ts/src/main.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Accordion } from "./accordion" -import { nanoid } from "nanoid" - -const accordion = new Accordion(".accordion", { - id: nanoid(), - multiple: true, -}) - -accordion.init() diff --git a/packages/machines/avatar/src/avatar.machine.ts b/packages/machines/avatar/src/avatar.machine.ts index a7a9b89502..d5c0988a41 100644 --- a/packages/machines/avatar/src/avatar.machine.ts +++ b/packages/machines/avatar/src/avatar.machine.ts @@ -26,7 +26,7 @@ export function machine(userContext: UserDefinedContext) { states: { loading: { activities: ["trackSrcChange"], - entry: ["checkImgStatus"], + entry: ["checkImageStatus"], on: { "IMG.LOADED": { target: "loaded", @@ -89,10 +89,10 @@ export function machine(userContext: UserDefinedContext) { invokeOnError(ctx) { ctx.onStatusChange?.({ status: "error" }) }, - checkImgStatus(ctx, _evt, { send }) { - const img = dom.getImageEl(ctx) - if (img?.complete) { - const type = img.currentSrc ? "IMG.LOADED" : "IMG.ERROR" + checkImageStatus(ctx, _evt, { send }) { + const imageEl = dom.getImageEl(ctx) + if (imageEl?.complete) { + const type = hasLoaded(imageEl) ? "IMG.LOADED" : "IMG.ERROR" send({ type, src: "ssr" }) } }, @@ -100,3 +100,7 @@ export function machine(userContext: UserDefinedContext) { }, ) } + +function hasLoaded(image: HTMLImageElement) { + return image.complete && image.naturalWidth !== 0 && image.naturalHeight !== 0 +}