Signed-off-by: Alexander Platov <alexander.platov@hardcoreeng.com>
This commit is contained in:
Alexander Platov 2024-07-25 18:16:08 +03:00 committed by GitHub
parent 75c125cc9a
commit 1329fb1d7a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
193 changed files with 2392 additions and 1727 deletions

View File

@ -259,7 +259,7 @@ export function createModel (builder: Builder): void {
core.space.Model,
{
label: calendar.string.Calendar,
icon: calendar.icon.Calendar,
icon: calendar.icon.CalendarView,
component: calendar.component.CalendarView
},
calendar.viewlet.Calendar

View File

@ -181,6 +181,7 @@ export function createModel (builder: Builder): void {
query: {
[documents.mixin.DocumentTemplate]: { $exists: false }
},
icon: documents.icon.Library,
title: documents.string.Documents,
config: [
['effective', documents.string.Effective, {}],

View File

@ -533,6 +533,7 @@ function defineApplication (builder: Builder): void {
component: workbench.component.SpecialView,
componentProps: {
_class: document.class.Teamspace,
icon: view.icon.List,
label: document.string.Teamspaces
},
position: 'top'

View File

@ -657,7 +657,7 @@ function defineApplication (builder: Builder): void {
core.space.Model,
{
label: drive.string.Drive,
icon: drive.icon.Drive,
icon: drive.icon.DriveApplication,
alias: driveId,
hidden: false,
locationResolver: drive.resolver.Location,
@ -667,7 +667,7 @@ function defineApplication (builder: Builder): void {
id: 'browser',
accessLevel: AccountRole.User,
label: drive.string.Drives,
icon: view.icon.List,
icon: drive.icon.Drives,
component: workbench.component.SpecialView,
componentProps: {
_class: drive.class.Drive,

View File

@ -117,7 +117,7 @@ export function createModel (builder: Builder): void {
accessLevel: AccountRole.User,
componentProps: {
_class: lead.mixin.Customer,
icon: lead.icon.Lead,
icon: contact.icon.Person,
label: lead.string.Customers
},
position: 'top'
@ -125,12 +125,13 @@ export function createModel (builder: Builder): void {
{
id: 'funnels',
component: workbench.component.SpecialView,
icon: view.icon.List,
icon: lead.icon.Funnels,
label: lead.string.Funnels,
position: 'bottom',
accessLevel: AccountRole.User,
componentProps: {
_class: lead.class.Funnel,
icon: lead.icon.Funnels,
label: lead.string.Funnels,
createComponent: lead.component.CreateFunnel,
createLabel: lead.string.CreateFunnel

View File

@ -141,7 +141,6 @@ export function createModel (builder: Builder): void {
alias: loveId,
hidden: false,
position: 'top',
modern: true,
component: love.component.Main
},
love.app.Love

View File

@ -455,6 +455,7 @@ function defineApplication (builder: Builder): void {
component: workbench.component.SpecialView,
componentProps: {
_class: products.class.Product,
icon: products.icon.Product,
label: products.string.Products
},
position: 'top'
@ -466,6 +467,7 @@ function defineApplication (builder: Builder): void {
component: workbench.component.SpecialView,
componentProps: {
_class: products.class.ProductVersion,
icon: products.icon.ProductVersion,
label: products.string.ProductVersions
},
position: 'top'

View File

@ -226,6 +226,7 @@ export function createModel (builder: Builder): void {
position: 'event',
componentProps: {
labelTasks: recruit.string.Applications,
icon: recruit.icon.AssignedToMe,
_class: recruit.class.Applicant,
config: [
['assigned', view.string.Assigned, {}],

View File

@ -349,8 +349,7 @@ export function createModel (builder: Builder): void {
icon: setting.icon.Setting,
alias: settingId,
hidden: true,
component: setting.component.Settings,
modern: true
component: setting.component.Settings
},
setting.ids.SettingApp
)

View File

@ -197,7 +197,6 @@ export function createModel (builder: Builder): void {
alias: timeId,
hidden: false,
position: 'top',
modern: true,
component: time.component.Me
},
time.app.Me

View File

@ -324,6 +324,7 @@ function defineApplication (
icon: tracker.icon.MyIssues,
component: tracker.component.MyIssues,
componentProps: {
icon: tracker.icon.MyIssues,
config: [
['assigned', view.string.Assigned, {}],
['active', tracker.string.Active, {}],
@ -341,6 +342,7 @@ function defineApplication (
component: tracker.component.Issues,
componentProps: {
space: undefined,
icon: tracker.icon.Issues,
title: tracker.string.AllIssues,
config: [
['all', tracker.string.All, {}],
@ -360,6 +362,7 @@ function defineApplication (
spaceClass: tracker.class.Project,
componentProps: {
_class: tracker.class.Project,
icon: view.icon.List,
label: tracker.string.AllProjects
}
}
@ -380,6 +383,7 @@ function defineApplication (
icon: tracker.icon.Issues,
component: tracker.component.Issues,
componentProps: {
icon: tracker.icon.Issues,
title: tracker.string.Issues,
config: [
['all', tracker.string.All, {}],

View File

@ -45,9 +45,14 @@
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 let printHeader = true
export let printAside = false
export let printHeader: boolean = true
export let printAside: boolean = false
export let adaptive: 'default' | 'freezeActions' | 'doubleRow' | 'disabled' = 'disabled'
export let hideBefore: boolean = false
export let hideSearch: boolean = true
export let hideActions: boolean = false
export let hideExtra: boolean = false
export let overflowExtra: boolean = false
export function getAside (): string | boolean {
return panel.getAside()
@ -115,13 +120,18 @@
on:close
{allowClose}
{embedded}
{kind}
{floatAside}
bind:useMaxWidth
{isFullSize}
{customAside}
{printHeader}
{printAside}
{adaptive}
{hideBefore}
{hideSearch}
{hideActions}
{hideExtra}
{overflowExtra}
bind:selectedAside
on:select={(result) => {
selectedAside = result.detail
@ -144,7 +154,6 @@
<svelte:fragment slot="utils">
{#if isUtils && $$slots.utils}
<slot name="utils" />
<div class="buttons-divider max-h-7 h-7 mx-2 no-print" />
{/if}
</svelte:fragment>
@ -196,6 +205,10 @@
<slot name="post-utils" />
</svelte:fragment>
<svelte:fragment slot="extra">
<slot name="extra" />
</svelte:fragment>
<svelte:fragment slot="page-header">
<slot name="page-header" />
</svelte:fragment>

View File

@ -31,7 +31,6 @@
import SpaceInfo from './SpaceInfo.svelte'
export let _classes: Ref<Class<Space>>[] = []
export let allowDeselect: boolean = false
export let titleDeselect: IntlString | undefined = undefined
export let placeholder: IntlString = presentation.string.Search
export let placeholderParam: any | undefined = undefined
@ -100,26 +99,21 @@
<div class="box">
{#each shownSpaces as space}
<button
class="menu-item"
class="menu-item justify-between"
on:click={() => {
checkSelected(space)
}}
>
<div class="check pointer-events-none">
<CheckBox checked={isSelected(space)} kind={'primary'} />
</div>
<SpaceInfo size={'medium'} value={space} {iconWithEmoji} {defaultIcon} />
{#if allowDeselect && space._id === selected}
<div class="check pointer-events-none">
{#if titleDeselect}
<div class="clear-mins" use:tooltip={{ label: titleDeselect ?? presentation.string.Deselect }}>
<CheckBox checked circle kind={'primary'} />
</div>
{:else}
<CheckBox checked circle kind={'primary'} />
{/if}
</div>
{/if}
<div class="check ml-0 pointer-events-none">
{#if titleDeselect}
<div class="clear-mins" use:tooltip={{ label: titleDeselect ?? presentation.string.Deselect }}>
<CheckBox checked={isSelected(space)} kind={'primary'} />
</div>
{:else}
<CheckBox checked={isSelected(space)} kind={'primary'} />
{/if}
</div>
</button>
{/each}
</div>

View File

@ -189,7 +189,7 @@ input.search {
.inline-flex { display: inline-flex; }
.flex-grow { flex-grow: 1; }
.flex-no-shrink { flex-shrink: 0; }
.flex-shrink { flex-shrink: 1; }
.flex-shrink { flex-shrink: 1 !important; }
.flex-wrap { flex-wrap: wrap !important; }
.flex-nowrap { flex-wrap: nowrap !important; }
.flex-baseline {
@ -270,7 +270,7 @@ input.search {
flex-direction: column;
align-items: stretch;
}
.justify-between { justify-content: space-between; }
.justify-between { justify-content: space-between !important; }
.justify-start { justify-content: flex-start; }
.justify-end { justify-content: flex-end !important; }
.justify-center { justify-content: center; }
@ -309,6 +309,7 @@ input.search {
.icon {
color: var(--theme-dark-color);
&.primary { color: var(--button-primary-BackgroundColor); }
&.circle {
padding: .25rem;
background-color: var(--avatar-bg-color);
@ -345,7 +346,11 @@ input.search {
margin-left: .75rem;
}
&:hover {
.icon { color: var(--theme-caption-color); }
.icon {
color: var(--theme-caption-color);
&.primary { color: var(--button-primary-hover-BackgroundColor); }
}
.label {
color: var(--theme-caption-color);
@ -483,6 +488,7 @@ input.search {
}
/* Margins & Paddings */
.ml-0 { margin-left: 0 !important; }
.ml-0-5 { margin-left: .125rem; }
.ml-1 { margin-left: .25rem; }
.ml-1-5 { margin-left: .375rem; }
@ -508,6 +514,7 @@ input.search {
.mr-8 { margin-right: 2rem; }
.mr-10 { margin-right: 2.5rem; }
.mr-32 { margin-right: 8rem }
.mt--1 { margin-top: -.25rem; }
.mt-0-5 { margin-top: .125rem; }
.mt-1 { margin-top: .25rem; }
.mt-2 { margin-top: .5rem; }
@ -731,6 +738,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-13 { min-height: 3.25rem; }
.min-h-16 { min-height: 4rem; }
.min-h-30 { min-height: 7.5rem; }
.min-h-60 { min-height: 15rem; }
@ -958,6 +966,7 @@ a.no-line {
&.bordered { border-color: var(--theme-button-border); }
}
.overflow-hidden { overflow: hidden; }
.overflow-x-auto { overflow-x: auto; }
.overflow-y-auto { overflow-y: auto; }
.overflow-x-auto,

View File

@ -53,6 +53,7 @@
--global-popover-hover-BackgroundColor: #1F2737;
--global-popover-BorderColor: #A5BDFF1A;
--global-primary-LinkColor: #4D7FF5;
--global-primary-IconColor: #ffffff;
--global-primary-TextColor: #FFFFFF;
--global-secondary-TextColor: #C1C9D6;
--global-tertiary-TextColor: #8E99AF;
@ -66,6 +67,7 @@
--global-higlight-Color: #F76E53;
--global-accent-SkyText: #B9D1F5;
--global-accent-BackgroundColor: #204DC8;
--global-on-nuance-TextColor: #041d7d;
--global-no-priority-PriorityColor: #8E99AF;
--global-low-PriorityColor: #6493FF;
@ -147,6 +149,7 @@
--global-popover-hover-BackgroundColor: #1F2737;
--global-popover-BorderColor: #A5BDFF26;
--global-primary-LinkColor: #3566E2;
--global-primary-IconColor: #0f121a;
--global-primary-TextColor: #0F121A;
--global-secondary-TextColor: #5A667E;
--global-tertiary-TextColor: #7B879E;
@ -160,6 +163,7 @@
--global-higlight-Color: #F76E53;
--global-accent-SkyText:#B9D1F5;
--global-accent-BackgroundColor: #3566E2;
--global-on-nuance-TextColor: #2553cf;
--global-no-priority-PriorityColor: #7B879E;
--global-low-PriorityColor: #3566E2;

View File

@ -84,7 +84,7 @@
&:hover .btn-icon { color: var(--theme-caption-color); }
&:not(.no-focus):focus {
&:not(.sh-filter) { box-shadow: 0 0 0 2px var(--primary-button-outline); }
&:not(.sh-filter, .regular) { box-shadow: 0 0 0 2px var(--primary-button-outline); }
&.sh-filter { border-color: var(--primary-button-outline); }
}
@ -109,7 +109,8 @@
&.pressed:hover { background-color: var(--theme-button-pressed); }
&:focus {
background-color: var(--theme-button-focused);
border-color: var(--theme-button-focused-border);
outline: 2px solid var(--global-focus-BorderColor);
outline-offset: 2px;
}
&:disabled { background-color: var(--theme-button-disabled); }
&.selected {

View File

@ -25,12 +25,16 @@
.font-bold-14,
.paragraph-regular-14,
.heading-medium-16,
.heading-bold-16,
.heading-ui-H2,
.heading-medium-20,
.heading-bold-20 {
font-family: var(--font-family);
font-style: normal;
color: var(--global-primary-TextColor);
&:not(.secondary, .tertiary) { color: var(--global-primary-TextColor); }
&.secondary { color: var(--global-secondary-TextColor); }
&.tertiary { color: var(--global-tertiary-TextColor); }
&:not(.line-height-auto) { line-height: 1rem; }
}
.font-regular-11,
@ -63,15 +67,24 @@
.heading-medium-20 {
font-weight: 500;
}
.heading-ui-H2 {
font-weight: 600;
}
.font-bold-12,
.font-bold-14,
.heading-bold-16,
.heading-bold-20 {
font-weight: 700;
}
.heading-medium-16 {
.heading-medium-16,
.heading-bold-16 {
font-size: 1rem;
line-height: 1.125rem;
}
.heading-ui-H2 {
font-size: 1.125rem;
line-height: 1.25rem;
}
.heading-medium-20,
.heading-bold-20 {
font-size: 1.25rem;
@ -143,13 +156,8 @@
filter: drop-shadow(2px 0 1px rgba(0, 0, 0, .2));
z-index: 450;
&.portrait {
left: 0;
}
&.landscape {
.modern-app & { left: var(--app-panel-width); }
:not(.modern-app) & { left: calc(var(--app-panel-width) + 1px); }
}
&.portrait { left: 0; }
&.landscape { left: var(--app-panel-width); }
}
}
.antiPanel-component {

View File

@ -31,6 +31,14 @@
overflow: hidden;
background-color: var(--theme-comp-header-color); // var(--global-surface-02-BackgroundColor);
}
&.beforeAside {
border-right: none;
border-radius: var(--small-focus-BorderRadius) 0 0 var(--small-focus-BorderRadius);
}
&.aside {
border-left: none;
border-radius: 0 var(--small-focus-BorderRadius) var(--small-focus-BorderRadius) 0;
}
}
.hulyComponent-content,
.hulyComponent-content__container,
@ -44,8 +52,9 @@
min-height: 0;
}
.hulyComponent-content {
flex-shrink: 0;
&:not(.noShrink) {
flex-shrink: 0;
}
&:not(.withoutMaxWidth) {
max-width: 64rem;
}
@ -308,15 +317,45 @@
.hulyHeader-container {
display: flex;
align-items: center;
padding: var(--spacing-1_5) var(--spacing-2);
width: 100%;
height: var(--spacing-6_5);
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);
}
}
&:not(.hideSeparator) {
border-bottom: 1px solid var(--theme-divider-color); // var(--global-surface-02-BorderColor);
}
&.topIndent {
margin-top: 1px;
&.topIndent { margin-top: 1px; }
.hulyHeader-row {
display: flex;
align-items: center;
flex-grow: 1;
padding: var(--spacing-1_5) 0;
min-width: 0;
&:has(.extra.overflow) { padding: 0; }
&:not(.noBorder):first-child {
max-height: var(--spacing-6_5);
border-bottom: 1px solid var(--theme-divider-color);
}
&:nth-child(2) { margin-top: -1px; }
&.between { justify-content: space-between; }
&.reverse { flex-direction: row-reverse; }
&__divider {
margin-top: -1px;
width: 100%;
min-height: 1px;
max-height: 1px;
background-color: var(--theme-divider-color);
}
}
.hulyHeader-button {
display: flex;
@ -347,19 +386,38 @@
.hulyHeader-titleGroup,
.hulyHeader-buttonsGroup {
display: flex;
align-items: center;
min-width: 0;
min-height: 0;
}
.hulyHeader-titleGroup {
flex-grow: 1;
gap: var(--spacing-0_5);
&.withDescription { flex-direction: column; }
&:not(.withDescription) {
align-items: center;
gap: var(--spacing-0_5);
}
}
.hulyHeader-buttonsGroup {
align-items: center;
flex-shrink: 0;
gap: var(--spacing-1);
margin-left: var(--spacing-2);
&.extra {
flex-shrink: 1;
margin-left: var(--spacing-2);
&.overflow {
overflow-x: auto;
margin: 0 -.25rem 0 1rem;
padding: .25rem;
}
}
&.before {
gap: var(--spacing-0_5);
&.freezeBefore { min-width: var(--global-small-Size); }
}
&:not(.before) { gap: var(--spacing-1); }
&__label {
display: flex;
flex-direction: column;
@ -368,8 +426,21 @@
color: var(--global-secondary-TextColor);
}
}
.hulyHotKey-item {
margin-right: .625rem;
.hulyHotKey-item { margin-right: .625rem; }
&.doubleRow {
flex-direction: column;
align-items: stretch;
flex-shrink: 0;
padding: 0 var(--spacing-2);
.hulyHeader-row { min-height: var(--spacing-6_5); }
.hulyHeader-buttonsGroup.search { flex-direction: row-reverse; }
.hulyHeader-buttonsGroup.actions { margin-left: 1rem; }
}
&:not(.doubleRow) {
.hulyHeader-buttonsGroup:not(.before) { margin-left: 1rem; }
.hulyHeader-buttonsGroup.search + .hulyHeader-divider + .hulyHeader-buttonsGroup.actions { margin-left: 0; }
}
}
@ -916,13 +987,13 @@
min-width: 0;
.ac-header {
padding: 0.5rem 2.25rem;
padding: var(--spacing-1) var(--spacing-2);
// height: 3.5rem;
// min-height: 2.5rem;
&:not(.withoutBackground) { background-color: var(--theme-comp-header-color); }
&.caption-height { min-height: 3.25rem; }
&.search-start { padding-left: 1.75rem; }
&.caption-height { min-height: 3.5rem; }
&.search-start { padding-left: var(--spacing-3); }
&.tabs-start { padding: 0 2.25rem; }
&.short {
display: flex;
@ -1382,14 +1453,14 @@
&::-webkit-scrollbar-thumb { background-color: var(--scrollbar-bar-color); }
&.mask-none { mask-image: none }
&.mask-left { mask-image: linear-gradient(to right, rgba(0, 0, 0, 0) 0, rgba(0, 0, 0, 1) 1rem); }
&.mask-right { mask-image: linear-gradient(to left, rgba(0, 0, 0, 0) 0, rgba(0, 0, 0, 1) 1rem); }
&.mask-left { mask-image: linear-gradient(to right, rgba(0, 0, 0, 0) 0, rgba(0, 0, 0, 1) 2rem); }
&.mask-right { mask-image: linear-gradient(to left, rgba(0, 0, 0, 0) 0, rgba(0, 0, 0, 1) 2rem); }
&.mask-both {
mask-image: linear-gradient(
to right,
rgba(0, 0, 0, 0) 0,
rgba(0, 0, 0, 1) 1rem,
rgba(0, 0, 0, 1) calc(100% - 1rem),
rgba(0, 0, 0, 1) 2rem,
rgba(0, 0, 0, 1) calc(100% - 2rem),
rgba(0, 0, 0, 0) 100%
);
}

View File

@ -61,7 +61,7 @@
min-width: 0;
min-height: 0;
}
&.small { padding-bottom: .75rem; }
&.small { padding-bottom: var(--spacing-1_5); }
}
.hulyNavGroup-container {
@ -291,8 +291,13 @@
height: 100%;
min-width: 0;
min-height: 0;
border-radius: var(--small-focus-BorderRadius);
&:not(.rowContent) { flex-direction: column; }
.panel-instance & {
background-color: var(--theme-panel-color);
border: 1px solid var(--theme-divider-color);
}
.popupPanel-title {
display: flex;
@ -300,7 +305,7 @@
justify-content: stretch;
align-items: center;
min-width: 0;
min-height: 3.25rem;
min-height: 3.5rem;
background-color: var(--theme-comp-header-color);
border-bottom: 1px solid var(--theme-divider-color);
@ -347,7 +352,7 @@
width: 100%;
height: 100%;
background-color: var(--theme-panel-color);
border: 1px solid var(--theme-divider-color);
// border: 1px solid var(--theme-divider-color);
border-top: none;
border-left: none;
@ -555,15 +560,6 @@
visibility: hidden;
}
&.modern {
border: 1px solid transparent;
border-radius: var(--small-focus-BorderRadius);
.popupPanel-title {
min-height: 3.5rem;
}
}
@media print {
overflow: visible;
}

View File

@ -223,16 +223,15 @@
margin-right: .5rem;
}
.icon,
.check {
div.check {
width: 1rem;
height: 1rem;
color: var(--theme-dark-color);
}
.check {
div.check {
flex-shrink: 0;
margin-left: .5rem;
&.ml-3 { margin-left: .75rem; }
&:not(.ml-0) { margin-left: .5rem; }
}
.color {
width: .875rem;

View File

@ -15,24 +15,17 @@
<script lang="ts">
import { createEventDispatcher, ComponentType } from 'svelte'
import type { Asset, IntlString } from '@hcengineering/platform'
import { AnySvelteComponent } from '../types'
import { AnySvelteComponent, BreadcrumbItem } from '../types'
import ChevronRight from './icons/ChevronRight.svelte'
import Label from './Label.svelte'
import Breadcrumb from './Breadcrumb.svelte'
interface BreadcrumbItem {
icon?: Asset | AnySvelteComponent | ComponentType
iconProps?: any
iconWidth?: string
withoutIconBackground?: boolean
label?: IntlString
title?: string
}
export let items: BreadcrumbItem[]
export let afterLabel: IntlString | undefined = undefined
export let size: 'large' | 'small' = 'large'
export let selected: number | null = null
export let currentOnly: boolean = false
export let hideAfter: boolean = false
const dispatch = createEventDispatcher()
</script>
@ -43,13 +36,13 @@
<Breadcrumb
{...item}
{size}
isCurrent={selected === i}
isCurrent={selected === i || currentOnly}
on:click={() => {
if (selected !== i) dispatch('select', i)
}}
/>
{/each}
{#if afterLabel || $$slots.afterLabel}
{#if (afterLabel || $$slots.afterLabel) && !hideAfter}
<span class="hulyBreadcrumbs-afterLabel font-medium-12">
{#if afterLabel}<Label label={afterLabel} />{/if}
<slot name="afterLabel" />
@ -65,6 +58,7 @@
min-width: 0;
.hulyBreadcrumbs-afterLabel {
max-width: 10rem;
white-space: nowrap;
word-break: break-all;
text-overflow: ellipsis;

View File

@ -63,6 +63,7 @@
export let shrink: number = 0
export let accent: boolean = false
export let noFocus: boolean = false
export let noPrint: boolean = false
export let adaptiveShrink: WidthType | null = null
export let gap: 'medium' | 'large' = 'medium'
export let stopPropagation: boolean = true
@ -128,6 +129,7 @@
class="antiButton {kind} {size} jf-{justify} sh-{shape ?? 'no-shape'} bs-{borderStyle} gap-{gap}"
class:only-icon={iconOnly || adaptive}
class:no-focus={noFocus}
class:no-print={noPrint}
class:accent
class:highlight
class:pressed

View File

@ -34,6 +34,7 @@
export let loading: boolean = false
export let pressed: boolean = false
export let hasMenu: boolean = false
export let noPrint: boolean = false
export let autoFocus: boolean = false
export let type: ButtonBaseType
export let inheritColor: boolean = false
@ -42,6 +43,7 @@
export let element: HTMLButtonElement | undefined = undefined
export let shape: 'rectangle' | 'round' = 'rectangle'
export let id: string | undefined = undefined
export let dataId: string | undefined = undefined
let actualIconSize: IconSize = 'small'
@ -97,7 +99,9 @@
class:inheritFont
class:menu={hasMenu}
class:iconOnly
class:no-print={noPrint}
disabled={loading || disabled}
data-id={dataId}
use:tp={tooltip}
on:click|stopPropagation|preventDefault
on:keydown
@ -120,8 +124,7 @@
flex-shrink: 0;
gap: var(--spacing-1);
border: 1px;
border-style: solid;
border: 1px solid transparent;
&:not(:disabled, .loading) {
cursor: pointer;

View File

@ -26,13 +26,14 @@
export let disabled: boolean = false
export let pressed: boolean = false
export let hasMenu: boolean = false
export let noPrint: boolean = false
export let loading: boolean = false
export let inheritColor: boolean = false
export let tooltip: LabelAndProps | undefined = undefined
export let focusIndex = -1
export let id: string | undefined = undefined
let element: ButtonBase | undefined
export let dataId: string | undefined = undefined
export let element: HTMLButtonElement | undefined = undefined
export function focus () {
element?.focus()
@ -40,7 +41,7 @@
</script>
<ButtonBase
bind:this={element}
bind:element
type={'type-button-icon'}
{kind}
{iconSize}
@ -52,9 +53,11 @@
{inheritColor}
{pressed}
{hasMenu}
{noPrint}
{tooltip}
{focusIndex}
{id}
{dataId}
on:click
on:keydown
/>

View File

@ -32,6 +32,7 @@
export let loading: boolean = false
export let inheritColor: boolean = false
export let noSelection: boolean = false
export let noPrint: boolean = false
export let autoSelectionIfOne: boolean = false
export let tooltip: LabelAndProps | undefined = undefined
@ -41,6 +42,7 @@
export let element: HTMLButtonElement | undefined = undefined
export let focusIndex = -1
export let id: string | undefined = undefined
export let dataId: string | undefined = undefined
let opened: boolean = false
@ -89,11 +91,13 @@
{icon}
{iconProps}
{disabled}
{noPrint}
{loading}
{inheritColor}
pressed={opened}
{focusIndex}
{id}
{dataId}
{tooltip}
on:click={openPopup}
/>

View File

@ -67,6 +67,7 @@
{kind}
{justify}
{disabled}
pressed={opened}
showTooltip={{ label, direction: labelDirection }}
on:click={() => {
if (!opened) {
@ -98,7 +99,12 @@
}
}}
>
<span slot="content" class="overflow-label disabled" class:content-color={selectedItem === undefined}>
<span
slot="content"
class="overflow-label disabled"
class:mr-2={showDropdownIcon}
class:content-color={selectedItem === undefined}
>
{#if $$slots.content}
<slot name="content" />
{:else if Array.isArray(selectedItem)}

View File

@ -13,46 +13,161 @@
// limitations under the License.
-->
<script lang="ts">
import { createEventDispatcher } from 'svelte'
import { IconMaximize, IconMinimize, IconClose, ButtonIcon, deviceOptionsStore as deviceInfo } from '..'
import { createEventDispatcher, onMount, onDestroy } from 'svelte'
import {
IconMaximize,
IconMinimize,
IconClose,
ButtonIcon,
deviceOptionsStore as deviceInfo,
resizeObserver
} from '..'
export let type: 'type-aside' | 'type-popup' | 'type-component' | 'type-panel' = 'type-component'
export let noResize: boolean = false
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 hideBefore: boolean = false
export let hideDescription: boolean = false
export let hideSearch: boolean = false
export let hideActions: boolean = false
export let hideExtra: boolean = false
export let overflowExtra: boolean = false
export let noPrint: boolean = false
export let freezeBefore: boolean = false
const dispatch = createEventDispatcher()
const closeButton: boolean = ['type-popup', 'type-aside'].some((v) => v === type)
let doubleRow: boolean = false
onMount(() => {
if (closeButton) window.addEventListener('keydown', _close)
})
onDestroy(() => {
if (closeButton) window.removeEventListener('keydown', _close)
})
function _close (ev: KeyboardEvent): void {
if (closeButton && ev.key === 'Escape') {
ev.preventDefault()
ev.stopPropagation()
dispatch('close')
}
}
</script>
<div class="hulyHeader-container" class:topIndent={type === 'type-panel'} class:hideSeparator>
{#if type === 'type-component'}
{#if !noResize}
<button
class="hulyHeader-button"
on:click={() => ($deviceInfo.navigator.visible = !$deviceInfo.navigator.visible)}
>
{#if $deviceInfo.navigator.visible}
<IconMaximize size={'small'} />
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div
use:resizeObserver={(element) => {
if (!doubleRow && element.clientWidth <= 768) doubleRow = true
else if (doubleRow && element.clientWidth > 768) doubleRow = false
}}
class="hulyHeader-container"
class:doubleRow={adaptive === 'doubleRow' || (adaptive !== 'disabled' && doubleRow)}
class:topIndent
class:clearPadding={$$slots.description}
class:hideSeparator
class:no-print={noPrint}
>
{#if adaptive === 'doubleRow' || (adaptive !== 'disabled' && doubleRow)}
<div class="hulyHeader-row">
{#if allowFullsize}
<ButtonIcon
icon={$deviceInfo.navigator.visible ? IconMaximize : IconMinimize}
kind={'tertiary'}
size={'small'}
noPrint
on:click={() => ($deviceInfo.navigator.visible = !$deviceInfo.navigator.visible)}
/>
<div class="hulyHeader-divider no-print" />
{/if}
{#if $$slots.beforeTitle && !hideBefore}
<div class="hulyHeader-buttonsGroup before mr-2 no-print" class:freezeBefore>
<slot name="beforeTitle" {doubleRow} />
</div>
{/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>
{#if $$slots.description && !hideDescription}
<div class="hulyHeader-titleGroup"><slot /></div>
<div class="hulyHeader-titleGroup"><slot name="description" /></div>
{:else}
<IconMinimize size={'small'} />
<slot />
{/if}
</button>
{/if}
<slot name="beforeTitle" />
{#if !noResize || $$slots.beforeTitle}
<div class="hulyHeader-divider" />
{/if}
{/if}
<div class="hulyHeader-titleGroup">
<slot />
</div>
{#if $$slots.actions}
<div class="hulyHeader-buttonsGroup">
<slot name="actions" />
</div>
{#if $$slots.actions && !hideActions && (adaptive === 'freezeActions' || adaptive === 'doubleRow')}
<div class="hulyHeader-buttonsGroup actions no-print">
<slot name="actions" {doubleRow} />
</div>
{/if}
{#if closeButton}
{#if type !== 'type-popup'}<div class="hulyHeader-divider no-print" />{/if}
<div class="hulyHotKey-item no-print">Esc</div>
<ButtonIcon icon={IconClose} kind={'tertiary'} size={'small'} noPrint on:click={() => dispatch('close')} />
{/if}
</div>
{/if}
{#if type === 'type-popup' || type === 'type-aside'}
{#if type !== 'type-popup'}<div class="hulyHeader-divider" />{/if}
<div class="hulyHotKey-item">Esc</div>
<ButtonIcon icon={IconClose} kind={'tertiary'} size={'small'} on:click={() => dispatch('close')} />
<!-- <div class="hulyHeader-row__divider" /> -->
<div class="hulyHeader-row no-print" class:between={$$slots.search} class:reverse={!$$slots.search}>
{#if $$slots.search}
<div class="hulyHeader-buttonsGroup search">
<slot name="search" {doubleRow} />
</div>
{/if}
{#if $$slots.actions && !hideActions && adaptive !== 'freezeActions' && adaptive !== 'doubleRow'}
<div class="hulyHeader-buttonsGroup actions">
<slot name="actions" {doubleRow} />
</div>
{/if}
{#if $$slots.extra && !hideExtra && adaptive === 'doubleRow'}
<div class="hulyHeader-buttonsGroup extra" class:overflow={overflowExtra}>
<slot name="extra" {doubleRow} />
</div>
{/if}
</div>
{:else}
{#if allowFullsize}
<ButtonIcon
icon={$deviceInfo.navigator.visible ? IconMaximize : IconMinimize}
kind={'tertiary'}
size={'small'}
noPrint
on:click={() => ($deviceInfo.navigator.visible = !$deviceInfo.navigator.visible)}
/>
<div class="hulyHeader-divider no-print" />
{/if}
{#if $$slots.beforeTitle && !hideBefore}
<div class="hulyHeader-buttonsGroup before mr-2 no-print">
<slot name="beforeTitle" {doubleRow} />
</div>
{/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>
{#if $$slots.description && !hideDescription}
<slot />
<slot name="description" />
{:else}
<slot />
{/if}
</div>
{#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.actions && !hideActions}
<div class="hulyHeader-buttonsGroup actions no-print">
<slot name="actions" {doubleRow} />
</div>
{/if}
{#if closeButton}
{#if type !== 'type-popup'}<div class="hulyHeader-divider no-print" />{/if}
<div class="hulyHotKey-item no-print">Esc</div>
<ButtonIcon icon={IconClose} kind={'tertiary'} size={'small'} noPrint on:click={() => dispatch('close')} />
{/if}
{/if}
</div>

View File

@ -31,7 +31,9 @@
export let okLabel: IntlString = ui.string.Ok
export let padding: string | undefined = undefined
export let hidden: boolean = false
export let noResize: boolean = false
export let allowFullsize: boolean = false
export let hideFooter: boolean = false
export let adaptive: 'default' | 'freezeActions' | 'doubleRow' | 'disabled' = 'disabled'
const dispatch = createEventDispatcher()
@ -54,7 +56,14 @@
<svelte:window on:keydown={onKeyDown} />
<div class="hulyModal-container {type}" class:hidden>
<Header {type} {noResize} on:close={close}>
<Header
{type}
{allowFullsize}
{adaptive}
on:close={close}
hideBefore={!$$slots.beforeTitle}
hideActions={!$$slots.actions}
>
<svelte:fragment slot="beforeTitle">
<slot name="beforeTitle" />
</svelte:fragment>
@ -78,7 +87,7 @@
</Scroller>
</div>
<slot name="afterContent" />
{#if type !== 'type-component'}
{#if type !== 'type-component' && !hideFooter}
<div class="hulyModal-footer">
<ButtonBase
type={'type-button'}

View File

@ -1,9 +1,10 @@
<script lang="ts" generics="T extends string">
import TabList from './TabList.svelte'
import Switcher from './Switcher.svelte'
import { IModeSelector } from '../utils'
export let props: IModeSelector
export let kind: 'separated' | 'separated-free' = 'separated'
export let kind: 'nuance' | 'subtle' = 'nuance'
export let onlyIcons: boolean = false
export let expansion: 'stretch' | 'default' = 'default'
export let padding: string | undefined = undefined
@ -19,15 +20,13 @@
})
</script>
<div class="ac-header withoutBackground tabs-start full" style:padding>
<TabList
items={modeList}
selected={props.mode}
{kind}
{expansion}
adaptiveShrink="sm"
on:select={(result) => {
if (result.detail !== undefined && result.detail.action) result.detail.action()
}}
/>
</div>
<Switcher
name={'modeSelector'}
items={modeList}
selected={props.mode}
{kind}
{onlyIcons}
on:select={(result) => {
if (result.detail !== undefined && result.detail.action) result.detail.action()
}}
/>

View File

@ -16,18 +16,24 @@
export let size: ButtonBaseSize = 'large'
export let shape: 'rectangle' | 'round' = 'rectangle'
export let icon: Asset | AnySvelteComponent | ComponentType | undefined = undefined
export let iconProps: any | undefined = undefined
export let iconSize: IconSize | undefined = undefined
export let disabled: boolean = false
export let loading: boolean = false
export let pressed: boolean = false
export let hasMenu: boolean = false
export let noPrint: boolean = false
export let autoFocus: boolean = false
export let inheritFont: boolean = false
export let focusIndex = -1
export let tooltip: LabelAndProps | undefined = undefined
export let element: HTMLButtonElement | undefined = undefined
export let id: string | undefined = undefined
export let dataId: string | undefined = undefined
</script>
<ButtonBase
bind:element
type={'type-button'}
{title}
{shape}
@ -36,15 +42,19 @@
{kind}
{size}
{icon}
{iconProps}
{iconSize}
{loading}
{disabled}
{pressed}
{hasMenu}
{noPrint}
{inheritFont}
{focusIndex}
{tooltip}
{autoFocus}
{id}
{dataId}
on:click
>
<slot />

View File

@ -213,6 +213,7 @@
margin: 0;
padding: 0;
width: 100%;
text-overflow: ellipsis;
color: var(--input-TextColor);
caret-color: var(--global-focus-BorderColor);
background-color: transparent;

View File

@ -256,7 +256,7 @@
background-color: var(--global-ui-hover-highlight-BackgroundColor);
}
&.selected {
cursor: auto;
cursor: default;
background-color: var(--global-ui-highlight-BackgroundColor);
// &:not(.type-anchor-link) .hulyNavItem-label:not(.description) {

View File

@ -23,7 +23,8 @@
ButtonGroup,
Scroller,
panelSeparators,
ButtonItem
ButtonItem,
Header
} from '../../'
import IconClose from './icons/Close.svelte'
import IconDetails from './icons/Details.svelte'
@ -44,9 +45,14 @@
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 let printHeader = true
export let printAside = false
export let adaptive: 'default' | 'freezeActions' | 'doubleRow' | 'disabled' = 'default'
export let hideBefore: boolean = false
export let hideSearch: boolean = true
export let hideActions: boolean = false
export let hideExtra: boolean = false
export let overflowExtra: boolean = false
export function getAside (): string | boolean {
if (customAside) return selectedAside
@ -147,7 +153,7 @@
<div
bind:this={el}
class="popupPanel panel {kind}"
class="popupPanel panel"
class:withPageHeader={$$slots['page-header'] !== undefined}
class:withPageFooter={$$slots['page-footer'] !== undefined}
class:embedded
@ -156,48 +162,45 @@
checkPanel()
}}
>
<div class="popupPanel-title" class:no-print={!printHeader} class:indent={allowClose}>
{#if allowClose}
<div class="no-print">
<Header
type={'type-panel'}
noPrint={!printHeader}
{adaptive}
{hideBefore}
{hideSearch}
{hideActions}
{hideExtra}
{overflowExtra}
>
<svelte:fragment slot="beforeTitle">
{#if allowClose}
<Button
id={'btnPClose'}
focusIndex={10001}
icon={IconClose}
iconProps={{ size: 'medium' }}
kind={'icon'}
noPrint
on:click={() => {
dispatch('close')
}}
/>
<div class="antiHSpacer x2" />
</div>
{/if}
<div class="popupPanel-title__content">
{#if !withoutTitle}<slot name="title" />{/if}
</div>
<slot name="pre-utils" />
<div class="flex-row-center flex-no-shrink ml-3 no-print">
<slot name="utils" />
{#if $$slots.aside && isAside}
{#if customAside}
<ButtonGroup
items={customAside}
props={{ kind: 'icon', iconProps: { size: 'medium' } }}
bind:selected={selectedAside}
on:select={handleSelectAside}
/>
{:else}
<Button
id={'btnPAside'}
focusIndex={10008}
icon={IconDetails}
iconProps={{ size: 'medium', filled: asideShown }}
kind={'icon'}
selected={asideShown}
on:click={handleAside}
/>
{#if $$slots.beforeTitle}
<div class="hulyHeader-divider short no-print" />
{/if}
{/if}
<slot name="beforeTitle" />
</svelte:fragment>
{#if !withoutTitle}<slot name="title" />{/if}
<svelte:fragment slot="search">
<slot name="search" />
</svelte:fragment>
<svelte:fragment slot="actions">
<slot name="actions" />
<slot name="pre-utils" />
<slot name="utils" />
{#if useMaxWidth !== undefined}
<Button
focusIndex={10009}
@ -224,9 +227,32 @@
}}
/>
{/if}
</div>
<slot name="post-utils" />
</div>
{#if $$slots.aside && isAside}
{#if customAside}
<ButtonGroup
items={customAside}
props={{ kind: 'icon', iconProps: { size: 'medium' } }}
bind:selected={selectedAside}
on:select={handleSelectAside}
/>
{:else}
<Button
id={'btnPAside'}
focusIndex={10008}
icon={IconDetails}
iconProps={{ size: 'medium', filled: asideShown }}
kind={'icon'}
selected={asideShown}
on:click={handleAside}
/>
{/if}
{/if}
<slot name="post-utils" />
</svelte:fragment>
<svelte:fragment slot="extra">
<slot name="extra" />
</svelte:fragment>
</Header>
<div class="popupPanel-body {$deviceInfo.isMobile ? 'mobile' : 'main'}" class:asideShown>
{#if $deviceInfo.isMobile}
<Scroller horizontal padding={'.5rem .75rem'}>

View File

@ -24,7 +24,6 @@
import Spinner from './Spinner.svelte'
export let contentPanel: HTMLElement | undefined
export let kind: 'default' | 'modern' = 'default'
export let embedded: boolean = false
export let readonly: boolean = false
@ -181,7 +180,6 @@
_class={props._class}
rightSection={props.rightSection}
position={props.element}
{kind}
{readonly}
{embedded}
bind:popupOptions={options}

View File

@ -13,39 +13,77 @@
// limitations under the License.
-->
<script lang="ts">
import { createEventDispatcher, onDestroy } from 'svelte'
import type { IntlString } from '@hcengineering/platform'
import { translate } from '@hcengineering/platform'
import { themeStore } from '@hcengineering/theme'
import IconSearch from './icons/Search.svelte'
import IconClose from './icons/Close.svelte'
import plugin from '../plugin'
export let value: string | undefined = undefined
export let placeholder: string
export let placeholder: IntlString = plugin.string.Search
export let placeholderParam: any | undefined = undefined
export let collapsed: boolean = false
let input: HTMLInputElement
let phTranslate: string = ''
$: void translate(placeholder, placeholderParam ?? {}, $themeStore.language).then((res) => {
phTranslate = res
})
$: _search = value
const dispatch = createEventDispatcher()
let timer: any
function restartTimer (): void {
clearTimeout(timer)
timer = setTimeout(() => {
value = _search
dispatch('change', _search)
}, 500)
}
onDestroy(() => {
clearTimeout(timer)
})
</script>
<label class="searchInput-wrapper" class:collapsed class:filled={value && value !== ''}>
<div class="searchInput-icon">
<div><IconSearch size={'small'} /></div>
<IconSearch size={'small'} />
</div>
<input
bind:this={input}
type="text"
class="font-regular-14"
bind:value
{placeholder}
bind:value={_search}
placeholder={phTranslate}
autocomplete="off"
spellcheck="false"
on:change
on:input
on:change={() => {
restartTimer()
}}
on:input={() => {
restartTimer()
}}
on:keydown={(evt) => {
if (evt.key === 'Enter') {
clearTimeout(timer)
value = _search
dispatch('change', _search)
}
}}
/>
<button
class="searchInput-button"
on:click={() => {
value = ''
dispatch('change', '')
input.focus()
}}
>
<div><IconClose size={'small'} /></div>
<IconClose size={'small'} />
</button>
</label>
@ -56,11 +94,11 @@
align-items: center;
align-self: stretch;
padding: 0 var(--spacing-0_5) 0 0;
height: var(--spacing-4);
min-width: var(--spacing-4);
background-color: var(--input-BackgroundColor);
height: var(--global-small-Size);
min-width: var(--global-small-Size);
background-color: var(--theme-button-default); // var(--input-BackgroundColor);
border-radius: var(--small-BorderRadius);
box-shadow: inset 0 0 0 1px var(--input-BorderColor);
box-shadow: inset 0 0 0 1px var(--theme-button-border); // var(--input-BorderColor);
transition: max-width 0.2s;
cursor: text;
@ -75,15 +113,15 @@
border: none;
div {
width: var(--spacing-2);
height: var(--spacing-2);
width: var(--global-min-Size);
height: var(--global-min-Size);
}
}
.searchInput-icon {
margin: 0 var(--spacing-0_5) 0 0;
width: var(--spacing-4);
height: var(--spacing-4);
fill: var(--input-search-IconColor);
width: var(--global-small-Size);
height: var(--global-small-Size);
color: var(--input-search-IconColor);
border-radius: var(--small-BorderRadius);
outline: none;
cursor: text;
@ -95,9 +133,9 @@
}
.searchInput-button {
visibility: hidden;
width: var(--spacing-3);
height: var(--spacing-3);
fill: var(--global-primary-TextColor);
width: var(--global-extra-small-Size);
height: var(--global-extra-small-Size);
color: var(--global-primary-TextColor);
border-radius: var(--extra-small-BorderRadius);
cursor: pointer;
@ -108,10 +146,6 @@
background-color: var(--button-tertiary-active-BackgroundColor);
border-color: var(--button-menu-active-BorderColor);
}
&:focus {
outline: 2px solid var(--global-focus-BorderColor);
outline-offset: 2px;
}
}
input {
@ -128,7 +162,7 @@
appearance: none;
&::placeholder {
color: var(--input-PlaceholderColor);
color: var(--theme-trans-color); // var(--input-PlaceholderColor);
}
&:not(:placeholder-shown) + .searchInput-button {
visibility: visible;
@ -139,25 +173,25 @@
background-color: var(--input-hover-BackgroundColor);
input::placeholder {
color: var(--input-hover-PlaceholderColor);
color: var(--theme-darker-color); // var(--input-hover-PlaceholderColor);
}
}
&:active,
&:focus-within {
padding: 0 var(--spacing-0_5) 0 0;
max-width: 100%;
max-width: 15rem;
background-color: var(--input-BackgroundColor);
outline: 2px solid var(--global-focus-BorderColor);
outline-offset: 2px;
input::placeholder {
color: var(--input-focus-PlaceholderColor);
color: var(--theme-darker-color); // var(--input-focus-PlaceholderColor);
}
}
&.collapsed:not(:focus-within, :active, .filled) {
padding: 0;
max-width: var(--spacing-4);
max-width: var(--global-small-Size);
.searchInput-icon {
cursor: pointer;

View File

@ -0,0 +1,55 @@
<script lang="ts">
//
// © 2023 Hardcore Engineering, Inc. All Rights Reserved.
// Licensed under the Eclipse Public License v2.0 (SPDX: EPL-2.0).
//
import { createEventDispatcher } from 'svelte'
import type { TabItem } from '../types'
import SwitcherBase from './SwitcherBase.svelte'
export let items: TabItem[]
export let selected: string | number = ''
export let kind: 'nuance' | 'subtle' = 'nuance'
export let name: string
export let onlyIcons: boolean = false
const dispatch = createEventDispatcher()
</script>
<div class="switcher-container {kind}">
{#each items as item}
<SwitcherBase
id={item.id}
{name}
{kind}
checked={selected === item.id}
icon={item.icon}
color={item.color}
title={onlyIcons ? undefined : item.label}
label={onlyIcons ? undefined : item.labelIntl}
labelParams={onlyIcons ? undefined : item.labelParams}
tooltip={item.tooltip ? { label: item.tooltip } : undefined}
on:change={() => {
dispatch('select', item)
if (item.action !== undefined) item.action()
}}
/>
{/each}
</div>
<style lang="scss">
.switcher-container {
display: flex;
align-items: center;
flex-wrap: nowrap;
flex-shrink: 0;
min-width: 0;
gap: var(--spacing-0_5);
border-radius: var(--small-BorderRadius);
&.subtle {
background-color: var(--selector-BackgroundColor);
}
}
</style>

View File

@ -0,0 +1,116 @@
<script lang="ts">
//
// © 2023 Hardcore Engineering, Inc. All Rights Reserved.
// Licensed under the Eclipse Public License v2.0 (SPDX: EPL-2.0).
//
import type { Asset, IntlString } from '@hcengineering/platform'
import type { AnySvelteComponent, LabelAndProps } from '../types'
import type { ComponentType } from 'svelte'
import Icon from './Icon.svelte'
import Label from './Label.svelte'
import { tooltip as tp } from '..'
export let id: string | number
export let icon: Asset | AnySvelteComponent | ComponentType | undefined = undefined
export let color: string | undefined = undefined
export let label: IntlString | undefined = undefined
export let labelParams: Record<string, any> = {}
export let title: string | undefined = undefined
export let kind: 'nuance' | 'subtle' = 'nuance'
export let name: string
export let checked: boolean = false
export let tooltip: LabelAndProps | undefined = undefined
$: woTitle = title === undefined && label === undefined
</script>
<label use:tp={tooltip} class="switcher-element__wrapper" data-view={tooltip?.label} data-id={`tab-${id}`}>
<input type="radio" class="switcher" {name} {checked} on:change />
<div class="switcher-element {kind}" class:woTitle>
{#if icon}<div class="icon"><Icon {icon} size={'small'} fill={color} /></div>{/if}
{#if label}<span><Label {label} params={labelParams} /></span>{/if}
{#if title}<span>{title}</span>{/if}
</div>
</label>
<style lang="scss">
.switcher-element,
.switcher-element__wrapper {
display: flex;
min-width: var(--global-small-Size);
min-height: var(--global-small-Size);
}
.switcher-element {
justify-content: center;
align-items: center;
flex-wrap: nowrap;
flex-shrink: 0;
gap: var(--spacing-0_75);
border-radius: var(--small-BorderRadius);
&:not(.woTitle) {
padding: 0 var(--spacing-1_5);
}
&.woTitle {
padding: 0 var(--spacing-1);
}
.icon {
width: var(--spacing-2);
height: var(--spacing-2);
color: var(--global-secondary-IconColor);
}
span {
text-wrap: nowrap;
color: var(--global-secondary-TextColor);
user-select: none;
}
}
.switcher {
overflow: hidden;
position: absolute;
margin: -1px;
padding: 0;
width: 1px;
height: 1px;
border: 0;
clip: rect(0 0 0 0);
&:checked + .switcher-element.nuance {
background-color: var(--global-accent-BackgroundColor);
.icon {
color: var(--global-on-nuance-TextColor);
}
span {
color: var(--global-on-nuance-TextColor);
}
}
&:checked + .switcher-element.subtle {
background-color: var(--global-ui-active-BackgroundColor);
.icon {
color: var(--global-primary-IconColor);
}
span {
color: var(--global-primary-TextColor);
}
}
&:focus + .switcher-element {
box-shadow: 0 0 0 var(--spacing-0_25) var(--global-focus-inset-BorderColor);
outline: var(--spacing-0_25) solid var(--global-focus-BorderColor);
outline-offset: var(--spacing-0_25);
}
}
.switcher-element__wrapper:hover .switcher-element {
background-color: var(--selector-BackgroundColor);
cursor: pointer;
.icon {
color: var(--global-primary-IconColor);
}
span {
color: var(--global-primary-TextColor);
}
}
</style>

View File

@ -21,7 +21,7 @@
import DropdownLabelsIntl from './DropdownLabelsIntl.svelte'
import { Scroller, checkAdaptiveMatching, deviceOptionsStore as deviceInfo } from '..'
export let selected: string | string[] = ''
export let selected: string | number | Array<string | number> = ''
export let multiselect: boolean = false
export let items: TabItem[]
export let kind: 'normal' | 'regular' | 'plain' | 'separated' | 'separated-free' = 'normal'
@ -37,7 +37,7 @@
if (multiselect && selected === '') selected = []
if (selected === '') selected = items[0].id
const getSelected = (id: string, selected: string | string[]): boolean => {
const getSelected = (id: string | number, selected: string | number | Array<string | number>): boolean => {
let res: boolean = false
if (multiselect && Array.isArray(selected)) res = selected.filter((it) => it === id).length > 0
else if (selected === id) res = true

View File

@ -19,8 +19,8 @@
const fill: string = 'currentColor'
</script>
<svg class="svg-{size}" {fill} viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
<rect x="1" y="3.5" width="14" height="1.5" />
<rect x="2.5" y="7.5" width="11" height="1.5" />
<rect x="4.5" y="11.5" width="7" height="1.5" />
<svg class="svg-{size}" {fill} viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
<path
d="M29.9,4.1c-0.1-0.4-0.4-0.8-0.7-1.1c-0.3-0.3-0.7-0.5-1.1-0.7c-0.1,0-0.2-0.1-0.3-0.1H4.2c-0.1,0-0.2,0-0.3,0.1C3.5,2.5,3.1,2.7,2.8,3C2.5,3.3,2.3,3.7,2.1,4.1C2,4.5,2,5,2,5.4c0.1,0.4,0.2,0.8,0.5,1.2c0,0,0.1,0.1,0.1,0.1l8.8,9.7v6.9c0,0.3,0.1,0.6,0.4,0.8l7.3,5.4c0.2,0.1,0.4,0.2,0.6,0.2c0.2,0,0.3,0,0.4-0.1c0.3-0.2,0.6-0.5,0.6-0.9V16.4l8.8-9.7c0,0,0.1-0.1,0.1-0.1c0.2-0.4,0.4-0.8,0.5-1.2C30,5,30,4.5,29.9,4.1z M27.9,5.4l-9,9.9c-0.2,0.2-0.3,0.4-0.3,0.7v10.7l-5.3-3.9V16c0-0.2-0.1-0.5-0.3-0.7l-9-9.9C4.1,5.3,4,5.2,4,5.1C4,5,4,4.9,4,4.8c0-0.1,0.1-0.2,0.2-0.3c0.1-0.1,0.1-0.1,0.2-0.1h23.2c0.1,0,0.1,0.1,0.2,0.1c0.1,0.1,0.2,0.2,0.2,0.3C28,4.9,28,5,28,5.1C28,5.2,27.9,5.3,27.9,5.4z"
/>
</svg>

View File

@ -36,6 +36,7 @@ export type {
ButtonItem,
IconSize,
TabItem,
BreadcrumbItem,
DeviceOptions,
TSeparatedItem,
SeparatedItem,
@ -102,6 +103,9 @@ export { default as Row } from './components/Row.svelte'
export { default as EditWithIcon } from './components/EditWithIcon.svelte'
export { default as SearchEdit } from './components/SearchEdit.svelte'
export { default as SearchPicker } from './components/SearchPicker.svelte'
export { default as SearchInput } from './components/SearchInput.svelte'
export { default as Switcher } from './components/Switcher.svelte'
export { default as SwitcherBase } from './components/SwitcherBase.svelte'
export { default as Chip } from './components/Chip.svelte'
export { default as Loading } from './components/Loading.svelte'
export { default as Spinner } from './components/Spinner.svelte'

View File

@ -115,7 +115,7 @@ export interface Tab extends TabBase {
export type TabModel = Tab[]
export interface TabItem {
id: string
id: string | number
label?: string
labelIntl?: IntlString
labelParams?: Record<string, any>
@ -125,6 +125,15 @@ export interface TabItem {
action?: () => void
}
export interface BreadcrumbItem {
icon?: Asset | AnySvelteComponent | ComponentType
iconProps?: any
iconWidth?: string
withoutIconBackground?: boolean
label?: IntlString
title?: string
}
export interface RadioItem {
id?: string
label?: string

View File

@ -174,7 +174,7 @@
<MessageTimestamp date={message.createdOn ?? message.modifiedOn} shortTime />
</span>
{:else}
<div class="min-w-6 mt-1 relative">
<div class="min-w-6 mt-1 relative flex-no-shrink">
{#if $$slots.icon}
<slot name="icon" />
{:else if person}

View File

@ -24,7 +24,7 @@
"FileBrowserFilterIn": "In",
"FileBrowserFilterDate": "Date",
"FileBrowserFilterFileType": "File type",
"FileBrowserSort": "Sort:",
"FileBrowserSort": "Sort",
"FileBrowserSortNewest": "Newest file",
"FileBrowserSortOldest": "Oldest file",
"FileBrowserSortAZ": "A to Z",

View File

@ -24,7 +24,7 @@
"FileBrowserFilterIn": "En",
"FileBrowserFilterDate": "Fecha",
"FileBrowserFilterFileType": "Tipo de archivo",
"FileBrowserSort": "Ordenar:",
"FileBrowserSort": "Ordenar",
"FileBrowserSortNewest": "Archivo más reciente",
"FileBrowserSortOldest": "Archivo más antiguo",
"FileBrowserSortAZ": "A a Z",

View File

@ -24,7 +24,7 @@
"FileBrowserFilterIn": "Dans",
"FileBrowserFilterDate": "Date",
"FileBrowserFilterFileType": "Type de fichier",
"FileBrowserSort": "Trier :",
"FileBrowserSort": "Trier",
"FileBrowserSortNewest": "Fichier le plus récent",
"FileBrowserSortOldest": "Fichier le plus ancien",
"FileBrowserSortAZ": "A à Z",

View File

@ -24,7 +24,7 @@
"FileBrowserFilterIn": "Em",
"FileBrowserFilterDate": "Data",
"FileBrowserFilterFileType": "Tipo de ficheiro",
"FileBrowserSort": "Ordenar:",
"FileBrowserSort": "Ordenar",
"FileBrowserSortNewest": "Ficheiro mais recente",
"FileBrowserSortOldest": "Ficheiro mais antigo",
"FileBrowserSortAZ": "A a Z",

View File

@ -24,7 +24,7 @@
"FileBrowserFilterIn": "В",
"FileBrowserFilterDate": "Дата",
"FileBrowserFilterFileType": "Тип файла",
"FileBrowserSort": "Сортировка:",
"FileBrowserSort": "Сортировка",
"FileBrowserSortNewest": "Самый новый файл",
"FileBrowserSortOldest": "Самый старый файл",
"FileBrowserSortAZ": "От А до Я",

View File

@ -24,7 +24,7 @@
"FileBrowserFilterIn": "在",
"FileBrowserFilterDate": "日期",
"FileBrowserFilterFileType": "文件类型",
"FileBrowserSort": "排序",
"FileBrowserSort": "排序",
"FileBrowserSortNewest": "最新文件",
"FileBrowserSortOldest": "最旧文件",
"FileBrowserSortAZ": "A 到 Z",

View File

@ -75,9 +75,10 @@
.attachmentRow {
display: flex;
align-items: center;
flex-shrink: 0;
margin: 0.5rem 1.5rem;
padding: 0.5rem;
border: 1px solid var(--divider-color);
border: 1px solid var(--theme-divider-color);
border-radius: 0.5rem;
.eAttachmentRowActions {

View File

@ -121,12 +121,7 @@
</div>
</div>
{/if}
<div class="ac-header full divide search-start">
<div class="ac-header-full small-gap">
<SearchEdit bind:value={search} on:change={() => {}} />
<!-- <ActionIcon icon={IconMoreH} size={'small'} /> -->
<div class="buttons-divider" />
</div>
<div class="hulyHeader-container background-comp-header-color">
<FileBrowserFilters
{requestedSpaceClasses}
{spaceId}
@ -136,13 +131,13 @@
bind:selectedFileTypeId
/>
</div>
<div class="hulyHeader-container justify-between">
<span class="caption-color ml-4">
<Label label={attachment.string.FileBrowserFileCounter} params={{ results: attachments?.length ?? 0 }} />
</span>
<FileBrowserSortMenu bind:selectedSort />
</div>
<div class="group">
<div class="groupHeader">
<div class="eGroupHeaderCount">
<Label label={attachment.string.FileBrowserFileCounter} params={{ results: attachments?.length ?? 0 }} />
</div>
<FileBrowserSortMenu bind:selectedSort />
</div>
{#if isLoading}
<div class="flex-grow">
<Loading />
@ -168,15 +163,4 @@
padding: 1rem 0 1rem 0.5rem;
height: 100%;
}
.groupHeader {
margin: 0 1.5rem 0.75rem 1.5rem;
display: flex;
justify-content: space-between;
.eGroupHeaderCount {
font-size: 0.75rem;
color: var(--caption-color);
}
}
</style>

View File

@ -44,7 +44,7 @@
<SpaceMultiBoxList
_classes={requestedSpaceClasses}
label={attachment.string.FileBrowserFilterIn}
selectedItems={spaceId ? [spaceId] : []}
selectedItems={selectedSpaces}
kind={'ghost'}
size={'medium'}
on:update={(evt) => {

View File

@ -1,6 +1,6 @@
<script lang="ts">
import { IntlString } from '@hcengineering/platform'
import { Label, Menu, showPopup } from '@hcengineering/ui'
import { Label, Menu, showPopup, ModernButton, IconOptions } from '@hcengineering/ui'
import { FileBrowserSortMode } from '..'
import attachment from '../plugin'
@ -71,15 +71,12 @@
}
</script>
<!-- svelte-ignore a11y-no-static-element-interactions -->
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div class="sortMenu" on:click={(event) => showSortMenu(event)}>
<Label label={attachment.string.FileBrowserSort} />
<Label label={sortModeToString(selectedSort)} />
</div>
<style lang="scss">
.sortMenu {
cursor: pointer;
}
</style>
<ModernButton
icon={IconOptions}
iconSize={'small'}
label={sortModeToString(selectedSort)}
kind={'tertiary'}
size={'small'}
tooltip={{ label: attachment.string.FileBrowserSort }}
on:click={(event) => showSortMenu(event)}
/>

View File

@ -2,6 +2,9 @@
<symbol id="calendar" viewBox="0 0 32 32">
<path fill-rule="evenodd" clip-rule="evenodd" d="M28 24.0044C28 26.2136 26.2091 28 24 28H8C5.79086 28 4 26.2091 4 24V8C4 5.79086 5.79086 4 8 4H10V3C10 2.44772 10.4477 2 11 2C11.5523 2 12 2.44772 12 3V4H19.9995V3C19.9995 2.44772 20.4472 2 20.9995 2C21.5518 2 21.9995 2.44772 21.9995 3V4H24C26.2091 4 28 5.79086 28 8C28 11.5569 28 19.2641 28 24.0044ZM19.9995 6V7C19.9995 7.55228 20.4472 8 20.9995 8C21.5518 8 21.9995 7.55228 21.9995 7V6H24C25.1046 6 26 6.89543 26 8V12H6V8C6 6.89543 6.89543 6 8 6H10V7C10 7.55228 10.4477 8 11 8C11.5523 8 12 7.55228 12 7V6H19.9995ZM6 14V24C6 25.1046 6.89543 26 8 26H24C25.1046 26 26 25.1046 26 24V14H6Z" />
</symbol>
<symbol id="calendarView" viewBox="0 0 32 32">
<path d="M24.7,4.9h-1.9V3c0-0.6-0.4-1-1-1s-1,0.4-1,1v1.9h-9.6V3c0-0.6-0.4-1-1-1s-1,0.4-1,1v1.9H7.3c-2.1,0-3.9,1.7-3.9,3.9v17.3c0,2.1,1.7,3.9,3.9,3.9h17.3c2.1,0,3.9-1.7,3.9-3.9V8.8C28.6,6.6,26.8,4.9,24.7,4.9z M7.3,6.9h1.9v1.9c0,0.6,0.4,1,1,1s1-0.4,1-1V6.9h9.6v1.9c0,0.6,0.4,1,1,1s1-0.4,1-1V6.9h1.9c1,0,1.9,0.8,1.9,1.9v4.8H5.4V8.8C5.4,7.7,6.3,6.9,7.3,6.9z M24.7,28H7.3c-1,0-1.9-0.8-1.9-1.9V15.6h21.1v10.6C26.6,27.2,25.7,28,24.7,28z" />
</symbol>
<symbol id="location" viewBox="0 0 16 16">
<path d="M8,4.6c-1.5,0-2.6,1.2-2.6,2.6S6.5,9.9,8,9.9s2.6-1.2,2.6-2.6S9.5,4.6,8,4.6z M8,8.9c-0.9,0-1.6-0.7-1.6-1.6 c0-0.9,0.7-1.6,1.6-1.6s1.6,0.7,1.6,1.6C9.6,8.2,8.9,8.9,8,8.9z"/>
<path d="M8,1.8c-3,0-5.5,2.5-5.5,5.5c0,1.9,0.9,3.4,2,4.5c1.1,1.1,2.3,1.8,2.9,2.1c0.4,0.2,0.9,0.2,1.3,0c0.6-0.3,1.8-1,2.9-2.1 c1.1-1.1,2-2.6,2-4.5C13.5,4.3,11,1.8,8,1.8z M10.8,11.1c-1,1-2.1,1.7-2.6,2c-0.1,0.1-0.2,0.1-0.3,0c-0.6-0.3-1.7-1-2.6-2 c-1-1-1.7-2.3-1.7-3.8c0-2.5,2-4.5,4.5-4.5s4.5,2,4.5,4.5C12.5,8.8,11.7,10.1,10.8,11.1z"/>

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -19,6 +19,7 @@ import { loadMetadata } from '@hcengineering/platform'
const icons = require('../assets/icons.svg') as string // eslint-disable-line
loadMetadata(calendar.icon, {
Calendar: `${icons}#calendar`,
CalendarView: `${icons}#calendarView`,
Reminder: `${icons}#reminder`,
Notifications: `${icons}#notifications`,
Location: `${icons}#location`,

View File

@ -15,8 +15,18 @@
<script lang="ts">
import { Event } from '@hcengineering/calendar'
import { Class, DocumentQuery, Ref, Space, WithLookup } from '@hcengineering/core'
import { IntlString } from '@hcengineering/platform'
import { AnyComponent, Button, Component, IconAdd, Label, Loading, showPopup } from '@hcengineering/ui'
import { IntlString, Asset } from '@hcengineering/platform'
import {
AnyComponent,
Button,
Component,
IconAdd,
Label,
Loading,
showPopup,
Header,
Breadcrumb
} from '@hcengineering/ui'
import { Viewlet, ViewletPreference } from '@hcengineering/view'
import { ViewletSelector, getViewOptions, viewOptionStore } from '@hcengineering/view-resources'
import calendar from '../plugin'
@ -26,7 +36,7 @@
export let space: Ref<Space> | undefined = undefined
export let query: DocumentQuery<Event> = {}
// export let viewIcon: Asset = calendar.icon.Calendar
export let viewIcon: Asset = calendar.icon.Calendar
export let viewLabel: IntlString = calendar.string.Events
export let createComponent: AnyComponent | undefined = calendar.component.CreateEvent
@ -58,17 +68,19 @@
$: viewOptions = getViewOptions(viewlet, $viewOptionStore)
</script>
<div class="ac-header full divide">
<div class="ac-header__wrap-title mr-3">
<span class="ac-header__title"><Label label={viewLabel} /></span>
</div>
<div class="ac-header-full medium-gap mb-1">
<Header adaptive={'disabled'}>
<svelte:fragment slot="beforeTitle">
<ViewletSelector bind:viewlet bind:loading bind:preference bind:viewlets viewletQuery={{ attachTo: _class }} />
</svelte:fragment>
<Breadcrumb icon={viewIcon} label={viewLabel} size={'large'} isCurrent />
<svelte:fragment slot="actions">
<Button icon={IconAdd} label={createLabel} kind={'primary'} on:click={showCreateDialog} />
</div>
</div>
<div class="flex-col w-full h-full background-comp-header-color">
</svelte:fragment>
</Header>
<div class="flex-col w-full h-full">
{#if viewlet?.$lookup?.descriptor?.component}
{#if loading}
<Loading />

View File

@ -149,6 +149,7 @@ const calendarPlugin = plugin(calendarId, {
},
icon: {
Calendar: '' as Asset,
CalendarView: '' as Asset,
Location: '' as Asset,
Reminder: '' as Asset,
Notifications: '' as Asset,

View File

@ -2,22 +2,34 @@
<symbol id="chunter" viewBox="0 0 32 32">
<path fill-rule="evenodd" clip-rule="evenodd" d="M16 6C9.20948 6 4 10.6272 4 16C4 18.1619 4.82539 20.1782 6.25857 21.8353C6.48585 22.0981 6.56044 22.46 6.45559 22.7913C6.11025 23.8822 5.59859 24.9049 4.96437 25.8562C6.36749 25.6496 7.72455 25.2236 8.98685 24.5982C9.26684 24.4595 9.59558 24.4596 9.87546 24.5986C11.6632 25.4861 13.7558 26 16 26C22.7905 26 28 21.3728 28 16C28 10.6272 22.7905 6 16 6ZM2 16C2 9.2225 8.43112 4 16 4C23.5689 4 30 9.2225 30 16C30 22.7775 23.5689 28 16 28C13.6319 28 11.3944 27.4963 9.43078 26.6016C7.4311 27.5144 5.24057 28 3 28C2.63121 28 2.29235 27.797 2.11833 27.4719C1.94431 27.1467 1.96338 26.7522 2.16795 26.4453L3.37487 24.6349C3.78033 24.0267 4.11535 23.3749 4.37375 22.692C2.88402 20.7925 2 18.4911 2 16Z" />
</symbol>
<symbol id="hashtag" viewBox="0 0 24 24">
<path d="M21.5,8.2h-5.3l0.6-5.2c0-0.4-0.3-0.8-0.7-0.8c-0.4,0-0.8,0.3-0.8,0.7l-0.6,5.3h-4.5l0.6-5.2c0-0.4-0.3-0.8-0.7-0.8 c-0.4,0-0.8,0.3-0.8,0.7L8.7,8.2H3.5C3.1,8.2,2.8,8.6,2.8,9s0.3,0.8,0.8,0.8h5L8,14.2H2.5c-0.4,0-0.8,0.3-0.8,0.8s0.3,0.8,0.8,0.8 h5.3l-0.6,5.2c0,0.4,0.3,0.8,0.7,0.8c0,0,0.1,0,0.1,0c0.4,0,0.7-0.3,0.7-0.7l0.6-5.3h4.5l-0.6,5.2c0,0.4,0.3,0.8,0.7,0.8 c0,0,0.1,0,0.1,0c0.4,0,0.7-0.3,0.7-0.7l0.6-5.3h5.2c0.4,0,0.8-0.3,0.8-0.8s-0.3-0.8-0.8-0.8h-5L16,9.8h5.5c0.4,0,0.8-0.3,0.8-0.8 S21.9,8.2,21.5,8.2z M14,14.2H9.5L10,9.8h4.5L14,14.2z" />
<symbol id="hashtag" viewBox="0 0 32 32">
<path d="M27.4,19.9h-6.4l1.9-7.8h4.5c0.6,0,1-0.4,1-1s-0.4-1-1-1h-4l1.7-6.9c0.1-0.5-0.2-1.1-0.7-1.2c-0.5-0.1-1.1,0.2-1.2,0.7l-1.8,7.4h-7.7l1.7-6.9c0.1-0.5-0.2-1.1-0.7-1.2c-0.5-0.1-1.1,0.2-1.2,0.7l-1.8,7.4H4.6c-0.6,0-1,0.4-1,1s0.4,1,1,1h6.4l-1.9,7.8H4.6c-0.6,0-1,0.4-1,1s0.4,1,1,1h4l-1.7,6.9c-0.1,0.5,0.2,1.1,0.7,1.2c0.1,0,0.2,0,0.2,0c0.4,0,0.9-0.3,1-0.8l1.8-7.4h7.7l-1.7,6.9c-0.1,0.5,0.2,1.1,0.7,1.2c0.1,0,0.2,0,0.2,0c0.4,0,0.9-0.3,1-0.8l1.8-7.4h6.9c0.6,0,1-0.4,1-1S27.9,19.9,27.4,19.9z M11.2,19.9l1.9-7.8h7.7l-1.9,7.8H11.2z" />
</symbol>
<symbol id="lock" viewBox="0 0 16 16">
<path d="M12,7.1h-0.7V5.4c0-1.8-1.5-3.3-3.3-3.3c-1.8,0-3.3,1.5-3.3,3.3v1.7H4c-0.8,0-1.5,0.7-1.5,1.5v4.1c0,0.8,0.7,1.5,1.5,1.5h8 c0.8,0,1.5-0.7,1.5-1.5V8.6C13.5,7.8,12.8,7.1,12,7.1z M5.7,5.4c0-1.2,1-2.3,2.3-2.3s2.3,1,2.3,2.3v1.7H5.7V5.4z M12.5,12.7 c0,0.3-0.2,0.5-0.5,0.5H4c-0.3,0-0.5-0.2-0.5-0.5V8.6c0-0.3,0.2-0.5,0.5-0.5h8c0.3,0,0.5,0.2,0.5,0.5V12.7z"/>
</symbol>
<symbol id="thread" viewBox="0 0 32 32">
<path fill-rule="evenodd" clip-rule="evenodd" d="M16 6C9.20948 6 4 10.6272 4 16C4 18.1619 4.82539 20.1782 6.25857 21.8353C6.48585 22.0981 6.56044 22.46 6.45559 22.7913C6.11025 23.8822 5.59859 24.9049 4.96437 25.8562C6.36749 25.6496 7.72455 25.2236 8.98685 24.5982C9.26684 24.4595 9.59558 24.4596 9.87546 24.5986C11.6632 25.4861 13.7558 26 16 26C22.7905 26 28 21.3728 28 16C28 10.6272 22.7905 6 16 6ZM2 16C2 9.2225 8.43112 4 16 4C23.5689 4 30 9.2225 30 16C30 22.7775 23.5689 28 16 28C13.6319 28 11.3944 27.4963 9.43078 26.6016C7.4311 27.5144 5.24057 28 3 28C2.63121 28 2.29235 27.797 2.11833 27.4719C1.94431 27.1467 1.96338 26.7522 2.16795 26.4453L3.37487 24.6349C3.78033 24.0267 4.11535 23.3749 4.37375 22.692C2.88402 20.7925 2 18.4911 2 16Z"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M27.4,25.4L27.4,25.4c0.2,0,0.4,0,0.6-0.1c0.2-0.1,0.3-0.2,0.5-0.3c0.1-0.1,0.2-0.3,0.3-0.5C29,24.2,29,24,29,23.9v-0.1c0-0.1,0-0.2-0.1-0.4l-1-3.5c0,0,0-0.1,0.1-0.1c0,0,0-0.1,0.1-0.1l0,0c1.2-1.8,1.9-4,1.9-6.2c0-3-1.2-5.8-3.3-7.9C24.4,3.3,21.4,2,18.1,2c-2.7,0-5.4,0.9-7.5,2.6c-2.1,1.7-3.5,4-4.1,6.5c-0.2,0.8-0.3,1.6-0.3,2.4c0,1.5,0.3,3,0.9,4.4c0.6,1.4,1.4,2.7,2.5,3.7c2.2,2.2,5.2,3.4,8.3,3.4c1.2,0,2.8-0.4,3.2-0.5c0.8-0.2,1.6-0.5,1.7-0.5c0.1,0,0.2,0,0.2,0c0.1,0,0.2,0,0.2,0l0,0l3.5,1.3C27.1,25.3,27.2,25.3,27.4,25.4L27.4,25.4z M26.7,23l-2.7-1h0c-0.6-0.2-1.3-0.3-1.9,0c0,0,0,0,0,0c-0.2,0.1-0.8,0.3-1.5,0.5C19.9,22.8,18.7,23,18,23c-5.3,0-9.7-4.3-9.7-9.6c0-0.6,0.1-1.3,0.2-1.9c0.9-4.4,5-7.5,9.7-7.5c2.7,0,5.3,1,7.1,2.9C27,8.7,28,11,28,13.4c0,1.8-0.5,3.6-1.5,5.1c-0.1,0.1-0.1,0.2-0.2,0.3l0,0c-0.3,0.6-0.4,1.1-0.3,1.5L26.7,23z" />
<path d="M3.6,29.7C3.8,29.9,4.2,30,4.5,30h0c0.2,0,0.4,0,0.6-0.1l3.5-1.4l0,0c1.4,0.5,2.8,0.8,4.2,0.8h0c1.9,0,3.8-0.5,5.5-1.5c0.1-0.1,0.2-0.2,0.3-0.3c0.1-0.1,0.1-0.2,0.2-0.4s0-0.3,0-0.4c0-0.1-0.1-0.3-0.1-0.4c-0.1-0.1-0.2-0.2-0.3-0.3c-0.1-0.1-0.2-0.1-0.4-0.2c-0.1,0-0.3,0-0.4,0c-0.1,0-0.3,0.1-0.4,0.1c-1.4,0.8-2.9,1.2-4.5,1.2h0c-1.1,0-2.3-0.2-3.4-0.7c-0.5-0.2-1-0.2-1.5,0l-2.7,1c0.3-1.3,0.5-2.8,0.6-2.9c0.1-0.5-0.1-0.9-0.3-1.3l0,0c-0.8-1.2-1.3-2.7-1.4-4.2c-0.1-1.5,0.3-3,1-4.3c0.1-0.2,0.2-0.5,0.1-0.8C5,14,4.9,13.8,4.6,13.6c-0.2-0.1-0.5-0.2-0.8-0.1c-0.3,0.1-0.5,0.2-0.6,0.5c-0.9,1.6-1.3,3.5-1.3,5.4c0.1,1.9,0.7,3.7,1.7,5.2v0c-0.1,0.5-0.3,1.7-0.5,2.7C3.1,27.7,3.1,28,3,28.2C3,28.5,3,28.8,3.1,29C3.2,29.3,3.3,29.5,3.6,29.7L3.6,29.7z" />
</symbol>
<symbol id="channelbrowser" viewBox="0 0 24 24">
<path d="M12.4,20.2H8.6c-4.3,0-5.9-1.6-5.9-5.8V8.7c0-4.4,1.5-6,5.9-6h5.7c4.4,0,6,1.6,6,6v3.7c0,0.4,0.3,0.7,0.8,0.7 s0.8-0.3,0.8-0.7V8.7c0-5.2-2.2-7.5-7.5-7.5H8.6c-5.2,0-7.4,2.2-7.4,7.5v5.7c0,5.1,2.3,7.3,7.4,7.3h3.8c0.4,0,0.7-0.3,0.7-0.8 S12.8,20.2,12.4,20.2z" />
<path d="M22.2,21.4l-1-1c0,0,0,0-0.1,0c0.4-0.6,0.7-1.4,0.7-2.2c0-2.2-1.7-3.9-3.9-3.9s-4,1.7-4,3.9c0,2.1,1.8,4,4,4 c0.8,0,1.6-0.2,2.2-0.7c0,0,0,0,0,0.1l1,1c0.1,0.1,0.3,0.2,0.5,0.2s0.4-0.1,0.5-0.2C22.5,22.1,22.5,21.7,22.2,21.4z M17.9,20.5 c-1.3,0-2.5-1.2-2.5-2.5c0-1.4,1.1-2.4,2.5-2.4s2.4,1.1,2.4,2.4S19.3,20.5,17.9,20.5z" />
<path d="M16.3,9.5c0.4,0,0.8-0.3,0.8-0.8S16.7,8,16.3,8h-2.9l0.3-2.9c0-0.4-0.3-0.8-0.7-0.8c-0.4,0-0.8,0.3-0.8,0.7 l-0.3,3H9.7L10,5.1c0-0.4-0.3-0.8-0.7-0.8C9,4.2,8.6,4.5,8.6,4.9L8.2,8H5.3C4.9,8,4.5,8.3,4.5,8.7s0.3,0.8,0.8,0.8h2.8l-0.2,2.1 H4.7c-0.4,0-0.8,0.3-0.8,0.8S4.3,13,4.7,13h3l-0.3,2.9c0,0.4,0.3,0.8,0.7,0.8c0,0,0.1,0,0.1,0c0.4,0,0.7-0.3,0.7-0.7l0.3-3h2.2 l-0.3,2.9c0,0.4,0.3,0.8,0.7,0.8c0,0,0.1,0,0.1,0c0.4,0,0.7-0.3,0.7-0.7l0.3-3h2.8c0.4,0,0.8-0.3,0.8-0.8s-0.3-0.8-0.8-0.8H13 l0.2-2.1H16.3z M11.5,11.5H9.3l0.2-2.1h2.2L11.5,11.5z" />
<symbol id="channelbrowser" viewBox="0 0 32 32">
<path d="M27.4,10.1h-4l1.7-6.9c0.1-0.5-0.2-1.1-0.7-1.2c-0.5-0.1-1.1,0.2-1.2,0.7l-1.8,7.4h-7.7l1.7-6.9c0.1-0.5-0.2-1.1-0.7-1.2c-0.5-0.1-1.1,0.2-1.2,0.7l-1.8,7.4H4.6c-0.6,0-1,0.4-1,1s0.4,1,1,1h6.4l-1.9,7.8H4.6c-0.6,0-1,0.4-1,1s0.4,1,1,1h4l-1.7,6.9c-0.1,0.5,0.2,1.1,0.7,1.2c0.1,0,0.2,0,0.2,0c0.4,0,0.9-0.3,1-0.8l1.8-7.4h4.6c0.6,0,1-0.4,1-1s-0.4-1-1-1h-4.1l1.9-7.8h7.7l-0.9,3.6c-0.1,0.5,0.2,1.1,0.7,1.2c0.1,0,0.2,0,0.2,0c0.4,0,0.9-0.3,1-0.8l1-4.1h4.5c0.6,0,1-0.4,1-1S27.9,10.1,27.4,10.1z" />
<path d="M29.7,28.3l-2-2c0.6-0.8,0.9-1.9,0.9-3c0-2.9-2.4-5.3-5.3-5.3S18,20.4,18,23.3s2.4,5.3,5.3,5.3c1.1,0,2.1-0.3,3-0.9l2,2c0.2,0.2,0.5,0.3,0.7,0.3s0.5-0.1,0.7-0.3C30.1,29.3,30.1,28.7,29.7,28.3z M20,23.3c0-1.8,1.5-3.3,3.3-3.3s3.3,1.5,3.3,3.3s-1.5,3.3-3.3,3.3S20,25.1,20,23.3z" />
</symbol>
<symbol id="chunterbrowser" viewBox="0 0 32 32">
<path d="M16,2C9.8,2,3.4,3.8,3.4,7.3V16v8.7C3.4,28.2,9.8,30,16,30c0.6,0,1-0.4,1-1s-0.4-1-1-1c-6.4,0-10.6-2-10.6-3.3V19c2.4,1.5,6.5,2.3,10.6,2.3c0.6,0,1-0.4,1-1s-0.4-1-1-1c-6.4,0-10.6-2-10.6-3.3v-5.7c2.4,1.5,6.5,2.3,10.6,2.3s8.2-0.8,10.6-2.3v4.9c0,0.6,0.4,1,1,1s1-0.4,1-1V7.3C28.6,3.8,22.2,2,16,2z M16,10.7c-6.4,0-10.6-2-10.6-3.3S9.6,4,16,4c6.4,0,10.6,2,10.6,3.3S22.4,10.7,16,10.7z" />
<path d="M29.7,28.3l-2-2c0.6-0.8,0.9-1.9,0.9-3c0-2.9-2.4-5.3-5.3-5.3c-2.9,0-5.3,2.4-5.3,5.3c0,2.9,2.4,5.3,5.3,5.3c1.1,0,2.1-0.3,3-0.9l2,2c0.2,0.2,0.5,0.3,0.7,0.3s0.5-0.1,0.7-0.3C30.1,29.3,30.1,28.7,29.7,28.3z M20,23.3c0-1.8,1.5-3.3,3.3-3.3s3.3,1.5,3.3,3.3s-1.5,3.3-3.3,3.3S20,25.1,20,23.3z" />
</symbol>
<symbol id="copy" viewBox="0 0 12 14">
<path fill-rule="evenodd" clip-rule="evenodd" d="M3 2C3 0.89543 3.89543 0 5 0H10C11.1046 0 12 0.895431 12 2V9C12 10.1046 11.1046 11 10 11H5C3.89543 11 3 10.1046 3 9V2ZM5 1C4.44772 1 4 1.44772 4 2V9C4 9.55228 4.44772 10 5 10H10C10.5523 10 11 9.55229 11 9V2C11 1.44772 10.5523 1 10 1H5Z"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M2 4C1.44772 4 1 4.44772 1 5V12C1 12.5523 1.44772 13 2 13H7C7.55228 13 8 12.5523 8 12V11H9V12C9 13.1046 8.10457 14 7 14H2C0.895431 14 0 13.1046 0 12V5C0 3.89543 0.895431 3 2 3H3V4H2Z" />
</symbol>
<symbol id="messages" viewBox="0 0 32 32">
<path fill-rule="evenodd" clip-rule="evenodd" d="M27.4,25.4L27.4,25.4c0.2,0,0.4,0,0.6-0.1c0.2-0.1,0.3-0.2,0.5-0.3c0.1-0.1,0.2-0.3,0.3-0.5C29,24.2,29,24,29,23.9v-0.1c0-0.1,0-0.2-0.1-0.4l-1-3.5c0,0,0-0.1,0.1-0.1c0,0,0-0.1,0.1-0.1l0,0c1.2-1.8,1.9-4,1.9-6.2c0-3-1.2-5.8-3.3-7.9C24.4,3.3,21.4,2,18.1,2c-2.7,0-5.4,0.9-7.5,2.6c-2.1,1.7-3.5,4-4.1,6.5c-0.2,0.8-0.3,1.6-0.3,2.4c0,1.5,0.3,3,0.9,4.4c0.6,1.4,1.4,2.7,2.5,3.7c2.2,2.2,5.2,3.4,8.3,3.4c1.2,0,2.8-0.4,3.2-0.5c0.8-0.2,1.6-0.5,1.7-0.5c0.1,0,0.2,0,0.2,0c0.1,0,0.2,0,0.2,0l0,0l3.5,1.3C27.1,25.3,27.2,25.3,27.4,25.4L27.4,25.4z M26.7,23l-2.7-1h0c-0.6-0.2-1.3-0.3-1.9,0c0,0,0,0,0,0c-0.2,0.1-0.8,0.3-1.5,0.5C19.9,22.8,18.7,23,18,23c-5.3,0-9.7-4.3-9.7-9.6c0-0.6,0.1-1.3,0.2-1.9c0.9-4.4,5-7.5,9.7-7.5c2.7,0,5.3,1,7.1,2.9C27,8.7,28,11,28,13.4c0,1.8-0.5,3.6-1.5,5.1c-0.1,0.1-0.1,0.2-0.2,0.3l0,0c-0.3,0.6-0.4,1.1-0.3,1.5L26.7,23z" />
<path d="M3.6,29.7C3.8,29.9,4.2,30,4.5,30h0c0.2,0,0.4,0,0.6-0.1l3.5-1.4l0,0c1.4,0.5,2.8,0.8,4.2,0.8h0c1.9,0,3.8-0.5,5.5-1.5c0.1-0.1,0.2-0.2,0.3-0.3c0.1-0.1,0.1-0.2,0.2-0.4s0-0.3,0-0.4c0-0.1-0.1-0.3-0.1-0.4c-0.1-0.1-0.2-0.2-0.3-0.3c-0.1-0.1-0.2-0.1-0.4-0.2c-0.1,0-0.3,0-0.4,0c-0.1,0-0.3,0.1-0.4,0.1c-1.4,0.8-2.9,1.2-4.5,1.2h0c-1.1,0-2.3-0.2-3.4-0.7c-0.5-0.2-1-0.2-1.5,0l-2.7,1c0.3-1.3,0.5-2.8,0.6-2.9c0.1-0.5-0.1-0.9-0.3-1.3l0,0c-0.8-1.2-1.3-2.7-1.4-4.2c-0.1-1.5,0.3-3,1-4.3c0.1-0.2,0.2-0.5,0.1-0.8C5,14,4.9,13.8,4.6,13.6c-0.2-0.1-0.5-0.2-0.8-0.1c-0.3,0.1-0.5,0.2-0.6,0.5c-0.9,1.6-1.3,3.5-1.3,5.4c0.1,1.9,0.7,3.7,1.7,5.2v0c-0.1,0.5-0.3,1.7-0.5,2.7C3.1,27.7,3.1,28,3,28.2C3,28.5,3,28.8,3.1,29C3.2,29.3,3.3,29.5,3.6,29.7L3.6,29.7z" />
</symbol>
<symbol id="bookmarks" viewBox="0 0 32 32">
<path d="M18.2,7.4H8.4c-2.5,0-4.6,2.1-4.6,4.6v17c0,0.4,0.2,0.7,0.5,0.9c0.3,0.2,0.7,0.1,1-0.1l7.9-5.8l7.9,5.8c0.2,0.1,0.4,0.2,0.6,0.2c0.2,0,0.3,0,0.5-0.1c0.3-0.2,0.5-0.5,0.5-0.9V12C22.8,9.4,20.8,7.4,18.2,7.4z M20.8,27l-6.9-5.1c-0.4-0.3-0.8-0.3-1.2,0L5.8,27V12c0-1.4,1.2-2.6,2.6-2.6h9.9c1.4,0,2.6,1.2,2.6,2.6V27z" />
<path d="M23.6,2h-9.9c-0.8,0-1.5,0.2-2.2,0.6c-0.5,0.3-0.7,0.9-0.4,1.4c0.3,0.5,0.9,0.7,1.4,0.4C12.9,4.1,13.3,4,13.8,4h9.9c1.4,0,2.6,1.2,2.6,2.6v17c0,0.6,0.4,1,1,1s1-0.4,1-1v-17C28.2,4.1,26.1,2,23.6,2z" />
</symbol>
</svg>

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 7.8 KiB

View File

@ -23,5 +23,8 @@ loadMetadata(chunter.icon, {
Thread: `${icons}#thread`,
Lock: `${icons}#lock`,
ChannelBrowser: `${icons}#channelbrowser`,
Copy: `${icons}#copy`
ChunterBrowser: `${icons}#chunterbrowser`,
Copy: `${icons}#copy`,
Messages: `${icons}#messages`,
Bookmarks: `${icons}#bookmarks`
})

View File

@ -28,10 +28,10 @@
export let _id: Ref<Doc>
export let _class: Ref<Class<Doc>>
export let object: Doc | undefined
export let allowClose = false
export let canOpen = false
export let withAside = false
export let isAsideShown = false
export let allowClose: boolean = false
export let canOpen: boolean = false
export let withAside: boolean = false
export let isAsideShown: boolean = false
export let filters: Ref<ActivityMessagesFilter>[] = []
const client = getClient()
@ -60,30 +60,23 @@
hierarchy.isDerived(_class, chunter.class.DirectMessage) || hierarchy.isDerived(_class, contact.class.Person)
</script>
<div class="ac-header divide full caption-height">
<Header
bind:filters
{object}
icon={getObjectIcon(_class)}
iconProps={{ value: object, showStatus: true }}
label={title}
intlLabel={chunter.string.Channel}
{description}
titleKind={isPerson ? 'default' : 'breadcrumbs'}
withFilters={!hierarchy.isDerived(_class, chunter.class.ChunterSpace)}
{allowClose}
{canOpen}
{withAside}
{isAsideShown}
on:aside-toggled
on:close
>
<PinnedMessages {_id} {_class} on:select />
</Header>
</div>
<style lang="scss">
.ac-header {
padding: 0.5rem 1rem;
}
</style>
<Header
bind:filters
{object}
icon={getObjectIcon(_class)}
iconProps={{ value: object, showStatus: true }}
label={title}
intlLabel={chunter.string.Channel}
{description}
titleKind={isPerson ? 'default' : 'breadcrumbs'}
withFilters={!hierarchy.isDerived(_class, chunter.class.ChunterSpace)}
{allowClose}
{canOpen}
{withAside}
{isAsideShown}
hideBefore
on:aside-toggled
on:close
>
<PinnedMessages {_id} {_class} on:select />
</Header>

View File

@ -16,6 +16,7 @@
import { Button, eventToHTMLElement, IconFilter, showPopup } from '@hcengineering/ui'
import { Ref } from '@hcengineering/core'
import activity, { ActivityMessagesFilter } from '@hcengineering/activity'
import view from '@hcengineering/view-resources/src/plugin'
import { getClient } from '@hcengineering/presentation'
import { ActivityMessagesFilterPopup } from '@hcengineering/activity-resources'
@ -41,4 +42,11 @@
}
</script>
<Button icon={IconFilter} iconProps={{ size: 'small' }} kind="icon" on:click={handleClick} />
<Button
icon={IconFilter}
kind={'regular'}
size={'medium'}
pressed={selectedFilters[0] !== activity.ids.AllFilter}
showTooltip={{ label: view.string.Filter }}
on:click={handleClick}
/>

View File

@ -37,7 +37,5 @@
</script>
{#if object}
<div class="antiComponent">
<ChannelView {object} {context} embedded allowClose on:close />
</div>
<ChannelView {object} {context} on:close />
{/if}

View File

@ -771,7 +771,7 @@
}
.ref-input {
margin: 1.25rem 1rem;
margin: 1.25rem 1rem 1rem;
}
.overlay {

View File

@ -40,8 +40,7 @@
export let object: Doc
export let context: DocNotifyContext | undefined
export let allowClose = false
export let embedded = false
export let embedded: boolean = false
const client = getClient()
const hierarchy = client.getHierarchy()
@ -94,15 +93,15 @@
}
</script>
<div class="popupPanel panel" class:embedded>
<div class="popupPanel panel">
<ChannelHeader
_id={object._id}
_class={object._class}
{object}
{allowClose}
{withAside}
bind:filters
canOpen={isDocChat}
allowClose={embedded}
{isAsideShown}
on:close
on:select={handleMessageSelect}

View File

@ -19,10 +19,10 @@
Breadcrumbs,
Button,
Icon,
IconClose,
IconDetails,
Label,
SearchEdit
SearchInput,
Header
} from '@hcengineering/ui'
import { createEventDispatcher } from 'svelte'
import view from '@hcengineering/view'
@ -41,102 +41,116 @@
export let label: string | undefined = undefined
export let intlLabel: IntlString | undefined = undefined
export let description: string | undefined = undefined
export let allowClose = false
export let canOpen = false
export let withAside = false
export let isAsideShown = false
export let allowClose: boolean = false
export let allowFullsize: boolean = false
export let canOpen: boolean = false
export let withAside: boolean = false
export let isAsideShown: boolean = false
export let titleKind: 'default' | 'breadcrumbs' = 'default'
export let withFilters = false
export let withFilters: boolean = false
export let filters: Ref<ActivityMessagesFilter>[] = []
export let adaptive: 'default' | 'freezeActions' | 'doubleRow' | 'disabled' = 'default'
export let hideBefore: boolean = false
export let hideActions: boolean = false
const client = getClient()
const dispatch = createEventDispatcher()
let searchValue: string = ''
export let searchValue: string = ''
userSearch.subscribe((v) => (searchValue = v))
</script>
<div class="header ac-header__wrap-title flex-grow">
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div class="ac-header__wrap-title" on:click>
{#if allowClose}
<Button
focusIndex={10001}
icon={IconClose}
iconProps={{ size: 'medium' }}
kind={'icon'}
on:click={() => {
dispatch('close')
}}
/>
<div class="antiHSpacer x2" />
{/if}
<Header
{allowFullsize}
type={allowClose ? 'type-aside' : 'type-component'}
hideBefore={$$slots.default === undefined || hideBefore}
hideActions={!((canOpen && object) || withAside || $$slots.actions) || hideActions}
hideDescription={!description}
adaptive={adaptive !== 'default' ? adaptive : withFilters ? 'freezeActions' : 'disabled'}
on:click
on:close
>
<svelte:fragment slot="beforeTitle">
<slot />
{#if titleKind === 'breadcrumbs'}
<Breadcrumbs
items={[
{
icon,
iconProps,
title: label,
label: label ? undefined : intlLabel
}
]}
/>
{:else}
</svelte:fragment>
{#if titleKind === 'breadcrumbs'}
<Breadcrumbs
items={[
{
icon,
iconProps,
title: label,
label: label ? undefined : intlLabel
}
]}
currentOnly
/>
{:else}
<div class="hulyHeader-titleGroup">
{#if icon}
<div class="ac-header__icon pl-2">
<div class="content-color mr-2 pl-2">
<Icon {icon} size={'small'} {iconProps} />
</div>
{/if}
{#if label}
<span class="title overflow-label heading-medium-16 mr-2">{label}</span>
<span class="secondary-textColor overflow-label heading-medium-16 mr-2">{label}</span>
{:else if intlLabel}
<div class="title overflow-label mr-2">
<div class="secondary-textColor overflow-label mr-2">
<Label label={intlLabel} />
</div>
{/if}
{/if}
</div>
{#if description}
<div class="ac-header__description over-underline" style="flex: 1" title={description}>{description}</div>
</div>
{/if}
</div>
{#if withFilters}
<ChannelMessagesFilter bind:selectedFilters={filters} />
{/if}
{#if canOpen && object}
<Button
icon={view.icon.Open}
iconProps={{ size: 'small' }}
kind={'icon'}
on:click={() => {
if (object) {
openDoc(client.getHierarchy(), object)
}
}}
/>
{/if}
<SearchEdit
value={searchValue}
on:change={(ev) => {
userSearch.set(ev.detail)
if (ev.detail !== '') {
navigateToSpecial('chunterBrowser')
}
}}
/>
{#if withAside}
<Button
icon={IconDetails}
iconProps={{ size: 'medium', filled: isAsideShown }}
kind={'icon'}
selected={isAsideShown}
on:click={() => dispatch('aside-toggled')}
/>
{/if}
<svelte:fragment slot="description">
{#if description}
<div class="overflow-label content-dark-color text-sm pl-2 mt--1" title={description}>{description}</div>
{/if}
</svelte:fragment>
<svelte:fragment slot="search" let:doubleRow>
<SearchInput
collapsed
bind:value={searchValue}
on:change={(ev) => {
userSearch.set(ev.detail)
if (ev.detail !== '') {
navigateToSpecial('chunterBrowser')
}
}}
/>
{#if withFilters}
<ChannelMessagesFilter bind:selectedFilters={filters} />
{/if}
<slot name="search" {doubleRow} />
</svelte:fragment>
<svelte:fragment slot="actions" let:doubleRow>
<slot name="actions" {doubleRow} />
{#if canOpen && object}
<Button
icon={view.icon.Open}
iconProps={{ size: 'small' }}
kind={'icon'}
on:click={() => {
if (object) {
openDoc(client.getHierarchy(), object)
}
}}
/>
{/if}
{#if withAside}
<Button
icon={IconDetails}
iconProps={{ size: 'medium', filled: isAsideShown }}
kind={'icon'}
selected={isAsideShown}
on:click={() => dispatch('aside-toggled')}
/>
{/if}
</svelte:fragment>
</Header>
<style lang="scss">
.title {

View File

@ -63,8 +63,7 @@
</script>
{#if count > 0}
<div class="antiHSpacer x2" />
<ModernButton size={'extra-small'} on:click={openMessagesPopup}>
<ModernButton size={'small'} on:click={openMessagesPopup}>
<Icon icon={view.icon.Pin} size={'x-small'} />
<span class="text-sm"><Label label={chunter.string.PinnedCount} params={{ count }} /></span>
</ModernButton>

View File

@ -13,7 +13,7 @@
// limitations under the License.
-->
<script lang="ts">
import { Doc, Ref, Class } from '@hcengineering/core'
import { Doc, Ref, Class, Space } from '@hcengineering/core'
import { createQuery, getClient } from '@hcengineering/presentation'
import {
Component,
@ -24,11 +24,12 @@
Separator,
Location,
restoreLocation,
deviceOptionsStore as deviceInfo
deviceOptionsStore as deviceInfo,
type AnyComponent
} from '@hcengineering/ui'
import { NavigatorModel, SpecialNavModel } from '@hcengineering/workbench'
import { InboxNotificationsClientImpl } from '@hcengineering/notification-resources'
import { onMount } from 'svelte'
import { onMount, onDestroy } from 'svelte'
import { chunterId } from '@hcengineering/chunter'
import view, { decodeObjectURI } from '@hcengineering/view'
import { parseLinkId, getObjectLinkId } from '@hcengineering/view-resources'
@ -39,6 +40,10 @@
import { SelectChannelEvent } from './types'
import { openChannel } from '../../navigation'
export let currentSpace: Ref<Space> | undefined = undefined
export let asideComponent: AnyComponent | undefined = undefined
export let asideId: string | undefined = undefined
const notificationsClient = InboxNotificationsClientImpl.getClient()
const contextByDocStore = notificationsClient.contextByDoc
const objectQuery = createQuery()
@ -56,6 +61,7 @@
let currentSpecial: SpecialNavModel | undefined
let object: Doc | undefined = undefined
let replacedPanel: HTMLElement
location.subscribe((loc) => {
syncLocation(loc)
@ -137,26 +143,40 @@
defineSeparators('chat', [
{ minSize: 20, maxSize: 40, size: 30, float: 'navigator' },
{ size: 'auto', minSize: 30, maxSize: 'auto', float: undefined }
{ size: 'auto', minSize: 20, maxSize: 'auto' },
{ size: 20, minSize: 20, maxSize: 50, float: 'aside' }
])
onMount(() => {
loadSavedAttachments()
})
$: $deviceInfo.replacedPanel = replacedPanel
onDestroy(() => ($deviceInfo.replacedPanel = undefined))
</script>
<div class="flex-row-top h-full">
<div class="hulyPanels-container">
{#if $deviceInfo.navigator.visible}
<div class="antiPanel-navigator {$deviceInfo.navigator.direction === 'horizontal' ? 'portrait' : 'landscape'}">
<div
class="antiPanel-navigator {$deviceInfo.navigator.direction === 'horizontal'
? 'portrait'
: 'landscape'} border-left"
>
<div class="antiPanel-wrap__content hulyNavPanel-container">
<ChatNavigator {object} {currentSpecial} on:select={handleChannelSelected} />
</div>
<Separator name="chat" float={$deviceInfo.navigator.float ? 'navigator' : true} index={0} />
</div>
<Separator name="chat" float={$deviceInfo.navigator.float} index={0} />
<Separator
name="chat"
float={$deviceInfo.navigator.float}
index={0}
color={'transparent'}
separatorSize={0}
short
/>
{/if}
<div class="antiPanel-component filled w-full">
<div bind:this={replacedPanel} class="hulyComponent" class:beforeAside={asideComponent !== undefined && asideId}>
{#if currentSpecial}
<Component
is={currentSpecial.component}
@ -177,4 +197,10 @@
<ChannelView {object} {context} />
{/if}
</div>
{#if asideComponent !== undefined && asideId}
<Separator name={'chat'} index={1} color={'var(--theme-divider-color)'} separatorSize={1} />
<div class="hulyComponent aside">
<Component is={asideComponent} props={{ currentSpace, _id: asideId }} on:close />
</div>
{/if}
</div>

View File

@ -44,5 +44,5 @@
{#if threadId}
<ThreadView _id={threadId} on:close />
{:else if object}
<ChannelView {object} {context} allowClose embedded on:close />
<ChannelView {object} {context} embedded on:close />
{/if}

View File

@ -73,7 +73,7 @@
}
</script>
<div class="hulyNavPanel-header withButton">
<div class="hulyNavPanel-header" class:withButton={hasAccountRole(getCurrentAccount(), AccountRole.User)}>
<span class="overflow-label">
<Label label={chunter.string.Chat} />
</span>

View File

@ -14,13 +14,16 @@
-->
<script lang="ts">
import attachment from '@hcengineering/attachment'
import chunter from '@hcengineering/chunter'
import { FileBrowser } from '@hcengineering/attachment-resources'
import { AnySvelteComponent, Button, Scroller } from '@hcengineering/ui'
import { Scroller, Switcher } from '@hcengineering/ui'
import type { AnySvelteComponent } from '@hcengineering/ui'
import workbench from '@hcengineering/workbench'
import contact from '@hcengineering/contact-resources/src/plugin'
import contact from '@hcengineering/contact'
import contactPlg from '@hcengineering/contact-resources/src/plugin'
import { EmployeeBrowser } from '@hcengineering/contact-resources'
import MessagesBrowser from './MessagesBrowser.svelte'
import { FilterButton } from '@hcengineering/view-resources'
import { FilterBar, FilterButton } from '@hcengineering/view-resources'
import { Class, Doc, Ref } from '@hcengineering/core'
import { userSearch } from '../../../index'
@ -31,7 +34,9 @@
let userSearch_: string = ''
userSearch.subscribe((v) => (userSearch_ = v))
let searchType: SearchType = SearchType.Messages
const saved = localStorage.getItem('chunter-browser-st')
let searchType: SearchType = saved ? parseInt(saved, 10) : SearchType.Messages
$: localStorage.setItem('chunter-browser-st', searchType.toString())
const components: {
component: AnySvelteComponent
@ -47,66 +52,71 @@
requestedSpaceClasses: [plugin.class.Channel, plugin.class.DirectMessage]
}
},
{ searchType: SearchType.Contacts, component: EmployeeBrowser, filterClass: contact.mixin.Employee }
{ searchType: SearchType.Contacts, component: EmployeeBrowser, filterClass: contactPlg.mixin.Employee }
]
let searchValue: string = ''
</script>
<div class="flex-col h-full">
<div class="ac-header divide full caption-height" style="padding: 0.5rem 1rem">
<Header icon={workbench.icon.Search} intlLabel={plugin.string.ChunterBrowser} titleKind="breadcrumbs" />
</div>
<div class="h-full browser">
<div class="pb-16 component">
<div class="h-full">
{#if components[searchType].component}
<Scroller>
<svelte:component
this={components[searchType].component}
withHeader={false}
bind:search={userSearch_}
{...components[searchType].props}
/>
</Scroller>
{/if}
</div>
</div>
<div class="p-3 bar">
<div class="w-32 flex-center"><FilterButton _class={components[searchType].filterClass} /></div>
<div class="flex-center w-full mr-32 buttons">
<div class="ml-1 p-1 btn">
<Button
label={plugin.string.Messages}
selected={searchType === SearchType.Messages}
kind="ghost"
on:click={() => {
searchType = SearchType.Messages
}}
/>
</div>
<div class="ml-1 p-1 btn">
<Button
label={attachment.string.Files}
kind="ghost"
selected={searchType === SearchType.Files}
on:click={() => {
searchType = SearchType.Files
}}
/>
</div>
<div class="ml-1 p-1 btn">
<Button
kind="ghost"
label={contact.string.Contacts}
selected={searchType === SearchType.Contacts}
on:click={() => {
searchType = SearchType.Contacts
}}
/>
</div>
</div>
</div>
</div>
</div>
<Header
icon={plugin.icon.ChunterBrowser}
intlLabel={plugin.string.ChunterBrowser}
titleKind={'breadcrumbs'}
bind:searchValue
adaptive={'freezeActions'}
>
<svelte:fragment slot="search">
<FilterButton _class={components[searchType].filterClass} />
</svelte:fragment>
<svelte:fragment slot="actions">
<Switcher
name={'browser_group'}
kind={'subtle'}
selected={searchType}
items={[
{
id: SearchType.Messages,
icon: chunter.icon.Messages,
labelIntl: plugin.string.Messages,
tooltip: plugin.string.Messages
},
{
id: SearchType.Files,
icon: attachment.icon.FileBrowser,
labelIntl: attachment.string.Files,
tooltip: attachment.string.Files
},
{
id: SearchType.Contacts,
icon: contact.icon.Contacts,
labelIntl: contactPlg.string.Contacts,
tooltip: contactPlg.string.Contacts
}
]}
on:select={(result) => {
if (result !== undefined && result.detail.id !== undefined) searchType = result.detail.id
}}
/>
</svelte:fragment>
</Header>
{#if components[searchType].filterClass !== undefined}
<FilterBar
_class={components[searchType].filterClass}
space={undefined}
query={{ $search: searchValue }}
hideSaveButtons
/>
{/if}
{#if components[searchType].component}
<Scroller>
<svelte:component
this={components[searchType].component}
withHeader={false}
search={userSearch_}
{...components[searchType].props}
/>
</Scroller>
{/if}
<style lang="scss">
.browser {
@ -114,7 +124,7 @@
display: flex;
justify-content: flex-start;
flex-direction: column-reverse;
background-color: var(--theme-popup-color);
background-color: var(--theme-panel-color);
}
.bar {

View File

@ -76,7 +76,7 @@
on:change={(e) => (resultQuery = e.detail)}
/>
{#if messages.length > 0}
<Scroller>
<Scroller padding={'1rem 0'} bottomPadding={'1rem'}>
{#each messages as message}
<ActivityMessagePresenter
value={message}

View File

@ -19,7 +19,7 @@
import { personAccountByIdStore, personByIdStore } from '@hcengineering/contact-resources'
import { getDisplayTime, IdMap, Ref, WithLookup } from '@hcengineering/core'
import { getClient } from '@hcengineering/presentation'
import { Icon, Label, Scroller } from '@hcengineering/ui'
import { Label, Scroller } from '@hcengineering/ui'
import activity, { ActivityMessage, SavedMessage } from '@hcengineering/activity'
import { ActivityMessagePresenter, savedMessagesStore } from '@hcengineering/activity-resources'
@ -67,62 +67,54 @@
}
</script>
<div class="ac-header full divide caption-height" style="padding: 0.5rem 1rem">
<Header icon={activity.icon.Bookmark} intlLabel={chunter.string.Saved} titleKind="breadcrumbs" />
</div>
<Header icon={chunter.icon.Bookmarks} intlLabel={chunter.string.Saved} titleKind={'breadcrumbs'} />
<div class="body h-full w-full clear-mins">
<Scroller padding={'.75rem .5rem'} bottomPadding={'.75rem'}>
{#if savedMessages.length > 0 || savedAttachments.length > 0}
{#each savedMessages as message}
{#if message.$lookup?.attachedTo}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<Scroller padding={'.75rem .5rem'} bottomPadding={'.75rem'}>
{#if savedMessages.length > 0 || savedAttachments.length > 0}
{#each savedMessages as message}
{#if message.$lookup?.attachedTo}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<ActivityMessagePresenter
value={message.$lookup?.attachedTo}
onClick={() => {
handleMessageClicked(message.$lookup?.attachedTo)
}}
/>
{/if}
{/each}
{#each savedAttachments as attach}
{#if attach.$lookup?.attachedTo}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div
class="attachmentContainer flex-no-shrink clear-mins"
on:click={() => openAttachment(attach.$lookup?.attachedTo)}
>
<AttachmentPreview value={attach.$lookup.attachedTo} isSaved={true} />
<div class="label">
<Label
label={chunter.string.SharedBy}
params={{
name: getName(attach.$lookup.attachedTo, $personAccountByIdStore, $personByIdStore),
time: getDisplayTime(attach.modifiedOn)
}}
/>
</div>
<ActivityMessagePresenter
value={message.$lookup?.attachedTo}
onClick={() => {
handleMessageClicked(message.$lookup?.attachedTo)
}}
/>
{/if}
{/each}
{#each savedAttachments as attach}
{#if attach.$lookup?.attachedTo}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div
class="attachmentContainer flex-no-shrink clear-mins"
on:click={() => openAttachment(attach.$lookup?.attachedTo)}
>
<AttachmentPreview value={attach.$lookup.attachedTo} isSaved={true} />
<div class="label">
<Label
label={chunter.string.SharedBy}
params={{
name: getName(attach.$lookup.attachedTo, $personAccountByIdStore, $personByIdStore),
time: getDisplayTime(attach.modifiedOn)
}}
/>
</div>
{/if}
{/each}
{:else}
<BlankView
icon={activity.icon.Bookmark}
header={chunter.string.EmptySavedHeader}
label={chunter.string.EmptySavedText}
/>
{/if}
</Scroller>
</div>
</div>
{/if}
{/each}
{:else}
<BlankView
icon={activity.icon.Bookmark}
header={chunter.string.EmptySavedHeader}
label={chunter.string.EmptySavedText}
/>
{/if}
</Scroller>
<style lang="scss">
.body {
background-color: var(--theme-panel-color);
}
.attachmentContainer {
cursor: pointer;
padding: 2rem;

View File

@ -30,7 +30,6 @@ import { get, writable } from 'svelte/store'
import view from '@hcengineering/view'
import workbench, { type SpecialNavModel } from '@hcengineering/workbench'
import attachment, { type SavedAttachments } from '@hcengineering/attachment'
import activity from '@hcengineering/activity'
import { InboxNotificationsClientImpl } from '@hcengineering/notification-resources'
import { type Action, showPopup } from '@hcengineering/ui'
import contact, { type PersonAccount } from '@hcengineering/contact'
@ -84,24 +83,25 @@ export const chatSpecials: SpecialNavModel[] = [
{
id: 'saved',
label: chunter.string.Saved,
icon: activity.icon.Bookmark,
icon: chunter.icon.Bookmarks,
position: 'top',
component: chunter.component.SavedMessages
},
{
id: 'chunterBrowser',
label: chunter.string.ChunterBrowser,
icon: view.icon.Database,
icon: chunter.icon.ChunterBrowser,
component: chunter.component.ChunterBrowser,
position: 'top'
},
{
id: 'channels',
label: chunter.string.Channels,
icon: view.icon.List,
icon: chunter.icon.ChannelBrowser,
component: workbench.component.SpecialView,
componentProps: {
_class: chunter.class.Channel,
icon: chunter.icon.ChannelBrowser,
label: chunter.string.Channels,
createLabel: chunter.string.CreateChannel,
createComponent: chunter.component.CreateChannel

View File

@ -15,7 +15,7 @@
<script lang="ts">
import { Doc, Ref } from '@hcengineering/core'
import { createQuery, getClient } from '@hcengineering/presentation'
import { Breadcrumbs, IconClose, Label, location as locationStore } from '@hcengineering/ui'
import { Breadcrumbs, IconClose, Label, location as locationStore, Header } from '@hcengineering/ui'
import { createEventDispatcher, onDestroy } from 'svelte'
import activity, { ActivityMessage, DisplayActivityMessage } from '@hcengineering/activity'
import { getMessageFromLoc, messageInFocus } from '@hcengineering/activity-resources'
@ -105,71 +105,41 @@
}
</script>
<div class="popupPanel panel">
{#if showHeader}
<div class="ac-header divide full caption-height" style="padding: 0.5rem 1rem">
<Breadcrumbs items={getBreadcrumbsItems(channel, message, channelName)} />
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div
class="close"
on:click={() => {
dispatch('close')
}}
>
<IconClose size="medium" />
</div>
</div>
{/if}
{#if showHeader}
<Header type={'type-aside'} adaptive={'disabled'} on:close>
<Breadcrumbs items={getBreadcrumbsItems(channel, message, channelName)} currentOnly />
</Header>
{/if}
<div class="popupPanel-body">
<div class="container">
{#if message && dataProvider !== undefined}
<ChannelScrollView
bind:selectedMessageId
embedded
skipLabels
object={message}
objectId={message._id}
objectClass={message._class}
provider={dataProvider}
>
<svelte:fragment slot="header">
<div class="mt-3">
<ThreadParentMessage {message} />
<div class="hulyComponent-content hulyComponent-content__container noShrink">
{#if message && dataProvider !== undefined}
<ChannelScrollView
bind:selectedMessageId
embedded
skipLabels
object={message}
objectId={message._id}
objectClass={message._class}
provider={dataProvider}
>
<svelte:fragment slot="header">
<div class="mt-3">
<ThreadParentMessage {message} />
</div>
<div class="separator">
{#if message.replies && message.replies > 0}
<div class="label lower">
<Label label={activity.string.RepliesCount} params={{ replies: message.replies }} />
</div>
<div class="separator">
{#if message.replies && message.replies > 0}
<div class="label lower">
<Label label={activity.string.RepliesCount} params={{ replies: message.replies }} />
</div>
{/if}
<div class="line" />
</div>
</svelte:fragment>
</ChannelScrollView>
{/if}
</div>
</div>
{/if}
<div class="line" />
</div>
</svelte:fragment>
</ChannelScrollView>
{/if}
</div>
<style lang="scss">
.container {
display: flex;
flex-direction: column;
width: 100%;
}
.close {
margin-left: 0.75rem;
opacity: 0.4;
cursor: pointer;
&:hover {
opacity: 1;
}
}
.separator {
display: flex;
align-items: center;

View File

@ -44,20 +44,10 @@
)
</script>
<div class="ac-header full divide caption-height" style="padding: 0.5rem 1rem">
<Header icon={chunter.icon.Thread} intlLabel={chunter.string.Threads} titleKind="breadcrumbs" />
</div>
<Header icon={chunter.icon.Thread} intlLabel={chunter.string.Threads} titleKind={'breadcrumbs'} />
<div class="body h-full w-full">
<Scroller padding="0.75rem 0.5rem">
{#each threads as thread}
<ActivityMessagePresenter value={thread} onClick={() => openMessageFromSpecial(thread)} withShowMore={false} />
{/each}
</Scroller>
</div>
<style lang="scss">
.body {
background-color: var(--theme-panel-color);
}
</style>
<Scroller padding="0.75rem 0.5rem">
{#each threads as thread}
<ActivityMessagePresenter value={thread} onClick={() => openMessageFromSpecial(thread)} withShowMore={false} />
{/each}
</Scroller>

View File

@ -86,7 +86,10 @@ export default plugin(chunterId, {
Thread: '' as Asset,
Lock: '' as Asset,
ChannelBrowser: '' as Asset,
Copy: '' as Asset
ChunterBrowser: '' as Asset,
Copy: '' as Asset,
Messages: '' as Asset,
Bookmarks: '' as Asset
},
component: {
DmHeader: '' as AnyComponent,

View File

@ -89,4 +89,10 @@
<path d="M1 3C1 1.89543 1.89543 1 3 1H9C10.1046 1 11 1.89543 11 3V3.5H6C4.61929 3.5 3.5 4.61929 3.5 6V11H3C1.89543 11 1 10.1046 1 9V3Z" />
<path fill-rule="evenodd" clip-rule="evenodd" d="M7 5C5.89543 5 5 5.89543 5 7V13C5 14.1046 5.89543 15 7 15H13C14.1046 15 15 14.1046 15 13V7C15 5.89543 14.1046 5 13 5H7ZM10 10C10.9665 10 11.5 9.2165 11.5 8.25C11.5 7.2835 10.9665 6.5 10 6.5C9.0335 6.5 8.5 7.2835 8.5 8.25C8.5 9.2165 9.0335 10 10 10ZM7 12.5616C7 11.5144 7.9841 10.746 9 11C9.47572 11.7136 10.5243 11.7136 11 11C12.0159 10.746 13 11.5144 13 12.5616V13.0101C13 13.2806 12.7806 13.5 12.5101 13.5H7.48995C7.21936 13.5 7 13.2806 7 13.0101V12.5616Z" />
</symbol>
<symbol id="contacts" viewBox="0 0 32 32">
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.7,5.7c-1.4,0-2.7,0.5-3.5,1.5c-0.9,0.9-1.3,2.3-1.2,3.7c0.2,2.7,2.3,5.1,4.8,5.1c2.5,0,4.6-2.3,4.8-5.1C25.7,8,23.5,5.7,20.7,5.7z M18.5,8.5C18,9,17.7,9.8,17.7,10.8c0.1,2,1.6,3.3,2.9,3.3c1.3,0,2.8-1.3,2.9-3.3c0.1-1.9-1.1-3.2-2.9-3.2C19.8,7.6,19,7.9,18.5,8.5z" />
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.4,24.1c1.1-4.2,5.2-6.3,9.3-6.3c4,0,8.2,2,9.3,6.3c0.2,0.9-0.4,2.1-1.6,2.1H13C11.7,26.3,11.2,25.1,11.4,24.1z M13.3,24.4h14.8c-0.9-3-3.9-4.7-7.4-4.7C17.2,19.7,14.1,21.4,13.3,24.4z" />
<path d="M9.6,16.2c-2.1,0-3.9-1.9-4-4.3c-0.1-1.2,0.3-2.3,1-3.1c0.8-0.8,1.8-1.2,3-1.2c1.2,0,2.2,0.4,3,1.3c0.8,0.8,1.1,1.9,1.1,3.1C13.5,14.3,11.7,16.2,9.6,16.2z M9.6,9.5C9,9.5,8.4,9.7,8,10.1c-0.4,0.4-0.6,1-0.5,1.7c0.1,1.4,1.1,2.5,2.2,2.5s2.1-1.2,2.2-2.5c0-0.7-0.2-1.3-0.6-1.7C10.9,9.7,10.3,9.5,9.6,9.5z" />
<path fill-rule="evenodd" clip-rule="evenodd" d="M2,22.5c0.9-3.5,4.3-5.2,7.6-5.2c1.3,0,2.6,0.2,3.8,0.8c0.5,0.2,0.7,0.8,0.5,1.2c-0.2,0.5-0.8,0.7-1.2,0.5c-0.9-0.4-1.9-0.6-3.1-0.6c-2.6,0-4.9,1.2-5.7,3.4H10c0.5,0,0.9,0.4,0.9,0.9s-0.4,0.9-0.9,0.9H3.5C2.4,24.4,1.8,23.3,2,22.5L2,22.5z" />
</symbol>
</svg>

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 21 KiB

View File

@ -41,6 +41,7 @@ loadMetadata(contact.icon, {
Whatsapp: `${icons}#whatsapp`,
Profile: `${icons}#profile`,
KickUser: `${icons}#kickUser`,
ComponentMembers: `${icons}#componentMembers`
ComponentMembers: `${icons}#componentMembers`,
Contacts: `${icons}#contacts`
})
addStringsLoader(contactId, async (lang: string) => await import(`../lang/${lang}.json`))

View File

@ -29,29 +29,23 @@
$: updateEmployees(resultQuery)
</script>
<div class="container">
<Scroller>
<div>
{#each employees as employee}
<div class="fs-title item">
<EmployeePresenter value={employee} avatarSize="medium" />
</div>
{/each}
<Scroller padding={'var(--spacing-2)'}>
{#each employees as employee}
<div class="fs-title item">
<EmployeePresenter value={employee} avatarSize="medium" />
</div>
</Scroller>
</div>
{/each}
</Scroller>
<style lang="scss">
.container {
border-top: 1px solid var(--divider-color);
}
.item {
color: var(--caption-color);
padding: 0.5rem 2rem;
color: var(--theme-caption-color);
padding: 0.5rem 0.5rem;
border-radius: var(--medium-BorderRadius);
&:hover,
&:focus {
background-color: var(--popup-bg-hover);
background-color: var(--highlight-hover);
}
}
</style>

View File

@ -78,7 +78,7 @@
bind:loading
viewletQuery={{ _id: contact.viewlet.TableMember }}
/>
<ViewletSettingButton kind={'ghost'} bind:viewlet />
<ViewletSettingButton kind={'tertiary'} bind:viewlet />
{#if !readonly}
<Button id={contact.string.AddMember} icon={IconAdd} kind={'ghost'} on:click={createApp} />
{/if}

View File

@ -41,6 +41,6 @@
<Avatar person={value} {size} {icon} name={value.name} on:accent-color {showStatus} account={account?._id} />
<div class="flex-col min-w-0 {size === 'tiny' || size === 'inline' ? 'ml-1' : 'ml-2'}" class:max-w-20={short}>
{#if subtitle}<div class="content-dark-color text-sm">{subtitle}</div>{/if}
<div class="label text-left">{getName(client.getHierarchy(), value)}</div>
<div class="label text-left overflow-label">{getName(client.getHierarchy(), value)}</div>
</div>
</div>

View File

@ -265,7 +265,8 @@ export const contactPlugin = plugin(contactId, {
Whatsapp: '' as Asset,
ComponentMembers: '' as Asset,
Profile: '' as Asset,
KickUser: '' as Asset
KickUser: '' as Asset,
Contacts: '' as Asset
},
space: {
Contacts: '' as Ref<Space>

View File

@ -47,43 +47,43 @@
mode: 'browser'
}}
/>
<div class="antiPanel-component">
<ViewletPanelHeader
viewletQuery={{
attachTo: _class,
descriptor: view.viewlet.Table
}}
bind:viewlet
bind:loading
bind:viewOptions
bind:preference
{_class}
title={document.string.Categories}
{query}
bind:resultQuery
>
<div slot="header-tools">
{#if canCreate}
<Button
icon={IconAdd}
label={document.string.DocumentCategoryCreateLabel}
size="small"
kind="primary"
on:click={showCreateDialog}
/>
{/if}
</div>
</ViewletPanelHeader>
<ViewletPanelHeader
viewletQuery={{
attachTo: _class,
descriptor: view.viewlet.Table
}}
bind:viewlet
bind:loading
bind:viewOptions
bind:preference
{_class}
title={document.string.Categories}
icon={documents.icon.Library}
{query}
bind:resultQuery
hideActions={!canCreate}
>
<svelte:fragment slot="actions">
{#if canCreate}
<Button
icon={IconAdd}
label={document.string.DocumentCategoryCreateLabel}
size="small"
kind="primary"
on:click={showCreateDialog}
/>
{/if}
</svelte:fragment>
</ViewletPanelHeader>
{#if loading}
<Loading />
{:else if viewlet && viewOptions}
<TableBrowser
{_class}
config={preference?.config ?? viewlet.config}
options={viewlet.options}
query={resultQuery}
showNotification
/>
{/if}
</div>
{#if loading}
<Loading />
{:else if viewlet && viewOptions}
<TableBrowser
{_class}
config={preference?.config ?? viewlet.config}
options={viewlet.options}
query={resultQuery}
showNotification
/>
{/if}

View File

@ -76,11 +76,13 @@
bind:viewOptions
bind:preference
{_class}
icon={documents.icon.Library}
title={documents.string.DocumentTemplates}
query={srcQuery}
bind:resultQuery
hideActions={!canAddTemplate}
>
<div slot="header-tools">
<svelte:fragment slot="actions">
{#if canAddTemplate}
<Button
icon={IconAdd}
@ -90,7 +92,7 @@
on:click={showCreateDialog}
/>
{/if}
</div>
</svelte:fragment>
</ViewletPanelHeader>
{#if loading}

View File

@ -1,7 +1,7 @@
<script lang="ts">
import { Document } from '@hcengineering/controlled-documents'
import { Class, DocumentQuery, Ref, Space, WithLookup } from '@hcengineering/core'
import { IntlString } from '@hcengineering/platform'
import type { IntlString, Asset } from '@hcengineering/platform'
import { IModeSelector } from '@hcengineering/ui'
import view, { Viewlet, ViewletPreference, ViewOptions } from '@hcengineering/view'
import { ViewletPanelHeader } from '@hcengineering/view-resources'
@ -12,6 +12,7 @@
export let _class: Ref<Class<Document>> = document.class.Document
export let query: DocumentQuery<Document> = {}
export let title: IntlString
export let icon: Asset | undefined = undefined
export let space: Ref<Space> | undefined = undefined
export let panelWidth: number = 0
export let modeSelectorProps: IModeSelector | undefined = undefined
@ -40,6 +41,8 @@
bind:preference
{_class}
{title}
{icon}
adaptive={'doubleRow'}
{modeSelectorProps}
{query}
bind:resultQuery

View File

@ -15,7 +15,7 @@
<script lang="ts">
import { createEventDispatcher } from 'svelte'
import { Class, DocumentQuery, Ref, Space } from '@hcengineering/core'
import { IntlString } from '@hcengineering/platform'
import type { IntlString, Asset } from '@hcengineering/platform'
import { IModeSelector, resolvedLocationStore } from '@hcengineering/ui'
import documents, { type Document, type DocumentSpace, DocumentState } from '@hcengineering/controlled-documents'
@ -26,6 +26,7 @@
export let _class: Ref<Class<Document>> = document.class.Document
export let query: DocumentQuery<Document> = {}
export let title: IntlString
export let icon: Asset | undefined = undefined
export let space: Ref<Space> | undefined = undefined
export let config: [string, IntlString, object][]
export let panelWidth: number = 0
@ -77,4 +78,4 @@
}
</script>
<Documents query={modifiedQuery} {_class} {title} {space} {panelWidth} {modeSelectorProps} />
<Documents query={modifiedQuery} {_class} {icon} {title} {space} {panelWidth} {modeSelectorProps} />

View File

@ -273,11 +273,13 @@
isSub={false}
selectedAside={$activeRightPanelTab ?? false}
withoutActivity
contentClasses="m0 h-full flex-col"
contentClasses="h-full flex-col"
withoutContentScroll
allowClose={withClose && !embedded}
{embedded}
printHeader={false}
adaptive={'doubleRow'}
overflowExtra
on:close
on:select={(ev) => rightPanelTabChanged(ev.detail)}
>
@ -312,8 +314,8 @@
</button>
</div>
</svelte:fragment>
<svelte:fragment slot="pre-utils">
<div class="flex flex-gap-2 no-print">
<svelte:fragment slot="extra">
<div class="flex flex-gap-1 no-print">
{#if $isProjectEditable}
{#if $isDocumentOwner && !$documentReviewIsActive && !$documentApprovalIsActive}
{#if $canSendForReview}
@ -377,7 +379,7 @@
</div>
</svelte:fragment>
<svelte:fragment slot="post-utils">
<div class="no-print">
<div class="no-print ml-1">
<Button
icon={IconMoreV}
iconProps={{ size: 'medium' }}

View File

@ -22,6 +22,7 @@
<DocumentsContainer
query={resultQuery}
icon={documents.icon.Document}
title={documents.string.MyDocuments}
{config}
on:action={(event) => dispatch('action', event.detail)}

View File

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

View File

@ -63,7 +63,6 @@
export let _id: Ref<Document>
export let readonly: boolean = false
export let embedded: boolean = false
export let kind: 'default' | 'modern' = 'default'
$: locked = doc?.lockedBy != null
$: readonly = $restrictionStore.readonly || locked
@ -240,7 +239,7 @@
useMaxWidth={false}
printHeader={false}
{embedded}
{kind}
adaptive={'default'}
bind:content
bind:innerWidth
floatAside={false}

View File

@ -1,6 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
<symbol id="driveapplication" viewBox="0 0 32 32">
<path d="M27.7,24.8h-8.3c-0.3-1.2-1.3-2.1-2.4-2.4v-1.8c0-0.6-0.4-1-1-1s-1,0.4-1,1v1.8c-1.2,0.3-2.1,1.3-2.4,2.4H4.3c-0.6,0-1,0.4-1,1s0.4,1,1,1h8.3c0.4,1.5,1.8,2.6,3.4,2.6s3-1.1,3.4-2.6h8.3c0.6,0,1-0.4,1-1S28.3,24.8,27.7,24.8z M16,27.3c-0.9,0-1.6-0.7-1.6-1.6s0.7-1.6,1.6-1.6s1.6,0.7,1.6,1.6S16.9,27.3,16,27.3z" />
<path d="M9.1,21.6h1.7c0.6,0,1-0.4,1-1s-0.4-1-1-1H9.1c-2.8,0-5.1-2.2-5.1-4.8s2.3-4.8,5.1-4.8c0.5,0,0.9-0.3,1-0.8c0.4-1.9,2-3.5,4.1-4.2c2.1-0.6,4.5-0.2,6.2,1.1c1.6,1.3,2.4,3.3,1.9,5.2c-0.1,0.3,0,0.6,0.2,0.8c0.2,0.2,0.5,0.4,0.8,0.4h1.3c1.9,0,3.5,1.6,3.5,3.5s-1.6,3.5-3.5,3.5h-3.3c-0.6,0-1,0.4-1,1s0.4,1,1,1h3.3c3,0,5.5-2.5,5.5-5.5s-2.5-5.5-5.5-5.5h-0.2c0.1-2.3-0.9-4.5-2.8-6c-2.2-1.7-5.2-2.3-8-1.5C11,3.8,9.1,5.6,8.3,7.9C4.8,8.3,2,11.2,2,14.7C2,18.5,5.2,21.5,9.1,21.6z" />
</symbol>
<symbol id="drive" viewBox="0 0 32 32">
<path d="M16 3C10.7021 3 5 4.252 5 7V25C5 27.748 10.7021 29 16 29C21.2979 29 27 27.748 27 25V7C27 4.252 21.2979 3 16 3ZM16 5C21.7976 5 24.7949 6.4341 24.9968 7C24.7949 7.5659 21.7976 9 16 9C10.1587 9 7.1606 7.5444 7 7.0176V7.0127C7.1606 6.4556 10.1587 5 16 5ZM7 9.4277C9.1279 10.4951 12.6426 11 16 11C19.3574 11 22.8721 10.4951 25 9.4277V12.9873C24.8394 13.5444 21.8413 15 16 15C10.1499 15 7.1509 13.54 7 13V9.4277ZM7 15.4277C9.1279 16.4951 12.6426 17 16 17C19.3574 17 22.8721 16.4951 25 15.4277V18.9873C24.8394 19.5444 21.8413 21 16 21C10.1499 21 7.1509 19.54 7 19V15.4277ZM16 27C10.1499 27 7.1509 25.54 7 25V21.4277C9.1279 22.4951 12.6426 23 16 23C19.3574 23 22.8721 22.4951 25 21.4277V24.9873C24.8394 25.5444 21.8413 27 16 27Z" fill="currentColor"/>
<path d="M29.9,15.6C29.9,15.6,29.9,15.6,29.9,15.6l-4.5-9c-0.3-0.6-0.8-1.1-1.3-1.5c-0.6-0.4-1.2-0.5-1.9-0.5H9.8c-0.7,0-1.3,0.2-1.9,0.5C7.3,5.5,6.9,6,6.6,6.6l-4.5,9c0,0,0,0,0,0C2,15.7,2,15.8,2,16v7.8c0,1,0.4,1.9,1.1,2.5c0.7,0.7,1.6,1.1,2.5,1.1h20.8c1,0,1.9-0.4,2.5-1.1s1.1-1.6,1.1-2.5V16C30,15.8,30,15.7,29.9,15.6z M8.4,7.5C8.4,7.5,8.4,7.5,8.4,7.5C8.5,7.2,8.7,7,9,6.8c0.3-0.2,0.5-0.2,0.8-0.2h12.4c0.3,0,0.6,0.1,0.8,0.2c0.3,0.2,0.5,0.4,0.6,0.7l3.8,7.5H4.6L8.4,7.5z M27.5,24.9c-0.3,0.3-0.7,0.5-1.1,0.5H5.6c-0.4,0-0.8-0.2-1.1-0.5C4.2,24.6,4,24.2,4,23.8V17h24v6.8C28,24.2,27.8,24.6,27.5,24.9z" />
<path d="M8.2,20.2L8.2,20.2c-0.6,0-1,0.4-1,1s0.5,1,1,1s1-0.4,1-1S8.8,20.2,8.2,20.2z" />
<path d="M13.4,20.2L13.4,20.2c-0.6,0-1,0.4-1,1s0.5,1,1,1s1-0.4,1-1S14,20.2,13.4,20.2z" />
</symbol>
<symbol id="drives" viewBox="0 0 32 32">
<path d="M30,11.7V8.8c0-2.9-2.4-5.3-5.3-5.3H7.3C4.4,3.4,2,5.8,2,8.8v2.9C2,13.5,2.9,15,4.2,16C2.9,17,2,18.5,2,20.3v2.9c0,2.9,2.4,5.3,5.3,5.3h17.3c2.9,0,5.3-2.4,5.3-5.3v-2.9c0-1.8-0.9-3.4-2.2-4.3C29.1,15,30,13.5,30,11.7z M28,20.3v2.9c0,1.8-1.5,3.3-3.3,3.3H7.3c-1.8,0-3.3-1.5-3.3-3.3v-2.9C4,18.5,5.5,17,7.3,17h17.3C26.5,17,28,18.5,28,20.3z M28,11.7c0,1.8-1.5,3.3-3.3,3.3H7.3C5.5,15,4,13.5,4,11.7V8.8c0-1.8,1.5-3.3,3.3-3.3h17.3c1.8,0,3.3,1.5,3.3,3.3V11.7z" />
<path d="M8.8,9.2c-0.6,0-1,0.5-1,1s0.4,1,1,1s1-0.4,1-1v0C9.8,9.7,9.3,9.2,8.8,9.2z" />
<path d="M8.8,22.8c0.6,0,1-0.4,1-1v0c0-0.6-0.4-1-1-1s-1,0.5-1,1S8.2,22.8,8.8,22.8z" />
<path d="M23.2,9.2h-8.7c-0.6,0-1,0.4-1,1s0.4,1,1,1h8.7c0.6,0,1-0.4,1-1S23.8,9.2,23.2,9.2z" />
<path d="M14.6,22.8h8.7c0.6,0,1-0.4,1-1s-0.4-1-1-1h-8.7c-0.6,0-1,0.4-1,1S14,22.8,14.6,22.8z" />
</symbol>
<symbol id="grid" viewBox="0 0 20 20">
<path fill-rule="evenodd" clip-rule="evenodd"
@ -12,13 +25,15 @@
<symbol id="folder" viewBox="0 0 32 32">
<path fill-rule="evenodd" clip-rule="evenodd" d="M2 9C2 6.23858 4.23858 4 7 4H10.6716C11.4672 4 12.2303 4.31607 12.7929 4.87868L15.6213 7.70711C15.8089 7.89464 16.0632 8 16.3284 8H25C27.7614 8 30 10.2386 30 13V23C30 25.7614 27.7614 28 25 28H7C4.23858 28 2 25.7614 2 23V9ZM7 6C5.34315 6 4 7.34315 4 9V23C4 24.6569 5.34315 26 7 26H25C26.6569 26 28 24.6569 28 23V13C28 11.3431 26.6569 10 25 10H16.3284C15.5328 10 14.7697 9.68393 14.2071 9.12132L11.3787 6.29289C11.1911 6.10536 10.9368 6 10.6716 6H7Z" fill="currentColor"/>
</symbol>
<symbol id="folder-open" viewBox="0 0 33 32">
<path fill-rule="evenodd" clip-rule="evenodd" d="M28 14V13C28 11.3431 26.6569 10 25 10H16.3284C15.5328 10 14.7697 9.68393 14.2071 9.12132L11.3787 6.29289C11.1911 6.10536 10.9368 6 10.6716 6H7C5.34315 6 4 7.34315 4 9V21L5.17111 16.9011C5.66174 15.1839 7.23128 14 9.01721 14H28Z" fill="currentColor" fill-opacity="0.1"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M7 4C4.23858 4 2 6.23858 2 9V23C2 25.7614 4.23858 28 7 28H26.9828C28.7687 28 30.3383 26.8161 30.8289 25.0989L32.5432 19.0989C33.147 16.9856 31.9391 14.8804 30 14.2152V13C30 10.2386 27.7614 8 25 8H16.3284C16.0632 8 15.8089 7.89464 15.6213 7.70711L12.7929 4.87868C12.2303 4.31607 11.4672 4 10.6716 4H7ZM28 14V13C28 11.3431 26.6569 10 25 10H16.3284C15.5328 10 14.7697 9.68393 14.2071 9.12132L11.3787 6.29289C11.1911 6.10536 10.9368 6 10.6716 6H7C5.34315 6 4 7.34315 4 9V21L5.17111 16.9011C5.66174 15.1839 7.23128 14 9.01721 14H28Z" fill="currentColor"/>
<symbol id="folder-open" viewBox="0 0 32 32">
<path opacity="0.5" d="M4.7,13.9v-5c0-1.8,1.5-3.3,3.3-3.3h3.3c1,0,2,0.5,2.6,1.2l1,1.3h9c1.8,0,3.3,1.5,3.3,3.3v2.5" />
<path opacity="0.75" d="M5.5,13.9h21c1.6,0,2.8,1.6,2.4,3.1l-1.8,6.9c-0.4,1.5-1.7,2.5-3.2,2.5H8.1c-1.5,0-2.8-1-3.2-2.5l-1.8-6.9C2.7,15.5,3.9,13.9,5.5,13.9L5.5,13.9z" />
<path d="M29.3,14.3c-0.3-0.4-0.6-0.7-1-0.9v-2c0-2.4-1.9-4.3-4.3-4.3h-8.5l-0.7-0.9c-0.8-1-2.1-1.6-3.4-1.6H8.1c-2.4,0-4.3,1.9-4.3,4.3v4.5c-0.4,0.2-0.7,0.5-1,0.9c-0.7,0.9-0.9,2-0.6,3L4,24.2c0.5,1.9,2.2,3.2,4.2,3.2h15.7c2,0,3.7-1.3,4.2-3.2l1.8-6.9C30.2,16.3,29.9,15.2,29.3,14.3z M8.1,6.6h3.3c0.7,0,1.4,0.3,1.8,0.9l1,1.3c0.2,0.2,0.5,0.4,0.8,0.4h9c1.3,0,2.3,1,2.3,2.3v1.5H5.7v-4C5.7,7.6,6.8,6.6,8.1,6.6z M27.9,16.8l-1.8,6.9c-0.3,1-1.2,1.7-2.3,1.7H8.1c-1.1,0-2-0.7-2.3-1.7l-1.8-6.9c-0.1-0.5,0-0.9,0.3-1.3c0.3-0.4,0.7-0.6,1.2-0.6h21c0.5,0,0.9,0.2,1.2,0.6C28,15.9,28.1,16.3,27.9,16.8z" />
</symbol>
<symbol id="folder-closed" viewBox="0 0 32 32">
<path fill-rule="evenodd" clip-rule="evenodd" d="M2 9C2 6.23858 4.23858 4 7 4H10.6716C11.4672 4 12.2303 4.31607 12.7929 4.87868L15.6213 7.70711C15.8089 7.89464 16.0632 8 16.3284 8H25C27.7614 8 30 10.2386 30 13V23C30 25.7614 27.7614 28 25 28H7C4.23858 28 2 25.7614 2 23V9ZM7 6C5.34315 6 4 7.34315 4 9V23C4 24.6569 5.34315 26 7 26H25C26.6569 26 28 24.6569 28 23V13C28 11.3431 26.6569 10 25 10H16.3284C15.5328 10 14.7697 9.68393 14.2071 9.12132L11.3787 6.29289C11.1911 6.10536 10.9368 6 10.6716 6H7Z" fill="currentColor"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M7 6C5.34315 6 4 7.34315 4 9V23C4 24.6569 5.34315 26 7 26H25C26.6569 26 28 24.6569 28 23V13C28 11.3431 26.6569 10 25 10H16.3284C15.5328 10 14.7697 9.68393 14.2071 9.12132L11.3787 6.29289C11.1911 6.10536 10.9368 6 10.6716 6H7Z" fill="currentColor" fill-opacity="0.1"/>
<path opacity="0.5" d="M23.9,8.1h-9l-1-1.3c-0.6-0.8-1.6-1.2-2.6-1.2H8.1c-1.8,0-3.3,1.5-3.3,3.3v6.7c0-1.8,1.5-3.3,3.3-3.3h15.8c1.8,0,3.3,1.5,3.3,3.3v-4.2C27.3,9.6,25.8,8.1,23.9,8.1z" />
<path opacity="0.75" d="M23.9,12.2H8.1c-1.8,0-3.3,1.5-3.3,3.3v7.5c0,1.8,1.5,3.3,3.3,3.3h15.8c1.8,0,3.3-1.5,3.3-3.3v-7.5C27.3,13.7,25.8,12.2,23.9,12.2z" />
<path d="M23.9,7.1h-8.5l-0.7-0.9c-0.8-1-2.1-1.6-3.4-1.6H8.1c-2.4,0-4.3,1.9-4.3,4.3v6.7v7.5c0,2.4,1.9,4.3,4.3,4.3h15.8c2.4,0,4.3-1.9,4.3-4.3v-6.9v-0.6v-4.2C28.3,9,26.3,7.1,23.9,7.1z M8.1,6.6h3.3c0.7,0,1.4,0.3,1.8,0.9l1,1.3c0.2,0.2,0.5,0.4,0.8,0.4h9c1.3,0,2.3,1,2.3,2.3v0.5c-0.7-0.4-1.5-0.7-2.3-0.7H8.1c-0.9,0-1.7,0.3-2.3,0.7v-3C5.7,7.6,6.8,6.6,8.1,6.6z M26.3,23.1c0,1.3-1,2.3-2.3,2.3H8.1c-1.3,0-2.3-1-2.3-2.3v-7.5c0-1.3,1-2.3,2.3-2.3h15.8c1.3,0,2.3,1,2.3,2.3v0.6V23.1z" />
</symbol>
<symbol id="download" viewBox="0 0 24 24">
<path d="M18.7,9.6C18.5,6,15.6,3.2,12,3.2C8.4,3.2,5.5,6,5.3,9.5c-2.3,0.7-4,2.9-4,5.5c0,3.2,2.6,5.8,5.8,5.8 c0.5,0,1.1-0.1,1.6-0.2C9,20.4,9.2,20,9.1,19.6S8.6,19,8.2,19.1c-0.4,0.1-0.8,0.2-1.2,0.2c-2.3,0-4.2-1.9-4.2-4.2 c0-2,1.4-3.7,3.3-4.1c0.2,0,0.3-0.1,0.4-0.2c0.2-0.1,0.4-0.4,0.4-0.6c0-2.9,2.4-5.2,5.2-5.2s5.2,2.4,5.2,5.2c0,0.1,0,0.1,0,0.2 c0,0.3,0.2,0.6,0.6,0.7c1.9,0.4,3.3,2.1,3.3,4.1c0,2.3-1.9,4.2-4.2,4.2c-0.4,0-0.8-0.1-1.2-0.2c-0.4-0.1-0.8,0.1-0.9,0.5 c-0.1,0.4,0.1,0.8,0.5,0.9c0.5,0.1,1,0.2,1.6,0.2c3.2,0,5.8-2.6,5.8-5.8C22.6,12.5,21,10.4,18.7,9.6z" />

Before

Width:  |  Height:  |  Size: 6.0 KiB

After

Width:  |  Height:  |  Size: 7.7 KiB

View File

@ -18,7 +18,9 @@ import drive from '@hcengineering/drive'
const icons = require('../assets/icons.svg') as string // eslint-disable-line
loadMetadata(drive.icon, {
DriveApplication: `${icons}#driveapplication`,
Drive: `${icons}#drive`,
Drives: `${icons}#drives`,
Grid: `${icons}#grid`,
File: `${icons}#file`,
Folder: `${icons}#folder`,

View File

@ -16,16 +16,13 @@
import { type Ref } from '@hcengineering/core'
import drive, { type Drive } from '@hcengineering/drive'
import { createQuery } from '@hcengineering/presentation'
import { Panel, Scroller, Button, IconMoreH } from '@hcengineering/ui'
import { DocAttributeBar, showMenu } from '@hcengineering/view-resources'
import { showMenu } from '@hcengineering/view-resources'
import DrivePresenter from './DrivePresenter.svelte'
import FolderBrowser from './FolderBrowser.svelte'
export let _id: Ref<Drive>
export let readonly: boolean = false
export let embedded: boolean = false
export let kind: 'default' | 'modern' = 'default'
export function canClose (): boolean {
return false
@ -40,36 +37,15 @@
</script>
{#if object}
<Panel {embedded} allowClose={false} {kind} selectedAside={false}>
<svelte:fragment slot="title">
<div class="title">
<DrivePresenter value={object} shouldShowAvatar={false} disabled noUnderline />
</div>
</svelte:fragment>
<svelte:fragment slot="utils">
<Button
icon={IconMoreH}
iconProps={{ size: 'medium' }}
kind={'icon'}
on:click={(ev) => {
showMenu(ev, { object })
}}
/>
<div class="buttons-divider max-h-7 h-7 mx-2 no-print" />
</svelte:fragment>
<svelte:fragment slot="aside">
<Scroller>
<DocAttributeBar {object} {readonly} ignoreKeys={[]} />
<div class="space-divider bottom" />
</Scroller>
</svelte:fragment>
<FolderBrowser
space={object._id}
parent={drive.ids.Root}
on:contextmenu={(evt) => {
showMenu(evt, { object })
}}
/>
</Panel>
<FolderBrowser
space={object._id}
parent={drive.ids.Root}
{object}
{embedded}
{readonly}
type={'drive'}
on:contextmenu={(evt) => {
showMenu(evt, { object })
}}
/>
{/if}

View File

@ -22,5 +22,5 @@
</script>
{#if object}
<FolderBrowser space={object.space} parent={object._id} {readonly} />
<FolderBrowser space={object.space} parent={object._id} {readonly} type={'folder'} />
{/if}

View File

@ -32,6 +32,6 @@
<div class="antiHSpacer x2" />
<DocsNavigator elements={parents} />
<div class="title">
<div class="fs-title flex-row-center">
<FilePresenter value={object} shouldShowAvatar={false} shouldShowVersion disabled noUnderline />
</div>

View File

@ -33,7 +33,6 @@
export let _id: Ref<DriveFile>
export let readonly: boolean = false
export let embedded: boolean = false
export let kind: 'default' | 'modern' = 'default'
export function canClose (): boolean {
return false
@ -95,10 +94,10 @@
<Panel
{object}
{embedded}
{kind}
allowClose={!embedded}
isHeader={false}
useMaxWidth={false}
adaptive={'default'}
on:open
on:close
on:update

View File

@ -15,24 +15,32 @@
<script lang="ts">
import { type Doc, type DocumentQuery, type Ref, type WithLookup } from '@hcengineering/core'
import drive, { type Drive, type Folder } from '@hcengineering/drive'
import { Scroller, SearchEdit } from '@hcengineering/ui'
import { Scroller, SearchInput, Panel, Button, IconMoreH } from '@hcengineering/ui'
import { Viewlet, ViewOptions } from '@hcengineering/view'
import {
FilterBar,
FilterButton,
ViewletContentView,
ViewletSelector,
ViewletSettingButton
ViewletSettingButton,
DocAttributeBar,
showMenu
} from '@hcengineering/view-resources'
import DrivePresenter from './DrivePresenter.svelte'
import FolderHeader from './FolderHeader.svelte'
import FileDropArea from './FileDropArea.svelte'
export let space: Ref<Drive>
export let parent: Ref<Folder>
export let object: Drive | Folder | undefined = undefined
export let readonly: boolean = false
export let embedded: boolean = false
export let type: 'drive' | 'folder'
const _class = drive.class.Resource
$: object = type === 'drive' ? (object as Drive) : (object as Folder)
$: query = { space, parent }
let viewlet: WithLookup<Viewlet> | undefined = undefined
@ -48,26 +56,49 @@
function updateSearchQuery (search: string): void {
searchQuery = search === '' ? { ...query } : { ...query, $search: search }
}
const getDrive = (obj: Drive | Folder): Drive => obj as Drive
const getFolder = (obj: Drive | Folder): Folder => obj as Folder
</script>
{#if space !== undefined}
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div class="antiComponent">
<div class="ac-header full divide caption-height">
<div class="ac-header-full small-gap">
<SearchEdit bind:value={search} on:change={() => {}} />
<div class="buttons-divider" />
<FilterButton {_class} {space} />
</div>
<div class="ac-header-full medium-gap">
<ViewletSettingButton bind:viewOptions bind:viewlet />
<ViewletSelector bind:viewlet viewletQuery={{ attachTo: _class }} />
</div>
</div>
{#if space !== undefined && object}
<Panel {embedded} allowClose={false} selectedAside={false}>
<svelte:fragment slot="beforeTitle">
<ViewletSelector bind:viewlet viewletQuery={{ attachTo: _class }} />
<ViewletSettingButton bind:viewOptions bind:viewlet />
</svelte:fragment>
<svelte:fragment slot="title">
{#if type === 'drive'}
<DrivePresenter value={getDrive(object)} shouldShowAvatar={false} disabled noUnderline />
{:else}
<FolderHeader object={getFolder(object)} />
{/if}
</svelte:fragment>
<svelte:fragment slot="search">
<SearchInput bind:value={search} collapsed />
<FilterButton {_class} {space} />
</svelte:fragment>
<svelte:fragment slot="utils">
<Button
icon={IconMoreH}
iconProps={{ size: 'medium' }}
kind={'icon'}
on:click={(ev) => {
showMenu(ev, { object })
}}
/>
</svelte:fragment>
<svelte:fragment slot="aside">
<Scroller>
<DocAttributeBar {object} {readonly} ignoreKeys={[]} />
<div class="space-divider bottom" />
</Scroller>
</svelte:fragment>
{#if viewlet !== undefined && viewOptions}
<FilterBar {_class} {space} query={searchQuery} {viewOptions} on:change={(e) => (resultQuery = e.detail)} />
<div class="popupPanel rowContent" on:contextmenu>
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div class="popupPanel-body" on:contextmenu>
{#if viewlet}
<FileDropArea {space} {parent} canDrop={() => !readonly}>
<Scroller horizontal={true}>
@ -77,5 +108,5 @@
{/if}
</div>
{/if}
</div>
</Panel>
{/if}

View File

@ -16,16 +16,13 @@
import { type Ref } from '@hcengineering/core'
import drive, { type Folder } from '@hcengineering/drive'
import { createQuery } from '@hcengineering/presentation'
import { Panel, Button, Scroller, IconMoreH } from '@hcengineering/ui'
import { DocAttributeBar, showMenu } from '@hcengineering/view-resources'
import { showMenu } from '@hcengineering/view-resources'
import FolderHeader from './FolderHeader.svelte'
import FolderBrowser from './FolderBrowser.svelte'
export let _id: Ref<Folder>
export let readonly: boolean = false
export let embedded: boolean = false
export let kind: 'default' | 'modern' = 'default'
export function canClose (): boolean {
return false
@ -40,35 +37,15 @@
</script>
{#if object}
<Panel {embedded} allowClose={false} {kind} selectedAside={false}>
<svelte:fragment slot="title">
<FolderHeader {object} />
</svelte:fragment>
<svelte:fragment slot="utils">
<Button
icon={IconMoreH}
iconProps={{ size: 'medium' }}
kind={'icon'}
on:click={(ev) => {
showMenu(ev, { object })
}}
/>
<div class="buttons-divider max-h-7 h-7 mx-2 no-print" />
</svelte:fragment>
<svelte:fragment slot="aside">
<Scroller>
<DocAttributeBar {object} {readonly} ignoreKeys={[]} />
<div class="space-divider bottom" />
</Scroller>
</svelte:fragment>
<FolderBrowser
space={object.space}
parent={object._id}
{readonly}
on:contextmenu={(evt) => {
showMenu(evt, { object })
}}
/>
</Panel>
<FolderBrowser
space={object.space}
parent={object._id}
{object}
{embedded}
{readonly}
type={'folder'}
on:contextmenu={(evt) => {
showMenu(evt, { object })
}}
/>
{/if}

View File

@ -42,7 +42,7 @@
</script>
{#if isFolder}
<Icon icon={IconFolderThumbnail} size={'full'} fill={'var(--theme-trans-color)'} />
<Icon icon={IconFolderThumbnail} size={'full'} fill={'var(--global-no-priority-PriorityColor)'} />
{:else if previewRef != null && isImage && !isError}
{#await getBlobRef(undefined, previewRef, object.name, sizeToWidth(size)) then blobSrc}
<img

View File

@ -39,7 +39,9 @@ export const drivePlugin = plugin(driveId, {
DefaultDriveTypeData: '' as Ref<Mixin<Drive>>
},
icon: {
DriveApplication: '' as Asset,
Drive: '' as Asset,
Drives: '' as Asset,
Grid: '' as Asset,
File: '' as Asset,
Folder: '' as Asset,

View File

@ -13,6 +13,7 @@
// limitations under the License.
-->
<script lang="ts">
import { onDestroy } from 'svelte'
import { CalendarMode } from '@hcengineering/calendar-resources'
import calendar from '@hcengineering/calendar-resources/src/plugin'
import { Employee, PersonAccount } from '@hcengineering/contact'
@ -20,25 +21,32 @@
import { DocumentQuery, Ref, getCurrentAccount } from '@hcengineering/core'
import { Department, Staff } from '@hcengineering/hr'
import { createQuery } from '@hcengineering/presentation'
import type { TabItem } from '@hcengineering/ui'
import { getEmbeddedLabel } from '@hcengineering/platform'
import type { TabItem, DropdownIntlItem } from '@hcengineering/ui'
import {
Button,
ModernButton,
ButtonIcon,
IconBack,
IconForward,
Label,
SearchEdit,
SearchInput,
Separator,
TabList,
Header,
Breadcrumb,
Switcher,
defineSeparators,
workbenchSeparators,
deviceOptionsStore as deviceInfo
deviceOptionsStore as deviceInfo,
tableToCSV,
showPopup
} from '@hcengineering/ui'
import view from '@hcengineering/view'
import view, { Viewlet, ViewletPreference } from '@hcengineering/view'
import { ViewletSelector, ViewletSettingButton } from '@hcengineering/view-resources'
import hr from '../plugin'
import ScheduleView from './ScheduleView.svelte'
import Sidebar from './sidebar/Sidebar.svelte'
import ExportPopup from './schedule/ExportPopup.svelte'
const accountEmployee = $employeeByIdStore.get((getCurrentAccount() as PersonAccount).person as Ref<Employee>)
let accountStaff: Staff | undefined
@ -122,10 +130,60 @@
{ id: 'stats', icon: view.icon.Table }
]
let viewlet: Viewlet | undefined
let preference: ViewletPreference | undefined
let loading = false
function exportTable (evt: Event) {
interface ExportPopupItem extends DropdownIntlItem {
separator: ',' | ';'
}
const items: ExportPopupItem[] = [
{
id: '0',
label: getEmbeddedLabel(', (csv)'),
separator: ','
},
{
id: '1',
label: getEmbeddedLabel('; (MS Excel)'),
separator: ';'
}
]
showPopup(
ExportPopup,
{
items
},
evt.target as HTMLElement,
(res) => {
if (res != null) {
const filename = 'exportStaff' + new Date().toLocaleDateString() + '.csv'
const link = document.createElement('a')
link.style.display = 'none'
link.setAttribute('target', '_blank')
link.setAttribute(
'href',
'data:text/csv;charset=utf-8,%EF%BB%BF' +
encodeURIComponent(tableToCSV('exportableData', items[res].separator))
)
link.setAttribute('download', filename)
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
}
}
)
}
let replacedPanel: HTMLElement
$: $deviceInfo.replacedPanel = replacedPanel
onDestroy(() => ($deviceInfo.replacedPanel = undefined))
defineSeparators('workbench', workbenchSeparators)
</script>
<div class="flex h-full clear-mins">
<div class="hulyPanels-container">
{#if $deviceInfo.navigator.visible}
<Sidebar
{department}
@ -140,68 +198,90 @@
float={$deviceInfo.navigator.float}
disabledWhen={['panel-aside']}
index={0}
color={'var(--theme-navpanel-border)'}
color={'transparent'}
separatorSize={0}
short
/>
{/if}
<div class="antiPanel-component filled">
<div class="ac-header full divide caption-height">
<div class="ac-header__wrap-title mr-3">
<span class="ac-header__title"><Label label={hr.string.Schedule} /></span>
</div>
<div class="ac-header-full medium-gap mb-1">
<div class="hulyComponent" bind:this={replacedPanel}>
<Header
adaptive={'disabled'}
hideBefore={mode === CalendarMode.Year}
hideActions={!(mode === CalendarMode.Month && display === 'stats')}
>
<svelte:fragment slot="beforeTitle">
{#if mode === CalendarMode.Month}
<TabList
<Switcher
name={'schedule-mode-view'}
items={viewslist}
multiselect={false}
kind={'subtle'}
selected={display}
on:select={(result) => {
if (result.detail !== undefined) display = result.detail.id
}}
/>
{#if display === 'stats'}
<ViewletSelector
hidden
bind:viewlet
bind:preference
bind:loading
viewletQuery={{ _id: hr.viewlet.StaffStats }}
/>
<ViewletSettingButton bind:viewlet />
{/if}
{/if}
</div>
</div>
<div class="ac-header full divide search-start">
<div class="ac-header-full small-gap">
<SearchEdit
</svelte:fragment>
<Breadcrumb icon={hr.icon.HR} label={hr.string.Schedule} size={'large'} isCurrent />
<svelte:fragment slot="search">
<SearchInput
bind:value={search}
collapsed
on:change={() => {
updateResultQuery(search)
}}
/>
<!-- <ActionIcon icon={IconMoreH} size={'small'} /> -->
</div>
<div class="ac-header-full medium-gap">
<!-- <ViewletSettingButton bind:viewOptions {viewlet} /> -->
<!-- <ActionIcon icon={IconMoreH} size={'small'} /> -->
</div>
</div>
<div class="ac-header full divide">
<div class="ac-header-full small-gap">
<Button
</svelte:fragment>
<svelte:fragment slot="actions">
{#if mode === CalendarMode.Month && display === 'stats'}
<ModernButton
label={hr.string.Export}
size={'small'}
on:click={(evt) => {
exportTable(evt)
}}
/>
{/if}
</svelte:fragment>
</Header>
<div class="hulyHeader-container clearPadding justify-between flex-gap-4">
<div class="flex-row-center flex-gap-2">
<ButtonIcon
icon={IconBack}
kind={'ghost'}
kind={'tertiary'}
size={'small'}
on:click={() => {
inc(-1)
}}
/>
<Button
<ModernButton
label={calendar.string.Today}
kind={'ghost'}
on:click={() => {
currentDate = new Date()
}}
kind={'tertiary'}
size={'small'}
on:click={() => (currentDate = new Date())}
/>
<Button
<ButtonIcon
icon={IconForward}
kind={'ghost'}
kind={'tertiary'}
size={'small'}
on:click={() => {
inc(1)
}}
/>
<div class="buttons-divider" />
<div class="hulyHeader-divider short" />
<div class="fs-title flex-row-center flex-grow firstLetter">
{#if mode === CalendarMode.Month}
<span class="mr-2 overflow-label">{getMonthName(currentDate)}</span>
@ -209,12 +289,14 @@
{currentDate.getFullYear()}
</div>
</div>
<TabList
<Switcher
name={'calendar-mode-view'}
selected={mode === CalendarMode.Month ? 'ModeMonth' : 'ModeYear'}
kind={'subtle'}
items={[
{ id: 'ModeMonth', labelIntl: calendar.string.ModeMonth },
{ id: 'ModeYear', labelIntl: calendar.string.ModeYear }
]}
multiselect={false}
on:select={handleSelect}
/>
</div>
@ -227,6 +309,9 @@
{currentDate}
{mode}
{display}
{preference}
{viewlet}
{loading}
/>
</div>
</div>

Some files were not shown because too many files have changed in this diff Show More