@ai-table/grid 0.0.3 → 0.0.5

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 (100) hide show
  1. package/components/cell-editors/cell-editor.scss +20 -0
  2. package/components/cell-editors/number/number-editor.component.d.ts.map +1 -1
  3. package/components/cell-editors/progress/progress-editor.component.d.ts +22 -0
  4. package/components/cell-editors/progress/progress-editor.component.d.ts.map +1 -0
  5. package/components/cell-editors/progress/progress-editor.component.scss +21 -0
  6. package/components/cell-editors/rating/rating-editor.component.d.ts.map +1 -1
  7. package/components/cell-editors/select/select-editor.component.d.ts +15 -0
  8. package/components/cell-editors/select/select-editor.component.d.ts.map +1 -0
  9. package/components/cell-editors/text/text-editor.component.d.ts +9 -1
  10. package/components/cell-editors/text/text-editor.component.d.ts.map +1 -1
  11. package/components/field-menu/field-menu.component.d.ts +6 -6
  12. package/components/field-menu/field-menu.component.d.ts.map +1 -1
  13. package/components/field-property-editor/field-property-editor.component.d.ts +2 -1
  14. package/components/field-property-editor/field-property-editor.component.d.ts.map +1 -1
  15. package/components/index.d.ts +6 -0
  16. package/components/index.d.ts.map +1 -1
  17. package/constants/editor.d.ts +1 -1
  18. package/constants/editor.d.ts.map +1 -1
  19. package/constants/field.d.ts +12 -6
  20. package/constants/field.d.ts.map +1 -1
  21. package/constants/grid.d.ts +1 -0
  22. package/constants/grid.d.ts.map +1 -1
  23. package/core/action/field.d.ts +6 -0
  24. package/core/action/field.d.ts.map +1 -1
  25. package/core/action/general.d.ts.map +1 -1
  26. package/core/action/index.d.ts +5 -0
  27. package/core/action/index.d.ts.map +1 -1
  28. package/core/action/record.d.ts +4 -0
  29. package/core/action/record.d.ts.map +1 -1
  30. package/core/constants/field.d.ts +2 -0
  31. package/core/constants/field.d.ts.map +1 -1
  32. package/core/types/action.d.ts +31 -2
  33. package/core/types/action.d.ts.map +1 -1
  34. package/core/types/core.d.ts +41 -14
  35. package/core/types/core.d.ts.map +1 -1
  36. package/core/utils/common.d.ts +2 -1
  37. package/core/utils/common.d.ts.map +1 -1
  38. package/core/utils/field.d.ts +1 -1
  39. package/core/utils/queries.d.ts +3 -1
  40. package/core/utils/queries.d.ts.map +1 -1
  41. package/esm2022/components/cell-editors/abstract-cell-editor.component.mjs +2 -2
  42. package/esm2022/components/cell-editors/number/number-editor.component.mjs +7 -6
  43. package/esm2022/components/cell-editors/progress/progress-editor.component.mjs +75 -0
  44. package/esm2022/components/cell-editors/rating/rating-editor.component.mjs +7 -6
  45. package/esm2022/components/cell-editors/select/select-editor.component.mjs +46 -0
  46. package/esm2022/components/cell-editors/text/text-editor.component.mjs +54 -21
  47. package/esm2022/components/field-menu/field-menu.component.mjs +7 -6
  48. package/esm2022/components/field-property-editor/field-property-editor.component.mjs +9 -6
  49. package/esm2022/components/index.mjs +7 -1
  50. package/esm2022/constants/editor.mjs +11 -13
  51. package/esm2022/constants/field.mjs +17 -5
  52. package/esm2022/constants/grid.mjs +7 -5
  53. package/esm2022/core/action/field.mjs +43 -2
  54. package/esm2022/core/action/general.mjs +68 -14
  55. package/esm2022/core/action/record.mjs +22 -5
  56. package/esm2022/core/constants/field.mjs +61 -15
  57. package/esm2022/core/types/action.mjs +6 -1
  58. package/esm2022/core/types/core.mjs +15 -14
  59. package/esm2022/core/types/index.mjs +1 -1
  60. package/esm2022/core/utils/common.mjs +10 -1
  61. package/esm2022/core/utils/field.mjs +4 -4
  62. package/esm2022/core/utils/queries.mjs +36 -6
  63. package/esm2022/core/utils/record.mjs +4 -4
  64. package/esm2022/grid.component.mjs +107 -34
  65. package/esm2022/pipes/grid.pipe.mjs +31 -0
  66. package/esm2022/pipes/index.mjs +2 -2
  67. package/esm2022/services/event.service.mjs +43 -23
  68. package/esm2022/services/selection.service.mjs +72 -0
  69. package/esm2022/types/field.mjs +1 -1
  70. package/esm2022/types/grid.mjs +2 -8
  71. package/esm2022/utils/build.mjs +40 -5
  72. package/esm2022/utils/cell.mjs +3 -3
  73. package/fesm2022/ai-table-grid.mjs +1013 -487
  74. package/fesm2022/ai-table-grid.mjs.map +1 -1
  75. package/grid.component.d.ts +23 -13
  76. package/grid.component.d.ts.map +1 -1
  77. package/package.json +1 -1
  78. package/pipes/grid.pipe.d.ts +15 -0
  79. package/pipes/grid.pipe.d.ts.map +1 -0
  80. package/pipes/index.d.ts +1 -1
  81. package/pipes/index.d.ts.map +1 -1
  82. package/services/event.service.d.ts +9 -6
  83. package/services/event.service.d.ts.map +1 -1
  84. package/services/selection.service.d.ts +16 -0
  85. package/services/selection.service.d.ts.map +1 -0
  86. package/styles/styles.scss +73 -10
  87. package/types/field.d.ts +4 -4
  88. package/types/field.d.ts.map +1 -1
  89. package/types/grid.d.ts +15 -8
  90. package/types/grid.d.ts.map +1 -1
  91. package/utils/build.d.ts +3 -2
  92. package/utils/build.d.ts.map +1 -1
  93. package/utils/cell.d.ts +1 -1
  94. package/utils/cell.d.ts.map +1 -1
  95. package/components/cell-editors/single-select/single-select-editor.component.d.ts +0 -14
  96. package/components/cell-editors/single-select/single-select-editor.component.d.ts.map +0 -1
  97. package/esm2022/components/cell-editors/single-select/single-select-editor.component.mjs +0 -44
  98. package/esm2022/pipes/grid.mjs +0 -18
  99. package/pipes/grid.d.ts +0 -9
  100. package/pipes/grid.d.ts.map +0 -1
@@ -1,79 +1,56 @@
1
+ import * as i1$2 from '@angular/common';
2
+ import { NgIf, NgTemplateOutlet, NgForOf, NgClass, CommonModule, NgComponentOutlet } from '@angular/common';
1
3
  import * as i0 from '@angular/core';
2
- import { Pipe, computed, model, inject, booleanAttribute, Component, ChangeDetectionStrategy, Input, Injectable, input, signal, output } from '@angular/core';
3
- import { NgIf, NgTemplateOutlet, NgForOf, CommonModule, NgClass, NgComponentOutlet } from '@angular/common';
4
- import { ThyTag } from 'ngx-tethys/tag';
5
- import * as i1$1 from 'ngx-tethys/popover';
6
- import { ThyPopoverRef, ThyPopoverModule } from 'ngx-tethys/popover';
4
+ import { signal, input, inject, computed, Component, ChangeDetectionStrategy, Input, HostListener, ElementRef, Renderer2, model, booleanAttribute, Injectable, Pipe, DestroyRef, output, NgZone } from '@angular/core';
7
5
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
8
- import { createDraft, finishDraft } from 'immer';
9
- import { helpers, isUndefinedOrNull } from 'ngx-tethys/util';
10
- import { ThyIcon } from 'ngx-tethys/icon';
11
- import { of, fromEvent } from 'rxjs';
12
6
  import * as i1 from '@angular/forms';
13
7
  import { FormsModule } from '@angular/forms';
14
- import { ThyInputGroup, ThyInputCount, ThyInputDirective, ThyInput } from 'ngx-tethys/input';
8
+ import { ThyAction } from 'ngx-tethys/action';
9
+ import * as i3 from 'ngx-tethys/checkbox';
10
+ import { ThyCheckboxModule } from 'ngx-tethys/checkbox';
11
+ import { ThyDatePicker, ThyDatePickerFormatPipe } from 'ngx-tethys/date-picker';
12
+ import { ThyDropdownDirective, ThyDropdownMenuComponent, ThyDropdownMenuItemDirective, ThyDropdownMenuItemNameDirective, ThyDropdownMenuItemIconDirective } from 'ngx-tethys/dropdown';
13
+ import { ThyFlexibleText } from 'ngx-tethys/flexible-text';
14
+ import { ThyIcon } from 'ngx-tethys/icon';
15
+ import * as i1$1 from 'ngx-tethys/popover';
16
+ import { ThyPopoverRef, ThyPopover, ThyPopoverModule } from 'ngx-tethys/popover';
17
+ import { ThyProgress } from 'ngx-tethys/progress';
18
+ import { ThyRate } from 'ngx-tethys/rate';
19
+ import { ThyAutofocusDirective, ThyEnterDirective, ThyOption, ThyStopPropagationDirective } from 'ngx-tethys/shared';
20
+ import { ThyTag } from 'ngx-tethys/tag';
21
+ import { mergeWith } from 'rxjs/operators';
22
+ import { helpers, isUndefinedOrNull } from 'ngx-tethys/util';
23
+ import { createDraft, finishDraft } from 'immer';
24
+ import { ThyTimePickerModule } from 'ngx-tethys/time-picker';
25
+ import { ThyInputNumber } from 'ngx-tethys/input-number';
26
+ import { ThySlider } from 'ngx-tethys/slider';
27
+ import { ThyInputDirective, ThyInputGroup, ThyInputCount, ThyInput } from 'ngx-tethys/input';
15
28
  import * as i2 from 'ngx-tethys/form';
16
29
  import { ThyUniqueCheckValidator, ThyFormModule, ThyConfirmValidatorDirective } from 'ngx-tethys/form';
17
- import { ThyDropdownDirective, ThyDropdownMenuComponent, ThyDropdownMenuItemDirective, ThyDropdownMenuItemNameDirective, ThyDropdownMenuItemIconDirective } from 'ngx-tethys/dropdown';
18
30
  import { ThyButton } from 'ngx-tethys/button';
19
31
  import { ThyListItem } from 'ngx-tethys/list';
20
- import { ThySelect } from 'ngx-tethys/select';
21
- import { ThyOption, ThyAutofocusDirective, ThyEnterDirective, 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 { ThyFlexibleText } from 'ngx-tethys/flexible-text';
32
+ import { of, Subject, fromEvent, debounceTime } from 'rxjs';
28
33
  import { ThyDivider } from 'ngx-tethys/divider';
29
- import { ThyAction } from 'ngx-tethys/action';
30
-
31
- ;
32
- class SelectOptionPipe {
33
- transform(id, options) {
34
- return options.find((item) => item.id === id);
35
- }
36
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: SelectOptionPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
37
- static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "18.0.2", ngImport: i0, type: SelectOptionPipe, isStandalone: true, name: "selectOption" }); }
38
- }
39
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: SelectOptionPipe, decorators: [{
40
- type: Pipe,
41
- args: [{
42
- name: 'selectOption',
43
- standalone: true
44
- }]
45
- }] });
46
-
47
- ;
48
- const buildGridData = (recordValue, fieldsValue) => {
49
- return {
50
- type: 'grid',
51
- fields: fieldsValue,
52
- records: recordValue
53
- };
54
- };
55
-
56
- function getRecordOrField(value, id) {
57
- return computed(() => {
58
- return value().find((item) => item.id === id);
59
- });
60
- }
34
+ import { ThySelect } from 'ngx-tethys/select';
35
+ import * as i4 from 'ngx-tethys/avatar';
36
+ import { ThyAvatarModule } from 'ngx-tethys/avatar';
61
37
 
62
38
  var AITableFieldType;
