Updated navigator layout (#5619)

Signed-off-by: Alexander Platov <alexander.platov@hardcoreeng.com>
This commit is contained in:
Alexander Platov
2024-06-05 12:50:13 +07:00
committed by GitHub
parent 07d35f0827
commit a437926f35
79 changed files with 1582 additions and 1012 deletions
@@ -25,6 +25,7 @@
export let props: Record<string, any> = {}
export let inline: boolean = false
export let accent: boolean = false
export let colorInherit: boolean = false
export let shouldShowAvatar: boolean = true
export let noUnderline: boolean = false
export let disabled: boolean = false
@@ -75,6 +76,7 @@
value={doc}
{inline}
{accent}
{colorInherit}
{shouldShowAvatar}
{shouldShowName}
{noUnderline}
@@ -19,36 +19,42 @@
import type { Action, AnySvelteComponent, IconSize } from '@hcengineering/ui'
import {
ActionIcon,
Icon,
IconChevronDown,
IconMoreH,
Label,
Menu,
showPopup,
getTreeCollapsed,
setTreeCollapsed
NavItem,
NavGroup,
ButtonIcon
} from '@hcengineering/ui'
import { createEventDispatcher } from 'svelte'
export let _id: Ref<Doc> | string | undefined = undefined
export let icon: Asset | AnySvelteComponent | undefined = undefined
export let folderIcon: boolean = false
export let iconProps: Record<string, any> | undefined = undefined
export let iconSize: IconSize = 'small'
export let label: IntlString | undefined = undefined
export let title: string | undefined = undefined
export let notifications = 0
export let parent: boolean = false
export let node: boolean = false
export let type: 'default' | 'nested' | 'nested-selectable' = 'default'
export let indent: boolean = false
export let folder: boolean = false
export let isFold: boolean = false
export let empty: boolean = false
export let visible: boolean = false
export let level: number = 0
export let collapsed: boolean = getTreeCollapsed(_id)
export let collapsedPrefix: string = ''
export let collapsed: boolean = getTreeCollapsed(_id, collapsedPrefix)
export let highlighted: boolean = false
export let selected: boolean = false
export let bold: boolean = false
export let shouldTooltip: boolean = false
export let showMenu: boolean = false
export let showNotify: boolean = false
export let forciblyСollapsed: boolean = false
export let actions: (originalEvent?: MouseEvent) => Promise<Action[]> = async () => []
let hovered = false
let pressed: boolean = false
let inlineActions: Action[] = []
let popupMenuActions: Action[] = []
@@ -59,96 +65,132 @@
async function onMenuClick (ev: MouseEvent): Promise<void> {
// Read actual popup actions on open as visibility might have been changed
pressed = true
popupMenuActions = await actions().then((res) => res.filter((action) => action.inline !== true))
showPopup(Menu, { actions: popupMenuActions, ctx: _id }, ev.target as HTMLElement, () => {
hovered = false
pressed = false
})
hovered = true
}
async function onInlineClick (ev: MouseEvent, action: Action): Promise<void> {
action.action([], ev)
}
const dispatch = createEventDispatcher()
$: if (_id) collapsed = getTreeCollapsed(_id)
$: setTreeCollapsed(_id, collapsed)
</script>
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div
class="antiNav-element relative"
class:selected
class:hovered
class:parent
class:collapsed
style:padding-left={`${level > 0 ? level * 1.25 + 0.125 : indent ? 2.5 : 0.75}rem`}
on:click={() => {
collapsed = !collapsed
dispatch('click')
}}
>
{#if showNotify}
<div class="notify" />
{/if}
{#if icon && !node}
<div class="an-element__icon" class:folder>
<Icon {icon} {iconProps} size={iconSize} />
</div>
{/if}
<span class="an-element__label" class:title={node} class:bold>
{#if label}<Label {label} />{:else}{title}{/if}
</span>
{#if parent}
<div class="an-element__tool arrow hidden">
<IconChevronDown size={'small'} />
</div>
{/if}
<div class="an-element__grow" />
{#if inlineActions.length > 0}
{#each inlineActions as action}
<div class="an-element__tool" on:click|preventDefault|stopPropagation={(ev) => onInlineClick(ev, action)}>
<Icon icon={action.icon ?? ActionIcon} size={'small'} />
</div>
{/each}
{/if}
{#if popupMenuActions.length === 1 && popupMenuActions[0].icon}
<div id={_id} class="an-element__tool">
<ActionIcon
label={popupMenuActions[0].label}
icon={popupMenuActions[0].icon}
size={'small'}
action={async (ev) => {
void popupMenuActions[0].action(_id, ev)
}}
/>
</div>
{:else if popupMenuActions.length > 0}
<div class="an-element__tool" class:pressed={hovered} on:click|preventDefault|stopPropagation={onMenuClick}>
<IconMoreH size={'small'} />
</div>
{/if}
{#if notifications > 0 && collapsed}
<div class="an-element__counter">{notifications}</div>
{/if}
</div>
{#if parent && !collapsed}
<div class="antiNav-element__dropbox"><slot /></div>
{#if parent}
<NavGroup
{_id}
categoryName={_id ?? 'nav'}
{icon}
{folderIcon}
{iconProps}
{iconSize}
{label}
{title}
{highlighted}
{selected}
isOpen={!collapsed}
{collapsedPrefix}
{type}
{isFold}
{empty}
{visible}
{forciblyСollapsed}
{shouldTooltip}
showMenu={showMenu || pressed}
on:click
on:toggle={(ev) => {
if (ev.detail !== undefined) collapsed = !ev.detail
}}
>
<svelte:fragment slot="extra"><slot name="extra" /></svelte:fragment>
<svelte:fragment slot="tools">
{#if inlineActions.length > 0}
{#each inlineActions as action}
<ButtonIcon
icon={action.icon ?? ActionIcon}
size={'extra-small'}
kind={'tertiary'}
on:click={(ev) => onInlineClick(ev, action)}
/>
{/each}
{/if}
{#if popupMenuActions.length === 1 && popupMenuActions[0].icon}
<ButtonIcon
id={_id}
icon={popupMenuActions[0].icon}
size={'extra-small'}
kind={'tertiary'}
tooltip={{ label: popupMenuActions[0].label, direction: 'top' }}
on:click={async (ev) => {
void popupMenuActions[0].action(_id, ev)
}}
/>
{:else if popupMenuActions.length > 0}
<ButtonIcon icon={IconMoreH} size={'extra-small'} kind={'tertiary'} {pressed} on:click={onMenuClick} />
{/if}
</svelte:fragment>
<svelte:fragment slot="visible"><slot name="visible" /></svelte:fragment>
<slot />
</NavGroup>
{:else}
<NavItem
{_id}
{label}
{title}
{icon}
{folderIcon}
{iconProps}
{iconSize}
{selected}
{bold}
{indent}
isOpen={!collapsed}
{collapsedPrefix}
{isFold}
{empty}
{visible}
{forciblyСollapsed}
{level}
{shouldTooltip}
showMenu={showMenu || pressed}
on:click
>
<slot />
<svelte:fragment slot="extra"><slot name="extra" /></svelte:fragment>
<svelte:fragment slot="actions">
{#if $$slots.actions}<slot name="actions" />{/if}
{#if inlineActions.length > 0}
{#each inlineActions as action}
<ButtonIcon
icon={action.icon ?? ActionIcon}
size={'extra-small'}
kind={'tertiary'}
on:click={(ev) => onInlineClick(ev, action)}
/>
{/each}
{/if}
{#if popupMenuActions.length === 1 && popupMenuActions[0].icon}
<ButtonIcon
id={_id}
icon={popupMenuActions[0].icon}
size={'extra-small'}
kind={'tertiary'}
tooltip={{ label: popupMenuActions[0].label, direction: 'top' }}
on:click={async (ev) => {
void popupMenuActions[0].action(_id, ev)
}}
/>
{:else if popupMenuActions.length > 0}
<ButtonIcon icon={IconMoreH} size={'extra-small'} kind={'tertiary'} {pressed} on:click={onMenuClick} />
{/if}
</svelte:fragment>
<svelte:fragment slot="notify">
{#if $$slots.notify}<slot name="notify" />{/if}
</svelte:fragment>
<svelte:fragment slot="dropbox">
{#if isFold}<slot name="dropbox" />{/if}
</svelte:fragment>
<svelte:fragment slot="visible"><slot name="visible" /></svelte:fragment>
</NavItem>
{/if}
<style lang="scss">
.notify {
position: absolute;
top: 0.5rem;
left: 1.5rem;
height: 0.5rem;
width: 0.5rem;
background-color: var(--theme-inbox-notify);
border-radius: 50%;
}
</style>
@@ -19,31 +19,50 @@
import TreeElement from './TreeElement.svelte'
import { AnySvelteComponent } from '@hcengineering/ui'
export let _id: Ref<Doc>
export let _id: Ref<Doc> | undefined = undefined
export let icon: Asset | AnySvelteComponent | undefined = undefined
export let folderIcon: boolean = false
export let iconProps: Record<string, any> | undefined = undefined
export let iconSize: IconSize = 'small'
export let title: string
export let notifications = 0
export let actions: (originalEvent?: MouseEvent) => Promise<Action[]> = async () => []
export let selected: boolean = false
export let showMenu: boolean = false
export let empty: boolean = false
export let bold: boolean = false
export let indent: boolean = false
export let isFold: boolean = false
export let level: number = 0
export let shouldTooltip: boolean = false
export let showNotify: boolean = false
export let forciblyСollapsed: boolean = false
export let collapsedPrefix: string = ''
</script>
<TreeElement
{_id}
{icon}
{folderIcon}
{iconSize}
{iconProps}
{title}
{notifications}
{selected}
{actions}
{iconProps}
{bold}
{indent}
{showNotify}
collapsed
{shouldTooltip}
{isFold}
{empty}
{collapsedPrefix}
{level}
{showMenu}
{forciblyСollapsed}
on:click
/>
>
<svelte:fragment slot="extra"><slot name="extra" /></svelte:fragment>
<svelte:fragment slot="dropbox"><slot name="dropbox" /></svelte:fragment>
<svelte:fragment slot="visible"><slot name="visible" /></svelte:fragment>
</TreeElement>
@@ -22,13 +22,22 @@
export let title: string | undefined = undefined
export let label: IntlString | undefined = undefined
export let icon: Asset | AnySvelteComponent | undefined = undefined
export let folderIcon: boolean = false
export let iconProps: Record<string, any> | undefined = undefined
export let actions: () => Promise<Action[]> = async () => []
export let notifications: number = 0
export let parent: boolean = true
export let node: boolean = false
export let folder: boolean = false
export let type: 'default' | 'nested' | 'nested-selectable' = 'default'
export let isFold: boolean = false
export let empty: boolean = false
export let visible: boolean = false
export let collapsed: boolean = false
export let collapsedPrefix: string = ''
export let highlighted: boolean = false
export let selected: boolean = false
export let showMenu: boolean = false
export let shouldTooltip: boolean = false
export let forciblyСollapsed: boolean = false
</script>
<TreeElement
@@ -37,13 +46,24 @@
{label}
{iconProps}
{icon}
{folderIcon}
{notifications}
{collapsed}
bind:collapsed
{collapsedPrefix}
{actions}
{node}
{folder}
{type}
{isFold}
{empty}
{visible}
{parent}
{highlighted}
{selected}
{shouldTooltip}
{showMenu}
{forciblyСollapsed}
on:click
>
<slot />
<svelte:fragment slot="extra"><slot name="extra" /></svelte:fragment>
<svelte:fragment slot="visible"><slot name="visible" /></svelte:fragment>
</TreeElement>