@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.
package/build/main.css CHANGED
@@ -146,6 +146,10 @@
146
146
  color: rgba(95, 95, 95, 0.86);
147
147
  }
148
148
 
149
+ .audio-player.ui.modal > .content > audio {
150
+ width: 100%;
151
+ }
152
+
149
153
  .color-button.ui.button {
150
154
  border: 1px solid rgba(34, 36, 38, 0.15);
151
155
  vertical-align: middle;
@@ -418,7 +422,38 @@ div.react-calendar {
418
422
  left: 4px;
419
423
  }
420
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
+
433
+ .lazy-audio.ui.segment {
434
+ display: inline-block !important;
435
+ max-width: 100%;
436
+ padding: 0;
437
+ }
438
+ .lazy-audio.ui.segment .buttons {
439
+ display: flex;
440
+ flex-direction: column;
441
+ }
442
+ .lazy-audio.ui.segment .buttons .ui.button {
443
+ margin-top: 5px;
444
+ margin-bottom: 5px;
445
+ }
446
+ .lazy-audio .placeholder-image.ui.image {
447
+ background-color: #f9fafb;
448
+ box-shadow: 0 1px 3px 0 #d4d4d5, 0 0 0 1px #d4d4d5;
449
+ padding-top: 20%;
450
+ padding-bottom: 20%;
451
+ text-align: center;
452
+ }
453
+
421
454
  .lazy-document.ui.segment {
455
+ display: inline-block !important;
456
+ max-width: 100%;
422
457
  padding: 0;
423
458
  }
424
459
  .lazy-document.ui.segment .buttons {
@@ -446,6 +481,8 @@ div.react-calendar {
446
481
  }
447
482
 
448
483
  .lazy-image.ui.segment {
484
+ display: inline-block !important;
485
+ max-width: 100%;
449
486
  padding: 0;
450
487
  }
451
488
  .lazy-image.ui.segment .buttons {
@@ -469,6 +506,8 @@ div.react-calendar {
469
506
  }
470
507
 
471
508
  .lazy-video.ui.segment {
509
+ display: inline-block !important;
510
+ max-width: 100%;
472
511
  padding: 0;
473
512
  }
474
513
  .lazy-video.ui.segment .buttons {
@@ -486,6 +525,10 @@ div.react-calendar {
486
525
  padding-bottom: 20%;
487
526
  text-align: center;
488
527
  }
528
+ .lazy-video.ui.segment > .image > video {
529
+ display: table;
530
+ width: 100%;
531
+ }
489
532
 
490
533
  .listLoader.ui.segment {
491
534
  position: absolute;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@performant-software/semantic-components",
3
- "version": "0.5.15",
3
+ "version": "0.5.16-beta.2",
4
4
  "description": "A package of shared components based on the Semantic UI Framework.",
5
5
  "license": "MIT",
6
6
  "main": "./build/index.js",
@@ -12,7 +12,7 @@
12
12
  "build": "webpack --mode production && flow-copy-source -v src types"
13
13
  },
14
14
  "dependencies": {
15
- "@performant-software/shared-components": "^0.5.15",
15
+ "@performant-software/shared-components": "^0.5.16-beta.2",
16
16
  "@react-google-maps/api": "^2.8.1",
17
17
  "axios": "^0.26.1",
18
18
  "i18next": "^19.4.4",
@@ -33,7 +33,7 @@
33
33
  "react-dom": ">= 16.13.1 < 18.0.0"
34
34
  },
35
35
  "devDependencies": {
36
- "@performant-software/webpack-config": "^0.5.15",
36
+ "@performant-software/webpack-config": "^0.5.16-beta.2",
37
37
  "flow-copy-source": "^2.0.9",
38
38
  "less": "^4.1.2",
39
39
  "less-loader": "^11.0.0",
@@ -0,0 +1,3 @@
1
+ .audio-player.ui.modal > .content > audio {
2
+ width: 100%;
3
+ }
@@ -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,23 @@
1
+ .lazy-audio.ui.segment {
2
+ display: inline-block !important;
3
+ max-width: 100%;
4
+ padding: 0;
5
+ }
6
+
7
+ .lazy-audio.ui.segment .buttons {
8
+ display: flex;
9
+ flex-direction: column;
10
+ }
11
+
12
+ .lazy-audio.ui.segment .buttons .ui.button {
13
+ margin-top: 5px;
14
+ margin-bottom: 5px;
15
+ }
16
+
17
+ .lazy-audio .placeholder-image.ui.image {
18
+ background-color: #f9fafb;
19
+ box-shadow: 0 1px 3px 0 #d4d4d5, 0 0 0 1px #d4d4d5;
20
+ padding-top: 20%;
21
+ padding-bottom: 20%;
22
+ text-align: center;
23
+ }
@@ -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,4 +1,6 @@
1
1
  .lazy-document.ui.segment {
2
+ display: inline-block !important;
3
+ max-width: 100%;
2
4
  padding: 0;
3
5
  }
4
6
 
@@ -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,8 +11,8 @@ 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 = {
@@ -20,23 +20,17 @@ type Props = {
20
20
  dimmable?: boolean,
21
21
  duration?: number,
22
22
  image?: any,
23
+ pdf?: boolean,
23
24
  preview?: ?string,
24
25
  size?: string,
25
26
  src?: string
26
27
  };
27
28
 
28
29
  const LazyDocument = (props: Props) => {
29
- const [visible, setVisible] = useState(false);
30
30
  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]);
31
+ const [error, setError] = useState(false);
32
+ const [loaded, setLoaded] = useState(!props.preview);
33
+ const [visible, setVisible] = useState(false);
40
34
 
41
35
  if (!visible) {
42
36
  return (
@@ -68,14 +62,28 @@ const LazyDocument = (props: Props) => {
68
62
  onMouseEnter={() => setDimmer(true)}
69
63
  onMouseLeave={() => setDimmer(false)}
70
64
  >
71
- { props.preview && (
65
+ { !loaded && (
66
+ <LazyLoader
67
+ active
68
+ size={props.size}
69
+ />
70
+ )}
71
+ { !error && props.preview && (
72
72
  <Image
73
73
  {...props.image}
74
+ onError={() => {
75
+ setError(true);
76
+ setLoaded(true);
77
+ }}
78
+ onLoad={() => {
79
+ setError(false);
80
+ setLoaded(true);
81
+ }}
74
82
  src={props.preview}
75
83
  size={props.size}
76
84
  />
77
85
  )}
78
- { !props.preview && props.src && contentType === 'application/pdf' && (
86
+ { !props.preview && props.src && props.pdf && (
79
87
  <Image
80
88
  {...props.image}
81
89
  size={props.size}
@@ -89,7 +97,7 @@ const LazyDocument = (props: Props) => {
89
97
  </Document>
90
98
  </Image>
91
99
  )}
92
- { !props.preview && (!props.src || contentType !== 'application/pdf') && (
100
+ { (error || (!props.preview && !(props.src && props.pdf))) && (
93
101
  <Image
94
102
  {...props.image}
95
103
  className='placeholder-image'
@@ -110,10 +118,8 @@ const LazyDocument = (props: Props) => {
110
118
  >
111
119
  { props.src && (
112
120
  <DownloadButton
113
- content={i18n.t('LazyDocument.buttons.download')}
114
- icon='cloud download'
115
121
  primary
116
- url={props.src || ''}
122
+ src={props.src}
117
123
  />
118
124
  )}
119
125
  { props.children }
@@ -129,6 +135,7 @@ const LazyDocument = (props: Props) => {
129
135
  LazyDocument.defaultProps = {
130
136
  dimmable: true,
131
137
  duration: 1000,
138
+ pdf: false,
132
139
  preview: undefined,
133
140
  size: 'medium',
134
141
  src: undefined
@@ -1,4 +1,6 @@
1
1
  .lazy-image.ui.segment {
2
+ display: inline-block !important;
3
+ max-width: 100%;
2
4
  padding: 0;
3
5
  }
4
6
 
@@ -18,4 +20,4 @@
18
20
  padding-top: 20%;
19
21
  padding-bottom: 20%;
20
22
  text-align: center;
21
- }
23
+ }
@@ -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
  };
@@ -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;