uniform-ui 2.4.1 → 3.0.0.beta8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/lib/assets/javascripts/uniform.js +13 -13
  3. data/lib/assets/javascripts/uniform/checkbox.js +59 -16
  4. data/lib/assets/javascripts/uniform/component.js +27 -4
  5. data/lib/assets/javascripts/uniform/dropdown.js +78 -209
  6. data/lib/assets/javascripts/uniform/floating-label-input.js +65 -0
  7. data/lib/assets/javascripts/uniform/icons.js +12 -3
  8. data/lib/assets/javascripts/uniform/modal.js +29 -30
  9. data/lib/assets/javascripts/uniform/popover.js +26 -24
  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 +39 -0
  21. data/lib/assets/stylesheets/uniform/components/label.scss +21 -16
  22. data/lib/assets/stylesheets/uniform/components/loaders.scss +28 -54
  23. data/lib/assets/stylesheets/uniform/components/modal.scss +21 -36
  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 +26 -0
  30. data/lib/assets/stylesheets/uniform/defaults.scss +31 -10
  31. data/lib/assets/stylesheets/uniform/functions.scss +32 -7
  32. data/lib/assets/stylesheets/uniform/mixins.scss +110 -57
  33. data/lib/assets/stylesheets/uniform/utilities.scss +55 -0
  34. data/lib/assets/stylesheets/uniform/utilities/background.scss +9 -0
  35. data/lib/assets/stylesheets/uniform/utilities/borders.scss +84 -0
  36. data/lib/assets/stylesheets/uniform/utilities/effects.scss +172 -0
  37. data/lib/assets/stylesheets/uniform/utilities/layout.scss +181 -0
  38. data/lib/assets/stylesheets/uniform/utilities/position.scss +42 -0
  39. data/lib/assets/stylesheets/uniform/utilities/sizing.scss +60 -0
  40. data/lib/assets/stylesheets/uniform/utilities/spacing.scss +68 -0
  41. data/lib/assets/stylesheets/uniform/utilities/svg.scss +5 -0
  42. data/lib/assets/stylesheets/uniform/utilities/text.scss +158 -0
  43. data/lib/assets/stylesheets/uniform/variables.scss +113 -42
  44. data/lib/uniform/version.rb +1 -1
  45. metadata +22 -45
  46. data/lib/assets/javascripts/uniform.jquery.js +0 -152
  47. data/lib/assets/javascripts/uniform/dom-helpers.js +0 -158
  48. data/lib/assets/javascripts/uniform/floating-label.js +0 -54
  49. data/lib/assets/stylesheets/uniform-print.scss +0 -1
  50. data/lib/assets/stylesheets/uniform/components.scss +0 -11
  51. data/lib/assets/stylesheets/uniform/components/alert.scss +0 -72
  52. data/lib/assets/stylesheets/uniform/components/card.scss +0 -93
  53. data/lib/assets/stylesheets/uniform/components/form.scss +0 -149
  54. data/lib/assets/stylesheets/uniform/components/form/checkbox-collection.scss +0 -103
  55. data/lib/assets/stylesheets/uniform/components/form/checkbox.scss +0 -58
  56. data/lib/assets/stylesheets/uniform/components/form/floating-label.scss +0 -65
  57. data/lib/assets/stylesheets/uniform/components/form/input-group.scss +0 -56
  58. data/lib/assets/stylesheets/uniform/components/form/tristate.scss +0 -88
  59. data/lib/assets/stylesheets/uniform/components/grid.scss +0 -179
  60. data/lib/assets/stylesheets/uniform/components/row.scss +0 -67
  61. data/lib/assets/stylesheets/uniform/components/tooltip.scss +0 -41
  62. data/lib/assets/stylesheets/uniform/helpers.scss +0 -133
  63. data/lib/assets/stylesheets/uniform/helpers/border.scss +0 -28
  64. data/lib/assets/stylesheets/uniform/helpers/colors.scss +0 -24
  65. data/lib/assets/stylesheets/uniform/helpers/margin.scss +0 -27
  66. data/lib/assets/stylesheets/uniform/helpers/padding.scss +0 -9
  67. data/lib/assets/stylesheets/uniform/helpers/position.scss +0 -20
  68. data/lib/assets/stylesheets/uniform/helpers/sizes.scss +0 -38
  69. data/lib/assets/stylesheets/uniform/helpers/text.scss +0 -152
  70. data/lib/assets/stylesheets/uniform/print/grid.scss +0 -50
