mirror of
https://github.com/hcengineering/platform.git
synced 2025-05-09 08:56:40 +00:00
Updated layout of Project Types (#4345)
Signed-off-by: Alexander Platov <sas_lord@mail.ru>
This commit is contained in:
parent
262c2dd82c
commit
3ac0e6e259
@ -521,7 +521,7 @@ export function createModel (builder: Builder): void {
|
||||
{
|
||||
name: board.string.BoardApplication,
|
||||
description: board.string.ManageBoardStatuses,
|
||||
icon: board.component.TemplatesIcon,
|
||||
icon: board.icon.Board,
|
||||
baseClass: board.class.Board
|
||||
},
|
||||
board.descriptors.BoardType
|
||||
|
@ -703,7 +703,7 @@ export function createModel (builder: Builder): void {
|
||||
{
|
||||
name: lead.string.LeadApplication,
|
||||
description: lead.string.ManageFunnelStatuses,
|
||||
icon: lead.component.TemplatesIcon,
|
||||
icon: lead.icon.LeadApplication,
|
||||
baseClass: lead.class.Funnel
|
||||
},
|
||||
lead.descriptors.FunnelType
|
||||
|
@ -1774,7 +1774,7 @@ export function createModel (builder: Builder): void {
|
||||
{
|
||||
name: recruit.string.RecruitApplication,
|
||||
description: recruit.string.ManageVacancyStatuses,
|
||||
icon: recruit.component.TemplatesIcon,
|
||||
icon: recruit.icon.RecruitApplication,
|
||||
editor: recruit.component.VacancyTemplateEditor,
|
||||
baseClass: recruit.class.Vacancy
|
||||
},
|
||||
|
@ -248,7 +248,7 @@ export class TTaskType extends TDoc implements TaskType {
|
||||
export class TProjectTypeDescriptor extends TDoc implements ProjectTypeDescriptor {
|
||||
name!: IntlString
|
||||
description!: IntlString
|
||||
icon!: AnyComponent
|
||||
icon!: Asset
|
||||
editor?: AnyComponent
|
||||
baseClass!: Ref<Class<Task>>
|
||||
}
|
||||
|
@ -672,7 +672,7 @@ export function createModel (builder: Builder): void {
|
||||
{
|
||||
name: tracker.string.TrackerApplication,
|
||||
description: tracker.string.ManageWorkflowStatuses,
|
||||
icon: task.component.TemplatesIcon,
|
||||
icon: task.icon.Task,
|
||||
baseClass: tracker.class.Project
|
||||
},
|
||||
tracker.descriptors.ProjectType
|
||||
|
@ -267,6 +267,7 @@ input.search {
|
||||
.justify-stretch { justify-content: stretch; }
|
||||
.items-baseline { align-items: baseline; }
|
||||
.items-start { align-items: flex-start; }
|
||||
.items-end { align-items: flex-end; }
|
||||
.items-center { align-items: center; }
|
||||
.items-stretch { align-items: stretch; }
|
||||
.self-start { align-self: flex-start; }
|
||||
|
@ -1,16 +1,6 @@
|
||||
//
|
||||
// Copyright © 2021 Anticrm Platform Contributors.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// © 2023 Hardcore Engineering, Inc. All Rights Reserved.
|
||||
// Licensed under the Eclipse Public License v2.0 (SPDX: EPL-2.0).
|
||||
//
|
||||
|
||||
/* Common Colors */
|
||||
@ -37,6 +27,7 @@
|
||||
/* Dark Theme */
|
||||
.theme-dark {
|
||||
--global-ui-BackgroundColor: #A5BDFF0D;
|
||||
--global-ui-BorderColor: #A5BDFF1A;
|
||||
--global-ui-hover-BackgroundColor: #A5BDFF1A;
|
||||
--global-ui-highlight-BackgroundColor: #A5BDFF0D;
|
||||
--global-ui-hover-highlight-BackgroundColor: #A5BDFF26;
|
||||
@ -53,6 +44,7 @@
|
||||
--global-primary-TextColor: #FFFFFF;
|
||||
--global-secondary-TextColor: #C1C9D6;
|
||||
--global-tertiary-TextColor: #8E99AF;
|
||||
--global-disabled-TextColor: #5A667E;
|
||||
--global-accent-TextColor: #4D7FF5;
|
||||
--global-focus-BorderColor: #2A59D6;
|
||||
|
||||
@ -88,6 +80,7 @@
|
||||
/* Light Theme */
|
||||
.theme-light {
|
||||
--global-ui-BackgroundColor: #1530720D;
|
||||
--global-ui-BorderColor: #1530721A;
|
||||
--global-ui-hover-BackgroundColor: #1530721A;
|
||||
--global-ui-highlight-BackgroundColor: #A5BDFF26;
|
||||
--global-ui-hover-highlight-BackgroundColor: #A5BDFF40;
|
||||
@ -104,6 +97,7 @@
|
||||
--global-primary-TextColor: #0F121A;
|
||||
--global-secondary-TextColor: #5A667E;
|
||||
--global-tertiary-TextColor: #7B879E;
|
||||
--global-disabled-TextColor: #A1ABBF;
|
||||
--global-accent-TextColor: #3566E2;
|
||||
--global-focus-BorderColor: #204DC8;
|
||||
|
||||
|
@ -28,7 +28,8 @@
|
||||
.hulyComponent-content,
|
||||
.hulyComponent-content__container,
|
||||
.hulyComponent-content__column,
|
||||
.hulyComponent-content__column.content,
|
||||
.hulyComponent-content__column-group,
|
||||
.hulyComponent-content__header,
|
||||
.hulyComponent-content__navHeader {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
@ -39,6 +40,9 @@
|
||||
flex-shrink: 0;
|
||||
max-width: 64rem;
|
||||
|
||||
&.gap {
|
||||
gap: var(--spacing-4);
|
||||
}
|
||||
&__container {
|
||||
height: 100%;
|
||||
}
|
||||
@ -54,18 +58,21 @@
|
||||
margin: 0 0.75rem;
|
||||
}
|
||||
&.content {
|
||||
overflow-y: auto;
|
||||
align-items: stretch;
|
||||
}
|
||||
&-group {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: var(--spacing-3);
|
||||
flex-shrink: 0;
|
||||
height: fit-content;
|
||||
}
|
||||
}
|
||||
&__navHeader {
|
||||
flex-direction: column;
|
||||
flex-shrink: 0;
|
||||
padding-bottom: var(--spacing-3);
|
||||
border-bottom: 1px solid var(--theme-navpanel-divider);
|
||||
|
||||
&.divide {
|
||||
border-bottom: 1px solid var(--theme-navpanel-divider);
|
||||
}
|
||||
&-menu {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
@ -76,9 +83,18 @@
|
||||
height: var(--global-extra-large-Size);
|
||||
}
|
||||
&-hint {
|
||||
margin: var(--spacing-0_25) var(--spacing-3) 0 var(--spacing-2);
|
||||
margin: var(--spacing-0_25) var(--spacing-3) var(--spacing-3) var(--spacing-2);
|
||||
}
|
||||
}
|
||||
&__header {
|
||||
justify-content: space-between;
|
||||
align-self: stretch;
|
||||
padding: 0 0 var(--spacing-1) var(--spacing-1_5);
|
||||
}
|
||||
textarea {
|
||||
font-weight: 400 !important;
|
||||
color: var(--global-tertiary-TextColor) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Header */
|
||||
@ -123,8 +139,17 @@
|
||||
gap: var(--spacing-0_5);
|
||||
}
|
||||
.hulyHeader-buttonsGroup {
|
||||
flex-shrink: 0;
|
||||
gap: var(--spacing-1);
|
||||
margin-left: var(--spacing-2);
|
||||
|
||||
&__label {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
flex-shrink: 0;
|
||||
color: var(--global-secondary-TextColor);
|
||||
}
|
||||
}
|
||||
.hulyHotKey-item {
|
||||
margin-right: .625rem;
|
||||
|
@ -26,6 +26,7 @@
|
||||
@import "./mixins.scss";
|
||||
@import "./panel.scss";
|
||||
@import "./prose.scss";
|
||||
@import "./tables.scss";
|
||||
@import "./_text-editor.scss";
|
||||
|
||||
@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital,wght@0,400;0,500;1,400;1,500&display=swap');
|
||||
|
240
packages/theme/styles/tables.scss
Normal file
240
packages/theme/styles/tables.scss
Normal file
@ -0,0 +1,240 @@
|
||||
//
|
||||
// © 2024 Hardcore Engineering, Inc. All Rights Reserved.
|
||||
// Licensed under the Eclipse Public License v2.0 (SPDX: EPL-2.0).
|
||||
//
|
||||
|
||||
/* Huly Attribute Table */
|
||||
.hulyTableAttr-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
align-self: stretch;
|
||||
background-color: var(--theme-table-row-color);
|
||||
border: 1px solid var(--theme-divider-color);
|
||||
border-radius: var(--large-BorderRadius);
|
||||
|
||||
.hulyTableAttr-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
align-self: stretch;
|
||||
flex-shrink: 0;
|
||||
text-transform: uppercase;
|
||||
color: var(--global-secondary-TextColor);
|
||||
|
||||
&.withButton {
|
||||
padding: var(--spacing-2);
|
||||
}
|
||||
&:not(.withButton) {
|
||||
padding: var(--spacing-2) var(--spacing-2) var(--spacing-2) var(--spacing-2_5);
|
||||
}
|
||||
span {
|
||||
flex-grow: 1;
|
||||
margin-left: var(--spacing-1_5);
|
||||
}
|
||||
}
|
||||
.hulyTableAttr-content {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
align-self: stretch;
|
||||
flex-shrink: 0;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
border-top: 1px solid var(--theme-divider-color);
|
||||
|
||||
&:not(.withTitle) {
|
||||
flex-direction: column;
|
||||
}
|
||||
&.withTitle {
|
||||
gap: var(--spacing-1);
|
||||
|
||||
.hulyTableAttr-content__title {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
align-self: stretch;
|
||||
gap: 8px;
|
||||
padding: var(--spacing-1_5);
|
||||
min-width: 8.75rem;
|
||||
max-width: 8.75rem;
|
||||
text-transform: uppercase;
|
||||
font-size: .75rem;
|
||||
font-weight: 500;
|
||||
line-height: 1rem;
|
||||
color: var(--global-secondary-TextColor);
|
||||
}
|
||||
.hulyTableAttr-content__wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
height: fit-content;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
}
|
||||
}
|
||||
&__row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-self: stretch;
|
||||
flex-grow: 1;
|
||||
margin: 0;
|
||||
min-width: 0;
|
||||
border-radius: var(--small-BorderRadius);
|
||||
border: none;
|
||||
outline: none;
|
||||
|
||||
&-dragMenu,
|
||||
&-icon-wrapper {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
&-dragMenu {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: var(--global-extra-small-Size);
|
||||
height: var(--global-extra-small-Size);
|
||||
color: var(--button-disabled-IconColor);
|
||||
border-radius: var(--extra-small-BorderRadius);
|
||||
border: none;
|
||||
outline: none;
|
||||
}
|
||||
&-icon {
|
||||
width: var(--global-min-Size);
|
||||
height: var(--global-min-Size);
|
||||
color: var(--global-primary-TextColor);
|
||||
}
|
||||
&-icon-wrapper {
|
||||
width: var(--global-medium-Size);
|
||||
height: var(--global-medium-Size);
|
||||
color: var(--global-secondary-TextColor);
|
||||
background-color: var(--theme-panel-color); // var(--global-surface-02-BackgroundColor);
|
||||
border-radius: var(--small-BorderRadius);
|
||||
|
||||
&.pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
&-labels-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
gap: var(--spacing-0_25);
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
}
|
||||
&-label {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
word-break: break-all;
|
||||
text-overflow: ellipsis;
|
||||
text-align: left;
|
||||
flex-shrink: 1;
|
||||
min-width: 0;
|
||||
color: var(--global-primary-TextColor);
|
||||
|
||||
&.grow {
|
||||
flex-grow: 1;
|
||||
}
|
||||
&.accent {
|
||||
font-weight: 500;
|
||||
}
|
||||
&.dark {
|
||||
color: var(--global-secondary-TextColor);
|
||||
}
|
||||
p {
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
&-type {
|
||||
white-space: nowrap;
|
||||
text-transform: uppercase;
|
||||
color: var(--global-secondary-TextColor);
|
||||
}
|
||||
&-arrow {
|
||||
display: none;
|
||||
flex-shrink: 0;
|
||||
width: var(--global-min-Size);
|
||||
height: var(--global-min-Size);
|
||||
color: var(--global-primary-LinkColor);
|
||||
}
|
||||
}
|
||||
&.class .hulyTableAttr-content__row,
|
||||
&.task .hulyTableAttr-content__row {
|
||||
&.hovered,
|
||||
&:hover {
|
||||
background-color: var(--theme-table-header-color); // var(--global-surface-03-hover-BackgroundColor);
|
||||
}
|
||||
&.selected {
|
||||
background-color: var(--theme-table-header-color); // var(--global-surface-03-hover-BackgroundColor);
|
||||
|
||||
.hulyTableAttr-content__row-icon,
|
||||
.hulyTableAttr-content__row-arrow,
|
||||
.hulyTableAttr-content__row-label {
|
||||
color: var(--global-primary-LinkColor);
|
||||
}
|
||||
.hulyTableAttr-content__row-type {
|
||||
color: var(--global-primary-TextColor);
|
||||
}
|
||||
.hulyTableAttr-content__row-label {
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
}
|
||||
&.class {
|
||||
padding: var(--spacing-1);
|
||||
|
||||
.hulyTableAttr-content__row {
|
||||
gap: var(--spacing-1);
|
||||
padding: var(--spacing-1) var(--spacing-2) var(--spacing-1) var(--spacing-1);
|
||||
|
||||
&.hovered .hulyTableAttr-content__row-arrow,
|
||||
&:hover .hulyTableAttr-content__row-arrow,
|
||||
&.selected .hulyTableAttr-content__row-arrow {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
&.task {
|
||||
.hulyTableAttr-content__row {
|
||||
gap: var(--spacing-1);
|
||||
padding: var(--spacing-1_5);
|
||||
border-radius: 0;
|
||||
|
||||
&:last-child {
|
||||
border-radius: 0 0 var(--large-BorderRadius) var(--large-BorderRadius);
|
||||
}
|
||||
.hulyTableAttr-content__row-icon-wrapper {
|
||||
margin-right: var(--spacing-0_5);
|
||||
}
|
||||
}
|
||||
.hulyTableAttr-content__row + .hulyTableAttr-content__row {
|
||||
border-top: 1px solid var(--theme-divider-color);
|
||||
}
|
||||
}
|
||||
&.automation {
|
||||
.hulyTableAttr-content__row {
|
||||
gap: var(--spacing-2);
|
||||
padding: var(--spacing-1_5) var(--spacing-1_5) var(--spacing-1_5) var(--spacing-2_5);
|
||||
border-radius: 0;
|
||||
cursor: default;
|
||||
|
||||
&:last-child {
|
||||
border-radius: 0 0 var(--large-BorderRadius) var(--large-BorderRadius);
|
||||
}
|
||||
.hulyTableAttr-content__row-icon-group {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
flex-wrap: nowrap;
|
||||
gap: var(--spacing-1);
|
||||
}
|
||||
}
|
||||
.hulyTableAttr-content__row + .hulyTableAttr-content__row {
|
||||
border-top: 1px solid var(--theme-divider-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -4,6 +4,8 @@
|
||||
"Ok": "Ok",
|
||||
"Cancel": "Cancel",
|
||||
"Save": "Save",
|
||||
"Publish": "Publish",
|
||||
"SaveDraft": "Save draft",
|
||||
"Minutes": "{minutes, plural, =0 {less than a minute ago} =1 {a minute ago} other {# minutes ago}}",
|
||||
"Hours": "{hours, plural, =0 {less than an hour ago} =1 {an hour ago} other {# hours ago}}",
|
||||
"Days": "{days, plural, =0 {today} =1 {yesterday} other {# days ago}}",
|
||||
|
@ -4,6 +4,8 @@
|
||||
"Ok": "Ок",
|
||||
"Cancel": "Отменить",
|
||||
"Save": "Сохранить",
|
||||
"Publish": "Опубликовать",
|
||||
"SaveDraft": "Сохранить черновик",
|
||||
"Minutes": "{minutes, plural, =0 {меньше минуты назад} =1 {минуту назад} one {# минуту назад} few {# минуты назад} other {# минут назад}}",
|
||||
"Hours": "{hours, plural, =0 {меньше часа назад} =1 {час назад} one {# час назад} few {# часа назад} other {# часов назад}}",
|
||||
"Days": "{days, plural, =0 {сегодня} =1 {вчера} one {# день назад} few {# дня назад} other {# дней назад}}",
|
||||
|
@ -46,9 +46,10 @@
|
||||
}}
|
||||
/>
|
||||
{/each}
|
||||
{#if afterLabel}
|
||||
<span class="hulyBreadcrumbs-afterLabel">
|
||||
<Label label={afterLabel} />
|
||||
{#if afterLabel || $$slots.afterLabel}
|
||||
<span class="hulyBreadcrumbs-afterLabel font-medium-12">
|
||||
{#if afterLabel}<Label label={afterLabel} />{/if}
|
||||
<slot name="afterLabel" />
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
|
@ -22,6 +22,7 @@
|
||||
|
||||
export let title: string | undefined = undefined
|
||||
export let label: IntlString | undefined = undefined
|
||||
export let labelParams: Record<string, any> = {}
|
||||
export let icon: Asset | AnySvelteComponent | ComponentType | undefined = undefined
|
||||
export let kind: 'primary' | 'secondary' | 'tertiary' | 'negative'
|
||||
export let size: 'large' | 'medium' | 'small'
|
||||
@ -43,10 +44,13 @@
|
||||
on:click
|
||||
>
|
||||
{#if loading}
|
||||
<div class="icon animate"><Spinner size={'small'} /></div>
|
||||
{:else if icon}<div class="icon"><Icon {icon} size={'small'} /></div>{/if}
|
||||
<div class="icon animate"><Spinner size={type === 'type-button' && !hasMenu ? 'medium' : 'small'} /></div>
|
||||
{:else if icon}<div class="icon">
|
||||
<Icon {icon} size={type === 'type-button' && !hasMenu ? 'medium' : 'small'} />
|
||||
</div>{/if}
|
||||
{#if label}<span><Label {label} params={labelParams} /></span>{/if}
|
||||
{#if title}<span>{title}</span>{/if}
|
||||
{#if label}<span><Label {label} /></span>{/if}
|
||||
<slot />
|
||||
</button>
|
||||
|
||||
<style lang="scss">
|
||||
@ -113,7 +117,8 @@
|
||||
width: var(--spacing-4);
|
||||
}
|
||||
}
|
||||
&.type-button-icon .icon {
|
||||
&.type-button-icon .icon,
|
||||
&.menu .icon {
|
||||
width: var(--spacing-2);
|
||||
height: var(--spacing-2);
|
||||
}
|
||||
@ -123,7 +128,7 @@
|
||||
background-color: var(--button-primary-BackgroundColor);
|
||||
|
||||
.icon {
|
||||
fill: var(--button-accent-IconColor);
|
||||
color: var(--button-accent-IconColor);
|
||||
}
|
||||
span {
|
||||
color: var(--button-accent-LabelColor);
|
||||
@ -145,7 +150,7 @@
|
||||
cursor: not-allowed;
|
||||
|
||||
.icon {
|
||||
fill: var(--button-disabled-IconColor);
|
||||
color: var(--button-disabled-IconColor);
|
||||
}
|
||||
span {
|
||||
color: var(--button-disabled-LabelColor);
|
||||
@ -165,7 +170,7 @@
|
||||
background-color: var(--button-secondary-BackgroundColor);
|
||||
|
||||
.icon {
|
||||
fill: var(--button-subtle-IconColor);
|
||||
color: var(--button-subtle-IconColor);
|
||||
}
|
||||
span {
|
||||
color: var(--button-subtle-LabelColor);
|
||||
@ -187,7 +192,7 @@
|
||||
cursor: not-allowed;
|
||||
|
||||
.icon {
|
||||
fill: var(--button-disabled-IconColor);
|
||||
color: var(--button-disabled-IconColor);
|
||||
}
|
||||
span {
|
||||
color: var(--button-disabled-LabelColor);
|
||||
@ -207,13 +212,13 @@
|
||||
background-color: transparent;
|
||||
|
||||
&:not(.inheritColor) .icon {
|
||||
fill: var(--button-subtle-IconColor);
|
||||
color: var(--button-subtle-IconColor);
|
||||
}
|
||||
&.inheritColor {
|
||||
color: inherit;
|
||||
|
||||
.icon {
|
||||
fill: currentColor;
|
||||
color: currentColor;
|
||||
}
|
||||
}
|
||||
span {
|
||||
@ -235,7 +240,7 @@
|
||||
cursor: not-allowed;
|
||||
|
||||
.icon {
|
||||
fill: var(--button-disabled-IconColor);
|
||||
color: var(--button-disabled-IconColor);
|
||||
}
|
||||
span {
|
||||
color: var(--button-disabled-LabelColor);
|
||||
@ -255,7 +260,7 @@
|
||||
background-color: var(--button-negative-BackgroundColor);
|
||||
|
||||
.icon {
|
||||
fill: var(--button-accent-IconColor);
|
||||
color: var(--button-accent-IconColor);
|
||||
}
|
||||
span {
|
||||
color: var(--button-accent-LabelColor);
|
||||
@ -277,7 +282,7 @@
|
||||
cursor: not-allowed;
|
||||
|
||||
.icon {
|
||||
fill: var(--button-disabled-IconColor);
|
||||
color: var(--button-disabled-IconColor);
|
||||
}
|
||||
span {
|
||||
color: var(--button-disabled-LabelColor);
|
||||
|
37
packages/ui/src/components/ModernButton.svelte
Normal file
37
packages/ui/src/components/ModernButton.svelte
Normal file
@ -0,0 +1,37 @@
|
||||
<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 { AnySvelteComponent } from '../types'
|
||||
import { ComponentType } from 'svelte'
|
||||
import ButtonBase from './ButtonBase.svelte'
|
||||
|
||||
export let title: string | undefined = undefined
|
||||
export let label: IntlString | undefined = undefined
|
||||
export let labelParams: Record<string, any> = {}
|
||||
export let kind: 'primary' | 'secondary' | 'tertiary' | 'negative' = 'secondary'
|
||||
export let size: 'large' | 'medium' | 'small' = 'large'
|
||||
export let icon: Asset | AnySvelteComponent | ComponentType | undefined = undefined
|
||||
export let disabled: boolean = false
|
||||
export let loading: boolean = false
|
||||
export let hasMenu: boolean = false
|
||||
</script>
|
||||
|
||||
<ButtonBase
|
||||
type={'type-button'}
|
||||
{title}
|
||||
{label}
|
||||
{labelParams}
|
||||
{kind}
|
||||
{size}
|
||||
{icon}
|
||||
{loading}
|
||||
{disabled}
|
||||
{hasMenu}
|
||||
on:click
|
||||
>
|
||||
<slot />
|
||||
</ButtonBase>
|
@ -1,18 +1,10 @@
|
||||
<!--
|
||||
// Copyright © 2023 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
//
|
||||
// © 2023 Hardcore Engineering, Inc. All Rights Reserved.
|
||||
// Licensed under the Eclipse Public License v2.0 (SPDX: EPL-2.0).
|
||||
//
|
||||
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { IntlString, translate } from '@hcengineering/platform'
|
||||
import Label from './Label.svelte'
|
||||
import { themeStore } from '..'
|
||||
@ -26,6 +18,8 @@
|
||||
export let password: boolean = false
|
||||
export let limit: number = 0
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
$: maxlength = limit === 0 ? null : limit
|
||||
|
||||
let placeholderStr: string = ''
|
||||
@ -48,10 +42,12 @@
|
||||
spellcheck="false"
|
||||
{disabled}
|
||||
{maxlength}
|
||||
on:blur
|
||||
on:change
|
||||
on:keyup
|
||||
on:input
|
||||
on:blur={() => {
|
||||
dispatch('blur', value)
|
||||
}}
|
||||
/>
|
||||
{:else}
|
||||
<input
|
||||
@ -64,10 +60,12 @@
|
||||
spellcheck="false"
|
||||
{disabled}
|
||||
{maxlength}
|
||||
on:blur
|
||||
on:change
|
||||
on:keyup
|
||||
on:input
|
||||
on:blur={() => {
|
||||
dispatch('blur', value)
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
{#if labeled}<div class="font-regular-14 label"><Label {label} /></div>{/if}
|
||||
|
@ -174,6 +174,7 @@
|
||||
}
|
||||
&.type-anchor-link {
|
||||
padding: 0 0.75rem 0 0.625rem;
|
||||
width: fit-content;
|
||||
min-height: 1.75rem;
|
||||
|
||||
.hulyNavItem-icon,
|
||||
|
@ -25,6 +25,7 @@
|
||||
import IconUpOutline from './icons/UpOutline.svelte'
|
||||
|
||||
export let padding: string | undefined = undefined
|
||||
export let bottomPadding: string | undefined = undefined
|
||||
export let autoscroll: boolean = false
|
||||
export let bottomStart: boolean = false
|
||||
export let fade: FadeOptions = defaultSP
|
||||
@ -32,6 +33,7 @@
|
||||
export let invertScroll: boolean = false
|
||||
export let contentDirection: 'vertical' | 'vertical-reverse' | 'horizontal' = 'vertical'
|
||||
export let horizontal: boolean = contentDirection === 'horizontal'
|
||||
export let align: 'start' | 'center' | 'end' | 'stretch' = 'stretch'
|
||||
export let gap: string | undefined = undefined
|
||||
export let noStretch: boolean = autoscroll
|
||||
export let buttons: 'normal' | 'union' | false = false
|
||||
@ -555,6 +557,7 @@
|
||||
? 'column-reverse'
|
||||
: 'row'}
|
||||
style:height={contentDirection === 'vertical-reverse' ? 'max-content' : noStretch ? 'auto' : '100%'}
|
||||
style:align-items={align}
|
||||
use:resizeObserver={() => {
|
||||
checkAutoScroll()
|
||||
checkFade()
|
||||
@ -565,6 +568,9 @@
|
||||
>
|
||||
{#if bottomStart}<div class="flex-grow flex-shrink" />{/if}
|
||||
<slot />
|
||||
{#if bottomPadding}
|
||||
<div style:width={'100%'} style:min-height={bottomPadding} />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -22,6 +22,7 @@
|
||||
export let label: IntlString | undefined = undefined
|
||||
export let width: string | undefined = undefined
|
||||
export let height: string | undefined = undefined
|
||||
export let margin: string | undefined = undefined
|
||||
export let value: string | undefined = undefined
|
||||
export let placeholder: IntlString = plugin.string.EditBoxPlaceholder
|
||||
export let placeholderParam: any | undefined = undefined
|
||||
@ -40,7 +41,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="textarea" class:no-focus-border={noFocusBorder} style:width style:height>
|
||||
<div class="textarea" class:no-focus-border={noFocusBorder} style:width style:height style:margin>
|
||||
{#if label}<div class="label"><Label {label} /></div>{/if}
|
||||
<textarea
|
||||
bind:value
|
||||
|
@ -15,7 +15,7 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
export let size: 'small' | 'medium' | 'large'
|
||||
const fill: string = 'currentColor'
|
||||
export let fill: string = 'currentColor'
|
||||
</script>
|
||||
|
||||
<svg class="svg-{size}" {fill} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||
|
10
packages/ui/src/components/icons/Send.svelte
Normal file
10
packages/ui/src/components/icons/Send.svelte
Normal file
@ -0,0 +1,10 @@
|
||||
<script lang="ts">
|
||||
export let size: 'small' | 'medium' | 'large'
|
||||
const fill: string = 'currentColor'
|
||||
</script>
|
||||
|
||||
<svg class="svg-{size}" {fill} viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M27.71 4.29049C27.575 4.15616 27.4045 4.06316 27.2185 4.02241C27.0325 3.98167 26.8388 3.99488 26.66 4.06049L4.66 12.0605C4.47027 12.1325 4.30692 12.2604 4.19165 12.4274C4.07638 12.5944 4.01465 12.7926 4.01465 12.9955C4.01465 13.1984 4.07638 13.3965 4.19165 13.5635C4.30692 13.7305 4.47027 13.8585 4.66 13.9305L14.26 17.7705L18.1 27.3705C18.1721 27.5519 18.2958 27.7082 18.4557 27.8201C18.6157 27.9321 18.8049 27.9948 19 28.0005C19.2021 27.9963 19.3982 27.9311 19.5624 27.8132C19.7266 27.6954 19.8513 27.5306 19.92 27.3405L27.92 5.34049C27.9881 5.16356 28.0046 4.97092 27.9674 4.78501C27.9302 4.5991 27.8409 4.4276 27.71 4.29049ZM19 24.2005L16.21 17.2005L20.295 13.1155C20.6844 12.7261 20.6844 12.0948 20.295 11.7055C19.9056 11.3161 19.2744 11.3161 18.885 11.7055L14.76 15.8305L7.8 13.0005L25.33 6.67049L19 24.2005Z"
|
||||
/>
|
||||
</svg>
|
13
packages/ui/src/components/icons/SquareExpand.svelte
Normal file
13
packages/ui/src/components/icons/SquareExpand.svelte
Normal file
@ -0,0 +1,13 @@
|
||||
<script lang="ts">
|
||||
export let size: 'small' | 'medium' | 'large' | 'full'
|
||||
const fill: string = 'currentColor'
|
||||
</script>
|
||||
|
||||
<svg class="svg-{size}" {fill} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
||||
<path
|
||||
d="M18 6.00012V4.00012H28V14.0001H26V7.41012L15.7136 17.7008L15.7071 17.7073C15.3166 18.0978 14.6834 18.0978 14.2929 17.7073C13.9024 17.3167 13.9024 16.6836 14.2929 16.293L24.59 6.00012H18Z"
|
||||
/>
|
||||
<path
|
||||
d="M7.8415 6.10109C8.88208 5.96118 9.94455 5.97558 11.0001 5.99059C11.5524 5.99845 12.0001 5.55245 12.0001 5.00017C12.0001 4.44788 11.5524 3.99873 11.0001 3.99235C9.85582 3.97912 8.7048 3.96702 7.57501 4.11892C6.64718 4.24366 5.82775 4.51563 5.17164 5.17174C4.51553 5.82785 4.24357 6.64727 4.11882 7.5751C4 8.45885 4.00003 11.5755 4.00007 12.9296V21.0707C4.00003 22.4248 4 23.5415 4.11882 24.4252C4.24357 25.3531 4.51553 26.1725 5.17164 26.8286C5.82775 27.4847 6.64718 27.7567 7.57501 27.8814C8.45875 28.0002 9.57537 28.0002 10.9295 28.0002H21.0706C22.4247 28.0002 23.5414 28.0002 24.4251 27.8814C25.353 27.7567 26.1724 27.4847 26.8285 26.8286C27.4846 26.1725 27.7566 25.3531 27.8813 24.4252C28.0332 23.2954 28.0211 22.1444 28.0079 21.0001C28.0015 20.4479 27.5524 20.0002 27.0001 20.0002C26.4478 20.0002 26.0018 20.4479 26.0096 21.0001C26.0247 22.0557 26.0391 23.1182 25.8992 24.1587C25.8042 24.8648 25.6369 25.1918 25.4143 25.4144C25.1917 25.637 24.8647 25.8043 24.1586 25.8993C23.4238 25.998 22.4426 26.0002 21.0001 26.0002H11.0001C9.55759 26.0002 8.57632 25.998 7.8415 25.8993C7.13545 25.8043 6.80843 25.637 6.58586 25.4144C6.36329 25.1918 6.19591 24.8648 6.10099 24.1587C6.00219 23.4239 6.00007 22.4426 6.00007 21.0002V13.0002C6.00007 11.5577 6.00219 8.57642 6.10099 7.8416C6.19591 7.13555 6.36329 6.80853 6.58586 6.58595C6.80843 6.36338 7.13545 6.19601 7.8415 6.10109Z"
|
||||
/>
|
||||
</svg>
|
@ -128,6 +128,7 @@ export { default as Header } from './components/Header.svelte'
|
||||
export { default as Breadcrumb } from './components/Breadcrumb.svelte'
|
||||
export { default as Breadcrumbs } from './components/Breadcrumbs.svelte'
|
||||
export { default as ButtonIcon } from './components/ButtonIcon.svelte'
|
||||
export { default as ModernButton } from './components/ModernButton.svelte'
|
||||
export { default as ModernEditbox } from './components/ModernEditbox.svelte'
|
||||
export { default as NavItem } from './components/NavItem.svelte'
|
||||
export { default as NavGroup } from './components/NavGroup.svelte'
|
||||
@ -197,6 +198,8 @@ export { default as IconMinimize } from './components/icons/Minimize.svelte'
|
||||
export { default as IconChevronRight } from './components/icons/ChevronRight.svelte'
|
||||
export { default as IconDescription } from './components/icons/Description.svelte'
|
||||
export { default as IconSettings } from './components/icons/Settings.svelte'
|
||||
export { default as IconSend } from './components/icons/Send.svelte'
|
||||
export { default as IconSquareExpand } from './components/icons/SquareExpand.svelte'
|
||||
|
||||
export { default as PanelInstance } from './components/PanelInstance.svelte'
|
||||
export { default as Panel } from './components/Panel.svelte'
|
||||
|
@ -29,6 +29,8 @@ export const uis = plugin(uiId, {
|
||||
Ok: '' as IntlString,
|
||||
Cancel: '' as IntlString,
|
||||
Save: '' as IntlString,
|
||||
Publish: '' as IntlString,
|
||||
SaveDraft: '' as IntlString,
|
||||
Minutes: '' as IntlString,
|
||||
Hours: '' as IntlString,
|
||||
Days: '' as IntlString,
|
||||
|
@ -164,4 +164,6 @@ export const settingsSeparators: DefSeparators = [
|
||||
{ minSize: 17, size: 30, maxSize: 32, float: 'aside' }
|
||||
]
|
||||
|
||||
export const secondNavSeparators: DefSeparators = [{ minSize: 7, size: 7.5, maxSize: 15, float: 'navigator' }, null]
|
||||
|
||||
export const separatorsStore = writable<string[]>([])
|
||||
|
@ -140,8 +140,8 @@
|
||||
</div>
|
||||
<Separator name={'notificationSettings'} index={0} color={'var(--theme-divider-color)'} />
|
||||
<div class="hulyComponent-content__column content">
|
||||
<div class="hulyComponent-content">
|
||||
<Scroller>
|
||||
<Scroller align={'center'} padding={'var(--spacing-3)'} bottomPadding={'var(--spacing-3)'}>
|
||||
<div class="hulyComponent-content">
|
||||
{#if group}
|
||||
<NotificationGroupSetting {group} {settings} />
|
||||
{/if}
|
||||
@ -150,8 +150,8 @@
|
||||
<svelte:component this={presenter} />
|
||||
{/await}
|
||||
{/if}
|
||||
</Scroller>
|
||||
</div>
|
||||
</div>
|
||||
</Scroller>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -85,6 +85,11 @@
|
||||
"ConfigurationDisabled": "Disabled",
|
||||
"ConfigDisable": "Disable",
|
||||
"ConfigEnable": "Enable",
|
||||
"ConfigBeta": "Beta version"
|
||||
"ConfigBeta": "Beta version",
|
||||
"Properties": "Properties",
|
||||
"TaskTypes": "Task types",
|
||||
"Automations": "Automations",
|
||||
"Collections": "Collections",
|
||||
"ClassColon": "Class:"
|
||||
}
|
||||
}
|
@ -86,6 +86,11 @@
|
||||
"ConfigurationDisabled": "Выключено",
|
||||
"ConfigDisable": "Выключить",
|
||||
"ConfigEnable": "Включить",
|
||||
"ConfigBeta": "Ознакомительная версия"
|
||||
"ConfigBeta": "Ознакомительная версия",
|
||||
"Properties": "Свойства",
|
||||
"TaskTypes": "Типы задач",
|
||||
"Automations": "Автоматизация",
|
||||
"Collections": "Коллекции",
|
||||
"ClassColon": "Класс:"
|
||||
}
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
<!--
|
||||
// Copyright © 2022 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import core, { AnyAttribute, EnumOf, Type } from '@hcengineering/core'
|
||||
import { IntlString } from '@hcengineering/platform'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { AnySvelteComponent, Icon, IconMoreV2, Label, IconOpenedArrow } from '@hcengineering/ui'
|
||||
import settings from '../plugin'
|
||||
|
||||
export let attribute: AnyAttribute
|
||||
export let attributeType: IntlString | undefined = undefined
|
||||
export let selected: boolean = false
|
||||
export let hovered: boolean = false
|
||||
export let clickMore: (event: MouseEvent) => Promise<void>
|
||||
|
||||
export let attributeMapper:
|
||||
| {
|
||||
component: AnySvelteComponent
|
||||
label: IntlString
|
||||
props: Record<string, any>
|
||||
}
|
||||
| undefined = undefined
|
||||
|
||||
const client = getClient()
|
||||
|
||||
async function getEnumName (type: Type<any>): Promise<string | undefined> {
|
||||
const ref = (type as EnumOf).of
|
||||
const res = await client.findOne(core.class.Enum, { _id: ref })
|
||||
return res?.name
|
||||
}
|
||||
</script>
|
||||
|
||||
<button class="hulyTableAttr-content__row" class:hovered class:selected on:contextmenu on:click>
|
||||
<button class="hulyTableAttr-content__row-dragMenu" on:click|stopPropagation={clickMore}>
|
||||
<IconMoreV2 size={'small'} />
|
||||
</button>
|
||||
{#if attribute.isCustom}
|
||||
<div class="hulyChip-item font-medium-12">
|
||||
<Label label={settings.string.Custom} />
|
||||
</div>
|
||||
{/if}
|
||||
{#if attribute.icon !== undefined}
|
||||
<div class="hulyTableAttr-content__row-icon">
|
||||
<Icon icon={attribute.icon} size={'small'} />
|
||||
</div>
|
||||
{/if}
|
||||
<div class="hulyTableAttr-content__row-label font-regular-14 grow" class:accent={!attribute.hidden}>
|
||||
<Label label={attribute.label} />
|
||||
</div>
|
||||
{#if attributeMapper}
|
||||
<svelte:component this={attributeMapper.component} {...attributeMapper.props} {attribute} />
|
||||
{/if}
|
||||
<div class="hulyTableAttr-content__row-type font-medium-12">
|
||||
<Label label={attribute.type.label} />
|
||||
{#if attributeType !== undefined}
|
||||
: <Label label={attributeType} />
|
||||
{/if}
|
||||
{#if attribute.type._class === core.class.EnumOf}
|
||||
{#await getEnumName(attribute.type) then name}
|
||||
{#if name}
|
||||
: {name}
|
||||
{/if}
|
||||
{/await}
|
||||
{/if}
|
||||
</div>
|
||||
<div class="hulyTableAttr-content__row-arrow">
|
||||
<IconOpenedArrow size={'small'} />
|
||||
</div>
|
||||
</button>
|
@ -13,50 +13,35 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import core, {
|
||||
AnyAttribute,
|
||||
ArrOf,
|
||||
AttachedDoc,
|
||||
Class,
|
||||
ClassifierKind,
|
||||
Collection,
|
||||
Doc,
|
||||
EnumOf,
|
||||
Ref,
|
||||
RefTo,
|
||||
Type
|
||||
} from '@hcengineering/core'
|
||||
import { IntlString, getResource } from '@hcengineering/platform'
|
||||
import presentation, { MessageBox, createQuery, getClient } from '@hcengineering/presentation'
|
||||
import core, { AnyAttribute, Class, ClassifierKind, Doc, Ref, Space } from '@hcengineering/core'
|
||||
import { IntlString } from '@hcengineering/platform'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import {
|
||||
Action,
|
||||
ActionIcon,
|
||||
AnySvelteComponent,
|
||||
ButtonIcon,
|
||||
Icon,
|
||||
IconAdd,
|
||||
IconDelete,
|
||||
IconEdit,
|
||||
IconMoreV2,
|
||||
Label,
|
||||
Menu,
|
||||
getEventPositionElement,
|
||||
showPopup,
|
||||
IconSettings,
|
||||
IconOpenedArrow
|
||||
ModernButton
|
||||
} from '@hcengineering/ui'
|
||||
import { getContextActions } from '@hcengineering/view-resources'
|
||||
import { ObjectPresenter } from '@hcengineering/view-resources'
|
||||
import settings from '../plugin'
|
||||
import CreateAttribute from './CreateAttribute.svelte'
|
||||
import ClassAttributesList from './ClassAttributesList.svelte'
|
||||
import EditAttribute from './EditAttribute.svelte'
|
||||
import EditClassLabel from './EditClassLabel.svelte'
|
||||
import { settingsStore, clearSettingsStore } from '../store'
|
||||
import TypesPopup from './typeEditors/TypesPopup.svelte'
|
||||
import { onDestroy } from 'svelte'
|
||||
|
||||
export let _class: Ref<Class<Doc>>
|
||||
export let ofClass: Ref<Class<Doc>> | undefined = undefined
|
||||
export let useOfClassAttributes = true
|
||||
export let showTitle = true
|
||||
export let showHierarchy: boolean = false
|
||||
export let showTitle: boolean = !showHierarchy
|
||||
|
||||
export let attributeMapper:
|
||||
| {
|
||||
@ -72,139 +57,65 @@
|
||||
const classQuery = createQuery()
|
||||
|
||||
let clazz: Class<Doc> | undefined
|
||||
let hovered: number | null = null
|
||||
let selected: number | null = null
|
||||
let btnAdd: ButtonIcon
|
||||
let classes: Class<Doc>[] = []
|
||||
let clazzHierarchy: Class<Doc<Space>>
|
||||
let selected: AnyAttribute | undefined = undefined
|
||||
|
||||
$: classQuery.query(core.class.Class, { _id: _class }, (res) => {
|
||||
clazz = res.shift()
|
||||
})
|
||||
$: attributes = getCustomAttributes(_class)
|
||||
|
||||
function getCustomAttributes (_class: Ref<Class<Doc>>): AnyAttribute[] {
|
||||
function hasCustomAttributes (_class: Ref<Class<Doc>>): boolean {
|
||||
const cl = hierarchy.getClass(_class)
|
||||
const attributes = Array.from(
|
||||
hierarchy
|
||||
.getAllAttributes(_class, _class === ofClass && useOfClassAttributes ? core.class.Doc : cl.extends)
|
||||
.values()
|
||||
hierarchy.getAllAttributes(_class, _class === ofClass ? core.class.Doc : cl.extends).values()
|
||||
)
|
||||
return attributes
|
||||
}
|
||||
|
||||
const attrQuery = createQuery()
|
||||
|
||||
$: attrQuery.query(core.class.Attribute, { attributeOf: _class }, () => {
|
||||
attributes = getCustomAttributes(_class)
|
||||
})
|
||||
|
||||
function update (): void {
|
||||
attributes = getCustomAttributes(_class)
|
||||
return attributes.length > 0
|
||||
}
|
||||
|
||||
export function createAttribute (ev: MouseEvent): void {
|
||||
showPopup(TypesPopup, { _class }, getEventPositionElement(ev), (_id) => {
|
||||
if (_id !== undefined) $settingsStore = { component: CreateAttribute, props: { _id, _class } }
|
||||
})
|
||||
// showPopup(CreateAttribute, { _class }, 'top', update)
|
||||
}
|
||||
|
||||
export async function editAttribute (attribute: AnyAttribute, exist: boolean): Promise<void> {
|
||||
showPopup(EditAttribute, { attribute, exist }, 'top', update)
|
||||
}
|
||||
|
||||
export async function removeAttribute (attribute: AnyAttribute, exist: boolean): Promise<void> {
|
||||
showPopup(
|
||||
MessageBox,
|
||||
{
|
||||
label: settings.string.DeleteAttribute,
|
||||
message: exist ? settings.string.DeleteAttributeExistConfirm : settings.string.DeleteAttributeConfirm
|
||||
},
|
||||
'top',
|
||||
async (result) => {
|
||||
if (result != null) {
|
||||
await client.remove(attribute)
|
||||
update()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
async function showMenu (ev: MouseEvent, attribute: AnyAttribute, row: number): Promise<void> {
|
||||
hovered = row
|
||||
const exist = (await client.findOne(attribute.attributeOf, { [attribute.name]: { $exists: true } })) !== undefined
|
||||
|
||||
const actions: Action[] = [
|
||||
{
|
||||
label: presentation.string.Edit,
|
||||
icon: IconEdit,
|
||||
action: async () => {
|
||||
await selectAttribute(attribute, row)
|
||||
}
|
||||
}
|
||||
]
|
||||
if (attribute.isCustom === true) {
|
||||
actions.push({
|
||||
label: presentation.string.Remove,
|
||||
icon: IconDelete,
|
||||
action: async () => {
|
||||
await removeAttribute(attribute, exist)
|
||||
}
|
||||
})
|
||||
}
|
||||
const extra = await getContextActions(client, attribute, { mode: 'context' })
|
||||
actions.push(
|
||||
...extra.map((it) => ({
|
||||
label: it.label,
|
||||
icon: it.icon,
|
||||
action: async (_: any, evt: Event) => {
|
||||
const r = await getResource(it.action)
|
||||
await r(attribute, evt, it.actionProps)
|
||||
}
|
||||
}))
|
||||
)
|
||||
showPopup(Menu, { actions }, getEventPositionElement(ev), () => {
|
||||
hovered = null
|
||||
})
|
||||
}
|
||||
|
||||
function getAttrType (type: Type<any>): IntlString | undefined {
|
||||
switch (type._class) {
|
||||
case core.class.RefTo:
|
||||
return client.getHierarchy().getClass((type as RefTo<Doc>).to).label
|
||||
case core.class.Collection:
|
||||
return client.getHierarchy().getClass((type as Collection<AttachedDoc>).of).label
|
||||
case core.class.ArrOf:
|
||||
return (type as ArrOf<Doc>).of.label
|
||||
default:
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
async function getEnumName (type: Type<any>): Promise<string | undefined> {
|
||||
const ref = (type as EnumOf).of
|
||||
const res = await client.findOne(core.class.Enum, { _id: ref })
|
||||
return res?.name
|
||||
}
|
||||
function editLabel (evt: MouseEvent): void {
|
||||
showPopup(EditClassLabel, { clazz }, getEventPositionElement(evt))
|
||||
}
|
||||
async function selectAttribute (attribute: AnyAttribute, n: number): Promise<void> {
|
||||
if (selected === n) {
|
||||
selected = null
|
||||
clearSettingsStore()
|
||||
return
|
||||
}
|
||||
selected = n
|
||||
const exist = (await client.findOne(attribute.attributeOf, { [attribute.name]: { $exists: true } })) !== undefined
|
||||
$settingsStore = { component: EditAttribute, props: { attribute, exist } }
|
||||
}
|
||||
settingsStore.subscribe((value) => {
|
||||
if (value.component == null) selected = null
|
||||
})
|
||||
|
||||
const classUpdated = (_clazz: Ref<Class<Doc>>): void => {
|
||||
selected = null
|
||||
selected = undefined
|
||||
classes = client
|
||||
.getHierarchy()
|
||||
.getAncestors(_class)
|
||||
.map((it) => client.getHierarchy().getClass(it))
|
||||
.filter((it) => {
|
||||
return (
|
||||
!it.hidden &&
|
||||
it.label !== undefined &&
|
||||
it._id !== core.class.Doc &&
|
||||
it._id !== core.class.AttachedDoc &&
|
||||
it._id !== _class
|
||||
)
|
||||
})
|
||||
clazzHierarchy = client.getHierarchy().getClass(_class)
|
||||
}
|
||||
$: classUpdated(_class)
|
||||
|
||||
settingsStore.subscribe((value) => {
|
||||
if (value.id === undefined) selected = undefined
|
||||
})
|
||||
const handleDeselect = (): void => {
|
||||
selected = undefined
|
||||
clearSettingsStore()
|
||||
}
|
||||
const handleSelect = async (event: CustomEvent): Promise<void> => {
|
||||
selected = event.detail as AnyAttribute
|
||||
const exist = (await client.findOne(selected.attributeOf, { [selected.name]: { $exists: true } })) !== undefined
|
||||
$settingsStore = { id: selected._id, component: EditAttribute, props: { attribute: selected, exist } }
|
||||
}
|
||||
onDestroy(() => {
|
||||
if (selected !== undefined) clearSettingsStore()
|
||||
})
|
||||
</script>
|
||||
|
||||
{#if showTitle}
|
||||
@ -222,11 +133,17 @@
|
||||
{/if}
|
||||
{/if}
|
||||
<div class="hulyTableAttr-container">
|
||||
<div class="hulyTableAttr-header font-medium-12">
|
||||
<IconSettings size={'small'} />
|
||||
<span><Label label={settings.string.ClassProperties} /></span>
|
||||
<div class="hulyTableAttr-header font-medium-12" class:withButton={showHierarchy}>
|
||||
{#if showHierarchy}
|
||||
<ModernButton icon={IconSettings} kind={'secondary'} size={'small'} hasMenu>
|
||||
<Label label={settings.string.ClassColon} />
|
||||
<ObjectPresenter _class={clazzHierarchy._class} objectId={clazzHierarchy._id} value={clazzHierarchy} />
|
||||
</ModernButton>
|
||||
{:else}
|
||||
<IconSettings size={'small'} />
|
||||
<span><Label label={settings.string.ClassProperties} /></span>
|
||||
{/if}
|
||||
<ButtonIcon
|
||||
bind:this={btnAdd}
|
||||
kind={'primary'}
|
||||
icon={IconAdd}
|
||||
size={'small'}
|
||||
@ -235,63 +152,50 @@
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{#if attributes.length}
|
||||
<div class="hulyTableAttr-content">
|
||||
{#each attributes as attr, i}
|
||||
{@const attrType = getAttrType(attr.type)}
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
class="hulyTableAttr-content__row"
|
||||
class:hovered={hovered === i}
|
||||
class:selected={selected === i}
|
||||
on:contextmenu={(ev) => {
|
||||
ev.preventDefault()
|
||||
void showMenu(ev, attr, i)
|
||||
}}
|
||||
on:click={async () => {
|
||||
void selectAttribute(attr, i)
|
||||
}}
|
||||
>
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<div class="hulyTableAttr-content__row-dragMenu" on:click|stopPropagation={(ev) => showMenu(ev, attr, i)}>
|
||||
<IconMoreV2 size={'small'} />
|
||||
</div>
|
||||
{#if attr.isCustom}
|
||||
<div class="hulyChip-item font-medium-12">
|
||||
<Label label={settings.string.Custom} />
|
||||
</div>
|
||||
{/if}
|
||||
{#if attr.icon !== undefined}
|
||||
<div class="hulyTableAttr-content__row-icon">
|
||||
<Icon icon={attr.icon} size={'small'} />
|
||||
</div>
|
||||
{/if}
|
||||
<div class="hulyTableAttr-content__row-label font-regular-14" class:accent={!attr.hidden}>
|
||||
<Label label={attr.label} />
|
||||
</div>
|
||||
{#if attributeMapper}
|
||||
<svelte:component this={attributeMapper.component} {...attributeMapper.props} attribute={attr} />
|
||||
{/if}
|
||||
<div class="hulyTableAttr-content__row-type font-medium-12">
|
||||
<Label label={attr.type.label} />
|
||||
{#if attrType !== undefined}
|
||||
: <Label label={attrType} />
|
||||
{/if}
|
||||
{#if attr.type._class === core.class.EnumOf}
|
||||
{#await getEnumName(attr.type) then name}
|
||||
{#if name}
|
||||
: {name}
|
||||
{/if}
|
||||
{/await}
|
||||
{/if}
|
||||
</div>
|
||||
<div class="hulyTableAttr-content__row-arrow">
|
||||
<IconOpenedArrow size={'small'} />
|
||||
</div>
|
||||
{#if showHierarchy}
|
||||
<div class="hulyTableAttr-content class withTitle">
|
||||
<div class="hulyTableAttr-content__title">
|
||||
<Label label={settings.string.Properties} />
|
||||
</div>
|
||||
<div class="hulyTableAttr-content__wrapper">
|
||||
<ClassAttributesList
|
||||
{_class}
|
||||
{ofClass}
|
||||
{attributeMapper}
|
||||
{selected}
|
||||
on:deselect={handleDeselect}
|
||||
on:select={handleSelect}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{#each classes as clazz2}
|
||||
<div class="hulyTableAttr-content class withTitle">
|
||||
<div class="hulyTableAttr-content__title">
|
||||
<Label label={clazz2.label} />
|
||||
</div>
|
||||
{/each}
|
||||
<div class="hulyTableAttr-content__wrapper">
|
||||
<ClassAttributesList
|
||||
_class={clazz2._id}
|
||||
{ofClass}
|
||||
{attributeMapper}
|
||||
{selected}
|
||||
notUseOfClass
|
||||
on:deselect={handleDeselect}
|
||||
on:select={handleSelect}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
{:else if hasCustomAttributes(_class)}
|
||||
<div class="hulyTableAttr-content class">
|
||||
<ClassAttributesList
|
||||
{_class}
|
||||
{ofClass}
|
||||
{attributeMapper}
|
||||
{selected}
|
||||
on:deselect={handleDeselect}
|
||||
on:select={handleSelect}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
@ -306,114 +210,4 @@
|
||||
line-height: 2rem;
|
||||
color: var(--input-TextColor);
|
||||
}
|
||||
.hulyTableAttr-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
align-self: stretch;
|
||||
background-color: var(--theme-table-row-color);
|
||||
border: 1px solid var(--theme-divider-color);
|
||||
border-radius: var(--large-BorderRadius);
|
||||
|
||||
.hulyTableAttr-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
align-self: stretch;
|
||||
flex-shrink: 0;
|
||||
padding: var(--spacing-2) var(--spacing-2) var(--spacing-2) var(--spacing-2_5);
|
||||
text-transform: uppercase;
|
||||
color: var(--global-secondary-TextColor);
|
||||
|
||||
span {
|
||||
flex-grow: 1;
|
||||
margin-left: var(--spacing-1_5);
|
||||
}
|
||||
}
|
||||
.hulyTableAttr-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
align-self: stretch;
|
||||
flex-shrink: 0;
|
||||
padding: var(--spacing-1);
|
||||
border-top: 1px solid var(--theme-divider-color);
|
||||
|
||||
&__row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-self: stretch;
|
||||
gap: var(--spacing-1);
|
||||
padding: var(--spacing-1) var(--spacing-2) var(--spacing-1) var(--spacing-1);
|
||||
border-radius: var(--small-BorderRadius);
|
||||
cursor: pointer;
|
||||
|
||||
&-dragMenu {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
width: var(--global-extra-small-Size);
|
||||
height: var(--global-extra-small-Size);
|
||||
border-radius: var(--extra-small-BorderRadius);
|
||||
}
|
||||
&-icon {
|
||||
width: var(--global-min-Size);
|
||||
height: var(--global-min-Size);
|
||||
color: var(--global-primary-TextColor);
|
||||
}
|
||||
&-label {
|
||||
white-space: nowrap;
|
||||
word-break: break-all;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-grow: 1;
|
||||
min-width: 0;
|
||||
color: var(--global-primary-TextColor);
|
||||
|
||||
&.accent {
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
&-type {
|
||||
text-transform: uppercase;
|
||||
color: var(--global-secondary-TextColor);
|
||||
}
|
||||
&-arrow {
|
||||
display: none;
|
||||
flex-shrink: 0;
|
||||
width: var(--global-min-Size);
|
||||
height: var(--global-min-Size);
|
||||
color: var(--global-primary-LinkColor);
|
||||
}
|
||||
|
||||
&.hovered,
|
||||
&:hover {
|
||||
background-color: var(--theme-table-header-color); // var(--global-surface-03-hover-BackgroundColor);
|
||||
}
|
||||
&.selected {
|
||||
background-color: var(--theme-table-header-color); // var(--global-surface-03-hover-BackgroundColor);
|
||||
|
||||
.hulyTableAttr-content__row-icon,
|
||||
.hulyTableAttr-content__row-arrow,
|
||||
.hulyTableAttr-content__row-label {
|
||||
color: var(--global-primary-LinkColor);
|
||||
}
|
||||
.hulyTableAttr-content__row-type {
|
||||
color: var(--global-primary-TextColor);
|
||||
}
|
||||
.hulyTableAttr-content__row-label {
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
&.hovered .hulyTableAttr-content__row-arrow,
|
||||
&:hover .hulyTableAttr-content__row-arrow,
|
||||
&.selected .hulyTableAttr-content__row-arrow {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -13,50 +13,28 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import core, {
|
||||
AnyAttribute,
|
||||
ArrOf,
|
||||
AttachedDoc,
|
||||
Class,
|
||||
ClassifierKind,
|
||||
Collection,
|
||||
Doc,
|
||||
EnumOf,
|
||||
Ref,
|
||||
RefTo,
|
||||
Type
|
||||
} from '@hcengineering/core'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import core, { AnyAttribute, ArrOf, AttachedDoc, Class, Collection, Doc, Ref, RefTo, Type } from '@hcengineering/core'
|
||||
import { IntlString, getResource } from '@hcengineering/platform'
|
||||
import presentation, { MessageBox, createQuery, getClient } from '@hcengineering/presentation'
|
||||
import {
|
||||
Action,
|
||||
ActionIcon,
|
||||
AnySvelteComponent,
|
||||
CircleButton,
|
||||
Icon,
|
||||
IconAdd,
|
||||
IconDelete,
|
||||
IconEdit,
|
||||
IconMoreV2,
|
||||
Label,
|
||||
Menu,
|
||||
getEventPositionElement,
|
||||
getEventPopupPositionElement,
|
||||
showPopup
|
||||
} from '@hcengineering/ui'
|
||||
import { getContextActions } from '@hcengineering/view-resources'
|
||||
import settings from '../plugin'
|
||||
import CreateAttribute from './CreateAttribute.svelte'
|
||||
import ClassAttributeRow from './ClassAttributeRow.svelte'
|
||||
import EditAttribute from './EditAttribute.svelte'
|
||||
import EditClassLabel from './EditClassLabel.svelte'
|
||||
import { settingsStore } from '../store'
|
||||
import TypesPopup from './typeEditors/TypesPopup.svelte'
|
||||
|
||||
export let _class: Ref<Class<Doc>>
|
||||
export let ofClass: Ref<Class<Doc>> | undefined = undefined
|
||||
export let useOfClassAttributes = true
|
||||
export let showTitle = true
|
||||
export let showCreate = true
|
||||
export let notUseOfClass: boolean = false
|
||||
export let selected: AnyAttribute | undefined = undefined
|
||||
|
||||
export let attributeMapper:
|
||||
| {
|
||||
@ -66,12 +44,15 @@
|
||||
}
|
||||
| undefined = undefined
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
|
||||
const classQuery = createQuery()
|
||||
|
||||
let clazz: Class<Doc> | undefined
|
||||
let hovered: number | null = null
|
||||
|
||||
$: classQuery.query(core.class.Class, { _id: _class }, (res) => {
|
||||
clazz = res.shift()
|
||||
@ -81,9 +62,7 @@
|
||||
function getCustomAttributes (_class: Ref<Class<Doc>>): AnyAttribute[] {
|
||||
const cl = hierarchy.getClass(_class)
|
||||
const attributes = Array.from(
|
||||
hierarchy
|
||||
.getAllAttributes(_class, _class === ofClass && useOfClassAttributes ? core.class.Doc : cl.extends)
|
||||
.values()
|
||||
hierarchy.getAllAttributes(_class, _class === ofClass && !notUseOfClass ? core.class.Doc : cl.extends).values()
|
||||
)
|
||||
return attributes
|
||||
}
|
||||
@ -98,12 +77,6 @@
|
||||
attributes = getCustomAttributes(_class)
|
||||
}
|
||||
|
||||
export function createAttribute (ev: MouseEvent): void {
|
||||
showPopup(TypesPopup, { _class }, getEventPopupPositionElement(ev), (_id) => {
|
||||
if (_id !== undefined) $settingsStore = { component: CreateAttribute, props: { _id, _class } }
|
||||
})
|
||||
}
|
||||
|
||||
export async function editAttribute (attribute: AnyAttribute, exist: boolean): Promise<void> {
|
||||
showPopup(EditAttribute, { attribute, exist }, 'top', update)
|
||||
}
|
||||
@ -125,7 +98,8 @@
|
||||
)
|
||||
}
|
||||
|
||||
async function showMenu (ev: MouseEvent, attribute: AnyAttribute): Promise<void> {
|
||||
async function showMenu (ev: MouseEvent, attribute: AnyAttribute, row: number): Promise<void> {
|
||||
hovered = row
|
||||
const exist = (await client.findOne(attribute.attributeOf, { [attribute.name]: { $exists: true } })) !== undefined
|
||||
|
||||
const actions: Action[] = [
|
||||
@ -133,7 +107,7 @@
|
||||
label: presentation.string.Edit,
|
||||
icon: IconEdit,
|
||||
action: async () => {
|
||||
await editAttribute(attribute, exist)
|
||||
dispatch('select', attribute._id)
|
||||
}
|
||||
}
|
||||
]
|
||||
@ -157,7 +131,9 @@
|
||||
}
|
||||
}))
|
||||
)
|
||||
showPopup(Menu, { actions }, getEventPositionElement(ev))
|
||||
showPopup(Menu, { actions }, getEventPositionElement(ev), () => {
|
||||
hovered = null
|
||||
})
|
||||
}
|
||||
|
||||
function getAttrType (type: Type<any>): IntlString | undefined {
|
||||
@ -172,116 +148,27 @@
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
async function getEnumName (type: Type<any>): Promise<string | undefined> {
|
||||
const ref = (type as EnumOf).of
|
||||
const res = await client.findOne(core.class.Enum, { _id: ref })
|
||||
return res?.name
|
||||
}
|
||||
function editLabel (evt: MouseEvent): void {
|
||||
showPopup(EditClassLabel, { clazz }, getEventPositionElement(evt))
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if showTitle}
|
||||
<div class="flex-row-center fs-title mb-3">
|
||||
{#if clazz?.icon}
|
||||
<div class="mr-2 flex">
|
||||
<Icon icon={clazz.icon} size={'medium'} />
|
||||
{#if clazz.kind === ClassifierKind.MIXIN && hierarchy.hasMixin(clazz, settings.mixin.UserMixin)}
|
||||
<Icon icon={IconAdd} size={'x-small'} />
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
{#if clazz}
|
||||
<Label label={clazz.label} />
|
||||
{#if clazz.kind === ClassifierKind.MIXIN && hierarchy.hasMixin(clazz, settings.mixin.UserMixin)}
|
||||
<div class="ml-2">
|
||||
<ActionIcon icon={IconEdit} size="small" action={editLabel} />
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
{#if showCreate}
|
||||
<div class="flex-between trans-title mb-3">
|
||||
<Label label={settings.string.Attributes} />
|
||||
<CircleButton icon={IconAdd} size="medium" on:click={createAttribute} />
|
||||
</div>
|
||||
{/if}
|
||||
{#each attributes as attr, i}
|
||||
{@const attrType = getAttrType(attr.type)}
|
||||
<tr
|
||||
class="antiTable-body__row"
|
||||
on:contextmenu={(ev) => {
|
||||
ev.preventDefault()
|
||||
void showMenu(ev, attr)
|
||||
<ClassAttributeRow
|
||||
attribute={attr}
|
||||
attributeType={attrType}
|
||||
selected={selected && attr._id === selected._id}
|
||||
hovered={hovered === i}
|
||||
{attributeMapper}
|
||||
clickMore={async (event) => {
|
||||
event.preventDefault()
|
||||
void showMenu(event, attr, i)
|
||||
}}
|
||||
>
|
||||
<td>
|
||||
{#if i === 0 && clazz?.label !== undefined}
|
||||
<div class="trans-title">
|
||||
<Label label={clazz.label} />
|
||||
</div>
|
||||
{/if}
|
||||
</td>
|
||||
<td>
|
||||
<div class="antiTable-cells__firstCell whitespace-nowrap flex-row-center">
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<div id="context-menu" on:click={(ev) => showMenu(ev, attr)}>
|
||||
<div class="p-1">
|
||||
<IconMoreV2 size={'medium'} />
|
||||
</div>
|
||||
</div>
|
||||
{#if attr.icon !== undefined}
|
||||
<div class="p-1">
|
||||
<Icon icon={attr.icon} size={'small'} />
|
||||
</div>
|
||||
{/if}
|
||||
{#if attr.isCustom}
|
||||
<div class="trans-title p-1">
|
||||
<Label label={settings.string.Custom} />
|
||||
</div>
|
||||
{/if}
|
||||
<div class:accent={!attr.hidden}>
|
||||
<Label label={attr.label} />
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="select-text whitespace-nowrap trans-title text-xs text-right" style:padding-right={'1rem !important'}>
|
||||
<Label label={attr.type.label} />
|
||||
{#if attrType !== undefined}
|
||||
: <Label label={attrType} />
|
||||
{/if}
|
||||
{#if attr.type._class === core.class.EnumOf}
|
||||
{#await getEnumName(attr.type) then name}
|
||||
{#if name}
|
||||
: {name}
|
||||
{/if}
|
||||
{/await}
|
||||
{/if}
|
||||
</td>
|
||||
{#if attributeMapper}
|
||||
<td>
|
||||
<svelte:component this={attributeMapper.component} {...attributeMapper.props} attribute={attr} />
|
||||
</td>
|
||||
{/if}
|
||||
</tr>
|
||||
on:contextmenu={async (event) => {
|
||||
event.preventDefault()
|
||||
void showMenu(event, attr, i)
|
||||
}}
|
||||
on:click={async () => {
|
||||
if (selected && selected._id === attr._id) dispatch('deselect')
|
||||
else dispatch('select', attr)
|
||||
}}
|
||||
/>
|
||||
{/each}
|
||||
{#if attributes.length === 0}
|
||||
<tr class="antiTable-body__row">
|
||||
<td>
|
||||
<div class="trans-title">
|
||||
{#if clazz}
|
||||
<Label label={clazz.label} />
|
||||
{/if}
|
||||
</div>
|
||||
</td>
|
||||
<td class="select-text whitespace-nowrap"> </td>
|
||||
<td> </td>
|
||||
{#if attributeMapper}
|
||||
<td> </td>
|
||||
{/if}
|
||||
</tr>
|
||||
{/if}
|
||||
|
@ -110,7 +110,7 @@
|
||||
{/if}
|
||||
<div class="hulyComponent-content__container columns">
|
||||
<div class="hulyComponent-content__column">
|
||||
<div class="hulyComponent-content__navHeader">
|
||||
<div class="hulyComponent-content__navHeader divide">
|
||||
<div class="hulyComponent-content__navHeader-menu">
|
||||
<ButtonIcon kind={'tertiary'} icon={IconDescription} size={'small'} inheritColor />
|
||||
</div>
|
||||
@ -134,13 +134,13 @@
|
||||
</div>
|
||||
<Separator name={'workspaceSettings'} index={0} color={'var(--theme-divider-color)'} />
|
||||
<div class="hulyComponent-content__column content">
|
||||
<div class="hulyComponent-content">
|
||||
{#if _class !== undefined}
|
||||
<Scroller>
|
||||
<ClassAttributes {_class} {ofClass} {attributeMapper} {useOfClassAttributes} />
|
||||
</Scroller>
|
||||
{/if}
|
||||
</div>
|
||||
<Scroller align={'center'} padding={'var(--spacing-3)'} bottomPadding={'var(--spacing-3)'}>
|
||||
<div class="hulyComponent-content">
|
||||
{#if _class !== undefined}
|
||||
<ClassAttributes {_class} {ofClass} {attributeMapper} />
|
||||
{/if}
|
||||
</div>
|
||||
</Scroller>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -16,7 +16,7 @@
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { PluginConfiguration } from '@hcengineering/core'
|
||||
import { configurationStore, getClient } from '@hcengineering/presentation'
|
||||
import { Button, Icon, IconInfo, Label, Header, Breadcrumb } from '@hcengineering/ui'
|
||||
import { Button, Icon, IconInfo, Label, Header, Breadcrumb, Scroller } from '@hcengineering/ui'
|
||||
import setting from '../plugin'
|
||||
|
||||
export let visibleNav: boolean = true
|
||||
@ -37,39 +37,41 @@
|
||||
<Breadcrumb icon={setting.icon.Setting} label={setting.string.Configuration} size={'large'} isCurrent />
|
||||
</Header>
|
||||
<div class="hulyComponent-content__column content">
|
||||
<div class="flex-row-center flex-wrap gap-around-4">
|
||||
{#each $configurationStore.list as config}
|
||||
{#if config.label}
|
||||
<div class="cardBox flex-col clear-mins" class:enabled={config.enabled ?? true}>
|
||||
<div class="flex-row-center">
|
||||
<span class="mr-2">
|
||||
<Icon icon={config.icon ?? IconInfo} size={'medium'} />
|
||||
</span>
|
||||
<span class="fs-title">
|
||||
<Label label={config.label} />
|
||||
</span>
|
||||
</div>
|
||||
{#if config.description}
|
||||
<div class="my-3 flex-grow clear-mins">
|
||||
<Label label={config.description} />
|
||||
<Scroller align={'center'} padding={'var(--spacing-3)'} bottomPadding={'var(--spacing-3)'}>
|
||||
<div class="flex-row-center flex-wrap gap-around-4">
|
||||
{#each $configurationStore.list as config}
|
||||
{#if config.label}
|
||||
<div class="cardBox flex-col clear-mins" class:enabled={config.enabled ?? true}>
|
||||
<div class="flex-row-center">
|
||||
<span class="mr-2">
|
||||
<Icon icon={config.icon ?? IconInfo} size={'medium'} />
|
||||
</span>
|
||||
<span class="fs-title">
|
||||
<Label label={config.label} />
|
||||
</span>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="flex-between flex-row-center">
|
||||
{#if config.beta}
|
||||
<Label label={setting.string.ConfigBeta} />
|
||||
{#if config.description}
|
||||
<div class="my-3 flex-grow clear-mins">
|
||||
<Label label={config.description} />
|
||||
</div>
|
||||
{/if}
|
||||
<div class="flex-row-center flex-reverse flex-grow max-h-9">
|
||||
<Button
|
||||
label={config.enabled ?? true ? setting.string.ConfigDisable : setting.string.ConfigEnable}
|
||||
size={'large'}
|
||||
on:click={() => change(config, !(config.enabled ?? true))}
|
||||
/>
|
||||
<div class="flex-between flex-row-center">
|
||||
{#if config.beta}
|
||||
<Label label={setting.string.ConfigBeta} />
|
||||
{/if}
|
||||
<div class="flex-row-center flex-reverse flex-grow max-h-9">
|
||||
<Button
|
||||
label={config.enabled ?? true ? setting.string.ConfigDisable : setting.string.ConfigEnable}
|
||||
size={'large'}
|
||||
on:click={() => change(config, !(config.enabled ?? true))}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
</Scroller>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -28,7 +28,8 @@
|
||||
Breadcrumb,
|
||||
defineSeparators,
|
||||
settingsSeparators,
|
||||
Separator
|
||||
Separator,
|
||||
Scroller
|
||||
} from '@hcengineering/ui'
|
||||
import { ContextMenu } from '@hcengineering/view-resources'
|
||||
import setting from '../plugin'
|
||||
@ -104,11 +105,13 @@
|
||||
</div>
|
||||
<Separator name={'workspaceSettings'} index={0} color={'var(--theme-divider-color)'} />
|
||||
<div class="hulyComponent-content__column content">
|
||||
<div class="hulyComponent-content">
|
||||
{#if selected !== undefined}
|
||||
<EnumValues value={selected} />
|
||||
{/if}
|
||||
</div>
|
||||
<Scroller align={'center'} padding={'var(--spacing-3)'} bottomPadding={'var(--spacing-3)'}>
|
||||
<div class="hulyComponent-content">
|
||||
{#if selected !== undefined}
|
||||
<EnumValues value={selected} />
|
||||
{/if}
|
||||
</div>
|
||||
</Scroller>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -18,7 +18,7 @@
|
||||
import { EmployeePresenter, personByIdStore } from '@hcengineering/contact-resources'
|
||||
import { AccountRole, SortingOrder, getCurrentAccount } from '@hcengineering/core'
|
||||
import presentation, { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { DropdownIntlItem, DropdownLabelsIntl, EditBox, Header, Breadcrumb } from '@hcengineering/ui'
|
||||
import { DropdownIntlItem, DropdownLabelsIntl, EditBox, Header, Breadcrumb, Scroller } from '@hcengineering/ui'
|
||||
import setting from '../plugin'
|
||||
|
||||
export let visibleNav: boolean = true
|
||||
@ -65,32 +65,34 @@
|
||||
<EditBox kind={'search-style'} focusIndex={1} bind:value={search} placeholder={presentation.string.Search} />
|
||||
</Header>
|
||||
<div class="hulyComponent-content__column content">
|
||||
<div class="hulyComponent-content">
|
||||
{#each accounts as account (account._id)}
|
||||
{@const employee = $personByIdStore.get(account.person)}
|
||||
{#if employee?.name?.includes(search)}
|
||||
<div class="flex-row-center p-2 flex-no-shrink">
|
||||
<div class="p-1 min-w-80">
|
||||
{#if employee}
|
||||
<EmployeePresenter value={employee} disabled={false} />
|
||||
{:else}
|
||||
{account.email}
|
||||
{/if}
|
||||
<Scroller align={'center'} padding={'var(--spacing-3)'} bottomPadding={'var(--spacing-3)'}>
|
||||
<div class="hulyComponent-content">
|
||||
{#each accounts as account (account._id)}
|
||||
{@const employee = $personByIdStore.get(account.person)}
|
||||
{#if employee?.name?.includes(search)}
|
||||
<div class="flex-row-center p-2 flex-no-shrink">
|
||||
<div class="p-1 min-w-80">
|
||||
{#if employee}
|
||||
<EmployeePresenter value={employee} disabled={false} />
|
||||
{:else}
|
||||
{account.email}
|
||||
{/if}
|
||||
</div>
|
||||
<DropdownLabelsIntl
|
||||
label={setting.string.Role}
|
||||
disabled={account.role > currentRole || (account.role === AccountRole.Owner && owners.length === 1)}
|
||||
kind={'primary'}
|
||||
size={'medium'}
|
||||
{items}
|
||||
selected={account.role?.toString()}
|
||||
on:selected={(e) => {
|
||||
void change(account, Number(e.detail))
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<DropdownLabelsIntl
|
||||
label={setting.string.Role}
|
||||
disabled={account.role > currentRole || (account.role === AccountRole.Owner && owners.length === 1)}
|
||||
kind={'primary'}
|
||||
size={'medium'}
|
||||
{items}
|
||||
selected={account.role?.toString()}
|
||||
on:selected={(e) => {
|
||||
void change(account, Number(e.detail))
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
</Scroller>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -78,6 +78,7 @@ export default mergeIds(settingId, setting, {
|
||||
ConfigEnable: '' as IntlString,
|
||||
ConfigBeta: '' as IntlString,
|
||||
ClassSettingHint: '' as IntlString,
|
||||
ClassProperties: '' as IntlString
|
||||
ClassProperties: '' as IntlString,
|
||||
ClassColon: '' as IntlString
|
||||
}
|
||||
})
|
||||
|
@ -20,8 +20,9 @@ import { writable } from 'svelte/store'
|
||||
* @public
|
||||
*/
|
||||
export interface SettingsStore {
|
||||
component?: ComponentType | null
|
||||
props?: object | null
|
||||
id?: any | undefined
|
||||
component?: ComponentType | undefined
|
||||
props?: object | undefined
|
||||
}
|
||||
|
||||
/**
|
||||
@ -33,5 +34,5 @@ export const settingsStore = writable<SettingsStore>({})
|
||||
* @public
|
||||
*/
|
||||
export const clearSettingsStore = (): void => {
|
||||
settingsStore.set({ component: null, props: null })
|
||||
settingsStore.set({ id: undefined, component: undefined, props: undefined })
|
||||
}
|
||||
|
@ -169,7 +169,11 @@ export default plugin(settingId, {
|
||||
Classes: '' as IntlString,
|
||||
Owners: '' as IntlString,
|
||||
Configure: '' as IntlString,
|
||||
InviteSettings: '' as IntlString
|
||||
InviteSettings: '' as IntlString,
|
||||
Properties: '' as IntlString,
|
||||
TaskTypes: '' as IntlString,
|
||||
Automations: '' as IntlString,
|
||||
Collections: '' as IntlString
|
||||
},
|
||||
icon: {
|
||||
AccountSettings: '' as Asset,
|
||||
|
@ -84,6 +84,10 @@
|
||||
"TaskType": "Task type",
|
||||
"ManageProjects": "Project types",
|
||||
"CreateProjectType": "Create project type",
|
||||
"ClassicProject": "Classic project"
|
||||
"ClassicProject": "Classic project",
|
||||
"LastSave": "Last save",
|
||||
"Published": "Published",
|
||||
"CountProjects": "{count, plural, =0 {No projects} =1 {# project} other {# projects}}",
|
||||
"ProjectTypeTitle": "Project type title"
|
||||
}
|
||||
}
|
@ -84,6 +84,10 @@
|
||||
"TaskType": "Тип задачи",
|
||||
"ManageProjects": "Управление проектами",
|
||||
"CreateProjectType": "Создать тип проекта",
|
||||
"ClassicProject": "Классический проект"
|
||||
"ClassicProject": "Классический проект",
|
||||
"LastSave": "Последнее сохранение",
|
||||
"Published": "Опубликовано",
|
||||
"CountProjects": "{count, plural, =0 {Нет проектов} =1 {# проект} other {# проектов}}",
|
||||
"ProjectTypeTitle": "Название типа проекта"
|
||||
}
|
||||
}
|
30
plugins/task-resources/src/components/icons/Layers.svelte
Normal file
30
plugins/task-resources/src/components/icons/Layers.svelte
Normal file
@ -0,0 +1,30 @@
|
||||
<!--
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
export let size: 'small' | 'medium' | 'large'
|
||||
const fill: string = 'currentColor'
|
||||
</script>
|
||||
|
||||
<svg class="svg-{size}" {fill} viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M16 24.0001C15.8345 24.0001 15.6715 23.9589 15.5259 23.8801L3.40621 17.3541C2.92006 17.0923 2.73823 16.486 3.00011 15.9999C3.26191 15.514 3.86801 15.3322 4.35401 15.5939L16 21.8644L27.6456 15.5941C28.1318 15.3323 28.7381 15.5142 29 16.0004C29.2618 16.4866 29.0799 17.0931 28.5937 17.3549L16.4741 23.8808C16.3284 23.9593 16.1655 24.0003 16 24.0001Z"
|
||||
/>
|
||||
<path
|
||||
d="M16 30.0001C15.8345 30.0001 15.6715 29.9589 15.5259 29.8801L3.40621 23.3541C2.92006 23.0923 2.73823 22.486 3.00011 21.9999C3.26191 21.514 3.86801 21.3322 4.35401 21.5939L16 27.8644L27.6456 21.5941C28.1318 21.3323 28.7381 21.5142 29 22.0004C29.2618 22.4866 29.0799 23.0931 28.5937 23.3549L16.4741 29.8808C16.3284 29.9593 16.1655 30.0003 16 30.0001Z"
|
||||
/>
|
||||
<path
|
||||
d="M16 18.0001C15.8345 18.0001 15.6715 17.9589 15.5259 17.8801L2.5259 10.8801C2.36698 10.7945 2.23419 10.6674 2.14164 10.5124C2.04909 10.3574 2.00023 10.1803 2.00023 9.99976C2.00023 9.81923 2.04909 9.64208 2.14164 9.48708C2.23419 9.33209 2.36698 9.20503 2.5259 9.11941L15.5259 2.11941C15.6716 2.04079 15.8345 1.99963 16 1.99963C16.1655 1.99963 16.3284 2.04079 16.4741 2.11941L29.4741 9.11941C29.633 9.20503 29.7658 9.33209 29.8584 9.48708C29.9509 9.64208 29.9998 9.81923 29.9998 9.99976C29.9998 10.1803 29.9509 10.3574 29.8584 10.5124C29.7658 10.6674 29.633 10.7945 29.4741 10.8801L16.4741 17.8801C16.3285 17.9589 16.1655 18.0001 16 18.0001ZM5.1094 10.0001L16 15.8644L26.8906 10.0001L16 4.13591L5.1094 10.0001Z"
|
||||
/>
|
||||
</svg>
|
@ -15,10 +15,10 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import { createQuery, hasResource } from '@hcengineering/presentation'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import setting from '@hcengineering/setting'
|
||||
import task, { ProjectTypeDescriptor } from '@hcengineering/task'
|
||||
import { Component, Icon, IconChevronDown, Label } from '@hcengineering/ui'
|
||||
import { Icon, IconChevronDown, Label } from '@hcengineering/ui'
|
||||
|
||||
export let descriptor: ProjectTypeDescriptor | undefined
|
||||
export let descriptorId: Ref<ProjectTypeDescriptor> | undefined
|
||||
@ -32,7 +32,7 @@
|
||||
})
|
||||
|
||||
$: if (descriptor === undefined && categories.length > 0) {
|
||||
descriptor = categories.filter((f) => hasResource(f.icon))[0]
|
||||
descriptor = categories.filter((f) => f.icon)[0]
|
||||
}
|
||||
|
||||
function select (item: ProjectTypeDescriptor): void {
|
||||
@ -45,7 +45,7 @@
|
||||
</div>
|
||||
<div class="flex-col overflow-y-auto">
|
||||
{#each categories as f (f._id)}
|
||||
{#if hasResource(f.icon)}
|
||||
{#if f.icon}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<div
|
||||
@ -56,7 +56,7 @@
|
||||
}}
|
||||
>
|
||||
<div class="icon flex-no-shrink mr-4">
|
||||
<Component is={f.icon} />
|
||||
<Icon icon={f.icon} size={'small'} />
|
||||
</div>
|
||||
<div class="flex-grow flex-col">
|
||||
<div class="fs-title overflow-label">
|
||||
|
@ -18,19 +18,20 @@
|
||||
import { ComponentExtensions, createQuery, getClient } from '@hcengineering/presentation'
|
||||
import task, { Project, ProjectType, ProjectTypeDescriptor, Task, TaskType } from '@hcengineering/task'
|
||||
|
||||
import { Ref, SortingOrder, Status } from '@hcengineering/core'
|
||||
import { Ref, SortingOrder } from '@hcengineering/core'
|
||||
import { getEmbeddedLabel } from '@hcengineering/platform'
|
||||
import {
|
||||
Button,
|
||||
import type { Asset, IntlString } from '@hcengineering/platform'
|
||||
import ui, {
|
||||
ButtonIcon,
|
||||
Component,
|
||||
EditBox,
|
||||
Icon,
|
||||
ModernEditbox,
|
||||
TextArea,
|
||||
IconAdd,
|
||||
IconCopy,
|
||||
IconDelete,
|
||||
IconFile,
|
||||
IconSquareExpand,
|
||||
IconMoreV,
|
||||
IconFolder,
|
||||
Label,
|
||||
Location,
|
||||
eventToHTMLElement,
|
||||
@ -39,15 +40,25 @@
|
||||
resolvedLocationStore,
|
||||
showPopup,
|
||||
Header,
|
||||
Breadcrumbs
|
||||
Breadcrumbs,
|
||||
ModernButton,
|
||||
IconSend,
|
||||
IconDescription,
|
||||
Separator,
|
||||
Scroller,
|
||||
defineSeparators,
|
||||
secondNavSeparators,
|
||||
NavItem
|
||||
} from '@hcengineering/ui'
|
||||
import { ContextMenu } from '@hcengineering/view-resources'
|
||||
import plugin from '../../plugin'
|
||||
import setting from '@hcengineering/setting'
|
||||
import { ClassAttributes } from '@hcengineering/setting-resources'
|
||||
import CreateTaskType from '../taskTypes/CreateTaskType.svelte'
|
||||
import TaskTypeEditor from '../taskTypes/TaskTypeEditor.svelte'
|
||||
import TaskTypeIcon from '../taskTypes/TaskTypeIcon.svelte'
|
||||
import TaskTypeKindEditor from '../taskTypes/TaskTypeKindEditor.svelte'
|
||||
import TypeClassEditor from '../taskTypes/TypeClassEditor.svelte'
|
||||
import IconLayers from '../icons/Layers.svelte'
|
||||
|
||||
export let type: ProjectType
|
||||
export let descriptor: ProjectTypeDescriptor | undefined
|
||||
@ -143,8 +154,23 @@
|
||||
}
|
||||
$: items =
|
||||
selectedTaskType !== undefined
|
||||
? [{ label: plugin.string.ProjectType }, { title: selectedTaskType.name }]
|
||||
: [{ label: plugin.string.ProjectType }]
|
||||
? [
|
||||
{ label: plugin.string.ProjectType, icon: descriptor?.icon },
|
||||
{ title: selectedTaskType.name, icon: selectedTaskType.icon }
|
||||
]
|
||||
: [{ label: plugin.string.ProjectType, icon: descriptor?.icon }]
|
||||
|
||||
const navigator: {
|
||||
id: string
|
||||
label: IntlString
|
||||
}[] = [
|
||||
{ id: 'properties', label: setting.string.Properties },
|
||||
{ id: 'tasktypes', label: setting.string.TaskTypes },
|
||||
{ id: 'automations', label: setting.string.Automations },
|
||||
{ id: 'collections', label: setting.string.Collections }
|
||||
]
|
||||
|
||||
defineSeparators('typeSettings', secondNavSeparators)
|
||||
</script>
|
||||
|
||||
{#if type !== undefined && descriptor !== undefined}
|
||||
@ -180,26 +206,56 @@
|
||||
on:select={(event) => {
|
||||
if (event.detail === 0) selectTaskType(undefined)
|
||||
}}
|
||||
/>
|
||||
>
|
||||
<!-- afterLabel={plugin.string.Published} -->
|
||||
<!-- <span slot="afterLabel">{dateStr}</span> -->
|
||||
</Breadcrumbs>
|
||||
<svelte:fragment slot="actions">
|
||||
<!-- <div class="hulyHeader-buttonsGroup__label font-regular-12">
|
||||
<Label label={plugin.string.LastSave} />
|
||||
<span>{dateStr}</span>
|
||||
</div> -->
|
||||
<ModernButton kind={'secondary'} label={ui.string.SaveDraft} size={'small'} />
|
||||
<ModernButton kind={'primary'} icon={IconSend} label={ui.string.Publish} size={'small'} />
|
||||
</svelte:fragment>
|
||||
</Header>
|
||||
<div class="hulyComponent-content__column content">
|
||||
<div class="hulyComponent-content">
|
||||
{#if selectedTaskType === undefined}
|
||||
<!-- <div class="flex-col">Navigation</div> -->
|
||||
<div class="flex-grow h-full">
|
||||
<div class="p-4 flex-col">
|
||||
<div>
|
||||
<div class="flex-row-center flex-between mb-2">
|
||||
<div>
|
||||
<Component is={descriptor.icon} props={{ size: 'large' }} />
|
||||
</div>
|
||||
<div class="hulyComponent-content__container columns">
|
||||
{#if selectedTaskTypeId === undefined}
|
||||
<div class="hulyComponent-content__column">
|
||||
<div class="hulyComponent-content__navHeader">
|
||||
<div class="hulyComponent-content__navHeader-menu">
|
||||
<ButtonIcon kind={'tertiary'} icon={IconDescription} size={'small'} inheritColor />
|
||||
</div>
|
||||
</div>
|
||||
{#each navigator as navItem (navItem.id)}
|
||||
<NavItem type={'type-anchor-link'} label={navItem.label} />
|
||||
{/each}
|
||||
</div>
|
||||
<Separator name={'typeSettings'} index={0} color={'transparent'} />
|
||||
{/if}
|
||||
<div class="hulyComponent-content__column content">
|
||||
<Scroller align={'center'} padding={'var(--spacing-3)'} bottomPadding={'var(--spacing-3)'}>
|
||||
<div class="hulyComponent-content gap">
|
||||
{#if selectedTaskType === undefined}
|
||||
<div class="hulyComponent-content__column-group">
|
||||
<div class="hulyComponent-content__header">
|
||||
<ButtonIcon icon={descriptor.icon} size={'large'} kind={'secondary'} />
|
||||
<div class="flex-row-center no-word-wrap">
|
||||
<Icon icon={IconFile} size={'small'} />
|
||||
{projects.length} projects
|
||||
<ModernButton
|
||||
icon={IconSquareExpand}
|
||||
label={plugin.string.CountProjects}
|
||||
labelParams={{ count: projects.length }}
|
||||
disabled={projects.length === 0}
|
||||
kind={'tertiary'}
|
||||
size={'medium'}
|
||||
hasMenu
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<EditBox
|
||||
kind={'large-style'}
|
||||
<ModernEditbox
|
||||
kind={'ghost'}
|
||||
size={'large'}
|
||||
label={plugin.string.ProjectTypeTitle}
|
||||
value={type?.name ?? ''}
|
||||
on:blur={(evt) => {
|
||||
if (type !== undefined) {
|
||||
@ -207,111 +263,75 @@
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<div class="p-2">
|
||||
<EditBox
|
||||
placeholder={getEmbeddedLabel('Description')}
|
||||
kind={'small-style'}
|
||||
bind:value={type.shortDescription}
|
||||
on:change={() => onShortDescriptionChange(type?.shortDescription ?? '')}
|
||||
/>
|
||||
</div>
|
||||
<TextArea
|
||||
placeholder={getEmbeddedLabel('Description')}
|
||||
width={'100%'}
|
||||
height={'4.5rem'}
|
||||
margin={'var(--spacing-1) var(--spacing-2)'}
|
||||
noFocusBorder
|
||||
bind:value={type.shortDescription}
|
||||
on:change={() => onShortDescriptionChange(type?.shortDescription ?? '')}
|
||||
/>
|
||||
{#if descriptor?.editor}
|
||||
<Component is={descriptor.editor} props={{ type }} />
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="panelBox flex-col row">
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<div class="fs-title flex flex-between bottom-divider">
|
||||
<div class="trans-title">
|
||||
<Label label={getEmbeddedLabel('Task types')} />
|
||||
</div>
|
||||
<div class="p-1">
|
||||
<Button
|
||||
icon={IconAdd}
|
||||
kind={'primary'}
|
||||
size={'small'}
|
||||
on:click={(event) => {
|
||||
showPopup(CreateTaskType, { type, descriptor }, 'top')
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-1">
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
{#each taskTypes as taskType}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
class="flex-grow p-2 antiButton sh-round flex-row-center"
|
||||
class:regular={taskType._id === selectedTaskTypeId}
|
||||
class:ghost={taskType._id !== selectedTaskTypeId}
|
||||
on:click|stopPropagation={() => {
|
||||
selectTaskType(taskType._id)
|
||||
}}
|
||||
>
|
||||
<div class="p-2">
|
||||
<TaskTypeIcon value={taskType} size={'small'} />
|
||||
</div>
|
||||
<div class="fs-title">
|
||||
{taskType.name}
|
||||
</div>
|
||||
<div class="ml-2 text-sm">
|
||||
<TaskTypeKindEditor readonly kind={taskType.kind} buttonKind={'link'} />
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
<ClassAttributes ofClass={descriptor.baseClass} _class={type.targetClass} showHierarchy />
|
||||
|
||||
<div class="hulyTableAttr-container">
|
||||
<div class="hulyTableAttr-header font-medium-12">
|
||||
<IconLayers size={'small'} />
|
||||
<span><Label label={setting.string.TaskTypes} /></span>
|
||||
<ButtonIcon
|
||||
kind={'primary'}
|
||||
icon={IconAdd}
|
||||
size={'small'}
|
||||
on:click={(ev) => {
|
||||
showPopup(CreateTaskType, { type, descriptor }, 'top')
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{#if taskTypes.length}
|
||||
<div class="hulyTableAttr-content task">
|
||||
{#each taskTypes as taskType}
|
||||
<button
|
||||
class="hulyTableAttr-content__row"
|
||||
on:click|stopPropagation={() => {
|
||||
selectTaskType(taskType._id)
|
||||
}}
|
||||
>
|
||||
<div class="hulyTableAttr-content__row-icon-wrapper">
|
||||
<TaskTypeIcon value={taskType} size={'small'} />
|
||||
</div>
|
||||
{#if taskType.name}
|
||||
<div class="hulyTableAttr-content__row-label font-medium-14">
|
||||
{taskType.name}
|
||||
</div>
|
||||
{/if}
|
||||
<div class="hulyTableAttr-content__row-label grow dark font-regular-14">
|
||||
<TaskTypeKindEditor readonly kind={taskType.kind} buttonKind={'link'} />
|
||||
</div>
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<ComponentExtensions extension={task.extensions.ProjectEditorExtension} props={{ type }} />
|
||||
|
||||
<div class="panelBox flex-col row">
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<div class="fs-title flex flex-between bottom-divider">
|
||||
<div class="trans-title">
|
||||
<Label label={getEmbeddedLabel('Collections')} />
|
||||
</div>
|
||||
<div class="p-1">
|
||||
<Button icon={IconAdd} kind={'primary'} size={'small'} on:click={(event) => {}} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-1">
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<div class="hulyTableAttr-container">
|
||||
<div class="hulyTableAttr-header font-medium-12">
|
||||
<IconFolder size={'small'} />
|
||||
<span><Label label={setting.string.Collections} /></span>
|
||||
<ButtonIcon kind={'primary'} icon={IconAdd} size={'small'} on:click={() => {}} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panelBox flex-col row">
|
||||
<div class="mt-1">
|
||||
<TypeClassEditor ofClass={descriptor.baseClass} _class={type.targetClass} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<TaskTypeEditor taskType={selectedTaskType} projectType={type} {taskTypes} {taskTypeCounter} />
|
||||
{/if}
|
||||
</div>
|
||||
{:else}
|
||||
<TaskTypeEditor taskType={selectedTaskType} projectType={type} {taskTypes} {taskTypeCounter} />
|
||||
{/if}
|
||||
</Scroller>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
.row {
|
||||
// border-bottom: 1px solid var(--theme-list-border-color);
|
||||
// border-top: 1px solid var(--theme-list-border-color);
|
||||
width: 100%;
|
||||
}
|
||||
.panelBox {
|
||||
margin-top: 1rem;
|
||||
padding: 0.5rem;
|
||||
// TODO: Need to update it
|
||||
border: 1px solid var(--theme-button-border);
|
||||
background: var(--theme-button-default);
|
||||
border-radius: 8px;
|
||||
}
|
||||
.editorBox {
|
||||
max-width: 1024px;
|
||||
min-width: 768px;
|
||||
}
|
||||
</style>
|
||||
|
@ -16,7 +16,7 @@
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import task, { type ProjectType } from '@hcengineering/task'
|
||||
import { Component } from '@hcengineering/ui'
|
||||
import { Icon } from '@hcengineering/ui'
|
||||
import { typeStore } from '../..'
|
||||
|
||||
export let value: ProjectType | Ref<ProjectType> | undefined
|
||||
@ -29,7 +29,7 @@
|
||||
{#if _value !== undefined}
|
||||
<span class="label flex-row-center gap-1 ml-3 no-word-wrap">
|
||||
{#if descriptor?.icon}
|
||||
<Component is={descriptor?.icon} props={{ size: 'small' }} />
|
||||
<Icon icon={descriptor?.icon} size={'small'} />
|
||||
{/if}
|
||||
<span class:ml-1={descriptor?.icon !== undefined}>
|
||||
{_value.name}
|
||||
|
@ -16,7 +16,7 @@
|
||||
<script lang="ts">
|
||||
import { Ref, WithLookup } from '@hcengineering/core'
|
||||
import { ProjectType } from '@hcengineering/task'
|
||||
import { Component, Label, IconOpenedArrow } from '@hcengineering/ui'
|
||||
import { Icon, Label, IconOpenedArrow } from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
|
||||
export let type: ProjectType | undefined
|
||||
@ -42,7 +42,7 @@
|
||||
<div class="hulyTaskNavLink-avatar">
|
||||
{#if typeItem.$lookup?.descriptor?.icon}
|
||||
<div class="hulyTaskNavLink-icon">
|
||||
<Component is={typeItem.$lookup?.descriptor?.icon} props={{ size: 'small', fill: 'currentColor' }} />
|
||||
<Icon icon={typeItem.$lookup?.descriptor?.icon} size={'small'} fill={'currentColor'} />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
@ -16,23 +16,20 @@
|
||||
<script lang="ts">
|
||||
import { AttributeEditor, getClient } from '@hcengineering/presentation'
|
||||
import task, { ProjectType, TaskType, calculateStatuses } from '@hcengineering/task'
|
||||
|
||||
import { Ref, Status } from '@hcengineering/core'
|
||||
import { Asset, getEmbeddedLabel } from '@hcengineering/platform'
|
||||
import { Label, showPopup } from '@hcengineering/ui'
|
||||
import { IconPicker, statusStore } from '@hcengineering/view-resources'
|
||||
import { ClassAttributes } from '@hcengineering/setting-resources'
|
||||
import { taskTypeStore } from '../..'
|
||||
import StatesProjectEditor from '../state/StatesProjectEditor.svelte'
|
||||
import TaskTypeKindEditor from '../taskTypes/TaskTypeKindEditor.svelte'
|
||||
import TaskTypeRefEditor from '../taskTypes/TaskTypeRefEditor.svelte'
|
||||
import TaskTypeIcon from './TaskTypeIcon.svelte'
|
||||
import TypeClassEditor from './TypeClassEditor.svelte'
|
||||
|
||||
export let projectType: ProjectType
|
||||
export let taskType: TaskType
|
||||
|
||||
export let taskTypeCounter: Map<Ref<TaskType>, number>
|
||||
|
||||
export let taskTypes: TaskType[]
|
||||
|
||||
const client = getClient()
|
||||
@ -56,101 +53,95 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="p-4 flex-col">
|
||||
<div class="flex-col">
|
||||
<div class="flex-row-center flex-between">
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div class="mb-1" on:click={selectIcon}>
|
||||
<TaskTypeIcon value={taskType} size={'large'} />
|
||||
</div>
|
||||
<div class="ml-2 flex-row-center">
|
||||
{taskTypeCounter.get(taskType._id) ?? 0}
|
||||
</div>
|
||||
<div class="flex-col">
|
||||
<div class="flex-row-center flex-between">
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div class="mb-1" on:click={selectIcon}>
|
||||
<TaskTypeIcon value={taskType} size={'large'} />
|
||||
</div>
|
||||
<div class="flex-row-center">
|
||||
<div class="fs-title mb-4">
|
||||
<AttributeEditor
|
||||
maxWidth={'10rem'}
|
||||
_class={task.class.TaskType}
|
||||
object={taskType}
|
||||
key="name"
|
||||
editKind={'large-style'}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<TaskTypeKindEditor
|
||||
kind={taskType.kind}
|
||||
on:change={(evt) => {
|
||||
void client.diffUpdate(taskType, { kind: evt.detail })
|
||||
}}
|
||||
/>
|
||||
|
||||
<div class="flex-row-center p-2 mt-4">
|
||||
<div class="flex-no-shrink trans-title uppercase">
|
||||
<Label label={getEmbeddedLabel('Parent type restrictions')} />
|
||||
</div>
|
||||
{#if taskType.kind === 'subtask' || taskType.kind === 'both'}
|
||||
<TaskTypeRefEditor
|
||||
label={getEmbeddedLabel('Allowed parents')}
|
||||
value={taskType.allowedAsChildOf}
|
||||
types={taskTypes.filter((it) => it.kind === 'task' || it.kind === 'both')}
|
||||
onChange={(evt) => {
|
||||
void client.diffUpdate(taskType, { allowedAsChildOf: evt })
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
<div class="ml-2 flex-row-center">
|
||||
{taskTypeCounter.get(taskType._id) ?? 0}
|
||||
</div>
|
||||
</div>
|
||||
<div class="panelBox">
|
||||
<div class="p-2 trans-title">
|
||||
<Label label={getEmbeddedLabel('Process')} />
|
||||
</div>
|
||||
<div class="ml-2">
|
||||
<StatesProjectEditor
|
||||
{taskType}
|
||||
type={projectType}
|
||||
{states}
|
||||
on:delete={async (evt) => {
|
||||
const index = taskType.statuses.findIndex((p) => p === evt.detail.state._id)
|
||||
taskType.statuses.splice(index, 1)
|
||||
await client.update(taskType, {
|
||||
statuses: taskType.statuses
|
||||
})
|
||||
await client.update(projectType, {
|
||||
statuses: calculateStatuses(projectType, $taskTypeStore, [
|
||||
{ taskTypeId: taskType._id, statuses: taskType.statuses }
|
||||
])
|
||||
})
|
||||
}}
|
||||
on:move={async (evt) => {
|
||||
const index = taskType.statuses.findIndex((p) => p === evt.detail.stateID)
|
||||
const state = taskType.statuses.splice(index, 1)[0]
|
||||
|
||||
const statuses = [
|
||||
...taskType.statuses.slice(0, evt.detail.position),
|
||||
state,
|
||||
...taskType.statuses.slice(evt.detail.position)
|
||||
]
|
||||
await client.update(taskType, {
|
||||
statuses
|
||||
})
|
||||
|
||||
await client.update(projectType, {
|
||||
statuses: calculateStatuses(projectType, $taskTypeStore, [{ taskTypeId: taskType._id, statuses }])
|
||||
})
|
||||
}}
|
||||
<div class="flex-row-center">
|
||||
<div class="fs-title mb-4">
|
||||
<AttributeEditor
|
||||
maxWidth={'10rem'}
|
||||
_class={task.class.TaskType}
|
||||
object={taskType}
|
||||
key="name"
|
||||
editKind={'large-style'}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panelBox flex flex-col">
|
||||
<div class="ml-2">
|
||||
<TypeClassEditor ofClass={taskType.ofClass} _class={taskType.targetClass} />
|
||||
<TaskTypeKindEditor
|
||||
kind={taskType.kind}
|
||||
on:change={(evt) => {
|
||||
void client.diffUpdate(taskType, { kind: evt.detail })
|
||||
}}
|
||||
/>
|
||||
|
||||
<div class="flex-row-center p-2 mt-4">
|
||||
<div class="flex-no-shrink trans-title uppercase">
|
||||
<Label label={getEmbeddedLabel('Parent type restrictions')} />
|
||||
</div>
|
||||
{#if taskType.kind === 'subtask' || taskType.kind === 'both'}
|
||||
<TaskTypeRefEditor
|
||||
label={getEmbeddedLabel('Allowed parents')}
|
||||
value={taskType.allowedAsChildOf}
|
||||
types={taskTypes.filter((it) => it.kind === 'task' || it.kind === 'both')}
|
||||
onChange={(evt) => {
|
||||
void client.diffUpdate(taskType, { allowedAsChildOf: evt })
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<div class="panelBox">
|
||||
<div class="p-2 trans-title">
|
||||
<Label label={getEmbeddedLabel('Process')} />
|
||||
</div>
|
||||
<div class="ml-2">
|
||||
<StatesProjectEditor
|
||||
{taskType}
|
||||
type={projectType}
|
||||
{states}
|
||||
on:delete={async (evt) => {
|
||||
const index = taskType.statuses.findIndex((p) => p === evt.detail.state._id)
|
||||
taskType.statuses.splice(index, 1)
|
||||
await client.update(taskType, {
|
||||
statuses: taskType.statuses
|
||||
})
|
||||
await client.update(projectType, {
|
||||
statuses: calculateStatuses(projectType, $taskTypeStore, [
|
||||
{ taskTypeId: taskType._id, statuses: taskType.statuses }
|
||||
])
|
||||
})
|
||||
}}
|
||||
on:move={async (evt) => {
|
||||
const index = taskType.statuses.findIndex((p) => p === evt.detail.stateID)
|
||||
const state = taskType.statuses.splice(index, 1)[0]
|
||||
|
||||
const statuses = [
|
||||
...taskType.statuses.slice(0, evt.detail.position),
|
||||
state,
|
||||
...taskType.statuses.slice(evt.detail.position)
|
||||
]
|
||||
await client.update(taskType, {
|
||||
statuses
|
||||
})
|
||||
|
||||
await client.update(projectType, {
|
||||
statuses: calculateStatuses(projectType, $taskTypeStore, [{ taskTypeId: taskType._id, statuses }])
|
||||
})
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ClassAttributes ofClass={taskType.ofClass} _class={taskType.targetClass} showHierarchy />
|
||||
|
||||
<style lang="scss">
|
||||
.row {
|
||||
|
@ -1,82 +0,0 @@
|
||||
<!--
|
||||
// Copyright © 2022 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import core, { Class, Doc, Obj, Ref } from '@hcengineering/core'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { ClassAttributesList } from '@hcengineering/setting-resources'
|
||||
import { Button, Icon, IconAdd } from '@hcengineering/ui'
|
||||
import { ObjectPresenter } from '@hcengineering/view-resources'
|
||||
|
||||
export let ofClass: Ref<Class<Obj>>
|
||||
export let _class: Ref<Class<Obj>>
|
||||
|
||||
const client = getClient()
|
||||
|
||||
let classes: Class<Doc>[] = []
|
||||
|
||||
$: classes = client
|
||||
.getHierarchy()
|
||||
.getAncestors(_class)
|
||||
.map((it) => client.getHierarchy().getClass(it))
|
||||
.filter((it) => {
|
||||
return (
|
||||
!it.hidden &&
|
||||
it.label !== undefined &&
|
||||
it._id !== core.class.Doc &&
|
||||
it._id !== core.class.AttachedDoc &&
|
||||
it._id !== _class
|
||||
)
|
||||
})
|
||||
$: clazz = client.getHierarchy().getClass(_class)
|
||||
|
||||
let mainAttributes: ClassAttributesList
|
||||
</script>
|
||||
|
||||
<div class="flex flex-between mb-4">
|
||||
<div class="antiButton regular accent medium sh-round p-2 flex-row-center">
|
||||
{#if clazz?.icon}
|
||||
<div class="mr-2 flex">
|
||||
<Icon icon={clazz.icon} size={'medium'} />
|
||||
</div>
|
||||
{/if}
|
||||
{#if clazz}
|
||||
<ObjectPresenter _class={clazz._class} objectId={clazz._id} value={clazz} />
|
||||
{/if}
|
||||
</div>
|
||||
<Button icon={IconAdd} size={'small'} kind={'primary'} on:click={(ev) => mainAttributes?.createAttribute(ev)} />
|
||||
</div>
|
||||
<div class="ml-2 mr-2">
|
||||
<table class="antiTable mx-2">
|
||||
<tbody>
|
||||
<ClassAttributesList
|
||||
bind:this={mainAttributes}
|
||||
{_class}
|
||||
{ofClass}
|
||||
useOfClassAttributes={false}
|
||||
showTitle={false}
|
||||
showCreate={false}
|
||||
/>
|
||||
{#each classes as clazz2}
|
||||
<ClassAttributesList
|
||||
_class={clazz2._id}
|
||||
{ofClass}
|
||||
useOfClassAttributes={false}
|
||||
showTitle={false}
|
||||
showCreate={false}
|
||||
/>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
@ -77,7 +77,11 @@ export default mergeIds(taskId, task, {
|
||||
StatusChange: '' as IntlString,
|
||||
TaskCreated: '' as IntlString,
|
||||
CreateProjectType: '' as IntlString,
|
||||
ClassicProject: '' as IntlString
|
||||
ClassicProject: '' as IntlString,
|
||||
LastSave: '' as IntlString,
|
||||
Published: '' as IntlString,
|
||||
CountProjects: '' as IntlString,
|
||||
ProjectTypeTitle: '' as IntlString
|
||||
},
|
||||
status: {
|
||||
AssigneeRequired: '' as IntlString
|
||||
|
@ -185,7 +185,7 @@ export interface ProjectType extends Space {
|
||||
export interface ProjectTypeDescriptor extends Doc {
|
||||
name: IntlString
|
||||
description: IntlString
|
||||
icon: AnyComponent
|
||||
icon: Asset
|
||||
editor?: AnyComponent
|
||||
baseClass: Ref<Class<Task>>
|
||||
}
|
||||
|
@ -18,7 +18,8 @@
|
||||
Breadcrumb,
|
||||
Separator,
|
||||
defineSeparators,
|
||||
settingsSeparators
|
||||
settingsSeparators,
|
||||
Scroller
|
||||
} from '@hcengineering/ui'
|
||||
import { getActions as getContributedActions, TreeItem, TreeNode } from '@hcengineering/view-resources'
|
||||
import templatesPlugin from '../plugin'
|
||||
@ -195,79 +196,81 @@
|
||||
</div>
|
||||
<Separator name={'workspaceSettings'} index={0} color={'var(--theme-divider-color)'} />
|
||||
<div class="hulyComponent-content__column content">
|
||||
<div class="hulyComponent-content">
|
||||
{#if newTemplate}
|
||||
<div class="flex-between mr-4">
|
||||
<span class="trans-title mb-3">
|
||||
<Scroller align={'center'} padding={'var(--spacing-3)'} bottomPadding={'var(--spacing-3)'}>
|
||||
<div class="hulyComponent-content">
|
||||
{#if newTemplate}
|
||||
<div class="flex-between mr-4">
|
||||
<span class="trans-title mb-3">
|
||||
{#if mode === Mode.Create}
|
||||
<Label label={templatesPlugin.string.CreateTemplate} />
|
||||
{:else if mode === Mode.Edit}
|
||||
<Label label={templatesPlugin.string.EditTemplate} />
|
||||
{:else}
|
||||
<Label label={templatesPlugin.string.ViewTemplate} />
|
||||
{/if}
|
||||
</span>
|
||||
{#if mode === Mode.Create}
|
||||
<Label label={templatesPlugin.string.CreateTemplate} />
|
||||
{:else if mode === Mode.Edit}
|
||||
<Label label={templatesPlugin.string.EditTemplate} />
|
||||
{:else}
|
||||
<Label label={templatesPlugin.string.ViewTemplate} />
|
||||
<SpaceSelector
|
||||
_class={templatesPlugin.class.TemplateCategory}
|
||||
label={templatesPlugin.string.TemplateCategory}
|
||||
bind:space
|
||||
create={{
|
||||
component: templatesPlugin.component.CreateTemplateCategory,
|
||||
label: templatesPlugin.string.CreateTemplateCategory
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
</span>
|
||||
{#if mode === Mode.Create}
|
||||
<SpaceSelector
|
||||
_class={templatesPlugin.class.TemplateCategory}
|
||||
label={templatesPlugin.string.TemplateCategory}
|
||||
bind:space
|
||||
create={{
|
||||
component: templatesPlugin.component.CreateTemplateCategory,
|
||||
label: templatesPlugin.string.CreateTemplateCategory
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="text-lg caption-color">
|
||||
</div>
|
||||
<div class="text-lg caption-color">
|
||||
{#if mode !== Mode.View}
|
||||
<EditBox bind:value={newTemplate.title} placeholder={templatesPlugin.string.TemplatePlaceholder} />
|
||||
{:else}
|
||||
{newTemplate.title}
|
||||
{/if}
|
||||
</div>
|
||||
<div class="separator" />
|
||||
{#if mode !== Mode.View}
|
||||
<EditBox bind:value={newTemplate.title} placeholder={templatesPlugin.string.TemplatePlaceholder} />
|
||||
<StyledTextEditor bind:content={newTemplate.message} bind:this={textEditor} on:value={updateTemplate}>
|
||||
<div class="flex flex-reverse flex-grow">
|
||||
<div class="ml-2">
|
||||
<Button
|
||||
disabled={newTemplate.title.trim().length === 0}
|
||||
kind={'primary'}
|
||||
label={templatesPlugin.string.SaveTemplate}
|
||||
on:click={saveNewTemplate}
|
||||
/>
|
||||
</div>
|
||||
<div class="ml-2">
|
||||
<Button
|
||||
label={templatesPlugin.string.Cancel}
|
||||
on:click={() => {
|
||||
if (mode === Mode.Create) {
|
||||
newTemplate = undefined
|
||||
}
|
||||
mode = Mode.View
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<Button label={templatesPlugin.string.Field} on:click={addField} />
|
||||
</div>
|
||||
</StyledTextEditor>
|
||||
{:else}
|
||||
{newTemplate.title}
|
||||
{/if}
|
||||
</div>
|
||||
<div class="separator" />
|
||||
{#if mode !== Mode.View}
|
||||
<StyledTextEditor bind:content={newTemplate.message} bind:this={textEditor} on:value={updateTemplate}>
|
||||
<div class="flex flex-reverse flex-grow">
|
||||
<div class="ml-2">
|
||||
<Button
|
||||
disabled={newTemplate.title.trim().length === 0}
|
||||
kind={'primary'}
|
||||
label={templatesPlugin.string.SaveTemplate}
|
||||
on:click={saveNewTemplate}
|
||||
/>
|
||||
</div>
|
||||
<div class="ml-2">
|
||||
<Button
|
||||
label={templatesPlugin.string.Cancel}
|
||||
on:click={() => {
|
||||
if (mode === Mode.Create) {
|
||||
newTemplate = undefined
|
||||
}
|
||||
mode = Mode.View
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<Button label={templatesPlugin.string.Field} on:click={addField} />
|
||||
<div class="text">
|
||||
<MessageViewer message={newTemplate.message} />
|
||||
</div>
|
||||
</StyledTextEditor>
|
||||
{:else}
|
||||
<div class="text">
|
||||
<MessageViewer message={newTemplate.message} />
|
||||
</div>
|
||||
<div class="flex flex-reverse">
|
||||
<Button
|
||||
kind={'primary'}
|
||||
label={templatesPlugin.string.EditTemplate}
|
||||
on:click={() => {
|
||||
mode = Mode.Edit
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-reverse">
|
||||
<Button
|
||||
kind={'primary'}
|
||||
label={templatesPlugin.string.EditTemplate}
|
||||
on:click={() => {
|
||||
mode = Mode.Edit
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</Scroller>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue
Block a user