@performant-software/semantic-components 0.5.16-beta.0 → 0.5.16-beta.3

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.3",
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.3",
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.3",
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;
@@ -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;
@@ -13,22 +13,28 @@ import {
13
13
  } from 'semantic-ui-react';
14
14
  import i18n from '../i18n/i18n';
15
15
  import AudioPlayer from './AudioPlayer';
16
+ import DownloadButton from './DownloadButton';
17
+ import LazyLoader from './LazyLoader';
16
18
  import './LazyAudio.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 LazyAudio = (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.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 LazyAudio = (props: Props) => {
60
66
  onMouseEnter={() => setDimmer(true)}
61
67
  onMouseLeave={() => setDimmer(false)}
62
68
  >
63
- { props.preview && (
69
+ { !loaded && (
70
+ <LazyLoader
71
+ active
72
+ size={props.size}
73
+ />
74
+ )}
75
+ { !error && props.preview && (
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}
68
88
  />
69
89
  )}
70
- { !props.preview && (
90
+ { (error || !props.preview) && (
71
91
  <Image
72
92
  {...props.image}
73
93
  className='placeholder-image'
@@ -94,6 +114,13 @@ const LazyAudio = (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>
@@ -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 = {
@@ -27,8 +27,10 @@ type Props = {
27
27
  };
28
28
 
29
29
  const LazyDocument = (props: Props) => {
30
- const [visible, setVisible] = useState(false);
31
30
  const [dimmer, setDimmer] = useState(false);
31
+ const [error, setError] = useState(false);
32
+ const [loaded, setLoaded] = useState(!props.preview);
33
+ const [visible, setVisible] = useState(false);
32
34
 
33
35
  if (!visible) {
34
36
  return (
@@ -60,9 +62,23 @@ const LazyDocument = (props: Props) => {
60
62
  onMouseEnter={() => setDimmer(true)}
61
63
  onMouseLeave={() => setDimmer(false)}
62
64
  >
63
- { props.preview && (
65
+ { !loaded && (
66
+ <LazyLoader
67
+ active
68
+ size={props.size}
69
+ />
70
+ )}
71
+ { !error && props.preview && (
64
72
  <Image
65
73
  {...props.image}
74
+ onError={() => {
75
+ setError(true);
76
+ setLoaded(true);
77
+ }}
78
+ onLoad={() => {
79
+ setError(false);
80
+ setLoaded(true);
81
+ }}
66
82
  src={props.preview}
67
83
  size={props.size}
68
84
  />
@@ -81,7 +97,7 @@ const LazyDocument = (props: Props) => {
81
97
  </Document>
82
98
  </Image>
83
99
  )}
84
- { !props.preview && !(props.src && props.pdf) && (
100
+ { (error || (!props.preview && !(props.src && props.pdf))) && (
85
101
  <Image
86
102
  {...props.image}
87
103
  className='placeholder-image'
@@ -102,8 +118,6 @@ const LazyDocument = (props: Props) => {
102
118
  >
103
119
  { props.src && (
104
120
  <DownloadButton
105
- content={i18n.t('LazyDocument.buttons.download')}
106
- icon='cloud download'
107
121
  primary
108
122
  url={props.src}
109
123
  />
@@ -0,0 +1,44 @@
1
+ // @flow
2
+
3
+ import React, { useState, type Node } from 'react';
4
+ import { Button } from 'semantic-ui-react';
5
+ import i18n from '../i18n/i18n';
6
+ import IIIFModal from './IIIFModal';
7
+ import LazyMedia from './LazyMedia';
8
+
9
+ type Props = {
10
+ children?: Node,
11
+ manifest?: string
12
+ };
13
+
14
+ const LazyIIIF = (props: Props) => {
15
+ const [modal, setModal] = useState(false);
16
+
17
+ return (
18
+ <>
19
+ <LazyMedia
20
+ {...props}
21
+ >
22
+ { props.manifest && (
23
+ <Button
24
+ content={i18n.t('Common.buttons.open')}
25
+ icon='images outline'
26
+ onClick={() => setModal(true)}
27
+ />
28
+ )}
29
+ { props.children }
30
+ </LazyMedia>
31
+ { modal && (
32
+ <IIIFModal
33
+ manifestId={props.manifest}
34
+ onClose={() => setModal(false)}
35
+ options={{
36
+ showIIIFBadge: false
37
+ }}
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.preview || 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.preview || 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,6 +114,13 @@ 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>
@@ -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;