├── .gitignore ├── assets ├── bottom-sheet-demo.gif └── bottom-sheet-screenshot.png ├── index.html ├── main.js ├── package-lock.json ├── package.json ├── readme.md ├── src ├── TouchDragListener.js ├── _bottom-sheet.scss ├── _keyframes.scss └── _reboot.scss ├── style.scss ├── yarn-error.log └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | node_modules/ 3 | .cache/ 4 | -------------------------------------------------------------------------------- /assets/bottom-sheet-demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hwindo/bottom-sheet/7de993eb490a3860d65aa65f6b59dd40f9d41279/assets/bottom-sheet-demo.gif -------------------------------------------------------------------------------- /assets/bottom-sheet-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hwindo/bottom-sheet/7de993eb490a3860d65aa65f6b59dd40f9d41279/assets/bottom-sheet-screenshot.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Parcel Sandbox 7 | 8 | 9 | 10 | 11 |
12 |
13 |

Bottom-Sheet

14 | 15 |
16 | 17 |
18 |
19 |
20 | 21 |
22 | 23 | 24 |
25 | 26 | 37 | 38 |
39 |
40 | 41 |
42 |
43 | 44 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | import "./style.scss"; 2 | import TouchDragListener from "./src/TouchDragListener.js"; 3 | 4 | /** 5 | * Bottom Sheet 6 | * @author Windo 7 | */ 8 | class BottomSheet { 9 | constructor(id) { 10 | this.id = id; 11 | this.el = document.getElementById(id); 12 | this.scrim = this.el.querySelector(".c-bottom-sheet__scrim"); 13 | this.handle = this.el.querySelector(".c-bottom-sheet__handle"); 14 | this.sheet = this.el.querySelector(".c-bottom-sheet__sheet"); 15 | this.activate = this.activate.bind(this); 16 | this.deactivate = this.deactivate.bind(this); 17 | 18 | this.scrim.addEventListener("click", this.deactivate); 19 | this.handle.addEventListener("click", this.deactivate); 20 | 21 | this.sheetListener = new TouchDragListener({ 22 | el: this.sheet, 23 | touchStartCallback: ({el, active, initialY, currentY, yOffset}) => { 24 | el.style.setProperty("--translateY", `translateY(0)`); 25 | el.style.setProperty("transition", `unset`); 26 | }, 27 | touchEndCallback: ({el, active, initialY, currentY, yOffset}) => { 28 | el.style.setProperty( 29 | "transition", 30 | `transform 150ms cubic-bezier(0.4, 0, 0.2, 1)` 31 | ); 32 | el.style.setProperty( 33 | "--translateY", 34 | `translateY(${currentY}px)` 35 | ); 36 | }, 37 | touchMoveCallback: ({el, active, initialY, currentY, yOffset}) => { 38 | if (currentY <= -40) { 39 | currentY = -41 + currentY / 10; 40 | } else if (currentY <= -60) { 41 | currentY = -60; 42 | } else if (currentY >= 210) { 43 | this.deactivate(currentY); 44 | return; 45 | } 46 | 47 | el.style.setProperty( 48 | "--translateY", 49 | `translateY(${currentY}px)` 50 | ); 51 | } 52 | }); 53 | 54 | this.scrimListener = new TouchDragListener({ 55 | el: this.scrim, 56 | touchMoveCallback: ({el, active, initialY, currentY, yOffset}) => { 57 | if (currentY >= 83) { 58 | this.deactivate(); 59 | return; 60 | } 61 | } 62 | }); 63 | } 64 | activate(e) { 65 | if (e) e.preventDefault(); 66 | this.el.classList.add("active"); 67 | } 68 | deactivate(translateY) { 69 | if (!translateY) { 70 | this.sheet.style.setProperty("--translateY", `translateY(201px)`); 71 | } else { 72 | this.sheet.style.setProperty( 73 | "transition", 74 | `transform 150ms cubic-bezier(0.4, 0, 0.2, 1)` 75 | ); 76 | this.sheet.style.setProperty( 77 | "--translateY", 78 | `translateY(${translateY}px)` 79 | ); 80 | } 81 | 82 | this.el.classList.remove("active"); 83 | } 84 | } 85 | 86 | const bottomSheet = new BottomSheet("country-selector"); 87 | document 88 | .getElementById("country-select-button") 89 | .addEventListener("click", bottomSheet.activate); 90 | 91 | window.bottomSheet = bottomSheet; 92 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bottom-sheet", 3 | "version": "0.0.1", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "anymatch": { 8 | "version": "3.1.1", 9 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", 10 | "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", 11 | "dev": true, 12 | "requires": { 13 | "normalize-path": "^3.0.0", 14 | "picomatch": "^2.0.4" 15 | } 16 | }, 17 | "binary-extensions": { 18 | "version": "2.1.0", 19 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", 20 | "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==", 21 | "dev": true 22 | }, 23 | "braces": { 24 | "version": "3.0.2", 25 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", 26 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", 27 | "dev": true, 28 | "requires": { 29 | "fill-range": "^7.0.1" 30 | } 31 | }, 32 | "chokidar": { 33 | "version": "3.4.2", 34 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.2.tgz", 35 | "integrity": "sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A==", 36 | "dev": true, 37 | "requires": { 38 | "anymatch": "~3.1.1", 39 | "braces": "~3.0.2", 40 | "fsevents": "~2.1.2", 41 | "glob-parent": "~5.1.0", 42 | "is-binary-path": "~2.1.0", 43 | "is-glob": "~4.0.1", 44 | "normalize-path": "~3.0.0", 45 | "readdirp": "~3.4.0" 46 | } 47 | }, 48 | "fill-range": { 49 | "version": "7.0.1", 50 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", 51 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", 52 | "dev": true, 53 | "requires": { 54 | "to-regex-range": "^5.0.1" 55 | } 56 | }, 57 | "fsevents": { 58 | "version": "2.1.3", 59 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", 60 | "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", 61 | "dev": true, 62 | "optional": true 63 | }, 64 | "glob-parent": { 65 | "version": "5.1.1", 66 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", 67 | "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", 68 | "dev": true, 69 | "requires": { 70 | "is-glob": "^4.0.1" 71 | } 72 | }, 73 | "is-binary-path": { 74 | "version": "2.1.0", 75 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 76 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 77 | "dev": true, 78 | "requires": { 79 | "binary-extensions": "^2.0.0" 80 | } 81 | }, 82 | "is-extglob": { 83 | "version": "2.1.1", 84 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 85 | "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", 86 | "dev": true 87 | }, 88 | "is-glob": { 89 | "version": "4.0.1", 90 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", 91 | "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", 92 | "dev": true, 93 | "requires": { 94 | "is-extglob": "^2.1.1" 95 | } 96 | }, 97 | "is-number": { 98 | "version": "7.0.0", 99 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 100 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 101 | "dev": true 102 | }, 103 | "normalize-path": { 104 | "version": "3.0.0", 105 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 106 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 107 | "dev": true 108 | }, 109 | "picomatch": { 110 | "version": "2.2.2", 111 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", 112 | "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", 113 | "dev": true 114 | }, 115 | "readdirp": { 116 | "version": "3.4.0", 117 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz", 118 | "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==", 119 | "dev": true, 120 | "requires": { 121 | "picomatch": "^2.2.1" 122 | } 123 | }, 124 | "sass": { 125 | "version": "1.26.10", 126 | "resolved": "https://registry.npmjs.org/sass/-/sass-1.26.10.tgz", 127 | "integrity": "sha512-bzN0uvmzfsTvjz0qwccN1sPm2HxxpNI/Xa+7PlUEMS+nQvbyuEK7Y0qFqxlPHhiNHb1Ze8WQJtU31olMObkAMw==", 128 | "dev": true, 129 | "requires": { 130 | "chokidar": ">=2.0.0 <4.0.0" 131 | } 132 | }, 133 | "to-regex-range": { 134 | "version": "5.0.1", 135 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 136 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 137 | "dev": true, 138 | "requires": { 139 | "is-number": "^7.0.0" 140 | } 141 | } 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bottom-sheet", 3 | "version": "0.0.1", 4 | "description": "Bottom sheet material like", 5 | "main": "main.js", 6 | "scripts": { 7 | "dev": "parcel watch index.html", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "keywords": [ 11 | "material", 12 | "bottom-sheet" 13 | ], 14 | "author": "Herwindo Artono", 15 | "license": "ISC", 16 | "devDependencies": { 17 | "sass": "^1.22.7" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Bottom-Sheet 2 | Vanilla Javascript Material design's Bottom-sheet for web 3 | 4 | ![Bottom-Sheet Demo Gif](./assets/bottom-sheet-demo.gif) 5 | 6 | ![Bottom-Sheet Demo Screenshot](./assets/bottom-sheet-screenshot.png) 7 | 8 | ## Demo 9 | Setup parcel before running the demo 10 | `npm install -g parcel-bundler` 11 | 12 | and then just run 13 | `parcel index.html` 14 | -------------------------------------------------------------------------------- /src/TouchDragListener.js: -------------------------------------------------------------------------------- 1 | 2 | class TouchDragListener { 3 | constructor({el, touchStartCallback, touchEndCallback, touchMoveCallback, showLog}) { 4 | this.el = el; 5 | this.touchStartCallback = touchStartCallback; 6 | this.touchEndCallback = touchEndCallback; 7 | this.touchMoveCallback = touchMoveCallback; 8 | this.showLog = showLog; 9 | 10 | this.active = false; 11 | this.currentY; 12 | this.initialY; 13 | this.yOffset = 0; 14 | 15 | this.dragStart = this.dragStart.bind(this); 16 | this.dragEnd = this.dragEnd.bind(this); 17 | this.drag = this.drag.bind(this); 18 | 19 | this.el.addEventListener("mousedown", this.dragStart); 20 | this.el.addEventListener("mouseleave", this.dragEnd); 21 | this.el.addEventListener("mouseup", this.dragEnd); 22 | this.el.addEventListener("mousemove", this.drag); 23 | 24 | this.el.addEventListener("touchstart", this.dragStart); 25 | this.el.addEventListener("touchend", this.dragEnd); 26 | this.el.addEventListener("touchmove", this.drag); 27 | } 28 | 29 | dragStart(e) { 30 | this.active = true; 31 | this.el.classList.add("active"); 32 | 33 | if (e.type === "touchstart") { 34 | this.initialY = e.touches[0].clientY - this.yOffset; 35 | } else { 36 | this.initialY = e.clientY - this.yOffset; 37 | } 38 | 39 | if (!this.touchStartCallback) return; 40 | 41 | this.touchStartCallback({ 42 | el: this.el, 43 | active: this.active, 44 | currentY: this.currentY, 45 | initialY: this.initialY, 46 | yOffset: this.offSetY 47 | }) 48 | } 49 | 50 | dragEnd(e) { 51 | this.active = false; 52 | this.el.classList.remove("active"); 53 | 54 | this.yOffset = 0; 55 | 56 | this.initialY = this.currentY; 57 | 58 | if (!this.touchEndCallback) return; 59 | 60 | this.touchEndCallback({ 61 | el: this.el, 62 | active: this.active, 63 | currentY: this.currentY, 64 | initialY: this.initialY, 65 | yOffset: this.offSetY 66 | }) 67 | } 68 | drag(e) { 69 | if (!this.active) return; 70 | e.preventDefault(); 71 | 72 | if (e.type === "touchmove") { 73 | this.currentY = e.touches[0].clientY - this.initialY; 74 | } else { 75 | this.currentY = e.clientY - this.initialY; 76 | } 77 | 78 | this.yOffset = this.currentX; 79 | 80 | if (!this.touchMoveCallback) return; 81 | 82 | this.touchMoveCallback({ 83 | el: this.el, 84 | active: this.active, 85 | currentY: this.currentY, 86 | initialY: this.initialY, 87 | yOffset: this.offSetY 88 | }); 89 | 90 | if (this.showLog) { 91 | console.log({ 92 | active: this.active, 93 | initialY: this.initialY, 94 | currentY: this.currentY, 95 | offSetY: this.offSetY 96 | }); 97 | } 98 | } 99 | } 100 | 101 | export default TouchDragListener; -------------------------------------------------------------------------------- /src/_bottom-sheet.scss: -------------------------------------------------------------------------------- 1 | :root { 2 | --translatey: 0px; 3 | } 4 | 5 | body { 6 | perspective: 1000px; 7 | } 8 | 9 | .c-bottom-sheet { 10 | $component: &; 11 | $timing-fn: cubic-bezier(0.4, 0, 0.2, 1); 12 | 13 | pointer-events: none; 14 | visibility: hidden; 15 | overflow: hidden; 16 | 17 | &.active { 18 | visibility: visible; 19 | pointer-events: unset; 20 | } 21 | 22 | position: fixed; 23 | top: 0; 24 | height: 100vh; 25 | width: 100vw; 26 | z-index: 15; 27 | transition: opacity, visibility 0.25s; 28 | 29 | &__scrim { 30 | opacity: 0; 31 | 32 | .active & { 33 | opacity: 1; 34 | } 35 | 36 | display: block; 37 | position: absolute; 38 | height: 100vh; 39 | width: 100vw; 40 | background-color: rgba(0, 0, 0, 0.3); 41 | transition: opacity 0.3s; 42 | top: 0; 43 | } 44 | 45 | &__sheet { 46 | display: inline-block; 47 | position: absolute; 48 | left: 0; 49 | bottom: -100px; 50 | width: 100%; 51 | min-height: 38vh; 52 | 53 | background-color: white; 54 | border-radius: 12px 12px 0 0; 55 | padding: 0 1.5rem calc(1rem + 100px) 1.5rem; 56 | transition: transform 250ms $timing-fn; 57 | transform: translateY(100%); 58 | // animation: cubic-bezier(0.4, 0, 0.2, 1) 0.2s slideUp-0 forwards; 59 | 60 | .active & { 61 | transform: translateY(0); 62 | // animation: cubic-bezier(0.4, 0, 0.2, 1) 0.25s slideUp-1 forwards; 63 | } 64 | 65 | &.active { 66 | transform: var(--translateY); 67 | } 68 | } 69 | 70 | &__handle { 71 | display: flex; 72 | flex-direction: column; 73 | align-items: center; 74 | padding-top: 0.5rem; 75 | padding-bottom: 1rem; 76 | 77 | > span { 78 | display: block; 79 | height: 2px; 80 | width: 28px; 81 | margin-bottom: 2px; 82 | background-color: rgba(0, 0, 0, 0.3); 83 | border-radius: 2px; 84 | } 85 | } 86 | 87 | &__item { 88 | $border-item: 1px solid rgba(0, 0, 0, 0.3); 89 | width: 100%; 90 | list-style: none; 91 | 92 | border-bottom: $border-item; 93 | 94 | 95 | &:first-child { 96 | margin-top: 40px; 97 | border-top: $border-item; 98 | } 99 | 100 | &.active { 101 | font-family: sans-serif; 102 | font-weight: 900; 103 | } 104 | } 105 | 106 | &__link { 107 | display: block; 108 | padding: 1rem 0; 109 | &:hover, &:visited { 110 | color: unset; 111 | text-decoration: none; 112 | } 113 | } 114 | 115 | &__list { 116 | margin: 0; 117 | padding: 0; 118 | 119 | display: flex; 120 | flex-direction: column; 121 | align-items: center; 122 | } 123 | 124 | &__tick { 125 | display: none; 126 | font-size: 24px; 127 | object-fit: contain; 128 | vertical-align: middle; 129 | 130 | &:before { 131 | color: #48af4a; 132 | } 133 | 134 | #{$component}__item.active & { 135 | display: block; 136 | } 137 | } 138 | 139 | // on larger screen 140 | @media screen and (min-width: 769px) { 141 | // display: none; 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/_keyframes.scss: -------------------------------------------------------------------------------- 1 | @keyframes ball1Move-0 { 2 | 0% { 3 | transform: translate(0, 0); 4 | } 5 | 62% { 6 | transform: translate(-6px, -3px); 7 | } 8 | 100% { 9 | transform: translate(0, 0); 10 | } 11 | } 12 | 13 | @keyframes ball2Move-0 { 14 | 0% { 15 | transform: translate(0, 0); 16 | } 17 | 62% { 18 | transform: translate(3px, 6px); 19 | } 20 | 100% { 21 | transform: translate(0, 0); 22 | } 23 | } 24 | 25 | @keyframes ballHover-0 { 26 | 0% { 27 | transform: scale(1) 28 | } 29 | 16% { 30 | transform: scale(1.016) 31 | } 32 | 100% { 33 | transform: scale(1) 34 | } 35 | } 36 | 37 | @keyframes fade { 38 | 0% { 39 | opacity: 0; 40 | } 41 | 42 | 100% { 43 | opacity: 1; 44 | } 45 | } 46 | 47 | @keyframes horizontal-move { 48 | 0% { 49 | transform: scale(2) translateX(0); 50 | opacity: 1; 51 | display: flex; 52 | } 53 | 100% { 54 | transform: scale(2) translateX(6vw); 55 | opacity: 0; 56 | display: none; 57 | } 58 | } 59 | 60 | @keyframes horizontal-move-2 { 61 | 0% { 62 | transform: translateX(-15vw); 63 | opacity: 0; 64 | } 65 | 66 | 100% { 67 | transform: translateX(0); 68 | opacity: 1; 69 | } 70 | } 71 | 72 | @keyframes rotate180-0 { 73 | from { 74 | transform: rotate(-180deg); 75 | } 76 | to { 77 | transform: rotate(0deg); 78 | } 79 | } 80 | 81 | @keyframes rotate180-1 { 82 | from { 83 | transform: rotate(0deg); 84 | } 85 | to { 86 | transform: rotate(-180deg); 87 | } 88 | } 89 | 90 | @keyframes rotate180-reverse-0 { 91 | from { 92 | transform: rotate(180deg); 93 | } 94 | to { 95 | transform: rotate(0deg); 96 | } 97 | } 98 | 99 | @keyframes rotate180-reverse-1 { 100 | from { 101 | transform: rotate(0deg); 102 | } 103 | to { 104 | transform: rotate(180deg); 105 | } 106 | } 107 | 108 | @keyframes slideDown-1 { 109 | from { 110 | opacity: 0; 111 | transform: translateY(-100%); 112 | } 113 | to { 114 | opacity: 1; 115 | transform: translateY(0%); 116 | } 117 | } 118 | 119 | @keyframes slideUp-0 { 120 | from { 121 | transform: translateY(0%); 122 | } 123 | to { 124 | transform: translateY(100%); 125 | } 126 | } 127 | 128 | @keyframes slideUp-1 { 129 | from { 130 | transform: translateY(100%); 131 | } 132 | to { 133 | transform: translateY(0%); 134 | } 135 | } 136 | 137 | @keyframes markChange-0 { 138 | 0% { 139 | background: unset; 140 | } 141 | 62% { 142 | background: #333; 143 | } 144 | 100% { 145 | background: unset; 146 | } 147 | } 148 | 149 | @keyframes markChange-1 { 150 | } 151 | 152 | @keyframes pop-countryselect-1 { 153 | from { 154 | transform: scale(1, .33) translate(0, 100%); 155 | } 156 | to { 157 | transform: scale(1, 1) translate(0, 0); 158 | } 159 | } 160 | 161 | @keyframes pop-countryselect-0 { 162 | from { 163 | transform: scale(1, 1) translate(0, 0); 164 | } 165 | to { 166 | transform: scale(1, .33) translate(0, 100%); 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /src/_reboot.scss: -------------------------------------------------------------------------------- 1 | *, 2 | *::before, 3 | *::after { 4 | box-sizing: border-box; 5 | } 6 | 7 | html { 8 | font-family: sans-serif; 9 | line-height: 1.15; 10 | -webkit-text-size-adjust: 100%; 11 | -ms-text-size-adjust: 100%; 12 | -ms-overflow-style: scrollbar; 13 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 14 | } 15 | 16 | @-ms-viewport { 17 | width: device-width; 18 | } 19 | article, aside, dialog, figcaption, figure, footer, header, hgroup, main, nav, section { 20 | display: block; 21 | } 22 | 23 | body { 24 | margin: 0; 25 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; 26 | font-size: 1rem; 27 | font-weight: 400; 28 | line-height: 1.5; 29 | color: #212529; 30 | text-align: left; 31 | background-color: #fff; 32 | } 33 | 34 | [tabindex="-1"]:focus { 35 | outline: none !important; 36 | } 37 | 38 | hr { 39 | box-sizing: content-box; 40 | height: 0; 41 | overflow: visible; 42 | } 43 | 44 | h1, h2, h3, h4, h5, h6 { 45 | margin-top: 0; 46 | margin-bottom: 0.5rem; 47 | } 48 | 49 | p { 50 | margin-top: 0; 51 | margin-bottom: 1rem; 52 | } 53 | 54 | abbr[title], 55 | abbr[data-original-title] { 56 | text-decoration: underline; 57 | text-decoration: underline dotted; 58 | cursor: help; 59 | border-bottom: 0; 60 | } 61 | 62 | address { 63 | margin-bottom: 1rem; 64 | font-style: normal; 65 | line-height: inherit; 66 | } 67 | 68 | ol, 69 | ul, 70 | dl { 71 | margin-top: 0; 72 | margin-bottom: 1rem; 73 | } 74 | 75 | ol ol, 76 | ul ul, 77 | ol ul, 78 | ul ol { 79 | margin-bottom: 0; 80 | } 81 | 82 | dt { 83 | font-weight: 700; 84 | } 85 | 86 | dd { 87 | margin-bottom: .5rem; 88 | margin-left: 0; 89 | } 90 | 91 | blockquote { 92 | margin: 0 0 1rem; 93 | } 94 | 95 | dfn { 96 | font-style: italic; 97 | } 98 | 99 | b, 100 | strong { 101 | font-weight: bolder; 102 | } 103 | 104 | small { 105 | font-size: 80%; 106 | } 107 | 108 | sub, 109 | sup { 110 | position: relative; 111 | font-size: 75%; 112 | line-height: 0; 113 | vertical-align: baseline; 114 | } 115 | 116 | sub { 117 | bottom: -.25em; 118 | } 119 | 120 | sup { 121 | top: -.5em; 122 | } 123 | 124 | a { 125 | color: theme-color("primary"); 126 | text-decoration: none; 127 | background-color: transparent; 128 | -webkit-text-decoration-skip: objects; 129 | } 130 | a:hover { 131 | color: #0056b3; 132 | text-decoration: underline; 133 | } 134 | 135 | a:not([href]):not([tabindex]) { 136 | color: inherit; 137 | text-decoration: none; 138 | } 139 | a:not([href]):not([tabindex]):hover, a:not([href]):not([tabindex]):focus { 140 | color: inherit; 141 | text-decoration: none; 142 | } 143 | a:not([href]):not([tabindex]):focus { 144 | outline: 0; 145 | } 146 | 147 | pre, 148 | code, 149 | kbd, 150 | samp { 151 | font-family: monospace, monospace; 152 | font-size: 1em; 153 | } 154 | 155 | pre { 156 | margin-top: 0; 157 | margin-bottom: 1rem; 158 | overflow: auto; 159 | -ms-overflow-style: scrollbar; 160 | } 161 | 162 | figure { 163 | margin: 0 0 1rem; 164 | } 165 | 166 | img { 167 | vertical-align: middle; 168 | border-style: none; 169 | } 170 | 171 | svg:not(:root) { 172 | overflow: hidden; 173 | } 174 | 175 | a, 176 | area, 177 | button, 178 | [role="button"], 179 | input:not([type="range"]), 180 | label, 181 | select, 182 | summary, 183 | textarea { 184 | touch-action: manipulation; 185 | } 186 | 187 | table { 188 | border-collapse: collapse; 189 | } 190 | 191 | caption { 192 | padding-top: 0.75rem; 193 | padding-bottom: 0.75rem; 194 | color: #868e96; 195 | text-align: left; 196 | caption-side: bottom; 197 | } 198 | 199 | th { 200 | text-align: inherit; 201 | } 202 | 203 | label { 204 | display: inline-block; 205 | margin-bottom: .5rem; 206 | } 207 | 208 | button { 209 | border-radius: 0; 210 | } 211 | 212 | button:focus { 213 | outline: 1px dotted; 214 | outline: 5px auto -webkit-focus-ring-color; 215 | } 216 | 217 | input, 218 | button, 219 | select, 220 | optgroup, 221 | textarea { 222 | margin: 0; 223 | font-family: inherit; 224 | font-size: inherit; 225 | line-height: inherit; 226 | } 227 | 228 | button, 229 | input { 230 | overflow: visible; 231 | } 232 | 233 | button, 234 | select { 235 | text-transform: none; 236 | } 237 | 238 | button, 239 | html [type="button"], 240 | [type="reset"], 241 | [type="submit"] { 242 | -webkit-appearance: button; 243 | } 244 | 245 | button::-moz-focus-inner, 246 | [type="button"]::-moz-focus-inner, 247 | [type="reset"]::-moz-focus-inner, 248 | [type="submit"]::-moz-focus-inner { 249 | padding: 0; 250 | border-style: none; 251 | } 252 | 253 | input[type="radio"], 254 | input[type="checkbox"] { 255 | box-sizing: border-box; 256 | padding: 0; 257 | } 258 | 259 | input[type="date"], 260 | input[type="time"], 261 | input[type="datetime-local"], 262 | input[type="month"] { 263 | -webkit-appearance: listbox; 264 | } 265 | 266 | textarea { 267 | overflow: auto; 268 | resize: vertical; 269 | } 270 | 271 | fieldset { 272 | min-width: 0; 273 | padding: 0; 274 | margin: 0; 275 | border: 0; 276 | } 277 | 278 | legend { 279 | display: block; 280 | width: 100%; 281 | max-width: 100%; 282 | padding: 0; 283 | margin-bottom: .5rem; 284 | font-size: 1.5rem; 285 | line-height: inherit; 286 | color: inherit; 287 | white-space: normal; 288 | } 289 | 290 | progress { 291 | vertical-align: baseline; 292 | } 293 | 294 | [type="number"]::-webkit-inner-spin-button, 295 | [type="number"]::-webkit-outer-spin-button { 296 | height: auto; 297 | } 298 | 299 | [type="search"] { 300 | outline-offset: -2px; 301 | -webkit-appearance: none; 302 | } 303 | 304 | [type="search"]::-webkit-search-cancel-button, 305 | [type="search"]::-webkit-search-decoration { 306 | -webkit-appearance: none; 307 | } 308 | 309 | ::-webkit-file-upload-button { 310 | font: inherit; 311 | -webkit-appearance: button; 312 | } 313 | 314 | output { 315 | display: inline-block; 316 | } 317 | 318 | summary { 319 | display: list-item; 320 | } 321 | 322 | template { 323 | display: none; 324 | } 325 | 326 | [hidden] { 327 | display: none !important; 328 | } -------------------------------------------------------------------------------- /style.scss: -------------------------------------------------------------------------------- 1 | @import "src/reboot"; 2 | @import "src/keyframes"; 3 | @import "src/bottom-sheet"; 4 | 5 | body { 6 | font-size: 20px; 7 | background-image: linear-gradient(#00aa13, #1ab32b, #009911); 8 | } 9 | 10 | .header { 11 | display: flex; 12 | flex-flow: column wrap; 13 | justify-content: center; 14 | align-items: center; 15 | height: 100vh; 16 | } -------------------------------------------------------------------------------- /yarn-error.log: -------------------------------------------------------------------------------- 1 | Arguments: 2 | /Users/windo/.nvm/versions/node/v10.15.2/bin/node /Users/windo/.nvm/versions/node/v10.15.2/bin/yarn add style.scss 3 | 4 | PATH: 5 | /Users/windo/.nvm/versions/node/v10.15.2/bin:/usr/local/Cellar/pyenv-virtualenv/1.1.3/shims:/Users/windo/.pyenv/shims:/Users/windo/.pyenv/bin:/Users/windo/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Users/windo/.nvm/versions/node/v10.15.2/bin:/usr/local/Cellar/pyenv-virtualenv/1.1.3/shims:/Users/windo/.pyenv/shims:/Users/windo/.pyenv/bin:/Users/windo/bin:/usr/local/mysql/bin:/Users/windo/Development/flutter/bin/:/usr/local/mysql/bin:/Users/windo/Development/flutter/bin/ 6 | 7 | Yarn version: 8 | 1.13.0 9 | 10 | Node version: 11 | 10.15.2 12 | 13 | Platform: 14 | darwin x64 15 | 16 | Trace: 17 | Error: https://registry.yarnpkg.com/style.scss: Not found 18 | at Request.params.callback [as _callback] (/Users/windo/.nvm/versions/node/v10.15.2/lib/node_modules/yarn/lib/cli.js:66255:18) 19 | at Request.self.callback (/Users/windo/.nvm/versions/node/v10.15.2/lib/node_modules/yarn/lib/cli.js:129397:22) 20 | at Request.emit (events.js:189:13) 21 | at Request. (/Users/windo/.nvm/versions/node/v10.15.2/lib/node_modules/yarn/lib/cli.js:130369:10) 22 | at Request.emit (events.js:189:13) 23 | at IncomingMessage. (/Users/windo/.nvm/versions/node/v10.15.2/lib/node_modules/yarn/lib/cli.js:130291:12) 24 | at Object.onceWrapper (events.js:277:13) 25 | at IncomingMessage.emit (events.js:194:15) 26 | at endReadableNT (_stream_readable.js:1103:12) 27 | at process._tickCallback (internal/process/next_tick.js:63:19) 28 | 29 | npm manifest: 30 | { 31 | "name": "bottom-sheet", 32 | "version": "0.0.1", 33 | "description": "Bottom sheet material like", 34 | "main": "main.js", 35 | "scripts": { 36 | "dev": "parcel watch index.html", 37 | "test": "echo \"Error: no test specified\" && exit 1" 38 | }, 39 | "keywords": ["material", "bottom-sheet"], 40 | "author": "Herwindo Artono", 41 | "license": "ISC" 42 | } 43 | 44 | yarn manifest: 45 | No manifest 46 | 47 | Lockfile: 48 | No lockfile 49 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | anymatch@^3.0.1: 6 | version "3.0.3" 7 | resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.0.3.tgz#2fb624fe0e84bccab00afee3d0006ed310f22f09" 8 | integrity sha512-c6IvoeBECQlMVuYUjSwimnhmztImpErfxJzWZhIQinIvQWoGOnB0dLIgifbPHQt5heS6mNlaZG16f06H3C8t1g== 9 | dependencies: 10 | normalize-path "^3.0.0" 11 | picomatch "^2.0.4" 12 | 13 | binary-extensions@^2.0.0: 14 | version "2.0.0" 15 | resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.0.0.tgz#23c0df14f6a88077f5f986c0d167ec03c3d5537c" 16 | integrity sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow== 17 | 18 | braces@^3.0.2: 19 | version "3.0.2" 20 | resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" 21 | integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== 22 | dependencies: 23 | fill-range "^7.0.1" 24 | 25 | "chokidar@>=2.0.0 <4.0.0": 26 | version "3.0.2" 27 | resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.0.2.tgz#0d1cd6d04eb2df0327446188cd13736a3367d681" 28 | integrity sha512-c4PR2egjNjI1um6bamCQ6bUNPDiyofNQruHvKgHQ4gDUP/ITSVSzNsiI5OWtHOsX323i5ha/kk4YmOZ1Ktg7KA== 29 | dependencies: 30 | anymatch "^3.0.1" 31 | braces "^3.0.2" 32 | glob-parent "^5.0.0" 33 | is-binary-path "^2.1.0" 34 | is-glob "^4.0.1" 35 | normalize-path "^3.0.0" 36 | readdirp "^3.1.1" 37 | optionalDependencies: 38 | fsevents "^2.0.6" 39 | 40 | fill-range@^7.0.1: 41 | version "7.0.1" 42 | resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" 43 | integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== 44 | dependencies: 45 | to-regex-range "^5.0.1" 46 | 47 | fsevents@^2.0.6: 48 | version "2.0.7" 49 | resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.0.7.tgz#382c9b443c6cbac4c57187cdda23aa3bf1ccfc2a" 50 | integrity sha512-a7YT0SV3RB+DjYcppwVDLtn13UQnmg0SWZS7ezZD0UjnLwXmy8Zm21GMVGLaFGimIqcvyMQaOJBrop8MyOp1kQ== 51 | 52 | glob-parent@^5.0.0: 53 | version "5.0.0" 54 | resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.0.0.tgz#1dc99f0f39b006d3e92c2c284068382f0c20e954" 55 | integrity sha512-Z2RwiujPRGluePM6j699ktJYxmPpJKCfpGA13jz2hmFZC7gKetzrWvg5KN3+OsIFmydGyZ1AVwERCq1w/ZZwRg== 56 | dependencies: 57 | is-glob "^4.0.1" 58 | 59 | is-binary-path@^2.1.0: 60 | version "2.1.0" 61 | resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" 62 | integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== 63 | dependencies: 64 | binary-extensions "^2.0.0" 65 | 66 | is-extglob@^2.1.1: 67 | version "2.1.1" 68 | resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" 69 | integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= 70 | 71 | is-glob@^4.0.1: 72 | version "4.0.1" 73 | resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" 74 | integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== 75 | dependencies: 76 | is-extglob "^2.1.1" 77 | 78 | is-number@^7.0.0: 79 | version "7.0.0" 80 | resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" 81 | integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== 82 | 83 | normalize-path@^3.0.0: 84 | version "3.0.0" 85 | resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" 86 | integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== 87 | 88 | picomatch@^2.0.4: 89 | version "2.0.7" 90 | resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.0.7.tgz#514169d8c7cd0bdbeecc8a2609e34a7163de69f6" 91 | integrity sha512-oLHIdio3tZ0qH76NybpeneBhYVj0QFTfXEFTc/B3zKQspYfYYkWYgFsmzo+4kvId/bQRcNkVeguI3y+CD22BtA== 92 | 93 | readdirp@^3.1.1: 94 | version "3.1.1" 95 | resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.1.1.tgz#b158123ac343c8b0f31d65680269cc0fc1025db1" 96 | integrity sha512-XXdSXZrQuvqoETj50+JAitxz1UPdt5dupjT6T5nVB+WvjMv2XKYj+s7hPeAVCXvmJrL36O4YYyWlIC3an2ePiQ== 97 | dependencies: 98 | picomatch "^2.0.4" 99 | 100 | sass@^1.22.7: 101 | version "1.22.9" 102 | resolved "https://registry.yarnpkg.com/sass/-/sass-1.22.9.tgz#41a2ed6038027f58be2bd5041293452a29c2cb84" 103 | integrity sha512-FzU1X2V8DlnqabrL4u7OBwD2vcOzNMongEJEx3xMEhWY/v26FFR3aG0hyeu2T965sfR0E9ufJwmG+Qjz78vFPQ== 104 | dependencies: 105 | chokidar ">=2.0.0 <4.0.0" 106 | 107 | to-regex-range@^5.0.1: 108 | version "5.0.1" 109 | resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" 110 | integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== 111 | dependencies: 112 | is-number "^7.0.0" 113 | --------------------------------------------------------------------------------