move files from main project
This commit is contained in:
parent
2184df9f45
commit
a8c9778496
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
node_modules/
|
||||
build/
|
||||
bundle/
|
||||
demo/
|
113
index.debug.ts
Normal file
113
index.debug.ts
Normal file
@ -0,0 +1,113 @@
|
||||
/*
|
||||
* 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(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: any) {
|
||||
console.log(data)
|
||||
})
|
||||
devClient.on('error', function incoming(error: any) {
|
||||
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: string) {
|
||||
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: WebSocket) {
|
||||
console.log('debugger server started on 2080')
|
||||
})
|
||||
|
||||
global.injectGlobal = (objName: string, obj: string) => {
|
||||
Reflect.set(global, objName, JSON.parse(obj))
|
||||
}
|
||||
|
||||
global.sendToNative = () => {
|
||||
|
||||
}
|
||||
global.receiveFromNative = () => {
|
||||
|
||||
}
|
||||
|
||||
export * from './index'
|
16
index.runtime.ts
Normal file
16
index.runtime.ts
Normal file
@ -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'
|
21
index.ts
Normal file
21
index.ts
Normal file
@ -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 './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"
|
125
package-lock.json
generated
Normal file
125
package-lock.json
generated
Normal file
@ -0,0 +1,125 @@
|
||||
{
|
||||
"name": "doric",
|
||||
"version": "0.1.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"@types/estree": {
|
||||
"version": "0.0.39",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz",
|
||||
"integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw=="
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "12.12.11",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.11.tgz",
|
||||
"integrity": "sha512-O+x6uIpa6oMNTkPuHDa9MhMMehlxLAd5QcOvKRjAFsBVpeFWTOPnXbDvILvFgFFZfQ1xh1EZi1FbXxUix+zpsQ=="
|
||||
},
|
||||
"@types/resolve": {
|
||||
"version": "0.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-0.0.8.tgz",
|
||||
"integrity": "sha512-auApPaJf3NPfe18hSoJkp8EbZzer2ISk7o8mCC3M9he/a04+gbMF97NkpD2S8riMGvm4BMRI59/SZQSaLTKpsQ==",
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/ws": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-6.0.3.tgz",
|
||||
"integrity": "sha512-yBTM0P05Tx9iXGq00BbJPo37ox68R5vaGTXivs6RGh/BQ6QP5zqZDGWdAO6JbRE/iR1l80xeGAwCQS2nMV9S/w==",
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"acorn": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.0.tgz",
|
||||
"integrity": "sha512-kL5CuoXA/dgxlBbVrflsflzQ3PAas7RYZB52NOm/6839iVYJgKMJ3cQJD+t2i5+qFa8h3MDpEOJiS64E8JLnSQ=="
|
||||
},
|
||||
"async-limiter": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
|
||||
"integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ=="
|
||||
},
|
||||
"builtin-modules": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.1.0.tgz",
|
||||
"integrity": "sha512-k0KL0aWZuBt2lrxrcASWDfwOLMnodeQjodT/1SxEQAXsHANgo6ZC/VEaSEHCXt7aSTZ4/4H5LKa+tBXmW7Vtvw=="
|
||||
},
|
||||
"estree-walker": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz",
|
||||
"integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w=="
|
||||
},
|
||||
"is-module": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz",
|
||||
"integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE="
|
||||
},
|
||||
"path-parse": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
|
||||
"integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw=="
|
||||
},
|
||||
"reflect-metadata": {
|
||||
"version": "0.1.13",
|
||||
"resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz",
|
||||
"integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg=="
|
||||
},
|
||||
"resolve": {
|
||||
"version": "1.12.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz",
|
||||
"integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==",
|
||||
"requires": {
|
||||
"path-parse": "^1.0.6"
|
||||
}
|
||||
},
|
||||
"rollup": {
|
||||
"version": "1.27.2",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-1.27.2.tgz",
|
||||
"integrity": "sha512-sD3iyd0zlvgK1S3MmICi6F/Y+R/QWY5XxzsTGN4pAd+nCasDUizmAhgq2hdh1t2eLux974NHU2TW41fhuGPv+Q==",
|
||||
"requires": {
|
||||
"@types/estree": "*",
|
||||
"@types/node": "*",
|
||||
"acorn": "^7.1.0"
|
||||
}
|
||||
},
|
||||
"rollup-plugin-node-resolve": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-5.2.0.tgz",
|
||||
"integrity": "sha512-jUlyaDXts7TW2CqQ4GaO5VJ4PwwaV8VUGA7+km3n6k6xtOEacf61u0VXwN80phY/evMcaS+9eIeJ9MOyDxt5Zw==",
|
||||
"requires": {
|
||||
"@types/resolve": "0.0.8",
|
||||
"builtin-modules": "^3.1.0",
|
||||
"is-module": "^1.0.0",
|
||||
"resolve": "^1.11.1",
|
||||
"rollup-pluginutils": "^2.8.1"
|
||||
}
|
||||
},
|
||||
"rollup-pluginutils": {
|
||||
"version": "2.8.2",
|
||||
"resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz",
|
||||
"integrity": "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==",
|
||||
"requires": {
|
||||
"estree-walker": "^0.6.1"
|
||||
}
|
||||
},
|
||||
"tslib": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
|
||||
"integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ=="
|
||||
},
|
||||
"typescript": {
|
||||
"version": "3.7.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.2.tgz",
|
||||
"integrity": "sha512-ml7V7JfiN2Xwvcer+XAf2csGO1bPBdRbFCkYBczNZggrBZ9c7G3riSUeJmqEU5uOtXNPMhE3n+R4FA/3YOAWOQ=="
|
||||
},
|
||||
"ws": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.2.0.tgz",
|
||||
"integrity": "sha512-+SqNqFbwTm/0DC18KYzIsMTnEWpLwJsiasW/O17la4iDRRIO9uaHbvKiAS3AHgTiuuWerK/brj4O6MYZkei9xg==",
|
||||
"requires": {
|
||||
"async-limiter": "^1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
31
package.json
Normal file
31
package.json
Normal file
@ -0,0 +1,31 @@
|
||||
{
|
||||
"name": "doric",
|
||||
"version": "0.1.0",
|
||||
"description": "The JS Framework of Doric",
|
||||
"main": "bundle/doric-vm.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"build": "tsc -p .&& rollup -c",
|
||||
"dev": "tsc -w -p . & rollup -c -w",
|
||||
"clean": "rm -rf build && rm -rf bundle"
|
||||
},
|
||||
"repository": {
|
||||
"type": "https",
|
||||
"url": "https://github.com/doric-pub/doric"
|
||||
},
|
||||
"author": "pengfeizhou",
|
||||
"license": "Apache-2.0",
|
||||
"bugs": {
|
||||
"url": "https://github.com/doric-pub/doric/issues"
|
||||
},
|
||||
"homepage": "https://github.com/doric-pub/doric#readme",
|
||||
"dependencies": {
|
||||
"@types/ws": "^6.0.3",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"rollup": "^1.27.2",
|
||||
"rollup-plugin-node-resolve": "^5.2.0",
|
||||
"tslib": "^1.10.0",
|
||||
"typescript": "^3.7.2",
|
||||
"ws": "^7.2.0"
|
||||
}
|
||||
}
|
51
rollup.config.js
Normal file
51
rollup.config.js
Normal file
@ -0,0 +1,51 @@
|
||||
const path = require('path')
|
||||
const fs = require('fs')
|
||||
import resolve from 'rollup-plugin-node-resolve'
|
||||
|
||||
export default [
|
||||
{
|
||||
input: "build/index.runtime.js",
|
||||
output: {
|
||||
name: "doric",
|
||||
format: "iife",
|
||||
file: "bundle/doric-sandbox.js",
|
||||
},
|
||||
plugins: [
|
||||
resolve({ mainFields: ["jsnext"] }),
|
||||
],
|
||||
onwarn: function(warning) {
|
||||
if ( warning.code === 'THIS_IS_UNDEFINED' ) { return; }
|
||||
console.warn( warning.message );
|
||||
}
|
||||
},
|
||||
{
|
||||
input: "build/index.js",
|
||||
output: {
|
||||
format: "cjs",
|
||||
file: "bundle/doric-lib.js",
|
||||
},
|
||||
plugins: [
|
||||
resolve({ mainFields: ["jsnext"] }),
|
||||
],
|
||||
external: ['reflect-metadata'],
|
||||
onwarn: function(warning) {
|
||||
if ( warning.code === 'THIS_IS_UNDEFINED' ) { return; }
|
||||
console.warn( warning.message );
|
||||
}
|
||||
},
|
||||
{
|
||||
input: "build/index.debug.js",
|
||||
output: {
|
||||
format: "cjs",
|
||||
file: "bundle/doric-vm.js",
|
||||
},
|
||||
plugins: [
|
||||
resolve({ mainFields: ["jsnext"] }),
|
||||
],
|
||||
external: ['ws'],
|
||||
onwarn: function(warning) {
|
||||
if ( warning.code === 'THIS_IS_UNDEFINED' ) { return; }
|
||||
console.warn( warning.message );
|
||||
}
|
||||
},
|
||||
]
|
43
src/mock/driver.ts
Normal file
43
src/mock/driver.ts
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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 { 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
|
||||
}
|
51
src/native/animate.ts
Normal file
51
src/native/animate.ts
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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(panel: Panel) {
|
||||
return (args: {
|
||||
animations: () => void,
|
||||
duration: number,
|
||||
}) => {
|
||||
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 as any).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')
|
||||
})
|
||||
})
|
||||
}) as Promise<any>
|
||||
}
|
||||
}
|
22
src/native/index.native.ts
Normal file
22
src/native/index.native.ts
Normal file
@ -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'
|
61
src/native/modal.ts
Normal file
61
src/native/modal.ts
Normal file
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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 { BridgeContext } from "../runtime/global"
|
||||
import { Gravity } from "../util/gravity"
|
||||
|
||||
export function modal(context: BridgeContext) {
|
||||
return {
|
||||
toast: (msg: string, gravity: Gravity = Gravity.Bottom) => {
|
||||
context.modal.toast({
|
||||
msg,
|
||||
gravity: gravity.toModel(),
|
||||
})
|
||||
},
|
||||
alert: (arg: string | {
|
||||
title: string,
|
||||
msg: string,
|
||||
okLabel?: string,
|
||||
}) => {
|
||||
if (typeof arg === 'string') {
|
||||
return context.modal.alert({ msg: arg })
|
||||
} else {
|
||||
return context.modal.alert(arg)
|
||||
}
|
||||
},
|
||||
confirm: (arg: string | {
|
||||
title: string,
|
||||
msg: string,
|
||||
okLabel?: string,
|
||||
cancelLabel?: string,
|
||||
}) => {
|
||||
if (typeof arg === 'string') {
|
||||
return context.modal.confirm({ msg: arg })
|
||||
} else {
|
||||
return context.modal.confirm(arg)
|
||||
}
|
||||
},
|
||||
prompt: (arg: {
|
||||
title?: string,
|
||||
msg?: string,
|
||||
okLabel?: string,
|
||||
cancelLabel?: string,
|
||||
text?: string,
|
||||
defaultText?: string,
|
||||
}) => {
|
||||
return context.modal.prompt(arg) as Promise<string>
|
||||
},
|
||||
}
|
||||
}
|
47
src/native/navbar.ts
Normal file
47
src/native/navbar.ts
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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 { BridgeContext } from "../runtime/global"
|
||||
import { Panel } from "../ui/panel"
|
||||
import { Color } from "../util/color"
|
||||
|
||||
export function navbar(context: BridgeContext) {
|
||||
const entity = context.entity
|
||||
let panel: Panel | undefined = undefined
|
||||
if (entity instanceof Panel) {
|
||||
panel = entity
|
||||
}
|
||||
|
||||
return {
|
||||
isHidden: () => {
|
||||
return context.navbar.isHidden() as Promise<boolean>
|
||||
},
|
||||
setHidden: (hidden: boolean) => {
|
||||
return context.navbar.setHidden({
|
||||
hidden,
|
||||
})
|
||||
},
|
||||
setTitle: (title: string) => {
|
||||
return context.navbar.setTitle({
|
||||
title,
|
||||
})
|
||||
},
|
||||
setBgColor: (color: Color) => {
|
||||
return context.navbar.setBgColor({
|
||||
color: color.toModel(),
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
29
src/native/navigator.ts
Normal file
29
src/native/navigator.ts
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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 { BridgeContext } from "../runtime/global"
|
||||
|
||||
export function navigator(context: BridgeContext) {
|
||||
return {
|
||||
push: (scheme: string, alias: string, animated = true) => {
|
||||
return context.navigator.push({
|
||||
scheme, alias, animated
|
||||
})
|
||||
},
|
||||
pop: (animated = true) => {
|
||||
return context.navigator.pop({ animated })
|
||||
},
|
||||
}
|
||||
}
|
109
src/native/network.ts
Normal file
109
src/native/network.ts
Normal file
@ -0,0 +1,109 @@
|
||||
/*
|
||||
* 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 { BridgeContext } from "../runtime/global"
|
||||
|
||||
export interface IRequest {
|
||||
// `url` is the server URL that will be used for the request
|
||||
url?: string,
|
||||
// `method` is the request method to be used when making the request
|
||||
method?: "get" | "post" | "put" | "delete",
|
||||
// `headers` are custom headers to be sent
|
||||
headers?: { [index: string]: string }
|
||||
// `params` are the URL parameters to be sent with the request
|
||||
// Must be a plain object or a URLSearchParams object
|
||||
params?: { [index: string]: string }
|
||||
// `data` is the data to be sent as the request body
|
||||
// Only applicable for request methods 'PUT', 'POST', and 'PATCH'
|
||||
data?: object | string
|
||||
// `timeout` specifies the number of milliseconds before the request times out.
|
||||
// If the request takes longer than `timeout`, the request will be aborted.
|
||||
timeout?: number, // default is `0` (no timeout)
|
||||
}
|
||||
|
||||
export interface IResponse {
|
||||
// `data` is the response that was provided by the server
|
||||
data: any,
|
||||
// `status` is the HTTP status code from the server response
|
||||
status: number,
|
||||
// `headers` the headers that the server responded with
|
||||
// All header names are lower cased
|
||||
headers?: { [index: string]: string },
|
||||
}
|
||||
|
||||
function transformRequest(request: IRequest) {
|
||||
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: BridgeContext) {
|
||||
return {
|
||||
request: (config: IRequest) => {
|
||||
return context.network.request(transformRequest(config)) as Promise<IResponse>
|
||||
},
|
||||
get: (url: string, config?: IRequest) => {
|
||||
let finalConfig = config
|
||||
if (finalConfig === undefined) {
|
||||
finalConfig = {}
|
||||
}
|
||||
finalConfig.url = url
|
||||
finalConfig.method = "get"
|
||||
return context.network.request(transformRequest(finalConfig)) as Promise<IResponse>
|
||||
},
|
||||
post: (url: string, data?: object | string, config?: IRequest) => {
|
||||
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)) as Promise<IResponse>
|
||||
},
|
||||
put: (url: string, data?: object | string, config?: IRequest) => {
|
||||
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)) as Promise<IResponse>
|
||||
},
|
||||
delete: (url: string, data?: object | string, config?: IRequest) => {
|
||||
let finalConfig = config
|
||||
if (finalConfig === undefined) {
|
||||
finalConfig = {}
|
||||
}
|
||||
finalConfig.url = url
|
||||
finalConfig.method = "delete"
|
||||
return context.network.request(transformRequest(finalConfig)) as Promise<IResponse>
|
||||
},
|
||||
}
|
||||
}
|
44
src/native/popover.ts
Normal file
44
src/native/popover.ts
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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 { BridgeContext } from "../runtime/global"
|
||||
import { View } from "../ui/view"
|
||||
import { Panel } from "../ui/panel"
|
||||
|
||||
export function popover(context: BridgeContext) {
|
||||
const entity = context.entity
|
||||
let panel: Panel | undefined = undefined
|
||||
if (entity instanceof Panel) {
|
||||
panel = entity
|
||||
}
|
||||
return {
|
||||
show: (view: View) => {
|
||||
if (panel) {
|
||||
panel.addHeadView(view)
|
||||
}
|
||||
return context.popover.show(view.toModel())
|
||||
},
|
||||
dismiss: (view: View | undefined = undefined) => {
|
||||
if (panel) {
|
||||
if (view) {
|
||||
panel.removeHeadView(view)
|
||||
} else {
|
||||
panel.clearHeadViews()
|
||||
}
|
||||
}
|
||||
return context.popover.dismiss(view ? { id: view.viewId } : undefined)
|
||||
},
|
||||
}
|
||||
}
|
33
src/native/storage.ts
Normal file
33
src/native/storage.ts
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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 { BridgeContext } from "../runtime/global"
|
||||
|
||||
export function storage(context: BridgeContext) {
|
||||
return {
|
||||
setItem: (key: string, value: string, zone?: string) => {
|
||||
return context.storage.setItem({ key, value, zone })
|
||||
},
|
||||
getItem: (key: string, zone?: string) => {
|
||||
return context.storage.getItem({ key, zone }) as Promise<string>
|
||||
},
|
||||
remove: (key: string, zone?: string) => {
|
||||
return context.storage.remove({ key, zone })
|
||||
},
|
||||
clear: (zone: string) => {
|
||||
return context.storage.clear({ zone })
|
||||
},
|
||||
}
|
||||
}
|
69
src/pattern/candies.ts
Normal file
69
src/pattern/candies.ts
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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<T>(target: T) {
|
||||
return (block: (p: T) => void) => {
|
||||
block(target)
|
||||
}
|
||||
}
|
||||
|
||||
export function takeNonNull<T, R>(target?: T) {
|
||||
return (block: (p: T) => R) => {
|
||||
if (target !== undefined) {
|
||||
return block(target)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function takeNull<T, R>(target?: T) {
|
||||
return (block: () => R) => {
|
||||
if (target === undefined) {
|
||||
return block()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function takeLet<T, R>(target: T) {
|
||||
return (block: (p: T) => R | undefined) => {
|
||||
return block(target)
|
||||
}
|
||||
}
|
||||
|
||||
export function takeAlso<T>(target: T) {
|
||||
return (block: (p: T) => void) => {
|
||||
block(target)
|
||||
return target
|
||||
}
|
||||
}
|
||||
|
||||
export function takeIf<T>(target: T) {
|
||||
return (predicate: (t: T) => boolean) => {
|
||||
return predicate(target) ? target : undefined
|
||||
}
|
||||
}
|
||||
|
||||
export function takeUnless<T>(target: T) {
|
||||
return (predicate: (t: T) => boolean) => {
|
||||
return predicate(target) ? undefined : target
|
||||
}
|
||||
}
|
||||
|
||||
export function repeat(action: (count: number) => void) {
|
||||
return (times: number) => {
|
||||
for (let i = 0; i < times; i++) {
|
||||
action(i)
|
||||
}
|
||||
}
|
||||
}
|
18
src/pattern/index.pattern.ts
Normal file
18
src/pattern/index.pattern.ts
Normal file
@ -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'
|
77
src/pattern/mvvm.ts
Normal file
77
src/pattern/mvvm.ts
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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 } from "../ui/view"
|
||||
import { Panel } from "../ui/panel"
|
||||
|
||||
export abstract class ViewHolder<M>{
|
||||
abstract build(root: Group): void
|
||||
abstract bind(state: M): void
|
||||
}
|
||||
|
||||
export type Setter<M> = (state: M) => void
|
||||
|
||||
export abstract class ViewModel<M extends Object, V extends ViewHolder<M>> {
|
||||
private state: M
|
||||
private viewHolder: V
|
||||
|
||||
constructor(obj: M, v: V) {
|
||||
this.state = obj
|
||||
this.viewHolder = v
|
||||
}
|
||||
|
||||
getState() {
|
||||
return this.state
|
||||
}
|
||||
|
||||
updateState(setter: Setter<M>) {
|
||||
setter(this.state)
|
||||
this.viewHolder.bind(this.state)
|
||||
}
|
||||
|
||||
attach(view: Group) {
|
||||
this.viewHolder.build(view)
|
||||
this.viewHolder.bind(this.state)
|
||||
this.onAttached(this.state, this.viewHolder)
|
||||
}
|
||||
|
||||
abstract onAttached(state: M, vh: V): void
|
||||
}
|
||||
export type ViewModelClass<M, V extends ViewHolder<M>> = new (m: M, v: V) => ViewModel<M, V>
|
||||
|
||||
export type ViewHolderClass<V> = new () => V
|
||||
|
||||
export abstract class VMPanel<M extends Object, V extends ViewHolder<M>> extends Panel {
|
||||
|
||||
private vm?: ViewModel<M, V>
|
||||
private vh?: V
|
||||
|
||||
abstract getViewModelClass(): ViewModelClass<M, V>
|
||||
|
||||
abstract getState(): M
|
||||
|
||||
abstract getViewHolderClass(): ViewHolderClass<V>
|
||||
|
||||
getViewModel() {
|
||||
return this.vm
|
||||
}
|
||||
|
||||
build(root: Group): void {
|
||||
this.vh = new (this.getViewHolderClass())
|
||||
this.vm = new (this.getViewModelClass())(this.getState(), this.vh)
|
||||
this.vm.attach(root)
|
||||
}
|
||||
}
|
||||
|
99
src/pattern/provider.ts
Normal file
99
src/pattern/provider.ts
Normal file
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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 type Observer<T> = (v: T) => void
|
||||
export type Updater<T> = (v: T) => T
|
||||
|
||||
export interface IObservable<T> {
|
||||
|
||||
addObserver(observer: Observer<T | undefined>): void
|
||||
|
||||
removeObserver(observer: Observer<T | undefined>): void
|
||||
|
||||
update(updater: Updater<T | undefined>): void
|
||||
}
|
||||
|
||||
export class Observable<M> implements IObservable<M>{
|
||||
private provider: IProvider
|
||||
|
||||
private clz: { new(...args: any[]): M }
|
||||
|
||||
private observers: Set<Observer<M | undefined>> = new Set
|
||||
constructor(provider: IProvider, clz: { new(...args: any[]): M }) {
|
||||
this.provider = provider
|
||||
this.clz = clz
|
||||
}
|
||||
|
||||
addObserver(observer: Observer<M | undefined>): void {
|
||||
this.observers.add(observer)
|
||||
}
|
||||
|
||||
removeObserver(observer: Observer<M | undefined>): void {
|
||||
this.observers.delete(observer)
|
||||
}
|
||||
|
||||
update(updater: Updater<M | undefined>): void {
|
||||
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 interface IProvider {
|
||||
provide(obj: Object): void
|
||||
acquire<T>(clz: { new(...args: any[]): T }): T | undefined
|
||||
remove<T>(clz: { new(...args: any[]): T }): void
|
||||
clear(): void
|
||||
observe<T>(clz: { new(...args: any[]): T }): Observable<T>
|
||||
}
|
||||
|
||||
export class Provider implements IProvider {
|
||||
|
||||
private provision: Map<Function, Object> = new Map
|
||||
private observableMap: Map<Function, Observable<any>> = new Map
|
||||
|
||||
provide(obj: Object) {
|
||||
this.provision.set(obj.constructor, obj)
|
||||
}
|
||||
|
||||
acquire<T>(clz: { new(...args: any[]): T }): T | undefined {
|
||||
const ret = this.provision.get(clz)
|
||||
return ret as T | undefined
|
||||
}
|
||||
|
||||
remove<T>(clz: new (...args: any[]) => T): void {
|
||||
this.provision.delete(clz)
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
this.provision.clear()
|
||||
}
|
||||
|
||||
observe<T>(clz: new (...args: any[]) => T): Observable<T> {
|
||||
let observable = this.observableMap.get(clz)
|
||||
if (observable === undefined) {
|
||||
observable = new Observable(this, clz)
|
||||
this.observableMap.set(clz, observable)
|
||||
}
|
||||
return observable
|
||||
}
|
||||
}
|
||||
|
24
src/runtime/global.ts
Normal file
24
src/runtime/global.ts
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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 'reflect-metadata'
|
||||
|
||||
export type BridgeContext = { [index: string]: { [index: string]: (args?: any) => Promise<any> } }
|
||||
|
||||
declare global {
|
||||
const context: BridgeContext
|
||||
function Entry(constructor: { new(...args: any[]): {} }): any
|
||||
}
|
||||
export { }
|
334
src/runtime/sandbox.ts
Normal file
334
src/runtime/sandbox.ts
Normal file
@ -0,0 +1,334 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
/**
|
||||
* ``` TypeScript
|
||||
* // load script in global scope
|
||||
* Reflect.apply(
|
||||
* function(doric,context,Entry,require){
|
||||
* //Script content
|
||||
* REG()
|
||||
* },doric.jsObtainContext(id),[
|
||||
* undefined,
|
||||
* doric.jsObtainContext(id),
|
||||
* doric.jsObtainEntry(id),
|
||||
* doric.__require__,
|
||||
* ])
|
||||
* // load module in global scope
|
||||
* Reflect.apply(doric.jsRegisterModule,this,[
|
||||
* moduleName,
|
||||
* Reflect.apply(function(__module){
|
||||
* (function(module,exports,require){
|
||||
* //module content
|
||||
* })(__module,__module.exports,doric.__require__);
|
||||
* return __module.exports
|
||||
* },this,[{exports:{}}])
|
||||
* ])
|
||||
*
|
||||
* ```
|
||||
*/
|
||||
|
||||
declare function nativeRequire(moduleName: string): boolean
|
||||
|
||||
declare function nativeBridge(contextId: string, namespace: string, method: string, callbackId?: string, args?: any): boolean
|
||||
|
||||
declare function nativeSetTimer(timerId: number, interval: number, repeat: boolean): void
|
||||
|
||||
declare function nativeClearTimer(timerId: number): void
|
||||
|
||||
function hookBeforeNativeCall(context?: Context) {
|
||||
if (context) {
|
||||
Reflect.defineMetadata('__doric_context__', context, global)
|
||||
context.hookBeforeNativeCall()
|
||||
}
|
||||
}
|
||||
|
||||
function hookAfterNativeCall(context?: Context) {
|
||||
if (context) {
|
||||
context.hookAfterNativeCall()
|
||||
}
|
||||
}
|
||||
|
||||
function getContext(): Context | undefined {
|
||||
return Reflect.getMetadata('__doric_context__', global)
|
||||
}
|
||||
|
||||
function setContext(context?: Context) {
|
||||
Reflect.defineMetadata('__doric_context__', context, global)
|
||||
}
|
||||
|
||||
export function jsCallResolve(contextId: string, callbackId: string, args?: any) {
|
||||
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: any = []
|
||||
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: string, callbackId: string, args?: any) {
|
||||
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: any = []
|
||||
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 {
|
||||
entity: any
|
||||
id: string
|
||||
callbacks: Map<string, { resolve: Function, reject: Function }> = new Map
|
||||
|
||||
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, [])
|
||||
}
|
||||
}
|
||||
|
||||
constructor(id: string) {
|
||||
this.id = id
|
||||
return new Proxy(this, {
|
||||
get: (target, p: any) => {
|
||||
if (Reflect.has(target, p)) {
|
||||
return Reflect.get(target, p)
|
||||
} else {
|
||||
const namespace = p
|
||||
return new Proxy({}, {
|
||||
get: (target, p: any) => {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
callNative(namespace: string, method: string, args?: any): Promise<any> {
|
||||
const callbackId = uniqueId('callback')
|
||||
nativeBridge(this.id, namespace, method, callbackId, args)
|
||||
return new Promise((resolve, reject) => {
|
||||
this.callbacks.set(callbackId, {
|
||||
resolve,
|
||||
reject,
|
||||
})
|
||||
})
|
||||
}
|
||||
register(instance: Object) {
|
||||
this.entity = instance
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const gContexts: Map<string, Context> = new Map
|
||||
const gModules: Map<string, any> = new Map
|
||||
|
||||
export function jsObtainContext(id: string) {
|
||||
if (gContexts.has(id)) {
|
||||
const context = gContexts.get(id)
|
||||
setContext(context)
|
||||
return context
|
||||
} else {
|
||||
const context: Context = new Context(id)
|
||||
gContexts.set(id, context)
|
||||
setContext(context)
|
||||
return context
|
||||
}
|
||||
}
|
||||
|
||||
export function jsReleaseContext(id: string) {
|
||||
const context = gContexts.get(id)
|
||||
if (context) {
|
||||
timerInfos.forEach((v, k) => {
|
||||
if (v.context === context) {
|
||||
if (global.nativeClearTimer === undefined) {
|
||||
return Reflect.apply(_clearTimeout, undefined, arguments)
|
||||
}
|
||||
timerInfos.delete(k)
|
||||
nativeClearTimer(k)
|
||||
}
|
||||
})
|
||||
}
|
||||
gContexts.delete(id)
|
||||
}
|
||||
|
||||
export function __require__(name: string): any {
|
||||
if (gModules.has(name)) {
|
||||
return gModules.get(name)
|
||||
} else {
|
||||
if (nativeRequire(name)) {
|
||||
return gModules.get(name)
|
||||
} else {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function jsRegisterModule(name: string, moduleObject: any) {
|
||||
gModules.set(name, moduleObject)
|
||||
}
|
||||
|
||||
export function jsCallEntityMethod(contextId: string, methodName: string, args?: any) {
|
||||
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: any = []
|
||||
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: string) {
|
||||
const context = jsObtainContext(contextId)
|
||||
return <T extends { new(...args: any[]): {} }>(constructor: T) => {
|
||||
const ret = class extends constructor {
|
||||
context = context
|
||||
}
|
||||
if (context) {
|
||||
context.register(new ret)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const global = Function('return this')()
|
||||
let __timerId__ = 0
|
||||
|
||||
const timerInfos: Map<number, { callback: () => void, context?: Context }> = new Map
|
||||
|
||||
const _setTimeout = global.setTimeout
|
||||
|
||||
const _setInterval = global.setInterval
|
||||
|
||||
const _clearTimeout = global.clearTimeout
|
||||
|
||||
const _clearInterval = global.clearInterval
|
||||
|
||||
global.setTimeout = function (handler: Function, timeout?: number | undefined, ...args: any[]) {
|
||||
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
|
||||
}
|
||||
global.setInterval = function (handler: Function, timeout?: number | undefined, ...args: any[]) {
|
||||
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
|
||||
}
|
||||
|
||||
global.clearTimeout = function (timerId: number) {
|
||||
if (global.nativeClearTimer === undefined) {
|
||||
return Reflect.apply(_clearTimeout, undefined, arguments)
|
||||
}
|
||||
timerInfos.delete(timerId)
|
||||
nativeClearTimer(timerId)
|
||||
}
|
||||
|
||||
global.clearInterval = function (timerId: number) {
|
||||
if (global.nativeClearTimer === undefined) {
|
||||
return Reflect.apply(_clearInterval, undefined, arguments)
|
||||
}
|
||||
timerInfos.delete(timerId)
|
||||
nativeClearTimer(timerId)
|
||||
}
|
||||
|
||||
export function jsCallbackTimer(timerId: number) {
|
||||
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)
|
||||
}
|
||||
}
|
263
src/ui/animation.ts
Normal file
263
src/ui/animation.ts
Normal file
@ -0,0 +1,263 @@
|
||||
import { Modeling, Model } from "../util/types"
|
||||
|
||||
/*
|
||||
* 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 type AnimatedKey = "translationX" | "translationY" | "scaleX" | "scaleY" | "rotation" | "pivotX" | "pivotY"
|
||||
|
||||
export 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 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 = 0x1,
|
||||
/**
|
||||
* The receiver clamps values before zero to zero when the animation is completed.
|
||||
*/
|
||||
Backward = 0x2,
|
||||
/**
|
||||
* The receiver clamps values at both ends of the object’s time space
|
||||
*/
|
||||
Both = 0x3,
|
||||
}
|
||||
|
||||
export 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,
|
||||
/**
|
||||
* Ease-in pacing, which causes an animation to begin slowly and then speed up as it progresses.
|
||||
*/
|
||||
EaseIn,
|
||||
/**
|
||||
* Ease-out pacing, which causes an animation to begin quickly and then slow as it progresses.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
EaseInEaseOut,
|
||||
}
|
||||
|
||||
abstract class Animation implements IAnimation {
|
||||
changeables: Map<AnimatedKey, Changeable> = new Map
|
||||
duration = 0
|
||||
repeatCount?: number
|
||||
repeatMode?: RepeatMode
|
||||
delay?: number
|
||||
fillMode = FillMode.Forward
|
||||
timingFunction?: TimingFunction
|
||||
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 {
|
||||
private scaleXChangeable: Changeable = {
|
||||
key: "scaleX",
|
||||
fromValue: 1,
|
||||
toValue: 1,
|
||||
}
|
||||
private scaleYChangeable: Changeable = {
|
||||
key: "scaleY",
|
||||
fromValue: 1,
|
||||
toValue: 1,
|
||||
}
|
||||
constructor() {
|
||||
super()
|
||||
this.changeables.set("scaleX", this.scaleXChangeable)
|
||||
this.changeables.set("scaleY", this.scaleYChangeable)
|
||||
}
|
||||
|
||||
|
||||
set fromScaleX(v: number) {
|
||||
this.scaleXChangeable.fromValue = v
|
||||
}
|
||||
|
||||
get fromScaleX() {
|
||||
return this.scaleXChangeable.fromValue
|
||||
}
|
||||
|
||||
set toScaleX(v: number) {
|
||||
this.scaleXChangeable.toValue = v
|
||||
}
|
||||
|
||||
get toScaleX() {
|
||||
return this.scaleXChangeable.toValue
|
||||
}
|
||||
set fromScaleY(v: number) {
|
||||
this.scaleYChangeable.fromValue = v
|
||||
}
|
||||
|
||||
get fromScaleY() {
|
||||
return this.scaleYChangeable.fromValue
|
||||
}
|
||||
|
||||
set toScaleY(v: number) {
|
||||
this.scaleYChangeable.toValue = v
|
||||
}
|
||||
|
||||
get toScaleY() {
|
||||
return this.scaleYChangeable.toValue
|
||||
}
|
||||
}
|
||||
|
||||
export class TranslationAnimation extends Animation {
|
||||
private translationXChangeable: Changeable = {
|
||||
key: "translationX",
|
||||
fromValue: 1,
|
||||
toValue: 1,
|
||||
}
|
||||
private translationYChangeable: Changeable = {
|
||||
key: "translationY",
|
||||
fromValue: 1,
|
||||
toValue: 1,
|
||||
}
|
||||
constructor() {
|
||||
super()
|
||||
this.changeables.set("translationX", this.translationXChangeable)
|
||||
this.changeables.set("translationY", this.translationYChangeable)
|
||||
}
|
||||
|
||||
set fromTranslationX(v: number) {
|
||||
this.translationXChangeable.fromValue = v
|
||||
}
|
||||
|
||||
get fromTranslationX() {
|
||||
return this.translationXChangeable.fromValue
|
||||
}
|
||||
|
||||
set toTranslationX(v: number) {
|
||||
this.translationXChangeable.toValue = v
|
||||
}
|
||||
|
||||
get toTranslationX() {
|
||||
return this.translationXChangeable.toValue
|
||||
}
|
||||
set fromTranslationY(v: number) {
|
||||
this.translationYChangeable.fromValue = v
|
||||
}
|
||||
|
||||
get fromTranslationY() {
|
||||
return this.translationYChangeable.fromValue
|
||||
}
|
||||
|
||||
set toTranslationY(v: number) {
|
||||
this.translationYChangeable.toValue = v
|
||||
}
|
||||
|
||||
get toTranslationY() {
|
||||
return this.translationYChangeable.toValue
|
||||
}
|
||||
}
|
||||
|
||||
export class RotationAnimation extends Animation {
|
||||
private rotationChaneable: Changeable = {
|
||||
key: "rotation",
|
||||
fromValue: 1,
|
||||
toValue: 1,
|
||||
}
|
||||
constructor() {
|
||||
super()
|
||||
this.changeables.set("rotation", this.rotationChaneable)
|
||||
}
|
||||
|
||||
set fromRotation(v: number) {
|
||||
this.rotationChaneable.fromValue = v
|
||||
}
|
||||
|
||||
get fromRotation() {
|
||||
return this.rotationChaneable.fromValue
|
||||
}
|
||||
|
||||
set toRotation(v: number) {
|
||||
this.rotationChaneable.toValue = v
|
||||
}
|
||||
|
||||
get toRotation() {
|
||||
return this.rotationChaneable.toValue
|
||||
}
|
||||
}
|
||||
|
||||
export class AnimationSet implements IAnimation {
|
||||
private animations: IAnimation[] = []
|
||||
_duration = 0
|
||||
delay?: number
|
||||
addAnimation(anim: IAnimation) {
|
||||
this.animations.push(anim)
|
||||
}
|
||||
|
||||
get duration() {
|
||||
return this._duration
|
||||
}
|
||||
|
||||
set duration(v: number) {
|
||||
this._duration = v
|
||||
this.animations.forEach(e => e.duration = v)
|
||||
}
|
||||
|
||||
toModel() {
|
||||
return {
|
||||
animations: this.animations.map(e => {
|
||||
return e.toModel()
|
||||
}) as Model,
|
||||
delay: this.delay,
|
||||
}
|
||||
}
|
||||
}
|
18
src/ui/index.ui.ts
Normal file
18
src/ui/index.ui.ts
Normal file
@ -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'
|
167
src/ui/panel.ts
Normal file
167
src/ui/panel.ts
Normal file
@ -0,0 +1,167 @@
|
||||
/*
|
||||
* 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 '../runtime/global'
|
||||
import { View, Group } from "./view"
|
||||
import { loge } from '../util/log'
|
||||
import { Model } from '../util/types'
|
||||
import { Root } from '../widget/layouts'
|
||||
|
||||
|
||||
export function NativeCall(target: Panel, propertyKey: string, descriptor: PropertyDescriptor) {
|
||||
const originVal = descriptor.value
|
||||
descriptor.value = function () {
|
||||
const ret = Reflect.apply(originVal, this, arguments)
|
||||
return ret
|
||||
}
|
||||
return descriptor
|
||||
}
|
||||
|
||||
type Frame = { width: number, height: number }
|
||||
|
||||
declare function nativeEmpty(): void
|
||||
|
||||
export abstract class Panel {
|
||||
context?: any
|
||||
onCreate() { }
|
||||
onDestroy() { }
|
||||
onShow() { }
|
||||
onHidden() { }
|
||||
|
||||
abstract build(rootView: Group): void
|
||||
|
||||
private __data__: any
|
||||
private __root__ = new Root
|
||||
private headviews: Map<string, View> = new Map
|
||||
|
||||
addHeadView(v: View) {
|
||||
this.headviews.set(v.viewId, v)
|
||||
}
|
||||
allHeadViews() {
|
||||
return this.headviews.values()
|
||||
}
|
||||
removeHeadView(v: View | string) {
|
||||
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__
|
||||
}
|
||||
|
||||
@NativeCall
|
||||
private __init__(frame: Frame, data: any) {
|
||||
this.__data__ = data
|
||||
this.__root__.width = frame.width
|
||||
this.__root__.height = frame.height
|
||||
this.__root__.children.length = 0
|
||||
this.build(this.__root__)
|
||||
}
|
||||
|
||||
@NativeCall
|
||||
private __onCreate__() {
|
||||
this.onCreate()
|
||||
}
|
||||
|
||||
@NativeCall
|
||||
private __onDestroy__() {
|
||||
this.onDestroy()
|
||||
}
|
||||
|
||||
@NativeCall
|
||||
private __onShow__() {
|
||||
this.onShow()
|
||||
}
|
||||
|
||||
@NativeCall
|
||||
private __onHidden__(): void {
|
||||
this.onHidden()
|
||||
}
|
||||
|
||||
@NativeCall
|
||||
private __build__() {
|
||||
this.build(this.__root__)
|
||||
}
|
||||
|
||||
@NativeCall
|
||||
private __response__(viewIds: string[], callbackId: string) {
|
||||
const v = this.retrospectView(viewIds)
|
||||
if (v === undefined) {
|
||||
loge(`Cannot find view for ${viewIds}`)
|
||||
} else {
|
||||
const argumentsList: any = [callbackId]
|
||||
for (let i = 2; i < arguments.length; i++) {
|
||||
argumentsList.push(arguments[i])
|
||||
}
|
||||
return Reflect.apply(v.responseCallback, v, argumentsList)
|
||||
}
|
||||
}
|
||||
|
||||
private retrospectView(ids: string[]): View | undefined {
|
||||
return ids.reduce((acc: View | undefined, 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)
|
||||
}
|
||||
|
||||
private nativeRender(model: Model) {
|
||||
if (this.context) {
|
||||
this.context.shader.render(model)
|
||||
}
|
||||
}
|
||||
|
||||
private hookBeforeNativeCall() {
|
||||
this.__root__.clean()
|
||||
for (let v of this.headviews.values()) {
|
||||
v.clean()
|
||||
}
|
||||
}
|
||||
|
||||
private hookAfterNativeCall() {
|
||||
//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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
395
src/ui/view.ts
Normal file
395
src/ui/view.ts
Normal file
@ -0,0 +1,395 @@
|
||||
/*
|
||||
* 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 { Color, GradientColor } from "../util/color"
|
||||
import { Modeling, Model, obj2Model } from "../util/types";
|
||||
import { uniqueId } from "../util/uniqueId";
|
||||
import { loge } from "../util/log";
|
||||
import { BridgeContext } from "../runtime/global";
|
||||
import { LayoutConfig } from '../util/layoutconfig'
|
||||
import { IAnimation } from "./animation";
|
||||
|
||||
export function Property(target: Object, propKey: string) {
|
||||
Reflect.defineMetadata(propKey, true, target)
|
||||
}
|
||||
|
||||
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
|
||||
/**----------transform----------*/
|
||||
}
|
||||
|
||||
|
||||
export abstract class View implements Modeling, IView {
|
||||
@Property
|
||||
width: number = 0
|
||||
|
||||
@Property
|
||||
height: number = 0
|
||||
|
||||
@Property
|
||||
x: number = 0
|
||||
|
||||
@Property
|
||||
y: number = 0
|
||||
|
||||
@Property
|
||||
backgroundColor?: Color | GradientColor
|
||||
|
||||
@Property
|
||||
corners?: number | { leftTop?: number; rightTop?: number; leftBottom?: number; rightBottom?: number }
|
||||
|
||||
@Property
|
||||
border?: { width: number; color: Color; }
|
||||
|
||||
@Property
|
||||
shadow?: { color: Color; opacity: number; radius: number; offsetX: number; offsetY: number }
|
||||
|
||||
@Property
|
||||
alpha?: number
|
||||
|
||||
@Property
|
||||
hidden?: boolean
|
||||
|
||||
@Property
|
||||
viewId = uniqueId('ViewId')
|
||||
|
||||
@Property
|
||||
padding?: {
|
||||
left?: number,
|
||||
right?: number,
|
||||
top?: number,
|
||||
bottom?: number,
|
||||
}
|
||||
|
||||
@Property
|
||||
layoutConfig?: LayoutConfig
|
||||
|
||||
@Property
|
||||
onClick?: Function
|
||||
|
||||
superview?: Superview
|
||||
|
||||
callbacks: Map<String, Function> = new Map
|
||||
|
||||
private callback2Id(f: Function) {
|
||||
const id = uniqueId('Function')
|
||||
this.callbacks.set(id, f)
|
||||
return id
|
||||
}
|
||||
|
||||
private id2Callback(id: string) {
|
||||
let f = this.callbacks.get(id)
|
||||
if (f === undefined) {
|
||||
f = Reflect.get(this, id) as Function
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
constructor() {
|
||||
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
|
||||
}
|
||||
})
|
||||
}
|
||||
/** Anchor start*/
|
||||
get left() {
|
||||
return this.x
|
||||
}
|
||||
set left(v: number) {
|
||||
this.x = v
|
||||
}
|
||||
|
||||
get right() {
|
||||
return this.x + this.width
|
||||
}
|
||||
set right(v: number) {
|
||||
this.x = v - this.width
|
||||
}
|
||||
|
||||
get top() {
|
||||
return this.y
|
||||
}
|
||||
|
||||
set top(v: number) {
|
||||
this.y = v
|
||||
}
|
||||
|
||||
get bottom() {
|
||||
return this.y + this.height
|
||||
}
|
||||
|
||||
set bottom(v: number) {
|
||||
this.y = v - this.height
|
||||
}
|
||||
|
||||
get centerX() {
|
||||
return this.x + this.width / 2
|
||||
}
|
||||
|
||||
get centerY() {
|
||||
return this.y + this.height / 2
|
||||
}
|
||||
|
||||
set centerX(v: number) {
|
||||
this.x = v - this.width / 2
|
||||
}
|
||||
|
||||
set centerY(v: number) {
|
||||
this.y = v - this.height / 2
|
||||
}
|
||||
/** Anchor end*/
|
||||
|
||||
private __dirty_props__: { [index: string]: Model | undefined } = {}
|
||||
|
||||
get dirtyProps() {
|
||||
return this.__dirty_props__
|
||||
}
|
||||
|
||||
nativeViewModel = {
|
||||
id: this.viewId,
|
||||
type: this.constructor.name,
|
||||
props: this.__dirty_props__,
|
||||
}
|
||||
|
||||
onPropertyChanged(propKey: string, oldV: Model, newV: Model): void {
|
||||
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: string, ...args: any) {
|
||||
const f = this.id2Callback(id)
|
||||
if (f instanceof Function) {
|
||||
const argumentsList: any = []
|
||||
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: (it: this) => void) {
|
||||
block(this)
|
||||
}
|
||||
|
||||
also(block: (it: this) => void) {
|
||||
block(this)
|
||||
return this
|
||||
}
|
||||
|
||||
apply(config: IView) {
|
||||
for (let key in config) {
|
||||
Reflect.set(this, key, Reflect.get(config, key, config), this)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
in(group: Group) {
|
||||
group.addChild(this)
|
||||
return this
|
||||
}
|
||||
|
||||
nativeChannel(context: any, name: string) {
|
||||
let thisView: View | undefined = this
|
||||
return function (args: any = 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]) as Promise<any>
|
||||
}
|
||||
}
|
||||
|
||||
getWidth(context: BridgeContext) {
|
||||
return this.nativeChannel(context, 'getWidth')() as Promise<number>
|
||||
}
|
||||
|
||||
getHeight(context: BridgeContext) {
|
||||
return this.nativeChannel(context, 'getHeight')() as Promise<number>
|
||||
}
|
||||
|
||||
/**++++++++++transform++++++++++*/
|
||||
@Property
|
||||
translationX?: number
|
||||
|
||||
@Property
|
||||
translationY?: number
|
||||
|
||||
@Property
|
||||
scaleX?: number
|
||||
|
||||
@Property
|
||||
scaleY?: number
|
||||
|
||||
@Property
|
||||
pivotX?: number
|
||||
|
||||
@Property
|
||||
pivotY?: number
|
||||
|
||||
@Property
|
||||
rotation?: number
|
||||
/**----------transform----------*/
|
||||
|
||||
doAnimation(context: BridgeContext, animation: IAnimation) {
|
||||
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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export abstract class Superview extends View {
|
||||
subviewById(id: string): View | undefined {
|
||||
for (let v of this.allSubviews()) {
|
||||
if (v.viewId === id) {
|
||||
return v
|
||||
}
|
||||
}
|
||||
}
|
||||
abstract allSubviews(): Iterable<View>
|
||||
|
||||
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()) {
|
||||
v.superview = this
|
||||
if (v.isDirty()) {
|
||||
subviews.push(v.toModel())
|
||||
}
|
||||
}
|
||||
this.dirtyProps.subviews = subviews
|
||||
return super.toModel()
|
||||
}
|
||||
}
|
||||
|
||||
export abstract class Group extends Superview {
|
||||
|
||||
readonly children: View[] = 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: View) {
|
||||
this.children.push(view)
|
||||
}
|
||||
}
|
||||
|
97
src/util/color.ts
Normal file
97
src/util/color.ts
Normal file
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* 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 { Modeling } from "./types";
|
||||
|
||||
/**
|
||||
* Store color as format AARRGGBB or RRGGBB
|
||||
*/
|
||||
export class Color implements Modeling {
|
||||
static BLACK = new Color(0xFF000000)
|
||||
static DKGRAY = new Color(0xFF444444)
|
||||
static GRAY = new Color(0xFF888888)
|
||||
static LTGRAY = new Color(0xFFCCCCCC)
|
||||
static WHITE = new Color(0xFFFFFFFF)
|
||||
static RED = new Color(0xFFFF0000)
|
||||
static GREEN = new Color(0xFF00FF00)
|
||||
static BLUE = new Color(0xFF0000FF)
|
||||
static YELLOW = new Color(0xFFFFFF00)
|
||||
static CYAN = new Color(0xFF00FFFF)
|
||||
static MAGENTA = new Color(0xFFFF00FF)
|
||||
static TRANSPARENT = new Color(0)
|
||||
|
||||
_value: number = 0
|
||||
|
||||
constructor(v: number) {
|
||||
this._value = v | 0x0
|
||||
}
|
||||
|
||||
static parse(str: string) {
|
||||
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: string, defVal: Color = Color.TRANSPARENT) {
|
||||
let color = defVal
|
||||
try {
|
||||
color = Color.parse(str)
|
||||
} catch (e) {
|
||||
} finally {
|
||||
return color
|
||||
}
|
||||
}
|
||||
|
||||
alpha(v: number) {
|
||||
return new Color((this._value & 0xffffff) | ((v & 0xff) << 24))
|
||||
}
|
||||
|
||||
toModel() {
|
||||
return this._value
|
||||
}
|
||||
}
|
||||
export 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,
|
||||
/** draw the gradient from the right to the left */
|
||||
RIGHT_LEFT,
|
||||
/** draw the gradient from the bottom-right to the top-left */
|
||||
BR_TL,
|
||||
/** draw the gradient from the bottom to the top */
|
||||
BOTTOM_TOP,
|
||||
/** draw the gradient from the bottom-left to the top-right */
|
||||
BL_TR,
|
||||
/** draw the gradient from the left to the right */
|
||||
LEFT_RIGHT,
|
||||
/** draw the gradient from the top-left to the bottom-right */
|
||||
TL_BR,
|
||||
}
|
||||
|
||||
export interface GradientColor {
|
||||
start: Color
|
||||
end: Color
|
||||
orientation: GradientOrientation
|
||||
}
|
||||
|
102
src/util/gravity.ts
Normal file
102
src/util/gravity.ts
Normal file
@ -0,0 +1,102 @@
|
||||
/*
|
||||
* 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 { Modeling } from "./types";
|
||||
|
||||
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 implements Modeling {
|
||||
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
|
||||
}
|
||||
private static origin = new Gravity
|
||||
|
||||
static Center = Gravity.origin.center()
|
||||
static Left = Gravity.origin.left()
|
||||
static Right = Gravity.origin.right()
|
||||
static Top = Gravity.origin.top()
|
||||
static Bottom = Gravity.origin.bottom()
|
||||
}
|
||||
export function gravity() {
|
||||
return new Gravity
|
||||
}
|
21
src/util/index.util.ts
Normal file
21
src/util/index.util.ts
Normal file
@ -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'
|
113
src/util/layoutconfig.ts
Normal file
113
src/util/layoutconfig.ts
Normal file
@ -0,0 +1,113 @@
|
||||
/*
|
||||
* 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 { Gravity } from "./gravity";
|
||||
import { Modeling } from "./types";
|
||||
|
||||
export enum LayoutSpec {
|
||||
EXACTLY = 0,
|
||||
WRAP_CONTENT = 1,
|
||||
AT_MOST = 2,
|
||||
}
|
||||
|
||||
export interface LayoutConfig {
|
||||
widthSpec?: LayoutSpec
|
||||
heightSpec?: LayoutSpec
|
||||
margin?: {
|
||||
left?: number,
|
||||
right?: number,
|
||||
top?: number,
|
||||
bottom?: number,
|
||||
}
|
||||
alignment?: Gravity
|
||||
//Only affective in VLayout or HLayout
|
||||
weight?: number
|
||||
}
|
||||
|
||||
export class LayoutConfigImpl implements LayoutConfig, Modeling {
|
||||
widthSpec?: LayoutSpec
|
||||
heightSpec?: LayoutSpec
|
||||
margin?: {
|
||||
left?: number,
|
||||
right?: number,
|
||||
top?: number,
|
||||
bottom?: number,
|
||||
}
|
||||
alignment?: Gravity
|
||||
//Only affective in VLayout or HLayout
|
||||
weight?: number
|
||||
|
||||
wrap() {
|
||||
this.widthSpec = LayoutSpec.WRAP_CONTENT
|
||||
this.heightSpec = LayoutSpec.WRAP_CONTENT
|
||||
return this
|
||||
}
|
||||
|
||||
atmost() {
|
||||
this.widthSpec = LayoutSpec.AT_MOST
|
||||
this.heightSpec = LayoutSpec.AT_MOST
|
||||
return this
|
||||
}
|
||||
|
||||
exactly() {
|
||||
this.widthSpec = LayoutSpec.EXACTLY
|
||||
this.heightSpec = LayoutSpec.EXACTLY
|
||||
return this
|
||||
}
|
||||
|
||||
w(w: LayoutSpec) {
|
||||
this.widthSpec = w
|
||||
return this
|
||||
}
|
||||
|
||||
h(h: LayoutSpec) {
|
||||
this.heightSpec = h
|
||||
return this
|
||||
}
|
||||
|
||||
m(m: {
|
||||
left?: number,
|
||||
right?: number,
|
||||
top?: number,
|
||||
bottom?: number,
|
||||
}) {
|
||||
this.margin = m
|
||||
return this
|
||||
}
|
||||
|
||||
a(a: Gravity) {
|
||||
this.alignment = a
|
||||
return this
|
||||
}
|
||||
|
||||
wg(w: number) {
|
||||
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
|
||||
}
|
65
src/util/log.ts
Normal file
65
src/util/log.ts
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
declare function nativeLog(type: 'd' | 'w' | 'e', message: string): void
|
||||
|
||||
function toString(message: any) {
|
||||
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: any) {
|
||||
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: any) {
|
||||
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: any) {
|
||||
let out = ""
|
||||
for (let i = 0; i < arguments.length; i++) {
|
||||
if (i > 0) {
|
||||
out += ','
|
||||
}
|
||||
out += toString(arguments[i])
|
||||
}
|
||||
nativeLog('w', out)
|
||||
}
|
71
src/util/types.ts
Normal file
71
src/util/types.ts
Normal file
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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 interface Modeling {
|
||||
toModel(): Model
|
||||
}
|
||||
export function obj2Model(obj: Model): Model {
|
||||
if (obj instanceof Array) {
|
||||
return obj.map(e => obj2Model(e)) as Model
|
||||
} 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
|
||||
}
|
||||
}
|
||||
|
||||
type _M = string | number | boolean | Modeling | { [index: string]: Model } | undefined
|
||||
export type Model = _M | Array<_M>
|
||||
|
||||
export type Binder<T> = (v: T) => void
|
||||
|
||||
export class Mutable<T>{
|
||||
private val: T
|
||||
|
||||
private binders: Set<Binder<T>> = new Set
|
||||
|
||||
get = () => {
|
||||
return this.val
|
||||
}
|
||||
|
||||
set = (v: T) => {
|
||||
this.val = v
|
||||
this.binders.forEach(e => {
|
||||
Reflect.apply(e, undefined, [this.val])
|
||||
})
|
||||
}
|
||||
|
||||
private constructor(v: T) {
|
||||
this.val = v
|
||||
}
|
||||
|
||||
bind(binder: Binder<T>) {
|
||||
this.binders.add(binder)
|
||||
Reflect.apply(binder, undefined, [this.val])
|
||||
}
|
||||
|
||||
static of<E>(v: E) {
|
||||
return new Mutable(v)
|
||||
}
|
||||
}
|
19
src/util/uniqueId.ts
Normal file
19
src/util/uniqueId.ts
Normal file
@ -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: string) {
|
||||
return `__${prefix}_${__uniqueId__++}__`;
|
||||
}
|
112
src/widget/flowlayout.ts
Normal file
112
src/widget/flowlayout.ts
Normal file
@ -0,0 +1,112 @@
|
||||
/*
|
||||
* 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, IView, Superview, View } from '../ui/view'
|
||||
import { layoutConfig } from '../util/index.util'
|
||||
|
||||
export class FlowLayoutItem extends Stack {
|
||||
/**
|
||||
* Set to reuse native view
|
||||
*/
|
||||
@Property
|
||||
identifier?: string
|
||||
}
|
||||
export interface IFlowLayout extends IView {
|
||||
renderItem: (index: number) => FlowLayoutItem
|
||||
|
||||
itemCount: number
|
||||
|
||||
batchCount?: number
|
||||
|
||||
columnCount?: number
|
||||
|
||||
columnSpace?: number
|
||||
|
||||
rowSpace?: number
|
||||
}
|
||||
|
||||
export class FlowLayout extends Superview implements IFlowLayout {
|
||||
private cachedViews: Map<string, FlowLayoutItem> = new Map
|
||||
private ignoreDirtyCallOnce = false
|
||||
|
||||
allSubviews() {
|
||||
return this.cachedViews.values()
|
||||
}
|
||||
|
||||
@Property
|
||||
columnCount = 2
|
||||
|
||||
@Property
|
||||
columnSpace?: number
|
||||
|
||||
@Property
|
||||
rowSpace?: number
|
||||
|
||||
@Property
|
||||
itemCount = 0
|
||||
|
||||
@Property
|
||||
renderItem!: (index: number) => FlowLayoutItem
|
||||
|
||||
@Property
|
||||
batchCount = 15
|
||||
|
||||
reset() {
|
||||
this.cachedViews.clear()
|
||||
this.itemCount = 0
|
||||
}
|
||||
private getItem(itemIdx: number) {
|
||||
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()
|
||||
}
|
||||
|
||||
private renderBunchedItems(start: number, length: number) {
|
||||
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()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export function flowlayout(config: IFlowLayout) {
|
||||
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: View) {
|
||||
return (new FlowLayoutItem).also((it) => {
|
||||
it.layoutConfig = layoutConfig().wrap()
|
||||
it.addChild(item)
|
||||
})
|
||||
}
|
51
src/widget/image.ts
Normal file
51
src/widget/image.ts
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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 { IView, View, Property } from "../ui/view"
|
||||
import { layoutConfig } from "../util/layoutconfig"
|
||||
|
||||
export enum ScaleType {
|
||||
ScaleToFill = 0,
|
||||
ScaleAspectFit,
|
||||
ScaleAspectFill,
|
||||
}
|
||||
|
||||
export interface IImage extends IView {
|
||||
imageUrl?: string
|
||||
imageBase64?: string
|
||||
scaleType?: ScaleType
|
||||
loadCallback?: (image: { width: number; height: number } | undefined) => void
|
||||
}
|
||||
|
||||
export class Image extends View implements IImage {
|
||||
@Property
|
||||
imageUrl?: string
|
||||
@Property
|
||||
imageBase64?: string
|
||||
@Property
|
||||
scaleType?: ScaleType
|
||||
|
||||
@Property
|
||||
loadCallback?: (image: { width: number; height: number } | undefined) => void
|
||||
}
|
||||
|
||||
export function image(config: IImage) {
|
||||
const ret = new Image
|
||||
ret.layoutConfig = layoutConfig().wrap()
|
||||
for (let key in config) {
|
||||
Reflect.set(ret, key, Reflect.get(config, key, config), ret)
|
||||
}
|
||||
return ret
|
||||
}
|
23
src/widget/index.widget.ts
Normal file
23
src/widget/index.widget.ts
Normal file
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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'
|
79
src/widget/layouts.ts
Normal file
79
src/widget/layouts.ts
Normal file
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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, IView, View } from "../ui/view";
|
||||
import { Gravity } from "../util/gravity";
|
||||
import { layoutConfig } from "../util/layoutconfig";
|
||||
|
||||
export interface IStack extends IView {
|
||||
}
|
||||
|
||||
export class Stack extends Group implements IStack {
|
||||
}
|
||||
|
||||
export class Root extends Stack {
|
||||
|
||||
}
|
||||
class LinearLayout extends Group {
|
||||
@Property
|
||||
space?: number
|
||||
|
||||
@Property
|
||||
gravity?: Gravity
|
||||
}
|
||||
|
||||
export interface IVLayout extends IView {
|
||||
space?: number
|
||||
gravity?: Gravity
|
||||
}
|
||||
|
||||
export class VLayout extends LinearLayout implements VLayout {
|
||||
}
|
||||
|
||||
|
||||
export interface IHLayout extends IView {
|
||||
space?: number
|
||||
gravity?: Gravity
|
||||
}
|
||||
|
||||
export class HLayout extends LinearLayout implements IHLayout {
|
||||
}
|
||||
|
||||
export function stack(views: View[]) {
|
||||
const ret = new Stack
|
||||
ret.layoutConfig = layoutConfig().wrap()
|
||||
for (let v of views) {
|
||||
ret.addChild(v)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
export function hlayout(views: View[]) {
|
||||
const ret = new HLayout
|
||||
ret.layoutConfig = layoutConfig().wrap()
|
||||
for (let v of views) {
|
||||
ret.addChild(v)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
export function vlayout(views: View[]) {
|
||||
const ret = new VLayout
|
||||
ret.layoutConfig = layoutConfig().wrap()
|
||||
for (let v of views) {
|
||||
ret.addChild(v)
|
||||
}
|
||||
return ret
|
||||
}
|
96
src/widget/list.ts
Normal file
96
src/widget/list.ts
Normal file
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* 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, Superview, IView } from "../ui/view";
|
||||
import { Stack } from "./layouts";
|
||||
import { layoutConfig } from "../util/layoutconfig";
|
||||
|
||||
export class ListItem extends Stack {
|
||||
/**
|
||||
* Set to reuse native view
|
||||
*/
|
||||
@Property
|
||||
identifier?: string
|
||||
}
|
||||
|
||||
export interface IList extends IView {
|
||||
renderItem: (index: number) => ListItem
|
||||
itemCount: number
|
||||
batchCount?: number
|
||||
}
|
||||
|
||||
export class List extends Superview implements IList {
|
||||
private cachedViews: Map<string, ListItem> = new Map
|
||||
private ignoreDirtyCallOnce = false
|
||||
allSubviews() {
|
||||
return this.cachedViews.values()
|
||||
}
|
||||
|
||||
@Property
|
||||
itemCount = 0
|
||||
|
||||
@Property
|
||||
renderItem!: (index: number) => ListItem
|
||||
|
||||
@Property
|
||||
batchCount = 15
|
||||
|
||||
reset() {
|
||||
this.cachedViews.clear()
|
||||
this.itemCount = 0
|
||||
}
|
||||
private getItem(itemIdx: number) {
|
||||
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()
|
||||
}
|
||||
|
||||
private renderBunchedItems(start: number, length: number) {
|
||||
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()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export function list(config: IList) {
|
||||
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: View) {
|
||||
return (new ListItem).also((it) => {
|
||||
it.layoutConfig = layoutConfig().wrap()
|
||||
it.addChild(item)
|
||||
})
|
||||
}
|
74
src/widget/refreshable.ts
Normal file
74
src/widget/refreshable.ts
Normal file
@ -0,0 +1,74 @@
|
||||
import { View, Property, Superview, IView } from "../ui/view";
|
||||
import { List } from "./list";
|
||||
import { Scroller } from "./scroller";
|
||||
import { BridgeContext } from "../runtime/global";
|
||||
import { layoutConfig } from "../util/layoutconfig";
|
||||
|
||||
export interface IRefreshable extends IView {
|
||||
content: View
|
||||
header?: View
|
||||
onRefresh?: () => void
|
||||
}
|
||||
|
||||
export class Refreshable extends Superview implements IRefreshable {
|
||||
|
||||
content!: List | Scroller
|
||||
|
||||
header?: View
|
||||
|
||||
@Property
|
||||
onRefresh?: () => void
|
||||
|
||||
allSubviews() {
|
||||
const ret: View[] = [this.content]
|
||||
if (this.header) {
|
||||
ret.push(this.header)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
setRefreshable(context: BridgeContext, refreshable: boolean) {
|
||||
return this.nativeChannel(context, 'setRefreshable')(refreshable)
|
||||
}
|
||||
|
||||
setRefreshing(context: BridgeContext, refreshing: boolean) {
|
||||
return this.nativeChannel(context, 'setRefreshing')(refreshing)
|
||||
}
|
||||
|
||||
isRefreshable(context: BridgeContext) {
|
||||
return this.nativeChannel(context, 'isRefreshable')() as Promise<boolean>
|
||||
}
|
||||
|
||||
isRefreshing(context: BridgeContext) {
|
||||
return this.nativeChannel(context, 'isRefreshing')() as Promise<boolean>
|
||||
}
|
||||
|
||||
toModel() {
|
||||
this.dirtyProps.content = this.content.viewId
|
||||
this.dirtyProps.header = (this.header || {}).viewId
|
||||
return super.toModel()
|
||||
}
|
||||
}
|
||||
|
||||
export function refreshable(config: IRefreshable) {
|
||||
const ret = new Refreshable
|
||||
ret.layoutConfig = layoutConfig().wrap()
|
||||
for (let key in config) {
|
||||
Reflect.set(ret, key, Reflect.get(config, key, config), ret)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
export interface IPullable {
|
||||
startAnimation(): void
|
||||
stopAnimation(): void
|
||||
setPullingDistance(distance: number): void
|
||||
}
|
||||
|
||||
|
||||
export function pullable(context: BridgeContext, v: View, config: IPullable) {
|
||||
Reflect.set(v, 'startAnimation', config.startAnimation)
|
||||
Reflect.set(v, 'stopAnimation', config.stopAnimation)
|
||||
Reflect.set(v, 'setPullingDistance', config.setPullingDistance)
|
||||
return v
|
||||
}
|
41
src/widget/scroller.ts
Normal file
41
src/widget/scroller.ts
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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, View, IView } from '../ui/view'
|
||||
import { layoutConfig } from '../util/layoutconfig'
|
||||
|
||||
export function scroller(content: View) {
|
||||
return (new Scroller).also(v => {
|
||||
v.layoutConfig = layoutConfig().wrap()
|
||||
v.content = content
|
||||
})
|
||||
}
|
||||
|
||||
export interface IScroller extends IView {
|
||||
content: View
|
||||
}
|
||||
|
||||
export class Scroller extends Superview implements IScroller {
|
||||
content!: View
|
||||
|
||||
allSubviews() {
|
||||
return [this.content]
|
||||
}
|
||||
|
||||
toModel() {
|
||||
this.dirtyProps.content = this.content.viewId
|
||||
return super.toModel()
|
||||
}
|
||||
}
|
78
src/widget/slider.ts
Normal file
78
src/widget/slider.ts
Normal file
@ -0,0 +1,78 @@
|
||||
import { Superview, View, Property, IView } from "../ui/view";
|
||||
import { Stack } from "./layouts";
|
||||
import { layoutConfig } from "../util/layoutconfig";
|
||||
|
||||
export class SlideItem extends Stack {
|
||||
/**
|
||||
* Set to reuse native view
|
||||
*/
|
||||
@Property
|
||||
identifier?: string
|
||||
}
|
||||
|
||||
export interface ISlider extends IView {
|
||||
renderPage: (index: number) => SlideItem
|
||||
itemCount: number
|
||||
batchCount?: number
|
||||
}
|
||||
|
||||
export class Slider extends Superview implements ISlider {
|
||||
private cachedViews: Map<string, SlideItem> = new Map
|
||||
|
||||
private ignoreDirtyCallOnce = false
|
||||
|
||||
allSubviews() {
|
||||
return this.cachedViews.values()
|
||||
}
|
||||
@Property
|
||||
itemCount = 0
|
||||
|
||||
@Property
|
||||
renderPage!: (index: number) => SlideItem
|
||||
|
||||
@Property
|
||||
batchCount = 3
|
||||
|
||||
|
||||
private getItem(itemIdx: number) {
|
||||
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()
|
||||
}
|
||||
|
||||
private renderBunchedItems(start: number, length: number) {
|
||||
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()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export function slideItem(item: View) {
|
||||
return (new SlideItem).also((it) => {
|
||||
it.layoutConfig = layoutConfig().wrap()
|
||||
it.addChild(item)
|
||||
})
|
||||
}
|
||||
|
||||
export function slider(config: ISlider) {
|
||||
const ret = new Slider
|
||||
for (let key in config) {
|
||||
Reflect.set(ret, key, Reflect.get(config, key, config), ret)
|
||||
}
|
||||
return ret
|
||||
}
|
53
src/widget/text.ts
Normal file
53
src/widget/text.ts
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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 { IView, View, Property } from "../ui/view"
|
||||
import { Color } from "../util/color"
|
||||
import { Gravity } from "../util/gravity"
|
||||
import { layoutConfig } from "../util/layoutconfig"
|
||||
|
||||
export interface IText extends IView {
|
||||
text?: string
|
||||
textColor?: Color
|
||||
textSize?: number
|
||||
maxLines?: number
|
||||
textAlignment?: Gravity
|
||||
}
|
||||
|
||||
export class Text extends View implements IText {
|
||||
@Property
|
||||
text?: string
|
||||
|
||||
@Property
|
||||
textColor?: Color
|
||||
|
||||
@Property
|
||||
textSize?: number
|
||||
|
||||
@Property
|
||||
maxLines?: number
|
||||
|
||||
@Property
|
||||
textAlignment?: Gravity
|
||||
}
|
||||
|
||||
export function text(config: IText) {
|
||||
const ret = new Text
|
||||
ret.layoutConfig = layoutConfig().wrap()
|
||||
for (let key in config) {
|
||||
Reflect.set(ret, key, Reflect.get(config, key, config), ret)
|
||||
}
|
||||
return ret
|
||||
}
|
62
tsconfig.json
Normal file
62
tsconfig.json
Normal file
@ -0,0 +1,62 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"experimentalDecorators": true,
|
||||
/* Basic Options */
|
||||
// "incremental": true, /* Enable incremental compilation */
|
||||
"target": "ES2015", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
|
||||
"module": "es2015", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
|
||||
"lib": [], /* Specify library files to be included in the compilation. */
|
||||
// "allowJs": true, /* Allow javascript files to be compiled. */
|
||||
// "checkJs": true, /* Report errors in .js files. */
|
||||
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
|
||||
// "declaration": true, /* Generates corresponding '.d.ts' file. */
|
||||
// "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. */
|
||||
// "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 */
|
||||
// "removeComments": true, /* Do not emit comments to output. */
|
||||
// "noEmit": true, /* Do not emit outputs. */
|
||||
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
|
||||
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
|
||||
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
|
||||
/* Strict Type-Checking Options */
|
||||
"strict": true, /* Enable all strict type-checking options. */
|
||||
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
|
||||
// "strictNullChecks": true, /* Enable strict null checks. */
|
||||
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
|
||||
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
|
||||
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
|
||||
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
|
||||
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
|
||||
/* Additional Checks */
|
||||
// "noUnusedLocals": true, /* Report errors on unused locals. */
|
||||
// "noUnusedParameters": true, /* Report errors on unused parameters. */
|
||||
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
|
||||
/* Module Resolution Options */
|
||||
"moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
|
||||
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
|
||||
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
|
||||
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
||||
// "typeRoots": [], /* List of folders to include type definitions from. */
|
||||
// "types": [], /* Type declaration files to be included in compilation. */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
|
||||
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
|
||||
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
|
||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||
/* Source Map Options */
|
||||
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
|
||||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
|
||||
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
|
||||
/* Experimental Options */
|
||||
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
|
||||
"emitDecoratorMetadata": true /* Enables experimental support for emitting type metadata for decorators. */
|
||||
},
|
||||
"include": [
|
||||
"**/*.ts",
|
||||
]
|
||||
}
|
Reference in New Issue
Block a user