@bitstack/ng-boundary 14.0.6-alpha.1 → 14.0.6-alpha.3

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.
@@ -1,6 +1,6 @@
1
- import { Component, EventEmitter, inject, Input, Output, } from '@angular/core';
1
+ import { Component, ElementRef, EventEmitter, inject, Input, Output, } from '@angular/core';
2
2
  import { BreakpointObserver } from '@angular/cdk/layout';
3
- import { Subject, takeUntil } from 'rxjs';
3
+ import { fromEvent, Subject, takeUntil } from 'rxjs';
4
4
  import { BsBoundaryContextService } from './bs-boundary-context.service';
5
5
  import * as i0 from "@angular/core";
6
6
  export class BsBoundary {
@@ -11,6 +11,7 @@ export class BsBoundary {
11
11
  this.orientationChange = new EventEmitter();
12
12
  this.breakpointObserver = inject(BreakpointObserver);
13
13
  this.boundaryContext = inject(BsBoundaryContextService);
14
+ this.elementRef = inject(ElementRef);
14
15
  this.destroy$ = new Subject();
15
16
  }
16
17
  get isLandscape() {
@@ -18,6 +19,11 @@ export class BsBoundary {
18
19
  }
19
20
  ngOnInit() {
20
21
  this.setupBreakpointObserver();
22
+ this.setupResizeObserver();
23
+ }
24
+ ngAfterViewInit() {
25
+ // 在 view 初始化後計算(確保 CSS 已完全應用)
26
+ this.updateBoundaryCalculations();
21
27
  }
22
28
  ngOnDestroy() {
23
29
  this.destroy$.next();
@@ -25,6 +31,7 @@ export class BsBoundary {
25
31
  }
26
32
  /**
27
33
  * 監控方向變化
34
+ * 只在 lock 模式時需要,auto 模式由 CSS 媒體查詢處理
28
35
  */
29
36
  setupBreakpointObserver() {
30
37
  if (this.lockOrientation === 'landscape') {
@@ -37,22 +44,77 @@ export class BsBoundary {
37
44
  this.orientationChange.emit(false);
38
45
  return;
39
46
  }
47
+ // Auto 模式:監聽實際的設備方向(用於 orientationChange 事件)
40
48
  this.breakpointObserver
41
49
  .observe('(orientation: landscape)')
42
50
  .pipe(takeUntil(this.destroy$))
43
51
  .subscribe(result => {
44
52
  this.boundaryContext.setIsLandscape(result.matches);
45
53
  this.orientationChange.emit(result.matches);
54
+ // 方向變化時重新計算
55
+ this.updateBoundaryCalculations();
56
+ });
57
+ }
58
+ /**
59
+ * 監控視口大小變化
60
+ */
61
+ setupResizeObserver() {
62
+ fromEvent(window, 'resize')
63
+ .pipe(
64
+ // debounceTime(150),
65
+ takeUntil(this.destroy$))
66
+ .subscribe(() => {
67
+ this.updateBoundaryCalculations();
46
68
  });
47
69
  }
70
+ /**
71
+ * 更新邊界計算(支援 Chrome 74,使用 JavaScript 模擬 min 函數)
72
+ * 設定 --boundary-width, --boundary-height, --px2vw-ratio
73
+ */
74
+ updateBoundaryCalculations() {
75
+ const hostElement = this.elementRef.nativeElement;
76
+ const computedStyle = getComputedStyle(hostElement);
77
+ // 讀取基礎 CSS 變數(這些是純數值,不是 calc() 表達式)
78
+ const vw100 = parseFloat(computedStyle.getPropertyValue('--vw100')) || window.innerWidth;
79
+ const vh100 = parseFloat(computedStyle.getPropertyValue('--vh100')) || window.innerHeight;
80
+ const designWidth = parseFloat(computedStyle.getPropertyValue('--design-width'));
81
+ const designHeight = parseFloat(computedStyle.getPropertyValue('--design-height'));
82
+ console.log('designWidth', designWidth);
83
+ // 檢查基礎變數是否準備好
84
+ if (isNaN(designWidth) || isNaN(designHeight) || designWidth === 0 || designHeight === 0) {
85
+ return; // CSS 變數還沒準備好,跳過
86
+ }
87
+ // 在 JavaScript 中計算所有值(避免讀取 calc() 表達式)
88
+ const landscapeRatio = designWidth / designHeight;
89
+ const portraitRatio = designHeight / designWidth;
90
+ // 根據方向計算邊界最大值
91
+ const isLandscape = this.isLandscape;
92
+ const boundaryMaxWidth = isLandscape
93
+ ? vh100 * portraitRatio
94
+ : vh100 * landscapeRatio;
95
+ const boundaryMaxHeight = isLandscape
96
+ ? vw100 * landscapeRatio
97
+ : vw100 * portraitRatio;
98
+ // 使用 JavaScript Math.min 模擬 CSS min() 函數
99
+ const boundaryWidth = Math.min(vw100, boundaryMaxWidth);
100
+ const boundaryHeight = Math.min(vh100, boundaryMaxHeight);
101
+ // 計算設計基準和 px2vw 比例
102
+ const designBaseWidth = isLandscape ? designHeight : designWidth;
103
+ const px2vwRatio = boundaryWidth / designBaseWidth;
104
+ // 設定 CSS 變數(向後兼容 + 精確的 ratio)
105
+ hostElement.style.setProperty('--boundary-width', `${boundaryWidth}px`);
106
+ hostElement.style.setProperty('--boundary-height', `${boundaryHeight}px`);
107
+ hostElement.style.setProperty('--px2vw-ratio', `${px2vwRatio}`);
108
+ }
48
109
  }
49
110
  BsBoundary.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: BsBoundary, deps: [], target: i0.ɵɵFactoryTarget.Component });
50
- BsBoundary.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: BsBoundary, isStandalone: true, selector: "bs-boundary", inputs: { isFixedCenter: "isFixedCenter", pointerEventsNone: "pointerEventsNone", lockOrientation: "lockOrientation", forwardStyle: "forwardStyle" }, outputs: { orientationChange: "orientationChange" }, host: { properties: { "class.landscape": "isLandscape" } }, providers: [BsBoundaryContextService], ngImport: i0, template: "<div\n class=\"boundary-wrapper\"\n [class.fixed-center]=\"isFixedCenter\"\n [class.pointerEventsNone]=\"pointerEventsNone\"\n [style]=\"forwardStyle\"\n>\n <ng-content></ng-content>\n</div>\n", styles: ["@charset \"UTF-8\";:host{--landscape-ratio: calc(var(--design-width) / var(--design-height));--portrait-ratio: calc(var(--design-height) / var(--design-width))}.boundary-wrapper{width:100%;height:100%;max-width:var(--boundary-width);max-height:var(--boundary-height)}.boundary-wrapper.pointerEventsNone{pointer-events:none}.boundary-wrapper{--active-ratio: var(--landscape-ratio);--active-inv-ratio: var(--portrait-ratio);--boundary-width: var(--vw100);--boundary-height: calc(var(--boundary-width) * var(--active-inv-ratio));--px2vw-ratio: calc(var(--boundary-width) / var(--design-base-width));--design-base-width: var(--design-width);--design-base-height: var(--design-height)}@media (min-aspect-ratio: 16/9){.boundary-wrapper{--boundary-height: var(--vh100);--boundary-width: calc(var(--boundary-height) * var(--active-ratio))}}.boundary-wrapper.landscape{--active-ratio: var(--portrait-ratio);--active-inv-ratio: var(--landscape-ratio);--design-base-width: var(--design-height);--design-base-height: var(--design-width);--boundary-width: var(--vw100);--boundary-height: calc(var(--boundary-width) * var(--active-inv-ratio))}@media (min-aspect-ratio: 9/16){.boundary-wrapper.landscape{--boundary-height: var(--vh100);--boundary-width: calc(var(--boundary-height) * var(--active-ratio))}}.fixed-center{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);transform-origin:center;z-index:99}\n"] });
111
+ BsBoundary.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: BsBoundary, isStandalone: true, selector: "bs-boundary", inputs: { isFixedCenter: "isFixedCenter", pointerEventsNone: "pointerEventsNone", lockOrientation: "lockOrientation", forwardStyle: "forwardStyle" }, outputs: { orientationChange: "orientationChange" }, host: { properties: { "class.landscape": "isLandscape && lockOrientation !== \"auto\"", "class.portrait": "!isLandscape && lockOrientation !== \"auto\"" } }, providers: [BsBoundaryContextService], ngImport: i0, template: "<div\n class=\"boundary-wrapper\"\n [class.fixed-center]=\"isFixedCenter\"\n [class.pointerEventsNone]=\"pointerEventsNone\"\n [style]=\"forwardStyle\"\n>\n <ng-content></ng-content>\n</div>\n", styles: [":host{--landscape-ratio: calc(var(--design-width) / var(--design-height));--portrait-ratio: calc(var(--design-height) / var(--design-width));--boundary-max-width: calc(var(--vh100) * var(--landscape-ratio));--boundary-max-height: calc(var(--vw100) * var(--portrait-ratio));--design-base-width: var(--design-width);--design-base-height: var(--design-height);--px2vw-ratio: calc(var(--vw100) / var(--design-base-width))}.boundary-wrapper{width:var(--vw100);height:var(--vh100);max-width:var(--boundary-max-width);max-height:var(--boundary-max-height)}.boundary-wrapper.pointerEventsNone{pointer-events:none}:host.landscape{--boundary-max-width: calc(var(--vh100) * var(--portrait-ratio));--boundary-max-height: calc(var(--vw100) * var(--landscape-ratio));--design-base-width: var(--design-height);--design-base-height: var(--design-width);--px2vw-ratio: calc(var(--vh100) * var(--portrait-ratio) / var(--design-base-width))}@media (orientation: landscape){:host:not(.landscape):not(.portrait){--boundary-max-width: calc(var(--vh100) * var(--portrait-ratio));--boundary-max-height: calc(var(--vw100) * var(--landscape-ratio));--design-base-width: var(--design-height);--design-base-height: var(--design-width);--px2vw-ratio: calc(var(--vh100) * var(--portrait-ratio) / var(--design-base-width))}}.fixed-center{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);transform-origin:center;z-index:99}\n"] });
51
112
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: BsBoundary, decorators: [{
52
113
  type: Component,
53
114
  args: [{ selector: 'bs-boundary', standalone: true, providers: [BsBoundaryContextService], host: {
54
- '[class.landscape]': 'isLandscape'
55
- }, template: "<div\n class=\"boundary-wrapper\"\n [class.fixed-center]=\"isFixedCenter\"\n [class.pointerEventsNone]=\"pointerEventsNone\"\n [style]=\"forwardStyle\"\n>\n <ng-content></ng-content>\n</div>\n", styles: ["@charset \"UTF-8\";:host{--landscape-ratio: calc(var(--design-width) / var(--design-height));--portrait-ratio: calc(var(--design-height) / var(--design-width))}.boundary-wrapper{width:100%;height:100%;max-width:var(--boundary-width);max-height:var(--boundary-height)}.boundary-wrapper.pointerEventsNone{pointer-events:none}.boundary-wrapper{--active-ratio: var(--landscape-ratio);--active-inv-ratio: var(--portrait-ratio);--boundary-width: var(--vw100);--boundary-height: calc(var(--boundary-width) * var(--active-inv-ratio));--px2vw-ratio: calc(var(--boundary-width) / var(--design-base-width));--design-base-width: var(--design-width);--design-base-height: var(--design-height)}@media (min-aspect-ratio: 16/9){.boundary-wrapper{--boundary-height: var(--vh100);--boundary-width: calc(var(--boundary-height) * var(--active-ratio))}}.boundary-wrapper.landscape{--active-ratio: var(--portrait-ratio);--active-inv-ratio: var(--landscape-ratio);--design-base-width: var(--design-height);--design-base-height: var(--design-width);--boundary-width: var(--vw100);--boundary-height: calc(var(--boundary-width) * var(--active-inv-ratio))}@media (min-aspect-ratio: 9/16){.boundary-wrapper.landscape{--boundary-height: var(--vh100);--boundary-width: calc(var(--boundary-height) * var(--active-ratio))}}.fixed-center{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);transform-origin:center;z-index:99}\n"] }]
115
+ '[class.landscape]': 'isLandscape && lockOrientation !== "auto"',
116
+ '[class.portrait]': '!isLandscape && lockOrientation !== "auto"'
117
+ }, template: "<div\n class=\"boundary-wrapper\"\n [class.fixed-center]=\"isFixedCenter\"\n [class.pointerEventsNone]=\"pointerEventsNone\"\n [style]=\"forwardStyle\"\n>\n <ng-content></ng-content>\n</div>\n", styles: [":host{--landscape-ratio: calc(var(--design-width) / var(--design-height));--portrait-ratio: calc(var(--design-height) / var(--design-width));--boundary-max-width: calc(var(--vh100) * var(--landscape-ratio));--boundary-max-height: calc(var(--vw100) * var(--portrait-ratio));--design-base-width: var(--design-width);--design-base-height: var(--design-height);--px2vw-ratio: calc(var(--vw100) / var(--design-base-width))}.boundary-wrapper{width:var(--vw100);height:var(--vh100);max-width:var(--boundary-max-width);max-height:var(--boundary-max-height)}.boundary-wrapper.pointerEventsNone{pointer-events:none}:host.landscape{--boundary-max-width: calc(var(--vh100) * var(--portrait-ratio));--boundary-max-height: calc(var(--vw100) * var(--landscape-ratio));--design-base-width: var(--design-height);--design-base-height: var(--design-width);--px2vw-ratio: calc(var(--vh100) * var(--portrait-ratio) / var(--design-base-width))}@media (orientation: landscape){:host:not(.landscape):not(.portrait){--boundary-max-width: calc(var(--vh100) * var(--portrait-ratio));--boundary-max-height: calc(var(--vw100) * var(--landscape-ratio));--design-base-width: var(--design-height);--design-base-height: var(--design-width);--px2vw-ratio: calc(var(--vh100) * var(--portrait-ratio) / var(--design-base-width))}}.fixed-center{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);transform-origin:center;z-index:99}\n"] }]
56
118
  }], propDecorators: { isFixedCenter: [{
57
119
  type: Input
58
120
  }], pointerEventsNone: [{
@@ -64,4 +126,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
64
126
  }], orientationChange: [{
65
127
  type: Output
66
128
  }] } });
