Skip to content

cn hx components Utilities

github-actions[bot] edited this page Aug 19, 2026 · 12 revisions

工具函数

@hx/components 导出的工具函数、上下文 Provider 和模式工具包。


构建内容

从操作/分组定义构建操作菜单内容。

import { buildContent } from '@hx/components';

const menu = buildContent([
  { label: '编辑', onClick: handleEdit },
  { type: 'separator' },
  { label: '删除', onClick: handleDelete, disabled: true },
]);

Input 工具函数

输入事件处理器的工厂函数。

import {
  createHxInputFocusHandler,
  createHxInputKeyDownHandler,
  createHxInputSelectAllHandler,
} from '@hx/components';

分页工具

根据原始条目/页数计算 HxPaginationData:

import { computePaginationData } from '@hx/components';

const data = computePaginationData(150, 20, 1);
// => { pageSize: 20, pageNumber: 1, totalPages: 8, totalItems: 150 }

上传工具

import {
  parseFileName,   // (name: string) => { name: string, ext: string }
  mapError,        // (error: unknown) => 用户友好的字符串
  isImage,         // (file: HxUploadFile) => boolean
  toImageSrc,      // (bytes: ArrayBuffer) => data: URL 字符串
  releaseImage,    // (src: string) => undefined —— 释放 object URL
} from '@hx/components';

Accept 校验

将 accept 字符串/数组规范化为验证函数:

import { computeAccept } from '@hx/components';

const check = computeAccept(['.pdf', 'image/*']);
check({ name: 'doc.pdf', mimeType: 'application/pdf' });   // true
check({ name: 'photo.jpg', mimeType: 'image/jpeg' });      // true
check({ name: 'data.exe', mimeType: 'application/x-msdownload' }); // false

SVG 图标默认值

import { computeSvgIconDefaults } from '@hx/components';
// 根据设置计算默认尺寸和样式

Context Provider

内部 Provider,使用 EventEmitter 进行跨组件通信:

Provider 提供的状态
HxPanelProvider { collapsed, toggle }
HxTabProvider { isActive, mark, disabled, activate }
HxTabsProvider { tabs, activeMark, switchToTab, restoreScroll }
HxUploadProvider { files, errors, uploading, addFiles, removeFile, uploadAll, clearFiles }
HxSelectOptionsProvider { options, selectedValue, searchText, selectOption }
HxOverlayInternalProvider 生命周期:entering → entered → exiting → exited

FormatInput 模式工具包

HxFormatInput 的抽象基类和内置实现:

import {
  AbstractHxFormatInputPatternKit,
  HxFormatInputNumberPatternKit,
  HxFormatInputDateTimePatternKit,
  HxFormatInputPatternKitsInner,  // 注册表:@n → NumberKit,@d → DateTimeKit
} from '@hx/components';

class MyKit extends AbstractHxFormatInputPatternKit {
  parse(raw: string): ParsedValue { /* ... */ }
  format(parsed: ParsedValue): string { /* ... */ }
}

日期本地化

DateLocaleFormatUtils 使用 Intl.DateTimeFormat.formatToParts() 提供各日期/时间部分的本地化格式化。所有格式化均为 UTC 语义——日期以 UTCDate(基于 JS Date 的无时区包装)传入,宿主时区不会影响输出。

import { DateLocaleFormatUtils, UTCDate } from '@hx/components';

const date = UTCDate.of(2025, 6, 6); // 2025-07-06

// 按 locale 格式化各部分(返回带字面量后缀的字符串)
DateLocaleFormatUtils.formatYear(date, 'ja-JP', false);  // "令和7年"
DateLocaleFormatUtils.formatYear(date, 'zh-CN', true);    // "2025年"(强制公历)
DateLocaleFormatUtils.formatMonth(date, 'zh-CN', false);  // "7月"
DateLocaleFormatUtils.formatDay(date, 'en-US', true);     // "6"
DateLocaleFormatUtils.formatWeekday(date, 'zh-CN', true); // "周日"

