kmaterialize 2.3.3-1.0.21 → 2.3.3-1.0.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/air-datepicker/air-datepicker.js +152 -0
- package/components/alert/alert.js +54 -0
- package/components/button/buttons.js +220 -0
- package/components/card/cards.js +149 -0
- package/components/carousel/carousel.js +574 -0
- package/components/carousel/slider.js +340 -0
- package/components/chip/chips.js +341 -0
- package/components/collapsible/collapsible.js +156 -0
- package/components/color-input/color-input.js +166 -0
- package/components/datepicker/datepicker.js +1035 -0
- package/components/dialog/materialbox.js +377 -0
- package/components/dialog/modal.js +72 -0
- package/components/dropdown/dropdown.js +505 -0
- package/components/file-input/file-input.js +169 -0
- package/components/kanban/_kanban.scss +69 -0
- package/components/kanban/kanban.js +168 -0
- package/components/kanban/kanban.ts +57 -8
- package/components/loading/_loading.scss +68 -0
- package/components/loading/loading.ts +104 -0
- package/components/loading/loadingSpec.js +102 -0
- package/components/navigation-drawer/sidenav.js +454 -0
- package/components/number-input/number-input.js +68 -0
- package/components/password-input/password-input.js +49 -0
- package/components/popup/_popup.scss +106 -0
- package/components/popup/popup.ts +113 -0
- package/components/popup/popupSpec.js +140 -0
- package/components/search/autocomplete.js +470 -0
- package/components/slider/range.js +169 -0
- package/components/snackbar/toasts.js +244 -0
- package/components/tabs/tabs.js +289 -0
- package/components/textfield/forms.js +172 -0
- package/components/textfield/select.js +385 -0
- package/components/timepicker/timepicker.js +698 -0
- package/components/tom-select/tom-select-field.js +130 -0
- package/components/toolbar/toolbar.js +134 -0
- package/components/tooltip/tooltip.js +270 -0
- package/dist/css/materialize.colors.min.css +1 -1
- package/dist/css/materialize.css +322 -1
- package/dist/css/materialize.min.css +2 -2
- package/dist/css/materialize.min.css.map +1 -1
- package/dist/js/materialize.cjs.js +258 -42
- package/dist/js/materialize.d.ts +50 -3
- package/dist/js/materialize.js +258 -42
- package/dist/js/materialize.min.js +2 -2
- package/dist/js/materialize.mjs +257 -43
- package/package.json +6 -1
- package/sass/materialize.scss +2 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import type Swal from 'sweetalert2';
|
|
2
|
+
import type {
|
|
3
|
+
SweetAlertCustomClass,
|
|
4
|
+
SweetAlertOptions,
|
|
5
|
+
SweetAlertResult
|
|
6
|
+
} from 'sweetalert2';
|
|
7
|
+
|
|
8
|
+
import { loadPeer } from '../../src/peer-loader';
|
|
9
|
+
|
|
10
|
+
export type PopupOptions = SweetAlertOptions;
|
|
11
|
+
export type PopupResult<T = unknown> = SweetAlertResult<T>;
|
|
12
|
+
|
|
13
|
+
function withClass(
|
|
14
|
+
required: string,
|
|
15
|
+
custom?: string | readonly string[]
|
|
16
|
+
): string {
|
|
17
|
+
return [
|
|
18
|
+
required,
|
|
19
|
+
...(typeof custom === 'string'
|
|
20
|
+
? custom.split(/\s+/)
|
|
21
|
+
: custom ?? [])
|
|
22
|
+
]
|
|
23
|
+
.filter(
|
|
24
|
+
(value, index, values) =>
|
|
25
|
+
value && values.indexOf(value) === index
|
|
26
|
+
)
|
|
27
|
+
.join(' ');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Materialize-themed SweetAlert2 dialogs. No element initialization is needed. */
|
|
31
|
+
export class Popup {
|
|
32
|
+
/** Fresh defaults so per-dialog customization cannot affect later popups. */
|
|
33
|
+
static get defaults(): PopupOptions {
|
|
34
|
+
return {
|
|
35
|
+
buttonsStyling: false,
|
|
36
|
+
heightAuto: false,
|
|
37
|
+
customClass: {
|
|
38
|
+
container: 'popup-container',
|
|
39
|
+
popup: 'popup',
|
|
40
|
+
confirmButton: 'btn filled',
|
|
41
|
+
cancelButton: 'btn outlined',
|
|
42
|
+
denyButton: 'btn tonal'
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
private static _load(): Promise<typeof Swal> {
|
|
48
|
+
// Use the JS-only build: the consumer controls stylesheet order.
|
|
49
|
+
return loadPeer<typeof Swal>(
|
|
50
|
+
{
|
|
51
|
+
specifier: 'sweetalert2',
|
|
52
|
+
globalName: 'Swal',
|
|
53
|
+
feature: 'Popup',
|
|
54
|
+
cdnHint:
|
|
55
|
+
'<link rel="stylesheet" href="path/to/sweetalert2.min.css">\n' +
|
|
56
|
+
' <script src="path/to/sweetalert2.min.js"></script>\n' +
|
|
57
|
+
' (copy from node_modules/sweetalert2/dist/; load the CSS before materialize.css)'
|
|
58
|
+
},
|
|
59
|
+
() => import('sweetalert2/dist/sweetalert2.js')
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Open a dialog and resolve with SweetAlert2's confirmation/dismissal result. */
|
|
64
|
+
static async fire<T = unknown>(
|
|
65
|
+
options: PopupOptions = {}
|
|
66
|
+
): Promise<PopupResult<Awaited<T>>> {
|
|
67
|
+
const swal = await Popup._load();
|
|
68
|
+
const defaults = Popup.defaults;
|
|
69
|
+
|
|
70
|
+
const customClass: SweetAlertCustomClass = {
|
|
71
|
+
...defaults.customClass,
|
|
72
|
+
...options.customClass,
|
|
73
|
+
|
|
74
|
+
// Keep the scoped theme even when consumers add their own classes.
|
|
75
|
+
container: withClass(
|
|
76
|
+
'popup-container',
|
|
77
|
+
options.customClass?.container
|
|
78
|
+
),
|
|
79
|
+
|
|
80
|
+
popup: withClass(
|
|
81
|
+
'popup',
|
|
82
|
+
options.customClass?.popup
|
|
83
|
+
)
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
/*
|
|
87
|
+
* SweetAlertOptions is a discriminated union because `input: "file"`
|
|
88
|
+
* uses a different inputValidator signature. Object spreading causes
|
|
89
|
+
* TypeScript to lose that correlation, even though the resulting
|
|
90
|
+
* object is valid.
|
|
91
|
+
*/
|
|
92
|
+
const mergedOptions = {
|
|
93
|
+
...defaults,
|
|
94
|
+
...options,
|
|
95
|
+
customClass
|
|
96
|
+
} as SweetAlertOptions;
|
|
97
|
+
|
|
98
|
+
const result = await swal.fire<T>(mergedOptions);
|
|
99
|
+
|
|
100
|
+
/*
|
|
101
|
+
* SweetAlert2 declares its own internal Awaited<T>, while modern
|
|
102
|
+
* TypeScript provides the global Awaited<T>. They are semantically
|
|
103
|
+
* equivalent here but TypeScript can report them as unrelated.
|
|
104
|
+
*/
|
|
105
|
+
return result as PopupResult<Awaited<T>>;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** Close the current SweetAlert2 dialog, resolving its pending result. */
|
|
109
|
+
static async close(): Promise<void> {
|
|
110
|
+
const swal = await Popup._load();
|
|
111
|
+
swal.close();
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
describe('Popup', function () {
|
|
2
|
+
let script;
|
|
3
|
+
let style;
|
|
4
|
+
let trigger;
|
|
5
|
+
|
|
6
|
+
beforeAll(async function () {
|
|
7
|
+
// Exercise the same optional global fallback as plain-script consumers.
|
|
8
|
+
style = document.createElement('link');
|
|
9
|
+
style.rel = 'stylesheet';
|
|
10
|
+
style.href = '/__spec__/node_modules/sweetalert2/dist/sweetalert2.min.css';
|
|
11
|
+
script = document.createElement('script');
|
|
12
|
+
script.src = '/__spec__/node_modules/sweetalert2/dist/sweetalert2.min.js';
|
|
13
|
+
await Promise.all([style, script].map(element => new Promise((resolve, reject) => {
|
|
14
|
+
element.onload = resolve;
|
|
15
|
+
element.onerror = () => reject(new Error('Could not load SweetAlert2 test dependency'));
|
|
16
|
+
document.head.appendChild(element);
|
|
17
|
+
})));
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
beforeEach(function () {
|
|
21
|
+
trigger = document.createElement('button');
|
|
22
|
+
trigger.textContent = 'Open popup';
|
|
23
|
+
document.body.appendChild(trigger);
|
|
24
|
+
trigger.focus();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
afterEach(async function () {
|
|
28
|
+
await M.Popup.close();
|
|
29
|
+
trigger.remove();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
afterAll(function () {
|
|
33
|
+
script.remove();
|
|
34
|
+
style.remove();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
async function open(options = {}) {
|
|
38
|
+
let result;
|
|
39
|
+
const el = await new Promise((resolve, reject) => {
|
|
40
|
+
result = M.Popup.fire({
|
|
41
|
+
showClass: { popup: '' },
|
|
42
|
+
hideClass: { popup: '' },
|
|
43
|
+
...options,
|
|
44
|
+
didOpen: resolve
|
|
45
|
+
});
|
|
46
|
+
result.catch(reject);
|
|
47
|
+
});
|
|
48
|
+
return { el, result };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
it('opens a themed dialog and returns confirmation without interpreting plain text as HTML', async function () {
|
|
52
|
+
const { el, result } = await open({ titleText: '<strong>Title</strong>', text: 'Details' });
|
|
53
|
+
expect(el.classList.contains('popup')).toBeTrue();
|
|
54
|
+
expect(el.parentElement.classList.contains('popup-container')).toBeTrue();
|
|
55
|
+
expect(el.querySelector('.swal2-title').textContent).toBe('<strong>Title</strong>');
|
|
56
|
+
expect(el.querySelector('.swal2-title strong')).toBeNull();
|
|
57
|
+
const confirm = el.querySelector('.swal2-confirm');
|
|
58
|
+
expect(confirm.classList.contains('btn')).toBeTrue();
|
|
59
|
+
expect(confirm.classList.contains('filled')).toBeTrue();
|
|
60
|
+
expect(confirm.classList.contains('swal2-styled')).toBeFalse();
|
|
61
|
+
confirm.click();
|
|
62
|
+
expect((await result).isConfirmed).toBeTrue();
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('returns cancellation and supports focusing the cancel action', async function () {
|
|
66
|
+
const { el, result } = await open({ showCancelButton: true, focusCancel: true });
|
|
67
|
+
const cancel = el.querySelector('.swal2-cancel');
|
|
68
|
+
expect(document.activeElement).toBe(cancel);
|
|
69
|
+
cancel.click();
|
|
70
|
+
const response = await result;
|
|
71
|
+
expect(response.isConfirmed).toBeFalse();
|
|
72
|
+
expect(response.dismiss).toBe('cancel');
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('validates input and resolves with the entered value', async function () {
|
|
76
|
+
const { el, result } = await open({
|
|
77
|
+
input: 'text',
|
|
78
|
+
inputLabel: 'Workspace name',
|
|
79
|
+
inputValidator: value => value.trim() ? undefined : 'Enter a name.'
|
|
80
|
+
});
|
|
81
|
+
const input = el.querySelector('.swal2-input');
|
|
82
|
+
el.querySelector('.swal2-confirm').click();
|
|
83
|
+
await new Promise(resolve => setTimeout(resolve, 20));
|
|
84
|
+
expect(el.querySelector('.swal2-validation-message').textContent).toBe('Enter a name.');
|
|
85
|
+
expect(el.isConnected).toBeTrue();
|
|
86
|
+
const bounds = input.getBoundingClientRect();
|
|
87
|
+
const popupBounds = el.getBoundingClientRect();
|
|
88
|
+
expect(bounds.left).toBeGreaterThan(popupBounds.left);
|
|
89
|
+
expect(bounds.right).toBeLessThan(popupBounds.right);
|
|
90
|
+
input.value = 'Studio';
|
|
91
|
+
el.querySelector('.swal2-confirm').click();
|
|
92
|
+
expect((await result).value).toBe('Studio');
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('preserves theme classes and callback options without mutating caller options', async function () {
|
|
96
|
+
const options = { customClass: { popup: ['custom-popup'], confirmButton: 'btn tonal' }, willOpen: jasmine.createSpy('willOpen') };
|
|
97
|
+
const { el, result } = await open(options);
|
|
98
|
+
expect(el.classList.contains('popup')).toBeTrue();
|
|
99
|
+
expect(el.classList.contains('custom-popup')).toBeTrue();
|
|
100
|
+
expect(options.willOpen).toHaveBeenCalled();
|
|
101
|
+
expect(options.customClass.popup).toEqual(['custom-popup']);
|
|
102
|
+
expect(el.querySelector('.swal2-confirm').classList.contains('filled')).toBeFalse();
|
|
103
|
+
el.querySelector('.swal2-confirm').click();
|
|
104
|
+
await result;
|
|
105
|
+
expect(M.Popup.defaults.customClass.confirmButton).toBe('btn filled');
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('shows a loader while preConfirm is pending and resolves its value', async function () {
|
|
109
|
+
let complete;
|
|
110
|
+
const { el, result } = await open({
|
|
111
|
+
showLoaderOnConfirm: true,
|
|
112
|
+
preConfirm: () => new Promise(resolve => { complete = resolve; })
|
|
113
|
+
});
|
|
114
|
+
el.querySelector('.swal2-confirm').click();
|
|
115
|
+
await new Promise(resolve => setTimeout(resolve, 0));
|
|
116
|
+
expect(getComputedStyle(el.querySelector('.swal2-loader')).display).not.toBe('none');
|
|
117
|
+
complete('Preview ready');
|
|
118
|
+
expect((await result).value).toBe('Preview ready');
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('closes a dialog programmatically and resolves its pending result', async function () {
|
|
122
|
+
const { el, result } = await open();
|
|
123
|
+
await M.Popup.close();
|
|
124
|
+
expect((await result).isDismissed).toBeTrue();
|
|
125
|
+
expect(el.isConnected).toBeFalse();
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('renders native select, radio, and checkbox inputs visibly', async function () {
|
|
129
|
+
for (const type of ['select', 'radio', 'checkbox']) {
|
|
130
|
+
const { el, result } = await open({ input: type, inputOptions: { one: 'One', two: 'Two' } });
|
|
131
|
+
const input = el.querySelector(type === 'select' ? 'select' : `input[type="${type}"]`);
|
|
132
|
+
const computed = getComputedStyle(input);
|
|
133
|
+
expect(computed.display).not.toBe('none');
|
|
134
|
+
expect(computed.opacity).toBe('1');
|
|
135
|
+
expect(computed.pointerEvents).not.toBe('none');
|
|
136
|
+
await M.Popup.close();
|
|
137
|
+
await result;
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
});
|