63
39
  (function (AITableFieldType) {
64
- // NotSupport = 0,
65
- AITableFieldType[AITableFieldType["Text"] = 1] = "Text";
66
- AITableFieldType[AITableFieldType["Number"] = 2] = "Number";
67
- AITableFieldType[AITableFieldType["SingleSelect"] = 3] = "SingleSelect";
68
- // MultiSelect = 4,
69
- AITableFieldType[AITableFieldType["DateTime"] = 5] = "DateTime";
70
- // Attachment = 6,
71
- AITableFieldType[AITableFieldType["Link"] = 7] = "Link";
72
- // Email = 9,
73
- // Phone = 10,
74
- // Checkbox = 11,
75
- AITableFieldType[AITableFieldType["Rating"] = 12] = "Rating";
76
- // Member = 13
40
+ AITableFieldType["text"] = "text";
41
+ AITableFieldType["richText"] = "rich_text";
42
+ AITableFieldType["select"] = "select";
43
+ AITableFieldType["number"] = "number";
44
+ AITableFieldType["date"] = "date";
45
+ AITableFieldType["member"] = "member";
46
+ // cascadeSelect = 'cascade_select', // 包含单选和多选,参数复杂后续再进行设计
47
+ AITableFieldType["progress"] = "progress";
48
+ AITableFieldType["rate"] = "rate";
49
+ AITableFieldType["link"] = "link";
50
+ AITableFieldType["createdAt"] = "created_at";
51
+ AITableFieldType["updatedAt"] = "updated_at";
52
+ AITableFieldType["createdBy"] = "created_by";
53
+ AITableFieldType["updatedBy"] = "updated_by";
77
54
  })(AITableFieldType || (AITableFieldType = {}));
78
55
  var AITableStatType;
79
56
  (function (AITableStatType) {
@@ -102,6 +79,11 @@ var ActionName;
102
79
  ActionName["UpdateFieldValue"] = "update_field_value";
103
80
  ActionName["AddRecord"] = "add_record";
104
81
  ActionName["AddField"] = "add_field";
82
+ ActionName["MoveField"] = "move_field";
83
+ ActionName["MoveRecord"] = "move_record";
84
+ ActionName["RemoveField"] = "remove_field";
85
+ ActionName["RemoveRecord"] = "remove_record";
86
+ ActionName["SetField"] = "set_field";
105
87
  })(ActionName || (ActionName = {}));
106
88
  var ExecuteType;
107
89
  (function (ExecuteType) {
@@ -110,65 +92,6 @@ var ExecuteType;
110
92
  ExecuteType[ExecuteType["Redo"] = 2] = "Redo";
111
93
  })(ExecuteType || (ExecuteType = {}));
112
94
 