插件架构

非公历历法(日本、民国、佛历、韩语、科普特、埃塞俄比亚、希伯来、伊斯兰 ×3、波斯、印度、中国)通过实现 DateLocaleNotGregorianProvider 接口的插件系统管理。每个插件声明自己的 accept()、calendar()、supportedLanguages() 以及可选的 eraAs() / yearAs() / yearHeaderLabel() / monthHeaderLabel() / eraOfDays() / monthsOfYear() / yearsAround() 方法。历法导航还需要实现 DateMoveNotGregorianProvider 的移动插件(moveYear / moveMonth / 边界钩子),通过 DateMoveUtils.enableNotGregorianMoveProvider() 注册。

启用 locale 特定历法支持:

import {
  DateBuddhistUtils, DateChineseUtils, DateCopticUtils, DateEthiopicUtils,
  DateHebrewUtils, DateIndianUtils, DateIslamicUtils, DateIslamicCivilUtils,
  DateIslamicUmalquraUtils, DateJapaneseUtils, DateKoreanUtils, DateMinguoUtils,
  DatePersianUtils
} from '@hx/components';

DateCopticUtils.enable();           // ar-EG → coptic(科普特殉教纪年)
DateEthiopicUtils.enable();         // am-ET / ti-ET → ethiopic(道成肉身纪元)
DateJapaneseUtils.enable();         // ja / ja-JP → japanese(日本历)
DateMinguoUtils.enable();           // zh-TW / zh-Hant-TW → roc(民国纪年)
DateKoreanUtils.enable();           // ko / ko-KR / ko-KP → 公历(无特殊历法)
DateBuddhistUtils.enable();         // th / th-TH → buddhist(佛历)
DateHebrewUtils.enable();           // he / he-IL → hebrew(希伯来历)
DateIslamicUtils.enable();          // ar-DZ / ar-MA / ar-TN → islamic
DateIslamicCivilUtils.enable();     // ar-AE / ar-IQ / ar-SY / ... → islamic-civil
DateIslamicUmalquraUtils.enable();  // ar-SA / ar-OM / ar-YE / ... → islamic-umalqura
DateIndianUtils.enable();           // hi / hi-IN / en-IN → indian(印度国定历)
DatePersianUtils.enable();          // fa / fa-IR / ckb-IR / ... → persian(波斯历)
DateChineseUtils.enable();          // zh-CN → chinese(农历)

DateCopticUtils.disable();          // 移除科普特插件(每个插件也提供 disable())

科普特历法跨两个纪元:殉教纪元(AM,公元 284 年起)和戴克里先纪元前(显示为 "B.D." 前缀,如 "B.D. 185")。插件实现了 yearAs() 以自动处理纪元前缀。

埃塞俄比亚历法跨两个纪元:道成肉身纪元(A.I.,公元 8 年起)和道成肉身纪元前(显示为 "B.I." 前缀,如 "B.I. 5493")。B.I. 纪元年份范围为 5493–5500(公元 1–8 年)。插件实现了 yearAs() 以自动处理纪元前缀。

波斯历的闰年规则与 ICU(persncal.cpp)一致:基础为 33 年周期 8 闰(年份余数 ∈ {1, 5, 9, 13, 17, 22, 26, 30}),并在波斯历 1502–2987 年由闰日平移表覆盖——每个条目 X 使 X 年变平年、X+1 年变闰年,闰日整体后移一年;2988 年起回到纯 mod-33 规则。移动提供者实现同一规则,保证导航与格式化始终与运行环境 Intl 一致。

历法解析 — 当 gregorian 为 false 时,DateLocaleFormatUtils 通过 CALENDAR_MAP 从 locale 解析对应历法,该映射由每个已启用插件的 supportedLanguages() 填充:

  • ar-AE / ar-BH / ar-IQ / ar-KW / ar-LB / ar-QA / ar-SY → islamic-civil
  • ar-DZ / ar-MA / ar-TN → islamic
  • ar-OM / ar-SA / ar-SD / ar-YE → islamic-umalqura
  • fa / fa-AF / fa-IR / ckb-IR → persian
  • ps / ps-AF → persian
  • mzn / mzn-IR / lrc / lrc-IR → persian
  • uz-Arab / uz-Arab-AF → persian
  • hi-IN / en-IN / hi → indian
  • he-IL / he → hebrew
  • 由插件管理:am-ET / ti-ET → ethiopic,ar-EG → coptic,ja / ja-JP → japanese,zh-TW / zh-Hant-TW → roc,th / th-TH → buddhist

HxDateTimeFormatCalendar 支持全部 18 个 ECMA-402 历法值:

type HxDateTimeFormatCalendar =
  | 'gregory' | 'buddhist' | 'chinese' | 'coptic' | 'dangi'
  | 'ethioaa' | 'ethiopic' | 'hebrew' | 'indian'
  | 'islamic' | 'islamic-civil' | 'islamic-umalqura'
  | 'islamic-tbla' | 'islamic-rgsa'
  | 'iso8601' | 'japanese' | 'persian' | 'roc'
  | string; // 可扩展,支持插件自定义历法

自定义历法插件

实现 DateLocaleNotGregorianProvider 接口添加自定义历法支持:

import type { DateLocaleNotGregorianProvider } from '@hx/components';
import { DateLocaleFormatUtils } from '@hx/components';

const myPlugin: DateLocaleNotGregorianProvider = {
  accept(lang) { return lang === 'my-LOCALE'; },
  calendar() { return 'dangi'; },
  supportedLanguages() { return ['my-LOCALE']; },
  eraAs(date, partsOf, lang) { /* 自定义年号格式化 */ },
  yearAs(date, partsOf, lang) { /* 自定义年份格式化 */ },
  yearHeaderLabel(value, era, year, lang) { /* 自定义年份标签 */ },
  monthHeaderLabel(value, era, year, month, lang) { /* 自定义月份标签 */ },
  eraOfDays(days, lang) { /* 每日年号标记 */ },
};

DateLocaleFormatUtils.enableNotGregorianLocaleProvider(myPlugin);
DateLocaleFormatUtils.disableNotGregorianLocaleProvider(myPlugin);

历法导航还需注册实现 DateMoveNotGregorianProvider 的移动插件:

import { DateMoveUtils, type DateMoveNotGregorianProvider } from '@hx/components';

const myMovePlugin: DateMoveNotGregorianProvider = {
  accept(lang) { return lang === 'my-LOCALE'; },
  moveYear(date, yearOffset, lang) { /* ... */ },
  moveMonth(date, monthOffset, lang) { /* ... */ },
  isPreviousYearAllowed(lang, firstDayOfCurrentMonthOfGregory) { /* ... */ },
  // isNextYearAllowed / isPreviousMonthAllowed / isNextMonthAllowed ...
};

DateMoveUtils.enableNotGregorianMoveProvider(myMovePlugin);

DateLocaleFormatUtils.getWeekInfo() 从 CLDR 数据读取 locale 的周末和每周第一天:

const { weekends, firstDayOfWeek } = DateLocaleFormatUtils.getWeekInfo('ar-SA');
// weekends: ['fri', 'sat'], firstDayOfWeek: 'sat'
const { weekends, firstDayOfWeek } = DateLocaleFormatUtils.getWeekInfo('en-US');
// weekends: ['sat', 'sun'], firstDayOfWeek: 'sun'

当 gregorian 为 true 时,所有格式化均使用公历。当 gregorian 为 false 时,使用 CALENDAR_MAP 中对应的 locale 历法,未映射时回退到公历。

Clone this wiki locally