@wordrhyme/auto-crud 1.12.2 → 1.13.0

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/README.md CHANGED
@@ -278,6 +278,8 @@ interface AutoCrudTableProps<TSchema> {
278
278
  form?: {
279
279
  overrides?: Record<string, any>; // 表单覆盖配置
280
280
  columns?: number; // 表单列数
281
+ labelAlign?: 'left' | 'top' | 'right'; // 默认 top;left/right 为左右布局及标签文字对齐
282
+ labelWidth?: number | string; // 左右布局的标签宽度,数字单位为 px
281
283
  };
282
284
  /**
283
285
  * 行操作配置,或统一配置 toolbar/row/batch。
@@ -1509,27 +1511,39 @@ function CustomTable({ data }) {
1509
1511
  ### 使用 AutoForm 组件
1510
1512
 
1511
1513
  ```typescript
1512
- import { AutoForm, createFormSchema } from "@wordrhyme/auto-crud";
1513
- import { createForm } from "@formily/core";
1514
+ import { AutoForm } from "@wordrhyme/auto-crud";
1514
1515
 
1515
1516
  function CustomForm() {
1516
- const form = createForm();
1517
- const formSchema = createFormSchema(taskSchema, {
1518
- exclude: ["id", "createdAt"],
1519
- layout: "grid",
1520
- gridColumns: 2,
1521
- });
1522
-
1523
1517
  return (
1524
1518
  <AutoForm
1525
- form={form}
1526
- schema={formSchema}
1519
+ schema={taskSchema}
1520
+ gridColumns={2}
1527
1521
  onSubmit={(values) => console.log(values)}
1528
1522
  />
1529
1523
  );
1530
1524
  }
1531
1525
  ```
1532
1526
 
1527
+ 详情使用同一条 Formily 渲染链路,通过 `mode="view"` 启用 `readPretty`,无需传入 `onSubmit`,也不会显示提交按钮:
1528
+
1529
+ ```tsx
1530
+ <AutoForm
1531
+ mode="view"
1532
+ schema={taskSchema}
1533
+ initialValues={selectedTask}
1534
+ fields={fields}
1535
+ gridColumns={2}
1536
+ />
1537
+ ```
1538
+
1539
+ 创建、编辑排除 ID 和审计托管字段;详情保留 ID、创建时间和更新时间。`form: false` 和字段级 `form['x-hidden']` 只影响创建、编辑;全局 `hidden` 和 `permissions.deny` 仍限制详情字段。其他表单覆盖配置、布局、scope 和选项标签继续共用。
1540
+
1541
+ 内置字段提供阅读态映射;通过 `components.register()` 注册的自定义组件应使用 Formily `mapReadPretty` 实现自己的阅读态。`AutoCrudTable` 详情仍展示当前选中记录,不会额外请求单条接口。
1542
+
1543
+ 详情默认单列,列数由 `AutoCrudTable` 的 `form.columns` 或 `AutoForm` 的 `gridColumns` 控制,使用浅色标签和宽松间距;长文本、数组和对象默认占满整行,字段 `gridSpan` 可指定跨度。单选标签显示为文字,多选标签使用轻量徽标。长内容自动换行;弹窗最高为视口高度的 85%,超出后正文滚动,标题保持可见。
1544
+
1545
+ `AutoCrudTable` 的 `form.labelAlign` 和 `form.labelWidth` 同时作用于创建、编辑及详情弹窗。例如 `form={{ columns: 1, labelAlign: 'left', labelWidth: 100 }}` 表示单列、左侧标签文字左对齐,标签宽度为 100px。`top` 表示上下布局(默认),`right` 表示左侧标签文字右对齐;`labelWidth` 仅在 `left` / `right` 布局时生效。
1546
+
1533
1547
  ### 使用 useDataTable Hook
1534
1548
 
1535
1549
  ```typescript
package/dist/index.cjs CHANGED
@@ -25,6 +25,8 @@ let __wordrhyme_formily_shadcn = require("@wordrhyme/formily-shadcn");
25
25
  __wordrhyme_formily_shadcn = require_position.__toESM(__wordrhyme_formily_shadcn);
26
26
  let __formily_core = require("@formily/core");
27
27
  __formily_core = require_position.__toESM(__formily_core);
28
+ let __formily_react = require("@formily/react");
29
+ __formily_react = require_position.__toESM(__formily_react);
28
30
  let __formily_reactive = require("@formily/reactive");
29
31
  __formily_reactive = require_position.__toESM(__formily_reactive);
30
32
  let sonner = require("sonner");
@@ -5478,6 +5480,7 @@ function FormModal({ open, onOpenChange, title, description, children, variant =
5478
5480
  open,
5479
5481
  onOpenChange,
5480
5482
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__wordrhyme_ui.SheetContent, {
5483
+ ...!description ? { "aria-describedby": void 0 } : {},
5481
5484
  side,
5482
5485
  className: cn("sm:max-w-lg flex flex-col p-0", className),
5483
5486
  children: [
@@ -5503,6 +5506,7 @@ function FormModal({ open, onOpenChange, title, description, children, variant =
5503
5506
  open,
5504
5507
  onOpenChange,
5505
5508
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__wordrhyme_ui.DialogContent, {
5509
+ ...!description ? { "aria-describedby": void 0 } : {},
5506
5510
  className: cn("max-w-2xl sm:max-w-2xl flex flex-col max-h-[85vh] overflow-hidden p-0", className),
5507
5511
  children: [
5508
5512
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__wordrhyme_ui.DialogHeader, {
@@ -5624,6 +5628,8 @@ function createFormSchema(schema, options) {
5624
5628
  const inferredByName = inferComponentByFieldName(key);
5625
5629
  const component = inferredByName?.component ?? fieldTypeToFormilyComponent[parsed.type];
5626
5630
  const inferredProps = inferredByName?.props ?? {};
5631
+ const componentProps = getComponentProps(parsed.type, parsed);
5632
+ if (parsed.type === "enum" && override?.enum) componentProps.options = override.enum;
5627
5633
  properties[key] = {
5628
5634
  type: mapToJsonSchemaType(parsed.type),
5629
5635
  title: humanize$1(key),
@@ -5632,7 +5638,7 @@ function createFormSchema(schema, options) {
5632
5638
  "x-decorator-props": decoratorProps,
5633
5639
  "x-component": component,
5634
5640
  "x-component-props": {
5635
- ...getComponentProps(parsed.type, parsed),
5641
+ ...componentProps,
5636
5642
  ...inferredProps
5637
5643
  },
5638
5644
  ...parsed.enumValues && { enum: parsed.enumValues.map((v) => ({
@@ -5666,6 +5672,51 @@ function createEditFormSchema(schema, options) {
5666
5672
  });
5667
5673
  }
5668
5674
 
5675
+ //#endregion
5676
+ //#region src/components/auto-crud/read-pretty-field.tsx
5677
+ const ReadPrettyBooleanLocale = (0, react.createContext)(zhCN.boolean);
5678
+ const ReadPrettyRenderers = (0, react.createContext)({});
5679
+ function ReadPrettyField({ options }) {
5680
+ const field = (0, __formily_react.useField)();
5681
+ const schema = (0, __formily_react.useFieldSchema)();
5682
+ const renderCustomValue = (0, react.useContext)(ReadPrettyRenderers)[field.path.toString()];
5683
+ return renderCustomValue ? renderCustomValue() : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReadPrettyValue, {
5684
+ value: field.value,
5685
+ options: options?.length ? options : field.dataSource,
5686
+ date: schema["x-component"] === "DatePicker"
5687
+ });
5688
+ }
5689
+ function ReadPrettyValue({ value, options: choices, date }) {
5690
+ const booleanLocale = (0, react.useContext)(ReadPrettyBooleanLocale);
5691
+ useDateFormatterVersion();
5692
+ const renderValue = (item) => {
5693
+ if (item == null || item === "") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5694
+ className: "text-muted-foreground",
5695
+ children: "-"
5696
+ });
5697
+ const option = choices?.find((choice) => String(choice.value) === String(item));
5698
+ if (option) return Array.isArray(value) ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__wordrhyme_shadcn.Badge, {
5699
+ variant: "secondary",
5700
+ className: "font-normal",
5701
+ children: option.label
5702
+ }) : option.label;
5703
+ if (typeof item === "boolean") return item ? booleanLocale.true : booleanLocale.false;
5704
+ if (item instanceof Date || date) return formatDate(item);
5705
+ if (typeof item === "object") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
5706
+ className: "rounded-md bg-muted/50 p-3 text-sm whitespace-pre-wrap break-words",
5707
+ children: JSON.stringify(item, null, 2)
5708
+ });
5709
+ return String(item);
5710
+ };
5711
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5712
+ className: "min-w-0 whitespace-pre-wrap break-words text-base leading-6 text-foreground",
5713
+ children: Array.isArray(value) ? value.length ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5714
+ className: "flex flex-wrap gap-2",
5715
+ children: value.map((item, index$1) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: renderValue(item) }, index$1))
5716
+ }) : renderValue(null) : renderValue(value)
5717
+ });
5718
+ }
5719
+
5669
5720
  //#endregion
5670
5721
  //#region src/lib/crud-reference-projection.ts
5671
5722
  function isRecord$1(value) {
@@ -5814,12 +5865,8 @@ function normalizeFieldOptions$1(options) {
5814
5865
  * 从统一字段配置生成 Formily overrides。
5815
5866
  * `legacyOverrides` 保持兼容旧 API,字段级 `form` 配置优先级最高。
5816
5867
  */
5817
- function buildFormOverrides(fields, legacyOverrides, denyFields) {
5868
+ function buildFormOverrides(fields, legacyOverrides, denyFields, mode = "edit") {
5818
5869
  const result = { ...legacyOverrides };
5819
- if (denyFields) for (const field of denyFields) result[field] = {
5820
- ...result[field],
5821
- "x-hidden": true
5822
- };
5823
5870
  if (fields) for (const [key, config] of Object.entries(fields)) {
5824
5871
  const fieldOptions = normalizeFieldOptions$1((config.form && typeof config.form === "object" ? config.form : void 0)?.enum) ?? normalizeFieldOptions$1(config.enum);
5825
5872
  const fieldDataSource = fieldOptions || !config.dataSource ? void 0 : normalizeDataSourceConfig(config.dataSource);
@@ -5827,10 +5874,6 @@ function buildFormOverrides(fields, legacyOverrides, denyFields) {
5827
5874
  ...result[key],
5828
5875
  title: config.label
5829
5876
  };
5830
- if (config.hidden) result[key] = {
5831
- ...result[key],
5832
- "x-hidden": true
5833
- };
5834
5877
  if (fieldOptions) result[key] = {
5835
5878
  ...result[key],
5836
5879
  enum: fieldOptions
@@ -5839,15 +5882,27 @@ function buildFormOverrides(fields, legacyOverrides, denyFields) {
5839
5882
  ...result[key],
5840
5883
  "x-data-source": config.dataSource
5841
5884
  };
5842
- if (config.form === false) result[key] = {
5885
+ if (config.form === false && mode !== "view") result[key] = {
5843
5886
  ...result[key],
5844
5887
  "x-hidden": true
5845
5888
  };
5846
- else if (config.form && typeof config.form === "object") result[key] = {
5889
+ else if (config.form && typeof config.form === "object") {
5890
+ const { "x-hidden": formHidden,...sharedFormConfig } = config.form;
5891
+ result[key] = {
5892
+ ...result[key],
5893
+ ...sharedFormConfig,
5894
+ ...mode !== "view" && formHidden !== void 0 ? { "x-hidden": formHidden } : {}
5895
+ };
5896
+ }
5897
+ if (config.hidden) result[key] = {
5847
5898
  ...result[key],
5848
- ...config.form
5899
+ "x-hidden": true
5849
5900
  };
5850
5901
  }
5902
+ for (const field of denyFields ?? []) result[field] = {
5903
+ ...result[field],
5904
+ "x-hidden": true
5905
+ };
5851
5906
  return result;
5852
5907
  }
5853
5908
 
@@ -5910,6 +5965,13 @@ const defaultFieldComponents = {
5910
5965
  decorator: "FormItem"
5911
5966
  }
5912
5967
  };
5968
+ const readPrettyFieldComponents = Object.fromEntries(Object.entries({
5969
+ ...__wordrhyme_formily_shadcn.defaultComponentRegistry,
5970
+ ...defaultFieldComponents
5971
+ }).filter(([, config]) => "decorator" in config).map(([name, config]) => [name, {
5972
+ ...config,
5973
+ component: (0, __wordrhyme_formily_shadcn.connect)(config.component, (0, __formily_react.mapReadPretty)(ReadPrettyField))
5974
+ }]));
5913
5975
  const AUTO_CRUD_DATA_SOURCE_SCOPE_KEY = "$autoCrudDataSource";
5914
5976
  const POPUP_SCROLL_LOAD_THRESHOLD$1 = 24;
5915
5977
  const dataSourceReactionStates = /* @__PURE__ */ new WeakMap();
@@ -5938,7 +6000,8 @@ function mergeReactions(existing, reaction) {
5938
6000
  if (Array.isArray(existing)) return existing.includes(reaction) ? existing : [...existing, reaction];
5939
6001
  return [existing, reaction];
5940
6002
  }
5941
- function applyDataSourceReactions(schema) {
6003
+ function applyDataSourceReactions(schema, readPretty = false) {
6004
+ if (readPretty) schema["x-pattern"] = "readPretty";
5942
6005
  const dataSourceConfig = getSchemaDataSourceConfig(schema);
5943
6006
  if (dataSourceConfig) {
5944
6007
  const reaction = `{{${AUTO_CRUD_DATA_SOURCE_SCOPE_KEY}(${JSON.stringify(dataSourceConfig)})}}`;
@@ -5946,12 +6009,12 @@ function applyDataSourceReactions(schema) {
5946
6009
  }
5947
6010
  const properties = schema.properties;
5948
6011
  if (properties && typeof properties === "object" && !Array.isArray(properties)) {
5949
- for (const property of Object.values(properties)) if (property && typeof property === "object" && !Array.isArray(property)) applyDataSourceReactions(property);
6012
+ for (const property of Object.values(properties)) if (property && typeof property === "object" && !Array.isArray(property)) applyDataSourceReactions(property, readPretty);
5950
6013
  }
5951
6014
  const items = schema.items;
5952
6015
  if (Array.isArray(items)) {
5953
- for (const item of items) if (item && typeof item === "object") applyDataSourceReactions(item);
5954
- } else if (items && typeof items === "object") applyDataSourceReactions(items);
6016
+ for (const item of items) if (item && typeof item === "object") applyDataSourceReactions(item, readPretty);
6017
+ } else if (items && typeof items === "object") applyDataSourceReactions(items, readPretty);
5955
6018
  }
5956
6019
  function getDataSourceReactionState(field) {
5957
6020
  const existing = dataSourceReactionStates.get(field);
@@ -5996,7 +6059,7 @@ function loadAutoCrudDataSource({ entry, field, registryVersion, search, sourceK
5996
6059
  });
5997
6060
  setAutoCrudDataSourceOptions(field, state.options);
5998
6061
  if (!append && state.loadKey === loadKey && state.registryVersion === registryVersion) return;
5999
- if (entry.reset && state.dependencyKey !== void 0 && state.dependencyKey !== dependencyKey) field.setValue(void 0);
6062
+ if (entry.reset && !field.readPretty && state.dependencyKey !== void 0 && state.dependencyKey !== dependencyKey) field.setValue(void 0);
6000
6063
  state.dependencyKey = dependencyKey;
6001
6064
  if (!append) {
6002
6065
  state.loadKey = loadKey;
@@ -6172,7 +6235,10 @@ function createAutoCrudDataSourceReaction(config, registryState) {
6172
6235
  };
6173
6236
  }
6174
6237
  function AutoFormInner({ schema: zodSchema, initialValues, onSubmit, fields, overrides, scope: scopeProp, mode = "create", loading = false, gridColumns = 1, labelAlign = "top", labelWidth, showSubmitButton = true }, ref) {
6175
- const form = (0, react.useMemo)(() => (0, __formily_core.createForm)({ initialValues }), [JSON.stringify(initialValues)]);
6238
+ const form = (0, react.useMemo)(() => (0, __formily_core.createForm)({
6239
+ initialValues,
6240
+ pattern: mode === "view" ? "readPretty" : "editable"
6241
+ }), [JSON.stringify(initialValues), mode]);
6176
6242
  const dataSourceRegistryState = (0, react.useMemo)(() => (0, __formily_reactive.observable)({ version: 0 }), []);
6177
6243
  const updateDataSourceRegistryState = (0, react.useCallback)(() => {
6178
6244
  (0, __formily_reactive.action)(() => {
@@ -6180,40 +6246,75 @@ function AutoFormInner({ schema: zodSchema, initialValues, onSubmit, fields, ove
6180
6246
  });
6181
6247
  }, [dataSourceRegistryState]);
6182
6248
  const dataSourceRegistryVersion = useRegistryVersion$1(dataSources.subscribe, updateDataSourceRegistryState);
6183
- const formOverrides = (0, react.useMemo)(() => buildFormOverrides(fields, overrides), [fields, overrides]);
6249
+ const formOverrides = (0, react.useMemo)(() => buildFormOverrides(fields, overrides, void 0, mode), [
6250
+ fields,
6251
+ overrides,
6252
+ mode
6253
+ ]);
6184
6254
  const formSchema = (0, react.useMemo)(() => {
6185
- const nextSchema = createEditFormSchema(zodSchema, {
6255
+ const nextSchema = (mode === "view" ? createFormSchema : createEditFormSchema)(zodSchema, {
6186
6256
  overrides: formOverrides,
6187
6257
  layout: "grid",
6188
6258
  gridColumns
6189
6259
  });
6190
- applyDataSourceReactions(nextSchema);
6260
+ const grid = typeof nextSchema.properties === "object" ? nextSchema.properties.layout : void 0;
6261
+ if (mode === "view" && grid && typeof grid === "object") {
6262
+ grid["x-component-props"] = {
6263
+ ...grid["x-component-props"],
6264
+ className: (0, __wordrhyme_shadcn.cn)(grid["x-component-props"]?.className, "gap-x-10 gap-y-7 py-2")
6265
+ };
6266
+ const properties = typeof grid.properties === "object" ? grid.properties : {};
6267
+ for (const property of Object.values(properties)) {
6268
+ const decoratorProps = property["x-decorator-props"];
6269
+ const fullWidth = property.type === "object" || property.type === "array" || ["Textarea", "RichTextEditor"].includes(property["x-component"]);
6270
+ property["x-decorator-props"] = {
6271
+ colon: false,
6272
+ ...decoratorProps,
6273
+ className: (0, __wordrhyme_shadcn.cn)(fullWidth && decoratorProps?.gridSpan == null && "col-span-full", decoratorProps?.className)
6274
+ };
6275
+ }
6276
+ }
6277
+ applyDataSourceReactions(nextSchema, mode === "view");
6191
6278
  return nextSchema;
6192
6279
  }, [
6193
6280
  zodSchema,
6194
6281
  formOverrides,
6195
6282
  gridColumns,
6196
- dataSourceRegistryVersion
6283
+ dataSourceRegistryVersion,
6284
+ mode
6197
6285
  ]);
6198
6286
  const formLayout = (0, react.useMemo)(() => {
6199
6287
  const inlineLabel = labelAlign === "left" || labelAlign === "right";
6200
- return {
6201
- labelPlacement: inlineLabel ? "start" : "top",
6202
- itemProps: inlineLabel ? { label: {
6203
- className: labelAlign === "right" ? "text-right" : void 0,
6204
- style: labelWidth != null ? { width: labelWidth } : void 0
6205
- } } : void 0
6206
- };
6207
- }, [labelAlign, labelWidth]);
6288
+ return { itemProps: {
6289
+ container: mode === "view" ? { className: "min-w-0 space-y-0 gap-2" } : void 0,
6290
+ label: {
6291
+ placement: inlineLabel ? "start" : "top",
6292
+ className: (0, __wordrhyme_shadcn.cn)(mode === "view" && "mb-0 text-sm font-normal text-muted-foreground leading-5", labelAlign === "right" && "text-right"),
6293
+ style: inlineLabel && labelWidth != null ? { width: labelWidth } : void 0
6294
+ }
6295
+ } };
6296
+ }, [
6297
+ labelAlign,
6298
+ labelWidth,
6299
+ mode
6300
+ ]);
6208
6301
  const fieldComponents = (0, react.useMemo)(() => ({ fields: {
6209
6302
  ...defaultFieldComponents,
6303
+ ...mode === "view" ? {
6304
+ ...readPrettyFieldComponents,
6305
+ ObjectField: {
6306
+ component: ReadPrettyField,
6307
+ decorator: "FormItem"
6308
+ }
6309
+ } : {},
6210
6310
  ...components.all()
6211
- } }), [useRegistryVersion$1(components.subscribe)]);
6311
+ } }), [useRegistryVersion$1(components.subscribe), mode]);
6212
6312
  const scope = (0, react.useMemo)(() => ({
6213
6313
  ...scopeProp,
6214
6314
  [AUTO_CRUD_DATA_SOURCE_SCOPE_KEY]: (config) => createAutoCrudDataSourceReaction(config, dataSourceRegistryState)
6215
6315
  }), [dataSourceRegistryState, scopeProp]);
6216
6316
  const handleSubmit = async () => {
6317
+ if (mode === "view" || !onSubmit) return;
6217
6318
  await form.validate();
6218
6319
  if (form.valid) await onSubmit(form.values);
6219
6320
  };
@@ -6225,7 +6326,7 @@ function AutoFormInner({ schema: zodSchema, initialValues, onSubmit, fields, ove
6225
6326
  schema: formSchema,
6226
6327
  components: fieldComponents,
6227
6328
  scope
6228
- }), showSubmitButton && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6329
+ }), mode !== "view" && showSubmitButton && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6229
6330
  className: "flex justify-end gap-2 mt-6",
6230
6331
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__wordrhyme_shadcn.Button, {
6231
6332
  type: "button",
@@ -6241,7 +6342,7 @@ const AutoForm = (0, react.forwardRef)(AutoFormInner);
6241
6342
  //#endregion
6242
6343
  //#region src/components/auto-crud/crud-form-modal.tsx
6243
6344
  function CrudFormModal({ open, onOpenChange, mode, schema, initialValues, onSubmit, loading = false, variant = "dialog", overrides, scope, title, locale = zhCN.formModal, gridColumns, labelAlign, labelWidth, className }) {
6244
- const defaultTitle = mode === "create" ? locale.createTitle : locale.editTitle;
6345
+ const defaultTitle = mode === "create" ? locale.createTitle : mode === "edit" ? locale.editTitle : zhCN.viewModal.title;
6245
6346
  const formRef = (0, react.useRef)(null);
6246
6347
  const handleSubmit = async () => {
6247
6348
  await formRef.current?.submit();
@@ -6252,7 +6353,7 @@ function CrudFormModal({ open, onOpenChange, mode, schema, initialValues, onSubm
6252
6353
  title: title ?? defaultTitle,
6253
6354
  variant,
6254
6355
  className,
6255
- footer: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6356
+ footer: mode !== "view" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6256
6357
  className: "flex justify-end gap-2",
6257
6358
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__wordrhyme_shadcn.Button, {
6258
6359
  type: "button",
@@ -6265,14 +6366,16 @@ function CrudFormModal({ open, onOpenChange, mode, schema, initialValues, onSubm
6265
6366
  disabled: loading,
6266
6367
  children: loading ? locale.submitting : mode === "create" ? locale.create : locale.save
6267
6368
  })]
6268
- }),
6369
+ }) : void 0,
6269
6370
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AutoForm, {
6270
6371
  ref: formRef,
6271
6372
  schema,
6272
- mode,
6373
+ ...mode === "view" ? { mode } : {
6374
+ mode,
6375
+ onSubmit
6376
+ },
6273
6377
  initialValues,
6274
6378
  loading,
6275
- onSubmit,
6276
6379
  overrides,
6277
6380
  scope,
6278
6381
  gridColumns,
@@ -7652,7 +7755,7 @@ function ViewModal(props) {
7652
7755
  if (!props.open || !props.data) return null;
7653
7756
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ViewModalContent, { ...props });
7654
7757
  }
7655
- function ViewModalContent({ open, onOpenChange, variant, data, schema, fields: fieldConfig, dynamicOptions, denyFields, locale, tableOverrides, view }) {
7758
+ function ViewModalContent({ open, onOpenChange, variant, data, schema, fields: fieldConfig, dynamicOptions, denyFields, locale, tableOverrides, view, form }) {
7656
7759
  const excluded = react.useMemo(() => {
7657
7760
  const denied = new Set(denyFields ?? []);
7658
7761
  return Object.keys(schema.shape).filter((key) => {
@@ -7667,70 +7770,82 @@ function ViewModalContent({ open, onOpenChange, variant, data, schema, fields: f
7667
7770
  ]);
7668
7771
  const rows = react.useMemo(() => data ? [data] : [], [data]);
7669
7772
  const resolvedOptions = useDynamicResolveOptions(fieldConfig, rows, excluded);
7670
- const content = /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dl", {
7671
- className: "grid gap-4 py-4",
7672
- children: (0, __tanstack_react_table.useReactTable)({
7673
- data: rows,
7674
- columns: react.useMemo(() => {
7675
- const overrides = {};
7676
- for (const [key, fieldSchema] of Object.entries(schema.shape).filter(([fieldKey]) => !excluded.includes(fieldKey))) {
7677
- const config = fieldConfig?.[key] ?? {};
7678
- const tableConfig = getTableConfig(config);
7679
- const listOverride = tableOverrides?.[key];
7680
- const options = getTableOptions(config) ?? resolvedOptions.optionsByField[key] ?? (tableConfig?.dataSource !== void 0 ? void 0 : dynamicOptions?.[key]);
7681
- overrides[key] = {
7682
- label: tableConfig?.label ?? config.label ?? listOverride?.label ?? humanize(key),
7683
- index: tableConfig?.index ?? listOverride?.index,
7684
- cell: ({ getValue, row }) => {
7685
- const value = getValue();
7686
- return config.format?.(value, {
7687
- row: row.original,
7688
- target: "display"
7689
- }) ?? renderFieldValue(value, parseZodField(fieldSchema).type, locale.boolean, options, tableConfig?.display ?? "auto", Infinity);
7690
- },
7691
- ...view?.presentation === "table" ? listOverride : void 0,
7692
- hidden: false,
7693
- ...view?.overrides?.[key]
7694
- };
7695
- }
7696
- return createTableSchema(schema, {
7697
- overrides,
7698
- exclude: excluded
7699
- });
7700
- }, [
7701
- schema,
7702
- excluded,
7703
- fieldConfig,
7704
- tableOverrides,
7705
- view,
7706
- resolvedOptions.optionsByField,
7707
- dynamicOptions,
7708
- locale.boolean
7709
- ]),
7710
- getCoreRowModel: (0, __tanstack_react_table.getCoreRowModel)()
7711
- }).getRowModel().rows[0]?.getAllCells().map((cell) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7712
- className: "grid grid-cols-3 items-start gap-4",
7713
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", {
7714
- className: "text-sm font-medium text-muted-foreground",
7715
- children: cell.column.columnDef.meta?.label ?? humanize(cell.column.id)
7716
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", {
7717
- className: "col-span-2 min-w-0 whitespace-pre-wrap break-words text-sm",
7718
- children: (0, __tanstack_react_table.flexRender)(cell.column.columnDef.cell, cell.getContext())
7719
- })]
7720
- }, cell.id))
7721
- });
7722
- if (variant === "sheet") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__wordrhyme_ui.Sheet, {
7723
- open,
7724
- onOpenChange,
7725
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__wordrhyme_ui.SheetContent, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__wordrhyme_ui.SheetHeader, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__wordrhyme_ui.SheetTitle, { children: locale.viewModal.title }) }), content] })
7773
+ const table = (0, __tanstack_react_table.useReactTable)({
7774
+ data: rows,
7775
+ columns: react.useMemo(() => {
7776
+ const overrides$1 = {};
7777
+ for (const [key, fieldSchema] of Object.entries(schema.shape).filter(([fieldKey]) => !excluded.includes(fieldKey))) {
7778
+ const config = fieldConfig?.[key] ?? {};
7779
+ const tableConfig = getTableConfig(config);
7780
+ const listOverride = tableOverrides?.[key];
7781
+ const options = getTableOptions(config) ?? resolvedOptions.optionsByField[key] ?? (tableConfig?.dataSource !== void 0 ? void 0 : dynamicOptions?.[key]);
7782
+ overrides$1[key] = {
7783
+ label: tableConfig?.label ?? config.label ?? listOverride?.label ?? humanize(key),
7784
+ index: tableConfig?.index ?? listOverride?.index,
7785
+ cell: ({ getValue, row }) => {
7786
+ const value = getValue();
7787
+ return config.format?.(value, {
7788
+ row: row.original,
7789
+ target: "display"
7790
+ }) ?? (tableConfig?.display && tableConfig.display !== "auto" || options?.some((option) => option.badgeTone) ? renderFieldValue(value, parseZodField(fieldSchema).type, locale.boolean, options, tableConfig?.display ?? "auto", Infinity) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReadPrettyValue, {
7791
+ value,
7792
+ options,
7793
+ date: parseZodField(fieldSchema).type === "date"
7794
+ }));
7795
+ },
7796
+ ...view?.presentation === "table" ? listOverride : void 0,
7797
+ hidden: false,
7798
+ ...view?.overrides?.[key]
7799
+ };
7800
+ }
7801
+ return createTableSchema(schema, {
7802
+ overrides: overrides$1,
7803
+ exclude: excluded
7804
+ });
7805
+ }, [
7806
+ schema,
7807
+ excluded,
7808
+ fieldConfig,
7809
+ tableOverrides,
7810
+ view,
7811
+ resolvedOptions.optionsByField,
7812
+ dynamicOptions,
7813
+ locale.boolean
7814
+ ]),
7815
+ getCoreRowModel: (0, __tanstack_react_table.getCoreRowModel)()
7726
7816
  });
