@stack-spot/portal-layout 2.0.1 → 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 +8 -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/package.json +2 -2
- package/readme.md +1 -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
|
@@ -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
|
|