@wordrhyme/auto-crud 1.5.4 → 1.5.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -405,6 +405,21 @@ function useDateFormatterVersion() {
405
405
  function getHostDateFormatter() {
406
406
  return hostDateFormatters[hostDateFormatters.length - 1]?.formatter;
407
407
  }
408
+ const canonicalDatePattern = /^(\d{4})-(\d{2})-(\d{2})$/;
409
+ const canonicalDateTimePrefixPattern = /^(\d{4})-(\d{2})-(\d{2})(?=[T ])/;
410
+ function isValidCalendarDate(year, month, day) {
411
+ if (!year || !month || !day) return false;
412
+ const date = /* @__PURE__ */ new Date(`${year}-${month}-${day}T00:00:00.000Z`);
413
+ return !Number.isNaN(date.getTime()) && date.getUTCFullYear() === Number(year) && date.getUTCMonth() + 1 === Number(month) && date.getUTCDate() === Number(day);
414
+ }
415
+ function isValidCanonicalDate(value) {
416
+ const match = canonicalDatePattern.exec(value);
417
+ return !!match && isValidCalendarDate(match[1], match[2], match[3]);
418
+ }
419
+ function hasValidCanonicalDateTimePrefix(value) {
420
+ const match = canonicalDateTimePrefixPattern.exec(value);
421
+ return !match || isValidCalendarDate(match[1], match[2], match[3]);
422
+ }
408
423
  /**
409
424
  * Register the host's process-wide date presentation policy without coupling
410
425
  * AutoCrud to locale or timezone selection. The returned cleanup removes only
@@ -431,18 +446,45 @@ function setDateFormatter(formatter) {
431
446
  if (hostDateFormatters.length !== previousLength && getHostDateFormatter() !== currentFormatter) notifyDateFormatterChange();
432
447
  };
433
448
  }
434
- function formatDate(date, opts = {}) {
449
+ function formatDate(date, opts = {}, preset) {
435
450
  if (date === void 0) return "";
451
+ if (preset === "date" && typeof date === "string" && canonicalDatePattern.test(date)) return isValidCanonicalDate(date) ? date : "";
452
+ if (preset === "datetime" && typeof date === "string" && !hasValidCanonicalDateTimePrefix(date)) return "";
436
453
  try {
437
454
  const value = new Date(date);
455
+ if (Number.isNaN(value.getTime())) return "";
438
456
  const options = {
439
- month: opts.month ?? "long",
440
- day: opts.day ?? "numeric",
457
+ month: opts.month ?? (preset ? "2-digit" : "long"),
458
+ day: opts.day ?? (preset ? "2-digit" : "numeric"),
441
459
  year: opts.year ?? "numeric",
460
+ ...preset === "datetime" ? {
461
+ hour: opts.hour ?? "2-digit",
462
+ minute: opts.minute ?? "2-digit",
463
+ second: opts.second ?? "2-digit",
464
+ hourCycle: opts.hourCycle ?? "h23"
465
+ } : {},
442
466
  ...opts
443
467
  };
444
468
  const hostDateFormatter = getHostDateFormatter();
445
- return hostDateFormatter ? hostDateFormatter(value, options) : new Intl.DateTimeFormat("en-US", options).format(value);
469
+ if (hostDateFormatter) return preset ? hostDateFormatter(value, options, preset) : hostDateFormatter(value, options);
470
+ if (preset) {
471
+ const parts = new Intl.DateTimeFormat("en-CA", {
472
+ timeZone: opts.timeZone,
473
+ year: "numeric",
474
+ month: "2-digit",
475
+ day: "2-digit",
476
+ ...preset === "datetime" ? {
477
+ hour: "2-digit",
478
+ minute: "2-digit",
479
+ second: "2-digit",
480
+ hourCycle: "h23"
481
+ } : {}
482
+ }).formatToParts(value);
483
+ const read = (type) => parts.find((part) => part.type === type)?.value ?? "";
484
+ const formattedDate = `${read("year")}-${read("month")}-${read("day")}`;
485
+ return preset === "datetime" ? `${formattedDate} ${read("hour")}:${read("minute")}:${read("second")}` : formattedDate;
486
+ }
487
+ return new Intl.DateTimeFormat("en-US", options).format(value);
446
488
  } catch {
447
489
  return "";
448
490
  }
@@ -5978,6 +6020,16 @@ function mergeFields(base, override) {
5978
6020
  const keys = new Set([...Object.keys(base ?? {}), ...Object.keys(override ?? {})]);
5979
6021
  return Object.fromEntries(Array.from(keys).map((key) => [key, mergeFieldConfig(base?.[key], override?.[key])]));
5980
6022
  }
6023
+ function applyResourceTablePresentation(fields, resourceFields) {
6024
+ if (!fields || !resourceFields) return fields;
6025
+ return Object.fromEntries(Object.entries(fields).map(([key, config]) => {
6026
+ const table = resourceFields[key]?.table;
6027
+ return [key, table === void 0 ? config : {
6028
+ ...config,
6029
+ table: mergeFieldPart(config.table, table)
6030
+ }];
6031
+ }));
6032
+ }
5981
6033
  function normalizeFieldOptions(options) {
5982
6034
  if (!options || options.length === 0) return void 0;
5983
6035
  return options.map((option) => ({
@@ -5985,6 +6037,18 @@ function normalizeFieldOptions(options) {
5985
6037
  value: String(option.value)
5986
6038
  }));
5987
6039
  }
6040
+ function getTableConfig(config) {
6041
+ return config.table && typeof config.table === "object" ? config.table : void 0;
6042
+ }
6043
+ function getTableOptions(config) {
6044
+ const table = getTableConfig(config);
6045
+ if (table?.options) return normalizeFieldOptions(table.options);
6046
+ if (table?.dataSource) return void 0;
6047
+ return normalizeFieldOptions(config.enum);
6048
+ }
6049
+ function getTableDataSource(config) {
6050
+ return getTableConfig(config)?.dataSource ?? config.dataSource;
6051
+ }
5988
6052
  function toTableOptions(options) {
5989
6053
  return options?.map(({ label, value, searchText, count, icon }) => ({
5990
6054
  label,
@@ -6060,8 +6124,8 @@ function mergeOptionsByField(left, right) {
6060
6124
  function shouldResolveOptions(field, config, hiddenColumns) {
6061
6125
  if (!config) return false;
6062
6126
  if (hiddenColumns.has(field)) return false;
6063
- if (normalizeFieldOptions(config.enum)) return false;
6064
- return normalizeDataSourceConfig(config.dataSource) !== void 0;
6127
+ if (getTableOptions(config)) return false;
6128
+ return normalizeDataSourceConfig(getTableDataSource(config)) !== void 0;
6065
6129
  }
6066
6130
  function useRegistryVersion(subscribe) {
6067
6131
  const [version$1, setVersion] = react.useState(0);
@@ -6306,7 +6370,7 @@ function useDynamicResolveOptions(fields, rows, hiddenColumns) {
6306
6370
  const hiddenSet = new Set(hiddenColumns);
6307
6371
  return Object.entries(fields).flatMap(([field, config]) => {
6308
6372
  if (!shouldResolveOptions(field, config, hiddenSet)) return [];
6309
- const source = normalizeDataSourceConfig(config.dataSource);
6373
+ const source = normalizeDataSourceConfig(getTableDataSource(config));
6310
6374
  const values = getResolveValues(rows, field);
6311
6375
  return source && values.length > 0 ? [{
6312
6376
  field,
@@ -6403,12 +6467,16 @@ function buildTableOverrides(fields, legacyOverrides, dynamicFilterState, dynami
6403
6467
  const result = { ...legacyOverrides };
6404
6468
  if (fields) for (const [key, config] of Object.entries(fields)) {
6405
6469
  const fieldOptions = normalizeFieldOptions(config.enum);
6470
+ const tableConfig = getTableConfig(config);
6471
+ const presentationOptions = getTableOptions(config);
6406
6472
  const tableOptions = toTableOptions(fieldOptions);
6407
6473
  const filterDataSourceRegistered = dynamicFilterState?.registeredByField[key];
6408
6474
  const filterDataSourceSearchable = dynamicFilterState?.searchableByField[key];
6409
6475
  const currentDynamicOptions = !fieldOptions && filterDataSourceRegistered ? toTableOptions(dynamicFilterState?.optionsByField[key] ?? []) : void 0;
6410
- const mergedDynamicOptions = mergeFieldOptions(!fieldOptions && filterDataSourceRegistered ? dynamicFilterState?.labelOptionsByField[key] ?? dynamicFilterState?.optionsByField[key] ?? [] : void 0, !fieldOptions ? dynamicResolveState?.optionsByField[key] : void 0);
6411
- const dynamicOptions = !fieldOptions && mergedDynamicOptions ? toTableOptions(mergedDynamicOptions) : void 0;
6476
+ const dynamicFilterLabelOptions = !fieldOptions && filterDataSourceRegistered ? dynamicFilterState?.labelOptionsByField[key] ?? dynamicFilterState?.optionsByField[key] ?? [] : void 0;
6477
+ const dynamicResolveOptions = !presentationOptions ? dynamicResolveState?.optionsByField[key] : void 0;
6478
+ const mergedSharedDynamicOptions = mergeFieldOptions(dynamicFilterLabelOptions, tableConfig?.dataSource ? void 0 : dynamicResolveOptions);
6479
+ const dynamicOptions = !fieldOptions && mergedSharedDynamicOptions ? toTableOptions(mergedSharedDynamicOptions) : void 0;
6412
6480
  const tableMeta = config.table !== false && typeof config.table === "object" ? config.table.meta : void 0;
6413
6481
  const fieldEnumMeta = tableOptions ? {
6414
6482
  options: tableOptions,
@@ -6472,11 +6540,24 @@ function buildTableOverrides(fields, legacyOverrides, dynamicFilterState, dynami
6472
6540
  hidden: true
6473
6541
  };
6474
6542
  else if (config.table && typeof config.table === "object") {
6475
- const { meta,...tableProps } = config.table;
6543
+ const { meta, display,...tableProps } = config.table;
6544
+ delete tableProps.options;
6545
+ delete tableProps.dataSource;
6476
6546
  result[key] = {
6477
6547
  ...result[key],
6478
6548
  ...tableProps
6479
6549
  };
6550
+ const hasTableLabels = config.table.options !== void 0 || config.table.dataSource !== void 0;
6551
+ if (display && display !== "auto" || hasTableLabels) {
6552
+ const options = presentationOptions ?? dynamicResolveOptions ?? mergedSharedDynamicOptions;
6553
+ result[key] = {
6554
+ ...result[key],
6555
+ cell: ({ row }) => renderFieldValue(row.getValue(key), "string", {
6556
+ true: "true",
6557
+ false: "false"
6558
+ }, options, display ?? "auto")
6559
+ };
6560
+ }
6480
6561
  }
6481
6562
  }
6482
6563
  return result;
@@ -6524,7 +6605,9 @@ function buildHiddenColumns(fields, legacyHidden, denyFields, legacyOverrides) {
6524
6605
  for (const key of DEFAULT_HIDDEN_AUDIT_FIELDS) if (!legacyHiddenSet.has(key) && !denySet.has(key) && isTableOverrideExplicitlyVisible(legacyOverrides?.[key])) hidden.delete(key);
6525
6606
  if (fields) for (const [key, config] of Object.entries(fields)) {
6526
6607
  if (isDefaultHiddenAuditField(key) && !legacyHiddenSet.has(key) && !denySet.has(key) && isFieldTableExplicitlyVisible(config)) hidden.delete(key);
6527
- if (config.hidden) hidden.add(key);
6608
+ if (config.table && typeof config.table === "object" && config.table.hidden === false) {
6609
+ if (!legacyHiddenSet.has(key) && !denySet.has(key)) hidden.delete(key);
6610
+ } else if (config.hidden) hidden.add(key);
6528
6611
  else if (config.table === false) hidden.add(key);
6529
6612
  else if (config.table && typeof config.table === "object" && config.table.hidden) hidden.add(key);
6530
6613
  }
@@ -6574,12 +6657,32 @@ function buildBatchUpdateFields(schema, batchFields, fields) {
6574
6657
  /**
6575
6658
  * 渲染字段值
6576
6659
  */
6577
- function renderFieldValue(value, type, booleanLocale, options) {
6660
+ function renderFieldValue(value, type, booleanLocale, options, display = "auto") {
6578
6661
  if (value === null || value === void 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6579
6662
  className: "text-muted-foreground",
6580
6663
  children: "-"
6581
6664
  });
6582
- if (options && options.length > 0) {
6665
+ const renderText = (item) => getOptionLabel(item, options);
6666
+ if (display === "text") return Array.isArray(value) ? value.map(renderText).join(", ") : renderText(value);
6667
+ if (display === "date" || display === "datetime") return formatDate(value, {}, display);
6668
+ if (display === "badge" && (!options || options.length === 0)) {
6669
+ if (Array.isArray(value)) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6670
+ className: "flex gap-1 flex-wrap",
6671
+ children: [value.slice(0, 5).map((item, index$1) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__wordrhyme_shadcn.Badge, {
6672
+ variant: "secondary",
6673
+ children: String(item)
6674
+ }, index$1)), value.length > 5 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__wordrhyme_shadcn.Badge, {
6675
+ variant: "outline",
6676
+ children: ["+", value.length - 5]
6677
+ })]
6678
+ });
6679
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__wordrhyme_shadcn.Badge, {
6680
+ variant: "outline",
6681
+ className: "capitalize",
6682
+ children: String(value)
6683
+ });
6684
+ }
6685
+ if ((display === "badge" || display === "auto") && options && options.length > 0) {
6583
6686
  if (Array.isArray(value)) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6584
6687
  className: "flex gap-1 flex-wrap",
6585
6688
  children: [value.slice(0, 5).map((v, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__wordrhyme_shadcn.Badge, {
@@ -6771,7 +6874,7 @@ function resolveActions(actionsOrFn, defaults, rowActionsLocale, context) {
6771
6874
  function AutoCrudTable({ id, title, description, schema, resource, fields, table: tableConfig, form: formConfig, permissions, actions: actionItems, toolbar, toolbarActions, locale: localeProp, onCreate }) {
6772
6875
  const locale = resolveLocale(localeProp);
6773
6876
  const resolvedSchema = resource.schema ?? schema;
6774
- const resolvedFields = react.useMemo(() => mergeFields(resource.fields, fields) ?? {}, [fields, resource.fields]);
6877
+ const resolvedFields = react.useMemo(() => applyResourceTablePresentation(mergeFields(resource.fields, fields), resource.fields) ?? {}, [fields, resource.fields]);
6775
6878
  const can = {
6776
6879
  create: permissions?.can?.create ?? true,
6777
6880
  update: permissions?.can?.update ?? true,
@@ -6794,7 +6897,18 @@ function AutoCrudTable({ id, title, description, schema, resource, fields, table
6794
6897
  tableConfig?.overrides
6795
6898
  ]);
6796
6899
  const dynamicResolveOptions = useDynamicResolveOptions(resolvedFields, resource.tableData.data, hiddenColumns);
6797
- const dynamicOptionsByField = react.useMemo(() => mergeOptionsByField(dynamicFilterOptions.labelOptionsByField, dynamicResolveOptions.optionsByField), [dynamicFilterOptions.labelOptionsByField, dynamicResolveOptions.optionsByField]);
6900
+ const sharedResolveOptions = useDynamicResolveOptions(react.useMemo(() => Object.fromEntries(Object.entries(resolvedFields).flatMap(([field, config]) => {
6901
+ const table = getTableConfig(config);
6902
+ if (table?.options === void 0 && table?.dataSource === void 0) return [];
6903
+ const { table: _table,...sharedConfig } = config;
6904
+ return [[field, sharedConfig]];
6905
+ })), [resolvedFields]), resource.tableData.data, hiddenColumns);
6906
+ const viewDynamicOptionsByField = react.useMemo(() => mergeOptionsByField(mergeOptionsByField(dynamicFilterOptions.labelOptionsByField, sharedResolveOptions.optionsByField), Object.fromEntries(Object.entries(dynamicResolveOptions.optionsByField).filter(([field]) => !getTableConfig(resolvedFields[field] ?? {})?.dataSource))), [
6907
+ dynamicFilterOptions.labelOptionsByField,
6908
+ dynamicResolveOptions.optionsByField,
6909
+ sharedResolveOptions.optionsByField,
6910
+ resolvedFields
6911
+ ]);
6798
6912
  const resourceIdKey = resource.idKey ?? "id";
6799
6913
  const actionRegistryVersion = useCrudActionsVersion();
6800
6914
  const unifiedActions = react.useMemo(() => {
@@ -7126,7 +7240,7 @@ function AutoCrudTable({ id, title, description, schema, resource, fields, table
7126
7240
  data: resource.modal.selected,
7127
7241
  schema: resolvedSchema,
7128
7242
  fields: resolvedFields,
7129
- dynamicOptions: dynamicOptionsByField,
7243
+ dynamicOptions: viewDynamicOptionsByField,
7130
7244
  denyFields,
7131
7245
  locale
7132
7246
  }),
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import * as react_jsx_runtime3 from "react/jsx-runtime";
1
+ import * as react_jsx_runtime6 from "react/jsx-runtime";
2
2
  import { z } from "zod";
3
3
  import { JsonSchemaFormScope } from "@wordrhyme/formily-shadcn";
4
4
  import * as _tanstack_react_table0 from "@tanstack/react-table";
@@ -413,7 +413,7 @@ declare function AutoTableActionBar<TData>({
413
413
  extraActions,
414
414
  actions,
415
415
  deleteConfirmation
416
- }: AutoTableActionBarProps<TData>): react_jsx_runtime3.JSX.Element;
416
+ }: AutoTableActionBarProps<TData>): react_jsx_runtime6.JSX.Element;
417
417
  //#endregion
418
418
  //#region src/lib/schema-bridge/types.d.ts
419
419
  /**
@@ -592,7 +592,7 @@ declare function AutoTable<T extends z.ZodObject<z.ZodRawShape>>({
592
592
  onSelectedCountChange,
593
593
  onSelectedRowsChange,
594
594
  getSelectedRows
595
- }: AutoTableProps<T>): react_jsx_runtime3.JSX.Element;
595
+ }: AutoTableProps<T>): react_jsx_runtime6.JSX.Element;
596
596
  //#endregion
597
597
  //#region src/i18n/locale.d.ts
598
598
  /**
@@ -808,6 +808,7 @@ interface FieldOption {
808
808
  icon?: React$1.FC<React$1.SVGProps<SVGSVGElement>>;
809
809
  disabled?: boolean;
810
810
  }
811
+ type FieldTableDisplay = 'auto' | 'text' | 'badge' | 'date' | 'datetime';
811
812
  /**
812
813
  * 统一字段配置
813
814
  * 支持共用配置 + 表格/表单特定配置
@@ -838,8 +839,16 @@ interface Field {
838
839
  table?: {
839
840
  /** 是否在表格中隐藏 */
840
841
  hidden?: boolean;
842
+ /** 表格列标签 */
843
+ label?: string;
841
844
  /** 排序权重,数值越小越靠前 */
842
845
  index?: number;
846
+ /** 仅用于表格展示的静态值标签 */
847
+ options?: FieldOption[];
848
+ /** 仅用于表格展示的动态值标签源 */
849
+ dataSource?: AutoCrudDataSourceConfig;
850
+ /** 声明式单元格展示模式;非 auto 模式会覆盖拥有方自定义 cell */
851
+ display?: FieldTableDisplay;
843
852
  /** 筛选器配置 */
844
853
  meta?: Record<string, unknown>;
845
854
  /** 其他列配置 */
@@ -1084,7 +1093,7 @@ declare function AutoCrudTable<TSchema extends z.ZodObject<z.ZodRawShape>>({
1084
1093
  toolbarActions,
1085
1094
  locale: localeProp,
1086
1095
  onCreate
1087
- }: AutoCrudTableProps<TSchema>): react_jsx_runtime3.JSX.Element;
1096
+ }: AutoCrudTableProps<TSchema>): react_jsx_runtime6.JSX.Element;
1088
1097
  //#endregion
1089
1098
  //#region src/components/auto-crud/auto-form.d.ts
1090
1099
  interface AutoFormProps<T extends z.ZodObject<z.ZodRawShape>> {
@@ -1123,7 +1132,7 @@ declare function AutoFormInner<T extends z.ZodObject<z.ZodRawShape>>({
1123
1132
  labelAlign,
1124
1133
  labelWidth,
1125
1134
  showSubmitButton
1126
- }: AutoFormProps<T>, ref: React.Ref<AutoFormRef>): react_jsx_runtime3.JSX.Element;
1135
+ }: AutoFormProps<T>, ref: React.Ref<AutoFormRef>): react_jsx_runtime6.JSX.Element;
1127
1136
  declare const AutoForm: <T extends z.ZodObject<z.ZodRawShape>>(props: AutoFormProps<T> & {
1128
1137
  ref?: React.Ref<AutoFormRef>;
1129
1138
  }) => ReturnType<typeof AutoFormInner>;
@@ -1439,7 +1448,7 @@ declare function AutoTableSimpleFilters<TData>({
1439
1448
  filters: externalFilters,
1440
1449
  onFiltersChange,
1441
1450
  leading
1442
- }: AutoTableSimpleFiltersProps<TData>): react_jsx_runtime3.JSX.Element | null;
1451
+ }: AutoTableSimpleFiltersProps<TData>): react_jsx_runtime6.JSX.Element | null;
1443
1452
  //#endregion
1444
1453
  //#region src/components/auto-crud/form-modal.d.ts
1445
1454
  type ModalVariant = 'dialog' | 'sheet';
@@ -1483,7 +1492,7 @@ declare function CrudFormModal<T extends z.ZodObject<z.ZodRawShape>>({
1483
1492
  labelAlign,
1484
1493
  labelWidth,
1485
1494
  className
1486
- }: CrudFormModalProps<T>): react_jsx_runtime3.JSX.Element;
1495
+ }: CrudFormModalProps<T>): react_jsx_runtime6.JSX.Element;
1487
1496
  //#endregion
1488
1497
  //#region src/components/auto-crud/import-dialog.d.ts
1489
1498
  interface ImportDialogProps {
@@ -1505,7 +1514,7 @@ declare function ImportDialog({
1505
1514
  columns,
1506
1515
  title,
1507
1516
  locale
1508
- }: ImportDialogProps): react_jsx_runtime3.JSX.Element;
1517
+ }: ImportDialogProps): react_jsx_runtime6.JSX.Element;
1509
1518
  //#endregion
