@appartmint/mint 0.12.17 → 0.12.28

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 (36) hide show
  1. package/dist/css/mint.css +31 -7
  2. package/dist/css/mint.css.map +1 -1
  3. package/dist/css/mint.min.css +1 -1
  4. package/dist/css/mint.min.css.map +1 -1
  5. package/dist/js/imports/components/header.d.ts +4 -0
  6. package/dist/js/imports/components/header.d.ts.map +1 -1
  7. package/dist/js/imports/util/object.d.ts.map +1 -1
  8. package/dist/js/index.js +22 -19
  9. package/dist/js/index.js.map +1 -1
  10. package/dist/js/index.min.js +1 -1
  11. package/dist/js/index.min.js.map +1 -1
  12. package/dist/js/util.js +2 -2
  13. package/dist/js/util.js.map +1 -1
  14. package/dist/js/util.min.js +1 -1
  15. package/dist/js/util.min.js.map +1 -1
  16. package/package.json +18 -13
  17. package/src/scss/imports/components/_header.scss +31 -6
  18. package/src/scss/imports/global/_global.scss +2 -2
  19. package/src/scss/imports/global/_texture.scss +1 -0
  20. package/src/scss/imports/util/_vars.scss +5 -0
  21. package/src/ts/imports/components/header.ts +378 -0
  22. package/src/ts/imports/enum.ts +9 -0
  23. package/src/ts/imports/models/color.ts +96 -0
  24. package/src/ts/imports/models/item.ts +66 -0
  25. package/src/ts/imports/util/display.ts +7 -0
  26. package/src/ts/imports/util/event.ts +7 -0
  27. package/src/ts/imports/util/icon.ts +67 -0
  28. package/src/ts/imports/util/list.ts +19 -0
  29. package/src/ts/imports/util/math.ts +17 -0
  30. package/src/ts/imports/util/object.ts +141 -0
  31. package/src/ts/imports/util/selectors.ts +230 -0
  32. package/src/ts/imports/util/settings.ts +120 -0
  33. package/src/ts/imports/util/text.ts +47 -0
  34. package/src/ts/imports/util/window.ts +7 -0
  35. package/src/ts/index.ts +33 -0
  36. package/src/ts/util.ts +209 -0
