UNPKG

8.38 kBJavaScriptView Raw
1import Methods from './methods.js';
2import Binder from './binder.js';
3import Loader from './loader.js';
4import Model from './model.js';
5import Style from './style.js';
6import Utility from './utility.js';
7import Definer from './definer.js';
8
9export default {
10
11 data: {},
12
13 async setup (options) {
14 const self = this;
15
16 options = options || {};
17
18 if (options.components) {
19 return Promise.all(options.components.map(function (component) {
20 if (typeof component === 'string') {
21 return Loader.load(component).then(function (load) {
22 return self.define(load.default);
23 });
24 } else {
25 return self.define(component);
26 }
27 }));
28 }
29
30 },
31
32 style (style, name) {
33
34 style = style.replace(/\n|\r|\t/g, '');
35 style = style.replace(/:host/g, name);
36
37 if (!window.CSS || !window.CSS.supports || !window.CSS.supports('(--t: black)')) {
38 const matches = style.match(/--\w+(?:-+\w+)*:\s*.*?;/g) || [];
39 for (let i = 0, l = matches.length; i < l; i++) {
40 const match = matches[i];
41 const rule = match.match(/(--\w+(?:-+\w+)*):\s*(.*?);/);
42 const pattern = new RegExp('var\\('+rule[1]+'\\)', 'g');
43 style = style.replace(rule[0], '');
44 style = style.replace(pattern, rule[2]);
45 }
46 }
47
48 return style;
49 },
50
51 slot (element, fragment) {
52 const fragmentSlots = fragment.querySelectorAll('slot[name]');
53 const defaultSlot = fragment.querySelector('slot:not([name])');
54
55 for (let i = 0, l = fragmentSlots.length; i < l; i++) {
56
57 const fragmentSlot = fragmentSlots[i];
58 const name = fragmentSlot.getAttribute('name');
59 const elementSlot = element.querySelector('[slot="'+ name + '"]');
60
61 if (elementSlot) {
62 fragmentSlot.parentNode.replaceChild(elementSlot, fragmentSlot);
63 } else {
64 fragmentSlot.parentNode.removeChild(fragmentSlot);
65 }
66
67 }
68
69 if (defaultSlot) {
70
71 if (element.children.length) {
72
73 while (element.firstChild) {
74 defaultSlot.parentNode.insertBefore(element.firstChild, defaultSlot);
75 }
76
77 }
78
79 defaultSlot.parentNode.removeChild(defaultSlot);
80 }
81
82 },
83
84 fragment (element, template, adopt) {
85 const fragment = document.createDocumentFragment();
86 const clone = template.cloneNode(true);
87
88 let child = clone.firstElementChild;
89 while (child) {
90
91 if (!adopt) {
92 Binder.add(child, { container: element, scope: element.scope });
93 }
94
95 fragment.appendChild(child);
96 child = clone.firstElementChild;
97 }
98
99 return fragment;
100 },
101
102 render (element, template, adopt, shadow) {
103
104 if (!template) {
105 return;
106 }
107
108 const fragment = this.fragment(element, template);
109
110 let root;
111
112 if (shadow && 'attachShadow' in document.body) {
113 root = element.attachShadow({ mode: 'open' })
114 } else if (shadow && 'createShadowRoot' in document.body) {
115 root = element.createShadowRoot();
116 } else {
117
118 if (fragment) {
119 this.slot(element, fragment);
120 }
121
122 root = element;
123 }
124
125 if (fragment) {
126 root.appendChild(fragment);
127 }
128
129 if (adopt) {
130 let child = root.firstElementChild;
131 while (child) {
132 Binder.add(child, { container: element, scope: element.scope });
133 child = child.nextElementSibling;
134 }
135 }
136
137 },
138
139 define (options) {
140 const self = this;
141
142 if (typeof options !== 'object') {
143 return console.warn('Oxe.component.define - invalid argument type');
144 }
145
146 if (options.constructor === Array) {
147
148 for (let i = 0, l = options.length; i < l; i++) {
149 self.define(options[i]);
150 }
151
152 return;
153 }
154
155 if (!options.name) {
156 return console.warn('Oxe.component.define - requires name');
157 }
158
159 options.name = options.name.toLowerCase();
160
161 if (options.name in self.data) {
162 console.log(options.name);
163 return console.warn('Oxe.component.define - component defined');
164 }
165
166 self.data[options.name] = options;
167
168 options.count = 0;
169 options.model = options.model || {};
170 options.adopt = options.adopt || false;
171 options.methods = options.methods || {};
172 options.shadow = options.shadow || false;
173 options.attributes = options.attributes || [];
174 options.properties = options.properties || {};
175
176 if (options.style) {
177 options.style = this.style(options.style, options.name);
178 Style.append(options.style);
179 }
180
181 if (options.template && typeof options.template === 'string') {
182 const data = document.createElement('div');
183 data.innerHTML = options.template;
184 options.template = data;
185 }
186
187 const constructor = function () {
188 this._created = false;
189 this._scope = options.name + '-' + options.count++;
190
191 // Object.defineProperties(this, {
192 // created: {
193 // value: false,
194 // enumerable: true,
195 // configurable: true
196 // },
197 // scope: {
198 // enumerable: true,
199 // value: scope
200 // }
201 // });
202
203 const properties = Utility.clone(options.properties);
204 const methods = Utility.clone(options.methods);
205 const model = Utility.clone(options.model);
206
207 Object.defineProperties(this, properties);
208 Methods.set(this.scope, methods);
209 Model.set(this.scope, model);
210 };
211
212 Object.defineProperties(constructor.prototype, {
213 created: {
214 get () { return this._created; }
215 },
216 scope: {
217 get () { return this._scope; }
218 },
219 methods: {
220 get () { return Methods.get(this.scope); }
221 },
222 model: {
223 get () { return Model.get(this.scope); },
224 set (data) {
225 return Model.set(this.scope, data && typeof data === 'object' ? data : {});
226 }
227 },
228 observedAttributes: {
229 value: options.attributes
230 },
231 attributeChangedCallback: {
232 value () {
233 if (options.attributed) options.attributed.apply(this, arguments);
234 }
235 },
236 adoptedCallback: {
237 value () {
238 if (options.adopted) options.adopted.apply(this, arguments);
239 }
240 },
241 disconnectedCallback: {
242 value () {
243 if (options.detached) options.detached.call(this);
244 }
245 },
246 connectedCallback: {
247 value () {
248 const instance = this;
249
250 if (instance.created) {
251 if (options.attached) {
252 options.attached.call(instance);
253 }
254 } else {
255 instance._created = true;
256
257 self.render(instance, options.template, options.adopt, options.shadow);
258
259 Promise.resolve().then(function () {
260 if (options.created) {
261 return options.created.call(instance);
262 }
263 }).then(function () {
264 if (options.attached) {
265 return options.attached.call(instance);
266 }
267 });
268
269 }
270 }
271 }
272 });
273
274 Definer.define(options.name, constructor);
275 }
276
277};