@performant-software/semantic-components 0.5.15 → 0.5.16-beta.10

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 (37) hide show
  1. package/build/index.js +1 -1
  2. package/build/index.js.map +1 -1
  3. package/build/main.css +43 -0
  4. package/package.json +3 -3
  5. package/src/components/AudioPlayer.css +3 -0
  6. package/src/components/AudioPlayer.js +54 -0
  7. package/src/components/DownloadButton.js +63 -12
  8. package/src/components/IIIFModal.js +26 -0
  9. package/src/components/LazyAudio.css +23 -0
  10. package/src/components/LazyAudio.js +150 -0
  11. package/src/components/LazyDocument.css +2 -0
  12. package/src/components/LazyDocument.js +29 -20
  13. package/src/components/LazyIIIF.js +44 -0
  14. package/src/components/LazyImage.css +3 -1
  15. package/src/components/LazyImage.js +40 -11
  16. package/src/components/LazyLoader.css +7 -0
  17. package/src/components/LazyLoader.js +28 -0
  18. package/src/components/LazyMedia.js +244 -0
  19. package/src/components/LazyVideo.css +8 -1
  20. package/src/components/LazyVideo.js +40 -5
  21. package/src/components/PhotoViewer.js +38 -25
  22. package/src/components/VideoPlayer.js +23 -2
  23. package/src/i18n/en.json +35 -3
  24. package/src/index.js +5 -0
  25. package/types/components/AudioPlayer.js.flow +54 -0
  26. package/types/components/DownloadButton.js.flow +63 -12
  27. package/types/components/IIIFModal.js.flow +26 -0
  28. package/types/components/LazyAudio.js.flow +150 -0
  29. package/types/components/LazyDocument.js.flow +29 -20
  30. package/types/components/LazyIIIF.js.flow +44 -0
  31. package/types/components/LazyImage.js.flow +40 -11
  32. package/types/components/LazyLoader.js.flow +28 -0
  33. package/types/components/LazyMedia.js.flow +244 -0
  34. package/types/components/LazyVideo.js.flow +40 -5
  35. package/types/components/PhotoViewer.js.flow +38 -25
  36. package/types/components/VideoPlayer.js.flow +23 -2
  37. package/types/index.js.flow +5 -0
package/src/i18n/en.json CHANGED
@@ -8,15 +8,26 @@
8
8
  "AccordionSelector": {
9
9
  "title": "Select Items"
10
10
  },
11
+ "AudioPlayer": {
12
+ "errors": {
13
+ "path": {
14
+ "content": "Please check the audio path: {{path}}",
15
+ "header": "There was a problem loading the audio"
16
+ }
17
+ }
18
+ },
11
19
  "Common": {
12
20
  "buttons": {
13
21
  "add": "Add",
14
22
  "cancel": "Cancel",
15
23
  "clear": "Clear",
16
24
  "close": "Close",
25
+ "download": "Download",
17
26
  "edit": "Edit",
18
27
  "ok": "OK",
19
- "save": "Save"
28
+ "open": "Open",
29
+ "save": "Save",
30
+ "upload": "Upload"
20
31
  },
21
32
  "errors": {
22
33
  "title": "Oops!"
@@ -135,9 +146,9 @@
135
146
  "value": "Value"
136
147
  }
137
148
  },
138
- "LazyDocument": {
149
+ "LazyAudio": {
139
150
  "buttons": {
140
- "download": "Download"
151
+ "play": "Play"
141
152
  }
142
153
  },
143
154
  "LazyImage": {
@@ -145,6 +156,11 @@
145
156
  "view": "View image"
146
157
  }
147
158
  },
159
+ "LazyMedia": {
160
+ "messages": {
161
+ "uploaded": "Your <bold>{{type}}</bold> has been received"
162
+ }
163
+ },
148
164
  "LazyVideo": {
149
165
  "buttons": {
150
166
  "play": "Play video"
@@ -193,6 +209,14 @@
193
209
  "loginErrorHeader": "Invalid Credentials",
194
210
  "password": "Password"
195
211
  },
212
+ "PhotoViewer": {
213
+ "errors": {
214
+ "path": {
215
+ "content": "Please check the image path: {{path}}",
216
+ "header": "There was a problem loading the image"
217
+ }
218
+ }
219
+ },
196
220
  "ReferenceCodeFormLabel": {
197
221
  "content": "The values in this list can be edited via the {{name}} reference table."
198
222
  },
@@ -240,6 +264,14 @@
240
264
  },
