uniform-ui 2.3.7 → 3.0.0.beta4

Sign up to get free protection for your applications and to get access to all the features.
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
  }