@limetech/lime-crm-building-blocks 1.105.0 → 1.105.2

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 (68) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/cjs/lime-crm-building-blocks.cjs.js +1 -1
  3. package/dist/cjs/{lime-query-validation-82aa2855.js → lime-query-validation-6d419d03.js} +78 -16
  4. package/dist/cjs/limebb-document-item.cjs.entry.js +29 -8
  5. package/dist/cjs/limebb-document-picker.cjs.entry.js +3 -3
  6. package/dist/cjs/limebb-lime-query-builder.cjs.entry.js +2 -2
  7. package/dist/cjs/limebb-lime-query-filter-group_3.cjs.entry.js +1 -1
  8. package/dist/cjs/limebb-lime-query-response-format-builder.cjs.entry.js +2 -2
  9. package/dist/cjs/limebb-property-selector.cjs.entry.js +1 -1
  10. package/dist/cjs/loader.cjs.js +1 -1
  11. package/dist/cjs/{property-resolution-fb42a46b.js → property-resolution-5f798b03.js} +47 -0
  12. package/dist/collection/components/chat-list/chat-item/chat-item.js +2 -2
  13. package/dist/collection/components/chat-list/chat-list.js +2 -2
  14. package/dist/collection/components/component-command-picker/component-config/component-config.js +2 -2
  15. package/dist/collection/components/component-command-picker/component-picker/component-picker.js +2 -2
  16. package/dist/collection/components/date-picker/date-picker.js +2 -2
  17. package/dist/collection/components/date-range/date-range.js +2 -2
  18. package/dist/collection/components/document-picker/document-item/document-item.js +55 -10
  19. package/dist/collection/components/document-picker/document-picker.js +5 -5
  20. package/dist/collection/components/feed/feed-item/feed-timeline-item.js +2 -2
  21. package/dist/collection/components/feed/feed.js +2 -2
  22. package/dist/collection/components/info-tile/info-tile.js +2 -2
  23. package/dist/collection/components/kanban/kanban-group/kanban-group.js +2 -2
  24. package/dist/collection/components/kanban/kanban-item/kanban-item.js +2 -2
  25. package/dist/collection/components/kanban/kanban.js +2 -2
  26. package/dist/collection/components/lime-query-builder/lime-query-validation.js +78 -17
  27. package/dist/collection/components/lime-query-builder/limetype-field/limetype-field.js +2 -2
  28. package/dist/collection/components/lime-query-builder/property-resolution.js +46 -0
  29. package/dist/collection/components/limeobject/file-viewer/file-viewer.js +2 -2
  30. package/dist/collection/components/loader/loader.js +2 -2
  31. package/dist/collection/components/locale-picker/locale-picker.js +2 -2
  32. package/dist/collection/components/notification-list/notification-item/notification-item.js +2 -2
  33. package/dist/collection/components/notification-list/notification-list.js +2 -2
  34. package/dist/collection/components/percentage-visualizer/percentage-visualizer.js +2 -2
  35. package/dist/collection/components/trend-indicator/trend-indicator.js +2 -2
  36. package/dist/components/document-item.js +31 -9
  37. package/dist/components/lime-query-validation.js +78 -16
  38. package/dist/components/limebb-document-picker.js +3 -3
  39. package/dist/components/property-selector.js +47 -1
  40. package/dist/esm/lime-crm-building-blocks.js +1 -1
  41. package/dist/esm/{lime-query-validation-9e386da8.js → lime-query-validation-237ee440.js} +78 -16
  42. package/dist/esm/limebb-document-item.entry.js +29 -8
  43. package/dist/esm/limebb-document-picker.entry.js +3 -3
  44. package/dist/esm/limebb-lime-query-builder.entry.js +2 -2
  45. package/dist/esm/limebb-lime-query-filter-group_3.entry.js +1 -1
  46. package/dist/esm/limebb-lime-query-response-format-builder.entry.js +2 -2
  47. package/dist/esm/limebb-property-selector.entry.js +1 -1
  48. package/dist/esm/loader.js +1 -1
  49. package/dist/esm/{property-resolution-c21a1369.js → property-resolution-e4e8dcf7.js} +47 -1
  50. package/dist/lime-crm-building-blocks/lime-crm-building-blocks.esm.js +1 -1
  51. package/dist/lime-crm-building-blocks/{p-ac9e81c9.entry.js → p-09ce8be4.entry.js} +1 -1
  52. package/dist/lime-crm-building-blocks/p-11aa4103.js +1 -0
  53. package/dist/lime-crm-building-blocks/p-2673c79e.entry.js +1 -0
  54. package/dist/lime-crm-building-blocks/p-80b9d946.entry.js +1 -0
  55. package/dist/lime-crm-building-blocks/{p-d8696b23.entry.js → p-9c2062bc.entry.js} +1 -1
  56. package/dist/lime-crm-building-blocks/p-b02c99d5.js +1 -0
  57. package/dist/lime-crm-building-blocks/{p-908dd7d5.entry.js → p-ee0e42dd.entry.js} +1 -1
  58. package/dist/lime-crm-building-blocks/{p-1421e1f8.entry.js → p-f7ea292d.entry.js} +1 -1
  59. package/dist/types/components/document-picker/document-item/document-item.d.ts +8 -1
  60. package/dist/types/components/document-picker/document-item/document-item.types.d.ts +3 -15
  61. package/dist/types/components/lime-query-builder/lime-query-validation.d.ts +14 -0
  62. package/dist/types/components/lime-query-builder/property-resolution.d.ts +12 -0
  63. package/dist/types/components.d.ts +96 -88
  64. package/package.json +4 -4
  65. package/dist/lime-crm-building-blocks/p-876701c6.entry.js +0 -1
  66. package/dist/lime-crm-building-blocks/p-9d25ed5a.entry.js +0 -1
  67. package/dist/lime-crm-building-blocks/p-b748c770.js +0 -1
  68. package/dist/lime-crm-building-blocks/p-efa5bcd4.js +0 -1