1510
1519
  //#region src/components/auto-crud/export-dialog.d.ts
1511
1520
  type ExportMode = 'selected' | 'filtered';
@@ -1525,7 +1534,7 @@ declare function ExportDialog({
1525
1534
  selectedCount,
1526
1535
  onExport,
1527
1536
  canExportFiltered
1528
- }: ExportDialogProps): react_jsx_runtime3.JSX.Element;
1537
+ }: ExportDialogProps): react_jsx_runtime6.JSX.Element;
1529
1538
  //#endregion
1530
1539
  //#region src/components/data-table/data-table.d.ts
1531
1540
  interface DataTableProps<TData> extends React$1.ComponentProps<'div'> {
@@ -1538,7 +1547,7 @@ declare function DataTable<TData>({
1538
1547
  children,
1539
1548
  className,
1540
1549
  ...props
1541
- }: DataTableProps<TData>): react_jsx_runtime3.JSX.Element;
1550
+ }: DataTableProps<TData>): react_jsx_runtime6.JSX.Element;
1542
1551
  //#endregion
1543
1552
  //#region src/components/data-table/data-table-advanced-toolbar.d.ts
1544
1553
  interface DataTableAdvancedToolbarProps<TData> extends React$1.ComponentProps<'div'> {
@@ -1549,7 +1558,7 @@ declare function DataTableAdvancedToolbar<TData>({
1549
1558
  children,
1550
1559
  className,
1551
1560
  ...props
1552
- }: DataTableAdvancedToolbarProps<TData>): react_jsx_runtime3.JSX.Element;
1561
+ }: DataTableAdvancedToolbarProps<TData>): react_jsx_runtime6.JSX.Element;
1553
1562
  //#endregion
1554
1563
  //#region src/components/data-table/data-table-column-header.d.ts
1555
1564
  interface DataTableColumnHeaderProps<TData, TValue> extends React.ComponentProps<typeof DropdownMenuTrigger> {
@@ -1561,7 +1570,7 @@ declare function DataTableColumnHeader<TData, TValue>({
1561
1570
  label,
1562
1571
  className,
1563
1572
  ...props
1564
- }: DataTableColumnHeaderProps<TData, TValue>): react_jsx_runtime3.JSX.Element;
1573
+ }: DataTableColumnHeaderProps<TData, TValue>): react_jsx_runtime6.JSX.Element;
1565
1574
  //#endregion
1566
1575
  //#region src/components/data-table/data-table-faceted-filter.d.ts
1567
1576
  interface DataTableFacetedFilterProps<TData, TValue> {
@@ -1575,7 +1584,7 @@ declare function DataTableFacetedFilter<TData, TValue>({
1575
1584
  title,
1576
1585
  options,
1577
1586
  multiple
1578
- }: DataTableFacetedFilterProps<TData, TValue>): react_jsx_runtime3.JSX.Element;
1587
+ }: DataTableFacetedFilterProps<TData, TValue>): react_jsx_runtime6.JSX.Element;
1579
1588
  //#endregion
