@performant-software/semantic-components 0.5.14 → 0.5.16-beta.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.
@@ -1,9 +1,10 @@
1
1
  // @flow
2
2
 
3
- import React from 'react';
4
- import { Image, Modal } from 'semantic-ui-react';
3
+ import React, { useState } from 'react';
4
+ import { Image, Message, Modal } from 'semantic-ui-react';
5
5
  import ModalContext from '../context/ModalContext';
6
6
  import './PhotoViewer.css';
7
+ import i18n from '../i18n/i18n';
7
8
 
8
9
  type Props = {
9
10
  alt?: string,
@@ -13,29 +14,41 @@ type Props = {
13
14
  size?: string
14
15
  };
15
16
 
16
- const PhotoViewer = (props: Props) => (
17
- <ModalContext.Consumer>
18
- { (mountNode) => (
19
- <Modal
20
- centered={false}
21
- className='photo-viewer'
22
- closeIcon
23
- mountNode={mountNode}
24
- onClose={props.onClose.bind(this)}
25
- open={props.open}
26
- size={props.size}
27
- >
28
- <Modal.Content>
29
- <Image
30
- alt={props.alt}
31
- fluid
32
- src={props.image}
33
- />
34
- </Modal.Content>
35
- </Modal>
36
- )}
37
- </ModalContext.Consumer>
38
- );
17
+ const PhotoViewer = (props: Props) => {
18
+ const [error, setError] = useState(false);
19
+
20
+ return (
21
+ <ModalContext.Consumer>
22
+ {(mountNode) => (
23
+ <Modal
24
+ centered={false}
25
+ className='photo-viewer'
26
+ closeIcon
27
+ mountNode={mountNode}
28
+ onClose={props.onClose.bind(this)}
29
+ open={props.open}
30
+ size={props.size}
31
+ >
32
+ <Modal.Content>
33
+ { error && (
34
+ <Message
35
+ content={i18n.t('PhotoViewer.errors.path.content', { path: props.image })}
36
+ header={i18n.t('PhotoViewer.errors.path.header')}
37
+ icon='exclamation circle'
38
+ />
39
+ )}
40
+ <Image
41
+ alt={props.alt}
42
+ fluid
43
+ onError={() => setError(true)}
44
+ src={props.image}
45
+ />
46
+ </Modal.Content>
47
+ </Modal>
48
+ )}
49
+ </ModalContext.Consumer>
50
+ );
51
+ };
39
52
 
40
53
  PhotoViewer.defaultProps = {
41
54
  size: 'small'
@@ -1,9 +1,20 @@
1
1
  // @flow
2
2
 
3
- import React, { type Element, useEffect, useRef } from 'react';
4
- import { Embed, Modal, Ref } from 'semantic-ui-react';
3
+ import React, {
4
+ useEffect,
5
+ useRef,
6
+ useState,
7
+ type Element
8
+ } from 'react';
9
+ import {
10
+ Embed,
11
+ Message,
12
+ Modal,
13
+ Ref
14
+ } from 'semantic-ui-react';
5
15
  import ModalContext from '../context/ModalContext';
6
16
  import './VideoPlayer.css';
17
+ import i18n from '../i18n/i18n';
7
18
 
8
19
  type Props = {
9
20
  autoPlay?: boolean,
@@ -18,6 +29,8 @@ type Props = {
18
29
  };
19
30
 
20
31
  const VideoPlayer = (props: Props) => {
32
+ const [error, setError] = useState(false);
33
+
21
34
  const embedRef = useRef();
22
35
 
23
36
  /**
@@ -46,6 +59,13 @@ const VideoPlayer = (props: Props) => {
46
59
  size={props.size}
47
60
  >
48
61
  <Modal.Content>
62
+ { error && (
63
+ <Message
64
+ content={i18n.t('VideoPlayer.errors.path.content', { path: props.video })}
65
+ header={i18n.t('VideoPlayer.errors.path.header')}
66
+ icon='exclamation circle'
67
+ />
68
+ )}
49
69
  { props.embedded && (
50
70
  <Ref
51
71
  innerRef={embedRef}
@@ -63,6 +83,7 @@ const VideoPlayer = (props: Props) => {
63
83
  <video
64
84
  autoPlay={props.autoPlay}
65
85
  controls
86
+ onError={() => setError(true)}
66
87
  src={props.video}
67
88
  />
68
89
  )}
package/src/i18n/en.json CHANGED
@@ -8,6 +8,14 @@
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",
@@ -135,6 +143,11 @@
135
143
  "value": "Value"
136
144
  }
137
145
  },
146
+ "LazyAudio": {
147
+ "buttons": {
148
+ "play": "Play"
149
+ }
150
+ },
138
151
  "LazyDocument": {
139
152
  "buttons": {
140
153
  "download": "Download"
@@ -193,6 +206,14 @@
193
206
  "loginErrorHeader": "Invalid Credentials",
194
207
  "password": "Password"
195
208
  },
209
+ "PhotoViewer": {
210
+ "errors": {
211
+ "path": {
212
+ "content": "Please check the image path: {{path}}",
213
+ "header": "There was a problem loading the image"
214
+ }
215
+ }
216
+ },
196
217
  "ReferenceCodeFormLabel": {
197
218
  "content": "The values in this list can be edited via the {{name}} reference table."
198
219
  },
@@ -240,6 +261,14 @@
240
261
  },
241
262
  "title": "Select Frame"
242
263
  },
264
+ "VideoPlayer": {
265
+ "errors": {
266
+ "path": {
267
+ "content": "Please check the video path: {{path}}",
268
+ "header": "There was a problem loading the video"
269
+ }
270
+ }
271
+ },
243
272
  "ViewXML": {
244
273
  "buttons": {
245
274
  "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';
@@ -39,6 +40,7 @@ export { default as ItemList } from './components/ItemList';
39
40
  export { default as Items } from './components/Items';
40
41
  export { default as KeyboardField } from './components/KeyboardField';
41
42
  export { default as KeyValuePairs } from './components/KeyValuePairs';
43
+ export { default as LazyAudio } from './components/LazyAudio';
42
44
  export { default as LazyDocument } from './components/LazyDocument';
43
45
  export { default as LazyImage } from './components/LazyImage';
44
46
  export { default as LazyVideo } from './components/LazyVideo';
@@ -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;
@@ -0,0 +1,140 @@
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 LazyLoader from './LazyLoader';
17
+ import './LazyAudio.css';
18
+
19
+ type Props = {
20
+ children?: Node,
21
+ dimmable: boolean,
22
+ duration?: number,
23
+ image?: any,
24
+ preview?: string,
25
+ size?: string,
26
+ src?: string
27
+ };
28
+
29
+ const LazyAudio = (props: Props) => {
30
+ const [dimmer, setDimmer] = useState(false);
31
+ const [error, setError] = useState(false);
32
+ const [loaded, setLoaded] = useState(!props.preview);
33
+ const [modal, setModal] = useState(false);
34
+ const [visible, setVisible] = useState(false);
35
+
36
+ if (!visible) {
37
+ return (
38
+ <Visibility
39
+ as='span'
40
+ fireOnMount
41
+ onTopVisible={() => setVisible(true)}
42
+ >
43
+ <Loader
44
+ active
45
+ inline='centered'
46
+ size={props.size}
47
+ />
48
+ </Visibility>
49
+ );
50
+ }
51
+
52
+ return (
53
+ <>
54
+ <Transition
55
+ duration={props.duration}
56
+ visible
57
+ >
58
+ <Dimmer.Dimmable
59
+ as={Segment}
60
+ className='lazy-audio'
61
+ compact
62
+ onBlur={() => setDimmer(false)}
63
+ onMouseEnter={() => setDimmer(true)}
64
+ onMouseLeave={() => setDimmer(false)}
65
+ >
66
+ { !loaded && (
67
+ <LazyLoader
68
+ active
69
+ size={props.size}
70
+ />
71
+ )}
72
+ { !error && props.preview && (
73
+ <Image
74
+ {...props.image}
75
+ onError={() => {
76
+ setError(true);
77
+ setLoaded(true);
78
+ }}
79
+ onLoad={() => {
80
+ setError(false);
81
+ setLoaded(true);
82
+ }}
83
+ size={props.size}
84
+ src={props.preview}
85
+ />
86
+ )}
87
+ { (error || !props.preview) && (
88
+ <Image
89
+ {...props.image}
90
+ className='placeholder-image'
91
+ size={props.size}
92
+ >
93
+ <Icon
94
+ name='file audio outline'
95
+ size='big'
96
+ />
97
+ </Image>
98
+ )}
99
+ { (props.src || props.children) && props.dimmable && (
100
+ <Dimmer
101
+ active={dimmer}
102
+ >
103
+ <div
104
+ className='buttons'
105
+ >
106
+ { props.src && (
107
+ <Button
108
+ content={i18n.t('LazyAudio.buttons.play')}
109
+ icon='play circle outline'
110
+ onClick={() => setModal(true)}
111
+ primary
112
+ />
113
+ )}
114
+ { props.children }
115
+ </div>
116
+ </Dimmer>
117
+ )}
118
+ </Dimmer.Dimmable>
119
+ </Transition>
120
+ { props.src && (
121
+ <AudioPlayer
122
+ onClose={() => setModal(false)}
123
+ open={modal}
124
+ size='large'
125
+ src={props.src}
126
+ />
127
+ )}
128
+ </>
129
+ );
130
+ };
131
+
132
+ LazyAudio.defaultProps = {
133
+ dimmable: true,
134
+ duration: 1000,
135
+ preview: undefined,
136
+ size: 'medium',
137
+ src: undefined
138
+ };
139
+
140
+ 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,
@@ -13,6 +13,7 @@ import {
13
13
  } from 'semantic-ui-react';
14
14
  import i18n from '../i18n/i18n';
15
15
  import DownloadButton from './DownloadButton';
16
+ import LazyLoader from './LazyLoader';
16
17
  import './LazyDocument.css';
17
18
 
18
19
  type Props = {
@@ -20,23 +21,17 @@ type Props = {
20
21
  dimmable?: boolean,
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,14 +63,28 @@ 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
+ { !props.preview && props.src && props.pdf && (
79
88
  <Image
80
89
  {...props.image}
81
90
  size={props.size}
@@ -89,7 +98,7 @@ const LazyDocument = (props: Props) => {
89
98
  </Document>
90
99
  </Image>
91
100
  )}
92
- { !props.preview && (!props.src || contentType !== 'application/pdf') && (
101
+ { (error || (!props.preview && !(props.src && props.pdf))) && (
93
102
  <Image
94
103
  {...props.image}
95
104
  className='placeholder-image'
@@ -113,7 +122,7 @@ const LazyDocument = (props: Props) => {
113
122
  content={i18n.t('LazyDocument.buttons.download')}
114
123
  icon='cloud download'
115
124
  primary
116
- url={props.src || ''}
125
+ url={props.src}
117
126
  />
118
127
  )}
119
128
  { props.children }
@@ -129,6 +138,7 @@ const LazyDocument = (props: Props) => {
129
138
  LazyDocument.defaultProps = {
130
139
  dimmable: true,
131
140
  duration: 1000,
141
+ pdf: false,
132
142
  preview: undefined,
133
143
  size: 'medium',
134
144
  src: undefined
@@ -12,6 +12,7 @@ import {
12
12
  Visibility
13
13
  } from 'semantic-ui-react';
14
14
  import i18n from '../i18n/i18n';
15
+ import LazyLoader from './LazyLoader';
15
16
  import PhotoViewer from './PhotoViewer';
16
17
  import './LazyImage.css';
17
18
 
@@ -26,9 +27,11 @@ type Props = {
26
27
  };
27
28
 
28
29
  const LazyImage = (props: Props) => {
29
- const [visible, setVisible] = useState(false);
30
- const [modal, setModal] = useState(false);
31
30
  const [dimmer, setDimmer] = useState(false);
31
+ const [error, setError] = useState(false);
32
+ const [loaded, setLoaded] = useState(!(props.src || props.preview));
33
+ const [modal, setModal] = useState(false);
34
+ const [visible, setVisible] = useState(false);
32
35
 
33
36
  if (!visible) {
34
37
  return (
@@ -60,14 +63,28 @@ const LazyImage = (props: Props) => {
60
63
  onMouseEnter={() => setDimmer(true)}
61
64
  onMouseLeave={() => setDimmer(false)}
62
65
  >
63
- { props.src && (
66
+ { !loaded && (
67
+ <LazyLoader
68
+ active
69
+ size={props.size}
70
+ />
71
+ )}
72
+ { !error && (props.preview || props.src) && (
64
73
  <Image
65
74
  {...props.image}
75
+ onError={() => {
76
+ setError(true);
77
+ setLoaded(true);
78
+ }}
79
+ onLoad={() => {
80
+ setError(false);
81
+ setLoaded(true);
82
+ }}
66
83
  size={props.size}
67
84
  src={props.preview || props.src}
68
85
  />
69
86
  )}
70
- { !props.src && (
87
+ { (error || !(props.preview || props.src)) && (
71
88
  <Image
72
89
  {...props.image}
73
90
  className='placeholder-image'
@@ -79,7 +96,7 @@ const LazyImage = (props: Props) => {
79
96
  />
80
97
  </Image>
81
98
  )}
82
- { (props.src || props.children) && props.dimmable && (
99
+ { !error && (props.src || props.children) && props.dimmable && (
83
100
  <Dimmer
84
101
  active={dimmer}
85
102
  >
@@ -100,12 +117,14 @@ const LazyImage = (props: Props) => {
100
117
  )}
101
118
  </Dimmer.Dimmable>
102
119
  </Transition>
103
- <PhotoViewer
104
- image={props.src || ''}
105
- onClose={() => setModal(false)}
106
- open={modal}
107
- size='large'
108
- />
120
+ { props.src && (
121
+ <PhotoViewer
122
+ image={props.src}
123
+ onClose={() => setModal(false)}
124
+ open={modal}
125
+ size='large'
126
+ />
127
+ )}
109
128
  </>
110
129
  );
111
130
  };
@@ -0,0 +1,28 @@
1
+ // @flow
2
+
3
+ import React from 'react';
4
+ import { Image, Loader } from 'semantic-ui-react';
5
+ import './LazyLoader.css';
6
+
7
+ type Props = {
8
+ active: boolean,
9
+ size: string
10
+ };
11
+
12
+ const LazyLoader = (props: Props) => (
13
+ <Image
14
+ className='lazy-loader'
15
+ size={props.size}
16
+ >
17
+ <Loader
18
+ active={props.active}
19
+ />
20
+ </Image>
21
+ );
22
+
23
+ LazyLoader.defaultProps = {
24
+ active: false,
25
+ size: 'small'
26
+ };
27
+
28
+ export default LazyLoader;
@@ -12,6 +12,7 @@ import {
12
12
  Visibility
13
13
  } from 'semantic-ui-react';
14
14
  import i18n from '../i18n/i18n';
15
+ import LazyLoader from './LazyLoader';
15
16
  import VideoPlayer from './VideoPlayer';
16
17
  import './LazyVideo.css';
17
18
 
@@ -29,9 +30,11 @@ type Props = {
29
30
  };
30
31
 
31
32
  const LazyVideo = (props: Props) => {
32
- const [visible, setVisible] = useState(false);
33
- const [modal, setModal] = useState(false);
34
33
  const [dimmer, setDimmer] = useState(false);
34
+ const [error, setError] = useState(false);
35
+ const [loaded, setLoaded] = useState(!(props.preview || props.src));
36
+ const [modal, setModal] = useState(false);
37
+ const [visible, setVisible] = useState(false);
35
38
 
36
39
  if (!visible) {
37
40
  return (
@@ -63,24 +66,46 @@ const LazyVideo = (props: Props) => {
63
66
  onMouseEnter={() => setDimmer(true)}
64
67
  onMouseLeave={() => setDimmer(false)}
65
68
  >
66
- { props.preview && (
69
+ { !loaded && (
70
+ <LazyLoader
71
+ active
72
+ size={props.size}
73
+ />
74
+ )}
75
+ { !error && props.preview && (
67
76
  <Image
68
77
  {...props.image}
78
+ onError={() => {
79
+ setError(true);
80
+ setLoaded(true);
81
+ }}
82
+ onLoad={() => {
83
+ setError(false);
84
+ setLoaded(true);
85
+ }}
69
86
  src={props.preview}
70
87
  size={props.size}
71
88
  />
72
89
  )}
73
- { !props.preview && props.src && (
90
+ { !error && !props.preview && props.src && (
74
91
  <Image
75
92
  {...props.image}
76
93
  size={props.size}
77
94
  >
78
95
  <video
96
+ onError={() => {
97
+ setError(true);
98
+ setLoaded(true);
99
+ }}
100
+ onLoadedData={() => {
101
+ setError(false);
102
+ setLoaded(true);
103
+ }}
79
104
  src={props.src}
80
105
  />
81
106
  </Image>
82
107
  )}
83
- { !props.preview && !props.src && (
108
+ { (error || (!props.preview && !props.src)) && (
84
109
  <Image
85
110
  {...props.image}
86
111
  className='placeholder-image'