@@ -0,0 +1,378 @@
1
+ /**
2
+ * Imports
3
+ */
4
+ import { mintSide } from '../enum';
5
+ import mintUtil from '../../util';
6
+ import mintSelectors from '../util/selectors';
7
+ import mintSettings from '../util/settings';
8
+
9
+ /**
10
+ * Main header functionality
11
+ * @public
12
+ */
13
+ export class mintHeader {
14
+ /**
15
+ * Last-logged window width
16
+ */
17
+ lastWidth: number = mintUtil.windowWidth();
18
+
19
+ /**
20
+ * Frequently-referenced elements
21
+ */
22
+ el: {[key: string]: HTMLElement | null} = {};
23
+
24
+ /**
25
+ * Initializes and closes the menu
26
+ */
27
+ constructor (settings?: {[key: string]: any}) {
28
+ let defaultSettings: {[key: string]: any} = {
29
+ from: mintSide.Top,
30
+ fixed: true
31
+ };
32
+ mintSettings.set({ ...defaultSettings, ...settings });
33
+
34
+ this.attachElements();
35
+ this.attachEvents();
36
+ this.addClasses();
37
+
38
+ this.setMobileMenu();
39
+ }
40
+
41
+ /**
42
+ * Adds elements to {@link el | `this.el`}
43
+ */
44
+ attachElements () : void {
45
+ this.el.html = document.querySelector('html');
46
+ this.el.body = document.querySelector('body');
47
+ this.el.header = document.getElementById(mintSelectors.getId('header'));
48
+ this.el.mobileButton = this.el.header?.querySelector(mintSelectors.controls(mintSelectors.getId('wrapper'))) || null;
49
+ this.el.wrapper = document.getElementById(mintSelectors.getId('wrapper'));
50
+ }
51
+
52
+ /**
53
+ * Adds events to the dom
54
+ */
55
+ attachEvents () : void {
56
+ //window.addEventListener('resize', mintUtil.throttleEvent(this.eHandleResize.bind(this), mintSettings.delay.default, { trailing: false }));
57
+ window.addEventListener('scroll', mintUtil.throttleEvent(this.eHandleScroll.bind(this), mintSettings.delay.default, { trailing: false }));
58
+
59
+ let focusables: NodeListOf<HTMLElement> | undefined = this.el.header?.querySelectorAll(mintSelectors.focusable),
60
+ lastFocusable: HTMLElement | undefined = focusables?.[focusables?.length - 1];
61
+ lastFocusable?.addEventListener('keydown', mintUtil.throttleEvent(this.eWrapTab.bind(this)));
62
+ focusables?.forEach((focusable: HTMLElement) => {
63
+ focusable.addEventListener('keydown', mintUtil.throttleEvent(this.eHandleKeypress.bind(this)));
64
+ });
65
+
66
+ let menuButtons: NodeListOf<HTMLElement> | undefined = this.el.header?.querySelectorAll(mintSelectors.controls() + mintSelectors.neg(mintSelectors.controls(mintSelectors.ids.wrapper as string)));
67
+ menuButtons?.forEach((menuButton: HTMLElement) => {
68
+ menuButton.addEventListener('click', mintUtil.throttleEvent(this.eToggleMenu.bind(this), mintSettings.delay.slow, { trailing: false }));
69
+ });
70
+
71
+ this.el.mobileButton?.addEventListener('click', mintUtil.throttleEvent(this.eToggleMobileMenu.bind(this), mintSettings.delay.slow, { trailing: false }));
72
+ this.el.wrapper?.addEventListener('transitionend', this.eTransitionEnd.bind(this));
73
+ }
74
+
75
+ /**
76
+ * Adds classes that inform the styles
77
+ */
78
+ addClasses () : void {
79
+ if (mintSettings.fixed) {
80
+ this.el.body?.classList.add(mintSelectors.getClass('fixed'));
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Sets the state of the mobile menu
86
+ * @param open - `true` to open the menu or `false` to close it
87
+ */
88
+ setMobileMenu (open: boolean = false) : void {
89
+ let ariaExpanded: string = open ? 'true' : 'false',
90
+ ariaLabel: string = open ? 'close menu' : 'open menu';
91
+
92
+ this.el.mobileButton?.setAttribute('aria-expanded', ariaExpanded);
93
+ setTimeout(() => {
94
+ this.el.mobileButton?.setAttribute('aria-label', ariaLabel);
95
+ }, mintSettings.delay.fast);
96
+
97
+ if (open) {
98
+ if (mintSettings.fixed !== true) {
99
+ window.scroll({
100
+ top: 0,
101
+ left: 0,
102
+ behavior: 'smooth'
103
+ });
104
+ }
105
+
106
+ setTimeout(() => {
107
+ if (this.el.html) {
108
+ this.el.html.style.overflow = 'hidden';
109
+ }
110
+ }, mintSettings.from === mintSide.Left ? mintSettings.delay.default : mintSettings.delay.instant);
111
+
112
+ if (this.el.wrapper) {
113
+ this.el.wrapper.style.display = 'flex';
114
+ }
115
+
116
+ requestAnimationFrame(() => {
117
+ this.el.wrapper?.classList.add(mintSelectors.getClass('open'));
118
+ });
119
+ } else {
120
+ if (this.el.html) {
121
+ this.el.html.style.overflow = 'auto';
122
+ }
123
+
124
+ requestAnimationFrame(() => {
125
+ this.el.wrapper?.classList.remove(mintSelectors.getClass('open'));
126
+ });
127
+
128
+ this.closeAllMenus();
129
+ }
130
+ }
131
+
132
+ /**
133
+ * Toggles the state of the mobile menu
134
+ */
135
+ toggleMobileMenu () : void {
136
+ this.setMobileMenu(this.el.mobileButton?.getAttribute('aria-expanded')?.toLowerCase() === 'false');
137
+ }
138
+
139
+ /**
140
+ * Sets the state of the provided button's menu
141
+ * @param button - Button element to set
142
+ * @param open - `true` to open the menu or `false` to close it
143
+ */
144
+ setMenu (button?: HTMLElement | null,
145
+ open: boolean = false) : void {
146
+ let ariaExpanded: string = open ? 'true' : 'false',
147
+ menu: HTMLElement | null = button?.nextElementSibling as HTMLElement | null;
148
+ if (button && menu) {
149
+ button.setAttribute('aria-expanded', ariaExpanded);
150
+ if (open) {
151
+ mintUtil.show(menu);
152
+ } else {
153
+ mintUtil.hide(menu);
154
+ this.closeSubMenus(button);
155
+ }
156
+ }
157
+ }
158
+
159
+ /**
160
+ * Toggles the state of the provided button's menu
161
+ * @param button - Button element to toggle
162
+ */
163
+ toggleMenu (button?: HTMLElement | null) : void {
164
+ this.setMenu(button, button?.getAttribute('aria-expanded')?.toLowerCase() !== 'true');
165
+ }
166
+
167
+ /**
168
+ * Closes all submenus of the provided button's menu
169
+ * @param button - Button element of the parent menu
170
+ */
171
+ closeSubMenus (button?: HTMLElement | null) : void {
172
+ let menu: HTMLElement | null | undefined = button?.nextElementSibling as HTMLElement,
173
+ subMenus: NodeListOf<HTMLElement> = menu?.querySelectorAll(mintSelectors.subMenuButtons) as NodeListOf<HTMLElement>;
174
+ subMenus.forEach((child: HTMLElement) => {
175
+ // setMenu calls this function, so ignore subsub menus
176
+ if (child.parentElement?.parentElement === menu) {
177
+ this.setMenu(child);
178
+ }
179
+ });
180
+ }
181
+
182
+ /**
183
+ * Closes all sibling menus of the provided button's menu
184
+ * @param button - Button element of the sibling menus
185
+ */
186
+ closeSiblingMenus (button?: HTMLElement | null) : void {
187
+ let menu: HTMLElement | null | undefined = button?.parentElement as HTMLElement,
188
+ siblingMenus: NodeListOf<HTMLElement> = menu?.parentElement?.querySelectorAll(mintSelectors.subMenuButtons) as NodeListOf<HTMLElement>;
189
+ siblingMenus.forEach((child: HTMLElement) => {
190
+ if (child !== button) {
191
+ this.setMenu(child);
192
+ }
193
+ });
194
+ }
195
+
196
+ /**
197
+ * Closes all submenus of the n4vbar
198
+ */
199
+ closeAllMenus () : void {
200
+ let menuButtons: NodeListOf<HTMLElement> | undefined = this.el.wrapper?.querySelectorAll(mintSelectors.subMenuButtons);
201
+ menuButtons?.forEach((menuButton: HTMLElement) => {
202
+ this.setMenu(menuButton);
203
+ });
204
+ }
205
+
206
+ /**
207
+ * Opens the menu closest to the document's focus
208
+ */
209
+ openClosestMenu () : void {
210
+ let activeButton: HTMLElement | null = document.activeElement as HTMLElement | null,
211
+ activeMenu: HTMLElement | null = activeButton?.nextElementSibling as HTMLElement | null,
212
+ showing: boolean = activeButton?.getAttribute('aria-expanded')?.toLowerCase() === 'true';
213
+ if (activeButton?.getAttribute('aria-controls') === mintSelectors.ids.wrapper) {
214
+ activeMenu = this.el.wrapper;
215
+ }
216
+
217
+ if (activeButton?.getAttribute('aria-controls') && activeMenu && !showing) {
218
+ activeButton.click();
219
+ let firstFocusable: HTMLElement | null = activeMenu.querySelector(mintSelectors.focusable);
220
+ firstFocusable?.focus();
221
+ }
222
+ }
223
+
224
+ /**
225
+ * Closes the menu closest to the document's focus
226
+ */
227
+ closeClosestMenu () : void {
228
+ let activeElement: HTMLElement | null = document.activeElement as HTMLElement | null,
229
+ activeMenu: HTMLElement | null = activeElement?.closest(mintSelectors.subMenu) as HTMLElement | null,
230
+ activeButton: HTMLElement | null = activeMenu?.previousElementSibling ? activeMenu.previousElementSibling as HTMLElement : this.el.mobileButton;
231
+ if (activeElement?.getAttribute('aria-controls') && activeElement?.getAttribute('aria-expanded')?.toLowerCase() === 'true') {
232
+ activeButton = activeElement;
233
+ }
234
+
235
+ if (activeButton?.getAttribute('aria-expanded')?.toLowerCase() === 'true') {
236
+ activeButton?.click();
237
+ activeButton?.focus();
238
+ }
239
+ }
240
+
241
+ /**
242
+ * Toggles the menu closest to the document's focus
243
+ */
244
+ toggleClosestMenu () : void {
245
+ if (document.activeElement?.getAttribute('aria-expanded')?.toLowerCase() === 'true') {
246
+ this.closeClosestMenu();
247
+ } else {
248
+ this.openClosestMenu();
249
+ }
250
+ }
251
+
252
+ /**
253
+ * Closes the mobile menu when the window resizes
254
+ */
255
+ eHandleResize (e: Event) : void {
256
+ // Also check if resized from mobile to desktop
257
+ if (mintUtil.windowWidth() !== this.lastWidth) {
258
+ this.setMobileMenu();
259
+ }
260
+ this.lastWidth = mintUtil.windowWidth();
261
+ }
262
+
263
+ /**
264
+ * Closes all submenus when the page is scrolled
265
+ */
266
+ eHandleScroll () : void {
267
+ this.closeAllMenus();
268
+ }
269
+
270
+ /**
271
+ * Sends the focus to the menu button after tabbing past the last menu item
272
+ * @param e - Keyboard event
273
+ */
274
+ eWrapTab (e: KeyboardEvent) : void {
275
+ if (e.key.toLowerCase() === 'tab' && !e.shiftKey) {
276
+ this.el.mobileButton?.focus();
277
+ if (document.activeElement === this.el.mobileButton) {
278
+ e.preventDefault();
279
+ }
280
+ }
281
+ }
282
+
283
+ /**
284
+ * Handles keypresses on n4vbar buttons
285
+ * @param e - Keyboard event
286
+ */
287
+ eHandleButtonKeypress (e: KeyboardEvent) : void {
288
+ let target = e.target as HTMLElement | null,
289
+ subMenu = target?.closest('li');
290
+ switch (e.key.toLowerCase()) {
291
+ case 'escape':
292
+ if (subMenu?.classList.contains(mintSelectors.classes.open as string)) {
293
+ this.setMenu(subMenu);
294
+ } else {
295
+ this.setMobileMenu();
296
+ this.el.menuButton?.focus();
297
+ }
298
+ break;
299
+ case 'arrowleft':
300
+ this.closeClosestMenu();
301
+ break;
302
+ case 'arrowright':
303
+ this.openClosestMenu();
304
+ break;
305
+ case 'enter':
306
+ case 'space':
307
+ target?.click();
308
+ break;
309
+ }
310
+ }
311
+
312
+ /**
313
+ * Handles keypresses on n4vbar links
314
+ * @param e - Keyboard event
315
+ */
316
+ eHandleLinkKeypress (e: KeyboardEvent) : void {
317
+ let target = e.target as HTMLElement | null;
318
+ switch (e.key.toLowerCase()) {
319
+ case 'escape':
320
+ case 'arrowleft':
321
+ this.closeClosestMenu();
322
+ break;
323
+ case 'arrowright':
324
+ this.openClosestMenu();
325
+ break;
326
+ case 'enter':
327
+ case 'space':
328
+ target?.click();
329
+ break;
330
+ }
331
+ }
332
+
333
+ /**
334
+ * Handles keypresses on the n4vbar
335
+ * @param e - Keyboard event
336
+ */
337
+ eHandleKeypress (e: KeyboardEvent) : void {
338
+ if (e.key.toLowerCase() !== 'tab') {
339
+ e.preventDefault();
340
+ }
341
+ const target = e.target as HTMLElement | null;
342
+ switch (target?.tagName.toLowerCase()) {
343
+ case 'a':
344
+ this.eHandleLinkKeypress(e);
345
+ break;
346
+ case 'button':
347
+ this.eHandleButtonKeypress(e);
348
+ break;
349
+ }
350
+ }
351
+
352
+ /**
353
+ * Toggles the mobile menu
354
+ */
355
+ eToggleMobileMenu () : void {
356
+ this.toggleMobileMenu();
357
+ }
358
+
359
+ /**
360
+ * Toggles the clicked submenu
361
+ * @param e - Mouse event
362
+ */
363
+ eToggleMenu (e: MouseEvent) : void {
364
+ let target = e.target as HTMLElement | null;
365
+ this.closeSiblingMenus(target);
366
+ this.toggleMenu(target);
367
+ }
368
+
369
+ /**
370
+ * Runs after the mobile menu transitions
371
+ */
372
+ eTransitionEnd () : void {
373
+ if (this.el.wrapper?.classList.contains(mintSelectors.getClass('open')) === false ) {
374
+ this.el.wrapper.style.display = 'none';
375
+ }
376
+ };
377
+ }
378
+ export default mintHeader;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Side Enum
3
+ */
4
+ export enum mintSide {
5
+ Top,
6
+ Right,
7
+ Bottom,
8
+ Left
9
+ };
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Color
3
+ */
4
+ export class mintColor {
5
+ protected static hexBase: number = 16;
6
+ protected static hexMax: string = 'FF';
7
+ public r: number;
8
+ public g: number;
9
+ public b: number;
10
+ public a: number;
11
+
12
+ constructor (args: {[key: string]: number | string}) {
13
+ this.r = typeof args.r === 'number' ? Math.max(Math.min(args.r, mintColor.hexBase ** 2 - 1), 0) : 0;
14
+ this.g = typeof args.g === 'number' ? Math.max(Math.min(args.g, mintColor.hexBase ** 2 - 1), 0) : 0;
15
+ this.b = typeof args.b === 'number' ? Math.max(Math.min(args.b, mintColor.hexBase ** 2 - 1), 0) : 0;
16
+ this.a = typeof args.a === 'number' ? Math.max(Math.min(args.a, 1), 0) : 1;
17
+ if (typeof args.color === 'string') {
18
+ this.stringConstructor(args.color);
19
+ }
20
+ }
21
+
22
+ /**
23
+ * Constructor from a string argument
24
+ */
25
+ protected stringConstructor (str: string) : void {
26
+ if (str.startsWith('#')) {
27
+ this.hexConstructor(str);
28
+ } else {
29
+ if (~str.indexOf('linear-gradient')) {
30
+ str = str.substring(str.indexOf('linear-gradient'), str.length);
31
+ }
32
+ this.rgbConstructor(str);
33
+ }
34
+ }
35
+
36
+ /**
37
+ * Constructor from a hex argument
38
+ */
39
+ protected hexConstructor (hex: string) : void {
40
+ switch (hex.length) {
41
+ case 1:
42
+ case 5:
43
+ case 6:
44
+ return;
45
+ case 2:
46
+ hex = '#' + hex[1] + hex[1] + hex[1] + hex[1] + hex[1] + hex[1] + mintColor.hexMax;
47
+ break;
48
+ case 3:
49
+ hex = '#' + hex[1] + hex[1] + hex[1] + hex[2] + hex[2] + hex[2] + mintColor.hexMax;
50
+ break;
51
+ case 4:
52
+ hex = '#' + hex[1] + hex[1] + hex[2] + hex[2] + hex[3] + hex[3] + mintColor.hexMax;
53
+ break;
54
+ case 7:
55
+ hex += mintColor.hexMax;
56
+ break;
57
+ case 8:
58
+ hex += hex[hex.length - 1];
59
+ break;
60
+ default:
61
+ hex = hex.substring(0, 9);
62
+ }
63
+
64
+ this.r = parseInt(hex.substring(1, 3), mintColor.hexBase);
65
+ this.g = parseInt(hex.substring(3, 5), mintColor.hexBase);
66
+ this.b = parseInt(hex.substring(5, 7), mintColor.hexBase);
67
+ this.a = parseInt(hex.substring(7, 9), mintColor.hexBase) / mintColor.hexBase ** 2;
68
+ }
69
+
70
+ /**
71
+ * Constructor from an rgba argument
72
+ */
73
+ protected rgbConstructor (rgb: string) : void {
74
+ let match: RegExpMatchArray | null = rgb.match(/rgba?\((\d{1,3}), ?(\d{1,3}), ?(\d{1,3})\)?(?:, ?(\d(?:\.\d*)?)\))?/);
75
+ if (match) {
76
+ this.r = parseInt(match[1]);
77
+ this.g = parseInt(match[2]);
78
+ this.b = parseInt(match[3]);
79
+ this.a = parseFloat(match[4]);
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Returns the perceived brightness of the color
85
+ */
86
+ getBrightness () : number {
87
+ if (this.a === 0) {
88
+ return 262;
89
+ }
90
+ if (!isNaN(this.r) && !isNaN(this.g) && !isNaN(this.b)) {
91
+ return Math.round((this.r * 299 + this.g * 587 + this.b * 144) / 1000);
92
+ }
93
+ return -1;
94
+ }
95
+ }
96
+ export default mintColor;
@@ -0,0 +1,66 @@
1
+ /**
2
+ * A generic item
3
+ * @note - this class must be convertable with JSON
4
+ * - only add strings, numbers, booleans, arrays, and objects
5
+ */
6
+ export class mintItem {
7
+ /**
8
+ * Item settings
9
+ */
10
+ version?: number = 0;
11
+ priority?: number = 0;
12
+ price?: number = 0;
13
+ level?: number = 0;
14
+ size?: number = 0;
15
+ num?: number = 0;
16
+ width?: number = 0;
17
+ height?: number = 0;
18
+
19
+ centered?: boolean = false;
20
+ disabled?: boolean = false;
21
+ private?: boolean = false;
22
+
23
+ /**
24
+ * Item properties
25
+ */
26
+ id?: string;
27
+ slug?: string;
28
+ name?: string;
29
+ title?: string;
30
+ subtitle?: string;
31
+ description?: string;
32
+ category?: string;
33
+ type?: string;
34
+ unit?: string;
35
+ logo?: mintItem;
36
+ icon?: string;
37
+ position?: string;
38
+ transform?: string;
39
+ date?: string;
40
+
41
+ /**
42
+ * Item links
43
+ */
44
+ src?: string;
45
+ href?: string;
46
+ target?: string;
47
+ routerLink?: string[];
48
+
49
+ /**
50
+ * Item data
51
+ */
52
+ attr?: { [key: string]: string } = {};
53
+ params?: { [key: string]: string } = {};
54
+ options?: { [key: string]: string } = {};
55
+ lists?: { [key: string]: string[] } = {};
56
+
57
+ /**
58
+ * Item lists
59
+ */
60
+ paragraphs?: string[] = [];
61
+ classes?: string[] = [];
62
+ items?: mintItem[] = [];
63
+ images?: mintItem[] = [];
64
+ buttons?: mintItem[] = [];
65
+ };
66
+ export default mintItem;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Handles the display of elements
3
+ */
4
+ export abstract class mintDisplay {
5
+
6
+ };
7
+ export default mintDisplay;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Event helper functions
3
+ */
4
+ export abstract class mintEvent {
5
+
6
+ };
7
+ export default mintEvent;
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Imports
3
+ */
4
+ import mintObject from "./object";
5
+
6
+ /**
7
+ * Icon helper functions
8
+ */
9
+ export abstract class mintIcon {
10
+ /**
11
+ * Default icons
12
+ */
13
+ static icons: {[key: string]: string} = {
14
+ 'a[href^="mailto:"]': 'far fa-envelope',
15
+ 'a[href^="tel:"]': 'fas fa-phone-flip',
16
+ 'a[href^="sms:"]': 'far fa-message',
17
+ 'a[href^="https://maps"]': 'fas fa-map-location-dot',
18
+ 'a[href^="http"]': 'fas fa-up-right-from-square'
19
+ };
20
+
21
+ /**
22
+ * Appends the given icon to the given selector if there is not already an icon appended
23
+ */
24
+ static append (icon: string, selector: string): void {
25
+ let items: NodeListOf<HTMLElement> = document.querySelectorAll(selector);
26
+ items.forEach((item: HTMLElement) => {
27
+ let iconElement: HTMLElement = document.createElement('i');
28
+ iconElement.classList.add(...icon.split(' '));
29
+ if (!item.querySelector('i')) {
30
+ item.appendChild(iconElement);
31
+ }
32
+ if (iconElement.classList.contains('fa-up-right-from-square')) {
33
+ item.setAttribute('target', '_blank');
34
+ }
35
+ });
36
+ }
37
+
38
+ /**
39
+ * Updates the icons
40
+ * @param icons - the icons to update
41
+ */
42
+ static update (icons?: {[key: string]: string | false}): void {
43
+ let activeIcons: {[key: string]: string} = mintObject.removeValues({
44
+ ...this.icons,
45
+ ...icons
46
+ }, [false]);
47
+
48
+ Object.keys(activeIcons).forEach((selector: string) => {
49
+ this.append(activeIcons[selector], selector);
50
+ });
51
+ }
52
+
53
+ /**
54
+ * Removes the given icon from the given selector
55
+ * @param icon - the icon to remove
56
+ */
57
+ static remove (icon: string, selector: string): void {
58
+ let items: NodeListOf<HTMLElement> = document.querySelectorAll(selector);
59
+ items.forEach((item: HTMLElement) => {
60
+ let iconElement: HTMLElement | null = item.querySelector('i');
61
+ if (iconElement) {
62
+ iconElement.remove();
63
+ }
64
+ });
65
+ }
66
+ };
67
+ export default mintIcon;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * List functions for the util library
3
+ */
4
+ export abstract class mintList {
5
+ /**
6
+ * Returns a copy of the provided list with the items in random order
7
+ * @param list - the list to shuffle
8
+ * @returns - the shuffled list
9
+ */
10
+ static shuffle (list: any[]): any[] {
11
+ let copy = [...list];
12
+ for (let i = copy.length - 1; i > 0; i--) {
13
+ const j = Math.floor(Math.random() * (i + 1));
14
+ [copy[i], copy[j]] = [copy[j], copy[i]];
15
+ }
16
+ return copy;
17
+ }
18
+ };
19
+ export default mintList;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Math functions for the util library
3
+ */
4
+ export abstract class mintMath {
5
+ /**
6
+ * Get a random integer between min and max
7
+ * @param max Maximum value to return
8
+ * @param min Minimum value to return (default is 0)
9
+ * @returns a random integer between min and max
10
+ */
11
+ static randomInt (max: number, min: number = 0): number {
12
+ min = Math.ceil(min);
13
+ max = Math.floor(max);
14
+ return Math.floor(Math.random() * (max - min) + min);
15
+ }
16
+ };
17
+ export default mintMath;