cabloy 5.1.159 → 5.1.161

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.
@@ -47,7 +47,7 @@ If you only remember one idea, remember this one:
47
47
  That leads to three common authoring surfaces:
48
48
 
49
49
  - `ZTable` — the root table component
50
- - schema `rest.table` metadata — the default source of visibility, order, and render decisions
50
+ - schema metadata — `ZovaRender.column(...)` supplies table-column layout/capability metadata, while shared/table-scene overlays supply effective visibility, order, and render metadata
51
51
  - `tableCell` beans — the main extension surface for reusable cell rendering
52
52
 
53
53
  ## One running example through this guide: Student list page
@@ -123,17 +123,12 @@ A practical reading takeaway is:
123
123
 
124
124
  In the default path, `ZTable` reads table-scene metadata from the row schema.
125
125
 
126
- The key table-facing metadata surface is the schema field `rest.table`, especially values such as:
127
-
128
- - `order`
129
- - `visible`
130
- - `render`
131
- - `columnProps`
126
+ The table sees the **effective table metadata**, not only one literal source object. It combines shared field `rest` metadata with the table-scene `rest.table` overlay, then orders the resulting properties by effective `rest.order`.
132
127
 
133
128
  A simplified mental model is:
134
129
 
135
130
  ```text
136
- schema row -> load table properties -> sort by order -> filter by visible -> build columns -> render cells
131
+ schema row -> merge table metadata -> sort by order -> filter by visible -> build columns -> render cells
137
132
  ```
138
133
 
139
134
  That means the default list-page question becomes less:
@@ -146,6 +141,58 @@ and more:
146
141
 
147
142
  This is why table work often belongs in the broader contract loop when the real source of truth is backend field metadata.
148
143
 
