-
Notifications
You must be signed in to change notification settings - Fork 78
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: added injectDocumentVisibility utility
Co-authored-by: Enea Jahollari <jahollarienea14@gmail.com>
- Loading branch information
1 parent
0275c02
commit f6b5e77
Showing
11 changed files
with
214 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
18 changes: 18 additions & 0 deletions
18
apps/test-app/src/app/document-visibility-state/document-visibility-state.component.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import { Component, effect } from '@angular/core'; | ||
import { injectDocumentVisibility } from 'ngxtension/document-visibility-state'; | ||
|
||
@Component({ | ||
template: ` | ||
{{ visibilityState() }} | ||
`, | ||
standalone: true, | ||
}) | ||
export default class DocumentVisibilityStateComponent { | ||
visibilityState = injectDocumentVisibility(); | ||
|
||
constructor() { | ||
effect(() => { | ||
console.log(this.visibilityState()); | ||
}); | ||
} | ||
} |
40 changes: 40 additions & 0 deletions
40
docs/src/content/docs/utilities/Injectors/document-visibility-state.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
--- | ||
title: documentVisibilityState | ||
description: ngxtension/document-visibility-state | ||
badge: stable | ||
contributor: fiorelozere | ||
--- | ||
|
||
`injectDocumentVisibility` is a utility function that provides a reactive signal reflecting the current visibility state of the document. This function is particularly useful for scenarios such as tracking user presence on a webpage (e.g., for analytics or pausing/resuming activities) and can be adapted for use with iframes or in testing environments. | ||
|
||
```ts | ||
import { injectDocumentVisibility } from 'ngxtension/document-visibility-state'; | ||
``` | ||
|
||
## Usage | ||
|
||
`injectDocumentVisibility` accepts an optional parameter `options` which can include a custom `document` and an `Injector` instance. The `document` parameter is particularly useful for testing scenarios or when needing to track the visibility of an iframe. The `injector` allows for dependency injection, providing more flexibility and facilitating testable code by decoupling from the global state or context. | ||
|
||
```ts | ||
const visibilityState = injectDocumentVisibility(); | ||
|
||
effect(() => { | ||
console.log(visibilityState.value); | ||
}); | ||
``` | ||
|
||
## API | ||
|
||
```ts | ||
function injectDocumentVisibility(options?: InjectDocumentVisibilityOptions): Signal<DocumentVisibilityState>; | ||
``` | ||
|
||
### Parameters | ||
|
||
- `options` (optional): An object that can have the following properties: | ||
- `document`: A custom `Document` instance, defaulting to the global `document` object. | ||
- `injector`: An `Injector` instance for Angular's dependency injection. | ||
|
||
### Returns | ||
|
||
- `Signal<DocumentVisibilityState>`: A reactive signal that emits the current `DocumentVisibilityState` (e.g., `"visible"`, `"hidden"`) and updates when the document visibility state changes. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# ngxtension/document-visibility-state | ||
|
||
Secondary entry point of `ngxtension`. It can be used by importing from `ngxtension/document-visibility-state`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{ | ||
"lib": { | ||
"entryFile": "src/index.ts" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
{ | ||
"name": "ngxtension/document-visibility-state", | ||
"$schema": "../../../node_modules/nx/schemas/project-schema.json", | ||
"projectType": "library", | ||
"sourceRoot": "libs/ngxtension/document-visibility-state/src", | ||
"targets": { | ||
"test": { | ||
"executor": "@nx/jest:jest", | ||
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"], | ||
"options": { | ||
"jestConfig": "libs/ngxtension/jest.config.ts", | ||
"testPathPattern": ["document-visibility-state"], | ||
"passWithNoTests": true | ||
}, | ||
"configurations": { | ||
"ci": { | ||
"ci": true, | ||
"codeCoverage": true | ||
} | ||
} | ||
}, | ||
"lint": { | ||
"executor": "@nx/eslint:lint", | ||
"outputs": ["{options.outputFile}"], | ||
"options": { | ||
"lintFilePatterns": [ | ||
"libs/ngxtension/document-visibility-state/**/*.ts", | ||
"libs/ngxtension/document-visibility-state/**/*.html" | ||
] | ||
} | ||
} | ||
} | ||
} |
37 changes: 37 additions & 0 deletions
37
libs/ngxtension/document-visibility-state/src/document-visibility-state.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import { Component } from '@angular/core'; | ||
import { TestBed } from '@angular/core/testing'; | ||
import { injectDocumentVisibility } from './document-visibility-state'; | ||
|
||
describe(injectDocumentVisibility.name, () => { | ||
@Component({ standalone: true, template: '{{visibilityState()}}' }) | ||
class Test { | ||
visibilityState = injectDocumentVisibility(); | ||
} | ||
|
||
function setup() { | ||
const fixture = TestBed.createComponent(Test); | ||
fixture.detectChanges(); | ||
return fixture.componentInstance; | ||
} | ||
|
||
function triggerVisibilityChange(newState: DocumentVisibilityState) { | ||
// Change the visibility state | ||
Object.defineProperty(document, 'visibilityState', { | ||
writable: true, | ||
configurable: true, | ||
value: newState, | ||
}); | ||
|
||
// Dispatch the event | ||
const event = new Event('visibilitychange'); | ||
document.dispatchEvent(event); | ||
} | ||
|
||
it('should work properly', () => { | ||
const cmp = setup(); | ||
triggerVisibilityChange('hidden'); | ||
expect(cmp.visibilityState()).toEqual('hidden'); | ||
triggerVisibilityChange('visible'); | ||
expect(cmp.visibilityState()).toEqual('visible'); | ||
}); | ||
}); |
64 changes: 64 additions & 0 deletions
64
libs/ngxtension/document-visibility-state/src/document-visibility-state.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
import { DOCUMENT } from '@angular/common'; | ||
import { | ||
Injector, | ||
inject, | ||
runInInjectionContext, | ||
type Signal, | ||
} from '@angular/core'; | ||
import { toSignal } from '@angular/core/rxjs-interop'; | ||
import { assertInjector } from 'ngxtension/assert-injector'; | ||
import { Observable, fromEvent, map, startWith } from 'rxjs'; | ||
|
||
export interface InjectDocumentVisibilityOptions { | ||
/* | ||
* Specify a custom `document` instance, e.g. working with iframes or in testing environments. | ||
*/ | ||
document?: Document; | ||
|
||
/* | ||
* Specify a custom `Injector` instance to use for dependency injection. | ||
*/ | ||
injector?: Injector; | ||
} | ||
|
||
/** | ||
* Injects and monitors the current document visibility state. Emits the state initially and then emits on every change. | ||
* | ||
* This function is useful for scenarios like tracking user presence on a page (e.g., for analytics or pausing/resuming activities) and is adaptable for use with iframes or in testing environments. | ||
* | ||
* @example | ||
* ```ts | ||
const visibilityState = injectDocumentVisibility(); | ||
effect(() => { | ||
console.log(this.visibilityState()); | ||
}); | ||
* ``` | ||
* | ||
* @param options An optional object with the following properties: | ||
* - `document`: (Optional) Specifies a custom `Document` instance. This is useful when working with iframes or in testing environments where the global `document` might not be appropriate. | ||
* - `injector`: (Optional) Specifies a custom `Injector` instance for dependency injection. This allows for more flexible and testable code by decoupling from a global state or context. | ||
* | ||
* @returns A signal that emits the current `DocumentVisibilityState` (`"visible"`, `"hidden"`, etc.) initially and whenever the document visibility state changes. | ||
*/ | ||
|
||
export function injectDocumentVisibility( | ||
options?: InjectDocumentVisibilityOptions | ||
): Signal<DocumentVisibilityState> { | ||
const injector = assertInjector(injectDocumentVisibility, options?.injector); | ||
|
||
return runInInjectionContext(injector, () => { | ||
const doc: Document = options?.document ?? inject(DOCUMENT); | ||
|
||
const docVisible$: Observable<DocumentVisibilityState> = fromEvent( | ||
doc, | ||
'visibilitychange' | ||
).pipe( | ||
startWith(doc.visibilityState), | ||
map(() => doc.visibilityState) | ||
); | ||
|
||
return toSignal<DocumentVisibilityState>(docVisible$, { | ||
requireSync: true, | ||
}); | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './document-visibility-state'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters