@boarteam/boar-pack-common-frontend 4.0.0 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/dist/tools/safetyRun.d.ts +2 -1
  2. package/dist/tools/safetyRun.js +2 -1
  3. package/dist/tools/safetyRun.js.map +1 -1
  4. package/package.json +2 -3
  5. package/src/components/ChangesModal/ChangesModal.tsx +0 -260
  6. package/src/components/ChangesModal/ChangesTab.tsx +0 -51
  7. package/src/components/ChangesModal/ConflictsTab.tsx +0 -176
  8. package/src/components/ChangesModal/ErrorsTab.tsx +0 -49
  9. package/src/components/ChangesModal/NewRecordsTab.tsx +0 -48
  10. package/src/components/ChangesModal/ResultsTab.tsx +0 -28
  11. package/src/components/ChangesModal/index.ts +0 -1
  12. package/src/components/Comment/Comment.tsx +0 -53
  13. package/src/components/Comment/CommentAvatar.tsx +0 -34
  14. package/src/components/Comment/CommentForm.tsx +0 -36
  15. package/src/components/Comment/CommentFormModal.tsx +0 -31
  16. package/src/components/Comment/index.ts +0 -5
  17. package/src/components/Descriptions/Descriptions.tsx +0 -328
  18. package/src/components/Descriptions/DescriptionsCreateModal.tsx +0 -65
  19. package/src/components/Descriptions/descriptionTypes.ts +0 -73
  20. package/src/components/Descriptions/index.ts +0 -5
  21. package/src/components/Descriptions/useContentViewMode.tsx +0 -28
  22. package/src/components/Descriptions/useDescriptionColumns.ts +0 -42
  23. package/src/components/Inputs/DateRange.tsx +0 -75
  24. package/src/components/Inputs/MultiStringSelect.tsx +0 -20
  25. package/src/components/Inputs/NumberInputHandlingNewRecord.tsx +0 -11
  26. package/src/components/Inputs/NumberSwitcher.tsx +0 -27
  27. package/src/components/Inputs/Password.tsx +0 -33
  28. package/src/components/Inputs/RelationSelect.tsx +0 -108
  29. package/src/components/Inputs/SearchSelect.tsx +0 -93
  30. package/src/components/Inputs/filterDropdowns.tsx +0 -144
  31. package/src/components/Inputs/index.ts +0 -9
  32. package/src/components/Inputs/useCheckConnection.tsx +0 -79
  33. package/src/components/List/List.tsx +0 -266
  34. package/src/components/List/index.ts +0 -3
  35. package/src/components/List/listTypes.ts +0 -31
  36. package/src/components/QuestionMarkHint/QuestionMarkHint.tsx +0 -33
  37. package/src/components/QuestionMarkHint/index.ts +0 -1
  38. package/src/components/Table/BulkDeleteButton.tsx +0 -55
  39. package/src/components/Table/BulkEditButton.tsx +0 -160
  40. package/src/components/Table/CreateEntityModal.tsx +0 -85
  41. package/src/components/Table/DeleteButton.tsx +0 -44
  42. package/src/components/Table/Table.tsx +0 -294
  43. package/src/components/Table/getTableDataQueryParams.ts +0 -79
  44. package/src/components/Table/index.ts +0 -7
  45. package/src/components/Table/tableTools.ts +0 -244
  46. package/src/components/Table/tableTypes.ts +0 -148
  47. package/src/components/Table/useBulkEditing.tsx +0 -128
  48. package/src/components/Table/useColumnsSets.tsx +0 -142
  49. package/src/components/Table/useCreation.tsx +0 -114
  50. package/src/components/Table/useEditableTable.tsx +0 -86
  51. package/src/components/Table/useImportExport.tsx +0 -315
  52. package/src/components/index.ts +0 -6
  53. package/src/index.ts +0 -2
  54. package/src/tools/ApiError.ts +0 -18
  55. package/src/tools/WebsocketClient.ts +0 -146
  56. package/src/tools/index.ts +0 -6
  57. package/src/tools/numberTools.ts +0 -6
  58. package/src/tools/safetyRun.ts +0 -5
  59. package/src/tools/useFullscreen.tsx +0 -62
  60. package/src/tools/useTabs.ts +0 -17