113
- function addField(aiTable, field, path) {
114
- const operation = {
115
- type: ActionName.AddField,
116
- field,
117
- path
118
- };
119
- aiTable.apply(operation);
120
- }
121
- const FieldActions = {
122
- addField
123
- };
124
-
125
- const apply = (aiTable, records, fields, options) => {
126
- switch (options.type) {
127
- case ActionName.UpdateFieldValue: {
128
- const [recordIndex, fieldIndex] = options.path;
129
- const fieldId = aiTable.fields()[fieldIndex].id;
130
- records[recordIndex].value[fieldId] = options.newFieldValue;
131
- break;
132
- }
133
- case ActionName.AddRecord: {
134
- const [recordIndex] = options.path;
135
- records.splice(recordIndex, 0, options.record);
136
- break;
137
- }
138
- case ActionName.AddField: {
139
- const [fieldIndex] = options.path;
140
- const newField = options.field;
141
- fields.splice(fieldIndex, 0, newField);
142
- const newRecord = {
143
- [newField.id]: ''
144
- };
145
- records.forEach((item) => {
146
- item.value = {
147
- ...item.value,
148
- ...newRecord
149
- };
150
- });
151
- }
152
- }
153
- return {
154
- records,
155
- fields
156
- };
157
- };
158
- const GeneralActions = {
159
- transform(aiTable, op) {
160
- const records = createDraft(aiTable.records());
161
- const fields = createDraft(aiTable.fields());
162
- apply(aiTable, records, fields, op);
163
- aiTable.fields.update(() => {
164
- return finishDraft(fields);
165
- });
166
- aiTable.records.update(() => {
167
- return finishDraft(records);
168
- });
169
- }
170
- };
171
-
172
95
  function idCreator(length = 5) {
173
96
  // remove numeral
174
97
  const $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz'; /**** Easily confusing characters are removed by default oOLl,9gq,Vv,Uu,I1****/
@@ -182,34 +105,80 @@ function idCreator(length = 5) {
182
105
 
183
106
  const BasicFields = [
184
107
  {
185
- type: AITableFieldType.Text,
186
- name: '文本',
187
- icon: 'font'
108
+ type: AITableFieldType.text,
109
+ name: '单行文本',
110
+ icon: 'font',
111
+ width: 300
188
112
  },
113
+ // 多行文本
189
114
  {
190
- type: AITableFieldType.SingleSelect,
115
+ type: AITableFieldType.select,
191
116
  name: '单选',
192
- icon: 'check-circle'
117
+ icon: 'check-circle',
118
+ width: 200
193
119
  },
120
+ // 多选
194
121
  {
195
- type: AITableFieldType.Number,
122
+ type: AITableFieldType.number,
196
123
  name: '数字',
197
- icon: 'hashtag'
124
+ icon: 'hashtag',
125
+ width: 200
198
126
  },
199
127
  {
200
- type: AITableFieldType.DateTime,
128
+ type: AITableFieldType.date,
201
129
  name: '日期',
202
- icon: 'calendar'
130
+ icon: 'calendar',
131
+ width: 200
132
+ },
133
+ {
134
+ type: AITableFieldType.member,
135
+ name: '成员',
136
+ icon: 'user',
137
+ width: 200
138
+ },
139
+ // 级联单选
140
+ // 级联多选
141
+ {
142
+ type: AITableFieldType.progress,
143
+ name: '进度',
144
+ icon: 'progress',
145
+ width: 200
203
146
  },
204
147
  {
205
- type: AITableFieldType.Rating,
148
+ type: AITableFieldType.rate,
206
149
  name: '评分',
207
- icon: 'star-circle'
150
+ icon: 'star-circle',
151
+ width: 200
208
152
  },
209
153
  {
210
- type: AITableFieldType.Link,
154
+ type: AITableFieldType.link,
211
155
  name: '链接',
212
- icon: 'link-insert'
156
+ icon: 'link-insert',
157
+ width: 300
158
+ },
159
+ {
160
+ type: AITableFieldType.createdBy,
161
+ name: '创建人',
162
+ icon: 'user',
163
+ width: 200
164
+ },
165
+ {
166
+ type: AITableFieldType.createdAt,
167
+ name: '创建时间',
168
+ icon: 'calendar',
169
+ width: 200
170
+ },
171
+ {
172
+ type: AITableFieldType.updatedBy,
173
+ name: '更新人',
174
+ icon: 'user',
175
+ width: 200
176
+ },
177
+ {
178
+ type: AITableFieldType.updatedAt,
179
+ name: '更新时间',
180
+ icon: 'calendar',
181
+ width: 200
213
182
  }
214
183
  ];
215
184
  const Fields = [...BasicFields];
@@ -218,13 +187,13 @@ const FieldsMap = helpers.keyBy([...BasicFields], 'type');
218
187
  function getDefaultFieldValue(type) {
219
188
  return '';
220
189
  }
221
- function createDefaultFieldName(aiTable, type = AITableFieldType.Text) {
190
+ function createDefaultFieldName(aiTable, type = AITableFieldType.text) {
222
191
  const fields = aiTable.fields();
223
192
  const count = fields.filter((item) => item.type === type).length;
224
193
  return count === 0 ? FieldsMap[type].name : FieldsMap[type].name + count;
225
194
  }
226
- function createDefaultField(aiTable, type = AITableFieldType.Text) {
227
- return { id: idCreator(), type, name: createDefaultFieldName(aiTable, type) };
195
+ function createDefaultField(aiTable, type = AITableFieldType.text) {
196
+ return { _id: idCreator(), type, name: createDefaultFieldName(aiTable, type) };
228
197
  }
229
198
 
230
199
  const FLUSHING = new WeakMap();
@@ -234,6 +203,11 @@ function createAITable(records, fields) {
234
203
  records,
235
204
  fields,
236
205
  actions: [],
206
+ selection: signal({
207
+ selectedRecords: new Map(),
208
+ selectedFields: new Map(),
209
+ selectedCells: new Map()
210
+ }),
237
211
  onChange: () => { },
238
212
  apply: (action) => {
239
213
  aiTable.actions.push(action);
@@ -250,14 +224,17 @@ function createAITable(records, fields) {
250
224
  };
251
225
  return aiTable;
252
226
  }
227
+ function isPathEqual(path, another) {
228
+ return path.length === another.length && path.every((n, i) => n === another[i]);
229
+ }
253
230
 
254
231
  function getDefaultRecord(fields) {
255
232
  const newRow = {
256
- id: idCreator(),
257
- value: {}
233
+ _id: idCreator(),
234
+ values: {}
258
235
  };
259
236
  fields.map((item) => {
260
- newRow.value[item.id] = getDefaultFieldValue(item.type);
237
+ newRow.values[item._id] = getDefaultFieldValue(item.type);
261
238
  });
262
239
  return newRow;
263
240
  }
@@ -275,23 +252,205 @@ const AITableQueries = {
275
252
  if (!isUndefinedOrNull(fieldIndex) && fieldIndex > -1) {
276
253
  return [fieldIndex];
277
254
  }
278
- throw new Error(`Unable to find the path: ${JSON.stringify({ ...(field || {}), ...(record || {}) })}`);
255
+ throw new Error(`can not find the path: ${JSON.stringify({ ...(field || {}), ...(record || {}) })}`);
279
256
  },
280
257
  getFieldValue(aiTable, path) {
281
- if (!aiTable || !aiTable.records() || !aiTable.fields()) {
282
- throw new Error(`Cannot find a descendant at path [${path}]`);
258
+ if (!aiTable) {
259
+ throw new Error(`aiTable does not exist`);
260
+ }
261
+ if (!aiTable.records()) {
262
+ throw new Error(`aiTable has no records`);
263
+ }
264
+ if (!aiTable.fields()) {
265
+ throw new Error(`aiTable has no fields`);
266
+ }
267
+ if (!path) {
268
+ throw new Error(`path does not exist as path [${path}]`);
269
+ }
270
+ const field = aiTable.fields()[path[1]];
271
+ if (!field) {
272
+ throw new Error(`can not find field at path [${path}]`);
273
+ }
274
+ return aiTable.records()[path[0]].values[field._id];
275
+ },
276
+ getField(aiTable, path) {
277
+ if (!aiTable) {
278
+ throw new Error(`aiTable does not exist`);
279
+ }
280
+ if (!path) {
281
+ throw new Error(`path does not exist as path [${path}]`);
282
+ }
283
+ return aiTable.fields()[path[0]];
284
+ },
285
+ getRecord(aiTable, path) {
286
+ if (!aiTable) {
287
+ throw new Error(`aiTable does not exist`);
288
+ }
289
+ if (!path) {
290
+ throw new Error(`path does not exist as path [${path}]`);
291
+ }
292
+ return aiTable.records()[path[0]];
293
+ }
294
+ };
295
+
296
+ function addField(aiTable, field, path) {
297
+ const operation = {
298
+ type: ActionName.AddField,
299
+ field,
300
+ path
301
+ };
302
+ aiTable.apply(operation);
303
+ }
304
+ function moveField(aiTable, path, newPath) {
305
+ const operation = {
306
+ type: ActionName.MoveField,
307
+ path,
308
+ newPath
309
+ };
310
+ aiTable.apply(operation);
311
+ }
312
+ function removeField(aiTable, path) {
313
+ const operation = {
314
+ type: ActionName.RemoveField,
315
+ path
316
+ };
317
+ aiTable.apply(operation);
318
+ }
319
+ function setField(aiTable, value, path) {
320
+ const field = AITableQueries.getField(aiTable, path);
321
+ if (field) {
322
+ const oldField = {};
323
+ const newField = {};
324
+ for (const k in value) {
325
+ if (field[k] !== value[k]) {
326
+ if (field.hasOwnProperty(k)) {
327
+ oldField[k] = field[k];
328
+ }
329
+ newField[k] = value[k];
330
+ }
331
+ }
332
+ const operation = {
333
+ type: ActionName.SetField,
334
+ field: oldField,
335
+ newField,
336
+ path
337
+ };
338
+ aiTable.apply(operation);
339
+ }
340
+ }
341
+ const FieldActions = {
342
+ addField,
343
+ moveField,
344
+ removeField,
345
+ setField
346
+ };
347
+
348
+ const apply = (aiTable, records, fields, options) => {
349
+ switch (options.type) {
350
+ case ActionName.UpdateFieldValue: {
351
+ const [recordIndex, fieldIndex] = options.path;
352
+ if (fieldIndex > -1 && recordIndex > -1) {
353
+ const fieldId = aiTable.fields()[fieldIndex]._id;
354
+ records[recordIndex].values[fieldId] = options.newFieldValue;
355
+ }
356
+ break;
357
+ }
358
+ case ActionName.AddRecord: {
359
+ const [recordIndex] = options.path;
360
+ if (recordIndex > -1) {
361
+ records.splice(recordIndex, 0, options.record);
362
+ }
363
+ break;
364
+ }
365
+ case ActionName.AddField: {
366
+ const [fieldIndex] = options.path;
367
+ if (fieldIndex > -1) {
368
+ const newField = options.field;
369
+ fields.splice(fieldIndex, 0, newField);
370
+ const newRecord = {
371
+ [newField._id]: ''
372
+ };
373
+ records.forEach((item) => {
374
+ item.values = {
375
+ ...item.values,
376
+ ...newRecord
377
+ };
378
+ });
379
+ }
380
+ break;
381
+ }
382
+ case ActionName.MoveRecord: {
383
+ if (isPathEqual(options.path, options.newPath)) {
384
+ return;
385
+ }
386
+ const record = records[options.path[0]];
387
+ records.splice(options.path[0], 1);
388
+ records.splice(options.newPath[0], 0, record);
389
+ break;
390
+ }
391
+ case ActionName.MoveField: {
392
+ if (isPathEqual(options.path, options.newPath)) {
393
+ return;
394
+ }
395
+ const field = fields[options.path[0]];
396
+ fields.splice(options.path[0], 1);
397
+ fields.splice(options.newPath[0], 0, field);
398
+ break;
399
+ }
400
+ case ActionName.RemoveField: {
401
+ const [fieldIndex] = options.path;
402
+ if (fieldIndex > -1) {
403
+ const fieldId = aiTable.fields()[fieldIndex]._id;
404
+ fields.splice(fieldIndex, 1);
405
+ records.forEach((item) => {
406
+ delete item.values[fieldId];
407
+ });
408
+ }
409
+ break;
410
+ }
411
+ case ActionName.RemoveRecord: {
412
+ const [recordIndex] = options.path;
413
+ if (recordIndex > -1) {
414
+ records.splice(recordIndex, 1);
415
+ }
416
+ break;
283
417
  }
284
- const fieldId = aiTable.fields()[path[1]].id;
285
- return aiTable.records()[path[0]].value[fieldId];
418
+ case ActionName.SetField: {
419
+ const [fieldIndex] = options.path;
420
+ if (fieldIndex > -1) {
421
+ fields.splice(fieldIndex, 1, {
422
+ ...fields[fieldIndex],
423
+ ...options.newField
424
+ });
425
+ }
426
+ break;
427
+ }
428
+ }
429
+ return {
430
+ records,
431
+ fields
432
+ };
433
+ };
434
+ const GeneralActions = {
435
+ transform(aiTable, op) {
436
+ const records = createDraft(aiTable.records());
437
+ const fields = createDraft(aiTable.fields());
438
+ apply(aiTable, records, fields, op);
439
+ aiTable.fields.update(() => {
440
+ return finishDraft(fields);
441
+ });
442
+ aiTable.records.update(() => {
443
+ return finishDraft(records);
444
+ });
286
445
  }
287
446
  };
288
447
 
289
448
  function updateFieldValue(aiTable, value, path) {
290
- const node = AITableQueries.getFieldValue(aiTable, path);
291
- if (node !== value) {
449
+ const field = AITableQueries.getFieldValue(aiTable, path);
450
+ if (field !== value) {
292
451
  const operation = {
293
452
  type: ActionName.UpdateFieldValue,
294
- fieldValue: node,
453
+ fieldValue: field,
295
454
  newFieldValue: value,
296
455
  path
297
456
  };
@@ -306,9 +465,26 @@ function addRecord(aiTable, record, path) {
306
465
  };
307
466
  aiTable.apply(operation);
308
467
  }
468
+ function moveRecord(aiTable, path, newPath) {
469
+ const operation = {
470
+ type: ActionName.MoveRecord,
471
+ path,
472
+ newPath
473
+ };
474
+ aiTable.apply(operation);
475
+ }
476
+ function removeRecord(aiTable, path) {
477
+ const operation = {
478
+ type: ActionName.RemoveRecord,
479
+ path
480
+ };
481
+ aiTable.apply(operation);
482
+ }
309
483
  const RecordActions = {
310
484
  addRecord,
311
- updateFieldValue
485
+ updateFieldValue,
486
+ moveRecord,
487
+ removeRecord
312
488
  };
313
489
 
314
490
  const Actions = {
@@ -317,50 +493,355 @@ const Actions = {
317
493
  ...FieldActions
318
494
  };
319
495
 
320
- const DEFAULT_COLUMN_WIDTH = 200;
321
- const MIN_COLUMN_WIDTH = 80;
322
- const DBL_CLICK_EDIT_TYPE = [
323
- AITableFieldType.Text,
324
- AITableFieldType.Number,
325
- AITableFieldType.SingleSelect,
326
- AITableFieldType.DateTime
327
- ];
328
- const RowHeight = {
329
- Short: 32,
330
- Medium: 57,
331
- Tall: 104,
332
- ExtraTall: 152
333
- };
334
-
335
- class AITableFieldPropertyEditor {
496
+ class AbstractEditCellEditor {
336
497
  constructor() {
337
- this.aiField = model.required();
338
- this.aiExternalTemplate = null;
339
- this.fieldType = computed(() => {
340
- return FieldsMap[this.aiField().type];
341
- });
342
- this.fieldMaxLength = 32;
343
- this.validatorConfig = {
344
- validationMessages: {
345
- fieldName: {
346
- required: '列名不能为空',
347
- thyUniqueCheck: '列名已存在'
348
- }
349
- }
350
- };
351
- this.selectableFields = Fields;
498
+ this.field = input.required();
499
+ this.record = input.required();
352
500
  this.thyPopoverRef = inject((ThyPopoverRef));
353
- this.checkUniqueName = (fieldName) => {
354
- fieldName = fieldName?.trim();
355
- return of(!!this.aiTable.fields()?.find((field) => field.name === fieldName && this.aiField()?.id !== field.id));
356
- };
357
501
  }
358
- selectFieldType(fieldType) {
359
- this.aiField.update((item) => ({ ...item, type: fieldType, name: createDefaultFieldName(this.aiTable, fieldType) }));
502
+ ngOnInit() {
503
+ this.modelValue = computed(() => {
504
+ const path = AITableQueries.findPath(this.aiTable, this.field(), this.record());
505
+ return AITableQueries.getFieldValue(this.aiTable, path);
506
+ })();
360
507
  }
361
- editFieldProperty() {
508
+ updateFieldValue() {
509
+ const path = AITableQueries.findPath(this.aiTable, this.field(), this.record());
510
+ Actions.updateFieldValue(this.aiTable, this.modelValue, path);
511
+ }
512
+ closePopover() {
513
+ this.thyPopoverRef?.close();
514
+ }
515
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: AbstractEditCellEditor, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
516
+ 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 }); }
517
+ }
518
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: AbstractEditCellEditor, decorators: [{
519
+ type: Component,
520
+ args: [{
521
+ selector: 'abstract-edit-cell',
522
+ template: ``,
523
+ standalone: true,
524
+ changeDetection: ChangeDetectionStrategy.OnPush
525
+ }]
526
+ }], propDecorators: { aiTable: [{
527
+ type: Input,
528
+ args: [{ required: true }]
529
+ }] } });
530
+
531
+ class DateTimeCellEditorComponent extends AbstractEditCellEditor {
532
+ constructor() {
533
+ super(...arguments);
534
+ this.dateShowTime = input(false);
535
+ this.dateFormat = computed(() => {
536
+ return this.dateShowTime() ? 'yyyy-MM-dd HH:mm' : 'yyyy-MM-dd';
537
+ })();
538
+ }
539
+ ngOnInit() {
540
+ super.ngOnInit();
541
+ if (!this.modelValue && this.dateShowTime()) {
542
+ this.modelValue = {
543
+ date: 0,
544
+ with_time: 1
545
+ };
546
+ }
547
+ }
548
+ updateValue() {
549
+ this.updateFieldValue();
550
+ this.closePopover();
551
+ }
552
+ thyOpenChange(isOpen) {
553
+ if (!isOpen) {
554
+ this.closePopover();
555
+ }
556
+ }
557
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: DateTimeCellEditorComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
558
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "18.0.2", type: DateTimeCellEditorComponent, isStandalone: true, selector: "date-time-cell-editor", inputs: { dateShowTime: { classPropertyName: "dateShowTime", publicName: "dateShowTime", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "date-time-cell-editor" }, usesInheritance: true, ngImport: i0, template: `
559
+ <thy-date-picker
560
+ class="h-100"
561
+ thyTimestampPrecision="milliseconds"
562
+ thyPlaceHolder="选择时间"
563
+ [(ngModel)]="modelValue"
564
+ (ngModelChange)="updateValue()"
565
+ (thyOpenChange)="thyOpenChange($event)"
566
+ [thyAllowClear]="true"
567
+ [thyShowShortcut]="true"
568
+ [thyHasBackdrop]="false"
569
+ [thyShowTime]="dateShowTime()"
570
+ [thyOpen]="true"
571
+ [thyFormat]="dateFormat"
572
+ >
573
+ </thy-date-picker>
574
+ `, isInline: true, dependencies: [{ 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: ThyDatePicker, selector: "thy-date-picker", exportAs: ["thyDatePicker"] }, { kind: "ngmodule", type: ThyTimePickerModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
575
+ }
576
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: DateTimeCellEditorComponent, decorators: [{
577
+ type: Component,
578
+ args: [{
579
+ selector: 'date-time-cell-editor',
580
+ template: `
581
+ <thy-date-picker
582
+ class="h-100"
583
+ thyTimestampPrecision="milliseconds"
584
+ thyPlaceHolder="选择时间"
585
+ [(ngModel)]="modelValue"
586
+ (ngModelChange)="updateValue()"
587
+ (thyOpenChange)="thyOpenChange($event)"
588
+ [thyAllowClear]="true"
589
+ [thyShowShortcut]="true"
590
+ [thyHasBackdrop]="false"
591
+ [thyShowTime]="dateShowTime()"
592
+ [thyOpen]="true"
593
+ [thyFormat]="dateFormat"
594
+ >
595
+ </thy-date-picker>
596
+ `,
597
+ standalone: true,
598
+ changeDetection: ChangeDetectionStrategy.OnPush,
599
+ imports: [FormsModule, ThyDatePicker, ThyTimePickerModule],
600
+ host: {
601
+ class: 'date-time-cell-editor'
602
+ }
603
+ }]
604
+ }] });
605
+
606
+ class LinkCellEditorComponent extends AbstractEditCellEditor {
607
+ updateValue() {
608
+ this.updateFieldValue();
609
+ this.closePopover();
610
+ }
611
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: LinkCellEditorComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
612
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.0.2", type: LinkCellEditorComponent, isStandalone: true, selector: "link-cell-editor", usesInheritance: true, ngImport: i0, template: ``, isInline: true, dependencies: [{ kind: "ngmodule", type: FormsModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
613
+ }
614
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: LinkCellEditorComponent, decorators: [{
615
+ type: Component,
616
+ args: [{
617
+ selector: 'link-cell-editor',
618
+ template: ``,
619
+ standalone: true,
620
+ changeDetection: ChangeDetectionStrategy.OnPush,
621
+ imports: [FormsModule, ThyAutofocusDirective, ThyEnterDirective, ThyInputNumber]
622
+ }]
623
+ }] });
624
+
625
+ class NumberCellEditorComponent extends AbstractEditCellEditor {
626
+ updateValue() {
627
+ this.updateFieldValue();
628
+ this.closePopover();
629
+ }
630
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: NumberCellEditorComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
631
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.0.2", type: NumberCellEditorComponent, isStandalone: true, selector: "number-cell-editor", host: { classAttribute: "number-cell-editor" }, usesInheritance: true, ngImport: i0, template: `<thy-input-number
632
+ class="h-100"
633
+ [thyAutoFocus]="true"
634
+ [(ngModel)]="modelValue"
635
+ (thyEnter)="updateValue()"
636
+ (thyBlur)="updateValue()"
637
+ /> `, isInline: true, dependencies: [{ 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: "directive", type: ThyEnterDirective, selector: "[thyEnter]", outputs: ["thyEnter"] }, { kind: "component", type: ThyInputNumber, selector: "thy-input-number", inputs: ["thyAutoFocus", "thyPlaceholder", "thyDisabled", "thyMax", "thyMin", "thyStep", "thyStepDelay", "thySize", "thyPrecision", "thySuffix"], outputs: ["thyBlur", "thyFocus", "thyStepChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
638
+ }
639
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: NumberCellEditorComponent, decorators: [{
640
+ type: Component,
641
+ args: [{
642
+ selector: 'number-cell-editor',
643
+ template: `<thy-input-number
644
+ class="h-100"
645
+ [thyAutoFocus]="true"
646
+ [(ngModel)]="modelValue"
647
+ (thyEnter)="updateValue()"
648
+ (thyBlur)="updateValue()"
649
+ /> `,
650
+ standalone: true,
651
+ changeDetection: ChangeDetectionStrategy.OnPush,
652
+ imports: [FormsModule, ThyAutofocusDirective, ThyEnterDirective, ThyInputNumber],
653
+ host: {
654
+ class: 'number-cell-editor'
655
+ }
656
+ }]
657
+ }] });
658
+
659
+ class ProgressEditorComponent extends AbstractEditCellEditor {
660
+ mousedownHandler(event) {
661
+ event.preventDefault();
662
+ }
663
+ constructor() {
664
+ super();
665
+ this.config = {
666
+ max: 100,
667
+ min: 0,
668
+ step: 1,
669
+ progressType: 'success',
670
+ suffix: '%',
671
+ size: 'md'
672
+ };
673
+ }
674
+ sliderMousedownHandler(event) {
675
+ event.preventDefault();
676
+ event.stopPropagation();
677
+ }
678
+ updateValue(value) {
679
+ this.updateFieldValue();
680
+ }
681
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: ProgressEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
682
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.0.2", type: ProgressEditorComponent, isStandalone: true, selector: "progress-editor", host: { listeners: { "mousedown": "mousedownHandler($event)" }, properties: { "attr.type": "field().type", "attr.fieldId": "field()._id", "attr.recordId": "record()._id" }, classAttribute: "grid-cell progress-editor" }, usesInheritance: true, ngImport: i0, template: `
683
+ <thy-slider
684
+ [(ngModel)]="modelValue"
685
+ [thyMax]="config?.max || 100"
686
+ [thyMin]="config?.min || 0"
687
+ [thyStep]="config?.step || 1"
688
+ [thyType]="config?.progressType || 'success'"
689
+ [thySize]="config?.size || 'md'"
690
+ (ngModelChange)="updateValue($event)"
691
+ (mousedown)="sliderMousedownHandler($event)"
692
+ ></thy-slider>
693
+ <span class="progress-text">{{ modelValue }}{{ config?.suffix || '%' }}</span>
694
+ `, isInline: true, dependencies: [{ 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: ThySlider, selector: "thy-slider", inputs: ["thyVertical", "thyDisabled", "thyMax", "thyMin", "thyStep", "thyType", "thyColor", "thySize"], outputs: ["thyAfterChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
695
+ }
696
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: ProgressEditorComponent, decorators: [{
697
+ type: Component,
698
+ args: [{
699
+ selector: 'progress-editor',
700
+ template: `
701
+ <thy-slider
702
+ [(ngModel)]="modelValue"
703
+ [thyMax]="config?.max || 100"
704
+ [thyMin]="config?.min || 0"
705
+ [thyStep]="config?.step || 1"
706
+ [thyType]="config?.progressType || 'success'"
707
+ [thySize]="config?.size || 'md'"
708
+ (ngModelChange)="updateValue($event)"
709
+ (mousedown)="sliderMousedownHandler($event)"
710
+ ></thy-slider>
711
+ <span class="progress-text">{{ modelValue }}{{ config?.suffix || '%' }}</span>
712
+ `,
713
+ standalone: true,
714
+ changeDetection: ChangeDetectionStrategy.OnPush,
715
+ imports: [FormsModule, ThySlider],
716
+ host: {
717
+ class: 'grid-cell progress-editor',
718
+ '[attr.type]': 'field().type',
719
+ '[attr.fieldId]': 'field()._id',
720
+ '[attr.recordId]': 'record()._id'
721
+ }
722
+ }]
723
+ }], ctorParameters: () => [], propDecorators: { mousedownHandler: [{
724
+ type: HostListener,
725
+ args: ['mousedown', ['$event']]
726
+ }] } });
727
+
728
+ class RatingCellEditorComponent extends AbstractEditCellEditor {
729
+ updateValue() {
730
+ this.updateFieldValue();
731
+ }
732
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: RatingCellEditorComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
733
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.0.2", type: RatingCellEditorComponent, isStandalone: true, selector: "rating-cell-editor", host: { classAttribute: "d-flex align-items-center h-100 px-3" }, usesInheritance: true, ngImport: i0, template: ` <thy-rate [(ngModel)]="modelValue" (ngModelChange)="updateValue()"></thy-rate> `, isInline: true, dependencies: [{ 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: ThyRate, selector: "thy-rate", inputs: ["thyCount", "thyDisabled", "thyAllowHalf", "thyAllowClear", "thyTooltips", "thyIconTemplate"], outputs: ["thyItemHoverChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
734
+ }
735
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: RatingCellEditorComponent, decorators: [{
736
+ type: Component,
737
+ args: [{
738
+ selector: 'rating-cell-editor',
739
+ template: ` <thy-rate [(ngModel)]="modelValue" (ngModelChange)="updateValue()"></thy-rate> `,
740
+ standalone: true,
741
+ changeDetection: ChangeDetectionStrategy.OnPush,
742
+ imports: [FormsModule, ThyRate],
743
+ host: {
744
+ class: 'd-flex align-items-center h-100 px-3'
745
+ }
746
+ }]
747
+ }] });
748
+
749
+ class TextCellEditorComponent extends AbstractEditCellEditor {
750
+ constructor() {
751
+ super();
752
+ this.elementRef = inject(ElementRef);
753
+ this.render2 = inject(Renderer2);
754
+ this.maxHeight = 148;
755
+ }
756
+ ngAfterViewInit() {
757
+ setTimeout(() => {
758
+ this.updateStyle();
759
+ });
760
+ }
761
+ updateStyle() {
762
+ const textarea = this.elementRef.nativeElement.querySelector('textarea');
763
+ const height = textarea.scrollHeight < this.maxHeight ? textarea.scrollHeight : this.maxHeight;
764
+ this.render2.setStyle(textarea, 'height', `${height}px`);
765
+ this.render2.setStyle(textarea, 'min-height', `44px`);
766
+ this.render2.setStyle(textarea, 'max-height', `${this.maxHeight}px`);
767
+ this.render2.setStyle(textarea, 'resize', 'none');
768
+ }
769
+ valueChange() {
770
+ this.updateStyle();
771
+ }
772
+ updateValue() {
773
+ this.updateFieldValue();
774
+ this.closePopover();
775
+ }
776
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: TextCellEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
777
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.0.2", type: TextCellEditorComponent, isStandalone: true, selector: "text-cell-editor", host: { classAttribute: "text-cell-editor" }, usesInheritance: true, ngImport: i0, template: `
778
+ <textarea
779
+ placeholder=""
780
+ rows="1"
781
+ thyInput
782
+ [thyAutofocus]="true"
783
+ [(ngModel)]="modelValue"
784
+ (ngModelChange)="valueChange()"
785
+ (thyEnter)="updateValue()"
786
+ (blur)="updateValue()"
787
+ ></textarea>
788
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: FormsModule }, { 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.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: ThyAutofocusDirective, selector: "input[thyAutofocus],textarea[thyAutofocus]", inputs: ["thyAutofocus", "thyAutoSelect"] }, { kind: "directive", type: ThyInputDirective, selector: "input[thyInput], select[thyInput], textarea[thyInput]", inputs: ["thySize"], exportAs: ["thyInput"] }, { kind: "directive", type: ThyEnterDirective, selector: "[thyEnter]", outputs: ["thyEnter"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
789
+ }
790
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: TextCellEditorComponent, decorators: [{
791
+ type: Component,
792
+ args: [{
793
+ selector: 'text-cell-editor',
794
+ template: `
795
+ <textarea
796
+ placeholder=""
797
+ rows="1"
798
+ thyInput
799
+ [thyAutofocus]="true"
800
+ [(ngModel)]="modelValue"
801
+ (ngModelChange)="valueChange()"
802
+ (thyEnter)="updateValue()"
803
+ (blur)="updateValue()"
804
+ ></textarea>
805
+ `,
806
+ standalone: true,
807
+ changeDetection: ChangeDetectionStrategy.OnPush,
808
+ imports: [NgIf, FormsModule, ThyAutofocusDirective, ThyInputDirective, ThyEnterDirective],
809
+ host: {
810
+ class: 'text-cell-editor'
811
+ }
812
+ }]
813
+ }], ctorParameters: () => [] });
814
+
815
+ class AITableFieldPropertyEditor {
816
+ constructor() {
817
+ this.aiField = model.required();
818
+ this.aiExternalTemplate = null;
819
+ this.fieldType = computed(() => {
820
+ return FieldsMap[this.aiField().type];
821
+ });
822
+ this.fieldMaxLength = 32;
823
+ this.validatorConfig = {
824
+ validationMessages: {
825
+ fieldName: {
826
+ required: '列名不能为空',
827
+ thyUniqueCheck: '列名已存在'
828
+ }
829
+ }
830
+ };
831
+ this.selectableFields = Fields;
832
+ this.thyPopoverRef = inject((ThyPopoverRef));
833
+ this.checkUniqueName = (fieldName) => {
834
+ fieldName = fieldName?.trim();
835
+ return of(!!this.aiTable.fields()?.find((field) => field.name === fieldName && this.aiField()?._id !== field._id));
836
+ };
837
+ }
838
+ selectFieldType(fieldType) {
839
+ this.aiField.update((item) => ({ ...item, type: fieldType, name: createDefaultFieldName(this.aiTable, fieldType) }));
840
+ }
841
+ editFieldProperty() {
362
842
  if (this.isUpdate) {
363
- //TODO: updateField
843
+ const path = this.aiTable.fields().findIndex((item) => item._id === this.aiField()._id);
844
+ Actions.setField(this.aiTable, this.aiField(), [path]);
364
845
  }
365
846
  else {
366
847
  Actions.addField(this.aiTable, this.aiField(), [this.aiTable.fields().length]);
@@ -371,7 +852,7 @@ class AITableFieldPropertyEditor {
371
852
  this.thyPopoverRef.close();
372
853
  }
373
854
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: AITableFieldPropertyEditor, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
374
- 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\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"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
855
+ 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 $index) {\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 }); }
375
856
  }
376
857
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: AITableFieldPropertyEditor, decorators: [{
377
858
  type: Component,
@@ -394,10 +875,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImpor
394
875
  ThyButton,
395
876
  ThyFormModule,
396
877
  ThyListItem,
397
- NgTemplateOutlet
878
+ NgTemplateOutlet,
879
+ ThyAutofocusDirective
398
880
  ], host: {
399
881
  class: 'field-property-editor d-block pl-5 pr-5 pb-5 pt-4'
400
- }, 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)]=\"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"] }]
882
+ }, 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 $index) {\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"] }]
401
883
  }], ctorParameters: () => [], propDecorators: { aiTable: [{
402
884
  type: Input,
403
885
  args: [{ required: true }]
@@ -408,6 +890,102 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImpor
408
890
  args: [{ transform: booleanAttribute }]
409
891
  }] } });
