Skip to content
This repository has been archived by the owner on Oct 23, 2023. It is now read-only.

Commit

Permalink
feat(store): save and load of pages, as well as preview
Browse files Browse the repository at this point in the history
  • Loading branch information
TheReincarnator authored and lkuechler committed Dec 11, 2017
1 parent a2c04a8 commit aee3caa
Show file tree
Hide file tree
Showing 11 changed files with 196 additions and 100 deletions.
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@
"react-dom": "^16.0.0",
"react-router": "^4.2.0",
"readts": "^0.1.0",
"serializr": "^1.1.14",
"styled-components": "^2.2.3",
"systemjs": "^0.20.19"
},
Expand Down
39 changes: 31 additions & 8 deletions src/component/app.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import { Chrome } from './container/chrome';
import { ipcRenderer, remote } from 'electron';
import { WebviewTag } from 'electron';
import { ElementList } from './container/element_list';
import { IconName, IconRegistry } from '../lsg/patterns/icons';
import Layout from '../lsg/patterns/layout';
import * as MobX from 'mobx';
import { observer } from 'mobx-react';
import DevTools from 'mobx-react-devtools';
import { Page } from '../store/page';
import { PatternList } from './container/pattern_list';
import { ProjectList } from './container/project_list';
import { PropertyList } from './container/property_list';
import * as React from 'react';
import * as ReactDom from 'react-dom';
import * as Serializr from 'serializr';
import { Store } from '../store';
import styledComponents from 'styled-components';
import TabNavigation, { TabNavigationItem } from '../lsg/patterns/tab-navigation';
Expand Down Expand Up @@ -57,6 +57,16 @@ class App extends React.Component<AppProps> {
this.handleTabNaviagtionClick = this.handleTabNaviagtionClick.bind(this);
}

public componentDidMount(): void {
console.log('mount');
// const webviewTag: WebviewTag = document.getElementById('preview') as WebviewTag;
window.setTimeout(() => {
console.log('load');
store.openStyleguide('../stacked-example');
store.openPage('my-project', 'mypage');
}, 1000);
}