@@ -1,294 +0,0 @@
1
- import ProTable, { ActionType } from "@ant-design/pro-table";
2
- import { useEffect, useRef, useState } from "react";
3
- import { Modal } from "antd";
4
- import { TFilterParams, TFilters, TSort, TTableProps } from "./tableTypes";
5
- import useColumnsSets from "./useColumnsSets";
6
- import { createStyles } from "antd-style";
7
- import { Descriptions } from "../Descriptions";
8
- import { KEY_SYMBOL, useCreation } from "./useCreation";
9
- import { getTableDataQueryParams } from "./getTableDataQueryParams";
10
- import { useEditableTable } from "./useEditableTable";
11
- import { useBulkEditing } from "./useBulkEditing";
12
- import { useImportExport } from "./useImportExport";
13
- import { ChangesModal } from "../ChangesModal";
14
- import { ProColumns } from "@ant-design/pro-components";
15
-
16
- const useStyles = createStyles(() => {
17
- return {
18
- table: {
19
- '.ant-pro-table-alert': {
20
- display: 'none',
21
- },
22
- }
23
- }
24
- })
25
-
26
- const Table = <
27
- Entity extends Record<string | symbol, any>,
28
- CreateDto = Entity,
29
- UpdateDto = Entity,
30
- TEntityParams = {},
31
- TPathParams extends Record<string, string | number> = {},
32
- TImportRequest = { // TODO: Add to Table types
33
- new?: Array<Entity>,
34
- modified?: Array<Entity>,
35
- },
36
- >(
37
- {
38
- getAll,
39
- onCreate,
40
- onUpdate,
41
- onUpdateMany,
42
- onDelete,
43
- onDeleteMany,
44
- exportUrl,
45
- exportParams,
46
- pathParams,
47
- idColumnName = 'id',
48
- entityToCreateDto,
49
- entityToUpdateDto,
50
- createNewDefaultParams,
51
- editableRecord,
52
- afterSave,
53
- actionRef: actionRefProp,
54
- editable,
55
- defaultSort = ['createdAt', 'DESC'],
56
- searchableColumns = [],
57
- viewOnly = false,
58
- columns = [],
59
- columnsSets,
60
- columnsState: managedColumnsState,
61
- columnsSetSelect: managedColumnsSetSelect,
62
- popupCreation = false,
63
- toolBarRender,
64
- params,
65
- editPopupTitle,
66
- createPopupTitle,
67
- descriptionsMainTitle,
68
- importConfig,
69
- ...rest
70
- }: TTableProps<Entity,
71
- CreateDto,
72
- UpdateDto,
73
- TEntityParams,
74
- TPathParams>
75
- ) => {
76
- const actionRefComponent = useRef<ActionType>();
77
- const actionRef = actionRefProp || actionRefComponent;
78
- const [updatePopupData, setUpdatePopupData] = useState<Partial<Entity> | undefined>();
79
- const { styles } = useStyles();
80
- const flatColumns: ProColumns<Entity>[] = [];
81
- columns.forEach((column) => {
82
- if (column.children && column.children.length > 0) {
83
- flatColumns.push(...column.children);
84
- } else {
85
- flatColumns.push(column);
86
- }
87
- });
88
-
89
- const {
90
- editableConfig,
91
- } = useEditableTable<Entity, CreateDto, UpdateDto, TPathParams>({
92
- actionRef,
93
- pathParams,
94
- onCreate,
95
- onUpdate,
96
- onDelete,
97
- entityToCreateDto,
98
- entityToUpdateDto,
99
- afterSave,
100
- editable,
101
- onDeleteMany,
102
- onUpdateMany,
103
- });
104
-
105
- const {
106
- rowSelection,
107
- setSelectedRecords,
108
- setLastRequest,
109
- bulkEditButton,
110
- bulkDeleteButton,
111
- messagesContext,
112
- } = useBulkEditing<Entity, TPathParams, UpdateDto, TEntityParams & TFilterParams>({
113
- actionRef,
114
- columns,
115
- idColumnName,
116
- onDeleteMany,
117
- onUpdateMany,
118
- entityToUpdateDto,
119
- pathParams,
120
- });
121
-
122
- const {
123
- creationModal,
124
- createButton,
125
- } = useCreation<Entity, CreateDto, TPathParams>({
126
- title: createPopupTitle,
127
- mainTitle: descriptionsMainTitle,
128
- columns: columns,
129
- idColumnName: idColumnName,
130
- onCreate,
131
- pathParams,
132
- entityToCreateDto,
133
- actionRef,
134
- createButtonSize: rest.size,
135
- popupCreation,
136
- createNewDefaultParams,
137
- });
138
-
139
- const {
140
- exportButton,
141
- importButton,
142
- setLastQueryParams,
143
- diffResult,
144
- setDiffResult,
145
- } = useImportExport<Entity, TPathParams>({
146
- columns: flatColumns,
147
- exportUrl,
148
- exportParams,
149
- changedRecordsColumnsConfig: importConfig?.changedRecordsColumnsConfig,
150
- relationalFields: importConfig?.relationalFields,
151
- })
152
-
153
- useEffect(() => {
154
- setUpdatePopupData(editableRecord);
155
- actionRef?.current?.reload();
156
- }, [editableRecord, JSON.stringify(pathParams), JSON.stringify(params)]);
157
-
158
- const {
159
- columnsSetSelect: localColumnsSetSelect,
160
- columnsState: localColumnsState,
161
- } = useColumnsSets<Entity>({
162
- columns,
163
- columnsSets,
164
- });
165
-
166
- const columnsState = managedColumnsState ?? localColumnsState;
167
- const columnsSetSelect = managedColumnsSetSelect ?? localColumnsSetSelect;
168
-
169
- const request = async (
170
- params: TFilterParams,
171
- sort: TSort = {},
172
- filters: TFilters = {},
173
- ) => {
174
- const queryParams = getTableDataQueryParams({
175
- params,
176
- sort,
177
- filters,
178
- pathParams,
179
- defaultSort,
180
- searchableColumns,
181
- columns,
182
- idColumnName,
183
- columnsState,
184
- });
185
-
186
- const result = await getAll(queryParams);
187
-
188
- setSelectedRecords([]);
189
- setLastRequest([
190
- queryParams,
191
- result,
192
- ]);
193
- setLastQueryParams(queryParams);
194
- return result;
195
- }
196
-
197
- return (<>
198
- <ProTable<Entity, TEntityParams & TFilterParams>
199
- actionRef={actionRef}
200
- className={styles.table}
201
- request={request}
202
- rowKey={record => record[KEY_SYMBOL] ?? (Array.isArray(idColumnName) ? idColumnName.map(colName => record[colName]).join('-') : record[idColumnName])}
203
- options={{
204
- fullScreen: true,
205
- reload: true,
206
- search: {
207
- allowClear: true,
208
- },
209
- density: true,
210
- setting: {
211
- draggable: false,
212
- checkable: true,
213
- checkedReset: true,
214
- listsHeight: 500,
215
- },
216
- }}
217
- scroll={{
218
- x: 'max-content',
219
- }}
220
- bordered
221
- search={false}
222
- editable={editableConfig}
223
- toolBarRender={toolBarRender === false ? false : (...args) => [
224
- columnsSetSelect?.() || null,
225
- !viewOnly && onUpdateMany
226
- ? bulkEditButton
227
- : null,
228
- !viewOnly && onDeleteMany
229
- ? bulkDeleteButton
230
- : null,
231
- !viewOnly && createButton || null,
232
- !viewOnly && importConfig?.onImport && importButton || null,
233
- exportUrl && exportButton || null,
234
- ...toolBarRender && toolBarRender(...args) || [],
235
- ]}
236
- columns={columns}
237
- defaultSize='small'
238
- columnsState={columnsState}
239
- params={params}
240
- {
241
- ...(
242
- !viewOnly && (onUpdateMany || onDeleteMany)
243
- ? { rowSelection }
244
- : {}
245
- )
246
- }
247
- {...rest}
248
- />
249
-
250
- {creationModal}
251
-
252
- <Modal
253
- title={editPopupTitle}
254
- open={updatePopupData !== undefined}
255
- width='80%'
256
- closeIcon={true}
257
- footer={null}
258
- onCancel={() => {
259
- actionRef?.current?.reload();
260
- setUpdatePopupData(undefined);
261
- }}
262
- >
263
- <Descriptions<Entity, CreateDto, UpdateDto, TPathParams>
264
- mainTitle={descriptionsMainTitle}
265
- columns={columns ?? []}
266
- entity={updatePopupData}
267
- canEdit={true}
268
- onUpdate={onUpdate}
269
- entityToUpdateDto={entityToUpdateDto}
270
- />
271
- </Modal>
272
- <ChangesModal<
273
- Entity,
274
- TImportRequest
275
- >
276
- {...(diffResult && { changes: diffResult })}
277
- onCommit={importConfig?.onImport}
278
- onClose={() => {
279
- actionRef.current?.reload();
280
- setDiffResult(undefined);
281
- }}
282
- originRecordsColumnsConfig={flatColumns}
283
- changedRecordsColumnsConfig={importConfig?.changedRecordsColumnsConfig}
284
- createdRecordsColumnsConfig={{
285
- columnsSets,
286
- columns: importConfig?.createdRecordsColumnsConfig
287
- }}
288
- relationalFields={importConfig?.relationalFields}
289
- />
290
- {messagesContext}
291
- </>);
292
- };
293
-
294
- export default Table;
@@ -1,79 +0,0 @@
1
- import { TFilterParams, TFilters, TGetAllParams, TSearchableColumn, TSort } from "./tableTypes";
2
- import { applyKeywordToSearch, buildJoinFields, collectFieldsFromColumns, getFiltersSearch } from "./tableTools";
3
- import { QuerySortArr } from "@nestjsx/crud-request";
4
- import { ProColumns } from "@ant-design/pro-components";
5
- import { ColumnStateType } from "@ant-design/pro-table/es/typing";
6
-
7
- export function getTableDataQueryParams<Entity, TPathParams extends Record<string, string | number> = {}>({
8
- params,
9
- sort = {},
10
- filters = {},
11
- pathParams,
12
- defaultSort,
13
- searchableColumns,
14
- columns = [],
15
- idColumnName = 'id',
16
- columnsState,
17
- }: {
18
- params: TFilterParams,
19
- sort?: TSort,
20
- filters?: TFilters,
21
- pathParams: TPathParams,
22
- defaultSort?: QuerySortArr,
23
- searchableColumns?: TSearchableColumn[],
24
- columns?: ProColumns<Entity>[],
25
- idColumnName?: string | string[];
26
- columnsState?: ColumnStateType;
27
- }): TGetAllParams & TPathParams {
28
- const {
29
- current,
30
- pageSize,
31
- keyword,
32
- baseFilters,
33
- join,
34
- sortMap,
35
- ...filtersFromSearchForm
36
- } = params;
37
-
38
- const queryParams: TGetAllParams & TPathParams = {
39
- ...pathParams,
40
- page: current,
41
- limit: pageSize,
42
- };
43
-
44
- const sortBy = Object
45
- .entries(sort)
46
- .reduce<string[]>(
47
- (data: string[], [key, direction]) => {
48
- data.push(`${sortMap?.[key] || key},${direction === 'ascend' ? 'ASC' : 'DESC'}`);
49
- return data;
50
- },
51
- []
52
- );
53
- if (!sortBy.length && defaultSort) {
54
- sortBy.push(defaultSort.join(','));
55
- }
56
- queryParams.sort = sortBy;
57
-
58
- let search = getFiltersSearch({
59
- baseFilters,
60
- filters: {
61
- ...filters,
62
- ...filtersFromSearchForm,
63
- },
64
- searchableColumns,
65
- });
66
- search = applyKeywordToSearch(search, searchableColumns!, columnsState.value!, keyword);
67
- queryParams.s = JSON.stringify(search);
68
-
69
- const { joinSelect, joinFields } = buildJoinFields(join);
70
- queryParams.join = joinSelect;
71
-
72
- queryParams.fields = columns && collectFieldsFromColumns(
73
- columns,
74
- idColumnName,
75
- joinFields,
76
- ) || [];
77
-
78
- return queryParams;
79
- }
@@ -1,7 +0,0 @@
1
- export * from './Table';
2
- export { default as Table } from './Table'
3
- export * from './tableTools';
4
- export * from './tableTypes';
5
- export * from './useColumnsSets';
6
- export { default as useColumnsSets } from './useColumnsSets';
7
- export * from "./useCreation";
@@ -1,244 +0,0 @@
1
- import { CondOperator, QueryJoin, SCondition } from "@nestjsx/crud-request";
2
- import { validate as uuidValidate } from 'uuid';
3
- import { IWithId, TFilters, TSearchableColumn } from "./tableTypes";
4
- import React, { Key } from "react";
5
- import { TColumnsStates } from "./useColumnsSets";
6
-
7
- export function getFiltersSearch({
8
- baseFilters = {},
9
- filters = {},
10
- searchableColumns,
11
- }: {
12
- baseFilters?: TFilters,
13
- filters?: TFilters,
14
- searchableColumns: TSearchableColumn[],
15
- }): SCondition {
16
- const filterKeys = new Set(Object.keys(filters).concat(Object.keys(baseFilters)));
17
- const search: SCondition = { '$and': [] };
18
- searchableColumns.forEach((col) => {
19
- const colDataIndex = Array.isArray(col.field) ? col.field.join('.') : col.field;
20
- const field = col.filterField || colDataIndex;
21
- let operator = col.filterOperator || col.operator;
22
- let value = filters[colDataIndex] || baseFilters[colDataIndex];
23
- filterKeys.delete(colDataIndex);
24
- if (
25
- value === '' ||
26
- value === undefined ||
27
- col.numeric && !Number.isFinite(Number(value)) ||
28
- col.uuid && (typeof value !== 'string' || !uuidValidate(value))
29
- ) {
30
- return;
31
- }
32
-
33
- switch (operator) {
34
- case Operators.between:
35
- if (Array.isArray(value)) {
36
- if (value?.[0] === undefined) {
37
- operator = Operators.lowerOrEquals;
38
- value = value?.[1];
39
- } else if (value?.[1] === undefined) {
40
- operator = Operators.greaterOrEquals;
41
- value = value?.[0];
42
- }
43
- }
44
- break;
45
-
46
- case Operators.isNull:
47
- if (Array.isArray(value)) {
48
- value = value[0];
49
- }
50
-
51
- if (value !== true) {
52
- operator = Operators.notNull;
53
- value = true;
54
- }
55
- break;
56
-
57
- case Operators.notNull:
58
- if (Array.isArray(value)) {
59
- value = value[0];
60
- }
61
-
62
- if (value !== true) {
63
- operator = Operators.isNull;
64
- value = true;
65
- }
66
- break;
67
-
68
- case Operators.in:
69
- case Operators.inLow:
70
- if (Array.isArray(value) && value.length === 1 && value[0] === null) {
71
- operator = Operators.isNull;
72
- value = true;
73
- }
74
- break;
75
-
76
- case Operators.equals:
77
- if (Array.isArray(value) && value.length === 1 && value[0] === null || value === null) {
78
- operator = Operators.isNull;
79
- value = true;
80
- }
81
- break;
82
-
83
- }
84
-
85
- search.$and?.push({ [field]: { [operator]: value } });
86
- });
87
-
88
- if (filterKeys.size) {
89
- throw new Error(`Some filters are not defined in searchableColumns: ${Array.from(filterKeys).join(', ')}`);
90
- }
91
-
92
- return search;
93
- }
94
-
95
- export const Operators = {
96
- containsLow: CondOperator.CONTAINS_LOW,
97
- contains: CondOperator.CONTAINS,
98
- equals: CondOperator.EQUALS,
99
- in: CondOperator.IN,
100
- inLow: CondOperator.IN_LOW,
101
- between: CondOperator.BETWEEN,
102
- greaterOrEquals: CondOperator.GREATER_THAN_EQUALS,
103
- lowerOrEquals: CondOperator.LOWER_THAN_EQUALS,
104
- isNull: CondOperator.IS_NULL,
105
- notNull: CondOperator.NOT_NULL,
106
- starts: CondOperator.STARTS,
107
- } as const;
108
-
109
- export function applyKeywordToSearch(
110
- filterSearch: SCondition,
111
- searchableColumns: TSearchableColumn[],
112
- columnsState?: TColumnsStates,
113
- keyword?: string,
114
- ): SCondition {
115
- if (!keyword) {
116
- return filterSearch;
117
- }
118
-
119
- const keywordSearches: SCondition[] = [];
120
- keyword.split(' ').forEach((word) => {
121
- const keywordSearch: SCondition = { $or: [] };
122
- searchableColumns!.forEach((col) => {
123
- if (col.searchField === null) {
124
- return;
125
- }
126
-
127
- const dataIndex = Array.isArray(col.field) ? col.field.join(',') : col.field;
128
- if (columnsState?.[dataIndex] && !columnsState[dataIndex].show) {
129
- return;
130
- }
131
-
132
- const field = col.searchField || (Array.isArray(col.field) ? col.field.join('.') : col.field);
133
- const operator = col.operator;
134
-
135
- const wrongNumeric = col.numeric && !Number.isFinite(Number(word));
136
- const wrongUuid = col.uuid && (typeof word !== 'string' || !uuidValidate(word));
137
-
138
- if (!wrongNumeric && !wrongUuid) {
139
- keywordSearch.$or.push({ [field]: { [operator]: word } });
140
- }
141
- });
142
-
143
- keywordSearches.push(keywordSearch);
144
- });
145
-
146
- if (!Array.isArray(filterSearch.$and)) {
147
- throw new Error('Bad format of filter search');
148
- }
149
-
150
- return {
151
- $and: [...filterSearch.$and, ...keywordSearches]
152
- }
153
- }
154
-
155
- export type TIndexableRecord = {
156
- dataIndex?: Key | Key[];
157
- children?: TIndexableRecord[] | React.ReactNode;
158
- editable?: boolean | ((...args: any[]) => boolean);
159
- };
160
-
161
- export function collectFieldsFromColumns<T>(
162
- columns: TIndexableRecord[] | undefined,
163
- idColumnName: string | string[],
164
- joinFields: Set<string> = new Set,
165
- fields: Set<string> = new Set
166
- ): string[] {
167
- return [Array.from(buildFieldsFromColumns<T>(columns, idColumnName, joinFields, fields)).join(',')];
168
- }
169
-
170
- export function buildFieldsFromColumnsForDescriptionsDisplay<T>(
171
- columns: TIndexableRecord[] | undefined,
172
- idColumnName: string | string[],
173
- fields: Set<string> = new Set,
174
- ): Set<string> {
175
- columns?.forEach(col => {
176
- if ('children' in col && Array.isArray(col.children)) {
177
- buildFieldsFromColumnsForDescriptionsDisplay(col.children, idColumnName, fields);
178
- }
179
- if (col.editable !== false) {
180
- fields.add(String(Array.isArray(col.dataIndex) ? col.dataIndex[0] : col.dataIndex));
181
- }
182
- });
183
-
184
- return fields;
185
- }
186
-
187
- export function buildFieldsFromColumns<T>(
188
- columns: TIndexableRecord[] | undefined,
189
- idColumnName: string | string[],
190
- joinFields: Set<string> = new Set,
191
- fields: Set<string> = new Set
192
- ): Set<string> {
193
- columns?.forEach(col => {
194
- if ('children' in col && Array.isArray(col.children)) {
195
- buildFieldsFromColumns(col.children, idColumnName, joinFields, fields);
196
- }
197
-
198
- // skip id column because it is always included by backend
199
- // and join fields because they are included by join
200
-
201
- const dataIndex = Array.isArray(col.dataIndex) ? col.dataIndex[0] : col.dataIndex;
202
- if (typeof dataIndex !== 'string') {
203
- return;
204
- }
205
-
206
- if (!dataIndex || (Array.isArray(idColumnName) ? idColumnName.includes(dataIndex) : dataIndex === idColumnName) || joinFields.has(dataIndex)) {
207
- return;
208
- }
209
-
210
- fields.add(dataIndex);
211
- });
212
-
213
- return fields;
214
- }
215
-
216
- export function withNumericId<T extends IWithId>(entity: T): T & { id: number } {
217
- return {
218
- ...entity,
219
- id: Number(entity.id),
220
- };
221
- }
222
-
223
- export function buildJoinFields(join?: QueryJoin | QueryJoin[]) {
224
- const joinFields = new Set<string>();
225
- let joinSelect: string[] = [];
226
- if (join) {
227
- let joinArr = join;
228
- if (!Array.isArray(joinArr)) {
229
- joinArr = [joinArr];
230
- }
231
- joinSelect = joinArr.map(relation => {
232
- joinFields.add(relation.field);
233
- let res = relation.field;
234
- if (relation.select) {
235
- res += `||${relation.select.join(',')}`;
236
- }
237
- return res;
238
- });
239
- }
240
- return {
241
- joinSelect,
242
- joinFields,
243
- };
244
- }