67
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYnMtYm91bmRhcnkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9wcm9qZWN0cy9iaXRzdGFjay1uZy1ib3VuZGFyeS9zcmMvbGliL2JzLWJvdW5kYXJ5LnRzIiwiLi4vLi4vLi4vLi4vcHJvamVjdHMvYml0c3RhY2stbmctYm91bmRhcnkvc3JjL2xpYi90ZW1wbGF0ZS5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFDTCxTQUFTLEVBQ1QsWUFBWSxFQUNaLE1BQU0sRUFDTixLQUFLLEVBRUwsTUFBTSxHQUNQLE1BQU0sZUFBZSxDQUFDO0FBRXZCLE9BQU8sRUFBQyxrQkFBa0IsRUFBQyxNQUFNLHFCQUFxQixDQUFDO0FBQ3ZELE9BQU8sRUFBQyxPQUFPLEVBQUUsU0FBUyxFQUFDLE1BQU0sTUFBTSxDQUFDO0FBQ3hDLE9BQU8sRUFBQyx3QkFBd0IsRUFBQyxNQUFNLCtCQUErQixDQUFDOztBQVl2RSxNQUFNLE9BQU8sVUFBVTtJQVZ2QjtRQVlhLGtCQUFhLEdBQWEsS0FBSyxDQUFDO1FBQ2hDLHNCQUFpQixHQUFhLEtBQUssQ0FBQztRQUNwQyxvQkFBZSxHQUFzQixNQUFNLENBQUM7UUFHM0Msc0JBQWlCLEdBQUcsSUFBSSxZQUFZLEVBQVcsQ0FBQztRQUVsRCx1QkFBa0IsR0FBRyxNQUFNLENBQUMsa0JBQWtCLENBQUMsQ0FBQztRQUNoRCxvQkFBZSxHQUFHLE1BQU0sQ0FBQyx3QkFBd0IsQ0FBQyxDQUFDO1FBQ25ELGFBQVEsR0FBRyxJQUFJLE9BQU8sRUFBUSxDQUFDO0tBdUMxQztJQXJDRyxJQUFJLFdBQVc7UUFDWCxPQUFPLElBQUksQ0FBQyxlQUFlLENBQUMsY0FBYyxFQUFFLENBQUM7SUFDakQsQ0FBQztJQUVELFFBQVE7UUFDSixJQUFJLENBQUMsdUJBQXVCLEVBQUUsQ0FBQztJQUNuQyxDQUFDO0lBRUQsV0FBVztRQUNQLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxFQUFFLENBQUM7UUFDckIsSUFBSSxDQUFDLFFBQVEsQ0FBQyxRQUFRLEVBQUUsQ0FBQztJQUM3QixDQUFDO0lBRUQ7O09BRUc7SUFDSCx1QkFBdUI7UUFDbkIsSUFBSSxJQUFJLENBQUMsZUFBZSxLQUFLLFdBQVcsRUFBRTtZQUN0QyxJQUFJLENBQUMsZUFBZSxDQUFDLGNBQWMsQ0FBQyxJQUFJLENBQUMsQ0FBQztZQUMxQyxJQUFJLENBQUMsaUJBQWlCLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO1lBQ2xDLE9BQU87U0FDVjtRQUVELElBQUksSUFBSSxDQUFDLGVBQWUsS0FBSyxVQUFVLEVBQUU7WUFDckMsSUFBSSxDQUFDLGVBQWUsQ0FBQyxjQUFjLENBQUMsS0FBSyxDQUFDLENBQUM7WUFDM0MsSUFBSSxDQUFDLGlCQUFpQixDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQztZQUNuQyxPQUFPO1NBQ1Y7UUFFRCxJQUFJLENBQUMsa0JBQWtCO2FBQ2xCLE9BQU8sQ0FBQywwQkFBMEIsQ0FBQzthQUNuQyxJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQzthQUM5QixTQUFTLENBQUMsTUFBTSxDQUFDLEVBQUU7WUFDaEIsSUFBSSxDQUFDLGVBQWUsQ0FBQyxjQUFjLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1lBQ3BELElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQ2hELENBQUMsQ0FBQyxDQUFDO0lBQ1gsQ0FBQzs7dUdBakRRLFVBQVU7MkZBQVYsVUFBVSxpVUFMUixDQUFDLHdCQUF3QixDQUFDLDBCQ2xCekMsd01BUUE7MkZEZWEsVUFBVTtrQkFWdEIsU0FBUzsrQkFDSSxhQUFhLGNBRVgsSUFBSSxhQUVMLENBQUMsd0JBQXdCLENBQUMsUUFDL0I7d0JBQ0YsbUJBQW1CLEVBQUUsYUFBYTtxQkFDckM7OEJBSVEsYUFBYTtzQkFBckIsS0FBSztnQkFDRyxpQkFBaUI7c0JBQXpCLEtBQUs7Z0JBQ0csZUFBZTtzQkFBdkIsS0FBSztnQkFDRyxZQUFZO3NCQUFwQixLQUFLO2dCQUVJLGlCQUFpQjtzQkFBMUIsTUFBTSIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7XG4gIENvbXBvbmVudCxcbiAgRXZlbnRFbWl0dGVyLFxuICBpbmplY3QsXG4gIElucHV0LCBPbkRlc3Ryb3ksXG4gIE9uSW5pdCxcbiAgT3V0cHV0LFxufSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7VE9yaWVudGF0aW9uTW9kZX0gZnJvbSAnLi9tb2RlbCc7XG5pbXBvcnQge0JyZWFrcG9pbnRPYnNlcnZlcn0gZnJvbSAnQGFuZ3VsYXIvY2RrL2xheW91dCc7XG5pbXBvcnQge1N1YmplY3QsIHRha2VVbnRpbH0gZnJvbSAncnhqcyc7XG5pbXBvcnQge0JzQm91bmRhcnlDb250ZXh0U2VydmljZX0gZnJvbSAnLi9icy1ib3VuZGFyeS1jb250ZXh0LnNlcnZpY2UnO1xuXG5AQ29tcG9uZW50KHtcbiAgICBzZWxlY3RvcjogJ2JzLWJvdW5kYXJ5JyxcbiAgICB0ZW1wbGF0ZVVybDogJ3RlbXBsYXRlLmh0bWwnLFxuICAgIHN0YW5kYWxvbmU6IHRydWUsXG4gICAgc3R5bGVVcmxzOiBbJy4vc3R5bGVzLnNjc3MnXSxcbiAgICBwcm92aWRlcnM6IFtCc0JvdW5kYXJ5Q29udGV4dFNlcnZpY2VdLFxuICAgIGhvc3Q6IHtcbiAgICAgICAgJ1tjbGFzcy5sYW5kc2NhcGVdJzogJ2lzTGFuZHNjYXBlJ1xuICAgIH1cbn0pXG5leHBvcnQgY2xhc3MgQnNCb3VuZGFyeSBpbXBsZW1lbnRzIE9uSW5pdCwgT25EZXN0cm95IHtcblxuICAgIEBJbnB1dCgpIGlzRml4ZWRDZW50ZXI/OiBib29sZWFuID0gZmFsc2U7XG4gICAgQElucHV0KCkgcG9pbnRlckV2ZW50c05vbmU/OiBib29sZWFuID0gZmFsc2U7XG4gICAgQElucHV0KCkgbG9ja09yaWVudGF0aW9uPzogVE9yaWVudGF0aW9uTW9kZSA9ICdhdXRvJztcbiAgICBASW5wdXQoKSBmb3J3YXJkU3R5bGU/OiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+O1xuXG4gICAgQE91dHB1dCgpIG9yaWVudGF0aW9uQ2hhbmdlID0gbmV3IEV2ZW50RW1pdHRlcjxib29sZWFuPigpO1xuXG4gICAgcHJpdmF0ZSBicmVha3BvaW50T2JzZXJ2ZXIgPSBpbmplY3QoQnJlYWtwb2ludE9ic2VydmVyKTtcbiAgICBwcml2YXRlIGJvdW5kYXJ5Q29udGV4dCA9IGluamVjdChCc0JvdW5kYXJ5Q29udGV4dFNlcnZpY2UpO1xuICAgIHByaXZhdGUgZGVzdHJveSQgPSBuZXcgU3ViamVjdDx2b2lkPigpO1xuXG4gICAgZ2V0IGlzTGFuZHNjYXBlKCk6IGJvb2xlYW4ge1xuICAgICAgICByZXR1cm4gdGhpcy5ib3VuZGFyeUNvbnRleHQuZ2V0SXNMYW5kc2NhcGUoKTtcbiAgICB9XG5cbiAgICBuZ09uSW5pdCgpOiB2b2lkIHtcbiAgICAgICAgdGhpcy5zZXR1cEJyZWFrcG9pbnRPYnNlcnZlcigpO1xuICAgIH1cblxuICAgIG5nT25EZXN0cm95KCk6IHZvaWQge1xuICAgICAgICB0aGlzLmRlc3Ryb3kkLm5leHQoKTtcbiAgICAgICAgdGhpcy5kZXN0cm95JC5jb21wbGV0ZSgpO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIOebo+aOp+aWueWQkeiuiuWMllxuICAgICAqL1xuICAgIHNldHVwQnJlYWtwb2ludE9ic2VydmVyKCkge1xuICAgICAgICBpZiAodGhpcy5sb2NrT3JpZW50YXRpb24gPT09ICdsYW5kc2NhcGUnKSB7XG4gICAgICAgICAgICB0aGlzLmJvdW5kYXJ5Q29udGV4dC5zZXRJc0xhbmRzY2FwZSh0cnVlKTtcbiAgICAgICAgICAgIHRoaXMub3JpZW50YXRpb25DaGFuZ2UuZW1pdCh0cnVlKTtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmICh0aGlzLmxvY2tPcmllbnRhdGlvbiA9PT0gJ3BvcnRyYWl0Jykge1xuICAgICAgICAgICAgdGhpcy5ib3VuZGFyeUNvbnRleHQuc2V0SXNMYW5kc2NhcGUoZmFsc2UpO1xuICAgICAgICAgICAgdGhpcy5vcmllbnRhdGlvbkNoYW5nZS5lbWl0KGZhbHNlKTtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMuYnJlYWtwb2ludE9ic2VydmVyXG4gICAgICAgICAgICAub2JzZXJ2ZSgnKG9yaWVudGF0aW9uOiBsYW5kc2NhcGUpJylcbiAgICAgICAgICAgIC5waXBlKHRha2VVbnRpbCh0aGlzLmRlc3Ryb3kkKSlcbiAgICAgICAgICAgIC5zdWJzY3JpYmUocmVzdWx0ID0+IHtcbiAgICAgICAgICAgICAgICB0aGlzLmJvdW5kYXJ5Q29udGV4dC5zZXRJc0xhbmRzY2FwZShyZXN1bHQubWF0Y2hlcyk7XG4gICAgICAgICAgICAgICAgdGhpcy5vcmllbnRhdGlvbkNoYW5nZS5lbWl0KHJlc3VsdC5tYXRjaGVzKTtcbiAgICAgICAgICAgIH0pO1xuICAgIH1cbn1cbiIsIjxkaXZcbiAgY2xhc3M9XCJib3VuZGFyeS13cmFwcGVyXCJcbiAgW2NsYXNzLmZpeGVkLWNlbnRlcl09XCJpc0ZpeGVkQ2VudGVyXCJcbiAgW2NsYXNzLnBvaW50ZXJFdmVudHNOb25lXT1cInBvaW50ZXJFdmVudHNOb25lXCJcbiAgW3N0eWxlXT1cImZvcndhcmRTdHlsZVwiXG4+XG4gICA8bmctY29udGVudD48L25nLWNvbnRlbnQ+XG48L2Rpdj5cbiJdfQ==
129
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYnMtYm91bmRhcnkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9wcm9qZWN0cy9iaXRzdGFjay1uZy1ib3VuZGFyeS9zcmMvbGliL2JzLWJvdW5kYXJ5LnRzIiwiLi4vLi4vLi4vLi4vcHJvamVjdHMvYml0c3RhY2stbmctYm91bmRhcnkvc3JjL2xpYi90ZW1wbGF0ZS5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFFTCxTQUFTLEVBQ1QsVUFBVSxFQUNWLFlBQVksRUFDWixNQUFNLEVBQ04sS0FBSyxFQUVMLE1BQU0sR0FDUCxNQUFNLGVBQWUsQ0FBQztBQUV2QixPQUFPLEVBQUMsa0JBQWtCLEVBQUMsTUFBTSxxQkFBcUIsQ0FBQztBQUN2RCxPQUFPLEVBQWUsU0FBUyxFQUFFLE9BQU8sRUFBRSxTQUFTLEVBQUMsTUFBTSxNQUFNLENBQUM7QUFDakUsT0FBTyxFQUFDLHdCQUF3QixFQUFDLE1BQU0sK0JBQStCLENBQUM7O0FBYXZFLE1BQU0sT0FBTyxVQUFVO0lBWHZCO1FBYWEsa0JBQWEsR0FBYSxLQUFLLENBQUM7UUFDaEMsc0JBQWlCLEdBQWEsS0FBSyxDQUFDO1FBQ3BDLG9CQUFlLEdBQXNCLE1BQU0sQ0FBQztRQUczQyxzQkFBaUIsR0FBRyxJQUFJLFlBQVksRUFBVyxDQUFDO1FBRWxELHVCQUFrQixHQUFHLE1BQU0sQ0FBQyxrQkFBa0IsQ0FBQyxDQUFDO1FBQ2hELG9CQUFlLEdBQUcsTUFBTSxDQUFDLHdCQUF3QixDQUFDLENBQUM7UUFDbkQsZUFBVSxHQUFHLE1BQU0sQ0FBQyxVQUFVLENBQUMsQ0FBQztRQUNoQyxhQUFRLEdBQUcsSUFBSSxPQUFPLEVBQVEsQ0FBQztLQStHMUM7SUE3R0csSUFBSSxXQUFXO1FBQ1gsT0FBTyxJQUFJLENBQUMsZUFBZSxDQUFDLGNBQWMsRUFBRSxDQUFDO0lBQ2pELENBQUM7SUFFRCxRQUFRO1FBQ0osSUFBSSxDQUFDLHVCQUF1QixFQUFFLENBQUM7UUFD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QixTQUFTOytCQUNJLGFBQWEsY0FFWCxJQUFJLGFBRUwsQ0FBQyx3QkFBd0IsQ0FBQyxRQUMvQjt3QkFDRixtQkFBbUIsRUFBRSwyQ0FBMkM7d0JBQ2hFLGtCQUFrQixFQUFFLDRDQUE0QztxQkFDbkU7OEJBSVEsYUFBYTtzQkFBckIsS0FBSztnQkFDRyxpQkFBaUI7c0JBQXpCLEtBQUs7Z0JBQ0csZUFBZTtzQkFBdkIsS0FBSztnQkFDRyxZQUFZO3NCQUFwQixLQUFLO2dCQUVJLGlCQUFpQjtzQkFBMUIsTUFBTSIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7XG4gIEFmdGVyVmlld0luaXQsXG4gIENvbXBvbmVudCxcbiAgRWxlbWVudFJlZixcbiAgRXZlbnRFbWl0dGVyLFxuICBpbmplY3QsXG4gIElucHV0LCBPbkRlc3Ryb3ksXG4gIE9uSW5pdCxcbiAgT3V0cHV0LFxufSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7VE9yaWVudGF0aW9uTW9kZX0gZnJvbSAnLi9tb2RlbCc7XG5pbXBvcnQge0JyZWFrcG9pbnRPYnNlcnZlcn0gZnJvbSAnQGFuZ3VsYXIvY2RrL2xheW91dCc7XG5pbXBvcnQge2RlYm91bmNlVGltZSwgZnJvbUV2ZW50LCBTdWJqZWN0LCB0YWtlVW50aWx9IGZyb20gJ3J4anMnO1xuaW1wb3J0IHtCc0JvdW5kYXJ5Q29udGV4dFNlcnZpY2V9IGZyb20gJy4vYnMtYm91bmRhcnktY29udGV4dC5zZXJ2aWNlJztcblxuQENvbXBvbmVudCh7XG4gICAgc2VsZWN0b3I6ICdicy1ib3VuZGFyeScsXG4gICAgdGVtcGxhdGVVcmw6ICd0ZW1wbGF0ZS5odG1sJyxcbiAgICBzdGFuZGFsb25lOiB0cnVlLFxuICAgIHN0eWxlVXJsczogWycuL3N0eWxlcy5zY3NzJ10sXG4gICAgcHJvdmlkZXJzOiBbQnNCb3VuZGFyeUNvbnRleHRTZXJ2aWNlXSxcbiAgICBob3N0OiB7XG4gICAgICAgICdbY2xhc3MubGFuZHNjYXBlXSc6ICdpc0xhbmRzY2FwZSAmJiBsb2NrT3JpZW50YXRpb24gIT09IFwiYXV0b1wiJyxcbiAgICAgICAgJ1tjbGFzcy5wb3J0cmFpdF0nOiAnIWlzTGFuZHNjYXBlICYmIGxvY2tPcmllbnRhdGlvbiAhPT0gXCJhdXRvXCInXG4gICAgfVxufSlcbmV4cG9ydCBjbGFzcyBCc0JvdW5kYXJ5IGltcGxlbWVudHMgT25Jbml0LCBBZnRlclZpZXdJbml0LCBPbkRlc3Ryb3kge1xuXG4gICAgQElucHV0KCkgaXNGaXhlZENlbnRlcj86IGJvb2xlYW4gPSBmYWxzZTtcbiAgICBASW5wdXQoKSBwb2ludGVyRXZlbnRzTm9uZT86IGJvb2xlYW4gPSBmYWxzZTtcbiAgICBASW5wdXQoKSBsb2NrT3JpZW50YXRpb24/OiBUT3JpZW50YXRpb25Nb2RlID0gJ2F1dG8nO1xuICAgIEBJbnB1dCgpIGZvcndhcmRTdHlsZT86IFJlY29yZDxzdHJpbmcsIHN0cmluZz47XG5cbiAgICBAT3V0cHV0KCkgb3JpZW50YXRpb25DaGFuZ2UgPSBuZXcgRXZlbnRFbWl0dGVyPGJvb2xlYW4+KCk7XG5cbiAgICBwcml2YXRlIGJyZWFrcG9pbnRPYnNlcnZlciA9IGluamVjdChCcmVha3BvaW50T2JzZXJ2ZXIpO1xuICAgIHByaXZhdGUgYm91bmRhcnlDb250ZXh0ID0gaW5qZWN0KEJzQm91bmRhcnlDb250ZXh0U2VydmljZSk7XG4gICAgcHJpdmF0ZSBlbGVtZW50UmVmID0gaW5qZWN0KEVsZW1lbnRSZWYpO1xuICAgIHByaXZhdGUgZGVzdHJveSQgPSBuZXcgU3ViamVjdDx2b2lkPigpO1xuXG4gICAgZ2V0IGlzTGFuZHNjYXBlKCk6IGJvb2xlYW4ge1xuICAgICAgICByZXR1cm4gdGhpcy5ib3VuZGFyeUNvbnRleHQuZ2V0SXNMYW5kc2NhcGUoKTtcbiAgICB9XG5cbiAgICBuZ09uSW5pdCgpOiB2b2lkIHtcbiAgICAgICAgdGhpcy5zZXR1cEJyZWFrcG9pbnRPYnNlcnZlcigpO1xuICAgICAgICB0aGlzLnNldHVwUmVzaXplT2JzZXJ2ZXIoKTtcbiAgICB9XG5cbiAgICBuZ0FmdGVyVmlld0luaXQoKTogdm9pZCB7XG4gICAgICAgIC8vIOWcqCB2aWV3IOWIneWni+WMluW+jOioiOeul++8iOeiuuS/nSBDU1Mg5bey5a6M5YWo5oeJ55So77yJXG4gICAgICAgIHRoaXMudXBkYXRlQm91bmRhcnlDYWxjdWxhdGlvbnMoKTtcbiAgICB9XG5cbiAgICBuZ09uRGVzdHJveSgpOiB2b2lkIHtcbiAgICAgICAgdGhpcy5kZXN0cm95JC5uZXh0KCk7XG4gICAgICAgIHRoaXMuZGVzdHJveSQuY29tcGxldGUoKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiDnm6PmjqfmlrnlkJHororljJZcbiAgICAgKiDlj6rlnKggbG9jayDmqKHlvI/mmYLpnIDopoHvvIxhdXRvIOaooeW8j+eUsSBDU1Mg5aqS6auU5p+l6Kmi6JmV55CGXG4gICAgICovXG4gICAgc2V0dXBCcmVha3BvaW50T2JzZXJ2ZXIoKSB7XG4gICAgICAgIGlmICh0aGlzLmxvY2tPcmllbnRhdGlvbiA9PT0gJ2xhbmRzY2FwZScpIHtcbiAgICAgICAgICAgIHRoaXMuYm91bmRhcnlDb250ZXh0LnNldElzTGFuZHNjYXBlKHRydWUpO1xuICAgICAgICAgICAgdGhpcy5vcmllbnRhdGlvbkNoYW5nZS5lbWl0KHRydWUpO1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHRoaXMubG9ja09yaWVudGF0aW9uID09PSAncG9ydHJhaXQnKSB7XG4gICAgICAgICAgICB0aGlzLmJvdW5kYXJ5Q29udGV4dC5zZXRJc0xhbmRzY2FwZShmYWxzZSk7XG4gICAgICAgICAgICB0aGlzLm9yaWVudGF0aW9uQ2hhbmdlLmVtaXQoZmFsc2UpO1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gQXV0byDmqKHlvI/vvJrnm6Pogb3lr6bpmpvnmoToqK3lgpnmlrnlkJHvvIjnlKjmlrwgb3JpZW50YXRpb25DaGFuZ2Ug5LqL5Lu277yJXG4gICAgICAgIHRoaXMuYnJlYWtwb2ludE9ic2VydmVyXG4gICAgICAgICAgICAub2JzZXJ2ZSgnKG9yaWVudGF0aW9uOiBsYW5kc2NhcGUpJylcbiAgICAgICAgICAgIC5waXBlKHRha2VVbnRpbCh0aGlzLmRlc3Ryb3kkKSlcbiAgICAgICAgICAgIC5zdWJzY3JpYmUocmVzdWx0ID0+IHtcbiAgICAgICAgICAgICAgICB0aGlzLmJvdW5kYXJ5Q29udGV4dC5zZXRJc0xhbmRzY2FwZShyZXN1bHQubWF0Y2hlcyk7XG4gICAgICAgICAgICAgICAgdGhpcy5vcmllbnRhdGlvbkNoYW5nZS5lbWl0KHJlc3VsdC5tYXRjaGVzKTtcbiAgICAgICAgICAgICAgICAvLyDmlrnlkJHororljJbmmYLph43mlrDoqIjnrpdcbiAgICAgICAgICAgICAgICB0aGlzLnVwZGF0ZUJvdW5kYXJ5Q2FsY3VsYXRpb25zKCk7XG4gICAgICAgICAgICB9KTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiDnm6Pmjqfoppblj6PlpKflsI/ororljJZcbiAgICAgKi9cbiAgICBzZXR1cFJlc2l6ZU9ic2VydmVyKCkge1xuICAgICAgICBmcm9tRXZlbnQod2luZG93LCAncmVzaXplJylcbiAgICAgICAgICAgIC5waXBlKFxuICAgICAgICAgICAgICAgIC8vIGRlYm91bmNlVGltZSgxNTApLFxuICAgICAgICAgICAgICAgIHRha2VVbnRpbCh0aGlzLmRlc3Ryb3kkKVxuICAgICAgICAgICAgKVxuICAgICAgICAgICAgLnN1YnNjcmliZSgoKSA9PiB7XG4gICAgICAgICAgICAgICAgdGhpcy51cGRhdGVCb3VuZGFyeUNhbGN1bGF0aW9ucygpO1xuICAgICAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICog5pu05paw6YKK55WM6KiI566X77yI5pSv5o+0IENocm9tZSA3NO+8jOS9v+eUqCBKYXZhU2NyaXB0IOaooeaTrCBtaW4g5Ye95pW477yJXG4gICAgICog6Kit5a6aIC0tYm91bmRhcnktd2lkdGgsIC0tYm91bmRhcnktaGVpZ2h0LCAtLXB4MnZ3LXJhdGlvXG4gICAgICovXG4gICAgdXBkYXRlQm91bmRhcnlDYWxjdWxhdGlvbnMoKSB7XG4gICAgICAgIGNvbnN0IGhvc3RFbGVtZW50ID0gdGhpcy5lbGVtZW50UmVmLm5hdGl2ZUVsZW1lbnQgYXMgSFRNTEVsZW1lbnQ7XG4gICAgICAgIGNvbnN0IGNvbXB1dGVkU3R5bGUgPSBnZXRDb21wdXRlZFN0eWxlKGhvc3RFbGVtZW50KTtcblxuICAgICAgICAvLyDoroDlj5bln7rnpI4gQ1NTIOiuiuaVuO+8iOmAmeS6m+aYr+e0lOaVuOWAvO+8jOS4jeaYryBjYWxjKCkg6KGo6YGU5byP77yJXG4gICAgICAgIGNvbnN0IHZ3MTAwID0gcGFyc2VGbG9hdChjb21wdXRlZFN0eWxlLmdldFByb3BlcnR5VmFsdWUoJy0tdncxMDAnKSkgfHwgd2luZG93LmlubmVyV2lkdGg7XG4gICAgICAgIGNvbnN0IHZoMTAwID0gcGFyc2VGbG9hdChjb21wdXRlZFN0eWxlLmdldFByb3BlcnR5VmFsdWUoJy0tdmgxMDAnKSkgfHwgd2luZG93LmlubmVySGVpZ2h0O1xuICAgICAgICBjb25zdCBkZXNpZ25XaWR0aCA9IHBhcnNlRmxvYXQoY29tcHV0ZWRTdHlsZS5nZXRQcm9wZXJ0eVZhbHVlKCctLWRlc2lnbi13aWR0aCcpKTtcbiAgICAgICAgY29uc3QgZGVzaWduSGVpZ2h0ID0gcGFyc2VGbG9hdChjb21wdXRlZFN0eWxlLmdldFByb3BlcnR5VmFsdWUoJy0tZGVzaWduLWhlaWdodCcpKTtcblxuICAgICAgICBjb25zb2xlLmxvZygnZGVzaWduV2lkdGgnLCBkZXNpZ25XaWR0aCk7XG5cbiAgICAgICAgLy8g5qqi5p+l5Z+656SO6K6K5pW45piv5ZCm5rqW5YKZ5aW9XG4gICAgICAgIGlmIChpc05hTihkZXNpZ25XaWR0aCkgfHwgaXNOYU4oZGVzaWduSGVpZ2h0KSB8fCBkZXNpZ25XaWR0aCA9PT0gMCB8fCBkZXNpZ25IZWlnaHQgPT09IDApIHtcbiAgICAgICAgICAgIHJldHVybjsgLy8gQ1NTIOiuiuaVuOmChOaykua6luWCmeWlve+8jOi3s+mBjlxuICAgICAgICB9XG5cbiAgICAgICAgLy8g5ZyoIEphdmFTY3JpcHQg5Lit6KiI566X5omA5pyJ5YC877yI6YG/5YWN6K6A5Y+WIGNhbGMoKSDooajpgZTlvI/vvIlcbiAgICAgICAgY29uc3QgbGFuZHNjYXBlUmF0aW8gPSBkZXNpZ25XaWR0aCAvIGRlc2lnbkhlaWdodDtcbiAgICAgICAgY29uc3QgcG9ydHJhaXRSYXRpbyA9IGRlc2lnbkhlaWdodCAvIGRlc2lnbldpZHRoO1xuXG4gICAgICAgIC8vIOagueaTmuaWueWQkeioiOeul+mCiueVjOacgOWkp+WAvFxuICAgICAgICBjb25zdCBpc0xhbmRzY2FwZSA9IHRoaXMuaXNMYW5kc2NhcGU7XG4gICAgICAgIGNvbnN0IGJvdW5kYXJ5TWF4V2lkdGggPSBpc0xhbmRzY2FwZVxuICAgICAgICAgICAgPyB2aDEwMCAqIHBvcnRyYWl0UmF0aW9cbiAgICAgICAgICAgIDogdmgxMDAgKiBsYW5kc2NhcGVSYXRpbztcbiAgICAgICAgY29uc3QgYm91bmRhcnlNYXhIZWlnaHQgPSBpc0xhbmRzY2FwZVxuICAgICAgICAgICAgPyB2dzEwMCAqIGxhbmRzY2FwZVJhdGlvXG4gICAgICAgICAgICA6IHZ3MTAwICogcG9ydHJhaXRSYXRpbztcblxuICAgICAgICAvLyDkvb/nlKggSmF2YVNjcmlwdCBNYXRoLm1pbiDmqKHmk6wgQ1NTIG1pbigpIOWHveaVuFxuICAgICAgICBjb25zdCBib3VuZGFyeVdpZHRoID0gTWF0aC5taW4odncxMDAsIGJvdW5kYXJ5TWF4V2lkdGgpO1xuICAgICAgICBjb25zdCBib3VuZGFyeUhlaWdodCA9IE1hdGgubWluKHZoMTAwLCBib3VuZGFyeU1heEhlaWdodCk7XG5cbiAgICAgICAgLy8g6KiI566X6Kit6KiI5Z+65rqW5ZKMIHB4MnZ3IOavlOS+i1xuICAgICAgICBjb25zdCBkZXNpZ25CYXNlV2lkdGggPSBpc0xhbmRzY2FwZSA/IGRlc2lnbkhlaWdodCA6IGRlc2lnbldpZHRoO1xuICAgICAgICBjb25zdCBweDJ2d1JhdGlvID0gYm91bmRhcnlXaWR0aCAvIGRlc2lnbkJhc2VXaWR0aDtcblxuICAgICAgICAvLyDoqK3lrpogQ1NTIOiuiuaVuO+8iOWQkeW+jOWFvOWuuSArIOeyvueiuueahCByYXRpb++8iVxuICAgICAgICBob3N0RWxlbWVudC5zdHlsZS5zZXRQcm9wZXJ0eSgnLS1ib3VuZGFyeS13aWR0aCcsIGAke2JvdW5kYXJ5V2lkdGh9cHhgKTtcbiAgICAgICAgaG9zdEVsZW1lbnQuc3R5bGUuc2V0UHJvcGVydHkoJy0tYm91bmRhcnktaGVpZ2h0JywgYCR7Ym91bmRhcnlIZWlnaHR9cHhgKTtcbiAgICAgICAgaG9zdEVsZW1lbnQuc3R5bGUuc2V0UHJvcGVydHkoJy0tcHgydnctcmF0aW8nLCBgJHtweDJ2d1JhdGlvfWApO1xuICAgIH1cbn1cbiIsIjxkaXZcbiAgY2xhc3M9XCJib3VuZGFyeS13cmFwcGVyXCJcbiAgW2NsYXNzLmZpeGVkLWNlbnRlcl09XCJpc0ZpeGVkQ2VudGVyXCJcbiAgW2NsYXNzLnBvaW50ZXJFdmVudHNOb25lXT1cInBvaW50ZXJFdmVudHNOb25lXCJcbiAgW3N0eWxlXT1cImZvcndhcmRTdHlsZVwiXG4+XG4gICA8bmctY29udGVudD48L25nLWNvbnRlbnQ+XG48L2Rpdj5cbiJdfQ==
@@ -1,7 +1,7 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Injectable, EventEmitter, inject, Component, Input, Output } from '@angular/core';
2
+ import { Injectable, EventEmitter, inject, ElementRef, Component, Input, Output } from '@angular/core';
3
3
  import { BreakpointObserver } from '@angular/cdk/layout';
