cabloy 5.1.61 → 5.1.63

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.
Files changed (230) hide show
  1. package/.claude/skills/cabloy-backend-scaffold/SKILL.md +2 -0
  2. package/.claude/skills/cabloy-backend-scaffold/references/follow-up-checklist.md +1 -1
  3. package/.claude/skills/cabloy-domain-planning/SKILL.md +212 -0
  4. package/.claude/skills/cabloy-frontend-scaffold/SKILL.md +2 -0
  5. package/CHANGELOG.md +55 -0
  6. package/CLAUDE.md +1 -0
  7. package/cabloy-docs/.vitepress/config.mjs +158 -12
  8. package/cabloy-docs/ai/docs-skills-rules-mapping.md +8 -0
  9. package/cabloy-docs/ai/future-skill-roadmap.md +2 -0
  10. package/cabloy-docs/ai/skills.md +1 -0
  11. package/cabloy-docs/backend/backend-contract-emission-output-inspection.md +189 -0
  12. package/cabloy-docs/backend/backend-contract-emission-source-reading-map.md +160 -0
  13. package/cabloy-docs/backend/backend-contract-emission-specimen.md +170 -0
  14. package/cabloy-docs/backend/backend-resource-module-contract-chain.md +323 -0
  15. package/cabloy-docs/backend/backend-source-reading-debug-checklist.md +173 -0
  16. package/cabloy-docs/backend/backend-source-reading-roadmap.md +129 -0
  17. package/cabloy-docs/backend/backend-source-reading-verify-playbook.md +166 -0
  18. package/cabloy-docs/backend/bean-scene-authoring.md +4 -4
  19. package/cabloy-docs/backend/broadcast-guide.md +3 -3
  20. package/cabloy-docs/backend/cli.md +20 -11
  21. package/cabloy-docs/backend/config-guide.md +4 -4
  22. package/cabloy-docs/backend/controller-aop-guide.md +10 -10
  23. package/cabloy-docs/backend/controller-guide.md +12 -2
  24. package/cabloy-docs/backend/crud-workflow.md +7 -3
  25. package/cabloy-docs/backend/dto-guide.md +12 -2
  26. package/cabloy-docs/backend/dto-infer-generation.md +201 -25
  27. package/cabloy-docs/backend/election-guide.md +2 -2
  28. package/cabloy-docs/backend/entity-guide.md +12 -3
  29. package/cabloy-docs/backend/error-guide.md +3 -3
  30. package/cabloy-docs/backend/event-guide.md +4 -4
  31. package/cabloy-docs/backend/external-aop-guide.md +2 -2
  32. package/cabloy-docs/backend/field-indexes.md +9 -3
  33. package/cabloy-docs/backend/foundation.md +8 -8
  34. package/cabloy-docs/backend/i18n-guide.md +6 -6
  35. package/cabloy-docs/backend/internal-aop-guide.md +2 -2
  36. package/cabloy-docs/backend/introduction.md +13 -0
  37. package/cabloy-docs/backend/migration-and-changes.md +3 -3
  38. package/cabloy-docs/backend/model-guide.md +16 -6
  39. package/cabloy-docs/backend/openapi-guide.md +3 -0
  40. package/cabloy-docs/backend/queue-guide.md +3 -3
  41. package/cabloy-docs/backend/redlock-guide.md +2 -2
  42. package/cabloy-docs/backend/schedule-guide.md +2 -2
  43. package/cabloy-docs/backend/scripts.md +8 -0
  44. package/cabloy-docs/backend/serialization-guide.md +2 -2
  45. package/cabloy-docs/backend/service-guide.md +18 -9
  46. package/cabloy-docs/backend/startup-guide.md +5 -5
  47. package/cabloy-docs/backend/status-guide.md +7 -7
  48. package/cabloy-docs/backend/unit-testing.md +3 -3
  49. package/cabloy-docs/backend/vona-source-reading-map.md +157 -0
  50. package/cabloy-docs/backend/websocket-protocol-guide.md +5 -5
  51. package/cabloy-docs/backend/websocket-usage-guide.md +15 -8
  52. package/cabloy-docs/frontend/a-model-under-the-hood.md +281 -0
  53. package/cabloy-docs/frontend/a-openapi-under-the-hood.md +248 -0
  54. package/cabloy-docs/frontend/a-router-guide.md +307 -0
  55. package/cabloy-docs/frontend/api-guide.md +4 -4
  56. package/cabloy-docs/frontend/api-schema-guide.md +1 -0
  57. package/cabloy-docs/frontend/app-startup-guide.md +7 -4
  58. package/cabloy-docs/frontend/bean-scene-authoring.md +1 -1
  59. package/cabloy-docs/frontend/behavior-guide.md +16 -16
  60. package/cabloy-docs/frontend/cli.md +5 -5
  61. package/cabloy-docs/frontend/command-scene-authoring.md +17 -8
  62. package/cabloy-docs/frontend/component-guide.md +5 -5
  63. package/cabloy-docs/frontend/component-props-guide.md +1 -1
  64. package/cabloy-docs/frontend/component-v-model-guide.md +2 -2
  65. package/cabloy-docs/frontend/filter-query-select-data-flow-guide.md +260 -0
  66. package/cabloy-docs/frontend/form-guide.md +27 -30
  67. package/cabloy-docs/frontend/form-scene-to-page-meta-guide.md +303 -0
  68. package/cabloy-docs/frontend/foundation.md +10 -6
  69. package/cabloy-docs/frontend/frontend-source-reading-roadmap.md +249 -0
  70. package/cabloy-docs/frontend/generated-contract-consumption-debug-checklist.md +190 -0
  71. package/cabloy-docs/frontend/generated-contract-consumption-entry-branch.md +205 -0
  72. package/cabloy-docs/frontend/generated-contract-consumption-list-branch.md +157 -0
  73. package/cabloy-docs/frontend/generated-contract-consumption-specimen.md +203 -0
  74. package/cabloy-docs/frontend/generated-contract-consumption-verify-playbook.md +189 -0
  75. package/cabloy-docs/frontend/generic-component-guide.md +1 -1
  76. package/cabloy-docs/frontend/introduction.md +29 -7
  77. package/cabloy-docs/frontend/model-architecture.md +38 -2
  78. package/cabloy-docs/frontend/model-resource-cookbook.md +11 -8
  79. package/cabloy-docs/frontend/model-resource-internals-deep-dive.md +238 -0
  80. package/cabloy-docs/frontend/model-resource-owner-pattern.md +22 -2
  81. package/cabloy-docs/frontend/model-resource-usage-guide.md +22 -6
  82. package/cabloy-docs/frontend/model-state-guide.md +12 -9
  83. package/cabloy-docs/frontend/module-scope.md +8 -8
  84. package/cabloy-docs/frontend/modules-and-suites.md +2 -1
  85. package/cabloy-docs/frontend/navigation-guards-guide.md +7 -0
  86. package/cabloy-docs/frontend/openapi-sdk-guide.md +12 -4
  87. package/cabloy-docs/frontend/page-guide.md +9 -9
  88. package/cabloy-docs/frontend/page-meta-guide.md +466 -0
  89. package/cabloy-docs/frontend/page-params-guide.md +3 -3
  90. package/cabloy-docs/frontend/page-query-guide.md +2 -2
  91. package/cabloy-docs/frontend/page-route-guide.md +6 -0
  92. package/cabloy-docs/frontend/permission-formscene-action-visibility-guide.md +263 -0
  93. package/cabloy-docs/frontend/quickstart.md +14 -2
  94. package/cabloy-docs/frontend/resource-entry-page-deep-dive.md +271 -0
  95. package/cabloy-docs/frontend/resource-list-page-deep-dive.md +279 -0
  96. package/cabloy-docs/frontend/rest-resource-source-reading-map.md +522 -0
  97. package/cabloy-docs/frontend/rest-resource-under-the-hood.md +622 -0
  98. package/cabloy-docs/frontend/root-behaviors-guide.md +282 -0
  99. package/cabloy-docs/frontend/route-alias-guide.md +6 -0
  100. package/cabloy-docs/frontend/router-stack-guide.md +229 -0
  101. package/cabloy-docs/frontend/router-tabs-introduction.md +26 -3
  102. package/cabloy-docs/frontend/router-tabs-layout-integration.md +367 -0
  103. package/cabloy-docs/frontend/router-tabs-mechanism.md +6 -0
  104. package/cabloy-docs/frontend/router-tabs-route-meta-cookbook.md +7 -0
  105. package/cabloy-docs/frontend/router-tabs-vs-stack.md +167 -0
  106. package/cabloy-docs/frontend/router-view-hosts-guide.md +450 -0
  107. package/cabloy-docs/frontend/server-data.md +2 -1
  108. package/cabloy-docs/frontend/system-startup-guide.md +2 -2
  109. package/cabloy-docs/frontend/table-action-visibility-permission-flow-guide.md +263 -0
  110. package/cabloy-docs/frontend/table-cell-cookbook.md +571 -0
  111. package/cabloy-docs/frontend/table-guide.md +373 -0
  112. package/cabloy-docs/frontend/table-resource-crud-cookbook.md +496 -0
  113. package/cabloy-docs/frontend/zova-app-guide.md +251 -0
  114. package/cabloy-docs/frontend/zova-form-source-reading-map.md +7 -9
  115. package/cabloy-docs/frontend/zova-form-under-the-hood.md +5 -0
  116. package/cabloy-docs/frontend/zova-router-under-the-hood.md +561 -0
  117. package/cabloy-docs/frontend/zova-source-reading-map.md +101 -7
  118. package/cabloy-docs/frontend/zova-table-controller-render-supplement.md +225 -0
  119. package/cabloy-docs/frontend/zova-table-source-reading-map.md +317 -0
  120. package/cabloy-docs/frontend/zova-table-under-the-hood.md +532 -0
  121. package/cabloy-docs/fullstack/backend-metadata-to-frontend-table-actions-debug-checklist.md +245 -0
  122. package/cabloy-docs/fullstack/backend-metadata-to-frontend-table-actions-source-reading-map.md +139 -0
  123. package/cabloy-docs/fullstack/backend-metadata-to-frontend-table-actions-verify-playbook.md +248 -0
  124. package/cabloy-docs/fullstack/backend-metadata-to-frontend-table-actions.md +511 -0
  125. package/cabloy-docs/fullstack/contract-loop-playbook.md +8 -2
  126. package/cabloy-docs/fullstack/edition-collaboration-differences.md +6 -0
  127. package/cabloy-docs/fullstack/frontend-metadata-to-backend.md +181 -48
  128. package/cabloy-docs/fullstack/introduction.md +3 -0
  129. package/cabloy-docs/fullstack/openapi-to-sdk.md +116 -2
  130. package/cabloy-docs/fullstack/suites-and-modules.md +333 -0
  131. package/cabloy-docs/fullstack/tutorial-1-first-module.md +3 -0
  132. package/cabloy-docs/fullstack/tutorial-2-first-crud.md +4 -0
  133. package/cabloy-docs/fullstack/tutorial-3-frontend-metadata-sharing.md +4 -0
  134. package/cabloy-docs/fullstack/tutorial-4-custom-level-renderers.md +31 -19
  135. package/cabloy-docs/fullstack/tutorial-5-backend-contract-sharing.md +5 -0
  136. package/cabloy-docs/fullstack/tutorial-6-one-contract-four-uses.md +4 -0
  137. package/cabloy-docs/fullstack/tutorials-overview.md +1 -1
  138. package/cabloy-docs/reference/bean-scene-boilerplates.md +13 -13
  139. package/cabloy-docs/reference/package-map.md +4 -3
  140. package/package.json +1 -1
  141. package/vona/pnpm-lock.yaml +22 -258
  142. package/vona/src/suite/a-training/modules/training-student/package.json +53 -0
  143. package/vona/src/suite/a-training/modules/training-student/src/.metadata/index.ts +400 -0
  144. package/vona/src/suite/a-training/modules/training-student/src/.metadata/locales.ts +18 -0
  145. package/vona/src/suite/a-training/modules/training-student/src/.metadata/this.ts +2 -0
  146. package/vona/src/suite/a-training/modules/training-student/src/bean/meta.index.ts +12 -0
  147. package/vona/src/suite/a-training/modules/training-student/src/bean/meta.version.ts +21 -0
  148. package/vona/src/suite/a-training/modules/training-student/src/bean/ssrMenu.student.ts +29 -0
  149. package/vona/src/suite/a-training/modules/training-student/src/config/locale/en-us.ts +15 -0
  150. package/vona/src/suite/a-training/modules/training-student/src/config/locale/zh-cn.ts +15 -0
  151. package/vona/src/suite/a-training/modules/training-student/src/controller/student.ts +74 -0
  152. package/vona/src/suite/a-training/modules/training-student/src/dto/studentCreate.tsx +28 -0
  153. package/vona/src/suite/a-training/modules/training-student/src/dto/studentSelectReq.tsx +44 -0
  154. package/vona/src/suite/a-training/modules/training-student/src/dto/studentSelectRes.tsx +11 -0
  155. package/vona/src/suite/a-training/modules/training-student/src/dto/studentSelectResItem.tsx +45 -0
  156. package/vona/src/suite/a-training/modules/training-student/src/dto/studentSummary.tsx +42 -0
  157. package/vona/src/suite/a-training/modules/training-student/src/dto/studentUpdate.tsx +28 -0
  158. package/vona/src/suite/a-training/modules/training-student/src/dto/studentView.tsx +25 -0
  159. package/vona/src/suite/a-training/modules/training-student/src/entity/student.tsx +84 -0
  160. package/vona/src/suite/a-training/modules/training-student/src/index.ts +2 -0
  161. package/vona/src/suite/a-training/modules/training-student/src/model/student.ts +10 -0
  162. package/vona/src/suite/a-training/modules/training-student/src/service/student.ts +57 -0
  163. package/vona/src/suite/a-training/modules/training-student/test/student.test.ts +173 -0
  164. package/vona/src/suite/a-training/modules/training-student/tsconfig.build.json +11 -0
  165. package/vona/src/suite/a-training/modules/training-student/tsconfig.json +7 -0
  166. package/vona/src/suite/a-training/package.json +12 -0
  167. package/vona/src/suite/a-training/tsconfig.base.json +4 -0
  168. package/vona/src/suite/a-training/tsconfig.json +10 -0
  169. package/zova/packages-zova/zova/package.json +2 -2
  170. package/zova/pnpm-lock.yaml +423 -682
  171. package/zova/src/suite/a-demo/modules/demo-basic/src/page/toolOne/render.tsx +5 -3
  172. package/zova/src/suite/a-home/modules/home-login/src/page/login/render.tsx +5 -3
  173. package/zova/src/suite/a-training/modules/training-student/cli/openapi.config.ts +9 -0
  174. package/zova/src/suite/a-training/modules/training-student/package.json +57 -0
  175. package/zova/src/suite/a-training/modules/training-student/src/.metadata/component/formFieldLevel.ts +31 -0
  176. package/zova/src/suite/a-training/modules/training-student/src/.metadata/index.ts +258 -0
  177. package/zova/src/suite/a-training/modules/training-student/src/.metadata/locales.ts +7 -0
  178. package/zova/src/suite/a-training/modules/training-student/src/.metadata/this.ts +2 -0
  179. package/zova/src/suite/a-training/modules/training-student/src/api/openapi/baseURL.ts +5 -0
  180. package/zova/src/suite/a-training/modules/training-student/src/api/openapi/index.ts +3 -0
  181. package/zova/src/suite/a-training/modules/training-student/src/api/openapi/schemas.ts +196 -0
  182. package/zova/src/suite/a-training/modules/training-student/src/api/openapi/types.ts +4146 -0
  183. package/zova/src/suite/a-training/modules/training-student/src/api/trainingStudent.ts +151 -0
  184. package/zova/src/suite/a-training/modules/training-student/src/apiSchema/trainingStudent.ts +43 -0
  185. package/zova/src/suite/a-training/modules/training-student/src/bean/tableCell.actionDeleteForce.tsx +53 -0
  186. package/zova/src/suite/a-training/modules/training-student/src/bean/tableCell.actionSummary.tsx +56 -0
  187. package/zova/src/suite/a-training/modules/training-student/src/bean/tableCell.level.tsx +63 -0
  188. package/zova/src/suite/a-training/modules/training-student/src/component/formFieldLevel/controller.tsx +117 -0
  189. package/zova/src/suite/a-training/modules/training-student/src/config/locale/en-us.ts +9 -0
  190. package/zova/src/suite/a-training/modules/training-student/src/config/locale/zh-cn.ts +9 -0
  191. package/zova/src/suite/a-training/modules/training-student/src/index.ts +2 -0
  192. package/zova/src/suite/a-training/modules/training-student/src/model/student.ts +42 -0
  193. package/zova/src/suite/a-training/modules/training-student/tsconfig.build.json +13 -0
  194. package/zova/src/suite/a-training/modules/training-student/tsconfig.json +5 -0
  195. package/zova/src/suite/a-training/package.json +12 -0
  196. package/zova/src/suite/a-training/tsconfig.base.json +4 -0
  197. package/zova/src/suite/a-training/tsconfig.json +4 -0
  198. package/zova/src/suite/cabloy-basic/modules/basic-app/package.json +60 -0
  199. package/zova/src/suite/cabloy-basic/modules/basic-app/src/.metadata/index.ts +137 -0
  200. package/zova/src/suite/cabloy-basic/modules/basic-app/src/.metadata/locales.ts +7 -0
  201. package/zova/src/suite/cabloy-basic/modules/basic-app/src/.metadata/this.ts +2 -0
  202. package/zova/src/suite/cabloy-basic/modules/basic-app/src/bean/behavior.appModal.tsx +260 -0
  203. package/zova/src/suite/cabloy-basic/modules/basic-app/src/config/config.ts +39 -0
  204. package/zova/src/suite/cabloy-basic/modules/basic-app/src/config/locale/en-us.ts +7 -0
  205. package/zova/src/suite/cabloy-basic/modules/basic-app/src/config/locale/zh-cn.ts +7 -0
  206. package/zova/src/suite/cabloy-basic/modules/basic-app/src/index.ts +4 -0
  207. package/zova/src/suite/cabloy-basic/modules/basic-app/src/lib/appModalItem.ts +16 -0
  208. package/zova/src/suite/cabloy-basic/modules/basic-app/src/lib/index.ts +1 -0
  209. package/zova/src/suite/cabloy-basic/modules/basic-app/src/monkey.ts +38 -0
  210. package/zova/src/suite/cabloy-basic/modules/basic-app/src/monkeySys.ts +14 -0
  211. package/zova/src/suite/cabloy-basic/modules/basic-app/src/service/appModal.ts +89 -0
  212. package/zova/src/suite/cabloy-basic/modules/basic-app/src/types/appModal.ts +52 -0
  213. package/zova/src/suite/cabloy-basic/modules/basic-app/src/types/index.ts +1 -0
  214. package/zova/src/suite/cabloy-basic/modules/basic-app/tsconfig.build.json +13 -0
  215. package/zova/src/suite/cabloy-basic/modules/basic-app/tsconfig.json +5 -0
  216. package/zova/src/suite/cabloy-basic/modules/basic-commands/package.json +6 -1
  217. package/zova/src/suite/cabloy-basic/modules/basic-commands/src/.metadata/index.ts +16 -0
  218. package/zova/src/suite/cabloy-basic/modules/basic-commands/src/bean/command.alert.tsx +8 -14
  219. package/zova/src/suite/cabloy-basic/modules/basic-commands/src/bean/command.confirm.tsx +10 -7
  220. package/zova/src/suite/cabloy-basic/modules/basic-commands/src/bean/command.prompt.tsx +30 -0
  221. package/zova/src/suite/cabloy-basic/modules/basic-pageentry/src/component/blockForm/controller.tsx +5 -3
  222. package/zova/src/suite/cabloy-basic/modules/basic-select/src/component/formFieldSelect/controller.tsx +29 -7
  223. package/zova/src/suite/cabloy-basic/modules/basic-select/src/component/select/controller.tsx +34 -11
  224. package/zova/src/suite/cabloy-basic/modules/basic-table/package.json +6 -1
  225. package/zova/src/suite/cabloy-basic/modules/basic-table/src/bean/tableCell.actionDelete.tsx +4 -2
  226. package/zova/src/suite/cabloy-basic/package.json +1 -0
  227. package/zova/src/suite-vendor/a-zova/modules/a-table/package.json +1 -1
  228. package/zova/src/suite-vendor/a-zova/modules/a-table/src/component/table/controller.tsx +3 -3
  229. package/zova/src/suite-vendor/a-zova/modules/a-table/src/lib/tableCell.ts +1 -1
  230. package/zova/src/suite-vendor/a-zova/package.json +2 -2
