@svgrid/enterprise 2.6.0 → 2.6.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.
- package/dist/ExpressionEditorHarness.svelte +47 -0
- package/dist/ExpressionEditorHarness.svelte.d.ts +15 -0
- package/dist/SvAdvancedFilter.svelte +208 -0
- package/dist/SvAdvancedFilter.svelte.d.ts +37 -0
- package/dist/SvExpressionEditor.svelte +255 -124
- package/dist/SvGridEditPanel.svelte +1157 -1012
- package/dist/SvGridEditPanel.svelte.d.ts +2 -0
- package/dist/advanced-filter/expr-columns-from-grid.d.ts +30 -0
- package/dist/advanced-filter/expr-columns-from-grid.js +44 -0
- package/dist/advanced-filter-enable.d.ts +5 -0
- package/dist/advanced-filter-enable.js +35 -0
- package/dist/cdn/svgrid-enterprise.svelte-external.js +7663 -6482
- package/dist/designer/assets/GridMenus-Bjr19Tz_.js +7 -0
- package/dist/designer/assets/{SvListBox-BVzVfCG3.js → SvListBox-BB8rt4aP.js} +1 -1
- package/dist/designer/assets/index-BfzoL904.css +1 -0
- package/dist/designer/assets/{index-BF0UH638.js → index-xjfKckuH.js} +139 -139
- package/dist/designer/assets/{js-scroller.svelte-tHanKJx0.js → js-scroller.svelte-CGkUUXwV.js} +2 -2
- package/dist/designer/assets/src-BT3LjWJL.css +1 -0
- package/dist/designer/assets/src-DDhAK0WP.js +2893 -0
- package/dist/designer/index.html +6 -6
- package/dist/expressions/builder-tree.d.ts +62 -0
- package/dist/expressions/builder-tree.js +133 -0
- package/dist/expressions/compile.d.ts +14 -0
- package/dist/expressions/compile.js +286 -0
- package/dist/expressions/expression-columns.d.ts +1 -11
- package/dist/expressions/expression-columns.js +40 -11
- package/dist/index.d.ts +7 -3
- package/dist/index.js +7 -3
- package/dist/install.js +2 -0
- package/dist/node/studio.js +743 -25
- package/dist/schema.d.ts +39 -0
- package/dist/schema.js +9 -0
- package/dist/sources/rest.js +25 -0
- package/dist/sources/supabase.js +49 -3
- package/dist/studio/emit-project.js +85 -11
- package/dist/studio/index.d.ts +2 -2
- package/dist/studio/index.js +5 -2
- package/dist/studio/project.d.ts +124 -3
- package/dist/studio/project.js +393 -4
- package/dist/studio/ui-components.generated.js +213 -0
- package/dist/sveltekit/in-memory.js +96 -3
- package/dist/sveltekit/query-plan.d.ts +32 -1
- package/dist/sveltekit/query-plan.js +88 -1
- package/dist/sveltekit/sql.d.ts +20 -0
- package/dist/sveltekit/sql.js +26 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +3 -3
- package/dist/designer/assets/GridMenus-CW-rnHS9.js +0 -7
- package/dist/designer/assets/index-oYok52zd.css +0 -1
- package/dist/designer/assets/src-BPJruwB9.js +0 -2873
- package/dist/designer/assets/src-D-O2F805.css +0 -1
|
@@ -509,6 +509,13 @@ export const GENERATED_UI_SURFACE = {
|
|
|
509
509
|
"description": "Marks the control invalid (`aria-invalid` + error styling).",
|
|
510
510
|
"group": "behavior"
|
|
511
511
|
},
|
|
512
|
+
{
|
|
513
|
+
"key": "block",
|
|
514
|
+
"label": "Block",
|
|
515
|
+
"type": "boolean",
|
|
516
|
+
"description": "Fill the container instead of the control's own default width. A default width is right for a control sitting on its own; in a form grid a row of inputs each stopping at a different width reads as broken.",
|
|
517
|
+
"group": "appearance"
|
|
518
|
+
},
|
|
512
519
|
{
|
|
513
520
|
"key": "error",
|
|
514
521
|
"label": "Error",
|
|
@@ -633,6 +640,13 @@ export const GENERATED_UI_SURFACE = {
|
|
|
633
640
|
"description": "Marks the control invalid (`aria-invalid` + error styling).",
|
|
634
641
|
"group": "behavior"
|
|
635
642
|
},
|
|
643
|
+
{
|
|
644
|
+
"key": "block",
|
|
645
|
+
"label": "Block",
|
|
646
|
+
"type": "boolean",
|
|
647
|
+
"description": "Fill the container instead of the control's own default width. A default width is right for a control sitting on its own; in a form grid a row of inputs each stopping at a different width reads as broken.",
|
|
648
|
+
"group": "appearance"
|
|
649
|
+
},
|
|
636
650
|
{
|
|
637
651
|
"key": "error",
|
|
638
652
|
"label": "Error",
|
|
@@ -770,6 +784,14 @@ export const GENERATED_UI_SURFACE = {
|
|
|
770
784
|
"description": "Marks the control invalid (`aria-invalid` + error styling).",
|
|
771
785
|
"group": "behavior"
|
|
772
786
|
},
|
|
787
|
+
{
|
|
788
|
+
"key": "block",
|
|
789
|
+
"label": "Block",
|
|
790
|
+
"type": "boolean",
|
|
791
|
+
"default": false,
|
|
792
|
+
"description": "Fill the container instead of the control's own default width. A default width is right for a control sitting on its own; in a form grid a row of inputs each stopping at a different width reads as broken.",
|
|
793
|
+
"group": "appearance"
|
|
794
|
+
},
|
|
773
795
|
{
|
|
774
796
|
"key": "error",
|
|
775
797
|
"label": "Error",
|
|
@@ -1003,6 +1025,13 @@ export const GENERATED_UI_SURFACE = {
|
|
|
1003
1025
|
"description": "Marks the control invalid (`aria-invalid` + error styling).",
|
|
1004
1026
|
"group": "behavior"
|
|
1005
1027
|
},
|
|
1028
|
+
{
|
|
1029
|
+
"key": "block",
|
|
1030
|
+
"label": "Block",
|
|
1031
|
+
"type": "boolean",
|
|
1032
|
+
"description": "Fill the container instead of the control's own default width. A default width is right for a control sitting on its own; in a form grid a row of inputs each stopping at a different width reads as broken.",
|
|
1033
|
+
"group": "appearance"
|
|
1034
|
+
},
|
|
1006
1035
|
{
|
|
1007
1036
|
"key": "error",
|
|
1008
1037
|
"label": "Error",
|
|
@@ -1178,6 +1207,14 @@ export const GENERATED_UI_SURFACE = {
|
|
|
1178
1207
|
"description": "Marks the control invalid (`aria-invalid` + error styling).",
|
|
1179
1208
|
"group": "behavior"
|
|
1180
1209
|
},
|
|
1210
|
+
{
|
|
1211
|
+
"key": "block",
|
|
1212
|
+
"label": "Block",
|
|
1213
|
+
"type": "boolean",
|
|
1214
|
+
"default": false,
|
|
1215
|
+
"description": "Fill the container instead of the control's own default width. A default width is right for a control sitting on its own; in a form grid a row of inputs each stopping at a different width reads as broken.",
|
|
1216
|
+
"group": "appearance"
|
|
1217
|
+
},
|
|
1181
1218
|
{
|
|
1182
1219
|
"key": "error",
|
|
1183
1220
|
"label": "Error",
|
|
@@ -1404,6 +1441,14 @@ export const GENERATED_UI_SURFACE = {
|
|
|
1404
1441
|
"description": "Marks the control invalid (`aria-invalid` + error styling).",
|
|
1405
1442
|
"group": "behavior"
|
|
1406
1443
|
},
|
|
1444
|
+
{
|
|
1445
|
+
"key": "block",
|
|
1446
|
+
"label": "Block",
|
|
1447
|
+
"type": "boolean",
|
|
1448
|
+
"default": false,
|
|
1449
|
+
"description": "Fill the container instead of the control's own default width. A default width is right for a control sitting on its own; in a form grid a row of inputs each stopping at a different width reads as broken.",
|
|
1450
|
+
"group": "appearance"
|
|
1451
|
+
},
|
|
1407
1452
|
{
|
|
1408
1453
|
"key": "error",
|
|
1409
1454
|
"label": "Error",
|
|
@@ -1580,6 +1625,14 @@ export const GENERATED_UI_SURFACE = {
|
|
|
1580
1625
|
"description": "Marks the control invalid (`aria-invalid` + error styling).",
|
|
1581
1626
|
"group": "behavior"
|
|
1582
1627
|
},
|
|
1628
|
+
{
|
|
1629
|
+
"key": "block",
|
|
1630
|
+
"label": "Block",
|
|
1631
|
+
"type": "boolean",
|
|
1632
|
+
"default": false,
|
|
1633
|
+
"description": "Fill the container instead of the control's own default width. A default width is right for a control sitting on its own; in a form grid a row of inputs each stopping at a different width reads as broken.",
|
|
1634
|
+
"group": "appearance"
|
|
1635
|
+
},
|
|
1583
1636
|
{
|
|
1584
1637
|
"key": "error",
|
|
1585
1638
|
"label": "Error",
|
|
@@ -1733,6 +1786,13 @@ export const GENERATED_UI_SURFACE = {
|
|
|
1733
1786
|
"description": "Marks the control invalid (`aria-invalid` + error styling).",
|
|
1734
1787
|
"group": "behavior"
|
|
1735
1788
|
},
|
|
1789
|
+
{
|
|
1790
|
+
"key": "block",
|
|
1791
|
+
"label": "Block",
|
|
1792
|
+
"type": "boolean",
|
|
1793
|
+
"description": "Fill the container instead of the control's own default width. A default width is right for a control sitting on its own; in a form grid a row of inputs each stopping at a different width reads as broken.",
|
|
1794
|
+
"group": "appearance"
|
|
1795
|
+
},
|
|
1736
1796
|
{
|
|
1737
1797
|
"key": "error",
|
|
1738
1798
|
"label": "Error",
|
|
@@ -1870,6 +1930,13 @@ export const GENERATED_UI_SURFACE = {
|
|
|
1870
1930
|
"description": "Marks the control invalid (`aria-invalid` + error styling).",
|
|
1871
1931
|
"group": "behavior"
|
|
1872
1932
|
},
|
|
1933
|
+
{
|
|
1934
|
+
"key": "block",
|
|
1935
|
+
"label": "Block",
|
|
1936
|
+
"type": "boolean",
|
|
1937
|
+
"description": "Fill the container instead of the control's own default width. A default width is right for a control sitting on its own; in a form grid a row of inputs each stopping at a different width reads as broken.",
|
|
1938
|
+
"group": "appearance"
|
|
1939
|
+
},
|
|
1873
1940
|
{
|
|
1874
1941
|
"key": "error",
|
|
1875
1942
|
"label": "Error",
|
|
@@ -2023,6 +2090,14 @@ export const GENERATED_UI_SURFACE = {
|
|
|
2023
2090
|
"description": "Marks the control invalid (`aria-invalid` + error styling).",
|
|
2024
2091
|
"group": "behavior"
|
|
2025
2092
|
},
|
|
2093
|
+
{
|
|
2094
|
+
"key": "block",
|
|
2095
|
+
"label": "Block",
|
|
2096
|
+
"type": "boolean",
|
|
2097
|
+
"default": false,
|
|
2098
|
+
"description": "Fill the container instead of the control's own default width. A default width is right for a control sitting on its own; in a form grid a row of inputs each stopping at a different width reads as broken.",
|
|
2099
|
+
"group": "appearance"
|
|
2100
|
+
},
|
|
2026
2101
|
{
|
|
2027
2102
|
"key": "error",
|
|
2028
2103
|
"label": "Error",
|
|
@@ -2767,6 +2842,14 @@ export const GENERATED_UI_SURFACE = {
|
|
|
2767
2842
|
"description": "Marks the control invalid (`aria-invalid` + error styling).",
|
|
2768
2843
|
"group": "behavior"
|
|
2769
2844
|
},
|
|
2845
|
+
{
|
|
2846
|
+
"key": "block",
|
|
2847
|
+
"label": "Block",
|
|
2848
|
+
"type": "boolean",
|
|
2849
|
+
"default": false,
|
|
2850
|
+
"description": "Fill the container instead of the control's own default width. A default width is right for a control sitting on its own; in a form grid a row of inputs each stopping at a different width reads as broken.",
|
|
2851
|
+
"group": "appearance"
|
|
2852
|
+
},
|
|
2770
2853
|
{
|
|
2771
2854
|
"key": "error",
|
|
2772
2855
|
"label": "Error",
|
|
@@ -2943,6 +3026,14 @@ export const GENERATED_UI_SURFACE = {
|
|
|
2943
3026
|
"description": "Marks the control invalid (`aria-invalid` + error styling).",
|
|
2944
3027
|
"group": "behavior"
|
|
2945
3028
|
},
|
|
3029
|
+
{
|
|
3030
|
+
"key": "block",
|
|
3031
|
+
"label": "Block",
|
|
3032
|
+
"type": "boolean",
|
|
3033
|
+
"default": false,
|
|
3034
|
+
"description": "Fill the container instead of the control's own default width. A default width is right for a control sitting on its own; in a form grid a row of inputs each stopping at a different width reads as broken.",
|
|
3035
|
+
"group": "appearance"
|
|
3036
|
+
},
|
|
2946
3037
|
{
|
|
2947
3038
|
"key": "error",
|
|
2948
3039
|
"label": "Error",
|
|
@@ -3104,6 +3195,14 @@ export const GENERATED_UI_SURFACE = {
|
|
|
3104
3195
|
"description": "Marks the control invalid (`aria-invalid` + error styling).",
|
|
3105
3196
|
"group": "behavior"
|
|
3106
3197
|
},
|
|
3198
|
+
{
|
|
3199
|
+
"key": "block",
|
|
3200
|
+
"label": "Block",
|
|
3201
|
+
"type": "boolean",
|
|
3202
|
+
"default": false,
|
|
3203
|
+
"description": "Fill the container instead of the control's own default width. A default width is right for a control sitting on its own; in a form grid a row of inputs each stopping at a different width reads as broken.",
|
|
3204
|
+
"group": "appearance"
|
|
3205
|
+
},
|
|
3107
3206
|
{
|
|
3108
3207
|
"key": "error",
|
|
3109
3208
|
"label": "Error",
|
|
@@ -3255,6 +3354,14 @@ export const GENERATED_UI_SURFACE = {
|
|
|
3255
3354
|
"description": "Marks the control invalid (`aria-invalid` + error styling).",
|
|
3256
3355
|
"group": "behavior"
|
|
3257
3356
|
},
|
|
3357
|
+
{
|
|
3358
|
+
"key": "block",
|
|
3359
|
+
"label": "Block",
|
|
3360
|
+
"type": "boolean",
|
|
3361
|
+
"default": false,
|
|
3362
|
+
"description": "Fill the container instead of the control's own default width. A default width is right for a control sitting on its own; in a form grid a row of inputs each stopping at a different width reads as broken.",
|
|
3363
|
+
"group": "appearance"
|
|
3364
|
+
},
|
|
3258
3365
|
{
|
|
3259
3366
|
"key": "error",
|
|
3260
3367
|
"label": "Error",
|
|
@@ -3440,6 +3547,13 @@ export const GENERATED_UI_SURFACE = {
|
|
|
3440
3547
|
"description": "Marks the control invalid (`aria-invalid` + error styling).",
|
|
3441
3548
|
"group": "behavior"
|
|
3442
3549
|
},
|
|
3550
|
+
{
|
|
3551
|
+
"key": "block",
|
|
3552
|
+
"label": "Block",
|
|
3553
|
+
"type": "boolean",
|
|
3554
|
+
"description": "Fill the container instead of the control's own default width. A default width is right for a control sitting on its own; in a form grid a row of inputs each stopping at a different width reads as broken.",
|
|
3555
|
+
"group": "appearance"
|
|
3556
|
+
},
|
|
3443
3557
|
{
|
|
3444
3558
|
"key": "error",
|
|
3445
3559
|
"label": "Error",
|
|
@@ -3579,6 +3693,13 @@ export const GENERATED_UI_SURFACE = {
|
|
|
3579
3693
|
"description": "Marks the control invalid (`aria-invalid` + error styling).",
|
|
3580
3694
|
"group": "behavior"
|
|
3581
3695
|
},
|
|
3696
|
+
{
|
|
3697
|
+
"key": "block",
|
|
3698
|
+
"label": "Block",
|
|
3699
|
+
"type": "boolean",
|
|
3700
|
+
"description": "Fill the container instead of the control's own default width. A default width is right for a control sitting on its own; in a form grid a row of inputs each stopping at a different width reads as broken.",
|
|
3701
|
+
"group": "appearance"
|
|
3702
|
+
},
|
|
3582
3703
|
{
|
|
3583
3704
|
"key": "error",
|
|
3584
3705
|
"label": "Error",
|
|
@@ -3738,6 +3859,14 @@ export const GENERATED_UI_SURFACE = {
|
|
|
3738
3859
|
"description": "Marks the control invalid (`aria-invalid` + error styling).",
|
|
3739
3860
|
"group": "behavior"
|
|
3740
3861
|
},
|
|
3862
|
+
{
|
|
3863
|
+
"key": "block",
|
|
3864
|
+
"label": "Block",
|
|
3865
|
+
"type": "boolean",
|
|
3866
|
+
"default": false,
|
|
3867
|
+
"description": "Fill the container instead of the control's own default width. A default width is right for a control sitting on its own; in a form grid a row of inputs each stopping at a different width reads as broken.",
|
|
3868
|
+
"group": "appearance"
|
|
3869
|
+
},
|
|
3741
3870
|
{
|
|
3742
3871
|
"key": "error",
|
|
3743
3872
|
"label": "Error",
|
|
@@ -3922,6 +4051,13 @@ export const GENERATED_UI_SURFACE = {
|
|
|
3922
4051
|
"description": "Marks the control invalid (`aria-invalid` + error styling).",
|
|
3923
4052
|
"group": "behavior"
|
|
3924
4053
|
},
|
|
4054
|
+
{
|
|
4055
|
+
"key": "block",
|
|
4056
|
+
"label": "Block",
|
|
4057
|
+
"type": "boolean",
|
|
4058
|
+
"description": "Fill the container instead of the control's own default width. A default width is right for a control sitting on its own; in a form grid a row of inputs each stopping at a different width reads as broken.",
|
|
4059
|
+
"group": "appearance"
|
|
4060
|
+
},
|
|
3925
4061
|
{
|
|
3926
4062
|
"key": "error",
|
|
3927
4063
|
"label": "Error",
|
|
@@ -4105,6 +4241,13 @@ export const GENERATED_UI_SURFACE = {
|
|
|
4105
4241
|
"description": "Marks the control invalid (`aria-invalid` + error styling).",
|
|
4106
4242
|
"group": "behavior"
|
|
4107
4243
|
},
|
|
4244
|
+
{
|
|
4245
|
+
"key": "block",
|
|
4246
|
+
"label": "Block",
|
|
4247
|
+
"type": "boolean",
|
|
4248
|
+
"description": "Fill the container instead of the control's own default width. A default width is right for a control sitting on its own; in a form grid a row of inputs each stopping at a different width reads as broken.",
|
|
4249
|
+
"group": "appearance"
|
|
4250
|
+
},
|
|
4108
4251
|
{
|
|
4109
4252
|
"key": "error",
|
|
4110
4253
|
"label": "Error",
|
|
@@ -4294,6 +4437,13 @@ export const GENERATED_UI_SURFACE = {
|
|
|
4294
4437
|
"description": "Marks the control invalid (`aria-invalid` + error styling).",
|
|
4295
4438
|
"group": "behavior"
|
|
4296
4439
|
},
|
|
4440
|
+
{
|
|
4441
|
+
"key": "block",
|
|
4442
|
+
"label": "Block",
|
|
4443
|
+
"type": "boolean",
|
|
4444
|
+
"description": "Fill the container instead of the control's own default width. A default width is right for a control sitting on its own; in a form grid a row of inputs each stopping at a different width reads as broken.",
|
|
4445
|
+
"group": "appearance"
|
|
4446
|
+
},
|
|
4297
4447
|
{
|
|
4298
4448
|
"key": "error",
|
|
4299
4449
|
"label": "Error",
|
|
@@ -4864,6 +5014,14 @@ export const GENERATED_UI_SURFACE = {
|
|
|
4864
5014
|
"description": "Marks the control invalid (`aria-invalid` + error styling).",
|
|
4865
5015
|
"group": "behavior"
|
|
4866
5016
|
},
|
|
5017
|
+
{
|
|
5018
|
+
"key": "block",
|
|
5019
|
+
"label": "Block",
|
|
5020
|
+
"type": "boolean",
|
|
5021
|
+
"default": false,
|
|
5022
|
+
"description": "Fill the container instead of the control's own default width. A default width is right for a control sitting on its own; in a form grid a row of inputs each stopping at a different width reads as broken.",
|
|
5023
|
+
"group": "appearance"
|
|
5024
|
+
},
|
|
4867
5025
|
{
|
|
4868
5026
|
"key": "error",
|
|
4869
5027
|
"label": "Error",
|
|
@@ -5555,6 +5713,14 @@ export const GENERATED_UI_SURFACE = {
|
|
|
5555
5713
|
"description": "Marks the control invalid (`aria-invalid` + error styling).",
|
|
5556
5714
|
"group": "behavior"
|
|
5557
5715
|
},
|
|
5716
|
+
{
|
|
5717
|
+
"key": "block",
|
|
5718
|
+
"label": "Block",
|
|
5719
|
+
"type": "boolean",
|
|
5720
|
+
"default": false,
|
|
5721
|
+
"description": "Fill the container instead of the control's own default width. A default width is right for a control sitting on its own; in a form grid a row of inputs each stopping at a different width reads as broken.",
|
|
5722
|
+
"group": "appearance"
|
|
5723
|
+
},
|
|
5558
5724
|
{
|
|
5559
5725
|
"key": "error",
|
|
5560
5726
|
"label": "Error",
|
|
@@ -5964,6 +6130,13 @@ export const GENERATED_UI_SURFACE = {
|
|
|
5964
6130
|
"description": "Marks the control invalid (`aria-invalid` + error styling).",
|
|
5965
6131
|
"group": "behavior"
|
|
5966
6132
|
},
|
|
6133
|
+
{
|
|
6134
|
+
"key": "block",
|
|
6135
|
+
"label": "Block",
|
|
6136
|
+
"type": "boolean",
|
|
6137
|
+
"description": "Fill the container instead of the control's own default width. A default width is right for a control sitting on its own; in a form grid a row of inputs each stopping at a different width reads as broken.",
|
|
6138
|
+
"group": "appearance"
|
|
6139
|
+
},
|
|
5967
6140
|
{
|
|
5968
6141
|
"key": "error",
|
|
5969
6142
|
"label": "Error",
|
|
@@ -6267,6 +6440,13 @@ export const GENERATED_UI_SURFACE = {
|
|
|
6267
6440
|
"description": "Marks the control invalid (`aria-invalid` + error styling).",
|
|
6268
6441
|
"group": "behavior"
|
|
6269
6442
|
},
|
|
6443
|
+
{
|
|
6444
|
+
"key": "block",
|
|
6445
|
+
"label": "Block",
|
|
6446
|
+
"type": "boolean",
|
|
6447
|
+
"description": "Fill the container instead of the control's own default width. A default width is right for a control sitting on its own; in a form grid a row of inputs each stopping at a different width reads as broken.",
|
|
6448
|
+
"group": "appearance"
|
|
6449
|
+
},
|
|
6270
6450
|
{
|
|
6271
6451
|
"key": "error",
|
|
6272
6452
|
"label": "Error",
|
|
@@ -6487,6 +6667,20 @@ export const GENERATED_UI_SURFACE = {
|
|
|
6487
6667
|
"type": "boolean",
|
|
6488
6668
|
"group": "common"
|
|
6489
6669
|
},
|
|
6670
|
+
{
|
|
6671
|
+
"key": "groupBy",
|
|
6672
|
+
"label": "Group By",
|
|
6673
|
+
"type": "json",
|
|
6674
|
+
"description": "Group the rows by these column ids, outermost first - `['region', 'country']` rolls country up inside region. ```svelte <SvGrid {data} {columns} {features} groupBy={['department']} /> ``` The prop seeds the group-by list and re-applies whenever it changes, so it works both as initial state and as a controlled value. The column menu and `api.setGroupBy()` write the same state; a change from either survives until this prop's own value changes. Ignored when `treeData` is set - a row cannot be both a hierarchy node and bucketed under a group banner.",
|
|
6675
|
+
"group": "common"
|
|
6676
|
+
},
|
|
6677
|
+
{
|
|
6678
|
+
"key": "expanded",
|
|
6679
|
+
"label": "Expanded",
|
|
6680
|
+
"type": "json",
|
|
6681
|
+
"description": "Which group / tree rows are expanded, keyed by row id. Expansion is owned by the engine by default; pass this prop to hoist it (saved views, \"expand everything on load\", persisted UI state). Like `groupBy` it seeds the state and re-applies whenever it changes. Group row ids are built from the grouping path, not the display label: `group_department_Engineering`, and one level deeper `group_department_Engineering_role_Senior`. Tree rows key off the engine's row id instead (set `getRowId` to make that your own id). Prefer capturing the map from `onExpandedChange` over hand-building these keys.",
|
|
6682
|
+
"group": "common"
|
|
6683
|
+
},
|
|
6490
6684
|
{
|
|
6491
6685
|
"key": "groupFooters",
|
|
6492
6686
|
"label": "Group Footers",
|
|
@@ -6889,6 +7083,13 @@ export const GENERATED_UI_SURFACE = {
|
|
|
6889
7083
|
"description": "The sort state to start in - `{ id, desc }` entries. Use with `externalSort` when the initial ordering is decided server-side (e.g. a URL `?sort=` param read in a SvelteKit `load`), so the header sort indicators match the already- sorted data on first render. Applied once at mount; user sorting takes over.",
|
|
6890
7084
|
"group": "common"
|
|
6891
7085
|
},
|
|
7086
|
+
{
|
|
7087
|
+
"key": "initialAdvancedFilter",
|
|
7088
|
+
"label": "Initial Advanced Filter",
|
|
7089
|
+
"type": "json",
|
|
7090
|
+
"description": "The advanced-filter expression to start in (Pro). Applied once at mount; change it afterwards through `api.setAdvancedFilter()`. Filtering only happens once `@svgrid/enterprise`'s `enableAdvancedFilter()` has registered a compiler - without it the expression is stored and reported but no rows are removed, so the free grid degrades to a no-op rather than to a wrong row set.",
|
|
7091
|
+
"group": "common"
|
|
7092
|
+
},
|
|
6892
7093
|
{
|
|
6893
7094
|
"key": "externalFilter",
|
|
6894
7095
|
"label": "External Filter",
|
|
@@ -7039,6 +7240,12 @@ export const GENERATED_UI_SURFACE = {
|
|
|
7039
7240
|
"prop": "onPivotModeChange",
|
|
7040
7241
|
"description": "Fired when the in-grid Pivot toggle flips `pivotMode`."
|
|
7041
7242
|
},
|
|
7243
|
+
{
|
|
7244
|
+
"key": "expandedChange",
|
|
7245
|
+
"label": "Expanded Change",
|
|
7246
|
+
"prop": "onExpandedChange",
|
|
7247
|
+
"description": "Fired whenever the expanded set changes - chevron clicks, `api.setRowExpanded()`, `expandAllGroups()` / `collapseAllGroups()`. Receives the full next map, so it can be written straight back into `expanded` for a controlled setup."
|
|
7248
|
+
},
|
|
7042
7249
|
{
|
|
7043
7250
|
"key": "paginationChange",
|
|
7044
7251
|
"label": "Pagination Change",
|
|
@@ -7069,6 +7276,12 @@ export const GENERATED_UI_SURFACE = {
|
|
|
7069
7276
|
"prop": "onSortingChange",
|
|
7070
7277
|
"description": "Fires whenever the sort clauses change. Receives the new array of `{ id, desc }` entries. Pair with `externalSort={true}` when the consumer wants to own the row ordering."
|
|
7071
7278
|
},
|
|
7279
|
+
{
|
|
7280
|
+
"key": "advancedFilterChange",
|
|
7281
|
+
"label": "Advanced Filter Change",
|
|
7282
|
+
"prop": "onAdvancedFilterChange",
|
|
7283
|
+
"description": "Fires whenever the advanced-filter expression changes, whatever changed it: `api.setAdvancedFilter()`, `clearAllFilters()`, or the toolbar's clear control. The panel that authors the expression is mounted by you, outside the grid, so it cannot see a change the grid made on its own. Without this, clearing from the toolbar leaves that panel showing a filter the grid is no longer applying. Feed this back into the panel's `expression` prop to keep them agreeing."
|
|
7284
|
+
},
|
|
7072
7285
|
{
|
|
7073
7286
|
"key": "filtersChange",
|
|
7074
7287
|
"label": "Filters Change",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { resolveIdField } from '../schema';
|
|
2
2
|
import { planQuery } from './query-plan';
|
|
3
|
+
import { compilePredicate } from '../expressions/compile';
|
|
3
4
|
import { aggregateRows } from '../sources/aggregate';
|
|
4
5
|
function asString(v) {
|
|
5
6
|
return v == null ? '' : String(v);
|
|
@@ -43,6 +44,39 @@ function matches(row, plan) {
|
|
|
43
44
|
}
|
|
44
45
|
return plan.where.every((p) => matchesPredicate(row, p));
|
|
45
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* Reduce one already-bucketed group to a single aggregate value.
|
|
49
|
+
*
|
|
50
|
+
* Semantics deliberately mirror the SQL `planToSql` emits, so the in-memory
|
|
51
|
+
* source and a real database agree:
|
|
52
|
+
* - `count` is COUNT(*): rows in the group, including null-valued ones.
|
|
53
|
+
* - the rest ignore non-numeric / null values, and yield null for an empty
|
|
54
|
+
* numeric set (SQL returns NULL for SUM/AVG/MIN/MAX over no rows).
|
|
55
|
+
*/
|
|
56
|
+
function aggregate(rows, field, fn) {
|
|
57
|
+
if (fn === 'count')
|
|
58
|
+
return rows.length;
|
|
59
|
+
const nums = [];
|
|
60
|
+
for (const row of rows) {
|
|
61
|
+
const n = Number(row[field]);
|
|
62
|
+
if (Number.isFinite(n))
|
|
63
|
+
nums.push(n);
|
|
64
|
+
}
|
|
65
|
+
if (nums.length === 0)
|
|
66
|
+
return null;
|
|
67
|
+
switch (fn) {
|
|
68
|
+
case 'sum':
|
|
69
|
+
return nums.reduce((s, n) => s + n, 0);
|
|
70
|
+
case 'avg':
|
|
71
|
+
return nums.reduce((s, n) => s + n, 0) / nums.length;
|
|
72
|
+
case 'min':
|
|
73
|
+
return Math.min(...nums);
|
|
74
|
+
case 'max':
|
|
75
|
+
return Math.max(...nums);
|
|
76
|
+
default:
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
46
80
|
function sortRows(rows, orderBy) {
|
|
47
81
|
if (orderBy.length === 0)
|
|
48
82
|
return rows;
|
|
@@ -71,10 +105,69 @@ export function createInMemoryDataSource(initial, schema) {
|
|
|
71
105
|
return {
|
|
72
106
|
async getRows(request) {
|
|
73
107
|
const plan = planQuery(schema, request);
|
|
74
|
-
|
|
108
|
+
let filtered = store.filter((r) => matches(r, plan));
|
|
109
|
+
// Advanced filter. `planQuery` only admits an expression whose columns
|
|
110
|
+
// are all on the schema, so reaching here means it is safe to run. If it
|
|
111
|
+
// was rejected, or fails to compile, we must NOT acknowledge it - the
|
|
112
|
+
// grid then tells the user the filter did not run rather than showing a
|
|
113
|
+
// superset that looks filtered.
|
|
114
|
+
let appliedExpression = false;
|
|
115
|
+
if (plan.expression) {
|
|
116
|
+
const predicate = compilePredicate(plan.expression, {
|
|
117
|
+
getValue: (row, columnId) => row[columnId],
|
|
118
|
+
rows: filtered,
|
|
119
|
+
});
|
|
120
|
+
if (predicate) {
|
|
121
|
+
filtered = filtered.filter(predicate);
|
|
122
|
+
appliedExpression = true;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
// Grouped request: return one row per distinct key at this level, each
|
|
126
|
+
// carrying the requested aggregates. The chosen path is already in
|
|
127
|
+
// `plan.where`, so `filtered` is exactly this group's slice of the data.
|
|
128
|
+
// Reference behaviour for what a SQL backend does with `planToSql`'s
|
|
129
|
+
// `select` / `groupByText` / `countText`.
|
|
130
|
+
if (plan.groupBy) {
|
|
131
|
+
const groupField = plan.groupBy;
|
|
132
|
+
const buckets = new Map();
|
|
133
|
+
for (const row of filtered) {
|
|
134
|
+
const key = asString(row[groupField]);
|
|
135
|
+
const bucket = buckets.get(key);
|
|
136
|
+
if (bucket)
|
|
137
|
+
bucket.push(row);
|
|
138
|
+
else
|
|
139
|
+
buckets.set(key, [row]);
|
|
140
|
+
}
|
|
141
|
+
const groups = [...buckets.entries()].map(([key, rows]) => {
|
|
142
|
+
const out = { [groupField]: key };
|
|
143
|
+
for (const agg of plan.aggregations ?? []) {
|
|
144
|
+
out[agg.field] = aggregate(rows, agg.field, agg.fn);
|
|
145
|
+
}
|
|
146
|
+
return out;
|
|
147
|
+
});
|
|
148
|
+
// Sort by the group key unless the request sorted on a column we
|
|
149
|
+
// actually produced (the key or an aggregate).
|
|
150
|
+
const produced = new Set([
|
|
151
|
+
groupField,
|
|
152
|
+
...(plan.aggregations ?? []).map((a) => a.field),
|
|
153
|
+
]);
|
|
154
|
+
const groupOrder = plan.orderBy.filter((o) => produced.has(o.field));
|
|
155
|
+
const sorted = groupOrder.length
|
|
156
|
+
? sortRows(groups, groupOrder)
|
|
157
|
+
: sortRows(groups, [{ field: groupField, desc: false }]);
|
|
158
|
+
return {
|
|
159
|
+
rows: sorted.slice(plan.offset, plan.offset + plan.limit),
|
|
160
|
+
// The count is DISTINCT GROUPS, not underlying rows - the grid sizes
|
|
161
|
+
// its scrollbar and paging from this.
|
|
162
|
+
rowCount: sorted.length,
|
|
163
|
+
appliedExpression,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
const sortedLeaves = sortRows(filtered, plan.orderBy);
|
|
75
167
|
return {
|
|
76
|
-
rows:
|
|
77
|
-
rowCount:
|
|
168
|
+
rows: sortedLeaves.slice(plan.offset, plan.offset + plan.limit),
|
|
169
|
+
rowCount: sortedLeaves.length,
|
|
170
|
+
appliedExpression,
|
|
78
171
|
};
|
|
79
172
|
},
|
|
80
173
|
async createRow(input) {
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* Unknown column names in the request are dropped, so a client cannot smuggle
|
|
11
11
|
* arbitrary identifiers through to SQL.
|
|
12
12
|
*/
|
|
13
|
-
import type { RowData, ServerRequest } from '@svgrid/grid';
|
|
13
|
+
import type { GridPredicateExpr, RowData, ServerRequest } from '@svgrid/grid';
|
|
14
14
|
import type { EntityFieldType, EntitySchema } from '../schema';
|
|
15
15
|
/** Normalized predicate operators (a stable superset of the grid's filter operators). */
|
|
16
16
|
export type PlanOp = 'eq' | 'contains' | 'startsWith' | 'gt' | 'lt' | 'between' | 'isBlank' | 'in';
|
|
@@ -24,6 +24,11 @@ export type PlanPredicate = {
|
|
|
24
24
|
/** Operands for `in` (set / facet filter). */
|
|
25
25
|
values?: unknown[];
|
|
26
26
|
};
|
|
27
|
+
/** An aggregate to compute per group. */
|
|
28
|
+
export type PlanAggregate = {
|
|
29
|
+
field: string;
|
|
30
|
+
fn: 'sum' | 'avg' | 'min' | 'max' | 'count';
|
|
31
|
+
};
|
|
27
32
|
export type QueryPlan = {
|
|
28
33
|
/** AND-combined column predicates. */
|
|
29
34
|
where: PlanPredicate[];
|
|
@@ -38,6 +43,32 @@ export type QueryPlan = {
|
|
|
38
43
|
}>;
|
|
39
44
|
limit: number;
|
|
40
45
|
offset: number;
|
|
46
|
+
/**
|
|
47
|
+
* The column to GROUP BY for this request, or null for a flat / leaf query.
|
|
48
|
+
*
|
|
49
|
+
* Server-side grouping is one level at a time: the grid asks for the distinct
|
|
50
|
+
* keys at the current level, and expanding a group is another request with
|
|
51
|
+
* that key appended to `groupKeys`. The path already chosen arrives as
|
|
52
|
+
* ordinary equality predicates in `where`, so a backend only ever has to
|
|
53
|
+
* handle "filter, then group by one column".
|
|
54
|
+
*
|
|
55
|
+
* Optional so a hand-built plan (and every plan written before server-side
|
|
56
|
+
* grouping existed) stays valid; `planQuery` always sets it.
|
|
57
|
+
*/
|
|
58
|
+
groupBy?: string | null;
|
|
59
|
+
/** Aggregates to compute alongside `groupBy`. Empty/absent for a leaf query. */
|
|
60
|
+
aggregations?: PlanAggregate[];
|
|
61
|
+
/**
|
|
62
|
+
* The advanced-filter expression, present ONLY when every column it
|
|
63
|
+
* references is on the schema.
|
|
64
|
+
*
|
|
65
|
+
* Rejected whole rather than in part. Dropping one clause of an AND makes the
|
|
66
|
+
* result broader, which for a filter means showing rows the user excluded -
|
|
67
|
+
* so a half-understood expression is worse than an ignored one. When this is
|
|
68
|
+
* absent but the request carried an expression, the backend must not
|
|
69
|
+
* acknowledge it (see `ServerResult.appliedExpression`).
|
|
70
|
+
*/
|
|
71
|
+
expression?: GridPredicateExpr;
|
|
41
72
|
};
|
|
42
73
|
/** Coerce a raw string request value to the field's runtime type for correct comparisons. */
|
|
43
74
|
export declare function coerce(type: EntityFieldType, raw: unknown): unknown;
|
|
@@ -72,5 +72,92 @@ export function planQuery(schema, request) {
|
|
|
72
72
|
const orderBy = (request.sortModel ?? [])
|
|
73
73
|
.filter((s) => fields.has(s.id))
|
|
74
74
|
.map((s) => ({ field: s.id, desc: !!s.desc }));
|
|
75
|
-
|
|
75
|
+
// ---- Server-side grouping ---------------------------------------------
|
|
76
|
+
// Same whitelist rule as the filters above: only schema fields reach the
|
|
77
|
+
// plan, so a client cannot group by or aggregate an arbitrary identifier.
|
|
78
|
+
const groupCols = (request.groupBy ?? []).filter((f) => fields.has(f));
|
|
79
|
+
const groupKeys = request.groupKeys ?? [];
|
|
80
|
+
// The already-chosen path becomes plain equality predicates, so the backend
|
|
81
|
+
// only ever sees "filter, then group by one column".
|
|
82
|
+
for (let i = 0; i < groupKeys.length && i < groupCols.length; i += 1) {
|
|
83
|
+
const field = groupCols[i];
|
|
84
|
+
const def = fields.get(field);
|
|
85
|
+
where.push({ field, op: 'eq', value: coerce(def.type, groupKeys[i]) });
|
|
86
|
+
}
|
|
87
|
+
// Above the innermost level the grid wants GROUP rows; at or below it, leaves.
|
|
88
|
+
const groupBy = groupKeys.length < groupCols.length ? (groupCols[groupKeys.length] ?? null) : null;
|
|
89
|
+
const aggregations = groupBy
|
|
90
|
+
? (request.aggregations ?? [])
|
|
91
|
+
.filter((a) => fields.has(a.col))
|
|
92
|
+
.map((a) => ({ field: a.col, fn: a.fn }))
|
|
93
|
+
: [];
|
|
94
|
+
// ---- Advanced filter ---------------------------------------------------
|
|
95
|
+
// Same whitelist, stricter consequence: if ANY referenced column is off the
|
|
96
|
+
// schema the whole expression is dropped. Keeping the understood parts would
|
|
97
|
+
// silently widen the result set.
|
|
98
|
+
const expression = admitExpression(request.filterModel?.expression, fields);
|
|
99
|
+
return {
|
|
100
|
+
where,
|
|
101
|
+
search,
|
|
102
|
+
orderBy,
|
|
103
|
+
limit: request.pageSize,
|
|
104
|
+
offset: request.startRow,
|
|
105
|
+
groupBy,
|
|
106
|
+
aggregations,
|
|
107
|
+
...(expression ? { expression } : {}),
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
/** Every column id an expression reads, so it can be whitelisted as a unit. */
|
|
111
|
+
function expressionColumns(expr, out = new Set()) {
|
|
112
|
+
const scalar = (s) => {
|
|
113
|
+
switch (s.kind) {
|
|
114
|
+
case 'col':
|
|
115
|
+
out.add(s.id);
|
|
116
|
+
break;
|
|
117
|
+
case 'agg':
|
|
118
|
+
out.add(s.column);
|
|
119
|
+
break;
|
|
120
|
+
case 'neg':
|
|
121
|
+
scalar(s.expr);
|
|
122
|
+
break;
|
|
123
|
+
case 'bin':
|
|
124
|
+
scalar(s.left);
|
|
125
|
+
scalar(s.right);
|
|
126
|
+
break;
|
|
127
|
+
case 'func':
|
|
128
|
+
s.args.forEach(scalar);
|
|
129
|
+
break;
|
|
130
|
+
default:
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
switch (expr.kind) {
|
|
135
|
+
case 'and':
|
|
136
|
+
case 'or':
|
|
137
|
+
expr.parts.forEach((p) => expressionColumns(p, out));
|
|
138
|
+
break;
|
|
139
|
+
case 'not':
|
|
140
|
+
expressionColumns(expr.expr, out);
|
|
141
|
+
break;
|
|
142
|
+
case 'cmp':
|
|
143
|
+
out.add(expr.column);
|
|
144
|
+
break;
|
|
145
|
+
case 'scalarCmp':
|
|
146
|
+
scalar(expr.left);
|
|
147
|
+
scalar(expr.right);
|
|
148
|
+
break;
|
|
149
|
+
default:
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
return out;
|
|
153
|
+
}
|
|
154
|
+
/** Return the expression only when every column it touches is on the schema. */
|
|
155
|
+
function admitExpression(expr, fields) {
|
|
156
|
+
if (!expr)
|
|
157
|
+
return undefined;
|
|
158
|
+
for (const col of expressionColumns(expr)) {
|
|
159
|
+
if (!fields.has(col))
|
|
160
|
+
return undefined;
|
|
161
|
+
}
|
|
162
|
+
return expr;
|
|
76
163
|
}
|