@performant-software/semantic-components 0.5.5 → 0.5.8

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.
Files changed (36) hide show
  1. package/build/index.js +1 -1
  2. package/build/index.js.map +1 -1
  3. package/build/main.css +4 -4
  4. package/build/semantic-ui.css +1 -1
  5. package/package.json +3 -3
  6. package/src/components/DataTable.js +12 -1
  7. package/src/components/DataTableColumnSelector.js +2 -1
  8. package/src/components/ListTable.js +19 -10
  9. package/src/css/theme.config +1 -1
  10. package/src/css/themes/default/elements/step.overrides +2 -2
  11. package/src/index.js +1 -0
  12. package/types/components/AccordionList.js.flow +2 -2
  13. package/types/components/AccordionSelector.js.flow +93 -86
  14. package/types/components/ColorPickerModal.js.flow +36 -31
  15. package/types/components/DataTable.js.flow +12 -1
  16. package/types/components/DataTableColumnSelector.js.flow +12 -0
  17. package/types/components/DataView.js.flow +36 -30
  18. package/types/components/DatabaseView.js.flow +126 -0
  19. package/types/components/DropdownMenu.js.flow +11 -1
  20. package/types/components/FileUploadModal.js.flow +43 -38
  21. package/types/components/FuzzyDate.js.flow +114 -99
  22. package/types/components/ListFilters.js.flow +115 -109
  23. package/types/components/ListTable.js.flow +22 -10
  24. package/types/components/LoginModal.js.flow +72 -65
  25. package/types/components/MenuBar.js.flow +1 -0
  26. package/types/components/PhotoViewer.js.flow +23 -15
  27. package/types/components/ReferenceCodeModal.js.flow +27 -21
  28. package/types/components/ReferenceTableModal.js.flow +61 -55
  29. package/types/components/Selectize.js.flow +50 -45
  30. package/types/components/TabbedModal.js.flow +30 -24
  31. package/types/components/VideoFrameSelector.js.flow +90 -78
  32. package/types/components/VideoPlayer.js.flow +58 -30
  33. package/types/components/ViewXML.js.flow +38 -32
  34. package/types/context/ModalContext.js.flow +5 -0
  35. package/types/index.js.flow +1 -0
  36. package/webpack.config.js +2 -1
@@ -3,65 +3,71 @@
3
3
  import type { EditContainerProps } from '@performant-software/shared-components';
4
4
  import React from 'react';
5
5
  import { Form, Header, Modal } from 'semantic-ui-react';
6
+ import i18n from '../i18n/i18n';
6
7
  import EmbeddedList from './EmbeddedList';
8
+ import ModalContext from '../context/ModalContext';
7
9
  import ReferenceCodeModal from './ReferenceCodeModal';
8
- import i18n from '../i18n/i18n';
9
10
 
