@ai-table/grid 0.0.2 → 0.0.4

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 (95) hide show
  1. package/components/cell-editors/abstract-cell-editor.component.d.ts +2 -2
  2. package/components/cell-editors/abstract-cell-editor.component.d.ts.map +1 -1
  3. package/components/cell-editors/select/select-editor.component.d.ts +15 -0
  4. package/components/cell-editors/select/select-editor.component.d.ts.map +1 -0
  5. package/components/field-menu/field-menu.component.d.ts +14 -0
  6. package/components/field-menu/field-menu.component.d.ts.map +1 -0
  7. package/components/field-property-editor/field-property-editor.component.d.ts +13 -15
  8. package/components/field-property-editor/field-property-editor.component.d.ts.map +1 -1
  9. package/components/index.d.ts +2 -0
  10. package/components/index.d.ts.map +1 -0
  11. package/constants/editor.d.ts +1 -1
  12. package/constants/editor.d.ts.map +1 -1
  13. package/constants/field.d.ts +20 -0
  14. package/constants/field.d.ts.map +1 -0
  15. package/constants/grid.d.ts.map +1 -1
  16. package/constants/index.d.ts +1 -0
  17. package/constants/index.d.ts.map +1 -1
  18. package/core/action/field.d.ts +8 -2
  19. package/core/action/field.d.ts.map +1 -1
  20. package/core/action/general.d.ts.map +1 -1
  21. package/core/action/index.d.ts +5 -0
  22. package/core/action/index.d.ts.map +1 -1
  23. package/core/action/record.d.ts +7 -3
  24. package/core/action/record.d.ts.map +1 -1
  25. package/core/constants/field.d.ts +6 -8
  26. package/core/constants/field.d.ts.map +1 -1
  27. package/core/index.d.ts +1 -0
  28. package/core/index.d.ts.map +1 -1
  29. package/core/types/action.d.ts +38 -8
  30. package/core/types/action.d.ts.map +1 -1
  31. package/core/types/core.d.ts +36 -10
  32. package/core/types/core.d.ts.map +1 -1
  33. package/core/utils/common.d.ts +2 -1
  34. package/core/utils/common.d.ts.map +1 -1
  35. package/core/utils/field.d.ts +7 -1
  36. package/core/utils/field.d.ts.map +1 -1
  37. package/core/utils/queries.d.ts +4 -2
  38. package/core/utils/queries.d.ts.map +1 -1
  39. package/esm2022/components/cell-editors/abstract-cell-editor.component.mjs +11 -9
  40. package/esm2022/components/cell-editors/select/select-editor.component.mjs +46 -0
  41. package/esm2022/components/field-menu/field-menu.component.mjs +37 -0
  42. package/esm2022/components/field-property-editor/field-property-editor.component.mjs +35 -24
  43. package/esm2022/components/index.mjs +2 -0
  44. package/esm2022/constants/editor.mjs +8 -8
  45. package/esm2022/constants/field.mjs +27 -0
  46. package/esm2022/constants/grid.mjs +5 -5
  47. package/esm2022/constants/index.mjs +2 -1
  48. package/esm2022/core/action/field.mjs +43 -2
  49. package/esm2022/core/action/general.mjs +68 -14
  50. package/esm2022/core/action/record.mjs +22 -5
  51. package/esm2022/core/constants/field.mjs +22 -16
  52. package/esm2022/core/index.mjs +2 -1
  53. package/esm2022/core/types/action.mjs +6 -1
  54. package/esm2022/core/types/core.mjs +11 -14
  55. package/esm2022/core/types/index.mjs +1 -1
  56. package/esm2022/core/utils/common.mjs +10 -1
  57. package/esm2022/core/utils/field.mjs +12 -1
  58. package/esm2022/core/utils/queries.mjs +36 -6
  59. package/esm2022/core/utils/record.mjs +3 -3
  60. package/esm2022/grid.component.mjs +72 -32
  61. package/esm2022/pipes/grid.mjs +1 -2
  62. package/esm2022/public-api.mjs +2 -1
  63. package/esm2022/services/event.service.mjs +13 -6
  64. package/esm2022/services/field.service.mjs +32 -0
  65. package/esm2022/services/selection.servive.mjs +64 -0
  66. package/esm2022/types/field.mjs +2 -0
  67. package/esm2022/types/grid.mjs +1 -1
  68. package/esm2022/types/index.mjs +2 -1
  69. package/esm2022/utils/build.mjs +13 -5
  70. package/fesm2022/ai-table-grid.mjs +655 -283
  71. package/fesm2022/ai-table-grid.mjs.map +1 -1
  72. package/grid.component.d.ts +22 -11
  73. package/grid.component.d.ts.map +1 -1
  74. package/package.json +1 -1
  75. package/pipes/grid.d.ts.map +1 -1
  76. package/public-api.d.ts +1 -0
  77. package/public-api.d.ts.map +1 -1
  78. package/services/event.service.d.ts +2 -0
  79. package/services/event.service.d.ts.map +1 -1
  80. package/services/field.service.d.ts +16 -0
  81. package/services/field.service.d.ts.map +1 -0
  82. package/services/selection.servive.d.ts +16 -0
  83. package/services/selection.servive.d.ts.map +1 -0
  84. package/styles/styles.scss +70 -3
  85. package/types/field.d.ts +11 -0
  86. package/types/field.d.ts.map +1 -0
  87. package/types/grid.d.ts +13 -3
  88. package/types/grid.d.ts.map +1 -1
  89. package/types/index.d.ts +1 -0
  90. package/types/index.d.ts.map +1 -1
  91. package/utils/build.d.ts +2 -2
  92. package/utils/build.d.ts.map +1 -1
  93. package/components/cell-editors/single-select/single-select-editor.component.d.ts +0 -14
  94. package/components/cell-editors/single-select/single-select-editor.component.d.ts.map +0 -1
  95. package/esm2022/components/cell-editors/single-select/single-select-editor.component.mjs +0 -44
@@ -1,32 +1,36 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Pipe, computed, input, inject, Component, ChangeDetectionStrategy, signal, Injectable, Input, model, output } from '@angular/core';
3
- import { NgForOf, NgIf, CommonModule, NgClass, NgComponentOutlet } from '@angular/common';
2
+ import { Pipe, signal, computed, model, inject, booleanAttribute, Component, ChangeDetectionStrategy, Input, Injectable, input, output } from '@angular/core';
3
+ import * as i4 from '@angular/common';
4
+ import { NgIf, NgTemplateOutlet, NgForOf, NgClass, CommonModule, NgComponentOutlet } from '@angular/common';
4
5
  import { ThyTag } from 'ngx-tethys/tag';
5
6
  import * as i1$1 from 'ngx-tethys/popover';
6
7
  import { ThyPopoverRef, ThyPopoverModule } from 'ngx-tethys/popover';
7
8
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
9
+ import { helpers, isUndefinedOrNull } from 'ngx-tethys/util';
8
10
  import { createDraft, finishDraft } from 'immer';
9
- import { isUndefinedOrNull, helpers } from 'ngx-tethys/util';
10
11
  import { ThyIcon } from 'ngx-tethys/icon';
11
- import { fromEvent, of } from 'rxjs';
12
+ import { of, Subject, fromEvent } from 'rxjs';
12
13
  import * as i1 from '@angular/forms';
13
14
  import { FormsModule } from '@angular/forms';
14
- import { ThySelect } from 'ngx-tethys/select';
15
- import { ThyOption, ThyAutofocusDirective, ThyEnterDirective, ThyStopPropagationDirective } from 'ngx-tethys/shared';
16
- import { ThyInputDirective, ThyInputGroup, ThyInputCount, ThyInput } from 'ngx-tethys/input';
17
- import { ThyInputNumber } from 'ngx-tethys/input-number';
18
- import { ThyDatePicker, ThyDatePickerFormatPipe } from 'ngx-tethys/date-picker';
19
- import { ThyTimePickerModule } from 'ngx-tethys/time-picker';
20
- import { ThyRate } from 'ngx-tethys/rate';
21
- import { ThyTooltipModule, ThyTooltipService } from 'ngx-tethys/tooltip';
15
+ import { ThyInputGroup, ThyInputCount, ThyInputDirective, ThyInput } from 'ngx-tethys/input';
22
16
  import * as i2 from 'ngx-tethys/form';
23
17
  import { ThyUniqueCheckValidator, ThyFormModule, ThyConfirmValidatorDirective } from 'ngx-tethys/form';
24
18
  import { ThyDropdownDirective, ThyDropdownMenuComponent, ThyDropdownMenuItemDirective, ThyDropdownMenuItemNameDirective, ThyDropdownMenuItemIconDirective } from 'ngx-tethys/dropdown';
25
19
  import { ThyButton } from 'ngx-tethys/button';
26
20
  import { ThyListItem } from 'ngx-tethys/list';
21
+ import { ThyAutofocusDirective, ThyEnterDirective, ThyOption, ThyStopPropagationDirective } from 'ngx-tethys/shared';
22
+ import { ThyInputNumber } from 'ngx-tethys/input-number';
23
+ import { ThyDatePicker, ThyDatePickerFormatPipe } from 'ngx-tethys/date-picker';
24
+ import { ThyTimePickerModule } from 'ngx-tethys/time-picker';
25
+ import { ThyRate } from 'ngx-tethys/rate';
26
+ import { ThyTooltipModule, ThyTooltipService } from 'ngx-tethys/tooltip';
27
+ import { ThySelect } from 'ngx-tethys/select';
27
28
  import { ThyFlexibleText } from 'ngx-tethys/flexible-text';
29
+ import * as i6 from 'ngx-tethys/checkbox';
30
+ import { ThyCheckboxModule } from 'ngx-tethys/checkbox';
31
+ import { ThyDivider } from 'ngx-tethys/divider';
32
+ import { ThyAction } from 'ngx-tethys/action';
28
33
 
29
- ;
30
34
  class SelectOptionPipe {
31
35
  transform(id, options) {
32
36
  return options.find((item) => item.id === id);
@@ -42,36 +46,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImpor
42
46
  }]
43
47
  }] });
44
48
 
45
- ;
46
- const buildGridData = (recordValue, fieldsValue) => {
47
- return {
48
- type: 'grid',
49
- fields: fieldsValue,
50
- records: recordValue
51
- };
52
- };
53
-
54
- function getRecordOrField(value, id) {
55
- return computed(() => {
56
- return value().find((item) => item.id === id);
57
- });
58
- }
59
-
60
49
  var AITableFieldType;
61
50
  (function (AITableFieldType) {
62
- // NotSupport = 0,
63
- AITableFieldType[AITableFieldType["Text"] = 1] = "Text";
64
- AITableFieldType[AITableFieldType["Number"] = 2] = "Number";
65
- AITableFieldType[AITableFieldType["SingleSelect"] = 3] = "SingleSelect";
66
- // MultiSelect = 4,
67
- AITableFieldType[AITableFieldType["DateTime"] = 5] = "DateTime";
68
- // Attachment = 6,
69
- AITableFieldType[AITableFieldType["Link"] = 7] = "Link";
70
- // Email = 9,
71
- // Phone = 10,
72
- // Checkbox = 11,
73
- AITableFieldType[AITableFieldType["Rating"] = 12] = "Rating";
74
- // Member = 13
51
+ AITableFieldType["text"] = "text";
52
+ AITableFieldType["richText"] = "rich_text";
53
+ AITableFieldType["select"] = "select";
54
+ AITableFieldType["number"] = "number";
55
+ AITableFieldType["date"] = "date";
56
+ AITableFieldType["member"] = "member";
57
+ // cascadeSelect = "cascade_select", // 包含单选和多选,参数复杂后续再进行设计
58
+ AITableFieldType["progress"] = "progress";
59
+ AITableFieldType["rate"] = "rate";
60
+ AITableFieldType["link"] = "link";
75
61
  })(AITableFieldType || (AITableFieldType = {}));
76
62
  var AITableStatType;
77
63
  (function (AITableStatType) {
@@ -100,6 +86,11 @@ var ActionName;
100
86
  ActionName["UpdateFieldValue"] = "update_field_value";
101
87
  ActionName["AddRecord"] = "add_record";
102
88
  ActionName["AddField"] = "add_field";
89
+ ActionName["MoveField"] = "move_field";
90
+ ActionName["MoveRecord"] = "move_record";
91
+ ActionName["RemoveField"] = "remove_field";
92
+ ActionName["RemoveRecord"] = "remove_record";
93
+ ActionName["SetField"] = "set_field";
103
94
  })(ActionName || (ActionName = {}));
104
95
  var ExecuteType;
105
96
  (function (ExecuteType) {
@@ -108,65 +99,6 @@ var ExecuteType;
108
99
  ExecuteType[ExecuteType["Redo"] = 2] = "Redo";
109
100
  })(ExecuteType || (ExecuteType = {}));
110
101
 
111
- function addField(aiTable, field, path) {
112
- const operation = {
113
- type: ActionName.AddField,
114
- field,
115
- path
116
- };
117
- aiTable.apply(operation);
118
- }
119
- const FieldActions = {
120
- addField
121
- };
122
-
123
- const apply = (aiTable, records, fields, options) => {
124
- switch (options.type) {
125
- case ActionName.UpdateFieldValue: {
126
- const [recordIndex, fieldIndex] = options.path;
127
- const fieldId = aiTable.fields()[fieldIndex].id;
128
- records[recordIndex].value[fieldId] = options.newFieldValue;
129
- break;
130
- }
131
- case ActionName.AddRecord: {
132
- const [recordIndex] = options.path;
133
- records.splice(recordIndex, 0, options.record);
134
- break;
135
- }
136
- case ActionName.AddField: {
137
- const [fieldIndex] = options.path;
138
- const newField = options.field;
139
- fields.splice(fieldIndex, 0, newField);
140
- const newRecord = {
141
- [newField.id]: ''
142
- };
143
- records.forEach((item) => {
144
- item.value = {
145
- ...item.value,
146
- ...newRecord
147
- };
148
- });
149
- }
150
- }
151
- return {
152
- records,
153
- fields
154
- };
155
- };
156
- const GeneralActions = {
157
- transform(aiTable, op) {
158
- const records = createDraft(aiTable.records());
159
- const fields = createDraft(aiTable.fields());
160
- apply(aiTable, records, fields, op);
161
- aiTable.fields.update(() => {
162
- return finishDraft(fields);
163
- });
164
- aiTable.records.update(() => {
165
- return finishDraft(records);
166
- });
167
- }
168
- };
169
-
170
102
  function idCreator(length = 5) {
171
103
  // remove numeral
172
104
  const $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz'; /**** Easily confusing characters are removed by default oOLl,9gq,Vv,Uu,I1****/
@@ -178,9 +110,58 @@ function idCreator(length = 5) {
178
110
  return key;
179
111
  }
180
112
 
113
+ const BasicFields = [
114
+ {
115
+ type: AITableFieldType.text,
116
+ name: '文本',
117
+ icon: 'font',
118
+ width: 300
119
+ },
120
+ {
121
+ type: AITableFieldType.select,
122
+ name: '单选',
123
+ icon: 'check-circle',
124
+ width: 200
125
+ },
126
+ {
127
+ type: AITableFieldType.number,
128
+ name: '数字',
129
+ icon: 'hashtag',
130
+ width: 200
131
+ },
132
+ {
133
+ type: AITableFieldType.date,
134
+ name: '日期',
135
+ icon: 'calendar',
136
+ width: 200
137
+ },
138
+ {
139
+ type: AITableFieldType.rate,
140
+ name: '评分',
141
+ icon: 'star-circle',
142
+ width: 200
143
+ },
144
+ {
145
+ type: AITableFieldType.link,
146
+ name: '链接',
147
+ icon: 'link-insert',
148
+ width: 300
149
+ }
150
+ ];
151
+ const Fields = [...BasicFields];
152
+ const FieldsMap = helpers.keyBy([...BasicFields], 'type');
153
+
181
154
  function getDefaultFieldValue(type) {
182
155
  return '';
183
156
  }
157
+ function createDefaultFieldName(aiTable, type = AITableFieldType.text) {
158
+ const fields = aiTable.fields();
159
+ const count = fields.filter((item) => item.type === type).length;
160
+ return count === 0 ? FieldsMap[type].name : FieldsMap[type].name + count;
161
+ }
162
+ function createDefaultField(aiTable, type = AITableFieldType.text) {
163
+ return { id: idCreator(), type, name: createDefaultFieldName(aiTable, type) };
164
+ }
184
165
 
185
166
  const FLUSHING = new WeakMap();
186
167
 
@@ -189,6 +170,11 @@ function createAITable(records, fields) {
189
170
  records,
190
171
  fields,
191
172
  actions: [],
173
+ selection: signal({
174
+ selectedRecords: new Map(),
175
+ selectedFields: new Map(),
176
+ selectedCells: new Map()
177
+ }),
192
178
  onChange: () => { },
193
179
  apply: (action) => {
194
180
  aiTable.actions.push(action);
@@ -205,14 +191,17 @@ function createAITable(records, fields) {
205
191
  };
206
192
  return aiTable;
207
193
  }
194
+ function isPathEqual(path, another) {
195
+ return path.length === another.length && path.every((n, i) => n === another[i]);
196
+ }
208
197
 
209
198
  function getDefaultRecord(fields) {
210
199
  const newRow = {
211
200
  id: idCreator(),
212
- value: {}
201
+ values: {}
213
202
  };
214
203
  fields.map((item) => {
215
- newRow.value[item.id] = getDefaultFieldValue(item.type);
204
+ newRow.values[item.id] = getDefaultFieldValue(item.type);
216
205
  });
217
206
  return newRow;
218
207
  }
@@ -230,23 +219,205 @@ const AITableQueries = {
230
219
  if (!isUndefinedOrNull(fieldIndex) && fieldIndex > -1) {
231
220
  return [fieldIndex];
232
221
  }
233
- throw new Error(`Unable to find the path: ${JSON.stringify({ ...(field || {}), ...(record || {}) })}`);
222
+ throw new Error(`can not find the path: ${JSON.stringify({ ...(field || {}), ...(record || {}) })}`);
234
223
  },
235
224
  getFieldValue(aiTable, path) {
236
- if (!aiTable || !aiTable.records() || !aiTable.fields()) {
237
- throw new Error(`Cannot find a descendant at path [${path}]`);
225
+ if (!aiTable) {
226
+ throw new Error(`aiTable does not exist`);
227
+ }
228
+ if (!aiTable.records()) {
229
+ throw new Error(`aiTable has no records`);
230
+ }
231
+ if (!aiTable.fields()) {
232
+ throw new Error(`aiTable has no fields`);
233
+ }
234
+ if (!path) {
235
+ throw new Error(`path does not exist as path [${path}]`);
236
+ }
237
+ const field = aiTable.fields()[path[1]];
238
+ if (!field) {
239
+ throw new Error(`can not find field at path [${path}]`);
240
+ }
241
+ return aiTable.records()[path[0]].values[field.id];
242
+ },
243
+ getField(aiTable, path) {
244
+ if (!aiTable) {
245
+ throw new Error(`aiTable does not exist`);
246
+ }
247
+ if (!path) {
248
+ throw new Error(`path does not exist as path [${path}]`);
238
249
  }
239
- const fieldId = aiTable.fields()[path[1]].id;
240
- return aiTable.records()[path[0]].value[fieldId];
250
+ return aiTable.fields()[path[0]];
251
+ },
252
+ getRecord(aiTable, path) {
253
+ if (!aiTable) {
254
+ throw new Error(`aiTable does not exist`);
255
+ }
256
+ if (!path) {
257
+ throw new Error(`path does not exist as path [${path}]`);
258
+ }
259
+ return aiTable.records()[path[0]];
260
+ }
261
+ };
262
+
263
+ function addField(aiTable, field, path) {
264
+ const operation = {
265
+ type: ActionName.AddField,
266
+ field,
267
+ path
268
+ };
269
+ aiTable.apply(operation);
270
+ }
271
+ function moveField(aiTable, path, newPath) {
272
+ const operation = {
273
+ type: ActionName.MoveField,
274
+ path,
275
+ newPath
276
+ };
277
+ aiTable.apply(operation);
278
+ }
279
+ function removeField(aiTable, path) {
280
+ const operation = {
281
+ type: ActionName.RemoveField,
282
+ path
283
+ };
284
+ aiTable.apply(operation);
285
+ }
286
+ function setField(aiTable, value, path) {
287
+ const field = AITableQueries.getField(aiTable, path);
288
+ if (field) {
289
+ const oldField = {};
290
+ const newField = {};
291
+ for (const k in value) {
292
+ if (field[k] !== value[k]) {
293
+ if (field.hasOwnProperty(k)) {
294
+ oldField[k] = field[k];
295
+ }
296
+ newField[k] = value[k];
297
+ }
298
+ }
299
+ const operation = {
300
+ type: ActionName.SetField,
301
+ field: oldField,
302
+ newField,
303
+ path
304
+ };
305
+ aiTable.apply(operation);
306
+ }
307
+ }
308
+ const FieldActions = {
309
+ addField,
310
+ moveField,
311
+ removeField,
312
+ setField
313
+ };
314
+
315
+ const apply = (aiTable, records, fields, options) => {
316
+ switch (options.type) {
317
+ case ActionName.UpdateFieldValue: {
318
+ const [recordIndex, fieldIndex] = options.path;
319
+ if (fieldIndex > -1 && recordIndex > -1) {
320
+ const fieldId = aiTable.fields()[fieldIndex].id;
321
+ records[recordIndex].values[fieldId] = options.newFieldValue;
322
+ }
323
+ break;
324
+ }
325
+ case ActionName.AddRecord: {
326
+ const [recordIndex] = options.path;
327
+ if (recordIndex > -1) {
328
+ records.splice(recordIndex, 0, options.record);
329
+ }
330
+ break;
331
+ }
332
+ case ActionName.AddField: {
333
+ const [fieldIndex] = options.path;
334
+ if (fieldIndex > -1) {
335
+ const newField = options.field;
336
+ fields.splice(fieldIndex, 0, newField);
337
+ const newRecord = {
338
+ [newField.id]: ''
339
+ };
340
+ records.forEach((item) => {
341
+ item.values = {
342
+ ...item.values,
343
+ ...newRecord
344
+ };
345
+ });
346
+ }
347
+ break;
348
+ }
349
+ case ActionName.MoveRecord: {
350
+ if (isPathEqual(options.path, options.newPath)) {
351
+ return;
352
+ }
353
+ const record = records[options.path[0]];
354
+ records.splice(options.path[0], 1);
355
+ records.splice(options.newPath[0], 0, record);
356
+ break;
357
+ }
358
+ case ActionName.MoveField: {
359
+ if (isPathEqual(options.path, options.newPath)) {
360
+ return;
361
+ }
362
+ const field = fields[options.path[0]];
363
+ fields.splice(options.path[0], 1);
364
+ fields.splice(options.newPath[0], 0, field);
365
+ break;
366
+ }
367
+ case ActionName.RemoveField: {
368
+ const [fieldIndex] = options.path;
369
+ if (fieldIndex > -1) {
370
+ const fieldId = aiTable.fields()[fieldIndex].id;
371
+ fields.splice(fieldIndex, 1);
372
+ records.forEach((item) => {
373
+ delete item.values[fieldId];
374
+ });
375
+ }
376
+ break;
377
+ }
378
+ case ActionName.RemoveRecord: {
379
+ const [recordIndex] = options.path;
380
+ if (recordIndex > -1) {
381
+ records.splice(recordIndex, 1);
382
+ }
383
+ break;
384
+ }
385
+ case ActionName.SetField: {
386
+ const [fieldIndex] = options.path;
387
+ if (fieldIndex > -1) {
388
+ fields.splice(fieldIndex, 1, {
389
+ ...fields[fieldIndex],
390
+ ...options.newField
391
+ });
392
+ }
393
+ break;
394
+ }
395
+ }
396
+ return {
397
+ records,
398
+ fields
399
+ };
400
+ };
401
+ const GeneralActions = {
402
+ transform(aiTable, op) {
403
+ const records = createDraft(aiTable.records());
404
+ const fields = createDraft(aiTable.fields());
405
+ apply(aiTable, records, fields, op);
406
+ aiTable.fields.update(() => {
407
+ return finishDraft(fields);
408
+ });
409
+ aiTable.records.update(() => {
410
+ return finishDraft(records);
411
+ });
241
412
  }
242
413
  };
243
414
 
244
415
  function updateFieldValue(aiTable, value, path) {
245
- const node = AITableQueries.getFieldValue(aiTable, path);
246
- if (node !== value) {
416
+ const field = AITableQueries.getFieldValue(aiTable, path);
417
+ if (field !== value) {
247
418
  const operation = {
248
419
  type: ActionName.UpdateFieldValue,
249
- fieldValue: node,
420
+ fieldValue: field,
250
421
  newFieldValue: value,
251
422
  path
252
423
  };
@@ -261,9 +432,26 @@ function addRecord(aiTable, record, path) {
261
432
  };
262
433
  aiTable.apply(operation);
263
434
  }
435
+ function moveRecord(aiTable, path, newPath) {
436
+ const operation = {
437
+ type: ActionName.MoveRecord,
438
+ path,
439
+ newPath
440
+ };
441
+ aiTable.apply(operation);
442
+ }
443
+ function removeRecord(aiTable, path) {
444
+ const operation = {
445
+ type: ActionName.RemoveRecord,
446
+ path
447
+ };
448
+ aiTable.apply(operation);
449
+ }
264
450
  const RecordActions = {
265
451
  addRecord,
266
- updateFieldValue
452
+ updateFieldValue,
453
+ moveRecord,
454
+ removeRecord
267
455
  };
268
456
 
269
457
  const Actions = {
@@ -272,13 +460,35 @@ const Actions = {
272
460
  ...FieldActions
273
461
  };
274
462
 
463
+ const buildGridData = (recordValue, fieldsValue, selection) => {
464
+ return {
465
+ type: 'grid',
466
+ fields: fieldsValue.map(item => {
467
+ return {
468
+ ...item,
469
+ icon: item.icon || FieldsMap[item.type].icon,
470
+ width: item.width || FieldsMap[item.type].width
471
+ };
472
+ }),
473
+ records: recordValue.map((item) => {
474
+ return { ...item, checked: selection.selectedRecords.has(item.id) };
475
+ })
476
+ };
477
+ };
478
+
479
+ function getRecordOrField(value, id) {
480
+ return computed(() => {
481
+ return value().find((item) => item.id === id);
482
+ });
483
+ }
484
+
275
485
  const DEFAULT_COLUMN_WIDTH = 200;
276
486
  const MIN_COLUMN_WIDTH = 80;
277
487
  const DBL_CLICK_EDIT_TYPE = [
278
- AITableFieldType.Text,
279
- AITableFieldType.Number,
280
- AITableFieldType.SingleSelect,
281
- AITableFieldType.DateTime
488
+ AITableFieldType.text,
489
+ AITableFieldType.number,
490
+ AITableFieldType.select,
491
+ AITableFieldType.date
282
492
  ];
283
493
  const RowHeight = {
284
494
  Short: 32,
@@ -287,28 +497,157 @@ const RowHeight = {
287
497
  ExtraTall: 152
288
498
  };
289
499
 
500
+ class AITableFieldPropertyEditor {
501
+ constructor() {
502
+ this.aiField = model.required();
503
+ this.aiExternalTemplate = null;
504
+ this.fieldType = computed(() => {
505
+ return FieldsMap[this.aiField().type];
506
+ });
507
+ this.fieldMaxLength = 32;
508
+ this.validatorConfig = {
509
+ validationMessages: {
510
+ fieldName: {
511
+ required: '列名不能为空',
512
+ thyUniqueCheck: '列名已存在'
513
+ }
514
+ }
515
+ };
516
+ this.selectableFields = Fields;
517
+ this.thyPopoverRef = inject((ThyPopoverRef));
518
+ this.checkUniqueName = (fieldName) => {
519
+ fieldName = fieldName?.trim();
520
+ return of(!!this.aiTable.fields()?.find((field) => field.name === fieldName && this.aiField()?.id !== field.id));
521
+ };
522
+ }
523
+ selectFieldType(fieldType) {
524
+ this.aiField.update((item) => ({ ...item, type: fieldType, name: createDefaultFieldName(this.aiTable, fieldType) }));
525
+ }
526
+ editFieldProperty() {
527
+ if (this.isUpdate) {
528
+ const path = this.aiTable.fields().findIndex((item) => item.id === this.aiField().id);
529
+ Actions.setField(this.aiTable, this.aiField(), [path]);
530
+ }
531
+ else {
532
+ Actions.addField(this.aiTable, this.aiField(), [this.aiTable.fields().length]);
533
+ }
534
+ this.thyPopoverRef.close();
535
+ }
536
+ cancel() {
537
+ this.thyPopoverRef.close();
538
+ }
539
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: AITableFieldPropertyEditor, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
540
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.0.2", type: AITableFieldPropertyEditor, isStandalone: true, selector: "ai-table-field-property-editor", inputs: { aiField: { classPropertyName: "aiField", publicName: "aiField", isSignal: true, isRequired: true, transformFunction: null }, aiTable: { classPropertyName: "aiTable", publicName: "aiTable", isSignal: false, isRequired: true, transformFunction: null }, aiExternalTemplate: { classPropertyName: "aiExternalTemplate", publicName: "aiExternalTemplate", isSignal: false, isRequired: false, transformFunction: null }, isUpdate: { classPropertyName: "isUpdate", publicName: "isUpdate", isSignal: false, isRequired: false, transformFunction: booleanAttribute } }, outputs: { aiField: "aiFieldChange" }, host: { classAttribute: "field-property-editor d-block pl-5 pr-5 pb-5 pt-4" }, ngImport: i0, template: "<form thyForm name=\"createPropertyForm\" [thyFormValidatorConfig]=\"validatorConfig\" thyLayout=\"vertical\">\n <thy-form-group thyLabelRequired thyLabelText=\"\u8868\u683C\u5217\u540D\">\n <thy-input-group>\n <input\n thyInput\n [thyAutofocus]=\"true\"\n name=\"fieldName\"\n [maxlength]=\"fieldMaxLength\"\n [(ngModel)]=\"aiField().name\"\n required\n placeholder=\"\u8F93\u5165\u5217\u540D\u79F0\"\n [thyUniqueCheck]=\"checkUniqueName\"\n />\n <ng-template #suffix>\n <thy-input-count></thy-input-count>\n </ng-template>\n </thy-input-group>\n </thy-form-group>\n <thy-form-group thyLabelText=\"\u5217\u7C7B\u578B\">\n <div class=\"ml-n5 mr-n5\">\n <thy-list-item [thyDropdown]=\"menu\" thyTrigger=\"hover\" thyPlacement=\"right\" class=\"justify-content-between\">\n <span>\n <thy-icon [thyIconName]=\"fieldType().icon\" class=\"text-desc mr-2\"></thy-icon>\n <span>{{ fieldType().name }}</span>\n </span>\n <thy-icon thyIconName=\"angle-right\" class=\"text-desc\"></thy-icon>\n </thy-list-item>\n </div>\n </thy-form-group>\n <ng-container *ngIf=\"aiExternalTemplate; else defaultTemplate\">\n <ng-container *ngTemplateOutlet=\"aiExternalTemplate\"></ng-container>\n </ng-container>\n <ng-template #defaultTemplate>\n <!-- TODO: \u5185\u90E8\u5C5E\u6027\u6E32\u67D3 -->\n </ng-template>\n <thy-form-group-footer thyAlign=\"right\">\n <button thyButton=\"link-secondary\" (click)=\"cancel()\" thySize=\"sm\">\u53D6\u6D88</button>\n <button thyButton=\"primary\" (thyFormSubmit)=\"editFieldProperty()\" thySize=\"sm\">\u786E\u5B9A</button>\n </thy-form-group-footer>\n</form>\n\n<thy-dropdown-menu #menu>\n @for (item of selectableFields; track item.type) {\n <a thyDropdownMenuItem href=\"javascript:;\" (click)=\"selectFieldType(item.type)\">\n <thy-icon [thyIconName]=\"item.icon\"></thy-icon>\n <span thyDropdownMenuItemName>{{ item.name }}</span>\n </a>\n }\n</thy-dropdown-menu>\n", styles: [":host{width:350px}\n"], dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: ThyIcon, selector: "thy-icon, [thy-icon]", inputs: ["thyIconType", "thyTwotoneColor", "thyIconName", "thyIconRotate", "thyIconSet", "thyIconLegging", "thyIconLinearGradient"] }, { kind: "component", type: ThyInputGroup, selector: "thy-input-group", inputs: ["thyAppendText", "thyAppendTextTranslateKey", "thyPrependText", "thyPrependTextTranslateKey", "thySize"] }, { kind: "component", type: ThyInputCount, selector: "thy-input-count", inputs: ["thyInput"] }, { kind: "directive", type: ThyInputDirective, selector: "input[thyInput], select[thyInput], textarea[thyInput]", inputs: ["thySize"], exportAs: ["thyInput"] }, { kind: "directive", type: ThyUniqueCheckValidator, selector: "[thyUniqueCheck]", inputs: ["thyUniqueCheck"] }, { kind: "directive", type: ThyDropdownDirective, selector: "[thyDropdown]", inputs: ["thyDropdownMenu", "thyDropdown", "thyTrigger", "thyShowDelay", "thyHideDelay", "thyActiveClass", "thyPopoverOptions", "thyPlacement", "thyMenuInsideClosable", "thyPanelClass"], outputs: ["thyActiveChange"] }, { kind: "component", type: ThyDropdownMenuComponent, selector: "thy-dropdown-menu", inputs: ["thyWidth", "thyImmediateRender"] }, { kind: "directive", type: ThyDropdownMenuItemDirective, selector: "[thyDropdownMenuItem]", inputs: ["thyType", "thyDisabled"] }, { kind: "directive", type: ThyDropdownMenuItemNameDirective, selector: "[thyDropdownMenuItemName]" }, { kind: "component", type: ThyButton, selector: "thy-button,[thy-button],[thyButton]", inputs: ["thyButton", "thyType", "thyLoading", "thyLoadingText", "thySize", "thyIcon", "thyBlock"] }, { kind: "ngmodule", type: ThyFormModule }, { kind: "directive", type: i2.ThyFormDirective, selector: "[thyForm],[thy-form]", inputs: ["thyLayout", "thyEnterKeyMode", "thyFormValidatorConfig"], exportAs: ["thyForm"] }, { kind: "component", type: i2.ThyFormGroup, selector: "thy-form-group", inputs: ["thyLabelText", "thyLabelTextTranslateKey", "thyLabelRequired", "thyLabelPaddingTopClear", "thyFeedbackIcon", "thyTipsMode", "thyTips", "thyTipsTranslateKey", "thyRowFill"] }, { kind: "directive", type: i2.ThyFormSubmitDirective, selector: "[thyFormSubmit],[thy-form-submit]", outputs: ["thyFormSubmit"] }, { kind: "component", type: i2.ThyFormGroupFooter, selector: "thy-form-group-footer", inputs: ["thyAlign"] }, { kind: "component", type: ThyListItem, selector: "thy-list-item,[thy-list-item]" }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: ThyAutofocusDirective, selector: "input[thyAutofocus],textarea[thyAutofocus]", inputs: ["thyAutofocus", "thyAutoSelect"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
541
+ }
542
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: AITableFieldPropertyEditor, decorators: [{
543
+ type: Component,
544
+ args: [{ selector: 'ai-table-field-property-editor', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [
545
+ NgIf,
546
+ NgForOf,
547
+ FormsModule,
548
+ ThyIcon,
549
+ ThyInput,
550
+ ThyInputGroup,
551
+ ThyInputCount,
552
+ ThyInputDirective,
553
+ ThyConfirmValidatorDirective,
554
+ ThyUniqueCheckValidator,
555
+ ThyDropdownDirective,
556
+ ThyDropdownMenuComponent,
557
+ ThyDropdownMenuItemDirective,
558
+ ThyDropdownMenuItemNameDirective,
559
+ ThyDropdownMenuItemIconDirective,
560
+ ThyButton,
561
+ ThyFormModule,
562
+ ThyListItem,
563
+ NgTemplateOutlet,
564
+ ThyAutofocusDirective
565
+ ], host: {
566
+ class: 'field-property-editor d-block pl-5 pr-5 pb-5 pt-4'
567
+ }, template: "<form thyForm name=\"createPropertyForm\" [thyFormValidatorConfig]=\"validatorConfig\" thyLayout=\"vertical\">\n <thy-form-group thyLabelRequired thyLabelText=\"\u8868\u683C\u5217\u540D\">\n <thy-input-group>\n <input\n thyInput\n [thyAutofocus]=\"true\"\n name=\"fieldName\"\n [maxlength]=\"fieldMaxLength\"\n [(ngModel)]=\"aiField().name\"\n required\n placeholder=\"\u8F93\u5165\u5217\u540D\u79F0\"\n [thyUniqueCheck]=\"checkUniqueName\"\n />\n <ng-template #suffix>\n <thy-input-count></thy-input-count>\n </ng-template>\n </thy-input-group>\n </thy-form-group>\n <thy-form-group thyLabelText=\"\u5217\u7C7B\u578B\">\n <div class=\"ml-n5 mr-n5\">\n <thy-list-item [thyDropdown]=\"menu\" thyTrigger=\"hover\" thyPlacement=\"right\" class=\"justify-content-between\">\n <span>\n <thy-icon [thyIconName]=\"fieldType().icon\" class=\"text-desc mr-2\"></thy-icon>\n <span>{{ fieldType().name }}</span>\n </span>\n <thy-icon thyIconName=\"angle-right\" class=\"text-desc\"></thy-icon>\n </thy-list-item>\n </div>\n </thy-form-group>\n <ng-container *ngIf=\"aiExternalTemplate; else defaultTemplate\">\n <ng-container *ngTemplateOutlet=\"aiExternalTemplate\"></ng-container>\n </ng-container>\n <ng-template #defaultTemplate>\n <!-- TODO: \u5185\u90E8\u5C5E\u6027\u6E32\u67D3 -->\n </ng-template>\n <thy-form-group-footer thyAlign=\"right\">\n <button thyButton=\"link-secondary\" (click)=\"cancel()\" thySize=\"sm\">\u53D6\u6D88</button>\n <button thyButton=\"primary\" (thyFormSubmit)=\"editFieldProperty()\" thySize=\"sm\">\u786E\u5B9A</button>\n </thy-form-group-footer>\n</form>\n\n<thy-dropdown-menu #menu>\n @for (item of selectableFields; track item.type) {\n <a thyDropdownMenuItem href=\"javascript:;\" (click)=\"selectFieldType(item.type)\">\n <thy-icon [thyIconName]=\"item.icon\"></thy-icon>\n <span thyDropdownMenuItemName>{{ item.name }}</span>\n </a>\n }\n</thy-dropdown-menu>\n", styles: [":host{width:350px}\n"] }]
568
+ }], ctorParameters: () => [], propDecorators: { aiTable: [{
569
+ type: Input,
570
+ args: [{ required: true }]
571
+ }], aiExternalTemplate: [{
572
+ type: Input
573
+ }], isUpdate: [{
574
+ type: Input,
575
+ args: [{ transform: booleanAttribute }]
576
+ }] } });
577
+
578
+ const AI_TABLE_GRID_FIELD_SERVICE_MAP = new WeakMap();
579
+ class AITableGridFieldService {
580
+ constructor(thyPopover) {
581
+ this.thyPopover = thyPopover;
582
+ }
583
+ initAIFieldConfig(aiFieldConfig) {
584
+ this.aiFieldConfig = aiFieldConfig;
585
+ }
586
+ editFieldProperty(origin, aiTable, aiField, isUpdate) {
587
+ const component = this.aiFieldConfig?.fieldPropertyEditor ?? AITableFieldPropertyEditor;
588
+ this.thyPopover.open(component, {
589
+ origin: origin,
590
+ manualClosure: true,
591
+ placement: 'bottomLeft',
592
+ initialState: {
593
+ aiTable,
594
+ aiField,
595
+ isUpdate
596
+ }
597
+ });
598
+ }
599
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: AITableGridFieldService, deps: [{ token: i1$1.ThyPopover }], target: i0.ɵɵFactoryTarget.Injectable }); }
600
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: AITableGridFieldService }); }
601
+ }
602
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: AITableGridFieldService, decorators: [{
603
+ type: Injectable
604
+ }], ctorParameters: () => [{ type: i1$1.ThyPopover }] });
605
+
606
+ const DividerMenuItem = {
607
+ id: 'divider'
608
+ };
609
+ const EditFieldPropertyItem = {
610
+ id: 'editFieldProperty',
611
+ name: '编辑列',
612
+ icon: 'edit',
613
+ exec: (aiTable, field, origin) => {
614
+ const fieldService = AI_TABLE_GRID_FIELD_SERVICE_MAP.get(aiTable);
615
+ const copyField = signal(JSON.parse(JSON.stringify(field())));
616
+ origin && fieldService?.editFieldProperty(origin, aiTable, copyField, true);
617
+ }
618
+ };
619
+ const RemoveFieldItem = {
620
+ id: 'removeField',
621
+ name: '删除列',
622
+ icon: 'trash',
623
+ exec: (aiTable, field) => {
624
+ const path = AITableQueries.findPath(aiTable, field());
625
+ Actions.removeField(aiTable, path);
626
+ }
627
+ };
628
+ const DefaultFieldMenus = [EditFieldPropertyItem, RemoveFieldItem];
629
+
290
630
  class AbstractEditCellEditor {
291
631
  constructor() {
292
632
  this.field = input.required();
293
633
  this.record = input.required();
294
- this.aiTable = input.required();
295
634
  this.thyPopoverRef = inject((ThyPopoverRef));
296
635
  }
297
636
  ngOnInit() {
298
637
  this.modelValue = computed(() => {
299
- const path = AITableQueries.findPath(this.aiTable(), this.field(), this.record());
300
- return AITableQueries.getFieldValue(this.aiTable(), path);
638
+ const path = AITableQueries.findPath(this.aiTable, this.field(), this.record());
639
+ return AITableQueries.getFieldValue(this.aiTable, path);
301
640
  })();
302
641
  }
303
642
  updateFieldValue() {
304
- const path = AITableQueries.findPath(this.aiTable(), this.field(), this.record());
305
- Actions.updateFieldValue(this.aiTable(), this.modelValue, path);
643
+ const path = AITableQueries.findPath(this.aiTable, this.field(), this.record());
644
+ Actions.updateFieldValue(this.aiTable, this.modelValue, path);
306
645
  }
307
646
  closePopover() {
308
647
  this.thyPopoverRef.close();
309
648
  }
310
649
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: AbstractEditCellEditor, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
311
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "18.0.2", type: AbstractEditCellEditor, isStandalone: true, selector: "abstract-edit-cell", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, record: { classPropertyName: "record", publicName: "record", isSignal: true, isRequired: true, transformFunction: null }, aiTable: { classPropertyName: "aiTable", publicName: "aiTable", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: ``, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
650
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "18.0.2", type: AbstractEditCellEditor, isStandalone: true, selector: "abstract-edit-cell", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, record: { classPropertyName: "record", publicName: "record", isSignal: true, isRequired: true, transformFunction: null }, aiTable: { classPropertyName: "aiTable", publicName: "aiTable", isSignal: false, isRequired: true, transformFunction: null } }, ngImport: i0, template: ``, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
312
651
  }
