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,21 @@
1
+ import React from 'react'; // eslint-disable-line no-unused-vars
2
+ import AddIcon from 'grommet/components/icons/base/TableAdd';
3
+ import { connect } from 'react-redux';
4
+ import { insertMode } from 'ory-editor-core/lib/actions/display';
5
+ import { isInsertMode } from 'ory-editor-core/lib/selector/display';
6
+ import { createStructuredSelector } from 'reselect';
7
+ import Button from './Button';
8
+
9
+ const Inner = ({ isInsertMode: isinm, insertMode: inm }) => (
10
+ <Button
11
+ icon={<AddIcon />}
12
+ label="Add"
13
+ active={isinm}
14
+ onClick={inm}
15
+ />
16
+ );
17
+
18
+ const mapStateToProps = createStructuredSelector({ isInsertMode });
19
+ const mapDispatchToProps = { insertMode };
20
+
21
+ export default connect(mapStateToProps, mapDispatchToProps)(Inner);
@@ -0,0 +1,21 @@
1
+ import React from 'react'; // eslint-disable-line no-unused-vars
2
+ import GridIcon from 'grommet/components/icons/base/Grid';
3
+ import { connect } from 'react-redux';
4
+ import { layoutMode } from 'ory-editor-core/lib/actions/display';
5
+ import { isLayoutMode } from 'ory-editor-core/lib/selector/display';
6
+ import { createStructuredSelector } from 'reselect';
7
+ import Button from './Button';
8
+
9
+ const Inner = ({ isLayoutMode: islm, layoutMode: lm }) => (
10
+ <Button
11
+ icon={<GridIcon />}
12
+ label="Layout"
13
+ active={islm}
14
+ onClick={lm}
15
+ />
16
+ );
17
+
18
+ const mapStateToProps = createStructuredSelector({ isLayoutMode });
19
+ const mapDispatchToProps = { layoutMode };
20
+
21
+ export default connect(mapStateToProps, mapDispatchToProps)(Inner);
@@ -0,0 +1,21 @@
1
+ import React from 'react'; // eslint-disable-line no-unused-vars
2
+ import Devices from 'material-ui/svg-icons/device/devices';
3
+ import { connect } from 'react-redux';
4
+ import { previewMode } from 'ory-editor-core/lib/actions/display';
5
+ import { isPreviewMode } from 'ory-editor-core/lib/selector/display';
6
+ import { createStructuredSelector } from 'reselect';
7
+ import Button from './Button';
8
+
9
+ const Inner = ({ isPreviewMode: ispvm, previewMode: pvm }) => (
10
+ <Button
11
+ icon={<Devices />}
12
+ label="Preview"
13
+ active={ispvm}
14
+ onClick={pvm}
15
+ />
16
+ );
17
+
18
+ const mapStateToProps = createStructuredSelector({ isPreviewMode });
19
+ const mapDispatchToProps = { previewMode };
20
+
21
+ export default connect(mapStateToProps, mapDispatchToProps)(Inner);
@@ -0,0 +1,21 @@
1
+ import React from 'react'; // eslint-disable-line no-unused-vars
2
+ import ResizeIcon from 'grommet/components/icons/base/Pan';
3
+ import { connect } from 'react-redux';
4
+ import { resizeMode } from 'ory-editor-core/lib/actions/display';
5
+ import { isResizeMode } from 'ory-editor-core/lib/selector/display';
6
+ import { createStructuredSelector } from 'reselect';
7
+ import Button from './Button';
8
+
9
+ const Inner = ({ isResizeMode: isrsm, resizeMode: rsm }) => (
10
+ <Button
11
+ icon={<ResizeIcon />}
12
+ label="Resize"
13
+ active={isrsm}
14
+ onClick={rsm}
15
+ />
16
+ );
17
+
18
+ const mapStateToProps = createStructuredSelector({ isResizeMode });
19
+ const mapDispatchToProps = { resizeMode };
20
+
21
+ export default connect(mapStateToProps, mapDispatchToProps)(Inner);
@@ -0,0 +1,30 @@
1
+ import React from 'react'; // eslint-disable-line no-unused-vars
2
+ import Header from 'grommet/components/Header';
3
+ import Provider from 'ory-editor-ui/lib/Provider';
4
+ import ToggleEdit from './ToggleEdit';
5
+ import ToggleInsert from './ToggleInsert';
6
+ import ToggleLayout from './ToggleLayout';
7
+ import TogglePreview from './TogglePreview';
8
+ import ToggleResize from './ToggleResize';
9
+ import SaveState from './SaveState';
10
+
11
+ const Inner = (props: any) => (
12
+ <Provider {...props}>
13
+ <Header
14
+ wrap
15
+ margin="small"
16
+ justify="end"
17
+ pad={{ horizontal: 'small' }}
18
+ >
19
+ <ToggleInsert />
20
+ <ToggleEdit />
21
+ <ToggleLayout />
22
+ <ToggleResize />
23
+ <TogglePreview />
24
+ {props.children}
25
+ <SaveState onSave={props.onSave} />
26
+ </Header>
27
+ </Provider>
28
+ );
29
+
30
+ export default Inner;
@@ -0,0 +1,80 @@
1
+ // @flow
2
+ import React from 'react';
3
+ import { observer, inject } from 'mobx-react';
4
+ import { action, computed } from 'mobx';
5
+ import { find } from 'lodash';
6
+ import Dropzone from 'react-dropzone';
7
+ import Button from 'grommet/components/Button';
8
+ import ImageIcon from 'grommet/components/icons/base/Image';
9
+ import cn from 'classnames';
10
+
11
+ const iconStyle = {
12
+ width: '100%',
13
+ height: 'auto',
14
+ padding: '0',
15
+ color: '#aaa',
16
+ textAlign: 'center',
17
+ minWidth: 64,
18
+ minHeight: 64,
19
+ maxHeight: 256,
20
+ };
21
+
22
+
23
+ @inject('assets')
24
+ @observer
25
+ export default class Display extends React.PureComponent {
26
+ @computed get asset() {
27
+ return find(this.props.assets, asset =>
28
+ asset.metadata && asset.metadata.nodeId === this.props.id);
29
+ }
30
+
31
+ @action.bound
32
+ onFileDrop(files) {
33
+ let asset = this.asset;
34
+ if (!asset) {
35
+ this.props.assets.push({ metadata: { nodeId: this.props.id } });
36
+ asset = this.props.assets.get(this.props.assets.length - 1);
37
+ }
38
+
39
+ asset.setFile(files[0]).then(() => {
40
+ this.props.onChange({ src: asset.previewUrl });
41
+ asset.save().then(() => {
42
+ this.props.onChange({
43
+ assetId: asset.id, src: asset.urlFor(),
44
+ });
45
+ });
46
+ });
47
+ }
48
+
49
+ renderImage() {
50
+ const { state: { src } } = this.props;
51
+ return src ? <img className="content-image" src={src} /> : <ImageIcon style={iconStyle} />;
52
+ }
53
+
54
+ @action.bound
55
+ onAddClick() {
56
+ this.dropZone.open();
57
+ }
58
+
59
+ render() {
60
+ const { isEditMode, state, focused } = this.props;
61
+ if (isEditMode) {
62
+ return (
63
+ <div className={cn('content-image-wrapper', { focused })}>
64
+ <Button onClick={this.onAddClick}>{state.src ? 'Change' : 'Add'}</Button>
65
+ <Dropzone
66
+ ref={dz => (this.dropZone = dz)}
67
+ style={{}}
68
+ disableClick
69
+ multiple={false}
70
+ onDrop={this.onFileDrop}
71
+ className="image-drop-zone"
72
+ >
73
+ {this.renderImage()}
74
+ </Dropzone>
75
+ </div>
76
+ );
77
+ }
78
+ return this.renderImage();
79
+ }
80
+ }
@@ -0,0 +1,40 @@
1
+ // @flow
2
+ import React from 'react';
3
+ import { inject, observer } from 'mobx-react';
4
+ import Dropzone from 'react-dropzone';
5
+ import { action } from 'mobx';
6
+ import { BottomToolbar } from 'ory-editor-ui';
7
+
8
+ import Display from '../Display';
9
+
10
+ @inject('model', 'images_attribute')
11
+ @observer
12
+ export default class Form extends React.PureComponent {
13
+ get assets() {
14
+ return this.props.model[this.props.images_attribute];
15
+ }
16
+
17
+ @action.bound
18
+ onFileDrop(files) {
19
+ this.assets.push({});
20
+ const asset = this.assets.at(this.assets.length - 1);
21
+ asset.setFile(files[0]);
22
+ }
23
+
24
+ render() {
25
+ const { props } = this;
26
+
27
+ return (
28
+ <div>
29
+ <Display {...props} />
30
+ <BottomToolbar open={props.focused}>
31
+ <Dropzone
32
+ onDrop={this.onFileDrop}
33
+ >
34
+ Drop a file here, or click to select one to upload.
35
+ </Dropzone>
36
+ </BottomToolbar>
37
+ </div>
38
+ );
39
+ }
40
+ }
@@ -0,0 +1,16 @@
1
+ // @flow
2
+ import React from 'react'; // eslint-disable-line no-unused-vars
3
+ import getMuiTheme from 'material-ui/styles/getMuiTheme';
4
+ import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
5
+ import type { ContentPluginProps } from 'ory-editor-core/lib/service/plugin/classes';
6
+ import Display from './Display';
7
+
8
+ export type PropTypes = ContentPluginProps<{ src: string, caption: string }>
9
+
10
+ const Image = (props: PropTypes) => (
11
+ <MuiThemeProvider muiTheme={getMuiTheme()}>
12
+ <Display {...props} />
13
+ </MuiThemeProvider>
14
+ );
15
+
16
+ export default Image;
@@ -0,0 +1,32 @@
1
+ // @flow
2
+ import React from 'react'; // eslint-disable-line no-unused-vars
3
+ import Panorama from 'material-ui/svg-icons/image/panorama';
4
+ import './index.scss';
5
+ import Asset from '../../../models/asset';
6
+ import Component from './Component';
7
+
8
+ export default {
9
+ Component,
10
+ name: 'ory/editor/core/content/image',
11
+ version: '0.0.1',
12
+ IconComponent: <Panorama />,
13
+ text: 'Image',
14
+ isInlineable: true,
15
+ description: 'Loads an image from an url.',
16
+
17
+ handleRemoveHotKey(ev, node) {
18
+ if (node.content.state.assetId) {
19
+ const asset = new Asset({ id: node.content.state.assetId });
20
+ return asset.destroy();
21
+ }
22
+ return Promise.resolve(node);
23
+ },
24
+
25
+ // We need this because otherwise we lose hotkey focus on elements like spoilers.
26
+ // This could probably be solved in an easier way by listening to window.document?
27
+
28
+ handleFocus: (props: any, source: any, ref: HTMLElement) => {
29
+ if (!ref) { return; }
30
+ setTimeout(() => ref.focus());
31
+ },
32
+ };
@@ -0,0 +1,25 @@
1
+ @import 'hippo/styles/global';
2
+
3
+ .ory-plugins-content-image {
4
+ width: 100%;
5
+ }
6
+
7
+ .ory-plugins-content-image-placeholder {
8
+ position: relative;
9
+ width: 100%;
10
+ text-align: center;
11
+ }
12
+
13
+ .content-image-wrapper {
14
+ border: 1px solid rgba(0, 0, 0, 0.15);
15
+ cursor: pointer;
16
+ &.focused {
17
+ border-width: 2px;
18
+ border-color: $focus-border-color;
19
+ }
20
+ button {
21
+ position: absolute;
22
+ top: 1rem;
23
+ right: 1rem;
24
+ }
25
+ }
@@ -0,0 +1,22 @@
1
+ import spacer from 'ory-editor-plugins-spacer';
2
+ import slate from 'ory-editor-plugins-slate';
3
+ import video from 'ory-editor-plugins-video';
4
+ import parallax from 'ory-editor-plugins-parallax-background';
5
+ import 'ory-editor-plugins-parallax-background/lib/index.css';
6
+ import divider from 'ory-editor-plugins-divider';
7
+
8
+ import 'ory-editor-plugins-image/lib/index.css';
9
+ import 'ory-editor-plugins-spacer/lib/index.css';
10
+ import 'ory-editor-plugins-video/lib/index.css';
11
+ import 'ory-editor-plugins-slate/lib/index.css';
12
+
13
+ import image from './image-plugin';
14
+
15
+ const defaultPlugin = slate();
16
+
17
+ const plugins = {
18
+ content: [slate(), spacer, divider, image, video], // Define plugins for content cells
19
+ layout: [parallax({ defaultPlugin })],
20
+ };
21
+
22
+ export { defaultPlugin, plugins };
@@ -0,0 +1,37 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { plugins } from 'hippo/components/text-editor/plugins';
4
+ import { observable, toJS } from 'mobx';
5
+ import { observer, Provider, PropTypes as MobxPropTypes } from 'mobx-react';
6
+ import { HTMLRenderer } from 'ory-editor-renderer';
7
+
8
+ import 'ory-editor-core/lib/index.css';
9
+
10
+ @observer
11
+ export default class TextEditorRenderer extends React.PureComponent {
12
+ static defaultProps = {
13
+ assets: observable.array(),
14
+ }
15
+
16
+ static propTypes = {
17
+ content: PropTypes.object.isRequired,
18
+ assets: MobxPropTypes.observableArray,
19
+ }
20
+
21
+ static defaultProps = {
22
+ className: PropTypes.string,
23
+ }
24
+
25
+ render() {
26
+ return (
27
+ <Provider
28
+ assets={this.props.assets}
29
+ >
30
+ <HTMLRenderer
31
+ className={this.props.className} state={toJS(this.props.content)}
32
+ plugins={plugins}
33
+ />
34
+ </Provider>
35
+ );
36
+ }
37
+ }
@@ -0,0 +1,49 @@
1
+ @import 'hippo/styles/global';
2
+
3
+ .grommet .text-editor {
4
+
5
+ border: 1px solid #ddd;
6
+ flex: 1;
7
+ display: flex;
8
+ flex-direction: column;
9
+
10
+ .text-editor-content {
11
+ flex: 1;
12
+ }
13
+
14
+
15
+ .toolbar > * {
16
+ display: flex;
17
+ flex-wrap: wrap;
18
+ justify-content: center;
19
+ align-items: center;
20
+ }
21
+
22
+ p:not(.grommetux-paragraph) {
23
+ max-width: inherit;
24
+ margin-left: 0;
25
+ margin-top: 0;
26
+ margin-bottom: 0;
27
+ }
28
+
29
+ .content-image {
30
+ width: 100%;
31
+ height: auto;
32
+ }
33
+
34
+
35
+ button:not(.grommetux-button) {
36
+ min-width: inherit;
37
+ padding: 0;
38
+ border: 0;
39
+ display: flex;
40
+ justify-content: center;
41
+ align-items: center;
42
+ }
43
+
44
+ header {
45
+ .active {
46
+ border-bottom: 2px solid $focus-border-color;
47
+ }
48
+ }
49
+ }
@@ -1,12 +1,11 @@
1
1
  import { observable, computed } from 'mobx';
2
2
  import { titleize, humanize } from '../lib/util';
3
3
  import Extensions from './index';
4
+ import RootView from '../workspace/root-view';
4
5
 
5
6
  export { identifiedBy, identifier } from '../models/base';
6
7
 
7
-
8
8
  export class BaseExtension {
9
-
10
9
  @observable data;
11
10
 
12
11
  static register() {
@@ -24,4 +23,8 @@ export class BaseExtension {
24
23
  @computed get domain() {
25
24
  return window.location.hostname.split('.').slice(-2).join('.');
26
25
  }
26
+
27
+ rootView() {
28
+ return RootView;
29
+ }
27
30
  }