410
892
 
893
+ const buildGridData = (recordValue, fieldsValue, references) => {
894
+ const fields = fieldsValue.map((item) => {
895
+ return {
896
+ ...item,
897
+ icon: item.icon || FieldsMap[item.type].icon,
898
+ width: item.width || FieldsMap[item.type].width
899
+ };
900
+ });
901
+ let records = buildRecordsByReferences(recordValue, fieldsValue, references);
902
+ return {
903
+ type: 'grid',
904
+ fields,
905
+ records
906
+ };
907
+ };
908
+ function buildRecordsByReferences(records, fields, references) {
909
+ if (!references) {
910
+ return records;
911
+ }
912
+ const memberFields = fields.filter((field) => [AITableFieldType.createdBy, AITableFieldType.updatedBy, AITableFieldType.member].includes(field.type));
913
+ if (memberFields.length) {
914
+ const uidToMember = references.members.reduce((map, member) => {
915
+ map[member.uid] = member;
916
+ return map;
917
+ }, {});
918
+ const draftRecords = createDraft(records);
919
+ draftRecords.forEach((record) => {
920
+ memberFields.forEach((field) => {
921
+ const value = record.values[field._id];
922
+ if (field.isMultiple) {
923
+ record.values[field._id] = value.map((uid) => uidToMember[uid]).filter(Boolean);
924
+ }
925
+ else {
926
+ record.values[field._id] = uidToMember[value] || {};
927
+ }
928
+ });
929
+ });
930
+ records = finishDraft(draftRecords);
931
+ }
932
+ return records;
933
+ }
934
+
935
+ function getRecordOrField(value, _id) {
936
+ return computed(() => {
937
+ return value().find((item) => item._id === _id);
938
+ });
939
+ }
940
+
941
+ class FieldMenu {
942
+ execute(menu) {
943
+ const field = getRecordOrField(this.aiTable.fields, this.fieldId);
944
+ menu.exec && menu.exec(this.aiTable, field, this.origin);
945
+ }
946
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: FieldMenu, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
947
+ 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.type === '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 }); }
948
+ }
949
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: FieldMenu, decorators: [{
950
+ type: Component,
951
+ args: [{ selector: 'field-menu', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [
952
+ ThyIcon,
953
+ ThyDivider,
954
+ ThyDropdownMenuComponent,
955
+ ThyDropdownMenuItemDirective,
956
+ ThyDropdownMenuItemNameDirective,
957
+ ThyDropdownMenuItemIconDirective
958
+ ], template: "@for (menu of fieldMenus; track index; let index = $index) {\n @if (menu.type === '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" }]
959
+ }], propDecorators: { fieldId: [{
960
+ type: Input,
961
+ args: [{ required: true }]
962
+ }], aiTable: [{
963
+ type: Input,
964
+ args: [{ required: true }]
965
+ }], fieldMenus: [{
966
+ type: Input,
967
+ args: [{ required: true }]
968
+ }], origin: [{
969
+ type: Input
970
+ }] } });
971
+
972
+ const DEFAULT_COLUMN_WIDTH = 200;
973
+ const MIN_COLUMN_WIDTH = 80;
974
+ const DBL_CLICK_EDIT_TYPE = [
975
+ AITableFieldType.text,
976
+ AITableFieldType.number,
977
+ AITableFieldType.select,
978
+ AITableFieldType.date,
979
+ AITableFieldType.member
980
+ ];
981
+ const MOUSEOVER_EDIT_TYPE = [AITableFieldType.progress];
982
+ const RowHeight = {
983
+ Short: 32,
984
+ Medium: 57,
985
+ Tall: 104,
986
+ ExtraTall: 152
987
+ };
988
+
411
989
  const AI_TABLE_GRID_FIELD_SERVICE_MAP = new WeakMap();
