mirror of
https://github.com/hcengineering/platform.git
synced 2025-05-28 19:08:01 +00:00
Update Scroller: detect Table. Replace ScrollBox -> Scroller. Replace icon. (#1047)
Signed-off-by: Alexander Platov <sas_lord@mail.ru>
This commit is contained in:
parent
5a74bda519
commit
7a8f581741
@ -25,51 +25,116 @@
|
||||
let divBack: HTMLElement
|
||||
let divBar: HTMLElement
|
||||
let divTrack: HTMLElement
|
||||
let divEl: HTMLElement
|
||||
let isBack: boolean = false
|
||||
let divTHead: HTMLElement
|
||||
let elTHead: Element
|
||||
let isBack: boolean = false // ?
|
||||
let isTHead: boolean = false
|
||||
let hasTHeads: boolean = false // ?
|
||||
let isScrolling: boolean = false
|
||||
let enabledChecking: boolean = false
|
||||
let dY: number
|
||||
let visibleEl: number | undefined = undefined
|
||||
|
||||
const checkBack = (): void => {
|
||||
const rectScroll = divScroll.getBoundingClientRect()
|
||||
const el = divBox.querySelector('.scroller-back')
|
||||
if (el && divScroll) {
|
||||
const rectEl = el.getBoundingClientRect()
|
||||
const bottom = document.body.clientHeight - rectScroll.bottom
|
||||
let top = rectEl.top
|
||||
if (top < rectScroll.top) top = rectScroll.top
|
||||
if (top > rectScroll.bottom) top = rectScroll.top + rectScroll.height
|
||||
divBack.style.left = rectScroll.left + 'px'
|
||||
divBack.style.right = document.body.clientWidth - rectScroll.right + 'px'
|
||||
divBack.style.top = top + 'px'
|
||||
divBack.style.bottom = bottom + 'px'
|
||||
divBack.style.height = 'auto'
|
||||
divBack.style.width = 'auto'
|
||||
divBack.style.visibility = 'visible'
|
||||
isBack = true
|
||||
} else {
|
||||
divBack.style.visibility = 'hidden'
|
||||
isBack = false
|
||||
if (divBox) {
|
||||
const el = divBox.querySelector('.scroller-back')
|
||||
if (el && divScroll) {
|
||||
const rectScroll = divScroll.getBoundingClientRect()
|
||||
const rectEl = el.getBoundingClientRect()
|
||||
const bottom = document.body.clientHeight - rectScroll.bottom
|
||||
let top = rectEl.top
|
||||
if (top < rectScroll.top) top = rectScroll.top
|
||||
if (top > rectScroll.bottom) top = rectScroll.top + rectScroll.height
|
||||
divBack.style.left = rectScroll.left + 'px'
|
||||
divBack.style.right = document.body.clientWidth - rectScroll.right + 'px'
|
||||
divBack.style.top = top + 'px'
|
||||
divBack.style.bottom = bottom + 'px'
|
||||
divBack.style.height = 'auto'
|
||||
divBack.style.width = 'auto'
|
||||
divBack.style.visibility = 'visible'
|
||||
isBack = true
|
||||
} else {
|
||||
divBack.style.visibility = 'hidden'
|
||||
isBack = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let observer = new IntersectionObserver(changes => {
|
||||
for (const change of changes) {
|
||||
if (divBack) {
|
||||
let rect = change.intersectionRect
|
||||
if (rect) {
|
||||
divBack.style.left = rect.left + 'px'
|
||||
divBack.style.right = document.body.clientWidth - rect.right + 'px'
|
||||
divBack.style.top = rect.top + 'px'
|
||||
divBack.style.bottom = document.body.clientHeight - rect.bottom + 'px'
|
||||
if (change.target) {
|
||||
const temp: HTMLElement = change.target as HTMLElement
|
||||
divBack.style.backgroundColor = temp.style.backgroundColor
|
||||
}
|
||||
} else divBack.style.visibility = 'hidden'
|
||||
}
|
||||
const checkTHeadSizes = (): void => {
|
||||
if (elTHead && divTHead && divScroll) {
|
||||
const elements = divTHead.querySelectorAll('div')
|
||||
elements.forEach((el, i) => {
|
||||
const th = elTHead.children.item(i)
|
||||
if (th) el.style.width = th.clientWidth + 'px'
|
||||
})
|
||||
}
|
||||
}, { root: null, threshold: .1 })
|
||||
}
|
||||
|
||||
const clearTHead = (): void => {
|
||||
visibleEl = undefined
|
||||
divTHead.innerHTML = ''
|
||||
divTHead.style.visibility = 'hidden'
|
||||
divTHead.style.opacity = '0'
|
||||
isTHead = false
|
||||
}
|
||||
|
||||
const fillTHead = (el: Element): boolean => {
|
||||
const tr: Element | null = el.children.item(0)
|
||||
if (tr) {
|
||||
for (let i = 0; i < tr.children.length; i++) {
|
||||
const th = tr.children.item(i)
|
||||
if (th) {
|
||||
let newStyle = `flex-shrink: 0; width: ${th.clientWidth}px; `
|
||||
if ((i === 0 && !enabledChecking) || (i === 1 && enabledChecking)) newStyle += `padding-right: 1.5rem;`
|
||||
else if (i === tr.children.length - 1) newStyle += `padding-left: 1.5rem;`
|
||||
else if (i === 0 && enabledChecking) newStyle += `padding: 0 .75rem;`
|
||||
else newStyle += `padding: 0 1.5rem;`
|
||||
if (th.classList.contains('sorted')) newStyle += ` margin-right: .25rem;`
|
||||
divTHead.insertAdjacentHTML('beforeend', `<div style="${newStyle}">${tr.children.item(i)?.textContent}</div>`)
|
||||
}
|
||||
}
|
||||
isTHead = true
|
||||
elTHead = tr
|
||||
}
|
||||
return isTHead
|
||||
}
|
||||
|
||||
const findTHeaders = (): void => {
|
||||
if (divBox) {
|
||||
const elements = divBox.querySelectorAll('.scroller-thead')
|
||||
if (elements.length > 0 && divScroll) {
|
||||
const rectScroll = divScroll.getBoundingClientRect()
|
||||
hasTHeads = true
|
||||
elements.forEach((el, i) => {
|
||||
const rect = el.getBoundingClientRect()
|
||||
enabledChecking = el.parentElement?.classList.contains('enableChecking') ?? false
|
||||
const rectTable = el.parentElement?.getBoundingClientRect()
|
||||
if (rectTable) {
|
||||
if (rectTable.top < rectScroll.top && rectTable.bottom > rectScroll.top + rect.height) {
|
||||
if (!isTHead && divTHead)
|
||||
if (fillTHead(el)) visibleEl = i
|
||||
if (isTHead) {
|
||||
if (rect.width > rectScroll.width) divTHead.style.width = rectScroll.width + 'px'
|
||||
else divTHead.style.width = rect.width + 'px'
|
||||
divTHead.style.height = rect.height + 'px'
|
||||
divTHead.style.left = rect.left + 'px'
|
||||
if (rect.bottom - 16 < rectScroll.top && rectTable.bottom > rectScroll.top + rect.height) {
|
||||
divTHead.style.top = rectScroll.top + 'px'
|
||||
divTHead.style.visibility = 'visible'
|
||||
divTHead.style.opacity = '.9'
|
||||
} else {
|
||||
divTHead.style.top = rect.top + 'px'
|
||||
divTHead.style.visibility = 'hidden'
|
||||
divTHead.style.opacity = '0'
|
||||
}
|
||||
}
|
||||
} else if ((rectTable.top > rectScroll.top + rect.height || rectTable.bottom < rectScroll.top + rect.height) && isTHead && visibleEl === i)
|
||||
clearTHead()
|
||||
}
|
||||
})
|
||||
} else hasTHeads = false
|
||||
}
|
||||
}
|
||||
|
||||
const checkBar = (): void => {
|
||||
if (divBar && divScroll) {
|
||||
@ -119,33 +184,42 @@
|
||||
}
|
||||
|
||||
const checkFade = (): void => {
|
||||
const t = divScroll.scrollTop
|
||||
const b = divScroll.scrollHeight - divScroll.clientHeight - t
|
||||
if (t > 0 && b > 0) mask = 'both'
|
||||
else if (t > 0) mask = 'bottom'
|
||||
else if (b > 0) mask = 'top'
|
||||
else mask = 'none'
|
||||
if (divScroll) {
|
||||
const t = divScroll.scrollTop
|
||||
const b = divScroll.scrollHeight - divScroll.clientHeight - t
|
||||
if (t > 0 && b > 0) mask = 'both'
|
||||
else if (t > 0) mask = 'bottom'
|
||||
else if (b > 0) mask = 'top'
|
||||
else mask = 'none'
|
||||
}
|
||||
checkBack()
|
||||
findTHeaders()
|
||||
if (isTHead) checkTHeadSizes()
|
||||
if (!isScrolling) checkBar()
|
||||
}
|
||||
|
||||
let observer = new IntersectionObserver(() => checkFade(), { root: null, threshold: .1 })
|
||||
|
||||
onMount(() => {
|
||||
if (divScroll && divBox) {
|
||||
divScroll.addEventListener('scroll', checkFade)
|
||||
const tempEl = divBox.querySelector('*') as HTMLElement
|
||||
observer.observe(tempEl)
|
||||
if (divBox.querySelector('.scroller-back')) {
|
||||
divEl = divBox.querySelector('.scroller-back') as HTMLElement
|
||||
observer.observe(divEl)
|
||||
}
|
||||
if (tempEl) observer.observe(tempEl)
|
||||
checkFade()
|
||||
}
|
||||
if (divBack) checkBack()
|
||||
})
|
||||
onDestroy(() => { if (divScroll) divScroll.removeEventListener('scroll', checkFade) })
|
||||
afterUpdate(() => { if (divScroll) checkFade() })
|
||||
afterUpdate(() => {
|
||||
if (divScroll && divBox) {
|
||||
const tempEl = divBox.querySelector('*') as HTMLElement
|
||||
if (tempEl) observer.observe(tempEl)
|
||||
checkFade()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<svelte:window on:resize={checkFade} />
|
||||
<svelte:window on:resize={checkFade} />
|
||||
<div class="scroller-container">
|
||||
<div
|
||||
bind:this={divScroll}
|
||||
@ -171,6 +245,7 @@
|
||||
class:hovered={isScrolling}
|
||||
bind:this={divTrack}
|
||||
/>
|
||||
<div bind:this={divTHead} class="fly-head thead-style" />
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
@ -184,7 +259,6 @@
|
||||
.scroll {
|
||||
flex-grow: 1;
|
||||
min-height: 0;
|
||||
// max-height: 10rem;
|
||||
height: max-content;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
@ -196,6 +270,7 @@
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.track {
|
||||
visibility: hidden;
|
||||
position: absolute;
|
||||
@ -224,7 +299,7 @@
|
||||
opacity: .5;
|
||||
cursor: pointer;
|
||||
z-index: 1;
|
||||
transition: all .1s ease-in-out;
|
||||
transition: all .1s;
|
||||
|
||||
&:hover, &.hovered {
|
||||
background-color: var(--theme-button-bg-hovered);
|
||||
@ -241,14 +316,33 @@
|
||||
}
|
||||
&.hovered { transition: none; }
|
||||
}
|
||||
|
||||
.back {
|
||||
visibility: hidden;
|
||||
position: fixed;
|
||||
// left: 0;
|
||||
// right: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
// background-color: red;
|
||||
background-color: var(--theme-bg-accent-color);
|
||||
z-index: -1;
|
||||
}
|
||||
.fly-head {
|
||||
overflow: hidden;
|
||||
position: fixed;
|
||||
pointer-events: none;
|
||||
visibility: hidden;
|
||||
transition: top .2s ease-out, opacity .2s;
|
||||
}
|
||||
.thead-style {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-width: 0;
|
||||
height: 2.5rem;
|
||||
font-weight: 500;
|
||||
font-size: 0.75rem;
|
||||
color: var(--theme-content-dark-color);
|
||||
background-color: var(--theme-bg-color);
|
||||
box-shadow: inset 0 -1px 0 0 var(--theme-bg-focused-color);
|
||||
user-select: none;
|
||||
}
|
||||
</style>
|
||||
|
@ -17,7 +17,7 @@
|
||||
<script lang="ts">
|
||||
import { Doc, DocumentQuery } from '@anticrm/core'
|
||||
import { getClient } from '@anticrm/presentation'
|
||||
import { Button, Icon, IconAdd, Label, ScrollBox, SearchEdit, showPopup } from '@anticrm/ui'
|
||||
import { Button, Icon, IconAdd, Label, Scroller, SearchEdit, showPopup } from '@anticrm/ui'
|
||||
import view, { Viewlet } from '@anticrm/view'
|
||||
import { Table } from '@anticrm/view-resources'
|
||||
import contact from '../plugin'
|
||||
@ -52,7 +52,7 @@
|
||||
<Button icon={IconAdd} label={contact.string.Create} primary={true} size={'small'} on:click={(ev) => showCreateDialog(ev)}/>
|
||||
</div>
|
||||
|
||||
<ScrollBox vertical stretch noShift>
|
||||
<Scroller>
|
||||
{#await tableDescriptor then descr}
|
||||
{#if descr}
|
||||
<Table
|
||||
@ -64,6 +64,5 @@
|
||||
/>
|
||||
{/if}
|
||||
{/await}
|
||||
</ScrollBox>
|
||||
<div class="ac-body__space-3" />
|
||||
</Scroller>
|
||||
</div>
|
||||
|
@ -15,7 +15,7 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { DocumentQuery } from '@anticrm/core'
|
||||
import { Button, Icon, Label, ScrollBox, SearchEdit, showPopup, IconAdd } from '@anticrm/ui'
|
||||
import { Button, Icon, Label, Scroller, SearchEdit, showPopup, IconAdd } from '@anticrm/ui'
|
||||
import type { Category } from '@anticrm/inventory'
|
||||
import inventory from '../plugin'
|
||||
import CreateCategory from './CreateCategory.svelte'
|
||||
@ -51,7 +51,6 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<ScrollBox vertical stretch noShift>
|
||||
<Scroller>
|
||||
<HierarchyView _class={inventory.class.Category} config={['', 'modifiedOn']} options={{}} query={resultQuery} />
|
||||
</ScrollBox>
|
||||
<div class="ac-body__space-3" />
|
||||
</Scroller>
|
||||
|
@ -14,7 +14,7 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import ui, { Button, EditWithIcon, Icon, IconSearch, Label, ScrollBox, showPopup, IconAdd } from '@anticrm/ui'
|
||||
import ui, { Button, EditWithIcon, Icon, IconSearch, Label, Scroller, showPopup, IconAdd } from '@anticrm/ui'
|
||||
import CreateProduct from './CreateProduct.svelte'
|
||||
import inventory from '../plugin'
|
||||
import { Table } from '@anticrm/view-resources'
|
||||
@ -58,7 +58,7 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<ScrollBox vertical stretch noShift>
|
||||
<Scroller>
|
||||
{#await tableDescriptor then descr}
|
||||
{#if descr}
|
||||
<Table
|
||||
@ -70,5 +70,4 @@
|
||||
/>
|
||||
{/if}
|
||||
{/await}
|
||||
</ScrollBox>
|
||||
<div class="ac-body__space-3" />
|
||||
</Scroller>
|
||||
|
@ -17,7 +17,7 @@
|
||||
<script lang="ts">
|
||||
import { Doc, DocumentQuery } from '@anticrm/core'
|
||||
import { getClient } from '@anticrm/presentation'
|
||||
import { Icon, Label, ScrollBox, SearchEdit } from '@anticrm/ui'
|
||||
import { Icon, Label, Scroller, SearchEdit } from '@anticrm/ui'
|
||||
import { Table } from '@anticrm/view-resources'
|
||||
import view, { Viewlet } from '@anticrm/view'
|
||||
import lead from '../plugin'
|
||||
@ -44,7 +44,7 @@
|
||||
}}/>
|
||||
</div>
|
||||
|
||||
<ScrollBox vertical stretch noShift>
|
||||
<Scroller>
|
||||
{#await tableDescriptor then descr}
|
||||
{#if descr}
|
||||
<Table
|
||||
@ -56,5 +56,4 @@
|
||||
/>
|
||||
{/if}
|
||||
{/await}
|
||||
</ScrollBox>
|
||||
<div class="ac-body__space-3" />
|
||||
</Scroller>
|
||||
|
@ -19,7 +19,7 @@
|
||||
import { Doc, DocumentQuery, Ref } from '@anticrm/core'
|
||||
import { createQuery, getClient } from '@anticrm/presentation'
|
||||
import tags, { selectedTagElements, TagCategory, TagElement } from '@anticrm/tags'
|
||||
import { Button, Component, Icon, Label, ScrollBox, SearchEdit, showPopup } from '@anticrm/ui'
|
||||
import { Button, Component, Icon, Label, Scroller, SearchEdit, showPopup } from '@anticrm/ui'
|
||||
import view, { Viewlet } from '@anticrm/view'
|
||||
import { Table } from '@anticrm/view-resources'
|
||||
import recruit from '../plugin'
|
||||
@ -75,7 +75,7 @@
|
||||
|
||||
<Component is={tags.component.TagsCategoryBar} props={{ targetClass: recruit.mixin.Candidate, category }} on:change={(evt) => updateCategory(evt.detail) }/>
|
||||
|
||||
<ScrollBox vertical stretch noShift>
|
||||
<Scroller>
|
||||
{#await tableDescriptor then descr}
|
||||
{#if descr}
|
||||
<Table
|
||||
@ -87,5 +87,4 @@
|
||||
/>
|
||||
{/if}
|
||||
{/await}
|
||||
</ScrollBox>
|
||||
<div class="ac-body__space-3" />
|
||||
</Scroller>
|
||||
|
@ -1,8 +1,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
|
||||
<symbol id="tags" viewBox="0 0 16 16" fill="none">
|
||||
<ellipse cx="8.00065" cy="5.33332" rx="2.66667" ry="2.66667" stroke="white" stroke-linecap="round"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.49602 10.1829C8.08386 10.1559 7.66752 10.1625 7.25413 10.2033C6.13653 10.3136 5.07583 10.6708 4.20812 11.2416C3.34031 11.8123 2.69243 12.5801 2.37694 13.4632C2.28403 13.7232 2.41953 14.0093 2.67957 14.1022C2.93962 14.1951 3.22574 14.0596 3.31865 13.7996C3.54917 13.1543 4.03933 12.5495 4.75765 12.077C5.47607 11.6045 6.37836 11.2946 7.35239 11.1985C7.40347 11.1934 7.45458 11.189 7.50571 11.1851C7.70324 10.7429 8.05662 10.3856 8.49602 10.1829Z" fill="white"/>
|
||||
<path d="M12 9.33334L12 14.6667" stroke="white" stroke-linecap="round"/>
|
||||
<path d="M14.666 12L9.33268 12" stroke="white" stroke-linecap="round"/>
|
||||
<symbol id="tags" viewBox="0 0 16 16">
|
||||
<path d="M8,8.5c1.7,0,3.2-1.4,3.2-3.2S9.7,2.2,8,2.2c-1.7,0-3.2,1.4-3.2,3.2S6.3,8.5,8,8.5z M8,3.2c1.2,0,2.2,1,2.2,2.2 S9.2,7.5,8,7.5s-2.2-1-2.2-2.2S6.8,3.2,8,3.2z"/>
|
||||
<path d="M4.2,11.2c-0.9,0.6-1.5,1.3-1.8,2.2c-0.1,0.3,0,0.5,0.3,0.6c0.3,0.1,0.5,0,0.6-0.3c0.2-0.6,0.7-1.3,1.4-1.7 c0.7-0.5,1.6-0.8,2.6-0.9c0.1,0,0.1,0,0.2,0c0.2-0.4,0.6-0.8,1-1c-0.4,0-0.8,0-1.2,0C6.1,10.3,5.1,10.7,4.2,11.2z"/>
|
||||
<path d="M14.7,11.5h-2.2V9.3c0-0.3-0.2-0.5-0.5-0.5s-0.5,0.2-0.5,0.5v2.2H9.3c-0.3,0-0.5,0.2-0.5,0.5s0.2,0.5,0.5,0.5h2.2v2.2 c0,0.3,0.2,0.5,0.5,0.5s0.5-0.2,0.5-0.5v-2.2h2.2c0.3,0,0.5-0.2,0.5-0.5S14.9,11.5,14.7,11.5z"/>
|
||||
</symbol>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 913 B After Width: | Height: | Size: 746 B |
@ -16,7 +16,7 @@
|
||||
import { Class, Doc, DocumentQuery, FindOptions, Ref } from '@anticrm/core'
|
||||
import { IntlString, translate } from '@anticrm/platform'
|
||||
import { TagCategory, TagElement } from '@anticrm/tags'
|
||||
import { Button, Icon, Label, ScrollBox, SearchEdit, showPopup } from '@anticrm/ui'
|
||||
import { Button, Icon, Label, Scroller, SearchEdit, showPopup } from '@anticrm/ui'
|
||||
import { Table } from '@anticrm/view-resources'
|
||||
import tags from '../plugin'
|
||||
import CategoryBar from './CategoryBar.svelte'
|
||||
@ -75,7 +75,7 @@
|
||||
updateResultQuery(search, category)
|
||||
}}
|
||||
/>
|
||||
<ScrollBox vertical stretch noShift>
|
||||
<Scroller>
|
||||
<Table
|
||||
_class={tags.class.TagElement}
|
||||
config={[
|
||||
@ -103,5 +103,4 @@
|
||||
query={resultQuery}
|
||||
enableChecking
|
||||
/>
|
||||
</ScrollBox>
|
||||
<div class="ac-body__space-3" />
|
||||
</Scroller>
|
||||
|
@ -124,8 +124,8 @@
|
||||
{/if}
|
||||
{:then model}
|
||||
<table class="table-body" class:enableChecking>
|
||||
<thead>
|
||||
<tr class="tr-head">
|
||||
<thead class="scroller-thead">
|
||||
<tr class="tr-head scroller-thead__tr">
|
||||
{#each model as attribute, cellHead}
|
||||
{#if enableChecking && !cellHead}
|
||||
<th>
|
||||
|
@ -15,7 +15,7 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import type { Class, Doc, DocumentQuery, FindOptions, Ref, Space } from '@anticrm/core'
|
||||
import { ScrollBox } from '@anticrm/ui'
|
||||
import { Scroller } from '@anticrm/ui'
|
||||
import Table from './Table.svelte'
|
||||
|
||||
export let _class: Ref<Class<Doc>>
|
||||
@ -29,17 +29,6 @@
|
||||
$: resultQuery = search === '' ? { space, ...query } : { $search: search, space, ...query }
|
||||
</script>
|
||||
|
||||
<div class="tableview-container">
|
||||
<ScrollBox vertical stretch noShift>
|
||||
<Table {_class} {config} {options} query={resultQuery} {baseMenuClass} enableChecking />
|
||||
</ScrollBox>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.tableview-container {
|
||||
flex-grow: 1;
|
||||
margin-bottom: .75rem;
|
||||
min-height: 0;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
<Scroller>
|
||||
<Table {_class} {config} {options} query={resultQuery} {baseMenuClass} enableChecking />
|
||||
</Scroller>
|
||||
|
Loading…
Reference in New Issue
Block a user