cabloy 5.1.169 → 5.1.171
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 +16 -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 +177 -500
- 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 +37 -22
- 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
package/zova/src/suite/cabloy-basic/modules/basic-page/src/component/blockPage/controller.tsx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { RowSelectionState, SortingState } from '@tanstack/vue-table';
|
|
2
2
|
import type { TableIdentity } from 'table-identity';
|
|
3
3
|
import type { IComponentOptions } from 'zova';
|
|
4
|
+
import type { ModelLayoutProfile } from 'zova-module-a-layoutprofile';
|
|
4
5
|
import type {
|
|
5
6
|
IJsxRenderContextPage,
|
|
6
7
|
IPageScope,
|
|
@@ -19,7 +20,7 @@ import { BeanControllerBase, deepEqual } from 'zova';
|
|
|
19
20
|
import { ZovaJsx } from 'zova-jsx';
|
|
20
21
|
import { Controller } from 'zova-module-a-bean';
|
|
21
22
|
import { $QueriesEnsureLoaded } from 'zova-module-a-model';
|
|
22
|
-
import { BeanControllerTableBase } from 'zova-module-a-table';
|
|
23
|
+
import { BeanControllerTableBase, ITableLayout } from 'zova-module-a-table';
|
|
23
24
|
import { ModelResource } from 'zova-module-rest-resource';
|
|
24
25
|
|
|
25
26
|
import {
|
|
@@ -62,6 +63,7 @@ export class ControllerBlockPage<TData extends {} = {}> extends BeanControllerBa
|
|
|
62
63
|
tableRef: BeanControllerTableBase<TData> | undefined;
|
|
63
64
|
private _tableMetaRefreshPending = false;
|
|
64
65
|
private _tableMetaRefreshTask: Promise<void> | undefined;
|
|
66
|
+
$$modelLayoutProfile: ModelLayoutProfile | undefined;
|
|
65
67
|
|
|
66
68
|
jsxZova: ZovaJsx;
|
|
67
69
|
jsxCelScope: IPageScope;
|
|
@@ -91,6 +93,14 @@ export class ControllerBlockPage<TData extends {} = {}> extends BeanControllerBa
|
|
|
91
93
|
true,
|
|
92
94
|
this.resource,
|
|
93
95
|
);
|
|
96
|
+
const layoutKey = this.layoutKey;
|
|
97
|
+
if (layoutKey) {
|
|
98
|
+
this.$$modelLayoutProfile = await this.bean._getBeanSelector(
|
|
99
|
+
'a-layoutprofile.model.layoutProfile',
|
|
100
|
+
true,
|
|
101
|
+
layoutKey,
|
|
102
|
+
);
|
|
103
|
+
}
|
|
94
104
|
// jsx
|
|
95
105
|
this._prepareJsx();
|
|
96
106
|
// query
|
|
@@ -120,6 +130,7 @@ export class ControllerBlockPage<TData extends {} = {}> extends BeanControllerBa
|
|
|
120
130
|
await $QueriesEnsureLoaded(
|
|
121
131
|
() => this.$$modelResource.apiSchemasSelect.sdk,
|
|
122
132
|
() => this.queryData,
|
|
133
|
+
() => this.queryLayoutProfile,
|
|
123
134
|
);
|
|
124
135
|
this._refreshSelectedRows((this.data ?? []) as unknown as Record<string, unknown>[]);
|
|
125
136
|
this._notifySelectionChange();
|
|
@@ -144,6 +155,38 @@ export class ControllerBlockPage<TData extends {} = {}> extends BeanControllerBa
|
|
|
144
155
|
return this.$props.resource;
|
|
145
156
|
}
|
|
146
157
|
|
|
158
|
+
get queryLayoutProfile() {
|
|
159
|
+
return this.$$modelLayoutProfile?.load();
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
get tableLayout(): ITableLayout | undefined {
|
|
163
|
+
return this.queryLayoutProfile?.data as ITableLayout | undefined;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
get layoutKey() {
|
|
167
|
+
return this.$pageRoute?.path;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
get columnConfigSeed() {
|
|
171
|
+
const layout = this.tableRef?.layout;
|
|
172
|
+
const properties = this.tableRef?.layoutProperties;
|
|
173
|
+
if (!layout || !properties || !this.$$modelLayoutProfile || !this.$passport.isAuthenticated)
|
|
174
|
+
return;
|
|
175
|
+
return { layout, properties };
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
async saveColumnConfig(layout: ITableLayout): Promise<void> {
|
|
179
|
+
if (!this.$$modelLayoutProfile) return;
|
|
180
|
+
await this.$$modelLayoutProfile.save().mutateAsync(layout as any);
|
|
181
|
+
await this._requestTableMetaRefresh();
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
async resetColumnConfig(): Promise<void> {
|
|
185
|
+
if (!this.$$modelLayoutProfile) return;
|
|
186
|
+
await this.$$modelLayoutProfile.reset().mutateAsync();
|
|
187
|
+
await this._requestTableMetaRefresh();
|
|
188
|
+
}
|
|
189
|
+
|
|
147
190
|
private _normalizeSelectedIds(ids: readonly TableIdentity[]) {
|
|
148
191
|
const selectedIds: TableIdentity[] = [];
|
|
149
192
|
const keys = new Set<string>();
|
package/zova/src/suite/cabloy-basic/modules/basic-page/src/component/blockTable/controller.tsx
CHANGED
|
@@ -50,6 +50,7 @@ export class ControllerBlockTable<TData extends {} = {}> extends BeanControllerB
|
|
|
50
50
|
rowSelection={$$page.rowSelection}
|
|
51
51
|
onRowSelectionChange={updater => $$page.onRowSelectionChange(updater)}
|
|
52
52
|
tableScope={$$page.jsxCelScope}
|
|
53
|
+
layout={$$page.tableLayout}
|
|
53
54
|
></ZTable>
|
|
54
55
|
</div>
|
|
55
56
|
);
|
package/zova/src/suite/cabloy-basic/modules/basic-page/src/component/blockToolbarBulk/controller.tsx
CHANGED
|
@@ -9,7 +9,10 @@ import { VNode } from 'vue';
|
|
|
9
9
|
import { BeanControllerBase, Use } from 'zova';
|
|
10
10
|
import { Controller } from 'zova-module-a-bean';
|
|
11
11
|
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
groupTableActionBulkActions,
|
|
14
|
+
resolveTableActionBulkDynamicProps,
|
|
15
|
+
} from '../../lib/selection.js';
|
|
13
16
|
|
|
14
17
|
declare module 'zova-module-a-openapi' {
|
|
15
18
|
export interface IResourceBlockRecord {
|
|
@@ -65,35 +68,48 @@ export class ControllerBlockToolbarBulk extends BeanControllerBase {
|
|
|
65
68
|
const { $$page } = this.$$renderContext;
|
|
66
69
|
const domActions = this._renderActions();
|
|
67
70
|
const selectionAvailable = $$page.selectionAvailable;
|
|
68
|
-
if (
|
|
71
|
+
if (domActions.start.length === 0 && domActions.end.length === 0 && !selectionAvailable) return;
|
|
69
72
|
return (
|
|
70
73
|
<div class={this.$props.class}>
|
|
71
|
-
<div
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
<div
|
|
75
|
+
class="flex flex-wrap items-center gap-3"
|
|
76
|
+
role="toolbar"
|
|
77
|
+
aria-label={this.scope.locale.BulkActions()}
|
|
78
|
+
>
|
|
79
|
+
{(selectionAvailable || domActions.start.length > 0) && (
|
|
80
|
+
<div class="join">
|
|
81
|
+
{$$page.selectionToggleAvailable && (
|
|
82
|
+
<button
|
|
83
|
+
type="button"
|
|
84
|
+
class="btn join-item"
|
|
85
|
+
onClick={() => $$page.toggleSelection()}
|
|
86
|
+
>
|
|
87
|
+
{$$page.selectionVisible ? this.scope.locale.Done() : this.scope.locale.Select()}
|
|
88
|
+
</button>
|
|
89
|
+
)}
|
|
90
|
+
{$$page.selectionEnabled && (
|
|
91
|
+
<span
|
|
92
|
+
class="join-item border-base-300 bg-base-200 px-3 py-2"
|
|
93
|
+
role="status"
|
|
94
|
+
aria-live="polite"
|
|
95
|
+
>
|
|
96
|
+
{this.scope.locale.SelectedItems($$page.selection.count)}
|
|
97
|
+
</span>
|
|
98
|
+
)}
|
|
99
|
+
{domActions.start}
|
|
100
|
+
</div>
|
|
76
101
|
)}
|
|
77
|
-
{
|
|
78
|
-
<span
|
|
79
|
-
class="join-item border-base-300 bg-base-200 px-3 py-2"
|
|
80
|
-
role="status"
|
|
81
|
-
aria-live="polite"
|
|
82
|
-
>
|
|
83
|
-
{this.scope.locale.SelectedItems($$page.selection.count)}
|
|
84
|
-
</span>
|
|
85
|
-
)}
|
|
86
|
-
{domActions}
|
|
102
|
+
{domActions.end.length > 0 && <div class="ml-auto join">{domActions.end}</div>}
|
|
87
103
|
</div>
|
|
88
104
|
</div>
|
|
89
105
|
);
|
|
90
106
|
}
|
|
91
107
|
|
|
92
|
-
private _renderActions() {
|
|
108
|
+
private _renderActions(): Record<'start' | 'end', VNode[]> {
|
|
93
109
|
const { $jsx, $celScope } = this.$$renderContext;
|
|
94
110
|
const actions = this.$props.actions;
|
|
95
|
-
if (!actions || actions.length === 0) return;
|
|
96
|
-
const
|
|
111
|
+
if (!actions || actions.length === 0) return { start: [], end: [] };
|
|
112
|
+
const items: { item: VNode; placement?: unknown }[] = [];
|
|
97
113
|
actions.forEach((action, index) => {
|
|
98
114
|
const actionName = action.name;
|
|
99
115
|
const permissionHint = action.options?.permission;
|
|
@@ -127,12 +143,11 @@ export class ControllerBlockToolbarBulk extends BeanControllerBase {
|
|
|
127
143
|
});
|
|
128
144
|
const domAction = $jsx.render(action.render!, options, $celScope, this.$$renderContext);
|
|
129
145
|
if (!domAction) return;
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
domActions.push(domAction);
|
|
146
|
+
const renderedActions = Array.isArray(domAction) ? domAction : [domAction];
|
|
147
|
+
for (const item of renderedActions) {
|
|
148
|
+
items.push({ item, placement: action.options?.placement });
|
|
134
149
|
}
|
|
135
150
|
});
|
|
136
|
-
return
|
|
151
|
+
return groupTableActionBulkActions(items);
|
|
137
152
|
}
|
|
138
153
|
}
|
|
@@ -33,6 +33,7 @@ export function resolvePickerSelectionMax(
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
export type TypeTableActionBulkDynamicDisabledReason = 'maxExceeded' | 'selectionUnavailable';
|
|
36
|
+
export type TypeTableActionBulkPlacement = 'start' | 'end';
|
|
36
37
|
|
|
37
38
|
export interface IResourceTableActionBulkDynamicProps {
|
|
38
39
|
dynamicSelection: IResourceTableSelectionPayload;
|
|
@@ -41,6 +42,16 @@ export interface IResourceTableActionBulkDynamicProps {
|
|
|
41
42
|
selectedMaxIds?: number;
|
|
42
43
|
}
|
|
43
44
|
|
|
45
|
+
export function groupTableActionBulkActions<T>(
|
|
46
|
+
items: readonly { item: T; placement?: unknown }[],
|
|
47
|
+
): Record<TypeTableActionBulkPlacement, T[]> {
|
|
48
|
+
const groups: Record<TypeTableActionBulkPlacement, T[]> = { start: [], end: [] };
|
|
49
|
+
for (const { item, placement } of items) {
|
|
50
|
+
groups[placement === 'end' ? 'end' : 'start'].push(item);
|
|
51
|
+
}
|
|
52
|
+
return groups;
|
|
53
|
+
}
|
|
54
|
+
|
|
44
55
|
export function resolveTableActionBulkDynamicProps(
|
|
45
56
|
options: IResourceTableActionBulkOptionsBase | undefined,
|
|
46
57
|
selection: IResourceTableSelectionPayload,
|
|
@@ -2,6 +2,7 @@ import assert from 'node:assert/strict';
|
|
|
2
2
|
import test from 'node:test';
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
|
+
groupTableActionBulkActions,
|
|
5
6
|
limitPickerRowSelection,
|
|
6
7
|
reconcileSelection,
|
|
7
8
|
resolvePickerSelectionMax,
|
|
@@ -11,6 +12,28 @@ import {
|
|
|
11
12
|
selectionRowIds,
|
|
12
13
|
} from '../../src/lib/selection.ts';
|
|
13
14
|
|
|
15
|
+
test('bulk actions group by placement without changing declaration order', () => {
|
|
16
|
+
const items = [
|
|
17
|
+
{ item: 'create' },
|
|
18
|
+
{ item: 'columnConfig', placement: 'end' },
|
|
19
|
+
{ item: 'delete' },
|
|
20
|
+
{ item: 'export', placement: 'end' },
|
|
21
|
+
{ item: 'unknown', placement: 'other' },
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
assert.deepEqual(groupTableActionBulkActions(items), {
|
|
25
|
+
start: ['create', 'delete', 'unknown'],
|
|
26
|
+
end: ['columnConfig', 'export'],
|
|
27
|
+
});
|
|
28
|
+
assert.deepEqual(items, [
|
|
29
|
+
{ item: 'create' },
|
|
30
|
+
{ item: 'columnConfig', placement: 'end' },
|
|
31
|
+
{ item: 'delete' },
|
|
32
|
+
{ item: 'export', placement: 'end' },
|
|
33
|
+
{ item: 'unknown', placement: 'other' },
|
|
34
|
+
]);
|
|
35
|
+
});
|
|
36
|
+
|
|
14
37
|
test('selection maximum uses action metadata before the frontend fallback', () => {
|
|
15
38
|
assert.equal(resolveSelectionMaxIds(undefined), 100);
|
|
16
39
|
assert.equal(resolveSelectionMaxIds(100), 100);
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { TypeControllerInnerProps } from 'zova';
|
|
2
|
+
|
|
3
|
+
import { defineComponent } from 'vue';
|
|
4
|
+
import { prepareComponentOptions, useController } from 'zova';
|
|
5
|
+
|
|
6
|
+
import type { ControllerActionColumnConfigProps } from '../../component/actionColumnConfig/controller.jsx';
|
|
7
|
+
|
|
8
|
+
import { ControllerActionColumnConfig } from '../../component/actionColumnConfig/controller.jsx';
|
|
9
|
+
export type ZActionColumnConfigProps = {
|
|
10
|
+
controllerRef?: (ref: ControllerActionColumnConfig) => void;
|
|
11
|
+
} & ControllerActionColumnConfigProps;
|
|
12
|
+
|
|
13
|
+
type ControllerInnerProps = TypeControllerInnerProps<
|
|
14
|
+
ControllerActionColumnConfigProps,
|
|
15
|
+
keyof typeof ControllerActionColumnConfig.$propsDefault
|
|
16
|
+
>;
|
|
17
|
+
declare module 'zova-module-basic-table' {
|
|
18
|
+
export interface ControllerActionColumnConfig {
|
|
19
|
+
$props: ControllerInnerProps;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const ZActionColumnConfig = defineComponent((_props: ZActionColumnConfigProps) => {
|
|
24
|
+
useController(ControllerActionColumnConfig, undefined, undefined);
|
|
25
|
+
return () => {};
|
|
26
|
+
}, prepareComponentOptions(ControllerActionColumnConfig.$componentOptions));
|
|
27
|
+
declare module 'zova-module-a-bean' {
|
|
28
|
+
export interface IVonaComponentRecord {
|
|
29
|
+
'basic-table:actionColumnConfig': ControllerActionColumnConfigProps;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// eslint-disable
|
|
2
2
|
/** controller: begin */
|
|
3
|
+
export * from '../component/actionColumnConfig/controller.jsx';
|
|
3
4
|
export * from '../component/actionCreate/controller.jsx';
|
|
4
5
|
export * from '../component/actionDeleteBulk/controller.jsx';
|
|
5
6
|
export * from '../component/table/controller.jsx';
|
|
@@ -11,6 +12,11 @@ declare module 'zova' {
|
|
|
11
12
|
}
|
|
12
13
|
declare module 'zova-module-basic-table' {
|
|
13
14
|
|
|
15
|
+
export interface ControllerActionColumnConfig {
|
|
16
|
+
/** @internal */
|
|
17
|
+
get scope(): ScopeModuleBasicTable;
|
|
18
|
+
}
|
|
19
|
+
|
|
14
20
|
export interface ControllerActionCreate {
|
|
15
21
|
/** @internal */
|
|
16
22
|
get scope(): ScopeModuleBasicTable;
|
|
@@ -28,13 +34,15 @@ declare module 'zova-module-basic-table' {
|
|
|
28
34
|
}
|
|
29
35
|
/** controller: end */
|
|
30
36
|
/** controller: begin */
|
|
31
|
-
import {
|
|
32
|
-
import {
|
|
33
|
-
import {
|
|
37
|
+
import type { ControllerActionColumnConfig } from '../component/actionColumnConfig/controller.jsx';
|
|
38
|
+
import type { ControllerActionCreate } from '../component/actionCreate/controller.jsx';
|
|
39
|
+
import type { ControllerActionDeleteBulk } from '../component/actionDeleteBulk/controller.jsx';
|
|
40
|
+
import type { ControllerTable } from '../component/table/controller.jsx';
|
|
34
41
|
import 'zova';
|
|
35
42
|
declare module 'zova' {
|
|
36
43
|
export interface IBeanRecordLocal {
|
|
37
|
-
'basic-table.controller.
|
|
44
|
+
'basic-table.controller.actionColumnConfig': ControllerActionColumnConfig;
|
|
45
|
+
'basic-table.controller.actionCreate': ControllerActionCreate;
|
|
38
46
|
'basic-table.controller.actionDeleteBulk': ControllerActionDeleteBulk;
|
|
39
47
|
'basic-table.controller.table': ControllerTable;
|
|
40
48
|
}
|
|
@@ -42,6 +50,8 @@ declare module 'zova' {
|
|
|
42
50
|
/** controller: end */
|
|
43
51
|
|
|
44
52
|
/** components: begin */
|
|
53
|
+
export * from './component/actionColumnConfig.js';
|
|
54
|
+
import { ZActionColumnConfig } from './component/actionColumnConfig.js';
|
|
45
55
|
export * from './component/actionCreate.js';
|
|
46
56
|
import { ZActionCreate } from './component/actionCreate.js';
|
|
47
57
|
export * from './component/actionDeleteBulk.js';
|
|
@@ -49,19 +59,22 @@ import { ZActionDeleteBulk } from './component/actionDeleteBulk.js';
|
|
|
49
59
|
export * from './component/table.js';
|
|
50
60
|
import { ZTable } from './component/table.js';
|
|
51
61
|
export const components = {
|
|
52
|
-
'
|
|
62
|
+
'actionColumnConfig': ZActionColumnConfig,
|
|
63
|
+
'actionCreate': ZActionCreate,
|
|
53
64
|
'actionDeleteBulk': ZActionDeleteBulk,
|
|
54
65
|
'table': ZTable,
|
|
55
66
|
};
|
|
56
67
|
import 'zova';
|
|
57
68
|
declare module 'zova' {
|
|
58
69
|
export interface IComponentRecord {
|
|
59
|
-
'basic-table:
|
|
70
|
+
'basic-table:actionColumnConfig': ControllerActionColumnConfig;
|
|
71
|
+
'basic-table:actionCreate': ControllerActionCreate;
|
|
60
72
|
'basic-table:actionDeleteBulk': ControllerActionDeleteBulk;
|
|
61
73
|
'basic-table:table': ControllerTable;
|
|
62
74
|
}
|
|
63
75
|
export interface IZovaComponentRecord {
|
|
64
|
-
'basic-table:
|
|
76
|
+
'basic-table:actionColumnConfig': typeof ZActionColumnConfig;
|
|
77
|
+
'basic-table:actionCreate': typeof ZActionCreate;
|
|
65
78
|
'basic-table:actionDeleteBulk': typeof ZActionDeleteBulk;
|
|
66
79
|
'basic-table:table': typeof ZTable;
|
|
67
80
|
}
|
|
@@ -84,7 +97,7 @@ declare module 'zova-module-basic-table' {
|
|
|
84
97
|
}
|
|
85
98
|
/** render: end */
|
|
86
99
|
/** render: begin */
|
|
87
|
-
import { RenderTable } from '../component/table/render.jsx';
|
|
100
|
+
import type { RenderTable } from '../component/table/render.jsx';
|
|
88
101
|
import 'zova';
|
|
89
102
|
declare module 'zova' {
|
|
90
103
|
export interface IBeanRecordLocal {
|
|
@@ -161,10 +174,10 @@ declare module 'zova-module-basic-table' {
|
|
|
161
174
|
}
|
|
162
175
|
/** tableCell: end */
|
|
163
176
|
/** tableCell: begin */
|
|
164
|
-
import { TableCellActionDelete } from '../bean/tableCell.actionDelete.jsx';
|
|
165
|
-
import { TableCellActionOperationsRow } from '../bean/tableCell.actionOperationsRow.jsx';
|
|
166
|
-
import { TableCellActionUpdate } from '../bean/tableCell.actionUpdate.jsx';
|
|
167
|
-
import { TableCellActionView } from '../bean/tableCell.actionView.jsx';
|
|
177
|
+
import type { TableCellActionDelete } from '../bean/tableCell.actionDelete.jsx';
|
|
178
|
+
import type { TableCellActionOperationsRow } from '../bean/tableCell.actionOperationsRow.jsx';
|
|
179
|
+
import type { TableCellActionUpdate } from '../bean/tableCell.actionUpdate.jsx';
|
|
180
|
+
import type { TableCellActionView } from '../bean/tableCell.actionView.jsx';
|
|
168
181
|
import 'zova';
|
|
169
182
|
declare module 'zova' {
|
|
170
183
|
export interface IBeanRecordGeneral {
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import type { IComponentOptions } from 'zova';
|
|
2
|
+
import type {
|
|
3
|
+
IJsxRenderContextPage,
|
|
4
|
+
IResourceTableActionBulkPropsBase,
|
|
5
|
+
ISchemaObjectExtensionField,
|
|
6
|
+
} from 'zova-module-a-openapi';
|
|
7
|
+
|
|
8
|
+
import { BeanControllerBase, Use } from 'zova';
|
|
9
|
+
import { Controller } from 'zova-module-a-bean';
|
|
10
|
+
import { ZIcon } from 'zova-module-a-icon';
|
|
11
|
+
import { ITableLayout } from 'zova-module-a-table';
|
|
12
|
+
import { reconcileTableLayout } from 'zova-module-a-table';
|
|
13
|
+
|
|
14
|
+
declare module 'zova-module-a-openapi' {
|
|
15
|
+
export interface IResourceTableActionBulkRecord {
|
|
16
|
+
'basic-table:actionColumnConfig'?: ControllerActionColumnConfigProps;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface ControllerActionColumnConfigProps extends IResourceTableActionBulkPropsBase {}
|
|
21
|
+
|
|
22
|
+
@Controller()
|
|
23
|
+
export class ControllerActionColumnConfig extends BeanControllerBase {
|
|
24
|
+
static $propsDefault = { class: 'btn join-item' };
|
|
25
|
+
static $componentOptions: IComponentOptions = { inheritAttrs: false, deepExtendDefault: true };
|
|
26
|
+
|
|
27
|
+
private _saving = false;
|
|
28
|
+
private _error: string | undefined;
|
|
29
|
+
private _reset = false;
|
|
30
|
+
private _draft: ITableLayout | undefined;
|
|
31
|
+
|
|
32
|
+
@Use({ injectionScope: 'host' })
|
|
33
|
+
$$renderContext: IJsxRenderContextPage;
|
|
34
|
+
|
|
35
|
+
get $$page() {
|
|
36
|
+
return this.$$renderContext.$$page;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
get seed() {
|
|
40
|
+
return this.$$page.columnConfigSeed;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
protected render() {
|
|
44
|
+
// const disabled =
|
|
45
|
+
// this.$props.disabled === true || this.$props.dynamicDisabled === true || !this.seed;
|
|
46
|
+
const disabled = this.$props.disabled === true || this.$props.dynamicDisabled === true;
|
|
47
|
+
return (
|
|
48
|
+
<button
|
|
49
|
+
class={[this.$props.class, 'btn-square']}
|
|
50
|
+
type="button"
|
|
51
|
+
disabled={disabled}
|
|
52
|
+
aria-label={this.scope.locale.ColumnConfiguration()}
|
|
53
|
+
title={this.scope.locale.ColumnConfiguration()}
|
|
54
|
+
onClick={() => this._open()}
|
|
55
|
+
>
|
|
56
|
+
<ZIcon name="::settings" width={20}></ZIcon>
|
|
57
|
+
</button>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
private _open() {
|
|
62
|
+
const seed = this.seed;
|
|
63
|
+
if (!seed) return;
|
|
64
|
+
this._error = undefined;
|
|
65
|
+
this._reset = false;
|
|
66
|
+
this._draft = this._cloneLayout(seed.layout);
|
|
67
|
+
const dialog = this.$appModal.dialog(
|
|
68
|
+
{
|
|
69
|
+
title: this.scope.locale.ColumnConfiguration(),
|
|
70
|
+
slotDefault: () => this._renderDialog(),
|
|
71
|
+
slotActions: modal => (
|
|
72
|
+
<>
|
|
73
|
+
{this._error && (
|
|
74
|
+
<span class="mr-auto text-sm text-error" role="alert">
|
|
75
|
+
{this._error}
|
|
76
|
+
</span>
|
|
77
|
+
)}
|
|
78
|
+
<button
|
|
79
|
+
class="btn btn-ghost"
|
|
80
|
+
type="button"
|
|
81
|
+
disabled={this._saving}
|
|
82
|
+
onClick={modal.close}
|
|
83
|
+
>
|
|
84
|
+
{this.scope.locale.Cancel()}
|
|
85
|
+
</button>
|
|
86
|
+
<button
|
|
87
|
+
class="btn btn-primary"
|
|
88
|
+
type="button"
|
|
89
|
+
disabled={this._saving}
|
|
90
|
+
onClick={() => this._save(dialog)}
|
|
91
|
+
>
|
|
92
|
+
{this._saving && <span class="loading loading-spinner loading-xs" />}
|
|
93
|
+
{this.scope.locale.Save()}
|
|
94
|
+
</button>
|
|
95
|
+
</>
|
|
96
|
+
),
|
|
97
|
+
},
|
|
98
|
+
{ closeOnBackdrop: false, closeOnEscape: false, maxWidth: 560, showCloseButton: true },
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
private _renderDialog() {
|
|
103
|
+
const draft = this._draft;
|
|
104
|
+
const properties = this.seed?.properties ?? [];
|
|
105
|
+
return (
|
|
106
|
+
<div class="max-h-[60vh] space-y-2 overflow-y-auto">
|
|
107
|
+
<div class="text-sm text-base-content/70">{this.scope.locale.Columns()}</div>
|
|
108
|
+
{draft?.columns.map((column, index) => {
|
|
109
|
+
const property = properties.find(item => item.key === column.key);
|
|
110
|
+
const previous = draft.columns[index - 1];
|
|
111
|
+
const next = draft.columns[index + 1];
|
|
112
|
+
const canMoveUp =
|
|
113
|
+
!!previous && this._sameFixedRegion(properties, column.key, previous.key);
|
|
114
|
+
const canMoveDown = !!next && this._sameFixedRegion(properties, column.key, next.key);
|
|
115
|
+
return (
|
|
116
|
+
<div
|
|
117
|
+
key={column.key}
|
|
118
|
+
class="flex items-center gap-2 rounded-box border border-base-300 p-2"
|
|
119
|
+
>
|
|
120
|
+
<input
|
|
121
|
+
class="checkbox checkbox-sm"
|
|
122
|
+
type="checkbox"
|
|
123
|
+
checked={column.visible}
|
|
124
|
+
aria-label={`${this.scope.locale.Visible()} ${property?.title ?? column.key}`}
|
|
125
|
+
onChange={(event: Event) => {
|
|
126
|
+
this._reset = false;
|
|
127
|
+
column.visible = (event.target as HTMLInputElement).checked;
|
|
128
|
+
}}
|
|
129
|
+
/>
|
|
130
|
+
<span class="min-w-0 flex-1 truncate">{property?.title ?? column.key}</span>
|
|
131
|
+
<input
|
|
132
|
+
class="input input-bordered input-sm w-20"
|
|
133
|
+
type="number"
|
|
134
|
+
min="1"
|
|
135
|
+
max="2000"
|
|
136
|
+
value={typeof column.width === 'number' ? column.width : ''}
|
|
137
|
+
aria-label={`${this.scope.locale.Width()} ${property?.title ?? column.key}`}
|
|
138
|
+
onInput={(event: Event) => {
|
|
139
|
+
this._reset = false;
|
|
140
|
+
const value = (event.target as HTMLInputElement).value;
|
|
141
|
+
column.width = value === '' ? 'auto' : Number(value);
|
|
142
|
+
}}
|
|
143
|
+
/>
|
|
144
|
+
<label class="flex items-center gap-1 text-xs">
|
|
145
|
+
<input
|
|
146
|
+
class="checkbox checkbox-sm"
|
|
147
|
+
type="checkbox"
|
|
148
|
+
checked={column.width === 'auto'}
|
|
149
|
+
aria-label={`${this.scope.locale.Auto()} ${property?.title ?? column.key}`}
|
|
150
|
+
onChange={(event: Event) => {
|
|
151
|
+
if ((event.target as HTMLInputElement).checked) {
|
|
152
|
+
this._reset = false;
|
|
153
|
+
column.width = 'auto';
|
|
154
|
+
}
|
|
155
|
+
}}
|
|
156
|
+
/>
|
|
157
|
+
{this.scope.locale.Auto()}
|
|
158
|
+
</label>
|
|
159
|
+
<button
|
|
160
|
+
class="btn btn-ghost btn-xs"
|
|
161
|
+
type="button"
|
|
162
|
+
disabled={!canMoveUp || this._saving}
|
|
163
|
+
aria-label={this.scope.locale.MoveUp()}
|
|
164
|
+
onClick={() => this._moveColumn(index, -1)}
|
|
165
|
+
>
|
|
166
|
+
↑
|
|
167
|
+
</button>
|
|
168
|
+
<button
|
|
169
|
+
class="btn btn-ghost btn-xs"
|
|
170
|
+
type="button"
|
|
171
|
+
disabled={!canMoveDown || this._saving}
|
|
172
|
+
aria-label={this.scope.locale.MoveDown()}
|
|
173
|
+
onClick={() => this._moveColumn(index, 1)}
|
|
174
|
+
>
|
|
175
|
+
↓
|
|
176
|
+
</button>
|
|
177
|
+
</div>
|
|
178
|
+
);
|
|
179
|
+
})}
|
|
180
|
+
<button
|
|
181
|
+
class="btn btn-outline btn-sm"
|
|
182
|
+
type="button"
|
|
183
|
+
disabled={this._saving}
|
|
184
|
+
onClick={() => this._resetDraft()}
|
|
185
|
+
>
|
|
186
|
+
{this.scope.locale.Reset() as string}
|
|
187
|
+
</button>
|
|
188
|
+
</div>
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
private _cloneLayout(layout: ITableLayout): ITableLayout {
|
|
193
|
+
return {
|
|
194
|
+
version: 1,
|
|
195
|
+
...(layout.schemaFingerprint ? { schemaFingerprint: layout.schemaFingerprint } : {}),
|
|
196
|
+
columns: layout.columns.map(column => ({ ...column })),
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
private _sameFixedRegion(
|
|
201
|
+
properties: readonly ISchemaObjectExtensionField[],
|
|
202
|
+
leftKey: string,
|
|
203
|
+
rightKey: string,
|
|
204
|
+
) {
|
|
205
|
+
const left = properties.find(item => item.key === leftKey)?.rest?.fixed;
|
|
206
|
+
const right = properties.find(item => item.key === rightKey)?.rest?.fixed;
|
|
207
|
+
return (left ?? 'center') === (right ?? 'center');
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
private _moveColumn(index: number, offset: -1 | 1) {
|
|
211
|
+
const columns = this._draft?.columns;
|
|
212
|
+
const properties = this.seed?.properties;
|
|
213
|
+
if (!columns || !properties) return;
|
|
214
|
+
const target = index + offset;
|
|
215
|
+
if (
|
|
216
|
+
target < 0 ||
|
|
217
|
+
target >= columns.length ||
|
|
218
|
+
!this._sameFixedRegion(properties, columns[index].key, columns[target].key)
|
|
219
|
+
) {
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
this._reset = false;
|
|
223
|
+
[columns[index], columns[target]] = [columns[target], columns[index]];
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
private _resetDraft() {
|
|
227
|
+
const seed = this.seed;
|
|
228
|
+
if (!seed || this._saving) return;
|
|
229
|
+
this._reset = true;
|
|
230
|
+
this._draft = reconcileTableLayout(seed.properties);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
private async _save(dialog: { close: () => void }) {
|
|
234
|
+
const seed = this.seed;
|
|
235
|
+
const draft = this._draft;
|
|
236
|
+
if (!seed || !draft || this._saving) return;
|
|
237
|
+
this._saving = true;
|
|
238
|
+
this._error = undefined;
|
|
239
|
+
try {
|
|
240
|
+
if (this._reset) {
|
|
241
|
+
await this.$$page.resetColumnConfig();
|
|
242
|
+
} else {
|
|
243
|
+
await this.$$page.saveColumnConfig(this._normalizeLayout(seed.properties, draft));
|
|
244
|
+
}
|
|
245
|
+
this._reset = false;
|
|
246
|
+
dialog.close();
|
|
247
|
+
} catch (error) {
|
|
248
|
+
this._error = error instanceof Error ? error.message : String(error);
|
|
249
|
+
} finally {
|
|
250
|
+
this._saving = false;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
private _normalizeLayout(
|
|
255
|
+
properties: readonly ISchemaObjectExtensionField[],
|
|
256
|
+
draft: ITableLayout,
|
|
257
|
+
): ITableLayout {
|
|
258
|
+
return reconcileTableLayout(properties, draft);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
@@ -22,6 +22,7 @@ export class RenderTable<TData extends {} = {}> extends BeanRenderBase {
|
|
|
22
22
|
tableScope={this.$props.tableScope}
|
|
23
23
|
getColumns={this.$props.getColumns}
|
|
24
24
|
getRowId={this.$props.getRowId}
|
|
25
|
+
layout={this.$props.layout}
|
|
25
26
|
controllerRef={ref => {
|
|
26
27
|
this.$props?.tableRef?.(ref);
|
|
27
28
|
}}
|
|
@@ -35,10 +36,9 @@ export class RenderTable<TData extends {} = {}> extends BeanRenderBase {
|
|
|
35
36
|
private _getColumnStyle(column: any): CSSProperties {
|
|
36
37
|
const rest = (column.columnDef.meta as any)?.rest;
|
|
37
38
|
const fixed = column.getIsPinned();
|
|
38
|
-
const width =
|
|
39
|
+
const width = column.getSize();
|
|
39
40
|
return {
|
|
40
|
-
width,
|
|
41
|
-
minWidth: width,
|
|
41
|
+
...(typeof rest?.width === 'number' ? { width: `${width}px`, minWidth: `${width}px` } : {}),
|
|
42
42
|
textAlign: rest?.align,
|
|
43
43
|
position: fixed ? ('sticky' as const) : undefined,
|
|
44
44
|
left: fixed === 'left' ? `${column.getStart('left')}px` : undefined,
|
|
@@ -5,4 +5,14 @@ export default {
|
|
|
5
5
|
SelectRow: 'Select row %s',
|
|
6
6
|
DeleteBulk: 'Bulk Delete',
|
|
7
7
|
DeleteBulkConfirm: 'Are you sure you want to bulk delete %d selected items?',
|
|
8
|
+
ColumnConfiguration: 'Column configuration',
|
|
9
|
+
Columns: 'Columns',
|
|
10
|
+
Visible: 'Visible',
|
|
11
|
+
Width: 'Width',
|
|
12
|
+
Auto: 'Auto',
|
|
13
|
+
MoveUp: 'Move up',
|
|
14
|
+
MoveDown: 'Move down',
|
|
15
|
+
Save: 'Save',
|
|
16
|
+
Cancel: 'Cancel',
|
|
17
|
+
Reset: 'Reset',
|
|
8
18
|
};
|