From a961ab6c4a1de09f6cf198ca69465da13998e700 Mon Sep 17 00:00:00 2001 From: Thomas Jacob Date: Thu, 7 Dec 2017 14:42:05 +0100 Subject: [PATCH] feat(store): property type system, setter, and coercing --- src/component/container/element_list.tsx | 2 +- src/component/presentation/preview.tsx | 2 +- src/store/index.ts | 68 ++++++++++--------- src/store/page/page_element.ts | 11 +-- src/store/pattern/folder/index.ts | 63 +++++++++-------- src/store/pattern/index.ts | 19 ++---- .../pattern/property/boolean_property.ts | 2 +- src/store/pattern/property/index.ts | 4 +- src/store/project/index.ts | 7 +- 9 files changed, 89 insertions(+), 89 deletions(-) diff --git a/src/component/container/element_list.tsx b/src/component/container/element_list.tsx index 00fdfd270..8693c9996 100644 --- a/src/component/container/element_list.tsx +++ b/src/component/container/element_list.tsx @@ -65,7 +65,7 @@ export class ElementList extends React.Component { } public createItemFromProperty(key: string, value: PropertyValue): ListPropsListItem { - if (Array.isArray(value)) { + if (value instanceof Array) { const items: ListPropsListItem[] = []; (value as (string | number)[]).forEach((child, index: number) => { items.push(this.createItemFromProperty(String(index + 1), child)); diff --git a/src/component/presentation/preview.tsx b/src/component/presentation/preview.tsx index e6a0a580e..99e08a5d2 100644 --- a/src/component/presentation/preview.tsx +++ b/src/component/presentation/preview.tsx @@ -34,7 +34,7 @@ export class Preview extends React.Component { * 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 (Array.isArray(value)) { + 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) => diff --git a/src/store/index.ts b/src/store/index.ts index 51c824168..7cf37a44c 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -1,6 +1,6 @@ import { PatternFolder } from './pattern/folder'; import * as FileUtils from 'fs'; -import { observable } from 'mobx'; +import * as MobX from 'mobx'; import { Page } from './page'; import { PageElement } from './page/page_element'; import { PageRef } from './page/page_ref'; @@ -9,11 +9,11 @@ import { Pattern } from './pattern'; import { Project } from './project'; export class Store { - @observable private currentPage?: Page; - @observable private selectedElement?: PageElement; - private projects: Project[] = []; + @MobX.observable private currentPage?: Page; + @MobX.observable private projects: Project[] = []; private patternRoot: PatternFolder; - @observable private styleGuidePath: string; + @MobX.observable private selectedElement?: PageElement; + @MobX.observable private styleGuidePath: string; public getCurrentPage(): Page | undefined { return this.currentPage; @@ -48,39 +48,43 @@ export class Store { } public openStyleguide(styleGuidePath: string): void { - if (!PathUtils.isAbsolute(styleGuidePath)) { - // Currently, store is two levels below stacked, so go two up - styleGuidePath = PathUtils.join(__dirname, '..', '..', styleGuidePath); - } - this.styleGuidePath = styleGuidePath; - this.currentPage = undefined; - this.projects = []; - this.patternRoot = new PatternFolder(this, 'patterns'); + MobX.transaction(() => { + if (!PathUtils.isAbsolute(styleGuidePath)) { + // Currently, store is two levels below stacked, so go two up + styleGuidePath = PathUtils.join(__dirname, '..', '..', styleGuidePath); + } + this.styleGuidePath = styleGuidePath; + this.currentPage = undefined; + this.projects = []; + this.patternRoot = new PatternFolder(this, 'patterns'); - const projects: Project[] = []; + const projects: Project[] = []; - const projectsPath = this.getProjectsPath(); - FileUtils.readdirSync(projectsPath) - .map((name: string) => ({ name, path: PathUtils.join(projectsPath, name) })) - .filter(child => FileUtils.lstatSync(child.path).isDirectory()) - .forEach(folder => { - const pages: PageRef[] = []; - FileUtils.readdirSync(folder.path) - .filter(child => child.match(/\.json$/)) - .forEach(file => { - const pageId: string = file.replace(/\.json$/, ''); - const pageName: string = file.replace(/\.json$/, ''); - pages.push(new PageRef(pageId, pageName)); - }); + const projectsPath = this.getProjectsPath(); + FileUtils.readdirSync(projectsPath) + .map((name: string) => ({ name, path: PathUtils.join(projectsPath, name) })) + .filter(child => FileUtils.lstatSync(child.path).isDirectory()) + .forEach(folder => { + const pages: PageRef[] = []; + FileUtils.readdirSync(folder.path) + .filter(child => child.match(/\.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; + projects.push(new Project(folder.name, folder.name, pages)); + }); + this.projects = projects; + }); } public openPage(projectId: string, pageId: string): void { - this.currentPage = new Page(this, projectId, pageId); - this.selectedElement = undefined; + MobX.transaction(() => { + this.currentPage = new Page(this, projectId, pageId); + this.selectedElement = undefined; + }); } public setSelectedElement(selectedElement: PageElement): void { diff --git a/src/store/page/page_element.ts b/src/store/page/page_element.ts index 16dc92fb2..068ee9f18 100644 --- a/src/store/page/page_element.ts +++ b/src/store/page/page_element.ts @@ -1,13 +1,14 @@ +import * as MobX from 'mobx'; import { Pattern } from '../pattern'; import { Property } from '../pattern/property'; import { PropertyValue } from './property_value'; import { Store } from '..'; export class PageElement { - private children: PageElement[] = []; + @MobX.observable private children: PageElement[] = []; private patternPath: string; private pattern?: Pattern; - private propertyValues: { [id: string]: PropertyValue } = {}; + @MobX.observable private propertyValues: Map = new Map(); // tslint:disable-next-line:no-any public constructor(json: any, store: Store) { @@ -57,7 +58,9 @@ export class PageElement { } public getPropertyValue(id: string): PropertyValue { - return this.propertyValues[id]; + const value: PropertyValue = this.propertyValues.get(id); + + return value; } // tslint:disable-next-line:no-any @@ -72,6 +75,6 @@ export class PageElement { return; } - this.propertyValues[id] = property.coerceValue(value); + this.propertyValues.set(id, value); } } diff --git a/src/store/pattern/folder/index.ts b/src/store/pattern/folder/index.ts index 1441d6e5f..6899461d2 100644 --- a/src/store/pattern/folder/index.ts +++ b/src/store/pattern/folder/index.ts @@ -1,15 +1,14 @@ import * as FileUtils from 'fs'; +import * as MobX from 'mobx'; import * as PathUtils from 'path'; import { Pattern } from '..'; import { Store } from '../..'; export class PatternFolder { - private children: PatternFolder[]; - private childrenByName: { [name: string]: PatternFolder } = {}; + @MobX.observable private children: Map = new Map(); private name: string; private parent?: PatternFolder; - private patterns: Pattern[]; - private patternsByName: { [id: string]: Pattern } = {}; + @MobX.observable private patterns: Map = new Map(); private store: Store; public constructor(store: Store, name: string, parent?: PatternFolder) { @@ -27,11 +26,11 @@ export class PatternFolder { public getChild(path: string): PatternFolder | undefined { const slashPos: number = path.indexOf('/'); if (slashPos < 0) { - return this.childrenByName[path]; + return this.children.get(path); } const folderName: string = path.substring(0, slashPos); - const folder: PatternFolder | undefined = this.childrenByName[folderName]; + const folder: PatternFolder | undefined = this.children.get(folderName); if (!folder) { return undefined; } @@ -41,7 +40,7 @@ export class PatternFolder { } public getChildren(): PatternFolder[] { - return this.children; + return Array.from(this.children.values()); } public getName(): string { @@ -53,17 +52,17 @@ export class PatternFolder { } public getPatterns(): Pattern[] { - return this.patterns; + return Array.from(this.patterns.values()); } public getPattern(path: string): Pattern | undefined { const slashPos: number = path.indexOf('/'); if (slashPos < 0) { - return this.patternsByName[path]; + return this.patterns.get(path); } const folderName: string = path.substring(0, slashPos); - const folder: PatternFolder | undefined = this.childrenByName[folderName]; + const folder: PatternFolder | undefined = this.children.get(folderName); if (!folder) { return undefined; } @@ -81,30 +80,30 @@ export class PatternFolder { } public reload(): void { - this.patterns = []; - this.children = []; - - const parentPath: string = this.getAbsolutePath(); - FileUtils.readdirSync(parentPath).forEach(childName => { - const childPath = PathUtils.join(parentPath, childName); - if (FileUtils.lstatSync(childPath).isDirectory()) { - if ( - FileUtils.existsSync(PathUtils.join(childPath, 'index.d.ts')) && - FileUtils.existsSync(PathUtils.join(childPath, 'index.js')) - ) { - const pattern: Pattern = new Pattern(this, childName); - if (pattern.isValid()) { - this.patterns.push(pattern); - this.patternsByName[childName] = pattern; - } - } else { - const childFolder: PatternFolder = new PatternFolder(this.store, childName, this); - if (childFolder.patterns.length || childFolder.children.length) { - this.children.push(childFolder); - this.childrenByName[childName] = childFolder; + MobX.transaction(() => { + this.patterns.clear(); + this.children.clear(); + + const parentPath: string = this.getAbsolutePath(); + FileUtils.readdirSync(parentPath).forEach(childName => { + const childPath = PathUtils.join(parentPath, childName); + if (FileUtils.lstatSync(childPath).isDirectory()) { + if ( + FileUtils.existsSync(PathUtils.join(childPath, 'index.d.ts')) && + FileUtils.existsSync(PathUtils.join(childPath, 'index.js')) + ) { + const pattern: Pattern = new Pattern(this, childName); + if (pattern.isValid()) { + this.patterns.set(childName, pattern); + } + } else { + const childFolder: PatternFolder = new PatternFolder(this.store, childName, this); + if (childFolder.patterns.size > 0 || childFolder.children.size > 0) { + this.children.set(childName, childFolder); + } } } - } + }); }); } } diff --git a/src/store/pattern/index.ts b/src/store/pattern/index.ts index 15088a106..f3cefebc7 100644 --- a/src/store/pattern/index.ts +++ b/src/store/pattern/index.ts @@ -20,12 +20,7 @@ export class Pattern { /** * The properties this pattern supports. */ - private properties: Property[]; - - /** - * The properties this pattern supports. - */ - private propertiesById: { [id: string]: Property } = {}; + private properties: Map = new Map(); /** * This is a valid pattern for Stacked (has been parsed successfully). @@ -52,11 +47,11 @@ export class Pattern { } public getProperties(): Property[] { - return this.properties; + return Array.from(this.properties.values()); } public getProperty(id: string): Property | undefined { - return this.propertiesById[id]; + return this.properties.get(id); } public getRelativePath(): string { @@ -73,14 +68,12 @@ export class Pattern { } this.valid = false; - this.properties = []; + this.properties.clear(); Pattern.parsers.some(parser => { const result: Property[] | undefined = parser.parse(this); if (result) { - this.properties = result; - this.propertiesById = {}; - this.properties.forEach(pattern => { - this.propertiesById[pattern.getId()] = pattern; + result.forEach(property => { + this.properties.set(property.getId(), property); }); this.valid = true; diff --git a/src/store/pattern/property/boolean_property.ts b/src/store/pattern/property/boolean_property.ts index 22970607b..d015fcd1e 100644 --- a/src/store/pattern/property/boolean_property.ts +++ b/src/store/pattern/property/boolean_property.ts @@ -7,7 +7,7 @@ export class BooleanProperty extends Property { // tslint:disable-next-line:no-any public coerceValue(value: any): any { - return value && (value === true || value === 'true' || value === 1); + return value === true || value === 'true' || value === 1; } public getType(): string { diff --git a/src/store/pattern/property/index.ts b/src/store/pattern/property/index.ts index 9d178ceb4..3a111c724 100644 --- a/src/store/pattern/property/index.ts +++ b/src/store/pattern/property/index.ts @@ -11,7 +11,7 @@ export abstract class Property { // tslint:disable-next-line:no-any protected arraysAndEqual(value1: any, value2: any): boolean { - if (!Array.isArray(value1) || !Array.isArray(value2)) { + if (!(value1 instanceof Array) || !(value2 instanceof Array)) { return false; } @@ -36,7 +36,7 @@ export abstract class Property { protected coerceArrayValue(value: any, elementCoercion: (value: any) => any): any { // tslint:disable-next-line:no-any let result: any[]; - if (Array.isArray(value)) { + if (value instanceof Array) { result = value; } else { result = [value]; diff --git a/src/store/project/index.ts b/src/store/project/index.ts index a933c9ebb..a8c3dbbeb 100644 --- a/src/store/project/index.ts +++ b/src/store/project/index.ts @@ -1,9 +1,10 @@ +import * as MobX from 'mobx'; import { PageRef } from '../page/page_ref'; export class Project { - private id: string; - private name: string; - private pages: PageRef[]; + @MobX.observable private id: string; + @MobX.observable private name: string; + @MobX.observable private pages: PageRef[] = []; public constructor(id: string, name: string, pages: PageRef[]) { this.id = id;