14 | );
15 | };
16 |
17 | it('should render string input', () => {
18 |
19 | let FieldType = renderFieldType({
20 | name: 'string',
21 | type: 'string'
22 | });
23 |
24 | let textarea = TestUtils.findRenderedDOMComponentWithTag(
25 | FieldType, 'textarea'
26 | );
27 |
28 | expect(textarea).toBeTruthy();
29 | });
30 |
31 | it('should render number input', () => {
32 |
33 | let FieldType = renderFieldType({
34 | name: 'number',
35 | type: 'number'
36 | });
37 |
38 | let input = TestUtils.findRenderedDOMComponentWithTag(
39 | FieldType, 'input'
40 | );
41 |
42 | expect(input).toBeTruthy();
43 | });
44 |
45 | it('should render bool input', () => {
46 |
47 | let FieldType = renderFieldType({
48 | name: 'bool',
49 | type: 'bool',
50 | defaultValue: false
51 | });
52 |
53 | let toggle = TestUtils.findRenderedDOMComponentWithClass(
54 | FieldType, 'atellier-toggle'
55 | );
56 |
57 | expect(toggle).toBeTruthy();
58 | });
59 |
60 | xit('should render array input', () => {
61 |
62 | let FieldType = renderFieldType({
63 | name: 'array',
64 | type: 'array'
65 | });
66 |
67 | let div = TestUtils.findRenderedDOMComponentWithClass(
68 | FieldType, 'properties-field'
69 | );
70 |
71 | expect(TestUtils.isElement(FieldType)).toBeFalsy();
72 | });
73 |
74 | xit('should render object input', () => {
75 |
76 | let FieldType = renderFieldType({
77 | name: 'object',
78 | type: 'object'
79 | });
80 |
81 | let div = TestUtils.findRenderedDOMComponentWithClass(
82 | FieldType, 'properties-field'
83 | );
84 |
85 | expect(TestUtils.isElement(FieldType)).toBeFalsy();
86 | });
87 |
88 | it('should handle input change', () => {
89 |
90 | let FieldType = renderFieldType({
91 | name: 'string',
92 | type: 'string'
93 | });
94 |
95 | let textarea = TestUtils.findRenderedDOMComponentWithTag(
96 | FieldType, 'textarea'
97 | );
98 |
99 | TestUtils.Simulate.change(textarea);
100 |
101 | });
102 | });
103 |
--------------------------------------------------------------------------------
/spec/PropTypesIdentifier.spec.jsx:
--------------------------------------------------------------------------------
1 | import { PropTypes } from 'react';
2 | import PropTypesIdentifier from '../lib/structural/PropTypesIdentifier.jsx';
3 |
4 | describe('PropTypesIdentifier', () => {
5 |
6 | it('should identifier proptype', () => {
7 | PropTypesIdentifier
8 | .create(PropTypes)
9 | .identify();
10 |
11 | expect(PropTypes.string.type).toBe('string');
12 | });
13 |
14 | it('should intercept oneOf proptype', () => {
15 | PropTypesIdentifier
16 | .create(PropTypes)
17 | .intercept(PropTypes.oneOf);
18 |
19 | let args = ['gato', 'cachorro', 'coelho'];
20 | let proptype = PropTypes.oneOf(args);
21 |
22 | expect(proptype.type).toBe('oneOf');
23 | expect(proptype.options).toBe(args);
24 | });
25 |
26 | });
27 |
--------------------------------------------------------------------------------
/spec/Sidebar.spec.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import TestUtils from 'react-addons-test-utils';
3 | import Immutable from 'immutable';
4 | import Sidebar from '../lib/Sidebar.jsx';
5 |
6 | describe('Sidebar', () => {
7 |
8 | class TestComponent extends React.Component {
9 | render() {
10 | return Test Component ;
11 | }
12 | };
13 |
14 | let componentItems = [
15 | {
16 | componentName: 'test component',
17 | component: TestComponent
18 | }
19 | ];
20 |
21 | let renderSideBar = (components=componentItems) => {
22 | let componentsList = Immutable.List(components);
23 | return TestUtils.renderIntoDocument(
24 |
25 | );
26 | };
27 |
28 | it('should render sidebar', () => {
29 | let sidebar = renderSideBar();
30 |
31 | let div = TestUtils.findRenderedDOMComponentWithClass(
32 | sidebar, 'sidebar'
33 | );
34 |
35 | expect(div).toBeTruthy();
36 | });
37 |
38 | it('should handle toggle sidebar', () => {
39 | let sidebar = renderSideBar();
40 |
41 | expect(sidebar.state.close).toBeFalsy();
42 |
43 | sidebar._handleToggleSidebar();
44 | expect(sidebar.state.close).toBeTruthy();
45 | });
46 |
47 | });
48 |
--------------------------------------------------------------------------------
/spec/Toggle.spec.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import TestUtils from 'react-addons-test-utils';
4 | import Toggle from '../lib/Toggle.jsx';
5 |
6 | describe('Toggle', function () {
7 |
8 | const renderToggle = function({ defaultValue, name, handleChange }) {
9 | return ReactDOM.findDOMNode(TestUtils.renderIntoDocument(
10 |
11 |
12 | {name}
13 |
14 | ));
15 | };
16 |
17 | it('should be selected if `defaultValue` property is `true`', function () {
18 | let domNode = renderToggle({ name: 'ToggleField', defaultValue: true });
19 | let toggle = domNode.querySelector('.atellier-toggle');
20 | expect(toggle.nodeName).toEqual('DIV');
21 | expect(domNode.querySelector('.atellier-toggle-checked')).toBeDefined();
22 | });
23 |
24 | it('should be unselected if `defaultValue` is `false', function () {
25 | let domNode = renderToggle({ name: 'ToggleField', defaultValue: false });
26 | let toggle = domNode.querySelector('.atellier-toggle');
27 | expect(toggle.nodeName).toEqual('DIV');
28 | expect(domNode.querySelector('.atellier-toggle-checked')).toBeNull();
29 | });
30 |
31 | it('should the `name` property match the label value', function () {
32 | let domNode = renderToggle({ name: 'ToggleField', defaultValue: false });
33 | let label = domNode.querySelector('label');
34 | let text = label.innerText || label.textContent;
35 | expect(text).toEqual('ToggleField');
36 | });
37 |
38 | it('should add/remove class `.atellier-toggle-checked` when toggled', function () {
39 | let domNode = renderToggle({ name: 'ToggleField', defaultValue: false });
40 | expect(domNode.querySelector('.atellier-toggle-checked')).toBeNull();
41 | let toggle = domNode.querySelector('.atellier-toggle');
42 | TestUtils.Simulate.click(toggle);
43 | expect(domNode.querySelector('.atellier-toggle-checked')).not.toBeNull();
44 | });
45 |
46 | it('should call `handleChange` callback when toggled', function () {
47 | let handleChange = function(){};
48 | let obj = { handleChange: handleChange };
49 | spyOn(obj, 'handleChange');
50 | let domNode = renderToggle({ name: 'ToggleField', defaultValue: false, handleChange: obj.handleChange });
51 | let toggle = domNode.querySelector('.atellier-toggle');
52 | TestUtils.Simulate.click(toggle);
53 | expect(obj.handleChange.calls.count()).toBe(1);
54 | });
55 |
56 | });
57 |
--------------------------------------------------------------------------------
/spec/Workspace.spec.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import TestUtils from 'react-addons-test-utils';
3 | import Workspace from '../lib/Workspace.jsx';
4 |
5 | describe('Workspace', () => {
6 |
7 | class TestComponent extends React.Component {
8 |
9 | static propTypes = {
10 | text: PropTypes.string
11 | };
12 |
13 | render() {
14 | return (
15 | { this.props.text }
16 | );
17 | }
18 | }
19 |
20 | let handleCloseProperties = () => {
21 | return null;
22 | };
23 |
24 | let renderWorkspace = ({ component, onCloseProperties=handleCloseProperties }) => {
25 | return TestUtils.renderIntoDocument(
26 |
27 | );
28 | };
29 |
30 | it('should render workspace', () => {
31 |
32 | let workspace = renderWorkspace({
33 | component: {
34 | component: TestComponent,
35 | componentName: 'TestComponent'
36 | }
37 | });
38 |
39 | let div = TestUtils.findRenderedDOMComponentWithClass(
40 | workspace, 'workspace'
41 | );
42 |
43 | expect(div).toBeTruthy();
44 | });
45 |
46 | it('should render workspace', () => {
47 |
48 | let workspace = renderWorkspace({
49 | component: {
50 | component: TestComponent,
51 | componentName: 'Test Component'
52 | }
53 | });
54 |
55 | let textarea = TestUtils.findRenderedDOMComponentWithTag(
56 | workspace, 'textarea'
57 | );
58 |
59 | TestUtils.Simulate.change(textarea);
60 | });
61 |
62 | });
63 |
--------------------------------------------------------------------------------
/src/Atellier.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import Immutable from 'immutable';
3 | import Sidebar from './Sidebar';
4 | import Workspace from './Workspace';
5 |
6 | import './styles/atellier.less';
7 |
8 | class Atellier extends React.Component {
9 |
10 | static defaultProps = {
11 | components: []
12 | };
13 |
14 | static propTypes = {
15 | components: PropTypes.arrayOf(PropTypes.shape({
16 | component: PropTypes.func,
17 | componentName: PropTypes.string
18 | }))
19 | };
20 |
21 | constructor(props) {
22 | super(props);
23 | this.state = {
24 | components: Immutable.List(props.components),
25 | stagedComponent: null
26 | };
27 | }
28 |
29 | render() {
30 | let { components, stagedComponent } = this.state;
31 | return (
32 |
33 |
34 |
35 |
36 | );
37 | }
38 |
39 | _handleSelectComponent = (component, key) => {
40 | component.indexKey = key;
41 | this.setState({stagedComponent: component});
42 | };
43 | }
44 |
45 | // commonjs2
46 | export default Atellier;
47 |
--------------------------------------------------------------------------------
/src/ComponentList.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import classNames from 'classnames';
3 | import Immutable from 'immutable';
4 |
5 | class ComponentList extends React.Component {
6 |
7 | static defaultProps = {
8 | components: [],
9 | onSelect: PropTypes.func,
10 | stagedComponent: {}
11 | };
12 |
13 | static propTypes = {
14 | components: PropTypes.instanceOf(Immutable.List),
15 | onSelect: PropTypes.func,
16 | stagedComponent: PropTypes.shape({
17 | component: PropTypes.func,
18 | componentName: PropTypes.string
19 | }),
20 | };
21 |
22 | constructor(props){
23 | super(props);
24 | this.state = {
25 | filter: ''
26 | };
27 | }
28 |
29 | render() {
30 | return (
31 |
32 |
33 |
37 |
38 |
39 | {this._renderComponentListItems()}
40 |
41 |
42 | );
43 | }
44 |
45 | _getInvalidComponents(){
46 | return JSON.parse(localStorage.getItem('invalidComponents')) || [];
47 | }
48 |
49 | _isInvalidComponent(component, indexKey){
50 | let invalidComponents = this._getInvalidComponents();
51 | let exists = invalidComponents.find( (item) => {
52 | return (item.componentName === component.componentName && item.indexKey === indexKey);
53 | });
54 | return !!exists;
55 | }
56 |
57 | _renderComponentListItems() {
58 | return this.props.components
59 | .filter(({componentName}) => {
60 | return ~componentName.toLowerCase().indexOf(this.state.filter.toLowerCase());
61 | })
62 | .map((component, key) => {
63 | let className = classNames('component-list-item', {
64 | 'component-list-item-error': this._isInvalidComponent(component, key),
65 | 'component-list-item-selected': Immutable.is(component, this.props.stagedComponent)
66 | });
67 |
68 | return (
69 |
70 | {component.componentName}
71 |
72 | );
73 | });
74 | }
75 |
76 | _handleSelectComponentItem = (component, key) => {
77 | return () => {
78 | this.props.onSelect(component, key);
79 | };
80 | };
81 |
82 | _handleFilterComponents = (event) => {
83 | this.setState({filter: event.target.value});
84 | };
85 |
86 | }
87 |
88 | export default ComponentList;
89 |
--------------------------------------------------------------------------------
/src/ComponentProperties.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import Immutable from 'immutable';
3 | import PropertiesContainer from './PropertiesContainer';
4 |
5 | class Properties extends React.Component {
6 |
7 | static propTypes = {
8 | component: PropTypes.shape({
9 | component: PropTypes.func,
10 | componentName: PropTypes.string
11 | }),
12 | componentProps: PropTypes.object,
13 | components: PropTypes.instanceOf(Immutable.List),
14 | onChangeProps: PropTypes.func,
15 | onCloseProperties: PropTypes.func
16 | };
17 |
18 | constructor(props) {
19 | super(props);
20 | }
21 |
22 | render() {
23 | let { component, componentProps, components, onCloseProperties } = this.props;
24 | let element = React.createElement(component.component);
25 | return (
26 |
36 | );
37 | }
38 |
39 | _handleChangeProps = (properties) => {
40 | this.props.onChangeProps(properties);
41 | };
42 |
43 | }
44 |
45 | export default Properties;
46 |
--------------------------------------------------------------------------------
/src/FieldType.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import AceEditor from 'react-ace';
3 | import jsbeautifier from 'js-beautify';
4 | import PropertiesContainer from './PropertiesContainer';
5 | import Toggle from './Toggle';
6 | import 'brace/mode/javascript';
7 | import 'brace/mode/json';
8 | import 'brace/theme/twilight';
9 |
10 | class FieldType extends React.Component {
11 |
12 | static propTypes = {
13 | // defaultValue: PropTypes.any,
14 | // name: PropTypes.string,
15 | // onChange: PropTypes.func,
16 | // type: PropTypes.string.isRequired,
17 | // options: PropTypes.any
18 | };
19 |
20 | static defaultProps = {
21 | // name: '',
22 | // type: 'string',
23 | // defaultValue: null,
24 | // onChange: PropTypes.func
25 | };
26 |
27 | constructor(props) {
28 | super(props);
29 |
30 | this._renderTypeHandlers = {
31 | string: this._renderStringInput,
32 | number: this._renderNumberInput,
33 | bool: this._renderBoolInput,
34 | array: this._renderObjectInput,
35 | object: this._renderObjectInput,
36 | oneOf: this._renderOneOf,
37 | element: this._renderElement,
38 | func: this._renderFunction,
39 | };
40 |
41 | this.state = {
42 | defaultValue: this._getDefaultValue(props)
43 | };
44 | }
45 |
46 | render() {
47 | let renderComponent = (typeof this.props.type && this._renderTypeHandlers[ this.props.type ]) || this._renderStringInput;
48 | return renderComponent.call(this, this.props);
49 | }
50 |
51 | _renderStringInput({ name, type, defaultValue }) {
52 | return (
53 |
54 | {name}
55 |
56 |
57 | );
58 | }
59 |
60 | _renderNumberInput({ name, type, defaultValue }) {
61 | return (
62 |
63 | {name}
64 |
65 |
66 | );
67 | }
68 |
69 | _renderBoolInput({ name, type, defaultValue }) {
70 | return (
71 |
72 |
73 | {name}
74 |
75 | );
76 | }
77 |
78 | _renderObjectInput({ name, type, defaultValue }) {
79 | let aceProps = {
80 | className: 'atellier-editor',
81 | mode: 'json',
82 | theme: 'twilight',
83 | showGutter: false,
84 | onChange: this._handleObjectChange,
85 | name: (Date.now()*Math.random()/Math.random()).toString(),
86 | value: this.state.defaultValue
87 | };
88 |
89 | return (
90 |
94 | );
95 | };
96 |
97 | _renderFunction({ name, type, defaultValue }) {
98 | let aceProps = {
99 | className: 'atellier-editor',
100 | mode: 'javascript',
101 | theme: 'twilight',
102 | showGutter: false,
103 | onChange: this._handleFunctionChange,
104 | name: (Date.now()*Math.random()/Math.random()).toString(),
105 | value: this.state.defaultValue
106 | };
107 |
108 | return (
109 |
113 | );
114 | }
115 |
116 | _renderOneOf({ name, type, defaultValue, options }) {
117 | let selectOptions = (options||[]).map((item, index) => {
118 | return ({item} );
119 | });
120 | return (
121 |
122 | {name}
123 |
124 | Nothing selected
125 | {selectOptions}
126 |
127 |
128 | );
129 | }
130 |
131 | _renderElement({ name, type, defaultValue, components }) {
132 | let containerProps;
133 | let selectComponents = components.map((item, index) => {
134 | return {item.componentName}
135 | });
136 |
137 | if ( defaultValue ) {
138 | containerProps = (
139 |
144 | );
145 | }
146 | return (
147 |
148 | {name}
149 |
150 | Nothing selected
151 | {selectComponents}
152 |
153 | {containerProps}
154 |
155 | );
156 | }
157 |
158 | _getDefaultValue(props) {
159 | switch(props.type) {
160 | case 'array':
161 | case 'object':
162 | return JSON.stringify(props.defaultValue, null, 2);
163 | case 'func':
164 | return jsbeautifier(
165 | (props.defaultValue && props.defaultValue.toString()) || 'function() { return; }'
166 | );
167 | default:
168 | return props.defaultValue;
169 | };
170 | }
171 |
172 | _handleChange = ( response ) => {
173 | let value = response;
174 | if (response.target) {
175 | value = response.target.type === 'number' && +response.target.value || response.target.value;
176 | }
177 | this.props.onChange(this.props.name, value);
178 | };
179 |
180 | _handleElementChange = (response) => {
181 | let component = this.props.components.get(response.target.value);
182 | let element = React.createElement(component.component);
183 | this.props.onChange(this.props.name, element);
184 | };
185 |
186 | _handleElementChangeProps = (properties) => {
187 | let { name, defaultValue } = this.props;
188 | let element = React.cloneElement(defaultValue, properties);
189 | this.props.onChange(this.props.name, element);
190 | };
191 |
192 | _handleObjectChange = (response) => {
193 | this.setState({defaultValue: response}, () => {
194 | try {
195 | this.props.onChange(this.props.name, JSON.parse(response));
196 | } catch(e) {
197 | console.error(e);
198 | }
199 | });
200 | };
201 |
202 | _handleFunctionChange = (response ) => {
203 | this.setState({defaultValue: response}, () => {
204 | try {
205 | this.props.onChange(this.props.name, new Function(`return ${response};`)());
206 | } catch(e) {
207 | console.error(e);
208 | };
209 | });
210 | };
211 |
212 | }
213 |
214 | export default FieldType;
215 |
--------------------------------------------------------------------------------
/src/PropertiesContainer.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import FieldType from './FieldType';
3 |
4 | class SimpleElement extends React.Component {
5 | render(){
6 | return ( );
7 | }
8 | }
9 |
10 | function getPropTypeName(validate) {
11 |
12 | const types = {
13 | array: [],
14 | string: '',
15 | number: 0,
16 | bool: true,
17 | func: () => {},
18 | object: {},
19 | element: ( ),
20 | oneOf: "____"
21 | }
22 |
23 | for (let typeName in types) {
24 | const errors = validate({"name": types[typeName]}, "name");
25 |
26 | if ( !errors ) {
27 | return {
28 | "name": typeName,
29 | "values": typeName=='element'? undefined:types[typeName]
30 | };
31 | }
32 |
33 | switch(true) {
34 | case /one of/.test(errors):
35 | let oneOfArray = /expected one of (\[.*\])/.exec(errors);
36 |
37 | if (oneOfArray && oneOfArray[1]) {
38 | return {
39 | name: 'oneOf',
40 | options: JSON.parse(oneOfArray[1]) || []
41 | };
42 | }
43 | break;
44 | }
45 | }
46 |
47 | return {
48 | "name": 'unknown',
49 | "values": undefined
50 | }
51 | }
52 |
53 | class PropertiesContainer extends React.Component {
54 |
55 | static defaultProps = {
56 | type: () => {},
57 | propTypes: {},
58 | defaultProps:{}
59 | };
60 |
61 | static propTypes = {
62 | // onChangeProps: PropTypes.func,
63 | // onCloseProperties: PropTypes.func,
64 | element: PropTypes.shape({
65 | type: PropTypes.func,
66 | propTypes: PropTypes.object,
67 | defaultProps: PropTypes.object
68 | }),
69 | // componentProps: PropTypes.object
70 | };
71 |
72 | constructor(props) {
73 | super(props);
74 | if (props) {
75 | this._defineProperties(props);
76 | }
77 | }
78 |
79 | componentWillReceiveProps(nextProps) {
80 | this._defineProperties(nextProps);
81 | }
82 |
83 | render() {
84 | let { name, element } = this.props;
85 |
86 | if ( (element) && element.type && typeof element.type.propTypes !== 'object') {
87 | return null;
88 | }
89 |
90 | return (
91 |
92 |
93 | {this._renderContainerHeader(name)}
94 |
95 | {this._renderPropertiesFields(element)}
96 |
97 |
98 |
99 | );
100 | }
101 |
102 | _renderContainerHeader(name) {
103 | return !!name && (
104 |
105 |
+
106 |
{name}
107 |
108 | );
109 | }
110 |
111 | _renderPropertiesFields(element) {
112 | let propTypes = element && element.type.propTypes;
113 | let propsFields = [];
114 | for (let prop in propTypes) {
115 | let proptype = propTypes[prop];
116 | const { name, values, options } = getPropTypeName(proptype);
117 | const defaultProps = this._properties[prop] || values;
118 | const propOptions = proptype.options || options;
119 | propsFields.push(
120 |
129 | );
130 | }
131 |
132 | return propsFields.length && propsFields || this._renderNoProperties();
133 | }
134 |
135 | _renderNoProperties() {
136 | return (
137 | No properties
138 | );
139 | }
140 |
141 | _handleChange = (propName, propValue) => {
142 | this._properties[propName] = propValue;
143 | this.props.onChangeProps(this._properties);
144 | };
145 |
146 | _handleCloseProperties = () => {
147 | return this.props.onCloseProperties && this.props.onCloseProperties(null);
148 | };
149 |
150 | _defineProperties = (props) => {
151 | if ( props.element.type ) {
152 | try{
153 | Object.keys(props.element.type.propTypes).filter( function(prop) {
154 | if ( (props.element.type.defaultProps) && !props.element.type.defaultProps[prop] ) {
155 | props.element.type.defaultProps[prop] = null;
156 | }
157 | });
158 | } catch(e) {
159 | this._properties = {};
160 | }
161 | }
162 | this._properties = Object.assign({}, props.element.props, props.elementProps);
163 | };
164 |
165 | }
166 |
167 | export default PropertiesContainer;
168 |
--------------------------------------------------------------------------------
/src/Sidebar.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import Immutable from 'immutable';
3 | import classNames from 'classnames';
4 | import ComponentList from './ComponentList';
5 | import toggleButtonImage from './images/arrow.png';
6 |
7 | class Sidebar extends React.Component {
8 |
9 | static defaultProps = {
10 | components: [],
11 | onSelect: PropTypes.func,
12 | stagedComponent: {}
13 | };
14 |
15 | static propTypes = {
16 | components: PropTypes.instanceOf(Immutable.List),
17 | onSelect: PropTypes.func,
18 | stagedComponent: PropTypes.shape({
19 | component: PropTypes.func,
20 | componentName: PropTypes.string
21 | }),
22 | };
23 |
24 | constructor(props) {
25 | super(props);
26 | this.state = {
27 | close: false
28 | };
29 | }
30 |
31 | render() {
32 | let { components, stagedComponent, onSelect } = this.props;
33 | let className = classNames('sidebar', {'sidebar-close': this.state.close})
34 | return (
35 |
36 |
ATELLIER
37 |
38 |
39 |
40 |
41 |
42 | );
43 | }
44 |
45 | _handleToggleSidebar = () => {
46 | this.setState({close: !this.state.close});
47 | };
48 |
49 | }
50 |
51 | export default Sidebar;
52 |
--------------------------------------------------------------------------------
/src/Stage.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import ReactDOM from 'react-dom';
3 | import classNames from 'classnames';
4 |
5 | class Stage extends React.Component {
6 |
7 | static defaultProps = {
8 | component: {},
9 | properties: {}
10 | };
11 |
12 | static propTypes = {
13 | component: PropTypes.shape({
14 | component: PropTypes.func,
15 | componentName: PropTypes.string
16 | }),
17 | properties: PropTypes.object
18 | };
19 |
20 | constructor(props){
21 | super(props);
22 | this.state = {
23 | constrast: false,
24 | componentError: null,
25 | reloadNeeded: false
26 | };
27 | this._defineElement(props);
28 | }
29 |
30 | componentWillReceiveProps(nextProps) {
31 | this._defineElement(nextProps);
32 | }
33 |
34 | _renderComponent(){
35 | let { component, properties } = this.props;
36 | let targetRender = document.getElementById('__stage_render__');
37 | try {
38 | ReactDOM.unstable_renderSubtreeIntoContainer(
39 | this,
40 |
41 | {this._renderStageBoard()}
42 | {this._renderStageTools()}
43 |
,
44 | targetRender
45 | );
46 | } catch(error) {
47 | targetRender.innerHTML = '';
48 | ReactDOM.unstable_renderSubtreeIntoContainer(
49 | this,
50 |
51 | {this._renderErrorAlert(error)}
52 |
,
53 | targetRender
54 | );
55 | }
56 | }
57 |
58 | render() {
59 | return (
60 |
61 | );
62 | }
63 |
64 | componentDidMount(){
65 | this._renderComponent();
66 | }
67 |
68 | componentDidUpdate(){
69 | this._renderComponent();
70 | }
71 |
72 | _renderStageBoard() {
73 | let className = classNames('stage-board', {'stage-board-dark': this.state.constrast});
74 | return (
75 |
76 | {this._instance}
77 |
78 | );
79 | }
80 |
81 | onReloadAtellier = () => {
82 | window.location.reload();
83 | };
84 |
85 | _renderErrorAlert(error) {
86 | this._setInvalidComponent( this.props.component );
87 | return (
88 |
89 |
Reload
90 |
91 | React component {this.state.componentError} crashed!
92 |
93 |
{error.message}
94 |
95 | );
96 | }
97 |
98 | _setInvalidComponent(component){
99 | let componentName = component.componentName,
100 | indexKey = component.indexKey;
101 |
102 | let invalidComponents = JSON.parse(localStorage.getItem('invalidComponents')) || [];
103 |
104 | let exists = invalidComponents.filter( (item) => {
105 | return (item.componentName === componentName && item.indexKey === indexKey);
106 | });
107 |
108 | if ( !exists.length && !this.state.reloadNeeded ) {
109 | this.setState({
110 | reloadNeeded: true
111 | });
112 | invalidComponents.push({
113 | indexKey: indexKey,
114 | componentName: componentName
115 | });
116 | } else if(exists.length && !this.state.reloadNeeded) {
117 | this.setState({
118 | reloadNeeded: true
119 | });
120 | }
121 |
122 | localStorage.setItem('invalidComponents', JSON.stringify(invalidComponents));
123 | }
124 |
125 | _renderStageTools() {
126 | return (
127 |
128 | Stage color
129 |
130 | Light
131 | Dark
132 |
133 |
134 | );
135 | }
136 |
137 | _handleChangeConstrast = (event) => {
138 | this.setState({
139 | constrast: event.target.value === 'true'
140 | });
141 | };
142 |
143 | _defineElement = (props) => {
144 | let { component, properties } = props;
145 | if (component && component.component) {
146 | this._instance = React.cloneElement(React.createElement(component.component, properties))
147 | }
148 | };
149 | }
150 |
151 | export default Stage;
152 |
--------------------------------------------------------------------------------
/src/Toggle.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import classNames from 'classnames';
3 |
4 | class Toggle extends React.Component {
5 |
6 | static propTypes = {
7 | defaultValue: PropTypes.bool,
8 | onChange: PropTypes.func
9 | };
10 |
11 | static defaultProps = {
12 | defaultValue: false,
13 | onChange: PropTypes.func
14 | };
15 |
16 | constructor(props) {
17 | super(props);
18 | this.state = {
19 | checked: this.props.defaultValue
20 | };
21 | }
22 |
23 | render() {
24 | let className = classNames('atellier-toggle', this.props.className, {'atellier-toggle-checked': this.state.checked});
25 | return (
26 |
27 | );
28 | }
29 |
30 | _handleChange = () => {
31 | this.setState({checked: !this.state.checked}, () => {
32 | this.props.onChange(this.state.checked);
33 | });
34 | };
35 | }
36 |
37 | export default Toggle;
38 |
--------------------------------------------------------------------------------
/src/Workspace.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import Immutable from 'immutable';
3 | import ComponentProperties from './ComponentProperties';
4 | import Stage from './Stage';
5 |
6 | class Workspace extends React.Component {
7 |
8 | static propTypes = {
9 | component: PropTypes.shape({
10 | component: PropTypes.func,
11 | componentName: PropTypes.string
12 | }),
13 | components: PropTypes.instanceOf(Immutable.List),
14 | onCloseProperties: PropTypes.func,
15 | };
16 |
17 | constructor(props) {
18 | super(props);
19 | this.state = {
20 | componentProps: {}
21 | };
22 | }
23 |
24 | render() {
25 | let { components, component, onCloseProperties } = this.props;
26 | let { componentProps } = this.state;
27 | return !!component && (
28 |
29 |
35 |
38 |
39 | );
40 | }
41 |
42 | _handleChangeProps = (properties) => {
43 | this.setState({componentProps: properties});
44 | };
45 | }
46 |
47 | export default Workspace;
48 |
--------------------------------------------------------------------------------
/src/fonts/ProximaNova-Light.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scup/atellier/5ad6a401d78b5625eb5293891b4741aad0149788/src/fonts/ProximaNova-Light.otf
--------------------------------------------------------------------------------
/src/fonts/ProximaNova-LightItalic.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scup/atellier/5ad6a401d78b5625eb5293891b4741aad0149788/src/fonts/ProximaNova-LightItalic.otf
--------------------------------------------------------------------------------
/src/fonts/ProximaNova-RegItalic.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scup/atellier/5ad6a401d78b5625eb5293891b4741aad0149788/src/fonts/ProximaNova-RegItalic.otf
--------------------------------------------------------------------------------
/src/fonts/ProximaNova-Regular.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scup/atellier/5ad6a401d78b5625eb5293891b4741aad0149788/src/fonts/ProximaNova-Regular.otf
--------------------------------------------------------------------------------
/src/fonts/ProximaNova-SboldItalic.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scup/atellier/5ad6a401d78b5625eb5293891b4741aad0149788/src/fonts/ProximaNova-SboldItalic.otf
--------------------------------------------------------------------------------
/src/fonts/ProximaNova-Semibold.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scup/atellier/5ad6a401d78b5625eb5293891b4741aad0149788/src/fonts/ProximaNova-Semibold.otf
--------------------------------------------------------------------------------
/src/fonts/QuattrocentoSans-Bold.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scup/atellier/5ad6a401d78b5625eb5293891b4741aad0149788/src/fonts/QuattrocentoSans-Bold.otf
--------------------------------------------------------------------------------
/src/fonts/SciFly-Sans.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scup/atellier/5ad6a401d78b5625eb5293891b4741aad0149788/src/fonts/SciFly-Sans.ttf
--------------------------------------------------------------------------------
/src/images/arrow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scup/atellier/5ad6a401d78b5625eb5293891b4741aad0149788/src/images/arrow.png
--------------------------------------------------------------------------------
/src/images/magnifying-glass.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scup/atellier/5ad6a401d78b5625eb5293891b4741aad0149788/src/images/magnifying-glass.png
--------------------------------------------------------------------------------
/src/images/x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scup/atellier/5ad6a401d78b5625eb5293891b4741aad0149788/src/images/x.png
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import Atellier from './Atellier';
2 |
3 | export default Atellier;
4 |
--------------------------------------------------------------------------------
/src/structural/PropTypesIdentifier.js:
--------------------------------------------------------------------------------
1 |
2 | export default class PropTypesIdentifier {
3 |
4 | constructor(propTypes) {
5 | this.propTypes = propTypes;
6 | }
7 |
8 | static create(propTypes) {
9 | return new this(propTypes);
10 | }
11 |
12 | identify() {
13 | for (let proptype in this.propTypes) {
14 | this.propTypes[proptype].type = proptype;
15 | if (this.propTypes[proptype].isRequired) {
16 | this.propTypes[proptype].isRequired.type = proptype;
17 | }
18 | }
19 | return this;
20 | }
21 |
22 | intercept(proptype) {
23 | if (proptype === this.propTypes.oneOf) {
24 | let _oneOf = this.propTypes.oneOf;
25 | this.propTypes.oneOf = (oneOfArguments) => {
26 | let proptypeFunc = this._getInterceptFunc(_oneOf, oneOfArguments);
27 | proptypeFunc.type = 'oneOf';
28 | proptypeFunc.options = oneOfArguments;
29 | return proptypeFunc;
30 | }
31 | }
32 |
33 | return this;
34 | }
35 |
36 | _getInterceptFunc(func, args) {
37 | return (proptypeArguments) => {
38 | let instance = func(args)(proptypeArguments);
39 | return instance;
40 | };
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/styles/_atellier.less:
--------------------------------------------------------------------------------
1 | /* ------------------------- *\
2 | ATTELLIER
3 | \* ------------------------- */
4 |
5 | .atellier {
6 | background-color: @color-base;
7 | position: fixed;
8 | height: 100%;
9 | width: 100%;
10 | .flex-container();
11 | }
12 |
--------------------------------------------------------------------------------
/src/styles/_base.less:
--------------------------------------------------------------------------------
1 | /* ------------------------- *\
2 | BASE
3 | \* ------------------------- */
4 |
5 | html, body, #main {
6 | height: 100%;
7 | width: 100%;
8 | }
9 |
10 | body {
11 | font: @font-weight @font-size @font-family;
12 | margin: 0;
13 | padding: 0;
14 | }
15 |
--------------------------------------------------------------------------------
/src/styles/_colors.less:
--------------------------------------------------------------------------------
1 | /* ------------------------- *\
2 | COLORS
3 | \* ------------------------- */
4 |
5 | @color-base: #393234;
6 | @color-text: #a49d98;
7 | @color-secundary-text: #cac4bc;
8 | @color-disabled: #696161;
9 | @color-dividers: #3c3336;
10 | @color-border: #2f272a;
11 | @color-primary: #494043;
12 | @color-accent: #987b62;
13 | @color-error: #c64040;
14 |
15 | @color-input-error-background: lighten(@color-error, 36%);
16 | @color-input-background: #3f3639;
17 | @color-placeholder: #736d6b;
18 |
19 | @color-toggle-off-background: #3e3538;
20 | @color-toggle-on-background: #94785f;
21 |
22 | @color-stage-board-light: #ffffff;
23 | @color-stage-board-dark: #000000;
24 | @color-stage-board-grid: #f1f1f1;
25 |
26 | @color-stage-error-alert-light: #F2DEDE;
27 | @color-stage-error-alert-dark: #C9302C;
28 |
29 | @color-sidebar-toggle-button: #3f3639;
30 | @color-sidebar-toggle-button-arrow: #9d9693;
31 |
--------------------------------------------------------------------------------
/src/styles/_componentList.less:
--------------------------------------------------------------------------------
1 | /* ------------------------- *\
2 | COMPONENT LIST
3 | \* ------------------------- */
4 |
5 | .component-nav {
6 | .flex-item(1 1 auto);
7 | .flex-container(column);
8 | }
9 |
10 | .component-tools {
11 | padding: 0 12px;
12 | .flex-item(0 0 56px);
13 | }
14 |
15 | .component-filter {
16 | margin: 8px 0;
17 | height: 40px;
18 | background-image: url("../images/magnifying-glass.png");
19 | }
20 |
21 | .component-list {
22 | border-top: 1px solid @color-border;
23 | display: block;
24 | height: 1px;
25 | list-style: none;
26 | margin-top: 18px;
27 | margin: 0;
28 | overflow: scroll;
29 | padding: 0;
30 | .flex-item(1 1 auto);
31 | }
32 |
33 | .component-list-item {
34 | border-bottom: 1px solid @color-dividers;
35 | color: @color-text;
36 | cursor: pointer;
37 | display: block;
38 | padding: 19px 24px;
39 | line-height: @line-height;
40 | .text-overflow();
41 |
42 | &-selected {
43 | background-color: @color-accent;
44 | color: @color-secundary-text;
45 | font-weight: bold;
46 | }
47 | }
48 |
49 | .component-list-item-error {
50 | opacity: 0.5;
51 | text-shadow: none;
52 | color: grey;
53 | text-decoration: line-through;
54 | }
55 |
--------------------------------------------------------------------------------
/src/styles/_componentProperties.less:
--------------------------------------------------------------------------------
1 | /* ------------------------- *\
2 | COMPONENT PROPERTIES
3 | \* ------------------------- */
4 |
5 | .component-properties {
6 | padding: 25px;
7 | position: relative;
8 | overflow: scroll;
9 | border-right: 1px solid @color-border;
10 | .flex-item(0 0 362px);
11 | }
12 |
13 | .properties-container {
14 | background-color: @color-primary;
15 | border-radius: @border-radius;
16 | border: 1px solid @color-border;
17 | position: relative;
18 | width: 100%;
19 | }
20 |
21 | .properties-container .properties-container {
22 | margin-top: 21px;
23 | }
24 |
25 | .container-close-button {
26 | color: @color-border;
27 | cursor: pointer;
28 | cursor: pointer;
29 | font-size: 35px;
30 | position: absolute;
31 | right: 23px;
32 | top: 7px;
33 | transform: rotate(45deg);
34 | }
35 |
36 | .properties-component {
37 | border-bottom: 1px solid @color-border;
38 | color: @color-secundary-text;
39 | font-size: 18.67px;
40 | font-weight: 400;
41 | margin: 0;
42 | padding: 14px 24px;
43 | }
44 |
45 | .properties-form {
46 | padding: 0 14px;
47 | position: relative;
48 | }
49 |
50 | .no-properties {
51 | margin: 22px 0;
52 | }
53 |
--------------------------------------------------------------------------------
/src/styles/_default.less:
--------------------------------------------------------------------------------
1 | /* ------------------------- *\
2 | DEFAULT
3 | \* ------------------------- */
4 |
5 | @font-family-logo: "Sci Fly";
6 | @font-family: 'Proxima Nova';
7 | @font-size: 16px;
8 | @font-weight: 300;
9 | @line-height: 16px;
10 |
11 | @border-radius: 2px;
12 |
--------------------------------------------------------------------------------
/src/styles/_fieldType.less:
--------------------------------------------------------------------------------
1 | /* ------------------------- *\
2 | FIELD TYPE
3 | \* ------------------------- */
4 |
5 | .properties-field {
6 | padding: 21px 0;
7 | border-top: 1px solid @color-dividers;
8 |
9 | &:first-child {
10 | border-top: none;
11 | }
12 |
13 | label {
14 | color: @color-secundary-text;
15 | display: inline-block;
16 | font-weight: 400;
17 | margin-bottom: 11px;
18 | }
19 |
20 | .atellier-toggle {
21 | float: right;
22 |
23 | & + label {
24 | margin-bottom: 0;
25 | line-height: 25px;
26 | }
27 | }
28 |
29 | &.error {
30 | color: @color-error;
31 |
32 | label {
33 | color: @color-error;
34 | }
35 |
36 | input,
37 | textarea,
38 | select {
39 | color: @color-error;
40 | background-color: @color-input-error-background;
41 | border-top: 1px solid @color-error;
42 | border: 1px solid @color-error;
43 | }
44 | }
45 |
46 |
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/src/styles/_fonts.less:
--------------------------------------------------------------------------------
1 | /* ------------------------- *\
2 | FONTS
3 | \* ------------------------- */
4 |
5 | // Proxima Nova
6 |
7 | @font-face {
8 | font-family: "Proxima Nova";
9 | src: url("../fonts/ProximaNova-Light.otf");
10 | font-weight: 300;
11 | }
12 |
13 | @font-face {
14 | font-family: "Proxima Nova";
15 | src: url("../fonts/ProximaNova-Regular.otf");
16 | font-weight: 400;
17 | }
18 |
19 | @font-face {
20 | font-family: "Proxima Nova";
21 | src: url("../fonts/ProximaNova-Semibold.otf");
22 | font-weight: 500;
23 | }
24 |
25 | // Sci Fly Sans
26 |
27 | @font-face {
28 | font-family: "Sci Fly";
29 | src: url("../fonts/SciFly-Sans.ttf");
30 | }
31 |
--------------------------------------------------------------------------------
/src/styles/_forms.less:
--------------------------------------------------------------------------------
1 | /* ------------------------- *\
2 | FORMS
3 | \* ------------------------- */
4 |
5 | .atellier-input {
6 | background-color: @color-input-background;
7 | border-radius: @border-radius;
8 | // border: 1px solid transparent;
9 | border: none;
10 | color: @color-text;
11 | font-size: 16px;
12 | font-weight: 300;
13 | outline: none;
14 | padding: 13px;
15 | width: 100%;
16 | resize: none;
17 | box-sizing: border-box;
18 |
19 | &::-webkit-input-placeholder {
20 | color: @color-placeholder;
21 | }
22 |
23 | &::-webkit-outer-spin-button,
24 | &::-webkit-inner-spin-button {
25 | -webkit-appearance: none;
26 | margin: 0;
27 | }
28 |
29 | &-icon {
30 | background-repeat: no-repeat;
31 | background-position: 13px;
32 | padding-left: 36px;
33 | }
34 |
35 | &.json{
36 | height: 160px;
37 | }
38 |
39 | }
40 |
41 | select.atellier-input{
42 | height: 44px;
43 | }
44 |
45 | .atellier-editor{
46 | width: 100%!important;
47 | height: 300px!important;
48 | }
49 |
--------------------------------------------------------------------------------
/src/styles/_mixins.less:
--------------------------------------------------------------------------------
1 | /* ------------------------- *\
2 | ANIMATIONS
3 | \* ------------------------- */
4 |
5 | // FLEX
6 |
7 | .flex-container(@flow: row nowrap, @justify: flex-start, @align-items: stretch, @align-content: stretch) {
8 | align-content: @align-content;
9 | align-items: @align-items;
10 | display: flex;
11 | flex-flow: @flow;
12 | justify-content: @justify;
13 | }
14 |
15 | .flex-item(@values) {
16 | flex: @values;
17 | }
18 |
19 | // ANIMATION
20 |
21 | .keyframes(@animationName, @content) {
22 | @keyframes @animationName {
23 | @content();
24 | }
25 | }
26 |
27 | .animation(@delay, @duration, @animation) {
28 | animation-delay: @delay;
29 | animation-duration: @duration;
30 | animation-name: @animation;
31 | animation-fill-mode: forwards;
32 | }
33 |
34 | // TEXT
35 |
36 | .text-overflow() {
37 | overflow: hidden;
38 | text-overflow: ellipsis;
39 | white-space: nowrap;
40 | }
41 |
--------------------------------------------------------------------------------
/src/styles/_sidebar.less:
--------------------------------------------------------------------------------
1 | /* ------------------------- *\
2 | SIDEBAR
3 | \* ------------------------- */
4 |
5 | .sidebar {
6 | background-color: @color-primary;
7 | border-right: 1px solid @color-border;
8 | height: 100%;
9 | position: relative;
10 | margin-left: 0;
11 | margin-right: 0;
12 | transition: margin-left 0.5s, margin-right 0.5s;
13 | .flex-item(0 0 315px);
14 | .flex-container(column);
15 |
16 | &-close {
17 | margin-left: -315px;
18 | margin-right: 50px;
19 |
20 | .sidebar-toggle-button {
21 | background-color: @color-primary;
22 | right: -51px;
23 |
24 | img {
25 | transform: rotate(0deg);
26 | }
27 | }
28 | }
29 | }
30 |
31 | .sidebar-toggle-button {
32 | background-color: @color-sidebar-toggle-button;
33 | color: @color-sidebar-toggle-button-arrow;
34 | cursor: pointer;
35 | display: block;
36 | font-weight: 400;
37 | height: 25px;
38 | line-height: 25px;
39 | position: absolute;
40 | right: 12px;
41 | text-align: center;
42 | top: 25px;
43 | transition: right 0.5s, color 0.5s;
44 | width: 25px;
45 |
46 | img {
47 | padding: 8px 0;
48 | transform: rotate(180deg);
49 | transition: transform 0.5s;
50 | }
51 | }
52 |
53 | .logo {
54 | color: @color-accent;
55 | font: 23.33px @font-family-logo;
56 | letter-spacing: 3px;
57 | margin: 25px 0 18px 0;
58 | text-align: center;
59 | text-transform: uppercase;
60 | .flex-item(0 0 25px);
61 | }
62 |
--------------------------------------------------------------------------------
/src/styles/_stage.less:
--------------------------------------------------------------------------------
1 | /* ------------------------- *\
2 | STAGE
3 | \* ------------------------- */
4 |
5 | .stage {
6 | padding: 25px;
7 | position: relative;
8 | overflow: hidden;
9 | .flex-item(1 1 auto);
10 | .flex-container(column);
11 | }
12 |
13 | .stage-board {
14 | border-radius: @border-radius;
15 | background-color: @color-stage-board-light;
16 | background-image: linear-gradient(@color-stage-board-grid 1px, transparent 1px),
17 | linear-gradient(90deg, @color-stage-board-grid 1px, transparent 1px);
18 | background-size:20px 20px, 20px 20px;
19 | background-position:-1px -1px, -1px -1px;
20 | position: relative;
21 | overflow: scroll;
22 | .flex-item(1 1 auto);
23 |
24 | &-dark {
25 | background-color: @color-stage-board-dark;
26 | }
27 | }
28 |
29 | .stage-tools {
30 | margin-top: 25px;
31 | .flex-item(0 0 54px);
32 |
33 | label {
34 | color: @color-secundary-text;
35 | margin-right: 10px;
36 | font-weight: 400;
37 | }
38 |
39 | select.atellier-input {
40 | width: 200px;
41 | }
42 | }
43 |
44 | .stage-render-error {
45 | background-color: @color-stage-error-alert-light;
46 | padding: 10px 20px;
47 |
48 | .stage-error-text {
49 | margin-left: 10px;
50 | }
51 |
52 | .stage-error-message{
53 | padding: 5px;
54 | background: white;
55 | }
56 |
57 | button.reload {
58 | border: 0;
59 | padding: 5px 20px;
60 | background-color: @color-stage-error-alert-dark;
61 | color: white;
62 | cursor: pointer;
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/styles/_toggle.less:
--------------------------------------------------------------------------------
1 | /* ------------------------- *\
2 | TOGGLE
3 | \* ------------------------- */
4 |
5 | .atellier-toggle {
6 | border-radius: 25px;
7 | cursor: pointer;
8 | display: block;
9 | height: 25px;
10 | outline: none;
11 | position: relative;
12 | user-select: none;
13 | width: 49px;
14 |
15 | &:before,
16 | &:after {
17 | bottom: 1px;
18 | content: "";
19 | display: block;
20 | left: 1px;
21 | position: absolute;
22 | top: 1px;
23 | }
24 |
25 | &:before {
26 | background-color: @color-toggle-off-background;
27 | border-radius: 25px;
28 | right: 1px;
29 | transition: background 0.4s;
30 | }
31 |
32 | &:after {
33 | width: 23px;
34 | border: 1px solid @color-border;
35 | background-color: @color-primary;
36 | border-radius: 100%;
37 | box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3);
38 | transition: margin 0.4s;
39 | }
40 |
41 | &-checked:before {
42 | background-color: @color-toggle-on-background;
43 | }
44 |
45 | &-checked:after {
46 | margin-left: 25px;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/styles/_workspace.less:
--------------------------------------------------------------------------------
1 | /* ------------------------- *\
2 | WORKSPACE
3 | \* ------------------------- */
4 |
5 | .workspace {
6 | height: 100%;
7 | overflow: hidden;
8 | .flex-item(1 1 auto);
9 | .flex-container();
10 | }
11 |
--------------------------------------------------------------------------------
/src/styles/atellier.less:
--------------------------------------------------------------------------------
1 |
2 |
3 | /* ------------------------- *\
4 | ATTELLIER
5 | \* ------------------------- */
6 |
7 |
8 |
9 | /* ------------------------- *\
10 | SETTINGS
11 | \* ------------------------- */
12 |
13 | @import '_fonts';
14 | @import '_colors';
15 | @import '_default';
16 | @import '_mixins';
17 |
18 |
19 |
20 | /* ------------------------- *\
21 | BASE
22 | \* ------------------------- */
23 |
24 | @import '_base';
25 | @import '_forms';
26 |
27 |
28 |
29 | /* ------------------------- *\
30 | COMPONENTS
31 | \* ------------------------- */
32 |
33 | @import '_atellier';
34 | @import '_componentList';
35 | @import '_componentProperties';
36 | @import '_fieldType';
37 | @import '_sidebar';
38 | @import '_stage';
39 | @import '_toggle';
40 | @import '_workspace';
41 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 |
4 | module.exports = {
5 | debug: false,
6 | entry: './src/index.js',
7 | output: {
8 | path: path.join(__dirname, 'dist'),
9 | filename: 'react-atellier.js',
10 | library: 'ReactAtellier',
11 | libraryTarget: 'umd',
12 | },
13 | externals: {
14 | // Use external version of React
15 | 'react': 'react',
16 | 'react-dom': 'react-dom'
17 | },
18 | module: {
19 | loaders: [
20 | {
21 | test: /\.less$/,
22 | loader: 'style!css!less?outputStyle=expanded&' +
23 | 'includePaths[]=' + (path.resolve(__dirname, './bower_components')) + '&' +
24 | 'includePaths[]=' + (path.resolve(__dirname, './node_modules'))
25 | },
26 | {
27 | test: /\.less$/,
28 | loader: 'autoprefixer'
29 | },
30 | {
31 | test: /\.(otf|ttf)$/,
32 | loader: 'url?limit=100000'
33 | },
34 | {
35 | test: /\.(png|jpg)$/,
36 | loader: 'url?limit=45000'
37 | },
38 | {
39 | test: /(\.js)$/,
40 | exclude: [path.resolve(__dirname, 'node_modules')],
41 | loader: 'babel',
42 | include: path.join(__dirname, 'src')
43 | },
44 | ],
45 | }
46 | };
47 |
--------------------------------------------------------------------------------