Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/web-api-demo/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<body>
<h1>@withease/web-api</h1>
<ul>
<li><a href="/window.html">window</a></li>
<li><a href="/media-query.html">media-query</a></li>
<li><a href="/network.html">network</a></li>
<li><a href="/page-visibility.html">page-visibility</a></li>
Expand Down
36 changes: 36 additions & 0 deletions apps/web-api-demo/src/window.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { trackWindowDimensions } from '@withease/web-api';
import { Store, createEvent } from 'effector';

const bindStoreToElement = (store: Store<number>, elementId: string) => {
const element = document.querySelector(elementId)!;

store.watch((value) => {
element.textContent = JSON.stringify(value);
});
};

const appStarted = createEvent();

const {
$scrollY,
$scrollX,
$innerWidth,
$innerHeight,
$outerWidth,
$outerHeight,
$screenLeft,
$screenTop,
} = trackWindowDimensions({
setup: appStarted,
});

bindStoreToElement($scrollY, '#scrollY');
bindStoreToElement($scrollX, '#scrollX');
bindStoreToElement($innerWidth, '#innerWidth');
bindStoreToElement($innerHeight, '#innerHeight');
bindStoreToElement($outerWidth, '#outerWidth');
bindStoreToElement($outerHeight, '#outerHeight');
bindStoreToElement($screenLeft, '#screenLeft');
bindStoreToElement($screenTop, '#screenTop');

appStarted();
25 changes: 25 additions & 0 deletions apps/web-api-demo/window.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>web-api demo</title>
<base href="/" />

<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" type="image/x-icon" href="favicon.ico" />
</head>
<body style="position: relative; min-height: 150vh; min-width: 150vw">
<section style="position: fixed; top: 0; left: 0">
<h2>Window</h2>
<p>scrollX: <span id="scrollX" /></p>
<p>scrollY: <span id="scrollY" /></p>
<p>innerWidth: <span id="innerWidth" /></p>
<p>innerHeight: <span id="innerHeight" /></p>
<p>outerWidth: <span id="outerWidth"></span></p>
<p>outerHeight: <span id="outerHeight"></span></p>
<p>screenLeft: <span id="screenLeft"></span></p>
<p>screenTop: <span id="screenTop"></span></p>
</section>
<script type="module" src="/src/window.ts"></script>
</body>
</html>
4 changes: 4 additions & 0 deletions apps/website/docs/.vitepress/config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ export default defineConfig({
text: 'Geolocation',
link: '/web-api/geolocation',
},
{
text: 'Window dimensions',
link: '/web-api/window_dimensions',
},
],
},
]),
Expand Down
80 changes: 80 additions & 0 deletions apps/website/docs/web-api/window_dimensions.live.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<script setup>
import { trackWindowDimensions } from '@withease/web-api';
import { createEvent } from 'effector';
import { useUnit } from 'effector-vue/composition';
import { onMounted } from 'vue';

const appStarted = createEvent();

const {
$scrollX,
$scrollY,
$innerWidth,
$innerHeight,
$outerWidth,
$outerHeight,
$screenTop,
$screenLeft,
} = trackWindowDimensions({
setup: appStarted,
});

const {
scrollX,
scrollY,
innerWidth,
innerHeight,
outerWidth,
outerHeight,
screenTop,
screenLeft,
} = useUnit({
$scrollX,
$scrollY,
$innerWidth,
$innerHeight,
$outerWidth,
$outerHeight,
$screenTop,
$screenLeft,
});

onMounted(appStarted);
</script>

<template>
<div class="wrapper">
<h2>Window</h2>
<p>
scrollX: <strong>{{ scrollX }}</strong>
</p>
<p>
scrollY: <strong>{{ scrollY }}</strong>
</p>
<p>
innerWidth: <strong>{{ innerWidth }}</strong>
</p>
<p>
innerHeight: <strong>{{ innerHeight }}</strong>
</p>
<p>
outerWidth: <strong>{{ outerWidth }}</strong>
</p>
<p>
outerHeight: <strong>{{ outerHeight }}</strong>
</p>
<p>
screenLeft: <strong>{{ screenLeft }}</strong>
</p>
<p>
screenTop: <strong>{{ screenTop }}</strong>
</p>
</div>
</template>

