hippo-fw 0.9.4 → 0.9.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (107) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +2 -3
  4. data/Gemfile +1 -1
  5. data/client/hippo/__mocks__/config.js +4 -4
  6. data/client/hippo/boot.jsx +1 -0
  7. data/client/hippo/components/asset.jsx +4 -4
  8. data/client/hippo/components/date-time.jsx +169 -0
  9. data/client/hippo/components/date-time.scss +38 -0
  10. data/client/hippo/components/date-time/calendar.jsx +115 -0
  11. data/client/hippo/components/date-time/date-time-drop.jsx +77 -0
  12. data/client/hippo/components/date-time/date-time.scss +157 -0
  13. data/client/hippo/components/date-time/time.jsx +120 -0
  14. data/client/hippo/components/form/fields/date-wrapper.jsx +4 -3
  15. data/client/hippo/components/form/model.js +0 -2
  16. data/client/hippo/components/text-editor.jsx +85 -0
  17. data/client/hippo/components/text-editor/display-modes/Button.jsx +17 -0
  18. data/client/hippo/components/text-editor/display-modes/SaveState.jsx +17 -0
  19. data/client/hippo/components/text-editor/display-modes/ToggleEdit.jsx +22 -0
  20. data/client/hippo/components/text-editor/display-modes/ToggleInsert.jsx +21 -0
  21. data/client/hippo/components/text-editor/display-modes/ToggleLayout.jsx +21 -0
  22. data/client/hippo/components/text-editor/display-modes/TogglePreview.jsx +21 -0
  23. data/client/hippo/components/text-editor/display-modes/ToggleResize.jsx +21 -0
  24. data/client/hippo/components/text-editor/display-modes/index.css +0 -0
  25. data/client/hippo/components/text-editor/display-modes/index.js +30 -0
  26. data/client/hippo/components/text-editor/image-plugin/Component/Display/index.js +80 -0
  27. data/client/hippo/components/text-editor/image-plugin/Component/Form/index.js +40 -0
  28. data/client/hippo/components/text-editor/image-plugin/Component/index.js +16 -0
  29. data/client/hippo/components/text-editor/image-plugin/Component/index.scss +0 -0
  30. data/client/hippo/components/text-editor/image-plugin/index.js +32 -0
  31. data/client/hippo/components/text-editor/image-plugin/index.scss +25 -0
  32. data/client/hippo/components/text-editor/plugins.js +22 -0
  33. data/client/hippo/components/text-editor/renderer.jsx +37 -0
  34. data/client/hippo/components/text-editor/text-editor.scss +49 -0
  35. data/client/hippo/extensions/base.js +5 -2
  36. data/client/hippo/extensions/hippo.js +5 -2
  37. data/client/hippo/extensions/index.js +9 -3
  38. data/client/hippo/lib/__mocks__/request-assets.js +2 -2
  39. data/client/hippo/lib/bootstrap.js +0 -1
  40. data/client/hippo/lib/smooth-scroll.js +0 -1
  41. data/client/hippo/lib/util.js +4 -4
  42. data/client/hippo/models/asset.js +43 -23
  43. data/client/hippo/models/base.js +1 -2
  44. data/client/hippo/models/config.js +0 -1
  45. data/client/hippo/models/decorators.js +3 -3
  46. data/client/hippo/models/pub_sub.js +2 -5
  47. data/client/hippo/models/pub_sub/channel.js +0 -1
  48. data/client/hippo/models/query.js +0 -1
  49. data/client/hippo/models/query/array-result.js +0 -1
  50. data/client/hippo/models/query/clause.js +0 -1
  51. data/client/hippo/models/query/field.js +0 -1
  52. data/client/hippo/models/query/info.js +0 -3
  53. data/client/hippo/models/query/operator.js +0 -2
  54. data/client/hippo/models/query/result.js +0 -1
  55. data/client/hippo/models/sync.js +1 -1
  56. data/client/hippo/models/system-setting.js +0 -2
  57. data/client/hippo/models/tenant.js +0 -1
  58. data/client/hippo/screens/definition.js +0 -2
  59. data/client/hippo/screens/group.js +0 -2
  60. data/client/hippo/screens/instance.js +0 -3
  61. data/client/hippo/screens/system-settings/mailer-config.jsx +0 -3
  62. data/client/hippo/styles/global.scss +3 -0
  63. data/client/hippo/testing/mocks/fetch.js +6 -6
  64. data/client/hippo/user.js +4 -4
  65. data/client/hippo/workspace/index.jsx +12 -5
  66. data/client/hippo/workspace/root-view.jsx +10 -0
  67. data/command-reference-files/initial/.gitignore +1 -0
  68. data/command-reference-files/initial/Gemfile +1 -1
  69. data/command-reference-files/initial/client/appy-app/extension.js +4 -0
  70. data/command-reference-files/initial/config/webpack.config.js +25 -23
  71. data/config/routes.rb +4 -1
  72. data/config/webpack.config.js +1 -2
  73. data/hippo-fw.gemspec +4 -2
  74. data/lib/hippo.rb +0 -1
  75. data/lib/hippo/api/handlers/asset.rb +9 -0
  76. data/lib/hippo/api/updates.rb +2 -2
  77. data/lib/hippo/configuration.rb +0 -4
  78. data/lib/hippo/db.rb +2 -2
  79. data/lib/hippo/{rails.rb → db/fake_rails.rb} +0 -0
  80. data/lib/hippo/extension.rb +1 -2
  81. data/lib/hippo/mailer.rb +1 -0
  82. data/lib/hippo/spec_helper.rb +1 -0
  83. data/lib/hippo/version.rb +1 -1
  84. data/package-lock.json +626 -68
  85. data/package.json +14 -13
  86. data/spec/client/access/login-dialog.spec.jsx +1 -0
  87. data/spec/client/components/asset.spec.jsx +1 -2
  88. data/spec/client/components/date-time.spec.jsx +20 -0
  89. data/spec/client/extension/base.spec.js +0 -2
  90. data/spec/client/models/base.spec.js +4 -4
  91. data/spec/client/models/query.spec.js +2 -2
  92. data/spec/client/models/sync.spec.js +1 -1
  93. data/spec/client/screens/system-settings.spec.jsx +2 -1
  94. data/spec/client/setup.js +3 -0
  95. data/spec/client/test-models.js +0 -2
  96. data/spec/server/api/tenant_change_spec.rb +1 -1
  97. data/spec/server/api/tenant_isolation_spec.rb +6 -3
  98. data/spec/server/concerns/api_path_spec.rb +2 -2
  99. data/templates/.gitignore +1 -0
  100. data/templates/client/extension.js +4 -0
  101. data/templates/config/webpack.config.js +25 -23
  102. metadata +48 -12
  103. data/client/hippo/lib/pub_sub.js +0 -34
  104. data/client/hippo/react/viewport-root.jsx +0 -44
  105. data/spec/client/components/__snapshots__/asset.spec.jsx.snap +0 -48
  106. data/spec/client/screens/__snapshots__/system-settings.spec.jsx.snap +0 -443
  107. data/templates/gitignore +0 -4
