@performant-software/semantic-components 0.5.16-beta.0 → 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.
package/build/main.css CHANGED
@@ -422,6 +422,14 @@ div.react-calendar {
422
422
  left: 4px;
423
423
  }
424
424
 
425
+ .lazy-loader {
426
+ background-color: #f9fafb;
427
+ box-shadow: 0 1px 3px 0 #d4d4d5, 0 0 0 1px #d4d4d5;
428
+ padding-top: 20%;
429
+ padding-bottom: 20%;
430
+ text-align: center;
431
+ }
432
+
425
433
  .lazy-audio.ui.segment {
426
434
  display: inline-block !important;
427
435
  max-width: 100%;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@performant-software/semantic-components",
3
- "version": "0.5.16-beta.0",
3
+ "version": "0.5.16-beta.1",
4
4
  "description": "A package of shared components based on the Semantic UI Framework.",
5
5
  "license": "MIT",
6
6
  "main": "./build/index.js",
@@ -9,12 +9,10 @@
9
9
  "./build/semantic-ui.css"
10
10
  ],
11
11
  "scripts": {
12
- "build": "webpack --mode production && flow-copy-source -v src types",
13
- "publish": "npm publish",
14
- "publish-beta": "npm publish --tag beta"
12
+ "build": "webpack --mode production && flow-copy-source -v src types"
15
13
  },
16
14
  "dependencies": {
17
- "@performant-software/shared-components": "^0.5.16-beta.0",
15
+ "@performant-software/shared-components": "^0.5.16-beta.1",
18
16
  "@react-google-maps/api": "^2.8.1",
19
17
  "axios": "^0.26.1",
20
18
  "i18next": "^19.4.4",
@@ -35,7 +33,7 @@
35
33
  "react-dom": ">= 16.13.1 < 18.0.0"
36
34
  },
37
35
  "devDependencies": {
38
- "@performant-software/webpack-config": "^0.5.16-beta.0",
36
+ "@performant-software/webpack-config": "^0.5.16-beta.1",
39
37
  "flow-copy-source": "^2.0.9",
40
38
  "less": "^4.1.2",
41
39
  "less-loader": "^11.0.0",
@@ -1,8 +1,9 @@
1
1
  // @flow
2
2
 
3
- import React from 'react';
4
- import { Button, Modal } from 'semantic-ui-react';
3
+ import React, { useState } from 'react';
4
+ import { Button, Message, Modal } from 'semantic-ui-react';
5
5
  import i18n from '../i18n/i18n';
6
+ import ModalContext from '../context/ModalContext';
6
7
  import './AudioPlayer.css';
7
8
 
8
9
  type Props = {
@@ -12,25 +13,42 @@ type Props = {
12
13
  src: string
13
14
  };
14
15
 
15
- const AudioPlayer = (props: Props) => (
16
- <Modal
17
- centered={props.centered}
18
- className='audio-player'
19
- open={props.open}
20
- >
21
- <Modal.Content>
22
- <audio
23
- controls
24
- src={props.src}
25
- />
26
- </Modal.Content>
27
- <Modal.Actions>
28
- <Button
29
- content={i18n.t('Common.buttons.close')}
30
- onClick={props.onClose}
31
- />
32
- </Modal.Actions>
33
- </Modal>
34
- );
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
+ };
35
53
 
36
54
  export default AudioPlayer;
@@ -13,6 +13,7 @@ import {
13
13
  } from 'semantic-ui-react';
14
14
  import i18n from '../i18n/i18n';
15
15
  import AudioPlayer from './AudioPlayer';
16
+ import LazyLoader from './LazyLoader';
16
17
  import './LazyAudio.css';
17
18
 
18
19
  type Props = {
@@ -26,9 +27,11 @@ type Props = {
26
27
  };
27
28
 
28
29
  const LazyAudio = (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.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 LazyAudio = (props: Props) => {
60
63
  onMouseEnter={() => setDimmer(true)}
61
64
  onMouseLeave={() => setDimmer(false)}
62
65
  >
63
- { props.preview && (
66
+ { !loaded && (
67
+ <LazyLoader
68
+ active
69
+ size={props.size}
70
+ />
71
+ )}
72
+ { !error && props.preview && (
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}
68
85
  />
69
86
  )}
70
- { !props.preview && (
87
+ { (error || !props.preview) && (
71
88
  <Image
72
89
  {...props.image}
73
90
  className='placeholder-image'
@@ -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 = {
@@ -27,8 +28,10 @@ type Props = {
27
28
  };
28
29
 
29
30
  const LazyDocument = (props: Props) => {
30
- const [visible, setVisible] = useState(false);
31
31
  const [dimmer, setDimmer] = useState(false);
32
+ const [error, setError] = useState(false);
33
+ const [loaded, setLoaded] = useState(!props.preview);
34
+ const [visible, setVisible] = useState(false);
32
35
 
33
36
  if (!visible) {
34
37
  return (
@@ -60,9 +63,23 @@ const LazyDocument = (props: Props) => {
60
63
  onMouseEnter={() => setDimmer(true)}
61
64
  onMouseLeave={() => setDimmer(false)}
62
65
  >
63
- { props.preview && (
66
+ { !loaded && (
67
+ <LazyLoader
68
+ active
69
+ size={props.size}
70
+ />
71
+ )}
72
+ { !error && props.preview && (
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
  src={props.preview}
67
84
  size={props.size}
68
85
  />
@@ -81,7 +98,7 @@ const LazyDocument = (props: Props) => {
81
98
  </Document>
82
99
  </Image>
83
100
  )}
84
- { !props.preview && !(props.src && props.pdf) && (
101
+ { (error || (!props.preview && !(props.src && props.pdf))) && (
85
102
  <Image
86
103
  {...props.image}
87
104
  className='placeholder-image'
@@ -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.preview || 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.preview || 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
  >
@@ -0,0 +1,7 @@
1
+ .lazy-loader {
2
+ background-color: #f9fafb;
3
+ box-shadow: 0 1px 3px 0 #d4d4d5, 0 0 0 1px #d4d4d5;
4
+ padding-top: 20%;
5
+ padding-bottom: 20%;
6
+ text-align: center;
7
+ }
@@ -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'
@@ -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",
@@ -198,6 +206,14 @@
198
206
  "loginErrorHeader": "Invalid Credentials",
199
207
  "password": "Password"
200
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
+ },
201
217
  "ReferenceCodeFormLabel": {
202
218
  "content": "The values in this list can be edited via the {{name}} reference table."
203
219
  },
@@ -245,6 +261,14 @@
245
261
  },
246
262
  "title": "Select Frame"
247
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
+ },
248
272
  "ViewXML": {
249
273
  "buttons": {
250
274
  "view": "View XML"
@@ -1,8 +1,9 @@
1
1
  // @flow
2
2
 
3
- import React from 'react';
4
- import { Button, Modal } from 'semantic-ui-react';
3
+ import React, { useState } from 'react';
4
+ import { Button, Message, Modal } from 'semantic-ui-react';
5
5
  import i18n from '../i18n/i18n';
6
+ import ModalContext from '../context/ModalContext';
6
7
  import './AudioPlayer.css';
7
8
 
8
9
  type Props = {
@@ -12,25 +13,42 @@ type Props = {
12
13
  src: string
13
14
  };
14
15
 
15
- const AudioPlayer = (props: Props) => (
16
- <Modal
17
- centered={props.centered}
18
- className='audio-player'
19
- open={props.open}
20
- >
21
- <Modal.Content>
22
- <audio
23
- controls
24
- src={props.src}
25
- />
26
- </Modal.Content>
27
- <Modal.Actions>
28
- <Button
29
- content={i18n.t('Common.buttons.close')}
30
- onClick={props.onClose}
31
- />
32
- </Modal.Actions>
33
- </Modal>
34
- );
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
+ };
35
53
 
36
54
  export default AudioPlayer;