cabloy 5.1.169 → 5.1.170
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/.cabloy-version +1 -1
- package/CHANGELOG.md +9 -0
- package/package.json +1 -1
- package/repo-e2e/specs/cabloy-basic.spec.ts +45 -0
- package/vona/packages-cli/cli/package.json +1 -1
- package/vona/packages-cli/cli-set-api/cli/templates/tools/crudBasic/boilerplate/src/dto/{{resourceName}}SelectResItem.tsx_ +4 -0
- package/vona/packages-cli/cli-set-api/cli/templates/tools/crudStart/boilerplate/src/dto/{{resourceName}}SelectResItem.tsx_ +4 -0
- package/vona/packages-cli/cli-set-api/package.json +1 -1
- package/vona/pnpm-lock.yaml +77 -127
- package/vona/src/suite/a-training/modules/training-student/src/dto/studentSelectResItem.tsx +4 -0
- package/vona/src/suite/a-training/modules/training-student/test/student.test.ts +5 -0
- package/vona/src/suite-vendor/a-cabloy/modules/a-layoutprofile/package.json +54 -0
- package/vona/src/suite-vendor/a-cabloy/modules/a-layoutprofile/src/.metadata/index.ts +346 -0
- package/vona/src/suite-vendor/a-cabloy/modules/a-layoutprofile/src/.metadata/this.ts +2 -0
- package/vona/src/suite-vendor/a-cabloy/modules/a-layoutprofile/src/bean/meta.index.ts +12 -0
- package/vona/src/suite-vendor/a-cabloy/modules/a-layoutprofile/src/bean/meta.redlock.ts +11 -0
- package/vona/src/suite-vendor/a-cabloy/modules/a-layoutprofile/src/bean/meta.version.ts +20 -0
- package/vona/src/suite-vendor/a-cabloy/modules/a-layoutprofile/src/controller/layoutprofile.ts +38 -0
- package/vona/src/suite-vendor/a-cabloy/modules/a-layoutprofile/src/dto/layoutProfile.tsx +26 -0
- package/vona/src/suite-vendor/a-cabloy/modules/a-layoutprofile/src/dto/layoutProfileLoad.tsx +13 -0
- package/vona/src/suite-vendor/a-cabloy/modules/a-layoutprofile/src/dto/layoutProfileSave.tsx +31 -0
- package/vona/src/suite-vendor/a-cabloy/modules/a-layoutprofile/src/entity/layoutprofile.ts +20 -0
- package/vona/src/suite-vendor/a-cabloy/modules/a-layoutprofile/src/index.ts +2 -0
- package/vona/src/suite-vendor/a-cabloy/modules/a-layoutprofile/src/model/layoutprofile.ts +10 -0
- package/vona/src/suite-vendor/a-cabloy/modules/a-layoutprofile/src/service/layoutprofile.ts +64 -0
- package/vona/src/suite-vendor/a-cabloy/modules/a-layoutprofile/src/types/index.ts +1 -0
- package/vona/src/suite-vendor/a-cabloy/modules/a-layoutprofile/src/types/layoutprofile.ts +21 -0
- package/vona/src/suite-vendor/a-cabloy/modules/a-layoutprofile/test/layoutprofile.test.ts +258 -0
- package/vona/src/suite-vendor/a-cabloy/modules/a-layoutprofile/tsconfig.build.json +11 -0
- package/vona/src/suite-vendor/a-cabloy/modules/a-layoutprofile/tsconfig.json +7 -0
- package/vona/src/suite-vendor/a-cabloy/package.json +2 -1
- package/vona/src/suite-vendor/a-cabloy/tsconfig.json +3 -0
- package/zova/packages-zova/zova/package.json +2 -2
- package/zova/pnpm-lock.yaml +21 -6
- package/zova/src/suite/a-home/modules/home-base/src/service/ssrLayout.ts +20 -10
- package/zova/src/suite/cabloy-basic/modules/basic-page/src/component/blockPage/controller.tsx +44 -1
- package/zova/src/suite/cabloy-basic/modules/basic-page/src/component/blockTable/controller.tsx +1 -0
- package/zova/src/suite/cabloy-basic/modules/basic-page/src/component/blockToolbarBulk/controller.tsx +40 -25
- package/zova/src/suite/cabloy-basic/modules/basic-page/src/lib/selection.ts +11 -0
- package/zova/src/suite/cabloy-basic/modules/basic-page/test/lib/selection.test.ts +23 -0
- package/zova/src/suite/cabloy-basic/modules/basic-table/src/.metadata/component/actionColumnConfig.ts +31 -0
- package/zova/src/suite/cabloy-basic/modules/basic-table/src/.metadata/index.ts +25 -12
- package/zova/src/suite/cabloy-basic/modules/basic-table/src/component/actionColumnConfig/controller.tsx +260 -0
- package/zova/src/suite/cabloy-basic/modules/basic-table/src/component/table/render.tsx +3 -3
- package/zova/src/suite/cabloy-basic/modules/basic-table/src/config/locale/en-us.ts +10 -0
- package/zova/src/suite/cabloy-basic/modules/basic-table/src/config/locale/zh-cn.ts +10 -0
- package/zova/src/suite-vendor/a-cabloy/modules/a-layoutprofile/cli/openapi.config.ts +9 -0
- package/zova/src/suite-vendor/a-cabloy/modules/a-layoutprofile/package.json +53 -0
- package/zova/src/suite-vendor/a-cabloy/modules/a-layoutprofile/src/.metadata/index.ts +140 -0
- package/zova/src/suite-vendor/a-cabloy/modules/a-layoutprofile/src/.metadata/this.ts +2 -0
- package/zova/src/suite-vendor/a-cabloy/modules/a-layoutprofile/src/api/layoutprofile.ts +67 -0
- package/zova/src/suite-vendor/a-cabloy/modules/a-layoutprofile/src/api/openapi/baseURL.ts +5 -0
- package/zova/src/suite-vendor/a-cabloy/modules/a-layoutprofile/src/api/openapi/index.ts +3 -0
- package/zova/src/suite-vendor/a-cabloy/modules/a-layoutprofile/src/api/openapi/schemas.ts +663 -0
- package/zova/src/suite-vendor/a-cabloy/modules/a-layoutprofile/src/api/openapi/types.ts +10150 -0
- package/zova/src/suite-vendor/a-cabloy/modules/a-layoutprofile/src/apiSchema/layoutprofile.ts +25 -0
- package/zova/src/suite-vendor/a-cabloy/modules/a-layoutprofile/src/index.ts +1 -0
- package/zova/src/suite-vendor/a-cabloy/modules/a-layoutprofile/src/model/layoutprofile.ts +76 -0
- package/zova/src/suite-vendor/a-cabloy/modules/a-layoutprofile/tsconfig.build.json +13 -0
- package/zova/src/suite-vendor/a-cabloy/modules/a-layoutprofile/tsconfig.json +5 -0
- package/zova/src/suite-vendor/a-cabloy/package.json +2 -1
- package/zova/src/suite-vendor/a-zova/modules/a-openapi/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-openapi/src/types/resource/tableActionBulk.ts +3 -0
- package/zova/src/suite-vendor/a-zova/modules/a-ssr/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-ssr/src/lib/bodyReadyObserver.ts +13 -11
- package/zova/src/suite-vendor/a-zova/modules/a-table/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-table/src/component/table/controller.tsx +64 -2
- package/zova/src/suite-vendor/a-zova/modules/a-table/src/component/table/render.tsx +2 -3
- package/zova/src/suite-vendor/a-zova/modules/a-table/src/lib/beanControllerTableBase.ts +10 -0
- package/zova/src/suite-vendor/a-zova/modules/a-table/src/types/index.ts +1 -0
- package/zova/src/suite-vendor/a-zova/modules/a-table/src/types/tableLayout.ts +101 -0
- package/zova/src/suite-vendor/a-zova/modules/a-table/test/lib/tableLayout.test.ts +112 -0
- package/zova/src/suite-vendor/a-zova/package.json +4 -4
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { IApiSchemaOptions } from 'zova-module-a-api';
|
|
2
|
+
|
|
3
|
+
import { BeanBase } from 'zova';
|
|
4
|
+
import { ApiSchema } from 'zova-module-a-api';
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
ApiApiLayoutprofileloadPath,
|
|
8
|
+
ApiApiLayoutprofilesavePath,
|
|
9
|
+
ApiApiLayoutprofileresetPath,
|
|
10
|
+
} from '../api/layoutprofile.js';
|
|
11
|
+
|
|
12
|
+
@ApiSchema()
|
|
13
|
+
export class ApiSchemaLayoutprofile extends BeanBase {
|
|
14
|
+
load(options?: IApiSchemaOptions) {
|
|
15
|
+
return this.$sdk.createApiSchemas(ApiApiLayoutprofileloadPath, 'get', options);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
save(options?: IApiSchemaOptions) {
|
|
19
|
+
return this.$sdk.createApiSchemas(ApiApiLayoutprofilesavePath, 'post', options);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
reset(options?: IApiSchemaOptions) {
|
|
23
|
+
return this.$sdk.createApiSchemas(ApiApiLayoutprofileresetPath, 'delete', options);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './.metadata/index.js';
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { IDecoratorModelOptions } from 'zova-module-a-model';
|
|
2
|
+
|
|
3
|
+
import { BeanModelBase, Model } from 'zova-module-a-model';
|
|
4
|
+
|
|
5
|
+
import type {
|
|
6
|
+
ApiApiLayoutprofileloadResponseBody,
|
|
7
|
+
ApiApiLayoutprofileresetResponseBody,
|
|
8
|
+
ApiApiLayoutprofilesaveRequestBody,
|
|
9
|
+
ApiApiLayoutprofilesaveResponseBody,
|
|
10
|
+
} from '../api/layoutprofile.js';
|
|
11
|
+
|
|
12
|
+
export interface IModelOptionsLayoutProfile extends IDecoratorModelOptions {
|
|
13
|
+
enableSelector: true;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
@Model<IModelOptionsLayoutProfile>({
|
|
17
|
+
enableSelector: true,
|
|
18
|
+
})
|
|
19
|
+
export class ModelLayoutProfile extends BeanModelBase {
|
|
20
|
+
layoutKey: string;
|
|
21
|
+
|
|
22
|
+
protected async __init__(layoutKey: string) {
|
|
23
|
+
if (!layoutKey) throw new Error('layout key not specified');
|
|
24
|
+
await super.__init__(layoutKey);
|
|
25
|
+
this.layoutKey = layoutKey;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
load() {
|
|
29
|
+
if (this.$ssr.cookieDisabledOnServer || !this.$passport.isAuthenticated) return;
|
|
30
|
+
if (process.env.CLIENT && this.$ssr.profile === 'public' && this.$ssr.isRuntimeSsrPreHydration)
|
|
31
|
+
return;
|
|
32
|
+
return this.$useStateData<ApiApiLayoutprofileloadResponseBody>({
|
|
33
|
+
queryKey: ['load'],
|
|
34
|
+
queryFn: async () => {
|
|
35
|
+
return await this.scope.api.layoutprofile.load({
|
|
36
|
+
query: { layoutKey: this.layoutKey },
|
|
37
|
+
});
|
|
38
|
+
},
|
|
39
|
+
meta: {
|
|
40
|
+
disableErrorEffect: true,
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
save() {
|
|
46
|
+
return this.$useMutationData<
|
|
47
|
+
ApiApiLayoutprofilesaveResponseBody,
|
|
48
|
+
ApiApiLayoutprofilesaveRequestBody['profile']
|
|
49
|
+
>({
|
|
50
|
+
mutationKey: ['save'],
|
|
51
|
+
mutationFn: async profile => {
|
|
52
|
+
return await this.scope.api.layoutprofile.save({
|
|
53
|
+
layoutKey: this.layoutKey,
|
|
54
|
+
profile,
|
|
55
|
+
});
|
|
56
|
+
},
|
|
57
|
+
onSuccess: profile => {
|
|
58
|
+
this.$setQueryData<ApiApiLayoutprofilesaveResponseBody>(['load'], profile);
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
reset() {
|
|
64
|
+
return this.$useMutationData<ApiApiLayoutprofileresetResponseBody>({
|
|
65
|
+
mutationKey: ['reset'],
|
|
66
|
+
mutationFn: async () => {
|
|
67
|
+
return await this.scope.api.layoutprofile.reset({
|
|
68
|
+
query: { layoutKey: this.layoutKey },
|
|
69
|
+
});
|
|
70
|
+
},
|
|
71
|
+
onSuccess: async () => {
|
|
72
|
+
this.$setQueryData<ApiApiLayoutprofileloadResponseBody>(['load'], undefined);
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../tsconfig.base",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"rootDir": "src",
|
|
5
|
+
"outDir": "dist",
|
|
6
|
+
"declaration": true,
|
|
7
|
+
"declarationMap": true,
|
|
8
|
+
"emitDeclarationOnly": true,
|
|
9
|
+
"allowImportingTsExtensions": true,
|
|
10
|
+
"noEmit": false
|
|
11
|
+
},
|
|
12
|
+
"include": ["src", "src/.metadata/**/*"]
|
|
13
|
+
}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zova-suite-a-cabloy",
|
|
3
|
-
"version": "5.1.
|
|
3
|
+
"version": "5.1.56",
|
|
4
4
|
"gitHead": "4dece229b6d3f4735930e833afcb4b95ec51bf86",
|
|
5
5
|
"description": "",
|
|
6
6
|
"author": "",
|
|
7
7
|
"files": [],
|
|
8
8
|
"type": "module",
|
|
9
9
|
"dependencies": {
|
|
10
|
+
"zova-module-a-layoutprofile": "^5.0.2",
|
|
10
11
|
"zova-module-a-rbac": "^5.0.1",
|
|
11
12
|
"zova-module-rest-resource": "^5.1.54"
|
|
12
13
|
},
|
package/zova/src/suite-vendor/a-zova/modules/a-openapi/src/types/resource/tableActionBulk.ts
CHANGED
|
@@ -20,9 +20,12 @@ export interface IResourceTableSelectionPayload<TData extends {} = Record<string
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
/** Declarative bulk-action metadata authored by a resource schema. */
|
|
23
|
+
export type TypeResourceTableActionBulkPlacement = 'start' | 'end';
|
|
24
|
+
|
|
23
25
|
export interface IResourceTableActionBulkOptionsBase {
|
|
24
26
|
class?: any;
|
|
25
27
|
style?: types.NestedCSSProperties;
|
|
28
|
+
placement?: TypeResourceTableActionBulkPlacement;
|
|
26
29
|
resource?: string;
|
|
27
30
|
permission?: IPermissionHintTableActionBulk;
|
|
28
31
|
requiresSelection?: boolean;
|
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
export function getBodyReadyObserverScript() {
|
|
2
2
|
return `<script id="ssr-body-ready-observer">
|
|
3
3
|
window.ssr_bodyReadyObserverClear=()=>{
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
document.querySelector('#ssr-body-ready-observer').remove();
|
|
11
|
-
}
|
|
4
|
+
window.ssr_body_ready_condition=undefined;
|
|
5
|
+
window.ssr_body_ready_callback=undefined;
|
|
6
|
+
window.ssr_bodyReadyObserver?.disconnect();
|
|
7
|
+
window.ssr_bodyReadyObserver=undefined;
|
|
8
|
+
document.body.style.display='block';
|
|
9
|
+
document.querySelector('#ssr-body-ready-observer')?.remove();
|
|
12
10
|
};
|
|
13
11
|
window.ssr_bodyReadyObserver = new MutationObserver(() => {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
const __targets=window.ssr_body_ready_condition?.();
|
|
13
|
+
if(__targets){
|
|
14
|
+
try{
|
|
15
|
+
window.ssr_body_ready_callback?.(__targets);
|
|
16
|
+
}finally{
|
|
17
|
+
window.ssr_bodyReadyObserverClear();
|
|
18
|
+
}
|
|
17
19
|
}
|
|
18
20
|
});
|
|
19
21
|
window.ssr_bodyReadyObserver.observe(document.body, {
|
|
@@ -45,13 +45,16 @@ import type {
|
|
|
45
45
|
ITableScope,
|
|
46
46
|
TypeTableCellRender,
|
|
47
47
|
} from '../../types/tableColumn.js';
|
|
48
|
+
import type { ITableLayout } from '../../types/tableLayout.js';
|
|
48
49
|
|
|
49
50
|
import { BeanControllerTableBase } from '../../lib/beanControllerTableBase.js';
|
|
50
51
|
import { TableColumnIdSelection } from '../../types/table.js';
|
|
51
52
|
import { ITableCellRenderColumnOptions } from '../../types/tableColumn.js';
|
|
53
|
+
import { reconcileTableLayout } from '../../types/tableLayout.js';
|
|
52
54
|
|
|
53
55
|
export interface ControllerTableProps<TData extends {} = {}> {
|
|
54
56
|
data?: TData[];
|
|
57
|
+
layout?: ITableLayout;
|
|
55
58
|
schema?: SchemaObject;
|
|
56
59
|
schemaOrder?: SchemaObject;
|
|
57
60
|
sorting?: SortingState;
|
|
@@ -102,6 +105,13 @@ export class ControllerTable<TData extends {} = {}> extends BeanControllerTableB
|
|
|
102
105
|
await this.refreshMeta();
|
|
103
106
|
},
|
|
104
107
|
);
|
|
108
|
+
this.$watch(
|
|
109
|
+
() => this.$props.layout,
|
|
110
|
+
async (newValue, oldValue) => {
|
|
111
|
+
if (deepEqual(newValue, oldValue)) return;
|
|
112
|
+
await this.refreshMeta();
|
|
113
|
+
},
|
|
114
|
+
);
|
|
105
115
|
// table
|
|
106
116
|
this._createTable();
|
|
107
117
|
}
|
|
@@ -122,6 +132,14 @@ export class ControllerTable<TData extends {} = {}> extends BeanControllerTableB
|
|
|
122
132
|
return this.$props.rowSelection ?? {};
|
|
123
133
|
}
|
|
124
134
|
|
|
135
|
+
public get layout(): ITableLayout {
|
|
136
|
+
return reconcileTableLayout(this.layoutProperties, this.$props.layout);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
public get layoutProperties(): readonly ISchemaObjectExtensionField[] {
|
|
140
|
+
return this.tableMeta?.properties ?? [];
|
|
141
|
+
}
|
|
142
|
+
|
|
125
143
|
public async refreshMeta() {
|
|
126
144
|
this.tableMeta = await this._createTableMeta();
|
|
127
145
|
this.columns = await this._createColumns();
|
|
@@ -148,6 +166,15 @@ export class ControllerTable<TData extends {} = {}> extends BeanControllerTableB
|
|
|
148
166
|
get rowSelection() {
|
|
149
167
|
return self.rowSelection;
|
|
150
168
|
},
|
|
169
|
+
get columnVisibility() {
|
|
170
|
+
return self._getColumnVisibility();
|
|
171
|
+
},
|
|
172
|
+
get columnOrder() {
|
|
173
|
+
return self._getColumnOrder();
|
|
174
|
+
},
|
|
175
|
+
get columnSizing() {
|
|
176
|
+
return self._getColumnSizing();
|
|
177
|
+
},
|
|
151
178
|
},
|
|
152
179
|
onSortingChange: updater => {
|
|
153
180
|
const sorting = functionalUpdate(updater, self.sorting).slice(0, 1);
|
|
@@ -235,10 +262,15 @@ export class ControllerTable<TData extends {} = {}> extends BeanControllerTableB
|
|
|
235
262
|
return property?.title || key;
|
|
236
263
|
},
|
|
237
264
|
cell: props => tableMeta.renders[key](props),
|
|
238
|
-
size:
|
|
265
|
+
size: this._getLayoutWidth(key),
|
|
239
266
|
enableSorting: property.rest?.enableSorting === true && this._isOrderable(property),
|
|
240
267
|
sortDescFirst: property.rest?.sortDescFirst === true,
|
|
241
|
-
meta: {
|
|
268
|
+
meta: {
|
|
269
|
+
rest: {
|
|
270
|
+
...property.rest,
|
|
271
|
+
width: this._getLayoutWidth(key),
|
|
272
|
+
},
|
|
273
|
+
},
|
|
242
274
|
}),
|
|
243
275
|
);
|
|
244
276
|
}
|
|
@@ -262,6 +294,36 @@ export class ControllerTable<TData extends {} = {}> extends BeanControllerTableB
|
|
|
262
294
|
return typeof enableRowSelection === 'function' ? enableRowSelection(row) : true;
|
|
263
295
|
}
|
|
264
296
|
|
|
297
|
+
private _getLayoutColumn(key: string) {
|
|
298
|
+
return this.layout.columns.find(column => column.key === key);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
private _getLayoutWidth(key: string) {
|
|
302
|
+
const width = this._getLayoutColumn(key)?.width;
|
|
303
|
+
return typeof width === 'number' ? width : undefined;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
private _getColumnVisibility() {
|
|
307
|
+
return Object.fromEntries(
|
|
308
|
+
this.layout.columns
|
|
309
|
+
.filter(column => column.key !== TableColumnIdSelection)
|
|
310
|
+
.map(column => [column.key, column.visible]),
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
private _getColumnOrder() {
|
|
315
|
+
const keys = this.layout.columns.map(column => column.key);
|
|
316
|
+
return this.$props.enableRowSelection ? [TableColumnIdSelection, ...keys] : keys;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
private _getColumnSizing() {
|
|
320
|
+
return Object.fromEntries(
|
|
321
|
+
this.layout.columns
|
|
322
|
+
.filter(column => typeof column.width === 'number')
|
|
323
|
+
.map(column => [column.key, column.width as number]),
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
|
|
265
327
|
private _getColumnPinning() {
|
|
266
328
|
const left: string[] = this.$props.enableRowSelection ? [TableColumnIdSelection] : [];
|
|
267
329
|
const right: string[] = [];
|
|
@@ -11,10 +11,9 @@ export class RenderTable extends BeanRenderBase {
|
|
|
11
11
|
private _getColumnStyle(column: any): CSSProperties {
|
|
12
12
|
const rest = (column.columnDef.meta as any)?.rest;
|
|
13
13
|
const fixed = column.getIsPinned();
|
|
14
|
-
const width =
|
|
14
|
+
const width = column.getSize();
|
|
15
15
|
return {
|
|
16
|
-
width,
|
|
17
|
-
minWidth: width,
|
|
16
|
+
...(typeof rest?.width === 'number' ? { width: `${width}px`, minWidth: `${width}px` } : {}),
|
|
18
17
|
textAlign: rest?.align,
|
|
19
18
|
position: fixed ? ('sticky' as const) : undefined,
|
|
20
19
|
left: fixed === 'left' ? `${column.getStart('left')}px` : undefined,
|
|
@@ -1,15 +1,25 @@
|
|
|
1
1
|
import type { Table } from '@tanstack/table-core';
|
|
2
2
|
import type { TableOptionsWithReactiveData } from '@tanstack/vue-table';
|
|
3
|
+
import type { ISchemaObjectExtensionField } from 'zova-module-a-openapi';
|
|
3
4
|
|
|
4
5
|
import { useVueTable } from '@tanstack/vue-table';
|
|
5
6
|
import { markRaw } from 'vue';
|
|
6
7
|
import { BeanControllerBase } from 'zova';
|
|
7
8
|
|
|
8
9
|
import { TypeTable } from '../types/table.js';
|
|
10
|
+
import { ITableLayout } from '../types/tableLayout.js';
|
|
9
11
|
|
|
10
12
|
export class BeanControllerTableBase<TData extends {} = {}> extends BeanControllerBase {
|
|
11
13
|
table: TypeTable<TData>;
|
|
12
14
|
|
|
15
|
+
public get layout(): ITableLayout {
|
|
16
|
+
throw new Error('should implement layout');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
public get layoutProperties(): readonly ISchemaObjectExtensionField[] {
|
|
20
|
+
throw new Error('should implement layoutProperties');
|
|
21
|
+
}
|
|
22
|
+
|
|
13
23
|
public $useTable(initialOptions: TableOptionsWithReactiveData<TData>): Table<TData> {
|
|
14
24
|
return this.ctx.util.instanceScope(() => {
|
|
15
25
|
return markRaw(useVueTable(initialOptions));
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import type { ISchemaObjectExtensionField } from 'zova-module-a-openapi';
|
|
2
|
+
|
|
3
|
+
import { TableColumnIdSelection } from './table.js';
|
|
4
|
+
|
|
5
|
+
export type TypeTableLayoutWidth = number | 'auto';
|
|
6
|
+
|
|
7
|
+
export interface ITableLayoutColumn {
|
|
8
|
+
key: string;
|
|
9
|
+
visible: boolean;
|
|
10
|
+
width: TypeTableLayoutWidth;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface ITableLayout {
|
|
14
|
+
version: 1;
|
|
15
|
+
schemaFingerprint?: string;
|
|
16
|
+
columns: ITableLayoutColumn[];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface ITableLayoutProfileLike {
|
|
20
|
+
version?: unknown;
|
|
21
|
+
schemaFingerprint?: unknown;
|
|
22
|
+
columns?: unknown;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function isWidth(value: unknown): value is TypeTableLayoutWidth {
|
|
26
|
+
return (
|
|
27
|
+
value === 'auto' ||
|
|
28
|
+
(typeof value === 'number' && Number.isInteger(value) && value >= 1 && value <= 2000)
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Reconciles a persisted layout with the currently eligible schema columns.
|
|
34
|
+
* Schema visibility and fixed regions always remain authoritative.
|
|
35
|
+
*/
|
|
36
|
+
export function reconcileTableLayout(
|
|
37
|
+
properties: readonly ISchemaObjectExtensionField[],
|
|
38
|
+
profile?: ITableLayoutProfileLike | ITableLayout,
|
|
39
|
+
): ITableLayout {
|
|
40
|
+
const entries = new Map<string, ITableLayoutColumn>();
|
|
41
|
+
const profileColumns = Array.isArray(profile?.columns) ? profile.columns : [];
|
|
42
|
+
for (const item of profileColumns) {
|
|
43
|
+
if (!item || typeof item !== 'object') continue;
|
|
44
|
+
const column = item as Record<string, unknown>;
|
|
45
|
+
const key = typeof column.key === 'string' ? column.key.trim() : '';
|
|
46
|
+
if (!key || entries.has(key)) continue;
|
|
47
|
+
if (typeof column.visible !== 'boolean' || !isWidth(column.width)) continue;
|
|
48
|
+
entries.set(key, { key, visible: column.visible, width: column.width });
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const eligible = properties.filter(
|
|
52
|
+
property =>
|
|
53
|
+
typeof property.key === 'string' && property.key && property.key !== TableColumnIdSelection,
|
|
54
|
+
);
|
|
55
|
+
const schemaKeys = new Set(eligible.map(property => property.key!));
|
|
56
|
+
const regions = new Map<'left' | 'center' | 'right', ISchemaObjectExtensionField[]>();
|
|
57
|
+
regions.set('left', []);
|
|
58
|
+
regions.set('center', []);
|
|
59
|
+
regions.set('right', []);
|
|
60
|
+
for (const property of eligible) {
|
|
61
|
+
const fixed = property.rest?.fixed;
|
|
62
|
+
regions.get(fixed === 'left' ? 'left' : fixed === 'right' ? 'right' : 'center')!.push(property);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const result: ITableLayoutColumn[] = [];
|
|
66
|
+
for (const region of ['left', 'center', 'right'] as const) {
|
|
67
|
+
const regionProperties = regions.get(region)!;
|
|
68
|
+
const regionKeys = new Set(regionProperties.map(property => property.key!));
|
|
69
|
+
const ordered = profileColumns
|
|
70
|
+
.map(item =>
|
|
71
|
+
item && typeof item === 'object' ? (item as Record<string, unknown>) : undefined,
|
|
72
|
+
)
|
|
73
|
+
.filter(item => {
|
|
74
|
+
const key = typeof item?.key === 'string' ? item.key.trim() : '';
|
|
75
|
+
return !!key && regionKeys.has(key) && schemaKeys.has(key) && entries.get(key)?.key === key;
|
|
76
|
+
})
|
|
77
|
+
.map(item => item!.key as string);
|
|
78
|
+
const seen = new Set<string>();
|
|
79
|
+
for (const key of [...ordered, ...regionProperties.map(property => property.key!)]) {
|
|
80
|
+
if (seen.has(key)) continue;
|
|
81
|
+
seen.add(key);
|
|
82
|
+
const property = regionProperties.find(item => item.key === key)!;
|
|
83
|
+
const entry = entries.get(key);
|
|
84
|
+
const schemaWidth = property.rest?.width;
|
|
85
|
+
result.push({
|
|
86
|
+
key,
|
|
87
|
+
visible: entry?.visible ?? true,
|
|
88
|
+
width:
|
|
89
|
+
entry?.width ??
|
|
90
|
+
(typeof schemaWidth === 'number' && schemaWidth > 0 ? schemaWidth : 'auto'),
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
version: 1,
|
|
96
|
+
...(typeof profile?.schemaFingerprint === 'string'
|
|
97
|
+
? { schemaFingerprint: profile.schemaFingerprint }
|
|
98
|
+
: {}),
|
|
99
|
+
columns: result,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import type { ISchemaObjectExtensionField } from 'zova-module-a-openapi';
|
|
2
|
+
|
|
3
|
+
import assert from 'node:assert/strict';
|
|
4
|
+
import test from 'node:test';
|
|
5
|
+
|
|
6
|
+
import { TableColumnIdSelection } from '../../src/types/table.js';
|
|
7
|
+
import { reconcileTableLayout } from '../../src/types/tableLayout.js';
|
|
8
|
+
|
|
9
|
+
function createProperties(...properties: ISchemaObjectExtensionField[]) {
|
|
10
|
+
return properties;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
test('table layout drops stale and malformed profile columns while preserving schema defaults', () => {
|
|
14
|
+
const layout = reconcileTableLayout(
|
|
15
|
+
createProperties(
|
|
16
|
+
{ key: 'id', type: 'number', rest: { width: 60, fixed: 'left' } },
|
|
17
|
+
{ key: 'name', type: 'string' },
|
|
18
|
+
{ key: 'level', type: 'string', rest: { width: 120 } },
|
|
19
|
+
{ key: 'operations', type: 'string', rest: { width: 360, fixed: 'right' } },
|
|
20
|
+
),
|
|
21
|
+
{
|
|
22
|
+
version: 1,
|
|
23
|
+
schemaFingerprint: 'student-v1',
|
|
24
|
+
columns: [
|
|
25
|
+
{ key: 'name', visible: false, width: 480 },
|
|
26
|
+
{ key: 'obsolete', visible: true, width: 80 },
|
|
27
|
+
{ key: 'level', visible: true, width: 0 },
|
|
28
|
+
{ key: 'operations', visible: true, width: 'auto' },
|
|
29
|
+
{ key: '', visible: false, width: 90 },
|
|
30
|
+
],
|
|
31
|
+
},
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
assert.deepEqual(layout, {
|
|
35
|
+
version: 1,
|
|
36
|
+
schemaFingerprint: 'student-v1',
|
|
37
|
+
columns: [
|
|
38
|
+
{ key: 'id', visible: true, width: 60 },
|
|
39
|
+
{ key: 'name', visible: false, width: 480 },
|
|
40
|
+
{ key: 'level', visible: true, width: 120 },
|
|
41
|
+
{ key: 'operations', visible: true, width: 'auto' },
|
|
42
|
+
],
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test('table layout retains the first valid duplicate and appends new schema fields', () => {
|
|
47
|
+
const layout = reconcileTableLayout(
|
|
48
|
+
createProperties(
|
|
49
|
+
{ key: 'name', type: 'string' },
|
|
50
|
+
{ key: 'level', type: 'string' },
|
|
51
|
+
{ key: 'createdAt', type: 'string', rest: { width: 180 } },
|
|
52
|
+
),
|
|
53
|
+
{
|
|
54
|
+
columns: [
|
|
55
|
+
{ key: 'level', visible: false, width: 240 },
|
|
56
|
+
{ key: 'name', visible: true, width: 'auto' },
|
|
57
|
+
{ key: 'level', visible: true, width: 300 },
|
|
58
|
+
],
|
|
59
|
+
},
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
assert.deepEqual(layout.columns, [
|
|
63
|
+
{ key: 'level', visible: false, width: 240 },
|
|
64
|
+
{ key: 'name', visible: true, width: 'auto' },
|
|
65
|
+
{ key: 'createdAt', visible: true, width: 180 },
|
|
66
|
+
]);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test('table layout permits ordering only within schema fixed regions', () => {
|
|
70
|
+
const layout = reconcileTableLayout(
|
|
71
|
+
createProperties(
|
|
72
|
+
{ key: 'leftFirst', type: 'string', rest: { fixed: 'left' } },
|
|
73
|
+
{ key: 'leftSecond', type: 'string', rest: { fixed: 'left' } },
|
|
74
|
+
{ key: 'centerFirst', type: 'string' },
|
|
75
|
+
{ key: 'centerSecond', type: 'string' },
|
|
76
|
+
{ key: 'rightFirst', type: 'string', rest: { fixed: 'right' } },
|
|
77
|
+
{ key: 'rightSecond', type: 'string', rest: { fixed: 'right' } },
|
|
78
|
+
),
|
|
79
|
+
{
|
|
80
|
+
columns: [
|
|
81
|
+
{ key: 'rightSecond', visible: true, width: 'auto' },
|
|
82
|
+
{ key: 'centerSecond', visible: true, width: 'auto' },
|
|
83
|
+
{ key: 'leftSecond', visible: true, width: 'auto' },
|
|
84
|
+
{ key: 'rightFirst', visible: true, width: 'auto' },
|
|
85
|
+
{ key: 'centerFirst', visible: true, width: 'auto' },
|
|
86
|
+
{ key: 'leftFirst', visible: true, width: 'auto' },
|
|
87
|
+
],
|
|
88
|
+
},
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
assert.deepEqual(
|
|
92
|
+
layout.columns.map(column => column.key),
|
|
93
|
+
['leftSecond', 'leftFirst', 'centerSecond', 'centerFirst', 'rightSecond', 'rightFirst'],
|
|
94
|
+
);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test('table layout excludes synthetic selection state from persistence', () => {
|
|
98
|
+
const layout = reconcileTableLayout(
|
|
99
|
+
createProperties(
|
|
100
|
+
{ key: TableColumnIdSelection, type: 'boolean' },
|
|
101
|
+
{ key: 'name', type: 'string' },
|
|
102
|
+
),
|
|
103
|
+
{
|
|
104
|
+
columns: [
|
|
105
|
+
{ key: TableColumnIdSelection, visible: false, width: 40 },
|
|
106
|
+
{ key: 'name', visible: true, width: 'auto' },
|
|
107
|
+
],
|
|
108
|
+
},
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
assert.deepEqual(layout.columns, [{ key: 'name', visible: true, width: 'auto' }]);
|
|
112
|
+
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zova-suite-a-zova",
|
|
3
|
-
"version": "5.1.
|
|
3
|
+
"version": "5.1.161",
|
|
4
4
|
"gitHead": "2c5c19284bab738e492856189acb6fad74b8a7b7",
|
|
5
5
|
"description": "zova",
|
|
6
6
|
"license": "MIT",
|
|
@@ -22,15 +22,15 @@
|
|
|
22
22
|
"zova-module-a-logger": "^5.1.28",
|
|
23
23
|
"zova-module-a-meta": "^5.1.22",
|
|
24
24
|
"zova-module-a-model": "^5.1.36",
|
|
25
|
-
"zova-module-a-openapi": "^5.1.
|
|
25
|
+
"zova-module-a-openapi": "^5.1.54",
|
|
26
26
|
"zova-module-a-router": "^5.1.37",
|
|
27
27
|
"zova-module-a-routerstack": "^5.1.29",
|
|
28
28
|
"zova-module-a-routertabs": "^5.1.34",
|
|
29
|
-
"zova-module-a-ssr": "^5.1.
|
|
29
|
+
"zova-module-a-ssr": "^5.1.39",
|
|
30
30
|
"zova-module-a-ssrhmr": "^5.1.23",
|
|
31
31
|
"zova-module-a-ssrserver": "^5.1.26",
|
|
32
32
|
"zova-module-a-style": "^5.1.35",
|
|
33
|
-
"zova-module-a-table": "^5.1.
|
|
33
|
+
"zova-module-a-table": "^5.1.43",
|
|
34
34
|
"zova-module-a-zod": "^5.1.37",
|
|
35
35
|
"zova-module-a-zova": "^5.1.104"
|
|
36
36
|
},
|