@sondor-technology/sondor-components 1.0.1-develop.6 → 1.0.1-develop.8

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.
@@ -8,10 +8,21 @@ export declare class StModal extends LitElement {
8
8
  * Title text displayed in the modal header.
9
9
  */
10
10
  heading: string;
11
+ /**
12
+ * Size of the modal (sm, md, lg, xl, xxl).
13
+ */
14
+ size: 'sm' | 'md' | 'lg' | 'xl' | 'xxl';
15
+ /**
16
+ * If true, the modal cannot be closed by clicking the backdrop or pressing Esc.
17
+ */
18
+ static: boolean;
11
19
  private dialogEl;
20
+ private isClosing;
12
21
  static styles: import("lit").CSSResult[];
13
22
  updated(changedProperties: PropertyValues): void;
23
+ private closeWithAnimation;
14
24
  private handleClose;
25
+ private handleCancel;
15
26
  private onDialogClose;
16
27
  private handleBackdropClick;
17
28
  render(): import("lit-html").TemplateResult<1>;
@@ -1 +1 @@
1
- {"version":3,"file":"modal.d.ts","sourceRoot":"","sources":["../../../src/components/modal/modal.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,cAAc,EAAE,MAAM,KAAK,CAAC;AAI5D,qBACa,OAAQ,SAAQ,UAAU;IACrC;;OAEG;IACyC,IAAI,UAAS;IAEzD;;OAEG;IACyB,OAAO,SAAM;IAExB,OAAO,CAAC,QAAQ,CAAqB;IAEtD,MAAM,CAAC,MAAM,4BAwEX;IAEF,OAAO,CAAC,iBAAiB,EAAE,cAAc,QAUxC;IAED,OAAO,CAAC,WAAW;IAKnB,OAAO,CAAC,aAAa;IAOrB,OAAO,CAAC,mBAAmB;IAY3B,MAAM,yCAuBL;CACF;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,UAAU,EAAE,OAAO,CAAC;KACrB;CACF"}
1
+ {"version":3,"file":"modal.d.ts","sourceRoot":"","sources":["../../../src/components/modal/modal.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,cAAc,EAAW,MAAM,KAAK,CAAC;AAIrE,qBACa,OAAQ,SAAQ,UAAU;IACrC;;OAEG;IACyC,IAAI,UAAS;IAEzD;;OAEG;IACyB,OAAO,SAAM;IAEzC;;OAEG;IACyB,IAAI,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,KAAK,CAAQ;IAE3E;;OAEG;IAC0B,MAAM,UAAS;IAE3B,OAAO,CAAC,QAAQ,CAAqB;IAEtD,OAAO,CAAC,SAAS,CAAS;IAE1B,MAAM,CAAC,MAAM,4BAkHX;IAEF,OAAO,CAAC,iBAAiB,EAAE,cAAc,QAYxC;IAED,OAAO,CAAC,kBAAkB;IAiB1B,OAAO,CAAC,WAAW;IAKnB,OAAO,CAAC,YAAY;IAOpB,OAAO,CAAC,aAAa;IAOrB,OAAO,CAAC,mBAAmB;IAe3B,MAAM,yCA4BL;CACF;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,UAAU,EAAE,OAAO,CAAC;KACrB;CACF"}
@@ -4,7 +4,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
4
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
- import { LitElement, html, css } from 'lit';
7
+ import { LitElement, html, css, nothing } from 'lit';
8
8
  import { customElement, property, query } from 'lit/decorators.js';
9
9
  import { resetStyles } from '../../styles/reset.js';
10
10
  let StModal = class StModal extends LitElement {
@@ -18,23 +18,55 @@ let StModal = class StModal extends LitElement {
18
18
  * Title text displayed in the modal header.
19
19
  */
20
20
  this.heading = '';
21
+ /**
22
+ * Size of the modal (sm, md, lg, xl, xxl).
23
+ */
24
+ this.size = 'md';
25
+ /**
26
+ * If true, the modal cannot be closed by clicking the backdrop or pressing Esc.
27
+ */
28
+ this.static = false;
29
+ this.isClosing = false;
21
30
  }
22
31
  updated(changedProperties) {
23
32
  if (changedProperties.has('open')) {
24
- if (this.dialogEl) {
25
- if (this.open && !this.dialogEl.open) {
33
+ if (this.open) {
34
+ this.dialogEl.classList.remove('closing');
35
+ this.isClosing = false;
36
+ if (!this.dialogEl.open) {
26
37
  this.dialogEl.showModal();
27
38
  }
28
- else if (!this.open && this.dialogEl.open) {
29
- this.dialogEl.close();
30
- }
39
+ }
40
+ else if (this.dialogEl.open && !this.isClosing) {
41
+ this.closeWithAnimation();
31
42
  }
32
43
  }
33
44
  }
45
+ closeWithAnimation() {
46
+ if (this.isClosing)
47
+ return;
48
+ this.isClosing = true;
49
+ this.dialogEl.classList.add('closing');
50
+ const onAnimationEnd = () => {
51
+ this.dialogEl.removeEventListener('animationend', onAnimationEnd);
52
+ this.dialogEl.classList.remove('closing');
53
+ this.isClosing = false;
54
+ if (!this.open) {
55
+ this.dialogEl.close();
56
+ }
57
+ };
58
+ this.dialogEl.addEventListener('animationend', onAnimationEnd);
59
+ }
34
60
  handleClose() {
35
61
  this.open = false;
36
62
  this.dispatchEvent(new CustomEvent('st-close', { bubbles: true, composed: true }));
37
63
  }
64
+ handleCancel(e) {
65
+ e.preventDefault();
66
+ if (!this.static) {
67
+ this.handleClose();
68
+ }
69
+ }
38
70
  onDialogClose() {
39
71
  if (this.open) {
40
72
  this.open = false;
@@ -42,28 +74,36 @@ let StModal = class StModal extends LitElement {
42
74
  }
43
75
  }
44
76
  handleBackdropClick(e) {
77
+ if (this.static)
78
+ return;
45
79
  const rect = this.dialogEl.getBoundingClientRect();
46
- if (e.clientX < rect.left ||
80
+ const clickedOutside = e.clientX < rect.left ||
47
81
  e.clientX > rect.right ||
48
82
  e.clientY < rect.top ||
49
- e.clientY > rect.bottom) {
83
+ e.clientY > rect.bottom;
84
+ if (clickedOutside) {
50
85
  this.handleClose();
51
86
  }
52
87
  }
53
88
  render() {
54
89
  return html `
55
90
  <dialog
56
- @cancel=${this.handleClose}
91
+ class="size-${this.size}"
92
+ aria-labelledby=${this.heading ? 'modal-heading' : nothing}
93
+ @cancel=${this.handleCancel}
57
94
  @click=${this.handleBackdropClick}
58
95
  @close=${this.onDialogClose}
59
96
  >
60
97
  <div class="modal-header" @click=${(e) => e.stopPropagation()}>
61
98
  <slot name="header">
62
- <h3 class="modal-title">${this.heading}</h3>
99
+ <h3 class="modal-title" id="modal-heading">${this.heading}</h3>
63
100
  </slot>
64
- <button class="close-btn" @click=${this.handleClose} aria-label="Close modal">
65
- <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
66
- </button>
101
+
102
+ ${!this.static ? html `
103
+ <button class="close-btn" @click=${this.handleClose} aria-label="Close modal">
104
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
105
+ </button>
106
+ ` : nothing}
67
107
  </div>
68
108
  <div class="modal-body" @click=${(e) => e.stopPropagation()}>
69
109
  <slot></slot>
@@ -88,17 +128,59 @@ StModal.styles = [
88
128
  border-radius: var(--st-radius-lg, 0.5rem);
89
129
  background-color: var(--st-color-surface);
90
130
  color: var(--st-color-text);
91
- width: 100%;
92
- max-width: 500px;
131
+
132
+ /* Responsive width calculation */
133
+ width: calc(100% - 2rem);
134
+ margin: auto;
135
+
93
136
  box-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
94
137
  font-family: var(--st-font-family, inherit);
95
138
  }
96
139
 
140
+ /* Sizes */
141
+ dialog.size-sm { max-width: 400px; }
142
+ dialog.size-md { max-width: 500px; }
143
+ dialog.size-lg { max-width: 800px; }
144
+ dialog.size-xl { max-width: 1024px; }
145
+ dialog.size-xxl { max-width: 1280px; }
146
+
97
147
  dialog::backdrop {
98
148
  background-color: rgba(0, 0, 0, 0.5);
99
149
  backdrop-filter: blur(2px);
100
150
  }
101
151
 
152
+ /* Animations */
153
+ @keyframes fade-in {
154
+ from { opacity: 0; }
155
+ to { opacity: 1; }
156
+ }
157
+ @keyframes fade-out {
158
+ from { opacity: 1; }
159
+ to { opacity: 0; }
160
+ }
161
+ @keyframes slide-up {
162
+ from { opacity: 0; transform: translateY(20px) scale(0.95); }
163
+ to { opacity: 1; transform: translateY(0) scale(1); }
164
+ }
165
+ @keyframes slide-down {
166
+ from { opacity: 1; transform: translateY(0) scale(1); }
167
+ to { opacity: 0; transform: translateY(20px) scale(0.95); }
168
+ }
169
+
170
+ dialog[open] {
171
+ animation: slide-up 0.2s cubic-bezier(0.16, 1, 0.3, 1) forwards;
172
+ }
173
+ dialog[open]::backdrop {
174
+ animation: fade-in 0.2s cubic-bezier(0.16, 1, 0.3, 1) forwards;
175
+ }
176
+
177
+ dialog.closing {
178
+ animation: slide-down 0.2s cubic-bezier(0.16, 1, 0.3, 1) forwards;
179
+ }
180
+ dialog.closing::backdrop {
181
+ animation: fade-out 0.2s cubic-bezier(0.16, 1, 0.3, 1) forwards;
182
+ }
183
+
102
184
  .modal-header {
103
185
  display: flex;
104
186
  align-items: center;
@@ -154,6 +236,12 @@ __decorate([
154
236
  __decorate([
155
237
  property({ type: String })
156
238
  ], StModal.prototype, "heading", void 0);
239
+ __decorate([
240
+ property({ type: String })
241
+ ], StModal.prototype, "size", void 0);
242
+ __decorate([
243
+ property({ type: Boolean })
244
+ ], StModal.prototype, "static", void 0);
157
245
  __decorate([
158
246
  query('dialog')
159
247
  ], StModal.prototype, "dialogEl", void 0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sondor-technology/sondor-components",
3
- "version": "1.0.1-develop.6",
3
+ "version": "1.0.1-develop.8",
4
4
  "description": "",
5
5
  "main": "src/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -1,4 +1,4 @@
1
- import { LitElement, html, css, PropertyValues } from 'lit';
1
+ import { LitElement, html, css, PropertyValues, nothing } from 'lit';
2
2
  import { customElement, property, query } from 'lit/decorators.js';
3
3
  import { resetStyles } from '../../styles/reset.js';
4
4
 
@@ -14,8 +14,20 @@ export class StModal extends LitElement {
14
14
  */
15
15
  @property({ type: String }) heading = '';
16
16
 
17
+ /**
18
+ * Size of the modal (sm, md, lg, xl, xxl).
19
+ */
20
+ @property({ type: String }) size: 'sm' | 'md' | 'lg' | 'xl' | 'xxl' = 'md';
21
+
22
+ /**
23
+ * If true, the modal cannot be closed by clicking the backdrop or pressing Esc.
24
+ */
25
+ @property({ type: Boolean }) static = false;
26
+
17
27
  @query('dialog') private dialogEl!: HTMLDialogElement;
18
28
 
29
+ private isClosing = false;
30
+
19
31
  static styles = [
20
32
  resetStyles,
21
33
  css`
@@ -29,17 +41,59 @@ export class StModal extends LitElement {
29
41
  border-radius: var(--st-radius-lg, 0.5rem);
30
42
  background-color: var(--st-color-surface);
31
43
  color: var(--st-color-text);
32
- width: 100%;
33
- max-width: 500px;
44
+
45
+ /* Responsive width calculation */
46
+ width: calc(100% - 2rem);
47
+ margin: auto;
48
+
34
49
  box-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
35
50
  font-family: var(--st-font-family, inherit);
36
51
  }
37
52
 
53
+ /* Sizes */
54
+ dialog.size-sm { max-width: 400px; }
55
+ dialog.size-md { max-width: 500px; }
56
+ dialog.size-lg { max-width: 800px; }
57
+ dialog.size-xl { max-width: 1024px; }
58
+ dialog.size-xxl { max-width: 1280px; }
59
+
38
60
  dialog::backdrop {
39
61
  background-color: rgba(0, 0, 0, 0.5);
40
62
  backdrop-filter: blur(2px);
41
63
  }
42
64
 
65
+ /* Animations */
66
+ @keyframes fade-in {
67
+ from { opacity: 0; }
68
+ to { opacity: 1; }
69
+ }
70
+ @keyframes fade-out {
71
+ from { opacity: 1; }
72
+ to { opacity: 0; }
73
+ }
74
+ @keyframes slide-up {
75
+ from { opacity: 0; transform: translateY(20px) scale(0.95); }
76
+ to { opacity: 1; transform: translateY(0) scale(1); }
77
+ }
78
+ @keyframes slide-down {
79
+ from { opacity: 1; transform: translateY(0) scale(1); }
80
+ to { opacity: 0; transform: translateY(20px) scale(0.95); }
81
+ }
82
+
83
+ dialog[open] {
84
+ animation: slide-up 0.2s cubic-bezier(0.16, 1, 0.3, 1) forwards;
85
+ }
86
+ dialog[open]::backdrop {
87
+ animation: fade-in 0.2s cubic-bezier(0.16, 1, 0.3, 1) forwards;
88
+ }
89
+
90
+ dialog.closing {
91
+ animation: slide-down 0.2s cubic-bezier(0.16, 1, 0.3, 1) forwards;
92
+ }
93
+ dialog.closing::backdrop {
94
+ animation: fade-out 0.2s cubic-bezier(0.16, 1, 0.3, 1) forwards;
95
+ }
96
+
43
97
  .modal-header {
44
98
  display: flex;
45
99
  align-items: center;
@@ -92,21 +146,47 @@ export class StModal extends LitElement {
92
146
 
93
147
  updated(changedProperties: PropertyValues) {
94
148
  if (changedProperties.has('open')) {
95
- if (this.dialogEl) {
96
- if (this.open && !this.dialogEl.open) {
149
+ if (this.open) {
150
+ this.dialogEl.classList.remove('closing');
151
+ this.isClosing = false;
152
+ if (!this.dialogEl.open) {
97
153
  this.dialogEl.showModal();
98
- } else if (!this.open && this.dialogEl.open) {
99
- this.dialogEl.close();
100
154
  }
155
+ } else if (this.dialogEl.open && !this.isClosing) {
156
+ this.closeWithAnimation();
101
157
  }
102
158
  }
103
159
  }
104
160
 
161
+ private closeWithAnimation() {
162
+ if (this.isClosing) return;
163
+ this.isClosing = true;
164
+ this.dialogEl.classList.add('closing');
165
+
166
+ const onAnimationEnd = () => {
167
+ this.dialogEl.removeEventListener('animationend', onAnimationEnd);
168
+ this.dialogEl.classList.remove('closing');
169
+ this.isClosing = false;
170
+ if (!this.open) {
171
+ this.dialogEl.close();
172
+ }
173
+ };
174
+
175
+ this.dialogEl.addEventListener('animationend', onAnimationEnd);
176
+ }
177
+
105
178
  private handleClose() {
106
179
  this.open = false;
107
180
  this.dispatchEvent(new CustomEvent('st-close', { bubbles: true, composed: true }));
108
181
  }
109
182
 
183
+ private handleCancel(e: Event) {
184
+ e.preventDefault();
185
+ if (!this.static) {
186
+ this.handleClose();
187
+ }
188
+ }
189
+
110
190
  private onDialogClose() {
111
191
  if (this.open) {
112
192
  this.open = false;
@@ -115,13 +195,16 @@ export class StModal extends LitElement {
115
195
  }
116
196
 
117
197
  private handleBackdropClick(e: MouseEvent) {
198
+ if (this.static) return;
199
+
118
200
  const rect = this.dialogEl.getBoundingClientRect();
119
- if (
201
+ const clickedOutside =
120
202
  e.clientX < rect.left ||
121
203
  e.clientX > rect.right ||
122
204
  e.clientY < rect.top ||
123
- e.clientY > rect.bottom
124
- ) {
205
+ e.clientY > rect.bottom;
206
+
207
+ if (clickedOutside) {
125
208
  this.handleClose();
126
209
  }
127
210
  }
@@ -129,17 +212,22 @@ export class StModal extends LitElement {
129
212
  render() {
130
213
  return html`
131
214
  <dialog
132
- @cancel=${this.handleClose}
215
+ class="size-${this.size}"
216
+ aria-labelledby=${this.heading ? 'modal-heading' : nothing}
217
+ @cancel=${this.handleCancel}
133
218
  @click=${this.handleBackdropClick}
134
219
  @close=${this.onDialogClose}
135
220
  >
136
221
  <div class="modal-header" @click=${(e: MouseEvent) => e.stopPropagation()}>
137
222
  <slot name="header">
138
- <h3 class="modal-title">${this.heading}</h3>
223
+ <h3 class="modal-title" id="modal-heading">${this.heading}</h3>
139
224
  </slot>
140
- <button class="close-btn" @click=${this.handleClose} aria-label="Close modal">
141
- <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
142
- </button>
225
+
226
+ ${!this.static ? html`
227
+ <button class="close-btn" @click=${this.handleClose} aria-label="Close modal">
228
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
229
+ </button>
230
+ ` : nothing}
143
231
  </div>
144
232
  <div class="modal-body" @click=${(e: MouseEvent) => e.stopPropagation()}>
145
233
  <slot></slot>