@ai-table/grid 0.0.3 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
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