hippo-fw 0.9.7 → 0.9.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.eslintrc.js +0 -3
- data/.nvmrc +1 -2
- data/.ruby-version +1 -1
- data/Rakefile +1 -1
- data/client/hippo/access/login-dialog.jsx +2 -0
- data/client/hippo/boot.jsx +12 -0
- data/client/hippo/components/asset.jsx +5 -1
- data/client/hippo/components/asset.scss +1 -0
- data/client/hippo/components/data-list.jsx +6 -4
- data/client/hippo/components/data-table.jsx +7 -6
- data/client/hippo/components/data-table/header-cell.jsx +2 -0
- data/client/hippo/components/date-time.jsx +12 -2
- data/client/hippo/components/form/api.js +12 -0
- data/client/hippo/components/form/fields.jsx +8 -1
- data/client/hippo/components/form/fields/checkbox-wrapper.jsx +2 -0
- data/client/hippo/components/form/fields/date-wrapper.jsx +2 -0
- data/client/hippo/components/form/fields/form-field.scss +2 -0
- data/client/hippo/components/form/fields/label.jsx +2 -0
- data/client/hippo/components/form/fields/react-tags.scss +170 -0
- data/client/hippo/components/form/fields/select-wrapper.jsx +2 -0
- data/client/hippo/components/form/fields/tags-wrapper.jsx +46 -0
- data/client/hippo/components/form/fields/text-wrapper.jsx +9 -1
- data/client/hippo/components/form/fields/textarea-wrapper.jsx +15 -0
- data/client/hippo/components/form/wrapper.jsx +21 -1
- data/client/hippo/components/help.jsx +21 -0
- data/client/hippo/components/master-detail.jsx +2 -0
- data/client/hippo/components/network-activity-overlay.jsx +3 -1
- data/client/hippo/components/popout-window.jsx +126 -0
- data/client/hippo/components/query-builder.jsx +9 -117
- data/client/hippo/components/query-builder/boolean-picker.jsx +28 -0
- data/client/hippo/components/query-builder/clause-filter.jsx +58 -0
- data/client/hippo/components/query-builder/clause.jsx +98 -0
- data/client/hippo/components/query-builder/date-picker.jsx +23 -0
- data/client/hippo/components/query-builder/query-builder.scss +7 -0
- data/client/hippo/components/record-finder.jsx +2 -0
- data/client/hippo/components/record-finder/query-layer.jsx +5 -1
- data/client/hippo/components/save-button.jsx +7 -1
- data/client/hippo/components/screen.jsx +2 -0
- data/client/hippo/components/text-editor.jsx +4 -5
- data/client/hippo/components/text-editor/display-modes/Button.jsx +5 -2
- data/client/hippo/components/text-editor/display-modes/ToggleEdit.jsx +2 -1
- data/client/hippo/components/text-editor/display-modes/ToggleInsert.jsx +2 -1
- data/client/hippo/components/text-editor/display-modes/ToggleLayout.jsx +2 -1
- data/client/hippo/components/text-editor/display-modes/TogglePreview.jsx +2 -1
- data/client/hippo/components/text-editor/display-modes/ToggleResize.jsx +2 -1
- data/client/hippo/components/text-editor/display-modes/index.js +7 -7
- data/client/hippo/components/text-editor/image-plugin/Component/Display/index.js +4 -2
- data/client/hippo/components/text-editor/image-plugin/Component/Form/index.js +2 -0
- data/client/hippo/components/text-editor/renderer.jsx +2 -0
- data/client/hippo/components/text-editor/text-editor.scss +13 -4
- data/client/hippo/components/time-zone-select.jsx +2 -0
- data/client/hippo/components/tool-tip.jsx +2 -0
- data/client/hippo/extensions/base.js +2 -0
- data/client/hippo/extensions/hippo.js +2 -0
- data/client/hippo/lib/__mocks__/request-assets.js +1 -2
- data/client/hippo/lib/bootstrap.js +2 -0
- data/client/hippo/lib/computed-properties.js +24 -0
- data/client/hippo/lib/date-range.js +13 -2
- data/client/hippo/lib/request-assets.js +3 -2
- data/client/hippo/lib/smooth-scroll.js +2 -0
- data/client/hippo/lib/util.js +1 -2
- data/client/hippo/models/asset.js +2 -0
- data/client/hippo/models/base.js +5 -2
- data/client/hippo/models/collection.js +2 -0
- data/client/hippo/models/config.js +3 -1
- data/client/hippo/models/pub_sub.js +2 -0
- data/client/hippo/models/pub_sub/channel.js +2 -0
- data/client/hippo/models/pub_sub/map.js +2 -0
- data/client/hippo/models/query.js +21 -12
- data/client/hippo/models/query/array-result.js +52 -16
- data/client/hippo/models/query/clause.js +11 -4
- data/client/hippo/models/query/field.js +2 -1
- data/client/hippo/models/query/info.js +7 -1
- data/client/hippo/models/query/operator.js +2 -0
- data/client/hippo/models/query/result.js +2 -0
- data/client/hippo/models/query/types.js +4 -0
- data/client/hippo/models/sync.js +2 -0
- data/client/hippo/models/system-setting.js +1 -15
- data/client/hippo/models/tenant.js +23 -7
- data/client/hippo/react/Root.jsx +2 -0
- data/client/hippo/react/component-not-found.jsx +2 -0
- data/client/hippo/screens/definition.js +2 -0
- data/client/hippo/screens/group.js +2 -0
- data/client/hippo/screens/instance.js +5 -1
- data/client/hippo/screens/system-settings.jsx +19 -5
- data/client/hippo/screens/system-settings/mailer-config.jsx +4 -2
- data/client/hippo/screens/system-settings/system-settings.scss +1 -1
- data/client/hippo/screens/system-settings/tenant.jsx +4 -2
- data/client/hippo/screens/user-management.jsx +2 -0
- data/client/hippo/screens/user-management/edit-form.jsx +2 -0
- data/client/hippo/testing/screens.js +10 -2
- data/client/hippo/user.js +4 -0
- data/client/hippo/workspace/index.jsx +3 -1
- data/client/hippo/workspace/menu-group.jsx +2 -0
- data/client/hippo/workspace/menu-option.jsx +2 -0
- data/client/hippo/workspace/menu.jsx +6 -3
- data/client/hippo/workspace/navbar.jsx +2 -0
- data/client/hippo/workspace/screen.jsx +2 -0
- data/client/hippo/workspace/styles.scss +5 -1
- data/command-reference-files/initial/.eslintrc.js +0 -3
- data/command-reference-files/initial/Gemfile +1 -1
- data/config/routes.rb +3 -1
- data/db/seed.rb +1 -1
- data/hippo-fw.gemspec +2 -2
- data/lib/hippo/api/controller_base.rb +2 -1
- data/lib/hippo/api/handlers/asset.rb +3 -2
- data/lib/hippo/api/handlers/tenant.rb +4 -6
- data/lib/hippo/api/helper_methods.rb +5 -7
- data/lib/hippo/api/route_set.rb +3 -2
- data/lib/hippo/asset.rb +4 -0
- data/lib/hippo/command/console.rb +1 -1
- data/lib/hippo/concerns/asset_uploader.rb +9 -4
- data/lib/hippo/concerns/queries.rb +3 -3
- data/lib/hippo/extension.rb +14 -4
- data/lib/hippo/extension/definition.rb +5 -1
- data/lib/hippo/logger.rb +26 -27
- data/lib/hippo/mailer.rb +19 -7
- data/lib/hippo/system_settings.rb +10 -4
- data/lib/hippo/templates/liquid.rb +1 -0
- data/lib/hippo/templates/liquid/pluralize.rb +16 -0
- data/lib/hippo/tenant.rb +17 -1
- data/lib/hippo/user.rb +10 -9
- data/lib/hippo/version.rb +1 -1
- data/lib/hippo/webpack.rb +22 -2
- data/package-lock.json +5462 -883
- data/package.json +9 -11
- data/spec/client/access/login-dialog.spec.jsx +2 -2
- data/spec/client/components/__snapshots__/query-builder.spec.jsx.snap +1 -1
- data/spec/client/components/__snapshots__/record-finder.spec.jsx.snap +1 -1
- data/spec/client/components/master-detail.spec.jsx +2 -1
- data/spec/client/components/query-builder.spec.jsx +3 -3
- data/spec/client/extension/base.spec.js +2 -0
- data/spec/client/models/base.spec.js +5 -3
- data/spec/client/models/query.spec.js +18 -6
- data/spec/client/models/sync.spec.js +2 -1
- data/spec/client/models/system-setting.spec.js +0 -12
- data/spec/client/screens/user-management.spec.jsx +1 -2
- data/spec/client/test-models.js +10 -0
- data/spec/server/api/tenant_change_spec.rb +1 -2
- data/spec/server/asset_spec.rb +2 -2
- data/templates/js/config-data.js +1 -1
- data/templates/spec/factories/model.rb +1 -1
- data/views/hippo_root_view.erb +1 -3
- metadata +17 -6
- data/client/hippo/components/text-editor/display-modes/SaveState.jsx +0 -17
@@ -6,6 +6,7 @@ import { get, find } from 'lodash';
|
|
6
6
|
|
7
7
|
@observer
|
8
8
|
export default class SelectFieldWrapper extends React.PureComponent {
|
9
|
+
|
9
10
|
@action.bound
|
10
11
|
onSelectChange({ value: { id } }) {
|
11
12
|
const ev = { target: { value: id } };
|
@@ -27,4 +28,5 @@ export default class SelectFieldWrapper extends React.PureComponent {
|
|
27
28
|
/>
|
28
29
|
);
|
29
30
|
}
|
31
|
+
|
30
32
|
}
|
@@ -0,0 +1,46 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import { isEmpty, isArray } from 'lodash';
|
3
|
+
import { observer } from 'mobx-react';
|
4
|
+
import { action, isObservableArray } from 'mobx';
|
5
|
+
import Tags from 'react-tag-autocomplete';
|
6
|
+
|
7
|
+
@observer
|
8
|
+
export default class CheckBoxWrapper extends React.PureComponent {
|
9
|
+
|
10
|
+
get tags() {
|
11
|
+
if (isEmpty(this.props.value)) {
|
12
|
+
return [];
|
13
|
+
} else if (!isArray(this.props.value) &&
|
14
|
+
!isObservableArray(this.props.value)) {
|
15
|
+
return [this.props.value];
|
16
|
+
}
|
17
|
+
return this.props.value;
|
18
|
+
}
|
19
|
+
|
20
|
+
@action.bound
|
21
|
+
onTagDelete(tagIndex) {
|
22
|
+
const { tags } = this;
|
23
|
+
tags.splice(tagIndex, 1);
|
24
|
+
this.props.onChange({ target: { value: tags } });
|
25
|
+
}
|
26
|
+
|
27
|
+
@action.bound onTagAdd(tag) {
|
28
|
+
const { tags } = this;
|
29
|
+
tags.push(tag);
|
30
|
+
this.props.onChange({ target: { value: tags } });
|
31
|
+
}
|
32
|
+
|
33
|
+
render() {
|
34
|
+
const { value: _, ...otherProps } = this.props;
|
35
|
+
|
36
|
+
return (
|
37
|
+
<Tags
|
38
|
+
handleDelete={this.onTagDelete}
|
39
|
+
handleAddition={this.onTagAdd}
|
40
|
+
tags={isObservableArray(this.tags) ? this.tags.peek() : this.tags}
|
41
|
+
{...otherProps}
|
42
|
+
/>
|
43
|
+
);
|
44
|
+
}
|
45
|
+
|
46
|
+
}
|
@@ -6,15 +6,23 @@ import TextInput from 'grommet/components/TextInput';
|
|
6
6
|
|
7
7
|
@observer
|
8
8
|
export default class TextWrapper extends React.PureComponent {
|
9
|
+
|
9
10
|
@action.bound focus() {
|
10
11
|
this.inputRef.componentRef.focus();
|
11
12
|
}
|
13
|
+
|
14
|
+
@action.bound onSelect({ suggestion }) {
|
15
|
+
this.props.onChange({ target: { value: suggestion } });
|
16
|
+
}
|
17
|
+
|
12
18
|
render() {
|
13
19
|
return (
|
14
20
|
<TextInput
|
15
|
-
ref={f =>
|
21
|
+
ref={(f) => { this.inputRef = f; }}
|
22
|
+
onSelect={this.onSelect}
|
16
23
|
{...this.props} onDOMChange={this.props.onChange}
|
17
24
|
/>
|
18
25
|
);
|
19
26
|
}
|
27
|
+
|
20
28
|
}
|
@@ -3,15 +3,18 @@ import PropTypes from 'prop-types';
|
|
3
3
|
import { PropTypes as MobxPropTypes, Provider, observer } from 'mobx-react';
|
4
4
|
import { observePubSub } from '../../models/pub_sub';
|
5
5
|
import { FormState } from './api';
|
6
|
+
import Screen from '../screen';
|
6
7
|
|
7
8
|
@observer
|
8
9
|
export default class FormWrapper extends React.PureComponent {
|
10
|
+
|
9
11
|
static propTypes = {
|
10
12
|
tag: PropTypes.string,
|
11
13
|
className: PropTypes.string,
|
12
14
|
children: PropTypes.node.isRequired,
|
13
15
|
state: PropTypes.instanceOf(FormState),
|
14
16
|
model: MobxPropTypes.observableObject,
|
17
|
+
screen: PropTypes.instanceOf(Screen.Instance),
|
15
18
|
}
|
16
19
|
|
17
20
|
static get defaultProps() {
|
@@ -39,7 +42,9 @@ export default class FormWrapper extends React.PureComponent {
|
|
39
42
|
}
|
40
43
|
|
41
44
|
renderTagged() {
|
42
|
-
const {
|
45
|
+
const {
|
46
|
+
tag: Tag, state, children, model: _, ...otherProps
|
47
|
+
} = this.props;
|
43
48
|
return (
|
44
49
|
<Provider formState={state}>
|
45
50
|
<Tag {...otherProps}>
|
@@ -49,8 +54,23 @@ export default class FormWrapper extends React.PureComponent {
|
|
49
54
|
);
|
50
55
|
}
|
51
56
|
|
57
|
+
renderScreen() {
|
58
|
+
const {
|
59
|
+
tag: _, state, children, screen, model: __, ...otherProps
|
60
|
+
} = this.props;
|
61
|
+
return (
|
62
|
+
<Provider formState={state}>
|
63
|
+
<Screen screen={screen} {...otherProps}>
|
64
|
+
{children}
|
65
|
+
</Screen>
|
66
|
+
</Provider>
|
67
|
+
);
|
68
|
+
}
|
69
|
+
|
52
70
|
render() {
|
53
71
|
if (this.props.model) { observePubSub(this.props.model); }
|
72
|
+
if (this.props.screen) { return this.renderScreen(); }
|
54
73
|
return this.props.tag ? this.renderTagged() : this.renderTagless();
|
55
74
|
}
|
75
|
+
|
56
76
|
}
|
@@ -0,0 +1,21 @@
|
|
1
|
+
import React from 'react'; // eslint-disable-line no-unused-vars
|
2
|
+
import PropTypes from 'prop-types';
|
3
|
+
import { Tooltip } from 'react-tippy';
|
4
|
+
import 'react-tippy/dist/tippy.css';
|
5
|
+
import CircleQuestionIcon from 'grommet/components/icons/base/CircleQuestion';
|
6
|
+
|
7
|
+
export default function Help({ message, ...ttProps }) {
|
8
|
+
return (
|
9
|
+
<Tooltip
|
10
|
+
arrow
|
11
|
+
title={message}
|
12
|
+
{...ttProps}
|
13
|
+
>
|
14
|
+
<CircleQuestionIcon colorIndex="brand" />
|
15
|
+
</Tooltip>
|
16
|
+
);
|
17
|
+
}
|
18
|
+
|
19
|
+
Help.propTypes = {
|
20
|
+
message: PropTypes.string.isRequired,
|
21
|
+
};
|
@@ -11,6 +11,7 @@ const DELAY_TIME = 500;
|
|
11
11
|
|
12
12
|
@observer
|
13
13
|
export default class MasterDetail extends React.PureComponent {
|
14
|
+
|
14
15
|
static propTypes = {
|
15
16
|
master: PropTypes.element.isRequired,
|
16
17
|
detail: PropTypes.element,
|
@@ -61,4 +62,5 @@ export default class MasterDetail extends React.PureComponent {
|
|
61
62
|
</div>
|
62
63
|
);
|
63
64
|
}
|
65
|
+
|
64
66
|
}
|
@@ -14,6 +14,7 @@ function Indicator({ error }) {
|
|
14
14
|
|
15
15
|
@observer
|
16
16
|
export default class NetworkActivityOverlay extends React.PureComponent {
|
17
|
+
|
17
18
|
static defaultProps = {
|
18
19
|
timeout: 30000,
|
19
20
|
errorTimeout: 2000,
|
@@ -85,7 +86,7 @@ export default class NetworkActivityOverlay extends React.PureComponent {
|
|
85
86
|
|
86
87
|
get message() {
|
87
88
|
const { props: { model } } = this;
|
88
|
-
let message = this.props
|
89
|
+
let { message } = this.props;
|
89
90
|
if (!message) {
|
90
91
|
if (this.hasError) {
|
91
92
|
message = model.errorMessage || 'Error';
|
@@ -121,4 +122,5 @@ export default class NetworkActivityOverlay extends React.PureComponent {
|
|
121
122
|
</div>
|
122
123
|
);
|
123
124
|
}
|
125
|
+
|
124
126
|
}
|
@@ -0,0 +1,126 @@
|
|
1
|
+
// inspiration from https://github.com/JakeGinnivan/react-popout
|
2
|
+
// this version triggers popup directly and leaves it open
|
3
|
+
// after the parent component unmounts
|
4
|
+
|
5
|
+
import React from 'react';
|
6
|
+
import PropTypes from 'prop-types';
|
7
|
+
import ReactDOM from 'react-dom';
|
8
|
+
import { isFunction, invoke, defaults, map, uniqueId } from 'lodash';
|
9
|
+
import { autobind } from 'core-decorators';
|
10
|
+
|
11
|
+
export default class PopoutWindow extends React.PureComponent {
|
12
|
+
|
13
|
+
static propTypes = {
|
14
|
+
title: PropTypes.string.isRequired,
|
15
|
+
children: PropTypes.node.isRequired,
|
16
|
+
onClose: PropTypes.func.isRequired,
|
17
|
+
url: PropTypes.string,
|
18
|
+
options: PropTypes.object,
|
19
|
+
windowImpl: PropTypes.shape({
|
20
|
+
open: PropTypes.func,
|
21
|
+
}),
|
22
|
+
};
|
23
|
+
|
24
|
+
static defaultProps = {
|
25
|
+
windowImpl: window,
|
26
|
+
url: 'about:blank',
|
27
|
+
};
|
28
|
+
|
29
|
+
containerId = uniqueId('popout-container');
|
30
|
+
|
31
|
+
popup = false;
|
32
|
+
|
33
|
+
defaultOptions = {
|
34
|
+
toolbar: 'no',
|
35
|
+
location: 'no',
|
36
|
+
directories: 'no',
|
37
|
+
status: 'no',
|
38
|
+
menubar: 'no',
|
39
|
+
scrollbars: 'yes',
|
40
|
+
resizable: 'yes',
|
41
|
+
width: 500,
|
42
|
+
height: 400,
|
43
|
+
top: (o, w) => ((w.innerHeight - o.height) / 2) + w.screenY,
|
44
|
+
left: (o, w) => ((w.innerWidth - o.width) / 2) + w.screenX,
|
45
|
+
};
|
46
|
+
|
47
|
+
|
48
|
+
componentDidMount() {
|
49
|
+
// May not exist if server-side rendering
|
50
|
+
if (this.props.windowImpl) {
|
51
|
+
this.open();
|
52
|
+
}
|
53
|
+
}
|
54
|
+
|
55
|
+
componentWillReceiveProps(nextProps) {
|
56
|
+
// re-render
|
57
|
+
if (this.isOpen && nextProps.children !== this.props.children) {
|
58
|
+
this.reRender();
|
59
|
+
}
|
60
|
+
}
|
61
|
+
|
62
|
+
get isOpen() {
|
63
|
+
return !!(this.popup && this.containerEl);
|
64
|
+
}
|
65
|
+
|
66
|
+
get containerEl() {
|
67
|
+
return this.popup.document.getElementById(this.containerId);
|
68
|
+
}
|
69
|
+
|
70
|
+
reRender(children = this.props.children) {
|
71
|
+
ReactDOM.render(children, this.containerEl);
|
72
|
+
}
|
73
|
+
|
74
|
+
open() {
|
75
|
+
if (this.isOpen) {
|
76
|
+
this.reRender();
|
77
|
+
this.popup.focus();
|
78
|
+
return;
|
79
|
+
}
|
80
|
+
// eslint-disable-next-line
|
81
|
+
const options = map(
|
82
|
+
defaults({}, this.props.options, this.defaultOptions),
|
83
|
+
(v, key, o) => `${key}=${v = isFunction(v) ? v(o, this.props.windowImpl) : v}`, // eslint-disable-line
|
84
|
+
).join(',');
|
85
|
+
|
86
|
+
this.popup = this.props.windowImpl.open(this.props.url, this.props.title, options);
|
87
|
+
|
88
|
+
this.popup.onbeforeunload = this.onWindowBeforeUnLoad;
|
89
|
+
this.popup.onload = this.onWindowLoad;
|
90
|
+
|
91
|
+
// just in case onload fails to fire
|
92
|
+
if ('complete' === this.popup.document.readyState) {
|
93
|
+
this.onWindowLoad();
|
94
|
+
}
|
95
|
+
}
|
96
|
+
|
97
|
+
@autobind
|
98
|
+
close() {
|
99
|
+
invoke(this.popup, 'close');
|
100
|
+
}
|
101
|
+
|
102
|
+
@autobind
|
103
|
+
onWindowBeforeUnLoad() {
|
104
|
+
invoke(this.props, 'onClose');
|
105
|
+
ReactDOM.unmountComponentAtNode(this.containerEl);
|
106
|
+
this.containerEl.parentNode.removeChild(this.containerEl);
|
107
|
+
}
|
108
|
+
|
109
|
+
@autobind
|
110
|
+
onWindowLoad() {
|
111
|
+
// called twice
|
112
|
+
if (this.containerEl) { return; }
|
113
|
+
|
114
|
+
this.popup.document.title = this.props.title;
|
115
|
+
const container = this.popup.document.createElement('div');
|
116
|
+
container.id = this.containerId;
|
117
|
+
this.popup.document.body.appendChild(container);
|
118
|
+
|
119
|
+
ReactDOM.render(this.props.children, container);
|
120
|
+
}
|
121
|
+
|
122
|
+
render() {
|
123
|
+
return null; // don't need to render anything directly
|
124
|
+
}
|
125
|
+
|
126
|
+
}
|
@@ -1,125 +1,14 @@
|
|
1
1
|
import React from 'react';
|
2
|
-
|
3
2
|
import PropTypes from 'prop-types';
|
4
3
|
import { find } from 'lodash';
|
5
4
|
import { observer } from 'mobx-react';
|
6
|
-
import
|
7
|
-
import
|
8
|
-
import
|
9
|
-
import RadioButton from 'grommet/components/RadioButton';
|
10
|
-
import TextInput from 'grommet/components/TextInput';
|
11
|
-
import DownIcon from 'grommet/components/icons/base/Down';
|
12
|
-
import { BaseModel } from '../models/base';
|
13
|
-
import Query from '../models/query';
|
14
|
-
import ClauseModel from '../models/query/clause';
|
15
|
-
|
16
|
-
@observer
|
17
|
-
class Radio extends React.PureComponent {
|
18
|
-
static propTypes = {
|
19
|
-
name: PropTypes.string.isRequired,
|
20
|
-
onSelect: PropTypes.func.isRequired,
|
21
|
-
model: PropTypes.instanceOf(BaseModel).isRequired,
|
22
|
-
clause: PropTypes.instanceOf(ClauseModel).isRequired,
|
23
|
-
}
|
24
|
-
|
25
|
-
@action.bound
|
26
|
-
onChange() {
|
27
|
-
this.props.clause[this.props.name] = this.props.model;
|
28
|
-
this.props.onSelect();
|
29
|
-
}
|
30
|
-
|
31
|
-
render() {
|
32
|
-
const { model } = this.props;
|
33
|
-
return (
|
34
|
-
<RadioButton
|
35
|
-
id={model.id}
|
36
|
-
name={model.id}
|
37
|
-
label={model.label}
|
38
|
-
onChange={this.onChange}
|
39
|
-
checked={(this.props.clause[this.props.name] === model)}
|
40
|
-
/>
|
41
|
-
);
|
42
|
-
}
|
43
|
-
}
|
44
|
-
|
45
|
-
@observer
|
46
|
-
class ClauseFilter extends React.PureComponent {
|
47
|
-
static propTypes = {
|
48
|
-
clause: PropTypes.instanceOf(ClauseModel).isRequired,
|
49
|
-
}
|
50
|
-
|
51
|
-
render() {
|
52
|
-
const { clause } = this.props;
|
53
|
-
return (
|
54
|
-
<span>
|
55
|
-
<DownIcon /> {clause.field.label} {clause.operator.label}
|
56
|
-
</span>
|
57
|
-
);
|
58
|
-
}
|
59
|
-
}
|
60
|
-
|
61
|
-
@observer
|
62
|
-
class Clause extends React.PureComponent {
|
63
|
-
static propTypes = {
|
64
|
-
clause: PropTypes.instanceOf(ClauseModel).isRequired,
|
65
|
-
}
|
66
|
-
|
67
|
-
@action.bound
|
68
|
-
onSelect() {
|
69
|
-
this.menuRef._onClose();
|
70
|
-
}
|
71
|
-
|
72
|
-
@action.bound
|
73
|
-
onValueChange(ev) {
|
74
|
-
this.props.clause.value = ev.target.value;
|
75
|
-
}
|
76
|
-
|
77
|
-
@action.bound
|
78
|
-
setMenuRef(ref) {
|
79
|
-
this.menuRef = ref;
|
80
|
-
}
|
81
|
-
|
82
|
-
render() {
|
83
|
-
const { clause } = this.props;
|
84
|
-
return (
|
85
|
-
<Box direction='row' pad={{ between: 'small' }}>
|
86
|
-
<Menu
|
87
|
-
ref={this.setMenuRef}
|
88
|
-
size='large'
|
89
|
-
pad='small'
|
90
|
-
closeOnClick={false} icon={<ClauseFilter clause={clause} />}
|
91
|
-
>
|
92
|
-
<Box direction='row' pad="small">
|
93
|
-
<Box size="small" pad={{ between: 'small' }}>
|
94
|
-
{clause.query.info.queryableFields.map(f =>
|
95
|
-
<Radio
|
96
|
-
key={f.id} model={f} clause={clause}
|
97
|
-
onSelect={this.onSelect} name='field' />,
|
98
|
-
)}
|
99
|
-
</Box>
|
100
|
-
<Box size="small" pad={{ between: 'small' }}>
|
101
|
-
{clause.validOperators.map(o =>
|
102
|
-
<Radio
|
103
|
-
key={o.id} model={o} clause={clause}
|
104
|
-
onSelect={this.onSelect} name='operator'
|
105
|
-
/>,
|
106
|
-
)}
|
107
|
-
</Box>
|
108
|
-
</Box>
|
109
|
-
</Menu>
|
110
|
-
<TextInput
|
111
|
-
autoFocus
|
112
|
-
style={{ flex: 1 }}
|
113
|
-
value={clause.value || ''}
|
114
|
-
onDOMChange={this.onValueChange}
|
115
|
-
/>
|
116
|
-
</Box>
|
117
|
-
);
|
118
|
-
}
|
119
|
-
}
|
5
|
+
import Query from '../models/query';
|
6
|
+
import Clause from './query-builder/clause';
|
7
|
+
import './query-builder/query-builder.scss';
|
120
8
|
|
121
9
|
@observer
|
122
10
|
export default class QueryBuilder extends React.PureComponent {
|
11
|
+
|
123
12
|
static defaultProps = {
|
124
13
|
autoFetch: false,
|
125
14
|
}
|
@@ -127,6 +16,7 @@ export default class QueryBuilder extends React.PureComponent {
|
|
127
16
|
static propTypes = {
|
128
17
|
autoFetch: PropTypes.bool,
|
129
18
|
query: PropTypes.instanceOf(Query).isRequired,
|
19
|
+
typeHandlers: PropTypes.object,
|
130
20
|
}
|
131
21
|
|
132
22
|
constructor(props) {
|
@@ -151,11 +41,13 @@ export default class QueryBuilder extends React.PureComponent {
|
|
151
41
|
}
|
152
42
|
|
153
43
|
render() {
|
154
|
-
const { query } = this.props;
|
44
|
+
const { query, typeHandlers } = this.props;
|
155
45
|
return (
|
156
46
|
<div className="query-builder">
|
157
|
-
{query.info.visibleClauses.map(c =>
|
47
|
+
{query.info.visibleClauses.map(c =>
|
48
|
+
<Clause key={c.id} clause={c} typeHandlers={typeHandlers} />)}
|
158
49
|
</div>
|
159
50
|
);
|
160
51
|
}
|
52
|
+
|
161
53
|
}
|