412
990
  class AITableGridFieldService {
413
991
  constructor(thyPopover) {
@@ -437,55 +1015,59 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImpor
437
1015
  }], ctorParameters: () => [{ type: i1$1.ThyPopover }] });
438
1016
 
439
1017
  const DividerMenuItem = {
440
- id: 'divider'
1018
+ type: 'divider'
441
1019
  };
442
1020
  const EditFieldPropertyItem = {
443
- id: 'editFieldProperty',
1021
+ type: 'editFieldProperty',
444
1022
  name: '编辑列',
445
1023
  icon: 'edit',
446
1024
  exec: (aiTable, field, origin) => {
447
1025
  const fieldService = AI_TABLE_GRID_FIELD_SERVICE_MAP.get(aiTable);
448
- origin && fieldService?.editFieldProperty(origin, aiTable, field, true);
1026
+ const copyField = signal(JSON.parse(JSON.stringify(field())));
1027
+ origin && fieldService?.editFieldProperty(origin, aiTable, copyField, true);
449
1028
  }
450
1029
  };
451
- const DefaultFieldMenus = [EditFieldPropertyItem];
452
-
453
- class AbstractEditCellEditor {
454
- constructor() {
455
- this.field = input.required();
456
- this.record = input.required();
457
- this.thyPopoverRef = inject((ThyPopoverRef));
458
- }
459
- ngOnInit() {
460
- this.modelValue = computed(() => {
461
- const path = AITableQueries.findPath(this.aiTable, this.field(), this.record());
462
- return AITableQueries.getFieldValue(this.aiTable, path);
463
- })();
1030
+ const RemoveFieldItem = {
1031
+ type: 'removeField',
1032
+ name: '删除列',
1033
+ icon: 'trash',
1034
+ exec: (aiTable, field) => {
1035
+ const path = AITableQueries.findPath(aiTable, field());
1036
+ Actions.removeField(aiTable, path);
464
1037
  }
465
- updateFieldValue() {
466
- const path = AITableQueries.findPath(this.aiTable, this.field(), this.record());
467
- Actions.updateFieldValue(this.aiTable, this.modelValue, path);
468
- }
469
- closePopover() {
470
- this.thyPopoverRef.close();
1038
+ };
1039
+ const DefaultFieldMenus = [EditFieldPropertyItem, RemoveFieldItem];
1040
+
1041
+ class SelectOptionPipe {
1042
+ transform(_id, options) {
1043
+ return options.find((item) => item._id === _id);
471
1044
  }
472
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: AbstractEditCellEditor, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
473
- 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 }); }
1045
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: SelectOptionPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
1046
+ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "18.0.2", ngImport: i0, type: SelectOptionPipe, isStandalone: true, name: "selectOption" }); }
474
1047
  }
475
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: AbstractEditCellEditor, decorators: [{
476
- type: Component,
1048
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: SelectOptionPipe, decorators: [{
1049
+ type: Pipe,
477
1050
  args: [{
478
- selector: 'abstract-edit-cell',
479
- template: ``,
480
- standalone: true,
481
- changeDetection: ChangeDetectionStrategy.OnPush
1051
+ name: 'selectOption',
1052
+ standalone: true
482
1053
  }]
483
- }], propDecorators: { aiTable: [{
484
- type: Input,
485
- args: [{ required: true }]
486
- }] } });
1054
+ }] });
1055
+ class IsSelectRecordPipe {
1056
+ transform(recordId, selection) {
1057
+ return selection.selectedRecords.has(recordId);
1058
+ }
1059
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: IsSelectRecordPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
1060
+ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "18.0.2", ngImport: i0, type: IsSelectRecordPipe, isStandalone: true, name: "isSelectRecord" }); }
1061
+ }
1062
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: IsSelectRecordPipe, decorators: [{
1063
+ type: Pipe,
1064
+ args: [{
1065
+ name: 'isSelectRecord',
1066
+ standalone: true
1067
+ }]
1068
+ }] });
487
1069
 
488
- class SingleSelectCellEditorComponent extends AbstractEditCellEditor {
1070
+ class SelectCellEditorComponent extends AbstractEditCellEditor {
489
1071
  constructor() {
490
1072
  super();
491
1073
  this.selectOptions = computed(() => {
@@ -498,17 +1080,17 @@ class SingleSelectCellEditorComponent extends AbstractEditCellEditor {
498
1080
  this.closePopover();
499
1081
  }
500
1082
  }
501
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: SingleSelectCellEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
502
- 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)">
503
- <thy-option *ngFor="let option of selectOptions()" [thyValue]="option.id" [thyLabelText]="option.name"> </thy-option>
1083
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: SelectCellEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1084
+ 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)">
1085
+ <thy-option *ngFor="let option of selectOptions()" [thyValue]="option._id" [thyLabelText]="option.text"> </thy-option>
504
1086
  </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 }); }
505
1087
  }
