├── .eslintrc.json ├── .github └── workflows │ └── tests.yml ├── .gitignore ├── .prettierrc ├── .versionrc ├── ACKNOWLEDGMENTS ├── CHANGELOG.md ├── LICENSE ├── README.md ├── demo ├── favicon-16x16.png ├── favicon-32x32.png ├── favicon.ico ├── index.html └── styles.css ├── package-lock.json ├── package.json ├── preview.gif ├── rollup.config.js ├── scripts └── build-styles.js ├── src ├── cross-burger.ts ├── demo │ └── demo-snippet.ts ├── fade-burger.ts ├── lib │ ├── components │ │ ├── burger.ts │ │ └── direction-burger.ts │ ├── entrypoints │ │ ├── cross.ts │ │ ├── fade.ts │ │ ├── pivot.ts │ │ ├── rotate.ts │ │ ├── slant.ts │ │ ├── sling.ts │ │ ├── spin.ts │ │ ├── spiral.ts │ │ ├── squash.ts │ │ ├── squeeze.ts │ │ ├── tilt.ts │ │ ├── turn.ts │ │ └── twirl.ts │ ├── internals.ts │ ├── styles │ │ └── burger.scss │ ├── types.ts │ └── utils │ │ └── dom.ts ├── pivot-burger.ts ├── rotate-burger.ts ├── slant-burger.ts ├── sling-burger.ts ├── spin-burger.ts ├── spiral-burger.ts ├── squash-burger.ts ├── squeeze-burger.ts ├── test │ ├── hamburger.test.ts │ └── visual │ │ ├── screenshots │ │ └── Chrome │ │ │ └── baseline │ │ │ ├── cross-default.png │ │ │ ├── cross-disabled.png │ │ │ ├── cross-pressed.png │ │ │ ├── fade-default.png │ │ │ ├── fade-disabled.png │ │ │ ├── fade-pressed.png │ │ │ ├── pivot-default.png │ │ │ ├── pivot-disabled.png │ │ │ ├── pivot-pressed.png │ │ │ ├── rotate-default.png │ │ │ ├── rotate-disabled.png │ │ │ ├── rotate-pressed.png │ │ │ ├── slant-default.png │ │ │ ├── slant-disabled.png │ │ │ ├── slant-pressed.png │ │ │ ├── sling-default.png │ │ │ ├── sling-disabled.png │ │ │ ├── sling-pressed.png │ │ │ ├── spin-default.png │ │ │ ├── spin-disabled.png │ │ │ ├── spin-pressed.png │ │ │ ├── spiral-default.png │ │ │ ├── spiral-disabled.png │ │ │ ├── spiral-pressed.png │ │ │ ├── squash-default.png │ │ │ ├── squash-disabled.png │ │ │ ├── squash-pressed.png │ │ │ ├── squeeze-default.png │ │ │ ├── squeeze-disabled.png │ │ │ ├── squeeze-pressed.png │ │ │ ├── tilt-default.png │ │ │ ├── tilt-disabled.png │ │ │ ├── tilt-pressed.png │ │ │ ├── turn-default.png │ │ │ ├── turn-disabled.png │ │ │ ├── turn-pressed.png │ │ │ ├── twirl-default.png │ │ │ ├── twirl-disabled.png │ │ │ └── twirl-pressed.png │ │ └── visual.test.ts ├── tilt-burger.ts ├── turn-burger.ts └── twirl-burger.ts ├── tsconfig.json └── web-test-runner.config.js /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "plugin:@typescript-eslint/eslint-recommended", 4 | "plugin:@typescript-eslint/recommended", 5 | "plugin:prettier/recommended", 6 | "prettier/@typescript-eslint" 7 | ], 8 | "plugins": ["@typescript-eslint", "prettier"], 9 | "parser": "@typescript-eslint/parser", 10 | "parserOptions": { 11 | "sourceType": "module", 12 | "ecmaVersion": 2018, 13 | "project": "./tsconfig.json" 14 | }, 15 | "env": { 16 | "browser": true 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: tests 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | lint: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - uses: actions/checkout@v2 11 | 12 | - uses: actions/setup-node@v1 13 | with: 14 | node-version: 14 15 | 16 | - run: npm ci 17 | 18 | - run: npm run lint 19 | 20 | size: 21 | runs-on: ubuntu-latest 22 | 23 | steps: 24 | - uses: actions/checkout@v2 25 | 26 | - uses: actions/setup-node@v1 27 | with: 28 | node-version: 14 29 | 30 | - name: npm install 31 | run: npm ci 32 | 33 | - name: Size limit 34 | run: npm run size 35 | 36 | unit-tests: 37 | runs-on: ubuntu-latest 38 | 39 | steps: 40 | - uses: actions/checkout@v2 41 | 42 | - uses: actions/setup-node@v1 43 | with: 44 | node-version: 14 45 | 46 | - name: npm install 47 | run: npm ci 48 | 49 | - name: Build 50 | run: npm run build 51 | 52 | - name: Unit tests 53 | run: npm run test 54 | 55 | visual-tests: 56 | runs-on: ubuntu-latest 57 | 58 | steps: 59 | - uses: actions/checkout@v2 60 | 61 | - uses: actions/setup-node@v1 62 | with: 63 | node-version: 14 64 | 65 | - name: npm install 66 | run: npm ci 67 | 68 | - name: Build 69 | run: npm run build 70 | 71 | - name: Visual tests 72 | run: npm run test:visual 73 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | /coverage 3 | /dist 4 | /lib 5 | /src/lib/styles/*.ts 6 | /test/**/*.d.ts 7 | /test/**/*.d.ts.map 8 | /test/**/*.js 9 | /test/**/*.js.map 10 | /demo/*.js 11 | *-burger.js 12 | *.d.ts 13 | *.map 14 | custom-elements.json 15 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "printWidth": 120, 4 | "trailingComma": "none" 5 | } 6 | -------------------------------------------------------------------------------- /.versionrc: -------------------------------------------------------------------------------- 1 | { 2 | "types": [ 3 | { "type": "feat", "section": "Features", "hidden": false }, 4 | { "type": "fix", "section": "Bug Fixes", "hidden": false }, 5 | { "type": "refactor", "section": "Internal Changes", "hidden": true } 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /ACKNOWLEDGMENTS: -------------------------------------------------------------------------------- 1 | ## Acknowledgments 2 | 3 | - Thanks to @luukdv who created the original `hamburger-react` library (MIT License). 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | ### [0.2.3](https://github.com/web-padawan/vanilla-hamburger/compare/v0.2.2...v0.2.3) (2020-10-23) 6 | 7 | 8 | ### Features 9 | 10 | * add disabled property and attribute ([#8](https://github.com/web-padawan/vanilla-hamburger/issues/8)) ([47d7dfd](https://github.com/web-padawan/vanilla-hamburger/commit/47d7dfd75385e990fd26b10206fdba40b809fc65)) 11 | 12 | ### [0.2.2](https://github.com/web-padawan/vanilla-hamburger/compare/v0.2.1...v0.2.2) (2020-10-05) 13 | 14 | 15 | ### Features 16 | 17 | * reflect pressed property to attribute ([21e256f](https://github.com/web-padawan/vanilla-hamburger/commit/21e256f0b4c07bad4bdcbfcaa0874068817b9769)) 18 | 19 | ### [0.2.1](https://github.com/web-padawan/vanilla-hamburger/compare/v0.2.0...v0.2.1) (2020-10-01) 20 | 21 | 22 | ### Bug Fixes 23 | 24 | * handle distance attribute properly ([#4](https://github.com/web-padawan/vanilla-hamburger/issues/4)) ([3c0fb78](https://github.com/web-padawan/vanilla-hamburger/commit/3c0fb78e5460e7ffd9c5c068a4e4ae44382c8c52)) 25 | 26 | ## [0.2.0](https://github.com/web-padawan/vanilla-hamburger/compare/v0.1.0...v0.2.0) (2020-09-28) 27 | 28 | 29 | ### ⚠ BREAKING CHANGES 30 | 31 | * rename protected methods 32 | 33 | ### Features 34 | 35 | * add burger props TS interface ([388bb1a](https://github.com/web-padawan/vanilla-hamburger/commit/388bb1a12b2300fca7dd89d459bc718027104338)) 36 | * add label property for a11y ([98dc9e6](https://github.com/web-padawan/vanilla-hamburger/commit/98dc9e64b6f50abe61e4896d688f1b110e878005)) 37 | * add native button CSS part ([5be3a5d](https://github.com/web-padawan/vanilla-hamburger/commit/5be3a5d8d81955b603bfb58113ab7ef15be60554)) 38 | 39 | 40 | ### Bug Fixes 41 | 42 | * check if native button exists ([fcc75e9](https://github.com/web-padawan/vanilla-hamburger/commit/fcc75e9371d90241e004c952a930cabf27afc4ee)) 43 | * focus and blur native button ([781868f](https://github.com/web-padawan/vanilla-hamburger/commit/781868fdd4185b1ecad362c1448a2e86ad1c4642)) 44 | 45 | 46 | ### Internal Changes 47 | 48 | * hide internals with symbols ([bd05887](https://github.com/web-padawan/vanilla-hamburger/commit/bd05887fef91d66fea668ffe3fde6539efbe329b)) 49 | 50 | ## 0.1.0 (2020-09-22) 51 | 52 | 53 | ### Features 54 | 55 | * add hamburger components ([9e2b5d1](https://github.com/web-padawan/vanilla-hamburger/commit/9e2b5d1dad25cf74f161b317be7686ecb002e81e)) 56 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Serhii Kulykov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | Preview 4 | 5 |

6 | 7 |
8 | 9 | npm 10 | 11 | 12 | build 13 | 14 | 15 | gzip size 16 | 17 | 18 | no dependencies 19 | 20 |
21 | 22 |

23 | vanilla-hamburger is a port of hamburger-react to vanilla Custom Elements. 24 |

25 | 26 | ## Features 27 | 28 | - **Small**: Just 1,8 KB (minified and gzipped). [Size Limit](https://github.com/ai/size-limit) controls the size. 29 | - **Fast**: Built with standards based Custom Elements. 30 | - **Bulletproof**: Written in strict TypeScript and covered by 30+ tests. 31 | - **Framework-agnostic**: Can be used [with any framework](https://custom-elements-everywhere.com/). 32 | - **Simple**: Uses native `${bar}${bar}`); 7 | 8 | export class Cross extends DirectionBurger { 9 | private [styles]!: CSSStyleDeclaration[]; 10 | 11 | constructor() { 12 | super(); 13 | this[styles] = getStyles(createRoot(this, tpl)); 14 | } 15 | 16 | protected get lines(): number { 17 | return 2; 18 | } 19 | 20 | protected [render](options: RenderOptions): void { 21 | const { barHeight, barStyles, margin, move, pressed, time, topOffset } = options; 22 | 23 | const isLeft = this.direction === 'left'; 24 | const transition = `${time}s ${this.easing}`; 25 | 26 | this.style.transition = transition; 27 | 28 | setStyles(this[styles][0], { 29 | ...barStyles, 30 | top: `${topOffset}px`, 31 | transition, 32 | transform: `${ 33 | pressed ? `rotate(${45 * (isLeft ? -1 : 1)}deg) translate(${move * (isLeft ? -1 : 1)}px, ${move}px)` : 'none' 34 | }` 35 | }); 36 | 37 | setStyles(this[styles][1], { 38 | ...barStyles, 39 | top: `${topOffset + barHeight + margin}px`, 40 | transition, 41 | transform: `${ 42 | pressed 43 | ? `rotate(${45 * (isLeft ? 1 : -1)}deg) translate(${move * (isLeft ? -1 : 1)}px, ${move * -1}px)` 44 | : 'none' 45 | }` 46 | }); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/lib/entrypoints/fade.ts: -------------------------------------------------------------------------------- 1 | import { DirectionBurger } from '../components/direction-burger.js'; 2 | import { bar, createTemplate, createRoot, getStyles, setStyles } from '../utils/dom.js'; 3 | import { render, styles } from '../internals.js'; 4 | import type { RenderOptions } from '../types'; 5 | 6 | const tpl = createTemplate(`${bar}${bar}${bar}`); 7 | 8 | export class Fade extends DirectionBurger { 9 | private [styles]!: CSSStyleDeclaration[]; 10 | 11 | constructor() { 12 | super(); 13 | this[styles] = getStyles(createRoot(this, tpl)); 14 | } 15 | 16 | protected get lines(): number { 17 | return 3; 18 | } 19 | 20 | protected [render](options: RenderOptions): void { 21 | const { barHeight, barStyles, margin, move, pressed, time, topOffset } = options; 22 | 23 | const isLeft = this.direction === 'left'; 24 | const transition = `${time}s ${this.easing}`; 25 | 26 | this.style.transition = transition; 27 | 28 | setStyles(this[styles][0], { 29 | ...barStyles, 30 | top: `${topOffset}px`, 31 | transition, 32 | transform: `${ 33 | pressed ? `rotate(${45 * (isLeft ? -1 : 1)}deg) translate(${move * (isLeft ? -1 : 1)}px, ${move}px)` : 'none' 34 | }` 35 | }); 36 | 37 | setStyles(this[styles][1], { 38 | ...barStyles, 39 | top: `${topOffset + barHeight + margin}px`, 40 | transition, 41 | opacity: `${pressed ? '0' : '1'}` 42 | }); 43 | 44 | setStyles(this[styles][2], { 45 | ...barStyles, 46 | top: `${topOffset + barHeight * 2 + margin * 2}px`, 47 | transition, 48 | transform: `${ 49 | pressed 50 | ? `rotate(${45 * (isLeft ? 1 : -1)}deg) translate(${move * (isLeft ? -1 : 1)}px, ${move * -1}px)` 51 | : 'none' 52 | }` 53 | }); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/lib/entrypoints/pivot.ts: -------------------------------------------------------------------------------- 1 | import { DirectionBurger } from '../components/direction-burger.js'; 2 | import { bar, createTemplate, createRoot, getStyles, setStyles } from '../utils/dom.js'; 3 | import { render, styles } from '../internals.js'; 4 | import type { RenderOptions } from '../types'; 5 | 6 | const tpl = createTemplate(`
${bar}
${bar}
`); 7 | 8 | export class Pivot extends DirectionBurger { 9 | private [styles]!: CSSStyleDeclaration[]; 10 | 11 | constructor() { 12 | super(); 13 | this[styles] = getStyles(createRoot(this, tpl)); 14 | } 15 | 16 | protected get lines(): number { 17 | return 2; 18 | } 19 | 20 | protected [render](options: RenderOptions): void { 21 | const { barHeight, barStyles, margin, pressed, time, topOffset } = options; 22 | 23 | const isLeft = this.direction === 'left'; 24 | 25 | const half = time / 2; 26 | const wrapTransition = `${half}s ${this.easing} ${pressed ? '0s' : `${half}s`}`; 27 | const barTransition = `${half}s ${this.easing} ${pressed ? `${half}s` : '0s'}`; 28 | 29 | setStyles(this.style, { 30 | transition: `${time}s ${this.easing}`, 31 | transform: `${pressed ? `rotate(${90 * (isLeft ? -1 : 1)}deg)` : 'none'}` 32 | }); 33 | 34 | setStyles(this[styles][0], { 35 | transition: wrapTransition, 36 | transform: `${pressed ? `translateY(${barHeight / 2 + margin / 2}px)` : 'none'}` 37 | }); 38 | 39 | setStyles(this[styles][1], { 40 | ...barStyles, 41 | top: `${topOffset}px`, 42 | transition: barTransition, 43 | transform: `${pressed ? `rotate(${45 * (isLeft ? 1 : -1)}deg)` : 'none'}` 44 | }); 45 | 46 | setStyles(this[styles][2], { 47 | transition: wrapTransition, 48 | transform: `${pressed ? `translateY(-${barHeight / 2 + margin / 2}px)` : 'none'}` 49 | }); 50 | 51 | setStyles(this[styles][3], { 52 | ...barStyles, 53 | top: `${topOffset + barHeight + margin}px`, 54 | transition: barTransition, 55 | transform: `${pressed ? `rotate(${45 * (isLeft ? -1 : 1)}deg)` : 'none'}` 56 | }); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/lib/entrypoints/rotate.ts: -------------------------------------------------------------------------------- 1 | import { DirectionBurger } from '../components/direction-burger.js'; 2 | import { bar, createTemplate, createRoot, getStyles, setStyles } from '../utils/dom.js'; 3 | import { render, styles } from '../internals.js'; 4 | import type { RenderOptions } from '../types'; 5 | 6 | const tpl = createTemplate(`${bar}${bar}`); 7 | 8 | export class Rotate extends DirectionBurger { 9 | private [styles]!: CSSStyleDeclaration[]; 10 | 11 | constructor() { 12 | super(); 13 | this[styles] = getStyles(createRoot(this, tpl)); 14 | } 15 | 16 | protected get lines(): number { 17 | return 2; 18 | } 19 | 20 | protected [render](options: RenderOptions): void { 21 | const { barHeight, barStyles, margin, move, pressed, time, topOffset } = options; 22 | 23 | const isLeft = this.direction === 'left'; 24 | const transition = `${time}s ${this.easing}`; 25 | 26 | setStyles(this.style, { 27 | transition, 28 | transform: `${pressed ? `rotateY(${180 * (isLeft ? -1 : 1)}deg)` : 'none'}` 29 | }); 30 | 31 | setStyles(this[styles][0], { 32 | ...barStyles, 33 | top: `${topOffset}px`, 34 | transition, 35 | transform: `${ 36 | pressed ? `rotate(${45 * (isLeft ? -1 : 1)}deg) translate(${move * (isLeft ? -1 : 1)}px, ${move}px)` : 'none' 37 | }` 38 | }); 39 | 40 | setStyles(this[styles][1], { 41 | ...barStyles, 42 | top: `${topOffset + barHeight + margin}px`, 43 | transition, 44 | transform: `${ 45 | pressed 46 | ? `rotate(${45 * (isLeft ? 1 : -1)}deg) translate(${move * (isLeft ? -1 : 1)}px, ${move * -1}px)` 47 | : 'none' 48 | }` 49 | }); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/lib/entrypoints/slant.ts: -------------------------------------------------------------------------------- 1 | import { DirectionBurger } from '../components/direction-burger.js'; 2 | import { bar, createTemplate, createRoot, getStyles, setStyles } from '../utils/dom.js'; 3 | import { render, styles } from '../internals.js'; 4 | import type { RenderOptions } from '../types'; 5 | 6 | const tpl = createTemplate(`${bar}${bar}`); 7 | 8 | export class Slant extends DirectionBurger { 9 | private [styles]!: CSSStyleDeclaration[]; 10 | 11 | constructor() { 12 | super(); 13 | this[styles] = getStyles(createRoot(this, tpl)); 14 | } 15 | 16 | protected get lines(): number { 17 | return 2; 18 | } 19 | 20 | protected [render](options: RenderOptions): void { 21 | const { barHeight, barStyles, margin, move, pressed, time, topOffset } = options; 22 | 23 | const isLeft = this.direction === 'left'; 24 | const transition = `${time}s ${this.easing}`; 25 | 26 | setStyles(this.style, { 27 | transition, 28 | transform: `${pressed ? `rotate(${90 * (isLeft ? -1 : 1)}deg)` : 'none'}` 29 | }); 30 | 31 | setStyles(this[styles][0], { 32 | ...barStyles, 33 | top: `${topOffset}px`, 34 | transition, 35 | transform: `${ 36 | pressed ? `rotate(${45 * (isLeft ? -1 : 1)}deg) translate(${move * (isLeft ? -1 : 1)}px, ${move}px)` : 'none' 37 | }` 38 | }); 39 | 40 | setStyles(this[styles][1], { 41 | ...barStyles, 42 | top: `${topOffset + barHeight + margin}px`, 43 | transition, 44 | transform: `${ 45 | pressed 46 | ? `rotate(${45 * (isLeft ? 1 : -1)}deg) translate(${move * (isLeft ? -1 : 1)}px, ${move * -1}px)` 47 | : 'none' 48 | }` 49 | }); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/lib/entrypoints/sling.ts: -------------------------------------------------------------------------------- 1 | import { DirectionBurger } from '../components/direction-burger.js'; 2 | import { bar, createTemplate, createRoot, getStyles, setStyles } from '../utils/dom.js'; 3 | import { render, styles } from '../internals.js'; 4 | import type { RenderOptions } from '../types'; 5 | 6 | const tpl = createTemplate(`${bar}${bar}${bar}`); 7 | 8 | export class Sling extends DirectionBurger { 9 | private [styles]!: CSSStyleDeclaration[]; 10 | 11 | constructor() { 12 | super(); 13 | this[styles] = getStyles(createRoot(this, tpl)); 14 | } 15 | 16 | protected get lines(): number { 17 | return 3; 18 | } 19 | 20 | protected [render](options: RenderOptions): void { 21 | const { barHeight, barStyles, margin, move, pressed, time, topOffset } = options; 22 | 23 | const isLeft = this.direction === 'left'; 24 | const transition = `${time}s ${this.easing}`; 25 | 26 | setStyles(this.style, { 27 | transition, 28 | transform: `${pressed ? `rotateY(${180 * (isLeft ? -1 : 1)}deg)` : 'none'}` 29 | }); 30 | 31 | setStyles(this[styles][0], { 32 | ...barStyles, 33 | top: `${topOffset}px`, 34 | transition, 35 | transform: `${ 36 | pressed ? `rotate(${45 * (isLeft ? -1 : 1)}deg) translate(${move * (isLeft ? -1 : 1)}px, ${move}px)` : 'none' 37 | }` 38 | }); 39 | 40 | setStyles(this[styles][1], { 41 | ...barStyles, 42 | top: `${topOffset + barHeight + margin}px`, 43 | transition, 44 | transform: `${pressed ? `scale(0, 1) translate(${move * 20 * (isLeft ? -1 : 1)}px, 0)` : 'none'}` 45 | }); 46 | 47 | setStyles(this[styles][2], { 48 | ...barStyles, 49 | top: `${topOffset + barHeight * 2 + margin * 2}px`, 50 | transition, 51 | transform: `${ 52 | pressed 53 | ? `rotate(${45 * (isLeft ? 1 : -1)}deg) translate(${move * (isLeft ? -1 : 1)}px, ${move * -1}px)` 54 | : 'none' 55 | }` 56 | }); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/lib/entrypoints/spin.ts: -------------------------------------------------------------------------------- 1 | import { DirectionBurger } from '../components/direction-burger.js'; 2 | import { bar, createTemplate, createRoot, getStyles, setStyles } from '../utils/dom.js'; 3 | import { render, styles } from '../internals.js'; 4 | import type { RenderOptions } from '../types'; 5 | 6 | const tpl = createTemplate(`${bar}${bar}${bar}`); 7 | 8 | export class Spin extends DirectionBurger { 9 | private [styles]!: CSSStyleDeclaration[]; 10 | 11 | constructor() { 12 | super(); 13 | this[styles] = getStyles(createRoot(this, tpl)); 14 | } 15 | 16 | protected get lines(): number { 17 | return 3; 18 | } 19 | 20 | protected [render](options: RenderOptions): void { 21 | const { barHeight, barStyles, margin, move, pressed, time, topOffset } = options; 22 | 23 | const isLeft = this.direction === 'left'; 24 | const transition = `${time}s ${this.easing}`; 25 | 26 | setStyles(this.style, { 27 | transition, 28 | transform: `${pressed ? `rotate(${180 * (isLeft ? -1 : 1)}deg)` : 'none'}` 29 | }); 30 | 31 | setStyles(this[styles][0], { 32 | ...barStyles, 33 | top: `${topOffset}px`, 34 | transition, 35 | transform: `${ 36 | pressed ? `rotate(${45 * (isLeft ? -1 : 1)}deg) translate(${move * (isLeft ? -1 : 1)}px, ${move}px)` : 'none' 37 | }` 38 | }); 39 | 40 | setStyles(this[styles][1], { 41 | ...barStyles, 42 | top: `${topOffset + barHeight + margin}px`, 43 | transition, 44 | opacity: `${pressed ? '0' : '1'}` 45 | }); 46 | 47 | setStyles(this[styles][2], { 48 | ...barStyles, 49 | top: `${topOffset + barHeight * 2 + margin * 2}px`, 50 | transition, 51 | transform: `${ 52 | pressed 53 | ? `rotate(${45 * (isLeft ? 1 : -1)}deg) translate(${move * (isLeft ? -1 : 1)}px, ${move * -1}px)` 54 | : 'none' 55 | }` 56 | }); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/lib/entrypoints/spiral.ts: -------------------------------------------------------------------------------- 1 | import { DirectionBurger } from '../components/direction-burger.js'; 2 | import { bar, createTemplate, createRoot, getStyles, setStyles } from '../utils/dom.js'; 3 | import { render, styles } from '../internals.js'; 4 | import type { RenderOptions } from '../types'; 5 | 6 | const tpl = createTemplate(`${bar}${bar}`); 7 | 8 | export class Spiral extends DirectionBurger { 9 | private [styles]!: CSSStyleDeclaration[]; 10 | 11 | constructor() { 12 | super(); 13 | this[styles] = getStyles(createRoot(this, tpl)); 14 | } 15 | 16 | protected get lines(): number { 17 | return 2; 18 | } 19 | 20 | protected [render](options: RenderOptions): void { 21 | const { barHeight, barStyles, margin, move, pressed, time, topOffset } = options; 22 | 23 | const isLeft = this.direction === 'left'; 24 | const transition = `${time}s ${this.easing}`; 25 | 26 | setStyles(this.style, { 27 | transition, 28 | transform: `${pressed ? `rotate(${180 * (isLeft ? -1 : 1)}deg)` : 'none'}` 29 | }); 30 | 31 | setStyles(this[styles][0], { 32 | ...barStyles, 33 | top: `${topOffset}px`, 34 | transition, 35 | transform: `${ 36 | pressed ? `rotate(${45 * (isLeft ? -1 : 1)}deg) translate(${move * (isLeft ? -1 : 1)}px, ${move}px)` : 'none' 37 | }` 38 | }); 39 | 40 | setStyles(this[styles][1], { 41 | ...barStyles, 42 | top: `${topOffset + barHeight + margin}px`, 43 | transition, 44 | transform: `${ 45 | pressed 46 | ? `rotate(${45 * (isLeft ? 1 : -1)}deg) translate(${move * (isLeft ? -1 : 1)}px, ${move * -1}px)` 47 | : 'none' 48 | }` 49 | }); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/lib/entrypoints/squash.ts: -------------------------------------------------------------------------------- 1 | import { Burger } from '../components/burger.js'; 2 | import { bar, createTemplate, createRoot, getStyles, setStyles } from '../utils/dom.js'; 3 | import { render, styles } from '../internals.js'; 4 | import type { RenderOptions } from '../types'; 5 | 6 | const tpl = createTemplate( 7 | `
${bar}
${bar}
${bar}
` 8 | ); 9 | 10 | export class Squash extends Burger { 11 | private [styles]!: CSSStyleDeclaration[]; 12 | 13 | constructor() { 14 | super(); 15 | this[styles] = getStyles(createRoot(this, tpl)); 16 | } 17 | 18 | protected get lines(): number { 19 | return 3; 20 | } 21 | 22 | protected [render](options: RenderOptions): void { 23 | const { barHeight, barStyles, margin, pressed, time, topOffset } = options; 24 | 25 | const half = time / 2; 26 | const wrapTransition = `${half}s ${this.easing} ${pressed ? '0s' : `${half}s`}`; 27 | const barTransition = `${half}s ${this.easing} ${pressed ? `${half}s` : '0s'}`; 28 | 29 | this.style.transition = `${time}s ${this.easing}`; 30 | 31 | setStyles(this[styles][0], { 32 | transition: wrapTransition, 33 | transform: `${pressed ? `translateY(${barHeight + margin}px)` : 'none'}` 34 | }); 35 | 36 | setStyles(this[styles][1], { 37 | ...barStyles, 38 | top: `${topOffset}px`, 39 | transition: barTransition, 40 | transform: `${pressed ? `rotate(45deg)` : 'none'}` 41 | }); 42 | 43 | setStyles(this[styles][2], { 44 | transition: `${half}s ${this.easing}`, 45 | opacity: `${pressed ? '0' : '1'}` 46 | }); 47 | 48 | setStyles(this[styles][3], { 49 | ...barStyles, 50 | top: `${topOffset + barHeight + margin}px`, 51 | transition: `${half}s ${this.easing}` 52 | }); 53 | 54 | setStyles(this[styles][4], { 55 | transition: wrapTransition, 56 | transform: `${pressed ? `translateY(-${barHeight + margin}px)` : 'none'}` 57 | }); 58 | 59 | setStyles(this[styles][5], { 60 | ...barStyles, 61 | top: `${topOffset + barHeight * 2 + margin * 2}px`, 62 | transition: barTransition, 63 | transform: `${pressed ? `rotate(-45deg)` : 'none'}` 64 | }); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/lib/entrypoints/squeeze.ts: -------------------------------------------------------------------------------- 1 | import { Burger } from '../components/burger.js'; 2 | import { bar, createTemplate, createRoot, getStyles, setStyles } from '../utils/dom.js'; 3 | import { render, styles } from '../internals.js'; 4 | import type { RenderOptions } from '../types'; 5 | 6 | const tpl = createTemplate(`
${bar}
${bar}
`); 7 | 8 | export class Squeeze extends Burger { 9 | private [styles]!: CSSStyleDeclaration[]; 10 | 11 | constructor() { 12 | super(); 13 | this[styles] = getStyles(createRoot(this, tpl)); 14 | } 15 | 16 | protected get lines(): number { 17 | return 2; 18 | } 19 | 20 | protected [render](options: RenderOptions): void { 21 | const { barHeight, barStyles, margin, pressed, time, topOffset } = options; 22 | 23 | const half = time / 2; 24 | const wrapTransition = `${half}s ${this.easing} ${pressed ? '0s' : `${half}s`}`; 25 | const barTransition = `${half}s ${this.easing} ${pressed ? `${half}s` : '0s'}`; 26 | 27 | this.style.transition = `${time}s ${this.easing}`; 28 | 29 | setStyles(this[styles][0], { 30 | transition: wrapTransition, 31 | transform: `${pressed ? `translateY(${barHeight / 2 + margin / 2}px)` : 'none'}` 32 | }); 33 | 34 | setStyles(this[styles][1], { 35 | ...barStyles, 36 | top: `${topOffset}px`, 37 | transition: barTransition, 38 | transform: `${pressed ? `rotate(45deg)` : 'none'}` 39 | }); 40 | 41 | setStyles(this[styles][2], { 42 | transition: wrapTransition, 43 | transform: `${pressed ? `translateY(-${barHeight / 2 + margin / 2}px)` : 'none'}` 44 | }); 45 | 46 | setStyles(this[styles][3], { 47 | ...barStyles, 48 | top: `${topOffset + barHeight + margin}px`, 49 | transition: barTransition, 50 | transform: `${pressed ? `rotate(-45deg)` : 'none'}` 51 | }); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/lib/entrypoints/tilt.ts: -------------------------------------------------------------------------------- 1 | import { DirectionBurger } from '../components/direction-burger.js'; 2 | import { bar, createTemplate, createRoot, getStyles, setStyles } from '../utils/dom.js'; 3 | import { render, styles } from '../internals.js'; 4 | import type { RenderOptions } from '../types'; 5 | 6 | const tpl = createTemplate(`${bar}${bar}${bar}`); 7 | 8 | export class Tilt extends DirectionBurger { 9 | private [styles]!: CSSStyleDeclaration[]; 10 | 11 | constructor() { 12 | super(); 13 | this[styles] = getStyles(createRoot(this, tpl)); 14 | } 15 | 16 | protected get lines(): number { 17 | return 3; 18 | } 19 | 20 | protected [render](options: RenderOptions): void { 21 | const { barHeight, barStyles, margin, move, pressed, time, topOffset } = options; 22 | 23 | const isLeft = this.direction === 'left'; 24 | const transition = `${time}s ${this.easing}`; 25 | 26 | setStyles(this.style, { 27 | transition, 28 | transform: `${pressed ? `rotate(${90 * (isLeft ? -1 : 1)}deg)` : 'none'}` 29 | }); 30 | 31 | setStyles(this[styles][0], { 32 | ...barStyles, 33 | top: `${topOffset}px`, 34 | transition, 35 | transform: `${ 36 | pressed ? `rotate(${45 * (isLeft ? -1 : 1)}deg) translate(${move * (isLeft ? -1 : 1)}px, ${move}px)` : 'none' 37 | }` 38 | }); 39 | 40 | setStyles(this[styles][1], { 41 | ...barStyles, 42 | top: `${topOffset + barHeight + margin}px`, 43 | transition, 44 | transform: `${pressed ? 'scaleX(0)' : 'none'}` 45 | }); 46 | 47 | setStyles(this[styles][2], { 48 | ...barStyles, 49 | top: `${topOffset + barHeight * 2 + margin * 2}px`, 50 | transition, 51 | transform: `${ 52 | pressed 53 | ? `rotate(${45 * (isLeft ? 1 : -1)}deg) translate(${move * (isLeft ? -1 : 1)}px, ${move * -1}px)` 54 | : 'none' 55 | }` 56 | }); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/lib/entrypoints/turn.ts: -------------------------------------------------------------------------------- 1 | import { DirectionBurger } from '../components/direction-burger.js'; 2 | import { bar, createTemplate, createRoot, getStyles, setStyles } from '../utils/dom.js'; 3 | import { render, styles } from '../internals.js'; 4 | import type { RenderOptions } from '../types'; 5 | 6 | const tpl = createTemplate(`${bar}${bar}${bar}`); 7 | 8 | export class Turn extends DirectionBurger { 9 | private [styles]!: CSSStyleDeclaration[]; 10 | 11 | constructor() { 12 | super(); 13 | this[styles] = getStyles(createRoot(this, tpl)); 14 | } 15 | 16 | protected get lines(): number { 17 | return 3; 18 | } 19 | 20 | protected [render](options: RenderOptions): void { 21 | const { barHeight, barStyles, margin, move, pressed, time, topOffset } = options; 22 | 23 | const isLeft = this.direction === 'left'; 24 | const transition = `${time}s ${this.easing}`; 25 | 26 | this.style.transition = transition; 27 | 28 | setStyles(this[styles][0], { 29 | ...barStyles, 30 | top: `${topOffset}px`, 31 | transition, 32 | transform: `${ 33 | pressed ? `rotate(${45 * (isLeft ? -1 : 1)}deg) translate(${move * (isLeft ? -1 : 1)}px, ${move}px)` : 'none' 34 | }` 35 | }); 36 | 37 | setStyles(this[styles][1], { 38 | ...barStyles, 39 | top: `${topOffset + barHeight + margin}px`, 40 | transition: `${time / 2}s ${this.easing}`, 41 | transform: `${pressed ? 'scaleX(0)' : 'none'}` 42 | }); 43 | 44 | setStyles(this[styles][2], { 45 | ...barStyles, 46 | top: `${topOffset + barHeight * 2 + margin * 2}px`, 47 | transition, 48 | transform: `${ 49 | pressed 50 | ? `rotate(${45 * (isLeft ? 1 : -1)}deg) translate(${move * (isLeft ? -1 : 1)}px, ${move * -1}px)` 51 | : 'none' 52 | }` 53 | }); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/lib/entrypoints/twirl.ts: -------------------------------------------------------------------------------- 1 | import { DirectionBurger } from '../components/direction-burger.js'; 2 | import { bar, createTemplate, createRoot, getStyles, setStyles } from '../utils/dom.js'; 3 | import { render, styles } from '../internals.js'; 4 | import type { RenderOptions } from '../types'; 5 | 6 | const tpl = createTemplate( 7 | `
${bar}
${bar}
${bar}
` 8 | ); 9 | 10 | export class Twirl extends DirectionBurger { 11 | private [styles]!: CSSStyleDeclaration[]; 12 | 13 | constructor() { 14 | super(); 15 | this[styles] = getStyles(createRoot(this, tpl)); 16 | } 17 | 18 | protected get lines(): number { 19 | return 3; 20 | } 21 | 22 | protected [render](options: RenderOptions): void { 23 | const { barHeight, barStyles, margin, pressed, time, topOffset } = options; 24 | 25 | const isLeft = this.direction === 'left'; 26 | 27 | const transition = `${time / 2}s ${this.easing}`; 28 | const wrapTransition = `${transition} ${pressed ? '0s' : `${time / 2}s`}`; 29 | const barTransition = `${transition} ${pressed ? `${time / 2}s` : '0s'}`; 30 | 31 | setStyles(this.style, { 32 | transition: `${time}s ${this.easing}`, 33 | transform: `${pressed ? `rotate(${90 * (isLeft ? -1 : 1)}deg)` : 'none'}` 34 | }); 35 | 36 | setStyles(this[styles][0], { 37 | transition: wrapTransition, 38 | transform: `${pressed ? `translateY(${barHeight + margin}px)` : 'none'}` 39 | }); 40 | 41 | setStyles(this[styles][1], { 42 | ...barStyles, 43 | top: `${topOffset}px`, 44 | transition: barTransition, 45 | transform: `${pressed ? `rotate(${45 * (isLeft ? 1 : -1)}deg)` : 'none'}` 46 | }); 47 | 48 | setStyles(this[styles][2], { 49 | transition, 50 | opacity: `${pressed ? '0' : '1'}` 51 | }); 52 | 53 | setStyles(this[styles][3], { 54 | ...barStyles, 55 | top: `${topOffset + barHeight + margin}px`, 56 | transition 57 | }); 58 | 59 | setStyles(this[styles][4], { 60 | transition: wrapTransition, 61 | transform: `${pressed ? `translateY(-${barHeight + margin}px)` : 'none'}` 62 | }); 63 | 64 | setStyles(this[styles][5], { 65 | ...barStyles, 66 | top: `${topOffset + barHeight * 2 + margin * 2}px`, 67 | transition: barTransition, 68 | transform: `${pressed ? `rotate(${45 * (isLeft ? -1 : 1)}deg)` : 'none'}` 69 | }); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/lib/internals.ts: -------------------------------------------------------------------------------- 1 | export const props = Symbol('props'); 2 | 3 | export const render = Symbol('render'); 4 | 5 | export const styles = Symbol('styles'); 6 | 7 | export const update = Symbol('update'); 8 | -------------------------------------------------------------------------------- /src/lib/styles/burger.scss: -------------------------------------------------------------------------------- 1 | :host { 2 | display: block; 3 | position: relative; 4 | width: 48px; 5 | height: 48px; 6 | cursor: pointer; 7 | -webkit-user-select: none; 8 | user-select: none; 9 | } 10 | 11 | :host([hidden]) { 12 | display: none !important; 13 | } 14 | 15 | :host([disabled]) { 16 | opacity: 0.5; 17 | } 18 | 19 | button { 20 | position: absolute; 21 | top: 0; 22 | left: 0; 23 | width: 100%; 24 | height: 100%; 25 | border: none; 26 | -webkit-appearance: none; 27 | background: transparent; 28 | color: inherit; 29 | } 30 | 31 | [part="bar"] { 32 | background: currentColor; 33 | position: absolute; 34 | } 35 | -------------------------------------------------------------------------------- /src/lib/types.ts: -------------------------------------------------------------------------------- 1 | export interface BurgerProps { 2 | direction: 'left' | 'right'; 3 | disabled: boolean; 4 | distance: 'sm' | 'md' | 'lg'; 5 | duration: number; 6 | easing: string; 7 | label: string; 8 | pressed: boolean; 9 | size: number; 10 | } 11 | 12 | export interface RenderOptions { 13 | barHeight: number; 14 | barStyles: Record; 15 | margin: number; 16 | move: number; 17 | pressed: boolean; 18 | time: number; 19 | topOffset: number; 20 | } 21 | -------------------------------------------------------------------------------- /src/lib/utils/dom.ts: -------------------------------------------------------------------------------- 1 | export const bar = '
'; 2 | 3 | export const createTemplate = (tpl: string): HTMLTemplateElement => { 4 | const template = document.createElement('template'); 5 | template.innerHTML = tpl; 6 | return template; 7 | }; 8 | 9 | export const createRoot = (node: T, tpl: HTMLTemplateElement): ShadowRoot => { 10 | const root = node.shadowRoot || node.attachShadow({ mode: 'open' }); 11 | root.appendChild(tpl.content.cloneNode(true)); 12 | return root; 13 | }; 14 | 15 | export const getStyles = (root: ShadowRoot): CSSStyleDeclaration[] => 16 | Array.from(root.querySelectorAll('div')).map((bar) => (bar as HTMLElement).style); 17 | 18 | export const setStyles = (style: CSSStyleDeclaration, props: Record): void => { 19 | for (const p in props) { 20 | style.setProperty(p, props[p]); 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /src/pivot-burger.ts: -------------------------------------------------------------------------------- 1 | import { Pivot } from './lib/entrypoints/pivot.js'; 2 | 3 | /** 4 | * A hamburger button custom element of Pivot type. 5 | * @element pivot-burger 6 | * @fires pressed-changed - Event fired when pressed property changes. 7 | * @csspart bar - Animated bar elements. 8 | * @csspart button - Native button element. 9 | */ 10 | export class PivotBurger extends Pivot {} 11 | 12 | customElements.define('pivot-burger', PivotBurger); 13 | 14 | declare global { 15 | interface HTMLElementTagNameMap { 16 | 'pivot-burger': PivotBurger; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/rotate-burger.ts: -------------------------------------------------------------------------------- 1 | import { Rotate } from './lib/entrypoints/rotate.js'; 2 | 3 | /** 4 | * A hamburger button custom element of Rotate type. 5 | * @element rotate-burger 6 | * @fires pressed-changed - Event fired when pressed property changes. 7 | * @csspart bar - Animated bar elements. 8 | * @csspart button - Native button element. 9 | */ 10 | export class RotateBurger extends Rotate {} 11 | 12 | customElements.define('rotate-burger', RotateBurger); 13 | 14 | declare global { 15 | interface HTMLElementTagNameMap { 16 | 'rotate-burger': RotateBurger; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/slant-burger.ts: -------------------------------------------------------------------------------- 1 | import { Slant } from './lib/entrypoints/slant.js'; 2 | 3 | /** 4 | * A hamburger button custom element of Slant type. 5 | * @element slant-burger 6 | * @fires pressed-changed - Event fired when pressed property changes. 7 | * @csspart bar - Animated bar elements. 8 | * @csspart button - Native button element. 9 | */ 10 | export class SlantBurger extends Slant {} 11 | 12 | customElements.define('slant-burger', SlantBurger); 13 | 14 | declare global { 15 | interface HTMLElementTagNameMap { 16 | 'slant-burger': SlantBurger; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/sling-burger.ts: -------------------------------------------------------------------------------- 1 | import { Sling } from './lib/entrypoints/sling.js'; 2 | 3 | /** 4 | * A hamburger button custom element of Sling type. 5 | * @element sling-burger 6 | * @fires pressed-changed - Event fired when pressed property changes. 7 | * @csspart bar - Animated bar elements. 8 | * @csspart button - Native button element. 9 | */ 10 | export class SlingBurger extends Sling {} 11 | 12 | customElements.define('sling-burger', SlingBurger); 13 | 14 | declare global { 15 | interface HTMLElementTagNameMap { 16 | 'sling-burger': SlingBurger; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/spin-burger.ts: -------------------------------------------------------------------------------- 1 | import { Spin } from './lib/entrypoints/spin.js'; 2 | 3 | /** 4 | * A hamburger button custom element of Spin type. 5 | * @element spin-burger 6 | * @fires pressed-changed - Event fired when pressed property changes. 7 | * @csspart bar - Animated bar elements. 8 | * @csspart button - Native button element. 9 | */ 10 | export class SpinBurger extends Spin {} 11 | 12 | customElements.define('spin-burger', SpinBurger); 13 | 14 | declare global { 15 | interface HTMLElementTagNameMap { 16 | 'spin-burger': SpinBurger; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/spiral-burger.ts: -------------------------------------------------------------------------------- 1 | import { Spiral } from './lib/entrypoints/spiral.js'; 2 | 3 | /** 4 | * A hamburger button custom element of Spiral type. 5 | * @element spiral-burger 6 | * @fires pressed-changed - Event fired when pressed property changes. 7 | * @csspart bar - Animated bar elements. 8 | * @csspart button - Native button element. 9 | */ 10 | export class SpiralBurger extends Spiral {} 11 | 12 | customElements.define('spiral-burger', SpiralBurger); 13 | 14 | declare global { 15 | interface HTMLElementTagNameMap { 16 | 'spiral-burger': SpiralBurger; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/squash-burger.ts: -------------------------------------------------------------------------------- 1 | import { Squash } from './lib/entrypoints/squash.js'; 2 | 3 | /** 4 | * A hamburger button custom element of Squash type. 5 | * @element squash-burger 6 | * @fires pressed-changed - Event fired when pressed property changes. 7 | * @csspart bar - Animated bar elements. 8 | * @csspart button - Native button element. 9 | */ 10 | export class SquashBurger extends Squash {} 11 | 12 | customElements.define('squash-burger', SquashBurger); 13 | 14 | declare global { 15 | interface HTMLElementTagNameMap { 16 | 'squash-burger': SquashBurger; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/squeeze-burger.ts: -------------------------------------------------------------------------------- 1 | import { Squeeze } from './lib/entrypoints/squeeze.js'; 2 | 3 | /** 4 | * A hamburger button custom element of Squeeze type. 5 | * @element squeeze-burger 6 | * @fires pressed-changed - Event fired when pressed property changes. 7 | * @csspart bar - Animated bar elements. 8 | * @csspart button - Native button element. 9 | */ 10 | export class SqueezeBurger extends Squeeze {} 11 | 12 | customElements.define('squeeze-burger', SqueezeBurger); 13 | 14 | declare global { 15 | interface HTMLElementTagNameMap { 16 | 'squeeze-burger': SqueezeBurger; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/test/hamburger.test.ts: -------------------------------------------------------------------------------- 1 | import { expect } from '@esm-bundle/chai'; 2 | import sinon from 'sinon'; 3 | import { fixture, html, nextFrame } from '@open-wc/testing-helpers'; 4 | import type { TiltBurger } from '../tilt-burger'; 5 | 6 | describe('hamburger', () => { 7 | let burger: TiltBurger; 8 | 9 | describe('lazy upgrade', () => { 10 | it('should work with properties set before upgrade', async () => { 11 | burger = document.createElement('tilt-burger'); 12 | document.body.appendChild(burger); 13 | burger.direction = 'right'; 14 | burger.distance = 'lg'; 15 | burger.duration = 0.5; 16 | burger.size = 24; 17 | await import('../tilt-burger'); 18 | expect(burger.direction).to.equal('right'); 19 | expect(burger.distance).to.equal('lg'); 20 | expect(burger.duration).to.equal(0.5); 21 | expect(burger.size).to.equal(24); 22 | document.body.removeChild(burger); 23 | }); 24 | }); 25 | 26 | describe('properties', () => { 27 | beforeEach(async () => { 28 | burger = await fixture(html``); 29 | }); 30 | 31 | it('should set default direction property value', () => { 32 | expect(burger.direction).to.equal('left'); 33 | }); 34 | 35 | it('should set default disabled property value', () => { 36 | expect(burger.disabled).to.equal(false); 37 | }); 38 | 39 | it('should set default distance property value', () => { 40 | expect(burger.distance).to.equal('md'); 41 | }); 42 | 43 | it('should set default duration property value', () => { 44 | expect(burger.duration).to.equal(0.4); 45 | }); 46 | 47 | it('should set default label property value', () => { 48 | expect(burger.label).to.equal('hamburger'); 49 | }); 50 | 51 | it('should set default size property value', () => { 52 | expect(burger.size).to.equal(32); 53 | }); 54 | 55 | it('should set default easing property value', () => { 56 | expect(burger.easing).to.equal('cubic-bezier(0, 0, 0, 1)'); 57 | }); 58 | 59 | it('should set default pressed property value', () => { 60 | expect(burger.pressed).to.equal(false); 61 | }); 62 | }); 63 | 64 | describe('attributes', () => { 65 | beforeEach(async () => { 66 | burger = await fixture(html``); 67 | }); 68 | 69 | it('should set direction property when attribute changes', () => { 70 | burger.setAttribute('direction', 'right'); 71 | expect(burger.direction).to.equal('right'); 72 | }); 73 | 74 | it('should set duration property when attribute changes', () => { 75 | burger.setAttribute('duration', '0.5'); 76 | expect(burger.duration).to.equal(0.5); 77 | }); 78 | 79 | it('should set distance property when attribute changes', () => { 80 | burger.setAttribute('distance', 'lg'); 81 | expect(burger.distance).to.equal('lg'); 82 | }); 83 | 84 | it('should set size property when attribute changes', () => { 85 | burger.setAttribute('size', '24'); 86 | expect(burger.size).to.equal(24); 87 | }); 88 | 89 | it('should set easing property when attribute changes', () => { 90 | burger.setAttribute('easing', 'ease-in'); 91 | expect(burger.easing).to.equal('ease-in'); 92 | }); 93 | 94 | it('should set pressed property when attribute changes', () => { 95 | burger.setAttribute('pressed', ''); 96 | expect(burger.pressed).to.equal(true); 97 | }); 98 | 99 | it('should set pressed attribute when property changes', () => { 100 | burger.pressed = true; 101 | expect(burger.hasAttribute('pressed')).to.be.true; 102 | }); 103 | 104 | it('should set disabled property when attribute changes', () => { 105 | burger.setAttribute('disabled', ''); 106 | expect(burger.disabled).to.equal(true); 107 | }); 108 | 109 | it('should set disabled attribute when property changes', () => { 110 | burger.disabled = true; 111 | expect(burger.hasAttribute('disabled')).to.be.true; 112 | }); 113 | }); 114 | 115 | describe('native button', () => { 116 | let button: HTMLElement; 117 | 118 | beforeEach(async () => { 119 | burger = await fixture(html``); 120 | button = burger.shadowRoot?.querySelector('button') as HTMLElement; 121 | }); 122 | 123 | it('should render native button in shadow root', () => { 124 | expect(button).to.be.ok; 125 | }); 126 | 127 | it('should set part attribute on the native button', () => { 128 | expect(button.getAttribute('part')).to.equal('button'); 129 | }); 130 | 131 | it('should set aria-pressed to false by default', () => { 132 | expect(button.getAttribute('aria-pressed')).to.equal('false'); 133 | }); 134 | 135 | it('should update aria-pressed on button click', () => { 136 | button.click(); 137 | expect(button.getAttribute('aria-pressed')).to.equal('true'); 138 | }); 139 | 140 | it('should update aria-pressed on pressed change', () => { 141 | burger.pressed = true; 142 | expect(button.getAttribute('aria-pressed')).to.equal('true'); 143 | }); 144 | 145 | it('should not throw on disabled change if not connected', () => { 146 | expect(() => { 147 | document.createElement('tilt-burger').disabled = true; 148 | }).to.not.throw(Error); 149 | }); 150 | 151 | it('should not throw on pressed change if not connected', () => { 152 | expect(() => { 153 | document.createElement('tilt-burger').pressed = true; 154 | }).to.not.throw(Error); 155 | }); 156 | 157 | it('should not throw on label change if not connected', () => { 158 | expect(() => { 159 | document.createElement('tilt-burger').label = 'menu'; 160 | }).to.not.throw(Error); 161 | }); 162 | 163 | it('should toggle disabled on the native button', () => { 164 | burger.disabled = true; 165 | expect(button.hasAttribute('disabled')).to.be.true; 166 | burger.disabled = false; 167 | expect(button.hasAttribute('disabled')).to.be.false; 168 | }); 169 | 170 | it('should set aria-label on the native button', () => { 171 | expect(button.getAttribute('aria-label')).to.equal('hamburger'); 172 | }); 173 | 174 | it('should update aria-label on label change', () => { 175 | burger.label = 'burger'; 176 | expect(button.getAttribute('aria-label')).to.equal('burger'); 177 | }); 178 | 179 | it('should focus native button using focus method', () => { 180 | const spy = sinon.spy(button, 'focus'); 181 | burger.focus(); 182 | expect(spy.calledOnce).to.be.true; 183 | }); 184 | 185 | it('should blur native button using blur method', () => { 186 | const spy = sinon.spy(button, 'blur'); 187 | burger.blur(); 188 | expect(spy.calledOnce).to.be.true; 189 | }); 190 | 191 | it('should not throw on focus if not connected', () => { 192 | expect(() => { 193 | document.createElement('tilt-burger').focus(); 194 | }).to.not.throw(Error); 195 | }); 196 | 197 | it('should not throw on blur if not connected', () => { 198 | expect(() => { 199 | document.createElement('tilt-burger').blur(); 200 | }).to.not.throw(Error); 201 | }); 202 | }); 203 | 204 | describe('styles', () => { 205 | let root: ShadowRoot; 206 | let bars: HTMLElement[]; 207 | 208 | beforeEach(async () => { 209 | burger = await fixture(html``); 210 | root = burger.shadowRoot as ShadowRoot; 211 | bars = Array.from(root.querySelectorAll('[part="bar"]')); 212 | }); 213 | 214 | it('should change display to none when hidden', () => { 215 | burger.setAttribute('hidden', ''); 216 | expect(getComputedStyle(burger).display).to.equal('none'); 217 | }); 218 | 219 | it('should set bars background when color is set', () => { 220 | burger.style.color = 'red'; 221 | bars.forEach((bar) => { 222 | expect(getComputedStyle(bar).backgroundColor).to.equal('rgb(255, 0, 0)'); 223 | }); 224 | }); 225 | }); 226 | 227 | describe('pressed', () => { 228 | beforeEach(async () => { 229 | burger = await fixture(html``); 230 | }); 231 | 232 | it('should toggle pressed property on click', () => { 233 | burger.dispatchEvent(new CustomEvent('click')); 234 | expect(burger.pressed).to.equal(true); 235 | burger.dispatchEvent(new CustomEvent('click')); 236 | expect(burger.pressed).to.equal(false); 237 | }); 238 | 239 | it('should fire pressed-changed event when property changes', () => { 240 | const spy = sinon.spy(); 241 | burger.addEventListener('pressed-changed', spy); 242 | burger.click(); 243 | expect(spy.callCount).to.equal(1); 244 | }); 245 | 246 | it('should not toggle pressed property on click if disabled', () => { 247 | burger.disabled = true; 248 | burger.click(); 249 | expect(burger.pressed).to.equal(false); 250 | }); 251 | }); 252 | 253 | describe('render', () => { 254 | let spy: sinon.SinonSpy; 255 | 256 | beforeEach(async () => { 257 | burger = await fixture(html``); 258 | spy = sinon.spy(burger.style, 'setProperty'); 259 | }); 260 | 261 | it('should update styles when direction property changes', async () => { 262 | burger.direction = 'right'; 263 | await nextFrame(); 264 | expect(spy.callCount).to.equal(2); 265 | }); 266 | 267 | it('should update styles when duration property changes', async () => { 268 | burger.duration = 0.5; 269 | await nextFrame(); 270 | expect(spy.callCount).to.equal(2); 271 | }); 272 | 273 | it('should update styles when size property changes', async () => { 274 | burger.size = 24; 275 | await nextFrame(); 276 | expect(spy.callCount).to.equal(2); 277 | }); 278 | 279 | it('should update styles when easing property changes', async () => { 280 | burger.easing = 'ease-in'; 281 | await nextFrame(); 282 | expect(spy.callCount).to.equal(2); 283 | }); 284 | 285 | it('should update styles when pressed property changes', async () => { 286 | burger.pressed = true; 287 | await nextFrame(); 288 | expect(spy.callCount).to.equal(2); 289 | }); 290 | 291 | it('should only trigger update once within a microtask', async () => { 292 | burger.distance = 'lg'; 293 | burger.pressed = true; 294 | burger.easing = 'ease-in'; 295 | await nextFrame(); 296 | expect(spy.callCount).to.equal(2); 297 | }); 298 | }); 299 | }); 300 | -------------------------------------------------------------------------------- /src/test/visual/screenshots/Chrome/baseline/cross-default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-padawan/vanilla-hamburger/52ffb9a0a3bba7ec3245f3c99761636f1d694e77/src/test/visual/screenshots/Chrome/baseline/cross-default.png -------------------------------------------------------------------------------- /src/test/visual/screenshots/Chrome/baseline/cross-disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-padawan/vanilla-hamburger/52ffb9a0a3bba7ec3245f3c99761636f1d694e77/src/test/visual/screenshots/Chrome/baseline/cross-disabled.png -------------------------------------------------------------------------------- /src/test/visual/screenshots/Chrome/baseline/cross-pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-padawan/vanilla-hamburger/52ffb9a0a3bba7ec3245f3c99761636f1d694e77/src/test/visual/screenshots/Chrome/baseline/cross-pressed.png -------------------------------------------------------------------------------- /src/test/visual/screenshots/Chrome/baseline/fade-default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-padawan/vanilla-hamburger/52ffb9a0a3bba7ec3245f3c99761636f1d694e77/src/test/visual/screenshots/Chrome/baseline/fade-default.png -------------------------------------------------------------------------------- /src/test/visual/screenshots/Chrome/baseline/fade-disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-padawan/vanilla-hamburger/52ffb9a0a3bba7ec3245f3c99761636f1d694e77/src/test/visual/screenshots/Chrome/baseline/fade-disabled.png -------------------------------------------------------------------------------- /src/test/visual/screenshots/Chrome/baseline/fade-pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-padawan/vanilla-hamburger/52ffb9a0a3bba7ec3245f3c99761636f1d694e77/src/test/visual/screenshots/Chrome/baseline/fade-pressed.png -------------------------------------------------------------------------------- /src/test/visual/screenshots/Chrome/baseline/pivot-default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-padawan/vanilla-hamburger/52ffb9a0a3bba7ec3245f3c99761636f1d694e77/src/test/visual/screenshots/Chrome/baseline/pivot-default.png -------------------------------------------------------------------------------- /src/test/visual/screenshots/Chrome/baseline/pivot-disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-padawan/vanilla-hamburger/52ffb9a0a3bba7ec3245f3c99761636f1d694e77/src/test/visual/screenshots/Chrome/baseline/pivot-disabled.png -------------------------------------------------------------------------------- /src/test/visual/screenshots/Chrome/baseline/pivot-pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-padawan/vanilla-hamburger/52ffb9a0a3bba7ec3245f3c99761636f1d694e77/src/test/visual/screenshots/Chrome/baseline/pivot-pressed.png -------------------------------------------------------------------------------- /src/test/visual/screenshots/Chrome/baseline/rotate-default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-padawan/vanilla-hamburger/52ffb9a0a3bba7ec3245f3c99761636f1d694e77/src/test/visual/screenshots/Chrome/baseline/rotate-default.png -------------------------------------------------------------------------------- /src/test/visual/screenshots/Chrome/baseline/rotate-disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-padawan/vanilla-hamburger/52ffb9a0a3bba7ec3245f3c99761636f1d694e77/src/test/visual/screenshots/Chrome/baseline/rotate-disabled.png -------------------------------------------------------------------------------- /src/test/visual/screenshots/Chrome/baseline/rotate-pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-padawan/vanilla-hamburger/52ffb9a0a3bba7ec3245f3c99761636f1d694e77/src/test/visual/screenshots/Chrome/baseline/rotate-pressed.png -------------------------------------------------------------------------------- /src/test/visual/screenshots/Chrome/baseline/slant-default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-padawan/vanilla-hamburger/52ffb9a0a3bba7ec3245f3c99761636f1d694e77/src/test/visual/screenshots/Chrome/baseline/slant-default.png -------------------------------------------------------------------------------- /src/test/visual/screenshots/Chrome/baseline/slant-disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-padawan/vanilla-hamburger/52ffb9a0a3bba7ec3245f3c99761636f1d694e77/src/test/visual/screenshots/Chrome/baseline/slant-disabled.png -------------------------------------------------------------------------------- /src/test/visual/screenshots/Chrome/baseline/slant-pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-padawan/vanilla-hamburger/52ffb9a0a3bba7ec3245f3c99761636f1d694e77/src/test/visual/screenshots/Chrome/baseline/slant-pressed.png -------------------------------------------------------------------------------- /src/test/visual/screenshots/Chrome/baseline/sling-default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-padawan/vanilla-hamburger/52ffb9a0a3bba7ec3245f3c99761636f1d694e77/src/test/visual/screenshots/Chrome/baseline/sling-default.png -------------------------------------------------------------------------------- /src/test/visual/screenshots/Chrome/baseline/sling-disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-padawan/vanilla-hamburger/52ffb9a0a3bba7ec3245f3c99761636f1d694e77/src/test/visual/screenshots/Chrome/baseline/sling-disabled.png -------------------------------------------------------------------------------- /src/test/visual/screenshots/Chrome/baseline/sling-pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-padawan/vanilla-hamburger/52ffb9a0a3bba7ec3245f3c99761636f1d694e77/src/test/visual/screenshots/Chrome/baseline/sling-pressed.png -------------------------------------------------------------------------------- /src/test/visual/screenshots/Chrome/baseline/spin-default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-padawan/vanilla-hamburger/52ffb9a0a3bba7ec3245f3c99761636f1d694e77/src/test/visual/screenshots/Chrome/baseline/spin-default.png -------------------------------------------------------------------------------- /src/test/visual/screenshots/Chrome/baseline/spin-disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-padawan/vanilla-hamburger/52ffb9a0a3bba7ec3245f3c99761636f1d694e77/src/test/visual/screenshots/Chrome/baseline/spin-disabled.png -------------------------------------------------------------------------------- /src/test/visual/screenshots/Chrome/baseline/spin-pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-padawan/vanilla-hamburger/52ffb9a0a3bba7ec3245f3c99761636f1d694e77/src/test/visual/screenshots/Chrome/baseline/spin-pressed.png -------------------------------------------------------------------------------- /src/test/visual/screenshots/Chrome/baseline/spiral-default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-padawan/vanilla-hamburger/52ffb9a0a3bba7ec3245f3c99761636f1d694e77/src/test/visual/screenshots/Chrome/baseline/spiral-default.png -------------------------------------------------------------------------------- /src/test/visual/screenshots/Chrome/baseline/spiral-disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-padawan/vanilla-hamburger/52ffb9a0a3bba7ec3245f3c99761636f1d694e77/src/test/visual/screenshots/Chrome/baseline/spiral-disabled.png -------------------------------------------------------------------------------- /src/test/visual/screenshots/Chrome/baseline/spiral-pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-padawan/vanilla-hamburger/52ffb9a0a3bba7ec3245f3c99761636f1d694e77/src/test/visual/screenshots/Chrome/baseline/spiral-pressed.png -------------------------------------------------------------------------------- /src/test/visual/screenshots/Chrome/baseline/squash-default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-padawan/vanilla-hamburger/52ffb9a0a3bba7ec3245f3c99761636f1d694e77/src/test/visual/screenshots/Chrome/baseline/squash-default.png -------------------------------------------------------------------------------- /src/test/visual/screenshots/Chrome/baseline/squash-disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-padawan/vanilla-hamburger/52ffb9a0a3bba7ec3245f3c99761636f1d694e77/src/test/visual/screenshots/Chrome/baseline/squash-disabled.png -------------------------------------------------------------------------------- /src/test/visual/screenshots/Chrome/baseline/squash-pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-padawan/vanilla-hamburger/52ffb9a0a3bba7ec3245f3c99761636f1d694e77/src/test/visual/screenshots/Chrome/baseline/squash-pressed.png -------------------------------------------------------------------------------- /src/test/visual/screenshots/Chrome/baseline/squeeze-default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-padawan/vanilla-hamburger/52ffb9a0a3bba7ec3245f3c99761636f1d694e77/src/test/visual/screenshots/Chrome/baseline/squeeze-default.png -------------------------------------------------------------------------------- /src/test/visual/screenshots/Chrome/baseline/squeeze-disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-padawan/vanilla-hamburger/52ffb9a0a3bba7ec3245f3c99761636f1d694e77/src/test/visual/screenshots/Chrome/baseline/squeeze-disabled.png -------------------------------------------------------------------------------- /src/test/visual/screenshots/Chrome/baseline/squeeze-pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-padawan/vanilla-hamburger/52ffb9a0a3bba7ec3245f3c99761636f1d694e77/src/test/visual/screenshots/Chrome/baseline/squeeze-pressed.png -------------------------------------------------------------------------------- /src/test/visual/screenshots/Chrome/baseline/tilt-default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-padawan/vanilla-hamburger/52ffb9a0a3bba7ec3245f3c99761636f1d694e77/src/test/visual/screenshots/Chrome/baseline/tilt-default.png -------------------------------------------------------------------------------- /src/test/visual/screenshots/Chrome/baseline/tilt-disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-padawan/vanilla-hamburger/52ffb9a0a3bba7ec3245f3c99761636f1d694e77/src/test/visual/screenshots/Chrome/baseline/tilt-disabled.png -------------------------------------------------------------------------------- /src/test/visual/screenshots/Chrome/baseline/tilt-pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-padawan/vanilla-hamburger/52ffb9a0a3bba7ec3245f3c99761636f1d694e77/src/test/visual/screenshots/Chrome/baseline/tilt-pressed.png -------------------------------------------------------------------------------- /src/test/visual/screenshots/Chrome/baseline/turn-default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-padawan/vanilla-hamburger/52ffb9a0a3bba7ec3245f3c99761636f1d694e77/src/test/visual/screenshots/Chrome/baseline/turn-default.png -------------------------------------------------------------------------------- /src/test/visual/screenshots/Chrome/baseline/turn-disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-padawan/vanilla-hamburger/52ffb9a0a3bba7ec3245f3c99761636f1d694e77/src/test/visual/screenshots/Chrome/baseline/turn-disabled.png -------------------------------------------------------------------------------- /src/test/visual/screenshots/Chrome/baseline/turn-pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-padawan/vanilla-hamburger/52ffb9a0a3bba7ec3245f3c99761636f1d694e77/src/test/visual/screenshots/Chrome/baseline/turn-pressed.png -------------------------------------------------------------------------------- /src/test/visual/screenshots/Chrome/baseline/twirl-default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-padawan/vanilla-hamburger/52ffb9a0a3bba7ec3245f3c99761636f1d694e77/src/test/visual/screenshots/Chrome/baseline/twirl-default.png -------------------------------------------------------------------------------- /src/test/visual/screenshots/Chrome/baseline/twirl-disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-padawan/vanilla-hamburger/52ffb9a0a3bba7ec3245f3c99761636f1d694e77/src/test/visual/screenshots/Chrome/baseline/twirl-disabled.png -------------------------------------------------------------------------------- /src/test/visual/screenshots/Chrome/baseline/twirl-pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-padawan/vanilla-hamburger/52ffb9a0a3bba7ec3245f3c99761636f1d694e77/src/test/visual/screenshots/Chrome/baseline/twirl-pressed.png -------------------------------------------------------------------------------- /src/test/visual/visual.test.ts: -------------------------------------------------------------------------------- 1 | import { visualDiff } from '@web/test-runner-visual-regression'; 2 | import { fixture, nextFrame } from '@open-wc/testing-helpers'; 3 | import type { Burger } from '../../lib/components/burger'; 4 | import '../../cross-burger.js'; 5 | import '../../fade-burger.js'; 6 | import '../../pivot-burger.js'; 7 | import '../../rotate-burger.js'; 8 | import '../../slant-burger.js'; 9 | import '../../sling-burger.js'; 10 | import '../../spin-burger.js'; 11 | import '../../spiral-burger.js'; 12 | import '../../squash-burger.js'; 13 | import '../../squeeze-burger.js'; 14 | import '../../tilt-burger.js'; 15 | import '../../turn-burger.js'; 16 | import '../../twirl-burger.js'; 17 | 18 | describe('visual tests', () => { 19 | let burger: Burger; 20 | 21 | [ 22 | 'cross', 23 | 'fade', 24 | 'pivot', 25 | 'rotate', 26 | 'slant', 27 | 'sling', 28 | 'spin', 29 | 'spiral', 30 | 'squash', 31 | 'squeeze', 32 | 'tilt', 33 | 'turn', 34 | 'twirl' 35 | ].forEach((type) => { 36 | describe(`${type}-burger`, () => { 37 | beforeEach(async () => { 38 | burger = await fixture(`<${type}-burger>`); 39 | }); 40 | 41 | it(`should render default ${type}-burger`, async () => { 42 | await visualDiff(burger, `${type}-default`); 43 | }); 44 | 45 | it(`should render pressed ${type}-burger`, async () => { 46 | burger.pressed = true; 47 | await nextFrame(); 48 | await visualDiff(burger, `${type}-pressed`); 49 | }); 50 | 51 | it(`should render disabled ${type}-burger`, async () => { 52 | burger.disabled = true; 53 | await nextFrame(); 54 | await visualDiff(burger, `${type}-disabled`); 55 | }); 56 | }); 57 | }); 58 | }); 59 | -------------------------------------------------------------------------------- /src/tilt-burger.ts: -------------------------------------------------------------------------------- 1 | import { Tilt } from './lib/entrypoints/tilt.js'; 2 | 3 | /** 4 | * A hamburger button custom element of Tilt type. 5 | * @element tilt-burger 6 | * @fires pressed-changed - Event fired when pressed property changes. 7 | * @csspart bar - Animated bar elements. 8 | * @csspart button - Native button element. 9 | */ 10 | export class TiltBurger extends Tilt {} 11 | 12 | customElements.define('tilt-burger', TiltBurger); 13 | 14 | declare global { 15 | interface HTMLElementTagNameMap { 16 | 'tilt-burger': TiltBurger; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/turn-burger.ts: -------------------------------------------------------------------------------- 1 | import { Turn } from './lib/entrypoints/turn.js'; 2 | 3 | /** 4 | * A hamburger button custom element of Turn type. 5 | * @element turn-burger 6 | * @fires pressed-changed - Event fired when pressed property changes. 7 | * @csspart bar - Animated bar elements. 8 | * @csspart button - Native button element. 9 | */ 10 | export class TurnBurger extends Turn {} 11 | 12 | customElements.define('turn-burger', TurnBurger); 13 | 14 | declare global { 15 | interface HTMLElementTagNameMap { 16 | 'turn-burger': TurnBurger; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/twirl-burger.ts: -------------------------------------------------------------------------------- 1 | import { Twirl } from './lib/entrypoints/twirl.js'; 2 | 3 | /** 4 | * A hamburger button custom element of Twirl type. 5 | * @element twirl-burger 6 | * @fires pressed-changed - Event fired when pressed property changes. 7 | * @csspart bar - Animated bar elements. 8 | * @csspart button - Native button element. 9 | */ 10 | export class TwirlBurger extends Twirl {} 11 | 12 | customElements.define('twirl-burger', TwirlBurger); 13 | 14 | declare global { 15 | interface HTMLElementTagNameMap { 16 | 'twirl-burger': TwirlBurger; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./", 4 | "target": "esNext", 5 | "module": "esNext", 6 | "declaration": true, 7 | "declarationMap": true, 8 | "sourceMap": true, 9 | "inlineSources": true, 10 | "lib": ["esnext", "es2017", "dom"], 11 | "moduleResolution": "node", 12 | "noFallthroughCasesInSwitch": true, 13 | "noImplicitAny": true, 14 | "noImplicitReturns": true, 15 | "noImplicitThis": true, 16 | "noUnusedLocals": true, 17 | "noUnusedParameters": true, 18 | "strict": true, 19 | "allowSyntheticDefaultImports": true 20 | }, 21 | "include": ["src/**/*.ts"], 22 | "exclude": [] 23 | } 24 | -------------------------------------------------------------------------------- /web-test-runner.config.js: -------------------------------------------------------------------------------- 1 | const { esbuildPlugin } = require('@web/dev-server-esbuild'); 2 | const { visualRegressionPlugin } = require('@web/test-runner-visual-regression/plugin'); 3 | 4 | module.exports = { 5 | nodeResolve: true, 6 | plugins: [ 7 | esbuildPlugin({ ts: true }), 8 | visualRegressionPlugin({ 9 | baseDir: 'src/test/visual/screenshots', 10 | diffOptions: { 11 | threshold: 0.2 12 | }, 13 | update: process.env.UPDATE_REFS === 'true' 14 | }) 15 | ], 16 | coverageConfig: { 17 | threshold: { 18 | statements: 98, 19 | branches: 75, 20 | functions: 100, 21 | lines: 98 22 | } 23 | } 24 | }; 25 | --------------------------------------------------------------------------------