mirror of
https://github.com/hcengineering/platform.git
synced 2025-05-31 20:57:31 +00:00
Updated layout of the Chat navigator (#5537)
Signed-off-by: Alexander Platov <alexander.platov@hardcoreeng.com>
This commit is contained in:
parent
5584f2702b
commit
bf80ba02e5
@ -680,6 +680,7 @@ input.search {
|
||||
.w-min { width: min-content; }
|
||||
.w-max { width: max-content; }
|
||||
.w-full { width: 100%; }
|
||||
.w-auto { width: auto !important; }
|
||||
.w-2 { width: .5rem; }
|
||||
.w-4 { width: 1rem; }
|
||||
.w-6 { width: 1.5rem; }
|
||||
|
@ -399,6 +399,17 @@
|
||||
}
|
||||
}
|
||||
|
||||
&.small .hulyAccordionItem-header__chevron {
|
||||
margin: 0 0.125rem;
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
&.medium .hulyAccordionItem-header__chevron {
|
||||
margin-right: var(--spacing-0_75);
|
||||
background-color: var(--global-ui-BackgroundColor);
|
||||
border: 1px solid var(--global-subtle-ui-BorderColor);
|
||||
}
|
||||
&.small,
|
||||
&.medium {
|
||||
padding: var(--spacing-1) var(--spacing-2);
|
||||
@ -413,11 +424,6 @@
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.hulyAccordionItem-header__chevron {
|
||||
background-color: var(--global-ui-BackgroundColor);
|
||||
border: 1px solid var(--global-subtle-ui-BorderColor);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.hulyAccordionItem-header__label {
|
||||
color: var(--global-primary-TextColor);
|
||||
@ -516,10 +522,10 @@
|
||||
color: var(--global-secondary-TextColor);
|
||||
}
|
||||
}
|
||||
&:hover .hulyAccordionItem-header__chevron {
|
||||
color: var(--button-subtle-IconColor);
|
||||
background-color: var(--global-ui-hover-BackgroundColor);
|
||||
}
|
||||
}
|
||||
&:hover .hulyAccordionItem-header__chevron {
|
||||
color: var(--button-subtle-IconColor);
|
||||
background-color: var(--global-ui-hover-BackgroundColor);
|
||||
}
|
||||
|
||||
&.isOpen {
|
||||
@ -533,13 +539,11 @@
|
||||
&.medium.bottomSpace + .hulyAccordionItem-content {
|
||||
padding-bottom: var(--spacing-2);
|
||||
}
|
||||
&.large {
|
||||
.hulyAccordionItem-header__chevron > * {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
&.bottomSpace + .hulyAccordionItem-content {
|
||||
padding-bottom: var(--spacing-2);
|
||||
}
|
||||
&.large.bottomSpace + .hulyAccordionItem-content {
|
||||
padding-bottom: var(--spacing-2);
|
||||
}
|
||||
&:is(.small, .large) .hulyAccordionItem-header__chevron > * {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,14 @@
|
||||
border-radius: var(--small-focus-BorderRadius);
|
||||
}
|
||||
}
|
||||
.hulyNavPanel-container a.noUnderline {
|
||||
display: inline-flex;
|
||||
flex-shrink: 0;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
|
||||
button.type-link { width: 100%; }
|
||||
}
|
||||
.hulyNavPanel-container .hulyNavItem-container,
|
||||
.hulyNavPanel-container .hulyTaskNavLink-container {
|
||||
margin: 0 0.75rem;
|
||||
|
@ -29,6 +29,7 @@
|
||||
export let placeholder: IntlString = plugin.string.EditBoxPlaceholder
|
||||
export let placeholderParam: any | undefined = undefined
|
||||
export let autoFocus: boolean = false
|
||||
export let kind: 'ghost' | 'secondary' = 'ghost'
|
||||
export let size: 'small' | 'medium' | 'large' = 'medium'
|
||||
export let loading = false
|
||||
|
||||
@ -52,7 +53,7 @@
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<div
|
||||
class="flex-between editbox {size}"
|
||||
class="flex-between editbox {kind} {size}"
|
||||
style={width != null ? 'width: ' + width : ''}
|
||||
on:click={() => {
|
||||
textHTML.focus()
|
||||
@ -116,5 +117,12 @@
|
||||
color: var(--theme-dark-color);
|
||||
}
|
||||
}
|
||||
|
||||
&.secondary {
|
||||
padding: var(--spacing-1);
|
||||
background-color: var(--theme-button-default);
|
||||
border: 1px solid var(--theme-button-border);
|
||||
border-radius: var(--small-BorderRadius);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -13,29 +13,56 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import type { IntlString } from '@hcengineering/platform'
|
||||
import type { AnyComponent } from '..'
|
||||
import { Label, Component } from '..'
|
||||
import { showPopup, Menu, Action, Label, Component, IconOpenedArrow } from '..'
|
||||
|
||||
export let label: IntlString | undefined = undefined
|
||||
export let title: string | undefined = undefined
|
||||
export let categoryName: string
|
||||
export let tools: AnyComponent | undefined = undefined
|
||||
export let isOpen: boolean = true
|
||||
export let isFold: boolean = false
|
||||
export let selected: boolean = false
|
||||
export let second: boolean = false
|
||||
export let actions: Action[] = []
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
$: id = `navGroup-${categoryName}`
|
||||
|
||||
const toggle = (): void => {
|
||||
isOpen = !isOpen
|
||||
dispatch('toggle', isOpen)
|
||||
}
|
||||
|
||||
function handleMenuClicked (ev: MouseEvent): void {
|
||||
if (actions.length === 0) return
|
||||
showPopup(Menu, { actions }, ev.target as HTMLElement)
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="hulyAccordionItem-container" class:second>
|
||||
<button class="hulyAccordionItem-header nav small" class:isOpen class:selected on:click={() => (isOpen = !isOpen)}>
|
||||
<button class="hulyAccordionItem-header nav small" class:isOpen class:selected on:click={toggle}>
|
||||
{#if isFold}
|
||||
<button class="hulyAccordionItem-header__chevron" class:collapsed={!isOpen}>
|
||||
<IconOpenedArrow size={'small'} />
|
||||
</button>
|
||||
{/if}
|
||||
<div class="hulyAccordionItem-header__label-wrapper font-medium-12">
|
||||
<span class="hulyAccordionItem-header__label">
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<span
|
||||
class="hulyAccordionItem-header__label"
|
||||
class:cursor-default={actions.length === 0}
|
||||
on:click|stopPropagation={handleMenuClicked}
|
||||
>
|
||||
{#if label}<Label {label} />{/if}
|
||||
{#if title}{title}{/if}
|
||||
</span>
|
||||
</div>
|
||||
{#if isFold}<div class="flex-grow" />{/if}
|
||||
{#if tools || $$slots.tools}
|
||||
<div class="hulyAccordionItem-header__tools">
|
||||
{#if tools}
|
||||
|
@ -14,50 +14,74 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import type { Asset, IntlString } from '@hcengineering/platform'
|
||||
import { Icon, Label, IconOpenedArrow, Fold } from '..'
|
||||
import { Icon, Label, IconOpenedArrow, Fold, AnySvelteComponent, IconSize } from '..'
|
||||
|
||||
export let icon: Asset | undefined = undefined
|
||||
export let icon: Asset | AnySvelteComponent | undefined = undefined
|
||||
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 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 empty: boolean = false
|
||||
export let level: number = 1
|
||||
</script>
|
||||
|
||||
<button
|
||||
class="hulyNavItem-container {type} {type === 'type-anchor-link' ? 'font-regular-12' : 'font-regular-14'}"
|
||||
class="hulyNavItem-container {type} {type === 'type-anchor-link' || isSecondary
|
||||
? 'font-regular-12'
|
||||
: 'font-regular-14'}"
|
||||
class:fold={isFold}
|
||||
class:selected
|
||||
on:click|stopPropagation
|
||||
on:contextmenu|preventDefault|stopPropagation
|
||||
class:showMenu
|
||||
on:click
|
||||
on:contextmenu
|
||||
>
|
||||
{#if isFold}
|
||||
<Fold {isOpen} {empty} {level} />
|
||||
{/if}
|
||||
{#if icon || (type === 'type-tag' && color)}
|
||||
<div class="hulyNavItem-icon">
|
||||
<div class="hulyNavItem-icon" class:withBackground class:w-auto={iconSize === 'x-small'}>
|
||||
{#if type !== 'type-tag' && icon}
|
||||
<Icon {icon} size={'small'} />
|
||||
<Icon {icon} size={iconSize} {iconProps} />
|
||||
{:else if type === 'type-tag'}
|
||||
<div style:background-color={color} class="hulyNavItem-icon__tag" />
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
<span class="hulyNavItem-label" style:color={type === 'type-tag' && selected ? color : null}>
|
||||
<span
|
||||
class="hulyNavItem-label"
|
||||
class:font-medium-12={description}
|
||||
class:flex-grow={!(type === 'type-anchor-link' || description)}
|
||||
style:color={type === 'type-tag' && selected ? color : null}
|
||||
>
|
||||
{#if label}<Label {label} />{/if}
|
||||
{#if title}{title}{/if}
|
||||
<slot />
|
||||
</span>
|
||||
{#if description}
|
||||
<span class="hulyNavItem-label description flex-grow">{description}</span>
|
||||
{/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}
|
||||
{#if selected && type === 'type-link'}
|
||||
<slot name="notify" />
|
||||
{#if selected && (type === 'type-link' || type === 'type-object')}
|
||||
<div class="hulyNavItem-icon right"><IconOpenedArrow size={'small'} /></div>
|
||||
{/if}
|
||||
</button>
|
||||
@ -68,9 +92,10 @@
|
||||
justify-content: stretch;
|
||||
align-items: center;
|
||||
padding: 0;
|
||||
min-height: 2rem;
|
||||
min-width: 0;
|
||||
min-height: var(--global-small-Size);
|
||||
border: none;
|
||||
border-radius: 0.375rem;
|
||||
border-radius: var(--small-BorderRadius);
|
||||
outline: none;
|
||||
|
||||
.hulyNavItem-icon {
|
||||
@ -78,8 +103,8 @@
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
width: var(--global-min-Size);
|
||||
height: var(--global-min-Size);
|
||||
color: var(--global-primary-TextColor);
|
||||
|
||||
&__tag {
|
||||
@ -90,11 +115,18 @@
|
||||
}
|
||||
&.right {
|
||||
visibility: hidden;
|
||||
margin-left: 0.5rem;
|
||||
margin-left: var(--spacing-0_5);
|
||||
color: var(--global-accent-IconColor);
|
||||
}
|
||||
&:not(.right) {
|
||||
margin-right: 0.5rem;
|
||||
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 {
|
||||
@ -105,19 +137,39 @@
|
||||
text-align: left;
|
||||
min-width: 0;
|
||||
color: var(--global-primary-TextColor);
|
||||
|
||||
&.description {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
.hulyNavItem-label + .hulyNavItem-label {
|
||||
margin-left: var(--spacing-0_5);
|
||||
}
|
||||
.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: 0.5rem;
|
||||
margin-left: var(--spacing-1);
|
||||
color: var(--global-tertiary-TextColor);
|
||||
}
|
||||
&:not(.selected):hover {
|
||||
&: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(.type-anchor-link) .hulyNavItem-label:not(.description) {
|
||||
font-weight: 700;
|
||||
}
|
||||
.hulyNavItem-count {
|
||||
@ -126,22 +178,19 @@
|
||||
}
|
||||
|
||||
&.type-link {
|
||||
padding: 0 0.625rem;
|
||||
padding: 0 var(--spacing-1_25);
|
||||
|
||||
.hulyNavItem-label {
|
||||
flex-grow: 1;
|
||||
}
|
||||
&.selected {
|
||||
&:not(.fold) {
|
||||
padding: 0 0.375rem 0 0.625rem;
|
||||
padding: 0 var(--spacing-0_75) 0 var(--spacing-1_25);
|
||||
}
|
||||
&.fold {
|
||||
padding: 0 0.375rem 0 0.25rem;
|
||||
padding: 0 var(--spacing-0_75) 0 var(--spacing-0_5);
|
||||
}
|
||||
.hulyNavItem-icon {
|
||||
color: var(--global-accent-TextColor);
|
||||
}
|
||||
.hulyNavItem-label {
|
||||
.hulyNavItem-label:not(.description) {
|
||||
color: var(--global-accent-TextColor);
|
||||
}
|
||||
.hulyNavItem-icon.right {
|
||||
@ -150,34 +199,36 @@
|
||||
}
|
||||
}
|
||||
&.type-tag {
|
||||
padding: 0 0.625rem;
|
||||
|
||||
.hulyNavItem-label {
|
||||
flex-grow: 1;
|
||||
}
|
||||
padding: 0 var(--spacing-1_25);
|
||||
}
|
||||
&.type-object {
|
||||
padding: 0 0.625rem 0 0.25rem;
|
||||
padding: 0 var(--spacing-0_5) 0 var(--spacing-0_5);
|
||||
|
||||
.hulyNavItem-icon {
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
background-color: var(--global-ui-BackgroundColor);
|
||||
border-radius: 0.25rem;
|
||||
width: var(--global-extra-small-Size);
|
||||
height: var(--global-extra-small-Size);
|
||||
|
||||
&:not(.right) {
|
||||
margin-right: 0.375rem;
|
||||
margin-right: var(--spacing-0_75);
|
||||
background-color: var(--global-ui-BackgroundColor);
|
||||
border-radius: var(--extra-small-BorderRadius);
|
||||
}
|
||||
}
|
||||
.hulyNavItem-label {
|
||||
flex-grow: 1;
|
||||
}
|
||||
&.selected .hulyNavItem-label {
|
||||
color: var(--global-accent-TextColor);
|
||||
&.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 0.75rem 0 0.625rem;
|
||||
padding: 0 var(--spacing-1_5) 0 var(--spacing-1_25);
|
||||
width: fit-content;
|
||||
min-height: 1.75rem;
|
||||
|
||||
@ -194,11 +245,16 @@
|
||||
}
|
||||
}
|
||||
&.fold {
|
||||
padding-left: 0.25rem;
|
||||
padding-left: var(--spacing-0_5);
|
||||
|
||||
:global(.hulyFold-container) {
|
||||
margin-right: 0.375rem;
|
||||
margin-right: var(--spacing-0_75);
|
||||
}
|
||||
}
|
||||
|
||||
&:hover .hulyNavItem-actions,
|
||||
&.showMenu .hulyNavItem-actions {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
export let value: string = ''
|
||||
export let width: string = '12rem'
|
||||
export let kind: 'ghost' | 'secondary' = 'ghost'
|
||||
|
||||
$: _search = value
|
||||
const dispatch = createEventDispatcher()
|
||||
@ -27,6 +28,7 @@
|
||||
icon={IconSearch}
|
||||
{width}
|
||||
placeholder={plugin.string.Search}
|
||||
{kind}
|
||||
bind:value={_search}
|
||||
on:change={() => {
|
||||
restartTimer()
|
||||
|
@ -133,7 +133,7 @@
|
||||
<div class="flex-row-top h-full">
|
||||
{#if visibleNav}
|
||||
<div class="antiPanel-navigator {appsDirection === 'horizontal' ? 'portrait' : 'landscape'}">
|
||||
<div class="antiPanel-wrap__content">
|
||||
<div class="antiPanel-wrap__content hulyNavPanel-container">
|
||||
<ChatNavigator objectId={selectedData?._id} {object} {currentSpecial} on:select={handleChannelSelected} />
|
||||
</div>
|
||||
<Separator name="chat" float={navFloat ? 'navigator' : true} index={0} />
|
||||
|
@ -36,6 +36,7 @@
|
||||
export let context: DocNotifyContext | undefined
|
||||
export let item: ChatNavItemModel
|
||||
export let isSelected = false
|
||||
export let type: 'type-link' | 'type-tag' | 'type-anchor-link' | 'type-object' = 'type-link'
|
||||
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
@ -44,7 +45,7 @@
|
||||
|
||||
let notifications: InboxNotification[] = []
|
||||
|
||||
let notificationsCount = 0
|
||||
let count: number | null = null
|
||||
let actions: Action[] = []
|
||||
|
||||
notificationClient.inboxNotificationsByContext.subscribe((res) => {
|
||||
@ -60,7 +61,7 @@
|
||||
})
|
||||
|
||||
$: void getNotificationsCount(context, notifications).then((res) => {
|
||||
notificationsCount = res
|
||||
count = res === 0 ? null : res
|
||||
})
|
||||
|
||||
$: void getChannelActions(context, item.object).then((res) => {
|
||||
@ -121,10 +122,11 @@
|
||||
iconSize={item.iconSize}
|
||||
{isSelected}
|
||||
iconProps={{ ...item.iconProps, value: item.object }}
|
||||
{notificationsCount}
|
||||
{count}
|
||||
title={item.title}
|
||||
description={item.description}
|
||||
{actions}
|
||||
{type}
|
||||
on:click={() => {
|
||||
dispatch('select', { object: item.object })
|
||||
}}
|
||||
|
@ -16,7 +16,7 @@
|
||||
import { Doc, Ref } from '@hcengineering/core'
|
||||
import { DocNotifyContext } from '@hcengineering/notification'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import ui, { Action, AnySvelteComponent, IconSize, ModernButton } from '@hcengineering/ui'
|
||||
import ui, { Action, AnySvelteComponent, IconSize, ModernButton, NavGroup } from '@hcengineering/ui'
|
||||
import { getDocTitle } from '@hcengineering/view-resources'
|
||||
import contact from '@hcengineering/contact'
|
||||
import { getResource, translate } from '@hcengineering/platform'
|
||||
@ -26,7 +26,6 @@
|
||||
import chunter from '../../../plugin'
|
||||
import { ChatNavItemModel } from '../types'
|
||||
import { getObjectIcon, getChannelName } from '../../../utils'
|
||||
import ChatSectionHeader from './ChatSectionHeader.svelte'
|
||||
import { navigatorStateStore, toggleSections } from '../utils'
|
||||
|
||||
export let id: string
|
||||
@ -134,19 +133,21 @@
|
||||
</script>
|
||||
|
||||
{#if items.length > 0 && contexts.length > 0}
|
||||
<div class="section">
|
||||
<ChatSectionHeader
|
||||
{header}
|
||||
{actions}
|
||||
{isCollapsed}
|
||||
on:collapse={() => {
|
||||
toggleSections(id)
|
||||
}}
|
||||
/>
|
||||
<NavGroup
|
||||
title={header}
|
||||
categoryName={id}
|
||||
{actions}
|
||||
isOpen={!isCollapsed}
|
||||
isFold
|
||||
second
|
||||
on:toggle={() => {
|
||||
toggleSections(id)
|
||||
}}
|
||||
>
|
||||
{#if !isCollapsed}
|
||||
{#each visibleItems as item (item.id)}
|
||||
{@const context = contexts.find(({ attachedTo }) => attachedTo === item.id)}
|
||||
<ChatNavItem {context} isSelected={objectId === item.id} {item} on:select />
|
||||
<ChatNavItem {context} isSelected={objectId === item.id} {item} type={'type-object'} on:select />
|
||||
{/each}
|
||||
{#if canShowMore}
|
||||
<div class="showMore">
|
||||
@ -166,18 +167,10 @@
|
||||
<ChatNavItem {context} isSelected {item} on:select />
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
</NavGroup>
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
.section {
|
||||
display: flex;
|
||||
gap: 0.125rem;
|
||||
flex-direction: column;
|
||||
padding: 0 var(--spacing-1) var(--spacing-1_5) var(--spacing-1);
|
||||
border-bottom: 1px solid var(--global-surface-02-BorderColor);
|
||||
}
|
||||
|
||||
.showMore {
|
||||
margin-top: var(--spacing-1);
|
||||
font-size: 0.75rem;
|
||||
|
@ -70,10 +70,10 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="header">
|
||||
<div class="overflow-label">
|
||||
<div class="hulyNavPanel-header">
|
||||
<span class="overflow-label">
|
||||
<Label label={chunter.string.Chat} />
|
||||
</div>
|
||||
</span>
|
||||
<ButtonIcon icon={IconAdd} kind={'primary'} size={'small'} on:click={addButtonClicked} />
|
||||
</div>
|
||||
|
||||
@ -94,6 +94,7 @@
|
||||
<SearchEdit
|
||||
value={searchValue}
|
||||
width="auto"
|
||||
kind={'secondary'}
|
||||
on:change={(ev) => {
|
||||
userSearch.set(ev.detail)
|
||||
|
||||
@ -111,17 +112,8 @@
|
||||
</Scroller>
|
||||
|
||||
<style lang="scss">
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: var(--spacing-1_5) var(--spacing-1_5) var(--spacing-3) var(--spacing-2_5);
|
||||
font-weight: 700;
|
||||
font-size: 1.25rem;
|
||||
color: var(--global-primary-TextColor);
|
||||
}
|
||||
.search {
|
||||
padding: var(--spacing-1_5);
|
||||
padding: var(--spacing-3) var(--spacing-1_5) var(--spacing-1_5);
|
||||
border-bottom: 1px solid var(--theme-navpanel-divider);
|
||||
}
|
||||
</style>
|
||||
|
@ -1,77 +0,0 @@
|
||||
<!--
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// 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 { Action, ButtonIcon, IconDropdownDown, IconDropdownRight, Menu, showPopup } from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
|
||||
export let header: string
|
||||
export let actions: Action[] = []
|
||||
export let isCollapsed = false
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
function handleMenuClicked (ev: MouseEvent): void {
|
||||
if (actions.length === 0) {
|
||||
return
|
||||
}
|
||||
showPopup(Menu, { actions }, ev.target as HTMLElement)
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="root">
|
||||
<ButtonIcon
|
||||
size="extra-small"
|
||||
kind="tertiary"
|
||||
inheritColor
|
||||
icon={isCollapsed ? IconDropdownRight : IconDropdownDown}
|
||||
on:click={() => dispatch('collapse')}
|
||||
/>
|
||||
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<div class="header uppercase" class:disabled={actions.length === 0} on:click={handleMenuClicked}>
|
||||
{header}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.root {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
border-radius: 0.375rem;
|
||||
padding-right: 0.25rem;
|
||||
color: var(--global-secondary-TextColor);
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.header {
|
||||
cursor: pointer;
|
||||
padding: var(--spacing-0_5) var(--spacing-0_75);
|
||||
border-radius: var(--extra-small-BorderRadius);
|
||||
background-color: var(--global-ui-BackgroundColor);
|
||||
width: fit-content;
|
||||
margin: 0.5rem 0.25rem;
|
||||
|
||||
&.disabled {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
&:hover:not(.disabled) {
|
||||
color: var(--global-primary-TextColor);
|
||||
background-color: var(--global-ui-highlight-BackgroundColor);
|
||||
}
|
||||
}
|
||||
</style>
|
@ -27,15 +27,16 @@
|
||||
|
||||
export let special: SpecialNavModel
|
||||
export let currentSpecial: SpecialNavModel | undefined = undefined
|
||||
export let type: 'type-link' | 'type-tag' | 'type-anchor-link' | 'type-object' = 'type-link'
|
||||
|
||||
const notificationsClient = InboxNotificationsClientImpl.getClient()
|
||||
const notificationsByContextStore = notificationsClient.inboxNotificationsByContext
|
||||
|
||||
let notificationsCount = 0
|
||||
let count: number | null = null
|
||||
let elementsCount = 0
|
||||
|
||||
$: void getNotificationsCount(special, $notificationsByContextStore).then((res) => {
|
||||
notificationsCount = res
|
||||
count = res === 0 ? null : res
|
||||
})
|
||||
$: elementsCount = getElementsCount(special, $savedMessagesStore, $savedAttachmentsStore)
|
||||
|
||||
@ -65,17 +66,13 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="mr-2 ml-2">
|
||||
<NavItem
|
||||
id={special.id}
|
||||
icon={special.icon}
|
||||
iconPadding="0 0 0 0.375rem"
|
||||
iconSize="small"
|
||||
padding="var(--spacing-1) var(--spacing-0_5)"
|
||||
intlTitle={special.label}
|
||||
withIconBackground={false}
|
||||
{notificationsCount}
|
||||
{elementsCount}
|
||||
isSelected={special.id === currentSpecial?.id}
|
||||
/>
|
||||
</div>
|
||||
<NavItem
|
||||
id={special.id}
|
||||
icon={special.icon}
|
||||
intlTitle={special.label}
|
||||
withIconBackground={false}
|
||||
{count}
|
||||
{elementsCount}
|
||||
isSelected={special.id === currentSpecial?.id}
|
||||
{type}
|
||||
/>
|
||||
|
@ -14,16 +14,15 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import {
|
||||
Action,
|
||||
type Action,
|
||||
ActionIcon,
|
||||
AnySvelteComponent,
|
||||
type AnySvelteComponent,
|
||||
Icon,
|
||||
IconMoreH,
|
||||
IconRight,
|
||||
IconSize,
|
||||
Label,
|
||||
Menu,
|
||||
showPopup
|
||||
showPopup,
|
||||
NavItem
|
||||
} from '@hcengineering/ui'
|
||||
import { NotifyMarker } from '@hcengineering/notification-resources'
|
||||
import { Asset, IntlString } from '@hcengineering/platform'
|
||||
@ -31,18 +30,17 @@
|
||||
export let id: string
|
||||
export let icon: Asset | AnySvelteComponent | undefined
|
||||
export let iconProps: any | undefined = undefined
|
||||
export let iconSize: IconSize = 'x-small'
|
||||
export let iconPadding: string | null = null
|
||||
export let padding: string | null = null
|
||||
export let withIconBackground = true
|
||||
export let isSelected = false
|
||||
export let isSecondary = false
|
||||
export let notificationsCount = 0
|
||||
export let iconSize: IconSize = 'small'
|
||||
export let withIconBackground: boolean = true
|
||||
export let isSelected: boolean = false
|
||||
export let isSecondary: boolean = false
|
||||
export let count: number | null = null
|
||||
export let title: string | undefined = undefined
|
||||
export let intlTitle: IntlString | undefined = undefined
|
||||
export let description: string | undefined = undefined
|
||||
export let actions: Action[] = []
|
||||
export let elementsCount = 0
|
||||
export let elementsCount: number = 0
|
||||
export let type: 'type-link' | 'type-tag' | 'type-anchor-link' | 'type-object' = 'type-link'
|
||||
|
||||
let menuOpened = false
|
||||
let inlineActions: Action[] = []
|
||||
@ -63,173 +61,64 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<div class="root" class:pressed={menuOpened || isSelected} style:padding on:click>
|
||||
{#if icon}
|
||||
<div class="icon" class:withBackground={withIconBackground} style:padding={iconPadding}>
|
||||
<Icon
|
||||
{icon}
|
||||
{iconProps}
|
||||
size={iconSize}
|
||||
fill={isSelected ? 'var(--theme-link-color)' : 'var(--global-primary-TextColor)'}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="content">
|
||||
<span
|
||||
class="label overflow-label"
|
||||
class:secondary={isSecondary}
|
||||
class:extraBold={notificationsCount > 0}
|
||||
class:selected={isSelected}
|
||||
style="flex-shrink: 0"
|
||||
>
|
||||
{#if title}
|
||||
{title}
|
||||
{:else if intlTitle}
|
||||
<Label label={intlTitle} />
|
||||
{/if}
|
||||
{#if description}
|
||||
<span class="label overflow-label ml-1-5" title={description}>
|
||||
{description}
|
||||
</span>
|
||||
{/if}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="grower" />
|
||||
|
||||
<div class="controls">
|
||||
<div class="flex-center">
|
||||
{#each inlineActions as action}
|
||||
<div
|
||||
class="action"
|
||||
class:pressed={menuOpened}
|
||||
on:click|preventDefault|stopPropagation={(ev) => handleInlineActionClicked(ev, action)}
|
||||
>
|
||||
<Icon icon={action.icon ?? ActionIcon} size="small" />
|
||||
</div>
|
||||
{/each}
|
||||
{#if menuActions.length > 0}
|
||||
<div class="action" class:pressed={menuOpened} on:click|preventDefault|stopPropagation={handleMenuClicked}>
|
||||
<IconMoreH size={'small'} />
|
||||
</div>
|
||||
{/if}
|
||||
{#if elementsCount > 0}
|
||||
<div class="ml-2" />
|
||||
<div class="elementsCounter">{elementsCount}</div>
|
||||
{/if}
|
||||
{#if notificationsCount > 0}
|
||||
<div class="ml-2" />
|
||||
<NotifyMarker count={notificationsCount} />
|
||||
{/if}
|
||||
{#if isSelected}
|
||||
<div class="ml-2" />
|
||||
<IconRight size="small" fill="var(--theme-link-color)" />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<NavItem
|
||||
{icon}
|
||||
{iconProps}
|
||||
{iconSize}
|
||||
label={intlTitle}
|
||||
{title}
|
||||
{description}
|
||||
selected={isSelected}
|
||||
{isSecondary}
|
||||
count={elementsCount > 0 ? elementsCount : null}
|
||||
{type}
|
||||
withBackground={withIconBackground}
|
||||
showMenu={menuOpened}
|
||||
on:click
|
||||
on:contextmenu
|
||||
>
|
||||
<svelte:fragment slot="actions">
|
||||
{#each inlineActions as action}
|
||||
<button
|
||||
class="action"
|
||||
class:pressed={menuOpened}
|
||||
on:click|preventDefault|stopPropagation={(ev) => handleInlineActionClicked(ev, action)}
|
||||
>
|
||||
<Icon icon={action.icon ?? ActionIcon} size="small" />
|
||||
</button>
|
||||
{/each}
|
||||
{#if menuActions.length > 0}
|
||||
<button class="action" class:pressed={menuOpened} on:click|preventDefault|stopPropagation={handleMenuClicked}>
|
||||
<IconMoreH size={'small'} />
|
||||
</button>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="notify">
|
||||
{#if count != null && count > 0}
|
||||
<div class="antiHSpacer" />
|
||||
<NotifyMarker {count} />
|
||||
<div class="antiHSpacer" />
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
</NavItem>
|
||||
|
||||
<style lang="scss">
|
||||
.root {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
padding: var(--spacing-0_5) var(--spacing-0_5);
|
||||
border-radius: var(--small-BorderRadius);
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--global-ui-highlight-BackgroundColor);
|
||||
|
||||
.action {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
|
||||
&.pressed {
|
||||
background-color: var(--global-ui-highlight-BackgroundColor);
|
||||
}
|
||||
|
||||
.action {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0.25rem;
|
||||
border-radius: 0.25rem;
|
||||
flex-shrink: 0;
|
||||
margin-left: 0.5rem;
|
||||
visibility: hidden;
|
||||
|
||||
&:hover,
|
||||
&.pressed {
|
||||
visibility: visible;
|
||||
color: var(--global-primary-TextColor);
|
||||
background-color: var(--global-ui-highlight-BackgroundColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.grower {
|
||||
flex-grow: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.icon {
|
||||
.action {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
margin-right: 0.375rem;
|
||||
color: var(--global-primary-TextColor);
|
||||
|
||||
&.withBackground {
|
||||
background: var(--global-ui-BackgroundColor);
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
border-radius: var(--extra-small-BorderRadius);
|
||||
border: 1px solid var(--global-subtle-ui-BorderColor);
|
||||
}
|
||||
}
|
||||
|
||||
.label {
|
||||
font-size: 0.875rem;
|
||||
color: var(--global-primary-TextColor);
|
||||
font-weight: 400;
|
||||
|
||||
&.secondary {
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
&.extraBold {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
&.selected {
|
||||
color: var(--global-accent-TextColor);
|
||||
}
|
||||
}
|
||||
|
||||
.controls {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
justify-content: center;
|
||||
max-height: 3.75rem;
|
||||
}
|
||||
|
||||
.elementsCounter {
|
||||
margin-left: var(--spacing-1);
|
||||
padding: var(--spacing-0_5);
|
||||
color: var(--global-tertiary-TextColor);
|
||||
font-size: 0.75rem;
|
||||
border: none;
|
||||
border-radius: var(--extra-small-BorderRadius);
|
||||
outline: none;
|
||||
|
||||
&:hover,
|
||||
&.pressed {
|
||||
color: var(--global-primary-TextColor);
|
||||
background-color: var(--global-ui-highlight-BackgroundColor);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -34,6 +34,7 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
border-radius: 50%;
|
||||
background-color: var(--global-higlight-Color);
|
||||
color: var(--global-on-accent-TextColor);
|
||||
|
@ -7,7 +7,7 @@ export class ChunterPage {
|
||||
this.page = page
|
||||
}
|
||||
|
||||
readonly buttonChannelBrowser = (): Locator => this.page.locator('.header > button.type-button-icon')
|
||||
readonly buttonChannelBrowser = (): Locator => this.page.locator('.hulyNavPanel-header > button.type-button-icon')
|
||||
readonly buttonNewChannelHeader = (): Locator => this.page.getByRole('button', { name: 'New channel' })
|
||||
readonly inputNewChannelName = (): Locator => this.page.getByPlaceholder('New channel')
|
||||
readonly inputDescription = (): Locator => this.page.getByPlaceholder('Description (optional)')
|
||||
|
Loading…
Reference in New Issue
Block a user