@limetech/lime-crm-building-blocks 1.103.5 → 1.103.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/cjs/limebb-lime-query-builder.cjs.entry.js +2 -4
  3. package/dist/cjs/limebb-lime-query-filter-group_3.cjs.entry.js +123 -12
  4. package/dist/cjs/limebb-lime-query-response-format-editor_2.cjs.entry.js +48 -34
  5. package/dist/collection/components/lime-query-builder/expressions/lime-query-value-input.js +15 -12
  6. package/dist/collection/components/lime-query-builder/lime-query-builder.js +2 -4
  7. package/dist/collection/components/lime-query-builder/response-format/response-format-editor.js +48 -34
  8. package/dist/collection/components/lime-query-builder/type-resolution.js +108 -0
  9. package/dist/components/lime-query-value-input.js +123 -12
  10. package/dist/components/limebb-lime-query-builder.js +2 -4
  11. package/dist/components/response-format-editor.js +48 -34
  12. package/dist/esm/limebb-lime-query-builder.entry.js +2 -4
  13. package/dist/esm/limebb-lime-query-filter-group_3.entry.js +123 -12
  14. package/dist/esm/limebb-lime-query-response-format-editor_2.entry.js +48 -34
  15. package/dist/lime-crm-building-blocks/lime-crm-building-blocks.esm.js +1 -1
  16. package/dist/lime-crm-building-blocks/p-908dd7d5.entry.js +1 -0
  17. package/dist/lime-crm-building-blocks/p-adfb9e90.entry.js +1 -0
  18. package/dist/lime-crm-building-blocks/p-fe6a94a1.entry.js +1 -0
  19. package/dist/types/components/lime-query-builder/expressions/lime-query-value-input.d.ts +1 -2
  20. package/dist/types/components/lime-query-builder/response-format/response-format-editor.d.ts +16 -0
  21. package/dist/types/components/lime-query-builder/type-resolution.d.ts +73 -0
  22. package/package.json +1 -1
  23. package/dist/lime-crm-building-blocks/p-47f4f505.entry.js +0 -1
  24. package/dist/lime-crm-building-blocks/p-4f605428.entry.js +0 -1
  25. package/dist/lime-crm-building-blocks/p-d635e6fc.entry.js +0 -1
@@ -2,6 +2,115 @@ import { proxyCustomElement, HTMLElement, createEvent, h, Host } from '@stencil/
2
2
  import { T as Te, Z as Zt } from './index.esm.js';
3
3
  import { g as getPropertyFromPath, d as defineCustomElement$1 } from './property-selector.js';
4
4
 
