uniform-ui 2.3.6 → 3.0.0.beta2

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 (69) hide show
  1. checksums.yaml +4 -4
  2. data/lib/assets/javascripts/uniform.es5.js +1 -1
  3. data/lib/assets/javascripts/uniform.js +8 -21
  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 +13 -12
  10. data/lib/assets/javascripts/uniform/popover.js +45 -26
  11. data/lib/assets/javascripts/uniform/resizer.js +26 -30
  12. data/lib/assets/javascripts/uniform/select.js +188 -220
  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 -7
  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 +53 -0
  34. data/lib/assets/stylesheets/uniform/utilities/background.scss +9 -0
  35. data/lib/assets/stylesheets/uniform/utilities/borders.scss +85 -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/text.scss +158 -0
  42. data/lib/assets/stylesheets/uniform/variables.scss +104 -44
  43. data/lib/uniform/version.rb +1 -1
  44. metadata +24 -48
  45. data/lib/assets/javascripts/uniform.jquery.js +0 -152
  46. data/lib/assets/javascripts/uniform/dom-helpers.js +0 -158
  47. data/lib/assets/javascripts/uniform/floating-label.js +0 -54
  48. data/lib/assets/stylesheets/uniform-print.scss +0 -1
  49. data/lib/assets/stylesheets/uniform/components.scss +0 -11
  50. data/lib/assets/stylesheets/uniform/components/alert.scss +0 -72
  51. data/lib/assets/stylesheets/uniform/components/card.scss +0 -93
  52. data/lib/assets/stylesheets/uniform/components/form.scss +0 -149
  53. data/lib/assets/stylesheets/uniform/components/form/checkbox-collection.scss +0 -103
  54. data/lib/assets/stylesheets/uniform/components/form/checkbox.scss +0 -58
  55. data/lib/assets/stylesheets/uniform/components/form/floating-label.scss +0 -65
  56. data/lib/assets/stylesheets/uniform/components/form/input-group.scss +0 -56
  57. data/lib/assets/stylesheets/uniform/components/form/tristate.scss +0 -88
  58. data/lib/assets/stylesheets/uniform/components/grid.scss +0 -179
  59. data/lib/assets/stylesheets/uniform/components/row.scss +0 -67
  60. data/lib/assets/stylesheets/uniform/components/tooltip.scss +0 -41
  61. data/lib/assets/stylesheets/uniform/helpers.scss +0 -133
  62. data/lib/assets/stylesheets/uniform/helpers/border.scss +0 -28
  63. data/lib/assets/stylesheets/uniform/helpers/colors.scss +0 -24
  64. data/lib/assets/stylesheets/uniform/helpers/margin.scss +0 -27
  65. data/lib/assets/stylesheets/uniform/helpers/padding.scss +0 -9
  66. data/lib/assets/stylesheets/uniform/helpers/position.scss +0 -20
  67. data/lib/assets/stylesheets/uniform/helpers/sizes.scss +0 -38
  68. data/lib/assets/stylesheets/uniform/helpers/text.scss +0 -152
  69. data/lib/assets/stylesheets/uniform/print/grid.scss +0 -50