10
11
  const ReferenceTableModal = (props: EditContainerProps) => (
11
- <Modal
12
- as={Form}
13
- centered={false}
14
- className='reference-table-modal'
15
- open
16
- >
17
- <Modal.Header
18
- content={props.item.id
19
- ? i18n.t('ReferenceTableModal.title.edit')
20
- : i18n.t('ReferenceTableModal.title.add')}
21
- />
22
- <Modal.Content>
23
- <Form.Input
24
- error={props.isError('name')}
25
- label={i18n.t('ReferenceTableModal.labels.name')}
26
- onChange={props.onTextInputChange.bind(this, 'name')}
27
- required={props.isRequired('name')}
28
- value={props.item.name}
29
- />
30
- <Form.Input
31
- error={props.isError('key')}
32
- label={i18n.t('ReferenceTableModal.labels.key')}
33
- onChange={props.onTextInputChange.bind(this, 'key')}
34
- required={props.isRequired('key')}
35
- value={props.item.key}
36
- />
37
- <Header
38
- content={i18n.t('ReferenceTableModal.labels.referenceCodes')}
39
- />
40
- <EmbeddedList
41
- actions={[{
42
- name: 'edit'
43
- }, {
44
- name: 'copy'
45
- }, {
46
- name: 'delete'
47
- }]}
48
- columns={[{
49
- name: 'name',
50
- label: i18n.t('ReferenceTableModal.referenceCodes.columns.name')
51
- }]}
52
- items={props.item.reference_codes}
53
- modal={{
54
- component: ReferenceCodeModal,
55
- props: {
56
- required: ['name']
57
- }
58
- }}
59
- onDelete={props.onDeleteChildAssociation.bind(this, 'reference_codes')}
60
- onSave={props.onSaveChildAssociation.bind(this, 'reference_codes')}
61
- />
62
- </Modal.Content>
63
- { props.children }
64
- </Modal>
12
+ <ModalContext.Consumer>
13
+ { (mountNode) => (
14
+ <Modal
15
+ as={Form}
16
+ centered={false}
17
+ className='reference-table-modal'
18
+ mountNode={mountNode}
19
+ open
20
+ >
21
+ <Modal.Header
22
+ content={props.item.id
23
+ ? i18n.t('ReferenceTableModal.title.edit')
24
+ : i18n.t('ReferenceTableModal.title.add')}
25
+ />
26
+ <Modal.Content>
27
+ <Form.Input
28
+ error={props.isError('name')}
29
+ label={i18n.t('ReferenceTableModal.labels.name')}
30
+ onChange={props.onTextInputChange.bind(this, 'name')}
31
+ required={props.isRequired('name')}
32
+ value={props.item.name}
33
+ />
34
+ <Form.Input
35
+ error={props.isError('key')}
36
+ label={i18n.t('ReferenceTableModal.labels.key')}
37
+ onChange={props.onTextInputChange.bind(this, 'key')}
38
+ required={props.isRequired('key')}
39
+ value={props.item.key}
40
+ />
41
+ <Header
42
+ content={i18n.t('ReferenceTableModal.labels.referenceCodes')}
43
+ />
44
+ <EmbeddedList
45
+ actions={[{
46
+ name: 'edit'
47
+ }, {
48
+ name: 'copy'
49
+ }, {
50
+ name: 'delete'
51
+ }]}
52
+ columns={[{
53
+ name: 'name',
54
+ label: i18n.t('ReferenceTableModal.referenceCodes.columns.name')
55
+ }]}
56
+ items={props.item.reference_codes}
57
+ modal={{
58
+ component: ReferenceCodeModal,
59
+ props: {
60
+ required: ['name']
61
+ }
62
+ }}
63
+ onDelete={props.onDeleteChildAssociation.bind(this, 'reference_codes')}
64
+ onSave={props.onSaveChildAssociation.bind(this, 'reference_codes')}
65
+ />
66
+ </Modal.Content>
67
+ { props.children }
68
+ </Modal>
69
+ )}
70
+ </ModalContext.Consumer>
65
71
  );
66
72
 
67
73
  export default ReferenceTableModal;
@@ -18,9 +18,10 @@ import {
18
18
  import _ from 'underscore';
19
19
  import SelectizeHeader from './SelectizeHeader';
20
20
  import i18n from '../i18n/i18n';
21
+ import ModalContext from '../context/ModalContext';
21
22
  import useDataList from './DataList';
22
- import './Selectize.css';
23
23
  import useList, { type Props as ListProps } from './List';
24
+ import './Selectize.css';
24
25
 
25
26
  type Props = {
26
27
  centered?: boolean,
@@ -238,51 +239,55 @@ const Selectize = (props: Props) => {
238
239
  }, [onSelect, props.modal]);
239
240
 
240
241
  return (
241
- <Modal
242
- as={Form}
243
- centered={props.centered}
244
- className='selectize'
245
- open
246
- noValidate
247
- size='small'
248
- >
249
- <Modal.Header
250
- content={props.title}
251
- />
252
- <Modal.Content>
253
- <SelectizeGrid
254
- {...props}
255
- actions={[]}
256
- isSelected={isSelected}
257
- onDelete={() => Promise.resolve()}
258
- onDeleteAll={() => Promise.resolve()}
259
- onItemSelection={onItemSelection}
260
- onSave={onSave}
261
- onSelect={onSelect}
262
- selectedItem={selectedItem}
263
- selectedItems={selectedItems}
264
- />
265
- </Modal.Content>
266
- <Modal.Actions>
267
- <Button
268
- onClick={props.onSave.bind(this, selectedItems)}
269
- primary
270
- size='medium'
271
- type='submit'
272
- >
273
- { i18n.t('Common.buttons.save') }
274
- </Button>
275
- <Button
276
- inverted
277
- onClick={props.onClose.bind(this)}
278
- primary
279
- size='medium'
280
- type='button'
242
+ <ModalContext.Consumer>
243
+ { (mountNode) => (
244
+ <Modal
245
+ as={Form}
246
+ centered={props.centered}
247
+ className='selectize'
248
+ mountNode={mountNode}
249
+ noValidate
250
+ open
251
+ size='small'
281
252
  >
282
- { i18n.t('Common.buttons.cancel') }
283
- </Button>
284
- </Modal.Actions>
285
- </Modal>
253
+ <Modal.Header
254
+ content={props.title}
255
+ />
256
+ <Modal.Content>
257
+ <SelectizeGrid
258
+ {...props}
259
+ actions={[]}
260
+ isSelected={isSelected}
261
+ onDelete={() => Promise.resolve()}
262
+ onDeleteAll={() => Promise.resolve()}
263
+ onItemSelection={onItemSelection}
264
+ onSave={onSave}
265
+ onSelect={onSelect}
266
+ selectedItem={selectedItem}
267
+ selectedItems={selectedItems}
268
+ />
269
+ </Modal.Content>
270
+ <Modal.Actions>
271
+ <Button
272
+ onClick={props.onSave.bind(this, selectedItems)}
273
+ primary
274
+ size='medium'
275
+ type='submit'
276
+ >
277
+ { i18n.t('Common.buttons.save') }
278
+ </Button>
279
+ <Button
280
+ basic
281
+ onClick={props.onClose.bind(this)}
282
+ size='medium'
283
+ type='button'
284
+ >
285
+ { i18n.t('Common.buttons.cancel') }
286
+ </Button>
287
+ </Modal.Actions>
288
+ </Modal>
289
+ )}
290
+ </ModalContext.Consumer>
286
291
  );
287
292
  };
