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

Commit

Permalink
feat(store): continue store API and pattern resolution
Browse files Browse the repository at this point in the history
  • Loading branch information
TheReincarnator committed Dec 6, 2017
1 parent a105812 commit ca4216d
Show file tree
Hide file tree
Showing 16 changed files with 380 additions and 156 deletions.
2 changes: 1 addition & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ curly_bracket_next_line = false
end_of_line = lf
indent_style = tab
insert_final_newline = true
max_line_length = 120
max_line_length = 100
spaces_around_brackets = false
spaces_around_operators = true
trim_trailing_whitespace = true
36 changes: 23 additions & 13 deletions src/component/container/element_list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { List, ListPropsListItem } from '../presentation/list';
import { observer } from 'mobx-react';
import { Page } from '../../store/page';
import { PageElement } from '../../store/page/page_element';
import { Pattern } from '../../store/pattern';
import { Property } from '../../store/pattern/property';
import * as React from 'react';
import { Store } from '../../store';

Expand All @@ -17,9 +19,9 @@ export class ElementList extends React.Component<ElementListProps> {
}

public render(): JSX.Element | null {
const page: Page | undefined = this.props.store.currentPage;
const page: Page | undefined = this.props.store.getCurrentPage();
if (page) {
const rootElement: PageElement = page.root as PageElement;
const rootElement: PageElement = page.getRoot() as PageElement;
return (
<List headline="Elements" items={[this.createItemFromElement('Root', rootElement)]} />
);
Expand All @@ -29,12 +31,22 @@ export class ElementList extends React.Component<ElementListProps> {
}

public createItemFromElement(key: string, element: PageElement): ListPropsListItem {
if (!element.getPattern()) {
return {
label: key,
value: '(invalid)',
children: []
};
}

const items: ListPropsListItem[] = [];
const properties: { [name: string]: ElementValue } = element.properties || {};
Object.keys(properties).forEach((propertyKey: string) => {
items.push(this.createItemFromProperty(propertyKey, properties[propertyKey]));
const pattern: Pattern = element.getPattern() as Pattern;
const properties: Property[] = pattern.getProperties() || {};
properties.forEach(property => {
const propertyId: string = property.getId();
items.push(this.createItemFromProperty(propertyId, element.getProperty(propertyId)));
});
const children: PageElement[] = element.children || [];
const children: PageElement[] = element.getChildren() || [];
children.forEach((value: PageElement, index: number) => {
items.push(
this.createItemFromProperty(
Expand All @@ -44,11 +56,11 @@ export class ElementList extends React.Component<ElementListProps> {
);
});

const patternSrc: string = element.patternSrc as string;
const patternPath: string = element.getPatternPath() as string;

return {
label: key,
value: patternSrc.replace(/^.*\//, ''),
value: patternPath.replace(/^.*\//, ''),
children: items
};
}
Expand All @@ -66,15 +78,13 @@ export class ElementList extends React.Component<ElementListProps> {
return { label: key, value: String(value) };
}

// tslint:disable-next-line:no-any
const valueAsAny: any = value as any;

if (valueAsAny['_type'] === 'pattern') {
if (value instanceof PageElement) {
return this.createItemFromElement(key, value as PageElement);
} else {
const items: ListPropsListItem[] = [];
Object.keys(value).forEach((childKey: string) => {
items.push(this.createItemFromProperty(childKey, valueAsAny[childKey]));
// tslint:disable-next-line:no-any
items.push(this.createItemFromProperty(childKey, (value as any)[childKey]));
});
return { value: key, children: items };
}
Expand Down
12 changes: 7 additions & 5 deletions src/component/container/pattern_list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,23 @@ export class PatternList extends React.Component<PatternListProps> {
}

public render(): JSX.Element {
const items: ListPropsListItem[] = this.createItemsFromFolder(this.props.store.patterns);
const items: ListPropsListItem[] = this.createItemsFromFolder(
this.props.store.getPatternRoot() as PatternFolder
);
return <List headline="Patterns" items={items} />;
}

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

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

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

return result;
Expand Down
10 changes: 5 additions & 5 deletions src/component/container/project_list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@ export class ProjectList extends React.Component<ProjectListProps> {
}

public render(): JSX.Element {
const items: ListPropsListItem[] = this.props.store.projects.map((project: Project) => ({
const items: ListPropsListItem[] = this.props.store.getProjects().map((project: Project) => ({
label: 'Project',
value: project.name,
children: project.pages.map((page: PageRef) => ({
value: project.getName(),
children: project.getPages().map((page: PageRef) => ({
label: 'Page',
value: page.name,
value: page.getName(),
children: [],
onClick: (event: React.MouseEvent<HTMLElement>) => {
this.props.store.openPage(project.id, page.id);
this.props.store.openPage(project.getId(), page.getId());
}
}))
}));
Expand Down
49 changes: 25 additions & 24 deletions src/component/presentation/preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { ElementValue } from '../../store/page/element_value';
import { observer } from 'mobx-react';
import { Page } from '../../store/page';
import { PageElement } from '../../store/page/page_element';
import * as PathUtils from 'path';
import { Pattern } from '../../store/pattern';
import * as React from 'react';
import { Store } from '../../store';

Expand All @@ -20,17 +20,17 @@ export class Preview extends React.Component<PreviewProps> {
}

public render(): JSX.Element {
const page: Page | undefined = this.props.store.currentPage;
return this.createComponent(page ? page.root : undefined) as JSX.Element;
const page: Page | undefined = this.props.store.getCurrentPage();
return this.createComponent(page ? page.getRoot() : undefined) as JSX.Element;
}

/**
* Converts a JSON-serializable declaration of a pattern, primitive, or collection
* into a React component (or primitive), deep-traversing through properties and children.
* @param value The value, may be a page element object with 'patternSrc' property (a pattern declaration),
* @param value The value, may be a page element (a pattern declaration),
* a primitive like a string, number, boolean, null, etc.,
* or an array or object of such values.
* @returns A React component in case of a pattern declaration, the primitive in case of a primitive,
* @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: ElementValue, key?: string): React.Component | ElementValue {
Expand All @@ -47,32 +47,32 @@ export class Preview extends React.Component<PreviewProps> {
return value;
}

// tslint:disable-next-line:no-any
const valueAsAny: any = value as any;

if (valueAsAny['_type'] === 'pattern') {
// The model is a pattern declaration, create a React pattern component
if (value instanceof PageElement) {
// The model is a page element, create a React pattern component

// First, process the properties and children of the declaration recursively
const pageElement: PageElement = value as PageElement;
if (!pageElement.getPattern()) {
return null;
}

const pattern: Pattern = pageElement.getPattern() as Pattern;

// tslint:disable-next-line:no-any
const componentProps: any = this.createComponent(pageElement.properties) || {};
componentProps.children = this.createComponent(pageElement.children);
const componentProps: any = {};
pattern.getProperties().forEach(property => {
componentProps[property.getId()] = pageElement.getProperty(property.getId());
});

componentProps.children = this.createComponent(pageElement.getChildren());

// Then, load the pattern factory
const patternFolder: string = PathUtils.join(
this.props.store.styleGuidePath,
'lib',
'patterns',
pageElement.patternSrc || ''
);
const patternSrc: string = PathUtils.join(patternFolder, 'index.js');
let patternFactory: React.StatelessComponent = this.patternFactories[patternFolder];
const patternPath: string = pattern.getAbsolutePath();
let patternFactory: React.StatelessComponent = this.patternFactories[patternPath];
if (patternFactory == null) {
console.log(`Loading pattern "${patternSrc}"...`);
patternFactory = require(patternSrc).default;
this.patternFactories[patternFolder] = patternFactory;
console.log(`Loading pattern "${patternPath}"...`);
patternFactory = require(patternPath).default;
this.patternFactories[patternPath] = patternFactory;
}

// Finally, build the component
Expand All @@ -84,7 +84,8 @@ export class Preview extends React.Component<PreviewProps> {
// tslint:disable-next-line:no-any
const result: any = {};
Object.keys(value).forEach(objectKey => {
result[objectKey] = this.createComponent(valueAsAny[objectKey]);
// tslint:disable-next-line:no-any
result[objectKey] = this.createComponent((value as any)[objectKey]);
});
return result;
}
Expand Down
61 changes: 46 additions & 15 deletions src/store/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,45 @@ import { PatternFolder } from './pattern/folder';
import * as FileUtils from 'fs';
import { observable } from 'mobx';
import { Page } from './page';
import { PageRef } from './page/page_ref';
import * as PathUtils from 'path';
import { Pattern } from './pattern';
import { Project } from './project';

export class Store {
@observable public currentPage?: Page;
public projects: Project[];
public patterns: PatternFolder;
@observable public styleGuidePath: string;
@observable private currentPage?: Page;
private projects: Project[] = [];
private patternRoot: PatternFolder;
@observable private styleGuidePath: string;

public getCurrentPage(): Page | undefined {
return this.currentPage;
}

public getPattern(path: string): Pattern | undefined {
return this.patternRoot.getPattern(path);
}

public getPatternsPath(): string {
return PathUtils.join(this.styleGuidePath, 'lib');
}

public getPatternRoot(): PatternFolder | undefined {
return this.patternRoot;
}

public getProjects(): Project[] {
return this.projects;
}

public getProjectsPath(): string {
return PathUtils.join(this.styleGuidePath, 'stacked', 'projects');
}

public getStyleGuidePath(): string {
return this.styleGuidePath;
}

public openStyleguide(styleGuidePath: string): void {
if (!PathUtils.isAbsolute(styleGuidePath)) {
// Currently, store is two levels below stacked, so go two up
Expand All @@ -23,22 +49,27 @@ export class Store {
this.styleGuidePath = styleGuidePath;
this.currentPage = undefined;
this.projects = [];
this.patterns = new PatternFolder(this, 'patterns');
this.patternRoot = new PatternFolder(this, 'patterns');

const projects: Project[] = [];

const projectsPath = this.getProjectsPath();
this.projects = FileUtils.readdirSync(projectsPath)
FileUtils.readdirSync(projectsPath)
.map((name: string) => ({ name, path: PathUtils.join(projectsPath, name) }))
.filter(child => FileUtils.lstatSync(child.path).isDirectory())
.map(folder => ({
id: folder.name,
name: folder.name,
pages: FileUtils.readdirSync(folder.path)
.forEach(folder => {
const pages: PageRef[] = [];
FileUtils.readdirSync(folder.path)
.filter(child => child.match(/\.json$/))
.map(file => ({
id: file.replace(/\.json$/, ''),
name: file.replace(/\.json$/, '')
}))
}));
.forEach(file => {
const pageId: string = file.replace(/\.json$/, '');
const pageName: string = file.replace(/\.json$/, '');
pages.push(new PageRef(pageId, pageName));
});

projects.push(new Project(folder.name, folder.name, pages));
});
this.projects = projects;
}

public openPage(projectId: string, pageId: string): void {
Expand Down
32 changes: 24 additions & 8 deletions src/store/page/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,45 @@ import * as PathUtils from 'path';
import { Store } from '..';

export class Page {
public name: string;
public pageId: string;
public projectId: string;
public root?: PageElement;
public store: Store;
private name: string;
private pageId: string;
private projectId: string;
private root?: PageElement;
private store: Store;

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

this.load();
this.reload();
}

public load(): void {
public getName(): string {
return this.name;
}

public getPageId(): string {
return this.pageId;
}

public getProjectId(): string {
return this.projectId;
}

public getRoot(): PageElement {
return this.root as PageElement;
}

public reload(): void {
const projectPath: string = PathUtils.join(this.store.getProjectsPath(), this.projectId);
const pagePath: string = PathUtils.join(projectPath, this.pageId + '.json');

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

this.name = pageModel.name;
this.root = pageModel.root;
this.root = new PageElement(pageModel.root, this.store);
}
}
Loading

0 comments on commit ca4216d

Please sign in to comment.