@limetech/lime-crm-building-blocks 1.100.0 → 1.101.0

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 (123) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/cjs/lime-crm-building-blocks.cjs.js +1 -1
  3. package/dist/cjs/limebb-lime-query-builder.cjs.entry.js +177 -204
  4. package/dist/cjs/{limebb-lime-query-filter-builder_3.cjs.entry.js → limebb-lime-query-filter-builder_4.cjs.entry.js} +86 -1
  5. package/dist/cjs/limebb-lime-query-filter-comparison_2.cjs.entry.js +147 -0
  6. package/dist/cjs/{limebb-lime-query-filter-comparison_5.cjs.entry.js → limebb-lime-query-filter-group_3.cjs.entry.js} +5 -333
  7. package/dist/cjs/limebb-lime-query-order-by-item.cjs.entry.js +51 -0
  8. package/dist/cjs/limebb-live-docs-info.cjs.entry.js +2 -2
  9. package/dist/cjs/limebb-locale-picker.cjs.entry.js +1 -1
  10. package/dist/cjs/limebb-mention-group-counter.cjs.entry.js +2 -2
  11. package/dist/cjs/limebb-navigation-button_2.cjs.entry.js +3 -3
  12. package/dist/cjs/limebb-notification-item.cjs.entry.js +1 -1
  13. package/dist/cjs/limebb-percentage-visualizer.cjs.entry.js +2 -2
  14. package/dist/cjs/limebb-property-selector.cjs.entry.js +234 -0
  15. package/dist/cjs/limebb-text-editor.cjs.entry.js +1 -1
  16. package/dist/cjs/limebb-trend-indicator.cjs.entry.js +1 -1
  17. package/dist/cjs/loader.cjs.js +1 -1
  18. package/dist/collection/collection-manifest.json +2 -2
  19. package/dist/collection/components/lime-query-builder/expressions/lime-query-filter-comparison.js +1 -1
  20. package/dist/collection/components/lime-query-builder/expressions/lime-query-filter-group.js +1 -1
  21. package/dist/collection/components/lime-query-builder/expressions/lime-query-filter-not.js +1 -1
  22. package/dist/collection/components/lime-query-builder/expressions/lime-query-value-input.js +7 -7
  23. package/dist/collection/components/lime-query-builder/lime-query-builder.css +2 -5
  24. package/dist/collection/components/lime-query-builder/lime-query-builder.js +19 -19
  25. package/dist/collection/components/lime-query-builder/lime-query-validation.js +160 -26
  26. package/dist/collection/components/lime-query-builder/lime-query.types.js +1 -57
  27. package/dist/collection/components/lime-query-builder/limetype-field/limetype-field.js +1 -1
  28. package/dist/collection/components/lime-query-builder/order-by/order-by-editor.css +72 -0
  29. package/dist/collection/components/lime-query-builder/order-by/order-by-editor.js +252 -0
  30. package/dist/collection/components/lime-query-builder/order-by/order-by-item.css +45 -0
  31. package/dist/collection/components/lime-query-builder/order-by/order-by-item.js +173 -0
  32. package/dist/collection/components/limeobject/file-viewer/live-docs-info.js +2 -2
  33. package/dist/collection/components/locale-picker/locale-picker.js +1 -1
  34. package/dist/collection/components/notification-list/notification-item/notification-item.js +1 -1
  35. package/dist/collection/components/percentage-visualizer/percentage-visualizer.js +2 -2
  36. package/dist/collection/components/summary-popover/summary-popover.js +3 -3
  37. package/dist/collection/components/text-editor/mention-group-counter.js +2 -2
  38. package/dist/collection/components/text-editor/text-editor.js +1 -1
  39. package/dist/collection/components/trend-indicator/trend-indicator.js +1 -1
  40. package/dist/components/lime-query-filter-comparison.js +1 -1
  41. package/dist/components/lime-query-filter-expression.js +2 -2
  42. package/dist/components/lime-query-value-input.js +3 -3
  43. package/dist/components/limebb-lime-query-builder.js +201 -215
  44. package/dist/components/limebb-lime-query-order-by-editor.d.ts +11 -0
  45. package/dist/components/limebb-lime-query-order-by-editor.js +6 -0
  46. package/dist/components/limebb-lime-query-order-by-item.d.ts +11 -0
  47. package/dist/components/limebb-lime-query-order-by-item.js +6 -0
  48. package/dist/components/limebb-locale-picker.js +1 -1
  49. package/dist/components/limebb-mention-group-counter.js +2 -2
  50. package/dist/components/limebb-percentage-visualizer.js +2 -2
  51. package/dist/components/limebb-text-editor.js +1 -1
  52. package/dist/components/limebb-trend-indicator.js +1 -1
  53. package/dist/components/limetype-field.js +1 -1
  54. package/dist/components/live-docs-info.js +2 -2
  55. package/dist/components/notification-item.js +1 -1
  56. package/dist/components/order-by-editor.js +123 -0
  57. package/dist/components/order-by-item.js +73 -0
  58. package/dist/components/property-selector.js +1 -1
  59. package/dist/components/summary-popover.js +3 -3
  60. package/dist/esm/lime-crm-building-blocks.js +1 -1
  61. package/dist/esm/limebb-lime-query-builder.entry.js +179 -206
  62. package/dist/esm/{limebb-lime-query-filter-builder_3.entry.js → limebb-lime-query-filter-builder_4.entry.js} +86 -2
  63. package/dist/esm/limebb-lime-query-filter-comparison_2.entry.js +142 -0
  64. package/dist/esm/{limebb-lime-query-filter-comparison_5.entry.js → limebb-lime-query-filter-group_3.entry.js} +7 -333
  65. package/dist/esm/limebb-lime-query-order-by-item.entry.js +47 -0
  66. package/dist/esm/limebb-live-docs-info.entry.js +2 -2
  67. package/dist/esm/limebb-locale-picker.entry.js +1 -1
  68. package/dist/esm/limebb-mention-group-counter.entry.js +2 -2
  69. package/dist/esm/limebb-navigation-button_2.entry.js +3 -3
  70. package/dist/esm/limebb-notification-item.entry.js +1 -1
  71. package/dist/esm/limebb-percentage-visualizer.entry.js +2 -2
  72. package/dist/esm/limebb-property-selector.entry.js +230 -0
  73. package/dist/esm/limebb-text-editor.entry.js +1 -1
  74. package/dist/esm/limebb-trend-indicator.entry.js +1 -1
  75. package/dist/esm/loader.js +1 -1
  76. package/dist/esm/{property-resolution-fde2375e.js → property-resolution-c21a1369.js} +1 -1
  77. package/dist/lime-crm-building-blocks/lime-crm-building-blocks.esm.js +1 -1
  78. package/dist/lime-crm-building-blocks/{p-631ca5a5.entry.js → p-0cd036ed.entry.js} +1 -1
  79. package/dist/lime-crm-building-blocks/p-2725671e.entry.js +1 -0
  80. package/dist/lime-crm-building-blocks/{p-9cac4de2.entry.js → p-425eaba2.entry.js} +1 -1
  81. package/dist/lime-crm-building-blocks/{p-e8946134.entry.js → p-5cf4898d.entry.js} +1 -1
  82. package/dist/lime-crm-building-blocks/p-67c174d0.entry.js +1 -0
  83. package/dist/lime-crm-building-blocks/p-6aa216ec.entry.js +1 -0
  84. package/dist/lime-crm-building-blocks/{p-93cadc1e.entry.js → p-8601eab5.entry.js} +1 -1
  85. package/dist/lime-crm-building-blocks/{p-b9b954d9.entry.js → p-8c2fb1c9.entry.js} +1 -1
  86. package/dist/lime-crm-building-blocks/{p-3122ea05.entry.js → p-92dfc5f8.entry.js} +1 -1
  87. package/dist/lime-crm-building-blocks/{p-569c86b5.entry.js → p-a1ee8990.entry.js} +1 -1
  88. package/dist/lime-crm-building-blocks/p-abfc7815.entry.js +1 -0
  89. package/dist/lime-crm-building-blocks/{p-e0ab1554.js → p-b748c770.js} +1 -1
  90. package/dist/lime-crm-building-blocks/p-ccf34631.entry.js +1 -0
  91. package/dist/lime-crm-building-blocks/{p-36ea13c0.entry.js → p-d84874dc.entry.js} +1 -1
  92. package/dist/lime-crm-building-blocks/p-f9efca1d.entry.js +1 -0
  93. package/dist/types/components/lime-query-builder/expressions/lime-query-value-input.d.ts +2 -3
  94. package/dist/types/components/lime-query-builder/lime-query-builder.d.ts +9 -6
  95. package/dist/types/components/lime-query-builder/lime-query.types.d.ts +0 -72
  96. package/dist/types/components/lime-query-builder/order-by/order-by-editor.d.ts +58 -0
  97. package/dist/types/components/lime-query-builder/order-by/order-by-item.d.ts +37 -0
  98. package/dist/types/components.d.ts +165 -205
  99. package/package.json +1 -1
  100. package/dist/cjs/limebb-lime-query-filter-and.cjs.entry.js +0 -80
  101. package/dist/cjs/limebb-lime-query-filter-expression.cjs.entry.js +0 -45
  102. package/dist/cjs/limebb-lime-query-filter-or.cjs.entry.js +0 -68
  103. package/dist/collection/components/lime-query-builder/expressions/lime-query-filter-and.css +0 -121
  104. package/dist/collection/components/lime-query-builder/expressions/lime-query-filter-and.js +0 -249
  105. package/dist/collection/components/lime-query-builder/expressions/lime-query-filter-or.css +0 -123
  106. package/dist/collection/components/lime-query-builder/expressions/lime-query-filter-or.js +0 -237
  107. package/dist/collection/components/lime-query-builder/filter-conversion.js +0 -255
  108. package/dist/components/limebb-lime-query-filter-and.d.ts +0 -11
  109. package/dist/components/limebb-lime-query-filter-and.js +0 -135
  110. package/dist/components/limebb-lime-query-filter-or.d.ts +0 -11
  111. package/dist/components/limebb-lime-query-filter-or.js +0 -123
  112. package/dist/esm/limebb-lime-query-filter-and.entry.js +0 -76
  113. package/dist/esm/limebb-lime-query-filter-expression.entry.js +0 -41
  114. package/dist/esm/limebb-lime-query-filter-or.entry.js +0 -64
  115. package/dist/lime-crm-building-blocks/p-24aeb928.entry.js +0 -1
  116. package/dist/lime-crm-building-blocks/p-422f6d51.entry.js +0 -1
  117. package/dist/lime-crm-building-blocks/p-67dbaa4c.entry.js +0 -1
  118. package/dist/lime-crm-building-blocks/p-6d119dab.entry.js +0 -1
  119. package/dist/lime-crm-building-blocks/p-91732502.entry.js +0 -1
  120. package/dist/lime-crm-building-blocks/p-b198194a.entry.js +0 -1
  121. package/dist/types/components/lime-query-builder/expressions/lime-query-filter-and.d.ts +0 -57
  122. package/dist/types/components/lime-query-builder/expressions/lime-query-filter-or.d.ts +0 -56
  123. package/dist/types/components/lime-query-builder/filter-conversion.d.ts +0 -31
