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

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,4 +1,6 @@
1
1
  .lazy-video.ui.segment {
2
+ display: inline-block !important;
3
+ max-width: 100%;
2
4
  padding: 0;
3
5
  }
4
6
 
@@ -18,4 +20,9 @@
18
20
  padding-top: 20%;
19
21
  padding-bottom: 20%;
20
22
  text-align: center;
21
- }
23
+ }
24
+
25
+ .lazy-video.ui.segment > .image > video {
26
+ display: table;
27
+ width: 100%;
28
+ }
@@ -12,6 +12,8 @@ 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 VideoPlayer from './VideoPlayer';
16
18
  import './LazyVideo.css';
17
19
 
@@ -19,19 +21,23 @@ type Props = {
19
21
  autoPlay?: boolean,
20
22
  children?: Node,
21
23
  dimmable: boolean,
24
+ download?: string,
22
25
  duration?: number,
23
26
  embedded?: boolean,
24
27
  icon?: string | Element<any>,
25
28
  image?: any,
29
+ name?: string,
26
30
  preview?: ?string,
27
31
  size?: string,
28
32
  src?: string
29
33
  };
30
34
 
31
35
  const LazyVideo = (props: Props) => {
32
- const [visible, setVisible] = useState(false);
33
- const [modal, setModal] = useState(false);
34
36
  const [dimmer, setDimmer] = useState(false);
37
+ const [error, setError] = useState(false);
38
+ const [loaded, setLoaded] = useState(!(props.preview || props.src));
39
+ const [modal, setModal] = useState(false);
40
+ const [visible, setVisible] = useState(false);
35
41
 
36
42
  if (!visible) {
37
43
  return (
@@ -63,24 +69,46 @@ const LazyVideo = (props: Props) => {
63
69
  onMouseEnter={() => setDimmer(true)}
64
70
  onMouseLeave={() => setDimmer(false)}
65
71
  >
66
- { props.preview && (
72
+ { !loaded && (
73
+ <LazyLoader
74
+ active
75
+ size={props.size}
76
+ />
77
+ )}
78
+ { !error && props.preview && (
67
79
  <Image
68
80
  {...props.image}
81
+ onError={() => {
82
+ setError(true);
83
+ setLoaded(true);
84
+ }}
85
+ onLoad={() => {
86
+ setError(false);
87
+ setLoaded(true);
88
+ }}
69
89
  src={props.preview}
70
90
  size={props.size}
71
91
  />
72
92
  )}
73
- { !props.preview && props.src && (
93
+ { !error && !props.preview && props.src && (
74
94
  <Image
75
95
  {...props.image}
76
96
  size={props.size}
77
97
  >
78
98
  <video
99
+ onError={() => {
100
+ setError(true);
101
+ setLoaded(true);
102
+ }}
103
+ onLoadedData={() => {
104
+ setError(false);
105
+ setLoaded(true);
106
+ }}
79
107
  src={props.src}
80
108
  />
81
109
  </Image>
82
110
  )}
83
- { !props.preview && !props.src && (
111
+ { (error || (!props.preview && !props.src)) && (
84
112
  <Image
85
113
  {...props.image}
86
114
  className='placeholder-image'
@@ -107,6 +135,13 @@ const LazyVideo = (props: Props) => {
107
135
  primary
108
136
  />
109
137
  )}
138
+ { props.download && (
139
+ <DownloadButton
140
+ color='green'
141
+ filename={props.name}
142
+ url={props.download}
143
+ />
144
+ )}
110
145
  { props.children }
111
146
  </div>
112
147
  </Dimmer>
@@ -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,12 +8,21 @@
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
28
  "save": "Save"
@@ -135,9 +144,9 @@
135
144
  "value": "Value"
136
145
  }
137
146
  },
138
- "LazyDocument": {
147
+ "LazyAudio": {
139
148
  "buttons": {
140
- "download": "Download"
149
+ "play": "Play"
141
150
  }
142
151
  },
143
152
  "LazyImage": {
@@ -193,6 +202,14 @@
193
202
  "loginErrorHeader": "Invalid Credentials",
194
203
  "password": "Password"
195
204
  },
205
+ "PhotoViewer": {
206
+ "errors": {
207
+ "path": {
208
+ "content": "Please check the image path: {{path}}",
209
+ "header": "There was a problem loading the image"
210
+ }
211
+ }
212
+ },
196
213
  "ReferenceCodeFormLabel": {
197
214
  "content": "The values in this list can be edited via the {{name}} reference table."
198
215
  },
@@ -240,6 +257,14 @@
240
257
  },
241
258
  "title": "Select Frame"
242
259
  },
260
+ "VideoPlayer": {
261
+ "errors": {
262
+ "path": {
263
+ "content": "Please check the video path: {{path}}",
264
+ "header": "There was a problem loading the video"
265
+ }
266
+ }
267
+ },
243
268
  "ViewXML": {
244
269
  "buttons": {
245
270
  "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;
@@ -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,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;