@@ -0,0 +1,157 @@
1
+ @import '../../styles/global';
2
+
3
+ .date-time-drop {
4
+ $slider-handle-size: 20px;
5
+ $slider-size: 4px;
6
+ $slider-fix: 20px;
7
+ .calendar {
8
+ header {
9
+ min-height: 50px;
10
+ }
11
+ .grid {
12
+ width: 100%;
13
+ padding: 12px;
14
+ }
15
+ table {
16
+ width: 100%;
17
+ border-collapse: collapse;
18
+ border-spacing: 0;
19
+ }
20
+ td {
21
+ padding: 8px 0;
22
+ text-align: center;
23
+ cursor: pointer;
24
+ }
25
+ thead {
26
+ td {
27
+ color: $secondary-text-color;
28
+ font-weight: normal;
29
+ }
30
+ }
31
+ tbody td {
32
+ color: #666666;
33
+
34
+ &:hover {
35
+ background-color: $hover-background-color;
36
+ color: $hover-text-color;
37
+ }
38
+ }
39
+ .current-day {
40
+ background-color: $brand-color;
41
+ color: $colored-text-color;
42
+ font-weight: 700;
43
+ }
44
+ .prev-month,
45
+ .next-month {
46
+ color: #999999;
47
+ }
48
+ }
49
+
50
+
51
+ .time-picker {
52
+ color: $background-color;
53
+ padding-top: 50px;
54
+
55
+ .showtime {
56
+ text-align: center;
57
+ }
58
+
59
+ .am-pm {
60
+ display: flex;
61
+ flex-direction: column;
62
+ margin-left: 10px;
63
+ }
64
+
65
+ .separater {
66
+ display: inline-block;
67
+ font-size: 32px;
68
+ font-weight: bold;
69
+ color: $inverse-background-color;
70
+ width: 32px;
71
+ height: 65px;
72
+ line-height: 65px;
73
+ text-align: center;
74
+ }
75
+
76
+ .time-text {
77
+ position: relative;
78
+ left: -10px;
79
+ font-size: 15px;
80
+ color: $inverse-background-color;
81
+ margin-top: 7px;
82
+ margin-bottom: 10px;
83
+ }
84
+
85
+ .sliders {
86
+ padding: 40px;
87
+ max-width: 500px;
88
+ margin: auto;
89
+ }
90
+
91
+ .minutes,
92
+ .hour {
93
+ width: 65px;
94
+ height: 65px;
95
+ padding: 0;
96
+ font-size: 38px;
97
+ background-color: $inverse-background-color;
98
+ border-radius: 3px;
99
+ text-align: center;
100
+ &:focus {
101
+ padding: 0;
102
+ }
103
+ }
104
+ }
105
+
106
+
107
+ .u-slider-time {
108
+ position: relative;
109
+ display: inline-block;
110
+ background-color: $control-background-color;
111
+ border-radius: 3px;
112
+ height: $slider-size;
113
+ width: 100%;
114
+ cursor: pointer;
115
+
116
+ .value {
117
+ position: absolute;
118
+ background-color: $brand-color;
119
+ border-radius: 3px;
120
+ top: 0;
121
+ height: 100%;
122
+ }
123
+
124
+ .handle {
125
+ position: absolute;
126
+ width: $slider-size;
127
+ height: $slider-size;
128
+
129
+ &:after {
130
+ position: relative;
131
+ display: block;
132
+ content: '';
133
+ top: -$slider-fix/2 - 2;
134
+ left: -($slider-size+$slider-fix)/2;
135
+ width: $slider-size + $slider-fix;
136
+ height: $slider-size + $slider-fix;
137
+ background-color: #fff;
138
+ border: 3px solid $brand-color; //inverse-background-color;
139
+ border-radius: 50%;
140
+ cursor: pointer;
141
+ }
142
+ }
143
+ }
144
+
145
+ .tab {
146
+ display: none;
147
+ &.active { display: block; }
148
+ }
149
+
150
+ .tabs {
151
+ > * { padding: 0.5rem; }
152
+ margin-bottom: 10px;
153
+ border-bottom: 1px solid;
154
+ .grommetux-control-icon { margin-right: 0.5rem; }
155
+ }
156
+
157
+ }
@@ -0,0 +1,120 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import cn from 'classnames';
4
+ import { inRange, get, isString } from 'lodash';
5
+ import { action, observable } from 'mobx';
6
+ import { observer } from 'mobx-react';
7
+ import InputSlider from 'react-input-slider';
8
+ import CheckBox from 'grommet/components/CheckBox';
9
+ import Box from 'grommet/components/Box';
10
+
11
+ const NON_DIGIT = /\D/;
12
+
13
+ function getDigits(ev) {
14
+ const val = get(ev, 'target.value', ev.x);
15
+ if (isString(val) && (!val || NON_DIGIT.test(val))) { return null; }
16
+ return parseInt(val, 10);
17
+ }
18
+
19
+ @observer
20
+ export default class TimeSelector extends React.PureComponent {
21
+
22
+ static propTypes = {
23
+ moment: PropTypes.object.isRequired,
24
+ onChange: PropTypes.func.isRequired,
25
+ minuteStep: PropTypes.number,
26
+ }
27
+
28
+ @observable isPM;
29
+ @observable hour;
30
+ @observable minutes;
31
+
32
+ componentWillMount() {
33
+ this.set(this.props);
34
+ }
35
+
36
+ componentWillReceiveProps(nextProps) {
37
+ this.set(nextProps);
38
+ }
39
+
40
+ set(props) {
41
+ this.isPM = !!(12 <= props.moment.hour());
42
+ this.hour = (12 <= props.moment.hour()) ? props.moment.hour() - 12 : props.moment.hour();
43
+ this.minutes = props.moment.minute();
44
+ }
45
+
46
+ @action.bound
47
+ onHourChange(ev) {
48
+ const d = getDigits(ev);
49
+ const m = this.props.moment;
50
+ if (inRange(d, 1, 13)) {
51
+ m.hours(this.isPM ? d + 12 : d);
52
+ this.hour = d;
53
+ } else {
54
+ this.hour = 0;
55
+ m.hours(0);
56
+ }
57
+ this.props.onChange(m);
58
+ }
59
+
60
+ @action.bound
61
+ onMinuteChange(ev) {
62
+ const d = getDigits(ev);
63
+ if (inRange(d, 0, 60)) {
64
+ const m = this.props.moment;
65
+ m.minutes(d);
66
+ this.props.onChange(m);
67
+ this.minutes = d;
68
+ } else {
69
+ this.minutes = 0;
70
+ }
71
+ }
72
+
73
+ @action.bound
74
+ toggleAMPM(ev) {
75
+ this.isPM = ev.target.checked;
76
+ this.props.moment.hours(this.isPM ? this.hour + 12 : this.hour);
77
+ this.props.onChange(this.props.moment);
78
+ }
79
+
80
+ render() {
81
+ const { hour, minutes } = this;
82
+
83
+ return (
84
+ <div className={cn('time-picker', this.props.className)}>
85
+ <Box direction='row' className="showtime" justify="center" align="center">
86
+ <input className="hour" value={hour} onChange={this.onHourChange} />
87
+ <span className="separater">:</span>
88
+ <input className="minutes" value={minutes} onChange={this.onMinuteChange} />
89
+ <CheckBox
90
+ toggle
91
+ className="am-pm"
92
+ label='AM / PM'
93
+ checked={this.isPM}
94
+ onChange={this.toggleAMPM}
95
+ />
96
+ </Box>
97
+
98
+ <div className="sliders">
99
+ <div className="time-text">Hours:</div>
100
+ <InputSlider
101
+ className="u-slider-time"
102
+ xmin={1}
103
+ xmax={12}
104
+ step={this.props.minuteStep}
105
+ x={hour}
106
+ onChange={this.onHourChange}
107
+ />
108
+ <div className="time-text">Minutes:</div>
109
+ <InputSlider
110
+ className="u-slider-time"
111
+ xmin={0}
112
+ xmax={59}
113
+ x={minutes || 0}
114
+ onChange={this.onMinuteChange}
115
+ />
116
+ </div>
117
+ </div>
118
+ );
119
+ }
120
+ }
@@ -3,11 +3,13 @@ import PropTypes from 'prop-types';
3
3
  import { observer } from 'mobx-react';
