@nectary/components 5.42.2 → 5.42.4

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
@@ -3485,6 +3485,12 @@ class Pop extends NectaryElement {
3485
3485
  targetStyleValue: null,
3486
3486
  transformedAncestor: null
3487
3487
  };
3488
+ static #dialogA11yAttrs = [
3489
+ "aria-label",
3490
+ "aria-labelledby",
3491
+ "aria-describedby",
3492
+ "aria-description"
3493
+ ];
3488
3494
  constructor() {
3489
3495
  super();
3490
3496
  const shadowRoot = this.attachShadow();
@@ -3515,7 +3521,7 @@ class Pop extends NectaryElement {
3515
3521
  const { signal } = this.#controller;
3516
3522
  this.#keydownContext.listen(signal);
3517
3523
  this.#visibilityContext.listen(signal);
3518
- this.setAttribute("role", "dialog");
3524
+ this.#syncDialogA11yAttrs();
3519
3525
  this.#$dialog.addEventListener("cancel", this.#onCancel, { signal });
3520
3526
  this.#$dialog.addEventListener("mousedown", this.#onBackdropMouseDown, { signal });
3521
3527
  this.addEventListener("-close", this.#onCloseReactHandler, { signal });
@@ -3536,7 +3542,12 @@ class Pop extends NectaryElement {
3536
3542
  static get observedAttributes() {
3537
3543
  return [
3538
3544
  "orientation",
3539
- "open"
3545
+ "open",
3546
+ "modal",
3547
+ "aria-label",
3548
+ "aria-labelledby",
3549
+ "aria-describedby",
3550
+ "aria-description"
3540
3551
  ];
3541
3552
  }
3542
3553
  get allowScroll() {
@@ -3606,7 +3617,42 @@ class Pop extends NectaryElement {
3606
3617
  }
3607
3618
  break;
3608
3619
  }
3620
+ case "modal": {
3621
+ if (this.#$dialog.open) {
3622
+ this.#syncDialogSemantics(this.#shouldUseModalSemantics());
3623
+ }
3624
+ break;
3625
+ }
3626
+ case "aria-label":
3627
+ case "aria-labelledby":
3628
+ case "aria-describedby":
3629
+ case "aria-description": {
3630
+ this.#syncDialogA11yAttrs();
3631
+ break;
3632
+ }
3633
+ }
3634
+ }
3635
+ #syncDialogA11yAttrs() {
3636
+ Pop.#dialogA11yAttrs.forEach((attrName) => {
3637
+ const attrVal = this.getAttribute(attrName);
3638
+ if (attrVal === null) {
3639
+ this.#$dialog.removeAttribute(attrName);
3640
+ } else {
3641
+ this.#$dialog.setAttribute(attrName, attrVal);
3642
+ }
3643
+ });
3644
+ }
3645
+ #syncDialogSemantics(useDialogSemantics) {
3646
+ if (useDialogSemantics) {
3647
+ this.setAttribute("role", "dialog");
3648
+ this.#$dialog.setAttribute("aria-modal", "true");
3649
+ return;
3609
3650
  }
3651
+ this.removeAttribute("role");
3652
+ this.#$dialog.removeAttribute("aria-modal");
3653
+ }
3654
+ #shouldUseModalSemantics(transformedAncestor = this.#openSession.transformedAncestor) {
3655
+ return this.modal && transformedAncestor === null;
3610
3656
  }
3611
3657
  #getTargetRect() {
3612
3658
  let item = getFirstSlotElement(this.#$targetSlot, true);
@@ -3682,7 +3728,7 @@ class Pop extends NectaryElement {
3682
3728
  }
3683
3729
  const transformedAncestor = getTransformedAncestor(this);
3684
3730
  const effectiveAllowScroll = this.allowScroll || transformedAncestor != null;
3685
- const shouldUseModal = this.modal && transformedAncestor == null;
3731
+ const shouldUseModal = this.#shouldUseModalSemantics(transformedAncestor);
3686
3732
  const openAsModal = shouldUseModal || !effectiveAllowScroll;
3687
3733
  this.#openSession = {
3688
3734
  effectiveAllowScroll,
@@ -3690,6 +3736,8 @@ class Pop extends NectaryElement {
3690
3736
  targetStyleValue: null,
3691
3737
  transformedAncestor
3692
3738
  };
3739
+ this.#syncDialogA11yAttrs();
3740
+ this.#syncDialogSemantics(shouldUseModal);
3693
3741
  this.#$targetSlot.addEventListener("blur", this.#stopEventPropagation, true);
3694
3742
  this.#$focus.setAttribute("tabindex", "-1");
3695
3743
  this.#$focus.style.display = "block";
@@ -3800,6 +3848,7 @@ class Pop extends NectaryElement {
3800
3848
  targetStyleValue: null,
3801
3849
  transformedAncestor: null
3802
3850
  };
3851
+ this.#syncDialogSemantics(false);
3803
3852
  }
3804
3853
  #onResize = () => {
3805
3854
  this.#resizeThrottle.fn();
@@ -4166,6 +4215,7 @@ class Tooltip extends NectaryElement {
4166
4215
  updateAttribute(this.#$pop, "orientation", getPopOrientation$1(this.orientation));
4167
4216
  updateBooleanAttribute(this.#$pop, "hide-outside-viewport", !this.showOutsideViewport);
4168
4217
  updateBooleanAttribute(this.#$pop, "disable-focus-restore", true);
4218
+ this.#updatePopAriaLabel();
4169
4219
  this.#updateText();
4170
4220
  }
4171
4221
  disconnectedCallback() {
@@ -4211,6 +4261,7 @@ class Tooltip extends NectaryElement {
4211
4261
  switch (name) {
4212
4262
  case "text": {
4213
4263
  this.#updateText();
4264
+ this.#updatePopAriaLabel();
4214
4265
  break;
4215
4266
  }
4216
4267
  case "orientation": {
@@ -4233,7 +4284,11 @@ class Tooltip extends NectaryElement {
4233
4284
  }
4234
4285
  case "aria-label":
4235
4286
  case "aria-description": {
4236
- updateAttribute(this.#$pop, name, newVal);
4287
+ if (name === "aria-label") {
4288
+ this.#updatePopAriaLabel();
4289
+ } else {
4290
+ updateAttribute(this.#$pop, name, newVal);
4291
+ }
4237
4292
  break;
4238
4293
  }
4239
4294
  case "show-outside-viewport": {
@@ -4809,6 +4864,13 @@ class Tooltip extends NectaryElement {
4809
4864
  this.#subscribeMouseEnterEvent();
4810
4865
  }
4811
4866
  }
4867
+ #updatePopAriaLabel() {
4868
+ const rawAriaLabel = getAttribute(this, "aria-label");
4869
+ const explicitAriaLabel = rawAriaLabel !== null && rawAriaLabel.trim().length > 0 ? rawAriaLabel.trim() : null;
4870
+ const fallbackAriaLabel = this.text ?? "";
4871
+ const ariaLabel = explicitAriaLabel ?? (fallbackAriaLabel.length === 0 ? null : fallbackAriaLabel);
4872
+ updateAttribute(this.#$pop, "aria-label", ariaLabel);
4873
+ }
4812
4874
  #subscribeMouseEnterEvent() {
4813
4875
  if (!this.isDomConnected || this.#isSubscribed) {
4814
4876
  return;
@@ -9582,7 +9644,7 @@ class HelpTooltip extends NectaryElement {
9582
9644
  this.#controller = null;
9583
9645
  }
9584
9646
  static get observedAttributes() {
9585
- return ["aria-label", "text", "width", "orientation"];
9647
+ return ["aria-label", "text", "orientation"];
9586
9648
  }
9587
9649
  attributeChangedCallback(name, _, newVal) {
9588
9650
  updateAttribute(this.#$tooltip, name, newVal);
@@ -9593,12 +9655,6 @@ class HelpTooltip extends NectaryElement {
9593
9655
  set text(value) {
9594
9656
  updateAttribute(this, "text", value);
9595
9657
  }
9596
- get width() {
9597
- return getIntegerAttribute(this, "width");
9598
- }
9599
- set width(value) {
9600
- updateIntegerAttribute(this, "width", value);
9601
- }
9602
9658
  get orientation() {
9603
9659
  return getAttribute(this, "orientation", "top");
9604
9660
  }
@@ -17101,7 +17157,7 @@ const getShortestCssDeg = (currentDeg, nextDeg) => {
17101
17157
  }
17102
17158
  return currentDeg + diff;
17103
17159
  };
17104
- const templateHTML$3 = '<style>:host{display:block;outline:0}#wrapper{display:flex;flex-direction:column;width:248px;padding:16px;box-sizing:border-box;gap:16px}#header{position:relative;width:100%;height:48px;font:var(--sinch-comp-time-picker-header-font);line-height:48px;user-select:none;color:var(--sinch-comp-time-picker-header-color-default-text-initial)}#footer{display:flex;justify-content:center;width:100%;height:32px}#picker{position:relative;width:216px;height:216px;border-radius:50%;box-sizing:border-box;border:1px solid var(--sinch-comp-time-picker-watch-face-color-default-border-initial);background-color:var(--sinch-comp-time-picker-watch-face-color-default-background-initial)}#picker-hours,#picker-minutes{position:absolute;left:0;top:0;width:100%;height:100%;border-radius:50%;pointer-events:none;user-select:none}.digit-hour-12,.digit-hour-24,.digit-minute{position:absolute;width:28px;height:28px;text-align:center;top:calc(50% - 14px);left:calc(50% - 14px);z-index:1;cursor:pointer}.digit-hour-12{font:var(--sinch-comp-time-picker-digit-font-default-h12);color:var(--sinch-comp-time-picker-digit-color-default-h12-initial);line-height:28px}.digit-hour-12.selected{font:var(--sinch-comp-time-picker-digit-font-checked-h12);color:var(--sinch-comp-time-picker-digit-color-checked-h12-default)}.digit-hour-24{font:var(--sinch-comp-time-picker-digit-font-default-h24);color:var(--sinch-comp-time-picker-digit-color-default-h24-initial);line-height:28px}.digit-hour-24.selected{font:var(--sinch-comp-time-picker-digit-font-checked-h24);color:var(--sinch-comp-time-picker-digit-color-checked-h24-initial)}.digit-minute{font:var(--sinch-comp-time-picker-digit-font-default-minutes);color:var(--sinch-comp-time-picker-digit-color-default-minute-initial);line-height:28px}.digit-minute.selected{font:var(--sinch-comp-time-picker-digit-font-checked-minutes);color:var(--sinch-comp-time-picker-digit-color-checked-minute-initial)}#picker-touch{position:absolute;left:0;top:0;width:100%;height:100%;cursor:pointer;border-radius:50%}#needle-hour,#needle-minute,#picker-touch::after{background-color:var(--sinch-comp-time-picker-needle-color-default-background-initial)}#needle-hour,#needle-minute{position:absolute;transform-origin:bottom center;transform:rotate(0);bottom:50%;height:50px;transition-duration:.25s;transition-timing-function:ease-in-out;transition-property:transform height;z-index:2;outline:0}@media (prefers-reduced-motion){#needle-hour,#needle-minute{transition:none}}#needle-hour{width:4px;left:calc(50% - 2px);border-radius:2px}#needle-minute{width:2px;left:calc(50% - 1px);border-radius:1px}#needle-hour:focus-visible,#needle-minute:focus-visible{background-color:var(--sinch-comp-time-picker-needle-color-default-background-focus)}#needle-minute:not(.selected)::after{content:"";position:absolute;transform:translateX(-50%);left:0;top:-16px;width:4px;height:4px;border-radius:50%;background-color:var(--sinch-comp-time-picker-digit-color-checked-minute-initial)}#picker-touch::after{content:"";position:absolute;top:50%;left:50%;width:12px;height:12px;border-radius:50%;transform:translate(-50%,-50%)}#header-hours,#header-minutes{position:absolute;padding:0 4px;width:50px;outline:0}#header-hours{right:calc(50% + 8px);text-align:right}#header-minutes{left:calc(50% + 8px)}#header-colon{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%)}#submit{position:absolute;right:0;top:50%;transform:translateY(-50%)}#submit-icon{--sinch-global-color-icon:var(--sinch-comp-time-picker-header-color-default-icon-initial)}:host([ampm]) .digit-hour-24{display:none}:host(:not([ampm])) #footer{display:none}</style><div id="wrapper"><div id="header"><div id="header-hours" role="meter" aria-valuemin="0" aria-valuemax="23" aria-valuenow="0" aria-valuetext="0"><span>00</span></div><div id="header-colon">&colon;</div><div id="header-minutes" role="meter" aria-valuemin="0" aria-valuemax="59" aria-valuenow="0" aria-valuetext="0"><span>00</span></div><sinch-button id="submit" size="s" aria-label="Submit"><sinch-icon icons-version="2" name="fa-check" id="icon-submit" slot="icon"></sinch-icon></sinch-button></div><div id="picker" role="group" aria-label="Time picker clock face"><div id="picker-hours"></div><div id="picker-minutes"></div><div id="picker-touch"><div id="needle-hour" tabindex="0" role="slider" aria-label="Hour selector" aria-valuemin="0" aria-valuemax="23" aria-valuenow="0"></div><div id="needle-minute" tabindex="0" role="slider" aria-label="Minute selector" aria-valuemin="0" aria-valuemax="59" aria-valuenow="0"></div></div></div><div id="footer"><sinch-segmented-control id="ampm"><sinch-segmented-control-option value="am" text="AM" aria-label="AM"></sinch-segmented-control-option><sinch-segmented-control-option value="pm" text="PM" aria-label="PM"></sinch-segmented-control-option></sinch-segmented-control></div></div>';
17160
+ const templateHTML$3 = '<style>:host{display:block;outline:0}#wrapper{display:flex;flex-direction:column;width:248px;padding:16px;box-sizing:border-box;gap:16px}#header{position:relative;width:100%;height:48px;font:var(--sinch-comp-time-picker-header-font);line-height:48px;user-select:none;color:var(--sinch-comp-time-picker-header-color-default-text-initial)}#footer{display:flex;justify-content:center;width:100%;height:32px}#picker{position:relative;width:216px;height:216px;border-radius:50%;box-sizing:border-box;border:1px solid var(--sinch-comp-time-picker-watch-face-color-default-border-initial);background-color:var(--sinch-comp-time-picker-watch-face-color-default-background-initial)}#picker-hours,#picker-minutes{position:absolute;left:0;top:0;width:100%;height:100%;border-radius:50%;pointer-events:none;user-select:none}.digit-hour-12,.digit-hour-24,.digit-minute{position:absolute;width:28px;height:28px;text-align:center;top:calc(50% - 14px);left:calc(50% - 14px);z-index:1;cursor:pointer}.digit-hour-12{font:var(--sinch-comp-time-picker-digit-font-default-h12);color:var(--sinch-comp-time-picker-digit-color-default-h12-initial);line-height:28px}.digit-hour-12.selected{font:var(--sinch-comp-time-picker-digit-font-checked-h12);color:var(--sinch-comp-time-picker-digit-color-checked-h12-default)}.digit-hour-24{font:var(--sinch-comp-time-picker-digit-font-default-h24);color:var(--sinch-comp-time-picker-digit-color-default-h24-initial);line-height:28px}.digit-hour-24.selected{font:var(--sinch-comp-time-picker-digit-font-checked-h24);color:var(--sinch-comp-time-picker-digit-color-checked-h24-initial)}.digit-minute{font:var(--sinch-comp-time-picker-digit-font-default-minutes);color:var(--sinch-comp-time-picker-digit-color-default-minute-initial);line-height:28px}.digit-minute.selected{font:var(--sinch-comp-time-picker-digit-font-checked-minutes);color:var(--sinch-comp-time-picker-digit-color-checked-minute-initial)}#picker-touch{position:absolute;left:0;top:0;width:100%;height:100%;cursor:pointer;border-radius:50%}#needle-hour,#needle-minute,#picker-touch::after{background-color:var(--sinch-comp-time-picker-needle-color-default-background-initial)}#needle-hour,#needle-minute{position:absolute;transform-origin:bottom center;transform:rotate(0);bottom:50%;height:50px;transition-duration:.25s;transition-timing-function:ease-in-out;transition-property:transform height;z-index:2;outline:0}@media (prefers-reduced-motion){#needle-hour,#needle-minute{transition:none}}#needle-hour{width:4px;left:calc(50% - 2px);border-radius:2px}#needle-minute{width:2px;left:calc(50% - 1px);border-radius:1px}#needle-hour:focus-visible,#needle-minute:focus-visible{background-color:var(--sinch-comp-time-picker-needle-color-default-background-focus)}#needle-minute:not(.selected)::after{content:"";position:absolute;transform:translateX(-50%);left:0;top:-16px;width:4px;height:4px;border-radius:50%;background-color:var(--sinch-comp-time-picker-digit-color-checked-minute-initial)}#picker-touch::after{content:"";position:absolute;top:50%;left:50%;width:12px;height:12px;border-radius:50%;transform:translate(-50%,-50%)}#header-hours,#header-minutes{position:absolute;padding:0 4px;width:50px;outline:0}#header-hours{right:calc(50% + 8px);text-align:right}#header-minutes{left:calc(50% + 8px)}#header-colon{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%)}#submit{position:absolute;right:0;top:50%;transform:translateY(-50%)}#submit-icon{--sinch-global-color-icon:var(--sinch-comp-time-picker-header-color-default-icon-initial)}:host([ampm]) .digit-hour-24{display:none}:host(:not([ampm])) #footer{display:none}</style><div id="wrapper"><div id="header"><div id="header-hours" role="meter" aria-valuemin="0" aria-valuemax="23" aria-valuenow="0" aria-valuetext="0"><span>00</span></div><div id="header-colon">&colon;</div><div id="header-minutes" role="meter" aria-valuemin="0" aria-valuemax="59" aria-valuenow="0" aria-valuetext="0"><span>00</span></div><sinch-button id="submit" size="s" aria-label="Submit"><sinch-icon icons-version="2" name="fa-check" id="icon-submit" slot="icon"></sinch-icon></sinch-button></div><div id="picker" role="group" aria-label="Time picker clock face"><div id="picker-hours"></div><div id="picker-minutes"></div><div id="picker-touch"><div id="needle-hour" tabindex="0" role="slider" aria-valuemin="0" aria-valuemax="23" aria-valuenow="0"></div><div id="needle-minute" tabindex="0" role="slider" aria-valuemin="0" aria-valuemax="59" aria-valuenow="0"></div></div></div><div id="footer"><sinch-segmented-control id="ampm"><sinch-segmented-control-option value="am" text="AM" aria-label="AM"></sinch-segmented-control-option><sinch-segmented-control-option value="pm" text="PM" aria-label="PM"></sinch-segmented-control-option></sinch-segmented-control></div></div>';
17105
17161
  const template$3 = document.createElement("template");
17106
17162
  template$3.innerHTML = templateHTML$3;
17107
17163
  const PICKER_RADIUS = 216 / 2;
@@ -17221,7 +17277,16 @@ class TimePicker extends NectaryElement {
17221
17277
  this.#controller = null;
17222
17278
  }
17223
17279
  static get observedAttributes() {
17224
- return ["value", "ampm", "submit-aria-label", "no-submit"];
17280
+ return [
17281
+ "value",
17282
+ "ampm",
17283
+ "submit-aria-label",
17284
+ "hour-meter-aria-label",
17285
+ "minute-meter-aria-label",
17286
+ "hour-needle-aria-label",
17287
+ "minute-needle-aria-label",
17288
+ "no-submit"
17289
+ ];
17225
17290
  }
17226
17291
  attributeChangedCallback(name, prevValue, newVal) {
17227
17292
  if (isAttrEqual(prevValue, newVal)) {
@@ -17247,6 +17312,22 @@ class TimePicker extends NectaryElement {
17247
17312
  updateAttribute(this.#$submitButton, "aria-label", newVal);
17248
17313
  break;
17249
17314
  }
17315
+ case "hour-meter-aria-label": {
17316
+ updateAttribute(this.#$headerHours, "aria-label", newVal);
17317
+ break;
17318
+ }
17319
+ case "minute-meter-aria-label": {
17320
+ updateAttribute(this.#$headerMinutes, "aria-label", newVal);
17321
+ break;
17322
+ }
17323
+ case "hour-needle-aria-label": {
17324
+ updateAttribute(this.#$needleHour, "aria-label", newVal);
17325
+ break;
17326
+ }
17327
+ case "minute-needle-aria-label": {
17328
+ updateAttribute(this.#$needleMinute, "aria-label", newVal);
17329
+ break;
17330
+ }
17250
17331
  case "no-submit": {
17251
17332
  const isNoSubmit = isAttrTrue(newVal);
17252
17333
  updateBooleanAttribute(this, "no-submit", isNoSubmit);
@@ -17273,6 +17354,30 @@ class TimePicker extends NectaryElement {
17273
17354
  get submitAriaLabel() {
17274
17355
  return getAttribute(this, "submit-aria-label", "");
17275
17356
  }
17357
+ set hourMeterAriaLabel(value) {
17358
+ updateAttribute(this, "hour-meter-aria-label", value);
17359
+ }
17360
+ get hourMeterAriaLabel() {
17361
+ return getAttribute(this, "hour-meter-aria-label", "");
17362
+ }
17363
+ set minuteMeterAriaLabel(value) {
17364
+ updateAttribute(this, "minute-meter-aria-label", value);
17365
+ }
17366
+ get minuteMeterAriaLabel() {
17367
+ return getAttribute(this, "minute-meter-aria-label", "");
17368
+ }
17369
+ set hourNeedleAriaLabel(value) {
17370
+ updateAttribute(this, "hour-needle-aria-label", value);
17371
+ }
17372
+ get hourNeedleAriaLabel() {
17373
+ return getAttribute(this, "hour-needle-aria-label", "");
17374
+ }
17375
+ set minuteNeedleAriaLabel(value) {
17376
+ updateAttribute(this, "minute-needle-aria-label", value);
17377
+ }
17378
+ get minuteNeedleAriaLabel() {
17379
+ return getAttribute(this, "minute-needle-aria-label", "");
17380
+ }
17276
17381
  set noSubmit(value) {
17277
17382
  updateBooleanAttribute(this, "no-submit", value);
17278
17383
  }
@@ -11,8 +11,6 @@ export declare class HelpTooltip extends NectaryElement {
11
11
  attributeChangedCallback(name: string, _: string | null, newVal: string | null): void;
12
12
  get text(): string;
13
13
  set text(value: string);
14
- get width(): number | undefined;
15
- set width(value: number | undefined);
16
14
  get orientation(): string;
17
15
  set orientation(value: string);
18
16
  get footprintRect(): import("../types").TRect;
@@ -1,6 +1,6 @@
1
1
  import "../tooltip/index.js";
2
2
  import "../icon/index.js";
3
- import { updateAttribute, getAttribute, getIntegerAttribute, updateIntegerAttribute } from "../utils/dom.js";
3
+ import { updateAttribute, getAttribute } from "../utils/dom.js";
4
4
  import { defineCustomElement, NectaryElement } from "../utils/element.js";
5
5
  import { getReactEventHandler } from "../utils/get-react-event-handler.js";
6
6
  const templateHTML = '<style>:host{display:contents}#icon{--sinch-global-size-icon:18px}</style><sinch-tooltip type="fast"><sinch-icon icons-version="2" name="circle-question" id="icon"></sinch-icon></sinch-tooltip>';
@@ -30,7 +30,7 @@ class HelpTooltip extends NectaryElement {
30
30
  this.#controller = null;
31
31
  }
32
32
  static get observedAttributes() {
33
- return ["aria-label", "text", "width", "orientation"];
33
+ return ["aria-label", "text", "orientation"];
34
34
  }
35
35
  attributeChangedCallback(name, _, newVal) {
36
36
  updateAttribute(this.#$tooltip, name, newVal);
@@ -41,12 +41,6 @@ class HelpTooltip extends NectaryElement {
41
41
  set text(value) {
42
42
  updateAttribute(this, "text", value);
43
43
  }
44
- get width() {
45
- return getIntegerAttribute(this, "width");
46
- }
47
- set width(value) {
48
- updateIntegerAttribute(this, "width", value);
49
- }
50
44
  get orientation() {
51
45
  return getAttribute(this, "orientation", "top");
52
46
  }
@@ -1,6 +1,15 @@
1
- import type { TSinchTooltipProps } from '../tooltip/types';
2
- import type { NectaryComponentReactByType, NectaryComponentVanillaByType, NectaryComponentReact, NectaryComponentVanilla } from '../types';
3
- export type TSinchHelpTooltipProps = TSinchTooltipProps;
1
+ import type { TSinchTooltipOrientation } from '../tooltip/types';
2
+ import type { NectaryComponentReactByType, NectaryComponentVanillaByType, NectaryComponentReact, NectaryComponentVanilla, TRect } from '../types';
3
+ export type TSinchHelpTooltipProps = {
4
+ /** Accessible name for the tooltip content */
5
+ 'aria-label'?: string;
6
+ /** Text */
7
+ text: string;
8
+ /** Orientation, where it *points to* from origin */
9
+ orientation?: TSinchTooltipOrientation;
10
+ readonly footprintRect?: TRect;
11
+ readonly tooltipRect?: TRect;
12
+ };
4
13
  export type TSinchHelpTooltipStyle = {
5
14
  '--sinch-global-size-icon'?: string;
6
15
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nectary/components",
3
- "version": "5.42.2",
3
+ "version": "5.42.4",
4
4
  "files": [
5
5
  "**/*/*.css",
6
6
  "**/*/*.json",
package/pop/index.js CHANGED
@@ -35,6 +35,12 @@ class Pop extends NectaryElement {
35
35
  targetStyleValue: null,
36
36
  transformedAncestor: null
37
37
  };
38
+ static #dialogA11yAttrs = [
39
+ "aria-label",
40
+ "aria-labelledby",
41
+ "aria-describedby",
42
+ "aria-description"
43
+ ];
38
44
  constructor() {
39
45
  super();
40
46
  const shadowRoot = this.attachShadow();
@@ -65,7 +71,7 @@ class Pop extends NectaryElement {
65
71
  const { signal } = this.#controller;
66
72
  this.#keydownContext.listen(signal);
67
73
  this.#visibilityContext.listen(signal);
68
- this.setAttribute("role", "dialog");
74
+ this.#syncDialogA11yAttrs();
69
75
  this.#$dialog.addEventListener("cancel", this.#onCancel, { signal });
70
76
  this.#$dialog.addEventListener("mousedown", this.#onBackdropMouseDown, { signal });
71
77
  this.addEventListener("-close", this.#onCloseReactHandler, { signal });
@@ -86,7 +92,12 @@ class Pop extends NectaryElement {
86
92
  static get observedAttributes() {
87
93
  return [
88
94
  "orientation",
89
- "open"
95
+ "open",
96
+ "modal",
97
+ "aria-label",
98
+ "aria-labelledby",
99
+ "aria-describedby",
100
+ "aria-description"
90
101
  ];
91
102
  }
92
103
  get allowScroll() {
@@ -156,8 +167,43 @@ class Pop extends NectaryElement {
156
167
  }
157
168
  break;
158
169
  }
170
+ case "modal": {
171
+ if (this.#$dialog.open) {
172
+ this.#syncDialogSemantics(this.#shouldUseModalSemantics());
173
+ }
174
+ break;
175
+ }
176
+ case "aria-label":
177
+ case "aria-labelledby":
178
+ case "aria-describedby":
179
+ case "aria-description": {
180
+ this.#syncDialogA11yAttrs();
181
+ break;
182
+ }
159
183
  }
160
184
  }
185
+ #syncDialogA11yAttrs() {
186
+ Pop.#dialogA11yAttrs.forEach((attrName) => {
187
+ const attrVal = this.getAttribute(attrName);
188
+ if (attrVal === null) {
189
+ this.#$dialog.removeAttribute(attrName);
190
+ } else {
191
+ this.#$dialog.setAttribute(attrName, attrVal);
192
+ }
193
+ });
194
+ }
195
+ #syncDialogSemantics(useDialogSemantics) {
196
+ if (useDialogSemantics) {
197
+ this.setAttribute("role", "dialog");
198
+ this.#$dialog.setAttribute("aria-modal", "true");
199
+ return;
200
+ }
201
+ this.removeAttribute("role");
202
+ this.#$dialog.removeAttribute("aria-modal");
203
+ }
204
+ #shouldUseModalSemantics(transformedAncestor = this.#openSession.transformedAncestor) {
205
+ return this.modal && transformedAncestor === null;
206
+ }
161
207
  #getTargetRect() {
162
208
  let item = getFirstSlotElement(this.#$targetSlot, true);
163
209
  if (item === null && this.#$dialog.open) {
@@ -232,7 +278,7 @@ class Pop extends NectaryElement {
232
278
  }
233
279
  const transformedAncestor = getTransformedAncestor(this);
234
280
  const effectiveAllowScroll = this.allowScroll || transformedAncestor != null;
235
- const shouldUseModal = this.modal && transformedAncestor == null;
281
+ const shouldUseModal = this.#shouldUseModalSemantics(transformedAncestor);
236
282
  const openAsModal = shouldUseModal || !effectiveAllowScroll;
237
283
  this.#openSession = {
238
284
  effectiveAllowScroll,
@@ -240,6 +286,8 @@ class Pop extends NectaryElement {
240
286
  targetStyleValue: null,
241
287
  transformedAncestor
242
288
  };
289
+ this.#syncDialogA11yAttrs();
290
+ this.#syncDialogSemantics(shouldUseModal);
243
291
  this.#$targetSlot.addEventListener("blur", this.#stopEventPropagation, true);
244
292
  this.#$focus.setAttribute("tabindex", "-1");
245
293
  this.#$focus.style.display = "block";
@@ -350,6 +398,7 @@ class Pop extends NectaryElement {
350
398
  targetStyleValue: null,
351
399
  transformedAncestor: null
352
400
  };
401
+ this.#syncDialogSemantics(false);
353
402
  }
354
403
  #onResize = () => {
355
404
  this.#resizeThrottle.fn();
@@ -17,6 +17,14 @@ export declare class TimePicker extends NectaryElement {
17
17
  get ampm(): boolean;
18
18
  set submitAriaLabel(value: string);
19
19
  get submitAriaLabel(): string;
20
+ set hourMeterAriaLabel(value: string);
21
+ get hourMeterAriaLabel(): string;
22
+ set minuteMeterAriaLabel(value: string);
23
+ get minuteMeterAriaLabel(): string;
24
+ set hourNeedleAriaLabel(value: string);
25
+ get hourNeedleAriaLabel(): string;
26
+ set minuteNeedleAriaLabel(value: string);
27
+ get minuteNeedleAriaLabel(): string;
20
28
  set noSubmit(value: boolean);
21
29
  get noSubmit(): boolean;
22
30
  get submitButtonRect(): TRect;
@@ -6,7 +6,7 @@ import { defineCustomElement, NectaryElement } from "../utils/element.js";
6
6
  import { getRect } from "../utils/rect.js";
7
7
  import { getReactEventHandler } from "../utils/get-react-event-handler.js";
8
8
  import { stringifyHourFace, stringifyMinute, parseTime, hourToIndex, stringifyHour, getNeedleRotationDeg, getShortestCssDeg, stringifyTime } from "./utils.js";
9
- const templateHTML = '<style>:host{display:block;outline:0}#wrapper{display:flex;flex-direction:column;width:248px;padding:16px;box-sizing:border-box;gap:16px}#header{position:relative;width:100%;height:48px;font:var(--sinch-comp-time-picker-header-font);line-height:48px;user-select:none;color:var(--sinch-comp-time-picker-header-color-default-text-initial)}#footer{display:flex;justify-content:center;width:100%;height:32px}#picker{position:relative;width:216px;height:216px;border-radius:50%;box-sizing:border-box;border:1px solid var(--sinch-comp-time-picker-watch-face-color-default-border-initial);background-color:var(--sinch-comp-time-picker-watch-face-color-default-background-initial)}#picker-hours,#picker-minutes{position:absolute;left:0;top:0;width:100%;height:100%;border-radius:50%;pointer-events:none;user-select:none}.digit-hour-12,.digit-hour-24,.digit-minute{position:absolute;width:28px;height:28px;text-align:center;top:calc(50% - 14px);left:calc(50% - 14px);z-index:1;cursor:pointer}.digit-hour-12{font:var(--sinch-comp-time-picker-digit-font-default-h12);color:var(--sinch-comp-time-picker-digit-color-default-h12-initial);line-height:28px}.digit-hour-12.selected{font:var(--sinch-comp-time-picker-digit-font-checked-h12);color:var(--sinch-comp-time-picker-digit-color-checked-h12-default)}.digit-hour-24{font:var(--sinch-comp-time-picker-digit-font-default-h24);color:var(--sinch-comp-time-picker-digit-color-default-h24-initial);line-height:28px}.digit-hour-24.selected{font:var(--sinch-comp-time-picker-digit-font-checked-h24);color:var(--sinch-comp-time-picker-digit-color-checked-h24-initial)}.digit-minute{font:var(--sinch-comp-time-picker-digit-font-default-minutes);color:var(--sinch-comp-time-picker-digit-color-default-minute-initial);line-height:28px}.digit-minute.selected{font:var(--sinch-comp-time-picker-digit-font-checked-minutes);color:var(--sinch-comp-time-picker-digit-color-checked-minute-initial)}#picker-touch{position:absolute;left:0;top:0;width:100%;height:100%;cursor:pointer;border-radius:50%}#needle-hour,#needle-minute,#picker-touch::after{background-color:var(--sinch-comp-time-picker-needle-color-default-background-initial)}#needle-hour,#needle-minute{position:absolute;transform-origin:bottom center;transform:rotate(0);bottom:50%;height:50px;transition-duration:.25s;transition-timing-function:ease-in-out;transition-property:transform height;z-index:2;outline:0}@media (prefers-reduced-motion){#needle-hour,#needle-minute{transition:none}}#needle-hour{width:4px;left:calc(50% - 2px);border-radius:2px}#needle-minute{width:2px;left:calc(50% - 1px);border-radius:1px}#needle-hour:focus-visible,#needle-minute:focus-visible{background-color:var(--sinch-comp-time-picker-needle-color-default-background-focus)}#needle-minute:not(.selected)::after{content:"";position:absolute;transform:translateX(-50%);left:0;top:-16px;width:4px;height:4px;border-radius:50%;background-color:var(--sinch-comp-time-picker-digit-color-checked-minute-initial)}#picker-touch::after{content:"";position:absolute;top:50%;left:50%;width:12px;height:12px;border-radius:50%;transform:translate(-50%,-50%)}#header-hours,#header-minutes{position:absolute;padding:0 4px;width:50px;outline:0}#header-hours{right:calc(50% + 8px);text-align:right}#header-minutes{left:calc(50% + 8px)}#header-colon{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%)}#submit{position:absolute;right:0;top:50%;transform:translateY(-50%)}#submit-icon{--sinch-global-color-icon:var(--sinch-comp-time-picker-header-color-default-icon-initial)}:host([ampm]) .digit-hour-24{display:none}:host(:not([ampm])) #footer{display:none}</style><div id="wrapper"><div id="header"><div id="header-hours" role="meter" aria-valuemin="0" aria-valuemax="23" aria-valuenow="0" aria-valuetext="0"><span>00</span></div><div id="header-colon">&colon;</div><div id="header-minutes" role="meter" aria-valuemin="0" aria-valuemax="59" aria-valuenow="0" aria-valuetext="0"><span>00</span></div><sinch-button id="submit" size="s" aria-label="Submit"><sinch-icon icons-version="2" name="fa-check" id="icon-submit" slot="icon"></sinch-icon></sinch-button></div><div id="picker" role="group" aria-label="Time picker clock face"><div id="picker-hours"></div><div id="picker-minutes"></div><div id="picker-touch"><div id="needle-hour" tabindex="0" role="slider" aria-label="Hour selector" aria-valuemin="0" aria-valuemax="23" aria-valuenow="0"></div><div id="needle-minute" tabindex="0" role="slider" aria-label="Minute selector" aria-valuemin="0" aria-valuemax="59" aria-valuenow="0"></div></div></div><div id="footer"><sinch-segmented-control id="ampm"><sinch-segmented-control-option value="am" text="AM" aria-label="AM"></sinch-segmented-control-option><sinch-segmented-control-option value="pm" text="PM" aria-label="PM"></sinch-segmented-control-option></sinch-segmented-control></div></div>';
9
+ const templateHTML = '<style>:host{display:block;outline:0}#wrapper{display:flex;flex-direction:column;width:248px;padding:16px;box-sizing:border-box;gap:16px}#header{position:relative;width:100%;height:48px;font:var(--sinch-comp-time-picker-header-font);line-height:48px;user-select:none;color:var(--sinch-comp-time-picker-header-color-default-text-initial)}#footer{display:flex;justify-content:center;width:100%;height:32px}#picker{position:relative;width:216px;height:216px;border-radius:50%;box-sizing:border-box;border:1px solid var(--sinch-comp-time-picker-watch-face-color-default-border-initial);background-color:var(--sinch-comp-time-picker-watch-face-color-default-background-initial)}#picker-hours,#picker-minutes{position:absolute;left:0;top:0;width:100%;height:100%;border-radius:50%;pointer-events:none;user-select:none}.digit-hour-12,.digit-hour-24,.digit-minute{position:absolute;width:28px;height:28px;text-align:center;top:calc(50% - 14px);left:calc(50% - 14px);z-index:1;cursor:pointer}.digit-hour-12{font:var(--sinch-comp-time-picker-digit-font-default-h12);color:var(--sinch-comp-time-picker-digit-color-default-h12-initial);line-height:28px}.digit-hour-12.selected{font:var(--sinch-comp-time-picker-digit-font-checked-h12);color:var(--sinch-comp-time-picker-digit-color-checked-h12-default)}.digit-hour-24{font:var(--sinch-comp-time-picker-digit-font-default-h24);color:var(--sinch-comp-time-picker-digit-color-default-h24-initial);line-height:28px}.digit-hour-24.selected{font:var(--sinch-comp-time-picker-digit-font-checked-h24);color:var(--sinch-comp-time-picker-digit-color-checked-h24-initial)}.digit-minute{font:var(--sinch-comp-time-picker-digit-font-default-minutes);color:var(--sinch-comp-time-picker-digit-color-default-minute-initial);line-height:28px}.digit-minute.selected{font:var(--sinch-comp-time-picker-digit-font-checked-minutes);color:var(--sinch-comp-time-picker-digit-color-checked-minute-initial)}#picker-touch{position:absolute;left:0;top:0;width:100%;height:100%;cursor:pointer;border-radius:50%}#needle-hour,#needle-minute,#picker-touch::after{background-color:var(--sinch-comp-time-picker-needle-color-default-background-initial)}#needle-hour,#needle-minute{position:absolute;transform-origin:bottom center;transform:rotate(0);bottom:50%;height:50px;transition-duration:.25s;transition-timing-function:ease-in-out;transition-property:transform height;z-index:2;outline:0}@media (prefers-reduced-motion){#needle-hour,#needle-minute{transition:none}}#needle-hour{width:4px;left:calc(50% - 2px);border-radius:2px}#needle-minute{width:2px;left:calc(50% - 1px);border-radius:1px}#needle-hour:focus-visible,#needle-minute:focus-visible{background-color:var(--sinch-comp-time-picker-needle-color-default-background-focus)}#needle-minute:not(.selected)::after{content:"";position:absolute;transform:translateX(-50%);left:0;top:-16px;width:4px;height:4px;border-radius:50%;background-color:var(--sinch-comp-time-picker-digit-color-checked-minute-initial)}#picker-touch::after{content:"";position:absolute;top:50%;left:50%;width:12px;height:12px;border-radius:50%;transform:translate(-50%,-50%)}#header-hours,#header-minutes{position:absolute;padding:0 4px;width:50px;outline:0}#header-hours{right:calc(50% + 8px);text-align:right}#header-minutes{left:calc(50% + 8px)}#header-colon{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%)}#submit{position:absolute;right:0;top:50%;transform:translateY(-50%)}#submit-icon{--sinch-global-color-icon:var(--sinch-comp-time-picker-header-color-default-icon-initial)}:host([ampm]) .digit-hour-24{display:none}:host(:not([ampm])) #footer{display:none}</style><div id="wrapper"><div id="header"><div id="header-hours" role="meter" aria-valuemin="0" aria-valuemax="23" aria-valuenow="0" aria-valuetext="0"><span>00</span></div><div id="header-colon">&colon;</div><div id="header-minutes" role="meter" aria-valuemin="0" aria-valuemax="59" aria-valuenow="0" aria-valuetext="0"><span>00</span></div><sinch-button id="submit" size="s" aria-label="Submit"><sinch-icon icons-version="2" name="fa-check" id="icon-submit" slot="icon"></sinch-icon></sinch-button></div><div id="picker" role="group" aria-label="Time picker clock face"><div id="picker-hours"></div><div id="picker-minutes"></div><div id="picker-touch"><div id="needle-hour" tabindex="0" role="slider" aria-valuemin="0" aria-valuemax="23" aria-valuenow="0"></div><div id="needle-minute" tabindex="0" role="slider" aria-valuemin="0" aria-valuemax="59" aria-valuenow="0"></div></div></div><div id="footer"><sinch-segmented-control id="ampm"><sinch-segmented-control-option value="am" text="AM" aria-label="AM"></sinch-segmented-control-option><sinch-segmented-control-option value="pm" text="PM" aria-label="PM"></sinch-segmented-control-option></sinch-segmented-control></div></div>';
10
10
  const template = document.createElement("template");
11
11
  template.innerHTML = templateHTML;
12
12
  const PICKER_RADIUS = 216 / 2;
@@ -126,7 +126,16 @@ class TimePicker extends NectaryElement {
126
126
  this.#controller = null;
127
127
  }
128
128
  static get observedAttributes() {
129
- return ["value", "ampm", "submit-aria-label", "no-submit"];
129
+ return [
130
+ "value",
131
+ "ampm",
132
+ "submit-aria-label",
133
+ "hour-meter-aria-label",
134
+ "minute-meter-aria-label",
135
+ "hour-needle-aria-label",
136
+ "minute-needle-aria-label",
137
+ "no-submit"
138
+ ];
130
139
  }
131
140
  attributeChangedCallback(name, prevValue, newVal) {
132
141
  if (isAttrEqual(prevValue, newVal)) {
@@ -152,6 +161,22 @@ class TimePicker extends NectaryElement {
152
161
  updateAttribute(this.#$submitButton, "aria-label", newVal);
153
162
  break;
154
163
  }
164
+ case "hour-meter-aria-label": {
165
+ updateAttribute(this.#$headerHours, "aria-label", newVal);
166
+ break;
167
+ }
168
+ case "minute-meter-aria-label": {
169
+ updateAttribute(this.#$headerMinutes, "aria-label", newVal);
170
+ break;
171
+ }
172
+ case "hour-needle-aria-label": {
173
+ updateAttribute(this.#$needleHour, "aria-label", newVal);
174
+ break;
175
+ }
176
+ case "minute-needle-aria-label": {
177
+ updateAttribute(this.#$needleMinute, "aria-label", newVal);
178
+ break;
179
+ }
155
180
  case "no-submit": {
156
181
  const isNoSubmit = isAttrTrue(newVal);
157
182
  updateBooleanAttribute(this, "no-submit", isNoSubmit);
@@ -178,6 +203,30 @@ class TimePicker extends NectaryElement {
178
203
  get submitAriaLabel() {
179
204
  return getAttribute(this, "submit-aria-label", "");
180
205
  }
206
+ set hourMeterAriaLabel(value) {
207
+ updateAttribute(this, "hour-meter-aria-label", value);
208
+ }
209
+ get hourMeterAriaLabel() {
210
+ return getAttribute(this, "hour-meter-aria-label", "");
211
+ }
212
+ set minuteMeterAriaLabel(value) {
213
+ updateAttribute(this, "minute-meter-aria-label", value);
214
+ }
215
+ get minuteMeterAriaLabel() {
216
+ return getAttribute(this, "minute-meter-aria-label", "");
217
+ }
218
+ set hourNeedleAriaLabel(value) {
219
+ updateAttribute(this, "hour-needle-aria-label", value);
220
+ }
221
+ get hourNeedleAriaLabel() {
222
+ return getAttribute(this, "hour-needle-aria-label", "");
223
+ }
224
+ set minuteNeedleAriaLabel(value) {
225
+ updateAttribute(this, "minute-needle-aria-label", value);
226
+ }
227
+ get minuteNeedleAriaLabel() {
228
+ return getAttribute(this, "minute-needle-aria-label", "");
229
+ }
181
230
  set noSubmit(value) {
182
231
  updateBooleanAttribute(this, "no-submit", value);
183
232
  }
@@ -10,6 +10,14 @@ export type TSinchTimePickerProps = {
10
10
  'no-submit'?: boolean;
11
11
  /** Submit button label that is used for a11y */
12
12
  'submit-aria-label': string;
13
+ /** Hours meter label that is used for a11y */
14
+ 'hour-meter-aria-label': string;
15
+ /** Minutes meter label that is used for a11y */
16
+ 'minute-meter-aria-label': string;
17
+ /** Hour needle slider label that is used for a11y */
18
+ 'hour-needle-aria-label': string;
19
+ /** Minute needle slider label that is used for a11y */
20
+ 'minute-needle-aria-label': string;
13
21
  readonly submitButtonRect?: TRect;
14
22
  readonly amButtonRect?: TRect | null;
15
23
  readonly pmButtonRect?: TRect | null;
package/tooltip/index.js CHANGED
@@ -94,6 +94,7 @@ class Tooltip extends NectaryElement {
94
94
  updateAttribute(this.#$pop, "orientation", getPopOrientation(this.orientation));
95
95
  updateBooleanAttribute(this.#$pop, "hide-outside-viewport", !this.showOutsideViewport);
96
96
  updateBooleanAttribute(this.#$pop, "disable-focus-restore", true);
97
+ this.#updatePopAriaLabel();
97
98
  this.#updateText();
98
99
  }
99
100
  disconnectedCallback() {
@@ -139,6 +140,7 @@ class Tooltip extends NectaryElement {
139
140
  switch (name) {
140
141
  case "text": {
141
142
  this.#updateText();
143
+ this.#updatePopAriaLabel();
142
144
  break;
143
145
  }
144
146
  case "orientation": {
@@ -161,7 +163,11 @@ class Tooltip extends NectaryElement {
161
163
  }
162
164
  case "aria-label":
163
165
  case "aria-description": {
164
- updateAttribute(this.#$pop, name, newVal);
166
+ if (name === "aria-label") {
167
+ this.#updatePopAriaLabel();
168
+ } else {
169
+ updateAttribute(this.#$pop, name, newVal);
170
+ }
165
171
  break;
166
172
  }
167
173
  case "show-outside-viewport": {
@@ -737,6 +743,13 @@ class Tooltip extends NectaryElement {
737
743
  this.#subscribeMouseEnterEvent();
738
744
  }
739
745
  }
746
+ #updatePopAriaLabel() {
747
+ const rawAriaLabel = getAttribute(this, "aria-label");
748
+ const explicitAriaLabel = rawAriaLabel !== null && rawAriaLabel.trim().length > 0 ? rawAriaLabel.trim() : null;
749
+ const fallbackAriaLabel = this.text ?? "";
750
+ const ariaLabel = explicitAriaLabel ?? (fallbackAriaLabel.length === 0 ? null : fallbackAriaLabel);
751
+ updateAttribute(this.#$pop, "aria-label", ariaLabel);
752
+ }
740
753
  #subscribeMouseEnterEvent() {
741
754
  if (!this.isDomConnected || this.#isSubscribed) {
742
755
  return;
@@ -20,6 +20,10 @@ export type TSinchTooltipProps = {
20
20
  * to true can have unexpected behavior in dialogs
21
21
  */
22
22
  'allow-scroll'?: boolean;
23
+ /** Overrides the accessible name of the tooltip popup. Falls back to `text` when absent or empty. */
24
+ 'aria-label'?: string;
25
+ /** Sets the aria-description on the tooltip popup */
26
+ 'aria-description'?: string;
23
27
  };
24
28
  export type TSinchTooltipEvents = {
25
29
  /** Show event handler */