diff --git a/.gitignore b/.gitignore
index fd4f2b0..a45a18c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,4 @@
node_modules
.DS_Store
+.idea/
+package-lock.json
\ No newline at end of file
diff --git a/abas/README2.md b/abas/README2.md
new file mode 100644
index 0000000..3cbd50c
--- /dev/null
+++ b/abas/README2.md
@@ -0,0 +1,22 @@
+# This readme is only for abas
+
+1. Checkout this wrapper repo into the same level of the project what you want to convert. e.g. abas-elements
+2. To fix the icons styles
+ Put 'WcIconMixin.js' file into abas-elements/src/mixins
+ Add 'import WcIconMixin from '../mixins/WcIconMixin';' in Icon.vue
+ Add mixin 'mixins: [WcIconMixin],' in Icon.vue
+3. Adjust 'vue.config.js' in abas-elements project by adding alias for the '@vue/web-component-wrapper'
+
+ '''
+ configureWebpack: {
+ resolve: {
+ alias: {
+ '@vue/web-component-wrapper': path.join(__dirname, '../vue-web-component-wrapper'),
+ },
+ },
+ },
+ '''
+4. add dependency into package.json in block 'devDependencies'
+ '''
+ "eslint-plugin-vue-libs": "^2.1.0"
+ '''
diff --git a/abas/WcIconMixin.js b/abas/WcIconMixin.js
new file mode 100644
index 0000000..fc720dc
--- /dev/null
+++ b/abas/WcIconMixin.js
@@ -0,0 +1,28 @@
+import { dom } from '@fortawesome/fontawesome-svg-core'
+
+/**
+ * @mixin
+ */
+export default {
+ mounted () {
+ const id = 'fa-styles'
+ const shadowRoot = this.getShadowRoot(this)
+ if (shadowRoot && !shadowRoot.getElementById(id)) {
+ const faStyles = document.createElement('style')
+ faStyles.setAttribute('id', id)
+ faStyles.textContent = dom.css()
+ shadowRoot.appendChild(faStyles)
+ }
+ },
+ methods: {
+ getShadowRoot (obj) {
+ if (obj.$parent) {
+ if (obj.$parent.$options && obj.$parent.$options.shadowRoot) {
+ return obj.$parent.$options.shadowRoot
+ }
+ return this.getShadowRoot(obj.$parent)
+ }
+ return null
+ }
+ }
+}
diff --git a/dist/vue-wc-wrapper.global.js b/dist/vue-wc-wrapper.global.js
index db8253a..ab07537 100644
--- a/dist/vue-wc-wrapper.global.js
+++ b/dist/vue-wc-wrapper.global.js
@@ -98,6 +98,54 @@ function getAttributes (node) {
return res
}
+function spreadProps (component) {
+ const result = {};
+ spreadNext(result, component);
+ return result
+}
+
+function spreadNext (result, component) {
+ if (component.props) {
+ appendProps(result, component.props);
+ }
+
+ if (component.mixins) {
+ component.mixins.forEach(function (mixin) {
+ spreadNext(result, mixin);
+ });
+ }
+ if (component.extends) {
+ spreadNext(result, component.extends);
+ }
+}
+
+function appendProps (result, props) {
+ if (Array.isArray(props)) {
+ processArrayProps(result, props);
+ } else {
+ processObjectProps(result, props);
+ }
+}
+
+function processObjectProps (result, props) {
+ for (const key in props) {
+ const camelKey = camelize(key);
+ if (!(camelKey in result)) {
+ result[camelKey] = props[key];
+ }
+ }
+}
+function processArrayProps (result, props) {
+ props.forEach(function (prop) {
+ if (typeof prop === 'string') {
+ const camelKey = camelize(prop);
+ if (!(camelKey in result)) {
+ result[camelKey] = undefined;
+ }
+ }
+ });
+}
+
function wrap (Vue, Component) {
const isAsync = typeof Component === 'function' && !Component.cid;
let isInitialized = false;
@@ -112,13 +160,13 @@ function wrap (Vue, Component) {
? Component.options
: Component;
+ // spread props
+ options.props = spreadProps(options);
// extract props info
- const propsList = Array.isArray(options.props)
- ? options.props
- : Object.keys(options.props || {});
+ const propsList = Object.keys(options.props || {});
hyphenatedPropsList = propsList.map(hyphenate);
camelizedPropsList = propsList.map(camelize);
- const originalPropsAsObject = Array.isArray(options.props) ? {} : options.props || {};
+ const originalPropsAsObject = options.props || {};
camelizedPropsMap = camelizedPropsList.reduce((map, key, i) => {
map[key] = originalPropsAsObject[propsList[i]];
return map
@@ -169,13 +217,13 @@ function wrap (Vue, Component) {
class CustomElement extends HTMLElement {
constructor () {
- super();
- this.attachShadow({ mode: 'open' });
+ const self = super();
+ self.attachShadow({ mode: 'open' });
- const wrapper = this._wrapper = new Vue({
+ const wrapper = self._wrapper = new Vue({
name: 'shadow-root',
- customElement: this,
- shadowRoot: this.shadowRoot,
+ customElement: self,
+ shadowRoot: self.shadowRoot,
data () {
return {
props: {},
@@ -195,8 +243,8 @@ function wrap (Vue, Component) {
let hasChildrenChange = false;
for (let i = 0; i < mutations.length; i++) {
const m = mutations[i];
- if (isInitialized && m.type === 'attributes' && m.target === this) {
- syncAttribute(this, m.attributeName);
+ if (isInitialized && m.type === 'attributes' && m.target === self) {
+ syncAttribute(self, m.attributeName);
} else {
hasChildrenChange = true;
}
@@ -204,11 +252,11 @@ function wrap (Vue, Component) {
if (hasChildrenChange) {
wrapper.slotChildren = Object.freeze(toVNodes(
wrapper.$createElement,
- this.childNodes
+ self.childNodes
));
}
});
- observer.observe(this, {
+ observer.observe(self, {
childList: true,
subtree: true,
characterData: true,
diff --git a/dist/vue-wc-wrapper.js b/dist/vue-wc-wrapper.js
index 36b4469..ba33aef 100644
--- a/dist/vue-wc-wrapper.js
+++ b/dist/vue-wc-wrapper.js
@@ -95,6 +95,54 @@ function getAttributes (node) {
return res
}
+function spreadProps (component) {
+ const result = {};
+ spreadNext(result, component);
+ return result
+}
+
+function spreadNext (result, component) {
+ if (component.props) {
+ appendProps(result, component.props);
+ }
+
+ if (component.mixins) {
+ component.mixins.forEach(function (mixin) {
+ spreadNext(result, mixin);
+ });
+ }
+ if (component.extends) {
+ spreadNext(result, component.extends);
+ }
+}
+
+function appendProps (result, props) {
+ if (Array.isArray(props)) {
+ processArrayProps(result, props);
+ } else {
+ processObjectProps(result, props);
+ }
+}
+
+function processObjectProps (result, props) {
+ for (const key in props) {
+ const camelKey = camelize(key);
+ if (!(camelKey in result)) {
+ result[camelKey] = props[key];
+ }
+ }
+}
+function processArrayProps (result, props) {
+ props.forEach(function (prop) {
+ if (typeof prop === 'string') {
+ const camelKey = camelize(prop);
+ if (!(camelKey in result)) {
+ result[camelKey] = undefined;
+ }
+ }
+ });
+}
+
function wrap (Vue, Component) {
const isAsync = typeof Component === 'function' && !Component.cid;
let isInitialized = false;
@@ -109,13 +157,13 @@ function wrap (Vue, Component) {
? Component.options
: Component;
+ // spread props
+ options.props = spreadProps(options);
// extract props info
- const propsList = Array.isArray(options.props)
- ? options.props
- : Object.keys(options.props || {});
+ const propsList = Object.keys(options.props || {});
hyphenatedPropsList = propsList.map(hyphenate);
camelizedPropsList = propsList.map(camelize);
- const originalPropsAsObject = Array.isArray(options.props) ? {} : options.props || {};
+ const originalPropsAsObject = options.props || {};
camelizedPropsMap = camelizedPropsList.reduce((map, key, i) => {
map[key] = originalPropsAsObject[propsList[i]];
return map
@@ -166,13 +214,13 @@ function wrap (Vue, Component) {
class CustomElement extends HTMLElement {
constructor () {
- super();
- this.attachShadow({ mode: 'open' });
+ const self = super();
+ self.attachShadow({ mode: 'open' });
- const wrapper = this._wrapper = new Vue({
+ const wrapper = self._wrapper = new Vue({
name: 'shadow-root',
- customElement: this,
- shadowRoot: this.shadowRoot,
+ customElement: self,
+ shadowRoot: self.shadowRoot,
data () {
return {
props: {},
@@ -192,8 +240,8 @@ function wrap (Vue, Component) {
let hasChildrenChange = false;
for (let i = 0; i < mutations.length; i++) {
const m = mutations[i];
- if (isInitialized && m.type === 'attributes' && m.target === this) {
- syncAttribute(this, m.attributeName);
+ if (isInitialized && m.type === 'attributes' && m.target === self) {
+ syncAttribute(self, m.attributeName);
} else {
hasChildrenChange = true;
}
@@ -201,11 +249,11 @@ function wrap (Vue, Component) {
if (hasChildrenChange) {
wrapper.slotChildren = Object.freeze(toVNodes(
wrapper.$createElement,
- this.childNodes
+ self.childNodes
));
}
});
- observer.observe(this, {
+ observer.observe(self, {
childList: true,
subtree: true,
characterData: true,
diff --git a/package.json b/package.json
index f7ec743..6abe05e 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@vue/web-component-wrapper",
- "version": "1.2.0",
+ "version": "1.3.0",
"description": "wrap a vue component as a web component.",
"main": "dist/vue-wc-wrapper.js",
"unpkg": "dist/vue-wc-wrapper.global.js",
diff --git a/src/index.js b/src/index.js
index 4409bd7..4788a1a 100644
--- a/src/index.js
+++ b/src/index.js
@@ -6,7 +6,8 @@ import {
injectHook,
getInitialProps,
createCustomEvent,
- convertAttributeValue
+ convertAttributeValue,
+ spreadProps
} from './utils.js'
export default function wrap (Vue, Component) {
@@ -23,13 +24,13 @@ export default function wrap (Vue, Component) {
? Component.options
: Component
+ // spread props
+ options.props = spreadProps(options)
// extract props info
- const propsList = Array.isArray(options.props)
- ? options.props
- : Object.keys(options.props || {})
+ const propsList = Object.keys(options.props || {})
hyphenatedPropsList = propsList.map(hyphenate)
camelizedPropsList = propsList.map(camelize)
- const originalPropsAsObject = Array.isArray(options.props) ? {} : options.props || {}
+ const originalPropsAsObject = options.props || {}
camelizedPropsMap = camelizedPropsList.reduce((map, key, i) => {
map[key] = originalPropsAsObject[propsList[i]]
return map
diff --git a/src/utils.js b/src/utils.js
index 0834f03..f202c68 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -94,3 +94,52 @@ function getAttributes (node) {
}
return res
}
+
+export function spreadProps (component) {
+ const result = {}
+ spreadNext(result, component)
+ return result
+}
+
+function spreadNext (result, component) {
+ if (component.props) {
+ appendProps(result, component.props)
+ }
+
+ if (component.mixins) {
+ component.mixins.forEach(function (mixin) {
+ spreadNext(result, mixin)
+ })
+ }
+ if (component.extends) {
+ spreadNext(result, component.extends)
+ }
+}
+
+function appendProps (result, props) {
+ if (Array.isArray(props)) {
+ processArrayProps(result, props)
+ } else {
+ processObjectProps(result, props)
+ }
+}
+
+function processObjectProps (result, props) {
+ for (const key in props) {
+ const camelKey = camelize(key)
+ if (!(camelKey in result)) {
+ result[camelKey] = props[key]
+ }
+ }
+}
+function processArrayProps (result, props) {
+ props.forEach(function (prop) {
+ if (typeof prop === 'string') {
+ const camelKey = camelize(prop)
+ if (!(camelKey in result)) {
+ result[camelKey] = undefined
+ }
+ }
+ })
+}
+
diff --git a/test/fixtures/spreadedProperties.html b/test/fixtures/spreadedProperties.html
new file mode 100644
index 0000000..e2d500f
--- /dev/null
+++ b/test/fixtures/spreadedProperties.html
@@ -0,0 +1,48 @@
+
+
+
+
diff --git a/test/setup.js b/test/setup.js
index 24e59e1..a69ac1b 100644
--- a/test/setup.js
+++ b/test/setup.js
@@ -20,6 +20,7 @@ module.exports = async function launchPage (name) {
}
beforeAll(async () => {
+ jest.setTimeout(10000)
browser = await puppeteer.launch(puppeteerOptions)
server = createServer({ root: process.cwd() })
await new Promise((resolve, reject) => {
diff --git a/test/test.js b/test/test.js
index 9423d7a..665d0fd 100644
--- a/test/test.js
+++ b/test/test.js
@@ -16,12 +16,76 @@ test('properties', async () => {
el.foo = 234
el.someProp = 'lol'
})
- const newFoo = await page.evaluate(() => el.vueComponent.foo)
+ const newFoo = await page.evaluate(() => el.vueComponent.foo)
expect(newFoo).toBe(234)
- const newBar = await page.evaluate(() => el.vueComponent.someProp)
+ const newBar = await page.evaluate(() => el.vueComponent.someProp)
expect(newBar).toBe('lol')
})
+test('spreadedProperties', async () => {
+ const { page } = await launchPage(`spreadedProperties`)
+
+ // props from 'extends'
+ const p0 = await page.evaluate(() => el.p0)
+ expect(p0).toBe(undefined)
+
+ // props from 'extends'
+ const p1 = await page.evaluate(() => el.p1)
+ expect(p1).toBe('p1')
+
+ // props from 'mixins'
+ const m0 = await page.evaluate(() => el.m0)
+ expect(m0).toBe('m0')
+
+ // props from 'mixins'
+ const m1 = await page.evaluate(() => el.m1)
+ expect(m1).toBe('m1')
+
+ // array props
+ const c1 = await page.evaluate(() => el.c1)
+ const c2 = await page.evaluate(() => el.c2)
+ const m2a = await page.evaluate(() => el.m2a)
+ const m2b = await page.evaluate(() => el.m2b)
+ expect(c1).toBe(undefined)
+ expect(c2).toBe(undefined)
+ expect(m2a).toBe(undefined)
+ expect(m2b).toBe(undefined)
+
+ // props proxying: set
+ await page.evaluate(() => {
+ el.p0 = 'new-p0'
+ el.p1 = 'new-p1'
+ el.m0 = 'new-m0'
+ el.m1 = 'new-m1'
+ el.c1 = 'new-c1'
+ el.m2a = 'new-m2a'
+ })
+ const newP0 = await page.evaluate(() => el.vueComponent.p0)
+ expect(newP0).toBe('new-p0')
+ const newP1 = await page.evaluate(() => el.vueComponent.p1)
+ expect(newP1).toBe('new-p1')
+ const newM0 = await page.evaluate(() => el.vueComponent.m0)
+ expect(newM0).toBe('new-m0')
+ const newM1 = await page.evaluate(() => el.vueComponent.m1)
+ expect(newM1).toBe('new-m1')
+ const newC1 = await page.evaluate(() => el.vueComponent.c1)
+ expect(newC1).toBe('new-c1')
+ const newM2a = await page.evaluate(() => el.vueComponent.m2a)
+ expect(newM2a).toBe('new-m2a')
+
+ // set via attribute
+ await page.evaluate(() => {
+ el.setAttribute('c1', 'foo')
+ el.setAttribute('p1', 'foo2')
+ el.setAttribute('m1', 'bar')
+ el.setAttribute('m2a', 'bla')
+ })
+ expect(await page.evaluate(() => el.c1)).toBe('foo')
+ expect(await page.evaluate(() => el.p1)).toBe('foo2')
+ expect(await page.evaluate(() => el.m1)).toBe('bar')
+ expect(await page.evaluate(() => el.m2a)).toBe('bla')
+})
+
test('attributes', async () => {
const { page } = await launchPage(`attributes`)