Skip to content

Commit ab5069f

Browse files
authored
feat: add useMedia composable function (#20)
* feat: impelement useMedia * refactor: use useMedia for the color scheme composable * docs: added useMedia to the documentation and readme
1 parent cf76481 commit ab5069f

File tree

9 files changed

+101
-33
lines changed

9 files changed

+101
-33
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ Each composition function is designed to degrade gracefully so you can safely us
4040
- [Geo-location API](https://logaretm.github.io/vue-use-web/guide/geolocation.html).
4141
- [Localstorage API](https://logaretm.github.io/vue-use-web/guide/local-storage.html)
4242
- [Intersection Observer](https://logaretm.github.io/vue-use-web/guide/intersection-observer.html).
43+
- [Mouse Query](https://logaretm.github.io/vue-use-web/guide/media-query.html)
4344
- [Mouse Position](https://logaretm.github.io/vue-use-web/guide/mouse-position.html)
4445
- [Network Information API](https://logaretm.github.io/vue-use-web/guide/network.html).
4546
- [Preferred Color Scheme](https://logaretm.github.io/vue-use-web/guide/preferred-color-scheme.html).

docs/.vuepress/config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ module.exports = {
6565
'geolocation',
6666
'intersection-observer',
6767
'local-storage',
68+
'media-query',
6869
'mouse-position',
6970
'network',
7071
'preferred-color-scheme',

docs/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ npm install @vue/composition-api vue-use-web
4747
- [Geo-location API](./guide/geolocation.md).
4848
- [Local-storage API](./guide/local-storage.md)
4949
- [Intersection Observer](./guide/intersection-observer.md).
50+
- [Media Query](./guide/media-query.md)
5051
- [Mouse Position](./guide/mouse-position.md)
5152
- [Network Information API](./guide/network.md).
5253
- [Preferred Color Scheme](./guide/preferred-color-scheme.md).

docs/guide/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ These are the currently implemented Web APIs and the planned ones.
3131
- [Geo-location API](./geolocation.md).
3232
- [Intersection Observer](./intersection-observer.md).
3333
- [Local storage API](./local-storage.md).
34-
- [Mouse Position](./guide/mouse-position.md).
34+
- [Media Query](./media-query.md).
35+
- [Mouse Position](./mouse-position.md).
3536
- [Network Information API](./network.md).
3637
- [Preferred Color Scheme](./preferred-color-scheme.md).
3738
- [Preferred Languages](./preferred-languages.md).

docs/guide/media-query.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Media Query
2+
3+
> he DOM provides features that can test the results of a media [query programmatically](https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries/Testing_media_queries), via the MediaQueryList interface and its methods and properties. Once you've created a MediaQueryList object, you can check the result of the query or receive notifications when the result changes.
4+
5+
## State
6+
7+
```js
8+
import { useMedia } from 'vue-use-web';
9+
10+
const isLg = useMedia('(min-width: 1024px)');
11+
```
12+
13+
`useMedia` only returns a single value which is a boolean.
14+
15+
| State | Type | Description |
16+
| ------- | -------------- | ---------------------------- |
17+
| matches | `Ref<boolean>` | If the query matches or not. |
18+
19+
## Example
20+
21+
```vue
22+
<template>
23+
<h1>Is Desktop: {{ isLg }}</h1>
24+
</template>
25+
26+
<script>
27+
import { useMedia } from 'vue-use-web';
28+
29+
export default {
30+
setup() {
31+
const isLg = useMedia('(min-width: 1024px)');
32+
33+
return { isLg };
34+
}
35+
};
36+
</script>
37+
```
38+
39+
## Demo
40+
41+
TODO: Add cool live example!

docs/guide/preferred-color-scheme.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
> The [matchMedia preferred-color-scheme](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme) is used to detect the user's preference for a `light` or `dark` theme. This can be useful when you should adapt your UI depending on the user preference.
44
5+
## State
6+
57
```js
68
import { usePreferredColorScheme } from 'vue-use-web';
79

@@ -12,6 +14,10 @@ const scheme = usePreferredColorScheme();
1214
| ----- | ------------- | ---------------------------------------------------------------------------------------- |
1315
| theme | `Ref<String>` | Current user color scheme preference, will be one of 'dark', 'light' and 'no-preference' |
1416

17+
:::tip
18+
This composable function uses [`useMedia`](./media-query.md) internally.
19+
:::
20+
1521
## Example
1622

1723
```vue

src/Media.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { ref, onMounted, onUnmounted } from '@vue/composition-api';
2+
3+
export function useMedia(query: string) {
4+
let mediaQuery!: MediaQueryList;
5+
6+
// try to fetch initial value (avoid SSR issues)
7+
if (typeof window !== 'undefined') {
8+
mediaQuery = window.matchMedia(query);
9+
}
10+
11+
const matches = ref(mediaQuery ? mediaQuery.matches : false);
12+
function handler(event: MediaQueryListEvent) {
13+
matches.value = event.matches;
14+
}
15+
16+
onMounted(() => {
17+
if (!mediaQuery) {
18+
mediaQuery = window.matchMedia(query);
19+
}
20+
21+
matches.value = mediaQuery.matches;
22+
mediaQuery.addListener(handler);
23+
});
24+
25+
onUnmounted(() => {
26+
mediaQuery.removeListener(handler);
27+
});
28+
29+
return matches;
30+
}

src/PreferredColorScheme.ts

Lines changed: 18 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,27 @@
1-
import { ref, onMounted, onUnmounted } from '@vue/composition-api';
2-
3-
export function usePreferredColorScheme() {
4-
const themesObj: Record<string, MediaQueryList> = {
5-
light: window.matchMedia('(prefers-color-scheme: light)'),
6-
dark: window.matchMedia('(prefers-color-scheme: dark)'),
7-
'no-preference': window.matchMedia('(prefers-color-scheme: no-preference)')
1+
import { computed, Ref } from '@vue/composition-api';
2+
import { useMedia } from './Media';
3+
4+
export function usePreferredColorScheme(): Ref<'dark' | 'light' | 'no-preference'> {
5+
const queries = {
6+
light: '(prefers-color-scheme: light)',
7+
dark: '(prefers-color-scheme: dark)',
8+
'no-preference': '(prefers-color-scheme: no-preference)'
89
};
910

10-
const value = ref(getTheme());
11+
const isDark = useMedia(queries.dark);
12+
const isLight = useMedia(queries.light);
1113

12-
function getTheme() {
13-
let theme = null;
14-
for (const key in themesObj) {
15-
if (themesObj[key].matches) {
16-
theme = key;
17-
break;
18-
}
14+
const theme = computed(() => {
15+
if (isDark.value) {
16+
return 'dark';
1917
}
2018

21-
return theme;
22-
}
23-
24-
function handler() {
25-
value.value = getTheme();
26-
}
27-
28-
onMounted(() => {
29-
Object.values(themesObj).forEach(theme => {
30-
theme.addListener(handler);
31-
});
32-
});
19+
if (isLight.value) {
20+
return 'light';
21+
}
3322

34-
onUnmounted(() => {
35-
Object.values(themesObj).forEach(themeMedia => {
36-
themeMedia.removeListener(handler);
37-
});
23+
return 'no-preference';
3824
});
3925

40-
return value;
26+
return theme;
4127
}

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export * from './FullScreen';
88
export * from './Geolocation';
99
export * from './IntersectionObserver';
1010
export * from './LocalStorage';
11+
export * from './Media';
1112
export * from './MousePosition';
1213
export * from './Network';
1314
export * from './PreferredColorScheme';

0 commit comments

Comments
 (0)