Skip to content

Commit d807056

Browse files
committed
include dist files
1 parent d4b049a commit d807056

File tree

3 files changed

+439
-1
lines changed

3 files changed

+439
-1
lines changed

.gitignore

-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
11
node_modules
22
.DS_Store
3-
dist

dist/vue-wc-wrapper.global.js

+222
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
var wrapVueWebComponent = (function () {
2+
'use strict';
3+
4+
const camelizeRE = /-(\w)/g;
5+
const camelize = str => {
6+
return str.replace(camelizeRE, (_, c) => c ? c.toUpperCase() : '')
7+
};
8+
9+
const hyphenateRE = /\B([A-Z])/g;
10+
const hyphenate = str => {
11+
return str.replace(hyphenateRE, '-$1').toLowerCase()
12+
};
13+
14+
function getInitialProps (propsList) {
15+
const res = {};
16+
propsList.forEach(key => {
17+
res[key] = undefined;
18+
});
19+
return res
20+
}
21+
22+
function callHooks (vm, hook) {
23+
if (vm) {
24+
const hooks = vm.$options[hook] || [];
25+
hooks.forEach(hook => {
26+
hook.call(vm);
27+
});
28+
}
29+
}
30+
31+
function createCustomEvent (name, args) {
32+
return new CustomEvent(name, {
33+
bubbles: false,
34+
cancelable: false,
35+
detail: args
36+
})
37+
}
38+
39+
const isBoolean = val => /function Boolean/.test(String(val));
40+
const isNumber = val => /function Number/.test(String(val));
41+
42+
function convertAttributeValue (value, name, options) {
43+
if (isBoolean(options.type)) {
44+
if (value === 'true' || value === 'false') {
45+
return value === 'true'
46+
}
47+
if (value === '' || value === name) {
48+
return true
49+
}
50+
return value != null
51+
} else if (isNumber(options.type)) {
52+
const parsed = parseFloat(value, 10);
53+
return isNaN(parsed) ? value : parsed
54+
} else {
55+
return value
56+
}
57+
}
58+
59+
function toVNodes (h, children) {
60+
const res = [];
61+
for (let i = 0; i < children.length; i++) {
62+
res.push(toVNode(h, children[i]));
63+
}
64+
return res
65+
}
66+
67+
function toVNode (h, node) {
68+
if (node.nodeType === 3) {
69+
return node.data.trim() ? node.data : null
70+
} else if (node.nodeType === 1) {
71+
const data = {
72+
attrs: getAttributes(node),
73+
domProps: {
74+
innerHTML: node.innerHTML
75+
}
76+
};
77+
if (data.attrs.slot) {
78+
data.slot = data.attrs.slot;
79+
delete data.attrs.slot;
80+
}
81+
return h(node.tagName, data)
82+
} else {
83+
return null
84+
}
85+
}
86+
87+
function getAttributes (node) {
88+
const res = {};
89+
for (let i = 0; i < node.attributes.length; i++) {
90+
const attr = node.attributes[i];
91+
res[attr.nodeName] = attr.nodeValue;
92+
}
93+
return res
94+
}
95+
96+
function wrap (Vue, Component) {
97+
const options = typeof Component === 'function'
98+
? Component.options
99+
: Component;
100+
101+
// inject hook to proxy $emit to native DOM events
102+
options.beforeCreate = [].concat(options.beforeCreate || []);
103+
options.beforeCreate.unshift(function () {
104+
const emit = this.$emit;
105+
this.$emit = (name, ...args) => {
106+
this.$root.$options.customElement.dispatchEvent(createCustomEvent(name, args));
107+
return emit.call(this, name, ...args)
108+
};
109+
});
110+
111+
// extract props info
112+
const propsList = Array.isArray(options.props)
113+
? options.props
114+
: Object.keys(options.props || {});
115+
const hyphenatedPropsList = propsList.map(hyphenate);
116+
const camelizedPropsList = propsList.map(camelize);
117+
const originalPropsAsObject = Array.isArray(options.props) ? {} : options.props || {};
118+
const camelizedPropsMap = camelizedPropsList.reduce((map, key, i) => {
119+
map[key] = originalPropsAsObject[propsList[i]];
120+
return map
121+
}, {});
122+
123+
class CustomElement extends HTMLElement {
124+
static get observedAttributes () {
125+
return hyphenatedPropsList
126+
}
127+
128+
constructor () {
129+
super();
130+
const wrapper = this._wrapper = new Vue({
131+
name: 'shadow-root',
132+
customElement: this,
133+
data () {
134+
return {
135+
props: getInitialProps(camelizedPropsList),
136+
slotChildren: []
137+
}
138+
},
139+
render (h) {
140+
return h(Component, {
141+
ref: 'inner',
142+
props: this.props
143+
}, this.slotChildren)
144+
}
145+
});
146+
147+
// in Chrome, this.childNodes will be empty when connectedCallback
148+
// is fired, so it's necessary to use a mutationObserver
149+
const observer = new MutationObserver(() => {
150+
wrapper.slotChildren = Object.freeze(toVNodes(
151+
wrapper.$createElement,
152+
this.childNodes
153+
));
154+
});
155+
observer.observe(this, {
156+
childList: true,
157+
subtree: true,
158+
characterData: true,
159+
attributes: true
160+
});
161+
}
162+
163+
get vueComponent () {
164+
return this._wrapper.$refs.inner
165+
}
166+
167+
connectedCallback () {
168+
const wrapper = this._wrapper;
169+
if (!wrapper._isMounted) {
170+
this._shadowRoot = this.attachShadow({ mode: 'open' });
171+
wrapper.$options.shadowRoot = this._shadowRoot;
172+
// initialize children
173+
wrapper.slotChildren = Object.freeze(toVNodes(
174+
wrapper.$createElement,
175+
this.childNodes
176+
));
177+
wrapper.$mount();
178+
// sync default props values to wrapper
179+
camelizedPropsList.forEach(key => {
180+
wrapper.props[key] = this.vueComponent[key];
181+
});
182+
this._shadowRoot.appendChild(wrapper.$el);
183+
} else {
184+
callHooks(this.vueComponent, 'activated');
185+
}
186+
}
187+
188+
disconnectedCallback () {
189+
callHooks(this.vueComponent, 'deactivated');
190+
}
191+
192+
// watch attribute change and sync
193+
attributeChangedCallback (attrName, oldVal, newVal) {
194+
const camelized = camelize(attrName);
195+
this._wrapper.props[camelized] = convertAttributeValue(
196+
newVal,
197+
attrName,
198+
camelizedPropsMap[camelized]
199+
);
200+
}
201+
}
202+
203+
// proxy props as Element properties
204+
camelizedPropsList.forEach(key => {
205+
Object.defineProperty(CustomElement.prototype, key, {
206+
get () {
207+
return this._wrapper.props[key]
208+
},
209+
set (newVal) {
210+
this._wrapper.props[key] = newVal;
211+
},
212+
enumerable: false,
213+
configurable: true
214+
});
215+
});
216+
217+
return CustomElement
218+
}
219+
220+
return wrap;
221+
222+
}());

0 commit comments

Comments
 (0)