506
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: SingleSelectCellEditorComponent, decorators: [{
1088
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: SelectCellEditorComponent, decorators: [{
507
1089
  type: Component,
508
1090
  args: [{
509
1091
  selector: 'single-select-cell-editor',
510
1092
  template: `<thy-select [(ngModel)]="modelValue" [thyAutoExpand]="true" (thyOnExpandStatusChange)="updateValue($event)">
511
- <thy-option *ngFor="let option of selectOptions()" [thyValue]="option.id" [thyLabelText]="option.name"> </thy-option>
1093
+ <thy-option *ngFor="let option of selectOptions()" [thyValue]="option._id" [thyLabelText]="option.text"> </thy-option>
512
1094
  </thy-select> `,
513
1095
  standalone: true,
514
1096
  changeDetection: ChangeDetectionStrategy.OnPush,
@@ -517,200 +1099,30 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImpor
517
1099
  },
518
1100
  imports: [NgIf, NgForOf, FormsModule, ThySelect, ThyOption, ThyTag, ThyIcon]
519
1101
  }]
520
- }], ctorParameters: () => [] });
521
-
522
- class TextCellEditorComponent extends AbstractEditCellEditor {
523
- updateValue() {
524
- this.updateFieldValue();
525
- this.closePopover();
526
- }
527
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: TextCellEditorComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
528
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.0.2", type: TextCellEditorComponent, isStandalone: true, selector: "text-cell-editor", usesInheritance: true, ngImport: i0, template: `<input
529
- thyInput
530
- [thyAutofocus]="true"
531
- [(ngModel)]="modelValue"
532
- (thyEnter)="updateValue()"
533
- (blur)="updateValue()"
534
- placeholder=""
535
- /> `, isInline: true, dependencies: [{ kind: "ngmodule", type: FormsModule }, { 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.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: ThyAutofocusDirective, selector: "input[thyAutofocus],textarea[thyAutofocus]", inputs: ["thyAutofocus", "thyAutoSelect"] }, { kind: "directive", type: ThyInputDirective, selector: "input[thyInput], select[thyInput], textarea[thyInput]", inputs: ["thySize"], exportAs: ["thyInput"] }, { kind: "directive", type: ThyEnterDirective, selector: "[thyEnter]", outputs: ["thyEnter"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
536
- }
537
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: TextCellEditorComponent, decorators: [{
538
- type: Component,
539
- args: [{
540
- selector: 'text-cell-editor',
541
- template: `<input
542
- thyInput
543
- [thyAutofocus]="true"
544
- [(ngModel)]="modelValue"
545
- (thyEnter)="updateValue()"
546
- (blur)="updateValue()"
547
- placeholder=""
548
- /> `,
549
- standalone: true,
550
- changeDetection: ChangeDetectionStrategy.OnPush,
551
- imports: [NgIf, FormsModule, ThyAutofocusDirective, ThyInputDirective, ThyEnterDirective]
552
- }]
553
- }] });
554
-
555
- class NumberCellEditorComponent extends AbstractEditCellEditor {
556
- updateValue() {
557
- this.updateFieldValue();
558
- this.closePopover();
559
- }
560
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: NumberCellEditorComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
561
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.0.2", type: NumberCellEditorComponent, isStandalone: true, selector: "number-cell-editor", usesInheritance: true, ngImport: i0, template: `<thy-input-number
562
- class="h-100"
563
- [thyAutoFocus]="true"
564
- [(ngModel)]="modelValue"
565
- (thyEnter)="updateValue()"
566
- (thyBlur)="updateValue()"
567
- placeholder=""
568
- /> `, isInline: true, dependencies: [{ 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: "directive", type: ThyEnterDirective, selector: "[thyEnter]", outputs: ["thyEnter"] }, { kind: "component", type: ThyInputNumber, selector: "thy-input-number", inputs: ["thyAutoFocus", "thyPlaceholder", "thyDisabled", "thyMax", "thyMin", "thyStep", "thyStepDelay", "thySize", "thyPrecision", "thySuffix"], outputs: ["thyBlur", "thyFocus"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
569
- }
570
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: NumberCellEditorComponent, decorators: [{
571
- type: Component,
572
- args: [{
573
- selector: 'number-cell-editor',
574
- template: `<thy-input-number
575
- class="h-100"
576
- [thyAutoFocus]="true"
577
- [(ngModel)]="modelValue"
578
- (thyEnter)="updateValue()"
579
- (thyBlur)="updateValue()"
580
- placeholder=""
581
- /> `,
582
- standalone: true,
583
- changeDetection: ChangeDetectionStrategy.OnPush,
584
- imports: [FormsModule, ThyAutofocusDirective, ThyEnterDirective, ThyInputNumber]
585
- }]
586
- }] });
587
-
588
- class DateTimeCellEditorComponent extends AbstractEditCellEditor {
589
- constructor() {
590
- super(...arguments);
591
- this.dateShowTime = input(false);
592
- this.dateFormat = computed(() => {
593
- return this.dateShowTime() ? 'yyyy-MM-dd HH:mm' : 'yyyy-MM-dd';
594
- })();
595
- }
596
- ngOnInit() {
597
- super.ngOnInit();
598
- if (!this.modelValue && this.dateShowTime()) {
599
- this.modelValue = {
600
- date: 0,
601
- with_time: 1
602
- };
603
- }
604
- }
605
- updateValue() {
606
- this.updateFieldValue();
607
- this.closePopover();
608
- }
609
- thyOpenChange(isOpen) {
610
- if (!isOpen) {
611
- this.closePopover();
612
- }
613
- }
614
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: DateTimeCellEditorComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
615
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "18.0.2", type: DateTimeCellEditorComponent, isStandalone: true, selector: "date-time-cell-editor", inputs: { dateShowTime: { classPropertyName: "dateShowTime", publicName: "dateShowTime", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "date-time-cell-editor" }, usesInheritance: true, ngImport: i0, template: `
616
- <thy-date-picker
617
- class="h-100"
618
- thyTimestampPrecision="milliseconds"
619
- thyPlaceHolder="选择时间"
620
- [(ngModel)]="modelValue"
621
- (ngModelChange)="updateValue()"
622
- (thyOpenChange)="thyOpenChange($event)"
623
- [thyAllowClear]="true"
624
- [thyShowShortcut]="true"
625
- [thyHasBackdrop]="false"
626
- [thyShowTime]="dateShowTime()"
627
- [thyOpen]="true"
628
- [thyFormat]="dateFormat"
629
- >
630
- </thy-date-picker>
631
- `, isInline: true, dependencies: [{ 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: ThyDatePicker, selector: "thy-date-picker", exportAs: ["thyDatePicker"] }, { kind: "ngmodule", type: ThyTimePickerModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
632
- }
633
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: DateTimeCellEditorComponent, decorators: [{
634
- type: Component,
635
- args: [{
636
- selector: 'date-time-cell-editor',
637
- template: `
638
- <thy-date-picker
639
- class="h-100"
640
- thyTimestampPrecision="milliseconds"
641
- thyPlaceHolder="选择时间"
642
- [(ngModel)]="modelValue"
643
- (ngModelChange)="updateValue()"
644
- (thyOpenChange)="thyOpenChange($event)"
645
- [thyAllowClear]="true"
646
- [thyShowShortcut]="true"
647
- [thyHasBackdrop]="false"
648
- [thyShowTime]="dateShowTime()"
649
- [thyOpen]="true"
650
- [thyFormat]="dateFormat"
651
- >
652
- </thy-date-picker>
653
- `,
654
- standalone: true,
655
- changeDetection: ChangeDetectionStrategy.OnPush,
656
- imports: [FormsModule, ThyDatePicker, ThyTimePickerModule],
657
- host: {
658
- class: 'date-time-cell-editor'
659
- }
660
- }]
661
- }] });
662
-
663
- class RatingCellEditorComponent extends AbstractEditCellEditor {
664
- updateValue() {
665
- this.updateFieldValue();
666
- this.closePopover();
667
- }
668
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: RatingCellEditorComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
669
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.0.2", type: RatingCellEditorComponent, isStandalone: true, selector: "rating-cell-editor", usesInheritance: true, ngImport: i0, template: ` <thy-rate [(ngModel)]="modelValue" (ngModelChange)="updateValue()"></thy-rate> `, isInline: true, dependencies: [{ 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: ThyRate, selector: "thy-rate", inputs: ["thyCount", "thyDisabled", "thyAllowHalf", "thyAllowClear", "thyTooltips", "thyIconTemplate"], outputs: ["thyItemHoverChange"] }, { kind: "ngmodule", type: ThyTooltipModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
670
- }
671
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: RatingCellEditorComponent, decorators: [{
672
- type: Component,
673
- args: [{
674
- selector: 'rating-cell-editor',
675
- template: ` <thy-rate [(ngModel)]="modelValue" (ngModelChange)="updateValue()"></thy-rate> `,
676
- standalone: true,
677
- changeDetection: ChangeDetectionStrategy.OnPush,
678
- imports: [FormsModule, ThyRate, ThyTooltipModule]
679
- }]
680
- }] });
681
-
682
- class LinkCellEditorComponent extends AbstractEditCellEditor {
683
- updateValue() {
684
- this.updateFieldValue();
685
- this.closePopover();
686
- }
687
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: LinkCellEditorComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
688
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.0.2", type: LinkCellEditorComponent, isStandalone: true, selector: "link-cell-editor", usesInheritance: true, ngImport: i0, template: ``, isInline: true, dependencies: [{ kind: "ngmodule", type: FormsModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
689
- }
690
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: LinkCellEditorComponent, decorators: [{
691
- type: Component,
692
- args: [{
693
- selector: 'link-cell-editor',
694
- template: ``,
695
- standalone: true,
696
- changeDetection: ChangeDetectionStrategy.OnPush,
697
- imports: [FormsModule, ThyAutofocusDirective, ThyEnterDirective, ThyInputNumber]
698
- }]
699
- }] });
1102
+ }], ctorParameters: () => [], propDecorators: { isMultiple: [{
1103
+ type: Input
1104
+ }] } });
700
1105
 
701
1106
  const GRID_CELL_EDITOR_MAP = {
702
- [AITableFieldType.Text]: TextCellEditorComponent,
703
- [AITableFieldType.SingleSelect]: SingleSelectCellEditorComponent,
704
- [AITableFieldType.Number]: NumberCellEditorComponent,
705
- [AITableFieldType.DateTime]: DateTimeCellEditorComponent,
706
- [AITableFieldType.Rating]: RatingCellEditorComponent,
707
- [AITableFieldType.Link]: LinkCellEditorComponent
1107
+ [AITableFieldType.text]: TextCellEditorComponent,
1108
+ [AITableFieldType.richText]: TextCellEditorComponent,
1109
+ [AITableFieldType.select]: SelectCellEditorComponent,
1110
+ [AITableFieldType.number]: NumberCellEditorComponent,
1111
+ [AITableFieldType.date]: DateTimeCellEditorComponent,
1112
+ [AITableFieldType.rate]: RatingCellEditorComponent,
1113
+ [AITableFieldType.link]: LinkCellEditorComponent,
1114
+ [AITableFieldType.progress]: ProgressEditorComponent
708
1115
  };
709
1116
 
710
1117
  class AITableGridEventService {
711
- constructor(thyPopover) {
712
- this.thyPopover = thyPopover;
713
- this.takeUntilDestroyed = takeUntilDestroyed();
1118
+ constructor() {
1119
+ this.dblClickEvent$ = new Subject();
1120
+ this.mousedownEvent$ = new Subject();
1121
+ this.mouseoverEvent$ = new Subject();
1122
+ this.globalMouseoverEvent$ = new Subject();
1123
+ this.globalMousedownEvent$ = new Subject();
1124
+ this.destroyRef = inject(DestroyRef);
1125
+ this.thyPopover = inject(ThyPopover);
714
1126
  }
715
1127
  initialize(aiTable, aiFieldRenderers) {
716
1128
  this.aiTable = aiTable;
@@ -718,17 +1130,30 @@ class AITableGridEventService {
718
1130
  }
719
1131
  registerEvents(element) {
720
1132
  fromEvent(element, 'dblclick')
721
- .pipe(this.takeUntilDestroyed)
1133
+ .pipe(takeUntilDestroyed(this.destroyRef))
722
1134
  .subscribe((event) => {
723
- this.dblClick(event);
1135
+ this.dblClickEvent$.next(event);
1136
+ });
1137
+ fromEvent(element, 'mouseover')
1138
+ .pipe(debounceTime(80), takeUntilDestroyed(this.destroyRef))
1139
+ .subscribe((event) => {
1140
+ this.mouseoverEvent$.next(event);
1141
+ });
1142
+ fromEvent(document, 'mouseover')
1143
+ .pipe(takeUntilDestroyed(this.destroyRef))
1144
+ .subscribe((event) => {
1145
+ this.globalMouseoverEvent$.next(event);
1146
+ });
1147
+ fromEvent(element, 'mousedown')
1148
+ .pipe(takeUntilDestroyed(this.destroyRef))
1149
+ .subscribe((event) => {
1150
+ this.mousedownEvent$.next(event);
1151
+ });
1152
+ fromEvent(document, 'mousedown')
1153
+ .pipe(takeUntilDestroyed(this.destroyRef))
1154
+ .subscribe((event) => {
1155
+ this.globalMousedownEvent$.next(event);
724
1156
  });
725
- }
726
- dblClick(event) {
727
- const cellDom = event.target.closest('.grid-cell');
728
- const type = cellDom && cellDom.getAttribute('type');
729
- if (type && DBL_CLICK_EDIT_TYPE.includes(Number(type))) {
730
- this.openEdit(cellDom);
731
- }
732
1157
  }
733
1158
  getEditorComponent(type) {
734
1159
  if (this.aiFieldRenderers && this.aiFieldRenderers[type]) {
@@ -743,7 +1168,7 @@ class AITableGridEventService {
743
1168
  const field = getRecordOrField(this.aiTable.fields, fieldId);
744
1169
  const record = getRecordOrField(this.aiTable.records, recordId);
745
1170
  const component = this.getEditorComponent(field().type);
746
- this.thyPopover.open(component, {
1171
+ const ref = this.thyPopover.open(component, {
747
1172
  origin: cellDom,
748
1173
  originPosition: {
749
1174
  x: x - 1,
@@ -751,10 +1176,11 @@ class AITableGridEventService {
751
1176
  width: width + 2,
752
1177
  height: height + 2
753
1178
  },
754
- width: width + 2 + 'px',
1179
+ width: width + 1 + 'px',
755
1180
  height: height + 2 + 'px',
756
1181
  placement: 'top',
757
1182
  offset: -(height + 4),
1183
+ minWidth: width,
758
1184
  initialState: {
759
1185
  field: field,
760
1186
  record: record,
@@ -764,72 +1190,123 @@ class AITableGridEventService {
764
1190
  outsideClosable: false,
765
1191
  hasBackdrop: false,
766
1192
  manualClosure: true,
767
- animationDisabled: true
1193
+ animationDisabled: true,
1194
+ autoAdaptive: true
768
1195
  });
1196
+ return ref;
769
1197
  }
770
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: AITableGridEventService, deps: [{ token: i1$1.ThyPopover }], target: i0.ɵɵFactoryTarget.Injectable }); }
1198
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: AITableGridEventService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
771
1199
  static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: AITableGridEventService }); }
772
1200
  }
773
1201
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: AITableGridEventService, decorators: [{
774
1202
  type: Injectable
775
- }], ctorParameters: () => [{ type: i1$1.ThyPopover }] });
1203
+ }] });
776
1204
 
777
- class FieldMenu {
778
- execute(menu) {
779
- const field = signal({ ...this.field });
780
- menu.exec && menu.exec(this.aiTable, field, this.origin);
1205
+ class AITableGridSelectionService {
1206
+ constructor() { }
1207
+ initialize(aiTable) {
1208
+ this.aiTable = aiTable;
781
1209
  }
782
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: FieldMenu, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
783
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.0.2", type: FieldMenu, isStandalone: true, selector: "field-menu", inputs: { field: "field", 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 }); }
1210
+ clearSelection() {
1211
+ this.aiTable.selection.set({
1212
+ selectedRecords: new Map(),
1213
+ selectedFields: new Map(),
1214
+ selectedCells: new Map()
1215
+ });
1216
+ }
1217
+ selectCell(recordId, fieldId) {
1218
+ this.clearSelection();
1219
+ this.aiTable.selection().selectedCells.set(recordId, { [fieldId]: true });
1220
+ }
1221
+ selectField(fieldId) {
1222
+ this.clearSelection();
1223
+ this.aiTable.selection().selectedFields.set(fieldId, true);
1224
+ }
1225
+ selectRecord(recordId) {
1226
+ if (this.aiTable.selection().selectedRecords.has(recordId)) {
1227
+ this.aiTable.selection().selectedRecords.delete(recordId);
1228
+ }
1229
+ else {
1230
+ this.aiTable.selection().selectedRecords.set(recordId, true);
1231
+ }
1232
+ this.aiTable.selection.set({
1233
+ selectedRecords: this.aiTable.selection().selectedRecords,
1234
+ selectedFields: new Map(),
1235
+ selectedCells: new Map()
1236
+ });
1237
+ }
1238
+ toggleSelectAll(checked) {
1239
+ this.clearSelection();
1240
+ if (checked) {
1241
+ this.aiTable.records().forEach((item) => {
1242
+ this.selectRecord(item._id);
1243
+ });
1244
+ }
1245
+ }
1246
+ updateSelect(event) {
1247
+ const target = event?.target;
1248
+ if (!target) {
1249
+ return;
1250
+ }
1251
+ const cellDom = target.closest('.grid-cell');
1252
+ const colDom = target.closest('.grid-field');
1253
+ const checkbox = target.tagName === 'INPUT' && target.type === 'checkbox' && target.closest('.grid-checkbox');
1254
+ const fieldAction = target.closest('.grid-field-action');
1255
+ if (cellDom) {
1256
+ const fieldId = cellDom.getAttribute('fieldId');
1257
+ const recordId = cellDom.getAttribute('recordId');
1258
+ fieldId && recordId && this.selectCell(recordId, fieldId);
1259
+ }
1260
+ if (colDom && !fieldAction) {
1261
+ const fieldId = colDom.getAttribute('fieldId');
1262
+ fieldId && this.selectField(fieldId);
1263
+ }
1264
+ if (!cellDom && !colDom && !checkbox) {
1265
+ this.clearSelection();
1266
+ }
1267
+ }
1268
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: AITableGridSelectionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1269
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: AITableGridSelectionService }); }
784
1270
  }
785
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: FieldMenu, decorators: [{
786
- type: Component,
787
- args: [{ selector: 'field-menu', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [
788
- ThyIcon,
789
- ThyDivider,
790
- ThyDropdownMenuComponent,
791
- ThyDropdownMenuItemDirective,
792
- ThyDropdownMenuItemNameDirective,
793
- ThyDropdownMenuItemIconDirective
794
- ], 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" }]
795
- }], propDecorators: { field: [{
796
- type: Input,
797
- args: [{ required: true }]
798
- }], aiTable: [{
799
- type: Input,
800
- args: [{ required: true }]
801
- }], fieldMenus: [{
802
- type: Input,
803
- args: [{ required: true }]
804
- }], origin: [{
805
- type: Input
806
- }] } });
1271
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: AITableGridSelectionService, decorators: [{
1272
+ type: Injectable
1273
+ }], ctorParameters: () => [] });
807
1274
 
808
1275
  class AITableGrid {
809
- constructor(elementRef, aiTableGridEventService, aiTableGridFieldService) {
810
- this.elementRef = elementRef;
811
- this.aiTableGridEventService = aiTableGridEventService;
812
- this.aiTableGridFieldService = aiTableGridFieldService;
1276
+ constructor() {
813
1277
  this.aiRecords = model.required();
814
1278
  this.aiFields = model.required();
815
- this.aiRowHeight = input();
816
1279
  this.aiFieldConfig = input();
817
1280
  this.aiReadonly = input();
1281
+ this.aiPlugins = input();
1282
+ this.aiReferences = input();
818
1283
  this.AITableFieldType = AITableFieldType;
819
- this.takeUntilDestroyed = takeUntilDestroyed();
1284
+ this.isSelectedAll = computed(() => {
1285
+ return this.aiTable.selection().selectedRecords.size === this.aiRecords().length;
1286
+ });
820
1287
  this.onChange = output();
821
1288
  this.aiTableInitialized = output();
822
1289
  this.gridData = computed(() => {
823
- return buildGridData(this.aiRecords(), this.aiFields());
1290
+ return buildGridData(this.aiRecords(), this.aiFields(), this.aiReferences());
824
1291
  });
1292
+ this.ngZone = inject(NgZone);
1293
+ this.elementRef = inject(ElementRef);
1294
+ this.destroyRef = inject(DestroyRef);
1295
+ this.aiTableGridFieldService = inject(AITableGridFieldService);
1296
+ this.aiTableGridEventService = inject(AITableGridEventService);
1297
+ this.aiTableGridSelectionService = inject(AITableGridSelectionService);
825
1298
  }
826
1299
  ngOnInit() {
827
1300
  this.initAITable();
828
1301
  this.initService();
829
1302
  this.buildFieldMenus();
1303
+ this.subscribeEvents();
830
1304
  }
831
1305
  initAITable() {
832
1306
  this.aiTable = createAITable(this.aiRecords, this.aiFields);
1307
+ this.aiPlugins()?.forEach((plugin) => {
1308
+ this.aiTable = plugin(this.aiTable);
1309
+ });
833
1310
  this.aiTableInitialized.emit(this.aiTable);
834
1311
  this.aiTable.onChange = () => {
835
1312
  this.onChange.emit({
@@ -841,6 +1318,7 @@ class AITableGrid {
841
1318
  }
842
1319
  initService() {
843
1320
  this.aiTableGridEventService.initialize(this.aiTable, this.aiFieldConfig()?.fieldPropertyEditor);
1321
+ this.aiTableGridSelectionService.initialize(this.aiTable);
844
1322
  this.aiTableGridEventService.registerEvents(this.elementRef.nativeElement);
845
1323
  this.aiTableGridFieldService.initAIFieldConfig(this.aiFieldConfig());
846
1324
  AI_TABLE_GRID_FIELD_SERVICE_MAP.set(this.aiTable, this.aiTableGridFieldService);
@@ -851,12 +1329,63 @@ class AITableGrid {
851
1329
  addRecord() {
852
1330
  Actions.addRecord(this.aiTable, getDefaultRecord(this.aiFields()), [this.aiRecords().length]);
853
1331
  }
1332
+ selectRecord(recordId) {
1333
+ this.aiTableGridSelectionService.selectRecord(recordId);
1334
+ }
1335
+ toggleSelectAll(checked) {
1336
+ this.aiTableGridSelectionService.toggleSelectAll(checked);
1337
+ }
854
1338
  addField(gridColumnBlank) {
855
- const field = signal(createDefaultField(this.aiTable, AITableFieldType.Text));
1339
+ const field = signal(createDefaultField(this.aiTable, AITableFieldType.text));
856
1340
  this.aiTableGridFieldService.editFieldProperty(gridColumnBlank, this.aiTable, field, false);
857
1341
  }
858
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: AITableGrid, deps: [{ token: i0.ElementRef }, { token: AITableGridEventService }, { token: AITableGridFieldService }], target: i0.ɵɵFactoryTarget.Component }); }
859
- 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 } }, outputs: { aiRecords: "aiRecordsChange", aiFields: "aiFieldsChange", onChange: "onChange", aiTableInitialized: "aiTableInitialized" }, host: { classAttribute: "ai-table-grid" }, providers: [ThyTooltipService, AITableGridEventService, AITableGridFieldService], 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 grid-field\" #fieldAction>\n {{ field.name }}\n <a thyAction thyActiveClass=\"active\" thyIcon=\"more-vertical\" [thyDropdown]=\"fieldMenu\" href=\"javascript:;\">\n <thy-dropdown-menu #fieldMenu>\n <field-menu [origin]=\"fieldAction\" [field]=\"field\" [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\">\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"] }, { kind: "component", type: FieldMenu, selector: "field-menu", inputs: ["field", "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"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1342
+ subscribeEvents() {
1343
+ this.ngZone.runOutsideAngular(() => {
1344
+ this.aiTableGridEventService.dblClickEvent$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((event) => {
1345
+ this.dblClick(event);
1346
+ });
1347
+ this.aiTableGridEventService.mousedownEvent$
1348
+ .pipe(mergeWith(this.aiTableGridEventService.globalMousedownEvent$), takeUntilDestroyed(this.destroyRef))
1349
+ .subscribe((event) => {
1350
+ this.aiTableGridSelectionService.updateSelect(event);
1351
+ });
1352
+ this.aiTableGridEventService.mouseoverEvent$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((event) => {
1353
+ this.mouseoverHandle(event);
1354
+ });
1355
+ this.aiTableGridEventService.globalMouseoverEvent$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((event) => {
1356
+ this.closeHoverCellEditor(event);
1357
+ });
1358
+ });
1359
+ }
1360
+ dblClick(event) {
1361
+ const cellDom = event.target.closest('.grid-cell');
1362
+ const type = cellDom && cellDom.getAttribute('type');
1363
+ const readonly = cellDom && cellDom.getAttribute('readonly');
1364
+ if (type && !readonly && DBL_CLICK_EDIT_TYPE.includes(type)) {
1365
+ this.aiTableGridEventService.openEdit(cellDom);
1366
+ }
1367
+ }
1368
+ mouseoverHandle(event) {
1369
+ if (this.mouseoverRef) {
1370
+ this.mouseoverRef?.close();
1371
+ }
1372
+ const cellDom = event.target.closest('.grid-cell');
1373
+ const type = cellDom && cellDom.getAttribute('type');
1374
+ if (type && MOUSEOVER_EDIT_TYPE.includes(type)) {
1375
+ this.mouseoverRef = this.aiTableGridEventService.openEdit(cellDom);
1376
+ }
1377
+ }
1378
+ closeHoverCellEditor(e) {
1379
+ if (this.mouseoverRef) {
1380
+ const hasGrid = e.target && e.target.closest('.ai-table-grid');
1381
+ const hasCellEditor = e.target && e.target.closest('.grid-cell-editor');
1382
+ if (!hasGrid && !hasCellEditor) {
1383
+ this.mouseoverRef.close();
1384
+ }
1385
+ }
1386
+ }
1387
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: AITableGrid, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1388
+ 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 }, 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 }, aiReferences: { classPropertyName: "aiReferences", publicName: "aiReferences", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { aiRecords: "aiRecordsChange", aiFields: "aiFieldsChange", onChange: "onChange", aiTableInitialized: "aiTableInitialized" }, host: { classAttribute: "ai-table-grid" }, providers: [AITableGridEventService, AITableGridFieldService, AITableGridSelectionService], ngImport: i0, template: "<div class=\"grid-header d-flex\">\n <div class=\"grid-column-checkbox grid-cell grid-checkbox\">\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: (record._id | isSelectRecord: aiTable.selection()) }\">\n <div class=\"grid-row-index grid-checkbox\">\n <label\n [ngClass]=\"(record._id | isSelectRecord: aiTable.selection()) ? 'checked-box' : 'unchecked-box'\"\n thyCheckbox\n thyLabelText=\"\"\n [ngModel]=\"record._id | isSelectRecord: aiTable.selection()\"\n (ngModelChange)=\"selectRecord(record._id)\"\n ></label>\n <span [ngClass]=\"(record._id | isSelectRecord: aiTable.selection()) ? 'grid-row-no-number' : 'grid-row-number'\">\n {{ index + 1 }}\n </span>\n </div>\n @for (field of gridData().fields; track field._id) {\n <div\n #cell\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.readonly]=\"[field?.readonly]\"\n [attr.recordId]=\"[record._id]\"\n [ngStyle]=\"{ width: field.width + 'px' }\"\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.text }}</thy-tag>\n }\n }\n @case (AITableFieldType.date) {\n {{ record.values[field._id] | thyDatePickerFormat }}\n }\n @case (AITableFieldType.updatedAt) {\n <div class=\"d-block user-select-none\">\n <span class=\"text-truncate\">\n {{ record.values[field._id] | thyDatePickerFormat: 'yyyy-MM-dd HH:mm' }}\n </span>\n </div>\n }\n @case (AITableFieldType.createdAt) {\n <div class=\"d-block user-select-none\">\n <span class=\"text-truncate\">\n {{ record.values[field._id] | thyDatePickerFormat: 'yyyy-MM-dd HH:mm' }}\n </span>\n </div>\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 target=\"_blank\"\n [href]=\"record.values[field._id]?.url\"\n thyStopPropagation\n thyFlexibleText\n [thyTooltipContent]=\"record.values[field._id]?.text\"\n >\n {{ record.values[field._id]?.text }}\n </a>\n }\n @case (AITableFieldType.progress) {\n <thy-progress\n class=\"w-100\"\n [thyValue]=\"record.values[field._id]\"\n [thySize]=\"record.values[field._id]?.config?.size || 'md'\"\n [thyMax]=\"record.values[field._id]?.config?.max || 100\"\n [thyType]=\"record.values[field._id]?.config?.progressType || 'success'\"\n >\n <span> {{ record.values[field._id] }}{{ record.values[field._id]?.config?.suffix || '%' }} </span>\n </thy-progress>\n }\n @case (AITableFieldType.member) {\n @if (!field.isMultiple) {\n <thy-avatar\n [thyName]=\"record.values[field._id].display_name\"\n [thySrc]=\"record.values[field._id].avatar\"\n thySize=\"xs\"\n thyShowName=\"true\"\n ></thy-avatar>\n } @else {\n <thy-avatar-list thyAvatarSize=\"xs\">\n @for (item of record.values[field._id]; track $index) {\n <thy-avatar [thyName]=\"item.display_name\" [thySrc]=\"item.avatar\"></thy-avatar>\n }\n </thy-avatar-list>\n }\n }\n @case (AITableFieldType.createdBy) {\n <thy-avatar\n [thyName]=\"record.values[field._id].display_name\"\n [thySrc]=\"record.values[field._id].avatar\"\n thySize=\"xs\"\n thyShowName=\"true\"\n ></thy-avatar>\n }\n @case (AITableFieldType.updatedBy) {\n <thy-avatar\n [thyName]=\"record.values[field._id].display_name\"\n [thySrc]=\"record.values[field._id].avatar\"\n thySize=\"xs\"\n thyShowName=\"true\"\n ></thy-avatar>\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>", dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.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: "component", type: ThyProgress, selector: "thy-progress", inputs: ["thyType", "thySize", "thyValue", "thyMax", "thyTips", "thyShape", "thyGapDegree", "thyGapPosition", "thyStrokeWidth"] }, { kind: "pipe", type: ThyDatePickerFormatPipe, name: "thyDatePickerFormat" }, { 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: i3.ThyCheckbox, selector: "thy-checkbox,[thy-checkbox],[thyCheckbox]", inputs: ["thyIndeterminate"] }, { kind: "ngmodule", type: ThyAvatarModule }, { kind: "component", type: i4.ThyAvatar, selector: "thy-avatar", inputs: ["thyShowName", "thySrc", "thyName", "thySize", "thyShowRemove", "thyRemovable", "thyImgClass", "thyDisabled", "thyLoading", "thyFetchPriority"], outputs: ["thyOnRemove", "thyRemove", "thyError"] }, { kind: "component", type: i4.ThyAvatarList, selector: "thy-avatar-list", inputs: ["thyMode", "thyAvatarSize"] }, { kind: "pipe", type: IsSelectRecordPipe, name: "isSelectRecord" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
860
1389
  }
861
1390
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: AITableGrid, decorators: [{
862
1391
  type: Component,
@@ -873,29 +1402,26 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImpor
873
1402
  ThyPopoverModule,
874
1403
  ThyIcon,
875
1404
  ThyRate,
1405
+ ThyProgress,
876
1406
  AITableFieldPropertyEditor,
877
1407
  ThyDatePickerFormatPipe,
878
- ThyTooltipModule,
879
1408
  ThyFlexibleText,
880
1409
  ThyStopPropagationDirective,
881
1410
  FieldMenu,
882
1411
  ThyAction,
883
1412
  ThyDropdownDirective,
884
- ThyDropdownMenuComponent
885
- ], providers: [ThyTooltipService, AITableGridEventService, AITableGridFieldService], 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 grid-field\" #fieldAction>\n {{ field.name }}\n <a thyAction thyActiveClass=\"active\" thyIcon=\"more-vertical\" [thyDropdown]=\"fieldMenu\" href=\"javascript:;\">\n <thy-dropdown-menu #fieldMenu>\n <field-menu [origin]=\"fieldAction\" [field]=\"field\" [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\">\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" }]
886
- }], ctorParameters: () => [{ type: i0.ElementRef }, { type: AITableGridEventService }, { type: AITableGridFieldService }] });
887
-
888
- var AITableRowHeight;
889
- (function (AITableRowHeight) {
890
- AITableRowHeight[AITableRowHeight["Short"] = 1] = "Short";
891
- AITableRowHeight[AITableRowHeight["Medium"] = 2] = "Medium";
892
- AITableRowHeight[AITableRowHeight["Tall"] = 3] = "Tall";
893
- AITableRowHeight[AITableRowHeight["ExtraTall"] = 4] = "ExtraTall";
894
- })(AITableRowHeight || (AITableRowHeight = {}));
1413
+ ThyDropdownMenuComponent,
1414
+ ThyCheckboxModule,
1415
+ ProgressEditorComponent,
1416
+ ThyAvatarModule,
1417
+ NgTemplateOutlet,
1418
+ IsSelectRecordPipe
1419
+ ], providers: [AITableGridEventService, AITableGridFieldService, AITableGridSelectionService], template: "<div class=\"grid-header d-flex\">\n <div class=\"grid-column-checkbox grid-cell grid-checkbox\">\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: (record._id | isSelectRecord: aiTable.selection()) }\">\n <div class=\"grid-row-index grid-checkbox\">\n <label\n [ngClass]=\"(record._id | isSelectRecord: aiTable.selection()) ? 'checked-box' : 'unchecked-box'\"\n thyCheckbox\n thyLabelText=\"\"\n [ngModel]=\"record._id | isSelectRecord: aiTable.selection()\"\n (ngModelChange)=\"selectRecord(record._id)\"\n ></label>\n <span [ngClass]=\"(record._id | isSelectRecord: aiTable.selection()) ? 'grid-row-no-number' : 'grid-row-number'\">\n {{ index + 1 }}\n </span>\n </div>\n @for (field of gridData().fields; track field._id) {\n <div\n #cell\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.readonly]=\"[field?.readonly]\"\n [attr.recordId]=\"[record._id]\"\n [ngStyle]=\"{ width: field.width + 'px' }\"\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.text }}</thy-tag>\n }\n }\n @case (AITableFieldType.date) {\n {{ record.values[field._id] | thyDatePickerFormat }}\n }\n @case (AITableFieldType.updatedAt) {\n <div class=\"d-block user-select-none\">\n <span class=\"text-truncate\">\n {{ record.values[field._id] | thyDatePickerFormat: 'yyyy-MM-dd HH:mm' }}\n </span>\n </div>\n }\n @case (AITableFieldType.createdAt) {\n <div class=\"d-block user-select-none\">\n <span class=\"text-truncate\">\n {{ record.values[field._id] | thyDatePickerFormat: 'yyyy-MM-dd HH:mm' }}\n </span>\n </div>\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 target=\"_blank\"\n [href]=\"record.values[field._id]?.url\"\n thyStopPropagation\n thyFlexibleText\n [thyTooltipContent]=\"record.values[field._id]?.text\"\n >\n {{ record.values[field._id]?.text }}\n </a>\n }\n @case (AITableFieldType.progress) {\n <thy-progress\n class=\"w-100\"\n [thyValue]=\"record.values[field._id]\"\n [thySize]=\"record.values[field._id]?.config?.size || 'md'\"\n [thyMax]=\"record.values[field._id]?.config?.max || 100\"\n [thyType]=\"record.values[field._id]?.config?.progressType || 'success'\"\n >\n <span> {{ record.values[field._id] }}{{ record.values[field._id]?.config?.suffix || '%' }} </span>\n </thy-progress>\n }\n @case (AITableFieldType.member) {\n @if (!field.isMultiple) {\n <thy-avatar\n [thyName]=\"record.values[field._id].display_name\"\n [thySrc]=\"record.values[field._id].avatar\"\n thySize=\"xs\"\n thyShowName=\"true\"\n ></thy-avatar>\n } @else {\n <thy-avatar-list thyAvatarSize=\"xs\">\n @for (item of record.values[field._id]; track $index) {\n <thy-avatar [thyName]=\"item.display_name\" [thySrc]=\"item.avatar\"></thy-avatar>\n }\n </thy-avatar-list>\n }\n }\n @case (AITableFieldType.createdBy) {\n <thy-avatar\n [thyName]=\"record.values[field._id].display_name\"\n [thySrc]=\"record.values[field._id].avatar\"\n thySize=\"xs\"\n thyShowName=\"true\"\n ></thy-avatar>\n }\n @case (AITableFieldType.updatedBy) {\n <thy-avatar\n [thyName]=\"record.values[field._id].display_name\"\n [thySrc]=\"record.values[field._id].avatar\"\n thySize=\"xs\"\n thyShowName=\"true\"\n ></thy-avatar>\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>" }]
1420
+ }] });
895
1421
 
896
1422
  /**
897
1423
  * Generated bundle index. Do not edit.
898
1424
  */
899
1425
 
900
- 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, RowHeight, SelectOptionPipe, buildGridData, createAITable, createDefaultField, createDefaultFieldName, getDefaultFieldValue, getDefaultRecord, getRecordOrField, idCreator };
1426
+ export { AITableFieldPropertyEditor, AITableFieldType, AITableGrid, AITableQueries, AITableStatType, ActionName, Actions, BasicFields, DBL_CLICK_EDIT_TYPE, DEFAULT_COLUMN_WIDTH, DateTimeCellEditorComponent, DefaultFieldMenus, DividerMenuItem, EditFieldPropertyItem, ExecuteType, FLUSHING, Fields, FieldsMap, IsSelectRecordPipe, LinkCellEditorComponent, MIN_COLUMN_WIDTH, MOUSEOVER_EDIT_TYPE, NumberCellEditorComponent, ProgressEditorComponent, RatingCellEditorComponent, RemoveFieldItem, RowHeight, SelectOptionPipe, TextCellEditorComponent, buildGridData, buildRecordsByReferences, createAITable, createDefaultField, createDefaultFieldName, getDefaultFieldValue, getDefaultRecord, getRecordOrField, idCreator, isPathEqual };
901
1427
  //# sourceMappingURL=ai-table-grid.mjs.map