j1-template 2022.0.17 → 2022.0.18
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/assets/error_pages/HTTP204.html +1 -1
- data/assets/error_pages/HTTP400.html +1 -1
- data/assets/error_pages/HTTP401.html +1 -1
- data/assets/error_pages/HTTP403.html +1 -1
- data/assets/error_pages/HTTP404.html +1 -1
- data/assets/error_pages/HTTP444.html +1 -1
- data/assets/error_pages/HTTP445.html +1 -1
- data/assets/error_pages/HTTP446.html +1 -1
- data/assets/error_pages/HTTP447.html +1 -1
- data/assets/error_pages/HTTP448.html +1 -1
- data/assets/error_pages/HTTP500.html +1 -1
- data/assets/error_pages/HTTP501.html +1 -1
- data/assets/error_pages/HTTP502.html +1 -1
- data/assets/error_pages/HTTP503.html +1 -1
- data/assets/themes/j1/adapter/js/customFunctions.js +221 -0
- data/assets/themes/j1/adapter/js/customModule.js +221 -0
- data/assets/themes/j1/adapter/js/dropdowns.js +319 -0
- data/assets/themes/j1/adapter/js/rtable.js +2 -6
- data/assets/themes/j1/modules/dropdowns/css/theme/uno/dropdowns.css +109 -0
- data/assets/themes/j1/modules/dropdowns/css/theme/uno/dropdowns.min.css +15 -0
- data/assets/themes/j1/modules/dropdowns/js/cash.js +978 -0
- data/assets/themes/j1/modules/dropdowns/js/dropdowns.js +864 -0
- data/lib/j1/commands/generate.rb +25 -14
- data/lib/j1/commands/rebuild.rb +1 -0
- data/lib/j1/commands/reset.rb +6 -3
- data/lib/j1/commands/setup.rb +18 -13
- data/lib/j1/commands/site.rb +2 -1
- data/lib/j1/utils/exec2.rb +33 -7
- data/lib/j1/version.rb +3 -3
- data/lib/starter_web/Gemfile +1 -1
- data/lib/starter_web/_config.yml +1 -1
- data/lib/starter_web/_data/apps/carousel.yml +138 -140
- data/lib/starter_web/_data/blocks/banner.yml +7 -7
- data/lib/starter_web/_data/modules/defaults/dropdowns.yml +164 -0
- data/lib/starter_web/_data/modules/dropdowns.yml +77 -0
- data/lib/starter_web/_data/resources.yml +73 -2
- data/lib/starter_web/_includes/attributes.asciidoc +1 -1
- data/lib/starter_web/_plugins/lunr_index.rb +2 -2
- data/lib/starter_web/package.json +2 -2
- data/lib/starter_web/pages/public/about/about_site.adoc +1 -1
- data/lib/starter_web/pages/public/asciidoc_skeletons/documentation/000_intro.adoc +1 -1
- data/lib/starter_web/pages/public/asciidoc_skeletons/documentation/documentation.adoc +1 -1
- data/lib/starter_web/pages/public/asciidoc_skeletons/multi-document/multi.adoc +1 -1
- data/lib/starter_web/pages/public/asciidoc_skeletons/simple-document/simple.adoc +1 -1
- data/lib/starter_web/pages/public/learn/roundtrip/100_present_images.adoc +1 -1
- data/lib/starter_web/pages/public/learn/roundtrip/100_present_images.ads.asciidoc +1 -1
- data/lib/starter_web/pages/public/learn/roundtrip/100_present_images.base.asciidoc +1 -1
- data/lib/starter_web/pages/public/learn/roundtrip/100_present_images.comments.asciidoc +1 -1
- data/lib/starter_web/pages/public/learn/roundtrip/200_typography.adoc +1 -1
- data/lib/starter_web/pages/public/learn/roundtrip/300_icon_fonts.adoc +1 -1
- data/lib/starter_web/pages/public/learn/roundtrip/400_asciidoc_extensions.adoc +1 -1
- data/lib/starter_web/pages/public/learn/roundtrip/410_bs_modals_extentions.adoc +1 -1
- data/lib/starter_web/pages/public/learn/roundtrip/420_responsive_tables_extensions.adoc +1 -1
- data/lib/starter_web/pages/public/learn/roundtrip/500_themes.adoc +1 -1
- data/lib/starter_web/pages/public/learn/roundtrip/600_quicksearch.adoc +1 -1
- data/lib/starter_web/pages/public/learn/where_to_go.adoc +1 -1
- data/lib/starter_web/pages/public/legal/en/100_copyright.adoc +1 -1
- data/lib/starter_web/pages/public/legal/en/200_impress.adoc +1 -1
- data/lib/starter_web/pages/public/legal/en/400_comment_policy.adoc +1 -1
- data/lib/starter_web/pages/public/manuals/j1-dropdown.adoc +294 -0
- data/lib/starter_web/pages/public/manuals/{dropdown-help.adoc → msdropdown.adoc} +0 -0
- data/lib/starter_web/pages/public/panels/intro_panel/panel.adoc +1 -1
- data/lib/starter_web/pages/public/previewer/preview_bootstrap_theme.adoc +1 -1
- data/lib/starter_web/utilsrv/_defaults/package.json +1 -1
- data/lib/starter_web/utilsrv/package.json +1 -1
- metadata +14 -6
- data/assets/themes/j1/modules/fab/css/theme/uno/fab.css +0 -373
- data/assets/themes/j1/modules/fab/css/theme/uno/fab.min.css +0 -15
@@ -0,0 +1,864 @@
|
|
1
|
+
/*
|
2
|
+
# -----------------------------------------------------------------------------
|
3
|
+
# ~/assets/themes/j1/modules/dropdowns/js/dropdowns.js
|
4
|
+
# MODIFIED DROPDOWN JS from project Materialize
|
5
|
+
# Provides JS Core funtions for the J1 DROPDOWNS Module
|
6
|
+
#
|
7
|
+
# Product/Info:
|
8
|
+
# https://jekyll.one
|
9
|
+
# https://github.com/Dogfalo/materialize
|
10
|
+
#
|
11
|
+
# Copyright (C) 2021 Juergen Adams
|
12
|
+
#
|
13
|
+
# J1 Template is licensed under the MIT License.
|
14
|
+
# See: https://github.com/jekyll-one-org/J1 Template/blob/master/LICENSE
|
15
|
+
# -----------------------------------------------------------------------------
|
16
|
+
# TODO:
|
17
|
+
# jadams, 2020-10-11: module needs to be rewitten to PURE jQuery
|
18
|
+
# -----------------------------------------------------------------------------
|
19
|
+
*/
|
20
|
+
'use strict';
|
21
|
+
|
22
|
+
// -----------------------------------------------------------------------------
|
23
|
+
// ESLint shimming
|
24
|
+
// -----------------------------------------------------------------------------
|
25
|
+
/* eslint indent: "off" */
|
26
|
+
/* eslint no-unused-vars: "off" */
|
27
|
+
/* eslint no-undef: "off" */
|
28
|
+
/* eslint no-redeclare: "off" */
|
29
|
+
/* eslint indent: "off" */
|
30
|
+
// -----------------------------------------------------------------------------
|
31
|
+
|
32
|
+
(function($, anim) {
|
33
|
+
|
34
|
+
var _defaults = {
|
35
|
+
direction: 'top',
|
36
|
+
hoverEnabled: true,
|
37
|
+
toolbarEnabled: false
|
38
|
+
};
|
39
|
+
|
40
|
+
$.fn.reverse = [].reverse;
|
41
|
+
|
42
|
+
// jadams, 2020-10-10, added class from main (materialize.js)
|
43
|
+
//
|
44
|
+
class Component {
|
45
|
+
|
46
|
+
constructor(classDef, el, options) {
|
47
|
+
// Display error if el is valid HTML Element
|
48
|
+
if (!(el instanceof Element)) {
|
49
|
+
console.error(Error(el + ' is not an HTML Element'));
|
50
|
+
}
|
51
|
+
|
52
|
+
// If exists, destroy and reinitialize in child
|
53
|
+
var ins = classDef.getInstance(el);
|
54
|
+
if (!!ins) {
|
55
|
+
ins.destroy();
|
56
|
+
}
|
57
|
+
|
58
|
+
this.el = el;
|
59
|
+
this.$el = cash(el);
|
60
|
+
}
|
61
|
+
|
62
|
+
/**
|
63
|
+
* Initializes components
|
64
|
+
* @param {class} classDef
|
65
|
+
* @param {Element | NodeList | jQuery} els
|
66
|
+
* @param {Object} options
|
67
|
+
*/
|
68
|
+
static init(classDef, els, options) {
|
69
|
+
var instances = null;
|
70
|
+
if (els instanceof Element) {
|
71
|
+
instances = new classDef(els, options);
|
72
|
+
} else if (!!els && (els.jquery || els.cash || els instanceof NodeList)) {
|
73
|
+
var instancesArr = [];
|
74
|
+
for (var i = 0; i < els.length; i++) {
|
75
|
+
instancesArr.push(new classDef(els[i], options));
|
76
|
+
}
|
77
|
+
instances = instancesArr;
|
78
|
+
}
|
79
|
+
return instances;
|
80
|
+
}
|
81
|
+
}
|
82
|
+
|
83
|
+
/**
|
84
|
+
* Initialize jQuery wrapper for plugin
|
85
|
+
* @param {Class} plugin javascript class
|
86
|
+
* @param {string} pluginName jQuery plugin name
|
87
|
+
* @param {string} classRef Class reference name
|
88
|
+
*/
|
89
|
+
j1.initializeJqueryWrapper = function (plugin, pluginName, classRef) {
|
90
|
+
jQuery.fn[pluginName] = function (methodOrOptions) {
|
91
|
+
// Call plugin method if valid method name is passed in
|
92
|
+
if (plugin.prototype[methodOrOptions]) {
|
93
|
+
var params = Array.prototype.slice.call(arguments, 1);
|
94
|
+
|
95
|
+
// Getter methods
|
96
|
+
if (methodOrOptions.slice(0, 3) === 'get') {
|
97
|
+
var instance = this.first()[0][classRef];
|
98
|
+
return instance[methodOrOptions].apply(instance, params);
|
99
|
+
}
|
100
|
+
|
101
|
+
// Void methods
|
102
|
+
return this.each(function () {
|
103
|
+
var instance = this[classRef];
|
104
|
+
instance[methodOrOptions].apply(instance, params);
|
105
|
+
});
|
106
|
+
|
107
|
+
// Initialize plugin if options or no argument is passed in
|
108
|
+
} else if (typeof methodOrOptions === 'object' || !methodOrOptions) {
|
109
|
+
plugin.init(this, arguments[0]);
|
110
|
+
return this;
|
111
|
+
}
|
112
|
+
|
113
|
+
// Return error if an unrecognized method name is passed in
|
114
|
+
jQuery.error("Method " + methodOrOptions + " does not exist on jQuery." + pluginName);
|
115
|
+
};
|
116
|
+
};
|
117
|
+
|
118
|
+
/**
|
119
|
+
* @class
|
120
|
+
*/
|
121
|
+
class Dropdowns extends Component {
|
122
|
+
constructor(el, options) {
|
123
|
+
super(Dropdowns, el, options);
|
124
|
+
|
125
|
+
this.el.M_Dropdown = this;
|
126
|
+
Dropdowns._dropdowns.push(this);
|
127
|
+
|
128
|
+
this.id = this.getIdFromTrigger(el);
|
129
|
+
this.dropdownEl = document.getElementById(this.id);
|
130
|
+
this.$dropdownEl = $(this.dropdownEl);
|
131
|
+
|
132
|
+
/**
|
133
|
+
* Options for the dropdown
|
134
|
+
* @member Dropdowns#options
|
135
|
+
* @prop {String} [alignment='left'] - Edge which the dropdown is aligned to
|
136
|
+
* @prop {Boolean} [autoFocus=true] - Automatically focus dropdown el for keyboard
|
137
|
+
* @prop {Boolean} [constrainWidth=true] - Constrain width to width of the button
|
138
|
+
* @prop {Element} container - Container element to attach dropdown to (optional)
|
139
|
+
* @prop {Boolean} [coverTrigger=true] - Place dropdown over trigger
|
140
|
+
* @prop {Boolean} [closeOnClick=true] - Close on click of dropdown item
|
141
|
+
* @prop {Boolean} [hover=false] - Open dropdown on hover
|
142
|
+
* @prop {Number} [inDuration=150] - Duration of open animation in ms
|
143
|
+
* @prop {Number} [outDuration=250] - Duration of close animation in ms
|
144
|
+
* @prop {Function} onOpenStart - Function called when dropdown starts opening
|
145
|
+
* @prop {Function} onOpenEnd - Function called when dropdown finishes opening
|
146
|
+
* @prop {Function} onCloseStart - Function called when dropdown starts closing
|
147
|
+
* @prop {Function} onCloseEnd - Function called when dropdown finishes closing
|
148
|
+
*/
|
149
|
+
this.options = $.extend({}, Dropdowns.defaults, options);
|
150
|
+
|
151
|
+
/**
|
152
|
+
* Describes open/close state of dropdown
|
153
|
+
* @type {Boolean}
|
154
|
+
*/
|
155
|
+
this.isOpen = false;
|
156
|
+
|
157
|
+
/**
|
158
|
+
* Describes if dropdown content is scrollable
|
159
|
+
* @type {Boolean}
|
160
|
+
*/
|
161
|
+
this.isScrollable = false;
|
162
|
+
|
163
|
+
/**
|
164
|
+
* Describes if touch moving on dropdown content
|
165
|
+
* @type {Boolean}
|
166
|
+
*/
|
167
|
+
this.isTouchMoving = false;
|
168
|
+
|
169
|
+
this.focusedIndex = -1;
|
170
|
+
this.filterQuery = [];
|
171
|
+
|
172
|
+
this.keys = {
|
173
|
+
TAB: 9,
|
174
|
+
ENTER: 13,
|
175
|
+
ESC: 27,
|
176
|
+
ARROW_UP: 38,
|
177
|
+
ARROW_DOWN: 40
|
178
|
+
};
|
179
|
+
|
180
|
+
// Move dropdown-content after dropdown-trigger
|
181
|
+
if (!!this.options.container) {
|
182
|
+
$(this.options.container).append(this.dropdownEl);
|
183
|
+
} else {
|
184
|
+
this.$el.after(this.dropdownEl);
|
185
|
+
}
|
186
|
+
|
187
|
+
this._makeDropdownFocusable();
|
188
|
+
this._resetFilterQueryBound = this._resetFilterQuery.bind(this);
|
189
|
+
this._handleDocumentClickBound = this._handleDocumentClick.bind(this);
|
190
|
+
this._handleDocumentTouchmoveBound = this._handleDocumentTouchmove.bind(this);
|
191
|
+
this._handleDropdownClickBound = this._handleDropdownClick.bind(this);
|
192
|
+
this._handleDropdownKeydownBound = this._handleDropdownKeydown.bind(this);
|
193
|
+
this._handleTriggerKeydownBound = this._handleTriggerKeydown.bind(this);
|
194
|
+
this._setupEventHandlers();
|
195
|
+
}
|
196
|
+
|
197
|
+
static get defaults() {
|
198
|
+
return _defaults;
|
199
|
+
}
|
200
|
+
|
201
|
+
static init(els, options) {
|
202
|
+
return super.init(this, els, options);
|
203
|
+
}
|
204
|
+
|
205
|
+
/**
|
206
|
+
* Get Instance
|
207
|
+
*/
|
208
|
+
static getInstance(el) {
|
209
|
+
let domElem = !!el.jquery ? el[0] : el;
|
210
|
+
return domElem.M_Dropdown;
|
211
|
+
}
|
212
|
+
|
213
|
+
/**
|
214
|
+
* Gets id of component from a trigger
|
215
|
+
* @param {Element} trigger trigger
|
216
|
+
* @returns {string}
|
217
|
+
*/
|
218
|
+
getIdFromTrigger (trigger) {
|
219
|
+
var id = trigger.getAttribute('data-target');
|
220
|
+
if (!id) {
|
221
|
+
id = trigger.getAttribute('href');
|
222
|
+
if (id) {
|
223
|
+
id = id.slice(1);
|
224
|
+
} else {
|
225
|
+
id = '';
|
226
|
+
}
|
227
|
+
}
|
228
|
+
return id;
|
229
|
+
}
|
230
|
+
|
231
|
+
// ---------------------------------------------------------------------------
|
232
|
+
// executeFunctionByName()
|
233
|
+
// execute a function by NAME (functionName) in a browser context
|
234
|
+
// (e.g. window) the function is published
|
235
|
+
// ---------------------------------------------------------------------------
|
236
|
+
executeFunctionByName (functionName, context /*, args */) {
|
237
|
+
var args = Array.prototype.slice.call(arguments, 2);
|
238
|
+
var namespaces = functionName.split('.');
|
239
|
+
var func = namespaces.pop();
|
240
|
+
for(var i = 0; i < namespaces.length; i++) {
|
241
|
+
context = context[namespaces[i]];
|
242
|
+
}
|
243
|
+
return context[func].apply(context, args);
|
244
|
+
}
|
245
|
+
|
246
|
+
checkPossibleAlignments (el, container, bounding, offset) {
|
247
|
+
var canAlign = {
|
248
|
+
top: true,
|
249
|
+
right: true,
|
250
|
+
bottom: true,
|
251
|
+
left: true,
|
252
|
+
spaceOnTop: null,
|
253
|
+
spaceOnRight: null,
|
254
|
+
spaceOnBottom: null,
|
255
|
+
spaceOnLeft: null
|
256
|
+
};
|
257
|
+
|
258
|
+
var containerAllowsOverflow = getComputedStyle(container).overflow === 'visible';
|
259
|
+
var containerRect = container.getBoundingClientRect();
|
260
|
+
var containerHeight = Math.min(containerRect.height, window.innerHeight);
|
261
|
+
var containerWidth = Math.min(containerRect.width, window.innerWidth);
|
262
|
+
var elOffsetRect = el.getBoundingClientRect();
|
263
|
+
|
264
|
+
var scrollLeft = container.scrollLeft;
|
265
|
+
var scrollTop = container.scrollTop;
|
266
|
+
|
267
|
+
var scrolledX = bounding.left - scrollLeft;
|
268
|
+
var scrolledYTopEdge = bounding.top - scrollTop;
|
269
|
+
var scrolledYBottomEdge = bounding.top + elOffsetRect.height - scrollTop;
|
270
|
+
|
271
|
+
// Check for container and viewport for left
|
272
|
+
canAlign.spaceOnRight = !containerAllowsOverflow ? containerWidth - (scrolledX + bounding.width) : window.innerWidth - (elOffsetRect.left + bounding.width);
|
273
|
+
if (canAlign.spaceOnRight < 0) {
|
274
|
+
canAlign.left = false;
|
275
|
+
}
|
276
|
+
|
277
|
+
// Check for container and viewport for Right
|
278
|
+
canAlign.spaceOnLeft = !containerAllowsOverflow ? scrolledX - bounding.width + elOffsetRect.width : elOffsetRect.right - bounding.width;
|
279
|
+
if (canAlign.spaceOnLeft < 0) {
|
280
|
+
canAlign.right = false;
|
281
|
+
}
|
282
|
+
|
283
|
+
// Check for container and viewport for Top
|
284
|
+
canAlign.spaceOnBottom = !containerAllowsOverflow ? containerHeight - (scrolledYTopEdge + bounding.height + offset) : window.innerHeight - (elOffsetRect.top + bounding.height + offset);
|
285
|
+
if (canAlign.spaceOnBottom < 0) {
|
286
|
+
canAlign.top = false;
|
287
|
+
}
|
288
|
+
|
289
|
+
// Check for container and viewport for Bottom
|
290
|
+
canAlign.spaceOnTop = !containerAllowsOverflow ? scrolledYBottomEdge - (bounding.height - offset) : elOffsetRect.bottom - (bounding.height + offset);
|
291
|
+
if (canAlign.spaceOnTop < 0) {
|
292
|
+
canAlign.bottom = false;
|
293
|
+
}
|
294
|
+
|
295
|
+
return canAlign;
|
296
|
+
}
|
297
|
+
|
298
|
+
/**
|
299
|
+
* Teardown component
|
300
|
+
*/
|
301
|
+
destroy() {
|
302
|
+
this._resetDropdownStyles();
|
303
|
+
this._removeEventHandlers();
|
304
|
+
Dropdowns._dropdowns.splice(Dropdowns._dropdowns.indexOf(this), 1);
|
305
|
+
this.el.M_Dropdown = undefined;
|
306
|
+
}
|
307
|
+
|
308
|
+
/**
|
309
|
+
* Setup Event Handlers
|
310
|
+
*/
|
311
|
+
_setupEventHandlers() {
|
312
|
+
// Trigger keydown handler
|
313
|
+
this.el.addEventListener('keydown', this._handleTriggerKeydownBound);
|
314
|
+
|
315
|
+
// Item click handler
|
316
|
+
this.dropdownEl.addEventListener('click', this._handleDropdownClickBound);
|
317
|
+
|
318
|
+
// Hover event handlers
|
319
|
+
if (this.options.hover) {
|
320
|
+
this._handleMouseEnterBound = this._handleMouseEnter.bind(this);
|
321
|
+
this.el.addEventListener('mouseenter', this._handleMouseEnterBound);
|
322
|
+
this._handleMouseLeaveBound = this._handleMouseLeave.bind(this);
|
323
|
+
this.el.addEventListener('mouseleave', this._handleMouseLeaveBound);
|
324
|
+
this.dropdownEl.addEventListener('mouseleave', this._handleMouseLeaveBound);
|
325
|
+
|
326
|
+
// Click event handlers
|
327
|
+
} else {
|
328
|
+
this._handleClickBound = this._handleClick.bind(this);
|
329
|
+
this.el.addEventListener('click', this._handleClickBound);
|
330
|
+
}
|
331
|
+
}
|
332
|
+
|
333
|
+
/**
|
334
|
+
* Remove Event Handlers
|
335
|
+
*/
|
336
|
+
_removeEventHandlers() {
|
337
|
+
this.el.removeEventListener('keydown', this._handleTriggerKeydownBound);
|
338
|
+
this.dropdownEl.removeEventListener('click', this._handleDropdownClickBound);
|
339
|
+
|
340
|
+
if (this.options.hover) {
|
341
|
+
this.el.removeEventListener('mouseenter', this._handleMouseEnterBound);
|
342
|
+
this.el.removeEventListener('mouseleave', this._handleMouseLeaveBound);
|
343
|
+
this.dropdownEl.removeEventListener('mouseleave', this._handleMouseLeaveBound);
|
344
|
+
} else {
|
345
|
+
this.el.removeEventListener('click', this._handleClickBound);
|
346
|
+
}
|
347
|
+
}
|
348
|
+
|
349
|
+
_setupTemporaryEventHandlers() {
|
350
|
+
// Use capture phase event handler to prevent click
|
351
|
+
document.body.addEventListener('click', this._handleDocumentClickBound, true);
|
352
|
+
document.body.addEventListener('touchend', this._handleDocumentClickBound);
|
353
|
+
document.body.addEventListener('touchmove', this._handleDocumentTouchmoveBound);
|
354
|
+
this.dropdownEl.addEventListener('keydown', this._handleDropdownKeydownBound);
|
355
|
+
}
|
356
|
+
|
357
|
+
_removeTemporaryEventHandlers() {
|
358
|
+
// Use capture phase event handler to prevent click
|
359
|
+
document.body.removeEventListener('click', this._handleDocumentClickBound, true);
|
360
|
+
document.body.removeEventListener('touchend', this._handleDocumentClickBound);
|
361
|
+
document.body.removeEventListener('touchmove', this._handleDocumentTouchmoveBound);
|
362
|
+
this.dropdownEl.removeEventListener('keydown', this._handleDropdownKeydownBound);
|
363
|
+
}
|
364
|
+
|
365
|
+
_handleClick(e) {
|
366
|
+
e.preventDefault();
|
367
|
+
this.open();
|
368
|
+
}
|
369
|
+
|
370
|
+
_handleMouseEnter() {
|
371
|
+
this.open();
|
372
|
+
}
|
373
|
+
|
374
|
+
_handleMouseLeave(e) {
|
375
|
+
let toEl = e.toElement || e.relatedTarget;
|
376
|
+
let leaveToDropdownContent = !!$(toEl).closest('.dropdown-content').length;
|
377
|
+
let leaveToActiveDropdownTrigger = false;
|
378
|
+
|
379
|
+
let $closestTrigger = $(toEl).closest('.dropdowns');
|
380
|
+
if (
|
381
|
+
$closestTrigger.length &&
|
382
|
+
!!$closestTrigger[0].M_Dropdown &&
|
383
|
+
$closestTrigger[0].M_Dropdown.isOpen
|
384
|
+
) {
|
385
|
+
leaveToActiveDropdownTrigger = true;
|
386
|
+
}
|
387
|
+
|
388
|
+
// Close hover dropdown if mouse did not leave to either active dropdown-trigger or dropdown-content
|
389
|
+
if (!leaveToActiveDropdownTrigger && !leaveToDropdownContent) {
|
390
|
+
this.close();
|
391
|
+
}
|
392
|
+
}
|
393
|
+
|
394
|
+
_handleDocumentClick(e) {
|
395
|
+
let $target = $(e.target);
|
396
|
+
if (
|
397
|
+
this.options.closeOnClick &&
|
398
|
+
$target.closest('.dropdown-content').length &&
|
399
|
+
!this.isTouchMoving
|
400
|
+
) {
|
401
|
+
// isTouchMoving to check if scrolling on mobile.
|
402
|
+
setTimeout(() => {
|
403
|
+
this.close();
|
404
|
+
}, 0);
|
405
|
+
} else if (
|
406
|
+
$target.closest('.dropdowns').length ||
|
407
|
+
!$target.closest('.dropdown-content').length
|
408
|
+
) {
|
409
|
+
setTimeout(() => {
|
410
|
+
this.close();
|
411
|
+
}, 0);
|
412
|
+
}
|
413
|
+
this.isTouchMoving = false;
|
414
|
+
}
|
415
|
+
|
416
|
+
_handleTriggerKeydown(e) {
|
417
|
+
// ARROW DOWN OR ENTER WHEN SELECT IS CLOSED - open Dropdowns
|
418
|
+
if ((e.which === this.keys.ARROW_DOWN || e.which === this.keys.ENTER) && !this.isOpen) {
|
419
|
+
e.preventDefault();
|
420
|
+
this.open();
|
421
|
+
}
|
422
|
+
}
|
423
|
+
|
424
|
+
/**
|
425
|
+
* Handle Document Touchmove
|
426
|
+
* @param {Event} e
|
427
|
+
*/
|
428
|
+
_handleDocumentTouchmove(e) {
|
429
|
+
let $target = $(e.target);
|
430
|
+
if ($target.closest('.dropdown-content').length) {
|
431
|
+
this.isTouchMoving = true;
|
432
|
+
}
|
433
|
+
}
|
434
|
+
|
435
|
+
/**
|
436
|
+
* Handle Dropdowns Click
|
437
|
+
* @param {Event} e
|
438
|
+
*/
|
439
|
+
_handleDropdownClick(e) {
|
440
|
+
|
441
|
+
// onItemClick callback (by reference)
|
442
|
+
// if (typeof this.options.onItemClick === 'function') {
|
443
|
+
// let itemEl = $(e.target).closest('li')[0];
|
444
|
+
// this.options.onItemClick.call(this, itemEl);
|
445
|
+
// }
|
446
|
+
|
447
|
+
// onItemClick callback (by name)
|
448
|
+
if (this.options.onItemClick !== 'false') {
|
449
|
+
var _this = this;
|
450
|
+
// var itemEl = $(e.target).closest('li')[0];
|
451
|
+
this.executeFunctionByName(this.options.onItemClick, window, _this);
|
452
|
+
}
|
453
|
+
}
|
454
|
+
|
455
|
+
/**
|
456
|
+
* Handle Dropdowns Keydown
|
457
|
+
* @param {Event} e
|
458
|
+
*/
|
459
|
+
_handleDropdownKeydown(e) {
|
460
|
+
if (e.which === this.keys.TAB) {
|
461
|
+
e.preventDefault();
|
462
|
+
this.close();
|
463
|
+
|
464
|
+
// Navigate down dropdown list
|
465
|
+
} else if ((e.which === this.keys.ARROW_DOWN || e.which === this.keys.ARROW_UP) && this.isOpen) {
|
466
|
+
e.preventDefault();
|
467
|
+
let direction = e.which === this.keys.ARROW_DOWN ? 1 : -1;
|
468
|
+
let newFocusedIndex = this.focusedIndex;
|
469
|
+
let foundNewIndex = false;
|
470
|
+
do {
|
471
|
+
newFocusedIndex = newFocusedIndex + direction;
|
472
|
+
|
473
|
+
if (
|
474
|
+
!!this.dropdownEl.children[newFocusedIndex] &&
|
475
|
+
this.dropdownEl.children[newFocusedIndex].tabIndex !== -1
|
476
|
+
) {
|
477
|
+
foundNewIndex = true;
|
478
|
+
break;
|
479
|
+
}
|
480
|
+
} while (newFocusedIndex < this.dropdownEl.children.length && newFocusedIndex >= 0);
|
481
|
+
|
482
|
+
if (foundNewIndex) {
|
483
|
+
this.focusedIndex = newFocusedIndex;
|
484
|
+
this._focusFocusedItem();
|
485
|
+
}
|
486
|
+
|
487
|
+
// ENTER selects choice on focused item
|
488
|
+
} else if (e.which === this.keys.ENTER && this.isOpen) {
|
489
|
+
// Search for <a> and <button>
|
490
|
+
let focusedElement = this.dropdownEl.children[this.focusedIndex];
|
491
|
+
let $activatableElement = $(focusedElement)
|
492
|
+
.find('a, button')
|
493
|
+
.first();
|
494
|
+
|
495
|
+
// Click a or button tag if exists, otherwise click li tag
|
496
|
+
if (!!$activatableElement.length) {
|
497
|
+
$activatableElement[0].click();
|
498
|
+
} else if (!!focusedElement) {
|
499
|
+
focusedElement.click();
|
500
|
+
}
|
501
|
+
|
502
|
+
// Close dropdown on ESC
|
503
|
+
} else if (e.which === this.keys.ESC && this.isOpen) {
|
504
|
+
e.preventDefault();
|
505
|
+
this.close();
|
506
|
+
}
|
507
|
+
|
508
|
+
// CASE WHEN USER TYPE LETTERS
|
509
|
+
let letter = String.fromCharCode(e.which).toLowerCase(),
|
510
|
+
nonLetters = [9, 13, 27, 38, 40];
|
511
|
+
if (letter && nonLetters.indexOf(e.which) === -1) {
|
512
|
+
this.filterQuery.push(letter);
|
513
|
+
|
514
|
+
let string = this.filterQuery.join(''),
|
515
|
+
newOptionEl = $(this.dropdownEl)
|
516
|
+
.find('li')
|
517
|
+
.filter((el) => {
|
518
|
+
return (
|
519
|
+
$(el)
|
520
|
+
.text()
|
521
|
+
.toLowerCase()
|
522
|
+
.indexOf(string) === 0
|
523
|
+
);
|
524
|
+
})[0];
|
525
|
+
|
526
|
+
if (newOptionEl) {
|
527
|
+
this.focusedIndex = $(newOptionEl).index();
|
528
|
+
this._focusFocusedItem();
|
529
|
+
}
|
530
|
+
}
|
531
|
+
|
532
|
+
this.filterTimeout = setTimeout(this._resetFilterQueryBound, 1000);
|
533
|
+
}
|
534
|
+
|
535
|
+
/**
|
536
|
+
* Setup dropdown
|
537
|
+
*/
|
538
|
+
_resetFilterQuery() {
|
539
|
+
this.filterQuery = [];
|
540
|
+
}
|
541
|
+
|
542
|
+
_resetDropdownStyles() {
|
543
|
+
this.$dropdownEl.css({
|
544
|
+
display: '',
|
545
|
+
width: '',
|
546
|
+
height: '',
|
547
|
+
left: '',
|
548
|
+
top: '',
|
549
|
+
'transform-origin': '',
|
550
|
+
transform: '',
|
551
|
+
opacity: ''
|
552
|
+
});
|
553
|
+
}
|
554
|
+
|
555
|
+
_makeDropdownFocusable() {
|
556
|
+
// Needed for arrow key navigation
|
557
|
+
this.dropdownEl.tabIndex = 0;
|
558
|
+
|
559
|
+
// Only set tabindex if it hasn't been set by user
|
560
|
+
$(this.dropdownEl)
|
561
|
+
.children()
|
562
|
+
.each(function(el) {
|
563
|
+
if (!el.getAttribute('tabindex')) {
|
564
|
+
el.setAttribute('tabindex', 0);
|
565
|
+
}
|
566
|
+
});
|
567
|
+
}
|
568
|
+
|
569
|
+
_focusFocusedItem() {
|
570
|
+
if (
|
571
|
+
this.focusedIndex >= 0 &&
|
572
|
+
this.focusedIndex < this.dropdownEl.children.length &&
|
573
|
+
this.options.autoFocus
|
574
|
+
) {
|
575
|
+
this.dropdownEl.children[this.focusedIndex].focus();
|
576
|
+
}
|
577
|
+
}
|
578
|
+
|
579
|
+
_getDropdownPosition() {
|
580
|
+
let offsetParentBRect = this.el.offsetParent.getBoundingClientRect();
|
581
|
+
let triggerBRect = this.el.getBoundingClientRect();
|
582
|
+
let dropdownBRect = this.dropdownEl.getBoundingClientRect();
|
583
|
+
|
584
|
+
let idealHeight = dropdownBRect.height;
|
585
|
+
let idealWidth = dropdownBRect.width;
|
586
|
+
let idealXPos = triggerBRect.left - dropdownBRect.left;
|
587
|
+
let idealYPos = triggerBRect.top - dropdownBRect.top;
|
588
|
+
|
589
|
+
let dropdownBounds = {
|
590
|
+
left: idealXPos,
|
591
|
+
top: idealYPos,
|
592
|
+
height: idealHeight,
|
593
|
+
width: idealWidth
|
594
|
+
};
|
595
|
+
|
596
|
+
// Countainer here will be closest ancestor with overflow: hidden
|
597
|
+
let closestOverflowParent = !!this.dropdownEl.offsetParent
|
598
|
+
? this.dropdownEl.offsetParent
|
599
|
+
: this.dropdownEl.parentNode;
|
600
|
+
|
601
|
+
let alignments = this.checkPossibleAlignments(
|
602
|
+
this.el,
|
603
|
+
closestOverflowParent,
|
604
|
+
dropdownBounds,
|
605
|
+
this.options.coverTrigger ? 0 : triggerBRect.height
|
606
|
+
);
|
607
|
+
|
608
|
+
let verticalAlignment = 'top';
|
609
|
+
let horizontalAlignment = this.options.alignment;
|
610
|
+
idealYPos += this.options.coverTrigger ? 0 : triggerBRect.height;
|
611
|
+
|
612
|
+
// Reset isScrollable
|
613
|
+
this.isScrollable = false;
|
614
|
+
|
615
|
+
if (!alignments.top) {
|
616
|
+
if (alignments.bottom) {
|
617
|
+
verticalAlignment = 'bottom';
|
618
|
+
} else {
|
619
|
+
this.isScrollable = true;
|
620
|
+
|
621
|
+
// Determine which side has most space and cutoff at correct height
|
622
|
+
if (alignments.spaceOnTop > alignments.spaceOnBottom) {
|
623
|
+
verticalAlignment = 'bottom';
|
624
|
+
idealHeight += alignments.spaceOnTop;
|
625
|
+
idealYPos -= alignments.spaceOnTop;
|
626
|
+
} else {
|
627
|
+
idealHeight += alignments.spaceOnBottom;
|
628
|
+
}
|
629
|
+
}
|
630
|
+
}
|
631
|
+
|
632
|
+
// If preferred horizontal alignment is possible
|
633
|
+
if (!alignments[horizontalAlignment]) {
|
634
|
+
let oppositeAlignment = horizontalAlignment === 'left' ? 'right' : 'left';
|
635
|
+
if (alignments[oppositeAlignment]) {
|
636
|
+
horizontalAlignment = oppositeAlignment;
|
637
|
+
} else {
|
638
|
+
// Determine which side has most space and cutoff at correct height
|
639
|
+
if (alignments.spaceOnLeft > alignments.spaceOnRight) {
|
640
|
+
horizontalAlignment = 'right';
|
641
|
+
idealWidth += alignments.spaceOnLeft;
|
642
|
+
idealXPos -= alignments.spaceOnLeft;
|
643
|
+
} else {
|
644
|
+
horizontalAlignment = 'left';
|
645
|
+
idealWidth += alignments.spaceOnRight;
|
646
|
+
}
|
647
|
+
}
|
648
|
+
}
|
649
|
+
|
650
|
+
if (verticalAlignment === 'bottom') {
|
651
|
+
idealYPos =
|
652
|
+
idealYPos - dropdownBRect.height + (this.options.coverTrigger ? triggerBRect.height : 0);
|
653
|
+
}
|
654
|
+
if (horizontalAlignment === 'right') {
|
655
|
+
idealXPos = idealXPos - dropdownBRect.width + triggerBRect.width;
|
656
|
+
}
|
657
|
+
return {
|
658
|
+
x: idealXPos,
|
659
|
+
y: idealYPos,
|
660
|
+
verticalAlignment: verticalAlignment,
|
661
|
+
horizontalAlignment: horizontalAlignment,
|
662
|
+
height: idealHeight,
|
663
|
+
width: idealWidth
|
664
|
+
};
|
665
|
+
}
|
666
|
+
|
667
|
+
/**
|
668
|
+
* Animate in dropdown
|
669
|
+
*/
|
670
|
+
_animateIn() {
|
671
|
+
anim.remove(this.dropdownEl);
|
672
|
+
anim({
|
673
|
+
targets: this.dropdownEl,
|
674
|
+
opacity: {
|
675
|
+
value: [0, 1],
|
676
|
+
easing: 'easeOutQuad'
|
677
|
+
},
|
678
|
+
scaleX: [0.3, 1],
|
679
|
+
scaleY: [0.3, 1],
|
680
|
+
duration: this.options.inDuration,
|
681
|
+
easing: 'easeOutQuint',
|
682
|
+
complete: (anim) => {
|
683
|
+
if (this.options.autoFocus) {
|
684
|
+
this.dropdownEl.focus();
|
685
|
+
}
|
686
|
+
|
687
|
+
// onOpenEnd callback (by reference)
|
688
|
+
if (typeof this.options.onOpenEnd === 'function') {
|
689
|
+
this.options.onOpenEnd.call(this, this.el);
|
690
|
+
}
|
691
|
+
}
|
692
|
+
});
|
693
|
+
}
|
694
|
+
|
695
|
+
/**
|
696
|
+
* Animate out dropdown
|
697
|
+
*/
|
698
|
+
_animateOut() {
|
699
|
+
anim.remove(this.dropdownEl);
|
700
|
+
anim({
|
701
|
+
targets: this.dropdownEl,
|
702
|
+
opacity: {
|
703
|
+
value: 0,
|
704
|
+
easing: 'easeOutQuint'
|
705
|
+
},
|
706
|
+
scaleX: 0.3,
|
707
|
+
scaleY: 0.3,
|
708
|
+
duration: this.options.outDuration,
|
709
|
+
easing: 'easeOutQuint',
|
710
|
+
complete: (anim) => {
|
711
|
+
this._resetDropdownStyles();
|
712
|
+
|
713
|
+
// onCloseEnd callback (by reference)
|
714
|
+
if (typeof this.options.onCloseEnd === 'function') {
|
715
|
+
this.options.onCloseEnd.call(this, this.el);
|
716
|
+
}
|
717
|
+
}
|
718
|
+
});
|
719
|
+
}
|
720
|
+
|
721
|
+
/**
|
722
|
+
* Place dropdown
|
723
|
+
*/
|
724
|
+
_placeDropdown() {
|
725
|
+
// Set width before calculating positionInfo
|
726
|
+
let idealWidth = this.options.constrainWidth
|
727
|
+
? this.el.getBoundingClientRect().width
|
728
|
+
: this.dropdownEl.getBoundingClientRect().width;
|
729
|
+
this.dropdownEl.style.width = idealWidth + 'px';
|
730
|
+
|
731
|
+
let positionInfo = this._getDropdownPosition();
|
732
|
+
this.dropdownEl.style.left = positionInfo.x + 'px';
|
733
|
+
this.dropdownEl.style.top = positionInfo.y + 'px';
|
734
|
+
this.dropdownEl.style.height = positionInfo.height + 'px';
|
735
|
+
this.dropdownEl.style.width = positionInfo.width + 'px';
|
736
|
+
this.dropdownEl.style.transformOrigin = `${
|
737
|
+
positionInfo.horizontalAlignment === 'left' ? '0' : '100%'
|
738
|
+
} ${positionInfo.verticalAlignment === 'top' ? '0' : '100%'}`;
|
739
|
+
}
|
740
|
+
|
741
|
+
/**
|
742
|
+
* Open Dropdowns
|
743
|
+
*/
|
744
|
+
open() {
|
745
|
+
if (this.isOpen) {
|
746
|
+
return;
|
747
|
+
}
|
748
|
+
this.isOpen = true;
|
749
|
+
|
750
|
+
// onOpen callback (by reference)
|
751
|
+
// if (typeof this.options.onOpen === 'function') {
|
752
|
+
// this.options.onOpen.call(this, this.el);
|
753
|
+
// }
|
754
|
+
|
755
|
+
var _this = this;
|
756
|
+
var listItems = '#' + _this.id + " li";
|
757
|
+
var menuItems = document.querySelectorAll(listItems);
|
758
|
+
|
759
|
+
// Loop through each <li> element
|
760
|
+
for (var i=0; i < menuItems.length; i++) {
|
761
|
+
// adding a event listener, mark selected menuItem by class active
|
762
|
+
menuItems[i].addEventListener('click', function(event) {
|
763
|
+
event.preventDefault();
|
764
|
+
for(var i=0; i < menuItems.length; i++) {
|
765
|
+
if (menuItems[i].classList.contains('active')) {
|
766
|
+
menuItems[i].classList.remove('active');
|
767
|
+
}
|
768
|
+
}
|
769
|
+
// add `active` to current clicked element
|
770
|
+
this.classList.add('active');
|
771
|
+
}, false);
|
772
|
+
}
|
773
|
+
|
774
|
+
// onOpen callback (ny name)
|
775
|
+
var _this = this;
|
776
|
+
if (this.options.onOpen) {
|
777
|
+
this.executeFunctionByName(this.options.onOpen, window, _this);
|
778
|
+
}
|
779
|
+
|
780
|
+
// Reset styles
|
781
|
+
this._resetDropdownStyles();
|
782
|
+
this.dropdownEl.style.display = 'block';
|
783
|
+
|
784
|
+
this._placeDropdown();
|
785
|
+
this._animateIn();
|
786
|
+
this._setupTemporaryEventHandlers();
|
787
|
+
}
|
788
|
+
|
789
|
+
/**
|
790
|
+
* Close Dropdowns
|
791
|
+
*/
|
792
|
+
close() {
|
793
|
+
if (!this.isOpen) {
|
794
|
+
return;
|
795
|
+
}
|
796
|
+
this.isOpen = false;
|
797
|
+
this.focusedIndex = -1;
|
798
|
+
|
799
|
+
// onClose callback (by reference)
|
800
|
+
// if (typeof this.options.onClose === 'function') {
|
801
|
+
// this.options.onCloseStart.call(this, this.el);
|
802
|
+
// }
|
803
|
+
|
804
|
+
// onClose callback (by name)
|
805
|
+
var _this = this;
|
806
|
+
if (this.options.onClose) {
|
807
|
+
this.executeFunctionByName(this.options.onClose, window, _this);
|
808
|
+
}
|
809
|
+
|
810
|
+
this._animateOut();
|
811
|
+
this._removeTemporaryEventHandlers();
|
812
|
+
|
813
|
+
if (this.options.autoFocus) {
|
814
|
+
this.el.focus();
|
815
|
+
}
|
816
|
+
}
|
817
|
+
|
818
|
+
/**
|
819
|
+
* Recalculate dimensions
|
820
|
+
*/
|
821
|
+
recalculateDimensions() {
|
822
|
+
if (this.isOpen) {
|
823
|
+
this.$dropdownEl.css({
|
824
|
+
width: '',
|
825
|
+
height: '',
|
826
|
+
left: '',
|
827
|
+
top: '',
|
828
|
+
'transform-origin': ''
|
829
|
+
});
|
830
|
+
this._placeDropdown();
|
831
|
+
}
|
832
|
+
}
|
833
|
+
}
|
834
|
+
|
835
|
+
/**
|
836
|
+
* @static
|
837
|
+
* @memberof Dropdowns
|
838
|
+
*/
|
839
|
+
Dropdowns._dropdowns = [];
|
840
|
+
|
841
|
+
// jadams, 2020-10-10: moved to j1 name space
|
842
|
+
//
|
843
|
+
// M.FloatingActionButton = FloatingActionButton;
|
844
|
+
j1.dropdowns = Dropdowns;
|
845
|
+
|
846
|
+
// jadams, 2020-10-10: check how to transform to jQuery
|
847
|
+
//
|
848
|
+
|
849
|
+
// Check for jQuery
|
850
|
+
//
|
851
|
+
j1.jQueryLoaded = !!window.jQuery;
|
852
|
+
|
853
|
+
if (j1.jQueryLoaded) {
|
854
|
+
j1.initializeJqueryWrapper (
|
855
|
+
Dropdowns,
|
856
|
+
'dropdown',
|
857
|
+
'M_Dropdown'
|
858
|
+
);
|
859
|
+
}
|
860
|
+
|
861
|
+
// jadams, 2020-10-10: TODO: check if anime could be a replacement
|
862
|
+
// for (huge) animate.css
|
863
|
+
// })($, j1.anime);
|
864
|
+
})(cash, j1.anime);
|