diff --git a/doric-js/.gitignore b/doric-js/.gitignore index 434bd5c6..f2d09a83 100644 --- a/doric-js/.gitignore +++ b/doric-js/.gitignore @@ -1,5 +1,3 @@ node_modules/ -build/ -demo/ .DS_Store package-lock.json \ No newline at end of file diff --git a/doric-js/lib/index.d.ts b/doric-js/lib/index.d.ts new file mode 100644 index 00000000..e27a74c3 --- /dev/null +++ b/doric-js/lib/index.d.ts @@ -0,0 +1,6 @@ +export * from './src/runtime/global'; +export * from './src/ui/index.ui'; +export * from "./src/widget/index.widget"; +export * from './src/native/index.native'; +export * from "./src/util/index.util"; +export * from "./src/pattern/index.pattern"; diff --git a/doric-js/lib/index.debug.d.ts b/doric-js/lib/index.debug.d.ts new file mode 100644 index 00000000..ea465c2a --- /dev/null +++ b/doric-js/lib/index.debug.d.ts @@ -0,0 +1 @@ +export * from './index'; diff --git a/doric-js/lib/index.debug.js b/doric-js/lib/index.debug.js new file mode 100644 index 00000000..09ce236b --- /dev/null +++ b/doric-js/lib/index.debug.js @@ -0,0 +1,108 @@ +/* + * 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 * as doric from './src/runtime/sandbox'; +import * as WebSocket from 'ws'; +const WebSocketClient = require('ws'); +const fs = require('fs'); +let context = process.cwd() + '/build/context'; +const contextId = fs.readFileSync(context, { encoding: 'utf8' }); +console.log("debugging context id: " + contextId); +let global = new Function('return this')(); +global.doric = doric; +global.context = doric.jsObtainContext(contextId); +global.Entry = doric.jsObtainEntry(contextId); +// dev kit client +const devClient = new WebSocketClient('ws://localhost:7777'); +devClient.on('open', function open() { + console.log('dev kit connected on 7777'); +}); +devClient.on('message', function incoming(data) { + console.log(data); +}); +devClient.on('error', function incoming(error) { + console.log(error); +}); +// debug server +const debugServer = new WebSocket.Server({ port: 2080 }); +debugServer.on('connection', function connection(ws) { + console.log('connected'); + ws.on('message', function incoming(message) { + let messageObject = JSON.parse(message); + switch (messageObject.cmd) { + case "injectGlobalJSFunction": + console.log(messageObject.name); + Reflect.set(global, messageObject.name, function () { + let args = [].slice.call(arguments); + console.log("==============================="); + console.log(args); + console.log("==============================="); + ws.send(JSON.stringify({ + cmd: 'injectGlobalJSFunction', + name: messageObject.name, + arguments: args + })); + }); + break; + case "invokeMethod": + console.log(messageObject.objectName); + console.log(messageObject.functionName); + let args = []; + for (let i = 0; i < messageObject.javaValues.length; i++) { + let javaValue = messageObject.javaValues[i]; + if (javaValue.type === 0) { + args.push(null); + } + else if (javaValue.type === 1) { + args.push(parseFloat(javaValue.value)); + } + else if (javaValue.type === 2) { + args.push((javaValue.value == 'true')); + } + else if (javaValue.type === 3) { + args.push(javaValue.value.toString()); + } + else if (javaValue.type === 4) { + args.push(JSON.parse(javaValue.value)); + } + else if (javaValue.type === 5) { + args.push(JSON.parse(javaValue.value)); + } + } + console.log(args); + console.log(messageObject.hashKey); + let object = Reflect.get(global, messageObject.objectName); + let method = Reflect.get(object, messageObject.functionName); + let result = Reflect.apply(method, undefined, args); + console.log(result); + ws.send(JSON.stringify({ + cmd: 'invokeMethod', + result: result + })); + break; + } + }); +}); +debugServer.on('listening', function connection(ws) { + console.log('debugger server started on 2080'); +}); +global.injectGlobal = (objName, obj) => { + Reflect.set(global, objName, JSON.parse(obj)); +}; +global.sendToNative = () => { +}; +global.receiveFromNative = () => { +}; +export * from './index'; diff --git a/doric-js/lib/index.js b/doric-js/lib/index.js new file mode 100644 index 00000000..05294bd7 --- /dev/null +++ b/doric-js/lib/index.js @@ -0,0 +1,5 @@ +export * from './src/ui/index.ui'; +export * from "./src/widget/index.widget"; +export * from './src/native/index.native'; +export * from "./src/util/index.util"; +export * from "./src/pattern/index.pattern"; diff --git a/doric-js/lib/index.runtime.d.ts b/doric-js/lib/index.runtime.d.ts new file mode 100644 index 00000000..f86e9e43 --- /dev/null +++ b/doric-js/lib/index.runtime.d.ts @@ -0,0 +1 @@ +export * from './src/runtime/sandbox'; diff --git a/doric-js/lib/index.runtime.js b/doric-js/lib/index.runtime.js new file mode 100644 index 00000000..318a1a59 --- /dev/null +++ b/doric-js/lib/index.runtime.js @@ -0,0 +1,16 @@ +/* + * 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. + */ +export * from './src/runtime/sandbox'; diff --git a/doric-js/lib/src/mock/driver.d.ts b/doric-js/lib/src/mock/driver.d.ts new file mode 100644 index 00000000..4bd2cd66 --- /dev/null +++ b/doric-js/lib/src/mock/driver.d.ts @@ -0,0 +1,24 @@ +import { Panel } from '../ui/panel'; +import { View } from '../ui/view'; +export interface Driver { + /** + * Create and destory page + */ + createPage(): Panel; + destoryPage(): Panel; + /** + * Page lifecycle + */ + dispatchOnCreate(): void; + dispatchOnDestory(): void; + dispatchOnShow(): void; + dispatchOnHidden(): void; + /** + * Page render + */ + dispatchBuild(): View; +} +export interface Responser { + constructor(): void; + respond(action: string, extra: any): void; +} diff --git a/doric-js/lib/src/mock/driver.js b/doric-js/lib/src/mock/driver.js new file mode 100644 index 00000000..e69de29b diff --git a/doric-js/lib/src/native/animate.d.ts b/doric-js/lib/src/native/animate.d.ts new file mode 100644 index 00000000..19dd6f93 --- /dev/null +++ b/doric-js/lib/src/native/animate.d.ts @@ -0,0 +1,9 @@ +import { BridgeContext } from "../runtime/global"; +/** + * Only supports x,y,width,height,corner(just for four corners),rotation,bgColor, + * @param panel @see Panel + */ +export declare function animate(context: BridgeContext): (args: { + animations: () => void; + duration: number; +}) => Promise; diff --git a/doric-js/lib/src/native/animate.js b/doric-js/lib/src/native/animate.js new file mode 100644 index 00000000..f3ce3d6b --- /dev/null +++ b/doric-js/lib/src/native/animate.js @@ -0,0 +1,57 @@ +/* + * 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 { Panel } from "../ui/panel"; +import { takeLet } from "../pattern/candies"; +/** + * Only supports x,y,width,height,corner(just for four corners),rotation,bgColor, + * @param panel @see Panel + */ +export function animate(context) { + const entity = context.entity; + if (entity instanceof Panel) { + let panel = entity; + return (args) => { + return takeLet(panel.context.animate)(it => { + return it.submit().then(() => { + args.animations(); + return takeLet(panel.getRootView())(root => { + if (root.isDirty()) { + const model = root.toModel(); + model.duration = args.duration; + const ret = it.animateRender(model); + root.clean(); + return ret; + } + for (let v of panel.allHeadViews()) { + if (v.isDirty()) { + const model = v.toModel(); + const ret = it.animateRender(model); + it.clean(); + return ret; + } + } + throw new Error('Cannot find any animated elements'); + }); + }); + }); + }; + } + else { + return (args) => { + return Promise.reject(`Cannot find panel in Context:${context.id}`); + }; + } +} diff --git a/doric-js/lib/src/native/index.native.d.ts b/doric-js/lib/src/native/index.native.d.ts new file mode 100644 index 00000000..e1500e52 --- /dev/null +++ b/doric-js/lib/src/native/index.native.d.ts @@ -0,0 +1,7 @@ +export * from './modal'; +export * from './navbar'; +export * from './navigator'; +export * from './network'; +export * from './storage'; +export * from './popover'; +export * from './animate'; diff --git a/doric-js/lib/src/native/index.native.js b/doric-js/lib/src/native/index.native.js new file mode 100644 index 00000000..bbd8b923 --- /dev/null +++ b/doric-js/lib/src/native/index.native.js @@ -0,0 +1,22 @@ +/* + * 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. + */ +export * from './modal'; +export * from './navbar'; +export * from './navigator'; +export * from './network'; +export * from './storage'; +export * from './popover'; +export * from './animate'; diff --git a/doric-js/lib/src/native/modal.d.ts b/doric-js/lib/src/native/modal.d.ts new file mode 100644 index 00000000..05cb00cb --- /dev/null +++ b/doric-js/lib/src/native/modal.d.ts @@ -0,0 +1,24 @@ +import { BridgeContext } from "../runtime/global"; +import { Gravity } from "../util/gravity"; +export declare function modal(context: BridgeContext): { + toast: (msg: string, gravity?: Gravity) => void; + alert: (arg: string | { + title: string; + msg: string; + okLabel?: string | undefined; + }) => Promise; + confirm: (arg: string | { + title: string; + msg: string; + okLabel?: string | undefined; + cancelLabel?: string | undefined; + }) => Promise; + prompt: (arg: { + title?: string | undefined; + msg?: string | undefined; + okLabel?: string | undefined; + cancelLabel?: string | undefined; + text?: string | undefined; + defaultText?: string | undefined; + }) => Promise; +}; diff --git a/doric-js/lib/src/native/modal.js b/doric-js/lib/src/native/modal.js new file mode 100644 index 00000000..fafb8713 --- /dev/null +++ b/doric-js/lib/src/native/modal.js @@ -0,0 +1,30 @@ +import { Gravity } from "../util/gravity"; +export function modal(context) { + return { + toast: (msg, gravity = Gravity.Bottom) => { + context.modal.toast({ + msg, + gravity: gravity.toModel(), + }); + }, + alert: (arg) => { + if (typeof arg === 'string') { + return context.modal.alert({ msg: arg }); + } + else { + return context.modal.alert(arg); + } + }, + confirm: (arg) => { + if (typeof arg === 'string') { + return context.modal.confirm({ msg: arg }); + } + else { + return context.modal.confirm(arg); + } + }, + prompt: (arg) => { + return context.modal.prompt(arg); + }, + }; +} diff --git a/doric-js/lib/src/native/navbar.d.ts b/doric-js/lib/src/native/navbar.d.ts new file mode 100644 index 00000000..98271847 --- /dev/null +++ b/doric-js/lib/src/native/navbar.d.ts @@ -0,0 +1,8 @@ +import { BridgeContext } from "../runtime/global"; +import { Color } from "../util/color"; +export declare function navbar(context: BridgeContext): { + isHidden: () => Promise; + setHidden: (hidden: boolean) => Promise; + setTitle: (title: string) => Promise; + setBgColor: (color: Color) => Promise; +}; diff --git a/doric-js/lib/src/native/navbar.js b/doric-js/lib/src/native/navbar.js new file mode 100644 index 00000000..3a153433 --- /dev/null +++ b/doric-js/lib/src/native/navbar.js @@ -0,0 +1,28 @@ +import { Panel } from "../ui/panel"; +export function navbar(context) { + const entity = context.entity; + let panel = undefined; + if (entity instanceof Panel) { + panel = entity; + } + return { + isHidden: () => { + return context.navbar.isHidden(); + }, + setHidden: (hidden) => { + return context.navbar.setHidden({ + hidden, + }); + }, + setTitle: (title) => { + return context.navbar.setTitle({ + title, + }); + }, + setBgColor: (color) => { + return context.navbar.setBgColor({ + color: color.toModel(), + }); + }, + }; +} diff --git a/doric-js/lib/src/native/navigator.d.ts b/doric-js/lib/src/native/navigator.d.ts new file mode 100644 index 00000000..b6a14f91 --- /dev/null +++ b/doric-js/lib/src/native/navigator.d.ts @@ -0,0 +1,9 @@ +import { BridgeContext } from "../runtime/global"; +export declare function navigator(context: BridgeContext): { + push: (scheme: string, config?: { + alias?: string | undefined; + animated?: boolean | undefined; + extra?: object | undefined; + } | undefined) => Promise; + pop: (animated?: boolean) => Promise; +}; diff --git a/doric-js/lib/src/native/navigator.js b/doric-js/lib/src/native/navigator.js new file mode 100644 index 00000000..00d853a2 --- /dev/null +++ b/doric-js/lib/src/native/navigator.js @@ -0,0 +1,15 @@ +export function navigator(context) { + return { + push: (scheme, config) => { + if (config && config.extra) { + config.extra = JSON.stringify(config.extra); + } + return context.navigator.push({ + scheme, config + }); + }, + pop: (animated = true) => { + return context.navigator.pop({ animated }); + }, + }; +} diff --git a/doric-js/lib/src/native/network.d.ts b/doric-js/lib/src/native/network.d.ts new file mode 100644 index 00000000..578e0d83 --- /dev/null +++ b/doric-js/lib/src/native/network.d.ts @@ -0,0 +1,27 @@ +import { BridgeContext } from "../runtime/global"; +export interface IRequest { + url?: string; + method?: "get" | "post" | "put" | "delete"; + headers?: { + [index: string]: string; + }; + params?: { + [index: string]: string; + }; + data?: object | string; + timeout?: number; +} +export interface IResponse { + data: any; + status: number; + headers?: { + [index: string]: string; + }; +} +export declare function network(context: BridgeContext): { + request: (config: IRequest) => Promise; + get: (url: string, config?: IRequest | undefined) => Promise; + post: (url: string, data?: string | object | undefined, config?: IRequest | undefined) => Promise; + put: (url: string, data?: string | object | undefined, config?: IRequest | undefined) => Promise; + delete: (url: string, data?: string | object | undefined, config?: IRequest | undefined) => Promise; +}; diff --git a/doric-js/lib/src/native/network.js b/doric-js/lib/src/native/network.js new file mode 100644 index 00000000..383718c3 --- /dev/null +++ b/doric-js/lib/src/native/network.js @@ -0,0 +1,63 @@ +function transformRequest(request) { + let url = request.url || ""; + if (request.params !== undefined) { + const queryStrings = []; + for (let key in request.params) { + queryStrings.push(`${key}=${encodeURIComponent(request.params[key])}`); + } + request.url = `${request.url}${url.indexOf('?') >= 0 ? '&' : '?'}${queryStrings.join('&')}`; + } + if (typeof request.data === 'object') { + request.data = JSON.stringify(request.data); + } + return request; +} +export function network(context) { + return { + request: (config) => { + return context.network.request(transformRequest(config)); + }, + get: (url, config) => { + let finalConfig = config; + if (finalConfig === undefined) { + finalConfig = {}; + } + finalConfig.url = url; + finalConfig.method = "get"; + return context.network.request(transformRequest(finalConfig)); + }, + post: (url, data, config) => { + let finalConfig = config; + if (finalConfig === undefined) { + finalConfig = {}; + } + finalConfig.url = url; + finalConfig.method = "post"; + if (data !== undefined) { + finalConfig.data = data; + } + return context.network.request(transformRequest(finalConfig)); + }, + put: (url, data, config) => { + let finalConfig = config; + if (finalConfig === undefined) { + finalConfig = {}; + } + finalConfig.url = url; + finalConfig.method = "put"; + if (data !== undefined) { + finalConfig.data = data; + } + return context.network.request(transformRequest(finalConfig)); + }, + delete: (url, data, config) => { + let finalConfig = config; + if (finalConfig === undefined) { + finalConfig = {}; + } + finalConfig.url = url; + finalConfig.method = "delete"; + return context.network.request(transformRequest(finalConfig)); + }, + }; +} diff --git a/doric-js/lib/src/native/popover.d.ts b/doric-js/lib/src/native/popover.d.ts new file mode 100644 index 00000000..5b3b2670 --- /dev/null +++ b/doric-js/lib/src/native/popover.d.ts @@ -0,0 +1,6 @@ +import { BridgeContext } from "../runtime/global"; +import { View } from "../ui/view"; +export declare function popover(context: BridgeContext): { + show: (view: View) => Promise; + dismiss: (view?: View | undefined) => Promise; +}; diff --git a/doric-js/lib/src/native/popover.js b/doric-js/lib/src/native/popover.js new file mode 100644 index 00000000..7e667cda --- /dev/null +++ b/doric-js/lib/src/native/popover.js @@ -0,0 +1,27 @@ +import { Panel } from "../ui/panel"; +export function popover(context) { + const entity = context.entity; + let panel = undefined; + if (entity instanceof Panel) { + panel = entity; + } + return { + show: (view) => { + if (panel) { + panel.addHeadView(view); + } + return context.popover.show(view.toModel()); + }, + dismiss: (view = undefined) => { + if (panel) { + if (view) { + panel.removeHeadView(view); + } + else { + panel.clearHeadViews(); + } + } + return context.popover.dismiss(view ? { id: view.viewId } : undefined); + }, + }; +} diff --git a/doric-js/lib/src/native/storage.d.ts b/doric-js/lib/src/native/storage.d.ts new file mode 100644 index 00000000..d90ebed0 --- /dev/null +++ b/doric-js/lib/src/native/storage.d.ts @@ -0,0 +1,7 @@ +import { BridgeContext } from "../runtime/global"; +export declare function storage(context: BridgeContext): { + setItem: (key: string, value: string, zone?: string | undefined) => Promise; + getItem: (key: string, zone?: string | undefined) => Promise; + remove: (key: string, zone?: string | undefined) => Promise; + clear: (zone: string) => Promise; +}; diff --git a/doric-js/lib/src/native/storage.js b/doric-js/lib/src/native/storage.js new file mode 100644 index 00000000..2bafc193 --- /dev/null +++ b/doric-js/lib/src/native/storage.js @@ -0,0 +1,16 @@ +export function storage(context) { + return { + setItem: (key, value, zone) => { + return context.storage.setItem({ key, value, zone }); + }, + getItem: (key, zone) => { + return context.storage.getItem({ key, zone }); + }, + remove: (key, zone) => { + return context.storage.remove({ key, zone }); + }, + clear: (zone) => { + return context.storage.clear({ zone }); + }, + }; +} diff --git a/doric-js/lib/src/pattern/candies.d.ts b/doric-js/lib/src/pattern/candies.d.ts new file mode 100644 index 00000000..88300cb4 --- /dev/null +++ b/doric-js/lib/src/pattern/candies.d.ts @@ -0,0 +1,8 @@ +export declare function take(target: T): (block: (p: T) => void) => void; +export declare function takeNonNull(target?: T): (block: (p: T) => R) => R | undefined; +export declare function takeNull(target?: T): (block: () => R) => R | undefined; +export declare function takeLet(target: T): (block: (p: T) => R | undefined) => R | undefined; +export declare function takeAlso(target: T): (block: (p: T) => void) => T; +export declare function takeIf(target: T): (predicate: (t: T) => boolean) => T | undefined; +export declare function takeUnless(target: T): (predicate: (t: T) => boolean) => T | undefined; +export declare function repeat(action: (count: number) => void): (times: number) => void; diff --git a/doric-js/lib/src/pattern/candies.js b/doric-js/lib/src/pattern/candies.js new file mode 100644 index 00000000..fb8c0255 --- /dev/null +++ b/doric-js/lib/src/pattern/candies.js @@ -0,0 +1,62 @@ +/* + * 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. + */ +export function take(target) { + return (block) => { + block(target); + }; +} +export function takeNonNull(target) { + return (block) => { + if (target !== undefined) { + return block(target); + } + }; +} +export function takeNull(target) { + return (block) => { + if (target === undefined) { + return block(); + } + }; +} +export function takeLet(target) { + return (block) => { + return block(target); + }; +} +export function takeAlso(target) { + return (block) => { + block(target); + return target; + }; +} +export function takeIf(target) { + return (predicate) => { + return predicate(target) ? target : undefined; + }; +} +export function takeUnless(target) { + return (predicate) => { + return predicate(target) ? undefined : target; + }; +} +export function repeat(action) { + return (times) => { + for (let i = 0; i < times; i++) { + action(i); + } + }; +} diff --git a/doric-js/lib/src/pattern/index.pattern.d.ts b/doric-js/lib/src/pattern/index.pattern.d.ts new file mode 100644 index 00000000..5fe46fe8 --- /dev/null +++ b/doric-js/lib/src/pattern/index.pattern.d.ts @@ -0,0 +1,3 @@ +export * from './candies'; +export * from './provider'; +export * from './mvvm'; diff --git a/doric-js/lib/src/pattern/index.pattern.js b/doric-js/lib/src/pattern/index.pattern.js new file mode 100644 index 00000000..6a15c9e3 --- /dev/null +++ b/doric-js/lib/src/pattern/index.pattern.js @@ -0,0 +1,18 @@ +/* + * 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. + */ +export * from './candies'; +export * from './provider'; +export * from './mvvm'; diff --git a/doric-js/lib/src/pattern/mvvm.d.ts b/doric-js/lib/src/pattern/mvvm.d.ts new file mode 100644 index 00000000..364766a5 --- /dev/null +++ b/doric-js/lib/src/pattern/mvvm.d.ts @@ -0,0 +1,27 @@ +import { Group } from "../ui/view"; +import { Panel } from "../ui/panel"; +export declare abstract class ViewHolder { + abstract build(root: Group): void; +} +export declare type Setter = (state: M) => void; +export declare abstract class ViewModel { + private state; + private viewHolder; + constructor(obj: M, v: V); + getState(): M; + updateState(setter: Setter): void; + attach(view: Group): void; + abstract onAttached(state: M, vh: V): void; + abstract onBind(state: M, vh: V): void; +} +export declare type ViewModelClass = new (m: M, v: V) => ViewModel; +export declare type ViewHolderClass = new () => V; +export declare abstract class VMPanel extends Panel { + private vm?; + private vh?; + abstract getViewModelClass(): ViewModelClass; + abstract getState(): M; + abstract getViewHolderClass(): ViewHolderClass; + getViewModel(): ViewModel | undefined; + build(root: Group): void; +} diff --git a/doric-js/lib/src/pattern/mvvm.js b/doric-js/lib/src/pattern/mvvm.js new file mode 100644 index 00000000..e1a642ab --- /dev/null +++ b/doric-js/lib/src/pattern/mvvm.js @@ -0,0 +1,31 @@ +import { Panel } from "../ui/panel"; +export class ViewHolder { +} +export class ViewModel { + constructor(obj, v) { + this.state = obj; + this.viewHolder = v; + } + getState() { + return this.state; + } + updateState(setter) { + setter(this.state); + this.onBind(this.state, this.viewHolder); + } + attach(view) { + this.viewHolder.build(view); + this.onAttached(this.state, this.viewHolder); + this.onBind(this.state, this.viewHolder); + } +} +export class VMPanel extends Panel { + getViewModel() { + return this.vm; + } + build(root) { + this.vh = new (this.getViewHolderClass()); + this.vm = new (this.getViewModelClass())(this.getState(), this.vh); + this.vm.attach(root); + } +} diff --git a/doric-js/lib/src/pattern/provider.d.ts b/doric-js/lib/src/pattern/provider.d.ts new file mode 100644 index 00000000..008573d9 --- /dev/null +++ b/doric-js/lib/src/pattern/provider.d.ts @@ -0,0 +1,42 @@ +export declare type Observer = (v: T) => void; +export declare type Updater = (v: T) => T; +export interface IObservable { + addObserver(observer: Observer): void; + removeObserver(observer: Observer): void; + update(updater: Updater): void; +} +export declare class Observable implements IObservable { + private provider; + private clz; + private observers; + constructor(provider: IProvider, clz: { + new (...args: any[]): M; + }); + addObserver(observer: Observer): void; + removeObserver(observer: Observer): void; + update(updater: Updater): void; +} +export interface IProvider { + provide(obj: Object): void; + acquire(clz: { + new (...args: any[]): T; + }): T | undefined; + remove(clz: { + new (...args: any[]): T; + }): void; + clear(): void; + observe(clz: { + new (...args: any[]): T; + }): Observable; +} +export declare class Provider implements IProvider { + private provision; + private observableMap; + provide(obj: Object): void; + acquire(clz: { + new (...args: any[]): T; + }): T | undefined; + remove(clz: new (...args: any[]) => T): void; + clear(): void; + observe(clz: new (...args: any[]) => T): Observable; +} diff --git a/doric-js/lib/src/pattern/provider.js b/doric-js/lib/src/pattern/provider.js new file mode 100644 index 00000000..41b95524 --- /dev/null +++ b/doric-js/lib/src/pattern/provider.js @@ -0,0 +1,50 @@ +export class Observable { + constructor(provider, clz) { + this.observers = new Set; + this.provider = provider; + this.clz = clz; + } + addObserver(observer) { + this.observers.add(observer); + } + removeObserver(observer) { + this.observers.delete(observer); + } + update(updater) { + const oldV = this.provider.acquire(this.clz); + const newV = updater(oldV); + if (newV !== undefined) { + this.provider.provide(newV); + } + for (let observer of this.observers) { + observer(newV); + } + } +} +export class Provider { + constructor() { + this.provision = new Map; + this.observableMap = new Map; + } + provide(obj) { + this.provision.set(obj.constructor, obj); + } + acquire(clz) { + const ret = this.provision.get(clz); + return ret; + } + remove(clz) { + this.provision.delete(clz); + } + clear() { + this.provision.clear(); + } + observe(clz) { + let observable = this.observableMap.get(clz); + if (observable === undefined) { + observable = new Observable(this, clz); + this.observableMap.set(clz, observable); + } + return observable; + } +} diff --git a/doric-js/lib/src/runtime/global.d.ts b/doric-js/lib/src/runtime/global.d.ts new file mode 100644 index 00000000..b1838796 --- /dev/null +++ b/doric-js/lib/src/runtime/global.d.ts @@ -0,0 +1,22 @@ +export * from 'reflect-metadata'; +export declare type BridgeContext = { + [index: string]: { + [index: string]: (args?: any) => Promise; + }; +}; +declare global { + const context: BridgeContext; + const Environment: { + platform: "Android" | "iOS" | "Qt" | "h5"; + platformVersion: string; + appName: string; + appVersion: string; + libVersion: string; + screenWidth: number; + screenHeight: number; + }; + function Entry(constructor: { + new (...args: any[]): {}; + }): any; +} +export {}; diff --git a/doric-js/lib/src/runtime/global.js b/doric-js/lib/src/runtime/global.js new file mode 100644 index 00000000..e69de29b diff --git a/doric-js/lib/src/runtime/sandbox.d.ts b/doric-js/lib/src/runtime/sandbox.d.ts new file mode 100644 index 00000000..774d3a31 --- /dev/null +++ b/doric-js/lib/src/runtime/sandbox.d.ts @@ -0,0 +1,27 @@ +import "reflect-metadata"; +export declare function jsCallResolve(contextId: string, callbackId: string, args?: any): void; +export declare function jsCallReject(contextId: string, callbackId: string, args?: any): void; +export declare class Context { + entity: any; + id: string; + callbacks: Map; + hookBeforeNativeCall(): void; + hookAfterNativeCall(): void; + constructor(id: string); + callNative(namespace: string, method: string, args?: any): Promise; + register(instance: Object): void; +} +export declare function jsObtainContext(id: string): Context | undefined; +export declare function jsReleaseContext(id: string): void; +export declare function __require__(name: string): any; +export declare function jsRegisterModule(name: string, moduleObject: any): void; +export declare function jsCallEntityMethod(contextId: string, methodName: string, args?: any): any; +export declare function jsObtainEntry(contextId: string): {}>(constructor: T) => { + new (...args: any[]): { + context: Context | undefined; + }; +} & T; +export declare function jsCallbackTimer(timerId: number): void; diff --git a/doric-js/lib/src/runtime/sandbox.js b/doric-js/lib/src/runtime/sandbox.js new file mode 100644 index 00000000..fe3f4074 --- /dev/null +++ b/doric-js/lib/src/runtime/sandbox.js @@ -0,0 +1,302 @@ +/* + * 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 { uniqueId } from "../util/uniqueId"; +import { loge } from "../util/log"; +import "reflect-metadata"; +function hookBeforeNativeCall(context) { + if (context) { + Reflect.defineMetadata('__doric_context__', context, global); + context.hookBeforeNativeCall(); + } +} +function hookAfterNativeCall(context) { + if (context) { + context.hookAfterNativeCall(); + } +} +function getContext() { + return Reflect.getMetadata('__doric_context__', global); +} +function setContext(context) { + Reflect.defineMetadata('__doric_context__', context, global); +} +export function jsCallResolve(contextId, callbackId, args) { + const context = gContexts.get(contextId); + if (context === undefined) { + loge(`Cannot find context for context id:${contextId}`); + return; + } + const callback = context.callbacks.get(callbackId); + if (callback === undefined) { + loge(`Cannot find call for context id:${contextId},callback id:${callbackId}`); + return; + } + const argumentsList = []; + for (let i = 2; i < arguments.length; i++) { + argumentsList.push(arguments[i]); + } + hookBeforeNativeCall(context); + Reflect.apply(callback.resolve, context, argumentsList); + hookAfterNativeCall(context); +} +export function jsCallReject(contextId, callbackId, args) { + const context = gContexts.get(contextId); + if (context === undefined) { + loge(`Cannot find context for context id:${contextId}`); + return; + } + const callback = context.callbacks.get(callbackId); + if (callback === undefined) { + loge(`Cannot find call for context id:${contextId},callback id:${callbackId}`); + return; + } + const argumentsList = []; + for (let i = 2; i < arguments.length; i++) { + argumentsList.push(arguments[i]); + } + hookBeforeNativeCall(context); + Reflect.apply(callback.reject, context.entity, argumentsList); + hookAfterNativeCall(context); +} +export class Context { + constructor(id) { + this.callbacks = new Map; + this.id = id; + return new Proxy(this, { + get: (target, p) => { + if (Reflect.has(target, p)) { + return Reflect.get(target, p); + } + else { + const namespace = p; + return new Proxy({}, { + get: (target, p) => { + if (Reflect.has(target, p)) { + return Reflect.get(target, p); + } + else { + const context = this; + return function () { + const args = []; + args.push(namespace); + args.push(p); + for (let arg of arguments) { + args.push(arg); + } + return Reflect.apply(context.callNative, context, args); + }; + } + } + }); + } + } + }); + } + hookBeforeNativeCall() { + if (this.entity && Reflect.has(this.entity, 'hookBeforeNativeCall')) { + Reflect.apply(Reflect.get(this.entity, 'hookBeforeNativeCall'), this.entity, []); + } + } + hookAfterNativeCall() { + if (this.entity && Reflect.has(this.entity, 'hookAfterNativeCall')) { + Reflect.apply(Reflect.get(this.entity, 'hookAfterNativeCall'), this.entity, []); + } + } + callNative(namespace, method, args) { + const callbackId = uniqueId('callback'); + nativeBridge(this.id, namespace, method, callbackId, args); + return new Promise((resolve, reject) => { + this.callbacks.set(callbackId, { + resolve, + reject, + }); + }); + } + register(instance) { + this.entity = instance; + } +} +const gContexts = new Map; +const gModules = new Map; +export function jsObtainContext(id) { + if (gContexts.has(id)) { + const context = gContexts.get(id); + setContext(context); + return context; + } + else { + const context = new Context(id); + gContexts.set(id, context); + setContext(context); + return context; + } +} +export function jsReleaseContext(id) { + const context = gContexts.get(id); + const args = arguments; + if (context) { + timerInfos.forEach((v, k) => { + if (v.context === context) { + if (global.nativeClearTimer === undefined) { + return Reflect.apply(_clearTimeout, undefined, args); + } + timerInfos.delete(k); + nativeClearTimer(k); + } + }); + } + gContexts.delete(id); +} +export function __require__(name) { + if (gModules.has(name)) { + return gModules.get(name); + } + else { + if (nativeRequire(name)) { + return gModules.get(name); + } + else { + return undefined; + } + } +} +export function jsRegisterModule(name, moduleObject) { + gModules.set(name, moduleObject); +} +export function jsCallEntityMethod(contextId, methodName, args) { + const context = gContexts.get(contextId); + if (context === undefined) { + loge(`Cannot find context for context id:${contextId}`); + return; + } + if (context.entity === undefined) { + loge(`Cannot find holder for context id:${contextId}`); + return; + } + if (Reflect.has(context.entity, methodName)) { + const argumentsList = []; + for (let i = 2; i < arguments.length; i++) { + argumentsList.push(arguments[i]); + } + hookBeforeNativeCall(context); + const ret = Reflect.apply(Reflect.get(context.entity, methodName), context.entity, argumentsList); + hookAfterNativeCall(context); + return ret; + } + else { + loge(`Cannot find method for context id:${contextId},method name is:${methodName}`); + } +} +export function jsObtainEntry(contextId) { + const context = jsObtainContext(contextId); + return (constructor) => { + const ret = class extends constructor { + constructor() { + super(...arguments); + this.context = context; + } + }; + if (context) { + context.register(new ret); + } + return ret; + }; +} +const global = Function('return this')(); +let __timerId__ = 0; +const timerInfos = new Map; +const _setTimeout = global.setTimeout; +const _setInterval = global.setInterval; +const _clearTimeout = global.clearTimeout; +const _clearInterval = global.clearInterval; +const doricSetTimeout = function (handler, timeout, ...args) { + if (global.nativeSetTimer === undefined) { + return Reflect.apply(_setTimeout, undefined, arguments); + } + const id = __timerId__++; + timerInfos.set(id, { + callback: () => { + Reflect.apply(handler, undefined, args); + timerInfos.delete(id); + }, + context: getContext(), + }); + nativeSetTimer(id, timeout || 0, false); + return id; +}; +const doricSetInterval = function (handler, timeout, ...args) { + if (global.nativeSetTimer === undefined) { + return Reflect.apply(_setInterval, undefined, arguments); + } + const id = __timerId__++; + timerInfos.set(id, { + callback: () => { + Reflect.apply(handler, undefined, args); + }, + context: getContext(), + }); + nativeSetTimer(id, timeout || 0, true); + return id; +}; +const doricClearTimeout = function (timerId) { + if (global.nativeClearTimer === undefined) { + return Reflect.apply(_clearTimeout, undefined, arguments); + } + timerInfos.delete(timerId); + nativeClearTimer(timerId); +}; +const doricClearInterval = function (timerId) { + if (global.nativeClearTimer === undefined) { + return Reflect.apply(_clearInterval, undefined, arguments); + } + timerInfos.delete(timerId); + nativeClearTimer(timerId); +}; +if (!global.setTimeout) { + global.setTimeout = doricSetTimeout; +} +else { + global.doricSetTimeout = doricSetTimeout; +} +if (!global.setInterval) { + global.setInterval = doricSetInterval; +} +else { + global.doricSetInterval = doricSetInterval; +} +if (!global.clearTimeout) { + global.clearTimeout = doricClearTimeout; +} +else { + global.doricClearTimeout = doricClearTimeout; +} +if (!global.clearInterval) { + global.clearInterval = doricClearInterval; +} +else { + global.doricClearInterval = doricClearInterval; +} +export function jsCallbackTimer(timerId) { + const timerInfo = timerInfos.get(timerId); + if (timerInfo === undefined) { + return; + } + if (timerInfo.callback instanceof Function) { + hookBeforeNativeCall(timerInfo.context); + Reflect.apply(timerInfo.callback, timerInfo.context, []); + hookAfterNativeCall(timerInfo.context); + } +} diff --git a/doric-js/lib/src/ui/animation.d.ts b/doric-js/lib/src/ui/animation.d.ts new file mode 100644 index 00000000..7ce73294 --- /dev/null +++ b/doric-js/lib/src/ui/animation.d.ts @@ -0,0 +1,127 @@ +import { Modeling, Model } from "../util/types"; +export declare type AnimatedKey = "translationX" | "translationY" | "scaleX" | "scaleY" | "rotation" | "pivotX" | "pivotY"; +export declare enum RepeatMode { + RESTART = 1, + REVERSE = 2 +} +export interface IAnimation extends Modeling { + duration: number; + delay?: number; +} +export interface Changeable { + fromValue: number; + toValue: number; + key: AnimatedKey; + repeatCount?: number; + repeatMode?: RepeatMode; +} +export declare enum FillMode { + /** + * The receiver is removed from the presentation when the animation is completed. + */ + Removed = 0, + /** + * The receiver remains visible in its final state when the animation is completed. + */ + Forward = 1, + /** + * The receiver clamps values before zero to zero when the animation is completed. + */ + Backward = 2, + /** + * The receiver clamps values at both ends of the object’s time space + */ + Both = 3 +} +export declare enum TimingFunction { + /** + * The system default timing function. Use this function to ensure that the timing of your animations matches that of most system animations. + */ + Default = 0, + /** + * Linear pacing, which causes an animation to occur evenly over its duration. + */ + Linear = 1, + /** + * Ease-in pacing, which causes an animation to begin slowly and then speed up as it progresses. + */ + EaseIn = 2, + /** + * Ease-out pacing, which causes an animation to begin quickly and then slow as it progresses. + */ + EaseOut = 3, + /** + * Ease-in-ease-out pacing, which causes an animation to begin slowly, accelerate through the middle of its duration, and then slow again before completing. + */ + EaseInEaseOut = 4 +} +declare abstract class Animation implements IAnimation { + changeables: Map; + duration: number; + repeatCount?: number; + repeatMode?: RepeatMode; + delay?: number; + fillMode: FillMode; + timingFunction?: TimingFunction; + toModel(): { + type: string; + delay: number | undefined; + duration: number; + changeables: { + key: AnimatedKey; + fromValue: number; + toValue: number; + }[]; + repeatCount: number | undefined; + repeatMode: RepeatMode | undefined; + fillMode: FillMode; + timingFunction: TimingFunction | undefined; + }; +} +export declare class ScaleAnimation extends Animation { + private scaleXChangeable; + private scaleYChangeable; + constructor(); + set fromScaleX(v: number); + get fromScaleX(): number; + set toScaleX(v: number); + get toScaleX(): number; + set fromScaleY(v: number); + get fromScaleY(): number; + set toScaleY(v: number); + get toScaleY(): number; +} +export declare class TranslationAnimation extends Animation { + private translationXChangeable; + private translationYChangeable; + constructor(); + set fromTranslationX(v: number); + get fromTranslationX(): number; + set toTranslationX(v: number); + get toTranslationX(): number; + set fromTranslationY(v: number); + get fromTranslationY(): number; + set toTranslationY(v: number); + get toTranslationY(): number; +} +export declare class RotationAnimation extends Animation { + private rotationChaneable; + constructor(); + set fromRotation(v: number); + get fromRotation(): number; + set toRotation(v: number); + get toRotation(): number; +} +export declare class AnimationSet implements IAnimation { + private animations; + _duration: number; + delay?: number; + addAnimation(anim: IAnimation): void; + get duration(): number; + set duration(v: number); + toModel(): { + animations: Model; + delay: number | undefined; + }; +} +export {}; diff --git a/doric-js/lib/src/ui/animation.js b/doric-js/lib/src/ui/animation.js new file mode 100644 index 00000000..2e90063d --- /dev/null +++ b/doric-js/lib/src/ui/animation.js @@ -0,0 +1,218 @@ +/* + * 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. + */ +export var RepeatMode; +(function (RepeatMode) { + RepeatMode[RepeatMode["RESTART"] = 1] = "RESTART"; + RepeatMode[RepeatMode["REVERSE"] = 2] = "REVERSE"; +})(RepeatMode || (RepeatMode = {})); +export var FillMode; +(function (FillMode) { + /** + * The receiver is removed from the presentation when the animation is completed. + */ + FillMode[FillMode["Removed"] = 0] = "Removed"; + /** + * The receiver remains visible in its final state when the animation is completed. + */ + FillMode[FillMode["Forward"] = 1] = "Forward"; + /** + * The receiver clamps values before zero to zero when the animation is completed. + */ + FillMode[FillMode["Backward"] = 2] = "Backward"; + /** + * The receiver clamps values at both ends of the object’s time space + */ + FillMode[FillMode["Both"] = 3] = "Both"; +})(FillMode || (FillMode = {})); +export var TimingFunction; +(function (TimingFunction) { + /** + * The system default timing function. Use this function to ensure that the timing of your animations matches that of most system animations. + */ + TimingFunction[TimingFunction["Default"] = 0] = "Default"; + /** + * Linear pacing, which causes an animation to occur evenly over its duration. + */ + TimingFunction[TimingFunction["Linear"] = 1] = "Linear"; + /** + * Ease-in pacing, which causes an animation to begin slowly and then speed up as it progresses. + */ + TimingFunction[TimingFunction["EaseIn"] = 2] = "EaseIn"; + /** + * Ease-out pacing, which causes an animation to begin quickly and then slow as it progresses. + */ + TimingFunction[TimingFunction["EaseOut"] = 3] = "EaseOut"; + /** + * Ease-in-ease-out pacing, which causes an animation to begin slowly, accelerate through the middle of its duration, and then slow again before completing. + */ + TimingFunction[TimingFunction["EaseInEaseOut"] = 4] = "EaseInEaseOut"; +})(TimingFunction || (TimingFunction = {})); +class Animation { + constructor() { + this.changeables = new Map; + this.duration = 0; + this.fillMode = FillMode.Forward; + } + toModel() { + const changeables = []; + for (let e of this.changeables.values()) { + changeables.push({ + key: e.key, + fromValue: e.fromValue, + toValue: e.toValue, + }); + } + return { + type: this.constructor.name, + delay: this.delay, + duration: this.duration, + changeables, + repeatCount: this.repeatCount, + repeatMode: this.repeatMode, + fillMode: this.fillMode, + timingFunction: this.timingFunction + }; + } +} +export class ScaleAnimation extends Animation { + constructor() { + super(); + this.scaleXChangeable = { + key: "scaleX", + fromValue: 1, + toValue: 1, + }; + this.scaleYChangeable = { + key: "scaleY", + fromValue: 1, + toValue: 1, + }; + this.changeables.set("scaleX", this.scaleXChangeable); + this.changeables.set("scaleY", this.scaleYChangeable); + } + set fromScaleX(v) { + this.scaleXChangeable.fromValue = v; + } + get fromScaleX() { + return this.scaleXChangeable.fromValue; + } + set toScaleX(v) { + this.scaleXChangeable.toValue = v; + } + get toScaleX() { + return this.scaleXChangeable.toValue; + } + set fromScaleY(v) { + this.scaleYChangeable.fromValue = v; + } + get fromScaleY() { + return this.scaleYChangeable.fromValue; + } + set toScaleY(v) { + this.scaleYChangeable.toValue = v; + } + get toScaleY() { + return this.scaleYChangeable.toValue; + } +} +export class TranslationAnimation extends Animation { + constructor() { + super(); + this.translationXChangeable = { + key: "translationX", + fromValue: 1, + toValue: 1, + }; + this.translationYChangeable = { + key: "translationY", + fromValue: 1, + toValue: 1, + }; + this.changeables.set("translationX", this.translationXChangeable); + this.changeables.set("translationY", this.translationYChangeable); + } + set fromTranslationX(v) { + this.translationXChangeable.fromValue = v; + } + get fromTranslationX() { + return this.translationXChangeable.fromValue; + } + set toTranslationX(v) { + this.translationXChangeable.toValue = v; + } + get toTranslationX() { + return this.translationXChangeable.toValue; + } + set fromTranslationY(v) { + this.translationYChangeable.fromValue = v; + } + get fromTranslationY() { + return this.translationYChangeable.fromValue; + } + set toTranslationY(v) { + this.translationYChangeable.toValue = v; + } + get toTranslationY() { + return this.translationYChangeable.toValue; + } +} +export class RotationAnimation extends Animation { + constructor() { + super(); + this.rotationChaneable = { + key: "rotation", + fromValue: 1, + toValue: 1, + }; + this.changeables.set("rotation", this.rotationChaneable); + } + set fromRotation(v) { + this.rotationChaneable.fromValue = v; + } + get fromRotation() { + return this.rotationChaneable.fromValue; + } + set toRotation(v) { + this.rotationChaneable.toValue = v; + } + get toRotation() { + return this.rotationChaneable.toValue; + } +} +export class AnimationSet { + constructor() { + this.animations = []; + this._duration = 0; + } + addAnimation(anim) { + this.animations.push(anim); + } + get duration() { + return this._duration; + } + set duration(v) { + this._duration = v; + this.animations.forEach(e => e.duration = v); + } + toModel() { + return { + animations: this.animations.map(e => { + return e.toModel(); + }), + delay: this.delay, + }; + } +} diff --git a/doric-js/lib/src/ui/index.ui.d.ts b/doric-js/lib/src/ui/index.ui.d.ts new file mode 100644 index 00000000..7153cd35 --- /dev/null +++ b/doric-js/lib/src/ui/index.ui.d.ts @@ -0,0 +1,3 @@ +export * from './view'; +export * from './panel'; +export * from './animation'; diff --git a/doric-js/lib/src/ui/index.ui.js b/doric-js/lib/src/ui/index.ui.js new file mode 100644 index 00000000..9ade5cc2 --- /dev/null +++ b/doric-js/lib/src/ui/index.ui.js @@ -0,0 +1,18 @@ +/* + * 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. + */ +export * from './view'; +export * from './panel'; +export * from './animation'; diff --git a/doric-js/lib/src/ui/panel.d.ts b/doric-js/lib/src/ui/panel.d.ts new file mode 100644 index 00000000..0acbd788 --- /dev/null +++ b/doric-js/lib/src/ui/panel.d.ts @@ -0,0 +1,32 @@ +import { View, Group } from "./view"; +import { Root } from '../widget/layouts'; +import { BridgeContext } from '../runtime/global'; +export declare function NativeCall(target: Panel, propertyKey: string, descriptor: PropertyDescriptor): PropertyDescriptor; +export declare abstract class Panel { + context: BridgeContext; + onCreate(): void; + onDestroy(): void; + onShow(): void; + onHidden(): void; + abstract build(rootView: Group): void; + private __data__?; + private __root__; + private headviews; + addHeadView(v: View): void; + allHeadViews(): IterableIterator; + removeHeadView(v: View | string): void; + clearHeadViews(): void; + getRootView(): Root; + getInitData(): object | undefined; + private __init__; + private __onCreate__; + private __onDestroy__; + private __onShow__; + private __onHidden__; + private __build__; + private __response__; + private retrospectView; + private nativeRender; + private hookBeforeNativeCall; + private hookAfterNativeCall; +} diff --git a/doric-js/lib/src/ui/panel.js b/doric-js/lib/src/ui/panel.js new file mode 100644 index 00000000..15328e93 --- /dev/null +++ b/doric-js/lib/src/ui/panel.js @@ -0,0 +1,206 @@ +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +/* + * 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 } from "./view"; +import { loge } from '../util/log'; +import { Root } from '../widget/layouts'; +export function NativeCall(target, propertyKey, descriptor) { + const originVal = descriptor.value; + descriptor.value = function () { + const ret = Reflect.apply(originVal, this, arguments); + return ret; + }; + return descriptor; +} +export class Panel { + constructor() { + this.__root__ = new Root; + this.headviews = new Map; + } + onCreate() { } + onDestroy() { } + onShow() { } + onHidden() { } + addHeadView(v) { + this.headviews.set(v.viewId, v); + } + allHeadViews() { + return this.headviews.values(); + } + removeHeadView(v) { + if (v instanceof View) { + this.headviews.delete(v.viewId); + } + else { + this.headviews.delete(v); + } + } + clearHeadViews() { + this.headviews.clear(); + } + getRootView() { + return this.__root__; + } + getInitData() { + return this.__data__; + } + __init__(frame, data) { + if (data) { + this.__data__ = JSON.parse(data); + } + this.__root__.width = frame.width; + this.__root__.height = frame.height; + this.__root__.children.length = 0; + this.build(this.__root__); + } + __onCreate__() { + this.onCreate(); + } + __onDestroy__() { + this.onDestroy(); + } + __onShow__() { + this.onShow(); + } + __onHidden__() { + this.onHidden(); + } + __build__() { + this.build(this.__root__); + } + __response__(viewIds, callbackId) { + const v = this.retrospectView(viewIds); + if (v === undefined) { + loge(`Cannot find view for ${viewIds}`); + } + else { + const argumentsList = [callbackId]; + for (let i = 2; i < arguments.length; i++) { + argumentsList.push(arguments[i]); + } + return Reflect.apply(v.responseCallback, v, argumentsList); + } + } + retrospectView(ids) { + return ids.reduce((acc, cur) => { + if (acc === undefined) { + if (cur === this.__root__.viewId) { + return this.__root__; + } + return this.headviews.get(cur); + } + else { + if (Reflect.has(acc, "subviewById")) { + return Reflect.apply(Reflect.get(acc, "subviewById"), acc, [cur]); + } + return acc; + } + }, undefined); + } + nativeRender(model) { + this.context.shader.render(model); + } + hookBeforeNativeCall() { + if (Environment.platform !== 'h5') { + this.__root__.clean(); + for (let v of this.headviews.values()) { + v.clean(); + } + } + } + hookAfterNativeCall() { + if (Environment.platform !== 'h5') { + //Here insert a native call to ensure the promise is resolved done. + nativeEmpty(); + if (this.__root__.isDirty()) { + const model = this.__root__.toModel(); + this.nativeRender(model); + } + for (let v of this.headviews.values()) { + if (v.isDirty()) { + const model = v.toModel(); + this.nativeRender(model); + } + } + } + else { + Promise.resolve().then(() => { + if (this.__root__.isDirty()) { + const model = this.__root__.toModel(); + this.nativeRender(model); + this.__root__.clean(); + } + for (let v of this.headviews.values()) { + if (v.isDirty()) { + const model = v.toModel(); + this.nativeRender(model); + v.clean(); + } + } + }); + } + } +} +__decorate([ + NativeCall, + __metadata("design:type", Function), + __metadata("design:paramtypes", [Object, String]), + __metadata("design:returntype", void 0) +], Panel.prototype, "__init__", null); +__decorate([ + NativeCall, + __metadata("design:type", Function), + __metadata("design:paramtypes", []), + __metadata("design:returntype", void 0) +], Panel.prototype, "__onCreate__", null); +__decorate([ + NativeCall, + __metadata("design:type", Function), + __metadata("design:paramtypes", []), + __metadata("design:returntype", void 0) +], Panel.prototype, "__onDestroy__", null); +__decorate([ + NativeCall, + __metadata("design:type", Function), + __metadata("design:paramtypes", []), + __metadata("design:returntype", void 0) +], Panel.prototype, "__onShow__", null); +__decorate([ + NativeCall, + __metadata("design:type", Function), + __metadata("design:paramtypes", []), + __metadata("design:returntype", void 0) +], Panel.prototype, "__onHidden__", null); +__decorate([ + NativeCall, + __metadata("design:type", Function), + __metadata("design:paramtypes", []), + __metadata("design:returntype", void 0) +], Panel.prototype, "__build__", null); +__decorate([ + NativeCall, + __metadata("design:type", Function), + __metadata("design:paramtypes", [Array, String]), + __metadata("design:returntype", void 0) +], Panel.prototype, "__response__", null); diff --git a/doric-js/lib/src/ui/view.d.ts b/doric-js/lib/src/ui/view.d.ts new file mode 100644 index 00000000..d6a29dbb --- /dev/null +++ b/doric-js/lib/src/ui/view.d.ts @@ -0,0 +1,174 @@ +import { Color, GradientColor } from "../util/color"; +import { Modeling, Model } from "../util/types"; +import { BridgeContext } from "../runtime/global"; +import { LayoutConfig } from '../util/layoutconfig'; +import { IAnimation } from "./animation"; +export declare function Property(target: Object, propKey: string): void; +export interface IView { + width?: number; + height?: number; + backgroundColor?: Color | GradientColor; + corners?: number | { + leftTop?: number; + rightTop?: number; + leftBottom?: number; + rightBottom?: number; + }; + border?: { + width: number; + color: Color; + }; + shadow?: { + color: Color; + opacity: number; + radius: number; + offsetX: number; + offsetY: number; + }; + /** + * float [0,..1] + */ + alpha?: number; + hidden?: boolean; + padding?: { + left?: number; + right?: number; + top?: number; + bottom?: number; + }; + layoutConfig?: LayoutConfig; + onClick?: Function; + identifier?: string; + /**++++++++++transform++++++++++*/ + translationX?: number; + translationY?: number; + scaleX?: number; + scaleY?: number; + /** + * float [0,..1] + */ + pivotX?: number; + /** + * float [0,..1] + */ + pivotY?: number; + /** + * rotation*PI + */ + rotation?: number; +} +export declare abstract class View implements Modeling, IView { + width: number; + height: number; + x: number; + y: number; + backgroundColor?: Color | GradientColor; + corners?: number | { + leftTop?: number; + rightTop?: number; + leftBottom?: number; + rightBottom?: number; + }; + border?: { + width: number; + color: Color; + }; + shadow?: { + color: Color; + opacity: number; + radius: number; + offsetX: number; + offsetY: number; + }; + alpha?: number; + hidden?: boolean; + viewId: string; + padding?: { + left?: number; + right?: number; + top?: number; + bottom?: number; + }; + layoutConfig?: LayoutConfig; + onClick?: Function; + superview?: Superview; + callbacks: Map; + private callback2Id; + private id2Callback; + constructor(); + /** Anchor start*/ + get left(): number; + set left(v: number); + get right(): number; + set right(v: number); + get top(): number; + set top(v: number); + get bottom(): number; + set bottom(v: number); + get centerX(): number; + get centerY(): number; + set centerX(v: number); + set centerY(v: number); + /** Anchor end*/ + private __dirty_props__; + get dirtyProps(): { + [index: string]: Model; + }; + nativeViewModel: { + id: string; + type: string; + props: { + [index: string]: Model; + }; + }; + onPropertyChanged(propKey: string, oldV: Model, newV: Model): void; + clean(): void; + isDirty(): boolean; + responseCallback(id: string, ...args: any): any; + toModel(): { + id: string; + type: string; + props: { + [index: string]: Model; + }; + }; + let(block: (it: this) => void): void; + also(block: (it: this) => void): this; + apply(config: IView): this; + in(group: Group): this; + nativeChannel(context: any, name: string): (args?: any) => Promise; + getWidth(context: BridgeContext): Promise; + getHeight(context: BridgeContext): Promise; + getLocationOnScreen(context: BridgeContext): Promise<{ + x: number; + y: number; + }>; + /**++++++++++transform++++++++++*/ + translationX?: number; + translationY?: number; + scaleX?: number; + scaleY?: number; + pivotX?: number; + pivotY?: number; + rotation?: number; + /**----------transform----------*/ + doAnimation(context: BridgeContext, animation: IAnimation): Promise; +} +export declare abstract class Superview extends View { + subviewById(id: string): View | undefined; + abstract allSubviews(): Iterable; + isDirty(): boolean; + clean(): void; + toModel(): { + id: string; + type: string; + props: { + [index: string]: Model; + }; + }; +} +export declare abstract class Group extends Superview { + readonly children: View[]; + allSubviews(): View[]; + addChild(view: View): void; +} diff --git a/doric-js/lib/src/ui/view.js b/doric-js/lib/src/ui/view.js new file mode 100644 index 00000000..d7515f36 --- /dev/null +++ b/doric-js/lib/src/ui/view.js @@ -0,0 +1,328 @@ +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +import { obj2Model } from "../util/types"; +import { uniqueId } from "../util/uniqueId"; +import { loge } from "../util/log"; +export function Property(target, propKey) { + Reflect.defineMetadata(propKey, true, target); +} +export class View { + constructor() { + this.width = 0; + this.height = 0; + this.x = 0; + this.y = 0; + this.viewId = uniqueId('ViewId'); + this.callbacks = new Map; + /** Anchor end*/ + this.__dirty_props__ = {}; + this.nativeViewModel = { + id: this.viewId, + type: this.constructor.name, + props: this.__dirty_props__, + }; + return new Proxy(this, { + get: (target, p, receiver) => { + return Reflect.get(target, p, receiver); + }, + set: (target, p, v, receiver) => { + const oldV = Reflect.get(target, p, receiver); + const ret = Reflect.set(target, p, v, receiver); + if (Reflect.getMetadata(p, target) && oldV !== v) { + receiver.onPropertyChanged(p.toString(), oldV, v); + } + return ret; + } + }); + } + callback2Id(f) { + const id = uniqueId('Function'); + this.callbacks.set(id, f); + return id; + } + id2Callback(id) { + let f = this.callbacks.get(id); + if (f === undefined) { + f = Reflect.get(this, id); + } + return f; + } + /** Anchor start*/ + get left() { + return this.x; + } + set left(v) { + this.x = v; + } + get right() { + return this.x + this.width; + } + set right(v) { + this.x = v - this.width; + } + get top() { + return this.y; + } + set top(v) { + this.y = v; + } + get bottom() { + return this.y + this.height; + } + set bottom(v) { + this.y = v - this.height; + } + get centerX() { + return this.x + this.width / 2; + } + get centerY() { + return this.y + this.height / 2; + } + set centerX(v) { + this.x = v - this.width / 2; + } + set centerY(v) { + this.y = v - this.height / 2; + } + get dirtyProps() { + return this.__dirty_props__; + } + onPropertyChanged(propKey, oldV, newV) { + if (newV instanceof Function) { + newV = this.callback2Id(newV); + } + else { + newV = obj2Model(newV); + } + this.__dirty_props__[propKey] = newV; + } + clean() { + for (const key in this.__dirty_props__) { + if (Reflect.has(this.__dirty_props__, key)) { + Reflect.deleteProperty(this.__dirty_props__, key); + } + } + } + isDirty() { + return Reflect.ownKeys(this.__dirty_props__).length !== 0; + } + responseCallback(id, ...args) { + const f = this.id2Callback(id); + if (f instanceof Function) { + const argumentsList = []; + for (let i = 1; i < arguments.length; i++) { + argumentsList.push(arguments[i]); + } + return Reflect.apply(f, this, argumentsList); + } + else { + loge(`Cannot find callback:${id} for ${JSON.stringify(this.toModel())}`); + } + } + toModel() { + return this.nativeViewModel; + } + let(block) { + block(this); + } + also(block) { + block(this); + return this; + } + apply(config) { + for (let key in config) { + Reflect.set(this, key, Reflect.get(config, key, config), this); + } + return this; + } + in(group) { + group.addChild(this); + return this; + } + nativeChannel(context, name) { + let thisView = this; + return function (args = undefined) { + const func = context.shader.command; + const viewIds = []; + while (thisView != undefined) { + viewIds.push(thisView.viewId); + thisView = thisView.superview; + } + const params = { + viewIds: viewIds.reverse(), + name, + args, + }; + return Reflect.apply(func, undefined, [params]); + }; + } + getWidth(context) { + return this.nativeChannel(context, 'getWidth')(); + } + getHeight(context) { + return this.nativeChannel(context, 'getHeight')(); + } + getLocationOnScreen(context) { + return this.nativeChannel(context, "getLocationOnScreen")(); + } + /**----------transform----------*/ + doAnimation(context, animation) { + return this.nativeChannel(context, "doAnimation")(animation.toModel()).then((args) => { + for (let key in args) { + Reflect.set(this, key, Reflect.get(args, key, args), this); + Reflect.deleteProperty(this.__dirty_props__, key); + } + }); + } +} +__decorate([ + Property, + __metadata("design:type", Number) +], View.prototype, "width", void 0); +__decorate([ + Property, + __metadata("design:type", Number) +], View.prototype, "height", void 0); +__decorate([ + Property, + __metadata("design:type", Number) +], View.prototype, "x", void 0); +__decorate([ + Property, + __metadata("design:type", Number) +], View.prototype, "y", void 0); +__decorate([ + Property, + __metadata("design:type", Object) +], View.prototype, "backgroundColor", void 0); +__decorate([ + Property, + __metadata("design:type", Object) +], View.prototype, "corners", void 0); +__decorate([ + Property, + __metadata("design:type", Object) +], View.prototype, "border", void 0); +__decorate([ + Property, + __metadata("design:type", Object) +], View.prototype, "shadow", void 0); +__decorate([ + Property, + __metadata("design:type", Number) +], View.prototype, "alpha", void 0); +__decorate([ + Property, + __metadata("design:type", Boolean) +], View.prototype, "hidden", void 0); +__decorate([ + Property, + __metadata("design:type", Object) +], View.prototype, "viewId", void 0); +__decorate([ + Property, + __metadata("design:type", Object) +], View.prototype, "padding", void 0); +__decorate([ + Property, + __metadata("design:type", Object) +], View.prototype, "layoutConfig", void 0); +__decorate([ + Property, + __metadata("design:type", Function) +], View.prototype, "onClick", void 0); +__decorate([ + Property, + __metadata("design:type", Number) +], View.prototype, "translationX", void 0); +__decorate([ + Property, + __metadata("design:type", Number) +], View.prototype, "translationY", void 0); +__decorate([ + Property, + __metadata("design:type", Number) +], View.prototype, "scaleX", void 0); +__decorate([ + Property, + __metadata("design:type", Number) +], View.prototype, "scaleY", void 0); +__decorate([ + Property, + __metadata("design:type", Number) +], View.prototype, "pivotX", void 0); +__decorate([ + Property, + __metadata("design:type", Number) +], View.prototype, "pivotY", void 0); +__decorate([ + Property, + __metadata("design:type", Number) +], View.prototype, "rotation", void 0); +export class Superview extends View { + subviewById(id) { + for (let v of this.allSubviews()) { + if (v.viewId === id) { + return v; + } + } + } + isDirty() { + if (super.isDirty()) { + return true; + } + else { + for (const v of this.allSubviews()) { + if (v.isDirty()) { + return true; + } + } + } + return false; + } + clean() { + for (let v of this.allSubviews()) { + v.clean(); + } + super.clean(); + } + toModel() { + const subviews = []; + for (let v of this.allSubviews()) { + if (v != undefined) { + v.superview = this; + if (v.isDirty()) { + subviews.push(v.toModel()); + } + } + } + this.dirtyProps.subviews = subviews; + return super.toModel(); + } +} +export class Group extends Superview { + constructor() { + super(...arguments); + this.children = new Proxy([], { + set: (target, index, value) => { + const ret = Reflect.set(target, index, value); + // Let getDirty return true + this.dirtyProps.children = this.children.map(e => e.viewId); + return ret; + } + }); + } + allSubviews() { + return this.children; + } + addChild(view) { + this.children.push(view); + } +} diff --git a/doric-js/lib/src/util/color.d.ts b/doric-js/lib/src/util/color.d.ts new file mode 100644 index 00000000..b23493f3 --- /dev/null +++ b/doric-js/lib/src/util/color.d.ts @@ -0,0 +1,47 @@ +import { Modeling } from "./types"; +/** + * Store color as format AARRGGBB or RRGGBB + */ +export declare class Color implements Modeling { + static BLACK: Color; + static DKGRAY: Color; + static GRAY: Color; + static LTGRAY: Color; + static WHITE: Color; + static RED: Color; + static GREEN: Color; + static BLUE: Color; + static YELLOW: Color; + static CYAN: Color; + static MAGENTA: Color; + static TRANSPARENT: Color; + _value: number; + constructor(v: number); + static parse(str: string): Color; + static safeParse(str: string, defVal?: Color): Color; + alpha(v: number): Color; + toModel(): number; +} +export declare enum GradientOrientation { + /** draw the gradient from the top to the bottom */ + TOP_BOTTOM = 0, + /** draw the gradient from the top-right to the bottom-left */ + TR_BL = 1, + /** draw the gradient from the right to the left */ + RIGHT_LEFT = 2, + /** draw the gradient from the bottom-right to the top-left */ + BR_TL = 3, + /** draw the gradient from the bottom to the top */ + BOTTOM_TOP = 4, + /** draw the gradient from the bottom-left to the top-right */ + BL_TR = 5, + /** draw the gradient from the left to the right */ + LEFT_RIGHT = 6, + /** draw the gradient from the top-left to the bottom-right */ + TL_BR = 7 +} +export interface GradientColor { + start: Color; + end: Color; + orientation: GradientOrientation; +} diff --git a/doric-js/lib/src/util/color.js b/doric-js/lib/src/util/color.js new file mode 100644 index 00000000..be1820cc --- /dev/null +++ b/doric-js/lib/src/util/color.js @@ -0,0 +1,73 @@ +/** + * Store color as format AARRGGBB or RRGGBB + */ +export class Color { + constructor(v) { + this._value = 0; + this._value = v | 0x0; + } + static parse(str) { + if (!str.startsWith("#")) { + throw new Error(`Parse color error with ${str}`); + } + const val = parseInt(str.substr(1), 16); + if (str.length === 7) { + return new Color(val | 0xff000000); + } + else if (str.length === 9) { + return new Color(val); + } + else { + throw new Error(`Parse color error with ${str}`); + } + } + static safeParse(str, defVal = Color.TRANSPARENT) { + let color = defVal; + try { + color = Color.parse(str); + } + catch (e) { + } + finally { + return color; + } + } + alpha(v) { + v = v * 255; + return new Color((this._value & 0xffffff) | ((v & 0xff) << 24)); + } + toModel() { + return this._value; + } +} +Color.BLACK = new Color(0xFF000000); +Color.DKGRAY = new Color(0xFF444444); +Color.GRAY = new Color(0xFF888888); +Color.LTGRAY = new Color(0xFFCCCCCC); +Color.WHITE = new Color(0xFFFFFFFF); +Color.RED = new Color(0xFFFF0000); +Color.GREEN = new Color(0xFF00FF00); +Color.BLUE = new Color(0xFF0000FF); +Color.YELLOW = new Color(0xFFFFFF00); +Color.CYAN = new Color(0xFF00FFFF); +Color.MAGENTA = new Color(0xFFFF00FF); +Color.TRANSPARENT = new Color(0); +export var GradientOrientation; +(function (GradientOrientation) { + /** draw the gradient from the top to the bottom */ + GradientOrientation[GradientOrientation["TOP_BOTTOM"] = 0] = "TOP_BOTTOM"; + /** draw the gradient from the top-right to the bottom-left */ + GradientOrientation[GradientOrientation["TR_BL"] = 1] = "TR_BL"; + /** draw the gradient from the right to the left */ + GradientOrientation[GradientOrientation["RIGHT_LEFT"] = 2] = "RIGHT_LEFT"; + /** draw the gradient from the bottom-right to the top-left */ + GradientOrientation[GradientOrientation["BR_TL"] = 3] = "BR_TL"; + /** draw the gradient from the bottom to the top */ + GradientOrientation[GradientOrientation["BOTTOM_TOP"] = 4] = "BOTTOM_TOP"; + /** draw the gradient from the bottom-left to the top-right */ + GradientOrientation[GradientOrientation["BL_TR"] = 5] = "BL_TR"; + /** draw the gradient from the left to the right */ + GradientOrientation[GradientOrientation["LEFT_RIGHT"] = 6] = "LEFT_RIGHT"; + /** draw the gradient from the top-left to the bottom-right */ + GradientOrientation[GradientOrientation["TL_BR"] = 7] = "TL_BR"; +})(GradientOrientation || (GradientOrientation = {})); diff --git a/doric-js/lib/src/util/gravity.d.ts b/doric-js/lib/src/util/gravity.d.ts new file mode 100644 index 00000000..5fe52e96 --- /dev/null +++ b/doric-js/lib/src/util/gravity.d.ts @@ -0,0 +1,26 @@ +import { Modeling } from "./types"; +export declare const LEFT: number; +export declare const RIGHT: number; +export declare const TOP: number; +export declare const BOTTOM: number; +export declare const CENTER_X: number; +export declare const CENTER_Y: number; +export declare const CENTER: number; +export declare class Gravity implements Modeling { + val: number; + left(): Gravity; + right(): Gravity; + top(): Gravity; + bottom(): Gravity; + center(): Gravity; + centerX(): Gravity; + centerY(): Gravity; + toModel(): number; + private static origin; + static Center: Gravity; + static Left: Gravity; + static Right: Gravity; + static Top: Gravity; + static Bottom: Gravity; +} +export declare function gravity(): Gravity; diff --git a/doric-js/lib/src/util/gravity.js b/doric-js/lib/src/util/gravity.js new file mode 100644 index 00000000..31058f10 --- /dev/null +++ b/doric-js/lib/src/util/gravity.js @@ -0,0 +1,71 @@ +const SPECIFIED = 1; +const START = 1 << 1; +const END = 1 << 2; +const SHIFT_X = 0; +const SHIFT_Y = 4; +export const LEFT = (START | SPECIFIED) << SHIFT_X; +export const RIGHT = (END | SPECIFIED) << SHIFT_X; +export const TOP = (START | SPECIFIED) << SHIFT_Y; +export const BOTTOM = (END | SPECIFIED) << SHIFT_Y; +export const CENTER_X = SPECIFIED << SHIFT_X; +export const CENTER_Y = SPECIFIED << SHIFT_Y; +export const CENTER = CENTER_X | CENTER_Y; +export class Gravity { + constructor() { + this.val = 0; + } + left() { + const val = this.val | LEFT; + const ret = new Gravity; + ret.val = val; + return ret; + } + right() { + const val = this.val | RIGHT; + const ret = new Gravity; + ret.val = val; + return ret; + } + top() { + const val = this.val | TOP; + const ret = new Gravity; + ret.val = val; + return ret; + } + bottom() { + const val = this.val | BOTTOM; + const ret = new Gravity; + ret.val = val; + return ret; + } + center() { + const val = this.val | CENTER; + const ret = new Gravity; + ret.val = val; + return ret; + } + centerX() { + const val = this.val | CENTER_X; + const ret = new Gravity; + ret.val = val; + return ret; + } + centerY() { + const val = this.val | CENTER_Y; + const ret = new Gravity; + ret.val = val; + return ret; + } + toModel() { + return this.val; + } +} +Gravity.origin = new Gravity; +Gravity.Center = Gravity.origin.center(); +Gravity.Left = Gravity.origin.left(); +Gravity.Right = Gravity.origin.right(); +Gravity.Top = Gravity.origin.top(); +Gravity.Bottom = Gravity.origin.bottom(); +export function gravity() { + return new Gravity; +} diff --git a/doric-js/lib/src/util/index.util.d.ts b/doric-js/lib/src/util/index.util.d.ts new file mode 100644 index 00000000..511773ef --- /dev/null +++ b/doric-js/lib/src/util/index.util.d.ts @@ -0,0 +1,6 @@ +export * from './color'; +export * from './gravity'; +export * from './layoutconfig'; +export * from './log'; +export * from './types'; +export * from './uniqueId'; diff --git a/doric-js/lib/src/util/index.util.js b/doric-js/lib/src/util/index.util.js new file mode 100644 index 00000000..7bab9468 --- /dev/null +++ b/doric-js/lib/src/util/index.util.js @@ -0,0 +1,21 @@ +/* + * 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. + */ +export * from './color'; +export * from './gravity'; +export * from './layoutconfig'; +export * from './log'; +export * from './types'; +export * from './uniqueId'; diff --git a/doric-js/lib/src/util/layoutconfig.d.ts b/doric-js/lib/src/util/layoutconfig.d.ts new file mode 100644 index 00000000..04a32a23 --- /dev/null +++ b/doric-js/lib/src/util/layoutconfig.d.ts @@ -0,0 +1,66 @@ +import { Gravity } from "./gravity"; +import { Modeling } from "./types"; +export declare enum LayoutSpec { + /** + * Depends on what's been set on width or height. + */ + JUST = 0, + /** + * Depends on it's content. + */ + FIT = 1, + /** + * Extend as much as parent let it take. + */ + MOST = 2 +} +export interface LayoutConfig { + widthSpec?: LayoutSpec; + heightSpec?: LayoutSpec; + margin?: { + left?: number; + right?: number; + top?: number; + bottom?: number; + }; + alignment?: Gravity; + weight?: number; +} +export declare class LayoutConfigImpl implements LayoutConfig, Modeling { + widthSpec?: LayoutSpec; + heightSpec?: LayoutSpec; + margin?: { + left?: number; + right?: number; + top?: number; + bottom?: number; + }; + alignment?: Gravity; + weight?: number; + fit(): this; + most(): this; + just(): this; + configWidth(w: LayoutSpec): this; + configHeight(h: LayoutSpec): this; + configMargin(m: { + left?: number; + right?: number; + top?: number; + bottom?: number; + }): this; + configAlignment(a: Gravity): this; + configWeight(w: number): this; + toModel(): { + widthSpec: LayoutSpec | undefined; + heightSpec: LayoutSpec | undefined; + margin: { + left?: number | undefined; + right?: number | undefined; + top?: number | undefined; + bottom?: number | undefined; + } | undefined; + alignment: number | undefined; + weight: number | undefined; + }; +} +export declare function layoutConfig(): LayoutConfigImpl; diff --git a/doric-js/lib/src/util/layoutconfig.js b/doric-js/lib/src/util/layoutconfig.js new file mode 100644 index 00000000..28cd90ad --- /dev/null +++ b/doric-js/lib/src/util/layoutconfig.js @@ -0,0 +1,64 @@ +export var LayoutSpec; +(function (LayoutSpec) { + /** + * Depends on what's been set on width or height. + */ + LayoutSpec[LayoutSpec["JUST"] = 0] = "JUST"; + /** + * Depends on it's content. + */ + LayoutSpec[LayoutSpec["FIT"] = 1] = "FIT"; + /** + * Extend as much as parent let it take. + */ + LayoutSpec[LayoutSpec["MOST"] = 2] = "MOST"; +})(LayoutSpec || (LayoutSpec = {})); +export class LayoutConfigImpl { + fit() { + this.widthSpec = LayoutSpec.FIT; + this.heightSpec = LayoutSpec.FIT; + return this; + } + most() { + this.widthSpec = LayoutSpec.MOST; + this.heightSpec = LayoutSpec.MOST; + return this; + } + just() { + this.widthSpec = LayoutSpec.JUST; + this.heightSpec = LayoutSpec.JUST; + return this; + } + configWidth(w) { + this.widthSpec = w; + return this; + } + configHeight(h) { + this.heightSpec = h; + return this; + } + configMargin(m) { + this.margin = m; + return this; + } + configAlignment(a) { + this.alignment = a; + return this; + } + configWeight(w) { + this.weight = w; + return this; + } + toModel() { + return { + widthSpec: this.widthSpec, + heightSpec: this.heightSpec, + margin: this.margin, + alignment: this.alignment ? this.alignment.toModel() : undefined, + weight: this.weight, + }; + } +} +export function layoutConfig() { + return new LayoutConfigImpl; +} diff --git a/doric-js/lib/src/util/log.d.ts b/doric-js/lib/src/util/log.d.ts new file mode 100644 index 00000000..a1de2d68 --- /dev/null +++ b/doric-js/lib/src/util/log.d.ts @@ -0,0 +1,3 @@ +export declare function log(...args: any): void; +export declare function loge(...message: any): void; +export declare function logw(...message: any): void; diff --git a/doric-js/lib/src/util/log.js b/doric-js/lib/src/util/log.js new file mode 100644 index 00000000..84696187 --- /dev/null +++ b/doric-js/lib/src/util/log.js @@ -0,0 +1,49 @@ +function toString(message) { + if (message instanceof Function) { + return message.toString(); + } + else if (message instanceof Object) { + try { + return JSON.stringify(message); + } + catch (e) { + return message.toString(); + } + } + else if (message === undefined) { + return "undefined"; + } + else { + return message.toString(); + } +} +export function log(...args) { + let out = ""; + for (let i = 0; i < arguments.length; i++) { + if (i > 0) { + out += ','; + } + out += toString(arguments[i]); + } + nativeLog('d', out); +} +export function loge(...message) { + let out = ""; + for (let i = 0; i < arguments.length; i++) { + if (i > 0) { + out += ','; + } + out += toString(arguments[i]); + } + nativeLog('e', out); +} +export function logw(...message) { + let out = ""; + for (let i = 0; i < arguments.length; i++) { + if (i > 0) { + out += ','; + } + out += toString(arguments[i]); + } + nativeLog('w', out); +} diff --git a/doric-js/lib/src/util/types.d.ts b/doric-js/lib/src/util/types.d.ts new file mode 100644 index 00000000..0dae8273 --- /dev/null +++ b/doric-js/lib/src/util/types.d.ts @@ -0,0 +1,19 @@ +export interface Modeling { + toModel(): Model; +} +export declare function obj2Model(obj: Model): Model; +declare type _M = string | number | boolean | Modeling | { + [index: string]: Model; +} | undefined; +export declare type Model = _M | Array<_M>; +export declare type Binder = (v: T) => void; +export declare class Mutable { + private val; + private binders; + get: () => T; + set: (v: T) => void; + private constructor(); + bind(binder: Binder): void; + static of(v: E): Mutable; +} +export {}; diff --git a/doric-js/lib/src/util/types.js b/doric-js/lib/src/util/types.js new file mode 100644 index 00000000..c1a5f16e --- /dev/null +++ b/doric-js/lib/src/util/types.js @@ -0,0 +1,43 @@ +export function obj2Model(obj) { + if (obj instanceof Array) { + return obj.map(e => obj2Model(e)); + } + else if (obj instanceof Object) { + if (Reflect.has(obj, 'toModel') && Reflect.get(obj, 'toModel') instanceof Function) { + obj = Reflect.apply(Reflect.get(obj, 'toModel'), obj, []); + return obj; + } + else { + for (let key in obj) { + const val = Reflect.get(obj, key); + Reflect.set(obj, key, obj2Model(val)); + } + return obj; + } + } + else { + return obj; + } +} +export class Mutable { + constructor(v) { + this.binders = new Set; + this.get = () => { + return this.val; + }; + this.set = (v) => { + this.val = v; + this.binders.forEach(e => { + Reflect.apply(e, undefined, [this.val]); + }); + }; + this.val = v; + } + bind(binder) { + this.binders.add(binder); + Reflect.apply(binder, undefined, [this.val]); + } + static of(v) { + return new Mutable(v); + } +} diff --git a/doric-js/lib/src/util/uniqueId.d.ts b/doric-js/lib/src/util/uniqueId.d.ts new file mode 100644 index 00000000..200ad9be --- /dev/null +++ b/doric-js/lib/src/util/uniqueId.d.ts @@ -0,0 +1 @@ +export declare function uniqueId(prefix: string): string; diff --git a/doric-js/lib/src/util/uniqueId.js b/doric-js/lib/src/util/uniqueId.js new file mode 100644 index 00000000..64e23cd1 --- /dev/null +++ b/doric-js/lib/src/util/uniqueId.js @@ -0,0 +1,19 @@ +/* + * 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. + */ +let __uniqueId__ = 0; +export function uniqueId(prefix) { + return `__${prefix}_${__uniqueId__++}__`; +} diff --git a/doric-js/lib/src/widget/flowlayout.d.ts b/doric-js/lib/src/widget/flowlayout.d.ts new file mode 100644 index 00000000..150a85fa --- /dev/null +++ b/doric-js/lib/src/widget/flowlayout.d.ts @@ -0,0 +1,43 @@ +import { Stack } from './layouts'; +import { IView, Superview, View } from '../ui/view'; +export declare class FlowLayoutItem extends Stack { + /** + * Set to reuse native view + */ + identifier?: string; +} +export interface IFlowLayout extends IView { + renderItem: (index: number) => FlowLayoutItem; + itemCount: number; + batchCount?: number; + columnCount?: number; + columnSpace?: number; + rowSpace?: number; +} +export declare class FlowLayout extends Superview implements IFlowLayout { + private cachedViews; + private ignoreDirtyCallOnce; + allSubviews(): IterableIterator | FlowLayoutItem[]; + columnCount: number; + columnSpace?: number; + rowSpace?: number; + itemCount: number; + renderItem: (index: number) => FlowLayoutItem; + batchCount: number; + onLoadMore?: () => void; + loadMore?: boolean; + loadMoreView?: FlowLayoutItem; + reset(): void; + private getItem; + isDirty(): boolean; + private renderBunchedItems; + toModel(): { + id: string; + type: string; + props: { + [index: string]: import("../util/types").Model; + }; + }; +} +export declare function flowlayout(config: IFlowLayout): FlowLayout; +export declare function flowItem(item: View): FlowLayoutItem; diff --git a/doric-js/lib/src/widget/flowlayout.js b/doric-js/lib/src/widget/flowlayout.js new file mode 100644 index 00000000..c05ed7a9 --- /dev/null +++ b/doric-js/lib/src/widget/flowlayout.js @@ -0,0 +1,131 @@ +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +/* + * 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 { Stack } from './layouts'; +import { Property, Superview } from '../ui/view'; +import { layoutConfig } from '../util/index.util'; +export class FlowLayoutItem extends Stack { +} +__decorate([ + Property, + __metadata("design:type", String) +], FlowLayoutItem.prototype, "identifier", void 0); +export class FlowLayout extends Superview { + constructor() { + super(...arguments); + this.cachedViews = new Map; + this.ignoreDirtyCallOnce = false; + this.columnCount = 2; + this.itemCount = 0; + this.batchCount = 15; + } + allSubviews() { + if (this.loadMoreView) { + return [...this.cachedViews.values(), this.loadMoreView]; + } + else { + return this.cachedViews.values(); + } + } + reset() { + this.cachedViews.clear(); + this.itemCount = 0; + } + getItem(itemIdx) { + let view = this.renderItem(itemIdx); + view.superview = this; + this.cachedViews.set(`${itemIdx}`, view); + return view; + } + isDirty() { + if (this.ignoreDirtyCallOnce) { + this.ignoreDirtyCallOnce = false; + //Ignore the dirty call once. + return false; + } + return super.isDirty(); + } + renderBunchedItems(start, length) { + this.ignoreDirtyCallOnce = true; + return new Array(Math.min(length, this.itemCount - start)).fill(0).map((_, idx) => { + const listItem = this.getItem(start + idx); + return listItem.toModel(); + }); + } + toModel() { + if (this.loadMoreView) { + this.dirtyProps['loadMoreView'] = this.loadMoreView.viewId; + } + return super.toModel(); + } +} +__decorate([ + Property, + __metadata("design:type", Object) +], FlowLayout.prototype, "columnCount", void 0); +__decorate([ + Property, + __metadata("design:type", Number) +], FlowLayout.prototype, "columnSpace", void 0); +__decorate([ + Property, + __metadata("design:type", Number) +], FlowLayout.prototype, "rowSpace", void 0); +__decorate([ + Property, + __metadata("design:type", Object) +], FlowLayout.prototype, "itemCount", void 0); +__decorate([ + Property, + __metadata("design:type", Function) +], FlowLayout.prototype, "renderItem", void 0); +__decorate([ + Property, + __metadata("design:type", Object) +], FlowLayout.prototype, "batchCount", void 0); +__decorate([ + Property, + __metadata("design:type", Function) +], FlowLayout.prototype, "onLoadMore", void 0); +__decorate([ + Property, + __metadata("design:type", Boolean) +], FlowLayout.prototype, "loadMore", void 0); +__decorate([ + Property, + __metadata("design:type", FlowLayoutItem) +], FlowLayout.prototype, "loadMoreView", void 0); +export function flowlayout(config) { + const ret = new FlowLayout; + for (let key in config) { + Reflect.set(ret, key, Reflect.get(config, key, config), ret); + } + return ret; +} +export function flowItem(item) { + return (new FlowLayoutItem).also((it) => { + it.layoutConfig = layoutConfig().fit(); + it.addChild(item); + }); +} diff --git a/doric-js/lib/src/widget/image.d.ts b/doric-js/lib/src/widget/image.d.ts new file mode 100644 index 00000000..fbee6139 --- /dev/null +++ b/doric-js/lib/src/widget/image.d.ts @@ -0,0 +1,27 @@ +import { IView, View } from "../ui/view"; +export declare enum ScaleType { + ScaleToFill = 0, + ScaleAspectFit = 1, + ScaleAspectFill = 2 +} +export interface IImage extends IView { + imageUrl?: string; + imageBase64?: string; + scaleType?: ScaleType; + isBlur?: boolean; + loadCallback?: (image: { + width: number; + height: number; + } | undefined) => void; +} +export declare class Image extends View implements IImage { + imageUrl?: string; + imageBase64?: string; + scaleType?: ScaleType; + isBlur?: boolean; + loadCallback?: (image: { + width: number; + height: number; + } | undefined) => void; +} +export declare function image(config: IImage): Image; diff --git a/doric-js/lib/src/widget/image.js b/doric-js/lib/src/widget/image.js new file mode 100644 index 00000000..9dce779c --- /dev/null +++ b/doric-js/lib/src/widget/image.js @@ -0,0 +1,62 @@ +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +/* + * 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, Property } from "../ui/view"; +import { layoutConfig } from "../util/layoutconfig"; +export var ScaleType; +(function (ScaleType) { + ScaleType[ScaleType["ScaleToFill"] = 0] = "ScaleToFill"; + ScaleType[ScaleType["ScaleAspectFit"] = 1] = "ScaleAspectFit"; + ScaleType[ScaleType["ScaleAspectFill"] = 2] = "ScaleAspectFill"; +})(ScaleType || (ScaleType = {})); +export class Image extends View { +} +__decorate([ + Property, + __metadata("design:type", String) +], Image.prototype, "imageUrl", void 0); +__decorate([ + Property, + __metadata("design:type", String) +], Image.prototype, "imageBase64", void 0); +__decorate([ + Property, + __metadata("design:type", Number) +], Image.prototype, "scaleType", void 0); +__decorate([ + Property, + __metadata("design:type", Boolean) +], Image.prototype, "isBlur", void 0); +__decorate([ + Property, + __metadata("design:type", Function) +], Image.prototype, "loadCallback", void 0); +export function image(config) { + const ret = new Image; + ret.layoutConfig = layoutConfig().fit(); + for (let key in config) { + Reflect.set(ret, key, Reflect.get(config, key, config), ret); + } + return ret; +} diff --git a/doric-js/lib/src/widget/index.widget.d.ts b/doric-js/lib/src/widget/index.widget.d.ts new file mode 100644 index 00000000..db0d5271 --- /dev/null +++ b/doric-js/lib/src/widget/index.widget.d.ts @@ -0,0 +1,10 @@ +export * from './layouts'; +export * from './text'; +export * from './image'; +export * from './list'; +export * from './slider'; +export * from './scroller'; +export * from './refreshable'; +export * from './flowlayout'; +export * from './input'; +export * from './nestedSlider'; diff --git a/doric-js/lib/src/widget/index.widget.js b/doric-js/lib/src/widget/index.widget.js new file mode 100644 index 00000000..5b137579 --- /dev/null +++ b/doric-js/lib/src/widget/index.widget.js @@ -0,0 +1,25 @@ +/* + * 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. + */ +export * from './layouts'; +export * from './text'; +export * from './image'; +export * from './list'; +export * from './slider'; +export * from './scroller'; +export * from './refreshable'; +export * from './flowlayout'; +export * from './input'; +export * from './nestedSlider'; diff --git a/doric-js/lib/src/widget/input.d.ts b/doric-js/lib/src/widget/input.d.ts new file mode 100644 index 00000000..f54de8b5 --- /dev/null +++ b/doric-js/lib/src/widget/input.d.ts @@ -0,0 +1,31 @@ +import { View, IView } from "../ui/view"; +import { Color } from "../util/color"; +import { Gravity } from "../util/gravity"; +import { BridgeContext } from "../runtime/global"; +export interface IInput extends IView { + text?: string; + textColor?: Color; + textSize?: number; + hintText?: string; + hintTextColor?: Color; + multilines?: boolean; + textAlignment?: Gravity; + onTextChange?: (text: string) => void; + onFocusChange?: (focused: boolean) => void; +} +export declare class Input extends View implements IInput { + text?: string; + textColor?: Color; + textSize?: number; + hintText?: string; + hintTextColor?: Color; + multiline?: boolean; + textAlignment?: Gravity; + onTextChange?: (text: string) => void; + onFocusChange?: (focused: boolean) => void; + getText(context: BridgeContext): Promise; + setSelection(context: BridgeContext, start: number, end?: number): Promise; + requestFocus(context: BridgeContext): Promise; + releaseFocus(context: BridgeContext): Promise; +} +export declare function input(config: IInput): Input; diff --git a/doric-js/lib/src/widget/input.js b/doric-js/lib/src/widget/input.js new file mode 100644 index 00000000..741731f0 --- /dev/null +++ b/doric-js/lib/src/widget/input.js @@ -0,0 +1,89 @@ +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +/* + * 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, Property } from "../ui/view"; +import { Color } from "../util/color"; +import { Gravity } from "../util/gravity"; +import { layoutConfig } from "../util/index.util"; +export class Input extends View { + getText(context) { + return this.nativeChannel(context, 'getText')(); + } + setSelection(context, start, end = start) { + return this.nativeChannel(context, 'setSelection')({ + start, + end, + }); + } + requestFocus(context) { + return this.nativeChannel(context, 'requestFocus')(); + } + releaseFocus(context) { + return this.nativeChannel(context, 'releaseFocus')(); + } +} +__decorate([ + Property, + __metadata("design:type", String) +], Input.prototype, "text", void 0); +__decorate([ + Property, + __metadata("design:type", Color) +], Input.prototype, "textColor", void 0); +__decorate([ + Property, + __metadata("design:type", Number) +], Input.prototype, "textSize", void 0); +__decorate([ + Property, + __metadata("design:type", String) +], Input.prototype, "hintText", void 0); +__decorate([ + Property, + __metadata("design:type", Color) +], Input.prototype, "hintTextColor", void 0); +__decorate([ + Property, + __metadata("design:type", Boolean) +], Input.prototype, "multiline", void 0); +__decorate([ + Property, + __metadata("design:type", Gravity) +], Input.prototype, "textAlignment", void 0); +__decorate([ + Property, + __metadata("design:type", Function) +], Input.prototype, "onTextChange", void 0); +__decorate([ + Property, + __metadata("design:type", Function) +], Input.prototype, "onFocusChange", void 0); +export function input(config) { + const ret = new Input; + ret.layoutConfig = layoutConfig().just(); + for (let key in config) { + Reflect.set(ret, key, Reflect.get(config, key, config), ret); + } + return ret; +} diff --git a/doric-js/lib/src/widget/layouts.d.ts b/doric-js/lib/src/widget/layouts.d.ts new file mode 100644 index 00000000..26a3b3e5 --- /dev/null +++ b/doric-js/lib/src/widget/layouts.d.ts @@ -0,0 +1,28 @@ +import { Group, IView, View } from "../ui/view"; +import { Gravity } from "../util/gravity"; +export interface IStack extends IView { +} +export declare class Stack extends Group implements IStack { +} +export declare class Root extends Stack { +} +declare class LinearLayout extends Group { + space?: number; + gravity?: Gravity; +} +export interface IVLayout extends IView { + space?: number; + gravity?: Gravity; +} +export declare class VLayout extends LinearLayout implements VLayout { +} +export interface IHLayout extends IView { + space?: number; + gravity?: Gravity; +} +export declare class HLayout extends LinearLayout implements IHLayout { +} +export declare function stack(views: View[]): Stack; +export declare function hlayout(views: View[]): HLayout; +export declare function vlayout(views: View[]): VLayout; +export {}; diff --git a/doric-js/lib/src/widget/layouts.js b/doric-js/lib/src/widget/layouts.js new file mode 100644 index 00000000..94c6c85e --- /dev/null +++ b/doric-js/lib/src/widget/layouts.js @@ -0,0 +1,69 @@ +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +/* + * 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 { Group, Property } from "../ui/view"; +import { Gravity } from "../util/gravity"; +import { layoutConfig } from "../util/layoutconfig"; +export class Stack extends Group { +} +export class Root extends Stack { +} +class LinearLayout extends Group { +} +__decorate([ + Property, + __metadata("design:type", Number) +], LinearLayout.prototype, "space", void 0); +__decorate([ + Property, + __metadata("design:type", Gravity) +], LinearLayout.prototype, "gravity", void 0); +export class VLayout extends LinearLayout { +} +export class HLayout extends LinearLayout { +} +export function stack(views) { + const ret = new Stack; + ret.layoutConfig = layoutConfig().fit(); + for (let v of views) { + ret.addChild(v); + } + return ret; +} +export function hlayout(views) { + const ret = new HLayout; + ret.layoutConfig = layoutConfig().fit(); + for (let v of views) { + ret.addChild(v); + } + return ret; +} +export function vlayout(views) { + const ret = new VLayout; + ret.layoutConfig = layoutConfig().fit(); + for (let v of views) { + ret.addChild(v); + } + return ret; +} diff --git a/doric-js/lib/src/widget/list.d.ts b/doric-js/lib/src/widget/list.d.ts new file mode 100644 index 00000000..e31c8588 --- /dev/null +++ b/doric-js/lib/src/widget/list.d.ts @@ -0,0 +1,37 @@ +import { View, Superview, IView } from "../ui/view"; +import { Stack } from "./layouts"; +export declare class ListItem extends Stack { + /** + * Set to reuse native view + */ + identifier?: string; +} +export interface IList extends IView { + renderItem: (index: number) => ListItem; + itemCount: number; + batchCount?: number; +} +export declare class List extends Superview implements IList { + private cachedViews; + private ignoreDirtyCallOnce; + allSubviews(): IterableIterator | ListItem[]; + itemCount: number; + renderItem: (index: number) => ListItem; + batchCount: number; + onLoadMore?: () => void; + loadMore?: boolean; + loadMoreView?: ListItem; + reset(): void; + private getItem; + isDirty(): boolean; + private renderBunchedItems; + toModel(): { + id: string; + type: string; + props: { + [index: string]: import("../..").Model; + }; + }; +} +export declare function list(config: IList): List; +export declare function listItem(item: View): ListItem; diff --git a/doric-js/lib/src/widget/list.js b/doric-js/lib/src/widget/list.js new file mode 100644 index 00000000..cd9e1f4e --- /dev/null +++ b/doric-js/lib/src/widget/list.js @@ -0,0 +1,121 @@ +/* + * 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. + */ +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +import { Property, Superview } from "../ui/view"; +import { Stack } from "./layouts"; +import { layoutConfig, LayoutSpec } from "../util/layoutconfig"; +export class ListItem extends Stack { +} +__decorate([ + Property, + __metadata("design:type", String) +], ListItem.prototype, "identifier", void 0); +export class List extends Superview { + constructor() { + super(...arguments); + this.cachedViews = new Map; + this.ignoreDirtyCallOnce = false; + this.itemCount = 0; + this.batchCount = 15; + } + allSubviews() { + if (this.loadMoreView) { + return [...this.cachedViews.values(), this.loadMoreView]; + } + else { + return this.cachedViews.values(); + } + } + reset() { + this.cachedViews.clear(); + this.itemCount = 0; + } + getItem(itemIdx) { + let view = this.cachedViews.get(`${itemIdx}`); + if (view === undefined) { + view = this.renderItem(itemIdx); + view.superview = this; + this.cachedViews.set(`${itemIdx}`, view); + } + return view; + } + isDirty() { + if (this.ignoreDirtyCallOnce) { + this.ignoreDirtyCallOnce = false; + //Ignore the dirty call once. + return false; + } + return super.isDirty(); + } + renderBunchedItems(start, length) { + this.ignoreDirtyCallOnce = true; + return new Array(Math.min(length, this.itemCount - start)).fill(0).map((_, idx) => { + const listItem = this.getItem(start + idx); + return listItem.toModel(); + }); + } + toModel() { + if (this.loadMoreView) { + this.dirtyProps['loadMoreView'] = this.loadMoreView.viewId; + } + return super.toModel(); + } +} +__decorate([ + Property, + __metadata("design:type", Object) +], List.prototype, "itemCount", void 0); +__decorate([ + Property, + __metadata("design:type", Function) +], List.prototype, "renderItem", void 0); +__decorate([ + Property, + __metadata("design:type", Object) +], List.prototype, "batchCount", void 0); +__decorate([ + Property, + __metadata("design:type", Function) +], List.prototype, "onLoadMore", void 0); +__decorate([ + Property, + __metadata("design:type", Boolean) +], List.prototype, "loadMore", void 0); +__decorate([ + Property, + __metadata("design:type", ListItem) +], List.prototype, "loadMoreView", void 0); +export function list(config) { + const ret = new List; + for (let key in config) { + Reflect.set(ret, key, Reflect.get(config, key, config), ret); + } + return ret; +} +export function listItem(item) { + return (new ListItem).also((it) => { + it.layoutConfig = layoutConfig().most().configHeight(LayoutSpec.FIT); + it.addChild(item); + }); +} diff --git a/doric-js/lib/src/widget/nestedSlider.d.ts b/doric-js/lib/src/widget/nestedSlider.d.ts new file mode 100644 index 00000000..4f6f6f44 --- /dev/null +++ b/doric-js/lib/src/widget/nestedSlider.d.ts @@ -0,0 +1,8 @@ +import { Group, View } from '../ui/view'; +import { BridgeContext } from '../runtime/global'; +export declare class NestedSlider extends Group { + onPageSlided?: (index: number) => void; + addSlideItem(view: View): void; + slidePage(context: BridgeContext, page: number, smooth?: boolean): Promise; + getSlidedPage(context: BridgeContext): Promise; +} diff --git a/doric-js/lib/src/widget/nestedSlider.js b/doric-js/lib/src/widget/nestedSlider.js new file mode 100644 index 00000000..8c52facc --- /dev/null +++ b/doric-js/lib/src/widget/nestedSlider.js @@ -0,0 +1,40 @@ +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +/* + * 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 { Group, Property } from '../ui/view'; +export class NestedSlider extends Group { + addSlideItem(view) { + this.addChild(view); + } + slidePage(context, page, smooth = false) { + return this.nativeChannel(context, "slidePage")({ page, smooth }); + } + getSlidedPage(context) { + return this.nativeChannel(context, "getSlidedPage")(); + } +} +__decorate([ + Property, + __metadata("design:type", Function) +], NestedSlider.prototype, "onPageSlided", void 0); diff --git a/doric-js/lib/src/widget/refreshable.d.ts b/doric-js/lib/src/widget/refreshable.d.ts new file mode 100644 index 00000000..55b3e984 --- /dev/null +++ b/doric-js/lib/src/widget/refreshable.d.ts @@ -0,0 +1,33 @@ +import { View, Superview, IView } from "../ui/view"; +import { List } from "./list"; +import { Scroller } from "./scroller"; +import { BridgeContext } from "../runtime/global"; +export interface IRefreshable extends IView { + content: View; + header?: View; + onRefresh?: () => void; +} +export declare class Refreshable extends Superview implements IRefreshable { + content: List | Scroller; + header?: View; + onRefresh?: () => void; + allSubviews(): View[]; + setRefreshable(context: BridgeContext, refreshable: boolean): Promise; + setRefreshing(context: BridgeContext, refreshing: boolean): Promise; + isRefreshable(context: BridgeContext): Promise; + isRefreshing(context: BridgeContext): Promise; + toModel(): { + id: string; + type: string; + props: { + [index: string]: import("../..").Model; + }; + }; +} +export declare function refreshable(config: IRefreshable): Refreshable; +export interface IPullable { + startAnimation(): void; + stopAnimation(): void; + setPullingDistance(distance: number): void; +} +export declare function pullable(v: View, config: IPullable): View; diff --git a/doric-js/lib/src/widget/refreshable.js b/doric-js/lib/src/widget/refreshable.js new file mode 100644 index 00000000..2d502d6d --- /dev/null +++ b/doric-js/lib/src/widget/refreshable.js @@ -0,0 +1,55 @@ +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +import { Property, Superview } from "../ui/view"; +import { layoutConfig } from "../util/layoutconfig"; +export class Refreshable extends Superview { + allSubviews() { + const ret = [this.content]; + if (this.header) { + ret.push(this.header); + } + return ret; + } + setRefreshable(context, refreshable) { + return this.nativeChannel(context, 'setRefreshable')(refreshable); + } + setRefreshing(context, refreshing) { + return this.nativeChannel(context, 'setRefreshing')(refreshing); + } + isRefreshable(context) { + return this.nativeChannel(context, 'isRefreshable')(); + } + isRefreshing(context) { + return this.nativeChannel(context, 'isRefreshing')(); + } + toModel() { + this.dirtyProps.content = this.content.viewId; + this.dirtyProps.header = (this.header || {}).viewId; + return super.toModel(); + } +} +__decorate([ + Property, + __metadata("design:type", Function) +], Refreshable.prototype, "onRefresh", void 0); +export function refreshable(config) { + const ret = new Refreshable; + ret.layoutConfig = layoutConfig().fit(); + for (let key in config) { + Reflect.set(ret, key, Reflect.get(config, key, config), ret); + } + return ret; +} +export function pullable(v, config) { + Reflect.set(v, 'startAnimation', config.startAnimation); + Reflect.set(v, 'stopAnimation', config.stopAnimation); + Reflect.set(v, 'setPullingDistance', config.setPullingDistance); + return v; +} diff --git a/doric-js/lib/src/widget/scroller.d.ts b/doric-js/lib/src/widget/scroller.d.ts new file mode 100644 index 00000000..7efafb8d --- /dev/null +++ b/doric-js/lib/src/widget/scroller.d.ts @@ -0,0 +1,16 @@ +import { Superview, View, IView } from '../ui/view'; +export declare function scroller(content: View): Scroller; +export interface IScroller extends IView { + content: View; +} +export declare class Scroller extends Superview implements IScroller { + content: View; + allSubviews(): View[]; + toModel(): { + id: string; + type: string; + props: { + [index: string]: import("../..").Model; + }; + }; +} diff --git a/doric-js/lib/src/widget/scroller.js b/doric-js/lib/src/widget/scroller.js new file mode 100644 index 00000000..cc6ced35 --- /dev/null +++ b/doric-js/lib/src/widget/scroller.js @@ -0,0 +1,32 @@ +/* + * 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 { Superview } from '../ui/view'; +import { layoutConfig } from '../util/layoutconfig'; +export function scroller(content) { + return (new Scroller).also(v => { + v.layoutConfig = layoutConfig().fit(); + v.content = content; + }); +} +export class Scroller extends Superview { + allSubviews() { + return [this.content]; + } + toModel() { + this.dirtyProps.content = this.content.viewId; + return super.toModel(); + } +} diff --git a/doric-js/lib/src/widget/slider.d.ts b/doric-js/lib/src/widget/slider.d.ts new file mode 100644 index 00000000..74ef4672 --- /dev/null +++ b/doric-js/lib/src/widget/slider.d.ts @@ -0,0 +1,31 @@ +import { Superview, View, IView } from "../ui/view"; +import { Stack } from "./layouts"; +import { BridgeContext } from "../runtime/global"; +export declare class SlideItem extends Stack { + /** + * Set to reuse native view + */ + identifier?: string; +} +export interface ISlider extends IView { + renderPage: (index: number) => SlideItem; + itemCount: number; + batchCount?: number; + onPageSlided?: (index: number) => void; +} +export declare class Slider extends Superview implements ISlider { + private cachedViews; + private ignoreDirtyCallOnce; + allSubviews(): IterableIterator; + itemCount: number; + renderPage: (index: number) => SlideItem; + batchCount: number; + onPageSlided?: (index: number) => void; + private getItem; + isDirty(): boolean; + private renderBunchedItems; + slidePage(context: BridgeContext, page: number, smooth?: boolean): Promise; + getSlidedPage(context: BridgeContext): Promise; +} +export declare function slideItem(item: View): SlideItem; +export declare function slider(config: ISlider): Slider; diff --git a/doric-js/lib/src/widget/slider.js b/doric-js/lib/src/widget/slider.js new file mode 100644 index 00000000..418a090c --- /dev/null +++ b/doric-js/lib/src/widget/slider.js @@ -0,0 +1,89 @@ +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +import { Superview, Property } from "../ui/view"; +import { Stack } from "./layouts"; +import { layoutConfig } from "../util/layoutconfig"; +export class SlideItem extends Stack { +} +__decorate([ + Property, + __metadata("design:type", String) +], SlideItem.prototype, "identifier", void 0); +export class Slider extends Superview { + constructor() { + super(...arguments); + this.cachedViews = new Map; + this.ignoreDirtyCallOnce = false; + this.itemCount = 0; + this.batchCount = 3; + } + allSubviews() { + return this.cachedViews.values(); + } + getItem(itemIdx) { + let view = this.cachedViews.get(`${itemIdx}`); + if (view === undefined) { + view = this.renderPage(itemIdx); + view.superview = this; + this.cachedViews.set(`${itemIdx}`, view); + } + return view; + } + isDirty() { + if (this.ignoreDirtyCallOnce) { + this.ignoreDirtyCallOnce = false; + //Ignore the dirty call once. + return false; + } + return super.isDirty(); + } + renderBunchedItems(start, length) { + this.ignoreDirtyCallOnce = true; + return new Array(Math.min(length, this.itemCount - start)).fill(0).map((_, idx) => { + const slideItem = this.getItem(start + idx); + return slideItem.toModel(); + }); + } + slidePage(context, page, smooth = false) { + return this.nativeChannel(context, "slidePage")({ page, smooth }); + } + getSlidedPage(context) { + return this.nativeChannel(context, "getSlidedPage")(); + } +} +__decorate([ + Property, + __metadata("design:type", Object) +], Slider.prototype, "itemCount", void 0); +__decorate([ + Property, + __metadata("design:type", Function) +], Slider.prototype, "renderPage", void 0); +__decorate([ + Property, + __metadata("design:type", Object) +], Slider.prototype, "batchCount", void 0); +__decorate([ + Property, + __metadata("design:type", Function) +], Slider.prototype, "onPageSlided", void 0); +export function slideItem(item) { + return (new SlideItem).also((it) => { + it.layoutConfig = layoutConfig().fit(); + it.addChild(item); + }); +} +export function slider(config) { + const ret = new Slider; + for (let key in config) { + Reflect.set(ret, key, Reflect.get(config, key, config), ret); + } + return ret; +} diff --git a/doric-js/lib/src/widget/text.d.ts b/doric-js/lib/src/widget/text.d.ts new file mode 100644 index 00000000..84732f91 --- /dev/null +++ b/doric-js/lib/src/widget/text.d.ts @@ -0,0 +1,18 @@ +import { IView, View } from "../ui/view"; +import { Color } from "../util/color"; +import { Gravity } from "../util/gravity"; +export interface IText extends IView { + text?: string; + textColor?: Color; + textSize?: number; + maxLines?: number; + textAlignment?: Gravity; +} +export declare class Text extends View implements IText { + text?: string; + textColor?: Color; + textSize?: number; + maxLines?: number; + textAlignment?: Gravity; +} +export declare function text(config: IText): Text; diff --git a/doric-js/lib/src/widget/text.js b/doric-js/lib/src/widget/text.js new file mode 100644 index 00000000..fe2f0a15 --- /dev/null +++ b/doric-js/lib/src/widget/text.js @@ -0,0 +1,58 @@ +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +/* + * 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, Property } from "../ui/view"; +import { Color } from "../util/color"; +import { Gravity } from "../util/gravity"; +import { layoutConfig } from "../util/layoutconfig"; +export class Text extends View { +} +__decorate([ + Property, + __metadata("design:type", String) +], Text.prototype, "text", void 0); +__decorate([ + Property, + __metadata("design:type", Color) +], Text.prototype, "textColor", void 0); +__decorate([ + Property, + __metadata("design:type", Number) +], Text.prototype, "textSize", void 0); +__decorate([ + Property, + __metadata("design:type", Number) +], Text.prototype, "maxLines", void 0); +__decorate([ + Property, + __metadata("design:type", Gravity) +], Text.prototype, "textAlignment", void 0); +export function text(config) { + const ret = new Text; + ret.layoutConfig = layoutConfig().fit(); + for (let key in config) { + Reflect.set(ret, key, Reflect.get(config, key, config), ret); + } + return ret; +} diff --git a/doric-js/package.json b/doric-js/package.json index 53cfb28f..1bebea52 100644 --- a/doric-js/package.json +++ b/doric-js/package.json @@ -1,13 +1,15 @@ { "name": "doric", - "version": "0.2.1", + "version": "0.2.2-alpha.0", "description": "The JS Framework of Doric", "main": "bundle/doric-vm.js", + "types": "./lib/index.d.ts", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", - "build": "tsc -p .&& rollup -c", + "build": "tsc -d -p .&& rollup -c", "dev": "tsc -w -p . & rollup -c -w", - "clean": "rm -rf build && rm -rf bundle" + "clean": "rm -rf build && rm -rf bundle", + "prepublish": "npm run build" }, "repository": { "type": "https", @@ -28,4 +30,4 @@ "typescript": "^3.7.4", "ws": "^7.2.1" } -} \ No newline at end of file +} diff --git a/doric-js/rollup.config.js b/doric-js/rollup.config.js index c2d1388c..64065757 100644 --- a/doric-js/rollup.config.js +++ b/doric-js/rollup.config.js @@ -4,7 +4,7 @@ import resolve from '@rollup/plugin-node-resolve' export default [ { - input: "build/index.runtime.js", + input: "lib/index.runtime.js", output: { name: "doric", format: "iife", @@ -13,13 +13,13 @@ export default [ plugins: [ resolve({ mainFields: ["jsnext"] }), ], - onwarn: function(warning) { - if ( warning.code === 'THIS_IS_UNDEFINED' ) { return; } - console.warn( warning.message ); + onwarn: function (warning) { + if (warning.code === 'THIS_IS_UNDEFINED') { return; } + console.warn(warning.message); } }, { - input: "build/index.js", + input: "lib/index.js", output: { format: "cjs", file: "bundle/doric-lib.js", @@ -28,13 +28,13 @@ export default [ resolve({ mainFields: ["jsnext"] }), ], external: ['reflect-metadata'], - onwarn: function(warning) { - if ( warning.code === 'THIS_IS_UNDEFINED' ) { return; } - console.warn( warning.message ); + onwarn: function (warning) { + if (warning.code === 'THIS_IS_UNDEFINED') { return; } + console.warn(warning.message); } }, { - input: "build/index.debug.js", + input: "lib/index.debug.js", output: { format: "cjs", file: "bundle/doric-vm.js", @@ -43,9 +43,9 @@ export default [ resolve({ mainFields: ["jsnext"] }), ], external: ['ws'], - onwarn: function(warning) { - if ( warning.code === 'THIS_IS_UNDEFINED' ) { return; } - console.warn( warning.message ); + onwarn: function (warning) { + if (warning.code === 'THIS_IS_UNDEFINED') { return; } + console.warn(warning.message); } }, ] \ No newline at end of file diff --git a/doric-js/tsconfig.json b/doric-js/tsconfig.json index 7946f238..393910a2 100644 --- a/doric-js/tsconfig.json +++ b/doric-js/tsconfig.json @@ -13,7 +13,7 @@ // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ // "sourceMap": true, /* Generates corresponding '.map' file. */ // "outFile": "./", /* Concatenate and emit output to single file. */ - "outDir": "build/", /* Redirect output structure to the directory. */ + "outDir": "lib/", /* Redirect output structure to the directory. */ // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ // "composite": true, /* Enable project compilation */ // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */