@ktfth/stickjs 3.0.0

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.
@@ -0,0 +1,426 @@
1
+ /*!
2
+ * Stick UI — Data Table plugin
3
+ * Provides sort, filter, and pagination for HTML tables.
4
+ *
5
+ * Handlers:
6
+ * table-sort click:table-sort:COL_INDEX — cycle asc/desc/none on column
7
+ * table-filter input:table-filter:.stk-table — substring search across all cells
8
+ * table-paginate click:table-paginate:next — next/prev/first/last/N
9
+ * table-page-size change:table-page-size:.stk-table — change rows per page
10
+ *
11
+ * Initialise: stkDataTable.init(tableEl, { pageSize: 10 })
12
+ */
13
+ (function (root) {
14
+ 'use strict';
15
+
16
+ /* ── State store (WeakMap — no globals on the element) ───── */
17
+ var STATE = new WeakMap();
18
+
19
+ function getState(table) {
20
+ return STATE.get(table);
21
+ }
22
+
23
+ function ensureState(table, opts) {
24
+ if (STATE.has(table)) return STATE.get(table);
25
+ var pageSize = Number(table.getAttribute('data-stk-page-size')) ||
26
+ (opts && opts.pageSize) || 10;
27
+ var state = {
28
+ currentPage: 1,
29
+ pageSize: pageSize,
30
+ sortCol: -1,
31
+ sortDir: 'none', // 'none' | 'ascending' | 'descending'
32
+ filterValue: '',
33
+ allRows: [], // original <tr> references (from <tbody>)
34
+ filteredRows: []
35
+ };
36
+ STATE.set(table, state);
37
+ return state;
38
+ }
39
+
40
+ /* ── Helpers ─────────────────────────────────────────────── */
41
+
42
+ function getTable(el, param) {
43
+ // param may be a CSS selector for the table; fall back to closest table
44
+ if (param) {
45
+ var t = document.querySelector(param);
46
+ if (t) {
47
+ if (t.tagName === 'TABLE') return t;
48
+ var inner = t.querySelector('table');
49
+ if (inner) return inner;
50
+ }
51
+ }
52
+ return el.closest('table') || el.closest('.stk-table-wrapper');
53
+ }
54
+
55
+ function getTableFromSelector(sel) {
56
+ if (!sel) return null;
57
+ var el = document.querySelector(sel);
58
+ if (!el) return null;
59
+ if (el.tagName === 'TABLE') return el;
60
+ return el.querySelector('table') || el;
61
+ }
62
+
63
+ function collectRows(table) {
64
+ var tbody = table.querySelector('tbody');
65
+ if (!tbody) return [];
66
+ return Array.prototype.slice.call(tbody.querySelectorAll('tr'));
67
+ }
68
+
69
+ function cellText(row, colIdx) {
70
+ var cells = row.querySelectorAll('td');
71
+ if (colIdx < 0 || colIdx >= cells.length) return '';
72
+ return (cells[colIdx].getAttribute('data-sort-value') || cells[colIdx].textContent || '').trim();
73
+ }
74
+
75
+ function isNumeric(val) {
76
+ return val !== '' && !isNaN(Number(val));
77
+ }
78
+
79
+ /* ── Sort ────────────────────────────────────────────────── */
80
+
81
+ function applySort(table) {
82
+ var state = getState(table);
83
+ if (!state) return;
84
+
85
+ // Reset aria-sort on all headers
86
+ var headers = table.querySelectorAll('th[data-stk-sortable]');
87
+ for (var i = 0; i < headers.length; i++) {
88
+ headers[i].removeAttribute('aria-sort');
89
+ }
90
+
91
+ if (state.sortCol < 0 || state.sortDir === 'none') {
92
+ // Restore original order
93
+ state.filteredRows = filterRows(state.allRows, state.filterValue);
94
+ applyPagination(table);
95
+ return;
96
+ }
97
+
98
+ // Mark active header
99
+ var activeHeader = table.querySelectorAll('th')[state.sortCol];
100
+ if (activeHeader) {
101
+ activeHeader.setAttribute('aria-sort', state.sortDir);
102
+ }
103
+
104
+ // Decide numeric vs string
105
+ var col = state.sortCol;
106
+ var rows = state.filteredRows.length ? state.filteredRows : filterRows(state.allRows, state.filterValue);
107
+ var allNumeric = rows.every(function (r) {
108
+ var v = cellText(r, col);
109
+ return v === '' || isNumeric(v);
110
+ });
111
+
112
+ var sorted = rows.slice().sort(function (a, b) {
113
+ var va = cellText(a, col);
114
+ var vb = cellText(b, col);
115
+ var cmp;
116
+ if (allNumeric) {
117
+ cmp = (Number(va) || 0) - (Number(vb) || 0);
118
+ } else {
119
+ cmp = va.localeCompare(vb, undefined, { sensitivity: 'base' });
120
+ }
121
+ return state.sortDir === 'descending' ? -cmp : cmp;
122
+ });
123
+
124
+ state.filteredRows = sorted;
125
+ applyPagination(table);
126
+ }
127
+
128
+ /* ── Filter ──────────────────────────────────────────────── */
129
+
130
+ function filterRows(allRows, filterValue) {
131
+ if (!filterValue) return allRows.slice();
132
+ var needle = filterValue.toLowerCase();
133
+ return allRows.filter(function (row) {
134
+ return (row.textContent || '').toLowerCase().indexOf(needle) !== -1;
135
+ });
136
+ }
137
+
138
+ function applyFilter(table) {
139
+ var state = getState(table);
140
+ if (!state) return;
141
+ state.filteredRows = filterRows(state.allRows, state.filterValue);
142
+ state.currentPage = 1;
143
+ // Re-apply sort on filtered set
144
+ if (state.sortCol >= 0 && state.sortDir !== 'none') {
145
+ applySort(table);
146
+ } else {
147
+ applyPagination(table);
148
+ }
149
+ }
150
+
151
+ /* ── Pagination ──────────────────────────────────────────── */
152
+
153
+ function totalPages(state) {
154
+ return Math.max(1, Math.ceil(state.filteredRows.length / state.pageSize));
155
+ }
156
+
157
+ function applyPagination(table) {
158
+ var state = getState(table);
159
+ if (!state) return;
160
+
161
+ var total = totalPages(state);
162
+ if (state.currentPage > total) state.currentPage = total;
163
+ if (state.currentPage < 1) state.currentPage = 1;
164
+
165
+ var start = (state.currentPage - 1) * state.pageSize;
166
+ var end = start + state.pageSize;
167
+
168
+ // Hide all rows first
169
+ state.allRows.forEach(function (row) {
170
+ row.hidden = true;
171
+ });
172
+
173
+ // Show only current page of filtered rows
174
+ state.filteredRows.forEach(function (row, idx) {
175
+ row.hidden = (idx < start || idx >= end);
176
+ });
177
+
178
+ updatePaginationUI(table, state);
179
+ }
180
+
181
+ function updatePaginationUI(table, state) {
182
+ // Find wrapper
183
+ var wrapper = table.closest('.stk-table-wrapper') || table.parentElement;
184
+ if (!wrapper) return;
185
+
186
+ var total = totalPages(state);
187
+ var start = (state.currentPage - 1) * state.pageSize + 1;
188
+ var end = Math.min(state.currentPage * state.pageSize, state.filteredRows.length);
189
+ var count = state.filteredRows.length;
190
+
191
+ // Update page info text
192
+ var info = wrapper.querySelector('.stk-table-page-info');
193
+ if (info) {
194
+ if (count === 0) {
195
+ info.textContent = 'No results';
196
+ } else {
197
+ info.textContent = 'Showing ' + start + '\u2013' + end + ' of ' + count;
198
+ }
199
+ }
200
+
201
+ // Enable/disable prev/next buttons
202
+ var prevBtns = wrapper.querySelectorAll('[data-stk-page="prev"]');
203
+ var nextBtns = wrapper.querySelectorAll('[data-stk-page="next"]');
204
+ var firstBtns = wrapper.querySelectorAll('[data-stk-page="first"]');
205
+ var lastBtns = wrapper.querySelectorAll('[data-stk-page="last"]');
206
+
207
+ for (var i = 0; i < prevBtns.length; i++) {
208
+ prevBtns[i].disabled = (state.currentPage <= 1);
209
+ }
210
+ for (i = 0; i < firstBtns.length; i++) {
211
+ firstBtns[i].disabled = (state.currentPage <= 1);
212
+ }
213
+ for (i = 0; i < nextBtns.length; i++) {
214
+ nextBtns[i].disabled = (state.currentPage >= total);
215
+ }
216
+ for (i = 0; i < lastBtns.length; i++) {
217
+ lastBtns[i].disabled = (state.currentPage >= total);
218
+ }
219
+
220
+ // Update page number indicator if present
221
+ var pageNum = wrapper.querySelector('.stk-table-page-num');
222
+ if (pageNum) {
223
+ pageNum.textContent = 'Page ' + state.currentPage + ' of ' + total;
224
+ }
225
+ }
226
+
227
+ /* ── Auto-create pagination controls ─────────────────────── */
228
+
229
+ function createPaginationControls(table) {
230
+ var wrapper = table.closest('.stk-table-wrapper') || table.parentElement;
231
+ if (!wrapper) return;
232
+ // Only auto-create if data-stk-paginate exists and no pagination bar yet
233
+ if (!table.hasAttribute('data-stk-paginate') &&
234
+ !wrapper.hasAttribute('data-stk-paginate')) return;
235
+ if (wrapper.querySelector('.stk-table-pagination')) return;
236
+
237
+ var bar = document.createElement('div');
238
+ bar.className = 'stk-table-pagination';
239
+
240
+ var info = document.createElement('span');
241
+ info.className = 'stk-table-page-info';
242
+ info.setAttribute('aria-live', 'polite');
243
+ info.setAttribute('role', 'status');
244
+ bar.appendChild(info);
245
+
246
+ var nav = document.createElement('span');
247
+ nav.className = 'stk-table-page-nav';
248
+
249
+ var prevBtn = document.createElement('button');
250
+ prevBtn.className = 'stk-table-page-btn';
251
+ prevBtn.setAttribute('data-stk-page', 'prev');
252
+ prevBtn.textContent = '\u2039 Prev';
253
+ prevBtn.type = 'button';
254
+
255
+ var pageNum = document.createElement('span');
256
+ pageNum.className = 'stk-table-page-num';
257
+
258
+ var nextBtn = document.createElement('button');
259
+ nextBtn.className = 'stk-table-page-btn';
260
+ nextBtn.setAttribute('data-stk-page', 'next');
261
+ nextBtn.textContent = 'Next \u203A';
262
+ nextBtn.type = 'button';
263
+
264
+ nav.appendChild(prevBtn);
265
+ nav.appendChild(pageNum);
266
+ nav.appendChild(nextBtn);
267
+ bar.appendChild(nav);
268
+
269
+ // Insert after table
270
+ if (table.nextSibling) {
271
+ wrapper.insertBefore(bar, table.nextSibling);
272
+ } else {
273
+ wrapper.appendChild(bar);
274
+ }
275
+
276
+ // Wire up click handlers on the auto-created buttons
277
+ prevBtn.addEventListener('click', function () {
278
+ navigatePage(table, 'prev');
279
+ });
280
+ nextBtn.addEventListener('click', function () {
281
+ navigatePage(table, 'next');
282
+ });
283
+ }
284
+
285
+ function navigatePage(table, param) {
286
+ var state = getState(table);
287
+ if (!state) return;
288
+ var total = totalPages(state);
289
+ switch (param) {
290
+ case 'next': state.currentPage = Math.min(state.currentPage + 1, total); break;
291
+ case 'prev': state.currentPage = Math.max(state.currentPage - 1, 1); break;
292
+ case 'first': state.currentPage = 1; break;
293
+ case 'last': state.currentPage = total; break;
294
+ default:
295
+ var n = parseInt(param, 10);
296
+ if (!isNaN(n) && n >= 1 && n <= total) state.currentPage = n;
297
+ }
298
+ applyPagination(table);
299
+ }
300
+
301
+ /* ── Plugin API ──────────────────────────────────────────── */
302
+
303
+ var stkDataTable = {
304
+
305
+ /**
306
+ * Register Stick handlers.
307
+ */
308
+ install: function (stick) {
309
+
310
+ /* table-sort — click:table-sort:COL_INDEX */
311
+ stick.add('table-sort', function (el, param) {
312
+ var colIdx = parseInt(param, 10);
313
+ if (isNaN(colIdx)) return;
314
+ var table = el.closest('table');
315
+ if (!table) return;
316
+
317
+ var state = getState(table);
318
+ if (!state) {
319
+ stkDataTable.init(table);
320
+ state = getState(table);
321
+ }
322
+
323
+ // Cycle: none → ascending → descending → none
324
+ if (state.sortCol !== colIdx) {
325
+ state.sortCol = colIdx;
326
+ state.sortDir = 'ascending';
327
+ } else {
328
+ switch (state.sortDir) {
329
+ case 'none': state.sortDir = 'ascending'; break;
330
+ case 'ascending': state.sortDir = 'descending'; break;
331
+ case 'descending': state.sortDir = 'none'; state.sortCol = -1; break;
332
+ }
333
+ }
334
+
335
+ state.currentPage = 1;
336
+ // Re-filter then sort
337
+ state.filteredRows = filterRows(state.allRows, state.filterValue);
338
+ applySort(table);
339
+ });
340
+
341
+ /* table-filter — input:table-filter:.stk-table */
342
+ stick.add('table-filter', function (el, param) {
343
+ var table = getTableFromSelector(param) || el.closest('table');
344
+ if (!table) return;
345
+
346
+ var state = getState(table);
347
+ if (!state) {
348
+ stkDataTable.init(table);
349
+ state = getState(table);
350
+ }
351
+
352
+ state.filterValue = (el.value || '').trim();
353
+ applyFilter(table);
354
+ });
355
+
356
+ /* table-paginate — click:table-paginate:next */
357
+ stick.add('table-paginate', function (el, param) {
358
+ var wrapper = el.closest('.stk-table-wrapper');
359
+ var table = wrapper ? wrapper.querySelector('table') : el.closest('table');
360
+ if (!table) return;
361
+
362
+ var state = getState(table);
363
+ if (!state) {
364
+ stkDataTable.init(table);
365
+ state = getState(table);
366
+ }
367
+
368
+ navigatePage(table, param);
369
+ });
370
+
371
+ /* table-page-size — change:table-page-size:.stk-table */
372
+ stick.add('table-page-size', function (el, param) {
373
+ var table = getTableFromSelector(param) || el.closest('table');
374
+ if (!table) return;
375
+
376
+ var state = getState(table);
377
+ if (!state) {
378
+ stkDataTable.init(table);
379
+ state = getState(table);
380
+ }
381
+
382
+ var newSize = parseInt(el.value, 10);
383
+ if (isNaN(newSize) || newSize < 1) return;
384
+ state.pageSize = newSize;
385
+ state.currentPage = 1;
386
+ applyPagination(table);
387
+ });
388
+ },
389
+
390
+ /**
391
+ * Initialise a table for data-table features.
392
+ * @param {HTMLTableElement} table
393
+ * @param {Object} [opts]
394
+ * @param {number} [opts.pageSize=10]
395
+ */
396
+ init: function (table, opts) {
397
+ if (!table) return;
398
+ // Resolve table if a wrapper was passed
399
+ if (table.tagName !== 'TABLE') {
400
+ var inner = table.querySelector('table');
401
+ if (inner) table = inner;
402
+ }
403
+
404
+ var state = ensureState(table, opts);
405
+ state.allRows = collectRows(table);
406
+ state.filteredRows = state.allRows.slice();
407
+
408
+ // Apply page size from options (override default if provided)
409
+ if (opts && opts.pageSize) {
410
+ state.pageSize = opts.pageSize;
411
+ }
412
+
413
+ // Auto-create pagination bar if attribute present
414
+ createPaginationControls(table);
415
+
416
+ // Initial render
417
+ applyPagination(table);
418
+ }
419
+ };
420
+
421
+ /* ── UMD export ──────────────────────────────────────────── */
422
+ if (root.Stick) root.Stick.use(stkDataTable);
423
+ if (typeof module !== 'undefined' && module.exports) module.exports = stkDataTable;
424
+ root.stkDataTable = stkDataTable;
425
+
426
+ }(typeof window !== 'undefined' ? window : this));
@@ -0,0 +1,70 @@
1
+ /*!
2
+ * Stick UI — Dropdown plugin
3
+ * Usage: data-stick="click:dropdown" data-stick-target="#menu"
4
+ * Features: click-outside close, Escape key, group exclusivity
5
+ */
6
+ (function (root) {
7
+ 'use strict';
8
+
9
+ var stkDropdown = {
10
+ install: function(stick) {
11
+ var openMenus = [];
12
+
13
+ function closeAll() {
14
+ openMenus.forEach(function(entry) {
15
+ entry.menu.hidden = true;
16
+ entry.trigger.setAttribute('aria-expanded', 'false');
17
+ });
18
+ openMenus = [];
19
+ }
20
+
21
+ document.addEventListener('click', function(e) {
22
+ var remaining = [];
23
+ openMenus.forEach(function(entry) {
24
+ if (!entry.trigger.contains(e.target) && !entry.menu.contains(e.target)) {
25
+ entry.menu.hidden = true;
26
+ entry.trigger.setAttribute('aria-expanded', 'false');
27
+ } else {
28
+ remaining.push(entry);
29
+ }
30
+ });
31
+ openMenus = remaining;
32
+ }, true);
33
+
34
+ document.addEventListener('keydown', function(e) {
35
+ if (e.key === 'Escape' && openMenus.length) {
36
+ var last = openMenus[openMenus.length - 1];
37
+ closeAll();
38
+ last.trigger.focus();
39
+ }
40
+ });
41
+
42
+ stick.add('dropdown', function(el, p, e, target) {
43
+ var isOpen = !target.hidden;
44
+
45
+ // Close others in same group
46
+ var group = target.dataset.stickGroup;
47
+ if (group) {
48
+ document.querySelectorAll('[data-stick-group="' + group + '"]').forEach(function(m) {
49
+ if (m !== target) m.hidden = true;
50
+ });
51
+ }
52
+
53
+ target.hidden = isOpen;
54
+ el.setAttribute('aria-expanded', String(!isOpen));
55
+
56
+ if (!isOpen) {
57
+ openMenus.push({ trigger: el, menu: target });
58
+ var first = target.querySelector('[role="menuitem"]');
59
+ if (first) first.focus();
60
+ } else {
61
+ openMenus = openMenus.filter(function(entry) { return entry.menu !== target; });
62
+ }
63
+ });
64
+ }
65
+ };
66
+
67
+ if (root.Stick) root.Stick.use(stkDropdown);
68
+ if (typeof module !== 'undefined' && module.exports) module.exports = stkDropdown;
69
+ root.stkDropdown = stkDropdown;
70
+ }(typeof window !== 'undefined' ? window : this));
@@ -0,0 +1,155 @@
1
+ /*!
2
+ * Stick UI — Stepper / Wizard plugin
3
+ * Usage:
4
+ * data-stick="click:step-next" data-stick-target="#my-stepper"
5
+ * data-stick="click:step-prev" data-stick-target="#my-stepper"
6
+ * data-stick="click:step-goto:2" data-stick-target="#my-stepper"
7
+ *
8
+ * Markup:
9
+ * <div id="my-stepper" class="stk-stepper" data-stk-stepper role="group" aria-label="...">
10
+ * <div class="stk-stepper-header">
11
+ * <div class="stk-stepper-indicator" data-stk-step-indicator aria-current="step">1</div>
12
+ * <div class="stk-stepper-connector"></div>
13
+ * <div class="stk-stepper-indicator" data-stk-step-indicator>2</div>
14
+ * ...
15
+ * </div>
16
+ * <div class="stk-stepper-body">
17
+ * <div class="stk-stepper-step" data-stk-step>Step 1 content</div>
18
+ * <div class="stk-stepper-step" data-stk-step hidden>Step 2 content</div>
19
+ * ...
20
+ * </div>
21
+ * </div>
22
+ */
23
+ (function (root) {
24
+ 'use strict';
25
+
26
+ var state = new WeakMap();
27
+
28
+ function getState(container) {
29
+ if (!state.has(container)) {
30
+ var steps = container.querySelectorAll('[data-stk-step]');
31
+ state.set(container, { currentStep: 0, totalSteps: steps.length });
32
+ }
33
+ return state.get(container);
34
+ }
35
+
36
+ function getSteps(container) {
37
+ return container.querySelectorAll('[data-stk-step]');
38
+ }
39
+
40
+ function getIndicators(container) {
41
+ return container.querySelectorAll('[data-stk-step-indicator]');
42
+ }
43
+
44
+ function getConnectors(container) {
45
+ return container.querySelectorAll('.stk-stepper-connector');
46
+ }
47
+
48
+ /**
49
+ * Validate required fields in the given step element.
50
+ * Returns true if all required fields have values, false otherwise.
51
+ */
52
+ function validateStep(stepEl) {
53
+ var required = stepEl.querySelectorAll('[required]');
54
+ var valid = true;
55
+ for (var i = 0; i < required.length; i++) {
56
+ var input = required[i];
57
+ if (!input.value || !input.value.trim()) {
58
+ valid = false;
59
+ input.classList.add('stk-stepper-invalid');
60
+ // Remove the class after the animation plays
61
+ (function (el) {
62
+ setTimeout(function () { el.classList.remove('stk-stepper-invalid'); }, 800);
63
+ })(input);
64
+ }
65
+ }
66
+ return valid;
67
+ }
68
+
69
+ /**
70
+ * Navigate to a specific step index.
71
+ * @param {Element} container - The stepper container
72
+ * @param {number} index - 0-based target step
73
+ * @param {boolean} skipValidation - skip validation (used for going backward)
74
+ */
75
+ function goTo(container, index, skipValidation) {
76
+ var s = getState(container);
77
+ var steps = getSteps(container);
78
+ var indicators = getIndicators(container);
79
+ var connectors = getConnectors(container);
80
+
81
+ if (index < 0 || index >= s.totalSteps) return;
82
+
83
+ // Validate current step when moving forward
84
+ if (!skipValidation && index > s.currentStep) {
85
+ if (!validateStep(steps[s.currentStep])) return;
86
+ }
87
+
88
+ // Hide all steps, show target
89
+ for (var i = 0; i < steps.length; i++) {
90
+ steps[i].hidden = (i !== index);
91
+ }
92
+
93
+ // Update indicators
94
+ for (var j = 0; j < indicators.length; j++) {
95
+ indicators[j].removeAttribute('aria-current');
96
+ if (j < index) {
97
+ indicators[j].setAttribute('data-stk-step-complete', '');
98
+ } else {
99
+ indicators[j].removeAttribute('data-stk-step-complete');
100
+ }
101
+ if (j === index) {
102
+ indicators[j].setAttribute('aria-current', 'step');
103
+ }
104
+ }
105
+
106
+ // Update connectors — connector at index i sits between indicator i and i+1
107
+ for (var k = 0; k < connectors.length; k++) {
108
+ if (k < index) {
109
+ connectors[k].setAttribute('data-complete', '');
110
+ } else {
111
+ connectors[k].removeAttribute('data-complete');
112
+ }
113
+ }
114
+
115
+ s.currentStep = index;
116
+ }
117
+
118
+ var stkStepper = {
119
+ install: function (stick) {
120
+
121
+ stick.add('step-next', function (el, param, e, target) {
122
+ var container = target && target.hasAttribute('data-stk-stepper') ? target : el.closest('[data-stk-stepper]');
123
+ if (!container) return;
124
+ var s = getState(container);
125
+ if (s.currentStep >= s.totalSteps - 1) {
126
+ // Already on the last step — emit completion event
127
+ container.dispatchEvent(new CustomEvent('stepper-complete', { bubbles: true }));
128
+ return;
129
+ }
130
+ goTo(container, s.currentStep + 1, false);
131
+ });
132
+
133
+ stick.add('step-prev', function (el, param, e, target) {
134
+ var container = target && target.hasAttribute('data-stk-stepper') ? target : el.closest('[data-stk-stepper]');
135
+ if (!container) return;
136
+ var s = getState(container);
137
+ goTo(container, s.currentStep - 1, true);
138
+ });
139
+
140
+ stick.add('step-goto', function (el, param, e, target) {
141
+ var container = target && target.hasAttribute('data-stk-stepper') ? target : el.closest('[data-stk-stepper]');
142
+ if (!container) return;
143
+ var index = parseInt(param, 10);
144
+ if (isNaN(index)) return;
145
+ var s = getState(container);
146
+ var skipValidation = index <= s.currentStep;
147
+ goTo(container, index, skipValidation);
148
+ });
149
+ }
150
+ };
151
+
152
+ if (root.Stick) root.Stick.use(stkStepper);
153
+ if (typeof module !== 'undefined' && module.exports) module.exports = stkStepper;
154
+ root.stkStepper = stkStepper;
155
+ }(typeof window !== 'undefined' ? window : this));
@@ -0,0 +1,51 @@
1
+ /*!
2
+ * Stick UI — Toast plugin
3
+ * Usage: data-stick="click:toast:Message here"
4
+ * data-stick="click:toast:success:Saved!"
5
+ * Container: auto-created, or add <div id="stk-toast-zone"></div>
6
+ */
7
+ (function (root) {
8
+ 'use strict';
9
+
10
+ function getContainer() {
11
+ var c = document.getElementById('stk-toast-zone');
12
+ if (!c) {
13
+ c = document.createElement('div');
14
+ c.id = 'stk-toast-zone';
15
+ c.className = 'stk-toast-zone';
16
+ document.body.appendChild(c);
17
+ }
18
+ return c;
19
+ }
20
+
21
+ function dismiss(t) {
22
+ t.classList.add('stk-toast-exit');
23
+ t.addEventListener('animationend', function() { t.remove(); }, { once: true });
24
+ setTimeout(function() { if (t.parentElement) t.remove(); }, 500);
25
+ }
26
+
27
+ var stkToast = {
28
+ install: function(stick) {
29
+ stick.add('toast', function(el, param) {
30
+ var variant = '', message = param;
31
+ var match = param.match(/^(success|error|warning|info):(.+)/);
32
+ if (match) { variant = match[1]; message = match[2]; }
33
+
34
+ var container = getContainer();
35
+ var toast = document.createElement('div');
36
+ toast.className = 'stk-toast' + (variant ? ' stk-toast-' + variant : '');
37
+ toast.setAttribute('role', 'status');
38
+ toast.setAttribute('aria-live', 'polite');
39
+ toast.innerHTML = '<span class="stk-toast-message">' + message + '</span>' +
40
+ '<button class="stk-toast-dismiss" aria-label="Dismiss">&times;</button>';
41
+ toast.querySelector('.stk-toast-dismiss').addEventListener('click', function() { dismiss(toast); });
42
+ container.appendChild(toast);
43
+ setTimeout(function() { dismiss(toast); }, 3000);
44
+ });
45
+ }
46
+ };
47
+
48
+ if (root.Stick) root.Stick.use(stkToast);
49
+ if (typeof module !== 'undefined' && module.exports) module.exports = stkToast;
50
+ root.stkToast = stkToast;
51
+ }(typeof window !== 'undefined' ? window : this));