@a11y-ngx/overlay-base 1.0.0

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.
@@ -0,0 +1,446 @@
1
+ import { Subject, Observable } from 'rxjs';
2
+ import { ViewportSize } from './overlay-base.type.private';
3
+ import { OverlayBaseConfig, OverlayBasePosition, OverlayBaseAlignment, OverlayBasePositionStrategy, OverlayBaseCalculatedPosition, OverlayBaseSafeSpace, OverlayBaseMaxSize } from './overlay-base.type';
4
+ export declare abstract class OverlayBase {
5
+ readonly uid: number;
6
+ readonly overlayConfig: OverlayBaseConfig;
7
+ private overlayMainElement;
8
+ triggerRect: DOMRect;
9
+ virtualTriggerRect: DOMRect;
10
+ overlayRect: DOMRect;
11
+ overlayOriginalRect: DOMRect;
12
+ boundaryRect: DOMRect;
13
+ isDetached$: Subject<void>;
14
+ private isAttached;
15
+ private forceUpdate$;
16
+ private resize$;
17
+ private resizeFn;
18
+ private scroll$;
19
+ private scrollFn;
20
+ private boundaryScroll$;
21
+ private boundaryScrollFn;
22
+ /**
23
+ * @description
24
+ * Allowed Positions & Alignments provided by the user.
25
+ */
26
+ private allowed;
27
+ private allowedOpposite;
28
+ /**
29
+ * @description
30
+ * An array of the listeners (for repositioning purposes).
31
+ */
32
+ private listeners;
33
+ /**
34
+ * @description
35
+ * To allow listening on page scrolling for repositioning.
36
+ */
37
+ private allowScrollListener;
38
+ /**
39
+ * @description
40
+ * Max `width` & `height` values allowed for the Overlay to perform
41
+ * proper calculations in case `fluidSize` is active.
42
+ */
43
+ readonly maxSize: OverlayBaseMaxSize;
44
+ /**
45
+ * @description
46
+ * Scrollbar size (`vertical` & `horizontal`) for a given custom Boundary.
47
+ */
48
+ private boundaryScrollSize;
49
+ /**
50
+ * @description
51
+ * The Viewport size (without the scrollbars into consideration).
52
+ */
53
+ get viewportSize(): ViewportSize;
54
+ /**
55
+ * @description
56
+ * The Viewport safe size.
57
+ * This will return width/height values taking into consideration a given custom Boundary and/or Safe Space.
58
+ */
59
+ get viewportSizeSafe(): ViewportSize;
60
+ private viewportSafe;
61
+ /**
62
+ * @description
63
+ * The chosen Position by the calculations.
64
+ */
65
+ get getCurrentPosition(): OverlayBasePosition;
66
+ private currentPosition;
67
+ private desiredPosition;
68
+ /**
69
+ * @description
70
+ * The chosen Alignment by the calculations.
71
+ */
72
+ get getCurrentAlignment(): OverlayBaseAlignment;
73
+ private currentAlignment;
74
+ private desiredAlignment;
75
+ /**
76
+ * @description
77
+ * This will provide the side of the Viewport/Boundary where the Overlay would be out of
78
+ * in case `fluidAlignment` is active and the Overlay, by the chosen alignment, is out of the visible area.
79
+ */
80
+ overlayOutside: OverlayBasePosition | undefined;
81
+ /**
82
+ * @description
83
+ * Checks that either TOP or BOTTOM positions are inside or outside the visible area.
84
+ *
85
+ * @param { number } valueTop - The numeric value for TOP position.
86
+ * @param { number } valueBottom - The numeric value for BOTTOM position.
87
+ */
88
+ private overlayOutsideCheckY;
89
+ /**
90
+ * @description
91
+ * Checks that either LEFT or RIGHT positions are inside or outside the visible area.
92
+ *
93
+ * @param { number } valueLeft - The numeric value for LEFT position.
94
+ * @param { number } valueRight - The numeric value for RIGHT position.
95
+ */
96
+ private overlayOutsideCheckX;
97
+ /**
98
+ * @description
99
+ * Boundary information such as position (top, bottom, left, right) & size (width, height).
100
+ */
101
+ private boundaryData;
102
+ /**
103
+ * @description
104
+ * The Trigger element to which the Overlay will be attached to.
105
+ */
106
+ get triggerElement(): HTMLElement;
107
+ set triggerElement(triggerElement: HTMLElement);
108
+ /**
109
+ * @description
110
+ * The Overlay element.
111
+ */
112
+ get overlayElement(): HTMLElement;
113
+ set overlayElement(overlayElement: HTMLElement);
114
+ /**
115
+ * @description
116
+ * Process and save the desired position & alignment.
117
+ */
118
+ private set overlayPosition(value);
119
+ /**
120
+ * @description
121
+ * Verifies if the given position is a valid value.
122
+ *
123
+ * @param { string } position - A given position (top, bottom, left, right).
124
+ * @returns { boolean }
125
+ */
126
+ private isValidPosition;
127
+ /**
128
+ * @description
129
+ * Verifies if the given alignment is a valid value.
130
+ *
131
+ * @param { string } alignment - A given alignment (start, center, end).
132
+ * @returns { boolean }
133
+ */
134
+ private isValidAlignment;
135
+ /**
136
+ * @description
137
+ * The position strategy to use (Fixed or Absolute).
138
+ */
139
+ get positionStrategy(): OverlayBasePositionStrategy;
140
+ set positionStrategy(positionStrategy: OverlayBasePositionStrategy);
141
+ /**
142
+ * @description
143
+ * Sets the allowed positions.
144
+ */
145
+ private set positionsAllowed(value);
146
+ /**
147
+ * @description
148
+ * Sets the allowed opposite positions.
149
+ */
150
+ private set oppositePositionsAllowed(value);
151
+ /**
152
+ * @description
153
+ * Verifies if the given position is allowed.
154
+ *
155
+ * @param { string } position - A given position (top, bottom, left, right).
156
+ * @returns { boolean }
157
+ */
158
+ private isPositionAllowed;
159
+ /**
160
+ * @description
161
+ * Sets the allowed alignments.
162
+ */
163
+ private set alignmentsAllowed(value);
164
+ /**
165
+ * @description
166
+ * Verifies if the given alignment is allowed.
167
+ *
168
+ * @param { string } alignment - A given alignment (start, center, end).
169
+ * @returns { boolean }
170
+ */
171
+ private isAlignmentAllowed;
172
+ private get isAlignmentStartAllowed();
173
+ private get isAlignmentCenterAllowed();
174
+ private get isAlignmentEndAllowed();
175
+ private get isAlignmentCenterOnly();
176
+ private get isDesiredAlignmentStart();
177
+ private get isDesiredAlignmentCenter();
178
+ private get isDesiredAlignmentEnd();
179
+ /**
180
+ * @description
181
+ * Sets a custom Boundary element.
182
+ *
183
+ * @default <body>
184
+ */
185
+ get boundaryElement(): HTMLElement;
186
+ set boundaryElement(boundaryElement: HTMLElement);
187
+ /**
188
+ * @description
189
+ * Checks if the Boundary is the `<body>` or not (custom).
190
+ */
191
+ private get isBoundaryCustom();
192
+ /**
193
+ * @description
194
+ * Considers an extra (safe) space for the Viewport.
195
+ */
196
+ get safeSpace(): OverlayBaseSafeSpace;
197
+ set safeSpace(safeSpace: OverlayBaseSafeSpace);
198
+ /**
199
+ * @description
200
+ * Distance between Trigger & Overlay.
201
+ */
202
+ get offsetSize(): number;
203
+ set offsetSize(offsetSize: number);
204
+ /**
205
+ * @description
206
+ * Overlay alignment is fluid, it doesn't make jumps between Start, Center or End.
207
+ * The Overlay will stick to the edges of the Viewport/Boundary when reaches any of them.
208
+ */
209
+ get fluidAlignment(): boolean;
210
+ set fluidAlignment(fluidAlignment: boolean);
211
+ /**
212
+ * @description
213
+ * Overlay size is fluid, it will adapt its width (for `left`/`right` positions) or
214
+ * height (for `top`/`bottom` positions) to the available free space.
215
+ *
216
+ * Hand in hand with this, would be good to establish `maxSize` values.
217
+ */
218
+ get fluidSize(): boolean;
219
+ set fluidSize(fluidSize: boolean);
220
+ get isTop(): boolean;
221
+ get isBottom(): boolean;
222
+ get isLeft(): boolean;
223
+ get isRight(): boolean;
224
+ get isStart(): boolean;
225
+ get isCenter(): boolean;
226
+ get isEnd(): boolean;
227
+ get isTopBottom(): boolean;
228
+ /**
229
+ * @description
230
+ * Distance between Trigger & Overlay.
231
+ */
232
+ private get triggerOverlayDistance();
233
+ /**
234
+ * @description
235
+ * Real Width difference between Trigger & Overlay.
236
+ *
237
+ * Positive value means the Overlay is wider than the Trigger.
238
+ */
239
+ private get triggerOverlayDifferenceWidth();
240
+ /**
241
+ * @description
242
+ * Real Height difference between Trigger & Overlay.
243
+ *
244
+ * Positive value means the Overlay is higher than the Trigger.
245
+ */
246
+ private get triggerOverlayDifferenceHeight();
247
+ /**
248
+ * @description
249
+ * Original Width difference between Trigger & Overlay.
250
+ *
251
+ * Positive value means the Overlay is wider than the Trigger.
252
+ */
253
+ private get triggerOverlayOriginalDifferenceWidth();
254
+ /**
255
+ * @description
256
+ * Original Height difference between Trigger & Overlay.
257
+ *
258
+ * Positive value means the Overlay is higher than the Trigger.
259
+ */
260
+ private get triggerOverlayOriginalDifferenceHeight();
261
+ /**
262
+ * @description
263
+ * Distance between Boundary & Viewport for the given Position.
264
+ *
265
+ * @param { OverlayBasePosition } position - A given position (top, bottom, left, right).
266
+ * @returns { number } The number of pixels between both elements.
267
+ */
268
+ private boundaryViewportDistance;
269
+ /**
270
+ * @description
271
+ * Distance between Trigger & Boundary for the given Position.
272
+ * This will take into consideration a given custom Boundary and/or Safe Space.
273
+ *
274
+ * @param { OverlayBasePosition } position - A given position (top, bottom, left, right).
275
+ * @returns { number } The number of pixels between both elements.
276
+ */
277
+ triggerBoundaryDistance(position: OverlayBasePosition): number;
278
+ /**
279
+ * @description
280
+ * Verifies if there is enough vertical/horizontal available space for the Overlay to fit.
281
+ */
282
+ private enoughAlignmentSpace;
283
+ /**
284
+ * @description
285
+ * Verifies if there is enough horizontal available space (horizontal scrolling)
286
+ * for the Overlay to fit.
287
+ */
288
+ private get overlayFitsHorizontally();
289
+ /**
290
+ * @description
291
+ * Verifies if there is enough vertical available space (vertical scrolling)
292
+ * for the Overlay to fit.
293
+ */
294
+ private get overlayFitsVertically();
295
+ /**
296
+ * @description
297
+ * Verifies if there is enough free space at the given position for the Overlay to fit.
298
+ *
299
+ * @param { OverlayBasePosition } position - A given position (top, bottom, left, right).
300
+ * @returns { boolean }
301
+ */
302
+ private enoughBoundarySpace;
303
+ /**
304
+ * @description
305
+ * Returns if the given Position is allowed and has enough space for the Overlay to fit.
306
+ */
307
+ private canPositionAt;
308
+ /**
309
+ * @description
310
+ * Calculates and returns the vertical alignment (for `left`/`right` positions).
311
+ */
312
+ private get getAlignmentVertically();
313
+ /**
314
+ * @description
315
+ * Calculates and return the horizontal alignment (for `top`/`bottom` positions).
316
+ */
317
+ private get getAlignmentHorizontally();
318
+ /**
319
+ * @description
320
+ * Calculates the right alignment based on the
321
+ *
322
+ * @param { number } distance - The main distance (`top` for vertical alignment, `left` for horizontal alignment).
323
+ * @param { boolean } canBeCentered - If the Overlay can be aligned to the center.
324
+ * @param { boolean } canBeStart - If the Overlay can be aligned to the start.
325
+ * @param { boolean } canBeEnd - If the Overlay can be aligned to the end.
326
+ * @returns { OverlayBaseAlignment } The alignment.
327
+ */
328
+ private getFinalAlignment;
329
+ /**
330
+ * @description
331
+ * Calculates maximum size (`width` or `height`) in case the overlay does not fit in the screen.
332
+ */
333
+ private get getMaxSize();
334
+ /**
335
+ * @description
336
+ * Returns the final Position, Alignment, Render and MaxSize.
337
+ */
338
+ private get getCalculatedPosition();
339
+ /**
340
+ * @description
341
+ * Returns the Overlay's `top` or `bottom` render Position/Alignment for the Strategy FIXED.
342
+ */
343
+ private get getFixedY();
344
+ /**
345
+ * @description
346
+ * Returns the Overlay's `left` or `right` render Position/Alignment for the Strategy FIXED.
347
+ */
348
+ private get getFixedX();
349
+ /**
350
+ * @description
351
+ * Returns the absolute offset distance for the given position.
352
+ *
353
+ * @param { 'left' | 'top' } position - A given position (left, top).
354
+ * @returns { number } The number of pixels between the chosen side and the Boundary.
355
+ */
356
+ private getAbsoluteOffset;
357
+ /**
358
+ * @description
359
+ * Returns the scroll distance for the given position.
360
+ *
361
+ * @param { 'left' | 'top' } position - A given position (left, top).
362
+ * @returns { number } The number of pixels scrolled on the chosen side for the Boundary.
363
+ */
364
+ private getAbsoluteScroll;
365
+ /**
366
+ * @description
367
+ * Returns the Overlay's `top` or `bottom` render Position/Alignment for the Strategy ABSOLUTE.
368
+ */
369
+ private get getAbsoluteY();
370
+ /**
371
+ * @description
372
+ * Returns the Overlay's `left` or `right` render Position/Alignment for the Strategy ABSOLUTE.
373
+ */
374
+ private get getAbsoluteX();
375
+ /**
376
+ * @description
377
+ * If the Trigger is (at least partially) outside of the Boundary on one or both of its axes
378
+ * and the viewport size is not big enough to fit the overlay,
379
+ * it'll choose the side with more available space.
380
+ */
381
+ private get getAutoPosition();
382
+ /**
383
+ * @description
384
+ * Returns the final position.
385
+ */
386
+ private get getPosition();
387
+ /**
388
+ * @description
389
+ * Returns the final alignment.
390
+ */
391
+ private get getAlignment();
392
+ /**
393
+ * @description
394
+ * Base listeners for Page Scroll, Page Resize, Boundary Scroll & Update on demand.
395
+ */
396
+ get overlayListeners$(): Observable<void>;
397
+ /**
398
+ * @description
399
+ * Update on demand to return recalculated position/alignment data.
400
+ */
401
+ recalculate(): void;
402
+ /**
403
+ * @description
404
+ * Update Overlay ClientRect data on demand in case its content changes.
405
+ *
406
+ * This will force recalculations.
407
+ */
408
+ updateOverlaySize(): void;
409
+ /**
410
+ * @description
411
+ * Sets / updates the basic config.
412
+ */
413
+ setBaseConfig(customConfig?: OverlayBaseConfig): void;
414
+ /**
415
+ * @description
416
+ * Assigns the provided overlay element and starts listening to the scroll & resize to calculate and return the best position for it.
417
+ *
418
+ * @param { HTMLElement } overlayElement - The `overlay` HTML element to base all calculation on.
419
+ * @param { number } debounceTimeMs - Delay between emissions.
420
+ * @returns { Observable<OverlayBaseCalculatedPosition> } An `observable` with
421
+ * - the `position` (top, bottom, left, right),
422
+ * - the `alignment` (start, center, end),
423
+ * - the position to `render` (top, bottom, left, right) based on the chosen `position strategy` and
424
+ * - the `max size` (if "`fluidSize`" is active) that'll provide max width/height so the overlay is always visible on screen.
425
+ */
426
+ attachOverlay(overlayElement: HTMLElement, debounceTimeMs?: number): Observable<OverlayBaseCalculatedPosition>;
427
+ /**
428
+ * @description
429
+ * Detaches the Overlay and removes the scroll listener for custom Boundary.
430
+ */
431
+ detachOverlay(): void;
432
+ /**
433
+ * @description
434
+ * Gets all the necessary ClientRect, Boundary and Viewport data.
435
+ */
436
+ private getElementsSizeInfo;
437
+ private getTriggerClientRect;
438
+ private getOverlayClientRect;
439
+ private getOverlayOriginalClientRect;
440
+ private getBoundaryClientRect;
441
+ private getBoundaryCalculatedRect;
442
+ private getBoundaryScrollSize;
443
+ private getViewportSizeSafe;
444
+ private getNumericValue;
445
+ private getBooleanValue;
446
+ }
@@ -0,0 +1 @@
1
+ export declare const ERROR_NO_TRIGGER_PROVIDED: () => string;
@@ -0,0 +1,64 @@
1
+ import { OverlayBaseRenderPosition } from './overlay-base.type.private';
2
+ export declare enum POSITION {
3
+ TOP = "top",
4
+ BOTTOM = "bottom",
5
+ LEFT = "left",
6
+ RIGHT = "right"
7
+ }
8
+ export declare enum ALIGNMENT {
9
+ START = "start",
10
+ END = "end",
11
+ CENTER = "center"
12
+ }
13
+ export declare enum POSITION_STRATEGY {
14
+ FIXED = "fixed",
15
+ ABSOLUTE = "absolute"
16
+ }
17
+ export declare const OVERLAY_BASE_DEFAULTS: Required<Omit<OverlayBaseConfig, 'trigger' | 'boundary'>>;
18
+ export declare type OverlayBasePosition = `${POSITION}`;
19
+ export declare type OverlayBaseAlignment = `${ALIGNMENT}`;
20
+ export declare type OverlayBasePositionStrategy = `${POSITION_STRATEGY}`;
21
+ export declare type OverlayBaseSafeSpace = Partial<{
22
+ [key in POSITION]: number;
23
+ }>;
24
+ export declare type OverlayBaseMaxSize = Partial<{
25
+ width: number | null;
26
+ height: number | null;
27
+ }>;
28
+ export declare type OverlayBasePositionInput = OverlayBasePosition | `${POSITION}-${ALIGNMENT}` | [OverlayBasePosition, OverlayBaseAlignment];
29
+ export declare type OverlayBasePositionsAllowed = 'auto' | 'opposite' | string | OverlayBasePosition | OverlayBasePosition[];
30
+ export declare type OverlayBaseAlignmentsAllowed = 'auto' | 'center' | 'edges' | string | OverlayBaseAlignment | OverlayBaseAlignment[];
31
+ export declare type OverlayBaseCalculatedPosition = {
32
+ /** @description Returns the position (top, bottom, left, right). */
33
+ position: OverlayBasePosition;
34
+ /** @description Returns the alignment (start, end, center). */
35
+ alignment: OverlayBaseAlignment;
36
+ /** @description Returns the numeric value to position the overlay (top, bottom, left, right). */
37
+ render: OverlayBaseRenderPosition;
38
+ /** @description Returns the maximum size (width, height). */
39
+ maxSize: OverlayBaseMaxSize;
40
+ };
41
+ export declare type OverlayBaseConfig = Partial<{
42
+ /** @description The trigger to which the overlay will be attached. */
43
+ trigger: HTMLElement | DOMRect;
44
+ /** @description To define a custom boundary, such as wrappers with established overflow. @default <body> */
45
+ boundary: HTMLElement;
46
+ /** @description The desired position and alignment. @default 'top-center' */
47
+ position: OverlayBasePositionInput;
48
+ /** @description Whether a `fixed` or `absolute` position will be used. @default 'fixed' */
49
+ positionStrategy: OverlayBasePositionStrategy;
50
+ /** @description To establish which positions are allowed when repositioning is needed. @default 'auto' */
51
+ positionsAllowed: OverlayBasePositionsAllowed;
52
+ /** @description To establish which alignments are allowed when repositioning is needed. @default 'auto' */
53
+ alignmentsAllowed: OverlayBaseAlignmentsAllowed;
54
+ /** @description The space between the overlay and its trigger (translated to pixels). @default 5 */
55
+ offsetSize: number;
56
+ /** @description To establish extra safe space to the viewport's edges in case some fixed areas are present. @default { top: 0, bottom: 0, left: 0, right: 0 } */
57
+ safeSpace: OverlayBaseSafeSpace;
58
+ /** @description To establish whether the overlay alignment will stick to the edges of the viewport/boundary. @default false */
59
+ fluidAlignment: boolean;
60
+ /** @description To establish whether the overlay size will adjust to the free space or stay as its original size. @default true */
61
+ fluidSize: boolean;
62
+ /** @description To allow listening for page scrolling. @default true */
63
+ allowScrollListener: boolean;
64
+ }>;
@@ -0,0 +1,31 @@
1
+ import { POSITION, OverlayBasePosition, OverlayBaseAlignment } from './overlay-base.type';
2
+ export declare type BoundaryData = {
3
+ [key in POSITION]: number;
4
+ } & {
5
+ width: number;
6
+ height: number;
7
+ };
8
+ export declare type OverlayBaseRenderPosition = OverlayBasePositionX & OverlayBasePositionY;
9
+ export declare type ScrollSize = {
10
+ horizontal: number;
11
+ vertical: number;
12
+ };
13
+ export declare type ViewportSize = {
14
+ width: number;
15
+ height: number;
16
+ };
17
+ export declare type OverlayBaseAllowed = {
18
+ positions: OverlayBasePosition[];
19
+ alignments: OverlayBaseAlignment[];
20
+ };
21
+ export declare type OverlayBaseSquareAreas = {
22
+ [key in POSITION]: number;
23
+ };
24
+ export declare type OverlayBasePositionY = {
25
+ top: number | null;
26
+ bottom: number | null;
27
+ };
28
+ export declare type OverlayBasePositionX = {
29
+ left: number | null;
30
+ right: number | null;
31
+ };
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@a11y-ngx/overlay-base",
3
+ "version": "1.0.0",
4
+ "description": "An overlay positioning system that allows them to remain within the visible area of the viewport or a given custom boundary (such as containers with overflow) and thus prevent being out of the screen",
5
+ "keywords": [
6
+ "overlay",
7
+ "position",
8
+ "positioning",
9
+ "viewport",
10
+ "boundary",
11
+ "overflow",
12
+ "accessibility",
13
+ "angular"
14
+ ],
15
+ "homepage": "https://github.com/LDV2k3/a11y-libraries/tree/master/projects/a11y-ngx/overlay-base#readme",
16
+ "bugs": {
17
+ "url": "https://github.com/LDV2k3/a11y-libraries/issues",
18
+ "email": "lucho.development@gmail.com"
19
+ },
20
+ "license": "MPL-2.0",
21
+ "author": {
22
+ "name": "Luciano Del Vacchio",
23
+ "email": "lucho.development@gmail.com",
24
+ "url": "https://github.com/LDV2k3/"
25
+ },
26
+ "peerDependencies": {
27
+ "@angular/common": "^12.2.0",
28
+ "@angular/core": "^12.2.0"
29
+ },
30
+ "dependencies": {
31
+ "tslib": "^1.10.0"
32
+ },
33
+ "main": "bundles/a11y-ngx-overlay-base.umd.js",
34
+ "module": "fesm2015/a11y-ngx-overlay-base.js",
35
+ "es2015": "fesm2015/a11y-ngx-overlay-base.js",
36
+ "esm2015": "esm2015/a11y-ngx-overlay-base.js",
37
+ "fesm2015": "fesm2015/a11y-ngx-overlay-base.js",
38
+ "typings": "a11y-ngx-overlay-base.d.ts",
39
+ "sideEffects": false
40
+ }
@@ -0,0 +1,2 @@
1
+ export * from './lib/overlay-base.abstract';
2
+ export * from './lib/overlay-base.type';