uniform-ui 2.4.1 → 3.0.0.beta2

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