uniform-ui 2.3.7 → 3.0.0.beta4

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 (70) hide show
  1. checksums.yaml +4 -4
  2. data/lib/assets/javascripts/uniform.es5.js +1 -1
  3. data/lib/assets/javascripts/uniform.js +19 -19
  4. data/lib/assets/javascripts/uniform/checkbox.js +59 -16
  5. data/lib/assets/javascripts/uniform/component.js +20 -4
  6. data/lib/assets/javascripts/uniform/dropdown.js +78 -209
  7. data/lib/assets/javascripts/uniform/floating-label-input.js +63 -0
  8. data/lib/assets/javascripts/uniform/icons.js +12 -3
  9. data/lib/assets/javascripts/uniform/modal.js +15 -18
  10. data/lib/assets/javascripts/uniform/popover.js +45 -29
  11. data/lib/assets/javascripts/uniform/resizer.js +26 -30
  12. data/lib/assets/javascripts/uniform/select.js +188 -218
  13. data/lib/assets/javascripts/uniform/tooltip.js +11 -11
  14. data/lib/assets/stylesheets/uniform.scss +3 -7
  15. data/lib/assets/stylesheets/uniform/base.scss +20 -1
  16. data/lib/assets/stylesheets/uniform/components/buttons.scss +171 -184
  17. data/lib/assets/stylesheets/uniform/components/checkbox.scss +104 -0
  18. data/lib/assets/stylesheets/uniform/components/container.scss +3 -2
  19. data/lib/assets/stylesheets/uniform/components/dropdown.scss +8 -5
  20. data/lib/assets/stylesheets/uniform/components/floating-label-input.scss +29 -0
  21. data/lib/assets/stylesheets/uniform/components/input-group.scss +30 -0
  22. data/lib/assets/stylesheets/uniform/components/label.scss +21 -16
  23. data/lib/assets/stylesheets/uniform/components/loaders.scss +28 -51
  24. data/lib/assets/stylesheets/uniform/components/nav.scss +50 -87
  25. data/lib/assets/stylesheets/uniform/components/pointer.scss +83 -0
  26. data/lib/assets/stylesheets/uniform/components/select.scss +97 -107
  27. data/lib/assets/stylesheets/uniform/components/table.scss +31 -138
  28. data/lib/assets/stylesheets/uniform/components/thumb.scss +40 -25
  29. data/lib/assets/stylesheets/uniform/components/z-input.scss +20 -0
  30. data/lib/assets/stylesheets/uniform/defaults.scss +31 -10
  31. data/lib/assets/stylesheets/uniform/functions.scss +32 -7
  32. data/lib/assets/stylesheets/uniform/mixins.scss +110 -57
  33. data/lib/assets/stylesheets/uniform/utilities.scss +55 -0
  34. data/lib/assets/stylesheets/uniform/utilities/background.scss +9 -0
  35. data/lib/assets/stylesheets/uniform/utilities/borders.scss +84 -0
  36. data/lib/assets/stylesheets/uniform/utilities/effects.scss +172 -0
  37. data/lib/assets/stylesheets/uniform/utilities/layout.scss +174 -0
  38. data/lib/assets/stylesheets/uniform/utilities/position.scss +42 -0
  39. data/lib/assets/stylesheets/uniform/utilities/sizing.scss +54 -0
  40. data/lib/assets/stylesheets/uniform/utilities/spacing.scss +62 -0
  41. data/lib/assets/stylesheets/uniform/utilities/svg.scss +5 -0
  42. data/lib/assets/stylesheets/uniform/utilities/text.scss +158 -0
  43. data/lib/assets/stylesheets/uniform/variables.scss +111 -42
  44. data/lib/uniform/version.rb +1 -1
  45. metadata +25 -48
  46. data/lib/assets/javascripts/uniform.jquery.js +0 -152
  47. data/lib/assets/javascripts/uniform/dom-helpers.js +0 -158
  48. data/lib/assets/javascripts/uniform/floating-label.js +0 -54
  49. data/lib/assets/stylesheets/uniform-print.scss +0 -1
  50. data/lib/assets/stylesheets/uniform/components.scss +0 -11
  51. data/lib/assets/stylesheets/uniform/components/alert.scss +0 -72
  52. data/lib/assets/stylesheets/uniform/components/card.scss +0 -93
  53. data/lib/assets/stylesheets/uniform/components/form.scss +0 -149
  54. data/lib/assets/stylesheets/uniform/components/form/checkbox-collection.scss +0 -103
  55. data/lib/assets/stylesheets/uniform/components/form/checkbox.scss +0 -58
  56. data/lib/assets/stylesheets/uniform/components/form/floating-label.scss +0 -65
  57. data/lib/assets/stylesheets/uniform/components/form/input-group.scss +0 -56
  58. data/lib/assets/stylesheets/uniform/components/form/tristate.scss +0 -88
  59. data/lib/assets/stylesheets/uniform/components/grid.scss +0 -179
  60. data/lib/assets/stylesheets/uniform/components/row.scss +0 -67
  61. data/lib/assets/stylesheets/uniform/components/tooltip.scss +0 -41
  62. data/lib/assets/stylesheets/uniform/helpers.scss +0 -133
  63. data/lib/assets/stylesheets/uniform/helpers/border.scss +0 -28
  64. data/lib/assets/stylesheets/uniform/helpers/colors.scss +0 -24
  65. data/lib/assets/stylesheets/uniform/helpers/margin.scss +0 -27
  66. data/lib/assets/stylesheets/uniform/helpers/padding.scss +0 -9
  67. data/lib/assets/stylesheets/uniform/helpers/position.scss +0 -20
  68. data/lib/assets/stylesheets/uniform/helpers/sizes.scss +0 -38
  69. data/lib/assets/stylesheets/uniform/helpers/text.scss +0 -152
  70. data/lib/assets/stylesheets/uniform/print/grid.scss +0 -50
