mirror of
https://github.com/hcengineering/platform.git
synced 2024-11-22 03:14:40 +03:00
Updated Planner layout (#4756)
Signed-off-by: Alexander Platov <alexander.platov@hardcoreeng.com>
This commit is contained in:
parent
0d2501a95a
commit
cc8a1d549a
@ -44,6 +44,7 @@
|
||||
export let withoutContentScroll: boolean = false
|
||||
export let customAside: ButtonItem[] | undefined = undefined
|
||||
export let selectedAside: string | boolean = customAside ? customAside[0].id : isAside
|
||||
export let kind: 'default' | 'modern' = 'default'
|
||||
|
||||
export function getAside (): string | boolean {
|
||||
return panel.getAside()
|
||||
@ -103,6 +104,7 @@
|
||||
on:close
|
||||
{allowClose}
|
||||
{embedded}
|
||||
{kind}
|
||||
{floatAside}
|
||||
bind:useMaxWidth
|
||||
{isFullSize}
|
||||
|
@ -717,6 +717,7 @@ input.search {
|
||||
.min-h-9 { min-height: 2.25rem; }
|
||||
.min-h-11 { min-height: 2.75rem; }
|
||||
.min-h-12 { min-height: 3rem; }
|
||||
.min-h-16 { min-height: 4rem; }
|
||||
.min-h-30 { min-height: 7.5rem; }
|
||||
.min-h-60 { min-height: 15rem; }
|
||||
.max-w-2 { max-width: .5rem; }
|
||||
|
@ -54,6 +54,9 @@
|
||||
--global-higlight-Color: #F76E53;
|
||||
--global-accent-SkyText: #B9D1F5;
|
||||
|
||||
--tag-on-subtle-PorpoiseText: #F2F4F6;
|
||||
--tag-subtle-PorpoiseBackground: #343F49;
|
||||
|
||||
/** Buttons **/
|
||||
--button-subtle-LabelColor: #fff;
|
||||
--button-subtle-IconColor: #fff;
|
||||
@ -113,6 +116,9 @@
|
||||
--global-higlight-Color: #F76E53;
|
||||
--global-accent-SkyText:#B9D1F5;
|
||||
|
||||
--tag-on-subtle-PorpoiseText: #293139;
|
||||
--tag-subtle-PorpoiseBackground: #C8D1D9;
|
||||
|
||||
/** Buttons **/
|
||||
--button-subtle-LabelColor: #000;
|
||||
--button-subtle-IconColor: #000;
|
||||
|
@ -14,6 +14,7 @@
|
||||
//
|
||||
|
||||
/* Typography */
|
||||
.font-medium-11,
|
||||
.font-regular-12,
|
||||
.font-medium-12,
|
||||
.font-bold-12,
|
||||
@ -29,6 +30,9 @@
|
||||
line-height: 1rem;
|
||||
color: var(--global-primary-TextColor);
|
||||
}
|
||||
.font-medium-11 {
|
||||
font-size: 0.6875rem;
|
||||
}
|
||||
.font-regular-12,
|
||||
.font-medium-12,
|
||||
.font-bold-12 {
|
||||
@ -45,8 +49,11 @@
|
||||
.paragraph-regular-14 {
|
||||
font-weight: 400;
|
||||
}
|
||||
.font-medium-11,
|
||||
.font-medium-12,
|
||||
.font-medium-14 {
|
||||
.font-medium-14,
|
||||
.heading-medium-16,
|
||||
.heading-medium-20 {
|
||||
font-weight: 500;
|
||||
}
|
||||
.font-bold-12,
|
||||
@ -54,10 +61,6 @@
|
||||
.heading-bold-20 {
|
||||
font-weight: 700;
|
||||
}
|
||||
.heading-medium-16,
|
||||
.heading-medium-20 {
|
||||
font-weight: 500;
|
||||
}
|
||||
.heading-medium-16 {
|
||||
font-size: 1rem;
|
||||
line-height: 1.125rem;
|
||||
|
@ -277,6 +277,11 @@
|
||||
color: var(--global-secondary-TextColor);
|
||||
}
|
||||
}
|
||||
&.type-component {
|
||||
background-color: var(--theme-comp-header-color); // var(--global-surface-02-BackgroundColor);
|
||||
border: 1px solid var(--theme-navpanel-divider); // var(--global-surface-02-BorderColor);
|
||||
border-radius: var(--small-focus-BorderRadius);
|
||||
}
|
||||
textarea {
|
||||
font-weight: 400 !important;
|
||||
color: var(--global-tertiary-TextColor) !important;
|
||||
@ -476,7 +481,8 @@
|
||||
& + .hulyAccordionItem-content {
|
||||
max-height: 100%;
|
||||
}
|
||||
&.small.bottomSpace + .hulyAccordionItem-content {
|
||||
&.small.bottomSpace + .hulyAccordionItem-content,
|
||||
&.small.nav + .hulyAccordionItem-content {
|
||||
padding-bottom: var(--spacing-1_5);
|
||||
}
|
||||
&.medium.bottomSpace + .hulyAccordionItem-content {
|
||||
|
@ -364,6 +364,15 @@
|
||||
// border-radius: 0 0 .5rem .5rem;
|
||||
// }
|
||||
}
|
||||
|
||||
&.modern {
|
||||
border: 1px solid transparent;
|
||||
border-radius: var(--small-focus-BorderRadius);
|
||||
|
||||
.popupPanel-title {
|
||||
min-height: 3.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Full size state
|
||||
|
@ -16,6 +16,7 @@
|
||||
import type { Asset, IntlString } from '@hcengineering/platform'
|
||||
import { AnySvelteComponent, IconSize, LabelAndProps } from '../types'
|
||||
import { tooltip as tp } from '../tooltips'
|
||||
import { registerFocus } from '../focus'
|
||||
import { ComponentType } from 'svelte'
|
||||
import Spinner from './Spinner.svelte'
|
||||
import Icon from './Icon.svelte'
|
||||
@ -46,6 +47,29 @@
|
||||
} else if (type === 'type-button' && !hasMenu) {
|
||||
actualIconSize = 'medium'
|
||||
}
|
||||
|
||||
// Focusable control with index
|
||||
export let focusIndex = -1
|
||||
const { idx, focusManager } = registerFocus(focusIndex, {
|
||||
focus: () => {
|
||||
if (!disabled) {
|
||||
element?.focus()
|
||||
}
|
||||
return !disabled && element != null
|
||||
},
|
||||
isFocus: () => document.activeElement === element
|
||||
})
|
||||
|
||||
$: if (idx !== -1 && focusManager) {
|
||||
focusManager.updateFocus(idx, focusIndex)
|
||||
}
|
||||
|
||||
const updateFocus = () => {
|
||||
focusManager?.setFocus(idx)
|
||||
}
|
||||
$: if (element != null) {
|
||||
element.addEventListener('focus', updateFocus, { once: true })
|
||||
}
|
||||
</script>
|
||||
|
||||
<button
|
||||
|
@ -28,6 +28,7 @@
|
||||
export let loading: boolean = false
|
||||
export let inheritColor: boolean = false
|
||||
export let tooltip: LabelAndProps | undefined = undefined
|
||||
export let focusIndex = -1
|
||||
</script>
|
||||
|
||||
<ButtonBase
|
||||
@ -42,5 +43,6 @@
|
||||
{pressed}
|
||||
{hasMenu}
|
||||
{tooltip}
|
||||
{focusIndex}
|
||||
on:click
|
||||
/>
|
||||
|
@ -36,6 +36,7 @@
|
||||
export let params: Record<string, any> = {}
|
||||
export let selected: DropdownIntlItem['id'] | undefined = undefined
|
||||
export let element: HTMLButtonElement | undefined = undefined
|
||||
export let focusIndex = -1
|
||||
|
||||
let opened: boolean = false
|
||||
|
||||
@ -81,5 +82,6 @@
|
||||
{loading}
|
||||
{inheritColor}
|
||||
pressed={opened}
|
||||
{focusIndex}
|
||||
on:click={openPopup}
|
||||
/>
|
||||
|
@ -19,7 +19,7 @@
|
||||
export let symbol: 'check' | 'minus' = 'check'
|
||||
export let size: 'small' | 'medium' | 'large' = 'small'
|
||||
export let circle: boolean = false
|
||||
export let kind: 'default' | 'primary' | 'positive' | 'negative' = 'default'
|
||||
export let kind: 'default' | 'primary' | 'positive' | 'negative' | 'todo' = 'default'
|
||||
export let readonly = false
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
@ -42,16 +42,7 @@
|
||||
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
|
||||
<label
|
||||
class="checkbox {size}"
|
||||
class:circle
|
||||
class:primary={kind === 'primary'}
|
||||
class:positive={kind === 'positive'}
|
||||
class:negative={kind === 'negative'}
|
||||
class:readonly
|
||||
class:checked
|
||||
on:click|stopPropagation
|
||||
>
|
||||
<label class="checkbox {size} {kind}" class:circle class:readonly class:checked on:click|stopPropagation>
|
||||
<input class="chBox" disabled={readonly} type="checkbox" bind:checked on:change|capture={handleValueChanged} />
|
||||
<svg class="checkSVG" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
||||
{#if checked}
|
||||
@ -132,6 +123,9 @@
|
||||
background-color: var(--negative-button-default);
|
||||
}
|
||||
}
|
||||
&.todo {
|
||||
border-color: var(--theme-divider-color);
|
||||
}
|
||||
|
||||
.chBox {
|
||||
position: absolute;
|
||||
|
@ -18,6 +18,7 @@
|
||||
|
||||
export let type: 'type-aside' | 'type-popup' | 'type-component' | 'type-panel' = 'type-component'
|
||||
export let minimize: boolean = false
|
||||
export let noResize: boolean = false
|
||||
export let hideSeparator: boolean = false
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
@ -25,13 +26,16 @@
|
||||
|
||||
<div class="hulyHeader-container" class:topIndent={type === 'type-panel'} class:hideSeparator>
|
||||
{#if type === 'type-component'}
|
||||
<button class="hulyHeader-button" on:click={() => dispatch('resize', minimize)}>
|
||||
{#if minimize}
|
||||
<IconMinimize size={'small'} />
|
||||
{:else}
|
||||
<IconMaximize size={'small'} />
|
||||
{/if}
|
||||
</button>
|
||||
{#if !noResize}
|
||||
<button class="hulyHeader-button" on:click={() => dispatch('resize', minimize)}>
|
||||
{#if minimize}
|
||||
<IconMinimize size={'small'} />
|
||||
{:else}
|
||||
<IconMaximize size={'small'} />
|
||||
{/if}
|
||||
</button>
|
||||
{/if}
|
||||
<slot name="beforeTitle" />
|
||||
<div class="hulyHeader-divider" />
|
||||
{/if}
|
||||
<div class="hulyHeader-titleGroup">
|
||||
|
@ -23,14 +23,15 @@
|
||||
import ui from '..'
|
||||
|
||||
export let type: 'type-aside' | 'type-popup' | 'type-component'
|
||||
export let label: IntlString
|
||||
export let label: IntlString | undefined = undefined
|
||||
export let labelProps: any | undefined = undefined
|
||||
export let okAction: () => Promise<void> | void
|
||||
export let okAction: () => Promise<void> | void = () => {}
|
||||
export let onCancel: (() => void) | undefined = undefined
|
||||
export let canSave: boolean = false
|
||||
export let okLabel: IntlString = ui.string.Ok
|
||||
export let padding: string | undefined = undefined
|
||||
export let hidden = false
|
||||
export let hidden: boolean = false
|
||||
export let noResize: boolean = false
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
@ -47,18 +48,23 @@
|
||||
? 'var(--spacing-2) var(--spacing-3) var(--spacing-4)'
|
||||
: type === 'type-aside'
|
||||
? 'var(--spacing-2) var(--spacing-1_5)'
|
||||
: 'var(--spacing-3)'
|
||||
: 'var(--spacing-4)'
|
||||
</script>
|
||||
|
||||
<svelte:window on:keydown={onKeyDown} />
|
||||
|
||||
<div class="hulyModal-container {type}" class:hidden>
|
||||
<Header {type} on:close={close}>
|
||||
<Label {label} params={labelProps} />
|
||||
<Header {type} {noResize} on:close={close}>
|
||||
<svelte:fragment slot="beforeTitle">
|
||||
<slot name="beforeTitle" />
|
||||
</svelte:fragment>
|
||||
{#if label}<Label {label} params={labelProps} />{/if}
|
||||
<slot name="title" />
|
||||
<svelte:fragment slot="actions">
|
||||
<slot name="actions" />
|
||||
</svelte:fragment>
|
||||
</Header>
|
||||
<slot name="beforeContent" />
|
||||
<div class="hulyModal-content">
|
||||
<Scroller
|
||||
padding={padding ?? typePadding}
|
||||
@ -66,11 +72,12 @@
|
||||
? undefined
|
||||
: type === 'type-aside'
|
||||
? 'var(--spacing-2)'
|
||||
: 'var(--spacing-3)'}
|
||||
: 'var(--spacing-4)'}
|
||||
>
|
||||
<slot />
|
||||
</Scroller>
|
||||
</div>
|
||||
<slot name="afterContent" />
|
||||
{#if type !== 'type-component'}
|
||||
<div class="hulyModal-footer">
|
||||
<ButtonBase
|
||||
|
@ -20,6 +20,7 @@
|
||||
export let loading: boolean = false
|
||||
export let hasMenu: boolean = false
|
||||
export let inheritFont: boolean = false
|
||||
export let focusIndex = -1
|
||||
</script>
|
||||
|
||||
<ButtonBase
|
||||
@ -35,6 +36,7 @@
|
||||
{disabled}
|
||||
{hasMenu}
|
||||
{inheritFont}
|
||||
{focusIndex}
|
||||
on:click
|
||||
>
|
||||
<slot />
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
import { createEventDispatcher, onMount } from 'svelte'
|
||||
import { IntlString, translate } from '@hcengineering/platform'
|
||||
import { registerFocus } from '../focus'
|
||||
import Label from './Label.svelte'
|
||||
import { themeStore } from '..'
|
||||
|
||||
@ -39,6 +40,22 @@
|
||||
element.focus()
|
||||
}
|
||||
})
|
||||
|
||||
// Focusable control with index
|
||||
export let focusIndex = -1
|
||||
const { idx, focusManager } = registerFocus(focusIndex, {
|
||||
focus: () => {
|
||||
element?.focus()
|
||||
return element != null
|
||||
},
|
||||
isFocus: () => document.activeElement === element
|
||||
})
|
||||
const updateFocus = () => {
|
||||
focusManager?.setFocus(idx)
|
||||
}
|
||||
$: if (element) {
|
||||
element.addEventListener('focus', updateFocus)
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
|
@ -27,8 +27,8 @@
|
||||
$: id = `navGroup-${categoryName}`
|
||||
</script>
|
||||
|
||||
<div class="hulyAccordionItem-container" class:isOpen class:second>
|
||||
<button class="hulyAccordionItem-header small" class:isOpen class:selected on:click={() => (isOpen = !isOpen)}>
|
||||
<div class="hulyAccordionItem-container" class:second>
|
||||
<button class="hulyAccordionItem-header nav small" class:isOpen class:selected on:click={() => (isOpen = !isOpen)}>
|
||||
<div class="hulyAccordionItem-header__label-wrapper font-medium-12">
|
||||
<span class="hulyAccordionItem-header__label">
|
||||
<Label {label} />
|
||||
@ -46,7 +46,7 @@
|
||||
</div>
|
||||
{/if}
|
||||
</button>
|
||||
<div {id} class="hulyAccordionItem-content" class:isOpen>
|
||||
<div {id} class="hulyAccordionItem-content">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
|
@ -44,6 +44,7 @@
|
||||
export let useMaxWidth: boolean | undefined = undefined
|
||||
export let customAside: ButtonItem[] | undefined = undefined
|
||||
export let selectedAside: string | boolean = customAside ? customAside[0].id : isAside
|
||||
export let kind: 'default' | 'modern' = 'default'
|
||||
|
||||
export function getAside (): string | boolean {
|
||||
if (customAside) return selectedAside
|
||||
@ -124,7 +125,7 @@
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="popupPanel panel"
|
||||
class="popupPanel panel {kind}"
|
||||
class:embedded
|
||||
use:resizeObserver={(element) => {
|
||||
panelWidth = element.clientWidth
|
||||
|
@ -24,6 +24,7 @@
|
||||
import Spinner from './Spinner.svelte'
|
||||
|
||||
export let contentPanel: HTMLElement
|
||||
export let kind: 'default' | 'modern' = 'default'
|
||||
|
||||
let modalHTML: HTMLElement
|
||||
let componentInstance: any
|
||||
@ -162,6 +163,7 @@
|
||||
_class={props._class}
|
||||
rightSection={props.rightSection}
|
||||
position={props.element}
|
||||
{kind}
|
||||
bind:popupOptions={options}
|
||||
on:open={_open}
|
||||
on:close={_close}
|
||||
|
@ -225,7 +225,7 @@
|
||||
item.maxSize !== -1 ? item.size + 'px' : item.minSize === -1 ? '0' : item.minSize + 'px'
|
||||
};`
|
||||
style += item.maxSize === -1 ? '' : `max-${side}:${item.size + 'px'};`
|
||||
style += item.maxSize !== -1 ? `${side}:${item.size}px;` : `${side}:calc(100% - ${sum}px);`
|
||||
style += item.maxSize !== -1 ? `${side}:${item.size}px;` : '' // `${side}:calc(100% - ${sum}px);`
|
||||
if (item.styles !== null) {
|
||||
item.styles.forEach((value, key) => {
|
||||
if (key !== 'pointer-events' || final) style += `${key}:${value};`
|
||||
|
@ -130,6 +130,7 @@ export { default as Breadcrumb } from './components/Breadcrumb.svelte'
|
||||
export { default as Breadcrumbs } from './components/Breadcrumbs.svelte'
|
||||
export { default as ButtonIcon } from './components/ButtonIcon.svelte'
|
||||
export { default as ButtonMenu } from './components/ButtonMenu.svelte'
|
||||
export { default as ButtonBase } from './components/ButtonBase.svelte'
|
||||
export { default as ModernButton } from './components/ModernButton.svelte'
|
||||
export { default as ModernEditbox } from './components/ModernEditbox.svelte'
|
||||
export { default as ModernPopup } from './components/ModernPopup.svelte'
|
||||
|
@ -212,7 +212,7 @@
|
||||
<CalendarSelector bind:value={space} focusIndex={10101} />
|
||||
<div class="flex-row-center flex-gap-1">
|
||||
<Icon icon={calendar.icon.Hidden} size={'small'} />
|
||||
<VisibilityEditor bind:value={visibility} kind={'ghost'} focusIndex={10102} withoutIcon />
|
||||
<VisibilityEditor bind:value={visibility} kind={'tertiary'} focusIndex={10102} withoutIcon />
|
||||
</div>
|
||||
<EventReminders bind:reminders focusIndex={10103} />
|
||||
</div>
|
||||
|
@ -218,7 +218,7 @@
|
||||
<CalendarSelector bind:value={space} focusIndex={10008} />
|
||||
<div class="flex-row-center flex-gap-1">
|
||||
<Icon icon={calendar.icon.Hidden} size={'small'} />
|
||||
<VisibilityEditor bind:value={visibility} kind={'ghost'} withoutIcon focusIndex={10009} />
|
||||
<VisibilityEditor bind:value={visibility} kind={'tertiary'} withoutIcon focusIndex={10009} />
|
||||
</div>
|
||||
<EventReminders bind:reminders focusIndex={10010} />
|
||||
</div>
|
||||
|
@ -14,45 +14,39 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Visibility } from '@hcengineering/calendar'
|
||||
import { translate } from '@hcengineering/platform'
|
||||
import { ButtonKind, Dropdown, ListItem, themeStore } from '@hcengineering/ui'
|
||||
import { ButtonMenu, DropdownIntlItem, themeStore } from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import calendar from '../plugin'
|
||||
|
||||
export let value: Visibility | undefined
|
||||
export let disabled: boolean = false
|
||||
export let kind: ButtonKind = 'regular'
|
||||
export let kind: 'primary' | 'secondary' | 'tertiary' | 'negative' = 'secondary'
|
||||
export let size: 'small' | 'medium' | 'large' = 'medium'
|
||||
export let withoutIcon: boolean = false
|
||||
export let focusIndex = -1
|
||||
|
||||
let items: ListItem[] = []
|
||||
|
||||
$: fill($themeStore.language)
|
||||
|
||||
async function fill (lang: string) {
|
||||
items = [
|
||||
{
|
||||
_id: 'public',
|
||||
label: await translate(calendar.string.Public, {}, lang),
|
||||
icon: calendar.icon.Public
|
||||
},
|
||||
{
|
||||
_id: 'freeBusy',
|
||||
label: await translate(calendar.string.FreeBusy, {}, lang),
|
||||
icon: calendar.icon.Private
|
||||
},
|
||||
{
|
||||
_id: 'private',
|
||||
label: await translate(calendar.string.Private, {}, lang),
|
||||
icon: calendar.icon.Hidden
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
$: selected = value !== undefined ? items.find((item) => item._id === value) : undefined
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
const items: DropdownIntlItem[] = [
|
||||
{
|
||||
id: 'public',
|
||||
label: calendar.string.Public,
|
||||
icon: calendar.icon.Public
|
||||
},
|
||||
{
|
||||
id: 'freeBusy',
|
||||
label: calendar.string.FreeBusy,
|
||||
icon: calendar.icon.Private
|
||||
},
|
||||
{
|
||||
id: 'private',
|
||||
label: calendar.string.Private,
|
||||
icon: calendar.icon.Hidden
|
||||
}
|
||||
]
|
||||
|
||||
$: selected = value !== undefined ? items.find((item) => item.id === value) : undefined
|
||||
|
||||
function change (val: Visibility) {
|
||||
if (value !== val) {
|
||||
dispatch('change', val)
|
||||
@ -61,17 +55,16 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<Dropdown
|
||||
{disabled}
|
||||
<ButtonMenu
|
||||
icon={withoutIcon ? undefined : calendar.icon.Hidden}
|
||||
{kind}
|
||||
size={'medium'}
|
||||
placeholder={calendar.string.DefaultVisibility}
|
||||
label={selected?.label}
|
||||
selected={selected?.id}
|
||||
{items}
|
||||
withSearch={false}
|
||||
{kind}
|
||||
{size}
|
||||
{disabled}
|
||||
{focusIndex}
|
||||
{selected}
|
||||
on:selected={(e) => {
|
||||
change(e.detail._id)
|
||||
on:selected={(event) => {
|
||||
if (event.detail) change(event.detail)
|
||||
}}
|
||||
/>
|
||||
|
@ -13,10 +13,11 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { Class, Doc, Ref } from '@hcengineering/core'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import tags, { TagReference } from '@hcengineering/tags'
|
||||
import { Button, ButtonKind, Icon, Label, getEventPopupPositionElement, showPopup } from '@hcengineering/ui'
|
||||
import { ButtonBase, ButtonKind, Icon, Label, getEventPopupPositionElement, showPopup } from '@hcengineering/ui'
|
||||
import tagsPlugin from '../plugin'
|
||||
import TagReferencePresenter from './TagReferencePresenter.svelte'
|
||||
import TagsEditorPopup from './TagsEditorPopup.svelte'
|
||||
@ -24,9 +25,14 @@
|
||||
|
||||
export let object: Doc
|
||||
export let targetClass: Ref<Class<Doc>>
|
||||
export let kind: ButtonKind = 'ghost'
|
||||
export let type: 'type-button-only' | 'type-content-only' = 'type-content-only'
|
||||
export let buttonParams: Record<string, any> = {}
|
||||
export let contentParams: Record<string, any> = {}
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
let items: TagReference[] = []
|
||||
let pressed: boolean = false
|
||||
const query = createQuery()
|
||||
const client = getClient()
|
||||
|
||||
@ -35,37 +41,54 @@
|
||||
})
|
||||
|
||||
async function click (evt: MouseEvent): Promise<void> {
|
||||
showPopup(TagsEditorPopup, { object, targetClass }, getEventPopupPositionElement(evt))
|
||||
pressed = true
|
||||
showPopup(TagsEditorPopup, { object, targetClass }, getEventPopupPositionElement(evt), () => {
|
||||
pressed = false
|
||||
})
|
||||
}
|
||||
|
||||
async function removeTag (tag: TagReference): Promise<void> {
|
||||
if (tag !== undefined) await client.remove(tag)
|
||||
}
|
||||
|
||||
let count: number = 0
|
||||
$: updated(items)
|
||||
|
||||
const updated = (its: TagReference[]): void => {
|
||||
if (count === its.length) return
|
||||
count = its.length
|
||||
dispatch('change', count)
|
||||
}
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<Button {kind} padding={'0rem;'} on:click={click}>
|
||||
<div slot="content" class="flex-row-center flex-gap-1">
|
||||
<Icon icon={TagIcon} size={'medium'} />
|
||||
<span class="overflow-label label"><Label label={tagsPlugin.string.AddLabel} /></span>
|
||||
</div>
|
||||
</Button>
|
||||
{#if type === 'type-button-only'}
|
||||
<ButtonBase
|
||||
icon={TagIcon}
|
||||
type={'type-button-icon'}
|
||||
kind={'secondary'}
|
||||
size={'small'}
|
||||
{pressed}
|
||||
hasMenu
|
||||
{...buttonParams}
|
||||
on:click={click}
|
||||
/>
|
||||
{/if}
|
||||
{#if type === 'type-content-only'}
|
||||
{#if items.length}
|
||||
<div class="flex-row-center flex-wrap">
|
||||
<div class="flex-row-center flex-wrap flex-gap-1-5">
|
||||
{#each items as value}
|
||||
<div class="step-container clear-mins">
|
||||
<TagReferencePresenter
|
||||
attr={undefined}
|
||||
isEditable
|
||||
{value}
|
||||
kind={'list'}
|
||||
on:remove={(res) => removeTag(res.detail)}
|
||||
/>
|
||||
</div>
|
||||
<TagReferencePresenter
|
||||
attr={undefined}
|
||||
isEditable
|
||||
{value}
|
||||
kind={'todo'}
|
||||
{...contentParams}
|
||||
on:remove={(res) => removeTag(res.detail)}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
.step-container {
|
||||
|
@ -9,15 +9,17 @@
|
||||
import TagsReferencePresenter from './TagsReferencePresenter.svelte'
|
||||
import TagsEditorPopup from './TagsEditorPopup.svelte'
|
||||
import TagsItemPresenter from './TagsItemPresenter.svelte'
|
||||
import LabelsPresenter from './LabelsPresenter.svelte'
|
||||
|
||||
export let value: number
|
||||
export let object: WithLookup<Doc>
|
||||
export let full: boolean
|
||||
export let ckeckFilled: boolean = false
|
||||
export let kind: 'short' | 'full' | 'list' | 'link' | 'todo' = 'short'
|
||||
export let kind: 'short' | 'full' | 'list' | 'link' | 'todo' | 'todo-compact' = 'short'
|
||||
export let isEditable: boolean = false
|
||||
export let action: (evt: MouseEvent) => Promise<void> | void = async () => {}
|
||||
export let compression: boolean = false
|
||||
export let ignoreFirst: boolean = false
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
@ -29,7 +31,7 @@
|
||||
function update (object: WithLookup<Doc>, value: number) {
|
||||
if (value > 0) {
|
||||
query.query(tags.class.TagReference, { attachedTo: object._id }, (result) => {
|
||||
items = result
|
||||
items = result.slice(ignoreFirst ? 1 : 0)
|
||||
})
|
||||
} else {
|
||||
query.unsubscribe()
|
||||
@ -52,7 +54,7 @@
|
||||
})
|
||||
</script>
|
||||
|
||||
{#if kind === 'list' || kind === 'link' || kind === 'todo'}
|
||||
{#if kind === 'list' || kind === 'link'}
|
||||
{#if items.length > 4}
|
||||
<div
|
||||
class="label-box no-shrink"
|
||||
@ -70,6 +72,29 @@
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
{:else if kind === 'todo'}
|
||||
<div class="flex-row-top flex-wrap flex-gap-0-5">
|
||||
{#each items as value}
|
||||
<TagReferencePresenter attr={undefined} {value} {kind} />
|
||||
{/each}
|
||||
</div>
|
||||
{:else if kind === 'todo-compact'}
|
||||
{#if items.length > 1}
|
||||
<div class="flex-row-top flex-wrap flex-gap-0-5">
|
||||
<TagReferencePresenter attr={undefined} value={items[0]} kind={'todo'} />
|
||||
<div
|
||||
class="todoLabel-plus-container font-medium-11"
|
||||
use:tooltip={{
|
||||
component: LabelsPresenter,
|
||||
props: { value, object, isEditable, kind: 'todo', ignoreFirst: true }
|
||||
}}
|
||||
>
|
||||
+{items.length - 1}
|
||||
</div>
|
||||
</div>
|
||||
{:else if items.length === 1}
|
||||
<TagReferencePresenter attr={undefined} value={items[0]} kind={'todo'} />
|
||||
{/if}
|
||||
{:else}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
@ -96,6 +121,12 @@
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
.todoLabel-plus-container {
|
||||
padding: var(--spacing-0_25) var(--spacing-0_75);
|
||||
color: var(--tag-on-subtle-PorpoiseText);
|
||||
background-color: var(--tag-subtle-PorpoiseBackground);
|
||||
border-radius: var(--extra-small-BorderRadius);
|
||||
}
|
||||
.labels-container {
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
|
@ -66,13 +66,19 @@
|
||||
</div>
|
||||
{:else if kind === 'todo'}
|
||||
<div
|
||||
class="todoLabel-container font-medium-12 overflow-label max-w-40"
|
||||
class="todoLabel-container font-medium-11 overflow-label max-w-40"
|
||||
class:isEditable
|
||||
style:background-color={color.color}
|
||||
use:resizeObserver={(element) => {
|
||||
realWidth = element.clientWidth
|
||||
}}
|
||||
>
|
||||
{value.title}
|
||||
{#if isEditable}
|
||||
<button class="btn-close" on:click|stopPropagation={() => dispatch('remove', value)}>
|
||||
<Icon icon={IconClose} size={'x-small'} />
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
@ -82,6 +88,28 @@
|
||||
padding: var(--spacing-0_25) var(--spacing-0_5);
|
||||
color: var(--global-on-accent-TextColor);
|
||||
border-radius: var(--extra-small-BorderRadius);
|
||||
|
||||
&.isEditable {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-0_5);
|
||||
padding: var(--spacing-0_25) var(--spacing-0_25) var(--spacing-0_25) var(--spacing-0_75);
|
||||
}
|
||||
.btn-close {
|
||||
flex-shrink: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: var(--global-min-Size);
|
||||
height: var(--global-min-Size);
|
||||
color: var(--global-on-accent-TextColor);
|
||||
border: none;
|
||||
border-radius: var(--min-BorderRadius);
|
||||
outline: none;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--button-tertiary-hover-BackgroundColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
.listitems-container {
|
||||
overflow: hidden;
|
||||
@ -117,7 +145,6 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.link-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
@ -1,22 +1,17 @@
|
||||
<script lang="ts">
|
||||
export let size: 'x-small' | 'small' | 'medium' | 'large'
|
||||
// export let fill: string = 'currentColor'
|
||||
export let fill: string = 'currentColor'
|
||||
</script>
|
||||
|
||||
<svg class="svg-{size}" fill="none" viewBox="0 0 16 16">
|
||||
<svg class="svg-{size}" {fill} viewBox="0 0 32 32">
|
||||
<path
|
||||
opacity="0.01"
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M0 16L16 16L16 2.54292e-07L-6.99382e-07 9.53674e-07L0 16Z"
|
||||
d="M10 24.0001C11.1046 24.0001 12 23.1047 12 22.0001C12 20.8956 11.1046 20.0001 10 20.0001C8.89543 20.0001 8 20.8956 8 22.0001C8 23.1047 8.89543 24.0001 10 24.0001ZM10 26.0001C12.2091 26.0001 14 24.2093 14 22.0001C14 19.791 12.2091 18.0001 10 18.0001C7.79086 18.0001 6 19.791 6 22.0001C6 24.2093 7.79086 26.0001 10 26.0001Z"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M6.28273 14.1415L1.86168 9.72159C1.63012 9.49034 1.5 9.17654 1.5 8.84932C1.5 8.5221 1.63012 8.20831 1.86168 7.97706L8.04102 1.79315C8.22858 1.60546 8.48305 1.5 8.74839 1.5L13.5 1.5C14.0523 1.5 14.5 1.94772 14.5 2.5L14.5 7.25015C14.5 7.5154 14.3946 7.76979 14.207 7.95733L8.02156 14.1415C7.54056 14.6195 6.76373 14.6195 6.28273 14.1415Z"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M20.4142 4.58591C19.6332 3.80486 18.3668 3.80486 17.5858 4.58591L4.58579 17.5859C4.19487 17.9768 4 18.4852 4 19.0001V28.0001H13C13.5149 28.0001 14.0233 27.8053 14.4142 27.4143L27.4142 14.4143C28.1953 13.6333 28.1953 12.367 27.4142 11.5859L20.4142 4.58591ZM21.8284 3.17169C20.2663 1.6096 17.7337 1.6096 16.1716 3.17169L3.17157 16.1717C2.39052 16.9527 2 17.9764 2 19.0001V28.0001C2 29.1047 2.89543 30.0001 4 30.0001H13C14.0237 30.0001 15.0474 29.6096 15.8284 28.8285L28.8284 15.8285C30.3905 14.2665 30.3905 11.7338 28.8284 10.1717L21.8284 3.17169Z"
|
||||
/>
|
||||
<circle cx="11.4996" cy="4.49961" r="0.9" />
|
||||
</svg>
|
||||
|
@ -16,7 +16,7 @@
|
||||
<script lang="ts">
|
||||
import { Ref, WithLookup } from '@hcengineering/core'
|
||||
import { ProjectType } from '@hcengineering/task'
|
||||
import { Location, resolvedLocationStore } from '@hcengineering/ui'
|
||||
import { Location, resolvedLocationStore, resizeObserver } from '@hcengineering/ui'
|
||||
|
||||
import { onDestroy } from 'svelte'
|
||||
import { typeStore } from '../../'
|
||||
@ -24,6 +24,7 @@
|
||||
|
||||
export let visibleNav: boolean = true
|
||||
|
||||
let visibleSecondNav: boolean = true
|
||||
let type: WithLookup<ProjectType> | undefined
|
||||
let typeId: Ref<ProjectType> | undefined
|
||||
|
||||
@ -38,8 +39,13 @@
|
||||
$: type = typeId !== undefined ? $typeStore.get(typeId) : undefined
|
||||
</script>
|
||||
|
||||
<div class="hulyComponent">
|
||||
<div
|
||||
class="hulyComponent"
|
||||
use:resizeObserver={(element) => {
|
||||
visibleSecondNav = element.clientWidth > 720
|
||||
}}
|
||||
>
|
||||
{#if type !== undefined}
|
||||
<ProjectEditor {type} descriptor={type.$lookup?.descriptor} {visibleNav} on:change />
|
||||
<ProjectEditor {type} descriptor={type.$lookup?.descriptor} {visibleNav} {visibleSecondNav} on:change />
|
||||
{/if}
|
||||
</div>
|
||||
|
@ -61,6 +61,7 @@
|
||||
export let type: ProjectType
|
||||
export let descriptor: ProjectTypeDescriptor | undefined
|
||||
export let visibleNav: boolean = true
|
||||
export let visibleSecondNav: boolean = true
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
@ -223,7 +224,7 @@
|
||||
</svelte:fragment> -->
|
||||
</Header>
|
||||
<div class="hulyComponent-content__container columns">
|
||||
{#if selectedTaskTypeId === undefined}
|
||||
{#if selectedTaskTypeId === undefined && visibleSecondNav}
|
||||
<div class="hulyComponent-content__column">
|
||||
<div class="hulyComponent-content__navHeader">
|
||||
<div class="hulyComponent-content__navHeader-menu">
|
||||
|
@ -57,6 +57,7 @@
|
||||
export let _id: Ref<Issue>
|
||||
export let _class: Ref<Class<Issue>>
|
||||
export let embedded: boolean = false
|
||||
export let kind: 'default' | 'modern' = 'default'
|
||||
|
||||
let lastId: Ref<Doc> = _id
|
||||
const queryClient = createQuery()
|
||||
@ -183,6 +184,7 @@
|
||||
isAside={true}
|
||||
isSub={false}
|
||||
{embedded}
|
||||
{kind}
|
||||
withoutActivity={false}
|
||||
bind:content
|
||||
bind:innerWidth
|
||||
|
@ -83,6 +83,7 @@
|
||||
import TopMenu from './icons/TopMenu.svelte'
|
||||
|
||||
let contentPanel: HTMLElement
|
||||
let replacedPanel: HTMLElement
|
||||
|
||||
const { setTheme } = getContext<{ setTheme: (theme: string) => void }>('theme')
|
||||
|
||||
@ -617,6 +618,9 @@
|
||||
let lastLoc: Location | undefined = undefined
|
||||
|
||||
defineSeparators('workbench', workbenchSeparators)
|
||||
|
||||
let modern: boolean
|
||||
$: modern = ['setting', 'time'].some((app) => currentApplication?.alias === app)
|
||||
</script>
|
||||
|
||||
{#if employee && !employee.active}
|
||||
@ -656,7 +660,7 @@
|
||||
</svg>
|
||||
<div
|
||||
class="workbench-container"
|
||||
class:modern-app={['setting', 'time'].some((app) => currentApplication?.alias === app)}
|
||||
class:modern-app={modern}
|
||||
style:flex-direction={appsDirection === 'horizontal' ? 'column-reverse' : 'row'}
|
||||
>
|
||||
<div class="antiPanel-application {appsDirection}" class:lastDivider={!visibleNav}>
|
||||
@ -811,7 +815,15 @@
|
||||
{/if}
|
||||
<div class="antiPanel-component antiComponent" bind:this={contentPanel}>
|
||||
{#if currentApplication && currentApplication.component}
|
||||
<Component is={currentApplication.component} props={{ currentSpace, visibleNav, navFloat, appsDirection }} />
|
||||
<Component
|
||||
is={currentApplication.component}
|
||||
props={{ currentSpace, visibleNav, navFloat, appsDirection }}
|
||||
on:change={(e) => {
|
||||
if (e.detail?.type === 'replacedPanel' && e.detail?.replacedPanel !== undefined) {
|
||||
replacedPanel = e.detail.replacedPanel
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{:else if specialComponent}
|
||||
<Component
|
||||
is={specialComponent.component}
|
||||
@ -853,12 +865,16 @@
|
||||
</div>
|
||||
<div bind:this={cover} class="cover" />
|
||||
<TooltipInstance />
|
||||
<PanelInstance bind:this={panelInstance} {contentPanel}>
|
||||
<PanelInstance
|
||||
bind:this={panelInstance}
|
||||
contentPanel={modern ? replacedPanel : contentPanel}
|
||||
kind={modern ? 'modern' : 'default'}
|
||||
>
|
||||
<svelte:fragment slot="panel-header">
|
||||
<ActionContext context={{ mode: 'panel' }} />
|
||||
</svelte:fragment>
|
||||
</PanelInstance>
|
||||
<Popup bind:this={popupInstance} {contentPanel}>
|
||||
<Popup bind:this={popupInstance} contentPanel={modern ? replacedPanel : contentPanel}>
|
||||
<svelte:fragment slot="popup-header">
|
||||
<ActionContext context={{ mode: 'popup' }} />
|
||||
</svelte:fragment>
|
||||
|
Loading…
Reference in New Issue
Block a user