|
17 | 17 | import { afterUpdate, beforeUpdate, createEventDispatcher, onDestroy, onMount } from 'svelte'
|
18 | 18 | import { resizeObserver } from '../resize'
|
19 | 19 | import { closeTooltip, tooltipstore } from '../tooltips'
|
20 |
| - import type { FadeOptions, ScrollParams } from '../types' |
| 20 | + import type { FadeOptions, ScrollParams, MouseTargetEvent } from '../types' |
21 | 21 | import { defaultSP } from '../types'
|
22 | 22 | import { DelayedCaller } from '../utils'
|
23 | 23 | import IconDownOutline from './icons/DownOutline.svelte'
|
24 | 24 | import HalfUpDown from './icons/HalfUpDown.svelte'
|
25 | 25 | import IconUpOutline from './icons/UpOutline.svelte'
|
| 26 | + import IconNavPrev from './icons/NavPrev.svelte' |
26 | 27 |
|
27 | 28 | export let padding: string | undefined = undefined
|
28 | 29 | export let bottomPadding: string | undefined = undefined
|
|
45 | 46 | export let checkForHeaders: boolean = false
|
46 | 47 | export let stickedScrollBars: boolean = false
|
47 | 48 | export let thinScrollBars: boolean = false
|
| 49 | + export let showOverflowArrows: boolean = false |
48 | 50 | export let disableOverscroll = false
|
49 | 51 | export let disablePointerEventsOnScroll = false
|
50 | 52 | export let onScroll: ((params: ScrollParams) => void) | undefined = undefined
|
|
74 | 76 | let topCrop: 'top' | 'bottom' | 'full' | 'none' = 'none'
|
75 | 77 | let topCropValue: number = 0
|
76 | 78 | let maskH: 'left' | 'right' | 'both' | 'none' = 'none'
|
| 79 | + let scrollArrows: boolean[] = [false, false, false, false] // [up, right, down, left] |
77 | 80 |
|
78 | 81 | let divHScroll: HTMLElement
|
79 | 82 | let divBar: HTMLElement
|
|
243 | 246 | isScrollingByBar = direction
|
244 | 247 | }
|
245 | 248 |
|
246 |
| - const renderFade = () => { |
| 249 | + const renderFade = (): void => { |
| 250 | + if (showOverflowArrows) { |
| 251 | + scrollArrows = [ |
| 252 | + mask === 'top' || mask === 'both', |
| 253 | + maskH === 'left' || maskH === 'both', |
| 254 | + mask === 'bottom' || mask === 'both', |
| 255 | + maskH === 'right' || maskH === 'both' |
| 256 | + ] |
| 257 | + } |
247 | 258 | if (divScroll && !noFade) {
|
248 | 259 | const th = shiftTop + (topCrop === 'top' ? 2 * fz - topCropValue : 0)
|
249 | 260 | const tf =
|
|
537 | 548 | }
|
538 | 549 | }
|
539 | 550 |
|
| 551 | + const tapToScroll = (event: MouseTargetEvent): void => { |
| 552 | + const target = event.currentTarget as HTMLButtonElement |
| 553 | + if (!target.hasAttribute('data-direct') || divScroll == null) return |
| 554 | +
|
| 555 | + const direct = target.getAttribute('data-direct') |
| 556 | + const dir = |
| 557 | + direct === 'up' |
| 558 | + ? { top: -stepScroll } |
| 559 | + : direct === 'down' |
| 560 | + ? { top: stepScroll } |
| 561 | + : direct === 'left' |
| 562 | + ? { left: -stepScroll } |
| 563 | + : direct === 'right' |
| 564 | + ? { left: stepScroll } |
| 565 | + : { top: 0, left: 0 } |
| 566 | + if (!(dir?.top === 0 && dir?.left === 0)) divScroll.scrollBy({ ...dir, behavior: 'smooth' }) |
| 567 | + } |
| 568 | +
|
540 | 569 | $: topButton =
|
541 | 570 | (orientir === 'vertical' && (mask === 'top' || mask === 'both')) ||
|
542 | 571 | (orientir === 'horizontal' && (maskH === 'right' || maskH === 'both'))
|
|
715 | 744 | on:pointerleave={checkFade}
|
716 | 745 | />
|
717 | 746 | {/if}
|
| 747 | + {#if showOverflowArrows} |
| 748 | + {#each ['up', 'right', 'down', 'left'] as dir, i} |
| 749 | + <button class="scrollArrow" data-direct={dir} class:shown={scrollArrows[i]} on:click={tapToScroll}> |
| 750 | + <IconNavPrev size={'full'} /> |
| 751 | + </button> |
| 752 | + {/each} |
| 753 | + {/if} |
718 | 754 | </div>
|
719 | 755 |
|
720 | 756 | <style lang="scss">
|
| 757 | + .scrollArrow { |
| 758 | + position: absolute; |
| 759 | + display: none; |
| 760 | + justify-content: center; |
| 761 | + align-items: center; |
| 762 | + margin: 0; |
| 763 | + padding: 0; |
| 764 | + width: 1rem; |
| 765 | + height: 2rem; |
| 766 | + color: var(--theme-halfcontent-color); |
| 767 | + background-color: var(--theme-popup-color); |
| 768 | + border: 1px solid var(--theme-button-border); |
| 769 | + border-radius: 0.25rem; |
| 770 | + outline: none; |
| 771 | + box-shadow: 0 0 0.375rem rgba($color: #000000, $alpha: 0.1); |
| 772 | + user-select: none; |
| 773 | + -webkit-tap-highlight-color: transparent; |
| 774 | + tap-highlight-color: transparent; |
| 775 | +
|
| 776 | + &.shown { |
| 777 | + display: flex; |
| 778 | + } |
| 779 | + &:hover, |
| 780 | + &:focus { |
| 781 | + background-color: var(--theme-popup-hover); |
| 782 | + color: var(--theme-content-color); |
| 783 | + } |
| 784 | + :global(> svg) { |
| 785 | + width: 0.5rem; |
| 786 | + height: 0.75rem; |
| 787 | + pointer-events: none; |
| 788 | + } |
| 789 | + } |
| 790 | + .scrollArrow[data-direct='up'] { |
| 791 | + top: var(--scroller-header-height, 0); |
| 792 | + left: calc( |
| 793 | + (100% - var(--scroller-right-offset, 0) - var(--scroller-left-offset, 0)) / 2 + var(--scroller-left-offset, 0) |
| 794 | + ); |
| 795 | + transform: translateX(-50%) rotate(90deg); |
| 796 | + } |
| 797 | + .scrollArrow[data-direct='right'] { |
| 798 | + top: calc( |
| 799 | + (100% - var(--scroller-header-height, 0) - var(--scroller-footer-height, 0)) / 2 + |
| 800 | + var(--scroller-header-height, 0) |
| 801 | + ); |
| 802 | + right: calc(var(--scroller-right-offset, 0) + 0.25rem); |
| 803 | + transform: translateY(-50%) rotate(180deg); |
| 804 | + } |
| 805 | + .scrollArrow[data-direct='down'] { |
| 806 | + bottom: var(--scroller-footer-height, 0); |
| 807 | + left: calc( |
| 808 | + (100% - var(--scroller-right-offset, 0) - var(--scroller-left-offset, 0)) / 2 + var(--scroller-left-offset, 0) |
| 809 | + ); |
| 810 | + transform: translateX(-50%) rotate(-90deg); |
| 811 | + } |
| 812 | + .scrollArrow[data-direct='left'] { |
| 813 | + top: calc( |
| 814 | + (100% - var(--scroller-header-height, 0) - var(--scroller-footer-height, 0)) / 2 + |
| 815 | + var(--scroller-header-height, 0) |
| 816 | + ); |
| 817 | + left: calc(var(--scroller-left-offset, 0) + 0.25rem); |
| 818 | + transform: translateY(-50%); |
| 819 | + } |
721 | 820 | .updown-container {
|
722 | 821 | position: absolute;
|
723 | 822 | display: flex;
|
|
0 commit comments