@bitstack/ng-boundary 14.0.6-alpha.2 → 14.0.6-alpha.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm2020/lib/bs-boundary-context.service.mjs +1 -1
- package/esm2020/lib/bs-boundary.mjs +221 -36
- package/esm2020/lib/model.mjs +1 -1
- package/fesm2015/bitstack-ng-boundary.mjs +220 -35
- package/fesm2015/bitstack-ng-boundary.mjs.map +1 -1
- package/fesm2020/bitstack-ng-boundary.mjs +220 -35
- package/fesm2020/bitstack-ng-boundary.mjs.map +1 -1
- package/lib/bs-boundary.d.ts +103 -10
- package/lib/model.d.ts +1 -1
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import { BreakpointObserver } from '@angular/cdk/layout';
|
|
1
2
|
import * as i0 from '@angular/core';
|
|
2
3
|
import { Injectable, EventEmitter, inject, ElementRef, Component, Input, Output } from '@angular/core';
|
|
3
|
-
import {
|
|
4
|
-
import { BehaviorSubject, Subject, takeUntil, fromEvent, debounceTime } from 'rxjs';
|
|
4
|
+
import { BehaviorSubject, Subject, takeUntil, fromEvent } from 'rxjs';
|
|
5
5
|
|
|
6
6
|
class BsBoundaryContextService {
|
|
7
7
|
constructor() {
|
|
@@ -21,115 +21,300 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
|
|
|
21
21
|
type: Injectable
|
|
22
22
|
}] });
|
|
23
23
|
|
|
24
|
+
/**
|
|
25
|
+
* ============================================================
|
|
26
|
+
* BsBoundary Component - AWD 自適應邊界容器
|
|
27
|
+
* ============================================================
|
|
28
|
+
*
|
|
29
|
+
* 功能說明:
|
|
30
|
+
* 1. 提供響應式邊界容器,自動適應不同設備尺寸
|
|
31
|
+
* 2. 支援 5 種方向模式:auto, portrait, landscape, portraitLockRotation, landscapeLockRotation
|
|
32
|
+
* 3. 使用 CSS 變數實現動態計算,支援 px2vw() 函數
|
|
33
|
+
* 4. 相容 Chrome 74+,使用 JavaScript 計算邊界尺寸
|
|
34
|
+
*
|
|
35
|
+
* 使用範例:
|
|
36
|
+
* ```html
|
|
37
|
+
* <bs-boundary [orientation]="'auto'" [isFixedCenter]="true">
|
|
38
|
+
* <div class="content">...</div>
|
|
39
|
+
* </bs-boundary>
|
|
40
|
+
* ```
|
|
41
|
+
*
|
|
42
|
+
* CSS 變數設定:
|
|
43
|
+
* ```scss
|
|
44
|
+
* bs-boundary {
|
|
45
|
+
* --design-width: 375; // 設計稿寬度
|
|
46
|
+
* --design-height: 667; // 設計稿高度
|
|
47
|
+
* }
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
24
50
|
class BsBoundary {
|
|
25
51
|
constructor() {
|
|
52
|
+
// ============================================================
|
|
53
|
+
// Inputs & Outputs
|
|
54
|
+
// ============================================================
|
|
55
|
+
/**
|
|
56
|
+
* 是否將內容固定在視窗中央
|
|
57
|
+
* @default false
|
|
58
|
+
*/
|
|
26
59
|
this.isFixedCenter = false;
|
|
60
|
+
/**
|
|
61
|
+
* 是否禁用指標事件
|
|
62
|
+
* @default false
|
|
63
|
+
*/
|
|
27
64
|
this.pointerEventsNone = false;
|
|
28
|
-
|
|
65
|
+
/**
|
|
66
|
+
* 方向模式
|
|
67
|
+
* - auto: 自動檢測方向
|
|
68
|
+
* - portrait: 鎖定直向
|
|
69
|
+
* - landscape: 鎖定橫向
|
|
70
|
+
* - portraitLockRotation: 強制直向(橫向設備時旋轉 -90°)
|
|
71
|
+
* - landscapeLockRotation: 強制橫向(直向設備時旋轉 90°)
|
|
72
|
+
* @default 'auto'
|
|
73
|
+
*/
|
|
74
|
+
this.orientation = 'auto';
|
|
75
|
+
/**
|
|
76
|
+
* 方向變化事件
|
|
77
|
+
* @emits true: landscape(橫屏)
|
|
78
|
+
* @emits false: portrait(直屏)
|
|
79
|
+
*/
|
|
29
80
|
this.orientationChange = new EventEmitter();
|
|
81
|
+
// ============================================================
|
|
82
|
+
// Private Dependencies
|
|
83
|
+
// ============================================================
|
|
30
84
|
this.breakpointObserver = inject(BreakpointObserver);
|
|
31
85
|
this.boundaryContext = inject(BsBoundaryContextService);
|
|
32
86
|
this.elementRef = inject(ElementRef);
|
|
33
87
|
this.destroy$ = new Subject();
|
|
88
|
+
// ============================================================
|
|
89
|
+
// Public Properties
|
|
90
|
+
// ============================================================
|
|
91
|
+
/**
|
|
92
|
+
* 實際設備方向(基於視口尺寸)
|
|
93
|
+
* true: 直向 (height > width)
|
|
94
|
+
* false: 橫向 (width >= height)
|
|
95
|
+
*/
|
|
96
|
+
this.actualIsPortrait = false;
|
|
34
97
|
}
|
|
98
|
+
// ============================================================
|
|
99
|
+
// Getters
|
|
100
|
+
// ============================================================
|
|
101
|
+
/**
|
|
102
|
+
* 判斷當前是否為橫向模式
|
|
103
|
+
* 根據 orientation 設定和實際設備方向判斷
|
|
104
|
+
*/
|
|
35
105
|
get isLandscape() {
|
|
106
|
+
// landscapeLockRotation: 強制橫屏模式
|
|
107
|
+
if (this.orientation === 'landscapeLockRotation') {
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
// portraitLockRotation: 強制直屏模式
|
|
111
|
+
if (this.orientation === 'portraitLockRotation') {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
// landscape: 鎖定橫向
|
|
115
|
+
if (this.orientation === 'landscape') {
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
// portrait: 鎖定直向
|
|
119
|
+
if (this.orientation === 'portrait') {
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
// auto: 自動檢測
|
|
36
123
|
return this.boundaryContext.getIsLandscape();
|
|
37
124
|
}
|
|
125
|
+
// ============================================================
|
|
126
|
+
// Lifecycle Hooks
|
|
127
|
+
// ============================================================
|
|
38
128
|
ngOnInit() {
|
|
129
|
+
this.updateActualOrientation();
|
|
39
130
|
this.setupBreakpointObserver();
|
|
40
131
|
this.setupResizeObserver();
|
|
41
|
-
|
|
42
|
-
|
|
132
|
+
}
|
|
133
|
+
ngAfterViewInit() {
|
|
134
|
+
// 在 view 初始化後計算(確保 CSS 已完全應用)
|
|
135
|
+
this.updateBoundaryCalculations();
|
|
43
136
|
}
|
|
44
137
|
ngOnDestroy() {
|
|
45
138
|
this.destroy$.next();
|
|
46
139
|
this.destroy$.complete();
|
|
47
140
|
}
|
|
141
|
+
// ============================================================
|
|
142
|
+
// Observers Setup - 監聽器設定
|
|
143
|
+
// ============================================================
|
|
48
144
|
/**
|
|
49
|
-
*
|
|
145
|
+
* 設定方向變化監控
|
|
146
|
+
*
|
|
147
|
+
* 工作原理:
|
|
148
|
+
* - lock 模式(portrait/landscape):直接設定固定方向,不監聽變化
|
|
149
|
+
* - lockRotation 模式:設定固定方向,但需監聽實際設備方向以觸發旋轉
|
|
150
|
+
* - auto 模式:使用 BreakpointObserver 監聽設備方向變化
|
|
50
151
|
*/
|
|
51
152
|
setupBreakpointObserver() {
|
|
52
|
-
|
|
153
|
+
// landscape 或 landscapeLockRotation: 鎖定為橫向
|
|
154
|
+
if (this.orientation === 'landscape' || this.orientation === 'landscapeLockRotation') {
|
|
53
155
|
this.boundaryContext.setIsLandscape(true);
|
|
54
156
|
this.orientationChange.emit(true);
|
|
55
|
-
// 鎖定方向後也需要更新計算
|
|
56
|
-
setTimeout(() => this.updateBoundaryCalculations(), 0);
|
|
57
157
|
return;
|
|
58
158
|
}
|
|
59
|
-
|
|
159
|
+
// portrait 或 portraitLockRotation: 鎖定為直向
|
|
160
|
+
if (this.orientation === 'portrait' || this.orientation === 'portraitLockRotation') {
|
|
60
161
|
this.boundaryContext.setIsLandscape(false);
|
|
61
162
|
this.orientationChange.emit(false);
|
|
62
|
-
// 鎖定方向後也需要更新計算
|
|
63
|
-
setTimeout(() => this.updateBoundaryCalculations(), 0);
|
|
64
163
|
return;
|
|
65
164
|
}
|
|
165
|
+
// auto 模式:監聽實際的設備方向(用於 orientationChange 事件)
|
|
66
166
|
this.breakpointObserver
|
|
67
167
|
.observe('(orientation: landscape)')
|
|
68
168
|
.pipe(takeUntil(this.destroy$))
|
|
69
169
|
.subscribe(result => {
|
|
70
170
|
this.boundaryContext.setIsLandscape(result.matches);
|
|
71
171
|
this.orientationChange.emit(result.matches);
|
|
172
|
+
// 方向變化時重新計算邊界
|
|
72
173
|
this.updateBoundaryCalculations();
|
|
73
174
|
});
|
|
74
175
|
}
|
|
75
176
|
/**
|
|
76
|
-
*
|
|
177
|
+
* 設定視窗大小變化監控
|
|
178
|
+
*
|
|
179
|
+
* 功能:
|
|
180
|
+
* - 監聽 window resize 事件
|
|
181
|
+
* - 更新實際設備方向
|
|
182
|
+
* - 重新計算邊界尺寸
|
|
77
183
|
*/
|
|
78
184
|
setupResizeObserver() {
|
|
79
185
|
fromEvent(window, 'resize')
|
|
80
|
-
.pipe(
|
|
186
|
+
.pipe(
|
|
187
|
+
// debounceTime(150), // 可選:防抖優化性能
|
|
188
|
+
takeUntil(this.destroy$))
|
|
81
189
|
.subscribe(() => {
|
|
190
|
+
this.updateActualOrientation();
|
|
82
191
|
this.updateBoundaryCalculations();
|
|
83
192
|
});
|
|
84
193
|
}
|
|
194
|
+
// ============================================================
|
|
195
|
+
// Calculation Methods - 計算方法
|
|
196
|
+
// ============================================================
|
|
197
|
+
/**
|
|
198
|
+
* 更新實際設備方向(基於視口尺寸)
|
|
199
|
+
*
|
|
200
|
+
* 工作原理:
|
|
201
|
+
* - 只在 lockRotation 模式下需要檢測實際方向
|
|
202
|
+
* - 通過比較 window.innerHeight 和 window.innerWidth 判斷
|
|
203
|
+
* - 結果用於觸發 CSS 旋轉變換
|
|
204
|
+
*/
|
|
205
|
+
updateActualOrientation() {
|
|
206
|
+
// 只有在 lockRotation 模式下才需要檢測實際方向
|
|
207
|
+
if (this.orientation !== 'landscapeLockRotation' && this.orientation !== 'portraitLockRotation') {
|
|
208
|
+
this.actualIsPortrait = false;
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
// 檢測實際視口方向:高度 > 寬度 = 直向
|
|
212
|
+
this.actualIsPortrait = window.innerHeight > window.innerWidth;
|
|
213
|
+
}
|
|
85
214
|
/**
|
|
86
|
-
* 更新邊界計算(支援 Chrome 74
|
|
215
|
+
* 更新邊界計算(支援 Chrome 74,使用 JavaScript 模擬 min 函數)
|
|
216
|
+
*
|
|
217
|
+
* 工作原理:
|
|
218
|
+
* 1. 讀取基礎 CSS 變數(vw100, vh100, design-width, design-height)
|
|
219
|
+
* 2. 處理 lockRotation 模式的視口尺寸交換
|
|
220
|
+
* 3. 計算邊界最大值和實際邊界尺寸
|
|
221
|
+
* 4. 計算 px2vw 轉換比例
|
|
222
|
+
* 5. 設定 CSS 變數供樣式使用
|
|
223
|
+
*
|
|
224
|
+
* 設定的 CSS 變數:
|
|
225
|
+
* - --boundary-width: 實際邊界寬度(px)
|
|
226
|
+
* - --boundary-height: 實際邊界高度(px)
|
|
227
|
+
* - --px2vw-ratio: px 轉 vw 的比例係數
|
|
87
228
|
*/
|
|
88
229
|
updateBoundaryCalculations() {
|
|
89
230
|
const hostElement = this.elementRef.nativeElement;
|
|
90
231
|
const computedStyle = getComputedStyle(hostElement);
|
|
91
|
-
//
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
232
|
+
// ========================================
|
|
233
|
+
// Step 1: 讀取基礎 CSS 變數
|
|
234
|
+
// ========================================
|
|
235
|
+
let vw100 = parseFloat(computedStyle.getPropertyValue('--vw100')) || window.innerWidth;
|
|
236
|
+
let vh100 = parseFloat(computedStyle.getPropertyValue('--vh100')) || window.innerHeight;
|
|
237
|
+
const designWidth = parseFloat(computedStyle.getPropertyValue('--design-width'));
|
|
238
|
+
const designHeight = parseFloat(computedStyle.getPropertyValue('--design-height'));
|
|
239
|
+
// 檢查基礎變數是否準備好
|
|
240
|
+
if (isNaN(designWidth)) {
|
|
241
|
+
console.error('[BsBoundary][BS001] CSS variable --design-width is not set or invalid');
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
if (isNaN(designHeight)) {
|
|
245
|
+
console.error('[BsBoundary][BS002] CSS variable --design-height is not set or invalid');
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
if (designWidth === 0) {
|
|
249
|
+
console.error('[BsBoundary][BS003] CSS variable --design-width cannot be zero');
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
if (designHeight === 0) {
|
|
253
|
+
console.error('[BsBoundary][BS004] CSS variable --design-height cannot be zero');
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
// ========================================
|
|
257
|
+
// Step 2: 處理 lockRotation 模式的視口尺寸交換
|
|
258
|
+
// ========================================
|
|
259
|
+
// landscapeLockRotation + 實際直向:交換視口尺寸
|
|
260
|
+
// 原因:旋轉後,內容的寬度對應設備的高度,內容的高度對應設備的寬度
|
|
261
|
+
if (this.orientation === 'landscapeLockRotation' && this.actualIsPortrait) {
|
|
262
|
+
[vw100, vh100] = [vh100, vw100];
|
|
263
|
+
}
|
|
264
|
+
// portraitLockRotation + 實際橫向:交換視口尺寸
|
|
265
|
+
if (this.orientation === 'portraitLockRotation' && !this.actualIsPortrait) {
|
|
266
|
+
[vw100, vh100] = [vh100, vw100];
|
|
267
|
+
}
|
|
268
|
+
// ========================================
|
|
269
|
+
// Step 3: 計算比例和邊界最大值
|
|
270
|
+
// ========================================
|
|
97
271
|
const landscapeRatio = designWidth / designHeight;
|
|
98
272
|
const portraitRatio = designHeight / designWidth;
|
|
99
|
-
//
|
|
273
|
+
// 根據方向模式計算邊界最大值
|
|
100
274
|
const isLandscape = this.isLandscape;
|
|
101
275
|
const boundaryMaxWidth = isLandscape
|
|
102
|
-
? vh100 * portraitRatio
|
|
103
|
-
: vh100 * landscapeRatio;
|
|
276
|
+
? vh100 * portraitRatio // 橫向:高度 * 高寬比
|
|
277
|
+
: vh100 * landscapeRatio; // 直向:高度 * 寬高比
|
|
104
278
|
const boundaryMaxHeight = isLandscape
|
|
105
|
-
? vw100 * landscapeRatio
|
|
106
|
-
: vw100 * portraitRatio;
|
|
107
|
-
//
|
|
279
|
+
? vw100 * landscapeRatio // 橫向:寬度 * 寬高比
|
|
280
|
+
: vw100 * portraitRatio; // 直向:寬度 * 高寬比
|
|
281
|
+
// ========================================
|
|
282
|
+
// Step 4: 計算實際邊界尺寸(使用 Math.min 模擬 CSS min())
|
|
283
|
+
// ========================================
|
|
284
|
+
// Chrome 79 才支援 CSS min() 函數,所以在 JavaScript 中計算
|
|
108
285
|
const boundaryWidth = Math.min(vw100, boundaryMaxWidth);
|
|
109
286
|
const boundaryHeight = Math.min(vh100, boundaryMaxHeight);
|
|
110
|
-
//
|
|
287
|
+
// ========================================
|
|
288
|
+
// Step 5: 計算 px2vw 轉換比例
|
|
289
|
+
// ========================================
|
|
290
|
+
// 橫向模式使用 design-height 作為基準,直向模式使用 design-width 作為基準
|
|
111
291
|
const designBaseWidth = isLandscape ? designHeight : designWidth;
|
|
112
|
-
const designBaseHeight = isLandscape ? designWidth : designHeight;
|
|
113
|
-
// 計算 px2vw 比例
|
|
114
292
|
const px2vwRatio = boundaryWidth / designBaseWidth;
|
|
115
|
-
//
|
|
293
|
+
// ========================================
|
|
294
|
+
// Step 6: 設定 CSS 變數
|
|
295
|
+
// ========================================
|
|
116
296
|
hostElement.style.setProperty('--boundary-width', `${boundaryWidth}px`);
|
|
117
297
|
hostElement.style.setProperty('--boundary-height', `${boundaryHeight}px`);
|
|
118
298
|
hostElement.style.setProperty('--px2vw-ratio', `${px2vwRatio}`);
|
|
119
299
|
}
|
|
120
300
|
}
|
|
121
301
|
BsBoundary.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: BsBoundary, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
122
|
-
BsBoundary.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: BsBoundary, isStandalone: true, selector: "bs-boundary", inputs: { isFixedCenter: "isFixedCenter", pointerEventsNone: "pointerEventsNone",
|
|
302
|
+
BsBoundary.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: BsBoundary, isStandalone: true, selector: "bs-boundary", inputs: { isFixedCenter: "isFixedCenter", pointerEventsNone: "pointerEventsNone", orientation: "orientation", forwardStyle: "forwardStyle" }, outputs: { orientationChange: "orientationChange" }, host: { properties: { "class.landscape": "isLandscape && orientation === \"landscape\"", "class.portrait": "!isLandscape && orientation === \"portrait\"", "class.landscape-lock-rotation": "orientation === \"landscapeLockRotation\"", "class.portrait-lock-rotation": "orientation === \"portraitLockRotation\"", "class.actual-portrait": "actualIsPortrait", "class.actual-landscape": "!actualIsPortrait" } }, 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.portrait{--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))}: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):not(.landscape-lock-rotation):not(.portrait-lock-rotation){--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}:host.landscape-lock-rotation{--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))}:host.landscape-lock-rotation.actual-portrait .boundary-wrapper{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%) rotate(90deg);transform-origin:center;width:var(--boundary-width);height:var(--boundary-height);max-width:none;max-height:none}:host.portrait-lock-rotation{--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))}:host.portrait-lock-rotation.actual-landscape .boundary-wrapper{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%) rotate(-90deg);transform-origin:center;width:var(--boundary-width);height:var(--boundary-height);max-width:none;max-height:none}\n"] });
|
|
123
303
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: BsBoundary, decorators: [{
|
|
124
304
|
type: Component,
|
|
125
305
|
args: [{ selector: 'bs-boundary', standalone: true, providers: [BsBoundaryContextService], host: {
|
|
126
|
-
'[class.landscape]': 'isLandscape'
|
|
127
|
-
|
|
306
|
+
'[class.landscape]': 'isLandscape && orientation === "landscape"',
|
|
307
|
+
'[class.portrait]': '!isLandscape && orientation === "portrait"',
|
|
308
|
+
'[class.landscape-lock-rotation]': 'orientation === "landscapeLockRotation"',
|
|
309
|
+
'[class.portrait-lock-rotation]': 'orientation === "portraitLockRotation"',
|
|
310
|
+
'[class.actual-portrait]': 'actualIsPortrait',
|
|
311
|
+
'[class.actual-landscape]': '!actualIsPortrait'
|
|
312
|
+
}, 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.portrait{--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))}: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):not(.landscape-lock-rotation):not(.portrait-lock-rotation){--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}:host.landscape-lock-rotation{--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))}:host.landscape-lock-rotation.actual-portrait .boundary-wrapper{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%) rotate(90deg);transform-origin:center;width:var(--boundary-width);height:var(--boundary-height);max-width:none;max-height:none}:host.portrait-lock-rotation{--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))}:host.portrait-lock-rotation.actual-landscape .boundary-wrapper{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%) rotate(-90deg);transform-origin:center;width:var(--boundary-width);height:var(--boundary-height);max-width:none;max-height:none}\n"] }]
|
|
128
313
|
}], propDecorators: { isFixedCenter: [{
|
|
129
314
|
type: Input
|
|
130
315
|
}], pointerEventsNone: [{
|
|
131
316
|
type: Input
|
|
132
|
-
}],
|
|
317
|
+
}], orientation: [{
|
|
133
318
|
type: Input
|
|
134
319
|
}], forwardStyle: [{
|
|
135
320
|
type: Input
|
|
@@ -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 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'\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 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 // 延遲執行以確保 CSS 變數已經設定\n setTimeout(() => this.updateBoundaryCalculations(), 0);\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 // 鎖定方向後也需要更新計算\n setTimeout(() => this.updateBoundaryCalculations(), 0);\n return;\n }\n\n if (this.lockOrientation === 'portrait') {\n this.boundaryContext.setIsLandscape(false);\n this.orientationChange.emit(false);\n // 鎖定方向後也需要更新計算\n setTimeout(() => this.updateBoundaryCalculations(), 0);\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 this.updateBoundaryCalculations();\n });\n }\n\n /**\n * 監控視口大小變化\n */\n setupResizeObserver() {\n fromEvent(window, 'resize')\n .pipe(\n debounceTime(100),\n takeUntil(this.destroy$)\n )\n .subscribe(() => {\n this.updateBoundaryCalculations();\n });\n }\n\n /**\n * 更新邊界計算(支援 Chrome 74,不使用 CSS min/max 函數)\n */\n updateBoundaryCalculations() {\n const hostElement = this.elementRef.nativeElement as HTMLElement;\n const computedStyle = getComputedStyle(hostElement);\n\n // 讀取 CSS 變數\n const designWidth = parseFloat(computedStyle.getPropertyValue('--design-width') || '1920');\n const designHeight = parseFloat(computedStyle.getPropertyValue('--design-height') || '1080');\n const vw100 = parseFloat(computedStyle.getPropertyValue('--vw100') || `${window.innerWidth}`);\n const vh100 = parseFloat(computedStyle.getPropertyValue('--vh100') || `${window.innerHeight}`);\n\n // 計算比例\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 // 計算實際邊界寬高(模擬 CSS min 函數)\n const boundaryWidth = Math.min(vw100, boundaryMaxWidth);\n const boundaryHeight = Math.min(vh100, boundaryMaxHeight);\n\n // 計算設計基準\n const designBaseWidth = isLandscape ? designHeight : designWidth;\n const designBaseHeight = isLandscape ? designWidth : designHeight;\n\n // 計算 px2vw 比例\n const px2vwRatio = boundaryWidth / designBaseWidth;\n\n // 設定 CSS 變數到 host 元素\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
|
+
{"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 {BreakpointObserver} from '@angular/cdk/layout';\nimport {\n AfterViewInit,\n Component,\n ElementRef,\n EventEmitter,\n inject,\n Input, OnDestroy,\n OnInit,\n Output,\n} from '@angular/core';\nimport {debounceTime, fromEvent, Subject, takeUntil} from 'rxjs';\n\nimport {BsBoundaryContextService} from './bs-boundary-context.service';\nimport {TOrientationMode} from './model';\n\n/**\n * ============================================================\n * BsBoundary Component - AWD 自適應邊界容器\n * ============================================================\n *\n * 功能說明:\n * 1. 提供響應式邊界容器,自動適應不同設備尺寸\n * 2. 支援 5 種方向模式:auto, portrait, landscape, portraitLockRotation, landscapeLockRotation\n * 3. 使用 CSS 變數實現動態計算,支援 px2vw() 函數\n * 4. 相容 Chrome 74+,使用 JavaScript 計算邊界尺寸\n *\n * 使用範例:\n * ```html\n * <bs-boundary [orientation]=\"'auto'\" [isFixedCenter]=\"true\">\n * <div class=\"content\">...</div>\n * </bs-boundary>\n * ```\n *\n * CSS 變數設定:\n * ```scss\n * bs-boundary {\n * --design-width: 375; // 設計稿寬度\n * --design-height: 667; // 設計稿高度\n * }\n * ```\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 && orientation === \"landscape\"',\n '[class.portrait]': '!isLandscape && orientation === \"portrait\"',\n '[class.landscape-lock-rotation]': 'orientation === \"landscapeLockRotation\"',\n '[class.portrait-lock-rotation]': 'orientation === \"portraitLockRotation\"',\n '[class.actual-portrait]': 'actualIsPortrait',\n '[class.actual-landscape]': '!actualIsPortrait'\n }\n})\nexport class BsBoundary implements OnInit, AfterViewInit, OnDestroy {\n\n // ============================================================\n // Inputs & Outputs\n // ============================================================\n\n /**\n * 是否將內容固定在視窗中央\n * @default false\n */\n @Input() isFixedCenter?: boolean = false;\n\n /**\n * 是否禁用指標事件\n * @default false\n */\n @Input() pointerEventsNone?: boolean = false;\n\n /**\n * 方向模式\n * - auto: 自動檢測方向\n * - portrait: 鎖定直向\n * - landscape: 鎖定橫向\n * - portraitLockRotation: 強制直向(橫向設備時旋轉 -90°)\n * - landscapeLockRotation: 強制橫向(直向設備時旋轉 90°)\n * @default 'auto'\n */\n @Input() orientation?: TOrientationMode = 'auto';\n\n /**\n * 向內層容器轉發的自訂樣式\n */\n @Input() forwardStyle?: Record<string, string>;\n\n /**\n * 方向變化事件\n * @emits true: landscape(橫屏)\n * @emits false: portrait(直屏)\n */\n @Output() orientationChange = new EventEmitter<boolean>();\n\n // ============================================================\n // Private Dependencies\n // ============================================================\n\n private breakpointObserver = inject(BreakpointObserver);\n private boundaryContext = inject(BsBoundaryContextService);\n private elementRef = inject(ElementRef);\n private destroy$ = new Subject<void>();\n\n // ============================================================\n // Public Properties\n // ============================================================\n\n /**\n * 實際設備方向(基於視口尺寸)\n * true: 直向 (height > width)\n * false: 橫向 (width >= height)\n */\n actualIsPortrait = false;\n\n // ============================================================\n // Getters\n // ============================================================\n\n /**\n * 判斷當前是否為橫向模式\n * 根據 orientation 設定和實際設備方向判斷\n */\n get isLandscape(): boolean {\n // landscapeLockRotation: 強制橫屏模式\n if (this.orientation === 'landscapeLockRotation') {\n return true;\n }\n // portraitLockRotation: 強制直屏模式\n if (this.orientation === 'portraitLockRotation') {\n return false;\n }\n // landscape: 鎖定橫向\n if (this.orientation === 'landscape') {\n return true;\n }\n // portrait: 鎖定直向\n if (this.orientation === 'portrait') {\n return false;\n }\n // auto: 自動檢測\n return this.boundaryContext.getIsLandscape();\n }\n\n // ============================================================\n // Lifecycle Hooks\n // ============================================================\n\n ngOnInit(): void {\n this.updateActualOrientation();\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 // Observers Setup - 監聽器設定\n // ============================================================\n\n /**\n * 設定方向變化監控\n *\n * 工作原理:\n * - lock 模式(portrait/landscape):直接設定固定方向,不監聽變化\n * - lockRotation 模式:設定固定方向,但需監聽實際設備方向以觸發旋轉\n * - auto 模式:使用 BreakpointObserver 監聽設備方向變化\n */\n private setupBreakpointObserver() {\n // landscape 或 landscapeLockRotation: 鎖定為橫向\n if (this.orientation === 'landscape' || this.orientation === 'landscapeLockRotation') {\n this.boundaryContext.setIsLandscape(true);\n this.orientationChange.emit(true);\n return;\n }\n\n // portrait 或 portraitLockRotation: 鎖定為直向\n if (this.orientation === 'portrait' || this.orientation === 'portraitLockRotation') {\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 * 功能:\n * - 監聽 window resize 事件\n * - 更新實際設備方向\n * - 重新計算邊界尺寸\n */\n private setupResizeObserver() {\n fromEvent(window, 'resize')\n .pipe(\n // debounceTime(150), // 可選:防抖優化性能\n takeUntil(this.destroy$)\n )\n .subscribe(() => {\n this.updateActualOrientation();\n this.updateBoundaryCalculations();\n });\n }\n\n // ============================================================\n // Calculation Methods - 計算方法\n // ============================================================\n\n /**\n * 更新實際設備方向(基於視口尺寸)\n *\n * 工作原理:\n * - 只在 lockRotation 模式下需要檢測實際方向\n * - 通過比較 window.innerHeight 和 window.innerWidth 判斷\n * - 結果用於觸發 CSS 旋轉變換\n */\n private updateActualOrientation() {\n // 只有在 lockRotation 模式下才需要檢測實際方向\n if (this.orientation !== 'landscapeLockRotation' && this.orientation !== 'portraitLockRotation') {\n this.actualIsPortrait = false;\n return;\n }\n\n // 檢測實際視口方向:高度 > 寬度 = 直向\n this.actualIsPortrait = window.innerHeight > window.innerWidth;\n }\n\n /**\n * 更新邊界計算(支援 Chrome 74,使用 JavaScript 模擬 min 函數)\n *\n * 工作原理:\n * 1. 讀取基礎 CSS 變數(vw100, vh100, design-width, design-height)\n * 2. 處理 lockRotation 模式的視口尺寸交換\n * 3. 計算邊界最大值和實際邊界尺寸\n * 4. 計算 px2vw 轉換比例\n * 5. 設定 CSS 變數供樣式使用\n *\n * 設定的 CSS 變數:\n * - --boundary-width: 實際邊界寬度(px)\n * - --boundary-height: 實際邊界高度(px)\n * - --px2vw-ratio: px 轉 vw 的比例係數\n */\n private updateBoundaryCalculations() {\n const hostElement = this.elementRef.nativeElement as HTMLElement;\n const computedStyle = getComputedStyle(hostElement);\n\n // ========================================\n // Step 1: 讀取基礎 CSS 變數\n // ========================================\n let vw100 = parseFloat(computedStyle.getPropertyValue('--vw100')) || window.innerWidth;\n let 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 // 檢查基礎變數是否準備好\n if (isNaN(designWidth)) {\n console.error('[BsBoundary][BS001] CSS variable --design-width is not set or invalid');\n return;\n }\n if (isNaN(designHeight)) {\n console.error('[BsBoundary][BS002] CSS variable --design-height is not set or invalid');\n return;\n }\n if (designWidth === 0) {\n console.error('[BsBoundary][BS003] CSS variable --design-width cannot be zero');\n return;\n }\n if (designHeight === 0) {\n console.error('[BsBoundary][BS004] CSS variable --design-height cannot be zero');\n return;\n }\n\n // ========================================\n // Step 2: 處理 lockRotation 模式的視口尺寸交換\n // ========================================\n // landscapeLockRotation + 實際直向:交換視口尺寸\n // 原因:旋轉後,內容的寬度對應設備的高度,內容的高度對應設備的寬度\n if (this.orientation === 'landscapeLockRotation' && this.actualIsPortrait) {\n [vw100, vh100] = [vh100, vw100];\n }\n\n // portraitLockRotation + 實際橫向:交換視口尺寸\n if (this.orientation === 'portraitLockRotation' && !this.actualIsPortrait) {\n [vw100, vh100] = [vh100, vw100];\n }\n\n // ========================================\n // Step 3: 計算比例和邊界最大值\n // ========================================\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 // ========================================\n // Step 4: 計算實際邊界尺寸(使用 Math.min 模擬 CSS min())\n // ========================================\n // Chrome 79 才支援 CSS min() 函數,所以在 JavaScript 中計算\n const boundaryWidth = Math.min(vw100, boundaryMaxWidth);\n const boundaryHeight = Math.min(vh100, boundaryMaxHeight);\n\n // ========================================\n // Step 5: 計算 px2vw 轉換比例\n // ========================================\n // 橫向模式使用 design-height 作為基準,直向模式使用 design-width 作為基準\n const designBaseWidth = isLandscape ? designHeight : designWidth;\n const px2vwRatio = boundaryWidth / designBaseWidth;\n\n // ========================================\n // Step 6: 設定 CSS 變數\n // ========================================\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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"}
|