1 |
|
2 |
|
3 |
|
4 | 'use strict';
|
5 |
|
6 | const Emitter = require('events').EventEmitter;
|
7 | const inherits = require('inherits');
|
8 | const extend = require('just-extend');
|
9 | const css = require('dom-css');
|
10 | const uid = require('get-uid');
|
11 | const fs = require('fs');
|
12 | const insertCss = require('insert-styles');
|
13 | const isPlainObject = require('is-plain-obj');
|
14 | const format = require('param-case');
|
15 | const px = require('add-px-to-style');
|
16 | const scopeCss = require('scope-css');
|
17 |
|
18 | module.exports = Panel
|
19 |
|
20 |
|
21 | insertCss(fs.readFileSync(__dirname + '/index.css', 'utf-8'));
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 | function Panel (items, opts) {
|
28 | if (!(this instanceof Panel)) return new Panel(items, opts)
|
29 |
|
30 | extend(this, opts);
|
31 |
|
32 |
|
33 | if (this.container === undefined) this.container = document.body || document.documentElement;
|
34 |
|
35 | this.container.classList.add('settings-panel-container');
|
36 |
|
37 |
|
38 | if (!this.id) this.id = uid();
|
39 | this.element = document.createElement('div')
|
40 | this.element.className = 'settings-panel settings-panel-' + this.id;
|
41 | if (this.className) this.element.className += ' ' + this.className;
|
42 |
|
43 |
|
44 | if (this.title) {
|
45 | this.titleEl = this.element.appendChild(document.createElement('h2'));
|
46 | this.titleEl.className = 'settings-panel-title';
|
47 | }
|
48 |
|
49 |
|
50 | if (this.collapsible && this.title) {
|
51 |
|
52 |
|
53 | this.element.classList.add('settings-panel--collapsible');
|
54 | this.titleEl.addEventListener('click', () => {
|
55 | if (this.collapsed) {
|
56 | this.collapsed = false;
|
57 | this.element.classList.remove('settings-panel--collapsed');
|
58 | }
|
59 | else {
|
60 | this.collapsed = true;
|
61 | this.element.classList.add('settings-panel--collapsed');
|
62 | }
|
63 | });
|
64 | }
|
65 |
|
66 |
|
67 | this.state = {};
|
68 |
|
69 |
|
70 | this.items = {};
|
71 |
|
72 |
|
73 | this.set(items);
|
74 |
|
75 | if (this.container) {
|
76 | this.container.appendChild(this.element)
|
77 | }
|
78 |
|
79 |
|
80 | this.update();
|
81 | }
|
82 |
|
83 | inherits(Panel, Emitter);
|
84 |
|
85 |
|
86 |
|
87 |
|
88 |
|
89 | Panel.prototype.set = function (name, value) {
|
90 |
|
91 | if (Array.isArray(name)) {
|
92 | let items = name;
|
93 | items.forEach((item) => {
|
94 | this.set(item.id || item.label, item);
|
95 | });
|
96 |
|
97 | return this;
|
98 | }
|
99 |
|
100 |
|
101 | if (isPlainObject(name)) {
|
102 | let items = name;
|
103 | let list = [];
|
104 | for (let key in items) {
|
105 | if (!isPlainObject(items[key])) {
|
106 | items[key] = {value: items[key]};
|
107 | }
|
108 | if (items[key].id == null) items[key].id = key;
|
109 | list.push(items[key]);
|
110 | }
|
111 | list = list.sort((a, b) => (a.order||0) - (b.order||0));
|
112 |
|
113 | return this.set(list);
|
114 | }
|
115 |
|
116 |
|
117 | name = name || '';
|
118 | name = name.replace(/\-/g,'dash-');
|
119 | name = format(name);
|
120 |
|
121 | if (name) {
|
122 | var item = this.items[name];
|
123 | if (!item) item = this.items[name] = { id: name, panel: this };
|
124 | }
|
125 |
|
126 | else {
|
127 | var item = {id: null, panel: this};
|
128 | }
|
129 |
|
130 | var initialValue = item.value;
|
131 | var isBefore = item.before;
|
132 | var isAfter = item.after;
|
133 |
|
134 | if (isPlainObject(value)) {
|
135 | item = extend(item, value);
|
136 | }
|
137 | else {
|
138 |
|
139 | if (value === item.value && value !== undefined) return this;
|
140 | item.value = value;
|
141 | }
|
142 |
|
143 | if (item.value === undefined) item.value = item.default;
|
144 |
|
145 | if (name) this.state[name] = item.value;
|
146 |
|
147 |
|
148 | if (item.label === undefined && item.id) {
|
149 | item.label = item.id;
|
150 | }
|
151 |
|
152 |
|
153 | if (!item.type) {
|
154 | if (item.value && Array.isArray(item.value)) {
|
155 | if (typeof item.value[0] === 'string') {
|
156 | item.type = 'checkbox';
|
157 | }
|
158 | else {
|
159 | item.type = 'interval'
|
160 | }
|
161 | } else if (item.scale || item.max || item.steps || item.step || typeof item.value === 'number') {
|
162 | item.type = 'range'
|
163 | } else if (item.options) {
|
164 | if (Array.isArray(item.options) && item.options.join('').length < 90 ) {
|
165 | item.type = 'switch'
|
166 | }
|
167 | else {
|
168 | item.type = 'select'
|
169 | }
|
170 | } else if (item.format) {
|
171 | item.type = 'color'
|
172 | } else if (typeof item.value === 'boolean') {
|
173 | item.type = 'checkbox'
|
174 | } else if (item.content != null) {
|
175 | item.type = 'raw'
|
176 | } else {
|
177 | if (item.value && (item.value.length > 140 || /\n/.test(item.value))) {
|
178 | item.type = 'textarea'
|
179 | }
|
180 | else {
|
181 | item.type = 'text'
|
182 | }
|
183 | }
|
184 | }
|
185 |
|
186 | var field, fieldId;
|
187 |
|
188 | if (item.id != null) {
|
189 | fieldId = 'settings-panel-field-' + item.id;
|
190 | field = this.element.querySelector('#' + fieldId);
|
191 | }
|
192 |
|
193 |
|
194 | if (!field) {
|
195 | field = document.createElement('div');
|
196 | if (fieldId != null) field.id = fieldId;
|
197 | this.element.appendChild(field);
|
198 | item.field = field;
|
199 | }
|
200 | else {
|
201 |
|
202 | if (isBefore) {
|
203 | this.element.removeChild(field.prevSibling);
|
204 | }
|
205 | if (isAfter) {
|
206 | this.element.removeChild(field.nextSibling);
|
207 | }
|
208 | }
|
209 |
|
210 | field.className = 'settings-panel-field settings-panel-field--' + item.type;
|
211 |
|
212 | if (item.orientation) field.className += ' settings-panel-orientation-' + item.orientation;
|
213 |
|
214 | if (item.className) field.className += ' ' + item.className;
|
215 |
|
216 | if (item.style) {
|
217 | if (isPlainObject(item.style)) {
|
218 | css(field, item.style);
|
219 | }
|
220 | else if (typeof item.style === 'string') {
|
221 | field.style.cssText = item.style;
|
222 | }
|
223 | }
|
224 | else if (item.style !== undefined) {
|
225 | field.style = null;
|
226 | }
|
227 |
|
228 | if (item.hidden) {
|
229 | field.setAttribute('hidden', true);
|
230 | }
|
231 | else {
|
232 | field.removeAttribute('hidden');
|
233 | }
|
234 |
|
235 |
|
236 | let inputContainer = field.querySelector('.settings-panel-input');
|
237 |
|
238 | if (!inputContainer) {
|
239 | inputContainer = document.createElement('div');
|
240 | inputContainer.className = 'settings-panel-input';
|
241 | item.container = inputContainer;
|
242 | field.appendChild(inputContainer);
|
243 | }
|
244 |
|
245 | if (item.disabled) field.className += ' settings-panel-field--disabled';
|
246 |
|
247 | let components = this.components;
|
248 | let component = item.component;
|
249 |
|
250 | if (!component) {
|
251 | item.component = component = (components[item.type] || components.text)(item);
|
252 |
|
253 | if (component.on) {
|
254 | component.on('init', (data) => {
|
255 | item.value = data
|
256 | if (item.id) this.state[item.id] = item.value;
|
257 | let state = extend({}, this.state);
|
258 |
|
259 | item.init && item.init(data, state)
|
260 | this.emit('init', item.id, data, state)
|
261 | item.change && item.change(data, state)
|
262 | this.emit('change', item.id, data, state)
|
263 | });
|
264 |
|
265 | component.on('input', (data) => {
|
266 | item.value = data
|
267 | if (item.id) this.state[item.id] = item.value;
|
268 | let state = extend({}, this.state);
|
269 |
|
270 | item.input && item.input(data, state)
|
271 | this.emit('input', item.id, data, state)
|
272 | item.change && item.change(data, state)
|
273 | this.emit('change', item.id, data, state)
|
274 | });
|
275 |
|
276 | component.on('change', (data) => {
|
277 | item.value = data
|
278 | if (item.id) this.state[item.id] = item.value;
|
279 | let state = extend({}, this.state);
|
280 |
|
281 | item.change && item.change(data, state)
|
282 | this.emit('change', item.id, data, state)
|
283 | });
|
284 | }
|
285 | }
|
286 | else {
|
287 | component.update(item);
|
288 | }
|
289 |
|
290 |
|
291 | if (component.label !== false && (item.label || item.label === '')) {
|
292 | let label = field.querySelector('.settings-panel-label');
|
293 | if (!label) {
|
294 | label = document.createElement('label')
|
295 | label.className = 'settings-panel-label';
|
296 | field.insertBefore(label, inputContainer);
|
297 | }
|
298 |
|
299 | label.htmlFor = item.id;
|
300 | label.innerHTML = item.label;
|
301 | label.title = item.title || item.label;
|
302 | }
|
303 |
|
304 |
|
305 |
|
306 |
|
307 |
|
308 |
|
309 |
|
310 |
|
311 |
|
312 |
|
313 |
|
314 |
|
315 |
|
316 |
|
317 |
|
318 |
|
319 |
|
320 |
|
321 |
|
322 |
|
323 |
|
324 |
|
325 |
|
326 |
|
327 |
|
328 |
|
329 |
|
330 |
|
331 | if (initialValue !== item.value) {
|
332 | this.emit('change', item.id, item.value, this.state)
|
333 | }
|
334 |
|
335 | return this;
|
336 | }
|
337 |
|
338 |
|
339 |
|
340 |
|
341 |
|
342 | Panel.prototype.get = function (name) {
|
343 | if (name == null) return this.state;
|
344 | return this.state[name];
|
345 | }
|
346 |
|
347 |
|
348 |
|
349 |
|
350 |
|
351 | Panel.prototype.update = function (opts) {
|
352 | extend(this, opts);
|
353 |
|
354 |
|
355 |
|
356 |
|
357 |
|
358 |
|
359 |
|
360 |
|
361 |
|
362 |
|
363 |
|
364 | if (this.titleEl) this.titleEl.innerHTML = this.title;
|
365 |
|
366 |
|
367 | this.element.classList.remove('settings-panel-orientation-top');
|
368 | this.element.classList.remove('settings-panel-orientation-bottom');
|
369 | this.element.classList.remove('settings-panel-orientation-left');
|
370 | this.element.classList.remove('settings-panel-orientation-right');
|
371 | this.element.classList.add('settings-panel-orientation-' + this.orientation);
|
372 |
|
373 |
|
374 | let cssStr = '';
|
375 | if (this.theme instanceof Function) {
|
376 | cssStr = this.theme.call(this, this);
|
377 | }
|
378 | else if (typeof this.theme === 'string') {
|
379 | cssStr = this.theme;
|
380 | }
|
381 |
|
382 |
|
383 | if (this.css) {
|
384 | if (this.css instanceof Function) {
|
385 | cssStr += this.css.call(this, this);
|
386 | }
|
387 | else if (typeof this.css === 'string') {
|
388 | cssStr += this.css;
|
389 | }
|
390 | }
|
391 |
|
392 |
|
393 | cssStr = scopeCss(cssStr || '', '.settings-panel-' + this.id) || '';
|
394 |
|
395 | insertCss(cssStr.trim(), {
|
396 | id: this.id
|
397 | });
|
398 |
|
399 | if (this.style) {
|
400 | if (isPlainObject(this.style)) {
|
401 | css(this.element, this.style);
|
402 | }
|
403 | else if (typeof this.style === 'string') {
|
404 | this.element.style.cssText = this.style;
|
405 | }
|
406 | }
|
407 | else if (this.style !== undefined) {
|
408 | this.element.style = null;
|
409 | }
|
410 |
|
411 | return this;
|
412 | }
|
413 |
|
414 |
|
415 | Panel.prototype.theme = require('./theme/none');
|
416 |
|
417 |
|
418 |
|
419 |
|
420 | Panel.prototype.components = {
|
421 | range: require('./src/range'),
|
422 |
|
423 | button: require('./src/button'),
|
424 | text: require('./src/text'),
|
425 | textarea: require('./src/textarea'),
|
426 |
|
427 | checkbox: require('./src/checkbox'),
|
428 | toggle: require('./src/checkbox'),
|
429 |
|
430 | switch: require('./src/switch'),
|
431 |
|
432 | color: require('./src/color'),
|
433 |
|
434 | interval: require('./src/interval'),
|
435 | multirange: require('./src/interval'),
|
436 |
|
437 | custom: require('./src/custom'),
|
438 | raw: require('./src/custom'),
|
439 |
|
440 | select: require('./src/select')
|
441 | };
|
442 |
|
443 |
|
444 |
|
445 |
|
446 |
|
447 | Panel.prototype.className;
|
448 |
|
449 |
|
450 |
|
451 |
|
452 |
|
453 | Panel.prototype.orientation = 'left';
|
454 |
|
455 |
|
456 |
|
457 | Panel.prototype.collapsible = false; |
\ | No newline at end of file |