@@ -0,0 +1,63 @@
1
+ import Component from './component';
2
+ import { isVisible, isFocus, hasClass, addClass, removeClass, 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
+ addClass(this.el, '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
+ addClass(this.el, 'present');
56
+ }
57
+
58
+ blur (e) {
59
+ if(this.input.value == ""){
60
+ removeClass(this.el, '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 {addClass, hasClass, removeClass, css, trigger} 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
+ addClass(this.el, 'uniformModal');
19
20
  this.listenTo(document, 'keyup', this.keyup);
20
21
  this.listenTo(this.el, 'click', this.checkCloseButton);
21
22
  }
@@ -31,17 +32,17 @@ export default class Modal extends Component {
31
32
 
32
33
  this.highest_z_index = 0;
33
34
  this.overlay = document.createElement('div');
34
- Helpers.addClass(this.overlay, 'uniformModal-overlay');
35
+ addClass(this.overlay, 'uniformModal-overlay');
35
36
 
36
37
  this.blur = document.createElement('div');
37
- Helpers.addClass(this.blur, 'uniformModal-blur');
38
+ addClass(this.blur, 'uniformModal-blur');
38
39
 
39
40
  this.original_scroll = window.scrollY;
40
41
  this.blur.style.top = 0 - this.original_scroll + "px";
41
42
 
42
43
  if (document.body.querySelectorAll('.uniformModal').length > 0) {
43
44
  this.highest_z_index = Math.max(Array.prototype.map.call(document.body.querySelectorAll('.uniformModal'), function(el){
44
- return parseInt(Helpers.css(el, 'zIndex'));
45
+ return parseInt(css(el, 'zIndex'));
45
46
  }));
46
47
  this.el.style.zIndex = this.highest_z_index + 2;
47
48
  }
@@ -57,12 +58,12 @@ export default class Modal extends Component {
57
58
  }
58
59
  }
59
60
 
60
- Helpers.addClass(document.body, 'uniformModal-active');
61
+ addClass(document.body, 'uniformModal-active');
61
62
  document.body.appendChild(this.blur);
62
63
  document.body.appendChild(this.el);
63
64
 
64
65
  var container = document.createElement('div');
65
- Helpers.addClass(container, 'uniformModal-container');
66
+ addClass(container, 'uniformModal-container');
66
67
  if (content instanceof Node) {
67
68
  container.appendChild(content);
68
69
  } else {
@@ -70,27 +71,27 @@ export default class Modal extends Component {
70
71
  }
71
72
 
72
73
  var closeButton = document.createElement('div');
73
- Helpers.addClass(closeButton, 'uniformModal-close');
74
+ addClass(closeButton, 'uniformModal-close');
74
75
  this.el.appendChild(closeButton);
75
76
 
76
77
  this.el.style.top = window.scrollY;
77
78
  this.listenTo(this.overlay, 'click', this.close);
78
79
  this.el.appendChild(container);
79
80
 
80
- if (this.options.klass) Helpers.addClass(container, this.options.klass);
81
- if (content.innerHTML) Helpers.trigger(content, 'rendered');
81
+ if (this.options.klass) addClass(container, this.options.klass);
82
+ if (content.innerHTML) trigger(content, 'rendered');
82
83
  this.trigger('rendered');
83
84
 
84
85
  return this;
85
86
  }
86
87
 
87
88
  checkCloseButton (e) {
88
- if(Helpers.hasClass(e.target, 'uniformModal-close'))
89
+ if(hasClass(e.target, 'uniformModal-close'))
89
90
  this.close();
90
91
  }
91
92
 
92
93
  close () {
93
- Helpers.removeClass(document.querySelectorAll('uniformModal-active'), 'uniformModal-active');
94
+ removeClass(document.querySelectorAll('uniformModal-active'), 'uniformModal-active');
94
95
  var elements = this.blur.children;
95
96
  var elementCount = elements.length
96
97
  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 { closest, offset, outerWidth, outerHeight, removeClass, addClass } from 'dolla';
3
3
 
4
4
  /*
5
5
  Requirements
@@ -12,13 +12,14 @@ 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) {
18
19
  this.showing = false;
19
20
  options = options || {};
20
21
  this.options = {
21
- zIndex: 2,
22
+ zIndex: 3,
22
23
  container: document.body,
23
24
  align: 'center bottom',
24
25
  anchor: document.body,
@@ -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 = closest(this.options.anchor, this.options.container)
40
42
  }
43
+ this.options.container = this.options.container || document.body
44
+
41
45
  }
42
46
 
43
47
  render () {
@@ -59,7 +63,7 @@ export default class Popover extends Component {
59
63
 
60
64
  resize () {
61
65
  this.setPosition();
62
- const bounds = this.el.getBoundingClientRect();
66
+ let bounds = this.el.getBoundingClientRect();
63
67
  const body_bounds = document.body.getBoundingClientRect();
64
68
  const window_bounds = {
65
69
  top: 0,
@@ -69,10 +73,14 @@ export default class Popover extends Component {
69
73
  };
70
74
 
71
75
  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"
76
+ if (bounds.bottom > Math.max(body_bounds.bottom, window_bounds.bottom)) {
77
+ var [leftAlign, topAlign] = this.options.align.split(" ");
78
+ this.setPosition(`${leftAlign} top`);
79
+ bounds = this.el.getBoundingClientRect()
80
+ if(bounds.top < 0) {
81
+ this.setPosition(`${leftAlign} bottom`);
82
+ }
83
+ bounds = this.el.getBoundingClientRect()
76
84
  }
77
85
  if (bounds.top < body_bounds.top) {
78
86
  const difference = body_bounds.top - bounds.top
@@ -83,6 +91,7 @@ export default class Popover extends Component {
83
91
  const difference = body_bounds.left - bounds.left
84
92
  if(this.el.style.left != null) this.el.style.left = parseInt(this.el.style.left) + difference + "px"
85
93
  if(this.el.style.right != null) this.el.style.right = parseInt(this.el.style.right) - difference + "px"
94
+ bounds = this.el.getBoundingClientRect()
86
95
  }
87
96
  if (bounds.right > body_bounds.right) {
88
97
  const difference = body_bounds.right - bounds.right
@@ -96,43 +105,43 @@ export default class Popover extends Component {
96
105
  var [leftAlign, topAlign] = align.split(" ");
97
106
  leftAlign = leftAlign || "bottom";
98
107
 
99
- var offset = Helpers.offset(this.options.anchor);
108
+ var anchorOffset = offset(this.options.anchor);
100
109
  var container = this.options.container;
101
110
  if(getComputedStyle(container)['position'] == "static") container = container.offsetParent;
102
111
  if(!container) container = document.body;
103
112
 
104
- var containerOffset = Helpers.offset(container);
105
- offset = {
106
- top: offset.top - containerOffset.top,
107
- left: offset.left - containerOffset.left
113
+ var containerOffset = offset(container);
114
+ anchorOffset = {
115
+ top: anchorOffset.top - containerOffset.top,
116
+ left: anchorOffset.left - containerOffset.left
108
117
  }
109
118
 
110
119
  var position = {};
111
120
  if(leftAlign == 'left'){
112
- position.right = Helpers.outerWidth(container) - offset.left;
121
+ position.right = outerWidth(container) - anchorOffset.left;
113
122
  } else if(leftAlign == 'center'){
114
- position.left = offset.left + Helpers.outerWidth(this.options.anchor) / 2 - Helpers.outerWidth(this.el) / 2;
123
+ position.left = anchorOffset.left + outerWidth(this.options.anchor) / 2 - outerWidth(this.el) / 2;
115
124
  } else if (leftAlign == 'right'){
116
- position.left = offset.left + Helpers.outerWidth(this.options.anchor);
125
+ position.left = anchorOffset.left + outerWidth(this.options.anchor);
117
126
  } else if (leftAlign.includes("px")){
118
- position.left = offset.left + parseInt(leftAlign);
127
+ position.left = anchorOffset.left + parseInt(leftAlign);
119
128
  }
120
129
 
121
130
  if(topAlign == 'top'){
122
- let height = Helpers.outerHeight(container);
131
+ let height = outerHeight(container);
123
132
  if(container == document.body && getComputedStyle(container)['position'] == "static"){
124
133
  height = window.innerHeight;
125
134
  } else if (container == document.body) {
126
135
  height = Math.max(height, document.body.clientHeight);
127
136
  }
128
- position.bottom = height - offset.top;
137
+ position.bottom = height - anchorOffset.top;
129
138
  } else if(topAlign == 'center'){
130
- position.top = offset.top + Helpers.outerHeight(this.options.anchor) / 2;
139
+ position.top = anchorOffset.top + outerHeight(this.options.anchor) / 2;
131
140
  position.transform = "translateY(-50%)";
132
141
  } else if (topAlign == 'bottom'){
133
- position.top = offset.top + Helpers.outerHeight(this.options.anchor);
142
+ position.top = anchorOffset.top + outerHeight(this.options.anchor);
134
143
  } else if (topAlign.includes("px")){
135
- position.top = offset.top + parseInt(topAlign);
144
+ position.top = anchorOffset.top + parseInt(topAlign);
136
145
  }
137
146
 
138
147
  if(this.options.offset.left) position.left += parseInt(this.options.offset.left);
@@ -145,9 +154,9 @@ export default class Popover extends Component {
145
154
  this.el.style.top = null;
146
155
  this.el.style.bottom = null;
147
156
  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);
157
+ removeClass(this.el, 'popover-left popover-right popover-center popover-top popover-bottom');
158
+ addClass(this.el, 'popover-' + topAlign);
159
+ addClass(this.el, 'popover-' + leftAlign);
151
160
  Object.keys(position).forEach(function(key){
152
161
  this.el.style[key] = position[key] + (key != "transform" ? "px" : "");
153
162
  }, this);
@@ -160,11 +169,13 @@ export default class Popover extends Component {
160
169
  if (e.target == this.options.anchor) return;
161
170
  if (this.el.contains(e.target)) return;
162
171
  if (this.options.anchor.contains(e.target)) return;
172
+ if (this.persisting) return;
163
173
  this.hide();
164
174
  }
165
175
 
166
176
  checkEscape (e) {
167
177
  if(e.which != 27) return;
178
+ if (this.persisting) return;
168
179
  this.hide();
169
180
  }
170
181
 
@@ -193,4 +204,12 @@ export default class Popover extends Component {
193
204
  flag = flag || this.showing;
194
205
  if(flag) this.hide(); else this.show();
195
206
  }
207
+
208
+ persist() {
209
+ this.persisting = true;
210
+ }
211
+
212
+ unpersist() {
213
+ this.persisting = false;
214
+ }
196
215
  }
@@ -1,43 +1,39 @@
1
1
  import Component from './component';
2
- import * as Helpers from './dom-helpers';
2
+ import { trigger, toggleClass } 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
+ toggleClass(this.el, css_class, width > value)
32
+ } else if (attribute == "max-width") {
33
+ toggleClass(this.el, css_class, width < value)
34
+ } else {
35
+ throw "unsupported media feature"
41
36
  }
37
+ });
42
38
  }
43
39
  }
@@ -1,250 +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, toggleClass, removeClass, isEmpty, trigger, hasClass, closest } 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: document.body
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
-
74
- // Set Min-Width for when empty
75
- const option = this.el.querySelectorAll("option")[0];
76
- this.edit_button.querySelector('.text-js').style.opacity = 0;
77
- this.edit_button.querySelector('.text-js').innerHTML = option.textContent;
78
- const min_width = this.edit_button.querySelector('.text-js').offsetWidth;
79
- this.edit_button.style.minWidth = min_width + "px";
80
- this.edit_button.querySelector('.text-js').innerHTML = "";
81
- this.edit_button.querySelector('.text-js').style.opacity = null;
82
-
83
- this.renderSelected();
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(" ");
84
94
 
85
- this.listenTo(this.edit_button, 'click', this.showOptions);
86
- this.listenTo(this.edit_button, 'click', '.uniformSelect-remove', this.removeSelection);
87
-
88
- 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
+ removeClass(e.target.offsetParent.querySelectorAll('.active'), 'active');
89
102
  }
90
-
91
- renderOptions () {
92
- this.button_options = Helpers.createElement("<div class='uniformSelect-options'>");
93
- if (this.el.name) {
94
- Helpers.addClass(this.button_options, this.el.name.toLowerCase().replace(/[^a-z0-9\-_]+/g, '-'));
95
- }
96
- this.button_options.style.fontSize = Helpers.css(this.el, 'font-size');
103
+ toggleClass(e.target, 'active', makeActive);
104
+ e.target.option.selected = makeActive;
97
105
 
98
- Helpers.each(this.el.querySelectorAll('option'), function(el, index){
99
- var button = Helpers.createElement("<button type='button' class='uniformSelect-option block outline text-left'>");
100
- button.option = el;
101
- button.textContent = el.textContent;
102
- button.value = el.value;
103
- if (button.textContent == "") button.innerHTML = "<span class='text-italic text-muted'>None</span>";
104
- if(el.selected){
105
- Helpers.addClass(button, 'active');
106
- button.append(this.activeIcon.cloneNode(true));
107
- } else if (this.options.limit && index > this.options.limit) {
108
- Helpers.addClass(button, 'hide');
109
- }
110
- this.button_options.append(button);
111
- }.bind(this));
112
-
113
- this.listenTo(this.button_options, 'click', '.uniformSelect-option', this.selectOption);
114
-
115
- const actions_el = Helpers.createElement('<div class="uniformSelect-options-actions"></div>');
116
- if (this.options.limit && this.el.querySelectorAll('option').length > this.options.limit) {
117
- const show_all_button = Helpers.createElement("<button type='button' class='uniformSelect-show-all outline blue' style='display: block; border: none'>Show All</button>");
118
- this.listenTo(show_all_button, 'click', function(e){
119
- Helpers.trigger(this.el, 'show_all');
120
- if (this.options.showAll) this.options.showAll(this.button_options);
121
- e.preventDefault();
122
- e.stopPropagation();
123
- })
124
- actions_el.appendChild(show_all_button);
125
- }
126
- if (this.el.multiple) {
127
- var done_button = Helpers.createElement("<button type='button' class='uniformSelect-done block outline blue'>Done</button>");
128
- this.listenTo(done_button, 'click', this.hideOptions);
129
- actions_el.appendChild(done_button);
130
- }
131
- if (!Helpers.is_empty(actions_el)) {
132
- this.button_options.appendChild(actions_el);
133
- }
106
+ if (!this.options.multiple) {
107
+ this.removeOptions();
134
108
  }
135
-
136
- renderSelected () {
137
- const selected_options = Helpers.filter(this.el.querySelectorAll("option"), function(el){
138
- return el.selected;
139
- });
140
- const html = Helpers.map(selected_options, function(el){
141
- return this.el.multiple ? `<span class="uniformSelect-selection">${el.textContent}<span class="uniformSelect-remove"></span></span>` : el.textContent;
142
- }.bind(this)).join(" ");
143
109
 
144
- if (html == "") {
145
- this.edit_button.querySelector('.text-js').innerHTML = "&nbsp;"
146
- } else {
147
- this.edit_button.querySelector('.text-js').innerHTML = html;
148
- }
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() == closest(e.target, '.uniformSelect-selection').innerText.trim();
118
+ })[0];
119
+ if(!option) return;
120
+ option.selected = false;
121
+ option.button.classList.remove('active');
149
122
 
150
- if(this.button_options) {
151
- Helpers.each(this.button_options.querySelectorAll('.uniformSelect-option'), function(el) {
152
- if(el.option.selected){
153
- Helpers.addClass(el, 'active');
154
- el.append(this.activeIcon.cloneNode(true));
155
- } else {
156
- Helpers.removeClass(el, 'active');
157
- Helpers.each(el.querySelectorAll('.uniformSelect-option-icon'), Helpers.remove);
158
- }
159
- }.bind(this))
160
- }
123
+ trigger(this.select, 'change');
124
+ }
125
+
126
+ toggleOptions (e) {
127
+ if(e && (e.target.matches('.uniformSelect-remove') || closest(e.target, '.uniformSelect-remove'))){
128
+ return;
161
129
  }
162
-
163
- hideOptions () {
164
- if(!this.button_options) return;
165
- if(this.button_options_modal) this.button_options_modal.close();
166
- if(this.button_options_popover) {
167
- this.button_options_popover.remove();
168
- delete this.button_options_popover;
169
- }
170
- Helpers.removeClass(this.edit_button, 'active');
130
+ toggleClass(this.el, 'active')
131
+ if(hasClass(this.el, 'active')){
132
+ this.renderOptions()
133
+ } else {
134
+ this.removeOptions()
171
135
  }
136
+ }
172
137
 
173
- showOptions(e) {
174
- if(Helpers.hasClass(e.target, 'uniformSelect-remove')) return;
175
- if(this.button_options_modal) return this.hideOptions();
176
- if(this.button_options_popover) return this.hideOptions();
177
- if(!this.button_options) this.renderOptions();
178
- Helpers.addClass(this.edit_button, 'active');
179
- // For Mobile: Render Full Screen
180
- if(window.innerWidth < 720) {
181
- const content = Helpers.createElement('<div class="uniformSelect-modal">');
182
- content.append(this.button_options);
183
- if (this.options.label) {
184
- content.append(`<div class="uniformSelect-label margin-bottom text-bold">${this.options.label}</div>`);
185
- }
186
- this.button_options_modal = new Modal({
187
- content: content,
188
- klass: '-reset'
189
- }).render();
190
- this.button_options_modal.on('closed', () => {
191
- Helpers.removeClass(this.edit_button, 'active');
192
- delete this.button_options_modal;
193
- });
194
- this.listenTo(content, 'click', function(e){
195
- if(e.target == content) {
196
- this.button_options_modal.close()
197
- }
198
- });
199
- // For Larger: Render Popover
200
- } else {
201
- this.button_options.style.minWidth = this.edit_button.offsetWidth + "px";
202
- this.button_options_popover = new Popover({
203
- offset: {top: 1},
204
- anchor: this.edit_button,
205
- align: '0px bottom',
206
- content: this.button_options,
207
- container: this.options.container
208
- }).render()
209
- this.button_options_popover.on('hidden', () => {
210
- Helpers.removeClass(this.edit_button, 'active');
211
- this.button_options_popover.remove();
212
- delete this.button_options_popover;
213
- })
214
- }
215
- }
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
+ toggleClass(button, '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);
216
165
 
217
- selectOption(e) {
218
- if (!this.el.multiple) {
219
- Helpers.each(Helpers.filter(this.el.querySelectorAll("option"), function(el){
220
- return el.selected;
221
- }), function (child) {
222
- child.selected = false;
223
- });
224
- Helpers.each(this.button_options.querySelectorAll('.uniformSelect-option.active .uniformSelect-option-icon'), Helpers.remove);
225
- Helpers.removeClass(this.button_options.querySelectorAll('.uniformSelect-option.active'), 'active');
226
- }
227
- Helpers.toggleClass(e.target, 'active');
228
- e.target.option.selected = Helpers.hasClass(e.target, 'active');
229
- Helpers.each(this.button_options.querySelectorAll("button"), function(button) {
230
- if(!button.option) return;
231
- Helpers.toggleClass(button, 'active', button.option.selected);
232
- if (Helpers.hasClass(button, 'active')) {
233
- button.append(this.activeIcon.cloneNode(true));
234
- } else {
235
- Helpers.each(button.querySelectorAll('.uniformSelect-option-icon'), Helpers.remove);
236
- }
237
- }.bind(this))
238
- Helpers.trigger(this.el, 'change');
239
- }
166
+
167
+ const actions = createElement('div', {
168
+ class: 'uniformSelect-actions'
169
+ });
240
170
 
241
- removeSelection(e) {
242
- e.preventDefault();
243
- var target = Helpers.filter(this.el.querySelectorAll("option"), function(el){
244
- return el.innerText.trim() == e.target.parentNode.innerText.trim();
245
- })[0];
246
- if(!target) return;
247
- target.selected = false;
248
- 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);
188
+ }
189
+ if (!isEmpty(actions)) {
190
+ options.append(actions);
249
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
+ removeClass(this.popover.el.querySelectorAll('button.hide'), 'hide');
215
+ var button = this.popover.el.querySelector('.uniformSelect-show-all');
216
+ button.parentNode.removeChild(button);
217
+ }
250
218
  }