├── .nvmrc ├── .prettierrc ├── .eslintignore ├── .babelrc ├── .gitignore ├── .editorconfig ├── .eslintrc ├── tsconfig.json ├── test └── client │ └── index.test.ts ├── CHANGELOG.md ├── .github └── workflows │ └── node.js.yml ├── LICENSE ├── rollup.config.js ├── src └── client │ └── index.ts ├── README.md └── package.json /.nvmrc: -------------------------------------------------------------------------------- 1 | 13.2.0 -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 100 3 | } 4 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | /test/fixtures 2 | dist/ 3 | example/ -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env"] 3 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | dist/ 4 | coverage/ 5 | docs/ -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = false -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["kaisermann/typescript"], 3 | "env": { 4 | "browser": true, 5 | "jest": true 6 | }, 7 | "parserOptions": { 8 | "sourceType": "module" 9 | }, 10 | "rules": { 11 | "@typescript-eslint/camelcase": "off" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "noImplicitAny": true, 5 | "sourceMap": false, 6 | "module": "esnext", 7 | "moduleResolution": "node", 8 | "esModuleInterop": true, 9 | "resolveJsonModule": true, 10 | "target": "es2017", 11 | "lib": ["es2018", "dom", "esnext"], 12 | "outDir": "dist", 13 | "types": ["svelte", "jest"], 14 | "declaration": true, 15 | "declarationDir": "./dist" 16 | }, 17 | "include": [ 18 | "src" 19 | ], 20 | "exclude": ["node_modules/**/*", "dist"] 21 | } 22 | -------------------------------------------------------------------------------- /test/client/index.test.ts: -------------------------------------------------------------------------------- 1 | import mediaFromBreakpoints from '../../src/client'; 2 | 3 | describe('mediaFromBreakpoints', () => { 4 | test('it exports a function that takes breakpoints and returns a store', () => { 5 | let breakpoints = { 6 | small: "(max-width: 849px)", 7 | large: "(min-width: 850px)", 8 | short: "(max-height: 399px)", 9 | wide: "(min-width: 960px)", 10 | widest: "(min-width: 1260px)", 11 | landscape: "(orientation: landscape) and (max-height: 499px)", 12 | tiny: "(orientation: portrait) and (max-height: 599px)" 13 | }; 14 | let store = mediaFromBreakpoints(breakpoints); 15 | expect(typeof store.subscribe).toEqual('function'); 16 | }) 17 | }) -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## [0.1.5](https://github.com/cibernox/svelte-media/compare/v0.1.4...v0.1.5) (2021-08-23) 4 | - Improve types of media query store for better TS/Intellisense errors. 5 | ## [0.1.4](https://github.com/cibernox/svelte-media/compare/v0.0.4...v0.1.4) (2021-05-31) 6 | 7 | 8 | 9 | ## [0.0.4](https://github.com/cibernox/svelte-media/compare/v0.0.3...v0.0.4) (2021-05-22) 10 | 11 | 12 | 13 | ## 0.0.3 (2021-05-22) 14 | 15 | 16 | 17 | ## [0.0.3](https://github.com/cibernox/svelte-media/compare/v0.0.2...v0.0.3) (2020-01-02) 18 | 19 | 20 | 21 | ## [0.0.2](https://github.com/cibernox/svelte-media/compare/v0.0.1...v0.0.2) (2020-01-02) 22 | 23 | 24 | 25 | ## 0.0.1 (2019-12-30) 26 | 27 | 28 | 29 | No releases yet -------------------------------------------------------------------------------- /.github/workflows/node.js.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean install of node dependencies, cache/restore them, build the source code and run tests across different versions of node 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 3 | 4 | name: Node.js CI 5 | 6 | on: 7 | push: 8 | branches: [ master ] 9 | pull_request: 10 | branches: [ master ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | strategy: 18 | matrix: 19 | node-version: [12.x, 14.x, 16.x] 20 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ 21 | 22 | steps: 23 | - uses: actions/checkout@v2 24 | - name: Use Node.js ${{ matrix.node-version }} 25 | uses: actions/setup-node@v2 26 | with: 27 | node-version: ${{ matrix.node-version }} 28 | cache: 'npm' 29 | - run: npm ci 30 | - run: npm run build --if-present 31 | - run: npm test 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2019 Miguel Camba 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import commonjs from 'rollup-plugin-commonjs' 2 | import ts from 'rollup-plugin-typescript2' 3 | import { terser } from 'rollup-plugin-terser' 4 | import autoExternal from 'rollup-plugin-auto-external' 5 | 6 | import pkg from './package.json' 7 | 8 | const PROD = !process.env.ROLLUP_WATCH 9 | 10 | export default [ 11 | { 12 | input: 'src/client/index.ts', 13 | external: [ 14 | ...Object.keys(pkg.dependencies), 15 | ...Object.keys(pkg.peerDependencies), 16 | 'svelte/store', 17 | ], 18 | output: [ 19 | { file: pkg.module, format: 'es' }, 20 | { file: pkg.main, format: 'cjs' }, 21 | ], 22 | plugins: [commonjs(), autoExternal(), ts(), PROD && terser()], 23 | }, 24 | // { 25 | // input: 'src/cli/index.ts', 26 | 27 | // // external: id => { 28 | // // if (id.startsWith('/')) return false 29 | // // return externals.has(id) || id.match(/svelte/gi) 30 | // // }, 31 | // output: [ 32 | // { 33 | // file: pkg.bin['svelte-i18n'], 34 | // name: 'cli.js', 35 | // format: 'cjs', 36 | // banner: `#!/usr/bin/env node`, 37 | // }, 38 | // ], 39 | // plugins: [autoExternal(), commonjs(), ts(), PROD && terser()], 40 | // }, 41 | ] 42 | -------------------------------------------------------------------------------- /src/client/index.ts: -------------------------------------------------------------------------------- 1 | import { writable } from "svelte/store"; 2 | 3 | type Media = Record> = { 4 | [K in keyof Query]?: boolean | string; 5 | } & { 6 | classNames: string; 7 | }; 8 | 9 | type MediaQueryLists = Record; 10 | 11 | function calculateMedia(mqls: MediaQueryLists) { 12 | let media: Media = { classNames: "" }; 13 | let mediaClasses = []; 14 | for (let name in mqls) { 15 | media[name] = mqls[name].matches; 16 | if (media[name]) { 17 | mediaClasses.push(`media-${name}`); 18 | } 19 | } 20 | media.classNames = mediaClasses.join(" "); 21 | return media; 22 | } 23 | 24 | export default function>(mediaqueries: Query) { 25 | return writable>({ classNames: "" }, set => { 26 | if (typeof window === "undefined") return; 27 | let mqls: MediaQueryLists = {}; 28 | let updateMedia = () => set(calculateMedia(mqls)); 29 | for (let key in mediaqueries) { 30 | let foo = window.matchMedia(mediaqueries[key]); 31 | mqls[key] = foo; 32 | mqls[key].addListener(updateMedia); 33 | } 34 | updateMedia(); 35 | return () => { 36 | for (let key in mqls) { 37 | mqls[key].removeListener(updateMedia); 38 | } 39 | }; 40 | }); 41 | } 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # svelte-media 2 | 3 | > Easy way to observe for media queries as a store for your Svelte apps 4 | 5 | `svelte-media` helps you define the media queries you want to observe. By using [stores](https://svelte.dev/docs#svelte_store) to keep track of the matching state of the given media queries it notifies your app in the most efficient way when a change happens. 6 | 7 | It works SSR environments where `window.matchMedia` is not available, so it can be used in Sapper apps safely. 8 | 9 | ## Instalation 10 | 11 | Just run `npm i --save-dev svelte-media` or `yarn add svelte-media`. 12 | 13 | ## Usage 14 | 15 | The package's default export is a function that takes an object with named mediaquery strings and returns a svelte _store_ that you can export to 16 | consume any way you want. 17 | 18 | ```js 19 | import watchMedia from "svelte-media"; 20 | 21 | const mediaqueries = { 22 | small: "(max-width: 849px)", 23 | large: "(min-width: 850px)", 24 | short: "(max-height: 399px)", 25 | landscape: "(orientation: landscape) and (max-height: 499px)", 26 | tiny: "(orientation: portrait) and (max-height: 599px)", 27 | dark: "(prefers-color-scheme: dark)", 28 | noanimations: "(prefers-reduced-motion: reduce)" 29 | }; 30 | 31 | export const media = watchMedia(mediaqueries); 32 | ``` 33 | 34 | Given an object with named media queries, the returned object from that store will have boolean properties named 35 | after the media queries that indicate if they are a match or not, and a property named `classNames`that 36 | contains a name of the matching media queries prefixed by `media-` to use as convenient css classes in any element. 37 | 38 | For the example above the object might look like this: 39 | 40 | ```js 41 | { 42 | small: false 43 | large: true 44 | short: true 45 | landscape: true 46 | tiny: false 47 | dark: true 48 | noanimations : false, 49 | classNames: 'media-large media-short media-landscape media-dark' 50 | } 51 | ``` 52 | 53 | As with any other store, you can subscribe to it in templates by prefixing it with `$`. 54 | 55 | ```html 56 | 59 | 60 |
61 | {if $media.large} 62 | 63 | {:else} 64 | 65 | {/if} 66 |
67 | ``` 68 | 69 | You can create more than one store if, for instance, you want to keep media queries about screen size 70 | separated from, say those about pixel density, as the latter very rarely will fire an update. 71 | 72 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "svelte-media", 3 | "version": "0.1.5", 4 | "main": "dist/svelte-media.js", 5 | "module": "dist/svelte-media.mjs", 6 | "types": "dist/index.d.ts", 7 | "license": "MIT", 8 | "description": "Svelte.js util to easily observe media queries as a reactive store", 9 | "author": "Miguel Camba ", 10 | "repository": "https://github.com/cibernox/svelte-media", 11 | "keywords": [ 12 | "svelte", 13 | "sapper", 14 | "media", 15 | "query", 16 | "matchmedia", 17 | "responsive", 18 | "media-query" 19 | ], 20 | "engines": { 21 | "node": ">= 11.15.0" 22 | }, 23 | "scripts": { 24 | "build": "rollup -c", 25 | "dev": "rollup -c -w", 26 | "pretest": "npm run build", 27 | "test": "jest", 28 | "test:ci": "jest --silent", 29 | "test:watch": "jest --verbose --watchAll", 30 | "lint": "eslint \"src/**/*.ts\"", 31 | "format": "prettier --loglevel silent --write \"src/**/*.ts\" && eslint --fix \"src/**/*.ts\"", 32 | "prerelease": "conventional-changelog -p angular -i CHANGELOG.md -s -r 1 && git add CHANGELOG.md", 33 | "release": " git add package.json && git commit -m \"chore(release): v$npm_package_version :tada:\"", 34 | "postrelease": "git tag -a v$npm_package_version -m 'Release v$npm_package_version'", 35 | "prepublishOnly": "run-s test:ci build release" 36 | }, 37 | "files": [ 38 | "dist/" 39 | ], 40 | "jest": { 41 | "collectCoverage": true, 42 | "testMatch": [ 43 | "/test/**/*.test.ts" 44 | ], 45 | "collectCoverageFrom": [ 46 | "/src/**/*.ts" 47 | ], 48 | "transform": { 49 | "^.+\\.tsx?$": "ts-jest" 50 | } 51 | }, 52 | "peerDependencies": { 53 | "svelte": "^3.14.1" 54 | }, 55 | "devDependencies": { 56 | "@babel/core": "^7.7.2", 57 | "@babel/preset-env": "^7.7.1", 58 | "@types/estree": "0.0.39", 59 | "@types/intl": "^1.2.0", 60 | "@types/jest": "^24.0.23", 61 | "babel-core": "^7.0.0-bridge.0", 62 | "babel-jest": "^24.9.0", 63 | "conventional-changelog-cli": "^2.0.28", 64 | "eslint": "^6.6.0", 65 | "jest": "^24.9.0", 66 | "npm-run-all": "^4.1.5", 67 | "prettier": "^1.19.1", 68 | "rollup": "^1.26.5", 69 | "rollup-plugin-auto-external": "^2.0.0", 70 | "rollup-plugin-commonjs": "^10.1.0", 71 | "rollup-plugin-terser": "^5.1.2", 72 | "rollup-plugin-typescript2": "^0.25.2", 73 | "sass": "^1.23.6", 74 | "svelte": "^3.14.1", 75 | "svelte-preprocess": "^3.2.6", 76 | "ts-jest": "^24.1.0", 77 | "typescript": "^3.7.2" 78 | }, 79 | "dependencies": { 80 | "commander": "^4.0.1", 81 | "deepmerge": "^4.2.2", 82 | "dlv": "^1.1.3", 83 | "estree-walker": "^0.9.0", 84 | "fast-memoize": "^2.5.1", 85 | "intl-messageformat": "^7.5.2", 86 | "tiny-glob": "^0.2.6" 87 | } 88 | } 89 | --------------------------------------------------------------------------------