2041 lines
70 KiB
JavaScript
2041 lines
70 KiB
JavaScript
import { $ as createTextVNode, C as vShow, Cn as withDirectives, Dt as mergeProps, Ft as onMounted, Kn as ref, M as Fragment, Ot as nextTick, Qn as toRef, U as computed, V as cloneVNode, Vn as onScopeDispose, W as createBaseVNode, Yn as shallowRef, _n as watchEffect, ar as normalizeClass, cn as useId, cr as toDisplayString, er as toValue, et as createVNode, gn as watch, nr as unref, sr as normalizeStyle } from "./vue.runtime.esm-bundler-BvoXUmaf.js";
|
|
import { $ as PREFERS_REDUCED_MOTION, B as matchesSelector, H as omit, O as focusableChildren, Q as IN_BROWSER, S as ensureValidVNode, Z as wrapInArray, _ as convertToUnit, a as provideDefaults, d as EventProp, f as callEvent, g as clamp, j as getPropertyFromItem, l as propsFactory, m as checkPrintable, n as genericComponent, p as camelizeProps, s as getCurrentInstance, w as filterInputAttrs, y as debounce } from "./defineComponent-DB6xIcDy.js";
|
|
import { t as Box } from "./box-BNWMOtF7.js";
|
|
import { a as VSlideYTransition, c as nullifyTransforms, n as VExpandXTransition, o as VDialogTransition, s as animate } from "./transitions-DCQ3sjjZ.js";
|
|
import { t as makeComponentProps } from "./component-DdiwBe6i.js";
|
|
import { t as deepEqual } from "./deepEqual-DDqmGqyF.js";
|
|
import { i as standardEasing } from "./easing-DfcvkbkS.js";
|
|
import { t as getScrollParent } from "./getScrollParent-DuXs8SPu.js";
|
|
import { t as useRender } from "./useRender-fVtVsZgv.js";
|
|
import { a as makeItemsProps, c as VListItem, i as VList, o as useItems, s as VListSubheader } from "./VList-DkWOjB0M.js";
|
|
import { r as useTextColor, t as useBackgroundColor } from "./color-B6vuQruj.js";
|
|
import { i as provideTheme, r as makeThemeProps } from "./theme-Cx5kFg0-.js";
|
|
import { t as VDivider } from "./VDivider-BJiijT0J.js";
|
|
import { n as IconValue } from "./icons-k2ZLE_i8.js";
|
|
import { n as useToggleScope, t as useProxiedModel } from "./proxiedModel-DSlSIQ0y.js";
|
|
import { i as useRtl, r as useLocale } from "./locale-DDGMqzqb.js";
|
|
import { a as useDisplay } from "./display-DKaCj-_K.js";
|
|
import { t as useResizeObserver } from "./resizeObserver-C12jWpYk.js";
|
|
import { t as makeTagProps } from "./tag-C_KkCPzB.js";
|
|
import { t as VDefaultsProvider } from "./VDefaultsProvider-C09t4-My.js";
|
|
import { n as useDimension, t as makeDimensionProps } from "./dimensions-BDdmuRdK.js";
|
|
import { n as Intersect } from "./VImg-DaEUT7gG.js";
|
|
import { n as useRounded, t as makeRoundedProps } from "./rounded-BuPGKRa9.js";
|
|
import { n as makeTransitionProps, t as MaybeTransition } from "./transition-DqoZ8fA1.js";
|
|
import { n as useBorder, t as makeBorderProps } from "./border-jCmRyoxP.js";
|
|
import { n as useElevation, t as makeElevationProps } from "./elevation-B0TH2wU6.js";
|
|
import { n as useDensity, t as makeDensityProps } from "./density-CpKZ5PhP.js";
|
|
import { n as makeVariantProps, r as useVariant, t as genOverlays } from "./variant-CqXtG9Ih.js";
|
|
import { i as useGroupItem, n as makeGroupProps, r as useGroup, t as makeGroupItemProps } from "./group-Cm2viEWK.js";
|
|
import { n as makeSizeProps, r as useSize, t as VIcon } from "./VIcon-1CJH_3Uo.js";
|
|
import { n as makeLoaderProps, r as useLoader, t as LoaderSlot } from "./loader-CV4sMFhE.js";
|
|
import { r as useLink, t as makeRouterProps } from "./router-D_jP4Uwb.js";
|
|
import { t as Ripple } from "./ripple-Z40rPDte.js";
|
|
import { t as VAvatar } from "./VAvatar-CA-KqvIX.js";
|
|
import { t as forwardRefs } from "./forwardRefs-CW3d8km7.js";
|
|
import { t as VMenu } from "./VMenu-DCQFp-2Y.js";
|
|
import { a as useFocus, c as makeVSelectionControlProps, i as makeFocusProps, l as VLabel, n as makeVInputProps, o as useInputIcon, r as useForm, s as VSelectionControl, t as VInput } from "./VInput-BxI8SL-_.js";
|
|
import { n as VSlideGroupSymbol, r as makeVSlideGroupProps, t as VSlideGroup } from "./VSlideGroup-TpMGTwfd.js";
|
|
import { t as VSheet } from "./VSheet-CLHX3uIJ.js";
|
|
import "/Users/thackmaster/Development/routie2/frontend/node_modules/vuetify/lib/components/VSelect/VSelect.css";
|
|
import "/Users/thackmaster/Development/routie2/frontend/node_modules/vuetify/lib/components/VChip/VChip.css";
|
|
import "/Users/thackmaster/Development/routie2/frontend/node_modules/vuetify/lib/components/VChipGroup/VChipGroup.css";
|
|
import "/Users/thackmaster/Development/routie2/frontend/node_modules/vuetify/lib/components/VTextField/VTextField.css";
|
|
import "/Users/thackmaster/Development/routie2/frontend/node_modules/vuetify/lib/components/VCounter/VCounter.css";
|
|
import "/Users/thackmaster/Development/routie2/frontend/node_modules/vuetify/lib/components/VField/VField.css";
|
|
import "/Users/thackmaster/Development/routie2/frontend/node_modules/vuetify/lib/components/VVirtualScroll/VVirtualScroll.css";
|
|
//#region node_modules/vuetify/lib/components/VCheckbox/VCheckboxBtn.js
|
|
var makeVCheckboxBtnProps = propsFactory({
|
|
indeterminate: Boolean,
|
|
indeterminateIcon: {
|
|
type: IconValue,
|
|
default: "$checkboxIndeterminate"
|
|
},
|
|
...makeVSelectionControlProps({
|
|
falseIcon: "$checkboxOff",
|
|
trueIcon: "$checkboxOn"
|
|
})
|
|
}, "VCheckboxBtn");
|
|
var VCheckboxBtn = genericComponent()({
|
|
name: "VCheckboxBtn",
|
|
props: makeVCheckboxBtnProps(),
|
|
emits: {
|
|
"update:modelValue": (value) => true,
|
|
"update:indeterminate": (value) => true
|
|
},
|
|
setup(props, { slots }) {
|
|
const indeterminate = useProxiedModel(props, "indeterminate");
|
|
const model = useProxiedModel(props, "modelValue");
|
|
function onChange(v) {
|
|
if (indeterminate.value) indeterminate.value = false;
|
|
}
|
|
const falseIcon = toRef(() => {
|
|
return indeterminate.value ? props.indeterminateIcon : props.falseIcon;
|
|
});
|
|
const trueIcon = toRef(() => {
|
|
return indeterminate.value ? props.indeterminateIcon : props.trueIcon;
|
|
});
|
|
useRender(() => {
|
|
return createVNode(VSelectionControl, mergeProps(omit(VSelectionControl.filterProps(props), ["modelValue"]), {
|
|
"modelValue": model.value,
|
|
"onUpdate:modelValue": [($event) => model.value = $event, onChange],
|
|
"class": ["v-checkbox-btn", props.class],
|
|
"style": props.style,
|
|
"type": "checkbox",
|
|
"falseIcon": falseIcon.value,
|
|
"trueIcon": trueIcon.value,
|
|
"aria-checked": indeterminate.value ? "mixed" : void 0
|
|
}), slots);
|
|
});
|
|
return {};
|
|
}
|
|
});
|
|
//#endregion
|
|
//#region node_modules/vuetify/lib/components/VChipGroup/VChipGroup.js
|
|
var VChipGroupSymbol = Symbol.for("vuetify:v-chip-group");
|
|
var makeVChipGroupProps = propsFactory({
|
|
baseColor: String,
|
|
column: Boolean,
|
|
filter: Boolean,
|
|
valueComparator: {
|
|
type: Function,
|
|
default: deepEqual
|
|
},
|
|
...makeVSlideGroupProps({ scrollToActive: false }),
|
|
...makeComponentProps(),
|
|
...makeGroupProps({ selectedClass: "v-chip--selected" }),
|
|
...makeTagProps(),
|
|
...makeThemeProps(),
|
|
...makeVariantProps({ variant: "tonal" })
|
|
}, "VChipGroup");
|
|
genericComponent()({
|
|
name: "VChipGroup",
|
|
props: makeVChipGroupProps(),
|
|
emits: { "update:modelValue": (value) => true },
|
|
setup(props, { slots }) {
|
|
const { themeClasses } = provideTheme(props);
|
|
const { isSelected, select, next, prev, selected } = useGroup(props, VChipGroupSymbol);
|
|
provideDefaults({ VChip: {
|
|
baseColor: toRef(() => props.baseColor),
|
|
color: toRef(() => props.color),
|
|
disabled: toRef(() => props.disabled),
|
|
filter: toRef(() => props.filter),
|
|
variant: toRef(() => props.variant)
|
|
} });
|
|
useRender(() => {
|
|
return createVNode(VSlideGroup, mergeProps(VSlideGroup.filterProps(props), {
|
|
"class": [
|
|
"v-chip-group",
|
|
{ "v-chip-group--column": props.column },
|
|
themeClasses.value,
|
|
props.class
|
|
],
|
|
"style": props.style
|
|
}), { default: () => [slots.default?.({
|
|
isSelected,
|
|
select,
|
|
next,
|
|
prev,
|
|
selected: selected.value
|
|
})] });
|
|
});
|
|
return {};
|
|
}
|
|
});
|
|
//#endregion
|
|
//#region node_modules/vuetify/lib/components/VChip/VChip.js
|
|
var makeVChipProps = propsFactory({
|
|
activeClass: String,
|
|
appendAvatar: String,
|
|
appendIcon: IconValue,
|
|
baseColor: String,
|
|
closable: Boolean,
|
|
closeIcon: {
|
|
type: IconValue,
|
|
default: "$delete"
|
|
},
|
|
closeLabel: {
|
|
type: String,
|
|
default: "$vuetify.close"
|
|
},
|
|
draggable: Boolean,
|
|
filter: Boolean,
|
|
filterIcon: {
|
|
type: IconValue,
|
|
default: "$complete"
|
|
},
|
|
label: Boolean,
|
|
link: {
|
|
type: Boolean,
|
|
default: void 0
|
|
},
|
|
pill: Boolean,
|
|
prependAvatar: String,
|
|
prependIcon: IconValue,
|
|
ripple: {
|
|
type: [Boolean, Object],
|
|
default: true
|
|
},
|
|
text: {
|
|
type: [
|
|
String,
|
|
Number,
|
|
Boolean
|
|
],
|
|
default: void 0
|
|
},
|
|
modelValue: {
|
|
type: Boolean,
|
|
default: true
|
|
},
|
|
onClick: EventProp(),
|
|
onClickOnce: EventProp(),
|
|
...makeBorderProps(),
|
|
...makeComponentProps(),
|
|
...makeDensityProps(),
|
|
...makeElevationProps(),
|
|
...makeGroupItemProps(),
|
|
...makeRoundedProps(),
|
|
...makeRouterProps(),
|
|
...makeSizeProps(),
|
|
...makeTagProps({ tag: "span" }),
|
|
...makeThemeProps(),
|
|
...makeVariantProps({ variant: "tonal" })
|
|
}, "VChip");
|
|
var VChip = genericComponent()({
|
|
name: "VChip",
|
|
directives: { vRipple: Ripple },
|
|
props: makeVChipProps(),
|
|
emits: {
|
|
"click:close": (e) => true,
|
|
"update:modelValue": (value) => true,
|
|
"group:selected": (val) => true,
|
|
click: (e) => true
|
|
},
|
|
setup(props, { attrs, emit, slots }) {
|
|
const { t } = useLocale();
|
|
const { borderClasses } = useBorder(props);
|
|
const { densityClasses } = useDensity(props);
|
|
const { elevationClasses } = useElevation(props);
|
|
const { roundedClasses } = useRounded(props);
|
|
const { sizeClasses } = useSize(props);
|
|
const { themeClasses } = provideTheme(props);
|
|
const isActive = useProxiedModel(props, "modelValue");
|
|
const group = useGroupItem(props, VChipGroupSymbol, false);
|
|
const slideGroup = useGroupItem(props, VSlideGroupSymbol, false);
|
|
const link = useLink(props, attrs);
|
|
const isLink = toRef(() => props.link !== false && link.isLink.value);
|
|
const isClickable = computed(() => !props.disabled && props.link !== false && (!!group || props.link || link.isClickable.value));
|
|
const closeProps = toRef(() => ({
|
|
"aria-label": t(props.closeLabel),
|
|
disabled: props.disabled,
|
|
onClick(e) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
isActive.value = false;
|
|
emit("click:close", e);
|
|
}
|
|
}));
|
|
watch(isActive, (val) => {
|
|
if (val) {
|
|
group?.register();
|
|
slideGroup?.register();
|
|
} else {
|
|
group?.unregister();
|
|
slideGroup?.unregister();
|
|
}
|
|
});
|
|
const { colorClasses, colorStyles, variantClasses } = useVariant(() => {
|
|
return {
|
|
color: !group || group.isSelected.value ? props.color ?? props.baseColor : props.baseColor,
|
|
variant: props.variant
|
|
};
|
|
});
|
|
function onClick(e) {
|
|
emit("click", e);
|
|
if (!isClickable.value) return;
|
|
link.navigate.value?.(e);
|
|
group?.toggle();
|
|
}
|
|
function onKeyDown(e) {
|
|
if (e.key === "Enter" || e.key === " ") {
|
|
e.preventDefault();
|
|
onClick(e);
|
|
}
|
|
}
|
|
return () => {
|
|
const Tag = link.isLink.value ? "a" : props.tag;
|
|
const hasAppendMedia = !!(props.appendIcon || props.appendAvatar);
|
|
const hasAppend = !!(hasAppendMedia || slots.append);
|
|
const hasClose = !!(slots.close || props.closable);
|
|
const hasFilter = !!(slots.filter || props.filter) && group;
|
|
const hasPrependMedia = !!(props.prependIcon || props.prependAvatar);
|
|
const hasPrepend = !!(hasPrependMedia || slots.prepend);
|
|
return isActive.value && withDirectives(createVNode(Tag, mergeProps(link.linkProps, {
|
|
"class": [
|
|
"v-chip",
|
|
{
|
|
"v-chip--disabled": props.disabled,
|
|
"v-chip--label": props.label,
|
|
"v-chip--link": isClickable.value,
|
|
"v-chip--filter": hasFilter,
|
|
"v-chip--pill": props.pill,
|
|
[`${props.activeClass}`]: props.activeClass && link.isActive?.value
|
|
},
|
|
themeClasses.value,
|
|
borderClasses.value,
|
|
colorClasses.value,
|
|
densityClasses.value,
|
|
elevationClasses.value,
|
|
roundedClasses.value,
|
|
sizeClasses.value,
|
|
variantClasses.value,
|
|
group?.selectedClass.value,
|
|
props.class
|
|
],
|
|
"style": [colorStyles.value, props.style],
|
|
"disabled": props.disabled || void 0,
|
|
"draggable": props.draggable,
|
|
"tabindex": isClickable.value ? 0 : void 0,
|
|
"onClick": onClick,
|
|
"onKeydown": isClickable.value && !isLink.value && onKeyDown
|
|
}), { default: () => [
|
|
genOverlays(isClickable.value, "v-chip"),
|
|
hasFilter && createVNode(VExpandXTransition, { "key": "filter" }, { default: () => [withDirectives(createBaseVNode("div", { "class": "v-chip__filter" }, [!slots.filter ? createVNode(VIcon, {
|
|
"key": "filter-icon",
|
|
"icon": props.filterIcon
|
|
}, null) : createVNode(VDefaultsProvider, {
|
|
"key": "filter-defaults",
|
|
"disabled": !props.filterIcon,
|
|
"defaults": { VIcon: { icon: props.filterIcon } }
|
|
}, slots.filter)]), [[vShow, group.isSelected.value]])] }),
|
|
hasPrepend && createBaseVNode("div", {
|
|
"key": "prepend",
|
|
"class": "v-chip__prepend"
|
|
}, [!slots.prepend ? createBaseVNode(Fragment, null, [props.prependIcon && createVNode(VIcon, {
|
|
"key": "prepend-icon",
|
|
"icon": props.prependIcon,
|
|
"start": true
|
|
}, null), props.prependAvatar && createVNode(VAvatar, {
|
|
"key": "prepend-avatar",
|
|
"image": props.prependAvatar,
|
|
"start": true
|
|
}, null)]) : createVNode(VDefaultsProvider, {
|
|
"key": "prepend-defaults",
|
|
"disabled": !hasPrependMedia,
|
|
"defaults": {
|
|
VAvatar: {
|
|
image: props.prependAvatar,
|
|
start: true
|
|
},
|
|
VIcon: {
|
|
icon: props.prependIcon,
|
|
start: true
|
|
}
|
|
}
|
|
}, slots.prepend)]),
|
|
createBaseVNode("div", {
|
|
"class": "v-chip__content",
|
|
"data-no-activator": ""
|
|
}, [slots.default?.({
|
|
isSelected: group?.isSelected.value,
|
|
selectedClass: group?.selectedClass.value,
|
|
select: group?.select,
|
|
toggle: group?.toggle,
|
|
value: group?.value.value,
|
|
disabled: props.disabled
|
|
}) ?? toDisplayString(props.text)]),
|
|
hasAppend && createBaseVNode("div", {
|
|
"key": "append",
|
|
"class": "v-chip__append"
|
|
}, [!slots.append ? createBaseVNode(Fragment, null, [props.appendIcon && createVNode(VIcon, {
|
|
"key": "append-icon",
|
|
"end": true,
|
|
"icon": props.appendIcon
|
|
}, null), props.appendAvatar && createVNode(VAvatar, {
|
|
"key": "append-avatar",
|
|
"end": true,
|
|
"image": props.appendAvatar
|
|
}, null)]) : createVNode(VDefaultsProvider, {
|
|
"key": "append-defaults",
|
|
"disabled": !hasAppendMedia,
|
|
"defaults": {
|
|
VAvatar: {
|
|
end: true,
|
|
image: props.appendAvatar
|
|
},
|
|
VIcon: {
|
|
end: true,
|
|
icon: props.appendIcon
|
|
}
|
|
}
|
|
}, slots.append)]),
|
|
hasClose && createBaseVNode("button", mergeProps({
|
|
"key": "close",
|
|
"class": "v-chip__close",
|
|
"type": "button",
|
|
"data-testid": "close-chip"
|
|
}, closeProps.value), [!slots.close ? createVNode(VIcon, {
|
|
"key": "close-icon",
|
|
"icon": props.closeIcon,
|
|
"size": "x-small"
|
|
}, null) : createVNode(VDefaultsProvider, {
|
|
"key": "close-defaults",
|
|
"defaults": { VIcon: {
|
|
icon: props.closeIcon,
|
|
size: "x-small"
|
|
} }
|
|
}, slots.close)])
|
|
] }), [[
|
|
Ripple,
|
|
isClickable.value && props.ripple,
|
|
null
|
|
]]);
|
|
};
|
|
}
|
|
});
|
|
//#endregion
|
|
//#region node_modules/vuetify/lib/components/VCounter/VCounter.js
|
|
var makeVCounterProps = propsFactory({
|
|
active: Boolean,
|
|
disabled: Boolean,
|
|
max: [Number, String],
|
|
value: {
|
|
type: [Number, String],
|
|
default: 0
|
|
},
|
|
...makeComponentProps(),
|
|
...makeTransitionProps({ transition: { component: VSlideYTransition } })
|
|
}, "VCounter");
|
|
var VCounter = genericComponent()({
|
|
name: "VCounter",
|
|
functional: true,
|
|
props: makeVCounterProps(),
|
|
setup(props, { slots }) {
|
|
const counter = toRef(() => {
|
|
return props.max ? `${props.value} / ${props.max}` : String(props.value);
|
|
});
|
|
useRender(() => createVNode(MaybeTransition, { "transition": props.transition }, { default: () => [withDirectives(createBaseVNode("div", {
|
|
"class": normalizeClass([
|
|
"v-counter",
|
|
{ "text-error": props.max && !props.disabled && parseFloat(props.value) > parseFloat(props.max) },
|
|
props.class
|
|
]),
|
|
"style": normalizeStyle(props.style)
|
|
}, [slots.default ? slots.default({
|
|
counter: counter.value,
|
|
max: props.max,
|
|
value: props.value
|
|
}) : counter.value]), [[vShow, props.active]])] }));
|
|
return {};
|
|
}
|
|
});
|
|
//#endregion
|
|
//#region node_modules/vuetify/lib/components/VField/VFieldLabel.js
|
|
var makeVFieldLabelProps = propsFactory({
|
|
floating: Boolean,
|
|
...makeComponentProps()
|
|
}, "VFieldLabel");
|
|
var VFieldLabel = genericComponent()({
|
|
name: "VFieldLabel",
|
|
props: makeVFieldLabelProps(),
|
|
setup(props, { slots }) {
|
|
useRender(() => createVNode(VLabel, {
|
|
"class": normalizeClass([
|
|
"v-field-label",
|
|
{ "v-field-label--floating": props.floating },
|
|
props.class
|
|
]),
|
|
"style": normalizeStyle(props.style)
|
|
}, slots));
|
|
return {};
|
|
}
|
|
});
|
|
//#endregion
|
|
//#region node_modules/vuetify/lib/components/VField/VField.js
|
|
var allowedVariants = [
|
|
"underlined",
|
|
"outlined",
|
|
"filled",
|
|
"solo",
|
|
"solo-inverted",
|
|
"solo-filled",
|
|
"plain"
|
|
];
|
|
var makeVFieldProps = propsFactory({
|
|
appendInnerIcon: IconValue,
|
|
bgColor: String,
|
|
clearable: Boolean,
|
|
clearIcon: {
|
|
type: IconValue,
|
|
default: "$clear"
|
|
},
|
|
active: Boolean,
|
|
centerAffix: {
|
|
type: Boolean,
|
|
default: void 0
|
|
},
|
|
color: String,
|
|
baseColor: String,
|
|
dirty: Boolean,
|
|
disabled: {
|
|
type: Boolean,
|
|
default: null
|
|
},
|
|
glow: Boolean,
|
|
error: Boolean,
|
|
flat: Boolean,
|
|
iconColor: [Boolean, String],
|
|
label: String,
|
|
persistentClear: Boolean,
|
|
prependInnerIcon: IconValue,
|
|
reverse: Boolean,
|
|
singleLine: Boolean,
|
|
variant: {
|
|
type: String,
|
|
default: "filled",
|
|
validator: (v) => allowedVariants.includes(v)
|
|
},
|
|
"onClick:clear": EventProp(),
|
|
"onClick:appendInner": EventProp(),
|
|
"onClick:prependInner": EventProp(),
|
|
...makeComponentProps(),
|
|
...makeLoaderProps(),
|
|
...makeRoundedProps(),
|
|
...makeThemeProps()
|
|
}, "VField");
|
|
var VField = genericComponent()({
|
|
name: "VField",
|
|
inheritAttrs: false,
|
|
props: {
|
|
id: String,
|
|
details: Boolean,
|
|
labelId: String,
|
|
...makeFocusProps(),
|
|
...makeVFieldProps()
|
|
},
|
|
emits: {
|
|
"update:focused": (focused) => true,
|
|
"update:modelValue": (value) => true
|
|
},
|
|
setup(props, { attrs, emit, slots }) {
|
|
const { themeClasses } = provideTheme(props);
|
|
const { loaderClasses } = useLoader(props);
|
|
const { focusClasses, isFocused, focus, blur } = useFocus(props);
|
|
const { InputIcon } = useInputIcon(props);
|
|
const { roundedClasses } = useRounded(props);
|
|
const { rtlClasses } = useRtl();
|
|
const isActive = toRef(() => props.dirty || props.active);
|
|
const hasLabel = toRef(() => !!(props.label || slots.label));
|
|
const hasFloatingLabel = toRef(() => !props.singleLine && hasLabel.value);
|
|
const uid = useId();
|
|
const id = computed(() => props.id || `input-${uid}`);
|
|
const messagesId = toRef(() => !props.details ? void 0 : `${id.value}-messages`);
|
|
const labelRef = ref();
|
|
const floatingLabelRef = ref();
|
|
const controlRef = ref();
|
|
const isPlainOrUnderlined = computed(() => ["plain", "underlined"].includes(props.variant));
|
|
const color = computed(() => {
|
|
return props.error || props.disabled ? void 0 : isActive.value && isFocused.value ? props.color : props.baseColor;
|
|
});
|
|
const iconColor = computed(() => {
|
|
if (props.iconColor === true || !props.iconColor && props.glow && isFocused.value) return color.value;
|
|
if (!props.iconColor || props.glow && !isFocused.value) return void 0;
|
|
return props.iconColor;
|
|
});
|
|
const { backgroundColorClasses, backgroundColorStyles } = useBackgroundColor(() => props.bgColor);
|
|
const { textColorClasses, textColorStyles } = useTextColor(color);
|
|
watch(isActive, (val) => {
|
|
if (hasFloatingLabel.value && !PREFERS_REDUCED_MOTION()) {
|
|
const el = labelRef.value.$el;
|
|
const targetEl = floatingLabelRef.value.$el;
|
|
requestAnimationFrame(() => {
|
|
const rect = nullifyTransforms(el);
|
|
const targetRect = new Box(targetEl);
|
|
const x = targetRect.x - rect.x;
|
|
const y = targetRect.y - rect.y - (rect.height / 2 - targetRect.height / 2);
|
|
const targetWidth = targetRect.width / .75;
|
|
const width = Math.abs(targetWidth - rect.width) > 1 ? { maxWidth: convertToUnit(targetWidth) } : void 0;
|
|
const style = getComputedStyle(el);
|
|
const targetStyle = getComputedStyle(targetEl);
|
|
const duration = parseFloat(style.transitionDuration) * 1e3 || 150;
|
|
const scale = parseFloat(targetStyle.getPropertyValue("--v-field-label-scale"));
|
|
const color = targetStyle.getPropertyValue("color");
|
|
el.style.visibility = "visible";
|
|
targetEl.style.visibility = "hidden";
|
|
animate(el, {
|
|
transform: `translate(${x}px, ${y}px) scale(${scale})`,
|
|
color,
|
|
...width
|
|
}, {
|
|
duration,
|
|
easing: standardEasing,
|
|
direction: val ? "normal" : "reverse"
|
|
}).finished.then(() => {
|
|
el.style.removeProperty("visibility");
|
|
targetEl.style.removeProperty("visibility");
|
|
});
|
|
});
|
|
}
|
|
}, { flush: "post" });
|
|
const slotProps = computed(() => ({
|
|
isActive,
|
|
isFocused,
|
|
controlRef,
|
|
iconColor,
|
|
blur,
|
|
focus
|
|
}));
|
|
const floatingLabelProps = toRef(() => {
|
|
const ariaHidden = !isActive.value;
|
|
return {
|
|
"aria-hidden": ariaHidden,
|
|
for: ariaHidden ? void 0 : id.value
|
|
};
|
|
});
|
|
const mainLabelProps = toRef(() => {
|
|
const ariaHidden = hasFloatingLabel.value && isActive.value;
|
|
return {
|
|
"aria-hidden": ariaHidden,
|
|
for: ariaHidden ? void 0 : id.value
|
|
};
|
|
});
|
|
function onClick(e) {
|
|
if (e.target !== document.activeElement) e.preventDefault();
|
|
}
|
|
useRender(() => {
|
|
const isOutlined = props.variant === "outlined";
|
|
const hasPrepend = !!(slots["prepend-inner"] || props.prependInnerIcon);
|
|
const hasClear = !!(props.clearable || slots.clear) && !props.disabled;
|
|
const hasAppend = !!(slots["append-inner"] || props.appendInnerIcon || hasClear);
|
|
const label = () => slots.label ? slots.label({
|
|
...slotProps.value,
|
|
label: props.label,
|
|
props: { for: id.value }
|
|
}) : props.label;
|
|
return createBaseVNode("div", mergeProps({
|
|
"class": [
|
|
"v-field",
|
|
{
|
|
"v-field--active": isActive.value,
|
|
"v-field--appended": hasAppend,
|
|
"v-field--center-affix": props.centerAffix ?? !isPlainOrUnderlined.value,
|
|
"v-field--disabled": props.disabled,
|
|
"v-field--dirty": props.dirty,
|
|
"v-field--error": props.error,
|
|
"v-field--glow": props.glow,
|
|
"v-field--flat": props.flat,
|
|
"v-field--has-background": !!props.bgColor,
|
|
"v-field--persistent-clear": props.persistentClear,
|
|
"v-field--prepended": hasPrepend,
|
|
"v-field--reverse": props.reverse,
|
|
"v-field--single-line": props.singleLine,
|
|
"v-field--no-label": !label(),
|
|
[`v-field--variant-${props.variant}`]: true
|
|
},
|
|
themeClasses.value,
|
|
backgroundColorClasses.value,
|
|
focusClasses.value,
|
|
loaderClasses.value,
|
|
roundedClasses.value,
|
|
rtlClasses.value,
|
|
props.class
|
|
],
|
|
"style": [backgroundColorStyles.value, props.style],
|
|
"onClick": onClick
|
|
}, attrs), [
|
|
createBaseVNode("div", { "class": "v-field__overlay" }, null),
|
|
createVNode(LoaderSlot, {
|
|
"name": "v-field",
|
|
"active": !!props.loading,
|
|
"color": props.error ? "error" : typeof props.loading === "string" ? props.loading : props.color
|
|
}, { default: slots.loader }),
|
|
hasPrepend && createBaseVNode("div", {
|
|
"key": "prepend",
|
|
"class": "v-field__prepend-inner"
|
|
}, [slots["prepend-inner"] ? slots["prepend-inner"](slotProps.value) : props.prependInnerIcon && createVNode(InputIcon, {
|
|
"key": "prepend-icon",
|
|
"name": "prependInner",
|
|
"color": iconColor.value
|
|
}, null)]),
|
|
createBaseVNode("div", {
|
|
"class": "v-field__field",
|
|
"data-no-activator": ""
|
|
}, [
|
|
[
|
|
"filled",
|
|
"solo",
|
|
"solo-inverted",
|
|
"solo-filled"
|
|
].includes(props.variant) && hasFloatingLabel.value && createVNode(VFieldLabel, mergeProps({
|
|
"key": "floating-label",
|
|
"ref": floatingLabelRef,
|
|
"class": [textColorClasses.value],
|
|
"floating": true
|
|
}, floatingLabelProps.value, { "style": textColorStyles.value }), { default: () => [label()] }),
|
|
hasLabel.value && createVNode(VFieldLabel, mergeProps({
|
|
"key": "label",
|
|
"ref": labelRef,
|
|
"id": props.labelId
|
|
}, mainLabelProps.value), { default: () => [label()] }),
|
|
slots.default?.({
|
|
...slotProps.value,
|
|
props: {
|
|
id: id.value,
|
|
class: "v-field__input",
|
|
"aria-describedby": messagesId.value
|
|
},
|
|
focus,
|
|
blur
|
|
}) ?? createBaseVNode("div", {
|
|
"id": id.value,
|
|
"class": "v-field__input",
|
|
"aria-describedby": messagesId.value
|
|
}, null)
|
|
]),
|
|
hasClear && createVNode(VExpandXTransition, { "key": "clear" }, { default: () => [withDirectives(createBaseVNode("div", {
|
|
"class": "v-field__clearable",
|
|
"onMousedown": (e) => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
}
|
|
}, [createVNode(VDefaultsProvider, { "defaults": { VIcon: { icon: props.clearIcon } } }, { default: () => [slots.clear ? slots.clear({
|
|
...slotProps.value,
|
|
props: {
|
|
onFocus: focus,
|
|
onBlur: blur,
|
|
onClick: props["onClick:clear"],
|
|
tabindex: -1
|
|
}
|
|
}) : createVNode(InputIcon, {
|
|
"name": "clear",
|
|
"onFocus": focus,
|
|
"onBlur": blur,
|
|
"tabindex": -1
|
|
}, null)] })]), [[vShow, props.dirty]])] }),
|
|
hasAppend && createBaseVNode("div", {
|
|
"key": "append",
|
|
"class": "v-field__append-inner"
|
|
}, [slots["append-inner"] ? slots["append-inner"](slotProps.value) : props.appendInnerIcon && createVNode(InputIcon, {
|
|
"key": "append-icon",
|
|
"name": "appendInner",
|
|
"color": iconColor.value
|
|
}, null)]),
|
|
createBaseVNode("div", {
|
|
"class": normalizeClass(["v-field__outline", textColorClasses.value]),
|
|
"style": normalizeStyle(textColorStyles.value)
|
|
}, [isOutlined && createBaseVNode(Fragment, null, [
|
|
createBaseVNode("div", { "class": "v-field__outline__start" }, null),
|
|
hasFloatingLabel.value && createBaseVNode("div", { "class": "v-field__outline__notch" }, [createVNode(VFieldLabel, mergeProps({
|
|
"ref": floatingLabelRef,
|
|
"floating": true
|
|
}, floatingLabelProps.value), { default: () => [label()] })]),
|
|
createBaseVNode("div", { "class": "v-field__outline__end" }, null)
|
|
]), isPlainOrUnderlined.value && hasFloatingLabel.value && createVNode(VFieldLabel, mergeProps({
|
|
"ref": floatingLabelRef,
|
|
"floating": true
|
|
}, floatingLabelProps.value), { default: () => [label()] })])
|
|
]);
|
|
});
|
|
return {
|
|
controlRef,
|
|
fieldIconColor: iconColor
|
|
};
|
|
}
|
|
});
|
|
//#endregion
|
|
//#region node_modules/vuetify/lib/composables/autocomplete.js
|
|
var makeAutocompleteProps = propsFactory({ autocomplete: String }, "autocomplete");
|
|
function useAutocomplete(props) {
|
|
const uniqueId = useId();
|
|
const reloadTrigger = shallowRef(0);
|
|
const isSuppressing = toRef(() => props.autocomplete === "suppress");
|
|
const fieldName = toRef(() => {
|
|
if (!props.name) return void 0;
|
|
return isSuppressing.value ? `${props.name}-${uniqueId}-${reloadTrigger.value}` : props.name;
|
|
});
|
|
return {
|
|
isSuppressing,
|
|
fieldAutocomplete: toRef(() => {
|
|
return isSuppressing.value ? "off" : props.autocomplete;
|
|
}),
|
|
fieldName,
|
|
update: () => reloadTrigger.value = (/* @__PURE__ */ new Date()).getTime()
|
|
};
|
|
}
|
|
//#endregion
|
|
//#region node_modules/vuetify/lib/composables/autofocus.js
|
|
function useAutofocus(props) {
|
|
function onIntersect(isIntersecting, entries) {
|
|
if (!props.autofocus || !isIntersecting) return;
|
|
const el = entries[0].target;
|
|
(el.matches("input,textarea") ? el : el.querySelector("input,textarea"))?.focus();
|
|
}
|
|
return { onIntersect };
|
|
}
|
|
//#endregion
|
|
//#region node_modules/vuetify/lib/components/VTextField/VTextField.js
|
|
var activeTypes = [
|
|
"color",
|
|
"file",
|
|
"time",
|
|
"date",
|
|
"datetime-local",
|
|
"week",
|
|
"month"
|
|
];
|
|
var makeVTextFieldProps = propsFactory({
|
|
autofocus: Boolean,
|
|
counter: [
|
|
Boolean,
|
|
Number,
|
|
String
|
|
],
|
|
counterValue: [Number, Function],
|
|
prefix: String,
|
|
placeholder: String,
|
|
persistentPlaceholder: Boolean,
|
|
persistentCounter: Boolean,
|
|
suffix: String,
|
|
role: String,
|
|
type: {
|
|
type: String,
|
|
default: "text"
|
|
},
|
|
modelModifiers: Object,
|
|
...makeAutocompleteProps(),
|
|
...omit(makeVInputProps(), ["direction"]),
|
|
...makeVFieldProps()
|
|
}, "VTextField");
|
|
var VTextField = genericComponent()({
|
|
name: "VTextField",
|
|
directives: { vIntersect: Intersect },
|
|
inheritAttrs: false,
|
|
props: makeVTextFieldProps(),
|
|
emits: {
|
|
"click:control": (e) => true,
|
|
"mousedown:control": (e) => true,
|
|
"update:focused": (focused) => true,
|
|
"update:modelValue": (val) => true
|
|
},
|
|
setup(props, { attrs, emit, slots }) {
|
|
const model = useProxiedModel(props, "modelValue", void 0, (v) => {
|
|
if (Object.is(v, -0)) return "-0";
|
|
return v;
|
|
});
|
|
const { isFocused, focus, blur } = useFocus(props);
|
|
const { onIntersect } = useAutofocus(props);
|
|
const counterValue = computed(() => {
|
|
return typeof props.counterValue === "function" ? props.counterValue(model.value) : typeof props.counterValue === "number" ? props.counterValue : (model.value ?? "").toString().length;
|
|
});
|
|
const max = computed(() => {
|
|
if (attrs.maxlength) return attrs.maxlength;
|
|
if (!props.counter || typeof props.counter !== "number" && typeof props.counter !== "string") return void 0;
|
|
return props.counter;
|
|
});
|
|
const isPlainOrUnderlined = computed(() => ["plain", "underlined"].includes(props.variant));
|
|
const vInputRef = ref();
|
|
const vFieldRef = ref();
|
|
const inputRef = ref();
|
|
const autocomplete = useAutocomplete(props);
|
|
const isActive = computed(() => activeTypes.includes(props.type) || props.persistentPlaceholder || isFocused.value || props.active);
|
|
function onFocus() {
|
|
if (autocomplete.isSuppressing.value) autocomplete.update();
|
|
if (!isFocused.value) focus();
|
|
nextTick(() => {
|
|
if (inputRef.value !== document.activeElement) inputRef.value?.focus();
|
|
});
|
|
}
|
|
function onControlMousedown(e) {
|
|
emit("mousedown:control", e);
|
|
if (e.target === inputRef.value) return;
|
|
onFocus();
|
|
e.preventDefault();
|
|
}
|
|
function onControlClick(e) {
|
|
emit("click:control", e);
|
|
}
|
|
function onClear(e, reset) {
|
|
e.stopPropagation();
|
|
onFocus();
|
|
nextTick(() => {
|
|
reset();
|
|
callEvent(props["onClick:clear"], e);
|
|
});
|
|
}
|
|
function onInput(e) {
|
|
const el = e.target;
|
|
if (!(props.modelModifiers?.trim && [
|
|
"text",
|
|
"search",
|
|
"password",
|
|
"tel",
|
|
"url"
|
|
].includes(props.type))) {
|
|
model.value = el.value;
|
|
return;
|
|
}
|
|
const value = el.value;
|
|
const start = el.selectionStart;
|
|
const end = el.selectionEnd;
|
|
model.value = value;
|
|
nextTick(() => {
|
|
let offset = 0;
|
|
if (value.trimStart().length === el.value.length) offset = value.length - el.value.length;
|
|
if (start != null) el.selectionStart = start - offset;
|
|
if (end != null) el.selectionEnd = end - offset;
|
|
});
|
|
}
|
|
useRender(() => {
|
|
const hasCounter = !!(slots.counter || props.counter !== false && props.counter != null);
|
|
const hasDetails = !!(hasCounter || slots.details);
|
|
const [rootAttrs, inputAttrs] = filterInputAttrs(attrs);
|
|
const { modelValue: _, ...inputProps } = VInput.filterProps(props);
|
|
const fieldProps = VField.filterProps(props);
|
|
return createVNode(VInput, mergeProps({
|
|
"ref": vInputRef,
|
|
"modelValue": model.value,
|
|
"onUpdate:modelValue": ($event) => model.value = $event,
|
|
"class": [
|
|
"v-text-field",
|
|
{
|
|
"v-text-field--prefixed": props.prefix,
|
|
"v-text-field--suffixed": props.suffix,
|
|
"v-input--plain-underlined": isPlainOrUnderlined.value
|
|
},
|
|
props.class
|
|
],
|
|
"style": props.style
|
|
}, rootAttrs, inputProps, {
|
|
"centerAffix": !isPlainOrUnderlined.value,
|
|
"focused": isFocused.value,
|
|
"indentDetails": props.indentDetails ?? !isPlainOrUnderlined.value
|
|
}), {
|
|
...slots,
|
|
default: ({ id, isDisabled, isDirty, isReadonly, isValid, hasDetails, reset }) => createVNode(VField, mergeProps({
|
|
"ref": vFieldRef,
|
|
"onMousedown": onControlMousedown,
|
|
"onClick": onControlClick,
|
|
"onClick:clear": (e) => onClear(e, reset),
|
|
"role": props.role
|
|
}, omit(fieldProps, ["onClick:clear"]), {
|
|
"id": id.value,
|
|
"labelId": `${id.value}-label`,
|
|
"active": isActive.value || isDirty.value,
|
|
"dirty": isDirty.value || props.dirty,
|
|
"disabled": isDisabled.value,
|
|
"focused": isFocused.value,
|
|
"details": hasDetails.value,
|
|
"error": isValid.value === false
|
|
}), {
|
|
...slots,
|
|
default: ({ props: { class: fieldClass, ...slotProps }, controlRef }) => {
|
|
const inputNode = createBaseVNode("input", mergeProps({
|
|
"ref": (val) => inputRef.value = controlRef.value = val,
|
|
"value": model.value,
|
|
"onInput": onInput,
|
|
"autofocus": props.autofocus,
|
|
"readonly": isReadonly.value,
|
|
"disabled": isDisabled.value,
|
|
"name": autocomplete.fieldName.value,
|
|
"autocomplete": autocomplete.fieldAutocomplete.value,
|
|
"placeholder": props.placeholder,
|
|
"size": 1,
|
|
"role": props.role,
|
|
"type": props.type,
|
|
"onFocus": focus,
|
|
"onBlur": blur,
|
|
"aria-labelledby": `${id.value}-label`
|
|
}, slotProps, inputAttrs), null);
|
|
return createBaseVNode(Fragment, null, [
|
|
props.prefix && createBaseVNode("span", { "class": "v-text-field__prefix" }, [createBaseVNode("span", { "class": "v-text-field__prefix__text" }, [props.prefix])]),
|
|
withDirectives(slots.default ? createBaseVNode("div", {
|
|
"class": normalizeClass(fieldClass),
|
|
"data-no-activator": ""
|
|
}, [slots.default({ id }), inputNode]) : cloneVNode(inputNode, { class: fieldClass }), [[
|
|
Intersect,
|
|
onIntersect,
|
|
null,
|
|
{ once: true }
|
|
]]),
|
|
props.suffix && createBaseVNode("span", { "class": "v-text-field__suffix" }, [createBaseVNode("span", { "class": "v-text-field__suffix__text" }, [props.suffix])])
|
|
]);
|
|
}
|
|
}),
|
|
details: hasDetails ? (slotProps) => createBaseVNode(Fragment, null, [slots.details?.(slotProps), hasCounter && createBaseVNode(Fragment, null, [createBaseVNode("span", null, null), createVNode(VCounter, {
|
|
"active": props.persistentCounter || isFocused.value,
|
|
"value": counterValue.value,
|
|
"max": max.value,
|
|
"disabled": props.disabled
|
|
}, slots.counter)])]) : void 0
|
|
});
|
|
});
|
|
return forwardRefs({}, vInputRef, vFieldRef, inputRef);
|
|
}
|
|
});
|
|
//#endregion
|
|
//#region node_modules/vuetify/lib/components/VVirtualScroll/VVirtualScrollItem.js
|
|
var makeVVirtualScrollItemProps = propsFactory({
|
|
renderless: Boolean,
|
|
...makeComponentProps()
|
|
}, "VVirtualScrollItem");
|
|
var VVirtualScrollItem = genericComponent()({
|
|
name: "VVirtualScrollItem",
|
|
inheritAttrs: false,
|
|
props: makeVVirtualScrollItemProps(),
|
|
emits: { "update:height": (height) => true },
|
|
setup(props, { attrs, emit, slots }) {
|
|
const { resizeRef, contentRect } = useResizeObserver(void 0, "border");
|
|
watch(() => contentRect.value?.height, (height) => {
|
|
if (height != null) emit("update:height", height);
|
|
});
|
|
useRender(() => props.renderless ? createBaseVNode(Fragment, null, [slots.default?.({ itemRef: resizeRef })]) : createBaseVNode("div", mergeProps({
|
|
"ref": resizeRef,
|
|
"class": ["v-virtual-scroll__item", props.class],
|
|
"style": props.style
|
|
}, attrs), [slots.default?.()]));
|
|
}
|
|
});
|
|
//#endregion
|
|
//#region node_modules/vuetify/lib/composables/virtual.js
|
|
var UP = -1;
|
|
var DOWN = 1;
|
|
/** Determines how large each batch of items should be */
|
|
var BUFFER_PX = 100;
|
|
var makeVirtualProps = propsFactory({
|
|
itemHeight: {
|
|
type: [Number, String],
|
|
default: null
|
|
},
|
|
itemKey: {
|
|
type: [
|
|
String,
|
|
Array,
|
|
Function
|
|
],
|
|
default: null
|
|
},
|
|
height: [Number, String]
|
|
}, "virtual");
|
|
function useVirtual(props, items) {
|
|
const display = useDisplay();
|
|
const itemHeight = shallowRef(0);
|
|
watchEffect(() => {
|
|
itemHeight.value = parseFloat(props.itemHeight || 0);
|
|
});
|
|
const first = shallowRef(0);
|
|
const last = shallowRef(Math.ceil((parseInt(props.height) || display.height.value) / (itemHeight.value || 16)) || 1);
|
|
const paddingTop = shallowRef(0);
|
|
const paddingBottom = shallowRef(0);
|
|
/** The scrollable element */
|
|
const containerRef = ref();
|
|
/** An element marking the top of the scrollable area,
|
|
* used to add an offset if there's padding or other elements above the virtual list */
|
|
const markerRef = ref();
|
|
/** markerRef's offsetTop, lazily evaluated */
|
|
let markerOffset = 0;
|
|
const { resizeRef, contentRect } = useResizeObserver();
|
|
watchEffect(() => {
|
|
resizeRef.value = containerRef.value;
|
|
});
|
|
const viewportHeight = computed(() => {
|
|
return containerRef.value === document.documentElement ? display.height.value : contentRect.value?.height || parseInt(props.height) || 0;
|
|
});
|
|
/** All static elements have been rendered and we have an assumed item height */
|
|
const hasInitialRender = computed(() => {
|
|
return !!(containerRef.value && markerRef.value && viewportHeight.value && itemHeight.value);
|
|
});
|
|
let sizes = Array.from({ length: items.value.length });
|
|
let offsets = Array.from({ length: items.value.length });
|
|
const updateTime = shallowRef(0);
|
|
let targetScrollIndex = -1;
|
|
function getSize(index) {
|
|
return sizes[index] || itemHeight.value;
|
|
}
|
|
const updateOffsets = debounce(() => {
|
|
const start = performance.now();
|
|
offsets[0] = 0;
|
|
const length = items.value.length;
|
|
for (let i = 1; i <= length; i++) offsets[i] = (offsets[i - 1] || 0) + getSize(i - 1);
|
|
updateTime.value = Math.max(updateTime.value, performance.now() - start);
|
|
}, updateTime);
|
|
const unwatch = watch(hasInitialRender, (v) => {
|
|
if (!v) return;
|
|
unwatch();
|
|
markerOffset = markerRef.value.offsetTop;
|
|
updateOffsets.immediate();
|
|
calculateVisibleItems();
|
|
if (!~targetScrollIndex) return;
|
|
nextTick(() => {
|
|
IN_BROWSER && window.requestAnimationFrame(() => {
|
|
scrollToIndex(targetScrollIndex);
|
|
targetScrollIndex = -1;
|
|
});
|
|
});
|
|
});
|
|
onScopeDispose(() => {
|
|
updateOffsets.clear();
|
|
});
|
|
function handleItemResize(index, height) {
|
|
const prevHeight = sizes[index];
|
|
const prevMinHeight = itemHeight.value;
|
|
itemHeight.value = prevMinHeight ? Math.min(itemHeight.value, height) : height;
|
|
if (prevHeight !== height || prevMinHeight !== itemHeight.value) {
|
|
sizes[index] = height;
|
|
updateOffsets();
|
|
}
|
|
}
|
|
function calculateOffset(index) {
|
|
index = clamp(index, 0, items.value.length);
|
|
const whole = Math.floor(index);
|
|
const fraction = index % 1;
|
|
const next = whole + 1;
|
|
const wholeOffset = offsets[whole] || 0;
|
|
return wholeOffset + ((offsets[next] || wholeOffset) - wholeOffset) * fraction;
|
|
}
|
|
function calculateIndex(scrollTop) {
|
|
return binaryClosest(offsets, scrollTop);
|
|
}
|
|
let lastScrollTop = 0;
|
|
let scrollVelocity = 0;
|
|
let lastScrollTime = 0;
|
|
watch(viewportHeight, (val, oldVal) => {
|
|
calculateVisibleItems();
|
|
if (val < oldVal) requestAnimationFrame(() => {
|
|
scrollVelocity = 0;
|
|
calculateVisibleItems();
|
|
});
|
|
});
|
|
let scrollTimeout = -1;
|
|
function handleScroll() {
|
|
if (!containerRef.value || !markerRef.value) return;
|
|
const scrollTop = containerRef.value.scrollTop;
|
|
const scrollTime = performance.now();
|
|
if (scrollTime - lastScrollTime > 500) {
|
|
scrollVelocity = Math.sign(scrollTop - lastScrollTop);
|
|
markerOffset = markerRef.value.offsetTop;
|
|
} else scrollVelocity = scrollTop - lastScrollTop;
|
|
lastScrollTop = scrollTop;
|
|
lastScrollTime = scrollTime;
|
|
window.clearTimeout(scrollTimeout);
|
|
scrollTimeout = window.setTimeout(handleScrollend, 500);
|
|
calculateVisibleItems();
|
|
}
|
|
function handleScrollend() {
|
|
if (!containerRef.value || !markerRef.value) return;
|
|
scrollVelocity = 0;
|
|
lastScrollTime = 0;
|
|
window.clearTimeout(scrollTimeout);
|
|
calculateVisibleItems();
|
|
}
|
|
let raf = -1;
|
|
function calculateVisibleItems() {
|
|
cancelAnimationFrame(raf);
|
|
raf = requestAnimationFrame(_calculateVisibleItems);
|
|
}
|
|
function _calculateVisibleItems() {
|
|
if (!containerRef.value || !viewportHeight.value || !itemHeight.value) return;
|
|
const scrollTop = lastScrollTop - markerOffset;
|
|
const direction = Math.sign(scrollVelocity);
|
|
const start = clamp(calculateIndex(Math.max(0, scrollTop - BUFFER_PX)), 0, items.value.length);
|
|
const end = clamp(calculateIndex(scrollTop + viewportHeight.value + BUFFER_PX) + 1, start + 1, items.value.length);
|
|
if ((direction !== UP || start < first.value) && (direction !== DOWN || end > last.value)) {
|
|
const topOverflow = calculateOffset(first.value) - calculateOffset(start);
|
|
const bottomOverflow = calculateOffset(end) - calculateOffset(last.value);
|
|
if (Math.max(topOverflow, bottomOverflow) > BUFFER_PX) {
|
|
first.value = start;
|
|
last.value = end;
|
|
} else {
|
|
if (start <= 0) first.value = start;
|
|
if (end >= items.value.length) last.value = end;
|
|
}
|
|
}
|
|
paddingTop.value = calculateOffset(first.value);
|
|
paddingBottom.value = calculateOffset(items.value.length) - calculateOffset(last.value);
|
|
}
|
|
function scrollToIndex(index) {
|
|
const offset = calculateOffset(index);
|
|
if (!containerRef.value || index && !offset) targetScrollIndex = index;
|
|
else containerRef.value.scrollTop = offset;
|
|
}
|
|
const computedItems = computed(() => {
|
|
return items.value.slice(first.value, last.value).map((item, index) => {
|
|
const _index = index + first.value;
|
|
return {
|
|
raw: item,
|
|
index: _index,
|
|
key: getPropertyFromItem(item, props.itemKey, _index)
|
|
};
|
|
});
|
|
});
|
|
watch(items, () => {
|
|
sizes = Array.from({ length: items.value.length });
|
|
offsets = Array.from({ length: items.value.length });
|
|
updateOffsets.immediate();
|
|
calculateVisibleItems();
|
|
}, { deep: 1 });
|
|
return {
|
|
calculateVisibleItems,
|
|
containerRef,
|
|
markerRef,
|
|
computedItems,
|
|
paddingTop,
|
|
paddingBottom,
|
|
scrollToIndex,
|
|
handleScroll,
|
|
handleScrollend,
|
|
handleItemResize
|
|
};
|
|
}
|
|
function binaryClosest(arr, val) {
|
|
let high = arr.length - 1;
|
|
let low = 0;
|
|
let mid = 0;
|
|
let item = null;
|
|
let target = -1;
|
|
if (arr[high] < val) return high;
|
|
while (low <= high) {
|
|
mid = low + high >> 1;
|
|
item = arr[mid];
|
|
if (item > val) high = mid - 1;
|
|
else if (item < val) {
|
|
target = mid;
|
|
low = mid + 1;
|
|
} else if (item === val) return mid;
|
|
else return low;
|
|
}
|
|
return target;
|
|
}
|
|
//#endregion
|
|
//#region node_modules/vuetify/lib/components/VVirtualScroll/VVirtualScroll.js
|
|
var makeVVirtualScrollProps = propsFactory({
|
|
items: {
|
|
type: Array,
|
|
default: () => []
|
|
},
|
|
renderless: Boolean,
|
|
...makeVirtualProps(),
|
|
...makeComponentProps(),
|
|
...makeDimensionProps()
|
|
}, "VVirtualScroll");
|
|
var VVirtualScroll = genericComponent()({
|
|
name: "VVirtualScroll",
|
|
props: makeVVirtualScrollProps(),
|
|
setup(props, { slots }) {
|
|
const vm = getCurrentInstance("VVirtualScroll");
|
|
const { dimensionStyles } = useDimension(props);
|
|
const { calculateVisibleItems, containerRef, markerRef, handleScroll, handleScrollend, handleItemResize, scrollToIndex, paddingTop, paddingBottom, computedItems } = useVirtual(props, toRef(() => props.items));
|
|
useToggleScope(() => props.renderless, () => {
|
|
function handleListeners(add = false) {
|
|
const method = add ? "addEventListener" : "removeEventListener";
|
|
if (!IN_BROWSER) return;
|
|
if (containerRef.value === document.documentElement) {
|
|
document[method]("scroll", handleScroll, { passive: true });
|
|
document[method]("scrollend", handleScrollend);
|
|
} else {
|
|
containerRef.value?.[method]("scroll", handleScroll, { passive: true });
|
|
containerRef.value?.[method]("scrollend", handleScrollend);
|
|
}
|
|
}
|
|
onMounted(() => {
|
|
containerRef.value = getScrollParent(vm.vnode.el, true);
|
|
handleListeners(true);
|
|
});
|
|
onScopeDispose(handleListeners);
|
|
});
|
|
useRender(() => {
|
|
const children = computedItems.value.map((item) => createVNode(VVirtualScrollItem, {
|
|
"key": item.key,
|
|
"renderless": props.renderless,
|
|
"onUpdate:height": (height) => handleItemResize(item.index, height)
|
|
}, { default: (slotProps) => slots.default?.({
|
|
item: item.raw,
|
|
index: item.index,
|
|
...slotProps
|
|
}) }));
|
|
return props.renderless ? createBaseVNode(Fragment, null, [
|
|
createBaseVNode("div", {
|
|
"ref": markerRef,
|
|
"class": "v-virtual-scroll__spacer",
|
|
"style": { paddingTop: convertToUnit(paddingTop.value) }
|
|
}, null),
|
|
children,
|
|
createBaseVNode("div", {
|
|
"class": "v-virtual-scroll__spacer",
|
|
"style": { paddingBottom: convertToUnit(paddingBottom.value) }
|
|
}, null)
|
|
]) : createBaseVNode("div", {
|
|
"ref": containerRef,
|
|
"class": normalizeClass(["v-virtual-scroll", props.class]),
|
|
"onScrollPassive": handleScroll,
|
|
"onScrollend": handleScrollend,
|
|
"style": normalizeStyle([dimensionStyles.value, props.style])
|
|
}, [createBaseVNode("div", {
|
|
"ref": markerRef,
|
|
"class": "v-virtual-scroll__container",
|
|
"style": {
|
|
paddingTop: convertToUnit(paddingTop.value),
|
|
paddingBottom: convertToUnit(paddingBottom.value)
|
|
}
|
|
}, [children])]);
|
|
});
|
|
return {
|
|
calculateVisibleItems,
|
|
scrollToIndex
|
|
};
|
|
}
|
|
});
|
|
//#endregion
|
|
//#region node_modules/vuetify/lib/components/VSelect/useScrolling.js
|
|
function useScrolling(listRef, textFieldRef) {
|
|
const isScrolling = shallowRef(false);
|
|
let scrollTimeout;
|
|
function onListScroll(e) {
|
|
cancelAnimationFrame(scrollTimeout);
|
|
isScrolling.value = true;
|
|
scrollTimeout = requestAnimationFrame(() => {
|
|
scrollTimeout = requestAnimationFrame(() => {
|
|
isScrolling.value = false;
|
|
});
|
|
});
|
|
}
|
|
async function finishScrolling() {
|
|
await new Promise((resolve) => requestAnimationFrame(resolve));
|
|
await new Promise((resolve) => requestAnimationFrame(resolve));
|
|
await new Promise((resolve) => requestAnimationFrame(resolve));
|
|
await new Promise((resolve) => {
|
|
if (isScrolling.value) {
|
|
const stop = watch(isScrolling, () => {
|
|
stop();
|
|
resolve();
|
|
});
|
|
} else resolve();
|
|
});
|
|
}
|
|
async function onListKeydown(e) {
|
|
if (e.key === "Tab") textFieldRef.value?.focus();
|
|
if (![
|
|
"PageDown",
|
|
"PageUp",
|
|
"Home",
|
|
"End"
|
|
].includes(e.key)) return;
|
|
const el = listRef.value?.$el;
|
|
if (!el) return;
|
|
if (e.key === "Home" || e.key === "End") el.scrollTo({
|
|
top: e.key === "Home" ? 0 : el.scrollHeight,
|
|
behavior: "smooth"
|
|
});
|
|
await finishScrolling();
|
|
const children = el.querySelectorAll(":scope > :not(.v-virtual-scroll__spacer)");
|
|
if (e.key === "PageDown" || e.key === "Home") {
|
|
const top = el.getBoundingClientRect().top;
|
|
for (const child of children) if (child.getBoundingClientRect().top >= top) {
|
|
child.focus();
|
|
break;
|
|
}
|
|
} else {
|
|
const bottom = el.getBoundingClientRect().bottom;
|
|
for (const child of [...children].reverse()) if (child.getBoundingClientRect().bottom <= bottom) {
|
|
child.focus();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return {
|
|
onScrollPassive: onListScroll,
|
|
onKeydown: onListKeydown
|
|
};
|
|
}
|
|
//#endregion
|
|
//#region node_modules/vuetify/lib/composables/focusGroups.js
|
|
function useFocusGroups({ groups, onLeave }) {
|
|
function getContentRef(group) {
|
|
return group.type === "list" ? group.contentRef.value?.$el : group.contentRef.value;
|
|
}
|
|
function getChildren(group) {
|
|
const contentRef = getContentRef(group);
|
|
return contentRef ? focusableChildren(contentRef) : [];
|
|
}
|
|
function onTabKeydown(e) {
|
|
const target = e.target;
|
|
const direction = e.shiftKey ? "backward" : "forward";
|
|
const children = groups.map(getChildren);
|
|
const currentGroupIndex = groups.map((g) => g.type === "list" ? g.contentRef.value?.$el : g.contentRef.value).findIndex((el) => el?.contains(target));
|
|
const nextIndex = nextFocusGroup(children, currentGroupIndex, direction, target);
|
|
if (nextIndex === null) {
|
|
const originGroup = groups[currentGroupIndex];
|
|
const origin = children[currentGroupIndex];
|
|
if (originGroup.type === "list" || (direction === "forward" ? origin.at(-1) === e.target : origin.at(0) === e.target)) onLeave();
|
|
} else {
|
|
e.preventDefault();
|
|
e.stopImmediatePropagation();
|
|
const nextGroup = groups[nextIndex];
|
|
if (nextGroup.type === "list" && toValue(nextGroup.displayItemsCount) > 0) nextGroup.contentRef.value?.focus(0);
|
|
else {
|
|
const fromBefore = direction === "forward";
|
|
children[nextIndex].at(fromBefore ? 0 : -1).focus();
|
|
}
|
|
}
|
|
}
|
|
function nextFocusGroup(children, currentIndex, direction, target) {
|
|
const originGroup = groups[currentIndex];
|
|
const origin = children[currentIndex];
|
|
if (originGroup.type !== "list") {
|
|
if (!(direction === "forward" ? origin.at(-1) === target : origin.at(0) === target)) return null;
|
|
}
|
|
const step = direction === "forward" ? 1 : -1;
|
|
for (let i = currentIndex + step; i >= 0 && i < groups.length; i += step) {
|
|
const group = groups[i];
|
|
if (children[i].length > 0 || group.type === "list" && toValue(group.displayItemsCount) > 0) return i;
|
|
}
|
|
return null;
|
|
}
|
|
return { onTabKeydown };
|
|
}
|
|
//#endregion
|
|
//#region node_modules/vuetify/lib/composables/filter.js
|
|
/**
|
|
* - boolean: match without highlight
|
|
* - number: single match (index), length already known
|
|
* - []: single match (start, end)
|
|
* - [][]: multiple matches (start, end), shouldn't overlap
|
|
*/
|
|
var defaultFilter = (value, query, item) => {
|
|
if (value == null || query == null) return -1;
|
|
if (!query.length) return 0;
|
|
value = value.toString().toLocaleLowerCase();
|
|
query = query.toString().toLocaleLowerCase();
|
|
const result = [];
|
|
let idx = value.indexOf(query);
|
|
while (~idx) {
|
|
result.push([idx, idx + query.length]);
|
|
idx = value.indexOf(query, idx + query.length);
|
|
}
|
|
return result.length ? result : -1;
|
|
};
|
|
function normaliseMatch(match, query) {
|
|
if (match == null || typeof match === "boolean" || match === -1) return;
|
|
if (typeof match === "number") return [[match, match + query.length]];
|
|
if (Array.isArray(match[0])) return match;
|
|
return [match];
|
|
}
|
|
var makeFilterProps = propsFactory({
|
|
customFilter: Function,
|
|
customKeyFilter: Object,
|
|
filterKeys: [Array, String],
|
|
filterMode: {
|
|
type: String,
|
|
default: "intersection"
|
|
},
|
|
noFilter: Boolean
|
|
}, "filter");
|
|
function filterItems(items, query, options) {
|
|
const array = [];
|
|
const filter = options?.default ?? defaultFilter;
|
|
const keys = options?.filterKeys ? wrapInArray(options.filterKeys) : false;
|
|
const customFiltersLength = Object.keys(options?.customKeyFilter ?? {}).length;
|
|
if (!items?.length) return array;
|
|
let lookAheadItems = [];
|
|
loop: for (let i = 0; i < items.length; i++) {
|
|
const [item, transformed = item] = wrapInArray(items[i]);
|
|
const customMatches = {};
|
|
const defaultMatches = {};
|
|
let match = -1;
|
|
if ((query || customFiltersLength > 0) && !options?.noFilter) {
|
|
let hasOnlyCustomFilters = false;
|
|
if (typeof item === "object") {
|
|
if (item.type === "divider" || item.type === "subheader") {
|
|
if (lookAheadItems.at(-1)?.type !== "divider" || item.type !== "subheader") lookAheadItems = [];
|
|
lookAheadItems.push({
|
|
index: i,
|
|
matches: {},
|
|
type: item.type
|
|
});
|
|
continue;
|
|
}
|
|
const filterKeys = keys || Object.keys(transformed);
|
|
hasOnlyCustomFilters = filterKeys.length === customFiltersLength;
|
|
for (const key of filterKeys) {
|
|
const value = getPropertyFromItem(transformed, key);
|
|
const keyFilter = options?.customKeyFilter?.[key];
|
|
match = keyFilter ? keyFilter(value, query, item) : filter(value, query, item);
|
|
if (match !== -1 && match !== false) if (keyFilter) customMatches[key] = normaliseMatch(match, query);
|
|
else defaultMatches[key] = normaliseMatch(match, query);
|
|
else if (options?.filterMode === "every") continue loop;
|
|
}
|
|
} else {
|
|
match = filter(item, query, item);
|
|
if (match !== -1 && match !== false) defaultMatches.title = normaliseMatch(match, query);
|
|
}
|
|
const defaultMatchesLength = Object.keys(defaultMatches).length;
|
|
const customMatchesLength = Object.keys(customMatches).length;
|
|
if (!defaultMatchesLength && !customMatchesLength) continue;
|
|
if (options?.filterMode === "union" && customMatchesLength !== customFiltersLength && !defaultMatchesLength) continue;
|
|
if (options?.filterMode === "intersection" && (customMatchesLength !== customFiltersLength || !defaultMatchesLength && customFiltersLength > 0 && !hasOnlyCustomFilters)) continue;
|
|
}
|
|
if (lookAheadItems.length) {
|
|
array.push(...lookAheadItems);
|
|
lookAheadItems = [];
|
|
}
|
|
array.push({
|
|
index: i,
|
|
matches: {
|
|
...defaultMatches,
|
|
...customMatches
|
|
}
|
|
});
|
|
}
|
|
return array;
|
|
}
|
|
function useFilter(props, items, query, options) {
|
|
const filteredItems = shallowRef([]);
|
|
const filteredMatches = shallowRef(/* @__PURE__ */ new Map());
|
|
const transformedItems = computed(() => options?.transform ? unref(items).map((item) => [item, options.transform(item)]) : unref(items));
|
|
watchEffect(() => {
|
|
const _query = typeof query === "function" ? query() : unref(query);
|
|
const strQuery = typeof _query !== "string" && typeof _query !== "number" ? "" : String(_query);
|
|
const results = filterItems(transformedItems.value, strQuery, {
|
|
customKeyFilter: {
|
|
...props.customKeyFilter,
|
|
...unref(options?.customKeyFilter)
|
|
},
|
|
default: props.customFilter,
|
|
filterKeys: props.filterKeys,
|
|
filterMode: props.filterMode,
|
|
noFilter: props.noFilter
|
|
});
|
|
const originalItems = unref(items);
|
|
const _filteredItems = [];
|
|
const _filteredMatches = /* @__PURE__ */ new Map();
|
|
results.forEach(({ index, matches }) => {
|
|
const item = originalItems[index];
|
|
_filteredItems.push(item);
|
|
_filteredMatches.set(item.value, matches);
|
|
});
|
|
filteredItems.value = _filteredItems;
|
|
filteredMatches.value = _filteredMatches;
|
|
});
|
|
function getMatches(item) {
|
|
return filteredMatches.value.get(item.value);
|
|
}
|
|
return {
|
|
filteredItems,
|
|
filteredMatches,
|
|
getMatches
|
|
};
|
|
}
|
|
function highlightResult(name, text, matches) {
|
|
if (matches == null || !matches.length) return text;
|
|
return matches.map((match, i) => {
|
|
const start = i === 0 ? 0 : matches[i - 1][1];
|
|
const result = [createBaseVNode("span", { "class": normalizeClass(`${name}__unmask`) }, [text.slice(start, match[0])]), createBaseVNode("span", { "class": normalizeClass(`${name}__mask`) }, [text.slice(match[0], match[1])])];
|
|
if (i === matches.length - 1) result.push(createBaseVNode("span", { "class": normalizeClass(`${name}__unmask`) }, [text.slice(match[1])]));
|
|
return createBaseVNode(Fragment, null, [result]);
|
|
});
|
|
}
|
|
//#endregion
|
|
//#region node_modules/vuetify/lib/composables/menuActivator.js
|
|
var makeMenuActivatorProps = propsFactory({
|
|
closeText: {
|
|
type: String,
|
|
default: "$vuetify.close"
|
|
},
|
|
openText: {
|
|
type: String,
|
|
default: "$vuetify.open"
|
|
}
|
|
}, "autocomplete");
|
|
function useMenuActivator(props, isOpen) {
|
|
const uid = useId();
|
|
const menuId = computed(() => `menu-${uid}`);
|
|
return {
|
|
menuId,
|
|
ariaExpanded: toRef(() => toValue(isOpen)),
|
|
ariaControls: toRef(() => menuId.value)
|
|
};
|
|
}
|
|
//#endregion
|
|
//#region node_modules/vuetify/lib/components/VSelect/VSelect.js
|
|
var makeSelectProps = propsFactory({
|
|
chips: Boolean,
|
|
closableChips: Boolean,
|
|
eager: Boolean,
|
|
hideNoData: Boolean,
|
|
hideSelected: Boolean,
|
|
listProps: { type: Object },
|
|
menu: Boolean,
|
|
menuElevation: [Number, String],
|
|
menuIcon: {
|
|
type: IconValue,
|
|
default: "$dropdown"
|
|
},
|
|
menuProps: { type: Object },
|
|
multiple: Boolean,
|
|
noDataText: {
|
|
type: String,
|
|
default: "$vuetify.noDataText"
|
|
},
|
|
openOnClear: Boolean,
|
|
itemColor: String,
|
|
noAutoScroll: Boolean,
|
|
...makeMenuActivatorProps(),
|
|
...makeItemsProps({ itemChildren: false })
|
|
}, "Select");
|
|
var makeVSelectProps = propsFactory({
|
|
search: String,
|
|
...makeFilterProps({ filterKeys: ["title"] }),
|
|
...makeSelectProps(),
|
|
...omit(makeVTextFieldProps({
|
|
modelValue: null,
|
|
role: "combobox"
|
|
}), ["validationValue", "dirty"]),
|
|
...makeTransitionProps({ transition: { component: VDialogTransition } })
|
|
}, "VSelect");
|
|
var VSelect = genericComponent()({
|
|
name: "VSelect",
|
|
props: makeVSelectProps(),
|
|
emits: {
|
|
"update:focused": (focused) => true,
|
|
"update:modelValue": (value) => true,
|
|
"update:menu": (ue) => true,
|
|
"update:search": (value) => true
|
|
},
|
|
setup(props, { slots }) {
|
|
const { t } = useLocale();
|
|
const vTextFieldRef = ref();
|
|
const vMenuRef = ref();
|
|
const headerRef = ref();
|
|
const footerRef = ref();
|
|
const vVirtualScrollRef = ref();
|
|
const { items, transformIn, transformOut } = useItems(props);
|
|
const search = useProxiedModel(props, "search", "");
|
|
const { filteredItems, getMatches } = useFilter(props, items, () => search.value);
|
|
const model = useProxiedModel(props, "modelValue", [], (v) => transformIn(v === null ? [null] : wrapInArray(v)), (v) => {
|
|
const transformed = transformOut(v);
|
|
return props.multiple ? transformed : transformed[0] ?? null;
|
|
});
|
|
const counterValue = computed(() => {
|
|
return typeof props.counterValue === "function" ? props.counterValue(model.value) : typeof props.counterValue === "number" ? props.counterValue : model.value.length;
|
|
});
|
|
const form = useForm(props);
|
|
const autocomplete = useAutocomplete(props);
|
|
const selectedValues = computed(() => model.value.map((selection) => selection.value));
|
|
const isFocused = shallowRef(false);
|
|
const closableChips = toRef(() => props.closableChips && !form.isReadonly.value && !form.isDisabled.value);
|
|
const { InputIcon } = useInputIcon(props);
|
|
let keyboardLookupPrefix = "";
|
|
let keyboardLookupIndex = 0;
|
|
let keyboardLookupLastTime;
|
|
const displayItems = computed(() => {
|
|
const baseItems = search.value ? filteredItems.value : items.value;
|
|
if (props.hideSelected) return baseItems.filter((item) => !model.value.some((s) => (props.valueComparator || deepEqual)(s, item)));
|
|
return baseItems;
|
|
});
|
|
const menuDisabled = computed(() => props.hideNoData && !displayItems.value.length || form.isReadonly.value || form.isDisabled.value);
|
|
const _menu = useProxiedModel(props, "menu");
|
|
const menu = computed({
|
|
get: () => _menu.value,
|
|
set: (v) => {
|
|
if (_menu.value && !v && vMenuRef.value?.ΨopenChildren.size) return;
|
|
if (v && menuDisabled.value) return;
|
|
_menu.value = v;
|
|
}
|
|
});
|
|
const { menuId, ariaExpanded, ariaControls } = useMenuActivator(props, menu);
|
|
const computedMenuProps = computed(() => {
|
|
return {
|
|
...props.menuProps,
|
|
activatorProps: {
|
|
...props.menuProps?.activatorProps || {},
|
|
"aria-haspopup": "listbox"
|
|
}
|
|
};
|
|
});
|
|
const listRef = ref();
|
|
const listEvents = useScrolling(listRef, vTextFieldRef);
|
|
const { onTabKeydown } = useFocusGroups({
|
|
groups: [
|
|
{
|
|
type: "element",
|
|
contentRef: headerRef
|
|
},
|
|
{
|
|
type: "list",
|
|
contentRef: listRef,
|
|
displayItemsCount: () => displayItems.value.length
|
|
},
|
|
{
|
|
type: "element",
|
|
contentRef: footerRef
|
|
}
|
|
],
|
|
onLeave: () => {
|
|
menu.value = false;
|
|
vTextFieldRef.value?.focus();
|
|
}
|
|
});
|
|
function onClear(e) {
|
|
if (props.openOnClear) menu.value = true;
|
|
}
|
|
function onMousedownControl() {
|
|
if (menuDisabled.value) return;
|
|
menu.value = !menu.value;
|
|
}
|
|
function onMenuKeydown(e) {
|
|
if (e.key === "Tab") onTabKeydown(e);
|
|
if (listRef.value?.$el.contains(e.target) && checkPrintable(e)) onKeydown(e);
|
|
}
|
|
function onKeydown(e) {
|
|
if (!e.key || form.isReadonly.value) return;
|
|
if ([
|
|
"Enter",
|
|
" ",
|
|
"ArrowDown",
|
|
"ArrowUp",
|
|
"Home",
|
|
"End"
|
|
].includes(e.key)) e.preventDefault();
|
|
if ([
|
|
"Enter",
|
|
"ArrowDown",
|
|
" "
|
|
].includes(e.key)) menu.value = true;
|
|
if (["Escape", "Tab"].includes(e.key)) menu.value = false;
|
|
if (props.clearable && e.key === "Backspace") {
|
|
e.preventDefault();
|
|
model.value = [];
|
|
onClear(e);
|
|
return;
|
|
}
|
|
if (e.key === "Home") listRef.value?.focus("first");
|
|
else if (e.key === "End") listRef.value?.focus("last");
|
|
const KEYBOARD_LOOKUP_THRESHOLD = 1e3;
|
|
if (!checkPrintable(e)) return;
|
|
const now = performance.now();
|
|
if (now - keyboardLookupLastTime > KEYBOARD_LOOKUP_THRESHOLD) {
|
|
keyboardLookupPrefix = "";
|
|
keyboardLookupIndex = 0;
|
|
}
|
|
keyboardLookupPrefix += e.key.toLowerCase();
|
|
keyboardLookupLastTime = now;
|
|
const items = displayItems.value;
|
|
function findItem() {
|
|
let result = findItemBase();
|
|
if (result) return result;
|
|
if (keyboardLookupPrefix.at(-1) === keyboardLookupPrefix.at(-2)) {
|
|
keyboardLookupPrefix = keyboardLookupPrefix.slice(0, -1);
|
|
keyboardLookupIndex++;
|
|
result = findItemBase();
|
|
if (result) return result;
|
|
}
|
|
keyboardLookupIndex = 0;
|
|
result = findItemBase();
|
|
if (result) return result;
|
|
keyboardLookupPrefix = e.key.toLowerCase();
|
|
return findItemBase();
|
|
}
|
|
function findItemBase() {
|
|
for (let i = keyboardLookupIndex; i < items.length; i++) {
|
|
const _item = items[i];
|
|
if (_item.title.toLowerCase().startsWith(keyboardLookupPrefix)) return [_item, i];
|
|
}
|
|
}
|
|
const result = findItem();
|
|
if (!result) return;
|
|
const [item, index] = result;
|
|
keyboardLookupIndex = index;
|
|
listRef.value?.focus(index);
|
|
if (!props.multiple) model.value = [item];
|
|
}
|
|
/** @param set - null means toggle */
|
|
function select(item, set = true) {
|
|
if (item.props.disabled) return;
|
|
if (props.multiple) {
|
|
const index = model.value.findIndex((selection) => (props.valueComparator || deepEqual)(selection.value, item.value));
|
|
const add = set == null ? !~index : set;
|
|
if (~index) {
|
|
const value = add ? [...model.value, item] : [...model.value];
|
|
value.splice(index, 1);
|
|
model.value = value;
|
|
} else if (add) model.value = [...model.value, item];
|
|
} else {
|
|
model.value = set !== false ? [item] : [];
|
|
nextTick(() => {
|
|
menu.value = false;
|
|
});
|
|
}
|
|
}
|
|
function onBlur(e) {
|
|
const target = e.target;
|
|
if (!vTextFieldRef.value?.$el.contains(target)) menu.value = false;
|
|
}
|
|
function getSelectedIndex() {
|
|
return displayItems.value.findIndex((item) => model.value.some((s) => (props.valueComparator || deepEqual)(s.value, item.value)));
|
|
}
|
|
function getSelectedFocusableIndex() {
|
|
if (!model.value.length) return -1;
|
|
const comparator = props.valueComparator || deepEqual;
|
|
let focusableIndex = 0;
|
|
for (const item of displayItems.value) {
|
|
if (model.value.some((s) => comparator(s.value, item.value))) return item.props.disabled ? -1 : focusableIndex;
|
|
if (!item.props.disabled) focusableIndex++;
|
|
}
|
|
return -1;
|
|
}
|
|
function onAfterEnter() {
|
|
if (props.eager) vVirtualScrollRef.value?.calculateVisibleItems();
|
|
if (listRef.value && isFocused.value) {
|
|
const index = getSelectedFocusableIndex();
|
|
listRef.value.focus(index >= 0 ? index : "first");
|
|
}
|
|
}
|
|
function onAfterLeave() {
|
|
search.value = "";
|
|
if (isFocused.value) vTextFieldRef.value?.focus();
|
|
}
|
|
function onFocusin(e) {
|
|
isFocused.value = true;
|
|
}
|
|
function onFocusout(e) {
|
|
if (!vTextFieldRef.value?.$el.contains(e.relatedTarget) && !e.currentTarget.contains(e.relatedTarget)) isFocused.value = false;
|
|
}
|
|
function onModelUpdate(v) {
|
|
if (v == null) model.value = [];
|
|
else if (matchesSelector(vTextFieldRef.value, ":autofill") || matchesSelector(vTextFieldRef.value, ":-webkit-autofill")) {
|
|
const item = items.value.find((item) => item.title === v);
|
|
if (item) select(item);
|
|
} else if (vTextFieldRef.value) vTextFieldRef.value.value = "";
|
|
}
|
|
watch(menu, () => {
|
|
if (!props.hideSelected && menu.value && model.value.length) {
|
|
const index = getSelectedIndex();
|
|
IN_BROWSER && !props.noAutoScroll && window.requestAnimationFrame(() => {
|
|
index >= 0 && vVirtualScrollRef.value?.scrollToIndex(index);
|
|
});
|
|
}
|
|
});
|
|
watch(items, (newVal, oldVal) => {
|
|
if (menu.value) return;
|
|
if (isFocused.value && props.hideNoData && !oldVal.length && newVal.length) menu.value = true;
|
|
});
|
|
useRender(() => {
|
|
const hasChips = !!(props.chips || slots.chip);
|
|
const hasList = !!(!props.hideNoData || displayItems.value.length || slots["prepend-item"] || slots["append-item"] || slots["no-data"]);
|
|
const isDirty = model.value.length > 0;
|
|
const textFieldProps = VTextField.filterProps(props);
|
|
const placeholder = isDirty || !isFocused.value && props.label && !props.persistentPlaceholder ? void 0 : props.placeholder;
|
|
const menuSlotProps = {
|
|
search,
|
|
filteredItems: filteredItems.value
|
|
};
|
|
return createVNode(VTextField, mergeProps({ "ref": vTextFieldRef }, textFieldProps, {
|
|
"modelValue": model.value.map((v) => v.props.title).join(", "),
|
|
"name": void 0,
|
|
"onUpdate:modelValue": onModelUpdate,
|
|
"focused": isFocused.value,
|
|
"onUpdate:focused": ($event) => isFocused.value = $event,
|
|
"validationValue": model.externalValue,
|
|
"counterValue": counterValue.value,
|
|
"dirty": isDirty,
|
|
"class": [
|
|
"v-select",
|
|
{
|
|
"v-select--active-menu": menu.value,
|
|
"v-select--chips": !!props.chips,
|
|
[`v-select--${props.multiple ? "multiple" : "single"}`]: true,
|
|
"v-select--selected": model.value.length,
|
|
"v-select--selection-slot": !!slots.selection
|
|
},
|
|
props.class
|
|
],
|
|
"style": props.style,
|
|
"inputmode": "none",
|
|
"placeholder": placeholder,
|
|
"onClick:clear": onClear,
|
|
"onMousedown:control": onMousedownControl,
|
|
"onBlur": onBlur,
|
|
"onKeydown": onKeydown,
|
|
"aria-expanded": ariaExpanded.value,
|
|
"aria-controls": ariaControls.value
|
|
}), {
|
|
...slots,
|
|
default: ({ id }) => createBaseVNode(Fragment, null, [
|
|
createBaseVNode("select", {
|
|
"hidden": true,
|
|
"multiple": props.multiple,
|
|
"name": autocomplete.fieldName.value
|
|
}, [items.value.map((item) => createBaseVNode("option", {
|
|
"key": item.value,
|
|
"value": item.value,
|
|
"selected": selectedValues.value.includes(item.value)
|
|
}, null))]),
|
|
createVNode(VMenu, mergeProps({
|
|
"id": menuId.value,
|
|
"ref": vMenuRef,
|
|
"modelValue": menu.value,
|
|
"onUpdate:modelValue": ($event) => menu.value = $event,
|
|
"activator": "parent",
|
|
"contentClass": "v-select__content",
|
|
"disabled": menuDisabled.value,
|
|
"eager": props.eager,
|
|
"maxHeight": 310,
|
|
"openOnClick": false,
|
|
"closeOnContentClick": false,
|
|
"transition": props.transition,
|
|
"onAfterEnter": onAfterEnter,
|
|
"onAfterLeave": onAfterLeave
|
|
}, computedMenuProps.value), { default: () => [createVNode(VSheet, {
|
|
"elevation": props.menuElevation,
|
|
"onFocusin": onFocusin,
|
|
"onFocusout": onFocusout,
|
|
"onKeydown": onMenuKeydown
|
|
}, { default: () => [
|
|
slots["menu-header"] && createBaseVNode("header", { "ref": headerRef }, [slots["menu-header"](menuSlotProps)]),
|
|
hasList && createVNode(VList, mergeProps({
|
|
"key": "select-list",
|
|
"ref": listRef,
|
|
"selected": selectedValues.value,
|
|
"selectStrategy": props.multiple ? "independent" : "single-independent",
|
|
"tabindex": "-1",
|
|
"selectable": !!displayItems.value.length,
|
|
"aria-live": "polite",
|
|
"aria-labelledby": `${id.value}-label`,
|
|
"aria-multiselectable": props.multiple,
|
|
"color": props.itemColor ?? props.color
|
|
}, listEvents, props.listProps), { default: () => [
|
|
slots["prepend-item"]?.(),
|
|
!displayItems.value.length && !props.hideNoData && (slots["no-data"]?.() ?? createVNode(VListItem, {
|
|
"key": "no-data",
|
|
"title": t(props.noDataText)
|
|
}, null)),
|
|
createVNode(VVirtualScroll, {
|
|
"ref": vVirtualScrollRef,
|
|
"renderless": true,
|
|
"items": displayItems.value,
|
|
"itemKey": "value"
|
|
}, { default: ({ item, index, itemRef }) => {
|
|
const camelizedProps = camelizeProps(item.props);
|
|
const itemProps = mergeProps(item.props, {
|
|
ref: itemRef,
|
|
key: item.value,
|
|
onClick: () => select(item, null),
|
|
"aria-posinset": index + 1,
|
|
"aria-setsize": displayItems.value.length
|
|
});
|
|
if (item.type === "divider") return slots.divider?.({
|
|
props: item.raw,
|
|
index
|
|
}) ?? createVNode(VDivider, mergeProps(item.props, { "key": `divider-${index}` }), null);
|
|
if (item.type === "subheader") return slots.subheader?.({
|
|
props: item.raw,
|
|
index
|
|
}) ?? createVNode(VListSubheader, mergeProps(item.props, { "key": `subheader-${index}` }), null);
|
|
return slots.item?.({
|
|
item: item.raw,
|
|
internalItem: item,
|
|
index,
|
|
props: itemProps
|
|
}) ?? createVNode(VListItem, mergeProps(itemProps, { "role": "option" }), {
|
|
prepend: ({ isSelected }) => createBaseVNode(Fragment, null, [
|
|
props.multiple && !props.hideSelected ? createVNode(VCheckboxBtn, {
|
|
"key": item.value,
|
|
"modelValue": isSelected,
|
|
"ripple": false,
|
|
"tabindex": "-1",
|
|
"aria-hidden": true,
|
|
"onClick": (event) => event.preventDefault()
|
|
}, null) : void 0,
|
|
camelizedProps.prependAvatar && createVNode(VAvatar, { "image": camelizedProps.prependAvatar }, null),
|
|
camelizedProps.prependIcon && createVNode(VIcon, { "icon": camelizedProps.prependIcon }, null)
|
|
]),
|
|
title: () => {
|
|
return search.value ? highlightResult("v-select", item.title, getMatches(item)?.title) : item.title;
|
|
}
|
|
});
|
|
} }),
|
|
slots["append-item"]?.()
|
|
] }),
|
|
slots["menu-footer"] && createBaseVNode("footer", { "ref": footerRef }, [slots["menu-footer"](menuSlotProps)])
|
|
] })] }),
|
|
model.value.map((item, index) => {
|
|
function onChipClose(e) {
|
|
e.stopPropagation();
|
|
e.preventDefault();
|
|
select(item, false);
|
|
}
|
|
const slotProps = mergeProps(VChip.filterProps(item.props), {
|
|
"onClick:close": onChipClose,
|
|
onKeydown(e) {
|
|
if (e.key !== "Enter" && e.key !== " ") return;
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
onChipClose(e);
|
|
},
|
|
onMousedown(e) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
},
|
|
modelValue: true,
|
|
"onUpdate:modelValue": void 0
|
|
});
|
|
const hasSlot = hasChips ? !!slots.chip : !!slots.selection;
|
|
const slotContent = hasSlot ? ensureValidVNode(hasChips ? slots.chip({
|
|
item: item.raw,
|
|
internalItem: item,
|
|
index,
|
|
props: slotProps
|
|
}) : slots.selection({
|
|
item: item.raw,
|
|
internalItem: item,
|
|
index
|
|
})) : void 0;
|
|
if (hasSlot && !slotContent) return void 0;
|
|
return createBaseVNode("div", {
|
|
"key": item.value,
|
|
"class": "v-select__selection"
|
|
}, [hasChips ? !slots.chip ? createVNode(VChip, mergeProps({
|
|
"key": "chip",
|
|
"closable": closableChips.value,
|
|
"size": "small",
|
|
"text": item.title,
|
|
"disabled": item.props.disabled
|
|
}, slotProps), null) : createVNode(VDefaultsProvider, {
|
|
"key": "chip-defaults",
|
|
"defaults": { VChip: {
|
|
closable: closableChips.value,
|
|
size: "small",
|
|
text: item.title
|
|
} }
|
|
}, { default: () => [slotContent] }) : slotContent ?? createBaseVNode("span", { "class": "v-select__selection-text" }, [item.title, props.multiple && index < model.value.length - 1 && createBaseVNode("span", { "class": "v-select__selection-comma" }, [createTextVNode(",")])])]);
|
|
})
|
|
]),
|
|
"append-inner": (...args) => createBaseVNode(Fragment, null, [
|
|
slots["append-inner"]?.(...args),
|
|
props.menuIcon ? createVNode(VIcon, {
|
|
"class": "v-select__menu-icon",
|
|
"color": vTextFieldRef.value?.fieldIconColor,
|
|
"icon": props.menuIcon,
|
|
"aria-hidden": true
|
|
}, null) : void 0,
|
|
props.appendInnerIcon && createVNode(InputIcon, {
|
|
"key": "append-icon",
|
|
"name": "appendInner",
|
|
"color": args[0].iconColor.value
|
|
}, null)
|
|
])
|
|
});
|
|
});
|
|
return forwardRefs({
|
|
isFocused,
|
|
menu,
|
|
search,
|
|
filteredItems,
|
|
select
|
|
}, vTextFieldRef);
|
|
}
|
|
});
|
|
//#endregion
|
|
export { VSelect };
|
|
|
|
//# sourceMappingURL=vuetify_components_VSelect.js.map
|