@@ -0,0 +1,63 @@
1
+ import Component from './component';
2
+ import { isVisible, isFocus, css, createElement } from 'dolla';
3
+
4
+ export default class FloatingLabel extends Component {
5
+
6
+ initialize(options){
7
+ if(options.input instanceof Element) {
8
+ this.input = options.input
9
+ } else {
10
+ this.input = createElement('input', Object.assign({}, {
11
+ type: this.constructor.type
12
+ }, options.input)) // TODO filter options to dolla.HTML_ATTRIBUTES
13
+ }
14
+ this.label = createElement('label', {
15
+ for: this.input.id,
16
+ children: [options.label]
17
+ });
18
+ this.input.setAttribute('aria-label', options.label);
19
+
20
+ this.el.classList.add('uniformFloatingLabelInput');
21
+
22
+ this.listenTo(this.input, 'focus', this.focus);
23
+ this.listenTo(this.input, 'blur', this.blur);
24
+ this.listenTo(this.input, 'revealed', this.render);
25
+ }
26
+
27
+ render () {
28
+ if(!isVisible(this.input)) return;
29
+
30
+ let internalHeight = parseInt(css(this.input, 'height')) - parseInt(css(this.input, 'borderTopWidth')) - parseInt(css(this.input, 'borderBottomWidth'));
31
+ this.input.style.lineHeight = 1;
32
+ let lineHeight = parseInt(css(this.input, 'lineHeight'));
33
+ let fontSize = parseInt(css(this.input, 'fontSize'));
34
+ let padding = internalHeight - lineHeight;
35
+
36
+ this.label.style.setProperty('--font-size', css(this.input, 'fontSize'))
37
+ this.label.style.paddingLeft = css(this.input, 'paddingLeft');
38
+ this.label.style.lineHeight = lineHeight + "px";
39
+ this.label.style.paddingTop = (internalHeight/2 - lineHeight) + "px";
40
+ this.label.style.paddingBottom = (internalHeight/2 - lineHeight) + "px";
41
+
42
+ this.input.style.paddingTop = internalHeight/2 - (lineHeight - fontSize) + "px";
43
+ this.input.style.paddingBottom = (internalHeight/2 - lineHeight) + (lineHeight - fontSize) + "px";
44
+
45
+ this.input.parentNode.insertBefore(this.el, this.input.nextSibling);
46
+ this.el.append(this.input);
47
+ this.el.append(this.label);
48
+
49
+ if(this.input.value != ""){
50
+ this.focus()
51
+ }
52
+ }
53
+
54
+ focus (e) {
55
+ this.el.classList.add('present');
56
+ }
57
+
58
+ blur (e) {
59
+ if(this.input.value == ""){
60
+ this.el.classList.remove('present');
61
+ }
62
+ }
63
+ }
@@ -1,16 +1,25 @@
1
1
  let check = `
2
- <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1em" height="1em" viewBox="0 0 32 32">
2
+ <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 32 32">
3
3
  <path d="M28.998 8.531l-2.134-2.134c-0.394-0.393-1.030-0.393-1.423 0l-12.795 12.795-6.086-6.13c-0.393-0.393-1.029-0.393-1.423 0l-2.134 2.134c-0.393 0.394-0.393 1.030 0 1.423l8.924 8.984c0.393 0.393 1.030 0.393 1.423 0l15.648-15.649c0.393-0.392 0.393-1.030 0-1.423z"></path>
4
4
  </svg>
5
5
  `.trim()
6
6
 
7
7
  let arrow_down = `
8
- <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1em" height="1em" viewBox="0 0 20 20">
8
+ <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 20 20">
9
9
  <path d="M13.418 7.601c0.271-0.268 0.709-0.268 0.979 0s0.271 0.701 0 0.969l-3.907 3.83c-0.271 0.268-0.709 0.268-0.979 0l-3.908-3.83c-0.27-0.268-0.27-0.701 0-0.969s0.708-0.268 0.979 0l3.418 3.14 3.418-3.14z"></path>
10
10
  </svg>
11
11
  `.trim()
12
12
 
13
+ let x = `
14
+ <svg version="1.2" baseProfile="tiny" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="64px" height="64px" viewBox="0 0 64 64" xml:space="preserve">
15
+ <g><rect x="-2.352" y="29.385" transform="matrix(0.7071 0.7071 -0.7071 0.7071 32.3545 -14.3899)" width="71.799" height="4.95"/></g>
16
+ <g><rect x="-2.374" y="29.376" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -12.7023 33.0352)" width="71.799" height="4.95"/></g>
17
+ </svg>
18
+
19
+ `.trim()
20
+
13
21
  export {
14
22
  check,
15
- arrow_down
23
+ arrow_down,
24
+ x
16
25
  }
@@ -1,5 +1,5 @@
1
1
  import Component from './component';
2
- import * as Helpers from './dom-helpers';
2
+ import {css, trigger, append} from 'dolla';
3
3
 