@@ -154,7 +154,7 @@ export class ComponentConfig {
154
154
  "optional": false,
155
155
  "docs": {
156
156
  "tags": [],
157
- "text": "Reference to the platform"
157
+ "text": "Reference to the platform service container.\n\nUse this to access all platform services like repositories, HTTP client,\ncommand bus, navigator, and more. The platform instance is shared across\nall components in the same application context."
158
158
  },
159
159
  "getter": false,
160
160
  "setter": false
@@ -177,7 +177,7 @@ export class ComponentConfig {
177
177
  "optional": false,
178
178
  "docs": {
179
179
  "tags": [],
180
- "text": "The context this component belongs to"
180
+ "text": "The context describing where this component is running.\n\nThe context provides information about the current limetype and record ID\n(if viewing/editing a specific record). Components can use this to load\nrelevant data and understand their operating environment."
181
181
  },
182
182
  "getter": false,
183
183
  "setter": false
@@ -174,7 +174,7 @@ export class ComponentPicker {
174
174
  "optional": false,
175
175
  "docs": {
176
176
  "tags": [],
177
- "text": "Reference to the platform"
177
+ "text": "Reference to the platform service container.\n\nUse this to access all platform services like repositories, HTTP client,\ncommand bus, navigator, and more. The platform instance is shared across\nall components in the same application context."
178
178
  },
179
179
  "getter": false,
180
180
  "setter": false
@@ -197,7 +197,7 @@ export class ComponentPicker {
197
197
  "optional": false,
198
198
  "docs": {
199
199
  "tags": [],
200
- "text": "The context this component belongs to"
200
+ "text": "The context describing where this component is running.\n\nThe context provides information about the current limetype and record ID\n(if viewing/editing a specific record). Components can use this to load\nrelevant data and understand their operating environment."
201
201
  },
202
202
  "getter": false,
203
203
  "setter": false
@@ -118,7 +118,7 @@ export class DatePicker {
118
118
  "name": "inheritdoc",
119
119
  "text": undefined
120
120
  }],
121
- "text": "Reference to the platform"
121
+ "text": "Reference to the platform service container.\n\nUse this to access all platform services like repositories, HTTP client,\ncommand bus, navigator, and more. The platform instance is shared across\nall components in the same application context."
122
122
  },
123
123
  "getter": false,
124
124
  "setter": false
@@ -144,7 +144,7 @@ export class DatePicker {
144
144
  "name": "inheritdoc",
145
145
  "text": undefined
146
146
  }],
147
- "text": "The context this component belongs to"
147
+ "text": "The context describing where this component is running.\n\nThe context provides information about the current limetype and record ID\n(if viewing/editing a specific record). Components can use this to load\nrelevant data and understand their operating environment."
148
148
  },
149
149
  "getter": false,
150
150
  "setter": false
@@ -89,7 +89,7 @@ export class DateRange {
89
89
  "name": "inheritdoc",
90
90
  "text": undefined
91
91
  }],
92
- "text": "Reference to the platform"
92
+ "text": "Reference to the platform service container.\n\nUse this to access all platform services like repositories, HTTP client,\ncommand bus, navigator, and more. The platform instance is shared across\nall components in the same application context."
93
93
  },
94
94
  "getter": false,
95
95
  "setter": false
@@ -115,7 +115,7 @@ export class DateRange {
115
115
  "name": "inheritdoc",
116
116
  "text": undefined
117
117
  }],
118
- "text": "The context this component belongs to"
118
+ "text": "The context describing where this component is running.\n\nThe context provides information about the current limetype and record ID\n(if viewing/editing a specific record). Components can use this to load\nrelevant data and understand their operating environment."
119
119
  },
120
120
  "getter": false,
121
121
  "setter": false
