refactor(core): refactor date-kit utils (#6781)

This commit is contained in:
白熱
2026-04-08 18:55:57 +08:00
committed by GitHub
parent 804206b8aa
commit 8bcb4424ca
38 changed files with 736 additions and 184 deletions
+1 -2
View File
@@ -78,10 +78,9 @@
"@univerjs/themes": "workspace:*",
"@wendellhu/redi": "1.1.1",
"async-lock": "^1.4.1",
"dayjs": "^1.11.20",
"fast-diff": "1.3.0",
"kdbush": "^4.0.2",
"lodash-es": "^4.17.23",
"lodash-es": "^4.18.1",
"nanoid": "5.1.7",
"numfmt": "^3.2.3",
"ot-json1": "^1.0.2",
@@ -15,7 +15,6 @@
*/
import type { IAccessor, IDisposable } from '../../common/di';
import type { IKeyValue } from '../../shared/types';
import { findLast, remove } from '../../common/array';
import { createIdentifier, Inject, Injector } from '../../common/di';
@@ -542,7 +541,7 @@ export class CommandService extends Disposable implements ICommandService {
this._multiCommandDisposables.set(command.id, disposableCollection);
} else {
if ((registry[0] as IKeyValue).multi !== true) {
if ((registry[0] as Record<string, any>).multi !== true) {
throw new Error('Command has registered as a single command.');
} else {
multiCommand = registry[0] as MultiCommand;
@@ -0,0 +1,59 @@
/**
* Copyright 2023-present DreamNum Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { describe, expect, it } from 'vitest';
import { dateKit } from '../date-kit';
describe('date-kit', () => {
it('should format common date tokens', () => {
expect(dateKit('2024-01-02 03:04:05').format('YYYY-MM-DD HH:mm:ss')).toBe('2024-01-02 03:04:05');
expect(dateKit('2024-01-02 03:04:05').format('YYYY/MM/DD HH:mm')).toBe('2024/01/02 03:04');
});
it('should parse cjk date strings', () => {
expect(dateKit('2020年11月11日').format('YYYY-MM-DD')).toBe('2020-11-11');
});
it('should keep month addition clamped to month end', () => {
expect(dateKit('2024-01-31 12:00:00').add(1, 'month').format('YYYY-MM-DD')).toBe('2024-02-29');
expect(dateKit('2024-03-31 12:00:00').subtract(1, 'month').format('YYYY-MM-DD')).toBe('2024-02-29');
});
it('should compute week boundaries with sunday as start', () => {
const value = dateKit('2024-01-03 10:20:30');
expect(value.startOf('week').format('YYYY-MM-DD')).toBe('2023-12-31');
expect(value.endOf('week').format('YYYY-MM-DD')).toBe('2024-01-06');
});
it('should support weekday and week helpers', () => {
expect(dateKit('2024-01-07').weekday()).toBe(0);
expect(dateKit('2024-01-07').weekday(1).format('YYYY-MM-DD')).toBe('2024-01-08');
expect(dateKit('2024-01-01').week()).toBeGreaterThanOrEqual(1);
});
it('should support localized and advanced format tokens', () => {
const value = dateKit('2024-02-01 15:04:05');
expect(value.format('L')).toBe('02/01/2024');
expect(value.format('Do')).toBe('1st');
expect(value.format('Qo')).toBe('1st');
});
it('should expose static helpers', () => {
expect(dateKit.utc('2024-01-01 00:00:00').format('Z')).toBe('+00:00');
expect(dateKit.unix(1704067200).valueOf()).toBe(1704067200000);
expect(dateKit.isDateKit(dateKit())).toBe(true);
});
});
+528
View File
@@ -0,0 +1,528 @@
/**
* Copyright 2023-present DreamNum Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
type DateKitCoreUnit = 'millisecond' | 'second' | 'minute' | 'hour' | 'day' | 'week' | 'month' | 'year';
export type DateKitUnitType =
| DateKitCoreUnit
| 'milliseconds'
| 'ms'
| 'seconds'
| 's'
| 'minutes'
| 'm'
| 'hours'
| 'h'
| 'days'
| 'd'
| 'weeks'
| 'w'
| 'months'
| 'M'
| 'years'
| 'y';
export type DateKitInput = Date | number | string | IDateKit | null | undefined;
const MILLISECOND = 1;
const SECOND = 1000 * MILLISECOND;
const MINUTE = 60 * SECOND;
const HOUR = 60 * MINUTE;
const DAY = 24 * HOUR;
const WEEK_START_DAY = 0; // Sunday
const MONTH_NAMES = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
const MONTH_NAMES_SHORT = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
const WEEKDAY_NAMES = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
const WEEKDAY_NAMES_SHORT = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
const WEEKDAY_NAMES_MIN = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'];
const LOCALIZED_FORMAT_MAP: Record<string, string> = {
L: 'MM/DD/YYYY',
LL: 'MMMM D, YYYY',
LLL: 'MMMM D, YYYY h:mm A',
LLLL: 'dddd, MMMM D, YYYY h:mm A',
l: 'M/D/YYYY',
ll: 'MMM D, YYYY',
lll: 'MMM D, YYYY h:mm A',
llll: 'ddd, MMM D, YYYY h:mm A',
};
const UNIT_ALIASES: Record<string, DateKitCoreUnit> = {
millisecond: 'millisecond',
milliseconds: 'millisecond',
ms: 'millisecond',
second: 'second',
seconds: 'second',
s: 'second',
minute: 'minute',
minutes: 'minute',
m: 'minute',
hour: 'hour',
hours: 'hour',
h: 'hour',
day: 'day',
days: 'day',
d: 'day',
week: 'week',
weeks: 'week',
w: 'week',
month: 'month',
months: 'month',
M: 'month',
year: 'year',
years: 'year',
y: 'year',
};
interface IDateParts {
year: number;
month: number;
day: number;
hour: number;
minute: number;
second: number;
millisecond: number;
dayOfWeek: number;
}
export interface IDateKit {
isValid(): boolean;
format(template?: string): string;
valueOf(): number;
toDate(): Date;
add(value: number, unit?: DateKitUnitType): IDateKit;
subtract(value: number, unit?: DateKitUnitType): IDateKit;
startOf(unit: DateKitUnitType): IDateKit;
endOf(unit: DateKitUnitType): IDateKit;
utc(): IDateKit;
local(): IDateKit;
weekday(): number;
weekday(value: number): IDateKit;
week(): number;
week(value: number): IDateKit;
}
export type DateKit = IDateKit;
export type OpUnitType = DateKitUnitType;
class DateKitImpl implements IDateKit {
constructor(private readonly _date: Date, private readonly _isUTC: boolean) {}
isValid() {
return !Number.isNaN(this._date.getTime());
}
format(template = 'YYYY-MM-DDTHH:mm:ssZ') {
if (!this.isValid()) {
return 'Invalid Date';
}
return formatDate(this._date, template, this._isUTC);
}
valueOf() {
return this._date.getTime();
}
toDate() {
return new Date(this.valueOf());
}
private _clone() {
return new DateKitImpl(this.toDate(), this._isUTC);
}
add(value: number, unit: DateKitUnitType = 'millisecond') {
const normalizedUnit = normalizeUnit(unit);
if (!this.isValid() || !normalizedUnit || !Number.isFinite(value)) {
return this._clone();
}
return new DateKitImpl(addDate(this._date, value, normalizedUnit, this._isUTC), this._isUTC);
}
subtract(value: number, unit: DateKitUnitType = 'millisecond') {
return this.add(-value, unit);
}
startOf(unit: DateKitUnitType) {
const normalizedUnit = normalizeUnit(unit);
if (!normalizedUnit || !this.isValid()) {
return this._clone();
}
return new DateKitImpl(startOf(this._date, normalizedUnit, this._isUTC), this._isUTC);
}
endOf(unit: DateKitUnitType) {
const normalizedUnit = normalizeUnit(unit);
if (!normalizedUnit || !this.isValid()) {
return this._clone();
}
if (normalizedUnit === 'millisecond') {
return this._clone();
}
return this.startOf(normalizedUnit).add(1, normalizedUnit).subtract(1, 'millisecond');
}
utc() {
return new DateKitImpl(this.toDate(), true);
}
local() {
return new DateKitImpl(this.toDate(), false);
}
weekday(): number;
weekday(value: number): IDateKit;
weekday(value?: number): number | IDateKit {
const day = getDateParts(this._date, this._isUTC).dayOfWeek;
const current = (day - WEEK_START_DAY + 7) % 7;
if (value === undefined) {
return current;
}
return this.add(value - current, 'day');
}
week(): number;
week(value: number): IDateKit;
week(value?: number): number | IDateKit {
const currentWeek = getWeekOfYear(this._date, this._isUTC);
if (value === undefined) {
return currentWeek;
}
return this.add((value - currentWeek) * 7, 'day');
}
}
function pad(value: number, length = 2) {
return String(Math.abs(value)).padStart(length, '0');
}
function getDateParts(date: Date, isUTC: boolean): IDateParts {
return isUTC
? {
year: date.getUTCFullYear(),
month: date.getUTCMonth() + 1,
day: date.getUTCDate(),
hour: date.getUTCHours(),
minute: date.getUTCMinutes(),
second: date.getUTCSeconds(),
millisecond: date.getUTCMilliseconds(),
dayOfWeek: date.getUTCDay(),
}
: {
year: date.getFullYear(),
month: date.getMonth() + 1,
day: date.getDate(),
hour: date.getHours(),
minute: date.getMinutes(),
second: date.getSeconds(),
millisecond: date.getMilliseconds(),
dayOfWeek: date.getDay(),
};
}
function toDate(input: DateKitInput, isUTC: boolean) {
if (input instanceof DateKitImpl) {
return input.toDate();
}
if (input === undefined) {
return new Date();
}
if (input === null) {
return new Date(Number.NaN);
}
if (input instanceof Date) {
return new Date(input.getTime());
}
if (typeof input === 'number') {
return new Date(input);
}
if (typeof input === 'string') {
return parseDateString(input, isUTC);
}
return new Date(Number.NaN);
}
function createDateWithParts(year: number, month: number, day: number, hour: number, minute: number, second: number, millisecond: number, isUTC: boolean) {
const parsed = isUTC
? new Date(Date.UTC(year, month - 1, day, hour, minute, second, millisecond))
: new Date(year, month - 1, day, hour, minute, second, millisecond);
const parts = getDateParts(parsed, isUTC);
if (
parts.year !== year
|| parts.month !== month
|| parts.day !== day
|| parts.hour !== hour
|| parts.minute !== minute
|| parts.second !== second
) {
return new Date(Number.NaN);
}
return parsed;
}
function parseDateString(input: string, isUTC: boolean) {
const text = input.trim();
if (!text) {
return new Date(Number.NaN);
}
const cjkMatch = text.match(/^(\d{4})年(\d{1,2})月(\d{1,2})日(?:\s+(\d{1,2})(?::(\d{1,2})(?::(\d{1,2}))?)?)?$/);
if (cjkMatch) {
const [, y, m, d, hh = '0', mm = '0', ss = '0'] = cjkMatch;
return createDateWithParts(Number(y), Number(m), Number(d), Number(hh), Number(mm), Number(ss), 0, isUTC);
}
const standardMatch = text.match(/^(\d{4})[-/](\d{1,2})[-/](\d{1,2})(?:[ T](\d{1,2})(?::(\d{1,2})(?::(\d{1,2})(?:\.(\d{1,3}))?)?)?)?$/);
if (standardMatch) {
const [, y, m, d, hh = '0', mm = '0', ss = '0', mss = '0'] = standardMatch;
return createDateWithParts(Number(y), Number(m), Number(d), Number(hh), Number(mm), Number(ss), Number(mss), isUTC);
}
if (/^-?\d+(\.\d+)?$/.test(text)) {
return new Date(Number(text));
}
return new Date(text);
}
function normalizeUnit(unit: DateKitUnitType) {
return UNIT_ALIASES[unit] ?? UNIT_ALIASES[String(unit).toLowerCase()];
}
function daysInMonth(year: number, month: number, isUTC: boolean) {
return isUTC
? new Date(Date.UTC(year, month, 0)).getUTCDate()
: new Date(year, month, 0).getDate();
}
function setDatePart(date: Date, isUTC: boolean, part: 'year' | 'month' | 'day' | 'hour' | 'minute' | 'second' | 'millisecond', value: number) {
const next = new Date(date.getTime());
if (isUTC) {
switch (part) {
case 'year':
next.setUTCFullYear(value);
break;
case 'month':
next.setUTCMonth(value);
break;
case 'day':
next.setUTCDate(value);
break;
case 'hour':
next.setUTCHours(value);
break;
case 'minute':
next.setUTCMinutes(value);
break;
case 'second':
next.setUTCSeconds(value);
break;
case 'millisecond':
next.setUTCMilliseconds(value);
break;
}
return next;
}
switch (part) {
case 'year':
next.setFullYear(value);
break;
case 'month':
next.setMonth(value);
break;
case 'day':
next.setDate(value);
break;
case 'hour':
next.setHours(value);
break;
case 'minute':
next.setMinutes(value);
break;
case 'second':
next.setSeconds(value);
break;
case 'millisecond':
next.setMilliseconds(value);
break;
}
return next;
}
function addDate(date: Date, value: number, unit: DateKitCoreUnit, isUTC: boolean) {
switch (unit) {
case 'millisecond':
return new Date(date.getTime() + value);
case 'second':
return new Date(date.getTime() + value * SECOND);
case 'minute':
return new Date(date.getTime() + value * MINUTE);
case 'hour':
return new Date(date.getTime() + value * HOUR);
case 'day':
return setDatePart(date, isUTC, 'day', (isUTC ? date.getUTCDate() : date.getDate()) + value);
case 'week':
return setDatePart(date, isUTC, 'day', (isUTC ? date.getUTCDate() : date.getDate()) + value * 7);
case 'month': {
const current = getDateParts(date, isUTC);
const anchor = createDateWithParts(current.year, current.month, 1, current.hour, current.minute, current.second, current.millisecond, isUTC);
const moved = setDatePart(anchor, isUTC, 'month', (isUTC ? anchor.getUTCMonth() : anchor.getMonth()) + value);
const movedParts = getDateParts(moved, isUTC);
const maxDay = daysInMonth(movedParts.year, movedParts.month, isUTC);
return setDatePart(moved, isUTC, 'day', Math.min(current.day, maxDay));
}
case 'year':
return addDate(date, value * 12, 'month', isUTC);
}
}
function startOf(date: Date, unit: DateKitCoreUnit, isUTC: boolean) {
const parts = getDateParts(date, isUTC);
switch (unit) {
case 'year':
return createDateWithParts(parts.year, 1, 1, 0, 0, 0, 0, isUTC);
case 'month':
return createDateWithParts(parts.year, parts.month, 1, 0, 0, 0, 0, isUTC);
case 'week': {
const day = parts.dayOfWeek;
const diff = (day - WEEK_START_DAY + 7) % 7;
const anchor = createDateWithParts(parts.year, parts.month, parts.day, 0, 0, 0, 0, isUTC);
return setDatePart(anchor, isUTC, 'day', (isUTC ? anchor.getUTCDate() : anchor.getDate()) - diff);
}
case 'day':
return createDateWithParts(parts.year, parts.month, parts.day, 0, 0, 0, 0, isUTC);
case 'hour':
return createDateWithParts(parts.year, parts.month, parts.day, parts.hour, 0, 0, 0, isUTC);
case 'minute':
return createDateWithParts(parts.year, parts.month, parts.day, parts.hour, parts.minute, 0, 0, isUTC);
case 'second':
return createDateWithParts(parts.year, parts.month, parts.day, parts.hour, parts.minute, parts.second, 0, isUTC);
case 'millisecond':
default:
return new Date(date.getTime());
}
}
function getWeekOfYear(date: Date, isUTC: boolean) {
const currentWeekStart = startOf(date, 'week', isUTC);
const yearStart = startOf(date, 'year', isUTC);
const firstWeekStart = startOf(yearStart, 'week', isUTC);
return Math.floor((currentWeekStart.getTime() - firstWeekStart.getTime()) / DAY / 7) + 1;
}
function getTimezoneString(date: Date, isUTC: boolean, compact = false) {
if (isUTC) {
return compact ? '+0000' : '+00:00';
}
const offsetMinutes = -date.getTimezoneOffset();
const sign = offsetMinutes >= 0 ? '+' : '-';
const abs = Math.abs(offsetMinutes);
const hours = pad(Math.floor(abs / 60), 2);
const minutes = pad(abs % 60, 2);
return compact ? `${sign}${hours}${minutes}` : `${sign}${hours}:${minutes}`;
}
function ordinal(value: number) {
const v = value % 100;
if (v >= 11 && v <= 13) {
return `${value}th`;
}
switch (value % 10) {
case 1:
return `${value}st`;
case 2:
return `${value}nd`;
case 3:
return `${value}rd`;
default:
return `${value}th`;
}
}
function replaceLocalizedTokens(template: string) {
return template.replace(/LLLL|LLL|LL|L|llll|lll|ll|l/g, (token) => LOCALIZED_FORMAT_MAP[token] ?? token);
}
function formatDate(date: Date, template: string, isUTC: boolean) {
const parts = getDateParts(date, isUTC);
const hour12Raw = parts.hour % 12;
const hour12 = hour12Raw === 0 ? 12 : hour12Raw;
const quarter = Math.ceil(parts.month / 3);
const tokenMap: Record<string, string> = {
YYYY: String(parts.year),
YY: pad(parts.year % 100, 2),
MMMM: MONTH_NAMES[parts.month - 1],
MMM: MONTH_NAMES_SHORT[parts.month - 1],
MM: pad(parts.month, 2),
M: String(parts.month),
DD: pad(parts.day, 2),
D: String(parts.day),
Do: ordinal(parts.day),
dddd: WEEKDAY_NAMES[parts.dayOfWeek],
ddd: WEEKDAY_NAMES_SHORT[parts.dayOfWeek],
dd: WEEKDAY_NAMES_MIN[parts.dayOfWeek],
d: String(parts.dayOfWeek),
HH: pad(parts.hour, 2),
H: String(parts.hour),
hh: pad(hour12, 2),
h: String(hour12),
mm: pad(parts.minute, 2),
m: String(parts.minute),
ss: pad(parts.second, 2),
s: String(parts.second),
SSS: pad(parts.millisecond, 3),
A: parts.hour >= 12 ? 'PM' : 'AM',
a: parts.hour >= 12 ? 'pm' : 'am',
Q: String(quarter),
Qo: ordinal(quarter),
X: String(Math.floor(date.getTime() / 1000)),
x: String(date.getTime()),
Z: getTimezoneString(date, isUTC, false),
ZZ: getTimezoneString(date, isUTC, true),
};
const escapedBlocks: string[] = [];
const escaped = template.replace(/\[([^\]]+)]/g, (_, value: string) => {
const index = escapedBlocks.push(value) - 1;
return `\u0000${index}\u0000`;
});
const localizedExpanded = replaceLocalizedTokens(escaped);
const replaced = localizedExpanded.replace(
/YYYY|YY|MMMM|MMM|MM|M|DD|Do|D|dddd|ddd|dd|d|HH|H|hh|h|mm|m|ss|s|SSS|A|a|Qo|Q|X|x|ZZ|Z/g,
(token) => tokenMap[token] ?? token
);
return replaced.replace(/\u0000(\d+)\u0000/g, (_, indexText: string) => escapedBlocks[Number(indexText)] ?? '');
}
interface IDateKitStatic {
(input?: DateKitInput): IDateKit;
utc: (input?: DateKitInput) => IDateKit;
isDateKit: (value: unknown) => value is IDateKit;
unix: (timestamp: number) => IDateKit;
}
function createDateKit(input?: DateKitInput) {
return new DateKitImpl(toDate(input, false), false);
}
export const dateKit: IDateKitStatic = Object.assign(createDateKit, {
utc: (input?: DateKitInput) => new DateKitImpl(toDate(input, true), true),
isDateKit: (value: unknown): value is IDateKit => value instanceof DateKitImpl,
unix: (timestamp: number) => createDateKit(timestamp * 1000),
});
-36
View File
@@ -1,36 +0,0 @@
/**
* Copyright 2023-present DreamNum Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import dayjs from 'dayjs';
import advancedFormat from 'dayjs/plugin/advancedFormat';
import customParseFormat from 'dayjs/plugin/customParseFormat';
import localeData from 'dayjs/plugin/localeData';
import localizedFormat from 'dayjs/plugin/localizedFormat';
import utc from 'dayjs/plugin/utc';
import weekday from 'dayjs/plugin/weekday';
import weekOfYear from 'dayjs/plugin/weekOfYear';
import weekYear from 'dayjs/plugin/weekYear';
dayjs.extend(customParseFormat);
dayjs.extend(advancedFormat);
dayjs.extend(weekday);
dayjs.extend(localeData);
dayjs.extend(weekOfYear);
dayjs.extend(weekYear);
dayjs.extend(utc);
dayjs.extend(localizedFormat);
export { dayjs };
+2 -1
View File
@@ -22,7 +22,8 @@ export { ColorKit, COLORS, type IRgbColor, RGB_PAREN, RGBA_PAREN } from './color
export * from './command-enum';
export * from './common';
export * from './compare';
export * from './dayjs';
export type { DateKit, DateKitInput, DateKitUnitType, OpUnitType } from './date-kit';
export { dateKit } from './date-kit';
export * from './doc-tool';
export * from './generate';
export * from './hash-algorithm';
+8 -8
View File
@@ -15,7 +15,7 @@
*/
import type { IStyleData } from '../types/interfaces';
import type { IKeyValue, Nullable } from './types';
import type { Nullable } from './types';
import { customAlphabet, nanoid } from 'nanoid';
import { isLegalUrl, normalizeUrl, topLevelDomainSet } from '../common/url';
@@ -89,7 +89,7 @@ function diffArrays(oneArray: any[], twoArray: any[]) {
return true;
}
function diffObject(oneObject: IKeyValue, twoObject: IKeyValue) {
function diffObject(oneObject: Record<string, any>, twoObject: Record<string, any>) {
const oneKeys = Object.keys(oneObject);
const twoKeys = Object.keys(twoObject);
if (oneKeys.length !== twoKeys.length) {
@@ -288,9 +288,9 @@ export class Tools {
return clone as T;
}
if (this.isObject(value)) {
const clone: IKeyValue = {};
Object.keys(value as IKeyValue).forEach((key) => {
const item = (value as IKeyValue)[key];
const clone: Record<string, any> = {};
Object.keys(value as Record<string, any>).forEach((key) => {
const item = (value as Record<string, any>)[key];
clone[key] = Tools.deepClone(item);
});
Object.setPrototypeOf(clone, Object.getPrototypeOf(value));
@@ -385,7 +385,7 @@ export class Tools {
* @param obj
* @returns
*/
static removeNull(value: IKeyValue): object {
static removeNull(value: Record<string, any>): object {
if (this.isObject(value)) {
Object.keys(value).forEach((key) => {
const item = value[key];
@@ -466,8 +466,8 @@ export class Tools {
* @param extendJson
* @returns
*/
static commonExtend<T>(originJson: IKeyValue, extendJson: IKeyValue): T {
const resultJsonObject: IKeyValue = {};
static commonExtend<T>(originJson: Record<string, any>, extendJson: Record<string, any>): T {
const resultJsonObject: Record<string, any> = {};
for (const attr in originJson) {
resultJsonObject[attr] = originJson[attr];
-18
View File
@@ -19,24 +19,6 @@
*/
export type Nullable<T> = T | null | undefined | void;
/**
* Key value object
*
* @ignore
* @deprecated As it has
*/
export interface IKeyValue {
[key: string]: any;
}
/**
* @ignore
* @deprecated, use {@link Record} instead.
*/
export interface IKeyType<T> {
[key: string]: T;
}
export type DeepReadonly<T> = {
readonly [P in keyof T]: DeepReadonly<T[P]>;
};
+4 -4
View File
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import type { IKeyType, Nullable } from '../shared';
import type { Nullable } from '../shared';
import type { IStyleData } from '../types/interfaces';
import type { ICellDataForSheetInterceptor } from './typedef';
import { generateRandomId, LRUMap, Tools } from '../shared';
@@ -24,11 +24,11 @@ import { generateRandomId, LRUMap, Tools } from '../shared';
*
*/
export class Styles {
private _styles: IKeyType<Nullable<IStyleData>>;
private _styles: Record<string, Nullable<IStyleData>>;
private _cacheMap = new LRUMap<string, string>(100000);
constructor(styles: IKeyType<Nullable<IStyleData>> = {}) {
constructor(styles: Record<string, Nullable<IStyleData>> = {}) {
this._styles = styles;
this._generateCacheMap();
@@ -100,7 +100,7 @@ export class Styles {
}
}
toJSON(): IKeyType<Nullable<IStyleData>> {
toJSON(): Record<string, Nullable<IStyleData>> {
return this._styles;
}
@@ -15,7 +15,7 @@
*/
import type { ISize } from '../../shared/shape';
import type { IKeyType, Nullable } from '../../shared/types';
import type { Nullable } from '../../shared/types';
import type { IWorksheetData } from '../../sheets/typedef';
import type { LocaleType, ThemeColorType } from '../enum';
import type { ShapeType } from '../enum/prst-geom-type';
@@ -121,7 +121,7 @@ export interface IPageElement {
/** @deprecated */
spreadsheet?: {
worksheet: IWorksheetData;
styles: IKeyType<Nullable<IStyleData>>;
styles: Record<string, Nullable<IStyleData>>;
};
/** @deprecated */
document?: IDocumentData;
-1
View File
@@ -79,7 +79,6 @@
"@univerjs/icons": "^1.1.1",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"dayjs": "^1.11.20",
"react-transition-group": "^4.4.5",
"sonner": "^2.0.7",
"tailwind-merge": "2.6.0"
@@ -15,10 +15,10 @@
*/
import { CalendarIcon } from '@univerjs/icons';
import dayjs from 'dayjs';
import { useState } from 'react';
import { borderClassName } from '../../helper/class-utilities';
import { clsx } from '../../helper/clsx';
import { formatDateWithPattern } from '../../helper/date';
import { Calendar } from '../calendar/Calendar';
import { Dropdown } from '../dropdown/Dropdown';
@@ -69,7 +69,7 @@ export function DatePicker(props: IDatePickerProps) {
`, borderClassName, className)}
type="button"
>
{dayjs(value).format('YYYY-MM-DD')}
{formatDateWithPattern(value, 'YYYY-MM-DD')}
<CalendarIcon
className={`
@@ -15,10 +15,10 @@
*/
import { CalendarIcon } from '@univerjs/icons';
import dayjs from 'dayjs';
import { useState } from 'react';
import { borderClassName } from '../../helper/class-utilities';
import { clsx } from '../../helper/clsx';
import { formatDateWithPattern } from '../../helper/date';
import { Calendar } from '../calendar/Calendar';
import { Dropdown } from '../dropdown/Dropdown';
@@ -90,9 +90,9 @@ export function DateRangePicker(props: IDateRangePickerProps) {
type="button"
>
<span className="univer-flex univer-gap-1">
<span>{dayjs(value[0]).format('YYYY-MM-DD')}</span>
<span>{formatDateWithPattern(value[0], 'YYYY-MM-DD')}</span>
<span>-</span>
<span>{dayjs(value[1]).format('YYYY-MM-DD')}</span>
<span>{formatDateWithPattern(value[1], 'YYYY-MM-DD')}</span>
</span>
<CalendarIcon
@@ -16,9 +16,9 @@
import type { ChangeEvent } from 'react';
import { ClockIcon } from '@univerjs/icons';
import dayjs from 'dayjs';
import { borderClassName } from '../../helper/class-utilities';
import { clsx } from '../../helper/clsx';
import { formatDateWithPattern } from '../../helper/date';
interface ITimeInputProps {
className?: string;
@@ -59,7 +59,7 @@ export function TimeInput(props: ITimeInputProps) {
`, borderClassName, className)}
type="time"
step="1"
value={dayjs(value).format('HH:mm:ss')}
value={formatDateWithPattern(value, 'HH:mm:ss')}
onChange={handleChangeTime}
/>
</div>
@@ -15,8 +15,8 @@
*/
import { cleanup, fireEvent, render } from '@testing-library/react';
import dayjs from 'dayjs';
import { afterEach, describe, expect, it, vi } from 'vitest';
import { formatDateWithPattern } from '../../../helper/date';
import { TimeInput } from '../TimeInput';
import '@testing-library/jest-dom/vitest';
@@ -26,7 +26,7 @@ describe('TimeInput', () => {
it('should render with current value and custom class', () => {
const value = new Date('2024-01-01T01:02:03.000Z');
const { container, getByDisplayValue } = render(<TimeInput value={value} className="custom-time" />);
const expected = dayjs(value).format('HH:mm:ss');
const expected = formatDateWithPattern(value, 'HH:mm:ss');
expect(container.querySelector('[data-u-comp="time-input"]')).toBeInTheDocument();
expect(container.querySelector('.custom-time')).toBeInTheDocument();
+37
View File
@@ -0,0 +1,37 @@
/**
* Copyright 2023-present DreamNum Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
function pad(value: number, length = 2) {
return String(value).padStart(length, '0');
}
export function formatDateWithPattern(value: Date | undefined, pattern: string) {
const date = value ?? new Date();
if (Number.isNaN(date.getTime())) {
return 'Invalid Date';
}
const tokenMap: Record<string, string> = {
YYYY: String(date.getFullYear()),
MM: pad(date.getMonth() + 1),
DD: pad(date.getDate()),
HH: pad(date.getHours()),
mm: pad(date.getMinutes()),
ss: pad(date.getSeconds()),
};
return pattern.replace(/YYYY|MM|DD|HH|mm|ss/g, (token) => tokenMap[token] ?? token);
}
+3 -3
View File
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import type { IGroupBaseBound, IKeyValue, ITransformState, Nullable } from '@univerjs/core';
import type { IGroupBaseBound, ITransformState, Nullable } from '@univerjs/core';
import type { IDragEvent, IMouseEvent, IPointerEvent, IWheelEvent } from './basics/i-events';
import type { IObjectFullState, ITransformChangeState } from './basics/interfaces';
@@ -564,7 +564,7 @@ export abstract class BaseObject extends Disposable {
optionKeys.forEach((pKey) => {
if (option[pKey as keyof IObjectFullState] !== undefined) {
preKeys[pKey as keyof IObjectFullState] = this[pKey as keyof BaseObject];
(this as IKeyValue)[pKey] = option[pKey as keyof IObjectFullState];
(this as Record<string, any>)[pKey] = option[pKey as keyof IObjectFullState];
}
});
@@ -847,7 +847,7 @@ export abstract class BaseObject extends Disposable {
}
toJson() {
const props: IKeyValue = {};
const props: Record<string, any> = {};
BASE_OBJECT_ARRAY.forEach((key) => {
if (this[key as keyof BaseObject]) {
props[key] = this[key as keyof BaseObject];
+2 -2
View File
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import type { IKeyValue, Nullable } from '@univerjs/core';
import type { Nullable } from '@univerjs/core';
import type { IObjectFullState } from './basics/interfaces';
import type { IViewportInfo, Vector2 } from './basics/vector2';
@@ -160,7 +160,7 @@ export class SceneViewer extends BaseObject {
return true;
}
(transformState as IKeyValue)[key] = props[key as keyof IObjectFullState];
(transformState as Record<string, any>)[key] = props[key as keyof IObjectFullState];
hasTransformState = true;
});
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import type { IAbsoluteTransform, IKeyValue, Nullable } from '@univerjs/core';
import type { IAbsoluteTransform, Nullable } from '@univerjs/core';
import type { Subscription } from 'rxjs';
import type { BaseObject } from './base-object';
@@ -535,7 +535,7 @@ export class Transformer extends Disposable implements ITransformerConfig {
return true;
}
(this as IKeyValue)[key] = props[key as keyof ITransformerConfig];
(this as Record<string, any>)[key] = props[key as keyof ITransformerConfig];
});
}
+3 -3
View File
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import type { IKeyValue, Nullable } from '@univerjs/core';
import type { Nullable } from '@univerjs/core';
import type { BaseObject } from './base-object';
import type { IDragEvent, IKeyboardEvent, IMouseEvent, IPointerEvent, IWheelEvent } from './basics/i-events';
import type { ISceneTransformState, ITransformChangeState } from './basics/interfaces';
@@ -443,8 +443,8 @@ export class Scene extends Disposable {
transformStateKeys.forEach((pKey) => {
if (state[pKey as keyof ISceneTransformState] !== undefined) {
(preKeys as IKeyValue)[pKey] = this[pKey as keyof Scene];
(this as IKeyValue)[pKey] = state[pKey as keyof ISceneTransformState];
(preKeys as Record<string, any>)[pKey] = this[pKey as keyof Scene];
(this as Record<string, any>)[pKey] = state[pKey as keyof ISceneTransformState];
}
});
+1 -2
View File
@@ -14,7 +14,6 @@
* limitations under the License.
*/
import type { IKeyValue } from '@univerjs/core';
import type { UniverRenderingContext } from '../context';
import type { IShapeProps } from './shape';
import { Tools } from '@univerjs/core';
@@ -67,7 +66,7 @@ export class CheckboxShape extends Shape<ICheckboxShapeProps> {
}
override toJson() {
const props: IKeyValue = {};
const props: Record<string, any> = {};
CHECK_OBJECT_ARRAY.forEach((key) => {
if (this[key as keyof CheckboxShape]) {
props[key] = this[key as keyof CheckboxShape];
+1 -3
View File
@@ -14,8 +14,6 @@
* limitations under the License.
*/
import type { IKeyValue } from '@univerjs/core';
import type { UniverRenderingContext } from '../context';
import type { IShapeProps } from './shape';
import { ObjectType } from '../base-object';
@@ -74,7 +72,7 @@ export class Circle extends Shape<ICircleProps> {
}
override toJson() {
const props: IKeyValue = {};
const props: Record<string, any> = {};
CIRCLE_OBJECT_ARRAY.forEach((key) => {
if (this[key as keyof Circle]) {
props[key] = this[key as keyof Circle];
+2 -2
View File
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import type { IKeyValue, Nullable } from '@univerjs/core';
import type { Nullable } from '@univerjs/core';
import type { IObjectFullState } from '../basics/interfaces';
import type { UniverRenderingContext } from '../context';
@@ -799,7 +799,7 @@ export class Path extends Shape<IPathProps> {
}
override toJson() {
const props: IKeyValue = {};
const props: Record<string, any> = {};
PATH_OBJECT_ARRAY.forEach((key) => {
if (this[key as keyof Path]) {
props[key] = this[key as keyof Path];
+2 -2
View File
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import type { IKeyValue, Nullable } from '@univerjs/core';
import type { Nullable } from '@univerjs/core';
import type { UniverRenderingContext } from '../context';
import type { IShapeProps } from './shape';
@@ -129,7 +129,7 @@ export class Rect<T extends IRectProps = IRectProps> extends Shape<T> {
}
override toJson() {
const props: IKeyValue = {};
const props: Record<string, any> = {};
RECT_OBJECT_ARRAY.forEach((key) => {
if (this[key as keyof Rect]) {
props[key] = this[key as keyof Rect];
@@ -14,8 +14,6 @@
* limitations under the License.
*/
import type { IKeyValue } from '@univerjs/core';
import type { IObjectFullState } from '../basics/interfaces';
import type { IPoint, Vector2 } from '../basics/vector2';
import type { UniverRenderingContext } from '../context';
@@ -161,7 +159,7 @@ export class RegularPolygon extends Shape<IRegularPolygonProps> {
}
override toJson() {
const props: IKeyValue = {};
const props: Record<string, any> = {};
REGULAR_POLYGON_OBJECT_ARRAY.forEach((key) => {
if (this[key as keyof RegularPolygon]) {
props[key] = this[key as keyof RegularPolygon];
@@ -18,7 +18,7 @@ import type {
IBorderData,
IColorStyle,
IDocumentData,
IKeyValue,
IStyleBase,
ITextDecoration,
ITransformState,
@@ -271,7 +271,7 @@ export class RichText extends BaseObject {
}
override toJson(): RichtextObjectJSONType {
const props: IKeyValue = {};
const props: Record<string, any> = {};
RICHTEXT_OBJECT_ARRAY.forEach((key) => {
// @ts-ignore
if (this[key]) {
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import type { EventState, IKeyValue, Nullable } from '@univerjs/core';
import type { EventState, Nullable } from '@univerjs/core';
import type { IMouseEvent, IPointerEvent } from '../basics/i-events';
import type { Vector2 } from '../basics/vector2';
@@ -155,7 +155,7 @@ export class ScrollBar extends Disposable {
themeKeys.forEach((key) => {
if (props[key as keyof IScrollBarProps] !== undefined) {
(this as IKeyValue)[`_${key}`] = props[key as keyof IScrollBarProps];
(this as Record<string, any>)[`_${key}`] = props[key as keyof IScrollBarProps];
}
});
+8 -8
View File
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import type { IKeyValue, IOffset, IScale, ISize, Nullable } from '@univerjs/core';
import type { IOffset, IScale, ISize, Nullable } from '@univerjs/core';
import type { IObjectFullState } from '../basics/interfaces';
import type { IViewportInfo, Vector2 } from '../basics/vector2';
@@ -359,12 +359,12 @@ export abstract class Shape<T extends IShapeProps> extends BaseObject {
return this;
}
themeKeys.forEach((key) => {
if ((props as IKeyValue)[key] === undefined) {
if ((props as Record<string, any>)[key] === undefined) {
return true;
}
if (!BASE_OBJECT_ARRAY_Set.has(key)) {
(this as IKeyValue)[`_${key}`] = (props as IKeyValue)[key];
(this as Record<string, any>)[`_${key}`] = (props as Record<string, any>)[key];
}
});
this.makeDirty(true);
@@ -372,11 +372,11 @@ export abstract class Shape<T extends IShapeProps> extends BaseObject {
}
getPropByKey<K extends keyof T>(key: K): T[K] {
return (this as IKeyValue)[`_${String(key)}`] as T[K];
return (this as Record<string, any>)[`_${String(key)}`] as T[K];
}
override toJson() {
const props: IKeyValue = {};
const props: Record<string, any> = {};
SHAPE_OBJECT_ARRAY.forEach((key) => {
if (this[key as keyof Shape<T>]) {
props[key] = this[key as keyof Shape<T>];
@@ -424,15 +424,15 @@ export abstract class Shape<T extends IShapeProps> extends BaseObject {
}
themeKeys.forEach((key) => {
if ((props as IKeyValue)[key] === undefined) {
if ((props as Record<string, any>)[key] === undefined) {
return true;
}
if (BASE_OBJECT_ARRAY_Set.has(key)) {
transformState[key as keyof IObjectFullState] = (props as IKeyValue)[key];
transformState[key as keyof IObjectFullState] = (props as Record<string, any>)[key];
hasTransformState = true;
} else {
(this as IKeyValue)[`_${key}`] = (props as IKeyValue)[key];
(this as Record<string, any>)[`_${key}`] = (props as Record<string, any>)[key];
}
});
+2 -2
View File
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import type { IKeyValue, Nullable } from '@univerjs/core';
import type { Nullable } from '@univerjs/core';
import type { UniverRenderingContext } from '../context';
import type { IShapeProps } from './shape';
import { CellValueType, HorizontalAlign, TextDecoration, VerticalAlign } from '@univerjs/core';
@@ -268,7 +268,7 @@ export class Text extends Shape<ITextProps> {
}
override toJson() {
const props: IKeyValue = {};
const props: Record<string, any> = {};
TEXT_OBJECT_ARRAY.forEach((key) => {
if (this[key as keyof Text]) {
props[key] = this[key as keyof Text];
@@ -306,8 +306,8 @@ export const ColorScaleStyleEditor = (props: IStyleEditorProps) => {
</div>
<div
className={`
univer-text-gray-600dark:!univer-text-gray-200
univer-mt-3 univer-text-xs
univer-mt-3 univer-text-xs univer-text-gray-600
dark:!univer-text-gray-200
`}
>
{localeService.t('sheet.cf.panel.medianValue')}
@@ -17,7 +17,7 @@
import type { IStyleData, Nullable } from '@univerjs/core';
import type { IAverageHighlightCell, IFormulaHighlightCell, IHighlightCell, INumberHighlightCell, IRankHighlightCell, ITextHighlightCell, ITimePeriodHighlightCell } from '../type';
import type { IContext } from './base-calculate-unit';
import { CellValueType, dayjs, Range, Tools } from '@univerjs/core';
import { CellValueType, dateKit, Range, Tools } from '@univerjs/core';
import { ERROR_TYPE_SET, FormulaResultStatus } from '@univerjs/engine-formula';
import { CFNumberOperator, CFSubRuleType, CFTextOperator, CFTimePeriodOperator } from '../../base/const';
import { ConditionalFormattingFormulaService } from '../../services/conditional-formatting-formula.service';
@@ -125,8 +125,8 @@ export class HighlightCellCalculateUnit extends BaseCalculateUnit<Nullable<IConf
case CFTimePeriodOperator.last7Days: {
return {
value: {
start: dayjs().subtract(7, 'day').valueOf(),
end: dayjs().valueOf(),
start: dateKit().subtract(7, 'day').valueOf(),
end: dateKit().valueOf(),
},
type: ruleConfig.subType,
};
@@ -134,8 +134,8 @@ export class HighlightCellCalculateUnit extends BaseCalculateUnit<Nullable<IConf
case CFTimePeriodOperator.lastMonth: {
return {
value: {
start: dayjs().startOf('month').subtract(1, 'month').valueOf(),
end: dayjs().endOf('month').subtract(1, 'month').valueOf(),
start: dateKit().startOf('month').subtract(1, 'month').valueOf(),
end: dateKit().endOf('month').subtract(1, 'month').valueOf(),
},
type: ruleConfig.subType,
};
@@ -143,8 +143,8 @@ export class HighlightCellCalculateUnit extends BaseCalculateUnit<Nullable<IConf
case CFTimePeriodOperator.lastWeek: {
return {
value: {
start: dayjs().startOf('week').subtract(1, 'week').valueOf(),
end: dayjs().endOf('week').subtract(1, 'week').valueOf(),
start: dateKit().startOf('week').subtract(1, 'week').valueOf(),
end: dateKit().endOf('week').subtract(1, 'week').valueOf(),
},
type: ruleConfig.subType,
};
@@ -152,8 +152,8 @@ export class HighlightCellCalculateUnit extends BaseCalculateUnit<Nullable<IConf
case CFTimePeriodOperator.nextMonth: {
return {
value: {
start: dayjs().startOf('month').add(1, 'month').valueOf(),
end: dayjs().endOf('month').add(1, 'month').valueOf(),
start: dateKit().startOf('month').add(1, 'month').valueOf(),
end: dateKit().endOf('month').add(1, 'month').valueOf(),
},
type: ruleConfig.subType,
};
@@ -161,8 +161,8 @@ export class HighlightCellCalculateUnit extends BaseCalculateUnit<Nullable<IConf
case CFTimePeriodOperator.nextWeek: {
return {
value: {
start: dayjs().startOf('week').add(1, 'week').valueOf(),
end: dayjs().endOf('week').add(1, 'week').valueOf(),
start: dateKit().startOf('week').add(1, 'week').valueOf(),
end: dateKit().endOf('week').add(1, 'week').valueOf(),
},
type: ruleConfig.subType,
};
@@ -170,8 +170,8 @@ export class HighlightCellCalculateUnit extends BaseCalculateUnit<Nullable<IConf
case CFTimePeriodOperator.thisMonth: {
return {
value: {
start: dayjs().startOf('month').valueOf(),
end: dayjs().endOf('month').valueOf(),
start: dateKit().startOf('month').valueOf(),
end: dateKit().endOf('month').valueOf(),
},
type: ruleConfig.subType,
};
@@ -179,8 +179,8 @@ export class HighlightCellCalculateUnit extends BaseCalculateUnit<Nullable<IConf
case CFTimePeriodOperator.thisWeek: {
return {
value: {
start: dayjs().startOf('week').valueOf(),
end: dayjs().endOf('week').valueOf(),
start: dateKit().startOf('week').valueOf(),
end: dateKit().endOf('week').valueOf(),
},
type: ruleConfig.subType,
};
@@ -188,8 +188,8 @@ export class HighlightCellCalculateUnit extends BaseCalculateUnit<Nullable<IConf
case CFTimePeriodOperator.today: {
return {
value: {
start: dayjs().startOf('day').valueOf(),
end: dayjs().endOf('day').valueOf(),
start: dateKit().startOf('day').valueOf(),
end: dateKit().endOf('day').valueOf(),
},
type: ruleConfig.subType,
};
@@ -197,8 +197,8 @@ export class HighlightCellCalculateUnit extends BaseCalculateUnit<Nullable<IConf
case CFTimePeriodOperator.tomorrow: {
return {
value: {
start: dayjs().startOf('day').add(1, 'day').valueOf(),
end: dayjs().endOf('day').add(1, 'day').valueOf(),
start: dateKit().startOf('day').add(1, 'day').valueOf(),
end: dateKit().endOf('day').add(1, 'day').valueOf(),
},
type: ruleConfig.subType,
};
@@ -206,8 +206,8 @@ export class HighlightCellCalculateUnit extends BaseCalculateUnit<Nullable<IConf
case CFTimePeriodOperator.yesterday: {
return {
value: {
start: dayjs().startOf('day').subtract(1, 'day').valueOf(),
end: dayjs().endOf('day').subtract(1, 'day').valueOf(),
start: dateKit().startOf('day').subtract(1, 'day').valueOf(),
end: dateKit().endOf('day').subtract(1, 'day').valueOf(),
},
type: ruleConfig.subType,
};
@@ -17,7 +17,7 @@
import type { CellValue, ICellData, IObjectMatrixPrimitiveType, IRange, Nullable } from '@univerjs/core';
import type { IConditionFormattingRule, IValueConfig } from '../type';
import type { IContext } from './base-calculate-unit';
import { BooleanNumber, CellValueType, ColorKit, dayjs, ObjectMatrix, Range } from '@univerjs/core';
import { BooleanNumber, CellValueType, ColorKit, dateKit, ObjectMatrix, Range } from '@univerjs/core';
import { BooleanValue, FormulaResultStatus } from '@univerjs/engine-formula';
import { CFNumberOperator, CFValueType } from '../../base/const';
import { ConditionalFormattingFormulaService } from '../../services/conditional-formatting-formula.service';
@@ -85,7 +85,7 @@ export const serialTimeToTimestamp = (value: number) => {
const mm = Math.floor(x / 60) % 60;
const ss = Math.floor(x) % 60;
// return it as a native date object
const dt = dayjs(`${y}/${m}/${d} ${hh}:${mm}:${ss}`);
const dt = dateKit(`${y}/${m}/${d} ${hh}:${mm}:${ss}`);
const result = dt.valueOf();
return result;
};
@@ -14,12 +14,12 @@
* limitations under the License.
*/
import type { CellValue, IDisposable, Nullable, Workbook } from '@univerjs/core';
import type { CellValue, DateKit, IDisposable, Nullable, Workbook } from '@univerjs/core';
import type { ISetRangeValuesCommandParams, ISheetLocation } from '@univerjs/sheets';
import type { ListValidator } from '@univerjs/sheets-data-validation';
import type { IDropdownParam, IEditorBridgeServiceVisibleParam } from '@univerjs/sheets-ui';
import type { IUniverSheetsDataValidationUIConfig } from '../config/config';
import { CellValueType, DataValidationErrorStyle, DataValidationRenderMode, dayjs, Disposable, DisposableCollection, ICommandService, IConfigService, Inject, Injector, IUniverInstanceService, numfmt, UniverInstanceType } from '@univerjs/core';
import { CellValueType, DataValidationErrorStyle, DataValidationRenderMode, dateKit, Disposable, DisposableCollection, ICommandService, IConfigService, Inject, Injector, IUniverInstanceService, numfmt, UniverInstanceType } from '@univerjs/core';
import { DataValidatorDropdownType, DataValidatorRegistryService } from '@univerjs/data-validation';
import { DeviceInputEventType } from '@univerjs/engine-render';
import { SetRangeValuesCommand, SheetsSelectionsService } from '@univerjs/sheets';
@@ -52,13 +52,13 @@ const transformDate = (value: Nullable<CellValue>) => {
}
// If the value is an empty string, return the current date
if (value === '') return dayjs();
if (value === '') return dateKit();
if (typeof value === 'number' || !Number.isNaN(+value)) {
return dayjs(numfmt.format('yyyy-MM-dd HH:mm:ss', Number(value)));
return dateKit(numfmt.format('yyyy-MM-dd HH:mm:ss', Number(value)));
}
const date = dayjs(value);
const date = dateKit(value);
if (date.isValid()) {
return date;
}
@@ -184,7 +184,7 @@ export class DataValidationDropdownManagerService extends Disposable {
let popupDisposable: Nullable<IDisposable>;
const handleSave = async (date: dayjs.Dayjs | undefined, targetPatternType: 'datetime' | 'date' | 'time') => {
const handleSave = async (date: DateKit | undefined, targetPatternType: 'datetime' | 'date' | 'time') => {
if (!date) {
return true;
}
@@ -17,7 +17,7 @@
import type { CellValue, IDataValidationRule, IDataValidationRuleBase, ISheetDataValidationRule, Nullable } from '@univerjs/core';
import type { IFormulaResult, IFormulaValidResult, IValidatorCellInfo } from '@univerjs/data-validation';
import type { ISheetLocationBase } from '@univerjs/sheets';
import { DataValidationOperator, DataValidationType, dayjs, isFormulaString, numfmt, Tools } from '@univerjs/core';
import { DataValidationOperator, DataValidationType, dateKit, isFormulaString, numfmt, Tools } from '@univerjs/core';
import { BaseDataValidator } from '@univerjs/data-validation';
import { LexerTreeBuilder } from '@univerjs/engine-formula';
import { DateOperatorErrorTitleMap, DateOperatorNameMap, DateOperatorTitleMap } from '../common/date-text-map';
@@ -43,7 +43,7 @@ const transformDate2SerialNumber = (value: Nullable<CellValue>) => {
}
// support like 2020年11月11日 locale date
return numfmt.parseDate(dayjs(value).format('YYYY-MM-DD HH:mm:ss'))?.v as number | undefined;
return numfmt.parseDate(dateKit(value).format('YYYY-MM-DD HH:mm:ss'))?.v as number | undefined;
};
export class DateValidator extends BaseDataValidator {
@@ -140,7 +140,7 @@ export class DateValidator extends BaseDataValidator {
date = numfmt.dateFromSerial(res);
}
return dayjs(`${date[0]}/${date[1]}/${date[2]} ${date[3]}:${date[4]}:${date[5]}`).format(bizInfo?.showTime ? 'YYYY-MM-DD HH:mm:ss' : 'YYYY-MM-DD');
return dateKit(`${date[0]}/${date[1]}/${date[2]} ${date[3]}:${date[4]}:${date[5]}`).format(bizInfo?.showTime ? 'YYYY-MM-DD HH:mm:ss' : 'YYYY-MM-DD');
};
return {
@@ -14,16 +14,17 @@
* limitations under the License.
*/
import type { DateKit } from '@univerjs/core';
import type { IPopup } from '@univerjs/ui';
import type { IBaseDropdownProps } from '../type';
import { dayjs, LocaleService } from '@univerjs/core';
import { dateKit, LocaleService } from '@univerjs/core';
import { borderTopClassName, Button, Calendar, clsx, TimeInput } from '@univerjs/design';
import { useDependency } from '@univerjs/ui';
import { useMemo, useState } from 'react';
export interface IDateDropdownProps {
defaultValue?: dayjs.Dayjs;
onChange?: (value: dayjs.Dayjs | undefined) => boolean | Promise<boolean>;
defaultValue?: DateKit;
onChange?: (value: DateKit | undefined) => boolean | Promise<boolean>;
patternType?: 'datetime' | 'date' | 'time';
showTime?: boolean;
}
@@ -32,8 +33,8 @@ export function DateDropdown(props: { popup: IPopup<IDateDropdownProps & IBaseDr
const { popup } = props;
const { extraProps } = popup;
const { hideFn, patternType, defaultValue, onChange, showTime } = extraProps!;
const [localDate, setLocalDate] = useState<dayjs.Dayjs | undefined>(defaultValue);
const defaultDate = useMemo(() => patternType !== 'time' ? dayjs() : dayjs('1900-01-01 00:00:00'), []);
const [localDate, setLocalDate] = useState<DateKit | undefined>(defaultValue);
const defaultDate = useMemo(() => patternType !== 'time' ? dateKit() : dateKit('1900-01-01 00:00:00'), []);
const date = localDate && localDate.isValid() ? localDate : defaultDate;
const localeService = useDependency(LocaleService);
@@ -57,7 +58,7 @@ export function DateDropdown(props: { popup: IPopup<IDateDropdownProps & IBaseDr
<TimeInput
value={date.toDate()}
onValueChange={(newValue) => {
setLocalDate(dayjs(newValue));
setLocalDate(dateKit(newValue));
}}
/>
)
@@ -66,7 +67,7 @@ export function DateDropdown(props: { popup: IPopup<IDateDropdownProps & IBaseDr
value={date.toDate()}
showTime={showTime ?? patternType === 'datetime'}
onValueChange={(newValue) => {
setLocalDate(dayjs(newValue));
setLocalDate(dateKit(newValue));
}}
/>
)}
+2 -2
View File
@@ -14,8 +14,8 @@
* limitations under the License.
*/
import { dayjs } from '@univerjs/core';
import { dateKit } from '@univerjs/core';
export function getDT(date?: Date) {
return dayjs(date).format('YYYY/MM/DD HH:mm');
return dateKit(date).format('YYYY/MM/DD HH:mm');
}
+5 -6
View File
@@ -18,7 +18,6 @@ import type {
IBorderData,
ICellData,
IDocumentData,
IKeyValue,
IRange,
IStyleData,
ITextDecoration,
@@ -434,11 +433,11 @@ export function handleStringToStyle($dom?: HTMLElement, cssStyle: string = '') {
},
};
for (const k in colors) {
(styleList.bd as IKeyValue)[k].cl.rgb = colors[k as keyof IBorderData];
(styleList.bd as Record<string, any>)[k].cl.rgb = colors[k as keyof IBorderData];
}
} else {
for (const k in colors) {
(styleList.bd as IKeyValue)[k].cl.rgb = colors[k as keyof IBorderData];
(styleList.bd as Record<string, any>)[k].cl.rgb = colors[k as keyof IBorderData];
}
}
}
@@ -446,7 +445,7 @@ export function handleStringToStyle($dom?: HTMLElement, cssStyle: string = '') {
if (key === 'border-width' || key === 'border-style') {
const width = handleBorder(value, ' ');
for (const k in width) {
(borderInfo as IKeyValue)[k] += ` ${width[k as keyof IBorderData]}`;
(borderInfo as Record<string, any>)[k] += ` ${width[k as keyof IBorderData]}`;
}
if (!styleList.bd) {
styleList.bd = {
@@ -901,8 +900,8 @@ export function handelExcelToJson(html: string) {
return data;
}
function getStyles(styleText: string): IKeyValue {
const output: IKeyValue = {};
function getStyles(styleText: string): Record<string, any> {
const output: Record<string, any> = {};
const string = styleText.replaceAll('<!--', '').replaceAll('-->', '').trim();
const style = string?.replaceAll('\t', '').replaceAll('\n', '').split('}');
for (let i = 0; i < style.length; i++) {
+5 -16
View File
@@ -780,9 +780,6 @@ importers:
async-lock:
specifier: ^1.4.1
version: 1.4.1
dayjs:
specifier: ^1.11.20
version: 1.11.20
fast-diff:
specifier: 1.3.0
version: 1.3.0
@@ -790,8 +787,8 @@ importers:
specifier: ^4.0.2
version: 4.0.2
lodash-es:
specifier: ^4.17.23
version: 4.17.23
specifier: ^4.18.1
version: 4.18.1
nanoid:
specifier: 5.1.7
version: 5.1.7
@@ -881,9 +878,6 @@ importers:
clsx:
specifier: ^2.1.1
version: 2.1.1
dayjs:
specifier: ^1.11.20
version: 1.11.20
react-transition-group:
specifier: ^4.4.5
version: 4.4.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
@@ -6783,9 +6777,6 @@ packages:
resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==}
engines: {node: '>= 0.4'}
dayjs@1.11.20:
resolution: {integrity: sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==}
debug@2.6.9:
resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
peerDependencies:
@@ -8450,8 +8441,8 @@ packages:
resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
engines: {node: '>=10'}
lodash-es@4.17.23:
resolution: {integrity: sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==}
lodash-es@4.18.1:
resolution: {integrity: sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==}
lodash.camelcase@4.3.0:
resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==}
@@ -14709,8 +14700,6 @@ snapshots:
es-errors: 1.3.0
is-data-view: 1.0.2
dayjs@1.11.20: {}
debug@2.6.9:
dependencies:
ms: 2.0.0
@@ -16625,7 +16614,7 @@ snapshots:
dependencies:
p-locate: 5.0.0
lodash-es@4.17.23: {}
lodash-es@4.18.1: {}
lodash.camelcase@4.3.0: {}