Skip to content

Commit

Permalink
Add settings defaultField and defaultOperator (#956)
Browse files Browse the repository at this point in the history
* fix shrink

* d.ts

* .

* upd

* ch

* fix

* fix tests

* more tests

* + test

* .
  • Loading branch information
ukrbublik authored Jul 24, 2023
1 parent 2e5d6ce commit a0d4991
Show file tree
Hide file tree
Showing 23 changed files with 408 additions and 140 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
- Autocomplete items now support `groupTitle` (PR #953) (issue #600)
- Add more types in `Utils.Autocomplete` (PR #953) (issue #934)
- Add settings `fieldItemKeysForSearch` and `listKeysForSearch` (PR #954) (issue #931)
- Add settings `defaultField` and `defaultOperator` (PR #956) (issue #763)
- 6.3.0
- Allow saving and loading config from server (PR #866) (issue #817)
- New utils: `compressConfig()`, `decompressConfig()`
Expand Down
4 changes: 2 additions & 2 deletions packages/core/modules/actions/tree.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@ export const setTree = (config, tree) => ({
* @param {Immutable.List} path
* @param {Immutable.Map} properties
*/
export const addRule = (config, path, properties, ruleType = "rule", children = null) => ({
export const addRule = (config, path, properties, ruleType = "rule", children = null, parentRuleGroupPath = null) => ({
type: constants.ADD_RULE,
ruleType: ruleType,
children: children,
path: toImmutableList(path),
id: uuid(),
properties: defaultRuleProperties(config).merge(properties || {}),
properties: defaultRuleProperties(config, parentRuleGroupPath).merge(properties || {}),
config: config
});

Expand Down
9 changes: 5 additions & 4 deletions packages/core/modules/import/tree.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ export const loadTree = (serTree) => {
if (isImmutableTree(serTree)) {
return serTree;
} else if (isTree(serTree)) {
return jsTreeToImmutable(serTree);
return jsToImmutable(serTree);
} else if (typeof serTree == "string" && serTree.startsWith('["~#iM"')) {
//tip: old versions of RAQB were saving tree with `transit.toJSON()`
// https://github.com/ukrbublik/react-awesome-query-builder/issues/69
throw "You are trying to load query in obsolete serialization format (Immutable string) which is not supported in versions starting from 2.1.17";
} else if (typeof serTree == "string") {
return jsTreeToImmutable(JSON.parse(serTree));
return jsToImmutable(JSON.parse(serTree));
} else throw "Can't load tree!";
};

Expand All @@ -47,8 +47,8 @@ export const isTree = (tree) => {

export {isJsonLogic};

function jsTreeToImmutable(tree) {
return fromJS(tree, function (key, value) {
export function jsToImmutable(tree) {
const imm = fromJS(tree, function (key, value) {
let outValue;
if (key == "properties") {
outValue = value.toOrderedMap();
Expand Down Expand Up @@ -83,5 +83,6 @@ function jsTreeToImmutable(tree) {
}
return outValue;
});
return imm;
}

193 changes: 140 additions & 53 deletions packages/core/modules/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export type Moment = MomentType;
export type ImmutableList<T> = ImmList<T>;
export type ImmutableMap<K, V> = ImmMap<K, V>;
export type ImmutableOMap<K, V> = ImmOMap<K, V>;
export type AnyImmutable = ImmutableList<any> | ImmutableMap<string, any> | ImmutableOMap<string, any>;

////////////////
// common
Expand All @@ -27,7 +28,8 @@ type AnyObject = {
};
type Empty = null | undefined;

type IdPath = Array<string> | ImmutableList<string>;
type ImmutablePath = ImmutableList<string>;
type IdPath = Array<string> | ImmutablePath;

type Optional<T> = {
[P in keyof T]?: T[P];
Expand Down Expand Up @@ -75,19 +77,50 @@ export type ConfigContext = {
[key: string]: any;
};

export type FlatItem = {
type: ItemType;
parent: string | null;
parentType: ItemType;
caseId: string;
isDefaultCase: boolean;
path: string[];
lev: number;
leaf: boolean;
index: number;
id: string;
children: string[];
leafsCount: number;
_top: number;
_height: number;
top: number;
height: number;
bottom: number;
collapsed: boolean;
node: ImmutableItem;
isLocked: boolean;
};
export type FlatTree = {
flat: string[];
items: TypedMap<FlatItem>;
};

////////////////
// query value
/////////////////

export type RuleValue = boolean | number | string | Date | Array<string> | any;
export type FieldPath = string;
export type FieldFuncValue = ImmutableMap<"func" | "args", any>;
export type FieldValue = FieldPath | FieldFuncValue;
export type FieldFuncValueI = ImmutableMap<"func" | "args", any>;
export interface FieldFuncValue {
func: string,
args: Record<string, any>
}
export type FieldValue = FieldPath | FieldFuncValueI;

export type ValueSource = "value" | "field" | "func" | "const";
export type FieldSource = "field" | "func";
export type RuleGroupMode = "struct" | "some" | "array";
export type ItemType = "group" | "rule_group" | "rule";
export type ItemType = "group" | "rule_group" | "rule" | "case_group" | "switch_group";
export type ItemProperties = RuleProperties | RuleGroupExtProperties | RuleGroupProperties | GroupProperties;

export type TypedValueSourceMap<T> = {
Expand Down Expand Up @@ -172,7 +205,7 @@ type JsonRule = {
export type JsonTree = JsonGroup|JsonSwitchGroup;

export type ImmutableTree = ImmutableOMap<string, any>;

export type ImmutableItem = ImmutableOMap<string, any>;

////////////////
// Utils
Expand All @@ -195,8 +228,23 @@ interface SpelConcatNormalValue {
}
type SpelConcatValue = SpelConcatNormalValue | SpelConcatCaseValue;

export interface Utils {
// export
interface Import {
// tree
getTree(tree: ImmutableTree, light?: boolean, children1AsArray?: boolean): JsonTree;
loadTree(jsonTree: JsonTree): ImmutableTree;
checkTree(tree: ImmutableTree, config: Config): ImmutableTree;
isValidTree(tree: ImmutableTree): boolean;
isImmutableTree(tree: any): boolean;
isTree(tree: any): boolean; // is JsonTree ?
isJsonLogic(value: any): boolean;
jsToImmutable(value: any): AnyImmutable;
// jsonlogic
loadFromJsonLogic(logicTree: JsonLogicTree | undefined, config: Config): ImmutableTree | undefined;
_loadFromJsonLogic(logicTree: JsonLogicTree | undefined, config: Config): [ImmutableTree | undefined, Array<string>];
// spel
loadFromSpel(spelStr: string, config: Config): [ImmutableTree | undefined, Array<string>];
}
interface Export {
jsonLogicFormat(tree: ImmutableTree, config: Config): JsonLogicResult;
// @deprecated
queryBuilderFormat(tree: ImmutableTree, config: Config): Object | undefined;
Expand All @@ -208,59 +256,94 @@ export interface Utils {
mongodbFormat(tree: ImmutableTree, config: Config): Object | undefined;
_mongodbFormat(tree: ImmutableTree, config: Config): [Object | undefined, Array<string>];
elasticSearchFormat(tree: ImmutableTree, config: Config, syntax?: "ES_6_SYNTAX" | "ES_7_SYNTAX"): Object | undefined;
// load, save
getTree(tree: ImmutableTree, light?: boolean, children1AsArray?: boolean): JsonTree;
loadTree(jsonTree: JsonTree): ImmutableTree;
checkTree(tree: ImmutableTree, config: Config): ImmutableTree;
isValidTree(tree: ImmutableTree): boolean;
getSwitchValues(tree: ImmutableTree): Array<SpelConcatParts | null>;
// import
loadFromJsonLogic(logicTree: JsonLogicTree | undefined, config: Config): ImmutableTree | undefined;
_loadFromJsonLogic(logicTree: JsonLogicTree | undefined, config: Config): [ImmutableTree | undefined, Array<string>];
loadFromSpel(spelStr: string, config: Config): [ImmutableTree | undefined, Array<string>];
}
interface Autocomplete {
simulateAsyncFetch(all: ListValues, pageSize?: number, delay?: number): AsyncFetchListValuesFn;
getListValue(value: string | number, listValues: ListValues): ListItem; // get by value
// internal
mergeListValues(oldValues: ListItems, newValues: ListItems, toStart = false): ListItems;
listValueToOption(listItem: ListItem): ListOptionUi;
}
interface ConfigUtils {
compressConfig(config: Config, baseConfig: Config): ZipConfig;
decompressConfig(zipConfig: ZipConfig, baseConfig: Config, ctx?: ConfigContext): Config;
compileConfig(config: Config): Config;
extendConfig(config: Config): Config;
getFieldConfig(config: Config, field: FieldValue): Field | Func | null;
getFuncConfig(config: Config, func: string): Func | null;
getFuncArgConfig(config: Config, func: string, arg: string): FuncArg | null;
getOperatorConfig(config: Config, operator: string, field?: FieldValue): Operator | null;
getFieldWidgetConfig(config: Config, field: FieldValue, operator: string, widget?: string, valueStr?: ValueSource): Widget | null;
isJSX(jsx: any): boolean;
isDirtyJSX(jsx: any): boolean;
cleanJSX(jsx: any): Object;
applyJsonLogic(logic: any, data?: any): any;
}
interface ExportUtils {
spelEscape(val: any): string;
spelFormatConcat(parts: SpelConcatParts): string;
spelImportConcat(val: SpelConcatValue): [SpelConcatParts | undefined, Array<string>];
}
interface ListUtils {
getTitleInListValues(listValues: ListValues, value: string | number): string;
getListValue(value: string | number, listValues: ListValues): ListItem; // get by value
searchListValue(search: string, listValues: ListValues): ListItem; // search by value and title
listValuesToArray(listValues: ListValues): ListItems; // normalize
toListValue(value: string | number | ListItem, title?: string): ListItem; // create
makeCustomListValue(value: string | number): ListItem; // create
}
interface TreeUtils {
jsToImmutable(value: any): AnyImmutable;
immutableToJs(imm: AnyImmutable): any;
isImmutable(value: any): boolean;
toImmutableList(path: string[]): ImmutablePath;
getItemByPath(tree: ImmutableTree, path: IdPath): ImmutableItem;
expandTreePath(path: ImmutablePath, ...suffix: string[]): ImmutablePath;
expandTreeSubpath(path: ImmutablePath, ...suffix: string[]): ImmutablePath;
fixEmptyGroupsInTree(tree: ImmutableTree): ImmutableTree;
fixPathsInTree(tree: ImmutableTree): ImmutableTree;
getFlatTree(tree: ImmutableTree): FlatTree;
getTotalReordableNodesCountInTree(tree: ImmutableTree): number;
getTotalRulesCountInTree(tree: ImmutableTree): number;
getTreeBadFields(tree: ImmutableTree): Array<FieldValue>;
isEmptyTree(tree: ImmutableTree): boolean;
}
interface OtherUtils {
uuid(): string;
deepEqual(a: any, b: any): boolean;
shallowEqual(a: any, b: any, deep = false): boolean;
mergeArraysSmart(a: string[], b: string[]): string[];
isJsonCompatible(tpl: object, target: object, bag: Record<string, any>): boolean; // mutates bag
isJsonLogic(value: any): boolean;
isJSX(jsx: any): boolean;
isDirtyJSX(jsx: any): boolean;
cleanJSX(jsx: any): Object;
escapeRegExp(str: string): string;
//applyToJS(imm: any): any; // same as immutableToJs
isImmutable(value: any): boolean;
toImmutableList(path: string[]): ImmutablePath;
}

export interface Utils extends Import, Export {
// case mode
getSwitchValues(tree: ImmutableTree): Array<SpelConcatParts | null>;
// other
uuid(): string;
// ssr
compressConfig(config: Config, baseConfig: Config): ZipConfig;
decompressConfig(zipConfig: ZipConfig, baseConfig: Config, ctx?: ConfigContext): Config;
// validation
validateTree(tree: ImmutableTree, _oldTree: ImmutableTree, config: Config, oldConfig: Config, removeEmptyGroups?: boolean, removeIncompleteRules?: boolean): ImmutableTree;
validateAndFixTree(tree: ImmutableTree, _oldTree: ImmutableTree, config: Config, oldConfig: Config, removeEmptyGroups?: boolean, removeIncompleteRules?: boolean): ImmutableTree;

Autocomplete: {
simulateAsyncFetch(all: ListValues, pageSize?: number, delay?: number): AsyncFetchListValuesFn;
getListValue(value: string | number, listValues: ListValues): ListItem; // get by value
// internal
mergeListValues(oldValues: ListItems, newValues: ListItems, toStart = false): ListItems;
listValueToOption(listItem: ListItem): ListOptionUi;
};
ConfigUtils: {
compressConfig(config: Config, baseConfig: Config): ZipConfig;
decompressConfig(zipConfig: ZipConfig, baseConfig: Config, ctx?: ConfigContext): Config;
compileConfig(config: Config): Config;
extendConfig(config: Config): Config;
getFieldConfig(config: Config, field: FieldValue): Field | Func | null;
getFuncConfig(config: Config, func: string): Func | null;
getFuncArgConfig(config: Config, func: string, arg: string): FuncArg | null;
getOperatorConfig(config: Config, operator: string, field?: FieldValue): Operator | null;
getFieldWidgetConfig(config: Config, field: FieldValue, operator: string, widget?: string, valueStr?: ValueSource): Widget | null;
isJsonLogic(value: any): boolean;
isJSX(jsx: any): boolean;
isDirtyJSX(jsx: any): boolean;
cleanJSX(jsx: any): Object;
applyJsonLogic(logic: any, data?: any): any;
};
ExportUtils: {
spelEscape(val: any): string;
spelFormatConcat(parts: SpelConcatParts): string;
spelImportConcat(val: SpelConcatValue): [SpelConcatParts | undefined, Array<string>],
},
ListUtils: {
getTitleInListValues(listValues: ListValues, value: string | number): string;
getListValue(value: string | number, listValues: ListValues): ListItem; // get by value
searchListValue(search: string, listValues: ListValues): ListItem; // search by value and title
listValuesToArray(listValues: ListValues): ListItems; // normalize
toListValue(value: string | number | ListItem, title?: string): ListItem; // create
makeCustomListValue(value: string | number): ListItem; // create
}
Import: Import;
Export: Export;
Autocomplete: Autocomplete;
ConfigUtils: ConfigUtils;
ExportUtils: ExportUtils;
ListUtils: ListUtils;
TreeUtils: TreeUtils;
OtherUtils: OtherUtils;
}


Expand Down Expand Up @@ -841,13 +924,15 @@ interface FieldGroup extends BaseField {
mode: RuleGroupMode,
isSpelArray?: boolean,
isSpelItemMap?: boolean,
defaultField?: FieldPath,
}
interface FieldGroupExt extends BaseField {
type: "!group",
subfields: Fields,
mode: "array",
operators?: Array<string>,
defaultOperator?: string,
defaultField?: FieldPath,
initialEmptyWhere?: boolean,
showNot?: boolean,
conjunctions?: Array<string>,
Expand Down Expand Up @@ -938,6 +1023,8 @@ export interface LocaleSettings {


export interface BehaviourSettings {
defaultField?: FieldPath | FieldFuncValue | FieldFuncValueI,
defaultOperator?: string;
fieldSources?: Array<FieldSource>,
valueSourcesInfo?: ValueSourcesInfo,
canCompareFieldWithField?: CanCompareFieldWithField | SerializedFunction,
Expand Down
26 changes: 19 additions & 7 deletions packages/core/modules/stores/tree.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
getTotalRulesCountInTree, fixEmptyGroupsInTree, isEmptyTree, hasChildren, removeIsLockedInTree
} from "../utils/treeUtils";
import {
defaultRuleProperties, defaultGroupProperties, defaultOperator,
defaultRuleProperties, defaultGroupProperties, getDefaultOperator,
defaultOperatorOptions, defaultRoot, defaultItemProperties
} from "../utils/defaultUtils";
import * as constants from "./constants";
Expand Down Expand Up @@ -66,8 +66,14 @@ const removeGroup = (state, path, config) => {
state = fixEmptyGroupsInTree(state);

if (isEmptyTree(state) && !canLeaveEmptyGroup) {
// if whole query is empty, add one empty rule to root
state = addItem(state, new Immutable.List(), "rule", uuid(), defaultRuleProperties(config), config);
// if whole query is empty, add one empty(!) rule to root
const canUseDefaultFieldAndOp = false;
const canGetFirst = false;
state = addItem(
state, new Immutable.List(), "rule", uuid(),
defaultRuleProperties(config, undefined, undefined, canUseDefaultFieldAndOp, canGetFirst),
config
);
}
}
state = fixPathsInTree(state);
Expand Down Expand Up @@ -108,8 +114,14 @@ const removeRule = (state, path, config) => {
state = fixEmptyGroupsInTree(state);

if (isEmptyTree(state) && !canLeaveEmptyGroup) {
// if whole query is empty, add one empty rule to root
state = addItem(state, new Immutable.List(), "rule", uuid(), defaultRuleProperties(config), config);
// if whole query is empty, add one empty(!) rule to root
const canUseDefaultFieldAndOp = false;
const canGetFirst = false;
state = addItem(
state, new Immutable.List(), "rule", uuid(),
defaultRuleProperties(config, undefined, undefined, canUseDefaultFieldAndOp, canGetFirst),
config
);
}
}
state = fixPathsInTree(state);
Expand Down Expand Up @@ -341,7 +353,7 @@ const setFieldSrc = (state, path, srcKey, config) => {
// clear ALL properties
state = state.setIn(
expandTreePath(path, "properties"),
defaultRuleProperties(config)
defaultRuleProperties(config, null, null, false)
);
} else {
// clear non-relevant properties
Expand Down Expand Up @@ -410,7 +422,7 @@ const setField = (state, path, newField, config, asyncListValues, __isInternal)
if (strategy == "keep" && !isChangeToAnotherType)
newOperator = lastOp;
else if (strategy == "default")
newOperator = defaultOperator(config, newField, false);
newOperator = getDefaultOperator(config, newField, false);
else if (strategy == "first")
newOperator = getFirstOperator(config, newField);
if (newOperator) //found op for strategy
Expand Down
1 change: 1 addition & 0 deletions packages/core/modules/utils/configUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ function _extendFieldConfig(fieldConfig, config, path = null, isFuncArg = false)
if (!isFuncArg) {
if (!fieldConfig.operators && operators)
fieldConfig.operators = Array.from(new Set(operators));
fieldConfig._origDefaultOperator = fieldConfig.defaultOperator;
if (!fieldConfig.defaultOperator && defaultOperator)
fieldConfig.defaultOperator = defaultOperator;
}
Expand Down
Loading

3 comments on commit a0d4991

@vercel
Copy link

@vercel vercel bot commented on a0d4991 Jul 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vercel
Copy link

@vercel vercel bot commented on a0d4991 Jul 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vercel
Copy link

@vercel vercel bot commented on a0d4991 Jul 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.