@watermarkinsights/ripple 3.2.0-9 → 3.3.0-3

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 (77) hide show
  1. package/dist/cjs/{global-2761e9ed.js → global-7210cd22.js} +1 -1
  2. package/dist/cjs/loader.cjs.js +2 -2
  3. package/dist/cjs/ripple.cjs.js +2 -2
  4. package/dist/cjs/wm-action-menu_2.cjs.entry.js +31 -4
  5. package/dist/cjs/wm-button.cjs.entry.js +21 -22
  6. package/dist/cjs/wm-chart.cjs.entry.js +18 -9
  7. package/dist/cjs/wm-datepicker.cjs.entry.js +18 -29
  8. package/dist/cjs/wm-input.cjs.entry.js +3 -2
  9. package/dist/cjs/wm-network-uploader.cjs.entry.js +1 -1
  10. package/dist/cjs/wm-option_2.cjs.entry.js +26 -4
  11. package/dist/cjs/wm-tag-input.cjs.entry.js +6 -5
  12. package/dist/cjs/wm-timepicker.cjs.entry.js +6 -4
  13. package/dist/cjs/wm-uploader.cjs.entry.js +1 -1
  14. package/dist/collection/components/wm-action-menu/wm-action-menu.js +33 -3
  15. package/dist/collection/components/wm-button/wm-button.css +1 -0
  16. package/dist/collection/components/wm-button/wm-button.js +20 -21
  17. package/dist/collection/components/wm-chart/wm-chart.css +13 -0
  18. package/dist/collection/components/wm-chart/wm-chart.js +35 -8
  19. package/dist/collection/components/wm-datepicker/wm-datepicker.js +18 -31
  20. package/dist/collection/components/wm-input/wm-input.js +3 -3
  21. package/dist/collection/components/wm-menuitem/wm-menuitem.js +35 -2
  22. package/dist/collection/components/wm-option/wm-option.js +28 -0
  23. package/dist/collection/components/wm-select/wm-select.js +47 -22
  24. package/dist/collection/components/wm-tag-input/wm-tag-input.css +2 -0
  25. package/dist/collection/components/wm-tag-input/wm-tag-input.js +5 -4
  26. package/dist/collection/components/wm-timepicker/wm-timepicker.js +7 -5
  27. package/dist/collection/components/wm-uploader/wm-network-uploader/wm-network-uploader.css +1 -0
  28. package/dist/collection/components/wm-uploader/wm-uploader.css +1 -0
  29. package/dist/collection/global/__mocks__/functions.js +1 -0
  30. package/dist/esm/{global-5e35d79d.js → global-4f92a130.js} +1 -1
  31. package/dist/esm/loader.js +2 -2
  32. package/dist/esm/ripple.js +2 -2
  33. package/dist/esm/wm-action-menu_2.entry.js +31 -4
  34. package/dist/esm/wm-button.entry.js +21 -22
  35. package/dist/esm/wm-chart.entry.js +18 -9
  36. package/dist/esm/wm-datepicker.entry.js +18 -29
  37. package/dist/esm/wm-input.entry.js +3 -2
  38. package/dist/esm/wm-network-uploader.entry.js +1 -1
  39. package/dist/esm/wm-option_2.entry.js +26 -4
  40. package/dist/esm/wm-tag-input.entry.js +6 -5
  41. package/dist/esm/wm-timepicker.entry.js +6 -4
  42. package/dist/esm/wm-uploader.entry.js +1 -1
  43. package/dist/ripple/p-3003d26d.entry.js +1 -0
  44. package/dist/ripple/p-301b67a3.entry.js +1 -0
  45. package/dist/ripple/p-55aff9e2.entry.js +1 -0
  46. package/dist/ripple/p-65e3a656.entry.js +1 -0
  47. package/dist/ripple/p-6b96b3d4.entry.js +1 -0
  48. package/dist/ripple/p-bc27b604.entry.js +1 -0
  49. package/dist/ripple/{p-f2308112.js → p-c533859b.js} +1 -1
  50. package/dist/ripple/p-cd58a15c.entry.js +1 -0
  51. package/dist/ripple/p-dc7ba72a.entry.js +1 -0
  52. package/dist/ripple/p-dc9c9fda.entry.js +1 -0
  53. package/dist/ripple/p-ec9697db.entry.js +1 -0
  54. package/dist/ripple/ripple.esm.js +1 -1
  55. package/dist/types/components/wm-action-menu/wm-action-menu.d.ts +3 -1
  56. package/dist/types/components/wm-button/wm-button.d.ts +1 -0
  57. package/dist/types/components/wm-chart/wm-chart.d.ts +2 -1
  58. package/dist/types/components/wm-datepicker/wm-datepicker.d.ts +3 -4
  59. package/dist/types/components/wm-input/wm-input.d.ts +1 -1
  60. package/dist/types/components/wm-menuitem/wm-menuitem.d.ts +4 -0
  61. package/dist/types/components/wm-option/wm-option.d.ts +4 -0
  62. package/dist/types/components/wm-select/wm-select.d.ts +3 -1
  63. package/dist/types/components/wm-tag-input/wm-tag-input.d.ts +1 -1
  64. package/dist/types/components/wm-timepicker/wm-timepicker.d.ts +2 -2
  65. package/dist/types/components.d.ts +4 -0
  66. package/dist/types/global/__mocks__/functions.d.ts +1 -0
  67. package/package.json +1 -1
  68. package/dist/ripple/p-0c95b055.entry.js +0 -1
  69. package/dist/ripple/p-3d3c0ca2.entry.js +0 -1
  70. package/dist/ripple/p-4485711d.entry.js +0 -1
  71. package/dist/ripple/p-4a307987.entry.js +0 -1
  72. package/dist/ripple/p-5cc55de9.entry.js +0 -1
  73. package/dist/ripple/p-6ea39bf4.entry.js +0 -1
  74. package/dist/ripple/p-70cfdb16.entry.js +0 -1
  75. package/dist/ripple/p-9d1b5f8f.entry.js +0 -1
  76. package/dist/ripple/p-b30e624a.entry.js +0 -1
  77. package/dist/ripple/p-fff66607.entry.js +0 -1
@@ -31,6 +31,9 @@ export class Button {
31
31
  // tooltip is only rendered for icononly and navigational types, or if the button text is truncated
32
32
  return this.tempButtonType === "icononly" || this.tempButtonType === "navigational" || this.isTruncated;
33
33
  }
34
+ get tooltipEl() {
35
+ return document.getElementById("wm-tooltip");
36
+ }
34
37
  toggleTabbingOn() {
35
38
  this.isTabbing = true;
36
39
  }
@@ -127,23 +130,20 @@ export class Button {
127
130
  }
128
131
  showTooltip() {
129
132
  if (this.hasTooltip) {
130
- const tooltipEl = document.querySelector("#wm-tooltip");
131
- if (!!tooltipEl) {
132
- // set tooltip text
133
- tooltipEl.textContent = this.isTruncated ? this.el.textContent : this.tooltip;
134
- // adjust position in case there is no space to place the tooltip where specified via prop
135
- this.adjustedTooltipPosition = adjustTooltipPosition(this.tooltipPosition, this.buttonEl, tooltipEl);
136
- this.positionTooltip(this.adjustedTooltipPosition, tooltipEl);
137
- tooltipEl.style.transitionDelay = "500ms";
138
- tooltipEl.style.opacity = "1";
139
- // tooltip is only visible after the transition delay.
140
- // This also fixes an issue for keyboard users:
141
- // if they tab to a button out of screen bounds, the page scrolls
142
- // and hideTooltip() would be triggered before the tooltip would show
143
- setTimeout(() => {
144
- this.tooltipVisible = true;
145
- }, 500);
146
- }
133
+ // set tooltip text
134
+ this.tooltipEl.textContent = this.isTruncated ? this.el.textContent : this.tooltip;
135
+ // adjust position in case there is no space to place the tooltip where specified via prop
136
+ this.adjustedTooltipPosition = adjustTooltipPosition(this.tooltipPosition, this.buttonEl, this.tooltipEl);
137
+ this.positionTooltip(this.adjustedTooltipPosition, this.tooltipEl);
138
+ this.tooltipEl.style.transitionDelay = "500ms";
139
+ this.tooltipEl.style.opacity = "1";
140
+ // tooltip is only visible after the transition delay.
141
+ // This also fixes an issue for keyboard users:
142
+ // if they tab to a button out of screen bounds, the page scrolls
143
+ // and hideTooltip() would be triggered before the tooltip would show
144
+ setTimeout(() => {
145
+ this.tooltipVisible = true;
146
+ }, 500);
147
147
  }
148
148
  }
149
149
  positionTooltip(tPos, tooltipEl) {
@@ -177,10 +177,9 @@ export class Button {
177
177
  tooltipEl.style.left = (horizontalPos / 16).toString() + "rem";
178
178
  }
179
179
  hideTooltip() {
180
- const tooltipEl = document.querySelector("#wm-tooltip");
181
- if (this.hasTooltip && !!tooltipEl) {
182
- tooltipEl.style.transitionDelay = "0s";
183
- tooltipEl.style.opacity = "0";
180
+ if (this.hasTooltip) {
181
+ this.tooltipEl.style.transitionDelay = "0s";
182
+ this.tooltipEl.style.opacity = "0";
184
183
  this.tooltipVisible = false;
185
184
  }
186
185
  }
@@ -238,6 +238,7 @@
238
238
  text-align: left;
239
239
  padding-right: 1.25rem;
240
240
  padding-bottom: 0;
241
+ flex: none;
241
242
  }
242
243
  .component-wrapper.bar1 .completion-message {
243
244
  right: 5.75rem;
@@ -309,6 +310,18 @@
309
310
  transform: translate(4px, 24px);
310
311
  text-anchor: middle;
311
312
  }
313
+ .component-wrapper.left-label {
314
+ flex-direction: row;
315
+ }
316
+ .component-wrapper.left-label .label {
317
+ width: 12rem;
318
+ padding-right: 1.25rem;
319
+ flex: none;
320
+ align-self: flex-end;
321
+ min-height: 30px;
322
+ display: flex;
323
+ align-items: center;
324
+ }
312
325
 
313
326
  /* KEYBOARD USERS */
314
327
  :host(:focus) {
@@ -6,6 +6,9 @@ export class Chart {
6
6
  this.chartType = "doughnut1";
7
7
  this.showLegend = true;
8
8
  this.notStartedColor = false;
9
+ // left labels are a temporary solution for the lack of a stacked bar chart
10
+ // it only works with bar4 and should be used without a legend
11
+ this.labelPosition = "top";
9
12
  this.isTabbing = false;
10
13
  this.uid = generateId();
11
14
  this.slicesData = [];
@@ -86,11 +89,13 @@ export class Chart {
86
89
  },
87
90
  };
88
91
  /* LIFECYCLE METHODS + EVENTS FROM THE CHILDREN */
89
- this.debouncedUpdate = debounce(async () => {
90
- this.setHybridType();
91
- await this.getData();
92
+ this.debouncedResize = debounce(async () => {
93
+ if (this.chartType === "hybrid") {
94
+ this.setHybridType();
95
+ await this.getData();
96
+ }
92
97
  forceUpdate(this.el);
93
- }, 30);
98
+ }, 10);
94
99
  this.debouncedSliceUpdate = debounce(async () => {
95
100
  await this.getData();
96
101
  forceUpdate(this.el);
@@ -338,8 +343,9 @@ export class Chart {
338
343
  }
339
344
  }
340
345
  handleResize() {
341
- if (this.chartType === "hybrid") {
342
- this.debouncedUpdate();
346
+ // handling resizing only needs to occur for bar charts (hybrid included)
347
+ if (this.chartType.includes("bar") || this.chartType === "hybrid") {
348
+ this.debouncedResize();
343
349
  }
344
350
  }
345
351
  async componentWillLoad() {
@@ -444,13 +450,16 @@ export class Chart {
444
450
  default:
445
451
  y = "0";
446
452
  }
453
+ // adjusting the width of the rect slightly ensures repainting on rerenders
454
+ // important for updating width accurate when resizing the page
455
+ const randomTinyNumber = Math.random() / 1000;
447
456
  return (h("g", { class: "barcontainer" },
448
457
  h("style", null, ` #${s.id} {
449
458
  fill:${s.color};
450
459
  x: ${`${this.amountToPercent(s.offset, false)}%`};
451
460
  y: ${y};
452
461
  height: 30px;
453
- width: calc(${this.amountToPercent(s.amount, false)}%${idx !== this.slicesData.length - 1 ? " - 2px" : ""});
462
+ width: calc(${this.amountToPercent(s.amount, false) + randomTinyNumber}%${idx !== this.slicesData.length - 1 ? " - 2px" : ""});
454
463
  }`),
455
464
  h("rect", { id: s.id, onClick: (ev) => {
456
465
  if (this.popoverEl) {
@@ -537,7 +546,7 @@ export class Chart {
537
546
  id: "chart.interactiveChart",
538
547
  defaultMessage: "Interactive chart. Use arrow keys to browse elements, press Tab to exit.",
539
548
  }), tabindex: "0" },
540
- h("div", { class: `component-wrapper ${this.getType()}` },
549
+ h("div", { class: `component-wrapper ${this.getType()} ${this.labelPosition === "left" && this.chartType === "bar4" ? "left-label" : ""}` },
541
550
  h("label", { class: "label", id: `label-${this.uid}`, htmlFor: `graphic-${this.uid}` },
542
551
  h("span", { class: "label-text" }, this.label),
543
552
  this.subinfo ? h("span", { class: "subinfo" }, this.subinfo) : ""),
@@ -694,6 +703,24 @@ export class Chart {
694
703
  "attribute": "not-started-color",
695
704
  "reflect": false,
696
705
  "defaultValue": "false"
706
+ },
707
+ "labelPosition": {
708
+ "type": "string",
709
+ "mutable": false,
710
+ "complexType": {
711
+ "original": "\"left\" | \"top\"",
712
+ "resolved": "\"left\" | \"top\"",
713
+ "references": {}
714
+ },
715
+ "required": false,
716
+ "optional": false,
717
+ "docs": {
718
+ "tags": [],
719
+ "text": ""
720
+ },
721
+ "attribute": "label-position",
722
+ "reflect": false,
723
+ "defaultValue": "\"top\""
697
724
  }
698
725
  }; }
699
726
  static get states() { return {
@@ -10,6 +10,9 @@ export class DatePicker {
10
10
  this.label = "";
11
11
  this.requiredField = false;
12
12
  this.displayError = "";
13
+ // we only want to fire the "change" event if the value has actually changed (that's
14
+ // how native elements do it), so we need to keep in state the last committed value.
15
+ this.lastCommittedValue = this.value;
13
16
  // 1, 2, or 4 digits, separator, 1 or 2 digits, separator, then 1 or 2 or 4 digits
14
17
  this.parsableEntry = /^(\d{1}|\d{2}|\d{4})[\-\.\/]\d{1,2}[\-\.\/](\d{1}|\d{2}|\d{4})$/;
15
18
  this.isoEntry = /^\d\d\d\d[-]\d\d[-]\d\d$/;
@@ -44,22 +47,13 @@ export class DatePicker {
44
47
  this.processInput();
45
48
  }
46
49
  }
47
- handleValue(value) {
48
- // handleInput will change the value prop at every keystroke
49
- // we need to differentiate change by the user and programmatic change
50
- // if the new value is the same as the input value, then the user is changing the input
51
- if (value !== this.inputEl.value) {
52
- this.inputEl.value = this.reformatDate(this.dateFormat, this.value);
53
- this.processInput();
54
- }
55
- }
56
50
  handleInput(ev) {
57
51
  // keep component's value in sync with input's value
58
52
  // validation only happens on blur and initial load,
59
53
  // but component's value should reflect user input at any time
60
54
  this.value = ev.target.value; // same as this.inputEl.value
61
55
  }
62
- // blur on input
56
+ // this is input blur, not component blur
63
57
  handleBlur(ev) {
64
58
  // do not validate if clicking to an element that should prevent validation (e.g. close button on modal)
65
59
  const shouldPreventValidation = this.preventValidation && isRelatedTarget(ev, this.preventValidation);
@@ -67,23 +61,18 @@ export class DatePicker {
67
61
  this.processInput();
68
62
  }
69
63
  this.dpWrapper.classList.remove("focus");
70
- // component emits blur when user tabs out of input (and to the calendar view button)
71
- // since internal errors will show when user tabs from input to button,
72
- // so should external errors, which are run on blur event.
73
- this.generateBlur();
74
64
  }
75
- generateBlur() {
65
+ // this is input focus, not component focus
66
+ focusHandler() {
67
+ this.dpWrapper.classList.add("focus");
68
+ }
69
+ handlePopupClosed() {
76
70
  this.el.tabIndex = 0;
77
71
  this.el.focus();
78
- this.el.blur();
79
- }
80
- generateBlurAndFocusButton() {
81
- this.generateBlur();
82
72
  }
83
73
  getActiveElement() {
84
74
  return checkForFocusableElInShadow(document.activeElement);
85
75
  }
86
- /* End blur stuff. Back to our normal programming :) */
87
76
  handleCellTriggered(ev) {
88
77
  let dateElement = ev.detail;
89
78
  let isoDate = dateElement.getAttribute("data-year") +
@@ -95,13 +84,9 @@ export class DatePicker {
95
84
  this.processInput();
96
85
  // Create event to trigger onInput function on host element, to get the updated value
97
86
  // Because there are more ways to input than just typing, we are firing this event upon cellTriggered
98
- let event = document.createEvent("Event");
99
- event.initEvent("input", true, true);
87
+ const event = new CustomEvent("input");
100
88
  this.el.dispatchEvent(event);
101
89
  }
102
- focusHandler() {
103
- this.dpWrapper.classList.add("focus");
104
- }
105
90
  processInput(isFirstLoad) {
106
91
  // The required field error should not display on first load
107
92
  const leftEmpty = !isFirstLoad && this.requiredField;
@@ -136,8 +121,13 @@ export class DatePicker {
136
121
  else if (!this.inputEl.value.length && !leftEmpty) {
137
122
  this.clearError();
138
123
  }
139
- // value is set to the reformated date or whatever the user passed
140
- this.value = isoDate;
124
+ if (isoDate !== this.lastCommittedValue) {
125
+ // value is set to the reformated date or whatever the user passed
126
+ this.value = isoDate;
127
+ const event = new CustomEvent("change");
128
+ this.el.dispatchEvent(event);
129
+ this.lastCommittedValue = this.value;
130
+ }
141
131
  // event must fire after we set this.value
142
132
  // only fire if new valid value is different from the previous one
143
133
  if (this.isValidDate(isoDate) && isoDate !== this.lastValidValue) {
@@ -443,9 +433,6 @@ export class DatePicker {
443
433
  static get watchers() { return [{
444
434
  "propName": "errorMessage",
445
435
  "methodName": "handleError"
446
- }, {
447
- "propName": "value",
448
- "methodName": "handleValue"
449
436
  }]; }
450
437
  static get listeners() { return [{
451
438
  "name": "keydown",
@@ -461,7 +448,7 @@ export class DatePicker {
461
448
  "passive": false
462
449
  }, {
463
450
  "name": "popupClosed",
464
- "method": "generateBlurAndFocusButton",
451
+ "method": "handlePopupClosed",
465
452
  "target": undefined,
466
453
  "capture": false,
467
454
  "passive": false
@@ -9,7 +9,6 @@ export class Input {
9
9
  this.requiredField = false;
10
10
  this.type = "text";
11
11
  this.step = 1;
12
- this.charCount = 0;
13
12
  this.announcement = "";
14
13
  this.previousBlurredValue = "";
15
14
  this.displayedErrorMessage = "";
@@ -23,6 +22,9 @@ export class Input {
23
22
  defaultMessage: "Please enter a valid number.",
24
23
  });
25
24
  }
25
+ get charCount() {
26
+ return this.value.length;
27
+ }
26
28
  componentWillLoad() {
27
29
  this.uid = this.el.id ? this.el.id : generateId();
28
30
  this.el.focus = function () {
@@ -67,7 +69,6 @@ export class Input {
67
69
  }
68
70
  handleInput(ev) {
69
71
  this.value = ev.target.value;
70
- this.charCount = this.value.length;
71
72
  if (this.characterLimit && this.charCount >= this.characterLimit - 5) {
72
73
  this.announce(this.generateCharacterLimitWarning(this.charCount, this.characterLimit));
73
74
  }
@@ -386,7 +387,6 @@ export class Input {
386
387
  }
387
388
  }; }
388
389
  static get states() { return {
389
- "charCount": {},
390
390
  "announcement": {},
391
391
  "previousBlurredValue": {},
392
392
  "displayedErrorMessage": {}
@@ -16,6 +16,9 @@ export class Menuitem {
16
16
  this.isKeying = false;
17
17
  }
18
18
  handleKeydown(ev) {
19
+ const modifierKeyUsed = ev.altKey || ev.ctrlKey || ev.metaKey;
20
+ const isSingleCharacter = /^.$/.test(ev.key);
21
+ const isCharacterEntry = isSingleCharacter && !modifierKeyUsed;
19
22
  switch (ev.key) {
20
23
  case "ArrowUp":
21
24
  ev.preventDefault();
@@ -48,8 +51,10 @@ export class Menuitem {
48
51
  this.wmTabKeyPressed.emit();
49
52
  break;
50
53
  default:
51
- ev.preventDefault();
52
- this.wmLetterPressed.emit(ev.key);
54
+ if (isCharacterEntry) {
55
+ ev.preventDefault();
56
+ this.wmLetterPressed.emit(ev.key);
57
+ }
53
58
  }
54
59
  }
55
60
  handleClick() {
@@ -57,6 +62,9 @@ export class Menuitem {
57
62
  this.wmMenuitemClicked.emit();
58
63
  }
59
64
  }
65
+ handleBlur(ev) {
66
+ this.wmMenuitemBlurred.emit({ relatedTarget: ev.relatedTarget });
67
+ }
60
68
  setOnClick() {
61
69
  if (this.disabled && this.el.onclick) {
62
70
  this.onClickFunc = this.el.onclick;
@@ -365,6 +373,25 @@ export class Menuitem {
365
373
  "resolved": "any",
366
374
  "references": {}
367
375
  }
376
+ }, {
377
+ "method": "wmMenuitemBlurred",
378
+ "name": "wmMenuitemBlurred",
379
+ "bubbles": true,
380
+ "cancelable": true,
381
+ "composed": true,
382
+ "docs": {
383
+ "tags": [],
384
+ "text": ""
385
+ },
386
+ "complexType": {
387
+ "original": "{ relatedTarget: EventTarget | null }",
388
+ "resolved": "{ relatedTarget: EventTarget | null; }",
389
+ "references": {
390
+ "EventTarget": {
391
+ "location": "global"
392
+ }
393
+ }
394
+ }
368
395
  }]; }
369
396
  static get elementRef() { return "el"; }
370
397
  static get watchers() { return [{
@@ -407,5 +434,11 @@ export class Menuitem {
407
434
  "target": undefined,
408
435
  "capture": false,
409
436
  "passive": false
437
+ }, {
438
+ "name": "blur",
439
+ "method": "handleBlur",
440
+ "target": undefined,
441
+ "capture": false,
442
+ "passive": false
410
443
  }]; }
411
444
  }
@@ -65,6 +65,9 @@ export class Option {
65
65
  // the parent wm-select is in charge of the actual selection
66
66
  }
67
67
  }
68
+ handleBlur(ev) {
69
+ this.wmOptionBlurred.emit({ relatedTarget: ev.relatedTarget });
70
+ }
68
71
  syncAriaSelected() {
69
72
  // this function only keeps the aria-selected attr in sync with the selected prop
70
73
  // all the logic for selecting / deselecting happens in the parent wm-select
@@ -363,6 +366,25 @@ export class Option {
363
366
  "resolved": "string",
364
367
  "references": {}
365
368
  }
369
+ }, {
370
+ "method": "wmOptionBlurred",
371
+ "name": "wmOptionBlurred",
372
+ "bubbles": true,
373
+ "cancelable": true,
374
+ "composed": true,
375
+ "docs": {
376
+ "tags": [],
377
+ "text": ""
378
+ },
379
+ "complexType": {
380
+ "original": "{ relatedTarget: EventTarget | null }",
381
+ "resolved": "{ relatedTarget: EventTarget | null; }",
382
+ "references": {
383
+ "EventTarget": {
384
+ "location": "global"
385
+ }
386
+ }
387
+ }
366
388
  }]; }
367
389
  static get elementRef() { return "el"; }
368
390
  static get watchers() { return [{
@@ -390,5 +412,11 @@ export class Option {
390
412
  "target": undefined,
391
413
  "capture": false,
392
414
  "passive": false
415
+ }, {
416
+ "name": "blur",
417
+ "method": "handleBlur",
418
+ "target": undefined,
419
+ "capture": false,
420
+ "passive": false
393
421
  }]; }
394
422
  }
@@ -1,4 +1,4 @@
1
- import { h, Component, Element, Event, Listen, Prop, State, Watch } from "@stencil/core";
1
+ import { h, Component, Element, Event, Listen, Prop, State, Watch, Host } from "@stencil/core";
2
2
  import { forceUpdate } from "@stencil/core";
3
3
  import { generateId, getTextDir, shouldOpenUp, intl } from "../../global/functions";
4
4
  export class Select {
@@ -80,12 +80,30 @@ export class Select {
80
80
  closePopupOnEscape() {
81
81
  this.close();
82
82
  }
83
- blurHandler(ev) {
83
+ handleOptionBlur(ev) {
84
+ const toElOrChild = ev.detail.relatedTarget === this.el || this.el.contains(ev.detail.relatedTarget);
85
+ // if the Option is blurred to something other than the component emit a blur event with the appropriate relatedTarget
86
+ // keeps our component's blur events accurate, and closes when focusing browser address bar
87
+ if (!toElOrChild) {
88
+ const event = new CustomEvent("blur");
89
+ // @ts-ignore
90
+ event.relatedTarget = ev.detail.relatedTarget;
91
+ this.el.dispatchEvent(event);
92
+ }
93
+ }
94
+ handleClick(ev) {
84
95
  const isElOrChild = ev.target === this.el || this.el.contains(ev.target);
85
96
  if (!isElOrChild && this.isExpanded) {
86
97
  this.close();
87
98
  }
88
99
  }
100
+ handleButtonBlur(ev) {
101
+ const toElOrChild = ev.relatedTarget === this.el || this.el.contains(ev.relatedTarget);
102
+ if (toElOrChild) {
103
+ // do not emit a blur event when opening the dropdown and focusing the Options
104
+ ev.stopPropagation();
105
+ }
106
+ }
89
107
  handleKey(ev) {
90
108
  switch (ev.keyCode) {
91
109
  // Arrow down
@@ -347,25 +365,26 @@ export class Select {
347
365
  onClick: () => (this.isExpanded ? this.close() : this.open()),
348
366
  onBlur: () => this.handleComponentBlur(),
349
367
  };
350
- return (h("div", { class: `wrapper ${getTextDir()} label-${this.labelPosition} ${this.invalid || this.errorMessage ? "invalid" : ""}` },
351
- h("div", { class: "label-wrapper" },
352
- h("label", { class: "label", id: `label-${this.uid}`, htmlFor: `selectbtn-${this.uid}` },
353
- this.label,
354
- // we can't use aria-required or required attributes because it's invalid on the elements we're using (button controlling a listbox)
355
- this.requiredField ? (h("span", { class: "required" },
356
- h("span", { class: "sr-only" }, this.requiredMessage),
357
- h("span", { "aria-hidden": "true" }, "*"))) : (""))),
358
- h("div", { class: "dropdown" },
359
- h("button", Object.assign({}, buttonProps, { class: `displayedoption ${this.isTabbing ? "user-is-tabbing" : ""}`, ref: (el) => (this.buttonEl = el) }),
360
- h("span", { class: this.selectedItems.length > 0 && this.selectedItems.filter((x) => x.subinfo).length > 0
361
- ? "overflowcontrol hassubinfo"
362
- : "overflowcontrol" },
363
- h("span", { class: "button-text" }, this.renderButtonText()),
364
- this.renderSubinfo()),
365
- this.renderOverflowCount()),
366
- h("div", { class: `options ${this.isExpanded ? "open" : ""} ${this.openUp ? "upwards" : ""}`, id: `list-${this.uid}`, tabindex: -1, role: "listbox", "aria-multiselectable": this.multiple ? "true" : null, "aria-labelledby": `label-${this.uid}`, ref: (el) => (this.optionsEl = el) },
367
- h("slot", null)),
368
- this.renderErrorContainer())));
368
+ return (h(Host, { onBlur: () => this.close(false) },
369
+ h("div", { class: `wrapper ${getTextDir()} label-${this.labelPosition} ${this.invalid || this.errorMessage ? "invalid" : ""}` },
370
+ h("div", { class: "label-wrapper" },
371
+ h("label", { class: "label", id: `label-${this.uid}`, htmlFor: `selectbtn-${this.uid}` },
372
+ this.label,
373
+ // we can't use aria-required or required attributes because it's invalid on the elements we're using (button controlling a listbox)
374
+ this.requiredField ? (h("span", { class: "required" },
375
+ h("span", { class: "sr-only" }, this.requiredMessage),
376
+ h("span", { "aria-hidden": "true" }, "*"))) : (""))),
377
+ h("div", { class: "dropdown" },
378
+ h("button", Object.assign({}, buttonProps, { class: `displayedoption ${this.isTabbing ? "user-is-tabbing" : ""}`, ref: (el) => (this.buttonEl = el), onBlur: (ev) => this.handleButtonBlur(ev) }),
379
+ h("span", { class: this.selectedItems.length > 0 && this.selectedItems.filter((x) => x.subinfo).length > 0
380
+ ? "overflowcontrol hassubinfo"
381
+ : "overflowcontrol" },
382
+ h("span", { class: "button-text" }, this.renderButtonText()),
383
+ this.renderSubinfo()),
384
+ this.renderOverflowCount()),
385
+ h("div", { class: `options ${this.isExpanded ? "open" : ""} ${this.openUp ? "upwards" : ""}`, id: `list-${this.uid}`, tabindex: -1, role: "listbox", "aria-multiselectable": this.multiple ? "true" : null, "aria-labelledby": `label-${this.uid}`, ref: (el) => (this.optionsEl = el) },
386
+ h("slot", null)),
387
+ this.renderErrorContainer()))));
369
388
  }
370
389
  static get is() { return "wm-select"; }
371
390
  static get encapsulation() { return "shadow"; }
@@ -670,9 +689,15 @@ export class Select {
670
689
  "target": undefined,
671
690
  "capture": false,
672
691
  "passive": false
692
+ }, {
693
+ "name": "wmOptionBlurred",
694
+ "method": "handleOptionBlur",
695
+ "target": undefined,
696
+ "capture": false,
697
+ "passive": false
673
698
  }, {
674
699
  "name": "click",
675
- "method": "blurHandler",
700
+ "method": "handleClick",
676
701
  "target": "document",
677
702
  "capture": true,
678
703
  "passive": false
@@ -217,6 +217,7 @@ wm-tag-input .tag-area [role=option] {
217
217
  margin-right: 0.25rem;
218
218
  margin-left: 0.25rem;
219
219
  transition: background-color 0.3s cubic-bezier(0.4, 0, 0.2, 1);
220
+ white-space: pre;
220
221
  }
221
222
  :host .tag-area [role=option].highlight,
222
223
  wm-tag-input .tag-area [role=option].highlight {
@@ -352,6 +353,7 @@ wm-tag-input .dropdown-wrapper div {
352
353
  background: #fff;
353
354
  list-style: none;
354
355
  height: 2.6875rem;
356
+ white-space: pre;
355
357
  }
356
358
  :host .dropdown-wrapper li[role=option], :host .dropdown-wrapper li.add-new-btn,
357
359
  :host .dropdown-wrapper div[role=option],
@@ -52,14 +52,14 @@ export class TagInput {
52
52
  description: "An alert for adding a tag that is already present.",
53
53
  }, { tagName: tag });
54
54
  }
55
- generateTagCounterMessage() {
55
+ generateTagCounterMessage(x, y) {
56
56
  return intl.formatMessage({
57
57
  id: "tagInput.tagsAddedCounter",
58
58
  defaultMessage: "Tags added: {x, number}/{y, number}",
59
59
  description: "For the user to understand how close they are to the tag limit.",
60
60
  }, {
61
- x: this.tagsList.length,
62
- y: this.maxTags,
61
+ x: x,
62
+ y: y,
63
63
  });
64
64
  }
65
65
  componentWillLoad() {
@@ -439,6 +439,7 @@ export class TagInput {
439
439
  }
440
440
  positionInput() {
441
441
  const lastTag = this.tagEls[this.tagEls.length - 1];
442
+ // default placement to fall back to when no tags are present, or not enough space is available
442
443
  this.inputEl.style.position = "static";
443
444
  this.inputEl.style.width = "100%";
444
445
  if (lastTag) {
@@ -532,7 +533,7 @@ export class TagInput {
532
533
  if (this.maxTags) {
533
534
  return (h("div", { class: "lower-row" },
534
535
  h("div", { id: "max-tags" },
535
- this.generateTagCounterMessage(),
536
+ this.generateTagCounterMessage(this.tagsList.length, this.maxTags),
536
537
  this.tagsList.length >= this.maxTags && h("span", { class: "sr-only" }, this.maxTagsReachedMessage))));
537
538
  }
538
539
  }
@@ -271,7 +271,7 @@ export class Timepicker {
271
271
  }
272
272
  });
273
273
  }
274
- close() {
274
+ close(returnFocus = true) {
275
275
  this.isExpanded = false;
276
276
  window.setTimeout(() => {
277
277
  this.optionsEl.classList.add("hidden");
@@ -279,7 +279,9 @@ export class Timepicker {
279
279
  // Delay is necessary for screenreader to get new expanded state before focus
280
280
  // window.requestAnimationFrame is probably enough, but since we are already using setTimeout it may as well be here
281
281
  // also UX wise, it makes sense for the button to only be focused after the animation is complete
282
- this.buttonEl.focus();
282
+ if (returnFocus) {
283
+ this.buttonEl.focus();
284
+ }
283
285
  }, 150);
284
286
  }
285
287
  focusOption(item) {
@@ -358,7 +360,7 @@ export class Timepicker {
358
360
  this.setDropdownPosition("center", this.selectedOption);
359
361
  }
360
362
  }
361
- handleBlur(ev) {
363
+ handleInputBlur(ev) {
362
364
  // do not validate if clicking to an element that should prevent validation (e.g. close button on modal)
363
365
  const shouldPreventValidation = this.preventValidation && isRelatedTarget(ev, this.preventValidation);
364
366
  if (!shouldPreventValidation) {
@@ -370,14 +372,14 @@ export class Timepicker {
370
372
  return this.times.map((time, index) => (h("li", { id: `option${index + 1}`, role: "option", "aria-selected": time === "09:00" ? "true" : false, onClick: () => this.handleOptionClick(time) }, this.formatToDisplay(time))));
371
373
  }
372
374
  render() {
373
- return (h(Host, { id: this.uid, invalid: !!this.displayedErrorMessage ? "true" : null },
375
+ return (h(Host, { id: this.uid, invalid: !!this.displayedErrorMessage ? "true" : null, onBlur: () => this.close(false) },
374
376
  h("div", { class: `wrapper label-${this.labelPosition} ${!!this.displayedErrorMessage ? "invalid" : ""}`, ref: (t) => (this.tpWrapper = t) },
375
377
  h("div", { class: "label-wrapper" }, this.labelPosition !== "none" && (h("label", { id: `label-${this.uid}`, class: "label", htmlFor: `time-input-${this.uid}` },
376
378
  this.label,
377
379
  this.requiredField && (h("span", { class: "required", "aria-hidden": "true" }, "*"))))),
378
380
  h("div", null,
379
381
  h("div", { class: "inner-wrapper" },
380
- h("input", { id: `time-input-${this.uid}`, "aria-label": this.label, "aria-describedby": `error-${this.uid}`, ref: (el) => (this.inputEl = el), onBlur: (ev) => this.handleBlur(ev), onInput: () => this.handleInput(), disabled: this.disabled, required: this.requiredField, placeholder: this.timeFormat, autocomplete: "off", onFocus: () => this.tpWrapper.classList.add("focus") }),
382
+ h("input", { id: `time-input-${this.uid}`, "aria-label": this.label, "aria-describedby": `error-${this.uid}`, ref: (el) => (this.inputEl = el), onBlur: (ev) => this.handleInputBlur(ev), onInput: () => this.handleInput(), disabled: this.disabled, required: this.requiredField, placeholder: this.timeFormat, autocomplete: "off", onFocus: () => this.tpWrapper.classList.add("focus") }),
381
383
  h("button", { id: `btn-${this.uid}`, class: this.isTabbing ? "user-is-tabbing" : "", ref: (el) => (this.buttonEl = el), disabled: this.disabled, "aria-controls": `list-${this.uid}`, "aria-expanded": this.isExpanded ? "true" : "false", "aria-label": this.buttonAriaLabel, "aria-describedby": `time-input-${this.uid}`, onClick: () => (this.isExpanded ? this.close() : this.open()) },
382
384
  h("span", { class: "clock" })),
383
385
  h("ul", { class: `options ${this.isExpanded ? "open" : ""} ${this.openUp ? "upwards" : ""}`, id: `list-${this.uid}`, role: "listbox", "aria-labelledby": `label-${this.uid}`, "aria-describedby": this.isExpanded ? "collapsed" : null, ref: (el) => (this.optionsEl = el) }, this.renderOptions())),
@@ -54,6 +54,7 @@ wm-network-uploader .wm-button {
54
54
  width: inherit;
55
55
  border: 2px solid #575195;
56
56
  color: #575195;
57
+ font-family: inherit;
57
58
  font-size: 0.75rem;
58
59
  font-weight: 700;
59
60
  height: 2.75rem;
@@ -54,6 +54,7 @@ wm-uploader .wm-button {
54
54
  width: inherit;
55
55
  border: 2px solid #575195;
56
56
  color: #575195;
57
+ font-family: inherit;
57
58
  font-size: 0.75rem;
58
59
  font-weight: 700;
59
60
  height: 2.75rem;