@stack-spot/portal-components 1.2.0 → 1.3.0-rc
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/dist/components/AsyncContent.d.ts +39 -0
- package/dist/components/AsyncContent.d.ts.map +1 -0
- package/dist/components/AsyncContent.js +27 -0
- package/dist/components/AsyncContent.js.map +1 -0
- package/dist/components/InfiniteScroll.d.ts +3 -0
- package/dist/components/InfiniteScroll.d.ts.map +1 -0
- package/dist/components/InfiniteScroll.js +5 -0
- package/dist/components/InfiniteScroll.js.map +1 -0
- package/dist/components/LazyMarkdown/BlockquoteMd.d.ts +4 -0
- package/dist/components/LazyMarkdown/BlockquoteMd.d.ts.map +1 -0
- package/dist/components/LazyMarkdown/BlockquoteMd.js +64 -0
- package/dist/components/LazyMarkdown/BlockquoteMd.js.map +1 -0
- package/dist/components/LazyMarkdown/CodeViewer.d.ts +19 -0
- package/dist/components/LazyMarkdown/CodeViewer.d.ts.map +1 -0
- package/dist/components/LazyMarkdown/CodeViewer.js +116 -0
- package/dist/components/LazyMarkdown/CodeViewer.js.map +1 -0
- package/dist/components/LazyMarkdown/Markdown.d.ts +6 -0
- package/dist/components/LazyMarkdown/Markdown.d.ts.map +1 -0
- package/dist/components/LazyMarkdown/Markdown.js +87 -0
- package/dist/components/LazyMarkdown/Markdown.js.map +1 -0
- package/dist/components/LazyMarkdown/MarkdownButton.d.ts +8 -0
- package/dist/components/LazyMarkdown/MarkdownButton.d.ts.map +1 -0
- package/dist/components/LazyMarkdown/MarkdownButton.js +4 -0
- package/dist/components/LazyMarkdown/MarkdownButton.js.map +1 -0
- package/dist/components/LazyMarkdown/Video.d.ts +4 -0
- package/dist/components/LazyMarkdown/Video.d.ts.map +1 -0
- package/dist/components/LazyMarkdown/Video.js +3 -0
- package/dist/components/LazyMarkdown/Video.js.map +1 -0
- package/dist/components/LazyMarkdown/index.d.ts +8 -0
- package/dist/components/LazyMarkdown/index.d.ts.map +1 -0
- package/dist/components/LazyMarkdown/index.js +8 -0
- package/dist/components/LazyMarkdown/index.js.map +1 -0
- package/dist/components/Notifications/NotificationComponent.d.ts +24 -0
- package/dist/components/Notifications/NotificationComponent.d.ts.map +1 -0
- package/dist/components/Notifications/NotificationComponent.js +114 -0
- package/dist/components/Notifications/NotificationComponent.js.map +1 -0
- package/dist/components/Notifications/NotificationItem.d.ts +11 -0
- package/dist/components/Notifications/NotificationItem.d.ts.map +1 -0
- package/dist/components/Notifications/NotificationItem.js +59 -0
- package/dist/components/Notifications/NotificationItem.js.map +1 -0
- package/dist/components/Notifications/index.d.ts +4 -0
- package/dist/components/Notifications/index.d.ts.map +1 -0
- package/dist/components/Notifications/index.js +4 -0
- package/dist/components/Notifications/index.js.map +1 -0
- package/dist/components/Notifications/types.d.ts +38 -0
- package/dist/components/Notifications/types.d.ts.map +1 -0
- package/dist/components/Notifications/types.js +20 -0
- package/dist/components/Notifications/types.js.map +1 -0
- package/dist/components/ScrollView.d.ts +27 -0
- package/dist/components/ScrollView.d.ts.map +1 -0
- package/dist/components/ScrollView.js +25 -0
- package/dist/components/ScrollView.js.map +1 -0
- package/dist/components/StatusCircle.d.ts +14 -0
- package/dist/components/StatusCircle.d.ts.map +1 -0
- package/dist/components/StatusCircle.js +26 -0
- package/dist/components/StatusCircle.js.map +1 -0
- package/dist/hooks/date.d.ts +22 -0
- package/dist/hooks/date.d.ts.map +1 -0
- package/dist/hooks/date.js +40 -0
- package/dist/hooks/date.js.map +1 -0
- package/package.json +16 -5
- package/src/components/AsyncContent.tsx +70 -0
- package/src/components/InfiniteScroll.tsx +13 -0
- package/src/components/LazyMarkdown/BlockquoteMd.tsx +94 -0
- package/src/components/LazyMarkdown/CodeViewer.tsx +154 -0
- package/src/components/LazyMarkdown/Markdown.tsx +107 -0
- package/src/components/LazyMarkdown/MarkdownButton.tsx +13 -0
- package/src/components/LazyMarkdown/Video.tsx +6 -0
- package/src/components/LazyMarkdown/index.tsx +18 -0
- package/src/components/Notifications/NotificationComponent.tsx +264 -0
- package/src/components/Notifications/NotificationItem.tsx +136 -0
- package/src/components/Notifications/index.tsx +3 -0
- package/src/components/Notifications/types.ts +46 -0
- package/src/components/ScrollView.tsx +51 -0
- package/src/components/StatusCircle.tsx +44 -0
- package/src/hooks/date.ts +43 -0
- package/src/index.ts +0 -1
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { Button, Flex, IconBox, OneOfColorSchemesWithVariants, SxProp, Text } from '@citric/core'
|
|
2
|
+
import { Envelope, EnvelopeOpen } from '@citric/icons'
|
|
3
|
+
import { theme } from '@stack-spot/portal-theme'
|
|
4
|
+
import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
|
|
5
|
+
import { differenceInDays, parseISO } from 'date-fns'
|
|
6
|
+
import { useAnchorTag } from '../../context/anchor'
|
|
7
|
+
import { useDateFormatter } from '../../hooks/date'
|
|
8
|
+
import { LazyMarkdown } from '../LazyMarkdown/index'
|
|
9
|
+
import { StackspotNotification } from './types'
|
|
10
|
+
|
|
11
|
+
const styles = {
|
|
12
|
+
item: (color: string, isRead: boolean) => ({
|
|
13
|
+
borderLeft: `2px solid ${color}`,
|
|
14
|
+
opacity: isRead ? 0.5 : 1,
|
|
15
|
+
}),
|
|
16
|
+
} satisfies Record<string, (...args: any) => SxProp>
|
|
17
|
+
|
|
18
|
+
const statusToColor: Record<string, OneOfColorSchemesWithVariants> = {
|
|
19
|
+
LOW: 'success.500',
|
|
20
|
+
MEDIUM: 'warning.500',
|
|
21
|
+
HIGH: 'danger.500',
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface Props {
|
|
25
|
+
notification: StackspotNotification,
|
|
26
|
+
isSummary: boolean,
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const NotificationHeader = ({ notification, isSummary, onClickMarkRead }: NotificationItemProps) => (
|
|
30
|
+
<Flex justifyContent="space-between" mb={2}>
|
|
31
|
+
<Text appearance={isSummary ? 'body2' : 'body1'}>
|
|
32
|
+
{notification.title}
|
|
33
|
+
</Text>
|
|
34
|
+
<IconBox appearance="circle"
|
|
35
|
+
onClick={() => {
|
|
36
|
+
!notification.committed && onClickMarkRead()
|
|
37
|
+
}}
|
|
38
|
+
sx={{ border: `1px solid ${theme.color.light[500]}`, cursor: notification.committed ? 'default' : 'pointer' }}>
|
|
39
|
+
{notification.committed ? <Envelope /> : <EnvelopeOpen />}
|
|
40
|
+
</IconBox>
|
|
41
|
+
</Flex>
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
const getDaysAgo = (dateString: string): number => {
|
|
45
|
+
const givenDate = parseISO(dateString)
|
|
46
|
+
const today = new Date()
|
|
47
|
+
return differenceInDays(today, givenDate)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const NotificationContent = ({ notification, isSummary }: Props) => {
|
|
51
|
+
const { formatDate } = useDateFormatter()
|
|
52
|
+
const t = useTranslate(dictionary)
|
|
53
|
+
|
|
54
|
+
const daysAgo = getDaysAgo(notification.trigger_at)
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<>
|
|
58
|
+
{!isSummary && <Flex mt={4} mb={3}>
|
|
59
|
+
<LazyMarkdown>{notification.description}</LazyMarkdown>
|
|
60
|
+
</Flex>}
|
|
61
|
+
<Flex>
|
|
62
|
+
<Text appearance="microtext1" colorScheme="light.700">
|
|
63
|
+
{t[notification.context]}
|
|
64
|
+
</Text>
|
|
65
|
+
<Text appearance="microtext1" colorScheme="light.700" mx="2">
|
|
66
|
+
•
|
|
67
|
+
</Text>
|
|
68
|
+
{!isSummary && <>
|
|
69
|
+
<Text appearance="microtext1" colorScheme="light.700">
|
|
70
|
+
{formatDate(notification.created_at)}
|
|
71
|
+
</Text>
|
|
72
|
+
<Text appearance="microtext1" colorScheme="light.700" mx="2">
|
|
73
|
+
•
|
|
74
|
+
</Text>
|
|
75
|
+
</>}
|
|
76
|
+
<Text appearance="microtext1" colorScheme="light.700">
|
|
77
|
+
{daysAgo > 0 ? daysAgo + ' '+ t.daysAgo : t.today }
|
|
78
|
+
</Text>
|
|
79
|
+
</Flex>
|
|
80
|
+
</>
|
|
81
|
+
)
|
|
82
|
+
}
|
|
83
|
+
interface NotificationFooterProps {
|
|
84
|
+
call_to_action: string,
|
|
85
|
+
onClickMarkRead: () => void,
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const NotificationFooter = ({ call_to_action, onClickMarkRead }: NotificationFooterProps) => {
|
|
89
|
+
const t = useTranslate(dictionary)
|
|
90
|
+
const Link = useAnchorTag()
|
|
91
|
+
|
|
92
|
+
return (
|
|
93
|
+
<Flex mt="5">
|
|
94
|
+
<Button size="sm" colorScheme="inverse" onClick={onClickMarkRead}>
|
|
95
|
+
<Link href={call_to_action}>
|
|
96
|
+
<Text colorScheme={'inverse.contrastText'}>
|
|
97
|
+
{t.takeMeThere}
|
|
98
|
+
</Text>
|
|
99
|
+
</Link>
|
|
100
|
+
</Button>
|
|
101
|
+
</Flex>
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
interface NotificationItemProps extends Props {
|
|
106
|
+
onClickMarkRead: () => void,
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export const NotificationItem = ({ notification, isSummary, onClickMarkRead }: NotificationItemProps) => (
|
|
110
|
+
<Flex bg="light.400" p="3 3 3 5" r="xs"
|
|
111
|
+
flexDirection="column" w="100%" sx={styles.item(statusToColor[notification.criticality], notification.committed)}>
|
|
112
|
+
<NotificationHeader notification={notification} isSummary={isSummary} onClickMarkRead={onClickMarkRead}/>
|
|
113
|
+
<NotificationContent notification={notification} isSummary={isSummary} />
|
|
114
|
+
{notification.call_to_action && <NotificationFooter call_to_action={notification.call_to_action}
|
|
115
|
+
onClickMarkRead={onClickMarkRead} />}
|
|
116
|
+
</Flex>
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
const dictionary = {
|
|
120
|
+
en: {
|
|
121
|
+
takeMeThere: 'Take me there',
|
|
122
|
+
ACCOUNT: 'Account',
|
|
123
|
+
WORKSPACE: 'Workspace',
|
|
124
|
+
STUDIO: 'Studio',
|
|
125
|
+
daysAgo: 'days ago',
|
|
126
|
+
today: 'today',
|
|
127
|
+
},
|
|
128
|
+
pt: {
|
|
129
|
+
takeMeThere: 'Leve-me para lá',
|
|
130
|
+
ACCOUNT: 'Conta',
|
|
131
|
+
WORKSPACE: 'Workspace',
|
|
132
|
+
STUDIO: 'Estúdio',
|
|
133
|
+
daysAgo: 'dias atrás',
|
|
134
|
+
today: 'hoje',
|
|
135
|
+
},
|
|
136
|
+
} satisfies Dictionary
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
2
|
+
export enum NotificationType {
|
|
3
|
+
High = 'HIGH',
|
|
4
|
+
Medium = 'MEDIUM',
|
|
5
|
+
Low = 'LOW',
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
9
|
+
export enum NotificationContext {
|
|
10
|
+
Account = 'ACCOUNT',
|
|
11
|
+
Workspace = 'WORKSPACE',
|
|
12
|
+
Studio = 'STUDIO',
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface StackspotNotification {
|
|
16
|
+
id: string,
|
|
17
|
+
title: string,
|
|
18
|
+
description: string,
|
|
19
|
+
criticality: NotificationType,
|
|
20
|
+
call_to_action?: string,
|
|
21
|
+
committed: boolean,
|
|
22
|
+
context: NotificationContext,
|
|
23
|
+
trigger_at: string,
|
|
24
|
+
created_at: string,
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export type NotificationTypeFilters = NotificationType | UnreadType
|
|
28
|
+
|
|
29
|
+
export interface NotificationFilters {
|
|
30
|
+
filterBy?: 'criticality' | 'context',
|
|
31
|
+
filterValue?: string,
|
|
32
|
+
sort?: string,
|
|
33
|
+
direction?: string,
|
|
34
|
+
page?: any,
|
|
35
|
+
size?: string,
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface NotificationCommitted {
|
|
39
|
+
id: string,
|
|
40
|
+
committed: boolean,
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
44
|
+
export enum UnreadType {
|
|
45
|
+
Unread = 'unread',
|
|
46
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { OneOfColorSchemesWithVariants, OneOfPalettesWithVariants } from '@citric/core'
|
|
2
|
+
import { getColor } from '@citric/core/dist/utils/theme'
|
|
3
|
+
import { styled } from 'styled-components'
|
|
4
|
+
|
|
5
|
+
type Color = OneOfColorSchemesWithVariants | OneOfPalettesWithVariants
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
/**
|
|
9
|
+
* By default, the ScrollView scrolls in both directions
|
|
10
|
+
*/
|
|
11
|
+
direction?: 'horizontal' | 'vertical' | 'both',
|
|
12
|
+
/**
|
|
13
|
+
* The color of the track in the scroll bar (background)
|
|
14
|
+
* light.400 by default
|
|
15
|
+
*/
|
|
16
|
+
trackColor?: Color,
|
|
17
|
+
/**
|
|
18
|
+
* The color of the thumb in the scroll bar (foreground)
|
|
19
|
+
* light.700 by default
|
|
20
|
+
*/
|
|
21
|
+
thumbColor?: Color,
|
|
22
|
+
/**
|
|
23
|
+
* `auto` to show the scroll-bar only when the content overflows. `scroll` to always show it.
|
|
24
|
+
* `auto` by default
|
|
25
|
+
*/
|
|
26
|
+
scrollType?: 'auto' | 'scroll',
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function getDirectionStyle({ direction = 'both', scrollType = 'auto' }: Props) {
|
|
30
|
+
const x = direction === 'both' || direction === 'horizontal' ? `overflow-x: ${scrollType};` : ''
|
|
31
|
+
const y = direction === 'both' || direction === 'vertical' ? `overflow-x: ${scrollType};` : ''
|
|
32
|
+
return `${x}${y}`
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const ScrollView = styled.div<Props>`
|
|
36
|
+
${getDirectionStyle}
|
|
37
|
+
&::-webkit-scrollbar {
|
|
38
|
+
width: 4px;
|
|
39
|
+
height: 4px;
|
|
40
|
+
}
|
|
41
|
+
&::-webkit-scrollbar-track {
|
|
42
|
+
background: ${({ theme, trackColor = 'light.400' }) => getColor(theme, trackColor)};
|
|
43
|
+
border-radius: 2px;
|
|
44
|
+
left: 40px;
|
|
45
|
+
}
|
|
46
|
+
&::-webkit-scrollbar-thumb,
|
|
47
|
+
&::-webkit-scrollbar-thumb:hover {
|
|
48
|
+
background: ${({ theme, thumbColor = 'light.700' }) => getColor(theme, thumbColor)};
|
|
49
|
+
border-radius: 2px;
|
|
50
|
+
}
|
|
51
|
+
`
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { OneOfColorSchemesWithVariants, OneOfPalettesWithVariants, Theme } from '@citric/core'
|
|
2
|
+
import { getColor } from '@citric/core/dist/utils/theme'
|
|
3
|
+
import { Tooltip } from '@citric/ui'
|
|
4
|
+
import styled from 'styled-components'
|
|
5
|
+
|
|
6
|
+
type Color = OneOfColorSchemesWithVariants | OneOfPalettesWithVariants
|
|
7
|
+
|
|
8
|
+
export type Status = 'success' | 'warning' | 'danger' | 'unknown' | 'progress'
|
|
9
|
+
|
|
10
|
+
interface Props {
|
|
11
|
+
status?: Status,
|
|
12
|
+
tooltip?: string,
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const statusToColor: Record<Status, Color> = {
|
|
16
|
+
success: 'success.500',
|
|
17
|
+
warning: 'warning.500',
|
|
18
|
+
danger: 'danger.500',
|
|
19
|
+
progress: 'secondary.500',
|
|
20
|
+
unknown: 'light.contrastText',
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function getColorFromStatus(theme: Theme, status?: Status) {
|
|
24
|
+
const colorName = status && status in statusToColor ? statusToColor[status] : statusToColor.unknown
|
|
25
|
+
return getColor(theme, colorName)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface WithTooltip {
|
|
29
|
+
tooltip?: string,
|
|
30
|
+
className?: string,
|
|
31
|
+
style?: React.CSSProperties,
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const CircleWithTooltip = ({ tooltip, className, style }: WithTooltip) => {
|
|
35
|
+
const circle = <div style={style} className={className}></div>
|
|
36
|
+
return tooltip && tooltip.length ? <Tooltip text={tooltip}>{circle}</Tooltip> : circle
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const StatusCircle = styled(CircleWithTooltip)<Props>`
|
|
40
|
+
background-color: ${({ status, theme }) => getColorFromStatus(theme as any, status)};
|
|
41
|
+
width: 12px;
|
|
42
|
+
height: 12px;
|
|
43
|
+
border-radius: 50%;
|
|
44
|
+
`
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Language, useLanguage } from '@stack-spot/portal-translate'
|
|
2
|
+
import { format } from 'date-fns'
|
|
3
|
+
import { ptBR } from 'date-fns/locale'
|
|
4
|
+
|
|
5
|
+
function formatDate(date: string, locale: Language, includeTime: boolean) {
|
|
6
|
+
const time = includeTime ? ' HH:mm' : ''
|
|
7
|
+
return locale === 'pt'
|
|
8
|
+
? format(new Date(date), `dd/MM/yyyy${time}`, { locale: ptBR })
|
|
9
|
+
: format(new Date(date), `MMMM dd yyyy${time}`)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @deprecated this hook is prone to errors due to how React Hooks work. Use `useDateFormatter()` instead.
|
|
14
|
+
* @param date
|
|
15
|
+
* @returns the formatted date
|
|
16
|
+
*/
|
|
17
|
+
export function useFormatDate(date?: string) {
|
|
18
|
+
const locale = useLanguage()
|
|
19
|
+
return date ? formatDate(date, locale, false) : undefined
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @deprecated this hook is prone to errors due to how React Hooks work. Use `useDateFormatter()` instead.
|
|
24
|
+
* @param date
|
|
25
|
+
* @returns the formatted date with its time
|
|
26
|
+
*/
|
|
27
|
+
export function useFormatDateWithTime(date?: string) {
|
|
28
|
+
const locale = useLanguage()
|
|
29
|
+
return date ? formatDate(date, locale, true) : undefined
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* A utility for formatting dates.
|
|
34
|
+
* @param date
|
|
35
|
+
* @returns a date formatter object.
|
|
36
|
+
*/
|
|
37
|
+
export function useDateFormatter() {
|
|
38
|
+
const locale = useLanguage()
|
|
39
|
+
return {
|
|
40
|
+
formatDate: (date?: string) => date ? formatDate(date, locale, false) : undefined,
|
|
41
|
+
formatDateWithTime: (date?: string) => date ? formatDate(date, locale, true) : undefined,
|
|
42
|
+
}
|
|
43
|
+
}
|
package/src/index.ts
CHANGED