1580
1589
  //#region src/components/data-table/data-table-pagination.d.ts
1581
1590
  interface DataTablePaginationProps<TData> extends React.ComponentProps<'div'> {
@@ -1587,7 +1596,7 @@ declare function DataTablePagination<TData>({
1587
1596
  pageSizeOptions,
1588
1597
  className,
1589
1598
  ...props
1590
- }: DataTablePaginationProps<TData>): react_jsx_runtime3.JSX.Element;
1599
+ }: DataTablePaginationProps<TData>): react_jsx_runtime6.JSX.Element;
1591
1600
  //#endregion
1592
1601
  //#region src/components/data-table/data-table-toolbar.d.ts
1593
1602
  interface DataTableToolbarProps<TData> extends React$1.ComponentProps<'div'> {
@@ -1598,7 +1607,7 @@ declare function DataTableToolbar<TData>({
1598
1607
  children,
1599
1608
  className,
1600
1609
  ...props
1601
- }: DataTableToolbarProps<TData>): react_jsx_runtime3.JSX.Element;
1610
+ }: DataTableToolbarProps<TData>): react_jsx_runtime6.JSX.Element;
1602
1611
  //#endregion
1603
1612
  //#region src/components/data-table/data-table-view-options.d.ts
1604
1613
  interface DataTableViewOptionsProps<TData> extends React$1.ComponentProps<typeof PopoverContent> {
@@ -1609,7 +1618,7 @@ declare function DataTableViewOptions<TData>({
1609
1618
  table,
1610
1619
  disabled,
1611
1620
  ...props
1612
- }: DataTableViewOptionsProps<TData>): react_jsx_runtime3.JSX.Element;
1621
+ }: DataTableViewOptionsProps<TData>): react_jsx_runtime6.JSX.Element;
1613
1622
  //#endregion
1614
1623
  //#region src/lib/crud-actions.d.ts
1615
1624
  type CrudActionZone = 'toolbar' | 'row' | 'batch';
@@ -1904,7 +1913,8 @@ declare function createMemoryDataSource<T extends {
1904
1913
  }>(initialData?: T[]): DataSource<T>;
1905
1914
  //#endregion
1906
1915
  //#region src/lib/format.d.ts
1907
- type DateFormatter = (date: Date, options: Intl.DateTimeFormatOptions) => string;
1916
+ type DateFormatPreset = 'date' | 'datetime';
1917
+ type DateFormatter = (date: Date, options: Intl.DateTimeFormatOptions, preset?: DateFormatPreset) => string;
1908
1918
  /**
1909
1919
  * Register the host's process-wide date presentation policy without coupling
1910
1920
  * AutoCrud to locale or timezone selection. The returned cleanup removes only
@@ -1912,7 +1922,7 @@ type DateFormatter = (date: Date, options: Intl.DateTimeFormatOptions) => string
1912
1922
  * Passing undefined clears every registration.
1913
1923
  */
1914
1924
  declare function setDateFormatter(formatter?: DateFormatter): () => void;
1915
- declare function formatDate(date: Date | string | number | undefined, opts?: Intl.DateTimeFormatOptions): string;
1925
+ declare function formatDate(date: Date | string | number | undefined, opts?: Intl.DateTimeFormatOptions, preset?: DateFormatPreset): string;
1916
1926
  //#endregion
1917
1927
  //#region src/lib/humanize.d.ts
1918
1928
  /**
@@ -1992,4 +2002,4 @@ declare function exportAllToCSV<T extends Record<string, unknown>>(data: T[], op
1992
2002
  */
1993
2003
  declare function downloadCSVTemplate(headers: string[], filename?: string): void;
1994
2004
  //#endregion
