mirror of
https://github.com/hcengineering/platform.git
synced 2024-12-22 02:51:54 +03:00
Update Settings layout (#4277)
Signed-off-by: Alexander Platov <sas_lord@mail.ru>
This commit is contained in:
parent
09579fd2b0
commit
f4d6faf4f8
@ -565,7 +565,7 @@ export function createModel (builder: Builder): void {
|
||||
name: 'relations',
|
||||
label: tracker.string.RelatedIssues,
|
||||
icon: tracker.icon.Relations,
|
||||
component: tracker.component.EditRelatedTargets,
|
||||
component: tracker.component.SettingsRelatedTargets,
|
||||
group: 'settings-editor',
|
||||
secured: false,
|
||||
order: 4000
|
||||
|
@ -59,6 +59,7 @@ export default mergeIds(trackerId, tracker, {
|
||||
MilestoneFilter: '' as AnyComponent,
|
||||
EditRelatedTargets: '' as AnyComponent,
|
||||
EditRelatedTargetsPopup: '' as AnyComponent,
|
||||
SettingsRelatedTargets: '' as AnyComponent,
|
||||
IssueSearchIcon: '' as AnyComponent,
|
||||
MembersArrayEditor: '' as AnyComponent
|
||||
},
|
||||
|
@ -83,6 +83,20 @@
|
||||
|
||||
// New
|
||||
--global-accent-IconColor: #6796FF;
|
||||
|
||||
--button-accent-LabelColor: #fff;
|
||||
--button-disabled-LabelColor: #8b97ad;
|
||||
--button-accent-IconColor: #fff;
|
||||
--button-disabled-IconColor: #8b97ad;
|
||||
--button-primary-BackgroundColor: #3364e2;
|
||||
--button-primary-BorderColor: #d1d5de1a;
|
||||
--button-primary-hover-BackgroundColor: #6191fe;
|
||||
--button-primary-active-BackgroundColor: #2553cf;
|
||||
--button-primary-loading-LabelColor: #6191fe;
|
||||
--button-negative-loading-LabelColor: #ff9187;
|
||||
--button-negative-BorderColor: #d1d5de26;
|
||||
--button-negative-hover-BackgroundColor: #e34748;
|
||||
--button-negative-active-BackgroundColor: #c42a32;
|
||||
}
|
||||
|
||||
/* Dark Theme */
|
||||
@ -317,12 +331,42 @@
|
||||
--global-ui-hover-BackgroundColor: #A5BDFF1A;
|
||||
--global-ui-highlight-BackgroundColor: #A5BDFF0D;
|
||||
--global-ui-hover-highlight-BackgroundColor: #A5BDFF26;
|
||||
--global-surface-01-BackgroundColor: #131925;
|
||||
--global-surface-01-BorderColor: #1F2737;
|
||||
--global-surface-02-BackgroundColor: #19202E;
|
||||
--global-surface-02-BorderColor: #262F40;
|
||||
--global-surface-03-hover-BackgroundColor: #19202E;
|
||||
--global-primary-LinkColor: #4D7FF5;
|
||||
--global-primary-TextColor: #FFFFFF;
|
||||
--global-secondary-TextColor: #C1C9D6;
|
||||
--global-tertiary-TextColor: #8E99AF;
|
||||
--global-accent-TextColor: #4D7FF5;
|
||||
/** Buttons **/
|
||||
--button-subtle-LabelColor: #fff;
|
||||
--button-subtle-IconColor: #fff;
|
||||
--button-disabled-BackgroundColor: #d1d5de0d;
|
||||
--button-primary-loading-LabelColor: #6191fe;
|
||||
--button-secondary-BackgroundColor: #d1d5de0d;
|
||||
--button-secondary-BorderColor: #d1d5de1a;
|
||||
--button-secondary-hover-BackgroundColor: #d1d5de1a;
|
||||
--button-secondary-active-BackgroundColor: #d1d5de26;
|
||||
--button-negative-BackgroundColor: #e34748;
|
||||
--button-tertiary-hover-BackgroundColor: #d1d5de1a;
|
||||
--button-tertiary-active-BackgroundColor: #d1d5de26;
|
||||
--button-menu-active-BorderColor: #d9dee6;
|
||||
/** Editbox **/
|
||||
--input-BackgroundColor: #a5bdff0d;
|
||||
--input-hover-BackgroundColor: #a5bdff1a;
|
||||
--input-BorderColor: #a5bdff0d;
|
||||
--input-TextColor: #ffffff;
|
||||
--input-LabelColor: #ffffff;
|
||||
--input-filled-LabelColor: #8b97ad;
|
||||
--input-PlaceholderColor: #8b97ad;
|
||||
--input-hover-PlaceholderColor: #ffffff;
|
||||
--input-focus-PlaceholderColor: #556178;
|
||||
--input-HelperColor: #8b97ad;
|
||||
--input-error-BorderColor: #fb6863;
|
||||
--input-search-IconColor: #ffffff;
|
||||
}
|
||||
|
||||
/* Light Theme */
|
||||
@ -557,10 +601,40 @@
|
||||
--global-ui-hover-BackgroundColor: #1530721A;
|
||||
--global-ui-highlight-BackgroundColor: #A5BDFF26;
|
||||
--global-ui-hover-highlight-BackgroundColor: #A5BDFF40;
|
||||
--global-surface-01-BackgroundColor: #F8F9FA;
|
||||
--global-surface-01-BorderColor: #DDE1E9;
|
||||
--global-surface-02-BackgroundColor: #19202E;
|
||||
--global-surface-02-BorderColor: #EBEEF2;
|
||||
--global-surface-03-hover-BackgroundColor: #F8F9FA;
|
||||
--global-primary-LinkColor: #3566E2;
|
||||
--global-primary-TextColor: #0F121A;
|
||||
--global-secondary-TextColor: #5A667E;
|
||||
--global-tertiary-TextColor: #7B879E;
|
||||
--global-accent-TextColor: #3566E2;
|
||||
/** Buttons **/
|
||||
--button-subtle-LabelColor: #000;
|
||||
--button-subtle-IconColor: #000;
|
||||
--button-disabled-BackgroundColor: #1725470d;
|
||||
--button-primary-loading-LabelColor: #95baff;
|
||||
--button-secondary-BackgroundColor: #1725470d;
|
||||
--button-secondary-BorderColor: #1725471a;
|
||||
--button-secondary-hover-BackgroundColor: #1725471a;
|
||||
--button-secondary-active-BackgroundColor: #17254726;
|
||||
--button-negative-BackgroundColor: #ea4c4c;
|
||||
--button-tertiary-hover-BackgroundColor: #1725471a;
|
||||
--button-tertiary-active-BackgroundColor: #17254726;
|
||||
--button-menu-active-BorderColor: #0f121a;
|
||||
/** Editbox **/
|
||||
--input-BackgroundColor: #1530720d;
|
||||
--input-hover-BackgroundColor: #1530721a;
|
||||
--input-BorderColor: #1530720d;
|
||||
--input-TextColor: #0f121a;
|
||||
--input-LabelColor: #0f121a;
|
||||
--input-filled-LabelColor: #556178;
|
||||
--input-PlaceholderColor: #556178;
|
||||
--input-hover-PlaceholderColor: #0f121a;
|
||||
--input-focus-PlaceholderColor: #8b97ad;
|
||||
--input-HelperColor: #556178;
|
||||
--input-error-BorderColor: #e34748;
|
||||
--input-search-IconColor: #0f121a;
|
||||
}
|
||||
|
@ -574,6 +574,7 @@ input.search {
|
||||
.py-0-5 { padding: 0.125rem 0; }
|
||||
.py-1 { padding: 0.25rem 0; }
|
||||
.py-2 { padding: 0.5rem 0; }
|
||||
.py-3 { padding: 0.75rem 0; }
|
||||
.py-4 { padding: 1rem 0; }
|
||||
.py-8 { padding: 2rem 0; }
|
||||
.py-10 { padding: 2.5rem 0; }
|
||||
|
59
packages/theme/styles/_vars.scss
Normal file
59
packages/theme/styles/_vars.scss
Normal file
@ -0,0 +1,59 @@
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
* {
|
||||
/** Space & Dimensions **/
|
||||
|
||||
--spacing-0_25: 0.125rem;
|
||||
--spacing-0_5: 0.25rem;
|
||||
--spacing-0_75: 0.375rem;
|
||||
--spacing-1: 0.5rem;
|
||||
--spacing-1_5: 0.75rem;
|
||||
--spacing-1_75: 0.875rem;
|
||||
--spacing-2: 1rem;
|
||||
--spacing-2_5: 1.25rem;
|
||||
--spacing-3: 1.5rem;
|
||||
--spacing-3_5: 1.75rem;
|
||||
--spacing-4: 2rem;
|
||||
--spacing-4_5: 2.25rem;
|
||||
--spacing-5: 2.5rem;
|
||||
--spacing-6: 3rem;
|
||||
--spacing-6_5: 3.5rem;
|
||||
--spacing-7: 4rem;
|
||||
--spacing-8: 5rem;
|
||||
--spacing-9: 6rem;
|
||||
--spacing-10: 7.5rem;
|
||||
|
||||
/** UI Elements Size **/
|
||||
|
||||
--global-min-Size: 1rem;
|
||||
--global-extra-small-Size: 1.5rem;
|
||||
--global-small-Size: 2rem;
|
||||
--global-medium-Size: 2.5rem;
|
||||
--global-large-Size: 3rem;
|
||||
--global-extra-large-Size: 3.5rem;
|
||||
--global-max-Size: 4rem;
|
||||
|
||||
/** Border Radius **/
|
||||
|
||||
--extra-small-BorderRadius: 0.25rem;
|
||||
--extra-small-focus-BorderRadius: 0.375rem;
|
||||
--small-BorderRadius: 0.375rem;
|
||||
--small-focus-BorderRadius: 0.5rem;
|
||||
--medium-BorderRadius: 0.5rem;
|
||||
--medium-focus-BorderRadius: 0.625rem;
|
||||
--large-BorderRadius: 1rem;
|
||||
--large-focus-BorderRadius: 1.125rem;
|
||||
}
|
@ -18,7 +18,9 @@
|
||||
.font-medium-12,
|
||||
.font-regular-14,
|
||||
.font-medium-14,
|
||||
.font-bold-14 {
|
||||
.font-bold-14,
|
||||
.paragraph-regular-14,
|
||||
.heading-medium-16 {
|
||||
font-family: var(--font-family);
|
||||
font-style: normal;
|
||||
line-height: 1rem;
|
||||
@ -30,11 +32,13 @@
|
||||
}
|
||||
.font-regular-14,
|
||||
.font-medium-14,
|
||||
.font-bold-14 {
|
||||
.font-bold-14,
|
||||
.paragraph-regular-14 {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
.font-regular-12,
|
||||
.font-regular-14 {
|
||||
.font-regular-14,
|
||||
.paragraph-regular-14 {
|
||||
font-weight: 400;
|
||||
}
|
||||
.font-medium-12,
|
||||
@ -44,7 +48,15 @@
|
||||
.font-bold-14 {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.heading-medium-16 {
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
line-height: 1.125rem;
|
||||
}
|
||||
.paragraph-regular-14 {
|
||||
line-height: 1.25rem;
|
||||
color: var(--global-tertiary-TextColor);
|
||||
}
|
||||
|
||||
/* Panels */
|
||||
* {
|
||||
@ -83,7 +95,7 @@
|
||||
|
||||
&.header { background-color: var(--theme-comp-header-color); }
|
||||
&.filled { background-color: var(--theme-bg-color); }
|
||||
&.filledNav { background-color: var(--theme-navpanel-color); }
|
||||
&.filledNav { background-color: var(--theme-navpanel-color) !important; }
|
||||
&.border-left { border-left: 1px solid var(--theme-divider-color); }
|
||||
&.border-right { border-right: 1px solid var(--theme-divider-color); }
|
||||
}
|
||||
|
@ -13,6 +13,74 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
/* Huly Component */
|
||||
.hulyComponent {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
min-width: 0;
|
||||
background-color: var(--theme-panel-color); // var(--global-surface-02-BackgroundColor);
|
||||
border: 1px solid var(--theme-divider-color); // var(--global-surface-02-BorderColor);
|
||||
border-radius: var(--small-focus-BorderRadius);
|
||||
}
|
||||
.hulyComponent-content,
|
||||
.hulyComponent-content__container,
|
||||
.hulyComponent-content__column,
|
||||
.hulyComponent-content__column.content,
|
||||
.hulyComponent-content__navHeader {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
}
|
||||
.hulyComponent-content {
|
||||
flex-shrink: 0;
|
||||
max-width: 64rem;
|
||||
|
||||
&__container {
|
||||
height: 100%;
|
||||
}
|
||||
&:not(.columns) {
|
||||
flex-direction: column;
|
||||
}
|
||||
&__column {
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
|
||||
&.navigation .hulyNavItem-container,
|
||||
.hulyNavItem-container {
|
||||
margin: 0 0.75rem;
|
||||
}
|
||||
&.content {
|
||||
overflow-y: auto;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: var(--spacing-3);
|
||||
}
|
||||
}
|
||||
&__navHeader {
|
||||
flex-direction: column;
|
||||
flex-shrink: 0;
|
||||
padding-bottom: var(--spacing-3);
|
||||
border-bottom: 1px solid var(--theme-navpanel-divider);
|
||||
|
||||
&-menu {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
padding: var(--spacing-2);
|
||||
width: var(--global-extra-large-Size);
|
||||
height: var(--global-extra-large-Size);
|
||||
}
|
||||
&-hint {
|
||||
margin: var(--spacing-0_25) var(--spacing-3) 0 var(--spacing-2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Component */
|
||||
.antiComponent {
|
||||
display: flex;
|
||||
|
@ -13,6 +13,7 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
@import "./_vars.scss";
|
||||
@import "./_colors.scss";
|
||||
@import "./_layouts.scss";
|
||||
@import "./common.scss";
|
||||
|
85
packages/ui/src/components/Breadcrumb.svelte
Normal file
85
packages/ui/src/components/Breadcrumb.svelte
Normal file
@ -0,0 +1,85 @@
|
||||
<!--
|
||||
// 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">
|
||||
import type { Asset, IntlString } from '@hcengineering/platform'
|
||||
import { AnySvelteComponent } from '../types'
|
||||
import { ComponentType } from 'svelte'
|
||||
import Icon from './Icon.svelte'
|
||||
import Label from './Label.svelte'
|
||||
|
||||
export let icon: Asset | AnySvelteComponent | ComponentType | undefined = undefined
|
||||
export let label: IntlString | undefined = undefined
|
||||
export let title: string | undefined = undefined
|
||||
export let size: 'large' | 'small'
|
||||
export let isCurrent: boolean = false
|
||||
</script>
|
||||
|
||||
<button class="hulyBreadcrumb-container {size}" class:current={isCurrent} on:click>
|
||||
{#if size === 'large' && icon}
|
||||
<div class="hulyBreadcrumb-avatar">
|
||||
<Icon {icon} size={'small'} />
|
||||
</div>
|
||||
{/if}
|
||||
<span class="{size === 'large' ? 'heading-medium-16' : 'font-regular-14'} hulyBreadcrumb-label overflow-label">
|
||||
{#if label}
|
||||
<Label {label} />
|
||||
{:else if title}
|
||||
{title}
|
||||
{/if}
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<style lang="scss">
|
||||
.hulyBreadcrumb-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-0_75);
|
||||
margin: 0;
|
||||
padding: 0 var(--spacing-1);
|
||||
min-width: 0;
|
||||
border: none;
|
||||
outline: none;
|
||||
|
||||
.hulyBreadcrumb-avatar {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
padding: var(--spacing-0_5);
|
||||
width: var(--global-extra-small-Size);
|
||||
height: var(--global-extra-small-Size);
|
||||
color: var(--global-secondary-TextColor);
|
||||
background-color: var(--global-ui-BackgroundColor);
|
||||
border-radius: var(--extra-small-BorderRadius);
|
||||
}
|
||||
.hulyBreadcrumb-label {
|
||||
color: var(--global-secondary-TextColor);
|
||||
}
|
||||
&.current .hulyBreadcrumb-label {
|
||||
font-weight: 700;
|
||||
}
|
||||
&:not(.current) {
|
||||
cursor: pointer;
|
||||
}
|
||||
&:hover {
|
||||
.hulyBreadcrumb-avatar {
|
||||
background-color: var(--global-ui-hover-BackgroundColor);
|
||||
}
|
||||
.hulyBreadcrumb-label {
|
||||
color: var(--global-primary-LinkColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
75
packages/ui/src/components/Breadcrumbs.svelte
Normal file
75
packages/ui/src/components/Breadcrumbs.svelte
Normal file
@ -0,0 +1,75 @@
|
||||
<!--
|
||||
// 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">
|
||||
import { createEventDispatcher, ComponentType } from 'svelte'
|
||||
import type { Asset, IntlString } from '@hcengineering/platform'
|
||||
import { AnySvelteComponent } from '../types'
|
||||
import ChevronRight from './icons/ChevronRight.svelte'
|
||||
import Label from './Label.svelte'
|
||||
import Breadcrumb from './Breadcrumb.svelte'
|
||||
|
||||
interface BreadcrumbItem {
|
||||
icon?: Asset | AnySvelteComponent | ComponentType
|
||||
label?: IntlString
|
||||
title?: string
|
||||
}
|
||||
|
||||
export let items: BreadcrumbItem[]
|
||||
export let afterLabel: IntlString | undefined = undefined
|
||||
export let size: 'large' | 'small' = 'large'
|
||||
export let selected: number | null = null
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
</script>
|
||||
|
||||
<div class="hulyBreadcrumbs-container {size}">
|
||||
{#each items as item, i}
|
||||
{#if i !== 0}<ChevronRight size={'small'} />{/if}
|
||||
<Breadcrumb
|
||||
{...item}
|
||||
{size}
|
||||
isCurrent={selected === i}
|
||||
on:click={() => {
|
||||
if (selected !== i) dispatch('select', i)
|
||||
}}
|
||||
/>
|
||||
{/each}
|
||||
{#if afterLabel}
|
||||
<span class="hulyBreadcrumbs-afterLabel">
|
||||
<Label label={afterLabel} />
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.hulyBreadcrumbs-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: var(--global-small-Size);
|
||||
min-width: 0;
|
||||
|
||||
.hulyBreadcrumbs-afterLabel {
|
||||
white-space: nowrap;
|
||||
word-break: break-all;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
padding: var(--spacing-0_25) var(--spacing-0_5);
|
||||
text-transform: uppercase;
|
||||
background-color: var(--global-ui-hover-BackgroundColor);
|
||||
color: var(--global-secondary-TextColor);
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
}
|
||||
</style>
|
304
packages/ui/src/components/ButtonBase.svelte
Normal file
304
packages/ui/src/components/ButtonBase.svelte
Normal file
@ -0,0 +1,304 @@
|
||||
<!--
|
||||
// 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">
|
||||
import type { Asset, IntlString } from '@hcengineering/platform'
|
||||
import { AnySvelteComponent } from '../types'
|
||||
import { ComponentType } from 'svelte'
|
||||
import Spinner from './Spinner.svelte'
|
||||
import Icon from './Icon.svelte'
|
||||
import Label from './Label.svelte'
|
||||
|
||||
export let title: string | undefined = undefined
|
||||
export let label: IntlString | undefined = undefined
|
||||
export let icon: Asset | AnySvelteComponent | ComponentType | undefined = undefined
|
||||
export let kind: 'primary' | 'secondary' | 'tertiary' | 'negative'
|
||||
export let size: 'large' | 'medium' | 'small'
|
||||
export let disabled: boolean = false
|
||||
export let loading: boolean = false
|
||||
export let pressed: boolean = false
|
||||
export let hasMenu: boolean = false
|
||||
export let type: 'type-button' | 'type-button-icon'
|
||||
export let inheritColor: boolean = false
|
||||
</script>
|
||||
|
||||
<button
|
||||
class="font-medium-14 {kind} {size} {type}"
|
||||
class:loading
|
||||
class:pressed
|
||||
class:inheritColor
|
||||
class:menu={hasMenu}
|
||||
disabled={loading || disabled}
|
||||
on:click
|
||||
>
|
||||
{#if loading}
|
||||
<div class="icon animate"><Spinner size={'small'} /></div>
|
||||
{:else if icon}<div class="icon"><Icon {icon} size={'small'} /></div>{/if}
|
||||
{#if title}<span>{title}</span>{/if}
|
||||
{#if label}<span><Label {label} /></span>{/if}
|
||||
</button>
|
||||
|
||||
<style lang="scss">
|
||||
button {
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
gap: var(--spacing-1);
|
||||
|
||||
border: 1px;
|
||||
border-style: solid;
|
||||
|
||||
&:not(:disabled, .loading) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: var(--spacing-2_5);
|
||||
height: var(--spacing-2_5);
|
||||
|
||||
&.animate {
|
||||
animation: rotate 2s linear infinite;
|
||||
}
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: 2px solid var(--global-focus-BorderColor);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
&.type-button-icon {
|
||||
padding: 0;
|
||||
}
|
||||
&.large {
|
||||
height: var(--spacing-6);
|
||||
border-radius: var(--medium-BorderRadius);
|
||||
|
||||
&.type-button {
|
||||
padding: 0 var(--spacing-2);
|
||||
}
|
||||
&.type-button-icon {
|
||||
width: var(--spacing-6);
|
||||
}
|
||||
}
|
||||
&.medium {
|
||||
height: var(--spacing-5);
|
||||
border-radius: var(--medium-BorderRadius);
|
||||
|
||||
&.type-button {
|
||||
padding: 0 var(--spacing-2);
|
||||
}
|
||||
&.type-button-icon {
|
||||
width: var(--spacing-5);
|
||||
}
|
||||
}
|
||||
&.small {
|
||||
height: var(--spacing-4);
|
||||
border-radius: var(--small-BorderRadius);
|
||||
|
||||
&.type-button {
|
||||
padding: 0 var(--spacing-1_5);
|
||||
}
|
||||
&.type-button-icon {
|
||||
width: var(--spacing-4);
|
||||
}
|
||||
}
|
||||
&.type-button-icon .icon {
|
||||
width: var(--spacing-2);
|
||||
height: var(--spacing-2);
|
||||
}
|
||||
|
||||
&.primary {
|
||||
border-color: var(--button-primary-BorderColor);
|
||||
background-color: var(--button-primary-BackgroundColor);
|
||||
|
||||
.icon {
|
||||
fill: var(--button-accent-IconColor);
|
||||
}
|
||||
span {
|
||||
color: var(--button-accent-LabelColor);
|
||||
}
|
||||
&:hover {
|
||||
background-color: var(--button-primary-hover-BackgroundColor);
|
||||
}
|
||||
&:active,
|
||||
&.pressed {
|
||||
background-color: var(--button-primary-active-BackgroundColor);
|
||||
}
|
||||
&.menu:enabled:active,
|
||||
&.pressed {
|
||||
border-color: var(--button-menu-active-BorderColor);
|
||||
}
|
||||
&:disabled:not(.loading) {
|
||||
background-color: var(--button-disabled-BackgroundColor);
|
||||
border-color: transparent;
|
||||
cursor: not-allowed;
|
||||
|
||||
.icon {
|
||||
fill: var(--button-disabled-IconColor);
|
||||
}
|
||||
span {
|
||||
color: var(--button-disabled-LabelColor);
|
||||
}
|
||||
}
|
||||
&.loading {
|
||||
background-color: var(--button-primary-active-BackgroundColor);
|
||||
|
||||
span {
|
||||
color: var(--button-primary-loading-LabelColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.secondary {
|
||||
border-color: var(--button-secondary-BorderColor);
|
||||
background-color: var(--button-secondary-BackgroundColor);
|
||||
|
||||
.icon {
|
||||
fill: var(--button-subtle-IconColor);
|
||||
}
|
||||
span {
|
||||
color: var(--button-subtle-LabelColor);
|
||||
}
|
||||
&:hover {
|
||||
background-color: var(--button-secondary-hover-BackgroundColor);
|
||||
}
|
||||
&:active,
|
||||
&.pressed {
|
||||
background-color: var(--button-secondary-active-BackgroundColor);
|
||||
}
|
||||
&.menu:enabled:active,
|
||||
&.pressed {
|
||||
border-color: var(--button-menu-active-BorderColor);
|
||||
}
|
||||
&:disabled:not(.loading) {
|
||||
background-color: var(--button-disabled-BackgroundColor);
|
||||
border-color: transparent;
|
||||
cursor: not-allowed;
|
||||
|
||||
.icon {
|
||||
fill: var(--button-disabled-IconColor);
|
||||
}
|
||||
span {
|
||||
color: var(--button-disabled-LabelColor);
|
||||
}
|
||||
}
|
||||
&.loading {
|
||||
background-color: var(--button-secondary-active-BackgroundColor);
|
||||
|
||||
span {
|
||||
color: var(--button-disabled-LabelColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.tertiary {
|
||||
border-color: transparent;
|
||||
background-color: transparent;
|
||||
|
||||
&:not(.inheritColor) .icon {
|
||||
fill: var(--button-subtle-IconColor);
|
||||
}
|
||||
&.inheritColor {
|
||||
color: inherit;
|
||||
|
||||
.icon {
|
||||
fill: currentColor;
|
||||
}
|
||||
}
|
||||
span {
|
||||
color: var(--button-subtle-LabelColor);
|
||||
}
|
||||
&:hover:enabled {
|
||||
background-color: var(--button-tertiary-hover-BackgroundColor);
|
||||
}
|
||||
&:active:enabled,
|
||||
&.pressed:enabled {
|
||||
background-color: var(--button-tertiary-active-BackgroundColor);
|
||||
}
|
||||
&.menu:active:enabled,
|
||||
&.pressed:enabled {
|
||||
border-color: var(--button-menu-active-BorderColor);
|
||||
}
|
||||
&:disabled:not(.loading) {
|
||||
border-color: transparent;
|
||||
cursor: not-allowed;
|
||||
|
||||
.icon {
|
||||
fill: var(--button-disabled-IconColor);
|
||||
}
|
||||
span {
|
||||
color: var(--button-disabled-LabelColor);
|
||||
}
|
||||
}
|
||||
&.loading {
|
||||
background-color: var(--button-tertiary-active-BackgroundColor);
|
||||
|
||||
span {
|
||||
color: var(--button-disabled-LabelColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.negative {
|
||||
border-color: var(--button-negative-BorderColor);
|
||||
background-color: var(--button-negative-BackgroundColor);
|
||||
|
||||
.icon {
|
||||
fill: var(--button-accent-IconColor);
|
||||
}
|
||||
span {
|
||||
color: var(--button-accent-LabelColor);
|
||||
}
|
||||
&:hover {
|
||||
background-color: var(--button-negative-hover-BackgroundColor);
|
||||
}
|
||||
&:active,
|
||||
&.pressed {
|
||||
background-color: var(--button-negative-active-BackgroundColor);
|
||||
}
|
||||
&.menu:enabled:active,
|
||||
&.pressed {
|
||||
border-color: var(--button-menu-active-BorderColor);
|
||||
}
|
||||
&:disabled:not(.loading) {
|
||||
background-color: var(--button-disabled-BackgroundColor);
|
||||
border-color: transparent;
|
||||
cursor: not-allowed;
|
||||
|
||||
.icon {
|
||||
fill: var(--button-disabled-IconColor);
|
||||
}
|
||||
span {
|
||||
color: var(--button-disabled-LabelColor);
|
||||
}
|
||||
}
|
||||
&.loading {
|
||||
background-color: var(--button-negative-active-BackgroundColor);
|
||||
|
||||
span {
|
||||
color: var(--button-negative-loading-LabelColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes rotate {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(359deg);
|
||||
}
|
||||
}
|
||||
</style>
|
29
packages/ui/src/components/ButtonIcon.svelte
Normal file
29
packages/ui/src/components/ButtonIcon.svelte
Normal file
@ -0,0 +1,29 @@
|
||||
<!--
|
||||
// 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">
|
||||
import type { Asset } from '@hcengineering/platform'
|
||||
import { AnySvelteComponent } from '../types'
|
||||
import { ComponentType } from 'svelte'
|
||||
import ButtonBase from './ButtonBase.svelte'
|
||||
|
||||
export let kind: 'primary' | 'secondary' | 'tertiary' | 'negative' = 'secondary'
|
||||
export let size: 'large' | 'medium' | 'small' = 'large'
|
||||
export let icon: Asset | AnySvelteComponent | ComponentType
|
||||
export let disabled: boolean = false
|
||||
export let loading: boolean = false
|
||||
export let inheritColor: boolean = false
|
||||
</script>
|
||||
|
||||
<ButtonBase type={'type-button-icon'} {kind} {size} {icon} {disabled} {loading} {inheritColor} on:click />
|
52
packages/ui/src/components/Fold.svelte
Normal file
52
packages/ui/src/components/Fold.svelte
Normal file
@ -0,0 +1,52 @@
|
||||
<!--
|
||||
// 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">
|
||||
export let isOpen: boolean
|
||||
export let empty: boolean = false
|
||||
export let level: number = 1
|
||||
</script>
|
||||
|
||||
<div class="hulyFold-container" class:opened={isOpen && !empty} style:margin-left={`${(level - 1) * 1.5}rem`}>
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||
{#if empty}
|
||||
<path
|
||||
d="M8 8.99988C8.55228 8.99988 9 8.55216 9 7.99988C9 7.44759 8.55228 6.99988 8 6.99988C7.44772 6.99988 7 7.44759 7 7.99988C7 8.55216 7.44772 8.99988 8 8.99988Z"
|
||||
/>
|
||||
{:else}
|
||||
<path d="M11 8L5.99995 3L5.29285 3.70711L9.58574 8L5.29285 12.2929L5.99995 13L11 8Z" />
|
||||
{/if}
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.hulyFold-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
padding: 0.25rem;
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
border-radius: 0.25rem;
|
||||
color: var(--button-disabled-IconColor);
|
||||
transform-origin: center;
|
||||
transform: rotate(0deg);
|
||||
transition: transform 0.15s ease-in-out;
|
||||
|
||||
&.opened {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
}
|
||||
</style>
|
89
packages/ui/src/components/Header.svelte
Normal file
89
packages/ui/src/components/Header.svelte
Normal file
@ -0,0 +1,89 @@
|
||||
<!--
|
||||
// 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">
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { IconMaximize, IconMinimize } from '..'
|
||||
|
||||
export let minimize: boolean = false
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
</script>
|
||||
|
||||
<div class="hulyHeader-container">
|
||||
<button class="hulyHeader-button" on:click={() => dispatch('resize', minimize)}>
|
||||
{#if minimize}
|
||||
<IconMinimize size={'small'} />
|
||||
{:else}
|
||||
<IconMaximize size={'small'} />
|
||||
{/if}
|
||||
</button>
|
||||
<div class="hulyHeader-divider" />
|
||||
<div class="hulyHeader-titleGroup">
|
||||
<slot />
|
||||
</div>
|
||||
{#if $$slots.actions}
|
||||
<div class="hulyHeader-buttonsGroup">
|
||||
<slot name="actions" />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.hulyHeader-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: var(--spacing-1_5) var(--spacing-2);
|
||||
width: 100%;
|
||||
height: var(--spacing-6_5);
|
||||
border-bottom: 1px solid var(--theme-divider-color); // var(--global-surface-02-BorderColor);
|
||||
|
||||
.hulyHeader-button {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
padding: 0;
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
color: var(--button-disabled-IconColor);
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
color: var(--button-subtle-LabelColor);
|
||||
}
|
||||
}
|
||||
.hulyHeader-divider {
|
||||
flex-shrink: 0;
|
||||
margin: 0 var(--spacing-2);
|
||||
width: 1px;
|
||||
height: var(--spacing-4);
|
||||
background-color: var(--theme-divider-color); // var(--global-surface-02-BorderColor);
|
||||
}
|
||||
.hulyHeader-titleGroup,
|
||||
.hulyHeader-buttonsGroup {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-width: 0;
|
||||
}
|
||||
.hulyHeader-titleGroup {
|
||||
flex-grow: 1;
|
||||
gap: var(--spacing-0_5);
|
||||
}
|
||||
.hulyHeader-buttonsGroup {
|
||||
gap: var(--spacing-1);
|
||||
margin-left: var(--spacing-2);
|
||||
}
|
||||
}
|
||||
</style>
|
189
packages/ui/src/components/ModernEditbox.svelte
Normal file
189
packages/ui/src/components/ModernEditbox.svelte
Normal file
@ -0,0 +1,189 @@
|
||||
<!--
|
||||
// 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">
|
||||
export let label: string
|
||||
export let value: string | undefined = undefined
|
||||
export let kind: 'default' | 'ghost' = 'default'
|
||||
export let size: 'small' | 'large' = 'small'
|
||||
export let disabled: boolean = false
|
||||
export let error: boolean = false
|
||||
export let password: boolean = false
|
||||
export let limit: number = 0
|
||||
|
||||
$: labeled = kind === 'default' && size === 'large'
|
||||
$: placeholder = labeled ? ' ' : label
|
||||
$: maxlength = limit === 0 ? null : limit
|
||||
</script>
|
||||
|
||||
<label class="editbox-wrapper {kind} {size}" class:error class:disabled>
|
||||
{#if password}
|
||||
<input
|
||||
type="password"
|
||||
class="font-regular-14"
|
||||
class:labeled
|
||||
bind:value
|
||||
autocomplete="off"
|
||||
{placeholder}
|
||||
spellcheck="false"
|
||||
{disabled}
|
||||
{maxlength}
|
||||
on:blur
|
||||
on:change
|
||||
on:keyup
|
||||
on:input
|
||||
/>
|
||||
{:else}
|
||||
<input
|
||||
type="text"
|
||||
class="font-regular-14"
|
||||
class:labeled
|
||||
bind:value
|
||||
autocomplete="off"
|
||||
{placeholder}
|
||||
spellcheck="false"
|
||||
{disabled}
|
||||
{maxlength}
|
||||
on:blur
|
||||
on:change
|
||||
on:keyup
|
||||
on:input
|
||||
/>
|
||||
{/if}
|
||||
{#if labeled}<div class="font-regular-14 label">{label}</div>{/if}
|
||||
</label>
|
||||
|
||||
<style lang="scss">
|
||||
.editbox-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-width: 0;
|
||||
border-radius: var(--medium-BorderRadius);
|
||||
|
||||
&.default {
|
||||
background-color: var(--input-BackgroundColor);
|
||||
box-shadow: inset 0 0 0 1px var(--input-BorderColor);
|
||||
|
||||
&.small {
|
||||
padding: var(--spacing-1) var(--spacing-1_5);
|
||||
height: var(--spacing-4);
|
||||
}
|
||||
&.large {
|
||||
position: relative;
|
||||
padding: 0 var(--spacing-2);
|
||||
height: var(--spacing-6_5);
|
||||
}
|
||||
}
|
||||
&.ghost {
|
||||
&.small {
|
||||
padding: var(--spacing-1_5) var(--spacing-2);
|
||||
height: var(--spacing-5);
|
||||
}
|
||||
&.large {
|
||||
padding: var(--spacing-1) var(--spacing-2);
|
||||
height: var(--spacing-6);
|
||||
|
||||
input {
|
||||
font-weight: 500;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.error {
|
||||
box-shadow: inset 0 0 0 1px var(--input-error-BorderColor);
|
||||
}
|
||||
&:not(.disabled) {
|
||||
cursor: text;
|
||||
|
||||
&.default {
|
||||
input::placeholder {
|
||||
color: var(--input-LabelColor);
|
||||
}
|
||||
&:active,
|
||||
&:focus-within {
|
||||
background-color: var(--input-BackgroundColor);
|
||||
outline: 2px solid var(--global-focus-BorderColor);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
&:hover {
|
||||
background-color: var(--input-hover-BackgroundColor);
|
||||
}
|
||||
}
|
||||
&.ghost input::placeholder {
|
||||
color: var(--input-PlaceholderColor);
|
||||
}
|
||||
&:hover input:not(:focus)::placeholder {
|
||||
color: var(--input-hover-PlaceholderColor);
|
||||
}
|
||||
input:focus::placeholder {
|
||||
color: var(--input-focus-PlaceholderColor);
|
||||
}
|
||||
}
|
||||
&.disabled {
|
||||
&,
|
||||
input {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
input::placeholder {
|
||||
color: var(--input-PlaceholderColor);
|
||||
}
|
||||
&.default {
|
||||
background-color: transparent;
|
||||
}
|
||||
&.ghost {
|
||||
box-shadow: inset 0 0 0 1px var(--input-BorderColor);
|
||||
}
|
||||
}
|
||||
|
||||
input {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
color: var(--input-TextColor);
|
||||
caret-color: var(--global-focus-BorderColor);
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
outline: none;
|
||||
appearance: none;
|
||||
|
||||
&.labeled {
|
||||
height: 100%;
|
||||
padding-top: var(--spacing-3_5);
|
||||
padding-bottom: var(--spacing-1_5);
|
||||
}
|
||||
}
|
||||
|
||||
.label {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
padding: var(--spacing-2_5) var(--spacing-2);
|
||||
font-size: 0.875rem;
|
||||
color: var(--input-LabelColor);
|
||||
transition:
|
||||
padding-top 0.2s,
|
||||
font-size 0.2s;
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
}
|
||||
input:focus + .label,
|
||||
input:not(:placeholder-shown) + .label {
|
||||
padding-top: var(--spacing-1_5);
|
||||
font-size: 0.75rem;
|
||||
color: var(--input-filled-LabelColor);
|
||||
}
|
||||
}
|
||||
</style>
|
@ -14,20 +14,25 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import type { IntlString } from '@hcengineering/platform'
|
||||
import type { AnyComponent } from '@hcengineering/ui'
|
||||
import { Label, Component } from '@hcengineering/ui'
|
||||
import type { AnyComponent } from '..'
|
||||
import { Label, Component } from '..'
|
||||
|
||||
export let label: IntlString
|
||||
export let categoryName: string
|
||||
export let selected: boolean = false
|
||||
export let tools: AnyComponent | undefined = undefined
|
||||
export let collapsed: boolean = false
|
||||
export let second: boolean = false
|
||||
|
||||
$: id = `navGroup-${categoryName}`
|
||||
</script>
|
||||
|
||||
<div class="hulyAccordionItem-container">
|
||||
<button class="hulyAccordionItem-header" class:selected on:click={() => (collapsed = !collapsed)}>
|
||||
<div class="hulyAccordionItem-container" class:collapsed class:second>
|
||||
<button
|
||||
class="hulyAccordionItem-header"
|
||||
class:selected={selected || !collapsed}
|
||||
on:click={() => (collapsed = !collapsed)}
|
||||
>
|
||||
<div class="hulyAccordionItem-header__label font-medium-12">
|
||||
<Label {label} />
|
||||
</div>
|
||||
@ -53,9 +58,13 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 0;
|
||||
border-top: 1px solid var(--theme-navpanel-divider);
|
||||
// border-bottom: 1px solid var(--theme-navpanel-divider); // var(--global-surface-01-BorderColor);
|
||||
|
||||
&:not(.second) {
|
||||
border-top: 1px solid var(--theme-navpanel-divider);
|
||||
}
|
||||
&.second.collapsed {
|
||||
border-bottom: 1px solid var(--theme-navpanel-divider); // var(--global-surface-01-BorderColor);
|
||||
}
|
||||
.hulyAccordionItem-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
@ -1,5 +1,5 @@
|
||||
<!--
|
||||
// Copyright © 2021 Anticrm Platform Contributors.
|
||||
// Copyright © 2021, 2023 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
|
||||
@ -14,7 +14,7 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import type { Asset, IntlString } from '@hcengineering/platform'
|
||||
import { Icon, Label, IconOpenedArrow } from '@hcengineering/ui'
|
||||
import { Icon, Label, IconOpenedArrow, Fold } from '..'
|
||||
|
||||
export let icon: Asset | undefined = undefined
|
||||
export let label: IntlString | undefined = undefined
|
||||
@ -23,20 +23,30 @@
|
||||
export let count: number | null = null
|
||||
export let selected: boolean = false
|
||||
export let isFold: boolean = false
|
||||
export let isOpen: boolean = false
|
||||
export let empty: boolean = false
|
||||
export let level: number = 1
|
||||
</script>
|
||||
|
||||
<button
|
||||
class="hulyNavItem-container {type} {type === 'type-anchor-link' ? 'font-regular-12' : 'font-regular-14'}"
|
||||
class:fold={isFold}
|
||||
class:selected
|
||||
on:click|stopPropagation
|
||||
on:contextmenu|preventDefault|stopPropagation
|
||||
>
|
||||
<div class="hulyNavItem-icon">
|
||||
{#if type !== 'type-tag' && icon}
|
||||
<Icon {icon} size={'small'} />
|
||||
{:else if type === 'type-tag'}
|
||||
<div style:background-color={color} class="hulyNavItem-icon__tag" />
|
||||
{/if}
|
||||
</div>
|
||||
{#if isFold}
|
||||
<Fold {isOpen} {empty} {level} />
|
||||
{/if}
|
||||
{#if icon || (type === 'type-tag' && color)}
|
||||
<div class="hulyNavItem-icon">
|
||||
{#if type !== 'type-tag' && icon}
|
||||
<Icon {icon} size={'small'} />
|
||||
{:else if type === 'type-tag'}
|
||||
<div style:background-color={color} class="hulyNavItem-icon__tag" />
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
<span class="hulyNavItem-label" style:color={type === 'type-tag' && selected ? color : null}>
|
||||
{#if label}<Label {label} />{/if}
|
||||
</span>
|
||||
@ -78,8 +88,12 @@
|
||||
}
|
||||
&.right {
|
||||
visibility: hidden;
|
||||
margin-left: 0.5rem;
|
||||
color: var(--global-accent-IconColor);
|
||||
}
|
||||
&:not(.right) {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
}
|
||||
.hulyNavItem-label {
|
||||
white-space: nowrap;
|
||||
@ -91,6 +105,7 @@
|
||||
color: var(--global-primary-TextColor);
|
||||
}
|
||||
.hulyNavItem-count {
|
||||
margin-left: 0.5rem;
|
||||
color: var(--global-tertiary-TextColor);
|
||||
}
|
||||
&:not(.selected):hover {
|
||||
@ -109,12 +124,15 @@
|
||||
}
|
||||
|
||||
&.type-link {
|
||||
gap: 0.5rem;
|
||||
padding: 0 0.625rem;
|
||||
|
||||
&.selected {
|
||||
padding: 0 0.375rem 0 0.625rem;
|
||||
|
||||
&:not(.fold) {
|
||||
padding: 0 0.375rem 0 0.625rem;
|
||||
}
|
||||
&.fold {
|
||||
padding: 0 0.375rem 0 0.25rem;
|
||||
}
|
||||
.hulyNavItem-icon {
|
||||
color: var(--global-accent-TextColor);
|
||||
}
|
||||
@ -128,7 +146,6 @@
|
||||
}
|
||||
}
|
||||
&.type-tag {
|
||||
gap: 0.5rem;
|
||||
padding: 0 0.625rem;
|
||||
|
||||
.hulyNavItem-label {
|
||||
@ -136,7 +153,6 @@
|
||||
}
|
||||
}
|
||||
&.type-object {
|
||||
gap: 0.375rem;
|
||||
padding: 0 0.625rem 0 0.25rem;
|
||||
|
||||
.hulyNavItem-icon {
|
||||
@ -144,6 +160,10 @@
|
||||
height: 1.5rem;
|
||||
background-color: var(--global-ui-BackgroundColor);
|
||||
border-radius: 0.25rem;
|
||||
|
||||
&:not(.right) {
|
||||
margin-right: 0.375rem;
|
||||
}
|
||||
}
|
||||
.hulyNavItem-label {
|
||||
flex-grow: 1;
|
||||
@ -153,7 +173,6 @@
|
||||
}
|
||||
}
|
||||
&.type-anchor-link {
|
||||
gap: 0.5rem;
|
||||
padding: 0 0.75rem 0 0.625rem;
|
||||
min-height: 1.75rem;
|
||||
|
||||
@ -169,5 +188,12 @@
|
||||
color: var(--global-primary-TextColor);
|
||||
}
|
||||
}
|
||||
&.fold {
|
||||
padding-left: 0.25rem;
|
||||
|
||||
:global(.hulyFold-container) {
|
||||
margin-right: 0.375rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
22
packages/ui/src/components/icons/ChevronRight.svelte
Normal file
22
packages/ui/src/components/icons/ChevronRight.svelte
Normal file
@ -0,0 +1,22 @@
|
||||
<!--
|
||||
// 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">
|
||||
export let size: 'small' | 'medium' | 'large'
|
||||
const fill: string = 'currentColor'
|
||||
</script>
|
||||
|
||||
<svg class="svg-{size}" {fill} viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.0001 8L6.00008 3L5.29297 3.70711L9.58586 8L5.29297 12.2929L6.00008 13L11.0001 8Z" />
|
||||
</svg>
|
31
packages/ui/src/components/icons/Description.svelte
Normal file
31
packages/ui/src/components/icons/Description.svelte
Normal file
@ -0,0 +1,31 @@
|
||||
<!--
|
||||
// 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">
|
||||
import { IconSize } from '../../types'
|
||||
export let size: IconSize
|
||||
const fill: string = 'currentColor'
|
||||
</script>
|
||||
|
||||
<svg class="svg-{size}" {fill} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||
<path
|
||||
d="M2 4.5C2 4.22386 2.22386 4 2.5 4H13.5C13.7761 4 14 4.22386 14 4.5C14 4.77614 13.7761 5 13.5 5H2.5C2.22386 5 2 4.77614 2 4.5Z"
|
||||
/>
|
||||
<path
|
||||
d="M2 8C2 7.72386 2.22386 7.5 2.5 7.5H9.5C9.77614 7.5 10 7.72386 10 8C10 8.27614 9.77614 8.5 9.5 8.5H2.5C2.22386 8.5 2 8.27614 2 8Z"
|
||||
/>
|
||||
<path
|
||||
d="M2 11.5C2 11.2239 2.22386 11 2.5 11H11.5C11.7761 11 12 11.2239 12 11.5C12 11.7761 11.7761 12 11.5 12H2.5C2.22386 12 2 11.7761 2 11.5Z"
|
||||
/>
|
||||
</svg>
|
23
packages/ui/src/components/icons/Maximize.svelte
Normal file
23
packages/ui/src/components/icons/Maximize.svelte
Normal file
@ -0,0 +1,23 @@
|
||||
<!--
|
||||
// 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">
|
||||
import { IconSize } from '../../types'
|
||||
export let size: IconSize
|
||||
const fill: string = 'currentColor'
|
||||
</script>
|
||||
|
||||
<svg class="svg-{size}" {fill} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||
<path d="M7 3V2H2V7H3V3.707L12.293 13H9V14H14V9H13V12.293L3.707 3H7Z" />
|
||||
</svg>
|
28
packages/ui/src/components/icons/Minimize.svelte
Normal file
28
packages/ui/src/components/icons/Minimize.svelte
Normal file
@ -0,0 +1,28 @@
|
||||
<!--
|
||||
// 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">
|
||||
import { IconSize } from '../../types'
|
||||
export let size: IconSize
|
||||
const fill: string = 'currentColor'
|
||||
</script>
|
||||
|
||||
<svg class="svg-{size}" {fill} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||
<path
|
||||
d="M14.1464 1.14645C14.3417 0.951184 14.6583 0.951184 14.8535 1.14645C15.0477 1.34063 15.0488 1.6548 14.8567 1.85031L10.707 5.99999H14V6.99999H8.99996V1.99999H9.99996V5.29299L14.1464 1.14645Z"
|
||||
/>
|
||||
<path
|
||||
d="M1.99996 8.99999V9.99999H5.29296L1.14645 14.1464C0.951184 14.3417 0.951184 14.6583 1.14645 14.8535C1.34171 15.0488 1.65829 15.0488 1.85355 14.8535L5.99996 10.707V14H6.99996V8.99999H1.99996Z"
|
||||
/>
|
||||
</svg>
|
@ -3,37 +3,27 @@
|
||||
const fill: string = 'currentColor'
|
||||
</script>
|
||||
|
||||
<svg class="svg-{size}" {fill} viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg class="svg-{size}" {fill} viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M13 9.99988C13 10.5522 13.4477 10.9999 14 10.9999C14.5523 10.9999 15 10.5522 15 9.99988C15 9.44759 14.5523 8.99988 14 8.99988C13.4477 8.99988 13 9.44759 13 9.99988Z"
|
||||
fill="#8B97AD"
|
||||
/>
|
||||
<path
|
||||
d="M13 13.9999C13 14.5522 13.4477 14.9999 14 14.9999C14.5523 14.9999 15 14.5522 15 13.9999C15 13.4476 14.5523 12.9999 14 12.9999C13.4477 12.9999 13 13.4476 13 13.9999Z"
|
||||
fill="#8B97AD"
|
||||
/>
|
||||
<path
|
||||
d="M14 18.9999C13.4477 18.9999 13 18.5522 13 17.9999C13 17.4476 13.4477 16.9999 14 16.9999C14.5523 16.9999 15 17.4476 15 17.9999C15 18.5522 14.5523 18.9999 14 18.9999Z"
|
||||
fill="#8B97AD"
|
||||
/>
|
||||
<path
|
||||
d="M13 6C13 6.55228 13.4477 7 14 7C14.5523 7 15 6.55228 15 6C15 5.44772 14.5523 5 14 5C13.4477 5 13 5.44772 13 6Z"
|
||||
fill="#8B97AD"
|
||||
d="M9 5.99988C9 6.55216 9.44772 6.99988 10 6.99988C10.5523 6.99988 11 6.55216 11 5.99988C11 5.44759 10.5523 4.99988 10 4.99988C9.44772 4.99988 9 5.44759 9 5.99988Z"
|
||||
/>
|
||||
<path
|
||||
d="M9 9.99988C9 10.5522 9.44772 10.9999 10 10.9999C10.5523 10.9999 11 10.5522 11 9.99988C11 9.44759 10.5523 8.99988 10 8.99988C9.44772 8.99988 9 9.44759 9 9.99988Z"
|
||||
fill="#8B97AD"
|
||||
/>
|
||||
<path
|
||||
d="M9 13.9999C9 14.5522 9.44772 14.9999 10 14.9999C10.5523 14.9999 11 14.5522 11 13.9999C11 13.4476 10.5523 12.9999 10 12.9999C9.44772 12.9999 9 13.4476 9 13.9999Z"
|
||||
fill="#8B97AD"
|
||||
d="M10 14.9999C9.44772 14.9999 9 14.5522 9 13.9999C9 13.4476 9.44772 12.9999 10 12.9999C10.5523 12.9999 11 13.4476 11 13.9999C11 14.5522 10.5523 14.9999 10 14.9999Z"
|
||||
/>
|
||||
<path
|
||||
d="M10 18.9999C9.44772 18.9999 9 18.5522 9 17.9999C9 17.4476 9.44772 16.9999 10 16.9999C10.5523 16.9999 11 17.4476 11 17.9999C11 18.5522 10.5523 18.9999 10 18.9999Z"
|
||||
fill="#8B97AD"
|
||||
d="M9 2C9 2.55228 9.44772 3 10 3C10.5523 3 11 2.55228 11 2C11 1.44772 10.5523 1 10 1C9.44772 1 9 1.44772 9 2Z"
|
||||
/>
|
||||
<path
|
||||
d="M9 6C9 6.55228 9.44772 7 10 7C10.5523 7 11 6.55228 11 6C11 5.44772 10.5523 5 10 5C9.44772 5 9 5.44772 9 6Z"
|
||||
fill="#8B97AD"
|
||||
d="M5 5.99988C5 6.55216 5.44772 6.99988 6 6.99988C6.55229 6.99988 7 6.55216 7 5.99988C7 5.44759 6.55229 4.99988 6 4.99988C5.44772 4.99988 5 5.44759 5 5.99988Z"
|
||||
/>
|
||||
<path
|
||||
d="M5 9.99988C5 10.5522 5.44772 10.9999 6 10.9999C6.55228 10.9999 7 10.5522 7 9.99988C7 9.44759 6.55229 8.99988 6 8.99988C5.44772 8.99988 5 9.44759 5 9.99988Z"
|
||||
/>
|
||||
<path
|
||||
d="M6 14.9999C5.44772 14.9999 5 14.5522 5 13.9999C5 13.4476 5.44772 12.9999 6 12.9999C6.55228 12.9999 7 13.4476 7 13.9999C7 14.5522 6.55228 14.9999 6 14.9999Z"
|
||||
/>
|
||||
<path d="M5 2C5 2.55228 5.44772 3 6 3C6.55229 3 7 2.55228 7 2C7 1.44772 6.55229 1 6 1C5.44772 1 5 1.44772 5 2Z" />
|
||||
</svg>
|
||||
|
@ -1,6 +1,5 @@
|
||||
<!--
|
||||
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||
// Copyright © 2021 Hardcore Engineering Inc.
|
||||
// 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
|
||||
|
@ -211,7 +211,7 @@
|
||||
font-size: 12px;
|
||||
line-height: 150%;
|
||||
background-color: var(--theme-statusbar-color);
|
||||
border-bottom: 1px solid var(--theme-navpanel-divider);
|
||||
// border-bottom: 1px solid var(--theme-navpanel-divider);
|
||||
|
||||
.history-box {
|
||||
-webkit-app-region: no-drag;
|
||||
|
@ -123,6 +123,14 @@ export { default as Chevron } from './components/Chevron.svelte'
|
||||
export { default as Timeline } from './components/Timeline.svelte'
|
||||
export { default as TimeShiftPresenter } from './components/TimeShiftPresenter.svelte'
|
||||
export { default as Separator } from './components/Separator.svelte'
|
||||
export { default as Fold } from './components/Fold.svelte'
|
||||
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 ModernEditbox } from './components/ModernEditbox.svelte'
|
||||
export { default as NavItem } from './components/NavItem.svelte'
|
||||
export { default as NavGroup } from './components/NavGroup.svelte'
|
||||
|
||||
export { default as IconAdd } from './components/icons/Add.svelte'
|
||||
export { default as IconCircleAdd } from './components/icons/CircleAdd.svelte'
|
||||
@ -183,6 +191,11 @@ export { default as IconObjects } from './components/icons/Objects.svelte'
|
||||
export { default as IconUndo } from './components/icons/Undo.svelte'
|
||||
export { default as IconRedo } from './components/icons/Redo.svelte'
|
||||
export { default as IconOpenedArrow } from './components/icons/OpenedArrow.svelte'
|
||||
export { default as IconMaximize } from './components/icons/Maximize.svelte'
|
||||
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 PanelInstance } from './components/PanelInstance.svelte'
|
||||
export { default as Panel } from './components/Panel.svelte'
|
||||
|
@ -1,61 +0,0 @@
|
||||
<!--
|
||||
// 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">
|
||||
import type { Asset, IntlString } from '@hcengineering/platform'
|
||||
import { Icon, Label } from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
|
||||
export let icon: Asset | undefined = undefined
|
||||
export let label: IntlString | undefined = undefined
|
||||
export let selected: boolean = false
|
||||
export let expandable = false
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
</script>
|
||||
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<div
|
||||
class="antiNav-element"
|
||||
class:selected
|
||||
class:expandable
|
||||
on:click|stopPropagation={() => {
|
||||
dispatch('click')
|
||||
}}
|
||||
>
|
||||
<div class="an-element__icon">
|
||||
{#if icon}
|
||||
<Icon {icon} size={'small'} />
|
||||
{/if}
|
||||
</div>
|
||||
<span class="an-element__label">
|
||||
{#if label}<Label {label} />{/if}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.expandable {
|
||||
position: relative;
|
||||
&::after {
|
||||
content: '▶';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 0.5rem;
|
||||
font-size: 0.375rem;
|
||||
color: var(--dark-color);
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
}
|
||||
</style>
|
@ -106,37 +106,34 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex-grow vScroll w-full">
|
||||
<div class="container">
|
||||
<Grid {column} columnGap={5} rowGap={1.5}>
|
||||
{#each types as type}
|
||||
<div class="flex">
|
||||
{#if type.generated}
|
||||
<Label label={getLabel(type)} />:
|
||||
{/if}
|
||||
<Label label={type.label} />
|
||||
</div>
|
||||
{#each providers as provider (provider._id)}
|
||||
{#if type.providers[provider._id] !== undefined}
|
||||
<div class="toggle">
|
||||
<ToggleWithLabel
|
||||
label={provider.label}
|
||||
on={getStatus(settings, type._id, provider._id)}
|
||||
on:change={createHandler(type._id, provider._id)}
|
||||
/>
|
||||
</div>
|
||||
{:else}
|
||||
<div />
|
||||
{/if}
|
||||
{/each}
|
||||
<div class="container">
|
||||
<Grid {column} columnGap={5} rowGap={1.5}>
|
||||
{#each types as type}
|
||||
<div class="flex">
|
||||
{#if type.generated}
|
||||
<Label label={getLabel(type)} />:
|
||||
{/if}
|
||||
<Label label={type.label} />
|
||||
</div>
|
||||
{#each providers as provider (provider._id)}
|
||||
{#if type.providers[provider._id] !== undefined}
|
||||
<div class="toggle">
|
||||
<ToggleWithLabel
|
||||
label={provider.label}
|
||||
on={getStatus(settings, type._id, provider._id)}
|
||||
on:change={createHandler(type._id, provider._id)}
|
||||
/>
|
||||
</div>
|
||||
{:else}
|
||||
<div />
|
||||
{/if}
|
||||
{/each}
|
||||
</Grid>
|
||||
</div>
|
||||
{/each}
|
||||
</Grid>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.container {
|
||||
padding: 3rem;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher, onDestroy } from 'svelte'
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import type {
|
||||
NotificationGroup,
|
||||
@ -22,12 +23,26 @@
|
||||
} from '@hcengineering/notification'
|
||||
import { getResource } from '@hcengineering/platform'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { Location, Scroller, getCurrentResolvedLocation, navigate, resolvedLocationStore } from '@hcengineering/ui'
|
||||
import { onDestroy } from 'svelte'
|
||||
import {
|
||||
Location,
|
||||
Scroller,
|
||||
getCurrentResolvedLocation,
|
||||
navigate,
|
||||
resolvedLocationStore,
|
||||
Header,
|
||||
Breadcrumb,
|
||||
defineSeparators,
|
||||
settingsSeparators,
|
||||
Separator,
|
||||
NavItem
|
||||
} from '@hcengineering/ui'
|
||||
import notification from '../plugin'
|
||||
import GroupElement from './GroupElement.svelte'
|
||||
import NotificationGroupSetting from './NotificationGroupSetting.svelte'
|
||||
|
||||
export let visibleNav: boolean = true
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
const client = getClient()
|
||||
let groups: NotificationGroup[] = []
|
||||
let preferencesGroups: NotificationPreferencesGroup[] = []
|
||||
@ -73,14 +88,23 @@
|
||||
})(loc)
|
||||
})
|
||||
)
|
||||
defineSeparators('notificationSettings', settingsSeparators)
|
||||
</script>
|
||||
|
||||
<div class="flex">
|
||||
<div class="antiPanel-element ml-4 mt-2">
|
||||
<div class="antiPanel-wrap__content">
|
||||
<div class="hulyComponent">
|
||||
<Header minimize={!visibleNav} on:resize={(event) => dispatch('change', event.detail)}>
|
||||
<Breadcrumb
|
||||
icon={notification.icon.Notifications}
|
||||
label={notification.string.Notifications}
|
||||
size={'large'}
|
||||
isCurrent
|
||||
/>
|
||||
</Header>
|
||||
<div class="hulyComponent-content__container columns">
|
||||
<div class="hulyComponent-content__column navigation py-2">
|
||||
<Scroller shrink>
|
||||
{#each preferencesGroups as preferenceGroup}
|
||||
<GroupElement
|
||||
<NavItem
|
||||
icon={preferenceGroup.icon}
|
||||
label={preferenceGroup.label}
|
||||
selected={preferenceGroup === currentPreferenceGroup}
|
||||
@ -94,10 +118,10 @@
|
||||
/>
|
||||
{/each}
|
||||
{#if preferencesGroups.length > 0 && groups.length > 0}
|
||||
<div class="antiNav-divider short line" />
|
||||
<div class="antiNav-divider line" />
|
||||
{/if}
|
||||
{#each groups as gr}
|
||||
<GroupElement
|
||||
<NavItem
|
||||
icon={gr.icon}
|
||||
label={gr.label}
|
||||
selected={gr._id === group}
|
||||
@ -114,15 +138,20 @@
|
||||
<div class="antiNav-space" />
|
||||
</Scroller>
|
||||
</div>
|
||||
</div>
|
||||
<div class="antiPanel-component filled">
|
||||
{#if group}
|
||||
<NotificationGroupSetting {group} {settings} />
|
||||
{/if}
|
||||
{#if currentPreferenceGroup}
|
||||
{#await getResource(currentPreferenceGroup.presenter) then presenter}
|
||||
<svelte:component this={presenter} />
|
||||
{/await}
|
||||
{/if}
|
||||
<Separator name={'notificationSettings'} index={0} color={'var(--theme-divider-color)'} />
|
||||
<div class="hulyComponent-content__column content">
|
||||
<div class="hulyComponent-content">
|
||||
<Scroller>
|
||||
{#if group}
|
||||
<NotificationGroupSetting {group} {settings} />
|
||||
{/if}
|
||||
{#if currentPreferenceGroup}
|
||||
{#await getResource(currentPreferenceGroup.presenter) then presenter}
|
||||
<svelte:component this={presenter} />
|
||||
{/await}
|
||||
{/if}
|
||||
</Scroller>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -31,6 +31,8 @@
|
||||
"IntegrationDisabledDescr": "Integration disabled",
|
||||
"IntegrationWith": "Integration with ",
|
||||
"ClassSetting": "Class setting",
|
||||
"ClassSettingHint": "A set or category of things having some property or attribute in common from others by kind, type, or quality.",
|
||||
"ClassProperties": "Class properties",
|
||||
"Classes": "Classes",
|
||||
"Attributes": "Attributes",
|
||||
"DeleteAttribute": "Delete attribute",
|
||||
|
@ -31,6 +31,8 @@
|
||||
"IntegrationDisabledDescr": "Интеграция отключена",
|
||||
"IntegrationWith": "Интеграция с ",
|
||||
"ClassSetting": "Настройки класса",
|
||||
"ClassSettingHint": "Набор или категория вещей, обладающих каким-либо свойством или атрибутом, отличающимся от других по виду, типу или качеству.",
|
||||
"ClassProperties": "Свойства класса",
|
||||
"Classes": "Классы",
|
||||
"Attributes": "Атрибуты",
|
||||
"DeleteAttribute": "Удалить атрибут",
|
||||
|
@ -32,7 +32,7 @@
|
||||
Action,
|
||||
ActionIcon,
|
||||
AnySvelteComponent,
|
||||
CircleButton,
|
||||
ButtonIcon,
|
||||
Icon,
|
||||
IconAdd,
|
||||
IconDelete,
|
||||
@ -41,7 +41,8 @@
|
||||
Label,
|
||||
Menu,
|
||||
getEventPositionElement,
|
||||
showPopup
|
||||
showPopup,
|
||||
IconSettings
|
||||
} from '@hcengineering/ui'
|
||||
import { getContextActions } from '@hcengineering/view-resources'
|
||||
import settings from '../plugin'
|
||||
@ -53,7 +54,6 @@
|
||||
export let ofClass: Ref<Class<Doc>> | undefined = undefined
|
||||
export let useOfClassAttributes = true
|
||||
export let showTitle = true
|
||||
export let showCreate = true
|
||||
|
||||
export let attributeMapper:
|
||||
| {
|
||||
@ -69,6 +69,7 @@
|
||||
const classQuery = createQuery()
|
||||
|
||||
let clazz: Class<Doc> | undefined
|
||||
let hovered: number | null = null
|
||||
|
||||
$: classQuery.query(core.class.Class, { _id: _class }, (res) => {
|
||||
clazz = res.shift()
|
||||
@ -120,7 +121,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[] = [
|
||||
@ -152,7 +154,9 @@
|
||||
}
|
||||
}))
|
||||
)
|
||||
showPopup(Menu, { actions }, getEventPositionElement(ev))
|
||||
showPopup(Menu, { actions }, getEventPositionElement(ev), () => {
|
||||
hovered = null
|
||||
})
|
||||
}
|
||||
|
||||
function getAttrType (type: Type<any>): IntlString | undefined {
|
||||
@ -179,104 +183,176 @@
|
||||
</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}
|
||||
{#if clazz}
|
||||
<div class="flex-row-center flex-no-shrink mb-6">
|
||||
<div class="hulyInput-body">
|
||||
<Label label={clazz.label} />
|
||||
</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>
|
||||
</div>
|
||||
{/if}
|
||||
{/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 class="hulyTableAttr-container">
|
||||
<div class="hulyTableAttr-header font-medium-12">
|
||||
<IconSettings size={'small'} />
|
||||
<span><Label label={settings.string.ClassProperties} /></span>
|
||||
<ButtonIcon kind={'primary'} icon={IconAdd} size={'small'} 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)
|
||||
}}
|
||||
>
|
||||
<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 -->
|
||||
{#if attributes.length}
|
||||
<div class="hulyTableAttr-content">
|
||||
{#each attributes as attr, i}
|
||||
{@const attrType = getAttrType(attr.type)}
|
||||
<!-- 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
|
||||
class="hulyTableAttr-content__row"
|
||||
class:hovered={hovered === i}
|
||||
on:contextmenu={(ev) => {
|
||||
ev.preventDefault()
|
||||
void showMenu(ev, 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={(ev) => showMenu(ev, attr, i)}>
|
||||
<IconMoreV2 size={'small'} />
|
||||
</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 attr.isCustom}
|
||||
<div class="hulyTableAttr-content__row-chip font-medium-12">
|
||||
<Label label={settings.string.Custom} />
|
||||
</div>
|
||||
{/if}
|
||||
{/await}
|
||||
{/if}
|
||||
</td>
|
||||
{#if attributeMapper}
|
||||
<td>
|
||||
<svelte:component this={attributeMapper.component} {...attributeMapper.props} attribute={attr} />
|
||||
</td>
|
||||
{/if}
|
||||
</tr>
|
||||
{/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}
|
||||
{#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>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.hulyInput-body {
|
||||
flex-grow: 1;
|
||||
flex-shrink: 0;
|
||||
padding: var(--spacing-1) var(--spacing-2);
|
||||
font-weight: 500;
|
||||
font-size: 1.5rem;
|
||||
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;
|
||||
|
||||
&.hovered,
|
||||
&:hover {
|
||||
background-color: var(--theme-table-header-color); // var(--global-surface-03-hover-BackgroundColor);
|
||||
}
|
||||
|
||||
&-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);
|
||||
}
|
||||
&-chip {
|
||||
padding: var(--spacing-0_25) var(--spacing-0_5);
|
||||
text-transform: uppercase;
|
||||
color: var(--global-tertiary-TextColor);
|
||||
background-color: var(--global-ui-BackgroundColor);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -0,0 +1,282 @@
|
||||
<!--
|
||||
// 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,
|
||||
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 {
|
||||
Action,
|
||||
ActionIcon,
|
||||
AnySvelteComponent,
|
||||
CircleButton,
|
||||
Icon,
|
||||
IconAdd,
|
||||
IconDelete,
|
||||
IconEdit,
|
||||
IconMoreV2,
|
||||
Label,
|
||||
Menu,
|
||||
getEventPositionElement,
|
||||
showPopup
|
||||
} from '@hcengineering/ui'
|
||||
import { getContextActions } from '@hcengineering/view-resources'
|
||||
import settings from '../plugin'
|
||||
import CreateAttribute from './CreateAttribute.svelte'
|
||||
import EditAttribute from './EditAttribute.svelte'
|
||||
import EditClassLabel from './EditClassLabel.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 attributeMapper:
|
||||
| {
|
||||
component: AnySvelteComponent
|
||||
label: IntlString
|
||||
props: Record<string, any>
|
||||
}
|
||||
| undefined = undefined
|
||||
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
|
||||
const classQuery = createQuery()
|
||||
|
||||
let clazz: Class<Doc> | undefined
|
||||
|
||||
$: classQuery.query(core.class.Class, { _id: _class }, (res) => {
|
||||
clazz = res.shift()
|
||||
})
|
||||
$: attributes = getCustomAttributes(_class)
|
||||
|
||||
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()
|
||||
)
|
||||
return attributes
|
||||
}
|
||||
|
||||
const attrQuery = createQuery()
|
||||
|
||||
$: attrQuery.query(core.class.Attribute, { attributeOf: _class }, () => {
|
||||
attributes = getCustomAttributes(_class)
|
||||
})
|
||||
|
||||
function update (): void {
|
||||
attributes = getCustomAttributes(_class)
|
||||
}
|
||||
|
||||
export function createAttribute (): void {
|
||||
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): Promise<void> {
|
||||
const exist = (await client.findOne(attribute.attributeOf, { [attribute.name]: { $exists: true } })) !== undefined
|
||||
|
||||
const actions: Action[] = [
|
||||
{
|
||||
label: presentation.string.Edit,
|
||||
icon: IconEdit,
|
||||
action: async () => {
|
||||
await editAttribute(attribute, exist)
|
||||
}
|
||||
}
|
||||
]
|
||||
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))
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
</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)
|
||||
}}
|
||||
>
|
||||
<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>
|
||||
{/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}
|
@ -15,15 +15,15 @@
|
||||
<script lang="ts">
|
||||
import { Class, ClassifierKind, Doc, Ref } from '@hcengineering/core'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { Icon, IconAdd, getEventPositionElement, showPopup } from '@hcengineering/ui'
|
||||
import { getEventPositionElement, showPopup, NavItem } from '@hcengineering/ui'
|
||||
import { ContextMenu } from '@hcengineering/view-resources'
|
||||
import ObjectPresenter from '@hcengineering/view-resources/src/components/ObjectPresenter.svelte'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import settings from '../plugin'
|
||||
|
||||
export let classes: Ref<Class<Doc>>[] = ['contact:class:Contact' as Ref<Class<Doc>>]
|
||||
export let _class: Ref<Class<Doc>> | undefined
|
||||
export let ofClass: Ref<Class<Doc>> | undefined
|
||||
export let level: number = 1
|
||||
|
||||
const client = getClient()
|
||||
const dispatch = createEventDispatcher()
|
||||
@ -58,35 +58,20 @@
|
||||
{#each classes as cl}
|
||||
{@const clazz = client.getHierarchy().getClass(cl)}
|
||||
{@const desc = getDescendants(cl)}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<div
|
||||
class="ac-column__list-item"
|
||||
class:ac-column__list-selected={cl === _class}
|
||||
<NavItem
|
||||
label={clazz.label}
|
||||
isFold
|
||||
empty
|
||||
{level}
|
||||
selected={cl === _class}
|
||||
on:click={() => {
|
||||
dispatch('select', cl)
|
||||
}}
|
||||
on:contextmenu|preventDefault|stopPropagation={(evt) => {
|
||||
on:contextmenu={(evt) => {
|
||||
showContextMenu(evt, clazz)
|
||||
}}
|
||||
>
|
||||
<div class="flex-row-center">
|
||||
{#if clazz.icon}
|
||||
<div class="mr-1 flex">
|
||||
<Icon icon={clazz.icon} size={'medium'} />
|
||||
{#if clazz.kind === ClassifierKind.MIXIN && client.getHierarchy().hasMixin(clazz, settings.mixin.UserMixin)}
|
||||
<Icon icon={IconAdd} size={'x-small'} fill={'var(--theme-dark-color)'} />
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
<span class="overflow-label caption-color">
|
||||
<ObjectPresenter _class={clazz._class} objectId={clazz._id} value={clazz} />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
/>
|
||||
{#if desc.length}
|
||||
<div class="ml-8 mt-3 mb-3">
|
||||
<svelte:self classes={desc} {_class} on:select />
|
||||
</div>
|
||||
<svelte:self classes={desc} {_class} level={level + 1} on:select />
|
||||
{/if}
|
||||
{/each}
|
||||
|
@ -13,10 +13,25 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import core, { Class, Doc, Obj, Ref } from '@hcengineering/core'
|
||||
import { IntlString } from '@hcengineering/platform'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { AnySvelteComponent, Icon, Label, getLocation, navigate } from '@hcengineering/ui'
|
||||
import {
|
||||
AnySvelteComponent,
|
||||
Scroller,
|
||||
ButtonIcon,
|
||||
IconDescription,
|
||||
Label,
|
||||
getLocation,
|
||||
navigate,
|
||||
Header,
|
||||
Breadcrumb,
|
||||
defineSeparators,
|
||||
settingsSeparators,
|
||||
Separator,
|
||||
NavGroup
|
||||
} from '@hcengineering/ui'
|
||||
import setting from '../plugin'
|
||||
import { filterDescendants } from '../utils'
|
||||
import ClassAttributes from './ClassAttributes.svelte'
|
||||
@ -32,6 +47,9 @@
|
||||
| undefined = undefined
|
||||
export let withoutHeader = false
|
||||
export let useOfClassAttributes = true
|
||||
export let visibleNav: boolean = true
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
const loc = getLocation()
|
||||
const client = getClient()
|
||||
@ -80,36 +98,47 @@
|
||||
$: if (ofClass !== undefined && _class !== undefined && !client.getHierarchy().isDerived(_class, ofClass)) {
|
||||
_class = ofClass
|
||||
}
|
||||
defineSeparators('workspaceSettings', settingsSeparators)
|
||||
</script>
|
||||
|
||||
<div class="antiComponent">
|
||||
<div class="hulyComponent">
|
||||
{#if !withoutHeader}
|
||||
<div class="ac-header short divide">
|
||||
<div class="ac-header__icon"><Icon icon={setting.icon.Clazz} size={'medium'} /></div>
|
||||
<div class="ac-header__title"><Label label={setting.string.ClassSetting} /></div>
|
||||
</div>
|
||||
<Header minimize={!visibleNav} on:resize={(event) => dispatch('change', event.detail)}>
|
||||
<Breadcrumb icon={setting.icon.Clazz} label={setting.string.ClassSetting} size={'large'} isCurrent />
|
||||
</Header>
|
||||
{/if}
|
||||
<div class="ac-body columns hScroll">
|
||||
<div class="ac-column">
|
||||
<div class="overflow-y-auto">
|
||||
<ClassHierarchy
|
||||
{classes}
|
||||
{_class}
|
||||
{ofClass}
|
||||
on:select={(e) => {
|
||||
_class = e.detail
|
||||
}}
|
||||
/>
|
||||
<div class="hulyComponent-content__container columns">
|
||||
<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 class="hulyComponent-content__navHeader-hint paragraph-regular-14">
|
||||
<Label label={setting.string.ClassSettingHint} />
|
||||
</div>
|
||||
</div>
|
||||
<Scroller>
|
||||
<NavGroup label={setting.string.Classes} selected={_class !== undefined} categoryName={'classes'} second>
|
||||
<ClassHierarchy
|
||||
{classes}
|
||||
{_class}
|
||||
{ofClass}
|
||||
on:select={(e) => {
|
||||
_class = e.detail
|
||||
}}
|
||||
/>
|
||||
</NavGroup>
|
||||
</Scroller>
|
||||
</div>
|
||||
<div class="ac-column max">
|
||||
{#if _class !== undefined}
|
||||
<table class="antiTable">
|
||||
<tbody>
|
||||
<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} />
|
||||
</tbody>
|
||||
</table>
|
||||
{/if}
|
||||
</Scroller>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -13,11 +13,16 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { PluginConfiguration } from '@hcengineering/core'
|
||||
import { configurationStore, getClient } from '@hcengineering/presentation'
|
||||
import { Button, Icon, IconInfo, Label, Scroller } from '@hcengineering/ui'
|
||||
import { Button, Icon, IconInfo, Label, Header, Breadcrumb } from '@hcengineering/ui'
|
||||
import setting from '../plugin'
|
||||
|
||||
export let visibleNav: boolean = true
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
const client = getClient()
|
||||
|
||||
async function change (config: PluginConfiguration, value: boolean): Promise<void> {
|
||||
@ -27,13 +32,12 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="antiComponent">
|
||||
<div class="ac-header short divide">
|
||||
<div class="ac-header__icon"><Icon icon={setting.icon.Setting} size={'medium'} /></div>
|
||||
<div class="ac-header__title"><Label label={setting.string.Configuration} /></div>
|
||||
</div>
|
||||
<Scroller>
|
||||
<div class="flex-row-center flex-wrap p-1 gap-around-4">
|
||||
<div class="hulyComponent">
|
||||
<Header minimize={!visibleNav} on:resize={(event) => dispatch('change', event.detail)}>
|
||||
<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}>
|
||||
@ -66,7 +70,7 @@
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
</Scroller>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
|
@ -13,26 +13,36 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import core, { Enum } from '@hcengineering/core'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import {
|
||||
CircleButton,
|
||||
EditBox,
|
||||
Icon,
|
||||
eventToHTMLElement,
|
||||
IconAdd,
|
||||
IconMoreH,
|
||||
Label,
|
||||
showPopup
|
||||
showPopup,
|
||||
Header,
|
||||
Breadcrumb,
|
||||
defineSeparators,
|
||||
settingsSeparators,
|
||||
Separator
|
||||
} from '@hcengineering/ui'
|
||||
import { ContextMenu } from '@hcengineering/view-resources'
|
||||
import setting from '../plugin'
|
||||
import EnumValues from './EnumValues.svelte'
|
||||
|
||||
export let visibleNav: boolean = true
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
const query = createQuery()
|
||||
|
||||
let enums: Enum[] = []
|
||||
let selected: Enum | undefined
|
||||
let hovered: number | null = null
|
||||
const client = getClient()
|
||||
|
||||
query.query(core.class.Enum, {}, (res) => {
|
||||
@ -51,25 +61,26 @@
|
||||
name: value.name
|
||||
})
|
||||
}
|
||||
defineSeparators('workspaceSettings', settingsSeparators)
|
||||
</script>
|
||||
|
||||
<div class="antiComponent">
|
||||
<div class="ac-header short divide">
|
||||
<div class="ac-header__icon"><Icon icon={setting.icon.Enums} size={'medium'} /></div>
|
||||
<div class="ac-header__title"><Label label={setting.string.Enums} /></div>
|
||||
</div>
|
||||
<div class="ac-body columns hScroll">
|
||||
<div class="ac-column">
|
||||
<div class="flex-between trans-title mb-3">
|
||||
<div class="hulyComponent">
|
||||
<Header minimize={!visibleNav} on:resize={(event) => dispatch('change', event.detail)}>
|
||||
<Breadcrumb icon={setting.icon.Enums} label={setting.string.Enums} size={'large'} isCurrent />
|
||||
</Header>
|
||||
<div class="hulyComponent-content__container columns">
|
||||
<div class="hulyComponent-content__column">
|
||||
<div class="flex-between trans-title m-3">
|
||||
<Label label={setting.string.Enums} />
|
||||
<CircleButton icon={IconAdd} size="medium" on:click={create} />
|
||||
</div>
|
||||
<div class="overflow-y-auto">
|
||||
{#each enums as value}
|
||||
{#each enums as value, i}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<div
|
||||
class="ac-column__list-item"
|
||||
class="enum__list-item"
|
||||
class:hovered={hovered === i}
|
||||
class:selected={selected === value}
|
||||
on:click={() => {
|
||||
selected = value
|
||||
@ -79,7 +90,10 @@
|
||||
<div
|
||||
class="hover-trans"
|
||||
on:click|stopPropagation={(ev) => {
|
||||
showPopup(ContextMenu, { object: value }, eventToHTMLElement(ev), () => {})
|
||||
hovered = i
|
||||
showPopup(ContextMenu, { object: value }, eventToHTMLElement(ev), () => {
|
||||
hovered = null
|
||||
})
|
||||
}}
|
||||
>
|
||||
<IconMoreH size={'medium'} />
|
||||
@ -88,10 +102,37 @@
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
<div class="ac-column max">
|
||||
{#if selected !== undefined}
|
||||
<EnumValues value={selected} />
|
||||
{/if}
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.enum__list-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
min-height: 2.5rem;
|
||||
margin: 0 0.75rem;
|
||||
padding: 0 1.25rem;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 12px;
|
||||
cursor: pointer;
|
||||
|
||||
&.hovered,
|
||||
&:hover {
|
||||
background-color: var(--theme-button-hovered);
|
||||
}
|
||||
&.selected {
|
||||
background-color: var(--theme-button-default);
|
||||
border-color: var(--theme-button-border);
|
||||
cursor: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -13,13 +13,18 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { Ref, getCurrentAccount } from '@hcengineering/core'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import type { Integration, IntegrationType } from '@hcengineering/setting'
|
||||
import setting from '@hcengineering/setting'
|
||||
import { Icon, Label } from '@hcengineering/ui'
|
||||
import { Header, Breadcrumb } from '@hcengineering/ui'
|
||||
import PluginCard from './PluginCard.svelte'
|
||||
|
||||
export let visibleNav: boolean = true
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
const accountId = getCurrentAccount()._id
|
||||
const typeQuery = createQuery()
|
||||
const integrationQuery = createQuery()
|
||||
@ -39,11 +44,10 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="antiComponent">
|
||||
<div class="ac-header short divide">
|
||||
<div class="ac-header__icon"><Icon icon={setting.icon.Integrations} size={'medium'} /></div>
|
||||
<div class="ac-header__title"><Label label={setting.string.Integrations} /></div>
|
||||
</div>
|
||||
<div class="hulyComponent">
|
||||
<Header minimize={!visibleNav} on:resize={(event) => dispatch('change', event.detail)}>
|
||||
<Breadcrumb icon={setting.icon.Integrations} label={setting.string.Integrations} size={'large'} isCurrent />
|
||||
</Header>
|
||||
<div class="ac-body__cards-container">
|
||||
{#each integrationTypes as integrationType (integrationType._id)}
|
||||
{#if integrationType.allowMultiple}
|
||||
|
@ -13,10 +13,15 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import login from '@hcengineering/login'
|
||||
import presentation, { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import setting, { InviteSettings } from '@hcengineering/setting'
|
||||
import { Button, EditBox, MiniToggle } from '@hcengineering/ui'
|
||||
import { Button, EditBox, MiniToggle, Header, Breadcrumb } from '@hcengineering/ui'
|
||||
|
||||
export let visibleNav: boolean = true
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
const client = getClient()
|
||||
let expTime: number = 48
|
||||
@ -55,23 +60,28 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="form">
|
||||
<div class="mt-2">
|
||||
<EditBox label={login.string.LinkValidHours} format={'number'} bind:value={expTime} />
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<EditBox label={login.string.EmailMask} bind:value={mask} />
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<MiniToggle bind:on={noLimit} label={login.string.NoLimit} on:change={() => noLimit && (limit = -1)} />
|
||||
</div>
|
||||
{#if !noLimit}
|
||||
<div class="hulyComponent">
|
||||
<Header minimize={!visibleNav} on:resize={(event) => dispatch('change', event.detail)}>
|
||||
<Breadcrumb icon={setting.icon.InviteSettings} label={setting.string.InviteSettings} size={'large'} isCurrent />
|
||||
</Header>
|
||||
<div class="form">
|
||||
<div class="mt-2">
|
||||
<EditBox label={login.string.InviteLimit} format={'number'} bind:value={limit} />
|
||||
<EditBox label={login.string.LinkValidHours} format={'number'} bind:value={expTime} />
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<EditBox label={login.string.EmailMask} bind:value={mask} />
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<MiniToggle bind:on={noLimit} label={login.string.NoLimit} on:change={() => noLimit && (limit = -1)} />
|
||||
</div>
|
||||
{#if !noLimit}
|
||||
<div class="mt-2">
|
||||
<EditBox label={login.string.InviteLimit} format={'number'} bind:value={limit} />
|
||||
</div>
|
||||
{/if}
|
||||
<div class="mt-2">
|
||||
<Button label={presentation.string.Save} size={'medium'} kind={'primary'} on:click={() => setInviteSettings()} />
|
||||
</div>
|
||||
{/if}
|
||||
<div class="mt-2">
|
||||
<Button label={presentation.string.Save} size={'medium'} kind={'primary'} on:click={() => setInviteSettings()} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -13,13 +13,18 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import contact, { PersonAccount } from '@hcengineering/contact'
|
||||
import { EmployeePresenter, personByIdStore } from '@hcengineering/contact-resources'
|
||||
import { AccountRole, SortingOrder, getCurrentAccount } from '@hcengineering/core'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { DropdownIntlItem, DropdownLabelsIntl, EditBox, Icon, Label } from '@hcengineering/ui'
|
||||
import presentation, { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { DropdownIntlItem, DropdownLabelsIntl, EditBox, Header, Breadcrumb } from '@hcengineering/ui'
|
||||
import setting from '../plugin'
|
||||
|
||||
export let visibleNav: boolean = true
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
const client = getClient()
|
||||
|
||||
const query = createQuery()
|
||||
@ -54,14 +59,13 @@
|
||||
let search = ''
|
||||
</script>
|
||||
|
||||
<div class="antiComponent">
|
||||
<div class="ac-header short divide">
|
||||
<div class="ac-header__icon"><Icon icon={setting.icon.Password} size={'medium'} /></div>
|
||||
<div class="ac-header__title"><Label label={setting.string.Owners} /></div>
|
||||
<EditBox kind={'search-style'} focusIndex={1} bind:value={search} />
|
||||
</div>
|
||||
<div class="ac-body columns">
|
||||
<div class="ac-column max">
|
||||
<div class="hulyComponent">
|
||||
<Header minimize={!visibleNav} on:resize={(event) => dispatch('change', event.detail)}>
|
||||
<Breadcrumb icon={setting.icon.Owners} label={setting.string.Owners} size={'large'} isCurrent />
|
||||
<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)}
|
||||
|
@ -13,14 +13,19 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import setting from '@hcengineering/setting'
|
||||
import presentation from '@hcengineering/presentation'
|
||||
import { Button, EditBox, Icon, Label } from '@hcengineering/ui'
|
||||
import { Button, EditBox, Icon, Label, Header, Breadcrumb } from '@hcengineering/ui'
|
||||
import login from '@hcengineering/login'
|
||||
import Error from './icons/Error.svelte'
|
||||
import plugin from '../plugin'
|
||||
import { getResource } from '@hcengineering/platform'
|
||||
|
||||
export let visibleNav: boolean = true
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
let oldPassword: string = ''
|
||||
let password: string = ''
|
||||
let password2: string = ''
|
||||
@ -54,11 +59,10 @@
|
||||
$: updateSaved(oldPassword, password, password2)
|
||||
</script>
|
||||
|
||||
<div class="antiComponent">
|
||||
<div class="ac-header short divide">
|
||||
<div class="ac-header__icon"><Icon icon={setting.icon.Password} size={'medium'} /></div>
|
||||
<div class="ac-header__title"><Label label={setting.string.ChangePassword} /></div>
|
||||
</div>
|
||||
<div class="hulyComponent">
|
||||
<Header minimize={!visibleNav} on:resize={(event) => dispatch('change', event.detail)}>
|
||||
<Breadcrumb icon={setting.icon.Password} label={setting.string.ChangePassword} size={'large'} isCurrent />
|
||||
</Header>
|
||||
<div class="flex-row-stretch flex-grow p-10">
|
||||
<div class="flex-grow flex-col">
|
||||
{#if error}
|
||||
|
@ -13,16 +13,20 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher, onDestroy } from 'svelte'
|
||||
import contact, { Employee, PersonAccount, combineName, getFirstName, getLastName } from '@hcengineering/contact'
|
||||
import { ChannelsEditor, EditableAvatar, employeeByIdStore } from '@hcengineering/contact-resources'
|
||||
import { Ref, getCurrentAccount } from '@hcengineering/core'
|
||||
import login from '@hcengineering/login'
|
||||
import { getResource } from '@hcengineering/platform'
|
||||
import { AttributeEditor, getClient, MessageBox } from '@hcengineering/presentation'
|
||||
import { Button, createFocusManager, EditBox, FocusHandler, Icon, Label, showPopup } from '@hcengineering/ui'
|
||||
import { onDestroy } from 'svelte'
|
||||
import { Button, createFocusManager, EditBox, FocusHandler, showPopup, Header, Breadcrumb } from '@hcengineering/ui'
|
||||
import setting from '../plugin'
|
||||
|
||||
export let visibleNav: boolean = true
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
const client = getClient()
|
||||
|
||||
let avatarEditor: EditableAvatar
|
||||
@ -84,11 +88,10 @@
|
||||
|
||||
<FocusHandler {manager} />
|
||||
|
||||
<div class="antiComponent">
|
||||
<div class="ac-header short divide">
|
||||
<div class="ac-header__icon"><Icon icon={setting.icon.AccountSettings} size={'medium'} /></div>
|
||||
<div class="ac-header__title"><Label label={setting.string.AccountSettings} /></div>
|
||||
</div>
|
||||
<div class="hulyComponent">
|
||||
<Header minimize={!visibleNav} on:resize={(event) => dispatch('change', event.detail)}>
|
||||
<Breadcrumb icon={setting.icon.AccountSettings} label={setting.string.AccountSettings} size={'large'} isCurrent />
|
||||
</Header>
|
||||
<div class="ac-body p-10">
|
||||
{#if employee}
|
||||
<div class="flex flex-grow w-full">
|
||||
|
@ -31,12 +31,12 @@
|
||||
resolvedLocationStore,
|
||||
setMetadataLocalStorage,
|
||||
showPopup,
|
||||
Label
|
||||
Label,
|
||||
NavItem,
|
||||
NavGroup
|
||||
} from '@hcengineering/ui'
|
||||
import { NavFooter } from '@hcengineering/workbench-resources'
|
||||
import { onDestroy } from 'svelte'
|
||||
import NavItem from './NavItem.svelte'
|
||||
import NavGroup from './NavGroup.svelte'
|
||||
|
||||
export let visibleNav: boolean = true
|
||||
export let navFloat: boolean = false
|
||||
@ -105,9 +105,12 @@
|
||||
defineSeparators('setting', settingsSeparators)
|
||||
</script>
|
||||
|
||||
<div class="flex h-full clear-mins">
|
||||
<div class="hulyPanels-container">
|
||||
{#if visibleNav}
|
||||
<div class="antiPanel-navigator {appsDirection === 'horizontal' ? 'portrait' : 'landscape'}">
|
||||
<div
|
||||
class="antiPanel-navigator {appsDirection === 'horizontal' ? 'portrait' : 'landscape'} border-left"
|
||||
class:border-right={category?.component === undefined}
|
||||
>
|
||||
<div class="antiPanel-wrap__content hulyNavPanel-container">
|
||||
<div class="hulyNavPanel-header">
|
||||
<Label label={setting.string.Settings} />
|
||||
@ -157,29 +160,40 @@
|
||||
<NavItem icon={setting.icon.Signout} label={setting.string.Signout} on:click={signOut} />
|
||||
</NavFooter>
|
||||
</div>
|
||||
<Separator
|
||||
name={'setting'}
|
||||
float={navFloat ? 'navigator' : true}
|
||||
index={0}
|
||||
color={'var(--theme-navpanel-border)'}
|
||||
/>
|
||||
<Separator name={'setting'} float={navFloat ? 'navigator' : true} index={0} color={'transparent'} />
|
||||
</div>
|
||||
<Separator name={'setting'} float={navFloat} index={0} color={'var(--theme-navpanel-border)'} />
|
||||
<Separator name={'setting'} float={navFloat} index={0} color={'transparent'} />
|
||||
{/if}
|
||||
|
||||
<div class="antiPanel-component filled">
|
||||
<div class="antiPanel-component filledNav">
|
||||
{#if category}
|
||||
<Component
|
||||
is={category.component}
|
||||
props={{
|
||||
kind: 'content',
|
||||
visibleNav
|
||||
}}
|
||||
on:change={(event) => (visibleNav = event.detail)}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.hulyPanels-container {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
background-color: var(--theme-navpanel-color); // var(--global-surface-01-BackgroundColor);
|
||||
|
||||
// .antiPanel-navigator {
|
||||
// background-color: transparent;
|
||||
// }
|
||||
.antiPanel-component {
|
||||
border-radius: var(--small-focus-BorderRadius);
|
||||
}
|
||||
}
|
||||
.hulyNavPanel-container :global(.hulyNavItem-container),
|
||||
.hulyNavPanel-container :global(.hulyTaskNavLink-container) {
|
||||
margin: 0 0.75rem;
|
||||
|
@ -17,12 +17,19 @@
|
||||
import { AccountRole, getCurrentAccount } from '@hcengineering/core'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import setting, { SettingsCategory } from '@hcengineering/setting'
|
||||
import { Component, Location, getCurrentResolvedLocation, navigate, resolvedLocationStore } from '@hcengineering/ui'
|
||||
import {
|
||||
Component,
|
||||
Location,
|
||||
getCurrentResolvedLocation,
|
||||
navigate,
|
||||
resolvedLocationStore,
|
||||
NavItem
|
||||
} from '@hcengineering/ui'
|
||||
import { onDestroy } from 'svelte'
|
||||
import NavItem from './NavItem.svelte'
|
||||
|
||||
export let kind: 'navigation' | undefined
|
||||
export let kind: 'navigation' | 'content' | undefined
|
||||
export let categoryName: string
|
||||
export let visibleNav: boolean = true
|
||||
|
||||
let category: SettingsCategory | undefined
|
||||
let categoryId: string = ''
|
||||
@ -81,6 +88,8 @@
|
||||
<Component is={category.extraComponents?.navigation} props={{ kind: 'navigation', categoryName: categoryId }} />
|
||||
{/if}
|
||||
{/each}
|
||||
{:else if kind === 'content' && !category}
|
||||
<div class="hulyComponent" />
|
||||
{:else if category}
|
||||
<Component is={category.component} props={{ kind: 'content' }} />
|
||||
<Component is={category.component} props={{ kind: 'content', visibleNav }} on:change />
|
||||
{/if}
|
||||
|
@ -47,8 +47,9 @@ import setting from './plugin'
|
||||
import IntegrationPanel from './components/IntegrationPanel.svelte'
|
||||
import { getOwnerFirstName, getOwnerLastName, getOwnerPosition, getValue, filterDescendants } from './utils'
|
||||
import ClassAttributes from './components/ClassAttributes.svelte'
|
||||
import ClassAttributesList from './components/ClassAttributesList.svelte'
|
||||
|
||||
export { ClassSetting, filterDescendants, ClassAttributes }
|
||||
export { ClassSetting, filterDescendants, ClassAttributes, ClassAttributesList }
|
||||
|
||||
async function DeleteMixin (object: Mixin<Class<Doc>>): Promise<void> {
|
||||
const docs = await getClient().findAll(object._id, {}, { limit: 1 })
|
||||
|
@ -66,7 +66,6 @@ export default mergeIds(settingId, setting, {
|
||||
ShowAttribute: '' as IntlString,
|
||||
Visibility: '' as IntlString,
|
||||
Hidden: '' as IntlString,
|
||||
InviteSettings: '' as IntlString,
|
||||
DefaultValue: '' as IntlString,
|
||||
SelectAValue: '' as IntlString,
|
||||
DateOnly: '' as IntlString,
|
||||
@ -77,6 +76,8 @@ export default mergeIds(settingId, setting, {
|
||||
ConfigurationDisabled: '' as IntlString,
|
||||
ConfigDisable: '' as IntlString,
|
||||
ConfigEnable: '' as IntlString,
|
||||
ConfigBeta: '' as IntlString
|
||||
ConfigBeta: '' as IntlString,
|
||||
ClassSettingHint: '' as IntlString,
|
||||
ClassProperties: '' as IntlString
|
||||
}
|
||||
})
|
||||
|
@ -168,7 +168,8 @@ export default plugin(settingId, {
|
||||
ClassSetting: '' as IntlString,
|
||||
Classes: '' as IntlString,
|
||||
Owners: '' as IntlString,
|
||||
Configure: '' as IntlString
|
||||
Configure: '' as IntlString,
|
||||
InviteSettings: '' as IntlString
|
||||
},
|
||||
icon: {
|
||||
AccountSettings: '' as Asset,
|
||||
|
@ -22,6 +22,8 @@
|
||||
import { typeStore } from '../../'
|
||||
import ProjectEditor from './ProjectEditor.svelte'
|
||||
|
||||
export let visibleNav: boolean = true
|
||||
|
||||
let type: WithLookup<ProjectType> | undefined
|
||||
let typeId: Ref<ProjectType> | undefined
|
||||
|
||||
@ -36,8 +38,8 @@
|
||||
$: type = typeId !== undefined ? $typeStore.get(typeId) : undefined
|
||||
</script>
|
||||
|
||||
<div class="p-1 w-full h-full">
|
||||
<div class="hulyComponent">
|
||||
{#if type !== undefined}
|
||||
<ProjectEditor {type} descriptor={type.$lookup?.descriptor} />
|
||||
<ProjectEditor {type} descriptor={type.$lookup?.descriptor} {visibleNav} on:change />
|
||||
{/if}
|
||||
</div>
|
||||
|
@ -14,6 +14,7 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher, onDestroy } from 'svelte'
|
||||
import { ComponentExtensions, createQuery, getClient } from '@hcengineering/presentation'
|
||||
import task, { Project, ProjectType, ProjectTypeDescriptor, Task, TaskType } from '@hcengineering/task'
|
||||
|
||||
@ -21,6 +22,7 @@
|
||||
import { getEmbeddedLabel } from '@hcengineering/platform'
|
||||
import {
|
||||
Button,
|
||||
ButtonIcon,
|
||||
Component,
|
||||
EditBox,
|
||||
Icon,
|
||||
@ -31,15 +33,15 @@
|
||||
IconMoreV,
|
||||
Label,
|
||||
Location,
|
||||
Scroller,
|
||||
eventToHTMLElement,
|
||||
getCurrentResolvedLocation,
|
||||
navigate,
|
||||
resolvedLocationStore,
|
||||
showPopup
|
||||
showPopup,
|
||||
Header,
|
||||
Breadcrumbs
|
||||
} from '@hcengineering/ui'
|
||||
import { ContextMenu } from '@hcengineering/view-resources'
|
||||
import { onDestroy } from 'svelte'
|
||||
import plugin from '../../plugin'
|
||||
import CreateTaskType from '../taskTypes/CreateTaskType.svelte'
|
||||
import TaskTypeEditor from '../taskTypes/TaskTypeEditor.svelte'
|
||||
@ -49,6 +51,9 @@
|
||||
|
||||
export let type: ProjectType
|
||||
export let descriptor: ProjectTypeDescriptor | undefined
|
||||
export let visibleNav: boolean = true
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
const client = getClient()
|
||||
const query = createQuery()
|
||||
@ -141,173 +146,160 @@
|
||||
selectedTaskTypeId = id as Ref<TaskType>
|
||||
navigate(loc)
|
||||
}
|
||||
$: items =
|
||||
selectedTaskType !== undefined
|
||||
? [{ label: plugin.string.ProjectType }, { title: selectedTaskType.name }]
|
||||
: [{ label: plugin.string.ProjectType }]
|
||||
</script>
|
||||
|
||||
<div class="h-full flex-col w-full">
|
||||
{#if type !== undefined && descriptor !== undefined}
|
||||
<div class="p-2 bottom-divider flex-row-center">
|
||||
<div class="button-group flex-row-center right-divider pr-2">
|
||||
<Button
|
||||
icon={IconDelete}
|
||||
kind={'regular'}
|
||||
on:click={(ev) => {
|
||||
// Ask for delete
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
icon={IconCopy}
|
||||
kind={'regular'}
|
||||
on:click={(ev) => {
|
||||
// Do copy of type
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
icon={IconMoreV}
|
||||
kind={'regular'}
|
||||
on:click={(ev) => {
|
||||
showPopup(ContextMenu, { object: type }, eventToHTMLElement(ev), () => {})
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<div class="fs-title ml-2 flex-row-center">
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
class="hover-trans"
|
||||
on:click={() => {
|
||||
selectTaskType(undefined)
|
||||
}}
|
||||
>
|
||||
<Label label={plugin.string.ProjectType} />
|
||||
</div>
|
||||
{#if selectedTaskType !== undefined}
|
||||
<span class="p-1">/</span>
|
||||
{selectedTaskType.name}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<div class="h-full flex-row-top w-full justify-center">
|
||||
<div class="h-full editorBox flex-col">
|
||||
<Scroller padding={'0 1rem'} noStretch shrink>
|
||||
{#if selectedTaskType === undefined}
|
||||
<!-- <div class="flex-col">Navigation</div> -->
|
||||
<div class="flex-grow h-full">
|
||||
<div class="p-4 flex-col">
|
||||
{#if type !== undefined && descriptor !== undefined}
|
||||
<Header minimize={!visibleNav} on:resize={(event) => dispatch('change', event.detail)}>
|
||||
<ButtonIcon
|
||||
icon={IconCopy}
|
||||
size={'small'}
|
||||
kind={'secondary'}
|
||||
on:click={(ev) => {
|
||||
// Do copy of type
|
||||
}}
|
||||
/>
|
||||
<ButtonIcon
|
||||
icon={IconDelete}
|
||||
size={'small'}
|
||||
kind={'secondary'}
|
||||
on:click={(ev) => {
|
||||
// Ask for delete
|
||||
}}
|
||||
/>
|
||||
<ButtonIcon
|
||||
icon={IconMoreV}
|
||||
size={'small'}
|
||||
kind={'secondary'}
|
||||
on:click={(ev) => {
|
||||
showPopup(ContextMenu, { object: type }, eventToHTMLElement(ev), () => {})
|
||||
}}
|
||||
/>
|
||||
<Breadcrumbs
|
||||
{items}
|
||||
size={'large'}
|
||||
selected={selectedTaskType !== undefined ? 1 : 0}
|
||||
on:select={(event) => {
|
||||
if (event.detail === 0) selectTaskType(undefined)
|
||||
}}
|
||||
/>
|
||||
</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>
|
||||
<div class="flex-row-center flex-between mb-2">
|
||||
<div>
|
||||
<Component is={descriptor.icon} props={{ size: 'large' }} />
|
||||
</div>
|
||||
<div class="flex-row-center no-word-wrap">
|
||||
<Icon icon={IconFile} size={'small'} />
|
||||
{projects.length} projects
|
||||
</div>
|
||||
</div>
|
||||
<EditBox
|
||||
kind={'large-style'}
|
||||
value={type?.name ?? ''}
|
||||
on:blur={(evt) => {
|
||||
if (type !== undefined) {
|
||||
void client.diffUpdate(type, { name: evt.detail })
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<div class="p-2">
|
||||
<EditBox
|
||||
placeholder={getEmbeddedLabel('Description')}
|
||||
kind={'small-style'}
|
||||
bind:value={type.shortDescription}
|
||||
on:change={() => onShortDescriptionChange(type?.shortDescription ?? '')}
|
||||
/>
|
||||
</div>
|
||||
{#if descriptor?.editor}
|
||||
<Component is={descriptor.editor} props={{ type }} />
|
||||
{/if}
|
||||
<Component is={descriptor.icon} props={{ size: 'large' }} />
|
||||
</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}
|
||||
</div>
|
||||
</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>
|
||||
</div>
|
||||
|
||||
<div class="panelBox flex-col row">
|
||||
<div class="mt-1">
|
||||
<TypeClassEditor ofClass={descriptor.baseClass} _class={type.targetClass} />
|
||||
</div>
|
||||
<div class="flex-row-center no-word-wrap">
|
||||
<Icon icon={IconFile} size={'small'} />
|
||||
{projects.length} projects
|
||||
</div>
|
||||
</div>
|
||||
<EditBox
|
||||
kind={'large-style'}
|
||||
value={type?.name ?? ''}
|
||||
on:blur={(evt) => {
|
||||
if (type !== undefined) {
|
||||
void client.diffUpdate(type, { name: evt.detail })
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<div class="p-2">
|
||||
<EditBox
|
||||
placeholder={getEmbeddedLabel('Description')}
|
||||
kind={'small-style'}
|
||||
bind:value={type.shortDescription}
|
||||
on:change={() => onShortDescriptionChange(type?.shortDescription ?? '')}
|
||||
/>
|
||||
</div>
|
||||
{#if descriptor?.editor}
|
||||
<Component is={descriptor.editor} props={{ type }} />
|
||||
{/if}
|
||||
</div>
|
||||
{:else}
|
||||
<TaskTypeEditor
|
||||
taskType={selectedTaskType}
|
||||
projectType={type}
|
||||
{taskTypes}
|
||||
{taskTypeCounter}
|
||||
{statusCounter}
|
||||
/>
|
||||
{/if}
|
||||
</Scroller>
|
||||
</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}
|
||||
</div>
|
||||
</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>
|
||||
</div>
|
||||
|
||||
<div class="panelBox flex-col row">
|
||||
<div class="mt-1">
|
||||
<TypeClassEditor ofClass={descriptor.baseClass} _class={type.targetClass} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<TaskTypeEditor taskType={selectedTaskType} projectType={type} {taskTypes} {taskTypeCounter} {statusCounter} />
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
.row {
|
||||
|
@ -15,7 +15,7 @@
|
||||
<script lang="ts">
|
||||
import core, { Class, Doc, Obj, Ref } from '@hcengineering/core'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { ClassAttributes } from '@hcengineering/setting-resources'
|
||||
import { ClassAttributesList } from '@hcengineering/setting-resources'
|
||||
import { Button, Icon, IconAdd } from '@hcengineering/ui'
|
||||
import { ObjectPresenter } from '@hcengineering/view-resources'
|
||||
|
||||
@ -41,7 +41,7 @@
|
||||
})
|
||||
$: clazz = client.getHierarchy().getClass(_class)
|
||||
|
||||
let mainAttributes: ClassAttributes
|
||||
let mainAttributes: ClassAttributesList
|
||||
</script>
|
||||
|
||||
<div class="flex flex-between mb-4">
|
||||
@ -60,7 +60,7 @@
|
||||
<div class="ml-2 mr-2">
|
||||
<table class="antiTable mx-2">
|
||||
<tbody>
|
||||
<ClassAttributes
|
||||
<ClassAttributesList
|
||||
bind:this={mainAttributes}
|
||||
{_class}
|
||||
{ofClass}
|
||||
@ -69,7 +69,7 @@
|
||||
showCreate={false}
|
||||
/>
|
||||
{#each classes as clazz2}
|
||||
<ClassAttributes
|
||||
<ClassAttributesList
|
||||
_class={clazz2._id}
|
||||
{ofClass}
|
||||
useOfClassAttributes={false}
|
||||
|
@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import core, { Data, Ref } from '@hcengineering/core'
|
||||
import { getEmbeddedLabel, getResource } from '@hcengineering/platform'
|
||||
import { createQuery, getClient, MessageViewer, SpaceSelector } from '@hcengineering/presentation'
|
||||
@ -9,17 +10,25 @@
|
||||
Button,
|
||||
EditBox,
|
||||
eventToHTMLElement,
|
||||
Icon,
|
||||
IconAdd,
|
||||
IconEdit,
|
||||
Label,
|
||||
showPopup
|
||||
showPopup,
|
||||
Header,
|
||||
Breadcrumb,
|
||||
Separator,
|
||||
defineSeparators,
|
||||
settingsSeparators
|
||||
} from '@hcengineering/ui'
|
||||
import { getActions as getContributedActions, TreeItem, TreeNode } from '@hcengineering/view-resources'
|
||||
import templatesPlugin from '../plugin'
|
||||
import CreateTemplateCategory from './CreateTemplateCategory.svelte'
|
||||
import FieldPopup from './FieldPopup.svelte'
|
||||
|
||||
export let visibleNav: boolean = true
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
const client = getClient()
|
||||
const query = createQuery()
|
||||
const spaceQ = createQuery()
|
||||
@ -138,17 +147,22 @@
|
||||
}
|
||||
|
||||
let space: Ref<TemplateCategory> | undefined = undefined
|
||||
defineSeparators('workspaceSettings', settingsSeparators)
|
||||
</script>
|
||||
|
||||
<div class="antiComponent">
|
||||
<div class="ac-header short divide">
|
||||
<div class="ac-header__icon"><Icon icon={templatesPlugin.icon.Templates} size={'medium'} /></div>
|
||||
<div class="ac-header__title"><Label label={templatesPlugin.string.Templates} /></div>
|
||||
</div>
|
||||
<div class="hulyComponent">
|
||||
<Header minimize={!visibleNav} on:resize={(event) => dispatch('change', event.detail)}>
|
||||
<Breadcrumb
|
||||
icon={templatesPlugin.icon.Templates}
|
||||
label={templatesPlugin.string.Templates}
|
||||
size={'large'}
|
||||
isCurrent
|
||||
/>
|
||||
</Header>
|
||||
|
||||
<div class="ac-body columns clear-mins">
|
||||
<div class="ac-column">
|
||||
<div id="create-template" class="flex-between trans-title mb-3">
|
||||
<div class="hulyComponent-content__container columns">
|
||||
<div class="hulyComponent-content__column">
|
||||
<div id="create-template" class="flex-between trans-title m-3">
|
||||
<Button
|
||||
icon={templatesPlugin.icon.Template}
|
||||
label={templatesPlugin.string.CreateTemplate}
|
||||
@ -179,79 +193,81 @@
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ac-column max template-container">
|
||||
{#if newTemplate}
|
||||
<div class="flex-between mr-4">
|
||||
<span class="trans-title mb-3">
|
||||
<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">
|
||||
{#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>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -0,0 +1,28 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { Space } from '@hcengineering/core'
|
||||
import { Header, Breadcrumb } from '@hcengineering/ui'
|
||||
import tracker from '../plugin'
|
||||
import EditRelatedTargets from './EditRelatedTargets.svelte'
|
||||
|
||||
export let value: Space | undefined
|
||||
export let visibleNav: boolean = true
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
</script>
|
||||
|
||||
<div class="hulyComponent">
|
||||
<Header minimize={!visibleNav} on:resize={(event) => dispatch('change', event.detail)}>
|
||||
<Breadcrumb icon={tracker.icon.Relations} label={tracker.string.RelatedIssues} size={'large'} isCurrent />
|
||||
</Header>
|
||||
<EditRelatedTargets {value} />
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.bordered {
|
||||
padding: 0.25rem 0.5rem;
|
||||
background-color: var(--theme-comp-header-color);
|
||||
border: 1px solid var(--theme-divider-color);
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
</style>
|
@ -44,6 +44,7 @@ import ProjectComponents from './components/components/ProjectComponents.svelte'
|
||||
import CreateIssue from './components/CreateIssue.svelte'
|
||||
import EditRelatedTargets from './components/EditRelatedTargets.svelte'
|
||||
import EditRelatedTargetsPopup from './components/EditRelatedTargetsPopup.svelte'
|
||||
import SettingsRelatedTargets from './components/SettingsRelatedTargets.svelte'
|
||||
import Inbox from './components/inbox/Inbox.svelte'
|
||||
import AssigneeEditor from './components/issues/AssigneeEditor.svelte'
|
||||
import DueDatePresenter from './components/issues/DueDatePresenter.svelte'
|
||||
@ -506,6 +507,7 @@ export default async (): Promise<Resources> => ({
|
||||
ComponentFilterValuePresenter,
|
||||
EditRelatedTargets,
|
||||
EditRelatedTargetsPopup,
|
||||
SettingsRelatedTargets,
|
||||
TimePresenter,
|
||||
EstimationValueEditor,
|
||||
IssueStatusIcon,
|
||||
|
@ -53,7 +53,8 @@
|
||||
resolvedLocationStore,
|
||||
setResolvedLocation,
|
||||
showPopup,
|
||||
workbenchSeparators
|
||||
workbenchSeparators,
|
||||
IconSettings
|
||||
} from '@hcengineering/ui'
|
||||
import view from '@hcengineering/view'
|
||||
import {
|
||||
@ -78,7 +79,6 @@
|
||||
import Navigator from './Navigator.svelte'
|
||||
import SelectWorkspaceMenu from './SelectWorkspaceMenu.svelte'
|
||||
import SpaceView from './SpaceView.svelte'
|
||||
import IconSettings from './icons/Settings.svelte'
|
||||
import TopMenu from './icons/TopMenu.svelte'
|
||||
|
||||
let contentPanel: HTMLElement
|
||||
@ -640,7 +640,11 @@
|
||||
/>
|
||||
</clipPath>
|
||||
</svg>
|
||||
<div class="workbench-container" style:flex-direction={appsDirection === 'horizontal' ? 'column-reverse' : 'row'}>
|
||||
<div
|
||||
class="workbench-container"
|
||||
class:setting-app={currentApplication?.alias === 'setting'}
|
||||
style:flex-direction={appsDirection === 'horizontal' ? 'column-reverse' : 'row'}
|
||||
>
|
||||
<div class="antiPanel-application {appsDirection}" class:lastDivider={!visibleNav}>
|
||||
<div
|
||||
class="hamburger-container clear-mins"
|
||||
@ -750,7 +754,7 @@
|
||||
application: currentApplication?._id
|
||||
}}
|
||||
/>
|
||||
<div class="workbench-container">
|
||||
<div class="workbench-container inner">
|
||||
{#if currentApplication && navigatorModel && navigator && visibleNav}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
@ -879,6 +883,28 @@
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
touch-action: none;
|
||||
|
||||
&:not(.setting-app, .inner) {
|
||||
border-top: 1px solid var(--theme-navpanel-divider);
|
||||
}
|
||||
&.setting-app {
|
||||
position: relative;
|
||||
background-color: var(--theme-statusbar-color);
|
||||
border-radius: var(--medium-BorderRadius);
|
||||
|
||||
&::after {
|
||||
position: absolute;
|
||||
content: '';
|
||||
inset: 0;
|
||||
border: 1px solid var(--theme-divider-color);
|
||||
border-radius: var(--medium-BorderRadius);
|
||||
pointer-events: none;
|
||||
}
|
||||
.antiPanel-application {
|
||||
border-radius: var(--medium-BorderRadius) 0 0 var(--medium-BorderRadius);
|
||||
border-right: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.hamburger-container {
|
||||
|
Loading…
Reference in New Issue
Block a user