@flexem/fc-gui 3.0.0-alpha.187 → 3.0.0-alpha.189
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/bundles/@flexem/fc-gui.umd.js +244 -137
- package/bundles/@flexem/fc-gui.umd.js.map +1 -1
- package/bundles/@flexem/fc-gui.umd.min.js +1 -1
- package/bundles/@flexem/fc-gui.umd.min.js.map +1 -1
- package/elements/alarm/alarm-element.d.ts +8 -12
- package/elements/alarm/alarm-element.js +75 -136
- package/elements/alarm/alarm-element.metadata.json +1 -1
- package/elements/alarm/alarm-table-scrollbar.d.ts +36 -0
- package/elements/alarm/alarm-table-scrollbar.js +166 -0
- package/elements/alarm/alarm-table-scrollbar.metadata.json +1 -0
- package/package.json +1 -1
|
@@ -37,11 +37,9 @@ export declare class AlarmElement extends ConditionalDisplayElement {
|
|
|
37
37
|
private rowHeight;
|
|
38
38
|
private touchStartY;
|
|
39
39
|
private touchScrollAccumulator;
|
|
40
|
-
private
|
|
41
|
-
private
|
|
42
|
-
private
|
|
43
|
-
private thumbDragStartWindow;
|
|
44
|
-
private thumbDragging;
|
|
40
|
+
private scrollbar;
|
|
41
|
+
private scrollHovered;
|
|
42
|
+
private touchScrolling;
|
|
45
43
|
private isDisposed;
|
|
46
44
|
constructor(element: HTMLElement, injector: Injector, permissionChecker: PermissionChecker, variableCommunicator: VariableCommunicator, variableStore: VariableStore, alarmsStore: AlarmsStore, signalRAppId: string, systemTextLibraryService?: SystemTextLibraryService, languageService?: LanguageService, guiContext?: GuiContext);
|
|
47
45
|
dispose(): void;
|
|
@@ -72,6 +70,7 @@ export declare class AlarmElement extends ConditionalDisplayElement {
|
|
|
72
70
|
private updateTableContent;
|
|
73
71
|
private renderScrollWindow;
|
|
74
72
|
private updateScrollThumb;
|
|
73
|
+
private getMaxWindowStart;
|
|
75
74
|
private getIconScale;
|
|
76
75
|
private renderElement;
|
|
77
76
|
private buildHeaderRow;
|
|
@@ -86,13 +85,10 @@ export declare class AlarmElement extends ConditionalDisplayElement {
|
|
|
86
85
|
private readonly handleScrollTouchMove;
|
|
87
86
|
private readonly handleScrollMouseEnter;
|
|
88
87
|
private readonly handleScrollMouseLeave;
|
|
89
|
-
private
|
|
90
|
-
private
|
|
91
|
-
private
|
|
92
|
-
private
|
|
93
|
-
private readonly handleThumbDragMove;
|
|
94
|
-
private readonly handleThumbDragEnd;
|
|
95
|
-
private readonly handleTrackClick;
|
|
88
|
+
private readonly handleScrollTouchEnd;
|
|
89
|
+
private getCanvasScaleY;
|
|
90
|
+
private getScreenRowHeight;
|
|
91
|
+
private supportsMouseHover;
|
|
96
92
|
private startAutoScroll;
|
|
97
93
|
private scrollOneRow;
|
|
98
94
|
private autoNextPage;
|
|
@@ -4,6 +4,7 @@ import { ConditionalDisplayElement } from '../base/conditional-display-element';
|
|
|
4
4
|
import { GetAlarmsArgs } from '../../config';
|
|
5
5
|
import * as moment from 'moment';
|
|
6
6
|
import { LOGGER_SERVICE_TOKEN } from '../../logger';
|
|
7
|
+
import { AlarmTableScrollbar } from './alarm-table-scrollbar';
|
|
7
8
|
var AlaertElementStatus;
|
|
8
9
|
(function (AlaertElementStatus) {
|
|
9
10
|
AlaertElementStatus[AlaertElementStatus["Normal"] = 0] = "Normal";
|
|
@@ -42,97 +43,62 @@ export class AlarmElement extends ConditionalDisplayElement {
|
|
|
42
43
|
this.rowHeight = 0;
|
|
43
44
|
this.touchStartY = 0;
|
|
44
45
|
this.touchScrollAccumulator = 0;
|
|
45
|
-
this.
|
|
46
|
-
this.
|
|
47
|
-
this.thumbDragging = false;
|
|
46
|
+
this.scrollHovered = false;
|
|
47
|
+
this.touchScrolling = false;
|
|
48
48
|
this.isDisposed = false;
|
|
49
49
|
this.handleScrollWheel = (event) => {
|
|
50
|
-
if (!this.rowHeight)
|
|
50
|
+
if (!this.rowHeight || !this.getMaxWindowStart())
|
|
51
51
|
return;
|
|
52
52
|
event.preventDefault();
|
|
53
|
-
|
|
54
|
-
const
|
|
53
|
+
event.stopPropagation();
|
|
54
|
+
const screenRowHeight = this.getScreenRowHeight();
|
|
55
|
+
const delta = event.deltaMode === 1 ? event.deltaY * screenRowHeight
|
|
56
|
+
: event.deltaMode === 2 ? event.deltaY * screenRowHeight * this.maxVisibleRows : event.deltaY;
|
|
57
|
+
this.touchScrollAccumulator += delta;
|
|
58
|
+
const rowDelta = Math.trunc(this.touchScrollAccumulator / screenRowHeight);
|
|
55
59
|
if (rowDelta !== 0) {
|
|
56
|
-
this.touchScrollAccumulator -= rowDelta *
|
|
60
|
+
this.touchScrollAccumulator -= rowDelta * screenRowHeight;
|
|
57
61
|
this.moveScrollWindowBy(rowDelta);
|
|
58
62
|
}
|
|
59
63
|
};
|
|
60
64
|
this.handleScrollTouchStart = (event) => {
|
|
65
|
+
if (event.touches.length !== 1 || !this.getMaxWindowStart())
|
|
66
|
+
return;
|
|
67
|
+
this.touchScrolling = true;
|
|
68
|
+
clearInterval(this.scrollIntervalId);
|
|
69
|
+
event.stopPropagation();
|
|
61
70
|
this.touchStartY = event.touches[0].clientY;
|
|
62
71
|
this.touchScrollAccumulator = 0;
|
|
63
72
|
};
|
|
64
73
|
this.handleScrollTouchMove = (event) => {
|
|
65
|
-
if (!this.
|
|
74
|
+
if (!this.touchScrolling || event.touches.length !== 1)
|
|
66
75
|
return;
|
|
67
76
|
event.preventDefault();
|
|
77
|
+
event.stopPropagation();
|
|
68
78
|
const currentY = event.touches[0].clientY;
|
|
69
79
|
const deltaY = this.touchStartY - currentY;
|
|
70
80
|
this.touchStartY = currentY;
|
|
71
81
|
this.touchScrollAccumulator += deltaY;
|
|
72
|
-
const
|
|
82
|
+
const screenRowHeight = this.getScreenRowHeight();
|
|
83
|
+
const rowDelta = Math.trunc(this.touchScrollAccumulator / screenRowHeight);
|
|
73
84
|
if (rowDelta !== 0) {
|
|
74
|
-
this.touchScrollAccumulator -= rowDelta *
|
|
85
|
+
this.touchScrollAccumulator -= rowDelta * screenRowHeight;
|
|
75
86
|
this.moveScrollWindowBy(rowDelta);
|
|
76
87
|
}
|
|
77
88
|
};
|
|
78
89
|
this.handleScrollMouseEnter = () => {
|
|
90
|
+
if (!this.supportsMouseHover())
|
|
91
|
+
return;
|
|
92
|
+
this.scrollHovered = true;
|
|
79
93
|
clearInterval(this.scrollIntervalId);
|
|
80
94
|
};
|
|
81
95
|
this.handleScrollMouseLeave = () => {
|
|
82
|
-
|
|
83
|
-
this.startAutoScroll();
|
|
84
|
-
};
|
|
85
|
-
// 拖拽滑块时挂全局 mousemove/mouseup,允许拖出滚动条区域也能继续跟手,松开时才解绑
|
|
86
|
-
this.handleThumbDragStart = (event) => {
|
|
87
|
-
event.preventDefault();
|
|
88
|
-
event.stopPropagation();
|
|
89
|
-
this.thumbDragging = true;
|
|
90
|
-
clearInterval(this.scrollIntervalId);
|
|
91
|
-
this.thumbDragStartY = this.getClientY(event);
|
|
92
|
-
this.thumbDragStartWindow = this.scrollWindowStart;
|
|
93
|
-
document.addEventListener('mousemove', this.handleThumbDragMove);
|
|
94
|
-
document.addEventListener('mouseup', this.handleThumbDragEnd);
|
|
95
|
-
document.addEventListener('touchmove', this.handleThumbDragMove, { passive: false });
|
|
96
|
-
document.addEventListener('touchend', this.handleThumbDragEnd);
|
|
97
|
-
};
|
|
98
|
-
this.handleThumbDragMove = (event) => {
|
|
99
|
-
if (!this.thumbDragging || !this.scrollTrack)
|
|
100
|
-
return;
|
|
101
|
-
event.preventDefault();
|
|
102
|
-
const maxWindowStart = Math.max(0, this.pageSize - this.maxVisibleRows);
|
|
103
|
-
if (maxWindowStart <= 0)
|
|
104
|
-
return;
|
|
105
|
-
const trackHeight = this.scrollTrack.clientHeight;
|
|
106
|
-
const thumbHeightRatio = Math.min(1, this.maxVisibleRows / this.pageSize);
|
|
107
|
-
const thumbHeight = Math.max(20, trackHeight * thumbHeightRatio);
|
|
108
|
-
const maxThumbTop = trackHeight - thumbHeight;
|
|
109
|
-
if (maxThumbTop <= 0)
|
|
110
|
-
return;
|
|
111
|
-
const deltaY = this.getClientY(event) - this.thumbDragStartY;
|
|
112
|
-
const deltaWindow = Math.round((deltaY / maxThumbTop) * maxWindowStart);
|
|
113
|
-
const nextStart = Math.min(maxWindowStart, Math.max(0, this.thumbDragStartWindow + deltaWindow));
|
|
114
|
-
if (nextStart === this.scrollWindowStart)
|
|
115
|
-
return;
|
|
116
|
-
this.scrollWindowStart = nextStart;
|
|
117
|
-
this.renderScrollWindow(this.scrollWindowStart);
|
|
118
|
-
};
|
|
119
|
-
this.handleThumbDragEnd = () => {
|
|
120
|
-
this.thumbDragging = false;
|
|
121
|
-
document.removeEventListener('mousemove', this.handleThumbDragMove);
|
|
122
|
-
document.removeEventListener('mouseup', this.handleThumbDragEnd);
|
|
123
|
-
document.removeEventListener('touchmove', this.handleThumbDragMove);
|
|
124
|
-
document.removeEventListener('touchend', this.handleThumbDragEnd);
|
|
96
|
+
this.scrollHovered = false;
|
|
125
97
|
this.startAutoScroll();
|
|
126
98
|
};
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
return;
|
|
131
|
-
const trackRect = this.scrollTrack.getBoundingClientRect();
|
|
132
|
-
const clickTop = event.clientY - trackRect.top;
|
|
133
|
-
const thumbTop = parseFloat(this.scrollThumb.style.top) || 0;
|
|
134
|
-
const rowDelta = clickTop < thumbTop ? -this.maxVisibleRows : this.maxVisibleRows;
|
|
135
|
-
this.moveScrollWindowBy(rowDelta);
|
|
99
|
+
this.handleScrollTouchEnd = () => {
|
|
100
|
+
this.touchScrolling = false;
|
|
101
|
+
this.startAutoScroll();
|
|
136
102
|
};
|
|
137
103
|
this.rootElement.selectAll('*').remove();
|
|
138
104
|
this.setStatusAsLoading();
|
|
@@ -415,7 +381,7 @@ export class AlarmElement extends ConditionalDisplayElement {
|
|
|
415
381
|
if (useScroll) {
|
|
416
382
|
if (!this.scrollContent)
|
|
417
383
|
return;
|
|
418
|
-
this.scrollWindowStart = Math.min(this.scrollWindowStart,
|
|
384
|
+
this.scrollWindowStart = Math.min(this.scrollWindowStart, this.getMaxWindowStart());
|
|
419
385
|
this.renderScrollWindow(this.scrollWindowStart);
|
|
420
386
|
return;
|
|
421
387
|
}
|
|
@@ -450,24 +416,12 @@ export class AlarmElement extends ConditionalDisplayElement {
|
|
|
450
416
|
}
|
|
451
417
|
this.updateScrollThumb();
|
|
452
418
|
}
|
|
453
|
-
// 滑块高度按"可见行数/总行数"的比例算,位置按当前窗口起始行占可滚动范围的比例算,
|
|
454
|
-
// 只用 top/height 两个数值属性更新,不涉及 transform,不会触发合成层
|
|
455
419
|
updateScrollThumb() {
|
|
456
|
-
if (
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
return;
|
|
462
|
-
}
|
|
463
|
-
this.scrollThumb.style.display = 'block';
|
|
464
|
-
const trackHeight = this.scrollTrack.clientHeight;
|
|
465
|
-
const thumbHeightRatio = Math.min(1, this.maxVisibleRows / this.pageSize);
|
|
466
|
-
const thumbHeight = Math.max(20, trackHeight * thumbHeightRatio);
|
|
467
|
-
const maxThumbTop = trackHeight - thumbHeight;
|
|
468
|
-
const thumbTop = maxThumbTop * (this.scrollWindowStart / maxWindowStart);
|
|
469
|
-
this.scrollThumb.style.height = `${thumbHeight}px`;
|
|
470
|
-
this.scrollThumb.style.top = `${thumbTop}px`;
|
|
420
|
+
if (this.scrollbar)
|
|
421
|
+
this.scrollbar.update(this.scrollWindowStart, this.pageSize);
|
|
422
|
+
}
|
|
423
|
+
getMaxWindowStart() {
|
|
424
|
+
return Math.max(0, this.pageSize - this.maxVisibleRows);
|
|
471
425
|
}
|
|
472
426
|
getIconScale() {
|
|
473
427
|
const initSize = this.model.initSize;
|
|
@@ -516,7 +470,7 @@ export class AlarmElement extends ConditionalDisplayElement {
|
|
|
516
470
|
vertical-align: middle;`;
|
|
517
471
|
for (let col = 0; col < 4; col++) {
|
|
518
472
|
const th = document.createElement('th');
|
|
519
|
-
th.
|
|
473
|
+
th.textContent = this.getHeaderTextByColumn(col);
|
|
520
474
|
th.style.cssText = `width: ${this.model.widths[col] / this.model.size.width * 100}%;
|
|
521
475
|
border-style: solid;
|
|
522
476
|
border-color: ${this.model.generalSetting.splitLine};
|
|
@@ -543,7 +497,7 @@ export class AlarmElement extends ConditionalDisplayElement {
|
|
|
543
497
|
border-left-width: ${!col || this.model.generalSetting.isHiddenColumnSplitLine ? 0 : 1}px;`;
|
|
544
498
|
if (this.alarmList[row]) {
|
|
545
499
|
if (col !== 2) {
|
|
546
|
-
td.
|
|
500
|
+
td.textContent = this.alarmList[row][col];
|
|
547
501
|
td.style.cssText += `${this.model.generalSetting.isUnderline ? 'text-decoration: underline' : ''};`;
|
|
548
502
|
}
|
|
549
503
|
else {
|
|
@@ -562,14 +516,14 @@ export class AlarmElement extends ConditionalDisplayElement {
|
|
|
562
516
|
border-radius: 5px;
|
|
563
517
|
background: ${levelColors[this.alarmList[row][col]]};
|
|
564
518
|
color: #ffffff;
|
|
565
|
-
width:
|
|
519
|
+
width: calc(100% - 30px);
|
|
566
520
|
height: ${alarmHeight}px;
|
|
567
521
|
max-height: ${rowHeight}px;
|
|
568
522
|
display: flex;
|
|
569
523
|
justify-content: center;
|
|
570
524
|
align-items: center;
|
|
571
525
|
${this.model.generalSetting.isUnderline ? 'text-decoration: underline' : ''};`;
|
|
572
|
-
alarmLevel.
|
|
526
|
+
alarmLevel.textContent = this.model.levelText[this.alarmList[row][col]];
|
|
573
527
|
td.appendChild(alarmLevel);
|
|
574
528
|
}
|
|
575
529
|
}
|
|
@@ -609,40 +563,27 @@ export class AlarmElement extends ConditionalDisplayElement {
|
|
|
609
563
|
const dataRowHeight = this.model.heights[1] || this.model.heights[0];
|
|
610
564
|
this.rowHeight = dataRowHeight;
|
|
611
565
|
this.scrollWindowStart = 0;
|
|
612
|
-
//
|
|
613
|
-
//
|
|
614
|
-
//
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
// 自定义滚动条(轨道+滑块)只用 position + top/height 定位,不用 overflow/transform/
|
|
618
|
-
// will-change,这些属性都不会触发 WebKit 合成层提升,不会重新引入 iOS 缩放错位问题。
|
|
619
|
-
const scrollbarWidth = 6;
|
|
620
|
-
const wrapper = document.createElement('div');
|
|
621
|
-
wrapper.style.cssText = `position: relative; width: ${this.model.size.width}px;
|
|
622
|
-
height: ${elementHeight - operationAreaReservedHeight}px;`;
|
|
566
|
+
// WKWebView can paint positioned HTML descendants outside the SVG scale.
|
|
567
|
+
// Keep a single unpositioned table directly in foreignObject. The sibling
|
|
568
|
+
// SVG scrollbar overlays the table's right edge without an HTML scrolling layer.
|
|
569
|
+
const scrollbarWidth = Math.min(12, this.model.size.width * 0.04);
|
|
570
|
+
const tableHeight = elementHeight - operationAreaReservedHeight;
|
|
623
571
|
const table = document.createElement('table');
|
|
624
572
|
table.style.cssText = `border: 2px solid ${this.model.generalSetting.stroke};
|
|
625
|
-
height: ${
|
|
626
|
-
|
|
573
|
+
height: ${tableHeight}px; width: 100%; table-layout: fixed;
|
|
574
|
+
overflow-wrap: break-word; -webkit-text-size-adjust: none; text-size-adjust: none;`;
|
|
627
575
|
table.appendChild(this.buildHeaderRow());
|
|
628
|
-
|
|
629
|
-
const track = document.createElement('div');
|
|
630
|
-
track.style.cssText = `position: absolute; top: 0; right: 0; width: ${scrollbarWidth}px;
|
|
631
|
-
height: 100%; background: rgba(0,0,0,0.06); border-radius: ${scrollbarWidth / 2}px;`;
|
|
632
|
-
const thumb = document.createElement('div');
|
|
633
|
-
thumb.style.cssText = `position: absolute; left: 0; width: 100%;
|
|
634
|
-
background: rgba(0,0,0,0.35); border-radius: ${scrollbarWidth / 2}px; cursor: pointer;`;
|
|
635
|
-
track.appendChild(thumb);
|
|
636
|
-
wrapper.appendChild(track);
|
|
637
|
-
this.element.appendChild(wrapper);
|
|
576
|
+
this.element.appendChild(table);
|
|
638
577
|
this.scrollContent = table;
|
|
639
|
-
this.scrollTrack = track;
|
|
640
|
-
this.scrollThumb = thumb;
|
|
641
578
|
this.renderScrollWindow(0);
|
|
579
|
+
const canvasScaleY = this.getCanvasScaleY();
|
|
580
|
+
const renderedTableHeight = table.getBoundingClientRect().height / canvasScaleY || tableHeight;
|
|
581
|
+
const renderedHeaderHeight = table.rows[0].getBoundingClientRect().height / canvasScaleY || this.model.heights[0];
|
|
582
|
+
const scrollbarInset = 2;
|
|
583
|
+
this.scrollbar = new AlarmTableScrollbar(this.element.parentNode, this.model.size.width - scrollbarWidth, renderedHeaderHeight + scrollbarInset, scrollbarWidth, Math.max(0, renderedTableHeight - renderedHeaderHeight - scrollbarInset * 2), this.maxVisibleRows, start => this.moveScrollWindowBy(start - this.scrollWindowStart), () => clearInterval(this.scrollIntervalId), () => this.startAutoScroll());
|
|
642
584
|
this.updateScrollThumb();
|
|
643
585
|
this.hoverTarget = table;
|
|
644
586
|
this.bindScrollHoverEvents();
|
|
645
|
-
this.bindScrollbarEvents();
|
|
646
587
|
this.startAutoScroll();
|
|
647
588
|
}
|
|
648
589
|
bindScrollHoverEvents() {
|
|
@@ -654,10 +595,16 @@ export class AlarmElement extends ConditionalDisplayElement {
|
|
|
654
595
|
this.hoverTarget.addEventListener('wheel', this.handleScrollWheel, { passive: false });
|
|
655
596
|
this.hoverTarget.addEventListener('touchstart', this.handleScrollTouchStart, { passive: true });
|
|
656
597
|
this.hoverTarget.addEventListener('touchmove', this.handleScrollTouchMove, { passive: false });
|
|
598
|
+
this.hoverTarget.addEventListener('touchend', this.handleScrollTouchEnd);
|
|
599
|
+
this.hoverTarget.addEventListener('touchcancel', this.handleScrollTouchEnd);
|
|
657
600
|
}
|
|
658
601
|
}
|
|
659
602
|
unbindScrollHoverEvents() {
|
|
660
|
-
this.
|
|
603
|
+
if (this.scrollbar)
|
|
604
|
+
this.scrollbar.dispose();
|
|
605
|
+
this.scrollbar = null;
|
|
606
|
+
this.scrollHovered = false;
|
|
607
|
+
this.touchScrolling = false;
|
|
661
608
|
if (!this.hoverTarget)
|
|
662
609
|
return;
|
|
663
610
|
this.hoverTarget.removeEventListener('mouseenter', this.handleScrollMouseEnter);
|
|
@@ -665,6 +612,8 @@ export class AlarmElement extends ConditionalDisplayElement {
|
|
|
665
612
|
this.hoverTarget.removeEventListener('wheel', this.handleScrollWheel);
|
|
666
613
|
this.hoverTarget.removeEventListener('touchstart', this.handleScrollTouchStart);
|
|
667
614
|
this.hoverTarget.removeEventListener('touchmove', this.handleScrollTouchMove);
|
|
615
|
+
this.hoverTarget.removeEventListener('touchend', this.handleScrollTouchEnd);
|
|
616
|
+
this.hoverTarget.removeEventListener('touchcancel', this.handleScrollTouchEnd);
|
|
668
617
|
this.hoverTarget = null;
|
|
669
618
|
}
|
|
670
619
|
// 手动滚动只在当前可见窗口范围内移动(不触发翻页),到顶/到底停住,
|
|
@@ -672,41 +621,31 @@ export class AlarmElement extends ConditionalDisplayElement {
|
|
|
672
621
|
moveScrollWindowBy(rowDelta) {
|
|
673
622
|
if (!this.scrollContent)
|
|
674
623
|
return;
|
|
675
|
-
const maxWindowStart =
|
|
624
|
+
const maxWindowStart = this.getMaxWindowStart();
|
|
676
625
|
const nextStart = Math.min(maxWindowStart, Math.max(0, this.scrollWindowStart + rowDelta));
|
|
677
626
|
if (nextStart === this.scrollWindowStart)
|
|
678
627
|
return;
|
|
679
628
|
this.scrollWindowStart = nextStart;
|
|
680
629
|
this.renderScrollWindow(this.scrollWindowStart);
|
|
681
630
|
}
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
this.scrollThumb.addEventListener('mousedown', this.handleThumbDragStart);
|
|
686
|
-
this.scrollThumb.addEventListener('touchstart', this.handleThumbDragStart, { passive: true });
|
|
687
|
-
this.scrollTrack.addEventListener('mousedown', this.handleTrackClick);
|
|
631
|
+
getCanvasScaleY() {
|
|
632
|
+
const matrix = this.element.getScreenCTM();
|
|
633
|
+
return matrix ? Math.sqrt(matrix.c * matrix.c + matrix.d * matrix.d) || 1 : 1;
|
|
688
634
|
}
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
}
|
|
694
|
-
if (this.scrollTrack) {
|
|
695
|
-
this.scrollTrack.removeEventListener('mousedown', this.handleTrackClick);
|
|
696
|
-
}
|
|
697
|
-
document.removeEventListener('mousemove', this.handleThumbDragMove);
|
|
698
|
-
document.removeEventListener('mouseup', this.handleThumbDragEnd);
|
|
699
|
-
document.removeEventListener('touchmove', this.handleThumbDragMove);
|
|
700
|
-
document.removeEventListener('touchend', this.handleThumbDragEnd);
|
|
701
|
-
this.scrollTrack = null;
|
|
702
|
-
this.scrollThumb = null;
|
|
635
|
+
getScreenRowHeight() {
|
|
636
|
+
var _a;
|
|
637
|
+
const row = (_a = this.scrollContent) === null || _a === void 0 ? void 0 : _a.querySelectorAll('tr')[1];
|
|
638
|
+
return (row === null || row === void 0 ? void 0 : row.getBoundingClientRect().height) || this.rowHeight * this.getCanvasScaleY();
|
|
703
639
|
}
|
|
704
|
-
|
|
705
|
-
|
|
640
|
+
supportsMouseHover() {
|
|
641
|
+
var _a, _b;
|
|
642
|
+
const view = (_b = (_a = this.element) === null || _a === void 0 ? void 0 : _a.ownerDocument) === null || _b === void 0 ? void 0 : _b.defaultView;
|
|
643
|
+
return !!view && (!view.matchMedia || view.matchMedia('(hover: hover) and (pointer: fine)').matches);
|
|
706
644
|
}
|
|
707
645
|
startAutoScroll() {
|
|
646
|
+
var _a;
|
|
708
647
|
clearInterval(this.scrollIntervalId);
|
|
709
|
-
if (!this.model.generalSetting.autoScroll)
|
|
648
|
+
if (this.isDisposed || this.scrollHovered || this.touchScrolling || ((_a = this.scrollbar) === null || _a === void 0 ? void 0 : _a.isInteracting) || !this.model.generalSetting.autoScroll)
|
|
710
649
|
return;
|
|
711
650
|
const totalPages = Math.max(1, Math.ceil(this.totalCount / this.pageSize));
|
|
712
651
|
if (totalPages <= 1 && !this.scrollContent)
|
|
@@ -719,7 +658,7 @@ export class AlarmElement extends ConditionalDisplayElement {
|
|
|
719
658
|
this.autoNextPage();
|
|
720
659
|
return;
|
|
721
660
|
}
|
|
722
|
-
const maxWindowStart = this.
|
|
661
|
+
const maxWindowStart = this.getMaxWindowStart();
|
|
723
662
|
if (maxWindowStart <= 0 || this.scrollWindowStart >= maxWindowStart) {
|
|
724
663
|
this.autoNextPage();
|
|
725
664
|
return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
[{"__symbolic":"module","version":4,"metadata":{"AlarmElement":{"__symbolic":"class","extends":{"__symbolic":"reference","module":"../base/conditional-display-element","name":"ConditionalDisplayElement","line":
|
|
1
|
+
[{"__symbolic":"module","version":4,"metadata":{"AlarmElement":{"__symbolic":"class","extends":{"__symbolic":"reference","module":"../base/conditional-display-element","name":"ConditionalDisplayElement","line":22,"character":34},"members":{"__ctor__":[{"__symbolic":"constructor","parameters":[{"__symbolic":"error","message":"Could not resolve type","line":68,"character":25,"context":{"typeName":"HTMLElement"}},{"__symbolic":"reference","module":"@angular/core","name":"Injector","line":69,"character":18},{"__symbolic":"reference","module":"../../service","name":"PermissionChecker","line":70,"character":27},{"__symbolic":"reference","module":"../../communication","name":"VariableCommunicator","line":71,"character":30},{"__symbolic":"reference","module":"../../config","name":"VariableStore","line":72,"character":23},{"__symbolic":"reference","module":"../../config","name":"AlarmsStore","line":73,"character":38},{"__symbolic":"reference","name":"string"},{"__symbolic":"reference","module":"../../service","name":"SystemTextLibraryService","line":75,"character":52},{"__symbolic":"reference","module":"../../service","name":"LanguageService","line":76,"character":43},{"__symbolic":"reference","module":"../../gui/gui-context","name":"GuiContext","line":77,"character":38}]}],"dispose":[{"__symbolic":"method"}],"subscribeLanguageChange":[{"__symbolic":"method"}],"updateLanguageTexts":[{"__symbolic":"method"}],"getCurrentCulture":[{"__symbolic":"method"}],"getHeaderTextByColumn":[{"__symbolic":"method"}],"getValidTimePeriods":[{"__symbolic":"method"}],"getTimePeriodText":[{"__symbolic":"method"}],"getAlarmData":[{"__symbolic":"method"}],"refreshCurrentPageData":[{"__symbolic":"method"}],"updateTableContent":[{"__symbolic":"method"}],"renderScrollWindow":[{"__symbolic":"method"}],"updateScrollThumb":[{"__symbolic":"method"}],"getMaxWindowStart":[{"__symbolic":"method"}],"getIconScale":[{"__symbolic":"method"}],"renderElement":[{"__symbolic":"method"}],"buildHeaderRow":[{"__symbolic":"method"}],"buildDataRow":[{"__symbolic":"method"}],"renderPlainTable":[{"__symbolic":"method"}],"renderScrollableTable":[{"__symbolic":"method"}],"bindScrollHoverEvents":[{"__symbolic":"method"}],"unbindScrollHoverEvents":[{"__symbolic":"method"}],"moveScrollWindowBy":[{"__symbolic":"method"}],"getCanvasScaleY":[{"__symbolic":"method"}],"getScreenRowHeight":[{"__symbolic":"method"}],"supportsMouseHover":[{"__symbolic":"method"}],"startAutoScroll":[{"__symbolic":"method"}],"scrollOneRow":[{"__symbolic":"method"}],"autoNextPage":[{"__symbolic":"method"}],"renderOperationArea":[{"__symbolic":"method"}],"updateTimeRange":[{"__symbolic":"method"}],"updateQueryTimeRange":[{"__symbolic":"method"}],"loadFirstPage":[{"__symbolic":"method"}],"loadNextPage":[{"__symbolic":"method"}],"loadPreviousPage":[{"__symbolic":"method"}],"loadLastPage":[{"__symbolic":"method"}],"setStatusAsNormal":[{"__symbolic":"method"}],"setStatusAsLoading":[{"__symbolic":"method"}],"renderStatus":[{"__symbolic":"method"}],"clearStatus":[{"__symbolic":"method"}]}}}}]
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** SVG scrollbar overlays the table bounds but stays outside foreignObject for WKWebView scaling. */
|
|
2
|
+
export declare class AlarmTableScrollbar {
|
|
3
|
+
private readonly y;
|
|
4
|
+
private readonly height;
|
|
5
|
+
private readonly visibleRows;
|
|
6
|
+
private readonly onChange;
|
|
7
|
+
private readonly onPause;
|
|
8
|
+
private readonly onResume;
|
|
9
|
+
private readonly group;
|
|
10
|
+
private readonly track;
|
|
11
|
+
private readonly thumb;
|
|
12
|
+
private readonly ownerDocument;
|
|
13
|
+
private maxStart;
|
|
14
|
+
private start;
|
|
15
|
+
private thumbHeight;
|
|
16
|
+
private thumbTop;
|
|
17
|
+
private dragging;
|
|
18
|
+
private hovered;
|
|
19
|
+
private dragStartY;
|
|
20
|
+
private dragStartRow;
|
|
21
|
+
constructor(parent: SVGElement, x: number, y: number, width: number, height: number, visibleRows: number, onChange: (start: number) => void, onPause: () => void, onResume: () => void);
|
|
22
|
+
get isInteracting(): boolean;
|
|
23
|
+
update(start: number, totalRows: number): void;
|
|
24
|
+
dispose(): void;
|
|
25
|
+
private createElement;
|
|
26
|
+
private setRect;
|
|
27
|
+
private getLocalY;
|
|
28
|
+
private readonly handleStart;
|
|
29
|
+
private readonly handleMove;
|
|
30
|
+
private readonly handleEnd;
|
|
31
|
+
private readonly handleLeave;
|
|
32
|
+
private readonly handleEnter;
|
|
33
|
+
private readonly handleKeyDown;
|
|
34
|
+
private unbindDrag;
|
|
35
|
+
private supportsMouseHover;
|
|
36
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/** SVG scrollbar overlays the table bounds but stays outside foreignObject for WKWebView scaling. */
|
|
2
|
+
export class AlarmTableScrollbar {
|
|
3
|
+
constructor(parent, x, y, width, height, visibleRows, onChange, onPause, onResume) {
|
|
4
|
+
this.y = y;
|
|
5
|
+
this.height = height;
|
|
6
|
+
this.visibleRows = visibleRows;
|
|
7
|
+
this.onChange = onChange;
|
|
8
|
+
this.onPause = onPause;
|
|
9
|
+
this.onResume = onResume;
|
|
10
|
+
this.maxStart = 0;
|
|
11
|
+
this.start = 0;
|
|
12
|
+
this.thumbHeight = 0;
|
|
13
|
+
this.thumbTop = 0;
|
|
14
|
+
this.dragging = false;
|
|
15
|
+
this.hovered = false;
|
|
16
|
+
this.dragStartY = 0;
|
|
17
|
+
this.dragStartRow = 0;
|
|
18
|
+
this.handleStart = (event) => {
|
|
19
|
+
if (!this.maxStart || ('button' in event && event.button !== 0) || ('touches' in event && event.touches.length !== 1))
|
|
20
|
+
return;
|
|
21
|
+
event.preventDefault();
|
|
22
|
+
event.stopPropagation();
|
|
23
|
+
this.onPause();
|
|
24
|
+
const localY = this.getLocalY(event);
|
|
25
|
+
// The full gutter is clickable, including the transparent sides of the thumb.
|
|
26
|
+
if (localY < this.y + this.thumbTop || localY > this.y + this.thumbTop + this.thumbHeight) {
|
|
27
|
+
this.onChange(this.start + (localY < this.y + this.thumbTop ? -this.visibleRows : this.visibleRows));
|
|
28
|
+
this.onResume();
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
this.dragging = true;
|
|
32
|
+
this.dragStartY = localY;
|
|
33
|
+
this.dragStartRow = this.start;
|
|
34
|
+
this.thumb.setAttribute('fill', '#8f99aa');
|
|
35
|
+
this.thumb.style.cursor = 'grabbing';
|
|
36
|
+
this.ownerDocument.addEventListener('mousemove', this.handleMove);
|
|
37
|
+
this.ownerDocument.addEventListener('mouseup', this.handleEnd);
|
|
38
|
+
this.ownerDocument.addEventListener('touchmove', this.handleMove, { passive: false });
|
|
39
|
+
this.ownerDocument.addEventListener('touchend', this.handleEnd);
|
|
40
|
+
this.ownerDocument.addEventListener('touchcancel', this.handleEnd);
|
|
41
|
+
this.ownerDocument.defaultView.addEventListener('blur', this.handleEnd);
|
|
42
|
+
};
|
|
43
|
+
this.handleMove = (event) => {
|
|
44
|
+
if (!this.dragging || ('touches' in event && event.touches.length !== 1))
|
|
45
|
+
return;
|
|
46
|
+
event.preventDefault();
|
|
47
|
+
event.stopPropagation();
|
|
48
|
+
const travel = this.height - this.thumbHeight;
|
|
49
|
+
if (travel <= 0)
|
|
50
|
+
return;
|
|
51
|
+
const rowDelta = Math.round((this.getLocalY(event) - this.dragStartY) / travel * this.maxStart);
|
|
52
|
+
this.onChange(this.dragStartRow + rowDelta);
|
|
53
|
+
};
|
|
54
|
+
this.handleEnd = () => {
|
|
55
|
+
if (!this.dragging)
|
|
56
|
+
return;
|
|
57
|
+
this.dragging = false;
|
|
58
|
+
this.unbindDrag();
|
|
59
|
+
this.thumb.setAttribute('fill', '#b6bdc9');
|
|
60
|
+
this.thumb.style.cursor = 'grab';
|
|
61
|
+
this.onResume();
|
|
62
|
+
};
|
|
63
|
+
this.handleLeave = () => {
|
|
64
|
+
this.hovered = false;
|
|
65
|
+
this.onResume();
|
|
66
|
+
};
|
|
67
|
+
this.handleEnter = () => {
|
|
68
|
+
if (!this.supportsMouseHover())
|
|
69
|
+
return;
|
|
70
|
+
this.hovered = true;
|
|
71
|
+
this.onPause();
|
|
72
|
+
};
|
|
73
|
+
this.handleKeyDown = (event) => {
|
|
74
|
+
const targets = {
|
|
75
|
+
ArrowUp: this.start - 1,
|
|
76
|
+
ArrowDown: this.start + 1,
|
|
77
|
+
PageUp: this.start - this.visibleRows,
|
|
78
|
+
PageDown: this.start + this.visibleRows,
|
|
79
|
+
Home: 0,
|
|
80
|
+
End: this.maxStart
|
|
81
|
+
};
|
|
82
|
+
if (!(event.key in targets))
|
|
83
|
+
return;
|
|
84
|
+
event.preventDefault();
|
|
85
|
+
event.stopPropagation();
|
|
86
|
+
this.onChange(targets[event.key]);
|
|
87
|
+
};
|
|
88
|
+
this.ownerDocument = parent.ownerDocument;
|
|
89
|
+
this.group = this.createElement('g');
|
|
90
|
+
this.group.setAttribute('role', 'scrollbar');
|
|
91
|
+
this.group.setAttribute('aria-orientation', 'vertical');
|
|
92
|
+
this.group.setAttribute('aria-valuemin', '0');
|
|
93
|
+
this.group.setAttribute('tabindex', '0');
|
|
94
|
+
this.group.style.cursor = 'default';
|
|
95
|
+
this.track = this.createElement('rect');
|
|
96
|
+
this.setRect(this.track, x, y, width, height);
|
|
97
|
+
this.track.setAttribute('fill', '#eef0f4');
|
|
98
|
+
this.track.setAttribute('rx', '0');
|
|
99
|
+
this.thumb = this.createElement('rect');
|
|
100
|
+
this.setRect(this.thumb, x + width / 4, y, width / 2, height);
|
|
101
|
+
this.thumb.setAttribute('fill', '#b6bdc9');
|
|
102
|
+
this.thumb.setAttribute('rx', String(width / 4));
|
|
103
|
+
this.thumb.style.cursor = 'grab';
|
|
104
|
+
this.group.appendChild(this.track);
|
|
105
|
+
this.group.appendChild(this.thumb);
|
|
106
|
+
parent.appendChild(this.group);
|
|
107
|
+
this.group.addEventListener('mousedown', this.handleStart);
|
|
108
|
+
this.group.addEventListener('touchstart', this.handleStart, { passive: false });
|
|
109
|
+
this.group.addEventListener('keydown', this.handleKeyDown);
|
|
110
|
+
this.group.addEventListener('mouseenter', this.handleEnter);
|
|
111
|
+
this.group.addEventListener('mouseleave', this.handleLeave);
|
|
112
|
+
}
|
|
113
|
+
get isInteracting() {
|
|
114
|
+
return this.dragging || this.hovered;
|
|
115
|
+
}
|
|
116
|
+
update(start, totalRows) {
|
|
117
|
+
this.maxStart = Math.max(0, totalRows - this.visibleRows);
|
|
118
|
+
this.start = Math.min(this.maxStart, Math.max(0, start));
|
|
119
|
+
this.group.setAttribute('display', this.maxStart ? 'inline' : 'none');
|
|
120
|
+
this.group.setAttribute('aria-valuemax', String(this.maxStart));
|
|
121
|
+
this.group.setAttribute('aria-valuenow', String(this.start));
|
|
122
|
+
this.thumbHeight = Math.min(this.height, Math.max(20, this.height * this.visibleRows / Math.max(1, totalRows)));
|
|
123
|
+
this.thumbTop = this.maxStart ? (this.height - this.thumbHeight) * this.start / this.maxStart : 0;
|
|
124
|
+
this.thumb.setAttribute('height', String(this.thumbHeight));
|
|
125
|
+
this.thumb.setAttribute('y', String(this.y + this.thumbTop));
|
|
126
|
+
}
|
|
127
|
+
dispose() {
|
|
128
|
+
this.dragging = false;
|
|
129
|
+
this.unbindDrag();
|
|
130
|
+
this.group.removeEventListener('mousedown', this.handleStart);
|
|
131
|
+
this.group.removeEventListener('touchstart', this.handleStart);
|
|
132
|
+
this.group.removeEventListener('keydown', this.handleKeyDown);
|
|
133
|
+
this.group.removeEventListener('mouseenter', this.handleEnter);
|
|
134
|
+
this.group.removeEventListener('mouseleave', this.handleLeave);
|
|
135
|
+
this.group.remove();
|
|
136
|
+
}
|
|
137
|
+
createElement(name) {
|
|
138
|
+
return this.ownerDocument.createElementNS('http://www.w3.org/2000/svg', name);
|
|
139
|
+
}
|
|
140
|
+
setRect(rect, x, y, width, height) {
|
|
141
|
+
rect.setAttribute('x', String(x));
|
|
142
|
+
rect.setAttribute('y', String(y));
|
|
143
|
+
rect.setAttribute('width', String(width));
|
|
144
|
+
rect.setAttribute('height', String(height));
|
|
145
|
+
}
|
|
146
|
+
getLocalY(event) {
|
|
147
|
+
const pointer = 'touches' in event ? event.touches[0] : event;
|
|
148
|
+
const point = this.group.ownerSVGElement.createSVGPoint();
|
|
149
|
+
point.x = pointer.clientX;
|
|
150
|
+
point.y = pointer.clientY;
|
|
151
|
+
const matrix = this.group.getScreenCTM();
|
|
152
|
+
return matrix ? point.matrixTransform(matrix.inverse()).y : point.y;
|
|
153
|
+
}
|
|
154
|
+
unbindDrag() {
|
|
155
|
+
this.ownerDocument.removeEventListener('mousemove', this.handleMove);
|
|
156
|
+
this.ownerDocument.removeEventListener('mouseup', this.handleEnd);
|
|
157
|
+
this.ownerDocument.removeEventListener('touchmove', this.handleMove);
|
|
158
|
+
this.ownerDocument.removeEventListener('touchend', this.handleEnd);
|
|
159
|
+
this.ownerDocument.removeEventListener('touchcancel', this.handleEnd);
|
|
160
|
+
this.ownerDocument.defaultView.removeEventListener('blur', this.handleEnd);
|
|
161
|
+
}
|
|
162
|
+
supportsMouseHover() {
|
|
163
|
+
const view = this.ownerDocument.defaultView;
|
|
164
|
+
return !!view && (!view.matchMedia || view.matchMedia('(hover: hover) and (pointer: fine)').matches);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
[{"__symbolic":"module","version":4,"metadata":{"AlarmTableScrollbar":{"__symbolic":"class","members":{"__ctor__":[{"__symbolic":"constructor","parameters":[{"__symbolic":"error","message":"Could not resolve type","line":16,"character":16,"context":{"typeName":"SVGElement"}},{"__symbolic":"reference","name":"number"},{"__symbolic":"reference","name":"number"},{"__symbolic":"reference","name":"number"},{"__symbolic":"reference","name":"number"},{"__symbolic":"reference","name":"number"},{"__symbolic":"error","message":"Expression form not supported","line":22,"character":35},{"__symbolic":"error","message":"Expression form not supported","line":23,"character":34},{"__symbolic":"error","message":"Expression form not supported","line":24,"character":35}]}],"update":[{"__symbolic":"method"}],"dispose":[{"__symbolic":"method"}],"createElement":[{"__symbolic":"method"}],"setRect":[{"__symbolic":"method"}],"getLocalY":[{"__symbolic":"method"}],"unbindDrag":[{"__symbolic":"method"}],"supportsMouseHover":[{"__symbolic":"method"}]}}}}]
|