4
4
  import { action, observable } from 'mobx';
5
5
  import moment from 'moment';
6
- import DateTime from 'grommet/components/DateTime';
6
+ import DateTime from '../../date-time';
7
7
 
8
8
  @observer
9
9
  export default class DateWrapper extends React.PureComponent {
10
- static defaultProps = { format: 'M/D/YYYY h:mm a' }
10
+ static defaultProps = {
11
+ format: 'M/D/YYYY h:mm a'
12
+ }
11
13
  static childContextTypes = { onDropChange: PropTypes.func }
12
14
  @observable isSelecting;
13
15
  @observable dateValue;
@@ -29,7 +31,6 @@ export default class DateWrapper extends React.PureComponent {
29
31
  this.props.onChange({ target: { value: this.dateValue } });
30
32
  }
31
33
 
32
-
33
34
  @action.bound onBlur(ev) {
34
35
  this.dateValue = moment(ev.target.value, this.props.format).toDate();
35
36
  const event = { target: { value: this.dateValue } };
@@ -69,7 +69,6 @@ export class FormField {
69
69
 
70
70
 
71
71
  export class FormState {
72
-
73
72
  fields = observable.map();
74
73
 
75
74
  @action
@@ -136,5 +135,4 @@ export class FormState {
136
135
  this.fields.forEach((field, name) => (model[name] = field.value));
137
136
  return Promise.resolve(model);
138
137
  }
139
-
140
138
  }
@@ -0,0 +1,85 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { action, observable, toJS } from 'mobx';
4
+ import { isEmpty } from 'lodash';
5
+ import { observer } from 'mobx-react';
6
+ import { Provider, PropTypes as MobxPropTypes } from 'mobx-react';
7
+
8
+ // The editor core
9
+ import Editor, { Editable, createEmptyState } from 'ory-editor-core';
10
+
11
+ // Require our ui components (optional). You can implement and use your own ui too!
12
+ import { Trash, Toolbar } from 'ory-editor-ui';
13
+ import tapEventPlugin from 'react-tap-event-plugin';
14
+
15
+ import { plugins, defaultPlugin } from './text-editor/plugins';
16
+
17
+ import 'ory-editor-ui/lib/index.css';
18
+ import 'ory-editor-core/lib/index.css';
19
+
20
+ import DisplayModeToggle from './text-editor/display-modes';
21
+ import './text-editor/text-editor.scss';
22
+
23
+
24
+ @observer
25
+ export default class TextEditor extends React.PureComponent {
26
+
27
+ static defaultProps = {
28
+ assets: observable.array(),
29
+ }
30
+
31
+ static propTypes = {
32
+ defaultContent: PropTypes.object,
33
+ assets: MobxPropTypes.observableArray,
34
+ onComplete: PropTypes.func.isRequired,
35
+ }
36
+
37
+ componentWillMount() {
38
+ const content = toJS(this.props.defaultContent);
39
+ this.content = isEmpty(content) ? createEmptyState() : content;
40
+ this.editor = new Editor({
41
+ plugins,
42
+ editables: [this.content],
43
+ defaultPlugin,
44
+ });
45
+ tapEventPlugin();
46
+ }
47
+
48
+ @action.bound
49
+ onSave() {
50
+ this.props.onComplete({ content: this.content });
51
+ }
52
+
53
+ @action.bound
54
+ onEditStateChange(state) {
55
+ this.content = state;
56
+ }
57
+
58
+ render() {
59
+ return (
60
+ <Provider
61
+ assets={this.props.assets}
62
+ >
63
+ <div className="text-editor">
64
+ <DisplayModeToggle
65
+ editor={this.editor}
66
+ onSave={this.onSave}
67
+ >
68
+ {this.props.children}
69
+ </DisplayModeToggle>
70
+ <div className="text-editor-content">
71
+ <Editable
72
+ editor={this.editor}
73
+ id={this.content.id}
74
+ onAddImage={this.props.onAddImage}
75
+ onChange={this.onEditStateChange}
76
+ />
77
+ </div>
78
+ <Trash editor={this.editor}/>
79
+ <Toolbar editor={this.editor} />
80
+ </div>
81
+
82
+ </Provider>
83
+ );
84
+ }
85
+ }
@@ -0,0 +1,17 @@
1
+ import React from 'react'; // eslint-disable-line no-unused-vars
2
+ import Button from 'grommet/components/Button';
3
+
4
+ const DisplayModeButton = ({ label, icon, onClick, active, disabled }) => (
5
+ <span className={active ? 'active' : ''}>
6
+ <Button
7
+ plain
8
+ secondary={active}
9
+ onClick={onClick}
10
+ label={label}
11
+ disabled={disabled}
12
+ icon={icon}
13
+ />
14
+ </span>
15
+ );
16
+
17
+ export default DisplayModeButton;
@@ -0,0 +1,17 @@
1
+ import React from 'react';
2
+ import SaveIcon from 'grommet/components/icons/base/Save';
3
+ import Button from './Button';
4
+
5
+ export default class SaveState extends React.PureComponent {
6
+ render() {
7
+ const { props: { onSave } } = this;
8
+
9
+ return (
10
+ <Button
11
+ icon={<SaveIcon />}
12
+ label="Save"
13
+ onClick={onSave}
14
+ />
15
+ );
16
+ }
17
+ }
@@ -0,0 +1,22 @@
1
+ // @flow
2
+ import React from 'react'; // eslint-disable-line no-unused-vars
3
+ import { connect } from 'react-redux';
4
+ import { editMode } from 'ory-editor-core/lib/actions/display';
5
+ import { isEditMode } from 'ory-editor-core/lib/selector/display';
6
+ import { createStructuredSelector } from 'reselect';
7
+ import EditIcon from 'grommet/components/icons/base/Edit';
8
+ import Button from './Button';
9
+
10
+ const Inner = ({ isEditMode: isedm, editMode: edm }) => (
11
+ <Button
12
+ icon={<EditIcon />}
13
+ label="Edit"
14
+ active={isedm}
15
+ onClick={edm}
16
+ />
17
+ );
18
+
19
+ const mapStateToProps = createStructuredSelector({ isEditMode });
20
+ const mapDispatchToProps = { editMode };
21
+
22
+ export default connect(mapStateToProps, mapDispatchToProps)(Inner);