@@ -1,166 +1,6 @@
1
1
  import { r as registerInstance, c as createEvent, h } from './index-96dd111f.js';
2
- import { T as Te } from './index.esm-bb569663.js';
3
- import { g as getNormalizedProperties, a as getPropertyFromPath } from './property-resolution-fde2375e.js';
4
-
5
- /**
6
- * TypeScript type definitions for Lime Query DSL
7
- *
8
- * These types represent the structure of Lime Query as defined in:
9
- * - lime-core/lime_query/schema.py
10
- * - lime-core/lime_filter/schema.py
11
- */
12
- /**
13
- * Available operators with display metadata
14
- */
15
- const LIME_QUERY_OPERATORS = [
16
- { value: '=', label: 'Equals', icon: 'equals' },
17
- { value: '!=', label: 'Not Equals', icon: 'not-equal' },
18
- {
19
- value: '>',
20
- label: 'Greater Than',
21
- icon: 'greater-than',
22
- applicableTypes: ['integer', 'decimal', 'date', 'time'],
23
- },
24
- {
25
- value: '>=',
26
- label: 'Greater or Equal',
27
- icon: 'greater-or-equal',
28
- applicableTypes: ['integer', 'decimal', 'date', 'time'],
29
- },
30
- {
31
- value: '<',
32
- label: 'Less Than',
33
- icon: 'less-than',
34
- applicableTypes: ['integer', 'decimal', 'date', 'time'],
35
- },
36
- {
37
- value: '<=',
38
- label: 'Less or Equal',
39
- icon: 'less-or-equal',
40
- applicableTypes: ['integer', 'decimal', 'date', 'time'],
41
- },
42
- { value: 'IN', label: 'In List', icon: 'list' },
43
- {
44
- value: '?',
45
- label: 'Contains',
46
- icon: '-lime-filter-contain',
47
- applicableTypes: ['string', 'text'],
48
- },
49
- {
50
- value: '=?',
51
- label: 'Begins With',
52
- icon: '-lime-filter-begin',
53
- applicableTypes: ['string', 'text'],
54
- },
55
- {
56
- value: '=$',
57
- label: 'Ends With',
58
- icon: '-lime-filter-end',
59
- applicableTypes: ['string', 'text'],
60
- },
61
- ];
62
-
63
- /**
64
- * Validate and convert a FilterExpression (comparison filter)
65
- * @param filter
66
- */
67
- function validateAndConvertFilterExpression(filter) {
68
- // Validate required properties exist
69
- if (!('op' in filter) || !('exp' in filter)) {
70
- throw new Error('Invalid filter: comparison must have key, op, and exp properties');
71
- }
72
- // Validate operator is supported
73
- const supportedOps = LIME_QUERY_OPERATORS.map((o) => o.value);
74
- if (!supportedOps.includes(filter.op)) {
75
- throw new Error(`Unsupported filter operator: ${filter.op}`);
76
- }
77
- // Check for unexpected properties
78
- // Note: 'type' property is allowed for filter references
79
- const knownProps = new Set(['key', 'op', 'exp', 'type']);
80
- const unexpectedProps = Object.keys(filter).filter((k) => !knownProps.has(k));
81
- if (unexpectedProps.length > 0) {
82
- throw new Error(`Unexpected properties in filter comparison: ${unexpectedProps.join(', ')}`);
83
- }
84
- return {
85
- filter_type: 'comparison',
86
- property: filter.key,
87
- operator: filter.op,
88
- value: filter.exp,
89
- };
90
- }
91
- /**
92
- * Validate and convert a FilterGroup (AND/OR/NOT filter)
93
- * @param filter
94
- */
95
- function validateAndConvertFilterGroup(filter) {
96
- // Validate it has 'op' property
97
- if (!('op' in filter)) {
98
- throw new Error('Invalid filter: filter group must have op property');
99
- }
100
- // Validate it has 'exp' property
101
- if (!('exp' in filter)) {
102
- throw new Error('Invalid filter: filter group must have exp property');
103
- }
104
- // Check for unexpected properties in filter groups
105
- const knownGroupProps = new Set(['op', 'exp']);
106
- const unexpectedGroupProps = Object.keys(filter).filter((k) => !knownGroupProps.has(k));
107
- if (unexpectedGroupProps.length > 0) {
108
- throw new Error(`Unexpected properties in filter group: ${unexpectedGroupProps.join(', ')}`);
109
- }
110
- if (filter.op === '!') {
111
- // NOT filter: exp should be a single expression (not array)
112
- if (Array.isArray(filter.exp)) {
113
- throw new TypeError('Invalid NOT filter: exp should be a single expression, not an array');
114
- }
115
- return {
116
- filter_type: 'NOT',
117
- expression: limeQueryToGuiFilter(filter.exp),
118
- };
119
- }
120
- if (filter.op === 'AND') {
121
- // AND filter: exp should be an array
122
- if (!Array.isArray(filter.exp)) {
123
- throw new TypeError('Invalid AND filter: exp must be an array');
124
- }
125
- return {
126
- filter_type: 'AND',
127
- expressions: filter.exp.map(limeQueryToGuiFilter),
128
- };
129
- }
130
- if (filter.op === 'OR') {
131
- // OR filter: exp should be an array
132
- if (!Array.isArray(filter.exp)) {
133
- throw new TypeError('Invalid OR filter: exp must be an array');
134
- }
135
- return {
136
- filter_type: 'OR',
137
- expressions: filter.exp.map(limeQueryToGuiFilter),
138
- };
139
- }
140
- // If we reach here, the operator is unsupported
141
- throw new Error(`Unsupported filter operator: ${filter.op}`);
142
- }
143
- /**
144
- * Convert Lime Query filter to UI filter representation
145
- * Throws errors for unsupported filter structures
146
- * @param filter
147
- */
148
- function limeQueryToGuiFilter(filter) {
149
- if (!filter) {
150
- // Return empty AND instead of missing expression
151
- // This ensures users see the "Add a condition" button
152
- return {
153
- filter_type: 'AND',
154
- expressions: [],
155
- };
156
- }
157
- // Check if it's a FilterExpression (has 'key' property)
158
- if ('key' in filter) {
159
- return validateAndConvertFilterExpression(filter);
160
- }
161
- // It's a FilterGroup
162
- return validateAndConvertFilterGroup(filter);
163
- }
2
+ import { Z as Zt, T as Te } from './index.esm-bb569663.js';
3
+ import { g as getPropertyFromPath, a as getNormalizedProperties } from './property-resolution-c21a1369.js';
164
4
 
