routie dev init since i didn't adhere to any proper guidance up until now

This commit is contained in:
2026-04-29 22:27:29 -06:00
commit e1dabb71e2
15301 changed files with 3562618 additions and 0 deletions
@@ -0,0 +1,44 @@
@layer vuetify-components {
.v-infinite-scroll--horizontal {
display: flex;
flex-direction: row;
overflow-x: auto;
}
.v-infinite-scroll--horizontal .v-infinite-scroll-intersect {
height: 100%;
width: var(--v-infinite-margin-size, 1px);
}
.v-infinite-scroll--vertical {
display: flex;
flex-direction: column;
overflow-y: auto;
}
.v-infinite-scroll--vertical .v-infinite-scroll-intersect {
height: 1px;
width: 100%;
}
.v-infinite-scroll-intersect {
overflow: hidden;
pointer-events: none;
margin-top: var(--v-infinite-margin);
margin-bottom: calc(var(--v-infinite-margin) * -1);
}
.v-infinite-scroll-intersect:nth-child(2) {
--v-infinite-margin: var(--v-infinite-margin-size, 1px);
}
.v-infinite-scroll-intersect:nth-last-child(2) {
--v-infinite-margin: calc(var(--v-infinite-margin-size, 1px) * -1);
}
.v-infinite-scroll__side {
align-items: center;
display: flex;
justify-content: center;
padding: 8px;
transition-property: padding;
transition-duration: 0.3s;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
}
.v-infinite-scroll__side:empty, .v-infinite-scroll__side:has(> div:only-child:empty) {
padding: 0;
}
}
@@ -0,0 +1,464 @@
import type { PropType } from 'vue';
export type InfiniteScrollSide = 'start' | 'end' | 'both';
export type InfiniteScrollStatus = 'ok' | 'empty' | 'loading' | 'error';
type InfiniteScrollSlot = {
side: InfiniteScrollSide;
props: Record<string, any>;
};
export declare const makeVInfiniteScrollProps: <Defaults extends {
tag?: unknown;
height?: unknown;
maxHeight?: unknown;
maxWidth?: unknown;
minHeight?: unknown;
minWidth?: unknown;
width?: unknown;
color?: unknown;
direction?: unknown;
side?: unknown;
mode?: unknown;
margin?: unknown;
loadMoreText?: unknown;
emptyText?: unknown;
} = {}>(defaults?: Defaults | undefined) => {
tag: unknown extends Defaults["tag"] ? {
type: PropType<string | import("../../util/index.js").JSXComponent>;
default: string;
} : Omit<{
type: PropType<string | import("../../util/index.js").JSXComponent>;
default: string;
}, "default" | "type"> & {
type: PropType<unknown extends Defaults["tag"] ? string | import("../../util/index.js").JSXComponent : string | Defaults["tag"] | import("../../util/index.js").JSXComponent>;
default: unknown extends Defaults["tag"] ? string | import("../../util/index.js").JSXComponent : Defaults["tag"] | NonNullable<string | import("../../util/index.js").JSXComponent>;
};
height: unknown extends Defaults["height"] ? (NumberConstructor | StringConstructor)[] : {
type: PropType<unknown extends Defaults["height"] ? string | number : string | number | Defaults["height"]>;
default: unknown extends Defaults["height"] ? string | number : Defaults["height"] | NonNullable<string | number>;
};
maxHeight: unknown extends Defaults["maxHeight"] ? (NumberConstructor | StringConstructor)[] : {
type: PropType<unknown extends Defaults["maxHeight"] ? string | number : string | number | Defaults["maxHeight"]>;
default: unknown extends Defaults["maxHeight"] ? string | number : Defaults["maxHeight"] | NonNullable<string | number>;
};
maxWidth: unknown extends Defaults["maxWidth"] ? (NumberConstructor | StringConstructor)[] : {
type: PropType<unknown extends Defaults["maxWidth"] ? string | number : string | number | Defaults["maxWidth"]>;
default: unknown extends Defaults["maxWidth"] ? string | number : Defaults["maxWidth"] | NonNullable<string | number>;
};
minHeight: unknown extends Defaults["minHeight"] ? (NumberConstructor | StringConstructor)[] : {
type: PropType<unknown extends Defaults["minHeight"] ? string | number : string | number | Defaults["minHeight"]>;
default: unknown extends Defaults["minHeight"] ? string | number : Defaults["minHeight"] | NonNullable<string | number>;
};
minWidth: unknown extends Defaults["minWidth"] ? (NumberConstructor | StringConstructor)[] : {
type: PropType<unknown extends Defaults["minWidth"] ? string | number : string | number | Defaults["minWidth"]>;
default: unknown extends Defaults["minWidth"] ? string | number : Defaults["minWidth"] | NonNullable<string | number>;
};
width: unknown extends Defaults["width"] ? (NumberConstructor | StringConstructor)[] : {
type: PropType<unknown extends Defaults["width"] ? string | number : string | number | Defaults["width"]>;
default: unknown extends Defaults["width"] ? string | number : Defaults["width"] | NonNullable<string | number>;
};
color: unknown extends Defaults["color"] ? StringConstructor : {
type: PropType<unknown extends Defaults["color"] ? string : string | Defaults["color"]>;
default: unknown extends Defaults["color"] ? string : string | Defaults["color"];
};
direction: unknown extends Defaults["direction"] ? {
type: PropType<'vertical' | 'horizontal'>;
default: string;
validator: (v: any) => boolean;
} : Omit<{
type: PropType<'vertical' | 'horizontal'>;
default: string;
validator: (v: any) => boolean;
}, "default" | "type"> & {
type: PropType<unknown extends Defaults["direction"] ? "horizontal" | "vertical" : "horizontal" | "vertical" | Defaults["direction"]>;
default: unknown extends Defaults["direction"] ? "horizontal" | "vertical" : Defaults["direction"] | NonNullable<"horizontal" | "vertical">;
};
side: unknown extends Defaults["side"] ? {
type: PropType<InfiniteScrollSide>;
default: string;
validator: (v: any) => boolean;
} : Omit<{
type: PropType<InfiniteScrollSide>;
default: string;
validator: (v: any) => boolean;
}, "default" | "type"> & {
type: PropType<unknown extends Defaults["side"] ? InfiniteScrollSide : Defaults["side"] | InfiniteScrollSide>;
default: unknown extends Defaults["side"] ? InfiniteScrollSide : Defaults["side"] | NonNullable<InfiniteScrollSide>;
};
mode: unknown extends Defaults["mode"] ? {
type: PropType<'intersect' | 'manual'>;
default: string;
validator: (v: any) => boolean;
} : Omit<{
type: PropType<'intersect' | 'manual'>;
default: string;
validator: (v: any) => boolean;
}, "default" | "type"> & {
type: PropType<unknown extends Defaults["mode"] ? "intersect" | "manual" : "intersect" | "manual" | Defaults["mode"]>;
default: unknown extends Defaults["mode"] ? "intersect" | "manual" : Defaults["mode"] | NonNullable<"intersect" | "manual">;
};
margin: unknown extends Defaults["margin"] ? (NumberConstructor | StringConstructor)[] : {
type: PropType<unknown extends Defaults["margin"] ? string | number : string | number | Defaults["margin"]>;
default: unknown extends Defaults["margin"] ? string | number : Defaults["margin"] | NonNullable<string | number>;
};
loadMoreText: unknown extends Defaults["loadMoreText"] ? {
type: StringConstructor;
default: string;
} : Omit<{
type: StringConstructor;
default: string;
}, "default" | "type"> & {
type: PropType<unknown extends Defaults["loadMoreText"] ? string : string | Defaults["loadMoreText"]>;
default: unknown extends Defaults["loadMoreText"] ? string : string | Defaults["loadMoreText"];
};
emptyText: unknown extends Defaults["emptyText"] ? {
type: StringConstructor;
default: string;
} : Omit<{
type: StringConstructor;
default: string;
}, "default" | "type"> & {
type: PropType<unknown extends Defaults["emptyText"] ? string : string | Defaults["emptyText"]>;
default: unknown extends Defaults["emptyText"] ? string : string | Defaults["emptyText"];
};
};
export declare const VInfiniteScrollIntersect: {
new (...args: any[]): import("vue").CreateComponentPublicInstanceWithMixins<Readonly<import("vue").ExtractPropTypes<{
side: {
type: PropType<InfiniteScrollSide>;
required: true;
};
rootMargin: StringConstructor;
}>> & {
onIntersect?: ((side: InfiniteScrollSide, isIntersecting: boolean) => any) | undefined;
}, {}, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
intersect: (side: InfiniteScrollSide, isIntersecting: boolean) => true;
}, import("vue").PublicProps, {}, true, {}, {}, import("vue").GlobalComponents, import("vue").GlobalDirectives, string, {}, any, import("vue").ComponentProvideOptions, {
P: {};
B: {};
D: {};
C: {};
M: {};
Defaults: {};
}, Readonly<import("vue").ExtractPropTypes<{
side: {
type: PropType<InfiniteScrollSide>;
required: true;
};
rootMargin: StringConstructor;
}>> & {
onIntersect?: ((side: InfiniteScrollSide, isIntersecting: boolean) => any) | undefined;
}, {}, {}, {}, {}, {}>;
__isFragment?: never;
__isTeleport?: never;
__isSuspense?: never;
} & import("vue").ComponentOptionsBase<Readonly<import("vue").ExtractPropTypes<{
side: {
type: PropType<InfiniteScrollSide>;
required: true;
};
rootMargin: StringConstructor;
}>> & {
onIntersect?: ((side: InfiniteScrollSide, isIntersecting: boolean) => any) | undefined;
}, {}, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
intersect: (side: InfiniteScrollSide, isIntersecting: boolean) => true;
}, string, {}, {}, string, {}, import("vue").GlobalComponents, import("vue").GlobalDirectives, string, import("vue").ComponentProvideOptions> & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps & import("../../util/index.js").FilterPropsOptions<{
side: {
type: PropType<InfiniteScrollSide>;
required: true;
};
rootMargin: StringConstructor;
}, import("vue").ExtractPropTypes<{
side: {
type: PropType<InfiniteScrollSide>;
required: true;
};
rootMargin: StringConstructor;
}>>;
export declare const VInfiniteScroll: {
new (...args: any[]): import("vue").CreateComponentPublicInstanceWithMixins<{
tag: string | import("../../util/index.js").JSXComponent;
direction: "horizontal" | "vertical";
side: InfiniteScrollSide;
mode: "intersect" | "manual";
loadMoreText: string;
emptyText: string;
} & {
height?: string | number | undefined;
maxHeight?: string | number | undefined;
maxWidth?: string | number | undefined;
minHeight?: string | number | undefined;
minWidth?: string | number | undefined;
width?: string | number | undefined;
color?: string | undefined;
margin?: string | number | undefined;
} & {
$children?: {
default?: (() => import("vue").VNodeChild) | undefined;
loading?: ((arg: InfiniteScrollSlot) => import("vue").VNodeChild) | undefined;
error?: ((arg: InfiniteScrollSlot) => import("vue").VNodeChild) | undefined;
empty?: ((arg: InfiniteScrollSlot) => import("vue").VNodeChild) | undefined;
'load-more'?: ((arg: InfiniteScrollSlot) => import("vue").VNodeChild) | undefined;
} | {
$stable?: boolean;
} | (() => import("vue").VNodeChild) | import("vue").VNodeChild;
'v-slots'?: {
default?: false | (() => import("vue").VNodeChild) | undefined;
loading?: false | ((arg: InfiniteScrollSlot) => import("vue").VNodeChild) | undefined;
error?: false | ((arg: InfiniteScrollSlot) => import("vue").VNodeChild) | undefined;
empty?: false | ((arg: InfiniteScrollSlot) => import("vue").VNodeChild) | undefined;
'load-more'?: false | ((arg: InfiniteScrollSlot) => import("vue").VNodeChild) | undefined;
} | undefined;
} & {
"v-slot:default"?: false | (() => import("vue").VNodeChild) | undefined;
"v-slot:empty"?: false | ((arg: InfiniteScrollSlot) => import("vue").VNodeChild) | undefined;
"v-slot:error"?: false | ((arg: InfiniteScrollSlot) => import("vue").VNodeChild) | undefined;
"v-slot:load-more"?: false | ((arg: InfiniteScrollSlot) => import("vue").VNodeChild) | undefined;
"v-slot:loading"?: false | ((arg: InfiniteScrollSlot) => import("vue").VNodeChild) | undefined;
} & {
onLoad?: ((options: {
side: InfiniteScrollSide;
done: (status: InfiniteScrollStatus) => void;
}) => any) | undefined;
}, {
reset: (side?: InfiniteScrollSide) => void;
}, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
load: (options: {
side: InfiniteScrollSide;
done: (status: InfiniteScrollStatus) => void;
}) => true;
}, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, {
tag: string | import("../../util/index.js").JSXComponent;
direction: "horizontal" | "vertical";
side: InfiniteScrollSide;
mode: "intersect" | "manual";
loadMoreText: string;
emptyText: string;
}, true, {}, import("vue").SlotsType<Partial<{
default: () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[];
loading: (arg: InfiniteScrollSlot) => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[];
error: (arg: InfiniteScrollSlot) => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[];
empty: (arg: InfiniteScrollSlot) => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[];
'load-more': (arg: InfiniteScrollSlot) => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[];
}>>, import("vue").GlobalComponents, import("vue").GlobalDirectives, string, {}, any, import("vue").ComponentProvideOptions, {
P: {};
B: {};
D: {};
C: {};
M: {};
Defaults: {};
}, {
tag: string | import("../../util/index.js").JSXComponent;
direction: "horizontal" | "vertical";
side: InfiniteScrollSide;
mode: "intersect" | "manual";
loadMoreText: string;
emptyText: string;
} & {
height?: string | number | undefined;
maxHeight?: string | number | undefined;
maxWidth?: string | number | undefined;
minHeight?: string | number | undefined;
minWidth?: string | number | undefined;
width?: string | number | undefined;
color?: string | undefined;
margin?: string | number | undefined;
} & {
$children?: {
default?: (() => import("vue").VNodeChild) | undefined;
loading?: ((arg: InfiniteScrollSlot) => import("vue").VNodeChild) | undefined;
error?: ((arg: InfiniteScrollSlot) => import("vue").VNodeChild) | undefined;
empty?: ((arg: InfiniteScrollSlot) => import("vue").VNodeChild) | undefined;
'load-more'?: ((arg: InfiniteScrollSlot) => import("vue").VNodeChild) | undefined;
} | {
$stable?: boolean;
} | (() => import("vue").VNodeChild) | import("vue").VNodeChild;
'v-slots'?: {
default?: false | (() => import("vue").VNodeChild) | undefined;
loading?: false | ((arg: InfiniteScrollSlot) => import("vue").VNodeChild) | undefined;
error?: false | ((arg: InfiniteScrollSlot) => import("vue").VNodeChild) | undefined;
empty?: false | ((arg: InfiniteScrollSlot) => import("vue").VNodeChild) | undefined;
'load-more'?: false | ((arg: InfiniteScrollSlot) => import("vue").VNodeChild) | undefined;
} | undefined;
} & {
"v-slot:default"?: false | (() => import("vue").VNodeChild) | undefined;
"v-slot:empty"?: false | ((arg: InfiniteScrollSlot) => import("vue").VNodeChild) | undefined;
"v-slot:error"?: false | ((arg: InfiniteScrollSlot) => import("vue").VNodeChild) | undefined;
"v-slot:load-more"?: false | ((arg: InfiniteScrollSlot) => import("vue").VNodeChild) | undefined;
"v-slot:loading"?: false | ((arg: InfiniteScrollSlot) => import("vue").VNodeChild) | undefined;
} & {
onLoad?: ((options: {
side: InfiniteScrollSide;
done: (status: InfiniteScrollStatus) => void;
}) => any) | undefined;
}, {
reset: (side?: InfiniteScrollSide) => void;
}, {}, {}, {}, {
tag: string | import("../../util/index.js").JSXComponent;
direction: "horizontal" | "vertical";
side: InfiniteScrollSide;
mode: "intersect" | "manual";
loadMoreText: string;
emptyText: string;
}>;
__isFragment?: never;
__isTeleport?: never;
__isSuspense?: never;
} & import("vue").ComponentOptionsBase<{
tag: string | import("../../util/index.js").JSXComponent;
direction: "horizontal" | "vertical";
side: InfiniteScrollSide;
mode: "intersect" | "manual";
loadMoreText: string;
emptyText: string;
} & {
height?: string | number | undefined;
maxHeight?: string | number | undefined;
maxWidth?: string | number | undefined;
minHeight?: string | number | undefined;
minWidth?: string | number | undefined;
width?: string | number | undefined;
color?: string | undefined;
margin?: string | number | undefined;
} & {
$children?: {
default?: (() => import("vue").VNodeChild) | undefined;
loading?: ((arg: InfiniteScrollSlot) => import("vue").VNodeChild) | undefined;
error?: ((arg: InfiniteScrollSlot) => import("vue").VNodeChild) | undefined;
empty?: ((arg: InfiniteScrollSlot) => import("vue").VNodeChild) | undefined;
'load-more'?: ((arg: InfiniteScrollSlot) => import("vue").VNodeChild) | undefined;
} | {
$stable?: boolean;
} | (() => import("vue").VNodeChild) | import("vue").VNodeChild;
'v-slots'?: {
default?: false | (() => import("vue").VNodeChild) | undefined;
loading?: false | ((arg: InfiniteScrollSlot) => import("vue").VNodeChild) | undefined;
error?: false | ((arg: InfiniteScrollSlot) => import("vue").VNodeChild) | undefined;
empty?: false | ((arg: InfiniteScrollSlot) => import("vue").VNodeChild) | undefined;
'load-more'?: false | ((arg: InfiniteScrollSlot) => import("vue").VNodeChild) | undefined;
} | undefined;
} & {
"v-slot:default"?: false | (() => import("vue").VNodeChild) | undefined;
"v-slot:empty"?: false | ((arg: InfiniteScrollSlot) => import("vue").VNodeChild) | undefined;
"v-slot:error"?: false | ((arg: InfiniteScrollSlot) => import("vue").VNodeChild) | undefined;
"v-slot:load-more"?: false | ((arg: InfiniteScrollSlot) => import("vue").VNodeChild) | undefined;
"v-slot:loading"?: false | ((arg: InfiniteScrollSlot) => import("vue").VNodeChild) | undefined;
} & {
onLoad?: ((options: {
side: InfiniteScrollSide;
done: (status: InfiniteScrollStatus) => void;
}) => any) | undefined;
}, {
reset: (side?: InfiniteScrollSide) => void;
}, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
load: (options: {
side: InfiniteScrollSide;
done: (status: InfiniteScrollStatus) => void;
}) => true;
}, string, {
tag: string | import("../../util/index.js").JSXComponent;
direction: "horizontal" | "vertical";
side: InfiniteScrollSide;
mode: "intersect" | "manual";
loadMoreText: string;
emptyText: string;
}, {}, string, import("vue").SlotsType<Partial<{
default: () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[];
loading: (arg: InfiniteScrollSlot) => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[];
error: (arg: InfiniteScrollSlot) => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[];
empty: (arg: InfiniteScrollSlot) => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[];
'load-more': (arg: InfiniteScrollSlot) => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[];
}>>, import("vue").GlobalComponents, import("vue").GlobalDirectives, string, import("vue").ComponentProvideOptions> & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps & import("../../util/index.js").FilterPropsOptions<{
tag: {
type: PropType<string | import("../../util/index.js").JSXComponent>;
default: string;
};
height: (NumberConstructor | StringConstructor)[];
maxHeight: (NumberConstructor | StringConstructor)[];
maxWidth: (NumberConstructor | StringConstructor)[];
minHeight: (NumberConstructor | StringConstructor)[];
minWidth: (NumberConstructor | StringConstructor)[];
width: (NumberConstructor | StringConstructor)[];
color: StringConstructor;
direction: {
type: PropType<'vertical' | 'horizontal'>;
default: string;
validator: (v: any) => boolean;
};
side: {
type: PropType<InfiniteScrollSide>;
default: string;
validator: (v: any) => boolean;
};
mode: {
type: PropType<'intersect' | 'manual'>;
default: string;
validator: (v: any) => boolean;
};
margin: (NumberConstructor | StringConstructor)[];
loadMoreText: {
type: StringConstructor;
default: string;
};
emptyText: {
type: StringConstructor;
default: string;
};
}, import("vue").ExtractPropTypes<{
tag: {
type: PropType<string | import("../../util/index.js").JSXComponent>;
default: string;
};
height: (NumberConstructor | StringConstructor)[];
maxHeight: (NumberConstructor | StringConstructor)[];
maxWidth: (NumberConstructor | StringConstructor)[];
minHeight: (NumberConstructor | StringConstructor)[];
minWidth: (NumberConstructor | StringConstructor)[];
width: (NumberConstructor | StringConstructor)[];
color: StringConstructor;
direction: {
type: PropType<'vertical' | 'horizontal'>;
default: string;
validator: (v: any) => boolean;
};
side: {
type: PropType<InfiniteScrollSide>;
default: string;
validator: (v: any) => boolean;
};
mode: {
type: PropType<'intersect' | 'manual'>;
default: string;
validator: (v: any) => boolean;
};
margin: (NumberConstructor | StringConstructor)[];
loadMoreText: {
type: StringConstructor;
default: string;
};
emptyText: {
type: StringConstructor;
default: string;
};
}>>;
export type VInfiniteScroll = InstanceType<typeof VInfiniteScroll>;
@@ -0,0 +1,271 @@
import { createTextVNode as _createTextVNode, createElementVNode as _createElementVNode, createVNode as _createVNode, normalizeClass as _normalizeClass, normalizeStyle as _normalizeStyle } from "vue";
// Styles
import "./VInfiniteScroll.css";
// Components
import { VBtn } from "../VBtn/index.js";
import { VProgressCircular } from "../VProgressCircular/index.js"; // Composables
import { makeDimensionProps, useDimension } from "../../composables/dimensions.js";
import { useIntersectionObserver } from "../../composables/intersectionObserver.js";
import { useLocale } from "../../composables/locale.js";
import { makeTagProps } from "../../composables/tag.js"; // Utilities
import { computed, nextTick, onMounted, ref, shallowRef, watch } from 'vue';
import { convertToUnit, defineComponent, genericComponent, propsFactory, useRender } from "../../util/index.js"; // Types
export const makeVInfiniteScrollProps = propsFactory({
color: String,
direction: {
type: String,
default: 'vertical',
validator: v => ['vertical', 'horizontal'].includes(v)
},
side: {
type: String,
default: 'end',
validator: v => ['start', 'end', 'both'].includes(v)
},
mode: {
type: String,
default: 'intersect',
validator: v => ['intersect', 'manual'].includes(v)
},
margin: [Number, String],
loadMoreText: {
type: String,
default: '$vuetify.infiniteScroll.loadMore'
},
emptyText: {
type: String,
default: '$vuetify.infiniteScroll.empty'
},
...makeDimensionProps(),
...makeTagProps()
}, 'VInfiniteScroll');
export const VInfiniteScrollIntersect = defineComponent({
name: 'VInfiniteScrollIntersect',
props: {
side: {
type: String,
required: true
},
rootMargin: String
},
emits: {
intersect: (side, isIntersecting) => true
},
setup(props, {
emit
}) {
const {
intersectionRef,
isIntersecting
} = useIntersectionObserver();
watch(isIntersecting, async val => {
emit('intersect', props.side, val);
});
useRender(() => _createElementVNode("div", {
"class": "v-infinite-scroll-intersect",
"style": {
'--v-infinite-margin-size': props.rootMargin
},
"ref": intersectionRef
}, [_createTextVNode("\xA0")]));
return {};
}
});
export const VInfiniteScroll = genericComponent()({
name: 'VInfiniteScroll',
props: makeVInfiniteScrollProps(),
emits: {
load: options => true
},
setup(props, {
slots,
emit
}) {
const rootEl = ref();
const startStatus = shallowRef('ok');
const endStatus = shallowRef('ok');
const margin = computed(() => convertToUnit(props.margin));
const isIntersecting = shallowRef(false);
function setScrollAmount(amount) {
if (!rootEl.value) return;
const property = props.direction === 'vertical' ? 'scrollTop' : 'scrollLeft';
rootEl.value[property] = amount;
}
function getScrollAmount() {
if (!rootEl.value) return 0;
const property = props.direction === 'vertical' ? 'scrollTop' : 'scrollLeft';
return rootEl.value[property];
}
function getScrollSize() {
if (!rootEl.value) return 0;
const property = props.direction === 'vertical' ? 'scrollHeight' : 'scrollWidth';
return rootEl.value[property];
}
function getContainerSize() {
if (!rootEl.value) return 0;
const property = props.direction === 'vertical' ? 'clientHeight' : 'clientWidth';
return rootEl.value[property];
}
onMounted(() => {
if (!rootEl.value) return;
if (props.side === 'start') {
setScrollAmount(getScrollSize());
} else if (props.side === 'both') {
setScrollAmount(getScrollSize() / 2 - getContainerSize() / 2);
}
});
function setStatus(side, status) {
if (side === 'start') {
startStatus.value = status;
} else if (side === 'end') {
endStatus.value = status;
} else if (side === 'both') {
startStatus.value = status;
endStatus.value = status;
}
}
function getStatus(side) {
return side === 'start' ? startStatus.value : endStatus.value;
}
let previousScrollSize = 0;
function handleIntersect(side, _isIntersecting) {
isIntersecting.value = _isIntersecting;
if (isIntersecting.value) {
intersecting(side);
}
}
function intersecting(side) {
if (props.mode !== 'manual' && !isIntersecting.value) return;
const status = getStatus(side);
if (!rootEl.value || ['empty', 'loading'].includes(status)) return;
previousScrollSize = getScrollSize();
setStatus(side, 'loading');
function done(status) {
setStatus(side, status);
nextTick(() => {
if (status === 'empty' || status === 'error') return;
if (status === 'ok' && side === 'start') {
setScrollAmount(getScrollSize() - previousScrollSize + getScrollAmount());
}
if (props.mode !== 'manual') {
nextTick(() => {
// Browser takes 2 - 3 animation frames to trigger IntersectionObserver after
// VInfiniteScrollIntersect leaves the viewpoint. So far I couldn't come up
// with a better solution than using 3 nested window.requestAnimationFrame. (#17475)
window.requestAnimationFrame(() => {
window.requestAnimationFrame(() => {
window.requestAnimationFrame(() => {
intersecting(side);
});
});
});
});
}
});
}
emit('load', {
side,
done
});
}
const {
t
} = useLocale();
function renderSide(side, status) {
if (props.side !== side && props.side !== 'both') return;
const onClick = () => intersecting(side);
const slotProps = {
side,
props: {
onClick,
color: props.color
}
};
if (status === 'error') return slots.error?.(slotProps);
if (status === 'empty') return slots.empty?.(slotProps) ?? _createElementVNode("div", null, [t(props.emptyText)]);
if (props.mode === 'manual') {
if (status === 'loading') {
return slots.loading?.(slotProps) ?? _createVNode(VProgressCircular, {
"indeterminate": true,
"color": props.color
}, null);
}
return slots['load-more']?.(slotProps) ?? _createVNode(VBtn, {
"variant": "outlined",
"color": props.color,
"onClick": onClick
}, {
default: () => [t(props.loadMoreText)]
});
}
return slots.loading?.(slotProps) ?? _createVNode(VProgressCircular, {
"indeterminate": true,
"color": props.color
}, null);
}
const {
dimensionStyles
} = useDimension(props);
useRender(() => {
const Tag = props.tag;
const hasStartIntersect = props.side === 'start' || props.side === 'both';
const hasEndIntersect = props.side === 'end' || props.side === 'both';
const intersectMode = props.mode === 'intersect';
return _createVNode(Tag, {
"ref": rootEl,
"class": _normalizeClass(['v-infinite-scroll', `v-infinite-scroll--${props.direction}`, {
'v-infinite-scroll--start': hasStartIntersect,
'v-infinite-scroll--end': hasEndIntersect
}]),
"style": _normalizeStyle(dimensionStyles.value)
}, {
default: () => [_createElementVNode("div", {
"class": "v-infinite-scroll__side"
}, [renderSide('start', startStatus.value)]), hasStartIntersect && intersectMode && _createVNode(VInfiniteScrollIntersect, {
"key": "start",
"side": "start",
"onIntersect": handleIntersect,
"rootMargin": margin.value
}, null), slots.default?.(), hasEndIntersect && intersectMode && _createVNode(VInfiniteScrollIntersect, {
"key": "end",
"side": "end",
"onIntersect": handleIntersect,
"rootMargin": margin.value
}, null), _createElementVNode("div", {
"class": "v-infinite-scroll__side"
}, [renderSide('end', endStatus.value)])]
});
});
function reset(side) {
const effectiveSide = side ?? props.side;
setStatus(effectiveSide, 'ok');
nextTick(() => {
if (effectiveSide !== 'end') {
setScrollAmount(getScrollSize() - previousScrollSize + getScrollAmount());
}
if (props.mode !== 'manual') {
nextTick(() => {
// See #17475
window.requestAnimationFrame(() => {
window.requestAnimationFrame(() => {
window.requestAnimationFrame(() => {
if (effectiveSide === 'both') {
intersecting('start');
intersecting('end');
} else {
intersecting(effectiveSide);
}
});
});
});
});
}
});
}
return {
reset
};
}
});
//# sourceMappingURL=VInfiniteScroll.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,46 @@
@use '../../styles/settings'
@use '../../styles/tools'
@use './variables' as *
@include tools.layer('components')
.v-infinite-scroll--horizontal
display: flex
flex-direction: row
overflow-x: auto
.v-infinite-scroll-intersect
height: 100%
width: var(--v-infinite-margin-size, 1px)
.v-infinite-scroll--vertical
display: flex
flex-direction: column
overflow-y: auto
.v-infinite-scroll-intersect
height: 1px
width: 100%
.v-infinite-scroll-intersect
overflow: hidden
pointer-events: none
margin-top: var(--v-infinite-margin)
margin-bottom: calc(var(--v-infinite-margin) * -1)
&:nth-child(2) // TODO: "1 of &" would be more stable if structure changes
--v-infinite-margin: var(--v-infinite-margin-size, 1px)
&:nth-last-child(2)
--v-infinite-margin: calc(var(--v-infinite-margin-size, 1px) * -1)
.v-infinite-scroll__side
align-items: center
display: flex
justify-content: center
padding: $infinite-scroll-side-padding
transition-property: padding
transition-duration: settings.$transition-duration-root
transition-timing-function: settings.$standard-easing
&:empty,
&:has(> div:only-child:empty)
padding: 0
@@ -0,0 +1,3 @@
@use '../../styles/settings';
$infinite-scroll-side-padding: 8px !default;
@@ -0,0 +1 @@
export { VInfiniteScroll } from './VInfiniteScroll.js';
@@ -0,0 +1,2 @@
export { VInfiniteScroll } from "./VInfiniteScroll.js";
//# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
{"version":3,"file":"index.js","names":["VInfiniteScroll"],"sources":["../../../src/components/VInfiniteScroll/index.ts"],"sourcesContent":["export { VInfiniteScroll } from './VInfiniteScroll'\n"],"mappings":"SAASA,eAAe","ignoreList":[]}