@@ -13,6 +13,11 @@ export class DocumentItemComponent {
13
13
  * - 'checkbox': renders a checkbox for multiple selection.
14
14
  */
15
15
  this.type = 'checkbox';
16
+ /**
17
+ * The preferred file URL types to use when displaying
18
+ * the document's image preview.
19
+ */
20
+ this.fileTypes = ['view', 'contents', 'download'];
16
21
  this.handleDocumentItemClick = (event) => {
17
22
  if (this.isUnavailable()) {
18
23
  return;
@@ -72,21 +77,22 @@ export class DocumentItemComponent {
72
77
  }
73
78
  render() {
74
79
  const isUnavailable = this.isUnavailable();
75
- return (h(Host, { key: '625f38ae7e31ae2b900366df8ad60ff2099a2ce6', id: this.item.id.toString(), class: {
76
- 'has-image': !!this.item.thumbnail,
80
+ const image = this.getImage();
81
+ return (h(Host, { key: '7fc37bbc0185cdb38c837f2487c88f4d275d1c2f', id: String(this.item.file.id), class: {
82
+ 'has-image': !!image,
77
83
  'has-error': !!this.item.hasError,
78
- }, role: this.type === 'radio' ? 'radio' : 'checkbox', "aria-checked": this.item.selected ? 'true' : 'false', "aria-disabled": isUnavailable ? 'true' : 'false', tabIndex: isUnavailable ? -1 : 0 }, h("limel-card", { key: 'b88db5b44b9dfcd858f1e395d0837623dfe9b5a5', image: this.item.thumbnail, clickable: isUnavailable ? false : true, onClick: this.handleDocumentItemClick, onKeyDown: this.handleKeyDown }, h("div", { key: '558028048aba88caf524373cbeb7c14ebc371bdb', slot: "component" }, this.renderFileSize(), this.renderBooleanInput())), this.renderHelp()));
84
+ }, role: this.type === 'radio' ? 'radio' : 'checkbox', "aria-checked": this.item.selected ? 'true' : 'false', "aria-disabled": isUnavailable ? 'true' : 'false', tabIndex: isUnavailable ? -1 : 0 }, h("limel-card", { key: 'da1329af4f1eac4617da054b5b02715b512631ff', image: image, clickable: isUnavailable ? false : true, onClick: this.handleDocumentItemClick, onKeyDown: this.handleKeyDown }, h("div", { key: 'a30361cfb76512065fe3cf1b513f31257cf115ee', slot: "component" }, this.renderFileSize(), this.renderBooleanInput())), this.renderHelp()));
79
85
  }
80
86
  renderBooleanInput() {
81
87
  const id = this.getControlId();
82
88
  if (this.type === 'radio') {
83
- return (h("limel-radio-button", { id: id, label: this.item.fileName, disabled: this.isUnavailable(), checked: this.item.selected, onClick: this.handleSelectionControlClick, onChange: this.handleSelectionControlChange }));
89
+ return (h("limel-radio-button", { id: id, label: this.item.file.filename, disabled: this.isUnavailable(), checked: this.item.selected, onClick: this.handleSelectionControlClick, onChange: this.handleSelectionControlChange }));
84
90
  }
85
- return (h("limel-checkbox", { id: id, label: this.item.fileName, disabled: this.isUnavailable(), checked: this.item.selected, onClick: this.handleSelectionControlClick, onChange: this.handleSelectionControlChange }));
91
+ return (h("limel-checkbox", { id: id, label: this.item.file.filename, disabled: this.isUnavailable(), checked: this.item.selected, onClick: this.handleSelectionControlClick, onChange: this.handleSelectionControlChange }));
86
92
  }
87
93
  renderFileSize() {
88
- var _a;
89
- const formattedFileSize = formatBytes((_a = this.item) === null || _a === void 0 ? void 0 : _a.fileSize);
94
+ var _a, _b;
95
+ const formattedFileSize = formatBytes((_b = (_a = this.item) === null || _a === void 0 ? void 0 : _a.file) === null || _b === void 0 ? void 0 : _b.size);
90
96
  if (!formattedFileSize) {
91
97
  return;
92
98
  }
@@ -112,7 +118,7 @@ export class DocumentItemComponent {
112
118
  this.interact.emit(Object.assign(Object.assign({}, this.item), { selected }));
113
119
  }
114
120
  getControlId() {
115
- return `boolean-input-${String(this.item.id)}`;
121
+ return `boolean-input-${String(this.item.file.id)}`;
116
122
  }
117
123
  isUnavailable() {
118
124
  var _a, _b;
@@ -126,6 +132,21 @@ export class DocumentItemComponent {
126
132
  input.checked = false;
127
133
  }
128
134
  }
135
+ getImage() {
136
+ const href = this.hrefForFile();
137
+ if (!href) {
138
+ return;
139
+ }
140
+ return {
141
+ src: href,
142
+ alt: this.item.file.filename,
143
+ };
144
+ }
145
+ hrefForFile() {
146
+ return ['preview', ...this.fileTypes]
147
+ .map((type) => this.item.file.getUrl(type))
148
+ .find(Boolean);
149
+ }
129
150
  static get is() { return "limebb-document-item"; }
130
151
  static get encapsulation() { return "shadow"; }
131
152
  static get delegatesFocus() { return true; }
@@ -162,7 +183,7 @@ export class DocumentItemComponent {
162
183
  "name": "inheritdoc",
163
184
  "text": undefined
164
185
  }],
165
- "text": "Reference to the platform"
186
+ "text": "Reference to the platform service container.\n\nUse this to access all platform services like repositories, HTTP client,\ncommand bus, navigator, and more. The platform instance is shared across\nall components in the same application context."
166
187
  },
167
188
  "getter": false,
168
189
  "setter": false
@@ -188,7 +209,7 @@ export class DocumentItemComponent {
188
209
  "name": "inheritdoc",
189
210
  "text": undefined
190
211
  }],
191
- "text": "The context this component belongs to"
212
+ "text": "The context describing where this component is running.\n\nThe context provides information about the current limetype and record ID\n(if viewing/editing a specific record). Components can use this to load\nrelevant data and understand their operating environment."
192
213
  },
