-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: resize observer store in core package
- Loading branch information
1 parent
d0065e6
commit 4523243
Showing
16 changed files
with
505 additions
and
44 deletions.
There are no files selected for viewing
54 changes: 54 additions & 0 deletions
54
angular/demo/src/app/samples/resizeObserver/resizeObserver.route.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,54 @@ | ||
import {AgnosUIAngularModule, createResizeObserver, toAngularSignal} from '@agnos-ui/angular'; | ||
import {UseDirective} from '@agnos-ui/angular-headless'; | ||
import {ChangeDetectionStrategy, Component, computed} from '@angular/core'; | ||
|
||
@Component({ | ||
standalone: true, | ||
imports: [UseDirective, AgnosUIAngularModule], | ||
changeDetection: ChangeDetectionStrategy.OnPush, | ||
template: ` | ||
<div class="demo-resize-observer"> | ||
<label for="resizable">Resizable textarea:</label> | ||
<textarea | ||
[auUse]="resizeDirective" | ||
name="resizable" | ||
id="resizable" | ||
rows="6" | ||
cols="50" | ||
class="form-control" | ||
[class.fontsize]="observedHeight$() && observedHeight$()! > 200" | ||
[style.height.px]="heightValue" | ||
> | ||
This simple example is using the resizeObserver feature from @agnos-ui/core and displays the height of the textarea below it. | ||
Modify the height to more than 200 px and see the font size changing. | ||
</textarea | ||
> | ||
<div> | ||
Textarea content height: <span id="dynamic-height">{{ observedHeight$() }}</span | ||
>px | ||
</div> | ||
<button type="button" class="btn btn-primary m-2" id="decreaseHeight" (click)="decreaseHeight()">Height --</button> | ||
<button type="button" class="btn btn-primary m-2" id="increaseHeight" (click)="increaseHeight()">Height ++</button> | ||
</div> | ||
`, | ||
styles: "@import '@agnos-ui/common/samples/resizeobserver/resizeobserver.scss';", | ||
}) | ||
export default class ResizeObserverComponent { | ||
heightValue = 180; | ||
|
||
readonly resizeObserver = createResizeObserver(); | ||
|
||
readonly resizeDirective = this.resizeObserver.directive; | ||
|
||
readonly dimensions$ = toAngularSignal(this.resizeObserver.dimensions$); | ||
|
||
readonly observedHeight$ = computed(() => this.dimensions$()?.contentRect?.height); | ||
|
||
increaseHeight() { | ||
this.heightValue = (this.observedHeight$() || 0) + 50; | ||
} | ||
|
||
decreaseHeight() { | ||
this.heightValue = this.observedHeight$() ? this.observedHeight$()! - 50 : 0; | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
div.demo-resize-observer { | ||
#resizable { | ||
resize: both; | ||
width: 400px; | ||
} | ||
.fontsize { | ||
font-size: x-large; | ||
} | ||
} |
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
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,108 @@ | ||
import {describe, expect, test, vi, vitest} from 'vitest'; | ||
import {createResizeObserver} from './resizeObserver'; | ||
|
||
describe(`ResizeObserver service`, () => { | ||
test(`Should have original dimensions as intial observed values`, async () => { | ||
const observedElement = document.body.appendChild(document.createElement('textarea')); | ||
observedElement.style.height = '100px'; | ||
observedElement.style.width = '100px'; | ||
const {directive, dimensions$} = createResizeObserver(); | ||
|
||
let emits = 0; | ||
let dimensions; | ||
const unsubscribe = dimensions$.subscribe((dim) => { | ||
emits++; | ||
dimensions = dim; | ||
}); | ||
|
||
// store first value | ||
await vi.waitUntil(() => emits === 1); | ||
expect(dimensions).toBeUndefined(); | ||
|
||
const directiveApplied = directive(observedElement); | ||
// element default values | ||
await vi.waitUntil(() => emits === 2); | ||
expect(dimensions!.contentRect).toMatchObject({width: 100, height: 100}); | ||
// cleanup | ||
observedElement.parentElement?.removeChild(observedElement); | ||
unsubscribe(); | ||
directiveApplied?.destroy?.(); | ||
}); | ||
|
||
test(`Should give the dimensions of observed element`, async () => { | ||
const observedElement = document.body.appendChild(document.createElement('div')); | ||
observedElement.style.height = '100px'; | ||
observedElement.style.width = '100px'; | ||
const {directive, dimensions$} = createResizeObserver(); | ||
let emits = 0; | ||
let dimensions; | ||
const unsubscribe = dimensions$.subscribe((dim) => { | ||
emits++; | ||
dimensions = dim; | ||
}); | ||
// store first value | ||
await vi.waitUntil(() => emits === 1); | ||
expect(dimensions).toBeUndefined(); | ||
|
||
const directiveApplied = directive(observedElement); | ||
// element default values | ||
await vi.waitUntil(() => emits === 2); | ||
expect(dimensions!.contentRect).toMatchObject({width: 100, height: 100}); | ||
|
||
// trigger first resize | ||
observedElement.style.height = '200px'; | ||
observedElement.style.width = '200px'; | ||
await vi.waitUntil(() => emits === 3); | ||
expect(dimensions!.contentRect).toMatchObject({width: 200, height: 200}); | ||
|
||
// trigger second resize | ||
observedElement.style.width = '300px'; | ||
await vi.waitUntil(() => emits === 4); | ||
expect(dimensions!.contentRect).toMatchObject({width: 300, height: 200}); | ||
|
||
//cleanup | ||
observedElement.parentElement?.removeChild(observedElement); | ||
directiveApplied?.destroy?.(); | ||
unsubscribe(); | ||
}); | ||
|
||
test(`Should keep current element when trying to add new one to the directive`, async () => { | ||
const consoleErrorSpy = vitest.spyOn(console, 'error').mockImplementation(() => {}); | ||
const textarea = document.body.appendChild(document.createElement('textarea')); | ||
|
||
textarea.style.height = '100px'; | ||
textarea.style.width = '100px'; | ||
|
||
const {directive, dimensions$} = createResizeObserver(); | ||
|
||
const div = document.body.appendChild(document.createElement('div')); | ||
div.style.height = '200px'; | ||
|
||
let emits = 0; | ||
let dimensions; | ||
const unsubscribe = dimensions$.subscribe((dim) => { | ||
emits++; | ||
dimensions = dim; | ||
}); | ||
// store first value | ||
await vi.waitUntil(() => emits === 1); | ||
expect(dimensions).toBeUndefined(); | ||
|
||
const directiveTextAreaApplied = directive(textarea); | ||
const directiveDivApplied = directive(div); | ||
|
||
expect(consoleErrorSpy).toHaveBeenCalledOnce(); | ||
expect(consoleErrorSpy.mock.calls[0][0]).toBe('The directive cannot be used on multiple elements.'); | ||
|
||
//textarea default values | ||
await vi.waitUntil(() => emits === 2); | ||
expect(dimensions!.contentRect).toMatchObject({width: 100, height: 100}); | ||
|
||
// cleanup | ||
textarea.parentElement?.removeChild(textarea); | ||
div.parentElement?.removeChild(div); | ||
unsubscribe(); | ||
directiveDivApplied?.destroy?.(); | ||
directiveTextAreaApplied?.destroy?.(); | ||
}); | ||
}); |
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,41 @@ | ||
import type {ReadableSignal} from '@amadeus-it-group/tansu'; | ||
import {derived} from '@amadeus-it-group/tansu'; | ||
import {createStoreDirective} from '../utils/directive'; | ||
import {noop} from '../utils/internal/func'; | ||
|
||
/** | ||
* Create a resize observer object | ||
* @returns An object containing the store with the dimentions of observed element (ResizeObserverEntry), the directive to be applied to the html element to be observed | ||
*/ | ||
export const createResizeObserver = () => { | ||
const {element$, directive} = createStoreDirective(); | ||
|
||
const observedElement$ = derived<ResizeObserverEntry | undefined, ReadableSignal<HTMLElement | null>>( | ||
element$, | ||
(element, set) => { | ||
if (element === null) { | ||
return noop; | ||
} | ||
|
||
const observer = new ResizeObserver((entries) => { | ||
set(entries[0]); | ||
}); | ||
|
||
observer.observe(element); | ||
|
||
return () => observer?.disconnect(); | ||
}, | ||
undefined, | ||
); | ||
|
||
return { | ||
/** | ||
* Store which contains the dimensions of the observed element (ResizeObserverEntry type) | ||
* See the [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserverEntry) | ||
*/ | ||
dimensions$: observedElement$, | ||
|
||
/** Directive to be attached to html element in order to listen to resize events */ | ||
directive, | ||
}; | ||
}; |
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
Oops, something went wrong.