@@ -0,0 +1,65 @@
1
+ import Component from './component';
2
+ import { isVisible, isFocus, css, createElement } from 'dolla';
3
+
4
+ export default class FloatingLabel extends Component {
5
+
6
+ initialize(options){
7
+ if(options.input instanceof Element) {
8
+ this.input = options.input
9
+ } else {
10
+ this.input = createElement('input', Object.assign({}, {
11
+ type: this.constructor.type
12
+ }, options.input)) // TODO filter options to dolla.HTML_ATTRIBUTES
13
+ }
14
+ this.label = createElement('label', {
15
+ for: this.input.id,
16
+ children: [options.label]
17
+ });
18
+ this.input.setAttribute('aria-label', options.label);
19
+
20
+ this.el.classList.add('uniformFloatingLabelInput');
21
+
22
+ this.listenTo(this.input, 'focus', this.focus);
23
+ this.listenTo(this.input, 'blur', this.blur);
24
+ this.listenTo(this.input, 'revealed', this.render);
25
+ }
26
+
27
+ render () {
28
+ if(!isVisible(this.input)) return this;
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
+ return this;
54
+ }
55
+
56
+ focus (e) {
57
+ this.el.classList.add('present');
58
+ }
59
+
60
+ blur (e) {
61
+ if(this.input.value == ""){
62
+ this.el.classList.remove('present');
63
+ }
64
+ }
65
+ }
@@ -1,16 +1,25 @@
1
1
  let check = `
2
- <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1em" height="1em" viewBox="0 0 32 32">
2
+ <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 32 32">
3
3
  <path d="M28.998 8.531l-2.134-2.134c-0.394-0.393-1.030-0.393-1.423 0l-12.795 12.795-6.086-6.13c-0.393-0.393-1.029-0.393-1.423 0l-2.134 2.134c-0.393 0.394-0.393 1.030 0 1.423l8.924 8.984c0.393 0.393 1.030 0.393 1.423 0l15.648-15.649c0.393-0.392 0.393-1.030 0-1.423z"></path>
4
4
  </svg>
5
5
  `.trim()
6
6
 
7
7
  let arrow_down = `
8
- <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1em" height="1em" viewBox="0 0 20 20">
8
+ <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 20 20">
9
9
  <path d="M13.418 7.601c0.271-0.268 0.709-0.268 0.979 0s0.271 0.701 0 0.969l-3.907 3.83c-0.271 0.268-0.709 0.268-0.979 0l-3.908-3.83c-0.27-0.268-0.27-0.701 0-0.969s0.708-0.268 0.979 0l3.418 3.14 3.418-3.14z"></path>
10
10
  </svg>
11
11
  `.trim()
12
12
 
13
+ let x = `
14
+ <svg version="1.2" baseProfile="tiny" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="64px" height="64px" viewBox="0 0 64 64" xml:space="preserve">
15
+ <g><rect x="-2.352" y="29.385" transform="matrix(0.7071 0.7071 -0.7071 0.7071 32.3545 -14.3899)" width="71.799" height="4.95"/></g>
16
+ <g><rect x="-2.374" y="29.376" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -12.7023 33.0352)" width="71.799" height="4.95"/></g>
17
+ </svg>
18
+
19
+ `.trim()
20
+
13
21
  export {
14
22
  check,
15
- arrow_down
23
+ arrow_down,
24
+ x
16
25
  }
@@ -1,5 +1,5 @@
1
1
  import Component from './component';
2
- import * as Helpers from './dom-helpers';
2
+ import {css, trigger, append, createElement} from 'dolla';
3
3
 