241
265
  "title": "Select Frame"
242
266
  },
267
+ "VideoPlayer": {
268
+ "errors": {
269
+ "path": {
270
+ "content": "Please check the video path: {{path}}",
271
+ "header": "There was a problem loading the video"
272
+ }
273
+ }
274
+ },
243
275
  "ViewXML": {
244
276
  "buttons": {
245
277
  "view": "View XML"
package/src/index.js CHANGED
@@ -9,6 +9,7 @@ export { default as AccordionList } from './components/AccordionList';
9
9
  export { default as AccordionSelector } from './components/AccordionSelector';
10
10
  export { default as ArrowButtons } from './components/ArrowButtons';
11
11
  export { default as AssociatedDropdown } from './components/AssociatedDropdown';
12
+ export { default as AudioPlayer } from './components/AudioPlayer';
12
13
  export { default as BooleanIcon } from './components/BooleanIcon';
13
14
  export { default as CancelButton } from './components/CancelButton';
14
15
  export { default as ColorButton } from './components/ColorButton';
@@ -34,13 +35,17 @@ export { default as FuzzyDate } from './components/FuzzyDate';
34
35
  export { default as GoogleMap } from './components/GoogleMap';
35
36
  export { default as GooglePlacesSearch } from './components/GooglePlacesSearch';
36
37
  export { default as HorizontalCards } from './components/HorizontalCards';
38
+ export { default as IIIFModal } from './components/IIIFModal';
37
39
  export { default as ItemCollection } from './components/ItemCollection';
38
40
  export { default as ItemList } from './components/ItemList';
39
41
  export { default as Items } from './components/Items';
40
42
  export { default as KeyboardField } from './components/KeyboardField';
41
43
  export { default as KeyValuePairs } from './components/KeyValuePairs';
44
+ export { default as LazyAudio } from './components/LazyAudio';
42
45
  export { default as LazyDocument } from './components/LazyDocument';
46
+ export { default as LazyIIIF } from './components/LazyIIIF';
43
47
  export { default as LazyImage } from './components/LazyImage';
48
+ export { default as LazyMedia } from './components/LazyMedia';
44
49
  export { default as LazyVideo } from './components/LazyVideo';
45
50
  export { default as LinkButton } from './components/LinkButton';
46
51
  export { default as ListFilters } from './components/ListFilters';
@@ -0,0 +1,54 @@
1
+ // @flow
2
+
3
+ import React, { useState } from 'react';
4
+ import { Button, Message, Modal } from 'semantic-ui-react';
5
+ import i18n from '../i18n/i18n';
6
+ import ModalContext from '../context/ModalContext';
7
+ import './AudioPlayer.css';
8
+
9
+ type Props = {
10
+ centered?: boolean,
11
+ onClose: () => void,
12
+ open: boolean,
13
+ src: string
14
+ };
15
+
16
+ const AudioPlayer = (props: Props) => {
17
+ const [error, setError] = useState(false);
18
+
19
+ return (
20
+ <ModalContext.Consumer>
21
+ {(mountNode) => (
22
+ <Modal
23
+ centered={props.centered}
24
+ className='audio-player'
25
+ mountNode={mountNode}
26
+ open={props.open}
27
+ >
28
+ <Modal.Content>
29
+ { error && (
30
+ <Message
31
+ content={i18n.t('AudioPlayer.errors.path.content', { path: props.src })}
32
+ header={i18n.t('AudioPlayer.errors.path.header')}
33
+ icon='exclamation circle'
34
+ />
35
+ )}
36
+ <audio
37
+ controls
38
+ onError={() => setError(true)}
39
+ src={props.src}
40
+ />
41
+ </Modal.Content>
42
+ <Modal.Actions>
43
+ <Button
44
+ content={i18n.t('Common.buttons.close')}
45
+ onClick={props.onClose}
46
+ />
47
+ </Modal.Actions>
48
+ </Modal>
49
+ )}
50
+ </ModalContext.Consumer>
51
+ );
52
+ };
53
+
54
+ export default AudioPlayer;
@@ -1,23 +1,74 @@
1
1
  // @flow
2
2
 
3
- import React from 'react';
3
+ import React, { useMemo } from 'react';
4
4
  import uuid from 'react-uuid';
5
- import { Button } from 'semantic-ui-react';
5
+ import { Icon } from 'semantic-ui-react';
6
+ import i18n from '../i18n/i18n';
6
7
 
7
8
  type Props = {
9
+ basic?: boolean,
10
+ className?: string,
11
+ color?: string,
12
+ compact?: boolean,
8
13
  filename?: string,
14
+ primary?: boolean,
15
+ size?: string,
16
+ secondary?: boolean,
9
17
  url: string
10
18
  };
11
19
 
12
- const DownloadButton = ({ filename, url, ...button }: Props) => (
13
- <a
14
- download={filename || uuid()}
15
- href={url}
16
- >
17
- <Button
18
- {...button}
19
- />
20
- </a>
21
- );
20
+ const DownloadButton = (props: Props) => {
21
+ /**
22
+ * Sets the appropriate class names based on the formatting props.
23
+ *
24
+ * @type {string}
25
+ */
26
+ const className = useMemo(() => {
27
+ const classNames = ['ui', 'button'];
28
+
29
+ if (props.basic) {
30
+ classNames.push('basic');
31
+ }
32
+
33
+ if (props.className) {
34
+ classNames.push(...props.className.split(' '));
35
+ }
36
+
37
+ if (props.color) {
38
+ classNames.push(props.color);
39
+ }
40
+
41
+ if (props.compact) {
42
+ classNames.push('compact');
43
+ }
44
+
45
+ if (props.primary) {
46
+ classNames.push('primary');
47
+ }
48
+
49
+ if (props.secondary) {
50
+ classNames.push('secondary');
51
+ }
52
+
53
+ if (props.size) {
54
+ classNames.push(props.size);
55
+ }
56
+
57
+ return classNames.join(' ');
58
+ }, [props.basic, props.color]);
59
+
60
+ return (
61
+ <a
62
+ className={className}
63
+ download={props.filename || uuid()}
64
+ href={props.url}
65
+ >
66
+ <Icon
67
+ name='cloud download'
68
+ />
69
+ { i18n.t('Common.buttons.download') }
70
+ </a>
71
+ );
72
+ };
22
73
 
23
74
  export default DownloadButton;
@@ -0,0 +1,26 @@
1
+ // @flow
2
+
3
+ import React from 'react';
4
+ import { Modal } from 'semantic-ui-react';
5
+ import { IIIFViewer } from '@performant-software/shared-components';
6
+
7
+ type Props = {
8
+ onClose: () => void
9
+ };
10
+
11
+ const IIIFModal = ({ onClose, ...props }: Props) => (
12
+ <Modal
13
+ centered={false}
14
+ closeIcon
15
+ onClose={onClose}
16
+ open
17
+ >
18
+ <Modal.Content>
19
+ <IIIFViewer
20
+ {...props}
21
+ />
22
+ </Modal.Content>
23
+ </Modal>
24
+ );
25
+
26
+ export default IIIFModal;
@@ -0,0 +1,150 @@
1
+ // @flow
2
+
3
+ import React, { useState, type Node } from 'react';
4
+ import {
5
+ Button,
6
+ Dimmer,
7
+ Icon,
8
+ Image,
9
+ Loader,
10
+ Segment,
11
+ Transition,
12
+ Visibility
13
+ } from 'semantic-ui-react';
14
+ import i18n from '../i18n/i18n';
15
+ import AudioPlayer from './AudioPlayer';
16
+ import DownloadButton from './DownloadButton';
17
+ import LazyLoader from './LazyLoader';
18
+ import './LazyAudio.css';
19
+
20
+ type Props = {
21
+ children?: Node,
22
+ dimmable: boolean,
23
+ download?: string,
24
+ duration?: number,
25
+ image?: any,
26
+ name?: string,
27
+ preview?: string,
28
+ size?: string,
29
+ src?: string
30
+ };
31
+
32
+ const LazyAudio = (props: Props) => {
33
+ const [dimmer, setDimmer] = useState(false);
34
+ const [error, setError] = useState(false);
35
+ const [loaded, setLoaded] = useState(!props.preview);
36
+ const [modal, setModal] = useState(false);
37
+ const [visible, setVisible] = useState(false);
38
+
39
+ if (!visible) {
40
+ return (
41
+ <Visibility
42
+ as='span'
43
+ fireOnMount
44
+ onTopVisible={() => setVisible(true)}
45
+ >
46
+ <Loader
47
+ active
48
+ inline='centered'
49
+ size={props.size}
50
+ />
51
+ </Visibility>
52
+ );
53
+ }
54
+
55
+ return (
56
+ <>
57
+ <Transition
58
+ duration={props.duration}
59
+ visible
60
+ >
61
+ <Dimmer.Dimmable
62
+ as={Segment}
63
+ className='lazy-audio'
64
+ compact
65
+ onBlur={() => setDimmer(false)}
66
+ onMouseEnter={() => setDimmer(true)}
67
+ onMouseLeave={() => setDimmer(false)}
68
+ >
69
+ { !loaded && (
70
+ <LazyLoader
71
+ active
72
+ size={props.size}
73
+ />
74
+ )}
75
+ { !error && props.preview && (
76
+ <Image
77
+ {...props.image}
78
+ onError={() => {
79
+ setError(true);
80
+ setLoaded(true);
81
+ }}
82
+ onLoad={() => {
83
+ setError(false);
84
+ setLoaded(true);
85
+ }}
86
+ size={props.size}
87
+ src={props.preview}
88
+ />
89
+ )}
90
+ { (error || !props.preview) && (
91
+ <Image
92
+ {...props.image}
93
+ className='placeholder-image'
94
+ size={props.size}
95
+ >
96
+ <Icon
97
+ name='file audio outline'
98
+ size='big'
99
+ />
100
+ </Image>
101
+ )}
102
+ { (props.src || props.children) && props.dimmable && (
103
+ <Dimmer
104
+ active={dimmer}
105
+ >
106
+ <div
107
+ className='buttons'
108
+ >
109
+ { props.src && (
110
+ <Button
111
+ content={i18n.t('LazyAudio.buttons.play')}
112
+ icon='play circle outline'
113
+ onClick={() => setModal(true)}
114
+ primary
115
+ />
116
+ )}
117
+ { props.download && (
118
+ <DownloadButton
119
+ color='green'
120
+ filename={props.name}
121
+ url={props.download}
122
+ />
123
+ )}
124
+ { props.children }
125
+ </div>
126
+ </Dimmer>
127
+ )}
128
+ </Dimmer.Dimmable>
129
+ </Transition>
130
+ { props.src && (
131
+ <AudioPlayer
132
+ onClose={() => setModal(false)}
133
+ open={modal}
134
+ size='large'
135
+ src={props.src}
136
+ />
137
+ )}
138
+ </>
139
+ );
140
+ };
141
+
142
+ LazyAudio.defaultProps = {
143
+ dimmable: true,
144
+ duration: 1000,
145
+ preview: undefined,
146
+ size: 'medium',
147
+ src: undefined
148
+ };
149
+
150
+ export default LazyAudio;
@@ -1,6 +1,6 @@
1
1
  // @flow
2
2
 
3
- import React, { useState, useEffect, type Node } from 'react';
3
+ import React, { useState, type Node } from 'react';
4
4
  import { Document, Page } from 'react-pdf/dist/esm/entry.webpack';
5
5
  import {
6
6
  Dimmer,
@@ -11,32 +11,27 @@ import {
11
11
  Transition,
12
12
  Visibility
13
13
  } from 'semantic-ui-react';
14
- import i18n from '../i18n/i18n';
15
14
  import DownloadButton from './DownloadButton';
15
+ import LazyLoader from './LazyLoader';
16
16
  import './LazyDocument.css';
17
17
 
18
18
  type Props = {
19
19
  children?: Node,
20
20
  dimmable?: boolean,
21
+ download?: string,
21
22
  duration?: number,
22
23
  image?: any,
24
+ pdf?: boolean,
23
25
  preview?: ?string,
24
26
  size?: string,
25
27
  src?: string
26
28
  };
27
29
 
28
30
  const LazyDocument = (props: Props) => {
29
- const [visible, setVisible] = useState(false);
30
31
  const [dimmer, setDimmer] = useState(false);
31
- const [contentType, setContentType] = useState('');
32
-
33
- useEffect(() => {
34
- if (props.src && !props.preview) {
35
- fetch(props.src)
36
- .then((response) => response.blob())
37
- .then((blob) => setContentType(blob.type));
38
- }
39
- }, [props.preview, props.src]);
32
+ const [error, setError] = useState(false);
33
+ const [loaded, setLoaded] = useState(!props.preview);
34
+ const [visible, setVisible] = useState(false);
40
35
 
41
36
  if (!visible) {
42
37
  return (
@@ -68,20 +63,35 @@ const LazyDocument = (props: Props) => {
68
63
  onMouseEnter={() => setDimmer(true)}
69
64
  onMouseLeave={() => setDimmer(false)}
70
65
  >
71
- { props.preview && (
66
+ { !loaded && (
67
+ <LazyLoader
68
+ active
69
+ size={props.size}
70
+ />
71
+ )}
72
+ { !error && props.preview && (
72
73
  <Image
73
74
  {...props.image}
75
+ onError={() => {
76
+ setError(true);
77
+ setLoaded(true);
78
+ }}
79
+ onLoad={() => {
80
+ setError(false);
81
+ setLoaded(true);
82
+ }}
74
83
  src={props.preview}
75
84
  size={props.size}
76
85
  />
77
86
  )}
78
- { !props.preview && props.src && contentType === 'application/pdf' && (
87
+ { !error && loaded && !props.preview && props.src && props.pdf && (
79
88
  <Image
80
89
  {...props.image}
81
90
  size={props.size}
82
91
  >
83
92
  <Document
84
93
  file={props.src}
94
+ onLoadError={(e) => console.log(e.message)}
85
95
  >
86
96
  <Page
87
97
  pageNumber={1}
@@ -89,7 +99,7 @@ const LazyDocument = (props: Props) => {
89
99
  </Document>
90
100
  </Image>
91
101
  )}
92
- { !props.preview && (!props.src || contentType !== 'application/pdf') && (
102
+ { (error || (!props.preview && !(props.src && props.pdf))) && (
93
103
  <Image
94
104
  {...props.image}
95
105
  className='placeholder-image'
@@ -101,19 +111,17 @@ const LazyDocument = (props: Props) => {
101
111
  />
102
112
  </Image>
103
113
  )}
104
- { (props.src || props.children) && props.dimmable && (
114
+ { (props.download || props.src || props.children) && props.dimmable && (
105
115
  <Dimmer
106
116
  active={dimmer}
107
117
  >
108
118
  <div
109
119
  className='buttons'
110
120
  >
111
- { props.src && (
121
+ { props.download && (
112
122
  <DownloadButton
113
- content={i18n.t('LazyDocument.buttons.download')}
114
- icon='cloud download'
115
123
  primary
116
- url={props.src || ''}
124
+ url={props.download || props.src}
117
125
  />
118
126
  )}
119
127
  { props.children }
@@ -129,6 +137,7 @@ const LazyDocument = (props: Props) => {
129
137
  LazyDocument.defaultProps = {
130
138
  dimmable: true,
131
139
  duration: 1000,
140
+ pdf: false,
132
141
  preview: undefined,
133
142
  size: 'medium',
134
143
  src: undefined
@@ -0,0 +1,44 @@
1
+ // @flow
2
+
3
+ import React, { useState, type Node } from 'react';
4
+ import { Button } from 'semantic-ui-react';
5
+ import _ from 'underscore';
6
+ import i18n from '../i18n/i18n';
7
+ import IIIFModal from './IIIFModal';
8
+ import LazyMedia from './LazyMedia';
9
+
10
+ type Props = {
11
+ children?: Node,
12
+ manifest?: string,
13
+ options?: any
14
+ };
15
+
16
+ const LazyIIIF = ({ manifest, options = {}, ...props }: Props) => {
17
+ const [modal, setModal] = useState(false);
18
+
19
+ return (
20
+ <>
21
+ <LazyMedia
22
+ {...props}
23
+ >
24
+ { manifest && (
25
+ <Button
26
+ content={i18n.t('Common.buttons.open')}
27
+ icon='images outline'
28
+ onClick={() => setModal(true)}
29
+ />
30
+ )}
31
+ { props.children }
32
+ </LazyMedia>
33
+ { modal && (
34
+ <IIIFModal
35
+ manifestId={manifest}
36
+ onClose={() => setModal(false)}
37
+ options={_.defaults(options, { showIIIFBadge: false })}
38
+ />
39
+ )}
40
+ </>
41
+ );
42
+ };
43
+
44
+ export default LazyIIIF;
@@ -12,23 +12,29 @@ import {
12
12
  Visibility
13
13
  } from 'semantic-ui-react';
14
14
  import i18n from '../i18n/i18n';
15
+ import DownloadButton from './DownloadButton';
16
+ import LazyLoader from './LazyLoader';
15
17
  import PhotoViewer from './PhotoViewer';
16
18
  import './LazyImage.css';
17
19
 
18
20
  type Props = {
19
21
  children?: Node,
20
22
  dimmable: boolean,
23
+ download?: string,
21
24
  duration?: number,
22
25
  image?: any,
26
+ name?: string,
23
27
  preview?: string,
24
28
  size?: string,
25
29
  src?: string
26
30
  };
27
31
 
28
32
  const LazyImage = (props: Props) => {
29
- const [visible, setVisible] = useState(false);
30
- const [modal, setModal] = useState(false);
31
33
  const [dimmer, setDimmer] = useState(false);
34
+ const [error, setError] = useState(false);
35
+ const [loaded, setLoaded] = useState(!(props.src || props.preview));
36
+ const [modal, setModal] = useState(false);
37
+ const [visible, setVisible] = useState(false);
32
38
 
33
39
  if (!visible) {
34
40
  return (
@@ -60,14 +66,28 @@ const LazyImage = (props: Props) => {
60
66
  onMouseEnter={() => setDimmer(true)}
61
67
  onMouseLeave={() => setDimmer(false)}
62
68
  >
63
- { props.src && (
69
+ { !loaded && (
70
+ <LazyLoader
71
+ active
72
+ size={props.size}
73
+ />
74
+ )}
75
+ { !error && (props.preview || props.src) && (
64
76
  <Image
65
77
  {...props.image}
78
+ onError={() => {
79
+ setError(true);
80
+ setLoaded(true);
81
+ }}
82
+ onLoad={() => {
83
+ setError(false);
84
+ setLoaded(true);
85
+ }}
66
86
  size={props.size}
67
87
  src={props.preview || props.src}
68
88
  />
69
89
  )}
70
- { !props.src && (
90
+ { (error || !(props.preview || props.src)) && (
71
91
  <Image
72
92
  {...props.image}
73
93
  className='placeholder-image'
@@ -79,7 +99,7 @@ const LazyImage = (props: Props) => {
79
99
  />
80
100
  </Image>
81
101
  )}
82
- { (props.src || props.children) && props.dimmable && (
102
+ { !error && (props.src || props.children) && props.dimmable && (
83
103
  <Dimmer
84
104
  active={dimmer}
85
105
  >
@@ -94,18 +114,27 @@ const LazyImage = (props: Props) => {
94
114
  primary
95
115
  />
96
116
  )}
117
+ { props.download && (
118
+ <DownloadButton
119
+ color='green'
120
+ filename={props.name}
121
+ url={props.download}
122
+ />
123
+ )}
97
124
  { props.children }
98
125
  </div>
99
126
  </Dimmer>
100
127
  )}
101
128
  </Dimmer.Dimmable>
102
129
  </Transition>
103
- <PhotoViewer
104
- image={props.src || ''}
105
- onClose={() => setModal(false)}
106
- open={modal}
107
- size='large'
108
- />
130
+ { props.src && (
131
+ <PhotoViewer
132
+ image={props.src}
133
+ onClose={() => setModal(false)}
134
+ open={modal}
135
+ size='large'
136
+ />
137
+ )}
109
138
  </>
110
139
  );
111
140
  };