1995
- export { type ActionConfig, type ActionItem, type ActionsColumnConfig, type AutoCrudActionConfig, type AutoCrudActionsConfig, type AutoCrudDataSourceConfig, type AutoCrudDataSourceContext, type AutoCrudDataSourceEntry, type AutoCrudDataSourceLoader, type AutoCrudDataSourceRegistration, type AutoCrudFormComponentConfig, type AutoCrudLocale, type AutoCrudOption, type AutoCrudQueryCapabilities, type AutoCrudRowActionContext, AutoCrudTable, type AutoCrudTableProps, type AutoCrudToolbarContext, type AutoCrudToolbarResolver, AutoForm, type AutoFormProps, type AutoQueryParams, AutoTable, AutoTableActionBar, AutoTableSimpleFilters, type BatchActionConfig, type BatchActionContext, type BatchActionItem, type BatchBuiltinActionItem, type BatchBuiltinActionType, type BatchCustomActionItem, type BatchUpdateField, type ColumnOverrides, type CreateFormSchemaOptions, type CreateTableSchemaOptions, type CrudActionBase, type CrudActionEntry, type CrudActionRegistration, type CrudActionZone, CrudFormModal, type CrudHooks, type CrudOperationPermissions, type CrudPermissions, type DataSource, DataTable, DataTableAdvancedToolbar, DataTableColumnHeader, DataTableFacetedFilter, DataTablePagination, DataTableRowAction, DataTableToolbar, DataTableViewOptions, type DateFormatter, type EnumOption, ExportDialog, type ExportDialogProps, type ExportMode, ExtendedColumnFilter, ExtendedColumnSort, type Field, type FieldOption, type FieldType, type Fields, type FilterConfig, FilterOperator, type FilterVariant, type FormSchemaOverrides, ImportDialog, type ImportDialogProps, type ImportResult, type JSONSchema, type JSONSchemaProperty, JoinOperator, type ListParams, type ListResult, type LocaleProp, MultiCombobox, type MultiComboboxOption, type MultiComboboxProps, type MultiComboboxTriggerRenderProps, Option, type ParsedImportData, type ParsedZodField, type Parser, QueryKeys, type RowActionConfig, type RowActionItem, type RowBuiltinActionItem, type RowBuiltinActionType, type RowCustomActionItem, SchemaAdapter, Select, type SelectMode, type SelectOption, type SelectProps, type SelectSearchableDynamicProps, type SelectSearchableMultipleProps, type SelectSearchableProps, type SelectSearchableSingleProps, type SelectSimpleProps, type SelectTriggerRenderProps, type SimpleFieldConfig, type SimpleFieldsConfig, type ToastAdapter, type ToolbarActionConfig, type ToolbarActionItem, type ToolbarBuiltinActionItem, type ToolbarBuiltinActionType, type ToolbarCustomActionItem, type UnifiedField, type UnifiedSchema, type UrlStateOptions, type UseAutoCrudResourceOptions, type UseAutoCrudResourceParams, type UseAutoCrudResourceReturn, cn, coerceRowValues, components, createActionsColumn, createEditFormSchema, createFormSchema, createMemoryDataSource, createSelectColumn, createTRPCDataSource, createTableSchema, crudActions, dataSources, dataToCSV, deDE, downloadCSVTemplate, enUS, esES, exportAllToCSV, exportTableToCSV, formComponents, formatDate, frFR, generateCSVTemplate, getUrlParams, humanize, jaJP, koKR, noopToastAdapter, normalizeDataSourceConfig, normalizeOptions, parseAsArrayOf, parseAsInteger, parseAsString, parseAsStringEnum, parseCSV, parseImportFile, parseJSON, parseZodField, resolveLocale, setDateFormatter, setSearchParams, setToolbarResolver, useAutoCrudResource, useDataTable, useQueryState, useQueryStates, useReadableFilters, useUrlState, useUrlStates, zhCN };
2005
+ export { type ActionConfig, type ActionItem, type ActionsColumnConfig, type AutoCrudActionConfig, type AutoCrudActionsConfig, type AutoCrudDataSourceConfig, type AutoCrudDataSourceContext, type AutoCrudDataSourceEntry, type AutoCrudDataSourceLoader, type AutoCrudDataSourceRegistration, type AutoCrudFormComponentConfig, type AutoCrudLocale, type AutoCrudOption, type AutoCrudQueryCapabilities, type AutoCrudRowActionContext, AutoCrudTable, type AutoCrudTableProps, type AutoCrudToolbarContext, type AutoCrudToolbarResolver, AutoForm, type AutoFormProps, type AutoQueryParams, AutoTable, AutoTableActionBar, AutoTableSimpleFilters, type BatchActionConfig, type BatchActionContext, type BatchActionItem, type BatchBuiltinActionItem, type BatchBuiltinActionType, type BatchCustomActionItem, type BatchUpdateField, type ColumnOverrides, type CreateFormSchemaOptions, type CreateTableSchemaOptions, type CrudActionBase, type CrudActionEntry, type CrudActionRegistration, type CrudActionZone, CrudFormModal, type CrudHooks, type CrudOperationPermissions, type CrudPermissions, type DataSource, DataTable, DataTableAdvancedToolbar, DataTableColumnHeader, DataTableFacetedFilter, DataTablePagination, DataTableRowAction, DataTableToolbar, DataTableViewOptions, type DateFormatPreset, type DateFormatter, type EnumOption, ExportDialog, type ExportDialogProps, type ExportMode, ExtendedColumnFilter, ExtendedColumnSort, type Field, type FieldOption, type FieldTableDisplay, type FieldType, type Fields, type FilterConfig, FilterOperator, type FilterVariant, type FormSchemaOverrides, ImportDialog, type ImportDialogProps, type ImportResult, type JSONSchema, type JSONSchemaProperty, JoinOperator, type ListParams, type ListResult, type LocaleProp, MultiCombobox, type MultiComboboxOption, type MultiComboboxProps, type MultiComboboxTriggerRenderProps, Option, type ParsedImportData, type ParsedZodField, type Parser, QueryKeys, type RowActionConfig, type RowActionItem, type RowBuiltinActionItem, type RowBuiltinActionType, type RowCustomActionItem, SchemaAdapter, Select, type SelectMode, type SelectOption, type SelectProps, type SelectSearchableDynamicProps, type SelectSearchableMultipleProps, type SelectSearchableProps, type SelectSearchableSingleProps, type SelectSimpleProps, type SelectTriggerRenderProps, type SimpleFieldConfig, type SimpleFieldsConfig, type ToastAdapter, type ToolbarActionConfig, type ToolbarActionItem, type ToolbarBuiltinActionItem, type ToolbarBuiltinActionType, type ToolbarCustomActionItem, type UnifiedField, type UnifiedSchema, type UrlStateOptions, type UseAutoCrudResourceOptions, type UseAutoCrudResourceParams, type UseAutoCrudResourceReturn, cn, coerceRowValues, components, createActionsColumn, createEditFormSchema, createFormSchema, createMemoryDataSource, createSelectColumn, createTRPCDataSource, createTableSchema, crudActions, dataSources, dataToCSV, deDE, downloadCSVTemplate, enUS, esES, exportAllToCSV, exportTableToCSV, formComponents, formatDate, frFR, generateCSVTemplate, getUrlParams, humanize, jaJP, koKR, noopToastAdapter, normalizeDataSourceConfig, normalizeOptions, parseAsArrayOf, parseAsInteger, parseAsString, parseAsStringEnum, parseCSV, parseImportFile, parseJSON, parseZodField, resolveLocale, setDateFormatter, setSearchParams, setToolbarResolver, useAutoCrudResource, useDataTable, useQueryState, useQueryStates, useReadableFilters, useUrlState, useUrlStates, zhCN };
package/dist/index.d.ts CHANGED
@@ -808,6 +808,7 @@ interface FieldOption {
808
808
  icon?: React$1.FC<React$1.SVGProps<SVGSVGElement>>;
809
809
  disabled?: boolean;
810
810
  }
811
+ type FieldTableDisplay = 'auto' | 'text' | 'badge' | 'date' | 'datetime';
811
812
  /**
812
813
  * 统一字段配置
813
814
  * 支持共用配置 + 表格/表单特定配置
@@ -838,8 +839,16 @@ interface Field {
838
839
  table?: {
839
840
  /** 是否在表格中隐藏 */
840
841
  hidden?: boolean;
842
+ /** 表格列标签 */
843
+ label?: string;
841
844
  /** 排序权重,数值越小越靠前 */
842
845
  index?: number;
846
+ /** 仅用于表格展示的静态值标签 */
847
+ options?: FieldOption[];
848
+ /** 仅用于表格展示的动态值标签源 */
849
+ dataSource?: AutoCrudDataSourceConfig;
850
+ /** 声明式单元格展示模式;非 auto 模式会覆盖拥有方自定义 cell */
851
+ display?: FieldTableDisplay;
843
852
  /** 筛选器配置 */
844
853
  meta?: Record<string, unknown>;
845
854
  /** 其他列配置 */
@@ -1904,7 +1913,8 @@ declare function createMemoryDataSource<T extends {
1904
1913
  }>(initialData?: T[]): DataSource<T>;
1905
1914
  //#endregion
1906
1915
  //#region src/lib/format.d.ts
1907
- type DateFormatter = (date: Date, options: Intl.DateTimeFormatOptions) => string;
1916
+ type DateFormatPreset = 'date' | 'datetime';
1917
+ type DateFormatter = (date: Date, options: Intl.DateTimeFormatOptions, preset?: DateFormatPreset) => string;
1908
1918
  /**
1909
1919
  * Register the host's process-wide date presentation policy without coupling
1910
1920
  * AutoCrud to locale or timezone selection. The returned cleanup removes only
@@ -1912,7 +1922,7 @@ type DateFormatter = (date: Date, options: Intl.DateTimeFormatOptions) => string
1912
1922
  * Passing undefined clears every registration.
1913
1923
  */
1914
1924
  declare function setDateFormatter(formatter?: DateFormatter): () => void;
1915
- declare function formatDate(date: Date | string | number | undefined, opts?: Intl.DateTimeFormatOptions): string;
1925
+ declare function formatDate(date: Date | string | number | undefined, opts?: Intl.DateTimeFormatOptions, preset?: DateFormatPreset): string;
1916
1926
  //#endregion
1917
1927
  //#region src/lib/humanize.d.ts
1918
1928
  /**
@@ -1992,4 +2002,4 @@ declare function exportAllToCSV<T extends Record<string, unknown>>(data: T[], op
1992
2002
  */
1993
2003
  declare function downloadCSVTemplate(headers: string[], filename?: string): void;
1994
2004
  //#endregion
