diff --git a/jsconfig.json b/jsconfig.json
index 5fb0b37701d289d66d02dd34c835c945f48adbec..5b9eea21335f2dfb2af18b35c5fa0a1f462b1060 100644
--- a/jsconfig.json
+++ b/jsconfig.json
@@ -2,6 +2,15 @@
"compilerOptions": {
"baseUrl": ".",
"paths": {
+ "uni-shared": [
+ "./src/shared/index.js"
+ ],
+ "uni-helpers/*": [
+ "./src/core/helpers/*"
+ ],
+ "uni-mixins/*": [
+ "./src/core/view/mixins/*"
+ ],
"uni-core/*": [
"./src/core/*"
],
diff --git a/packages/uni-template-compiler/lib/app/parser/tag-parser.js b/packages/uni-template-compiler/lib/app/parser/tag-parser.js
index 0ac94d9801c952ca4f4a67ecfaf2992ed62fd217..70e7d0dc0b4249c023802950ceb63ca0673d7c12 100644
--- a/packages/uni-template-compiler/lib/app/parser/tag-parser.js
+++ b/packages/uni-template-compiler/lib/app/parser/tag-parser.js
@@ -4,8 +4,17 @@ const {
const tags = require('@dcloudio/uni-cli-shared/lib/tags')
+// web components
+const elements = ['uni-view']
+
// 仅限 view 层
module.exports = function parseTag (el) {
+ const tag = el.tag
+ const element = elements.find(element => tag === element || 'uni-' + tag === element)
+ if (element) {
+ el.tag = element
+ return
+ }
if (el.tag.indexOf('v-uni-') !== 0 && hasOwn(tags, el.tag)) {
el.tag = 'v-uni-' + el.tag
}
diff --git a/packages/uni-template-compiler/lib/util.js b/packages/uni-template-compiler/lib/util.js
index fa0ef6afaf359ce1641751695de3bfcc6000d762..5e68bb7dc448d310a7861de4cece433f8d41069c 100644
--- a/packages/uni-template-compiler/lib/util.js
+++ b/packages/uni-template-compiler/lib/util.js
@@ -210,7 +210,7 @@ function isComponent (tagName) {
return false
}
}
- return !hasOwn(tags, getTagName(tagName.replace('v-uni-', '')))
+ return !hasOwn(tags, getTagName(tagName.replace(/^(v-)?uni-/, '')))
}
function makeMap (str, expectsLowerCase) {
diff --git a/src/core/helpers/custom-elements-define.js b/src/core/helpers/custom-elements-define.js
new file mode 100644
index 0000000000000000000000000000000000000000..1729cbd43c758c4c2d39e081e258c5cd257606d0
--- /dev/null
+++ b/src/core/helpers/custom-elements-define.js
@@ -0,0 +1,51 @@
+/**
+ * customElements.define
+ */
+(function () {
+ const defineProperty = Object.defineProperty
+ const createElement = document.createElement
+ const classes = new Map()
+ const registry = new Map()
+
+ if ('customElements' in window && customElements && customElements.define) {
+ return
+ }
+
+ function HTMLBuiltIn () {
+ const constructor = this.constructor
+ if (!classes.has(constructor)) {
+ throw new TypeError('Illegal constructor')
+ }
+ const is = classes.get(constructor)
+ const element = createElement.call(document, is)
+ return Object.setPrototypeOf(element, constructor.prototype)
+ }
+
+ defineProperty(HTMLBuiltIn.prototype = HTMLElement.prototype, 'constructor', {
+ value: HTMLBuiltIn
+ })
+ defineProperty(window, 'HTMLElement', {
+ configurable: true,
+ value: HTMLBuiltIn
+ })
+ defineProperty(document, 'createElement', {
+ configurable: true,
+ value: function value (name, options) {
+ const is = options && options.is
+ const Class = is ? registry.get(is) : registry.get(name)
+ return Class ? new Class() : createElement.call(document, name)
+ }
+ })
+ defineProperty(window, 'customElements', {
+ configurable: true,
+ value: {
+ define: function define (is, Class) {
+ if (registry.has(is)) {
+ throw new Error('the name "'.concat(is, '" has already been used with this registry'))
+ }
+ classes.set(Class, is)
+ registry.set(is, Class)
+ }
+ }
+ })
+})()
diff --git a/src/core/helpers/index.js b/src/core/helpers/index.js
index 257e6c483778ab89392183ce054687d30b789b0d..7a98e8d7376b536ca51209e55740ba42a2b1724c 100644
--- a/src/core/helpers/index.js
+++ b/src/core/helpers/index.js
@@ -63,7 +63,7 @@ export function getTargetDataset (target) {
$parent = $parent.$parent
}
} else {
- dataset = target.dataset || {}
+ dataset = Object.assign({}, target.dataset, target.__uniDataset)
}
return normalizeDataset(dataset)
}
diff --git a/src/core/view/components/index.js b/src/core/view/components/index.js
index 3564472fb89b30be2ab7ac65d24987a01cdf230a..e18b703c5f3b9f0ec534f508909141867036376c 100644
--- a/src/core/view/components/index.js
+++ b/src/core/view/components/index.js
@@ -9,6 +9,19 @@ const requireComponents = [
require.context('../../../platforms/' + __PLATFORM__ + '/view/components', true, /index\.vue$/)
]
+let elements = {}
+
+if (__PLATFORM__ === 'app-plus') {
+ // TODO use full polyfill
+ require('uni-core/helpers/custom-elements-define')
+ const module = require('../../../platforms/app-plus/view/elements/index.js')
+ elements = module.default || module
+ for (const key in elements) {
+ // TODO use kebabCase
+ customElements.define(`uni-${key.toLowerCase()}`, elements[key])
+ }
+}
+
requireComponents.forEach((components, index) => {
components.keys().forEach(fileName => {
// 获取组件配置
@@ -18,10 +31,12 @@ requireComponents.forEach((components, index) => {
componentConfig.mixins = componentConfig.mixins ? [].concat(baseMixin, componentConfig.mixins) : [baseMixin]
- componentConfig.mixins.push(animation)
+ if (!componentConfig.functional) {
+ componentConfig.mixins.push(animation)
+ }
+
+ componentConfig.name = 'VUni' + componentConfig.name
- componentConfig.name = 'VUni' + componentConfig.name
-
componentConfig.isReserved = true
// 全局注册组件
diff --git a/src/core/view/mixins/animation.js b/src/core/view/mixins/animation.js
index 9ae7baff0adc527f68b29c48bb9681d95fda7d60..a3577a3dd794024b07bf8022f598789c263cf0df 100644
--- a/src/core/view/mixins/animation.js
+++ b/src/core/view/mixins/animation.js
@@ -51,7 +51,7 @@ function getStyle (action) {
return style
}
-function startAnimation (context) {
+export function startAnimation (context) {
const animation = context.animation
if (!animation || !animation.actions || !animation.actions.length) {
return
diff --git a/src/platforms/app-plus/view/components/view/index.vue b/src/platforms/app-plus/view/components/view/index.vue
new file mode 100644
index 0000000000000000000000000000000000000000..151086931c4c518992719bc3538445b5b58cedc0
--- /dev/null
+++ b/src/platforms/app-plus/view/components/view/index.vue
@@ -0,0 +1,19 @@
+
+
+
diff --git a/src/platforms/app-plus/view/elements/animation.js b/src/platforms/app-plus/view/elements/animation.js
new file mode 100644
index 0000000000000000000000000000000000000000..cdc85f69fc18ce6956018a951932975cc5b4b9d0
--- /dev/null
+++ b/src/platforms/app-plus/view/elements/animation.js
@@ -0,0 +1,14 @@
+import UniElement from './element'
+import { startAnimation } from 'uni-core/view/mixins/animation'
+
+export default class UniAnimationElement extends UniElement {
+ setAttribute (key, value) {
+ if (key === 'animation') {
+ startAnimation({
+ $el: this,
+ animation: value
+ })
+ }
+ super.setAttribute(key, value)
+ }
+}
diff --git a/src/platforms/app-plus/view/elements/element.js b/src/platforms/app-plus/view/elements/element.js
new file mode 100644
index 0000000000000000000000000000000000000000..4b510875e247394b612645fa2dbc5df7ec733c50
--- /dev/null
+++ b/src/platforms/app-plus/view/elements/element.js
@@ -0,0 +1,24 @@
+import {
+ camelize
+} from 'uni-shared'
+
+function formatKey (key) {
+ return camelize(key.substring(5))
+}
+
+export default class UniElement extends HTMLElement {
+ setAttribute (key, value) {
+ if (key.startsWith('data-')) {
+ const dataset = this.__uniDataset || (this.__uniDataset = {})
+ dataset[formatKey(key)] = value
+ }
+ super.setAttribute(key, value)
+ }
+
+ removeAttribute (key) {
+ if (this.__uniDataset && key.startsWith('data-')) {
+ delete this.__uniDataset[formatKey(key)]
+ }
+ super.removeAttribute(key)
+ }
+}
diff --git a/src/platforms/app-plus/view/elements/hover.js b/src/platforms/app-plus/view/elements/hover.js
new file mode 100644
index 0000000000000000000000000000000000000000..deee362ba8749cd94481ceea45b18f5adc498bff
--- /dev/null
+++ b/src/platforms/app-plus/view/elements/hover.js
@@ -0,0 +1,109 @@
+import UniAnimationElement from './animation'
+
+export default class UniHoverElement extends UniAnimationElement {
+ setAttribute (key, value) {
+ console.log('setAttribute:', key, value)
+ if (key === 'hover-class') {
+ this._updateHoverClass(value)
+ }
+ super.setAttribute(key, value)
+ }
+
+ removeAttribute (key) {
+ if (key === 'hover-class') {
+ this._updateHoverClass()
+ }
+ super.removeAttribute(key)
+ }
+
+ get hovering () {
+ return this._hovering
+ }
+
+ set hovering (hovering) {
+ this._hovering = hovering
+ const hoverClass = this.getAttribute('hover-class')
+ if (hovering) {
+ this.classList.add(hoverClass)
+ } else {
+ this.classList.remove(hoverClass)
+ }
+ }
+
+ _updateHoverClass (hoverClass) {
+ hoverClass = hoverClass || 'none'
+ if (hoverClass === 'none') {
+ this._removeEventListener()
+ } else {
+ this._addEventListener()
+ }
+ }
+
+ _addEventListener () {
+ if (!this.__hoverTouchStart) {
+ this.addEventListener('touchstart', this.__hoverTouchStart = this._hoverTouchStart.bind(this))
+ this.addEventListener('touchend', this.__hoverTouchEnd = this._hoverTouchEnd.bind(this))
+ this.addEventListener('touchcancel', this.__hoverTouchCancel = this._hoverTouchCancel.bind(this))
+ }
+ }
+
+ _removeEventListener () {
+ if (this.__hoverTouchStart) {
+ this.removeEventListener('touchstart', this.__hoverTouchStart)
+ delete this.__hoverTouchStart
+ this.removeEventListener('touchend', this.__hoverTouchEnd)
+ delete this.__hoverTouchEnd
+ this.removeEventListener('touchcancel', this.__hoverTouchCancel)
+ delete this.__hoverTouchCancel
+ }
+ }
+
+ _hoverTouchStart (evt) {
+ if (evt._hoverPropagationStopped) {
+ return
+ }
+ if (this.disabled) {
+ return
+ }
+ if (evt.touches.length > 1) {
+ return
+ }
+ if (this.getAttribute('hover-stop-propagation')) {
+ evt._hoverPropagationStopped = true
+ }
+ this._hoverTouch = true
+ const hoverStartTimeDefault = 50
+ const hoverStartTime = Number(this.getAttribute('hover-start-time') || hoverStartTimeDefault)
+ this._hoverStartTimer = setTimeout(() => {
+ this.hovering = true
+ if (!this._hoverTouch) {
+ // 防止在hoverStartTime时间内触发了 touchend 或 touchcancel
+ this._hoverReset()
+ }
+ }, isNaN(hoverStartTime) ? hoverStartTimeDefault : hoverStartTime)
+ }
+
+ _hoverTouchEnd () {
+ this._hoverTouch = false
+ if (this.hovering) {
+ this._hoverReset()
+ }
+ }
+
+ _hoverReset () {
+ requestAnimationFrame(() => {
+ clearTimeout(this._hoverStayTimer)
+ const hoverStayTimeDefault = 400
+ const hoverStayTime = Number(this.getAttribute('hover-stay-time') || hoverStayTimeDefault)
+ this._hoverStayTimer = setTimeout(() => {
+ this.hovering = false
+ }, isNaN(hoverStayTime) ? hoverStayTimeDefault : hoverStayTime)
+ })
+ }
+
+ _hoverTouchCancel () {
+ this._hoverTouch = false
+ this.hovering = false
+ clearTimeout(this._hoverStartTimer)
+ }
+}
diff --git a/src/platforms/app-plus/view/elements/index.js b/src/platforms/app-plus/view/elements/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..7fd9d6ed6a380e7d24103c5a77b4bb1adbbd2f7f
--- /dev/null
+++ b/src/platforms/app-plus/view/elements/index.js
@@ -0,0 +1,5 @@
+import View from './view'
+
+export default {
+ View
+}
diff --git a/src/platforms/app-plus/view/elements/view.js b/src/platforms/app-plus/view/elements/view.js
new file mode 100644
index 0000000000000000000000000000000000000000..9ccac76150fff464b2caaf378751623561d9e653
--- /dev/null
+++ b/src/platforms/app-plus/view/elements/view.js
@@ -0,0 +1,5 @@
+import UniHoverElement from './hover'
+
+export default class UniView extends UniHoverElement {
+
+}
diff --git a/src/core/view/components/view/README.md b/src/platforms/h5/view/components/view/README.md
similarity index 100%
rename from src/core/view/components/view/README.md
rename to src/platforms/h5/view/components/view/README.md
diff --git a/src/core/view/components/view/index.vue b/src/platforms/h5/view/components/view/index.vue
similarity index 100%
rename from src/core/view/components/view/index.vue
rename to src/platforms/h5/view/components/view/index.vue