7727
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__wordrhyme_ui.Dialog, {
7728
- open,
7729
- onOpenChange,
7730
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__wordrhyme_ui.DialogContent, {
7731
- "aria-describedby": void 0,
7732
- className: "max-w-2xl max-h-[80vh] overflow-y-auto",
7733
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__wordrhyme_ui.DialogHeader, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__wordrhyme_ui.DialogTitle, { children: locale.viewModal.title }) }), content]
7817
+ const detailSchema = react.useMemo(() => schema.omit(Object.fromEntries(excluded.map((key) => [key, true]))), [schema, excluded]);
7818
+ const overrides = buildFormOverrides(fieldConfig, form?.overrides, denyFields, "view");
7819
+ const renderers = {};
7820
+ for (const [index$1, cell] of (table.getRowModel().rows[0]?.getAllCells() ?? []).entries()) {
7821
+ const key = cell.column.id;
7822
+ overrides[key] = {
7823
+ ...overrides[key],
7824
+ title: cell.column.columnDef.meta?.label ?? humanize(key),
7825
+ "x-index": index$1
7826
+ };
7827
+ renderers[key] = () => (0, __tanstack_react_table.flexRender)(cell.column.columnDef.cell, cell.getContext());
7828
+ delete overrides[key]["x-data-source"];
7829
+ }
7830
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReadPrettyBooleanLocale.Provider, {
7831
+ value: locale.boolean,
7832
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReadPrettyRenderers.Provider, {
7833
+ value: renderers,
7834
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CrudFormModal, {
7835
+ open,
7836
+ onOpenChange,
7837
+ mode: "view",
7838
+ variant,
7839
+ schema: detailSchema,
7840
+ initialValues: data ?? void 0,
7841
+ overrides,
7842
+ scope: form?.scope,
7843
+ title: locale.viewModal.title,
7844
+ gridColumns: form?.columns,
7845
+ labelAlign: form?.labelAlign,
7846
+ labelWidth: form?.labelWidth,
7847
+ className: form?.className
7848
+ })
7734
7849
  })
7735
7850
  });
7736
7851
  }
@@ -8235,6 +8350,8 @@ function AutoCrudTable({ id, title, description, schema, resource, fields, table
8235
8350
  scope: formConfig?.scope,
8236
8351
  locale: locale.formModal,
8237
8352
  gridColumns: formConfig?.columns,
8353
+ labelAlign: formConfig?.labelAlign,
8354
+ labelWidth: formConfig?.labelWidth,
8238
8355
  className: formConfig?.className
8239
8356
  }),
8240
8357
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ViewModal, {
@@ -8248,7 +8365,8 @@ function AutoCrudTable({ id, title, description, schema, resource, fields, table
8248
8365
  denyFields,
8249
8366
  locale,
8250
8367
  tableOverrides: detailTableOverrides,
8251
- view: viewConfig
8368
+ view: viewConfig,
8369
+ form: formConfig
8252
8370
  }),
8253
8371
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__wordrhyme_ui.AlertDialog, {
8254
8372
  open: resource.modal.deleteOpen,