4
4
  /* UniformModal.initialize
5
5
  Options
@@ -14,8 +14,9 @@ export default class Modal extends Component {
14
14
  this.options = {};
15
15
  this.options.klass = options.klass || false;
16
16
  this.content = options.content;
17
+ this.el.removeAttribute('content');
17
18
 
18
- Helpers.addClass(this.el, 'uniformModal');
19
+ this.el.classList.add('uniformModal');
19
20
  this.listenTo(document, 'keyup', this.keyup);
20
21
  this.listenTo(this.el, 'click', this.checkCloseButton);
21
22
  }
@@ -27,27 +28,21 @@ export default class Modal extends Component {
27
28
 
28
29
  render () {
29
30
  var that = this;
30
- var content = typeof this.content == 'function' ? this.content() : this.content;
31
31
 
32
32
  this.highest_z_index = 0;
33
- this.overlay = document.createElement('div');
34
- Helpers.addClass(this.overlay, 'uniformModal-overlay');
35
-
36
- this.blur = document.createElement('div');
37
- Helpers.addClass(this.blur, 'uniformModal-blur');
33
+ this.overlay = createElement('div', {class: 'uniformModal-overlay'});
34
+ this.blur = createElement('div', {class: 'uniformModal-blur'});
38
35
 
39
36
  this.original_scroll = window.scrollY;
40
37
  this.blur.style.top = 0 - this.original_scroll + "px";
41
38
 
42
39
  if (document.body.querySelectorAll('.uniformModal').length > 0) {
43
40
  this.highest_z_index = Math.max(Array.prototype.map.call(document.body.querySelectorAll('.uniformModal'), function(el){
44
- return parseInt(Helpers.css(el, 'zIndex'));
41
+ return parseInt(css(el, 'zIndex'));
45
42
  }));
46
43
  this.el.style.zIndex = this.highest_z_index + 2;
47
44
  }
48
45
 
49
- this.el.appendChild(this.overlay);
50
-
51
46
  let next_element = document.body.children[0]
52
47
  while(next_element){
53
48
  const element = next_element;
@@ -57,40 +52,44 @@ export default class Modal extends Component {
57
52
  }
58
53
  }
59
54
 
60
- Helpers.addClass(document.body, 'uniformModal-active');
55
+ document.body.classList.add('uniformModal-active');
61
56
  document.body.appendChild(this.blur);
62
57
  document.body.appendChild(this.el);
63
58
 
64
- var container = document.createElement('div');
65
- Helpers.addClass(container, 'uniformModal-container');
66
- if (content instanceof Node) {
67
- container.appendChild(content);
68
- } else {
69
- container.innerHTML = content;
70
- }
71
-
72
- var closeButton = document.createElement('div');
73
- Helpers.addClass(closeButton, 'uniformModal-close');
74
- this.el.appendChild(closeButton);
75
-
76
59
  this.el.style.top = window.scrollY;
77
60
  this.listenTo(this.overlay, 'click', this.close);
78
- this.el.appendChild(container);
61
+
62
+ const container = createElement('div', {
63
+ class: 'uniformModal-container',
64
+ children: this.content
65
+ });
66
+
67
+ const closeButton = createElement('div', {
68
+ class: 'uniformModal-close-container',
69
+ children: {
70
+ class: 'uniformModal-close'
71
+ }
72
+ });
73
+
74
+ this.el.append(this.overlay);
75
+ this.el.append(container);
76
+ this.el.append(closeButton);
79
77
 
80
- if (this.options.klass) Helpers.addClass(container, this.options.klass);
81
- if (content.innerHTML) Helpers.trigger(content, 'rendered');
78
+ if (this.options.klass) container.classList.add(this.options.klass);
79
+ if (this.content.innerHTML) trigger(this.content, 'rendered');
82
80
  this.trigger('rendered');
83
81
 
84
82
  return this;
85
83
  }
86
84
 
87
85
  checkCloseButton (e) {
88
- if(Helpers.hasClass(e.target, 'uniformModal-close'))
89
- this.close();
86
+ if(e.target.classList.contains('uniformModal-close')){
87
+ this.close();
88
+ }
90
89
  }
91
90
 
92
91
  close () {
93
- Helpers.removeClass(document.querySelectorAll('uniformModal-active'), 'uniformModal-active');
92
+ document.querySelectorAll('uniformModal-active').forEach(el => el.classList.remove('uniformModal-active'));
94
93
  var elements = this.blur.children;
95
94
  var elementCount = elements.length
96
95
  for(var i=0; i < elementCount; i++){
@@ -1,5 +1,5 @@
1
1
  import Component from './component';
2
- import * as Helpers from './dom-helpers';
2
+ import { offset, outerWidth, outerHeight, append } from 'dolla';
3
3
 
4
4
  /*
5
5
  Requirements
@@ -12,6 +12,7 @@ import * as Helpers from './dom-helpers';
12
12
  align: [left|right|center|#px] [top|center|bottom|#px] | default: 'center bottom'
13
13
  zIndex: # | default: unset
14
14
  offset: {left: 0, top: 0}
15
+ container: element to append popover to. default: document
15
16
  */
