Calendar.vue 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683
  1. <template>
  2. <FlexCol innerClass="nana-calendar" align="stretch">
  3. <FlexRow v-if="!scrollMode" center :gap="20">
  4. <IconButton icon="arrow-double-left" @click="prev('year')" />
  5. <IconButton icon="arrow-left-bold" @click="prev('month')" />
  6. <Text>{{ DateUtils.formatDate(currentShowMonth, props.topMonthFormat) }}</Text>
  7. <IconButton icon="arrow-right-bold" @click="next('month')" />
  8. <IconButton icon="arrow-double-right" @click="next('year')" />
  9. </FlexRow>
  10. <Height :size="20" />
  11. <FlexRow position="relative">
  12. <FlexCol v-if="scrollMode" width="100%">
  13. <FlexRow innerClass="nana-calendar-days" wrap justify="space-between">
  14. <CalendarItem
  15. v-for="weekText in props.weekTexts"
  16. :key="weekText"
  17. :disabled="true"
  18. :header="true"
  19. :date="weekText"
  20. :text="weekText"
  21. :textProps="headerTextProps"
  22. />
  23. </FlexRow>
  24. <FixedVirtualList
  25. direction="vertical"
  26. :itemSize="scrollModePageHeight"
  27. :containerStyle="{
  28. width: '100%',
  29. height: theme.resolveSize(scrollModePageHeight * 2),
  30. }"
  31. :data="dayGridsScroll"
  32. :bufferSize="1"
  33. class="nana-calendar-scroll"
  34. >
  35. <template #item="{ item }">
  36. <FlexCol position="relative">
  37. <FlexRow justify="center">
  38. <Text :text="item.title" />
  39. </FlexRow>
  40. <FlexRow innerClass="nana-calendar-days" wrap justify="flex-start">
  41. <template
  42. v-for="(day, i) in item.days"
  43. :key="i"
  44. >
  45. <view v-if="day.type === 'invisible'" style="width:14.28%"></view>
  46. <CalendarItem
  47. v-else
  48. :disabled="day.type === 'outOfRange'"
  49. :text="day.text"
  50. :textColor="day.color"
  51. :textProps="{
  52. ...dayTextProps,
  53. ...day.textProps,
  54. }"
  55. :date="day.date"
  56. :bottomText="day.bottomText?.text"
  57. :bottomTextProps="day.bottomText?.textProps"
  58. :topText="day.topText?.text"
  59. :topTextProps="day.topText?.textProps"
  60. @click="handleClick(day)"
  61. />
  62. </template>
  63. </FlexRow>
  64. <Text
  65. innerClass="nana-calendar-bg-text"
  66. color="lightGrey"
  67. :fontSize="150"
  68. v-bind="backgroundTextProps"
  69. >
  70. {{ item.month }}
  71. </Text>
  72. </FlexCol>
  73. </template>
  74. </FixedVirtualList>
  75. </FlexCol>
  76. <template v-else>
  77. <FlexRow innerClass="nana-calendar-days" wrap justify="space-between">
  78. <CalendarItem
  79. v-for="weekText in props.weekTexts"
  80. :key="weekText"
  81. :disabled="true"
  82. :header="true"
  83. :date="weekText"
  84. :text="weekText"
  85. :textProps="headerTextProps"
  86. />
  87. <CalendarItem
  88. v-for="(day, i) in dayGrids"
  89. :key="i"
  90. :disabled="day.type === 'outOfRange'"
  91. :text="day.text"
  92. :textColor="day.color"
  93. :textProps="{
  94. ...dayTextProps,
  95. ...day.textProps,
  96. }"
  97. :date="day.date"
  98. :bottomText="day.bottomText?.text"
  99. :bottomTextProps="day.bottomText?.textProps"
  100. :topText="day.topText?.text"
  101. :topTextProps="day.topText?.textProps"
  102. @click="handleClick(day)"
  103. />
  104. </FlexRow>
  105. <Text
  106. innerClass="nana-calendar-bg-text"
  107. color="lightGrey"
  108. :fontSize="150"
  109. v-bind="backgroundTextProps"
  110. >
  111. {{ currentMonth + 1 }}
  112. </Text>
  113. </template>
  114. </FlexRow>
  115. </FlexCol>
  116. </template>
  117. <script setup lang="ts">
  118. import { computed, provide, ref, toRef, watch, type ComputedRef, type Ref } from 'vue';
  119. import { useTheme } from '../theme/ThemeDefine';
  120. import { DateUtils } from '@imengyu/imengyu-utils';
  121. import IconButton from '../basic/IconButton.vue';
  122. import Text from '../basic/Text.vue';
  123. import FlexCol from '../layout/FlexCol.vue';
  124. import FlexRow from '../layout/FlexRow.vue';
  125. import Height from '../layout/space/Height.vue';
  126. import CalendarItem from './CalendarItem.vue';
  127. import type { TextProps } from '../basic/Text.vue';
  128. import calendar, { getFestival } from './CalendarUtils';
  129. import type { FlexProps } from '../layout/FlexView.vue';
  130. import FixedVirtualList from '../list/FixedVirtualList.vue';
  131. export type CalendarPickType = 'day'|'range'|'days';
  132. export interface CalendarProps {
  133. /**
  134. * 选择的日期
  135. */
  136. modelValue?: string[] | string;
  137. /**
  138. * 进入时当前日历显示的月份
  139. * @default 当前月份
  140. */
  141. currentMonth?: Date;
  142. /**
  143. * 顶部月份的格式
  144. * @default 'yyyy-MM'
  145. */
  146. topMonthFormat?: string;
  147. /**
  148. * 可以将月份平铺滚动展示,建议只显示最近的几个月,显示过多月份易卡顿。
  149. * @default false
  150. */
  151. scrollMode?: boolean;
  152. /**
  153. * 滚动模式下,每页的高度
  154. * @default 360
  155. */
  156. scrollModePageHeight?: number;
  157. /**
  158. * 滚动模式下,滚动容器高度,即显示几页(scrollModePageHeight * scrollModePages)
  159. * @default 2
  160. */
  161. scrollModePages?: number;
  162. /**
  163. * 日期格式
  164. * @default 'yyyy-MM-dd'
  165. */
  166. dateFormat?: string;
  167. /**
  168. * 选择模式:
  169. * * day 选择一天
  170. * * range 选择范围
  171. * * days 选择多天
  172. * @default 'day'
  173. */
  174. pickType?: CalendarPickType;
  175. /**
  176. * 设置选择后在日期按钮上的状态文本,
  177. * 可以是一个字符串,也可以是一个数组,选中的日期按索引顺序读取文字。
  178. */
  179. pickStateText?: string|string[];
  180. /**
  181. * 限制开始日期
  182. * @default 5年前
  183. */
  184. startDate?: string;
  185. /**
  186. * 限制结束日期
  187. * @default 10年后
  188. */
  189. endDate?: string;
  190. /**
  191. * 条目的自定义属性
  192. */
  193. itemProps?: FlexProps;
  194. /**
  195. * 是否展示固定节日。
  196. * 数据来源于 @swjs/chinese-holidays
  197. * @default true
  198. */
  199. showFestival?: boolean;
  200. /**
  201. * 在日期上、下方显示自定义文字,可以用于显示节假日、事件、优惠信息等
  202. */
  203. extraDayStrings?: Record<string, {
  204. text: string;
  205. textProps?: TextProps,
  206. topText?: string;
  207. topTextProps?: TextProps,
  208. }>;
  209. /**
  210. * 设置不可选的日期
  211. */
  212. disabledDates?: string[];
  213. /**
  214. * 最大可选天数,仅在模式为 days, range 时生效。超出最大可选天数会触发 pickOverMaxDays 事件。为0则不限制
  215. * @default 0
  216. */
  217. maxPickRangeDays?: number;
  218. /**
  219. * 自定义周文字
  220. * @default ['日', '一', '二', '三', '四', '五', '六']
  221. */
  222. weekTexts?: string[];
  223. /**
  224. * 自定义头部日期文字的样式
  225. */
  226. headerTextProps?: TextProps,
  227. /**
  228. * 自定义日期文字的样式
  229. */
  230. dayTextProps?: TextProps,
  231. /**
  232. * 自定义单个日期文字的样式
  233. */
  234. daysTextProps?: Record<string, TextProps>,
  235. /**
  236. * 自定义背景文字的样式
  237. */
  238. backgroundTextProps?: TextProps,
  239. }
  240. const emit = defineEmits([ 'update:modelValue', 'pickOverMaxDays', 'selectTextChange' ]);
  241. const props = withDefaults(defineProps<CalendarProps>(), {
  242. pickType: 'day',
  243. showFestival: true,
  244. maxPickRangeDays: 0,
  245. scrollModePageHeight: 360,
  246. scrollModePages: 2,
  247. dateFormat: 'yyyy-MM-dd',
  248. topMonthFormat: 'yyyy年MM月',
  249. currentMonth: () => new Date(),
  250. startDate: (props) => {
  251. const now = new Date();
  252. return DateUtils.formatDate(new Date(`${now.getFullYear() - 5}-01-01`), props.dateFormat);
  253. },
  254. endDate: (props) => {
  255. const now = new Date();
  256. return DateUtils.formatDate(new Date(`${now.getFullYear() + 10}-12-31`), props.dateFormat);
  257. },
  258. weekTexts: () => ['日', '一', '二', '三', '四', '五', '六'],
  259. });
  260. const currentYear = ref(props.currentMonth.getFullYear());
  261. const currentMonth = ref(props.currentMonth.getMonth());
  262. const startDate = computed(() => props.startDate ? new Date(props.startDate) : new Date('2000-01-01'));
  263. const endDate = computed(() => props.endDate ? new Date(props.endDate) : new Date('2035-12-31'));
  264. watch(() => props.currentMonth, (newVal) => {
  265. currentYear.value = newVal.getFullYear();
  266. currentMonth.value = newVal.getMonth();
  267. });
  268. interface DayGridInfo {
  269. text: string,
  270. color: string,
  271. date: string,
  272. bottomText?: {
  273. text: string,
  274. textProps?: TextProps,
  275. },
  276. topText?: {
  277. text: string,
  278. textProps?: TextProps,
  279. },
  280. textProps?: TextProps,
  281. type: 'prev'|'current'|'next'|'outOfRange'|'invisible',
  282. }
  283. const itemColors = computed(() => theme.resolveThemeColors({
  284. 'CalendarItemColorNormal': 'transparent',
  285. 'CalendarItemColorSelected': 'primary',
  286. 'CalendarItemColorSelectedInRange': 'mask.primary',
  287. 'CalendarItemColorDisabled': 'transparent',
  288. 'CalendarTextColorDisabled': 'text.second',
  289. 'CalendarTextColorNormal': 'text.content',
  290. 'CalendarTextColorNormalWeekend': 'danger',
  291. 'CalendarTextColorSelected': 'white',
  292. 'CalendarTextColorSelectedInRange': 'primary',
  293. }));
  294. const itemSizes = computed(() => theme.resolveThemeSizes({
  295. 'CalendarTopTextSize': 22,
  296. 'CalendarTextSize': 30,
  297. 'CalendarBottomTextSize': 22,
  298. }));
  299. const currentShowMonth = computed(() => new Date(`${currentYear.value}-${currentMonth.value + 1}-01`));
  300. const dayGrids = computed(() => {
  301. const weekOfStartDay = currentShowMonth.value.getDay();
  302. const daysInMonth = DateUtils.getMonthDays(currentYear.value, currentMonth.value)!;
  303. let prevYear = currentYear.value;
  304. let prevMonth = currentMonth.value - 1;
  305. if (prevMonth < 0) {
  306. prevMonth = 11;
  307. prevYear--;
  308. }
  309. let nextYear = currentYear.value;
  310. let nextMonth = currentMonth.value + 1;
  311. if (nextMonth >= 12) {
  312. nextMonth = 0;
  313. nextYear++;
  314. }
  315. const daysPrevMonth = DateUtils.getMonthDays(prevYear, prevMonth)!;
  316. const result = [] as DayGridInfo[];
  317. //上一个月的日期
  318. for(let i = daysPrevMonth, j = weekOfStartDay - 1; i > 0 && j >= 0; i--, j--) {
  319. const date = new Date(`${prevYear}-${prevMonth + 1}-${i}`);
  320. result.unshift({
  321. text: i.toString(),
  322. color: itemColors.value.CalendarTextColorDisabled,
  323. date: DateUtils.formatDate(date, props.dateFormat),
  324. type: 'prev',
  325. });
  326. }
  327. const isRangePickerAndPickedStart = props.pickType === 'range' && getPickValueArray().length === 1;
  328. const rangePickerStartString = getPickValueArray()[0];
  329. const rangePickerStart = rangePickerStartString ? DateUtils.parseDate(rangePickerStartString) : undefined;
  330. //当前月的日期
  331. for(let i = 1; i <= daysInMonth; i++) {
  332. const isWeekend = (i + weekOfStartDay) % 7 === 0 || (i + weekOfStartDay - 1) % 7 === 0;
  333. const date = new Date(`${currentYear.value}-${currentMonth.value + 1}-${i}`);
  334. const lunarInfo = calendar.solar2lunar(date.getFullYear(), date.getMonth() + 1, date.getDate());
  335. const festival = getFestival(date);
  336. const dateString = DateUtils.formatDate(date, props.dateFormat);
  337. const item : DayGridInfo = {
  338. text: i.toString(),
  339. color: isWeekend ? itemColors.value.CalendarTextColorNormalWeekend : itemColors.value.CalendarTextColorNormal,
  340. date: dateString,
  341. type: 'current',
  342. };
  343. if (date.getTime() < startDate.value.getTime() || date.getTime() > endDate.value.getTime())
  344. item.type = 'outOfRange';
  345. else if (isRangePickerAndPickedStart && rangePickerStart) {
  346. if (
  347. (props.maxPickRangeDays > 0 && ((date.getTime() - rangePickerStart.getTime()) / 864000000) > props.maxPickRangeDays)//选择范围情况下,超出最长可选天数
  348. || (date < rangePickerStart)
  349. )
  350. item.type = 'outOfRange';
  351. }
  352. if (props.daysTextProps?.[dateString]) {
  353. item.textProps = props.daysTextProps[dateString];
  354. }
  355. if (props.extraDayStrings?.[dateString]) {
  356. item.bottomText = {
  357. text: props.extraDayStrings[dateString].text,
  358. textProps: props.extraDayStrings[dateString].textProps,
  359. };
  360. if (props.extraDayStrings[dateString].topText) {
  361. item.topText = {
  362. text: props.extraDayStrings[dateString].topText,
  363. textProps: props.extraDayStrings[dateString].topTextProps,
  364. };
  365. }
  366. } else if (props.showFestival) {
  367. item.bottomText = {
  368. text: festival || (lunarInfo.IDayCn === '初一' ? lunarInfo.IMonthCn : lunarInfo.IDayCn) || '',
  369. textProps: {},
  370. };
  371. } else if (isWeekend) {
  372. item.color = itemColors.value.CalendarTextColorNormalWeekend;
  373. }
  374. result.push(item);
  375. }
  376. //下一个月的日期
  377. for(let j = 1; j <= 7 - ((daysInMonth + weekOfStartDay) % 7); j++) {
  378. const date = new Date(`${nextYear}-${nextMonth + 1}-${j}`);
  379. result.push({
  380. text: j.toString(),
  381. color: itemColors.value.CalendarTextColorDisabled,
  382. date: DateUtils.formatDate(date, props.dateFormat),
  383. type: 'next',
  384. });
  385. }
  386. return result;
  387. });
  388. const dayGridsScroll = computed(() => {
  389. const startDate = DateUtils.parseDate(props.startDate);
  390. const endDate = DateUtils.parseDate(props.endDate);
  391. const endYear = endDate.getFullYear();
  392. const endMonth = endDate.getMonth();
  393. let startYear = startDate.getFullYear();
  394. let startMonth = startDate.getMonth();
  395. const resultGroup : {
  396. title: string,
  397. days: DayGridInfo[],
  398. month: number,
  399. }[] = [];
  400. while (true) {
  401. const weekOfStartDay = new Date(`${startYear}-${startMonth + 1}-1`).getDay();
  402. const daysInMonth = DateUtils.getMonthDays(startYear, startMonth)!;
  403. let prevYear = startYear;
  404. let prevMonth = startMonth - 1;
  405. if (prevMonth < 0) {
  406. prevMonth = 11;
  407. prevYear--;
  408. }
  409. const daysPrevMonth = DateUtils.getMonthDays(prevYear, prevMonth)!;
  410. const result = {
  411. title: DateUtils.formatDate(new Date(`${startYear}-${startMonth + 1}-1`), props.topMonthFormat),
  412. days: [] as DayGridInfo[],
  413. month: startMonth + 1,
  414. };
  415. const isRangePickerAndPickedStart = props.pickType === 'range' && getPickValueArray().length === 1;
  416. const rangePickerStartString = getPickValueArray()[0];
  417. const rangePickerStart = rangePickerStartString ? DateUtils.parseDate(rangePickerStartString) : undefined;
  418. //上一个月的日期
  419. for(let i = daysPrevMonth, j = weekOfStartDay - 1; i > 0 && j >= 0; i--, j--) {
  420. const date = new Date(`${prevYear}-${prevMonth + 1}-${i}`);
  421. result.days.unshift({
  422. text: i.toString(),
  423. color: itemColors.value.CalendarTextColorDisabled,
  424. date: DateUtils.formatDate(date, props.dateFormat),
  425. type: 'invisible',
  426. });
  427. }
  428. //当前月的日期
  429. for(let i = 1; i <= daysInMonth; i++) {
  430. const isWeekend = (i + weekOfStartDay) % 7 === 0 || (i + weekOfStartDay - 1) % 7 === 0;
  431. const date = new Date(`${startYear}-${startMonth + 1}-${i}`);
  432. const lunarInfo = calendar.solar2lunar(date.getFullYear(), date.getMonth() + 1, date.getDate());
  433. const festival = getFestival(date);
  434. const dateString = DateUtils.formatDate(date, props.dateFormat);
  435. const item : DayGridInfo = {
  436. text: i.toString(),
  437. color: isWeekend ? itemColors.value.CalendarTextColorNormalWeekend : itemColors.value.CalendarTextColorNormal,
  438. date: dateString,
  439. type: 'current',
  440. };
  441. if (date.getTime() < startDate.getTime() || date.getTime() > endDate.getTime())
  442. item.type = 'outOfRange';
  443. else if (isRangePickerAndPickedStart && rangePickerStart) {
  444. if (
  445. (props.maxPickRangeDays > 0 && ((date.getTime() - rangePickerStart.getTime()) / 86400000) > props.maxPickRangeDays)//选择范围情况下,超出最长可选天数
  446. || (date < rangePickerStart)
  447. )
  448. item.type = 'outOfRange';
  449. }
  450. if (props.daysTextProps?.[dateString]) {
  451. item.textProps = props.daysTextProps[dateString];
  452. }
  453. if (props.extraDayStrings?.[dateString]) {
  454. item.bottomText = {
  455. text: props.extraDayStrings[dateString].text,
  456. textProps: props.extraDayStrings[dateString].textProps,
  457. };
  458. if (props.extraDayStrings[dateString].topText) {
  459. item.topText = {
  460. text: props.extraDayStrings[dateString].topText,
  461. textProps: props.extraDayStrings[dateString].topTextProps,
  462. };
  463. }
  464. } else if (props.showFestival) {
  465. item.bottomText = {
  466. text: festival || (lunarInfo.IDayCn === '初一' ? lunarInfo.IMonthCn : lunarInfo.IDayCn) || '',
  467. textProps: {},
  468. };
  469. } else if (isWeekend) {
  470. item.color = itemColors.value.CalendarTextColorNormalWeekend;
  471. }
  472. result.days.push(item);
  473. }
  474. resultGroup.push(result);
  475. startMonth++;
  476. if (startMonth > 11) {
  477. startMonth = 0;
  478. startYear++;
  479. }
  480. if (startYear > endYear)
  481. break;
  482. }
  483. return resultGroup;
  484. });
  485. function prev(type: 'month'|'year') {
  486. let now = new Date(currentShowMonth.value);
  487. if (type === 'month') {
  488. if (now.getMonth() === 0) {
  489. now.setFullYear(now.getFullYear() - 1);
  490. now.setMonth(11);
  491. } else {
  492. now.setMonth(now.getMonth() - 1);
  493. }
  494. } else {
  495. now.setFullYear(now.getFullYear() - 1);
  496. }
  497. if (now.getTime() < startDate.value.getTime())
  498. now = startDate.value;
  499. currentYear.value = now.getFullYear();
  500. currentMonth.value = now.getMonth();
  501. }
  502. function next(type: 'month'|'year') {
  503. let now = new Date(currentShowMonth.value);
  504. if (type === 'month') {
  505. if (now.getMonth() === 11) {
  506. now.setFullYear(now.getFullYear() + 1);
  507. now.setMonth(0);
  508. } else {
  509. now.setMonth(now.getMonth() + 1);
  510. }
  511. } else {
  512. now.setFullYear(now.getFullYear() + 1);
  513. }
  514. if (now.getTime() > endDate.value.getTime())
  515. now = endDate.value;
  516. currentYear.value = now.getFullYear();
  517. currentMonth.value = now.getMonth();
  518. }
  519. function getPickValueArray() {
  520. if (typeof props.modelValue === 'string')
  521. return [props.modelValue];
  522. return props.modelValue as string[] || [];
  523. }
  524. function latePickADay(dayString: string) {
  525. setTimeout(() => {
  526. const day = dayGrids.value.find(d => d.date === dayString);
  527. if (day)
  528. handleClick(day);
  529. }, 200);
  530. }
  531. function updateValue(value: string|string[]) {
  532. emit('selectTextChange', typeof value === 'string' ? value : (
  533. value.length == 2 ?
  534. `${value[0]} - ${value[1]}` :
  535. value.join('、')
  536. ));
  537. emit('update:modelValue', value);
  538. }
  539. function handleClick(day: DayGridInfo) {
  540. if (day.type === 'next') {
  541. next('month');
  542. latePickADay(day.date);
  543. return;
  544. }
  545. else if (day.type === 'prev') {
  546. prev('month');
  547. latePickADay(day.date);
  548. return;
  549. } else if (day.type === 'outOfRange') {
  550. return;
  551. }
  552. switch (props.pickType) {
  553. case 'day':
  554. updateValue(day.date);
  555. break;
  556. case 'days': {
  557. const index = getPickValueArray().indexOf(day.date);
  558. if (index !== -1)
  559. updateValue(getPickValueArray().filter((_, i) => i !== index));
  560. else {
  561. if (props.maxPickRangeDays > 0 && getPickValueArray().length >= props.maxPickRangeDays) {
  562. emit('pickOverMaxDays');
  563. return;
  564. }
  565. updateValue(getPickValueArray().concat([ day.date ]));
  566. }
  567. break;
  568. }
  569. case 'range':
  570. const len = getPickValueArray().length;
  571. if (len <= 0 || len >= 2) {
  572. updateValue([ day.date ]);
  573. return;
  574. } else if(len === 1) {
  575. const pickValue = getPickValueArray()[0];
  576. const rangePickerStart = DateUtils.parseDate(pickValue);
  577. if (pickValue === day.date) {
  578. updateValue([]);
  579. return;
  580. }
  581. if (DateUtils.parseDate(day.date).getTime() <= rangePickerStart.getTime()) {
  582. emit('pickOverMaxDays');
  583. return;
  584. }
  585. updateValue([ getPickValueArray()[0], day.date ]);
  586. }
  587. break;
  588. default:
  589. throw new Error('pickType not supported');
  590. }
  591. }
  592. const theme = useTheme();
  593. export interface CalendarContext {
  594. itemColors: ComputedRef<Record<string, string>>,
  595. itemSizes: ComputedRef<Record<string, number>>,
  596. itemProps: Ref<FlexProps|undefined>,
  597. disabledDates: Ref<string[]|undefined>,
  598. pickType: Ref<string>,
  599. pickValue: Ref<string|string[]|undefined>,
  600. pickStateText: Ref<string|string[]|undefined>,
  601. }
  602. provide<CalendarContext>('calendarContext', {
  603. itemColors,
  604. itemSizes,
  605. itemProps: toRef(props, 'itemProps'),
  606. disabledDates: toRef(props, 'disabledDates'),
  607. pickType: toRef(props, 'pickType'),
  608. pickValue: toRef(props, 'modelValue'),
  609. pickStateText: toRef(props, 'pickStateText'),
  610. })
  611. defineOptions({
  612. options: {
  613. styleIsolation: 'shared',
  614. }
  615. })
  616. </script>
  617. <style lang="scss">
  618. .nana-calendar-bg-text {
  619. position: absolute;
  620. top: 0;
  621. left: 0;
  622. width: 100%;
  623. height: 100%;
  624. display: flex;
  625. align-items: center;
  626. justify-content: center;
  627. z-index: 0;
  628. }
  629. .nana-calendar-days {
  630. position: relative;
  631. z-index: 1;
  632. }
  633. .nana-calendar-scroll {
  634. max-height: 70vh;
  635. }
  636. </style>