@decidables/decidables-elements 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/README.md +54 -0
  3. package/gulpfile.js +25 -0
  4. package/lib/decidablesElements.esm.js +4889 -0
  5. package/lib/decidablesElements.esm.js.map +1 -0
  6. package/lib/decidablesElements.esm.min.js +13 -0
  7. package/lib/decidablesElements.esm.min.js.map +1 -0
  8. package/lib/decidablesElements.umd.js +4907 -0
  9. package/lib/decidablesElements.umd.js.map +1 -0
  10. package/lib/decidablesElements.umd.min.js +13 -0
  11. package/lib/decidablesElements.umd.min.js.map +1 -0
  12. package/package.json +46 -0
  13. package/src/button.js +93 -0
  14. package/src/converter-array.js +15 -0
  15. package/src/converter-set.js +15 -0
  16. package/src/decidables-element.js +264 -0
  17. package/src/index.js +11 -0
  18. package/src/slider.js +333 -0
  19. package/src/spinner.js +149 -0
  20. package/src/switch.js +179 -0
  21. package/src/toggle-option.js +163 -0
  22. package/src/toggle.js +58 -0
  23. package/test/button.test.js +29 -0
  24. package/test/converter-array.test.js +14 -0
  25. package/test/converter-set.test.js +15 -0
  26. package/test/coverage/lcov-report/base.css +224 -0
  27. package/test/coverage/lcov-report/block-navigation.js +87 -0
  28. package/test/coverage/lcov-report/button.js.html +364 -0
  29. package/test/coverage/lcov-report/converter-array.js.html +130 -0
  30. package/test/coverage/lcov-report/converter-set.js.html +130 -0
  31. package/test/coverage/lcov-report/decidables-element.js.html +877 -0
  32. package/test/coverage/lcov-report/favicon.png +0 -0
  33. package/test/coverage/lcov-report/index.html +236 -0
  34. package/test/coverage/lcov-report/prettify.css +1 -0
  35. package/test/coverage/lcov-report/prettify.js +2 -0
  36. package/test/coverage/lcov-report/slider.js.html +1084 -0
  37. package/test/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  38. package/test/coverage/lcov-report/sorter.js +196 -0
  39. package/test/coverage/lcov-report/spinner.js.html +532 -0
  40. package/test/coverage/lcov-report/switch.js.html +622 -0
  41. package/test/coverage/lcov-report/toggle-option.js.html +574 -0
  42. package/test/coverage/lcov-report/toggle.js.html +259 -0
  43. package/test/coverage/lcov.info +1480 -0
  44. package/test/decidables-element.test.js +10 -0
  45. package/test/slider.test.js +64 -0
  46. package/test/spinner.test.js +55 -0
  47. package/test/switch.test.js +71 -0
  48. package/test/toggle-option.test.js +62 -0
  49. package/test/toggle.test.js +98 -0
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@decidables/decidables-elements",
3
+ "version": "0.0.1",
4
+ "description": "decidables-elements: Basic UI Web Components for the decidables project",
5
+ "type": "module",
6
+ "main": "./lib/decidablesElements.umd.min.js",
7
+ "browser": "./lib/decidablesElements.umd.min.js",
8
+ "module": "./lib/decidablesElements.esm.min.js",
9
+ "exports": {
10
+ "./lib/*.js": "./lib/*.js",
11
+ ".": "./src/index.js",
12
+ "./*": "./src/*.js"
13
+ },
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "https://github.com/decidables/decidables.git",
17
+ "directory": "libraries/decidables-elements"
18
+ },
19
+ "bugs": "https://github.com/decidables/decidables/issues",
20
+ "homepage": "https://github.com/decidables/decidables/tree/main/libraries/decidables-elements",
21
+ "author": "Adam Krawitz (https://web.uvic.ca/psyc/krawitz/)",
22
+ "license": "GPL-3.0",
23
+ "publishConfig": {
24
+ "access": "public"
25
+ },
26
+ "scripts": {
27
+ "lint": "gulp lint",
28
+ "test": "web-test-runner test/**/*.test.js --config ../../.webtestrunnerrc.js",
29
+ "test-file": "web-test-runner $@ --config ../../.webtestrunnerrc.js",
30
+ "build": "gulp build"
31
+ },
32
+ "browserslist": [
33
+ "> 0.25%",
34
+ "not dead"
35
+ ],
36
+ "devDependencies": {
37
+ "@open-wc/testing": "^3.1.1",
38
+ "@web/test-runner": "^0.13.27",
39
+ "gulp": "^4.0.2"
40
+ },
41
+ "dependencies": {
42
+ "d3": "^7.3.0",
43
+ "lit": "^2.1.2"
44
+ },
45
+ "gitHead": "560272dc4d2c353ff4cffa76a27d1dc5343674b2"
46
+ }
package/src/button.js ADDED
@@ -0,0 +1,93 @@
1
+
2
+ import {html, css} from 'lit';
3
+
4
+ import DecidablesElement from './decidables-element';
5
+
6
+ export default class DecidablesButton extends DecidablesElement {
7
+ static get properties() {
8
+ return {
9
+ disabled: {
10
+ attribute: 'disabled',
11
+ type: Boolean,
12
+ reflect: true,
13
+ },
14
+ };
15
+ }
16
+
17
+ constructor() {
18
+ super();
19
+
20
+ // Attributes
21
+ this.disabled = false;
22
+ }
23
+
24
+ static get styles() {
25
+ return [
26
+ super.styles,
27
+ css`
28
+ :host {
29
+ margin: 0.25rem;
30
+ }
31
+
32
+ button {
33
+ width: 100%;
34
+ height: 100%;
35
+ padding: 0.375rem 0.75rem;
36
+
37
+ font-family: var(---font-family-base);
38
+ font-size: 1.125rem;
39
+ line-height: 1.5;
40
+ color: var(---color-text-inverse);
41
+
42
+ border: 0;
43
+ border-radius: 0.25rem;
44
+ outline: none;
45
+ }
46
+
47
+ button:disabled {
48
+ background-color: var(--decidables-button-background-color, var(---color-element-disabled));
49
+ outline: none;
50
+ box-shadow: none;
51
+ }
52
+
53
+ button:enabled {
54
+ cursor: pointer;
55
+
56
+ background-color: var(--decidables-button-background-color, var(---color-element-enabled));
57
+ outline: none;
58
+ box-shadow: var(---shadow-2);
59
+ }
60
+
61
+ button:enabled:hover {
62
+ outline: none;
63
+ box-shadow: var(---shadow-4);
64
+ }
65
+
66
+ button:enabled:active {
67
+ outline: none;
68
+ box-shadow: var(---shadow-8);
69
+ }
70
+
71
+ :host(.keyboard) button:enabled:focus {
72
+ outline: none;
73
+ box-shadow: var(---shadow-4);
74
+ }
75
+
76
+ :host(.keyboard) button:enabled:focus:active {
77
+ outline: none;
78
+ box-shadow: var(---shadow-8);
79
+ }
80
+ `,
81
+ ];
82
+ }
83
+
84
+ render() {
85
+ return html`
86
+ <button ?disabled=${this.disabled}>
87
+ <slot></slot>
88
+ </button>
89
+ `;
90
+ }
91
+ }
92
+
93
+ customElements.define('decidables-button', DecidablesButton);
@@ -0,0 +1,15 @@
1
+
2
+ /*
3
+ Attribute: Space-separated sequence of numbers
4
+ Property: Array of numbers
5
+ */
6
+ const DecidablesConverterArray = {
7
+ fromAttribute: (value) => {
8
+ return value.split(/\s+/).map((item) => { return Number.parseFloat(item); });
9
+ },
10
+ toAttribute: (value) => {
11
+ return (value.length) ? value.map((item) => { return item.toFixed(3); }).join(' ') : null;
12
+ },
13
+ };
14
+
15
+ export default DecidablesConverterArray;
@@ -0,0 +1,15 @@
1
+
2
+ /*
3
+ Attribute: Space-separated sequence of strings
4
+ Property: Set of strings
5
+ */
6
+ const DecidablesConverterSet = {
7
+ fromAttribute: (value) => {
8
+ return new Set(value.split(/\s+/));
9
+ },
10
+ toAttribute: (value) => {
11
+ return (value.size) ? [...value].join(' ') : null;
12
+ },
13
+ };
14
+
15
+ export default DecidablesConverterSet;
@@ -0,0 +1,264 @@
1
+
2
+ import {
3
+ LitElement,
4
+ css,
5
+ svg,
6
+ unsafeCSS,
7
+ } from 'lit';
8
+ import * as d3 from 'd3';
9
+
10
+ /*
11
+ DecidablesElement Base Class - Not intended for instantiation!
12
+ <decidables-element>
13
+ */
14
+ export default class DecidablesElement extends LitElement {
15
+ // HACK: Create a unique ID for each DecidablesElement
16
+ // This is needed because Edge/IE11 don't have real Shadow DOM, so IDs leak
17
+ // out of elements and collide if there is more than one of an element on a
18
+ // page. Known issue for checkbox/switches and the id/for pattern on <input>
19
+ // and <label>
20
+ static get uniqueId() {
21
+ DecidablesElement.ID += 1;
22
+ return DecidablesElement.ID;
23
+ }
24
+
25
+ constructor() {
26
+ super();
27
+ this.uniqueId = `decidables-${DecidablesElement.uniqueId}`;
28
+ }
29
+
30
+ getComputedStyleValue(property) {
31
+ // HACK: IE11 requires use of polyfill interface to get custom property value in Javascript
32
+ if (window.ShadyCSS) {
33
+ return window.ShadyCSS.getComputedStyleValue(this, property);
34
+ }
35
+ return getComputedStyle(this).getPropertyValue(property);
36
+ }
37
+
38
+ firstUpdated(changedProperties) {
39
+ super.firstUpdated(changedProperties);
40
+
41
+ // Use focus highlighting if keyboard is used at all
42
+ d3.select(this.renderRoot.host)
43
+ .classed('keyboard', true)
44
+ .on('mousemove.keyboard touchstart.keyboard', (event) => {
45
+ const element = event.currentTarget;
46
+ d3.select(element.renderRoot.host)
47
+ .classed('keyboard', false)
48
+ .on('mousemove.keyboard touchstart.keyboard', null);
49
+ })
50
+ .on('keydown.keyboard', (event) => {
51
+ const element = event.currentTarget;
52
+ d3.select(element.renderRoot.host)
53
+ .classed('keyboard', true)
54
+ .on('keydown.keyboard mousemove.keyboard touchstart.keyboard', null);
55
+ });
56
+ }
57
+
58
+ static get greys() {
59
+ const grey = '#999999';
60
+ const greys = {};
61
+ greys.white = '#ffffff';
62
+ greys.light75 = d3.interpolateRgb(grey, '#ffffff')(0.75);
63
+ greys.light50 = d3.interpolateRgb(grey, '#ffffff')(0.5);
64
+ greys.light25 = d3.interpolateRgb(grey, '#ffffff')(0.25);
65
+ greys.grey = grey;
66
+ greys.dark25 = d3.interpolateRgb(grey, '#000000')(0.25);
67
+ greys.dark50 = d3.interpolateRgb(grey, '#000000')(0.5);
68
+ greys.dark75 = d3.interpolateRgb(grey, '#000000')(0.75);
69
+ greys.black = '#000000';
70
+ return greys;
71
+ }
72
+
73
+ static get shadows() {
74
+ // Material Design elevation styles
75
+ // References:
76
+ // https://github.com/material-components/material-components-web/tree/master/packages/mdc-elevation
77
+ // https://codepen.io/hanger/pen/yOGvQp
78
+ /* eslint-disable key-spacing, object-curly-newline */
79
+ return {
80
+ elevations: [0, 2, 4, 8, 16],
81
+ baselineColor: '#000000',
82
+ baselineColorString: '0, 0, 0',
83
+ inverseBaselineColor: '#FFFFFF',
84
+ inverseBaselineColorString: '255, 255, 255',
85
+ opacityUmbra: 0.2,
86
+ opacityPenumbra: 0.14,
87
+ opacityAmbient: 0.12,
88
+ opacityBoost: 0.2,
89
+ mapUmbra: {// $mdc-elevation-umbra-map
90
+ 0: {x: 0, y: 0, b: 0, s: 0}, // offset-x, offset-y, blur-radius, spread-radius
91
+ 2: {x: 0, y: 3, b: 1, s: -2},
92
+ 4: {x: 0, y: 2, b: 4, s: -1},
93
+ 8: {x: 0, y: 5, b: 5, s: -3},
94
+ 16: {x: 0, y: 8, b: 10, s: -5},
95
+ },
96
+ mapPenumbra: {// $mdc-elevation-penumbra-map
97
+ 0: {x: 0, y: 0, b: 0, s: 0}, // offset-x, offset-y, blur-radius, spread-radius
98
+ 2: {x: 0, y: 2, b: 2, s: 0},
99
+ 4: {x: 0, y: 4, b: 5, s: 0},
100
+ 8: {x: 0, y: 8, b: 10, s: 1},
101
+ 16: {x: 0, y: 16, b: 24, s: 2},
102
+ },
103
+ mapAmbient: {// $mdc-elevation-ambient-map
104
+ 0: {x: 0, y: 0, b: 0, s: 0}, // offset-x, offset-y, blur-radius, spread-radius
105
+ 2: {x: 0, y: 1, b: 5, s: 0},
106
+ 4: {x: 0, y: 1, b: 10, s: 0},
107
+ 8: {x: 0, y: 3, b: 14, s: 2},
108
+ 16: {x: 0, y: 6, b: 30, s: 5},
109
+ },
110
+ };
111
+ /* eslint-enable key-spacing, object-curly-newline */
112
+ }
113
+
114
+ static cssBoxShadow(elevation, rotate = false, inverse = false) {
115
+ const umbraO = this.shadows.opacityUmbra + this.shadows.opacityBoost;
116
+ const penumbraO = this.shadows.opacityPenumbra + this.shadows.opacityBoost;
117
+ const ambientO = this.shadows.opacityAmbient + this.shadows.opacityBoost;
118
+
119
+ const umbraC = (inverse)
120
+ ? `rgba(${this.shadows.inverseBaselineColorString}, ${umbraO})`
121
+ : `rgba(${this.shadows.baselineColorString}, ${umbraO})`;
122
+ const penumbraC = (inverse)
123
+ ? `rgba(${this.shadows.inverseBaselineColorString}, ${penumbraO})`
124
+ : `rgba(${this.shadows.baselineColorString}, ${penumbraO})`;
125
+ const ambientC = (inverse)
126
+ ? `rgba(${this.shadows.inverseBaselineColorString}, ${ambientO})`
127
+ : `rgba(${this.shadows.baselineColorString}, ${ambientO})`;
128
+
129
+ const umbraM = this.shadows.mapUmbra[elevation];
130
+ const penumbraM = this.shadows.mapPenumbra[elevation];
131
+ const ambientM = this.shadows.mapAmbient[elevation];
132
+
133
+ const umbraS = (rotate)
134
+ ? `${-umbraM.y}px ${umbraM.y / 2}px ${umbraM.b}px ${umbraM.s}px`
135
+ : `${umbraM.y / 2}px ${umbraM.y}px ${umbraM.b}px ${umbraM.s}px`;
136
+ const penumbraS = (rotate)
137
+ ? `${-penumbraM.y}px ${penumbraM.y / 2}px ${penumbraM.b}px ${penumbraM.s}px`
138
+ : `${penumbraM.y / 2}px ${penumbraM.y}px ${penumbraM.b}px ${penumbraM.s}px`;
139
+ const ambientS = (rotate)
140
+ ? `${-ambientM.y}px ${ambientM.y / 2}px ${ambientM.b}px ${ambientM.s}px`
141
+ : `${ambientM.y / 2}px ${ambientM.y}px ${ambientM.b}px ${ambientM.s}px`;
142
+
143
+ return `${umbraS} ${umbraC}, ${penumbraS} ${penumbraC}, ${ambientS} ${ambientC}`;
144
+ }
145
+
146
+ static get svgFilters() {
147
+ const shadows = DecidablesElement.shadows; // eslint-disable-line prefer-destructuring
148
+ const erodeRadius = 1;
149
+
150
+ const filters = shadows.elevations.map((z) => {
151
+ return svg`
152
+ <filter id=${`shadow-${z}`} x="-250%" y="-250%" width="600%" height="600%">
153
+ <feComponentTransfer in="SourceAlpha" result="solid">
154
+ <feFuncA type="table" tableValues="0 1 1"/>
155
+ </feComponentTransfer>
156
+ <feOffset in="solid" result="offU" dx=${shadows.mapUmbra[z].y / 2} dy=${shadows.mapUmbra[z].y} />
157
+ <feOffset in="solid" result="offP" dx=${shadows.mapPenumbra[z].y / 2} dy=${shadows.mapPenumbra[z].y} />
158
+ <feOffset in="solid" result="offA" dx=${shadows.mapAmbient[z].y / 2} dy=${shadows.mapAmbient[z].y} />
159
+ ${(shadows.mapUmbra[z].s === 0)
160
+ ? svg``
161
+ : svg`<feMorphology in="offU" result="spreadU" operator=${(shadows.mapUmbra[z].s > 0) ? 'dilate' : 'erode'} radius=${Math.abs(shadows.mapUmbra[z].s)} />`
162
+ }
163
+ ${(shadows.mapPenumbra[z].s === 0)
164
+ ? svg``
165
+ : svg`<feMorphology in="offP" result="spreadP" operator=${(shadows.mapPenumbra[z].s > 0) ? 'dilate' : 'erode'} radius=${Math.abs(shadows.mapPenumbra[z].s)} />`
166
+ }
167
+ ${(shadows.mapAmbient[z].s === 0)
168
+ ? svg``
169
+ : svg`<feMorphology in="offA" result="spreadA" operator=${(shadows.mapAmbient[z].s > 0) ? 'dilate' : 'erode'} radius=${Math.abs(shadows.mapAmbient[z].s)} />`
170
+ }
171
+ <feGaussianBlur in=${(shadows.mapUmbra[z].s === 0) ? 'offU' : 'spreadU'} result="blurU" stdDeviation=${shadows.mapUmbra[z].b / 2} />
172
+ <feGaussianBlur in=${(shadows.mapPenumbra[z].s === 0) ? 'offP' : 'spreadP'} result="blurP" stdDeviation=${shadows.mapPenumbra[z].b / 2} />
173
+ <feGaussianBlur in=${(shadows.mapAmbient[z].s === 0) ? 'offA' : 'spreadA'} result="blurA" stdDeviation=${shadows.mapAmbient[z].b / 2} />
174
+ <feFlood in="SourceGraphic" result="opU" flood-color=${shadows.baselineColor} flood-opacity=${shadows.opacityUmbra + shadows.opacityBoost} />
175
+ <feFlood in="SourceGraphic" result="opP" flood-color=${shadows.baselineColor} flood-opacity=${shadows.opacityPenumbra + shadows.opacityBoost} />
176
+ <feFlood in="SourceGraphic" result="opA" flood-color=${shadows.baselineColor} flood-opacity=${shadows.opacityAmbient + shadows.opacityBoost} />
177
+ <feComposite in="opU" in2="blurU" result="shU" operator="in" />
178
+ <feComposite in="opP" in2="blurP" result="shP" operator="in" />
179
+ <feComposite in="opA" in2="blurA" result="shA" operator="in" />
180
+ <!-- HACK Edge: Using a dynamic value for erode radius stops Edge from corrupting the "radius" value! -->
181
+ <feMorphology in="solid" result="smaller" operator="erode" radius=${erodeRadius} />
182
+ <feComposite in="shU" in2="smaller" result="finalU" operator="out" />
183
+ <feComposite in="shP" in2="smaller" result="finalP" operator="out" />
184
+ <feComposite in="shA" in2="smaller" result="finalA" operator="out" />
185
+ <feMerge>
186
+ <feMergeNode in="finalU" />
187
+ <feMergeNode in="finalP" />
188
+ <feMergeNode in="finalA" />
189
+ <feMergeNode in="SourceGraphic" />
190
+ </feMerge>
191
+ </filter>`;
192
+ });
193
+
194
+ return svg`
195
+ <svg class="defs">
196
+ <defs>
197
+ ${filters}
198
+ </defs>
199
+ </svg>
200
+ `;
201
+ }
202
+
203
+ static get styles() {
204
+ return css`
205
+ :host {
206
+ ---shadow-0: var(--shadow-0, ${unsafeCSS(this.cssBoxShadow(0))});
207
+ ---shadow-2: var(--shadow-2, ${unsafeCSS(this.cssBoxShadow(2))});
208
+ ---shadow-4: var(--shadow-4, ${unsafeCSS(this.cssBoxShadow(4))});
209
+ ---shadow-8: var(--shadow-8, ${unsafeCSS(this.cssBoxShadow(8))});
210
+
211
+ ---color-background: var(--color-background, ${unsafeCSS(this.greys.white)});
212
+ ---color-border: var(--color-border, ${unsafeCSS(this.greys.light75)});
213
+ ---color-text: var(--color-text, ${unsafeCSS(this.greys.dark75)});
214
+ ---color-text-inverse: var(--color-text-inverse, ${unsafeCSS(this.greys.white)});
215
+ ---color-link: var(--color-link, ${unsafeCSS(this.greys.dark25)});
216
+ ---color-element-background: var(--color-element-background, ${unsafeCSS(this.greys.light75)});
217
+ ---color-element-disabled: var(--color-element-disabled, ${unsafeCSS(this.greys.light50)});
218
+ ---color-element-enabled: var(--color-element-enabled, ${unsafeCSS(this.greys.grey)});
219
+ ---color-element-selected: var(--color-element-selected, ${unsafeCSS(this.greys.dark25)});
220
+ ---color-element-border: var(--color-element-border, ${unsafeCSS(this.greys.dark50)});
221
+ ---color-element-emphasis: var(--color-element-emphasis, ${unsafeCSS(this.greys.dark75)});
222
+
223
+ ---font-family-base: var(--font-family-base, "Source Sans", sans-serif);
224
+ ---font-family-math: var(--font-family-math, "Source Serif", serif);
225
+
226
+ ---transition-duration: var(--transition-duration, 500ms);
227
+
228
+ font-family: var(---font-family-base);
229
+ }
230
+
231
+ :host,
232
+ :host *,
233
+ :host *::before,
234
+ :host *::after {
235
+ box-sizing: border-box;
236
+ }
237
+
238
+ .math-greek {
239
+ font-family: var(---font-family-math);
240
+ font-style: normal;
241
+ }
242
+
243
+ .math-num {
244
+ font-family: var(---font-family-base);
245
+ font-style: normal;
246
+ }
247
+
248
+ .math-var {
249
+ font-family: var(---font-family-math);
250
+ font-style: italic;
251
+ }
252
+
253
+ .defs {
254
+ display: block;
255
+
256
+ width: 0;
257
+ height: 0;
258
+ }
259
+ `;
260
+ }
261
+ }
262
+
263
+ // Static property of DecidablesElement!
264
+ DecidablesElement.ID = 0;
package/src/index.js ADDED
@@ -0,0 +1,11 @@
1
+
2
+ export {default as DecidablesElement} from './decidables-element';
3
+ export {default as DecidablesButton} from './button';
4
+ export {default as DecidablesSlider} from './slider';
5
+ export {default as DecidablesSpinner} from './spinner';
6
+ export {default as DecidablesSwitch} from './switch';
7
+ export {default as DecidablesToggle} from './toggle';
8
+ export {default as DecidablesToggleOption} from './toggle-option';
9
+
10
+ export {default as DecidablesConverterArray} from './converter-array';
11
+ export {default as DecidablesConverterSet} from './converter-set';