Updated Header layout (#6298)

Signed-off-by: Alexander Platov <alexander.platov@hardcoreeng.com>
This commit is contained in:
Alexander Platov 2024-08-09 11:49:40 +07:00 committed by GitHub
parent 8af4a9b131
commit 71e2fc5453
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 173 additions and 47 deletions

View File

@ -19,7 +19,14 @@
import activity from '@hcengineering/activity'
import { Doc } from '@hcengineering/core'
import { Component, deviceOptionsStore as deviceInfo, Panel, Scroller, resizeObserver } from '@hcengineering/ui'
import {
Component,
deviceOptionsStore as deviceInfo,
Panel,
Scroller,
resizeObserver,
HeaderAdaptive
} from '@hcengineering/ui'
import type { ButtonItem } from '@hcengineering/ui'
import { getResource } from '@hcengineering/platform'
@ -47,7 +54,7 @@
export let selectedAside: string | boolean = customAside ? customAside[0].id : isAside
export let printHeader: boolean = true
export let printAside: boolean = false
export let adaptive: 'default' | 'freezeActions' | 'doubleRow' | 'disabled' = 'disabled'
export let adaptive: HeaderAdaptive = 'disabled'
export let hideBefore: boolean = false
export let hideSearch: boolean = true
export let hideActions: boolean = false

View File

@ -739,6 +739,7 @@ input.search {
.min-h-11 { min-height: 2.75rem; }
.min-h-12 { min-height: 3rem; }
.min-h-13 { min-height: 3.25rem; }
.min-h-14 { min-height: 3.5rem; }
.min-h-16 { min-height: 4rem; }
.min-h-30 { min-height: 7.5rem; }
.min-h-60 { min-height: 15rem; }

View File

@ -317,18 +317,14 @@
.hulyHeader-container {
display: flex;
align-items: center;
padding: 0 var(--spacing-2);
width: 100%;
min-width: 0;
min-height: var(--spacing-6_5);
&:not(.clearPadding) { padding: var(--spacing-1_5) var(--spacing-2); }
&.clearPadding {
padding: 0 var(--spacing-2);
& > .hulyHeader-row {
padding: 0;
min-height: var(--spacing-6_5);
}
&.clearPadding > .hulyHeader-row {
padding: 0;
min-height: var(--spacing-6_5);
}
&:not(.hideSeparator) {
border-bottom: 1px solid var(--theme-divider-color); // var(--global-surface-02-BorderColor);
@ -390,15 +386,15 @@
min-height: 0;
}
.hulyHeader-titleGroup {
flex-grow: 1;
user-select: text;
cursor: auto;
&.withDescription { flex-direction: column; }
&:not(.withDescription) {
align-items: center;
gap: var(--spacing-0_5);
}
&:not(.notGrow) { flex-grow: 1; }
}
.hulyHeader-buttonsGroup {
align-items: center;
@ -428,6 +424,7 @@
color: var(--global-secondary-TextColor);
}
}
.hulyHeader-spaceFiller { flex: 1 1; }
.hulyHotKey-item { margin-right: .625rem; }
&.doubleRow {

View File

@ -20,14 +20,15 @@
IconClose,
ButtonIcon,
deviceOptionsStore as deviceInfo,
resizeObserver
resizeObserver,
HeaderAdaptive
} from '..'
export let type: 'type-aside' | 'type-popup' | 'type-component' | 'type-panel' = 'type-component'
export let allowFullsize: boolean = false
export let hideSeparator: boolean = false
export let topIndent: boolean = false
export let adaptive: 'default' | 'freezeActions' | 'doubleRow' | 'disabled' = 'default'
export let adaptive: HeaderAdaptive = 'default'
export let hideBefore: boolean = false
export let hideDescription: boolean = false
export let hideSearch: boolean = false
@ -40,7 +41,15 @@
const dispatch = createEventDispatcher()
const closeButton: boolean = ['type-popup', 'type-aside'].some((v) => v === type)
let spaceFiller: HTMLElement
let doubleRow: boolean = false
let doubleExtra: boolean = false
let extraWidth: number = 0
let spaceWidth: number = 0
$: _doubleRow =
adaptive === 'doubleRow' ||
(adaptive !== 'disabled' && doubleRow) ||
(adaptive === 'autoExtra' && (doubleRow || doubleExtra))
onMount(() => {
if (closeButton) window.addEventListener('keydown', _close)
@ -65,13 +74,13 @@
else if (doubleRow && element.clientWidth > 768) doubleRow = false
}}
class="hulyHeader-container"
class:doubleRow={adaptive === 'doubleRow' || (adaptive !== 'disabled' && doubleRow)}
class:doubleRow={_doubleRow}
class:topIndent
class:clearPadding={$$slots.description}
class:hideSeparator
class:no-print={noPrint}
>
{#if adaptive === 'doubleRow' || (adaptive !== 'disabled' && doubleRow)}
{#if _doubleRow}
<div class="hulyHeader-row">
{#if allowFullsize}
<ButtonIcon
@ -90,7 +99,12 @@
{/if}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div class="hulyHeader-titleGroup" class:withDescription={$$slots.description && !hideDescription} on:click>
<div
class="hulyHeader-titleGroup"
class:withDescription={$$slots.description && !hideDescription}
class:notGrow={adaptive === 'autoExtra'}
on:click
>
{#if $$slots.description && !hideDescription}
<div class="hulyHeader-titleGroup"><slot /></div>
<div class="hulyHeader-titleGroup"><slot name="description" /></div>
@ -98,7 +112,19 @@
<slot />
{/if}
</div>
{#if $$slots.actions && !hideActions && (adaptive === 'freezeActions' || adaptive === 'doubleRow')}
{#if adaptive === 'autoExtra'}
<div
class="hulyHeader-spaceFiller"
bind:this={spaceFiller}
use:resizeObserver={(element) => {
if (spaceWidth !== element.clientWidth) spaceWidth = element.clientWidth
if (doubleExtra && element.clientWidth > extraWidth + 42) doubleExtra = false
}}
/>
{/if}
{#if $$slots.actions && !hideActions && (adaptive === 'freezeActions' || adaptive === 'doubleRow' || adaptive === 'autoExtra')}
<div class="hulyHeader-buttonsGroup actions no-print">
<slot name="actions" {doubleRow} />
</div>
@ -116,14 +142,20 @@
<slot name="search" {doubleRow} />
</div>
{/if}
{#if $$slots.actions && !hideActions && adaptive !== 'freezeActions' && adaptive !== 'doubleRow'}
<div class="hulyHeader-buttonsGroup actions">
<slot name="actions" {doubleRow} />
{#if $$slots.extra && !hideExtra && (adaptive === 'doubleRow' || adaptive === 'autoExtra')}
<div
class="hulyHeader-buttonsGroup extra"
class:overflow={overflowExtra}
use:resizeObserver={(element) => {
if (extraWidth !== element.clientWidth) extraWidth = element.clientWidth
}}
>
<slot name="extra" {doubleRow} />
</div>
{/if}
{#if $$slots.extra && !hideExtra && adaptive === 'doubleRow'}
<div class="hulyHeader-buttonsGroup extra" class:overflow={overflowExtra}>
<slot name="extra" {doubleRow} />
{#if $$slots.actions && !hideActions && !(adaptive === 'freezeActions' || adaptive === 'doubleRow' || adaptive === 'autoExtra')}
<div class="hulyHeader-buttonsGroup actions">
<slot name="actions" {doubleRow} />
</div>
{/if}
</div>
@ -145,7 +177,12 @@
{/if}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div class="hulyHeader-titleGroup" class:withDescription={$$slots.description && !hideDescription} on:click>
<div
class="hulyHeader-titleGroup"
class:withDescription={$$slots.description && !hideDescription}
class:notGrow={adaptive === 'autoExtra'}
on:click
>
{#if $$slots.description && !hideDescription}
<slot />
<slot name="description" />
@ -153,12 +190,35 @@
<slot />
{/if}
</div>
{#if adaptive === 'autoExtra'}
<div
class="hulyHeader-spaceFiller"
bind:this={spaceFiller}
use:resizeObserver={(element) => {
if (spaceWidth !== element.clientWidth) spaceWidth = element.clientWidth
if (!doubleExtra && element.clientWidth <= 16) doubleExtra = true
}}
/>
{/if}
{#if $$slots.search && !hideSearch}
<div class="hulyHeader-buttonsGroup search no-print">
<slot name="search" {doubleRow} />
</div>
{#if $$slots.actions && !hideActions}<div class="hulyHeader-divider no-print" />{/if}
{/if}
{#if $$slots.extra && !hideExtra}
<div
class="hulyHeader-buttonsGroup extra"
class:overflow={overflowExtra}
use:resizeObserver={(element) => {
if (extraWidth !== element.clientWidth) extraWidth = element.clientWidth
}}
>
<slot name="extra" {doubleRow} />
</div>
{/if}
{#if $$slots.actions && !hideActions}
<div class="hulyHeader-buttonsGroup actions no-print">
<slot name="actions" {doubleRow} />

View File

@ -24,7 +24,8 @@
Scroller,
panelSeparators,
ButtonItem,
Header
Header,
HeaderAdaptive
} from '../../'
import IconClose from './icons/Close.svelte'
import IconDetails from './icons/Details.svelte'
@ -47,7 +48,7 @@
export let selectedAside: string | boolean = customAside ? customAside[0].id : isAside
export let printHeader = true
export let printAside = false
export let adaptive: 'default' | 'freezeActions' | 'doubleRow' | 'disabled' = 'default'
export let adaptive: HeaderAdaptive = 'default'
export let hideBefore: boolean = false
export let hideSearch: boolean = true
export let hideActions: boolean = false

View File

@ -104,7 +104,7 @@
<div class="color" style:background-color={item.color} />
{/if}
{#if item.label || item.labelIntl}
<span class="flex-center overflow-label" class:ml-1-5={item.icon || item.color}>
<span class="overflow-label" class:ml-1-5={item.icon || item.color}>
{#if item.label}
{item.label}
{:else if item.labelIntl}

View File

@ -21,7 +21,7 @@
export let selected = 0
export let padding: string | undefined = undefined
export let noMargin: boolean = false
export let size: 'small' | 'medium' = 'medium'
export let size: 'small' | 'medium' | 'large' = 'medium'
</script>
<TabsControl {model} {padding} {noMargin} {size} bind:selected>

View File

@ -20,10 +20,10 @@
export let selected = 0
export let padding: string | undefined = undefined
export let noMargin: boolean = false
export let size: 'small' | 'medium' = 'medium'
export let size: 'small' | 'medium' | 'large' = 'medium'
</script>
<div class="flex-stretch tabs-container no-print" class:small={size === 'small'} style:padding class:noMargin>
<div class="flex-stretch tabs-container no-print {size}" style:padding class:noMargin>
{#each model as tab, i}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
@ -69,6 +69,13 @@
height: 3.25rem;
}
}
&.large {
height: 3.5rem;
.tab {
height: 3.5rem;
}
}
.tab {
height: 4.5rem;

View File

@ -34,6 +34,7 @@ export type {
ButtonKind,
ButtonSize,
ButtonItem,
HeaderAdaptive,
IconSize,
TabItem,
BreadcrumbItem,

View File

@ -190,6 +190,7 @@ export type EditStyle =
| 'default-large'
| 'ghost-large'
| 'modern-ghost-large'
export type HeaderAdaptive = 'default' | 'freezeActions' | 'autoExtra' | 'doubleRow' | 'disabled'
export interface ButtonItem {
id: string

View File

@ -22,7 +22,8 @@
IconDetails,
Label,
SearchInput,
Header
Header,
HeaderAdaptive
} from '@hcengineering/ui'
import { createEventDispatcher } from 'svelte'
import view from '@hcengineering/view'
@ -49,7 +50,7 @@
export let titleKind: 'default' | 'breadcrumbs' = 'default'
export let withFilters: boolean = false
export let filters: Ref<ActivityMessagesFilter>[] = []
export let adaptive: 'default' | 'freezeActions' | 'doubleRow' | 'disabled' = 'default'
export let adaptive: HeaderAdaptive = 'default'
export let hideActions: boolean = false
const client = getClient()

View File

@ -278,8 +278,9 @@
allowClose={withClose && !embedded}
{embedded}
printHeader={false}
adaptive={'doubleRow'}
adaptive={'autoExtra'}
overflowExtra
hideSearch
on:close
on:select={(ev) => rightPanelTabChanged(ev.detail)}
>
@ -413,7 +414,7 @@
{#if $editorMode === 'comparing'}
<DocumentDiffViewer />
{:else}
<Tabs model={tabs} bind:selected={selectedTab} size="small" padding="0 1.5rem" noMargin />
<Tabs model={tabs} bind:selected={selectedTab} size={'large'} padding="0 1.5rem" noMargin />
{/if}
</Collaboration>
<svelte:fragment slot="custom-attributes">

View File

@ -18,7 +18,7 @@
import { rightPanelTabChanged } from '../../../stores/editors/document'
</script>
<div class="header flex-between min-h-13 pl-4 pr-4 font-medium text-md bottom-divider">
<div class="header flex-between min-h-14 pl-4 pr-4 font-medium text-md bottom-divider">
<slot />
<Button
kind="ghost"

View File

@ -23,13 +23,28 @@
</script>
{#if issueUrl}
<CopyToClipboardButton icon={view.icon.CopyLink} title={tracker.string.CopyIssueUrl} text={issueUrl} />
<CopyToClipboardButton
icon={view.icon.CopyLink}
title={tracker.string.CopyIssueUrl}
text={issueUrl}
dataId={'btnCopyIssueUrl'}
/>
{/if}
{#if issueId}
<CopyToClipboardButton icon={view.icon.CopyId} title={tracker.string.CopyIssueId} text={issueId} />
<CopyToClipboardButton
icon={view.icon.CopyId}
title={tracker.string.CopyIssueId}
text={issueId}
dataId={'btnCopyIssueId'}
/>
{/if}
{#if issueBranch}
<CopyToClipboardButton icon={tracker.icon.CopyBranch} title={tracker.string.CopyIssueBranch} text={issueBranch} />
<CopyToClipboardButton
icon={tracker.icon.CopyBranch}
title={tracker.string.CopyIssueBranch}
text={issueBranch}
dataId={'btnCopyIssueBranch'}
/>
{/if}

View File

@ -20,6 +20,7 @@
export let icon: Asset
export let title: IntlString
export let text: string
export let dataId: string | undefined = undefined
</script>
<Button
@ -27,5 +28,6 @@
iconProps={{ size: 'medium' }}
kind={'icon'}
showTooltip={{ label: title, direction: 'bottom' }}
{dataId}
on:click={() => copyTextToClipboard(text)}
/>

View File

@ -233,13 +233,20 @@
<svelte:fragment slot="utils">
{#if !readonly}
<Button icon={IconMoreH} iconProps={{ size: 'medium' }} kind={'icon'} on:click={showContextMenu} />
<Button
icon={IconMoreH}
iconProps={{ size: 'medium' }}
kind={'icon'}
dataId={'btnMoreActions'}
on:click={showContextMenu}
/>
<CopyToClipboard issueUrl={generateIssueShortLink(issue.identifier)} />
<Button
icon={setting.icon.Setting}
kind={'icon'}
iconProps={{ size: 'medium' }}
showTooltip={{ label: setting.string.ClassSetting }}
dataId={'btnClassSetting'}
on:click={(ev) => {
ev.stopPropagation()
const loc = getCurrentResolvedLocation()
@ -258,6 +265,7 @@
iconProps={{ size: 'medium' }}
kind={'icon'}
selected={showAllMixins}
dataId={'btnMixin'}
on:click={() => {
showAllMixins = !showAllMixins
}}

View File

@ -179,12 +179,19 @@
{/if}
</svelte:fragment>
<svelte:fragment slot="utils">
<Button icon={IconMoreH} iconProps={{ size: 'medium' }} kind={'icon'} on:click={showContextMenu} />
<Button
icon={IconMoreH}
iconProps={{ size: 'medium' }}
kind={'icon'}
dataId={'btnMoreActions'}
on:click={showContextMenu}
/>
<Button
icon={setting.icon.Setting}
iconProps={{ size: 'medium' }}
kind={'icon'}
showTooltip={{ label: setting.string.ClassSetting }}
dataId={'btnClassSetting'}
on:click={(ev) => {
ev.stopPropagation()
const loc = getCurrentResolvedLocation()

View File

@ -46,6 +46,7 @@
bind:viewlet
bind:search
showLabelSelector={$$slots.label_selector}
adaptive={'default'}
>
<svelte:fragment slot="header-tools">
<ViewletSettingButton bind:viewOptions bind:viewlet />

View File

@ -253,6 +253,7 @@
icon={IconMoreH}
iconProps={{ size: 'medium' }}
kind={'icon'}
dataId={'btnMoreActions'}
on:click={(e) => {
showMenu(e, { object, excludedActions: [view.action.Open] })
}}
@ -263,6 +264,7 @@
iconProps={{ size: 'medium' }}
kind={'icon'}
selected={showAllMixins}
dataId={'btnMixin'}
on:click={() => {
showAllMixins = !showAllMixins
}}

View File

@ -1,6 +1,6 @@
<script lang="ts">
import { Class, Doc, DocumentQuery, Ref, Space, WithLookup } from '@hcengineering/core'
import { IModeSelector, ModeSelector, SearchInput, Header, Breadcrumb } from '@hcengineering/ui'
import { IModeSelector, ModeSelector, SearchInput, Header, Breadcrumb, HeaderAdaptive } from '@hcengineering/ui'
import type { Asset } from '@hcengineering/platform'
import { Viewlet } from '@hcengineering/view'
import ViewletSelector from './ViewletSelector.svelte'
@ -16,11 +16,17 @@
export let search: string
export let showLabelSelector = false
export let modeSelectorProps: IModeSelector | undefined = undefined
export let adaptive: HeaderAdaptive = 'doubleRow'
let scroller: HTMLElement
</script>
<Header adaptive={'doubleRow'} overflowExtra>
<Header
{adaptive}
overflowExtra
hideActions={!$$slots.actions}
hideExtra={!$$slots.extra && modeSelectorProps === undefined}
>
<svelte:fragment slot="beforeTitle">
<ViewletSelector bind:viewlet bind:viewlets viewletQuery={viewletQuery ?? { attachTo: _class }} />
<slot name="header-tools" />

View File

@ -7,7 +7,15 @@
import { Class, Doc, DocumentQuery, Ref, Space, WithLookup } from '@hcengineering/core'
import { translate } from '@hcengineering/platform'
import type { IntlString, Asset } from '@hcengineering/platform'
import { IModeSelector, SearchInput, ModeSelector, themeStore, Header, Breadcrumb } from '@hcengineering/ui'
import {
IModeSelector,
SearchInput,
ModeSelector,
themeStore,
Header,
Breadcrumb,
HeaderAdaptive
} from '@hcengineering/ui'
import { ViewOptions, Viewlet, ViewletPreference } from '@hcengineering/view'
import FilterBar from './filter/FilterBar.svelte'
@ -28,7 +36,7 @@
export let modeSelectorProps: IModeSelector | undefined = undefined
export let space: Ref<Space> | undefined = undefined
export let resultQuery: DocumentQuery<Doc>
export let adaptive: 'default' | 'freezeActions' | 'doubleRow' | 'disabled' = 'default'
export let adaptive: HeaderAdaptive = 'default'
export let hideActions: boolean = false
let label = ''

View File

@ -19,7 +19,7 @@ export class CategoryDetailsPage extends CalendarPage {
this.inputCategoryCode = page.locator('input[placeholder="Category"]')
this.textDescription = page.locator('div.grid div.tiptap')
this.inputAttachFile = page.locator('div.grid input#file')
this.buttonMoreActions = page.locator('.popupPanel > .hulyHeader-container > div:nth-child(3) > button').first()
this.buttonMoreActions = page.locator('.popupPanel > .hulyHeader-container button[data-id="btnMoreActions"]')
this.textAttachFile = page.locator('div.attachment-grid-container div[class*="attachment"] div.name')
}

View File

@ -26,7 +26,7 @@ export class CommonTrackerPage extends CalendarPage {
this.page.locator('form[id="tracker:string:MoveIssues"] input[type="checkbox"]')
buttonMoreActions = (): Locator =>
this.page.locator('.popupPanel > .hulyHeader-container > div:nth-child(3) > button').first()
this.page.locator('.popupPanel > .hulyHeader-container button[data-id="btnMoreActions"]')
textActivityContent = (): Locator => this.page.locator('div.activityMessage div.content')
linkInActivity = (): Locator => this.page.locator('div[id="activity:string:Activity"] a')