1 | import Methods from './methods.js';
|
2 | import Binder from './binder.js';
|
3 | import Loader from './loader.js';
|
4 | import Model from './model.js';
|
5 | import Style from './style.js';
|
6 | import Utility from './utility.js';
|
7 | import Definer from './definer.js';
|
8 |
|
9 | export 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 |
|
192 |
|
193 |
|
194 |
|
195 |
|
196 |
|
197 |
|
198 |
|
199 |
|
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 | };
|