@flexem/fc-gui 3.0.0-alpha.187 → 3.0.0-alpha.188

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,9 @@ 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;
96
91
  private startAutoScroll;
97
92
  private scrollOneRow;
98
93
  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,60 @@ 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
+ this.scrollHovered = true;
79
91
  clearInterval(this.scrollIntervalId);
80
92
  };
81
93
  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);
94
+ this.scrollHovered = false;
125
95
  this.startAutoScroll();
126
96
  };
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);
97
+ this.handleScrollTouchEnd = () => {
98
+ this.touchScrolling = false;
99
+ this.startAutoScroll();
136
100
  };
137
101
  this.rootElement.selectAll('*').remove();
138
102
  this.setStatusAsLoading();
@@ -415,7 +379,7 @@ export class AlarmElement extends ConditionalDisplayElement {
415
379
  if (useScroll) {
416
380
  if (!this.scrollContent)
417
381
  return;
418
- this.scrollWindowStart = Math.min(this.scrollWindowStart, Math.max(0, this.pageSize - this.maxVisibleRows));
382
+ this.scrollWindowStart = Math.min(this.scrollWindowStart, this.getMaxWindowStart());
419
383
  this.renderScrollWindow(this.scrollWindowStart);
420
384
  return;
421
385
  }
@@ -450,24 +414,12 @@ export class AlarmElement extends ConditionalDisplayElement {
450
414
  }
451
415
  this.updateScrollThumb();
452
416
  }
453
- // 滑块高度按"可见行数/总行数"的比例算,位置按当前窗口起始行占可滚动范围的比例算,
454
- // 只用 top/height 两个数值属性更新,不涉及 transform,不会触发合成层
455
417
  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`;
418
+ if (this.scrollbar)
419
+ this.scrollbar.update(this.scrollWindowStart, this.alarmList.length);
420
+ }
421
+ getMaxWindowStart() {
422
+ return Math.max(0, this.alarmList.length - this.maxVisibleRows);
471
423
  }
472
424
  getIconScale() {
473
425
  const initSize = this.model.initSize;
@@ -516,7 +468,7 @@ export class AlarmElement extends ConditionalDisplayElement {
516
468
  vertical-align: middle;`;
