upload project source code
This commit is contained in:
576
前端源码/uni-app/components/MysticDatePicker.vue
Normal file
576
前端源码/uni-app/components/MysticDatePicker.vue
Normal file
@@ -0,0 +1,576 @@
|
||||
<template>
|
||||
<transition name="mystic-date-picker">
|
||||
<div v-if="isOpen" class="mystic-date-picker-overlay">
|
||||
<!-- Backdrop -->
|
||||
<div class="mystic-date-picker-backdrop" @click="handleClose"></div>
|
||||
|
||||
<!-- Modal Content -->
|
||||
<div class="mystic-date-picker-modal">
|
||||
<!-- Header -->
|
||||
<div class="mystic-date-picker-header">
|
||||
<div class="mystic-date-picker-close" @click="handleClose">
|
||||
<CloseIcon :size="22" class="mystic-date-picker-icon" />
|
||||
</div>
|
||||
<span class="mystic-date-picker-title">{{ title }}</span>
|
||||
<div class="mystic-date-picker-confirm" @click="handleConfirm">
|
||||
<CheckIcon :size="22" class="mystic-date-picker-icon" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Picker View -->
|
||||
<div class="mystic-date-picker-view">
|
||||
<div class="mystic-date-picker-indicator"></div>
|
||||
|
||||
<!-- Year Column -->
|
||||
<div class="mystic-date-picker-column" @scroll="handleScroll($event, 0)">
|
||||
<div class="mystic-date-picker-padding"></div>
|
||||
<div v-for="(y, idx) in years" :key="y" :ref="el => setColumnRef(el, 0, idx)"
|
||||
class="mystic-date-picker-item" :class="{ active: pickerValue[0] === idx }" @click="selectItem(0, idx)">
|
||||
<span>{{ y }}年</span>
|
||||
</div>
|
||||
<div class="mystic-date-picker-padding"></div>
|
||||
</div>
|
||||
|
||||
<!-- Month Column -->
|
||||
<div class="mystic-date-picker-column" @scroll="handleScroll($event, 1)">
|
||||
<div class="mystic-date-picker-padding"></div>
|
||||
<div v-for="(m, idx) in months" :key="idx" :ref="el => setColumnRef(el, 1, idx)"
|
||||
class="mystic-date-picker-item" :class="{ active: pickerValue[1] === idx }" @click="selectItem(1, idx)">
|
||||
<span>{{ m }}</span>
|
||||
</div>
|
||||
<div class="mystic-date-picker-padding"></div>
|
||||
</div>
|
||||
|
||||
<!-- Day Column -->
|
||||
<div class="mystic-date-picker-column" @scroll="handleScroll($event, 2)">
|
||||
<div class="mystic-date-picker-padding"></div>
|
||||
<div v-for="(d, idx) in days" :key="d.val" :ref="el => setColumnRef(el, 2, idx)"
|
||||
class="mystic-date-picker-item" :class="{ active: pickerValue[2] === idx }" @click="selectItem(2, idx)">
|
||||
<span>{{ d.name }}</span>
|
||||
</div>
|
||||
<div class="mystic-date-picker-padding"></div>
|
||||
</div>
|
||||
|
||||
<!-- Time Column -->
|
||||
<div class="mystic-date-picker-column" @scroll="handleScroll($event, 3)">
|
||||
<div class="mystic-date-picker-padding"></div>
|
||||
<div v-for="(s, idx) in shichenOptions" :key="s.id" :ref="el => setColumnRef(el, 3, idx)"
|
||||
class="mystic-date-picker-item mystic-date-picker-item-time" :class="{ active: pickerValue[3] === idx }"
|
||||
@click="selectItem(3, idx)">
|
||||
<span class="mystic-date-picker-time-name">{{ s.name }}</span>
|
||||
<span class="mystic-date-picker-time-detail">{{ s.time }}</span>
|
||||
</div>
|
||||
<div class="mystic-date-picker-padding"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Footer Tip -->
|
||||
<div class="mystic-date-picker-footer">
|
||||
<span class="mystic-date-picker-tip">{{ footerTip || '滑动列表选择 · 系统自动换算干支' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, watch, nextTick } from 'vue';
|
||||
import CloseIcon from './icons/CloseIcon.vue';
|
||||
import CheckIcon from './icons/CheckIcon.vue';
|
||||
|
||||
interface Props {
|
||||
isOpen: boolean;
|
||||
title?: string;
|
||||
defaultValue?: string;
|
||||
/** 年份列下界(公历,含)。与 maxYear 同时用于自定义区间(如择吉期望范围可选至未来多年) */
|
||||
minYear?: number;
|
||||
/** 年份列上界(公历,含) */
|
||||
maxYear?: number;
|
||||
/** 底部提示,便于与其它场景日期选择区分 */
|
||||
footerTip?: string;
|
||||
/** 为 true 时公历日期不可晚于「今天」(用于择吉期望开始日等) */
|
||||
capAtToday?: boolean;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
title: '请择良辰',
|
||||
defaultValue: '',
|
||||
minYear: undefined,
|
||||
maxYear: undefined,
|
||||
footerTip: '',
|
||||
capAtToday: false,
|
||||
});
|
||||
|
||||
const emit = defineEmits<{
|
||||
close: [];
|
||||
confirm: [val: string, apiVal: string];
|
||||
}>();
|
||||
|
||||
// 时辰对照表
|
||||
const SHI_CHEN = [
|
||||
{ id: 'zi', name: '子时', time: '23:00-00:59', hour: '00:00:00' },
|
||||
{ id: 'chou', name: '丑时', time: '01:00-02:59', hour: '02:00:00' },
|
||||
{ id: 'yin', name: '寅时', time: '03:00-04:59', hour: '04:00:00' },
|
||||
{ id: 'mao', name: '卯时', time: '05:00-06:59', hour: '06:00:00' },
|
||||
{ id: 'chen', name: '辰时', time: '07:00-08:59', hour: '08:00:00' },
|
||||
{ id: 'si', name: '巳时', time: '09:00-10:59', hour: '10:00:00' },
|
||||
{ id: 'wu', name: '午时', time: '11:00-12:59', hour: '12:00:00' },
|
||||
{ id: 'wei', name: '未时', time: '13:00-14:59', hour: '14:00:00' },
|
||||
{ id: 'shen', name: '申时', time: '15:00-16:59', hour: '16:00:00' },
|
||||
{ id: 'you', name: '酉时', time: '17:00-18:59', hour: '18:00:00' },
|
||||
{ id: 'xu', name: '戌时', time: '19:00-20:59', hour: '20:00:00' },
|
||||
{ id: 'hai', name: '亥时', time: '21:00-22:59', hour: '22:00:00' },
|
||||
];
|
||||
|
||||
const CH_NUM = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九', '十'];
|
||||
const MONTHS = ['正月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '冬月', '腊月'];
|
||||
|
||||
/**
|
||||
* 年份列(降序):默认从当年起共 86 年(生辰等场景,不可超过当年);
|
||||
* 若传入 minYear/maxYear 则按该区间生成(用于择吉期望日期等可选未来多年)。
|
||||
*/
|
||||
const years = computed(() => {
|
||||
const nowY = new Date().getFullYear();
|
||||
const useCustom = props.minYear !== undefined || props.maxYear !== undefined;
|
||||
if (!useCustom) {
|
||||
const maxY = nowY;
|
||||
const minY = maxY - 85;
|
||||
return Array.from({ length: 86 }, (_, i) => maxY - i);
|
||||
}
|
||||
const maxY = props.maxYear ?? nowY;
|
||||
const minY = props.minYear ?? maxY - 85;
|
||||
const hi = Math.max(minY, maxY);
|
||||
const lo = Math.min(minY, maxY);
|
||||
return Array.from({ length: hi - lo + 1 }, (_, i) => hi - i);
|
||||
});
|
||||
const months = MONTHS;
|
||||
const shichenOptions = SHI_CHEN;
|
||||
|
||||
const pickerValue = ref([0, 0, 0, 0]);
|
||||
const columnRefs = ref<Array<Array<HTMLElement | null>>>([[], [], [], []]);
|
||||
const ITEM_HEIGHT = 50;
|
||||
|
||||
const setColumnRef = (el: any, columnIdx: number, itemIdx: number) => {
|
||||
if (el) {
|
||||
if (!columnRefs.value[columnIdx]) {
|
||||
columnRefs.value[columnIdx] = [];
|
||||
}
|
||||
columnRefs.value[columnIdx][itemIdx] = el;
|
||||
}
|
||||
};
|
||||
|
||||
const isLeapYear = (year: number): boolean => {
|
||||
return (year % 4 === 0 && year % 100 !== 0) || (year % 400 === 0);
|
||||
};
|
||||
|
||||
const getDaysInMonth = (year: number, month: number): number => {
|
||||
const daysMap = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
||||
if (month === 1 && isLeapYear(year)) {
|
||||
return 29;
|
||||
}
|
||||
return daysMap[month];
|
||||
};
|
||||
|
||||
const getDayName = (d: number): string => {
|
||||
if (d <= 10) return `初${CH_NUM[d]}`;
|
||||
if (d === 20) return '二十';
|
||||
if (d === 30) return '三十';
|
||||
if (d < 20) return `十${CH_NUM[d - 10]}`;
|
||||
if (d < 30) return `廿${CH_NUM[d - 20]}`;
|
||||
return `三十${CH_NUM[d - 30]}`;
|
||||
};
|
||||
|
||||
const days = computed(() => {
|
||||
const [yearIdx, monthIdx] = pickerValue.value;
|
||||
const year = years.value[yearIdx] || 2000;
|
||||
const daysCount = getDaysInMonth(year, monthIdx);
|
||||
|
||||
return Array.from({ length: daysCount }, (_, i) => ({
|
||||
val: i + 1,
|
||||
name: getDayName(i + 1)
|
||||
}));
|
||||
});
|
||||
|
||||
const scrollToIndex = (columnIdx: number, index: number, smooth = true) => {
|
||||
const column = document.querySelectorAll('.mystic-date-picker-column')[columnIdx] as HTMLElement;
|
||||
if (column) {
|
||||
const scrollTop = index * ITEM_HEIGHT;
|
||||
column.scrollTo({
|
||||
top: scrollTop,
|
||||
behavior: smooth ? 'smooth' : 'auto'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const selectItem = (columnIdx: number, index: number) => {
|
||||
let newValue = [...pickerValue.value];
|
||||
newValue[columnIdx] = index;
|
||||
|
||||
// 检查日期是否超出当月天数
|
||||
if (columnIdx === 0 || columnIdx === 1) {
|
||||
const [yearIdx, monthIdx] = newValue;
|
||||
const year = years.value[yearIdx] || 2000;
|
||||
const maxDays = getDaysInMonth(year, monthIdx);
|
||||
if (newValue[2] >= maxDays) {
|
||||
newValue[2] = maxDays - 1;
|
||||
}
|
||||
}
|
||||
|
||||
newValue = clampPickerValueToTodayMax(newValue, years.value);
|
||||
pickerValue.value = newValue;
|
||||
|
||||
nextTick(() => {
|
||||
pickerValue.value.forEach((val, idx) => {
|
||||
scrollToIndex(idx, val, false);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
let scrollTimer: number | null = null;
|
||||
|
||||
const handleScroll = (e: Event, columnIdx: number) => {
|
||||
if (scrollTimer) {
|
||||
clearTimeout(scrollTimer);
|
||||
}
|
||||
|
||||
scrollTimer = window.setTimeout(() => {
|
||||
const target = e.target as HTMLElement;
|
||||
const scrollTop = target.scrollTop;
|
||||
const index = Math.round(scrollTop / ITEM_HEIGHT);
|
||||
|
||||
selectItem(columnIdx, index);
|
||||
}, 150);
|
||||
};
|
||||
|
||||
const parseDefaultValue = (val: string, yearList: number[]) => {
|
||||
if (!val) return null;
|
||||
|
||||
const yearMatch = val.match(/(\d+)年/);
|
||||
const monthMatch = val.match(/年(.+?)(?:初|十|廿|三十)/);
|
||||
const shichenMatch = val.match(/(子|丑|寅|卯|辰|巳|午|未|申|酉|戌|亥)时/);
|
||||
|
||||
if (!yearMatch) return null;
|
||||
|
||||
const year = parseInt(yearMatch[1]);
|
||||
const yearIdx = yearList.findIndex(y => y === year);
|
||||
|
||||
let monthIdx = 0;
|
||||
if (monthMatch) {
|
||||
monthIdx = MONTHS.findIndex(m => m === monthMatch[1]);
|
||||
if (monthIdx < 0) monthIdx = 0;
|
||||
}
|
||||
|
||||
let dayIdx = 0;
|
||||
const dayPart = val.replace(/\d+年/, '').replace(/.*月/, '').replace(/(子|丑|寅|卯|辰|巳|午|未|申|酉|戌|亥)时/, '');
|
||||
if (dayPart) {
|
||||
for (let i = 1; i <= 31; i++) {
|
||||
if (getDayName(i) === dayPart) {
|
||||
dayIdx = i - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let shichenIdx = 0;
|
||||
if (shichenMatch) {
|
||||
shichenIdx = SHI_CHEN.findIndex(s => s.name.startsWith(shichenMatch[1]));
|
||||
if (shichenIdx < 0) shichenIdx = 0;
|
||||
}
|
||||
|
||||
return {
|
||||
yearIdx: yearIdx >= 0 ? yearIdx : 0,
|
||||
monthIdx,
|
||||
dayIdx,
|
||||
shichenIdx
|
||||
};
|
||||
};
|
||||
|
||||
const clampPickerDay = (parts: number[], yearList: number[]): number[] => {
|
||||
if (!yearList.length) return [0, 0, 0, 0];
|
||||
const yearIdx = Math.min(Math.max(0, parts[0]), yearList.length - 1);
|
||||
const monthIdx = Math.min(Math.max(0, parts[1]), 11);
|
||||
const y = yearList[yearIdx] ?? new Date().getFullYear();
|
||||
const maxD = getDaysInMonth(y, monthIdx);
|
||||
const dayIdx = Math.min(Math.max(0, parts[2]), maxD - 1);
|
||||
const shichenIdx = Math.min(Math.max(0, parts[3]), SHI_CHEN.length - 1);
|
||||
return [yearIdx, monthIdx, dayIdx, shichenIdx];
|
||||
};
|
||||
|
||||
const getTodayCalendar = () => {
|
||||
const n = new Date();
|
||||
return { y: n.getFullYear(), m: n.getMonth(), d: n.getDate() };
|
||||
};
|
||||
|
||||
/** capAtToday:将公历日期限制在「今天」及之前 */
|
||||
const clampPickerValueToTodayMax = (parts: number[], yearList: number[]): number[] => {
|
||||
let next = clampPickerDay(parts, yearList);
|
||||
if (!props.capAtToday || !yearList.length) return next;
|
||||
|
||||
const t = getTodayCalendar();
|
||||
let [yi, mi, di, si] = next;
|
||||
const sy = yearList[yi];
|
||||
|
||||
if (sy > t.y) {
|
||||
const ti = yearList.findIndex((yr) => yr === t.y);
|
||||
if (ti >= 0) next = clampPickerDay([ti, t.m, t.d - 1, si], yearList);
|
||||
return next;
|
||||
}
|
||||
if (sy < t.y) return next;
|
||||
|
||||
if (mi > t.m) {
|
||||
next = clampPickerDay([yi, t.m, t.d - 1, si], yearList);
|
||||
return next;
|
||||
}
|
||||
if (mi < t.m) return next;
|
||||
|
||||
if (di > t.d - 1) {
|
||||
next = clampPickerDay([yi, mi, t.d - 1, si], yearList);
|
||||
}
|
||||
return next;
|
||||
};
|
||||
|
||||
watch(() => props.isOpen, (newVal) => {
|
||||
if (newVal) {
|
||||
const ylist = years.value;
|
||||
const parsed = parseDefaultValue(props.defaultValue, ylist);
|
||||
if (parsed) {
|
||||
const yi = Math.min(Math.max(0, parsed.yearIdx), Math.max(0, ylist.length - 1));
|
||||
pickerValue.value = clampPickerValueToTodayMax(
|
||||
[yi, parsed.monthIdx, parsed.dayIdx, parsed.shichenIdx],
|
||||
ylist
|
||||
);
|
||||
} else {
|
||||
const now = new Date();
|
||||
const currentYear = now.getFullYear();
|
||||
const yearIdx = ylist.findIndex(y => y === currentYear);
|
||||
const monthIdx = now.getMonth();
|
||||
const dayIdx = now.getDate() - 1;
|
||||
const y = yearIdx >= 0 ? ylist[yearIdx] : ylist[0];
|
||||
const pickY = yearIdx >= 0 ? yearIdx : 0;
|
||||
|
||||
pickerValue.value = clampPickerValueToTodayMax(
|
||||
[
|
||||
pickY,
|
||||
monthIdx,
|
||||
Math.min(dayIdx, getDaysInMonth(y, monthIdx) - 1),
|
||||
0,
|
||||
],
|
||||
ylist
|
||||
);
|
||||
}
|
||||
|
||||
nextTick(() => {
|
||||
pickerValue.value.forEach((val, idx) => {
|
||||
scrollToIndex(idx, val, false);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const handleClose = () => {
|
||||
emit('close');
|
||||
};
|
||||
|
||||
const handleConfirm = () => {
|
||||
const ylist = years.value;
|
||||
pickerValue.value = clampPickerValueToTodayMax(pickerValue.value, ylist);
|
||||
const [yearIdx, monthIdx, dayIdx, shichenIdx] = pickerValue.value;
|
||||
const selectedYear = years.value[yearIdx] ?? years.value[0];
|
||||
const selectedMonth = MONTHS[monthIdx];
|
||||
const selectedDay = days.value[dayIdx]?.name || getDayName(dayIdx + 1);
|
||||
const selectedShichen = shichenOptions[shichenIdx].name;
|
||||
|
||||
const displayStr = `${selectedYear}年${selectedMonth}${selectedDay}${selectedShichen}`;
|
||||
|
||||
const month = String(monthIdx + 1).padStart(2, '0');
|
||||
const day = String(dayIdx + 1).padStart(2, '0');
|
||||
const hour = shichenOptions[shichenIdx].hour;
|
||||
const apiStr = `${selectedYear}-${month}-${day} ${hour}`;
|
||||
|
||||
emit('confirm', displayStr, apiStr);
|
||||
handleClose();
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.mystic-date-picker-overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 999;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.mystic-date-picker-backdrop {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: rgba(26, 26, 26, 0.6);
|
||||
backdrop-filter: blur(4px);
|
||||
}
|
||||
|
||||
.mystic-date-picker-modal {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
background: #fcfaf5;
|
||||
border-top-left-radius: 16px;
|
||||
border-top-right-radius: 16px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 -8px 32px rgba(0, 0, 0, 0.25);
|
||||
border-top: 4px solid #8b2323;
|
||||
}
|
||||
|
||||
.mystic-date-picker-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 16px 20px;
|
||||
border-bottom: 1px solid #eaddcf;
|
||||
background: #f9f7f2;
|
||||
}
|
||||
|
||||
.mystic-date-picker-close,
|
||||
.mystic-date-picker-confirm {
|
||||
padding: 8px;
|
||||
color: #5a5a5a;
|
||||
cursor: pointer;
|
||||
transition: opacity 0.3s;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.mystic-date-picker-close:active,
|
||||
.mystic-date-picker-confirm:active {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.mystic-date-picker-confirm {
|
||||
color: #8b2323;
|
||||
}
|
||||
|
||||
.mystic-date-picker-icon {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.mystic-date-picker-title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #2c2c2c;
|
||||
letter-spacing: 0.3em;
|
||||
font-family: SimSun, "Songti SC", serif;
|
||||
}
|
||||
|
||||
.mystic-date-picker-view {
|
||||
height: 300px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.mystic-date-picker-indicator {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 50px;
|
||||
transform: translateY(-50%);
|
||||
border-top: 1px solid #dcd3c9;
|
||||
border-bottom: 1px solid #dcd3c9;
|
||||
background: rgba(139, 35, 35, 0.02);
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.mystic-date-picker-column {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
scroll-snap-type: y mandatory;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.mystic-date-picker-column::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.mystic-date-picker-padding {
|
||||
height: 125px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.mystic-date-picker-item {
|
||||
height: 50px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
color: #8a8a8a;
|
||||
font-family: SimSun, "Songti SC", serif;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
scroll-snap-align: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.mystic-date-picker-item.active {
|
||||
color: #2c2c2c;
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.mystic-date-picker-item-time {
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.mystic-date-picker-time-name {
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.mystic-date-picker-item.active .mystic-date-picker-time-name {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.mystic-date-picker-time-detail {
|
||||
font-size: 10px;
|
||||
opacity: 0.6;
|
||||
color: #8a8a8a;
|
||||
}
|
||||
|
||||
.mystic-date-picker-footer {
|
||||
background: #f9f7f2;
|
||||
padding: 12px;
|
||||
text-align: center;
|
||||
border-top: 1px solid #eaddcf;
|
||||
}
|
||||
|
||||
.mystic-date-picker-tip {
|
||||
font-size: 11px;
|
||||
color: #8a8a8a;
|
||||
letter-spacing: 0.1em;
|
||||
}
|
||||
|
||||
/* Transition */
|
||||
.mystic-date-picker-enter-active,
|
||||
.mystic-date-picker-leave-active {
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
|
||||
.mystic-date-picker-enter-active .mystic-date-picker-modal,
|
||||
.mystic-date-picker-leave-active .mystic-date-picker-modal {
|
||||
transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||
}
|
||||
|
||||
.mystic-date-picker-enter-from,
|
||||
.mystic-date-picker-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.mystic-date-picker-enter-from .mystic-date-picker-modal,
|
||||
.mystic-date-picker-leave-to .mystic-date-picker-modal {
|
||||
transform: translateY(100%);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user