From 4a62f80175d48146fa2a96cf6ce2a11952a3b3c7 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 16 Apr 2026 16:36:39 +0000 Subject: [PATCH 01/35] feat(Image): add headless Image component and useImage composable Introduces a state-driven image loading system: - `useImage` composable: idle/loading/loaded/error state machine with reactive `eager` gate, gated `source`, retry, and src-change reset. - `Image` compound component: Root/Img/Placeholder/Fallback with optional IntersectionObserver-based lazy loading via the `lazy` prop. First-class support for modern image attributes (width/height/decoding/fetchpriority). Refactors `AvatarImage` to use `useImage` internally, exposing `status`, `isLoaded`, `isError`, and `retry` as new slot props. Selection logic and priority-based multi-source behavior are unchanged. https://claude.ai/code/session_01NMjcSYYdX2yniURgEfGrh4 --- .../src/examples/components/image/basic.vue | 26 ++ .../examples/components/image/observer.vue | 35 ++ .../src/examples/components/image/picture.vue | 30 ++ .../examples/composables/use-image/basic.vue | 59 +++ .../examples/composables/use-image/lazy.vue | 47 ++ .../examples/composables/use-image/retry.vue | 41 ++ apps/docs/src/pages/components/index.md | 1 + .../src/pages/components/semantic/avatar.md | 16 + .../src/pages/components/semantic/image.md | 178 ++++++++ apps/docs/src/pages/composables/index.md | 1 + .../src/pages/composables/system/use-image.md | 123 +++++ apps/docs/src/typed-router.d.ts | 39 ++ packages/0/README.md | 2 + .../0/src/components/Avatar/AvatarImage.vue | 51 ++- .../0/src/components/Image/ImageFallback.vue | 73 +++ packages/0/src/components/Image/ImageImg.vue | 154 +++++++ .../src/components/Image/ImagePlaceholder.vue | 77 ++++ packages/0/src/components/Image/ImageRoot.vue | 118 +++++ packages/0/src/components/Image/index.test.ts | 422 ++++++++++++++++++ packages/0/src/components/Image/index.ts | 86 ++++ packages/0/src/components/index.ts | 1 + packages/0/src/composables/index.ts | 1 + .../0/src/composables/useImage/index.test.ts | 214 +++++++++ packages/0/src/composables/useImage/index.ts | 172 +++++++ packages/0/src/maturity.json | 6 +- 25 files changed, 1962 insertions(+), 11 deletions(-) create mode 100644 apps/docs/src/examples/components/image/basic.vue create mode 100644 apps/docs/src/examples/components/image/observer.vue create mode 100644 apps/docs/src/examples/components/image/picture.vue create mode 100644 apps/docs/src/examples/composables/use-image/basic.vue create mode 100644 apps/docs/src/examples/composables/use-image/lazy.vue create mode 100644 apps/docs/src/examples/composables/use-image/retry.vue create mode 100644 apps/docs/src/pages/components/semantic/image.md create mode 100644 apps/docs/src/pages/composables/system/use-image.md create mode 100644 packages/0/src/components/Image/ImageFallback.vue create mode 100644 packages/0/src/components/Image/ImageImg.vue create mode 100644 packages/0/src/components/Image/ImagePlaceholder.vue create mode 100644 packages/0/src/components/Image/ImageRoot.vue create mode 100644 packages/0/src/components/Image/index.test.ts create mode 100644 packages/0/src/components/Image/index.ts create mode 100644 packages/0/src/composables/useImage/index.test.ts create mode 100644 packages/0/src/composables/useImage/index.ts diff --git a/apps/docs/src/examples/components/image/basic.vue b/apps/docs/src/examples/components/image/basic.vue new file mode 100644 index 000000000..55ffbb6bc --- /dev/null +++ b/apps/docs/src/examples/components/image/basic.vue @@ -0,0 +1,26 @@ + + + diff --git a/apps/docs/src/examples/components/image/observer.vue b/apps/docs/src/examples/components/image/observer.vue new file mode 100644 index 000000000..fad8b8eaf --- /dev/null +++ b/apps/docs/src/examples/components/image/observer.vue @@ -0,0 +1,35 @@ + + + diff --git a/apps/docs/src/examples/components/image/picture.vue b/apps/docs/src/examples/components/image/picture.vue new file mode 100644 index 000000000..2bde1be92 --- /dev/null +++ b/apps/docs/src/examples/components/image/picture.vue @@ -0,0 +1,30 @@ +/* eslint-disable vuejs-accessibility/alt-text */ + + + diff --git a/apps/docs/src/examples/composables/use-image/basic.vue b/apps/docs/src/examples/composables/use-image/basic.vue new file mode 100644 index 000000000..90e09c75b --- /dev/null +++ b/apps/docs/src/examples/composables/use-image/basic.vue @@ -0,0 +1,59 @@ +/* eslint-disable vuejs-accessibility/alt-text */ + + + diff --git a/apps/docs/src/examples/composables/use-image/lazy.vue b/apps/docs/src/examples/composables/use-image/lazy.vue new file mode 100644 index 000000000..a20c77b86 --- /dev/null +++ b/apps/docs/src/examples/composables/use-image/lazy.vue @@ -0,0 +1,47 @@ +/* eslint-disable vuejs-accessibility/alt-text */ + + + diff --git a/apps/docs/src/examples/composables/use-image/retry.vue b/apps/docs/src/examples/composables/use-image/retry.vue new file mode 100644 index 000000000..19d6688b7 --- /dev/null +++ b/apps/docs/src/examples/composables/use-image/retry.vue @@ -0,0 +1,41 @@ +/* eslint-disable vuejs-accessibility/alt-text */ + + + diff --git a/apps/docs/src/pages/components/index.md b/apps/docs/src/pages/components/index.md index fe16e97af..9cfad9c13 100644 --- a/apps/docs/src/pages/components/index.md +++ b/apps/docs/src/pages/components/index.md @@ -77,6 +77,7 @@ Components with meaningful HTML defaults. Render semantic elements by default bu | [Avatar](/components/semantic/avatar) | Image/fallback avatar with priority loading | | [Breadcrumbs](/components/semantic/breadcrumbs) | Navigation breadcrumbs with overflow detection and truncation | | [Carousel](/components/semantic/carousel) | Scroll-snap slide navigation with multi-slide display and drag/swipe | +| [Image](/components/semantic/image) | Image with placeholder, error fallback, and lazy loading | | [Pagination](/components/semantic/pagination) | Page navigation with semantic `