1995
- export { type ActionConfig, type ActionItem, type ActionsColumnConfig, type AutoCrudActionConfig, type AutoCrudActionsConfig, type AutoCrudDataSourceConfig, type AutoCrudDataSourceContext, type AutoCrudDataSourceEntry, type AutoCrudDataSourceLoader, type AutoCrudDataSourceRegistration, type AutoCrudFormComponentConfig, type AutoCrudLocale, type AutoCrudOption, type AutoCrudQueryCapabilities, type AutoCrudRowActionContext, AutoCrudTable, type AutoCrudTableProps, type AutoCrudToolbarContext, type AutoCrudToolbarResolver, AutoForm, type AutoFormProps, type AutoQueryParams, AutoTable, AutoTableActionBar, AutoTableSimpleFilters, type BatchActionConfig, type BatchActionContext, type BatchActionItem, type BatchBuiltinActionItem, type BatchBuiltinActionType, type BatchCustomActionItem, type BatchUpdateField, type ColumnOverrides, type CreateFormSchemaOptions, type CreateTableSchemaOptions, type CrudActionBase, type CrudActionEntry, type CrudActionRegistration, type CrudActionZone, CrudFormModal, type CrudHooks, type CrudOperationPermissions, type CrudPermissions, type DataSource, DataTable, DataTableAdvancedToolbar, DataTableColumnHeader, DataTableFacetedFilter, DataTablePagination, DataTableRowAction, DataTableToolbar, DataTableViewOptions, type DateFormatter, type EnumOption, ExportDialog, type ExportDialogProps, type ExportMode, ExtendedColumnFilter, ExtendedColumnSort, type Field, type FieldOption, type FieldType, type Fields, type FilterConfig, FilterOperator, type FilterVariant, type FormSchemaOverrides, ImportDialog, type ImportDialogProps, type ImportResult, type JSONSchema, type JSONSchemaProperty, JoinOperator, type ListParams, type ListResult, type LocaleProp, MultiCombobox, type MultiComboboxOption, type MultiComboboxProps, type MultiComboboxTriggerRenderProps, Option, type ParsedImportData, type ParsedZodField, type Parser, QueryKeys, type RowActionConfig, type RowActionItem, type RowBuiltinActionItem, type RowBuiltinActionType, type RowCustomActionItem, SchemaAdapter, Select, type SelectMode, type SelectOption, type SelectProps, type SelectSearchableDynamicProps, type SelectSearchableMultipleProps, type SelectSearchableProps, type SelectSearchableSingleProps, type SelectSimpleProps, type SelectTriggerRenderProps, type SimpleFieldConfig, type SimpleFieldsConfig, type ToastAdapter, type ToolbarActionConfig, type ToolbarActionItem, type ToolbarBuiltinActionItem, type ToolbarBuiltinActionType, type ToolbarCustomActionItem, type UnifiedField, type UnifiedSchema, type UrlStateOptions, type UseAutoCrudResourceOptions, type UseAutoCrudResourceParams, type UseAutoCrudResourceReturn, cn, coerceRowValues, components, createActionsColumn, createEditFormSchema, createFormSchema, createMemoryDataSource, createSelectColumn, createTRPCDataSource, createTableSchema, crudActions, dataSources, dataToCSV, deDE, downloadCSVTemplate, enUS, esES, exportAllToCSV, exportTableToCSV, formComponents, formatDate, frFR, generateCSVTemplate, getUrlParams, humanize, jaJP, koKR, noopToastAdapter, normalizeDataSourceConfig, normalizeOptions, parseAsArrayOf, parseAsInteger, parseAsString, parseAsStringEnum, parseCSV, parseImportFile, parseJSON, parseZodField, resolveLocale, setDateFormatter, setSearchParams, setToolbarResolver, useAutoCrudResource, useDataTable, useQueryState, useQueryStates, useReadableFilters, useUrlState, useUrlStates, zhCN };
2005
+ export { type ActionConfig, type ActionItem, type ActionsColumnConfig, type AutoCrudActionConfig, type AutoCrudActionsConfig, type AutoCrudDataSourceConfig, type AutoCrudDataSourceContext, type AutoCrudDataSourceEntry, type AutoCrudDataSourceLoader, type AutoCrudDataSourceRegistration, type AutoCrudFormComponentConfig, type AutoCrudLocale, type AutoCrudOption, type AutoCrudQueryCapabilities, type AutoCrudRowActionContext, AutoCrudTable, type AutoCrudTableProps, type AutoCrudToolbarContext, type AutoCrudToolbarResolver, AutoForm, type AutoFormProps, type AutoQueryParams, AutoTable, AutoTableActionBar, AutoTableSimpleFilters, type BatchActionConfig, type BatchActionContext, type BatchActionItem, type BatchBuiltinActionItem, type BatchBuiltinActionType, type BatchCustomActionItem, type BatchUpdateField, type ColumnOverrides, type CreateFormSchemaOptions, type CreateTableSchemaOptions, type CrudActionBase, type CrudActionEntry, type CrudActionRegistration, type CrudActionZone, CrudFormModal, type CrudHooks, type CrudOperationPermissions, type CrudPermissions, type DataSource, DataTable, DataTableAdvancedToolbar, DataTableColumnHeader, DataTableFacetedFilter, DataTablePagination, DataTableRowAction, DataTableToolbar, DataTableViewOptions, type DateFormatPreset, type DateFormatter, type EnumOption, ExportDialog, type ExportDialogProps, type ExportMode, ExtendedColumnFilter, ExtendedColumnSort, type Field, type FieldOption, type FieldTableDisplay, type FieldType, type Fields, type FilterConfig, FilterOperator, type FilterVariant, type FormSchemaOverrides, ImportDialog, type ImportDialogProps, type ImportResult, type JSONSchema, type JSONSchemaProperty, JoinOperator, type ListParams, type ListResult, type LocaleProp, MultiCombobox, type MultiComboboxOption, type MultiComboboxProps, type MultiComboboxTriggerRenderProps, Option, type ParsedImportData, type ParsedZodField, type Parser, QueryKeys, type RowActionConfig, type RowActionItem, type RowBuiltinActionItem, type RowBuiltinActionType, type RowCustomActionItem, SchemaAdapter, Select, type SelectMode, type SelectOption, type SelectProps, type SelectSearchableDynamicProps, type SelectSearchableMultipleProps, type SelectSearchableProps, type SelectSearchableSingleProps, type SelectSimpleProps, type SelectTriggerRenderProps, type SimpleFieldConfig, type SimpleFieldsConfig, type ToastAdapter, type ToolbarActionConfig, type ToolbarActionItem, type ToolbarBuiltinActionItem, type ToolbarBuiltinActionType, type ToolbarCustomActionItem, type UnifiedField, type UnifiedSchema, type UrlStateOptions, type UseAutoCrudResourceOptions, type UseAutoCrudResourceParams, type UseAutoCrudResourceReturn, cn, coerceRowValues, components, createActionsColumn, createEditFormSchema, createFormSchema, createMemoryDataSource, createSelectColumn, createTRPCDataSource, createTableSchema, crudActions, dataSources, dataToCSV, deDE, downloadCSVTemplate, enUS, esES, exportAllToCSV, exportTableToCSV, formComponents, formatDate, frFR, generateCSVTemplate, getUrlParams, humanize, jaJP, koKR, noopToastAdapter, normalizeDataSourceConfig, normalizeOptions, parseAsArrayOf, parseAsInteger, parseAsString, parseAsStringEnum, parseCSV, parseImportFile, parseJSON, parseZodField, resolveLocale, setDateFormatter, setSearchParams, setToolbarResolver, useAutoCrudResource, useDataTable, useQueryState, useQueryStates, useReadableFilters, useUrlState, useUrlStates, zhCN };
package/dist/index.js CHANGED
@@ -368,6 +368,21 @@ function useDateFormatterVersion() {
368
368
  function getHostDateFormatter() {
369
369
  return hostDateFormatters[hostDateFormatters.length - 1]?.formatter;
370
370
  }
371
+ const canonicalDatePattern = /^(\d{4})-(\d{2})-(\d{2})$/;
372
+ const canonicalDateTimePrefixPattern = /^(\d{4})-(\d{2})-(\d{2})(?=[T ])/;
373
+ function isValidCalendarDate(year, month, day) {
374
+ if (!year || !month || !day) return false;
375
+ const date = /* @__PURE__ */ new Date(`${year}-${month}-${day}T00:00:00.000Z`);
376
+ return !Number.isNaN(date.getTime()) && date.getUTCFullYear() === Number(year) && date.getUTCMonth() + 1 === Number(month) && date.getUTCDate() === Number(day);
377
+ }
378
+ function isValidCanonicalDate(value) {
379
+ const match = canonicalDatePattern.exec(value);
380
+ return !!match && isValidCalendarDate(match[1], match[2], match[3]);
381
+ }
382
+ function hasValidCanonicalDateTimePrefix(value) {
383
+ const match = canonicalDateTimePrefixPattern.exec(value);
384
+ return !match || isValidCalendarDate(match[1], match[2], match[3]);
385
+ }
371
386
  /**
372
387
  * Register the host's process-wide date presentation policy without coupling
373
388
  * AutoCrud to locale or timezone selection. The returned cleanup removes only
@@ -394,18 +409,45 @@ function setDateFormatter(formatter) {
394
409
  if (hostDateFormatters.length !== previousLength && getHostDateFormatter() !== currentFormatter) notifyDateFormatterChange();
395
410
  };
396
411
  }
397
- function formatDate(date, opts = {}) {
412
+ function formatDate(date, opts = {}, preset) {
398
413
  if (date === void 0) return "";
414
+ if (preset === "date" && typeof date === "string" && canonicalDatePattern.test(date)) return isValidCanonicalDate(date) ? date : "";
415
+ if (preset === "datetime" && typeof date === "string" && !hasValidCanonicalDateTimePrefix(date)) return "";
399
416
  try {
400
417
  const value = new Date(date);
418
+ if (Number.isNaN(value.getTime())) return "";
401
419
  const options = {
402
- month: opts.month ?? "long",
403
- day: opts.day ?? "numeric",
420
+ month: opts.month ?? (preset ? "2-digit" : "long"),
421
+ day: opts.day ?? (preset ? "2-digit" : "numeric"),
404
422
  year: opts.year ?? "numeric",
423
+ ...preset === "datetime" ? {
424
+ hour: opts.hour ?? "2-digit",
425
+ minute: opts.minute ?? "2-digit",
426
+ second: opts.second ?? "2-digit",
427
+ hourCycle: opts.hourCycle ?? "h23"
428
+ } : {},
405
429
  ...opts
406
430
  };
407
431
  const hostDateFormatter = getHostDateFormatter();
408
- return hostDateFormatter ? hostDateFormatter(value, options) : new Intl.DateTimeFormat("en-US", options).format(value);
432
+ if (hostDateFormatter) return preset ? hostDateFormatter(value, options, preset) : hostDateFormatter(value, options);
433
+ if (preset) {
434
+ const parts = new Intl.DateTimeFormat("en-CA", {
435
+ timeZone: opts.timeZone,
436
+ year: "numeric",
437
+ month: "2-digit",
438
+ day: "2-digit",
439
+ ...preset === "datetime" ? {
440
+ hour: "2-digit",
441
+ minute: "2-digit",
442
+ second: "2-digit",
443
+ hourCycle: "h23"
444
+ } : {}
445
+ }).formatToParts(value);
446
+ const read = (type) => parts.find((part) => part.type === type)?.value ?? "";
447
+ const formattedDate = `${read("year")}-${read("month")}-${read("day")}`;
448
+ return preset === "datetime" ? `${formattedDate} ${read("hour")}:${read("minute")}:${read("second")}` : formattedDate;
449
+ }
450
+ return new Intl.DateTimeFormat("en-US", options).format(value);
409
451
  } catch {
410
452
  return "";
411
453
  }
@@ -5941,6 +5983,16 @@ function mergeFields(base, override) {
5941
5983
  const keys = new Set([...Object.keys(base ?? {}), ...Object.keys(override ?? {})]);
5942
5984
  return Object.fromEntries(Array.from(keys).map((key) => [key, mergeFieldConfig(base?.[key], override?.[key])]));
5943
5985
  }
5986
+ function applyResourceTablePresentation(fields, resourceFields) {
5987
+ if (!fields || !resourceFields) return fields;
5988
+ return Object.fromEntries(Object.entries(fields).map(([key, config]) => {
5989
+ const table = resourceFields[key]?.table;
5990
+ return [key, table === void 0 ? config : {
5991
+ ...config,
5992
+ table: mergeFieldPart(config.table, table)
5993
+ }];
5994
+ }));
5995
+ }
5944
5996
  function normalizeFieldOptions(options) {
5945
5997
  if (!options || options.length === 0) return void 0;
5946
5998
  return options.map((option) => ({
@@ -5948,6 +6000,18 @@ function normalizeFieldOptions(options) {
5948
6000
  value: String(option.value)
5949
6001
  }));
5950
6002
  }
6003
+ function getTableConfig(config) {
6004
+ return config.table && typeof config.table === "object" ? config.table : void 0;
6005
+ }
6006
+ function getTableOptions(config) {
6007
+ const table = getTableConfig(config);
6008
+ if (table?.options) return normalizeFieldOptions(table.options);
6009
+ if (table?.dataSource) return void 0;
6010
+ return normalizeFieldOptions(config.enum);
6011
+ }
6012
+ function getTableDataSource(config) {
6013
+ return getTableConfig(config)?.dataSource ?? config.dataSource;
6014
+ }
5951
6015
  function toTableOptions(options) {
5952
6016
  return options?.map(({ label, value, searchText, count, icon }) => ({
5953
6017
  label,
@@ -6023,8 +6087,8 @@ function mergeOptionsByField(left, right) {
6023
6087
  function shouldResolveOptions(field, config, hiddenColumns) {
6024
6088
  if (!config) return false;
6025
6089
  if (hiddenColumns.has(field)) return false;
6026
- if (normalizeFieldOptions(config.enum)) return false;
6027
- return normalizeDataSourceConfig(config.dataSource) !== void 0;
6090
+ if (getTableOptions(config)) return false;
6091
+ return normalizeDataSourceConfig(getTableDataSource(config)) !== void 0;
6028
6092
  }
6029
6093
  function useRegistryVersion(subscribe) {
6030
6094
  const [version$1, setVersion] = React.useState(0);
@@ -6269,7 +6333,7 @@ function useDynamicResolveOptions(fields, rows, hiddenColumns) {
6269
6333
  const hiddenSet = new Set(hiddenColumns);
6270
6334
  return Object.entries(fields).flatMap(([field, config]) => {
6271
6335
  if (!shouldResolveOptions(field, config, hiddenSet)) return [];
6272
- const source = normalizeDataSourceConfig(config.dataSource);
6336
+ const source = normalizeDataSourceConfig(getTableDataSource(config));
6273
6337
  const values = getResolveValues(rows, field);
6274
6338
  return source && values.length > 0 ? [{
6275
6339
  field,
@@ -6366,12 +6430,16 @@ function buildTableOverrides(fields, legacyOverrides, dynamicFilterState, dynami
6366
6430
  const result = { ...legacyOverrides };
6367
6431
  if (fields) for (const [key, config] of Object.entries(fields)) {
6368
6432
  const fieldOptions = normalizeFieldOptions(config.enum);
6433
+ const tableConfig = getTableConfig(config);
6434
+ const presentationOptions = getTableOptions(config);
6369
6435
  const tableOptions = toTableOptions(fieldOptions);
6370
6436
  const filterDataSourceRegistered = dynamicFilterState?.registeredByField[key];
6371
6437
  const filterDataSourceSearchable = dynamicFilterState?.searchableByField[key];
6372
6438
  const currentDynamicOptions = !fieldOptions && filterDataSourceRegistered ? toTableOptions(dynamicFilterState?.optionsByField[key] ?? []) : void 0;
6373
- const mergedDynamicOptions = mergeFieldOptions(!fieldOptions && filterDataSourceRegistered ? dynamicFilterState?.labelOptionsByField[key] ?? dynamicFilterState?.optionsByField[key] ?? [] : void 0, !fieldOptions ? dynamicResolveState?.optionsByField[key] : void 0);
6374
- const dynamicOptions = !fieldOptions && mergedDynamicOptions ? toTableOptions(mergedDynamicOptions) : void 0;
6439
+ const dynamicFilterLabelOptions = !fieldOptions && filterDataSourceRegistered ? dynamicFilterState?.labelOptionsByField[key] ?? dynamicFilterState?.optionsByField[key] ?? [] : void 0;
6440
+ const dynamicResolveOptions = !presentationOptions ? dynamicResolveState?.optionsByField[key] : void 0;
6441
+ const mergedSharedDynamicOptions = mergeFieldOptions(dynamicFilterLabelOptions, tableConfig?.dataSource ? void 0 : dynamicResolveOptions);
6442
+ const dynamicOptions = !fieldOptions && mergedSharedDynamicOptions ? toTableOptions(mergedSharedDynamicOptions) : void 0;
6375
6443
  const tableMeta = config.table !== false && typeof config.table === "object" ? config.table.meta : void 0;
6376
6444
  const fieldEnumMeta = tableOptions ? {
6377
6445
  options: tableOptions,
@@ -6435,11 +6503,24 @@ function buildTableOverrides(fields, legacyOverrides, dynamicFilterState, dynami
6435
6503
  hidden: true
6436
6504
  };
6437
6505
  else if (config.table && typeof config.table === "object") {
6438
- const { meta,...tableProps } = config.table;
6506
+ const { meta, display,...tableProps } = config.table;
6507
+ delete tableProps.options;
6508
+ delete tableProps.dataSource;
6439
6509
  result[key] = {
6440
6510
  ...result[key],
6441
6511
  ...tableProps
6442
6512
  };
6513
+ const hasTableLabels = config.table.options !== void 0 || config.table.dataSource !== void 0;
6514
+ if (display && display !== "auto" || hasTableLabels) {
6515
+ const options = presentationOptions ?? dynamicResolveOptions ?? mergedSharedDynamicOptions;
6516
+ result[key] = {
6517
+ ...result[key],
6518
+ cell: ({ row }) => renderFieldValue(row.getValue(key), "string", {
6519
+ true: "true",
6520
+ false: "false"
6521
+ }, options, display ?? "auto")
6522
+ };
6523
+ }
6443
6524
  }
6444
6525
  }
6445
6526
  return result;
@@ -6487,7 +6568,9 @@ function buildHiddenColumns(fields, legacyHidden, denyFields, legacyOverrides) {
6487
6568
  for (const key of DEFAULT_HIDDEN_AUDIT_FIELDS) if (!legacyHiddenSet.has(key) && !denySet.has(key) && isTableOverrideExplicitlyVisible(legacyOverrides?.[key])) hidden.delete(key);
6488
6569
  if (fields) for (const [key, config] of Object.entries(fields)) {
6489
6570
  if (isDefaultHiddenAuditField(key) && !legacyHiddenSet.has(key) && !denySet.has(key) && isFieldTableExplicitlyVisible(config)) hidden.delete(key);
6490
- if (config.hidden) hidden.add(key);
6571
+ if (config.table && typeof config.table === "object" && config.table.hidden === false) {
6572
+ if (!legacyHiddenSet.has(key) && !denySet.has(key)) hidden.delete(key);
6573
+ } else if (config.hidden) hidden.add(key);
6491
6574
  else if (config.table === false) hidden.add(key);
6492
6575
  else if (config.table && typeof config.table === "object" && config.table.hidden) hidden.add(key);
6493
6576
  }
@@ -6537,12 +6620,32 @@ function buildBatchUpdateFields(schema, batchFields, fields) {
6537
6620
  /**
6538
6621
  * 渲染字段值
6539
6622
  */
6540
- function renderFieldValue(value, type, booleanLocale, options) {
6623
+ function renderFieldValue(value, type, booleanLocale, options, display = "auto") {
6541
6624
  if (value === null || value === void 0) return /* @__PURE__ */ jsx("span", {
6542
6625
  className: "text-muted-foreground",
6543
6626
  children: "-"
6544
6627
  });
6545
- if (options && options.length > 0) {
6628
+ const renderText = (item) => getOptionLabel(item, options);
6629
+ if (display === "text") return Array.isArray(value) ? value.map(renderText).join(", ") : renderText(value);
6630
+ if (display === "date" || display === "datetime") return formatDate(value, {}, display);
6631
+ if (display === "badge" && (!options || options.length === 0)) {
6632
+ if (Array.isArray(value)) return /* @__PURE__ */ jsxs("div", {
6633
+ className: "flex gap-1 flex-wrap",
6634
+ children: [value.slice(0, 5).map((item, index$1) => /* @__PURE__ */ jsx(Badge, {
6635
+ variant: "secondary",
6636
+ children: String(item)
6637
+ }, index$1)), value.length > 5 && /* @__PURE__ */ jsxs(Badge, {
6638
+ variant: "outline",
6639
+ children: ["+", value.length - 5]
6640
+ })]
6641
+ });
6642
+ return /* @__PURE__ */ jsx(Badge, {
6643
+ variant: "outline",
6644
+ className: "capitalize",
6645
+ children: String(value)
6646
+ });
6647
+ }
6648
+ if ((display === "badge" || display === "auto") && options && options.length > 0) {
6546
6649
  if (Array.isArray(value)) return /* @__PURE__ */ jsxs("div", {
6547
6650
  className: "flex gap-1 flex-wrap",
6548
6651
  children: [value.slice(0, 5).map((v, i) => /* @__PURE__ */ jsx(Badge, {
@@ -6734,7 +6837,7 @@ function resolveActions(actionsOrFn, defaults, rowActionsLocale, context) {
6734
6837
  function AutoCrudTable({ id, title, description, schema, resource, fields, table: tableConfig, form: formConfig, permissions, actions: actionItems, toolbar, toolbarActions, locale: localeProp, onCreate }) {
6735
6838
  const locale = resolveLocale(localeProp);
6736
6839
  const resolvedSchema = resource.schema ?? schema;
6737
- const resolvedFields = React.useMemo(() => mergeFields(resource.fields, fields) ?? {}, [fields, resource.fields]);
6840
+ const resolvedFields = React.useMemo(() => applyResourceTablePresentation(mergeFields(resource.fields, fields), resource.fields) ?? {}, [fields, resource.fields]);
6738
6841
  const can = {
6739
6842
  create: permissions?.can?.create ?? true,
6740
6843
  update: permissions?.can?.update ?? true,
@@ -6757,7 +6860,18 @@ function AutoCrudTable({ id, title, description, schema, resource, fields, table
6757
6860
  tableConfig?.overrides
6758
6861
  ]);
6759
6862
  const dynamicResolveOptions = useDynamicResolveOptions(resolvedFields, resource.tableData.data, hiddenColumns);
6760
- const dynamicOptionsByField = React.useMemo(() => mergeOptionsByField(dynamicFilterOptions.labelOptionsByField, dynamicResolveOptions.optionsByField), [dynamicFilterOptions.labelOptionsByField, dynamicResolveOptions.optionsByField]);
6863
+ const sharedResolveOptions = useDynamicResolveOptions(React.useMemo(() => Object.fromEntries(Object.entries(resolvedFields).flatMap(([field, config]) => {
6864
+ const table = getTableConfig(config);
6865
+ if (table?.options === void 0 && table?.dataSource === void 0) return [];
6866
+ const { table: _table,...sharedConfig } = config;
6867
+ return [[field, sharedConfig]];
6868
+ })), [resolvedFields]), resource.tableData.data, hiddenColumns);
6869
+ const viewDynamicOptionsByField = React.useMemo(() => mergeOptionsByField(mergeOptionsByField(dynamicFilterOptions.labelOptionsByField, sharedResolveOptions.optionsByField), Object.fromEntries(Object.entries(dynamicResolveOptions.optionsByField).filter(([field]) => !getTableConfig(resolvedFields[field] ?? {})?.dataSource))), [
6870
+ dynamicFilterOptions.labelOptionsByField,
6871
+ dynamicResolveOptions.optionsByField,
6872
+ sharedResolveOptions.optionsByField,
6873
+ resolvedFields
6874
+ ]);
6761
6875
  const resourceIdKey = resource.idKey ?? "id";
6762
6876
  const actionRegistryVersion = useCrudActionsVersion();
6763
6877
  const unifiedActions = React.useMemo(() => {
@@ -7089,7 +7203,7 @@ function AutoCrudTable({ id, title, description, schema, resource, fields, table
7089
7203
  data: resource.modal.selected,
7090
7204
  schema: resolvedSchema,
7091
7205
  fields: resolvedFields,
7092
- dynamicOptions: dynamicOptionsByField,
7206
+ dynamicOptions: viewDynamicOptionsByField,
7093
7207
  denyFields,
7094
7208
  locale
7095
7209
  }),
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@wordrhyme/auto-crud",
3
3
  "type": "module",
4
- "version": "1.5.4",
4
+ "version": "1.5.5",
5
5
  "description": "Schema-first CRUD components with auto-generated tables and forms",
6
6
  "author": "wordrhyme",
7
7
  "license": "MIT",
@@ -84,9 +84,9 @@
84
84
  "tsdown": "^0.15.12",
85
85
  "typescript": "^5.9.3",
86
86
  "vitest": "^3.2.4",
87
- "@internal/prettier-config": "0.0.1",
88
- "@internal/tsconfig": "0.1.0",
89
87
  "@internal/eslint-config": "0.3.0",
88
+ "@internal/tsconfig": "0.1.0",
89
+ "@internal/prettier-config": "0.0.1",
90
90
  "@internal/tsdown-config": "0.1.0",
91
91
  "@internal/vitest-config": "0.1.0"
92
92
  },