517
469
  for (let col = 0; col < 4; col++) {
518
470
  const th = document.createElement('th');
519
- th.innerHTML = this.getHeaderTextByColumn(col);
471
+ th.textContent = this.getHeaderTextByColumn(col);
520
472
  th.style.cssText = `width: ${this.model.widths[col] / this.model.size.width * 100}%;
521
473
  border-style: solid;
522
474
  border-color: ${this.model.generalSetting.splitLine};
@@ -543,7 +495,7 @@ export class AlarmElement extends ConditionalDisplayElement {
543
495
  border-left-width: ${!col || this.model.generalSetting.isHiddenColumnSplitLine ? 0 : 1}px;`;
544
496
  if (this.alarmList[row]) {
545
497
  if (col !== 2) {
546
- td.innerHTML = this.alarmList[row][col];
498
+ td.textContent = this.alarmList[row][col];
547
499
  td.style.cssText += `${this.model.generalSetting.isUnderline ? 'text-decoration: underline' : ''};`;
548
500
  }
549
501
  else {
@@ -562,14 +514,14 @@ export class AlarmElement extends ConditionalDisplayElement {
562
514
  border-radius: 5px;
563
515
  background: ${levelColors[this.alarmList[row][col]]};
564
516
  color: #ffffff;
565
- width: ${this.model.widths[col] - 30}px;
517
+ width: calc(100% - 30px);
566
518
  height: ${alarmHeight}px;
567
519
  max-height: ${rowHeight}px;
568
520
  display: flex;
569
521
  justify-content: center;
570
522
  align-items: center;
571
523
  ${this.model.generalSetting.isUnderline ? 'text-decoration: underline' : ''};`;
572
- alarmLevel.innerHTML = this.model.levelText[this.alarmList[row][col]];
524
+ alarmLevel.textContent = this.model.levelText[this.alarmList[row][col]];
573
525
  td.appendChild(alarmLevel);
574
526
  }
575
527
  }
@@ -609,40 +561,25 @@ export class AlarmElement extends ConditionalDisplayElement {
609
561
  const dataRowHeight = this.model.heights[1] || this.model.heights[0];
610
562
  this.rowHeight = dataRowHeight;
611
563
  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;`;
564
+ // WKWebView can paint positioned HTML descendants outside the SVG scale.
565
+ // Keep a single unpositioned table directly in foreignObject; the scrollbar
566
+ // is a sibling SVG group, never an HTML overlay or native scrolling layer.
567
+ const scrollbarWidth = Math.min(12, this.model.size.width * 0.04);
568
+ const tableHeight = elementHeight - operationAreaReservedHeight;
623
569
  const table = document.createElement('table');
624
570
  table.style.cssText = `border: 2px solid ${this.model.generalSetting.stroke};
625
- height: ${elementHeight - operationAreaReservedHeight}px;
626
- width: calc(100% - ${scrollbarWidth}px);`;
571
+ height: ${tableHeight}px; width: 100%; table-layout: fixed;
572
+ overflow-wrap: break-word; -webkit-text-size-adjust: none; text-size-adjust: none;`;
627
573
  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);
574
+ this.element.setAttribute('width', String(this.model.size.width - scrollbarWidth));
575
+ this.element.appendChild(table);
638
576
  this.scrollContent = table;
639
- this.scrollTrack = track;
640
- this.scrollThumb = thumb;
641
577
  this.renderScrollWindow(0);
578
+ const headerHeight = Math.min(tableHeight, table.rows[0].getBoundingClientRect().height / this.getCanvasScaleY());
579
+ this.scrollbar = new AlarmTableScrollbar(this.element.parentNode, this.model.size.width - scrollbarWidth, headerHeight, scrollbarWidth, Math.max(0, tableHeight - headerHeight), this.maxVisibleRows, start => this.moveScrollWindowBy(start - this.scrollWindowStart), () => clearInterval(this.scrollIntervalId), () => this.startAutoScroll());
642
580
  this.updateScrollThumb();
643
581
  this.hoverTarget = table;
644
582
  this.bindScrollHoverEvents();
645
- this.bindScrollbarEvents();
646
583
  this.startAutoScroll();
647
584
  }
648
585
  bindScrollHoverEvents() {
@@ -654,10 +591,16 @@ export class AlarmElement extends ConditionalDisplayElement {
654
591
  this.hoverTarget.addEventListener('wheel', this.handleScrollWheel, { passive: false });
655
592
  this.hoverTarget.addEventListener('touchstart', this.handleScrollTouchStart, { passive: true });
656
593
  this.hoverTarget.addEventListener('touchmove', this.handleScrollTouchMove, { passive: false });
594
+ this.hoverTarget.addEventListener('touchend', this.handleScrollTouchEnd);
595
+ this.hoverTarget.addEventListener('touchcancel', this.handleScrollTouchEnd);
657
596
  }
658
597
  }
659
598
  unbindScrollHoverEvents() {
660
- this.unbindScrollbarEvents();
599
+ if (this.scrollbar)
600
+ this.scrollbar.dispose();
601
+ this.scrollbar = null;
602
+ this.scrollHovered = false;
603
+ this.touchScrolling = false;
661
604
  if (!this.hoverTarget)
662
605
  return;
663
606
  this.hoverTarget.removeEventListener('mouseenter', this.handleScrollMouseEnter);
@@ -665,6 +608,8 @@ export class AlarmElement extends ConditionalDisplayElement {
665
608
  this.hoverTarget.removeEventListener('wheel', this.handleScrollWheel);
666
609
  this.hoverTarget.removeEventListener('touchstart', this.handleScrollTouchStart);
667
610
  this.hoverTarget.removeEventListener('touchmove', this.handleScrollTouchMove);
611
+ this.hoverTarget.removeEventListener('touchend', this.handleScrollTouchEnd);
612
+ this.hoverTarget.removeEventListener('touchcancel', this.handleScrollTouchEnd);
668
613
  this.hoverTarget = null;
669
614
  }
670
615
  // 手动滚动只在当前可见窗口范围内移动(不触发翻页),到顶/到底停住,
@@ -672,41 +617,26 @@ export class AlarmElement extends ConditionalDisplayElement {
672
617
  moveScrollWindowBy(rowDelta) {
673
618
  if (!this.scrollContent)
674
619
  return;
675
- const maxWindowStart = Math.max(0, this.pageSize - this.maxVisibleRows);
620
+ const maxWindowStart = this.getMaxWindowStart();
676
621
  const nextStart = Math.min(maxWindowStart, Math.max(0, this.scrollWindowStart + rowDelta));
677
622
  if (nextStart === this.scrollWindowStart)
678
623
  return;
679
624
  this.scrollWindowStart = nextStart;
680
625
  this.renderScrollWindow(this.scrollWindowStart);
681
626
  }
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);
627
+ getCanvasScaleY() {
628
+ const matrix = this.element.getScreenCTM();
629
+ return matrix ? Math.sqrt(matrix.c * matrix.c + matrix.d * matrix.d) || 1 : 1;
688
630
  }
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;
703
- }
704
- getClientY(event) {
705
- return 'touches' in event ? event.touches[0].clientY : event.clientY;
631
+ getScreenRowHeight() {
632
+ var _a;
633
+ const row = (_a = this.scrollContent) === null || _a === void 0 ? void 0 : _a.querySelectorAll('tr')[1];
634
+ return (row === null || row === void 0 ? void 0 : row.getBoundingClientRect().height) || this.rowHeight * this.getCanvasScaleY();
706
635
  }
707
636
  startAutoScroll() {
637
+ var _a;
708
638
  clearInterval(this.scrollIntervalId);
709
- if (!this.model.generalSetting.autoScroll)
639
+ if (this.isDisposed || this.scrollHovered || this.touchScrolling || ((_a = this.scrollbar) === null || _a === void 0 ? void 0 : _a.isInteracting) || !this.model.generalSetting.autoScroll)
710
640
  return;
711
641
  const totalPages = Math.max(1, Math.ceil(this.totalCount / this.pageSize));
712
642
  if (totalPages <= 1 && !this.scrollContent)
@@ -719,7 +649,7 @@ export class AlarmElement extends ConditionalDisplayElement {
719
649
  this.autoNextPage();
720
650
  return;
721
651
  }
722
- const maxWindowStart = this.pageSize - this.maxVisibleRows;
652
+ const maxWindowStart = this.getMaxWindowStart();
723
653
  if (maxWindowStart <= 0 || this.scrollWindowStart >= maxWindowStart) {
724
654
  this.autoNextPage();
725
655
  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"}],"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,38 @@
1
+ /** SVG scrollbar kept outside foreignObject so WKWebView applies the canvas scale. */
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 focused;
20
+ private dragStartY;
21
+ private dragStartRow;
22
+ constructor(parent: SVGElement, x: number, y: number, width: number, height: number, visibleRows: number, onChange: (start: number) => void, onPause: () => void, onResume: () => void);
23
+ get isInteracting(): boolean;
24
+ update(start: number, totalRows: number): void;
25
+ dispose(): void;
26
+ private createElement;
27
+ private setRect;
28
+ private getLocalY;
29
+ private readonly handleStart;
30
+ private readonly handleMove;
31
+ private readonly handleEnd;
32
+ private readonly handleLeave;
33
+ private readonly handleEnter;
34
+ private readonly handleFocus;
35
+ private readonly handleBlur;
36
+ private readonly handleKeyDown;
37
+ private unbindDrag;
38
+ }
@@ -0,0 +1,173 @@
1
+ /** SVG scrollbar kept outside foreignObject so WKWebView applies the canvas scale. */
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.focused = false;
17
+ this.dragStartY = 0;
18
+ this.dragStartRow = 0;
19
+ this.handleStart = (event) => {
20
+ if (!this.maxStart || ('button' in event && event.button !== 0) || ('touches' in event && event.touches.length !== 1))
21
+ return;
22
+ event.preventDefault();
23
+ event.stopPropagation();
24
+ this.onPause();
25
+ const localY = this.getLocalY(event);
26
+ // The full gutter is clickable, including the transparent sides of the thumb.
27
+ if (localY < this.y + this.thumbTop || localY > this.y + this.thumbTop + this.thumbHeight) {
28
+ this.onChange(this.start + (localY < this.y + this.thumbTop ? -this.visibleRows : this.visibleRows));
29
+ this.onResume();
30
+ return;
31
+ }
32
+ this.dragging = true;
33
+ this.dragStartY = localY;
34
+ this.dragStartRow = this.start;
35
+ this.thumb.setAttribute('fill', '#8f99aa');
36
+ this.thumb.style.cursor = 'grabbing';
37
+ this.ownerDocument.addEventListener('mousemove', this.handleMove);
38
+ this.ownerDocument.addEventListener('mouseup', this.handleEnd);
39
+ this.ownerDocument.addEventListener('touchmove', this.handleMove, { passive: false });
40
+ this.ownerDocument.addEventListener('touchend', this.handleEnd);
41
+ this.ownerDocument.addEventListener('touchcancel', this.handleEnd);
42
+ this.ownerDocument.defaultView.addEventListener('blur', this.handleEnd);
43
+ };
44
+ this.handleMove = (event) => {
45
+ if (!this.dragging || ('touches' in event && event.touches.length !== 1))
46
+ return;
47
+ event.preventDefault();
48
+ event.stopPropagation();
49
+ const travel = this.height - this.thumbHeight;
50
+ if (travel <= 0)
51
+ return;
52
+ const rowDelta = Math.round((this.getLocalY(event) - this.dragStartY) / travel * this.maxStart);
53
+ this.onChange(this.dragStartRow + rowDelta);
54
+ };
55
+ this.handleEnd = () => {
56
+ if (!this.dragging)
57
+ return;
58
+ this.dragging = false;
59
+ this.unbindDrag();
60
+ this.thumb.setAttribute('fill', '#b6bdc9');
61
+ this.thumb.style.cursor = 'grab';
62
+ this.onResume();
63
+ };
64
+ this.handleLeave = () => {
65
+ this.hovered = false;
66
+ this.onResume();
67
+ };
68
+ this.handleEnter = () => {
69
+ this.hovered = true;
70
+ this.onPause();
71
+ };
72
+ this.handleFocus = () => {
73
+ this.focused = true;
74
+ this.onPause();
75
+ };
76
+ this.handleBlur = () => {
77
+ this.focused = false;
78
+ this.onResume();
79
+ };
80
+ this.handleKeyDown = (event) => {
81
+ const targets = {
82
+ ArrowUp: this.start - 1,
83
+ ArrowDown: this.start + 1,
84
+ PageUp: this.start - this.visibleRows,
85
+ PageDown: this.start + this.visibleRows,
86
+ Home: 0,
87
+ End: this.maxStart
88
+ };
89
+ if (!(event.key in targets))
90
+ return;
91
+ event.preventDefault();
92
+ event.stopPropagation();
93
+ this.onChange(targets[event.key]);
94
+ };
95
+ this.ownerDocument = parent.ownerDocument;
96
+ this.group = this.createElement('g');
97
+ this.group.setAttribute('role', 'scrollbar');
98
+ this.group.setAttribute('aria-orientation', 'vertical');
99
+ this.group.setAttribute('aria-valuemin', '0');
100
+ this.group.setAttribute('tabindex', '0');
101
+ this.group.style.cursor = 'default';
102
+ this.track = this.createElement('rect');
103
+ this.setRect(this.track, x, y, width, height);
104
+ this.track.setAttribute('fill', '#eef0f4');
105
+ this.track.setAttribute('rx', String(width / 2));
106
+ this.thumb = this.createElement('rect');
107
+ this.setRect(this.thumb, x + width / 4, y, width / 2, height);
108
+ this.thumb.setAttribute('fill', '#b6bdc9');
109
+ this.thumb.setAttribute('rx', String(width / 4));
110
+ this.thumb.style.cursor = 'grab';
111
+ this.group.appendChild(this.track);
112
+ this.group.appendChild(this.thumb);
113
+ parent.appendChild(this.group);
114
+ this.group.addEventListener('mousedown', this.handleStart);
115
+ this.group.addEventListener('touchstart', this.handleStart, { passive: false });
116
+ this.group.addEventListener('keydown', this.handleKeyDown);
117
+ this.group.addEventListener('mouseenter', this.handleEnter);
118
+ this.group.addEventListener('mouseleave', this.handleLeave);
119
+ this.group.addEventListener('focus', this.handleFocus);
120
+ this.group.addEventListener('blur', this.handleBlur);
121
+ }
122
+ get isInteracting() {
123
+ return this.dragging || this.hovered || this.focused;
124
+ }
125
+ update(start, totalRows) {
126
+ this.maxStart = Math.max(0, totalRows - this.visibleRows);
127
+ this.start = Math.min(this.maxStart, Math.max(0, start));
128
+ this.group.setAttribute('display', this.maxStart ? 'inline' : 'none');
129
+ this.group.setAttribute('aria-valuemax', String(this.maxStart));
130
+ this.group.setAttribute('aria-valuenow', String(this.start));
131
+ this.thumbHeight = Math.min(this.height, Math.max(20, this.height * this.visibleRows / Math.max(1, totalRows)));
132
+ this.thumbTop = this.maxStart ? (this.height - this.thumbHeight) * this.start / this.maxStart : 0;
133
+ this.thumb.setAttribute('height', String(this.thumbHeight));
134
+ this.thumb.setAttribute('y', String(this.y + this.thumbTop));
135
+ }
136
+ dispose() {
137
+ this.dragging = false;
138
+ this.unbindDrag();
139
+ this.group.removeEventListener('mousedown', this.handleStart);
140
+ this.group.removeEventListener('touchstart', this.handleStart);
141
+ this.group.removeEventListener('keydown', this.handleKeyDown);
142
+ this.group.removeEventListener('mouseenter', this.handleEnter);
143
+ this.group.removeEventListener('mouseleave', this.handleLeave);
144
+ this.group.removeEventListener('focus', this.handleFocus);
145
+ this.group.removeEventListener('blur', this.handleBlur);
146
+ this.group.remove();
147
+ }
148
+ createElement(name) {
149
+ return this.ownerDocument.createElementNS('http://www.w3.org/2000/svg', name);
150
+ }
151
+ setRect(rect, x, y, width, height) {
152
+ rect.setAttribute('x', String(x));
153
+ rect.setAttribute('y', String(y));
154
+ rect.setAttribute('width', String(width));
155
+ rect.setAttribute('height', String(height));
156
+ }
157
+ getLocalY(event) {
158
+ const pointer = 'touches' in event ? event.touches[0] : event;
159
+ const point = this.group.ownerSVGElement.createSVGPoint();
160
+ point.x = pointer.clientX;
161
+ point.y = pointer.clientY;
162
+ const matrix = this.group.getScreenCTM();
163
+ return matrix ? point.matrixTransform(matrix.inverse()).y : point.y;
164
+ }
165
+ unbindDrag() {
166
+ this.ownerDocument.removeEventListener('mousemove', this.handleMove);
167
+ this.ownerDocument.removeEventListener('mouseup', this.handleEnd);
168
+ this.ownerDocument.removeEventListener('touchmove', this.handleMove);
169
+ this.ownerDocument.removeEventListener('touchend', this.handleEnd);
170
+ this.ownerDocument.removeEventListener('touchcancel', this.handleEnd);
171
+ this.ownerDocument.defaultView.removeEventListener('blur', this.handleEnd);
172
+ }
173
+ }
@@ -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":17,"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":23,"character":35},{"__symbolic":"error","message":"Expression form not supported","line":24,"character":34},{"__symbolic":"error","message":"Expression form not supported","line":25,"character":35}]}],"update":[{"__symbolic":"method"}],"dispose":[{"__symbolic":"method"}],"createElement":[{"__symbolic":"method"}],"setRect":[{"__symbolic":"method"}],"getLocalY":[{"__symbolic":"method"}],"unbindDrag":[{"__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.188",
4
4
  "module": "public_api.js",
5
5
  "typings": "public_api.d.ts",
6
6
  "license": "UNLICENSED",