Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(date-picker): Add needConfirm and onConfirm api (#2979) #2981

Merged
merged 1 commit into from
Aug 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/silver-ads-jog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@hi-ui/date-picker": minor
---

feat: Add needConfirm and onConfirm api
5 changes: 5 additions & 0 deletions .changeset/slow-bottles-talk.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@hi-ui/hiui": patch
---

feat(date-picker): Add needConfirm and onConfirm api
29 changes: 24 additions & 5 deletions packages/ui/date-picker/src/DatePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ export const DatePicker = forwardRef<HTMLDivElement | null, DatePickerProps>(
footerRender,
strideSelectMode = 'auto',
prefix,
needConfirm: needConfirmProp = false,
onConfirm,
...otherProps
},
ref
Expand All @@ -92,6 +94,14 @@ export const DatePicker = forwardRef<HTMLDivElement | null, DatePickerProps>(
const [inputFocus, setInputFocus] = useState(false)
const [type, setType] = useState<DatePickerTypeEnum>(propType)

const needConfirm = useMemo(() => {
// 如果是日期时间范围选择,则默认返回 true
if (type === 'daterange' && showTime) {
return true
}
return needConfirmProp
}, [needConfirmProp, showTime, type])

useEffect(() => {
moment.locale(locale === 'en-US' ? 'en' : 'zh-CN')
// V4: 不使用 weekOffset 判断国际化语言
Expand Down Expand Up @@ -301,12 +311,10 @@ export const DatePicker = forwardRef<HTMLDivElement | null, DatePickerProps>(
setInputFocus(false)
}, [])

const isInDateRangeTimeMode = useMemo(() => type === 'daterange' && showTime, [type, showTime])

const onPopperClose = useCallback(() => {
resetStatus()

if (!isInDateRangeTimeMode) {
if (!needConfirm) {
const outDateValue = outDate[0]
const isValid = moment(outDateValue).isValid()
// @ts-ignore
Expand All @@ -322,7 +330,7 @@ export const DatePicker = forwardRef<HTMLDivElement | null, DatePickerProps>(
}

onClose?.()
}, [resetStatus, outDate, isInDateRangeTimeMode, onClose, max, min, callback, changeOutDate])
}, [callback, changeOutDate, max, min, needConfirm, onClose, outDate, resetStatus])

const onClear = () => {
resetStatus()
Expand All @@ -349,8 +357,11 @@ export const DatePicker = forwardRef<HTMLDivElement | null, DatePickerProps>(
type === 'timeperiod' && `${prefixCls}__popper--timeperiod`,
shortcuts && `${prefixCls}__popper--shortcuts`
)

const [attachEl, setAttachEl] = useState<HTMLElement | null>(null)

const isInDateRangeTimeMode = useMemo(() => type === 'daterange' && showTime, [type, showTime])

const popContent = useMemo(() => {
// 日期时间范围选择器特殊处理
if (isInDateRangeTimeMode) {
Expand All @@ -367,7 +378,13 @@ export const DatePicker = forwardRef<HTMLDivElement | null, DatePickerProps>(
{type.includes('range') || type === 'timeperiod' ? (
<RangePanel />
) : (
<Panel onPick={onPick} outDate={outDate} disabledDate={disabledDate} />
<Panel
onPick={onPick}
outDate={outDate}
disabledDate={disabledDate}
needConfirm={needConfirm}
onConfirm={onConfirm}
/>
)}
</div>
)
Expand All @@ -378,6 +395,8 @@ export const DatePicker = forwardRef<HTMLDivElement | null, DatePickerProps>(
onPick,
outDate,
disabledDate,
needConfirm,
onConfirm,
dateRangeTimePanelNow,
])

Expand Down
18 changes: 3 additions & 15 deletions packages/ui/date-picker/src/components/date-range-time-panel.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react'
import { useLocaleContext } from '@hi-ui/core'
import HiButton from '@hi-ui/button'
import DPContext, { DPContextData } from '../context'
import Panel from './panel'
import { CalenderSelectedRange } from '../hooks/useCalenderData'
import moment from 'moment'
import { CalendarViewEnum } from '../types'
import { Footer } from './footer'

interface DateRangeTimePanelProps {
nowIndex: number
Expand All @@ -14,8 +13,7 @@ interface DateRangeTimePanelProps {

// 选择日期范围,并且希望选择时间
export const DateRangeTimePanel = (props: DateRangeTimePanelProps) => {
const i18n = useLocaleContext()
const { outDate, onPick, disabledDate, prefixCls, footerRender } = useContext(DPContext)
const { outDate, onPick, disabledDate } = useContext(DPContext)
const { nowIndex, onChangeNowIndex } = props

const [range, setRange] = useState<CalenderSelectedRange>({
Expand Down Expand Up @@ -94,16 +92,6 @@ export const DateRangeTimePanel = (props: DateRangeTimePanelProps) => {
return nowIndex === 0 ? !range.start : !range.end
}, [nowIndex, range])

const footer = useMemo(() => {
const sureActionContent = (
<HiButton type="primary" disabled={isConfirmButtonDisabled} onClick={onConfirmButtonClick}>
{i18n.get('datePicker.ok')}
</HiButton>
)

return typeof footerRender === 'function' ? footerRender(sureActionContent) : sureActionContent
}, [footerRender, i18n, isConfirmButtonDisabled, onConfirmButtonClick])

return (
<React.Fragment>
<Panel
Expand All @@ -112,7 +100,7 @@ export const DateRangeTimePanel = (props: DateRangeTimePanelProps) => {
outDate={panelData}
range={range}
/>
<div className={`${prefixCls}__date-range-time-bottom`}>{footer}</div>
<Footer disabled={isConfirmButtonDisabled} onConfirmButtonClick={onConfirmButtonClick} />
</React.Fragment>
)
}
26 changes: 26 additions & 0 deletions packages/ui/date-picker/src/components/footer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React, { FC, useContext, useMemo } from 'react'
import { useLocaleContext } from '@hi-ui/core'
import HiButton from '@hi-ui/button'
import DPContext from '../context'

interface FooterProps {
disabled?: boolean
onConfirmButtonClick: () => void
}

export const Footer: FC<FooterProps> = ({ disabled, onConfirmButtonClick }) => {
const i18n = useLocaleContext()
const { prefixCls, footerRender, size } = useContext(DPContext)

const footer = useMemo(() => {
const sureActionContent = (
<HiButton type="primary" disabled={disabled} onClick={onConfirmButtonClick} size={size}>
{i18n.get('datePicker.ok')}
</HiButton>
)

return typeof footerRender === 'function' ? footerRender(sureActionContent) : sureActionContent
}, [disabled, footerRender, i18n, onConfirmButtonClick, size])

return <div className={`${prefixCls}__footer`}>{footer}</div>
}
119 changes: 70 additions & 49 deletions packages/ui/date-picker/src/components/panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,18 @@ import { timePickerValueAdaptor } from '../utils/timePickerValueAdaptor'
import { useTimePickerFormat } from '../hooks/useTimePickerFormat'
import { getBelongWeekRange } from '../utils/week'
import { CalenderSelectedRange } from '../hooks/useCalenderData'
import { Footer } from './footer'

interface PanelProps {
onPick: DPContextData['onPick']
outDate: (moment.Moment | null)[]
disabledDate: DPContextData['disabledDate']
range?: CalenderSelectedRange
needConfirm?: boolean
onConfirm?: DPContextData['onConfirm']
}
const Panel = (props: PanelProps) => {
const { onPick, outDate, range, disabledDate } = props
const { onPick: onPickProp, outDate, range, disabledDate, needConfirm, onConfirm } = props
const {
// outDate,
type,
Expand Down Expand Up @@ -60,6 +63,13 @@ const Panel = (props: PanelProps) => {
setCalRenderDates([rDate])
}, [outDate])

const onPick = useCallback(
(dates: (moment.Moment | null)[], isShowPanel?: boolean) => {
onPickProp(dates, needConfirm ? true : isShowPanel)
},
[needConfirm, onPickProp]
)

const onCalenderPick = useCallback(
(date: moment.Moment) => {
onSelect(date as any, true)
Expand Down Expand Up @@ -116,7 +126,7 @@ const Panel = (props: PanelProps) => {
const panelCls = cx(
`${prefixCls}__panel`,
`theme__${theme}`,
showTime && `${prefixCls}__panel--time ${prefixCls}__panel--noshadow`
(showTime || needConfirm) && `${prefixCls}__panel--time ${prefixCls}__panel--noshadow`
)

const timePickerFormat = useTimePickerFormat(realFormat)
Expand Down Expand Up @@ -149,56 +159,67 @@ const Panel = (props: PanelProps) => {
}

return (
<div className={panelCls}>
<div className={`${prefixCls}__panel--left`}>
{calRenderDates[0] && (
<React.Fragment>
<Header
renderDate={calRenderDates[0]}
// 疑问:尚未找到这个 props
// renderDates={calRenderDates}
changeView={() => setView('year')}
onArrowEvent={onArrowEvent}
i18n={i18n}
view={view}
panelPosition={0}
// 疑问:尚未找到这个 props
// type={type}
locale={locale}
prefixCls={prefixCls}
/>
<Calendar
renderDate={calRenderDates[0]}
originDate={outDate[0]}
onPick={onCalenderPick}
view={view}
disabledDate={disabledDate}
range={range}
// panelPosition="left"
/>
</React.Fragment>
<>
<div className={panelCls}>
<div className={`${prefixCls}__panel--left`}>
{calRenderDates[0] && (
<React.Fragment>
<Header
renderDate={calRenderDates[0]}
// 疑问:尚未找到这个 props
// renderDates={calRenderDates}
changeView={() => setView('year')}
onArrowEvent={onArrowEvent}
i18n={i18n}
view={view}
panelPosition={0}
// 疑问:尚未找到这个 props
// type={type}
locale={locale}
prefixCls={prefixCls}
/>
<Calendar
renderDate={calRenderDates[0]}
originDate={outDate[0]}
onPick={onCalenderPick}
view={view}
disabledDate={disabledDate}
range={range}
// panelPosition="left"
/>
</React.Fragment>
)}
</div>
{showTime && (
<div className={`${prefixCls}__panel__time-container`}>
<div className={`${prefixCls}__panel__time-header`}>{timePickerData[0]}</div>
<div className={`${prefixCls}__panel__time-content`}>
<TimePickerPopContent
onChange={onTimeChange}
value={timePickerData}
hourStep={hourStep}
minuteStep={minuteStep}
secondStep={secondStep}
type="single"
format={timePickerFormat}
disabledHours={disabledHours}
disabledMinutes={disabledMinutes}
disabledSeconds={disabledSeconds}
/>
</div>
</div>
)}
</div>
{showTime && (
<div className={`${prefixCls}__panel__time-container`}>
<div className={`${prefixCls}__panel__time-header`}>{timePickerData[0]}</div>
<div className={`${prefixCls}__panel__time-content`}>
<TimePickerPopContent
onChange={onTimeChange}
value={timePickerData}
hourStep={hourStep}
minuteStep={minuteStep}
secondStep={secondStep}
type="single"
format={timePickerFormat}
disabledHours={disabledHours}
disabledMinutes={disabledMinutes}
disabledSeconds={disabledSeconds}
/>
</div>
</div>
{needConfirm && (
<Footer
disabled={!outDate[0]}
onConfirmButtonClick={() => {
onPickProp(calRenderDates, false)
onConfirm?.(calRenderDates[0].toDate())
}}
/>
)}
</div>
</>
)
}

Expand Down
5 changes: 3 additions & 2 deletions packages/ui/date-picker/src/components/range-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -384,10 +384,11 @@ const RangePanel = () => {
))}
</div>
</div>
{/* 目前不会执行到该逻辑 */}
{type === 'daterange' && showTime && (
<div
className={`${prefixCls}__footer ${
!isDisableFooter ? `${prefixCls}__footer--disable` : ''
className={`${prefixCls}-old ${
!isDisableFooter ? `${prefixCls}__footer-old--disable` : ''
}`}
onClick={() => {
isDisableFooter && setShowRangeMask(true)
Expand Down
6 changes: 3 additions & 3 deletions packages/ui/date-picker/src/styles/date-picker.scss
Original file line number Diff line number Diff line change
Expand Up @@ -708,7 +708,7 @@ $calendar-large-background-border-radius: $calendar-large-indicator-border-radiu
}
}

&__footer {
&__footer-old {
height: 48px;
border-top: 1px solid use-color('gray', 100);
box-sizing: border-box;
Expand All @@ -719,7 +719,7 @@ $calendar-large-background-border-radius: $calendar-large-indicator-border-radiu
font-size: 14px;
width: 100%;

&:not(.#{$prefix}__footer--disable):hover {
&:not(.#{$prefix}__footer-old--disable):hover {
color: use-color-mode('primary');
}

Expand Down Expand Up @@ -866,7 +866,7 @@ $calendar-large-background-border-radius: $calendar-large-indicator-border-radiu
}
}

&__date-range-time-bottom {
&__footer {
display: flex;
justify-content: space-between;
padding: 6px 8px;
Expand Down
9 changes: 9 additions & 0 deletions packages/ui/date-picker/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -287,4 +287,13 @@ export interface DatePickerProps extends Omit<HiBaseHTMLProps<'div'>, 'placehold
* 选择框前置内容
*/
prefix?: React.ReactNode
/**
* 是否需要确认按钮,为 <code>false</code> 时失去焦点即代表选择。日期时间范围选择默认为 <code>true</code>
* @default false
*/
needConfirm?: boolean
/**
* 点击确认按钮的回调
*/
onConfirm?: (date: Date) => void
}
1 change: 1 addition & 0 deletions packages/ui/date-picker/stories/index.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export * from './lunar.stories'
export * from './size.stories'
export * from './footer-render.stories'
export * from './addon.stories'
export * from './need-confirm.stories'

export default {
title: 'Data Input/DatePicker',
Expand Down
Loading