165
5
  /**
166
6
  * Dynamic filter values and placeholders that are valid in Lime Query
@@ -409,6 +249,49 @@ function validatePropertySelection(selection, limetypes, limetype, guiModeEnable
409
249
  }
410
250
  return allGuiLimitations;
411
251
  }
252
+ /**
253
+ * Validate a comparison expression (has 'key' property)
254
+ * @param filter
255
+ * @param activeLimetype
256
+ * @param limetypes
257
+ */
258
+ function validateComparisonExpression(filter, activeLimetype, limetypes) {
259
+ // Validate operator
260
+ const allValidOperators = Object.values(Zt);
261
+ if (!allValidOperators.includes(filter.op)) {
262
+ throw new Error(`Unsupported filter operator: ${filter.op}`);
263
+ }
264
+ // Validate placeholder
265
+ const result = validatePlaceholder(filter.exp, activeLimetype, limetypes);
266
+ if (!result.valid) {
267
+ throw new Error(`Invalid placeholder in filter '${filter.key}': ${result.error}`);
268
+ }
269
+ }
270
+ /**
271
+ * Validate a group expression (AND/OR/NOT)
272
+ * @param filter
273
+ * @param activeLimetype
274
+ * @param limetypes
275
+ * @param guiModeEnabled
276
+ */
277
+ function validateGroupExpression(filter, activeLimetype, limetypes, guiModeEnabled) {
278
+ // Validate operator
279
+ if (filter.op !== Zt.AND &&
280
+ filter.op !== Zt.OR &&
281
+ filter.op !== Zt.NOT) {
282
+ throw new Error(`Unsupported group operator: ${filter.op}`);
283
+ }
284
+ // Recursively validate children
285
+ if (filter.op === Zt.NOT) {
286
+ validateFilterPlaceholders(filter.exp, activeLimetype, limetypes, guiModeEnabled);
287
+ }
288
+ else if (filter.op === Zt.AND || filter.op === Zt.OR) {
289
+ const expressions = filter.exp;
290
+ for (const expr of expressions) {
291
+ validateFilterPlaceholders(expr, activeLimetype, limetypes, guiModeEnabled);
292
+ }
293
+ }
294
+ }
412
295
  /**
413
296
  * Validate placeholders in a filter expression
414
297
  * @param filter Filter expression to validate
@@ -420,27 +303,12 @@ function validateFilterPlaceholders(filter, activeLimetype, limetypes, guiModeEn
420
303
  if (!filter) {
421
304
  return;
422
305
  }
423
- // Check if it's a FilterExpression (has 'key' property)
424
306
  if ('key' in filter) {
425
- const result = validatePlaceholder(filter.exp, activeLimetype, limetypes);
426
- if (!result.valid) {
427
- throw new Error(`Invalid placeholder in filter '${filter.key}': ${result.error}`);
428
- }
307
+ validateComparisonExpression(filter, activeLimetype, limetypes);
429
308
  return;
430
309
  }
431
- // It's a FilterGroup - recursively validate children
432
310
  if ('exp' in filter) {
433
- if (filter.op === '!') {
434
- // NOT has a single expression
435
- validateFilterPlaceholders(filter.exp, activeLimetype, limetypes, guiModeEnabled);
436
- }
437
- else if (filter.op === 'AND' || filter.op === 'OR') {
438
- // AND/OR have array of expressions
439
- const expressions = filter.exp;
440
- for (const expr of expressions) {
441
- validateFilterPlaceholders(expr, activeLimetype, limetypes, guiModeEnabled);
442
- }
443
- }
311
+ validateGroupExpression(filter, activeLimetype, limetypes, guiModeEnabled);
444
312
  }
445
313
  }
446
314
  /**
@@ -454,7 +322,6 @@ function validateFilterPlaceholders(filter, activeLimetype, limetypes, guiModeEn
454
322
  function validateLimeQueryFilterInternal(filter, activeLimetype, limetypes, guiModeEnabled) {
455
323
  const errors = [];
456
324
  try {
457
- limeQueryToGuiFilter(filter);
458
325
  validateFilterPlaceholders(filter, activeLimetype, limetypes, guiModeEnabled);
459
326
  }
460
327
  catch (error) {
@@ -462,6 +329,113 @@ function validateLimeQueryFilterInternal(filter, activeLimetype, limetypes, guiM
462
329
  }
463
330
  return errors;
464
331
  }
332
+ /**
333
+ * Validate orderBy specification
334
+ * @param orderBy Array of orderBy items to validate
335
+ * @param limetypes Record of all available limetypes
336
+ * @param limetype The limetype for this Lime Query
337
+ * @returns Array of validation error messages
338
+ */
339
+ function validateOrderBy(orderBy, limetypes, limetype) {
340
+ const errors = [];
341
+ if (!Array.isArray(orderBy)) {
342
+ errors.push('orderBy must be an array');
343
+ return errors;
344
+ }
345
+ if (!limetype || !limetypes[limetype]) {
346
+ // Can't validate property paths without limetype
347
+ return errors;
348
+ }
349
+ for (const [index, item] of orderBy.entries()) {
350
+ const itemErrors = validateOrderByItem(item, index, limetypes, limetype);
351
+ errors.push(...itemErrors);
352
+ }
353
+ return errors;
354
+ }
355
+ /**
356
+ * Validate a single orderBy item
357
+ * @param item The orderBy item to validate
358
+ * @param index The index of the item in the array (for error messages)
359
+ * @param limetypes Record of all available limetypes
360
+ * @param limetype The limetype for this Lime Query
361
+ * @returns Array of validation error messages for this item
362
+ */
363
+ function validateOrderByItem(item, index, limetypes, limetype) {
364
+ const errors = [];
365
+ const objectError = validateOrderByItemIsObject(item, index);
366
+ if (objectError) {
367
+ return [objectError];
368
+ }
369
+ const keys = Object.keys(item);
370
+ const keyError = validateOrderByItemHasSingleKey(keys, index);
371
+ if (keyError) {
372
+ return [keyError];
373
+ }
374
+ const propertyPath = keys[0];
375
+ const direction = item[propertyPath];
376
+ const directionError = validateOrderByDirection(direction, index);
377
+ if (directionError) {
378
+ errors.push(directionError);
379
+ }
380
+ const pathError = validateOrderByPropertyPath(propertyPath, limetypes, limetype, index);
381
+ if (pathError) {
382
+ errors.push(pathError);
383
+ }
384
+ return errors;
385
+ }
386
+ /**
387
+ * Validate that the orderBy item is an object
388
+ * @param item
389
+ * @param index
390
+ */
391
+ function validateOrderByItemIsObject(item, index) {
392
+ if (typeof item !== 'object' || item === null) {
393
+ return `orderBy[${index}] must be an object`;
394
+ }
395
+ return null;
396
+ }
397
+ /**
398
+ * Validate that the orderBy item has exactly one property key
399
+ * @param keys
400
+ * @param index
401
+ */
402
+ function validateOrderByItemHasSingleKey(keys, index) {
403
+ if (keys.length === 0) {
404
+ return `orderBy[${index}] must have a property path`;
405
+ }
406
+ if (keys.length > 1) {
407
+ return `orderBy[${index}] must have exactly one property, got ${keys.length}`;
408
+ }
409
+ return null;
410
+ }
411
+ /**
412
+ * Validate that the sort direction is either ASC or DESC
413
+ * @param direction
414
+ * @param index
415
+ */
416
+ function validateOrderByDirection(direction, index) {
417
+ if (direction !== 'ASC' && direction !== 'DESC') {
418
+ return `orderBy[${index}]: direction must be 'ASC' or 'DESC', got '${direction}'`;
419
+ }
420
+ return null;
421
+ }
422
+ /**
423
+ * Validate that the property path exists on the limetype
424
+ * @param propertyPath
425
+ * @param limetypes
426
+ * @param limetype
427
+ * @param index
428
+ */
429
+ function validateOrderByPropertyPath(propertyPath, limetypes, limetype, index) {
430
+ if (!propertyPath || propertyPath === '') {
431
+ return null;
432
+ }
433
+ const property = getPropertyFromPath(limetypes, limetype, propertyPath);
434
+ if (!property) {
435
+ return `orderBy[${index}]: property path '${propertyPath}' does not exist on limetype '${limetype}'`;
436
+ }
437
+ return null;
438
+ }
465
439
  /**
466
440
  * Validate Lime Query response format and collect errors/limitations
467
441
  * @param responseFormat The response format to validate
@@ -511,14 +485,14 @@ function isLimeQuerySupported(limeQuery, limetypes, activeLimetype, guiModeEnabl
511
485
  if (limeQuery.offset !== undefined && !limeQuery.orderBy) {
512
486
  validationErrors.push('offset requires orderBy to be specified');
513
487
  }
488
+ // Validate orderBy
489
+ if (limeQuery.orderBy) {
490
+ const orderByErrors = validateOrderBy(limeQuery.orderBy, limetypes, limeQuery.limetype);
491
+ validationErrors.push(...orderByErrors);
492
+ }
514
493
  // Check for GUI-unsupported top-level properties
515
- if (guiModeEnabled) {
516
- if (limeQuery.orderBy) {
517
- guiLimitations.push('orderBy is not yet supported in GUI mode');
518
- }
519
- if (limeQuery.offset !== undefined) {
520
- guiLimitations.push('offset is not yet supported in GUI mode');
521
- }
494
+ if (guiModeEnabled && limeQuery.offset !== undefined) {
495
+ guiLimitations.push('offset is not yet supported in GUI mode');
522
496
  }
523
497
  // Validate filter
524
498
  if (limeQuery.filter) {
@@ -539,7 +513,7 @@ function isLimeQuerySupported(limeQuery, limetypes, activeLimetype, guiModeEnabl
539
513
  };
540
514
  }
541
515
 
542
- const limeQueryBuilderCss = ":host(limebb-lime-lime-query-builder){display:block;width:100%}.lime-query-builder{display:flex;flex-direction:column;gap:2rem}.mode-controls{display:flex;align-items:center;gap:0.5rem;padding:0.5rem}.mode-switch{display:flex;align-items:center;gap:0.5rem}.mode-switch limel-button{min-width:5rem}.mode-switch limel-helper-text{margin-left:1rem;color:rgb(var(--color-red-default))}.gui-mode,.code-mode{display:block}.code-editor-container{--code-editor-max-height:70vh;display:flex;flex-direction:column;gap:1rem}.code-editor-container .validation-errors{padding:0.75rem 1rem;color:rgb(var(--color-red-default));background-color:rgb(var(--color-red-lighter));border-left:0.25rem solid rgb(var(--color-red-default));border-radius:0.25rem;font-size:0.875rem}.code-editor-container .validation-errors strong{display:block;margin-bottom:0.5rem;font-weight:600}.code-editor-container .validation-errors ul{margin:0;padding-left:1.5rem}.code-editor-container .validation-errors li{margin:0.25rem 0}.code-editor-container .gui-limitations{padding:0.75rem 1rem;color:rgb(var(--color-blue-dark));background-color:rgb(var(--color-blue-lighter));border-left:0.25rem solid rgb(var(--color-blue-default));border-radius:0.25rem;font-size:0.875rem}.code-editor-container .gui-limitations strong{display:block;margin-bottom:0.5rem;font-weight:600}.code-editor-container .gui-limitations ul{margin:0;padding-left:1.5rem}.code-editor-container .gui-limitations li{margin:0.25rem 0}.lime-query-builder-label{margin:0;font-size:1.5rem;font-weight:600;color:rgb(var(--contrast-1100))}.limetype-section{display:flex;flex-direction:column}.filter-section,.query-options-section{display:flex;flex-direction:column;gap:1rem}.section-label{margin:0;font-size:1.125rem;font-weight:600;color:rgb(var(--contrast-1000))}.query-options-controls{display:grid;grid-template-columns:repeat(auto-fit, minmax(200px, 1fr));gap:1rem}@media (max-width: 768px){.lime-query-builder{gap:1.5rem}.query-options-controls{grid-template-columns:1fr}}";
516
+ const limeQueryBuilderCss = ":host(limebb-lime-lime-query-builder){display:block;width:100%}.lime-query-builder{display:flex;flex-direction:column;gap:2rem}.mode-controls{display:flex;align-items:center;gap:0.5rem;padding:0.5rem}.mode-switch{display:flex;align-items:center;gap:0.5rem}.mode-switch limel-button{min-width:5rem}.mode-switch limel-helper-text{margin-left:1rem;color:rgb(var(--color-red-default))}.gui-mode,.code-mode{display:block}.code-editor-container{--code-editor-max-height:70vh;display:flex;flex-direction:column;gap:1rem}.code-editor-container .validation-errors{padding:0.75rem 1rem;color:rgb(var(--color-red-default));background-color:rgb(var(--color-red-lighter));border-left:0.25rem solid rgb(var(--color-red-default));border-radius:0.25rem;font-size:0.875rem}.code-editor-container .validation-errors strong{display:block;margin-bottom:0.5rem;font-weight:600}.code-editor-container .validation-errors ul{margin:0;padding-left:1.5rem}.code-editor-container .validation-errors li{margin:0.25rem 0}.code-editor-container .gui-limitations{padding:0.75rem 1rem;color:rgb(var(--color-blue-dark));background-color:rgb(var(--color-blue-lighter));border-left:0.25rem solid rgb(var(--color-blue-default));border-radius:0.25rem;font-size:0.875rem}.code-editor-container .gui-limitations strong{display:block;margin-bottom:0.5rem;font-weight:600}.code-editor-container .gui-limitations ul{margin:0;padding-left:1.5rem}.code-editor-container .gui-limitations li{margin:0.25rem 0}.lime-query-builder-label{margin:0;font-size:1.5rem;font-weight:600;color:rgb(var(--contrast-1100))}.limetype-section{display:flex;flex-direction:column}.filter-section,.query-options-section{display:flex;flex-direction:column;gap:1rem}.section-label{margin:0;font-size:1.125rem;font-weight:600;color:rgb(var(--contrast-1000))}.query-options-controls{display:flex;flex-direction:column;gap:1rem}@media (max-width: 768px){.lime-query-builder{gap:1.5rem}}";
543
517
  const LimebbLimeQueryBuilderStyle0 = limeQueryBuilderCss;
544
518
 
545
519
  var __decorate = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
@@ -589,6 +563,11 @@ const LimeQueryBuilder = class {
589
563
  this.limit = value ? Number.parseInt(value, 10) : undefined;
590
564
  this.emitChange();
591
565
  };
566
+ this.handleOrderByChange = (event) => {
567
+ event.stopPropagation();
568
+ this.orderBy = event.detail;
569
+ this.emitChange();
570
+ };
592
571
  this.switchToGui = () => {
593
572
  // Validate JSON before switching
594
573
  try {
@@ -604,6 +583,7 @@ const LimeQueryBuilder = class {
604
583
  this.filter = parsed.filter;
605
584
  this.internalResponseFormat = parsed.responseFormat;
606
585
  this.limit = parsed.limit;
586
+ this.orderBy = parsed.orderBy;
607
587
  this.mode = 'gui';
608
588
  this.change.emit(parsed);
609
589
  }
@@ -645,6 +625,7 @@ const LimeQueryBuilder = class {
645
625
  this.filter = this.value.filter;
646
626
  this.internalResponseFormat = this.value.responseFormat;
647
627
  this.limit = this.value.limit;
628
+ this.orderBy = this.value.orderBy;
648
629
  }
649
630
  // Initialize code value from prop
650
631
  this.updateCodeValue();
@@ -657,7 +638,7 @@ const LimeQueryBuilder = class {
657
638
  render() {
658
639
  const guiSupported = this.checkGuiSupport();
659
640
  const showCodeMode = !this.guiModeEnabled || this.mode === 'code';
660
- return (h("div", { key: '0b4d2bb5b4d503a775d8d3215e1e4c74fdb1eceb', class: "lime-query-builder" }, this.renderLabel(), this.renderModeControls(guiSupported), showCodeMode
641
+ return (h("div", { key: 'f51ae0459c1c7f482f3a790abb4cc3bd60b83e70', class: "lime-query-builder" }, this.renderLabel(), this.renderModeControls(guiSupported), showCodeMode
661
642
  ? this.renderCodeMode(guiSupported)
662
643
  : this.renderGuiMode()));
663
644
  }
@@ -669,27 +650,16 @@ const LimeQueryBuilder = class {
669
650
  if (!this.limetype) {
670
651
  return;
671
652
  }
672
- // Use internal state, then default to returning just _id
673
- const responseFormat = this.internalResponseFormat || {
674
- object: {
675
- _id: null,
676
- },
677
- };
678
- const limeQuery = {
679
- limetype: this.limetype,
680
- responseFormat: responseFormat,
681
- filter: this.filter,
682
- };
683
- if (this.limit !== undefined && this.limit > 0) {
684
- limeQuery.limit = this.limit;
685
- }
686
- this.change.emit(limeQuery);
653
+ this.change.emit(this.buildLimeQuery());
687
654
  }
688
655
  updateCodeValue() {
689
656
  if (!this.limetype) {
690
657
  this.codeValue = JSON.stringify(this.value || {}, null, 2);
691
658
  return;
692
659
  }
660
+ this.codeValue = JSON.stringify(this.buildLimeQuery(), null, 2);
661
+ }
662
+ buildLimeQuery() {
693
663
  // Use internal state, then default to returning just _id
694
664
  const responseFormat = this.internalResponseFormat || {
695
665
  object: {
@@ -704,7 +674,10 @@ const LimeQueryBuilder = class {
704
674
  if (this.limit !== undefined && this.limit > 0) {
705
675
  limeQuery.limit = this.limit;
706
676
  }
707
- this.codeValue = JSON.stringify(limeQuery, null, 2);
677
+ if (this.orderBy && this.orderBy.length > 0) {
678
+ limeQuery.orderBy = this.orderBy;
679
+ }
680
+ return limeQuery;
708
681
  }
709
682
  checkGuiSupport() {
710
683
  if (!this.limetypes) {
@@ -789,7 +762,7 @@ const LimeQueryBuilder = class {
789
762
  if (!this.limetype) {
790
763
  return;
791
764
  }
792
- return (h("div", { class: "query-options-section" }, h("h4", { class: "section-label" }, "Query Options"), h("div", { class: "query-options-controls" }, h("limel-input-field", { label: "Limit", type: "number", value: ((_a = this.limit) === null || _a === void 0 ? void 0 : _a.toString()) || '', placeholder: "No limit", helperText: "Maximum number of results", onChange: this.handleLimitChange }))));
765
+ return (h("div", { class: "query-options-section" }, h("h4", { class: "section-label" }, "Query Options"), h("div", { class: "query-options-controls" }, h("limel-input-field", { label: "Limit", type: "number", value: ((_a = this.limit) === null || _a === void 0 ? void 0 : _a.toString()) || '', placeholder: "No limit", helperText: "Maximum number of results", onChange: this.handleLimitChange }), h("limebb-lime-query-order-by-editor", { platform: this.platform, context: this.context, limetype: this.limetype, value: this.orderBy, onChange: this.handleOrderByChange }))));
793
766
  }
794
767
  renderGuiMode() {
795
768
  return (h("div", { class: "gui-mode" }, this.renderLimetypeSection(), this.renderResponseFormatSection(), this.renderFilterSection(), this.renderQueryOptionsSection()));
@@ -135,6 +135,90 @@ const LimeQueryFilterBuilderComponent = class {
135
135
  };
136
136
  LimeQueryFilterBuilderComponent.style = LimebbLimeQueryFilterBuilderStyle0;
137
137
 
138
+ const orderByEditorCss = ":host(limebb-lime-query-order-by-editor){display:block;width:100%}.order-by-editor{display:flex;flex-direction:column;gap:1rem;padding:1rem}.header{display:flex;justify-content:space-between;align-items:center;gap:1rem}.header h4{margin:0;font-size:1rem;font-weight:600;color:rgb(var(--contrast-1000))}.order-by-list{display:flex;flex-direction:column;gap:1rem;padding:0.5rem;border:1px solid rgb(var(--contrast-300));border-radius:0.25rem;background-color:rgb(var(--contrast-50));min-height:4rem}.order-by-item{padding:0.5rem;border-radius:0.25rem;transition:background-color 0.2s}.order-by-item:hover{background-color:rgb(var(--contrast-100))}.actions{display:flex;justify-content:flex-start}.summary{display:flex;justify-content:space-between;align-items:center;padding-top:0.5rem;border-top:1px solid rgb(var(--contrast-300))}.summary .count{font-size:0.875rem;font-weight:500;color:rgb(var(--contrast-900))}.empty-state{padding:2rem;text-align:center;color:rgb(var(--contrast-700));font-style:italic}.empty-state p{margin:0}";
139
+ const LimebbLimeQueryOrderByEditorStyle0 = orderByEditorCss;
140
+
141
+ const OrderByEditor = class {
142
+ constructor(hostRef) {
143
+ registerInstance(this, hostRef);
144
+ this.change = createEvent(this, "change", 7);
145
+ /**
146
+ * Optional label
147
+ */
148
+ this.label = 'Sort Order';
149
+ this.items = [];
150
+ this.handleItemChange = (index) => (event) => {
151
+ event.stopPropagation();
152
+ const newItems = [...this.items];
153
+ if (event.detail === null) {
154
+ // Remove item
155
+ newItems.splice(index, 1);
156
+ }
157
+ else {
158
+ // Update item
159
+ newItems[index] = event.detail;
160
+ }
161
+ this.items = newItems;
162
+ this.emitChange();
163
+ };
164
+ this.handleAddSortField = () => {
165
+ // Add new item with empty path and ASC direction
166
+ this.items = [...this.items, { '': 'ASC' }];
167
+ this.emitChange();
168
+ };
169
+ }
170
+ componentWillLoad() {
171
+ if (this.value && this.value.length > 0) {
172
+ this.items = [...this.value];
173
+ }
174
+ }
175
+ handleValueChange(newValue) {
176
+ if (!newValue) {
177
+ return;
178
+ }
179
+ // Check if items have changed
180
+ const itemsChanged = newValue.length !== this.items.length ||
181
+ !newValue.every((item, index) => {
182
+ const currentItem = this.items[index];
183
+ if (!currentItem)
184
+ return false;
185
+ const itemKey = Object.keys(item)[0];
186
+ const currentKey = Object.keys(currentItem)[0];
187
+ return (itemKey === currentKey &&
188
+ item[itemKey] === currentItem[currentKey]);
189
+ });
190
+ if (itemsChanged) {
191
+ this.items = newValue.length > 0 ? [...newValue] : [];
192
+ }
193
+ }
194
+ render() {
195
+ if (!this.limetype) {
196
+ return (h("div", { class: "empty-state" }, h("p", null, "Select a limetype to configure sorting")));
197
+ }
198
+ return (h("div", { class: "order-by-editor" }, h("div", { class: "header" }, h("h4", null, this.label)), this.renderOrderByList(), this.renderActions()));
199
+ }
200
+ renderOrderByList() {
201
+ if (this.items.length === 0) {
202
+ return null;
203
+ }
204
+ return (h("div", { class: "order-by-list" }, this.items.map((item, index) => this.renderItem(item, index))));
205
+ }
206
+ renderActions() {
207
+ return (h("div", { class: "actions" }, h("limel-button", { label: "Add Sort Field", icon: "plus_math", onClick: this.handleAddSortField })));
208
+ }
209
+ renderItem(item, index) {
210
+ const propertyPath = Object.keys(item)[0] || '';
211
+ return (h("limebb-lime-query-order-by-item", { key: `${propertyPath}-${index}`, class: "order-by-item", platform: this.platform, context: this.context, limetype: this.limetype, item: item, onItemChange: this.handleItemChange(index) }));
212
+ }
213
+ emitChange() {
214
+ this.change.emit(this.items);
215
+ }
216
+ static get watchers() { return {
217
+ "value": ["handleValueChange"]
218
+ }; }
219
+ };
220
+ OrderByEditor.style = LimebbLimeQueryOrderByEditorStyle0;
221
+
138
222
  const RELATION_PROPERTY_TYPES = [
139
223
  'belongsto',
140
224
  'hasone',
@@ -220,7 +304,7 @@ const LimetypeField = class {
220
304
  value: '',
221
305
  };
222
306
  options.unshift(emptyOption);
223
- return (h("limel-select", { key: 'cf27b83c4751838e85436af443a275b3d7c9dfee', label: this.label, options: options, value: selectedOption, required: this.required, helperText: this.helperText, invalid: invalid, disabled: this.disabled || this.readonly, onChange: this.handleChange }));
307
+ return (h("limel-select", { key: 'fd9522c5df730f65a5ac57140bca6c05c5acd63d', label: this.label, options: options, value: selectedOption, required: this.required, helperText: this.helperText, invalid: invalid, disabled: this.disabled || this.readonly, onChange: this.handleChange }));
224
308
  }
225
309
  componentWillRender() {
226
310
  this.updatePropertyFields(this.value || '');
@@ -499,4 +583,4 @@ const ResponseFormatEditor = class {
499
583
  };
500
584
  ResponseFormatEditor.style = LimebbResponseFormatEditorStyle0;
501
585
 
502
- export { LimeQueryFilterBuilderComponent as limebb_lime_query_filter_builder, LimetypeField as limebb_limetype_field, ResponseFormatEditor as limebb_response_format_editor };
586
+ export { LimeQueryFilterBuilderComponent as limebb_lime_query_filter_builder, OrderByEditor as limebb_lime_query_order_by_editor, LimetypeField as limebb_limetype_field, ResponseFormatEditor as limebb_response_format_editor };