From dee851ec411b0b00abf8d7273052d4fb93eee2b5 Mon Sep 17 00:00:00 2001 From: u3u Date: Fri, 16 Aug 2019 10:48:27 +0800 Subject: [PATCH] feat(hooks): add `useMedia` hook --- package.json | 1 + src/__tests__/useMedia.test.ts | 42 ++++++++++++++++++++++++++++++++++ src/index.ts | 1 + src/useMedia.ts | 21 +++++++++++++++++ yarn.lock | 21 ++++++++++++++++- 5 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 src/__tests__/useMedia.test.ts create mode 100644 src/useMedia.ts diff --git a/package.json b/package.json index b93a3b5..80e9d30 100644 --- a/package.json +++ b/package.json @@ -94,6 +94,7 @@ "markdown-it-link-attributes": "^2.1.0", "markdown-it-loader": "^0.7.0", "markdown-it-prism": "^2.0.2", + "match-media-mock": "^0.1.1", "prettier": "^1.18.2", "prismjs": "^1.17.1", "rimraf": "^3.0.0", diff --git a/src/__tests__/useMedia.test.ts b/src/__tests__/useMedia.test.ts new file mode 100644 index 0000000..69fa99e --- /dev/null +++ b/src/__tests__/useMedia.test.ts @@ -0,0 +1,42 @@ +import { onMounted, onUnmounted } from 'vue-function-api'; +import { useMedia } from '..'; +import renderHook from '../util/renderHook'; + +const matchMediaMock = require('match-media-mock').create(); + +matchMediaMock.setConfig({ type: 'screen', width: 1280, height: 800 }); +window.matchMedia = matchMediaMock; + +describe('useMedia', () => { + it('should be defined', () => { + expect(useMedia).toBeDefined(); + }); + + it('should update matches', () => { + const { vm } = renderHook(() => { + const pc = useMedia('(min-width: 768px)'); + const sp = useMedia('(max-width: 768px)'); + expect(pc.value).toBe(false); + expect(sp.value).toBe(false); + + onMounted(() => { + expect(pc.value).toBe(true); + expect(sp.value).toBe(false); + + matchMediaMock.setConfig({ type: 'screen', width: 375, height: 667 }); + + expect(pc.value).toBe(false); + expect(sp.value).toBe(true); + }); + + onUnmounted(() => { + matchMediaMock.setConfig({ type: 'screen', width: 1280, height: 800 }); + + expect(pc.value).toBe(false); + expect(sp.value).toBe(true); + }); + }); + + vm.$destroy(); + }); +}); diff --git a/src/index.ts b/src/index.ts index ea16676..223ef44 100644 --- a/src/index.ts +++ b/src/index.ts @@ -15,6 +15,7 @@ export { default as useRouter } from './useRouter'; export { default as useRef } from './useRef'; export { default as useMountedState } from './useMountedState'; export { default as useTimeout } from './useTimeout'; +export { default as useMedia } from './useMedia'; export default function install(Vue: VueConstructor) { Vue.mixin({ beforeCreate: setRuntimeVM }); diff --git a/src/useMedia.ts b/src/useMedia.ts new file mode 100644 index 0000000..7687767 --- /dev/null +++ b/src/useMedia.ts @@ -0,0 +1,21 @@ +import { value, onMounted, onUnmounted } from 'vue-function-api'; + +export default function useMedia(query, defaultState = false) { + let mql; + const matches = value(defaultState); + const updateMatches = () => { + if (mql) matches.value = mql.matches; + }; + + onMounted(() => { + mql = window.matchMedia(query); + mql.addListener(updateMatches); + matches.value = mql.matches; + }); + + onUnmounted(() => { + mql.removeListener(updateMatches); + }); + + return matches; +} diff --git a/yarn.lock b/yarn.lock index 1a06f3f..d075f06 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4115,6 +4115,11 @@ css-loader@^2.1.1: postcss-value-parser "^3.3.0" schema-utils "^1.0.0" +css-mediaquery@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/css-mediaquery/-/css-mediaquery-0.1.2.tgz#6a2c37344928618631c54bd33cedd301da18bea0" + integrity sha1-aiw3NEkoYYYxxUvTPO3TAdoYvqA= + css-select@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858" @@ -5190,6 +5195,11 @@ execa@^2.0.3: signal-exit "^3.0.2" strip-final-newline "^2.0.0" +exenv@^1.2.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/exenv/-/exenv-1.2.2.tgz#2ae78e85d9894158670b03d47bec1f03bd91bb9d" + integrity sha1-KueOhdmJQVhnCwPUe+wfA72Ru50= + exit@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" @@ -8011,7 +8021,7 @@ lodash@4.17.14: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.14.tgz#9ce487ae66c96254fe20b599f21b6816028078ba" integrity sha512-mmKYbW3GLuJeX+iGP+Y7Gp1AiGHGbXHCOh/jZmrawMmsE7MS4znI3RL2FsjbqOyMayHInjOeykW7PEajUk1/xw== -lodash@^4.0.1, lodash@^4.11.2, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.4, lodash@^4.2.1: +lodash@^4.0.1, lodash@^4.11.2, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.1: version "4.17.15" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== @@ -8254,6 +8264,15 @@ marked@^0.7.0: resolved "https://registry.yarnpkg.com/marked/-/marked-0.7.0.tgz#b64201f051d271b1edc10a04d1ae9b74bb8e5c0e" integrity sha512-c+yYdCZJQrsRjTPhUx7VKkApw9bwDkNbHUKo1ovgcfDjb2kc8rLuRbIFyXL5WOEUwzSSKo3IXpph2K6DqB/KZg== +match-media-mock@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/match-media-mock/-/match-media-mock-0.1.1.tgz#25485e5b26942e56142ce147a8e1d59c406d85f8" + integrity sha512-YPCJ9HLrTkGQ5YV88TQraXnu/ZrEa2XCfefWY0cL84fq2gNhtmEVUA6tlO70eqCDJmRai7LQY3p8so9HPJ/oIQ== + dependencies: + css-mediaquery "^0.1.2" + exenv "^1.2.0" + lodash "^4.17.5" + material-colors@^1.2.1: version "1.2.6" resolved "https://registry.yarnpkg.com/material-colors/-/material-colors-1.2.6.tgz#6d1958871126992ceecc72f4bcc4d8f010865f46"