5
+ /**
6
+ * System property name to actual type mapping.
7
+ *
8
+ * Maps system property names (with or without underscore prefix) to their actual data types.
9
+ * This mapping is necessary because system properties have `type='system'` in the metadata
10
+ * (for historical reasons - database schema fieldtype 255), but we need to know their
11
+ * actual data types to render appropriate inputs.
12
+ */
13
+ const SYSTEM_TYPE_MAP = {
14
+ timestamp: 'time',
15
+ createdtime: 'time',
16
+ updatedtime: 'time',
17
+ id: 'integer',
18
+ createduser: 'integer',
19
+ updateduser: 'integer',
20
+ descriptive: 'string',
21
+ };
22
+ /**
23
+ * Lime CRM date/time property type to Lime Elements date picker type mapping.
24
+ *
25
+ * Maps Lime CRM property types to the corresponding Lime Elements date picker types,
26
+ * as they use different naming conventions.
27
+ */
28
+ const DATE_TIME_TYPE_MAP = {
29
+ date: 'date',
30
+ time: 'datetime',
31
+ timeofday: 'time',
32
+ month: 'month',
33
+ quarter: 'quarter',
34
+ year: 'year',
35
+ };
36
+ /**
37
+ * Get the actual type of a property, resolving 'system' type to the underlying type.
38
+ *
39
+ * System properties in Lime CRM have `type: 'system'` in the metadata (a categorical marker
40
+ * from the database schema fieldtype 255), but we need to know their actual data types
41
+ * to render appropriate input controls.
42
+ *
43
+ * @param property - The property to get the type for
44
+ * @param property.type
45
+ * @param property.name
46
+ * @returns The actual property type (resolves 'system' to the underlying type)
47
+ *
48
+ * @example
49
+ * ```typescript
50
+ * const property = { name: '_timestamp', type: 'system' };
51
+ * getActualPropertyType(property); // Returns 'time'
52
+ * ```
53
+ */
54
+ function getActualPropertyType(property) {
55
+ if (property.type === 'system') {
56
+ return resolveSystemPropertyType(property.name);
57
+ }
58
+ return property.type;
59
+ }
60
+ /**
61
+ * Map system property names to their actual data types.
62
+ *
63
+ * This mapping is necessary because system properties have `type='system'` in the metadata
64
+ * (for historical reasons - database schema fieldtype 255), but we need to know their
65
+ * actual data types to render appropriate inputs.
66
+ *
67
+ * The mapping is based on the Lime CRM database schema:
68
+ * - System datetime fields: `timestamp`, `createdtime`, `updatedtime`
69
+ * - System integer fields: `id`, `createduser`, `updateduser`
70
+ * - System string fields: `descriptive`
71
+ *
72
+ * @param propertyName - The system property name (with or without underscore prefix)
73
+ * @returns The actual property type
74
+ *
75
+ * @example
76
+ * ```typescript
77
+ * resolveSystemPropertyType('_timestamp'); // Returns 'time'
78
+ * resolveSystemPropertyType('timestamp'); // Returns 'time'
79
+ * resolveSystemPropertyType('_id'); // Returns 'integer'
80
+ * resolveSystemPropertyType('unknown'); // Returns 'string' (default)
81
+ * ```
82
+ */
83
+ function resolveSystemPropertyType(propertyName) {
84
+ const name = propertyName.replace(/^_/, '');
85
+ return SYSTEM_TYPE_MAP[name] || 'string';
86
+ }
87
+ /**
88
+ * Map Lime CRM property types to Lime Elements date picker types.
89
+ *
90
+ * Lime CRM and Lime Elements use different naming conventions for date/time types,
91
+ * so we need to translate between them:
92
+ *
93
+ * - `'date'` → `'date'` - Date only
94
+ * - `'time'` → `'datetime'` - Full datetime (date + time) in Lime CRM
95
+ * - `'timeofday'` → `'time'` - Time only (hh:mm)
96
+ * - `'month'` → `'month'` - Month picker (YYYY-MM)
97
+ * - `'quarter'` → `'quarter'` - Quarter picker
98
+ * - `'year'` → `'year'` - Year picker
99
+ *
100
+ * @param propertyType - The Lime CRM date/time property type
101
+ * @returns The corresponding Lime Elements date picker type
102
+ *
103
+ * @example
104
+ * ```typescript
105
+ * mapPropertyTypeToPickerType('time'); // Returns 'datetime'
106
+ * mapPropertyTypeToPickerType('timeofday'); // Returns 'time'
107
+ * mapPropertyTypeToPickerType('date'); // Returns 'date'
108
+ * ```
109
+ */
110
+ function mapPropertyTypeToPickerType(propertyType) {
111
+ return DATE_TIME_TYPE_MAP[propertyType] || 'date';
112
+ }
113
+
5
114
  const limeQueryValueInputCss = ":host{display:flex;gap:0.5rem}:host>*{flex-grow:1}*{box-sizing:border-box}.value-input-container{display:flex;flex-direction:row;align-items:flex-start;gap:0.5rem;width:100%}.mode-toggle{flex-shrink:0;flex-grow:0}.placeholder-input{flex-grow:1;display:flex;flex-direction:column;gap:0.5rem}.placeholder-preview{display:flex;align-items:center;gap:0.5rem;padding:0.5rem;background-color:rgba(var(--color-blue-light), 0.1);border-radius:var(--border-radius-small);font-size:0.875rem;color:rgb(var(--color-blue-default));border-left:3px solid rgb(var(--color-blue-default))}.placeholder-preview limel-icon{flex-shrink:0;color:rgb(var(--color-blue-default))}.placeholder-preview span{font-family:var(--font-monospace);word-break:break-all}";
