diff --git a/src/backend/controllers/hierarchy.js b/src/backend/controllers/hierarchy.js
index dc9d36fba4f7c6fa897b3fb714775b0a50c5ac22..cfc8d3d11b280ee2592a68cfed20bea7c850440f 100644
--- a/src/backend/controllers/hierarchy.js
+++ b/src/backend/controllers/hierarchy.js
@@ -65,7 +65,7 @@ const cacheHierarchy = () => {
const file = allFiles[fileIndex];
if (file) {
const cwd = getPath();
- exec(`git --no-pager log --follow --format="%H" "${file.path}"`, { cwd }, (error, stdout, stderr) => {
+ exec(`git --no-pager log --follow --no-merges --format="%H" "${file.path}"`, { cwd }, (error, stdout, stderr) => {
if (!error && !stderr) {
const output = stdout.toString().replace(/\n$/, '');
const shas = output.split('\n').reverse();
@@ -92,24 +92,22 @@ const getHierarchy = (req, res, next) => {
res.json({ hierarchy: cachedHierarchy });
};
-const getFile = (req, res, next) => {
- const { categoryKey, algorithmKey, fileName } = req.params;
+const getAlgorithm = (req, res, next) => {
+ const { categoryKey, algorithmKey } = req.params;
const category = cachedHierarchy.find(category => category.key === categoryKey);
if (!category) return next(new NotFoundError());
const algorithm = category.algorithms.find(algorithm => algorithm.key === algorithmKey);
if (!algorithm) return next(new NotFoundError());
- const file = algorithm.files.find(file => file.name === fileName);
- if (!file) return next(new NotFoundError());
- const { content, contributors } = file;
- res.json({ file: { content, contributors } });
+ const files = algorithm.files.map(({ name, content, contributors }) => ({ name, content, contributors }));
+ res.json({ algorithm: { ...algorithm, files } });
};
router.route('/')
.get(getHierarchy);
-router.route('/:categoryKey/:algorithmKey/:fileName')
- .get(getFile);
+router.route('/:categoryKey/:algorithmKey')
+ .get(getAlgorithm);
export default router;
\ No newline at end of file
diff --git a/src/backend/public/algorithms b/src/backend/public/algorithms
index f17c57048f65f287d12e13e6f35b606034aeeb31..c9b0606bd6c80e02bc12c0215722cbbd9751965e 160000
--- a/src/backend/public/algorithms
+++ b/src/backend/public/algorithms
@@ -1 +1 @@
-Subproject commit f17c57048f65f287d12e13e6f35b606034aeeb31
+Subproject commit c9b0606bd6c80e02bc12c0215722cbbd9751965e
diff --git a/src/frontend/apis/index.js b/src/frontend/apis/index.js
index 7a3d67b54b69e3d4de760ef2c8d0a6a44be9b2ff..6916fb5cafb27fdd8c6748f5265ebac1ad636286 100644
--- a/src/frontend/apis/index.js
+++ b/src/frontend/apis/index.js
@@ -51,7 +51,7 @@ const PUT = URL => {
const HierarchyApi = {
getHierarchy: GET('/hierarchy'),
- getFile: GET('/hierarchy/:categoryKey/:algorithmKey/:fileName'),
+ getAlgorithm: GET('/hierarchy/:categoryKey/:algorithmKey'),
};
const WikiApi = {
@@ -62,6 +62,7 @@ const WikiApi = {
const GitHubApi = {
auth: token => gh = new GitHub({ token }),
getProfile: () => gh.getUser().getProfile(),
+ listGists: () => gh.getUser().listGists(),
};
export {
diff --git a/src/frontend/common/config.js b/src/frontend/common/config.js
index fce5e00e991da32064d87c0f7351e4db2d38c113..932f87f28d8b621a68e7f9a87c3d333b50f5f9bf 100644
--- a/src/frontend/common/config.js
+++ b/src/frontend/common/config.js
@@ -1,5 +1,10 @@
-const stepLimit = 1e6;
+const stepLimit = 1e6; // TODO: limit number of traces
+
+const CATEGORY_SCRATCH_PAPER = 'scratch-paper';
+const ALGORITHM_NEW = 'new';
export {
stepLimit,
+ CATEGORY_SCRATCH_PAPER,
+ ALGORITHM_NEW,
};
\ No newline at end of file
diff --git a/src/frontend/components/App/index.jsx b/src/frontend/components/App/index.jsx
index bc56c5f82da153d88aedce313ff62fd03e947992..a3db569086aedb8940946299135e39b8a92df156 100644
--- a/src/frontend/components/App/index.jsx
+++ b/src/frontend/components/App/index.jsx
@@ -3,7 +3,6 @@ import { connect } from 'react-redux';
import { loadProgressBar } from 'axios-progress-bar'
import { CodeEditor, DescriptionViewer, Header, Navigator, ToastContainer, WikiViewer, } from '/components';
import { Workspace, WSSectionContainer, WSTabContainer } from '/workspace/components';
-import { Section } from '/workspace/core';
import { actions as toastActions } from '/reducers/toast';
import { actions as envActions } from '/reducers/env';
import { GitHubApi, HierarchyApi } from '/apis';
@@ -26,11 +25,21 @@ class App extends React.Component {
constructor(props) {
super(props);
- this.spawnReference = Workspace.createReference();
- this.navigatorReference = Workspace.createReference();
+ this.workspaceRef = React.createRef();
+ this.navigator = null;
+
+ this.state = {
+ files: [],
+ codeFile: null,
+ descFile: null,
+ renderers: [],
+ };
}
componentDidMount() {
+ const workspace = this.workspaceRef.current;
+ this.navigator = workspace.findSectionById('navigator');
+
this.updateDirectory(this.props.match.params);
HierarchyApi.getHierarchy()
@@ -45,7 +54,7 @@ class App extends React.Component {
const { signedIn, accessToken } = this.props.env;
if (signedIn) GitHubApi.auth(accessToken);
- tracerManager.setOnRender(renderers => this.handleChangeRenderers(renderers));
+ tracerManager.setOnRender(renderers => this.setState({ renderers }));
tracerManager.setOnError(error => this.props.showErrorToast(error.message));
}
@@ -63,51 +72,37 @@ class App extends React.Component {
updateDirectory({ categoryKey = null, algorithmKey = null }) {
if (categoryKey && algorithmKey) {
this.props.setDirectory(categoryKey, algorithmKey);
+ HierarchyApi.getAlgorithm(categoryKey, algorithmKey)
+ .then(({ algorithm }) => {
+ const { files } = algorithm;
+ const codeFile = files.find(file => file.name === 'code.js') || null;
+ const descFile = files.find(file => file.name === 'desc.md') || null;
+ this.setState({ files, codeFile, descFile });
+ })
+ .catch(() => this.setState({ files: [] }));
}
}
- handleChangeRenderers(renderers) {
- const oldSections = this.rendererSections || {};
- const newSections = {};
- for (const renderer of renderers) {
- const { tracerKey, element } = renderer;
- let section = null;
- if (tracerKey in oldSections) {
- section = oldSections[tracerKey];
- section.setElement(element);
- delete oldSections[tracerKey];
- } else {
- section = new Section(element);
- this.spawnReference.core.addChild(section);
- }
- newSections[tracerKey] = section;
- }
- Object.values(oldSections).forEach(tab => tab.remove());
- this.rendererSections = newSections;
- }
-
render() {
- const { hierarchy, categoryKey, algorithmKey } = this.props.env;
-
- const navigatorOpened = true;
+ const { codeFile, descFile, renderers } = this.state;
- return hierarchy && categoryKey && algorithmKey && (
+ return (
-
+
this.navigatorReference.core.setVisible(!this.navigatorReference.core.visible)}
- navigatorOpened={navigatorOpened} />
+ onClickTitleBar={() => this.navigator.setVisible(!this.navigator.visible)}
+ navigatorOpened={true /* TODO: fix */} />
@@ -116,12 +111,13 @@ class App extends React.Component {
title: 'Visualization',
removable: false,
horizontal: false,
- reference: this.spawnReference
- }} />
+ }}>
+ {renderers}
+
-
-
+
+
diff --git a/src/frontend/components/App/stylesheet.scss b/src/frontend/components/App/stylesheet.scss
index 3a72a15cac79559ba0e28a6f9383e0278dc69e21..75d72851f76c02291b4629f34c25253fb3180e6d 100644
--- a/src/frontend/components/App/stylesheet.scss
+++ b/src/frontend/components/App/stylesheet.scss
@@ -15,7 +15,6 @@ body {
user-select: none;
color: $color-font;
font-size: $font-size-normal;
- background-color: $theme-normal;
}
a {
diff --git a/src/frontend/components/CodeEditor/index.jsx b/src/frontend/components/CodeEditor/index.jsx
index 0170e7488b9535c0c4a245e7022ddcf02898bd0d..2d51f3403c026010675bb615d220b6c1a9696a03 100644
--- a/src/frontend/components/CodeEditor/index.jsx
+++ b/src/frontend/components/CodeEditor/index.jsx
@@ -1,46 +1,33 @@
import React from 'react';
import AceEditor from 'react-ace';
-import { connect } from 'react-redux';
import 'brace/mode/javascript';
import 'brace/theme/tomorrow_night_eighties';
+import 'brace/ext/searchbox';
import { tracerManager } from '/core';
import { classes } from '/common/util';
import styles from './stylesheet.scss';
-import { HierarchyApi } from '/apis';
import { ContributorsViewer } from '/components';
-import { actions as envActions } from '/reducers/env';
-// TODO: code should not be reloaded when reopening tab
-@connect(
- ({ env }) => ({
- env
- }), {
- ...envActions
- }
-)
class CodeEditor extends React.Component {
constructor(props) {
super(props);
+ const { file } = props;
const { lineIndicator } = tracerManager;
this.state = {
lineMarker: this.createLineMarker(lineIndicator),
- file: null,
+ code: file && file.content,
};
}
componentDidMount() {
- const { categoryKey, algorithmKey } = this.props.env;
- this.loadFile(categoryKey, algorithmKey);
-
tracerManager.setOnUpdateLineIndicator(lineIndicator => this.setState({ lineMarker: this.createLineMarker(lineIndicator) }));
}
componentWillReceiveProps(nextProps) {
- const { categoryKey, algorithmKey } = nextProps.env;
- if (categoryKey !== this.props.env.categoryKey ||
- algorithmKey !== this.props.env.algorithmKey) {
- this.loadFile(categoryKey, algorithmKey);
+ const { file } = nextProps;
+ if (file !== this.props.file) {
+ this.handleChangeCode(file && file.content);
}
}
@@ -48,15 +35,6 @@ class CodeEditor extends React.Component {
tracerManager.setOnUpdateLineIndicator(null);
}
- loadFile(categoryKey, algorithmKey) {
- HierarchyApi.getFile(categoryKey, algorithmKey, 'code.js')
- .then(({ file }) => {
- this.setState({ file });
- tracerManager.setCode(file.content);
- })
- .catch(() => this.setState({ file: null }));
- }
-
createLineMarker(lineIndicator) {
if (lineIndicator === null) return null;
const { lineNumber, cursor } = lineIndicator;
@@ -73,14 +51,13 @@ class CodeEditor extends React.Component {
}
handleChangeCode(code) {
- const file = { ...this.state.file, content: code };
- this.setState({ file });
+ this.setState({ code });
tracerManager.setCode(code);
}
render() {
- const { lineMarker, file } = this.state;
- const { className, relativeWeight } = this.props;
+ const { className, file } = this.props;
+ const { lineMarker, code } = this.state;
return file && (
@@ -92,10 +69,9 @@ class CodeEditor extends React.Component {
editorProps={{ $blockScrolling: true }}
onChange={code => this.handleChangeCode(code)}
markers={lineMarker ? [lineMarker] : []}
- value={file.content}
- width={`${relativeWeight}`} />
+ value={code} />
-
// TODO: trick to update on resize
+ // TODO: need resizing when parent resizes
);
}
}
diff --git a/src/frontend/components/DescriptionViewer/index.jsx b/src/frontend/components/DescriptionViewer/index.jsx
index 9124bafa38ece61d38c81d6ee61928657ff64f0e..75201bde17e4223f382c23672465e3a14e620073 100644
--- a/src/frontend/components/DescriptionViewer/index.jsx
+++ b/src/frontend/components/DescriptionViewer/index.jsx
@@ -1,55 +1,14 @@
import React from 'react';
-import { connect } from 'react-redux';
-import { actions as envActions } from '/reducers/env';
-import { HierarchyApi } from '/apis/index';
import { ContributorsViewer, MarkdownViewer } from '/components';
import styles from './stylesheet.scss';
import { classes } from '/common/util';
-@connect(
- ({ env }) => ({
- env
- }), {
- ...envActions
- }
-)
class DescriptionViewer extends React.Component {
- constructor(props) {
- super(props);
-
- this.state = {
- file: null,
- };
- }
-
- componentDidMount() {
- const { categoryKey, algorithmKey } = this.props.env;
- const href = `/algorithm/${categoryKey}/${algorithmKey}`;
- this.loadFile(href);
- }
-
- componentWillReceiveProps(nextProps) {
- const { categoryKey, algorithmKey } = nextProps.env;
- if (categoryKey !== this.props.env.categoryKey ||
- algorithmKey !== this.props.env.algorithmKey) {
- const href = `/algorithm/${categoryKey}/${algorithmKey}`;
- this.loadFile(href);
- }
- }
-
- loadFile(href) {
- const [, , categoryKey, algorithmKey] = href.split('/');
- HierarchyApi.getFile(categoryKey, algorithmKey, 'desc.md')
- .then(({ file }) => this.setState({ file }))
- .catch(() => this.setState({ file: null }));
- }
-
render() {
- const { className } = this.props;
- const { file } = this.state;
+ const { className, file } = this.props;
return file && (
- this.loadFile(href)} />
+
);
diff --git a/src/frontend/components/Header/index.jsx b/src/frontend/components/Header/index.jsx
index 93ee1c5f41646f3efe2885bb00b1b6fad748663e..648480ae70d9b78b379b1611645980ef2d0573da 100644
--- a/src/frontend/components/Header/index.jsx
+++ b/src/frontend/components/Header/index.jsx
@@ -72,15 +72,23 @@ class Header extends React.Component {
const { className, onClickTitleBar, navigatorOpened } = this.props;
const { hierarchy, categoryKey, algorithmKey, signedIn } = this.props.env;
- const category = hierarchy.find(category => category.key === categoryKey);
- const algorithm = category.algorithms.find(algorithm => algorithm.key === algorithmKey);
+ let directory = ['Algorithm Visualizer'];
+ if (hierarchy && categoryKey && algorithmKey) {
+ const category = hierarchy.find(category => category.key === categoryKey);
+ const algorithm = category.algorithms.find(algorithm => algorithm.key === algorithmKey);
+ directory = [category.name, algorithm.name];
+ }
return (
diff --git a/src/frontend/components/MarkdownViewer/index.jsx b/src/frontend/components/MarkdownViewer/index.jsx
index f85b2c40bb91d559c6593af9a0b46389449d678a..f2d2e23298fa37d854165a43ecc6dd8685321bfb 100644
--- a/src/frontend/components/MarkdownViewer/index.jsx
+++ b/src/frontend/components/MarkdownViewer/index.jsx
@@ -8,7 +8,7 @@ class MarkdownViewer extends React.Component {
const { className, source, onClickLink } = this.props;
const link = ({ href, ...rest }) => {
- return /^https?:\/\//i.test(href) ? (
+ return !onClickLink || /^https?:\/\//i.test(href) ? (
) : (
onClickLink(href)} {...rest} />
diff --git a/src/frontend/components/Navigator/index.jsx b/src/frontend/components/Navigator/index.jsx
index d03fdd4956208fba16a69accd1793d39ef24a1d6..e3bae9dab3d636afca2b783f207c9883c3a5f213 100644
--- a/src/frontend/components/Navigator/index.jsx
+++ b/src/frontend/components/Navigator/index.jsx
@@ -8,29 +8,44 @@ import faGithub from '@fortawesome/fontawesome-free-brands/faGithub';
import { ExpandableListItem, ListItem } from '/components';
import { classes } from '/common/util';
import { actions as envActions } from '/reducers/env';
+import { actions as toastActions } from '/reducers/toast';
import styles from './stylesheet.scss';
+import { ALGORITHM_NEW, CATEGORY_SCRATCH_PAPER } from '/common/config';
@connect(
({ env }) => ({
env
}), {
- ...envActions
+ ...envActions,
+ ...toastActions,
}
)
class Navigator extends React.Component {
constructor(props) {
super(props);
- const { categoryKey } = this.props.env;
-
this.state = {
- categoriesOpened: {
- [categoryKey]: true,
- },
+ categoriesOpened: {},
+ scratchPaperOpened: false,
+ favoritesOpened: false,
query: '',
}
}
+ componentDidMount() {
+ const { categoryKey } = this.props.env;
+ if (categoryKey) {
+ this.toggleCategory(categoryKey, true);
+ }
+ }
+
+ componentWillReceiveProps(nextProps) {
+ const { categoryKey } = nextProps.env;
+ if (categoryKey) {
+ this.toggleCategory(categoryKey, true);
+ }
+ }
+
toggleCategory(key, categoryOpened = !this.state.categoriesOpened[key]) {
const categoriesOpened = {
...this.state.categoriesOpened,
@@ -39,6 +54,14 @@ class Navigator extends React.Component {
this.setState({ categoriesOpened });
}
+ toggleScratchPaper(scratchPaperOpened = !this.state.scratchPaperOpened) {
+ this.setState({ scratchPaperOpened });
+ }
+
+ toggleFavorites(favoritesOpened = !this.state.favoritesOpened) {
+ this.setState({ favoritesOpened });
+ }
+
handleChangeQuery(e) {
const { hierarchy } = this.props.env;
const categoriesOpened = {};
@@ -48,7 +71,6 @@ class Navigator extends React.Component {
categoriesOpened[category.key] = true;
}
});
-
this.setState({ categoriesOpened, query });
}
@@ -58,9 +80,9 @@ class Navigator extends React.Component {
}
render() {
- const { categoriesOpened, query } = this.state;
+ const { categoriesOpened, scratchPaperOpened, favoritesOpened, query } = this.state;
const { className, style } = this.props;
- const { hierarchy, categoryKey: selectedCategoryKey, algorithmKey: selectedAlgorithmKey } = this.props.env;
+ const { hierarchy, categoryKey, algorithmKey, signedIn } = this.props.env;
return (
);
diff --git a/src/frontend/core/tracerManager.jsx b/src/frontend/core/tracerManager.jsx
index 156f7619b28e5908d0b76c1f419bb12f4c756d35..9bd4a0c84559f8852a3ebd76fffb9cdcce88691f 100644
--- a/src/frontend/core/tracerManager.jsx
+++ b/src/frontend/core/tracerManager.jsx
@@ -103,10 +103,9 @@ class TracerManager {
}[className];
const data = new DataClass(options);
this.datas[tracerKey] = data;
- const renderer = {
- tracerKey,
- element:
- };
+ const renderer = (
+
+ );
this.renderers.push(renderer);
}
diff --git a/src/frontend/workspace/components/WSSectionContainer/index.jsx b/src/frontend/workspace/components/WSSectionContainer/index.jsx
index 4a8c8c212a3339710369c45136dac19a5482a760..bf8051a4743dad7448b3296bfc17a1fb18968e1b 100644
--- a/src/frontend/workspace/components/WSSectionContainer/index.jsx
+++ b/src/frontend/workspace/components/WSSectionContainer/index.jsx
@@ -9,7 +9,7 @@ class WSSectionContainer extends React.Component {
super(props);
const { core } = props;
- core.reference.component = this;
+ core.component = this;
this.core = core;
}
@@ -57,7 +57,7 @@ class WSSectionContainer extends React.Component {
visibleChildren.forEach((child, visibleIndex) => {
const index = children.indexOf(child);
elements.push(
- 0 && ((target, dx, dy) => this.handleResize(visibleIndex, target, dx, dy))}
onDropTab={tab => this.handleDropTabToContainer(tab, index)}
onDropSection={section => this.handleDropSectionToContainer(section, index)}
@@ -71,13 +71,13 @@ class WSSectionContainer extends React.Component {
};
if (children.length === 1) {
elements.push(
-
+
{child.element}
);
} else {
elements.push(
-
+
{
!child.fixed &&
this.handleDropTabToContainer(tab, index + 1)}
onDropSection={section => this.handleDropSectionToContainer(section, index + 1)}
disableDrop={child.fixed} />
diff --git a/src/frontend/workspace/components/WSTabContainer/index.jsx b/src/frontend/workspace/components/WSTabContainer/index.jsx
index be4632d0307badf6928bff3f345d4152b83d9a1a..e1f5a0e2453834f9803de88b7c80931404a792bd 100644
--- a/src/frontend/workspace/components/WSTabContainer/index.jsx
+++ b/src/frontend/workspace/components/WSTabContainer/index.jsx
@@ -22,7 +22,7 @@ class WSTabContainer extends React.Component {
super(props);
const { core } = props;
- core.reference.component = this;
+ core.component = this;
this.core = core;
}
diff --git a/src/frontend/workspace/components/Workspace/index.jsx b/src/frontend/workspace/components/Workspace/index.jsx
index 1e284dac3bb74ec088a3f6604e4f0959d71febc6..b4d989458181686bd6f65821c137ec53a5f40593 100644
--- a/src/frontend/workspace/components/Workspace/index.jsx
+++ b/src/frontend/workspace/components/Workspace/index.jsx
@@ -1,29 +1,82 @@
import React from 'react';
import { classes } from '/common/util';
import { WSSectionContainer } from '/workspace/components';
-import { SectionContainer } from '/workspace/core';
+import { Parent, SectionContainer } from '/workspace/core';
import styles from './stylesheet.scss';
class Workspace extends React.Component {
- static createReference() {
- return {
- core: null,
- component: null,
- };
- }
-
constructor(props) {
super(props);
- const { className, children, wsProps } = props;
- this.sectionContainer = new SectionContainer(
+ this.sectionContainer = new SectionContainer(this.getElement(props));
+ }
+
+ componentDidMount() {
+ this.handleChangeElement(this.props);
+ }
+
+ componentWillReceiveProps(nextProps) {
+ this.handleChangeElement(nextProps);
+ }
+
+ getElement(props) {
+ const { className, wsProps, ...rest } = props;
+ return (
- {children}
-
+ wsProps={{ id: 'workspace', removable: false, ...wsProps }} {...rest} />
);
}
+ handleChangeElement(props) {
+ const element = this.getElement(props);
+
+ const unmark = section => {
+ section.updated = false;
+ if (section instanceof Parent) {
+ section.children.forEach(unmark);
+ }
+ };
+ unmark(this.sectionContainer);
+
+ console.log('----');
+ const update = (element, parentSection) => {
+ const { children = [], wsProps = {} } = element.props;
+ const id = wsProps.id || `${parentSection.id}-${element.key}`;
+ if (id.startsWith('workspace-.1-.1-.1-')) console.log(id.slice('workspace-.1-.1-.1-'.length));
+ let section = this.findSectionById(id);
+ if (section) {
+ section.setElement(element);
+ } else {
+ section = parentSection.childify(React.cloneElement(element, { wsProps: { ...wsProps, id } }));
+ parentSection.addChild(section);
+ }
+ section.updated = true;
+ if (section instanceof Parent) {
+ React.Children.toArray(children).forEach(element => update(element, section));
+ }
+ };
+ update(element);
+
+ const removeUnmarked = section => {
+ if (!section.updated) {
+ section.remove();
+ } else if (section instanceof Parent) {
+ section.children.forEach(removeUnmarked);
+ }
+ };
+ removeUnmarked(this.sectionContainer);
+ }
+
+ findSectionById(id, section = this.sectionContainer) {
+ if (section.id === id) return section;
+ if (section instanceof Parent) {
+ for (const childSection of section.children) {
+ const foundSection = this.findSectionById(id, childSection);
+ if (foundSection) return foundSection;
+ }
+ }
+ }
+
render() {
return this.sectionContainer.element;
}
diff --git a/src/frontend/workspace/core/Child.js b/src/frontend/workspace/core/Child.js
deleted file mode 100644
index 7fe6138a871bff65b47233bd84cbbc8973acf830..0000000000000000000000000000000000000000
--- a/src/frontend/workspace/core/Child.js
+++ /dev/null
@@ -1,53 +0,0 @@
-import React from 'react';
-import uuid from 'uuid';
-import { Workspace } from '/workspace/components';
-
-class Child {
- getDefaultProps() {
- return {
- reference: Workspace.createReference(),
- removable: true,
- };
- }
-
- createElement(wsProps) {
- return (
-
- );
- }
-
- constructor(element) {
- if (!React.isValidElement(element)) {
- element = this.createElement(element);
- }
- const { wsProps = {} } = element.props;
- Object.assign(this, this.getDefaultProps(), wsProps);
- this.reference.core = this;
- this.key = uuid.v4();
- this.parent = null;
- this.init(element);
- }
-
- init(element) {
- this.element = React.cloneElement(element, { core: this });
- }
-
- setParent(parent) {
- if (this.parent) this.remove(true);
- this.parent = parent;
- }
-
- setElement(element) {
- this.init(element);
- this.parent.render();
- }
-
- remove(moving = false) {
- if (this.removable || moving) {
- const index = this.parent.findIndex(this.key);
- this.parent.removeChild(index);
- }
- }
-}
-
-export default Child;
\ No newline at end of file
diff --git a/src/frontend/workspace/core/Parent.js b/src/frontend/workspace/core/Parent.js
new file mode 100644
index 0000000000000000000000000000000000000000..75d918bd2b892eb039dc840d702e9794826c6660
--- /dev/null
+++ b/src/frontend/workspace/core/Parent.js
@@ -0,0 +1,44 @@
+import React from 'react';
+import { Section } from '/workspace/core';
+
+class Parent extends Section {
+ constructor(element) {
+ super(element);
+ this.children = [];
+ }
+
+ setElement(element) {
+ super.setElement(React.cloneElement(element, { core: this }));
+ }
+
+ childify(element) {
+ return new Section(element);
+ }
+
+ addChild(child, index = this.children.length) {
+ if (child.parent === this) {
+ const oldIndex = this.children.indexOf(child);
+ this.children[oldIndex] = null;
+ this.children.splice(index, 0, child);
+ this.children = this.children.filter(child => child);
+ } else {
+ this.children.splice(index, 0, child);
+ child.setParent(this);
+ }
+ }
+
+ removeChild(index) {
+ this.children.splice(index, 1);
+ if (this.children.length === 0) this.remove();
+ }
+
+ findIndex(child) {
+ return this.children.indexOf(child);
+ }
+
+ render() {
+ if (this.component) this.component.forceUpdate();
+ }
+}
+
+export default Parent;
\ No newline at end of file
diff --git a/src/frontend/workspace/core/Section.js b/src/frontend/workspace/core/Section.js
index 6b9dbff4547b5f053607e84de7d2fb27e87a712a..f601372c92cab85f6e7135d31b6a519d64a4c544 100644
--- a/src/frontend/workspace/core/Section.js
+++ b/src/frontend/workspace/core/Section.js
@@ -1,9 +1,10 @@
-import { Child } from '/workspace/core';
+import React from 'react';
-class Section extends Child {
+class Section {
getDefaultProps() {
return {
- ...super.getDefaultProps(),
+ id: null,
+ removable: true,
visible: true,
resizable: true,
weight: 1,
@@ -17,13 +18,37 @@ class Section extends Child {
}
constructor(element) {
- super(element);
+ if (!React.isValidElement(element)) {
+ element = this.createElement(element);
+ }
+ this.parent = null;
+ Object.assign(this, this.getDefaultProps());
+ this.setElement(element);
+ }
+
+ createElement(wsProps) {
+ return (
+
+ );
+ }
+
+ setElement(element) {
+ const { wsProps = {} } = element.props;
+ Object.assign(this, wsProps);
this.relative = this.size === -1;
+ this.element = element;
+ }
+
+ setParent(parent) {
+ if (this.parent) this.remove(true);
+ this.parent = parent;
}
- setVisible(visible) {
- this.visible = visible;
- this.parent.render();
+ remove(moving = false) {
+ if (this.removable || moving) {
+ const index = this.parent.findIndex(this);
+ this.parent.removeChild(index);
+ }
}
}
diff --git a/src/frontend/workspace/core/SectionContainer.js b/src/frontend/workspace/core/SectionContainer.js
index 3ecf541be81cb33e71e573f848e31a97207ede0f..44b79920b28c005d733060d9e3628099dd05cce0 100644
--- a/src/frontend/workspace/core/SectionContainer.js
+++ b/src/frontend/workspace/core/SectionContainer.js
@@ -1,9 +1,8 @@
import React from 'react';
-import { Section, TabContainer } from '/workspace/core';
-import { parentMixin } from '/workspace/core/mixins';
+import { Parent, Section, TabContainer } from '/workspace/core';
import { WSSectionContainer, WSTabContainer } from '/workspace/components';
-class SectionContainer extends parentMixin(Section) {
+class SectionContainer extends Parent {
getDefaultProps() {
return {
...super.getDefaultProps(),
@@ -32,7 +31,7 @@ class SectionContainer extends parentMixin(Section) {
super.removeChild(index);
if (this.removable && this.children.length === 1) {
const [child] = this.children;
- const index = this.parent.findIndex(this.key);
+ const index = this.parent.findIndex(this);
this.parent.addChild(child, index);
}
}
diff --git a/src/frontend/workspace/core/TabContainer.js b/src/frontend/workspace/core/TabContainer.js
index 6f78c1516870181de2e53738250afc9e0c138492..2c2267b9d7a17d5972a683c22eaa120218d6b93e 100644
--- a/src/frontend/workspace/core/TabContainer.js
+++ b/src/frontend/workspace/core/TabContainer.js
@@ -1,9 +1,8 @@
import React from 'react';
-import { Section, Tab } from '/workspace/core';
+import { Parent, Tab } from '/workspace/core';
import { WSTabContainer } from '/workspace/components';
-import { parentMixin } from '/workspace/core/mixins';
-class TabContainer extends parentMixin(Section) {
+class TabContainer extends Parent {
getDefaultProps() {
return {
...super.getDefaultProps(),
@@ -22,15 +21,13 @@ class TabContainer extends parentMixin(Section) {
}
addChild(child, index = this.children.length) {
- super.addChild(child, index, () => {
- this.setTabIndex(Math.min(index, this.children.length - 1));
- });
+ super.addChild(child, index);
+ this.setTabIndex(Math.min(index, this.children.length - 1));
}
removeChild(index) {
- super.removeChild(index, () => {
- this.setTabIndex(Math.min(this.tabIndex, this.children.length - 1));
- });
+ super.removeChild(index);
+ this.setTabIndex(Math.min(this.tabIndex, this.children.length - 1));
}
setTabIndex(tabIndex) {
diff --git a/src/frontend/workspace/core/draggingData.js b/src/frontend/workspace/core/draggingData.js
deleted file mode 100644
index 921764acff412cb0fa8e0a214e8638774bc6fc2e..0000000000000000000000000000000000000000
--- a/src/frontend/workspace/core/draggingData.js
+++ /dev/null
@@ -1,25 +0,0 @@
-import uuid from 'uuid';
-
-const key = 'dragging-data-id';
-
-class DraggingData {
- constructor() {
- this.id = null;
- this.data = null;
- }
-
- set(e, type, child) {
- this.id = uuid.v4();
- this.data = { type, child };
- e.dataTransfer.dropEffect = 'move';
- e.dataTransfer.setData(key, this.id);
- }
-
- get(e) {
- const id = e.dataTransfer.getData(key);
- if (id === this.id) return this.data;
- }
-}
-
-const draggingData = new DraggingData();
-export default draggingData;
\ No newline at end of file
diff --git a/src/frontend/workspace/core/index.js b/src/frontend/workspace/core/index.js
index 49aa770369f4fcfa38c36a9185c588e561e72718..62a7f4d6120b823a1de7c8efb4eb52b70413e2f7 100644
--- a/src/frontend/workspace/core/index.js
+++ b/src/frontend/workspace/core/index.js
@@ -1,6 +1,5 @@
-export { default as Child } from './Child';
-export { default as draggingData } from './draggingData';
export { default as Section } from './Section';
+export { default as Parent } from './Parent';
export { default as SectionContainer } from './SectionContainer';
+export { default as TabContainer } from './TabContainer';
export { default as Tab } from './Tab';
-export { default as TabContainer } from './TabContainer';
\ No newline at end of file
diff --git a/src/frontend/workspace/core/mixins/index.js b/src/frontend/workspace/core/mixins/index.js
deleted file mode 100644
index df009bf84bd2fcdbc8aedee14cb292422f5aa19d..0000000000000000000000000000000000000000
--- a/src/frontend/workspace/core/mixins/index.js
+++ /dev/null
@@ -1 +0,0 @@
-export { default as parentMixin } from './parentMixin';
\ No newline at end of file
diff --git a/src/frontend/workspace/core/mixins/parentMixin.js b/src/frontend/workspace/core/mixins/parentMixin.js
deleted file mode 100644
index abc55d93808877663b57f0ff48c473f8a07dd67d..0000000000000000000000000000000000000000
--- a/src/frontend/workspace/core/mixins/parentMixin.js
+++ /dev/null
@@ -1,47 +0,0 @@
-import React from 'react';
-import { Child } from '/workspace/core';
-
-const parentMixin = (Base = Child) => class Parent extends Base {
- constructor(element) {
- super(element);
- const { children = [] } = this.element.props;
- this.children = [];
- React.Children.forEach(children, element => this.addChild(this.childify(element)));
- }
-
- childify(element) {
- return new Child(element);
- }
-
- addChild(child, index = this.children.length, beforeRender) {
- if (child.parent === this) {
- const oldIndex = this.children.indexOf(child);
- this.children[oldIndex] = null;
- this.children.splice(index, 0, child);
- this.children = this.children.filter(child => child);
- } else {
- this.children.splice(index, 0, child);
- child.setParent(this);
- }
- if(beforeRender) beforeRender();
- this.render();
- }
-
- removeChild(index, beforeRender) {
- this.children.splice(index, 1);
- if (this.children.length === 0) this.remove();
- if(beforeRender) beforeRender();
- this.render();
- }
-
- findIndex(key) {
- return this.children.findIndex(child => child.key === key);
- }
-
- render() {
- const { component } = this.reference;
- if (component) component.forceUpdate();
- }
-};
-
-export default parentMixin;
\ No newline at end of file