16
17
  export default class Popover extends Component {
17
18
  initialize (options) {
@@ -26,7 +27,9 @@ export default class Popover extends Component {
26
27
  offset: {left: 0, top: 0}
27
28
  };
28
29
  Object.assign(this.options, this.pick(options, Object.keys(this.options)));
30
+ this.el.removeAttribute('content');
29
31
 
32
+ this.options.anchor.popover = this
30
33
  this.listenTo(document, 'click', this.checkFocus);
31
34
  this.listenTo(document, 'focusin', this.checkFocus);
32
35
  this.listenTo(document, 'keyup', this.checkEscape);
@@ -35,9 +38,10 @@ export default class Popover extends Component {
35
38
  });
36
39
 
37
40
  if(typeof this.options.container == "string"){
38
- this.options.container = Helpers.closest(this.options.anchor, this.options.container)
39
- this.options.container = this.options.container || document.body
41
+ this.options.container = this.options.anchor.closest(this.options.container)
40
42
  }
43
+ this.options.container = this.options.container || document.body
44
+
41
45
  }
42
46
 
43
47
  render () {
@@ -45,10 +49,7 @@ export default class Popover extends Component {
45
49
  this.el.style.position = 'absolute';
46
50
  this.el.style.zIndex = this.options.zIndex;
47
51
 
48
- if(this.options.content instanceof Node)
49
- this.el.appendChild(this.options.content);
50
- else
51
- this.el.innerHTML = this.options.content;
52
+ append(this.el, this.options.content)
52
53
 
53
54
  this.options.container.appendChild(this.el);
54
55
  this.resize();
@@ -101,43 +102,44 @@ export default class Popover extends Component {
101
102
  var [leftAlign, topAlign] = align.split(" ");
102
103
  leftAlign = leftAlign || "bottom";
103
104
 
104
- var offset = Helpers.offset(this.options.anchor);
105
+ var anchorOffset = offset(this.options.anchor);
105
106
  var container = this.options.container;
106
107
  if(getComputedStyle(container)['position'] == "static") container = container.offsetParent;
107
108
  if(!container) container = document.body;
108
109
 
109
- var containerOffset = Helpers.offset(container);
110
- offset = {
111
- top: offset.top - containerOffset.top,
112
- left: offset.left - containerOffset.left
110
+ var containerOffset = offset(container);
111
+ anchorOffset = {
112
+ top: anchorOffset.top - containerOffset.top,
113
+ left: anchorOffset.left - containerOffset.left
113
114
  }
114
115
 
115
116
  var position = {};
116
117
  if(leftAlign == 'left'){
117
- position.right = Helpers.outerWidth(container) - offset.left;
118
+ position.right = outerWidth(container) - anchorOffset.left;
118
119
  } else if(leftAlign == 'center'){
119
- position.left = offset.left + Helpers.outerWidth(this.options.anchor) / 2 - Helpers.outerWidth(this.el) / 2;
120
+ position.left = anchorOffset.left + outerWidth(this.options.anchor) / 2;
121
+ position.transform = "translateX(-50%)";
120
122
  } else if (leftAlign == 'right'){
121
- position.left = offset.left + Helpers.outerWidth(this.options.anchor);
123
+ position.left = anchorOffset.left + outerWidth(this.options.anchor);
122
124
  } else if (leftAlign.includes("px")){
123
- position.left = offset.left + parseInt(leftAlign);
125
+ position.left = anchorOffset.left + parseInt(leftAlign);
124
126
  }
125
127
 
126
128
  if(topAlign == 'top'){
127
- let height = Helpers.outerHeight(container);
129
+ let height = outerHeight(container);
128
130
  if(container == document.body && getComputedStyle(container)['position'] == "static"){
129
131
  height = window.innerHeight;
130
132
  } else if (container == document.body) {
131
133
  height = Math.max(height, document.body.clientHeight);
132
134
  }
133
- position.bottom = height - offset.top;
135
+ position.bottom = height - anchorOffset.top;
134
136
  } else if(topAlign == 'center'){
135
- position.top = offset.top + Helpers.outerHeight(this.options.anchor) / 2;
137
+ position.top = anchorOffset.top + outerHeight(this.options.anchor) / 2;
136
138
  position.transform = "translateY(-50%)";
137
139
  } else if (topAlign == 'bottom'){
138
- position.top = offset.top + Helpers.outerHeight(this.options.anchor);
140
+ position.top = anchorOffset.top + outerHeight(this.options.anchor);
139
141
  } else if (topAlign.includes("px")){
140
- position.top = offset.top + parseInt(topAlign);
142
+ position.top = anchorOffset.top + parseInt(topAlign);
141
143
  }
142
144
 
143
145
  if(this.options.offset.left) position.left += parseInt(this.options.offset.left);
@@ -150,9 +152,9 @@ export default class Popover extends Component {
150
152
  this.el.style.top = null;
151
153
  this.el.style.bottom = null;
152
154
  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);
155
+ this.el.classList.remove('popover-left', 'popover-right', 'popover-center', 'popover-top', 'popover-bottom');
156
+ this.el.classList.add('popover-' + topAlign);
157
+ this.el.classList.add('popover-' + leftAlign);
156
158
  Object.keys(position).forEach(function(key){
157
159
  this.el.style[key] = position[key] + (key != "transform" ? "px" : "");
158
160
  }, this);
@@ -1,43 +1,39 @@
1
1
  import Component from './component';
2
- import * as Helpers from './dom-helpers';
2
+ import { trigger } from 'dolla';
3
3
 
4
4
  export default class Resizer extends Component {
5
5
 
6
6
  initialize () {
7
+ const breakpoints = getComputedStyle(window.document.body).getPropertyValue('--breakpoints')
8
+ this.breakpoints = {}
9
+ breakpoints.split(",").forEach(breakpoint => {
10
+ const [key, value] = breakpoint.split("/")
11
+ this.breakpoints[key.trim()] = value;
12
+ })
13
+
7
14
  this.listenTo(window, 'resize', this.resize);
8
- Helpers.trigger(window, 'resize');
15
+ this.resize();
9
16
  }
10
17
 
11
18
  resize () {
12
- // breakpoints at 720, 1080, 1440
13
- var width = this.el.offsetWidth;
14
-
15
- if(width > 720 && !Helpers.hasClass(this.el, 'md-size')) {
16
- Helpers.addClass(this.el, 'md-size');
17
- Helpers.trigger(window, 'resized-md');
18
- } else if (width < 720 && Helpers.hasClass(this.el, 'md-size')) {
19
- Helpers.removeClass(this.el, 'md-size');
20
- }
21
-
22
- if(width > 1080 && !Helpers.hasClass(this.el, 'lg-size')) {
23
- Helpers.addClass(this.el, 'lg-size');
24
- Helpers.trigger(window, 'resized-lg');
25
- } else if (width < 1080 && Helpers.hasClass(this.el, 'lg-size')) {
26
- Helpers.removeClass(this.el, 'lg-size');
19
+ const width = this.el.offsetWidth;
20
+ Object.keys(this.breakpoints).forEach(size => {
21
+ const query = this.breakpoints[size]
22
+ const css_class = size + '-container'
23
+ let [attribute, value] = query.split(":")
24
+ if(value.match("px")){
25
+ value = parseInt(value)
26
+ } else {
27
+ throw "unsupported media units"
27
28
  }
28
-
29
- if(width > 1440 && !Helpers.hasClass(this.el, 'xl-size')) {
30
- Helpers.addClass(this.el, 'xl-size');
31
- Helpers.trigger(window, 'resized-xl');
32
- } else if (width < 1440 && Helpers.hasClass(this.el, 'xl-size')) {
33
- Helpers.removeClass(this.el, 'xl-size');
34
- }
35
-
36
- if(width < 720 && !Helpers.hasClass(this.el, 'sm-size')) {
37
- Helpers.addClass(this.el, 'sm-size');
38
- Helpers.trigger(window, 'resized-sm');
39
- } else if (width > 720 && Helpers.hasClass(this.el, 'sm-size')) {
40
- Helpers.removeClass(this.el, 'sm-size');
29
+
30
+ if(attribute == "min-width") {
31
+ this.el.classList.toggle(css_class, width > value)
32
+ } else if (attribute == "max-width") {
33
+ this.el.classList.toggle(css_class, width < value)
34
+ } else {
35
+ throw "unsupported media feature"
41
36
  }
37
+ });
42
38
  }
43
39
  }
@@ -1,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, isEmpty, trigger } from 'dolla';
6
6
 
7
7
  /*
8
- options
9
- class: String, appended to uniformSelect-edit button as class
10
- limit: int | false - number of options to limit to, or false to not limit
11
- showAll: function(button_options) to run if/when "Show All" is clicked
12
- label: string, used for mobile menu
13
- container: selector for where to render dropdown
8
+ options: array of html options, each item can be string | array | object
9
+ ex. ["Employee", "Manager", "General Manager"]
10
+ ex. [
11
+ ["Employee", "employee", false],
12
+ ["Manager", "manager", false],
13
+ ["General Manager", "general_manager", true],
14
+ ]
15
+ ex. [
16
+ {value: "employee", text: 'Employee', selected: false},
17
+ {value: "manager", text: 'Manager', selected: false},
18
+ {value: "general_manager", text: 'General Manager', selected: true}
19
+ ]
20
+ limit: int | false - number of options to limit to, or false to not limit
21
+ container: selector for where to render dropdown
22
+ multiple: false
14
23
  */
15
24
  export default class Select extends Component {
16
25
 
17
- initialize (options = {}) {
18
- this.options = {
19
- label: false,
20
- class: "",
21
- showAll: function (button_options){
22
- Helpers.removeClass(button_options.querySelectorAll('button.hide'), 'hide');
23
- var button = button_options.querySelector('.uniformSelect-show-all');
24
- button.parentNode.removeChild(button);
25
-
26
- return false;
27
- },
28
- limit: 8,
29
- container: false
26
+ initialize (options = {}) {
27
+ this.htmlOptions = options.options.map(option => {
28
+ if(typeof option == "string") {
29
+ return {
30
+ value: option,
31
+ text: option
30
32
  }
33
+ } else if (Array.isArray(option)){
34
+ return {
35
+ value: option[1],
36
+ text: option[0],
37
+ selected: option[2]
38
+ }
39
+ } else if (typeof option == "object") {
40
+ return option
41
+ } else {
42
+ throw "option of unexpected type"
43
+ }
44
+ });
45
+ this.options = {
46
+ multiple: false,
47
+ limit: 8,
48
+ container: false
49
+ }
50
+ Object.assign(this.options, this.pick(options, Object.keys(this.options)));
31
51
 
32
- Object.assign(this.options, this.pick(options, Object.keys(this.options)));
52
+ this.el_options = Object.assign({}, this.pick(options, HTML_ATTRIBUTES));
53
+ this.el = createElement('button', this.el_options);
54
+ this.el.classList.add('uniformSelect');
33
55
 
34
- this.listenTo(this.el, 'change', this.renderSelected);
35
- this.listenTo(this.el, 'close', this.hideOptions);
36
- this.el.uniformSelect = this;
56
+ this.listenTo(this.el, 'click', this.toggleOptions);
57
+ this.listenTo(this.el, 'click', '.uniformSelect-remove', this.removeSelection);
58
+ this.listenTo(this.el, 'change', 'select', this.renderValue);
59
+ this.listenTo(this.el, 'close', 'select', this.removeOptions);
60
+ }
61
+
62
+ render () {
63
+ this.valueEl = createElement('span');
64
+ this.valueEl.classList.add('uniformSelect-value')
65
+ this.el.append(this.valueEl);
37
66
 
38
- this.activeIcon = document.createElement('span');
39
- this.activeIcon.innerHTML = checkIcon;
40
- Helpers.addClass(this.activeIcon, 'uniformSelect-option-icon');
41
- }
67
+ this.indicatorEl = createElement('span', {children: arrowIcon})
68
+ this.indicatorEl.classList.add('uniformSelect-indicator')
69
+ this.el.append(this.indicatorEl);
42
70
 
43
- remove () {
44
- Component.prototype.remove.apply(this, arguments);
45
- this.edit_button.parentNode.removeChild(this.edit_button);
46
- delete this.this.edit_button;
71
+ this.select = createElement('select', this.el_options);
72
+ this.htmlOptions.forEach(option => {
73
+ this.select.append(createElement('option', Object.assign({}, {children: option.text}, option)))
74
+ });
75
+ this.el.append(this.select);
47
76
 
48
- this.activeIcon.parentNode.removeChild(this.activeIcon);
49
- delete this.activeIcon;
50
77
 
51
- if(this.button_options_popover) this.button_options_popover.remove();
52
- if(this.button_options_modal) this.button_options_modal.remove();
78
+ // Append placeholder of longest option, to set width
79
+ const longestText = this.htmlOptions.map(x => x.text).sort((a, b) => a.length < b.length)[0]
80
+ const placeholder = createElement('span', {class: 'uniformSelect-placeholder', children: longestText})
81
+ this.el.append(placeholder);
53
82
 
54
- if(this.button_options) {
55
- this.button_options.parentNode.removeChild(this.button_options);
56
- delete this.button_options;
57
- }
58
- }
83
+ this.renderValue();
84
+ return this;
85
+ }
59
86
 
60
- render () {
61
- this.edit_button = Helpers.createElement(`
62
- <button type='button' class='uniformSelect-edit uniformInput outline block ${this.options.class}'>
63
- <span class="text-js"></span>
64
- <span class="uniformSelect-edit-icon">${arrowIcon}</span>
65
- </button>
66
- `);
67
-
68
- if (this.el.name) {
69
- Helpers.addClass(this.edit_button, this.el.name.toLowerCase().replace(/[^a-z0-9\-_]+/g, '-'));
70
- }
71
- this.el.style.display = "none";
72
- this.el.insertAdjacentElement('beforebegin', this.edit_button);
73
-
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
+ e.target.offsetParent.querySelectorAll('.active').forEach(el => el.classList.remove('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
+ e.target.classList.toggle('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() == e.target.closest('.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') || e.target.closest('.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
+ this.el.classList.toggle('active')
131
+ if(this.el.contains('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
+ button.classList.toggle('active', option.selected);
156
+
157
+ console.log(this.options.limit, index);
158
+ if (this.options.limit && (index + 1) > this.options.limit) {
159
+ button.classList.add('hide')
160
+ }
161
+ options.append(button);
162
+ }, this);
163
+
164
+ this.listenTo(options, 'click', '.uniformSelect-option', this.selectOption);
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
+ this.popover.el.querySelectorAll('button.hide').forEach(el => el.classList.remove('hide'));
215
+ var button = this.popover.el.querySelector('.uniformSelect-show-all');
216
+ button.parentNode.removeChild(button);
217
+ }
252
218
  }