6
115
  const LimebbLimeQueryValueInputStyle0 = limeQueryValueInputCss;
7
116
 
@@ -166,10 +275,12 @@ const LimeQueryValueInput = /*@__PURE__*/ proxyCustomElement(class LimeQueryValu
166
275
  if (!property) {
167
276
  return this.renderTextInput();
168
277
  }
169
- switch (property.type) {
278
+ // Resolve 'system' type to actual type
279
+ const actualType = getActualPropertyType(property);
280
+ switch (actualType) {
170
281
  case 'integer':
171
282
  case 'decimal': {
172
- return this.renderNumberInput(property.type);
283
+ return this.renderNumberInput(actualType);
173
284
  }
174
285
  case 'yesno': {
175
286
  return this.renderBooleanInput();
@@ -177,11 +288,13 @@ const LimeQueryValueInput = /*@__PURE__*/ proxyCustomElement(class LimeQueryValu
177
288
  case 'option': {
178
289
  return this.renderOptionInput(property);
179
290
  }
180
- case 'date': {
181
- return this.renderDateInput();
182
- }
183
- case 'time': {
184
- return this.renderTimeInput();
291
+ case 'date':
292
+ case 'time':
293
+ case 'timeofday':
294
+ case 'year':
295
+ case 'month':
296
+ case 'quarter': {
297
+ return this.renderDateTimeInput(actualType);
185
298
  }
186
299
  default: {
187
300
  return this.renderTextInput();
@@ -217,13 +330,11 @@ const LimeQueryValueInput = /*@__PURE__*/ proxyCustomElement(class LimeQueryValu
217
330
  const selectedOption = options.find((o) => o.value === this.value);
218
331
  return (h("limel-select", { label: this.label, options: options, value: selectedOption, onChange: this.handleSelectChange }));
219
332
  }
220
- renderDateInput() {
333
+ renderDateTimeInput(type) {
221
334
  // Convert string to Date if needed
222
335
  const dateValue = typeof this.value === 'string' ? new Date(this.value) : this.value;
223
- return (h("limel-date-picker", { label: this.label, value: dateValue, onChange: this.handleDateChange }));
224
- }
225
- renderTimeInput() {
226
- return (h("limel-input-field", { label: this.label, type: "time", value: this.value || '', onChange: this.handleTextChange }));
336
+ const pickerType = mapPropertyTypeToPickerType(type);
337
+ return (h("limel-date-picker", { label: this.label, value: dateValue, type: pickerType, onChange: this.handleDateChange }));
227
338
  }
228
339
  renderMultiValueInput() {
229
340
  // For IN operator, allow comma-separated values
@@ -39,11 +39,9 @@ const LimeQueryBuilder = /*@__PURE__*/ proxyCustomElement(class LimeQueryBuilder
39
39
  this.limetype = event.detail;
40
40
  // Reset filter when limetype changes
41
41
  this.filter = undefined;
42
- // Reset response format when limetype changes
42
+ // Reset response format when limetype changes - empty state
43
43
  this.internalResponseFormat = {
44
- object: {
45
- _id: null,
46
- },
44
+ object: {},
47
45
  };
48
46
  this.emitChange();
49
47
  };
@@ -196,46 +196,29 @@ const ResponseFormatEditor = /*@__PURE__*/ proxyCustomElement(class ResponseForm
196
196
  };
197
197
  }
198
198
  componentWillLoad() {
199
- var _a;
200
- // Check if value is truly empty ({}) - no object or aggregates
201
- const isTrulyEmpty = this.value &&
202
- Object.keys(this.value).length === 0 &&
203
- !this.value.object &&
204
- !this.value.aggregates;
205
- if (isTrulyEmpty) {
206
- // Keep items empty for truly empty objects
199
+ if (!this.value) {
200
+ // No value provided at all, use default _id
201
+ this.items = [{ path: '_id' }];
202
+ return;
203
+ }
204
+ if (this.isEmptyResponseFormat(this.value)) {
205
+ // Show empty state for explicit empty objects
207
206
  this.items = [];
208
207
  }
209
- else if ((_a = this.value) === null || _a === void 0 ? void 0 : _a.object) {
210
- const converted = propertySelectionToItems(this.value.object);
211
- if (converted.length > 0) {
212
- this.items = converted;
213
- }
214
- else {
215
- // Empty object property, but not a truly empty value
216
- // Use default _id for backward compatibility
217
- this.items = [{ path: '_id' }];
218
- }
208
+ else if (this.value.object) {
209
+ // Has object property with actual properties
210
+ this.items = propertySelectionToItems(this.value.object);
219
211
  }
220
- else if (!this.value) {
221
- // No value provided at all, use default _id
222
- this.items = [{ path: '_id' }];
212
+ else {
213
+ // If value has aggregates but no object property, initialize items to empty array
214
+ this.items = [];
223
215
  }
224
216
  }
225
217
  componentWillUpdate() {
226
- var _a;
227
- // Check if value is truly empty ({})
228
- const isTrulyEmpty = this.value &&
229
- Object.keys(this.value).length === 0 &&
230
- !this.value.object &&
231
- !this.value.aggregates;
232
- if (isTrulyEmpty) {
233
- // Keep items empty for truly empty objects
234
- if (this.items.length > 0) {
235
- this.items = [];
236
- }
218
+ if (!this.value) {
219
+ return;
237
220
  }
238
- else if ((_a = this.value) === null || _a === void 0 ? void 0 : _a.object) {
221
+ if (this.value.object && !this.isEmptyResponseFormat(this.value)) {
239
222
  const currentItems = propertySelectionToItems(this.value.object);
240
223
  // Check if items have changed
241
224
  const itemsChanged = currentItems.length !== this.items.length ||
@@ -247,10 +230,15 @@ const ResponseFormatEditor = /*@__PURE__*/ proxyCustomElement(class ResponseForm
247
230
  item.description === current.description);
248
231
  });
249
232
  if (itemsChanged) {
250
- // Allow empty items array - don't force _id
251
233
  this.items = currentItems;
252
234
  }
253
235
  }
236
+ else {
237
+ // Either empty response format or no object property - clear items
238
+ if (this.items.length > 0) {
239
+ this.items = [];
240
+ }
241
+ }
254
242
  }
255
243
  render() {
256
244
  if (!this.limetype) {
@@ -281,6 +269,32 @@ const ResponseFormatEditor = /*@__PURE__*/ proxyCustomElement(class ResponseForm
281
269
  };
282
270
  this.change.emit(responseFormat);
283
271
  }
272
+ /**
273
+ * Check if the response format is empty
274
+ *
275
+ * A response format is considered empty in the following cases:
276
+ * - Empty object: `{}`
277
+ * - Object with empty object property and no aggregates: `{ object: {} }`
278
+ *
279
+ * Returns false for:
280
+ * - `null` or `undefined`
281
+ * - Objects with properties: `{ object: { _id: null } }`
282
+ * - Objects with aggregates: `{ aggregates: {...} }`
283
+ *
284
+ * @param value - The ResponseFormat to check
285
+ * @returns True if the response format is empty, false otherwise
286
+ */
287
+ isEmptyResponseFormat(value) {
288
+ if (!value) {
289
+ return false;
290
+ }
291
+ if (Object.keys(value).length === 0) {
292
+ return true;
293
+ }
294
+ return !!(value.object &&
295
+ Object.keys(value.object).length === 0 &&
296
+ !value.aggregates);
297
+ }
284
298
  static get style() { return LimebbLimeQueryResponseFormatEditorStyle0; }
285
299
  }, [1, "limebb-lime-query-response-format-editor", {
286
300
  "platform": [16],
@@ -28,11 +28,9 @@ const LimeQueryBuilder = class {
28
28
  this.limetype = event.detail;
29
29
  // Reset filter when limetype changes
30
30
  this.filter = undefined;
31
- // Reset response format when limetype changes
31
+ // Reset response format when limetype changes - empty state
32
32
  this.internalResponseFormat = {
33
- object: {
34
- _id: null,
35
- },
33
+ object: {},
36
34
  };
37
35
  this.emitChange();
38
36
  };
@@ -242,6 +242,115 @@ const LimeQueryFilterNotComponent = class {
242
242
  };
243
243
  LimeQueryFilterNotComponent.style = LimebbLimeQueryFilterNotStyle0;
244
244
 
245
+ /**
246
+ * System property name to actual type mapping.
247
+ *
248
+ * Maps system property names (with or without underscore prefix) to their actual data types.
249
+ * This mapping is necessary because system properties have `type='system'` in the metadata
250
+ * (for historical reasons - database schema fieldtype 255), but we need to know their
251
+ * actual data types to render appropriate inputs.
252
+ */
253
+ const SYSTEM_TYPE_MAP = {
254
+ timestamp: 'time',
255
+ createdtime: 'time',
256
+ updatedtime: 'time',
257
+ id: 'integer',
258
+ createduser: 'integer',
259
+ updateduser: 'integer',
260
+ descriptive: 'string',
261
+ };
262
+ /**
263
+ * Lime CRM date/time property type to Lime Elements date picker type mapping.
264
+ *
265
+ * Maps Lime CRM property types to the corresponding Lime Elements date picker types,
266
+ * as they use different naming conventions.
267
+ */
268
+ const DATE_TIME_TYPE_MAP = {
269
+ date: 'date',
270
+ time: 'datetime',
271
+ timeofday: 'time',
272
+ month: 'month',
273
+ quarter: 'quarter',
274
+ year: 'year',
275
+ };
276
+ /**
277
+ * Get the actual type of a property, resolving 'system' type to the underlying type.
278
+ *
279
+ * System properties in Lime CRM have `type: 'system'` in the metadata (a categorical marker
280
+ * from the database schema fieldtype 255), but we need to know their actual data types
281
+ * to render appropriate input controls.
282
+ *
283
+ * @param property - The property to get the type for
284
+ * @param property.type
285
+ * @param property.name
286
+ * @returns The actual property type (resolves 'system' to the underlying type)
287
+ *
288
+ * @example
289
+ * ```typescript
290
+ * const property = { name: '_timestamp', type: 'system' };
291
+ * getActualPropertyType(property); // Returns 'time'
292
+ * ```
293
+ */
294
+ function getActualPropertyType(property) {
295
+ if (property.type === 'system') {
296
+ return resolveSystemPropertyType(property.name);
297
+ }
298
+ return property.type;
299
+ }
300
+ /**
301
+ * Map system property names to their actual data types.
302
+ *
303
+ * This mapping is necessary because system properties have `type='system'` in the metadata
304
+ * (for historical reasons - database schema fieldtype 255), but we need to know their
305
+ * actual data types to render appropriate inputs.
306
+ *
307
+ * The mapping is based on the Lime CRM database schema:
308
+ * - System datetime fields: `timestamp`, `createdtime`, `updatedtime`
309
+ * - System integer fields: `id`, `createduser`, `updateduser`
310
+ * - System string fields: `descriptive`
311
+ *
312
+ * @param propertyName - The system property name (with or without underscore prefix)
313
+ * @returns The actual property type
314
+ *
315
+ * @example
316
+ * ```typescript
317
+ * resolveSystemPropertyType('_timestamp'); // Returns 'time'
318
+ * resolveSystemPropertyType('timestamp'); // Returns 'time'
319
+ * resolveSystemPropertyType('_id'); // Returns 'integer'
320
+ * resolveSystemPropertyType('unknown'); // Returns 'string' (default)
321
+ * ```
322
+ */
323
+ function resolveSystemPropertyType(propertyName) {
324
+ const name = propertyName.replace(/^_/, '');
325
+ return SYSTEM_TYPE_MAP[name] || 'string';
326
+ }
327
+ /**
328
+ * Map Lime CRM property types to Lime Elements date picker types.
329
+ *
330
+ * Lime CRM and Lime Elements use different naming conventions for date/time types,
331
+ * so we need to translate between them:
332
+ *
333
+ * - `'date'` → `'date'` - Date only
334
+ * - `'time'` → `'datetime'` - Full datetime (date + time) in Lime CRM
335
+ * - `'timeofday'` → `'time'` - Time only (hh:mm)
336
+ * - `'month'` → `'month'` - Month picker (YYYY-MM)
337
+ * - `'quarter'` → `'quarter'` - Quarter picker
338
+ * - `'year'` → `'year'` - Year picker
339
+ *
340
+ * @param propertyType - The Lime CRM date/time property type
341
+ * @returns The corresponding Lime Elements date picker type
342
+ *
343
+ * @example
344
+ * ```typescript
345
+ * mapPropertyTypeToPickerType('time'); // Returns 'datetime'
346
+ * mapPropertyTypeToPickerType('timeofday'); // Returns 'time'
347
+ * mapPropertyTypeToPickerType('date'); // Returns 'date'
348
+ * ```
349
+ */
350
+ function mapPropertyTypeToPickerType(propertyType) {
351
+ return DATE_TIME_TYPE_MAP[propertyType] || 'date';
352
+ }
353
+
245
354
  const limeQueryValueInputCss = ":host{display:flex;gap:0.5rem}:host>*{flex-grow:1}*{box-sizing:border-box}.value-input-container{display:flex;flex-direction:row;align-items:flex-start;gap:0.5rem;width:100%}.mode-toggle{flex-shrink:0;flex-grow:0}.placeholder-input{flex-grow:1;display:flex;flex-direction:column;gap:0.5rem}.placeholder-preview{display:flex;align-items:center;gap:0.5rem;padding:0.5rem;background-color:rgba(var(--color-blue-light), 0.1);border-radius:var(--border-radius-small);font-size:0.875rem;color:rgb(var(--color-blue-default));border-left:3px solid rgb(var(--color-blue-default))}.placeholder-preview limel-icon{flex-shrink:0;color:rgb(var(--color-blue-default))}.placeholder-preview span{font-family:var(--font-monospace);word-break:break-all}";
246
355
  const LimebbLimeQueryValueInputStyle0 = limeQueryValueInputCss;
247
356
 
@@ -404,10 +513,12 @@ const LimeQueryValueInput = class {
404
513
  if (!property) {
405
514
  return this.renderTextInput();
406
515
  }
407
- switch (property.type) {
516
+ // Resolve 'system' type to actual type
517
+ const actualType = getActualPropertyType(property);
518
+ switch (actualType) {
408
519
  case 'integer':
409
520
  case 'decimal': {
410
- return this.renderNumberInput(property.type);
521
+ return this.renderNumberInput(actualType);
411
522
  }
412
523
  case 'yesno': {
413
524
  return this.renderBooleanInput();
@@ -415,11 +526,13 @@ const LimeQueryValueInput = class {
415
526
  case 'option': {
416
527
  return this.renderOptionInput(property);
417
528
  }
418
- case 'date': {
419
- return this.renderDateInput();
420
- }
421
- case 'time': {
422
- return this.renderTimeInput();
529
+ case 'date':
530
+ case 'time':
531
+ case 'timeofday':
532
+ case 'year':
533
+ case 'month':
534
+ case 'quarter': {
535
+ return this.renderDateTimeInput(actualType);
423
536
  }
424
537
  default: {
425
538
  return this.renderTextInput();
@@ -455,13 +568,11 @@ const LimeQueryValueInput = class {
455
568
  const selectedOption = options.find((o) => o.value === this.value);
456
569
  return (h("limel-select", { label: this.label, options: options, value: selectedOption, onChange: this.handleSelectChange }));
457
570
  }
458
- renderDateInput() {
571
+ renderDateTimeInput(type) {
459
572
  // Convert string to Date if needed
460
573
  const dateValue = typeof this.value === 'string' ? new Date(this.value) : this.value;
461
- return (h("limel-date-picker", { label: this.label, value: dateValue, onChange: this.handleDateChange }));
462
- }
463
- renderTimeInput() {
464
- return (h("limel-input-field", { label: this.label, type: "time", value: this.value || '', onChange: this.handleTextChange }));
574
+ const pickerType = mapPropertyTypeToPickerType(type);
575
+ return (h("limel-date-picker", { label: this.label, value: dateValue, type: pickerType, onChange: this.handleDateChange }));
465
576
  }
466
577
  renderMultiValueInput() {
467
578
  // For IN operator, allow comma-separated values
@@ -192,46 +192,29 @@ const ResponseFormatEditor = class {
192
192
  };
193
193
  }
194
194
  componentWillLoad() {
195
- var _a;
196
- // Check if value is truly empty ({}) - no object or aggregates
197
- const isTrulyEmpty = this.value &&
198
- Object.keys(this.value).length === 0 &&
199
- !this.value.object &&
200
- !this.value.aggregates;
201
- if (isTrulyEmpty) {
202
- // Keep items empty for truly empty objects
195
+ if (!this.value) {
196
+ // No value provided at all, use default _id
197
+ this.items = [{ path: '_id' }];
198
+ return;
199
+ }
200
+ if (this.isEmptyResponseFormat(this.value)) {
201
+ // Show empty state for explicit empty objects
203
202
  this.items = [];
204
203
  }
205
- else if ((_a = this.value) === null || _a === void 0 ? void 0 : _a.object) {
206
- const converted = propertySelectionToItems(this.value.object);
207
- if (converted.length > 0) {
208
- this.items = converted;
209
- }
210
- else {
211
- // Empty object property, but not a truly empty value
212
- // Use default _id for backward compatibility
213
- this.items = [{ path: '_id' }];
214
- }
204
+ else if (this.value.object) {
205
+ // Has object property with actual properties
206
+ this.items = propertySelectionToItems(this.value.object);
215
207
  }
216
- else if (!this.value) {
217
- // No value provided at all, use default _id
218
- this.items = [{ path: '_id' }];
208
+ else {
209
+ // If value has aggregates but no object property, initialize items to empty array
210
+ this.items = [];
219
211
  }
220
212
  }
221
213
  componentWillUpdate() {
222
- var _a;
223
- // Check if value is truly empty ({})
224
- const isTrulyEmpty = this.value &&
225
- Object.keys(this.value).length === 0 &&
226
- !this.value.object &&
227
- !this.value.aggregates;
228
- if (isTrulyEmpty) {
229
- // Keep items empty for truly empty objects
230
- if (this.items.length > 0) {
231
- this.items = [];
232
- }
214
+ if (!this.value) {
215
+ return;
233
216
  }
234
- else if ((_a = this.value) === null || _a === void 0 ? void 0 : _a.object) {
217
+ if (this.value.object && !this.isEmptyResponseFormat(this.value)) {
235
218
  const currentItems = propertySelectionToItems(this.value.object);
236
219
  // Check if items have changed
237
220
  const itemsChanged = currentItems.length !== this.items.length ||
@@ -243,10 +226,15 @@ const ResponseFormatEditor = class {
243
226
  item.description === current.description);
244
227
  });
245
228
  if (itemsChanged) {
246
- // Allow empty items array - don't force _id
247
229
  this.items = currentItems;
248
230
  }
249
231
  }
232
+ else {
233
+ // Either empty response format or no object property - clear items
234
+ if (this.items.length > 0) {
235
+ this.items = [];
236
+ }
237
+ }
250
238
  }
251
239
  render() {
252
240
  if (!this.limetype) {
@@ -277,6 +265,32 @@ const ResponseFormatEditor = class {
277
265
  };
278
266
  this.change.emit(responseFormat);
279
267
  }
268
+ /**
269
+ * Check if the response format is empty
270
+ *
271
+ * A response format is considered empty in the following cases:
272
+ * - Empty object: `{}`
273
+ * - Object with empty object property and no aggregates: `{ object: {} }`
274
+ *
275
+ * Returns false for:
276
+ * - `null` or `undefined`
277
+ * - Objects with properties: `{ object: { _id: null } }`
278
+ * - Objects with aggregates: `{ aggregates: {...} }`
279
+ *
280
+ * @param value - The ResponseFormat to check
281
+ * @returns True if the response format is empty, false otherwise
282
+ */
283
+ isEmptyResponseFormat(value) {
284
+ if (!value) {
285
+ return false;
286
+ }
287
+ if (Object.keys(value).length === 0) {
288
+ return true;
289
+ }
290
+ return !!(value.object &&
291
+ Object.keys(value.object).length === 0 &&
292
+ !value.aggregates);
293
+ }
280
294
  };
281
295
  ResponseFormatEditor.style = LimebbLimeQueryResponseFormatEditorStyle0;
282
296