Skip to content

Commit

Permalink
feat: support multiple select
Browse files Browse the repository at this point in the history
  • Loading branch information
uyarn committed Dec 26, 2024
1 parent 55cc765 commit b36f10c
Show file tree
Hide file tree
Showing 8 changed files with 118 additions and 86 deletions.
6 changes: 4 additions & 2 deletions src/date-picker/DatePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ export default defineComponent({
if (props.multiple) {
const newDate = processDate(date);
onChange(newDate, {
dayjsValue: parseToDayjs(date, props.format),
dayjsValue: parseToDayjs(date, formatRef.value.format),
trigger: 'pick',
});
return;
Expand All @@ -171,6 +171,7 @@ export default defineComponent({
}

function processDate(date: Date) {
if (!value.value) return [];
const isSameDate = (value.value as DateMultipleValue).some((val) => isSame(dayjs(val).toDate(), date));
let currentDate: DateMultipleValue;

Expand Down Expand Up @@ -311,7 +312,8 @@ export default defineComponent({
format: formatRef.value.format,
mode: props.mode,
presets: props.presets,
time: props.multiple ? false : time.value,
multiple: props.multiple,
time: props.multiple ? '' : time.value,
disableDate: props.disableDate,
firstDayOfWeek: props.firstDayOfWeek,
timePickerProps: props.timePickerProps,
Expand Down
12 changes: 6 additions & 6 deletions src/date-picker/DatePickerPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export default defineComponent({
}

// 头部快速切换
function onJumperClick({ trigger }: { trigger: string }) {
function onJumperClick({ trigger }: { trigger: 'prev' | 'next' | 'current' }) {
const triggerMap = {
prev: 'arrow-previous',
next: 'arrow-next',
Expand All @@ -91,14 +91,14 @@ export default defineComponent({
if (year.value !== nextYear) {
props.onYearChange?.({
year: nextYear,
date: dayjs(value.value).toDate(),
date: dayjs(value.value as DateValue).toDate(),
trigger: trigger === 'current' ? 'today' : (`year-${triggerMap[trigger]}` as DatePickerYearChangeTrigger),
});
}
if (month.value !== nextMonth) {
props.onMonthChange?.({
month: nextMonth,
date: dayjs(value.value).toDate(),
date: dayjs(value.value as DateValue).toDate(),
trigger: trigger === 'current' ? 'today' : (`month-${triggerMap[trigger]}` as DatePickerMonthChangeTrigger),
});
}
Expand All @@ -125,7 +125,7 @@ export default defineComponent({

props.onTimeChange?.({
time: val,
date: dayjs(value.value).toDate(),
date: dayjs(value.value as DateValue).toDate(),
trigger: 'time-hour',
});
}
Expand Down Expand Up @@ -159,7 +159,7 @@ export default defineComponent({

props.onYearChange?.({
year: year.value,
date: dayjs(value.value).toDate(),
date: dayjs(value.value as DateValue).toDate(),
trigger: 'year-select',
});
}
Expand All @@ -169,7 +169,7 @@ export default defineComponent({

props.onMonthChange?.({
month: month.value,
date: dayjs(value.value).toDate(),
date: dayjs(value.value as DateValue).toDate(),
trigger: 'month-select',
});
}
Expand Down
4 changes: 1 addition & 3 deletions src/date-picker/base/Table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,7 @@ export default defineComponent({
}

return {
[`${COMPONENT_NAME.value}-${props.mode}-row--active`]:
parseToDayjs(value, format).locale(dayjsLocale).week() ===
parseToDayjs(targetValue, format).locale(dayjsLocale).week(),
[`${COMPONENT_NAME.value}-${props.mode}-row--active`]: true,
};
};

Expand Down
131 changes: 68 additions & 63 deletions src/date-picker/hooks/useSingle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export default function useSingle(props: TdDatePickerProps) {
mode: props.mode,
format: props.format,
valueType: props.valueType,
enableTimePicker: props.enableTimePicker,
enableTimePicker: props.multiple ? false : props.enableTimePicker,
}),
);

Expand All @@ -44,77 +44,82 @@ export default function useSingle(props: TdDatePickerProps) {
const inputValue = ref(formatDate(value.value, { format: formatRef.value.format }));

// input 设置
const inputProps = computed(() => ({
...props.inputProps,
size: props.size,
ref: inputRef,
prefixIcon: () => renderTNodeJSX('prefixIcon'),
readonly: isReadOnly.value || !props.allowInput,
suffixIcon: () => {
return renderTNodeJSX('suffixIcon') || <CalendarIcon />;
},
class: [
{
[`${COMPONENT_NAME.value}__input--placeholder`]: isHoverCell.value,
const inputProps = computed(() => {
const defaultInputProps = {
...props.inputProps,
size: props.size,
ref: inputRef,
prefixIcon: () => renderTNodeJSX('prefixIcon'),
readonly: isReadOnly.value || !props.allowInput,
suffixIcon: () => {
return renderTNodeJSX('suffixIcon') || <CalendarIcon />;
},
],
onClear: (context: { e: InputEvent }) => {
context?.e?.stopPropagation();
popupVisible.value = false;
onChange?.('', { dayjsValue: dayjs(), trigger: 'clear' });
},
onBlur: (val: string, context: { e: FocusEvent }) => {
props.onBlur?.({ value: val, e: context.e });
},
onFocus: (_: string, { e }: { e: FocusEvent }) => {
props.onFocus?.({ value: value.value, e });
},
onChange: (val: string) => {
// 输入事件
inputValue.value = val;

// 跳过不符合格式化的输入框内容
if (!isValidDate(val, formatRef.value.format)) return;
cacheValue.value = val;
const newMonth = parseToDayjs(val, formatRef.value.format).month();
const newYear = parseToDayjs(val, formatRef.value.format).year();
const newTime = formatTime(val, formatRef.value.format, formatRef.value.timeFormat, props.defaultTime);
!Number.isNaN(newYear) && (year.value = newYear);
!Number.isNaN(newMonth) && (month.value = newMonth);
!Number.isNaN(newTime) && (time.value = newTime);
},
onEnter: (val: string) => {
if (!val) {
onChange('', { dayjsValue: dayjs(), trigger: 'enter' });
class: [
{
[`${COMPONENT_NAME.value}__input--placeholder`]: isHoverCell.value,
},
],
onClear: (context: { e: InputEvent }) => {
context?.e?.stopPropagation();
popupVisible.value = false;
return;
}
onChange?.('', { dayjsValue: dayjs(), trigger: 'clear' });
},
onBlur: (val: string, context: { e: FocusEvent }) => {
props.onBlur?.({ value: val, e: context.e });
},
onFocus: (_: string, { e }: { e: FocusEvent }) => {
props.onFocus?.({ value: value.value, e });
},
onChange: (val: string) => {
// 输入事件
inputValue.value = val;

if (!isValidDate(val, formatRef.value.format) && !isValidDate(value.value, formatRef.value.format)) return;
// 跳过不符合格式化的输入框内容
if (!isValidDate(val, formatRef.value.format)) return;
cacheValue.value = val;
const newMonth = parseToDayjs(val, formatRef.value.format).month();
const newYear = parseToDayjs(val, formatRef.value.format).year();
const newTime = formatTime(val, formatRef.value.format, formatRef.value.timeFormat, props.defaultTime);
!Number.isNaN(newYear) && (year.value = newYear);
!Number.isNaN(newMonth) && (month.value = newMonth);
!Number.isNaN(newTime) && (time.value = newTime);
},
onEnter: (val: string) => {
if (!val) {
onChange('', { dayjsValue: dayjs(), trigger: 'enter' });
popupVisible.value = false;
return;
}

if (!isValidDate(val, formatRef.value.format) && !isValidDate(value.value, formatRef.value.format)) return;

popupVisible.value = false;
if (isValidDate(val, formatRef.value.format)) {
onChange?.(
formatDate(val, { format: formatRef.value.format, targetFormat: formatRef.value.valueType }) as DateValue,
{
dayjsValue: parseToDayjs(val, formatRef.value.format),
trigger: 'enter',
},
);
} else if (isValidDate(value.value, formatRef.value.format)) {
inputValue.value = formatDate(value.value, {
format: formatRef.value.format,
});
} else {
inputValue.value = '';
}
},
};
return props.multiple ? omit(defaultInputProps, ['ref', 'class']) : defaultInputProps;
});

popupVisible.value = false;
if (isValidDate(val, formatRef.value.format)) {
onChange?.(
formatDate(val, { format: formatRef.value.format, targetFormat: formatRef.value.valueType }) as DateValue,
{
dayjsValue: parseToDayjs(val, formatRef.value.format),
trigger: 'enter',
},
);
} else if (isValidDate(value.value, formatRef.value.format)) {
inputValue.value = formatDate(value.value, {
format: formatRef.value.format,
});
} else {
inputValue.value = '';
}
},
}));
// popup 设置
const popupProps = computed(() => ({
expandAnimation: true,
...omit(props.popupProps, 'on-visible-change'),
disabled: disabled.value,
trigger: 'click',
overlayInnerStyle: props.popupProps?.overlayInnerStyle ?? { width: 'auto' },
overlayClassName: [props.popupProps?.overlayClassName, `${COMPONENT_NAME.value}__panel-container`],
onVisibleChange: (visible: boolean, context: any) => {
Expand Down
33 changes: 27 additions & 6 deletions src/date-picker/hooks/useSingleValue.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
parseToDayjs,
} from '../../_common/js/date-picker/format';
import useVModel from '../../hooks/useVModel';
import { TdDatePickerProps } from '../type';
import { TdDatePickerProps, DateMultipleValue, DateValue } from '../type';
import { extractTimeFormat } from '../../_common/js/date-picker/utils';

export default function useSingleValue(props: TdDatePickerProps) {
Expand All @@ -18,7 +18,7 @@ export default function useSingleValue(props: TdDatePickerProps) {
getDefaultFormat({
mode: props.mode,
format: props.format,
enableTimePicker: props.enableTimePicker,
enableTimePicker: props.multiple ? false : props.enableTimePicker,
}),
);

Expand All @@ -27,10 +27,31 @@ export default function useSingleValue(props: TdDatePickerProps) {
console.error(`format: ${formatRef.value.format} 不规范,包含时间选择必须要有时间格式化 HH:mm:ss`);
}

const time = ref(formatTime(value.value, formatRef.value.format, formatRef.value.timeFormat, props.defaultTime));
const month = ref<number>(parseToDayjs(value.value, formatRef.value.format).month());
const year = ref<number>(parseToDayjs(value.value, formatRef.value.format).year());
const cacheValue = ref(formatDate(value.value, { format: formatRef.value.format })); // 缓存选中值,panel 点击时更改
const time = ref(
formatTime(
props.multiple ? (value.value as DateMultipleValue)[0] : value.value,
formatRef.value.format,
formatRef.value.timeFormat,
props.defaultTime,
),
);
const month = ref<number>(
parseToDayjs(
props.multiple ? (value.value as DateMultipleValue)[0] : (value.value as DateValue),
formatRef.value.format,
).month(),
);
const year = ref<number>(
parseToDayjs(
props.multiple ? (value.value as DateMultipleValue)[0] : (value.value as DateValue),
formatRef.value.format,
).year(),
);
const cacheValue = ref(
formatDate(props.multiple ? (value.value as DateMultipleValue)[0] : value.value, {
format: formatRef.value.format,
}),
); // 缓存选中值,panel 点击时更改

// 输入框响应 value 变化
watchEffect(() => {
Expand Down
15 changes: 11 additions & 4 deletions src/date-picker/panel/SinglePanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ import { defineComponent, PropType, computed } from 'vue';
import { useConfig, usePrefixClass } from '../../hooks/useConfig';
import TPanelContent from './PanelContent';
import TExtraContent from './ExtraContent';
import { TdDatePickerProps } from '../type';
import { getDefaultFormat, parseToDayjs } from '../../_common/js/date-picker/format';
import useTableData from '../hooks/useTableData';
import useDisableDate from '../hooks/useDisableDate';

import type { TdDatePickerProps, DateMultipleValue, DateValue } from '../type';

export default defineComponent({
name: 'TSinglePanel',
props: {
Expand All @@ -29,6 +30,7 @@ export default defineComponent({
month: Number,
time: String,
popupVisible: Boolean,
multiple: Boolean,
needConfirm: {
type: Boolean,
default: true,
Expand Down Expand Up @@ -64,14 +66,19 @@ export default defineComponent({
disableDate: props.disableDate,
}),
);

const tableData = computed(() =>
useTableData({
year: props.year,
month: props.month,
mode: props.mode,
start: props.value ? parseToDayjs(props.value, format.value).toDate() : undefined,
start: props.value
? parseToDayjs(
props.multiple ? (props.value as DateMultipleValue)[0] : (props.value as DateValue),
format.value,
).toDate()
: undefined,
firstDayOfWeek: props.firstDayOfWeek || globalConfig.value.firstDayOfWeek,
multiple: props.multiple,
...disableDateOptions.value,
}),
);
Expand All @@ -85,7 +92,7 @@ export default defineComponent({
firstDayOfWeek: props.firstDayOfWeek || globalConfig.value.firstDayOfWeek,
tableData: tableData.value,
popupVisible: props.popupVisible,

multiple: props.multiple,
enableTimePicker: props.enableTimePicker,
timePickerProps: props.timePickerProps,
time: props.time,
Expand Down
1 change: 0 additions & 1 deletion src/date-picker/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,6 @@ export interface TdDatePickerPanelProps
| 'value'
| 'defaultValue'
| 'disableDate'
| 'disableTime'
| 'enableTimePicker'
| 'firstDayOfWeek'
| 'format'
Expand Down
2 changes: 1 addition & 1 deletion src/tag-input/tag-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ export default defineComponent({
const onClick: TdInputProps['onClick'] = (ctx) => {
if (isDisabled.value) return;
isFocused.value = true;
tagInputRef.value.focus();
tagInputRef.value?.focus();
props.onClick?.(ctx);
};

Expand Down

0 comments on commit b36f10c

Please sign in to comment.