mtrl 0.2.5 → 0.2.7

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.
Files changed (196) hide show
  1. package/index.ts +18 -0
  2. package/package.json +1 -1
  3. package/src/components/badge/_styles.scss +123 -115
  4. package/src/components/badge/api.ts +57 -59
  5. package/src/components/badge/badge.ts +16 -2
  6. package/src/components/badge/config.ts +65 -11
  7. package/src/components/badge/constants.ts +22 -12
  8. package/src/components/badge/features.ts +44 -40
  9. package/src/components/badge/types.ts +42 -30
  10. package/src/components/bottom-app-bar/_styles.scss +103 -0
  11. package/src/components/bottom-app-bar/bottom-app-bar.ts +196 -0
  12. package/src/components/bottom-app-bar/config.ts +73 -0
  13. package/src/components/bottom-app-bar/index.ts +11 -0
  14. package/src/components/bottom-app-bar/types.ts +108 -0
  15. package/src/components/button/_styles.scss +0 -66
  16. package/src/components/button/api.ts +5 -0
  17. package/src/components/button/button.ts +0 -2
  18. package/src/components/button/config.ts +5 -0
  19. package/src/components/button/constants.ts +0 -6
  20. package/src/components/button/index.ts +2 -2
  21. package/src/components/button/types.ts +7 -7
  22. package/src/components/card/_styles.scss +67 -25
  23. package/src/components/card/api.ts +54 -3
  24. package/src/components/card/card.ts +25 -6
  25. package/src/components/card/config.ts +189 -22
  26. package/src/components/card/constants.ts +20 -19
  27. package/src/components/card/content.ts +299 -2
  28. package/src/components/card/features.ts +158 -4
  29. package/src/components/card/index.ts +31 -9
  30. package/src/components/card/types.ts +166 -15
  31. package/src/components/checkbox/_styles.scss +0 -2
  32. package/src/components/chip/chip.ts +1 -9
  33. package/src/components/chip/constants.ts +0 -10
  34. package/src/components/chip/index.ts +1 -1
  35. package/src/components/chip/types.ts +1 -4
  36. package/src/components/datepicker/_styles.scss +358 -0
  37. package/src/components/datepicker/api.ts +272 -0
  38. package/src/components/datepicker/config.ts +144 -0
  39. package/src/components/datepicker/constants.ts +98 -0
  40. package/src/components/datepicker/datepicker.ts +346 -0
  41. package/src/components/datepicker/index.ts +9 -0
  42. package/src/components/datepicker/render.ts +452 -0
  43. package/src/components/datepicker/types.ts +268 -0
  44. package/src/components/datepicker/utils.ts +290 -0
  45. package/src/components/dialog/_styles.scss +174 -128
  46. package/src/components/dialog/api.ts +48 -13
  47. package/src/components/dialog/config.ts +9 -5
  48. package/src/components/dialog/dialog.ts +6 -3
  49. package/src/components/dialog/features.ts +290 -130
  50. package/src/components/dialog/types.ts +7 -4
  51. package/src/components/divider/_styles.scss +57 -0
  52. package/src/components/divider/config.ts +81 -0
  53. package/src/components/divider/divider.ts +37 -0
  54. package/src/components/divider/features.ts +207 -0
  55. package/src/components/divider/index.ts +5 -0
  56. package/src/components/divider/types.ts +55 -0
  57. package/src/components/extended-fab/_styles.scss +267 -0
  58. package/src/components/extended-fab/api.ts +141 -0
  59. package/src/components/extended-fab/config.ts +108 -0
  60. package/src/components/extended-fab/constants.ts +36 -0
  61. package/src/components/extended-fab/extended-fab.ts +125 -0
  62. package/src/components/extended-fab/index.ts +4 -0
  63. package/src/components/extended-fab/types.ts +287 -0
  64. package/src/components/fab/_styles.scss +225 -0
  65. package/src/components/fab/api.ts +97 -0
  66. package/src/components/fab/config.ts +94 -0
  67. package/src/components/fab/constants.ts +41 -0
  68. package/src/components/fab/fab.ts +67 -0
  69. package/src/components/fab/index.ts +4 -0
  70. package/src/components/fab/types.ts +234 -0
  71. package/src/components/navigation/_styles.scss +1 -0
  72. package/src/components/navigation/api.ts +78 -50
  73. package/src/components/navigation/features/items.ts +280 -0
  74. package/src/components/navigation/nav-item.ts +72 -23
  75. package/src/components/navigation/navigation.ts +54 -2
  76. package/src/components/navigation/types.ts +210 -188
  77. package/src/components/progress/_styles.scss +0 -65
  78. package/src/components/progress/config.ts +1 -2
  79. package/src/components/progress/constants.ts +0 -14
  80. package/src/components/progress/index.ts +1 -1
  81. package/src/components/progress/progress.ts +1 -4
  82. package/src/components/progress/types.ts +1 -4
  83. package/src/components/radios/_styles.scss +0 -45
  84. package/src/components/radios/api.ts +85 -60
  85. package/src/components/radios/config.ts +1 -2
  86. package/src/components/radios/constants.ts +0 -9
  87. package/src/components/radios/index.ts +1 -1
  88. package/src/components/radios/radio.ts +34 -11
  89. package/src/components/radios/radios.ts +2 -1
  90. package/src/components/radios/types.ts +1 -7
  91. package/src/components/search/_styles.scss +306 -0
  92. package/src/components/search/api.ts +203 -0
  93. package/src/components/search/config.ts +87 -0
  94. package/src/components/search/constants.ts +21 -0
  95. package/src/components/search/features/index.ts +4 -0
  96. package/src/components/search/features/search.ts +718 -0
  97. package/src/components/search/features/states.ts +165 -0
  98. package/src/components/search/features/structure.ts +198 -0
  99. package/src/components/search/index.ts +10 -0
  100. package/src/components/search/search.ts +52 -0
  101. package/src/components/search/types.ts +163 -0
  102. package/src/components/segmented-button/_styles.scss +117 -0
  103. package/src/components/segmented-button/config.ts +67 -0
  104. package/src/components/segmented-button/constants.ts +42 -0
  105. package/src/components/segmented-button/index.ts +4 -0
  106. package/src/components/segmented-button/segment.ts +155 -0
  107. package/src/components/segmented-button/segmented-button.ts +250 -0
  108. package/src/components/segmented-button/types.ts +219 -0
  109. package/src/components/slider/_styles.scss +221 -168
  110. package/src/components/slider/accessibility.md +59 -0
  111. package/src/components/slider/api.ts +41 -120
  112. package/src/components/slider/config.ts +51 -49
  113. package/src/components/slider/features/handlers.ts +495 -0
  114. package/src/components/slider/features/index.ts +1 -2
  115. package/src/components/slider/features/slider.ts +66 -84
  116. package/src/components/slider/features/states.ts +195 -0
  117. package/src/components/slider/features/structure.ts +141 -184
  118. package/src/components/slider/features/ui.ts +150 -201
  119. package/src/components/slider/index.ts +2 -11
  120. package/src/components/slider/slider.ts +9 -12
  121. package/src/components/slider/types.ts +39 -24
  122. package/src/components/switch/_styles.scss +0 -2
  123. package/src/components/tabs/_styles.scss +346 -154
  124. package/src/components/tabs/api.ts +178 -400
  125. package/src/components/tabs/config.ts +46 -52
  126. package/src/components/tabs/constants.ts +85 -8
  127. package/src/components/tabs/features.ts +403 -0
  128. package/src/components/tabs/index.ts +60 -3
  129. package/src/components/tabs/indicator.ts +285 -0
  130. package/src/components/tabs/responsive.ts +144 -0
  131. package/src/components/tabs/scroll-indicators.ts +149 -0
  132. package/src/components/tabs/state.ts +186 -0
  133. package/src/components/tabs/tab-api.ts +258 -0
  134. package/src/components/tabs/tab.ts +255 -0
  135. package/src/components/tabs/tabs.ts +50 -31
  136. package/src/components/tabs/types.ts +332 -128
  137. package/src/components/tabs/utils.ts +107 -0
  138. package/src/components/textfield/_styles.scss +0 -98
  139. package/src/components/textfield/config.ts +2 -3
  140. package/src/components/textfield/constants.ts +0 -14
  141. package/src/components/textfield/index.ts +2 -2
  142. package/src/components/textfield/textfield.ts +0 -2
  143. package/src/components/textfield/types.ts +1 -4
  144. package/src/components/timepicker/README.md +277 -0
  145. package/src/components/timepicker/_styles.scss +451 -0
  146. package/src/components/timepicker/api.ts +632 -0
  147. package/src/components/timepicker/clockdial.ts +482 -0
  148. package/src/components/timepicker/config.ts +130 -0
  149. package/src/components/timepicker/constants.ts +138 -0
  150. package/src/components/timepicker/index.ts +8 -0
  151. package/src/components/timepicker/render.ts +613 -0
  152. package/src/components/timepicker/timepicker.ts +117 -0
  153. package/src/components/timepicker/types.ts +336 -0
  154. package/src/components/timepicker/utils.ts +241 -0
  155. package/src/components/top-app-bar/_styles.scss +225 -0
  156. package/src/components/top-app-bar/config.ts +83 -0
  157. package/src/components/top-app-bar/index.ts +11 -0
  158. package/src/components/top-app-bar/top-app-bar.ts +316 -0
  159. package/src/components/top-app-bar/types.ts +140 -0
  160. package/src/core/build/_ripple.scss +6 -6
  161. package/src/core/build/ripple.ts +72 -95
  162. package/src/core/compose/component.ts +1 -1
  163. package/src/core/compose/features/badge.ts +79 -0
  164. package/src/core/compose/features/icon.ts +3 -1
  165. package/src/core/compose/features/index.ts +3 -1
  166. package/src/core/compose/features/ripple.ts +4 -1
  167. package/src/core/compose/features/textlabel.ts +26 -2
  168. package/src/core/dom/create.ts +5 -0
  169. package/src/index.ts +9 -0
  170. package/src/styles/abstract/_theme.scss +115 -3
  171. package/src/styles/themes/_autumn.scss +21 -0
  172. package/src/styles/themes/_base-theme.scss +61 -0
  173. package/src/styles/themes/_baseline.scss +58 -0
  174. package/src/styles/themes/_bluekhaki.scss +125 -0
  175. package/src/styles/themes/_brownbeige.scss +125 -0
  176. package/src/styles/themes/_browngreen.scss +125 -0
  177. package/src/styles/themes/_forest.scss +6 -0
  178. package/src/styles/themes/_greenbeige.scss +125 -0
  179. package/src/styles/themes/_material.scss +125 -0
  180. package/src/styles/themes/_ocean.scss +6 -0
  181. package/src/styles/themes/_sageivory.scss +125 -0
  182. package/src/styles/themes/_spring.scss +6 -0
  183. package/src/styles/themes/_summer.scss +5 -0
  184. package/src/styles/themes/_sunset.scss +5 -0
  185. package/src/styles/themes/_tealcaramel.scss +125 -0
  186. package/src/styles/themes/_winter.scss +6 -0
  187. package/src/components/card/actions.ts +0 -48
  188. package/src/components/card/header.ts +0 -88
  189. package/src/components/card/media.ts +0 -52
  190. package/src/components/navigation/features/items.js +0 -192
  191. package/src/components/slider/features/appearance.ts +0 -94
  192. package/src/components/slider/features/disabled.ts +0 -43
  193. package/src/components/slider/features/events.ts +0 -164
  194. package/src/components/slider/features/interactions.ts +0 -261
  195. package/src/components/slider/features/keyboard.ts +0 -112
  196. package/src/core/collection/adapters/mongodb.js +0 -232
