@stack-spot/portal-layout 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/dist/LayoutOverlayManager.d.ts +20 -0
- package/dist/LayoutOverlayManager.d.ts.map +1 -1
- package/dist/LayoutOverlayManager.js +74 -22
- package/dist/LayoutOverlayManager.js.map +1 -1
- package/dist/components/Backdrop.d.ts +10 -0
- package/dist/components/Backdrop.d.ts.map +1 -0
- package/dist/components/Backdrop.js +18 -0
- package/dist/components/Backdrop.js.map +1 -0
- package/dist/components/Header.d.ts +5 -1
- package/dist/components/Header.d.ts.map +1 -1
- package/dist/components/Header.js +3 -2
- package/dist/components/Header.js.map +1 -1
- package/dist/components/NotificationCenter/NotificationPanel.d.ts +3 -0
- package/dist/components/NotificationCenter/NotificationPanel.d.ts.map +1 -0
- package/dist/components/NotificationCenter/NotificationPanel.js +16 -0
- package/dist/components/NotificationCenter/NotificationPanel.js.map +1 -0
- package/dist/components/NotificationCenter/NotificationPanelHeader.d.ts +3 -0
- package/dist/components/NotificationCenter/NotificationPanelHeader.d.ts.map +1 -0
- package/dist/components/NotificationCenter/NotificationPanelHeader.js +16 -0
- package/dist/components/NotificationCenter/NotificationPanelHeader.js.map +1 -0
- package/dist/components/NotificationCenter/NotificationsPanelFooter.d.ts +4 -0
- package/dist/components/NotificationCenter/NotificationsPanelFooter.d.ts.map +1 -0
- package/dist/components/NotificationCenter/NotificationsPanelFooter.js +12 -0
- package/dist/components/NotificationCenter/NotificationsPanelFooter.js.map +1 -0
- package/dist/components/NotificationCenter/dictionary.d.ts +2 -0
- package/dist/components/NotificationCenter/dictionary.d.ts.map +1 -0
- package/dist/components/NotificationCenter/dictionary.js +43 -0
- package/dist/components/NotificationCenter/dictionary.js.map +1 -0
- package/dist/components/NotificationCenter/index.d.ts +2 -0
- package/dist/components/NotificationCenter/index.d.ts.map +1 -0
- package/dist/components/NotificationCenter/index.js +36 -0
- package/dist/components/NotificationCenter/index.js.map +1 -0
- package/dist/components/NotificationCenter/styled.d.ts +4 -0
- package/dist/components/NotificationCenter/styled.d.ts.map +1 -0
- package/dist/components/NotificationCenter/styled.js +74 -0
- package/dist/components/NotificationCenter/styled.js.map +1 -0
- package/dist/components/NotificationCenter/tour.d.ts +2 -0
- package/dist/components/NotificationCenter/tour.d.ts.map +1 -0
- package/dist/components/NotificationCenter/tour.js +15 -0
- package/dist/components/NotificationCenter/tour.js.map +1 -0
- package/dist/components/NotificationCenter/types.d.ts +21 -0
- package/dist/components/NotificationCenter/types.d.ts.map +1 -0
- package/dist/components/NotificationCenter/types.js +2 -0
- package/dist/components/NotificationCenter/types.js.map +1 -0
- package/dist/components/NotificationCenter/utils.d.ts +5 -0
- package/dist/components/NotificationCenter/utils.d.ts.map +1 -0
- package/dist/components/NotificationCenter/utils.js +18 -0
- package/dist/components/NotificationCenter/utils.js.map +1 -0
- package/dist/components/error/ErrorBoundary.d.ts +3 -0
- package/dist/components/error/ErrorBoundary.d.ts.map +1 -1
- package/dist/components/error/SilentErrorBoundary.d.ts +3 -0
- package/dist/components/error/SilentErrorBoundary.d.ts.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/layout.css +1 -1
- package/package.json +2 -2
- package/readme.md +2 -0
- package/src/LayoutOverlayManager.tsx +69 -16
- package/src/components/Backdrop.tsx +32 -0
- package/src/components/Header.tsx +7 -1
- package/src/components/NotificationCenter/NotificationPanel.tsx +35 -0
- package/src/components/NotificationCenter/NotificationPanelHeader.tsx +53 -0
- package/src/components/NotificationCenter/NotificationsPanelFooter.tsx +25 -0
- package/src/components/NotificationCenter/dictionary.ts +44 -0
- package/src/components/NotificationCenter/index.tsx +61 -0
- package/src/components/NotificationCenter/styled.ts +75 -0
- package/src/components/NotificationCenter/tour.tsx +19 -0
- package/src/components/NotificationCenter/types.ts +24 -0
- package/src/components/NotificationCenter/utils.ts +20 -0
- package/src/index.ts +4 -4
- package/src/layout.css +1 -1
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
|
|
2
|
+
|
|
3
|
+
const dictionary = {
|
|
4
|
+
en: {
|
|
5
|
+
notifications: 'Notifications',
|
|
6
|
+
'ALL.ariaLabel': 'Show all notifications',
|
|
7
|
+
'UNREAD.ariaLabel': 'Show unread notifications',
|
|
8
|
+
'HIGH.ariaLabel': 'Show critical notifications',
|
|
9
|
+
'MEDIUM.ariaLabel': 'Show notifications with medium criticality',
|
|
10
|
+
'LOW.ariaLabel': 'Show notifications with low criticality',
|
|
11
|
+
'ALL.label': 'All',
|
|
12
|
+
'UNREAD.label': 'Unread',
|
|
13
|
+
'HIGH.label': 'High',
|
|
14
|
+
'MEDIUM.label': 'Medium',
|
|
15
|
+
'LOW.label': 'Low',
|
|
16
|
+
seeAll: 'See all notifications',
|
|
17
|
+
openNotifications: 'View notifications',
|
|
18
|
+
hasUnread: 'Has Unread notifications',
|
|
19
|
+
close: 'Close',
|
|
20
|
+
loadingNotifications: "Loading notifications. You'll be able to click this button and see them in a few seconds.",
|
|
21
|
+
tour: 'Now we have Notifications, this will help you stay informed of the latest updates about your Workspaces, Studios, and the Stackspot platform.',
|
|
22
|
+
},
|
|
23
|
+
pt: {
|
|
24
|
+
notifications: 'Notificações',
|
|
25
|
+
'ALL.ariaLabel': 'Mostrar todas as notificações',
|
|
26
|
+
'UNREAD.ariaLabel': 'Mostrar notificações não lidas',
|
|
27
|
+
'HIGH.ariaLabel': 'Mostrar notificações críticas',
|
|
28
|
+
'MEDIUM.ariaLabel': 'Mostrar notificações com criticidade média',
|
|
29
|
+
'LOW.ariaLabel': 'Mostrar notificações com criticidade baixa',
|
|
30
|
+
'ALL.label': 'Todas',
|
|
31
|
+
'UNREAD.label': 'Não lidas',
|
|
32
|
+
'HIGH.label': 'Alto',
|
|
33
|
+
'MEDIUM.label': 'Médio',
|
|
34
|
+
'LOW.label': 'Baixo',
|
|
35
|
+
seeAll: 'Ver todas as notificações',
|
|
36
|
+
openNotifications: 'Visualizar notificações',
|
|
37
|
+
hasUnread: 'Existem notificações não lidas',
|
|
38
|
+
close: 'Fechar',
|
|
39
|
+
loadingNotifications: 'Carregando notificações. Você poderá clicar neste botão e vê-las em alguns segundos.',
|
|
40
|
+
tour: 'Agora temos Notificações, isso irá ajudá-lo a se manter informado sobre as últimas atualizações sobre seus Workspaces, estúdios e a plataforma Stackspot.',
|
|
41
|
+
},
|
|
42
|
+
} satisfies Dictionary
|
|
43
|
+
|
|
44
|
+
export const useNotificationsDictionary = () => useTranslate(dictionary)
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { IconBox } from '@citric/core'
|
|
2
|
+
import { Bell } from '@citric/icons'
|
|
3
|
+
import { IconButton } from '@citric/ui'
|
|
4
|
+
import { useEffectOnce } from '@stack-spot/portal-components'
|
|
5
|
+
import { useNotificationController, useNotificationList, useUnreadNotifications } from '@stack-spot/portal-components/Notifications'
|
|
6
|
+
import { listToClass } from '@stack-spot/portal-theme'
|
|
7
|
+
import { useEffect, useState } from 'react'
|
|
8
|
+
import { useNotificationsDictionary } from './dictionary'
|
|
9
|
+
import { NotificationPanel } from './NotificationPanel'
|
|
10
|
+
import { NotificationBox } from './styled'
|
|
11
|
+
import { useNotificationsTourStep } from './tour'
|
|
12
|
+
import { getFiltersFromName, getNameFromFilters } from './utils'
|
|
13
|
+
|
|
14
|
+
const MAX_ITEMS = 10
|
|
15
|
+
|
|
16
|
+
export const NotificationCenter = () => {
|
|
17
|
+
const [open, setOpen] = useState(false)
|
|
18
|
+
const t = useNotificationsDictionary()
|
|
19
|
+
const { applyFilters, filters, items, status, error } = useNotificationList({ size: MAX_ITEMS })
|
|
20
|
+
const hasUnreadNotification = useUnreadNotifications()
|
|
21
|
+
const controller = useNotificationController()
|
|
22
|
+
useNotificationsTourStep()
|
|
23
|
+
|
|
24
|
+
useEffectOnce(() => {
|
|
25
|
+
controller.checkUnread()
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
if (open && (hasUnreadNotification || getNameFromFilters(filters) !== 'ALL')) {
|
|
30
|
+
applyFilters(getFiltersFromName('ALL'))
|
|
31
|
+
}
|
|
32
|
+
}, [open])
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<NotificationBox>
|
|
36
|
+
<IconButton
|
|
37
|
+
aria-label={status === 'startup' ? t.loadingNotifications : t.openNotifications}
|
|
38
|
+
onClick={status === 'startup' ? undefined : () => setOpen(true)}
|
|
39
|
+
className={listToClass([
|
|
40
|
+
'notification-button',
|
|
41
|
+
status === 'startup' && 'loading',
|
|
42
|
+
status !== 'startup' && hasUnreadNotification && 'unread',
|
|
43
|
+
])}
|
|
44
|
+
aria-expanded={open}
|
|
45
|
+
>
|
|
46
|
+
<IconBox size="md" className="notificationsTour" >
|
|
47
|
+
<Bell />
|
|
48
|
+
</IconBox>
|
|
49
|
+
</IconButton>
|
|
50
|
+
<NotificationPanel
|
|
51
|
+
filter={getNameFromFilters(filters)}
|
|
52
|
+
loading={status === 'loading'}
|
|
53
|
+
error={error}
|
|
54
|
+
items={items}
|
|
55
|
+
visible={open}
|
|
56
|
+
onClose={() => setOpen(false)}
|
|
57
|
+
onFilter={filter => applyFilters({ ...getFiltersFromName(filter), size: MAX_ITEMS })}
|
|
58
|
+
/>
|
|
59
|
+
</NotificationBox>
|
|
60
|
+
)
|
|
61
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { theme } from '@stack-spot/portal-theme'
|
|
2
|
+
import { styled } from 'styled-components'
|
|
3
|
+
import { Backdrop } from '../Backdrop'
|
|
4
|
+
|
|
5
|
+
export const NotificationBox = styled.div`
|
|
6
|
+
.notification-button {
|
|
7
|
+
border: none;
|
|
8
|
+
background: transparent;
|
|
9
|
+
margin: 0 40px;
|
|
10
|
+
position: relative;
|
|
11
|
+
|
|
12
|
+
&.loading {
|
|
13
|
+
cursor: progress;
|
|
14
|
+
opacity: 0.5;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
&:before {
|
|
18
|
+
content: '';
|
|
19
|
+
position: absolute;
|
|
20
|
+
top: -1px;
|
|
21
|
+
right: -1px;
|
|
22
|
+
width: 12px;
|
|
23
|
+
height: 12px;
|
|
24
|
+
border-radius: 50%;
|
|
25
|
+
background-color: ${theme.color.danger['500']};
|
|
26
|
+
transform: scale(0);
|
|
27
|
+
transition: transform ease-in 0.3s;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
&.unread:before {
|
|
31
|
+
transform: scale(1);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
`
|
|
35
|
+
|
|
36
|
+
export const StyledBackdrop = styled(Backdrop)`
|
|
37
|
+
position: absolute;
|
|
38
|
+
top: calc(var(--header-height) + 10px);
|
|
39
|
+
right: 60px;
|
|
40
|
+
box-shadow: 4px 4px 48px ${theme.color.danger.contrastText};
|
|
41
|
+
|
|
42
|
+
.notification-panel {
|
|
43
|
+
width: 368px;
|
|
44
|
+
padding: 16px;
|
|
45
|
+
border-radius: 4px;
|
|
46
|
+
background-color: ${theme.color.light[300]};
|
|
47
|
+
border: 1px solid ${theme.color.light[400]};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.filter-list {
|
|
51
|
+
list-style: none;
|
|
52
|
+
margin: 16px 0;
|
|
53
|
+
padding: 0;
|
|
54
|
+
display: flex;
|
|
55
|
+
flex-direction: row;
|
|
56
|
+
justify-content: space-between;
|
|
57
|
+
.filter-btn {
|
|
58
|
+
&:focus {
|
|
59
|
+
border-color: transparent;
|
|
60
|
+
}
|
|
61
|
+
&[aria-pressed="true"] {
|
|
62
|
+
border-color: ${theme.color.primary[500]};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.see-all {
|
|
68
|
+
margin-top: 8px;
|
|
69
|
+
width: 100%;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.error-feedback {
|
|
73
|
+
flex-direction: column;
|
|
74
|
+
}
|
|
75
|
+
`
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Box, Text } from '@citric/core'
|
|
2
|
+
import { useTour } from '../tour'
|
|
3
|
+
import { useNotificationsDictionary } from './dictionary'
|
|
4
|
+
|
|
5
|
+
export const useNotificationsTourStep = () => {
|
|
6
|
+
const t = useNotificationsDictionary()
|
|
7
|
+
const { addStep } = useTour()
|
|
8
|
+
|
|
9
|
+
addStep({
|
|
10
|
+
content: <Box px={5} py={3}>
|
|
11
|
+
<Text appearance="microtext1" colorScheme="inverse.contrastText">
|
|
12
|
+
{t.tour}
|
|
13
|
+
</Text>
|
|
14
|
+
</Box>,
|
|
15
|
+
selector: '.notificationsTour',
|
|
16
|
+
title: t.notifications,
|
|
17
|
+
position: 'bottom',
|
|
18
|
+
} as any)
|
|
19
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { NotificationListProps } from '@stack-spot/portal-components/Notifications'
|
|
2
|
+
|
|
3
|
+
export type NotificationFilter = 'ALL' | 'UNREAD' | 'HIGH' | 'MEDIUM' | 'LOW'
|
|
4
|
+
|
|
5
|
+
export interface NotificationPanelProps extends Omit<NotificationListProps, 'compact' | 'onCommit'> {
|
|
6
|
+
filter: NotificationFilter,
|
|
7
|
+
loading: boolean,
|
|
8
|
+
error: any,
|
|
9
|
+
onFilter: (filter: NotificationFilter) => Promise<void>,
|
|
10
|
+
visible: boolean,
|
|
11
|
+
onClose: () => void,
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface NotificationPanelHeaderProps {
|
|
15
|
+
filter: NotificationFilter,
|
|
16
|
+
onChangeFilter: (value: NotificationFilter) => Promise<void>,
|
|
17
|
+
onClose: () => void,
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface FilterButtonProps {
|
|
21
|
+
value: NotificationFilter,
|
|
22
|
+
current: NotificationFilter,
|
|
23
|
+
onChangeFilter: (value: NotificationFilter) => void,
|
|
24
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { LoadNotificationsFilters } from '@stack-spot/portal-components/Notifications'
|
|
2
|
+
import { NotificationFilter } from './types'
|
|
3
|
+
|
|
4
|
+
const empty: LoadNotificationsFilters = { committed: undefined, context: undefined, criticality: undefined, search: undefined }
|
|
5
|
+
|
|
6
|
+
export function getFiltersFromName(filterName: NotificationFilter): LoadNotificationsFilters {
|
|
7
|
+
switch (filterName) {
|
|
8
|
+
case 'ALL': return empty
|
|
9
|
+
case 'HIGH': return { ...empty, criticality: 'HIGH' }
|
|
10
|
+
case 'LOW': return { ...empty, criticality: 'LOW' }
|
|
11
|
+
case 'MEDIUM': return { ...empty, criticality: 'MEDIUM' }
|
|
12
|
+
case 'UNREAD': return { ...empty, committed: false }
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function getNameFromFilters(filters: LoadNotificationsFilters): NotificationFilter {
|
|
17
|
+
if (filters.committed === false) return 'UNREAD'
|
|
18
|
+
if (filters.criticality) return filters.criticality
|
|
19
|
+
return 'ALL'
|
|
20
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
export { Layout, RawLayout } from './Layout'
|
|
2
|
-
export { overlay } from './LayoutOverlayManager'
|
|
3
1
|
export { Dialog } from './components/Dialog'
|
|
4
2
|
export { Header, HeaderProps } from './components/Header'
|
|
5
|
-
export { OverlayContent } from './components/OverlayContent'
|
|
6
|
-
export { PortalSwitcher } from './components/PortalSwitcher'
|
|
7
3
|
export { ActionItem, MenuContent, MenuGroup, Title } from './components/menu/MenuContent'
|
|
8
4
|
export { MenuSections } from './components/menu/MenuSections'
|
|
9
5
|
export * from './components/menu/types'
|
|
6
|
+
export { CLOSE_OVERLAY_ID, OverlayContent } from './components/OverlayContent'
|
|
7
|
+
export { PortalSwitcher } from './components/PortalSwitcher'
|
|
10
8
|
export * from './components/tour'
|
|
11
9
|
export * from './components/types'
|
|
12
10
|
export * from './elements'
|
|
13
11
|
export * from './errors'
|
|
12
|
+
export { Layout, RawLayout } from './Layout'
|
|
13
|
+
export { overlay } from './LayoutOverlayManager'
|
|
14
14
|
export * from './utils'
|
|
15
15
|
|