public render(): JSX.Element {
// Todo: project and page don't update on page change
const project = this.props.store.getCurrentProject();
Expand Down Expand Up @@ -99,7 +109,7 @@ class App extends React.Component<AppProps> {
<PreviewPane
dangerouslySetInnerHTML={{
__html:
'<webview style="height: 100%;" src="./preview.html" partition="electron" nodeintegration />'
'<webview id="preview" style="height: 100%;" src="./preview.html" partition="electron" nodeintegration />'
}}
/>

Expand All @@ -121,12 +131,25 @@ class App extends React.Component<AppProps> {
}

const store = new Store();
store.openStyleguide('../stacked-example');
store.openPage('my-project', 'mypage');
store.setAppTitle(remote.getCurrentWindow().getTitle());

MobX.observe(store, () => {
ipcRenderer.send('update-preview', Serializr.serialize(store.getCurrentPage()));
MobX.observe(store, (change: MobX.IObjectChange) => {
const webviewTag: WebviewTag = document.getElementById('preview') as WebviewTag;
if (!webviewTag || !webviewTag.send) {
return;
}

if (change.object === store && change.name === 'styleGuidePath') {
webviewTag.send('open-styleguide', {
styleGuidePath: store.getStyleGuidePath()
});
} else {
const page: Page | undefined = store.getCurrentPage();
webviewTag.send('page-change', {
page: page ? page.toJson() : undefined,
pageId: page ? page.getPageId() : undefined,
projectId: page ? page.getProjectId() : undefined
});
}
});

ReactDom.render(<App store={store} />, document.getElementById('app'));
5 changes: 2 additions & 3 deletions src/component/container/chrome.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import * as React from 'react'
import { observer } from 'mobx-react';
import ChromeComponent from '../../lsg/patterns/chrome';

import { observer } from 'mobx-react';
import * as React from 'react';

export interface ChromeProps {
title: string;
Expand Down
20 changes: 11 additions & 9 deletions src/component/container/pattern_list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,20 @@ export class PatternList extends React.Component<PatternListProps> {
return <List headline="Patterns" items={items} />;
}

public createItemsFromFolder(parent: PatternFolder): ListPropsListItem[] {
public createItemsFromFolder(folder: PatternFolder): ListPropsListItem[] {
const result: ListPropsListItem[] = [];

parent.getChildren().forEach((child: PatternFolder) => {
const childItem: ListPropsListItem = { value: child.getName() };
childItem.children = this.createItemsFromFolder(child);
result.push(childItem);
});
if (folder) {
folder.getChildren().forEach((child: PatternFolder) => {
const childItem: ListPropsListItem = { value: child.getName() };
childItem.children = this.createItemsFromFolder(child);
result.push(childItem);
});

parent.getPatterns().forEach((pattern: Pattern) => {
result.push({ value: pattern.getName() });
});
folder.getPatterns().forEach((pattern: Pattern) => {
result.push({ value: pattern.getName() });
});
}

return result;
}
Expand Down
20 changes: 9 additions & 11 deletions src/component/presentation/preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@ export class Preview extends React.Component<PreviewProps> {
}

public render(): JSX.Element {
return this.createComponent(this.props.page) as JSX.Element;
if (this.props.page) {
return this.createComponent(this.props.page.getRoot()) as JSX.Element;
} else {
return <div>(empty)</div>;
}
}

/**
Expand All @@ -31,15 +35,7 @@ export class Preview extends React.Component<PreviewProps> {
* @returns A React component in case of a page element, the primitive in case of a primitive,
* or an array or object with values converted in the same manner, if an array resp. object is provided.
*/
private createComponent(value: PropertyValue, key?: string): React.Component | PropertyValue {
if (value instanceof Array) {
const array: (string | number)[] = value;
// Handle arrays by returning a new array with recursively processed elements.
return array.map((element: PropertyValue, index: number) =>
this.createComponent(element, String(index))
);
}

private createComponent(value: PropertyValue, key?: string): JSX.Element | PropertyValue {
if (value === undefined || value === null || typeof value !== 'object') {
// Primitives stay primitives.
return value;
Expand All @@ -65,7 +61,9 @@ export class Preview extends React.Component<PreviewProps> {
);
});

componentProps.children = this.createComponent(pageElement.getChildren());
componentProps.children = pageElement
.getChildren()
.map((child, index) => this.createComponent(child, String(index)));

// Then, load the pattern factory
const patternPath: string = pattern.getAbsolutePath();
Expand Down
43 changes: 27 additions & 16 deletions src/component/preview.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { ipcMain } from 'electron';
import { ipcRenderer } from 'electron';
import { JsonObject } from '../store/json';
import Layout from '../lsg/patterns/layout';
import { observer } from 'mobx-react';
import DevTools from 'mobx-react-devtools';
import { Page } from '../store/page';
import { Preview } from './presentation/preview';
import * as React from 'react';
import * as ReactDom from 'react-dom';
import * as Serializr from 'serializr';
import { Store } from '../store';
import styledComponents from 'styled-components';

const PreviewPane = styledComponents.div`
Expand All @@ -14,35 +16,44 @@ const PreviewPane = styledComponents.div`
box-shadow: inset 0 0 10px 0 rgba(0,0,0,.25);
`;

interface PreviewAppProps {
store: Store;
}

interface PreviewAppState {
page?: Page;
}

class PreviewApp extends React.Component<{}, PreviewAppState> {
public constructor(props: {}) {
@observer
class PreviewApp extends React.Component<PreviewAppProps, PreviewAppState> {
public constructor(props: PreviewAppProps) {
super(props);

this.state = {
page: undefined
};

ipcMain.on('update-preview', (event: {}, pageJson: string) => {
this.setState({
page: Serializr.deserialize({}, pageJson)
});
});
}

public render(): JSX.Element {
return (
<Layout>
<PreviewPane>
<Preview page={this.state.page} />
<Preview page={this.props.store.getCurrentPage()} />
</PreviewPane>
<DevTools />
</Layout>
);
}
}

ReactDom.render(<PreviewApp />, document.getElementById('app'));
const store = new Store();

ipcRenderer.on('page-change', (event: {}, message: JsonObject) => {
store.setPageFromJsonInternal(
message.page as JsonObject,
message.pageId as string,
message.propertyId as string
);
});

ipcRenderer.on('open-styleguide', (event: {}, message: JsonObject) => {
store.openStyleguide(message.styleGuidePath as string);
});

ReactDom.render(<PreviewApp store={store} />, document.getElementById('app'));
27 changes: 19 additions & 8 deletions src/store/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { PatternFolder } from './pattern/folder';
import * as FileUtils from 'fs';
import { JsonObject } from './json';
import * as MobX from 'mobx';
import { Page } from './page';
import { PageElement } from './page/page_element';
Expand All @@ -14,11 +15,6 @@ export class Store {
private patternRoot: PatternFolder;
@MobX.observable private selectedElement?: PageElement;
@MobX.observable private styleGuidePath: string;
@MobX.observable private appTitle: string;

public getAppTitle(): string {
return this.appTitle;
}

public getCurrentPage(): Page | undefined {
return this.currentPage;
Expand Down Expand Up @@ -98,13 +94,28 @@ export class Store {

public openPage(projectId: string, pageId: string): void {
MobX.transaction(() => {
this.currentPage = new Page(this, projectId, pageId);
const projectPath: string = PathUtils.join(this.getProjectsPath(), projectId);
const pagePath: string = PathUtils.join(projectPath, pageId + '.json');
const json: JsonObject = JSON.parse(FileUtils.readFileSync(pagePath, 'utf8'));
this.currentPage = Page.fromJson(json, projectId, pageId, this);

this.selectedElement = undefined;
});
}

public setAppTitle(title: string): void {
this.appTitle = title;
public savePage(): void {
if (!this.currentPage) {
throw new Error('Cannot save page: No page open');
}

this.currentPage.save();
}

public setPageFromJsonInternal(json: JsonObject, projectId: string, pageId: string): void {
MobX.transaction(() => {
this.currentPage = json ? Page.fromJson(json, projectId, pageId, this) : undefined;
this.selectedElement = undefined;
});
}

public setSelectedElement(selectedElement: PageElement): void {
Expand Down
15 changes: 15 additions & 0 deletions src/store/json.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export type JsonValue =
| string
| number
| boolean
| undefined
| null
| Date
| JsonObject
| JsonArray;

export interface JsonObject {
[key: string]: JsonValue;
}

export interface JsonArray extends Array<JsonValue> {}
29 changes: 18 additions & 11 deletions src/store/page/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as FileUtils from 'fs';
import { JsonObject } from '../json';
import { PageElement } from './page_element';
import * as PathUtils from 'path';
import { Store } from '..';
Expand All @@ -10,13 +11,19 @@ export class Page {
private root?: PageElement;
private store: Store;

public constructor(store: Store, projectId: string, pageId: string) {
this.store = store;
this.projectId = projectId;
public constructor(projectId: string, pageId: string, store: Store) {
this.pageId = pageId;
this.name = 'New page';
this.projectId = projectId;
this.store = store;
this.name = 'New Page';
}

public static fromJson(json: JsonObject, projectId: string, pageId: string, store: Store): Page {
const page = new Page(projectId, pageId, store);
page.name = json.name as string;
page.root = PageElement.fromJson(json.root as JsonObject, store);

this.reload();
return page;
}

public getName(): string {
Expand All @@ -35,14 +42,14 @@ export class Page {
return this.root as PageElement;
}

public reload(): void {
public save(): void {
const projectPath: string = PathUtils.join(this.store.getProjectsPath(), this.projectId);
const pagePath: string = PathUtils.join(projectPath, this.pageId + '.json');
const json: JsonObject = this.toJson();
FileUtils.writeFileSync(pagePath, json, 'utf8');
}

// tslint:disable-next-line:no-any
const pageModel: any = JSON.parse(FileUtils.readFileSync(pagePath, 'utf8'));

this.name = pageModel.name;
this.root = new PageElement(this.store, pageModel.root);
public toJson(): JsonObject {
return { name: this.name, root: this.root ? this.root.toJson() : undefined };
}
}
Loading

0 comments on commit aee3caa

Please sign in to comment.