-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add NeToastNotification component (#42)
- Loading branch information
Showing
3 changed files
with
239 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
<!-- | ||
Copyright (C) 2024 Nethesis S.r.l. | ||
SPDX-License-Identifier: GPL-3.0-or-later | ||
--> | ||
|
||
<script setup lang="ts"> | ||
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome' | ||
import { faXmark as fasXmark } from '@fortawesome/free-solid-svg-icons' | ||
import { library } from '@fortawesome/fontawesome-svg-core' | ||
import { humanDistanceToNowLoc, formatDateLoc } from '../main' | ||
import NeButton from './NeButton.vue' | ||
import NeRoundedIcon from './NeRoundedIcon.vue' | ||
import NeTooltip from './NeTooltip.vue' | ||
import { type PropType } from 'vue' | ||
|
||
export interface NeNotification { | ||
id: string | ||
kind: 'info' | 'warning' | 'error' | 'success' | ||
title: string | ||
description?: string | ||
timestamp?: Date | ||
payload?: any | ||
isShown?: boolean | ||
primaryLabel?: string | ||
primaryAction?: Function | ||
secondaryLabel?: string | ||
secondaryAction?: Function | ||
} | ||
|
||
defineProps({ | ||
notification: { | ||
type: Object as PropType<NeNotification>, | ||
required: true | ||
}, | ||
srCloseLabel: { | ||
type: String, | ||
required: true | ||
}, | ||
primaryButtonRightAligned: { | ||
type: Boolean, | ||
default: false | ||
}, | ||
showCloseButton: { | ||
type: Boolean, | ||
default: false | ||
}, | ||
showTimestamp: { | ||
type: Boolean, | ||
default: false | ||
}, | ||
fullWidth: { | ||
type: Boolean, | ||
default: false | ||
} | ||
}) | ||
|
||
defineEmits(['close']) | ||
|
||
// add fontawesome icons | ||
library.add(fasXmark) | ||
</script> | ||
|
||
<template> | ||
<div | ||
:class="[ | ||
`pointer-events-auto w-full overflow-hidden rounded-lg bg-white text-sm shadow-lg ring-1 ring-black ring-opacity-5 dark:bg-gray-800 dark:shadow-gray-950`, | ||
{ 'max-w-sm': !fullWidth } | ||
]" | ||
> | ||
<div class="relative p-4"> | ||
<div | ||
v-if="showCloseButton || (showTimestamp && notification.timestamp)" | ||
class="absolute right-0 top-0 block pr-4 pt-4" | ||
> | ||
<!-- close button --> | ||
<button | ||
v-if="showCloseButton" | ||
type="button" | ||
class="rounded-md leading-none text-gray-600 transition-colors hover:text-gray-700 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2 focus:ring-offset-white dark:text-gray-300 dark:hover:text-gray-200 dark:focus:ring-primary-300 dark:focus:ring-offset-gray-900" | ||
@click="$emit('close')" | ||
> | ||
<span class="sr-only">{{ srCloseLabel }}</span> | ||
<font-awesome-icon :icon="['fas', 'xmark']" class="h-5 w-5" aria-hidden="true" /> | ||
</button> | ||
<!-- timestamp --> | ||
<NeTooltip | ||
v-else-if="showTimestamp && notification.timestamp" | ||
placement="left-start" | ||
class="text-gray-500 dark:text-gray-400" | ||
> | ||
<template #trigger> | ||
{{ humanDistanceToNowLoc(notification.timestamp) }} | ||
</template> | ||
<template #content> | ||
{{ formatDateLoc(notification.timestamp, 'Pp') }} | ||
</template> | ||
</NeTooltip> | ||
</div> | ||
<div class="flex items-start"> | ||
<div class="flex-shrink-0"> | ||
<!-- custom icon --> | ||
<template v-if="$slots.icon"> | ||
<slot name="icon" /> | ||
</template> | ||
<!-- standard icon --> | ||
<NeRoundedIcon v-else :kind="notification.kind" /> | ||
</div> | ||
<div class="ml-3 w-0 flex-1 pt-0.5"> | ||
<p | ||
:class="[ | ||
'font-semibold text-gray-900 dark:text-gray-50', | ||
{ | ||
'mr-6': showCloseButton, | ||
'!mr-24': showTimestamp && notification.timestamp && !showCloseButton | ||
} | ||
]" | ||
> | ||
{{ notification.title }} | ||
</p> | ||
<p | ||
v-if="notification.description" | ||
class="mt-2 break-words text-gray-700 dark:text-gray-200" | ||
> | ||
{{ notification.description }} | ||
</p> | ||
</div> | ||
</div> | ||
<div | ||
v-if="notification.primaryLabel || notification.secondaryLabel" | ||
class="ml-10 flex flex-shrink-0" | ||
> | ||
<div | ||
:class="[ | ||
`mt-4 flex grow justify-end`, | ||
primaryButtonRightAligned ? 'flex-row' : 'flex-row-reverse' | ||
]" | ||
> | ||
<NeButton | ||
v-if="notification.secondaryLabel && notification.secondaryAction" | ||
kind="tertiary" | ||
size="md" | ||
class="ml-3 mt-0 w-auto" | ||
@click="notification.secondaryAction" | ||
> | ||
{{ notification.secondaryLabel }} | ||
</NeButton> | ||
<NeButton | ||
v-if="notification.primaryLabel && notification.primaryAction" | ||
kind="primary" | ||
size="md" | ||
class="ml-3 w-auto" | ||
@click="notification.primaryAction" | ||
> | ||
{{ notification.primaryLabel }} | ||
</NeButton> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
</template> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
// Copyright (C) 2024 Nethesis S.r.l. | ||
// SPDX-License-Identifier: GPL-3.0-or-later | ||
|
||
import type { Meta, StoryObj } from '@storybook/vue3' | ||
import { NeToastNotification, NeNotification, NeRoundedIcon } from '../src/main' | ||
import { faHeart } from '@fortawesome/free-solid-svg-icons' | ||
import { library } from '@fortawesome/fontawesome-svg-core' | ||
|
||
const meta = { | ||
title: 'Visual/NeToastNotification', | ||
component: NeToastNotification, | ||
args: { | ||
srCloseLabel: 'Close', | ||
primaryButtonRightAligned: false, | ||
showCloseButton: false, | ||
showTimestamp: false, | ||
fullWidth: false, | ||
notification: undefined | ||
} | ||
} satisfies Meta<typeof NeToastNotification> | ||
|
||
export default meta | ||
type Story = StoryObj<typeof meta> | ||
|
||
library.add(faHeart) | ||
|
||
const notification: NeNotification = { | ||
id: '1', | ||
kind: 'info', | ||
title: 'Toast title', | ||
description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit', | ||
timestamp: new Date(), | ||
payload: undefined, | ||
isShown: true, | ||
primaryLabel: 'Primary', | ||
primaryAction: () => { | ||
console.log('Primary action') | ||
}, | ||
secondaryLabel: 'Secondary', | ||
secondaryAction: () => { | ||
console.log('Secondary action') | ||
} | ||
} | ||
|
||
const template = '<NeToastNotification v-bind="args"/>' | ||
|
||
export const Default: Story = { | ||
render: (args) => ({ | ||
components: { NeToastNotification }, | ||
setup() { | ||
return { args } | ||
}, | ||
template: template | ||
}), | ||
args: { notification } | ||
} | ||
|
||
const templateWithIconSlot = `<NeToastNotification v-bind="args"> | ||
<template #icon> | ||
<NeRoundedIcon | ||
:customIcon="['fas', 'heart']" | ||
customForegroundClasses="text-fuchsia-700 dark:text-fuchsia-50" | ||
customBackgroundClasses="bg-fuchsia-100 dark:bg-fuchsia-700" | ||
/> | ||
</template> | ||
</NeToastNotification>` | ||
|
||
export const IconSlot: Story = { | ||
render: (args) => ({ | ||
components: { NeToastNotification, NeRoundedIcon }, | ||
setup() { | ||
return { args } | ||
}, | ||
template: templateWithIconSlot | ||
}), | ||
args: { notification } | ||
} |