@react-querybuilder/core 8.21.2 → 8.22.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.
- package/README.md +1 -1
- package/dist/cjs/react-querybuilder_core.cjs.development.d.ts +1389 -48
- package/dist/cjs/react-querybuilder_core.cjs.development.js +2214 -85
- package/dist/cjs/react-querybuilder_core.cjs.development.js.map +1 -1
- package/dist/cjs/react-querybuilder_core.cjs.production.d.ts +1389 -48
- package/dist/cjs/react-querybuilder_core.cjs.production.js +1 -1
- package/dist/cjs/react-querybuilder_core.cjs.production.js.map +1 -1
- package/dist/formatQuery.js +47 -38
- package/dist/formatQuery.js.map +1 -1
- package/dist/formatQuery.mjs +47 -38
- package/dist/formatQuery.mjs.map +1 -1
- package/dist/parseCEL.js +6 -8
- package/dist/parseCEL.js.map +1 -1
- package/dist/parseCEL.mjs +6 -8
- package/dist/parseCEL.mjs.map +1 -1
- package/dist/parseCypher.js.map +1 -1
- package/dist/parseJSONata.js +2 -2
- package/dist/parseJSONata.js.map +1 -1
- package/dist/parseJSONata.mjs +1 -1
- package/dist/parseJsonLogic.js +3 -3
- package/dist/parseJsonLogic.js.map +1 -1
- package/dist/parseJsonLogic.mjs +3 -3
- package/dist/parseJsonLogic.mjs.map +1 -1
- package/dist/parseMongoDB.js +2 -2
- package/dist/parseMongoDB.mjs +2 -2
- package/dist/parseSPARQL.js +0 -1
- package/dist/parseSPARQL.js.map +1 -1
- package/dist/parseSPARQL.mjs +0 -1
- package/dist/parseSPARQL.mjs.map +1 -1
- package/dist/parseSQL.js +54 -62
- package/dist/parseSQL.js.map +1 -1
- package/dist/parseSQL.mjs +54 -62
- package/dist/parseSQL.mjs.map +1 -1
- package/dist/parseSpEL.js +3 -3
- package/dist/parseSpEL.js.map +1 -1
- package/dist/parseSpEL.mjs +3 -3
- package/dist/parseSpEL.mjs.map +1 -1
- package/dist/{prepareQueryObjects-C5dHLhnC.js → prepareQueryObjects-BnGL91Tt.js} +9 -5
- package/dist/{prepareQueryObjects-C5dHLhnC.js.map → prepareQueryObjects-BnGL91Tt.js.map} +1 -1
- package/dist/{prepareQueryObjects-9emVAGYG.mjs → prepareQueryObjects-ChINDIap.mjs} +9 -5
- package/dist/{prepareQueryObjects-9emVAGYG.mjs.map → prepareQueryObjects-ChINDIap.mjs.map} +1 -1
- package/dist/query-builder-layout.css +1 -1
- package/dist/query-builder-layout.css.map +1 -1
- package/dist/query-builder.css +1 -1
- package/dist/query-builder.css.map +1 -1
- package/dist/react-querybuilder_core.d.mts +1389 -48
- package/dist/react-querybuilder_core.legacy-esm.d.ts +1389 -48
- package/dist/react-querybuilder_core.legacy-esm.js +2237 -79
- package/dist/react-querybuilder_core.legacy-esm.js.map +1 -1
- package/dist/react-querybuilder_core.mjs +2173 -87
- package/dist/react-querybuilder_core.mjs.map +1 -1
- package/dist/react-querybuilder_core.production.d.mts +1389 -48
- package/dist/react-querybuilder_core.production.mjs +1 -1
- package/dist/react-querybuilder_core.production.mjs.map +1 -1
- package/dist/styles/_layout.scss +5 -2
- package/dist/{utils-CxWs-N47.mjs → utils-4k-j5FBa.mjs} +4 -2
- package/dist/utils-4k-j5FBa.mjs.map +1 -0
- package/dist/{utils-SchkzSCN.js → utils-C79dxwZb.js} +4 -2
- package/dist/utils-C79dxwZb.js.map +1 -0
- package/package.json +9 -8
- package/dist/utils-CxWs-N47.mjs.map +0 -1
- package/dist/utils-SchkzSCN.js.map +0 -1
|
@@ -131,6 +131,14 @@ const defaultTranslations = {
|
|
|
131
131
|
label: "˅",
|
|
132
132
|
title: "Shift down"
|
|
133
133
|
},
|
|
134
|
+
undo: {
|
|
135
|
+
label: "↶",
|
|
136
|
+
title: "Undo"
|
|
137
|
+
},
|
|
138
|
+
redo: {
|
|
139
|
+
label: "↷",
|
|
140
|
+
title: "Redo"
|
|
141
|
+
},
|
|
134
142
|
dragHandle: {
|
|
135
143
|
label: "⁞⁞",
|
|
136
144
|
title: "Drag handle"
|
|
@@ -455,6 +463,9 @@ const standardClassnames = {
|
|
|
455
463
|
valid: "queryBuilder-valid",
|
|
456
464
|
invalid: "queryBuilder-invalid",
|
|
457
465
|
shiftActions: "shiftActions",
|
|
466
|
+
undoRedoActions: "undoRedoActions",
|
|
467
|
+
undoAction: "undoRedoActions-undo",
|
|
468
|
+
redoAction: "undoRedoActions-redo",
|
|
458
469
|
dndDragging: "dndDragging",
|
|
459
470
|
dndOver: "dndOver",
|
|
460
471
|
dndCopy: "dndCopy",
|
|
@@ -503,6 +514,9 @@ const defaultControlClassnames = {
|
|
|
503
514
|
value: "",
|
|
504
515
|
removeRule: "",
|
|
505
516
|
shiftActions: "",
|
|
517
|
+
undoRedoActions: "",
|
|
518
|
+
undoAction: "",
|
|
519
|
+
redoAction: "",
|
|
506
520
|
dragHandle: "",
|
|
507
521
|
lockRule: "",
|
|
508
522
|
lockGroup: "",
|
|
@@ -560,6 +574,9 @@ const TestID = {
|
|
|
560
574
|
valueEditor: "value-editor",
|
|
561
575
|
notToggle: "not-toggle",
|
|
562
576
|
shiftActions: "shift-actions",
|
|
577
|
+
undoRedoActions: "undo-redo-actions",
|
|
578
|
+
undoAction: "undo-action",
|
|
579
|
+
redoAction: "redo-action",
|
|
563
580
|
dragHandle: "drag-handle",
|
|
564
581
|
lockRule: "lock-rule",
|
|
565
582
|
lockGroup: "lock-group",
|
|
@@ -606,6 +623,7 @@ const queryBuilderFlagDefaults = {
|
|
|
606
623
|
enableDragAndDrop: false,
|
|
607
624
|
enableMountQueryChange: true,
|
|
608
625
|
listsAsArrays: false,
|
|
626
|
+
preserveQueryStateOnUnmount: false,
|
|
609
627
|
resetOnFieldChange: true,
|
|
610
628
|
resetOnOperatorChange: false,
|
|
611
629
|
showCloneButtons: false,
|
|
@@ -614,8 +632,22 @@ const queryBuilderFlagDefaults = {
|
|
|
614
632
|
showMuteButtons: false,
|
|
615
633
|
showNotToggle: false,
|
|
616
634
|
showShiftActions: false,
|
|
635
|
+
showUndoRedo: false,
|
|
617
636
|
suppressStandardClassnames: false
|
|
618
637
|
};
|
|
638
|
+
/**
|
|
639
|
+
* Default maximum number of undo steps retained by query history.
|
|
640
|
+
*
|
|
641
|
+
* @group Defaults
|
|
642
|
+
*/
|
|
643
|
+
const defaultMaxHistory = 50;
|
|
644
|
+
/**
|
|
645
|
+
* Default time window (in milliseconds) within which consecutive changes to the same
|
|
646
|
+
* property of the same rule are coalesced into a single undo step.
|
|
647
|
+
*
|
|
648
|
+
* @group Defaults
|
|
649
|
+
*/
|
|
650
|
+
const defaultCoalesceMs = 500;
|
|
619
651
|
//#endregion
|
|
620
652
|
//#region src/utils/arrayUtils.ts
|
|
621
653
|
/**
|
|
@@ -801,6 +833,178 @@ function convertQuery(query) {
|
|
|
801
833
|
return isRuleGroupTypeIC(query) ? convertFromIC(query) : convertToIC(query);
|
|
802
834
|
}
|
|
803
835
|
//#endregion
|
|
836
|
+
//#region src/utils/deriveClassNames.ts
|
|
837
|
+
const ruleClassnameSources = {
|
|
838
|
+
shiftActions: ["shiftActions"],
|
|
839
|
+
dragHandle: ["dragHandle"],
|
|
840
|
+
fields: ["valueSelector", "fields"],
|
|
841
|
+
matchMode: ["valueSelector", "matchMode"],
|
|
842
|
+
matchThreshold: ["valueSelector", "matchThreshold"],
|
|
843
|
+
operators: ["valueSelector", "operators"],
|
|
844
|
+
valueSource: ["valueSelector", "valueSource"],
|
|
845
|
+
value: ["value"],
|
|
846
|
+
cloneRule: ["actionElement", "cloneRule"],
|
|
847
|
+
lockRule: ["actionElement", "lockRule"],
|
|
848
|
+
muteRule: ["actionElement", "muteRule"],
|
|
849
|
+
removeRule: ["actionElement", "removeRule"],
|
|
850
|
+
valueListItem: ["valueListItem"]
|
|
851
|
+
};
|
|
852
|
+
const ruleGroupClassnameSources = {
|
|
853
|
+
header: {
|
|
854
|
+
sources: ["header"],
|
|
855
|
+
conditions: [
|
|
856
|
+
{
|
|
857
|
+
key: "dndOver",
|
|
858
|
+
when: (s) => s.isOver,
|
|
859
|
+
standardOnly: true
|
|
860
|
+
},
|
|
861
|
+
{
|
|
862
|
+
key: "dndCopy",
|
|
863
|
+
when: (s) => s.isOver && s.dropEffect === "copy"
|
|
864
|
+
},
|
|
865
|
+
{
|
|
866
|
+
key: "dndDropNotAllowed",
|
|
867
|
+
when: (s) => s.dropNotAllowed
|
|
868
|
+
}
|
|
869
|
+
]
|
|
870
|
+
},
|
|
871
|
+
shiftActions: ["shiftActions"],
|
|
872
|
+
undoRedoActions: ["undoRedoActions"],
|
|
873
|
+
undoAction: ["actionElement", "undoAction"],
|
|
874
|
+
redoAction: ["actionElement", "redoAction"],
|
|
875
|
+
dragHandle: ["dragHandle"],
|
|
876
|
+
combinators: ["valueSelector", "combinators"],
|
|
877
|
+
notToggle: ["notToggle"],
|
|
878
|
+
addRule: ["actionElement", "addRule"],
|
|
879
|
+
addGroup: ["actionElement", "addGroup"],
|
|
880
|
+
cloneGroup: ["actionElement", "cloneGroup"],
|
|
881
|
+
lockGroup: ["actionElement", "lockGroup"],
|
|
882
|
+
muteGroup: ["actionElement", "muteGroup"],
|
|
883
|
+
removeGroup: ["actionElement", "removeGroup"],
|
|
884
|
+
body: ["body"]
|
|
885
|
+
};
|
|
886
|
+
/**
|
|
887
|
+
* Conditional classes applied to a rule's wrapper element.
|
|
888
|
+
*
|
|
889
|
+
* Note that this is deliberately _not_ the same set as {@link ruleGroupOuterConditions}: a rule
|
|
890
|
+
* reflects more drag-and-drop states than a group does. Declaring each set separately is what
|
|
891
|
+
* keeps an implementation from assuming they're symmetric.
|
|
892
|
+
*/
|
|
893
|
+
const ruleOuterConditions = [
|
|
894
|
+
{
|
|
895
|
+
key: "disabled",
|
|
896
|
+
when: (s) => s.disabled
|
|
897
|
+
},
|
|
898
|
+
{
|
|
899
|
+
key: "muted",
|
|
900
|
+
when: (s) => s.muted
|
|
901
|
+
},
|
|
902
|
+
{
|
|
903
|
+
key: "dndDragging",
|
|
904
|
+
when: (s) => s.isDragging
|
|
905
|
+
},
|
|
906
|
+
{
|
|
907
|
+
key: "dndOver",
|
|
908
|
+
when: (s) => s.isOver
|
|
909
|
+
},
|
|
910
|
+
{
|
|
911
|
+
key: "dndCopy",
|
|
912
|
+
when: (s) => s.isOver && s.dropEffect === "copy"
|
|
913
|
+
},
|
|
914
|
+
{
|
|
915
|
+
key: "dndGroup",
|
|
916
|
+
when: (s) => s.isOver && s.groupItems
|
|
917
|
+
},
|
|
918
|
+
{
|
|
919
|
+
key: "dndDropNotAllowed",
|
|
920
|
+
when: (s) => s.dropNotAllowed
|
|
921
|
+
},
|
|
922
|
+
{
|
|
923
|
+
key: "hasSubQuery",
|
|
924
|
+
when: (s) => s.hasSubQuery
|
|
925
|
+
}
|
|
926
|
+
];
|
|
927
|
+
/** Conditional classes applied to a rule group's wrapper element. */
|
|
928
|
+
const ruleGroupOuterConditions = [
|
|
929
|
+
{
|
|
930
|
+
key: "disabled",
|
|
931
|
+
when: (s) => s.disabled
|
|
932
|
+
},
|
|
933
|
+
{
|
|
934
|
+
key: "muted",
|
|
935
|
+
when: (s) => s.muted
|
|
936
|
+
},
|
|
937
|
+
{
|
|
938
|
+
key: "dndDragging",
|
|
939
|
+
when: (s) => s.isDragging
|
|
940
|
+
},
|
|
941
|
+
{
|
|
942
|
+
key: "dndGroup",
|
|
943
|
+
when: (s) => s.isOver && s.groupItems
|
|
944
|
+
}
|
|
945
|
+
];
|
|
946
|
+
/**
|
|
947
|
+
* Expands conditions into `clsx` arguments: every custom class first (in declaration order),
|
|
948
|
+
* then a single object of standard classes.
|
|
949
|
+
*/
|
|
950
|
+
const conditionArgs = (conditions, state, { classNames, suppressStandardClassnames }) => {
|
|
951
|
+
const evaluated = conditions.map((condition) => [condition, !!condition.when(state)]);
|
|
952
|
+
return [...evaluated.filter(([condition]) => !condition.standardOnly).map(([condition, active]) => active && classNames?.[condition.key]), suppressStandardClassnames || Object.fromEntries(evaluated.map(([condition, active]) => [standardClassnames[condition.key], active]))];
|
|
953
|
+
};
|
|
954
|
+
/** Composes the classname for a single entry of a spec map. */
|
|
955
|
+
const deriveFromSpec = (key, spec, state, options) => {
|
|
956
|
+
const { sources, conditions } = Array.isArray(spec) ? { sources: spec } : spec;
|
|
957
|
+
return clsx(options.suppressStandardClassnames || standardClassnames[key], ...sources.map((source) => options.classNames?.[source]), ...conditions ? conditionArgs(conditions, state, options) : []);
|
|
958
|
+
};
|
|
959
|
+
const deriveFromSpecs = (specs, state, options) => {
|
|
960
|
+
const result = {};
|
|
961
|
+
for (const [key, spec] of Object.entries(specs)) result[key] = deriveFromSpec(key, spec, state, options);
|
|
962
|
+
return result;
|
|
963
|
+
};
|
|
964
|
+
/**
|
|
965
|
+
* Classnames for each element rendered by a rule. This is the framework-agnostic core of the
|
|
966
|
+
* `classNames` object returned by the `useRule` hook.
|
|
967
|
+
*
|
|
968
|
+
* @group Query Tools
|
|
969
|
+
*/
|
|
970
|
+
const deriveRuleClassNames = (options) => deriveFromSpecs(ruleClassnameSources, {}, options);
|
|
971
|
+
/**
|
|
972
|
+
* The classname for a single element of a rule, composed from the same table as
|
|
973
|
+
* {@link deriveRuleClassNames}. Useful where only one is needed, such as the items of a
|
|
974
|
+
* multi-value editor.
|
|
975
|
+
*
|
|
976
|
+
* @group Query Tools
|
|
977
|
+
*/
|
|
978
|
+
const deriveRuleClassName = (key, options) => deriveFromSpec(key, ruleClassnameSources[key], {}, options);
|
|
979
|
+
/**
|
|
980
|
+
* Classnames for each element rendered by a rule group, including its conditionally-classed
|
|
981
|
+
* `header`. This is the framework-agnostic core of the `classNames` object returned by the
|
|
982
|
+
* `useRuleGroup` hook.
|
|
983
|
+
*
|
|
984
|
+
* @group Query Tools
|
|
985
|
+
*/
|
|
986
|
+
const deriveRuleGroupClassNames = (options) => deriveFromSpecs(ruleGroupClassnameSources, options, options);
|
|
987
|
+
const deriveOuterClassName = (standardKey, conditions, options) => {
|
|
988
|
+
const { classNames, suppressStandardClassnames, leadingClassNames = [], validationClassName } = options;
|
|
989
|
+
return clsx(...leadingClassNames, suppressStandardClassnames || standardClassnames[standardKey], classNames?.[standardKey], ...conditionArgs(conditions, options, options), validationClassName);
|
|
990
|
+
};
|
|
991
|
+
/**
|
|
992
|
+
* The outer (wrapper) classname for a rule, including every conditional state class.
|
|
993
|
+
*
|
|
994
|
+
* @group Query Tools
|
|
995
|
+
*/
|
|
996
|
+
const deriveRuleOuterClassName = (options) => deriveOuterClassName("rule", ruleOuterConditions, options);
|
|
997
|
+
/**
|
|
998
|
+
* The outer (wrapper) classname for a rule group, including every conditional state class.
|
|
999
|
+
*
|
|
1000
|
+
* A group reflects fewer drag-and-drop states than a rule—`dndOver`, `dndCopy`,
|
|
1001
|
+
* `dndDropNotAllowed`, and `hasSubQuery` do not apply—so this is not interchangeable with
|
|
1002
|
+
* {@link deriveRuleOuterClassName}.
|
|
1003
|
+
*
|
|
1004
|
+
* @group Query Tools
|
|
1005
|
+
*/
|
|
1006
|
+
const deriveRuleGroupOuterClassName = (options) => deriveOuterClassName("ruleGroup", ruleGroupOuterConditions, options);
|
|
1007
|
+
//#endregion
|
|
804
1008
|
//#region src/utils/defaultValidator.ts
|
|
805
1009
|
/**
|
|
806
1010
|
* This is an example validation function you can pass to {@link react-querybuilder!QueryBuilder QueryBuilder} in the
|
|
@@ -1061,8 +1265,10 @@ const prepareOptionList = (props) => {
|
|
|
1061
1265
|
...op,
|
|
1062
1266
|
[placeholderName]: defaultOption
|
|
1063
1267
|
};
|
|
1064
|
-
} else if (isFlexibleOptionGroupArray(optionList)) for (const og of optionList) for (const opt of og.options) optionsMap[opt.value ??
|
|
1065
|
-
|
|
1268
|
+
} else if (isFlexibleOptionGroupArray(optionList)) for (const og of optionList) for (const opt of og.options) optionsMap[opt.value ??
|
|
1269
|
+
/* v8 ignore start -- @preserve */ opt.name] = toFullOption(opt, baseOption);
|
|
1270
|
+
else for (const opt of optionList) optionsMap[opt.value ??
|
|
1271
|
+
/* v8 ignore start -- @preserve */ opt.name] = toFullOption(opt, baseOption);
|
|
1066
1272
|
return {
|
|
1067
1273
|
defaultOption,
|
|
1068
1274
|
optionList,
|
|
@@ -1076,7 +1282,8 @@ const filterByComparator = (field, operator, fieldToCompare) => {
|
|
|
1076
1282
|
const fullFieldToCompare = toFullOption(fieldToCompare);
|
|
1077
1283
|
if (fullField.value === fullFieldToCompare.value) return false;
|
|
1078
1284
|
if (typeof fullField.comparator === "string") return fullField[fullField.comparator] === fullFieldToCompare[fullField.comparator];
|
|
1079
|
-
return fullField.comparator?.(fullFieldToCompare, operator) ??
|
|
1285
|
+
return fullField.comparator?.(fullFieldToCompare, operator) ??
|
|
1286
|
+
/* v8 ignore start -- @preserve */ false;
|
|
1080
1287
|
};
|
|
1081
1288
|
/**
|
|
1082
1289
|
* For a given {@link FullField}, returns the `fields` list filtered for
|
|
@@ -1091,7 +1298,8 @@ const filterByComparator = (field, operator, fieldToCompare) => {
|
|
|
1091
1298
|
*/
|
|
1092
1299
|
const filterFieldsByComparator = (field, fields, operator) => {
|
|
1093
1300
|
if (!field.comparator) {
|
|
1094
|
-
const filterOutSameField = (f) => (f.value ?? f.name) !== (field.value ??
|
|
1301
|
+
const filterOutSameField = (f) => (f.value ?? /* v8 ignore start -- @preserve */ f.name) !== (field.value ??
|
|
1302
|
+
/* v8 ignore start -- @preserve */ field.name);
|
|
1095
1303
|
if (isFlexibleOptionGroupArray(fields)) return fields.map((og) => ({
|
|
1096
1304
|
...og,
|
|
1097
1305
|
options: og.options.filter((v) => filterOutSameField(v))
|
|
@@ -1105,6 +1313,128 @@ const filterFieldsByComparator = (field, fields, operator) => {
|
|
|
1105
1313
|
return fields.filter((f) => filterByComparator(field, operator, f));
|
|
1106
1314
|
};
|
|
1107
1315
|
//#endregion
|
|
1316
|
+
//#region src/utils/deriveRuleContext.ts
|
|
1317
|
+
/**
|
|
1318
|
+
* Resolves the field configuration for a rule's `field`, falling back to a minimal option object
|
|
1319
|
+
* when the field isn't present in the field map.
|
|
1320
|
+
*/
|
|
1321
|
+
const getFieldData = (field, fieldMap) => fieldMap?.[field] ?? {
|
|
1322
|
+
name: field,
|
|
1323
|
+
value: field,
|
|
1324
|
+
label: field
|
|
1325
|
+
};
|
|
1326
|
+
/**
|
|
1327
|
+
* The input type for a rule. A field's own `inputType` takes precedence over `getInputType`.
|
|
1328
|
+
*/
|
|
1329
|
+
const getRuleInputType = (field, operator, fieldData, getInputType) => fieldData.inputType ?? getInputType(field, operator, { fieldData });
|
|
1330
|
+
/**
|
|
1331
|
+
* Whether the value editor(s) should be hidden for an operator, based on its `arity`.
|
|
1332
|
+
*/
|
|
1333
|
+
const hideValueControlsForOperator = (operatorObject) => {
|
|
1334
|
+
const arity = operatorObject?.arity;
|
|
1335
|
+
return typeof arity === "string" && arity === "unary" || typeof arity === "number" && arity < 2;
|
|
1336
|
+
};
|
|
1337
|
+
/**
|
|
1338
|
+
* Value source options for a rule. A `valueSource` present on the rule but absent from the
|
|
1339
|
+
* configured list is appended, so the current selection is always representable.
|
|
1340
|
+
*/
|
|
1341
|
+
const getRuleValueSourceOptions = (rule, fieldData, getValueSources) => {
|
|
1342
|
+
const configuredVSs = getValueSources(rule.field, rule.operator, { fieldData });
|
|
1343
|
+
if (rule.valueSource && !getOption(configuredVSs, rule.valueSource)) return [...configuredVSs, {
|
|
1344
|
+
name: rule.valueSource,
|
|
1345
|
+
value: rule.valueSource,
|
|
1346
|
+
label: rule.valueSource
|
|
1347
|
+
}];
|
|
1348
|
+
return configuredVSs;
|
|
1349
|
+
};
|
|
1350
|
+
/**
|
|
1351
|
+
* Normalizes the result of `getParameters` to a non-empty list or `null`.
|
|
1352
|
+
*/
|
|
1353
|
+
const getParametersAsList = (parameters) => parameters && parameters.length > 0 ? parameters : null;
|
|
1354
|
+
/**
|
|
1355
|
+
* The value editor type for a rule. `valueSource: "field"` always uses a select list, and
|
|
1356
|
+
* `valueSource: "parameter"` uses a (multi)select when parameters are available.
|
|
1357
|
+
*/
|
|
1358
|
+
const getRuleValueEditorType = (rule, fieldData, parametersAsList, getValueEditorType) => rule.valueSource === "field" ? "select" : rule.valueSource === "parameter" ? parametersAsList ? lc(rule.operator) === "in" || lc(rule.operator) === "notin" ? "multiselect" : "select" : "text" : getValueEditorType(rule.field, rule.operator, { fieldData });
|
|
1359
|
+
/**
|
|
1360
|
+
* The option list presented by a rule's value editor, resolved from its `valueSource`.
|
|
1361
|
+
*/
|
|
1362
|
+
const getRuleValues = (rule, fieldData, fields, parametersAsList, getValues) => {
|
|
1363
|
+
const v = rule.valueSource === "field" ? filterFieldsByComparator(fieldData, fields, rule.operator) : rule.valueSource === "parameter" ? parametersAsList ?? [] : getValues(rule.field, rule.operator, { fieldData });
|
|
1364
|
+
return isFlexibleOptionArray(v) || isFlexibleOptionGroupArray(v) ? toFullOptionList(v) : v;
|
|
1365
|
+
};
|
|
1366
|
+
/**
|
|
1367
|
+
* The validation result for a rule: the entry from a query-level {@link ValidationMap} if present,
|
|
1368
|
+
* otherwise the field's own `validator` result, otherwise `null`.
|
|
1369
|
+
*/
|
|
1370
|
+
const getRuleValidationResult = (rule, fieldData, validationMap = {}, id = rule.id ?? "") => validationMap[id] ?? (typeof fieldData.validator === "function" ? fieldData.validator(rule) : null);
|
|
1371
|
+
/**
|
|
1372
|
+
* Resolves everything about a single rule that depends on the field/operator configuration:
|
|
1373
|
+
* its field data, operators, value editor type, value list, value sources, match modes, and
|
|
1374
|
+
* validation result.
|
|
1375
|
+
*
|
|
1376
|
+
* This is the framework-agnostic core of the `useRule` hook, shared with
|
|
1377
|
+
* {@link QueryManager.getRuleContext} so that non-React implementations derive identical results.
|
|
1378
|
+
* It performs no memoization; callers are responsible for caching as appropriate.
|
|
1379
|
+
*
|
|
1380
|
+
* @group Query Tools
|
|
1381
|
+
*/
|
|
1382
|
+
const deriveRuleContext = (rule, resolvers, options = {}) => {
|
|
1383
|
+
const { fields, fieldMap, getInputType, getMatchModes, getOperators, getParameters, getValueEditorType, getValues, getValueSources, getSubQueryBuilderProps } = resolvers;
|
|
1384
|
+
const fieldData = getFieldData(rule.field, fieldMap);
|
|
1385
|
+
const inputType = fieldData.inputType ?? getInputType(rule.field, rule.operator, { fieldData });
|
|
1386
|
+
const matchModes = getMatchModes(rule.field, { fieldData });
|
|
1387
|
+
const operators = getOperators(rule.field, { fieldData });
|
|
1388
|
+
const operatorObject = getOption(operators, rule.operator);
|
|
1389
|
+
const valueSourceOptions = getRuleValueSourceOptions(rule, fieldData, getValueSources);
|
|
1390
|
+
const parameters = getParametersAsList(getParameters(rule.field, rule.operator, { fieldData }));
|
|
1391
|
+
return {
|
|
1392
|
+
fieldData,
|
|
1393
|
+
hideValueControls: hideValueControlsForOperator(operatorObject),
|
|
1394
|
+
inputType,
|
|
1395
|
+
matchModes,
|
|
1396
|
+
operatorObject,
|
|
1397
|
+
operators,
|
|
1398
|
+
parameters,
|
|
1399
|
+
validationResult: getRuleValidationResult(rule, fieldData, options.validationMap, options.id),
|
|
1400
|
+
valueEditorType: getRuleValueEditorType(rule, fieldData, parameters, getValueEditorType),
|
|
1401
|
+
values: getRuleValues(rule, fieldData, fields, parameters, getValues),
|
|
1402
|
+
valueSourceOptions,
|
|
1403
|
+
valueSources: valueSourceOptions.map(({ value }) => value),
|
|
1404
|
+
subQueryBuilderProps: getSubQueryBuilderProps?.(rule.field, { fieldData }) ?? {}
|
|
1405
|
+
};
|
|
1406
|
+
};
|
|
1407
|
+
/**
|
|
1408
|
+
* The effective combinator for a group: its own `combinator` when it has one, otherwise the
|
|
1409
|
+
* first configured combinator (which is the case for groups with independent combinators).
|
|
1410
|
+
*
|
|
1411
|
+
* This intentionally covers only the current property-based API. The `RuleGroup` component
|
|
1412
|
+
* additionally falls back to its deprecated `combinator` prop; that fallback stays in the hook.
|
|
1413
|
+
*/
|
|
1414
|
+
const getRuleGroupCombinator = (ruleGroup, combinators) => isRuleGroupType(ruleGroup) ? ruleGroup.combinator : getFirstOption(combinators) ?? "";
|
|
1415
|
+
/**
|
|
1416
|
+
* Resolves everything about a rule group that depends on the combinator configuration, plus its
|
|
1417
|
+
* validation result.
|
|
1418
|
+
*
|
|
1419
|
+
* Note that unlike {@link deriveRuleContext}, there is no field-level validator fallback—a
|
|
1420
|
+
* group's validation result comes only from the query-level {@link ValidationMap}.
|
|
1421
|
+
*
|
|
1422
|
+
* @group Query Tools
|
|
1423
|
+
*/
|
|
1424
|
+
const deriveRuleGroupContext = (ruleGroup, combinators, options = {}) => {
|
|
1425
|
+
const independentCombinators = isRuleGroupTypeIC(ruleGroup);
|
|
1426
|
+
const combinator = getRuleGroupCombinator(ruleGroup, combinators);
|
|
1427
|
+
const combinatorObject = getOption(combinators, combinator);
|
|
1428
|
+
return {
|
|
1429
|
+
combinator,
|
|
1430
|
+
combinatorObject,
|
|
1431
|
+
combinators,
|
|
1432
|
+
combinatorBasedClassName: independentCombinators ? null : combinatorObject?.className ?? "",
|
|
1433
|
+
independentCombinators,
|
|
1434
|
+
validationResult: (options.validationMap ?? {})[options.id ?? ruleGroup.id ?? ""] ?? null
|
|
1435
|
+
};
|
|
1436
|
+
};
|
|
1437
|
+
//#endregion
|
|
1108
1438
|
//#region src/utils/parseNumber.ts
|
|
1109
1439
|
/**
|
|
1110
1440
|
* Converts a string to a number. Uses native `parseFloat` if `parseNumbers` is "native",
|
|
@@ -1124,6 +1454,104 @@ const parseNumber = (val, { parseNumbers, bigIntOnOverflow } = {}) => {
|
|
|
1124
1454
|
return typeof valAsNum === "bigint" || !Number.isNaN(valAsNum) ? valAsNum : val;
|
|
1125
1455
|
};
|
|
1126
1456
|
//#endregion
|
|
1457
|
+
//#region src/utils/deriveValueEditor.ts
|
|
1458
|
+
/** Operators whose value is a list rather than a single scalar. */
|
|
1459
|
+
const multiValueOperators = /* @__PURE__ */ new Set([
|
|
1460
|
+
"between",
|
|
1461
|
+
"notBetween",
|
|
1462
|
+
"in",
|
|
1463
|
+
"notIn"
|
|
1464
|
+
]);
|
|
1465
|
+
/**
|
|
1466
|
+
* Whether an operator's value is a list of two bounds.
|
|
1467
|
+
*
|
|
1468
|
+
* @group Value Editors
|
|
1469
|
+
*/
|
|
1470
|
+
const isBetweenOperator = (operator) => operator === "between" || operator === "notBetween";
|
|
1471
|
+
/**
|
|
1472
|
+
* Determines whether a rule's `value` needs to be collapsed to a single element because it no
|
|
1473
|
+
* longer represents a list, and what it should become.
|
|
1474
|
+
*
|
|
1475
|
+
* This happens when the value is an array (or a comma-containing string in a `number` input,
|
|
1476
|
+
* which `<input type="number">` can't display) while the operator is not one of the multi-value
|
|
1477
|
+
* operators and the editor is not a multiselect—typically right after the operator changes from
|
|
1478
|
+
* `"in"` or `"between"` to something else.
|
|
1479
|
+
*
|
|
1480
|
+
* The React `useValueEditor` hook applies the result in an effect; other implementations may
|
|
1481
|
+
* apply it wherever is idiomatic.
|
|
1482
|
+
*
|
|
1483
|
+
* @group Value Editors
|
|
1484
|
+
*/
|
|
1485
|
+
const getValueEditorReset = ({ skipHook, type, operator, value, inputType }) => {
|
|
1486
|
+
return !skipHook && type !== "multiselect" && !multiValueOperators.has(operator) && (Array.isArray(value) || inputType === "number" && typeof value === "string" && value.includes(",")) ? {
|
|
1487
|
+
reset: true,
|
|
1488
|
+
value: toArray(value, { retainEmptyStrings: true })[0] ?? ""
|
|
1489
|
+
} : {
|
|
1490
|
+
reset: false,
|
|
1491
|
+
value
|
|
1492
|
+
};
|
|
1493
|
+
};
|
|
1494
|
+
/**
|
|
1495
|
+
* Produces the next value for a series of value editors when the editor at `index` changes.
|
|
1496
|
+
*
|
|
1497
|
+
* For `between`/`notBetween`, editing the first bound guarantees an array of at least two
|
|
1498
|
+
* elements, seeding the second from the first available option. The result is a comma-joined
|
|
1499
|
+
* string unless `listsAsArrays` is `true`.
|
|
1500
|
+
*
|
|
1501
|
+
* @group Value Editors
|
|
1502
|
+
*/
|
|
1503
|
+
const getMultiValueUpdate = ({ value, index, valueAsArray, operator, values, listsAsArrays, parseNumberMethod }) => {
|
|
1504
|
+
const parsedVal = parseNumber(value, { parseNumbers: parseNumberMethod });
|
|
1505
|
+
const needsBetweenFix = index === 0 && isBetweenOperator(operator) && (valueAsArray.length < 2 || valueAsArray[1] === void 0);
|
|
1506
|
+
if (valueAsArray[index] === parsedVal && !needsBetweenFix) return listsAsArrays ? valueAsArray : joinWith(valueAsArray, ",");
|
|
1507
|
+
const v = [...valueAsArray];
|
|
1508
|
+
v[index] = parsedVal;
|
|
1509
|
+
if (needsBetweenFix) v[1] = getFirstOption(values) ?? "";
|
|
1510
|
+
return listsAsArrays ? v : joinWith(v, ",");
|
|
1511
|
+
};
|
|
1512
|
+
/**
|
|
1513
|
+
* Coerces a value to a `bigint`, falling back to the parsed number when it can't be represented
|
|
1514
|
+
* as one (an empty string or a decimal, for example).
|
|
1515
|
+
*
|
|
1516
|
+
* @group Value Editors
|
|
1517
|
+
*/
|
|
1518
|
+
const coerceBigIntValue = (value, parseNumberMethod) => {
|
|
1519
|
+
const valAsMaybeNumber = parseNumber(value, {
|
|
1520
|
+
parseNumbers: parseNumberMethod,
|
|
1521
|
+
bigIntOnOverflow: true
|
|
1522
|
+
});
|
|
1523
|
+
try {
|
|
1524
|
+
return BigInt(valAsMaybeNumber);
|
|
1525
|
+
} catch {
|
|
1526
|
+
return valAsMaybeNumber;
|
|
1527
|
+
}
|
|
1528
|
+
};
|
|
1529
|
+
/**
|
|
1530
|
+
* The `type` attribute an `<input>` should use for a rule. `bigint` values and the `in`/`notIn`
|
|
1531
|
+
* operators (whose value is a comma-separated list) both require a text input.
|
|
1532
|
+
*
|
|
1533
|
+
* @group Value Editors
|
|
1534
|
+
*/
|
|
1535
|
+
const coerceInputType = (inputType, operator) => inputType === "bigint" || operator === "in" || operator === "notIn" ? "text" : inputType || "text";
|
|
1536
|
+
/**
|
|
1537
|
+
* Produces the next value for a value selector. Multiselect values are normalized to an array
|
|
1538
|
+
* first, then comma-joined unless `listsAsArrays` is `true`. Single-select values pass through.
|
|
1539
|
+
*
|
|
1540
|
+
* @group Value Editors
|
|
1541
|
+
*/
|
|
1542
|
+
const getValueSelectorUpdate = (value, { multiple, listsAsArrays } = {}) => {
|
|
1543
|
+
if (!multiple) return value;
|
|
1544
|
+
const valueAsArray = toArray(value);
|
|
1545
|
+
return listsAsArrays ? valueAsArray : joinWith(valueAsArray, ",");
|
|
1546
|
+
};
|
|
1547
|
+
/**
|
|
1548
|
+
* Normalizes a value selector's current value for display. Multiselect values become an array of
|
|
1549
|
+
* strings so they match option names, which are always strings (e.g. `[42]` becomes `["42"]`).
|
|
1550
|
+
*
|
|
1551
|
+
* @group Value Editors
|
|
1552
|
+
*/
|
|
1553
|
+
const normalizeValueSelectorValue = (value, multiple) => multiple ? toArray(value).map(String) : value;
|
|
1554
|
+
//#endregion
|
|
1127
1555
|
//#region src/utils/transformQuery.ts
|
|
1128
1556
|
const remapProperties = (obj, propertyMap, deleteRemappedProperties) => {
|
|
1129
1557
|
const result = {};
|
|
@@ -1470,7 +1898,9 @@ const translationMatchFilter = (key, keyToTest, conditions) => keyToTest.startsW
|
|
|
1470
1898
|
*
|
|
1471
1899
|
* @group Export
|
|
1472
1900
|
*/
|
|
1473
|
-
const getNLTranslataion = (key, translations, conditions = []) => conditions.length === 0 ? translations[key] ?? defaultNLTranslations[key] ??
|
|
1901
|
+
const getNLTranslataion = (key, translations, conditions = []) => conditions.length === 0 ? translations[key] ?? defaultNLTranslations[key] ??
|
|
1902
|
+
/* v8 ignore start -- @preserve */ "" : Object.entries(translations).find(([keyToTest]) => translationMatchFilter(key, keyToTest, conditions))?.[1] ?? Object.entries(defaultNLTranslations).find(([keyToTest]) => translationMatchFilter(key, keyToTest, conditions))?.[1] ?? defaultNLTranslations[key] ??
|
|
1903
|
+
/* v8 ignore next -- @preserve */ "";
|
|
1474
1904
|
/**
|
|
1475
1905
|
* Transforms
|
|
1476
1906
|
* - `match: { mode: "atLeast", threshold: 1 }` to `match: { mode: "some" }`
|
|
@@ -1539,7 +1969,7 @@ const bigIntJsonParseReviver = (_key, value) => isPojo(value) && Object.keys(val
|
|
|
1539
1969
|
const defaultRuleGroupProcessorCEL = (ruleGroup, options) => {
|
|
1540
1970
|
const { fields, fallbackExpression, getParseNumberBoolean, placeholderFieldName, placeholderOperatorName, placeholderValueName, ruleProcessor, validateRule, validationMap } = options;
|
|
1541
1971
|
const processRuleGroup = (rg, outermost) => {
|
|
1542
|
-
if (!isRuleOrGroupValid(rg, validationMap[rg.id ?? ""])) return outermost ? fallbackExpression : "";
|
|
1972
|
+
if (!isRuleOrGroupValid(rg, validationMap[rg.id ?? /* v8 ignore start -- @preserve */ ""])) return outermost ? fallbackExpression : "";
|
|
1543
1973
|
const processedRules = [];
|
|
1544
1974
|
let precedingCombinator = "";
|
|
1545
1975
|
let firstRule = true;
|
|
@@ -1605,10 +2035,11 @@ const defaultRuleProcessorCEL = (rule, opts = {}) => {
|
|
|
1605
2035
|
const { mode, threshold } = matchEval;
|
|
1606
2036
|
const subqueryDepth = opts.subqueryDepth ?? 0;
|
|
1607
2037
|
const arrayElementAlias = getSubqueryElementAlias(subqueryDepth);
|
|
1608
|
-
const
|
|
2038
|
+
const celQuery = transformQuery(rule.value, { ruleProcessor: (r) => ({
|
|
1609
2039
|
...r,
|
|
1610
2040
|
field: `${arrayElementAlias}${r.field ? `.${r.field}` : ""}`
|
|
1611
|
-
}) })
|
|
2041
|
+
}) });
|
|
2042
|
+
const nestedArrayFilter = defaultRuleGroupProcessorCEL(celQuery, {
|
|
1612
2043
|
...opts,
|
|
1613
2044
|
subqueryDepth: subqueryDepth + 1
|
|
1614
2045
|
});
|
|
@@ -1685,7 +2116,7 @@ const defaultRuleGroupProcessorMongoDBQuery = (ruleGroup, options, meta) => {
|
|
|
1685
2116
|
const { context, fields, getParseNumberBoolean, placeholderFieldName, placeholderOperatorName, placeholderValueName, ruleProcessor, validateRule, validationMap } = options;
|
|
1686
2117
|
const { inExpressionContext } = context ?? {};
|
|
1687
2118
|
const processRuleGroup = (rg, outermost) => {
|
|
1688
|
-
if (!isRuleOrGroupValid(rg, validationMap[rg.id ?? ""])) return outermost ? mongoDbFallback : false;
|
|
2119
|
+
if (!isRuleOrGroupValid(rg, validationMap[rg.id ?? /* v8 ignore start -- @preserve */ ""])) return outermost ? mongoDbFallback : false;
|
|
1689
2120
|
const combinator = `$${lc(rg.combinator)}`;
|
|
1690
2121
|
let hasChildRules = false;
|
|
1691
2122
|
const expressions = rg.rules.map((rule) => {
|
|
@@ -1863,7 +2294,7 @@ const defaultRuleProcessorMongoDB = (rule, options) => {
|
|
|
1863
2294
|
const defaultRuleGroupProcessorSpEL = (ruleGroup, options) => {
|
|
1864
2295
|
const { fields, fallbackExpression, getParseNumberBoolean, placeholderFieldName, placeholderOperatorName, placeholderValueName, ruleProcessor, validateRule, validationMap } = options;
|
|
1865
2296
|
const processRuleGroup = (rg, outermost) => {
|
|
1866
|
-
if (!isRuleOrGroupValid(rg, validationMap[rg.id ?? ""])) return outermost ? fallbackExpression : "";
|
|
2297
|
+
if (!isRuleOrGroupValid(rg, validationMap[rg.id ?? /* v8 ignore start -- @preserve */ ""])) return outermost ? fallbackExpression : "";
|
|
1867
2298
|
const processedRules = [];
|
|
1868
2299
|
let precedingCombinator = "";
|
|
1869
2300
|
let firstRule = true;
|
|
@@ -2104,7 +2535,7 @@ const cypherCombinatorMap = {
|
|
|
2104
2535
|
const defaultRuleGroupProcessorCypher = (ruleGroup, options) => {
|
|
2105
2536
|
const { fields, fallbackExpression, getParseNumberBoolean, placeholderFieldName, placeholderOperatorName, placeholderValueName, ruleProcessor, validateRule, validationMap } = options;
|
|
2106
2537
|
const processRuleGroup = (rg, outermost) => {
|
|
2107
|
-
if (!isRuleOrGroupValid(rg, validationMap[rg.id ?? ""])) return outermost ? fallbackExpression : "";
|
|
2538
|
+
if (!isRuleOrGroupValid(rg, validationMap[rg.id ?? /* v8 ignore start -- @preserve */ ""])) return outermost ? fallbackExpression : "";
|
|
2108
2539
|
const processedRules = [];
|
|
2109
2540
|
let precedingCombinator = "";
|
|
2110
2541
|
let firstRule = true;
|
|
@@ -2168,7 +2599,7 @@ const defaultRuleGroupProcessorDrizzle = (ruleGroup, options, _meta) => (columns
|
|
|
2168
2599
|
if (!columns || !drizzleOperators) return void 0;
|
|
2169
2600
|
const { and, not, or } = drizzleOperators;
|
|
2170
2601
|
const processRuleGroup = (rg, _outermost) => {
|
|
2171
|
-
if (!isRuleOrGroupValid(rg, validationMap[rg.id ?? ""])) return;
|
|
2602
|
+
if (!isRuleOrGroupValid(rg, validationMap[rg.id ?? /* v8 ignore start -- @preserve */ ""])) return;
|
|
2172
2603
|
const processedRules = rg.rules.map((rule) => {
|
|
2173
2604
|
if (isRuleGroup(rule)) return processRuleGroup(rule);
|
|
2174
2605
|
const [validationResult, fieldValidator] = validateRule(rule);
|
|
@@ -2201,7 +2632,7 @@ const defaultRuleGroupProcessorDrizzle = (ruleGroup, options, _meta) => (columns
|
|
|
2201
2632
|
const defaultRuleGroupProcessorElasticSearch = (ruleGroup, options) => {
|
|
2202
2633
|
const { fields, getParseNumberBoolean, placeholderFieldName, placeholderOperatorName, placeholderValueName, ruleProcessor, validateRule, validationMap } = options;
|
|
2203
2634
|
const processRuleGroup = (rg) => {
|
|
2204
|
-
if (!isRuleOrGroupValid(rg, validationMap[rg.id ?? ""])) return false;
|
|
2635
|
+
if (!isRuleOrGroupValid(rg, validationMap[rg.id ?? /* v8 ignore start -- @preserve */ ""])) return false;
|
|
2205
2636
|
const processedRules = rg.rules.map((rule) => {
|
|
2206
2637
|
if (isRuleGroup(rule)) return processRuleGroup(rule);
|
|
2207
2638
|
const [validationResult, fieldValidator] = validateRule(rule);
|
|
@@ -2236,7 +2667,9 @@ const defaultRuleGroupProcessorGremlin = (ruleGroup, options) => {
|
|
|
2236
2667
|
// v8 ignore next -- @preserve
|
|
2237
2668
|
if (typeof rule === "string" || isRuleGroup(rule)) return void 0;
|
|
2238
2669
|
const [validationResult, fieldValidator] = validateRule(rule);
|
|
2239
|
-
if (!isRuleOrGroupValid(rule, validationResult, fieldValidator) ||
|
|
2670
|
+
if (!isRuleOrGroupValid(rule, validationResult, fieldValidator) ||
|
|
2671
|
+
/* v8 ignore next 2 -- @preserve */
|
|
2672
|
+
rule.field === placeholderFieldName || rule.operator === placeholderOperatorName || placeholderValueName !== void 0 && rule.value === placeholderValueName) return;
|
|
2240
2673
|
const fieldData = getOption(fields, rule.field);
|
|
2241
2674
|
return ruleProcessor(rule, {
|
|
2242
2675
|
...options,
|
|
@@ -2247,7 +2680,7 @@ const defaultRuleGroupProcessorGremlin = (ruleGroup, options) => {
|
|
|
2247
2680
|
};
|
|
2248
2681
|
/** Recursively processes a nested group into `.and()`/`.or()`/`.not()` form. */
|
|
2249
2682
|
const processNested = (rg) => {
|
|
2250
|
-
if (!isRuleOrGroupValid(rg, validationMap[rg.id ?? ""])) return "";
|
|
2683
|
+
if (!isRuleOrGroupValid(rg, validationMap[rg.id ?? /* v8 ignore start -- @preserve */ ""])) return "";
|
|
2251
2684
|
const predicates = [];
|
|
2252
2685
|
for (const rule of rg.rules) {
|
|
2253
2686
|
if (typeof rule === "string") continue;
|
|
@@ -2265,7 +2698,7 @@ const defaultRuleGroupProcessorGremlin = (ruleGroup, options) => {
|
|
|
2265
2698
|
if (predicates.length === 1 && !rg.not) return predicates[0];
|
|
2266
2699
|
return `.${prefix}(${predicates.map((p) => p.startsWith(".") ? `__${p}` : p).join(", ")})`;
|
|
2267
2700
|
};
|
|
2268
|
-
if (!isRuleOrGroupValid(ruleGroup, validationMap[ruleGroup.id ?? ""])) return fallbackExpression;
|
|
2701
|
+
if (!isRuleOrGroupValid(ruleGroup, validationMap[ruleGroup.id ?? /* v8 ignore start -- @preserve */ ""])) return fallbackExpression;
|
|
2269
2702
|
const steps = [];
|
|
2270
2703
|
for (const rule of ruleGroup.rules) {
|
|
2271
2704
|
if (typeof rule === "string") continue;
|
|
@@ -2290,7 +2723,7 @@ const defaultRuleGroupProcessorGremlin = (ruleGroup, options) => {
|
|
|
2290
2723
|
const defaultRuleGroupProcessorJSONata = (ruleGroup, options) => {
|
|
2291
2724
|
const { fields, fallbackExpression, getParseNumberBoolean, placeholderFieldName, placeholderOperatorName, placeholderValueName, ruleProcessor, validateRule, validationMap } = options;
|
|
2292
2725
|
const processRuleGroup = (rg, outermost) => {
|
|
2293
|
-
if (!isRuleOrGroupValid(rg, validationMap[rg.id ?? ""])) return outermost ? fallbackExpression : "";
|
|
2726
|
+
if (!isRuleOrGroupValid(rg, validationMap[rg.id ?? /* v8 ignore start -- @preserve */ ""])) return outermost ? fallbackExpression : "";
|
|
2294
2727
|
const processedRules = [];
|
|
2295
2728
|
let precedingCombinator = "";
|
|
2296
2729
|
let firstRule = true;
|
|
@@ -2346,7 +2779,7 @@ const defaultRuleGroupProcessorJsonLogic = (ruleGroup, options) => {
|
|
|
2346
2779
|
const { fields, getParseNumberBoolean, placeholderFieldName, placeholderOperatorName, placeholderValueName, ruleProcessor, validateRule, validationMap } = options;
|
|
2347
2780
|
const query = convertFromIC(ruleGroup);
|
|
2348
2781
|
const processRuleGroup = (rg, _outermost) => {
|
|
2349
|
-
if (!isRuleOrGroupValid(rg, validationMap[rg.id ?? ""])) return false;
|
|
2782
|
+
if (!isRuleOrGroupValid(rg, validationMap[rg.id ?? /* v8 ignore start -- @preserve */ ""])) return false;
|
|
2350
2783
|
const processedRules = rg.rules.map((rule) => {
|
|
2351
2784
|
if (isRuleGroup(rule)) return processRuleGroup(rule);
|
|
2352
2785
|
const [validationResult, fieldValidator] = validateRule(rule);
|
|
@@ -2374,7 +2807,7 @@ const defaultRuleGroupProcessorJsonLogic = (ruleGroup, options) => {
|
|
|
2374
2807
|
const defaultRuleGroupProcessorLDAP = (ruleGroup, options) => {
|
|
2375
2808
|
const { fields, fallbackExpression, getParseNumberBoolean, placeholderFieldName, placeholderOperatorName, placeholderValueName, ruleProcessor, validateRule, validationMap } = options;
|
|
2376
2809
|
const processRuleGroup = (rg, outermost) => {
|
|
2377
|
-
if (!isRuleOrGroupValid(rg, validationMap[rg.id ?? ""])) return outermost ? fallbackExpression : "";
|
|
2810
|
+
if (!isRuleOrGroupValid(rg, validationMap[rg.id ?? /* v8 ignore start -- @preserve */ ""])) return outermost ? fallbackExpression : "";
|
|
2378
2811
|
const rules = rg.rules.map((rule) => {
|
|
2379
2812
|
if (isRuleGroup(rule)) return processRuleGroup(rule);
|
|
2380
2813
|
const [validationResult, fieldValidator] = validateRule(rule);
|
|
@@ -2407,7 +2840,7 @@ const isBracketed = (str) => str.startsWith("{") && str.endsWith("}");
|
|
|
2407
2840
|
const defaultRuleGroupProcessorMongoDB = (ruleGroup, options, meta) => {
|
|
2408
2841
|
const { fields, fallbackExpression, getParseNumberBoolean, placeholderFieldName, placeholderOperatorName, placeholderValueName, ruleProcessor, validateRule, validationMap } = options;
|
|
2409
2842
|
const processRuleGroup = (rg, outermost) => {
|
|
2410
|
-
if (!isRuleOrGroupValid(rg, validationMap[rg.id ?? ""])) return outermost ? fallbackExpression : "";
|
|
2843
|
+
if (!isRuleOrGroupValid(rg, validationMap[rg.id ?? /* v8 ignore start -- @preserve */ ""])) return outermost ? fallbackExpression : "";
|
|
2411
2844
|
const combinator = `"$${lc(rg.combinator)}"`;
|
|
2412
2845
|
let hasChildRules = false;
|
|
2413
2846
|
const expressions = rg.rules.map((rule) => {
|
|
@@ -2444,7 +2877,7 @@ const defaultRuleGroupProcessorMongoDB = (ruleGroup, options, meta) => {
|
|
|
2444
2877
|
const defaultRuleGroupProcessorNL = (ruleGroup, options) => {
|
|
2445
2878
|
const { fields, fallbackExpression, getParseNumberBoolean, placeholderFieldName, placeholderOperatorName, placeholderValueName, ruleProcessor, translations, validateRule, validationMap } = options;
|
|
2446
2879
|
const processRuleGroup = (rg, outermostOrLonelyInGroup) => {
|
|
2447
|
-
if (!isRuleOrGroupValid(rg, validationMap[rg.id ?? ""])) return outermostOrLonelyInGroup ? fallbackExpression : "";
|
|
2880
|
+
if (!isRuleOrGroupValid(rg, validationMap[rg.id ?? /* v8 ignore start -- @preserve */ ""])) return outermostOrLonelyInGroup ? fallbackExpression : "";
|
|
2448
2881
|
const rg2 = isRuleGroupTypeIC(rg) && rg.rules.some((r) => typeof r === "string" && lc(r) === "xor") ? convertFromIC(rg) : rg;
|
|
2449
2882
|
const processedRules = [];
|
|
2450
2883
|
let precedingCombinator = "";
|
|
@@ -2455,7 +2888,8 @@ const defaultRuleGroupProcessorNL = (ruleGroup, options) => {
|
|
|
2455
2888
|
continue;
|
|
2456
2889
|
}
|
|
2457
2890
|
if (isRuleGroup(rule)) {
|
|
2458
|
-
const processedGroup = processRuleGroup(rule, rg2.rules.length === 1 && !(rg2.not || /^xor$/i.test(rg2.combinator ??
|
|
2891
|
+
const processedGroup = processRuleGroup(rule, rg2.rules.length === 1 && !(rg2.not || /^xor$/i.test(rg2.combinator ??
|
|
2892
|
+
/* v8 ignore start -- @preserve */ "")));
|
|
2459
2893
|
// v8 ignore else
|
|
2460
2894
|
if (processedGroup) {
|
|
2461
2895
|
if (!firstRule && precedingCombinator) {
|
|
@@ -2542,7 +2976,7 @@ const defaultRuleGroupProcessorParameterized = (ruleGroup, options) => {
|
|
|
2542
2976
|
return sql;
|
|
2543
2977
|
};
|
|
2544
2978
|
const processRuleGroup = (rg, outermostOrLonelyInGroup) => {
|
|
2545
|
-
if (!isRuleOrGroupValid(rg, validationMap[rg.id ?? ""])) return outermostOrLonelyInGroup ? fallbackExpression : "";
|
|
2979
|
+
if (!isRuleOrGroupValid(rg, validationMap[rg.id ?? /* v8 ignore start -- @preserve */ ""])) return outermostOrLonelyInGroup ? fallbackExpression : "";
|
|
2546
2980
|
const processedRules = [];
|
|
2547
2981
|
let precedingCombinator = "";
|
|
2548
2982
|
let firstRule = true;
|
|
@@ -2602,7 +3036,7 @@ const prismaFallback = {};
|
|
|
2602
3036
|
const defaultRuleGroupProcessorPrisma = (ruleGroup, options) => {
|
|
2603
3037
|
const { fields, getParseNumberBoolean, placeholderFieldName, placeholderOperatorName, placeholderValueName, ruleProcessor, validateRule, validationMap } = options;
|
|
2604
3038
|
const processRuleGroup = (rg, outermost) => {
|
|
2605
|
-
if (!isRuleOrGroupValid(rg, validationMap[rg.id ?? ""])) return outermost ? prismaFallback : void 0;
|
|
3039
|
+
if (!isRuleOrGroupValid(rg, validationMap[rg.id ?? /* v8 ignore start -- @preserve */ ""])) return outermost ? prismaFallback : void 0;
|
|
2606
3040
|
const combinator = rg.combinator.toUpperCase();
|
|
2607
3041
|
let hasChildRules = false;
|
|
2608
3042
|
const expressions = rg.rules.map((rule) => {
|
|
@@ -2641,7 +3075,7 @@ const defaultRuleGroupProcessorSequelize = (ruleGroup, options) => {
|
|
|
2641
3075
|
const { sequelizeOperators: Op } = context;
|
|
2642
3076
|
if (!Op) return void 0;
|
|
2643
3077
|
const processRuleGroup = (rg, _outermost) => {
|
|
2644
|
-
if (!isRuleOrGroupValid(rg, validationMap[rg.id ?? ""])) return;
|
|
3078
|
+
if (!isRuleOrGroupValid(rg, validationMap[rg.id ?? /* v8 ignore start -- @preserve */ ""])) return;
|
|
2645
3079
|
const combinator = rg.combinator.toUpperCase();
|
|
2646
3080
|
let hasChildRules = false;
|
|
2647
3081
|
const expressions = rg.rules.map((rule) => {
|
|
@@ -2680,7 +3114,7 @@ const defaultRuleGroupProcessorSequelize = (ruleGroup, options) => {
|
|
|
2680
3114
|
const defaultRuleGroupProcessorSPARQL = (ruleGroup, options) => {
|
|
2681
3115
|
const { fields, fallbackExpression, getParseNumberBoolean, placeholderFieldName, placeholderOperatorName, placeholderValueName, ruleProcessor, validateRule, validationMap } = options;
|
|
2682
3116
|
const processRuleGroup = (rg, outermost) => {
|
|
2683
|
-
if (!isRuleOrGroupValid(rg, validationMap[rg.id ?? ""])) return outermost ? fallbackExpression : "";
|
|
3117
|
+
if (!isRuleOrGroupValid(rg, validationMap[rg.id ?? /* v8 ignore start -- @preserve */ ""])) return outermost ? fallbackExpression : "";
|
|
2684
3118
|
const processedRules = [];
|
|
2685
3119
|
let precedingCombinator = "";
|
|
2686
3120
|
let firstRule = true;
|
|
@@ -2735,7 +3169,7 @@ const defaultRuleGroupProcessorSPARQL = (ruleGroup, options) => {
|
|
|
2735
3169
|
const defaultRuleGroupProcessorSQL = (ruleGroup, options) => {
|
|
2736
3170
|
const { fields, fallbackExpression, getParseNumberBoolean, placeholderFieldName, placeholderOperatorName, placeholderValueName, ruleProcessor, validateRule, validationMap } = options;
|
|
2737
3171
|
const processRuleGroup = (rg, outermostOrLonelyInGroup) => {
|
|
2738
|
-
if (!isRuleOrGroupValid(rg, validationMap[rg.id ?? ""])) return outermostOrLonelyInGroup ? fallbackExpression : "";
|
|
3172
|
+
if (!isRuleOrGroupValid(rg, validationMap[rg.id ?? /* v8 ignore start -- @preserve */ ""])) return outermostOrLonelyInGroup ? fallbackExpression : "";
|
|
2739
3173
|
const processedRules = [];
|
|
2740
3174
|
let precedingCombinator = "";
|
|
2741
3175
|
let firstRule = true;
|
|
@@ -2803,7 +3237,7 @@ const defaultRuleGroupProcessorTanStackDB = (ruleGroup, options) => (refs) => {
|
|
|
2803
3237
|
/* v8 ignore next -- @preserve */
|
|
2804
3238
|
if (refKeys.length === 0) return fallback;
|
|
2805
3239
|
const processRuleGroup = (rg) => {
|
|
2806
|
-
if (!isRuleOrGroupValid(rg, validationMap[rg.id ?? ""])) return;
|
|
3240
|
+
if (!isRuleOrGroupValid(rg, validationMap[rg.id ?? /* v8 ignore start -- @preserve */ ""])) return;
|
|
2807
3241
|
const processedRules = rg.rules.map((rule) => {
|
|
2808
3242
|
if (isRuleGroup(rule)) return processRuleGroup(rule);
|
|
2809
3243
|
const [validationResult, fieldValidator] = validateRule(rule);
|
|
@@ -3206,7 +3640,7 @@ const defaultRuleProcessorCypher = (rule, opts = {}) => {
|
|
|
3206
3640
|
* @group Export
|
|
3207
3641
|
*/
|
|
3208
3642
|
const defaultRuleProcessorDrizzle = (rule, _options) => {
|
|
3209
|
-
const opts = _options ??
|
|
3643
|
+
const opts = _options ?? /* v8 ignore start -- @preserve */ {};
|
|
3210
3644
|
// v8 ignore next
|
|
3211
3645
|
const { parseNumbers, preserveValueOrder, context = {} } = opts;
|
|
3212
3646
|
const { columns, drizzleOperators, useRawFields } = context;
|
|
@@ -3229,10 +3663,11 @@ const defaultRuleProcessorDrizzle = (rule, _options) => {
|
|
|
3229
3663
|
const { mode, threshold } = matchEval;
|
|
3230
3664
|
const subqueryDepth = opts.subqueryDepth ?? 0;
|
|
3231
3665
|
const arrayElementAlias = getSubqueryElementAlias(subqueryDepth);
|
|
3232
|
-
const
|
|
3666
|
+
const sqlQuery = transformQuery(rule.value, { ruleProcessor: (r) => ({
|
|
3233
3667
|
...r,
|
|
3234
3668
|
field: arrayElementAlias
|
|
3235
|
-
}) })
|
|
3669
|
+
}) });
|
|
3670
|
+
const nestedArrayFilter = defaultRuleGroupProcessorDrizzle(sqlQuery, {
|
|
3236
3671
|
...opts,
|
|
3237
3672
|
context: {
|
|
3238
3673
|
...opts.context,
|
|
@@ -3771,7 +4206,7 @@ const defaultValueProcessorNL = (rule, opts = {}) => {
|
|
|
3771
4206
|
const { escapeQuotes, fields, parseNumbers, quoteFieldNamesWith, quoteValuesWith, fieldIdentifierSeparator, translations } = opts;
|
|
3772
4207
|
const valueIsField = rule.valueSource === "field";
|
|
3773
4208
|
const operatorLowerCase = lc(rule.operator);
|
|
3774
|
-
const quoteChar = quoteValuesWith || "'";
|
|
4209
|
+
const quoteChar = quoteValuesWith || /* v8 ignore start -- @preserve */ "'";
|
|
3775
4210
|
const quoteValue = (v) => `${quoteChar}${v}${quoteChar}`;
|
|
3776
4211
|
const escapeValue = (v) => escapeStringValueQuotes(v, quoteChar, escapeQuotes);
|
|
3777
4212
|
const wrapAndEscape = (v) => quoteValue(escapeValue(v));
|
|
@@ -3779,7 +4214,7 @@ const defaultValueProcessorNL = (rule, opts = {}) => {
|
|
|
3779
4214
|
quoteFieldNamesWith,
|
|
3780
4215
|
fieldIdentifierSeparator
|
|
3781
4216
|
});
|
|
3782
|
-
const t = translations ??
|
|
4217
|
+
const t = translations ?? /* v8 ignore start -- @preserve */ {};
|
|
3783
4218
|
const orTL = t.or ?? "or";
|
|
3784
4219
|
const trueTL = t.true ?? "true";
|
|
3785
4220
|
const falseTL = t.false ?? "false";
|
|
@@ -3791,7 +4226,8 @@ const defaultValueProcessorNL = (rule, opts = {}) => {
|
|
|
3791
4226
|
case "between":
|
|
3792
4227
|
case "notbetween": {
|
|
3793
4228
|
if (!valueIsField) return defaultValueProcessorByRule(rule, opts);
|
|
3794
|
-
const valueAsArray = toArray(rule.value, { retainEmptyStrings: true }).slice(0, 2).map((v) => wrapFieldName(getOption(fields ??
|
|
4229
|
+
const valueAsArray = toArray(rule.value, { retainEmptyStrings: true }).slice(0, 2).map((v) => wrapFieldName(getOption(fields ??
|
|
4230
|
+
/* v8 ignore start -- @preserve */ [], v)?.label ?? v));
|
|
3795
4231
|
if (valueAsArray.length < 2 || !isValidValue(valueAsArray[0]) || !isValidValue(valueAsArray[1])) return "";
|
|
3796
4232
|
return defaultValueProcessorByRule({
|
|
3797
4233
|
...rule,
|
|
@@ -3802,14 +4238,16 @@ const defaultValueProcessorNL = (rule, opts = {}) => {
|
|
|
3802
4238
|
case "notin": {
|
|
3803
4239
|
const valueAsArray = toArray(rule.value);
|
|
3804
4240
|
if (valueAsArray.length === 0) return "";
|
|
3805
|
-
const valStringArray = valueAsArray.map((v) => valueIsField ? wrapFieldName(getOption(fields ??
|
|
4241
|
+
const valStringArray = valueAsArray.map((v) => valueIsField ? wrapFieldName(getOption(fields ??
|
|
4242
|
+
/* v8 ignore start -- @preserve */ [], v)?.label ?? v) : shouldRenderAsNumber(v, parseNumbers) ? `${trimIfString(v)}` : `${wrapAndEscape(v)}`);
|
|
3806
4243
|
if (valStringArray.length === 1) return valStringArray[0];
|
|
3807
4244
|
const oxfordComma = useOxfordComma && valStringArray.length > 2 ? "," : "";
|
|
3808
4245
|
return `(${`${valStringArray.slice(0, -1).join(listSep)}${oxfordComma} ${orTL} ${valStringArray.at(-1)}`})`;
|
|
3809
4246
|
}
|
|
3810
4247
|
}
|
|
3811
4248
|
if (typeof rule.value === "boolean") return rule.value ? trueTL : falseTL;
|
|
3812
|
-
return valueIsField ? wrapFieldName(getOption(fields ??
|
|
4249
|
+
return valueIsField ? wrapFieldName(getOption(fields ??
|
|
4250
|
+
/* v8 ignore start -- @preserve */ [], rule.value)?.label ?? rule.value) : shouldRenderAsNumber(rule.value, parseNumbers) ? `${trimIfString(rule.value)}` : `${wrapAndEscape(rule.value)}`;
|
|
3813
4251
|
};
|
|
3814
4252
|
//#endregion
|
|
3815
4253
|
//#region src/utils/formatQuery/defaultRuleProcessorNL.ts
|
|
@@ -3861,7 +4299,7 @@ const defaultOperatorProcessorNL = (rule, opts = {}) => {
|
|
|
3861
4299
|
name: field,
|
|
3862
4300
|
value: field,
|
|
3863
4301
|
label: field
|
|
3864
|
-
} }) ?? []), normalizedOperator) ?? {
|
|
4302
|
+
} }) ?? /* v8 ignore start -- @preserve */ []), normalizedOperator) ?? {
|
|
3865
4303
|
name: normalizedOperator,
|
|
3866
4304
|
value: normalizedOperator,
|
|
3867
4305
|
label: normalizedOperator
|
|
@@ -3877,7 +4315,7 @@ const defaultOperatorProcessorNL = (rule, opts = {}) => {
|
|
|
3877
4315
|
const defaultRuleProcessorNL = (rule, opts) => {
|
|
3878
4316
|
const { field, operator } = rule;
|
|
3879
4317
|
// v8 ignore next
|
|
3880
|
-
const { fieldData, quoteFieldNamesWith = ["", ""], fieldIdentifierSeparator = "", quoteValuesWith = `'`, operatorProcessor = defaultOperatorProcessorNL, valueProcessor = defaultValueProcessorNL, concatOperator = "||", wordOrder = "SVO" } = opts ??
|
|
4318
|
+
const { fieldData, quoteFieldNamesWith = ["", ""], fieldIdentifierSeparator = "", quoteValuesWith = `'`, operatorProcessor = defaultOperatorProcessorNL, valueProcessor = defaultValueProcessorNL, concatOperator = "||", wordOrder = "SVO" } = opts ?? /* v8 ignore start -- @preserve */ {};
|
|
3881
4319
|
const processedField = getQuotedFieldName(fieldData?.label ?? field, {
|
|
3882
4320
|
quoteFieldNamesWith,
|
|
3883
4321
|
fieldIdentifierSeparator
|
|
@@ -4098,7 +4536,7 @@ const defaultRuleProcessorParameterized = (rule, opts, meta) => {
|
|
|
4098
4536
|
}
|
|
4099
4537
|
let paramValue = rule.value;
|
|
4100
4538
|
if (typeof rule.value === "string") if (shouldRenderAsNumber(rule.value, parseNumbers)) paramValue = parseNumber(rule.value, { parseNumbers });
|
|
4101
|
-
else paramValue = /^'.*'$/g.test(value) ? value.replaceAll(/(^'|'$)/g, "") :
|
|
4539
|
+
else paramValue = /^'.*'$/g.test(value) ? value.replaceAll(/(^'|'$)/g, "") : /* v8 ignore next -- @preserve */ value;
|
|
4102
4540
|
let paramName = "";
|
|
4103
4541
|
if (parameterized) params.push(paramValue);
|
|
4104
4542
|
else {
|
|
@@ -4315,7 +4753,7 @@ const defaultRuleProcessorSPARQL = (rule, opts = {}) => {
|
|
|
4315
4753
|
* @group Export
|
|
4316
4754
|
*/
|
|
4317
4755
|
const defaultRuleProcessorTanStackDB = (rule, _options) => {
|
|
4318
|
-
const { parseNumbers, preserveValueOrder, context = {} } = _options ??
|
|
4756
|
+
const { parseNumbers, preserveValueOrder, context = {} } = _options ?? /* v8 ignore start -- @preserve */ {};
|
|
4319
4757
|
const ops = context.tanStackDbOperators;
|
|
4320
4758
|
const refs = context._tanstackDbRefs;
|
|
4321
4759
|
const primaryRef = context._tanstackDbPrimaryRef;
|
|
@@ -4551,7 +4989,8 @@ function formatQuery(ruleGroup, optionParam = {}) {
|
|
|
4551
4989
|
const ruleProcessor = (typeof ruleProcessor_option === "function" ? ruleProcessor_option : null) ?? (valueProcessorCanActAsRuleProcessor.has(format) && typeof ruleProcessor_option !== "function" && valueProcessor_option ? valueProcessor : null) ?? defaultRuleProcessors[format] ?? defaultRuleProcessorSQL;
|
|
4552
4990
|
const quoteFieldNamesWith = getQuoteFieldNamesWithArray(quoteFieldNamesWith_option);
|
|
4553
4991
|
const fields = toFullOptionList(optObj.fields);
|
|
4554
|
-
const getOperators = (f, m) => toFullOptionList(getOperators_option(f, m) ??
|
|
4992
|
+
const getOperators = (f, m) => toFullOptionList(getOperators_option(f, m) ??
|
|
4993
|
+
/* v8 ignore start -- @preserve */ []);
|
|
4555
4994
|
const fallbackExpression = fallbackExpression_option ?? defaultFallbackExpressions[format] ?? defaultFallbackExpressions.sql;
|
|
4556
4995
|
let validationMap = {};
|
|
4557
4996
|
// v8 ignore else
|
|
@@ -4574,7 +5013,7 @@ function formatQuery(ruleGroup, optionParam = {}) {
|
|
|
4574
5013
|
const uniqueFields = toFlatOptionArray(fields);
|
|
4575
5014
|
for (const f of uniqueFields)
|
|
4576
5015
|
// v8 ignore else
|
|
4577
|
-
if (typeof f.validator === "function") validatorMap[f.value ?? f.name] = f.validator;
|
|
5016
|
+
if (typeof f.validator === "function") validatorMap[f.value ?? /* v8 ignore start -- @preserve */ f.name] = f.validator;
|
|
4578
5017
|
const validateRule = (rule) => {
|
|
4579
5018
|
let validationResult;
|
|
4580
5019
|
let fieldValidator;
|
|
@@ -4745,12 +5184,20 @@ const getParentPath = (path) => path.slice(0, -1);
|
|
|
4745
5184
|
/**
|
|
4746
5185
|
* Determines if two paths (each `Path`) are equivalent.
|
|
4747
5186
|
*/
|
|
4748
|
-
const pathsAreEqual = (path1, path2) =>
|
|
5187
|
+
const pathsAreEqual = (path1, path2) => {
|
|
5188
|
+
if (path1.length !== path2.length) return false;
|
|
5189
|
+
for (let i = 0; i < path1.length; i++) if (path1[i] !== path2[i]) return false;
|
|
5190
|
+
return true;
|
|
5191
|
+
};
|
|
4749
5192
|
/**
|
|
4750
5193
|
* Determines if the first path is an ancestor of the second path. The first path must
|
|
4751
5194
|
* be shorter and exactly match the second path up through the length of the first path.
|
|
4752
5195
|
*/
|
|
4753
|
-
const isAncestor = (maybeAncestor, path) =>
|
|
5196
|
+
const isAncestor = (maybeAncestor, path) => {
|
|
5197
|
+
if (maybeAncestor.length >= path.length) return false;
|
|
5198
|
+
for (let i = 0; i < maybeAncestor.length; i++) if (maybeAncestor[i] !== path[i]) return false;
|
|
5199
|
+
return true;
|
|
5200
|
+
};
|
|
4754
5201
|
/**
|
|
4755
5202
|
* Finds the deepest/longest path that two paths have in common.
|
|
4756
5203
|
*/
|
|
@@ -4783,11 +5230,38 @@ const pathIsDisabled = (path, query) => {
|
|
|
4783
5230
|
}
|
|
4784
5231
|
return disabled;
|
|
4785
5232
|
};
|
|
5233
|
+
/**
|
|
5234
|
+
* Determines if the rule or group at the specified path is disabled by `disabledPaths`—the array
|
|
5235
|
+
* form of the `QueryBuilder` `disabled` prop, which disables nodes by position rather than by a
|
|
5236
|
+
* `disabled` property on the node itself. A path is disabled if it appears in `disabledPaths` or
|
|
5237
|
+
* descends from a path that does.
|
|
5238
|
+
*
|
|
5239
|
+
* @group Paths
|
|
5240
|
+
*/
|
|
5241
|
+
const pathIsDisabledByPaths = (path, disabledPaths = []) => disabledPaths.some((p) => pathsAreEqual(p, path) || isAncestor(p, path));
|
|
5242
|
+
/**
|
|
5243
|
+
* Builds the {@link PathInfo} for each child of a group at `path`. A child is disabled if its
|
|
5244
|
+
* parent is disabled or if its own path appears in `disabledPaths`.
|
|
5245
|
+
*
|
|
5246
|
+
* @group Paths
|
|
5247
|
+
*/
|
|
5248
|
+
const derivePathInfo = (path, childCount, { disabled = false, disabledPaths = [] } = {}) => {
|
|
5249
|
+
const paths = [];
|
|
5250
|
+
for (let i = 0; i < childCount; i++) {
|
|
5251
|
+
const thisPath = [...path, i];
|
|
5252
|
+
paths[i] = {
|
|
5253
|
+
path: thisPath,
|
|
5254
|
+
disabled: disabled || disabledPaths.some((p) => pathsAreEqual(thisPath, p))
|
|
5255
|
+
};
|
|
5256
|
+
}
|
|
5257
|
+
return paths;
|
|
5258
|
+
};
|
|
4786
5259
|
//#endregion
|
|
4787
5260
|
//#region src/utils/generateAccessibleDescription.ts
|
|
4788
5261
|
const generateAccessibleDescription = (params) => pathsAreEqual([], params.path) ? `Query builder` : `Rule group at path ${params.path.join("-")}`;
|
|
4789
5262
|
//#endregion
|
|
4790
5263
|
//#region src/utils/generateID.ts
|
|
5264
|
+
/* v8 ignore file -- this is fine */
|
|
4791
5265
|
const cryptoModule = globalThis.crypto;
|
|
4792
5266
|
const uuidV4regex = /^[\da-f]{8}-[\da-f]{4}-4[\da-f]{3}-[89ab][\da-f]{3}-[\da-f]{12}$/i;
|
|
4793
5267
|
/**
|
|
@@ -4836,7 +5310,7 @@ const dummyFD$1 = {
|
|
|
4836
5310
|
* `FullOption<MatchMode>[]` of all match modes by default.
|
|
4837
5311
|
*/
|
|
4838
5312
|
const getMatchModesUtil = (fieldData, getMatchModes) => {
|
|
4839
|
-
const fd = fieldData ? toFullOption(fieldData) :
|
|
5313
|
+
const fd = fieldData ? toFullOption(fieldData) : /* v8 ignore next -- @preserve */ dummyFD$1;
|
|
4840
5314
|
let matchModes = fd.matchModes ?? false;
|
|
4841
5315
|
if (!matchModes && getMatchModes) matchModes = getMatchModes(fd.value, { fieldData: fd });
|
|
4842
5316
|
if (matchModes === true) return defaultMatchModes;
|
|
@@ -4854,7 +5328,7 @@ const getFirstOptionsFrom = (opts, r, listsAsArrays) => {
|
|
|
4854
5328
|
const firstOption = getFirstOption(opts);
|
|
4855
5329
|
if (r.operator === "between" || r.operator === "notBetween") {
|
|
4856
5330
|
const valueAsArray = [firstOption, firstOption];
|
|
4857
|
-
return listsAsArrays ? valueAsArray : joinWith(valueAsArray.map((v) => v ?? ""), ",");
|
|
5331
|
+
return listsAsArrays ? valueAsArray : joinWith(valueAsArray.map((v) => v ?? /* v8 ignore start -- @preserve */ ""), ",");
|
|
4858
5332
|
}
|
|
4859
5333
|
return firstOption;
|
|
4860
5334
|
};
|
|
@@ -4990,6 +5464,9 @@ const mergeClassnames = (...args) => ({
|
|
|
4990
5464
|
removeRule: joinClassnamesByName("removeRule", args),
|
|
4991
5465
|
notToggle: joinClassnamesByName("notToggle", args),
|
|
4992
5466
|
shiftActions: joinClassnamesByName("shiftActions", args),
|
|
5467
|
+
undoRedoActions: joinClassnamesByName("undoRedoActions", args),
|
|
5468
|
+
undoAction: joinClassnamesByName("undoAction", args),
|
|
5469
|
+
redoAction: joinClassnamesByName("redoAction", args),
|
|
4993
5470
|
dragHandle: joinClassnamesByName("dragHandle", args),
|
|
4994
5471
|
lockRule: joinClassnamesByName("lockRule", args),
|
|
4995
5472
|
lockGroup: joinClassnamesByName("lockGroup", args),
|
|
@@ -5019,6 +5496,58 @@ const mergeClassnames = (...args) => ({
|
|
|
5019
5496
|
valueDateTimeRelative: joinClassnamesByName("valueDateTimeRelative", args)
|
|
5020
5497
|
});
|
|
5021
5498
|
//#endregion
|
|
5499
|
+
//#region src/utils/optionResolvers.ts
|
|
5500
|
+
/**
|
|
5501
|
+
* Resolves the operator list for a field, applying the same precedence as the `QueryBuilder`
|
|
5502
|
+
* component: the field's own `operators`, then the `getOperators` callback, then the
|
|
5503
|
+
* query-level operator list.
|
|
5504
|
+
*
|
|
5505
|
+
* @group Option Lists
|
|
5506
|
+
*/
|
|
5507
|
+
const resolveOperatorList = ({ field, fieldData, getOperators, operators, placeholder, baseOption, autoSelectOption }) => prepareOptionList({
|
|
5508
|
+
optionList: fieldData?.operators ?? getOperators?.(field, { fieldData }) ?? operators,
|
|
5509
|
+
placeholder,
|
|
5510
|
+
baseOption,
|
|
5511
|
+
labelMap: defaultOperatorLabelMap,
|
|
5512
|
+
autoSelectOption
|
|
5513
|
+
}).optionList;
|
|
5514
|
+
/**
|
|
5515
|
+
* Resolves the default operator for a field, applying the same precedence as the `QueryBuilder`
|
|
5516
|
+
* component: the field's own `defaultOperator`, then the `getDefaultOperator` option (a string or
|
|
5517
|
+
* a function), then the first available operator.
|
|
5518
|
+
*
|
|
5519
|
+
* @group Option Lists
|
|
5520
|
+
*/
|
|
5521
|
+
const resolveDefaultOperator = ({ field, fieldData, getDefaultOperator, getOperators }) => {
|
|
5522
|
+
if (fieldData?.defaultOperator) return fieldData.defaultOperator;
|
|
5523
|
+
if (getDefaultOperator) return typeof getDefaultOperator === "function" ? getDefaultOperator(field, { fieldData }) : getDefaultOperator;
|
|
5524
|
+
return getFirstOption(getOperators(field, { fieldData })) ?? "";
|
|
5525
|
+
};
|
|
5526
|
+
/**
|
|
5527
|
+
* Resolves the value editor type for a field/operator pair, applying the same precedence as the
|
|
5528
|
+
* `QueryBuilder` component: the field's own `valueEditorType` (a string or a function of the
|
|
5529
|
+
* operator), then the `getValueEditorType` callback, then `"text"`.
|
|
5530
|
+
*
|
|
5531
|
+
* @group Option Lists
|
|
5532
|
+
*/
|
|
5533
|
+
const resolveValueEditorType = ({ field, operator, fieldData, getValueEditorType }) => {
|
|
5534
|
+
if (fieldData?.valueEditorType) return typeof fieldData.valueEditorType === "function" ? fieldData.valueEditorType(operator) : fieldData.valueEditorType;
|
|
5535
|
+
return getValueEditorType?.(field, operator, { fieldData }) ?? "text";
|
|
5536
|
+
};
|
|
5537
|
+
/**
|
|
5538
|
+
* Resolves the value option list for a field/operator pair, applying the same precedence as the
|
|
5539
|
+
* `QueryBuilder` component: the field's own `values`, then the `getValues` callback, then an
|
|
5540
|
+
* empty list.
|
|
5541
|
+
*
|
|
5542
|
+
* @group Option Lists
|
|
5543
|
+
*/
|
|
5544
|
+
const resolveValueList = ({ field, operator, fieldData, getValues, placeholder, baseOption, autoSelectOption }) => prepareOptionList({
|
|
5545
|
+
optionList: fieldData?.values ?? getValues?.(field, operator, { fieldData }) ?? [],
|
|
5546
|
+
placeholder,
|
|
5547
|
+
baseOption,
|
|
5548
|
+
autoSelectOption
|
|
5549
|
+
}).optionList;
|
|
5550
|
+
//#endregion
|
|
5022
5551
|
//#region src/utils/preferProp.ts
|
|
5023
5552
|
const preferPropDefaultTrue = (prop, context) => prop === false ? false : prop ? true : !(context === false);
|
|
5024
5553
|
const preferPropDefaultFalse = (prop, context) => prop ? true : prop === false ? false : !!context;
|
|
@@ -5083,6 +5612,21 @@ const prepareRuleGroup = (queryObject, { idGenerator = generateID } = {}) => {
|
|
|
5083
5612
|
* Ensures that a rule or group is valid. See {@link prepareRule} and {@link prepareRuleGroup}.
|
|
5084
5613
|
*/
|
|
5085
5614
|
const prepareRuleOrGroup = (rg, { idGenerator = generateID } = {}) => isRuleGroup(rg) ? prepareRuleGroup(rg, { idGenerator }) : prepareRule(rg, { idGenerator });
|
|
5615
|
+
/**
|
|
5616
|
+
* Resolves the query a query builder should render from the available sources, in precedence
|
|
5617
|
+
* order: the controlled `query`, then whatever is already in the store, then the uncontrolled
|
|
5618
|
+
* `defaultQuery`, then a freshly created empty group.
|
|
5619
|
+
*
|
|
5620
|
+
* The result is prepared with {@link prepareRuleGroup} unless it already has an `id`, which is
|
|
5621
|
+
* taken to mean it has been prepared before—most often because the caller is passing back the
|
|
5622
|
+
* object it received from `onQueryChange`.
|
|
5623
|
+
*
|
|
5624
|
+
* @group Query Tools
|
|
5625
|
+
*/
|
|
5626
|
+
const resolveCandidateQuery = (sources, options) => {
|
|
5627
|
+
const candidateQuery = sources.query ?? sources.storeQuery ?? sources.defaultQuery ?? sources.fallbackQuery;
|
|
5628
|
+
return candidateQuery.id ? candidateQuery : prepareRuleGroup(candidateQuery, options);
|
|
5629
|
+
};
|
|
5086
5630
|
//#endregion
|
|
5087
5631
|
//#region src/utils/regenerateIDs.ts
|
|
5088
5632
|
/**
|
|
@@ -5112,6 +5656,28 @@ const regenerateIDs = (subject, { idGenerator = generateID } = {}) => {
|
|
|
5112
5656
|
//#endregion
|
|
5113
5657
|
//#region src/utils/queryTools.ts
|
|
5114
5658
|
/**
|
|
5659
|
+
* Whether a mutation targeting `pathOrID` is blocked by the given guards, and why.
|
|
5660
|
+
* Returns `null` when the mutation may proceed.
|
|
5661
|
+
*
|
|
5662
|
+
* Exported so that callers which run their own logic before mutating—such as a UI layer that
|
|
5663
|
+
* invokes a confirmation callback—can apply the same rules without duplicating them.
|
|
5664
|
+
*
|
|
5665
|
+
* @group Query Tools
|
|
5666
|
+
*/
|
|
5667
|
+
const getGuardAbortReason = (query, pathOrID, guards = {}, { asParent = false } = {}) => {
|
|
5668
|
+
if (guards.queryDisabled) return asParent ? "parent-disabled" : "target-disabled";
|
|
5669
|
+
if (!guards.respectDisabled || pathOrID === void 0) return null;
|
|
5670
|
+
const path = Array.isArray(pathOrID) ? pathOrID : getPathOfID(pathOrID, query);
|
|
5671
|
+
if (path && (pathIsDisabled(path, query) || pathIsDisabledByPaths(path, guards.disabledPaths))) return asParent ? "parent-disabled" : "target-disabled";
|
|
5672
|
+
return null;
|
|
5673
|
+
};
|
|
5674
|
+
/**
|
|
5675
|
+
* Whether adding a group beneath `parentPath` would exceed `maxLevels`.
|
|
5676
|
+
*
|
|
5677
|
+
* @group Query Tools
|
|
5678
|
+
*/
|
|
5679
|
+
const exceedsMaxLevels = (parentPath, { maxLevels = Infinity } = {}) => !!parentPath && parentPath.length >= maxLevels;
|
|
5680
|
+
/**
|
|
5115
5681
|
* Adds a rule or group to a query without mutating the original query.
|
|
5116
5682
|
*
|
|
5117
5683
|
* @returns A new query with the rule or group added.
|
|
@@ -5127,9 +5693,42 @@ const add = (query, ruleOrGroup, parentPathOrID, options = {}) => (0, immer.prod
|
|
|
5127
5693
|
* @group Query Tools
|
|
5128
5694
|
*/
|
|
5129
5695
|
const addInPlace = (query, ruleOrGroup, parentPathOrID, options = {}) => {
|
|
5130
|
-
const { combinators = defaultCombinators, combinatorPreceding, idGenerator = generateID } = options;
|
|
5696
|
+
const { combinators = defaultCombinators, combinatorPreceding, idGenerator = generateID, onAbort } = options;
|
|
5131
5697
|
const parent = Array.isArray(parentPathOrID) ? findPath(parentPathOrID, query) : findID(parentPathOrID, query);
|
|
5132
|
-
if (!parent
|
|
5698
|
+
if (!parent) {
|
|
5699
|
+
onAbort?.({
|
|
5700
|
+
reason: "parent-not-found",
|
|
5701
|
+
operation: "add",
|
|
5702
|
+
pathOrID: parentPathOrID
|
|
5703
|
+
});
|
|
5704
|
+
return query;
|
|
5705
|
+
}
|
|
5706
|
+
if (!isRuleGroup(parent)) {
|
|
5707
|
+
onAbort?.({
|
|
5708
|
+
reason: "parent-not-a-group",
|
|
5709
|
+
operation: "add",
|
|
5710
|
+
pathOrID: parentPathOrID
|
|
5711
|
+
});
|
|
5712
|
+
return query;
|
|
5713
|
+
}
|
|
5714
|
+
const parentPath = Array.isArray(parentPathOrID) ? parentPathOrID : getPathOfID(parentPathOrID, query);
|
|
5715
|
+
const addGuardReason = getGuardAbortReason(query, parentPath, options, { asParent: true });
|
|
5716
|
+
if (addGuardReason) {
|
|
5717
|
+
onAbort?.({
|
|
5718
|
+
reason: addGuardReason,
|
|
5719
|
+
operation: "add",
|
|
5720
|
+
pathOrID: parentPathOrID
|
|
5721
|
+
});
|
|
5722
|
+
return query;
|
|
5723
|
+
}
|
|
5724
|
+
if (isRuleGroup(ruleOrGroup) && exceedsMaxLevels(parentPath, options)) {
|
|
5725
|
+
onAbort?.({
|
|
5726
|
+
reason: "max-levels-exceeded",
|
|
5727
|
+
operation: "add",
|
|
5728
|
+
pathOrID: parentPathOrID
|
|
5729
|
+
});
|
|
5730
|
+
return query;
|
|
5731
|
+
}
|
|
5133
5732
|
if (isRuleGroupTypeIC(parent) && parent.rules.length > 0) {
|
|
5134
5733
|
const prevCombinator = parent.rules.at(-2);
|
|
5135
5734
|
parent.rules.push(combinatorPreceding ?? (typeof prevCombinator === "string" ? prevCombinator : getFirstOption(combinators)));
|
|
@@ -5150,7 +5749,7 @@ const updatePropRank = {
|
|
|
5150
5749
|
value: 4
|
|
5151
5750
|
};
|
|
5152
5751
|
/** Stable-sorts `[prop, value]` entries by {@link updatePropRank}. */
|
|
5153
|
-
const orderUpdateEntries = (entries) => entries.
|
|
5752
|
+
const orderUpdateEntries = (entries) => entries.toSorted((x, y) => (updatePropRank[x[0]] ?? 3) - (updatePropRank[y[0]] ?? 3));
|
|
5154
5753
|
/**
|
|
5155
5754
|
* Normalizes the variadic {@link update}/{@link updateInPlace} arguments (single
|
|
5156
5755
|
* prop+value, parallel arrays, or property map) into a canonically-ordered list
|
|
@@ -5213,19 +5812,69 @@ const updateInPlace = ((query, a, b, c, d) => applyUpdatesInPlace(query, a, b, c
|
|
|
5213
5812
|
* Updates a single property of a rule or group within a query in place.
|
|
5214
5813
|
*/
|
|
5215
5814
|
const updateInPlaceSingle = (query, prop, value, pathOrID, options = {}) => {
|
|
5216
|
-
const { resetOnFieldChange: _resetOnFieldChange = true, resetOnOperatorChange = false, getRuleDefaultOperator = () => "=", getValueSources = () => ["value"], getRuleDefaultValue = () => "", getMatchModes = () => [] } = options;
|
|
5815
|
+
const { resetOnFieldChange: _resetOnFieldChange = true, resetOnOperatorChange = false, getRuleDefaultOperator = () => "=", getValueSources = () => ["value"], getRuleDefaultValue = () => "", getMatchModes = () => [], onAbort } = options;
|
|
5217
5816
|
let resetOnFieldChange = _resetOnFieldChange;
|
|
5218
5817
|
const path = Array.isArray(pathOrID) ? pathOrID : getPathOfID(pathOrID, query);
|
|
5219
|
-
if (!path)
|
|
5818
|
+
if (!path) {
|
|
5819
|
+
onAbort?.({
|
|
5820
|
+
reason: "target-not-found",
|
|
5821
|
+
operation: "update",
|
|
5822
|
+
pathOrID
|
|
5823
|
+
});
|
|
5824
|
+
return query;
|
|
5825
|
+
}
|
|
5826
|
+
const updateGuards = prop === "disabled" ? { queryDisabled: options.queryDisabled } : options;
|
|
5827
|
+
const updateGuardReason = getGuardAbortReason(query, path, updateGuards);
|
|
5828
|
+
if (updateGuardReason) {
|
|
5829
|
+
onAbort?.({
|
|
5830
|
+
reason: updateGuardReason,
|
|
5831
|
+
operation: "update",
|
|
5832
|
+
pathOrID
|
|
5833
|
+
});
|
|
5834
|
+
return query;
|
|
5835
|
+
}
|
|
5220
5836
|
if (prop === "combinator" && !isRuleGroupType(query)) {
|
|
5221
|
-
const
|
|
5222
|
-
if (
|
|
5837
|
+
const parent = findPath(getParentPath(path), query);
|
|
5838
|
+
if (!parent || !isRuleGroup(parent)) {
|
|
5839
|
+
onAbort?.({
|
|
5840
|
+
reason: "target-not-found",
|
|
5841
|
+
operation: "update",
|
|
5842
|
+
pathOrID
|
|
5843
|
+
});
|
|
5844
|
+
return query;
|
|
5845
|
+
}
|
|
5846
|
+
const parentRules = parent.rules;
|
|
5847
|
+
if (path.at(-1) % 2 === 1) if (parentRules[path.at(-1)] === value) onAbort?.({
|
|
5848
|
+
reason: "no-change",
|
|
5849
|
+
operation: "update",
|
|
5850
|
+
pathOrID
|
|
5851
|
+
});
|
|
5852
|
+
else parentRules[path.at(-1)] = value;
|
|
5853
|
+
else onAbort?.({
|
|
5854
|
+
reason: "not-a-combinator-slot",
|
|
5855
|
+
operation: "update",
|
|
5856
|
+
pathOrID
|
|
5857
|
+
});
|
|
5223
5858
|
return query;
|
|
5224
5859
|
}
|
|
5225
5860
|
const ruleOrGroup = findPath(path, query);
|
|
5226
|
-
if (!ruleOrGroup)
|
|
5861
|
+
if (!ruleOrGroup) {
|
|
5862
|
+
onAbort?.({
|
|
5863
|
+
reason: "target-not-found",
|
|
5864
|
+
operation: "update",
|
|
5865
|
+
pathOrID
|
|
5866
|
+
});
|
|
5867
|
+
return query;
|
|
5868
|
+
}
|
|
5227
5869
|
const isGroup = isRuleGroup(ruleOrGroup);
|
|
5228
|
-
if (ruleOrGroup[prop] === value)
|
|
5870
|
+
if (ruleOrGroup[prop] === value) {
|
|
5871
|
+
onAbort?.({
|
|
5872
|
+
reason: "no-change",
|
|
5873
|
+
operation: "update",
|
|
5874
|
+
pathOrID
|
|
5875
|
+
});
|
|
5876
|
+
return query;
|
|
5877
|
+
}
|
|
5229
5878
|
if (prop !== "valueSource") ruleOrGroup[prop] = value;
|
|
5230
5879
|
if (isGroup) return query;
|
|
5231
5880
|
let resetValueSource = false;
|
|
@@ -5271,7 +5920,7 @@ const updateInPlaceSingle = (query, prop, value, pathOrID, options = {}) => {
|
|
|
5271
5920
|
*
|
|
5272
5921
|
* @group Query Tools
|
|
5273
5922
|
*/
|
|
5274
|
-
const remove = (query, pathOrID) => (0, immer.produce)(query, (q) => removeInPlace(q, pathOrID));
|
|
5923
|
+
const remove = (query, pathOrID, options = {}) => (0, immer.produce)(query, (q) => removeInPlace(q, pathOrID, options));
|
|
5275
5924
|
/**
|
|
5276
5925
|
* Removes a rule or group from a query in place.
|
|
5277
5926
|
*
|
|
@@ -5279,12 +5928,45 @@ const remove = (query, pathOrID) => (0, immer.produce)(query, (q) => removeInPla
|
|
|
5279
5928
|
*
|
|
5280
5929
|
* @group Query Tools
|
|
5281
5930
|
*/
|
|
5282
|
-
const removeInPlace = (query, pathOrID) => {
|
|
5931
|
+
const removeInPlace = (query, pathOrID, options = {}) => {
|
|
5932
|
+
const { onAbort } = options;
|
|
5283
5933
|
const path = Array.isArray(pathOrID) ? pathOrID : getPathOfID(pathOrID, query);
|
|
5284
|
-
if (!path
|
|
5934
|
+
if (!path) {
|
|
5935
|
+
onAbort?.({
|
|
5936
|
+
reason: "target-not-found",
|
|
5937
|
+
operation: "remove",
|
|
5938
|
+
pathOrID
|
|
5939
|
+
});
|
|
5940
|
+
return query;
|
|
5941
|
+
}
|
|
5942
|
+
if (path.length === 0) {
|
|
5943
|
+
onAbort?.({
|
|
5944
|
+
reason: "root-not-allowed",
|
|
5945
|
+
operation: "remove",
|
|
5946
|
+
pathOrID
|
|
5947
|
+
});
|
|
5948
|
+
return query;
|
|
5949
|
+
}
|
|
5950
|
+
const removeGuardReason = getGuardAbortReason(query, path, options);
|
|
5951
|
+
if (removeGuardReason) {
|
|
5952
|
+
onAbort?.({
|
|
5953
|
+
reason: removeGuardReason,
|
|
5954
|
+
operation: "remove",
|
|
5955
|
+
pathOrID
|
|
5956
|
+
});
|
|
5957
|
+
return query;
|
|
5958
|
+
}
|
|
5959
|
+
if (!findPath(path, query)) {
|
|
5960
|
+
onAbort?.({
|
|
5961
|
+
reason: "target-not-found",
|
|
5962
|
+
operation: "remove",
|
|
5963
|
+
pathOrID
|
|
5964
|
+
});
|
|
5965
|
+
return query;
|
|
5966
|
+
}
|
|
5285
5967
|
const index = path.at(-1);
|
|
5286
5968
|
const parent = findPath(getParentPath(path), query);
|
|
5287
|
-
if (
|
|
5969
|
+
if (!isRuleGroupType(parent) && parent.rules.length > 1) {
|
|
5288
5970
|
const idxStartDelete = index === 0 ? 0 : index - 1;
|
|
5289
5971
|
parent.rules.splice(idxStartDelete, 2);
|
|
5290
5972
|
} else parent.rules.splice(index, 1);
|
|
@@ -5309,7 +5991,8 @@ const getNextPath = (query, currentPath, newPathOrShiftDirection) => {
|
|
|
5309
5991
|
return [...getParentPath(parentPath), parentPath.at(-1) + 1];
|
|
5310
5992
|
} else {
|
|
5311
5993
|
const evaluationPath = [...getParentPath(currentPath), currentPath.at(-1) + (ic ? 2 : 1)];
|
|
5312
|
-
|
|
5994
|
+
const entityToEvaluate = findPath(evaluationPath, query);
|
|
5995
|
+
if (isRuleGroup(entityToEvaluate)) return [...evaluationPath, 0];
|
|
5313
5996
|
else return [...getParentPath(currentPath), currentPath.at(-1) + (ic ? 3 : 2)];
|
|
5314
5997
|
}
|
|
5315
5998
|
return currentPath;
|
|
@@ -5332,39 +6015,96 @@ const move = (query, oldPathOrID, newPath, options = {}) => (0, immer.produce)(q
|
|
|
5332
6015
|
* @group Query Tools
|
|
5333
6016
|
*/
|
|
5334
6017
|
const moveInPlace = (query, oldPathOrID, newPath, options = {}) => {
|
|
5335
|
-
const { clone = false, combinators = defaultCombinators, idGenerator = generateID } = options;
|
|
6018
|
+
const { clone = false, combinators = defaultCombinators, idGenerator = generateID, onAbort } = options;
|
|
5336
6019
|
const oldPath = Array.isArray(oldPathOrID) ? oldPathOrID : getPathOfID(oldPathOrID, query);
|
|
5337
|
-
if (!oldPath)
|
|
5338
|
-
|
|
5339
|
-
|
|
6020
|
+
if (!oldPath) {
|
|
6021
|
+
onAbort?.({
|
|
6022
|
+
reason: "target-not-found",
|
|
6023
|
+
operation: "move",
|
|
6024
|
+
pathOrID: oldPathOrID
|
|
6025
|
+
});
|
|
6026
|
+
return query;
|
|
6027
|
+
}
|
|
5340
6028
|
const ruleOrGroupOriginal = findPath(oldPath, query);
|
|
5341
|
-
if (!ruleOrGroupOriginal)
|
|
5342
|
-
|
|
5343
|
-
|
|
5344
|
-
|
|
5345
|
-
|
|
5346
|
-
|
|
5347
|
-
|
|
5348
|
-
|
|
5349
|
-
|
|
5350
|
-
|
|
6029
|
+
if (!ruleOrGroupOriginal) {
|
|
6030
|
+
onAbort?.({
|
|
6031
|
+
reason: "target-not-found",
|
|
6032
|
+
operation: "move",
|
|
6033
|
+
pathOrID: oldPathOrID
|
|
6034
|
+
});
|
|
6035
|
+
return query;
|
|
6036
|
+
}
|
|
6037
|
+
const nextPath = getNextPath(query, oldPath, newPath);
|
|
6038
|
+
if (oldPath.length === 0) {
|
|
6039
|
+
onAbort?.({
|
|
6040
|
+
reason: "root-not-allowed",
|
|
6041
|
+
operation: "move",
|
|
6042
|
+
pathOrID: oldPathOrID
|
|
6043
|
+
});
|
|
6044
|
+
return query;
|
|
6045
|
+
}
|
|
6046
|
+
const moveGuardReason = getGuardAbortReason(query, oldPath, options);
|
|
6047
|
+
if (moveGuardReason) {
|
|
6048
|
+
onAbort?.({
|
|
6049
|
+
reason: moveGuardReason,
|
|
6050
|
+
operation: "move",
|
|
6051
|
+
pathOrID: oldPathOrID
|
|
6052
|
+
});
|
|
6053
|
+
return query;
|
|
6054
|
+
}
|
|
6055
|
+
if (pathsAreEqual(oldPath, nextPath)) {
|
|
6056
|
+
onAbort?.({
|
|
6057
|
+
reason: "same-location",
|
|
6058
|
+
operation: "move",
|
|
6059
|
+
pathOrID: oldPathOrID
|
|
6060
|
+
});
|
|
6061
|
+
return query;
|
|
6062
|
+
}
|
|
6063
|
+
if (!findPath(getParentPath(nextPath), query)) {
|
|
6064
|
+
onAbort?.({
|
|
6065
|
+
reason: "destination-not-found",
|
|
6066
|
+
operation: "move",
|
|
6067
|
+
pathOrID: newPath
|
|
6068
|
+
});
|
|
6069
|
+
return query;
|
|
6070
|
+
}
|
|
6071
|
+
if (!clone && isAncestor(oldPath, nextPath)) {
|
|
6072
|
+
onAbort?.({
|
|
6073
|
+
reason: "destination-not-found",
|
|
6074
|
+
operation: "move",
|
|
6075
|
+
pathOrID: newPath
|
|
6076
|
+
});
|
|
6077
|
+
return query;
|
|
6078
|
+
}
|
|
6079
|
+
const ruleOrGroup = clone ? regenerateIDs((0, immer.isDraft)(ruleOrGroupOriginal) ? (0, immer.current)(ruleOrGroupOriginal) : ruleOrGroupOriginal, { idGenerator }) : ruleOrGroupOriginal;
|
|
6080
|
+
const independentCombinators = isRuleGroupTypeIC(query);
|
|
6081
|
+
const parentOfRuleToRemove = findPath(getParentPath(oldPath), query);
|
|
6082
|
+
const ruleToRemoveIndex = oldPath.at(-1);
|
|
6083
|
+
const oldPrevCombinator = independentCombinators && ruleToRemoveIndex > 0 ? parentOfRuleToRemove.rules[ruleToRemoveIndex - 1] : null;
|
|
6084
|
+
const oldNextCombinator = independentCombinators && ruleToRemoveIndex < parentOfRuleToRemove.rules.length - 1 ? parentOfRuleToRemove.rules[ruleToRemoveIndex + 1] : null;
|
|
6085
|
+
if (!clone) {
|
|
6086
|
+
const idxStartDelete = independentCombinators ? Math.max(0, ruleToRemoveIndex - 1) : ruleToRemoveIndex;
|
|
6087
|
+
const deleteLength = independentCombinators ? 2 : 1;
|
|
5351
6088
|
parentOfRuleToRemove.rules.splice(idxStartDelete, deleteLength);
|
|
5352
6089
|
}
|
|
5353
6090
|
const newNewPath = [...nextPath];
|
|
6091
|
+
if (independentCombinators && newNewPath.at(-1) % 2 === 1) newNewPath[newNewPath.length - 1] += 1;
|
|
5354
6092
|
const commonAncestorPath = getCommonAncestorPath(oldPath, nextPath);
|
|
5355
|
-
if (!clone && oldPath.length === commonAncestorPath.length + 1 &&
|
|
5356
|
-
const
|
|
6093
|
+
if (!clone && oldPath.length === commonAncestorPath.length + 1 && newNewPath[commonAncestorPath.length] > oldPath[commonAncestorPath.length]) newNewPath[commonAncestorPath.length] -= independentCombinators ? 2 : 1;
|
|
6094
|
+
const newNewParentPath = getParentPath(newNewPath);
|
|
6095
|
+
const parentToInsertInto = findPath(newNewParentPath, query);
|
|
5357
6096
|
const newIndex = newNewPath.at(-1);
|
|
6097
|
+
const spliceIndex = independentCombinators && newIndex > 0 ? newIndex - 1 : newIndex;
|
|
5358
6098
|
/**
|
|
5359
6099
|
* This function 1) glosses over the need for type assertions to splice directly
|
|
5360
6100
|
* into `parentToInsertInto.rules`, and 2) shortens the actual insertion code.
|
|
5361
6101
|
*/
|
|
5362
|
-
const insertRuleOrGroup = (...args) => parentToInsertInto.rules.splice(
|
|
6102
|
+
const insertRuleOrGroup = (...args) => parentToInsertInto.rules.splice(spliceIndex, 0, ...args);
|
|
5363
6103
|
if (parentToInsertInto.rules.length === 0 || !independentCombinators) insertRuleOrGroup(ruleOrGroup);
|
|
5364
6104
|
else if (newIndex === 0) if (ruleToRemoveIndex === 0 && oldNextCombinator) insertRuleOrGroup(ruleOrGroup, oldNextCombinator);
|
|
5365
6105
|
else insertRuleOrGroup(ruleOrGroup, parentToInsertInto.rules[1] ?? oldPrevCombinator ?? getFirstOption(combinators));
|
|
5366
6106
|
else if (oldPrevCombinator) insertRuleOrGroup(oldPrevCombinator, ruleOrGroup);
|
|
5367
|
-
else insertRuleOrGroup(parentToInsertInto.rules[
|
|
6107
|
+
else insertRuleOrGroup(parentToInsertInto.rules[spliceIndex - 2] ?? oldNextCombinator ?? getFirstOption(combinators), ruleOrGroup);
|
|
5368
6108
|
return query;
|
|
5369
6109
|
};
|
|
5370
6110
|
/**
|
|
@@ -5383,9 +6123,42 @@ const insert = (query, ruleOrGroup, path, options = {}) => (0, immer.produce)(qu
|
|
|
5383
6123
|
* @group Query Tools
|
|
5384
6124
|
*/
|
|
5385
6125
|
const insertInPlace = (query, ruleOrGroup, path, options = {}) => {
|
|
5386
|
-
const { combinators = defaultCombinators, combinatorPreceding, combinatorSucceeding, idGenerator = generateID, replace = false } = options;
|
|
6126
|
+
const { combinators = defaultCombinators, combinatorPreceding, combinatorSucceeding, idGenerator = generateID, replace = false, onAbort } = options;
|
|
5387
6127
|
const parentToInsertInto = findPath(getParentPath(path), query);
|
|
5388
|
-
if (!parentToInsertInto
|
|
6128
|
+
if (!parentToInsertInto) {
|
|
6129
|
+
onAbort?.({
|
|
6130
|
+
reason: "parent-not-found",
|
|
6131
|
+
operation: "insert",
|
|
6132
|
+
pathOrID: path
|
|
6133
|
+
});
|
|
6134
|
+
return query;
|
|
6135
|
+
}
|
|
6136
|
+
if (!isRuleGroup(parentToInsertInto)) {
|
|
6137
|
+
onAbort?.({
|
|
6138
|
+
reason: "parent-not-a-group",
|
|
6139
|
+
operation: "insert",
|
|
6140
|
+
pathOrID: path
|
|
6141
|
+
});
|
|
6142
|
+
return query;
|
|
6143
|
+
}
|
|
6144
|
+
const parentPath = getParentPath(path);
|
|
6145
|
+
const insertGuardReason = getGuardAbortReason(query, parentPath, options, { asParent: true });
|
|
6146
|
+
if (insertGuardReason) {
|
|
6147
|
+
onAbort?.({
|
|
6148
|
+
reason: insertGuardReason,
|
|
6149
|
+
operation: "insert",
|
|
6150
|
+
pathOrID: path
|
|
6151
|
+
});
|
|
6152
|
+
return query;
|
|
6153
|
+
}
|
|
6154
|
+
if (isRuleGroup(ruleOrGroup) && exceedsMaxLevels(parentPath, options)) {
|
|
6155
|
+
onAbort?.({
|
|
6156
|
+
reason: "max-levels-exceeded",
|
|
6157
|
+
operation: "insert",
|
|
6158
|
+
pathOrID: path
|
|
6159
|
+
});
|
|
6160
|
+
return query;
|
|
6161
|
+
}
|
|
5389
6162
|
const rorg = regenerateIDs(ruleOrGroup, { idGenerator });
|
|
5390
6163
|
const independentCombinators = isRuleGroupTypeIC(query);
|
|
5391
6164
|
const newIndex = path.at(-1);
|
|
@@ -5425,15 +6198,85 @@ const group = (query, sourcePathOrID, targetPathOrID, options = {}) => (0, immer
|
|
|
5425
6198
|
* @group Query Tools
|
|
5426
6199
|
*/
|
|
5427
6200
|
const groupInPlace = (query, sourcePathOrID, targetPathOrID, options = {}) => {
|
|
5428
|
-
const { clone = false, combinators = defaultCombinators, idGenerator = generateID } = options;
|
|
6201
|
+
const { clone = false, combinators = defaultCombinators, idGenerator = generateID, onAbort } = options;
|
|
5429
6202
|
const sourcePath = Array.isArray(sourcePathOrID) ? sourcePathOrID : getPathOfID(sourcePathOrID, query);
|
|
5430
6203
|
const targetPath = Array.isArray(targetPathOrID) ? targetPathOrID : getPathOfID(targetPathOrID, query);
|
|
5431
|
-
if (!sourcePath
|
|
6204
|
+
if (!sourcePath) {
|
|
6205
|
+
onAbort?.({
|
|
6206
|
+
reason: "target-not-found",
|
|
6207
|
+
operation: "group",
|
|
6208
|
+
pathOrID: sourcePathOrID
|
|
6209
|
+
});
|
|
6210
|
+
return query;
|
|
6211
|
+
}
|
|
6212
|
+
if (!targetPath) {
|
|
6213
|
+
onAbort?.({
|
|
6214
|
+
reason: "target-not-found",
|
|
6215
|
+
operation: "group",
|
|
6216
|
+
pathOrID: targetPathOrID
|
|
6217
|
+
});
|
|
6218
|
+
return query;
|
|
6219
|
+
}
|
|
5432
6220
|
const nextPath = getNextPath(query, sourcePath, targetPath);
|
|
5433
|
-
if (sourcePath.length === 0
|
|
6221
|
+
if (sourcePath.length === 0) {
|
|
6222
|
+
onAbort?.({
|
|
6223
|
+
reason: "root-not-allowed",
|
|
6224
|
+
operation: "group",
|
|
6225
|
+
pathOrID: sourcePathOrID
|
|
6226
|
+
});
|
|
6227
|
+
return query;
|
|
6228
|
+
}
|
|
6229
|
+
const groupGuardReason = getGuardAbortReason(query, sourcePath, options);
|
|
6230
|
+
if (groupGuardReason) {
|
|
6231
|
+
onAbort?.({
|
|
6232
|
+
reason: groupGuardReason,
|
|
6233
|
+
operation: "group",
|
|
6234
|
+
pathOrID: sourcePathOrID
|
|
6235
|
+
});
|
|
6236
|
+
return query;
|
|
6237
|
+
}
|
|
6238
|
+
if (pathsAreEqual(sourcePath, nextPath)) {
|
|
6239
|
+
onAbort?.({
|
|
6240
|
+
reason: "same-location",
|
|
6241
|
+
operation: "group",
|
|
6242
|
+
pathOrID: sourcePathOrID
|
|
6243
|
+
});
|
|
6244
|
+
return query;
|
|
6245
|
+
}
|
|
6246
|
+
if (!findPath(getParentPath(nextPath), query)) {
|
|
6247
|
+
onAbort?.({
|
|
6248
|
+
reason: "destination-not-found",
|
|
6249
|
+
operation: "group",
|
|
6250
|
+
pathOrID: targetPathOrID
|
|
6251
|
+
});
|
|
6252
|
+
return query;
|
|
6253
|
+
}
|
|
6254
|
+
if (!clone && isAncestor(sourcePath, nextPath)) {
|
|
6255
|
+
onAbort?.({
|
|
6256
|
+
reason: "destination-not-found",
|
|
6257
|
+
operation: "group",
|
|
6258
|
+
pathOrID: targetPathOrID
|
|
6259
|
+
});
|
|
6260
|
+
return query;
|
|
6261
|
+
}
|
|
5434
6262
|
const sourceRuleOrGroupOriginal = findPath(sourcePath, query);
|
|
5435
6263
|
const targetRuleOrGroup = findPath(targetPath, query);
|
|
5436
|
-
if (!sourceRuleOrGroupOriginal
|
|
6264
|
+
if (!sourceRuleOrGroupOriginal) {
|
|
6265
|
+
onAbort?.({
|
|
6266
|
+
reason: "target-not-found",
|
|
6267
|
+
operation: "group",
|
|
6268
|
+
pathOrID: sourcePathOrID
|
|
6269
|
+
});
|
|
6270
|
+
return query;
|
|
6271
|
+
}
|
|
6272
|
+
if (!targetRuleOrGroup) {
|
|
6273
|
+
onAbort?.({
|
|
6274
|
+
reason: "target-not-found",
|
|
6275
|
+
operation: "group",
|
|
6276
|
+
pathOrID: targetPathOrID
|
|
6277
|
+
});
|
|
6278
|
+
return query;
|
|
6279
|
+
}
|
|
5437
6280
|
const sourceRuleOrGroup = clone ? regenerateIDs((0, immer.isDraft)(sourceRuleOrGroupOriginal) ? (0, immer.current)(sourceRuleOrGroupOriginal) : sourceRuleOrGroupOriginal, { idGenerator }) : sourceRuleOrGroupOriginal;
|
|
5438
6281
|
const independentCombinators = isRuleGroupTypeIC(query);
|
|
5439
6282
|
const parentOfRuleToRemove = findPath(getParentPath(sourcePath), query);
|
|
@@ -5446,7 +6289,8 @@ const groupInPlace = (query, sourcePathOrID, targetPathOrID, options = {}) => {
|
|
|
5446
6289
|
const newNewPath = [...nextPath];
|
|
5447
6290
|
const commonAncestorPath = getCommonAncestorPath(sourcePath, nextPath);
|
|
5448
6291
|
if (!clone && sourcePath.length === commonAncestorPath.length + 1 && nextPath[commonAncestorPath.length] > sourcePath[commonAncestorPath.length]) newNewPath[commonAncestorPath.length] -= independentCombinators ? 2 : 1;
|
|
5449
|
-
const
|
|
6292
|
+
const newNewParentPath = getParentPath(newNewPath);
|
|
6293
|
+
const parentOfTargetPath = findPath(newNewParentPath, query);
|
|
5450
6294
|
const targetPathIndex = newNewPath.at(-1);
|
|
5451
6295
|
parentOfTargetPath.rules.splice(targetPathIndex, 1, prepareRuleOrGroup(independentCombinators ? { rules: [
|
|
5452
6296
|
targetRuleOrGroup,
|
|
@@ -5459,7 +6303,1251 @@ const groupInPlace = (query, sourcePathOrID, targetPathOrID, options = {}) => {
|
|
|
5459
6303
|
return query;
|
|
5460
6304
|
};
|
|
5461
6305
|
//#endregion
|
|
6306
|
+
//#region src/utils/queryActions.ts
|
|
6307
|
+
/**
|
|
6308
|
+
* Builds the query builder's six mutation handlers as pure functions of the current query.
|
|
6309
|
+
*
|
|
6310
|
+
* This is the framework-agnostic core of the action handlers in `useQueryBuilderSchema`. It owns
|
|
6311
|
+
* the policy that surrounds the query tools—disabled gating, the confirmation callback protocol,
|
|
6312
|
+
* `maxLevels`, and debug logging—so an implementation only has to supply its own storage: read
|
|
6313
|
+
* the current query, call the action, and apply a non-`undefined` result.
|
|
6314
|
+
*
|
|
6315
|
+
* @group Query Tools
|
|
6316
|
+
*/
|
|
6317
|
+
const createQueryActions = (config = {}) => {
|
|
6318
|
+
const { qbId, combinators, idGenerator, maxLevels = Infinity, queryDisabled, respectDisabled = true, disabledPaths, resetOnFieldChange, resetOnOperatorChange, getRuleDefaultOperator, getValueSources, getRuleDefaultValue, getMatchModes, onAddRule, onAddGroup, onRemove, onMoveRule, onMoveGroup, onGroupRule, onGroupGroup, onLog } = config;
|
|
6319
|
+
const log = (payload) => onLog?.({
|
|
6320
|
+
qbId,
|
|
6321
|
+
...payload
|
|
6322
|
+
});
|
|
6323
|
+
const guards = {
|
|
6324
|
+
respectDisabled,
|
|
6325
|
+
queryDisabled,
|
|
6326
|
+
disabledPaths
|
|
6327
|
+
};
|
|
6328
|
+
/** Whether the mutation is blocked, using the same rules the query tools apply internally. */
|
|
6329
|
+
const blocked = (query, path, asParent = false) => !!getGuardAbortReason(query, path, guards, { asParent });
|
|
6330
|
+
return {
|
|
6331
|
+
addRule: (query, rule, parentPath, context) => {
|
|
6332
|
+
if (blocked(query, parentPath, true)) {
|
|
6333
|
+
log({
|
|
6334
|
+
type: LogType.parentPathDisabled,
|
|
6335
|
+
rule,
|
|
6336
|
+
parentPath,
|
|
6337
|
+
query
|
|
6338
|
+
});
|
|
6339
|
+
return;
|
|
6340
|
+
}
|
|
6341
|
+
const nextRule = onAddRule ? onAddRule(rule, parentPath, query, context) : true;
|
|
6342
|
+
if (!nextRule) {
|
|
6343
|
+
log({
|
|
6344
|
+
type: LogType.onAddRuleFalse,
|
|
6345
|
+
rule,
|
|
6346
|
+
parentPath,
|
|
6347
|
+
query
|
|
6348
|
+
});
|
|
6349
|
+
return;
|
|
6350
|
+
}
|
|
6351
|
+
const newRule = nextRule === true ? rule : nextRule;
|
|
6352
|
+
const newQuery = add(query, newRule, parentPath, {
|
|
6353
|
+
combinators,
|
|
6354
|
+
combinatorPreceding: newRule.combinatorPreceding,
|
|
6355
|
+
idGenerator
|
|
6356
|
+
});
|
|
6357
|
+
log({
|
|
6358
|
+
type: LogType.add,
|
|
6359
|
+
query,
|
|
6360
|
+
newQuery,
|
|
6361
|
+
newRule,
|
|
6362
|
+
parentPath
|
|
6363
|
+
});
|
|
6364
|
+
return newQuery;
|
|
6365
|
+
},
|
|
6366
|
+
addGroup: (query, ruleGroup, parentPath, context) => {
|
|
6367
|
+
if (parentPath.length >= maxLevels) return void 0;
|
|
6368
|
+
if (blocked(query, parentPath, true)) {
|
|
6369
|
+
log({
|
|
6370
|
+
type: LogType.parentPathDisabled,
|
|
6371
|
+
ruleGroup,
|
|
6372
|
+
parentPath,
|
|
6373
|
+
query
|
|
6374
|
+
});
|
|
6375
|
+
return;
|
|
6376
|
+
}
|
|
6377
|
+
const nextGroup = onAddGroup ? onAddGroup(ruleGroup, parentPath, query, context) : true;
|
|
6378
|
+
if (!nextGroup) {
|
|
6379
|
+
log({
|
|
6380
|
+
type: LogType.onAddGroupFalse,
|
|
6381
|
+
ruleGroup,
|
|
6382
|
+
parentPath,
|
|
6383
|
+
query
|
|
6384
|
+
});
|
|
6385
|
+
return;
|
|
6386
|
+
}
|
|
6387
|
+
const newGroup = nextGroup === true ? ruleGroup : nextGroup;
|
|
6388
|
+
const newQuery = add(query, newGroup, parentPath, {
|
|
6389
|
+
combinators,
|
|
6390
|
+
combinatorPreceding: newGroup.combinatorPreceding ?? void 0,
|
|
6391
|
+
idGenerator
|
|
6392
|
+
});
|
|
6393
|
+
log({
|
|
6394
|
+
type: LogType.add,
|
|
6395
|
+
query,
|
|
6396
|
+
newQuery,
|
|
6397
|
+
newGroup,
|
|
6398
|
+
parentPath
|
|
6399
|
+
});
|
|
6400
|
+
return newQuery;
|
|
6401
|
+
},
|
|
6402
|
+
propChange: (query, prop, value, path) => {
|
|
6403
|
+
if (getGuardAbortReason(query, path, prop === "disabled" ? { queryDisabled } : guards)) {
|
|
6404
|
+
log({
|
|
6405
|
+
type: LogType.pathDisabled,
|
|
6406
|
+
path,
|
|
6407
|
+
prop,
|
|
6408
|
+
value,
|
|
6409
|
+
query
|
|
6410
|
+
});
|
|
6411
|
+
return;
|
|
6412
|
+
}
|
|
6413
|
+
const newQuery = update(query, prop, value, path, {
|
|
6414
|
+
resetOnFieldChange,
|
|
6415
|
+
resetOnOperatorChange,
|
|
6416
|
+
getRuleDefaultOperator,
|
|
6417
|
+
getValueSources,
|
|
6418
|
+
getRuleDefaultValue,
|
|
6419
|
+
getMatchModes
|
|
6420
|
+
});
|
|
6421
|
+
log({
|
|
6422
|
+
type: LogType.update,
|
|
6423
|
+
query,
|
|
6424
|
+
newQuery,
|
|
6425
|
+
prop,
|
|
6426
|
+
value,
|
|
6427
|
+
path
|
|
6428
|
+
});
|
|
6429
|
+
return newQuery;
|
|
6430
|
+
},
|
|
6431
|
+
removeRuleOrGroup: (query, path, context) => {
|
|
6432
|
+
if (blocked(query, path)) {
|
|
6433
|
+
log({
|
|
6434
|
+
type: LogType.pathDisabled,
|
|
6435
|
+
path,
|
|
6436
|
+
query
|
|
6437
|
+
});
|
|
6438
|
+
return;
|
|
6439
|
+
}
|
|
6440
|
+
const ruleOrGroup = findPath(path, query);
|
|
6441
|
+
/* v8 ignore next -- @preserve */
|
|
6442
|
+
if (!ruleOrGroup) return void 0;
|
|
6443
|
+
if (onRemove && !onRemove(ruleOrGroup, path, query, context)) {
|
|
6444
|
+
log({
|
|
6445
|
+
type: LogType.onRemoveFalse,
|
|
6446
|
+
ruleOrGroup,
|
|
6447
|
+
path,
|
|
6448
|
+
query
|
|
6449
|
+
});
|
|
6450
|
+
return;
|
|
6451
|
+
}
|
|
6452
|
+
const newQuery = remove(query, path);
|
|
6453
|
+
log({
|
|
6454
|
+
type: LogType.remove,
|
|
6455
|
+
query,
|
|
6456
|
+
newQuery,
|
|
6457
|
+
path,
|
|
6458
|
+
ruleOrGroup
|
|
6459
|
+
});
|
|
6460
|
+
return newQuery;
|
|
6461
|
+
},
|
|
6462
|
+
moveRule: (query, oldPath, newPath, clone, context) => {
|
|
6463
|
+
if (blocked(query, oldPath)) {
|
|
6464
|
+
log({
|
|
6465
|
+
type: LogType.pathDisabled,
|
|
6466
|
+
oldPath,
|
|
6467
|
+
newPath,
|
|
6468
|
+
query
|
|
6469
|
+
});
|
|
6470
|
+
return;
|
|
6471
|
+
}
|
|
6472
|
+
const ruleOrGroup = findPath(oldPath, query);
|
|
6473
|
+
if (!ruleOrGroup) return void 0;
|
|
6474
|
+
const nextQuery = move(query, oldPath, newPath, {
|
|
6475
|
+
clone,
|
|
6476
|
+
combinators,
|
|
6477
|
+
idGenerator
|
|
6478
|
+
});
|
|
6479
|
+
const isGroup = isRuleGroup(ruleOrGroup);
|
|
6480
|
+
const callback = isGroup ? onMoveGroup : onMoveRule;
|
|
6481
|
+
const callbackResult = callback ? callback(ruleOrGroup, oldPath, newPath, query, nextQuery, {
|
|
6482
|
+
clone,
|
|
6483
|
+
combinators
|
|
6484
|
+
}, context) : true;
|
|
6485
|
+
if (!callbackResult) {
|
|
6486
|
+
log({
|
|
6487
|
+
type: isGroup ? LogType.onMoveGroupFalse : LogType.onMoveRuleFalse,
|
|
6488
|
+
ruleOrGroup,
|
|
6489
|
+
oldPath,
|
|
6490
|
+
newPath,
|
|
6491
|
+
clone,
|
|
6492
|
+
query,
|
|
6493
|
+
nextQuery
|
|
6494
|
+
});
|
|
6495
|
+
return;
|
|
6496
|
+
}
|
|
6497
|
+
const newQuery = isRuleGroup(callbackResult) ? callbackResult : nextQuery;
|
|
6498
|
+
log({
|
|
6499
|
+
type: LogType.move,
|
|
6500
|
+
query,
|
|
6501
|
+
newQuery,
|
|
6502
|
+
oldPath,
|
|
6503
|
+
newPath,
|
|
6504
|
+
clone
|
|
6505
|
+
});
|
|
6506
|
+
return newQuery;
|
|
6507
|
+
},
|
|
6508
|
+
groupRule: (query, sourcePath, targetPath, clone, context) => {
|
|
6509
|
+
if (blocked(query, sourcePath)) {
|
|
6510
|
+
log({
|
|
6511
|
+
type: LogType.pathDisabled,
|
|
6512
|
+
sourcePath,
|
|
6513
|
+
targetPath,
|
|
6514
|
+
query
|
|
6515
|
+
});
|
|
6516
|
+
return;
|
|
6517
|
+
}
|
|
6518
|
+
const ruleOrGroup = findPath(sourcePath, query);
|
|
6519
|
+
if (!ruleOrGroup) return void 0;
|
|
6520
|
+
const nextQuery = group(query, sourcePath, targetPath, {
|
|
6521
|
+
clone,
|
|
6522
|
+
combinators,
|
|
6523
|
+
idGenerator
|
|
6524
|
+
});
|
|
6525
|
+
const isGroup = isRuleGroup(ruleOrGroup);
|
|
6526
|
+
const callback = isGroup ? onGroupGroup : onGroupRule;
|
|
6527
|
+
const callbackResult = callback ? callback(ruleOrGroup, sourcePath, targetPath, query, nextQuery, {
|
|
6528
|
+
clone,
|
|
6529
|
+
combinators
|
|
6530
|
+
}, context) : true;
|
|
6531
|
+
if (!callbackResult) {
|
|
6532
|
+
log({
|
|
6533
|
+
type: isGroup ? LogType.onGroupGroupFalse : LogType.onGroupRuleFalse,
|
|
6534
|
+
ruleOrGroup,
|
|
6535
|
+
sourcePath,
|
|
6536
|
+
targetPath,
|
|
6537
|
+
clone,
|
|
6538
|
+
query,
|
|
6539
|
+
nextQuery
|
|
6540
|
+
});
|
|
6541
|
+
return;
|
|
6542
|
+
}
|
|
6543
|
+
const newQuery = isRuleGroup(callbackResult) ? callbackResult : nextQuery;
|
|
6544
|
+
log({
|
|
6545
|
+
type: LogType.group,
|
|
6546
|
+
query,
|
|
6547
|
+
newQuery,
|
|
6548
|
+
sourcePath,
|
|
6549
|
+
targetPath,
|
|
6550
|
+
clone
|
|
6551
|
+
});
|
|
6552
|
+
return newQuery;
|
|
6553
|
+
}
|
|
6554
|
+
};
|
|
6555
|
+
};
|
|
6556
|
+
//#endregion
|
|
6557
|
+
//#region src/utils/ruleFactory.ts
|
|
6558
|
+
/**
|
|
6559
|
+
* Creates a rule from the given configuration, applying the same precedence as the
|
|
6560
|
+
* `QueryBuilder` component. The `value` is computed in a second pass, once `field`, `operator`,
|
|
6561
|
+
* and `valueSource` are known, since the default value depends on all three.
|
|
6562
|
+
*
|
|
6563
|
+
* @group Query Tools
|
|
6564
|
+
*/
|
|
6565
|
+
const createRule = ({ fields, getDefaultField, getRuleDefaultOperator, getValueSources, getMatchModes, getRuleDefaultValue, idGenerator = generateID }) => {
|
|
6566
|
+
let field = getFirstOption(fields) ?? "";
|
|
6567
|
+
if (getDefaultField) if (typeof getDefaultField === "function") {
|
|
6568
|
+
const defaultField = getDefaultField(fields);
|
|
6569
|
+
if (defaultField) field = defaultField;
|
|
6570
|
+
} else field = getDefaultField;
|
|
6571
|
+
const fieldData = getOption(fields, field);
|
|
6572
|
+
const operator = getRuleDefaultOperator(field);
|
|
6573
|
+
const valueSource = getFirstOption(getValueSources(field, operator, { fieldData })) ?? "value";
|
|
6574
|
+
const matchMode = getFirstOption(getMatchModes(field, { fieldData }));
|
|
6575
|
+
const newRule = {
|
|
6576
|
+
id: idGenerator(),
|
|
6577
|
+
field,
|
|
6578
|
+
operator,
|
|
6579
|
+
valueSource,
|
|
6580
|
+
value: "",
|
|
6581
|
+
...matchMode ? { match: {
|
|
6582
|
+
mode: matchMode,
|
|
6583
|
+
threshold: 1
|
|
6584
|
+
} } : null
|
|
6585
|
+
};
|
|
6586
|
+
return {
|
|
6587
|
+
...newRule,
|
|
6588
|
+
value: getRuleDefaultValue(newRule)
|
|
6589
|
+
};
|
|
6590
|
+
};
|
|
6591
|
+
/**
|
|
6592
|
+
* Creates a group from the given configuration. Pass `true` for `independentCombinators` to omit
|
|
6593
|
+
* the `combinator` property.
|
|
6594
|
+
*
|
|
6595
|
+
* The group's own `id` is generated _before_ any contained rule's, matching the order the
|
|
6596
|
+
* `QueryBuilder` component has always used. This is observable when `idGenerator` is
|
|
6597
|
+
* deterministic, as it often is in tests.
|
|
6598
|
+
*
|
|
6599
|
+
* @group Query Tools
|
|
6600
|
+
*/
|
|
6601
|
+
const createRuleGroup = ({ combinators, addRuleToNewGroups, createRule: createRuleMain, idGenerator = generateID }, independentCombinators) => {
|
|
6602
|
+
const id = idGenerator();
|
|
6603
|
+
const rules = addRuleToNewGroups ? [createRuleMain()] : [];
|
|
6604
|
+
if (independentCombinators) return {
|
|
6605
|
+
id,
|
|
6606
|
+
rules,
|
|
6607
|
+
not: false
|
|
6608
|
+
};
|
|
6609
|
+
return {
|
|
6610
|
+
id,
|
|
6611
|
+
rules,
|
|
6612
|
+
combinator: getFirstOption(combinators) ?? "",
|
|
6613
|
+
not: false
|
|
6614
|
+
};
|
|
6615
|
+
};
|
|
6616
|
+
//#endregion
|
|
6617
|
+
//#region src/utils/signature.ts
|
|
6618
|
+
/**
|
|
6619
|
+
* Signature returned when a change alters the _shape_ of the query—rules or groups added,
|
|
6620
|
+
* removed, moved, or reordered—rather than the properties of a single node. Structural changes
|
|
6621
|
+
* never coalesce with each other, so each one produces its own history entry.
|
|
6622
|
+
*/
|
|
6623
|
+
const structuralSignature = "~structural";
|
|
6624
|
+
/**
|
|
6625
|
+
* Signature returned when two queries differ by reference only, with no observable difference
|
|
6626
|
+
* in their properties.
|
|
6627
|
+
*/
|
|
6628
|
+
const unchangedSignature = "";
|
|
6629
|
+
/**
|
|
6630
|
+
* Returns the list of property names that differ between two nodes, ignoring `rules` (which is
|
|
6631
|
+
* compared structurally by the caller).
|
|
6632
|
+
*/
|
|
6633
|
+
const changedProps = (prev, next) => {
|
|
6634
|
+
const keys = /* @__PURE__ */ new Set([...Object.keys(prev), ...Object.keys(next)]);
|
|
6635
|
+
keys.delete("rules");
|
|
6636
|
+
const changed = [];
|
|
6637
|
+
for (const key of keys) if (!Object.is(prev[key], next[key])) changed.push(key);
|
|
6638
|
+
return changed.toSorted();
|
|
6639
|
+
};
|
|
6640
|
+
const signatureOfNode = (prev, next, parentId, index) => {
|
|
6641
|
+
if (prev === next) return "";
|
|
6642
|
+
if (typeof prev === "string" || typeof next === "string") return typeof prev === typeof next ? `${parentId}:combinator[${index}]` : structuralSignature;
|
|
6643
|
+
const prevIsGroup = isRuleGroup(prev);
|
|
6644
|
+
const nextIsGroup = isRuleGroup(next);
|
|
6645
|
+
if (prevIsGroup !== nextIsGroup) return structuralSignature;
|
|
6646
|
+
if (!prevIsGroup || !nextIsGroup) {
|
|
6647
|
+
const props = changedProps(prev, next);
|
|
6648
|
+
return props.length === 0 ? "" : `${next.id ?? ""}:${props.join(",")}`;
|
|
6649
|
+
}
|
|
6650
|
+
if (prev.rules.length !== next.rules.length) return structuralSignature;
|
|
6651
|
+
const ownProps = changedProps(prev, next);
|
|
6652
|
+
let changedIndex = -1;
|
|
6653
|
+
for (const [i, prevRule] of prev.rules.entries()) if (prevRule !== next.rules[i]) {
|
|
6654
|
+
if (changedIndex !== -1) return structuralSignature;
|
|
6655
|
+
changedIndex = i;
|
|
6656
|
+
}
|
|
6657
|
+
if (changedIndex === -1) return ownProps.length === 0 ? "" : `${next.id ?? ""}:${ownProps.join(",")}`;
|
|
6658
|
+
if (ownProps.length > 0) return structuralSignature;
|
|
6659
|
+
return signatureOfNode(prev.rules[changedIndex], next.rules[changedIndex], next.id ?? "", changedIndex);
|
|
6660
|
+
};
|
|
6661
|
+
/**
|
|
6662
|
+
* Describes _what changed_ between two versions of a query as a short string, so that
|
|
6663
|
+
* consecutive edits to the same property of the same rule (e.g. typing in a value editor) can
|
|
6664
|
+
* be recognized and coalesced into a single history entry.
|
|
6665
|
+
*
|
|
6666
|
+
* Returns {@link structuralSignature} for changes to the shape of the query, {@link unchangedSignature} when the
|
|
6667
|
+
* two queries differ by reference only, and `"<id>:<props>"` otherwise.
|
|
6668
|
+
*
|
|
6669
|
+
* The walk prunes on reference equality, so it costs O(depth) rather than O(size) for the
|
|
6670
|
+
* single-node edits that make up the overwhelming majority of changes.
|
|
6671
|
+
*
|
|
6672
|
+
* Note that this relies on the structural sharing that Immer—and therefore every query
|
|
6673
|
+
* produced by RQB's own update functions—guarantees: nodes that did not change keep their
|
|
6674
|
+
* identity. A query that has been wholly rebuilt (deep-cloned, round-tripped through JSON,
|
|
6675
|
+
* re-parsed from a string) shares no identity with its predecessor, so it is reported as
|
|
6676
|
+
* {@link structuralSignature} and will not coalesce. That is a graceful degradation—every change simply
|
|
6677
|
+
* gets its own history entry—but controlled components that clone the query on every change
|
|
6678
|
+
* will not benefit from coalescing.
|
|
6679
|
+
*/
|
|
6680
|
+
const signatureOf = (prev, next) => signatureOfNode(prev, next, "", -1);
|
|
6681
|
+
//#endregion
|
|
6682
|
+
//#region src/utils/QueryManager.ts
|
|
6683
|
+
/**
|
|
6684
|
+
* Abort reasons that {@link QueryManager}'s `strict` mode treats as errors. The remaining
|
|
6685
|
+
* reasons—`"same-location"` and `"no-change"`—describe valid operations that had nothing to do,
|
|
6686
|
+
* so they are reported to `onInvalidTarget` but never throw.
|
|
6687
|
+
*
|
|
6688
|
+
* @group Query Tools
|
|
6689
|
+
*/
|
|
6690
|
+
const strictAbortReasons = [
|
|
6691
|
+
"target-not-found",
|
|
6692
|
+
"parent-not-found",
|
|
6693
|
+
"parent-not-a-group",
|
|
6694
|
+
"destination-not-found",
|
|
6695
|
+
"root-not-allowed",
|
|
6696
|
+
"not-a-combinator-slot",
|
|
6697
|
+
"target-disabled",
|
|
6698
|
+
"parent-disabled",
|
|
6699
|
+
"max-levels-exceeded"
|
|
6700
|
+
];
|
|
6701
|
+
const strictAbortReasonSet = new Set(strictAbortReasons);
|
|
6702
|
+
/**
|
|
6703
|
+
* Thrown by {@link QueryManager} methods in `strict` mode when an operation cannot be carried
|
|
6704
|
+
* out because its target could not be used.
|
|
6705
|
+
*
|
|
6706
|
+
* @group Query Tools
|
|
6707
|
+
*/
|
|
6708
|
+
var QueryManagerError = class QueryManagerError extends Error {
|
|
6709
|
+
/** Why the operation was aborted. */
|
|
6710
|
+
code;
|
|
6711
|
+
/** Full details about the aborted operation. */
|
|
6712
|
+
info;
|
|
6713
|
+
constructor(info) {
|
|
6714
|
+
super(`QueryManager: "${info.operation}" aborted (${info.reason})${info.pathOrID === void 0 ? "" : ` for target ${JSON.stringify(info.pathOrID)}`}.`);
|
|
6715
|
+
this.name = "QueryManagerError";
|
|
6716
|
+
this.code = info.reason;
|
|
6717
|
+
this.info = info;
|
|
6718
|
+
Object.setPrototypeOf(this, QueryManagerError.prototype);
|
|
6719
|
+
}
|
|
6720
|
+
};
|
|
6721
|
+
/**
|
|
6722
|
+
* Stateful wrapper around the {@link add}/{@link remove}/{@link update}/{@link move}/
|
|
6723
|
+
* {@link insert}/{@link group} query tools, plus rule/group factories, {@link defaultValidator
|
|
6724
|
+
* validation}, and {@link formatQuery formatting}.
|
|
6725
|
+
*
|
|
6726
|
+
* The query is held internally, so each method takes the same arguments as its `queryTools`
|
|
6727
|
+
* counterpart minus the leading `query` parameter, and returns the manager itself for chaining.
|
|
6728
|
+
* Mutations use the non-`InPlace` tools, so a query previously handed out by
|
|
6729
|
+
* {@link QueryManager.getQuery} is never modified.
|
|
6730
|
+
*
|
|
6731
|
+
* ```ts
|
|
6732
|
+
* const q = new QueryManager(undefined, { fields });
|
|
6733
|
+
* q.add(q.createRule()).add({ field: 'firstName', operator: '=', value: 'Steve' });
|
|
6734
|
+
* q.format('sql');
|
|
6735
|
+
* ```
|
|
6736
|
+
*
|
|
6737
|
+
* Like the underlying query tools, methods are a no-op when the target path or `id` can't be
|
|
6738
|
+
* resolved (including attempts to remove the root group). By default nothing is thrown; pass
|
|
6739
|
+
* `strict: true` to raise a {@link QueryManagerError} instead, or `onInvalidTarget` to observe
|
|
6740
|
+
* aborted operations without changing control flow.
|
|
6741
|
+
*
|
|
6742
|
+
* @group Query Tools
|
|
6743
|
+
*/
|
|
6744
|
+
var QueryManager = class QueryManager {
|
|
6745
|
+
#query;
|
|
6746
|
+
#options;
|
|
6747
|
+
#fields;
|
|
6748
|
+
#fieldMap;
|
|
6749
|
+
#operators;
|
|
6750
|
+
#combinators;
|
|
6751
|
+
#idGenerator;
|
|
6752
|
+
#validator;
|
|
6753
|
+
#strict;
|
|
6754
|
+
#respectDisabled;
|
|
6755
|
+
#onInvalidTarget;
|
|
6756
|
+
#listeners = /* @__PURE__ */ new Set();
|
|
6757
|
+
#historyEnabled;
|
|
6758
|
+
#maxHistory;
|
|
6759
|
+
#coalesceMs;
|
|
6760
|
+
#now;
|
|
6761
|
+
#past = [];
|
|
6762
|
+
#future = [];
|
|
6763
|
+
#lastSig;
|
|
6764
|
+
#lastAt = 0;
|
|
6765
|
+
#batchDepth = 0;
|
|
6766
|
+
#batchSnapshot;
|
|
6767
|
+
/** Whether a history-stack method ran inside the batch currently in progress. */
|
|
6768
|
+
#historyBypassed = false;
|
|
6769
|
+
/**
|
|
6770
|
+
* The query the cached fields below were derived from. Caches are keyed on query _identity_
|
|
6771
|
+
* rather than invalidated from {@link QueryManager.#commit} because `undo`, `redo`, and
|
|
6772
|
+
* `batch`'s rollback all assign `#query` directly.
|
|
6773
|
+
*/
|
|
6774
|
+
#cacheFor;
|
|
6775
|
+
#idPathIndex;
|
|
6776
|
+
#validation;
|
|
6777
|
+
constructor(query, options = {}) {
|
|
6778
|
+
this.#options = options;
|
|
6779
|
+
this.#idGenerator = options.idGenerator ?? generateID;
|
|
6780
|
+
this.#validator = options.validator ?? defaultValidator;
|
|
6781
|
+
this.#strict = options.strict ?? false;
|
|
6782
|
+
this.#respectDisabled = options.respectDisabled ?? true;
|
|
6783
|
+
this.#onInvalidTarget = options.onInvalidTarget;
|
|
6784
|
+
const history = options.history ?? false;
|
|
6785
|
+
const historyOptions = typeof history === "object" ? history : {};
|
|
6786
|
+
this.#historyEnabled = history !== false;
|
|
6787
|
+
this.#maxHistory = historyOptions.maxHistory ?? 50;
|
|
6788
|
+
this.#coalesceMs = historyOptions.coalesceMs ?? 500;
|
|
6789
|
+
this.#now = options.now ?? Date.now;
|
|
6790
|
+
const { optionList: fields, optionsMap: fieldMap } = prepareOptionList({
|
|
6791
|
+
optionList: options.fields,
|
|
6792
|
+
baseOption: options.baseField,
|
|
6793
|
+
autoSelectOption: options.autoSelectField
|
|
6794
|
+
});
|
|
6795
|
+
this.#fields = (0, immer.freeze)(fields, true);
|
|
6796
|
+
this.#fieldMap = (0, immer.freeze)(fieldMap, true);
|
|
6797
|
+
this.#operators = prepareOptionList({
|
|
6798
|
+
optionList: options.operators ?? defaultOperators,
|
|
6799
|
+
baseOption: options.baseOperator,
|
|
6800
|
+
labelMap: defaultOperatorLabelMap,
|
|
6801
|
+
autoSelectOption: options.autoSelectOperator
|
|
6802
|
+
}).optionList;
|
|
6803
|
+
this.#combinators = (0, immer.freeze)(prepareOptionList({
|
|
6804
|
+
optionList: options.combinators ?? defaultCombinators,
|
|
6805
|
+
baseOption: options.baseCombinator
|
|
6806
|
+
}).optionList, true);
|
|
6807
|
+
this.#query = (0, immer.freeze)(query ? prepareRuleGroup(query, { idGenerator: this.#idGenerator }) : this.createRuleGroup(), true);
|
|
6808
|
+
}
|
|
6809
|
+
/** Resolves the field configuration for a field name. */
|
|
6810
|
+
#fieldData(field) {
|
|
6811
|
+
return this.#fieldMap[field] ?? {};
|
|
6812
|
+
}
|
|
6813
|
+
/** Resolves the operator list for a field, mirroring `QueryBuilder`'s precedence. */
|
|
6814
|
+
#operatorsFor(field) {
|
|
6815
|
+
return resolveOperatorList({
|
|
6816
|
+
field,
|
|
6817
|
+
fieldData: this.#fieldData(field),
|
|
6818
|
+
getOperators: this.#options.getOperators,
|
|
6819
|
+
operators: this.#operators,
|
|
6820
|
+
baseOption: this.#options.baseOperator,
|
|
6821
|
+
autoSelectOption: this.#options.autoSelectOperator
|
|
6822
|
+
});
|
|
6823
|
+
}
|
|
6824
|
+
/** Resolves the default operator for a field, mirroring `QueryBuilder`'s precedence. */
|
|
6825
|
+
#defaultOperator(field) {
|
|
6826
|
+
return resolveDefaultOperator({
|
|
6827
|
+
field,
|
|
6828
|
+
fieldData: this.#fieldData(field),
|
|
6829
|
+
getDefaultOperator: this.#options.getDefaultOperator,
|
|
6830
|
+
getOperators: (f) => this.#operatorsFor(f)
|
|
6831
|
+
});
|
|
6832
|
+
}
|
|
6833
|
+
#valueSourcesFor(field, operator) {
|
|
6834
|
+
return getValueSourcesUtil(this.#fieldData(field), operator, this.#options.getValueSources);
|
|
6835
|
+
}
|
|
6836
|
+
#matchModesFor(field) {
|
|
6837
|
+
return getMatchModesUtil(this.#fieldData(field), this.#options.getMatchModes);
|
|
6838
|
+
}
|
|
6839
|
+
#valuesFor(field, operator) {
|
|
6840
|
+
return resolveValueList({
|
|
6841
|
+
field,
|
|
6842
|
+
operator,
|
|
6843
|
+
fieldData: this.#fieldData(field),
|
|
6844
|
+
getValues: this.#options.getValues,
|
|
6845
|
+
autoSelectOption: this.#options.autoSelectValue
|
|
6846
|
+
});
|
|
6847
|
+
}
|
|
6848
|
+
#valueEditorTypeFor(field, operator) {
|
|
6849
|
+
return resolveValueEditorType({
|
|
6850
|
+
field,
|
|
6851
|
+
operator,
|
|
6852
|
+
fieldData: this.#fieldData(field),
|
|
6853
|
+
getValueEditorType: this.#options.getValueEditorType
|
|
6854
|
+
});
|
|
6855
|
+
}
|
|
6856
|
+
/** Computes the default `value` for a rule, mirroring `QueryBuilder`'s precedence. */
|
|
6857
|
+
#defaultValue(rule) {
|
|
6858
|
+
const { getDefaultValue, getParameters, listsAsArrays } = this.#options;
|
|
6859
|
+
return getRuleDefaultValue(rule, {
|
|
6860
|
+
fieldData: this.#fieldData(rule.field),
|
|
6861
|
+
fields: this.#fields,
|
|
6862
|
+
listsAsArrays,
|
|
6863
|
+
getValueEditorType: (f, o) => this.#valueEditorTypeFor(f, o),
|
|
6864
|
+
getValues: (f, o) => this.#valuesFor(f, o),
|
|
6865
|
+
getDefaultValue: getDefaultValue && ((r, misc) => getDefaultValue(r, misc)),
|
|
6866
|
+
getParameters: getParameters && ((f, o, misc) => this.#parametersFor(f, o, misc))
|
|
6867
|
+
});
|
|
6868
|
+
}
|
|
6869
|
+
/**
|
|
6870
|
+
* Resolves the parameter list for a field/operator pair, normalized the same way as every
|
|
6871
|
+
* other option list. Shared by {@link QueryManager.#defaultValue} and
|
|
6872
|
+
* {@link QueryManager.getRuleContext} so both see the same shape.
|
|
6873
|
+
*/
|
|
6874
|
+
#parametersFor(field, operator, misc) {
|
|
6875
|
+
return prepareOptionList({
|
|
6876
|
+
optionList: this.#options.getParameters?.(field, operator, misc) ?? [],
|
|
6877
|
+
autoSelectOption: this.#options.autoSelectValue
|
|
6878
|
+
}).optionList;
|
|
6879
|
+
}
|
|
6880
|
+
/** Defaults shared by every mutating method, overridable per call. */
|
|
6881
|
+
#guardOptions() {
|
|
6882
|
+
const { maxLevels } = this.#options;
|
|
6883
|
+
return {
|
|
6884
|
+
maxLevels: (maxLevels ?? 0) > 0 ? Number(maxLevels) : Infinity,
|
|
6885
|
+
respectDisabled: this.#respectDisabled,
|
|
6886
|
+
disabledPaths: this.#options.disabledPaths,
|
|
6887
|
+
queryDisabled: this.#options.queryDisabled
|
|
6888
|
+
};
|
|
6889
|
+
}
|
|
6890
|
+
#toolOptions() {
|
|
6891
|
+
return {
|
|
6892
|
+
combinators: this.#combinators,
|
|
6893
|
+
idGenerator: this.#idGenerator,
|
|
6894
|
+
...this.#guardOptions()
|
|
6895
|
+
};
|
|
6896
|
+
}
|
|
6897
|
+
/**
|
|
6898
|
+
* Builds the `onAbort` handler passed to the query tools, applying the per-call overrides on
|
|
6899
|
+
* top of the manager's own options.
|
|
6900
|
+
*/
|
|
6901
|
+
#onAbort({ strict, onInvalidTarget }) {
|
|
6902
|
+
const strictMain = strict ?? this.#strict;
|
|
6903
|
+
const handler = onInvalidTarget ?? this.#onInvalidTarget;
|
|
6904
|
+
return (info) => {
|
|
6905
|
+
handler?.(info);
|
|
6906
|
+
if (strictMain && strictAbortReasonSet.has(info.reason)) throw new QueryManagerError(info);
|
|
6907
|
+
};
|
|
6908
|
+
}
|
|
6909
|
+
/** Defaults for {@link update}, so resets mirror `QueryBuilder`'s behavior. */
|
|
6910
|
+
#updateOptions() {
|
|
6911
|
+
return {
|
|
6912
|
+
getRuleDefaultOperator: (f) => this.#defaultOperator(f),
|
|
6913
|
+
getRuleDefaultValue: (r) => this.#defaultValue(r),
|
|
6914
|
+
getValueSources: (f, o) => this.#valueSourcesFor(f, o),
|
|
6915
|
+
getMatchModes: (f) => this.#matchModesFor(f),
|
|
6916
|
+
resetOnFieldChange: this.#options.resetOnFieldChange,
|
|
6917
|
+
resetOnOperatorChange: this.#options.resetOnOperatorChange,
|
|
6918
|
+
...this.#guardOptions()
|
|
6919
|
+
};
|
|
6920
|
+
}
|
|
6921
|
+
/**
|
|
6922
|
+
* Applies a new query, recording history and notifying subscribers as appropriate. Every
|
|
6923
|
+
* mutation funnels through here. A tool that could not resolve its target returns the same
|
|
6924
|
+
* query object, which is treated as a no-op.
|
|
6925
|
+
*/
|
|
6926
|
+
#commit(next) {
|
|
6927
|
+
const prev = this.#query;
|
|
6928
|
+
if (prev === next) return;
|
|
6929
|
+
this.#query = next;
|
|
6930
|
+
if (this.#batchDepth > 0) return;
|
|
6931
|
+
this.#record(prev, next);
|
|
6932
|
+
this.#notify();
|
|
6933
|
+
}
|
|
6934
|
+
/**
|
|
6935
|
+
* Records a change, either as a new history entry or by absorbing it into the current one.
|
|
6936
|
+
* Mirrors the recording semantics of the `react-querybuilder/history` entry point.
|
|
6937
|
+
*/
|
|
6938
|
+
#record(prev, next) {
|
|
6939
|
+
if (!this.#historyEnabled) return;
|
|
6940
|
+
const sig = signatureOf(prev, next);
|
|
6941
|
+
if (sig === "") return;
|
|
6942
|
+
const now = this.#now();
|
|
6943
|
+
if (!(sig !== "~structural" && sig === this.#lastSig && now - this.#lastAt < this.#coalesceMs)) {
|
|
6944
|
+
this.#past.push(prev);
|
|
6945
|
+
if (this.#past.length > this.#maxHistory) this.#past.shift();
|
|
6946
|
+
this.#future = [];
|
|
6947
|
+
}
|
|
6948
|
+
this.#lastSig = sig;
|
|
6949
|
+
this.#lastAt = now;
|
|
6950
|
+
}
|
|
6951
|
+
#notify() {
|
|
6952
|
+
for (const listener of this.#listeners) listener();
|
|
6953
|
+
}
|
|
6954
|
+
/**
|
|
6955
|
+
* Records that a history-stack method ran inside the current batch. Those methods manage
|
|
6956
|
+
* `#past`/`#future` themselves, so the batch must not also record an entry on completion —
|
|
6957
|
+
* doing so would push a duplicate onto `#past` and clear the redo stack that `undo` just
|
|
6958
|
+
* populated.
|
|
6959
|
+
*/
|
|
6960
|
+
#markHistoryBypassed() {
|
|
6961
|
+
if (this.#batchDepth > 0) this.#historyBypassed = true;
|
|
6962
|
+
}
|
|
6963
|
+
/**
|
|
6964
|
+
* Discards every cached derivation when the query has been replaced since they were computed.
|
|
6965
|
+
* Called at the top of each cached reader.
|
|
6966
|
+
*/
|
|
6967
|
+
#ensureCache() {
|
|
6968
|
+
if (this.#cacheFor === this.#query) return;
|
|
6969
|
+
this.#cacheFor = this.#query;
|
|
6970
|
+
this.#idPathIndex = void 0;
|
|
6971
|
+
this.#validation = void 0;
|
|
6972
|
+
}
|
|
6973
|
+
/**
|
|
6974
|
+
* The single traversal implementation. Combinator strings in independent-combinator groups
|
|
6975
|
+
* are skipped.
|
|
6976
|
+
*
|
|
6977
|
+
* @yields The subtree rooted at `node`, depth-first in pre-order, starting with `node` itself.
|
|
6978
|
+
*/
|
|
6979
|
+
*#walkFrom(node, path, parent) {
|
|
6980
|
+
yield {
|
|
6981
|
+
node,
|
|
6982
|
+
path,
|
|
6983
|
+
parent
|
|
6984
|
+
};
|
|
6985
|
+
if (!isRuleGroup(node)) return;
|
|
6986
|
+
const startGroup = node;
|
|
6987
|
+
for (const [index, child] of startGroup.rules.entries()) {
|
|
6988
|
+
if (typeof child === "string") continue;
|
|
6989
|
+
yield* this.#walkFrom(child, [...path, index], startGroup);
|
|
6990
|
+
}
|
|
6991
|
+
}
|
|
6992
|
+
/** Builds (once per query) the `id` to {@link Path} index backing `findID`/`getPathOfID`. */
|
|
6993
|
+
#index() {
|
|
6994
|
+
this.#ensureCache();
|
|
6995
|
+
if (!this.#idPathIndex) {
|
|
6996
|
+
const index = /* @__PURE__ */ new Map();
|
|
6997
|
+
for (const { node, path } of this.#walkFrom(this.#query, [], null)) if (node.id !== void 0 && !index.has(node.id)) index.set(node.id, path);
|
|
6998
|
+
this.#idPathIndex = index;
|
|
6999
|
+
}
|
|
7000
|
+
return this.#idPathIndex;
|
|
7001
|
+
}
|
|
7002
|
+
/** Resolves a path or `id` to a path, or `null` when the `id` isn't present. */
|
|
7003
|
+
#toPath(pathOrID) {
|
|
7004
|
+
return typeof pathOrID === "string" ? this.#index().get(pathOrID) ?? null : pathOrID;
|
|
7005
|
+
}
|
|
7006
|
+
/**
|
|
7007
|
+
* The current query. The returned object is frozen and structurally shared, so it is safe to
|
|
7008
|
+
* retain and compare by reference to detect changes.
|
|
7009
|
+
*
|
|
7010
|
+
* Like {@link QueryManager.subscribe}, this method is bound to the instance, so it can be
|
|
7011
|
+
* passed as a bare reference (e.g. as the `getSnapshot` argument to `useSyncExternalStore`).
|
|
7012
|
+
*/
|
|
7013
|
+
getQuery = () => this.#query;
|
|
7014
|
+
/** Replaces the current query, ensuring every rule and group has an `id`. */
|
|
7015
|
+
setQuery(query) {
|
|
7016
|
+
this.#commit((0, immer.freeze)(prepareRuleGroup(query, { idGenerator: this.#idGenerator }), true));
|
|
7017
|
+
return this;
|
|
7018
|
+
}
|
|
7019
|
+
/**
|
|
7020
|
+
* Creates a rule using the configured fields, operators, and defaults. The rule is not added
|
|
7021
|
+
* to the query—pass it to {@link QueryManager.add} or {@link QueryManager.insert}.
|
|
7022
|
+
*/
|
|
7023
|
+
createRule() {
|
|
7024
|
+
return createRule({
|
|
7025
|
+
fields: this.#fields,
|
|
7026
|
+
getDefaultField: this.#options.getDefaultField,
|
|
7027
|
+
getRuleDefaultOperator: (f) => this.#defaultOperator(f),
|
|
7028
|
+
getValueSources: (f, o) => this.#valueSourcesFor(f, o),
|
|
7029
|
+
getMatchModes: (f) => this.#matchModesFor(f),
|
|
7030
|
+
getRuleDefaultValue: (r) => this.#defaultValue(r),
|
|
7031
|
+
idGenerator: this.#idGenerator
|
|
7032
|
+
});
|
|
7033
|
+
}
|
|
7034
|
+
/**
|
|
7035
|
+
* Creates a group. Pass `true` for a group with independent combinators. The group is not
|
|
7036
|
+
* added to the query—pass it to {@link QueryManager.add} or {@link QueryManager.insert}.
|
|
7037
|
+
*/
|
|
7038
|
+
createRuleGroup(independentCombinators) {
|
|
7039
|
+
return createRuleGroup({
|
|
7040
|
+
combinators: this.#combinators,
|
|
7041
|
+
addRuleToNewGroups: this.#options.addRuleToNewGroups,
|
|
7042
|
+
createRule: () => this.createRule(),
|
|
7043
|
+
idGenerator: this.#idGenerator
|
|
7044
|
+
}, independentCombinators);
|
|
7045
|
+
}
|
|
7046
|
+
/**
|
|
7047
|
+
* Adds a rule or group to the end of the group at `parentPathOrID`, which defaults to the
|
|
7048
|
+
* root group.
|
|
7049
|
+
*/
|
|
7050
|
+
add(ruleOrGroup, parentPathOrID = [], options = {}) {
|
|
7051
|
+
const { strict, onInvalidTarget, ...toolOptions } = options;
|
|
7052
|
+
this.#commit(add(this.#query, ruleOrGroup, parentPathOrID, {
|
|
7053
|
+
...this.#toolOptions(),
|
|
7054
|
+
...toolOptions,
|
|
7055
|
+
onAbort: this.#onAbort({
|
|
7056
|
+
strict,
|
|
7057
|
+
onInvalidTarget
|
|
7058
|
+
})
|
|
7059
|
+
}));
|
|
7060
|
+
return this;
|
|
7061
|
+
}
|
|
7062
|
+
/** Removes the rule or group at the given path or `id`. The root group cannot be removed. */
|
|
7063
|
+
remove(pathOrID, options = {}) {
|
|
7064
|
+
const { strict, onInvalidTarget, ...toolOptions } = options;
|
|
7065
|
+
this.#commit(remove(this.#query, pathOrID, {
|
|
7066
|
+
...this.#guardOptions(),
|
|
7067
|
+
...toolOptions,
|
|
7068
|
+
onAbort: this.#onAbort({
|
|
7069
|
+
strict,
|
|
7070
|
+
onInvalidTarget
|
|
7071
|
+
})
|
|
7072
|
+
}));
|
|
7073
|
+
return this;
|
|
7074
|
+
}
|
|
7075
|
+
update(a, b, c, d) {
|
|
7076
|
+
const optionsIndex = typeof a === "string" || Array.isArray(a) ? 3 : 2;
|
|
7077
|
+
const args = [
|
|
7078
|
+
a,
|
|
7079
|
+
b,
|
|
7080
|
+
c,
|
|
7081
|
+
d
|
|
7082
|
+
];
|
|
7083
|
+
const { strict, onInvalidTarget, ...toolOptions } = args[optionsIndex] ?? {};
|
|
7084
|
+
args[optionsIndex] = {
|
|
7085
|
+
...this.#updateOptions(),
|
|
7086
|
+
...toolOptions,
|
|
7087
|
+
onAbort: this.#onAbort({
|
|
7088
|
+
strict,
|
|
7089
|
+
onInvalidTarget
|
|
7090
|
+
})
|
|
7091
|
+
};
|
|
7092
|
+
this.#commit(update(this.#query, ...args.slice(0, optionsIndex + 1)));
|
|
7093
|
+
return this;
|
|
7094
|
+
}
|
|
7095
|
+
/** Moves the rule or group at `oldPathOrID` to `newPath`, or shifts it `'up'`/`'down'`. */
|
|
7096
|
+
move(oldPathOrID, newPath, options = {}) {
|
|
7097
|
+
const { strict, onInvalidTarget, ...toolOptions } = options;
|
|
7098
|
+
this.#commit(move(this.#query, oldPathOrID, newPath, {
|
|
7099
|
+
...this.#toolOptions(),
|
|
7100
|
+
...toolOptions,
|
|
7101
|
+
onAbort: this.#onAbort({
|
|
7102
|
+
strict,
|
|
7103
|
+
onInvalidTarget
|
|
7104
|
+
})
|
|
7105
|
+
}));
|
|
7106
|
+
return this;
|
|
7107
|
+
}
|
|
7108
|
+
/**
|
|
7109
|
+
* Inserts a rule or group at the given path. Unlike the other methods, this accepts a path
|
|
7110
|
+
* only—inserting _at_ an `id` would be ambiguous.
|
|
7111
|
+
*/
|
|
7112
|
+
insert(ruleOrGroup, path, options = {}) {
|
|
7113
|
+
const { strict, onInvalidTarget, ...toolOptions } = options;
|
|
7114
|
+
this.#commit(insert(this.#query, ruleOrGroup, path, {
|
|
7115
|
+
...this.#toolOptions(),
|
|
7116
|
+
...toolOptions,
|
|
7117
|
+
onAbort: this.#onAbort({
|
|
7118
|
+
strict,
|
|
7119
|
+
onInvalidTarget
|
|
7120
|
+
})
|
|
7121
|
+
}));
|
|
7122
|
+
return this;
|
|
7123
|
+
}
|
|
7124
|
+
/**
|
|
7125
|
+
* Creates a new group at `targetPathOrID` containing the rules/groups currently at
|
|
7126
|
+
* `targetPathOrID` and `sourcePathOrID`.
|
|
7127
|
+
*/
|
|
7128
|
+
group(sourcePathOrID, targetPathOrID, options = {}) {
|
|
7129
|
+
const { strict, onInvalidTarget, ...toolOptions } = options;
|
|
7130
|
+
this.#commit(group(this.#query, sourcePathOrID, targetPathOrID, {
|
|
7131
|
+
...this.#toolOptions(),
|
|
7132
|
+
...toolOptions,
|
|
7133
|
+
onAbort: this.#onAbort({
|
|
7134
|
+
strict,
|
|
7135
|
+
onInvalidTarget
|
|
7136
|
+
})
|
|
7137
|
+
}));
|
|
7138
|
+
return this;
|
|
7139
|
+
}
|
|
7140
|
+
/**
|
|
7141
|
+
* Creates an independent manager with the same configuration and the current query.
|
|
7142
|
+
*
|
|
7143
|
+
* Subscribers and history are _not_ carried over: the clone starts with no listeners and an
|
|
7144
|
+
* empty undo stack. Because every mutation produces a new query object, the two managers
|
|
7145
|
+
* share the initial query safely and diverge from the first change.
|
|
7146
|
+
*
|
|
7147
|
+
* Pass `{ regenerateIDs: true }` to give every rule and group in the clone a new `id`, which
|
|
7148
|
+
* is useful when both queries will be used together (e.g. inserted into the same tree).
|
|
7149
|
+
*/
|
|
7150
|
+
clone(options) {
|
|
7151
|
+
const query = options?.regenerateIDs ? regenerateIDs(this.#query, { idGenerator: this.#idGenerator }) : this.#query;
|
|
7152
|
+
return new QueryManager(query, this.#options);
|
|
7153
|
+
}
|
|
7154
|
+
/**
|
|
7155
|
+
* Registers a listener called after every change to the query, and returns a function that
|
|
7156
|
+
* unregisters it. Mutations that resolve to a no-op do not notify, and a
|
|
7157
|
+
* {@link QueryManager.batch batch} notifies once no matter how many changes it contains.
|
|
7158
|
+
*
|
|
7159
|
+
* Together with {@link QueryManager.getQuery}, this satisfies React's `useSyncExternalStore`
|
|
7160
|
+
* contract. Both methods are bound to the instance, so they are stable references across
|
|
7161
|
+
* renders and can be passed directly:
|
|
7162
|
+
*
|
|
7163
|
+
* ```ts
|
|
7164
|
+
* const query = useSyncExternalStore(q.subscribe, q.getQuery);
|
|
7165
|
+
* ```
|
|
7166
|
+
*
|
|
7167
|
+
* In React, prefer the `useQueryManager` hook from `react-querybuilder`, which wraps this.
|
|
7168
|
+
*/
|
|
7169
|
+
subscribe = (listener) => {
|
|
7170
|
+
this.#listeners.add(listener);
|
|
7171
|
+
return () => {
|
|
7172
|
+
this.#listeners.delete(listener);
|
|
7173
|
+
};
|
|
7174
|
+
};
|
|
7175
|
+
/**
|
|
7176
|
+
* Runs `fn`, deferring history recording and subscriber notification until it returns. The
|
|
7177
|
+
* whole batch becomes a single undo step and triggers a single notification, or neither if
|
|
7178
|
+
* the query ends up unchanged.
|
|
7179
|
+
*
|
|
7180
|
+
* Batches may be nested; only the outermost one commits. If `fn` throws, the query and its
|
|
7181
|
+
* history are restored to their pre-batch state and the error propagates, so a batch either
|
|
7182
|
+
* applies completely or not at all.
|
|
7183
|
+
*
|
|
7184
|
+
* {@link QueryManager.undo}, {@link QueryManager.redo}, and {@link QueryManager.clearHistory}
|
|
7185
|
+
* may be called inside a batch; their notifications are deferred like everything else. Because
|
|
7186
|
+
* they manage the history stacks themselves, a batch containing one of them records no entry of
|
|
7187
|
+
* its own, leaving the stacks exactly as those methods left them.
|
|
7188
|
+
*/
|
|
7189
|
+
batch(fn) {
|
|
7190
|
+
this.#batchDepth++;
|
|
7191
|
+
if (this.#batchDepth === 1) this.#batchSnapshot = {
|
|
7192
|
+
query: this.#query,
|
|
7193
|
+
past: [...this.#past],
|
|
7194
|
+
future: [...this.#future],
|
|
7195
|
+
lastSig: this.#lastSig,
|
|
7196
|
+
lastAt: this.#lastAt
|
|
7197
|
+
};
|
|
7198
|
+
try {
|
|
7199
|
+
fn();
|
|
7200
|
+
} catch (error) {
|
|
7201
|
+
if (this.#batchDepth === 1) {
|
|
7202
|
+
const snapshot = this.#batchSnapshot;
|
|
7203
|
+
this.#query = snapshot.query;
|
|
7204
|
+
this.#past = snapshot.past;
|
|
7205
|
+
this.#future = snapshot.future;
|
|
7206
|
+
this.#lastSig = snapshot.lastSig;
|
|
7207
|
+
this.#lastAt = snapshot.lastAt;
|
|
7208
|
+
}
|
|
7209
|
+
throw error;
|
|
7210
|
+
} finally {
|
|
7211
|
+
this.#batchDepth--;
|
|
7212
|
+
if (this.#batchDepth === 0) {
|
|
7213
|
+
const { query: base } = this.#batchSnapshot;
|
|
7214
|
+
this.#batchSnapshot = void 0;
|
|
7215
|
+
if (base !== this.#query) {
|
|
7216
|
+
if (!this.#historyBypassed) this.#record(base, this.#query);
|
|
7217
|
+
this.#notify();
|
|
7218
|
+
}
|
|
7219
|
+
this.#historyBypassed = false;
|
|
7220
|
+
}
|
|
7221
|
+
}
|
|
7222
|
+
return this;
|
|
7223
|
+
}
|
|
7224
|
+
/** Whether there is a previous query to restore. Always `false` unless `history` is enabled. */
|
|
7225
|
+
canUndo() {
|
|
7226
|
+
return this.#past.length > 0;
|
|
7227
|
+
}
|
|
7228
|
+
/** Whether there is an undone query to restore. Always `false` unless `history` is enabled. */
|
|
7229
|
+
canRedo() {
|
|
7230
|
+
return this.#future.length > 0;
|
|
7231
|
+
}
|
|
7232
|
+
/** Restores the previous query. No-op when {@link QueryManager.canUndo} is `false`. */
|
|
7233
|
+
undo() {
|
|
7234
|
+
if (this.#past.length === 0) return this;
|
|
7235
|
+
this.#future.unshift(this.#query);
|
|
7236
|
+
this.#query = this.#past.pop();
|
|
7237
|
+
this.#lastSig = void 0;
|
|
7238
|
+
this.#markHistoryBypassed();
|
|
7239
|
+
if (this.#batchDepth === 0) this.#notify();
|
|
7240
|
+
return this;
|
|
7241
|
+
}
|
|
7242
|
+
/** Restores the most recently undone query. No-op when {@link QueryManager.canRedo} is `false`. */
|
|
7243
|
+
redo() {
|
|
7244
|
+
if (this.#future.length === 0) return this;
|
|
7245
|
+
this.#past.push(this.#query);
|
|
7246
|
+
this.#query = this.#future.shift();
|
|
7247
|
+
this.#lastSig = void 0;
|
|
7248
|
+
this.#markHistoryBypassed();
|
|
7249
|
+
if (this.#batchDepth === 0) this.#notify();
|
|
7250
|
+
return this;
|
|
7251
|
+
}
|
|
7252
|
+
/** Discards all undo/redo history without changing the current query. */
|
|
7253
|
+
clearHistory() {
|
|
7254
|
+
this.#markHistoryBypassed();
|
|
7255
|
+
this.#past = [];
|
|
7256
|
+
this.#future = [];
|
|
7257
|
+
this.#lastSig = void 0;
|
|
7258
|
+
return this;
|
|
7259
|
+
}
|
|
7260
|
+
/**
|
|
7261
|
+
* The recorded history: `past` oldest first, `future` newest first. Both are copies, so
|
|
7262
|
+
* mutating them does not affect the manager.
|
|
7263
|
+
*/
|
|
7264
|
+
getHistory() {
|
|
7265
|
+
return {
|
|
7266
|
+
past: [...this.#past],
|
|
7267
|
+
future: [...this.#future]
|
|
7268
|
+
};
|
|
7269
|
+
}
|
|
7270
|
+
/**
|
|
7271
|
+
* Validates the current query with the configured validator.
|
|
7272
|
+
*
|
|
7273
|
+
* The result is cached until the query changes, so a custom `validator` with side effects (or
|
|
7274
|
+
* one that depends on anything other than the query) may run fewer times than expected.
|
|
7275
|
+
*/
|
|
7276
|
+
validate() {
|
|
7277
|
+
this.#ensureCache();
|
|
7278
|
+
this.#validation ??= this.#validator(this.#query);
|
|
7279
|
+
return this.#validation;
|
|
7280
|
+
}
|
|
7281
|
+
format(options) {
|
|
7282
|
+
return formatQuery(this.#query, options);
|
|
7283
|
+
}
|
|
7284
|
+
/**
|
|
7285
|
+
* Yields every rule and group in the query, depth-first in pre-order, starting with the root
|
|
7286
|
+
* group itself. Combinator strings in independent-combinator groups are skipped.
|
|
7287
|
+
*
|
|
7288
|
+
* ```ts
|
|
7289
|
+
* for (const { node, path, parent } of qm.walk({ rulesOnly: true })) {
|
|
7290
|
+
* console.log(path, node.field);
|
|
7291
|
+
* }
|
|
7292
|
+
* ```
|
|
7293
|
+
*
|
|
7294
|
+
* Traversal operates on the query as it was when iteration began, so mutating the manager
|
|
7295
|
+
* mid-iteration does not affect a walk already in progress. Because generators are lazy, that
|
|
7296
|
+
* happens on the first iteration step rather than when `walk` is called.
|
|
7297
|
+
*
|
|
7298
|
+
* @yields Every rule and group in the query, subject to `options`.
|
|
7299
|
+
*/
|
|
7300
|
+
*walk(options = {}) {
|
|
7301
|
+
const { from, rulesOnly, groupsOnly } = options;
|
|
7302
|
+
let start = this.#query;
|
|
7303
|
+
let startPath = [];
|
|
7304
|
+
let startParent = null;
|
|
7305
|
+
if (from !== void 0) {
|
|
7306
|
+
const path = this.#toPath(from);
|
|
7307
|
+
const node = path && findPath(path, this.#query);
|
|
7308
|
+
if (!path || !node) return;
|
|
7309
|
+
start = node;
|
|
7310
|
+
startPath = path;
|
|
7311
|
+
startParent = path.length === 0 ? null : findPath(getParentPath(path), this.#query);
|
|
7312
|
+
}
|
|
7313
|
+
for (const entry of this.#walkFrom(start, startPath, startParent)) {
|
|
7314
|
+
if (rulesOnly && isRuleGroup(entry.node)) continue;
|
|
7315
|
+
if (groupsOnly && !isRuleGroup(entry.node)) continue;
|
|
7316
|
+
yield entry;
|
|
7317
|
+
}
|
|
7318
|
+
}
|
|
7319
|
+
/** Yields every rule in the query. Shorthand for `walk({ ...options, rulesOnly: true })`. */
|
|
7320
|
+
rules(options = {}) {
|
|
7321
|
+
return this.walk({
|
|
7322
|
+
...options,
|
|
7323
|
+
rulesOnly: true
|
|
7324
|
+
});
|
|
7325
|
+
}
|
|
7326
|
+
/**
|
|
7327
|
+
* Yields every group in the query, including the root group. Shorthand for
|
|
7328
|
+
* `walk({ ...options, groupsOnly: true })`.
|
|
7329
|
+
*/
|
|
7330
|
+
groups(options = {}) {
|
|
7331
|
+
return this.walk({
|
|
7332
|
+
...options,
|
|
7333
|
+
groupsOnly: true
|
|
7334
|
+
});
|
|
7335
|
+
}
|
|
7336
|
+
/** Returns the first node matching `predicate`, or `null` if there is none. */
|
|
7337
|
+
find(predicate, options = {}) {
|
|
7338
|
+
for (const entry of this.walk(options)) if (predicate(entry)) return entry;
|
|
7339
|
+
return null;
|
|
7340
|
+
}
|
|
7341
|
+
/** Returns every node matching `predicate`. */
|
|
7342
|
+
filter(predicate, options = {}) {
|
|
7343
|
+
const results = [];
|
|
7344
|
+
for (const entry of this.walk(options)) if (predicate(entry)) results.push(entry);
|
|
7345
|
+
return results;
|
|
7346
|
+
}
|
|
7347
|
+
/** Equivalent to {@link QueryManager.walk} with no options, enabling `for...of` and spread. */
|
|
7348
|
+
[Symbol.iterator]() {
|
|
7349
|
+
return this.walk();
|
|
7350
|
+
}
|
|
7351
|
+
/**
|
|
7352
|
+
* Returns the rule or group at the given path, or `null` if the path can't be resolved.
|
|
7353
|
+
*
|
|
7354
|
+
* Unlike the standalone {@link findPath}, which can return `undefined` for an out-of-range
|
|
7355
|
+
* index, unresolvable paths are always normalized to `null` here.
|
|
7356
|
+
*/
|
|
7357
|
+
findPath(path) {
|
|
7358
|
+
return findPath(path, this.#query) ?? null;
|
|
7359
|
+
}
|
|
7360
|
+
/**
|
|
7361
|
+
* Returns the rule or group with the given `id`, or `null` if there is none. Backed by an
|
|
7362
|
+
* index built once per query, so repeated lookups are constant time.
|
|
7363
|
+
*/
|
|
7364
|
+
findID(id) {
|
|
7365
|
+
const path = this.#index().get(id);
|
|
7366
|
+
return path === void 0 ? null : findPath(path, this.#query);
|
|
7367
|
+
}
|
|
7368
|
+
/**
|
|
7369
|
+
* Returns the {@link Path} of the rule or group with the given `id`, or `null` if there is
|
|
7370
|
+
* none. Backed by an index built once per query, so repeated lookups are constant time.
|
|
7371
|
+
*/
|
|
7372
|
+
getPathOfID(id) {
|
|
7373
|
+
return this.#index().get(id) ?? null;
|
|
7374
|
+
}
|
|
7375
|
+
/**
|
|
7376
|
+
* Determines whether the rule or group at the given path is disabled, either itself or by an
|
|
7377
|
+
* ancestor group.
|
|
7378
|
+
*/
|
|
7379
|
+
pathIsDisabled(path) {
|
|
7380
|
+
return pathIsDisabled(path, this.#query);
|
|
7381
|
+
}
|
|
7382
|
+
/** Returns the rule or group at the given path or `id`, or `null` if it can't be resolved. */
|
|
7383
|
+
getNode(pathOrID) {
|
|
7384
|
+
const path = this.#toPath(pathOrID);
|
|
7385
|
+
return path === null ? null : findPath(path, this.#query) ?? null;
|
|
7386
|
+
}
|
|
7387
|
+
/**
|
|
7388
|
+
* Returns the rule at the given path or `id`, or `null` if it can't be resolved _or_ resolves
|
|
7389
|
+
* to a group.
|
|
7390
|
+
*/
|
|
7391
|
+
getRule(pathOrID) {
|
|
7392
|
+
const node = this.getNode(pathOrID);
|
|
7393
|
+
return node && !isRuleGroup(node) ? node : null;
|
|
7394
|
+
}
|
|
7395
|
+
/**
|
|
7396
|
+
* Returns the group at the given path or `id`, or `null` if it can't be resolved _or_
|
|
7397
|
+
* resolves to a rule.
|
|
7398
|
+
*/
|
|
7399
|
+
getGroup(pathOrID) {
|
|
7400
|
+
const node = this.getNode(pathOrID);
|
|
7401
|
+
return node && isRuleGroup(node) ? node : null;
|
|
7402
|
+
}
|
|
7403
|
+
/**
|
|
7404
|
+
* Returns the group containing the rule or group at the given path or `id`. Returns `null`
|
|
7405
|
+
* for the root group, which has no parent, and when the target can't be resolved.
|
|
7406
|
+
*/
|
|
7407
|
+
getParent(pathOrID) {
|
|
7408
|
+
const path = this.#toPath(pathOrID);
|
|
7409
|
+
if (path === null || path.length === 0) return null;
|
|
7410
|
+
if (!findPath(path, this.#query)) return null;
|
|
7411
|
+
return findPath(getParentPath(path), this.#query);
|
|
7412
|
+
}
|
|
7413
|
+
/**
|
|
7414
|
+
* The normalized field list, as the `QueryBuilder` component would render it. Needed to
|
|
7415
|
+
* populate a field selector.
|
|
7416
|
+
*/
|
|
7417
|
+
getFields() {
|
|
7418
|
+
return this.#fields;
|
|
7419
|
+
}
|
|
7420
|
+
/**
|
|
7421
|
+
* The normalized combinator list, as the `QueryBuilder` component would render it. Needed to
|
|
7422
|
+
* populate a combinator selector.
|
|
7423
|
+
*/
|
|
7424
|
+
getCombinators() {
|
|
7425
|
+
return this.#combinators;
|
|
7426
|
+
}
|
|
7427
|
+
/**
|
|
7428
|
+
* The field configuration for a field name. When the field isn't configured, returns the same
|
|
7429
|
+
* minimal fallback (`{ name, value, label }`, all set to the field name) that
|
|
7430
|
+
* {@link QueryManager.getRuleContext} reports as `fieldData`, so both access paths agree.
|
|
7431
|
+
*/
|
|
7432
|
+
getFieldData(field) {
|
|
7433
|
+
return getFieldData(field, this.#fieldMap);
|
|
7434
|
+
}
|
|
7435
|
+
/** The operator list for a field, mirroring `QueryBuilder`'s precedence. */
|
|
7436
|
+
getOperators(field) {
|
|
7437
|
+
return this.#operatorsFor(field);
|
|
7438
|
+
}
|
|
7439
|
+
/** The value sources available for a field/operator pair. */
|
|
7440
|
+
getValueSources(field, operator) {
|
|
7441
|
+
return this.#valueSourcesFor(field, operator);
|
|
7442
|
+
}
|
|
7443
|
+
/** The match modes available for a field. */
|
|
7444
|
+
getMatchModes(field) {
|
|
7445
|
+
return this.#matchModesFor(field);
|
|
7446
|
+
}
|
|
7447
|
+
/** The value option list for a field/operator pair. */
|
|
7448
|
+
getValues(field, operator) {
|
|
7449
|
+
return this.#valuesFor(field, operator);
|
|
7450
|
+
}
|
|
7451
|
+
/** The value editor type for a field/operator pair. */
|
|
7452
|
+
getValueEditorType(field, operator) {
|
|
7453
|
+
return this.#valueEditorTypeFor(field, operator);
|
|
7454
|
+
}
|
|
7455
|
+
/**
|
|
7456
|
+
* Resolves everything about a rule that depends on the field/operator configuration—field data,
|
|
7457
|
+
* operators, value editor type, value list, value sources, match modes, and validation result.
|
|
7458
|
+
* Returns `null` when the target can't be resolved or isn't a rule.
|
|
7459
|
+
*
|
|
7460
|
+
* This is the same derivation the `useRule` hook performs, so a non-React implementation can
|
|
7461
|
+
* render a rule without reimplementing the configuration precedence rules.
|
|
7462
|
+
*/
|
|
7463
|
+
getRuleContext(pathOrID) {
|
|
7464
|
+
const rule = this.getRule(pathOrID);
|
|
7465
|
+
if (!rule) return null;
|
|
7466
|
+
const validation = this.validate();
|
|
7467
|
+
return deriveRuleContext(rule, {
|
|
7468
|
+
fields: this.#fields,
|
|
7469
|
+
fieldMap: this.#fieldMap,
|
|
7470
|
+
getInputType: (f, o, misc) => this.#options.getInputType?.(f, o, misc) ?? null,
|
|
7471
|
+
getMatchModes: (f) => this.#matchModesFor(f),
|
|
7472
|
+
getOperators: (f) => this.#operatorsFor(f),
|
|
7473
|
+
getParameters: (f, o, misc) => this.#parametersFor(f, o, misc),
|
|
7474
|
+
getValueEditorType: (f, o) => this.#valueEditorTypeFor(f, o),
|
|
7475
|
+
getValues: (f, o) => this.#valuesFor(f, o),
|
|
7476
|
+
getValueSources: (f, o) => this.getValueSources(f, o),
|
|
7477
|
+
getSubQueryBuilderProps: (f, misc) => this.#options.getSubQueryBuilderProps?.(f, misc) ?? {}
|
|
7478
|
+
}, {
|
|
7479
|
+
validationMap: typeof validation === "boolean" ? {} : validation,
|
|
7480
|
+
id: rule.id
|
|
7481
|
+
});
|
|
7482
|
+
}
|
|
7483
|
+
/**
|
|
7484
|
+
* Resolves everything about a rule group that depends on the combinator configuration, plus its
|
|
7485
|
+
* validation result. Returns `null` when the target can't be resolved or isn't a group.
|
|
7486
|
+
*
|
|
7487
|
+
* This is the same derivation the `useRuleGroup` hook performs.
|
|
7488
|
+
*/
|
|
7489
|
+
getRuleGroupContext(pathOrID = []) {
|
|
7490
|
+
const ruleGroup = this.getGroup(pathOrID);
|
|
7491
|
+
if (!ruleGroup) return null;
|
|
7492
|
+
const validation = this.validate();
|
|
7493
|
+
return deriveRuleGroupContext(ruleGroup, this.#combinators, {
|
|
7494
|
+
validationMap: typeof validation === "boolean" ? {} : validation,
|
|
7495
|
+
id: ruleGroup.id
|
|
7496
|
+
});
|
|
7497
|
+
}
|
|
7498
|
+
/** Whether the current query uses independent combinators. */
|
|
7499
|
+
isIC() {
|
|
7500
|
+
return isRuleGroupTypeIC(this.#query);
|
|
7501
|
+
}
|
|
7502
|
+
/**
|
|
7503
|
+
* Returns the signature describing how the current query differs from `other`, as used by
|
|
7504
|
+
* this manager's history coalescing.
|
|
7505
|
+
*/
|
|
7506
|
+
signatureOf(other) {
|
|
7507
|
+
return signatureOf(this.#query, other);
|
|
7508
|
+
}
|
|
7509
|
+
/** Generates a {@link DiagnosticsResult}. Shorthand for `format('diagnostics')`. */
|
|
7510
|
+
diagnostics() {
|
|
7511
|
+
return formatQuery(this.#query, "diagnostics");
|
|
7512
|
+
}
|
|
7513
|
+
/**
|
|
7514
|
+
* Returns the current query, so `JSON.stringify(queryManager)` produces the same output as
|
|
7515
|
+
* `JSON.stringify(queryManager.getQuery())`.
|
|
7516
|
+
*/
|
|
7517
|
+
toJSON() {
|
|
7518
|
+
return this.#query;
|
|
7519
|
+
}
|
|
7520
|
+
/**
|
|
7521
|
+
* Returns a new manager with the same configuration and the current query converted to use
|
|
7522
|
+
* independent combinators. Idempotent, and never modifies this manager. As with
|
|
7523
|
+
* {@link QueryManager.clone}, subscribers and history are not carried over.
|
|
7524
|
+
*/
|
|
7525
|
+
toIC() {
|
|
7526
|
+
return new QueryManager(convertToIC(this.#query), this.#options);
|
|
7527
|
+
}
|
|
7528
|
+
/**
|
|
7529
|
+
* Returns a new manager with the same configuration and the current query converted to use a
|
|
7530
|
+
* single combinator per group. Idempotent, and never modifies this manager. As with
|
|
7531
|
+
* {@link QueryManager.clone}, subscribers and history are not carried over.
|
|
7532
|
+
*/
|
|
7533
|
+
fromIC() {
|
|
7534
|
+
return new QueryManager(convertFromIC(this.#query), this.#options);
|
|
7535
|
+
}
|
|
7536
|
+
/**
|
|
7537
|
+
* Runs {@link transformQuery} against the current query and returns its result.
|
|
7538
|
+
*
|
|
7539
|
+
* Unlike {@link QueryManager.toIC}/{@link QueryManager.fromIC}, this returns the raw
|
|
7540
|
+
* transformed value rather than a new manager, since `transformQuery` can produce arbitrary
|
|
7541
|
+
* shapes that are no longer valid queries. This manager is never modified.
|
|
7542
|
+
*/
|
|
7543
|
+
transform(options) {
|
|
7544
|
+
return transformQuery(this.#query, options);
|
|
7545
|
+
}
|
|
7546
|
+
};
|
|
7547
|
+
//#endregion
|
|
5462
7548
|
exports.LogType = LogType;
|
|
7549
|
+
exports.QueryManager = QueryManager;
|
|
7550
|
+
exports.QueryManagerError = QueryManagerError;
|
|
5463
7551
|
exports.TestID = TestID;
|
|
5464
7552
|
exports.add = add;
|
|
5465
7553
|
exports.addInPlace = addInPlace;
|
|
@@ -5468,11 +7556,17 @@ exports.bigIntJsonParseReviver = bigIntJsonParseReviver;
|
|
|
5468
7556
|
exports.bigIntJsonStringifyReplacer = bigIntJsonStringifyReplacer;
|
|
5469
7557
|
exports.celCombinatorMap = celCombinatorMap;
|
|
5470
7558
|
exports.clsx = clsx;
|
|
7559
|
+
exports.coerceBigIntValue = coerceBigIntValue;
|
|
7560
|
+
exports.coerceInputType = coerceInputType;
|
|
5471
7561
|
exports.convertFromIC = convertFromIC;
|
|
5472
7562
|
exports.convertQuery = convertQuery;
|
|
5473
7563
|
exports.convertToIC = convertToIC;
|
|
7564
|
+
exports.createQueryActions = createQueryActions;
|
|
7565
|
+
exports.createRule = createRule;
|
|
7566
|
+
exports.createRuleGroup = createRuleGroup;
|
|
5474
7567
|
exports.cypherCombinatorMap = cypherCombinatorMap;
|
|
5475
7568
|
exports.defaultCELValueProcessor = defaultCELValueProcessor;
|
|
7569
|
+
exports.defaultCoalesceMs = defaultCoalesceMs;
|
|
5476
7570
|
exports.defaultCombinatorLabelMap = defaultCombinatorLabelMap;
|
|
5477
7571
|
exports.defaultCombinators = defaultCombinators;
|
|
5478
7572
|
exports.defaultCombinatorsExtended = defaultCombinatorsExtended;
|
|
@@ -5480,6 +7574,7 @@ exports.defaultControlClassnames = defaultControlClassnames;
|
|
|
5480
7574
|
exports.defaultExportOperatorMap = defaultExportOperatorMap;
|
|
5481
7575
|
exports.defaultJoinChar = defaultJoinChar;
|
|
5482
7576
|
exports.defaultMatchModes = defaultMatchModes;
|
|
7577
|
+
exports.defaultMaxHistory = defaultMaxHistory;
|
|
5483
7578
|
exports.defaultMongoDBValueProcessor = defaultMongoDBValueProcessor;
|
|
5484
7579
|
exports.defaultNLTranslations = defaultNLTranslations;
|
|
5485
7580
|
exports.defaultOperatorLabelMap = defaultOperatorLabelMap;
|
|
@@ -5544,6 +7639,15 @@ exports.defaultValueProcessorCELByRule = defaultValueProcessorCELByRule;
|
|
|
5544
7639
|
exports.defaultValueProcessorMongoDBByRule = defaultValueProcessorMongoDBByRule;
|
|
5545
7640
|
exports.defaultValueProcessorNL = defaultValueProcessorNL;
|
|
5546
7641
|
exports.defaultValueProcessorSpELByRule = defaultValueProcessorSpELByRule;
|
|
7642
|
+
exports.derivePathInfo = derivePathInfo;
|
|
7643
|
+
exports.deriveRuleClassName = deriveRuleClassName;
|
|
7644
|
+
exports.deriveRuleClassNames = deriveRuleClassNames;
|
|
7645
|
+
exports.deriveRuleContext = deriveRuleContext;
|
|
7646
|
+
exports.deriveRuleGroupClassNames = deriveRuleGroupClassNames;
|
|
7647
|
+
exports.deriveRuleGroupContext = deriveRuleGroupContext;
|
|
7648
|
+
exports.deriveRuleGroupOuterClassName = deriveRuleGroupOuterClassName;
|
|
7649
|
+
exports.deriveRuleOuterClassName = deriveRuleOuterClassName;
|
|
7650
|
+
exports.exceedsMaxLevels = exceedsMaxLevels;
|
|
5547
7651
|
exports.filterFieldsByComparator = filterFieldsByComparator;
|
|
5548
7652
|
exports.findID = findID;
|
|
5549
7653
|
exports.findPath = findPath;
|
|
@@ -5557,28 +7661,42 @@ Object.defineProperty(exports, "generateID", {
|
|
|
5557
7661
|
}
|
|
5558
7662
|
});
|
|
5559
7663
|
exports.getCommonAncestorPath = getCommonAncestorPath;
|
|
7664
|
+
exports.getFieldData = getFieldData;
|
|
5560
7665
|
exports.getFirstOption = getFirstOption;
|
|
7666
|
+
exports.getGuardAbortReason = getGuardAbortReason;
|
|
5561
7667
|
exports.getLikeWildcards = getLikeWildcards;
|
|
5562
7668
|
exports.getMatchModesUtil = getMatchModesUtil;
|
|
7669
|
+
exports.getMultiValueUpdate = getMultiValueUpdate;
|
|
5563
7670
|
exports.getNLTranslataion = getNLTranslataion;
|
|
5564
7671
|
exports.getOption = getOption;
|
|
7672
|
+
exports.getParametersAsList = getParametersAsList;
|
|
5565
7673
|
exports.getParentPath = getParentPath;
|
|
5566
7674
|
exports.getParseNumberMethod = getParseNumberMethod;
|
|
5567
7675
|
exports.getPathOfID = getPathOfID;
|
|
5568
7676
|
exports.getQuoteFieldNamesWithArray = getQuoteFieldNamesWithArray;
|
|
5569
7677
|
exports.getQuotedFieldName = getQuotedFieldName;
|
|
5570
7678
|
exports.getRuleDefaultValue = getRuleDefaultValue;
|
|
7679
|
+
exports.getRuleGroupCombinator = getRuleGroupCombinator;
|
|
7680
|
+
exports.getRuleInputType = getRuleInputType;
|
|
7681
|
+
exports.getRuleValidationResult = getRuleValidationResult;
|
|
7682
|
+
exports.getRuleValueEditorType = getRuleValueEditorType;
|
|
7683
|
+
exports.getRuleValueSourceOptions = getRuleValueSourceOptions;
|
|
7684
|
+
exports.getRuleValues = getRuleValues;
|
|
5571
7685
|
exports.getSQLConcat = getSQLConcat;
|
|
5572
7686
|
exports.getSubqueryElementAlias = getSubqueryElementAlias;
|
|
5573
7687
|
exports.getValidationClassNames = getValidationClassNames;
|
|
7688
|
+
exports.getValueEditorReset = getValueEditorReset;
|
|
7689
|
+
exports.getValueSelectorUpdate = getValueSelectorUpdate;
|
|
5574
7690
|
exports.getValueSourcesUtil = getValueSourcesUtil;
|
|
5575
7691
|
exports.group = group;
|
|
5576
7692
|
exports.groupInPlace = groupInPlace;
|
|
5577
7693
|
exports.groupInvalidReasons = groupInvalidReasons;
|
|
7694
|
+
exports.hideValueControlsForOperator = hideValueControlsForOperator;
|
|
5578
7695
|
exports.inOperators = inOperators;
|
|
5579
7696
|
exports.insert = insert;
|
|
5580
7697
|
exports.insertInPlace = insertInPlace;
|
|
5581
7698
|
exports.isAncestor = isAncestor;
|
|
7699
|
+
exports.isBetweenOperator = isBetweenOperator;
|
|
5582
7700
|
exports.isFlexibleOptionArray = isFlexibleOptionArray;
|
|
5583
7701
|
exports.isFlexibleOptionGroupArray = isFlexibleOptionGroupArray;
|
|
5584
7702
|
exports.isFullOptionArray = isFullOptionArray;
|
|
@@ -5606,6 +7724,7 @@ exports.mongoOperators = mongoOperators;
|
|
|
5606
7724
|
exports.move = move;
|
|
5607
7725
|
exports.moveInPlace = moveInPlace;
|
|
5608
7726
|
exports.normalizeConstituentWordOrder = normalizeConstituentWordOrder;
|
|
7727
|
+
exports.normalizeValueSelectorValue = normalizeValueSelectorValue;
|
|
5609
7728
|
exports.nullFreeArray = nullFreeArray;
|
|
5610
7729
|
exports.nullOperators = nullOperators;
|
|
5611
7730
|
exports.nullOrUndefinedOrEmpty = nullOrUndefinedOrEmpty;
|
|
@@ -5615,6 +7734,7 @@ exports.objectEntries = objectEntries;
|
|
|
5615
7734
|
exports.objectKeys = objectKeys;
|
|
5616
7735
|
exports.parseNumber = parseNumber;
|
|
5617
7736
|
exports.pathIsDisabled = pathIsDisabled;
|
|
7737
|
+
exports.pathIsDisabledByPaths = pathIsDisabledByPaths;
|
|
5618
7738
|
exports.pathsAreEqual = pathsAreEqual;
|
|
5619
7739
|
exports.preferAnyProp = preferAnyProp;
|
|
5620
7740
|
exports.preferFlagProps = preferFlagProps;
|
|
@@ -5632,13 +7752,21 @@ exports.regenerateIDs = regenerateIDs;
|
|
|
5632
7752
|
exports.relationalOperators = relationalOperators;
|
|
5633
7753
|
exports.remove = remove;
|
|
5634
7754
|
exports.removeInPlace = removeInPlace;
|
|
7755
|
+
exports.resolveCandidateQuery = resolveCandidateQuery;
|
|
7756
|
+
exports.resolveDefaultOperator = resolveDefaultOperator;
|
|
7757
|
+
exports.resolveOperatorList = resolveOperatorList;
|
|
7758
|
+
exports.resolveValueEditorType = resolveValueEditorType;
|
|
7759
|
+
exports.resolveValueList = resolveValueList;
|
|
5635
7760
|
exports.rootPath = rootPath;
|
|
5636
7761
|
exports.shouldRenderAsNumber = shouldRenderAsNumber;
|
|
7762
|
+
exports.signatureOf = signatureOf;
|
|
5637
7763
|
exports.sparqlVar = sparqlVar;
|
|
5638
7764
|
exports.splitBy = splitBy;
|
|
5639
7765
|
exports.sqlDialectPresets = sqlDialectPresets;
|
|
5640
7766
|
exports.standardClassnames = standardClassnames;
|
|
7767
|
+
exports.strictAbortReasons = strictAbortReasons;
|
|
5641
7768
|
exports.stripParamPrefix = stripParamPrefix;
|
|
7769
|
+
exports.structuralSignature = structuralSignature;
|
|
5642
7770
|
exports.subqueryElementAliasBase = subqueryElementAliasBase;
|
|
5643
7771
|
exports.substringOperators = substringOperators;
|
|
5644
7772
|
exports.toArray = toArray;
|
|
@@ -5648,6 +7776,7 @@ exports.toFullOptionList = toFullOptionList;
|
|
|
5648
7776
|
exports.toFullOptionMap = toFullOptionMap;
|
|
5649
7777
|
exports.transformQuery = transformQuery;
|
|
5650
7778
|
exports.trimIfString = trimIfString;
|
|
7779
|
+
exports.unchangedSignature = unchangedSignature;
|
|
5651
7780
|
exports.uniqByIdentifier = uniqByIdentifier;
|
|
5652
7781
|
exports.uniqByName = uniqByName;
|
|
5653
7782
|
exports.uniqOptGroups = uniqOptGroups;
|