mirror of
https://github.com/hcengineering/platform.git
synced 2026-09-11 04:07:43 +02:00
Added hyperlink type, ScrollerBar (#2353)
Signed-off-by: Alexander Platov <sas_lord@mail.ru>
This commit is contained in:
@@ -17,8 +17,8 @@
|
||||
import { Ref, SortingOrder } from '@hcengineering/core'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import task, { SpaceWithStates, State } from '@hcengineering/task'
|
||||
import { getPlatformColor } from '@hcengineering/ui'
|
||||
import { createEventDispatcher, onDestroy, onMount } from 'svelte'
|
||||
import { getPlatformColor, ScrollerBar } from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import StatesBarElement from './StatesBarElement.svelte'
|
||||
import type { StatesBarPosition } from '../..'
|
||||
|
||||
@@ -27,18 +27,7 @@
|
||||
export let gap: 'none' | 'small' | 'big' = 'small'
|
||||
|
||||
let states: State[] = []
|
||||
|
||||
let divScroll: HTMLElement
|
||||
let divBar: HTMLElement
|
||||
let isScrolling: boolean = false
|
||||
let dX: number
|
||||
let timer: number
|
||||
|
||||
let maskLeft: boolean = false
|
||||
let maskRight: boolean = false
|
||||
let mask: 'left' | 'right' | 'both' | 'none' = 'none'
|
||||
let stepStyle: string
|
||||
$: stepStyle = gap === 'small' ? 'gap-1' : gap === 'big' ? 'gap-2' : ''
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
@@ -56,77 +45,6 @@
|
||||
}
|
||||
)
|
||||
|
||||
const checkBar = (): void => {
|
||||
if (divBar && divScroll) {
|
||||
const trackW = divScroll.clientWidth
|
||||
const scrollW = divScroll.scrollWidth
|
||||
const proc = scrollW / trackW
|
||||
divBar.style.width = divScroll.clientWidth / proc + 'px'
|
||||
divBar.style.left = divScroll.scrollLeft / proc + 'px'
|
||||
if (mask === 'none') divBar.style.visibility = 'hidden'
|
||||
else {
|
||||
divBar.style.visibility = 'visible'
|
||||
if (divBar) {
|
||||
if (timer) {
|
||||
clearTimeout(timer)
|
||||
divBar.style.opacity = '1'
|
||||
}
|
||||
timer = setTimeout(() => {
|
||||
if (divBar) divBar.style.opacity = '0'
|
||||
}, 2000)
|
||||
}
|
||||
}
|
||||
if (divScroll.clientWidth >= divScroll.scrollWidth) divBar.style.visibility = 'hidden'
|
||||
}
|
||||
}
|
||||
|
||||
const onScroll = (event: MouseEvent): void => {
|
||||
if (isScrolling && divBar && divScroll) {
|
||||
const rectScroll = divScroll.getBoundingClientRect()
|
||||
let X = event.clientX - dX
|
||||
if (X < rectScroll.left) X = rectScroll.left
|
||||
if (X > rectScroll.right - divBar.clientWidth) X = rectScroll.right - divBar.clientWidth
|
||||
divBar.style.left = X - rectScroll.x + 'px'
|
||||
const leftBar = X - rectScroll.x
|
||||
const widthScroll = rectScroll.width - divBar.clientWidth
|
||||
const procBar = leftBar / widthScroll
|
||||
divScroll.scrollLeft = (divScroll.scrollWidth - divScroll.clientWidth) * procBar
|
||||
}
|
||||
}
|
||||
const onScrollEnd = (event: MouseEvent): void => {
|
||||
const el: HTMLElement = event.currentTarget as HTMLElement
|
||||
if (el && isScrolling) {
|
||||
document.removeEventListener('mousemove', onScroll)
|
||||
document.body.style.userSelect = 'auto'
|
||||
document.body.style.webkitUserSelect = 'auto'
|
||||
}
|
||||
document.removeEventListener('mouseup', onScrollEnd)
|
||||
isScrolling = false
|
||||
}
|
||||
const onScrollStart = (event: MouseEvent): void => {
|
||||
const el: HTMLElement = event.currentTarget as HTMLElement
|
||||
if (el && divScroll) {
|
||||
dX = event.clientX - el.getBoundingClientRect().x
|
||||
document.addEventListener('mouseup', onScrollEnd)
|
||||
document.addEventListener('mousemove', onScroll)
|
||||
document.body.style.userSelect = 'none'
|
||||
document.body.style.webkitUserSelect = 'none'
|
||||
isScrolling = true
|
||||
}
|
||||
}
|
||||
|
||||
const checkMask = (): void => {
|
||||
maskLeft = !!(divScroll && divScroll.scrollLeft > 1)
|
||||
maskRight = !!(divScroll && divScroll.scrollWidth - divScroll.scrollLeft - divScroll.clientWidth > 1)
|
||||
if (maskLeft || maskRight) {
|
||||
if (maskLeft && maskRight) mask = 'both'
|
||||
else if (maskLeft) mask = 'left'
|
||||
else if (maskRight) mask = 'right'
|
||||
} else mask = 'none'
|
||||
|
||||
if (!isScrolling) checkBar()
|
||||
}
|
||||
|
||||
const selectItem = (ev: Event, item: State): void => {
|
||||
const el: HTMLElement = ev.currentTarget as HTMLElement
|
||||
const rect = el.getBoundingClientRect()
|
||||
@@ -149,100 +67,18 @@
|
||||
else if (n === states.length - 1) return 'end'
|
||||
else return 'middle'
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
if (divScroll) {
|
||||
const observer = new IntersectionObserver(() => checkMask(), { root: null, threshold: 0.1 })
|
||||
const tempEl = divScroll.querySelector('*') as HTMLElement
|
||||
if (tempEl) observer.observe(tempEl)
|
||||
checkMask()
|
||||
divScroll.addEventListener('scroll', checkMask)
|
||||
}
|
||||
})
|
||||
onDestroy(() => {
|
||||
if (divScroll) divScroll.removeEventListener('scroll', checkMask)
|
||||
})
|
||||
const _resize = (): void => checkMask()
|
||||
</script>
|
||||
|
||||
<svelte:window on:resize={_resize} />
|
||||
<div class="statesbar-container">
|
||||
<div bind:this={divScroll} class="antiStatesBar mask-{mask} {stepStyle}">
|
||||
{#each states as item, i (item._id)}
|
||||
<StatesBarElement
|
||||
label={item.title}
|
||||
position={getPosition(i)}
|
||||
selected={item._id === state}
|
||||
color={getPlatformColor(item.color)}
|
||||
on:click={(ev) => {
|
||||
if (item._id !== state) selectItem(ev, item)
|
||||
}}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
<div
|
||||
class="bar"
|
||||
class:hovered={isScrolling}
|
||||
bind:this={divBar}
|
||||
on:mousedown={onScrollStart}
|
||||
on:mouseleave={checkMask}
|
||||
/>
|
||||
<div class="track" class:hovered={isScrolling} />
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.statesbar-container {
|
||||
position: relative;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.track {
|
||||
visibility: hidden;
|
||||
position: absolute;
|
||||
bottom: -8px;
|
||||
left: 0;
|
||||
height: 5px;
|
||||
transform-origin: center;
|
||||
transform: scaleX(0);
|
||||
transition: all 0.1s ease-in-out;
|
||||
background-color: var(--scrollbar-track-color);
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
|
||||
.bar {
|
||||
visibility: hidden;
|
||||
position: absolute;
|
||||
bottom: -8px;
|
||||
left: 0;
|
||||
height: 5px;
|
||||
min-width: 2rem;
|
||||
max-width: calc(100% - 12px);
|
||||
transform-origin: center;
|
||||
transform: scaleY(0.5);
|
||||
background-color: var(--scrollbar-bar-color);
|
||||
border-radius: 0.125rem;
|
||||
opacity: 0;
|
||||
box-shadow: 0 0 1px 1px var(--board-bg-color);
|
||||
cursor: pointer;
|
||||
z-index: 1;
|
||||
transition: all 0.15s;
|
||||
|
||||
&:hover,
|
||||
&.hovered {
|
||||
background-color: var(--scrollbar-bar-hover);
|
||||
transform: scaleY(1);
|
||||
border-radius: 0.25rem;
|
||||
opacity: 1 !important;
|
||||
box-shadow: 0 0 1px black;
|
||||
|
||||
& + .track {
|
||||
visibility: visible;
|
||||
left: 0;
|
||||
transform: scaleY(1);
|
||||
}
|
||||
}
|
||||
&.hovered {
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<ScrollerBar {gap} bind:scroller={divScroll}>
|
||||
{#each states as item, i (item._id)}
|
||||
<StatesBarElement
|
||||
label={item.title}
|
||||
position={getPosition(i)}
|
||||
selected={item._id === state}
|
||||
color={getPlatformColor(item.color)}
|
||||
on:click={(ev) => {
|
||||
if (item._id !== state) selectItem(ev, item)
|
||||
}}
|
||||
/>
|
||||
{/each}
|
||||
</ScrollerBar>
|
||||
|
||||
Reference in New Issue
Block a user