EZQMS-562: Introduced reusable NotificationToast component (#4873)

Signed-off-by: Petr Vyazovetskiy <develop.pit@gmail.com>
This commit is contained in:
Pete Anøther 2024-03-05 02:39:49 -03:00 committed by GitHub
parent 13d391bde5
commit 8dd0a60a70
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 140 additions and 95 deletions

View File

@ -0,0 +1,112 @@
<!--
//
// Copyright © 2024 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
//
-->
<script lang="ts">
import type { ComponentType } from 'svelte'
import { fade } from 'svelte/transition'
import Button from './Button.svelte'
import Icon from './Icon.svelte'
import CheckCircle from './icons/CheckCircle.svelte'
import Close from './icons/Close.svelte'
import Info from './icons/Info.svelte'
import { NotificationSeverity } from './notifications/NotificationSeverity'
export let onClose: (() => void) | undefined = undefined
export let severity: NotificationSeverity | undefined = undefined
export let title: string
const getIcon = (): ComponentType | undefined => {
switch (severity) {
case NotificationSeverity.Success:
return CheckCircle
case NotificationSeverity.Error:
case NotificationSeverity.Info:
case NotificationSeverity.Warning:
return Info
}
}
$: icon = getIcon()
</script>
<div class="root" in:fade out:fade>
<div class="flex-between">
<div class="flex-row-center">
{#if icon}
<div
class="mr-2"
class:icon-success={severity === NotificationSeverity.Success}
class:icon-error={severity === NotificationSeverity.Error}
class:icon-info={severity === NotificationSeverity.Info}
class:icon-warning={severity === NotificationSeverity.Warning}
>
<Icon {icon} size="medium" />
</div>
{/if}
<span class="overflow-label fs-bold text-base caption-color">{title}</span>
</div>
{#if onClose !== undefined}
<Button icon={Close} kind="ghost" size="small" on:click={onClose} />
{/if}
</div>
<div class="content">
<slot name="content" />
</div>
{#if $$slots.buttons}
<div class="flex-between gap-2">
<slot name="buttons" />
</div>
{/if}
</div>
<style lang="scss">
.root {
overflow: hidden;
display: flex;
flex-direction: column;
margin: 0.75rem;
padding: 0.5rem;
min-width: 25rem;
max-width: 35rem;
min-height: 7rem;
color: var(--theme-caption-color);
background-color: var(--theme-popup-color);
border: 1px solid var(--theme-popup-divider);
border-radius: 0.5rem;
box-shadow: var(--theme-popup-shadow);
.icon-success {
color: var(--theme-won-color);
}
.icon-error {
color: var(--theme-lost-color);
}
.icon-info {
color: var(--primary-color-skyblue);
}
.icon-warning {
color: var(--theme-warning-color);
}
.content {
flex-grow: 1;
margin: 1rem 0 1.25rem;
}
}
</style>

View File

@ -140,6 +140,7 @@ export { default as NavItem } from './components/NavItem.svelte'
export { default as NavGroup } from './components/NavGroup.svelte' export { default as NavGroup } from './components/NavGroup.svelte'
export { default as Modal } from './components/Modal.svelte' export { default as Modal } from './components/Modal.svelte'
export { default as AccordionItem } from './components/AccordionItem.svelte' export { default as AccordionItem } from './components/AccordionItem.svelte'
export { default as NotificationToast } from './components/NotificationToast.svelte'
export { default as IconAdd } from './components/icons/Add.svelte' export { default as IconAdd } from './components/icons/Add.svelte'
export { default as IconCircleAdd } from './components/icons/CircleAdd.svelte' export { default as IconCircleAdd } from './components/icons/CircleAdd.svelte'

View File

@ -2,20 +2,8 @@
import { getMetadata } from '@hcengineering/platform' import { getMetadata } from '@hcengineering/platform'
import presentation, { copyTextToClipboard, createQuery } from '@hcengineering/presentation' import presentation, { copyTextToClipboard, createQuery } from '@hcengineering/presentation'
import { Issue, IssueStatus } from '@hcengineering/tracker' import { Issue, IssueStatus } from '@hcengineering/tracker'
import { import { Button, Notification, navigate, parseLocation, NotificationToast } from '@hcengineering/ui'
AnySvelteComponent,
Button,
Icon,
IconCheckCircle,
IconClose,
IconInfo,
Notification,
NotificationSeverity,
navigate,
parseLocation
} from '@hcengineering/ui'
import view from '@hcengineering/view' import view from '@hcengineering/view'
import { fade } from 'svelte/transition'
import { statusStore } from '@hcengineering/view-resources' import { statusStore } from '@hcengineering/view-resources'
import tracker from '../../plugin' import tracker from '../../plugin'
@ -27,10 +15,10 @@
const issueQuery = createQuery() const issueQuery = createQuery()
let issue: Issue | undefined let issue: Issue | undefined = undefined
let status: IssueStatus | undefined let status: IssueStatus | undefined = undefined
const { title, subTitle, severity, params } = notification const { subTitle, params } = notification
$: issueQuery.query( $: issueQuery.query(
tracker.class.Issue, tracker.class.Issue,
@ -45,30 +33,7 @@
status = $statusStore.byId.get(issue.status) status = $statusStore.byId.get(issue.status)
} }
const getIcon = (): AnySvelteComponent | undefined => { function handleIssueOpened (): void {
switch (severity) {
case NotificationSeverity.Success:
return IconCheckCircle
case NotificationSeverity.Error:
case NotificationSeverity.Info:
case NotificationSeverity.Warning:
return IconInfo
}
}
const getIconColor = () => {
switch (severity) {
case NotificationSeverity.Success:
return '#34db80'
case NotificationSeverity.Error:
return '#eb5757'
case NotificationSeverity.Info:
return '#93caf3'
case NotificationSeverity.Warning:
return '#f2994a'
}
}
const handleIssueOpened = () => {
if (params?.issueUrl) { if (params?.issueUrl) {
const url = new URL(params?.issueUrl) const url = new URL(params?.issueUrl)
const frontUrl = getMetadata(presentation.metadata.FrontUrl) ?? window.location.origin const frontUrl = getMetadata(presentation.metadata.FrontUrl) ?? window.location.origin
@ -80,67 +45,34 @@
onRemove() onRemove()
} }
const handleCopyUrl = () => {
if (issue) { function handleCopyUrl (): void {
copyTextToClipboard(params?.issueUrl) if (issue === undefined) {
void copyTextToClipboard(params?.issueUrl)
} }
} }
$: icon = getIcon()
</script> </script>
<div class="notify-container" in:fade out:fade> <NotificationToast title={notification.title} severity={notification.severity} onClose={onRemove}>
<div class="flex-between"> <svelte:fragment slot="content">
<div class="flex-row-center"> <div class="flex-row-center flex-wrap gap-2 reverse">
{#if icon} {#if status === undefined && issue}
<div class="mr-2"><Icon {icon} size="medium" fill={getIconColor()} /></div> <IssueStatusIcon value={status} space={issue.space} size="small" />
{/if} {/if}
<span class="overflow-label fs-bold caption-color">{title}</span> {#if issue}
<IssuePresenter value={issue} />
{/if}
<span class="overflow-label">
{subTitle}
</span>
<span class="content-dark-color">
{params?.subTitlePostfix}
</span>
</div> </div>
<Button icon={IconClose} kind="ghost" size="small" on:click={onRemove} /> </svelte:fragment>
</div>
<div class="content flex-row-center flex-wrap gap-2 reverse"> <svelte:fragment slot="buttons">
{#if status && issue}
<IssueStatusIcon value={status} space={issue.space} size="small" />
{/if}
{#if issue}
<IssuePresenter value={issue} />
{/if}
<span class="overflow-label">
{subTitle}
</span>
<span class="content-dark-color">
{params?.subTitlePostfix}
</span>
</div>
<div class="flex-between gap-2">
<Button label={tracker.string.ViewIssue} on:click={handleIssueOpened} /> <Button label={tracker.string.ViewIssue} on:click={handleIssueOpened} />
<Button icon={view.icon.CopyLink} label={tracker.string.CopyIssueUrl} on:click={handleCopyUrl} /> <Button icon={view.icon.CopyLink} label={tracker.string.CopyIssueUrl} on:click={handleCopyUrl} />
</div> </svelte:fragment>
</div> </NotificationToast>
<style lang="scss">
.notify-container {
overflow: hidden;
display: flex;
flex-direction: column;
margin: 0.75rem;
padding: 0.5rem;
min-width: 25rem;
max-width: 35rem;
min-height: 7rem;
color: var(--theme-caption-color);
background-color: var(--theme-popup-color);
border: 1px solid var(--theme-popup-divider);
border-radius: 0.5rem;
box-shadow: var(--theme-popup-shadow);
.content {
flex-grow: 1;
margin: 1rem 0 1.25rem;
padding: 0 1rem;
// border: 1px solid var(--theme-divider-color);
border-radius: 0.5rem;
}
}
</style>