• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

teableio / teable / 8387093605

22 Mar 2024 07:48AM CUT coverage: 28.027% (-0.2%) from 28.222%
8387093605

Pull #484

github

web-flow
Merge 174ef76f7 into a06c6afb1
Pull Request #484: feat: support increment import

2099 of 3218 branches covered (65.23%)

24 of 703 new or added lines in 18 files covered. (3.41%)

49 existing lines in 6 files now uncovered.

25815 of 92109 relevant lines covered (28.03%)

5.52 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/packages/sdk/src/components/editor/date/EditorMain.tsx
1
import { type IDateFieldOptions, TimeFormatting } from '@teable/core';
×
2
import { Button, Calendar, Input } from '@teable/ui-lib';
×
3
import { enUS, zhCN } from 'date-fns/locale';
×
4
import { formatInTimeZone, toDate, utcToZonedTime, zonedTimeToUtc } from 'date-fns-tz';
×
5
import type { ForwardRefRenderFunction } from 'react';
×
6
import { forwardRef, useContext, useImperativeHandle, useMemo, useRef, useState } from 'react';
×
7
import { AppContext } from '../../../context';
×
8
import { useTranslation } from '../../../context/app/i18n';
×
9
import type { ICellEditor, IEditorRef } from '../type';
×
10

×
11
export interface IDateEditorMain extends ICellEditor<string | null> {
×
12
  style?: React.CSSProperties;
×
13
  options?: IDateFieldOptions;
×
14
  disableTimePicker?: boolean;
×
15
}
×
16

×
17
const LOCAL_MAP = {
×
18
  zh: zhCN,
×
19
  en: enUS,
×
20
};
×
21

×
22
const DateEditorMainBase: ForwardRefRenderFunction<IEditorRef<string>, IDateEditorMain> = (
×
23
  props,
×
24
  ref
×
25
) => {
×
26
  const { value, style, className, onChange, readonly, options, disableTimePicker = false } = props;
×
27
  const inputRef = useRef<HTMLInputElement | null>(null);
×
28
  const { time, timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone } =
×
29
    options?.formatting || {};
×
30
  const [date, setDate] = useState<string | null>(value || null);
×
31
  const notHaveTimePicker = disableTimePicker || time === TimeFormatting.None;
×
32
  const defaultFocusRef = useRef<HTMLInputElement | null>(null);
×
33
  const { lang = 'en' } = useContext(AppContext);
×
34
  const { t } = useTranslation();
×
35

×
36
  useImperativeHandle(ref, () => ({
×
37
    focus: () => defaultFocusRef.current?.focus?.(),
×
38
    setValue: (value?: string) => setDate(value || null),
×
39
    saveValue,
×
40
  }));
×
41

×
42
  const onSelect = (value?: Date) => {
×
43
    if (!value) return onChange?.(null);
×
44

×
45
    const curDatetime = zonedTimeToUtc(value, timeZone);
×
46

×
47
    if (date) {
×
48
      const prevDatetime = toDate(date, { timeZone });
×
49

×
50
      curDatetime.setHours(prevDatetime.getHours());
×
51
      curDatetime.setMinutes(prevDatetime.getMinutes());
×
52
      curDatetime.setSeconds(prevDatetime.getSeconds());
×
53
    } else {
×
54
      const tempDate = now();
×
55

×
56
      curDatetime.setHours(tempDate.getHours());
×
57
      curDatetime.setMinutes(tempDate.getMinutes());
×
58
      curDatetime.setSeconds(tempDate.getSeconds());
×
59
    }
×
60

×
61
    const dateStr = curDatetime.toISOString();
×
62
    setDate(dateStr);
×
63
    onChange?.(dateStr);
×
64
  };
×
65

×
66
  const timeValue = useMemo(() => {
×
67
    if (!date) return '';
×
68
    return formatInTimeZone(date, timeZone, 'HH:mm');
×
69
  }, [date, timeZone]);
×
70

×
71
  const selectedDate = useMemo(() => {
×
72
    if (!date) {
×
73
      return;
×
74
    }
×
75

×
76
    return utcToZonedTime(date, timeZone);
×
77
  }, [date, timeZone]);
×
78

×
79
  const onTimeChange: React.ChangeEventHandler<HTMLInputElement> = (e) => {
×
80
    if (!date) return;
×
81
    const datetime = utcToZonedTime(date, timeZone);
×
82
    const timeValue = e.target.value;
×
83

×
84
    const hours = Number.parseInt(timeValue.split(':')[0] || '00', 10);
×
85
    const minutes = Number.parseInt(timeValue.split(':')[1] || '00', 10);
×
86

×
87
    datetime.setHours(hours);
×
88
    datetime.setMinutes(minutes);
×
89

×
90
    setDate(zonedTimeToUtc(datetime, timeZone).toISOString());
×
91
  };
×
92

×
93
  const saveValue = (nowDate?: string) => {
×
94
    const val = nowDate || date;
×
95

×
96
    if (value == val) return;
×
97
    onChange?.(val);
×
98
  };
×
99

×
100
  const now = () => {
×
101
    return zonedTimeToUtc(new Date(), timeZone);
×
102
  };
×
103

×
104
  return (
×
105
    <>
×
106
      <Calendar
×
107
        locale={LOCAL_MAP[lang as keyof typeof LOCAL_MAP]}
×
108
        style={style}
×
109
        mode="single"
×
110
        selected={selectedDate}
×
111
        defaultMonth={selectedDate}
×
112
        onSelect={onSelect}
×
113
        className={className}
×
114
        disabled={readonly}
×
115
        fromYear={1970}
×
116
        toYear={2100}
×
UNCOV
117
        captionLayout="dropdown-buttons"
×
UNCOV
118
        footer={
×
UNCOV
119
          <div className="flex items-center justify-center p-1">
×
UNCOV
120
            {!notHaveTimePicker && date ? (
×
UNCOV
121
              <Input
×
UNCOV
122
                className="mr-3 w-7/12"
×
UNCOV
123
                ref={inputRef}
×
UNCOV
124
                type="time"
×
UNCOV
125
                value={timeValue}
×
UNCOV
126
                onChange={onTimeChange}
×
UNCOV
127
                onBlur={() => saveValue()}
×
UNCOV
128
              />
×
UNCOV
129
            ) : null}
×
UNCOV
130
            <Button
×
UNCOV
131
              className="h-[34px] w-2/5 text-sm"
×
UNCOV
132
              size="sm"
×
UNCOV
133
              onClick={() => {
×
UNCOV
134
                saveValue(now().toISOString());
×
UNCOV
135
              }}
×
UNCOV
136
            >
×
UNCOV
137
              {t('editor.date.today')}
×
UNCOV
138
            </Button>
×
UNCOV
139
          </div>
×
UNCOV
140
        }
×
UNCOV
141
      />
×
UNCOV
142
      <input className="invisible size-0 opacity-0" ref={defaultFocusRef} />
×
UNCOV
143
    </>
×
UNCOV
144
  );
×
UNCOV
145
};
×
UNCOV
146

×
UNCOV
147
export const DateEditorMain = forwardRef(DateEditorMainBase);
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc