UNPKG

svelte-scrollable-card

Version:
421 lines (353 loc) 11 kB
<script> import {fly} from 'svelte/transition'; import ScrollItem from './ScrollItem.svelte'; import {onDestroy, onMount} from 'svelte'; let left = 0; let list; onMount(() => { left = list.scrollLeft; window.addEventListener('resize', () => { if (left !== list.scrollLeft) { left = list.scrollLeft; } }); }); const myInterval = setInterval(() => { if (left !== list.scrollLeft) { left = list.scrollLeft; } }, 500); onDestroy(() => { clearInterval(myInterval); }); function moveLeft() { list.scrollTo({ left: list.scrollLeft - window.innerWidth - 21, behavior: 'smooth', }); left = list.scrollLeft; } function moveRight() { list.scrollTo({ left: list.scrollLeft + window.innerWidth - 21, behavior: 'smooth', }); left = list.scrollLeft; } $: leftEnable = list ? left !== 0 : false; $: leftRight = list ? left < (list.scrollWidth - list.clientWidth) : false; </script> <style> .horizontal-list-container { display: flex; align-content: center; width: calc(100vw - 20px); margin-bottom: -5px; animation-name: fadeIn; animation-duration: 0.2s; animation-fill-mode: both; } .horizontal-list-container .skip-button { position: absolute; align-self: center; z-index: 99999; margin-top: 10px !important; } @media (max-width: 600px) { .horizontal-list-container .skip-button { display: none; } } .horizontal-list-container .skip-button.start { left: 0; } .horizontal-list-container .skip-button.end { right: 0; } .horizontal-list-container .items { overflow: hidden; transition-duration: 0.15s; transition-timing-function: cubic-bezier(0.4, 0, 1, 1); will-change: transform; display: inline-block; white-space: nowrap; padding: 14px 14px 0; } @media (max-width: 600px) { .horizontal-list-container .items { overflow: auto; } } @keyframes fadeIn { 0% { opacity: 0; transform: translateY(100%); } to { opacity: 1; transform: translateY(0); } } .mdc-fab { box-shadow: 0px 3px 5px -1px rgba(0, 0, 0, 0.2), 0px 6px 10px 0px rgba(0, 0, 0, 0.14), 0px 1px 18px 0px rgba(0, 0, 0, 0.12); display: inline-flex; position: relative; align-items: center; justify-content: center; box-sizing: border-box; width: 56px; height: 56px; padding: 0; border: none; fill: currentColor; cursor: pointer; user-select: none; -moz-appearance: none; -webkit-appearance: none; overflow: hidden; transition: box-shadow 280ms cubic-bezier(0.4, 0, 0.2, 1), opacity 15ms linear 30ms, transform 270ms 0ms cubic-bezier(0, 0, 0.2, 1); background-color: #018786; color: #fff; /* @alternate */ color: var(--mdc-theme-on-secondary, #fff); } .mdc-fab:not(.mdc-fab--extended) { border-radius: 50%; } .mdc-fab::-moz-focus-inner { padding: 0; border: 0; } .mdc-fab:hover, .mdc-fab:focus { box-shadow: 0px 5px 5px -3px rgba(0, 0, 0, 0.2), 0px 8px 10px 1px rgba(0, 0, 0, 0.14), 0px 3px 14px 2px rgba(0, 0, 0, 0.12); } .mdc-fab:active { box-shadow: 0px 7px 8px -4px rgba(0, 0, 0, 0.2), 0px 12px 17px 2px rgba(0, 0, 0, 0.14), 0px 5px 22px 4px rgba(0, 0, 0, 0.12); } .mdc-fab:active, .mdc-fab:focus { outline: none; } .mdc-fab:hover { cursor: pointer; } .mdc-fab > svg { width: 100%; } @supports not (-ms-ime-align: auto) { .mdc-fab { /* @alternate */ background-color: var(--mdc-theme-secondary, #018786); } } .mdc-fab .mdc-fab__icon { width: 24px; height: 24px; font-size: 24px; } .mdc-fab--mini { width: 40px; height: 40px; } .mdc-fab--extended { font-family: Roboto, sans-serif; -moz-osx-font-smoothing: grayscale; -webkit-font-smoothing: antialiased; font-size: 0.875rem; line-height: 2.25rem; font-weight: 500; letter-spacing: 0.08929em; text-decoration: none; text-transform: uppercase; border-radius: 24px; padding: 0 20px; width: auto; max-width: 100%; height: 48px; } .mdc-fab--extended .mdc-fab__icon { /* @noflip */ margin-left: -8px; /* @noflip */ margin-right: 12px; } [dir="rtl"] .mdc-fab--extended .mdc-fab__icon, .mdc-fab--extended .mdc-fab__icon[dir="rtl"] { /* @noflip */ margin-left: 12px; /* @noflip */ margin-right: -8px; } .mdc-fab--extended .mdc-fab__label + .mdc-fab__icon { /* @noflip */ margin-left: 12px; /* @noflip */ margin-right: -8px; } [dir="rtl"] .mdc-fab--extended .mdc-fab__label + .mdc-fab__icon, .mdc-fab--extended .mdc-fab__label + .mdc-fab__icon[dir="rtl"] { /* @noflip */ margin-left: -8px; /* @noflip */ margin-right: 12px; } .mdc-fab__label { justify-content: flex-start; text-overflow: ellipsis; white-space: nowrap; overflow: hidden; } .mdc-fab__icon { transition: transform 180ms 90ms cubic-bezier(0, 0, 0.2, 1); fill: currentColor; will-change: transform; } .mdc-fab .mdc-fab__icon { display: inline-flex; align-items: center; justify-content: center; } .mdc-fab--exited { transform: scale(0); opacity: 0; transition: opacity 15ms linear 150ms, transform 180ms 0ms cubic-bezier(0.4, 0, 1, 1); } .mdc-fab--exited .mdc-fab__icon { transform: scale(0); transition: transform 135ms 0ms cubic-bezier(0.4, 0, 1, 1); } @keyframes mdc-ripple-fg-radius-in { from { animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1); transform: translate(var(--mdc-ripple-fg-translate-start, 0)) scale(1); } to { transform: translate(var(--mdc-ripple-fg-translate-end, 0)) scale(var(--mdc-ripple-fg-scale, 1)); } } @keyframes mdc-ripple-fg-opacity-in { from { animation-timing-function: linear; opacity: 0; } to { opacity: var(--mdc-ripple-fg-opacity, 0); } } @keyframes mdc-ripple-fg-opacity-out { from { animation-timing-function: linear; opacity: var(--mdc-ripple-fg-opacity, 0); } to { opacity: 0; } } .mdc-ripple-surface--test-edge-var-bug { --mdc-ripple-surface-test-edge-var: 1px solid #000; visibility: hidden; } .mdc-ripple-surface--test-edge-var-bug::before { border: var(--mdc-ripple-surface-test-edge-var); } .mdc-fab { --mdc-ripple-fg-size: 0; --mdc-ripple-left: 0; --mdc-ripple-top: 0; --mdc-ripple-fg-scale: 1; --mdc-ripple-fg-translate-end: 0; --mdc-ripple-fg-translate-start: 0; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } .mdc-fab::before, .mdc-fab::after { position: absolute; border-radius: 50%; opacity: 0; pointer-events: none; content: ""; } .mdc-fab::before { transition: opacity 15ms linear, background-color 15ms linear; z-index: 1; } .mdc-fab.mdc-ripple-upgraded::before { transform: scale(var(--mdc-ripple-fg-scale, 1)); } .mdc-fab.mdc-ripple-upgraded::after { top: 0; /* @noflip */ left: 0; transform: scale(0); transform-origin: center center; } .mdc-fab.mdc-ripple-upgraded--unbounded::after { top: var(--mdc-ripple-top, 0); /* @noflip */ left: var(--mdc-ripple-left, 0); } .mdc-fab.mdc-ripple-upgraded--foreground-activation::after { animation: mdc-ripple-fg-radius-in 225ms forwards, mdc-ripple-fg-opacity-in 75ms forwards; } .mdc-fab.mdc-ripple-upgraded--foreground-deactivation::after { animation: mdc-ripple-fg-opacity-out 150ms; transform: translate(var(--mdc-ripple-fg-translate-end, 0)) scale(var(--mdc-ripple-fg-scale, 1)); } .mdc-fab::before, .mdc-fab::after { top: calc(50% - 100%); /* @noflip */ left: calc(50% - 100%); width: 200%; height: 200%; } .mdc-fab.mdc-ripple-upgraded::after { width: var(--mdc-ripple-fg-size, 100%); height: var(--mdc-ripple-fg-size, 100%); } .mdc-fab::before, .mdc-fab::after { background-color: #fff; } @supports not (-ms-ime-align: auto) { .mdc-fab::before, .mdc-fab::after { /* @alternate */ background-color: var(--mdc-theme-on-secondary, #fff); } } .mdc-fab:hover::before { opacity: 0.08; } .mdc-fab:not(.mdc-ripple-upgraded):focus::before, .mdc-fab.mdc-ripple-upgraded--background-focused::before { transition-duration: 75ms; opacity: 0.24; } .mdc-fab:not(.mdc-ripple-upgraded)::after { transition: opacity 150ms linear; } .mdc-fab:not(.mdc-ripple-upgraded):active::after { transition-duration: 75ms; opacity: 0.24; } .mdc-fab.mdc-ripple-upgraded { --mdc-ripple-fg-opacity: 0.24; } </style> <div class="horizontal-list-container"> {#if leftEnable} <div class="mdc-fab mdc-fab--mini skip-button start" on:click="{moveLeft}" transition:fly="{{ x: -100, duration: 200 }}" role="button"> <svg class="mdc-fab__icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"> <path d="M15.41 16.59L10.83 12l4.58-4.59L14 6l-6 6 6 6 1.41-1.41z"/> <path fill="none" d="M0 0h24v24H0V0z"/> </svg> </div> {/if} <div class="items" bind:this={list}> <slot></slot> </div> {#if leftRight} <div class="mdc-fab mdc-fab--mini skip-button end" on:click="{moveRight}" transition:fly="{{ x: 100, duration: 200 }}" role="button"> <svg class="mdc-fab__icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"> <path d="M8.59 16.59L13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.41z"/> <path fill="none" d="M0 0h24v24H0V0z"/> </svg> </div> {/if} </div> <svelte:options tag="sv-scrollable"/>