UNPKG

@blocklet/ui-react

Version:

Some useful front-end web components that can be used in Blocklets.

225 lines (203 loc) 6.3 kB
import dayjs from 'dayjs'; import timezone from 'dayjs/plugin/timezone'; import utc from 'dayjs/plugin/utc'; import { DurationEnum, UserMetadata } from '../../../@types'; // 扩展 dayjs 插件 dayjs.extend(utc); dayjs.extend(timezone); const HOUR = 3600; const MINUTES_30 = 1800; const MINUTES_10 = 600; const MINUTES_5 = 300; const MINUTES_1 = 60; const SECOND = 1; export const currentTimezone = dayjs.tz.guess(); // 常用时区列表,作为兼容性 fallback const COMMON_TIMEZONES = [ 'America/New_York', 'America/Chicago', 'America/Denver', 'America/Los_Angeles', 'Europe/London', 'Europe/Paris', 'Europe/Berlin', 'Europe/Rome', 'Asia/Tokyo', 'Asia/Shanghai', 'Asia/Hong_Kong', 'Asia/Singapore', 'Asia/Seoul', 'Asia/Kolkata', 'Australia/Sydney', 'Australia/Melbourne', 'Pacific/Auckland', 'America/Sao_Paulo', 'America/Mexico_City', 'Africa/Cairo', 'UTC', ]; // 获取时区列表的兼容性函数 const getTimezoneList = () => { // 优先使用现代 API if (typeof Intl !== 'undefined' && Intl.supportedValuesOf) { try { return Intl.supportedValuesOf('timeZone'); } catch (error) { console.warn('Intl.supportedValuesOf not supported, falling back to common timezones'); } } // 尝试使用 Intl.DateTimeFormat 获取时区 if (typeof Intl !== 'undefined' && Intl.DateTimeFormat) { try { // 使用 resolvedOptions 检测当前时区是否可用 const formatter = new Intl.DateTimeFormat('en', { timeZone: 'UTC' }); if (formatter.resolvedOptions().timeZone) { return COMMON_TIMEZONES; } } catch (error) { console.warn('Intl.DateTimeFormat timezone support limited'); } } // 最后的 fallback return COMMON_TIMEZONES; }; export const getTimezones = () => { const timezones = getTimezoneList(); const formattedTimezones = timezones .map((tz) => { try { const offset = dayjs.tz(dayjs(), tz).utcOffset() / 60; // 计算 UTC 偏移 (小时) const hours = Math.floor(offset); const minutes = (offset % 1) * 60; const label = `GMT${hours >= 0 ? '+' : ''}${hours}:${minutes === 30 ? '30' : '00'}`; return { label, value: tz }; } catch (error) { // 如果时区不支持,跳过 console.warn(`Timezone ${tz} not supported, skipping`); return null; } }) .filter((tz): tz is { label: string; value: string } => tz !== null); // 类型守卫 return formattedTimezones .sort((a, b) => { const [hoursA, minutesA] = a.label.replace('GMT', '').split(':').map(Number); const [hoursB, minutesB] = b.label.replace('GMT', '').split(':').map(Number); const totalOffsetA = hoursA * 60 + minutesA; // 统一为分钟数 const totalOffsetB = hoursB * 60 + minutesB; return totalOffsetB - totalOffsetA; // **降序排列** }) .map((tz) => ({ label: `(${tz.label}) ${tz.value}`, value: tz.value, })); }; export const isValidUrl = (url: string) => { const urlPattern = /^(https?:\/\/)?((([a-zA-Z\d]([a-zA-Z\d-]*[a-zA-Z\d])*)\.)+[a-zA-Z]{2,}|((\d{1,3}\.){3}\d{1,3}))(:\d+)?(\/[-a-zA-Z\d%_.~+]*)*(\?[;&a-zA-Z\d%_.~+=-]*)?(#[a-zA-Z\d_]*)?$/; return urlPattern.test(url); }; /** * 根据 duration 类型,计算出date range * @param status * @returns */ export const getStatusDuration = (status: UserMetadata['status']) => { let dateRange: dayjs.Dayjs[] = status?.dateRange?.map((d) => dayjs(d)) ?? []; const current = dayjs(); switch (status?.duration) { case DurationEnum.ThirtyMinutes: dateRange = [current, current.add(30, 'minutes')]; break; case DurationEnum.OneHour: dateRange = [current, current.add(1, 'hour')]; break; case DurationEnum.FourHours: dateRange = [current, current.add(4, 'hours')]; break; case DurationEnum.Today: dateRange = [current, current.endOf('day')]; break; case DurationEnum.ThisWeek: dateRange = [current, current.endOf('week')]; break; case DurationEnum.NoClear: dateRange = [current, current]; break; default: break; } return dateRange.map((d) => d.toDate()); }; /** * 根据状态的 duration,判断是否在时间范围内 * @param status * @returns */ export const isWithinTimeRange = (dateRange: [Date, Date]) => { const current = dayjs(); return current.isAfter(dayjs(dateRange[0])) && current.isBefore(dayjs(dateRange[1])); }; /** * 判断状态持续时间是否为不可清除 * @param status * @returns */ export const isNotClear = (status: UserMetadata['status']) => { const { duration, dateRange } = status ?? {}; if (!duration || !dateRange) { return false; } return duration === DurationEnum.NoClear || dayjs(dateRange?.[0]).isSame(dayjs(dateRange?.[1])); }; /** * 获取当前时间距离结束时间还有多久 */ export const getTimeRemaining = (date: Date) => { const now = dayjs(); const end = dayjs(date); const diffSeconds = end.diff(now, 'seconds'); // 转换为毫秒 const toMilliseconds = (seconds: number) => seconds * 1000; if (diffSeconds >= HOUR) { return toMilliseconds(HOUR); // 1小时 = 3600000ms } if (diffSeconds >= MINUTES_30) { return toMilliseconds(MINUTES_30); // 30分钟 = 1800000ms } if (diffSeconds >= MINUTES_10) { return toMilliseconds(MINUTES_10); // 10分钟 = 600000ms } if (diffSeconds >= MINUTES_5) { return toMilliseconds(MINUTES_5); // 5分钟 = 300000ms } if (diffSeconds >= MINUTES_1) { return toMilliseconds(MINUTES_1); // 1分钟 = 60000ms } if (diffSeconds >= SECOND) { return toMilliseconds(SECOND); // 1秒 = 1000ms } return 0; // 如果时间已过期,返回0 }; // 只支持在 sx 中使用 export const defaultButtonStyle = { color: 'text.primary', borderColor: 'grey.100', backgroundColor: 'background.default', '&:hover': { borderColor: 'grey.100', backgroundColor: 'action.hover', }, py: 0.5, borderRadius: 1, }; export const primaryButtonStyle = { color: 'primary.contrastText', borderColor: 'primary.main', backgroundColor: 'primary.main', '&:hover': { borderColor: 'primary.main', backgroundColor: 'primary.main', }, py: 0.5, borderRadius: 1, };