@nectary/components 5.1.2 → 5.1.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.
package/bundle.js
CHANGED
|
@@ -10494,12 +10494,80 @@ class SegmentedControlOption extends NectaryElement {
|
|
|
10494
10494
|
};
|
|
10495
10495
|
}
|
|
10496
10496
|
defineCustomElement("sinch-segmented-control-option", SegmentedControlOption);
|
|
10497
|
+
function getActualActiveElement() {
|
|
10498
|
+
let activeElement = document.activeElement;
|
|
10499
|
+
while (activeElement?.shadowRoot?.activeElement != null) {
|
|
10500
|
+
activeElement = activeElement.shadowRoot.activeElement;
|
|
10501
|
+
}
|
|
10502
|
+
return activeElement;
|
|
10503
|
+
}
|
|
10504
|
+
function createKeyboardNavigation() {
|
|
10505
|
+
return {
|
|
10506
|
+
navigateToNextOption(enabledOptions, forward) {
|
|
10507
|
+
const optionsLength = enabledOptions.length;
|
|
10508
|
+
if (optionsLength === 0) {
|
|
10509
|
+
return;
|
|
10510
|
+
}
|
|
10511
|
+
const currentIndex = enabledOptions.findIndex((option) => option === getActualActiveElement());
|
|
10512
|
+
let nextIndex;
|
|
10513
|
+
if (currentIndex !== -1) {
|
|
10514
|
+
if (forward) {
|
|
10515
|
+
nextIndex = (currentIndex + 1) % optionsLength;
|
|
10516
|
+
} else {
|
|
10517
|
+
nextIndex = currentIndex === 0 ? optionsLength - 1 : currentIndex - 1;
|
|
10518
|
+
}
|
|
10519
|
+
} else {
|
|
10520
|
+
nextIndex = forward ? 0 : optionsLength - 1;
|
|
10521
|
+
}
|
|
10522
|
+
this.navigateToOption(enabledOptions, nextIndex);
|
|
10523
|
+
},
|
|
10524
|
+
navigateToOption(enabledOptions, index) {
|
|
10525
|
+
const optionsLength = enabledOptions.length;
|
|
10526
|
+
if (enabledOptions.length === 0 || index < 0 || index >= optionsLength) {
|
|
10527
|
+
return;
|
|
10528
|
+
}
|
|
10529
|
+
const option = enabledOptions[index];
|
|
10530
|
+
option.focus();
|
|
10531
|
+
},
|
|
10532
|
+
handleKeyboardNavigation(e, enabledOptions) {
|
|
10533
|
+
switch (e.code) {
|
|
10534
|
+
case "Space":
|
|
10535
|
+
case "Enter": {
|
|
10536
|
+
e.preventDefault();
|
|
10537
|
+
const target = getTargetByAttribute(e, "value");
|
|
10538
|
+
if (target !== null) {
|
|
10539
|
+
target.click();
|
|
10540
|
+
}
|
|
10541
|
+
break;
|
|
10542
|
+
}
|
|
10543
|
+
case "ArrowLeft":
|
|
10544
|
+
case "ArrowRight": {
|
|
10545
|
+
e.preventDefault();
|
|
10546
|
+
this.navigateToNextOption(enabledOptions, e.code === "ArrowRight");
|
|
10547
|
+
break;
|
|
10548
|
+
}
|
|
10549
|
+
case "Home": {
|
|
10550
|
+
e.preventDefault();
|
|
10551
|
+
this.navigateToOption(enabledOptions, 0);
|
|
10552
|
+
break;
|
|
10553
|
+
}
|
|
10554
|
+
case "End": {
|
|
10555
|
+
e.preventDefault();
|
|
10556
|
+
this.navigateToOption(enabledOptions, enabledOptions.length - 1);
|
|
10557
|
+
break;
|
|
10558
|
+
}
|
|
10559
|
+
}
|
|
10560
|
+
}
|
|
10561
|
+
};
|
|
10562
|
+
}
|
|
10497
10563
|
const templateHTML$k = '<style>:host{display:block;outline:0}#wrapper{display:flex;flex-direction:row;width:100%;box-sizing:border-box;position:relative;z-index:0}</style><div id="wrapper"><slot></slot></div>';
|
|
10498
10564
|
const template$k = document.createElement("template");
|
|
10499
10565
|
template$k.innerHTML = templateHTML$k;
|
|
10500
10566
|
class SegmentedControl extends NectaryElement {
|
|
10501
10567
|
#$slot;
|
|
10502
10568
|
#controller = null;
|
|
10569
|
+
#enabledOptions = [];
|
|
10570
|
+
#keyboardNav = createKeyboardNavigation();
|
|
10503
10571
|
constructor() {
|
|
10504
10572
|
super();
|
|
10505
10573
|
const shadowRoot = this.attachShadow();
|
|
@@ -10511,10 +10579,12 @@ class SegmentedControl extends NectaryElement {
|
|
|
10511
10579
|
const { signal } = this.#controller;
|
|
10512
10580
|
const options = { signal };
|
|
10513
10581
|
this.setAttribute("role", "tablist");
|
|
10582
|
+
this.setAttribute("aria-orientation", "horizontal");
|
|
10514
10583
|
this.#$slot.addEventListener("slotchange", this.#onSlotChange, options);
|
|
10515
10584
|
this.#$slot.addEventListener("click", this.#onOptionClick, options);
|
|
10516
10585
|
this.#$slot.addEventListener("keydown", this.#onOptionKeydown, options);
|
|
10517
10586
|
this.addEventListener("-change", this.#onChangeReactHandler);
|
|
10587
|
+
this.#updateEnabledOptions();
|
|
10518
10588
|
}
|
|
10519
10589
|
disconnectedCallback() {
|
|
10520
10590
|
this.#controller.abort();
|
|
@@ -10537,8 +10607,14 @@ class SegmentedControl extends NectaryElement {
|
|
|
10537
10607
|
get value() {
|
|
10538
10608
|
return getAttribute(this, "value", "");
|
|
10539
10609
|
}
|
|
10610
|
+
#updateEnabledOptions = () => {
|
|
10611
|
+
this.#enabledOptions = Array.from(this.#$slot.assignedElements()).filter(
|
|
10612
|
+
(option) => !getBooleanAttribute(option, "disabled")
|
|
10613
|
+
);
|
|
10614
|
+
};
|
|
10540
10615
|
#onSlotChange = () => {
|
|
10541
10616
|
this.#onValueChange(this.value);
|
|
10617
|
+
this.#updateEnabledOptions();
|
|
10542
10618
|
};
|
|
10543
10619
|
#onOptionClick = (e) => {
|
|
10544
10620
|
const target = getTargetByAttribute(e, "value");
|
|
@@ -10551,16 +10627,7 @@ class SegmentedControl extends NectaryElement {
|
|
|
10551
10627
|
);
|
|
10552
10628
|
};
|
|
10553
10629
|
#onOptionKeydown = (e) => {
|
|
10554
|
-
|
|
10555
|
-
case "Space":
|
|
10556
|
-
case "Enter": {
|
|
10557
|
-
e.preventDefault();
|
|
10558
|
-
const target = getTargetByAttribute(e, "value");
|
|
10559
|
-
if (target !== null) {
|
|
10560
|
-
target.click();
|
|
10561
|
-
}
|
|
10562
|
-
}
|
|
10563
|
-
}
|
|
10630
|
+
this.#keyboardNav.handleKeyboardNavigation(e, this.#enabledOptions);
|
|
10564
10631
|
};
|
|
10565
10632
|
#onValueChange(value) {
|
|
10566
10633
|
for (const $option of this.#$slot.assignedElements()) {
|
|
@@ -10656,6 +10723,8 @@ template$i.innerHTML = templateHTML$i;
|
|
|
10656
10723
|
class SegmentedIconControl extends NectaryElement {
|
|
10657
10724
|
#$slot;
|
|
10658
10725
|
#controller = null;
|
|
10726
|
+
#keyboardNav = createKeyboardNavigation();
|
|
10727
|
+
#enabledOptions = [];
|
|
10659
10728
|
constructor() {
|
|
10660
10729
|
super();
|
|
10661
10730
|
const shadowRoot = this.attachShadow();
|
|
@@ -10667,10 +10736,12 @@ class SegmentedIconControl extends NectaryElement {
|
|
|
10667
10736
|
const { signal } = this.#controller;
|
|
10668
10737
|
const options = { signal };
|
|
10669
10738
|
this.setAttribute("role", "tablist");
|
|
10739
|
+
this.setAttribute("aria-orientation", "horizontal");
|
|
10670
10740
|
this.#$slot.addEventListener("slotchange", this.#onSlotChange, options);
|
|
10671
10741
|
this.#$slot.addEventListener("click", this.#onOptionClick, options);
|
|
10672
10742
|
this.#$slot.addEventListener("keydown", this.#onOptionKeydown, options);
|
|
10673
10743
|
this.addEventListener("-change", this.#onChangeReactHandler, options);
|
|
10744
|
+
this.#updateEnabledOptions();
|
|
10674
10745
|
}
|
|
10675
10746
|
disconnectedCallback() {
|
|
10676
10747
|
this.#controller.abort();
|
|
@@ -10699,8 +10770,14 @@ class SegmentedIconControl extends NectaryElement {
|
|
|
10699
10770
|
get multiple() {
|
|
10700
10771
|
return getBooleanAttribute(this, "multiple");
|
|
10701
10772
|
}
|
|
10773
|
+
#updateEnabledOptions = () => {
|
|
10774
|
+
this.#enabledOptions = Array.from(this.#$slot.assignedElements()).filter(
|
|
10775
|
+
(option) => !getBooleanAttribute(option, "disabled")
|
|
10776
|
+
);
|
|
10777
|
+
};
|
|
10702
10778
|
#onSlotChange = () => {
|
|
10703
10779
|
this.#onValueChange(this.value);
|
|
10780
|
+
this.#updateEnabledOptions();
|
|
10704
10781
|
};
|
|
10705
10782
|
#onOptionClick = (e) => {
|
|
10706
10783
|
const target = getTargetByAttribute(e, "value");
|
|
@@ -10714,16 +10791,7 @@ class SegmentedIconControl extends NectaryElement {
|
|
|
10714
10791
|
);
|
|
10715
10792
|
};
|
|
10716
10793
|
#onOptionKeydown = (e) => {
|
|
10717
|
-
|
|
10718
|
-
case "Space":
|
|
10719
|
-
case "Enter": {
|
|
10720
|
-
e.preventDefault();
|
|
10721
|
-
const target = getTargetByAttribute(e, "value");
|
|
10722
|
-
if (target !== null) {
|
|
10723
|
-
target.click();
|
|
10724
|
-
}
|
|
10725
|
-
}
|
|
10726
|
-
}
|
|
10794
|
+
this.#keyboardNav.handleKeyboardNavigation(e, this.#enabledOptions);
|
|
10727
10795
|
};
|
|
10728
10796
|
#onValueChange(csv) {
|
|
10729
10797
|
if (this.multiple) {
|
package/package.json
CHANGED
|
@@ -2,12 +2,15 @@ import { updateAttribute, getAttribute, getBooleanAttribute, updateBooleanAttrib
|
|
|
2
2
|
import { defineCustomElement, NectaryElement } from "../utils/element.js";
|
|
3
3
|
import { getReactEventHandler } from "../utils/get-react-event-handler.js";
|
|
4
4
|
import { getTargetByAttribute } from "../utils/event-target.js";
|
|
5
|
+
import { createKeyboardNavigation } from "../utils/control-keyboard-navigation.js";
|
|
5
6
|
const templateHTML = '<style>:host{display:block;outline:0}#wrapper{display:flex;flex-direction:row;width:100%;box-sizing:border-box;position:relative;z-index:0}</style><div id="wrapper"><slot></slot></div>';
|
|
6
7
|
const template = document.createElement("template");
|
|
7
8
|
template.innerHTML = templateHTML;
|
|
8
9
|
class SegmentedControl extends NectaryElement {
|
|
9
10
|
#$slot;
|
|
10
11
|
#controller = null;
|
|
12
|
+
#enabledOptions = [];
|
|
13
|
+
#keyboardNav = createKeyboardNavigation();
|
|
11
14
|
constructor() {
|
|
12
15
|
super();
|
|
13
16
|
const shadowRoot = this.attachShadow();
|
|
@@ -19,10 +22,12 @@ class SegmentedControl extends NectaryElement {
|
|
|
19
22
|
const { signal } = this.#controller;
|
|
20
23
|
const options = { signal };
|
|
21
24
|
this.setAttribute("role", "tablist");
|
|
25
|
+
this.setAttribute("aria-orientation", "horizontal");
|
|
22
26
|
this.#$slot.addEventListener("slotchange", this.#onSlotChange, options);
|
|
23
27
|
this.#$slot.addEventListener("click", this.#onOptionClick, options);
|
|
24
28
|
this.#$slot.addEventListener("keydown", this.#onOptionKeydown, options);
|
|
25
29
|
this.addEventListener("-change", this.#onChangeReactHandler);
|
|
30
|
+
this.#updateEnabledOptions();
|
|
26
31
|
}
|
|
27
32
|
disconnectedCallback() {
|
|
28
33
|
this.#controller.abort();
|
|
@@ -45,8 +50,14 @@ class SegmentedControl extends NectaryElement {
|
|
|
45
50
|
get value() {
|
|
46
51
|
return getAttribute(this, "value", "");
|
|
47
52
|
}
|
|
53
|
+
#updateEnabledOptions = () => {
|
|
54
|
+
this.#enabledOptions = Array.from(this.#$slot.assignedElements()).filter(
|
|
55
|
+
(option) => !getBooleanAttribute(option, "disabled")
|
|
56
|
+
);
|
|
57
|
+
};
|
|
48
58
|
#onSlotChange = () => {
|
|
49
59
|
this.#onValueChange(this.value);
|
|
60
|
+
this.#updateEnabledOptions();
|
|
50
61
|
};
|
|
51
62
|
#onOptionClick = (e) => {
|
|
52
63
|
const target = getTargetByAttribute(e, "value");
|
|
@@ -59,16 +70,7 @@ class SegmentedControl extends NectaryElement {
|
|
|
59
70
|
);
|
|
60
71
|
};
|
|
61
72
|
#onOptionKeydown = (e) => {
|
|
62
|
-
|
|
63
|
-
case "Space":
|
|
64
|
-
case "Enter": {
|
|
65
|
-
e.preventDefault();
|
|
66
|
-
const target = getTargetByAttribute(e, "value");
|
|
67
|
-
if (target !== null) {
|
|
68
|
-
target.click();
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
73
|
+
this.#keyboardNav.handleKeyboardNavigation(e, this.#enabledOptions);
|
|
72
74
|
};
|
|
73
75
|
#onValueChange(value) {
|
|
74
76
|
for (const $option of this.#$slot.assignedElements()) {
|
|
@@ -3,12 +3,15 @@ import { updateAttribute, getAttribute, updateBooleanAttribute, getBooleanAttrib
|
|
|
3
3
|
import { defineCustomElement, NectaryElement } from "../utils/element.js";
|
|
4
4
|
import { getReactEventHandler } from "../utils/get-react-event-handler.js";
|
|
5
5
|
import { getTargetByAttribute } from "../utils/event-target.js";
|
|
6
|
+
import { createKeyboardNavigation } from "../utils/control-keyboard-navigation.js";
|
|
6
7
|
const templateHTML = '<style>:host{display:block;outline:0}#wrapper{display:flex;flex-direction:row;width:100%;box-sizing:border-box;position:relative;z-index:0}</style><div id="wrapper"><slot></slot></div>';
|
|
7
8
|
const template = document.createElement("template");
|
|
8
9
|
template.innerHTML = templateHTML;
|
|
9
10
|
class SegmentedIconControl extends NectaryElement {
|
|
10
11
|
#$slot;
|
|
11
12
|
#controller = null;
|
|
13
|
+
#keyboardNav = createKeyboardNavigation();
|
|
14
|
+
#enabledOptions = [];
|
|
12
15
|
constructor() {
|
|
13
16
|
super();
|
|
14
17
|
const shadowRoot = this.attachShadow();
|
|
@@ -20,10 +23,12 @@ class SegmentedIconControl extends NectaryElement {
|
|
|
20
23
|
const { signal } = this.#controller;
|
|
21
24
|
const options = { signal };
|
|
22
25
|
this.setAttribute("role", "tablist");
|
|
26
|
+
this.setAttribute("aria-orientation", "horizontal");
|
|
23
27
|
this.#$slot.addEventListener("slotchange", this.#onSlotChange, options);
|
|
24
28
|
this.#$slot.addEventListener("click", this.#onOptionClick, options);
|
|
25
29
|
this.#$slot.addEventListener("keydown", this.#onOptionKeydown, options);
|
|
26
30
|
this.addEventListener("-change", this.#onChangeReactHandler, options);
|
|
31
|
+
this.#updateEnabledOptions();
|
|
27
32
|
}
|
|
28
33
|
disconnectedCallback() {
|
|
29
34
|
this.#controller.abort();
|
|
@@ -52,8 +57,14 @@ class SegmentedIconControl extends NectaryElement {
|
|
|
52
57
|
get multiple() {
|
|
53
58
|
return getBooleanAttribute(this, "multiple");
|
|
54
59
|
}
|
|
60
|
+
#updateEnabledOptions = () => {
|
|
61
|
+
this.#enabledOptions = Array.from(this.#$slot.assignedElements()).filter(
|
|
62
|
+
(option) => !getBooleanAttribute(option, "disabled")
|
|
63
|
+
);
|
|
64
|
+
};
|
|
55
65
|
#onSlotChange = () => {
|
|
56
66
|
this.#onValueChange(this.value);
|
|
67
|
+
this.#updateEnabledOptions();
|
|
57
68
|
};
|
|
58
69
|
#onOptionClick = (e) => {
|
|
59
70
|
const target = getTargetByAttribute(e, "value");
|
|
@@ -67,16 +78,7 @@ class SegmentedIconControl extends NectaryElement {
|
|
|
67
78
|
);
|
|
68
79
|
};
|
|
69
80
|
#onOptionKeydown = (e) => {
|
|
70
|
-
|
|
71
|
-
case "Space":
|
|
72
|
-
case "Enter": {
|
|
73
|
-
e.preventDefault();
|
|
74
|
-
const target = getTargetByAttribute(e, "value");
|
|
75
|
-
if (target !== null) {
|
|
76
|
-
target.click();
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
}
|
|
81
|
+
this.#keyboardNav.handleKeyboardNavigation(e, this.#enabledOptions);
|
|
80
82
|
};
|
|
81
83
|
#onValueChange(csv) {
|
|
82
84
|
if (this.multiple) {
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare function createKeyboardNavigation(): {
|
|
2
|
+
navigateToNextOption(enabledOptions: Element[], forward: boolean): void;
|
|
3
|
+
navigateToOption(enabledOptions: Element[], index: number): void;
|
|
4
|
+
handleKeyboardNavigation(e: KeyboardEvent, enabledOptions: Element[]): void;
|
|
5
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { getTargetByAttribute } from "./event-target.js";
|
|
2
|
+
function getActualActiveElement() {
|
|
3
|
+
let activeElement = document.activeElement;
|
|
4
|
+
while (activeElement?.shadowRoot?.activeElement != null) {
|
|
5
|
+
activeElement = activeElement.shadowRoot.activeElement;
|
|
6
|
+
}
|
|
7
|
+
return activeElement;
|
|
8
|
+
}
|
|
9
|
+
function createKeyboardNavigation() {
|
|
10
|
+
return {
|
|
11
|
+
navigateToNextOption(enabledOptions, forward) {
|
|
12
|
+
const optionsLength = enabledOptions.length;
|
|
13
|
+
if (optionsLength === 0) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
const currentIndex = enabledOptions.findIndex((option) => option === getActualActiveElement());
|
|
17
|
+
let nextIndex;
|
|
18
|
+
if (currentIndex !== -1) {
|
|
19
|
+
if (forward) {
|
|
20
|
+
nextIndex = (currentIndex + 1) % optionsLength;
|
|
21
|
+
} else {
|
|
22
|
+
nextIndex = currentIndex === 0 ? optionsLength - 1 : currentIndex - 1;
|
|
23
|
+
}
|
|
24
|
+
} else {
|
|
25
|
+
nextIndex = forward ? 0 : optionsLength - 1;
|
|
26
|
+
}
|
|
27
|
+
this.navigateToOption(enabledOptions, nextIndex);
|
|
28
|
+
},
|
|
29
|
+
navigateToOption(enabledOptions, index) {
|
|
30
|
+
const optionsLength = enabledOptions.length;
|
|
31
|
+
if (enabledOptions.length === 0 || index < 0 || index >= optionsLength) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const option = enabledOptions[index];
|
|
35
|
+
option.focus();
|
|
36
|
+
},
|
|
37
|
+
handleKeyboardNavigation(e, enabledOptions) {
|
|
38
|
+
switch (e.code) {
|
|
39
|
+
case "Space":
|
|
40
|
+
case "Enter": {
|
|
41
|
+
e.preventDefault();
|
|
42
|
+
const target = getTargetByAttribute(e, "value");
|
|
43
|
+
if (target !== null) {
|
|
44
|
+
target.click();
|
|
45
|
+
}
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
case "ArrowLeft":
|
|
49
|
+
case "ArrowRight": {
|
|
50
|
+
e.preventDefault();
|
|
51
|
+
this.navigateToNextOption(enabledOptions, e.code === "ArrowRight");
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
case "Home": {
|
|
55
|
+
e.preventDefault();
|
|
56
|
+
this.navigateToOption(enabledOptions, 0);
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
case "End": {
|
|
60
|
+
e.preventDefault();
|
|
61
|
+
this.navigateToOption(enabledOptions, enabledOptions.length - 1);
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
export {
|
|
69
|
+
createKeyboardNavigation
|
|
70
|
+
};
|