diff --git a/packages/driver-harmony/CHANGELOG.md b/packages/driver-harmony/CHANGELOG.md new file mode 100644 index 000000000..6d81f6f12 --- /dev/null +++ b/packages/driver-harmony/CHANGELOG.md @@ -0,0 +1,3 @@ +## Changelog + +### v1.0.0 diff --git a/packages/driver-harmony/README.md b/packages/driver-harmony/README.md new file mode 100644 index 000000000..27faa2519 --- /dev/null +++ b/packages/driver-harmony/README.md @@ -0,0 +1,28 @@ +# driver-harmony + +> Harmony OS driver for Rax. + +## Install + +```bash +$ npm install --save driver-harmony +``` + +## Use + +```jsx +import {createElement, render} from 'rax'; +import DriverHarmony from 'driver-harmony'; + +function Example() { + return ( +
+ +
+ ); +} + +render(, null, { + driver: DriverHarmony +}); +``` diff --git a/packages/driver-harmony/package.json b/packages/driver-harmony/package.json new file mode 100644 index 000000000..b47fdeb7e --- /dev/null +++ b/packages/driver-harmony/package.json @@ -0,0 +1,22 @@ +{ + "name": "driver-harmony", + "version": "1.0.0-beta.0", + "description": "Harmony driver for Rax", + "license": "BSD-3-Clause", + "main": "lib/index.js", + "module": "es/index.js", + "repository": { + "type": "git", + "url": "git+https://github.com/alibaba/rax.git" + }, + "bugs": { + "url": "https://github.com/alibaba/rax/issues" + }, + "homepage": "https://github.com/alibaba/rax#readme", + "dependencies": { + "style-unit": "^3.0.0" + }, + "devDependencies": { + "rax": "^1.0.0" + } +} diff --git a/packages/driver-harmony/src/index.d.ts b/packages/driver-harmony/src/index.d.ts new file mode 100644 index 000000000..0658336d4 --- /dev/null +++ b/packages/driver-harmony/src/index.d.ts @@ -0,0 +1,20 @@ +// Driver spec: https://github.com/alibaba/rax/blob/master/docs/en-US/driver-spec.md +declare namespace Driver { + function createBody(): void; + function createEmpty(component: any): void; + function createText(): void; + function updateText(node: any, text: any) + function createElement(type: any, props: any, component: any) + function appendChild(node: any, parent: any) + function removeChild(node: any, parent: any) + function replaceChild(newChild: any, oldChild: any, parent: any) + function insertAfter(node: any, after: any, parent: any) + function insertBefore(node: any, before: any, parent: any) + function addEventListener(node: any, eventName: any, eventHandler: any) + function removeEventListener(node: any, eventName: any, eventHandler: any) + function setAttribute(node: any, propKey: any, propValue: any) + function removeAttribute(node: any, propKey: any) + function setStyle(node: any, styleObject: any) +} + +export default Driver; diff --git a/packages/driver-harmony/src/index.js b/packages/driver-harmony/src/index.js new file mode 100644 index 000000000..315c632f3 --- /dev/null +++ b/packages/driver-harmony/src/index.js @@ -0,0 +1,200 @@ +import { convertUnit, setTargetPlatform } from 'style-unit'; + +/** + * Server driver + **/ +const ID = 'id'; +const STYLE = 'style'; +const CHILDREN = 'children'; +const EVENT_PREFIX_REGEXP = /^on[A-Z]/; +const REF = 'ref'; + +const TEXT = 'text'; +const EMPTY = ''; + +setTargetPlatform('harmony'); + +const Driver = { + // Internal state + nodeMaps: {}, + + getElementById(id) { + return this.nodeMaps[id]; + }, + + createBody() { + return document.body; + }, + + createComment() { + // Use block element as comment node, because harmony not support append comment node + const node = this.createElement('block'); + node.__type = 'comment'; + return node; + }, + + createEmpty() { + return this.createComment(EMPTY); + }, + + createText(text) { + // Use comment node type mock text node + const node = this.createComment(EMPTY); + node.value = text; + return node; + }, + + updateText(node, text) { + node.value = text; + this.updateTextValue(node.parentNode); + }, + + updateTextValue(node) { + const value = node.children.map(function(child) { + // Comment node type + return child.__type === 'comment' ? child.value : EMPTY; + }).join(EMPTY); + + node.setAttr('value', value); + }, + + createElement(type, props) { + props = props || {}; + const style = {}; + const originStyle = props.style; + if (originStyle) { + for (let prop in originStyle) { + style[prop] = convertUnit(originStyle[prop], prop); + } + } + + const node = document.createElement(type, { + style, + }); + + this.setNativeProps(node, props, true); + + return node; + }, + + appendChild(node, parent) { + parent.appendChild(node); + + if (parent.type === TEXT) { + this.updateTextValue(parent); + } + }, + + aceAppendChildren(node, parent) { + this.aceAppendChild(node, parent); + }, + + removeChild(node, parent) { + parent = parent || node.parentNode; + let id = node.attr && node.attr[ID]; + if (id != null) { + this.nodeMaps[id] = null; + } + + parent.removeChild(node); + + if (parent.type === TEXT) { + this.updateTextValue(parent); + } + }, + + replaceChild(newChild, oldChild, parent) { + parent = parent || oldChild.parentNode; + + let previousSibling; + let nextSibling; + + previousSibling = oldChild.previousSibling; + nextSibling = oldChild.nextSibling; + this.removeChild(oldChild, parent); + + if (previousSibling) { + this.insertAfter(newChild, previousSibling, parent); + } else if (nextSibling) { + this.insertBefore(newChild, nextSibling, parent); + } else { + this.appendChild(newChild, parent); + } + }, + + insertAfter(node, after, parent) { + parent = parent || after.parentNode; + parent.insertAfter(node, after); + + + if (parent.type === TEXT) { + this.updateTextValue(parent); + } + }, + + insertBefore(node, before, parent) { + parent = parent || before.parentNode; + parent.insertBefore(node, before); + + if (parent.type === TEXT) { + this.updateTextValue(parent); + } + }, + + addEventListener(node, eventName, eventHandler) { + node.addEvent(eventName, eventHandler); + }, + + removeEventListener(node, eventName, eventHandler) { + node.removeEvent(eventName, eventHandler); + }, + + removeAttribute(node, propKey, propValue) { + if (propKey === ID) { + this.nodeMaps[propValue] = null; + } + return node.setAttr(propKey, undefined, false); + }, + + setAttribute(node, propKey, propValue) { + if (propKey === ID) { + this.nodeMaps[propValue] = node; + } + // ref is harmony node internal key + if (propKey === REF) return; + return node.setAttr(propKey, propValue); + }, + + setStyle(node, style) { + for (let prop in style) { + // Translate `rpx` to weex `px` + style[prop] = convertUnit(style[prop], prop); + } + node.setStyles(style); + }, + + setNativeProps(node, props, shouldIgnoreStyleProp) { + for (let prop in props) { + let value = props[prop]; + if (prop === CHILDREN) { + continue; + } + + if (value != null) { + if (prop === STYLE) { + if (shouldIgnoreStyleProp) { + continue; + } + this.setStyle(node, value); + } else if (EVENT_PREFIX_REGEXP.test(prop)) { + let eventName = prop.slice(2).toLowerCase(); + this.addEventListener(node, eventName, value); + } else { + this.setAttribute(node, prop, value); + } + } + } + } +}; + +export default Driver; diff --git a/packages/driver-universal/package.json b/packages/driver-universal/package.json index 8e4da57af..b2486db35 100644 --- a/packages/driver-universal/package.json +++ b/packages/driver-universal/package.json @@ -1,6 +1,6 @@ { "name": "driver-universal", - "version": "3.3.1", + "version": "3.4.0-beta.1", "description": "Driver for Universal App.", "license": "BSD-3-Clause", "main": "lib/index.js", @@ -15,6 +15,7 @@ "bytedance-microapp": "./es/miniapp.js", "baidu-smartprogram": "./es/miniapp.js", "kuaishou-miniprogram": "./es/miniapp.js", + "harmony": "./es/harmony.js", "default": "./es/index.js" }, "./*": "./*" @@ -36,6 +37,7 @@ "driver-kraken": "^0.2.0", "driver-miniapp": "^0.1.0", "driver-weex": "^2.0.0", + "driver-harmony": "^1.0.0-beta.0", "universal-env": "^3.0.0" } } diff --git a/packages/driver-universal/src/harmony.js b/packages/driver-universal/src/harmony.js new file mode 100644 index 000000000..5e69cdf13 --- /dev/null +++ b/packages/driver-universal/src/harmony.js @@ -0,0 +1,3 @@ +import HarmonyDriver from 'driver-harmony'; + +export default HarmonyDriver; diff --git a/packages/driver-universal/src/index.js b/packages/driver-universal/src/index.js index f3198ba44..1e948ff9d 100644 --- a/packages/driver-universal/src/index.js +++ b/packages/driver-universal/src/index.js @@ -10,7 +10,9 @@ import { } from 'universal-env'; let currentDriver; -if (isWeex) { +if (typeof ace !== 'undefined') { + currentDriver = require('./harmony').default; +} else if (isWeex) { currentDriver = require('./weex').default; } else if (isWeb) { currentDriver = require('./web').default; diff --git a/packages/style-unit/src/index.js b/packages/style-unit/src/index.js index 112c180d4..08ff64743 100644 --- a/packages/style-unit/src/index.js +++ b/packages/style-unit/src/index.js @@ -65,7 +65,7 @@ export function calcRpx(str) { // In Web convert rpx to 'vw', same as driver-dom and driver-universal // '375rpx' => '50vw' return str.replace(RPX_REG, decimalVWTransformer); - } else if (targetPlatform === 'weex') { + } else if (targetPlatform === 'weex' || targetPlatform === 'harmony') { // In Weex convert rpx to 'px' // '375rpx' => 375 * px return str.replace(RPX_REG, decimalPixelTransformer); diff --git a/scripts/dist-core.js b/scripts/dist-core.js index 56f510d57..7db471c3b 100755 --- a/scripts/dist-core.js +++ b/scripts/dist-core.js @@ -174,6 +174,10 @@ buildCorePackages({ packageName: 'driver-weex', name: 'DriverKraken' }); +buildCorePackages({ + packageName: 'driver-harmony', + name: 'DriverHarmony' +}); // Build rax compat react version to rax/lib/compat/index.js // It needs external ../../index, which won't bundle rax into lib/compat/index.js