@nectary/components 1.1.2 → 1.2.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/card/index.js +70 -6
- package/card/types.d.ts +6 -1
- package/date-picker/index.js +2 -2
- package/date-picker/utils.js +18 -17
- package/package.json +1 -1
package/card/index.js
CHANGED
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
import '../title';
|
|
2
2
|
import '../text';
|
|
3
|
-
import { defineCustomElement, getBooleanAttribute, getAttribute, updateBooleanAttribute, updateAttribute, setClass, NectaryElement } from '../utils';
|
|
4
|
-
const templateHTML = '<style>:host{display:block;outline:0}#wrapper{border-radius:var(--sinch-shape-radius-l);border:1px solid var(--sinch-color-snow-700);background-color:var(--sinch-color-snow-100);box-shadow:var(--sinch-elevation-level-0);overflow:hidden}:host(:hover) #wrapper{box-shadow:var(--sinch-elevation-level-1)}#card-body{padding:24px;box-sizing:border-box;display:flex;flex-direction:column;gap:16px}#illustration-wrapper{display:none;overflow:hidden;height:240px}#illustration-wrapper.active{display:block}#label{color:var(--sinch-color-stormy-300)}#label:empty{display:none}#header{display:flex;flex-direction:row;align-items:center;gap:8px;--sinch-size-icon:48px}#caption{color:var(--sinch-color-stormy-500);flex:1;min-width:0}#description{color:var(--sinch-color-stormy-500);flex:1;min-height:0;max-height:120px;overflow-y:auto}#description:empty{display:none}:host([disabled]:not([disabled=false])) :is(#illustration-wrapper,#description,#header,#label){opacity:.6}slot[name=action]::slotted(*){margin-top:20px;align-self:flex-start;max-width:100%}</style><div id="wrapper"><div id="illustration-wrapper"><slot name="illustration"></slot></div><div id="card-body"><sinch-text id="label" type="s" emphasized ellipsis></sinch-text><div id="header"><slot name="icon"></slot><sinch-title id="caption" type="m" level="3" ellipsis></sinch-title></div><sinch-text id="description" type="m"></sinch-text><slot name="action"></slot></div></div>';
|
|
3
|
+
import { defineCustomElement, getBooleanAttribute, getAttribute, updateBooleanAttribute, updateAttribute, setClass, NectaryElement, isAttrTrue, getRect } from '../utils';
|
|
4
|
+
const templateHTML = '<style>:host{display:block;outline:0}#wrapper{border-radius:var(--sinch-shape-radius-l);border:1px solid var(--sinch-color-snow-700);background-color:var(--sinch-color-snow-100);box-shadow:var(--sinch-elevation-level-0);overflow:hidden}:host(:hover) #wrapper{box-shadow:var(--sinch-elevation-level-1)}#card-body{padding:24px;box-sizing:border-box;display:flex;flex-direction:column;gap:16px}#illustration-wrapper{display:none;overflow:hidden;height:240px}#illustration-wrapper.active{display:block}#label{color:var(--sinch-color-stormy-300)}#label:empty{display:none}#header{display:flex;flex-direction:row;align-items:center;gap:8px;--sinch-size-icon:48px}#caption{color:var(--sinch-color-stormy-500);flex:1;min-width:0}#description{color:var(--sinch-color-stormy-500);flex:1;min-height:0;max-height:120px;overflow-y:auto}#description:empty{display:none}:host([disabled]:not([disabled=false])) :is(#illustration-wrapper,#description,#header,#label){opacity:.6}slot[name=action]::slotted(*){margin-top:20px;align-self:flex-start;max-width:100%}:host([draggable=true]) #header{cursor:grab}</style><div id="wrapper"><div id="illustration-wrapper"><slot name="illustration"></slot></div><div id="card-body"><sinch-text id="label" type="s" emphasized ellipsis></sinch-text><div id="header"><slot name="icon"></slot><sinch-title id="caption" type="m" level="3" ellipsis></sinch-title></div><sinch-text id="description" type="m"></sinch-text><slot name="action"></slot></div></div>';
|
|
5
5
|
const template = document.createElement('template');
|
|
6
6
|
template.innerHTML = templateHTML;
|
|
7
7
|
defineCustomElement('sinch-card', class extends NectaryElement {
|
|
8
8
|
#$text;
|
|
9
9
|
#$label;
|
|
10
10
|
#$caption;
|
|
11
|
+
#$header;
|
|
12
|
+
#$cardBody;
|
|
13
|
+
#$iconSlot;
|
|
11
14
|
#$illustrationSlot;
|
|
12
15
|
#$illustrationSlotWrapper;
|
|
16
|
+
#isDraggingCorrectHandle = false;
|
|
17
|
+
#isDraggingSubscribed = false;
|
|
18
|
+
#controller = null;
|
|
13
19
|
constructor() {
|
|
14
20
|
super();
|
|
15
21
|
const shadowRoot = this.attachShadow();
|
|
@@ -17,20 +23,36 @@ defineCustomElement('sinch-card', class extends NectaryElement {
|
|
|
17
23
|
this.#$text = shadowRoot.querySelector('#description');
|
|
18
24
|
this.#$label = shadowRoot.querySelector('#label');
|
|
19
25
|
this.#$caption = shadowRoot.querySelector('#caption');
|
|
26
|
+
this.#$header = shadowRoot.querySelector('#header');
|
|
27
|
+
this.#$cardBody = shadowRoot.querySelector('#wrapper');
|
|
28
|
+
this.#$iconSlot = shadowRoot.querySelector('slot[name="icon"]');
|
|
20
29
|
this.#$illustrationSlot = shadowRoot.querySelector('slot[name="illustration"]');
|
|
21
30
|
this.#$illustrationSlotWrapper = shadowRoot.querySelector('#illustration-wrapper');
|
|
22
31
|
}
|
|
23
32
|
connectedCallback() {
|
|
24
|
-
|
|
33
|
+
super.connectedCallback();
|
|
34
|
+
this.#controller = new AbortController();
|
|
35
|
+
const options = {
|
|
36
|
+
signal: this.#controller.signal
|
|
37
|
+
};
|
|
38
|
+
this.#$illustrationSlot.addEventListener('slotchange', this.#onIllustrationSlotChange, options);
|
|
25
39
|
this.#onIllustrationSlotChange();
|
|
40
|
+
if (getBooleanAttribute(this, 'draggable')) {
|
|
41
|
+
this.#enableDraggable();
|
|
42
|
+
}
|
|
26
43
|
}
|
|
27
44
|
disconnectedCallback() {
|
|
28
|
-
|
|
45
|
+
super.disconnectedCallback();
|
|
46
|
+
this.#disableDragging();
|
|
47
|
+
this.#controller.abort();
|
|
29
48
|
}
|
|
30
49
|
static get observedAttributes() {
|
|
31
|
-
return ['text', 'label', 'caption', 'disabled'];
|
|
50
|
+
return ['text', 'label', 'caption', 'disabled', 'draggable'];
|
|
32
51
|
}
|
|
33
|
-
attributeChangedCallback(name,
|
|
52
|
+
attributeChangedCallback(name, prevVal, newVal) {
|
|
53
|
+
if (prevVal === newVal) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
34
56
|
switch (name) {
|
|
35
57
|
case 'text':
|
|
36
58
|
{
|
|
@@ -47,6 +69,22 @@ defineCustomElement('sinch-card', class extends NectaryElement {
|
|
|
47
69
|
updateAttribute(this.#$caption, 'text', newVal);
|
|
48
70
|
break;
|
|
49
71
|
}
|
|
72
|
+
case 'disabled':
|
|
73
|
+
{
|
|
74
|
+
updateBooleanAttribute(this, 'disabled', isAttrTrue(newVal));
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
case 'draggable':
|
|
78
|
+
{
|
|
79
|
+
const isDraggingEnabled = isAttrTrue(newVal);
|
|
80
|
+
if (isDraggingEnabled) {
|
|
81
|
+
this.#enableDraggable();
|
|
82
|
+
} else {
|
|
83
|
+
this.#disableDragging();
|
|
84
|
+
this.removeAttribute('draggable');
|
|
85
|
+
}
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
50
88
|
}
|
|
51
89
|
}
|
|
52
90
|
set text(value) {
|
|
@@ -73,7 +111,33 @@ defineCustomElement('sinch-card', class extends NectaryElement {
|
|
|
73
111
|
get disabled() {
|
|
74
112
|
return getBooleanAttribute(this, 'disabled');
|
|
75
113
|
}
|
|
114
|
+
get dragRect() {
|
|
115
|
+
return this.#isDraggingSubscribed ? getRect(this.#$header) : null;
|
|
116
|
+
}
|
|
76
117
|
#onIllustrationSlotChange = () => {
|
|
77
118
|
setClass(this.#$illustrationSlotWrapper, 'active', this.#$illustrationSlot.assignedElements().length > 0);
|
|
78
119
|
};
|
|
120
|
+
#enableDraggable() {
|
|
121
|
+
if (this.isConnected && !this.#isDraggingSubscribed) {
|
|
122
|
+
this.addEventListener('dragstart', this.#onDragStart);
|
|
123
|
+
this.#$cardBody.addEventListener('mousedown', this.#onDraggableMouseDown);
|
|
124
|
+
this.#isDraggingSubscribed = true;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
#disableDragging() {
|
|
128
|
+
if (this.#isDraggingSubscribed) {
|
|
129
|
+
this.removeEventListener('dragstart', this.#onDragStart);
|
|
130
|
+
this.#$cardBody.removeEventListener('mousedown', this.#onDraggableMouseDown);
|
|
131
|
+
this.#isDraggingSubscribed = false;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
#onDraggableMouseDown = e => {
|
|
135
|
+
this.#isDraggingCorrectHandle = this.#$header.contains(e.target) || this.#$iconSlot.assignedElements().includes(e.target);
|
|
136
|
+
};
|
|
137
|
+
#onDragStart = e => {
|
|
138
|
+
if (!this.#isDraggingCorrectHandle) {
|
|
139
|
+
e.preventDefault();
|
|
140
|
+
e.stopPropagation();
|
|
141
|
+
}
|
|
142
|
+
};
|
|
79
143
|
});
|
package/card/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { TSinchElementReact } from '../types';
|
|
1
|
+
import type { TRect, TSinchElementReact } from '../types';
|
|
2
2
|
export type TSinchCardElement = HTMLElement & {
|
|
3
3
|
/** Text */
|
|
4
4
|
text: string;
|
|
@@ -8,6 +8,7 @@ export type TSinchCardElement = HTMLElement & {
|
|
|
8
8
|
label: string | null;
|
|
9
9
|
/** Disabled */
|
|
10
10
|
disabled: boolean;
|
|
11
|
+
readonly dragRect: TRect | null;
|
|
11
12
|
/** Text */
|
|
12
13
|
setAttribute(name: 'text', value: string): void;
|
|
13
14
|
/** Caption */
|
|
@@ -16,6 +17,8 @@ export type TSinchCardElement = HTMLElement & {
|
|
|
16
17
|
setAttribute(name: 'label', value: string): void;
|
|
17
18
|
/** Disabled */
|
|
18
19
|
setAttribute(name: 'disabled', value: ''): void;
|
|
20
|
+
/** Draggable */
|
|
21
|
+
setAttribute(name: 'draggable', value: ''): void;
|
|
19
22
|
};
|
|
20
23
|
export type TSinchCardReact = TSinchElementReact<TSinchCardElement> & {
|
|
21
24
|
/** Text */
|
|
@@ -26,4 +29,6 @@ export type TSinchCardReact = TSinchElementReact<TSinchCardElement> & {
|
|
|
26
29
|
label?: string;
|
|
27
30
|
/** Disabled */
|
|
28
31
|
disabled?: boolean;
|
|
32
|
+
/** Draggable */
|
|
33
|
+
draggable?: boolean;
|
|
29
34
|
};
|
package/date-picker/index.js
CHANGED
|
@@ -365,7 +365,7 @@ defineCustomElement('sinch-date-picker', class extends NectaryElement {
|
|
|
365
365
|
updateBooleanAttribute(this.#$nextMonth, 'disabled', canGoNextMonth(this.#uiDate, this.#maxDate) === false);
|
|
366
366
|
updateBooleanAttribute(this.#$prevYear, 'disabled', canGoPrevYear(this.#uiDate, this.#minDate) === false);
|
|
367
367
|
updateBooleanAttribute(this.#$nextYear, 'disabled', canGoNextYear(this.#uiDate, this.#maxDate) === false);
|
|
368
|
-
this.#$date.textContent = `${this.#monthNames[this.#uiDate.
|
|
368
|
+
this.#$date.textContent = `${this.#monthNames[this.#uiDate.getUTCMonth()]} ${this.#uiDate.getUTCFullYear()}`;
|
|
369
369
|
for (let wi = 0; wi < this.#$days.length; wi++) {
|
|
370
370
|
const $week = this.#$days[wi];
|
|
371
371
|
let isEmptyWeek = true;
|
|
@@ -383,7 +383,7 @@ defineCustomElement('sinch-date-picker', class extends NectaryElement {
|
|
|
383
383
|
$day.setAttribute('aria-hidden', 'true');
|
|
384
384
|
} else {
|
|
385
385
|
const dayIso = dateToIso(day);
|
|
386
|
-
$day.textContent = day.
|
|
386
|
+
$day.textContent = day.getUTCDate().toString();
|
|
387
387
|
$day.setAttribute('data-date', dayIso);
|
|
388
388
|
if (isDateBetween(day, this.#minDate, this.#maxDate)) {
|
|
389
389
|
$day.removeAttribute('disabled');
|
package/date-picker/utils.js
CHANGED
|
@@ -9,11 +9,11 @@ export const getCalendarMonth = (date, options) => {
|
|
|
9
9
|
firstDayOfWeek: 1,
|
|
10
10
|
...options
|
|
11
11
|
};
|
|
12
|
-
const firstDateOfMonth = new Date(date.
|
|
13
|
-
const lastDateOfMonth = new Date(date.
|
|
14
|
-
const firstWeekdayOfMonth = firstDateOfMonth.
|
|
15
|
-
const lastWeekdayOfMonth = lastDateOfMonth.
|
|
16
|
-
const daysInMonth = lastDateOfMonth.
|
|
12
|
+
const firstDateOfMonth = new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), 1));
|
|
13
|
+
const lastDateOfMonth = new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth() + 1, 0));
|
|
14
|
+
const firstWeekdayOfMonth = firstDateOfMonth.getUTCDay();
|
|
15
|
+
const lastWeekdayOfMonth = lastDateOfMonth.getUTCDay();
|
|
16
|
+
const daysInMonth = lastDateOfMonth.getUTCDate();
|
|
17
17
|
const daysToPrepend = (firstWeekdayOfMonth - firstDayOfWeek + DAYS_IN_WEEK) % DAYS_IN_WEEK;
|
|
18
18
|
const daysToAppend = (DAYS_IN_WEEK - 1 - lastWeekdayOfMonth + firstDayOfWeek) % DAYS_IN_WEEK;
|
|
19
19
|
const month = [];
|
|
@@ -23,7 +23,7 @@ export const getCalendarMonth = (date, options) => {
|
|
|
23
23
|
week.push(null);
|
|
24
24
|
} else {
|
|
25
25
|
const result = new Date(date);
|
|
26
|
-
result.
|
|
26
|
+
result.setUTCDate(i);
|
|
27
27
|
week.push(result);
|
|
28
28
|
}
|
|
29
29
|
if (week.length === 7) {
|
|
@@ -34,13 +34,14 @@ export const getCalendarMonth = (date, options) => {
|
|
|
34
34
|
return month;
|
|
35
35
|
};
|
|
36
36
|
export const dateToIso = date => {
|
|
37
|
-
return `${date.
|
|
37
|
+
return `${date.getUTCFullYear()}-${pad(date.getUTCMonth() + 1)}-${pad(date.getUTCDate())}`;
|
|
38
38
|
};
|
|
39
39
|
export const isoToDate = value => {
|
|
40
|
-
return new Date(`${value.substring(0, 10)}T00:00:
|
|
40
|
+
return new Date(`${value.substring(0, 10)}T00:00:00Z`);
|
|
41
41
|
};
|
|
42
42
|
export const today = () => {
|
|
43
|
-
|
|
43
|
+
const today = new Date();
|
|
44
|
+
return new Date(Date.UTC(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate()));
|
|
44
45
|
};
|
|
45
46
|
export const getDayNames = locale => {
|
|
46
47
|
const formatter = new Intl.DateTimeFormat(locale, {
|
|
@@ -99,35 +100,35 @@ export const clampMaxDate = (date, max) => {
|
|
|
99
100
|
}
|
|
100
101
|
};
|
|
101
102
|
export const incMonth = (date, max) => {
|
|
102
|
-
date.
|
|
103
|
+
date.setUTCMonth(date.getUTCMonth() + 1);
|
|
103
104
|
clampMaxDate(date, max);
|
|
104
105
|
};
|
|
105
106
|
export const decMonth = (date, min) => {
|
|
106
|
-
date.
|
|
107
|
+
date.setUTCMonth(date.getUTCMonth() - 1);
|
|
107
108
|
clampMinDate(date, min);
|
|
108
109
|
};
|
|
109
110
|
export const incYear = (date, max) => {
|
|
110
|
-
date.
|
|
111
|
+
date.setUTCFullYear(date.getUTCFullYear() + 1);
|
|
111
112
|
clampMaxDate(date, max);
|
|
112
113
|
};
|
|
113
114
|
export const decYear = (date, min) => {
|
|
114
|
-
date.
|
|
115
|
+
date.setUTCFullYear(date.getUTCFullYear() - 1);
|
|
115
116
|
clampMinDate(date, min);
|
|
116
117
|
};
|
|
117
118
|
export const canGoPrevMonth = (date, min) => {
|
|
118
|
-
const prevMonth = new Date(Date.UTC(date.
|
|
119
|
+
const prevMonth = new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), 0));
|
|
119
120
|
return compareDates(prevMonth, min) >= 0;
|
|
120
121
|
};
|
|
121
122
|
export const canGoNextMonth = (date, max) => {
|
|
122
|
-
const nextMonth = new Date(Date.UTC(date.
|
|
123
|
+
const nextMonth = new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth() + 1, 1));
|
|
123
124
|
return compareDates(max, nextMonth) >= 0;
|
|
124
125
|
};
|
|
125
126
|
export const canGoNextYear = (date, max) => {
|
|
126
|
-
const nextYear = new Date(Date.UTC(date.
|
|
127
|
+
const nextYear = new Date(Date.UTC(date.getUTCFullYear() + 1, 0, 1));
|
|
127
128
|
return compareDates(max, nextYear) >= 0;
|
|
128
129
|
};
|
|
129
130
|
export const canGoPrevYear = (date, min) => {
|
|
130
|
-
const prevYear = new Date(Date.UTC(date.
|
|
131
|
+
const prevYear = new Date(Date.UTC(date.getUTCFullYear(), 0, 0));
|
|
131
132
|
return compareDates(prevYear, min) >= 0;
|
|
132
133
|
};
|
|
133
134
|
export const isDateBetween = (date, min, max) => {
|