UBER-199,-217,-232: fixed header in ListView, EditMember, ViewOptions (#3273)

Signed-off-by: Alexander Platov <sas_lord@mail.ru>
This commit is contained in:
Alexander Platov 2023-05-29 09:20:07 +03:00 committed by GitHub
parent e347c8a1ce
commit 89ecba6b9a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 275 additions and 327 deletions

View File

@ -32,7 +32,7 @@
export let hideSubheader: boolean = false
export let accentHeader: boolean = false
export let gap: string | undefined = undefined
export let width: 'large' | 'medium' | 'small' | 'x-small' = 'large'
export let width: 'large' | 'medium' | 'small' | 'x-small' | 'menu' = 'large'
const dispatch = createEventDispatcher()

View File

@ -406,6 +406,7 @@ input.search {
&:not(.reverse) > *:not(:first-child) { margin-left: .5rem; }
&.reverse > *:not(:last-child) { margin-right: .5rem; }
}
.gapV-2 > *:not(:last-child) { margin-bottom: .5rem; }
.gap-3 {
&:not(.reverse) > *:not(:first-child) { margin-left: .75rem; }
&.reverse > *:not(:last-child) { margin-right: .75rem; }

View File

@ -366,6 +366,79 @@
}
}
/* Contact Card */
.antiContactCard {
display: flex;
flex-direction: column;
padding: 1rem 1.5rem 1.25rem;
background-color: var(--theme-button-enabled);
border: 1px solid var(--theme-button-border);
border-radius: 0.5rem;
transition-property: box-shadow, background-color, border-color;
transition-timing-function: var(--timing-shadow);
transition-duration: 0.15s;
user-select: text;
height: 100%;
min-width: 15rem;
min-height: 15rem;
&:hover {
background-color: var(--theme-button-hovered);
box-shadow: var(--accent-shadow);
}
.logo {
width: 4.5rem;
height: 4.5rem;
color: var(--primary-button-color);
background-color: var(--primary-button-enabled);
border-radius: 50%;
}
.label {
margin-bottom: 1.75rem;
font-weight: 500;
font-size: 0.625rem;
color: var(--theme-dark-color);
}
.name {
margin: 1rem 0 0.25rem;
font-weight: 500;
font-size: 1rem;
color: var(--theme-caption-color);
}
.description {
font-size: 0.75rem;
color: var(--theme-dark-color);
}
.footer {
display: flex;
flex-direction: row-reverse;
justify-content: space-between;
align-items: flex-end;
flex-grow: 1;
margin-top: 1.5rem;
width: 100%;
min-width: 0;
}
&.inline {
padding: 0.5rem 0.5rem 0.25rem;
min-width: 1rem;
min-height: 1rem;
background-color: inherit;
border: inherit;
border-radius: inherit;
.name {
margin: 0.25rem 0 0.25rem;
font-size: 0.75rem;
}
.label {
margin-bottom: 0rem;
}
}
}
/* Table */
.antiTable {
position: relative;

View File

@ -164,6 +164,37 @@
}
}
.antiCard-menu__item {
display: flex;
justify-content: space-between;
align-items: center;
flex-shrink: 0;
margin: 0 .5rem;
padding: .25rem .5rem;
min-width: 0;
min-height: 2.5rem;
border-radius: .25rem;
&.hoverable{
cursor: pointer;
&:hover { background-color: var(--theme-popup-hover); }
}
span {
margin-right: .5rem;
color: var(--theme-caption-color);
}
}
.antiCard-menu__spacer {
flex-shrink: 0;
height: .5rem;
}
.antiCard-menu__divider {
flex-shrink: 0;
margin: .5rem 0;
height: 1px;
background-color: var(--theme-popup-divider);
}
.antiCard-group {
padding: .5rem 1rem;
@ -195,22 +226,26 @@
height: auto;
max-height: inherit;
&.large {
&.large { // 720
width: 45rem;
max-width: 60rem;
}
&.medium {
&.medium { // 600
width: 37.5rem;
max-width: 37.5rem;
}
&.small {
&.small { // 480
width: 30rem;
max-width: 30rem;
}
&.x-small {
&.x-small { // 400
width: 25rem;
max-width: 25rem;
}
&.menu { // 280
width: 17.5rem;
max-width: 17.5rem;
}
&.full {
width: max-content;
// max-width: 100%;

View File

@ -195,6 +195,7 @@
display: flex;
justify-content: center;
align-items: center;
flex-shrink: 0;
width: 100%;
min-width: 0;
min-height: 3.5rem;

View File

@ -23,6 +23,7 @@
max-width: 17rem;
max-height: 22rem;
background: var(--theme-popup-color);
border: 1px solid var(--theme-popup-divider);
border-radius: .5rem;
box-shadow: var(--theme-popup-shadow);
@ -217,6 +218,7 @@
min-width: 0;
max-width: 30rem;
background: var(--theme-popup-color);
border: 1px solid var(--theme-popup-divider);
border-radius: .5rem;
box-shadow: var(--theme-popup-shadow);
user-select: none;

View File

@ -23,6 +23,7 @@
import DropdownLabelsPopupIntl from './DropdownLabelsPopupIntl.svelte'
import Label from './Label.svelte'
import ui from '../plugin'
import DropdownIcon from './icons/Dropdown.svelte'
export let icon: Asset | AnySvelteComponent | undefined = undefined
export let label: IntlString = ui.string.DropdownDefaultLabel
@ -84,8 +85,11 @@
showTooltip={{ label, direction: labelDirection }}
on:click={openPopup}
>
<span slot="content" class="overflow-label disabled">
<span slot="content" class="overflow-label disabled flex-grow text-left mr-2">
<Label label={selectedItem ? selectedItem.label : label} />
</span>
<svelte:fragment slot="iconRight">
<DropdownIcon size={'small'} fill={'var(--theme-dark-color)'} />
</svelte:fragment>
</Button>
</div>

View File

@ -15,7 +15,7 @@
<script lang="ts">
import { createEventDispatcher } from 'svelte'
import type { DropdownIntlItem } from '../types'
import CheckBox from './CheckBox.svelte'
import IconCheck from './icons/Check.svelte'
import Label from './Label.svelte'
import { resizeObserver } from '..'
@ -50,10 +50,10 @@
dispatch('close', item.id)
}}
>
<div class="flex-grow caption-color lines-limit-2"><Label label={item.label} /></div>
{#if item.id === selected}
<div class="check"><CheckBox checked primary /></div>
{/if}
<div class="flex-grow caption-color nowrap"><Label label={item.label} /></div>
<div class="check">
{#if item.id === selected}<IconCheck size={'small'} />{/if}
</div>
</button>
{/each}
</div>

View File

@ -0,0 +1,8 @@
<script lang="ts">
export let size: 'x-small' | 'small' | 'medium' | 'large' | 'full'
export let fill: string = 'currentColor'
</script>
<svg class="svg-{size}" {fill} viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
<polygon points="8,11.7 1.6,5.4 2.4,4.6 8,10.3 13.6,4.6 14.4,5.4 " />
</svg>

View File

@ -16,6 +16,7 @@
import contact, { Contact, Member, Organization } from '@hcengineering/contact'
import { Ref } from '@hcengineering/core'
import { createQuery } from '@hcengineering/presentation'
import { Scroller } from '@hcengineering/ui'
import { createEventDispatcher, onMount } from 'svelte'
import ExpandRightDouble from './icons/ExpandRightDouble.svelte'
import OrganizationCard from './OrganizationCard.svelte'
@ -51,11 +52,13 @@
</script>
{#if object !== undefined && refContact !== undefined && organization !== undefined}
<div class="flex-between">
<Scroller horizontal>
<div class="flex-between min-w-min">
<div class="card"><PersonCard object={refContact} on:click /></div>
<div class="arrows"><ExpandRightDouble /></div>
<div class="card"><OrganizationCard {organization} /></div>
</div>
</Scroller>
{/if}
<style lang="scss">

View File

@ -38,7 +38,7 @@
)
</script>
<div class="flex-col h-full card-container">
<div class="antiContactCard">
<div class="label uppercase"><Label label={contact.string.Organization} /></div>
<div class="flex-center logo">
<Avatar avatar={organization.avatar} size={'large'} icon={contact.icon.Company} />
@ -50,62 +50,21 @@
{organization.name}
</div>
</DocNavLink>
<div class="footer flex flex-reverse flex-grow">
<div class="flex-center flex-wrap">
<div class="footer">
<div class="flex-row-center gap-2">
<Component
is={attachment.component.AttachmentsPresenter}
props={{ value: organization.attachments, object: organization, size: 'medium', showCounter: true }}
props={{ value: organization.attachments, object: organization, size: 'small', showCounter: true }}
/>
</div>
{#if channels[0]}
<div class="flex flex-grow">
<ChannelsEditor
attachedTo={channels[0].attachedTo}
attachedClass={channels[0].attachedToClass}
length={'short'}
editable={false}
/>
</div>
{/if}
</div>
{/if}
</div>
<style lang="scss">
.card-container {
padding: 1rem 1.5rem 1.25rem;
background-color: var(--board-card-bg-color);
border: 1px solid var(--divider-color);
border-radius: 0.5rem;
transition-property: box-shadow, background-color, border-color;
transition-timing-function: var(--timing-shadow);
transition-duration: 0.15s;
user-select: text;
&:hover {
background-color: var(--board-card-bg-hover);
border-color: var(--button-border-color);
box-shadow: var(--accent-shadow);
}
.logo {
width: 5rem;
height: 5rem;
color: var(--primary-button-color);
background-color: var(--primary-button-enabled);
border-radius: 50%;
}
.label {
margin-bottom: 1.75rem;
font-weight: 500;
font-size: 0.625rem;
color: var(--dark-color);
}
.name {
margin: 1rem 0 0.25rem;
font-weight: 500;
font-size: 1rem;
color: var(--caption-color);
}
}
</style>

View File

@ -37,7 +37,7 @@
)
</script>
<div class="flex-col h-full card-container">
<div class="antiContactCard">
<div class="label uppercase"><Label label={contact.string.Person} /></div>
<div class="flex-center logo">
<Avatar avatar={object.avatar} size={'large'} icon={contact.icon.Company} />
@ -50,63 +50,21 @@
</div>
</DocNavLink>
<div class="description overflow-label">{object.city ?? ''}</div>
<div class="footer flex flex-reverse flex-grow">
<div class="flex-center flex-wrap">
<div class="footer">
<div class="flex-row-center gap-2">
<Component
is={attachment.component.AttachmentsPresenter}
props={{ value: object.attachments, object, size: 'medium', showCounter: true }}
props={{ value: object.attachments, object, size: 'small', showCounter: true }}
/>
</div>
{#if channels[0]}
<div class="flex flex-grow">
<ChannelsEditor
attachedTo={channels[0].attachedTo}
attachedClass={channels[0].attachedToClass}
length={'short'}
editable={false}
/>
</div>
{/if}
</div>
{/if}
</div>
<style lang="scss">
.card-container {
padding: 1rem 1.5rem 1.25rem;
background-color: var(--board-card-bg-color);
border: 1px solid var(--divider-color);
border-radius: 0.5rem;
transition-property: box-shadow, background-color, border-color;
transition-timing-function: var(--timing-shadow);
transition-duration: 0.15s;
user-select: text;
&:hover {
background-color: var(--board-card-bg-hover);
border-color: var(--button-border-color);
box-shadow: var(--accent-shadow);
}
.label {
margin-bottom: 1.75rem;
font-weight: 500;
font-size: 0.625rem;
color: var(--dark-color);
}
.name {
margin: 1rem 0 0.25rem;
font-weight: 500;
font-size: 1rem;
color: var(--caption-color);
}
.description {
font-size: 0.75rem;
color: var(--dark-color);
}
.footer {
margin-top: 1.5rem;
// overflow: hidden;
}
}
</style>

View File

@ -44,7 +44,7 @@
const client = getClient()
</script>
<div class="flex-col h-full flex-grow card-container">
<div class="antiContactCard">
<div class="label uppercase"><Label label={recruit.string.Talent} /></div>
<Avatar avatar={candidate?.avatar} size={'large'} />
{#if candidate}
@ -61,69 +61,25 @@
{/if}
{/if}
<div class="description overflow-label">{candidate.city ?? ''}</div>
<div class="footer flex flex-reverse flex-grow">
<div class="flex-center flex-wrap">
<div class="footer">
<div class="flex-row-center gap-2">
<Component
is={chunter.component.CommentsPresenter}
props={{ value: candidate.comments, object: candidate, size: 'medium', showCounter: true }}
props={{ value: candidate.comments, object: candidate, size: 'small', showCounter: true }}
/>
<Component
is={attachment.component.AttachmentsPresenter}
props={{ value: candidate.attachments, object: candidate, size: 'medium', showCounter: true }}
props={{ value: candidate.attachments, object: candidate, size: 'small', showCounter: true }}
/>
</div>
{#if channels[0]}
<div class="flex flex-grow">
<ChannelsEditor
attachedTo={channels[0].attachedTo}
attachedClass={channels[0].attachedToClass}
length={'short'}
editable={false}
/>
</div>
{/if}
</div>
{/if}
</div>
<style lang="scss">
.card-container {
padding: 1rem 1.5rem 1.25rem;
background-color: var(--theme-button-enabled);
border: 1px solid var(--theme-button-border);
border-radius: 0.5rem;
transition-property: box-shadow, background-color, border-color;
transition-timing-function: var(--timing-shadow);
transition-duration: 0.15s;
user-select: text;
min-width: 15rem;
min-height: 15rem;
max-width: 25rem;
&:hover {
background-color: var(--theme-button-hovered);
box-shadow: var(--accent-shadow);
}
.label {
margin-bottom: 1.75rem;
font-weight: 500;
font-size: 0.625rem;
color: var(--theme-dark-color);
}
.name {
margin: 1rem 0 0.25rem;
font-weight: 500;
font-size: 1rem;
color: var(--theme-caption-color);
}
.description {
font-size: 0.75rem;
color: var(--theme-dark-color);
}
.footer {
margin-top: 1.5rem;
// overflow: hidden;
}
}
</style>

View File

@ -64,7 +64,7 @@
}
</script>
<div class="flex-col h-full card-container" class:inline>
<div class="antiContactCard" class:inline>
{#if !inline}
<div class="label uppercase"><Label label={recruit.string.Vacancy} /></div>
<div class="flex-center logo">
@ -74,8 +74,7 @@
{#if vacancy}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<NavLink {disabled} space={vacancy._id} app={recruitId}>
<div class="name lines-limit-2">
<div class="text-md">
<div class="name">
{#if inline}
<div class="flex-row-center">
<VacancyIcon size={'small'} />
@ -87,97 +86,33 @@
{vacancy.name}
{/if}
</div>
</div>
</NavLink>
{#if company}
<span class="label">{company.name}</span>
<span class="label overflow-label">{company.name}</span>
{/if}
{#if !inline || vacancy.description}
<div class="description lines-limit-2 text-md">{vacancy.description ?? ''}</div>
{/if}
<div class="footer flex flex-reverse flex-grow">
<div class="flex-center flex-wrap">
<div class="footer">
<div class="flex-row-center gap-2">
<Component
is={chunter.component.CommentsPresenter}
props={{ value: vacancy.comments, object: vacancy, size: 'medium', showCounter: true }}
props={{ value: vacancy.comments, object: vacancy, size: 'small', showCounter: true }}
/>
<Component
is={attachment.component.AttachmentsPresenter}
props={{ value: vacancy.attachments, object: vacancy, size: 'medium', showCounter: true }}
props={{ value: vacancy.attachments, object: vacancy, size: 'small', showCounter: true }}
/>
</div>
{#if channels[0]}
<div class="flex flex-grow">
<ChannelsEditor
attachedTo={channels[0].attachedTo}
attachedClass={channels[0].attachedToClass}
length={'short'}
editable={false}
/>
</div>
{/if}
</div>
{/if}
</div>
<style lang="scss">
.card-container {
padding: 1rem 1.5rem 1.25rem;
background-color: var(--theme-button-enabled);
border: 1px solid var(--theme-button-border);
border-radius: 0.5rem;
transition-property: box-shadow, background-color, border-color;
transition-timing-function: var(--timing-shadow);
transition-duration: 0.15s;
user-select: text;
min-width: 15rem;
min-height: 15rem;
&:hover {
background-color: var(--theme-button-hovered);
box-shadow: var(--accent-shadow);
}
.logo {
width: 4.5rem;
height: 4.5rem;
color: var(--primary-button-color);
background-color: var(--primary-button-enabled);
border-radius: 50%;
}
.label {
margin-bottom: 1.75rem;
font-weight: 500;
font-size: 0.625rem;
color: var(--dark-color);
}
.name {
margin: 1rem 0 0.25rem;
font-weight: 500;
font-size: 1rem;
color: var(--caption-color);
}
.description {
font-size: 0.75rem;
color: var(--dark-color);
}
&.inline {
padding: 0.5rem 0.5rem 0.25rem;
min-width: 1rem;
min-height: 1rem;
background-color: inherit;
border: inherit;
border-radius: inherit;
.name {
margin: 0.25rem 0 0.25rem;
font-size: 0.75rem;
}
.label {
margin-bottom: 0rem;
}
}
}
</style>

View File

@ -198,7 +198,7 @@
handleFilterToggle(element)
}}
>
<div class="tag" style:background-color={color.background} />
<div class="tag" style:background-color={color.color} />
<span class="overflow-label label flex-grow" style:color={color.title}>{element.title}</span>
<div class="check pointer-events-none">
{#if isSelected(element)}

View File

@ -345,9 +345,9 @@
{/if}
</svelte:fragment>
<svelte:fragment slot="subheader">
<svelte:fragment slot="header">
{#if mainEditor && mainEditor.pinned}
<div class="flex-col flex-grow my-4 step-tb-6">
<div class="flex-col flex-grow my-4">
<Component is={mainEditor.editor} props={{ object }} on:open={handleOpen} />
</div>
{/if}

View File

@ -1,7 +1,7 @@
<script lang="ts">
import { getClient } from '@hcengineering/presentation'
import { DropdownIntlItem, DropdownLabelsIntl, Label, MiniToggle } from '@hcengineering/ui'
import { Viewlet, ViewOptions, ViewOptionsModel } from '@hcengineering/view'
import { DropdownIntlItem, DropdownLabelsIntl, Label, Toggle } from '@hcengineering/ui'
import { Viewlet, ViewOptions, ViewOptionsModel, ViewOptionModel } from '@hcengineering/view'
import { createEventDispatcher } from 'svelte'
import view from '../plugin'
import { buildConfigLookup, getKeyLabel } from '../utils'
@ -59,15 +59,22 @@
const notAllowed = current.slice(0, i)
return groupBy.filter((p) => !notAllowed.includes(p.id as string))
}
const changeToggle = (model: ViewOptionModel) => {
viewOptions[model.key] = !viewOptions[model.key]
dispatch('update', { key: model.key, value: viewOptions[model.key] })
}
</script>
<div class="antiCard">
<div class="antiCard-group grid">
<div class="antiCard dialog menu">
<div class="antiCard-menu__spacer" />
{#each groups as group, i}
<span class="label"><Label label={i === 0 ? view.string.Grouping : view.string.Then} /></span>
<div class="value grouping">
<div class="antiCard-menu__item grouping">
<span class="overflow-label"><Label label={i === 0 ? view.string.Grouping : view.string.Then} /></span>
<DropdownLabelsIntl
label={view.string.Grouping}
kind={'secondary'}
size={'medium'}
items={getItems(groupBy, i, viewOptions.groupBy)}
selected={group}
width="10rem"
@ -76,10 +83,12 @@
/>
</div>
{/each}
<span class="label"><Label label={view.string.Ordering} /></span>
<div class="value ordering">
<div class="antiCard-menu__item ordering">
<span class="overflow-label"><Label label={view.string.Ordering} /></span>
<DropdownLabelsIntl
label={view.string.Ordering}
kind={'secondary'}
size={'medium'}
items={orderBy}
selected={viewOptions.orderBy[0]}
width="10rem"
@ -94,21 +103,24 @@
}}
/>
</div>
<div class="antiCard-menu__divider" />
{#each config.other.filter((p) => !p.hidden?.(viewOptions)) as model}
<span class="label"><Label label={model.label} /></span>
<div class="value">
{#if isToggleType(model)}
<MiniToggle
on={viewOptions[model.key] ?? model.defaultValue}
on:change={() => {
viewOptions[model.key] = !viewOptions[model.key]
dispatch('update', { key: model.key, value: viewOptions[model.key] })
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div
class="antiCard-menu__item hoverable ordering"
on:click={() => {
if (isToggleType(model)) changeToggle(model)
}}
/>
>
<span class="overflow-label"><Label label={model.label} /></span>
{#if isToggleType(model)}
<Toggle on={viewOptions[model.key] ?? model.defaultValue} on:change={() => changeToggle(model)} />
{:else if isDropdownType(model)}
{@const items = model.values.filter(({ hidden }) => !hidden?.(viewOptions))}
<DropdownLabelsIntl
label={model.label}
kind={'secondary'}
size={'medium'}
{items}
selected={viewOptions[model.key] ?? model.defaultValue}
width="10rem"
@ -122,5 +134,5 @@
</div>
{/each}
<slot name="extra" />
</div>
<div class="antiCard-menu__spacer" />
</div>

View File

@ -81,7 +81,7 @@
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div
style:z-index={10 - level}
style:background={headerBGColor}
style:--header-bg-color={headerBGColor}
class="flex-between categoryHeader row"
class:flat
class:collapsed
@ -150,7 +150,7 @@
{:else}
<span class="antiSection-header__counter ml-2">{items.length}</span>
{/if}
<div class="flex-row-center flex-reverse flex-grow">
<div class="flex-row-center flex-reverse flex-grow mr-2 gap-2 reverse">
{#each extraHeaders ?? [] as extra}
<Component is={extra} props={{ ...props, value: category, category: groupByKey, docs: items }} />
{/each}
@ -228,6 +228,7 @@
border-bottom-color: transparent;
}
&::before {
background: var(--header-bg-color);
z-index: -1;
}
@ -250,8 +251,8 @@
top: 2.75rem;
padding: 0 2.5rem;
background: var(--theme-list-subheader-color);
border-left: 1px solid var(--theme-list-border-color);
border-right: 1px solid var(--theme-list-border-color);
border-left: 1px solid var(--theme-list-subheader-divider);
border-right: 1px solid var(--theme-list-subheader-divider);
border-bottom: 1px solid var(--theme-list-subheader-divider);
// here should be top 3rem for sticky, but with ExpandCollapse it gives strange behavior