193
214
  "getter": false,
194
215
  "setter": false
@@ -235,6 +256,30 @@ export class DocumentItemComponent {
235
256
  "attribute": "type",
236
257
  "reflect": true,
237
258
  "defaultValue": "'checkbox'"
259
+ },
260
+ "fileTypes": {
261
+ "type": "unknown",
262
+ "mutable": false,
263
+ "complexType": {
264
+ "original": "LimeFileUrlType[]",
265
+ "resolved": "LimeFileUrlType[]",
266
+ "references": {
267
+ "LimeFileUrlType": {
268
+ "location": "import",
269
+ "path": "@limetech/lime-web-components",
270
+ "id": "node_modules::LimeFileUrlType"
271
+ }
272
+ }
273
+ },
274
+ "required": false,
275
+ "optional": false,
276
+ "docs": {
277
+ "tags": [],
278
+ "text": "The preferred file URL types to use when displaying\nthe document's image preview."
279
+ },
280
+ "getter": false,
281
+ "setter": false,
282
+ "defaultValue": "['view', 'contents', 'download']"
238
283
  }
239
284
  };
240
285
  }
@@ -47,7 +47,7 @@ export class DocumentPicker {
47
47
  // or allow deselecting if clicking the already selected item
48
48
  updatedItems = this.items
49
49
  .map((item) => {
50
- if (item.id === interactedItem.id) {
50
+ if (item.file.id === interactedItem.file.id) {
51
51
  return Object.assign(Object.assign({}, item), { selected: interactedItem.selected });
52
52
  }
53
53
  return Object.assign(Object.assign({}, item), { selected: false });
@@ -57,7 +57,7 @@ export class DocumentPicker {
57
57
  else {
58
58
  updatedItems = this.items
59
59
  .map((item) => {
60
- if (item.id === interactedItem.id) {
60
+ if (item.file.id === interactedItem.file.id) {
61
61
  return Object.assign(Object.assign({}, item), { selected: interactedItem.selected });
62
62
  }
63
63
  return item;
@@ -79,7 +79,7 @@ export class DocumentPicker {
79
79
  return (h(Host, { key: '5e4a9004ed3b81b6c35be868c664e61cb88d490c' }, h("limel-notched-outline", { key: 'ada8928b3ed815896e1ef5fb61f539f0ab77c7af', labelId: this.labelId, label: this.label, required: this.required, invalid: this.invalid, hasFloatingLabel: true }, h("div", { key: '343aeef460a4ea4e0a4a247daf6988f749dc2b49', slot: "content", role: this.type === 'radio' ? 'radiogroup' : 'group', "aria-labelledby": this.label ? this.labelId : undefined, "aria-describedby": this.helperText ? this.helperTextId : undefined }, this.renderItems())), this.renderHelperLine()));
80
80
  }
81
81
  renderItems() {
82
- return this.items.map((item) => (h("limebb-document-item", { platform: this.platform, context: this.context, item: item, key: item.id, type: this.type, onInteract: this.handleItemInteract })));
82
+ return this.items.map((item) => (h("limebb-document-item", { platform: this.platform, context: this.context, item: item, key: item.file.id, type: this.type, onInteract: this.handleItemInteract })));
83
83
  }
84
84
  static get is() { return "limebb-document-picker"; }
85
85
  static get encapsulation() { return "shadow"; }
@@ -116,7 +116,7 @@ export class DocumentPicker {
116
116
  "name": "inheritdoc",
117
117
  "text": undefined
118
118
  }],
119
- "text": "Reference to the platform"
119
+ "text": "Reference to the platform service container.\n\nUse this to access all platform services like repositories, HTTP client,\ncommand bus, navigator, and more. The platform instance is shared across\nall components in the same application context."
120
120
  },
121
121
  "getter": false,
122
122
  "setter": false
@@ -142,7 +142,7 @@ export class DocumentPicker {
142
142
  "name": "inheritdoc",
143
143
  "text": undefined
144
144
  }],
145
- "text": "The context this component belongs to"
145
+ "text": "The context describing where this component is running.\n\nThe context provides information about the current limetype and record ID\n(if viewing/editing a specific record). Components can use this to load\nrelevant data and understand their operating environment."
146
146
  },
147
147
  "getter": false,
148
148
  "setter": false
@@ -419,7 +419,7 @@ export class FeedTimelineItem {
419
419
  "name": "inheritdoc",
420
420
  "text": undefined
421
421
  }],
422
- "text": "Reference to the platform"
422
+ "text": "Reference to the platform service container.\n\nUse this to access all platform services like repositories, HTTP client,\ncommand bus, navigator, and more. The platform instance is shared across\nall components in the same application context."
423
423
  },
424
424
  "getter": false,
425
425
  "setter": false
@@ -445,7 +445,7 @@ export class FeedTimelineItem {
445
445
  "name": "inheritdoc",
446
446
  "text": undefined
447
447
  }],
448
- "text": "The context this component belongs to"
448
+ "text": "The context describing where this component is running.\n\nThe context provides information about the current limetype and record ID\n(if viewing/editing a specific record). Components can use this to load\nrelevant data and understand their operating environment."
449
449
  },
450
450
  "getter": false,
451
451
  "setter": false
@@ -227,7 +227,7 @@ export class Feed {
227
227
  "name": "inheritdoc",
228
228
  "text": undefined
229
229
  }],
