uniform-ui 2.4.1 → 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 (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
  }