Skip to content

Commit

Permalink
Implement simple formatting, add required packages
Browse files Browse the repository at this point in the history
  • Loading branch information
y-moroz committed May 3, 2020
1 parent cf86af0 commit 83f9295
Show file tree
Hide file tree
Showing 17 changed files with 342 additions and 69 deletions.
25 changes: 25 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"classnames": "^2.2.6",
"prop-types": "^15.7.2",
"ramda": "^0.27.0",
"react": "^16.2.0",
"react-dom": "^16.2.0",
"react-icons": "^3.10.0",
"react-scripts": "1.1.1"
},
"scripts": {
Expand Down
46 changes: 24 additions & 22 deletions src/App.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,30 @@
import React, {Component} from 'react';
import React, { useEffect, useState } from 'react';
import './App.css';
import ControlPanel from "./control-panel/ControlPanel";
import FileZone from "./file-zone/FileZone";
import PLUGINS from './text-editor/plugins';
import { groupBy, isNil } from 'ramda';
import TextEditor from './text-editor/TextEditor';
import getMockText from './text.service';

class App extends Component {
getText() {
getMockText().then(function (result) {
console.log(result);
});
}
render() {
return (
<div className="App">
<header>
<span>Simple Text Editor</span>
</header>
<main>
<ControlPanel/>
<FileZone/>
</main>
</div>
);
}
const groupedPlugins = groupBy(
plugin => String(!isNil(plugin.groupId) ? plugin.groupId : 0),
PLUGINS
);

function App() {
const [text, setText] = useState('');

useEffect(() => {getMockText().then(text => setText(text))}, []);

return (
<div className="App">
<header>
<span>Simple Text Editor</span>
</header>
<main>
<TextEditor groupedPlugins={groupedPlugins}>{text}</TextEditor>
</main>
</div>
);
}

export default App;
12 changes: 0 additions & 12 deletions src/control-panel/ControlPanel.css

This file was deleted.

18 changes: 0 additions & 18 deletions src/control-panel/ControlPanel.js

This file was deleted.

16 changes: 0 additions & 16 deletions src/file-zone/FileZone.js

This file was deleted.

6 changes: 5 additions & 1 deletion src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,8 @@ footer {

#root {
height: 100%;
}
}

:root {
--content-width: 600px;
}
19 changes: 19 additions & 0 deletions src/text-editor/TextEditor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import PropTypes from 'prop-types';
import React, { Fragment } from 'react';
import ControlPanel from './components/control-panel/ControlPanel';
import FileZone from './components/file-zone/FileZone';

function TextEditor(props) {
return (
<Fragment>
<ControlPanel groups={props.groupedPlugins}/>
<FileZone>{props.children}</FileZone>
</Fragment>
);
}

TextEditor.propTypes = {
groupedPlugins: PropTypes.object
};

export default TextEditor;
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import PropTypes from 'prop-types';
import React from 'react';

function ControlPanelActionGroup(props) {
const renderedPlugins = props.plugins.map((plugin, i) => {
const TagName = plugin.component;
return <TagName {...plugin.props} key={i}/>;
});
return (
<div className="control-panel-actions">
{renderedPlugins}
</div>
);
}

ControlPanelActionGroup.propTypes = {
plugins: PropTypes.array
};

ControlPanelActionGroup.defaultProps = {
plugins: []
};

export default ControlPanelActionGroup;
13 changes: 13 additions & 0 deletions src/text-editor/components/control-panel/ControlPanel.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
.control-panel {
background-color: #fff;
height: 25px;
display: flex;
align-items: center;
padding: 5px 0;
width: var(--content-width);
margin: 0 auto;
}

.control-panel__group:not(:last-child) {
border-right: 1px solid gray;
}
23 changes: 23 additions & 0 deletions src/text-editor/components/control-panel/ControlPanel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from 'react';
import PropTypes from 'prop-types';

import './ControlPanel.css';
import ControlPanelActionGroup from '../control-panel-action-group/ControlPanelActionGroup';

function ControlPanel(props) {
return (
<div className="control-panel">
{Object.keys(props.groups).map(groupId => (
<div className="control-panel__group" key={groupId}>
<ControlPanelActionGroup plugins={props.groups[groupId]}/>
</div>
))}
</div>
);
}

ControlPanel.propTypes = {
groups: PropTypes.object.isRequired
};

export default ControlPanel;
File renamed without changes.
14 changes: 14 additions & 0 deletions src/text-editor/components/file-zone/FileZone.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React from 'react';
import './FileZone.css';

function FileZone(props) {
return (
<div id="file-zone">
<div id="file" contentEditable={true}>
{props.children}
</div>
</div>
);
}

export default FileZone;
7 changes: 7 additions & 0 deletions src/text-editor/plugins/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { SIMPLE_ACTION_PLUGIN_CONFIG } from './simple-action';

const PLUGINS = [
...SIMPLE_ACTION_PLUGIN_CONFIG
];

export default PLUGINS;
17 changes: 17 additions & 0 deletions src/text-editor/plugins/simple-action/SimpleAction.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
.action-button {
background-color: transparent;
border: none;
padding: 5px 5px;
}

.action-button:hover {
background-color: lightgrey;
}

.action-button__icon {
vertical-align: middle;
}

.action-button--active {
background-color: grey;
}
39 changes: 39 additions & 0 deletions src/text-editor/plugins/simple-action/SimpleAction.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import PropTypes from 'prop-types';
import React, { useEffect, useState } from 'react';
import classNames from 'classnames';

import './SimpleAction.css';

function SimpleAction(props) {
const [isActive, setIsActive] = useState(document.queryCommandState(props.command));
const Icon = props.icon;

useEffect(
() => {
const listener = () => setIsActive(document.queryCommandState(props.command));
document.addEventListener('selectionchange', listener);
return () => document.removeEventListener('selectionchange', listener);
},
[]
);

return (
<button
type="button"
className={classNames({
'action-button': true,
'action-button--active': isActive
})}
onClick={() => document.execCommand(props.command)}
>
<Icon className="action-button__icon"/>
</button>
);
}

SimpleAction.propTypes = {
command: PropTypes.string.isRequired,
icon: PropTypes.func.isRequired
};

export default SimpleAction;
Loading

0 comments on commit 83f9295

Please sign in to comment.