mirror of
https://github.com/hcengineering/platform.git
synced 2026-09-30 21:45:01 +02:00
Updated navigator layout (#5619)
Signed-off-by: Alexander Platov <alexander.platov@hardcoreeng.com>
This commit is contained in:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user