@performant-software/semantic-components 1.0.18 → 1.0.19-beta.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/build/index.js +1 -1
- package/build/index.js.map +1 -1
- package/build/main.css +12 -0
- package/package.json +2 -2
- package/src/components/BreadcrumbItem.css +0 -0
- package/src/components/BreadcrumbItem.js +69 -0
- package/src/components/Breadcrumbs.css +4 -0
- package/src/components/Breadcrumbs.js +111 -0
- package/src/components/SimpleEditPage.css +7 -0
- package/src/components/SimpleEditPage.js +246 -0
- package/src/i18n/en.json +8 -1
- package/src/index.js +2 -0
- package/types/components/BreadcrumbItem.js.flow +69 -0
- package/types/components/Breadcrumbs.js.flow +111 -0
- package/types/components/SimpleEditPage.js.flow +246 -0
- package/types/index.js.flow +2 -0
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
|
|
3
|
+
import { Element } from '@performant-software/shared-components';
|
|
4
|
+
import type { EditContainerProps } from '@performant-software/shared-components/types';
|
|
5
|
+
import React, {
|
|
6
|
+
useCallback,
|
|
7
|
+
useEffect,
|
|
8
|
+
useMemo,
|
|
9
|
+
useRef,
|
|
10
|
+
useState
|
|
11
|
+
} from 'react';
|
|
12
|
+
import {
|
|
13
|
+
Button,
|
|
14
|
+
Form,
|
|
15
|
+
Grid,
|
|
16
|
+
Menu,
|
|
17
|
+
MenuProps,
|
|
18
|
+
Message,
|
|
19
|
+
Ref,
|
|
20
|
+
Sticky
|
|
21
|
+
} from 'semantic-ui-react';
|
|
22
|
+
import _ from 'underscore';
|
|
23
|
+
import i18n from '../i18n/i18n';
|
|
24
|
+
import Toaster from './Toaster';
|
|
25
|
+
import './SimpleEditPage.css';
|
|
26
|
+
|
|
27
|
+
type Props = EditContainerProps & {
|
|
28
|
+
/**
|
|
29
|
+
* Additional class attribute to apply to the root DOM element.
|
|
30
|
+
*/
|
|
31
|
+
className?: string,
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Sets the default visible tab. If no value is provided, the first tab will be visible.
|
|
35
|
+
*/
|
|
36
|
+
defaultTab?: string,
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Props to provide to the Semantic UI `Menu` component.
|
|
40
|
+
*/
|
|
41
|
+
menuProps?: typeof MenuProps,
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Callback fired when the "Cancel" button is clicked.
|
|
45
|
+
*/
|
|
46
|
+
onCancel: () => void,
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Callback fired when a tab is clicked.
|
|
50
|
+
*
|
|
51
|
+
* @param tab
|
|
52
|
+
*/
|
|
53
|
+
onTabClick?: (tab: string) => void,
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* If `true`, the saved toaster will display when the component is mounted.
|
|
57
|
+
*/
|
|
58
|
+
saved?: boolean,
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* If `true`, the tabs menu will "stick" to the top of the window.
|
|
62
|
+
*/
|
|
63
|
+
stickyMenu?: boolean
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* This component can be used to render the layout for a form/page with edit capabilities. Use in conjunction with the
|
|
68
|
+
* `withEditPage` higher-order component for a fully fledged record editing environment.
|
|
69
|
+
*/
|
|
70
|
+
const SimpleEditPage: any = (props: Props) => {
|
|
71
|
+
const [currentTab, setCurrentTab] = useState();
|
|
72
|
+
const [saved, setSaved] = useState(false);
|
|
73
|
+
|
|
74
|
+
const contentRef = useRef();
|
|
75
|
+
|
|
76
|
+
// $FlowIgnore
|
|
77
|
+
const tabs = Element.findByType(props.children, SimpleEditPage.Tab);
|
|
78
|
+
const tab = useMemo(() => _.find(tabs, (nextTab) => nextTab.key === currentTab), [currentTab, tabs]);
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Memo-izes the class name variable.
|
|
82
|
+
*
|
|
83
|
+
* @type {string}
|
|
84
|
+
*/
|
|
85
|
+
const className = useMemo(() => {
|
|
86
|
+
const classNames = ['simple-edit-page'];
|
|
87
|
+
|
|
88
|
+
if (props.className) {
|
|
89
|
+
classNames.push(props.className);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return classNames.join(' ');
|
|
93
|
+
}, [props.className]);
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Sets the current tab.
|
|
97
|
+
*
|
|
98
|
+
* @type {(function(*): void)|*}
|
|
99
|
+
*/
|
|
100
|
+
const onTabClick = useCallback((item) => {
|
|
101
|
+
const { key } = item;
|
|
102
|
+
setCurrentTab(key);
|
|
103
|
+
|
|
104
|
+
if (props.onTabClick) {
|
|
105
|
+
props.onTabClick(key);
|
|
106
|
+
}
|
|
107
|
+
}, [props.onTabClick]);
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Renders the tab menu component.
|
|
111
|
+
*
|
|
112
|
+
* @type {(function(): (*))|*}
|
|
113
|
+
*/
|
|
114
|
+
const renderTabs = useCallback(() => {
|
|
115
|
+
const menu = (
|
|
116
|
+
<Menu
|
|
117
|
+
{...props.menuProps}
|
|
118
|
+
>
|
|
119
|
+
{ tabs?.length > 1 && _.map(tabs, (item) => (
|
|
120
|
+
<Menu.Item
|
|
121
|
+
active={item.key === currentTab}
|
|
122
|
+
disabled={props.loading || props.saving}
|
|
123
|
+
key={item.key}
|
|
124
|
+
name={item.props.name}
|
|
125
|
+
onClick={() => onTabClick(item)}
|
|
126
|
+
/>
|
|
127
|
+
))}
|
|
128
|
+
<Menu.Menu
|
|
129
|
+
position='right'
|
|
130
|
+
>
|
|
131
|
+
<Menu.Item
|
|
132
|
+
className='button-container'
|
|
133
|
+
>
|
|
134
|
+
<Button
|
|
135
|
+
content={i18n.t('Common.buttons.save')}
|
|
136
|
+
disabled={props.loading || props.saving}
|
|
137
|
+
onClick={props.onSave}
|
|
138
|
+
primary
|
|
139
|
+
/>
|
|
140
|
+
<Button
|
|
141
|
+
basic
|
|
142
|
+
content={i18n.t('Common.buttons.cancel')}
|
|
143
|
+
disabled={props.loading || props.saving}
|
|
144
|
+
onClick={props.onCancel}
|
|
145
|
+
/>
|
|
146
|
+
</Menu.Item>
|
|
147
|
+
</Menu.Menu>
|
|
148
|
+
</Menu>
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
if (props.stickyMenu) {
|
|
152
|
+
return (
|
|
153
|
+
<Sticky
|
|
154
|
+
context={contentRef}
|
|
155
|
+
offset={20}
|
|
156
|
+
>
|
|
157
|
+
{ menu }
|
|
158
|
+
</Sticky>
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return menu;
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
useEffect(() => {
|
|
166
|
+
// Sets the default tab to the first tab in the list, or the tab on the URL state
|
|
167
|
+
let defaultTab;
|
|
168
|
+
|
|
169
|
+
if (props.defaultTab) {
|
|
170
|
+
defaultTab = { key: props.defaultTab };
|
|
171
|
+
} else {
|
|
172
|
+
defaultTab = _.first(tabs);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (defaultTab) {
|
|
176
|
+
onTabClick(defaultTab);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Sets the saved indicator based on the URL state
|
|
180
|
+
if (props.saved) {
|
|
181
|
+
setSaved(true);
|
|
182
|
+
}
|
|
183
|
+
}, []);
|
|
184
|
+
|
|
185
|
+
return (
|
|
186
|
+
<Grid
|
|
187
|
+
className={className}
|
|
188
|
+
>
|
|
189
|
+
<Grid.Row>
|
|
190
|
+
<Grid.Column>
|
|
191
|
+
{ renderTabs() }
|
|
192
|
+
</Grid.Column>
|
|
193
|
+
</Grid.Row>
|
|
194
|
+
<Grid.Row>
|
|
195
|
+
<Grid.Column>
|
|
196
|
+
<Ref
|
|
197
|
+
innerRef={contentRef}
|
|
198
|
+
>
|
|
199
|
+
<div>
|
|
200
|
+
<Form
|
|
201
|
+
error={!_.isEmpty(props.errors)}
|
|
202
|
+
loading={props.loading || props.saving}
|
|
203
|
+
noValidate
|
|
204
|
+
>
|
|
205
|
+
<Message
|
|
206
|
+
error
|
|
207
|
+
header={i18n.t('Common.errors.save')}
|
|
208
|
+
list={props.errors}
|
|
209
|
+
/>
|
|
210
|
+
{ tab && tab.props.children }
|
|
211
|
+
</Form>
|
|
212
|
+
{ saved && (
|
|
213
|
+
<Toaster
|
|
214
|
+
onDismiss={() => setSaved(false)}
|
|
215
|
+
type={Toaster.MessageTypes.positive}
|
|
216
|
+
>
|
|
217
|
+
<Message.Header
|
|
218
|
+
content={i18n.t('Common.messages.save.header')}
|
|
219
|
+
/>
|
|
220
|
+
<Message.Content
|
|
221
|
+
content={i18n.t('Common.messages.save.content')}
|
|
222
|
+
/>
|
|
223
|
+
</Toaster>
|
|
224
|
+
)}
|
|
225
|
+
</div>
|
|
226
|
+
</Ref>
|
|
227
|
+
</Grid.Column>
|
|
228
|
+
</Grid.Row>
|
|
229
|
+
</Grid>
|
|
230
|
+
);
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
SimpleEditPage.defaultProps = {
|
|
234
|
+
menuProps: {
|
|
235
|
+
pointing: true,
|
|
236
|
+
secondary: true
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
const Tab = (props: any) => props.children;
|
|
241
|
+
Tab.displayName = 'Tab';
|
|
242
|
+
|
|
243
|
+
// $FlowIgnore
|
|
244
|
+
const SimpleEditPageStatic = Object.assign(SimpleEditPage, { Tab });
|
|
245
|
+
|
|
246
|
+
export default SimpleEditPageStatic;
|
package/types/index.js.flow
CHANGED
|
@@ -14,6 +14,7 @@ export { default as BibliographyForm } from './components/BibliographyForm';
|
|
|
14
14
|
export { default as BibliographyList } from './components/BibliographyList';
|
|
15
15
|
export { default as BibliographySearchInput } from './components/BibliographySearchInput';
|
|
16
16
|
export { default as BooleanIcon } from './components/BooleanIcon';
|
|
17
|
+
export { default as Breadcrumbs } from './components/Breadcrumbs';
|
|
17
18
|
export { default as CancelButton } from './components/CancelButton';
|
|
18
19
|
export { default as ColorButton } from './components/ColorButton';
|
|
19
20
|
export { default as ColorPickerModal } from './components/ColorPickerModal';
|
|
@@ -88,6 +89,7 @@ export { default as Section } from './components/Section';
|
|
|
88
89
|
export { default as Selectize } from './components/Selectize';
|
|
89
90
|
export { default as SelectizeHeader } from './components/SelectizeHeader';
|
|
90
91
|
export { default as SelectizeImageHeader } from './components/SelectizeImageHeader';
|
|
92
|
+
export { default as SimpleEditPage } from './components/SimpleEditPage';
|
|
91
93
|
export { default as StyleSelector } from './components/StyleSelector';
|
|
92
94
|
export { default as TabbedModal } from './components/TabbedModal';
|
|
93
95
|
export { default as TabsMenu } from './components/TabsMenu';
|