4
- import { BehaviorSubject, Subject, takeUntil } from 'rxjs';
4
+ import { BehaviorSubject, Subject, takeUntil, fromEvent } from 'rxjs';
5
5
 
6
6
  class BsBoundaryContextService {
7
7
  constructor() {
@@ -29,6 +29,7 @@ class BsBoundary {
29
29
  this.orientationChange = new EventEmitter();
30
30
  this.breakpointObserver = inject(BreakpointObserver);
31
31
  this.boundaryContext = inject(BsBoundaryContextService);
32
+ this.elementRef = inject(ElementRef);
32
33
  this.destroy$ = new Subject();
33
34
  }
34
35
  get isLandscape() {
@@ -36,6 +37,11 @@ class BsBoundary {
36
37
  }
37
38
  ngOnInit() {
38
39
  this.setupBreakpointObserver();
40
+ this.setupResizeObserver();
41
+ }
42
+ ngAfterViewInit() {
43
+ // 在 view 初始化後計算(確保 CSS 已完全應用)
44
+ this.updateBoundaryCalculations();
39
45
  }
40
46
  ngOnDestroy() {
41
47
  this.destroy$.next();
@@ -43,6 +49,7 @@ class BsBoundary {
43
49
  }
44
50
  /**
45
51
  * 監控方向變化
52
+ * 只在 lock 模式時需要,auto 模式由 CSS 媒體查詢處理
46
53
  */
47
54
  setupBreakpointObserver() {
48
55
  if (this.lockOrientation === 'landscape') {
@@ -55,22 +62,77 @@ class BsBoundary {
55
62
  this.orientationChange.emit(false);
56
63
  return;
57
64
  }
65
+ // Auto 模式:監聽實際的設備方向(用於 orientationChange 事件)
58
66
  this.breakpointObserver
59
67
  .observe('(orientation: landscape)')
60
68
  .pipe(takeUntil(this.destroy$))
61
69
  .subscribe(result => {
62
70
  this.boundaryContext.setIsLandscape(result.matches);
63
71
  this.orientationChange.emit(result.matches);
72
+ // 方向變化時重新計算
73
+ this.updateBoundaryCalculations();
74
+ });
75
+ }
76
+ /**
77
+ * 監控視口大小變化
78
+ */
79
+ setupResizeObserver() {
80
+ fromEvent(window, 'resize')
81
+ .pipe(
82
+ // debounceTime(150),
83
+ takeUntil(this.destroy$))
84
+ .subscribe(() => {
85
+ this.updateBoundaryCalculations();
64
86
  });
65
87
  }
88
+ /**
89
+ * 更新邊界計算(支援 Chrome 74,使用 JavaScript 模擬 min 函數)
90
+ * 設定 --boundary-width, --boundary-height, --px2vw-ratio
91
+ */
92
+ updateBoundaryCalculations() {
93
+ const hostElement = this.elementRef.nativeElement;
94
+ const computedStyle = getComputedStyle(hostElement);
95
+ // 讀取基礎 CSS 變數(這些是純數值,不是 calc() 表達式)
96
+ const vw100 = parseFloat(computedStyle.getPropertyValue('--vw100')) || window.innerWidth;
97
+ const vh100 = parseFloat(computedStyle.getPropertyValue('--vh100')) || window.innerHeight;
98
+ const designWidth = parseFloat(computedStyle.getPropertyValue('--design-width'));
99
+ const designHeight = parseFloat(computedStyle.getPropertyValue('--design-height'));
100
+ console.log('designWidth', designWidth);
101
+ // 檢查基礎變數是否準備好
102
+ if (isNaN(designWidth) || isNaN(designHeight) || designWidth === 0 || designHeight === 0) {
103
+ return; // CSS 變數還沒準備好,跳過
104
+ }
105
+ // 在 JavaScript 中計算所有值(避免讀取 calc() 表達式)
106
+ const landscapeRatio = designWidth / designHeight;
107
+ const portraitRatio = designHeight / designWidth;
108
+ // 根據方向計算邊界最大值
109
+ const isLandscape = this.isLandscape;
110
+ const boundaryMaxWidth = isLandscape
111
+ ? vh100 * portraitRatio
112
+ : vh100 * landscapeRatio;
113
+ const boundaryMaxHeight = isLandscape
114
+ ? vw100 * landscapeRatio
115
+ : vw100 * portraitRatio;
116
+ // 使用 JavaScript Math.min 模擬 CSS min() 函數
117
+ const boundaryWidth = Math.min(vw100, boundaryMaxWidth);
118
+ const boundaryHeight = Math.min(vh100, boundaryMaxHeight);
119
+ // 計算設計基準和 px2vw 比例
120
+ const designBaseWidth = isLandscape ? designHeight : designWidth;
121
+ const px2vwRatio = boundaryWidth / designBaseWidth;
122
+ // 設定 CSS 變數(向後兼容 + 精確的 ratio)
123
+ hostElement.style.setProperty('--boundary-width', `${boundaryWidth}px`);
124
+ hostElement.style.setProperty('--boundary-height', `${boundaryHeight}px`);
125
+ hostElement.style.setProperty('--px2vw-ratio', `${px2vwRatio}`);
126
+ }
66
127
  }
67
128
  BsBoundary.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: BsBoundary, deps: [], target: i0.ɵɵFactoryTarget.Component });
68
- BsBoundary.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: BsBoundary, isStandalone: true, selector: "bs-boundary", inputs: { isFixedCenter: "isFixedCenter", pointerEventsNone: "pointerEventsNone", lockOrientation: "lockOrientation", forwardStyle: "forwardStyle" }, outputs: { orientationChange: "orientationChange" }, host: { properties: { "class.landscape": "isLandscape" } }, providers: [BsBoundaryContextService], ngImport: i0, template: "<div\n class=\"boundary-wrapper\"\n [class.fixed-center]=\"isFixedCenter\"\n [class.pointerEventsNone]=\"pointerEventsNone\"\n [style]=\"forwardStyle\"\n>\n <ng-content></ng-content>\n</div>\n", styles: ["@charset \"UTF-8\";:host{--landscape-ratio: calc(var(--design-width) / var(--design-height));--portrait-ratio: calc(var(--design-height) / var(--design-width))}.boundary-wrapper{width:100%;height:100%;max-width:var(--boundary-width);max-height:var(--boundary-height)}.boundary-wrapper.pointerEventsNone{pointer-events:none}.boundary-wrapper{--active-ratio: var(--landscape-ratio);--active-inv-ratio: var(--portrait-ratio);--boundary-width: var(--vw100);--boundary-height: calc(var(--boundary-width) * var(--active-inv-ratio));--px2vw-ratio: calc(var(--boundary-width) / var(--design-base-width));--design-base-width: var(--design-width);--design-base-height: var(--design-height)}@media (min-aspect-ratio: 16/9){.boundary-wrapper{--boundary-height: var(--vh100);--boundary-width: calc(var(--boundary-height) * var(--active-ratio))}}.boundary-wrapper.landscape{--active-ratio: var(--portrait-ratio);--active-inv-ratio: var(--landscape-ratio);--design-base-width: var(--design-height);--design-base-height: var(--design-width);--boundary-width: var(--vw100);--boundary-height: calc(var(--boundary-width) * var(--active-inv-ratio))}@media (min-aspect-ratio: 9/16){.boundary-wrapper.landscape{--boundary-height: var(--vh100);--boundary-width: calc(var(--boundary-height) * var(--active-ratio))}}.fixed-center{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);transform-origin:center;z-index:99}\n"] });
129
+ BsBoundary.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: BsBoundary, isStandalone: true, selector: "bs-boundary", inputs: { isFixedCenter: "isFixedCenter", pointerEventsNone: "pointerEventsNone", lockOrientation: "lockOrientation", forwardStyle: "forwardStyle" }, outputs: { orientationChange: "orientationChange" }, host: { properties: { "class.landscape": "isLandscape && lockOrientation !== \"auto\"", "class.portrait": "!isLandscape && lockOrientation !== \"auto\"" } }, providers: [BsBoundaryContextService], ngImport: i0, template: "<div\n class=\"boundary-wrapper\"\n [class.fixed-center]=\"isFixedCenter\"\n [class.pointerEventsNone]=\"pointerEventsNone\"\n [style]=\"forwardStyle\"\n>\n <ng-content></ng-content>\n</div>\n", styles: [":host{--landscape-ratio: calc(var(--design-width) / var(--design-height));--portrait-ratio: calc(var(--design-height) / var(--design-width));--boundary-max-width: calc(var(--vh100) * var(--landscape-ratio));--boundary-max-height: calc(var(--vw100) * var(--portrait-ratio));--design-base-width: var(--design-width);--design-base-height: var(--design-height);--px2vw-ratio: calc(var(--vw100) / var(--design-base-width))}.boundary-wrapper{width:var(--vw100);height:var(--vh100);max-width:var(--boundary-max-width);max-height:var(--boundary-max-height)}.boundary-wrapper.pointerEventsNone{pointer-events:none}:host.landscape{--boundary-max-width: calc(var(--vh100) * var(--portrait-ratio));--boundary-max-height: calc(var(--vw100) * var(--landscape-ratio));--design-base-width: var(--design-height);--design-base-height: var(--design-width);--px2vw-ratio: calc(var(--vh100) * var(--portrait-ratio) / var(--design-base-width))}@media (orientation: landscape){:host:not(.landscape):not(.portrait){--boundary-max-width: calc(var(--vh100) * var(--portrait-ratio));--boundary-max-height: calc(var(--vw100) * var(--landscape-ratio));--design-base-width: var(--design-height);--design-base-height: var(--design-width);--px2vw-ratio: calc(var(--vh100) * var(--portrait-ratio) / var(--design-base-width))}}.fixed-center{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);transform-origin:center;z-index:99}\n"] });
69
130
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: BsBoundary, decorators: [{
70
131
  type: Component,
71
132
  args: [{ selector: 'bs-boundary', standalone: true, providers: [BsBoundaryContextService], host: {
72
- '[class.landscape]': 'isLandscape'
73
- }, template: "<div\n class=\"boundary-wrapper\"\n [class.fixed-center]=\"isFixedCenter\"\n [class.pointerEventsNone]=\"pointerEventsNone\"\n [style]=\"forwardStyle\"\n>\n <ng-content></ng-content>\n</div>\n", styles: ["@charset \"UTF-8\";:host{--landscape-ratio: calc(var(--design-width) / var(--design-height));--portrait-ratio: calc(var(--design-height) / var(--design-width))}.boundary-wrapper{width:100%;height:100%;max-width:var(--boundary-width);max-height:var(--boundary-height)}.boundary-wrapper.pointerEventsNone{pointer-events:none}.boundary-wrapper{--active-ratio: var(--landscape-ratio);--active-inv-ratio: var(--portrait-ratio);--boundary-width: var(--vw100);--boundary-height: calc(var(--boundary-width) * var(--active-inv-ratio));--px2vw-ratio: calc(var(--boundary-width) / var(--design-base-width));--design-base-width: var(--design-width);--design-base-height: var(--design-height)}@media (min-aspect-ratio: 16/9){.boundary-wrapper{--boundary-height: var(--vh100);--boundary-width: calc(var(--boundary-height) * var(--active-ratio))}}.boundary-wrapper.landscape{--active-ratio: var(--portrait-ratio);--active-inv-ratio: var(--landscape-ratio);--design-base-width: var(--design-height);--design-base-height: var(--design-width);--boundary-width: var(--vw100);--boundary-height: calc(var(--boundary-width) * var(--active-inv-ratio))}@media (min-aspect-ratio: 9/16){.boundary-wrapper.landscape{--boundary-height: var(--vh100);--boundary-width: calc(var(--boundary-height) * var(--active-ratio))}}.fixed-center{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);transform-origin:center;z-index:99}\n"] }]
133
+ '[class.landscape]': 'isLandscape && lockOrientation !== "auto"',
134
+ '[class.portrait]': '!isLandscape && lockOrientation !== "auto"'
135
+ }, template: "<div\n class=\"boundary-wrapper\"\n [class.fixed-center]=\"isFixedCenter\"\n [class.pointerEventsNone]=\"pointerEventsNone\"\n [style]=\"forwardStyle\"\n>\n <ng-content></ng-content>\n</div>\n", styles: [":host{--landscape-ratio: calc(var(--design-width) / var(--design-height));--portrait-ratio: calc(var(--design-height) / var(--design-width));--boundary-max-width: calc(var(--vh100) * var(--landscape-ratio));--boundary-max-height: calc(var(--vw100) * var(--portrait-ratio));--design-base-width: var(--design-width);--design-base-height: var(--design-height);--px2vw-ratio: calc(var(--vw100) / var(--design-base-width))}.boundary-wrapper{width:var(--vw100);height:var(--vh100);max-width:var(--boundary-max-width);max-height:var(--boundary-max-height)}.boundary-wrapper.pointerEventsNone{pointer-events:none}:host.landscape{--boundary-max-width: calc(var(--vh100) * var(--portrait-ratio));--boundary-max-height: calc(var(--vw100) * var(--landscape-ratio));--design-base-width: var(--design-height);--design-base-height: var(--design-width);--px2vw-ratio: calc(var(--vh100) * var(--portrait-ratio) / var(--design-base-width))}@media (orientation: landscape){:host:not(.landscape):not(.portrait){--boundary-max-width: calc(var(--vh100) * var(--portrait-ratio));--boundary-max-height: calc(var(--vw100) * var(--landscape-ratio));--design-base-width: var(--design-height);--design-base-height: var(--design-width);--px2vw-ratio: calc(var(--vh100) * var(--portrait-ratio) / var(--design-base-width))}}.fixed-center{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);transform-origin:center;z-index:99}\n"] }]
74
136
  }], propDecorators: { isFixedCenter: [{
75
137
  type: Input
76
138
  }], pointerEventsNone: [{
@@ -1 +1 @@
1
- {"version":3,"file":"bitstack-ng-boundary.mjs","sources":["../../../projects/bitstack-ng-boundary/src/lib/bs-boundary-context.service.ts","../../../projects/bitstack-ng-boundary/src/lib/bs-boundary.ts","../../../projects/bitstack-ng-boundary/src/lib/template.html","../../../projects/bitstack-ng-boundary/src/public-api.ts","../../../projects/bitstack-ng-boundary/src/bitstack-ng-boundary.ts"],"sourcesContent":["import { Injectable } from '@angular/core';\nimport { BehaviorSubject } from 'rxjs';\n\n@Injectable()\nexport class BsBoundaryContextService {\n private _isLandscape = new BehaviorSubject<boolean>(false);\n\n readonly isLandscape$ = this._isLandscape.asObservable();\n\n setIsLandscape(value: boolean): void {\n this._isLandscape.next(value);\n }\n\n getIsLandscape(): boolean {\n return this._isLandscape.value;\n }\n}\n","import {\n Component,\n EventEmitter,\n inject,\n Input, OnDestroy,\n OnInit,\n Output,\n} from '@angular/core';\nimport {TOrientationMode} from './model';\nimport {BreakpointObserver} from '@angular/cdk/layout';\nimport {Subject, takeUntil} from 'rxjs';\nimport {BsBoundaryContextService} from './bs-boundary-context.service';\n\n@Component({\n selector: 'bs-boundary',\n templateUrl: 'template.html',\n standalone: true,\n styleUrls: ['./styles.scss'],\n providers: [BsBoundaryContextService],\n host: {\n '[class.landscape]': 'isLandscape'\n }\n})\nexport class BsBoundary implements OnInit, OnDestroy {\n\n @Input() isFixedCenter?: boolean = false;\n @Input() pointerEventsNone?: boolean = false;\n @Input() lockOrientation?: TOrientationMode = 'auto';\n @Input() forwardStyle?: Record<string, string>;\n\n @Output() orientationChange = new EventEmitter<boolean>();\n\n private breakpointObserver = inject(BreakpointObserver);\n private boundaryContext = inject(BsBoundaryContextService);\n private destroy$ = new Subject<void>();\n\n get isLandscape(): boolean {\n return this.boundaryContext.getIsLandscape();\n }\n\n ngOnInit(): void {\n this.setupBreakpointObserver();\n }\n\n ngOnDestroy(): void {\n this.destroy$.next();\n this.destroy$.complete();\n }\n\n /**\n * 監控方向變化\n */\n setupBreakpointObserver() {\n if (this.lockOrientation === 'landscape') {\n this.boundaryContext.setIsLandscape(true);\n this.orientationChange.emit(true);\n return;\n }\n\n if (this.lockOrientation === 'portrait') {\n this.boundaryContext.setIsLandscape(false);\n this.orientationChange.emit(false);\n return;\n }\n\n this.breakpointObserver\n .observe('(orientation: landscape)')\n .pipe(takeUntil(this.destroy$))\n .subscribe(result => {\n this.boundaryContext.setIsLandscape(result.matches);\n this.orientationChange.emit(result.matches);\n });\n }\n}\n","<div\n class=\"boundary-wrapper\"\n [class.fixed-center]=\"isFixedCenter\"\n [class.pointerEventsNone]=\"pointerEventsNone\"\n [style]=\"forwardStyle\"\n>\n <ng-content></ng-content>\n</div>\n","/*\n * Public API Surface of @bitstack/ng-boundary\n */\n\nexport * from './lib/bs-boundary';\nexport * from './lib/bs-boundary-context.service';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"bitstack-ng-boundary.mjs","sources":["../../../projects/bitstack-ng-boundary/src/lib/bs-boundary-context.service.ts","../../../projects/bitstack-ng-boundary/src/lib/bs-boundary.ts","../../../projects/bitstack-ng-boundary/src/lib/template.html","../../../projects/bitstack-ng-boundary/src/public-api.ts","../../../projects/bitstack-ng-boundary/src/bitstack-ng-boundary.ts"],"sourcesContent":["import { Injectable } from '@angular/core';\nimport { BehaviorSubject } from 'rxjs';\n\n@Injectable()\nexport class BsBoundaryContextService {\n private _isLandscape = new BehaviorSubject<boolean>(false);\n\n readonly isLandscape$ = this._isLandscape.asObservable();\n\n setIsLandscape(value: boolean): void {\n this._isLandscape.next(value);\n }\n\n getIsLandscape(): boolean {\n return this._isLandscape.value;\n }\n}\n","import {\n AfterViewInit,\n Component,\n ElementRef,\n EventEmitter,\n inject,\n Input, OnDestroy,\n OnInit,\n Output,\n} from '@angular/core';\nimport {TOrientationMode} from './model';\nimport {BreakpointObserver} from '@angular/cdk/layout';\nimport {debounceTime, fromEvent, Subject, takeUntil} from 'rxjs';\nimport {BsBoundaryContextService} from './bs-boundary-context.service';\n\n@Component({\n selector: 'bs-boundary',\n templateUrl: 'template.html',\n standalone: true,\n styleUrls: ['./styles.scss'],\n providers: [BsBoundaryContextService],\n host: {\n '[class.landscape]': 'isLandscape && lockOrientation !== \"auto\"',\n '[class.portrait]': '!isLandscape && lockOrientation !== \"auto\"'\n }\n})\nexport class BsBoundary implements OnInit, AfterViewInit, OnDestroy {\n\n @Input() isFixedCenter?: boolean = false;\n @Input() pointerEventsNone?: boolean = false;\n @Input() lockOrientation?: TOrientationMode = 'auto';\n @Input() forwardStyle?: Record<string, string>;\n\n @Output() orientationChange = new EventEmitter<boolean>();\n\n private breakpointObserver = inject(BreakpointObserver);\n private boundaryContext = inject(BsBoundaryContextService);\n private elementRef = inject(ElementRef);\n private destroy$ = new Subject<void>();\n\n get isLandscape(): boolean {\n return this.boundaryContext.getIsLandscape();\n }\n\n ngOnInit(): void {\n this.setupBreakpointObserver();\n this.setupResizeObserver();\n }\n\n ngAfterViewInit(): void {\n // 在 view 初始化後計算(確保 CSS 已完全應用)\n this.updateBoundaryCalculations();\n }\n\n ngOnDestroy(): void {\n this.destroy$.next();\n this.destroy$.complete();\n }\n\n /**\n * 監控方向變化\n * 只在 lock 模式時需要,auto 模式由 CSS 媒體查詢處理\n */\n setupBreakpointObserver() {\n if (this.lockOrientation === 'landscape') {\n this.boundaryContext.setIsLandscape(true);\n this.orientationChange.emit(true);\n return;\n }\n\n if (this.lockOrientation === 'portrait') {\n this.boundaryContext.setIsLandscape(false);\n this.orientationChange.emit(false);\n return;\n }\n\n // Auto 模式:監聽實際的設備方向(用於 orientationChange 事件)\n this.breakpointObserver\n .observe('(orientation: landscape)')\n .pipe(takeUntil(this.destroy$))\n .subscribe(result => {\n this.boundaryContext.setIsLandscape(result.matches);\n this.orientationChange.emit(result.matches);\n // 方向變化時重新計算\n this.updateBoundaryCalculations();\n });\n }\n\n /**\n * 監控視口大小變化\n */\n setupResizeObserver() {\n fromEvent(window, 'resize')\n .pipe(\n // debounceTime(150),\n takeUntil(this.destroy$)\n )\n .subscribe(() => {\n this.updateBoundaryCalculations();\n });\n }\n\n /**\n * 更新邊界計算(支援 Chrome 74,使用 JavaScript 模擬 min 函數)\n * 設定 --boundary-width, --boundary-height, --px2vw-ratio\n */\n updateBoundaryCalculations() {\n const hostElement = this.elementRef.nativeElement as HTMLElement;\n const computedStyle = getComputedStyle(hostElement);\n\n // 讀取基礎 CSS 變數(這些是純數值,不是 calc() 表達式)\n const vw100 = parseFloat(computedStyle.getPropertyValue('--vw100')) || window.innerWidth;\n const vh100 = parseFloat(computedStyle.getPropertyValue('--vh100')) || window.innerHeight;\n const designWidth = parseFloat(computedStyle.getPropertyValue('--design-width'));\n const designHeight = parseFloat(computedStyle.getPropertyValue('--design-height'));\n\n console.log('designWidth', designWidth);\n\n // 檢查基礎變數是否準備好\n if (isNaN(designWidth) || isNaN(designHeight) || designWidth === 0 || designHeight === 0) {\n return; // CSS 變數還沒準備好,跳過\n }\n\n // 在 JavaScript 中計算所有值(避免讀取 calc() 表達式)\n const landscapeRatio = designWidth / designHeight;\n const portraitRatio = designHeight / designWidth;\n\n // 根據方向計算邊界最大值\n const isLandscape = this.isLandscape;\n const boundaryMaxWidth = isLandscape\n ? vh100 * portraitRatio\n : vh100 * landscapeRatio;\n const boundaryMaxHeight = isLandscape\n ? vw100 * landscapeRatio\n : vw100 * portraitRatio;\n\n // 使用 JavaScript Math.min 模擬 CSS min() 函數\n const boundaryWidth = Math.min(vw100, boundaryMaxWidth);\n const boundaryHeight = Math.min(vh100, boundaryMaxHeight);\n\n // 計算設計基準和 px2vw 比例\n const designBaseWidth = isLandscape ? designHeight : designWidth;\n const px2vwRatio = boundaryWidth / designBaseWidth;\n\n // 設定 CSS 變數(向後兼容 + 精確的 ratio)\n hostElement.style.setProperty('--boundary-width', `${boundaryWidth}px`);\n hostElement.style.setProperty('--boundary-height', `${boundaryHeight}px`);\n hostElement.style.setProperty('--px2vw-ratio', `${px2vwRatio}`);\n }\n}\n","<div\n class=\"boundary-wrapper\"\n [class.fixed-center]=\"isFixedCenter\"\n [class.pointerEventsNone]=\"pointerEventsNone\"\n [style]=\"forwardStyle\"\n>\n <ng-content></ng-content>\n</div>\n","/*\n * Public API Surface of @bitstack/ng-boundary\n */\n\nexport * from './lib/bs-boundary';\nexport * from './lib/bs-boundary-context.service';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Injectable, EventEmitter, inject, Component, Input, Output } from '@angular/core';
2
+ import { Injectable, EventEmitter, inject, ElementRef, Component, Input, Output } from '@angular/core';
3
3
  import { BreakpointObserver } from '@angular/cdk/layout';
4
- import { BehaviorSubject, Subject, takeUntil } from 'rxjs';
4
+ import { BehaviorSubject, Subject, takeUntil, fromEvent } from 'rxjs';
5
5
 
6
6
  class BsBoundaryContextService {
7
7
  constructor() {
@@ -29,6 +29,7 @@ class BsBoundary {
29
29
  this.orientationChange = new EventEmitter();
30
30
  this.breakpointObserver = inject(BreakpointObserver);
31
31
  this.boundaryContext = inject(BsBoundaryContextService);
32
+ this.elementRef = inject(ElementRef);
32
33
  this.destroy$ = new Subject();
33
34
  }
34
35
  get isLandscape() {
@@ -36,6 +37,11 @@ class BsBoundary {
36
37
  }
37
38
  ngOnInit() {
38
39
  this.setupBreakpointObserver();
40
+ this.setupResizeObserver();
41
+ }
42
+ ngAfterViewInit() {
43
+ // 在 view 初始化後計算(確保 CSS 已完全應用)
44
+ this.updateBoundaryCalculations();
39
45
  }
40
46
  ngOnDestroy() {
41
47
  this.destroy$.next();
@@ -43,6 +49,7 @@ class BsBoundary {
43
49
  }
44
50
  /**
45
51
  * 監控方向變化
52
+ * 只在 lock 模式時需要,auto 模式由 CSS 媒體查詢處理
46
53
  */
47
54
  setupBreakpointObserver() {
48
55
  if (this.lockOrientation === 'landscape') {
@@ -55,22 +62,77 @@ class BsBoundary {
55
62
  this.orientationChange.emit(false);
56
63
  return;
57
64
  }
65
+ // Auto 模式:監聽實際的設備方向(用於 orientationChange 事件)
58
66
  this.breakpointObserver
59
67
  .observe('(orientation: landscape)')
60
68
  .pipe(takeUntil(this.destroy$))
61
69
  .subscribe(result => {
62
70
  this.boundaryContext.setIsLandscape(result.matches);
63
71
  this.orientationChange.emit(result.matches);
72
+ // 方向變化時重新計算
73
+ this.updateBoundaryCalculations();
74
+ });
75
+ }
76
+ /**
77
+ * 監控視口大小變化
78
+ */
79
+ setupResizeObserver() {
80
+ fromEvent(window, 'resize')
81
+ .pipe(
82
+ // debounceTime(150),
83
+ takeUntil(this.destroy$))
84
+ .subscribe(() => {
85
+ this.updateBoundaryCalculations();
64
86
  });
65
87
  }
88
+ /**
89
+ * 更新邊界計算(支援 Chrome 74,使用 JavaScript 模擬 min 函數)
90
+ * 設定 --boundary-width, --boundary-height, --px2vw-ratio
91
+ */
92
+ updateBoundaryCalculations() {
93
+ const hostElement = this.elementRef.nativeElement;
94
+ const computedStyle = getComputedStyle(hostElement);
95
+ // 讀取基礎 CSS 變數(這些是純數值,不是 calc() 表達式)
96
+ const vw100 = parseFloat(computedStyle.getPropertyValue('--vw100')) || window.innerWidth;
97
+ const vh100 = parseFloat(computedStyle.getPropertyValue('--vh100')) || window.innerHeight;
98
+ const designWidth = parseFloat(computedStyle.getPropertyValue('--design-width'));
99
+ const designHeight = parseFloat(computedStyle.getPropertyValue('--design-height'));
100
+ console.log('designWidth', designWidth);
101
+ // 檢查基礎變數是否準備好
102
+ if (isNaN(designWidth) || isNaN(designHeight) || designWidth === 0 || designHeight === 0) {
103
+ return; // CSS 變數還沒準備好,跳過
104
+ }
105
+ // 在 JavaScript 中計算所有值(避免讀取 calc() 表達式)
106
+ const landscapeRatio = designWidth / designHeight;
107
+ const portraitRatio = designHeight / designWidth;
108
+ // 根據方向計算邊界最大值
109
+ const isLandscape = this.isLandscape;
110
+ const boundaryMaxWidth = isLandscape
111
+ ? vh100 * portraitRatio
112
+ : vh100 * landscapeRatio;
113
+ const boundaryMaxHeight = isLandscape
114
+ ? vw100 * landscapeRatio
115
+ : vw100 * portraitRatio;
116
+ // 使用 JavaScript Math.min 模擬 CSS min() 函數
117
+ const boundaryWidth = Math.min(vw100, boundaryMaxWidth);
118
+ const boundaryHeight = Math.min(vh100, boundaryMaxHeight);
119
+ // 計算設計基準和 px2vw 比例
120
+ const designBaseWidth = isLandscape ? designHeight : designWidth;
121
+ const px2vwRatio = boundaryWidth / designBaseWidth;
122
+ // 設定 CSS 變數(向後兼容 + 精確的 ratio)
123
+ hostElement.style.setProperty('--boundary-width', `${boundaryWidth}px`);
124
+ hostElement.style.setProperty('--boundary-height', `${boundaryHeight}px`);
125
+ hostElement.style.setProperty('--px2vw-ratio', `${px2vwRatio}`);
126
+ }
66
127
  }
67
128
  BsBoundary.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: BsBoundary, deps: [], target: i0.ɵɵFactoryTarget.Component });
68
- BsBoundary.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: BsBoundary, isStandalone: true, selector: "bs-boundary", inputs: { isFixedCenter: "isFixedCenter", pointerEventsNone: "pointerEventsNone", lockOrientation: "lockOrientation", forwardStyle: "forwardStyle" }, outputs: { orientationChange: "orientationChange" }, host: { properties: { "class.landscape": "isLandscape" } }, providers: [BsBoundaryContextService], ngImport: i0, template: "<div\n class=\"boundary-wrapper\"\n [class.fixed-center]=\"isFixedCenter\"\n [class.pointerEventsNone]=\"pointerEventsNone\"\n [style]=\"forwardStyle\"\n>\n <ng-content></ng-content>\n</div>\n", styles: ["@charset \"UTF-8\";:host{--landscape-ratio: calc(var(--design-width) / var(--design-height));--portrait-ratio: calc(var(--design-height) / var(--design-width))}.boundary-wrapper{width:100%;height:100%;max-width:var(--boundary-width);max-height:var(--boundary-height)}.boundary-wrapper.pointerEventsNone{pointer-events:none}.boundary-wrapper{--active-ratio: var(--landscape-ratio);--active-inv-ratio: var(--portrait-ratio);--boundary-width: var(--vw100);--boundary-height: calc(var(--boundary-width) * var(--active-inv-ratio));--px2vw-ratio: calc(var(--boundary-width) / var(--design-base-width));--design-base-width: var(--design-width);--design-base-height: var(--design-height)}@media (min-aspect-ratio: 16/9){.boundary-wrapper{--boundary-height: var(--vh100);--boundary-width: calc(var(--boundary-height) * var(--active-ratio))}}.boundary-wrapper.landscape{--active-ratio: var(--portrait-ratio);--active-inv-ratio: var(--landscape-ratio);--design-base-width: var(--design-height);--design-base-height: var(--design-width);--boundary-width: var(--vw100);--boundary-height: calc(var(--boundary-width) * var(--active-inv-ratio))}@media (min-aspect-ratio: 9/16){.boundary-wrapper.landscape{--boundary-height: var(--vh100);--boundary-width: calc(var(--boundary-height) * var(--active-ratio))}}.fixed-center{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);transform-origin:center;z-index:99}\n"] });
129
+ BsBoundary.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: BsBoundary, isStandalone: true, selector: "bs-boundary", inputs: { isFixedCenter: "isFixedCenter", pointerEventsNone: "pointerEventsNone", lockOrientation: "lockOrientation", forwardStyle: "forwardStyle" }, outputs: { orientationChange: "orientationChange" }, host: { properties: { "class.landscape": "isLandscape && lockOrientation !== \"auto\"", "class.portrait": "!isLandscape && lockOrientation !== \"auto\"" } }, providers: [BsBoundaryContextService], ngImport: i0, template: "<div\n class=\"boundary-wrapper\"\n [class.fixed-center]=\"isFixedCenter\"\n [class.pointerEventsNone]=\"pointerEventsNone\"\n [style]=\"forwardStyle\"\n>\n <ng-content></ng-content>\n</div>\n", styles: [":host{--landscape-ratio: calc(var(--design-width) / var(--design-height));--portrait-ratio: calc(var(--design-height) / var(--design-width));--boundary-max-width: calc(var(--vh100) * var(--landscape-ratio));--boundary-max-height: calc(var(--vw100) * var(--portrait-ratio));--design-base-width: var(--design-width);--design-base-height: var(--design-height);--px2vw-ratio: calc(var(--vw100) / var(--design-base-width))}.boundary-wrapper{width:var(--vw100);height:var(--vh100);max-width:var(--boundary-max-width);max-height:var(--boundary-max-height)}.boundary-wrapper.pointerEventsNone{pointer-events:none}:host.landscape{--boundary-max-width: calc(var(--vh100) * var(--portrait-ratio));--boundary-max-height: calc(var(--vw100) * var(--landscape-ratio));--design-base-width: var(--design-height);--design-base-height: var(--design-width);--px2vw-ratio: calc(var(--vh100) * var(--portrait-ratio) / var(--design-base-width))}@media (orientation: landscape){:host:not(.landscape):not(.portrait){--boundary-max-width: calc(var(--vh100) * var(--portrait-ratio));--boundary-max-height: calc(var(--vw100) * var(--landscape-ratio));--design-base-width: var(--design-height);--design-base-height: var(--design-width);--px2vw-ratio: calc(var(--vh100) * var(--portrait-ratio) / var(--design-base-width))}}.fixed-center{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);transform-origin:center;z-index:99}\n"] });
69
130
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: BsBoundary, decorators: [{
70
131
  type: Component,
71
132
  args: [{ selector: 'bs-boundary', standalone: true, providers: [BsBoundaryContextService], host: {
72
- '[class.landscape]': 'isLandscape'
73
- }, template: "<div\n class=\"boundary-wrapper\"\n [class.fixed-center]=\"isFixedCenter\"\n [class.pointerEventsNone]=\"pointerEventsNone\"\n [style]=\"forwardStyle\"\n>\n <ng-content></ng-content>\n</div>\n", styles: ["@charset \"UTF-8\";:host{--landscape-ratio: calc(var(--design-width) / var(--design-height));--portrait-ratio: calc(var(--design-height) / var(--design-width))}.boundary-wrapper{width:100%;height:100%;max-width:var(--boundary-width);max-height:var(--boundary-height)}.boundary-wrapper.pointerEventsNone{pointer-events:none}.boundary-wrapper{--active-ratio: var(--landscape-ratio);--active-inv-ratio: var(--portrait-ratio);--boundary-width: var(--vw100);--boundary-height: calc(var(--boundary-width) * var(--active-inv-ratio));--px2vw-ratio: calc(var(--boundary-width) / var(--design-base-width));--design-base-width: var(--design-width);--design-base-height: var(--design-height)}@media (min-aspect-ratio: 16/9){.boundary-wrapper{--boundary-height: var(--vh100);--boundary-width: calc(var(--boundary-height) * var(--active-ratio))}}.boundary-wrapper.landscape{--active-ratio: var(--portrait-ratio);--active-inv-ratio: var(--landscape-ratio);--design-base-width: var(--design-height);--design-base-height: var(--design-width);--boundary-width: var(--vw100);--boundary-height: calc(var(--boundary-width) * var(--active-inv-ratio))}@media (min-aspect-ratio: 9/16){.boundary-wrapper.landscape{--boundary-height: var(--vh100);--boundary-width: calc(var(--boundary-height) * var(--active-ratio))}}.fixed-center{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);transform-origin:center;z-index:99}\n"] }]
133
+ '[class.landscape]': 'isLandscape && lockOrientation !== "auto"',
134
+ '[class.portrait]': '!isLandscape && lockOrientation !== "auto"'
135
+ }, template: "<div\n class=\"boundary-wrapper\"\n [class.fixed-center]=\"isFixedCenter\"\n [class.pointerEventsNone]=\"pointerEventsNone\"\n [style]=\"forwardStyle\"\n>\n <ng-content></ng-content>\n</div>\n", styles: [":host{--landscape-ratio: calc(var(--design-width) / var(--design-height));--portrait-ratio: calc(var(--design-height) / var(--design-width));--boundary-max-width: calc(var(--vh100) * var(--landscape-ratio));--boundary-max-height: calc(var(--vw100) * var(--portrait-ratio));--design-base-width: var(--design-width);--design-base-height: var(--design-height);--px2vw-ratio: calc(var(--vw100) / var(--design-base-width))}.boundary-wrapper{width:var(--vw100);height:var(--vh100);max-width:var(--boundary-max-width);max-height:var(--boundary-max-height)}.boundary-wrapper.pointerEventsNone{pointer-events:none}:host.landscape{--boundary-max-width: calc(var(--vh100) * var(--portrait-ratio));--boundary-max-height: calc(var(--vw100) * var(--landscape-ratio));--design-base-width: var(--design-height);--design-base-height: var(--design-width);--px2vw-ratio: calc(var(--vh100) * var(--portrait-ratio) / var(--design-base-width))}@media (orientation: landscape){:host:not(.landscape):not(.portrait){--boundary-max-width: calc(var(--vh100) * var(--portrait-ratio));--boundary-max-height: calc(var(--vw100) * var(--landscape-ratio));--design-base-width: var(--design-height);--design-base-height: var(--design-width);--px2vw-ratio: calc(var(--vh100) * var(--portrait-ratio) / var(--design-base-width))}}.fixed-center{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);transform-origin:center;z-index:99}\n"] }]
74
136
  }], propDecorators: { isFixedCenter: [{
75
137
  type: Input
76
138
  }], pointerEventsNone: [{
@@ -1 +1 @@
1
- {"version":3,"file":"bitstack-ng-boundary.mjs","sources":["../../../projects/bitstack-ng-boundary/src/lib/bs-boundary-context.service.ts","../../../projects/bitstack-ng-boundary/src/lib/bs-boundary.ts","../../../projects/bitstack-ng-boundary/src/lib/template.html","../../../projects/bitstack-ng-boundary/src/public-api.ts","../../../projects/bitstack-ng-boundary/src/bitstack-ng-boundary.ts"],"sourcesContent":["import { Injectable } from '@angular/core';\nimport { BehaviorSubject } from 'rxjs';\n\n@Injectable()\nexport class BsBoundaryContextService {\n private _isLandscape = new BehaviorSubject<boolean>(false);\n\n readonly isLandscape$ = this._isLandscape.asObservable();\n\n setIsLandscape(value: boolean): void {\n this._isLandscape.next(value);\n }\n\n getIsLandscape(): boolean {\n return this._isLandscape.value;\n }\n}\n","import {\n Component,\n EventEmitter,\n inject,\n Input, OnDestroy,\n OnInit,\n Output,\n} from '@angular/core';\nimport {TOrientationMode} from './model';\nimport {BreakpointObserver} from '@angular/cdk/layout';\nimport {Subject, takeUntil} from 'rxjs';\nimport {BsBoundaryContextService} from './bs-boundary-context.service';\n\n@Component({\n selector: 'bs-boundary',\n templateUrl: 'template.html',\n standalone: true,\n styleUrls: ['./styles.scss'],\n providers: [BsBoundaryContextService],\n host: {\n '[class.landscape]': 'isLandscape'\n }\n})\nexport class BsBoundary implements OnInit, OnDestroy {\n\n @Input() isFixedCenter?: boolean = false;\n @Input() pointerEventsNone?: boolean = false;\n @Input() lockOrientation?: TOrientationMode = 'auto';\n @Input() forwardStyle?: Record<string, string>;\n\n @Output() orientationChange = new EventEmitter<boolean>();\n\n private breakpointObserver = inject(BreakpointObserver);\n private boundaryContext = inject(BsBoundaryContextService);\n private destroy$ = new Subject<void>();\n\n get isLandscape(): boolean {\n return this.boundaryContext.getIsLandscape();\n }\n\n ngOnInit(): void {\n this.setupBreakpointObserver();\n }\n\n ngOnDestroy(): void {\n this.destroy$.next();\n this.destroy$.complete();\n }\n\n /**\n * 監控方向變化\n */\n setupBreakpointObserver() {\n if (this.lockOrientation === 'landscape') {\n this.boundaryContext.setIsLandscape(true);\n this.orientationChange.emit(true);\n return;\n }\n\n if (this.lockOrientation === 'portrait') {\n this.boundaryContext.setIsLandscape(false);\n this.orientationChange.emit(false);\n return;\n }\n\n this.breakpointObserver\n .observe('(orientation: landscape)')\n .pipe(takeUntil(this.destroy$))\n .subscribe(result => {\n this.boundaryContext.setIsLandscape(result.matches);\n this.orientationChange.emit(result.matches);\n });\n }\n}\n","<div\n class=\"boundary-wrapper\"\n [class.fixed-center]=\"isFixedCenter\"\n [class.pointerEventsNone]=\"pointerEventsNone\"\n [style]=\"forwardStyle\"\n>\n <ng-content></ng-content>\n</div>\n","/*\n * Public API Surface of @bitstack/ng-boundary\n */\n\nexport * from './lib/bs-boundary';\nexport * from './lib/bs-boundary-context.service';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"bitstack-ng-boundary.mjs","sources":["../../../projects/bitstack-ng-boundary/src/lib/bs-boundary-context.service.ts","../../../projects/bitstack-ng-boundary/src/lib/bs-boundary.ts","../../../projects/bitstack-ng-boundary/src/lib/template.html","../../../projects/bitstack-ng-boundary/src/public-api.ts","../../../projects/bitstack-ng-boundary/src/bitstack-ng-boundary.ts"],"sourcesContent":["import { Injectable } from '@angular/core';\nimport { BehaviorSubject } from 'rxjs';\n\n@Injectable()\nexport class BsBoundaryContextService {\n private _isLandscape = new BehaviorSubject<boolean>(false);\n\n readonly isLandscape$ = this._isLandscape.asObservable();\n\n setIsLandscape(value: boolean): void {\n this._isLandscape.next(value);\n }\n\n getIsLandscape(): boolean {\n return this._isLandscape.value;\n }\n}\n","import {\n AfterViewInit,\n Component,\n ElementRef,\n EventEmitter,\n inject,\n Input, OnDestroy,\n OnInit,\n Output,\n} from '@angular/core';\nimport {TOrientationMode} from './model';\nimport {BreakpointObserver} from '@angular/cdk/layout';\nimport {debounceTime, fromEvent, Subject, takeUntil} from 'rxjs';\nimport {BsBoundaryContextService} from './bs-boundary-context.service';\n\n@Component({\n selector: 'bs-boundary',\n templateUrl: 'template.html',\n standalone: true,\n styleUrls: ['./styles.scss'],\n providers: [BsBoundaryContextService],\n host: {\n '[class.landscape]': 'isLandscape && lockOrientation !== \"auto\"',\n '[class.portrait]': '!isLandscape && lockOrientation !== \"auto\"'\n }\n})\nexport class BsBoundary implements OnInit, AfterViewInit, OnDestroy {\n\n @Input() isFixedCenter?: boolean = false;\n @Input() pointerEventsNone?: boolean = false;\n @Input() lockOrientation?: TOrientationMode = 'auto';\n @Input() forwardStyle?: Record<string, string>;\n\n @Output() orientationChange = new EventEmitter<boolean>();\n\n private breakpointObserver = inject(BreakpointObserver);\n private boundaryContext = inject(BsBoundaryContextService);\n private elementRef = inject(ElementRef);\n private destroy$ = new Subject<void>();\n\n get isLandscape(): boolean {\n return this.boundaryContext.getIsLandscape();\n }\n\n ngOnInit(): void {\n this.setupBreakpointObserver();\n this.setupResizeObserver();\n }\n\n ngAfterViewInit(): void {\n // 在 view 初始化後計算(確保 CSS 已完全應用)\n this.updateBoundaryCalculations();\n }\n\n ngOnDestroy(): void {\n this.destroy$.next();\n this.destroy$.complete();\n }\n\n /**\n * 監控方向變化\n * 只在 lock 模式時需要,auto 模式由 CSS 媒體查詢處理\n */\n setupBreakpointObserver() {\n if (this.lockOrientation === 'landscape') {\n this.boundaryContext.setIsLandscape(true);\n this.orientationChange.emit(true);\n return;\n }\n\n if (this.lockOrientation === 'portrait') {\n this.boundaryContext.setIsLandscape(false);\n this.orientationChange.emit(false);\n return;\n }\n\n // Auto 模式:監聽實際的設備方向(用於 orientationChange 事件)\n this.breakpointObserver\n .observe('(orientation: landscape)')\n .pipe(takeUntil(this.destroy$))\n .subscribe(result => {\n this.boundaryContext.setIsLandscape(result.matches);\n this.orientationChange.emit(result.matches);\n // 方向變化時重新計算\n this.updateBoundaryCalculations();\n });\n }\n\n /**\n * 監控視口大小變化\n */\n setupResizeObserver() {\n fromEvent(window, 'resize')\n .pipe(\n // debounceTime(150),\n takeUntil(this.destroy$)\n )\n .subscribe(() => {\n this.updateBoundaryCalculations();\n });\n }\n\n /**\n * 更新邊界計算(支援 Chrome 74,使用 JavaScript 模擬 min 函數)\n * 設定 --boundary-width, --boundary-height, --px2vw-ratio\n */\n updateBoundaryCalculations() {\n const hostElement = this.elementRef.nativeElement as HTMLElement;\n const computedStyle = getComputedStyle(hostElement);\n\n // 讀取基礎 CSS 變數(這些是純數值,不是 calc() 表達式)\n const vw100 = parseFloat(computedStyle.getPropertyValue('--vw100')) || window.innerWidth;\n const vh100 = parseFloat(computedStyle.getPropertyValue('--vh100')) || window.innerHeight;\n const designWidth = parseFloat(computedStyle.getPropertyValue('--design-width'));\n const designHeight = parseFloat(computedStyle.getPropertyValue('--design-height'));\n\n console.log('designWidth', designWidth);\n\n // 檢查基礎變數是否準備好\n if (isNaN(designWidth) || isNaN(designHeight) || designWidth === 0 || designHeight === 0) {\n return; // CSS 變數還沒準備好,跳過\n }\n\n // 在 JavaScript 中計算所有值(避免讀取 calc() 表達式)\n const landscapeRatio = designWidth / designHeight;\n const portraitRatio = designHeight / designWidth;\n\n // 根據方向計算邊界最大值\n const isLandscape = this.isLandscape;\n const boundaryMaxWidth = isLandscape\n ? vh100 * portraitRatio\n : vh100 * landscapeRatio;\n const boundaryMaxHeight = isLandscape\n ? vw100 * landscapeRatio\n : vw100 * portraitRatio;\n\n // 使用 JavaScript Math.min 模擬 CSS min() 函數\n const boundaryWidth = Math.min(vw100, boundaryMaxWidth);\n const boundaryHeight = Math.min(vh100, boundaryMaxHeight);\n\n // 計算設計基準和 px2vw 比例\n const designBaseWidth = isLandscape ? designHeight : designWidth;\n const px2vwRatio = boundaryWidth / designBaseWidth;\n\n // 設定 CSS 變數(向後兼容 + 精確的 ratio)\n hostElement.style.setProperty('--boundary-width', `${boundaryWidth}px`);\n hostElement.style.setProperty('--boundary-height', `${boundaryHeight}px`);\n hostElement.style.setProperty('--px2vw-ratio', `${px2vwRatio}`);\n }\n}\n","<div\n class=\"boundary-wrapper\"\n [class.fixed-center]=\"isFixedCenter\"\n [class.pointerEventsNone]=\"pointerEventsNone\"\n [style]=\"forwardStyle\"\n>\n <ng-content></ng-content>\n</div>\n","/*\n * Public API Surface of @bitstack/ng-boundary\n */\n\nexport * from './lib/bs-boundary';\nexport * from './lib/bs-boundary-context.service';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
@@ -1,7 +1,7 @@
1
- import { EventEmitter, OnDestroy, OnInit } from '@angular/core';
1
+ import { AfterViewInit, EventEmitter, OnDestroy, OnInit } from '@angular/core';
2
2
  import { TOrientationMode } from './model';
3
3
  import * as i0 from "@angular/core";
4
- export declare class BsBoundary implements OnInit, OnDestroy {
4
+ export declare class BsBoundary implements OnInit, AfterViewInit, OnDestroy {
5
5
  isFixedCenter?: boolean;
6
6
  pointerEventsNone?: boolean;
7
7
  lockOrientation?: TOrientationMode;
@@ -9,14 +9,26 @@ export declare class BsBoundary implements OnInit, OnDestroy {
9
9
  orientationChange: EventEmitter<boolean>;
10
10
  private breakpointObserver;
11
11
  private boundaryContext;
12
+ private elementRef;
12
13
  private destroy$;
13
14
  get isLandscape(): boolean;
14
15
  ngOnInit(): void;
16
+ ngAfterViewInit(): void;
15
17
  ngOnDestroy(): void;
16
18
  /**
17
19
  * 監控方向變化
20
+ * 只在 lock 模式時需要,auto 模式由 CSS 媒體查詢處理
18
21
  */
19
22
  setupBreakpointObserver(): void;
23
+ /**
24
+ * 監控視口大小變化
25
+ */
26
+ setupResizeObserver(): void;
27
+ /**
28
+ * 更新邊界計算(支援 Chrome 74,使用 JavaScript 模擬 min 函數)
29
+ * 設定 --boundary-width, --boundary-height, --px2vw-ratio
30
+ */
31
+ updateBoundaryCalculations(): void;
20
32
  static ɵfac: i0.ɵɵFactoryDeclaration<BsBoundary, never>;
21
33
  static ɵcmp: i0.ɵɵComponentDeclaration<BsBoundary, "bs-boundary", never, { "isFixedCenter": "isFixedCenter"; "pointerEventsNone": "pointerEventsNone"; "lockOrientation": "lockOrientation"; "forwardStyle": "forwardStyle"; }, { "orientationChange": "orientationChange"; }, never, ["*"], true>;
22
34
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bitstack/ng-boundary",
3
- "version": "14.0.6-alpha.1",
3
+ "version": "14.0.6-alpha.3",
4
4
  "peerDependencies": {
5
5
  "@angular/common": ">=14 <16",
6
6
  "@angular/core": ">=14 <16",
@@ -25,7 +25,7 @@
25
25
  // --design-height: 667;
26
26
  // }
27
27
  @function px2vw($px) {
28
- @return calc(#{$px} * var(--px2vw-ratio));
28
+ @return calc(#{$px}px * var(--px2vw-ratio));
29
29
  }
30
30
 
31
31