@schukai/monster 3.73.9 → 3.75.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/package.json +1 -1
  3. package/source/components/datatable/change-button.mjs +4 -4
  4. package/source/components/datatable/columnbar.mjs +2 -2
  5. package/source/components/datatable/dataset.mjs +2 -2
  6. package/source/components/datatable/datasource/rest.mjs +8 -8
  7. package/source/components/datatable/datatable.mjs +6 -6
  8. package/source/components/datatable/filter/date-range.mjs +6 -6
  9. package/source/components/datatable/filter/range.mjs +4 -4
  10. package/source/components/datatable/filter.mjs +4 -4
  11. package/source/components/datatable/save-button.mjs +4 -4
  12. package/source/components/datatable/util.mjs +2 -2
  13. package/source/components/form/api-button.mjs +0 -1
  14. package/source/components/form/context-error.mjs +0 -1
  15. package/source/components/form/context-help.mjs +0 -1
  16. package/source/components/form/message-state-button.mjs +0 -1
  17. package/source/components/form/popper-button.mjs +0 -1
  18. package/source/components/form/select.mjs +16 -5
  19. package/source/components/form/toggle-switch.mjs +0 -3
  20. package/source/components/form/tree-select.mjs +0 -1
  21. package/source/components/layout/slider.mjs +649 -0
  22. package/source/components/layout/style/slider.pcss +114 -0
  23. package/source/components/layout/stylesheet/slider.mjs +38 -0
  24. package/source/components/navigation/table-of-content.mjs +0 -1
  25. package/source/components/tree-menu/dragable-tree-menu.mjs +4 -4
  26. package/source/components/tree-menu/tree-menu.mjs +6 -6
  27. package/source/data/datasource/server/restapi.mjs +0 -1
  28. package/source/dom/events.mjs +1 -1
  29. package/source/dom/updater.mjs +767 -772
  30. package/source/monster.mjs +11 -3
  31. package/source/types/observer.mjs +7 -8
  32. package/source/types/version.mjs +1 -1
  33. package/test/cases/monster.mjs +1 -1
  34. package/test/web/test.html +2 -2
  35. package/test/web/tests.js +243 -202