4
4
  /* UniformModal.initialize
5
5
  Options
@@ -14,8 +14,9 @@ export default class Modal extends Component {
14
14
  this.options = {};
15
15
  this.options.klass = options.klass || false;
16
16
  this.content = options.content;
17
+ this.el.removeAttribute('content');
17
18
 
18
- Helpers.addClass(this.el, 'uniformModal');
19
+ this.el.classList.add('uniformModal');
19
20
  this.listenTo(document, 'keyup', this.keyup);
20
21
  this.listenTo(this.el, 'click', this.checkCloseButton);
21
22
  }
@@ -27,21 +28,20 @@ export default class Modal extends Component {
27
28
 
28
29
  render () {
29
30
  var that = this;
30
- var content = typeof this.content == 'function' ? this.content() : this.content;
31
31
 
32
32
  this.highest_z_index = 0;
33
33
  this.overlay = document.createElement('div');
34
- Helpers.addClass(this.overlay, 'uniformModal-overlay');
34
+ this.overlay.classList.add('uniformModal-overlay');
35
35
 
36
36
  this.blur = document.createElement('div');
37
- Helpers.addClass(this.blur, 'uniformModal-blur');
37
+ this.blur.classList.add('uniformModal-blur');
38
38
 
39
39
  this.original_scroll = window.scrollY;
40
40
  this.blur.style.top = 0 - this.original_scroll + "px";
41
41
 
42
42
  if (document.body.querySelectorAll('.uniformModal').length > 0) {
43
43
  this.highest_z_index = Math.max(Array.prototype.map.call(document.body.querySelectorAll('.uniformModal'), function(el){
44
- return parseInt(Helpers.css(el, 'zIndex'));
44
+ return parseInt(css(el, 'zIndex'));
45
45
  }));
46
46
  this.el.style.zIndex = this.highest_z_index + 2;
47
47
  }
@@ -57,40 +57,37 @@ export default class Modal extends Component {
57
57
  }
58
58
  }
59
59
 
60
- Helpers.addClass(document.body, 'uniformModal-active');
60
+ document.body.classList.add('uniformModal-active');
61
61
  document.body.appendChild(this.blur);
62
62
  document.body.appendChild(this.el);
63
63
 
64
64
  var container = document.createElement('div');
65
- Helpers.addClass(container, 'uniformModal-container');
66
- if (content instanceof Node) {
67
- container.appendChild(content);
68
- } else {
69
- container.innerHTML = content;
70
- }
65
+ container.classList.add('uniformModal-container');
66
+
67
+ append(container, this.content);
71
68
 
72
69
  var closeButton = document.createElement('div');
73
- Helpers.addClass(closeButton, 'uniformModal-close');
70
+ closeButton.classList.add('uniformModal-close');
74
71
  this.el.appendChild(closeButton);
75
72
 
76
73
  this.el.style.top = window.scrollY;
77
74
  this.listenTo(this.overlay, 'click', this.close);
78
75
  this.el.appendChild(container);
79
76
 
80
- if (this.options.klass) Helpers.addClass(container, this.options.klass);
81
- if (content.innerHTML) Helpers.trigger(content, 'rendered');
77
+ if (this.options.klass) container.classList.add(this.options.klass);
78
+ if (this.content.innerHTML) trigger(this.content, 'rendered');
82
79
  this.trigger('rendered');
83
80
 
84
81
  return this;
85
82
  }
86
83
 
87
84
  checkCloseButton (e) {
88
- if(Helpers.hasClass(e.target, 'uniformModal-close'))
85
+ if(e.target.classList.contains('uniformModal-close'))
89
86
  this.close();
90
87
  }
91
88
 
92
89
  close () {
93
- Helpers.removeClass(document.querySelectorAll('uniformModal-active'), 'uniformModal-active');
90
+ document.querySelectorAll('uniformModal-active').forEach(el => el.classList.remove('uniformModal-active'));
94
91
  var elements = this.blur.children;
95
92
  var elementCount = elements.length
96
93
  for(var i=0; i < elementCount; i++){
@@ -1,5 +1,5 @@
1
1
  import Component from './component';
2
- import * as Helpers from './dom-helpers';
2
+ import { offset, outerWidth, outerHeight, append } from 'dolla';
3
3
 
4
4
  /*
5
5
  Requirements
@@ -12,6 +12,7 @@ import * as Helpers from './dom-helpers';
12
12
  align: [left|right|center|#px] [top|center|bottom|#px] | default: 'center bottom'
13
13
  zIndex: # | default: unset
14
14
  offset: {left: 0, top: 0}
15
+ container: element to append popover to. default: document
15
16
  */
16
17
  export default class Popover extends Component {
17
18
  initialize (options) {
@@ -26,7 +27,9 @@ export default class Popover extends Component {
26
27
  offset: {left: 0, top: 0}
27
28
  };
28
29
  Object.assign(this.options, this.pick(options, Object.keys(this.options)));
30
+ this.el.removeAttribute('content');
29
31
 
32
+ this.options.anchor.popover = this
30
33
  this.listenTo(document, 'click', this.checkFocus);
31
34
  this.listenTo(document, 'focusin', this.checkFocus);
32
35
  this.listenTo(document, 'keyup', this.checkEscape);
@@ -35,9 +38,10 @@ export default class Popover extends Component {
35
38
  });
36
39
 
37
40
  if(typeof this.options.container == "string"){
38
- this.options.container = Helpers.closest(this.options.anchor, this.options.container)
39
- this.options.container = this.options.container || document.body
41
+ this.options.container = this.options.anchor.closest(this.options.container)
40
42
  }
43
+ this.options.container = this.options.container || document.body
44
+
41
45
  }
42
46
 
