From 4d92d5ba08bdece9e79e7f385f57afbbccda6702 Mon Sep 17 00:00:00 2001 From: "pengfei.zhou" Date: Fri, 25 Oct 2019 02:19:59 +0800 Subject: [PATCH] mvvm and candies --- js-framework/index.ts | 3 +- js-framework/src/util/candies.ts | 100 ++++++++++++++++++++++++++++++ js-framework/src/vm/mvvm.ts | 103 ++++++++++++------------------- 3 files changed, 142 insertions(+), 64 deletions(-) create mode 100644 js-framework/src/util/candies.ts diff --git a/js-framework/index.ts b/js-framework/index.ts index 66dcb3ff..39b32acf 100644 --- a/js-framework/index.ts +++ b/js-framework/index.ts @@ -19,5 +19,6 @@ export * from "./src/util/color" export * from './src/util/log' export * from './src/util/types' export * from './src/util/gravity' +export * from './src/util/candies' export * from './src/vm/mvvm' -export * from './src/runtime/global' \ No newline at end of file +export * from './src/runtime/global' diff --git a/js-framework/src/util/candies.ts b/js-framework/src/util/candies.ts new file mode 100644 index 00000000..4543b728 --- /dev/null +++ b/js-framework/src/util/candies.ts @@ -0,0 +1,100 @@ +/* + * Copyright [2019] [Doric.Pub] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { View, Stack, VLayout, HLayout } from "../ui/view" + +export type ViewBlock = () => View + +export function stack(blocks: ViewBlock[]) { + return takeAlso(new Stack)( + it => { + for (let block of blocks) { + it.addChild(block()) + } + }) +} + +export function vlayout(blocks: ViewBlock[]) { + return takeAlso(new VLayout)( + it => { + for (let block of blocks) { + it.addChild(block()) + } + }) +} + +export function hlayout(blocks: ViewBlock[]) { + return takeAlso(new HLayout)( + it => { + for (let block of blocks) { + it.addChild(block()) + } + }) +} + +export function take(target: T) { + return (block: (p: T) => void) => { + block(target) + } +} + +export function takeNonNull(target?: T) { + return (block: (p: T) => R) => { + if (target !== undefined) { + return block(target) + } + } +} + +export function takeNull(target?: T) { + return (block: () => R) => { + if (target === undefined) { + return block() + } + } +} + +export function takeLet(target: T) { + return (block: (p: T) => R | undefined) => { + return block(target) + } +} + +export function takeAlso(target: T) { + return (block: (p: T) => void) => { + block(target) + return target + } +} + +export function takeIf(target: T) { + return (predicate: (t: T) => boolean) => { + return predicate(target) ? target : undefined + } +} + +export function takeUnless(target: T) { + return (predicate: (t: T) => boolean) => { + return predicate(target) ? undefined : target + } +} + +export function repeat(action: (count: number) => void) { + return (times: number) => { + for (let i = 0; i < times; i++) { + action(i) + } + } +} \ No newline at end of file diff --git a/js-framework/src/vm/mvvm.ts b/js-framework/src/vm/mvvm.ts index 70feae8f..e31b4576 100644 --- a/js-framework/src/vm/mvvm.ts +++ b/js-framework/src/vm/mvvm.ts @@ -13,84 +13,61 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { View, Group } from "../ui/view"; +import { Group } from "../ui/view"; import { Panel } from "../ui/panel"; -function listen(obj: T, listener: Function): T { - return new Proxy(obj, { - get: (target, prop, receiver) => { - const ret = Reflect.get(target, prop, receiver) - if (ret instanceof Function) { - return Reflect.get(target, prop, receiver) - } else if (ret instanceof Object) { - return listen(ret, listener) - } else { - return ret - } - }, - - set: (target, prop, value, receiver) => { - const ret = Reflect.set(target, prop, value, receiver) - Reflect.apply(listener, undefined, []) - return ret - }, - }) -} - -export abstract class ViewHolder { +export abstract class ViewHolder{ abstract build(root: Group): void + abstract bind(state: M): void } -export abstract class VMPanel extends Panel { +export type Setter = (state: M) => void - private vm?: ViewModel +export abstract class ViewModel> { + private state: M + private viewHolder: V - abstract getVMClass(): new (m: M, v: V) => ViewModel + constructor(obj: M, v: V) { + this.state = obj + this.viewHolder = v + } + getState() { + return this.state + } - abstract getModel(): M + updateState(setter: Setter) { + setter(this.state) + this.viewHolder.bind(this.state) + } - abstract getViewHolder(): V + attach(view: Group) { + this.viewHolder.build(view) + } +} +export type ViewModelClass = new (m: M, v: ViewHolder) => ViewModel> - getVM() { +export type ViewHolderClass = new () => ViewHolder + +export abstract class VMPanel extends Panel { + + private vm?: ViewModel> + private vh?: ViewHolder + + abstract getViewModelClass(): ViewModelClass + + abstract getState(): M + + abstract getViewHolderClass(): ViewHolderClass + + getViewModel() { return this.vm } build(root: Group): void { - this.vm = new (this.getVMClass())(this.getModel(), this.getViewHolder()) - this.vm.build(root) + this.vh = new (this.getViewHolderClass()) + this.vm = new (this.getViewModelClass())(this.getState(), this.vh) + this.vm.attach(root) } } -export abstract class ViewModel { - private model: M - private listeners: Function[] = [] - private viewHolder: V - - constructor(obj: M, v: V) { - this.model = listen(obj, () => { - this.listeners.forEach(e => { - Reflect.apply(e, this.model, [this.model]) - }) - }) - this.viewHolder = v - } - - build(root: Group) { - this.viewHolder.build(root) - this.bind((data: M) => { - this.binding(this.viewHolder, data) - }) - } - - abstract binding(v: V, model: M): void - - getModel() { - return this.model - } - - bind(f: (data: M) => void) { - Reflect.apply(f, this.model, [this.model]) - this.listeners.push(f) - } -} \ No newline at end of file