<style lang="css" scoped>
.wrapper {
min-width: 150vw;
min-height: 150vh;
}
</style>
58 changes: 58 additions & 0 deletions apps/website/docs/web-api/window_dimensions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
---
title: Window dimensions
---

# Window dimensions

Allows tracking window dimensions with [_Events_](https://effector.dev/en/api/effector/event/) and [_Stores_](https://effector.dev/docs/api/effector/store).

::: info

Uses [Window: resize event](https://developer.mozilla.org/en-US/docs/Web/API/Window/resize_event), [Window: scroll event](https://developer.mozilla.org/en-US/docs/Web/API/Document/scroll_event), [Window: requestAnimationFrame](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame) under the hood

:::

## Usage

All you need to do is to create an integration by calling `trackWindowDimensions` with an integration options:

- `setup`: after this [_Event_](https://effector.dev/en/api/effector/event/) all listeners will be installed, and the integration will be ready to use; it is required because it is better to use [explicit initialization _Event_ in the application](/magazine/explicit_start).
- `teardown?`: after this [_Event_](https://effector.dev/en/api/effector/event/) all listeners will be removed, and the integration will be ready to be destroyed.

```ts
import { trackWindowDimensions } from '@withease/web-api';

const {
$scrollY,
$scrollX,
$innerWidth,
$innerHeight,
$outerWidth,
$outerHeight,
$screenLeft,
$screenTop,
} = trackWindowDimensions({
setup: appStarted,
});
```

Returns an object with:

- `$scrollY`: [_Store_](https://effector.dev/docs/api/effector/store) with the current scroll position on the Y-axis
- `$scrollX`: [_Store_](https://effector.dev/docs/api/effector/store) with the current scroll position on the X-axis
- `$innerWidth`: [_Store_](https://effector.dev/docs/api/effector/store) with the current width of the viewport
- `$innerHeight`: [_Store_](https://effector.dev/docs/api/effector/store) with the current height of the viewport
- `$outerWidth`: [_Store_](https://effector.dev/docs/api/effector/store) with the current width of the entire window
- `$outerHeight`: [_Store_](https://effector.dev/docs/api/effector/store) with the current height of the entire window
- `$screenLeft`: [_Store_](https://effector.dev/docs/api/effector/store) with the current left position of the screen
- `$screenTop`: [_Store_](https://effector.dev/docs/api/effector/store) with the current top position of the screen

## Live demo

Let us show you a live demo of how it works. The following demo displays a `$scrollX`, `$innerWidth`, `$innerHeight`, `$outerWidth`, `$outerHeight`, `$screenLeft`, and `$screenTop` values of the current window dimensions. _Scroll, resize, and move the window to see how it works._

<script setup lang="ts">
import demoFile from './window_dimensions.live.vue?raw';
</script>

<LiveDemo :demoFile="demoFile" />
1 change: 1 addition & 0 deletions packages/web-api/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export { trackNetworkStatus } from './network_status';
export { trackMediaQuery } from './media_query';
export { trackPreferredLanguages } from './preferred_languages';
export { trackGeolocation } from './geolocation';
export { trackWindowDimensions } from './window';
36 changes: 36 additions & 0 deletions packages/web-api/src/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,3 +114,39 @@ export function readonly<T>(unit: EventCallable<T>): Event<T>;
export function readonly(unit: any) {
return unit.map((v: any) => v);
}

type UnsubscribeFn = () => void;
type UpdaterFn = (updateFn: () => void) => UnsubscribeFn;

export const createAutoUpdatedStore =
({ setup, teardown }: Setupable) =>
<T>(readValue: () => T, updater: UpdaterFn) => {
const store = createStore(readValue());
const update = createEvent<T>();

let unsubscribe: () => void | undefined;

sample({
clock: update,
target: store,
});

sample({
clock: setup,
target: createEffect(
() => (unsubscribe = updater(() => update(readValue())))
),
});

if (teardown) {
sample({
clock: teardown,
filter: () => Boolean(unsubscribe),
target: createEffect(() => {
unsubscribe!();
}),
});
}

return store;
};
Loading