@performant-software/semantic-components 0.5.1

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 (218) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +0 -0
  3. package/build/index.js +2 -0
  4. package/build/index.js.map +1 -0
  5. package/build/main.css +786 -0
  6. package/index.js +1 -0
  7. package/package.json +37 -0
  8. package/src/components/AccordionDataList.css +8 -0
  9. package/src/components/AccordionDataList.js +224 -0
  10. package/src/components/AccordionList.css +27 -0
  11. package/src/components/AccordionList.js +596 -0
  12. package/src/components/AccordionSelector.css +3 -0
  13. package/src/components/AccordionSelector.js +359 -0
  14. package/src/components/ArrowButtons.css +4 -0
  15. package/src/components/ArrowButtons.js +38 -0
  16. package/src/components/AssociatedDropdown.css +44 -0
  17. package/src/components/AssociatedDropdown.js +338 -0
  18. package/src/components/BooleanIcon.css +0 -0
  19. package/src/components/BooleanIcon.js +33 -0
  20. package/src/components/CancelButton.css +0 -0
  21. package/src/components/CancelButton.js +25 -0
  22. package/src/components/ColorButton.css +4 -0
  23. package/src/components/ColorButton.js +34 -0
  24. package/src/components/ColorPickerModal.css +3 -0
  25. package/src/components/ColorPickerModal.js +77 -0
  26. package/src/components/ColumnResize.css +9 -0
  27. package/src/components/ColumnResize.js +20 -0
  28. package/src/components/DataList.css +0 -0
  29. package/src/components/DataList.js +531 -0
  30. package/src/components/DataTable.css +43 -0
  31. package/src/components/DataTable.js +596 -0
  32. package/src/components/DataTableColumnSelector.css +10 -0
  33. package/src/components/DataTableColumnSelector.js +146 -0
  34. package/src/components/DateInput.css +6 -0
  35. package/src/components/DateInput.js +58 -0
  36. package/src/components/DatePicker.css +72 -0
  37. package/src/components/DatePicker.js +81 -0
  38. package/src/components/DescriptorField.css +0 -0
  39. package/src/components/DescriptorField.js +42 -0
  40. package/src/components/DownloadButton.css +0 -0
  41. package/src/components/DownloadButton.js +23 -0
  42. package/src/components/Draggable.css +0 -0
  43. package/src/components/Draggable.js +94 -0
  44. package/src/components/DropdownButton.css +3 -0
  45. package/src/components/DropdownButton.js +65 -0
  46. package/src/components/DropdownMenu.css +0 -0
  47. package/src/components/DropdownMenu.js +68 -0
  48. package/src/components/EditModal.css +8 -0
  49. package/src/components/EditModal.js +99 -0
  50. package/src/components/EditPage.css +7 -0
  51. package/src/components/EditPage.js +249 -0
  52. package/src/components/EmbeddedList.css +7 -0
  53. package/src/components/EmbeddedList.js +278 -0
  54. package/src/components/FileInputButton.css +0 -0
  55. package/src/components/FileInputButton.js +54 -0
  56. package/src/components/FileUpload.css +31 -0
  57. package/src/components/FileUpload.js +188 -0
  58. package/src/components/FileUploadModal.css +0 -0
  59. package/src/components/FileUploadModal.js +408 -0
  60. package/src/components/FuzzyDate.css +8 -0
  61. package/src/components/FuzzyDate.js +575 -0
  62. package/src/components/GoogleMap.css +0 -0
  63. package/src/components/GoogleMap.js +105 -0
  64. package/src/components/GooglePlacesSearch.css +0 -0
  65. package/src/components/GooglePlacesSearch.js +43 -0
  66. package/src/components/HorizontalCards.css +50 -0
  67. package/src/components/HorizontalCards.js +226 -0
  68. package/src/components/ItemCollection.css +3 -0
  69. package/src/components/ItemCollection.js +159 -0
  70. package/src/components/ItemList.css +0 -0
  71. package/src/components/ItemList.js +126 -0
  72. package/src/components/Items.css +19 -0
  73. package/src/components/Items.js +365 -0
  74. package/src/components/ItemsToggle.css +0 -0
  75. package/src/components/ItemsToggle.js +168 -0
  76. package/src/components/KeyboardField.css +4 -0
  77. package/src/components/KeyboardField.js +147 -0
  78. package/src/components/LazyDocument.css +21 -0
  79. package/src/components/LazyDocument.js +113 -0
  80. package/src/components/LazyImage.css +21 -0
  81. package/src/components/LazyImage.js +119 -0
  82. package/src/components/LazyVideo.css +21 -0
  83. package/src/components/LazyVideo.js +131 -0
  84. package/src/components/LinkButton.css +8 -0
  85. package/src/components/LinkButton.js +23 -0
  86. package/src/components/LinkLabel.css +8 -0
  87. package/src/components/LinkLabel.js +29 -0
  88. package/src/components/List.css +8 -0
  89. package/src/components/List.js +761 -0
  90. package/src/components/ListFilters.css +0 -0
  91. package/src/components/ListFilters.js +408 -0
  92. package/src/components/ListLoader.css +8 -0
  93. package/src/components/ListLoader.js +32 -0
  94. package/src/components/ListTable.css +3 -0
  95. package/src/components/ListTable.js +86 -0
  96. package/src/components/LoginModal.css +7 -0
  97. package/src/components/LoginModal.js +102 -0
  98. package/src/components/MasonryGrid.css +48 -0
  99. package/src/components/MasonryGrid.js +202 -0
  100. package/src/components/MediaGallery.css +37 -0
  101. package/src/components/MediaGallery.js +148 -0
  102. package/src/components/MediaGrid.css +72 -0
  103. package/src/components/MediaGrid.js +74 -0
  104. package/src/components/MediaList.css +3 -0
  105. package/src/components/MediaList.js +98 -0
  106. package/src/components/ModalDropdown.css +11 -0
  107. package/src/components/ModalDropdown.js +84 -0
  108. package/src/components/NestedAccordion.css +41 -0
  109. package/src/components/NestedAccordion.js +276 -0
  110. package/src/components/PhotoViewer.css +3 -0
  111. package/src/components/PhotoViewer.js +36 -0
  112. package/src/components/PlayButton.css +3 -0
  113. package/src/components/PlayButton.js +37 -0
  114. package/src/components/RemoteDropdown.css +13 -0
  115. package/src/components/RemoteDropdown.js +368 -0
  116. package/src/components/SaveButton.css +0 -0
  117. package/src/components/SaveButton.js +31 -0
  118. package/src/components/Section.css +0 -0
  119. package/src/components/Section.js +41 -0
  120. package/src/components/Selectize.css +11 -0
  121. package/src/components/Selectize.js +297 -0
  122. package/src/components/SelectizeHeader.css +3 -0
  123. package/src/components/SelectizeHeader.js +40 -0
  124. package/src/components/TabbedModal.css +14 -0
  125. package/src/components/TabbedModal.js +165 -0
  126. package/src/components/TabsMenu.css +0 -0
  127. package/src/components/TabsMenu.js +35 -0
  128. package/src/components/TagsList.css +0 -0
  129. package/src/components/TagsList.js +43 -0
  130. package/src/components/Thumbnail.css +0 -0
  131. package/src/components/Thumbnail.js +47 -0
  132. package/src/components/Toaster.css +9 -0
  133. package/src/components/Toaster.js +73 -0
  134. package/src/components/VideoFrameSelector.css +3 -0
  135. package/src/components/VideoFrameSelector.js +148 -0
  136. package/src/components/VideoPlayer.css +3 -0
  137. package/src/components/VideoPlayer.js +55 -0
  138. package/src/components/VideoPlayerButton.css +17 -0
  139. package/src/components/VideoPlayerButton.js +17 -0
  140. package/src/components/ViewXML.css +0 -0
  141. package/src/components/ViewXML.js +72 -0
  142. package/src/i18n/en.json +204 -0
  143. package/src/i18n/i18n.js +24 -0
  144. package/src/index.js +76 -0
  145. package/types/components/AccordionDataList.js.flow +224 -0
  146. package/types/components/AccordionList.js.flow +596 -0
  147. package/types/components/AccordionSelector.js.flow +359 -0
  148. package/types/components/ArrowButtons.js.flow +38 -0
  149. package/types/components/AssociatedDropdown.js.flow +338 -0
  150. package/types/components/BooleanIcon.js.flow +33 -0
  151. package/types/components/CancelButton.js.flow +25 -0
  152. package/types/components/ColorButton.js.flow +34 -0
  153. package/types/components/ColorPickerModal.js.flow +77 -0
  154. package/types/components/ColumnResize.js.flow +20 -0
  155. package/types/components/DataList.js.flow +531 -0
  156. package/types/components/DataTable.js.flow +596 -0
  157. package/types/components/DataTableColumnSelector.js.flow +146 -0
  158. package/types/components/DataView.js.flow +125 -0
  159. package/types/components/DateInput.js.flow +58 -0
  160. package/types/components/DatePicker.js.flow +81 -0
  161. package/types/components/DescriptorField.js.flow +42 -0
  162. package/types/components/DownloadButton.js.flow +23 -0
  163. package/types/components/Draggable.js.flow +94 -0
  164. package/types/components/DropdownButton.js.flow +65 -0
  165. package/types/components/DropdownMenu.js.flow +68 -0
  166. package/types/components/EditModal.js.flow +99 -0
  167. package/types/components/EditPage.js.flow +249 -0
  168. package/types/components/EmbeddedList.js.flow +278 -0
  169. package/types/components/FileInputButton.js.flow +54 -0
  170. package/types/components/FileUpload.js.flow +188 -0
  171. package/types/components/FileUploadModal.js.flow +408 -0
  172. package/types/components/FuzzyDate.js.flow +575 -0
  173. package/types/components/GoogleMap.js.flow +105 -0
  174. package/types/components/GooglePlacesSearch.js.flow +43 -0
  175. package/types/components/HorizontalCards.js.flow +226 -0
  176. package/types/components/ItemCollection.js.flow +159 -0
  177. package/types/components/ItemList.js.flow +126 -0
  178. package/types/components/Items.js.flow +365 -0
  179. package/types/components/ItemsToggle.js.flow +168 -0
  180. package/types/components/KeyboardField.js.flow +147 -0
  181. package/types/components/LazyDocument.js.flow +113 -0
  182. package/types/components/LazyImage.js.flow +119 -0
  183. package/types/components/LazyVideo.js.flow +131 -0
  184. package/types/components/LinkButton.js.flow +23 -0
  185. package/types/components/LinkLabel.js.flow +29 -0
  186. package/types/components/List.js.flow +761 -0
  187. package/types/components/ListFilters.js.flow +408 -0
  188. package/types/components/ListLoader.js.flow +32 -0
  189. package/types/components/ListTable.js.flow +86 -0
  190. package/types/components/LoginModal.js.flow +102 -0
  191. package/types/components/MasonryGrid.js.flow +202 -0
  192. package/types/components/MediaGallery.js.flow +148 -0
  193. package/types/components/MediaGrid.js.flow +74 -0
  194. package/types/components/MediaList.js.flow +98 -0
  195. package/types/components/MenuBar.js.flow +77 -0
  196. package/types/components/MenuSidebar.js.flow +72 -0
  197. package/types/components/ModalDropdown.js.flow +84 -0
  198. package/types/components/NestedAccordion.js.flow +276 -0
  199. package/types/components/PhotoViewer.js.flow +36 -0
  200. package/types/components/PlayButton.js.flow +37 -0
  201. package/types/components/RemoteDropdown.js.flow +368 -0
  202. package/types/components/SaveButton.js.flow +31 -0
  203. package/types/components/Section.js.flow +41 -0
  204. package/types/components/Selectize.js.flow +297 -0
  205. package/types/components/SelectizeHeader.js.flow +40 -0
  206. package/types/components/TabbedModal.js.flow +165 -0
  207. package/types/components/TabsMenu.js.flow +35 -0
  208. package/types/components/TagsList.js.flow +43 -0
  209. package/types/components/Thumbnail.js.flow +47 -0
  210. package/types/components/Toaster.js.flow +73 -0
  211. package/types/components/VideoFrameSelector.js.flow +148 -0
  212. package/types/components/VideoPlayer.js.flow +55 -0
  213. package/types/components/VideoPlayerButton.js.flow +17 -0
  214. package/types/components/ViewXML.js.flow +72 -0
  215. package/types/hooks/Imageable.js.flow +54 -0
  216. package/types/i18n/i18n.js.flow +24 -0
  217. package/types/index.js.flow +78 -0
  218. package/webpack.config.js +3 -0
@@ -0,0 +1,202 @@
1
+ // @flow
2
+
3
+ import React, {
4
+ useEffect,
5
+ useState,
6
+ type Element,
7
+ type Node
8
+ } from 'react';
9
+ import {
10
+ Dimmer,
11
+ Header,
12
+ Image,
13
+ Loader,
14
+ Pagination,
15
+ Segment,
16
+ Transition
17
+ } from 'semantic-ui-react';
18
+ import _ from 'underscore';
19
+ import './MasonryGrid.css';
20
+
21
+ type Item = {
22
+ alt: string,
23
+ extra?: Element<any> | string,
24
+ id: number,
25
+ image: string,
26
+ subtitle?: string,
27
+ title?: string,
28
+ };
29
+
30
+ type LayoutProps = {
31
+ children: Array<Node>,
32
+ columns: number,
33
+ gap: number
34
+ };
35
+
36
+ const MasonryLayout = (props: LayoutProps) => {
37
+ const columnWrapper = {};
38
+ const result = [];
39
+
40
+ // create columns
41
+ for (let i = 0; i < props.columns; i += 1) {
42
+ columnWrapper[`column${i}`] = [];
43
+ }
44
+
45
+ // divide children into columns
46
+ for (let i = 0; i < props.children.length; i += 1) {
47
+ const columnIndex = i % props.columns;
48
+ columnWrapper[`column${columnIndex}`].push(
49
+ <div
50
+ className='itemContainer'
51
+ key={i}
52
+ style={{
53
+ marginBottom: `${props.gap}px`
54
+ }}
55
+ >
56
+ { props.children[i] }
57
+ </div>
58
+ );
59
+ }
60
+
61
+ // wrap children in each column with a div
62
+ for (let i = 0; i < props.columns; i += 1) {
63
+ result.push(
64
+ <div
65
+ className='column'
66
+ key={i}
67
+ style={{
68
+ marginLeft: `${i > 0 ? props.gap : 0}px`
69
+ }}
70
+ >
71
+ { columnWrapper[`column${i}`] }
72
+ </div>
73
+ );
74
+ }
75
+
76
+ return (
77
+ <div
78
+ className='masonryGrid'
79
+ >
80
+ { result }
81
+ </div>
82
+ );
83
+ };
84
+
85
+ type TileProps = {
86
+ item: Item,
87
+ onClick?: (item: any) => void,
88
+ renderContent?: (item: Item) => Node
89
+ };
90
+
91
+ const HEIGHT_OFFSET = 350;
92
+ const MIN_HEIGHT = 175;
93
+
94
+ const MasonryTile = (props: TileProps) => {
95
+ const [dimmerActive, setDimmerActive] = useState(false);
96
+ const [height, setHeight] = useState(0);
97
+
98
+ /**
99
+ * Calculate the height of the tile once the component mounts.
100
+ */
101
+ useEffect(() => {
102
+ setHeight(MIN_HEIGHT + Math.ceil(Math.random() * HEIGHT_OFFSET));
103
+ }, []);
104
+
105
+ return (
106
+ <Dimmer.Dimmable
107
+ className='item'
108
+ dimmed={dimmerActive}
109
+ onClick={() => props.onClick && props.onClick(props.item)}
110
+ onMouseEnter={() => setDimmerActive(true)}
111
+ onMouseLeave={() => setDimmerActive(false)}
112
+ style={{
113
+ cursor: props.onClick ? 'pointer' : undefined,
114
+ height: `${height}px`
115
+ }}
116
+ >
117
+ <Transition
118
+ duration={800}
119
+ visible={dimmerActive}
120
+ >
121
+ <Dimmer.Inner
122
+ active={dimmerActive}
123
+ >
124
+ <Header
125
+ content={props.item.title}
126
+ inverted
127
+ subheader={props.item.subtitle}
128
+ />
129
+ { props.item.extra && (
130
+ <div
131
+ className='extraContainer'
132
+ >
133
+ { props.item.extra }
134
+ </div>
135
+ )}
136
+ </Dimmer.Inner>
137
+ </Transition>
138
+ <Image
139
+ alt={props.item.alt}
140
+ className='image'
141
+ src={props.item.image}
142
+ />
143
+ { props.renderContent && props.renderContent(props.item) }
144
+ </Dimmer.Dimmable>
145
+ );
146
+ };
147
+
148
+ type Props = {
149
+ columns: number,
150
+ gap: number,
151
+ items: Array<Item>,
152
+ loading?: boolean,
153
+ onClick?: (item: any) => void,
154
+ onPageChange?: (page: number) => void,
155
+ page: number,
156
+ pages: number,
157
+ renderContent?: (item: Item) => Node,
158
+ renderEmpty?: () => Node
159
+ };
160
+
161
+ const MasonryGrid = (props: Props) => (
162
+ <div
163
+ className='masonry-grid'
164
+ >
165
+ <Loader
166
+ active={props.loading}
167
+ />
168
+ <div>
169
+ <MasonryLayout
170
+ columns={props.columns}
171
+ gap={props.gap}
172
+ >
173
+ { _.map(props.items, (item, index) => (
174
+ <MasonryTile
175
+ key={index}
176
+ item={item}
177
+ onClick={props.onClick}
178
+ renderContent={props.renderContent}
179
+ />
180
+ ))}
181
+ </MasonryLayout>
182
+ { props.pages > 1 && props.onPageChange && (
183
+ <Segment
184
+ basic
185
+ textAlign='center'
186
+ >
187
+ <Pagination
188
+ activePage={props.page}
189
+ firstItem={null}
190
+ lastItem={null}
191
+ onPageChange={(e, { activePage }) => props.onPageChange && props.onPageChange(activePage)}
192
+ totalPages={props.pages}
193
+ size='small'
194
+ />
195
+ </Segment>
196
+ )}
197
+ </div>
198
+ { !props.loading && !(props.items && props.items.length) && props.renderEmpty && props.renderEmpty() }
199
+ </div>
200
+ );
201
+
202
+ export default MasonryGrid;
@@ -0,0 +1,148 @@
1
+ // @flow
2
+
3
+ import React, { useState, type Element } from 'react';
4
+ import {
5
+ Button,
6
+ Dimmer, Image,
7
+ Loader,
8
+ Transition
9
+ } from 'semantic-ui-react';
10
+ import PlayButton from './PlayButton';
11
+ import VideoPlayer from './VideoPlayer';
12
+ import VideoPlayerButton from './VideoPlayerButton';
13
+ import './MediaGallery.css';
14
+
15
+ type Item = {
16
+ image?: string,
17
+ embedded?: boolean,
18
+ preview?: string,
19
+ src?: string,
20
+ type: 'image' | 'video'
21
+ };
22
+
23
+ type Props = {
24
+ item: Item,
25
+ label?: string,
26
+ loading?: boolean,
27
+ onClose?: () => void,
28
+ onNext?: () => void,
29
+ onPrevious?: () => void,
30
+ renderContent?: () => Element<any>,
31
+ visible: boolean
32
+ };
33
+
34
+ const MediaGallery = (props: Props) => {
35
+ const [video, setVideo] = useState(false);
36
+
37
+ return (
38
+ <Dimmer
39
+ active={props.visible}
40
+ className='media-gallery mediaGallery'
41
+ page
42
+ >
43
+ { props.loading && (
44
+ <Loader
45
+ active
46
+ size='big'
47
+ />
48
+ )}
49
+ { props.item && (
50
+ <>
51
+ <Transition
52
+ animation='fade'
53
+ mountOnShow={false}
54
+ >
55
+ <div
56
+ className='imageContainer'
57
+ >
58
+ <Image
59
+ src={props.item.image}
60
+ style={{
61
+ objectFit: 'contain'
62
+ }}
63
+ />
64
+ { props.item.type === 'video' && (
65
+ <PlayButton
66
+ onClick={() => setVideo(true)}
67
+ style={{
68
+ position: 'absolute',
69
+ top: '50%'
70
+ }}
71
+ />
72
+ )}
73
+ </div>
74
+ </Transition>
75
+ { props.renderContent && props.renderContent() }
76
+ { props.onClose && (
77
+ <div
78
+ className='controls top right'
79
+ >
80
+ <Button
81
+ basic
82
+ circular
83
+ icon='times'
84
+ inverted
85
+ onClick={props.onClose}
86
+ size='large'
87
+ />
88
+ </div>
89
+ )}
90
+ { props.label && (
91
+ <div
92
+ className='controls top left'
93
+ >
94
+ <Button
95
+ basic
96
+ content={props.label}
97
+ inverted
98
+ size='large'
99
+ />
100
+ </div>
101
+ )}
102
+ { props.onPrevious && (
103
+ <div
104
+ className='controls middle left'
105
+ >
106
+ <Button
107
+ basic
108
+ disabled={!props.onPrevious}
109
+ inverted
110
+ icon='angle left'
111
+ onClick={props.onPrevious}
112
+ size='huge'
113
+ />
114
+ </div>
115
+ )}
116
+ { props.onNext && (
117
+ <div
118
+ className='controls middle right'
119
+ >
120
+ <Button
121
+ basic
122
+ disabled={!props.onNext}
123
+ inverted
124
+ icon='angle right'
125
+ onClick={props.onNext}
126
+ size='huge'
127
+ />
128
+ </div>
129
+ )}
130
+ { props.item.src && (
131
+ <VideoPlayer
132
+ autoPlay
133
+ embedded={props.item.embedded}
134
+ icon={<VideoPlayerButton />}
135
+ onClose={() => setVideo(false)}
136
+ open={video}
137
+ placeholder={props.item.preview}
138
+ size='huge'
139
+ video={props.item.src}
140
+ />
141
+ )}
142
+ </>
143
+ )}
144
+ </Dimmer>
145
+ );
146
+ };
147
+
148
+ export default MediaGallery;
@@ -0,0 +1,74 @@
1
+ // @flow
2
+
3
+ import React, { useEffect, useState } from 'react';
4
+ import { Image, Loader } from 'semantic-ui-react';
5
+ import _ from 'underscore';
6
+ import './MediaGrid.css';
7
+
8
+ type Item = {
9
+ image: string
10
+ };
11
+
12
+ type Props = {
13
+ items: Array<Item>,
14
+ loading?: boolean,
15
+ onClick?: (item: Item) => void
16
+ };
17
+
18
+ const MAX_IMAGES = 5;
19
+
20
+ const MediaGrid = (props: Props) => {
21
+ const [items, setItems] = useState([]);
22
+
23
+ /**
24
+ * Grab the list of image URLs to display and save it on the sate.
25
+ */
26
+ useEffect(() => {
27
+ const list = [];
28
+
29
+ for (let i = 0; i < props.items.length && list.length < MAX_IMAGES; i += 1) {
30
+ if (!_.isEmpty(props.items[i].image)) {
31
+ list.push(props.items[i]);
32
+ }
33
+ }
34
+
35
+ setItems(list);
36
+ }, [props.items]);
37
+
38
+ // Return null if no items are passed
39
+ if (_.isEmpty(props.items)) {
40
+ return null;
41
+ }
42
+
43
+ return (
44
+ <div
45
+ className='mediaGrid'
46
+ >
47
+ <Loader
48
+ active={props.loading}
49
+ />
50
+ <div
51
+ className={`grid${items.length}`}
52
+ >
53
+ { _.map(items, (item, index) => (
54
+ <div
55
+ className='mediaImage'
56
+ key={index}
57
+ >
58
+ <Image
59
+ onClick={() => props.onClick && props.onClick(item)}
60
+ src={item.image}
61
+ style={{
62
+ cursor: props.onClick ? 'pointer' : undefined,
63
+ objectFit: 'cover',
64
+ width: '100%'
65
+ }}
66
+ />
67
+ </div>
68
+ ))}
69
+ </div>
70
+ </div>
71
+ );
72
+ };
73
+
74
+ export default MediaGrid;
@@ -0,0 +1,98 @@
1
+ // @flow
2
+
3
+ import React, { type Element } from 'react';
4
+ import { InfiniteScroll } from '@performant-software/shared-components';
5
+ import { Card, Image } from 'semantic-ui-react';
6
+ import _ from 'underscore';
7
+ import ListLoader from './ListLoader';
8
+ import PlayButton from './PlayButton';
9
+ import './MediaList.css';
10
+
11
+ type Item = {
12
+ ...any,
13
+ image: string,
14
+ type: 'image' | 'video'
15
+ };
16
+
17
+ type Props = {
18
+ items: Array<Item>,
19
+ loading?: boolean,
20
+ onBottomReached: () => void,
21
+ onClick: (item: any, index: number) => void,
22
+ renderDescription?: (item: Item) => Element<any>,
23
+ renderExtra?: (item: Item) => Element<any>,
24
+ renderHeader?: (item: Item) => Element<any>,
25
+ renderMeta?: (item: Item) => Element<any>,
26
+ textAlign?: 'left' | 'center' | 'right'
27
+ };
28
+
29
+ const MediaList = (props: Props) => (
30
+ <InfiniteScroll
31
+ offset={100}
32
+ onBottomReached={props.onBottomReached}
33
+ >
34
+ <ListLoader
35
+ active={props.loading || false}
36
+ />
37
+ <Card.Group
38
+ doubling
39
+ stackable
40
+ >
41
+ { _.map(props.items, (item, i) => (
42
+ <Card
43
+ onClick={props.onClick.bind(this, item, i)}
44
+ >
45
+ <Image
46
+ src={item.image}
47
+ style={{
48
+ objectFit: 'cover'
49
+ }}
50
+ />
51
+ { item.type === 'video' && (
52
+ <div
53
+ style={{
54
+ position: 'absolute',
55
+ top: '50%',
56
+ left: '50%',
57
+ transform: 'translate(-50%, -50%)'
58
+ }}
59
+ >
60
+ <PlayButton />
61
+ </div>
62
+ )}
63
+ { (props.renderMeta || props.renderHeader || props.renderDescription) && (
64
+ <Card.Content>
65
+ { props.renderHeader && (
66
+ <Card.Header
67
+ content={props.renderHeader(item)}
68
+ textAlign={props.textAlign}
69
+ />
70
+ )}
71
+ { props.renderMeta && (
72
+ <Card.Meta
73
+ content={props.renderMeta(item)}
74
+ textAlign={props.textAlign}
75
+ />
76
+ )}
77
+ { props.renderDescription && (
78
+ <Card.Description
79
+ content={props.renderDescription(item)}
80
+ textAlign={props.textAlign}
81
+ />
82
+ )}
83
+ </Card.Content>
84
+ )}
85
+ { props.renderExtra && (
86
+ <Card.Content
87
+ content={props.renderExtra(item)}
88
+ extra
89
+ textAlign={props.textAlign}
90
+ />
91
+ )}
92
+ </Card>
93
+ ))}
94
+ </Card.Group>
95
+ </InfiniteScroll>
96
+ );
97
+
98
+ export default MediaList;
@@ -0,0 +1,77 @@
1
+ // @flow
2
+
3
+ import React, { useCallback } from 'react';
4
+ import {
5
+ Dropdown,
6
+ Menu,
7
+ type DropdownItemProps,
8
+ type HeaderProps,
9
+ type MenuProps
10
+ } from 'semantic-ui-react';
11
+ import _ from 'underscore';
12
+ import DropdownMenu from './DropdownMenu';
13
+
14
+ type Props = MenuProps & {
15
+ header: HeaderProps,
16
+ items: Array<DropdownItemProps | MenuProps>
17
+ };
18
+
19
+ const MenuBar = ({ header, items, ...props }: Props) => {
20
+ /**
21
+ * Renders the passed item as a dropdown item.
22
+ *
23
+ * @param item
24
+ *
25
+ * @returns {JSX.Element}
26
+ */
27
+ const renderDropdownItem = useCallback((item) => (
28
+ <Dropdown.Item {...item} />
29
+ ), []);
30
+
31
+ /**
32
+ * Renders the passed item as a menu item.
33
+ *
34
+ * @returns {JSX.Element}
35
+ */
36
+ const renderMenuItem = useCallback((item) => (
37
+ <Menu.Item
38
+ {...item}
39
+ />
40
+ ), []);
41
+
42
+ /**
43
+ * Renders the passed item as a dropdown menu.
44
+ *
45
+ * @returns {JSX.Element}
46
+ */
47
+ const renderDropdown = useCallback((item) => (
48
+ <DropdownMenu
49
+ item
50
+ text={item.content}
51
+ >
52
+ { _.map(item.items, (i) => (i.items ? renderDropdown(i) : renderDropdownItem(i)))}
53
+ </DropdownMenu>
54
+ ), []);
55
+
56
+ /**
57
+ * Renders the passed item.
58
+ *
59
+ * @returns {JSX.Element}
60
+ */
61
+ const renderItem = useCallback((item) => (item.items ? renderDropdown(item) : renderMenuItem(item)), []);
62
+
63
+ return (
64
+ <Menu
65
+ {...props}
66
+ className='menu-bar'
67
+ >
68
+ <Menu.Item
69
+ {...header}
70
+ header
71
+ />
72
+ { _.map(items, (item) => renderItem(item)) }
73
+ </Menu>
74
+ );
75
+ };
76
+
77
+ export default MenuBar;
@@ -0,0 +1,72 @@
1
+ // @flow
2
+
3
+ import React, { useCallback } from 'react';
4
+ import {
5
+ Header,
6
+ Menu,
7
+ type HeaderProps,
8
+ type MenuItemProps, Ref
9
+ } from 'semantic-ui-react';
10
+ import _ from 'underscore';
11
+
12
+ type Props = {
13
+ header: HeaderProps,
14
+ items: Array<HeaderProps | MenuItemProps>
15
+ };
16
+
17
+ const MenuSidebar = ({ header, items, ...props }: Props) => {
18
+ /**
19
+ * Renders the passed item as a menu.
20
+ *
21
+ * @type {unknown}
22
+ */
23
+ const renderMenu = useCallback((item) => (
24
+ <Menu.Item>
25
+ <Menu.Header
26
+ {...item}
27
+ />
28
+ <Menu.Menu>
29
+ { _.map(item.items, (i) => renderItem(i)) }
30
+ </Menu.Menu>
31
+ </Menu.Item>
32
+ ), []);
33
+
34
+ /**
35
+ * Renders the passed item as a menu item.
36
+ *
37
+ * @type {unknown}
38
+ */
39
+ const renderMenuItem = useCallback((item) => (
40
+ <Menu.Item
41
+ {...item}
42
+ />
43
+ ), []);
44
+
45
+ /**
46
+ * Renders the passed item.
47
+ *
48
+ * @type {function(*): *}
49
+ */
50
+ const renderItem = useCallback((item) => (item.items ? renderMenu(item) : renderMenuItem(item)), []);
51
+
52
+ return (
53
+ <Ref
54
+ innerRef={props.contextRef}
55
+ >
56
+ <Menu
57
+ {...props}
58
+ fixed='left'
59
+ vertical
60
+ >
61
+ <Menu.Item>
62
+ <Header
63
+ {...header}
64
+ />
65
+ </Menu.Item>
66
+ { _.map(items, (item) => renderItem(item)) }
67
+ </Menu>
68
+ </Ref>
69
+ );
70
+ };
71
+
72
+ export default MenuSidebar;