@@ -0,0 +1,571 @@
1
+ # TableCell Authoring Cookbook
2
+
3
+ This cookbook explains how to author `tableCell` resources in Zova for the most common business table patterns.
4
+
5
+ Use this page when the public table runtime already makes sense to you and your next question is practical:
6
+
7
+ - how should I write a custom `tableCell` bean?
8
+ - when should I use `next()` directly?
9
+ - how should I format values, wrap them with styles, or trigger commands?
10
+ - when should I use the `tableActionRow` boilerplate instead of a normal `tableCell`?
11
+
12
+ Use this page together with:
13
+
14
+ - [Table Guide](/frontend/table-guide)
15
+ - [Table + Resource CRUD Cookbook](/frontend/table-resource-crud-cookbook)
16
+ - [Zova Table Under the Hood](/frontend/zova-table-under-the-hood)
17
+ - [Zova Table Source Reading Map](/frontend/zova-table-source-reading-map)
18
+ - [Bean Scene Authoring](/frontend/bean-scene-authoring)
19
+ - [Bean Scene Boilerplate Variants](/reference/bean-scene-boilerplates)
20
+ - [Tutorial 4: Custom Form/Table Renderers for Level](/fullstack/tutorial-4-custom-level-renderers)
21
+ - [Tutorial 5: Backend Contract Sharing](/fullstack/tutorial-5-backend-contract-sharing)
22
+
23
+ ## What a `tableCell` bean is for
24
+
25
+ A `tableCell` bean is the main reusable extension surface for one table cell in Zova.
26
+
27
+ It is a good fit when:
28
+
29
+ - the same cell behavior should be reused across pages
30
+ - a schema field should point to a named frontend render resource
31
+ - a row action should be represented as one reusable render unit
32
+ - the table runtime should continue owning scope, visibility, and render orchestration
33
+
34
+ It is **not** the first choice when:
35
+
36
+ - the change is only one page-local layout tweak with no reuse value
37
+ - the page should instead add or remove whole columns through `getColumns(...)`
38
+ - the business truth really belongs in backend schema metadata and no custom frontend behavior is needed yet
39
+
40
+ A practical rule is:
41
+
42
+ - use schema metadata first
43
+ - use built-in table-cell resources second
44
+ - add a custom `tableCell` bean only when the UI behavior becomes business-specific or reusable
45
+
46
+ ## The smallest correct mental model
47
+
48
+ If you only remember one idea, remember this one:
49
+
50
+ > A `tableCell` bean does not own the whole table. The table controller owns the table runtime, and your `tableCell` bean receives a prepared render context plus `next()` for the current cell.
51
+
52
+ That is why `tableCell` authoring usually feels small and focused.
53
+
54
+ Your bean normally decides only one of these things:
55
+
56
+ - format the current value
57
+ - wrap the current value with extra markup
58
+ - trigger a command or action
59
+ - orchestrate several nested row actions
60
+
61
+ ## Step 1: Start from the CLI scaffold
62
+
63
+ Inspect the CLI surface first:
64
+
65
+ ```bash
66
+ npm run zova :create:bean --help
67
+ ```
68
+
69
+ Create a normal table-cell bean:
70
+
71
+ ```bash
72
+ npm run zova :create:bean tableCell level -- --module=training-student
73
+ ```
74
+
75
+ Create a row-action variant:
76
+
77
+ ```bash
78
+ npm run zova :create:bean tableCell actionOperationsRow -- --module=training-student --boilerplate=tableActionRow
79
+ ```
80
+
81
+ The default scaffold shape is intentionally small:
82
+
83
+ ```tsx
84
+ @TableCell<ITableCellOptionsLevel>()
85
+ export class TableCellLevel extends BeanBase implements ITableCellRender {
86
+ render(
87
+ _options: ITableCellOptionsLevel,
88
+ _renderContext: IJsxRenderContextTableCell,
89
+ next: NextTableCellRender,
90
+ ) {
91
+ return next();
92
+ }
93
+ }
94
+ ```
95
+
96
+ That is already the correct architectural starting point because:
97
+
98
+ - the bean is registered in the `tableCell` scene
99
+ - the options type is local to your resource identity
100
+ - the render context is already prepared by the table controller
101
+ - `next()` already represents the cell’s current value or downstream content path
102
+
103
+ ## Step 2: Understand the three parameters you receive
104
+
105
+ The `render(...)` method usually receives:
106
+
107
+ - `options` — the final merged options for this cell resource
108
+ - `renderContext` — the current table/cell runtime context
109
+ - `next` — the function that gives you the downstream cell content
110
+
111
+ ### `options`
112
+
113
+ Use `options` for things such as:
114
+
115
+ - `class`
116
+ - formatting presets
117
+ - display items
118
+ - command options
119
+ - action lists for row-action cells
120
+
121
+ These options may come from more than one layer, including:
122
+
123
+ - decorator defaults on `@TableCell(...)`
124
+ - schema `rest.table.columnProps`
125
+ - action metadata options
126
+
127
+ ### `renderContext`
128
+
129
+ Use `renderContext` when you need access to:
130
+
131
+ - `$host` — the host controller utilities such as `$performCommand(...)`
132
+ - `$$table` — the current table controller
133
+ - `cellContext` — the TanStack cell context
134
+ - `$celScope` — the current cell scope
135
+
136
+ ### `next()`
137
+
138
+ Use `next()` when your cell should build on top of the current value rather than replace the whole rendering path.
139
+
140
+ A practical rule is:
141
+
142
+ - if the cell is basically a formatter or wrapper, start from `next()`
143
+ - if the cell is a command button or link, you may ignore `next()` and render your own content
144
+
145
+ ## Pattern 1: Minimal pass-through cell
146
+
147
+ Use this pattern when you only want a reusable placeholder bean or a very thin wrapper.
148
+
149
+ Representative shape:
150
+
151
+ ```tsx
152
+ @TableCell<ITableCellOptionsLevel>()
153
+ export class TableCellLevel extends BeanBase implements ITableCellRender {
154
+ render(
155
+ _options: ITableCellOptionsLevel,
156
+ _renderContext: IJsxRenderContextTableCell,
157
+ next: NextTableCellRender,
158
+ ) {
159
+ return next();
160
+ }
161
+ }
162
+ ```
163
+
164
+ Use this when:
165
+
166
+ - the backend already points to a custom cell resource but the UI behavior is still small
167
+ - you want the resource identity to exist before you add richer logic
168
+ - you want a stable extension point for future customization
169
+
170
+ This is the right starting point for many business-specific fields.
171
+
172
+ ## Pattern 2: Format a value and optionally wrap it with a class
173
+
174
+ Use this pattern when the cell is still “show the field value,” but the value needs formatting.
175
+
176
+ Representative built-in examples are:
177
+
178
+ - `basic-date:date`
179
+ - `basic-currency:currency`
180
+ - `basic-select:select`
181
+ - `basic-text:text`
182
+
183
+ The common pattern is:
184
+
185
+ 1. call `next()`
186
+ 2. transform the value
187
+ 3. if `options.class` exists, wrap the result with a container
188
+
189
+ Representative shape:
190
+
191
+ ```tsx
192
+ @TableCell<ITableCellOptionsCurrency>()
193
+ export class TableCellCurrency extends BeanBase implements ITableCellRender {
194
+ render(
195
+ options: ITableCellOptionsCurrency,
196
+ _renderContext: IJsxRenderContextTableCell,
197
+ next: NextTableCellRender,
198
+ ) {
199
+ const value = currencyFormat(next(), options);
200
+ if (!options.class) return value;
201
+ return <div class={options.class}>{value}</div>;
202
+ }
203
+ }
204
+ ```
205
+
206
+ This is a good fit when:
207
+
208
+ - the field is still conceptually one value
209
+ - formatting logic should be shared across multiple pages
210
+ - schema metadata should be able to point to one stable frontend renderer
211
+
212
+ A practical rule is:
213
+
214
+ - prefer formatting around `next()` for date, currency, enum-label, or badge-like display
215
+ - do not re-fetch row data manually if the current cell value is already enough
216
+
217
+ ## Pattern 3: Map a raw value to an item label
218
+
219
+ Use this pattern when the raw stored value is not the final display value.
220
+
221
+ Representative built-in example:
222
+
223
+ - `basic-select:select`
224
+
225
+ Representative shape:
226
+
227
+ ```tsx
228
+ @TableCell<ITableCellOptionsSelect>({
229
+ itemValue: 'value',
230
+ itemTitle: 'title',
231
+ })
232
+ export class TableCellSelect extends BeanBase implements ITableCellRender {
233
+ render(
234
+ options: ITableCellOptionsSelect,
235
+ _renderContext: IJsxRenderContextTableCell,
236
+ next: NextTableCellRender,
237
+ ) {
238
+ const value = next();
239
+ const item = options.items?.find(
240
+ item => String(item[String(options.itemValue)]) === String(value),
241
+ );
242
+ const value2 = item?.[String(options.itemTitle)];
243
+ if (!options.class) return value2;
244
+ return <div class={options.class}>{value2}</div>;
245
+ }
246
+ }
247
+ ```
248
+
249
+ This is a good fit when:
250
+
251
+ - the stored value is a code, enum, or id-like value
252
+ - the visible table should show a label instead
253
+ - the same mapping should be reused in several places
254
+
255
+ ## Pattern 4: Turn the cell into a command link or button
256
+
257
+ Use this pattern when the cell should trigger a user action.
258
+
259
+ Representative built-in examples are:
260
+
261
+ - `basic-table:actionView`
262
+ - `basic-table:actionDelete`
263
+
264
+ A command-link pattern looks like this:
265
+
266
+ ```tsx
267
+ @TableCell<ITableCellOptionsActionView>({
268
+ class: 'hover:text-blue-500',
269
+ })
270
+ export class TableCellActionView extends BeanBase implements ITableCellRender {
271
+ render(
272
+ options: ITableCellOptionsActionView,
273
+ renderContext: IJsxRenderContextTableCell,
274
+ next: NextTableCellRender,
275
+ ) {
276
+ const { $host } = renderContext;
277
+ const value = next();
278
+ return (
279
+ <a
280
+ class={options.class}
281
+ href="#"
282
+ onClick={async e => {
283
+ e.preventDefault();
284
+ e.stopPropagation();
285
+ await $host.$performCommand('basic-commands:view', options, renderContext);
286
+ }}
287
+ >
288
+ {value}
289
+ </a>
290
+ );
291
+ }
292
+ }
293
+ ```
294
+
295
+ A command-button pattern looks like this:
296
+
297
+ ```tsx
298
+ @TableCell<ITableCellOptionsActionDelete>({
299
+ class: 'btn btn-outline btn-error join-item',
300
+ })
301
+ export class TableCellActionDelete extends BeanBase implements ITableCellRender {
302
+ render(
303
+ options: ITableCellOptionsActionDelete,
304
+ renderContext: IJsxRenderContextTableCell,
305
+ _next: NextTableCellRender,
306
+ ) {
307
+ const { $host } = renderContext;
308
+ return (
309
+ <button
310
+ class={options.class}
311
+ type="button"
312
+ onClick={async () => {
313
+ const confirmed = await $host.$performCommand('basic-commands:confirm', {
314
+ text: this.scope.locale.DeleteConfirm(),
315
+ });
316
+ if (!confirmed) return;
317
+ await $host.$performCommand('basic-commands:delete', options, renderContext);
318
+ }}
319
+ >
320
+ Delete
321
+ </button>
322
+ );
323
+ }
324
+ }
325
+ ```
326
+
327
+ This is a good fit when:
328
+
329
+ - the cell is really an action, not only formatted display
330
+ - the same action should be reusable across resource pages
331
+ - command-scene infrastructure should remain the action execution path
332
+
333
+ A practical rule is:
334
+
335
+ - let the cell resource own the render interaction
336
+ - let the command bean or model layer own the actual action semantics
337
+
338
+ ## Pattern 5: Row-action composition with `tableActionRow`
339
+
340
+ Use this pattern when one cell should show several row actions together.
341
+
342
+ Representative built-in example:
343
+
344
+ - `basic-table:actionOperationsRow`
345
+
346
+ This pattern is different from a normal single-action cell because the bean often needs to:
347
+
348
+ - inspect an `actions` array
349
+ - filter actions by permission
350
+ - preload nested renders
351
+ - render several child action cells in one wrapper
352
+
353
+ That is why the `tableActionRow` boilerplate exists.
354
+
355
+ Representative runtime shape:
356
+
357
+ ```tsx
358
+ @TableCell<ITableCellOptionsActionOperationsRow>({
359
+ class: 'join',
360
+ })
361
+ export class TableCellActionOperationsRow extends BeanBase implements ITableCellRender {
362
+ async checkVisible(
363
+ options: ITableCellOptionsActionOperationsRow,
364
+ renderContext: IJsxRenderContextTableColumn,
365
+ ): Promise<boolean> {
366
+ const { $celScope, $host, $$table } = renderContext;
367
+ const permissions = $celScope.permissions;
368
+ const actions = options.actions;
369
+ if (!actions || actions.length === 0) return false;
370
+ const renders: TypeTableCellRenderComponent[] = [];
371
+ for (const action of actions) {
372
+ const actionName = action.name;
373
+ const actionRender = action.render;
374
+ const permissionHint = action.options?.permission;
375
+ if ($host.$passport.checkPermission(permissions, actionName, permissionHint)) {
376
+ if (!actionRender) throw new Error(`should specify action render: ${actionName}`);
377
+ renders.push(actionRender);
378
+ }
379
+ }
380
+ await $$table.cellRenderPrepare(renders);
381
+ return renders.length > 0;
382
+ }
383
+ }
384
+ ```
385
+
386
+ And the render step usually reuses the table runtime again:
387
+
388
+ ```tsx
389
+ render(options, renderContext) {
390
+ const { $celScope, $host, $$table } = renderContext;
391
+ const permissions = $celScope.permissions;
392
+ const actions = options.actions;
393
+ if (!actions || actions.length === 0) return;
394
+ const domActions = [];
395
+ actions.forEach((action, index) => {
396
+ const actionName = action.name;
397
+ const permissionHint = action.options?.permission;
398
+ if (!$host.$passport.checkPermission(permissions, actionName, permissionHint)) return;
399
+ const options2 = Object.assign({ key: index }, action.options);
400
+ domActions.push($$table.cellRender(action.render!, options2, renderContext));
401
+ });
402
+ return <div class={options.class}>{domActions}</div>;
403
+ }
404
+ ```
405
+
406
+ This is a good fit when:
407
+
408
+ - one Operations column should contain several reusable child actions
409
+ - permission filtering belongs in the table action layer
410
+ - nested action cells should still reuse the same table-cell pipeline
411
+
412
+ A practical rule is:
413
+
414
+ - use the normal scaffold for one render resource
415
+ - use `--boilerplate=tableActionRow` when the cell is really a row-action container
416
+
417
+ ## Pattern 6: Business-specific badge or label cell
418
+
419
+ Use this pattern when a field such as `level`, `status`, or `state` should have module-owned presentation.
420
+
421
+ A typical workflow is:
422
+
423
+ 1. create a module-local table cell bean
424
+ 2. move the presentation logic into that bean
425
+ 3. point backend metadata to the bean resource name
426
+
427
+ Representative CLI command:
428
+
429
+ ```bash
430
+ npm run zova :create:bean tableCell level -- --module=training-student
431
+ ```
432
+
433
+ Representative backend-facing metadata target:
434
+
435
+ ```typescript
436
+ ZovaRender.cell('training-student:level', { items: levelItems });
437
+ ```
438
+
439
+ This is the right fit when:
440
+
441
+ - the field now has business-specific UI meaning
442
+ - built-in renderers are a good start but no longer enough
443
+ - the backend contract should still choose the frontend resource identity
444
+
445
+ For the full walkthrough around `level`, see [Tutorial 4: Custom Form/Table Renderers for Level](/fullstack/tutorial-4-custom-level-renderers).
446
+
447
+ ## Pattern 7: Backend contract to frontend cell handoff
448
+
449
+ Many table-cell workflows are easiest to understand as a contract chain.
450
+
451
+ A common chain is:
452
+
453
+ 1. backend row DTO owns the field metadata or row-action metadata
454
+ 2. frontend `tableCell` bean provides the render resource implementation
455
+ 3. the table controller resolves that render resource at runtime
456
+ 4. the visible table page reuses the same table runtime with no page-local duplication
457
+
458
+ For row actions, the chain often continues further:
459
+
460
+ 1. backend action contract exists
461
+ 2. frontend generated API exists
462
+ 3. frontend model wraps the generated API thinly
463
+ 4. `tableCell.actionXxx.tsx` triggers that action through command or model logic
464
+
465
+ That is why custom table cells often belong to the contract loop rather than to isolated page-only UI work.
466
+
467
+ For the forward-chain row-action example, see [Tutorial 5: Backend Contract Sharing](/fullstack/tutorial-5-backend-contract-sharing).
468
+
469
+ ## How to choose between the common patterns
470
+
471
+ Use this quick rule set:
472
+
473
+ ### Choose a minimal pass-through cell when:
474
+
475
+ - you only need a stable module-owned resource identity
476
+ - you expect richer logic later
477
+
478
+ ### Choose a formatting cell when:
479
+
480
+ - the cell still represents one value
481
+ - the business behavior is formatting or display mapping
482
+
483
+ ### Choose a command cell when:
484
+
485
+ - the cell is an action trigger
486
+ - the action should be reusable and command-oriented
487
+
488
+ ### Choose a row-action cell when:
489
+
490
+ - one column should contain several child actions
491
+ - permission-sensitive action composition is needed
492
+
493
+ ### Stay with schema metadata only when:
494
+
495
+ - built-in render resources already solve the problem
496
+ - no module-owned frontend logic is needed yet
497
+
498
+ ## Common mistakes to avoid
499
+
500
+ ### Mistake 1: Rebuilding row access manually
501
+
502
+ The table controller already prepares `cellContext`, scope, and render context. Use `next()` and `renderContext` first.
503
+
504
+ ### Mistake 2: Using a custom `tableCell` bean for a one-off page-local column shape
505
+
506
+ If the behavior is not reusable, consider `getColumns(...)` or page-local rendering first.
507
+
508
+ ### Mistake 3: Putting business action semantics directly into ad hoc click handlers everywhere
509
+
510
+ Prefer one reusable command or model path, and let the `tableCell` bean stay the render-facing adapter.
511
+
512
+ ### Mistake 4: Using the normal scaffold when the cell is really a row-action container
513
+
514
+ If the cell will host several actions, prefer `--boilerplate=tableActionRow`.
515
+
516
+ ### Mistake 5: Treating a `tableCell` bean like a full table replacement
517
+
518
+ It only owns one cell render resource. The table controller still owns the table runtime.
519
+
520
+ ## A practical authoring order
521
+
522
+ If you want the shortest path to a correct custom table cell, use this order:
523
+
524
+ 1. confirm whether built-in renderers already solve the problem
525
+ 2. confirm whether the truth should live in backend schema or row-action metadata
526
+ 3. generate the correct `tableCell` scaffold
527
+ 4. start from `next()` when the cell is still value-centric
528
+ 5. use `renderContext.$host` when the cell needs commands or host services
529
+ 6. use `tableActionRow` only for multi-action composition
530
+ 7. point backend metadata to the new frontend cell resource when the contract is ready
531
+ 8. continue with [Table + Resource CRUD Cookbook](/frontend/table-resource-crud-cookbook) if the cell belongs inside a standard resource list page
532
+ 9. continue with [Backend Metadata to Frontend Table Actions](/fullstack/backend-metadata-to-frontend-table-actions) if the cell is part of a larger contract-loop action chain
533
+ 10. continue with [Zova Table Under the Hood](/frontend/zova-table-under-the-hood) if you need the controller-level runtime explanation
534
+ 11. continue with [Zova Table Source Reading Map](/frontend/zova-table-source-reading-map) if you need the exact next files to read
535
+
536
+ ## Verification checklist
537
+
538
+ When authoring or documenting a custom `tableCell`, verify in this order:
539
+
540
+ 1. confirm the CLI command shape still exists:
541
+
542
+ ```bash
543
+ npm run zova :create:bean --help
544
+ ```
545
+
546
+ 2. confirm the scene variant still exists before recommending it:
547
+
548
+ ```bash
549
+ npm run zova :create:bean tableCell test -- --module=training-student --boilerplate=tableActionRow
550
+ ```
551
+
552
+ 3. confirm your bean follows the current `ITableCellRender` contract
553
+ 4. confirm the target backend metadata points to the intended resource name
554
+ 5. run the docs build if you changed docs:
555
+
556
+ ```bash
557
+ npm run docs:build
558
+ ```
559
+
560
+ ## Final takeaway
561
+
562
+ A good `tableCell` bean is usually small.
563
+
564
+ It does not replace the table runtime. It plugs into it.
565
+
566
+ That is why the best `tableCell` authoring style is usually:
567
+
568
+ - small bean
569
+ - clear options type
570
+ - minimal render responsibility
571
+ - stable contract-facing resource identity