288
293
 
@@ -4,6 +4,7 @@ import { Element } from '@performant-software/shared-components';
4
4
  import React, { Component, type Node } from 'react';
5
5
  import { Header, Menu, Modal } from 'semantic-ui-react';
6
6
  import _ from 'underscore';
7
+ import ModalContext from '../context/ModalContext';
7
8
  import './TabbedModal.css';
8
9
 
9
10
  type Props = {
@@ -84,32 +85,37 @@ class TabbedModal extends Component<Props, State> {
84
85
  const tab = _.find(tabs, (t) => t.props.name === this.state.tab);
85
86
 
86
87
  return (
87
- <Modal
88
- className={this.getModalClasses()}
89
- {..._.omit(this.props, 'header', 'renderHeader', 'inlineTabs', 'className')}
90
- >
91
- <Modal.Header
92
- className={this.getHeaderClasses()}
93
- >
94
- { this.renderHeader() }
95
- <Menu
96
- float='right'
97
- secondary
88
+ <ModalContext.Consumer>
89
+ { (mountNode) => (
90
+ <Modal
91
+ className={this.getModalClasses()}
92
+ mountNode={mountNode}
93
+ {..._.omit(this.props, 'header', 'renderHeader', 'inlineTabs', 'className')}
98
94
  >
99
- { _.map(Element.findByType(this.props.children, TabbedModal.Tab), this.renderTab.bind(this)) }
100
- </Menu>
101
- </Modal.Header>
102
- <Modal.Content>
103
- { tab && (
104
- <div
105
- key={tab.props.name}
95
+ <Modal.Header
96
+ className={this.getHeaderClasses()}
106
97
  >
107
- { tab.props.children }
108
- </div>
109
- )}
110
- </Modal.Content>
111
- { Element.findByType(this.props.children, Modal.Actions) }
112
- </Modal>
98
+ { this.renderHeader() }
99
+ <Menu
100
+ float='right'
101
+ secondary
102
+ >
103
+ { _.map(Element.findByType(this.props.children, TabbedModal.Tab), this.renderTab.bind(this)) }
104
+ </Menu>
105
+ </Modal.Header>
106
+ <Modal.Content>
107
+ { tab && (
108
+ <div
109
+ key={tab.props.name}
110
+ >
111
+ { tab.props.children }
112
+ </div>
113
+ )}
114
+ </Modal.Content>
115
+ { Element.findByType(this.props.children, Modal.Actions) }
116
+ </Modal>
117
+ )}
118
+ </ModalContext.Consumer>
113
119
  );
114
120
  }
115
121
 
@@ -12,6 +12,7 @@ import {
12
12
  Segment
13
13
  } from 'semantic-ui-react';
14
14
  import i18n from '../i18n/i18n';
15
+ import ModalContext from '../context/ModalContext';
15
16
  import './VideoFrameSelector.css';
16
17
 
17
18
  type Props = {
@@ -46,91 +47,102 @@ const VideoFrameSelector = (props: Props) => {
46
47
  {...props.button}
47
48
  onClick={() => setModal(true)}
48
49
  />
49
- <Modal
50
- centered={false}
51
- className='video-frame-selector'
52
- open={modal}
53
- size='small'
54
- >
55
- <Modal.Header
56
- content={props.title}
57
- />
58
- <Modal.Content>
59
- <Segment>
60
- <video
61
- crossOrigin='anonymous'
62
- onLoadedMetadata={() => videoRef.current && setDuration(videoRef.current.duration)}
63
- ref={videoRef}
64
- src={props.src}
65
- />
66
- </Segment>
67
- <Grid
68
- columns={2}
50
+ <ModalContext.Consumer>
51
+ { (mountNode) => (
52
+ <Modal
53
+ centered={false}
54
+ className='video-frame-selector'
55
+ mountNode={mountNode}
56
+ open={modal}
57
+ size='small'
69
58
  >
70
- <Grid.Column>
71
- <div>
72
- <Label
73
- content={i18n.t('VideoFrameSelector.labels.interval', { count: interval })}
59
+ <Modal.Header
60
+ content={props.title}
61
+ />
62
+ <Modal.Content>
63
+ <Segment>
64
+ <video
65
+ crossOrigin='anonymous'
66
+ onLoadedMetadata={() => videoRef.current && setDuration(videoRef.current.duration)}
67
+ ref={videoRef}
68
+ src={props.src}
74
69
  />
75
- </div>
76
- <Input
77
- min={MIN_INTERVAL}
78
- max={MAX_INTERVAL}
79
- name='duration'
80
- onChange={(e, { value }) => setInterval(Number(value))}
81
- step={INTERVAL_STEP}
82
- type='range'
83
- value={interval}
84
- />
85
- </Grid.Column>
86
- <Grid.Column
87
- textAlign='right'
88
- >
70
+ </Segment>
71
+ <Grid
72
+ columns={2}
73
+ >
74
+ <Grid.Column>
75
+ <div>
76
+ <Label
77
+ content={i18n.t('VideoFrameSelector.labels.interval', { count: interval })}
78
+ />
79
+ </div>
80
+ <Input
81
+ aria-label='Interval Selector'
82
+ min={MIN_INTERVAL}
83
+ max={MAX_INTERVAL}
84
+ name='duration'
85
+ onChange={(e, { value }) => setInterval(Number(value))}
86
+ step={INTERVAL_STEP}
87
+ type='range'
88
+ value={interval}
89
+ />
90
+ </Grid.Column>
91
+ <Grid.Column
92
+ textAlign='right'
93
+ >
94
+ <Button
95
+ aria-label='Previous Frame'
96
+ basic
97
+ disabled={time === 0}
98
+ icon='arrow left'
99
+ onClick={() => setTime(Math.max(time - interval, 0))}
100
+ />
101
+ <Button
102
+ aria-label='Next Frame'
103
+ basic
104
+ disabled={time === duration}
105
+ icon='arrow right'
106
+ onClick={() => setTime(Math.min(time + interval, duration))}
107
+ />
108
+ </Grid.Column>
109
+ </Grid>
110
+ </Modal.Content>
111
+ <Modal.Actions>
89
112
  <Button
90
- basic
91
- disabled={time === 0}
92
- icon='arrow left'
93
- onClick={() => setTime(Math.max(time - interval, 0))}
113
+ content={i18n.t('Common.buttons.ok')}
114
+ primary
115
+ onClick={() => {
116
+ const video = videoRef.current;
117
+
118
+ if (video && Browser.isBrowser()) {
119
+ const canvas = document.createElement('canvas');
120
+ canvas.width = video.videoWidth;
121
+ canvas.height = video.videoHeight;
122
+
123
+ canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height);
124
+
125
+ canvas.toBlob((blob) => {
126
+ const file = new File([blob], 'test.png', {
127
+ lastModified: new Date().getTime(),
128
+ type: blob.type
129
+ });
130
+
131
+ props.onSelect(file);
132
+ setModal(false);
133
+ });
134
+ }
135
+ }}
94
136
  />
95
137
  <Button
96
138
  basic
97
- disabled={time === duration}
98
- icon='arrow right'
99
- onClick={() => setTime(Math.min(time + interval, duration))}
139
+ content={i18n.t('Common.buttons.cancel')}
140
+ onClick={() => setModal(false)}
100
141
  />
101
- </Grid.Column>
102
- </Grid>
103
- </Modal.Content>
104
- <Modal.Actions>
105
- <Button
106
- content={i18n.t('Common.buttons.ok')}
107
- primary
108
- onClick={() => {
109
- const video = videoRef.current;
110
-
111
- if (video && Browser.isBrowser()) {
112
- const canvas = document.createElement('canvas');
113
- canvas.width = video.videoWidth;
114
- canvas.height = video.videoHeight;
115
-
116
- canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height);
117
-
118
- canvas.toBlob((blob) => {
119
- const file = new File([blob], 'test.png', { lastModified: new Date().getTime(), type: blob.type });
120
- props.onSelect(file);
121
- setModal(false);
122
- });
123
- }
124
- }}
125
- />
126
- <Button
127
- content={i18n.t('Common.buttons.cancel')}
128
- inverted
129
- onClick={() => setModal(false)}
130
- primary
131
- />
132
- </Modal.Actions>
133
- </Modal>
142
+ </Modal.Actions>
143
+ </Modal>
144
+ )}
145
+ </ModalContext.Consumer>
134
146
  </>
135
147
  );
