<!-- // Copyright © 2021, 2023 Anticrm Platform Contributors. // // Licensed under the Eclipse Public License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. You may // obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // // See the License for the specific language governing permissions and // limitations under the License. --> <script lang="ts"> import type { Asset, IntlString } from '@hcengineering/platform' import { getEmbeddedLabel } from '@hcengineering/platform' import { Icon, Label, IconOpenedArrow, IconDown, AnySvelteComponent, IconSize, getTreeCollapsed, setTreeCollapsed, tooltip, IconFolderExpanded, IconFolderCollapsed } from '..' export let icon: Asset | AnySvelteComponent | undefined = undefined export let folderIcon: boolean = false export let iconProps: any | undefined = undefined export let iconSize: IconSize = 'small' export let label: IntlString | undefined = undefined export let title: string | undefined = undefined export let description: string | undefined = undefined export let type: 'type-link' | 'type-tag' | 'type-anchor-link' | 'type-object' = 'type-link' export let color: string | null = null export let count: number | null = null export let selected: boolean = false export let indent: boolean = false export let bold: boolean = false export let disabled: boolean = false export let isFold: boolean = false export let isOpen: boolean = false export let isSecondary: boolean = false export let withBackground: boolean = false export let showMenu: boolean = false export let shouldTooltip: boolean = false export let empty: boolean = false export let collapsedPrefix: string = '' export let visible: boolean = false export let forciblyСollapsed: boolean = false export let level: number = 0 export let _id: any = undefined let labelEl: HTMLSpanElement let labelWidth: number let levelReset: boolean = false let hovered: boolean = false $: showArrow = selected && (type === 'type-link' || type === 'type-object') $: if (!showMenu && levelReset && !hovered) levelReset = false $: isOpen = !getTreeCollapsed(_id, collapsedPrefix) $: setTreeCollapsed(_id, !isOpen, collapsedPrefix) $: visibleIcon = folderIcon ? (isOpen && !empty ? IconFolderExpanded : IconFolderCollapsed) : icon const mouseOver = () => { if (!hovered) { labelWidth = labelEl.getBoundingClientRect().width hovered = true } if (!levelReset && labelWidth < 16 && level > 0) levelReset = true } </script> <!-- svelte-ignore a11y-mouse-events-have-key-events --> <button class="hulyNavItem-container {type} {type === 'type-anchor-link' || isSecondary ? 'font-regular-12' : 'font-regular-14'}" class:selected class:bold class:indent class:disabled class:showMenu class:noActions={$$slots.actions === undefined} on:mouseover={mouseOver} on:mouseleave={() => { if (levelReset && !showMenu) levelReset = false hovered = false }} on:click={() => { if (selected && isFold) isOpen = !isOpen }} on:click on:contextmenu > {#if isFold} <button class="hulyNavItem-chevron" class:isOpen style:margin-left={`${(levelReset ? 0 : level) * 1.25}rem`} disabled={empty} on:click|stopPropagation={() => { if (!empty) isOpen = !isOpen }} > {#if !empty}<IconDown size={'x-small'} />{/if} </button> {/if} {#if visibleIcon || (type === 'type-tag' && color)} <div class="hulyNavItem-icon" class:withBackground class:w-auto={iconSize === 'x-small'}> {#if type !== 'type-tag' && visibleIcon} <Icon icon={visibleIcon} size={iconSize} {iconProps} /> {:else if type === 'type-tag'} <div style:background-color={color} class="hulyNavItem-icon__tag" /> {/if} </div> {/if} <span bind:this={labelEl} use:tooltip={shouldTooltip ? { label: label ?? getEmbeddedLabel(title ?? ''), direction: 'top' } : undefined} class="{description ? 'hulyNavItem-wideLabel' : 'hulyNavItem-label'} overflow-label" class:flex-grow={!(type === 'type-anchor-link')} style:color={type === 'type-tag' && selected ? color : null} > {#if description} <span class="hulyNavItem-label font-medium-12 mr-0-5"> {#if label}<Label {label} />{/if} {#if title}{title}{/if} <slot /> </span> {description} {:else} {#if label}<Label {label} />{/if} {#if title}{title}{/if} <slot /> {/if} </span> {#if $$slots.extra}<slot name="extra" />{/if} {#if showMenu || $$slots.actions} <div class="hulyNavItem-actions"> <slot name="actions" /> </div> {/if} {#if count !== null} <span class="hulyNavItem-count font-regular-12"> {count} </span> {/if} <slot name="notify" /> {#if showArrow} <div class="hulyNavItem-icon right"><IconOpenedArrow size={'small'} /></div> {/if} </button> {#if (isFold && (isOpen || (!isOpen && visible)) && !empty) || forciblyСollapsed} <div class="hulyNavItem-dropbox"> {#if (!isOpen && visible) || forciblyСollapsed} <slot name="visible" {isOpen} /> {:else} <slot name="dropbox" /> {/if} </div> {/if} <style lang="scss"> .hulyNavItem-container { overflow: hidden; display: flex; justify-content: stretch; align-items: center; padding: 0; min-width: 0; min-height: var(--global-small-Size); border: none; border-radius: var(--small-BorderRadius); outline: none; .hulyNavItem-chevron, .hulyNavItem-icon { display: flex; justify-content: center; align-items: center; flex-shrink: 0; } .hulyNavItem-chevron { margin: 0; margin-right: var(--spacing-0_75); padding: 0; width: 0.75rem; height: 0.75rem; color: var(--global-tertiary-TextColor); border: none; border-radius: var(--min-BorderRadius); outline: none; &:disabled { pointer-events: none; } } .hulyNavItem-icon { width: var(--global-min-Size); height: var(--global-min-Size); color: var(--global-primary-TextColor); &__tag { flex-shrink: 0; width: 0.625rem; height: 0.625rem; border-radius: var(--min-BorderRadius); } &.right { visibility: hidden; margin-left: var(--spacing-0_5); color: var(--global-accent-IconColor); } &:not(.right) { margin-right: var(--spacing-1); } &.withBackground { width: var(--global-extra-small-Size); height: var(--global-extra-small-Size); background: var(--global-ui-BackgroundColor); border: 1px solid var(--global-subtle-ui-BorderColor); border-radius: var(--extra-small-BorderRadius); } } .hulyNavItem-label, .hulyNavItem-wideLabel { text-align: left; color: var(--global-primary-TextColor); } .hulyNavItem-wideLabel { font-size: 0.875rem; } .hulyNavItem-actions { display: none; align-items: center; flex-shrink: 0; min-width: 0; min-height: 0; gap: var(--spacing-0_25); } .hulyNavItem-count { margin-left: var(--spacing-1); color: var(--global-tertiary-TextColor); } &:not(.selected) .hulyNavItem-count { margin-right: var(--spacing-1); } &:not(.selected):hover, &:not(.selected).showMenu { background-color: var(--global-ui-hover-highlight-BackgroundColor); } &.selected { cursor: auto; background-color: var(--global-ui-highlight-BackgroundColor); // &:not(.type-anchor-link) .hulyNavItem-label:not(.description) { // font-weight: 700; // } .hulyNavItem-count { color: var(--global-secondary-TextColor); } } // &.bold:not(.type-anchor-link) .hulyNavItem-label:not(.description) { // font-weight: 700; // } &.type-link { padding: 0 var(--spacing-0_5) 0 var(--spacing-1_25); &.selected { padding: 0 var(--spacing-0_75) 0 var(--spacing-1_25); &.indent { padding-left: var(--spacing-4); } .hulyNavItem-icon { color: var(--global-accent-TextColor); } .hulyNavItem-label:not(.description) { color: var(--global-accent-TextColor); } .hulyNavItem-icon.right { visibility: visible; } } } &.type-tag { padding: 0 var(--spacing-1_25); .hulyNavItem-icon { width: 0.75rem; margin-right: 0.625rem; } } &.type-object { padding: 0 var(--spacing-0_5) 0 var(--spacing-0_5); .hulyNavItem-icon { width: var(--global-extra-small-Size); height: var(--global-extra-small-Size); &:not(.right) { margin-right: var(--spacing-0_75); background-color: var(--global-ui-BackgroundColor); border-radius: var(--extra-small-BorderRadius); } } &.selected { .hulyNavItem-label:not(.description) { color: var(--global-accent-TextColor); } .hulyNavItem-icon { color: var(--global-accent-TextColor); &.right { visibility: visible; } } } } &.type-anchor-link { padding: 0 var(--spacing-1_5) 0 var(--spacing-1_25); width: fit-content; min-height: 1.75rem; .hulyNavItem-icon, .hulyNavItem-label { color: var(--global-secondary-TextColor); } .hulyNavItem-label { font-weight: 500; } &.selected .hulyNavItem-icon, &.selected .hulyNavItem-label { color: var(--global-primary-TextColor); } } &.indent { padding-left: var(--spacing-4); } &:hover .hulyNavItem-chevron:enabled { color: var(--global-secondary-TextColor); background-color: var(--button-tertiary-hover-BackgroundColor); } &:not(.noActions):hover, &:not(.noActions).showMenu { .hulyNavItem-actions { display: flex; } .hulyNavItem-icon.right { display: none; } } &.disabled { cursor: not-allowed; .hulyNavItem-icon { opacity: 0.5; } .hulyNavItem-label { color: rgb(var(--theme-caption-color) / 40%); } } } .hulyNavItem-dropbox { display: flex; flex-direction: column; min-width: 0; min-height: 0; } </style>