@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.
@@ -37,11 +37,9 @@ export declare class AlarmElement extends ConditionalDisplayElement {
37
37
  private rowHeight;
38
38
  private touchStartY;
39
39
  private touchScrollAccumulator;
40
- private scrollTrack;
41
- private scrollThumb;
42
- private thumbDragStartY;
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 bindScrollbarEvents;
90
- private unbindScrollbarEvents;
91
- private getClientY;
92
- private readonly handleThumbDragStart;
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.thumbDragStartY = 0;
46
- this.thumbDragStartWindow = 0;
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
- this.touchScrollAccumulator += event.deltaY;
54
- const rowDelta = Math.trunc(this.touchScrollAccumulator / this.rowHeight);
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 * this.rowHeight;
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.rowHeight)
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 rowDelta = Math.trunc(this.touchScrollAccumulator / this.rowHeight);
82
+ const screenRowHeight = this.getScreenRowHeight();
83
+ const rowDelta = Math.trunc(this.touchScrollAccumulator / screenRowHeight);
73
84
  if (rowDelta !== 0) {
74
- this.touchScrollAccumulator -= rowDelta * this.rowHeight;
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
- if (!this.thumbDragging)
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
- this.handleTrackClick = (event) => {
129
- if (event.target !== this.scrollTrack || !this.scrollTrack)
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, Math.max(0, this.pageSize - this.maxVisibleRows));
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 (!this.scrollTrack || !this.scrollThumb)
457
- return;
458
- const maxWindowStart = Math.max(0, this.pageSize - this.maxVisibleRows);
459
- if (maxWindowStart <= 0) {
460
- this.scrollThumb.style.display = 'none';
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.innerHTML = this.getHeaderTextByColumn(col);
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.innerHTML = this.alarmList[row][col];
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: ${this.model.widths[col] - 30}px;
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.innerHTML = this.model.levelText[this.alarmList[row][col]];
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
- // 之前用"表头/表体分两个 table + flex 包裹容器 + overflow/transform 滚动"的结构,
613
- // 在 iOS WKWebView 上表体会明显放大错位,换了两种滚动实现方式(原生 overflow:auto、
614
- // JS transform 平移)都复现同样的问题,怀疑是这套多层 div + 双 table 结构本身的问题。
615
- // 这里改成和 renderPlainTable() 完全同构的单 table 结构(已验证在所有平台上都正常),
616
- // DOM 里始终只保留 maxVisibleRows 行数据,滚动时只增删 <tr> 内容,不做任何位移/滚动。
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: ${elementHeight - operationAreaReservedHeight}px;
626
- width: calc(100% - ${scrollbarWidth}px);`;
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
- wrapper.appendChild(table);
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.unbindScrollbarEvents();
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 = Math.max(0, this.pageSize - this.maxVisibleRows);
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
- bindScrollbarEvents() {
683
- if (!this.scrollTrack || !this.scrollThumb)
684
- return;
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
- unbindScrollbarEvents() {
690
- if (this.scrollThumb) {
691
- this.scrollThumb.removeEventListener('mousedown', this.handleThumbDragStart);
692
- this.scrollThumb.removeEventListener('touchstart', this.handleThumbDragStart);
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
- getClientY(event) {
705
- return 'touches' in event ? event.touches[0].clientY : event.clientY;
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.pageSize - this.maxVisibleRows;
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":21,"character":34},"members":{"__ctor__":[{"__symbolic":"constructor","parameters":[{"__symbolic":"error","message":"Could not resolve type","line":69,"character":25,"context":{"typeName":"HTMLElement"}},{"__symbolic":"reference","module":"@angular/core","name":"Injector","line":70,"character":18},{"__symbolic":"reference","module":"../../service","name":"PermissionChecker","line":71,"character":27},{"__symbolic":"reference","module":"../../communication","name":"VariableCommunicator","line":72,"character":30},{"__symbolic":"reference","module":"../../config","name":"VariableStore","line":73,"character":23},{"__symbolic":"reference","module":"../../config","name":"AlarmsStore","line":74,"character":38},{"__symbolic":"reference","name":"string"},{"__symbolic":"reference","module":"../../service","name":"SystemTextLibraryService","line":76,"character":52},{"__symbolic":"reference","module":"../../service","name":"LanguageService","line":77,"character":43},{"__symbolic":"reference","module":"../../gui/gui-context","name":"GuiContext","line":78,"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"}],"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"}],"bindScrollbarEvents":[{"__symbolic":"method"}],"unbindScrollbarEvents":[{"__symbolic":"method"}],"getClientY":[{"__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"}]}}}}]
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"}]}}}}]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "main": "bundles/fc-gui.umd.js",
3
- "version": "3.0.0-alpha.187",
3
+ "version": "3.0.0-alpha.189",
4
4
  "module": "public_api.js",
5
5
  "typings": "public_api.d.ts",
6
6
  "license": "UNLICENSED",