Skip to content

Commit 7b8679c

Browse files
authored
RELATIVE_DATETIME, NOW (#438)
* RELATIVE_DATE * showPrefix default false, no need to document * works * Funcs moved to BasicFuns * Added mongoFormatValue * parentFuncs * .. * 4.3.0 * lint fix * fix tests for mongo date
1 parent 6bcd0ee commit 7b8679c

19 files changed

+296
-92
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
11
# Changelog
2+
- 4.3.0
3+
- Improved function support
4+
- Functions used in examples now moved to `BasicFuncs` (exported with lib)
5+
- Added funcs `RELATIVE_DATETIME`, `NOW`, `UPPER`
6+
- Added option `showPrefix` for func args (false by default)
7+
- Added missing `mongoFormatValue` for all types in basic config (now dates are exported as `Date` objects)
28
- 4.2.0
39
- Added `textarea` widget
410
- 4.1.1

examples/demo/config.tsx

Lines changed: 3 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React, {Component} from "react";
22
import merge from "lodash/merge";
33
import {
4-
BasicConfig,
4+
BasicConfig, BasicFuncs,
55
// types:
66
Operators, Widgets, Fields, Config, Types, Conjunctions, Settings, LocaleSettings, OperatorProximity, Funcs,
77
DateTimeFieldSettings,
@@ -393,7 +393,7 @@ export default (skin: string) => {
393393
datetime: {
394394
label: "DateTime",
395395
type: "datetime",
396-
valueSources: ["value"]
396+
valueSources: ["value", "func"]
397397
},
398398
datetime2: {
399399
label: "DateTime2",
@@ -511,61 +511,10 @@ export default (skin: string) => {
511511
//////////////////////////////////////////////////////////////////////
512512

513513
const funcs: Funcs = {
514-
LOWER: {
515-
label: "Lowercase",
516-
mongoFunc: "$toLower",
517-
jsonLogic: "toLowerCase",
518-
//jsonLogicIsMethod: true, // Removed in JsonLogic 2.x due to Prototype Pollution
519-
returnType: "text",
520-
args: {
521-
str: {
522-
label: "String",
523-
type: "text",
524-
valueSources: ["value", "field"],
525-
},
526-
}
527-
},
528-
LINEAR_REGRESSION: {
529-
label: "Linear regression",
530-
returnType: "number",
531-
formatFunc: ({coef, bias, val}, _) => `(${coef} * ${val} + ${bias})`,
532-
sqlFormatFunc: ({coef, bias, val}) => `(${coef} * ${val} + ${bias})`,
533-
mongoFormatFunc: ({coef, bias, val}) => ({"$sum": [{"$multiply": [coef, val]}, bias]}),
534-
jsonLogic: ({coef, bias, val}) => ({ "+": [ {"*": [coef, val]}, bias ] }),
535-
/* eslint-disable */
536-
jsonLogicImport: (v: any) => {
537-
const coef = v["+"][0]["*"][0];
538-
const val = v["+"][0]["*"][1];
539-
const bias = v["+"][1];
540-
return [coef, val, bias];
541-
},
542-
/* eslint-enable */
543-
renderBrackets: ["", ""],
544-
renderSeps: [" * ", " + "],
545-
args: {
546-
coef: {
547-
label: "Coef",
548-
type: "number",
549-
defaultValue: 1,
550-
valueSources: ["value"],
551-
},
552-
val: {
553-
label: "Value",
554-
type: "number",
555-
valueSources: ["value"],
556-
},
557-
bias: {
558-
label: "Bias",
559-
type: "number",
560-
defaultValue: 0,
561-
valueSources: ["value"],
562-
}
563-
}
564-
},
514+
...BasicFuncs
565515
};
566516

567517

568-
569518
const config: Config = {
570519
conjunctions,
571520
operators,

examples/webpack.config.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ let plugins = [
2222
NODE_ENV: JSON.stringify(MODE),
2323
}
2424
}),
25+
new webpack.ProvidePlugin({
26+
process: 'process/browser',
27+
Buffer: ['buffer', 'Buffer'],
28+
}),
2529
];
2630
let aliases = {
2731
[lib_name]: MODULES

modules/components/rule/FuncSelect.jsx

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,12 @@ export default class FuncSelect extends PureComponent {
1616
static propTypes = {
1717
config: PropTypes.object.isRequired,
1818
field: PropTypes.string.isRequired,
19-
operator: PropTypes.string.isRequired,
19+
operator: PropTypes.string,
2020
customProps: PropTypes.object,
2121
value: PropTypes.string,
2222
setValue: PropTypes.func.isRequired,
2323
readonly: PropTypes.bool,
24+
parentFuncs: PropTypes.array,
2425
};
2526

2627
constructor(props) {
@@ -37,17 +38,17 @@ export default class FuncSelect extends PureComponent {
3738
const needUpdateItems = !this.items || keysForItems.map(k => (nextProps[k] !== prevProps[k])).filter(ch => ch).length > 0;
3839
const needUpdateMeta = !this.meta || keysForMeta.map(k => (nextProps[k] !== prevProps[k])).filter(ch => ch).length > 0;
3940

40-
if (needUpdateItems) {
41-
this.items = this.getItems(nextProps);
42-
}
4341
if (needUpdateMeta) {
4442
this.meta = this.getMeta(nextProps);
4543
}
44+
if (needUpdateItems) {
45+
this.items = this.getItems(nextProps);
46+
}
4647
}
4748

48-
getItems({config, field, operator}) {
49+
getItems({config, field, operator, parentFuncs}) {
4950
const {canUseFuncForField} = config.settings;
50-
const filteredFuncs = this.filterFuncs(config, config.funcs, field, operator, canUseFuncForField);
51+
const filteredFuncs = this.filterFuncs(config, config.funcs, field, operator, canUseFuncForField, parentFuncs);
5152
const items = this.buildOptions(config, filteredFuncs);
5253
return items;
5354
}
@@ -79,7 +80,7 @@ export default class FuncSelect extends PureComponent {
7980
};
8081
}
8182

82-
filterFuncs(config, funcs, leftFieldFullkey, operator, canUseFuncForField) {
83+
filterFuncs(config, funcs, leftFieldFullkey, operator, canUseFuncForField, parentFuncs) {
8384
funcs = clone(funcs);
8485
const fieldSeparator = config.settings.fieldSeparator;
8586
const leftFieldConfig = getFieldConfig(config, leftFieldFullkey);
@@ -109,6 +110,9 @@ export default class FuncSelect extends PureComponent {
109110
canUse = canUse && leftFieldConfig.funcs.includes(funcFullkey);
110111
if (canUseFuncForField)
111112
canUse = canUse && canUseFuncForField(leftFieldFullkey, leftFieldConfig, funcFullkey, funcConfig, operator);
113+
// don't use func in func (can be configurable, but usually users don't need this)
114+
if (parentFuncs && parentFuncs.includes(funcFullkey))
115+
canUse = false;
112116
if (!canUse)
113117
delete list[funcKey];
114118
}

modules/components/rule/FuncWidget.jsx

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,12 @@ export default class FuncWidget extends PureComponent {
1414
static propTypes = {
1515
config: PropTypes.object.isRequired,
1616
field: PropTypes.string.isRequired,
17-
operator: PropTypes.string.isRequired,
17+
operator: PropTypes.string,
1818
customProps: PropTypes.object,
1919
value: PropTypes.object, //instanceOf(Immutable.Map) //with keys 'func' and `args`
2020
setValue: PropTypes.func.isRequired,
2121
readonly: PropTypes.bool,
22+
parentFuncs: PropTypes.array,
2223
};
2324

2425
constructor(props) {
@@ -68,12 +69,12 @@ export default class FuncWidget extends PureComponent {
6869
};
6970

7071
renderFuncSelect = () => {
71-
const {config, field, operator, customProps, value, readonly} = this.props;
72+
const {config, field, operator, customProps, value, readonly, parentFuncs} = this.props;
7273
const funcKey = value ? value.get("func") : null;
7374
const selectProps = {
7475
value: funcKey,
7576
setValue: this.setFunc,
76-
config, field, operator, customProps, readonly,
77+
config, field, operator, customProps, readonly, parentFuncs,
7778
};
7879
const {showLabels, funcLabel} = config.settings;
7980
const widgetLabel = showLabels
@@ -89,21 +90,23 @@ export default class FuncWidget extends PureComponent {
8990
};
9091

9192
renderArgLabel = (argKey, argDefinition) => {
93+
const {valueSources, type, showPrefix, label} = argDefinition;
9294
const {config} = this.props;
93-
const isConst = argDefinition.valueSources && argDefinition.valueSources.length == 1 && argDefinition.valueSources[0] == "const";
94-
const forceShow = !config.settings.showLabels && (argDefinition.type == "boolean" || isConst);
95+
const isConst = valueSources && valueSources.length == 1 && valueSources[0] == "const";
96+
const forceShow = !config.settings.showLabels && (type == "boolean" || isConst) && showPrefix;
9597
if (!forceShow) return null;
9698
return (
9799
<Col className="rule--func--arg-label">
98-
{argDefinition.label || argKey}
100+
{label || argKey}
99101
</Col>
100102
);
101103
};
102104

103105
renderArgLabelSep = (argKey, argDefinition) => {
106+
const {valueSources, type, showPrefix} = argDefinition;
104107
const {config} = this.props;
105-
const isConst = argDefinition.valueSources && argDefinition.valueSources.length == 1 && argDefinition.valueSources[0] == "const";
106-
const forceShow = !config.settings.showLabels && (argDefinition.type == "boolean" || isConst);
108+
const isConst = valueSources && valueSources.length == 1 && valueSources[0] == "const";
109+
const forceShow = !config.settings.showLabels && (type == "boolean" || isConst) && showPrefix;
107110
if (!forceShow) return null;
108111
return (
109112
<Col className="rule--func--arg-label-sep">
@@ -113,7 +116,7 @@ export default class FuncWidget extends PureComponent {
113116
};
114117

115118
renderArgVal = (funcKey, argKey, argDefinition) => {
116-
const {config, field, operator, value, readonly} = this.props;
119+
const {config, field, operator, value, readonly, parentFuncs} = this.props;
117120
const arg = value ? value.getIn(["args", argKey]) : null;
118121
const argVal = arg ? arg.get("value") : undefined;
119122
const defaultValueSource = argDefinition.valueSources.length == 1 ? argDefinition.valueSources[0] : undefined;
@@ -133,6 +136,7 @@ export default class FuncWidget extends PureComponent {
133136
argKey,
134137
argDefinition,
135138
readonly,
139+
parentFuncs,
136140
};
137141
//tip: value & valueSrc will be converted to Immutable.List at <Widget>
138142

@@ -210,6 +214,7 @@ class ArgWidget extends PureComponent {
210214
setValue: PropTypes.func.isRequired,
211215
setValueSrc: PropTypes.func.isRequired,
212216
readonly: PropTypes.bool,
217+
parentFuncs: PropTypes.array,
213218
};
214219

215220
setValue = (_delta, value, _widgetType) => {
@@ -223,12 +228,14 @@ class ArgWidget extends PureComponent {
223228
}
224229

225230
render() {
231+
const {funcKey, parentFuncs} = this.props;
226232
return (
227233
<Widget
228234
{...this.props}
229235
setValue={this.setValue}
230236
setValueSrc={this.setValueSrc}
231237
isFuncArg={true}
238+
parentFuncs={[...(parentFuncs || []), funcKey]}
232239
/>
233240
);
234241
}

modules/components/rule/Widget.jsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ export default class Widget extends PureComponent {
3636
// for RuleGroupExt
3737
isForRuleGruop: PropTypes.bool,
3838
parentField: PropTypes.string,
39+
// for func in func
40+
parentFuncs: PropTypes.array,
3941
};
4042

4143
constructor(props) {
@@ -162,7 +164,7 @@ export default class Widget extends PureComponent {
162164
}
163165

164166
renderWidget = (delta, meta, props) => {
165-
const {config, isFuncArg, leftField, operator, value: values, valueError, readonly, parentField} = props;
167+
const {config, isFuncArg, leftField, operator, value: values, valueError, readonly, parentField, parentFuncs} = props;
166168
const {settings} = config;
167169
const { widgets, iValues, aField } = meta;
168170
const value = isFuncArg ? iValues : values;
@@ -187,6 +189,7 @@ export default class Widget extends PureComponent {
187189
config={config}
188190
field={field}
189191
parentField={parentField}
192+
parentFuncs={parentFuncs}
190193
operator={operator}
191194
readonly={readonly}
192195
/>

modules/components/rule/WidgetFactory.jsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ export default ({
66
value: immValue, valueError: immValueError,
77
isSpecialRange, fieldDefinition,
88
widget, widgetDefinition, widgetValueLabel, valueLabels, textSeparators, setValueHandler,
9-
config, field, operator, readonly, parentField,
9+
config, field, operator, readonly, parentField, parentFuncs,
1010
}) => {
1111
const {factory: widgetFactory, ...fieldWidgetProps} = widgetDefinition;
1212
const isConst = isFuncArg && fieldDefinition.valueSources && fieldDefinition.valueSources.length == 1 && fieldDefinition.valueSources[0] == "const";
@@ -30,6 +30,7 @@ export default ({
3030
config: config,
3131
field: field,
3232
parentField: parentField,
33+
parentFuncs: parentFuncs,
3334
fieldDefinition: fieldDefinition,
3435
operator: operator,
3536
delta: delta,

modules/config/basic.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,7 @@ const widgets = {
481481
}
482482
},
483483
toJS: (val, fieldSettings) => (val),
484+
mongoFormatValue: (val, fieldDef, wgtDef) => (val),
484485
},
485486
textarea: {
486487
type: "text",
@@ -500,6 +501,7 @@ const widgets = {
500501
}
501502
},
502503
toJS: (val, fieldSettings) => (val),
504+
mongoFormatValue: (val, fieldDef, wgtDef) => (val),
503505
fullWidth: true,
504506
},
505507
number: {
@@ -520,6 +522,7 @@ const widgets = {
520522
return SqlString.escape(val);
521523
},
522524
toJS: (val, fieldSettings) => (val),
525+
mongoFormatValue: (val, fieldDef, wgtDef) => (val),
523526
},
524527
slider: {
525528
type: "number",
@@ -535,6 +538,7 @@ const widgets = {
535538
return SqlString.escape(val);
536539
},
537540
toJS: (val, fieldSettings) => (val),
541+
mongoFormatValue: (val, fieldDef, wgtDef) => (val),
538542
},
539543
select: {
540544
type: "select",
@@ -551,6 +555,7 @@ const widgets = {
551555
return SqlString.escape(val);
552556
},
553557
toJS: (val, fieldSettings) => (val),
558+
mongoFormatValue: (val, fieldDef, wgtDef) => (val),
554559
},
555560
multiselect: {
556561
type: "multiselect",
@@ -567,6 +572,7 @@ const widgets = {
567572
return vals.map(v => SqlString.escape(v));
568573
},
569574
toJS: (val, fieldSettings) => (val),
575+
mongoFormatValue: (val, fieldDef, wgtDef) => (val),
570576
},
571577
date: {
572578
type: "date",
@@ -595,6 +601,10 @@ const widgets = {
595601
const dateVal = moment(val, fieldSettings.valueFormat);
596602
return dateVal.isValid() ? dateVal.toDate() : undefined;
597603
},
604+
mongoFormatValue: (val, fieldDef, wgtDef) => {
605+
const dateVal = moment(val, wgtDef.valueFormat);
606+
return dateVal.isValid() ? dateVal.toDate() : undefined;
607+
}
598608
},
599609
time: {
600610
type: "time",
@@ -629,6 +639,11 @@ const widgets = {
629639
const dateVal = moment(val, fieldSettings.valueFormat);
630640
return dateVal.isValid() ? dateVal.get("hour") * 60 * 60 + dateVal.get("minute") * 60 + dateVal.get("second") : undefined;
631641
},
642+
mongoFormatValue: (val, fieldDef, wgtDef) => {
643+
// return seconds of day
644+
const dateVal = moment(val, wgtDef.valueFormat);
645+
return dateVal.get("hour") * 60 * 60 + dateVal.get("minute") * 60 + dateVal.get("second");
646+
},
632647
},
633648
datetime: {
634649
type: "datetime",
@@ -659,6 +674,10 @@ const widgets = {
659674
const dateVal = moment(val, fieldSettings.valueFormat);
660675
return dateVal.isValid() ? dateVal.toDate() : undefined;
661676
},
677+
mongoFormatValue: (val, fieldDef, wgtDef) => {
678+
const dateVal = moment(val, wgtDef.valueFormat);
679+
return dateVal.isValid() ? dateVal.toDate() : undefined;
680+
}
662681
},
663682
boolean: {
664683
type: "boolean",
@@ -675,6 +694,7 @@ const widgets = {
675694
},
676695
defaultValue: false,
677696
toJS: (val, fieldSettings) => (val),
697+
mongoFormatValue: (val, fieldDef, wgtDef) => (val),
678698
},
679699
field: {
680700
valueSrc: "field",

0 commit comments

Comments
 (0)