vuetify
Version:
Vue Material Component Framework
118 lines (117 loc) • 4.5 kB
JavaScript
import { mergeProps as _mergeProps, createVNode as _createVNode } from "vue";
// Styles
import "./VTab.css";
// Components
import { VBtn } from "../VBtn/index.mjs"; // Composables
import { IconValue } from "../../composables/icons.mjs";
import { makeGroupItemProps } from "../../composables/group.mjs";
import { makeRouterProps } from "../../composables/router.mjs";
import { makeTagProps } from "../../composables/tag.mjs";
import { makeThemeProps } from "../../composables/theme.mjs";
import { useTextColor } from "../../composables/color.mjs"; // Utilities
import { computed, ref } from 'vue';
import { animate, genericComponent, pick, standardEasing, useRender } from "../../util/index.mjs"; // Types
import { VTabsSymbol } from "./shared.mjs";
export const VTab = genericComponent()({
name: 'VTab',
props: {
fixed: Boolean,
icon: [Boolean, String, Function, Object],
prependIcon: IconValue,
appendIcon: IconValue,
stacked: Boolean,
title: String,
ripple: {
type: Boolean,
default: true
},
color: String,
sliderColor: String,
hideSlider: Boolean,
direction: {
type: String,
default: 'horizontal'
},
...makeTagProps(),
...makeRouterProps(),
...makeGroupItemProps({
selectedClass: 'v-tab--selected'
}),
...makeThemeProps()
},
setup(props, _ref) {
let {
slots,
attrs
} = _ref;
const {
textColorClasses: sliderColorClasses,
textColorStyles: sliderColorStyles
} = useTextColor(props, 'sliderColor');
const isHorizontal = computed(() => props.direction === 'horizontal');
const isSelected = ref(false);
const rootEl = ref();
const sliderEl = ref();
function updateSlider(_ref2) {
let {
value
} = _ref2;
isSelected.value = value;
if (value) {
const prevEl = rootEl.value?.$el.parentElement?.querySelector('.v-tab--selected .v-tab__slider');
const nextEl = sliderEl.value;
if (!prevEl || !nextEl) return;
const color = getComputedStyle(prevEl).color;
const prevBox = prevEl.getBoundingClientRect();
const nextBox = nextEl.getBoundingClientRect();
const xy = isHorizontal.value ? 'x' : 'y';
const XY = isHorizontal.value ? 'X' : 'Y';
const rightBottom = isHorizontal.value ? 'right' : 'bottom';
const widthHeight = isHorizontal.value ? 'width' : 'height';
const prevPos = prevBox[xy];
const nextPos = nextBox[xy];
const delta = prevPos > nextPos ? prevBox[rightBottom] - nextBox[rightBottom] : prevBox[xy] - nextBox[xy];
const origin = Math.sign(delta) > 0 ? isHorizontal.value ? 'right' : 'bottom' : Math.sign(delta) < 0 ? isHorizontal.value ? 'left' : 'top' : 'center';
const size = Math.abs(delta) + (Math.sign(delta) < 0 ? prevBox[widthHeight] : nextBox[widthHeight]);
const scale = size / Math.max(prevBox[widthHeight], nextBox[widthHeight]);
const initialScale = prevBox[widthHeight] / nextBox[widthHeight];
const sigma = 1.5;
animate(nextEl, {
backgroundColor: [color, ''],
transform: [`translate${XY}(${delta}px) scale${XY}(${initialScale})`, `translate${XY}(${delta / sigma}px) scale${XY}(${(scale - 1) / sigma + 1})`, ''],
transformOrigin: Array(3).fill(origin)
}, {
duration: 225,
easing: standardEasing
});
}
}
useRender(() => {
const [btnProps] = pick(props, ['href', 'to', 'replace', 'icon', 'stacked', 'prependIcon', 'appendIcon', 'ripple', 'theme', 'disabled', 'selectedClass', 'value', 'color']);
return _createVNode(VBtn, _mergeProps({
"_as": "VTab",
"symbol": VTabsSymbol,
"ref": rootEl,
"class": ['v-tab'],
"tabindex": isSelected.value ? 0 : -1,
"role": "tab",
"aria-selected": String(isSelected.value),
"active": false,
"block": props.fixed,
"maxWidth": props.fixed ? 300 : undefined,
"variant": "text",
"rounded": 0
}, btnProps, attrs, {
"onGroup:selected": updateSlider
}), {
default: () => [slots.default ? slots.default() : props.title, !props.hideSlider && _createVNode("div", {
"ref": sliderEl,
"class": ['v-tab__slider', sliderColorClasses.value],
"style": sliderColorStyles.value
}, null)]
});
});
return {};
}
});
//# sourceMappingURL=VTab.mjs.map