230
- "text": "Reference to the platform"
230
+ "text": "Reference to the platform service container.\n\nUse this to access all platform services like repositories, HTTP client,\ncommand bus, navigator, and more. The platform instance is shared across\nall components in the same application context."
231
231
  },
232
232
  "getter": false,
233
233
  "setter": false
@@ -253,7 +253,7 @@ export class Feed {
253
253
  "name": "inheritdoc",
254
254
  "text": undefined
255
255
  }],
256
- "text": "The context this component belongs to"
256
+ "text": "The context describing where this component is running.\n\nThe context provides information about the current limetype and record ID\n(if viewing/editing a specific record). Components can use this to load\nrelevant data and understand their operating environment."
257
257
  },
258
258
  "getter": false,
259
259
  "setter": false
@@ -189,7 +189,7 @@ export class InfoTile {
189
189
  "optional": false,
190
190
  "docs": {
191
191
  "tags": [],
192
- "text": "Reference to the platform"
192
+ "text": "Reference to the platform service container.\n\nUse this to access all platform services like repositories, HTTP client,\ncommand bus, navigator, and more. The platform instance is shared across\nall components in the same application context."
193
193
  },
194
194
  "getter": false,
195
195
  "setter": false
@@ -212,7 +212,7 @@ export class InfoTile {
212
212
  "optional": false,
213
213
  "docs": {
214
214
  "tags": [],
215
- "text": "The context this component belongs to"
215
+ "text": "The context describing where this component is running.\n\nThe context provides information about the current limetype and record ID\n(if viewing/editing a specific record). Components can use this to load\nrelevant data and understand their operating environment."
216
216
  },
217
217
  "getter": false,
218
218
  "setter": false
@@ -127,7 +127,7 @@ export class KanbanGroup {
127
127
  "name": "inheritdoc",
128
128
  "text": undefined
129
129
  }],
130
- "text": "Reference to the platform"
130
+ "text": "Reference to the platform service container.\n\nUse this to access all platform services like repositories, HTTP client,\ncommand bus, navigator, and more. The platform instance is shared across\nall components in the same application context."
131
131
  },
132
132
  "getter": false,
133
133
  "setter": false
@@ -153,7 +153,7 @@ export class KanbanGroup {
153
153
  "name": "inheritdoc",
154
154
  "text": undefined
155
155
  }],
156
- "text": "The context this component belongs to"
156
+ "text": "The context describing where this component is running.\n\nThe context provides information about the current limetype and record ID\n(if viewing/editing a specific record). Components can use this to load\nrelevant data and understand their operating environment."
157
157
  },
158
158
  "getter": false,
159
159
  "setter": false
@@ -164,7 +164,7 @@ export class KanbanItemComponent {
164
164
  "name": "inheritdoc",
165
165
  "text": undefined
166
166
  }],
167
- "text": "Reference to the platform"
167
+ "text": "Reference to the platform service container.\n\nUse this to access all platform services like repositories, HTTP client,\ncommand bus, navigator, and more. The platform instance is shared across\nall components in the same application context."
168
168
  },
169
169
  "getter": false,
170
170
  "setter": false
@@ -190,7 +190,7 @@ export class KanbanItemComponent {
190
190
  "name": "inheritdoc",
191
191
  "text": undefined
192
192
  }],
193
- "text": "The context this component belongs to"
193
+ "text": "The context describing where this component is running.\n\nThe context provides information about the current limetype and record ID\n(if viewing/editing a specific record). Components can use this to load\nrelevant data and understand their operating environment."
194
194
  },
195
195
  "getter": false,
196
196
  "setter": false
@@ -72,7 +72,7 @@ export class Kanban {
72
72
  "name": "inheritdoc",
73
73
  "text": undefined
74
74
  }],
