From e76c2dbef33b4e0140979e462a0571ee35ec9f84 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 16 Jan 2026 02:43:00 +0000 Subject: [PATCH 1/2] feat(components): add Lazy component for viewport-based deferred rendering Combines useIntersectionObserver + createSelection + useProxyRegistry to provide: - LazyRoot: detects viewport intersection, manages selection via events - LazyPlaceholder: shows before intersection - LazyContent: shows after intersection Supports eager mode and IntersectionObserver options (rootMargin, threshold). --- .../0/src/components/Lazy/LazyContent.vue | 64 +++++++++ .../0/src/components/Lazy/LazyPlaceholder.vue | 64 +++++++++ packages/0/src/components/Lazy/LazyRoot.vue | 128 ++++++++++++++++++ packages/0/src/components/Lazy/index.ts | 90 ++++++++++++ packages/0/src/components/index.ts | 1 + 5 files changed, 347 insertions(+) create mode 100644 packages/0/src/components/Lazy/LazyContent.vue create mode 100644 packages/0/src/components/Lazy/LazyPlaceholder.vue create mode 100644 packages/0/src/components/Lazy/LazyRoot.vue create mode 100644 packages/0/src/components/Lazy/index.ts diff --git a/packages/0/src/components/Lazy/LazyContent.vue b/packages/0/src/components/Lazy/LazyContent.vue new file mode 100644 index 000000000..40b3ad787 --- /dev/null +++ b/packages/0/src/components/Lazy/LazyContent.vue @@ -0,0 +1,64 @@ +/** + * @module LazyContent + * + * @remarks + * Content component shown after the element intersects the viewport. + * Registers with the Lazy context and displays when content is loaded. + */ + + + + + + diff --git a/packages/0/src/components/Lazy/LazyPlaceholder.vue b/packages/0/src/components/Lazy/LazyPlaceholder.vue new file mode 100644 index 000000000..0a1a97040 --- /dev/null +++ b/packages/0/src/components/Lazy/LazyPlaceholder.vue @@ -0,0 +1,64 @@ +/** + * @module LazyPlaceholder + * + * @remarks + * Placeholder component shown before content intersects the viewport. + * Registers with the Lazy context and displays when content is not yet loaded. + */ + + + + + + diff --git a/packages/0/src/components/Lazy/LazyRoot.vue b/packages/0/src/components/Lazy/LazyRoot.vue new file mode 100644 index 000000000..930e0ad75 --- /dev/null +++ b/packages/0/src/components/Lazy/LazyRoot.vue @@ -0,0 +1,128 @@ +/** + * @module LazyRoot + * + * @remarks + * Root component for deferred content rendering. Uses IntersectionObserver + * to detect when the element enters the viewport and switches from + * placeholder to content. Uses selection internally to ensure one + * child is always visible. + */ + + + + + + diff --git a/packages/0/src/components/Lazy/index.ts b/packages/0/src/components/Lazy/index.ts new file mode 100644 index 000000000..15de9c8fa --- /dev/null +++ b/packages/0/src/components/Lazy/index.ts @@ -0,0 +1,90 @@ +export { default as LazyContent } from './LazyContent.vue' +export { default as LazyPlaceholder } from './LazyPlaceholder.vue' +export { provideLazyRoot, useLazyRoot } from './LazyRoot.vue' +export { default as LazyRoot } from './LazyRoot.vue' + +export type { LazyContentProps, LazyContentSlotProps } from './LazyContent.vue' +export type { LazyPlaceholderProps, LazyPlaceholderSlotProps } from './LazyPlaceholder.vue' +export type { LazyRootContext, LazyRootProps, LazyRootSlotProps, LazyTicket } from './LazyRoot.vue' + +// Components +import Content from './LazyContent.vue' +import Placeholder from './LazyPlaceholder.vue' +import Root from './LazyRoot.vue' + +/** + * Lazy component for deferred content rendering based on viewport intersection. + * + * @see https://0.vuetifyjs.com/components/lazy + * + * @example + * ```vue + * + * + * + * ``` + * + * @example + * Eager mode (render content immediately): + * ```vue + * + * ``` + * + * @example + * With root margin for preloading: + * ```vue + * + * ``` + */ +export const Lazy = { + /** + * Root component for lazy loading contexts. + * + * @see https://0.vuetifyjs.com/components/lazy#lazyroot + */ + Root, + /** + * Placeholder component shown before content intersects viewport. + * + * @see https://0.vuetifyjs.com/components/lazy#lazyplaceholder + */ + Placeholder, + /** + * Content component shown after viewport intersection. + * + * @see https://0.vuetifyjs.com/components/lazy#lazycontent + */ + Content, +} diff --git a/packages/0/src/components/index.ts b/packages/0/src/components/index.ts index 97e7849cc..cf4deab6c 100644 --- a/packages/0/src/components/index.ts +++ b/packages/0/src/components/index.ts @@ -4,6 +4,7 @@ export * from './Checkbox' export * from './Dialog' export * from './ExpansionPanel' export * from './Group' +export * from './Lazy' export * from './Pagination' export * from './Popover' export * from './Radio' From 693976679bdd3da84b47283648ced5080e77d5ad Mon Sep 17 00:00:00 2001 From: John Leider Date: Fri, 16 Jan 2026 13:53:27 -0600 Subject: [PATCH 2/2] fix(components): improve Lazy component patterns and add tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Use toRef() instead of computed() for slot props (consistency) - Normalize slot prop naming (isVisible → hasContent) - Expose reset/onAfterLeave in LazyRoot slot props - Export LazyContext type from barrel - Simplify LazyRootContext to type alias - Add accessibility attributes to docs example - Add comprehensive component tests (31 tests) --- .../src/examples/components/lazy/basic.vue | 35 + .../0/src/components/Lazy/LazyContent.vue | 14 +- .../0/src/components/Lazy/LazyPlaceholder.vue | 16 +- packages/0/src/components/Lazy/LazyRoot.vue | 71 +- packages/0/src/components/Lazy/index.test.ts | 653 ++++++++++++++++++ packages/0/src/components/Lazy/index.ts | 48 +- 6 files changed, 771 insertions(+), 66 deletions(-) create mode 100644 apps/docs/src/examples/components/lazy/basic.vue create mode 100644 packages/0/src/components/Lazy/index.test.ts diff --git a/apps/docs/src/examples/components/lazy/basic.vue b/apps/docs/src/examples/components/lazy/basic.vue new file mode 100644 index 000000000..9d5031008 --- /dev/null +++ b/apps/docs/src/examples/components/lazy/basic.vue @@ -0,0 +1,35 @@ + + + diff --git a/packages/0/src/components/Lazy/LazyContent.vue b/packages/0/src/components/Lazy/LazyContent.vue index 40b3ad787..16f750ea6 100644 --- a/packages/0/src/components/Lazy/LazyContent.vue +++ b/packages/0/src/components/Lazy/LazyContent.vue @@ -3,7 +3,7 @@ * * @remarks * Content component shown after the element intersects the viewport. - * Registers with the Lazy context and displays when content is loaded. + * Consumes the Lazy context and displays when hasContent is true. */ @@ -28,7 +28,7 @@ import { useLazyRoot } from './LazyRoot.vue' // Utilities - import { onUnmounted, toRef } from 'vue' + import { toRef } from 'vue' defineOptions({ name: 'LazyContent' }) @@ -44,18 +44,14 @@ const context = useLazyRoot(namespace) - const ticket = context.register({ type: 'content' }) - const slotProps = toRef((): LazyContentSlotProps => ({ - isSelected: ticket.isSelected.value, + hasContent: context.hasContent.value, })) - - onUnmounted(() => context.unregister(ticket.id))