144
+ ## Step 4: Configure physical columns with `ZovaRender.column(...)`
145
+
146
+ `ZovaRender.column(...)` is a shared Cabloy/Zova schema-metadata API available in both Cabloy Basic and Cabloy Start. It returns a schema transformer that writes column options under `rest.table`; it does **not** register a frontend column or render a cell by itself.
147
+
148
+ ```ts
149
+ @Api.field(
150
+ v.title($locale('Name')),
151
+ ZovaRender.column({
152
+ align: 'left',
153
+ width: 240,
154
+ fixed: 'left',
155
+ enableSorting: true,
156
+ sortDescFirst: true,
157
+ }),
158
+ v.string(),
159
+ )
160
+ name: string;
161
+ ```
162
+
163
+ The current options are:
164
+
165
+ | Option | Meaning in the table runtime |
166
+ | --------------- | ------------------------------------------------------------------------------------------------------------------------------ |
167
+ | `order` | Orders this property among the effective table properties. For shared field order, `ZovaRender.order(...)` is usually clearer. |
168
+ | `align` | Applies `left`, `center`, or `right` text alignment to the header and cells. |
169
+ | `width` | Supplies the TanStack column size and current renderers apply it as pixel width and minimum width. |
170
+ | `fixed` | Pins the column to the `left` or `right`; the table runtime calculates pinning and renderers apply sticky offsets. |
171
+ | `enableSorting` | Requests a sortable header, subject to the order-schema capability described below. |
172
+ | `sortDescFirst` | Makes the first header toggle descend; it does not change backend order syntax. |
173
+
174
+ `ZovaRender.column(...)` configures the physical column. `ZovaRender.cell(...)` independently selects the cell renderer and its options. For example, a virtual Operations field can be right-pinned and centered while its content comes from a reusable `tableCell` resource:
175
+
176
+ ```ts
177
+ @Api.field(
178
+ v.title($locale('Operations')),
179
+ ZovaRender.order(1, 'max'),
180
+ ZovaRender.column({ align: 'center', width: 360, fixed: 'right' }),
181
+ ZovaRender.cell('basic-table:actionOperationsRow', { actions }),
182
+ )
183
+ _operationsRow?: unknown;
184
+ ```
185
+
186
+ The `basic-table:*` renderer key in this example is Cabloy Basic-specific. The `ZovaRender.column(...)` contract itself is shared; use the active edition's renderer keys when selecting a cell resource.
187
+
188
+ ### Enable server-backed sorting
189
+
190
+ `enableSorting: true` is necessary but not sufficient. The same field, canonical key, or schema alias must also exist in `schemaOrder`; otherwise the table deliberately creates a non-sortable column.
191
+
192
+ A direct `ZTable` consumer owns and passes `schemaOrder`, controlled `sorting`, and `onSortingChange`. In the standard Cabloy Basic resource-page path, `basic-page:blockTable` passes those values from `basic-page:blockPage`; the page translates one manual table sort into backend `orders` and reloads the resource query. The loaded page is not client-side sorted by TanStack.
193
+
194
+ For relation-order rewriting, see [Existing Resource Field Update](/backend/resource-field-update#filter-and-sort-a-relation-by-its-display-field). For the resource-page bridge, see [Table + Resource CRUD Cookbook](/frontend/table-resource-crud-cookbook).
195
+
149
196
  A practical expression example is a schema-driven cell display that formats the current row value through CEL:
150
197
 
151
198
  ```text
@@ -156,7 +203,7 @@ In the shared table CEL scope, `getValue(name)` reads the current row value and
156
203
 
157
204
  For the schema side of that contract, also see [API Schema Guide](/frontend/api-schema-guide).
158
205
 
159
- ## Step 4: Use built-in or custom `tableCell` render resources
206
+ ## Step 5: Use built-in or custom `tableCell` render resources
160
207
 
161
208
  A column render is usually chosen through schema metadata such as:
162
209
 
@@ -184,7 +231,7 @@ A practical rule is:
184
231
  - use built-in `tableCell` resources first
185
232
  - add a custom `tableCell` bean only when the business UI really needs module-owned rendering behavior
186
233
 
187
- ## Step 5: Create a custom `tableCell` bean
234
+ ## Step 6: Create a custom `tableCell` bean
188
235
 
189
236
  When a built-in renderer is not enough, use the existing CLI-backed scene workflow.
190
237
 
@@ -230,7 +277,7 @@ That scaffold is useful because it starts from the correct Zova scene contract:
230
277
 
231
278
  For the broader scene system behind this decorator, see [Bean Scene Authoring](/frontend/bean-scene-authoring).
232
279
 
233
- ## Step 6: Add page-local custom columns with `getColumns(...)`
280
+ ## Step 7: Add page-local custom columns with `getColumns(...)`
234
281
 
235
282
  When most columns should stay schema-driven but one column should be page-local, use `getColumns(...)`.
236
283
 
@@ -275,7 +322,7 @@ A practical caveat is:
275
322
 
276
323
  So a mixed table does **not** mean bypassing Zova. It means extending the existing runtime through its own hooks.
277
324
 
278
- ## Step 7: Use resource-driven page blocks for CRUD list pages
325
+ ## Step 8: Use resource-driven page blocks for CRUD list pages
279
326
 
280
327
  For standard resource pages, the more common public surface is not direct `ZTable` usage. It is the block-based page runtime:
281
328
 
@@ -299,7 +346,7 @@ This gives you a cohesive CRUD path where:
299
346
 
300
347
  For the resource ownership side of that page shape, see [Model Resource Owner Pattern](/frontend/model-resource-owner-pattern).
301
348
 
302
- ## Step 8: Know when to use `BeanControllerPageTableBase`
349
+ ## Step 9: Know when to use `BeanControllerPageTableBase`
303
350
 
304
351
  If your table belongs directly to a page controller rather than a reusable component controller, the page-oriented base class already exists:
305
352
 
@@ -322,15 +369,19 @@ TanStack Table is still important, but the business-facing runtime is controller
322
369
 
323
370
  For many resource pages, the faster path is to let `rest.table` metadata describe the default columns first.
324
371
 
325
- ### Mistake 3: Bypassing `tableCell` beans for reusable cell behavior
372
+ ### Mistake 3: Treating `ZovaRender.column(...)` as a cell renderer
373
+
374
+ `ZovaRender.column(...)` supplies column layout and sorting metadata. Use `ZovaRender.cell(...)` to name the cell render target, and use a `tableCell` bean when that reusable renderer needs module-owned behavior.
375
+
376
+ ### Mistake 4: Bypassing `tableCell` beans for reusable cell behavior
326
377
 
327
378
  If a renderer should be shared across pages or modules, prefer a `tableCell` resource over repeated page-local callbacks.
328
379
 
329
- ### Mistake 4: Treating `controllerRef` like a generic Vue DOM ref
380
+ ### Mistake 5: Treating `controllerRef` like a generic Vue DOM ref
330
381
 
331
382
  `controllerRef` gives you the table controller instance, not a plain DOM element.
332
383
 
333
- ### Mistake 5: Rebuilding CRUD list wiring manually when `basic-page` already owns it
384
+ ### Mistake 6: Rebuilding CRUD list wiring manually when `basic-page` already owns it
334
385
 
335
386
  For standard resource pages, prefer the existing block/page runtime before hand-building a custom table flow.
336
387
 
@@ -340,12 +391,12 @@ If you want the shortest accurate path to a real business table, use this order:
340
391
 
341
392
  1. make sure the backend row schema is already the right contract truth
342
393
  2. start with a resource page or direct `ZTable`
343
- 3. let schema metadata drive the default columns
344
- 4. reuse built-in `tableCell` renderers first
394
+ 3. let schema metadata drive the default columns; add `ZovaRender.column(...)` only where layout, pinning, or sorting metadata is needed
395
+ 4. use `ZovaRender.cell(...)` and built-in `tableCell` renderers for cell content first
345
396
  5. add page-local `getColumns(...)` only where needed
346
397
  6. create custom `tableCell` beans only where the UI becomes business-specific
347
398
  7. continue with [TableCell Authoring Cookbook](/frontend/table-cell-cookbook) when you want concrete patterns for custom cell beans and row actions
348
- 8. continue with [Table + Resource CRUD Cookbook](/frontend/table-resource-crud-cookbook) when you want the standard resource-page integration path for filter, bulk actions, table, and pager
399
+ 8. continue with [Table + Resource CRUD Cookbook](/frontend/table-resource-crud-cookbook) when you want the standard resource-page integration path for filter, bulk actions, table, pager, and server sorting
349
400
  9. continue with [Zova Table Under the Hood](/frontend/zova-table-under-the-hood) when you want the runtime explanation behind the public authoring surface
350
401
  10. continue with [Zova Table Source Reading Map](/frontend/zova-table-source-reading-map) when you need framework-level source details and targeted file-order guidance
351
402
 
@@ -359,9 +410,11 @@ When documenting or changing a table workflow, verify in this order:
359
410
  npm run zova :create:bean --help
360
411
  ```
361
412
 
362
- 2. confirm the `tableCell` scene metadata still exists in the current `a-table` module
363
- 3. confirm the runtime claims against the current `a-table`, `basic-table`, and `basic-page` source
364
- 4. if you changed docs, build the docs site:
413
+ 2. confirm the `ZovaRender.column(...)` option contract and table-scene metadata merge still match current source
414
+ 3. confirm the `tableCell` scene metadata still exists in the current `a-table` module
415
+ 4. for sortable resource columns, verify header state, fixed-column layout/overflow where relevant, and the backend `orders` request
416
+ 5. confirm the runtime claims against the current `a-table`, `basic-table`, and `basic-page` source
417
+ 6. if you changed docs, build the docs site:
365
418
 
366
419
  ```bash
367
420
  npm run docs:build
@@ -269,6 +269,8 @@ Its bridge role is very clear:
269
269
  - render `ZTable`
270
270
  - pass page data into `data`
271
271
  - pass row schema into `schema`
272
+ - pass the resource order schema into `schemaOrder`
273
+ - pass controlled `sorting` and `onSortingChange`
272
274
  - pass page CEL scope into `tableScope`
273
275
  - capture the table controller through `controllerRef`
274
276
 
@@ -329,10 +331,13 @@ _operationsRow?: unknown;
329
331
 
330
332
  This is the practical reverse-sharing model:
331
333
 
332
- - the backend row contract chooses the table cell resource identity
334
+ - `ZovaRender.column(...)` configures physical-column behavior such as alignment, width, pinning, and sorting eligibility
335
+ - `ZovaRender.cell(...)` chooses the table-cell resource identity and its options
333
336
  - the frontend table runtime resolves that resource
334
337
  - the page block and table block do not need page-local hard-coded row-action wiring
335
338
 
339
+ For example, an Operations field can add `ZovaRender.column({ align: 'center', width: 360, fixed: 'right' })` beside the existing `ZovaRender.cell(...)`. `ZovaRender.column(...)` is a shared Basic/Start capability; the `basic-table:*` cell key remains a Cabloy Basic choice.
340
+
336
341
  For the built-in metadata-sharing teaching path, see [Tutorial 3: Frontend Metadata Sharing](/fullstack/tutorial-3-frontend-metadata-sharing).
337
342
 
338
343
  For the forward-chain row-action teaching path, see [Tutorial 5: Backend Contract Sharing](/fullstack/tutorial-5-backend-contract-sharing).
@@ -346,10 +351,24 @@ For standard CRUD list pages, most customization should happen in one of these p
346
351
  Use this when:
347
352
 
348
353
  - a field should change order
354
+ - a column should change alignment, width, or left/right pinning
355
+ - a server-authorized field should expose a sortable header
349
356
  - a column should be visible or hidden
350
357
  - a field should use a different built-in or custom `tableCell`
351
358
 
352
- This is usually the first and best extension point.
359
+ This is usually the first and best extension point. Use `ZovaRender.column(...)` for the column metadata and `ZovaRender.cell(...)` for the cell renderer; do not treat either helper as a replacement for the other.
360
+
361
+ ### Enable standard resource-page sorting
362
+
363
+ For a normal Cabloy Basic resource list, backend field metadata can request a sortable column with:
364
+
365
+ ```ts
366
+ ZovaRender.column({ enableSorting: true });
367
+ ```
368
+
369
+ The field must also be present in the resource `schemaOrder`; that order schema remains the authority for whether the header is actually sortable. `blockTable` forwards the order schema plus controlled sorting state into `ZTable`, and `blockPage` turns one header selection into backend `orders` before reloading the resource query. This is server sorting, not local reordering of the current page.
370
+
371
+ For the full shared `ZovaRender.column(...)` contract, see [Table Guide](/frontend/table-guide#configure-physical-columns-with-zovarendercolumn). For relation-backed order rewriting, see [Existing Resource Field Update](/backend/resource-field-update#filter-and-sort-a-relation-by-its-display-field).
353
372
 
354
373
  ### Extension point B: backend block composition metadata
355
374
 
@@ -505,8 +524,8 @@ If you want the shortest path to a real CRUD list page, use this order:
505
524
  1. generate or confirm the backend CRUD contract thread
506
525
  2. confirm the resource-owner model already exposes the required schemas and permissions
507
526
  3. keep the standard `blockPage -> blockFilter -> blockToolbarBulk -> blockTable -> blockPager` chain
508
- 4. refine backend row metadata for visible columns and row actions
509
- 5. reuse built-in `tableCell` resources first
527
+ 4. refine backend row metadata for visible columns, layout/pinning/sort capability through `ZovaRender.column(...)`, and row actions
528
+ 5. reuse `ZovaRender.cell(...)` with built-in `tableCell` resources first
510
529
  6. add custom `tableCell` resources only where the UI becomes business-specific
511
530
  7. change block controllers only when the existing runtime is structurally insufficient
512
531
  8. continue with [Table Guide](/frontend/table-guide) for the public `ZTable` surface
@@ -533,6 +552,8 @@ When authoring or documenting a resource CRUD list page, verify in this order:
533
552
  - filter works
534
553
  - bulk actions render correctly
535
554
  - table columns and row actions match metadata
555
+ - fixed columns, widths, and overflow behave as intended when configured
556
+ - sortable headers expose the expected state and reload with the intended backend `orders`
536
557
  - pager updates the list correctly
537
558
  7. if you changed docs, build the docs site:
538
559
 
@@ -120,7 +120,8 @@ Use this path when you are asking questions like:
120
120
  Use this path when you are asking questions like:
121
121
 
122
122
  - how does a row schema become visible columns?
123
- - where do `visible`, `order`, `render`, and `columnProps` come from?
123
+ - where do `order`, `align`, `width`, `fixed`, `enableSorting`, and `sortDescFirst` come from?
124
+ - where do `visible`, `render`, and `columnProps` come from?
124
125
  - how does the table scene differ from form or filter scenes?
125
126
 
126
127
  ### Read the docs first
@@ -130,17 +131,51 @@ Use this path when you are asking questions like:
130
131
 
131
132
  ### Then read source in this order
132
133
 
134
+ 1. `zova/packages-cli/cli-set-front/cli/templates/rest/rest.ts`
135
+ 2. `zova/src/suite-vendor/a-zova/modules/a-openapi/src/types/rest.ts`
136
+ 3. `zova/src/suite-vendor/a-zova/modules/a-openapi/src/lib/schema.ts`
137
+ 4. `zova/src/suite-vendor/a-zova/modules/a-table/src/component/table/controller.tsx`
138
+ 5. `zova/src/suite-vendor/a-zova/modules/a-table/src/component/table/render.tsx`
139
+
140
+ ### What each file clarifies
141
+
142
+ - `cli/templates/rest/rest.ts` shows that `ZovaRender.column(...)` writes `ITableColumnOptions` under `rest.table`
143
+ - `types/rest.ts` defines `order`, `align`, `width`, `fixed`, `enableSorting`, and `sortDescFirst`
144
+ - `schema.ts` shows `loadSchemaProperties(...)`, `$ref` resolution, and the merge of shared `rest` metadata with table-scene overlays
145
+ - `controller.tsx` shows `_createProperties()`, `_createTableMeta()`, `_createColumnsMiddle()`, order-schema sorting eligibility, and pinning
146
+ - `render.tsx` shows how effective metadata becomes alignment, dimensions, fixed-column styles, and sortable header DOM
147
+
148
+ ## 4. Column metadata, sortable headers, and resource orders
149
+
150
+ Use this path when you are asking questions like:
151
+
152
+ - why did `ZovaRender.column({ enableSorting: true })` not make a header sortable?
153
+ - how do fixed columns become sticky left/right columns?
154
+ - how does one header click become a resource request `orders` value?
155
+
156
+ ### Read the docs first
157
+
158
+ - [Table Guide](/frontend/table-guide)
159
+ - [Table + Resource CRUD Cookbook](/frontend/table-resource-crud-cookbook)
160
+ - [Existing Resource Field Update](/backend/resource-field-update#filter-and-sort-a-relation-by-its-display-field)
161
+
162
+ ### Then read source in this order
163
+
133
164
  1. `zova/src/suite-vendor/a-zova/modules/a-table/src/component/table/controller.tsx`
134
- 2. `zova/src/suite-vendor/a-zova/modules/a-openapi/src/lib/schema.ts`
135
- 3. `zova/src/suite-vendor/a-zova/modules/a-openapi/src/types/rest.ts`
165
+ 2. `zova/src/suite-vendor/a-zova/modules/a-table/src/component/table/render.tsx`
166
+ 3. `zova/src/suite/cabloy-basic/modules/basic-page/src/component/blockTable/controller.tsx`
167
+ 4. `zova/src/suite/cabloy-basic/modules/basic-page/src/component/blockPage/controller.tsx`
136
168
 
137
169
  ### What each file clarifies
138
170
 
139
- - `controller.tsx` shows `_createProperties()`, `_createTableMeta()`, and `_createColumnsMiddle()`
140
- - `schema.ts` shows `loadSchemaProperties(...)`, `$ref` resolution, and scene-specific `rest` overlays
141
- - `rest.ts` shows the schema extension contract, including `rest.table` and table-related render types
171
+ - `controller.tsx` shows that `enableSorting` also requires the property key or aliases to exist in `schemaOrder`, and shows left/right pinning construction
172
+ - `render.tsx` shows the header toggle, `aria-sort`, indicators, and sticky styles
173
+ - `blockTable/controller.tsx` shows the handoff of `schemaOrder`, controlled sorting, and `onSortingChange`
174
+ - `blockPage/controller.tsx` shows conversion of the single current sort into backend `orders` and query reload
175
+
176
+ The result is a controlled, manual server-sorting path: the standard resource page does not client-side sort rows that have already been fetched. The `basic-page` files are Cabloy Basic specimens; the metadata API and base Zova Table behavior are shared with Cabloy Start, whose page module paths must be resolved in that edition.
142
177
 
143
- ## 4. `tableCell` bean-scene contract and decorator surface
178
+ ## 5. `tableCell` bean-scene contract and decorator surface
144
179
 
145
180
  Use this path when you are asking questions like:
146
181
 
@@ -168,7 +203,7 @@ Use this path when you are asking questions like:
168
203
  - `package.json` shows `zovaModule.onions.tableCell` and boilerplate metadata
169
204
  - the boilerplate files show the intended scaffold shape for normal cells and row-action cells
170
205
 
171
- ## 5. Cell render pipeline and CEL/JSX scope
206
+ ## 6. Cell render pipeline and CEL/JSX scope
172
207
 
173
208
  Use this path when you are asking questions like:
174
209
 
@@ -193,7 +228,7 @@ Use this path when you are asking questions like:
193
228
  - `tableColumn.ts` shows column scope, cell scope, and table column render types
194
229
  - `tableCell.ts` shows the render-context contract received by a `tableCell` bean
195
230
 
196
- ## 6. Representative built-in cell renderers
231
+ ## 7. Representative built-in cell renderers
197
232
 
198
233
  Use this path when you are asking questions like:
199
234
 
@@ -220,7 +255,7 @@ Use this path when you are asking questions like:
220
255
  - `tableCell.select.tsx` shows value-to-item mapping
221
256
  - `actionOperationsRow.tsx` shows advanced visibility checks, nested action rendering, and permission-aware orchestration
222
257
 
223
- ## 7. Custom columns through `getColumns(...)`
258
+ ## 8. Custom columns through `getColumns(...)`
224
259
 
225
260
  Use this path when you are asking questions like:
226
261
 
@@ -243,15 +278,15 @@ Use this path when you are asking questions like:
243
278
 
244
279
  - `types/table.ts` shows `TypeTableGetColumns` and `TypeTableCreateColumnRender`
245
280
  - `controller.tsx` shows how custom columns are given `next(...)`, `createColumnRender(...)`, and the table controller itself
246
- - `basic-table/render.tsx` shows a module-level consumer wrapping `ZTable` rather than replacing its runtime
281
+ - `basic-table/render.tsx` is a Cabloy Basic forwarding wrapper around `ZTable`; it preserves the custom-column hook rather than serving as an executable custom-column specimen
247
282
 
248
- ## 8. Resource-page integration
283
+ ## 9. Resource-page integration
249
284
 
250
285
  Use this path when you are asking questions like:
251
286
 
252
287
  - how does a resource list page feed schema and data into `ZTable`?
253
- - where do `data`, `schemaRow`, and `tableScope` come from?
254
- - where should permission-sensitive table refresh be debugged?
288
+ - where do `data`, `schemaRow`, `schemaOrder`, and `tableScope` come from?
289
+ - where should permission-sensitive table refresh or resource sorting be debugged?
255
290
 
256
291
  ### Read the docs first
257
292
 
@@ -266,11 +301,11 @@ Use this path when you are asking questions like:
266
301
 
267
302
  ### What each file clarifies
268
303
 
269
- - `blockPage/controller.tsx` shows resource ownership, query state, page CEL scope, permissions, and table refresh integration
270
- - `blockTable/controller.tsx` shows the direct bridge from page block to `ZTable`
304
+ - `blockPage/controller.tsx` shows resource ownership, query state, page CEL scope, permissions, table refresh integration, and `orders` mapping
305
+ - `blockTable/controller.tsx` shows the direct bridge from page block to `ZTable`, including order schema and controlled sorting props
271
306
  - the Vona DTO specimen shows the block-based page composition consumed from backend-owned metadata
272
307
 
273
- ## 9. Representative specimens to read before editing the framework
308
+ ## 10. Representative specimens to read before editing the framework
274
309
 
275
310
  Use this section when you want one small example before reading framework internals.
276
311
 
@@ -288,7 +323,7 @@ Use this section when you want one small example before reading framework intern
288
323
 
289
324
  Together they give you the public integration, the advanced cell path, and the minimal cell path before you descend into the whole table runtime.
290
325
 
291
- ## 10. A compact reading strategy
326
+ ## 11. A compact reading strategy
292
327
 
293
328
  When in doubt, use this order:
294
329
 
@@ -301,7 +336,7 @@ When in doubt, use this order:
301
336
 
302
337
  That order usually gets you to the answer faster than starting from the deepest runtime files first.
303
338
 
304
- ## 11. Final takeaway
339
+ ## 12. Final takeaway
305
340
 
306
341
  The fastest way to read Zova Table accurately is not to memorize every file in `a-table`.
307
342
 
@@ -58,13 +58,14 @@ This page is that bridge.
58
58
 
59
59
  For a typical Zova table, the shortest accurate model is:
60
60
 
61
- 1. the `ZTable` wrapper creates a table controller bean through the normal Zova controller path
62
- 2. the table controller loads table-scene schema properties from the row schema
63
- 3. the controller builds table metadata with visible properties and per-column render functions
64
- 4. the controller creates TanStack table options through Zova’s `$useTable(...)` wrapper
65
- 5. each cell render resolves either to text fallback, a general JSX render target, or a `tableCell` bean
66
- 6. the cell runtime evaluates JSX/CEL props with table-aware column and cell scope
67
- 7. resource pages feed schema, data, permissions, and page scope into the same table runtime through `basic-page:blockTable`
61
+ 1. backend schema helpers such as `ZovaRender.column(...)` attach table-column metadata to the field contract
62
+ 2. the `ZTable` wrapper creates a table controller bean through the normal Zova controller path
63
+ 3. the table controller loads effective table-scene schema properties from the row schema
64
+ 4. the controller builds table metadata with visible properties and per-column render functions
65
+ 5. the controller creates TanStack table options through Zova’s `$useTable(...)` wrapper
66
+ 6. each cell render resolves either to text fallback, a general JSX render target, or a `tableCell` bean
67
+ 7. the cell runtime evaluates JSX/CEL props with table-aware column and cell scope
68
+ 8. resource pages feed schema, data, permissions, and page scope into the same table runtime through `basic-page:blockTable`
68
69
 
69
70
  That is why Zova Table is not only a thin wrapper around TanStack Table. The business-facing runtime surface is still Zova-native.
70
71
 
@@ -98,25 +99,27 @@ These three files already show the core architecture:
98
99
 
99
100
  When you want to trace the full mechanism, read these files in order:
100
101
 
101
- 1. `zova/src/suite-vendor/a-zova/modules/a-table/src/.metadata/component/table.ts`
102
- 2. `zova/src/suite-vendor/a-zova/modules/a-table/src/component/table/controller.tsx`
103
- 3. `zova/src/suite-vendor/a-zova/modules/a-table/src/lib/beanControllerTableBase.ts`
104
- 4. `zova/src/suite-vendor/a-zova/modules/a-table/src/component/table/render.tsx`
105
- 5. `zova/src/suite-vendor/a-zova/modules/a-openapi/src/lib/schema.ts`
106
- 6. `zova/src/suite-vendor/a-zova/modules/a-table/src/types/tableCell.ts`
107
- 7. `zova/src/suite/cabloy-basic/modules/basic-page/src/component/blockTable/controller.tsx`
108
- 8. `zova/src/suite/cabloy-basic/modules/basic-page/src/component/blockPage/controller.tsx`
102
+ 1. `zova/packages-cli/cli-set-front/cli/templates/rest/rest.ts`
103
+ 2. `zova/src/suite-vendor/a-zova/modules/a-table/src/.metadata/component/table.ts`
104
+ 3. `zova/src/suite-vendor/a-zova/modules/a-table/src/component/table/controller.tsx`
105
+ 4. `zova/src/suite-vendor/a-zova/modules/a-table/src/lib/beanControllerTableBase.ts`
106
+ 5. `zova/src/suite-vendor/a-zova/modules/a-table/src/component/table/render.tsx`
107
+ 6. `zova/src/suite-vendor/a-zova/modules/a-openapi/src/lib/schema.ts`
108
+ 7. `zova/src/suite-vendor/a-zova/modules/a-table/src/types/tableCell.ts`
109
+ 8. `zova/src/suite/cabloy-basic/modules/basic-page/src/component/blockTable/controller.tsx`
110
+ 9. `zova/src/suite/cabloy-basic/modules/basic-page/src/component/blockPage/controller.tsx`
109
111
 
110
112
  A compact role map is:
111
113
 
114
+ - `cli/templates/rest/rest.ts` shows that `ZovaRender.column(...)` stores its options under `rest.table`
112
115
  - `table.ts` shows how the public wrapper enters `useController(...)`
113
- - `component/table/controller.tsx` owns schema properties, metadata refresh, TanStack bridge, and cell rendering
116
+ - `component/table/controller.tsx` owns schema properties, metadata refresh, TanStack bridge, pinning, sorting eligibility, and cell rendering
114
117
  - `beanControllerTableBase.ts` shows the Zova wrapper around `useVueTable(...)`
115
- - `component/table/render.tsx` shows the default table DOM render path through `FlexRender`
116
- - `schema.ts` shows how table-scene schema properties are loaded and ordered
118
+ - `component/table/render.tsx` shows the default table DOM render path, layout metadata, sortable headers, and `FlexRender`
119
+ - `schema.ts` shows how top-level and table-scene metadata are merged and ordered
117
120
  - `types/tableCell.ts` shows the `tableCell` scene contract
118
- - `blockTable/controller.tsx` shows how page blocks feed `data`, `schema`, and `tableScope` into `ZTable`
119
- - `blockPage/controller.tsx` shows where resource data, permissions, and page scope come from
121
+ - `blockTable/controller.tsx` shows how Basic page blocks feed data, schemas, scope, and sorting state into `ZTable`
122
+ - `blockPage/controller.tsx` shows where resource data, permissions, page scope, and backend `orders` mapping come from
120
123
 
121
124
  ## Step-by-step runtime path
122
125
 
@@ -205,16 +208,19 @@ zova/src/suite-vendor/a-zova/modules/a-openapi/src/lib/schema.ts
205
208
 
206
209
  The important runtime path is:
207
210
 
208
- 1. the table receives `schema`
209
- 2. `_createProperties()` computes `this.$sdk.loadSchemaProperties(this.schema, 'table')`
210
- 3. `loadSchemaProperties(...)` resolves `$ref`, applies `rest.table` overlays, and sorts by `rest.order`
211
- 4. `_createTableMeta()` iterates those properties and decides visibility and render behavior
212
- 5. `_createColumnsMiddle()` converts the surviving properties into TanStack column definitions
211
+ 1. `ZovaRender.column(options)` stores the field's column options under `rest.table`
212
+ 2. the table receives `schema`
213
+ 3. `_createProperties()` computes `this.$sdk.loadSchemaProperties(this.schema, 'table')`
214
+ 4. `loadSchemaProperties(...)` resolves `$ref`, merges shared `rest` metadata with the `rest.table` overlay, and sorts by effective `rest.order`
215
+ 5. `_createTableMeta()` iterates those properties and decides visibility and render behavior
216
+ 6. `_createColumnsMiddle()` converts the surviving properties into TanStack column definitions
217
+
218
+ `ZovaRender.column(...)` is a shared Zova API available in both Cabloy Basic and Cabloy Start. It supplies metadata only; it neither registers a frontend column nor selects a cell renderer. `ZovaRender.cell(...)` supplies the render target, and `tableCell` beans implement reusable cell behavior.
213
219
 
214
220
  A practical reading takeaway is:
215
221
 
216
222
  - **schema is not only validation truth**
217
- - **schema also drives table order, visibility, and cell render metadata**
223
+ - **schema also drives table order, physical-column behavior, visibility, and cell render metadata**
218
224
 
219
225
  ## 5. How table metadata is built
220
226
 
@@ -257,6 +263,21 @@ A practical reading takeaway is:
257
263
  - **TanStack owns the row-model mechanics**
258
264
  - **the controller still owns which data and columns TanStack sees**
259
265
 
266
+ ### Column metadata becomes TanStack and DOM behavior
267
+
268
+ When `_createColumnsMiddle()` creates each surviving property column, it maps effective table metadata as follows:
269
+
270
+ | Metadata | Controller and render behavior |
271
+ | -------------------- | ---------------------------------------------------------------------------------------------------- |
272
+ | `rest.order` | Orders properties during schema-property loading. |
273
+ | `rest.width` | Becomes the TanStack `size`; render beans apply pixel `width` and `min-width`. |
274
+ | `rest.align` | Remains in column metadata; render beans apply header/cell text alignment. |
275
+ | `rest.fixed` | Builds left/right TanStack pinning arrays; render beans use sticky positions and calculated offsets. |
276
+ | `rest.enableSorting` | Enables sorting only when the field's key or aliases are also present in `schemaOrder`. |
277
+ | `rest.sortDescFirst` | Becomes TanStack's first-toggle direction. |
278
+
279
+ The sorting gate is intentional: metadata can request sorting, but the order schema remains the contract that authorizes the field. The standard resource-page table is controlled and `manualSorting`; it does not locally reorder the fetched data.
280
+
260
281
  ## 7. Column and cell render context are explicitly separated
261
282
 
262
283
  The controller creates two related but distinct runtime contexts.
@@ -392,12 +413,12 @@ That contract defines:
392
413
  The decorator itself is:
393
414
 
394
415
  ```typescript
395
- createBeanDecorator('tableCell', 'sys', true, options);
416
+ createBeanDecorator('tableCell', 'app', true, options);
396
417
  ```
397
418
 
398
- That means `tableCell` is not only a naming convention. It is a frontend bean scene with:
419
+ That means `tableCell` is not only a naming convention. It is an app-scoped frontend bean scene with:
399
420
 
400
- - system-scoped resolution behavior
421
+ - app-scoped reusable bean resolution
401
422
  - scene-level typing
402
423
  - CLI boilerplate support
403
424
  - metadata-driven resource identity
@@ -437,9 +458,12 @@ The default render bean lives in:
437
458
  zova/src/suite-vendor/a-zova/modules/a-table/src/component/table/render.tsx
438
459
  ```
439
460
 
440
- It does two important jobs:
461
+ It does more than delegate vnode creation:
441
462
 
442
463
  - render the outer table markup with `<table class="table">`
464
+ - apply effective column alignment, pixel width/minimum width, and fixed-column sticky positioning
465
+ - calculate and apply left/right pinned-column offsets
466
+ - render accessible sortable header buttons, `aria-sort`, and sort-state indicators when the controller exposes a sortable column
443
467
  - delegate header and cell vnode creation to TanStack `FlexRender`
444
468
 
445
469
  If `slotDefault` is supplied, the render bean yields to that slot instead of the built-in table DOM.
@@ -470,7 +494,8 @@ zova/src/suite/cabloy-basic/modules/basic-page/src/component/blockTable/controll
470
494
  - loads `ModelResource`
471
495
  - creates page-level JSX/CEL environment
472
496
  - computes resource query state
473
- - exposes `data`, `schemaRow`, and `permissions`
497
+ - exposes `data`, `schemaRow`, `schemaOrder`, and `permissions`
498
+ - owns controlled table sorting and converts its single current sorting entry into backend `orders`
474
499
  - refreshes table metadata when permissions change
475
500
 
476
501
  ### Table block path
@@ -480,6 +505,8 @@ zova/src/suite/cabloy-basic/modules/basic-page/src/component/blockTable/controll
480
505
  - renders `ZTable`
481
506
  - passes `data={$$page.data}`
482
507
  - passes `schema={$$page.schemaRow}`
508
+ - passes `schemaOrder={$$page.schemaOrder}`
509
+ - passes controlled `sorting` and `onSortingChange`
483
510
  - passes `tableScope={$$page.jsxCelScope}`
484
511
  - captures `controllerRef` and stores `tableRef` back onto the page controller
485
512
 
@@ -487,18 +514,22 @@ This is one of the most important integration facts about the module.
487
514
 
488
515
  The resource page does not manually rebuild the table runtime. It feeds page-owned resource state into the same reusable table controller.
489
516
 
517
+ This section uses Cabloy Basic `basic-page` paths as the concrete resource-page specimen. The core `ZovaRender.column(...)` metadata contract and the base Zova Table controller behavior are shared by Cabloy Basic and Cabloy Start; resolve Start-specific page modules and renderer keys from the active Start repository.
518
+
490
519
  ## 14. Compact call-flow sketch
491
520
 
492
521
  When in doubt, use this short call flow:
493
522
 
494
- 1. `ZTable` wrapper enters the normal Zova controller path
495
- 2. `ControllerTable.__init__()` creates CEL/JSX support and schema-driven properties
496
- 3. `refreshMeta()` computes visible table properties and per-column render functions
497
- 4. `_createTable()` creates the TanStack bridge through `$useTable(...)`
498
- 5. `RenderTable` renders headers and rows through `FlexRender`
499
- 6. each cell render resolves to text fallback, a general render target, or a `tableCell` bean
500
- 7. `tableCell` beans receive controller-prepared options, scope, and `next()`
501
- 8. resource pages prepare `data`, `schemaRow`, `permissions`, and `tableScope` before entering the same runtime
523
+ 1. `ZovaRender.column(...)` contributes `rest.table` metadata to the backend/OpenAPI field contract
524
+ 2. `ZTable` wrapper enters the normal Zova controller path
525
+ 3. `ControllerTable.__init__()` creates CEL/JSX support and schema-driven properties
526
+ 4. `refreshMeta()` computes visible table properties, column pinning, and per-column render functions
527
+ 5. `_createTable()` creates the TanStack bridge through `$useTable(...)`
528
+ 6. `RenderTable` applies column metadata and renders headers and rows through `FlexRender`
529
+ 7. each cell render resolves to text fallback, a general render target, or a `tableCell` bean
530
+ 8. `tableCell` beans receive controller-prepared options, scope, and `next()`
531
+ 9. resource pages prepare `data`, `schemaRow`, `schemaOrder`, sorting state, permissions, and `tableScope` before entering the same runtime
532
+ 10. a resource-page sort becomes backend `orders`; TanStack does not locally sort the fetched page
502
533
 
503
534
  That is the shortest end-to-end explanation of how the module cooperates.
504
535