UBER-1101: updated Separator (Float mode), fixed Scroller visibility (#3902)

Signed-off-by: Alexander Platov <sas_lord@mail.ru>
This commit is contained in:
Alexander Platov 2023-10-27 09:11:48 +03:00 committed by GitHub
parent b9a9c8c2ef
commit f1b0cb7dd7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 492 additions and 278 deletions

View File

@ -569,6 +569,7 @@ input.search {
.px-10 { padding: 0 2.5rem; }
.py-0-5 { padding: 0.125rem 0; }
.py-1 { padding: 0.25rem 0; }
.py-2 { padding: 0.5rem 0; }
.py-4 { padding: 1rem 0; }
.py-8 { padding: 2rem 0; }
.py-10 { padding: 2.5rem 0; }
@ -942,7 +943,7 @@ a.no-line {
.background-button-noborder-bg-hover { background-color: var(--noborder-bg-hover); }
.background-primary-color { background-color: var(--primary-button-default); }
.background-content-accent-color { background-color: var(--accent-color); }
.background-comp-header-color { background-color: var(--theme-comp-header-color); }
.background-comp-header-color { background-color: var(--theme-comp-header-color) !important; }
.content-trans-color { color: var(--theme-trans-color); }
.content-darker-color { color: var(--theme-darker-color); }

View File

@ -44,12 +44,9 @@
.antiPanel-navigator,
.antiPanel-component {
flex-grow: 1;
display: flex;
flex-direction: column;
height: 100%;
min-height: 0;
overflow: hidden;
&.header { background-color: var(--theme-comp-header-color); }
&.filled { background-color: var(--theme-bg-color); }
@ -63,8 +60,8 @@
max-width: 22.5rem;
width: 17.5rem;
background-color: var(--theme-navpanel-color);
// border-right: 1px solid var(--theme-navpanel-border);
}
@media (max-width: 1024px) {
.antiPanel-navigator {
position: fixed;
@ -82,16 +79,29 @@
}
}
}
.antiPanel-component:not(.aside) {
.antiPanel-component {
overflow: hidden;
flex-direction: column;
flex-grow: 1;
background-color: var(--theme-bg-color);
}
.antiPanel-component:not(.aside) { background-color: var(--theme-bg-color); }
.antiPanel-component.aside {
min-width: 30rem;
width: 30rem;
max-width: 30rem;
}
.antiPanel-wrap__content {
display: flex;
flex-direction: column;
width: 100%;
height: 100%;
min-width: 0;
min-height: 0;
&.hidden { overflow: hidden; }
}
/* Navigation */
.antiNav-header {
display: flex;

View File

@ -243,15 +243,16 @@
&.float {
position: absolute;
flex-direction: row;
flex-shrink: 0;
top: 0;
left: 100%;
width: var(--panel-aside-width);
min-width: 0;
max-width: var(--panel-aside-width);
height: 100%;
min-width: 0;
min-height: 0;
background-color: var(--theme-panel-color);
border-left: 1px solid var(--theme-divider-color);
// border-left: 1px solid var(--theme-divider-color);
border-bottom-right-radius: .45rem;
box-shadow: 0 0 0 0 rgba(0, 0, 0, 0);
transition: box-shadow 150ms ease 0s, transform 150ms cubic-bezier(0.175, 0.885, 0.32, 1.275);

View File

@ -219,14 +219,17 @@
</div>
{/if}
{#if $$slots.aside && isAside && asideShown}
<Separator name={'panel-aside'} index={0} />
<Separator name={'panel-aside'} float={asideFloat} index={0} />
<div class="popupPanel-body__aside" class:float={asideFloat} class:shown={asideShown}>
{#if moveUtils}
<div class="buttons-group justify-end xsmall-gap" style:margin={'.5rem 2rem 0'}>
<slot name="utils" />
</div>
{/if}
<slot name="aside" />
<Separator name={'panel-aside'} float={asideFloat ? 'aside' : true} index={0} />
<div class="antiPanel-wrap__content">
{#if moveUtils}
<div class="buttons-group justify-end xsmall-gap" style:margin={'.5rem 2rem 0'}>
<slot name="utils" />
</div>
{/if}
<slot name="aside" />
</div>
</div>
{/if}
</div>

View File

@ -194,6 +194,7 @@
const el: HTMLElement = event.currentTarget as HTMLElement
if (el && isScrolling) {
document.removeEventListener('mousemove', onScroll)
// document.body.style.pointerEvents = 'all'
document.body.style.userSelect = 'auto'
document.body.style.webkitUserSelect = 'auto'
}
@ -210,6 +211,7 @@
: event.clientX - el.getBoundingClientRect().x
document.addEventListener('mouseup', onScrollEnd)
document.addEventListener('mousemove', onScroll)
// document.body.style.pointerEvents = 'none'
document.body.style.userSelect = 'none'
document.body.style.webkitUserSelect = 'none'
isScrolling = direction
@ -593,16 +595,18 @@
</button>
</div>
{/if}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div class="track" class:hovered={isScrolling === 'vertical'} on:click|stopPropagation={(ev) => clickOnTrack(ev)} />
<div
class="bar"
class:hovered={isScrolling === 'vertical'}
bind:this={divBar}
on:mousedown={(ev) => onScrollStart(ev, 'vertical')}
on:mouseleave={checkFade}
/>
{#if horizontal}
{#if mask !== 'none'}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div class="track" class:hovered={isScrolling === 'vertical'} on:click|stopPropagation={(ev) => clickOnTrack(ev)} />
<div
class="bar"
class:hovered={isScrolling === 'vertical'}
bind:this={divBar}
on:mousedown={(ev) => onScrollStart(ev, 'vertical')}
on:mouseleave={checkFade}
/>
{/if}
{#if horizontal && maskH !== 'none'}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div
class="track-horizontal"

View File

@ -21,7 +21,8 @@
getSeparators,
saveSeparator,
SeparatedElement,
separatorsStore
separatorsStore,
SeparatorState
} from '..'
export let prevElementSize: SeparatedItem | undefined = undefined
@ -31,15 +32,22 @@
export let name: string
export let disabledWhen: string[] = []
export let index: number // index = -1 ; for custom sizes without saving to a localStorage
export let float: string | boolean = false // false - default state, true - hidden state for float, name - panel name for resize (float state)
let sState: SeparatorState
$: sState =
typeof float === 'string' ? SeparatorState.FLOAT : float === true ? SeparatorState.HIDDEN : SeparatorState.NORMAL
const direction: 'horizontal' | 'vertical' = 'horizontal'
let separators: SeparatedItem[] | null = null
let separatorMap: SeparatedElement[]
let prevElSize: SeparatedItem
let nextElSize: SeparatedItem
let panel: SeparatedItem
let separator: HTMLElement
let prevElement: HTMLElement | null
let nextElement: HTMLElement | null
let parentElement: HTMLElement | null
let mounted: boolean = false
let isSeparate: boolean = false
let excludedIndexes: number[] = []
@ -55,16 +63,28 @@
}
let parentSize: { start: number; end: number; size: number } | null = null
let disabled: boolean = false
let side: 'start' | 'end' | undefined = undefined
const fetchSeparators = (): void => {
separators = getSeparators(name)
prevElSize = separators !== null ? separators[index] : nullSeparatedItem
nextElSize = separators !== null ? separators[index + 1] : nullSeparatedItem
const res = getSeparators(name, float)
if (res !== null && !Array.isArray(res)) panel = res
else if (Array.isArray(res)) {
separators = res
prevElSize = separators !== null ? separators[index] : nullSeparatedItem
nextElSize = separators !== null ? separators[index + 1] : nullSeparatedItem
}
}
$: if (name) {
$: if (name || float) {
fetchSeparators()
if (prevElementSize !== undefined) prevElSize = prevElementSize
if (nextElementSize !== undefined) nextElSize = nextElementSize
if (sState === SeparatorState.NORMAL) {
if (prevElementSize !== undefined) prevElSize = prevElementSize
if (nextElementSize !== undefined) nextElSize = nextElementSize
setTimeout(() => {
if (!parentElement && separator) parentElement = separator.parentElement
checkSibling(true)
calculateSeparators()
})
}
checkSizes()
}
@ -87,21 +107,17 @@
}
const sizePx = direction === 'horizontal' ? element.clientWidth : element.clientHeight
element.setAttribute('data-size', `${sizePx}`)
if (separators) separators[index + (next ? 1 : 0)].size = pxToRem(sizePx)
if (next) nextElSize.size = typeof size === 'number' ? pxToRem(sizePx) : size
else prevElSize.size = typeof size === 'number' ? pxToRem(sizePx) : size
if (sState === SeparatorState.NORMAL) {
if (separators) separators[index + (next ? 1 : 0)].size = pxToRem(sizePx)
if (next) nextElSize.size = typeof size === 'number' ? pxToRem(sizePx) : size
else prevElSize.size = typeof size === 'number' ? pxToRem(sizePx) : size
}
}
const generateMap = (): void => {
const parent = separator.parentElement
if (parent === null) return
const p = parent.getBoundingClientRect()
parentSize =
direction === 'horizontal'
? { start: p.left, end: p.right, size: p.width }
: { start: p.top, end: p.bottom, size: p.height }
const children: Element[] = Array.from(parent.children)
if (children.length > 1 && separators !== null) {
if (parentElement === null) return
const children: Element[] = Array.from(parentElement.children)
if (children.length > 1 && separators !== null && separatorsSizes !== null) {
const elements = children.filter(
(el) =>
!el.classList.contains('antiSeparator') && (el.hasAttribute('data-size') || el.hasAttribute('data-auto'))
@ -162,6 +178,7 @@
? -1
: endBoxes.map((box) => box.maxSize).reduce((prev, a) => prev + a, 0)
}
isSeparate = true
}
const initSize = (element: HTMLElement, props: SeparatedItem, next: boolean = false): void => {
@ -187,12 +204,16 @@
}
const checkSizes = (): void => {
if (prevElement) initSize(prevElement, prevElSize)
if (nextElement) initSize(nextElement, nextElSize, true)
if (sState === SeparatorState.FLOAT) {
if (parentElement) initSize(parentElement, panel)
} else if (sState === SeparatorState.NORMAL) {
if (prevElement) initSize(prevElement, prevElSize)
if (nextElement) initSize(nextElement, nextElSize, true)
}
}
const applyStyles = (final: boolean = false): void => {
if (separatorMap === null) return
if (separatorMap == null) return
const side = direction === 'horizontal' ? 'width' : 'height'
const sum = separatorMap
.filter((f) => f.maxSize !== -1)
@ -234,6 +255,50 @@
}
function mouseMove (event: MouseEvent) {
if (sState === SeparatorState.NORMAL) normalMouseMove(event)
else if (sState === SeparatorState.FLOAT) floatMouseMove(event)
}
const preparePanel = (): void => {
if (!parentElement || parentSize === null) return
setSize(parentElement, panel.size === 'auto' ? 'auto' : remToPx(panel.size))
const s = separator.getBoundingClientRect()
if (s) {
const currentPoint = direction === 'horizontal' ? s.x : s.y
side =
parentSize.end - separatorSize === currentPoint
? 'end'
: parentSize.start === currentPoint
? 'start'
: undefined
}
if (side !== undefined) isSeparate = true
parentElement.style.pointerEvents = 'none'
}
function floatMouseMove (event: MouseEvent) {
if (!isSeparate || parentSize === null || parentElement === null) return
const coord: number = direction === 'horizontal' ? event.x - offset : event.y - offset
const parentCoord: number = coord - parentSize.start
const min = remToPx(panel.minSize === 'auto' ? 10 : panel.minSize)
const max = remToPx(panel.maxSize === 'auto' ? 30 : panel.maxSize)
const newCoord =
side === 'start'
? parentSize.size - parentCoord < min - separatorSize
? min
: parentSize.size - parentCoord > max - separatorSize
? max
: parentSize.size - parentCoord
: parentCoord < min - separatorSize
? min
: parentCoord > max - separatorSize
? max
: parentCoord - separatorSize
panel.size = pxToRem(newCoord)
setSize(parentElement, newCoord)
}
function normalMouseMove (event: MouseEvent) {
if (!isSeparate || separatorMap === null || parentSize === null || separatorsSizes === null) return
const coord: number = direction === 'horizontal' ? event.x - offset : event.y - offset
let parentCoord: number = coord - parentSize.start
@ -314,48 +379,59 @@
function mouseUp () {
isSeparate = false
applyStyles(true)
if (index !== -1 && separators && separatorMap) {
let ind: number = 0
const sep: SeparatedItem[] = []
separators.forEach((sm, i) => {
let save = false
if (excludedIndexes.includes(i)) {
save = true
ind++
}
if (save) sep.push(sm)
else {
sep.push({
size: separatorMap[i - ind].maxSize === -1 ? 'auto' : pxToRem(separatorMap[i - ind].size),
minSize: pxToRem(separatorMap[i - ind].minSize),
maxSize: separatorMap[i - ind].maxSize === -1 ? 'auto' : pxToRem(separatorMap[i - ind].maxSize),
float: separatorMap[i - ind].float
})
}
})
saveSeparator(name, sep)
if (sState === SeparatorState.NORMAL) {
applyStyles(true)
if (index !== -1 && separators && separatorMap) {
let ind: number = 0
const sep: SeparatedItem[] = []
separators.forEach((sm, i) => {
let save = false
if (excludedIndexes.includes(i)) {
save = true
ind++
}
if (save) sep.push(sm)
else {
sep.push({
size: separatorMap[i - ind].maxSize === -1 ? 'auto' : pxToRem(separatorMap[i - ind].size),
minSize: pxToRem(separatorMap[i - ind].minSize),
maxSize: separatorMap[i - ind].maxSize === -1 ? 'auto' : pxToRem(separatorMap[i - ind].maxSize),
float: separatorMap[i - ind].float
})
}
})
saveSeparator(name, false, sep)
}
} else if (sState === SeparatorState.FLOAT && parentElement) {
parentElement.style.pointerEvents = 'all'
saveSeparator(name, float, panel)
}
document.body.style.userSelect = 'auto'
document.body.style.webkitUserSelect = 'auto'
document.body.style.cursor = ''
document.removeEventListener('mousemove', mouseMove)
document.removeEventListener('mouseup', mouseUp)
}
function mouseDown (event: MouseEvent) {
if (prevElement === null || nextElement === null) {
if (!parentElement) return
if (sState === SeparatorState.FLOAT && parentElement === null) {
checkParent()
return
} else if (sState === SeparatorState.NORMAL && (prevElement === null || nextElement === null)) {
checkSibling()
return
}
offset = direction === 'horizontal' ? event.offsetX : event.offsetY
generateMap()
isSeparate = true
applyStyles(true)
const p = parentElement.getBoundingClientRect()
parentSize =
direction === 'horizontal'
? { start: p.left, end: p.right, size: p.width }
: { start: p.top, end: p.bottom, size: p.height }
if (sState === SeparatorState.NORMAL) {
generateMap()
applyStyles(true)
} else if (sState === SeparatorState.FLOAT) preparePanel()
document.addEventListener('mousemove', mouseMove)
document.addEventListener('mouseup', mouseUp)
document.body.style.userSelect = 'none'
document.body.style.webkitUserSelect = 'none'
document.body.style.cursor = direction === 'horizontal' ? 'col-resize' : 'row-resize'
}
@ -369,48 +445,66 @@
nextElement.setAttribute('data-float', separators[index + 1].float ?? '')
}
}
const checkParent = (): void => {
if (parentElement === null) parentElement = separator.parentElement as HTMLElement
if (parentElement && typeof float === 'string') parentElement.setAttribute('data-float', float)
}
const calculateSeparators = (): void => {
if (parentElement) {
const elements: Element[] = Array.from(parentElement.children)
separatorsSizes = elements
.filter((el) => el.classList.contains('antiSeparator'))
.map((el) => parseInt(el.getAttribute('data-size') ?? '0', 10))
separatorsWide.total = separatorsSizes.reduce((prev, a) => prev + a, 0)
separatorsWide.start = separatorsSizes.slice(0, index).reduce((prev, a) => prev + a, 0)
separatorsWide.end = separatorsSizes.slice(index + 1, separatorsSizes.length).reduce((prev, a) => prev + a, 0)
}
}
onMount(() => {
if (separator) {
checkSibling(true)
const parent = separator.parentElement
if (parent) {
const elements: Element[] = Array.from(parent.children)
separatorsSizes = elements
.filter((el) => el.classList.contains('antiSeparator'))
.map((el) => parseInt(el.getAttribute('data-size') ?? '0', 10))
separatorsWide.total = separatorsSizes.reduce((prev, a) => prev + a, 0)
separatorsWide.start = separatorsSizes.slice(0, index).reduce((prev, a) => prev + a, 0)
separatorsWide.end = separatorsSizes.slice(index + 1, separatorsSizes.length).reduce((prev, a) => prev + a, 0)
parentElement = separator.parentElement as HTMLElement
if (sState === SeparatorState.FLOAT) checkParent()
else if (sState === SeparatorState.NORMAL) {
checkSibling(true)
calculateSeparators()
}
checkSizes()
mounted = true
}
document.addEventListener('resize', checkSizes)
if ($separatorsStore.filter((f) => f === name).length === 0) $separatorsStore = [...$separatorsStore, name]
disabled = $separatorsStore.filter((f) => disabledWhen.findIndex((d) => d === f) !== -1).length > 0
if (sState !== SeparatorState.FLOAT && $separatorsStore.filter((f) => f === name).length === 0) {
$separatorsStore = [...$separatorsStore, name]
}
})
onDestroy(() => {
document.removeEventListener('resize', checkSizes)
if ($separatorsStore.filter((f) => f === name).length > 0) {
if (sState !== SeparatorState.FLOAT && $separatorsStore.filter((f) => f === name).length > 0) {
$separatorsStore = $separatorsStore.filter((f) => f !== name)
}
})
afterUpdate(() => {
if (mounted) checkSibling()
if (mounted) {
if (sState === SeparatorState.FLOAT) checkParent()
else if (sState === SeparatorState.NORMAL) checkSibling()
}
})
$: disabled = $separatorsStore.filter((f) => disabledWhen.findIndex((d) => d === f) !== -1).length > 0
</script>
<div
bind:this={separator}
style:--separator-size={`${separatorSize}px`}
style:background-color={color}
style:pointer-events={disabled ? 'none' : 'all'}
class="antiSeparator {direction}"
class:hovered={isSeparate}
data-size={separatorSize}
on:mousedown|stopPropagation={mouseDown}
/>
{#if sState !== SeparatorState.HIDDEN}
<div
bind:this={separator}
style:--separator-size={`${separatorSize}px`}
style:background-color={color}
style:pointer-events={disabled ? 'none' : 'all'}
class="antiSeparator {direction}"
class:hovered={isSeparate}
data-size={separatorSize}
on:mousedown|stopPropagation={mouseDown}
/>
{/if}
<style lang="scss">
.antiSeparator {

View File

@ -64,6 +64,15 @@ export function resizeObserver (element: Element, onResize: (element: Element) =
*/
export const separatorsKeyId = 'separators'
/**
* @public
*/
export enum SeparatorState {
FLOAT = 'float',
HIDDEN = 'hidden',
NORMAL = 'normal'
}
export const nullSeparatedItem: SeparatedItem = {
size: 'auto',
minSize: 20,
@ -71,46 +80,76 @@ export const nullSeparatedItem: SeparatedItem = {
float: undefined
}
const compareSeparators = (a: SeparatedItem[], b: SeparatedItem[]): boolean => {
const compareSeparators = (a: SeparatedItem[] | SeparatedItem, b: SeparatedItem[] | SeparatedItem): boolean => {
if (!Array.isArray(a) && !Array.isArray(b)) {
return a.minSize === b.minSize && a.maxSize === b.maxSize && a.float === b.float
}
if (!Array.isArray(a) || !Array.isArray(b)) return false
if (a.length !== b.length) return false
return a.every(
(sep, index) => sep.minSize === b[index].minSize && sep.maxSize === b[index].maxSize && sep.float === b[index].float
)
}
const generateSeparatorsId = (name: string): string => {
return separatorsKeyId + '_' + name
const generateSeparatorsId = (name: string, float: string | boolean): string => {
return separatorsKeyId + '_' + name + (typeof float === 'string' ? '-float-' + float : '')
}
export function defineSeparators (name: string, items: DefSeparators): void {
const id = generateSeparatorsId(name)
const id = generateSeparatorsId(name, false)
const income = items.map((it) => (it === null ? nullSeparatedItem : it))
let needAdd = true
const saved = localStorage.getItem(id)
let needAdd = false
if (saved !== null) {
const loaded: SeparatedItem[] = JSON.parse(saved)
if (!compareSeparators(loaded, income)) {
localStorage.removeItem(id)
needAdd = true
if (typeof saved === 'string') {
if (saved === 'undefined') localStorage.removeItem(id)
else {
const loaded: SeparatedItem[] = JSON.parse(saved)
if (!compareSeparators(loaded, income)) localStorage.removeItem(id)
else needAdd = false
}
} else needAdd = true
}
if (needAdd) localStorage.setItem(id, JSON.stringify(income))
items.forEach((it) => {
if (typeof it?.float === 'string') {
const idF = generateSeparatorsId(name, it.float)
let needAdd = true
const savedF = localStorage.getItem(idF)
if (typeof savedF === 'string') {
if (savedF === 'undefined') localStorage.removeItem(idF)
else {
const loadedF: SeparatedItem = JSON.parse(savedF)
if (!compareSeparators(loadedF, it)) localStorage.removeItem(idF)
else needAdd = false
}
}
if (needAdd) localStorage.setItem(idF, JSON.stringify(it))
}
})
}
export function getSeparators (name: string): SeparatedItem[] | null {
const id = generateSeparatorsId(name)
export function getSeparators (name: string, float: string | boolean): SeparatedItem[] | SeparatedItem | null {
const id = generateSeparatorsId(name, float)
const saved = localStorage.getItem(id)
return saved !== null ? JSON.parse(saved) : null
if (saved === null) return null
const result = JSON.parse(saved)
return Array.isArray(result) ? (result as SeparatedItem[]) : (result as SeparatedItem)
}
export function saveSeparator (name: string, separators: SeparatedItem[]): void {
const id = generateSeparatorsId(name)
localStorage.setItem(id, JSON.stringify(separators))
export function saveSeparator (
name: string,
float: string | boolean,
separators: SeparatedItem | SeparatedItem[]
): void {
const id = generateSeparatorsId(name, float)
localStorage.setItem(
id,
Array.isArray(separators) && typeof float === 'string' ? JSON.stringify(separators[0]) : JSON.stringify(separators)
)
}
export const panelSeparators: DefSeparators = [
{ minSize: 30, size: 'auto', maxSize: 'auto' },
{ minSize: 17, size: 25, maxSize: 50, float: 'aside' }
{ minSize: 17, size: 25, maxSize: 35, float: 'aside' }
]
export const workbenchSeparators: DefSeparators = [

View File

@ -39,7 +39,9 @@
import ScheduleView from './ScheduleView.svelte'
import Sidebar from './sidebar/Sidebar.svelte'
export let visibileNav = true
export let visibileNav: boolean = true
export let navFloat: boolean = false
export let appsDirection: 'vertical' | 'horizontal' = 'horizontal'
const accountEmployee = $employeeByIdStore.get((getCurrentAccount() as PersonAccount).person as Ref<Employee>)
let accountStaff: Staff | undefined
@ -130,9 +132,17 @@
{department}
{descendants}
departmentById={departments}
{navFloat}
{appsDirection}
on:selected={(e) => departmentSelected(e.detail)}
/>
<Separator name={'workbench'} disabledWhen={['panel-aside']} index={0} color={'var(--theme-navpanel-border)'} />
<Separator
name={'workbench'}
float={navFloat}
disabledWhen={['panel-aside']}
index={0}
color={'var(--theme-navpanel-border)'}
/>
{/if}
<div class="antiPanel-component filled">

View File

@ -15,7 +15,7 @@
<script lang="ts">
import { Ref } from '@hcengineering/core'
import { Department } from '@hcengineering/hr'
import { Scroller } from '@hcengineering/ui'
import { Scroller, Separator } from '@hcengineering/ui'
import { TreeNode } from '@hcengineering/view-resources'
import { NavFooter, NavHeader } from '@hcengineering/workbench-resources'
@ -26,19 +26,29 @@
export let department: Ref<Department>
export let descendants: Map<Ref<Department>, Department[]>
export let departmentById: Map<Ref<Department>, Department>
export let navFloat: boolean = false
export let appsDirection: 'horizontal' | 'vertical' = 'horizontal'
const departments = [hr.ids.Head]
</script>
<div class="antiPanel-navigator">
<NavHeader label={hr.string.HRApplication} />
<div class="antiPanel-navigator {appsDirection === 'horizontal' ? 'portrait' : 'landscape'}">
<div class="antiPanel-wrap__content">
<NavHeader label={hr.string.HRApplication} />
<Scroller shrink>
<TreeNode label={hr.string.Departments} node>
<DepartmentsHierarchy {departments} {descendants} {departmentById} selected={department} on:selected />
</TreeNode>
<div class="antiNav-space" />
</Scroller>
<Scroller shrink>
<TreeNode label={hr.string.Departments} node>
<DepartmentsHierarchy {departments} {descendants} {departmentById} selected={department} on:selected />
</TreeNode>
<div class="antiNav-space" />
</Scroller>
<NavFooter />
<NavFooter />
</div>
<Separator
name={'workbench'}
float={navFloat ? 'navigator' : true}
index={0}
color={'var(--theme-navpanel-border)'}
/>
</div>

View File

@ -179,7 +179,7 @@
mode: 'browser'
}}
/>
<div class="inbox-activity">
<div class="inbox-activity py-2">
<Scroller noStretch>
{#if loading}
<Loading />

View File

@ -42,7 +42,9 @@
import People from './People.svelte'
import { subscribe } from '../utils'
export let visibileNav: boolean
export let visibileNav: boolean = true
export let navFloat: boolean = false
export let appsDirection: 'vertical' | 'horizontal' = 'horizontal'
let filter: 'all' | 'read' | 'unread' = 'all'
const client = getClient()
@ -134,34 +136,45 @@
}
)
}
defineSeparators('inbox', [{ minSize: 20, maxSize: 40, size: 30 }, null])
defineSeparators('inbox', [
{ minSize: 20, maxSize: 40, size: 30, float: 'navigator' },
{ size: 'auto', minSize: 30, maxSize: 'auto', float: undefined }
])
</script>
<div class="flex-row-top h-full">
{#if visibileNav}
<div class="antiPanel-component header aside min-w-100 flex-no-shrink">
<Tabs
bind:selected={selectedTab}
model={tabs}
on:change={(e) => select(e.detail)}
on:open={(e) => {
selectedEmployee = e.detail
select(undefined)
}}
padding={'0 1.75rem'}
size="small"
>
<svelte:fragment slot="rightButtons">
<div class="flex flex-gap-2">
{#if selectedTab > 0}
<Button label={chunter.string.Message} icon={IconAdd} kind="primary" on:click={openUsersPopup} />
{/if}
<Filter bind:filter />
</div>
</svelte:fragment>
</Tabs>
<div
class="antiPanel-navigator {appsDirection === 'horizontal'
? 'portrait'
: 'landscape'} background-comp-header-color"
>
<div class="antiPanel-wrap__content">
<Tabs
bind:selected={selectedTab}
model={tabs}
on:change={(e) => select(e.detail)}
on:open={(e) => {
selectedEmployee = e.detail
select(undefined)
}}
padding={'0 1.75rem'}
size={'small'}
noMargin
>
<svelte:fragment slot="rightButtons">
<div class="flex flex-gap-2">
{#if selectedTab > 0}
<Button label={chunter.string.Message} icon={IconAdd} kind="primary" on:click={openUsersPopup} />
{/if}
<Filter bind:filter />
</div>
</svelte:fragment>
</Tabs>
</div>
<Separator name={'inbox'} float={navFloat ? 'navigator' : true} index={0} />
</div>
<Separator name={'inbox'} index={0} />
<Separator name={'inbox'} float={navFloat} index={0} />
{/if}
<div class="antiPanel-component filled w-full">
{#if selectedEmployee !== undefined && component === undefined}
@ -174,11 +187,7 @@
}}
/>
{:else if component && _id && _class}
<Component
is={component}
props={{ _id, _class, embedded: selectedTab === 0 }}
on:close={() => select(undefined)}
/>
<Component is={component} props={{ _id, _class, embedded: true }} on:close={() => select(undefined)} />
{/if}
</div>
</div>

View File

@ -69,37 +69,39 @@
<div class="flex h-full clear-mins">
<div class="antiPanel-navigator">
<div class="antiNav-header overflow-label">
<Label label={notification.string.Notifications} />
<div class="antiPanel-wrap__content">
<div class="antiNav-header overflow-label">
<Label label={notification.string.Notifications} />
</div>
<Scroller shrink>
{#each preferencesGroups as preferenceGroup}
<GroupElement
icon={preferenceGroup.icon}
label={preferenceGroup.label}
selected={preferenceGroup === currentPreferenceGroup}
on:click={() => {
currentPreferenceGroup = preferenceGroup
group = undefined
}}
/>
{/each}
{#if preferencesGroups.length > 0 && groups.length > 0}
<div class="antiNav-divider short line" />
{/if}
{#each groups as gr}
<GroupElement
icon={gr.icon}
label={gr.label}
selected={gr._id === group}
on:click={() => {
group = gr._id
currentPreferenceGroup = undefined
}}
/>
{/each}
<div class="antiNav-space" />
</Scroller>
</div>
<Scroller shrink>
{#each preferencesGroups as preferenceGroup}
<GroupElement
icon={preferenceGroup.icon}
label={preferenceGroup.label}
selected={preferenceGroup === currentPreferenceGroup}
on:click={() => {
currentPreferenceGroup = preferenceGroup
group = undefined
}}
/>
{/each}
{#if preferencesGroups.length > 0 && groups.length > 0}
<div class="antiNav-divider short line" />
{/if}
{#each groups as gr}
<GroupElement
icon={gr.icon}
label={gr.label}
selected={gr._id === group}
on:click={() => {
group = gr._id
currentPreferenceGroup = undefined
}}
/>
{/each}
<div class="antiNav-space" />
</Scroller>
</div>
<Separator name={'settingNotify'} index={0} color={'var(--theme-navpanel-border)'} />

View File

@ -163,7 +163,7 @@
}
</script>
<div class="inbox-activity">
<div class="inbox-activity py-2">
<Scroller noStretch>
{#if loading}
<Loading />

View File

@ -36,7 +36,10 @@
import { onDestroy } from 'svelte'
import CategoryElement from './CategoryElement.svelte'
export let visibileNav = true
export let visibileNav: boolean = true
export let navFloat: boolean = false
export let appsDirection: 'vertical' | 'horizontal' = 'horizontal'
let category: SettingsCategory | undefined
let categoryId: string = ''
@ -95,42 +98,50 @@
<div class="flex h-full clear-mins">
{#if visibileNav}
<div class="antiPanel-navigator">
<NavHeader label={setting.string.Settings} />
<div class="antiPanel-navigator {appsDirection === 'horizontal' ? 'portrait' : 'landscape'}">
<div class="antiPanel-wrap__content">
<NavHeader label={setting.string.Settings} />
<Scroller shrink>
{#each categories as category, i}
{#if i > 0 && categories[i - 1].group !== category.group}
<div class="antiNav-divider short line" />
{/if}
<Scroller shrink>
{#each categories as category, i}
{#if i > 0 && categories[i - 1].group !== category.group}
<div class="antiNav-divider short line" />
{/if}
<CategoryElement
icon={category.icon}
label={category.label}
selected={category.name === categoryId}
expandable={category._id === setting.ids.Setting}
on:click={() => {
selectCategory(category.name)
}}
/>
{/each}
<div class="antiNav-space" />
</Scroller>
<NavFooter split>
<CategoryElement
icon={category.icon}
label={category.label}
selected={category.name === categoryId}
expandable={category._id === setting.ids.Setting}
on:click={() => {
selectCategory(category.name)
}}
icon={setting.icon.SelectWorkspace}
label={setting.string.SelectWorkspace}
on:click={selectWorkspace}
/>
{/each}
<div class="antiNav-space" />
</Scroller>
<NavFooter split>
<CategoryElement
icon={setting.icon.SelectWorkspace}
label={setting.string.SelectWorkspace}
on:click={selectWorkspace}
/>
<CategoryElement
icon={login.icon.InviteWorkspace}
label={setting.string.InviteWorkspace}
on:click={inviteWorkspace}
/>
<CategoryElement icon={setting.icon.Signout} label={setting.string.Signout} on:click={signOut} />
</NavFooter>
<CategoryElement
icon={login.icon.InviteWorkspace}
label={setting.string.InviteWorkspace}
on:click={inviteWorkspace}
/>
<CategoryElement icon={setting.icon.Signout} label={setting.string.Signout} on:click={signOut} />
</NavFooter>
</div>
<Separator
name={'setting'}
float={navFloat ? 'navigator' : true}
index={0}
color={'var(--theme-navpanel-border)'}
/>
</div>
<Separator name={'setting'} index={0} color={'var(--theme-navpanel-border)'} />
<Separator name={'setting'} float={navFloat} index={0} color={'var(--theme-navpanel-border)'} />
{/if}
<div class="antiPanel-component filled">

View File

@ -74,22 +74,24 @@
<div class="flex h-full clear-mins">
{#if visibileNav}
<div class="antiPanel-navigator filledNav indent">
<div class="antiNav-header overflow-label">
<Label label={setting.string.WorkspaceSetting} />
<div class="antiPanel-wrap__content">
<div class="antiNav-header overflow-label">
<Label label={setting.string.WorkspaceSetting} />
</div>
<Scroller shrink>
{#each categories as category}
<CategoryElement
icon={category.icon}
label={category.label}
selected={category.name === categoryId}
on:click={() => {
selectCategory(category.name)
}}
/>
{/each}
<div class="antiNav-space" />
</Scroller>
</div>
<Scroller shrink>
{#each categories as category}
<CategoryElement
icon={category.icon}
label={category.label}
selected={category.name === categoryId}
on:click={() => {
selectCategory(category.name)
}}
/>
{/each}
<div class="antiNav-space" />
</Scroller>
</div>
<Separator name={'settingWorkspace'} index={0} color={'var(--theme-navpanel-border)'} />
{/if}

View File

@ -686,44 +686,52 @@
<div class="workbench-container">
{#if currentApplication && navigatorModel && navigator && visibileNav}
<!-- svelte-ignore a11y-click-events-have-key-events -->
{#if visibileNav && navFloat}<div class="cover shown" on:click={() => (visibileNav = false)} />{/if}
{#if navFloat}<div class="cover shown" on:click={() => (visibileNav = false)} />{/if}
<div class="antiPanel-navigator {appsDirection === 'horizontal' ? 'portrait' : 'landscape'}">
{#if currentApplication}
<NavHeader label={currentApplication.label} />
{#if currentApplication.navHeaderComponent}
{#await checkIsHeaderHidden(currentApplication) then isHidden}
{#if !isHidden}
{#await checkIsHeaderDisabled(currentApplication) then disabled}
<Component
is={currentApplication.navHeaderComponent}
props={{
currentSpace,
currentSpecial,
currentFragment,
disabled
}}
shrink
/>
{/await}
{/if}
{/await}
<div class="antiPanel-wrap__content">
{#if currentApplication}
<NavHeader label={currentApplication.label} />
{#if currentApplication.navHeaderComponent}
{#await checkIsHeaderHidden(currentApplication) then isHidden}
{#if !isHidden}
{#await checkIsHeaderDisabled(currentApplication) then disabled}
<Component
is={currentApplication.navHeaderComponent}
props={{
currentSpace,
currentSpecial,
currentFragment,
disabled
}}
shrink
/>
{/await}
{/if}
{/await}
{/if}
{/if}
{/if}
<Navigator
{currentSpace}
{currentSpecial}
{currentFragment}
model={navigatorModel}
{currentApplication}
on:open={checkOnHide}
<Navigator
{currentSpace}
{currentSpecial}
{currentFragment}
model={navigatorModel}
{currentApplication}
on:open={checkOnHide}
/>
<NavFooter>
{#if currentApplication.navFooterComponent}
<Component is={currentApplication.navFooterComponent} props={{ currentSpace }} />
{/if}
</NavFooter>
</div>
<Separator
name={'workbench'}
float={navFloat ? 'navigator' : true}
index={0}
color={'var(--theme-navpanel-border)'}
/>
<NavFooter>
{#if currentApplication.navFooterComponent}
<Component is={currentApplication.navFooterComponent} props={{ currentSpace }} />
{/if}
</NavFooter>
</div>
<Separator name={'workbench'} index={0} color={'var(--theme-navpanel-border)'} />
<Separator name={'workbench'} float={navFloat} index={0} color={'var(--theme-navpanel-border)'} />
{/if}
<div
class="antiPanel-component antiComponent"
@ -733,11 +741,18 @@
}}
>
{#if currentApplication && currentApplication.component}
<Component is={currentApplication.component} props={{ currentSpace, visibileNav }} />
<Component is={currentApplication.component} props={{ currentSpace, visibileNav, navFloat, appsDirection }} />
{:else if specialComponent}
<Component
is={specialComponent.component}
props={{ model: navigatorModel, ...specialComponent.componentProps, currentSpace, visibileNav }}
props={{
model: navigatorModel,
...specialComponent.componentProps,
currentSpace,
visibileNav,
navFloat,
appsDirection
}}
on:action={(e) => {
if (e?.detail) {
const loc = getCurrentLocation()
@ -747,7 +762,10 @@
}}
/>
{:else if currentView?.component !== undefined}
<Component is={currentView.component} props={{ ...currentView.componentProps, currentView, visibileNav }} />
<Component
is={currentView.component}
props={{ ...currentView.componentProps, currentView, visibileNav, navFloat, appsDirection }}
/>
{:else}
<SpaceView {currentSpace} {currentView} {createItemDialog} {createItemLabel} />
{/if}