@performant-software/semantic-components 0.5.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/LICENSE +21 -0
- package/README.md +0 -0
- package/build/index.js +2 -0
- package/build/index.js.map +1 -0
- package/build/main.css +786 -0
- package/index.js +1 -0
- package/package.json +37 -0
- package/src/components/AccordionDataList.css +8 -0
- package/src/components/AccordionDataList.js +224 -0
- package/src/components/AccordionList.css +27 -0
- package/src/components/AccordionList.js +596 -0
- package/src/components/AccordionSelector.css +3 -0
- package/src/components/AccordionSelector.js +359 -0
- package/src/components/ArrowButtons.css +4 -0
- package/src/components/ArrowButtons.js +38 -0
- package/src/components/AssociatedDropdown.css +44 -0
- package/src/components/AssociatedDropdown.js +338 -0
- package/src/components/BooleanIcon.css +0 -0
- package/src/components/BooleanIcon.js +33 -0
- package/src/components/CancelButton.css +0 -0
- package/src/components/CancelButton.js +25 -0
- package/src/components/ColorButton.css +4 -0
- package/src/components/ColorButton.js +34 -0
- package/src/components/ColorPickerModal.css +3 -0
- package/src/components/ColorPickerModal.js +77 -0
- package/src/components/ColumnResize.css +9 -0
- package/src/components/ColumnResize.js +20 -0
- package/src/components/DataList.css +0 -0
- package/src/components/DataList.js +531 -0
- package/src/components/DataTable.css +43 -0
- package/src/components/DataTable.js +596 -0
- package/src/components/DataTableColumnSelector.css +10 -0
- package/src/components/DataTableColumnSelector.js +146 -0
- package/src/components/DateInput.css +6 -0
- package/src/components/DateInput.js +58 -0
- package/src/components/DatePicker.css +72 -0
- package/src/components/DatePicker.js +81 -0
- package/src/components/DescriptorField.css +0 -0
- package/src/components/DescriptorField.js +42 -0
- package/src/components/DownloadButton.css +0 -0
- package/src/components/DownloadButton.js +23 -0
- package/src/components/Draggable.css +0 -0
- package/src/components/Draggable.js +94 -0
- package/src/components/DropdownButton.css +3 -0
- package/src/components/DropdownButton.js +65 -0
- package/src/components/DropdownMenu.css +0 -0
- package/src/components/DropdownMenu.js +68 -0
- package/src/components/EditModal.css +8 -0
- package/src/components/EditModal.js +99 -0
- package/src/components/EditPage.css +7 -0
- package/src/components/EditPage.js +249 -0
- package/src/components/EmbeddedList.css +7 -0
- package/src/components/EmbeddedList.js +278 -0
- package/src/components/FileInputButton.css +0 -0
- package/src/components/FileInputButton.js +54 -0
- package/src/components/FileUpload.css +31 -0
- package/src/components/FileUpload.js +188 -0
- package/src/components/FileUploadModal.css +0 -0
- package/src/components/FileUploadModal.js +408 -0
- package/src/components/FuzzyDate.css +8 -0
- package/src/components/FuzzyDate.js +575 -0
- package/src/components/GoogleMap.css +0 -0
- package/src/components/GoogleMap.js +105 -0
- package/src/components/GooglePlacesSearch.css +0 -0
- package/src/components/GooglePlacesSearch.js +43 -0
- package/src/components/HorizontalCards.css +50 -0
- package/src/components/HorizontalCards.js +226 -0
- package/src/components/ItemCollection.css +3 -0
- package/src/components/ItemCollection.js +159 -0
- package/src/components/ItemList.css +0 -0
- package/src/components/ItemList.js +126 -0
- package/src/components/Items.css +19 -0
- package/src/components/Items.js +365 -0
- package/src/components/ItemsToggle.css +0 -0
- package/src/components/ItemsToggle.js +168 -0
- package/src/components/KeyboardField.css +4 -0
- package/src/components/KeyboardField.js +147 -0
- package/src/components/LazyDocument.css +21 -0
- package/src/components/LazyDocument.js +113 -0
- package/src/components/LazyImage.css +21 -0
- package/src/components/LazyImage.js +119 -0
- package/src/components/LazyVideo.css +21 -0
- package/src/components/LazyVideo.js +131 -0
- package/src/components/LinkButton.css +8 -0
- package/src/components/LinkButton.js +23 -0
- package/src/components/LinkLabel.css +8 -0
- package/src/components/LinkLabel.js +29 -0
- package/src/components/List.css +8 -0
- package/src/components/List.js +761 -0
- package/src/components/ListFilters.css +0 -0
- package/src/components/ListFilters.js +408 -0
- package/src/components/ListLoader.css +8 -0
- package/src/components/ListLoader.js +32 -0
- package/src/components/ListTable.css +3 -0
- package/src/components/ListTable.js +86 -0
- package/src/components/LoginModal.css +7 -0
- package/src/components/LoginModal.js +102 -0
- package/src/components/MasonryGrid.css +48 -0
- package/src/components/MasonryGrid.js +202 -0
- package/src/components/MediaGallery.css +37 -0
- package/src/components/MediaGallery.js +148 -0
- package/src/components/MediaGrid.css +72 -0
- package/src/components/MediaGrid.js +74 -0
- package/src/components/MediaList.css +3 -0
- package/src/components/MediaList.js +98 -0
- package/src/components/ModalDropdown.css +11 -0
- package/src/components/ModalDropdown.js +84 -0
- package/src/components/NestedAccordion.css +41 -0
- package/src/components/NestedAccordion.js +276 -0
- package/src/components/PhotoViewer.css +3 -0
- package/src/components/PhotoViewer.js +36 -0
- package/src/components/PlayButton.css +3 -0
- package/src/components/PlayButton.js +37 -0
- package/src/components/RemoteDropdown.css +13 -0
- package/src/components/RemoteDropdown.js +368 -0
- package/src/components/SaveButton.css +0 -0
- package/src/components/SaveButton.js +31 -0
- package/src/components/Section.css +0 -0
- package/src/components/Section.js +41 -0
- package/src/components/Selectize.css +11 -0
- package/src/components/Selectize.js +297 -0
- package/src/components/SelectizeHeader.css +3 -0
- package/src/components/SelectizeHeader.js +40 -0
- package/src/components/TabbedModal.css +14 -0
- package/src/components/TabbedModal.js +165 -0
- package/src/components/TabsMenu.css +0 -0
- package/src/components/TabsMenu.js +35 -0
- package/src/components/TagsList.css +0 -0
- package/src/components/TagsList.js +43 -0
- package/src/components/Thumbnail.css +0 -0
- package/src/components/Thumbnail.js +47 -0
- package/src/components/Toaster.css +9 -0
- package/src/components/Toaster.js +73 -0
- package/src/components/VideoFrameSelector.css +3 -0
- package/src/components/VideoFrameSelector.js +148 -0
- package/src/components/VideoPlayer.css +3 -0
- package/src/components/VideoPlayer.js +55 -0
- package/src/components/VideoPlayerButton.css +17 -0
- package/src/components/VideoPlayerButton.js +17 -0
- package/src/components/ViewXML.css +0 -0
- package/src/components/ViewXML.js +72 -0
- package/src/i18n/en.json +204 -0
- package/src/i18n/i18n.js +24 -0
- package/src/index.js +76 -0
- package/types/components/AccordionDataList.js.flow +224 -0
- package/types/components/AccordionList.js.flow +596 -0
- package/types/components/AccordionSelector.js.flow +359 -0
- package/types/components/ArrowButtons.js.flow +38 -0
- package/types/components/AssociatedDropdown.js.flow +338 -0
- package/types/components/BooleanIcon.js.flow +33 -0
- package/types/components/CancelButton.js.flow +25 -0
- package/types/components/ColorButton.js.flow +34 -0
- package/types/components/ColorPickerModal.js.flow +77 -0
- package/types/components/ColumnResize.js.flow +20 -0
- package/types/components/DataList.js.flow +531 -0
- package/types/components/DataTable.js.flow +596 -0
- package/types/components/DataTableColumnSelector.js.flow +146 -0
- package/types/components/DataView.js.flow +125 -0
- package/types/components/DateInput.js.flow +58 -0
- package/types/components/DatePicker.js.flow +81 -0
- package/types/components/DescriptorField.js.flow +42 -0
- package/types/components/DownloadButton.js.flow +23 -0
- package/types/components/Draggable.js.flow +94 -0
- package/types/components/DropdownButton.js.flow +65 -0
- package/types/components/DropdownMenu.js.flow +68 -0
- package/types/components/EditModal.js.flow +99 -0
- package/types/components/EditPage.js.flow +249 -0
- package/types/components/EmbeddedList.js.flow +278 -0
- package/types/components/FileInputButton.js.flow +54 -0
- package/types/components/FileUpload.js.flow +188 -0
- package/types/components/FileUploadModal.js.flow +408 -0
- package/types/components/FuzzyDate.js.flow +575 -0
- package/types/components/GoogleMap.js.flow +105 -0
- package/types/components/GooglePlacesSearch.js.flow +43 -0
- package/types/components/HorizontalCards.js.flow +226 -0
- package/types/components/ItemCollection.js.flow +159 -0
- package/types/components/ItemList.js.flow +126 -0
- package/types/components/Items.js.flow +365 -0
- package/types/components/ItemsToggle.js.flow +168 -0
- package/types/components/KeyboardField.js.flow +147 -0
- package/types/components/LazyDocument.js.flow +113 -0
- package/types/components/LazyImage.js.flow +119 -0
- package/types/components/LazyVideo.js.flow +131 -0
- package/types/components/LinkButton.js.flow +23 -0
- package/types/components/LinkLabel.js.flow +29 -0
- package/types/components/List.js.flow +761 -0
- package/types/components/ListFilters.js.flow +408 -0
- package/types/components/ListLoader.js.flow +32 -0
- package/types/components/ListTable.js.flow +86 -0
- package/types/components/LoginModal.js.flow +102 -0
- package/types/components/MasonryGrid.js.flow +202 -0
- package/types/components/MediaGallery.js.flow +148 -0
- package/types/components/MediaGrid.js.flow +74 -0
- package/types/components/MediaList.js.flow +98 -0
- package/types/components/MenuBar.js.flow +77 -0
- package/types/components/MenuSidebar.js.flow +72 -0
- package/types/components/ModalDropdown.js.flow +84 -0
- package/types/components/NestedAccordion.js.flow +276 -0
- package/types/components/PhotoViewer.js.flow +36 -0
- package/types/components/PlayButton.js.flow +37 -0
- package/types/components/RemoteDropdown.js.flow +368 -0
- package/types/components/SaveButton.js.flow +31 -0
- package/types/components/Section.js.flow +41 -0
- package/types/components/Selectize.js.flow +297 -0
- package/types/components/SelectizeHeader.js.flow +40 -0
- package/types/components/TabbedModal.js.flow +165 -0
- package/types/components/TabsMenu.js.flow +35 -0
- package/types/components/TagsList.js.flow +43 -0
- package/types/components/Thumbnail.js.flow +47 -0
- package/types/components/Toaster.js.flow +73 -0
- package/types/components/VideoFrameSelector.js.flow +148 -0
- package/types/components/VideoPlayer.js.flow +55 -0
- package/types/components/VideoPlayerButton.js.flow +17 -0
- package/types/components/ViewXML.js.flow +72 -0
- package/types/hooks/Imageable.js.flow +54 -0
- package/types/i18n/i18n.js.flow +24 -0
- package/types/index.js.flow +78 -0
- package/webpack.config.js +3 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
|
|
3
|
+
import React, { Component, type ComponentType } from 'react';
|
|
4
|
+
import { Checkbox, Dropdown, Icon } from 'semantic-ui-react';
|
|
5
|
+
import _ from 'underscore';
|
|
6
|
+
import Draggable from './Draggable';
|
|
7
|
+
import './DataTableColumnSelector.css';
|
|
8
|
+
|
|
9
|
+
import type { Column } from './DataTable';
|
|
10
|
+
|
|
11
|
+
type Props = {
|
|
12
|
+
className: string,
|
|
13
|
+
columns: Array<Column>
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
type State = {
|
|
17
|
+
columns: Array<Column>
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Returns a function to wrap the passed component in a column selector. The ColumnSelector component will bind a
|
|
22
|
+
* renderListHeader function, which can be used in the wrapped component to render a column selector dropdown button.
|
|
23
|
+
*
|
|
24
|
+
* @param WrappedComponent
|
|
25
|
+
*/
|
|
26
|
+
const useColumnSelector = (WrappedComponent: ComponentType<any>) => (
|
|
27
|
+
class extends Component<Props, State> {
|
|
28
|
+
static defaultProps = {
|
|
29
|
+
className: ''
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Constructs a new DataTableColumnSelector component.
|
|
34
|
+
*
|
|
35
|
+
* @param props
|
|
36
|
+
*/
|
|
37
|
+
constructor(props: Props) {
|
|
38
|
+
super(props);
|
|
39
|
+
|
|
40
|
+
this.state = {
|
|
41
|
+
columns: props.columns
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Toggles the hidden property for the passed column.
|
|
47
|
+
*
|
|
48
|
+
* @param column
|
|
49
|
+
*/
|
|
50
|
+
onColumnCheckbox(column: Column) {
|
|
51
|
+
this.setState((state) => ({
|
|
52
|
+
columns: _.map(state.columns, (c) => (c.name === column.name ? { ...c, hidden: !c.hidden } : c))
|
|
53
|
+
}));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Drags/drops the column at the passed index to the new position.
|
|
58
|
+
*
|
|
59
|
+
* @param dragIndex
|
|
60
|
+
* @param hoverIndex
|
|
61
|
+
*/
|
|
62
|
+
onDrag(dragIndex: number, hoverIndex: number) {
|
|
63
|
+
this.setState((state) => {
|
|
64
|
+
const columns = [];
|
|
65
|
+
const anchoredColumns = [];
|
|
66
|
+
|
|
67
|
+
// Preserve the index of any unlabeled columns
|
|
68
|
+
_.each(state.columns, (column, index) => {
|
|
69
|
+
if (column.label && column.label.length) {
|
|
70
|
+
columns.push(column);
|
|
71
|
+
} else {
|
|
72
|
+
anchoredColumns.push({ index, column });
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const column = columns[dragIndex];
|
|
77
|
+
columns.splice(dragIndex, 1);
|
|
78
|
+
columns.splice(hoverIndex, 0, column);
|
|
79
|
+
|
|
80
|
+
// Add the unlabeled columns back in
|
|
81
|
+
_.each(anchoredColumns, (c) => columns.splice(c.index, 0, c.column));
|
|
82
|
+
|
|
83
|
+
return { columns };
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Renders the DataTableColumnSelector component.
|
|
89
|
+
*
|
|
90
|
+
* @returns {*}
|
|
91
|
+
*/
|
|
92
|
+
render() {
|
|
93
|
+
return (
|
|
94
|
+
<WrappedComponent
|
|
95
|
+
{...this.props}
|
|
96
|
+
className={`data-table-column-selector ${this.props.className}`}
|
|
97
|
+
columns={this.state.columns}
|
|
98
|
+
renderListHeader={this.renderHeader.bind(this)}
|
|
99
|
+
/>
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Renders the dropdown column selector.
|
|
105
|
+
*
|
|
106
|
+
* @returns {*}
|
|
107
|
+
*/
|
|
108
|
+
renderHeader() {
|
|
109
|
+
return (
|
|
110
|
+
<Dropdown
|
|
111
|
+
basic
|
|
112
|
+
button
|
|
113
|
+
icon='cog'
|
|
114
|
+
className='icon configure-button open-right'
|
|
115
|
+
simple
|
|
116
|
+
>
|
|
117
|
+
<Dropdown.Menu>
|
|
118
|
+
{ this.state.columns
|
|
119
|
+
.filter((c) => c.label && c.label.length)
|
|
120
|
+
.map((c, index) => (
|
|
121
|
+
<Draggable
|
|
122
|
+
id={c.name}
|
|
123
|
+
index={index}
|
|
124
|
+
key={c.name}
|
|
125
|
+
onDrag={this.onDrag.bind(this)}
|
|
126
|
+
>
|
|
127
|
+
<Dropdown.Item>
|
|
128
|
+
<Icon
|
|
129
|
+
name='bars'
|
|
130
|
+
/>
|
|
131
|
+
<Checkbox
|
|
132
|
+
checked={!c.hidden}
|
|
133
|
+
label={c.label}
|
|
134
|
+
onClick={this.onColumnCheckbox.bind(this, c)}
|
|
135
|
+
/>
|
|
136
|
+
</Dropdown.Item>
|
|
137
|
+
</Draggable>
|
|
138
|
+
))}
|
|
139
|
+
</Dropdown.Menu>
|
|
140
|
+
</Dropdown>
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
export default useColumnSelector;
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
|
|
3
|
+
import axios from 'axios';
|
|
4
|
+
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
|
5
|
+
import { Container } from 'semantic-ui-react';
|
|
6
|
+
import _ from 'underscore';
|
|
7
|
+
import MenuSidebar from './MenuSidebar';
|
|
8
|
+
import MenuBar from './MenuBar';
|
|
9
|
+
import ListTable from './ListTable';
|
|
10
|
+
|
|
11
|
+
type Item = {
|
|
12
|
+
id: string,
|
|
13
|
+
label: string,
|
|
14
|
+
group?: string,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
type Props = {
|
|
18
|
+
items: Array<Item>,
|
|
19
|
+
layout: 'top' | 'left',
|
|
20
|
+
title: string
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const DataView = (props: Props) => {
|
|
24
|
+
const [activeItem, setActiveItem] = useState();
|
|
25
|
+
const [columns, setColumns] = useState([]);
|
|
26
|
+
const [menu, setMenu] = useState([]);
|
|
27
|
+
const [paddingLeft, setPaddingLeft] = useState();
|
|
28
|
+
|
|
29
|
+
const menuRef = useRef();
|
|
30
|
+
|
|
31
|
+
const onLoad = useCallback((params) => {
|
|
32
|
+
if (activeItem) {
|
|
33
|
+
axios.get
|
|
34
|
+
fetch(activeItem.url).then((response) => {
|
|
35
|
+
console.log(response);
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}, [activeItem]);
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Re-formats the passed items as a menu object. If we're grouping the menu items, items will be added as
|
|
42
|
+
* a child property of the group object.
|
|
43
|
+
*/
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
const hasGroups = _.every(props.items, (item) => !!item.group);
|
|
46
|
+
|
|
47
|
+
if (!hasGroups) {
|
|
48
|
+
setMenu(props.items);
|
|
49
|
+
} else {
|
|
50
|
+
const menuItems = _.groupBy(props.items, 'group');
|
|
51
|
+
const headers = _.keys(menuItems);
|
|
52
|
+
|
|
53
|
+
setMenu(_.map(headers, (header) => ({
|
|
54
|
+
content: header,
|
|
55
|
+
items: _.map(menuItems[header], (item) => ({
|
|
56
|
+
active: activeItem && activeItem.id === item.id,
|
|
57
|
+
content: item.label,
|
|
58
|
+
onClick: () => setActiveItem(item)
|
|
59
|
+
}))
|
|
60
|
+
})));
|
|
61
|
+
}
|
|
62
|
+
}, [activeItem, props.items]);
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Set the active item as the first item in the list.
|
|
66
|
+
*/
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
if (props.items && props.items.length) {
|
|
69
|
+
setActiveItem(props.items[0]);
|
|
70
|
+
}
|
|
71
|
+
}, [props.items]);
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* If we're using the sidebar layout, set the padding so the fixed positioned menu does not overlap the content.
|
|
75
|
+
*/
|
|
76
|
+
useEffect(() => {
|
|
77
|
+
if (props.layout === 'left' && menuRef && menuRef.current) {
|
|
78
|
+
setPaddingLeft(menuRef.current.offsetWidth);
|
|
79
|
+
}
|
|
80
|
+
}, [menuRef, props.layout]);
|
|
81
|
+
|
|
82
|
+
return (
|
|
83
|
+
<div
|
|
84
|
+
className='data-view'
|
|
85
|
+
>
|
|
86
|
+
{ props.layout === 'top' && (
|
|
87
|
+
<MenuBar
|
|
88
|
+
header={{
|
|
89
|
+
content: props.title
|
|
90
|
+
}}
|
|
91
|
+
items={menu}
|
|
92
|
+
/>
|
|
93
|
+
)}
|
|
94
|
+
{ props.layout === 'left' && (
|
|
95
|
+
<MenuSidebar
|
|
96
|
+
contextRef={menuRef}
|
|
97
|
+
header={{
|
|
98
|
+
content: props.title
|
|
99
|
+
}}
|
|
100
|
+
items={menu}
|
|
101
|
+
/>
|
|
102
|
+
)}
|
|
103
|
+
<Container
|
|
104
|
+
style={{
|
|
105
|
+
paddingLeft
|
|
106
|
+
}}
|
|
107
|
+
>
|
|
108
|
+
{ activeItem && (
|
|
109
|
+
<ListTable
|
|
110
|
+
collectionName='items'
|
|
111
|
+
columns={columns}
|
|
112
|
+
onLoad={(params) => Api.onLoad(_.extend(params, {
|
|
113
|
+
items,
|
|
114
|
+
perPage: number('Per page', 10)
|
|
115
|
+
}))}
|
|
116
|
+
searchable
|
|
117
|
+
/>
|
|
118
|
+
)}
|
|
119
|
+
<div>Content</div>
|
|
120
|
+
</Container>
|
|
121
|
+
</div>
|
|
122
|
+
);
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
export default DataView;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { Icon, Input } from 'semantic-ui-react';
|
|
5
|
+
import './DateInput.css';
|
|
6
|
+
|
|
7
|
+
type Props = {
|
|
8
|
+
display?: string,
|
|
9
|
+
formatOptions?: any,
|
|
10
|
+
locale?: string,
|
|
11
|
+
onChange: (date: ?Date) => void,
|
|
12
|
+
onClick: () => void,
|
|
13
|
+
value?: ?Date
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const DateInput = (props: Props) => {
|
|
17
|
+
const formatDate = () => {
|
|
18
|
+
let date = '';
|
|
19
|
+
|
|
20
|
+
if (props.display) {
|
|
21
|
+
date = props.display;
|
|
22
|
+
} else if (props.value) {
|
|
23
|
+
date = props.value.toLocaleDateString(props.locale, props.formatOptions);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return date;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<Input
|
|
31
|
+
className='date-input icon'
|
|
32
|
+
icon='calendar alternate outline'
|
|
33
|
+
iconPosition='left'
|
|
34
|
+
>
|
|
35
|
+
<Icon
|
|
36
|
+
className='left icon'
|
|
37
|
+
name='calendar alternate outline'
|
|
38
|
+
/>
|
|
39
|
+
<input
|
|
40
|
+
onClick={props.onClick.bind(this)}
|
|
41
|
+
readOnly
|
|
42
|
+
type='text'
|
|
43
|
+
value={formatDate()}
|
|
44
|
+
/>
|
|
45
|
+
<Icon
|
|
46
|
+
className='right icon'
|
|
47
|
+
name='times'
|
|
48
|
+
onClick={(e) => {
|
|
49
|
+
e.stopPropagation();
|
|
50
|
+
e.preventDefault();
|
|
51
|
+
props.onChange(null);
|
|
52
|
+
}}
|
|
53
|
+
/>
|
|
54
|
+
</Input>
|
|
55
|
+
);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export default DateInput;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
|
|
3
|
+
import React, { useEffect, useRef, useState } from 'react';
|
|
4
|
+
import Calendar from 'react-calendar';
|
|
5
|
+
import { Icon, Transition } from 'semantic-ui-react';
|
|
6
|
+
import DateInput from './DateInput';
|
|
7
|
+
|
|
8
|
+
import './DatePicker.css';
|
|
9
|
+
|
|
10
|
+
type Props = {
|
|
11
|
+
closeOnSelection?: boolean,
|
|
12
|
+
formatOptions?: any,
|
|
13
|
+
locale?: string,
|
|
14
|
+
onChange: (date: ?Date) => void,
|
|
15
|
+
value: ?Date
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const DatePicker = (props: Props) => {
|
|
19
|
+
const [calendar, setCalendar] = useState(false);
|
|
20
|
+
const calendarWrapper = useRef<any>(null);
|
|
21
|
+
|
|
22
|
+
const onDocumentClick = (event: Event) => {
|
|
23
|
+
const calendarInstance = calendarWrapper.current;
|
|
24
|
+
|
|
25
|
+
if (calendarInstance && !calendarInstance.contains(event.target)) {
|
|
26
|
+
setCalendar(false);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
// Bind the event listener
|
|
32
|
+
document.addEventListener('mousedown', onDocumentClick);
|
|
33
|
+
|
|
34
|
+
// Unbind the event listener
|
|
35
|
+
return () => document.removeEventListener('mousedown', onDocumentClick);
|
|
36
|
+
}, [calendarWrapper]);
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<>
|
|
40
|
+
<DateInput
|
|
41
|
+
formatOptions={props.formatOptions}
|
|
42
|
+
locale={props.locale}
|
|
43
|
+
onChange={props.onChange.bind(this)}
|
|
44
|
+
onClick={() => setCalendar(true)}
|
|
45
|
+
value={props.value}
|
|
46
|
+
/>
|
|
47
|
+
<Transition
|
|
48
|
+
visible={calendar}
|
|
49
|
+
>
|
|
50
|
+
<div
|
|
51
|
+
ref={calendarWrapper}
|
|
52
|
+
style={{
|
|
53
|
+
position: 'absolute',
|
|
54
|
+
zIndex: '999'
|
|
55
|
+
}}
|
|
56
|
+
>
|
|
57
|
+
<Calendar
|
|
58
|
+
locale={props.locale}
|
|
59
|
+
onChange={(date) => {
|
|
60
|
+
props.onChange(date);
|
|
61
|
+
if (props.closeOnSelection) {
|
|
62
|
+
setCalendar(false);
|
|
63
|
+
}
|
|
64
|
+
}}
|
|
65
|
+
next2Label={<Icon name='angle double right' />}
|
|
66
|
+
nextLabel={<Icon name='chevron right' />}
|
|
67
|
+
prev2Label={<Icon name='angle double left' />}
|
|
68
|
+
prevLabel={<Icon name='chevron left' />}
|
|
69
|
+
value={props.value}
|
|
70
|
+
/>
|
|
71
|
+
</div>
|
|
72
|
+
</Transition>
|
|
73
|
+
</>
|
|
74
|
+
);
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
DatePicker.defaultProps = {
|
|
78
|
+
closeOnSelection: true
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export default DatePicker;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
|
|
3
|
+
import React, { type Component } from 'react';
|
|
4
|
+
import { Popup } from 'semantic-ui-react';
|
|
5
|
+
|
|
6
|
+
type Props = {
|
|
7
|
+
className?: string,
|
|
8
|
+
content: string,
|
|
9
|
+
delayInterval?: number,
|
|
10
|
+
renderPopup?: () => Component<any>,
|
|
11
|
+
popupContent?: string
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Renders the DescriptorField component. This component can be used to static text as with a popup on hover.
|
|
16
|
+
*
|
|
17
|
+
* @param props
|
|
18
|
+
*
|
|
19
|
+
* @returns {*}
|
|
20
|
+
*
|
|
21
|
+
* @constructor
|
|
22
|
+
*/
|
|
23
|
+
const DescriptorField = (props: Props) => (
|
|
24
|
+
<Popup
|
|
25
|
+
className={props.className}
|
|
26
|
+
content={props.popupContent}
|
|
27
|
+
hoverable
|
|
28
|
+
mouseEnterDelay={props.delayInterval}
|
|
29
|
+
trigger={props.renderPopup
|
|
30
|
+
? props.renderPopup()
|
|
31
|
+
: (<span className='text'>{ props.content }</span>)}
|
|
32
|
+
wide
|
|
33
|
+
/>
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
DescriptorField.defaultProps = {
|
|
37
|
+
delayInterval: 1000,
|
|
38
|
+
renderPopup: undefined,
|
|
39
|
+
popupContent: undefined
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export default DescriptorField;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import uuid from 'react-uuid';
|
|
5
|
+
import { Button } from 'semantic-ui-react';
|
|
6
|
+
|
|
7
|
+
type Props = {
|
|
8
|
+
filename?: string,
|
|
9
|
+
url: string
|
|
10
|
+
};
|
|
11
|
+
|
|
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
|
+
);
|
|
22
|
+
|
|
23
|
+
export default DownloadButton;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
|
|
3
|
+
import React, { type Element, useRef } from 'react';
|
|
4
|
+
import { Ref } from 'semantic-ui-react';
|
|
5
|
+
import { useDrag, useDrop } from 'react-dnd';
|
|
6
|
+
|
|
7
|
+
type Props = {
|
|
8
|
+
children: Element<any>,
|
|
9
|
+
id: any,
|
|
10
|
+
index: number,
|
|
11
|
+
onDrag: (dragIndex: number, hoverIndex: number) => void
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const TYPE_ANY = 'any';
|
|
15
|
+
|
|
16
|
+
const Draggable = (props: Props) => {
|
|
17
|
+
const { index, id } = props;
|
|
18
|
+
|
|
19
|
+
const ref = useRef(null);
|
|
20
|
+
const [, drop] = useDrop({
|
|
21
|
+
accept: TYPE_ANY,
|
|
22
|
+
hover(i, monitor) {
|
|
23
|
+
if (!ref.current) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const dragIndex = i.index;
|
|
28
|
+
const hoverIndex = index;
|
|
29
|
+
|
|
30
|
+
// Don't replace items with themselves
|
|
31
|
+
if (dragIndex === hoverIndex) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Determine rectangle on screen
|
|
36
|
+
const hoverBoundingRect = ref.current.getBoundingClientRect();
|
|
37
|
+
|
|
38
|
+
// Get vertical middle
|
|
39
|
+
const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;
|
|
40
|
+
|
|
41
|
+
// Determine mouse position
|
|
42
|
+
const clientOffset = monitor.getClientOffset();
|
|
43
|
+
|
|
44
|
+
// Get pixels to the top
|
|
45
|
+
const hoverClientY = clientOffset.y - hoverBoundingRect.top;
|
|
46
|
+
|
|
47
|
+
// Only perform the move when the mouse has crossed half of the items height
|
|
48
|
+
// When dragging downwards, only move when the cursor is below 50%
|
|
49
|
+
// When dragging upwards, only move when the cursor is above 50%
|
|
50
|
+
// Dragging downwards
|
|
51
|
+
if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Dragging upwards
|
|
56
|
+
if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Time to actually perform the action
|
|
61
|
+
props.onDrag(dragIndex, hoverIndex);
|
|
62
|
+
|
|
63
|
+
// // Note: we're mutating the monitor item here!
|
|
64
|
+
// // Generally it's better to avoid mutations,
|
|
65
|
+
// // but it's good here for the sake of performance
|
|
66
|
+
// // to avoid expensive index searches.
|
|
67
|
+
// eslint-disable-next-line no-param-reassign
|
|
68
|
+
i.index = hoverIndex;
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
const [{ isDragging }, drag] = useDrag({
|
|
73
|
+
item: { type: TYPE_ANY, id, index },
|
|
74
|
+
collect: (monitor) => ({
|
|
75
|
+
isDragging: monitor.isDragging(),
|
|
76
|
+
}),
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
drag(drop(ref));
|
|
80
|
+
|
|
81
|
+
if (ref && ref.current) {
|
|
82
|
+
ref.current.style.opacity = isDragging ? 0 : 1;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return (
|
|
86
|
+
<Ref
|
|
87
|
+
innerRef={ref}
|
|
88
|
+
>
|
|
89
|
+
{ props.children }
|
|
90
|
+
</Ref>
|
|
91
|
+
);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
export default Draggable;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
|
|
3
|
+
import React, { useRef } from 'react';
|
|
4
|
+
import { Button, Dropdown } from 'semantic-ui-react';
|
|
5
|
+
import './DropdownButton.css';
|
|
6
|
+
|
|
7
|
+
type Option = {
|
|
8
|
+
key: any,
|
|
9
|
+
value: any,
|
|
10
|
+
text: string
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
type Props = {
|
|
14
|
+
basic?: boolean,
|
|
15
|
+
color?: string,
|
|
16
|
+
direction?: string,
|
|
17
|
+
disabled?: boolean,
|
|
18
|
+
icon?: string,
|
|
19
|
+
options: Array<Option>,
|
|
20
|
+
onChange: (e: Event, { value: any }) => void,
|
|
21
|
+
scrolling?: boolean,
|
|
22
|
+
selectOnBlur?: boolean,
|
|
23
|
+
text: string,
|
|
24
|
+
value: any
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const DropdownButton = (props: Props) => {
|
|
28
|
+
const dropdownRef = useRef();
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<Button.Group
|
|
32
|
+
basic={props.basic}
|
|
33
|
+
className='dropdown-button'
|
|
34
|
+
color={props.color}
|
|
35
|
+
>
|
|
36
|
+
<Button
|
|
37
|
+
content={props.text}
|
|
38
|
+
disabled={props.disabled}
|
|
39
|
+
icon={props.icon}
|
|
40
|
+
onClick={(e) => dropdownRef.current && dropdownRef.current.handleClick(e)}
|
|
41
|
+
/>
|
|
42
|
+
<Dropdown
|
|
43
|
+
className='button icon'
|
|
44
|
+
direction={props.direction}
|
|
45
|
+
disabled={props.disabled}
|
|
46
|
+
floating
|
|
47
|
+
onChange={props.onChange.bind(this)}
|
|
48
|
+
options={props.options}
|
|
49
|
+
ref={dropdownRef}
|
|
50
|
+
scrolling={props.scrolling}
|
|
51
|
+
selectOnBlur={props.selectOnBlur}
|
|
52
|
+
trigger={<></>}
|
|
53
|
+
value={props.value}
|
|
54
|
+
/>
|
|
55
|
+
</Button.Group>
|
|
56
|
+
);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
DropdownButton.defaultProps = {
|
|
60
|
+
color: undefined,
|
|
61
|
+
icon: undefined,
|
|
62
|
+
selectOnBlur: false
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export default DropdownButton;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
|
|
3
|
+
import React, {
|
|
4
|
+
useCallback,
|
|
5
|
+
useEffect,
|
|
6
|
+
useRef,
|
|
7
|
+
useState,
|
|
8
|
+
type Node
|
|
9
|
+
} from 'react';
|
|
10
|
+
import { Dropdown, Ref } from 'semantic-ui-react';
|
|
11
|
+
|
|
12
|
+
type Props = {
|
|
13
|
+
children: Node,
|
|
14
|
+
onClick?: () => void
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const DropdownMenu = (props: Props) => {
|
|
18
|
+
const [open, setOpen] = useState(false);
|
|
19
|
+
const ref = useRef();
|
|
20
|
+
|
|
21
|
+
const onMouseEnter = useCallback(() => setOpen(true), []);
|
|
22
|
+
const onMouseLeave = useCallback(() => setOpen(false), []);
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Adds/removes the mouseenter/mouseleave events.
|
|
26
|
+
*/
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
if (ref.current) {
|
|
29
|
+
ref.current.addEventListener('mouseenter', onMouseEnter);
|
|
30
|
+
ref.current.addEventListener('mouseleave', onMouseLeave);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return () => {
|
|
34
|
+
if (ref.current) {
|
|
35
|
+
ref.current.removeEventListener('mouseenter', onMouseEnter);
|
|
36
|
+
ref.current.removeEventListener('mouseleave', onMouseLeave);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
}, [ref]);
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<Ref
|
|
43
|
+
innerRef={ref}
|
|
44
|
+
>
|
|
45
|
+
<Dropdown
|
|
46
|
+
{...props}
|
|
47
|
+
open={open}
|
|
48
|
+
onClick={() => {
|
|
49
|
+
if (props.onClick) {
|
|
50
|
+
props.onClick();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
setOpen(false);
|
|
54
|
+
}}
|
|
55
|
+
>
|
|
56
|
+
<Dropdown.Menu>
|
|
57
|
+
{ props.children }
|
|
58
|
+
</Dropdown.Menu>
|
|
59
|
+
</Dropdown>
|
|
60
|
+
</Ref>
|
|
61
|
+
);
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
DropdownMenu.defaultProps = {
|
|
65
|
+
onClick: undefined
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export default DropdownMenu;
|