75
- "text": "Reference to the platform"
75
+ "text": "Reference to the platform service container.\n\nUse this to access all platform services like repositories, HTTP client,\ncommand bus, navigator, and more. The platform instance is shared across\nall components in the same application context."
76
76
  },
77
77
  "getter": false,
78
78
  "setter": false
@@ -98,7 +98,7 @@ export class Kanban {
98
98
  "name": "inheritdoc",
99
99
  "text": undefined
100
100
  }],
101
- "text": "The context this component belongs to"
101
+ "text": "The context describing where this component is running.\n\nThe context provides information about the current limetype and record ID\n(if viewing/editing a specific record). Components can use this to load\nrelevant data and understand their operating environment."
102
102
  },
103
103
  "getter": false,
104
104
  "setter": false
@@ -1,6 +1,5 @@
1
1
  import { Operator } from "@limetech/lime-web-components";
2
- import { getNormalizedProperties } from "./property-resolution";
3
- import { getPropertyFromPath } from "./property-resolution";
2
+ import { getNormalizedProperties, getPropertyFromPath, validatePropertyPath, } from "./property-resolution";
4
3
  /**
5
4
  * Dynamic filter values and placeholders that are valid in Lime Query
6
5
  */
@@ -85,6 +84,46 @@ export function validatePlaceholder(value, activeLimetype, limetypes) {
85
84
  };
86
85
  }
87
86
  }
87
+ /**
88
+ * Validates a filter expression key (property path).
89
+ * Supports both regular property paths and placeholders.
90
+ *
91
+ * @param key - Property path to validate (e.g., "name", "company.name", "%activeObject%.name")
92
+ * @param limetypes - All limetype definitions
93
+ * @param limetype - The limetype being filtered
94
+ * @param activeLimetype - Active limetype for placeholder resolution
95
+ * @returns Validation result with error message if invalid
96
+ */
97
+ export function validateFilterKey(key, limetypes, limetype, activeLimetype) {
98
+ // 1. Handle empty/missing keys
99
+ if (!key) {
100
+ return { valid: false, error: 'Filter key cannot be empty' };
101
+ }
102
+ // 2. Check if key is a placeholder
103
+ if (key.startsWith('%activeObject%')) {
104
+ const placeholderResult = validatePlaceholder(key, activeLimetype, limetypes);
105
+ if (!placeholderResult.valid) {
106
+ return placeholderResult;
107
+ }
108
+ // Extract property path after the placeholder and validate for hasMany/hasAndBelongsToMany
109
+ const propertyPath = key.replace(/^%activeObject%\.?/, '');
110
+ if (propertyPath && activeLimetype) {
111
+ const { error } = validatePropertyPath(limetypes, activeLimetype, propertyPath);
112
+ if (error) {
113
+ return { valid: false, error };
114
+ }
115
+ }
116
+ return placeholderResult;
117
+ }
118
+ // 3. Validate regular property path (including intermediate properties)
119
+ const { error } = validatePropertyPath(limetypes, limetype, key);
120
+ if (error) {
121
+ return { valid: false, error };
122
+ }
123
+ // validatePropertyPath always returns an error if property is undefined,
124
+ // so if we reach here, the property exists and is valid
125
+ return { valid: true };
126
+ }
88
127
  /**
89
128
  * Validate a response format against limetype schemas
90
129
  * Throws errors for invalid property references
@@ -253,13 +292,19 @@ export function validatePropertySelection(selection, limetypes, limetype, visual
253
292
  * @param filter
254
293
  * @param activeLimetype
255
294
  * @param limetypes
295
+ * @param limetype
256
296
  */