43
47
  render () {
@@ -45,10 +49,7 @@ export default class Popover extends Component {
45
49
  this.el.style.position = 'absolute';
46
50
  this.el.style.zIndex = this.options.zIndex;
47
51
 
48
- if(this.options.content instanceof Node)
49
- this.el.appendChild(this.options.content);
50
- else
51
- this.el.innerHTML = this.options.content;
52
+ append(this.el, this.options.content)
52
53
 
53
54
  this.options.container.appendChild(this.el);
54
55
  this.resize();
@@ -59,7 +60,7 @@ export default class Popover extends Component {
59
60
 
60
61
  resize () {
61
62
  this.setPosition();
62
- const bounds = this.el.getBoundingClientRect();
63
+ let bounds = this.el.getBoundingClientRect();
63
64
  const body_bounds = document.body.getBoundingClientRect();
64
65
  const window_bounds = {
65
66
  top: 0,
@@ -69,10 +70,14 @@ export default class Popover extends Component {
69
70
  };
70
71
 
71
72
  var reposition = false;
72
- if (bounds.bottom > body_bounds.bottom && bounds.bottom > window_bounds.bottom) {
73
- const difference = body_bounds.bottom - bounds.bottom
74
- if(this.el.style.top != null) this.el.style.top = parseInt(this.el.style.top) - difference + "px"
75
- if(this.el.style.bottom != null) this.el.style.bottom = parseInt(this.el.style.bottom) + difference + "px"
73
+ if (bounds.bottom > Math.max(body_bounds.bottom, window_bounds.bottom)) {
74
+ var [leftAlign, topAlign] = this.options.align.split(" ");
75
+ this.setPosition(`${leftAlign} top`);
76
+ bounds = this.el.getBoundingClientRect()
77
+ if(bounds.top < 0) {
78
+ this.setPosition(`${leftAlign} bottom`);
79
+ }
80
+ bounds = this.el.getBoundingClientRect()
76
81
  }
77
82
  if (bounds.top < body_bounds.top) {
78
83
  const difference = body_bounds.top - bounds.top
@@ -83,6 +88,7 @@ export default class Popover extends Component {
83
88
  const difference = body_bounds.left - bounds.left
84
89
  if(this.el.style.left != null) this.el.style.left = parseInt(this.el.style.left) + difference + "px"
85
90
  if(this.el.style.right != null) this.el.style.right = parseInt(this.el.style.right) - difference + "px"
91
+ bounds = this.el.getBoundingClientRect()
86
92
  }
87
93
  if (bounds.right > body_bounds.right) {
88
94
  const difference = body_bounds.right - bounds.right
@@ -96,43 +102,43 @@ export default class Popover extends Component {
96
102
  var [leftAlign, topAlign] = align.split(" ");
97
103
  leftAlign = leftAlign || "bottom";
98
104
 
99
- var offset = Helpers.offset(this.options.anchor);
105
+ var anchorOffset = offset(this.options.anchor);
100
106
  var container = this.options.container;
101
107
  if(getComputedStyle(container)['position'] == "static") container = container.offsetParent;
102
108
  if(!container) container = document.body;
103
109
 
104
- var containerOffset = Helpers.offset(container);
105
- offset = {
106
- top: offset.top - containerOffset.top,
107
- left: offset.left - containerOffset.left
110
+ var containerOffset = offset(container);
111
+ anchorOffset = {
112
+ top: anchorOffset.top - containerOffset.top,
113
+ left: anchorOffset.left - containerOffset.left
108
114
  }
109
115
 
110
116
  var position = {};
111
117
  if(leftAlign == 'left'){
112
- position.right = Helpers.outerWidth(container) - offset.left;
118
+ position.right = outerWidth(container) - anchorOffset.left;
113
119
  } else if(leftAlign == 'center'){
114
- position.left = offset.left + Helpers.outerWidth(this.options.anchor) / 2 - Helpers.outerWidth(this.el) / 2;
120
+ position.left = anchorOffset.left + outerWidth(this.options.anchor) / 2 - outerWidth(this.el) / 2;
115
121
  } else if (leftAlign == 'right'){
116
- position.left = offset.left + Helpers.outerWidth(this.options.anchor);
122
+ position.left = anchorOffset.left + outerWidth(this.options.anchor);
117
123
  } else if (leftAlign.includes("px")){
118
- position.left = offset.left + parseInt(leftAlign);
124
+ position.left = anchorOffset.left + parseInt(leftAlign);
119
125
  }
120
126
 
121
127
  if(topAlign == 'top'){
122
- let height = Helpers.outerHeight(container);
128
+ let height = outerHeight(container);
123
129
  if(container == document.body && getComputedStyle(container)['position'] == "static"){
124
130
  height = window.innerHeight;
125
131
  } else if (container == document.body) {
126
132
  height = Math.max(height, document.body.clientHeight);
127
133
  }
128
- position.bottom = height - offset.top;
134
+ position.bottom = height - anchorOffset.top;
129
135
  } else if(topAlign == 'center'){
130
- position.top = offset.top + Helpers.outerHeight(this.options.anchor) / 2;
136
+ position.top = anchorOffset.top + outerHeight(this.options.anchor) / 2;
131
137
  position.transform = "translateY(-50%)";
132
138
  } else if (topAlign == 'bottom'){
133
- position.top = offset.top + Helpers.outerHeight(this.options.anchor);
139
+ position.top = anchorOffset.top + outerHeight(this.options.anchor);
134
140
  } else if (topAlign.includes("px")){
135
- position.top = offset.top + parseInt(topAlign);
141
+ position.top = anchorOffset.top + parseInt(topAlign);
136
142
  }
137
143
 
138
144
  if(this.options.offset.left) position.left += parseInt(this.options.offset.left);
@@ -145,9 +151,9 @@ export default class Popover extends Component {
145
151
  this.el.style.top = null;
146
152
  this.el.style.bottom = null;
147
153
  this.el.style.transform = null;
148
- Helpers.removeClass(this.el, 'popover-left popover-right popover-center popover-top popover-bottom');
149
- Helpers.addClass(this.el, 'popover-' + topAlign);
150
- Helpers.addClass(this.el, 'popover-' + leftAlign);
154
+ this.el.classList.remove('popover-left', 'popover-right', 'popover-center', 'popover-top', 'popover-bottom');
155
+ this.el.classList.add('popover-' + topAlign);
156
+ this.el.classList.add('popover-' + leftAlign);
151
157
  Object.keys(position).forEach(function(key){
152
158
  this.el.style[key] = position[key] + (key != "transform" ? "px" : "");
153
159
  }, this);
@@ -160,11 +166,13 @@ export default class Popover extends Component {
160
166
  if (e.target == this.options.anchor) return;
161
167
  if (this.el.contains(e.target)) return;
162
168
  if (this.options.anchor.contains(e.target)) return;
169
+ if (this.persisting) return;
163
170
  this.hide();
164
171
  }
165
172
 
166
173
  checkEscape (e) {
167
174
  if(e.which != 27) return;
175
+ if (this.persisting) return;
168
176
  this.hide();
169
177
  }
170
178
 
@@ -193,4 +201,12 @@ export default class Popover extends Component {
193
201
  flag = flag || this.showing;
194
202
  if(flag) this.hide(); else this.show();
195
203
  }
204
+
205
+ persist() {
206
+ this.persisting = true;
207
+ }
208
+
209
+ unpersist() {
210
+ this.persisting = false;
211
+ }
196
212
  }
@@ -1,43 +1,39 @@
1
1
  import Component from './component';
2
- import * as Helpers from './dom-helpers';
2
+ import { trigger } from 'dolla';
3
3
 
4
4
  export default class Resizer extends Component {
5
5
 
6
6
  initialize () {
7
+ const breakpoints = getComputedStyle(window.document.body).getPropertyValue('--breakpoints')
8
+ this.breakpoints = {}
9
+ breakpoints.split(",").forEach(breakpoint => {
10
+ const [key, value] = breakpoint.split("/")
11
+ this.breakpoints[key.trim()] = value;
12
+ })
13
+
7
14
  this.listenTo(window, 'resize', this.resize);
8
- Helpers.trigger(window, 'resize');
15
+ this.resize();
9
16
  }
10
17
 
11
18
  resize () {
12
- // breakpoints at 720, 1080, 1440
13
- var width = this.el.offsetWidth;
14
-
15
- if(width > 720 && !Helpers.hasClass(this.el, 'md-size')) {
16
- Helpers.addClass(this.el, 'md-size');
17
- Helpers.trigger(window, 'resized-md');
18
- } else if (width < 720 && Helpers.hasClass(this.el, 'md-size')) {
19
- Helpers.removeClass(this.el, 'md-size');
20
- }
21
-
22
- if(width > 1080 && !Helpers.hasClass(this.el, 'lg-size')) {
23
- Helpers.addClass(this.el, 'lg-size');
24
- Helpers.trigger(window, 'resized-lg');
25
- } else if (width < 1080 && Helpers.hasClass(this.el, 'lg-size')) {
26
- Helpers.removeClass(this.el, 'lg-size');
19
+ const width = this.el.offsetWidth;
20
+ Object.keys(this.breakpoints).forEach(size => {
21
+ const query = this.breakpoints[size]
22
+ const css_class = size + '-container'
23
+ let [attribute, value] = query.split(":")
24
+ if(value.match("px")){
25
+ value = parseInt(value)
26
+ } else {
27
+ throw "unsupported media units"
27
28
  }
28
-
29
- if(width > 1440 && !Helpers.hasClass(this.el, 'xl-size')) {
30
- Helpers.addClass(this.el, 'xl-size');
31
- Helpers.trigger(window, 'resized-xl');
32
- } else if (width < 1440 && Helpers.hasClass(this.el, 'xl-size')) {
33
- Helpers.removeClass(this.el, 'xl-size');
34
- }
35
-
36
- if(width < 720 && !Helpers.hasClass(this.el, 'sm-size')) {
37
- Helpers.addClass(this.el, 'sm-size');
38
- Helpers.trigger(window, 'resized-sm');
39
- } else if (width > 720 && Helpers.hasClass(this.el, 'sm-size')) {
40
- Helpers.removeClass(this.el, 'sm-size');
29
+
30
+ if(attribute == "min-width") {
31
+ this.el.classList.toggle(css_class, width > value)
32
+ } else if (attribute == "max-width") {
33
+ this.el.classList.toggle(css_class, width < value)
34
+ } else {
35
+ throw "unsupported media feature"
41
36
  }
37
+ });
42
38
  }
43
39
  }
@@ -1,248 +1,218 @@
1
1
  import Component from './component';
2
2
  import Popover from './popover';
3
3
  import Modal from './modal';
4
- import { check as checkIcon, arrow_down as arrowIcon } from './icons';
5
- import * as Helpers from './dom-helpers';
4
+ import { check as checkIcon, arrow_down as arrowIcon, x as xIcon } from './icons';
5
+ import { createElement, HTML_ATTRIBUTES, filter, css, isEmpty, trigger } from 'dolla';
6
6
 
7
7
  /*
8
- options
9
- class: String, appended to uniformSelect-edit button as class
10
- limit: int | false - number of options to limit to, or false to not limit
11
- showAll: function(button_options) to run if/when "Show All" is clicked
12
- label: string, used for mobile menu
13
- container: selector for where to render dropdown
8
+ options: array of html options, each item can be string | array | object
9
+ ex. ["Employee", "Manager", "General Manager"]
10
+ ex. [
11
+ ["Employee", "employee", false],
12
+ ["Manager", "manager", false],
13
+ ["General Manager", "general_manager", true],
14
+ ]
15
+ ex. [
16
+ {value: "employee", text: 'Employee', selected: false},
17
+ {value: "manager", text: 'Manager', selected: false},
18
+ {value: "general_manager", text: 'General Manager', selected: true}
19
+ ]
20
+ limit: int | false - number of options to limit to, or false to not limit
21
+ container: selector for where to render dropdown
22
+ multiple: false
14
23
  */
15
24
  export default class Select extends Component {
16
25
 
17
- initialize (options = {}) {
18
- this.options = {
19
- label: false,
20
- class: "",
21
- showAll: function (button_options){
22
- Helpers.removeClass(button_options.querySelectorAll('button.hide'), 'hide');
23
- var button = button_options.querySelector('.uniformSelect-show-all');
24
- button.parentNode.removeChild(button);
25
-
26
- return false;
27
- },
28
- limit: 8,
29
- container: false
26
+ initialize (options = {}) {
27
+ this.htmlOptions = options.options.map(option => {
28
+ if(typeof option == "string") {
29
+ return {
30
+ value: option,
31
+ text: option
30
32
  }
33
+ } else if (Array.isArray(option)){
34
+ return {
35
+ value: option[1],
36
+ text: option[0],
37
+ selected: option[2]
38
+ }
39
+ } else if (typeof option == "object") {
40
+ return option
41
+ } else {
42
+ throw "option of unexpected type"
43
+ }
44
+ });
45
+ this.options = {
46
+ multiple: false,
47
+ limit: 8,
48
+ container: false
49
+ }
50
+ Object.assign(this.options, this.pick(options, Object.keys(this.options)));
31
51
 
32
- Object.assign(this.options, this.pick(options, Object.keys(this.options)));
52
+ this.el_options = Object.assign({}, this.pick(options, HTML_ATTRIBUTES));
53
+ this.el = createElement('button', this.el_options);
54
+ this.el.classList.add('uniformSelect');
33
55
 
34
- this.listenTo(this.el, 'change', this.renderSelected);
35
- this.listenTo(this.el, 'close', this.hideOptions);
36
- this.el.uniformSelect = this;
56
+ this.listenTo(this.el, 'click', this.toggleOptions);
57
+ this.listenTo(this.el, 'click', '.uniformSelect-remove', this.removeSelection);
58
+ this.listenTo(this.el, 'change', 'select', this.renderValue);
59
+ this.listenTo(this.el, 'close', 'select', this.removeOptions);
60
+ }
61
+
62
+ render () {
63
+ this.valueEl = createElement('span');
64
+ this.valueEl.classList.add('uniformSelect-value')
65
+ this.el.append(this.valueEl);
37
66
 
38
- this.activeIcon = document.createElement('span');
39
- this.activeIcon.innerHTML = checkIcon;
40
- Helpers.addClass(this.activeIcon, 'uniformSelect-option-icon');
41
- }
67
+ this.indicatorEl = createElement('span', {children: arrowIcon})
68
+ this.indicatorEl.classList.add('uniformSelect-indicator')
69
+ this.el.append(this.indicatorEl);
42
70
 
43
- remove () {
44
- Component.prototype.remove.apply(this, arguments);
45
- this.edit_button.parentNode.removeChild(this.edit_button);
46
- delete this.this.edit_button;
71
+ this.select = createElement('select', this.el_options);
72
+ this.htmlOptions.forEach(option => {
73
+ this.select.append(createElement('option', Object.assign({}, {children: option.text}, option)))
74
+ });
75
+ this.el.append(this.select);
47
76
 
48
- this.activeIcon.parentNode.removeChild(this.activeIcon);
49
- delete this.activeIcon;
50
77
 
51
- if(this.button_options_popover) this.button_options_popover.remove();
52
- if(this.button_options_modal) this.button_options_modal.remove();
78
+ // Append placeholder of longest option, to set width
79
+ const longestText = this.htmlOptions.map(x => x.text).sort((a, b) => a.length < b.length)[0]
80
+ const placeholder = createElement('span', {class: 'uniformSelect-placeholder', children: longestText})
81
+ this.el.append(placeholder);
53
82
 
54
- if(this.button_options) {
55
- this.button_options.parentNode.removeChild(this.button_options);
56
- delete this.button_options;
57
- }
58
- }
83
+ this.renderValue();
84
+ return this;
85
+ }
59
86
 
60
- render () {
61
- this.edit_button = Helpers.createElement(`
62
- <button type='button' class='uniformSelect-edit uniformInput outline block ${this.options.class}'>
63
- <span class="text-js"></span>
64
- <span class="uniformSelect-edit-icon">${arrowIcon}</span>
65
- </button>
66
- `);
67
-
68
- if (this.el.name) {
69
- Helpers.addClass(this.edit_button, this.el.name.toLowerCase().replace(/[^a-z0-9\-_]+/g, '-'));
70
- }
71
- this.el.style.display = "none";
72
- this.el.insertAdjacentElement('beforebegin', this.edit_button);
73
- if(!this.options.container) {
74
- this.options.container = this.edit_button.parentElement
75
- }
76
-
77
- // Set Min-Width for when empty
78
- const option = this.el.querySelectorAll("option")[0];
79
- this.edit_button.querySelector('.text-js').style.opacity = 0;
80
- this.edit_button.querySelector('.text-js').innerHTML = option.textContent;
81
- const min_width = this.edit_button.querySelector('.text-js').offsetWidth;
82
- this.edit_button.style.minWidth = min_width + "px";
83
- this.edit_button.querySelector('.text-js').innerHTML = "";
84
- this.edit_button.querySelector('.text-js').style.opacity = null;
87
+ renderValue () {
88
+ const selectedOptions = filter(this.select.querySelectorAll("option"), el => el.selected);
89
+ const html = selectedOptions.map(el => this.options.multiple ? `
90
+ <span class="uniformSelect-selection">
91
+ <span>${el.textContent}</span><span class="uniformSelect-remove">${xIcon}</span>
92
+ </span>
93
+ ` : el.textContent).join(" ");
85
94
 
86
- this.renderSelected();
87
-
88
- this.listenTo(this.edit_button, 'click', this.showOptions);
89
- this.listenTo(this.edit_button, 'click', '.uniformSelect-remove', this.removeSelection);
90
-
91
- return this;
95
+ this.valueEl.innerHTML = html;
96
+ }
97
+
98
+ selectOption (e) {
99
+ const makeActive = !e.target.option.selected;
100
+ if (!this.options.multiple && makeActive) {
101
+ e.target.offsetParent.querySelectorAll('.active').forEach(el => el.classList.remove('active'));
92
102
  }
93
-
94
- renderOptions () {
95
- this.button_options = Helpers.createElement("<div class='uniformSelect-options'>");
96
- if (this.el.name) {
97
- Helpers.addClass(this.button_options, this.el.name.toLowerCase().replace(/[^a-z0-9\-_]+/g, '-'));
98
- }
99
- this.button_options.style.fontSize = Helpers.css(this.el, 'font-size');
103
+ e.target.classList.toggle('active', makeActive);
104
+ e.target.option.selected = makeActive;
100
105
 
101
- Helpers.each(this.el.querySelectorAll('option'), function(el, index){
102
- var button = Helpers.createElement("<button type='button' class='uniformSelect-option block outline text-left'>");
103
- button.option = el;
104
- button.textContent = el.textContent;
105
- button.value = el.value;
106
- if (button.textContent == "") button.innerHTML = "<span class='text-italic text-muted'>None</span>";
107
- if(el.selected){
108
- Helpers.addClass(button, 'active');
109
- button.append(this.activeIcon.cloneNode(true));
110
- } else if (this.options.limit && index > this.options.limit) {
111
- Helpers.addClass(button, 'hide');
112
- }
113
- this.button_options.append(button);
114
- }.bind(this));
115
-
116
- this.listenTo(this.button_options, 'click', '.uniformSelect-option', this.selectOption);
117
-
118
- const actions_el = Helpers.createElement('<div class="uniformSelect-options-actions"></div>');
119
- if (this.options.limit && this.el.querySelectorAll('option').length > this.options.limit) {
120
- const show_all_button = Helpers.createElement("<button type='button' class='uniformSelect-show-all outline blue' style='display: block; border: none'>Show All</button>");
121
- this.listenTo(show_all_button, 'click', function(e){
122
- Helpers.trigger(this.el, 'show_all');
123
- if (this.options.showAll) this.options.showAll(this.button_options);
124
- e.preventDefault();
125
- e.stopPropagation();
126
- })
127
- actions_el.appendChild(show_all_button);
128
- }
129
- if (this.el.multiple) {
130
- var done_button = Helpers.createElement("<button type='button' class='uniformSelect-done block outline blue'>Done</button>");
131
- this.listenTo(done_button, 'click', this.hideOptions);
132
- actions_el.appendChild(done_button);
133
- }
134
- if (!Helpers.is_empty(actions_el)) {
135
- this.button_options.appendChild(actions_el);
136
- }
106
+ if (!this.options.multiple) {
107
+ this.removeOptions();
137
108
  }
138
-
139
- renderSelected () {
140
- const selected_options = Helpers.filter(this.el.querySelectorAll("option"), function(el){
141
- return el.selected;
142
- });
143
- const html = Helpers.map(selected_options, function(el){
144
- return this.el.multiple ? `<span class="uniformSelect-selection">${el.textContent}<span class="uniformSelect-remove"></span></span>` : el.textContent;
145
- }.bind(this)).join(" ");
146
109
 
147
- if (html == "") {
148
- this.edit_button.querySelector('.text-js').innerHTML = "&nbsp;"
149
- } else {
150
- this.edit_button.querySelector('.text-js').innerHTML = html;
151
- }
110
+ trigger(this.select, 'change');
111
+ }
112
+
113
+ removeSelection (e) {
114
+ e.preventDefault();
115
+ e.stopPropagation();
116
+ var option = filter(this.select.querySelectorAll("option"), function(el){
117
+ return el.innerText.trim() == e.target.closest('.uniformSelect-selection').innerText.trim();
118
+ })[0];
119
+ if(!option) return;
120
+ option.selected = false;
121
+ option.button.classList.remove('active');
152
122
 
153
- if(this.button_options) {
154
- Helpers.each(this.button_options.querySelectorAll('.uniformSelect-option'), function(el) {
155
- if(el.option.selected){
156
- Helpers.addClass(el, 'active');
157
- el.append(this.activeIcon.cloneNode(true));
158
- } else {
159
- Helpers.removeClass(el, 'active');
160
- Helpers.each(el.querySelectorAll('.uniformSelect-option-icon'), Helpers.remove);
161
- }
162
- }.bind(this))
163
- }
123
+ trigger(this.select, 'change');
124
+ }
125
+
126
+ toggleOptions (e) {
127
+ if(e && (e.target.matches('.uniformSelect-remove') || e.target.closest('.uniformSelect-remove'))){
128
+ return;
164
129
  }
165
-
166
- hideOptions () {
167
- if(!this.button_options) return;
168
- if(this.button_options_modal) this.button_options_modal.close();
169
- if(this.button_options_popover) {
170
- this.button_options_popover.remove();
171
- delete this.button_options_popover;
172
- }
173
- Helpers.removeClass(this.edit_button, 'active');
130
+ this.el.classList.toggle('active')
131
+ if(this.el.contains('active')){
132
+ this.renderOptions()
133
+ } else {
134
+ this.removeOptions()
174
135
  }
136
+ }
175
137
 
176
- showOptions(e) {
177
- if(Helpers.hasClass(e.target, 'uniformSelect-remove')) return;
178
- if(this.button_options_modal) return this.hideOptions();
179
- if(this.button_options_popover) return this.hideOptions();
180
- if(!this.button_options) this.renderOptions();
181
- Helpers.addClass(this.edit_button, 'active');
182
- // For Mobile: Render Full Screen
183
- if(window.innerWidth < 720) {
184
- const content = Helpers.createElement('<div class="uniformSelect-modal">');
185
- content.append(this.button_options);
186
- if (this.options.label) {
187
- content.append(`<div class="uniformSelect-label margin-bottom text-bold">${this.options.label}</div>`);
188
- }
189
- this.button_options_modal = new Modal({
190
- content: content,
191
- klass: '-reset'
192
- }).render();
193
- this.button_options_modal.on('closed', () => {
194
- Helpers.removeClass(this.edit_button, 'active');
195
- delete this.button_options_modal;
196
- });
197
- this.listenTo(content, 'click', function(e){
198
- if(e.target == content) {
199
- this.button_options_modal.close()
200
- }
201
- });
202
- // For Larger: Render Popover
203
- } else {
204
- this.button_options.style.minWidth = this.edit_button.offsetWidth + "px";
205
- this.button_options_popover = new Popover({
206
- offset: {top: 1},
207
- anchor: this.edit_button,
208
- align: '0px bottom',
209
- content: this.button_options,
210
- container: this.options.container
211
- }).render()
212
- this.button_options_popover.on('hidden', () => {
213
- Helpers.removeClass(this.edit_button, 'active');
214
- this.button_options_popover.remove();
215
- delete this.button_options_popover;
216
- })
217
- }
218
- }
138
+ renderOptions () {
139
+ const options = createElement("div", {
140
+ class: 'uniformSelect-options'
141
+ });
142
+
143
+ options.style.fontSize = css(this.el, 'font-size')
144
+
145
+ this.select.querySelectorAll('option').forEach(function(option, index){
146
+ var button = createElement("button", {
147
+ type: 'button',
148
+ class: 'uniformSelect-option'
149
+ });
150
+ button.option = option;
151
+ option.button = button;
152
+ button.textContent = option.textContent;
153
+ button.value = option.value;
154
+ if (button.textContent == "") button.innerHTML = "<span class='text-italic text-muted'>None</span>";
155
+ button.classList.toggle('active', option.selected);
156
+
157
+ console.log(this.options.limit, index);
158
+ if (this.options.limit && (index + 1) > this.options.limit) {
159
+ button.classList.add('hide')
160
+ }
161
+ options.append(button);
162
+ }, this);
163
+
164
+ this.listenTo(options, 'click', '.uniformSelect-option', this.selectOption);
219
165
 
220
- selectOption(e) {
221
- const button = e.target;
222
- if (!this.el.multiple) {
223
- Helpers.each(this.button_options.querySelectorAll('.uniformSelect-option.active .uniformSelect-option-icon'), Helpers.remove);
224
- Helpers.removeClass(this.button_options.querySelectorAll('.uniformSelect-option.active'), 'active');
225
- }
226
- Helpers.toggleClass(e.target, 'active');
227
- e.target.option.selected = Helpers.hasClass(e.target, 'active');
228
- if (Helpers.hasClass(e.target, 'active')) {
229
- e.target.append(this.activeIcon.cloneNode(true));
230
- } else {
231
- Helpers.each(e.target.querySelectorAll('.uniformSelect-option-icon'), Helpers.remove);
232
- }
233
- if (!this.el.multiple) {
234
- this.hideOptions();
235
- }
236
- Helpers.trigger(this.el, 'change');
237
- }
166
+
167
+ const actions = createElement('div', {
168
+ class: 'uniformSelect-actions'
169
+ });
238
170
 
239
- removeSelection(e) {
240
- e.preventDefault();
241
- var target = Helpers.filter(this.el.querySelectorAll("option"), function(el){
242
- return el.innerText.trim() == e.target.parentNode.innerText.trim();
243
- })[0];
244
- if(!target) return;
245
- target.selected = false;
246
- Helpers.trigger(this.el, 'change');
171
+ if (this.options.limit && this.htmlOptions.length > this.options.limit) {
172
+ const button = createElement('button', {
173
+ type: 'button',
174
+ class: 'uniformSelect-show-all',
175
+ children: 'Show All'
176
+ });
177
+ this.listenTo(button, 'click', this.showAllOptions.bind(this))
178
+ actions.append(button);
179
+ }
180
+ if (this.options.multiple) {
181
+ const button = createElement('button', {
182
+ type: 'button',
183
+ class: 'uniformSelect-done',
184
+ children: ['Done']
185
+ });
186
+ this.listenTo(button, 'click', this.removeOptions.bind(this));
187
+ actions.append(button);
247
188
  }
189
+ if (!isEmpty(actions)) {
190
+ options.append(actions);
191
+ }
192
+
193
+ this.popover = new Popover({
194
+ offset: {top: 1},
195
+ align: '0px bottom',
196
+ anchor: this.el,
197
+ content: options,
198
+ container: this.options.container
199
+ }).render()
200
+
201
+ this.listenTo(this.popover, 'hidden', this.removeOptions);
202
+ }
203
+
204
+ removeOptions () {
205
+ this.el.classList.remove('active')
206
+ if(!this.popover) return;
207
+ this.popover.remove()
208
+ }
209
+
210
+ showAllOptions (e) {
211
+ e.preventDefault();
212
+ e.stopPropagation();
213
+ if(!this.popover) return;
214
+ this.popover.el.querySelectorAll('button.hide').forEach(el => el.classList.remove('hide'));
215
+ var button = this.popover.el.querySelector('.uniformSelect-show-all');
216
+ button.parentNode.removeChild(button);
217
+ }
248
218
  }