136
148
  };
@@ -1,7 +1,8 @@
1
1
  // @flow
2
2
 
3
- import React, { type Element } from 'react';
4
- import { Embed, Modal } from 'semantic-ui-react';
3
+ import React, { type Element, useEffect, useRef } from 'react';
4
+ import { Embed, Modal, Ref } from 'semantic-ui-react';
5
+ import ModalContext from '../context/ModalContext';
5
6
  import './VideoPlayer.css';
6
7
 
7
8
  type Props = {
@@ -11,39 +12,66 @@ type Props = {
11
12
  onClose: () => void,
12
13
  open: boolean,
13
14
  placeholder?: ?string,
15
+ placeholderAlt?: string,
14
16
  size?: string,
15
17
  video: string
16
18
  };
17
19
 
18
- const VideoPlayer = (props: Props) => (
19
- <Modal
20
- centered={false}
21
- className='video-player'
22
- closeIcon
23
- onClose={props.onClose.bind(this)}
24
- open={props.open}
25
- size={props.size}
26
- >
27
- <Modal.Content>
28
- { props.embedded && (
29
- <Embed
30
- active={props.autoPlay}
31
- icon={props.icon}
32
- iframe={props.autoPlay ? { allow: 'autoplay' } : undefined}
33
- placeholder={props.placeholder}
34
- url={`${props.video}${props.autoPlay ? '?autoplay=true' : ''}`}
35
- />
36
- )}
37
- { !props.embedded && (
38
- <video
39
- autoPlay={props.autoPlay}
40
- controls
41
- src={props.video}
42
- />
20
+ const VideoPlayer = (props: Props) => {
21
+ const embedRef = useRef();
22
+
23
+ /**
24
+ * Work-around to set the "alt" attribute on the placeholder <img> element if provided.
25
+ */
26
+ useEffect(() => {
27
+ if (embedRef && embedRef.current && props.placeholderAlt) {
28
+ const placeholder = embedRef.current.querySelector('.placeholder');
29
+
30
+ if (placeholder) {
31
+ placeholder.setAttribute('alt', props.placeholderAlt);
32
+ }
33
+ }
34
+ }, [embedRef, props.placeholderAlt]);
35
+
36
+ return (
37
+ <ModalContext.Consumer>
38
+ { (mountNode) => (
39
+ <Modal
40
+ centered={false}
41
+ className='video-player'
42
+ closeIcon
43
+ mountNode={mountNode}
44
+ onClose={props.onClose.bind(this)}
45
+ open={props.open}
46
+ size={props.size}
47
+ >
48
+ <Modal.Content>
49
+ { props.embedded && (
50
+ <Ref
51
+ innerRef={embedRef}
52
+ >
53
+ <Embed
54
+ active={props.autoPlay}
55
+ icon={props.icon}
56
+ iframe={props.autoPlay ? { allow: 'autoplay' } : undefined}
57
+ placeholder={props.placeholder}
58
+ url={`${props.video}${props.autoPlay ? '?autoplay=true' : ''}`}
59
+ />
60
+ </Ref>
61
+ )}
62
+ { !props.embedded && (
63
+ <video
64
+ autoPlay={props.autoPlay}
65
+ controls
66
+ src={props.video}
67
+ />
68
+ )}
69
+ </Modal.Content>
70
+ </Modal>
43
71
  )}
44
- </Modal.Content>
45
- </Modal>
46
- );
72
+ </ModalContext.Consumer>
73
+ );
74
+ };
47
75
 
48
76
  VideoPlayer.defaultProps = {
49
77
  autoPlay: false,