257
- function validateComparisonExpression(filter, activeLimetype, limetypes) {
297
+ function validateComparisonExpression(filter, activeLimetype, limetypes, limetype) {
258
298
  // Validate operator
259
299
  const allValidOperators = Object.values(Operator);
260
300
  if (!allValidOperators.includes(filter.op)) {
261
301
  throw new Error(`Unsupported filter operator: ${filter.op}`);
262
302
  }
303
+ // Validate filter key
304
+ const keyResult = validateFilterKey(filter.key, limetypes, limetype, activeLimetype);
305
+ if (!keyResult.valid) {
306
+ throw new Error(`Invalid filter key '${filter.key}': ${keyResult.error}`);
307
+ }
263
308
  // Validate placeholder
264
309
  const result = validatePlaceholder(filter.exp, activeLimetype, limetypes);
265
310
  if (!result.valid) {
@@ -271,9 +316,10 @@ function validateComparisonExpression(filter, activeLimetype, limetypes) {
271
316
  * @param filter
272
317
  * @param activeLimetype
273
318
  * @param limetypes
319
+ * @param limetype
274
320
  * @param visualModeEnabled
275
321
  */
276
- function validateGroupExpression(filter, activeLimetype, limetypes, visualModeEnabled) {
322
+ function validateGroupExpression(filter, activeLimetype, limetypes, limetype, visualModeEnabled) {
277
323
  // Validate operator
278
324
  if (filter.op !== Operator.AND &&
279
325
  filter.op !== Operator.OR &&
@@ -282,12 +328,12 @@ function validateGroupExpression(filter, activeLimetype, limetypes, visualModeEn
282
328
  }
283
329
  // Recursively validate children
284
330
  if (filter.op === Operator.NOT) {
285
- validateFilterPlaceholders(filter.exp, activeLimetype, limetypes, visualModeEnabled);
331
+ validateFilterPlaceholders(filter.exp, activeLimetype, limetypes, limetype, visualModeEnabled);
286
332
  }
287
333
  else if (filter.op === Operator.AND || filter.op === Operator.OR) {
288
334
  const expressions = filter.exp;
289
335
  for (const expr of expressions) {
290
- validateFilterPlaceholders(expr, activeLimetype, limetypes, visualModeEnabled);
336
+ validateFilterPlaceholders(expr, activeLimetype, limetypes, limetype, visualModeEnabled);
291
337
  }
292
338
  }
293
339
  }
@@ -296,37 +342,51 @@ function validateGroupExpression(filter, activeLimetype, limetypes, visualModeEn
296
342
  * @param filter Filter expression to validate
297
343
  * @param activeLimetype The limetype of the active object
298
344
  * @param limetypes Record of all available limetypes
345
+ * @param limetype The limetype being filtered
299
346
  * @param visualModeEnabled Whether visual mode is enabled (affects validation)
300
347
  */
301
- function validateFilterPlaceholders(filter, activeLimetype, limetypes, visualModeEnabled = true) {
348
+ function validateFilterPlaceholders(filter, activeLimetype, limetypes, limetype, visualModeEnabled = true) {
302
349
  if (!filter) {
303
350
  return;
304
351
  }
305
352
  if ('key' in filter) {
306
- validateComparisonExpression(filter, activeLimetype, limetypes);
353
+ validateComparisonExpression(filter, activeLimetype, limetypes, limetype);
307
354
  return;
308
355
  }
309
356
  if ('exp' in filter) {
310
- validateGroupExpression(filter, activeLimetype, limetypes, visualModeEnabled);
357
+ validateGroupExpression(filter, activeLimetype, limetypes, limetype, visualModeEnabled);
311
358
  }
312
359
  }
313
360
  /**
314
- * Validate Lime Query filter and collect errors
361
+ * Validate Lime Query filter and collect errors and visual mode limitations
315
362
  * @param filter The filter expression or group to validate
316
363
  * @param activeLimetype Optional active object limetype for placeholder validation
317
364
  * @param limetypes Record of all available limetypes
365
+ * @param limetype The limetype being filtered
318
366
  * @param visualModeEnabled Whether visual mode is enabled
319
- * @returns Array of validation error messages
367
+ * @returns Object with validation errors and visual mode limitations
320
368
  */
321
- function validateLimeQueryFilterInternal(filter, activeLimetype, limetypes, visualModeEnabled) {
369
+ function validateLimeQueryFilterInternal(filter, activeLimetype, limetypes, limetype, visualModeEnabled) {
322
370
  const errors = [];
371
+ const limitations = [];
323
372
  try {
324
- validateFilterPlaceholders(filter, activeLimetype, limetypes, visualModeEnabled);
373
+ validateFilterPlaceholders(filter, activeLimetype, limetypes, limetype, visualModeEnabled);
325
374
  }
326
375
  catch (error) {
327
- errors.push(`Invalid filter: ${error.message}`);
376
+ const errorMessage = error.message;
377
+ // Invalid keys are BOTH spec violations AND rendering limitations:
378
+ // - Backend will reject them (validation error)
379
+ // - Visual mode can't show them in property selector (visual limitation)
380
+ if (errorMessage.includes('Invalid filter key') ||
381
+ errorMessage.includes('Cannot filter on many-relation')) {
382
+ errors.push(`Invalid filter: ${errorMessage}`);
383
+ limitations.push(errorMessage);
384
+ }
385
+ else {
386
+ errors.push(`Invalid filter: ${errorMessage}`);
387
+ }
328
388
  }
329
- return errors;
389
+ return { errors, limitations };
330
390
  }
331
391
  /**
332
392
  * Validate orderBy specification
@@ -495,8 +555,9 @@ export function isLimeQuerySupported(limeQuery, limetypes, activeLimetype, visua
495
555
  }
496
556
  // Validate filter
497
557
  if (limeQuery.filter) {
498
- const filterErrors = validateLimeQueryFilterInternal(limeQuery.filter, activeLimetype, limetypes, visualModeEnabled);
499
- validationErrors.push(...filterErrors);
558
+ const { errors, limitations } = validateLimeQueryFilterInternal(limeQuery.filter, activeLimetype, limetypes, limeQuery.limetype, visualModeEnabled);
559
+ validationErrors.push(...errors);
560
+ visualModeLimitations.push(...limitations);
500
561
  }
501
562
  // Validate responseFormat
502
563
  if (limeQuery.responseFormat) {
@@ -133,7 +133,7 @@ export class LimetypeField {
133
133
  "name": "inheritdoc",
134
134
  "text": undefined
135
135
  }],
136
- "text": "Reference to the platform"
136
+ "text": "Reference to the platform service container.\n\nUse this to access all platform services like repositories, HTTP client,\ncommand bus, navigator, and more. The platform instance is shared across\nall components in the same application context."
137
137
  },
138
138
  "getter": false,
139
139
  "setter": false
@@ -159,7 +159,7 @@ export class LimetypeField {
159
159
  "name": "inheritdoc",
160
160
  "text": undefined
161
161
  }],
162
- "text": "The context this component belongs to"
162
+ "text": "The context describing where this component is running.\n\nThe context provides information about the current limetype and record ID\n(if viewing/editing a specific record). Components can use this to load\nrelevant data and understand their operating environment."
163
163
  },
164
164
  "getter": false,
165
165
  "setter": false
@@ -59,3 +59,49 @@ export function getPropertyFromPath(limetypes, limetype, path) {
59
59
  }
60
60
  return property;
61
61
  }
62
+ /**
63
+ * Validates a property path to ensure no intermediate properties are hasMany or hasAndBelongsToMany relations.
64
+ * These relation types cannot be traversed in filter expressions.
65
+ * @param limetypes All limetype definitions
66
+ * @param limetype The starting limetype
67
+ * @param path The property path to validate (e.g., "company.name")
68
+ * @returns The property at the end of the path if valid, or undefined with error if invalid
69
+ */
70
+ export function validatePropertyPath(limetypes, limetype, path) {
71
+ if (!path || !limetype || !limetypes) {
72
+ return { property: undefined };
73
+ }
74
+ const parts = path.split('.');
75
+ let currentType = limetypes[limetype];
76
+ let property;
77
+ for (let i = 0; i < parts.length; i++) {
78
+ const part = parts[i];
79
+ if (!currentType) {
80
+ return { property: undefined };
81
+ }
82
+ const normalizedProperties = getNormalizedProperties(currentType);
83
+ property = normalizedProperties[part];
84
+ if (!property) {
85
+ return {
86
+ property: undefined,
87
+ error: `Property '${part}' does not exist on limetype '${currentType.name}'`,
88
+ };
89
+ }
90
+ // Check if this property is a hasMany/hasAndBelongsToMany relation
91
+ // These cannot be traversed in filter expressions
92
+ if (property.type === 'hasmany' ||
93
+ property.type === 'hasandbelongstomany') {
94
+ // Build the path up to this point for the error message
95
+ const invalidPath = parts.slice(0, i + 1).join('.');
96
+ return {
97
+ property: undefined,
98
+ error: `Cannot filter on many-relation '${invalidPath}'. Use a related limetype's filter instead.`,
99
+ };
100
+ }
101
+ // If this is a relation, get the related limetype for next iteration
102
+ if (property.relation) {
103
+ currentType = property.relation.getLimetype();
104
+ }
105
+ }
106
+ return { property };
107
+ }
@@ -161,7 +161,7 @@ export class FileViewer {
161
161
  "optional": false,
162
162
  "docs": {
163
163
  "tags": [],
164
- "text": "Reference to the platform"
164
+ "text": "Reference to the platform service container.\n\nUse this to access all platform services like repositories, HTTP client,\ncommand bus, navigator, and more. The platform instance is shared across\nall components in the same application context."
165
165
  },
166
166
  "getter": false,
167
167
  "setter": false
@@ -184,7 +184,7 @@ export class FileViewer {
184
184
  "optional": false,
185
185
  "docs": {
186
186
  "tags": [],
187
- "text": "The context this component belongs to"
187
+ "text": "The context describing where this component is running.\n\nThe context provides information about the current limetype and record ID\n(if viewing/editing a specific record). Components can use this to load\nrelevant data and understand their operating environment."
188
188
  },
189
189
  "getter": false,
190
190
  "setter": false
@@ -39,7 +39,7 @@ export class CrmComponentsLoader {
39
39
  "name": "inheritdoc",
40
40
  "text": undefined
41
41
  }],
42
- "text": "Reference to the platform"
42
+ "text": "Reference to the platform service container.\n\nUse this to access all platform services like repositories, HTTP client,\ncommand bus, navigator, and more. The platform instance is shared across\nall components in the same application context."
43
43
  },
44
44
  "getter": false,
45
45
  "setter": false
@@ -65,7 +65,7 @@ export class CrmComponentsLoader {
65
65
  "name": "inheritdoc",
66
66
  "text": undefined
67
67
  }],
68
- "text": "The context this component belongs to"
68
+ "text": "The context describing where this component is running.\n\nThe context provides information about the current limetype and record ID\n(if viewing/editing a specific record). Components can use this to load\nrelevant data and understand their operating environment."
69
69
  },
70
70
  "getter": false,
71
71
  "setter": false