@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.
Files changed (77) hide show
  1. package/dist/components/AsyncContent.d.ts +39 -0
  2. package/dist/components/AsyncContent.d.ts.map +1 -0
  3. package/dist/components/AsyncContent.js +27 -0
  4. package/dist/components/AsyncContent.js.map +1 -0
  5. package/dist/components/InfiniteScroll.d.ts +3 -0
  6. package/dist/components/InfiniteScroll.d.ts.map +1 -0
  7. package/dist/components/InfiniteScroll.js +5 -0
  8. package/dist/components/InfiniteScroll.js.map +1 -0
  9. package/dist/components/LazyMarkdown/BlockquoteMd.d.ts +4 -0
  10. package/dist/components/LazyMarkdown/BlockquoteMd.d.ts.map +1 -0
  11. package/dist/components/LazyMarkdown/BlockquoteMd.js +64 -0
  12. package/dist/components/LazyMarkdown/BlockquoteMd.js.map +1 -0
  13. package/dist/components/LazyMarkdown/CodeViewer.d.ts +19 -0
  14. package/dist/components/LazyMarkdown/CodeViewer.d.ts.map +1 -0
  15. package/dist/components/LazyMarkdown/CodeViewer.js +116 -0
  16. package/dist/components/LazyMarkdown/CodeViewer.js.map +1 -0
  17. package/dist/components/LazyMarkdown/Markdown.d.ts +6 -0
  18. package/dist/components/LazyMarkdown/Markdown.d.ts.map +1 -0
  19. package/dist/components/LazyMarkdown/Markdown.js +87 -0
  20. package/dist/components/LazyMarkdown/Markdown.js.map +1 -0
  21. package/dist/components/LazyMarkdown/MarkdownButton.d.ts +8 -0
  22. package/dist/components/LazyMarkdown/MarkdownButton.d.ts.map +1 -0
  23. package/dist/components/LazyMarkdown/MarkdownButton.js +4 -0
  24. package/dist/components/LazyMarkdown/MarkdownButton.js.map +1 -0
  25. package/dist/components/LazyMarkdown/Video.d.ts +4 -0
  26. package/dist/components/LazyMarkdown/Video.d.ts.map +1 -0
  27. package/dist/components/LazyMarkdown/Video.js +3 -0
  28. package/dist/components/LazyMarkdown/Video.js.map +1 -0
  29. package/dist/components/LazyMarkdown/index.d.ts +8 -0
  30. package/dist/components/LazyMarkdown/index.d.ts.map +1 -0
  31. package/dist/components/LazyMarkdown/index.js +8 -0
  32. package/dist/components/LazyMarkdown/index.js.map +1 -0
  33. package/dist/components/Notifications/NotificationComponent.d.ts +24 -0
  34. package/dist/components/Notifications/NotificationComponent.d.ts.map +1 -0
  35. package/dist/components/Notifications/NotificationComponent.js +114 -0
  36. package/dist/components/Notifications/NotificationComponent.js.map +1 -0
  37. package/dist/components/Notifications/NotificationItem.d.ts +11 -0
  38. package/dist/components/Notifications/NotificationItem.d.ts.map +1 -0
  39. package/dist/components/Notifications/NotificationItem.js +59 -0
  40. package/dist/components/Notifications/NotificationItem.js.map +1 -0
  41. package/dist/components/Notifications/index.d.ts +4 -0
  42. package/dist/components/Notifications/index.d.ts.map +1 -0
  43. package/dist/components/Notifications/index.js +4 -0
  44. package/dist/components/Notifications/index.js.map +1 -0
  45. package/dist/components/Notifications/types.d.ts +38 -0
  46. package/dist/components/Notifications/types.d.ts.map +1 -0
  47. package/dist/components/Notifications/types.js +20 -0
  48. package/dist/components/Notifications/types.js.map +1 -0
  49. package/dist/components/ScrollView.d.ts +27 -0
  50. package/dist/components/ScrollView.d.ts.map +1 -0
  51. package/dist/components/ScrollView.js +25 -0
  52. package/dist/components/ScrollView.js.map +1 -0
  53. package/dist/components/StatusCircle.d.ts +14 -0
  54. package/dist/components/StatusCircle.d.ts.map +1 -0
  55. package/dist/components/StatusCircle.js +26 -0
  56. package/dist/components/StatusCircle.js.map +1 -0
  57. package/dist/hooks/date.d.ts +22 -0
  58. package/dist/hooks/date.d.ts.map +1 -0
  59. package/dist/hooks/date.js +40 -0
  60. package/dist/hooks/date.js.map +1 -0
  61. package/package.json +16 -5
  62. package/src/components/AsyncContent.tsx +70 -0
  63. package/src/components/InfiniteScroll.tsx +13 -0
  64. package/src/components/LazyMarkdown/BlockquoteMd.tsx +94 -0
  65. package/src/components/LazyMarkdown/CodeViewer.tsx +154 -0
  66. package/src/components/LazyMarkdown/Markdown.tsx +107 -0
  67. package/src/components/LazyMarkdown/MarkdownButton.tsx +13 -0
  68. package/src/components/LazyMarkdown/Video.tsx +6 -0
  69. package/src/components/LazyMarkdown/index.tsx +18 -0
  70. package/src/components/Notifications/NotificationComponent.tsx +264 -0
  71. package/src/components/Notifications/NotificationItem.tsx +136 -0
  72. package/src/components/Notifications/index.tsx +3 -0
  73. package/src/components/Notifications/types.ts +46 -0
  74. package/src/components/ScrollView.tsx +51 -0
  75. package/src/components/StatusCircle.tsx +44 -0
  76. package/src/hooks/date.ts +43 -0
  77. 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,3 @@
1
+ export { NotificationComponent } from './NotificationComponent'
2
+ export { NotificationItem } from './NotificationItem'
3
+ export * from './types'
@@ -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
@@ -7,4 +7,3 @@ export { titleEffect, useTitleEffect } from './hooks/title'
7
7
  export { useEffectOnce } from './hooks/use-effect-once'
8
8
  export * from './utils/accessibility'
9
9
  export * from './utils/cookie'
10
-