1
1
import * as React from 'react' ;
2
2
3
- import { Icon , Link , useMobile , useUniqId } from '@gravity-ui/uikit' ;
3
+ import { Flex , Icon , Link , useMobile , useUniqId } from '@gravity-ui/uikit' ;
4
4
5
5
import { CnMods , block } from '../utils/cn' ;
6
6
@@ -15,13 +15,64 @@ type Props = {notification: NotificationProps};
15
15
export const Notification = React . memo ( function Notification ( props : Props ) {
16
16
const mobile = useMobile ( ) ;
17
17
const { notification} = props ;
18
- const { title, content, formattedDate, source, unread, theme} = notification ;
18
+ const {
19
+ title,
20
+ content,
21
+ formattedDate,
22
+ source,
23
+ unread,
24
+ theme,
25
+ sourcePlacement = 'bottom' ,
26
+ } = notification ;
19
27
20
28
const modifiers : CnMods = { unread, theme, mobile, active : Boolean ( notification . onClick ) } ;
21
29
const titleId = useUniqId ( ) ;
22
30
23
31
const sourceIcon = source && renderSourceIcon ( source , titleId ) ;
24
32
33
+ const renderedTitle = title ? (
34
+ < div className = { b ( 'title-wrapper' ) } >
35
+ < div className = { b ( 'title' ) } > { title } </ div >
36
+ </ div >
37
+ ) : null ;
38
+
39
+ const renderedSideActions = (
40
+ < div className = { b ( 'actions' , { 'side-actions' : true } ) } > { props . notification . sideActions } </ div >
41
+ ) ;
42
+
43
+ const renderedBottomActions = props . notification . bottomActions ? (
44
+ < div className = { b ( 'actions' , { 'bottom-actions' : true } ) } >
45
+ { props . notification . bottomActions }
46
+ </ div >
47
+ ) : null ;
48
+
49
+ const renderedContent = < div className = { b ( 'content' ) } > { content } </ div > ;
50
+
51
+ const renderedSourceText =
52
+ source ?. title || formattedDate ? (
53
+ < Flex className = { b ( 'source-text' ) } gap = { 1 } >
54
+ { source ?. title
55
+ ? renderSourceTitle ( {
56
+ title : source . title ,
57
+ href : source . href ,
58
+ id : titleId ,
59
+ } )
60
+ : null }
61
+ { source ?. title && formattedDate ? < span > •</ span > : null }
62
+ { formattedDate ? < div className = { b ( 'right-date' ) } > { formattedDate } </ div > : null }
63
+ </ Flex >
64
+ ) : null ;
65
+
66
+ const hasSourceOnTop = renderedSourceText && sourcePlacement === 'top' ;
67
+ const hasSourceOnBottom = renderedSourceText && sourcePlacement === 'bottom' ;
68
+ const topPart =
69
+ renderedTitle || hasSourceOnTop
70
+ ? withSideActions (
71
+ renderTitleAndSource ( renderedTitle , hasSourceOnTop ? renderedSourceText : null ) ,
72
+ renderedSideActions ,
73
+ )
74
+ : null ;
75
+
25
76
return (
26
77
// eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions
27
78
< div
@@ -31,45 +82,56 @@ export const Notification = React.memo(function Notification(props: Props) {
31
82
onClick = { notification . onClick }
32
83
>
33
84
{ sourceIcon ? < div className = { b ( 'left' ) } > { sourceIcon } </ div > : null }
34
- < div className = { b ( 'right' ) } >
35
- < div className = { b ( 'right-top-part' ) } >
36
- < div className = { b ( 'right-meta-and-title' ) } >
37
- < div className = { b ( 'right-meta' ) } >
38
- { source ?. title
39
- ? renderSourceTitle ( {
40
- title : source . title ,
41
- href : source . href ,
42
- id : titleId ,
43
- } )
44
- : null }
45
- { source ?. title && formattedDate ? < span > •</ span > : null }
46
- { formattedDate ? (
47
- < div className = { b ( 'right-date' ) } > { formattedDate } </ div >
48
- ) : null }
49
- </ div >
50
- { title ? < div className = { b ( 'right-title' ) } > { title } </ div > : null }
51
- </ div >
52
- < div className = { b ( 'actions' , { 'right-side-actions' : true } ) } >
53
- { props . notification . sideActions }
54
- </ div >
55
- </ div >
56
- < div className = { b ( 'right-content' ) } > { content } </ div >
57
- { props . notification . bottomActions ? (
58
- < div className = { b ( 'actions' , { 'right-bottom-actions' : true } ) } >
59
- { props . notification . bottomActions }
60
- </ div >
61
- ) : null }
62
- </ div >
85
+
86
+ < Flex className = { b ( 'right' ) } justifyContent = "space-between" gap = { 2 } overflow = "hidden" >
87
+ < Flex direction = "column" overflow = "hidden" width = "100%" >
88
+ { topPart }
89
+
90
+ { withSideActions (
91
+ renderedContent ,
92
+ ! renderedTitle && ! hasSourceOnTop ? renderedSideActions : null ,
93
+ ) }
94
+
95
+ { hasSourceOnBottom ? (
96
+ < div className = { b ( 'bottom-source' ) } > { renderedSourceText } </ div >
97
+ ) : null }
98
+
99
+ { renderedBottomActions }
100
+ </ Flex >
101
+ </ Flex >
63
102
</ div >
64
103
) ;
65
104
} ) ;
66
105
106
+ function withSideActions ( content : React . ReactNode , sideActions : React . ReactNode ) {
107
+ return sideActions ? (
108
+ < Flex alignItems = "center" justifyContent = "space-between" width = "100%" overflow = "hidden" >
109
+ { content }
110
+ { sideActions }
111
+ </ Flex >
112
+ ) : (
113
+ content
114
+ ) ;
115
+ }
116
+
117
+ function renderTitleAndSource ( title : React . ReactNode , source : React . ReactNode ) {
118
+ return title && source ? (
119
+ < Flex className = { b ( 'title-with-source' ) } direction = "column" overflow = "hidden" >
120
+ { source }
121
+ { title }
122
+ </ Flex >
123
+ ) : (
124
+ ( title ?? source )
125
+ ) ;
126
+ }
127
+
67
128
interface RenderSourceTitleOptions {
68
129
title : string ;
69
130
href ?: string ;
70
131
id : string ;
71
132
}
72
- function renderSourceTitle ( { title, href, id} : RenderSourceTitleOptions ) : JSX . Element {
133
+
134
+ function renderSourceTitle ( { title, href, id} : RenderSourceTitleOptions ) : React . ReactNode {
73
135
return href ? (
74
136
< Link className = { b ( 'right-source-title' ) } href = { href } target = "_blank" title = { title } id = { id } >
75
137
{ title }
@@ -81,7 +143,7 @@ function renderSourceTitle({title, href, id}: RenderSourceTitleOptions): JSX.Ele
81
143
) ;
82
144
}
83
145
84
- function renderSourceIcon ( source : NotificationSourceProps , titleId : string ) : JSX . Element | null {
146
+ function renderSourceIcon ( source : NotificationSourceProps , titleId : string ) : React . ReactNode {
85
147
const iconElement = getIconElement ( source ) ;
86
148
87
149
if ( ! iconElement ) return null ;
@@ -95,11 +157,13 @@ function renderSourceIcon(source: NotificationSourceProps, titleId: string): JSX
95
157
) ;
96
158
}
97
159
98
- function getIconElement ( source : NotificationSourceProps ) : JSX . Element | null {
160
+ function getIconElement ( source : NotificationSourceProps ) : React . ReactNode {
99
161
if ( 'icon' in source && source . icon ) {
100
162
return < Icon className = { b ( 'source-icon' ) } size = { 36 } data = { source . icon } /> ;
101
163
} else if ( 'imageSrc' in source && source . imageSrc ) {
102
164
return < img alt = "" className = { b ( 'source-icon' ) } src = { source . imageSrc } /> ;
165
+ } else if ( 'custom' in source && source . custom ) {
166
+ return source . custom ;
103
167
} else {
104
168
return null ;
105
169
}
0 commit comments