UNPKG

8.23 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 options.properties.created = {
188 get () { return this._created; }
189 };
190
191 options.properties.scope = {
192 get () { return this._scope; }
193 };
194
195 options.properties.methods = {
196 get () { return Methods.get(this.scope); }
197 };
198
199 options.properties.model = {
200 get () { return Model.get(this.scope); },
201 set (data) {
202 return Model.set(this.scope, data && typeof data === 'object' ? data : {});
203 }
204 };
205
206 options.properties.observedAttributes = {
207 value: options.attributes
208 };
209
210 options.properties.attributeChangedCallback = {
211 value () {
212 if (options.attributed) options.attributed.apply(this, arguments);
213 }
214 };
215
216 options.properties.adoptedCallback = {
217 value () {
218 if (options.adopted) options.adopted.apply(this, arguments);
219 }
220 };
221
222 options.properties.disconnectedCallback = {
223 value () {
224 if (options.detached) options.detached.call(this);
225 }
226 };
227
228 options.properties.connectedCallback = {
229 value () {
230 const instance = this;
231
232 if (instance.created) {
233 if (options.attached) {
234 options.attached.call(instance);
235 }
236 } else {
237 instance._created = true;
238
239 self.render(instance, options.template, options.adopt, options.shadow);
240
241 Promise.resolve().then(function () {
242 if (options.created) {
243 return options.created.call(instance);
244 }
245 }).then(function () {
246 if (options.attached) {
247 return options.attached.call(instance);
248 }
249 });
250
251 }
252 }
253 };
254
255 const constructor = function () {
256 this._created = false;
257 this._scope = options.name + '-' + options.count++;
258
259 // Object.defineProperties(this, {
260 // created: {
261 // value: false,
262 // enumerable: true,
263 // configurable: true
264 // },
265 // scope: {
266 // enumerable: true,
267 // value: scope
268 // }
269 // });
270
271 const methods = Utility.clone(options.methods);
272 const model = Utility.clone(options.model);
273
274 Methods.set(this.scope, methods);
275 Model.set(this.scope, model);
276 };
277
278 Object.defineProperties(constructor.prototype, options.properties);
279 Definer.define(options.name, constructor);
280 }
281
282};