@@ -0,0 +1,649 @@
1
+ /**
2
+ * Copyright © schukai GmbH and all contributing authors, {{copyRightYear}}. All rights reserved.
3
+ * Node module: @schukai/monster
4
+ *
5
+ * This source code is licensed under the GNU Affero General Public License version 3 (AGPLv3).
6
+ * The full text of the license can be found at: https://www.gnu.org/licenses/agpl-3.0.en.html
7
+ *
8
+ * For those who do not wish to adhere to the AGPLv3, a commercial license is available.
9
+ * Acquiring a commercial license allows you to use this software without complying with the AGPLv3 terms.
10
+ * For more information about purchasing a commercial license, please contact schukai GmbH.
11
+ */
12
+
13
+ import { instanceSymbol } from "../../constants.mjs";
14
+ import { ATTRIBUTE_PREFIX, ATTRIBUTE_ROLE } from "../../dom/constants.mjs";
15
+ import { CustomElement, getSlottedElements } from "../../dom/customelement.mjs";
16
+ import {
17
+ assembleMethodSymbol,
18
+ registerCustomElement,
19
+ } from "../../dom/customelement.mjs";
20
+ import { SliderStyleSheet } from "./stylesheet/slider.mjs";
21
+ import { fireCustomEvent } from "../../dom/events.mjs";
22
+
23
+ import { getWindow } from "../../dom/util.mjs";
24
+
25
+ export { Slider };
26
+
27
+ /**
28
+ * @private
29
+ * @type {symbol}
30
+ */
31
+ const sliderElementSymbol = Symbol("sliderElement");
32
+
33
+ /**
34
+ * @private
35
+ * @type {symbol}
36
+ */
37
+ const controlElementSymbol = Symbol("controlElement");
38
+
39
+ /**
40
+ * @private
41
+ * @type {symbol}
42
+ */
43
+ const prevElementSymbol = Symbol("prevElement");
44
+
45
+ /**
46
+ * @private
47
+ * @type {symbol}
48
+ */
49
+ const nextElementSymbol = Symbol("nextElement");
50
+
51
+ /**
52
+ * @private
53
+ * @type {symbol}
54
+ */
55
+ const configSymbol = Symbol("config");
56
+
57
+ /**
58
+ * @private
59
+ * @type {string}
60
+ */
61
+ const ATTRIBUTE_CLON_FROM = ATTRIBUTE_PREFIX + "clone-from";
62
+
63
+ /**
64
+ * A Slider
65
+ *
66
+ * @fragments /fragments/components/form/slider/
67
+ *
68
+ * @example /examples/components/form/slider-simple
69
+ *
70
+ * @since 3.74.0
71
+ * @copyright schukai GmbH
72
+ * @summary A beautiful Slider that can make your life easier and also looks good.
73
+ */
74
+ class Slider extends CustomElement {
75
+ /**
76
+ * This method is called by the `instanceof` operator.
77
+ * @returns {symbol}
78
+ */
79
+ static get [instanceSymbol]() {
80
+ return Symbol.for("@schukai/monster/components/layout/slider@@instance");
81
+ }
82
+
83
+ /**
84
+ *
85
+ * @return {Components.Layout.Slider
86
+ */
87
+ [assembleMethodSymbol]() {
88
+ super[assembleMethodSymbol]();
89
+
90
+ this[configSymbol] = {
91
+ currentIndex: 0,
92
+
93
+ isDragging: false,
94
+ draggingPos: 0,
95
+ startPos: 0,
96
+ autoPlayInterval: null,
97
+ };
98
+
99
+ initControlReferences.call(this);
100
+ initEventHandler.call(this);
101
+ initStructure.call(this);
102
+
103
+ return this;
104
+ }
105
+
106
+ /**
107
+ * To set the options via the html tag the attribute `data-monster-options` must be used.
108
+ * @see {@link https://monsterjs.org/en/doc/#configurate-a-monster-control}
109
+ *
110
+ * The individual configuration values can be found in the table.
111
+ *
112
+ * @property {Object} templates Template definitions
113
+ * @property {string} templates.main Main template
114
+ * @property {string} actions.click="throw Error" Callback when clicked
115
+ * @property {Object} features Features
116
+ * @property {boolean} features.carousel=true Carousel feature
117
+ * @property {boolean} features.autoPlay=true Auto play feature
118
+ * @property {boolean} features.thumbnails=true Thumbnails feature
119
+ * @property {boolean} features.drag=true Drag feature
120
+ * @property {Object} autoPlay Auto play configuration
121
+ * @property {number} autoPlay.delay=1500 Delay between slides
122
+ * @property {number} autoPlay.startDelay=1000 Start delay
123
+ * @property {string} autoPlay.direction="next" Direction of the auto play
124
+ * @property {boolean} autoPlay.mouseOverPause=true Pause on mouse over
125
+ * @property {boolean} autoPlay.touchPause=true Pause on touch
126
+ * @property {Object} classes CSS classes
127
+ * @property {boolean} disabled=false Disabled state
128
+ */
129
+ get defaults() {
130
+ return Object.assign({}, super.defaults, {
131
+ templates: {
132
+ main: getTemplate(),
133
+ },
134
+
135
+ classes: {},
136
+ disabled: false,
137
+
138
+ features: {
139
+ carousel: true,
140
+ autoPlay: true,
141
+ thumbnails: true,
142
+ drag: true,
143
+ },
144
+
145
+ autoPlay: {
146
+ delay: 1500,
147
+ startDelay: 1000,
148
+ direction: "next",
149
+ mouseOverPause: true,
150
+ touchPause: true,
151
+ },
152
+ });
153
+ }
154
+
155
+ /**
156
+ * @return {string}
157
+ */
158
+ static getTag() {
159
+ return "monster-slider";
160
+ }
161
+
162
+ /**
163
+ * @return {CSSStyleSheet[]}
164
+ */
165
+ static getCSSStyleSheet() {
166
+ return [SliderStyleSheet];
167
+ }
168
+
169
+ /**
170
+ * moves the slider to the given index
171
+ *
172
+ * @param index
173
+ * @returns {void}
174
+ */
175
+ moveTo(index) {
176
+ return moveTo.call(this, index);
177
+ }
178
+
179
+ /**
180
+ * shows the previous slide
181
+ *
182
+ * @return {void}
183
+ */
184
+ previous() {
185
+ return prev.call(this);
186
+ }
187
+
188
+ /**
189
+ * shows the next slide
190
+ *
191
+ * @return {void}
192
+ */
193
+ next() {
194
+ return next.call(this);
195
+ }
196
+
197
+ /**
198
+ * stops the auto play
199
+ *
200
+ * @return {void}
201
+ */
202
+ stopAutoPlay() {
203
+ if (this[configSymbol].autoPlayInterval) {
204
+ clearInterval(this[configSymbol].autoPlayInterval);
205
+ }
206
+ }
207
+
208
+ /**
209
+ * starts the auto play
210
+ *
211
+ * @return {void}
212
+ */
213
+ startAutoPlay() {
214
+ initAutoPlay.call(this);
215
+ }
216
+ }
217
+
218
+ /**
219
+ * @private
220
+ * @param name
221
+ */
222
+ function initNavigation(name) {
223
+ const element = this.shadowRoot.querySelector("." + name + "");
224
+ const elementHeight = element.offsetHeight;
225
+ element.style.top = `calc(50% - ${elementHeight / 2}px)`;
226
+ }
227
+
228
+ /**
229
+ * @private
230
+ */
231
+ function initStructure() {
232
+ initNavigation.call(this, "next");
233
+ initNavigation.call(this, "prev");
234
+
235
+ if (this.getOption("features.thumbnails")) {
236
+ initThumbnails.call(this);
237
+ }
238
+
239
+ if (this.getOption("features.carousel")) {
240
+ initCarousel.call(this);
241
+ }
242
+
243
+ if (this.getOption("features.autoPlay")) {
244
+ initAutoPlay.call(this);
245
+ }
246
+ }
247
+
248
+ /**
249
+ * @private
250
+ */
251
+ function initThumbnails() {
252
+ const self = this;
253
+ const thumbnails = this.shadowRoot.querySelector(
254
+ "[data-monster-role='thumbnails']",
255
+ );
256
+ const slides = Array.from(getSlottedElements.call(this, ":scope", null));
257
+
258
+ slides.forEach((slide, index) => {
259
+ const thumbnail = document.createElement("div");
260
+ thumbnail.classList.add("thumbnail");
261
+ thumbnail.addEventListener("click", () => {
262
+ if (self.getOption("features.carousel")) {
263
+ this.moveTo(index + 1);
264
+ } else {
265
+ this.moveTo(index);
266
+ }
267
+ });
268
+
269
+ thumbnails.appendChild(thumbnail);
270
+ });
271
+
272
+ this.addEventListener("monster-slider-moved", (e) => {
273
+ const index = e.detail.index;
274
+ const thumbnail = thumbnails.children[index];
275
+
276
+ if (!thumbnail) {
277
+ return;
278
+ }
279
+
280
+ Array.from(thumbnails.children).forEach((thumb) => {
281
+ thumb.classList.remove("current");
282
+ });
283
+
284
+ thumbnail.classList.add("current");
285
+ });
286
+ }
287
+
288
+ /**
289
+ * @private
290
+ */
291
+ function initAutoPlay() {
292
+ const self = this;
293
+ const autoPlay = this.getOption("autoPlay");
294
+ const delay = autoPlay.delay;
295
+ const startDelay = autoPlay.startDelay;
296
+ const direction = autoPlay.direction;
297
+
298
+ function start() {
299
+ if (self[configSymbol].autoPlayInterval) {
300
+ clearInterval(self[configSymbol].autoPlayInterval);
301
+ }
302
+
303
+ self[configSymbol].autoPlayInterval = setInterval(() => {
304
+ if (direction === "next") {
305
+ if (self.next() === -1) {
306
+ if (self.getOption("features.carousel")) {
307
+ clearInterval(self[configSymbol].autoPlayInterval);
308
+ }
309
+ }
310
+ } else {
311
+ if (self.previous() === -1) {
312
+ if (self.getOption("features.carousel")) {
313
+ clearInterval(self[configSymbol].autoPlayInterval);
314
+ }
315
+ }
316
+ }
317
+ }, delay);
318
+ }
319
+
320
+ setTimeout(() => {
321
+ start();
322
+ }, startDelay);
323
+
324
+ if (autoPlay.mouseOverPause) {
325
+ this.addEventListener("mouseover", () => {
326
+ clearInterval(this[configSymbol].autoPlayInterval);
327
+ });
328
+
329
+ this.addEventListener("mouseout", () => {
330
+ if (this[configSymbol].isDragging) {
331
+ return;
332
+ }
333
+ start();
334
+ });
335
+ }
336
+
337
+ if (autoPlay.touchPause) {
338
+ this.addEventListener("touchstart", () => {
339
+ clearInterval(this[configSymbol].autoPlayInterval);
340
+ });
341
+
342
+ this.addEventListener("touchend", () => {
343
+ start();
344
+ });
345
+ }
346
+ }
347
+
348
+ /**
349
+ * @private
350
+ */
351
+ function initCarousel() {
352
+ const { slides, totalSlides } = getSlidesAndTotal.call(this);
353
+ if (this.getOption("features.carousel") && totalSlides > 2) {
354
+ const firstElement = slides[0].cloneNode(true);
355
+ firstElement.setAttribute(ATTRIBUTE_CLON_FROM, 1);
356
+
357
+ const lastElement = slides[totalSlides - 1].cloneNode(true);
358
+ lastElement.setAttribute(ATTRIBUTE_CLON_FROM, totalSlides);
359
+ slides[totalSlides - 1].insertAdjacentElement("afterend", firstElement);
360
+
361
+ slides[0].insertAdjacentElement("beforebegin", lastElement);
362
+
363
+ moveTo.call(this, 1);
364
+ }
365
+ }
366
+
367
+ /**
368
+ * @private
369
+ * @returns {{slides: unknown[], totalSlides: number}}
370
+ */
371
+ function getSlidesAndTotal() {
372
+ const slides = Array.from(getSlottedElements.call(this, ":scope", null));
373
+ const totalSlides = slides.length;
374
+ return { slides, totalSlides };
375
+ }
376
+
377
+ /**
378
+ * @private
379
+ * @returns {number}
380
+ */
381
+ function next() {
382
+ const { slides, totalSlides } = getSlidesAndTotal.call(this);
383
+ const nextIndex = this[configSymbol].currentIndex + 1;
384
+
385
+ if (nextIndex >= totalSlides) {
386
+ return -1;
387
+ }
388
+
389
+ queueMicrotask(() => {
390
+ getWindow().requestAnimationFrame(() => {
391
+ getWindow().requestAnimationFrame(() => {
392
+ moveTo.call(this, nextIndex);
393
+ });
394
+ });
395
+ });
396
+
397
+ return 0;
398
+ }
399
+
400
+ /**
401
+ * @private
402
+ * @returns {number}
403
+ */
404
+ function prev() {
405
+ const prevIndex = this[configSymbol].currentIndex - 1;
406
+
407
+ if (prevIndex < 0) {
408
+ return -1;
409
+ }
410
+
411
+ moveTo.call(this, prevIndex);
412
+ return 0;
413
+ }
414
+
415
+ /**
416
+ * @private
417
+ * @param slides
418
+ * @param index
419
+ */
420
+ function setMoveProperties(slides, index) {
421
+ this[configSymbol].currentIndex = index;
422
+
423
+ slides.forEach((slide) => {
424
+ slide.classList.remove("current");
425
+ });
426
+
427
+ let offset = -(index * 100);
428
+ if (offset !== 0) {
429
+ offset += "%";
430
+ }
431
+
432
+ this[sliderElementSymbol].style.transform =
433
+ `translateX(calc(${offset} + ${this[configSymbol].draggingPos}px))`;
434
+ slides[index].classList.add("current");
435
+ }
436
+
437
+ /**
438
+ * @private
439
+ * @param index
440
+ * @fires monster-slider-moved
441
+ */
442
+ function moveTo(index) {
443
+ const { slides, totalSlides } = getSlidesAndTotal.call(this);
444
+
445
+ if (index < 0) {
446
+ index = totalSlides - 1;
447
+ } else if (index >= totalSlides) {
448
+ index = 0;
449
+ }
450
+
451
+ const slider = this[sliderElementSymbol];
452
+
453
+ setMoveProperties.call(this, slides, index);
454
+
455
+ const style = getComputedStyle(this[sliderElementSymbol]);
456
+ const duration = style.transitionDuration;
457
+ const durationMilis = parseFloat(duration) * 1000;
458
+
459
+ let slideIndex = index;
460
+ let eventFired = false;
461
+
462
+ if (this.getOption("features.carousel")) {
463
+ slideIndex = index - 1;
464
+
465
+ if (slides[index].hasAttribute(ATTRIBUTE_CLON_FROM)) {
466
+ const from = parseInt(slides[index].getAttribute(ATTRIBUTE_CLON_FROM));
467
+
468
+ getWindow().requestAnimationFrame(() => {
469
+ getWindow().requestAnimationFrame(() => {
470
+ setTimeout(() => {
471
+ slider.style.transitionProperty = "none";
472
+
473
+ setMoveProperties.call(this, slides, from);
474
+ slideIndex = from - 1;
475
+
476
+ getWindow().requestAnimationFrame(() => {
477
+ getWindow().requestAnimationFrame(() => {
478
+ slider.style.transitionProperty = "";
479
+
480
+ fireCustomEvent(this, "monster-slider-moved", {
481
+ index: slideIndex,
482
+ });
483
+
484
+ eventFired = true;
485
+ });
486
+ });
487
+ }, durationMilis);
488
+ });
489
+ });
490
+ }
491
+ }
492
+
493
+ if (!eventFired) {
494
+ fireCustomEvent(this, "monster-slider-moved", {
495
+ index: slideIndex,
496
+ });
497
+ }
498
+ }
499
+
500
+ /**
501
+ * @private
502
+ * @return {initEventHandler}
503
+ */
504
+ function initEventHandler() {
505
+ const self = this;
506
+
507
+ const nextElements = this[nextElementSymbol];
508
+
509
+ if (nextElements) {
510
+ nextElements.addEventListener("click", () => {
511
+ self.next();
512
+ });
513
+ }
514
+
515
+ const prevElements = this[prevElementSymbol];
516
+
517
+ if (prevElements) {
518
+ prevElements.addEventListener("click", () => {
519
+ self.previous();
520
+ });
521
+ }
522
+
523
+ if (this.getOption("features.drag")) {
524
+ this[sliderElementSymbol].addEventListener("mousedown", (e) =>
525
+ startDragging.call(this, e, "mouse"),
526
+ );
527
+
528
+ this[sliderElementSymbol].addEventListener("touchstart", (e) =>
529
+ startDragging.call(this, e, "touch"),
530
+ );
531
+ }
532
+
533
+ return this;
534
+ }
535
+
536
+ /**
537
+ * @private
538
+ * @param e
539
+ * @param type
540
+ */
541
+ function startDragging(e, type) {
542
+ this[configSymbol].isDragging = true;
543
+ this[configSymbol].startPos = getPositionX(e, type);
544
+ this[sliderElementSymbol].classList.add("grabbing");
545
+ this[sliderElementSymbol].style.transitionProperty = "none";
546
+
547
+ const callbackMousemove = (x) => {
548
+ dragging.call(this, x, type);
549
+ };
550
+
551
+ const callbackMouseUp = () => {
552
+ const endEvent = type === "mouse" ? "mouseup" : "touchend";
553
+ const moveEvent = type === "mouse" ? "mousemove" : "touchmove";
554
+
555
+ document.body.removeEventListener(endEvent, callbackMouseUp);
556
+ document.body.removeEventListener(moveEvent, callbackMousemove);
557
+
558
+ this[configSymbol].isDragging = false;
559
+ this[configSymbol].startPos = 0;
560
+ this[sliderElementSymbol].classList.remove("grabbing");
561
+ this[sliderElementSymbol].style.transitionProperty = "";
562
+
563
+ const lastPos = this[configSymbol].draggingPos;
564
+ const widthOfSlider = this[sliderElementSymbol].offsetWidth;
565
+ this[configSymbol].draggingPos = 0;
566
+
567
+ let newIndex = this[configSymbol].currentIndex;
568
+
569
+ const x = lastPos / widthOfSlider;
570
+ if (x > 0.5) {
571
+ newIndex--;
572
+ } else if (x < -0.5) {
573
+ newIndex++;
574
+ }
575
+
576
+ this.moveTo(newIndex);
577
+ };
578
+
579
+ document.body.addEventListener("mouseup", callbackMouseUp);
580
+ document.body.addEventListener("mousemove", callbackMousemove);
581
+ }
582
+
583
+ /**
584
+ * @private
585
+ * @param e
586
+ * @param type
587
+ * @returns {*|number|number}
588
+ */
589
+ function getPositionX(e, type) {
590
+ return type === "mouse" ? e.pageX : e.touches[0].clientX;
591
+ }
592
+
593
+ /**
594
+ * @private
595
+ * @param e
596
+ * @param type
597
+ */
598
+ function dragging(e, type) {
599
+ if (!this[configSymbol].isDragging) return;
600
+ this[configSymbol].draggingPos =
601
+ getPositionX(e, type) - this[configSymbol].startPos;
602
+ const { slides, totalSlides } = getSlidesAndTotal.call(this);
603
+ setMoveProperties.call(this, slides, this[configSymbol].currentIndex);
604
+ }
605
+
606
+ /**
607
+ * @private
608
+ * @return {void}
609
+ */
610
+ function initControlReferences() {
611
+ this[controlElementSymbol] = this.shadowRoot.querySelector(
612
+ `[${ATTRIBUTE_ROLE}="control"]`,
613
+ );
614
+
615
+ this[sliderElementSymbol] = this.shadowRoot.querySelector(
616
+ `[${ATTRIBUTE_ROLE}="slider"]`,
617
+ );
618
+
619
+ this[prevElementSymbol] = this.shadowRoot.querySelector(
620
+ `[${ATTRIBUTE_ROLE}="prev"]`,
621
+ );
622
+
623
+ this[nextElementSymbol] = this.shadowRoot.querySelector(
624
+ `[${ATTRIBUTE_ROLE}="next"]`,
625
+ );
626
+ }
627
+
628
+ /**
629
+ * @private
630
+ * @return {string}
631
+ */
632
+ function getTemplate() {
633
+ // language=HTML
634
+ return `
635
+ <div data-monster-role="control" part="control">
636
+ <div class="prev" data-monster-role="prev" part="prev">
637
+ <slot name="prev"></slot>
638
+ </div>
639
+ <div data-monster-role="slider">
640
+ <slot></slot>
641
+ </div>
642
+ <div data-monster-role="thumbnails"></div>
643
+ <div class="next" data-monster-role="next" part="next">
644
+ <slot name="next"></slot>
645
+ </div>
646
+ </div>`;
647
+ }
648
+
649
+ registerCustomElement(Slider);