@nectary/components 1.1.0 → 1.1.1
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/dialog/index.js +54 -30
- package/package.json +1 -1
- package/pop/index.js +10 -8
package/dialog/index.js
CHANGED
|
@@ -2,8 +2,9 @@ import '../icon-button';
|
|
|
2
2
|
import '../icon';
|
|
3
3
|
import '../stop-events';
|
|
4
4
|
import '../title';
|
|
5
|
-
import {
|
|
6
|
-
|
|
5
|
+
import { disableScroll, enableScroll } from '../pop/utils';
|
|
6
|
+
import { defineCustomElement, getAttribute, getBooleanAttribute, getRect, isAttrTrue, updateAttribute, getReactEventHandler, NectaryElement, updateBooleanAttribute, getCssVar, setClass } from '../utils';
|
|
7
|
+
const templateHTML = '<style>:host{display:contents}#dialog{position:fixed;left:0;right:0;margin:auto;display:flex;flex-direction:column;padding:24px 0;max-width:var(--sinch-dialog-max-width,512px);max-height:unset;border-radius:var(--sinch-shape-radius-l);box-sizing:border-box;contain:content;background-color:var(--sinch-color-snow-100);color:var(--sinch-color-text-default);font:var(--sinch-font-text-m);border:none;box-shadow:var(--sinch-elevation-level-3)}#dialog:not([open]){display:none}dialog::backdrop{background-color:#000;opacity:.55}._dialog_overlay{position:fixed;top:0;right:0;bottom:0;left:0}dialog.fixed{position:fixed;top:50%;transform:translate(0,-50%)}#header{display:flex;flex-direction:row;justify-content:space-between;align-items:flex-start;margin-bottom:12px;padding:0 24px}#caption{color:var(--sinch-color-text-default)}#content{min-height:0;overflow:auto;max-height:var(--sinch-dialog-max-height,50vh);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{transform:translate(4px,-4px)}</style><dialog id="dialog"><div id="header"><sinch-title id="caption" type="m" level="3" ellipsis></sinch-title><sinch-icon-button id="close" size="s" tabindex="0"><sinch-icon id="icon-close" slot="icon"></sinch-icon></sinch-icon-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>';
|
|
7
8
|
const template = document.createElement('template');
|
|
8
9
|
template.innerHTML = templateHTML;
|
|
9
10
|
defineCustomElement('sinch-dialog', class extends NectaryElement {
|
|
@@ -11,16 +12,19 @@ defineCustomElement('sinch-dialog', class extends NectaryElement {
|
|
|
11
12
|
#$dialog;
|
|
12
13
|
#$closeButton;
|
|
13
14
|
#$caption;
|
|
15
|
+
#$actionWrapper;
|
|
16
|
+
#$actionSlot;
|
|
14
17
|
#controller = null;
|
|
15
|
-
#prevOverflowValue = '';
|
|
16
18
|
constructor() {
|
|
17
19
|
super();
|
|
18
20
|
const shadowRoot = this.attachShadow();
|
|
19
21
|
shadowRoot.appendChild(template.content.cloneNode(true));
|
|
20
|
-
this.#$dialog = shadowRoot.querySelector('dialog');
|
|
22
|
+
this.#$dialog = shadowRoot.querySelector('#dialog');
|
|
21
23
|
this.#$closeButton = shadowRoot.querySelector('#close');
|
|
22
24
|
this.#$caption = shadowRoot.querySelector('#caption');
|
|
23
25
|
this.#$iconClose = shadowRoot.querySelector('#icon-close');
|
|
26
|
+
this.#$actionWrapper = shadowRoot.querySelector('#action');
|
|
27
|
+
this.#$actionSlot = shadowRoot.querySelector('slot[name="buttons"]');
|
|
24
28
|
}
|
|
25
29
|
connectedCallback() {
|
|
26
30
|
super.connectedCallback();
|
|
@@ -30,16 +34,20 @@ defineCustomElement('sinch-dialog', class extends NectaryElement {
|
|
|
30
34
|
signal: this.#controller.signal
|
|
31
35
|
};
|
|
32
36
|
this.#$closeButton.addEventListener('click', this.#onCloseClick, options);
|
|
33
|
-
this.#$dialog.addEventListener('mousedown', this.#
|
|
37
|
+
this.#$dialog.addEventListener('mousedown', this.#onBackdropMouseDown, options);
|
|
34
38
|
this.#$dialog.addEventListener('cancel', this.#onCancel, options);
|
|
39
|
+
this.#$actionSlot.addEventListener('slotchange', this.#onActionSlotChange, options);
|
|
35
40
|
this.addEventListener('-close', this.#onCloseReactHandler, options);
|
|
36
41
|
updateAttribute(this.#$iconClose, 'name', getCssVar(this, '--sinch-dialog-icon-close'));
|
|
37
|
-
this.#
|
|
42
|
+
this.#onActionSlotChange();
|
|
43
|
+
if (getBooleanAttribute(this, 'open')) {
|
|
44
|
+
this.#onExpand();
|
|
45
|
+
}
|
|
38
46
|
}
|
|
39
47
|
disconnectedCallback() {
|
|
40
48
|
super.disconnectedCallback();
|
|
41
49
|
this.#controller.abort();
|
|
42
|
-
this.#
|
|
50
|
+
this.#onCollapse();
|
|
43
51
|
}
|
|
44
52
|
static get observedAttributes() {
|
|
45
53
|
return ['caption', 'open', 'close-aria-label'];
|
|
@@ -54,7 +62,13 @@ defineCustomElement('sinch-dialog', class extends NectaryElement {
|
|
|
54
62
|
case 'open':
|
|
55
63
|
{
|
|
56
64
|
const shouldOpen = isAttrTrue(newVal);
|
|
57
|
-
|
|
65
|
+
if (shouldOpen) {
|
|
66
|
+
requestAnimationFrame(() => {
|
|
67
|
+
this.#onExpand();
|
|
68
|
+
});
|
|
69
|
+
} else {
|
|
70
|
+
this.#onCollapse();
|
|
71
|
+
}
|
|
58
72
|
updateBooleanAttribute(this, 'open', shouldOpen);
|
|
59
73
|
break;
|
|
60
74
|
}
|
|
@@ -71,21 +85,29 @@ defineCustomElement('sinch-dialog', class extends NectaryElement {
|
|
|
71
85
|
get caption() {
|
|
72
86
|
return getAttribute(this, 'caption', '');
|
|
73
87
|
}
|
|
88
|
+
get dialogRect() {
|
|
89
|
+
return getRect(this.#$dialog);
|
|
90
|
+
}
|
|
91
|
+
get closeButtonRect() {
|
|
92
|
+
return getRect(this.#$closeButton);
|
|
93
|
+
}
|
|
74
94
|
#onCancel = e => {
|
|
75
95
|
e.preventDefault();
|
|
96
|
+
e.stopPropagation();
|
|
76
97
|
this.#dispatchCloseEvent('escape');
|
|
77
98
|
};
|
|
78
99
|
#onCloseClick = () => {
|
|
79
100
|
this.#dispatchCloseEvent('close');
|
|
80
101
|
};
|
|
81
|
-
#
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
102
|
+
#onBackdropMouseDown = e => {
|
|
103
|
+
const tgt = e.originalTarget ?? e.target;
|
|
104
|
+
if (tgt === this.#$dialog) {
|
|
105
|
+
const rect = this.dialogRect;
|
|
106
|
+
const isInside = e.x >= rect.x && e.x < rect.x + rect.width && e.y >= rect.y && e.y < rect.y + rect.height;
|
|
107
|
+
if (!isInside) {
|
|
108
|
+
e.stopPropagation();
|
|
109
|
+
this.#dispatchCloseEvent('backdrop');
|
|
110
|
+
}
|
|
89
111
|
}
|
|
90
112
|
};
|
|
91
113
|
#onCloseReactHandler = e => {
|
|
@@ -97,22 +119,24 @@ defineCustomElement('sinch-dialog', class extends NectaryElement {
|
|
|
97
119
|
detail
|
|
98
120
|
}));
|
|
99
121
|
}
|
|
100
|
-
#
|
|
101
|
-
if (
|
|
102
|
-
|
|
103
|
-
this.#prevOverflowValue = document.body.style.overflow;
|
|
104
|
-
document.body.style.overflow = 'hidden';
|
|
105
|
-
this.#$dialog.showModal();
|
|
106
|
-
}
|
|
107
|
-
} else {
|
|
108
|
-
this.#$dialog.close?.();
|
|
109
|
-
document.body.style.overflow = this.#prevOverflowValue;
|
|
122
|
+
#onExpand() {
|
|
123
|
+
if (!this.isConnected || this.#isOpen()) {
|
|
124
|
+
return;
|
|
110
125
|
}
|
|
126
|
+
this.#$dialog.showModal();
|
|
127
|
+
disableScroll();
|
|
111
128
|
}
|
|
112
|
-
|
|
113
|
-
|
|
129
|
+
#onCollapse() {
|
|
130
|
+
if (!this.#isOpen()) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
this.#$dialog.close?.();
|
|
134
|
+
enableScroll();
|
|
114
135
|
}
|
|
115
|
-
|
|
116
|
-
return
|
|
136
|
+
#isOpen() {
|
|
137
|
+
return getBooleanAttribute(this.#$dialog, 'open');
|
|
117
138
|
}
|
|
139
|
+
#onActionSlotChange = () => {
|
|
140
|
+
setClass(this.#$actionWrapper, 'empty', this.#$actionSlot.assignedElements().length === 0);
|
|
141
|
+
};
|
|
118
142
|
});
|
package/package.json
CHANGED
package/pop/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { defineCustomElement, getBooleanAttribute, getLiteralAttribute, getRect, isAttrTrue, updateLiteralAttribute, getReactEventHandler, updateBooleanAttribute, NectaryElement, throttleAnimationFrame, isElementFocused, updateIntegerAttribute, getIntegerAttribute, getFirstFocusableElement, getFirstSlotElement, Context, subscribeContext } from '../utils';
|
|
2
|
-
const templateHTML = '<style>:host{display:contents;position:relative}dialog{position:fixed;left:0;top:0;margin:0;outline:0;padding:0;border:none;box-sizing:border-box;max-width:unset;max-height:unset;z-index:1;background:0 0;overflow:visible}dialog:not([open]){display:none}dialog::backdrop{background-color:transparent}dialog+.backdrop{position:fixed;top:0;right:0;bottom:0;left:0;background-color:transparent}dialog.fixed{position:fixed;top:50%;transform:translate(0,-50%)}._dialog_overlay{position:fixed;top:0;right:0;bottom:0;left:0}#content{position:relative;z-index:1}#target-open{display:flex;flex-direction:column;position:absolute;left:0;top:0}#focus{display:none;position:absolute;width:0;height:0}</style><slot id="target" aria-haspopup="dialog" aria-expanded="false"
|
|
2
|
+
const templateHTML = '<style>:host{display:contents;position:relative}dialog{position:fixed;left:0;top:0;margin:0;outline:0;padding:0;border:none;box-sizing:border-box;max-width:unset;max-height:unset;z-index:1;background:0 0;overflow:visible}dialog:not([open]){display:none}dialog::backdrop{background-color:transparent}dialog+.backdrop{position:fixed;top:0;right:0;bottom:0;left:0;background-color:transparent}dialog.fixed{position:fixed;top:50%;transform:translate(0,-50%)}._dialog_overlay{position:fixed;top:0;right:0;bottom:0;left:0}#content{position:relative;z-index:1}#target-open{display:flex;flex-direction:column;position:absolute;left:0;top:0}#focus{display:none;position:absolute;width:0;height:0}</style><slot id="target" name="target" aria-haspopup="dialog" aria-expanded="false"></slot><div id="focus" tabindex="-1"></div><dialog id="dialog"><div id="content"><slot name="content"></slot></div><div id="target-open"><slot name="target-open"></slot></div></dialog>';
|
|
3
3
|
import { assertOrientation, disableScroll, enableScroll, orientationValues } from './utils';
|
|
4
4
|
const template = document.createElement('template');
|
|
5
5
|
template.innerHTML = templateHTML;
|
|
@@ -299,17 +299,19 @@ defineCustomElement('sinch-pop', class extends NectaryElement {
|
|
|
299
299
|
}
|
|
300
300
|
};
|
|
301
301
|
#onBackdropMouseDown = e => {
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
302
|
+
const tgt = e.originalTarget ?? e.target;
|
|
303
|
+
if (tgt === this.#$dialog) {
|
|
304
|
+
const rect = this.popoverRect;
|
|
305
|
+
const isInside = e.x >= rect.x && e.x < rect.x + rect.width && e.y >= rect.y && e.y < rect.y + rect.height;
|
|
306
|
+
if (!isInside) {
|
|
307
|
+
e.stopPropagation();
|
|
308
|
+
this.#dispatchCloseEvent();
|
|
309
|
+
}
|
|
309
310
|
}
|
|
310
311
|
};
|
|
311
312
|
#onCancel = e => {
|
|
312
313
|
e.preventDefault();
|
|
314
|
+
e.stopPropagation();
|
|
313
315
|
this.#dispatchCloseEvent();
|
|
314
316
|
};
|
|
315
317
|
#onCloseReactHandler = e => {
|