313
652
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: AbstractEditCellEditor, decorators: [{
314
653
  type: Component,
@@ -318,41 +657,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImpor
318
657
  standalone: true,
319
658
  changeDetection: ChangeDetectionStrategy.OnPush
320
659
  }]
321
- }] });
322
-
323
- class SingleSelectCellEditorComponent extends AbstractEditCellEditor {
324
- constructor() {
325
- super();
326
- this.selectOptions = computed(() => {
327
- return this.field().options;
328
- });
329
- }
330
- updateValue(value) {
331
- if (!value) {
332
- this.updateFieldValue();
333
- this.closePopover();
334
- }
335
- }
336
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: SingleSelectCellEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
337
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.0.2", type: SingleSelectCellEditorComponent, isStandalone: true, selector: "single-select-cell-editor", host: { classAttribute: "d-block h-100" }, usesInheritance: true, ngImport: i0, template: `<thy-select [(ngModel)]="modelValue" [thyAutoExpand]="true" (thyOnExpandStatusChange)="updateValue($event)">
338
- <thy-option *ngFor="let option of selectOptions()" [thyValue]="option.id" [thyLabelText]="option.name"> </thy-option>
339
- </thy-select> `, isInline: true, dependencies: [{ kind: "directive", type: NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: ThySelect, selector: "thy-select,thy-custom-select", inputs: ["thyDropdownWidthMode", "thyShowSearch", "thyPlaceHolder", "thyServerSearch", "thyLoadState", "thyAutoActiveFirstItem", "thyMode", "thySize", "thyEmptyStateText", "thyEmptySearchMessageText", "thyEnableScrollLoad", "thyAllowClear", "thyDisabled", "thySortComparator", "thyFooterTemplate", "thyPlacement", "thyOrigin", "thyFooterClass", "thyAutoExpand", "thyHasBackdrop", "thyMaxTagCount", "thyBorderless", "thyOptions", "thyPreset"], outputs: ["thyOnSearch", "thyOnScrollToBottom", "thyOnExpandStatusChange"], exportAs: ["thySelect"] }, { kind: "component", type: ThyOption, selector: "thy-option", inputs: ["thyValue", "thyRawValue", "thyLabelText", "thyShowOptionCustom", "thySearchKey", "thyDisabled"], outputs: ["selectionChange", "visibleChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
340
- }
341
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: SingleSelectCellEditorComponent, decorators: [{
342
- type: Component,
343
- args: [{
344
- selector: 'single-select-cell-editor',
345
- template: `<thy-select [(ngModel)]="modelValue" [thyAutoExpand]="true" (thyOnExpandStatusChange)="updateValue($event)">
346
- <thy-option *ngFor="let option of selectOptions()" [thyValue]="option.id" [thyLabelText]="option.name"> </thy-option>
347
- </thy-select> `,
348
- standalone: true,
349
- changeDetection: ChangeDetectionStrategy.OnPush,
350
- host: {
351
- class: 'd-block h-100'
352
- },
353
- imports: [NgIf, NgForOf, FormsModule, ThySelect, ThyOption, ThyTag, ThyIcon]
354
- }]
355
- }], ctorParameters: () => [] });
660
+ }], propDecorators: { aiTable: [{
661
+ type: Input,
662
+ args: [{ required: true }]
663
+ }] } });
356
664
 
357
665
  class TextCellEditorComponent extends AbstractEditCellEditor {
358
666
  updateValue() {
@@ -533,19 +841,56 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImpor
533
841
  }]
534
842
  }] });
535
843
 
844
+ class SelectCellEditorComponent extends AbstractEditCellEditor {
845
+ constructor() {
846
+ super();
847
+ this.selectOptions = computed(() => {
848
+ return this.field().options;
849
+ });
850
+ }
851
+ updateValue(value) {
852
+ if (!value) {
853
+ this.updateFieldValue();
854
+ this.closePopover();
855
+ }
856
+ }
857
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: SelectCellEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
858
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.0.2", type: SelectCellEditorComponent, isStandalone: true, selector: "single-select-cell-editor", inputs: { isMultiple: "isMultiple" }, host: { classAttribute: "d-block h-100" }, usesInheritance: true, ngImport: i0, template: `<thy-select [(ngModel)]="modelValue" [thyAutoExpand]="true" (thyOnExpandStatusChange)="updateValue($event)">
859
+ <thy-option *ngFor="let option of selectOptions()" [thyValue]="option.id" [thyLabelText]="option.name"> </thy-option>
860
+ </thy-select> `, isInline: true, dependencies: [{ kind: "directive", type: NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: ThySelect, selector: "thy-select,thy-custom-select", inputs: ["thyDropdownWidthMode", "thyShowSearch", "thyPlaceHolder", "thyServerSearch", "thyLoadState", "thyAutoActiveFirstItem", "thyMode", "thySize", "thyEmptyStateText", "thyEmptySearchMessageText", "thyEnableScrollLoad", "thyAllowClear", "thyDisabled", "thySortComparator", "thyFooterTemplate", "thyPlacement", "thyOrigin", "thyFooterClass", "thyAutoExpand", "thyHasBackdrop", "thyMaxTagCount", "thyBorderless", "thyOptions", "thyPreset"], outputs: ["thyOnSearch", "thyOnScrollToBottom", "thyOnExpandStatusChange"], exportAs: ["thySelect"] }, { kind: "component", type: ThyOption, selector: "thy-option", inputs: ["thyValue", "thyRawValue", "thyLabelText", "thyShowOptionCustom", "thySearchKey", "thyDisabled"], outputs: ["selectionChange", "visibleChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
861
+ }
862
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: SelectCellEditorComponent, decorators: [{
863
+ type: Component,
864
+ args: [{
865
+ selector: 'single-select-cell-editor',
866
+ template: `<thy-select [(ngModel)]="modelValue" [thyAutoExpand]="true" (thyOnExpandStatusChange)="updateValue($event)">
867
+ <thy-option *ngFor="let option of selectOptions()" [thyValue]="option.id" [thyLabelText]="option.name"> </thy-option>
868
+ </thy-select> `,
869
+ standalone: true,
870
+ changeDetection: ChangeDetectionStrategy.OnPush,
871
+ host: {
872
+ class: 'd-block h-100'
873
+ },
874
+ imports: [NgIf, NgForOf, FormsModule, ThySelect, ThyOption, ThyTag, ThyIcon]
875
+ }]
876
+ }], ctorParameters: () => [], propDecorators: { isMultiple: [{
877
+ type: Input
878
+ }] } });
879
+
536
880
  const GRID_CELL_EDITOR_MAP = {
537
- [AITableFieldType.Text]: TextCellEditorComponent,
538
- [AITableFieldType.SingleSelect]: SingleSelectCellEditorComponent,
539
- [AITableFieldType.Number]: NumberCellEditorComponent,
540
- [AITableFieldType.DateTime]: DateTimeCellEditorComponent,
541
- [AITableFieldType.Rating]: RatingCellEditorComponent,
542
- [AITableFieldType.Link]: LinkCellEditorComponent
881
+ [AITableFieldType.text]: TextCellEditorComponent,
882
+ [AITableFieldType.select]: SelectCellEditorComponent,
883
+ [AITableFieldType.number]: NumberCellEditorComponent,
884
+ [AITableFieldType.date]: DateTimeCellEditorComponent,
885
+ [AITableFieldType.rate]: RatingCellEditorComponent,
886
+ [AITableFieldType.link]: LinkCellEditorComponent
543
887
  };
544
888
 
545
889
  class AITableGridEventService {
546
890
  constructor(thyPopover) {
547
891
  this.thyPopover = thyPopover;
548
892
  this.takeUntilDestroyed = takeUntilDestroyed();
893
+ this.mousedownEvent$ = new Subject();
549
894
  }
550
895
  initialize(aiTable, aiFieldRenderers) {
551
896
  this.aiTable = aiTable;
@@ -557,17 +902,22 @@ class AITableGridEventService {
557
902
  .subscribe((event) => {
558
903
  this.dblClick(event);
559
904
  });
905
+ fromEvent(element, 'mousedown')
906
+ .pipe(this.takeUntilDestroyed)
907
+ .subscribe((event) => {
908
+ this.mousedownEvent$.next(event);
909
+ });
560
910
  }
561
911
  dblClick(event) {
562
912
  const cellDom = event.target.closest('.grid-cell');
563
913
  const type = cellDom && cellDom.getAttribute('type');
564
- if (type && DBL_CLICK_EDIT_TYPE.includes(Number(type))) {
914
+ if (type && DBL_CLICK_EDIT_TYPE.includes(type)) {
565
915
  this.openEdit(cellDom);
566
916
  }
567
917
  }
568
918
  getEditorComponent(type) {
569
919
  if (this.aiFieldRenderers && this.aiFieldRenderers[type]) {
570
- return this.aiFieldRenderers[type].edit;
920
+ return this.aiFieldRenderers[type].editor;
571
921
  }
572
922
  return GRID_CELL_EDITOR_MAP[type];
573
923
  }
@@ -590,10 +940,11 @@ class AITableGridEventService {
590
940
  height: height + 2 + 'px',
591
941
  placement: 'top',
592
942
  offset: -(height + 4),
943
+ minWidth: width,
593
944
  initialState: {
594
945
  field: field,
595
946
  record: record,
596
- aiTable: signal(this.aiTable)
947
+ aiTable: this.aiTable
597
948
  },
598
949
  panelClass: 'grid-cell-editor',
599
950
  outsideClosable: false,
@@ -609,132 +960,141 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImpor
609
960
  type: Injectable
610
961
  }], ctorParameters: () => [{ type: i1$1.ThyPopover }] });
611
962
 
612
- const BasicFieldTypes = [
613
- {
614
- type: AITableFieldType.Text,
615
- name: '文本',
616
- icon: 'font'
617
- },
618
- {
619
- type: AITableFieldType.SingleSelect,
620
- name: '单选',
621
- icon: 'check-circle'
622
- },
623
- {
624
- type: AITableFieldType.Number,
625
- name: '数字',
626
- icon: 'hashtag'
627
- },
628
- {
629
- type: AITableFieldType.DateTime,
630
- name: '日期',
631
- icon: 'calendar'
632
- },
633
- {
634
- type: AITableFieldType.Rating,
635
- name: '评分',
636
- icon: 'star-circle'
637
- },
638
- {
639
- type: AITableFieldType.Link,
640
- name: '链接',
641
- icon: 'link-insert'
963
+ class FieldMenu {
964
+ execute(menu) {
965
+ const field = getRecordOrField(this.aiTable.fields, this.fieldId);
966
+ menu.exec && menu.exec(this.aiTable, field, this.origin);
642
967
  }
643
- ];
644
- const FieldTypes = [...BasicFieldTypes];
645
- const FieldTypesMap = helpers.keyBy([...FieldTypes], 'type');
646
-
647
- class FieldPropertyEditorComponent {
648
- constructor() {
649
- this.fields = input.required();
650
- this.confirmAction = null;
651
- this.field = signal({ id: idCreator(), type: AITableFieldType.Text, name: '' });
652
- this.fieldType = computed(() => {
653
- return FieldTypesMap[this.field().type];
654
- });
655
- this.fieldMaxLength = 32;
656
- this.validatorConfig = {
657
- validationMessages: {
658
- fieldName: {
659
- required: '列名不能为空',
660
- thyUniqueCheck: '列名已存在'
661
- }
662
- }
663
- };
664
- this.selectableFields = FieldTypes;
665
- this.thyPopoverRef = inject((ThyPopoverRef));
666
- this.checkUniqueName = (fieldName) => {
667
- fieldName = fieldName?.trim();
668
- return of(!!this.fields()?.find((field) => field.name === fieldName));
669
- };
670
- }
671
- ngOnInit() { }
672
- selectFieldType(fieldType) {
673
- this.field.update((item) => ({ ...item, type: fieldType }));
674
- }
675
- addField() {
676
- this.confirmAction(this.field());
677
- this.thyPopoverRef.close();
678
- }
679
- cancel() {
680
- this.thyPopoverRef.close();
681
- }
682
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: FieldPropertyEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
683
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.0.2", type: FieldPropertyEditorComponent, isStandalone: true, selector: "field-property-editor", inputs: { fields: { classPropertyName: "fields", publicName: "fields", isSignal: true, isRequired: true, transformFunction: null }, confirmAction: { classPropertyName: "confirmAction", publicName: "confirmAction", isSignal: false, isRequired: true, transformFunction: null } }, host: { classAttribute: "field-property-editor d-block pl-5 pr-5 pb-5 pt-4" }, ngImport: i0, template: "<form thyForm name=\"createPropertyForm\" [thyFormValidatorConfig]=\"validatorConfig\" thyLayout=\"vertical\">\n <thy-form-group thyLabelRequired thyLabelText=\"\u8868\u683C\u5217\u540D\">\n <thy-input-group>\n <input\n thyInput\n thyAutofocus\n name=\"fieldName\"\n [maxlength]=\"fieldMaxLength\"\n [(ngModel)]=\"field().name\"\n required\n placeholder=\"\u8F93\u5165\u5217\u540D\u79F0\"\n [thyUniqueCheck]=\"checkUniqueName\"\n />\n <ng-template #suffix>\n <thy-input-count></thy-input-count>\n </ng-template>\n </thy-input-group>\n </thy-form-group>\n <thy-form-group thyLabelText=\"\u5217\u7C7B\u578B\">\n <div class=\"ml-n5 mr-n5\">\n <thy-list-item [thyDropdown]=\"menu\" thyTrigger=\"hover\" thyPlacement=\"right\" class=\"justify-content-between\">\n <span>\n <thy-icon [thyIconName]=\"fieldType().icon\" class=\"text-desc mr-2\"></thy-icon>\n <span>{{ fieldType().name }}</span>\n </span>\n <thy-icon thyIconName=\"angle-right\" class=\"text-desc\"></thy-icon>\n </thy-list-item>\n </div>\n </thy-form-group>\n <thy-form-group-footer thyAlign=\"right\">\n <button thyButton=\"link-secondary\" (click)=\"cancel()\" thySize=\"sm\">\u53D6\u6D88</button>\n <button thyButton=\"primary\" (thyFormSubmit)=\"addField()\" thySize=\"sm\">\u786E\u5B9A</button>\n </thy-form-group-footer>\n</form>\n\n<thy-dropdown-menu #menu>\n @for (item of selectableFields; track item.type) {\n <a thyDropdownMenuItem href=\"javascript:;\" (click)=\"selectFieldType(item.type)\">\n <thy-icon [thyIconName]=\"item.icon\"></thy-icon>\n <span thyDropdownMenuItemName>{{ item.name }}</span>\n </a>\n }\n</thy-dropdown-menu>\n", styles: [":host{width:350px}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: ThyIcon, selector: "thy-icon, [thy-icon]", inputs: ["thyIconType", "thyTwotoneColor", "thyIconName", "thyIconRotate", "thyIconSet", "thyIconLegging", "thyIconLinearGradient"] }, { kind: "component", type: ThyInputGroup, selector: "thy-input-group", inputs: ["thyAppendText", "thyAppendTextTranslateKey", "thyPrependText", "thyPrependTextTranslateKey", "thySize"] }, { kind: "component", type: ThyInputCount, selector: "thy-input-count", inputs: ["thyInput"] }, { kind: "directive", type: ThyInputDirective, selector: "input[thyInput], select[thyInput], textarea[thyInput]", inputs: ["thySize"], exportAs: ["thyInput"] }, { kind: "directive", type: ThyUniqueCheckValidator, selector: "[thyUniqueCheck]", inputs: ["thyUniqueCheck"] }, { kind: "directive", type: ThyDropdownDirective, selector: "[thyDropdown]", inputs: ["thyDropdownMenu", "thyDropdown", "thyTrigger", "thyShowDelay", "thyHideDelay", "thyActiveClass", "thyPopoverOptions", "thyPlacement", "thyMenuInsideClosable", "thyPanelClass"], outputs: ["thyActiveChange"] }, { kind: "component", type: ThyDropdownMenuComponent, selector: "thy-dropdown-menu", inputs: ["thyWidth", "thyImmediateRender"] }, { kind: "directive", type: ThyDropdownMenuItemDirective, selector: "[thyDropdownMenuItem]", inputs: ["thyType", "thyDisabled"] }, { kind: "directive", type: ThyDropdownMenuItemNameDirective, selector: "[thyDropdownMenuItemName]" }, { kind: "component", type: ThyButton, selector: "thy-button,[thy-button],[thyButton]", inputs: ["thyButton", "thyType", "thyLoading", "thyLoadingText", "thySize", "thyIcon", "thyBlock"] }, { kind: "ngmodule", type: ThyFormModule }, { kind: "directive", type: i2.ThyFormDirective, selector: "[thyForm],[thy-form]", inputs: ["thyLayout", "thyEnterKeyMode", "thyFormValidatorConfig"], exportAs: ["thyForm"] }, { kind: "component", type: i2.ThyFormGroup, selector: "thy-form-group", inputs: ["thyLabelText", "thyLabelTextTranslateKey", "thyLabelRequired", "thyLabelPaddingTopClear", "thyFeedbackIcon", "thyTipsMode", "thyTips", "thyTipsTranslateKey", "thyRowFill"] }, { kind: "directive", type: i2.ThyFormSubmitDirective, selector: "[thyFormSubmit],[thy-form-submit]", outputs: ["thyFormSubmit"] }, { kind: "component", type: i2.ThyFormGroupFooter, selector: "thy-form-group-footer", inputs: ["thyAlign"] }, { kind: "component", type: ThyListItem, selector: "thy-list-item,[thy-list-item]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
968
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: FieldMenu, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
969
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.0.2", type: FieldMenu, isStandalone: true, selector: "field-menu", inputs: { fieldId: "fieldId", aiTable: "aiTable", fieldMenus: "fieldMenus", origin: "origin" }, ngImport: i0, template: "@for (menu of fieldMenus; track index; let index = $index) {\n @if (menu.id === 'divider') {\n <thy-divider [thyStyle]=\"'solid'\"></thy-divider>\n } @else {\n <a thyDropdownMenuItem href=\"javascript:;\" (click)=\"execute(menu)\">\n <thy-icon [thyIconName]=\"menu.icon!\"></thy-icon>\n <span>{{ menu.name! }}</span>\n </a>\n }\n}\n", dependencies: [{ kind: "component", type: ThyIcon, selector: "thy-icon, [thy-icon]", inputs: ["thyIconType", "thyTwotoneColor", "thyIconName", "thyIconRotate", "thyIconSet", "thyIconLegging", "thyIconLinearGradient"] }, { kind: "component", type: ThyDivider, selector: "thy-divider", inputs: ["thyVertical", "thyStyle", "thyColor", "thyText", "thyTextDirection", "thyDeeper"] }, { kind: "directive", type: ThyDropdownMenuItemDirective, selector: "[thyDropdownMenuItem]", inputs: ["thyType", "thyDisabled"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
684
970
  }
685
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: FieldPropertyEditorComponent, decorators: [{
971
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: FieldMenu, decorators: [{
686
972
  type: Component,
687
- args: [{ selector: 'field-property-editor', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [
688
- NgIf,
689
- NgForOf,
690
- FormsModule,
973
+ args: [{ selector: 'field-menu', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [
691
974
  ThyIcon,
692
- ThyInput,
693
- ThyInputGroup,
694
- ThyInputCount,
695
- ThyInputDirective,
696
- ThyConfirmValidatorDirective,
697
- ThyUniqueCheckValidator,
698
- ThyDropdownDirective,
975
+ ThyDivider,
699
976
  ThyDropdownMenuComponent,
700
977
  ThyDropdownMenuItemDirective,
701
978
  ThyDropdownMenuItemNameDirective,
702
- ThyDropdownMenuItemIconDirective,
703
- ThyButton,
704
- ThyFormModule,
705
- ThyListItem
706
- ], host: {
707
- class: 'field-property-editor d-block pl-5 pr-5 pb-5 pt-4'
708
- }, template: "<form thyForm name=\"createPropertyForm\" [thyFormValidatorConfig]=\"validatorConfig\" thyLayout=\"vertical\">\n <thy-form-group thyLabelRequired thyLabelText=\"\u8868\u683C\u5217\u540D\">\n <thy-input-group>\n <input\n thyInput\n thyAutofocus\n name=\"fieldName\"\n [maxlength]=\"fieldMaxLength\"\n [(ngModel)]=\"field().name\"\n required\n placeholder=\"\u8F93\u5165\u5217\u540D\u79F0\"\n [thyUniqueCheck]=\"checkUniqueName\"\n />\n <ng-template #suffix>\n <thy-input-count></thy-input-count>\n </ng-template>\n </thy-input-group>\n </thy-form-group>\n <thy-form-group thyLabelText=\"\u5217\u7C7B\u578B\">\n <div class=\"ml-n5 mr-n5\">\n <thy-list-item [thyDropdown]=\"menu\" thyTrigger=\"hover\" thyPlacement=\"right\" class=\"justify-content-between\">\n <span>\n <thy-icon [thyIconName]=\"fieldType().icon\" class=\"text-desc mr-2\"></thy-icon>\n <span>{{ fieldType().name }}</span>\n </span>\n <thy-icon thyIconName=\"angle-right\" class=\"text-desc\"></thy-icon>\n </thy-list-item>\n </div>\n </thy-form-group>\n <thy-form-group-footer thyAlign=\"right\">\n <button thyButton=\"link-secondary\" (click)=\"cancel()\" thySize=\"sm\">\u53D6\u6D88</button>\n <button thyButton=\"primary\" (thyFormSubmit)=\"addField()\" thySize=\"sm\">\u786E\u5B9A</button>\n </thy-form-group-footer>\n</form>\n\n<thy-dropdown-menu #menu>\n @for (item of selectableFields; track item.type) {\n <a thyDropdownMenuItem href=\"javascript:;\" (click)=\"selectFieldType(item.type)\">\n <thy-icon [thyIconName]=\"item.icon\"></thy-icon>\n <span thyDropdownMenuItemName>{{ item.name }}</span>\n </a>\n }\n</thy-dropdown-menu>\n", styles: [":host{width:350px}\n"] }]
709
- }], ctorParameters: () => [], propDecorators: { confirmAction: [{
979
+ ThyDropdownMenuItemIconDirective
980
+ ], template: "@for (menu of fieldMenus; track index; let index = $index) {\n @if (menu.id === 'divider') {\n <thy-divider [thyStyle]=\"'solid'\"></thy-divider>\n } @else {\n <a thyDropdownMenuItem href=\"javascript:;\" (click)=\"execute(menu)\">\n <thy-icon [thyIconName]=\"menu.icon!\"></thy-icon>\n <span>{{ menu.name! }}</span>\n </a>\n }\n}\n" }]
981
+ }], propDecorators: { fieldId: [{
982
+ type: Input,
983
+ args: [{ required: true }]
984
+ }], aiTable: [{
710
985
  type: Input,
711
986
  args: [{ required: true }]
987
+ }], fieldMenus: [{
988
+ type: Input,
989
+ args: [{ required: true }]
990
+ }], origin: [{
991
+ type: Input
712
992
  }] } });
713
993
 
714
- class AITableGridComponent {
715
- constructor(elementRef, aiTableGridEventService, thyPopover) {
994
+ class AITableGridSelectionService {
995
+ constructor() { }
996
+ initialize(aiTable) {
997
+ this.aiTable = aiTable;
998
+ }
999
+ clearSelection() {
1000
+ this.aiTable.selection.set({
1001
+ selectedRecords: new Map(),
1002
+ selectedFields: new Map(),
1003
+ selectedCells: new Map()
1004
+ });
1005
+ }
1006
+ selectCell(recordId, fieldId) {
1007
+ this.clearSelection();
1008
+ this.aiTable.selection().selectedCells.set(recordId, { [fieldId]: true });
1009
+ }
1010
+ selectField(fieldId) {
1011
+ this.clearSelection();
1012
+ this.aiTable.selection().selectedFields.set(fieldId, true);
1013
+ }
1014
+ selectRecord(recordId) {
1015
+ if (this.aiTable.selection().selectedRecords.has(recordId)) {
1016
+ this.aiTable.selection().selectedRecords.delete(recordId);
1017
+ }
1018
+ else {
1019
+ this.aiTable.selection().selectedRecords.set(recordId, true);
1020
+ }
1021
+ this.aiTable.selection.set({
1022
+ selectedRecords: this.aiTable.selection().selectedRecords,
1023
+ selectedFields: new Map(),
1024
+ selectedCells: new Map()
1025
+ });
1026
+ }
1027
+ toggleSelectAll(checked) {
1028
+ this.clearSelection();
1029
+ if (checked) {
1030
+ this.aiTable.records().forEach((item) => {
1031
+ this.selectRecord(item.id);
1032
+ });
1033
+ }
1034
+ }
1035
+ updateSelect(event) {
1036
+ const target = event.target;
1037
+ const cellDom = target.closest('.grid-cell');
1038
+ const colDom = target.closest('.grid-field');
1039
+ if (cellDom) {
1040
+ const fieldId = cellDom.getAttribute('fieldId');
1041
+ const recordId = cellDom.getAttribute('recordId');
1042
+ fieldId && recordId && this.selectCell(recordId, fieldId);
1043
+ }
1044
+ if (colDom) {
1045
+ const fieldId = colDom.getAttribute('fieldId');
1046
+ fieldId && this.selectField(fieldId);
1047
+ }
1048
+ }
1049
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: AITableGridSelectionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1050
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: AITableGridSelectionService }); }
1051
+ }
1052
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: AITableGridSelectionService, decorators: [{
1053
+ type: Injectable
1054
+ }], ctorParameters: () => [] });
1055
+
1056
+ class AITableGrid {
1057
+ get isSelectedAll() {
1058
+ return this.aiTable.selection().selectedRecords.size === this.aiRecords().length;
1059
+ }
1060
+ constructor(elementRef, aiTableGridEventService, aiTableGridSelectionService, aiTableGridFieldService, ngZone) {
716
1061
  this.elementRef = elementRef;
717
1062
  this.aiTableGridEventService = aiTableGridEventService;
718
- this.thyPopover = thyPopover;
1063
+ this.aiTableGridSelectionService = aiTableGridSelectionService;
1064
+ this.aiTableGridFieldService = aiTableGridFieldService;
1065
+ this.ngZone = ngZone;
719
1066
  this.aiRecords = model.required();
720
1067
  this.aiFields = model.required();
721
1068
  this.aiRowHeight = input();
722
- this.aiFieldRenderers = input();
1069
+ this.aiFieldConfig = input();
723
1070
  this.aiReadonly = input();
1071
+ this.aiPlugins = input();
724
1072
  this.AITableFieldType = AITableFieldType;
725
1073
  this.takeUntilDestroyed = takeUntilDestroyed();
726
1074
  this.onChange = output();
1075
+ this.aiTableInitialized = output();
727
1076
  this.gridData = computed(() => {
728
- return buildGridData(this.aiRecords(), this.aiFields());
1077
+ return buildGridData(this.aiRecords(), this.aiFields(), this.aiTable.selection());
729
1078
  });
730
1079
  }
731
1080
  ngOnInit() {
732
1081
  this.initAITable();
733
- this.aiTableGridEventService.initialize(this.aiTable, this.aiFieldRenderers());
734
- this.aiTableGridEventService.registerEvents(this.elementRef.nativeElement);
1082
+ this.initService();
1083
+ this.buildFieldMenus();
1084
+ this.ngZone.runOutsideAngular(() => {
1085
+ this.aiTableGridEventService.mousedownEvent$.pipe(this.takeUntilDestroyed).subscribe((event) => {
1086
+ if (event?.target) {
1087
+ this.aiTableGridSelectionService.updateSelect(event);
1088
+ }
1089
+ });
1090
+ });
735
1091
  }
736
1092
  initAITable() {
737
1093
  this.aiTable = createAITable(this.aiRecords, this.aiFields);
1094
+ this.aiPlugins()?.forEach((plugin) => {
1095
+ this.aiTable = plugin(this.aiTable);
1096
+ });
1097
+ this.aiTableInitialized.emit(this.aiTable);
738
1098
  this.aiTable.onChange = () => {
739
1099
  this.onChange.emit({
740
1100
  records: this.aiRecords(),
@@ -743,26 +1103,33 @@ class AITableGridComponent {
743
1103
  });
744
1104
  };
745
1105
  }
1106
+ initService() {
1107
+ this.aiTableGridEventService.initialize(this.aiTable, this.aiFieldConfig()?.fieldPropertyEditor);
1108
+ this.aiTableGridSelectionService.initialize(this.aiTable);
1109
+ this.aiTableGridEventService.registerEvents(this.elementRef.nativeElement);
1110
+ this.aiTableGridFieldService.initAIFieldConfig(this.aiFieldConfig());
1111
+ AI_TABLE_GRID_FIELD_SERVICE_MAP.set(this.aiTable, this.aiTableGridFieldService);
1112
+ }
1113
+ buildFieldMenus() {
1114
+ this.fieldMenus = this.aiFieldConfig()?.fieldMenus ?? DefaultFieldMenus;
1115
+ }
746
1116
  addRecord() {
747
1117
  Actions.addRecord(this.aiTable, getDefaultRecord(this.aiFields()), [this.aiRecords().length]);
748
1118
  }
749
- addField(event) {
750
- this.thyPopover.open(FieldPropertyEditorComponent, {
751
- origin: event.currentTarget,
752
- manualClosure: true,
753
- placement: 'bottomLeft',
754
- initialState: {
755
- fields: this.aiFields,
756
- confirmAction: (field) => {
757
- Actions.addField(this.aiTable, field, [this.aiFields().length]);
758
- }
759
- }
760
- });
1119
+ selectRecord(recordId) {
1120
+ this.aiTableGridSelectionService.selectRecord(recordId);
1121
+ }
1122
+ toggleSelectAll(checked) {
1123
+ this.aiTableGridSelectionService.toggleSelectAll(checked);
761
1124
  }
762
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: AITableGridComponent, deps: [{ token: i0.ElementRef }, { token: AITableGridEventService }, { token: i1$1.ThyPopover }], target: i0.ɵɵFactoryTarget.Component }); }
763
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.0.2", type: AITableGridComponent, isStandalone: true, selector: "ai-table-grid", inputs: { aiRecords: { classPropertyName: "aiRecords", publicName: "aiRecords", isSignal: true, isRequired: true, transformFunction: null }, aiFields: { classPropertyName: "aiFields", publicName: "aiFields", isSignal: true, isRequired: true, transformFunction: null }, aiRowHeight: { classPropertyName: "aiRowHeight", publicName: "aiRowHeight", isSignal: true, isRequired: false, transformFunction: null }, aiFieldRenderers: { classPropertyName: "aiFieldRenderers", publicName: "aiFieldRenderers", isSignal: true, isRequired: false, transformFunction: null }, aiReadonly: { classPropertyName: "aiReadonly", publicName: "aiReadonly", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { aiRecords: "aiRecordsChange", aiFields: "aiFieldsChange", onChange: "onChange" }, host: { classAttribute: "ai-table-grid" }, providers: [ThyTooltipService, AITableGridEventService], ngImport: i0, template: "<div class=\"grid-header d-flex\">\n <div class=\"grid-column-checkbox grid-cell\">\n <input type=\"checkbox\" />\n </div>\n @for (field of gridData().fields; track field.id) {\n <div class=\"grid-cell\">{{ field.name }}</div>\n }\n <div class=\"grid-column-blank cursor-pointer\" (click)=\"addField($event)\"><thy-icon thyIconName=\"plus\"></thy-icon></div>\n</div>\n<div class=\"grid-body d-flex\">\n @for (record of gridData().records; track record.id; let index = $index) {\n <div class=\"grid-row d-flex\">\n <div class=\"grid-row-index\">\n {{ index + 1 }}\n </div>\n @for (field of gridData().fields; track $index) {\n <div class=\"grid-cell\" [attr.type]=\"[field.type]\" [attr.fieldId]=\"[field.id]\" [attr.recordId]=\"[record.id]\" #cell>\n @switch (field.type) {\n @case (AITableFieldType.SingleSelect) {\n @if (record.value[field.id] | selectOption: field['options']; as selectedOption) {\n <thy-tag [thyColor]=\"selectedOption!.color!\">{{ selectedOption.name }}</thy-tag>\n }\n }\n @case (AITableFieldType.DateTime) {\n {{ record.value[field.id] | thyDatePickerFormat }}\n }\n @case (AITableFieldType.Rating) {\n <thy-rate [(ngModel)]=\"record.value[field.id]\"></thy-rate>\n }\n @case (AITableFieldType.Link) {\n <a\n class=\"d-block pl-4 pr-4\"\n thyStopPropagation\n thyFlexibleText\n [thyTooltipContent]=\"record.value[field.id]?.text\"\n [href]=\"record.value[field.id]?.url\"\n target=\"_blank\"\n >{{ record.value[field.id]?.text }}</a\n >\n }\n @default {\n {{ record.value[field.id] }}\n }\n }\n </div>\n }\n <div class=\"grid-column-blank\"></div>\n </div>\n }\n <div class=\"grid-row-insert grid-row cursor-pointer\" (click)=\"addRecord()\">\n <thy-icon thyIconName=\"plus\"></thy-icon>\n </div>\n</div>\n\n<div #activeBorder class=\"active-border\"></div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "pipe", type: SelectOptionPipe, name: "selectOption" }, { kind: "component", type: ThyTag, selector: "thy-tag,[thyTag]", inputs: ["thyTag", "thyShape", "thyColor", "thyTheme", "thySize", "thyHoverable"] }, { kind: "ngmodule", type: ThyPopoverModule }, { kind: "component", type: ThyIcon, selector: "thy-icon, [thy-icon]", inputs: ["thyIconType", "thyTwotoneColor", "thyIconName", "thyIconRotate", "thyIconSet", "thyIconLegging", "thyIconLinearGradient"] }, { kind: "component", type: ThyRate, selector: "thy-rate", inputs: ["thyCount", "thyDisabled", "thyAllowHalf", "thyAllowClear", "thyTooltips", "thyIconTemplate"], outputs: ["thyItemHoverChange"] }, { kind: "pipe", type: ThyDatePickerFormatPipe, name: "thyDatePickerFormat" }, { kind: "ngmodule", type: ThyTooltipModule }, { kind: "component", type: ThyFlexibleText, selector: "thy-flexible-text,[thyFlexibleText]", inputs: ["thyTooltipTrigger", "thyContainerClass", "thyTooltipContent", "thyTooltipPlacement", "thyTooltipOffset"], exportAs: ["thyFlexibleText"] }, { kind: "directive", type: ThyStopPropagationDirective, selector: "[thyStopPropagation]", inputs: ["thyStopPropagation"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1125
+ addField(gridColumnBlank) {
1126
+ const field = signal(createDefaultField(this.aiTable, AITableFieldType.text));
1127
+ this.aiTableGridFieldService.editFieldProperty(gridColumnBlank, this.aiTable, field, false);
1128
+ }
1129
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: AITableGrid, deps: [{ token: i0.ElementRef }, { token: AITableGridEventService }, { token: AITableGridSelectionService }, { token: AITableGridFieldService }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component }); }
1130
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.0.2", type: AITableGrid, isStandalone: true, selector: "ai-table-grid", inputs: { aiRecords: { classPropertyName: "aiRecords", publicName: "aiRecords", isSignal: true, isRequired: true, transformFunction: null }, aiFields: { classPropertyName: "aiFields", publicName: "aiFields", isSignal: true, isRequired: true, transformFunction: null }, aiRowHeight: { classPropertyName: "aiRowHeight", publicName: "aiRowHeight", isSignal: true, isRequired: false, transformFunction: null }, aiFieldConfig: { classPropertyName: "aiFieldConfig", publicName: "aiFieldConfig", isSignal: true, isRequired: false, transformFunction: null }, aiReadonly: { classPropertyName: "aiReadonly", publicName: "aiReadonly", isSignal: true, isRequired: false, transformFunction: null }, aiPlugins: { classPropertyName: "aiPlugins", publicName: "aiPlugins", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { aiRecords: "aiRecordsChange", aiFields: "aiFieldsChange", onChange: "onChange", aiTableInitialized: "aiTableInitialized" }, host: { classAttribute: "ai-table-grid" }, providers: [ThyTooltipService, AITableGridEventService, AITableGridFieldService, AITableGridSelectionService], ngImport: i0, template: "<div class=\"grid-header d-flex\">\n <div class=\"grid-column-checkbox grid-cell\">\n <label thyCheckbox thyLabelText=\"\" [ngModel]=\"isSelectedAll\" (ngModelChange)=\"toggleSelectAll($event)\"></label>\n </div>\n @for (field of gridData().fields; track field.id) {\n <div\n class=\"grid-cell grid-field\"\n #fieldAction\n [attr.fieldId]=\"field.id\"\n [ngClass]=\"{ highlight: aiTable.selection().selectedFields.has(field.id) }\"\n [ngStyle]=\"{ width: field.width + 'px' }\"\n >\n <span class=\"text-truncate\">\n <thy-icon [thyIconName]=\"field.icon!\" class=\"mr-2 text-muted\"></thy-icon>\n <span>{{ field.name }}</span>\n </span>\n <a\n class=\"grid-field-action\"\n thyAction\n thyActiveClass=\"active\"\n thyIcon=\"more-vertical\"\n [thyDropdown]=\"fieldMenu\"\n href=\"javascript:;\"\n >\n <thy-dropdown-menu #fieldMenu>\n <field-menu [origin]=\"fieldAction\" [fieldId]=\"field.id\" [aiTable]=\"aiTable\" [fieldMenus]=\"fieldMenus\"></field-menu>\n </thy-dropdown-menu>\n </a>\n </div>\n }\n <div class=\"grid-column-blank cursor-pointer\" #gridColumnBlank (click)=\"addField(gridColumnBlank)\">\n <thy-icon thyIconName=\"plus\"></thy-icon>\n </div>\n</div>\n<div class=\"grid-body d-flex\">\n @for (record of gridData().records; track record.id; let index = $index) {\n <div class=\"grid-row d-flex\" [ngClass]=\"{ highlight: aiTable.selection().selectedRecords.has(record.id) }\">\n <div class=\"grid-row-index\">\n <label\n [ngClass]=\"record.checked ? 'checked-box' : 'unchecked-box'\"\n thyCheckbox\n thyLabelText=\"\"\n [ngModel]=\"record.checked\"\n (ngModelChange)=\"selectRecord(record.id)\"\n ></label>\n <span [ngClass]=\"record.checked ? 'grid-row-no-number' : 'grid-row-number'\"> {{ index + 1 }} </span>\n </div>\n @for (field of gridData().fields; track $index) {\n <div\n class=\"grid-cell\"\n [ngClass]=\"{\n highlight: aiTable.selection().selectedCells.has(record.id) || aiTable.selection().selectedFields.has(field.id),\n selected: aiTable.selection().selectedCells.get(record.id)?.hasOwnProperty(field.id)\n }\"\n [attr.type]=\"[field.type]\"\n [attr.fieldId]=\"[field.id]\"\n [attr.recordId]=\"[record.id]\"\n [ngStyle]=\"{ width: field.width + 'px' }\"\n #cell\n >\n @switch (field.type) {\n @case (AITableFieldType.select) {\n @if (!field.isMultiple && record.values[field.id] | selectOption: field['options']; as selectedOption) {\n <thy-tag [thyColor]=\"selectedOption!.color!\">{{ selectedOption.name }}</thy-tag>\n }\n }\n @case (AITableFieldType.date) {\n {{ record.values[field.id] | thyDatePickerFormat }}\n }\n @case (AITableFieldType.rate) {\n <thy-rate [(ngModel)]=\"record.values[field.id]\"></thy-rate>\n }\n @case (AITableFieldType.link) {\n <a\n class=\"d-block\"\n thyStopPropagation\n thyFlexibleText\n [thyTooltipContent]=\"record.values[field.id]?.text\"\n [href]=\"record.values[field.id]?.url\"\n target=\"_blank\"\n >{{ record.values[field.id]?.text }}</a\n >\n }\n @default {\n <span class=\"text-truncate\"> {{ record.values[field.id] }}</span>\n }\n }\n <div class=\"autofill-container\"></div>\n </div>\n }\n <div class=\"grid-column-blank\"></div>\n </div>\n }\n <div class=\"grid-row-insert grid-row cursor-pointer\" (click)=\"addRecord()\">\n <thy-icon thyIconName=\"plus\"></thy-icon>\n </div>\n</div>\n\n<div #activeBorder class=\"active-border\"></div>\n", dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i4.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "pipe", type: SelectOptionPipe, name: "selectOption" }, { kind: "component", type: ThyTag, selector: "thy-tag,[thyTag]", inputs: ["thyTag", "thyShape", "thyColor", "thyTheme", "thySize", "thyHoverable"] }, { kind: "ngmodule", type: ThyPopoverModule }, { kind: "component", type: ThyIcon, selector: "thy-icon, [thy-icon]", inputs: ["thyIconType", "thyTwotoneColor", "thyIconName", "thyIconRotate", "thyIconSet", "thyIconLegging", "thyIconLinearGradient"] }, { kind: "component", type: ThyRate, selector: "thy-rate", inputs: ["thyCount", "thyDisabled", "thyAllowHalf", "thyAllowClear", "thyTooltips", "thyIconTemplate"], outputs: ["thyItemHoverChange"] }, { kind: "pipe", type: ThyDatePickerFormatPipe, name: "thyDatePickerFormat" }, { kind: "ngmodule", type: ThyTooltipModule }, { kind: "component", type: ThyFlexibleText, selector: "thy-flexible-text,[thyFlexibleText]", inputs: ["thyTooltipTrigger", "thyContainerClass", "thyTooltipContent", "thyTooltipPlacement", "thyTooltipOffset"], exportAs: ["thyFlexibleText"] }, { kind: "directive", type: ThyStopPropagationDirective, selector: "[thyStopPropagation]", inputs: ["thyStopPropagation"] }, { kind: "component", type: FieldMenu, selector: "field-menu", inputs: ["fieldId", "aiTable", "fieldMenus", "origin"] }, { kind: "component", type: ThyAction, selector: "thy-action, [thyAction]", inputs: ["thyType", "thyIcon", "thyActionIcon", "thyActive", "thyActionActive", "thyTheme", "thyHoverIcon", "thyDisabled"] }, { kind: "directive", type: ThyDropdownDirective, selector: "[thyDropdown]", inputs: ["thyDropdownMenu", "thyDropdown", "thyTrigger", "thyShowDelay", "thyHideDelay", "thyActiveClass", "thyPopoverOptions", "thyPlacement", "thyMenuInsideClosable", "thyPanelClass"], outputs: ["thyActiveChange"] }, { kind: "component", type: ThyDropdownMenuComponent, selector: "thy-dropdown-menu", inputs: ["thyWidth", "thyImmediateRender"] }, { kind: "ngmodule", type: ThyCheckboxModule }, { kind: "component", type: i6.ThyCheckbox, selector: "thy-checkbox,[thy-checkbox],[thyCheckbox]", inputs: ["thyIndeterminate"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
764
1131
  }
765
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: AITableGridComponent, decorators: [{
1132
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: AITableGrid, decorators: [{
766
1133
  type: Component,
767
1134
  args: [{ selector: 'ai-table-grid', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, host: {
768
1135
  class: 'ai-table-grid'
@@ -777,13 +1144,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImpor
777
1144
  ThyPopoverModule,
778
1145
  ThyIcon,
779
1146
  ThyRate,
780
- FieldPropertyEditorComponent,
1147
+ AITableFieldPropertyEditor,
781
1148
  ThyDatePickerFormatPipe,
782
1149
  ThyTooltipModule,
783
1150
  ThyFlexibleText,
784
- ThyStopPropagationDirective
785
- ], providers: [ThyTooltipService, AITableGridEventService], template: "<div class=\"grid-header d-flex\">\n <div class=\"grid-column-checkbox grid-cell\">\n <input type=\"checkbox\" />\n </div>\n @for (field of gridData().fields; track field.id) {\n <div class=\"grid-cell\">{{ field.name }}</div>\n }\n <div class=\"grid-column-blank cursor-pointer\" (click)=\"addField($event)\"><thy-icon thyIconName=\"plus\"></thy-icon></div>\n</div>\n<div class=\"grid-body d-flex\">\n @for (record of gridData().records; track record.id; let index = $index) {\n <div class=\"grid-row d-flex\">\n <div class=\"grid-row-index\">\n {{ index + 1 }}\n </div>\n @for (field of gridData().fields; track $index) {\n <div class=\"grid-cell\" [attr.type]=\"[field.type]\" [attr.fieldId]=\"[field.id]\" [attr.recordId]=\"[record.id]\" #cell>\n @switch (field.type) {\n @case (AITableFieldType.SingleSelect) {\n @if (record.value[field.id] | selectOption: field['options']; as selectedOption) {\n <thy-tag [thyColor]=\"selectedOption!.color!\">{{ selectedOption.name }}</thy-tag>\n }\n }\n @case (AITableFieldType.DateTime) {\n {{ record.value[field.id] | thyDatePickerFormat }}\n }\n @case (AITableFieldType.Rating) {\n <thy-rate [(ngModel)]=\"record.value[field.id]\"></thy-rate>\n }\n @case (AITableFieldType.Link) {\n <a\n class=\"d-block pl-4 pr-4\"\n thyStopPropagation\n thyFlexibleText\n [thyTooltipContent]=\"record.value[field.id]?.text\"\n [href]=\"record.value[field.id]?.url\"\n target=\"_blank\"\n >{{ record.value[field.id]?.text }}</a\n >\n }\n @default {\n {{ record.value[field.id] }}\n }\n }\n </div>\n }\n <div class=\"grid-column-blank\"></div>\n </div>\n }\n <div class=\"grid-row-insert grid-row cursor-pointer\" (click)=\"addRecord()\">\n <thy-icon thyIconName=\"plus\"></thy-icon>\n </div>\n</div>\n\n<div #activeBorder class=\"active-border\"></div>\n" }]
786
- }], ctorParameters: () => [{ type: i0.ElementRef }, { type: AITableGridEventService }, { type: i1$1.ThyPopover }] });
1151
+ ThyStopPropagationDirective,
1152
+ FieldMenu,
1153
+ ThyAction,
1154
+ ThyDropdownDirective,
1155
+ ThyDropdownMenuComponent,
1156
+ ThyCheckboxModule
1157
+ ], providers: [ThyTooltipService, AITableGridEventService, AITableGridFieldService, AITableGridSelectionService], template: "<div class=\"grid-header d-flex\">\n <div class=\"grid-column-checkbox grid-cell\">\n <label thyCheckbox thyLabelText=\"\" [ngModel]=\"isSelectedAll\" (ngModelChange)=\"toggleSelectAll($event)\"></label>\n </div>\n @for (field of gridData().fields; track field.id) {\n <div\n class=\"grid-cell grid-field\"\n #fieldAction\n [attr.fieldId]=\"field.id\"\n [ngClass]=\"{ highlight: aiTable.selection().selectedFields.has(field.id) }\"\n [ngStyle]=\"{ width: field.width + 'px' }\"\n >\n <span class=\"text-truncate\">\n <thy-icon [thyIconName]=\"field.icon!\" class=\"mr-2 text-muted\"></thy-icon>\n <span>{{ field.name }}</span>\n </span>\n <a\n class=\"grid-field-action\"\n thyAction\n thyActiveClass=\"active\"\n thyIcon=\"more-vertical\"\n [thyDropdown]=\"fieldMenu\"\n href=\"javascript:;\"\n >\n <thy-dropdown-menu #fieldMenu>\n <field-menu [origin]=\"fieldAction\" [fieldId]=\"field.id\" [aiTable]=\"aiTable\" [fieldMenus]=\"fieldMenus\"></field-menu>\n </thy-dropdown-menu>\n </a>\n </div>\n }\n <div class=\"grid-column-blank cursor-pointer\" #gridColumnBlank (click)=\"addField(gridColumnBlank)\">\n <thy-icon thyIconName=\"plus\"></thy-icon>\n </div>\n</div>\n<div class=\"grid-body d-flex\">\n @for (record of gridData().records; track record.id; let index = $index) {\n <div class=\"grid-row d-flex\" [ngClass]=\"{ highlight: aiTable.selection().selectedRecords.has(record.id) }\">\n <div class=\"grid-row-index\">\n <label\n [ngClass]=\"record.checked ? 'checked-box' : 'unchecked-box'\"\n thyCheckbox\n thyLabelText=\"\"\n [ngModel]=\"record.checked\"\n (ngModelChange)=\"selectRecord(record.id)\"\n ></label>\n <span [ngClass]=\"record.checked ? 'grid-row-no-number' : 'grid-row-number'\"> {{ index + 1 }} </span>\n </div>\n @for (field of gridData().fields; track $index) {\n <div\n class=\"grid-cell\"\n [ngClass]=\"{\n highlight: aiTable.selection().selectedCells.has(record.id) || aiTable.selection().selectedFields.has(field.id),\n selected: aiTable.selection().selectedCells.get(record.id)?.hasOwnProperty(field.id)\n }\"\n [attr.type]=\"[field.type]\"\n [attr.fieldId]=\"[field.id]\"\n [attr.recordId]=\"[record.id]\"\n [ngStyle]=\"{ width: field.width + 'px' }\"\n #cell\n >\n @switch (field.type) {\n @case (AITableFieldType.select) {\n @if (!field.isMultiple && record.values[field.id] | selectOption: field['options']; as selectedOption) {\n <thy-tag [thyColor]=\"selectedOption!.color!\">{{ selectedOption.name }}</thy-tag>\n }\n }\n @case (AITableFieldType.date) {\n {{ record.values[field.id] | thyDatePickerFormat }}\n }\n @case (AITableFieldType.rate) {\n <thy-rate [(ngModel)]=\"record.values[field.id]\"></thy-rate>\n }\n @case (AITableFieldType.link) {\n <a\n class=\"d-block\"\n thyStopPropagation\n thyFlexibleText\n [thyTooltipContent]=\"record.values[field.id]?.text\"\n [href]=\"record.values[field.id]?.url\"\n target=\"_blank\"\n >{{ record.values[field.id]?.text }}</a\n >\n }\n @default {\n <span class=\"text-truncate\"> {{ record.values[field.id] }}</span>\n }\n }\n <div class=\"autofill-container\"></div>\n </div>\n }\n <div class=\"grid-column-blank\"></div>\n </div>\n }\n <div class=\"grid-row-insert grid-row cursor-pointer\" (click)=\"addRecord()\">\n <thy-icon thyIconName=\"plus\"></thy-icon>\n </div>\n</div>\n\n<div #activeBorder class=\"active-border\"></div>\n" }]
1158
+ }], ctorParameters: () => [{ type: i0.ElementRef }, { type: AITableGridEventService }, { type: AITableGridSelectionService }, { type: AITableGridFieldService }, { type: i0.NgZone }] });
787
1159
 
788
1160
  var AITableRowHeight;
789
1161
  (function (AITableRowHeight) {
@@ -797,5 +1169,5 @@ var AITableRowHeight;
797
1169
  * Generated bundle index. Do not edit.
798
1170
  */
799
1171
 
800
- export { AITableFieldType, AITableGridComponent, AITableQueries, AITableRowHeight, AITableStatType, ActionName, Actions, DBL_CLICK_EDIT_TYPE, DEFAULT_COLUMN_WIDTH, ExecuteType, FLUSHING, MIN_COLUMN_WIDTH, RowHeight, SelectOptionPipe, buildGridData, createAITable, getDefaultFieldValue, getDefaultRecord, getRecordOrField, idCreator };
1172
+ export { AITableFieldPropertyEditor, AITableFieldType, AITableGrid, AITableQueries, AITableRowHeight, AITableStatType, ActionName, Actions, BasicFields, DBL_CLICK_EDIT_TYPE, DEFAULT_COLUMN_WIDTH, DefaultFieldMenus, DividerMenuItem, EditFieldPropertyItem, ExecuteType, FLUSHING, Fields, FieldsMap, MIN_COLUMN_WIDTH, RemoveFieldItem, RowHeight, SelectOptionPipe, buildGridData, createAITable, createDefaultField, createDefaultFieldName, getDefaultFieldValue, getDefaultRecord, getRecordOrField, idCreator, isPathEqual };
801
1173
  //# sourceMappingURL=ai-table-grid.mjs.map