@@ -1,29 +1,34 @@
1
- // src/components/dialog/features.ts
1
+ // src/components/dialog/features.ts (partial updated code)
2
+
2
3
  import { getOverlayConfig } from './config';
3
4
  import { DIALOG_SIZES, DIALOG_ANIMATIONS, DIALOG_FOOTER_ALIGNMENTS, DIALOG_EVENTS } from './constants';
4
5
  import { DialogConfig, DialogButton, DialogEvent } from './types';
5
6
  import createButton from '../button';
7
+ import { createDivider } from '../divider'; // Import the divider component
6
8
  import { BUTTON_VARIANTS } from '../button/constants';
9
+ import { addClass, removeClass, hasClass } from '../../core/dom/classes';
7
10
 
8
11
  /**
9
- * Creates the dialog DOM structure
12
+ * Creates the dialog DOM structure with proper divider handling
10
13
  * @param config Dialog configuration
11
14
  * @returns Component enhancer with DOM structure
12
15
  */
13
16
  export const withStructure = (config: DialogConfig) => component => {
14
17
  // Create the overlay element
15
18
  const overlayConfig = getOverlayConfig(config);
16
- const overlay = document.createElement(overlayConfig.tag);
19
+ const overlay = document.createElement(overlayConfig.tag || 'div');
17
20
 
18
21
  // Add overlay classes
19
22
  overlay.classList.add(component.getClass('dialog-overlay'));
20
23
 
21
- // Set overlay attributes
22
- Object.entries(overlayConfig.attrs || {}).forEach(([key, value]) => {
23
- if (value !== undefined) {
24
- overlay.setAttribute(key, String(value));
25
- }
26
- });
24
+ // Set overlay attributes safely
25
+ if (overlayConfig.attrs && typeof overlayConfig.attrs === 'object') {
26
+ Object.entries(overlayConfig.attrs).forEach(([key, value]) => {
27
+ if (key && typeof key === 'string' && value !== undefined) {
28
+ overlay.setAttribute(key, String(value));
29
+ }
30
+ });
31
+ }
27
32
 
28
33
  // Set custom z-index if provided
29
34
  if (config.zIndex) {
@@ -63,9 +68,18 @@ export const withStructure = (config: DialogConfig) => component => {
63
68
  <line x1="6" y1="6" x2="18" y2="18"></line>
64
69
  </svg>
65
70
  `;
66
- closeButton.addEventListener('click', () => {
67
- component.events.trigger(DIALOG_EVENTS.CLOSE, { dialog: component });
71
+
72
+ // Close button click handler with event-based communication
73
+ closeButton.addEventListener('click', (e) => {
74
+ e.preventDefault();
75
+ e.stopPropagation();
76
+
77
+ // Use the dialog:close custom event which will be listened for in withVisibility
78
+ if (component && component.emit) {
79
+ component.emit('dialog:close', { source: 'closeButton' });
80
+ }
68
81
  });
82
+
69
83
  header.appendChild(closeButton);
70
84
  }
71
85
 
@@ -90,7 +104,7 @@ export const withStructure = (config: DialogConfig) => component => {
90
104
  // Apply footer alignment
91
105
  const alignment = config.footerAlignment || DIALOG_FOOTER_ALIGNMENTS.RIGHT;
92
106
  if (alignment !== DIALOG_FOOTER_ALIGNMENTS.RIGHT) {
93
- footer.classList.add(`${component.getClass('dialog-footer')}--${alignment}`);
107
+ addClass(footer, `${component.getClass('dialog-footer')}--${alignment}`);
94
108
  }
95
109
 
96
110
  // Add buttons if provided
@@ -101,36 +115,70 @@ export const withStructure = (config: DialogConfig) => component => {
101
115
  return footer;
102
116
  };
103
117
 
118
+ const createDividerElement = () => {
119
+ const divider = createDivider({
120
+ variant: 'full-width',
121
+ class: component.getClass('dialog-divider')
122
+ });
123
+ return divider;
124
+ };
125
+
104
126
  // Create the dialog structure
105
127
  const header = createHeader();
106
128
  const content = createContent();
107
129
  const footer = Array.isArray(config.buttons) && config.buttons.length > 0 ? createFooter() : null;
108
130
 
109
- // Add header, content, and footer to dialog
110
- component.element.appendChild(header);
111
- component.element.appendChild(content);
112
- if (footer) {
113
- component.element.appendChild(footer);
114
- }
115
-
116
- // Add dialog to overlay
117
- overlay.appendChild(component.element);
118
-
119
- // Add dialog classes
120
- component.element.classList.add(component.getClass('dialog'));
131
+ // Add dialog classes to the main component element
132
+ addClass(component.element, component.getClass('dialog'));
121
133
 
122
134
  // Apply size class
123
135
  const size = config.size || DIALOG_SIZES.MEDIUM;
124
136
  if (size !== DIALOG_SIZES.MEDIUM) {
125
- component.element.classList.add(`${component.getClass('dialog')}--${size}`);
137
+ addClass(component.element, `${component.getClass('dialog')}--${size}`);
126
138
  }
127
139
 
128
140
  // Apply animation class
129
141
  const animation = config.animation || DIALOG_ANIMATIONS.SCALE;
130
142
  if (animation !== DIALOG_ANIMATIONS.SCALE) {
131
- component.element.classList.add(`${component.getClass('dialog')}--${animation}`);
143
+ addClass(component.element, `${component.getClass('dialog')}--${animation}`);
144
+ }
145
+
146
+ // Add header to dialog
147
+ component.element.appendChild(header);
148
+
149
+ // Create divider elements if configured
150
+ let headerDivider = null;
151
+ let footerDivider = null;
152
+
153
+ if (config.divider) {
154
+ // Add header divider (between header and content)
155
+ headerDivider = createDividerElement();
156
+ headerDivider.element.classList.add(component.getClass('dialog-header-divider'));
157
+ component.element.appendChild(headerDivider.element);
158
+
159
+ // If footer exists, add footer divider (between content and footer)
160
+ if (footer) {
161
+ footerDivider = createDividerElement();
162
+ footerDivider.element.classList.add(component.getClass('dialog-footer-divider'));
163
+ }
164
+ }
165
+
166
+ // Add content to dialog
167
+ component.element.appendChild(content);
168
+
169
+ // Add footer divider before footer if it exists
170
+ if (footerDivider) {
171
+ component.element.appendChild(footerDivider.element);
132
172
  }
133
173
 
174
+ // Add footer to dialog if exists
175
+ if (footer) {
176
+ component.element.appendChild(footer);
177
+ }
178
+
179
+ // Add the dialog element to the overlay
180
+ overlay.appendChild(component.element);
181
+
134
182
  // Add overlay to container or document.body
135
183
  const container = config.container || document.body;
136
184
  container.appendChild(overlay);
@@ -143,11 +191,88 @@ export const withStructure = (config: DialogConfig) => component => {
143
191
  header,
144
192
  content,
145
193
  footer,
194
+ headerDivider,
195
+ footerDivider,
146
196
  container
147
197
  }
148
198
  };
149
199
  };
150
200
 
201
+
202
+ /**
203
+ * Add methods to manage dividers
204
+ * @returns Component enhancer with divider management features
205
+ */
206
+ export const withDivider = () => component => {
207
+ return {
208
+ ...component,
209
+ divider: {
210
+ /**
211
+ * Shows or hides the dividers
212
+ * @param show Whether to show the dividers
213
+ * @returns Component instance for chaining
214
+ */
215
+ toggleDivider(show) {
216
+ // Handle header divider
217
+ if (show && !component.structure.headerDivider) {
218
+ // Create and add header divider
219
+ const headerDivider = createDivider({
220
+ variant: 'full-width',
221
+ class: `${component.getClass('dialog-divider')} ${component.getClass('dialog-header-divider')}`
222
+ });
223
+
224
+ // Insert after header, before content
225
+ component.element.insertBefore(
226
+ headerDivider.element,
227
+ component.structure.content
228
+ );
229
+
230
+ component.structure.headerDivider = headerDivider;
231
+
232
+ // If footer exists, add footer divider
233
+ if (component.structure.footer && !component.structure.footerDivider) {
234
+ const footerDivider = createDivider({
235
+ variant: 'full-width',
236
+ class: `${component.getClass('dialog-divider')} ${component.getClass('dialog-footer-divider')}`
237
+ });
238
+
239
+ // Insert before footer
240
+ component.element.insertBefore(
241
+ footerDivider.element,
242
+ component.structure.footer
243
+ );
244
+
245
+ component.structure.footerDivider = footerDivider;
246
+ }
247
+ } else if (!show) {
248
+ // Remove header divider if it exists
249
+ if (component.structure.headerDivider) {
250
+ component.structure.headerDivider.element.remove();
251
+ component.structure.headerDivider = null;
252
+ }
253
+
254
+ // Remove footer divider if it exists
255
+ if (component.structure.footerDivider) {
256
+ component.structure.footerDivider.element.remove();
257
+ component.structure.footerDivider = null;
258
+ }
259
+ }
260
+
261
+ return component;
262
+ },
263
+
264
+ /**
265
+ * Checks if the dialog has dividers
266
+ * @returns Whether the dialog has dividers
267
+ */
268
+ hasDivider() {
269
+ return component.structure.headerDivider !== null;
270
+ }
271
+ }
272
+ };
273
+ };
274
+
275
+
151
276
  /**
152
277
  * Adds button to dialog footer
153
278
  * @param footer Footer element
@@ -170,26 +295,32 @@ const addButton = (footer: HTMLElement, buttonConfig: DialogButton, component: a
170
295
  ...attrs
171
296
  });
172
297
 
173
- // Add click handler
298
+ // Button click handler with event-based communication
174
299
  button.on('click', event => {
300
+ console.log('button click');
175
301
  let shouldClose = closeDialog;
176
302
 
177
303
  // Call onClick handler if provided
178
304
  if (typeof onClick === 'function') {
179
- const result = onClick(event, component);
180
- // If onClick returns false, don't close the dialog
181
- if (result === false) {
182
- shouldClose = false;
305
+ try {
306
+ const result = onClick(event, component);
307
+ if (result === false) {
308
+ shouldClose = false;
309
+ }
310
+ } catch (err) {
311
+ console.error('Error in onClick handler:', err);
183
312
  }
184
313
  }
185
314
 
186
- // Close dialog if needed
315
+ // Close dialog if needed - using event-based communication
187
316
  if (shouldClose) {
188
- component.events.trigger(DIALOG_EVENTS.CLOSE, { dialog: component });
317
+ if (component && component.emit) {
318
+ component.emit('dialog:close', { source: 'button', text });
319
+ }
189
320
  }
190
321
  });
191
322
 
192
- // Set autofocus attribute if needed
323
+ // Set autofocus if needed
193
324
  if (autofocus) {
194
325
  button.element.setAttribute('autofocus', 'true');
195
326
  }
@@ -300,125 +431,154 @@ export const withVisibility = () => component => {
300
431
  function handleOverlayClick(e: MouseEvent) {
301
432
  // Only close if the click was directly on the overlay
302
433
  if (e.target === component.overlay) {
303
- component.events.trigger(DIALOG_EVENTS.CLOSE, {
304
- dialog: component,
305
- originalEvent: e
306
- });
434
+ visibility.close();
307
435
  }
308
436
  }
309
437
 
310
438
  function handleEscKey(e: KeyboardEvent) {
311
- if (e.key === 'Escape' && component.visibility.isOpen()) {
312
- component.events.trigger(DIALOG_EVENTS.CLOSE, {
313
- dialog: component,
314
- originalEvent: e
315
- });
439
+ if (e.key === 'Escape' && visibility.isOpen()) {
440
+ visibility.close();
316
441
  }
317
442
  }
318
443
 
319
444
  // Setup initial state
320
445
  if (isOpen) {
321
- component.overlay.classList.add(`${component.getClass('dialog-overlay')}--visible`);
322
- component.element.classList.add(`${component.getClass('dialog')}--visible`);
446
+ addClass(component.overlay, `${component.getClass('dialog-overlay')}--visible`);
447
+ addClass(component.element, `${component.getClass('dialog')}--visible`);
323
448
 
324
449
  // Setup focus trap and events
325
450
  trapFocus();
326
451
  setupEvents();
327
452
  }
328
453
 
329
- return {
330
- ...component,
331
- visibility: {
332
- open() {
333
- // Don't do anything if already open
334
- if (this.isOpen()) return;
335
-
336
- // Store the currently focused element
337
- previouslyFocusedElement = document.activeElement as HTMLElement;
338
-
339
- // Trigger before open event
340
- const beforeOpenEvent = { dialog: component, defaultPrevented: false, preventDefault: () => { beforeOpenEvent.defaultPrevented = true; } };
341
- component.events.trigger(DIALOG_EVENTS.BEFORE_OPEN, beforeOpenEvent);
342
-
343
- // If event was prevented, don't open
344
- if (beforeOpenEvent.defaultPrevented) return;
454
+ // Create visibility object with clean methods
455
+ const visibility = {
456
+ open() {
457
+ // Don't do anything if already open
458
+ if (this.isOpen()) return;
459
+
460
+ // Store the currently focused element
461
+ previouslyFocusedElement = document.activeElement as HTMLElement;
462
+
463
+ // Trigger before open event
464
+ const beforeOpenEvent = {
465
+ dialog: component,
466
+ defaultPrevented: false,
467
+ preventDefault: () => { beforeOpenEvent.defaultPrevented = true; }
468
+ };
469
+
470
+ if (typeof component.emit === 'function') {
471
+ component.emit(DIALOG_EVENTS.BEFORE_OPEN, beforeOpenEvent);
472
+ }
473
+
474
+ // If event was prevented, don't open
475
+ if (beforeOpenEvent.defaultPrevented) return;
476
+
477
+ // Add to DOM if needed
478
+ if (component.overlay && !component.overlay.parentNode) {
479
+ const container = component.structure.container || document.body;
480
+ container.appendChild(component.overlay);
481
+ }
482
+
483
+ // Show the overlay
484
+ addClass(component.overlay, `${component.getClass('dialog-overlay')}--visible`);
485
+
486
+ // Show the dialog
487
+ setTimeout(() => {
488
+ addClass(component.element, `${component.getClass('dialog')}--visible`);
345
489
 
346
- // Show the overlay
347
- component.overlay.classList.add(`${component.getClass('dialog-overlay')}--visible`);
490
+ // Setup focus trap and events
491
+ trapFocus();
492
+ setupEvents();
348
493
 
349
- // Show the dialog after a small delay to allow the overlay animation to start
350
- setTimeout(() => {
351
- component.element.classList.add(`${component.getClass('dialog')}--visible`);
352
-
353
- // Setup focus trap and events
354
- trapFocus();
355
- setupEvents();
494
+ // Trigger open event
495
+ if (typeof component.emit === 'function') {
496
+ component.emit(DIALOG_EVENTS.OPEN, { dialog: component });
356
497
 
357
- // Trigger after open event after animation completes
358
498
  setTimeout(() => {
359
- component.events.trigger(DIALOG_EVENTS.AFTER_OPEN, { dialog: component });
499
+ component.emit(DIALOG_EVENTS.AFTER_OPEN, { dialog: component });
360
500
  }, animationDuration);
361
-
362
- // Trigger open event
363
- component.events.trigger(DIALOG_EVENTS.OPEN, { dialog: component });
364
- }, 10);
365
- },
501
+ }
502
+ }, 10);
503
+ },
504
+
505
+ close() {
506
+ console.log('Dialog close method called');
366
507
 
367
- close() {
368
- // Don't do anything if already closed
369
- if (!this.isOpen()) return;
370
-
371
- // Trigger before close event
372
- const beforeCloseEvent = { dialog: component, defaultPrevented: false, preventDefault: () => { beforeCloseEvent.defaultPrevented = true; } };
373
- component.events.trigger(DIALOG_EVENTS.BEFORE_CLOSE, beforeCloseEvent);
374
-
375
- // If event was prevented, don't close
376
- if (beforeCloseEvent.defaultPrevented) return;
377
-
378
- // Hide the dialog
379
- component.element.classList.remove(`${component.getClass('dialog')}--visible`);
380
-
381
- // Release focus trap and cleanup events
382
- releaseFocus();
383
- cleanupEvents();
384
-
385
- // Trigger close event
386
- component.events.trigger(DIALOG_EVENTS.CLOSE, { dialog: component });
387
-
388
- // Hide the overlay after animation completes
389
- setTimeout(() => {
390
- component.overlay.classList.remove(`${component.getClass('dialog-overlay')}--visible`);
391
-
392
- // Trigger after close event
393
- component.events.trigger(DIALOG_EVENTS.AFTER_CLOSE, { dialog: component });
394
- }, animationDuration);
395
- },
508
+ // Trigger before close event
509
+ const beforeCloseEvent = {
510
+ dialog: component,
511
+ defaultPrevented: false,
512
+ preventDefault: () => { beforeCloseEvent.defaultPrevented = true; }
513
+ };
396
514
 
397
- toggle(open?: boolean) {
398
- if (open === undefined) {
399
- // Toggle based on current state
400
- if (this.isOpen()) {
401
- this.close();
402
- } else {
403
- this.open();
404
- }
405
- } else if (open) {
406
- this.open();
407
- } else {
408
- this.close();
409
- }
410
- },
515
+ if (typeof component.emit === 'function') {
516
+ component.emit(DIALOG_EVENTS.BEFORE_CLOSE, beforeCloseEvent);
517
+ }
518
+
519
+ // If event was prevented, don't close
520
+ if (beforeCloseEvent.defaultPrevented) {
521
+ console.log('Dialog close prevented by event handler');
522
+ return;
523
+ }
524
+
525
+ // Get class names
526
+ const dialogVisibleClass = `${component.getClass('dialog')}--visible`;
527
+ const overlayVisibleClass = `${component.getClass('dialog-overlay')}--visible`;
528
+
529
+ // Remove dialog visible class
530
+ removeClass(component.element, dialogVisibleClass);
531
+
532
+ // Remove overlay visible class
533
+ removeClass(component.overlay, overlayVisibleClass);
534
+
535
+ // Release focus and cleanup events
536
+ releaseFocus();
537
+ cleanupEvents();
411
538
 
412
- isOpen() {
413
- return component.element.classList.contains(`${component.getClass('dialog')}--visible`);
539
+ // Trigger close events
540
+ if (typeof component.emit === 'function') {
541
+ component.emit(DIALOG_EVENTS.CLOSE, { dialog: component });
542
+ }
543
+
544
+ // Remove from DOM after animation completes
545
+ setTimeout(() => {
546
+ if (component.overlay && component.overlay.parentNode) {
547
+ component.overlay.parentNode.removeChild(component.overlay);
548
+ }
549
+
550
+ if (typeof component.emit === 'function') {
551
+ component.emit(DIALOG_EVENTS.AFTER_CLOSE, { dialog: component });
552
+ }
553
+ }, animationDuration);
554
+ },
555
+
556
+ toggle(open?: boolean) {
557
+ if (open === undefined) {
558
+ this.isOpen() ? this.close() : this.open();
559
+ } else if (open) {
560
+ this.open();
561
+ } else {
562
+ this.close();
414
563
  }
415
564
  },
416
565
 
417
- focus: {
418
- trapFocus,
419
- releaseFocus
566
+ isOpen() {
567
+ return component.element.classList.contains(`${component.getClass('dialog')}--visible`);
420
568
  }
421
569
  };
570
+
571
+ // Set up event listener for the dialog:close event
572
+ if (component && component.on) {
573
+ component.on('dialog:close', () => {
574
+ visibility.close();
575
+ });
576
+ }
577
+
578
+ return {
579
+ ...component,
580
+ visibility
581
+ };
422
582
  };
423
583
 
424
584
  /**
@@ -560,7 +720,7 @@ export const withButtons = () => component => {
560
720
  // Apply footer alignment
561
721
  const alignment = component.config.footerAlignment || DIALOG_FOOTER_ALIGNMENTS.RIGHT;
562
722
  if (alignment !== DIALOG_FOOTER_ALIGNMENTS.RIGHT) {
563
- footer.classList.add(`${component.getClass('dialog-footer')}--${alignment}`);
723
+ addClass(footer, `${component.getClass('dialog-footer')}--${alignment}`);
564
724
  }
565
725
 
566
726
  component.element.appendChild(footer);
@@ -620,13 +780,13 @@ export const withButtons = () => component => {
620
780
  // Remove existing alignment classes
621
781
  Object.values(DIALOG_FOOTER_ALIGNMENTS).forEach(align => {
622
782
  if (align !== DIALOG_FOOTER_ALIGNMENTS.RIGHT) {
623
- component.structure.footer.classList.remove(`${component.getClass('dialog-footer')}--${align}`);
783
+ removeClass(component.structure.footer, `${component.getClass('dialog-footer')}--${align}`);
624
784
  }
625
785
  });
626
786
 
627
787
  // Add new alignment class if not right (default)
628
788
  if (alignment !== DIALOG_FOOTER_ALIGNMENTS.RIGHT) {
629
- component.structure.footer.classList.add(`${component.getClass('dialog-footer')}--${alignment}`);
789
+ addClass(component.structure.footer, `${component.getClass('dialog-footer')}--${alignment}`);
630
790
  }
631
791
  }
632
792
  }
@@ -648,12 +808,12 @@ export const withSize = () => component => {
648
808
  setSize(size: keyof typeof DIALOG_SIZES | DIALOG_SIZES) {
649
809
  // Remove existing size classes
650
810
  Object.values(DIALOG_SIZES).forEach(sizeValue => {
651
- component.element.classList.remove(`${component.getClass('dialog')}--${sizeValue}`);
811
+ removeClass(component.element, `${component.getClass('dialog')}--${sizeValue}`);
652
812
  });
653
813
 
654
814
  // Add new size class if not medium (default)
655
815
  if (size !== DIALOG_SIZES.MEDIUM) {
656
- component.element.classList.add(`${component.getClass('dialog')}--${size}`);
816
+ addClass(component.element, `${component.getClass('dialog')}--${size}`);
657
817
  }
658
818
  }
659
819
  }
@@ -54,10 +54,7 @@ export interface DialogConfig {
54
54
  buttons?: DialogButton[];
55
55
 
56
56
  /** Whether to show a divider between header and content */
57
- headerDivider?: boolean;
58
-
59
- /** Whether to show a divider between content and footer */
60
- footerDivider?: boolean;
57
+ divider?: boolean;
61
58
 
62
59
  /** Dialog z-index (defaults to 1000) */
63
60
  zIndex?: number;
@@ -187,6 +184,12 @@ export interface DialogComponent {
187
184
  /** Gets dialog footer element */
188
185
  getFooterElement: () => HTMLElement | null;
189
186
 
187
+ /** Shows or hides the divider */
188
+ toggleDivider: (show: boolean) => DialogComponent;
189
+
190
+ /** Checks if the dialog has a divider */
191
+ hasDivider: () => boolean;
192
+
190
193
  /** Creates a confirmation dialog with Yes/No buttons */
191
194
  confirm: (options?: DialogConfirmOptions) => Promise<boolean>;
192
195
 
@@ -0,0 +1,57 @@
1
+ // src/components/divider/_styles.scss
2
+
3
+ @use '../../styles/abstract/base' as base;
4
+ @use '../../styles/abstract/variables' as v;
5
+ @use '../../styles/abstract/functions' as f;
6
+ @use '../../styles/abstract/mixins' as m;
7
+ @use '../../styles/abstract/theme' as t;
8
+
9
+ $component: '#{base.$prefix}-divider';
10
+
11
+ .#{$component} {
12
+ // Base styles
13
+ display: block;
14
+ border: none;
15
+ margin: 0;
16
+ padding: 0;
17
+ background-color: t.color('outline-variant');
18
+
19
+ // Orientation variants
20
+ &--horizontal {
21
+ width: 100%;
22
+ height: 1px;
23
+ }
24
+
25
+ &--vertical {
26
+ height: 100%;
27
+ width: 1px;
28
+ display: inline-block;
29
+ }
30
+
31
+ // Variant styles (these set the base for the variants, specific insets are handled via JS)
32
+ &--full-width {
33
+ width: 100%;
34
+ }
35
+
36
+ &--inset {
37
+ // Base class for inset styles - specific margins are set via JS
38
+ }
39
+
40
+ &--middle-inset {
41
+ // Base class for middle inset styles - specific margins are set via JS
42
+ }
43
+
44
+ // Parent-child relationship context
45
+ .#{base.$prefix}-list & {
46
+ margin: 8px 0;
47
+ }
48
+
49
+ .#{base.$prefix}-card & {
50
+ margin: 8px 0;
51
+ }
52
+
53
+ // Dark theme support
54
+ :root[data-theme-mode="dark"] & {
55
+ // Dark theme color is handled through the CSS variable in theme
56
+ }
57
+ }