├── .gitignore
├── .npmignore
├── LICENSE
├── README.md
├── contributors.md
├── index.css
├── index.js
├── package.json
├── preview.png
├── qa.md
├── src
├── button.js
├── checkbox.js
├── color.js
├── custom.js
├── interval.js
├── range.js
├── select.js
├── switch.js
├── text.js
├── textarea.js
└── value.js
├── test.js
└── theme
├── control.js
├── dat.js
├── dragon.js
├── flat.js
├── json.js
├── lucy.js
├── merka.js
├── none.js
├── pages.js
└── typer.js
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 |
6 | # Runtime data
7 | pids
8 | *.pid
9 | *.seed
10 |
11 | # Directory for instrumented libs generated by jscoverage/JSCover
12 | lib-cov
13 |
14 | # Coverage directory used by tools like istanbul
15 | coverage
16 |
17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
18 | .grunt
19 |
20 | # node-waf configuration
21 | .lock-wscript
22 |
23 | # Compiled binary addons (http://nodejs.org/api/addons.html)
24 | build/Release
25 |
26 | # Dependency directory
27 | node_modules
28 |
29 | # Optional npm cache directory
30 | .npm
31 |
32 | # Optional REPL history
33 | .node_repl_history
34 |
35 | bundle.js
36 | demo
37 |
38 | images
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | test.js
2 | *.jpg
3 | *.gif
4 | demo
5 | *.png
6 | *.md
7 | images
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Jeremy Freeman
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # settings-panel [](http://github.com/badges/stability-badges)
2 |
3 | Simple settings panel for your app, demo or tests.
4 |
5 | [](http://dy.github.io/settings-panel/)
6 |
7 | In the preview there is a _typer_ theme, for other themes or customizations see [demo](http://dy.github.io/settings-panel/).
8 |
9 | ## Usage
10 |
11 | [](https://npmjs.org/package/settings-panel/)
12 |
13 | ```javascript
14 | var createPanel = require('settings-panel')
15 |
16 | var panel = createPanel([
17 | {type: 'range', label: 'my range', min: 0, max: 100, value: 20},
18 | {type: 'range', label: 'log range', min: 0.1, max: 100, value: 20, scale: 'log'},
19 | {type: 'text', label: 'my text', value: 'my cool setting', help: 'why this is cool'},
20 | {type: 'checkbox', label: 'my checkbox', value: true},
21 | {type: 'color', label: 'my color', format: 'rgb', value: 'rgb(10,200,0)', change: value => console.log(value)},
22 | {type: 'button', label: 'gimme an alert', change: () => alert('hello!')},
23 | {type: 'select', label: 'select one', options: ['option 1', 'option 2'], value: 'option 1'}
24 | ],
25 | {
26 | title: 'Settings',
27 | style: 'position: absolute; right: 0; z-index: 1'
28 | }
29 | );
30 | ```
31 |
32 | [**Run this in requirebin**](http://requirebin.com/?gist=21fc39f7f206ca50a4d5cd7298f8b9f8)
33 |
34 | ## API
35 |
36 | `const Panel = require('settings-panel')`
37 | **`let panel = new Panel(fields, options?)`**
38 |
39 | The first argument is a list of fields or object with id/field pairs. Each field may have following properties:
40 |
41 | * `type` one of `range` • `interval` • `checkbox` • `color` • `select` • `switch` • `raw` • `textarea` • `text` or any ` ` type. If undefined, type will be detected from the value.
42 | * `id` used as key to identify the field. If undefined, the label will be used instead.
43 | * `label` label for the input. If label is false, it will be hidden.
44 | * `value` current value of the field.
45 | * `default` explicitly defines default value, if differs from the initial value.
46 | * `orientation` defines position of a label relative to the input, one of `top`, `left`, `right`, `bottom`. Redefines `options.orientation`.
47 | * `style` appends additinal style to the field, can be a css object or css string.
48 | * `hidden` defines whether field should be visually hidden, but present as a value.
49 | * `disabled` just disables the input, making it inactive.
50 | * `input` callback, invoked if value changed.
51 | * `init` invoked once component is set up.
52 | * `change` invoked each time the field value changed, whether through `input` or API.
53 | * `before` and `after` define an html to display before or after the element, can be a string, an element or a function returning one of the two. That may come handy in displaying help, info or validation messages, separators, additional buttons, range limits etc - anything related to the element.
54 | * `title` will display text in tooltip.
55 |
56 | For example,
57 |
58 | ```javascript
59 | {type: 'checkbox', label: 'My Checkbox', value: true, input: value => {}}
60 | ```
61 |
62 | Some types have additional properties:
63 |
64 | - `range` can specify a `min`, `max`, and `step` (or integer `steps`). Scale can be either `'linear'` (default) or `'log'`. If a log scale, the sign of `min`, `max`, and `value` must be the same and only `steps` is permitted (since the step size is not constant on a log scale). It also takes `precision` optional parameter for the displayed value.
65 | - `interval` obeys the same semantics as `range` inputs, except the input and ouput is a two-element array corresponding to the low/high bounds, e.g. `value: [1, 7.5]`.
66 | - `color` can specify a `format` as either `rgb` • `hex` • `array`
67 | - `select`, `switch` and `checkbox` can specify `options`, either as an `Array` (in which case the value is the same as the option text) or as an object containing key/value pairs (in which case the key/value pair maps to value value/label pairs).
68 | - `text` and `textarea` can specify `placeholder`.
69 | - `raw` can define `content` method, returning HTML string, element or documentFragment.
70 |
71 | #### options
72 |
73 | ```js
74 | // element to which to append the panel
75 | container: document.body,
76 |
77 | // a title to add to the top of the panel
78 | title: 'Settings',
79 |
80 | // specifies label position relative to the input: `top` • `left` • `bottom` • `right`
81 | orientation: 'left',
82 |
83 | // collapse by clicking on title
84 | collapsible: false,
85 |
86 | // use a theme, see `theme` folder.
87 | // available themes: typer, flat, control, dragon
88 | theme: require('settings-panel/theme/none'),
89 |
90 | //theme customization, can redefine theme defaults
91 | palette: ['black', 'white'],
92 | labelWidth: '9em',
93 | inputHeight: '1.6em',
94 | fontFamily: 'sans-serif',
95 | fontSize: 13,
96 |
97 | //additional css, aside from the theme’s one. Useful for custom styling
98 | css: '',
99 |
100 | //appends additional className to the panel element.
101 | className: ''
102 | ```
103 |
104 |
105 | **`panel.on(event, callback)`**
106 |
107 | Attach callback to `change`, `input` or `init` event.
108 |
109 | The callback will recieve `name`, `data` and `state` arguments:
110 |
111 | ```javascript
112 | panel.on('change', (name, value, state) => {
113 | // name === 'my checkbox'
114 | // value === false
115 | // state === {'my checkbox': false, 'my range': 75, ...}
116 | });
117 | ```
118 |
119 |
120 | **`panel.get(name?)`**
121 |
122 | Get the value of a field defined by `name`. Or get full list of values, if `name` is undefined.
123 |
124 |
125 | **`panel.set(name, value|options)`**
126 |
127 | Update specific field, with value or field options. You can also pass an object or array to update multiple fields:
128 |
129 | ```js
130 | panel.set({ 'my range': { min: -100, value: 200}, 'my color': '#fff' });
131 | ```
132 |
133 |
134 | **`panel.update(options?)`**
135 |
136 | Rerender panel with new options. Options may include values for the theme, like `palette`, `fontSize`, `fontFamily`, `labelWidth`, `padding` etc, see specific theme file for possible options.
137 |
138 |
139 |
140 | ## Spotted in the wild
141 |
142 | > [plot-grid](https://dy.github.io/plot-grid)
143 | > [app-audio](https://dy.github.io/app-audio)
144 | > [gl-waveform](https://dy.github.io/gl-waveform)
145 |
146 | ## See also
147 |
148 | > [control-panel](https://github.com/freeman-lab/control-panel) — original forked settings panel.
149 | > [oui](https://github.com/wearekuva/oui) — sci-ish panel.
150 | > [dat.gui](https://github.com/dataarts/dat.gui) — other oldschool settings panel.
151 | > [quicksettings](https://github.com/bit101/quicksettings) — an alternative versatile settings panel.
152 | > [dis-gui](https://github.com/wwwtyro/dis-gui) — remake on dat.gui.
153 |
--------------------------------------------------------------------------------
/contributors.md:
--------------------------------------------------------------------------------
1 | # collaborators
2 |
3 | `settings-panel` is only possible due to the excellent work of the following collaborators:
4 |
5 |
--------------------------------------------------------------------------------
/index.css:
--------------------------------------------------------------------------------
1 | .settings-panel {
2 | position: relative;
3 | -webkit-user-select: none;
4 | -moz-user-select: none;
5 | -ms-user-select: none;
6 | user-select: none;
7 | cursor: default;
8 | text-align: left;
9 | box-sizing: border-box;
10 | font-family: sans-serif;
11 | font-size: 1rem;
12 | width: 32em;
13 | max-width: 100%;
14 | padding: 1em;
15 | }
16 |
17 | .settings-panel [hidden] {
18 | display: none!important;
19 | }
20 |
21 | .settings-panel * {
22 | box-sizing: border-box;
23 | }
24 |
25 | .settings-panel svg {
26 | fill: currentColor;
27 | max-width: 100%;
28 | max-height: 100%;
29 | display: inline-block;
30 | }
31 |
32 | .settings-panel input,
33 | .settings-panel button,
34 | .settings-panel textarea,
35 | .settings-panel select {
36 | font-family: inherit;
37 | font-size: inherit;
38 | }
39 |
40 | .settings-panel textarea {
41 | max-height: 8em;
42 | }
43 |
44 |
45 | .settings-panel a {
46 | color: inherit;
47 | }
48 |
49 | /** Basic layout */
50 | .settings-panel-field {
51 | position: relative;
52 | padding: .25em;
53 | display: table;
54 | width: 100%;
55 | }
56 | .settings-panel-field:last-child {
57 | margin-bottom: 0;
58 | }
59 | .settings-panel-label {
60 | left: 0;
61 | display: table-cell;
62 | line-height: 1.2;
63 | vertical-align: baseline;
64 | padding-top: 0;
65 | max-width: 100%;
66 | }
67 | .settings-panel-input {
68 | display: table-cell;
69 | vertical-align: baseline;
70 | position: relative;
71 | white-space: nowrap;
72 | }
73 |
74 | .settings-panel-orientation-left .settings-panel-label {
75 | width: 9em;
76 | padding-right: .5em;
77 | }
78 | .settings-panel-orientation-right .settings-panel-label {
79 | display: block;
80 | margin-right: 0;
81 | float: right;
82 | width: 9em;
83 | padding-top: .4em;
84 | padding-left: .5em;
85 | }
86 | .settings-panel-orientation-right .settings-panel-label + .settings-panel-input {
87 | display: block;
88 | width: calc(100% - 9em);
89 | }
90 | .settings-panel-orientation-top .settings-panel-label {
91 | display: block;
92 | width: 100%;
93 | margin-right: 0;
94 | padding-top: 0;
95 | line-height: 1.5;
96 | }
97 | .settings-panel-orientation-top .settings-panel-label + .settings-panel-input {
98 | display: block;
99 | width: 100%;
100 | padding: 0;
101 | }
102 | .settings-panel-orientation-bottom .settings-panel-label {
103 | display: block;
104 | width: 100%;
105 | margin-right: 0;
106 | padding: 0;
107 | line-height: 1.5;
108 | border-top: 2.5em solid transparent;
109 | }
110 | .settings-panel-orientation-bottom .settings-panel-label + .settings-panel-input {
111 | width: 100%;
112 | position: absolute;
113 | top: 0;
114 | }
115 |
116 | .settings-panel-orientation-left > .settings-panel-label {
117 | width: 9em;
118 | display: table-cell;
119 | }
120 |
121 | .settings-panel-title {
122 | font-size: 1.6em;
123 | line-height: 1.25;
124 | margin-top: 0;
125 | margin-bottom: 0;
126 | padding: .25em .25em;
127 | text-align: center;
128 | }
129 | .settings-panel--collapsible .settings-panel-title {
130 | cursor: pointer;
131 | }
132 | .settings-panel--collapsed > *:not(.settings-panel-title) {
133 | display: none!important;
134 | }
135 |
136 |
137 | /** Button */
138 | .settings-panel-field--button {
139 | display: inline-block;
140 | }
141 | .settings-panel-field--button .settings-panel-input {
142 | display: block;
143 | text-align: center;
144 | }
145 | .settings-panel-button {
146 | vertical-align: baseline;
147 | line-height: 1;
148 | min-height: 2em;
149 | padding: .2em 1em;
150 | width: 100%;
151 | cursor: pointer;
152 | }
153 |
154 |
155 | /** Default text and alike style */
156 | .settings-panel-text {
157 | height: 2em;
158 | width: 100%;
159 | vertical-align: baseline;
160 | }
161 | .settings-panel-textarea {
162 | width: 100%;
163 | display: block;
164 | vertical-align: top; /* allowable as we use autoheight */
165 | min-height: 2em;
166 | }
167 |
168 | /** Checkbox style */
169 | .settings-panel-field--checkbox .settings-panel-input {
170 | line-height: 2em;
171 | }
172 | .settings-panel-checkbox-group {
173 | border: none;
174 | -webkit-appearance: none;
175 | -moz-appearance: none;
176 | -o-appearance: none;
177 | appearance: none;
178 | margin: 0;
179 | padding: 0;
180 | white-space: normal;
181 | }
182 | .settings-panel-checkbox {
183 | display: inline-block;
184 | vertical-align: middle;
185 | width: 1.2em;
186 | height: 1.2em;
187 | line-height: 1.2em;
188 | margin: -.15em .25em 0 0;
189 | }
190 | .settings-panel-checkbox-label {
191 | display: inline-block;
192 | vertical-align: baseline;
193 | -webkit-user-select: none;
194 | -moz-user-select: none;
195 | -ms-user-select: none;
196 | user-select: none;
197 | line-height: 1.2;
198 | margin-right: 1em;
199 | }
200 | .settings-panel-checkbox-group .settings-panel-checkbox-label:last-child {
201 | margin-right: 0;
202 | }
203 |
204 |
205 | /** Color picker style */
206 | .settings-panel-color {
207 | position: relative;
208 | width: 2em;
209 | height: 2em;
210 | position: absolute;
211 | top: 0;
212 | bottom: 0;
213 | margin: auto;
214 | }
215 | .settings-panel-color-value {
216 | width: 100%;
217 | height: 2em;
218 | padding: 0 0 0 2.5em;
219 | }
220 | .settings-panel .Scp {
221 | -webkit-user-select: none;
222 | -moz-user-select: none;
223 | -ms-user-select: none;
224 | user-select: none;
225 | position: absolute;
226 | z-index: 10;
227 | cursor: pointer;
228 | bottom: -120px;
229 | }
230 | .settings-panel .Scp-saturation {
231 | position: relative;
232 | width: calc(100% - 25px);
233 | height: 100%;
234 | background: linear-gradient(to right, #fff 0%, #f00 100%);
235 | float: left;
236 | }
237 | .settings-panel .Scp-brightness {
238 | width: 100%;
239 | height: 100%;
240 | background: linear-gradient(to top, #000 0%, rgba(255,255,255,0) 100%);
241 | }
242 | .settings-panel .Scp-sbSelector {
243 | border: 1px solid;
244 | position: absolute;
245 | width: 14px;
246 | height: 14px;
247 | background: #fff;
248 | border-radius: 10px;
249 | top: -7px;
250 | left: -7px;
251 | box-sizing: border-box;
252 | z-index: 10;
253 | }
254 | .settings-panel .Scp-hue {
255 | width: 20px;
256 | height: 100%;
257 | position: relative;
258 | float: left;
259 | background: linear-gradient(to bottom, #f00 0%, #f0f 17%, #00f 34%, #0ff 50%, #0f0 67%, #ff0 84%, #f00 100%);
260 | }
261 | .settings-panel .Scp-hSelector {
262 | position: absolute;
263 | background: #fff;
264 | border-bottom: 1px solid #000;
265 | right: -3px;
266 | width: 10px;
267 | height: 2px;
268 | }
269 |
270 |
271 |
272 | /** Interval style */
273 | .settings-panel-interval {
274 | position: relative;
275 | -webkit-appearance: none;
276 | display: inline-block;
277 | vertical-align: top;
278 | height: 2em;
279 | margin: 0px 0;
280 | width: 70%;
281 | background: #ddd;
282 | cursor: ew-resize;
283 | -webkit-touch-callout: none;
284 | -webkit-user-select: none;
285 | -khtml-user-select: none;
286 | -moz-user-select: none;
287 | -ms-user-select: none;
288 | user-select: none;
289 | }
290 | .settings-panel-interval-handle {
291 | background: #7a4;
292 | position: absolute;
293 | top: 0;
294 | bottom: 0;
295 | min-width: 1px;
296 | }
297 | .settings-panel.settings-panel-interval-dragging * {
298 | -webkit-touch-callout: none !important;
299 | -webkit-user-select: none !important;
300 | -khtml-user-select: none !important;
301 | -moz-user-select: none !important;
302 | -ms-user-select: none !important;
303 | user-select: none !important;
304 |
305 | cursor: ew-resize !important;
306 | }
307 |
308 | .settings-panel-interval + .settings-panel-value {
309 | right: 0;
310 | padding-left: .5em;
311 | }
312 |
313 |
314 |
315 | /** Select style */
316 | .settings-panel-select {
317 | display: inline-block;
318 | width: 100%;
319 | height: 2em;
320 | vertical-align: baseline;
321 | }
322 |
323 | /** Value style */
324 | .settings-panel-value {
325 | -webkit-appearance: none;
326 | -moz-appearance: none;
327 | -o-appearance: none;
328 | appearance: none;
329 | min-width: 3em;
330 | padding: 0 0 0 0em;
331 | display: inline-block;
332 | vertical-align: baseline;
333 | cursor: text;
334 | height: 2em;
335 | border: none;
336 | border-radius: 0;
337 | outline: none;
338 | font-family: inherit;
339 | background: none;
340 | color: inherit;
341 | width: 15%;
342 | }
343 | .settings-panel-value:focus {
344 | outline: 0;
345 | box-shadow: 0;
346 | }
347 | .settings-panel-value-tip {
348 | display: none;
349 | }
350 |
351 | /** Range style */
352 | .settings-panel-range {
353 | width: 85%;
354 | padding: 0;
355 | margin: 0px 0;
356 | height: 2em;
357 | vertical-align: top;
358 | }
359 | .settings-panel-range + .settings-panel-value {
360 | padding-left: .5em;
361 | width: 15%;
362 | }
363 |
364 | .settings-panel-switch {
365 | -webkit-appearance: none;
366 | -moz-appearance: none;
367 | appearance: none;
368 | border: none;
369 | display: block;
370 | vertical-align: baseline;
371 | padding: 0;
372 | margin: 0;
373 | line-height: 2em;
374 | }
375 | .settings-panel-switch-input {
376 | margin: 0;
377 | vertical-align: middle;
378 | width: 1.2em;
379 | height: 1.2em;
380 | cursor: pointer;
381 | margin-right: .25em;
382 | }
383 | .settings-panel-switch-label {
384 | display: inline-block;
385 | vertical-align: baseline;
386 | line-height: 1.2;
387 | margin-right: 1em;
388 | }
389 |
390 |
391 | .settings-panel hr {
392 | border: none;
393 | height: 0;
394 | margin: .5em 0;
395 | border-bottom: 1px dotted;
396 | }
397 |
398 | .settings-panel-field--disabled {
399 | opacity: .5;
400 | pointer-events: none;
401 | }
402 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @module settings-panel
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 | * @constructor
26 | */
27 | function Panel (items, opts) {
28 | if (!(this instanceof Panel)) return new Panel(items, opts)
29 |
30 | extend(this, opts);
31 |
32 | //ensure container
33 | if (this.container === undefined) this.container = document.body || document.documentElement;
34 |
35 | this.container.classList.add('settings-panel-container');
36 |
37 | //create element
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 | //create title
44 | if (this.title) {
45 | this.titleEl = this.element.appendChild(document.createElement('h2'));
46 | this.titleEl.className = 'settings-panel-title';
47 | }
48 |
49 | //create collapse button
50 | if (this.collapsible && this.title) {
51 | // this.collapseEl = this.element.appendChild(document.createElement('div'));
52 | // this.collapseEl.className = 'settings-panel-collapse';
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 | //state is values of items
67 | this.state = {};
68 |
69 | //items is all items settings
70 | this.items = {};
71 |
72 | //create fields
73 | this.set(items);
74 |
75 | if (this.container) {
76 | this.container.appendChild(this.element)
77 | }
78 |
79 | //create theme style
80 | this.update();
81 | }
82 |
83 | inherits(Panel, Emitter);
84 |
85 |
86 | /**
87 | * Set item value/options
88 | */
89 | Panel.prototype.set = function (name, value) {
90 | //handle list of properties
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 | //handle plain object
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 | //format name
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 | //noname items should not be saved in state
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 | //ignore nothing-changed set
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 | //define label via name
148 | if (item.label === undefined && item.id) {
149 | item.label = item.id;
150 | }
151 |
152 | //detect type
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 | //create field container
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 | //clean previous before/after
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 | //createe container for the input
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('action', () => {
277 | let state = extend({}, this.state);
278 | item.action && item.action(state);
279 | });
280 |
281 | component.on('change', (data) => {
282 | item.value = data
283 | if (item.id) this.state[item.id] = item.value;
284 | let state = extend({}, this.state);
285 |
286 | item.change && item.change(data, state)
287 | this.emit('change', item.id, data, state)
288 | });
289 | }
290 | }
291 | else {
292 | component.update(item);
293 | }
294 |
295 | //create field label
296 | if (component.label !== false && (item.label || item.label === '')) {
297 | let label = field.querySelector('.settings-panel-label');
298 | if (!label) {
299 | label = document.createElement('label')
300 | label.className = 'settings-panel-label';
301 | field.insertBefore(label, inputContainer);
302 | }
303 |
304 | label.htmlFor = item.id;
305 | label.innerHTML = item.label;
306 | label.title = item.title || item.label;
307 | }
308 |
309 | //handle after and before
310 | // if (item.before) {
311 | // let before = item.before;
312 | // if (before instanceof Function) {
313 | // before = item.before.call(item, component);
314 | // }
315 | // if (before instanceof HTMLElement) {
316 | // this.element.insertBefore(before, field);
317 | // }
318 | // else {
319 | // field.insertAdjacentHTML('beforebegin', before);
320 | // }
321 | // }
322 | // if (item.after) {
323 | // let after = item.after;
324 | // if (after instanceof Function) {
325 | // after = item.after.call(item, component);
326 | // }
327 | // if (after instanceof HTMLElement) {
328 | // this.element.insertBefore(after, field.nextSibling);
329 | // }
330 | // else {
331 | // field.insertAdjacentHTML('afterend', after);
332 | // }
333 | // }
334 |
335 | //emit change
336 | if (initialValue !== item.value) {
337 | this.emit('change', item.id, item.value, this.state)
338 | }
339 |
340 | return this;
341 | }
342 |
343 |
344 | /**
345 | * Return property value or a list
346 | */
347 | Panel.prototype.get = function (name) {
348 | if (name == null) return this.state;
349 | return this.state[name];
350 | }
351 |
352 |
353 | /**
354 | * Update theme
355 | */
356 | Panel.prototype.update = function (opts) {
357 | extend(this, opts);
358 |
359 | //FIXME: decide whether we have to reset these params
360 | // if (opts && opts.theme) {
361 | // if (opts.theme.fontSize) this.fontSize = opts.theme.fontSize;
362 | // if (opts.theme.inputHeight) this.inputHeight = opts.theme.inputHeight;
363 | // if (opts.theme.fontFamily) this.fontFamily = opts.theme.fontFamily;
364 | // if (opts.theme.labelWidth) this.labelWidth = opts.theme.labelWidth;
365 | // if (opts.theme.palette) this.palette = opts.theme.palette;
366 | // }
367 |
368 | //update title, if any
369 | if (this.titleEl) this.titleEl.innerHTML = this.title;
370 |
371 | //update orientation
372 | this.element.classList.remove('settings-panel-orientation-top');
373 | this.element.classList.remove('settings-panel-orientation-bottom');
374 | this.element.classList.remove('settings-panel-orientation-left');
375 | this.element.classList.remove('settings-panel-orientation-right');
376 | this.element.classList.add('settings-panel-orientation-' + this.orientation);
377 |
378 | //apply style
379 | let cssStr = '';
380 | if (this.theme instanceof Function) {
381 | cssStr = this.theme.call(this, this);
382 | }
383 | else if (typeof this.theme === 'string') {
384 | cssStr = this.theme;
385 | }
386 |
387 | //append extra css
388 | if (this.css) {
389 | if (this.css instanceof Function) {
390 | cssStr += this.css.call(this, this);
391 | }
392 | else if (typeof this.css === 'string') {
393 | cssStr += this.css;
394 | }
395 | }
396 |
397 | //scope each rule
398 | cssStr = scopeCss(cssStr || '', '.settings-panel-' + this.id) || '';
399 |
400 | insertCss(cssStr.trim(), {
401 | id: this.id
402 | });
403 |
404 | if (this.style) {
405 | if (isPlainObject(this.style)) {
406 | css(this.element, this.style);
407 | }
408 | else if (typeof this.style === 'string') {
409 | this.element.style.cssText = this.style;
410 | }
411 | }
412 | else if (this.style !== undefined) {
413 | this.element.style = null;
414 | }
415 |
416 | return this;
417 | }
418 |
419 | //instance theme
420 | Panel.prototype.theme = require('./theme/none');
421 |
422 | /**
423 | * Registered components
424 | */
425 | Panel.prototype.components = {
426 | range: require('./src/range'),
427 |
428 | button: require('./src/button'),
429 | text: require('./src/text'),
430 | textarea: require('./src/textarea'),
431 |
432 | checkbox: require('./src/checkbox'),
433 | toggle: require('./src/checkbox'),
434 |
435 | switch: require('./src/switch'),
436 |
437 | color: require('./src/color'),
438 |
439 | interval: require('./src/interval'),
440 | multirange: require('./src/interval'),
441 |
442 | custom: require('./src/custom'),
443 | raw: require('./src/custom'),
444 |
445 | select: require('./src/select')
446 | };
447 |
448 |
449 | /**
450 | * Additional class name
451 | */
452 | Panel.prototype.className;
453 |
454 |
455 | /**
456 | * Additional visual setup
457 | */
458 | Panel.prototype.orientation = 'left';
459 |
460 |
461 | /** Display collapse button */
462 | Panel.prototype.collapsible = false;
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "settings-panel",
3 | "version": "1.8.17",
4 | "description": "Panel of inputs for parameter setting",
5 | "main": "index.js",
6 | "scripts": {
7 | "build": "browserify test.js -g bubleify -o demo/index.js",
8 | "test:browser": "budo test.js -- -g bubleify"
9 | },
10 | "repository": {
11 | "type": "git",
12 | "url": "git+https://github.com/dfcreative/settings-panel.git"
13 | },
14 | "keywords": [
15 | "slider",
16 | "input",
17 | "range",
18 | "checkbox",
19 | "component",
20 | "color-picker",
21 | "xypad",
22 | "dom",
23 | "css",
24 | "settings-panel",
25 | "control-panel",
26 | "options",
27 | "form",
28 | "dat.gui",
29 | "oui",
30 | "ouioui",
31 | "datgui"
32 | ],
33 | "author": "ΔY ",
34 | "contributors": [
35 | "freeman-lab",
36 | "rreusser"
37 | ],
38 | "license": "MIT",
39 | "bugs": {
40 | "url": "https://github.com/dfcreative/settings-panel/issues"
41 | },
42 | "browserify": {
43 | "transform": [
44 | "brfs"
45 | ]
46 | },
47 | "homepage": "https://github.com/dfcreative/settings-panel#readme",
48 | "dependencies": {
49 | "add-px-to-style": "^1.0.0",
50 | "autosize": "^3.0.15",
51 | "brfs": "^1.4.3",
52 | "color-interpolate": "^1.0.1",
53 | "dom-css": "^2.0.0",
54 | "get-uid": "^1.0.1",
55 | "google-fonts": "^1.0.0",
56 | "inherits": "^2.0.1",
57 | "input-number": "^1.0.5",
58 | "insert-styles": "^1.2.1",
59 | "is-mobile": "^0.2.2",
60 | "is-numeric": "0.0.5",
61 | "is-plain-obj": "^1.1.0",
62 | "just-extend": "^1.1.18",
63 | "mumath": "^2.3.0",
64 | "param-case": "^1.1.2",
65 | "scope-css": "^1.0.0",
66 | "simple-color-picker": "0.0.9",
67 | "tinycolor2": "^1.3.0"
68 | },
69 | "devDependencies": {
70 | "bubleify": "^0.5.0",
71 | "colormap": "^2.2.0",
72 | "dom-css": "^2.0.1",
73 | "filter-obj": "^1.1.0",
74 | "nice-color-palettes": "^1.0.1",
75 | "sortablejs": "^1.4.2",
76 | "tst": "^1.3.1"
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dy/settings-panel/d973e5d88ec6f9e5c88268564425c38075eacc3a/preview.png
--------------------------------------------------------------------------------
/qa.md:
--------------------------------------------------------------------------------
1 | # Q: should we separate active color?
2 | + Makes sense for flat
3 | + Makes sense for typer
4 | + Unseful for control
5 | * Bearable for dragon
6 | * Possible to autodetect active color from palette, if undefined.
7 | + Expands palette to full range
8 | + Enables need in component enabler
9 | * funny thing is that many schemes have active color in between edges, it is contrast to all of edge values.
10 | * Also edge values seem good working for labels/title as it increase readability due to contrast, but active color highlights, it does not make things contrast.
11 | * Also too often we don’t need brightness relationship between label/title/bg, because as far bg can be transparent, we can manage to do bw scale via opacity.
12 | ✔~ Therefore initial idea of picking active color from the middle may be good. As far as we avoid `active` option and put things back to single palette.
13 |
14 | # Q: what are good theme principles?
15 | * It should look good in bw and inverse bw
16 | → don’t do bg gray, gray is property of palette, not the step. bg should allow for accents, but be on the edge, like .8, .9 etc.
17 | ? how do we do lighting? b/w or edge palette colors?
18 | * It should be able to reach maximum contrast in bw mode
19 | →
20 | * Bg, active color should be easily changeable
21 | → therefore bg and active are the opposites
22 | * It should respond to interactions: hover, focus, active etc., by highlighting active element.
23 | * It should be able to change scale gracefully, i. e. keeping select size, thumbnails, etc consistent.
24 |
25 | # Q: what is the minimal and easiest way to cover all the use-cases?
26 | * 1. Redefine properties on per-component basis, like label-position, field-style, item-style etc.
27 | * 2. type=group or type=row
28 | + grouping fields into a row
29 | + customizing background
30 | * 3. type=custom
31 | + cover titles h1..h6
32 | + cover br's
33 | * 4. type=panel, for nested panels
34 | - possible to fold into type=custom
35 | + type=custom does not make a big sense, it is rather rare exception, like canvas etc
36 | + grouping tasks
37 | + theme recustomization
38 | + label-position, field-style is solved on per-theme basis
39 | + allows for enabling inner panel
40 | * 5. per-component theme update - let components decide their look based on theme
41 | - firbids user component customization
42 |
43 | # Q: should title be a separate element, or not?
44 | * + it allows for inserting multiple titles
45 | - multiple titles wants be styles in custom way, ideally h2/h3
46 | * - is is discrepancy with control-panel
47 | * + it makes title in code visually easier to find
48 | * - title is not really a field
49 | * - subgroups are better done in subpanels
50 |
51 | # Q: what is the optimal way to organize themes?
52 | * use themes/*.js with functions, returning dynamic css based off variables
53 | * use palette instead of picked colors to allow for dynamic switching without theme change.
--------------------------------------------------------------------------------
/src/button.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const EventEmitter = require('events').EventEmitter
4 | const inherits = require('inherits')
5 |
6 | module.exports = Button
7 | inherits(Button, EventEmitter)
8 |
9 | function Button (opts) {
10 | if (!(this instanceof Button)) return new Button(opts)
11 |
12 | var input = opts.container.querySelector('.settings-panel-button');
13 | if (!input) {
14 | this.element = input = opts.container.appendChild(document.createElement('button'))
15 | input.className = 'settings-panel-button';
16 | input.addEventListener('click', (e) => {
17 | e.preventDefault();
18 | this.emit('input');
19 | this.emit('action');
20 | })
21 | }
22 |
23 | this.update(opts);
24 | }
25 |
26 | Button.prototype.update = function (opts) {
27 | this.element.innerHTML = opts.value || opts.label;
28 | return this;
29 | };
30 |
31 | Button.prototype.label = false;
--------------------------------------------------------------------------------
/src/checkbox.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const EventEmitter = require('events').EventEmitter
4 | const inherits = require('inherits')
5 | const format = require('param-case')
6 | const extend = require('just-extend');
7 |
8 | module.exports = Checkbox
9 | inherits(Checkbox, EventEmitter)
10 |
11 | function Checkbox (opts) {
12 | if (!(this instanceof Checkbox)) return new Checkbox(opts)
13 |
14 | var that = this;
15 |
16 | if (!this.group) {
17 | this.group = document.createElement('fieldset');
18 | this.group.className = 'settings-panel-checkbox-group';
19 | opts.container.appendChild(this.group);
20 | }
21 |
22 | //detect multiple options from array value
23 | if (!opts.options && Array.isArray(opts.value)) {
24 | opts.options = opts.value;
25 | }
26 |
27 | //single checkbox
28 | if (!opts.options) {
29 | let input = this.group.querySelector('.settings-panel-checkbox');
30 | let label = this.group.querySelector('.settings-panel-checkbox-label');
31 | if (!input) {
32 | this.element = input = this.group.appendChild(document.createElement('input'));
33 | input.className = 'settings-panel-checkbox';
34 | this.labelEl = label = this.group.appendChild(document.createElement('label'));
35 | this.labelEl.innerHTML = ' ';
36 | label.className = 'settings-panel-checkbox-label';
37 | input.onchange = function (data) {
38 | that.emit('input', data.target.checked)
39 | }
40 | setTimeout(function () {
41 | that.emit('init', input.checked)
42 | })
43 | }
44 | }
45 | //multiple checkboxes
46 | else {
47 | var html = '';
48 |
49 | if (Array.isArray(opts.options)) {
50 | for (let i = 0; i < opts.options.length; i++) {
51 | let option = opts.options[i]
52 | html += createOption(option, option);
53 | }
54 | } else {
55 | for (let key in opts.options) {
56 | html += createOption(opts.options[key], key);
57 | }
58 | }
59 |
60 | this.group.innerHTML = html;
61 |
62 | this.group.addEventListener('change', () => {
63 | this.emit('input', getState());
64 | });
65 | setTimeout(() => {
66 | this.emit('init', getState());
67 | });
68 | }
69 |
70 | function getState () {
71 | let v = [];
72 | [].slice.call(that.group.querySelectorAll('.settings-panel-checkbox')).forEach(el => {
73 | if (el.checked) v.push(el.getAttribute('data-value'));
74 | });
75 | return v;
76 | }
77 |
78 | function createOption (label, value) {
79 | let htmlFor = `settings-panel-${format(opts.panel.id)}-${format(opts.id)}-input-${format(value)}`;
80 |
81 | let html = `${label} `;
82 | return html;
83 | }
84 |
85 | this.update(opts);
86 | }
87 |
88 | Checkbox.prototype.update = function (opts) {
89 | extend(this, opts);
90 |
91 | if (!this.options) {
92 | this.labelEl.htmlFor = this.id
93 | this.element.id = this.id
94 | this.element.type = 'checkbox';
95 | this.element.checked = !!this.value;
96 | }
97 | else {
98 | if (!Array.isArray(this.value)) this.value = [this.value];
99 | let els = [].slice.call(this.group.querySelectorAll('.settings-panel-checkbox'));
100 | els.forEach(el => {
101 | if (this.value.indexOf(el.getAttribute('data-value')) >= 0) {
102 | el.checked = true;
103 | }
104 | else {
105 | el.checked = false;
106 | }
107 | });
108 | }
109 |
110 | this.group.disabled = !!this.disabled;
111 |
112 | return this;
113 | }
114 |
--------------------------------------------------------------------------------
/src/color.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const EventEmitter = require('events').EventEmitter
4 | const ColorPicker = require('simple-color-picker')
5 | const inherits = require('inherits')
6 | const css = require('dom-css')
7 | const tinycolor = require('tinycolor2')
8 | const formatParam = require('param-case')
9 | const num = require('input-number')
10 |
11 | module.exports = Color
12 | inherits(Color, EventEmitter)
13 |
14 | function Color (opts) {
15 | if (!(this instanceof Color)) return new Color(opts)
16 |
17 | this.update(opts);
18 | }
19 |
20 | Color.prototype.update = function (opts) {
21 | opts.container.innerHTML = '';
22 |
23 | opts = opts || {}
24 | opts.format = opts.format || 'rgb'
25 | opts.value = opts.value || '#123456';
26 | var icon = opts.container.appendChild(document.createElement('div'))
27 | //FIXME: this needed to make el vertical-aligned by baseline
28 | icon.innerHTML = ' ';
29 | icon.className = 'settings-panel-color'
30 |
31 | var valueInput = opts.container.appendChild(document.createElement('input'));
32 | valueInput.id = opts.id;
33 | valueInput.className = 'settings-panel-color-value';
34 | num(valueInput);
35 | valueInput.onchange = () => {
36 | picker.setColor(valueInput.value);
37 | };
38 | valueInput.oninput = () => {
39 | picker.setColor(valueInput.value);
40 | };
41 |
42 | if (opts.readonly) {
43 | valueInput.setAttribute('readonly', true);
44 | }
45 |
46 |
47 | var initial = opts.value
48 | switch (opts.format) {
49 | case 'rgb':
50 | initial = tinycolor(initial).toHexString()
51 | break
52 | case 'hex':
53 | initial = tinycolor(initial).toHexString()
54 | break
55 | case 'array':
56 | initial = tinycolor.fromRatio({r: initial[0], g: initial[1], b: initial[2]}).toHexString()
57 | break
58 | default:
59 | break
60 | }
61 |
62 | var picker = new ColorPicker({
63 | el: icon,
64 | color: initial,
65 | width: 160,
66 | height: 120
67 | });
68 |
69 | picker.$el.style.display = 'none';
70 |
71 | if (!opts.readonly) {
72 | icon.onmouseover = function () {
73 | picker.$el.style.display = ''
74 | }
75 | icon.onmouseout = (e) => {
76 | picker.$el.style.display = 'none'
77 | }
78 | }
79 |
80 | setTimeout(() => {
81 | this.emit('init', initial)
82 | });
83 |
84 | picker.onChange((hex) => {
85 | let v = format(hex);
86 | if (v !== valueInput.value) valueInput.value = v;
87 | css(icon, {backgroundColor: hex})
88 | this.emit('input', format(hex))
89 | })
90 |
91 | function format (hex) {
92 | switch (opts.format) {
93 | case 'rgb':
94 | return tinycolor(hex).toRgbString()
95 | case 'hex':
96 | return tinycolor(hex).toHexString()
97 | case 'array':
98 | var rgb = tinycolor(hex).toRgb()
99 | return [rgb.r / 255, rgb.g / 255, rgb.b / 255].map(function (x) {
100 | return x.toFixed(2)
101 | })
102 | default:
103 | return hex
104 | }
105 | };
106 |
107 | return this;
108 | }
109 |
--------------------------------------------------------------------------------
/src/custom.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @module settings-panel/src/custom
3 | *
4 | * A custom html component
5 | */
6 |
7 | 'use strict';
8 |
9 | const EventEmitter = require('events').EventEmitter
10 | const inherits = require('inherits')
11 | const extend = require('just-extend')
12 |
13 | module.exports = Custom
14 | inherits(Custom, EventEmitter)
15 |
16 | function Custom (opts) {
17 | if (!(this instanceof Custom)) return new Custom(opts);
18 |
19 | //FIXME: these guys force unnecessary events, esp if element returns wrong value
20 | // opts.container.addEventListener('input', (e) => {
21 | // this.emit('input', e.target.value);
22 | // });
23 | // opts.container.addEventListener('change', (e) => {
24 | // this.emit('change', e.target.value);
25 | // });
26 |
27 | this.update(opts);
28 | }
29 |
30 | Custom.prototype.update = function (opts) {
31 | extend(this, opts);
32 | var el = this.content;
33 | if (this.content instanceof Function) {
34 | el = this.content(this);
35 | if (!el) return;
36 |
37 | if (typeof el === 'string') {
38 | this.container.innerHTML = el;
39 | }
40 | else if (!this.container.contains(el)) {
41 | this.container.appendChild(el);
42 | }
43 | }
44 | else if (typeof this.content === 'string') {
45 | this.container.innerHTML = el;
46 | }
47 | else if (this.content instanceof Element && (!this.container.contains(el))) {
48 | this.container.appendChild(el);
49 | }
50 | else {
51 | //empty content is allowable, in case if user wants to show only label for example
52 | // throw Error('`content` should be a function returning html element or string');
53 | }
54 | };
--------------------------------------------------------------------------------
/src/interval.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const isNumeric = require('is-numeric')
4 | const css = require('dom-css')
5 | const isMobile = require('is-mobile')()
6 | const format = require('param-case')
7 | const clamp = require('mumath/clamp')
8 | const EventEmitter = require('events').EventEmitter
9 | const inherits = require('inherits');
10 | const precision = require('mumath/precision');
11 |
12 | module.exports = Range
13 |
14 | inherits(Range, EventEmitter);
15 |
16 | function Range (opts) {
17 | if (!(this instanceof Range)) return new Range(opts);
18 |
19 | this.update(opts);
20 | }
21 |
22 | Range.prototype.update = function (opts) {
23 | var self = this
24 | var scaleValue, scaleValueInverse, logmin, logmax, logsign, input, handle, panel;
25 |
26 | if (!!opts.step && !!opts.steps) {
27 | throw new Error('Cannot specify both step and steps. Got step = ' + opts.step + ', steps = ', opts.steps)
28 | }
29 |
30 | opts.container.innerHTML = '';
31 |
32 | if (opts.step) {
33 | var prec = precision(opts.step) || 1;
34 | }
35 | else {
36 | var prec = precision( (opts.max - opts.min) / opts.steps ) || 1;
37 | }
38 |
39 | // Create scale functions for converting to/from the desired scale:
40 | if (opts.scale === 'log' || opts.log) {
41 | scaleValue = function (x) {
42 | return logsign * Math.exp(Math.log(logmin) + (Math.log(logmax) - Math.log(logmin)) * x / 100)
43 | }
44 | scaleValueInverse = function (y) {
45 | return (Math.log(y * logsign) - Math.log(logmin)) * 100 / (Math.log(logmax) - Math.log(logmin))
46 | }
47 | } else {
48 | scaleValue = scaleValueInverse = function (x) { return x }
49 | }
50 |
51 | if (!Array.isArray(opts.value)) {
52 | opts.value = []
53 | }
54 | if (opts.scale === 'log' || opts.log) {
55 | // Get options or set defaults:
56 | opts.max = (isNumeric(opts.max)) ? opts.max : 100
57 | opts.min = (isNumeric(opts.min)) ? opts.min : 0.1
58 |
59 | // Check if all signs are valid:
60 | if (opts.min * opts.max <= 0) {
61 | throw new Error('Log range min/max must have the same sign and not equal zero. Got min = ' + opts.min + ', max = ' + opts.max)
62 | } else {
63 | // Pull these into separate variables so that opts can define the *slider* mapping
64 | logmin = opts.min
65 | logmax = opts.max
66 | logsign = opts.min > 0 ? 1 : -1
67 |
68 | // Got the sign so force these positive:
69 | logmin = Math.abs(logmin)
70 | logmax = Math.abs(logmax)
71 |
72 | // These are now simply 0-100 to which we map the log range:
73 | opts.min = 0
74 | opts.max = 100
75 |
76 | // Step is invalid for a log range:
77 | if (isNumeric(opts.step)) {
78 | throw new Error('Log may only use steps (integer number of steps), not a step value. Got step =' + opts.step)
79 | }
80 | // Default step is simply 1 in linear slider space:
81 | opts.step = 1
82 | }
83 |
84 | opts.value = [
85 | scaleValueInverse(isNumeric(opts.value[0]) ? opts.value[0] : scaleValue(opts.min + (opts.max - opts.min) * 0.25)),
86 | scaleValueInverse(isNumeric(opts.value[1]) ? opts.value[1] : scaleValue(opts.min + (opts.max - opts.min) * 0.75))
87 | ]
88 |
89 | if (scaleValue(opts.value[0]) * scaleValue(opts.max) <= 0 || scaleValue(opts.value[1]) * scaleValue(opts.max) <= 0) {
90 | throw new Error('Log range initial value must have the same sign as min/max and must not equal zero. Got initial value = [' + scaleValue(opts.value[0]) + ', ' + scaleValue(opts.value[1]) + ']')
91 | }
92 | } else {
93 | // If linear, this is much simpler:
94 | opts.max = (isNumeric(opts.max)) ? opts.max : 100
95 | opts.min = (isNumeric(opts.min)) ? opts.min : 0
96 | opts.step = (isNumeric(opts.step)) ? opts.step : (opts.max - opts.min) / 100
97 |
98 | opts.value = [
99 | isNumeric(opts.value[0]) ? opts.value[0] : (opts.min + opts.max) * 0.25,
100 | isNumeric(opts.value[1]) ? opts.value[1] : (opts.min + opts.max) * 0.75
101 | ]
102 | }
103 |
104 | // If we got a number of steps, use that instead:
105 | if (isNumeric(opts.steps)) {
106 | opts.step = isNumeric(opts.steps) ? (opts.max - opts.min) / opts.steps : opts.step
107 | }
108 |
109 | // Quantize the initial value to the requested step:
110 | opts.value[0] = opts.min + opts.step * Math.round((opts.value[0] - opts.min) / opts.step)
111 | opts.value[1] = opts.min + opts.step * Math.round((opts.value[1] - opts.min) / opts.step)
112 |
113 |
114 | //create DOM
115 | var lValue = require('./value')({
116 | container: opts.container,
117 | value: scaleValue(opts.value[0]).toFixed(prec),
118 | type: 'text',
119 | left: true,
120 | disabled: opts.disabled,
121 | id: opts.id,
122 | className: 'settings-panel-interval-value settings-panel-interval-value--left',
123 | input: v => {
124 | //TODO
125 | }
126 | })
127 |
128 | panel = opts.container.parentNode;
129 |
130 | input = opts.container.appendChild(document.createElement('span'))
131 | input.id = 'settings-panel-interval'
132 | input.className = 'settings-panel-interval'
133 |
134 | handle = document.createElement('span')
135 | handle.className = 'settings-panel-interval-handle'
136 | handle.value = 50;
137 | handle.min = 0;
138 | handle.max = 50;
139 | input.appendChild(handle)
140 |
141 | var value = opts.value
142 |
143 | // Display the values:
144 | var rValue = require('./value')({
145 | container: opts.container,
146 | disabled: opts.disabled,
147 | value: scaleValue(opts.value[1]).toFixed(prec),
148 | type: 'text',
149 | className: 'settings-panel-interval-value settings-panel-interval-value--right',
150 | input: v => {
151 | //TODO
152 | }
153 | })
154 |
155 | function setHandleCSS () {
156 | let left = ((value[0] - opts.min) / (opts.max - opts.min) * 100);
157 | let right = (100 - (value[1] - opts.min) / (opts.max - opts.min) * 100);
158 | css(handle, {
159 | left: left + '%',
160 | width: (100 - left - right) + '%'
161 | });
162 | opts.container.style.setProperty('--low', left + '%');
163 | opts.container.style.setProperty('--high', 100 - right + '%');
164 | lValue.style.setProperty('--value', left + '%');
165 | rValue.style.setProperty('--value', 100 - right + '%');
166 | }
167 |
168 | // Initialize CSS:
169 | setHandleCSS()
170 | // An index to track what's being dragged:
171 | var activeIndex = -1
172 |
173 | function mouseX (ev) {
174 | // Get mouse/touch position in page coords relative to the container:
175 | return (ev.touches && ev.touches[0] || ev).pageX - input.getBoundingClientRect().left
176 | }
177 |
178 | function setActiveValue (fraction) {
179 | if (activeIndex === -1) {
180 | return
181 | }
182 |
183 | // Get the position in the range [0, 1]:
184 | var lofrac = (value[0] - opts.min) / (opts.max - opts.min)
185 | var hifrac = (value[1] - opts.min) / (opts.max - opts.min)
186 |
187 | // Clip against the other bound:
188 | if (activeIndex === 0) {
189 | fraction = Math.min(hifrac, fraction)
190 | } else {
191 | fraction = Math.max(lofrac, fraction)
192 | }
193 |
194 | // Compute and quantize the new value:
195 | var newValue = opts.min + Math.round((opts.max - opts.min) * fraction / opts.step) * opts.step
196 |
197 | // Update value, in linearized coords:
198 | value[activeIndex] = newValue
199 |
200 | // Update and send the event:
201 | setHandleCSS()
202 | input.oninput()
203 | }
204 |
205 | var mousemoveListener = function (ev) {
206 | if (ev.target === input || ev.target === handle) ev.preventDefault()
207 |
208 | var fraction = clamp(mouseX(ev) / input.offsetWidth, 0, 1)
209 |
210 | setActiveValue(fraction)
211 | }
212 |
213 | var mouseupListener = function (ev) {
214 | panel.classList.remove('settings-panel-interval-dragging')
215 |
216 | document.removeEventListener(isMobile ? 'touchmove' : 'mousemove', mousemoveListener)
217 | document.removeEventListener(isMobile ? 'touchend' : 'mouseup', mouseupListener)
218 |
219 | activeIndex = -1
220 | }
221 |
222 | input.addEventListener(isMobile ? 'touchstart' : 'mousedown', function (ev) {
223 | // Tweak control to make dragging experience a little nicer:
224 | panel.classList.add('settings-panel-interval-dragging')
225 |
226 | // Get mouse position fraction:
227 | var fraction = clamp(mouseX(ev) / input.offsetWidth, 0, 1)
228 |
229 | // Get the current fraction of position --> [0, 1]:
230 | var lofrac = (value[0] - opts.min) / (opts.max - opts.min)
231 | var hifrac = (value[1] - opts.min) / (opts.max - opts.min)
232 |
233 | // This is just for making decisions, so perturb it ever
234 | // so slightly just in case the bounds are numerically equal:
235 | lofrac -= Math.abs(opts.max - opts.min) * 1e-15
236 | hifrac += Math.abs(opts.max - opts.min) * 1e-15
237 |
238 | // Figure out which is closer:
239 | var lodiff = Math.abs(lofrac - fraction)
240 | var hidiff = Math.abs(hifrac - fraction)
241 |
242 | activeIndex = lodiff < hidiff ? 0 : 1
243 |
244 | // Attach this to *document* so that we can still drag if the mouse
245 | // passes outside the container:
246 | document.addEventListener(isMobile ? 'touchmove' : 'mousemove', mousemoveListener)
247 | document.addEventListener(isMobile ? 'touchend' : 'mouseup', mouseupListener)
248 | })
249 |
250 | setTimeout(() => {
251 | var scaledLValue = scaleValue(value[0])
252 | var scaledRValue = scaleValue(value[1])
253 | lValue.value = scaledLValue.toFixed(prec)
254 | rValue.value = scaledRValue.toFixed(prec)
255 | this.emit('init', [scaledLValue, scaledRValue])
256 | })
257 |
258 | input.oninput = () => {
259 | var scaledLValue = scaleValue(value[0])
260 | var scaledRValue = scaleValue(value[1])
261 | lValue.value = scaledLValue.toFixed(prec)
262 | rValue.value = scaledRValue.toFixed(prec)
263 | this.emit('input', [scaledLValue, scaledRValue])
264 | }
265 |
266 | return this;
267 | }
--------------------------------------------------------------------------------
/src/range.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const EventEmitter = require('events').EventEmitter
4 | const inherits = require('inherits')
5 | const isNumeric = require('is-numeric')
6 | const css = require('dom-css')
7 | const format = require('param-case')
8 | const precision = require('mumath/precision')
9 |
10 | module.exports = Range
11 | inherits(Range, EventEmitter)
12 |
13 | function Range (opts) {
14 | if (!(this instanceof Range)) return new Range(opts);
15 |
16 | this.update(opts);
17 | }
18 |
19 | Range.prototype.update = function (opts) {
20 | var scaleValue, scaleValueInverse, logmin, logmax, logsign
21 |
22 | if (!!opts.step && !!opts.steps) {
23 | throw new Error('Cannot specify both step and steps. Got step = ' + opts.step + ', steps = ', opts.steps)
24 | }
25 |
26 | opts.container.innerHTML = '';
27 |
28 | if (!opts.container) opts.container = document.body;
29 |
30 | var input = opts.container.querySelector('.settings-panel-range');
31 |
32 | if (!input) {
33 | input = opts.container.appendChild(document.createElement('input'))
34 | input.type = 'range'
35 | input.className = 'settings-panel-range'
36 | }
37 |
38 | if (opts.disabled) input.disabled = true;
39 |
40 | if (opts.log) opts.scale = 'log';
41 |
42 | // Create scale functions for converting to/from the desired scale:
43 | if (opts.scale === 'log') {
44 | scaleValue = function (x) {
45 | return logsign * Math.exp(Math.log(logmin) + (Math.log(logmax) - Math.log(logmin)) * x / 100)
46 | }
47 | scaleValueInverse = function (y) {
48 | return (Math.log(y * logsign) - Math.log(logmin)) * 100 / (Math.log(logmax) - Math.log(logmin))
49 | }
50 | } else {
51 | scaleValue = scaleValueInverse = function (x) { return x }
52 | }
53 |
54 | // Get initial value:
55 | if (opts.scale === 'log') {
56 | // Get options or set defaults:
57 | opts.max = (isNumeric(opts.max)) ? opts.max : 100
58 | opts.min = (isNumeric(opts.min)) ? opts.min : 0.1
59 |
60 | // Check if all signs are valid:
61 | if (opts.min * opts.max <= 0) {
62 | throw new Error('Log range min/max must have the same sign and not equal zero. Got min = ' + opts.min + ', max = ' + opts.max)
63 | } else {
64 | // Pull these into separate variables so that opts can define the *slider* mapping
65 | logmin = opts.min
66 | logmax = opts.max
67 | logsign = opts.min > 0 ? 1 : -1
68 |
69 | // Got the sign so force these positive:
70 | logmin = Math.abs(logmin)
71 | logmax = Math.abs(logmax)
72 |
73 | // These are now simply 0-100 to which we map the log range:
74 | opts.min = 0
75 | opts.max = 100
76 |
77 | // Step is invalid for a log range:
78 | if (isNumeric(opts.step)) {
79 | throw new Error('Log may only use steps (integer number of steps), not a step value. Got step =' + opts.step)
80 | }
81 | // Default step is simply 1 in linear slider space:
82 | opts.step = 1
83 | }
84 |
85 | opts.value = scaleValueInverse(isNumeric(opts.value) ? opts.value : scaleValue((opts.min + opts.max) * 0.5))
86 |
87 | if (opts.value * scaleValueInverse(opts.max) <= 0) {
88 | throw new Error('Log range initial value must have the same sign as min/max and must not equal zero. Got initial value = ' + opts.value)
89 | }
90 | } else {
91 | // If linear, this is much simpler:
92 | opts.max = (isNumeric(opts.max)) ? opts.max : 100
93 | opts.min = (isNumeric(opts.min)) ? opts.min : 0
94 | opts.step = (isNumeric(opts.step)) ? opts.step : (opts.max - opts.min) / 100
95 |
96 | opts.value = isNumeric(opts.value) ? opts.value : (opts.min + opts.max) * 0.5
97 | }
98 |
99 | // If we got a number of steps, use that instead:
100 | if (isNumeric(opts.steps)) {
101 | opts.step = isNumeric(opts.steps) ? (opts.max - opts.min) / opts.steps : opts.step
102 | }
103 |
104 | // Quantize the initial value to the requested step:
105 | var initialStep = Math.round((opts.value - opts.min) / opts.step)
106 | opts.value = opts.min + opts.step * initialStep
107 |
108 | //preser container data for display
109 | opts.container.setAttribute('data-min', opts.min);
110 | opts.container.setAttribute('data-max', opts.max);
111 |
112 | if (opts.scale === 'log') {
113 | //FIXME: not every log is of precision 3
114 | var prec = opts.precision != null ? opts.precision : 3;
115 | }
116 | else {
117 | if (opts.step) {
118 | var prec = opts.precision != null ? opts.precision : precision(opts.step);
119 | }
120 | else if (opts.steps) {
121 | var prec = opts.precision != null ? opts.precision : precision( (opts.max - opts.min) / opts.steps );
122 | }
123 | }
124 |
125 | var value = require('./value')({
126 | id: opts.id,
127 | container: opts.container,
128 | className: 'settings-panel-range-value',
129 | value: scaleValue(opts.value).toFixed(prec),
130 | type: opts.scale === 'log' ? 'text' : 'number',
131 | min: scaleValue(opts.min),
132 | max: scaleValue(opts.max),
133 | disabled: opts.disabled,
134 | //FIXME: step here might vary
135 | step: opts.step,
136 | input: (v) => {
137 | let scaledValue = scaleValueInverse(v)
138 | input.value = scaledValue;
139 | value.title = input.value;
140 | // value.value = v
141 | this.emit('input', v);
142 | input.setAttribute('value', scaledValue.toFixed(0))
143 | opts.container.style.setProperty('--value', scaledValue + '%');
144 | opts.container.style.setProperty('--coef', scaledValue/100);
145 | }
146 | });
147 |
148 | // Set value on the input itself:
149 | input.min = opts.min
150 | input.max = opts.max
151 | input.step = opts.step
152 | input.value = opts.value
153 | let v = 100 * (opts.value - opts.min) / (opts.max - opts.min);
154 | input.setAttribute('value', v.toFixed(0))
155 | opts.container.style.setProperty('--value', v + '%');
156 | opts.container.style.setProperty('--coef', v/100);
157 |
158 | setTimeout(() => {
159 | this.emit('init', parseFloat(value.value))
160 | });
161 |
162 | input.oninput = (data) => {
163 | var scaledValue = scaleValue(parseFloat(data.target.value));
164 | value.value = scaledValue.toFixed(prec);
165 | let v = 100 * (data.target.value - opts.min) / (opts.max - opts.min);
166 | input.setAttribute('value', v.toFixed(0));
167 | opts.container.style.setProperty('--value', v + '%');
168 | opts.container.style.setProperty('--coef', v/100);
169 | value.title = scaledValue;
170 | this.emit('input', scaledValue);
171 | }
172 |
173 | return this;
174 | }
175 |
--------------------------------------------------------------------------------
/src/select.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var EventEmitter = require('events').EventEmitter
4 | var inherits = require('inherits')
5 | var format = require('param-case')
6 |
7 | module.exports = Select
8 | inherits(Select, EventEmitter)
9 |
10 | function Select (opts) {
11 | if (!(this instanceof Select)) return new Select(opts);
12 |
13 | this.update(opts);
14 | }
15 |
16 | Select.prototype.update = function (opts) {
17 | var i, container, input, downTriangle, upTriangle, key, option, el, keys
18 |
19 | opts.container.innerHTML = '';
20 |
21 | input = document.createElement('select')
22 | input.id = opts.id
23 | input.className = 'settings-panel-select';
24 | input.title = opts.title || opts.label;
25 |
26 | if (opts.disabled) input.disabled = true;
27 |
28 | downTriangle = document.createElement('span')
29 | downTriangle.className = 'settings-panel-select-triangle settings-panel-select-triangle--down'
30 |
31 | upTriangle = document.createElement('span')
32 | upTriangle.className = 'settings-panel-select-triangle settings-panel-select-triangle--up'
33 |
34 | if (Array.isArray(opts.options)) {
35 | for (let i = 0; i < opts.options.length; i++) {
36 | option = opts.options[i]
37 | el = document.createElement('option')
38 | el.value = el.textContent = option
39 | if (opts.value === option) {
40 | el.selected = 'selected'
41 | }
42 | input.appendChild(el)
43 | }
44 | } else {
45 | keys = Object.keys(opts.options)
46 | for (let i = 0; i < keys.length; i++) {
47 | key = keys[i]
48 | el = document.createElement('option')
49 | el.value = key
50 | if (opts.value === key) {
51 | el.selected = 'selected'
52 | }
53 | el.textContent = opts.options[key]
54 | input.appendChild(el)
55 | }
56 | }
57 |
58 | opts.container.appendChild(input)
59 | opts.container.appendChild(downTriangle)
60 | opts.container.appendChild(upTriangle)
61 |
62 | setTimeout(() => {
63 | this.emit('init', opts.value)
64 | })
65 |
66 | input.onchange = (data) => {
67 | this.emit('input', data.target.value)
68 | }
69 |
70 | return this;
71 | }
72 |
--------------------------------------------------------------------------------
/src/switch.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const inherits = require('inherits');
4 | const Emitter = require('events').EventEmitter;
5 | const format = require('param-case');
6 | const extend = require('just-extend');
7 |
8 | module.exports = Switch;
9 |
10 | inherits(Switch, Emitter);
11 |
12 | function Switch (opts) {
13 | if (!(this instanceof Switch)) return new Switch(opts);
14 |
15 | this.switch = opts.container.querySelector('.settings-panel-switch');
16 |
17 | if (!this.switch) {
18 | this.switch = document.createElement('fieldset');
19 | this.switch.className = 'settings-panel-switch';
20 | opts.container.appendChild(this.switch);
21 |
22 | var html = '';
23 |
24 | if (Array.isArray(opts.options)) {
25 | for (let i = 0; i < opts.options.length; i++) {
26 | let option = opts.options[i]
27 | html += createOption(option, option);
28 | }
29 | } else {
30 | for (let key in opts.options) {
31 | html += createOption(opts.options[key], key);
32 | }
33 | }
34 |
35 | this.switch.innerHTML = html;
36 |
37 | this.switch.onchange = (e) => {
38 | this.emit('input', e.target.getAttribute('data-value'));
39 | }
40 |
41 | setTimeout(() => {
42 | this.emit('init', opts.value)
43 | })
44 | }
45 |
46 | this.switch.id = opts.id;
47 |
48 | this.update(opts);
49 |
50 | function createOption (label, value) {
51 | let htmlFor = `settings-panel-${format(opts.panel.id)}-${format(opts.id)}-input-${format(value)}`;
52 |
53 | let html = `${label} `;
54 | return html;
55 | }
56 | }
57 |
58 | Switch.prototype.update = function (opts) {
59 | return this;
60 | }
--------------------------------------------------------------------------------
/src/text.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const EventEmitter = require('events').EventEmitter
4 | const inherits = require('inherits')
5 | const css = require('dom-css')
6 | const num = require('input-number');
7 | const extend = require('just-extend');
8 |
9 | module.exports = Text
10 | inherits(Text, EventEmitter)
11 |
12 | function Text (opts) {
13 | if (!(this instanceof Text)) return new Text(opts)
14 |
15 | let element = opts.container.querySelector('.settings-panel-text');
16 |
17 | if (!element) {
18 | element = opts.container.appendChild(document.createElement('input'));
19 | element.className = 'settings-panel-text';
20 | num(element);
21 |
22 | if (opts.placeholder) element.placeholder = opts.placeholder;
23 |
24 | this.element = element;
25 |
26 | element.oninput = (data) => {
27 | this.emit('input', data.target.value)
28 | }
29 | setTimeout(() => {
30 | this.emit('init', element.value)
31 | });
32 | }
33 |
34 | this.update(opts);
35 | }
36 |
37 | Text.prototype.update = function (opts) {
38 | extend(this, opts);
39 | this.element.type = this.type
40 | this.element.id = this.id
41 | this.element.value = this.value || ''
42 | this.element.disabled = !!this.disabled;
43 | return this;
44 | }
45 |
--------------------------------------------------------------------------------
/src/textarea.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const EventEmitter = require('events').EventEmitter
4 | const inherits = require('inherits')
5 | const css = require('dom-css')
6 | const autosize = require('autosize');
7 | const extend = require('just-extend');
8 |
9 | module.exports = Textarea
10 | inherits(Textarea, EventEmitter)
11 |
12 | function Textarea (opts) {
13 | if (!(this instanceof Textarea)) return new Textarea(opts)
14 |
15 | //
16 | let input = opts.container.querySelector('.settings-panel-textarea');
17 | if (!input) {
18 | input = opts.container.appendChild(document.createElement('textarea'));
19 | input.className = 'settings-panel-textarea';
20 |
21 | this.element = input;
22 |
23 | setTimeout(() => {
24 | this.emit('init', input.value)
25 | autosize.update(input);
26 | })
27 |
28 | input.oninput = (data) => {
29 | this.emit('input', data.target.value)
30 | }
31 |
32 | autosize(input);
33 | }
34 |
35 | this.update(opts);
36 | }
37 |
38 | Textarea.prototype.update = function (opts) {
39 | extend(this, opts);
40 |
41 | this.element.rows = this.rows || 1;
42 | this.element.placeholder = this.placeholder || '';
43 | this.element.id = this.id
44 |
45 | this.element.value = this.value || '';
46 |
47 | this.element.disabled = !!this.disabled;
48 |
49 | autosize.update(this.element);
50 |
51 | return this;
52 | }
--------------------------------------------------------------------------------
/src/value.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const num = require('input-number');
4 |
5 | module.exports = function (opts) {
6 | opts = opts || {}
7 | var value = document.createElement('input');
8 |
9 | num(value, opts);
10 |
11 | if (opts.input) {
12 | value.addEventListener('input', function () {
13 | let v = value.value;
14 | if (opts.type === 'number') v = parseFloat(v);
15 | opts.input(v)
16 | })
17 | }
18 | if (opts.change) {
19 | value.addEventListener('change', function () {
20 | let v = value.value;
21 | if (opts.type === 'number') v = parseFloat(v);
22 | opts.change(v)
23 | })
24 | }
25 |
26 | if (opts.disabled) value.disabled = true;
27 |
28 | value.value = opts.value
29 |
30 | if (opts.id) value.id = opts.id;
31 | value.className = 'settings-panel-value';
32 | if (opts.className) value.className += ' ' + opts.className;
33 | opts.container.appendChild(value);
34 |
35 | //add tip holder after value
36 | let tip = opts.container.appendChild(document.createElement('div'));
37 | tip.className = 'settings-panel-value-tip';
38 |
39 | return value
40 | }
41 |
--------------------------------------------------------------------------------
/test.js:
--------------------------------------------------------------------------------
1 | const createPanel = require('./')
2 | const insertCSS = require('insert-styles');
3 | const css = require('dom-css');
4 | const Picker = require('simple-color-picker');
5 | const Sortable = require('sortablejs');
6 | const color = require('tinycolor2');
7 | const colormap = require('colormap');
8 | const colorScales = require('colormap/colorScales');
9 | let palettes = require('nice-color-palettes/500');
10 |
11 | let colormaps = {};
12 |
13 | for (var name in colorScales) {
14 | if (name === 'alpha') continue;
15 | if (name === 'hsv') continue;
16 | if (name === 'rainbow') continue;
17 | if (name === 'rainbow-soft') continue;
18 | if (name === 'phase') continue;
19 |
20 | colormaps[name] = colormap({
21 | colormap: colorScales[name],
22 | nshades: 16,
23 | format: 'rgbaString'
24 | });
25 | }
26 |
27 | palettes = palettes
28 | //filter not readable palettes
29 | .filter((palette) => {
30 | return color.isReadable(palette[0], palette.slice(-1)[0], {
31 | level:"AA", size:"large"
32 | });
33 | });
34 |
35 | // prepare mobile
36 | var meta = document.createElement('meta')
37 | meta.setAttribute('name', 'viewport')
38 | meta.setAttribute('content', 'width=device-width, initial-scale=1, shrink-to-fit=no, user-scalable=0')
39 | document.head.appendChild(meta)
40 |
41 |
42 | insertCSS(`
43 | body {
44 | margin: 0;
45 | position: relative;
46 | min-height: 100vh;
47 | background: url('./images/land.jpg');
48 | background-position: center top;
49 | background-size: cover;
50 | background-attachment: fixed;
51 | }
52 | .settings-panel-preview {
53 | margin: 2em auto;
54 | }
55 | .sidebar {
56 | display: none;
57 | position: fixed;
58 | top: 0;
59 | right: 0;
60 | bottom: 0;
61 | font-size: 12px;
62 | width: 240px;
63 | box-shadow: -1px 0 3px rgba(0,0,0,.05);
64 | }
65 | @media (min-width: 640px) {
66 | .frame {
67 | min-height: 100vh;
68 | position: relative;
69 | width: calc(100% - 240px);
70 | }
71 | .sidebar {
72 | display: block;
73 | }
74 | }
75 | `);
76 |
77 |
78 | const themes = {
79 | none: require('./theme/none'),
80 | // lucy: require('./theme/lucy'),
81 | control: require('./theme/control'),
82 | dragon: require('./theme/dragon'),
83 | // merka: require('./theme/merka'),
84 | json: require('./theme/json'),
85 | flat: require('./theme/flat'),
86 | typer: require('./theme/typer')
87 | };
88 |
89 | var frame = document.createElement('div');
90 | frame.classList.add('frame');
91 | document.body.appendChild(frame);
92 |
93 |
94 | var panel = createPanel([
95 | {type: 'switch', label: 'Switch', options: ['One', 'Two', 'Three'], value: 'One'},
96 | {type: 'range', label: 'Range', min: 0, max: 100, value: 80, help: 'Default slider'},
97 | // {type: 'range', label: 'Range stepped', min: 0, max: 1, step: 0.2, value: 0.6},
98 | // {type: 'range', scale: 'log', label: 'Range log', min: 0.01, max: 100, value: 1},
99 | // {type: 'range', scale: 'log', label: 'Stepped log', min: 0.01, max: 100, steps: 10, value: 1},
100 | // {type: 'range', scale: 'log', label: 'Range -log', min: -0.01, max: -100, value: -1},
101 | // {type: 'range', scale: 'log', label: 'Stepped -log', min: -0.01, max: -100, steps: 10, value: -1},
102 | // {type: 'raw', content: ' '},
103 | {type: 'interval', label: 'Interval', min: 0, max: 10, value: [3, 7], steps: 20},
104 | // {type: 'interval', label: 'Log interval', min: 0.1, max: 10, value: [0.1, 1], scale: 'log', steps: 20},
105 | // {type: 'interval', label: 'Neg log interval', min: -0.1, max: -10, value: [-0.3, -1], scale: 'log', steps: 20},
106 | {type: 'checkbox', label: 'Checkbox', value: true},
107 | {type: 'checkbox', label: 'Checkbox group', value: ['b', 'c'], options: {
108 | a: 'Option A',
109 | b: 'Option B',
110 | c: 'Option C'
111 | }
112 | },
113 | {type: 'text', label: 'Text', value: 'my setting'},
114 | {type: 'color', label: 'Color', format: 'rgb', value: 'rgb(100,200,100)'},
115 | // {type: 'color', label: 'Color hex', format: 'hex', value: '#30b2ba'},
116 | {type: 'select', label: 'Select', options: {state1: 'State One', state2: 'State Two'}, value: 'state1'},
117 | {type: 'email', label: 'Email', placeholder: 'email'},
118 | {type: 'text', label: 'Disabled', disabled: true, value: 'disabled value'},
119 | {type: 'textarea', label: 'Long text', placeholder: 'long text...'},
120 | {type: 'raw', content: ' '},
121 | {type: 'button', label: 'Cancel', input: function () { window.alert('hello!') }, style: {width: '50%'}},
122 | {type: 'button', label: 'Ok', input: function () { window.alert('hello!') }, style: 'width: 50%'},
123 | // {type: 'switch', label: 'Orientation', options: 'top|left|bottom|right'.split('|'), value: 'left' }
124 | ], {
125 | id: 'preview',
126 | title: 'Example panel',
127 | className: 'settings-panel-preview',
128 | container: frame,
129 | theme: themes.typer
130 | });
131 |
132 | panel.on('input', function (name, value, data) {
133 | console.log(name, value, data)
134 | })
135 |
136 |
137 |
138 | //create main form
139 | var settings = createPanel([
140 | {label: 'Theme', type: 'select', options: Object.keys(themes), value: panel.theme.name, change: v => {
141 | if (!v) return;
142 | panel.update({theme: themes[v]});
143 | settings.set({
144 | 'font-size': panel.fontSize,
145 | 'font-family': panel.fontFamily,
146 | 'label-width': panel.labelWidth,
147 | 'input-height': panel.inputHeight,
148 | 'padding': themes[v].padding
149 | });
150 | settings.set('palette', panel.theme.palette);
151 | }},
152 |
153 | {label: 'Palette', type: 'raw', id: 'palette', options: palettes, save: false, value: panel.theme.palette, content: function (opts) {
154 | let palette = opts.value || this.value;
155 |
156 | let list = this.field.querySelector('.palette');
157 | if (!list) {
158 | list = document.createElement('ul');
159 | list.className = 'palette';
160 | css(list, {
161 | listStyle: 'none',
162 | margin: '0 0 0',
163 | padding: 0,
164 | minHeight: '2em',
165 | display: 'inline-block'
166 | });
167 | setTimeout(() => {
168 | this.emit('init', palette);
169 | });
170 | }
171 |
172 | //add randomize btn
173 | let randomize = list.querySelector('.palette-randomize');
174 | if (!randomize) {
175 | randomize = document.createElement('li');
176 | randomize.className = 'palette-randomize';
177 | randomize.innerHTML = '⚂ ';
178 | css(randomize, {
179 | height: '2em',
180 | width: '2em',
181 | float: 'left',
182 | clear: 'left',
183 | position: 'relative',
184 | marginTop: '.5em',
185 | lineHeight: '2em',
186 | textAlign: 'center'
187 | });
188 | randomize.title = 'Randomize palette';
189 | randomize.onclick = () => {
190 | palette = palettes[Math.floor(Math.random() * palettes.length)];
191 | this.emit('change', palette.slice());
192 | settings.set('palette', palette.slice());
193 | colormap.firstChild.value = 'custom';
194 | };
195 | list.appendChild(randomize);
196 | }
197 |
198 | //add reverse btn
199 | let reverse = list.querySelector('.palette-reverse');
200 | if (!reverse) {
201 | reverse = document.createElement('li');
202 | reverse.className = 'palette-reverse';
203 | reverse.innerHTML = '↻ ';
204 | css(reverse, {
205 | height: '2em',
206 | width: '2em',
207 | float: 'left',
208 | marginTop: '.5em',
209 | lineHeight: '2em',
210 | textAlign: 'center'
211 | });
212 | reverse.title = 'Reverse palette';
213 | reverse.onclick = () => {
214 | palette = palette.reverse();
215 | this.emit('change', palette.slice());
216 | settings.set('palette', palette.slice());
217 | colormap.firstChild.value = 'custom';
218 | };
219 | list.appendChild(reverse);
220 | }
221 |
222 | //add colormap select
223 | let colormap = list.querySelector('.palette-colormap');
224 | if (!colormap) {
225 | colormap = document.createElement('li');
226 | colormap.className = 'palette-colormap';
227 | colormap.innerHTML = `custom ${Object.keys(colormaps).map(key => `${key} `)} `;
228 | css(colormap, {
229 | height: '2em',
230 | width: 'auto',
231 | marginLeft: '.5em',
232 | float: 'left',
233 | marginTop: '.5em',
234 | lineHeight: '2em',
235 | textAlign: 'center'
236 | });
237 | colormap.title = 'Choose colormap';
238 | colormap.onchange = (e) => {
239 | let cm = e.target.value;
240 | palette = colormaps[cm] || panel.theme.palette;
241 | this.emit('change', palette.slice());
242 | settings.set('palette', palette.slice());
243 | };
244 | list.appendChild(colormap);
245 | }
246 |
247 | //update palette
248 | let els = list.querySelectorAll('.palette-color');
249 | [].forEach.call(els, (el) => {
250 | list.removeChild(el);
251 | });
252 |
253 | if (typeof palette === 'string') {
254 | palette = palette.split(',');
255 | }
256 | else if (Array.isArray(palette[0])) {
257 | palette = palette.map((arr) => `rgb(${arr.map(v => v.toFixed(0))})`);
258 | }
259 |
260 | palette.forEach((color) => {
261 | let item = document.createElement('li');
262 | item.title = color;
263 | item.className = 'palette-color';
264 | item.setAttribute('data-id', color);
265 | css(item, {
266 | height: '2em',
267 | width: '2em',
268 | float: 'left',
269 | background: color,
270 | cursor: 'move'
271 | });
272 | list.insertBefore(item, randomize);
273 |
274 | //create picker for each color
275 | let picker = new Picker({
276 | el: item,
277 | color: color,
278 | height: 162
279 | });
280 | picker.$el.style.display = 'none';
281 | item.onmouseout = (e) => {
282 | picker.$el.style.display = 'none'
283 | }
284 | item.onmouseover = function () {
285 | picker.$el.style.display = ''
286 | }
287 | picker.onChange((color) => {
288 | css(item, {background: color});
289 | item.setAttribute('data-id', color);
290 | opts.sortable && this.emit('change', opts.sortable.toArray());
291 | })
292 | });
293 |
294 | if (opts.sortable) {
295 | opts.sortable.destroy();
296 | opts.sortable = null;
297 | }
298 |
299 | opts.sortable = new Sortable(list, {
300 | draggable: '.palette-color',
301 | filter: '.Scp',
302 | forceFallback: true,
303 | onUpdate: (e) => {
304 | this.emit('change', opts.sortable.toArray());
305 | settings.set('palette', opts.sortable.toArray());
306 | }
307 | });
308 |
309 | return list;
310 | },
311 | change: (v) => {
312 | let palette = null;
313 | if (v) {
314 | if (Array.isArray(v)) palette = v;
315 | else palette = v.split(/\s*,\s*/);
316 | }
317 | panel.update({palette: palette});
318 | }
319 | },
320 | // {label: 'Active color', type:'color', id: 'active', value: panel.theme.active || panel.theme.palette[0], change: v => {
321 | // panel.update({active: v});
322 | // }},
323 | {label: 'Font family', id: 'font-family', type: 'text', value: panel.theme.fontFamily, change: v => {
324 | panel.update({fontFamily: v});
325 | }},
326 | {type: 'raw', content: ' '},
327 | {label: 'Label orientation', type: 'switch', options: {top: '↑', left: '←', bottom: '↓', right: '→'}, value: panel.orientation, change: (v) => {
328 | panel.update({orientation: v});
329 | if (v === 'top' || v === 'bottom') {
330 | settings.set('label-width', {
331 | disabled: true
332 | });
333 | }
334 | else {
335 | settings.set({
336 | 'label-width': {
337 | disabled: false
338 | }
339 | });
340 | }
341 | }
342 | },
343 | {label: '1em = ', title: 'Font size', id: 'font-size', type: 'text', value: panel.theme.fontSize, change: (v) => {
344 | panel.update({fontSize: v});
345 | }, orientation: 'left', style: 'width: 50%; float: left'},
346 | {type: 'text', label: ' ↨', title: 'Input height, em', id: 'input-height', value: panel.theme.inputHeight, input: (v) => {
347 | panel.update({inputHeight: v});
348 | }, orientation: 'left', style: 'width: 50%'},
349 | {type: 'text', label: ' ↔', title: 'Full panel width', id: 'width', value: '32em', input: (v) => {
350 | panel.update({style: `width: ${v};`})
351 | }, orientation: 'left', style: 'width: 50%; float: left'},
352 | {label: ' ↦', title: 'Label width', id: 'label-width', type: 'text', value: panel.theme.labelWidth, change: (v) => {
353 | panel.update({labelWidth: v});
354 | }, orientation: 'left', style: 'width: 50%'},
355 | {label: 'padding', id: 'padding', type: 'range', min: 0, max: 1, value: 1/5, change: v => {
356 | panel.update({padding: v})
357 | }},
358 | {type: 'raw', content: ' '},
359 | {type: 'button', label: 'Get the code!', input: () => {
360 | alert('code');
361 | }}
362 | ], {
363 | // theme: require('./theme/dragon'),
364 | id: 'settings',
365 | className: 'sidebar',
366 | orientation: 'top',
367 | labelWidth: '22%',
368 | style: 'background: rgba(253,253,253,.82);'
369 | });
370 |
--------------------------------------------------------------------------------
/theme/control.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @module prama/theme/control
3 | *
4 | * Control-panel theme on steroids
5 | */
6 | 'use strict';
7 |
8 | const px = require('add-px-to-style');
9 | const fonts = require('google-fonts');
10 | const color = require('tinycolor2');
11 | const scopeCss = require('scope-css');
12 | const interpolate = require('color-interpolate');
13 | const none = require('./none');
14 |
15 | module.exports = control;
16 |
17 | // control.palette = ['#292929', '#e7e7e7'];
18 | control.palette = ['#e7e7e7', '#292929'];
19 |
20 | control.fontSize = '12px';
21 | control.fontFamily = '"Space Mono", monospace';
22 | control.labelWidth = '33.3%';
23 | control.inputHeight = 1.66666;
24 | control.padding = 1/8;
25 |
26 | fonts.add({
27 | 'Space Mono': 400
28 | });
29 |
30 |
31 | function control (opts) {
32 | opts = opts || {};
33 | let fs = opts.fontSize || control.fontSize;
34 | let font = opts.fontFamily || control.fontFamily;
35 | let h = opts.inputHeight || control.inputHeight;
36 | let labelWidth = opts.labelWidth || control.labelWidth;
37 | let padding = opts.padding || control.padding;
38 |
39 | let palette = opts.palette || control.palette;
40 | let pick = interpolate(palette);
41 |
42 | let white = color(pick(.0)).toString();
43 | let light = color(pick(.1)).toString();
44 | let gray = color(pick(.75)).toString();
45 | let dark = color(pick(.95)).toString();
46 | let black = color(pick(1)).toString();
47 |
48 | //NOTE: this is in case of scaling palette to black/white range
49 | // let white = color(pick(0.1607843137254902)).toString();
50 | // let light = color(pick(0.23529411764705882)).toString();
51 | // let gray = color(pick(0.5705882352941177)).toString();
52 | // let dark = color(pick(0.7196078431372549)).toString();
53 | // let black = color(pick(0.9058823529411765)).toString();
54 |
55 | //none theme defines sizes, the rest (ours) is up to style
56 | return none({
57 | fontSize: fs,
58 | fontFamily: font,
59 | inputHeight: h,
60 | labelWidth: labelWidth,
61 | padding: padding
62 | }) + `
63 | :host {
64 | background: ${white};
65 | font-family: ${font};
66 | font-size: ${px('font-size',fs)};
67 | color: ${gray};
68 | padding: ${h/2}em;
69 | }
70 |
71 | .settings-panel-title {
72 | font-size: 1em;
73 | text-align: center;
74 | font-weight: 400;
75 | text-transform: uppercase;
76 | color: ${black};
77 | padding-bottom: ${h/3}em;
78 | }
79 |
80 | .settings-panel-label {
81 | color: ${black};
82 | }
83 |
84 | /** Text */
85 | .settings-panel-text,
86 | .settings-panel-textarea {
87 | padding-left: ${h/4}em;
88 | border: none;
89 | font-family: inherit;
90 | background: ${light};
91 | color: inherit;
92 | border-radius: 0;
93 | }
94 | .settings-panel-text:hover,
95 | .settings-panel-textarea:hover,
96 | .settings-panel-text:focus,
97 | .settings-panel-textarea:focus {
98 | outline: none;
99 | color: ${dark};
100 | }
101 |
102 |
103 | /** Range */
104 | .settings-panel-range {
105 | -webkit-appearance: none;
106 | -moz-appearance: none;
107 | appearance: none;
108 | background: ${light};
109 | width: 80%;
110 | border-radius: 0;
111 | }
112 | .settings-panel-range:focus {
113 | outline: none;
114 | }
115 | .settings-panel-range::-webkit-slider-thumb {
116 | -webkit-appearance: none;
117 | height: ${h}em;
118 | width: ${h/2}em;
119 | background: ${gray};
120 | border: 0;
121 | margin-top: 0px;
122 | }
123 | .settings-panel-range:hover::-webkit-slider-thumb {
124 | background: ${dark};
125 | }
126 | .settings-panel-range::-moz-range-track {
127 | -moz-appearance: none;
128 | background: none;
129 | }
130 | .settings-panel-range::-moz-range-thumb {
131 | -moz-appearance: none;
132 | background: ${gray};
133 | border: none;
134 | border-radius: 0px;
135 | height: ${h}em;
136 | width: ${h/2}em;
137 | }
138 | .settings-panel-range:hover::-moz-range-thumb {
139 | background: ${dark};
140 | }
141 | .settings-panel-range::-ms-track {
142 | background: none;
143 | border: none;
144 | outline: none;
145 | color: transparent;
146 | }
147 | .settings-panel-range::-ms-fill-lower {
148 | background: none;
149 | }
150 | .settings-panel-range::-ms-fill-upper {
151 | background: none;
152 | }
153 | .settings-panel-range::-ms-thumb {
154 | border-radius: 0px;
155 | border: 0;
156 | background: ${gray};
157 | width: ${h/2}em;
158 | height: ${h}em;
159 | }
160 | .settings-panel-range:hover::-ms-thumb {
161 | background: ${dark};
162 | }
163 | .settings-panel-range:focus::-ms-fill-lower {
164 | background: none;
165 | outline: none;
166 | }
167 | .settings-panel-range:focus::-ms-fill-upper {
168 | background: none;
169 | outline: none;
170 | }
171 |
172 |
173 | /** Interval */
174 | .settings-panel-interval-handle {
175 | background: ${gray};
176 | }
177 | .settings-panel-interval {
178 | background: ${light};
179 | position: relative;
180 | width: 60%;
181 | }
182 | .settings-panel-interval:hover .settings-panel-interval-handle {
183 | background: ${dark};
184 | }
185 |
186 | /** Values */
187 | .settings-panel-value {
188 | background: ${light};
189 | margin-left: ${h/4}em;
190 | width: calc(20% - ${h/4}em);
191 | padding-left: ${h/4}em;
192 | }
193 | .settings-panel-value:first-child {
194 | margin-left: 0;
195 | margin-right: ${h/4}em;
196 | }
197 | .settings-panel-value:hover,
198 | .settings-panel-value:focus {
199 | color: ${dark};
200 | }
201 |
202 |
203 | /** Select */
204 | .settings-panel-select {
205 | font-family: inherit;
206 | background: ${light};
207 | color: inherit;
208 | padding-left: ${h/4}em;
209 | border-radius: 0;
210 | outline: none;
211 | border: none;
212 | -webkit-appearance: none;
213 | -moz-appearance: none;
214 | -o-appearance:none;
215 | appearance:none;
216 | }
217 | .settings-panel-select:hover,
218 | .settings-panel-select:focus {
219 | color: ${dark};
220 | }
221 | .settings-panel-select::-ms-expand {
222 | display: none;
223 | }
224 | .settings-panel-select-triangle {
225 | display: block;
226 | }
227 |
228 |
229 | /** Checkbox */
230 | .settings-panel-checkbox {
231 | display: none;
232 | }
233 | .settings-panel-checkbox-label:before {
234 | content: 'x';
235 | color: transparent;
236 | position: relative;
237 | display: inline-block;
238 | vertical-align: baseline;
239 | width: ${h*.85}em;
240 | height: ${h*.85}em;
241 | line-height: ${h*.85}em;
242 | background: ${light};
243 | margin-right: ${h*.25}em;
244 | margin-bottom: ${h*.15}em;
245 | }
246 | .settings-panel-checkbox:checked + .settings-panel-checkbox-label:before {
247 | background: ${gray};
248 | box-shadow: inset 0 0 0 ${h*.2}em ${light};
249 | }
250 | .settings-panel-checkbox:checked + .settings-panel-checkbox-label:hover:before {
251 | background: ${dark};
252 | }
253 | .settings-panel-checkbox-label:last-child:before {
254 | margin-bottom: ${h*.1}em;
255 | }
256 |
257 |
258 | /** Color */
259 | .settings-panel-color {
260 | position: relative;
261 | width: calc(20% - ${h/4}em);
262 | margin-right: ${h/4}em;
263 | display: inline-block;
264 | vertical-align: baseline;
265 | }
266 | .settings-panel-color-value {
267 | border: none;
268 | padding-left: ${h/4}em;
269 | width: 80%;
270 | font-family: inherit;
271 | background: ${light};
272 | color: inherit;
273 | border-radius: 0;
274 | }
275 | .settings-panel-color-value:hover,
276 | .settings-panel-color-value:focus {
277 | outline: none;
278 | color: ${dark};
279 | }
280 |
281 |
282 | /** Button */
283 | .settings-panel-button {
284 | color: ${black};
285 | background: ${light};
286 | text-align: center;
287 | border: none;
288 | }
289 | .settings-panel-button:focus {
290 | outline: none;
291 | }
292 | .settings-panel-button:hover {
293 | background: ${gray};
294 | }
295 | .settings-panel-button:active {
296 | background: ${dark};
297 | }
298 |
299 |
300 | /** Switch style */
301 | .settings-panel-switch {
302 | }
303 | .settings-panel-switch-input {
304 | display: none;
305 | }
306 | .settings-panel-switch-label {
307 | position: relative;
308 | display: inline-block;
309 | padding: 0 ${h/2}em;
310 | margin: 0;
311 | z-index: 2;
312 | text-align: center;
313 | }
314 | .settings-panel-switch-input:checked + .settings-panel-switch-label {
315 | background: ${light};
316 | color: ${gray};
317 | }
318 | .settings-panel-switch-input + .settings-panel-switch-label:hover {
319 | color: ${dark};
320 | }
321 |
322 | /** Decorations */
323 | ::-webkit-input-placeholder {
324 | color: ${gray};
325 | }
326 | ::-moz-placeholder {
327 | color: ${gray};
328 | }
329 | :-ms-input-placeholder {
330 | color: ${gray};
331 | }
332 | :-moz-placeholder {
333 | color: ${gray};
334 | }
335 | ::-moz-selection {
336 | color: ${white};
337 | background: ${dark};
338 | }
339 | ::selection {
340 | color: ${white};
341 | background: ${black};
342 | }
343 | :host hr {
344 | margin: ${h/4}em ${h/8}em;
345 | color: ${light};
346 | opacity: 1;
347 | }
348 | :host a {
349 | border-bottom: 1px solid ${alpha(gray, .15)};
350 | }
351 | :host a:hover {
352 | color: ${dark};
353 | border-bottom: 1px solid ${gray};
354 | }
355 | `};
356 |
357 |
358 | function alpha (c, value) {
359 | return color(c).setAlpha(value).toString();
360 | }
361 |
--------------------------------------------------------------------------------
/theme/dat.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @module prama/theme/dat
3 | *
4 | * dat.gui reproduction
5 | */
6 | 'use strict';
7 |
8 | const px = require('add-px-to-style');
9 | const fonts = require('google-fonts');
10 | const color = require('tinycolor2');
11 | const scopeCss = require('scope-css');
12 | const lerp = require('interpolation-arrays');
13 | const none = require('./none');
14 |
15 | module.exports = dat;
16 |
17 | fonts.add({
18 | 'Roboto': 400
19 | });
20 |
21 | dat.palette = ['#1b1b1b', '#f7f7f7'];
22 |
23 | dat.fontSize = '12px';
24 | dat.fontFamily = '"Roboto", sans-serif';
25 | dat.labelWidth = '33.3%';
26 | dat.inputHeight = 2;
27 |
28 | function dat (opts) {
29 | opts = opts || {};
30 |
31 | let h = opts.inputHeight || dat.inputHeight;
32 | let labelWidth = opts.labelWidth || dat.labelWidth;
33 | let fontSize = opts.fontSize || dat.fontSize;
34 | let font = opts.fontFamily || dat.fontFamily;
35 |
36 | let palette = (opts.palette || dat.palette).map(v => color(v).toRgb());
37 | let pick = lerp(palette);
38 |
39 | let white = color(pick(1)).toString();
40 | let light = color(pick(.6)).toString();
41 | let notSoLight = color(pick(.5)).toString();
42 | let gray = color(pick(.13)).toString();
43 | let dark = color(pick(.07)).toString();
44 | let black = color(pick(0)).toString();
45 |
46 | return none({
47 | fontSize: fontSize,
48 | fontFamily: font,
49 | inputHeight: h,
50 | labelWidth: labelWidth,
51 | palette: palette
52 | }) +
--------------------------------------------------------------------------------
/theme/dragon.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @module prama/theme/dragon
3 | *
4 | * Midragonlistic theme based off https://dribbble.com/guidorosso NIMA editor settings
5 | */
6 | 'use strict';
7 |
8 | const px = require('add-px-to-style');
9 | const fonts = require('google-fonts');
10 | const color = require('tinycolor2');
11 | const interpolate = require('color-interpolate');
12 | const none = require('./none');
13 |
14 | module.exports = dragon;
15 |
16 | fonts.add({
17 | 'Roboto': 400
18 | });
19 |
20 | dragon.palette = ['black', 'white'];
21 | dragon.palette = ['#1b1b1b', '#f7f7f7'];
22 |
23 | dragon.fontSize = '12px';
24 | dragon.fontFamily = '"Roboto", sans-serif';
25 | dragon.labelWidth = '33.3%';
26 | dragon.inputHeight = 2;
27 | dragon.padding = 1/5;
28 |
29 | function dragon (opts) {
30 | opts = opts || {};
31 |
32 | let h = opts.inputHeight || dragon.inputHeight;
33 | let labelWidth = opts.labelWidth || dragon.labelWidth;
34 | let fontSize = opts.fontSize || dragon.fontSize;
35 | let font = opts.fontFamily || dragon.fontFamily;
36 | let padding = opts.padding || dragon.padding;
37 |
38 | let palette = opts.palette || dragon.palette;
39 | let pick = interpolate(palette);
40 |
41 | let white = color(pick(1)).toString();
42 | let light = color(pick(.65)).toString();
43 | let notSoLight = color(pick(.45)).toString();
44 | let gray = color(pick(.13)).toString();
45 | let dark = color(pick(.07)).toString();
46 | let black = color(pick(0)).toString();
47 |
48 | return none({
49 | fontSize: fontSize,
50 | fontFamily: font,
51 | inputHeight: h,
52 | labelWidth: labelWidth,
53 | palette: [white, black],
54 | padding: padding
55 | }) + `
56 | :host {
57 | color: ${light};
58 | background: ${alpha(gray, .93)};
59 | font-size: ${px('font-size', fontSize)};
60 | font-family: ${font};
61 | font-weight: 400;
62 | padding: ${h*.5}em;
63 | }
64 | :host a {
65 | text-decoration: none;
66 | border-bottom: .15em solid ${alpha(white, .2)};
67 | }
68 | :host a:hover {
69 | text-decoration: none;
70 | border-bottom: .15em solid ${alpha(white, 1)};
71 | }
72 |
73 | .settings-panel-title {
74 | text-transform: none;
75 | font-weight: 400;
76 | letter-spacing: .05ex;
77 | color: ${white};
78 | padding: ${h * 2 * padding / 1.333}em ${h * padding / 1.333 }em ${h * 4 * padding / 1.333}em;
79 | }
80 |
81 | .settings-panel-label {
82 | }
83 |
84 | /** Select style */
85 | .settings-panel-select {
86 | height: ${h}em;
87 | background: none;
88 | outline: none;
89 | border: none;
90 | -webkit-appearance: none;
91 | -moz-appearance: none;
92 | -o-appearance:none;
93 | appearance:none;
94 | width: 100%;
95 | padding-right: 1em;
96 | margin-right: -1em;
97 | color: ${white};
98 | border-radius: 0;
99 | box-shadow: 0 2px ${dark};
100 | }
101 | .settings-panel-select::-ms-expand {
102 | display:none;
103 | }
104 | .settings-panel-select-triangle {
105 | content: '';
106 | border-right: .3em solid transparent;
107 | border-left: .3em solid transparent;
108 | line-height: 2em;
109 | position: relative;
110 | z-index: 1;
111 | vertical-align: middle;
112 | display: inline-block;
113 | width: 0;
114 | text-align: center;
115 | pointer-events: none;
116 | }
117 | .settings-panel-select-triangle--down {
118 | top: 0em;
119 | left: .5em;
120 | border-top: .3em solid ${white};
121 | border-bottom: .0 transparent;
122 | }
123 | .settings-panel-select-triangle--up {
124 | display: none;
125 | }
126 | .settings-panel-field--select:hover .settings-panel-select,
127 | .settings-panel-select:focus {
128 | box-shadow: 0 2px ${black};
129 | }
130 |
131 | /** Values */
132 | .settings-panel-value {
133 | color: ${white};
134 | }
135 | .settings-panel-value:hover,
136 | .settings-panel-value:focus {
137 | color: ${white};
138 | }
139 |
140 | /** Text */
141 | .settings-panel-text,
142 | .settings-panel-textarea {
143 | -webkit-appearance: none;
144 | -moz-appearance: none;
145 | -o-appearance:none;
146 | border: none;
147 | height: ${h}em;
148 | padding: 0;
149 | background: none;
150 | color: ${white};
151 | width: 100%;
152 | border-radius: 0;
153 | box-shadow: 0 2px ${dark};
154 | }
155 | .settings-panel-textarea {
156 | padding-top: .35em;
157 | padding-left: 0;
158 | }
159 |
160 | .settings-panel-text:focus,
161 | .settings-panel-textarea:focus,
162 | .settings-panel-text:hover,
163 | .settings-panel-textarea:hover {
164 | outline: none;
165 | color: ${white};
166 | box-shadow: 0 2px ${black};
167 | }
168 |
169 | /** Color */
170 | .settings-panel-color {
171 | height: ${h*.7}em;
172 | width: ${h*.7}em;
173 | top: 0;
174 | bottom: 0;
175 | margin-top: auto;
176 | margin-bottom: auto;
177 | }
178 | .settings-panel-color-value {
179 | -webkit-appearance: none;
180 | -moz-appearance: none;
181 | -o-appearance:none;
182 | border: none;
183 | background: none;
184 | height: ${h}em;
185 | color: ${white};
186 | box-shadow: 0 2px ${dark};
187 | padding-left: ${h}em;
188 | width: 100%;
189 | }
190 | .settings-panel-color-value:hover,
191 | .settings-panel-color-value:focus {
192 | outline: none;
193 | color: ${white};
194 | box-shadow: 0 2px ${black};
195 | }
196 |
197 |
198 | /** Switch style */
199 | .settings-panel-switch {
200 | -webkit-appearance: none;
201 | -moz-appearance: none;
202 | appearance: none;
203 | margin-left: -1px;
204 | }
205 | .settings-panel-switch-input {
206 | display: none;
207 | }
208 | .settings-panel-switch-label {
209 | cursor: pointer;
210 | min-height: ${h}em;
211 | padding: 0 ${h/2}em;
212 | margin: 0 2px 2px 0;
213 | line-height: ${h*.999}em;
214 | color: ${notSoLight};
215 | border-radius: ${h}em;
216 | }
217 | .settings-panel-switch-label:hover {
218 | color: ${white};
219 | }
220 | .settings-panel-switch-label:last-child {
221 | margin-right: 0;
222 | }
223 | .settings-panel-switch-input:checked + .settings-panel-switch-label {
224 | color: ${white};
225 | box-shadow: 0 0 0 2px ${notSoLight};
226 | }
227 | .settings-panel-switch-input:checked + .settings-panel-switch-label:hover {
228 | box-shadow: 0 0 0 2px ${white};
229 | }
230 |
231 |
232 | /** Checkbox */
233 | .settings-panel-checkbox {
234 | display: none;
235 | }
236 | .settings-panel-checkbox-label {
237 | position: relative;
238 | margin-top: ${h/6}em;
239 | width: 100%;
240 | color: ${notSoLight};
241 | margin-bottom: ${h/6}em;
242 | }
243 | .settings-panel-checkbox-label:empty {
244 | margin-left: -${h/4}em;
245 | }
246 | .settings-panel-checkbox-label:after {
247 | content: 'x';
248 | color: transparent;
249 | position: absolute;
250 | top: -${h*.125}em;
251 | right: 0;
252 | margin-left: ${h/4}em;
253 | margin-top: 0;
254 | width: ${h*1.8}em;
255 | height: ${h*.85}em;
256 | line-height: ${h*.9}em;
257 | border-radius: ${h}em;
258 | margin-bottom: 0;
259 | background: ${dark};
260 | }
261 | .settings-panel-checkbox:hover + .settings-panel-checkbox-label:after,
262 | .settings-panel-checkbox-label:hover:after {
263 | background: ${black};
264 | }
265 | .settings-panel-checkbox-label:before {
266 | content: '';
267 | position: absolute;
268 | border-radius: ${h}em;
269 | width: ${h*.6}em;
270 | height: ${h*.6}em;
271 | top: 0;
272 | right: ${h*1.05}em;
273 | background: ${notSoLight};
274 | transition: .1s ease-in;
275 | z-index: 1;
276 | }
277 | .settings-panel-checkbox:checked + .settings-panel-checkbox-label:before {
278 | transform: translateX(${h*.9}em);
279 | background: ${white};
280 | }
281 |
282 |
283 | /** Button */
284 | .settings-panel-button {
285 | -webkit-appearance: none;
286 | -moz-appearance: none;
287 | appearance: none;
288 | border: none;
289 | outline: none;
290 | padding: ${h*.125}em;
291 | min-height: ${h}em;
292 | line-height: ${h}em;
293 | color: ${white};
294 | border-radius: ${h}em;
295 | background: none;
296 | text-transform: uppercase;
297 | font-size: .95em;
298 | letter-spacing: .1ex;
299 | box-shadow: inset 0 0 0 2px ${notSoLight};
300 | }
301 | .settings-panel-button:hover {
302 | color: ${white};
303 | box-shadow: 0 0 0 2px ${white};
304 | }
305 | .settings-panel-button:active {
306 | color: ${white};
307 | }
308 |
309 |
310 | /** Sliders */
311 | .settings-panel-range {
312 | -webkit-appearance: none;
313 | -moz-appearance: none;
314 | appearance: none;
315 | background: none;
316 | color: ${dark};
317 | border: 0;
318 | }
319 | .settings-panel-field--range:hover .settings-panel-range,
320 | .settings-panel-range:focus {
321 | color: ${black};
322 | outline: none;
323 | }
324 | .settings-panel-range::-webkit-slider-runnable-track {
325 | background: none;
326 | height: 2px;
327 | background: ${dark};
328 | }
329 | .settings-panel-field--range:hover .settings-panel-range::-webkit-slider-runnable-track,
330 | .settings-panel-range:focus::-webkit-slider-runnable-track {
331 | /**background: ${black};*/
332 | }
333 | .settings-panel-range::-moz-range-track {
334 | background: none;
335 | height: 2px;
336 | color: transparent;
337 | border: none;
338 | outline: none;
339 | background: ${dark};
340 | }
341 | .settings-panel-field--range:hover .settings-panel-range::-moz-range-track,
342 | .settings-panel-range:focus::-moz-range-track {
343 | background: ${black};
344 | }
345 | .settings-panel-range::-moz-range-progress {
346 | background: ${notSoLight};
347 | }
348 | .settings-panel-field--range:hover .settings-panel-range::-moz-range-progress,
349 | .settings-panel-range:focus::-moz-range-progress {
350 | background: ${white};
351 | }
352 | .settings-panel-range::-ms-track {
353 | height: 2px;
354 | color: transparent;
355 | border: none;
356 | outline: none;
357 | }
358 | .settings-panel-range::-ms-fill-lower {
359 | background: ${notSoLight};
360 | }
361 | .settings-panel-range::-ms-fill-upper {
362 | background: ${black};
363 | }
364 | .settings-panel-field--range:hover .settings-panel-range::-ms-fill-lower,
365 | .settings-panel-range:focus::-ms-fill-lower {
366 | background: ${white};
367 | }
368 |
369 | @supports (--css: variables) {
370 | .settings-panel-range {
371 | --active: ${notSoLight};
372 | --bg: ${dark};
373 | --track-background: linear-gradient(to right, var(--active) 0, var(--active) var(--value), var(--bg) 0) no-repeat;
374 | }
375 | .settings-panel-range::-webkit-slider-runnable-track {
376 | background: var(--track-background);
377 | }
378 | .settings-panel-range::-moz-range-track {
379 | background: var(--track-background);
380 | }
381 | .settings-panel-field--range:hover .settings-panel-range,
382 | .settings-panel-range:focus {
383 | --bg: ${black};
384 | --active: ${white};
385 | }
386 | }
387 |
388 | .settings-panel-range::-webkit-slider-thumb {
389 | background: ${notSoLight};
390 | height: ${h/2}em;
391 | width: ${h/2}em;
392 | border-radius: ${h/2}em;
393 | margin-top: -${h/4}em;
394 | border: 0;
395 | position: relative;
396 | top: 1px;
397 | -webkit-appearance: none;
398 | appearance: none;
399 | }
400 | .settings-panel-range:focus::-webkit-slider-thumb,
401 | .settings-panel-range:hover::-webkit-slider-thumb,
402 | .settings-panel-field--range:hover .settings-panel-range::-webkit-slider-thumb {
403 | background: ${white};
404 | }
405 | .settings-panel-range::-moz-range-thumb {
406 | background: ${notSoLight};
407 | height: ${h/2}em;
408 | width: ${h/2}em;
409 | border-radius: ${h/2}em;
410 | margin-top: -${h/4}em;
411 | border: 0;
412 | position: relative;
413 | top: 1px;
414 | -webkit-appearance: none;
415 | -moz-appearance: none;
416 | }
417 | .settings-panel-range:focus::-moz-range-thumb,
418 | .settings-panel-range:hover::-moz-range-thumb,
419 | .settings-panel-field--range:hover .settings-panel-range::-moz-range-thumb {
420 | background: ${white};
421 | }
422 | .settings-panel-range::-ms-thumb {
423 | appearance: none;
424 | outline: 0;
425 | border: none;
426 | position: relative;
427 | top: 1px;
428 | background: ${notSoLight};
429 | width: ${h/2}em;
430 | height: ${h/2}em;
431 | border-radius: ${h/2}em;
432 | cursor: pointer;
433 | }
434 | .settings-panel-range:focus::-ms-thumb,
435 | .settings-panel-range:hover::-ms-thumb,
436 | .settings-panel-field--range:hover .settings-panel-range::-ms-thumb {
437 | background: ${white};
438 | }
439 |
440 | .settings-panel-range-value {
441 | text-align: right;
442 | padding: 0;
443 | }
444 |
445 | :host.settings-panel-orientation-top .settings-panel-range,
446 | .settings-panel-orientation-top .settings-panel-range {
447 | width: 100%;
448 | }
449 | :host.settings-panel-orientation-top .settings-panel-range + .settings-panel-value,
450 | .settings-panel-orientation-top .settings-panel-range + .settings-panel-value {
451 | position: absolute;
452 | top: -${h*.8}em;
453 | right: 0%;
454 | text-align: right;
455 | }
456 |
457 | .settings-panel-field--color + .settings-panel-field--range,
458 | .settings-panel-field--color + .settings-panel-field--interval,
459 | .settings-panel-field--textarea + .settings-panel-field--range,
460 | .settings-panel-field--textarea + .settings-panel-field--interval,
461 | .settings-panel-field--text + .settings-panel-field--interval,
462 | .settings-panel-field--text + .settings-panel-field--range {
463 | margin-top: ${h/2.5}em;
464 | }
465 |
466 |
467 | /** Interval */
468 | .settings-panel-interval {
469 | background: none;
470 | }
471 | .settings-panel-interval:after {
472 | content: '';
473 | position: absolute;
474 | width: 100%;
475 | left: 0;
476 | bottom: 0;
477 | top: 0;
478 | background: ${dark};
479 | height: 2px;
480 | margin-top: auto;
481 | margin-bottom: auto;
482 | }
483 | .settings-panel-interval-handle {
484 | position: absolute;
485 | z-index: 1;
486 | height: 2px;
487 | top: 0;
488 | bottom: 0;
489 | margin-top: auto;
490 | margin-bottom: auto;
491 | background: ${notSoLight};
492 | }
493 | .settings-panel-field--interval:hover .settings-panel-interval:after {
494 | background: ${black};
495 | }
496 | .settings-panel-field--interval:hover .settings-panel-interval-handle {
497 | background: ${white};
498 | }
499 | .settings-panel-field--interval:hover .settings-panel-value {
500 | color: ${white};
501 | }
502 | .settings-panel-interval-value--right {
503 | text-align: right;
504 | }
505 | .settings-panel-interval-handle:after {
506 | content: '';
507 | position: absolute;
508 | right: -${h/4}em;
509 | top: 0;
510 | bottom: 0;
511 | margin: auto;
512 | height: ${h/2}em;
513 | width: ${h/2}em;
514 | border-radius: ${h/2}em;
515 | background: inherit;
516 | }
517 | .settings-panel-interval-handle:before {
518 | content: '';
519 | position: absolute;
520 | left: -${h/4}em;
521 | top: 0;
522 | bottom: 0;
523 | margin: auto;
524 | height: ${h/2}em;
525 | width: ${h/2}em;
526 | border-radius: ${h/2}em;
527 | background: inherit;
528 | }
529 |
530 | .settings-panel-interval-dragging .settings-panel-interval-handle {
531 | background: ${white};
532 | }
533 |
534 |
535 | /** Decorations */
536 | :host hr {
537 | border: none;
538 | height: 0;
539 | margin: ${h*.25}em 0;
540 | opacity: .333;
541 | border-bottom: 1px dotted ${notSoLight};
542 | }
543 | ::-webkit-input-placeholder {
544 | color: ${notSoLight};
545 | }
546 | ::-moz-placeholder {
547 | color: ${notSoLight};
548 | }
549 | :-ms-input-placeholder {
550 | color: ${notSoLight};
551 | }
552 | :-moz-placeholder {
553 | color: ${notSoLight};
554 | }
555 |
556 | ::-moz-selection {
557 | color: ${white};
558 | background: ${gray};
559 | }
560 | ::selection {
561 | color: ${white};
562 | background: ${gray};
563 | }
564 | .settings-panel-field--disabled {
565 | opacity: .333;
566 | pointer-events: none;
567 | }
568 | `;
569 |
570 |
571 | }
572 |
573 |
574 | function alpha (c, value) {
575 | return color(c).setAlpha(value).toString();
576 | }
577 |
--------------------------------------------------------------------------------
/theme/flat.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @module prama/theme/flat
3 | *
4 | * Control-panel theme on steroids
5 | */
6 | 'use strict';
7 |
8 | const px = require('add-px-to-style');
9 | const fonts = require('google-fonts');
10 | const color = require('tinycolor2');
11 | const scopeCss = require('scope-css');
12 | const none = require('./none');
13 | const interpolate = require('color-interpolate');
14 |
15 | module.exports = flat;
16 |
17 | //uses reflective scheme
18 | flat.palette = ['black', '#fff'];
19 | flat.palette = ['#272727', '#f95759', '#fff'];
20 | // flat.active = '#f95759';
21 |
22 | flat.fontSize = '14px';
23 | flat.fontFamily = '"Roboto", sans-serif';
24 | flat.labelWidth = '33.3%';
25 | flat.inputHeight = 2;
26 | flat.padding = 1/5;
27 |
28 | fonts.add({
29 | 'Roboto': 500,
30 | 'Material Icons': 400
31 | });
32 |
33 |
34 | function flat (opts) {
35 | opts = opts || {};
36 | let fs = opts.fontSize || flat.fontSize;
37 | let font = opts.fontFamily || flat.fontFamily;
38 | let h = opts.inputHeight || flat.inputHeight;
39 | let labelWidth = opts.labelWidth || flat.labelWidth;
40 | let padding = opts.padding || flat.padding;
41 |
42 | let palette = opts.palette || flat.palette;
43 | let pick = interpolate(palette);
44 |
45 | //NOTE: this is in case of scaling palette to black/white range
46 | let white = tone(1);
47 | let black = tone(0);
48 | let active = opts.active || tone(.5);
49 |
50 | function tone (amt) {
51 | return color(pick(amt)).toString();
52 | }
53 |
54 | //none theme defines sizes, the rest (ours) is up to style
55 | return none({
56 | fontSize: fs,
57 | fontFamily: font,
58 | inputHeight: h,
59 | labelWidth: labelWidth,
60 | padding: padding
61 | }) + `
62 | :host {
63 | background: ${white};
64 | color: ${black};
65 | font-family: ${font};
66 | font-weight: 500;
67 | -webkit-text-size-adjust: 100%;
68 | -webkit-font-smoothing: antialiased;
69 | }
70 | :host a {
71 | text-decoration: none;
72 | border-bottom: 1px solid ${alpha(tone(.0), .2)};
73 | }
74 | :host a:hover {
75 | text-decoration: none;
76 | border-bottom: 1px solid ${alpha(tone(.0), 1)};
77 | }
78 |
79 | .settings-panel-title {
80 | color: ${tone(.0)};
81 | font-family: ${font};
82 | font-weight: 500;
83 | }
84 |
85 | .settings-panel-label {
86 | color: ${alpha(tone(.0), .666)};
87 | font-weight: 500;
88 | }
89 |
90 | /** Text */
91 | .settings-panel-text,
92 | .settings-panel-textarea,
93 | .settings-panel-color-value {
94 | -webkit-appearance: none;
95 | -moz-appearance: none;
96 | -o-appearance: none;
97 | appearance: none;
98 | outline: none;
99 | border: 0;
100 | width: auto;
101 | border-radius: 0;
102 | font-weight: 500;
103 | background: none;
104 | color: ${active};
105 | box-shadow: 0 1px ${alpha(tone(.0), .2)};
106 | }
107 | .settings-panel-text:hover,
108 | .settings-panel-color-value:hover,
109 | .settings-panel-textarea:hover {
110 | color: ${active};
111 | }
112 | .settings-panel-text:focus,
113 | .settings-panel-color-value:focus,
114 | .settings-panel-textarea:focus {
115 | box-shadow: 0 1px ${active};
116 |
117 | }
118 |
119 |
120 | /** Sliders */
121 | .settings-panel-range {
122 | -webkit-appearance: none;
123 | -moz-appearance: none;
124 | appearance: none;
125 | background: none;
126 | color: ${tone(0)};
127 | border: 0;
128 | width: 85%;
129 | margin-right: ${h/4}em;
130 | }
131 | .settings-panel-range + .settings-panel-value {
132 | width: calc(15% - ${h/4}em);
133 | padding-left: 0;
134 | }
135 | .settings-panel-field--range:hover .settings-panel-range,
136 | .settings-panel-range:focus {
137 | outline: none;
138 | }
139 | .settings-panel-range::-webkit-slider-runnable-track {
140 | background: none;
141 | height: 2px;
142 | background: ${active};
143 | }
144 | .settings-panel-field--range:hover .settings-panel-range::-webkit-slider-runnable-track,
145 | .settings-panel-range:focus::-webkit-slider-runnable-track {
146 | /* background: ${active}; */
147 | }
148 | .settings-panel-range::-moz-range-track {
149 | background: none;
150 | height: 2px;
151 | background: ${active};
152 | }
153 | .settings-panel-field--range:hover .settings-panel-range::-moz-range-track,
154 | .settings-panel-range:focus::-moz-range-track {
155 | /* background: ${active}; */
156 | }
157 |
158 | .settings-panel-range::-ms-track {
159 | height: 2px;
160 | color: transparent;
161 | border: none;
162 | outline: none;
163 | }
164 | .settings-panel-range::-ms-fill-lower {
165 | background: ${active};
166 | }
167 | .settings-panel-range::-ms-fill-upper {
168 | background: ${alpha(active, .2)};
169 | }
170 |
171 | @supports (--css: variables) {
172 | .settings-panel-range {
173 | --active: ${active};
174 | --bg: ${alpha(active, .2)};
175 | --track-background: linear-gradient(to right, var(--active) 0, var(--active) var(--value), var(--bg) 0) no-repeat;
176 | }
177 | .settings-panel-range::-webkit-slider-runnable-track {
178 | background: var(--track-background);
179 | }
180 | .settings-panel-range::-moz-range-track {
181 | background: var(--track-background);
182 | }
183 | .settings-panel-field--range:hover .settings-panel-range,
184 | .settings-panel-range:focus {
185 | --bg: ${alpha(active, .2)};
186 | --active: ${active};
187 | }
188 | }
189 |
190 | .settings-panel-range::-webkit-slider-thumb {
191 | background: ${active};
192 | height: ${h/2}em;
193 | width: ${h/2}em;
194 | border-radius: ${h/2}em;
195 | margin-top: -${h/4}em;
196 | border: 0;
197 | position: relative;
198 | top: 1px;
199 | -webkit-appearance: none;
200 | appearance: none;
201 | transition: .05s ease-in transform;
202 | transform: scale(1, 1);
203 | transform-origin: center center;
204 | }
205 | .settings-panel-range:focus::-webkit-slider-thumb,
206 | .settings-panel-range::-webkit-slider-thumb:hover {
207 | box-shadow: 0 0 0 0;
208 | transform: scale(1.2, 1.2);
209 | }
210 | .settings-panel-range[value="0"]::-webkit-slider-thumb {
211 | background: ${white};
212 | box-shadow: inset 0 0 0 1.5px ${active};
213 | }
214 | .settings-panel-range::-moz-range-thumb {
215 | background: ${active};
216 | height: ${h/2}em;
217 | width: ${h/2}em;
218 | border-radius: ${h/2}em;
219 | margin-top: -${h/4}em;
220 | border: 0;
221 | position: relative;
222 | top: 1px;
223 | -moz-appearance: none;
224 | appearance: none;
225 | transition: .05s ease-in transform;
226 | transform: scale(1, 1);
227 | transform-origin: center center;
228 | }
229 | .settings-panel-range:focus::-moz-range-thumb,
230 | .settings-panel-range::-moz-range-thumb:hover {
231 | box-shadow: 0 0 0 0;
232 | transform: scale(1.2, 1.2);
233 | }
234 | .settings-panel-range[value="0"]::-moz-range-thumb {
235 | background: ${white};
236 | box-shadow: inset 0 0 0 1.5px ${active};
237 | }
238 | .settings-panel-range::-ms-thumb {
239 | background: ${active};
240 | height: ${h/2}em;
241 | width: ${h/2}em;
242 | border-radius: ${h/2}em;
243 | border: 0;
244 | position: relative;
245 | top: 1px;
246 | appearance: none;
247 | transition: .05s ease-in transform;
248 | transform: scale(1, 1);
249 | transform-origin: center center;
250 | }
251 | .settings-panel-range:focus::-ms-thumb,
252 | .settings-panel-range::-ms-thumb:hover {
253 | box-shadow: 0 0 0 0;
254 | transform: scale(1.2, 1.2);
255 | }
256 | .settings-panel-range[value="0"]::-ms-thumb {
257 | background: ${white};
258 | box-shadow: inset 0 0 0 1.5px ${active};
259 | }
260 |
261 | /** Interval */
262 | .settings-panel-interval {
263 | background: none;
264 | }
265 | .settings-panel-interval:after {
266 | content: '';
267 | position: absolute;
268 | width: 100%;
269 | left: 0;
270 | bottom: 0;
271 | top: 0;
272 | background: ${alpha(active, .2)};
273 | height: 2px;
274 | margin-top: auto;
275 | margin-bottom: auto;
276 | }
277 | .settings-panel-interval-handle {
278 | position: absolute;
279 | z-index: 1;
280 | height: 2px;
281 | top: 0;
282 | bottom: 0;
283 | margin-top: auto;
284 | margin-bottom: auto;
285 | background: ${active};
286 | }
287 | .settings-panel-field--interval:hover .settings-panel-interval:after {
288 | background: ${alpha(active, .2)};
289 | }
290 | .settings-panel-field--interval:hover .settings-panel-interval-handle {
291 | background: ${active};
292 | }
293 | .settings-panel-field--interval:hover .settings-panel-value {
294 | color: ${black};
295 | font-weight: 500;
296 | }
297 | .settings-panel-interval-handle:after,
298 | .settings-panel-interval-handle:before {
299 | content: '';
300 | position: absolute;
301 | right: -${h/4}em;
302 | top: 0;
303 | bottom: 0;
304 | margin: auto;
305 | height: ${h/2}em;
306 | width: ${h/2}em;
307 | border-radius: ${h/2}em;
308 | background: inherit;
309 | transform: scale(1, 1);
310 | transform-origin: center center;
311 | transition: .05s ease-in transform;
312 | }
313 | .settings-panel-interval-handle:before {
314 | left: -${h/4}em;
315 | right: auto;
316 | }
317 | .settings-panel-interval-dragging .settings-panel-interval-handle:after,
318 | .settings-panel-interval-dragging .settings-panel-interval-handle:before,
319 | .settings-panel-interval:hover .settings-panel-interval-handle:after,
320 | .settings-panel-interval:hover .settings-panel-interval-handle:before {
321 | transform: scale(1.2, 1.2);
322 | }
323 |
324 |
325 | /** Values */
326 | .settings-panel-value {
327 | color: ${tone(0)};
328 | font-weight: 500;
329 | }
330 | .settings-panel-value:first-child {
331 | margin-left: 0;
332 | }
333 | .settings-panel-value:hover,
334 | .settings-panel-value:focus {
335 | }
336 |
337 |
338 | /** Select */
339 | .settings-panel-select {
340 | font-family: inherit;
341 | color: inherit;
342 | border-radius: 0;
343 | outline: none;
344 | border: none;
345 | -webkit-appearance: none;
346 | -moz-appearance: none;
347 | -o-appearance:none;
348 | appearance:none;
349 | font-weight: 500;
350 | padding-right: 2em;
351 | margin-right: -1em;
352 | color: ${active};
353 | background: none;
354 | line-height: ${h}em;
355 | box-shadow: 0 1px ${alpha(tone(.0), .2)};
356 | width: auto;
357 | }
358 | .settings-panel-select:hover,
359 | .settings-panel-select:focus {
360 | }
361 | .settings-panel-select::-ms-expand {
362 | display: none;
363 | }
364 | .settings-panel-select-triangle {
365 | content: '';
366 | border-right: .3em solid transparent;
367 | border-left: .3em solid transparent;
368 | line-height: 2em;
369 | position: relative;
370 | z-index: 1;
371 | vertical-align: middle;
372 | display: inline-block;
373 | width: 0;
374 | text-align: center;
375 | pointer-events: none;
376 | }
377 | .settings-panel-select-triangle--down {
378 | top: 0em;
379 | left: .5em;
380 | border-top: .3em solid ${active};
381 | border-bottom: .0 transparent;
382 | }
383 | .settings-panel-select-triangle--up {
384 | display: none;
385 | }
386 | .settings-panel-field--select:hover .settings-panel-select,
387 | .settings-panel-select:focus {
388 | }
389 |
390 |
391 | /** Checkbox */
392 | .settings-panel-checkbox {
393 | display: none;
394 | }
395 | .settings-panel-checkbox-label {
396 | display: inline-block;
397 | color: ${tone(0)};
398 | position: relative;
399 | margin-right: ${h}em;
400 | /* margin-bottom: ${h/2}em; */
401 | }
402 | .settings-panel-checkbox-label:before {
403 | /*content: '✓';*/
404 | font-family: "Material Icons";
405 | content: '';
406 | font-weight: bolder;
407 | color: ${alpha(white, 0)};
408 | display: block;
409 | float: left;
410 | width: ${h*.5}em;
411 | height: ${h*.5}em;
412 | border-radius: .5px;
413 | position: relative;
414 | margin-right: ${h/3}em;
415 | margin-left: 2px;
416 | box-shadow: 0 0 0 2px ${alpha(tone(0), .9)};
417 | line-height: ${h/2}em;
418 | margin-top: 1px;
419 | text-align: center;
420 | }
421 | .settings-panel-checkbox-label:hover:before {
422 | box-shadow: 0 0 0 2px ${tone(0)};
423 | }
424 | .settings-panel-checkbox:checked + .settings-panel-checkbox-label {
425 | color: ${active};
426 | }
427 | .settings-panel-checkbox:checked + .settings-panel-checkbox-label:before {
428 | box-shadow: 0 0 0 2px ${active};
429 | background: ${active};
430 | color: ${tone(1)};
431 | }
432 | .settings-panel-checkbox-label:after {
433 | content: '';
434 | z-index: 1;
435 | position: absolute;
436 | width: ${h*1.5}em;
437 | height: ${h*1.5}em;
438 | background: ${tone(.1)};
439 | border-radius: ${h}em;
440 | top: -${h*.45}em;
441 | left: -${h*.5}em;
442 | opacity: 0;
443 | margin-left: 2px;
444 | transform-origin: center center;
445 | transform: scale(.5, .5);
446 | transition: .1s ease-out;
447 | }
448 | .settings-panel-checkbox-label:active:after {
449 | transform: scale(1, 1);
450 | opacity: .08;
451 | }
452 | .settings-panel-checkbox:checked + .settings-panel-checkbox-label:after {
453 | background: ${active};
454 | }
455 |
456 |
457 | /** Color */
458 | .settings-panel-color {
459 | height: ${h*.5}em;
460 | width: ${h*.5}em;
461 | display: inline-block;
462 | vertical-align: baseline;
463 | }
464 | .settings-panel-color-value {
465 | border: none;
466 | font-family: inherit;
467 | border-radius: 0;
468 | padding-left: ${h*.75}em;
469 | }
470 | .settings-panel-color-value:hover,
471 | .settings-panel-color-value:focus {
472 | outline: none;
473 | }
474 |
475 |
476 | /** Button */
477 | .settings-panel-button {
478 | text-align: center;
479 | border: none;
480 | text-transform: uppercase;
481 | color: ${tone(0)};
482 | font-weight: 500;
483 | background: none;
484 | width: auto;
485 | padding: ${h/3}em ${h/3}em;
486 | min-width: ${h*3}em;
487 | margin-top: -${h/4}em;
488 | margin-bottom: -${h/4}em;
489 | }
490 | .settings-panel-button:focus {
491 | outline: none;
492 | }
493 | .settings-panel-button:hover {
494 | background: ${alpha(tone(0), .08)};
495 | }
496 | .settings-panel-button:active {
497 | background: ${alpha(tone(0), .333)};
498 | }
499 |
500 |
501 | /** Switch style */
502 | .settings-panel-switch {
503 | }
504 | .settings-panel-switch-input {
505 | display: none;
506 | }
507 | .settings-panel-switch-label {
508 | position: relative;
509 | display: inline-block;
510 | margin: 0;
511 | margin-right: ${h*.75}em;
512 | z-index: 2;
513 | text-align: center;
514 | padding: 0 0;
515 | color: ${tone(0)};
516 | }
517 | .settings-panel-switch-input:checked + .settings-panel-switch-label {
518 | color: ${active};
519 | }
520 | .settings-panel-switch-input + .settings-panel-switch-label:hover {
521 | }
522 | .settings-panel-switch-label:hover {
523 | color: ${tone(0)};
524 | }
525 | .settings-panel-switch-label:active {
526 | color: ${tone(0)};
527 | }
528 | .settings-panel-switch-label:after {
529 | content: '';
530 | z-index: 1;
531 | position: absolute;
532 | width: ${h*2}em;
533 | height: ${h*2}em;
534 | min-width: 100%;
535 | min-height: 100%;
536 | background: ${tone(.1)};
537 | border-radius: ${h}em;
538 | top: 50%;
539 | left: 50%;
540 | margin-left: -${h}em;
541 | margin-top: -${h}em;
542 | opacity: 0;
543 | transform-origin: center center;
544 | transform: scale(.5, .5);
545 | transition: .1s ease-out;
546 | }
547 | .settings-panel-switch-label:active:after {
548 | transform: scale(1, 1);
549 | opacity: .08;
550 | }
551 | .settings-panel-checkbox:checked + .settings-panel-switch-label:after {
552 | background: ${active};
553 | }
554 |
555 | /** Decorations */
556 | ::-webkit-input-placeholder {
557 | color: ${alpha(active, .5)};
558 | }
559 | ::-moz-placeholder {
560 | color: ${alpha(active, .5)};
561 | }
562 | :-ms-input-placeholder {
563 | color: ${alpha(active, .5)};
564 | }
565 | :-moz-placeholder {
566 | color: ${alpha(active, .5)};
567 | }
568 | ::-moz-selection {
569 | background: ${active};
570 | color: ${white};
571 | }
572 | ::selection {
573 | background: ${active};
574 | color: ${white};
575 | }
576 | :host hr {
577 | opacity: 1;
578 | border-bottom: 1px solid ${alpha(tone(.0), .2)};
579 | margin-left: -${h*.666}em;
580 | margin-right: -${h*.666}em;
581 | margin-top: ${h*.75}em;
582 | }
583 | :host a {
584 | }
585 | :host a:hover {
586 | }
587 | `};
588 |
589 |
590 | function alpha (c, value) {
591 | return color(c).setAlpha(value).toString();
592 | }
593 |
--------------------------------------------------------------------------------
/theme/json.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @module prama/theme/json
3 | *
4 | * Json representation theme
5 | */
6 | 'use strict';
7 |
8 | const px = require('add-px-to-style');
9 | const none = require('./none');
10 |
11 | module.exports = json;
12 |
13 | json.palette = ['white', 'rgb(200,0,0)', 'rgb(40,40,40)'];
14 |
15 | json.fontSize = '12px';
16 | json.fontFamily = 'monospace';
17 | json.labelWidth = 'auto';
18 | json.inputHeight = 1.5;
19 |
20 | function json (opts) {
21 | opts = opts || {};
22 |
23 | let h = opts.inputHeight || json.inputHeight;
24 | let labelWidth = opts.labelWidth || json.labelWidth;
25 | let fontSize = opts.fontSize || json.fontSize;
26 | let font = opts.fontFamily || json.fontFamily;
27 |
28 | let palette = opts.palette || json.palette;
29 |
30 | let white = palette[0];
31 | let black = palette[palette.length - 1];
32 | let red = palette[palette.length - 2];
33 |
34 | return none({
35 | fontSize: fontSize,
36 | fontFamily: font,
37 | inputHeight: h,
38 | labelWidth: labelWidth,
39 | palette: [white, black]
40 | }) + `
41 | :host {
42 | -webkit-user-select: initial;
43 | -moz-user-select: initial;
44 | -ms-user-select: initial;
45 | user-select: initial;
46 | }
47 |
48 | .settings-panel-title {
49 | margin: 0;
50 | font-size: 1.4em;
51 | }
52 |
53 | .settings-panel-field {
54 | display: inline-block;
55 | }
56 |
57 | .settings-panel-label {
58 | display: inline-block;
59 | width: ${labelWidth};
60 | color: ${red};
61 | }
62 | .settings-panel-label:before {
63 | content: '"';
64 | opacity: .3;
65 | color: ${black};
66 | }
67 | .settings-panel-label:after {
68 | content: '":';
69 | opacity: .3;
70 | color: ${black};
71 | }
72 | .settings-panel-input {
73 | display: inline-block;
74 | min-height: 0;
75 | }
76 | /*
77 | .settings-panel-input:after {
78 | content: ',';
79 | color: ${black};
80 | opacity: .3;
81 | }
82 | */
83 |
84 | .settings-panel-range {
85 | display: none;
86 | }
87 | .settings-panel-range + .settings-panel-value {
88 | width: auto;
89 | }
90 | .settings-panel-value {
91 | padding: 0;
92 | }
93 |
94 |
95 | .settings-panel-text,
96 | .settings-panel-color-value,
97 | .settings-panel-select,
98 | .settings-panel-textarea {
99 | -webkit-appearance: none;
100 | -moz-appearance: none;
101 | -o-appearance:none;
102 | border: none;
103 | background: none;
104 | }
105 |
106 | .settings-panel-text:focus,
107 | .settings-panel-color-value:focus,
108 | .settings-panel-textarea:focus {
109 | outline: none;
110 | }
111 |
112 |
113 | .settings-panel-interval {
114 | display: none;
115 | }
116 | .settings-panel-field--interval {
117 | white-space: nowrap;
118 | }
119 | .settings-panel-field--interval .settings-panel-input:before {
120 | content: '[';
121 | opacity: .3;
122 | }
123 | .settings-panel-field--interval .settings-panel-input:after {
124 | content: ']';
125 | opacity: .3;
126 | }
127 |
128 | /** Decorations */
129 | :host hr {
130 | margin: 0;
131 | border: none;
132 | height: 0;
133 | }
134 | .settings-panel-field--disabled {
135 | opacity: .333;
136 | pointer-events: none;
137 | }
138 | `;
139 | };
--------------------------------------------------------------------------------
/theme/lucy.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @module prama/theme/lucy
3 | *
4 | * Round theme
5 | */
6 | 'use strict';
7 |
8 | const px = require('add-px-to-style');
9 | const fonts = require('google-fonts');
10 | const color = require('tinycolor2');
11 | const scopeCss = require('scope-css');
12 |
13 | fonts.add({
14 | 'Ubuntu Condensed': 400,
15 | 'Ubuntu Mono': true
16 | });
17 |
18 | module.exports = lucy;
19 |
20 | lucy.palette = [
21 | '#00FFFA',
22 | '#999',
23 | '#eee',
24 | '#222',
25 | '#333'
26 | ];
27 |
28 | function lucy () {
29 | let defaultPalette = lucy.palette;
30 |
31 | let palette = this.palette || defaultPalette;
32 |
33 | let bg = palette[4] || defaultPalette[4];
34 | let primary = palette[0];
35 | let secondary = palette[1];
36 | let active = palette[2];
37 | let dark = palette[3];
38 |
39 | let mono = '"Ubuntu Mono", monospace';
40 | let fontSize = this.fontSize || '1em';
41 |
42 | let css = `
43 | :host > .prama {
44 | background: none;
45 | overflow: visible;
46 | padding: 0;
47 | }
48 | .popoff-popup {
49 | min-width: 0;
50 | }
51 | .prama.popoff-sidebar .settings-panel {
52 | border-radius: 0;
53 | height: 100%;
54 | }
55 |
56 | .prama-button {
57 | color: ${bg};
58 | fill: ${bg};
59 | }
60 | .prama-button:hover {
61 | color: ${active};
62 | fill: ${active};
63 | }
64 |
65 | .popoff-close {
66 | color: ${secondary};
67 | }
68 | .popoff-close:hover {
69 | color: ${active};
70 | }
71 | `;
72 |
73 | css = scopeCss(css, '.prama-container-' + this.id).trim();
74 |
75 | //set panel css
76 | this.panel.css = `
77 | :host {
78 | box-shadow: 0 .5em 3em -1em ${dark};
79 | color: ${primary};
80 | background: ${alpha(bg, .9)};
81 | font-size: ${px('font-size', fontSize)};
82 | font-family: "Ubuntu Condensed", sans-serif;
83 | padding: 1em 1.5em;
84 | border-radius: .666em;
85 | }
86 |
87 | .settings-panel-title {
88 | font-size: 2.2em;
89 | font-weight: normal;
90 | letter-spacing: 0;
91 | text-transform: none;
92 | text-shadow: 0 0 .666em ${alpha(primary, .2)};
93 | }
94 |
95 | :host.settings-panel-orientation-top .settings-panel-field,
96 | :host.settings-panel-orientation-bottom .settings-panel-field {
97 | text-align: center;
98 | }
99 |
100 | .settings-panel-label {
101 | vertical-align: top;
102 | padding-top: .5em;
103 | }
104 | :host.settings-panel-orientation-left .settings-panel-label,
105 | :host.settings-panel-orientation-right .settings-panel-label {
106 | width: 6em;
107 | }
108 | :host.settings-panel-orientation-top .settings-panel-label,
109 | :host.settings-panel-orientation-bottom .settings-panel-label {
110 | width: 100%;
111 | }
112 |
113 | .settings-panel-field--interval .settings-panel-input,
114 | .settings-panel-field--range .settings-panel-input {
115 | text-align: left;
116 | }
117 |
118 | .settings-panel-textarea,
119 | .settings-panel-text,
120 | .settings-panel-select {
121 | padding-left: 0em;
122 | padding-right: 0em;
123 | text-align: left;
124 | }
125 |
126 | .settings-panel-textarea:hover,
127 | .settings-panel-text:hover,
128 | .settings-panel-select:hover {
129 | color: ${active};
130 | }
131 |
132 | /** Inputs fill */
133 | .settings-panel-interval,
134 | .settings-panel-value,
135 | .settings-panel-select,
136 | .settings-panel-text,
137 | .settings-panel-checkbox-label {
138 | background: none;
139 | font-family: ${mono};
140 | color: ${secondary};
141 | }
142 |
143 | /** Panel value */
144 | .settings-panel-value {
145 | padding-right: 0;
146 | }
147 | .settings-panel-value:focus {
148 | color: ${active};
149 | }
150 |
151 |
152 | /** Text */
153 | .settings-panel-text {
154 | border: none;
155 | background: none;
156 | }
157 |
158 | .settings-panel-textarea {
159 | background: none;
160 | border: 0;
161 | }
162 | .settings-panel-text:focus,
163 | .settings-panel-textarea:focus {
164 | outline: none;
165 | color: ${active};
166 | }
167 |
168 |
169 | /** Select */
170 | .settings-panel-select {
171 | background: none;
172 | outline: none;
173 | border: none;
174 | -webkit-appearance: none;
175 | -moz-appearance: none;
176 | -o-appearance:none;
177 | appearance:none;
178 | width: auto;
179 | padding-right: 1em;
180 | margin-right: -.5em;
181 | }
182 | .settings-panel-select::-ms-expand {
183 | display:none;
184 | }
185 | .settings-panel-select-triangle {
186 | content: ' ';
187 | border-right: .3em solid transparent;
188 | border-left: .3em solid transparent;
189 | line-height: 2em;
190 | position: relative;
191 | z-index: 1;
192 | vertical-align: middle;
193 | display: inline-block;
194 | width: 0;
195 | text-align: center;
196 | pointer-events: none;
197 | }
198 | .settings-panel-select-triangle--down {
199 | top: 0em;
200 | left: 0;
201 | border-top: .5em solid ${secondary};
202 | border-bottom: .0 transparent;
203 | }
204 | .settings-panel-select-triangle--up {
205 | display: none;
206 | }
207 | .settings-panel-select:focus {
208 | color: ${active}
209 | }
210 | .settings-panel-select:focus + .settings-panel-select-triangle {
211 | border-top-color: ${active};
212 | }
213 |
214 |
215 |
216 | /** Switch style */
217 | .settings-panel-switch {
218 | -webkit-appearance: none;
219 | -moz-appearance: none;
220 | appearance: none;
221 | color: ${secondary};
222 | font-family: ${mono};
223 | }
224 | .settings-panel-switch-input {
225 | display: none;
226 | }
227 | .settings-panel-switch-label {
228 | cursor: pointer;
229 | }
230 | .settings-panel-switch-label:hover {
231 | color: ${active};
232 | }
233 | .settings-panel-switch-label:last-child {
234 | margin-right: 0;
235 | }
236 | .settings-panel-switch-input:checked + .settings-panel-switch-label {
237 | color: ${active};
238 | font-weight: bold;
239 | }
240 |
241 |
242 | /** Slider */
243 | .settings-panel-range {
244 | text-align: left;
245 | -webkit-appearance: none;
246 | -moz-appearance: none;
247 | appearance: none;
248 | background: none;
249 | border-radius: 1em;
250 | color: ${secondary};
251 | margin-left: 15%;
252 | width: 70%;
253 | }
254 | .settings-panel-range:focus {
255 | outline: none;
256 | color: ${primary};
257 | }
258 | .settings-panel-range::-webkit-slider-runnable-track {
259 | background: ${secondary};
260 | height: 1px;
261 | }
262 | .settings-panel-range:focus::-webkit-slider-runnable-track {
263 | background: ${active};
264 | }
265 | .settings-panel-range::-moz-range-track {
266 | background: ${secondary};
267 | height: 1px;
268 | }
269 | .settings-panel-range:focus::-moz-range-track {
270 | background: ${active};
271 | }
272 | .settings-panel-range::-ms-fill-lower {
273 | background: ${secondary};
274 | }
275 | .settings-panel-range::-ms-fill-upper {
276 | background: ${secondary};
277 | }
278 |
279 | .settings-panel-range::-webkit-slider-thumb {
280 | background: ${active};
281 | border-radius: 1em;
282 | height: 1em;
283 | width: 1em;
284 | border: 0;
285 | cursor: ew-resize;
286 | -webkit-appearance: none;
287 | appearance: none;
288 | margin-top: -.5em;
289 | }
290 | .settings-panel-range::-moz-range-thumb {
291 | background: ${active};
292 | border-radius: 1em;
293 | height: 1em;
294 | width: 1em;
295 | border: 0;
296 | cursor: ew-resize;
297 | -webkit-appearance: none;
298 | margin-top: 0px;
299 | }
300 | .settings-panel-range::-ms-thumb {
301 | background: ${secondary};
302 | border-radius: 1em;
303 | }
304 | .settings-panel-field--interval .settings-panel-value:first-child {
305 | text-align: right;
306 | padding-left: 0;
307 | padding-right: .5em;
308 | }
309 | .settings-panel-interval:after {
310 | content: '';
311 | position: absolute;
312 | width: 100%;
313 | left: 0;
314 | background: ${secondary};
315 | height: 1px;
316 | margin-top: 1em;
317 | }
318 | .settings-panel-interval-handle {
319 | position: absolute;
320 | z-index: 1;
321 | height: 1px;
322 | margin-top: 1em;
323 | background: ${active};
324 | }
325 | .settings-panel-interval-handle:after {
326 | content: '';
327 | position: absolute;
328 | right: 0;
329 | top: -.5em;
330 | width: 1em;
331 | height: 1em;
332 | border-radius: 1em;
333 | background: inherit;
334 | }
335 | .settings-panel-interval-handle:before {
336 | content: '';
337 | position: absolute;
338 | left: 0;
339 | top: -.5em;
340 | width: 1em;
341 | height: 1em;
342 | border-radius: 1em;
343 | background: inherit;
344 | }
345 |
346 | .settings-panel-interval-dragging .settings-panel-interval-handle {
347 | background: ${active};
348 | }
349 |
350 | .settings-panel-field--range .settings-panel-input:before {
351 | content: attr(data-min);
352 | position: absolute;
353 | width: 15%;
354 | color: ${secondary};
355 | text-align: right;
356 | line-height: 2em;
357 | height: 2em;
358 | padding-right: .5em;
359 | box-sizing: border-box;
360 | }
361 |
362 |
363 | /** Checkbox */
364 | .settings-panel-field--checkbox .settings-panel-label {
365 | margin-bottom: .5em;
366 | }
367 | .settings-panel-checkbox {
368 | display: none;
369 | }
370 | .settings-panel-checkbox-label {
371 | position: relative;
372 | display: inline-block;
373 | vertical-align: top;
374 | width: 4.5em;
375 | height: 2em;
376 | cursor: pointer;
377 | background: ${secondary};
378 | border-radius: 2em;
379 | }
380 | .settings-panel-checkbox-label:before {
381 | position: absolute;
382 | content: "";
383 | height: 1em;
384 | width: 1em;
385 | left: .5em;
386 | bottom: .5em;
387 | background-color: ${active};
388 | -webkit-transition: .4s;
389 | transition: .4s;
390 | border-radius: 2em;
391 | }
392 | .settings-panel-checkbox:checked + .settings-panel-checkbox-label {
393 | background: ${secondary};
394 | }
395 | .settings-panel-checkbox:focus + .settings-panel-checkbox-label {
396 | }
397 | .settings-panel-checkbox:checked + .settings-panel-checkbox-label:before {
398 | left: calc(100% - 1.6em);
399 | background-color: ${active};
400 | }
401 |
402 |
403 | /** Color */
404 | .settings-panel-color {
405 | height: 1em;
406 | width: 1em;
407 | top: 0;
408 | bottom: 0;
409 | margin-top: auto;
410 | margin-bottom: auto;
411 | }
412 | .settings-panel-color-value {
413 | border: none;
414 | background: none;
415 | color: ${secondary};
416 | padding-left: 1.5em;
417 | width: 100%;
418 | }
419 | .settings-panel-color-value:focus {
420 | outline: none;
421 | color: ${active};
422 | }
423 |
424 |
425 | /** Button */
426 | .settings-panel-button {
427 | -webkit-appearance: none;
428 | -moz-appearance: none;
429 | appearance: none;
430 | border: none;
431 | outline: none;
432 | cursor: pointer;
433 | min-height: 2.5em;
434 | padding: .75em 1.5em;
435 | color: ${primary};
436 | box-shadow: 0 0 0 2px ${alpha(primary, .6)};
437 | background: none;
438 | margin-left: 6em;
439 | }
440 | .settings-panel-button:hover {
441 | box-shadow: 0 0 0 2px ${alpha(primary, 1)};
442 | color: ${primary};
443 | }
444 | .settings-panel-button:active {
445 | color: ${primary};
446 | }
447 |
448 |
449 | /** Decorations */
450 | :host hr {
451 | border: none;
452 | height: 0;
453 | margin: 1.25em 0;
454 | border-bottom: 1px dotted ${alpha(primary, .2)};
455 | }
456 | ::-webkit-input-placeholder {
457 | color: ${secondary};
458 | }
459 | ::-moz-placeholder {
460 | color: ${secondary};
461 | }
462 | :-ms-input-placeholder {
463 | color: ${secondary};
464 | }
465 | :-moz-placeholder {
466 | color: ${secondary};
467 | }
468 | ::-moz-selection {
469 | color: ${active};
470 | background: ${dark};
471 | }
472 | ::selection {
473 | color: ${active};
474 | background: ${dark};
475 | }
476 | `;
477 |
478 | return css;
479 | }
480 |
481 |
482 | function alpha (c, value) {
483 | return color(c).setAlpha(value).toString();
484 | }
485 |
486 | function darken (c, value) {
487 | return color(c).darken(value*100).toString();
488 | }
489 |
490 | function lighten (c, value) {
491 | return color(c).lighten(value*100).toString();
492 | }
--------------------------------------------------------------------------------
/theme/merka.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @module prama/theme/merka
3 | *
4 | * Rounded corners theme
5 | */
6 | 'use strict';
--------------------------------------------------------------------------------
/theme/none.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @module settings-panel/theme/none
3 | */
4 | 'use strict';
5 |
6 | const px = require('add-px-to-style');
7 |
8 | module.exports = none;
9 |
10 | none.palette = ['white', 'black'];
11 | none.fontSize = 13;
12 | none.fontFamily = 'sans-serif';
13 | none.labelWidth = '9em';
14 | none.inputHeight = 2;
15 | none.padding = 1/5;
16 |
17 | function none (opts) {
18 | opts = opts || {};
19 | let fs = opts.fontSize || none.fontSize;
20 | let font = opts.fontFamily || none.fontFamily;
21 | let h = opts.inputHeight || none.inputHeight;
22 | let labelWidth = opts.labelWidth || none.labelWidth;
23 | let padding = opts.padding || none.padding;
24 | let palette = opts.palette || none.palette;
25 | let white = palette[0];
26 | let black = palette[palette.length - 1];
27 |
28 | if (/[-0-9.]*/.test(fs)) fs = parseFloat(fs);
29 |
30 | //just size part
31 | return `
32 | :host {
33 | background: ${white};
34 | color: ${black};
35 | font-family: ${font};
36 | font-size: ${px('font-size', fs)};
37 | padding: ${h*2.5*padding}em;
38 | }
39 |
40 | .settings-panel-title {
41 | min-height: ${h}em;
42 | line-height: 1.5;
43 | text-align: left;
44 | font-size: ${px('font-size',fs*1.333)};
45 | padding: ${h * 2 * padding / 1.333}em ${h * padding / 1.333 }em;
46 | min-height: ${h/1.333}em;
47 | margin: 0;
48 | }
49 |
50 | .settings-panel-field {
51 | padding: ${h * padding}em;
52 | }
53 |
54 | :host.settings-panel-orientation-left .settings-panel-label,
55 | :host .settings-panel-orientation-left .settings-panel-label,
56 | :host.settings-panel-orientation-right .settings-panel-label,
57 | :host .settings-panel-orientation-right .settings-panel-label {
58 | width: ${px('width', labelWidth)};
59 | }
60 | :host.settings-panel-orientation-bottom .settings-panel-label {
61 | border-top-width: ${h}em;
62 | }
63 | :host.settings-panel-orientation-bottom .settings-panel-label + .settings-panel-input {
64 | top: ${h/8}em;
65 | }
66 | :host.settings-panel-orientation-left .settings-panel-label {
67 | padding-right: ${h/2}em;
68 | }
69 | :host.settings-panel-orientation-right .settings-panel-label {
70 | padding-left: ${h/2}em;
71 | }
72 | :host.settings-panel-orientation-right .settings-panel-label + .settings-panel-input {
73 | width: calc(100% - ${labelWidth});
74 | }
75 |
76 | .settings-panel-text,
77 | .settings-panel-textarea,
78 | .settings-panel-range,
79 | .settings-panel-interval,
80 | .settings-panel-select,
81 | .settings-panel-color,
82 | .settings-panel-color-value,
83 | .settings-panel-value {
84 | height: ${h}em;
85 | }
86 |
87 | .settings-panel-button,
88 | .settings-panel-input,
89 | .settings-panel-switch,
90 | .settings-panel-checkbox-group,
91 | .settings-panel-switch-label {
92 | min-height: ${h}em;
93 | }
94 | .settings-panel-input,
95 | .settings-panel-switch,
96 | .settings-panel-select,
97 | .settings-panel-checkbox-group,
98 | .settings-panel-switch-label {
99 | line-height: ${h}em;
100 | }
101 |
102 | .settings-panel-switch-label,
103 | .settings-panel-checkbox,
104 | .settings-panel-checkbox-label,
105 | .settings-panel-button {
106 | cursor: pointer;
107 | }
108 |
109 | .settings-panel-range::-webkit-slider-thumb {
110 | cursor: ew-resize;
111 | }
112 | .settings-panel-range::-moz-range-thumb {
113 | cursor: ew-resize;
114 | }
115 | .settings-panel-range::-ms-track {
116 | cursor: ew-resize;
117 | }
118 | .settings-panel-range::-ms-thumb {
119 | cursor: ew-resize;
120 | }
121 |
122 | /* Default triangle styles are from control theme, just set display: block */
123 | .settings-panel-select-triangle {
124 | display: none;
125 | position: absolute;
126 | border-right: .3em solid transparent;
127 | border-left: .3em solid transparent;
128 | line-height: ${h}em;
129 | right: 2.5%;
130 | height: 0;
131 | z-index: 1;
132 | pointer-events: none;
133 | }
134 | .settings-panel-select-triangle--up {
135 | top: ${h/2}em;
136 | margin-top: -${h/4 + h/24}em;
137 | border-bottom: ${h/4}em solid;
138 | border-top: 0px transparent;
139 | }
140 | .settings-panel-select-triangle--down {
141 | top: ${h/2}em;
142 | margin-top: ${h/24}em;
143 | border-top: ${h/4}em solid;
144 | border-bottom: .0 transparent;
145 | }
146 |
147 | :host hr {
148 | opacity: .5;
149 |
150 | color: ${black}
151 | }
152 | `;
153 | }
--------------------------------------------------------------------------------
/theme/pages.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dy/settings-panel/d973e5d88ec6f9e5c88268564425c38075eacc3a/theme/pages.js
--------------------------------------------------------------------------------
/theme/typer.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @module settings-panel/theme/typer
3 | *
4 | * White theme
5 | */
6 | 'use strict';
7 |
8 | const px = require('add-px-to-style');
9 | const fonts = require('google-fonts');
10 | const color = require('tinycolor2');
11 | const scopeCss = require('scope-css');
12 | const interpolate = require('color-interpolate');
13 | const none = require('./none');
14 |
15 |
16 | module.exports = typer;
17 |
18 | // typer.palette = ['#4B4E59', '#BCC1C7' ,'#F1F1F3'];
19 | // typer.palette = ['#32393F', '#3F4851', '#49565F', '#ADB7C0', '#F4FBFF'];
20 | // typer.palette = ['#111', '#eee'];
21 | typer.palette = ['white', 'black'];
22 | // typer.palette = ['#fff', '#24D4C0' ,'#21282E'];
23 | typer.active = '#24D4C0';
24 |
25 | typer.fontSize = 12;
26 | typer.fontFamily = '"Montserrat", sans-serif';
27 | typer.labelWidth = '9em';
28 | typer.inputHeight = 2;
29 | typer.padding = 1/5;
30 |
31 | //color balance
32 | typer.bg = .9;
33 | typer.radius = 2;
34 | typer.fg = .08;
35 |
36 | fonts.add({
37 | 'Montserrat': [400, 600],
38 | 'Material Icons': 400
39 | });
40 |
41 |
42 | function typer (opts) {
43 | opts = opts || {};
44 |
45 | let h = opts.inputHeight || typer.inputHeight;
46 | let labelWidth = opts.labelWidth || typer.labelWidth;
47 | let fontSize = opts.fontSize || typer.fontSize;
48 | let font = opts.fontFamily || typer.fontFamily;
49 | let radius = opts.radius || typer.radius;
50 | let padding = opts.padding || typer.padding;
51 |
52 | //background/active tones
53 | let bg = opts.bg || typer.bg;
54 | let fg = opts.fg || typer.fg;
55 |
56 |
57 | //palette
58 | let palette = opts.palette || typer.palette;
59 | let pick = interpolate(palette);
60 |
61 | //obtain palette sorted by brightnes
62 | let nPalette = palette.slice().sort((a, b) => color(a).getLuminance() - color(b).getLuminance());
63 | let npick = interpolate(nPalette);
64 |
65 | //the color of light/shadow to mix
66 | let light = color.mix('white', nPalette[nPalette.length - 1], 25).toString();
67 | let shadow = color.mix('black', nPalette[0], 25).toString();
68 |
69 | let active = opts.active || tone(.5);
70 |
71 | //helpers
72 | function tone (amt) {
73 | if (typeof amt === 'number') {
74 | amt = Math.max(Math.min(amt, 1), 0);
75 | return color(pick(amt)).toString();
76 | }
77 | return amt;
78 | }
79 | function ntone (amt) {
80 | if (typeof amt === 'number') {
81 | amt = Math.max(Math.min(amt, 1), 0);
82 | return color(npick(amt)).toString();
83 | }
84 | return amt;
85 | }
86 | function lighten (v, amt, t = tone) {
87 | return color(t(v)).lighten(amt*100);
88 | }
89 | function darken (v, amt, t = tone) {
90 | return color(t(v)).darken(amt*100);
91 | }
92 | function alpha (c, value) {
93 | return color(c).setAlpha(value).toString();
94 | }
95 |
96 | let inversed = color(palette[0]).getLuminance() > color(palette[palette.length - 1]).getLuminance();
97 |
98 |
99 | let pop = (v = .9, d = .05, t = tone) => `
100 | ${text(fg, v)}
101 | background-color: ${t(v)};
102 | background-image: linear-gradient(to bottom, ${lighten(v, d, t)}, ${darken(v, d, t)});
103 | box-shadow: inset 1px 0px ${alpha(light, .04)}, inset 0px 1px ${alpha(light, .15)}, inset 0px -1px 1px ${alpha(light, .07)}, 0 1px 2px ${alpha(shadow, .35)};
104 | `;
105 | let push = (v = .1, d = .05, t = tone) => `
106 | background: ${t(v)};
107 | /*background-image: linear-gradient(to bottom, ${darken(v, d, t)}, ${lighten(v, d, t)});*/
108 | box-shadow: inset 0 1px 2px ${alpha(shadow, .18)}, 0 1px ${alpha(light, .2)};
109 | color: ${t(1 - fg)};
110 | text-shadow: none;
111 | `;
112 | let text = (v, bg) => color(tone(v)).getLuminance() > color(tone(bg)).getLuminance() ? `
113 | color: ${tone(v)};
114 | background: ${tone(bg)};
115 | text-shadow: 0 -1px ${color.mix(tone(bg), shadow, 50)};
116 | ` : `
117 | color: ${tone(v)};
118 | background: ${tone(bg)};
119 | text-shadow: 0 1px ${color.mix(tone(bg), light, 50)};
120 | `;
121 |
122 | return none({
123 | fontSize: fontSize,
124 | fontFamily: font,
125 | inputHeight: h,
126 | labelWidth: labelWidth,
127 | palette: [tone(fg), tone(bg)],
128 | padding: padding
129 | }) + `
130 | :host {
131 | ${text(fg, bg)};
132 | box-shadow: inset 0 1px ${alpha(light, .15)}, 0 1.5px 11px -2px ${alpha(shadow, .666)};
133 | border-radius: ${radius*2}px;
134 | }
135 |
136 | :host a {
137 | text-decoration: none;
138 | border-bottom: 1px solid ${alpha(tone(.0), .1)};
139 | }
140 | :host a:hover {
141 | text-decoration: none;
142 | border-bottom: 1px solid ${alpha(tone(.0), 1)};
143 | }
144 |
145 | .settings-panel-title {
146 | font-weight: 400;
147 | ${text(fg, bg)};
148 | background: none;
149 | }
150 |
151 | .settings-panel-label {
152 | ${text(.25, bg)};
153 | background: none;
154 | }
155 |
156 | .settings-panel-field:hover .settings-panel-label {
157 | color: ${tone(fg)};
158 | }
159 |
160 |
161 |
162 | /** Values */
163 | .settings-panel-value {
164 | }
165 | .settings-panel-value:first-child {
166 | }
167 | .settings-panel-value:hover,
168 | .settings-panel-value:focus {
169 | }
170 |
171 |
172 |
173 | /** Sliders */
174 | .settings-panel-range {
175 | -webkit-appearance: none;
176 | -moz-appearance: none;
177 | appearance: none;
178 | background: none;
179 | border: 0;
180 | }
181 | .settings-panel-field--range:hover .settings-panel-range,
182 | .settings-panel-range:focus {
183 | outline: none;
184 | }
185 | .settings-panel-range::-webkit-slider-runnable-track {
186 | height: .5em;
187 | border-radius: .5em;
188 | ${push(fg, .05)}
189 | }
190 | .settings-panel-range::-moz-range-track {
191 | height: .5em;
192 | border-radius: .5em;
193 | ${push(fg, .05)}
194 | }
195 | .settings-panel-range::-ms-track {
196 | ${push(fg, .05)}
197 | outline: none;
198 | color: transparent;
199 | border: none;
200 | height: .5em;
201 | border-radius: .5em;
202 | }
203 | .settings-panel-range::-ms-fill-lower {
204 | border-radius: .5em;
205 | ${push(fg, .05)}
206 | }
207 | .settings-panel-range::-ms-fill-upper {
208 | border-radius: .5em;
209 | ${push(.93, .05)}
210 | }
211 |
212 | @supports (--css: variables) {
213 | .settings-panel-range {
214 | width: 100%;
215 | --active: ${active};
216 | --bg: ${tone(.9)};
217 | --track-background: linear-gradient(to right, var(--active) 0, var(--active) var(--value), var(--bg) 0) no-repeat;
218 | }
219 | .settings-panel-range::-webkit-slider-runnable-track {
220 | background: var(--track-background);
221 | }
222 | .settings-panel-range::-moz-range-track {
223 | background: var(--track-background);
224 | }
225 | .settings-panel-field--range .settings-panel-input {
226 | margin-right: ${h}em;
227 | }
228 | .settings-panel-field--range:hover .settings-panel-range,
229 | .settings-panel-range:focus {
230 | --bg: ${tone(.93)};
231 | }
232 | .settings-panel-range-value {
233 | display: none;
234 | position: absolute;
235 | top: -${h*1.25}em;
236 | text-align: center;
237 | padding: 0;
238 | color: ${tone(fg)};
239 | background: ${tone(1)};
240 | box-shadow: 0 1px 5px -1px ${alpha(shadow, .5)};
241 | border-radius: ${radius}px;
242 | z-index: 3;
243 | margin-left: ${-h*.65}em;
244 | width: ${h*2}em;
245 | text-shadow: none;
246 | left: calc(var(--value) - var(--coef) * ${h*.8}em);
247 | }
248 | .settings-panel-field--range .settings-panel-value-tip {
249 | position: absolute;
250 | height: 0;
251 | top: -${h*.25}em;
252 | left: calc(var(--value) - var(--coef) * ${h*.8}em);
253 | margin-left: ${h*.1}em;
254 | display: none;
255 | z-index: 3;
256 | border-top: ${h*.3}em solid ${tone(1)};
257 | border-left: ${h*.3}em solid transparent;
258 | border-right: ${h*.3}em solid transparent;
259 | border-bottom: none;
260 | }
261 | .settings-panel-input:before {
262 | border-top: ${h*.3}em solid ${alpha(shadow, .25)};
263 | }
264 | .settings-panel-field--range:hover .settings-panel-value-tip,
265 | .settings-panel-range:focus ~ .settings-panel-value-tip {
266 | display: block;
267 | }
268 | .settings-panel-field--range:hover .settings-panel-value,
269 | .settings-panel-range:focus ~ .settings-panel-value {
270 | display: block;
271 | }
272 | }
273 |
274 | .settings-panel-range::-webkit-slider-thumb {
275 | ${pop(fg, -.05)};
276 | height: ${h*.8}em;
277 | width: ${h*.8}em;
278 | border-radius: ${h*.8}em;
279 | margin-top: -${h*.4}em;
280 | border: 0;
281 | position: relative;
282 | top: .25em;
283 | -webkit-appearance: none;
284 | appearance: none;
285 | z-index: 3;
286 | }
287 | .settings-panel-range:focus::-webkit-slider-thumb,
288 | .settings-panel-range:hover::-webkit-slider-thumb,
289 | .settings-panel-field--range:hover .settings-panel-range::-webkit-slider-thumb {
290 | }
291 | .settings-panel-range::-webkit-slider-thumb:active {
292 | }
293 |
294 | .settings-panel-range::-moz-range-thumb {
295 | ${pop(fg, -.05)};
296 | height: ${h*.8}em;
297 | width: ${h*.8}em;
298 | border-radius: ${h*.8}em;
299 | margin-top: -${h*.4}em;
300 | border: 0;
301 | position: relative;
302 | top: .25em;
303 | -moz-appearance: none;
304 | appearance: none;
305 | z-index: 3;
306 | }
307 | .settings-panel-range:focus::-moz-range-thumb,
308 | .settings-panel-range::-moz-range-thumb:hover,
309 | .settings-panel-field--range:hover .settings-panel-range::-moz-range-thumb {
310 | }
311 | .settings-panel-range::-moz-range-thumb:active {
312 | }
313 |
314 | .settings-panel-range::-ms-thumb {
315 | ${pop(fg, -.05)};
316 | height: ${h*.8}em;
317 | width: ${h*.8}em;
318 | border-radius: ${h*.8}em;
319 | border: 0;
320 | position: relative;
321 | top: .25em;
322 | -ms-appearance: none;
323 | appearance: none;
324 | z-index: 3;
325 | }
326 | .settings-panel-range:focus::-ms-thumb,
327 | .settings-panel-range:hover::-ms-thumb,
328 | .settings-panel-field--range:hover .settings-panel-range::-ms-thumb {
329 | }
330 | .settings-panel-range::-ms-thumb:active {
331 | }
332 |
333 |
334 | /** Interval */
335 | .settings-panel-interval {
336 | background: none;
337 | }
338 | .settings-panel-interval:after {
339 | content: '';
340 | position: absolute;
341 | width: 100%;
342 | left: 0;
343 | bottom: 0;
344 | top: 0;
345 | height: .5em;
346 | border-radius: .5em;
347 | margin-top: auto;
348 | margin-bottom: auto;
349 | ${push(.9, .05)}
350 | background: ${tone(.9)};
351 | }
352 | .settings-panel-field--interval:hover .settings-panel-interval:after,
353 | .settings-panel-interval-dragging .settings-panel-interval:after {
354 | background: ${tone(.93)};
355 | }
356 | .settings-panel-interval-handle {
357 | position: absolute;
358 | z-index: 1;
359 | top: 0;
360 | height: .5em;
361 | bottom: 0;
362 | margin-top: auto;
363 | margin-bottom: auto;
364 | background: ${active};
365 | }
366 | .settings-panel-interval-handle:after,
367 | .settings-panel-interval-handle:before {
368 | content: '';
369 | position: absolute;
370 | right: -${h*.4}em;
371 | top: 0;
372 | bottom: 0;
373 | margin: auto;
374 | height: ${h*.8}em;
375 | width: ${h*.8}em;
376 | border-radius: ${h*.8}em;
377 | ${pop(fg, -.05)};
378 | }
379 | .settings-panel-interval-handle:before {
380 | left: -${h*.4}em;
381 | right: auto;
382 | }
383 |
384 | .settings-panel-field--interval:hover .settings-panel-interval-handle:after,
385 | .settings-panel-field--interval:hover .settings-panel-interval-handle:before,
386 | .settings-panel-interval-dragging .settings-panel-interval-handle:after,
387 | .settings-panel-interval-dragging .settings-panel-interval-handle:before {
388 | ${pop(fg, -.05)};
389 | }
390 |
391 | @supports (--css: variables) {
392 | .settings-panel-interval {
393 | width: 100%;
394 | }
395 |
396 | .settings-panel-interval-value {
397 | position: absolute;
398 | top: -${h*1.25}em;
399 | text-align: center;
400 | padding: 0;
401 | display: none;
402 | color: ${tone(fg)};
403 | background: ${tone(1)};
404 | box-shadow: 0 1px 5px -1px ${alpha(shadow, .5)};
405 | border-radius: ${radius}px;
406 | z-index: 3;
407 | margin-left: ${-h}em;
408 | width: ${h*2}em;
409 | text-shadow: none;
410 | left: var(--value);
411 | }
412 |
413 | .settings-panel-field--interval .settings-panel-value-tip {
414 | position: absolute;
415 | height: 0;
416 | display: none;
417 | top: -${h*.25}em;
418 | left: var(--low);
419 | margin-left: ${-h*.3}em;
420 | z-index: 3;
421 | border-top: ${h*.3}em solid ${tone(1)};
422 | border-left: ${h*.3}em solid transparent;
423 | border-right: ${h*.3}em solid transparent;
424 | border-bottom: none;
425 | }
426 | .settings-panel-interval-value--right + .settings-panel-value-tip {
427 | left: var(--high);
428 | }
429 |
430 | .settings-panel-input:before {
431 | border-top: ${h*.3}em solid ${alpha(shadow, .25)};
432 | }
433 | .settings-panel-field--interval:hover .settings-panel-interval-value,
434 | .settings-panel-interval-dragging .settings-panel-interval-value {
435 | display: block;
436 | }
437 | @media (min-width: 640px) {
438 | .settings-panel-field--interval:hover .settings-panel-value-tip,
439 | .settings-panel-interval-dragging .settings-panel-value-tip {
440 | display: block;
441 | }
442 | }
443 | }
444 |
445 |
446 | /** Switch style */
447 | .settings-panel-switch {
448 | }
449 | .settings-panel-switch-input {
450 | display: none;
451 | }
452 | .settings-panel-switch-label {
453 | position: relative;
454 | display: inline-block;
455 | padding: 0 ${h/2}em;
456 | margin: 0;
457 | z-index: 2;
458 | text-align: center;
459 | ${pop(bg * .95, .07)};
460 | color: ${tone(.25)};
461 | }
462 | .settings-panel-switch-input:checked + .settings-panel-switch-label {
463 | ${push(.95, bg)};
464 | color: ${tone(fg)};
465 | }
466 |
467 | .settings-panel-switch-input:first-child + .settings-panel-switch-label {
468 | border-top-left-radius: 2px;
469 | border-bottom-left-radius: 2px;
470 | }
471 | .settings-panel-switch-label:last-child {
472 | border-top-right-radius: 2px;
473 | border-bottom-right-radius: 2px;
474 | }
475 |
476 | .settings-panel-switch-label:hover {
477 | ${pop(bg * .95 + (inversed ? -.07 : .07), .07)};
478 | }
479 | .settings-panel-switch-label:active {
480 | ${pop(bg * .95 + (inversed ? .07 : -.07), .07)};
481 | }
482 |
483 |
484 | /** Select */
485 | .settings-panel-select {
486 | border-radius: ${radius}px;
487 | padding-left: ${h/4}em;
488 | padding-right: ${h/2}em;
489 | margin-right: -${h/8}em;
490 | min-width: 4em;
491 | outline: none;
492 | border: none;
493 | -webkit-appearance: none;
494 | -moz-appearance: none;
495 | -o-appearance:none;
496 | appearance:none;
497 | ${pop(bg * .95, .07)};
498 | color: ${tone(.25)};
499 | }
500 | .settings-panel-select:hover,
501 | .settings-panel-select:active,
502 | .settings-panel-select:focus {
503 | ${pop(bg * .95 + (inversed ? -.07 : .07), .07)};
504 | }
505 | .settings-panel-select::-ms-expand {
506 | display: none;
507 | }
508 | .settings-panel-select-triangle {
509 | color: inherit;
510 | display: block;
511 | transform: scale(.8);
512 | }
513 |
514 |
515 | /** Button */
516 | .settings-panel-button {
517 | text-align: center;
518 | border: none;
519 | border-radius: ${radius}px;
520 | ${pop(bg * .95, .07)};
521 | color: ${tone(.25)};
522 | }
523 | .settings-panel-button:focus {
524 | outline: none;
525 | }
526 | .settings-panel-button:hover {
527 | ${pop(bg * .95 + (inversed ? -.07 : .07), .07)};
528 | color: ${tone(fg)};
529 | }
530 | .settings-panel-button:active {
531 | ${push(fg, .05)};
532 | }
533 |
534 |
535 | /** Text */
536 | .settings-panel-text,
537 | .settings-panel-textarea {
538 | -webkit-appearance: none;
539 | -moz-appearance: none;
540 | -o-appearance:none;
541 | border: none;
542 | height: ${h}em;
543 | padding: 0;
544 | width: 100%;
545 | border-radius: ${radius}px;
546 | padding-left: .4em;
547 | ${push(bg*.95)};
548 | color: ${tone(fg)};
549 | text-shadow: none;
550 | }
551 | .settings-panel-textarea {
552 | padding-top: .35em;
553 | }
554 |
555 | .settings-panel-text:hover,
556 | .settings-panel-textarea:hover,
557 | .settings-panel-text:focus,
558 | .settings-panel-textarea:focus {
559 | ${push(bg * .95 - .04)};
560 | color: ${tone(fg)};
561 | outline: none;
562 | }
563 |
564 | /** Color */
565 | .settings-panel-color {
566 | position: relative;
567 | width: ${h}em;
568 | border-top-left-radius: 3px;
569 | border-bottom-left-radius: 3px;
570 | display: inline-block;
571 | vertical-align: baseline;
572 | box-shadow: 0 1px ${alpha(light, .2)};
573 | }
574 | .settings-panel-color-value {
575 | -webkit-appearance: none;
576 | -moz-appearance: none;
577 | -o-appearance:none;
578 | border: none;
579 | padding-left: ${h/4}em;
580 | width: calc(100% - ${h}em);
581 | font-family: inherit;
582 | border-top-left-radius: 0;
583 | border-bottom-left-radius: 0;
584 | border-top-right-radius: 3px;
585 | border-bottom-right-radius: 3px;
586 | ${push(bg*.95)};
587 | color: ${tone(fg)};
588 | text-shadow: none;
589 | }
590 | .settings-panel-color-value:hover,
591 | .settings-panel-color-value:focus {
592 | outline: none;
593 | ${push(bg * .95 - .04)};
594 | color: ${tone(fg)};
595 | }
596 |
597 |
598 | /** Checkbox */
599 | .settings-panel-checkbox {
600 | display: none;
601 | }
602 | .settings-panel-checkbox-label {
603 | display: inline-block;
604 | ${text(.25, bg)};
605 | position: relative;
606 | margin-right: ${h}em;
607 | background: none;
608 | }
609 | .settings-panel-checkbox-label:before {
610 | font-family: "Material Icons";
611 | content: '';
612 | font-weight: bolder;
613 | font-size: ${h*.75}em;
614 | display: block;
615 | float: left;
616 | width: 2em;
617 | margin-right: -2em;
618 | margin-top: -${h*.1}em;
619 | opacity: 0;
620 | z-index: 1;
621 | position: relative;
622 | color: ${tone(fg)};
623 | text-shadow: 0 1px 2px ${alpha(shadow, .5)};
624 | }
625 | .settings-panel-checkbox-label:after {
626 | content: '';
627 | display: block;
628 | float: left;
629 | margin-top: -${h*.05}em;
630 | width: ${h*.666}em;
631 | height: ${h*.666}em;
632 | border-radius: ${radius}px;
633 | position: relative;
634 | margin-right: ${h/3}em;
635 | line-height: ${h/2}em;
636 | text-align: center;
637 | z-index: 0;
638 | ${push(.915)};
639 | }
640 | .settings-panel-checkbox-label:hover {
641 | color: ${tone(fg)};
642 | }
643 | .settings-panel-checkbox-label:hover:after {
644 | ${push(.93, .07)};
645 | }
646 | .settings-panel-checkbox:checked + .settings-panel-checkbox-label {
647 | }
648 | .settings-panel-checkbox:checked + .settings-panel-checkbox-label:before {
649 | opacity: 1;
650 | }
651 | .settings-panel-checkbox:checked + .settings-panel-checkbox-label:after {
652 | ${push(.93, .1)};
653 | }
654 |
655 |
656 | /** Decorations */
657 | ::-webkit-input-placeholder {
658 | color: ${alpha(tone(0), .5)};
659 | }
660 | ::-moz-placeholder {
661 | color: ${alpha(tone(0), .5)};
662 | }
663 | :-ms-input-placeholder {
664 | color: ${alpha(tone(0), .5)};
665 | }
666 | :-moz-placeholder {
667 | color: ${alpha(tone(0), .5)};
668 | }
669 | ::-moz-selection {
670 | background: ${tone(fg)};
671 | color: ${tone(bg)};
672 | }
673 | ::selection {
674 | background: ${tone(fg)};
675 | color: ${tone(bg)};
676 | }
677 | :host hr {
678 | border: none;
679 | height: 3px;
680 | border-radius: ${radius}px;
681 | margin: ${h/3}em 0;
682 | ${push(bg * .98, .05)};
683 | }
684 | `;
685 | };
686 |
--------------------------------------------------------------------------------