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`) pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy