@nectary/components 5.28.0 → 5.29.0
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 +116 -6
- package/dialog/index.d.ts +4 -0
- package/dialog/index.js +65 -4
- package/dialog/types.d.ts +6 -0
- package/package.json +1 -1
- package/persistent-overlay/index.js +15 -1
- package/persistent-overlay/types.d.ts +4 -2
- package/time-picker/index.d.ts +2 -0
- package/time-picker/index.js +38 -4
- package/time-picker/types.d.ts +4 -0
- package/utils/scroll-lock.d.ts +1 -0
- package/utils/scroll-lock.js +3 -1
package/bundle.js
CHANGED
|
@@ -5038,6 +5038,7 @@ class StopEvents extends HTMLElement {
|
|
|
5038
5038
|
}
|
|
5039
5039
|
defineCustomElement("sinch-stop-events", StopEvents);
|
|
5040
5040
|
const bodyEl = document.body;
|
|
5041
|
+
const isScrollDisabled = () => bodyEl.__dialog_counter__ > 0;
|
|
5041
5042
|
const disableScroll = () => {
|
|
5042
5043
|
bodyEl.__dialog_counter__ = (bodyEl.__dialog_counter__ ?? 0) + 1;
|
|
5043
5044
|
if (bodyEl.__dialog_counter__ === 1) {
|
|
@@ -5055,7 +5056,7 @@ const enableScroll = () => {
|
|
|
5055
5056
|
bodyEl.style.removeProperty("padding-right");
|
|
5056
5057
|
}
|
|
5057
5058
|
};
|
|
5058
|
-
const templateHTML$P = '<style>:host{display:contents;--sinch-comp-dialog-max-width:512px;--sinch-comp-dialog-max-height:90vh;--sinch-comp-dialog-width:fit-content;--sinch-dialog-close-button-display:unset}#dialog{position:fixed;left:0;right:0;margin:auto;display:flex;flex-direction:column;padding:24px 0;width:var(--sinch-comp-dialog-width);max-width:var(--sinch-comp-dialog-max-width);max-height:var(--sinch-comp-dialog-max-height);border-radius:var(--sinch-comp-dialog-shape-radius);box-sizing:border-box;contain:content;background-color:var(--sinch-comp-dialog-color-default-background-initial);border:none;box-shadow:var(--sinch-comp-dialog-shadow);outline:0}#dialog:not([open]){display:none}dialog::backdrop{background-color:#000;opacity:.55}#header{display:flex;flex-direction:row;align-items:flex-start;margin-bottom:12px;padding:0 24px;gap:8px;--sinch-global-size-icon:24px;--sinch-global-color-icon:var(--sinch-comp-dialog-color-default-icon-initial)}#caption{--sinch-global-color-text:var(--sinch-comp-dialog-color-default-title-initial);--sinch-comp-title-font:var(--sinch-comp-dialog-font-title)}#content{min-height:0;overflow:auto;padding:4px 24px}#action{display:flex;flex-direction:row;justify-content:flex-end;gap:16px;margin-top:20px;padding:0 24px}#action.empty{display:none}#close{display:var(--sinch-dialog-close-button-display,initial);position:relative;left:4px;top:-4px;margin-left:auto}</style><dialog id="dialog"><div id="header"><slot id="icon" name="icon"></slot><sinch-title id="caption" type="m" level="3"></sinch-title><sinch-button id="close" size="s"><sinch-icon icons-version="2" name="fa-xmark" id="icon-close" slot="icon"></sinch-icon></sinch-button></div><div id="content"><sinch-stop-events events="close"><slot name="content"></slot></sinch-stop-events></div><div id="action"><sinch-stop-events events="close"><slot name="buttons"></slot></sinch-stop-events></div></dialog>';
|
|
5059
|
+
const templateHTML$P = '<style>:host{display:contents;--sinch-comp-dialog-max-width:512px;--sinch-comp-dialog-max-height:90vh;--sinch-comp-dialog-width:fit-content;--sinch-dialog-close-button-display:unset}#dialog{position:fixed;left:0;right:0;margin:auto;display:flex;flex-direction:column;padding:24px 0;width:var(--sinch-comp-dialog-width);max-width:var(--sinch-comp-dialog-max-width);max-height:var(--sinch-comp-dialog-max-height);border-radius:var(--sinch-comp-dialog-shape-radius);box-sizing:border-box;contain:content;background-color:var(--sinch-comp-dialog-color-default-background-initial);border:none;box-shadow:var(--sinch-comp-dialog-shadow);outline:0}#dialog:not([open]){display:none}dialog::backdrop{background-color:#000;opacity:.55}#header{display:flex;flex-direction:row;align-items:flex-start;margin-bottom:12px;padding:0 24px;gap:8px;--sinch-global-size-icon:24px;--sinch-global-color-icon:var(--sinch-comp-dialog-color-default-icon-initial)}#caption{--sinch-global-color-text:var(--sinch-comp-dialog-color-default-title-initial);--sinch-comp-title-font:var(--sinch-comp-dialog-font-title)}#content{min-height:0;overflow:auto;padding:4px 24px}#action{display:flex;flex-direction:row;justify-content:flex-end;gap:16px;margin-top:20px;padding:0 24px}#action.empty{display:none}#close{display:var(--sinch-dialog-close-button-display,initial);position:relative;left:4px;top:-4px;margin-left:auto}:host([hide-close-button]) #close{display:none}</style><dialog id="dialog"><div id="header"><slot id="icon" name="icon"></slot><sinch-title id="caption" type="m" level="3"></sinch-title><sinch-button id="close" size="s"><sinch-icon icons-version="2" name="fa-xmark" id="icon-close" slot="icon"></sinch-icon></sinch-button></div><div id="content"><sinch-stop-events events="close"><slot name="content"></slot></sinch-stop-events></div><div id="action"><sinch-stop-events events="close"><slot name="buttons"></slot></sinch-stop-events></div></dialog>';
|
|
5059
5060
|
const template$P = document.createElement("template");
|
|
5060
5061
|
template$P.innerHTML = templateHTML$P;
|
|
5061
5062
|
class Dialog extends NectaryElement {
|
|
@@ -5089,12 +5090,18 @@ class Dialog extends NectaryElement {
|
|
|
5089
5090
|
options
|
|
5090
5091
|
);
|
|
5091
5092
|
this.#$dialog.addEventListener("cancel", this.#onCancel, options);
|
|
5093
|
+
this.#$dialog.addEventListener("close", this.#onDialogClose, options);
|
|
5092
5094
|
this.#$actionSlot.addEventListener(
|
|
5093
5095
|
"slotchange",
|
|
5094
5096
|
this.#onActionSlotChange,
|
|
5095
5097
|
options
|
|
5096
5098
|
);
|
|
5097
5099
|
this.addEventListener("-close", this.#onCloseReactHandler, options);
|
|
5100
|
+
this.addEventListener(
|
|
5101
|
+
"-dismiss-blocked",
|
|
5102
|
+
this.#onDismissBlockedReactHandler,
|
|
5103
|
+
options
|
|
5104
|
+
);
|
|
5098
5105
|
this.#onActionSlotChange();
|
|
5099
5106
|
if (this.open) {
|
|
5100
5107
|
this.#onExpand();
|
|
@@ -5107,7 +5114,13 @@ class Dialog extends NectaryElement {
|
|
|
5107
5114
|
this.#controller = null;
|
|
5108
5115
|
}
|
|
5109
5116
|
static get observedAttributes() {
|
|
5110
|
-
return [
|
|
5117
|
+
return [
|
|
5118
|
+
"caption",
|
|
5119
|
+
"open",
|
|
5120
|
+
"close-aria-label",
|
|
5121
|
+
"hide-close-button",
|
|
5122
|
+
"prevent-close"
|
|
5123
|
+
];
|
|
5111
5124
|
}
|
|
5112
5125
|
attributeChangedCallback(name, oldVal, newVal) {
|
|
5113
5126
|
if (isAttrEqual(oldVal, newVal)) {
|
|
@@ -5134,6 +5147,10 @@ class Dialog extends NectaryElement {
|
|
|
5134
5147
|
updateAttribute(this.#$closeButton, "aria-label", newVal);
|
|
5135
5148
|
break;
|
|
5136
5149
|
}
|
|
5150
|
+
case "hide-close-button": {
|
|
5151
|
+
updateBooleanAttribute(this, name, isAttrTrue(newVal));
|
|
5152
|
+
break;
|
|
5153
|
+
}
|
|
5137
5154
|
}
|
|
5138
5155
|
}
|
|
5139
5156
|
set caption(caption) {
|
|
@@ -5148,6 +5165,18 @@ class Dialog extends NectaryElement {
|
|
|
5148
5165
|
get open() {
|
|
5149
5166
|
return getBooleanAttribute(this, "open");
|
|
5150
5167
|
}
|
|
5168
|
+
set hideCloseButton(isHidden) {
|
|
5169
|
+
updateBooleanAttribute(this, "hide-close-button", isHidden);
|
|
5170
|
+
}
|
|
5171
|
+
get hideCloseButton() {
|
|
5172
|
+
return getBooleanAttribute(this, "hide-close-button");
|
|
5173
|
+
}
|
|
5174
|
+
set preventClose(isPrevented) {
|
|
5175
|
+
updateBooleanAttribute(this, "prevent-close", isPrevented);
|
|
5176
|
+
}
|
|
5177
|
+
get preventClose() {
|
|
5178
|
+
return getBooleanAttribute(this, "prevent-close");
|
|
5179
|
+
}
|
|
5151
5180
|
get dialogRect() {
|
|
5152
5181
|
return getRect(this.#$dialog);
|
|
5153
5182
|
}
|
|
@@ -5155,6 +5184,12 @@ class Dialog extends NectaryElement {
|
|
|
5155
5184
|
return getRect(this.#$closeButton);
|
|
5156
5185
|
}
|
|
5157
5186
|
#onCancel = (e) => {
|
|
5187
|
+
if (this.preventClose) {
|
|
5188
|
+
e.preventDefault();
|
|
5189
|
+
e.stopPropagation();
|
|
5190
|
+
this.#dispatchDismissBlockedEvent("escape");
|
|
5191
|
+
return;
|
|
5192
|
+
}
|
|
5158
5193
|
if (e.cancelable) {
|
|
5159
5194
|
e.preventDefault();
|
|
5160
5195
|
} else {
|
|
@@ -5163,7 +5198,22 @@ class Dialog extends NectaryElement {
|
|
|
5163
5198
|
e.stopPropagation();
|
|
5164
5199
|
this.#dispatchCloseEvent("escape", e.cancelable);
|
|
5165
5200
|
};
|
|
5201
|
+
#onDialogClose = () => {
|
|
5202
|
+
if (!this.isDomConnected || !this.preventClose || !this.open) {
|
|
5203
|
+
return;
|
|
5204
|
+
}
|
|
5205
|
+
if (!this.#$dialog.open) {
|
|
5206
|
+
this.#$dialog.showModal();
|
|
5207
|
+
if (!isScrollDisabled()) {
|
|
5208
|
+
disableScroll();
|
|
5209
|
+
}
|
|
5210
|
+
}
|
|
5211
|
+
};
|
|
5166
5212
|
#onCloseClick = () => {
|
|
5213
|
+
if (this.preventClose) {
|
|
5214
|
+
this.#dispatchDismissBlockedEvent("close");
|
|
5215
|
+
return;
|
|
5216
|
+
}
|
|
5167
5217
|
this.#dispatchCloseEvent("close", true);
|
|
5168
5218
|
};
|
|
5169
5219
|
#onBackdropMouseDown = (e) => {
|
|
@@ -5171,6 +5221,11 @@ class Dialog extends NectaryElement {
|
|
|
5171
5221
|
const rect = this.dialogRect;
|
|
5172
5222
|
const isInside = e.x >= rect.x && e.x < rect.x + rect.width && e.y >= rect.y && e.y < rect.y + rect.height;
|
|
5173
5223
|
if (!isInside) {
|
|
5224
|
+
if (this.preventClose) {
|
|
5225
|
+
e.stopPropagation();
|
|
5226
|
+
this.#dispatchDismissBlockedEvent("backdrop");
|
|
5227
|
+
return;
|
|
5228
|
+
}
|
|
5174
5229
|
e.stopPropagation();
|
|
5175
5230
|
this.#dispatchCloseEvent("backdrop", e.cancelable);
|
|
5176
5231
|
}
|
|
@@ -5180,9 +5235,16 @@ class Dialog extends NectaryElement {
|
|
|
5180
5235
|
getReactEventHandler(this, "on-close")?.(e);
|
|
5181
5236
|
getReactEventHandler(this, "onClose")?.(e);
|
|
5182
5237
|
};
|
|
5238
|
+
#onDismissBlockedReactHandler = (e) => {
|
|
5239
|
+
getReactEventHandler(this, "on-dismiss-blocked")?.(e);
|
|
5240
|
+
getReactEventHandler(this, "onDismissBlocked")?.(e);
|
|
5241
|
+
};
|
|
5183
5242
|
#dispatchCloseEvent(detail, cancelable) {
|
|
5184
5243
|
this.dispatchEvent(new CustomEvent("-close", { detail, cancelable }));
|
|
5185
5244
|
}
|
|
5245
|
+
#dispatchDismissBlockedEvent(detail) {
|
|
5246
|
+
this.dispatchEvent(new CustomEvent("-dismiss-blocked", { detail }));
|
|
5247
|
+
}
|
|
5186
5248
|
#onExpand() {
|
|
5187
5249
|
if (!this.isDomConnected || this.#$dialog.open || !this.open) {
|
|
5188
5250
|
return;
|
|
@@ -8285,7 +8347,7 @@ class Pagination extends NectaryElement {
|
|
|
8285
8347
|
}
|
|
8286
8348
|
}
|
|
8287
8349
|
defineCustomElement("sinch-pagination", Pagination);
|
|
8288
|
-
const templateHTML$v = '<style>#persisted-dialog{--sinch-dialog-close-button-display:none}::slotted(*){display:block}</style><sinch-dialog id="persisted-dialog"><div slot="icon"><slot id="icon" name="icon"></slot></div><div slot="content"><slot name="content"></slot></div><div slot="buttons"><slot name="buttons"></slot></div></sinch-dialog>';
|
|
8350
|
+
const templateHTML$v = '<style>#persisted-dialog{--sinch-dialog-close-button-display:none}::slotted(*){display:block}</style><sinch-dialog id="persisted-dialog" prevent-close><div slot="icon"><slot id="icon" name="icon"></slot></div><div slot="content"><slot name="content"></slot></div><div slot="buttons"><slot name="buttons"></slot></div></sinch-dialog>';
|
|
8289
8351
|
const template$v = document.createElement("template");
|
|
8290
8352
|
template$v.innerHTML = templateHTML$v;
|
|
8291
8353
|
function isVisible(elementStyle) {
|
|
@@ -8338,6 +8400,8 @@ class PersistentOverlay extends NectaryElement {
|
|
|
8338
8400
|
this.#controller = new AbortController();
|
|
8339
8401
|
const { signal } = this.#controller;
|
|
8340
8402
|
this.addEventListener("-visibility-altered", this.#onVisibilityAlteredReactHandler, { signal });
|
|
8403
|
+
this.addEventListener("-dismiss-blocked", this.#onDismissBlockedReactHandler, { signal });
|
|
8404
|
+
this.#$sinchDialog.addEventListener("-dismiss-blocked", this.#onDismissBlocked, { signal });
|
|
8341
8405
|
if (this.open) {
|
|
8342
8406
|
this.#startObservingAlteration();
|
|
8343
8407
|
}
|
|
@@ -8373,10 +8437,22 @@ class PersistentOverlay extends NectaryElement {
|
|
|
8373
8437
|
#onVisibilityAltered() {
|
|
8374
8438
|
this.dispatchEvent(new CustomEvent("-visibility-altered"));
|
|
8375
8439
|
}
|
|
8440
|
+
#onDismissBlocked = (e) => {
|
|
8441
|
+
this.#dispatchDismissBlockedEvent(
|
|
8442
|
+
e.detail
|
|
8443
|
+
);
|
|
8444
|
+
};
|
|
8376
8445
|
#onVisibilityAlteredReactHandler = (e) => {
|
|
8377
8446
|
getReactEventHandler(this, "on-visibility-altered")?.(e);
|
|
8378
8447
|
getReactEventHandler(this, "onVisibilityAltered")?.(e);
|
|
8379
8448
|
};
|
|
8449
|
+
#onDismissBlockedReactHandler = (e) => {
|
|
8450
|
+
getReactEventHandler(this, "on-dismiss-blocked")?.(e);
|
|
8451
|
+
getReactEventHandler(this, "onDismissBlocked")?.(e);
|
|
8452
|
+
};
|
|
8453
|
+
#dispatchDismissBlockedEvent(detail) {
|
|
8454
|
+
this.dispatchEvent(new CustomEvent("-dismiss-blocked", { detail }));
|
|
8455
|
+
}
|
|
8380
8456
|
}
|
|
8381
8457
|
defineCustomElement("sinch-persistent-overlay", PersistentOverlay);
|
|
8382
8458
|
const ATTR_PROGRESS_STEPPER_ITEM_CHECKED = "data-checked";
|
|
@@ -13948,13 +14024,14 @@ class TimePicker extends NectaryElement {
|
|
|
13948
14024
|
this.#$needleHour.addEventListener("keydown", this.#onHoursKeydown, options);
|
|
13949
14025
|
this.#$needleMinute.addEventListener("keydown", this.#onMinutesKeydown, options);
|
|
13950
14026
|
this.addEventListener("-change", this.#onChangeReactHandler, options);
|
|
14027
|
+
this.addEventListener("-submit", this.#onSubmitReactHandler, options);
|
|
13951
14028
|
}
|
|
13952
14029
|
disconnectedCallback() {
|
|
13953
14030
|
this.#controller.abort();
|
|
13954
14031
|
this.#controller = null;
|
|
13955
14032
|
}
|
|
13956
14033
|
static get observedAttributes() {
|
|
13957
|
-
return ["value", "ampm", "submit-aria-label"];
|
|
14034
|
+
return ["value", "ampm", "submit-aria-label", "no-submit"];
|
|
13958
14035
|
}
|
|
13959
14036
|
attributeChangedCallback(name, prevValue, newVal) {
|
|
13960
14037
|
if (isAttrEqual(prevValue, newVal)) {
|
|
@@ -13980,6 +14057,12 @@ class TimePicker extends NectaryElement {
|
|
|
13980
14057
|
updateAttribute(this.#$submitButton, "aria-label", newVal);
|
|
13981
14058
|
break;
|
|
13982
14059
|
}
|
|
14060
|
+
case "no-submit": {
|
|
14061
|
+
const isNoSubmit = isAttrTrue(newVal);
|
|
14062
|
+
updateBooleanAttribute(this, "no-submit", isNoSubmit);
|
|
14063
|
+
this.#render();
|
|
14064
|
+
break;
|
|
14065
|
+
}
|
|
13983
14066
|
}
|
|
13984
14067
|
}
|
|
13985
14068
|
set value(value) {
|
|
@@ -14000,6 +14083,12 @@ class TimePicker extends NectaryElement {
|
|
|
14000
14083
|
get submitAriaLabel() {
|
|
14001
14084
|
return getAttribute(this, "submit-aria-label", "");
|
|
14002
14085
|
}
|
|
14086
|
+
set noSubmit(value) {
|
|
14087
|
+
updateBooleanAttribute(this, "no-submit", value);
|
|
14088
|
+
}
|
|
14089
|
+
get noSubmit() {
|
|
14090
|
+
return getBooleanAttribute(this, "no-submit");
|
|
14091
|
+
}
|
|
14003
14092
|
get submitButtonRect() {
|
|
14004
14093
|
return getRect(this.#$submitButton);
|
|
14005
14094
|
}
|
|
@@ -14066,6 +14155,7 @@ class TimePicker extends NectaryElement {
|
|
|
14066
14155
|
this.#$headerMinutes.focus();
|
|
14067
14156
|
}
|
|
14068
14157
|
this.#render();
|
|
14158
|
+
this.#onChange();
|
|
14069
14159
|
};
|
|
14070
14160
|
#render() {
|
|
14071
14161
|
const is24 = getBooleanAttribute(this, "ampm") === false;
|
|
@@ -14073,6 +14163,7 @@ class TimePicker extends NectaryElement {
|
|
|
14073
14163
|
this.#selectMinute();
|
|
14074
14164
|
this.#$headerHoursText.textContent = stringifyHour(this.#hour, is24);
|
|
14075
14165
|
this.#$headerMinutesText.textContent = stringifyMinute(this.#minute);
|
|
14166
|
+
this.#$submitButton.style.display = this.noSubmit ? "none" : "";
|
|
14076
14167
|
updateAttribute(this.#$headerHours, "aria-valuenow", this.#hour);
|
|
14077
14168
|
updateAttribute(this.#$headerHours, "aria-valuetext", this.#hour);
|
|
14078
14169
|
updateAttribute(this.#$headerMinutes, "aria-valuenow", this.#minute);
|
|
@@ -14122,6 +14213,7 @@ class TimePicker extends NectaryElement {
|
|
|
14122
14213
|
if (this.#hour >= 12) {
|
|
14123
14214
|
this.#hour -= 12;
|
|
14124
14215
|
this.#render();
|
|
14216
|
+
this.#onChange();
|
|
14125
14217
|
}
|
|
14126
14218
|
break;
|
|
14127
14219
|
}
|
|
@@ -14129,31 +14221,41 @@ class TimePicker extends NectaryElement {
|
|
|
14129
14221
|
if (this.#hour < 12) {
|
|
14130
14222
|
this.#hour += 12;
|
|
14131
14223
|
this.#render();
|
|
14224
|
+
this.#onChange();
|
|
14132
14225
|
}
|
|
14133
14226
|
break;
|
|
14134
14227
|
}
|
|
14135
14228
|
}
|
|
14136
14229
|
};
|
|
14137
|
-
#
|
|
14230
|
+
#onEventEmit = (eventName) => {
|
|
14138
14231
|
const value = stringifyTime(this.#hour, this.#minute);
|
|
14139
14232
|
this.dispatchEvent(
|
|
14140
|
-
new CustomEvent(
|
|
14233
|
+
new CustomEvent(eventName, {
|
|
14141
14234
|
detail: value
|
|
14142
14235
|
})
|
|
14143
14236
|
);
|
|
14144
14237
|
};
|
|
14238
|
+
#onChange = () => {
|
|
14239
|
+
this.#onEventEmit("-change");
|
|
14240
|
+
};
|
|
14241
|
+
#onSubmitButtonClick = () => {
|
|
14242
|
+
this.#onEventEmit("-submit");
|
|
14243
|
+
this.#onEventEmit("-change");
|
|
14244
|
+
};
|
|
14145
14245
|
#onHoursKeydown = (e) => {
|
|
14146
14246
|
switch (e.key) {
|
|
14147
14247
|
case "ArrowUp": {
|
|
14148
14248
|
e.preventDefault();
|
|
14149
14249
|
this.#hour = (this.#hour + 1) % 24;
|
|
14150
14250
|
this.#render();
|
|
14251
|
+
this.#onChange();
|
|
14151
14252
|
break;
|
|
14152
14253
|
}
|
|
14153
14254
|
case "ArrowDown": {
|
|
14154
14255
|
e.preventDefault();
|
|
14155
14256
|
this.#hour = (this.#hour + 23) % 24;
|
|
14156
14257
|
this.#render();
|
|
14258
|
+
this.#onChange();
|
|
14157
14259
|
break;
|
|
14158
14260
|
}
|
|
14159
14261
|
}
|
|
@@ -14164,12 +14266,14 @@ class TimePicker extends NectaryElement {
|
|
|
14164
14266
|
e.preventDefault();
|
|
14165
14267
|
this.#minute = (this.#minute + 1) % 60;
|
|
14166
14268
|
this.#render();
|
|
14269
|
+
this.#onChange();
|
|
14167
14270
|
break;
|
|
14168
14271
|
}
|
|
14169
14272
|
case "ArrowDown": {
|
|
14170
14273
|
e.preventDefault();
|
|
14171
14274
|
this.#minute = (this.#minute + 59) % 60;
|
|
14172
14275
|
this.#render();
|
|
14276
|
+
this.#onChange();
|
|
14173
14277
|
break;
|
|
14174
14278
|
}
|
|
14175
14279
|
}
|
|
@@ -14178,13 +14282,19 @@ class TimePicker extends NectaryElement {
|
|
|
14178
14282
|
getReactEventHandler(this, "on-change")?.(e);
|
|
14179
14283
|
getReactEventHandler(this, "onChange")?.(e);
|
|
14180
14284
|
};
|
|
14285
|
+
#onSubmitReactHandler = (e) => {
|
|
14286
|
+
getReactEventHandler(this, "on-submit")?.(e);
|
|
14287
|
+
getReactEventHandler(this, "onSubmit")?.(e);
|
|
14288
|
+
};
|
|
14181
14289
|
#onHourDigitClick = (hour) => {
|
|
14182
14290
|
this.#hour = hour;
|
|
14183
14291
|
this.#render();
|
|
14292
|
+
this.#onChange();
|
|
14184
14293
|
};
|
|
14185
14294
|
#onMinuteDigitClick = (minute) => {
|
|
14186
14295
|
this.#minute = minute;
|
|
14187
14296
|
this.#render();
|
|
14297
|
+
this.#onChange();
|
|
14188
14298
|
};
|
|
14189
14299
|
#onDigitKeydown = (e, callback) => {
|
|
14190
14300
|
if (e.key === "Enter" || e.key === " ") {
|
package/dialog/index.d.ts
CHANGED
|
@@ -14,6 +14,10 @@ export declare class Dialog extends NectaryElement {
|
|
|
14
14
|
get caption(): string;
|
|
15
15
|
set open(isOpen: boolean);
|
|
16
16
|
get open(): boolean;
|
|
17
|
+
set hideCloseButton(isHidden: boolean);
|
|
18
|
+
get hideCloseButton(): boolean;
|
|
19
|
+
set preventClose(isPrevented: boolean);
|
|
20
|
+
get preventClose(): boolean;
|
|
17
21
|
get dialogRect(): import("../types").TRect;
|
|
18
22
|
get closeButtonRect(): import("../types").TRect;
|
|
19
23
|
}
|
package/dialog/index.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import "../icon/index.js";
|
|
2
2
|
import "../stop-events/index.js";
|
|
3
3
|
import "../title/index.js";
|
|
4
|
-
import { isAttrEqual,
|
|
4
|
+
import { isAttrEqual, updateBooleanAttribute, isAttrTrue, updateAttribute, getAttribute, getBooleanAttribute, setClass } from "../utils/dom.js";
|
|
5
5
|
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 { isTargetEqual } from "../utils/event-target.js";
|
|
9
|
-
import { disableScroll, enableScroll } from "../utils/scroll-lock.js";
|
|
10
|
-
const templateHTML = '<style>:host{display:contents;--sinch-comp-dialog-max-width:512px;--sinch-comp-dialog-max-height:90vh;--sinch-comp-dialog-width:fit-content;--sinch-dialog-close-button-display:unset}#dialog{position:fixed;left:0;right:0;margin:auto;display:flex;flex-direction:column;padding:24px 0;width:var(--sinch-comp-dialog-width);max-width:var(--sinch-comp-dialog-max-width);max-height:var(--sinch-comp-dialog-max-height);border-radius:var(--sinch-comp-dialog-shape-radius);box-sizing:border-box;contain:content;background-color:var(--sinch-comp-dialog-color-default-background-initial);border:none;box-shadow:var(--sinch-comp-dialog-shadow);outline:0}#dialog:not([open]){display:none}dialog::backdrop{background-color:#000;opacity:.55}#header{display:flex;flex-direction:row;align-items:flex-start;margin-bottom:12px;padding:0 24px;gap:8px;--sinch-global-size-icon:24px;--sinch-global-color-icon:var(--sinch-comp-dialog-color-default-icon-initial)}#caption{--sinch-global-color-text:var(--sinch-comp-dialog-color-default-title-initial);--sinch-comp-title-font:var(--sinch-comp-dialog-font-title)}#content{min-height:0;overflow:auto;padding:4px 24px}#action{display:flex;flex-direction:row;justify-content:flex-end;gap:16px;margin-top:20px;padding:0 24px}#action.empty{display:none}#close{display:var(--sinch-dialog-close-button-display,initial);position:relative;left:4px;top:-4px;margin-left:auto}</style><dialog id="dialog"><div id="header"><slot id="icon" name="icon"></slot><sinch-title id="caption" type="m" level="3"></sinch-title><sinch-button id="close" size="s"><sinch-icon icons-version="2" name="fa-xmark" id="icon-close" slot="icon"></sinch-icon></sinch-button></div><div id="content"><sinch-stop-events events="close"><slot name="content"></slot></sinch-stop-events></div><div id="action"><sinch-stop-events events="close"><slot name="buttons"></slot></sinch-stop-events></div></dialog>';
|
|
9
|
+
import { isScrollDisabled, disableScroll, enableScroll } from "../utils/scroll-lock.js";
|
|
10
|
+
const templateHTML = '<style>:host{display:contents;--sinch-comp-dialog-max-width:512px;--sinch-comp-dialog-max-height:90vh;--sinch-comp-dialog-width:fit-content;--sinch-dialog-close-button-display:unset}#dialog{position:fixed;left:0;right:0;margin:auto;display:flex;flex-direction:column;padding:24px 0;width:var(--sinch-comp-dialog-width);max-width:var(--sinch-comp-dialog-max-width);max-height:var(--sinch-comp-dialog-max-height);border-radius:var(--sinch-comp-dialog-shape-radius);box-sizing:border-box;contain:content;background-color:var(--sinch-comp-dialog-color-default-background-initial);border:none;box-shadow:var(--sinch-comp-dialog-shadow);outline:0}#dialog:not([open]){display:none}dialog::backdrop{background-color:#000;opacity:.55}#header{display:flex;flex-direction:row;align-items:flex-start;margin-bottom:12px;padding:0 24px;gap:8px;--sinch-global-size-icon:24px;--sinch-global-color-icon:var(--sinch-comp-dialog-color-default-icon-initial)}#caption{--sinch-global-color-text:var(--sinch-comp-dialog-color-default-title-initial);--sinch-comp-title-font:var(--sinch-comp-dialog-font-title)}#content{min-height:0;overflow:auto;padding:4px 24px}#action{display:flex;flex-direction:row;justify-content:flex-end;gap:16px;margin-top:20px;padding:0 24px}#action.empty{display:none}#close{display:var(--sinch-dialog-close-button-display,initial);position:relative;left:4px;top:-4px;margin-left:auto}:host([hide-close-button]) #close{display:none}</style><dialog id="dialog"><div id="header"><slot id="icon" name="icon"></slot><sinch-title id="caption" type="m" level="3"></sinch-title><sinch-button id="close" size="s"><sinch-icon icons-version="2" name="fa-xmark" id="icon-close" slot="icon"></sinch-icon></sinch-button></div><div id="content"><sinch-stop-events events="close"><slot name="content"></slot></sinch-stop-events></div><div id="action"><sinch-stop-events events="close"><slot name="buttons"></slot></sinch-stop-events></div></dialog>';
|
|
11
11
|
const template = document.createElement("template");
|
|
12
12
|
template.innerHTML = templateHTML;
|
|
13
13
|
class Dialog extends NectaryElement {
|
|
@@ -41,12 +41,18 @@ class Dialog extends NectaryElement {
|
|
|
41
41
|
options
|
|
42
42
|
);
|
|
43
43
|
this.#$dialog.addEventListener("cancel", this.#onCancel, options);
|
|
44
|
+
this.#$dialog.addEventListener("close", this.#onDialogClose, options);
|
|
44
45
|
this.#$actionSlot.addEventListener(
|
|
45
46
|
"slotchange",
|
|
46
47
|
this.#onActionSlotChange,
|
|
47
48
|
options
|
|
48
49
|
);
|
|
49
50
|
this.addEventListener("-close", this.#onCloseReactHandler, options);
|
|
51
|
+
this.addEventListener(
|
|
52
|
+
"-dismiss-blocked",
|
|
53
|
+
this.#onDismissBlockedReactHandler,
|
|
54
|
+
options
|
|
55
|
+
);
|
|
50
56
|
this.#onActionSlotChange();
|
|
51
57
|
if (this.open) {
|
|
52
58
|
this.#onExpand();
|
|
@@ -59,7 +65,13 @@ class Dialog extends NectaryElement {
|
|
|
59
65
|
this.#controller = null;
|
|
60
66
|
}
|
|
61
67
|
static get observedAttributes() {
|
|
62
|
-
return [
|
|
68
|
+
return [
|
|
69
|
+
"caption",
|
|
70
|
+
"open",
|
|
71
|
+
"close-aria-label",
|
|
72
|
+
"hide-close-button",
|
|
73
|
+
"prevent-close"
|
|
74
|
+
];
|
|
63
75
|
}
|
|
64
76
|
attributeChangedCallback(name, oldVal, newVal) {
|
|
65
77
|
if (isAttrEqual(oldVal, newVal)) {
|
|
@@ -86,6 +98,10 @@ class Dialog extends NectaryElement {
|
|
|
86
98
|
updateAttribute(this.#$closeButton, "aria-label", newVal);
|
|
87
99
|
break;
|
|
88
100
|
}
|
|
101
|
+
case "hide-close-button": {
|
|
102
|
+
updateBooleanAttribute(this, name, isAttrTrue(newVal));
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
89
105
|
}
|
|
90
106
|
}
|
|
91
107
|
set caption(caption) {
|
|
@@ -100,6 +116,18 @@ class Dialog extends NectaryElement {
|
|
|
100
116
|
get open() {
|
|
101
117
|
return getBooleanAttribute(this, "open");
|
|
102
118
|
}
|
|
119
|
+
set hideCloseButton(isHidden) {
|
|
120
|
+
updateBooleanAttribute(this, "hide-close-button", isHidden);
|
|
121
|
+
}
|
|
122
|
+
get hideCloseButton() {
|
|
123
|
+
return getBooleanAttribute(this, "hide-close-button");
|
|
124
|
+
}
|
|
125
|
+
set preventClose(isPrevented) {
|
|
126
|
+
updateBooleanAttribute(this, "prevent-close", isPrevented);
|
|
127
|
+
}
|
|
128
|
+
get preventClose() {
|
|
129
|
+
return getBooleanAttribute(this, "prevent-close");
|
|
130
|
+
}
|
|
103
131
|
get dialogRect() {
|
|
104
132
|
return getRect(this.#$dialog);
|
|
105
133
|
}
|
|
@@ -107,6 +135,12 @@ class Dialog extends NectaryElement {
|
|
|
107
135
|
return getRect(this.#$closeButton);
|
|
108
136
|
}
|
|
109
137
|
#onCancel = (e) => {
|
|
138
|
+
if (this.preventClose) {
|
|
139
|
+
e.preventDefault();
|
|
140
|
+
e.stopPropagation();
|
|
141
|
+
this.#dispatchDismissBlockedEvent("escape");
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
110
144
|
if (e.cancelable) {
|
|
111
145
|
e.preventDefault();
|
|
112
146
|
} else {
|
|
@@ -115,7 +149,22 @@ class Dialog extends NectaryElement {
|
|
|
115
149
|
e.stopPropagation();
|
|
116
150
|
this.#dispatchCloseEvent("escape", e.cancelable);
|
|
117
151
|
};
|
|
152
|
+
#onDialogClose = () => {
|
|
153
|
+
if (!this.isDomConnected || !this.preventClose || !this.open) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
if (!this.#$dialog.open) {
|
|
157
|
+
this.#$dialog.showModal();
|
|
158
|
+
if (!isScrollDisabled()) {
|
|
159
|
+
disableScroll();
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
};
|
|
118
163
|
#onCloseClick = () => {
|
|
164
|
+
if (this.preventClose) {
|
|
165
|
+
this.#dispatchDismissBlockedEvent("close");
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
119
168
|
this.#dispatchCloseEvent("close", true);
|
|
120
169
|
};
|
|
121
170
|
#onBackdropMouseDown = (e) => {
|
|
@@ -123,6 +172,11 @@ class Dialog extends NectaryElement {
|
|
|
123
172
|
const rect = this.dialogRect;
|
|
124
173
|
const isInside = e.x >= rect.x && e.x < rect.x + rect.width && e.y >= rect.y && e.y < rect.y + rect.height;
|
|
125
174
|
if (!isInside) {
|
|
175
|
+
if (this.preventClose) {
|
|
176
|
+
e.stopPropagation();
|
|
177
|
+
this.#dispatchDismissBlockedEvent("backdrop");
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
126
180
|
e.stopPropagation();
|
|
127
181
|
this.#dispatchCloseEvent("backdrop", e.cancelable);
|
|
128
182
|
}
|
|
@@ -132,9 +186,16 @@ class Dialog extends NectaryElement {
|
|
|
132
186
|
getReactEventHandler(this, "on-close")?.(e);
|
|
133
187
|
getReactEventHandler(this, "onClose")?.(e);
|
|
134
188
|
};
|
|
189
|
+
#onDismissBlockedReactHandler = (e) => {
|
|
190
|
+
getReactEventHandler(this, "on-dismiss-blocked")?.(e);
|
|
191
|
+
getReactEventHandler(this, "onDismissBlocked")?.(e);
|
|
192
|
+
};
|
|
135
193
|
#dispatchCloseEvent(detail, cancelable) {
|
|
136
194
|
this.dispatchEvent(new CustomEvent("-close", { detail, cancelable }));
|
|
137
195
|
}
|
|
196
|
+
#dispatchDismissBlockedEvent(detail) {
|
|
197
|
+
this.dispatchEvent(new CustomEvent("-dismiss-blocked", { detail }));
|
|
198
|
+
}
|
|
138
199
|
#onExpand() {
|
|
139
200
|
if (!this.isDomConnected || this.#$dialog.open || !this.open) {
|
|
140
201
|
return;
|
package/dialog/types.d.ts
CHANGED
|
@@ -9,12 +9,18 @@ export type TSinchDialogProps = {
|
|
|
9
9
|
'aria-label': string;
|
|
10
10
|
/** Close button label that is used for a11y */
|
|
11
11
|
'close-aria-label': string;
|
|
12
|
+
/** Hides the close button when set */
|
|
13
|
+
'hide-close-button'?: boolean;
|
|
14
|
+
/** Prevents closing via close button, backdrop, or Escape key. You should provide an explicit action to close the dialog if using this. */
|
|
15
|
+
'prevent-close'?: boolean;
|
|
12
16
|
readonly dialogRect?: TRect;
|
|
13
17
|
readonly closeButtonRect?: TRect;
|
|
14
18
|
};
|
|
15
19
|
export type TSinchDialogEvents = {
|
|
16
20
|
/** close event handler */
|
|
17
21
|
'-close'?: (e: CustomEvent<TSinchDialogCloseDetail>) => void;
|
|
22
|
+
/** Dismiss blocked event handler (when prevent-close is true). Consumers can use this to provide inline guidance or a polite live-region announcement for instance. */
|
|
23
|
+
'-dismiss-blocked'?: (e: CustomEvent<TSinchDialogCloseDetail>) => void;
|
|
18
24
|
};
|
|
19
25
|
export type TSinchDialogStyle = {
|
|
20
26
|
'--sinch-comp-dialog-max-width'?: string;
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@ import "../dialog/index.js";
|
|
|
2
2
|
import { updateAttribute, getAttribute, updateBooleanAttribute, getBooleanAttribute, isAttrTrue } from "../utils/dom.js";
|
|
3
3
|
import { defineCustomElement, NectaryElement } from "../utils/element.js";
|
|
4
4
|
import { getReactEventHandler } from "../utils/get-react-event-handler.js";
|
|
5
|
-
const templateHTML = '<style>#persisted-dialog{--sinch-dialog-close-button-display:none}::slotted(*){display:block}</style><sinch-dialog id="persisted-dialog"><div slot="icon"><slot id="icon" name="icon"></slot></div><div slot="content"><slot name="content"></slot></div><div slot="buttons"><slot name="buttons"></slot></div></sinch-dialog>';
|
|
5
|
+
const templateHTML = '<style>#persisted-dialog{--sinch-dialog-close-button-display:none}::slotted(*){display:block}</style><sinch-dialog id="persisted-dialog" prevent-close><div slot="icon"><slot id="icon" name="icon"></slot></div><div slot="content"><slot name="content"></slot></div><div slot="buttons"><slot name="buttons"></slot></div></sinch-dialog>';
|
|
6
6
|
const template = document.createElement("template");
|
|
7
7
|
template.innerHTML = templateHTML;
|
|
8
8
|
function isVisible(elementStyle) {
|
|
@@ -55,6 +55,8 @@ class PersistentOverlay extends NectaryElement {
|
|
|
55
55
|
this.#controller = new AbortController();
|
|
56
56
|
const { signal } = this.#controller;
|
|
57
57
|
this.addEventListener("-visibility-altered", this.#onVisibilityAlteredReactHandler, { signal });
|
|
58
|
+
this.addEventListener("-dismiss-blocked", this.#onDismissBlockedReactHandler, { signal });
|
|
59
|
+
this.#$sinchDialog.addEventListener("-dismiss-blocked", this.#onDismissBlocked, { signal });
|
|
58
60
|
if (this.open) {
|
|
59
61
|
this.#startObservingAlteration();
|
|
60
62
|
}
|
|
@@ -90,10 +92,22 @@ class PersistentOverlay extends NectaryElement {
|
|
|
90
92
|
#onVisibilityAltered() {
|
|
91
93
|
this.dispatchEvent(new CustomEvent("-visibility-altered"));
|
|
92
94
|
}
|
|
95
|
+
#onDismissBlocked = (e) => {
|
|
96
|
+
this.#dispatchDismissBlockedEvent(
|
|
97
|
+
e.detail
|
|
98
|
+
);
|
|
99
|
+
};
|
|
93
100
|
#onVisibilityAlteredReactHandler = (e) => {
|
|
94
101
|
getReactEventHandler(this, "on-visibility-altered")?.(e);
|
|
95
102
|
getReactEventHandler(this, "onVisibilityAltered")?.(e);
|
|
96
103
|
};
|
|
104
|
+
#onDismissBlockedReactHandler = (e) => {
|
|
105
|
+
getReactEventHandler(this, "on-dismiss-blocked")?.(e);
|
|
106
|
+
getReactEventHandler(this, "onDismissBlocked")?.(e);
|
|
107
|
+
};
|
|
108
|
+
#dispatchDismissBlockedEvent(detail) {
|
|
109
|
+
this.dispatchEvent(new CustomEvent("-dismiss-blocked", { detail }));
|
|
110
|
+
}
|
|
97
111
|
}
|
|
98
112
|
defineCustomElement("sinch-persistent-overlay", PersistentOverlay);
|
|
99
113
|
export {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { TSinchDialogProps } from '../dialog/types';
|
|
1
|
+
import type { TSinchDialogCloseDetail, TSinchDialogProps } from '../dialog/types';
|
|
2
2
|
import type { NectaryComponentReactByType, NectaryComponentVanillaByType, NectaryComponentReact, NectaryComponentVanilla } from '../types';
|
|
3
3
|
export type TSinchPersistentOverlayProps = {
|
|
4
4
|
/** Controls whether the dialog should be open */
|
|
@@ -10,7 +10,9 @@ export type TSinchPersistentOverlayProps = {
|
|
|
10
10
|
};
|
|
11
11
|
export type TSinchPersistentOverlayEvents = {
|
|
12
12
|
/** visibility altered event handler */
|
|
13
|
-
'-visibility-altered'
|
|
13
|
+
'-visibility-altered'?: (e: CustomEvent) => void;
|
|
14
|
+
/** Forwarded when the wrapped dialog blocks a dismissal attempt. Consumers can use this to provide inline guidance or a polite live-region announcement. */
|
|
15
|
+
'-dismiss-blocked'?: (e: CustomEvent<TSinchDialogCloseDetail>) => void;
|
|
14
16
|
};
|
|
15
17
|
export type TSinchPersistentOverlayStyle = {
|
|
16
18
|
'--sinch-dialog-close-button-display'?: string;
|
package/time-picker/index.d.ts
CHANGED
|
@@ -17,6 +17,8 @@ export declare class TimePicker extends NectaryElement {
|
|
|
17
17
|
get ampm(): boolean;
|
|
18
18
|
set submitAriaLabel(value: string);
|
|
19
19
|
get submitAriaLabel(): string;
|
|
20
|
+
set noSubmit(value: boolean);
|
|
21
|
+
get noSubmit(): boolean;
|
|
20
22
|
get submitButtonRect(): TRect;
|
|
21
23
|
get amButtonRect(): TRect | null;
|
|
22
24
|
get pmButtonRect(): TRect | null;
|
package/time-picker/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import "../icon/index.js";
|
|
2
2
|
import "../segmented-control/index.js";
|
|
3
3
|
import "../segmented-control-option/index.js";
|
|
4
|
-
import { isAttrEqual,
|
|
4
|
+
import { isAttrEqual, updateBooleanAttribute, updateAttribute, getAttribute, getBooleanAttribute, setClass, isAttrTrue } from "../utils/dom.js";
|
|
5
5
|
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";
|
|
@@ -119,13 +119,14 @@ class TimePicker extends NectaryElement {
|
|
|
119
119
|
this.#$needleHour.addEventListener("keydown", this.#onHoursKeydown, options);
|
|
120
120
|
this.#$needleMinute.addEventListener("keydown", this.#onMinutesKeydown, options);
|
|
121
121
|
this.addEventListener("-change", this.#onChangeReactHandler, options);
|
|
122
|
+
this.addEventListener("-submit", this.#onSubmitReactHandler, options);
|
|
122
123
|
}
|
|
123
124
|
disconnectedCallback() {
|
|
124
125
|
this.#controller.abort();
|
|
125
126
|
this.#controller = null;
|
|
126
127
|
}
|
|
127
128
|
static get observedAttributes() {
|
|
128
|
-
return ["value", "ampm", "submit-aria-label"];
|
|
129
|
+
return ["value", "ampm", "submit-aria-label", "no-submit"];
|
|
129
130
|
}
|
|
130
131
|
attributeChangedCallback(name, prevValue, newVal) {
|
|
131
132
|
if (isAttrEqual(prevValue, newVal)) {
|
|
@@ -151,6 +152,12 @@ class TimePicker extends NectaryElement {
|
|
|
151
152
|
updateAttribute(this.#$submitButton, "aria-label", newVal);
|
|
152
153
|
break;
|
|
153
154
|
}
|
|
155
|
+
case "no-submit": {
|
|
156
|
+
const isNoSubmit = isAttrTrue(newVal);
|
|
157
|
+
updateBooleanAttribute(this, "no-submit", isNoSubmit);
|
|
158
|
+
this.#render();
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
154
161
|
}
|
|
155
162
|
}
|
|
156
163
|
set value(value) {
|
|
@@ -171,6 +178,12 @@ class TimePicker extends NectaryElement {
|
|
|
171
178
|
get submitAriaLabel() {
|
|
172
179
|
return getAttribute(this, "submit-aria-label", "");
|
|
173
180
|
}
|
|
181
|
+
set noSubmit(value) {
|
|
182
|
+
updateBooleanAttribute(this, "no-submit", value);
|
|
183
|
+
}
|
|
184
|
+
get noSubmit() {
|
|
185
|
+
return getBooleanAttribute(this, "no-submit");
|
|
186
|
+
}
|
|
174
187
|
get submitButtonRect() {
|
|
175
188
|
return getRect(this.#$submitButton);
|
|
176
189
|
}
|
|
@@ -237,6 +250,7 @@ class TimePicker extends NectaryElement {
|
|
|
237
250
|
this.#$headerMinutes.focus();
|
|
238
251
|
}
|
|
239
252
|
this.#render();
|
|
253
|
+
this.#onChange();
|
|
240
254
|
};
|
|
241
255
|
#render() {
|
|
242
256
|
const is24 = getBooleanAttribute(this, "ampm") === false;
|
|
@@ -244,6 +258,7 @@ class TimePicker extends NectaryElement {
|
|
|
244
258
|
this.#selectMinute();
|
|
245
259
|
this.#$headerHoursText.textContent = stringifyHour(this.#hour, is24);
|
|
246
260
|
this.#$headerMinutesText.textContent = stringifyMinute(this.#minute);
|
|
261
|
+
this.#$submitButton.style.display = this.noSubmit ? "none" : "";
|
|
247
262
|
updateAttribute(this.#$headerHours, "aria-valuenow", this.#hour);
|
|
248
263
|
updateAttribute(this.#$headerHours, "aria-valuetext", this.#hour);
|
|
249
264
|
updateAttribute(this.#$headerMinutes, "aria-valuenow", this.#minute);
|
|
@@ -293,6 +308,7 @@ class TimePicker extends NectaryElement {
|
|
|
293
308
|
if (this.#hour >= 12) {
|
|
294
309
|
this.#hour -= 12;
|
|
295
310
|
this.#render();
|
|
311
|
+
this.#onChange();
|
|
296
312
|
}
|
|
297
313
|
break;
|
|
298
314
|
}
|
|
@@ -300,31 +316,41 @@ class TimePicker extends NectaryElement {
|
|
|
300
316
|
if (this.#hour < 12) {
|
|
301
317
|
this.#hour += 12;
|
|
302
318
|
this.#render();
|
|
319
|
+
this.#onChange();
|
|
303
320
|
}
|
|
304
321
|
break;
|
|
305
322
|
}
|
|
306
323
|
}
|
|
307
324
|
};
|
|
308
|
-
#
|
|
325
|
+
#onEventEmit = (eventName) => {
|
|
309
326
|
const value = stringifyTime(this.#hour, this.#minute);
|
|
310
327
|
this.dispatchEvent(
|
|
311
|
-
new CustomEvent(
|
|
328
|
+
new CustomEvent(eventName, {
|
|
312
329
|
detail: value
|
|
313
330
|
})
|
|
314
331
|
);
|
|
315
332
|
};
|
|
333
|
+
#onChange = () => {
|
|
334
|
+
this.#onEventEmit("-change");
|
|
335
|
+
};
|
|
336
|
+
#onSubmitButtonClick = () => {
|
|
337
|
+
this.#onEventEmit("-submit");
|
|
338
|
+
this.#onEventEmit("-change");
|
|
339
|
+
};
|
|
316
340
|
#onHoursKeydown = (e) => {
|
|
317
341
|
switch (e.key) {
|
|
318
342
|
case "ArrowUp": {
|
|
319
343
|
e.preventDefault();
|
|
320
344
|
this.#hour = (this.#hour + 1) % 24;
|
|
321
345
|
this.#render();
|
|
346
|
+
this.#onChange();
|
|
322
347
|
break;
|
|
323
348
|
}
|
|
324
349
|
case "ArrowDown": {
|
|
325
350
|
e.preventDefault();
|
|
326
351
|
this.#hour = (this.#hour + 23) % 24;
|
|
327
352
|
this.#render();
|
|
353
|
+
this.#onChange();
|
|
328
354
|
break;
|
|
329
355
|
}
|
|
330
356
|
}
|
|
@@ -335,12 +361,14 @@ class TimePicker extends NectaryElement {
|
|
|
335
361
|
e.preventDefault();
|
|
336
362
|
this.#minute = (this.#minute + 1) % 60;
|
|
337
363
|
this.#render();
|
|
364
|
+
this.#onChange();
|
|
338
365
|
break;
|
|
339
366
|
}
|
|
340
367
|
case "ArrowDown": {
|
|
341
368
|
e.preventDefault();
|
|
342
369
|
this.#minute = (this.#minute + 59) % 60;
|
|
343
370
|
this.#render();
|
|
371
|
+
this.#onChange();
|
|
344
372
|
break;
|
|
345
373
|
}
|
|
346
374
|
}
|
|
@@ -349,13 +377,19 @@ class TimePicker extends NectaryElement {
|
|
|
349
377
|
getReactEventHandler(this, "on-change")?.(e);
|
|
350
378
|
getReactEventHandler(this, "onChange")?.(e);
|
|
351
379
|
};
|
|
380
|
+
#onSubmitReactHandler = (e) => {
|
|
381
|
+
getReactEventHandler(this, "on-submit")?.(e);
|
|
382
|
+
getReactEventHandler(this, "onSubmit")?.(e);
|
|
383
|
+
};
|
|
352
384
|
#onHourDigitClick = (hour) => {
|
|
353
385
|
this.#hour = hour;
|
|
354
386
|
this.#render();
|
|
387
|
+
this.#onChange();
|
|
355
388
|
};
|
|
356
389
|
#onMinuteDigitClick = (minute) => {
|
|
357
390
|
this.#minute = minute;
|
|
358
391
|
this.#render();
|
|
392
|
+
this.#onChange();
|
|
359
393
|
};
|
|
360
394
|
#onDigitKeydown = (e, callback) => {
|
|
361
395
|
if (e.key === "Enter" || e.key === " ") {
|
package/time-picker/types.d.ts
CHANGED
|
@@ -6,6 +6,8 @@ export type TSinchTimePickerProps = {
|
|
|
6
6
|
ampm?: boolean;
|
|
7
7
|
/** Label that is used for a11y */
|
|
8
8
|
'aria-label': string;
|
|
9
|
+
/** Hide submit button */
|
|
10
|
+
'no-submit'?: boolean;
|
|
9
11
|
/** Submit button label that is used for a11y */
|
|
10
12
|
'submit-aria-label': string;
|
|
11
13
|
readonly submitButtonRect?: TRect;
|
|
@@ -19,6 +21,8 @@ export type TSinchTimePickerMethods = {
|
|
|
19
21
|
export type TSinchTimePickerEvents = {
|
|
20
22
|
/** Change value handler, return time in ISO 8601 format */
|
|
21
23
|
'-change'?: (e: CustomEvent<string>) => void;
|
|
24
|
+
/** Submit value handler, return time in ISO 8601 format */
|
|
25
|
+
'-submit'?: (e: CustomEvent<string>) => void;
|
|
22
26
|
};
|
|
23
27
|
export type TSinchTimePickerStyle = {
|
|
24
28
|
'--sinch-comp-time-picker-header-font'?: string;
|
package/utils/scroll-lock.d.ts
CHANGED
package/utils/scroll-lock.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const bodyEl = document.body;
|
|
2
|
+
const isScrollDisabled = () => bodyEl.__dialog_counter__ > 0;
|
|
2
3
|
const disableScroll = () => {
|
|
3
4
|
bodyEl.__dialog_counter__ = (bodyEl.__dialog_counter__ ?? 0) + 1;
|
|
4
5
|
if (bodyEl.__dialog_counter__ === 1) {
|
|
@@ -18,5 +19,6 @@ const enableScroll = () => {
|
|
|
18
19
|
};
|
|
19
20
|
export {
|
|
20
21
|
disableScroll,
|
|
21
|
-
enableScroll
|
|
22
|
+
enableScroll,
|
|
23
|
+
isScrollDisabled
|
|
22
24
|
};
|