@graupl/graupl 1.0.0-alpha.15 → 1.0.0-alpha.16

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 (167) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/build.js +7 -0
  3. package/dist/css/base/button.css +2 -0
  4. package/dist/css/base/button.css.map +1 -0
  5. package/dist/css/base/form.css.map +1 -0
  6. package/dist/css/base/link.css.map +1 -0
  7. package/dist/css/base/table.css.map +1 -0
  8. package/dist/css/base.css +2 -0
  9. package/dist/css/base.css.map +1 -0
  10. package/dist/css/component/accordion.css +2 -0
  11. package/dist/css/component/accordion.css.map +1 -0
  12. package/dist/css/component/alert.css +2 -0
  13. package/dist/css/component/alert.css.map +1 -0
  14. package/dist/css/component/card.css.map +1 -0
  15. package/dist/css/component/carousel.css +2 -0
  16. package/dist/css/component/carousel.css.map +1 -0
  17. package/dist/css/component/input-group.css.map +1 -0
  18. package/dist/css/component/menu.css.map +1 -0
  19. package/dist/css/component/navigation.css.map +1 -0
  20. package/dist/css/component.css +2 -0
  21. package/dist/css/component.css.map +1 -0
  22. package/dist/css/graupl.css +2 -0
  23. package/dist/css/graupl.css.map +1 -0
  24. package/dist/css/init.css.map +1 -0
  25. package/dist/css/layout/columns.css.map +1 -0
  26. package/dist/css/layout/container.css.map +1 -0
  27. package/dist/css/layout/flex-columns.css.map +1 -0
  28. package/dist/css/layout.css +2 -0
  29. package/dist/css/layout.css.map +1 -0
  30. package/dist/css/normalize.css.map +1 -0
  31. package/dist/css/state/focus.css +2 -0
  32. package/dist/css/state/focus.css.map +1 -0
  33. package/dist/css/state.css +2 -0
  34. package/dist/css/state.css.map +1 -0
  35. package/dist/css/theme/color.css.map +1 -0
  36. package/dist/css/theme/typography.css.map +1 -0
  37. package/dist/css/theme.css.map +1 -0
  38. package/dist/css/utilities/alignment.css.map +1 -0
  39. package/dist/css/utilities/color.css.map +1 -0
  40. package/dist/css/utilities/display.css.map +1 -0
  41. package/dist/css/utilities/flex.css.map +1 -0
  42. package/dist/css/utilities/height.css.map +1 -0
  43. package/dist/css/utilities/inset.css.map +1 -0
  44. package/dist/css/utilities/justification.css.map +1 -0
  45. package/dist/css/utilities/list.css.map +1 -0
  46. package/dist/css/utilities/order.css.map +1 -0
  47. package/dist/css/utilities/postion.css.map +1 -0
  48. package/dist/css/utilities/spacing.css.map +1 -0
  49. package/dist/css/utilities/typography.css.map +1 -0
  50. package/dist/css/utilities/visibility.css.map +1 -0
  51. package/dist/css/utilities/width.css.map +1 -0
  52. package/dist/css/utilities.css.map +1 -0
  53. package/dist/js/component/accordion.cjs.js +3 -0
  54. package/dist/js/component/accordion.esm.js +1289 -0
  55. package/dist/js/component/accordion.iife.js +3 -0
  56. package/dist/js/component/alert.cjs.js +3 -0
  57. package/dist/js/component/alert.esm.js +529 -0
  58. package/dist/js/component/alert.iife.js +3 -0
  59. package/dist/js/component/carousel.cjs.js +3 -0
  60. package/dist/js/component/carousel.esm.js +1110 -0
  61. package/dist/js/component/carousel.iife.js +3 -0
  62. package/dist/js/graupl.cjs.js +5 -0
  63. package/dist/js/graupl.esm.js +1462 -0
  64. package/dist/js/graupl.iife.js +5 -0
  65. package/index.html +56 -2
  66. package/index.js +12 -0
  67. package/package.json +26 -5
  68. package/scss/component/accordion.scss +3 -0
  69. package/src/js/accordion/Accordion.js +1163 -0
  70. package/src/js/accordion/AccordionItem.js +496 -0
  71. package/src/js/accordion/index.js +10 -0
  72. package/src/js/alert/Alert.js +71 -1
  73. package/src/js/alert/index.js +1 -11
  74. package/src/js/carousel/Carousel.js +67 -16
  75. package/src/js/carousel/index.js +1 -11
  76. package/src/js/eventHandlers.js +7 -0
  77. package/src/js/storage.js +106 -0
  78. package/src/scss/_defaults.scss +29 -0
  79. package/src/scss/base/button/_mixins.scss +64 -62
  80. package/src/scss/component/_index.scss +1 -0
  81. package/src/scss/component/accordion/_defaults.scss +40 -0
  82. package/src/scss/component/accordion/_index.scss +180 -0
  83. package/src/scss/component/accordion/_variables.scss +304 -0
  84. package/src/scss/component/carousel/_index.scss +6 -0
  85. package/src/scss/layout/columns/_index.scss +1 -1
  86. package/src/scss/layout/flex-columns/_index.scss +1 -1
  87. package/src/scss/state/focus/_index.scss +6 -6
  88. package/src/scss/state/focus/_mixins.scss +15 -0
  89. package/vite.config.js +57 -0
  90. package/dist/base/button.css +0 -2
  91. package/dist/base/button.css.map +0 -1
  92. package/dist/base/form.css.map +0 -1
  93. package/dist/base/link.css.map +0 -1
  94. package/dist/base/table.css.map +0 -1
  95. package/dist/base.css +0 -2
  96. package/dist/base.css.map +0 -1
  97. package/dist/component/alert.css +0 -2
  98. package/dist/component/alert.css.map +0 -1
  99. package/dist/component/card.css.map +0 -1
  100. package/dist/component/carousel.css +0 -2
  101. package/dist/component/carousel.css.map +0 -1
  102. package/dist/component/input-group.css.map +0 -1
  103. package/dist/component/menu.css.map +0 -1
  104. package/dist/component/navigation.css.map +0 -1
  105. package/dist/component.css +0 -2
  106. package/dist/component.css.map +0 -1
  107. package/dist/graupl.css +0 -2
  108. package/dist/graupl.css.map +0 -1
  109. package/dist/init.css.map +0 -1
  110. package/dist/layout/columns.css.map +0 -1
  111. package/dist/layout/container.css.map +0 -1
  112. package/dist/layout/flex-columns.css.map +0 -1
  113. package/dist/layout.css +0 -2
  114. package/dist/layout.css.map +0 -1
  115. package/dist/normalize.css.map +0 -1
  116. package/dist/state/focus.css +0 -2
  117. package/dist/state/focus.css.map +0 -1
  118. package/dist/state.css +0 -2
  119. package/dist/state.css.map +0 -1
  120. package/dist/theme/color.css.map +0 -1
  121. package/dist/theme/typography.css.map +0 -1
  122. package/dist/theme.css.map +0 -1
  123. package/dist/utilities/alignment.css.map +0 -1
  124. package/dist/utilities/color.css.map +0 -1
  125. package/dist/utilities/display.css.map +0 -1
  126. package/dist/utilities/flex.css.map +0 -1
  127. package/dist/utilities/height.css.map +0 -1
  128. package/dist/utilities/inset.css.map +0 -1
  129. package/dist/utilities/justification.css.map +0 -1
  130. package/dist/utilities/list.css.map +0 -1
  131. package/dist/utilities/order.css.map +0 -1
  132. package/dist/utilities/postion.css.map +0 -1
  133. package/dist/utilities/spacing.css.map +0 -1
  134. package/dist/utilities/typography.css.map +0 -1
  135. package/dist/utilities/visibility.css.map +0 -1
  136. package/dist/utilities/width.css.map +0 -1
  137. package/dist/utilities.css.map +0 -1
  138. /package/dist/{base → css/base}/form.css +0 -0
  139. /package/dist/{base → css/base}/link.css +0 -0
  140. /package/dist/{base → css/base}/table.css +0 -0
  141. /package/dist/{component → css/component}/card.css +0 -0
  142. /package/dist/{component → css/component}/input-group.css +0 -0
  143. /package/dist/{component → css/component}/menu.css +0 -0
  144. /package/dist/{component → css/component}/navigation.css +0 -0
  145. /package/dist/{init.css → css/init.css} +0 -0
  146. /package/dist/{layout → css/layout}/columns.css +0 -0
  147. /package/dist/{layout → css/layout}/container.css +0 -0
  148. /package/dist/{layout → css/layout}/flex-columns.css +0 -0
  149. /package/dist/{normalize.css → css/normalize.css} +0 -0
  150. /package/dist/{theme → css/theme}/color.css +0 -0
  151. /package/dist/{theme → css/theme}/typography.css +0 -0
  152. /package/dist/{theme.css → css/theme.css} +0 -0
  153. /package/dist/{utilities → css/utilities}/alignment.css +0 -0
  154. /package/dist/{utilities → css/utilities}/color.css +0 -0
  155. /package/dist/{utilities → css/utilities}/display.css +0 -0
  156. /package/dist/{utilities → css/utilities}/flex.css +0 -0
  157. /package/dist/{utilities → css/utilities}/height.css +0 -0
  158. /package/dist/{utilities → css/utilities}/inset.css +0 -0
  159. /package/dist/{utilities → css/utilities}/justification.css +0 -0
  160. /package/dist/{utilities → css/utilities}/list.css +0 -0
  161. /package/dist/{utilities → css/utilities}/order.css +0 -0
  162. /package/dist/{utilities → css/utilities}/postion.css +0 -0
  163. /package/dist/{utilities → css/utilities}/spacing.css +0 -0
  164. /package/dist/{utilities → css/utilities}/typography.css +0 -0
  165. /package/dist/{utilities → css/utilities}/visibility.css +0 -0
  166. /package/dist/{utilities → css/utilities}/width.css +0 -0
  167. /package/dist/{utilities.css → css/utilities.css} +0 -0
