ant-design-vue/components/vc-picker/RangePicker.tsx
tangjinzhou a2e50dc43e
Feat v4 (#6329)
* refactor(icon): remove style dir (#6215)

* refactor: rename locale

* refactor: locale-provider

* refactor: modal

* refactor: menu

* fix: custom class (#6217)

* refactor: tooltip

* refactor: grid (#6220)

* refactor: grid

* fix(grid): align & justify responsive

* chore: update demo and snapshot

* fix: row ts type not work

* doc: update demo

* refactor: ts

* refactor: spin (#6222)

* fix: typo (#6218)

* fix:  typo

* docs<upload>: docs update

* refactor: spin

* refactor: spin

* refactor: spin

* refactor: spinnn

* refactor: spin

---------

Co-authored-by: lyn <76365499@qq.com>

* fix: spin error #6222

* test: test case error (#6225)

* fix: inject value maybe undefined

* fix: tootip emit correct value

* fix: rollback warning suffix avoid test break

* doc(grid): remove unused type="flex"

* refactor: skeleton (#6224)

* refactor: skeleton

* refactor: skeleton style

* chore: modify skeleton demo style

* fix(button): link and text should not have wave (#6226)

* refactor: dropdown

* refactor: popover & popconfirm

* refactor(tag): less to cssinjs (#6227)

* refactor(empty): less to cssinjs (#6230)

* refactor(empty): less to cssinjs

* chore: remove unuse code

* fix: reactivity lose

* fix: empty props #6230

* refactor: progress style (#6234)

* refactor: progress

* refactor: progress style

* fix: progress attrs

* refactor: progress #6234

* refactor: switch (#6236)

* refactor: switch style

* refactor: delete switch style

* refactor:input (#6237)

* refactor:input

* fix inheritAttrs:false

* fix attrs.class

* feat: input add disabled

* refactor:comment (#6238)

* refactor:comment

* fix inheritAttrs: false & attrs.class

* refactor:pageheader (#6239)

* refactor:pageheader

* fix inheritAttrs: false & attrs.class

* refactor:statistic (#6240)

* refactor:statistic

* fix inheritAttrs: false & attrs.class

* refactor:list (#6241)

* refactor:list

* fix inheritAttrs: false & attrs.class

* feat: update type

* refactor(Space): less to cssinjs & add compact mode (#6229)

* refactor(Space): less to cssinjs & add compact mode

* chore(space): update md

* chore(space): add demo

* chore(space): add some demo

* feat(button): add compact mode

* fix: reactivity lose

* docs: fix props version

---------

Co-authored-by: tangjinzhou <415800467@qq.com>

* perf: space compact

* refactor:typography (#6244)

* refactor:typography

* fix return

* fix import type

* fix: typography #6244

* refactor:datepicker (#6245)

* refactor: datepicker type

* refactor: rate style (#6254)

* refactor(layout): less to cssinjs (#6249)

* doc: update layout cover

* refactor(result): less to cssinjs (#6246)

* refactor(result): less to cssinjs

* fix: class name is overridden

* docs: update result cover

* refactor:slider (#6250)

* feat: slider  deprecated tooltipVisible

* refactor(crad): less to cssinjs  (#6258)

* update

* switch

* Style adjustment

* refactor(Card): less to cssinjs

* Eliminate invalid code

* optimization and adjustment css

* Adjust the css

* Optimize each item

* adjustment css

* refactor: card #6258

* refactor:carousel (#6262)

* refactor:carousel

* docs:update & refactor: carousel type

---------

Co-authored-by: tangjinzhou <415800467@qq.com>

* refactor:transfer (#6247)

* refactor:transfer

* merge v4 branch & fix theme interface conflict

* docs:update & refactor: transfer type

* perf: transfer

* refactor:checkbox (#6248)

* refactor:checkbox

* docs:update & refactor: checkbox type

* feat: checkbox add disabled context

* refactor:pagination (#6251)

* refactor:pagination

* docs:update & refactor: pagination type

* style: update pagination props type

* refactor: mentions (#6255)

* refactor: mentions

* refactor: mentions menu provider

* doc: update mentions demo

* refcator:upload (#6261)

* refcator:upload

* docs:update & refactor: upload type

* Update style.ts

---------

Co-authored-by: tangjinzhou <415800467@qq.com>

* perf: upload motion

* refactor:timeline (#6263)

* refactor:timeline

* docs:update & refactor: timeline type

* perf: timeline

* refactor:steps (#6264)

* refactor:steps

* fix ...attrs

* fix StepsToken error

* docs:update & refactor: steps type

* fix: steps icon clss error

* refactor:collapse (#6266)

* refactor:collapse

* fix collapse props version

* docs:update & refactor: collapse type & fix collapsible

* feat: update collapse type

* refactor:inputnumber (#6265)

* refactor:inputnumber

* docs:update & refactor: inputnumber type

---------

Co-authored-by: tangjinzhou <415800467@qq.com>

* feat: number add compactSize & disabledContext

* refactor:table (#6267)

* refactor:table

* docs:update & refactor: table type

---------

Co-authored-by: tangjinzhou <415800467@qq.com>

* refactor: table

* feat: table add expandColumnTitle slot

* refactor:calendar (#6269)

* refactor:calendar

* docs:update

* refactor:timepicker (#6270)

* refactor:timepicker

* docs:update & refactor: timepicker type

* refactor:tree (#6276)

* Feat v4 fix type errors (#6285)

* fix compile type errors

* fix menuprops type import

* fix lint errors

* fix lint errors

* fix format error

* fix node version

* fix run dist error

* fix run lint

* fix as any

* fix string type

* refactor: rename locale file

* feat: tree add leafIcon

* [tabs] :less to cssinjs  (#6288)

* update

* switch

* Style adjustment

* refactor(Card): less to cssinjs

* tabs: less to cssinjs 开发ing

* add function cssinjs

* Eliminate irrelevant code

* Eliminate irrelevant code 2

* update components

* Eliminate irrelevant input  code

* refactor: tabs #6288

* feat: add segmented (#6286)

* refactor: segmented #6286

* refactor:select (#6295)

* refactor:select

* update doc

* delete useless

* feat: select add context size

* refactor: tree select (#6296)

* feat: tree-select add context size

* perf: table

* docs: update doc toc

* refactor: cascader

* refactor: auto-complete

* refactor: image

* refactor: drawer

* refactor:radio (#6299)

* refactor:radio

* fix attrs

* feat: radio add disabled context

* fix: some type & doc (#6292)

* fix: typo (#6218)

* fix:  typo

* docs<upload>: docs update

* fix: type of minute in props disabledDateTime of DatePicker (#6233)

* docs: typo (#6256)

* feat: tooltip added overlayInnerStyle attribute

* Update abstractTooltipProps.ts

* Update Tooltip.tsx

---------

Co-authored-by: lyn <76365499@qq.com>
Co-authored-by: H1mple <35363759+baohangxing@users.noreply.github.com>
Co-authored-by: tangjinzhou <415800467@qq.com>

* refactor: form

* fix: directive not work

* fix: use open, remove visible

* doc: update cover

* refactor: remove not use code

* chore: update build script

* doc: update doc

* doc: refactor doc

* chore: update token error

* chore: update style

* refactor: rename _style to style

* fix: tag warning

* fix(dropdown): open invalid (#6316)

* feat: add watermark  (#6300)

* feat: add watermark

* feat: add watermark demo

* feat: add mutationObserver

* feat: add watermark demo

* refactor: watermark type

* doc: add theme-editor

* fix: inject value maybe undefined && tag style invalid (#6320)

* fix: inject value maybe undefined

* fix(tag): style invalid

* feat: add qrcode (#6315)

* feat: add qrcode

* fix: qrcode bug

* fix: qrcode value required

* refactor: props  deconstruct

* Feat v4 floatbutton (#6294)

* feat: add float-button components

* fix type &  demo display

* fix components entry

* fix review bug

* fix bug

* fix .value

* refactor: qrcode #6315

* refactor: float-button

* fix: groupsize context error

* fix: floatbutton animation not work

* Feat v4 theme editor (#6348)

* feat: add theme editor container

* feat: add theme editor layout

* add left panel

* add vue-colorful & fix bug

* 修复hue组件抖动问题

* fix bug && add demo

* fix bug

* fix demo preview

* fix theme editor components demo

* fix: token effect error

* Feat v4 theme editor (#6349)

* feat: add theme editor container

* feat: add theme editor layout

* add left panel

* add vue-colorful & fix bug

* 修复hue组件抖动问题

* fix bug && add demo

* fix bug

* fix demo preview

* fix theme editor components demo

* add theme editor token drawer

* add theme editor token drawer

* fix bug

* open commment

* fix error demo

* fix theme editor bug

* fix: cssinjs effect error

* doc: format code

* fix: tag click event not trigger

* release 4.0.0-alpha.1

* fix: qrcode type

* fix: remove not use file

* doc: update doc site

* doc: update site

* doc: fix theme editor bgcolor (#6358)

* fix: motion not work

* release 4.0.0-alpha.2

* fix: qrcode ; error, close #6362

* fix docs dark theme & add docs coverDark (#6367)

* fix docs dark theme & add docs coverDark

* fix theme Editor edit

* fix: dropdown divider disappear, close  #6365 (#6369)

* doc: update baner

* fix: button wave not work

* fix: ant-piker-cell-range-hover-end style error (#6373)

* fix: ant-piker-cell-range-hover-end style error

* feat: be consistent with antd

* feat: be consistent with antd

* fix: ConfigProvider error for style, close #6368

* release 4.0.0-alpha.4

* style: add dark style for `pre` and `code` (#6382)

* docs: version menu (#6390)

* Feat(DatePicker): increase presets prop (#6387)

* feat(date-picker): add PresetDate type

* feat(date-picker): add usePresets hook

* feat(date-picker): add PresetPanel Component

* feat(date-picker): add PresetPanel Component

* feat(demo): update Preset Ranges Examples

* feat(docs): add new prop presets

* feat(docs): add new prop presets with english

* fix(RangePicker): footer is not managed by panels

* chore(Picker): prefixCls default rc-picker

* chore(date-picker): update presetted-ranges demo

* chore(date-picker): update rangePickerProps'presets

* feat(date-picker): presets reactively processing

* chore(date-picker): update type

* refactor(RangePicker): deprecated ranges prop

* chore(date-picker): update type

* chore(PickerPanel): del notuse panelRef

---------

Co-authored-by: tangjinzhou <415800467@qq.com>

* fix: datepicker presets error #6387

* docs: update datepicker doc #6387

* feat(Steps): add items prop and variants (#6406)

* refactor(steps): add items prop and variants

* feat(steps): add Label Placement and Inline Steps demo

* feat(steps): Label Placement and Inline Steps snap

* test(steps): Steps demo snap

* feat(Steps): update docs

* fix(Step): progressDot

* chore(useLegacyItems): change from warning to devWarning

* refactor(Steps): Remove useLegacyItems

* refactor(Steps): renderStep

* test(Steps): update test snapshot

* chore(Steps): filterEmpty

* feat(Steps): update docs

* docs: update site

* refactor: steps #6406

* test: update steps

* perf: shallowRef instead ref

* fix(Modal): fix modal locale (#6423)

* feat(StyleProvider): add StyleProvider handle cssinjs features (#6415)

* feat(StyleProvider): StyleProvider

* feat(StyleProvider): refactor to  use context

* chore(StyleProvider): update AStyleProviderProps type

* chore(App): reback

* chore(StyleProvider): export StyleProvider

* feat(StyleProvider): update StyleProvider docs

* feat(StyleProvider): update StyleProvider docs

* feat(StyleProvider): add StyleProvider docs routes

* chore(StyleProvider): with useStyleProvider

* docs: update compatiple #6415

* feat(Progress): enhance size prop and add variants (#6409)

* refactor(progress): Progress size and add variants

* feat(progress): add `getsize`

* refactor(progress): Progress size and add variants

* chore(progress): update props type

* chore(progress): update props type

* feat(progress): update demo

* feat(progress): update docs

* test(progress): update test snap

* fix(Circle): Merging classes

* test(progress): update test snap

* feat(progress): add size demo

* test(progress): add size snapshot

* chore(Progress): reback Circle svg class change

* fix: progress borderRadius reactive #6409

* fix(defaultConfigProvider): add getPopupContainer (#6425), close #6419

* fix: qrcode size error, close #6418

* release 4.0.0-alpha.4

* fix: picker import error

* test: add QRCode unit testing (#6441)

* fix

* fix compile type errors

* fix menuprops type import

* fix lint errors

* fix lint errors

* fix format error

* fix node version

* fix run dist error

* fix run lint

* fix as any

* fix string type

* fix steps error & fix docs version select option & fix theme editor error

* fix(badge): badge props count default value error (#6433)

* docs: update site responsive

* fix: modal api method i18n not work, close #6438

* release 4.0.0-alpha.5

* chore(docs): update docs (#6446)

* docs(space): update demo

* docs(affix): update docs

* fix: cssinjs compatibility (#6454)

* feat: add convertLegacyToken

* docs: v4 vuedocs (#6468)

* fix introduce doc

* fix getting-started doc

* add migration-v4 doc

* fix docs

* Update migration-v4.zh-CN.md

* Update migration-v4.zh-CN.md

* Update migration-v4.en-US.md

* Update migration-v4.zh-CN.md

* Update getting-started.en-US.md

* Update getting-started.zh-CN.md

* Update introduce.en-US.md

* Update introduce.zh-CN.md

---------

Co-authored-by: tangjinzhou <415800467@qq.com>

* feat: remove backtop

* feat(anchor): add direction action (#6447)

* refactor(anchor): direction show

* refactor(anchor): update anchor css

* feat(anchor): update demo

* test(anchor): update demo test snap

* feat(anchor): update docs

* Update index.zh-CN.md

* Update index.en-US.md

---------

Co-authored-by: tangjinzhou <415800467@qq.com>

* feat: anchor add customTitle slot #6447

* docs: update doc anchor

* feat(menu): icon support function components with items and update demo (#6457)

* fix(menu): icon do not show problem

* fix(menu): icon do not show problem

* feat(menu): update demo

* test(menu): update demo snap

* chore(Menu): update docs

* test(Menu): update demo

* Update MenuItem.tsx

* Update SubMenu.tsx

---------

Co-authored-by: tangjinzhou <415800467@qq.com>

* doc: update menu icon

* feat: menu items icon add arg

* fix: antd.min error

* release 4.0.0-alpha.6

* fix: table resizable not work && type error (#6514)

* Refactor(demo): change options to composition api (#6499)

* feat(demo): A-B

* feat(demo): update B-checkbox

* feat(demo): update CheckBox -DatePicker

* feat(demo): update DatePicker - Form

* feat(demo): update Form - List

* feat(demo): update  List-pagination

* feat(demo): update  List - skeleton

* feat(demo): update  skeleton - switch

* feat(demo): update  skeleton - switch

* feat(demo): update   switch - upload

* feat(demo): update  watermark

* fix(demo): del hashId

* fix: submenu type lose theme

* fix: dropdown menu hide error

* fix: dealing with switching topics modal, notification, message does not take effect close #6512 (#6518)

* fix: resolve dark mode not support

* fix: unified expression

* feat(modal): add useModal (#6517)

* feat(modal): add useModal hook

* feat(modal): add HookModal demo

* test(modal): update HookModal demo snap

* feat(modal): update modal docs

* chore(modal): update modal type

* perf: useModal #6517

* release 4.0.0-beta.1

* docs: fix tab demo error

* fix(config-provider): fix ConfigProvider.config is not function close #6528 (#6529)

* Feat(use): add useMessage useNotification (#6527)

* feat(Message): add useMessage hook

* feat(Notification): add useNotification hook

* feat(Message): add Hook demo

* feat(Notification): add Hook demo

* test(Message): update demo snap

* test(Notification): update demo snap

* docs(Message): update docs with FAQ

* docs(Notification): update docs with FAQ

* refactor: useMessage #6527

* refactor: useNotification #6527

* release 4.0.0-beta.2

* docs(button): update demo with space (#6536)

* feat(button): demo space

* test(button): update demo snap

* chore(button): disabled demo Ghost space

* test(button): update disabled demo snap

* docs(introduce): update docs (#6539)

* docs(introduce): update docs

* docs(introduce): add Dollar

* Update introduce.zh-CN.md

* Update introduce.en-US.md

---------

Co-authored-by: tangjinzhou <415800467@qq.com>

* docs(customize-theme): update docs (#6540)

* fix introduce doc

* fix getting-started doc

* add migration-v4 doc

* fix docs

* Update migration-v4.zh-CN.md

* Update migration-v4.zh-CN.md

* Update migration-v4.en-US.md

* Update migration-v4.zh-CN.md

* Update getting-started.en-US.md

* Update getting-started.zh-CN.md

* Update introduce.en-US.md

* Update introduce.zh-CN.md

* update customize-theme doc & fix migration-v4 error

* update customize-theme doc

* fix migration-v4 error

* remove SSR & shadowDom

* Update customize-theme.zh-CN.md

* Update customize-theme.en-US.md

---------

Co-authored-by: tangjinzhou <415800467@qq.com>

* fix: getPopupContainer not work

* release 4.0.0-beta.3

* release 4.0.0-beta.4

* docs: update grid docs (#6549)

Co-authored-by: zhuzhengjian <zhuzhengjian@hoteamsoft.com>

* test(alert): update demo with space (#6541)

* docs(alert): update demo with space

* docs(alert): update alert test snap

---------

Co-authored-by: zhuzhengjian <zhuzhengjian@hoteamsoft.com>

* fix: components bug & update docs (#6548)

* fix bug

* fix test case and update snapshot,fix space merge class

* docs(grid): update migrate docs && delete xxxl in grid docs (#6562)

* fix: segmentd disabled label is undefined (#6556)

* fix: segmentd disabled label is undefined

* fix: segmentd disabled label is undefined

* fix: segmentd disabled label is undefined

* fix(grid): remove grid xxxl attribute (#6572)

* fix: remove grid xxxl attribute

* docs: remove xxxl in grid docs

* fix: tooltip custom color error

* feat: remove Step __legacy

* feat: add tour (#6332)

* feat v4 add tour

* fix type error

* sync tour from antd5.4.6 & fix type error

* fix error

* refactor: tour #6332

* fix: tour center

* fix: picker support v-show

* test: update snap

* test: update tour test

* fix: tour-mask attrs pointer-events (#6577)

* fix: tour animated

* feat: support vue 3.3 slot type

* release 4.0.0-rc.1

* release 4.0.0-rc.2, close #6588

* 4.0.0-rc.3

* chore: remove vue private api

* fix: paginantion error, close #6590

* release 4.0.0-rc.4

* fix: checxbox style

* fix: pagination mini size style

* release 4.0.0-rc.5

* docs: update v4 tabs doc error(#6606) (#6607)

* docs: add ant-design-vue nuxt module (#6620)

* fix: layout-sider and menu transition style(#6637) (#6640)

* docs: fixed the style error of online demo (#6630)

* feat: checkbox label slot support use option label (#6642)

* docs: 📃change the default  setting of  "treeNodeFilterProp" from  "value" to "label"

* revert: ↩revert this config and create another pr to commit

* feat: checkbox label slot support use option label

* test: 🧪update checkbox *.snap file

---------

Co-authored-by: tangjinzhou <415800467@qq.com>

* fix: add disabledContext override with form components (#6618)

* fix: add disabledContext override with form components

* test: update snap

* fix: LabelWidth demo filename

* fix: fontsize spelling mistake

* fix(tour): target position (#6629)

* style: format lint

* docs(form): add form disabled demo (#6658)

* fix: comment node error

* release 4.0

* fix: portalWrapper add autoLock prop (#6687), close #6649

* fix: image animation & zindex, close #6675

* docs(QRCode): Synchronize QR code demonstration and add SVG (#6660)

* fix: Synchronize QR code demonstration and add SVG

* fix: responsive loss and invalid border style

* docs: synchronize antd5.6.3 QRCode color in dark mode

* feat:  calendar select support info.source param (#6697)

* docs: add ant-design-vue nuxt module

* feat: calendar select support info.source param

* docs: synchronous config-provider demo (#6706)

* revert: #6706

* docs: export space-compact types (#6716)

* release 4.0.0

---------

Co-authored-by: bqy_fe <1743369777@qq.com>
Co-authored-by: zkwolf <chenhao5866@gmail.com>
Co-authored-by: Zev Zhu <45655660+aibayanyu20@users.noreply.github.com>
Co-authored-by: lyn <76365499@qq.com>
Co-authored-by: 果冻橙 <shifeng199307@gmail.com>
Co-authored-by: songsong0707 <74165917+songsong0707@users.noreply.github.com>
Co-authored-by: yang <30883395+webvs2@users.noreply.github.com>
Co-authored-by: selicens <1244620067@qq.com>
Co-authored-by: 一堆菠萝 <53335668+JavanShen@users.noreply.github.com>
Co-authored-by: H1mple <35363759+baohangxing@users.noreply.github.com>
Co-authored-by: Cherry7 <79909910+CCherry07@users.noreply.github.com>
Co-authored-by: Konv Suu <2583695112@qq.com>
Co-authored-by: luoawai <32483950+luoawai@users.noreply.github.com>
Co-authored-by: 鱼见 <657715602@qq.com>
Co-authored-by: zhuzhengjian <zhuzhengjian@hoteamsoft.com>
Co-authored-by: Cupid Valentine <53572196+valcosmos@users.noreply.github.com>
Co-authored-by: 专业逮虾户aa <30494925+waldonUB@users.noreply.github.com>
Co-authored-by: PanStar <PanStar@users.noreply.github.com>
2023-07-14 11:58:27 +08:00

1319 lines
43 KiB
Vue

import type {
DisabledTimes,
PanelMode,
PickerMode,
RangeValue,
EventValue,
PresetDate,
} from './interface';
import type { PickerBaseProps, PickerDateProps, PickerTimeProps } from './Picker';
import type { SharedTimeProps } from './panels/TimePanel';
import PickerTrigger from './PickerTrigger';
import PickerPanel from './PickerPanel';
import usePickerInput from './hooks/usePickerInput';
import PresetPanel from './PresetPanel';
import getDataOrAriaProps, { toArray, getValue, updateValues } from './utils/miscUtil';
import { getDefaultFormat, getInputSize, elementsContains } from './utils/uiUtil';
import type { ContextOperationRefProps } from './PanelContext';
import { useProvidePanel } from './PanelContext';
import {
isEqual,
getClosingViewDate,
isSameDate,
isSameWeek,
isSameQuarter,
formatValue,
parseValue,
} from './utils/dateUtil';
import useValueTexts from './hooks/useValueTexts';
import useTextValueMapping from './hooks/useTextValueMapping';
import usePresets from './hooks/usePresets';
import type { GenerateConfig } from './generate';
import type { PickerPanelProps } from '.';
import { RangeContextProvider } from './RangeContext';
import useRangeDisabled from './hooks/useRangeDisabled';
import getExtraFooter from './utils/getExtraFooter';
import getRanges from './utils/getRanges';
import useRangeViewDates from './hooks/useRangeViewDates';
import type { DateRender } from './panels/DatePanel/DateBody';
import useHoverValue from './hooks/useHoverValue';
import type { VueNode } from '../_util/type';
import type { ChangeEvent, FocusEventHandler, MouseEventHandler } from '../_util/EventInterface';
import { computed, defineComponent, ref, toRef, watch, watchEffect } from 'vue';
import useMergedState from '../_util/hooks/useMergedState';
import { warning } from '../vc-util/warning';
import useState from '../_util/hooks/useState';
import classNames from '../_util/classNames';
import { legacyPropsWarning } from './utils/warnUtil';
import { useElementSize } from '../_util/hooks/_vueuse/useElementSize';
function reorderValues<DateType>(
values: RangeValue<DateType>,
generateConfig: GenerateConfig<DateType>,
): RangeValue<DateType> {
if (values && values[0] && values[1] && generateConfig.isAfter(values[0], values[1])) {
return [values[1], values[0]];
}
return values;
}
function canValueTrigger<DateType>(
value: EventValue<DateType>,
index: number,
disabled: [boolean, boolean],
allowEmpty?: [boolean, boolean] | null,
): boolean {
if (value) {
return true;
}
if (allowEmpty && allowEmpty[index]) {
return true;
}
if (disabled[(index + 1) % 2]) {
return true;
}
return false;
}
export type RangeType = 'start' | 'end';
export type RangeInfo = {
range: RangeType;
};
export type RangeDateRender<DateType> = (props: {
current: DateType;
today: DateType;
info: RangeInfo;
}) => VueNode;
export type RangePickerSharedProps<DateType> = {
id?: string;
value?: RangeValue<DateType>;
defaultValue?: RangeValue<DateType>;
defaultPickerValue?: [DateType, DateType];
placeholder?: [string, string];
disabled?: boolean | [boolean, boolean];
disabledTime?: (date: EventValue<DateType>, type: RangeType) => DisabledTimes;
presets?: PresetDate<RangeValue<DateType>>[];
/** @deprecated Please use `presets` instead */
ranges?: Record<
string,
Exclude<RangeValue<DateType>, null> | (() => Exclude<RangeValue<DateType>, null>)
>;
separator?: VueNode;
allowEmpty?: [boolean, boolean];
mode?: [PanelMode, PanelMode];
onChange?: (values: RangeValue<DateType>, formatString: [string, string]) => void;
onCalendarChange?: (
values: RangeValue<DateType>,
formatString: [string, string],
info: RangeInfo,
) => void;
onPanelChange?: (values: RangeValue<DateType>, modes: [PanelMode, PanelMode]) => void;
onFocus?: FocusEventHandler;
onBlur?: FocusEventHandler;
onMousedown?: MouseEventHandler;
onMouseup?: MouseEventHandler;
onMouseenter?: MouseEventHandler;
onMouseleave?: MouseEventHandler;
onClick?: MouseEventHandler;
onOk?: (dates: RangeValue<DateType>) => void;
direction?: 'ltr' | 'rtl';
autocomplete?: string;
/** @private Internal control of active picker. Do not use since it's private usage */
activePickerIndex?: 0 | 1;
dateRender?: RangeDateRender<DateType>;
panelRender?: (originPanel: VueNode) => VueNode;
};
type OmitPickerProps<Props> = Omit<
Props,
| 'value'
| 'defaultValue'
| 'defaultPickerValue'
| 'placeholder'
| 'disabled'
| 'disabledTime'
| 'showToday'
| 'showTime'
| 'mode'
| 'onChange'
| 'onSelect'
| 'onPanelChange'
| 'pickerValue'
| 'onPickerValueChange'
| 'onOk'
| 'dateRender'
| 'presets'
>;
type RangeShowTimeObject<DateType> = Omit<SharedTimeProps<DateType>, 'defaultValue'> & {
defaultValue?: DateType[];
};
export type RangePickerBaseProps<DateType> = {} & RangePickerSharedProps<DateType> &
OmitPickerProps<PickerBaseProps<DateType>>;
export type RangePickerDateProps<DateType> = {
showTime?: boolean | RangeShowTimeObject<DateType>;
} & RangePickerSharedProps<DateType> &
OmitPickerProps<PickerDateProps<DateType>>;
export type RangePickerTimeProps<DateType> = {
order?: boolean;
} & RangePickerSharedProps<DateType> &
OmitPickerProps<PickerTimeProps<DateType>>;
export type RangePickerProps<DateType> =
| RangePickerBaseProps<DateType>
| RangePickerDateProps<DateType>
| RangePickerTimeProps<DateType>;
// TMP type to fit for ts 3.9.2
type OmitType<DateType> = Omit<RangePickerBaseProps<DateType>, 'picker'> &
Omit<RangePickerDateProps<DateType>, 'picker'> &
Omit<RangePickerTimeProps<DateType>, 'picker'>;
type MergedRangePickerProps<DateType> = {
picker?: PickerMode;
} & OmitType<DateType>;
function RangerPicker<DateType>() {
return defineComponent<MergedRangePickerProps<DateType>>({
name: 'RangerPicker',
inheritAttrs: false,
props: [
'prefixCls',
'id',
'popupStyle',
'dropdownClassName',
'transitionName',
'dropdownAlign',
'getPopupContainer',
'generateConfig',
'locale',
'placeholder',
'autofocus',
'disabled',
'format',
'picker',
'showTime',
'showNow',
'showHour',
'showMinute',
'showSecond',
'use12Hours',
'separator',
'value',
'defaultValue',
'defaultPickerValue',
'open',
'defaultOpen',
'disabledDate',
'disabledTime',
'dateRender',
'panelRender',
'ranges',
'allowEmpty',
'allowClear',
'suffixIcon',
'clearIcon',
'pickerRef',
'inputReadOnly',
'mode',
'renderExtraFooter',
'onChange',
'onOpenChange',
'onPanelChange',
'onCalendarChange',
'onFocus',
'onBlur',
'onMousedown',
'onMouseup',
'onMouseenter',
'onMouseleave',
'onClick',
'onOk',
'onKeydown',
'components',
'order',
'direction',
'activePickerIndex',
'autocomplete',
'minuteStep',
'hourStep',
'secondStep',
'hideDisabledOptions',
'disabledMinutes',
'presets',
] as any,
setup(props, { attrs, expose }) {
const needConfirmButton = computed(
() => (props.picker === 'date' && !!props.showTime) || props.picker === 'time',
);
const presets = computed(() => props.presets);
const ranges = computed(() => props.ranges);
const presetList = usePresets(presets, ranges);
// We record oqqpened status here in case repeat open with picker
const openRecordsRef = ref<Record<number, boolean>>({});
const containerRef = ref<HTMLDivElement>(null);
const panelDivRef = ref<HTMLDivElement>(null);
const startInputDivRef = ref<HTMLDivElement>(null);
const endInputDivRef = ref<HTMLDivElement>(null);
const separatorRef = ref<HTMLDivElement>(null);
const startInputRef = ref<HTMLInputElement>(null);
const endInputRef = ref<HTMLInputElement>(null);
const arrowRef = ref<HTMLDivElement>(null);
// ============================ Warning ============================
if (process.env.NODE_ENV !== 'production') {
legacyPropsWarning(props);
}
// ============================= Misc ==============================
const formatList = computed(() =>
toArray(
getDefaultFormat<DateType>(props.format, props.picker, props.showTime, props.use12Hours),
),
);
// Active picker
const [mergedActivePickerIndex, setMergedActivePickerIndex] = useMergedState<0 | 1>(0, {
value: toRef(props, 'activePickerIndex'),
});
// Operation ref
const operationRef = ref<ContextOperationRefProps>(null);
const mergedDisabled = computed<[boolean, boolean]>(() => {
const { disabled } = props;
if (Array.isArray(disabled)) {
return disabled;
}
return [disabled || false, disabled || false];
});
// ============================= Value =============================
const [mergedValue, setInnerValue] = useMergedState<RangeValue<DateType>>(null, {
value: toRef(props, 'value'),
defaultValue: props.defaultValue,
postState: values =>
props.picker === 'time' && !props.order
? values
: reorderValues(values, props.generateConfig),
});
// =========================== View Date ===========================
// Config view panel
const [startViewDate, endViewDate, setViewDate] = useRangeViewDates({
values: mergedValue,
picker: toRef(props, 'picker'),
defaultDates: props.defaultPickerValue,
generateConfig: toRef(props, 'generateConfig'),
});
// ========================= Select Values =========================
const [selectedValue, setSelectedValue] = useMergedState(mergedValue.value, {
postState: values => {
let postValues = values;
if (mergedDisabled.value[0] && mergedDisabled.value[1]) {
return postValues;
}
// Fill disabled unit
for (let i = 0; i < 2; i += 1) {
if (
mergedDisabled.value[i] &&
!getValue(postValues, i) &&
!getValue(props.allowEmpty, i)
) {
postValues = updateValues(postValues, props.generateConfig.getNow(), i);
}
}
return postValues;
},
});
// ============================= Modes =============================
const [mergedModes, setInnerModes] = useMergedState<[PanelMode, PanelMode]>(
[props.picker, props.picker],
{
value: toRef(props, 'mode'),
},
);
watch(
() => props.picker,
() => {
setInnerModes([props.picker, props.picker]);
},
);
const triggerModesChange = (modes: [PanelMode, PanelMode], values: RangeValue<DateType>) => {
setInnerModes(modes);
props.onPanelChange?.(values, modes);
};
// ========================= Disable Date ==========================
const [disabledStartDate, disabledEndDate] = useRangeDisabled(
{
picker: toRef(props, 'picker'),
selectedValue,
locale: toRef(props, 'locale'),
disabled: mergedDisabled,
disabledDate: toRef(props, 'disabledDate'),
generateConfig: toRef(props, 'generateConfig'),
},
openRecordsRef,
);
// ============================= Open ==============================
const [mergedOpen, triggerInnerOpen] = useMergedState(false, {
value: toRef(props, 'open'),
defaultValue: props.defaultOpen,
postState: postOpen =>
mergedDisabled.value[mergedActivePickerIndex.value] ? false : postOpen,
onChange: newOpen => {
props.onOpenChange?.(newOpen);
if (!newOpen && operationRef.value && operationRef.value.onClose) {
operationRef.value.onClose();
}
},
});
const startOpen = computed(() => mergedOpen.value && mergedActivePickerIndex.value === 0);
const endOpen = computed(() => mergedOpen.value && mergedActivePickerIndex.value === 1);
const panelLeft = ref(0);
const arrowLeft = ref(0);
// ============================= Popup =============================
// Popup min width
const popupMinWidth = ref(0);
const { width: containerWidth } = useElementSize(containerRef);
watch([mergedOpen, containerWidth], () => {
if (!mergedOpen.value && containerRef.value) {
popupMinWidth.value = containerWidth.value;
}
});
const { width: panelDivWidth } = useElementSize(panelDivRef);
const { width: arrowWidth } = useElementSize(arrowRef);
const { width: startInputDivWidth } = useElementSize(startInputDivRef);
const { width: separatorWidth } = useElementSize(separatorRef);
watch(
[
mergedActivePickerIndex,
mergedOpen,
panelDivWidth,
arrowWidth,
startInputDivWidth,
separatorWidth,
() => props.direction,
],
() => {
arrowLeft.value = 0;
if (mergedOpen.value && mergedActivePickerIndex.value) {
if (startInputDivRef.value && separatorRef.value && panelDivRef.value) {
arrowLeft.value = startInputDivWidth.value + separatorWidth.value;
if (
panelDivWidth.value &&
arrowWidth.value &&
arrowLeft.value >
panelDivWidth.value -
arrowWidth.value -
(props.direction === 'rtl' || arrowRef.value.offsetLeft > arrowLeft.value
? 0
: arrowRef.value.offsetLeft)
) {
panelLeft.value = arrowLeft.value;
}
}
} else if (mergedActivePickerIndex.value === 0) {
panelLeft.value = 0;
}
},
{ immediate: true },
);
// ============================ Trigger ============================
const triggerRef = ref<any>();
function triggerOpen(newOpen: boolean, index: 0 | 1) {
if (newOpen) {
clearTimeout(triggerRef.value);
openRecordsRef.value[index] = true;
setMergedActivePickerIndex(index);
triggerInnerOpen(newOpen);
// Open to reset view date
if (!mergedOpen.value) {
setViewDate(null, index);
}
} else if (mergedActivePickerIndex.value === index) {
triggerInnerOpen(newOpen);
// Clean up async
// This makes ref not quick refresh in case user open another input with blur trigger
const openRecords = openRecordsRef.value;
triggerRef.value = setTimeout(() => {
if (openRecords === openRecordsRef.value) {
openRecordsRef.value = {};
}
});
}
}
function triggerOpenAndFocus(index: 0 | 1) {
triggerOpen(true, index);
// Use setTimeout to make sure panel DOM exists
setTimeout(() => {
const inputRef = [startInputRef, endInputRef][index];
if (inputRef.value) {
inputRef.value.focus();
}
}, 0);
}
function triggerChange(newValue: RangeValue<DateType>, sourceIndex: 0 | 1) {
let values = newValue;
let startValue = getValue(values, 0);
let endValue = getValue(values, 1);
const {
generateConfig,
locale,
picker,
order,
onCalendarChange,
allowEmpty,
onChange,
showTime,
} = props;
// >>>>> Format start & end values
if (startValue && endValue && generateConfig.isAfter(startValue, endValue)) {
if (
// WeekPicker only compare week
(picker === 'week' &&
!isSameWeek(generateConfig, locale.locale, startValue, endValue)) ||
// QuotaPicker only compare week
(picker === 'quarter' && !isSameQuarter(generateConfig, startValue, endValue)) ||
// Other non-TimePicker compare date
(picker !== 'week' &&
picker !== 'quarter' &&
picker !== 'time' &&
!(showTime
? isEqual(generateConfig, startValue, endValue)
: isSameDate(generateConfig, startValue, endValue)))
) {
// Clean up end date when start date is after end date
if (sourceIndex === 0) {
values = [startValue, null];
endValue = null;
} else {
startValue = null;
values = [null, endValue];
}
// Clean up cache since invalidate
openRecordsRef.value = {
[sourceIndex]: true,
};
} else if (picker !== 'time' || order !== false) {
// Reorder when in same date
values = reorderValues(values, generateConfig);
}
}
setSelectedValue(values);
const startStr =
values && values[0]
? formatValue(values[0], { generateConfig, locale, format: formatList.value[0] })
: '';
const endStr =
values && values[1]
? formatValue(values[1], { generateConfig, locale, format: formatList.value[0] })
: '';
if (onCalendarChange) {
const info: RangeInfo = { range: sourceIndex === 0 ? 'start' : 'end' };
onCalendarChange(values, [startStr, endStr], info);
}
// >>>>> Trigger `onChange` event
const canStartValueTrigger = canValueTrigger(
startValue,
0,
mergedDisabled.value,
allowEmpty,
);
const canEndValueTrigger = canValueTrigger(endValue, 1, mergedDisabled.value, allowEmpty);
const canTrigger = values === null || (canStartValueTrigger && canEndValueTrigger);
if (canTrigger) {
// Trigger onChange only when value is validate
setInnerValue(values);
if (
onChange &&
(!isEqual(generateConfig, getValue(mergedValue.value, 0), startValue) ||
!isEqual(generateConfig, getValue(mergedValue.value, 1), endValue))
) {
onChange(values, [startStr, endStr]);
}
}
// >>>>> Open picker when
// Always open another picker if possible
let nextOpenIndex: 0 | 1 = null;
if (sourceIndex === 0 && !mergedDisabled.value[1]) {
nextOpenIndex = 1;
} else if (sourceIndex === 1 && !mergedDisabled.value[0]) {
nextOpenIndex = 0;
}
if (
nextOpenIndex !== null &&
nextOpenIndex !== mergedActivePickerIndex.value &&
(!openRecordsRef.value[nextOpenIndex] || !getValue(values, nextOpenIndex)) &&
getValue(values, sourceIndex)
) {
// Delay to focus to avoid input blur trigger expired selectedValues
triggerOpenAndFocus(nextOpenIndex);
} else {
triggerOpen(false, sourceIndex);
}
}
const forwardKeydown = (e: KeyboardEvent) => {
if (mergedOpen && operationRef.value && operationRef.value.onKeydown) {
// Let popup panel handle keyboard
return operationRef.value.onKeydown(e);
}
/* istanbul ignore next */
/* eslint-disable no-lone-blocks */
{
warning(
false,
'Picker not correct forward Keydown operation. Please help to fire issue about this.',
);
return false;
}
};
// ============================= Text ==============================
const sharedTextHooksProps = {
formatList,
generateConfig: toRef(props, 'generateConfig'),
locale: toRef(props, 'locale'),
};
const [startValueTexts, firstStartValueText] = useValueTexts<DateType>(
computed(() => getValue(selectedValue.value, 0)),
sharedTextHooksProps,
);
const [endValueTexts, firstEndValueText] = useValueTexts<DateType>(
computed(() => getValue(selectedValue.value, 1)),
sharedTextHooksProps,
);
const onTextChange = (newText: string, index: 0 | 1) => {
const inputDate = parseValue(newText, {
locale: props.locale,
formatList: formatList.value,
generateConfig: props.generateConfig,
});
const disabledFunc = index === 0 ? disabledStartDate : disabledEndDate;
if (inputDate && !disabledFunc(inputDate)) {
setSelectedValue(updateValues(selectedValue.value, inputDate, index));
setViewDate(inputDate, index);
}
};
const [startText, triggerStartTextChange, resetStartText] = useTextValueMapping({
valueTexts: startValueTexts,
onTextChange: newText => onTextChange(newText, 0),
});
const [endText, triggerEndTextChange, resetEndText] = useTextValueMapping({
valueTexts: endValueTexts,
onTextChange: newText => onTextChange(newText, 1),
});
const [rangeHoverValue, setRangeHoverValue] = useState<RangeValue<DateType>>(null);
// ========================== Hover Range ==========================
const [hoverRangedValue, setHoverRangedValue] = useState<RangeValue<DateType>>(null);
const [startHoverValue, onStartEnter, onStartLeave] = useHoverValue(
startText,
sharedTextHooksProps,
);
const [endHoverValue, onEndEnter, onEndLeave] = useHoverValue(endText, sharedTextHooksProps);
const onDateMouseenter = (date: DateType) => {
setHoverRangedValue(updateValues(selectedValue.value, date, mergedActivePickerIndex.value));
if (mergedActivePickerIndex.value === 0) {
onStartEnter(date);
} else {
onEndEnter(date);
}
};
const onDateMouseleave = () => {
setHoverRangedValue(updateValues(selectedValue.value, null, mergedActivePickerIndex.value));
if (mergedActivePickerIndex.value === 0) {
onStartLeave();
} else {
onEndLeave();
}
};
// ============================= Input =============================
const getSharedInputHookProps = (index: 0 | 1, resetText: () => void) => ({
forwardKeydown,
onBlur: (e: FocusEvent) => {
props.onBlur?.(e);
},
isClickOutside: (target: EventTarget | null) =>
!elementsContains(
[panelDivRef.value, startInputDivRef.value, endInputDivRef.value, containerRef.value],
target as HTMLElement,
),
onFocus: (e: FocusEvent) => {
setMergedActivePickerIndex(index);
props.onFocus?.(e);
},
triggerOpen: (newOpen: boolean) => {
triggerOpen(newOpen, index);
},
onSubmit: () => {
if (
// When user typing disabledDate with keyboard and enter, this value will be empty
!selectedValue.value ||
// Normal disabled check
(props.disabledDate && props.disabledDate(selectedValue.value[index]))
) {
return false;
}
triggerChange(selectedValue.value, index);
resetText();
},
onCancel: () => {
triggerOpen(false, index);
setSelectedValue(mergedValue.value);
resetText();
},
});
const [startInputProps, { focused: startFocused, typing: startTyping }] = usePickerInput({
...getSharedInputHookProps(0, resetStartText),
blurToCancel: needConfirmButton,
open: startOpen,
value: startText,
onKeydown: (e, preventDefault) => {
props.onKeydown?.(e, preventDefault);
},
});
const [endInputProps, { focused: endFocused, typing: endTyping }] = usePickerInput({
...getSharedInputHookProps(1, resetEndText),
blurToCancel: needConfirmButton,
open: endOpen,
value: endText,
onKeydown: (e, preventDefault) => {
props.onKeydown?.(e, preventDefault);
},
});
// ========================== Click Picker ==========================
const onPickerClick = (e: MouseEvent) => {
// When click inside the picker & outside the picker's input elements
// the panel should still be opened
props.onClick?.(e);
if (
!mergedOpen.value &&
!startInputRef.value.contains(e.target as Node) &&
!endInputRef.value.contains(e.target as Node)
) {
if (!mergedDisabled.value[0]) {
triggerOpenAndFocus(0);
} else if (!mergedDisabled.value[1]) {
triggerOpenAndFocus(1);
}
}
};
const onPickerMousedown = (e: MouseEvent) => {
// shouldn't affect input elements if picker is active
props.onMousedown?.(e);
if (
mergedOpen.value &&
(startFocused.value || endFocused.value) &&
!startInputRef.value.contains(e.target as Node) &&
!endInputRef.value.contains(e.target as Node)
) {
e.preventDefault();
}
};
// ============================= Sync ==============================
// Close should sync back with text value
const startStr = computed(() =>
mergedValue.value?.[0]
? formatValue(mergedValue.value[0], {
locale: props.locale,
format: 'YYYYMMDDHHmmss',
generateConfig: props.generateConfig,
})
: '',
);
const endStr = computed(() =>
mergedValue.value?.[1]
? formatValue(mergedValue.value[1], {
locale: props.locale,
format: 'YYYYMMDDHHmmss',
generateConfig: props.generateConfig,
})
: '',
);
watch([mergedOpen, startValueTexts, endValueTexts], () => {
if (!mergedOpen.value) {
setSelectedValue(mergedValue.value);
if (!startValueTexts.value.length || startValueTexts.value[0] === '') {
triggerStartTextChange('');
} else if (firstStartValueText.value !== startText.value) {
resetStartText();
}
if (!endValueTexts.value.length || endValueTexts.value[0] === '') {
triggerEndTextChange('');
} else if (firstEndValueText.value !== endText.value) {
resetEndText();
}
}
});
// Sync innerValue with control mode
watch([startStr, endStr], () => {
setSelectedValue(mergedValue.value);
});
// ============================ Warning ============================
if (process.env.NODE_ENV !== 'production') {
watchEffect(() => {
const { value, disabled } = props;
if (
value &&
Array.isArray(disabled) &&
((getValue(disabled, 0) && !getValue(value, 0)) ||
(getValue(disabled, 1) && !getValue(value, 1)))
) {
warning(
false,
'`disabled` should not set with empty `value`. You should set `allowEmpty` or `value` instead.',
);
}
});
}
expose({
focus: () => {
if (startInputRef.value) {
startInputRef.value.focus();
}
},
blur: () => {
if (startInputRef.value) {
startInputRef.value.blur();
}
if (endInputRef.value) {
endInputRef.value.blur();
}
},
});
// ============================= Panel =============================
const panelHoverRangedValue = computed(() => {
if (
mergedOpen.value &&
hoverRangedValue.value &&
hoverRangedValue.value[0] &&
hoverRangedValue.value[1] &&
props.generateConfig.isAfter(hoverRangedValue.value[1], hoverRangedValue.value[0])
) {
return hoverRangedValue.value;
} else {
return null;
}
});
function renderPanel(
panelPosition: 'left' | 'right' | false = false,
panelProps: Partial<PickerPanelProps<DateType>> = {},
) {
const { generateConfig, showTime, dateRender, direction, disabledTime, prefixCls, locale } =
props;
let panelShowTime: boolean | SharedTimeProps<DateType> | undefined =
showTime as SharedTimeProps<DateType>;
if (showTime && typeof showTime === 'object' && showTime.defaultValue) {
const timeDefaultValues: DateType[] = showTime.defaultValue!;
panelShowTime = {
...showTime,
defaultValue: getValue(timeDefaultValues, mergedActivePickerIndex.value) || undefined,
};
}
let panelDateRender: DateRender<DateType> | null = null;
if (dateRender) {
panelDateRender = ({ current: date, today }) =>
dateRender({
current: date,
today,
info: {
range: mergedActivePickerIndex.value ? 'end' : 'start',
},
});
}
return (
<RangeContextProvider
value={{
inRange: true,
panelPosition,
rangedValue: rangeHoverValue.value || selectedValue.value,
hoverRangedValue: panelHoverRangedValue.value,
}}
>
<PickerPanel<DateType>
{...(props as any)}
{...panelProps}
dateRender={panelDateRender}
showTime={panelShowTime}
mode={mergedModes.value[mergedActivePickerIndex.value]}
generateConfig={generateConfig}
style={undefined}
direction={direction}
disabledDate={
mergedActivePickerIndex.value === 0 ? disabledStartDate : disabledEndDate
}
disabledTime={date => {
if (disabledTime) {
return disabledTime(date, mergedActivePickerIndex.value === 0 ? 'start' : 'end');
}
return false;
}}
class={classNames({
[`${prefixCls}-panel-focused`]:
mergedActivePickerIndex.value === 0 ? !startTyping.value : !endTyping.value,
})}
value={getValue(selectedValue.value, mergedActivePickerIndex.value)}
locale={locale}
tabIndex={-1}
onPanelChange={(date, newMode) => {
// clear hover value when panel change
if (mergedActivePickerIndex.value === 0) {
onStartLeave(true);
}
if (mergedActivePickerIndex.value === 1) {
onEndLeave(true);
}
triggerModesChange(
updateValues(mergedModes.value, newMode, mergedActivePickerIndex.value),
updateValues(selectedValue.value, date, mergedActivePickerIndex.value),
);
let viewDate = date;
if (
panelPosition === 'right' &&
mergedModes.value[mergedActivePickerIndex.value] === newMode
) {
viewDate = getClosingViewDate(viewDate, newMode as any, generateConfig, -1);
}
setViewDate(viewDate, mergedActivePickerIndex.value);
}}
onOk={null}
onSelect={undefined}
onChange={undefined}
defaultValue={
mergedActivePickerIndex.value === 0
? getValue(selectedValue.value, 1)
: getValue(selectedValue.value, 0)
}
/>
</RangeContextProvider>
);
}
const onContextSelect = (date: DateType, type: 'key' | 'mouse' | 'submit') => {
const values = updateValues(selectedValue.value, date, mergedActivePickerIndex.value);
if (type === 'submit' || (type !== 'key' && !needConfirmButton.value)) {
// triggerChange will also update selected values
triggerChange(values, mergedActivePickerIndex.value);
// clear hover value style
if (mergedActivePickerIndex.value === 0) {
onStartLeave();
} else {
onEndLeave();
}
} else {
setSelectedValue(values);
}
};
useProvidePanel({
operationRef,
hideHeader: computed(() => props.picker === 'time'),
onDateMouseenter,
onDateMouseleave,
hideRanges: computed(() => true),
onSelect: onContextSelect,
open: mergedOpen,
});
return () => {
const {
prefixCls = 'rc-picker',
id,
popupStyle,
dropdownClassName,
transitionName,
dropdownAlign,
getPopupContainer,
generateConfig,
locale,
placeholder,
autofocus,
picker = 'date',
showTime,
separator = '~',
disabledDate,
panelRender,
allowClear,
suffixIcon,
clearIcon,
inputReadOnly,
renderExtraFooter,
onMouseenter,
onMouseleave,
onMouseup,
onOk,
components,
direction,
autocomplete = 'off',
} = props;
const arrowPositionStyle =
direction === 'rtl'
? { right: `${arrowLeft.value}px` }
: { left: `${arrowLeft.value}px` };
function renderPanels() {
let panels: VueNode;
const extraNode = getExtraFooter(
prefixCls,
mergedModes.value[mergedActivePickerIndex.value],
renderExtraFooter,
);
const rangesNode = getRanges({
prefixCls,
components,
needConfirmButton: needConfirmButton.value,
okDisabled:
!getValue(selectedValue.value, mergedActivePickerIndex.value) ||
(disabledDate && disabledDate(selectedValue.value[mergedActivePickerIndex.value])),
locale,
onOk: () => {
if (getValue(selectedValue.value, mergedActivePickerIndex.value)) {
// triggerChangeOld(selectedValue.value);
triggerChange(selectedValue.value, mergedActivePickerIndex.value);
if (onOk) {
onOk(selectedValue.value);
}
}
},
});
if (picker !== 'time' && !showTime) {
const viewDate =
mergedActivePickerIndex.value === 0 ? startViewDate.value : endViewDate.value;
const nextViewDate = getClosingViewDate(viewDate, picker, generateConfig);
const currentMode = mergedModes.value[mergedActivePickerIndex.value];
const showDoublePanel = currentMode === picker;
const leftPanel = renderPanel(showDoublePanel ? 'left' : false, {
pickerValue: viewDate,
onPickerValueChange: newViewDate => {
setViewDate(newViewDate, mergedActivePickerIndex.value);
},
});
const rightPanel = renderPanel('right', {
pickerValue: nextViewDate,
onPickerValueChange: newViewDate => {
setViewDate(
getClosingViewDate(newViewDate, picker, generateConfig, -1),
mergedActivePickerIndex.value,
);
},
});
if (direction === 'rtl') {
panels = (
<>
{rightPanel}
{showDoublePanel && leftPanel}
</>
);
} else {
panels = (
<>
{leftPanel}
{showDoublePanel && rightPanel}
</>
);
}
} else {
panels = renderPanel();
}
let mergedNodes: VueNode = (
<div class={`${prefixCls}-panel-layout`}>
<PresetPanel
prefixCls={prefixCls}
presets={presetList.value}
onClick={nextValue => {
triggerChange(nextValue, null);
triggerOpen(false, mergedActivePickerIndex.value);
}}
onHover={hoverValue => {
setRangeHoverValue(hoverValue);
}}
/>
<div>
<div class={`${prefixCls}-panels`}>{panels}</div>
{(extraNode || rangesNode) && (
<div class={`${prefixCls}-footer`}>
{extraNode}
{rangesNode}
</div>
)}
</div>
</div>
);
if (panelRender) {
mergedNodes = panelRender(mergedNodes);
}
return (
<div
class={`${prefixCls}-panel-container`}
style={{ marginLeft: `${panelLeft.value}px` }}
ref={panelDivRef}
onMousedown={e => {
e.preventDefault();
}}
>
{mergedNodes}
</div>
);
}
const rangePanel = (
<div
class={classNames(`${prefixCls}-range-wrapper`, `${prefixCls}-${picker}-range-wrapper`)}
style={{ minWidth: `${popupMinWidth.value}px` }}
>
<div ref={arrowRef} class={`${prefixCls}-range-arrow`} style={arrowPositionStyle} />
{renderPanels()}
</div>
);
// ============================= Icons =============================
let suffixNode: VueNode;
if (suffixIcon) {
suffixNode = <span class={`${prefixCls}-suffix`}>{suffixIcon}</span>;
}
let clearNode: VueNode;
if (
allowClear &&
((getValue(mergedValue.value, 0) && !mergedDisabled.value[0]) ||
(getValue(mergedValue.value, 1) && !mergedDisabled.value[1]))
) {
clearNode = (
<span
onMousedown={e => {
e.preventDefault();
e.stopPropagation();
}}
onMouseup={e => {
e.preventDefault();
e.stopPropagation();
let values = mergedValue.value;
if (!mergedDisabled.value[0]) {
values = updateValues(values, null, 0);
}
if (!mergedDisabled.value[1]) {
values = updateValues(values, null, 1);
}
triggerChange(values, null);
triggerOpen(false, mergedActivePickerIndex.value);
}}
class={`${prefixCls}-clear`}
>
{clearIcon || <span class={`${prefixCls}-clear-btn`} />}
</span>
);
}
const inputSharedProps = {
size: getInputSize(picker, formatList.value[0], generateConfig),
};
let activeBarLeft = 0;
let activeBarWidth = 0;
if (startInputDivRef.value && endInputDivRef.value && separatorRef.value) {
if (mergedActivePickerIndex.value === 0) {
activeBarWidth = startInputDivRef.value.offsetWidth;
} else {
activeBarLeft = arrowLeft.value;
activeBarWidth = endInputDivRef.value.offsetWidth;
}
}
const activeBarPositionStyle =
direction === 'rtl' ? { right: `${activeBarLeft}px` } : { left: `${activeBarLeft}px` };
// ============================ Return =============================
return (
<div
ref={containerRef}
class={classNames(prefixCls, `${prefixCls}-range`, attrs.class, {
[`${prefixCls}-disabled`]: mergedDisabled.value[0] && mergedDisabled.value[1],
[`${prefixCls}-focused`]:
mergedActivePickerIndex.value === 0 ? startFocused.value : endFocused.value,
[`${prefixCls}-rtl`]: direction === 'rtl',
})}
style={attrs.style}
onClick={onPickerClick}
onMouseenter={onMouseenter}
onMouseleave={onMouseleave}
onMousedown={onPickerMousedown}
onMouseup={onMouseup}
{...getDataOrAriaProps(props)}
>
<div
class={classNames(`${prefixCls}-input`, {
[`${prefixCls}-input-active`]: mergedActivePickerIndex.value === 0,
[`${prefixCls}-input-placeholder`]: !!startHoverValue.value,
})}
ref={startInputDivRef}
>
<input
id={id}
disabled={mergedDisabled.value[0]}
readonly={
inputReadOnly || typeof formatList.value[0] === 'function' || !startTyping.value
}
value={startHoverValue.value || startText.value}
onInput={(e: ChangeEvent) => {
triggerStartTextChange(e.target.value);
}}
autofocus={autofocus}
placeholder={getValue(placeholder, 0) || ''}
ref={startInputRef}
{...startInputProps.value}
{...inputSharedProps}
autocomplete={autocomplete}
/>
</div>
<div class={`${prefixCls}-range-separator`} ref={separatorRef}>
{separator}
</div>
<div
class={classNames(`${prefixCls}-input`, {
[`${prefixCls}-input-active`]: mergedActivePickerIndex.value === 1,
[`${prefixCls}-input-placeholder`]: !!endHoverValue.value,
})}
ref={endInputDivRef}
>
<input
disabled={mergedDisabled.value[1]}
readonly={
inputReadOnly || typeof formatList.value[0] === 'function' || !endTyping.value
}
value={endHoverValue.value || endText.value}
onInput={(e: ChangeEvent) => {
triggerEndTextChange(e.target.value);
}}
placeholder={getValue(placeholder, 1) || ''}
ref={endInputRef}
{...endInputProps.value}
{...inputSharedProps}
autocomplete={autocomplete}
/>
</div>
<div
class={`${prefixCls}-active-bar`}
style={{
...activeBarPositionStyle,
width: `${activeBarWidth}px`,
position: 'absolute',
}}
/>
{suffixNode}
{clearNode}
<PickerTrigger
visible={mergedOpen.value}
popupStyle={popupStyle}
prefixCls={prefixCls}
dropdownClassName={dropdownClassName}
dropdownAlign={dropdownAlign}
getPopupContainer={getPopupContainer}
transitionName={transitionName}
range
direction={direction}
v-slots={{
popupElement: () => rangePanel,
}}
>
<div
style={{
pointerEvents: 'none',
position: 'absolute',
top: 0,
bottom: 0,
left: 0,
right: 0,
}}
></div>
</PickerTrigger>
</div>
);
};
},
});
}
const InterRangerPicker = RangerPicker<any>();
export default InterRangerPicker;