@@ -0,0 +1,496 @@
1
+ /**
2
+ * @file
3
+ * The Accordion Item class.
4
+ */
5
+
6
+ /* global Accordion */
7
+
8
+ import { isTag, isValidType } from "../validate.js";
9
+ import { addClass, removeClass } from "../domHelpers.js";
10
+
11
+ /**
12
+ * Creates a new accordion item.
13
+ *
14
+ * @class
15
+ */
16
+ class AccordionItem {
17
+ /**
18
+ * The HTML elements for the accordion item in the DOM.
19
+ *
20
+ * @protected
21
+ *
22
+ * @type {Object<HTMLElement>}
23
+ *
24
+ * @property {HTMLElement} item - The accordion item element.
25
+ * @property {HTMLElement} toggle - The controller element.
26
+ * @property {HTMLElement} header - The header element.
27
+ * @property {HTMLElement} content - The content element.
28
+ */
29
+ _dom = {
30
+ item: null,
31
+ toggle: null,
32
+ header: null,
33
+ content: null,
34
+ };
35
+
36
+ /**
37
+ * The declared graupl accordion elements within the accordion item.
38
+ *
39
+ * @protected
40
+ *
41
+ * @type {Object<Accordion>}
42
+ *
43
+ * @property {Accordion} parentAccordion - The parent accordion containing this item.
44
+ */
45
+ _elements = {
46
+ parentAccordion: null,
47
+ };
48
+
49
+ /**
50
+ * The open state of the accordion.
51
+ *
52
+ * @protected
53
+ *
54
+ * @type {boolean}
55
+ */
56
+ _open = false;
57
+
58
+ /**
59
+ * The locked state of the accordions item.
60
+ *
61
+ * If locked, the accordion item cannot be closed.
62
+ *
63
+ * @protected
64
+ *
65
+ * @type {boolean}
66
+ */
67
+ _locked = false;
68
+
69
+ /**
70
+ * The event that is triggered when the accordion item is shown.
71
+ *
72
+ * @protected
73
+ *
74
+ * @event grauplAccordionItemExpand
75
+ *
76
+ * @type {CustomEvent}
77
+ *
78
+ * @property {boolean} bubbles - A flag to bubble the event.
79
+ * @property {Object<AccordionItem>} detail - The details object containing the Accordion item itself.
80
+ */
81
+ _expandEvent = new CustomEvent("grauplAccordionItemExpand", {
82
+ bubbles: true,
83
+ detail: { item: this },
84
+ });
85
+
86
+ /**
87
+ * The event that is triggered when the accordion item is hidden.
88
+ *
89
+ * @protected
90
+ *
91
+ * @event grauplAccordionItemCollapse
92
+ *
93
+ * @type {CustomEvent}
94
+ *
95
+ * @property {boolean} bubbles - A flag to bubble the event.
96
+ * @property {Object<AccordionItem>} detail - The details object containing the Accordion item itself.
97
+ */
98
+ _collapseEvent = new CustomEvent("grauplAccordionItemCollapse", {
99
+ bubbles: true,
100
+ detail: { item: this },
101
+ });
102
+
103
+ /**
104
+ * Constructs a new Accordion item object.
105
+ *
106
+ * @class
107
+ *
108
+ * @param {object} options - The options object.
109
+ * @param {HTMLElement} options.accordionItemElement - The accordion item element.
110
+ * @param {HTMLElement} options.accordionItemToggleElement - The toggle element.
111
+ * @param {HTMLElement} options.accordionItemHeaderElement - The header element.
112
+ * @param {HTMLElement} options.accordionItemContentElement - The content element.
113
+ * @param {Accordion} [options.parentAccordion = null] - The accordion containing this item.
114
+ */
115
+ constructor({
116
+ accordionItemElement,
117
+ accordionItemToggleElement,
118
+ accordionItemHeaderElement,
119
+ accordionItemContentElement,
120
+ parentAccordion = null,
121
+ }) {
122
+ // Set DOM elements.
123
+ this._dom.item = accordionItemElement;
124
+ this._dom.toggle = accordionItemToggleElement;
125
+ this._dom.header = accordionItemHeaderElement;
126
+ this._dom.content = accordionItemContentElement;
127
+
128
+ // Set the accordion elements.
129
+ this._elements.parentAccordion = parentAccordion;
130
+ }
131
+
132
+ /**
133
+ * Initializes the accordion item.
134
+ */
135
+ initialize() {
136
+ // Set the IDs for the accordion item and it's elements if they don't exist.
137
+ this._setIds();
138
+
139
+ // Set the ARIA attributes for the accordion item and it's elements.
140
+ this._setAriaAttributes();
141
+
142
+ // Set the initial state of the accordion item.
143
+ if (this.dom.toggle.getAttribute("aria-expanded") === "true") {
144
+ this.show(false, false);
145
+ } else {
146
+ this.hide(false, false);
147
+ }
148
+ }
149
+
150
+ /**
151
+ * The HTML elements for the accordion item in the DOM.
152
+ *
153
+ * @readonly
154
+ *
155
+ * @type {object}
156
+ *
157
+ * @see _dom
158
+ */
159
+ get dom() {
160
+ return this._dom;
161
+ }
162
+
163
+ /**
164
+ * The declared graupl accordion elements within the accordion item.
165
+ *
166
+ * @readonly
167
+ *
168
+ * @type {Object<Accordion>}
169
+ *
170
+ * @see _elements
171
+ */
172
+ get elements() {
173
+ return this._elements;
174
+ }
175
+
176
+ /**
177
+ * The open state of the accordion.
178
+ *
179
+ * @readonly
180
+ *
181
+ * @type {object}
182
+ *
183
+ * @see _open
184
+ */
185
+ get isOpen() {
186
+ return this._open;
187
+ }
188
+
189
+ /**
190
+ * The locked state of the accordions item.
191
+ *
192
+ * If locked, the accordion item cannot be closed.
193
+ *
194
+ * @readonly
195
+ *
196
+ * @type {boolean}
197
+ *
198
+ * @see _locked
199
+ */
200
+ get isLocked() {
201
+ return this._locked;
202
+ }
203
+
204
+ set isOpen(value) {
205
+ isValidType("boolean", { value });
206
+
207
+ if (this._open !== value) {
208
+ this._open = value;
209
+ }
210
+ }
211
+
212
+ /**
213
+ * Sets the IDs for the accordion item and it's elements if they don't exist.
214
+ *
215
+ * The generated IDs use the parent accordion's key and follows the pattern:
216
+ * - Accordion item: `accordion-item-{key}-{index}`
217
+ * - Accordion item toggle: `accordion-item-toggle-{key}-{index}`
218
+ * - Accordion item content: `accordion-item-content-{key}-{index}`
219
+ */
220
+ _setIds() {
221
+ // Get the required information for IDs.
222
+ const { key } = this.elements.parentAccordion;
223
+ const index = this.elements.parentAccordion.dom.accordionItems.indexOf(
224
+ this.dom.item
225
+ );
226
+
227
+ this.dom.item.id = this.dom.item.id || `accordion-item-${key}-${index}`;
228
+ this.dom.toggle.id =
229
+ this.dom.toggle.id || `accordion-item-toggle-${key}-${index}`;
230
+ this.dom.header.id =
231
+ this.dom.header.id || `accordion-item-header-${key}-${index}`;
232
+ this.dom.content.id =
233
+ this.dom.content.id || `accordion-item-content-${key}-${index}`;
234
+ }
235
+
236
+ /**
237
+ * Sets the ARIA attributes for the accordion item and it's elements.
238
+ */
239
+ _setAriaAttributes() {
240
+ // Set the ARIA attributes for the accordion item toggle.
241
+ // If the toggle is not a button, then set the role to "button".
242
+ if (!isTag("button", { toggle: this.dom.toggle })) {
243
+ this.dom.toggle.setAttribute("role", "button");
244
+ }
245
+
246
+ // If aria-expanded is not explicitly set to "true", then set it to "false".
247
+ if (this.dom.toggle.getAttribute("aria-expanded") !== "true") {
248
+ this.dom.toggle.setAttribute("aria-expanded", "false");
249
+ }
250
+
251
+ // Set the aria-controls attribute for the toggle.
252
+ this.dom.toggle.setAttribute("aria-controls", this.dom.content.id);
253
+
254
+ // Set the ARIA attributes for the accordion item content.
255
+ // If the content is not a section, then set the role to "region".
256
+ if (!isTag("section", { content: this.dom.content })) {
257
+ this.dom.content.setAttribute("role", "region");
258
+ }
259
+
260
+ // Set the aria-labelledby attribute for the content.
261
+ this.dom.content.setAttribute("aria-labelledby", this.dom.toggle.id);
262
+ }
263
+
264
+ /**
265
+ * Shows the accordion item.
266
+ *
267
+ * @public
268
+ *
269
+ * @fires grauplAccordionItemExpand
270
+ *
271
+ * @param {boolean} [emit = true] - Emit the show event once shown.
272
+ * @param {boolean} [transition = true] - Respect the transition class.
273
+ */
274
+ show(emit = true, transition = true) {
275
+ if (this._open) {
276
+ return;
277
+ }
278
+
279
+ const { closeClass, openClass, transitionClass, openDuration } =
280
+ this.elements.parentAccordion;
281
+
282
+ // Set aria-expanded to true when hiding accordion item.
283
+ this.dom.toggle.setAttribute("aria-expanded", "true");
284
+
285
+ // If we're dealing with transition classes, then we need to utilize
286
+ // requestAnimationFrame to add the transition class, remove the hide class,
287
+ // add the show class, and finally remove the transition class.
288
+ //
289
+ // If `transition` is false, then it doesn't matter if the transition class
290
+ // is set. Do not use the transition.
291
+ if (transition && transitionClass !== "") {
292
+ addClass(transitionClass, this.dom.item);
293
+
294
+ requestAnimationFrame(() => {
295
+ removeClass(closeClass, this.dom.item);
296
+
297
+ this.dom.item.style.height = `${this.dom.header.getBoundingClientRect().height}px`;
298
+
299
+ requestAnimationFrame(() => {
300
+ addClass(openClass, this.dom.item);
301
+
302
+ this.dom.item.style.height = `${this.dom.header.getBoundingClientRect().height + this.dom.content.getBoundingClientRect().height}px`;
303
+
304
+ requestAnimationFrame(() => {
305
+ setTimeout(() => {
306
+ removeClass(transitionClass, this.dom.item);
307
+
308
+ this.dom.item.style.height = "";
309
+ }, openDuration);
310
+ });
311
+ });
312
+ });
313
+ } else {
314
+ // Add the show class
315
+ addClass(openClass, this.dom.item);
316
+
317
+ // Remove the hide class.
318
+ removeClass(closeClass, this.dom.item);
319
+ }
320
+
321
+ this._open = true;
322
+
323
+ // If the parent accordion only allows a single item to be open at a time,
324
+ // then close all other items.
325
+ if (!this.elements.parentAccordion.allowMultipleExpand) {
326
+ this.unlockSiblings();
327
+ this.closeSiblings();
328
+ }
329
+
330
+ // If the parent accordion requires at least one item to be open, and this
331
+ // is the only open item, then lock it. Otherwise, unlock all siblings.
332
+ if (!this.elements.parentAccordion.allowNoExpand) {
333
+ if (this.elements.parentAccordion.openAccordionItems.length <= 1) {
334
+ this.lock();
335
+ } else {
336
+ this.unlockSiblings();
337
+ }
338
+ }
339
+
340
+ if (emit) {
341
+ this.dom.item.dispatchEvent(this._expandEvent);
342
+ }
343
+ }
344
+
345
+ /**
346
+ * Hides the accordion item.
347
+ *
348
+ * @public
349
+ *
350
+ * @fires grauplAccordionItemCollapse
351
+ *
352
+ * @param {boolean} [emit = true] - Emit the show event once shown.
353
+ * @param {boolean} [transition = true] - Respect the transition class.
354
+ */
355
+ hide(emit = true, transition = true) {
356
+ if (!this._open) {
357
+ return;
358
+ }
359
+
360
+ if (
361
+ !this.elements.parentAccordion.allowNoExpand &&
362
+ this.elements.parentAccordion.openAccordionItems.length <= 1
363
+ ) {
364
+ return;
365
+ }
366
+
367
+ const { closeClass, openClass, transitionClass, closeDuration } =
368
+ this.elements.parentAccordion;
369
+
370
+ // Set aria-expanded to false when hiding accordion item.
371
+ this.dom.toggle.setAttribute("aria-expanded", "false");
372
+
373
+ // If we're dealing with transition classes, then we need to utilize
374
+ // requestAnimationFrame to add the transition class, remove the show class,
375
+ // add the hide class, and finally remove the transition class.
376
+ //
377
+ // If `transition` is false, then it doesn't matter if the transition class
378
+ // is set. Do not use the transition.
379
+ if (transition && transitionClass !== "") {
380
+ addClass(transitionClass, this.dom.item);
381
+ this.dom.item.style.height = `${this.dom.item.getBoundingClientRect().height}px`;
382
+
383
+ requestAnimationFrame(() => {
384
+ removeClass(openClass, this.dom.item);
385
+ this.dom.item.style.height = `${this.dom.header.getBoundingClientRect().height}px`;
386
+
387
+ requestAnimationFrame(() => {
388
+ addClass(closeClass, this.dom.item);
389
+
390
+ requestAnimationFrame(() => {
391
+ setTimeout(() => {
392
+ removeClass(transitionClass, this.dom.item);
393
+
394
+ this.dom.item.style.height = "";
395
+ }, closeDuration);
396
+ });
397
+ });
398
+ });
399
+ } else {
400
+ // Add the hide class
401
+ addClass(closeClass, this.dom.item);
402
+
403
+ // Remove the show class.
404
+ removeClass(openClass, this.dom.item);
405
+ }
406
+
407
+ this._open = false;
408
+
409
+ // If the parent accordion requires at least one item to be open, and this was
410
+ // the second to last open item, then lock to last open item.
411
+ if (
412
+ !this.elements.parentAccordion.allowNoExpand &&
413
+ this.elements.parentAccordion.openAccordionItems.length === 1
414
+ ) {
415
+ this.elements.parentAccordion.openAccordionItems[0].lock();
416
+ }
417
+
418
+ if (emit) {
419
+ this.dom.item.dispatchEvent(this._collapseEvent);
420
+ }
421
+ }
422
+
423
+ /**
424
+ * Toggle the accordion item.
425
+ *
426
+ * @public
427
+ */
428
+ toggle() {
429
+ this.isOpen ? this.hide() : this.show();
430
+ }
431
+
432
+ /**
433
+ * Focuses the accordion item.
434
+ *
435
+ * @public
436
+ */
437
+ focus() {
438
+ this.dom.toggle.focus();
439
+ }
440
+
441
+ /**
442
+ * Blurs the accordion item.
443
+ *
444
+ * @public
445
+ */
446
+ blur() {
447
+ this.dom.toggle.blur();
448
+ }
449
+
450
+ /**
451
+ * Locks the accordion item.
452
+ *
453
+ * @public
454
+ */
455
+ lock() {
456
+ this._locked = true;
457
+ this.dom.toggle.setAttribute("disabled", "true");
458
+ }
459
+
460
+ /**
461
+ * Unlocks the accordion item.
462
+ *
463
+ * @public
464
+ */
465
+ unlock() {
466
+ this._locked = false;
467
+ this.dom.toggle.removeAttribute("disabled");
468
+ }
469
+
470
+ closeSiblings() {
471
+ if (this.elements.parentAccordion) {
472
+ this.elements.parentAccordion.elements.accordionItems.forEach((item) => {
473
+ if (item !== this) {
474
+ item.hide();
475
+ }
476
+ });
477
+ }
478
+ }
479
+
480
+ /**
481
+ * Unlocks the siblings of the accordion item.
482
+ *
483
+ * @public
484
+ */
485
+ unlockSiblings() {
486
+ if (this.elements.parentAccordion) {
487
+ this.elements.parentAccordion.elements.accordionItems.forEach((item) => {
488
+ if (item !== this) {
489
+ item.unlock();
490
+ }
491
+ });
492
+ }
493
+ }
494
+ }
495
+
496
+ export default AccordionItem;
@@ -0,0 +1,10 @@
1
+ import Accordion from "./Accordion.js";
2
+
3
+ document.addEventListener("DOMContentLoaded", () => {
4
+ document.querySelectorAll(".accordion").forEach((accordionElement) => {
5
+ new Accordion({
6
+ accordionElement,
7
+ initialize: true,
8
+ });
9
+ });
10
+ });
@@ -6,6 +6,7 @@
6
6
  import { isValidClassList, isValidInstance, isValidType } from "../validate.js";
7
7
  import { addClass, removeClass } from "../domHelpers.js";
8
8
  import { keyPress, preventEvent } from "../eventHandlers.js";
9
+ import storage from "../storage.js";
9
10
 
10
11
  class Alert {
11
12
  /**
@@ -65,6 +66,15 @@ class Alert {
65
66
  */
66
67
  _transitionTimer = 150;
67
68
 
69
+ /**
70
+ * The key used to generate IDs throughout the carousel.
71
+ *
72
+ * @protected
73
+ *
74
+ * @type {string}
75
+ */
76
+ _key = "";
77
+
68
78
  /**
69
79
  * An array of error messages generated by the alert.
70
80
  *
@@ -83,7 +93,7 @@ class Alert {
83
93
  *
84
94
  * @type {CustomEvent}
85
95
  *
86
- * @property {boolean} bubbles - A flag to buggle the event.
96
+ * @property {boolean} bubbles - A flag to bubble the event.
87
97
  * @property {Object<Alert>} detail - The details object container the Alert itself.
88
98
  */
89
99
  _showEvent = new CustomEvent("grauplAlertShow", {
@@ -119,6 +129,7 @@ class Alert {
119
129
  * @param {string|string[]|null} [options.transitionClass = transitioning] - The class to add when the alert is transitioning between shown and hidden.
120
130
  * @param {number} [options.transitionTimer = 150] - The time in milliseconds the transition will take.
121
131
  * @param {boolean} [options.isHidden = false] - A flag to determine the initial state of the alert.
132
+ * @param {?string} [options.key = null] - The key used to generate IDs throughout the alert.
122
133
  * @param {boolean} [options.initialize = false] - AA flag to initialize the alert immediately upon creation.
123
134
  */
124
135
  constructor({
@@ -129,6 +140,7 @@ class Alert {
129
140
  transitionClass = "transitioning",
130
141
  transitionTimer = 150,
131
142
  isHidden = false,
143
+ key = null,
132
144
  initialize = false,
133
145
  }) {
134
146
  this._dom.alert = alertElement;
@@ -139,6 +151,9 @@ class Alert {
139
151
  this._transitionTimer = transitionTimer;
140
152
  this._hidden = isHidden;
141
153
 
154
+ // Set the key.
155
+ this._key = key || "";
156
+
142
157
  if (initialize) {
143
158
  this.initialize();
144
159
  }
@@ -157,9 +172,18 @@ class Alert {
157
172
  );
158
173
  }
159
174
 
175
+ // Set up the DOM.
176
+ this._generateKey();
177
+ this._setIds();
178
+
179
+ // Handle events.
160
180
  this._handleClick();
161
181
  this._handleKeydown();
162
182
  this._handleKeyup();
183
+
184
+ // Set up the storage.
185
+ storage.initializeStorage("alerts");
186
+ storage.pushToStorage("alerts", this.dom.alert.id, this);
163
187
  } catch (error) {
164
188
  console.error(error);
165
189
  }
@@ -222,6 +246,17 @@ class Alert {
222
246
  return this._transitionTimer;
223
247
  }
224
248
 
249
+ /**
250
+ * The key used to generate IDs throughout the accordion.
251
+ *
252
+ * @type {string}
253
+ *
254
+ * @see _key
255
+ */
256
+ get key() {
257
+ return this._key;
258
+ }
259
+
225
260
  set showClass(value) {
226
261
  isValidClassList({ showClass: value });
227
262
 
@@ -254,6 +289,14 @@ class Alert {
254
289
  }
255
290
  }
256
291
 
292
+ set key(value) {
293
+ isValidType("string", { value });
294
+
295
+ if (this._key !== value) {
296
+ this._key = value;
297
+ }
298
+ }
299
+
257
300
  /**
258
301
  * Validates all aspects of the alert to ensure proper functionality.
259
302
  *
@@ -334,6 +377,33 @@ class Alert {
334
377
  return check;
335
378
  }
336
379
 
380
+ /**
381
+ * Generates a key for the alert.
382
+ *
383
+ * @param {boolean} [regenerate = false] - A flag to determine if the key should be regenerated.
384
+ */
385
+ _generateKey(regenerate = false) {
386
+ if (this.key === "" || regenerate) {
387
+ this.key = Math.random()
388
+ .toString(36)
389
+ .replace(/[^a-z]+/g, "")
390
+ .substring(0, 10);
391
+ }
392
+ }
393
+
394
+ /**
395
+ * Sets the IDs of the alert and it's children if they do not already exist.
396
+ *
397
+ * The generated IDs use the key and follow the format:
398
+ * - alert: `alert-${key}`
399
+ * - controller: `alert-controller-${key}`
400
+ */
401
+ _setIds() {
402
+ this.dom.alert.id = this.dom.alert.id || `alert-${this.key}`;
403
+ this.dom.controller.id =
404
+ this.dom.controller.id || `alert-controller-${this.key}`;
405
+ }
406
+
337
407
  /**
338
408
  * Shows the alert.
339
409
  *
@@ -1,21 +1,11 @@
1
1
  import Alert from "./Alert.js";
2
2
 
3
3
  document.addEventListener("DOMContentLoaded", () => {
4
- const alerts = [];
5
-
6
4
  document.querySelectorAll(".alert").forEach((alertElement) => {
7
- const alert = new Alert({
5
+ new Alert({
8
6
  alertElement,
9
7
  controllerElement: alertElement.querySelector(".alert-dismisser") || null,
10
8
  initialize: true,
11
9
  });
12
-
13
- alerts.push(alert);
14
10
  });
15
-
16
- const graupl = window.Graupl || {};
17
-
18
- graupl.alerts = alerts;
19
-
20
- window.Graupl = graupl;
21
11
  });