@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.
Files changed (218) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +0 -0
  3. package/build/index.js +2 -0
  4. package/build/index.js.map +1 -0
  5. package/build/main.css +786 -0
  6. package/index.js +1 -0
  7. package/package.json +37 -0
  8. package/src/components/AccordionDataList.css +8 -0
  9. package/src/components/AccordionDataList.js +224 -0
  10. package/src/components/AccordionList.css +27 -0
  11. package/src/components/AccordionList.js +596 -0
  12. package/src/components/AccordionSelector.css +3 -0
  13. package/src/components/AccordionSelector.js +359 -0
  14. package/src/components/ArrowButtons.css +4 -0
  15. package/src/components/ArrowButtons.js +38 -0
  16. package/src/components/AssociatedDropdown.css +44 -0
  17. package/src/components/AssociatedDropdown.js +338 -0
  18. package/src/components/BooleanIcon.css +0 -0
  19. package/src/components/BooleanIcon.js +33 -0
  20. package/src/components/CancelButton.css +0 -0
  21. package/src/components/CancelButton.js +25 -0
  22. package/src/components/ColorButton.css +4 -0
  23. package/src/components/ColorButton.js +34 -0
  24. package/src/components/ColorPickerModal.css +3 -0
  25. package/src/components/ColorPickerModal.js +77 -0
  26. package/src/components/ColumnResize.css +9 -0
  27. package/src/components/ColumnResize.js +20 -0
  28. package/src/components/DataList.css +0 -0
  29. package/src/components/DataList.js +531 -0
  30. package/src/components/DataTable.css +43 -0
  31. package/src/components/DataTable.js +596 -0
  32. package/src/components/DataTableColumnSelector.css +10 -0
  33. package/src/components/DataTableColumnSelector.js +146 -0
  34. package/src/components/DateInput.css +6 -0
  35. package/src/components/DateInput.js +58 -0
  36. package/src/components/DatePicker.css +72 -0
  37. package/src/components/DatePicker.js +81 -0
  38. package/src/components/DescriptorField.css +0 -0
  39. package/src/components/DescriptorField.js +42 -0
  40. package/src/components/DownloadButton.css +0 -0
  41. package/src/components/DownloadButton.js +23 -0
  42. package/src/components/Draggable.css +0 -0
  43. package/src/components/Draggable.js +94 -0
  44. package/src/components/DropdownButton.css +3 -0
  45. package/src/components/DropdownButton.js +65 -0
  46. package/src/components/DropdownMenu.css +0 -0
  47. package/src/components/DropdownMenu.js +68 -0
  48. package/src/components/EditModal.css +8 -0
  49. package/src/components/EditModal.js +99 -0
  50. package/src/components/EditPage.css +7 -0
  51. package/src/components/EditPage.js +249 -0
  52. package/src/components/EmbeddedList.css +7 -0
  53. package/src/components/EmbeddedList.js +278 -0
  54. package/src/components/FileInputButton.css +0 -0
  55. package/src/components/FileInputButton.js +54 -0
  56. package/src/components/FileUpload.css +31 -0
  57. package/src/components/FileUpload.js +188 -0
  58. package/src/components/FileUploadModal.css +0 -0
  59. package/src/components/FileUploadModal.js +408 -0
  60. package/src/components/FuzzyDate.css +8 -0
  61. package/src/components/FuzzyDate.js +575 -0
  62. package/src/components/GoogleMap.css +0 -0
  63. package/src/components/GoogleMap.js +105 -0
  64. package/src/components/GooglePlacesSearch.css +0 -0
  65. package/src/components/GooglePlacesSearch.js +43 -0
  66. package/src/components/HorizontalCards.css +50 -0
  67. package/src/components/HorizontalCards.js +226 -0
  68. package/src/components/ItemCollection.css +3 -0
  69. package/src/components/ItemCollection.js +159 -0
  70. package/src/components/ItemList.css +0 -0
  71. package/src/components/ItemList.js +126 -0
  72. package/src/components/Items.css +19 -0
  73. package/src/components/Items.js +365 -0
  74. package/src/components/ItemsToggle.css +0 -0
  75. package/src/components/ItemsToggle.js +168 -0
  76. package/src/components/KeyboardField.css +4 -0
  77. package/src/components/KeyboardField.js +147 -0
  78. package/src/components/LazyDocument.css +21 -0
  79. package/src/components/LazyDocument.js +113 -0
  80. package/src/components/LazyImage.css +21 -0
  81. package/src/components/LazyImage.js +119 -0
  82. package/src/components/LazyVideo.css +21 -0
  83. package/src/components/LazyVideo.js +131 -0
  84. package/src/components/LinkButton.css +8 -0
  85. package/src/components/LinkButton.js +23 -0
  86. package/src/components/LinkLabel.css +8 -0
  87. package/src/components/LinkLabel.js +29 -0
  88. package/src/components/List.css +8 -0
  89. package/src/components/List.js +761 -0
  90. package/src/components/ListFilters.css +0 -0
  91. package/src/components/ListFilters.js +408 -0
  92. package/src/components/ListLoader.css +8 -0
  93. package/src/components/ListLoader.js +32 -0
  94. package/src/components/ListTable.css +3 -0
  95. package/src/components/ListTable.js +86 -0
  96. package/src/components/LoginModal.css +7 -0
  97. package/src/components/LoginModal.js +102 -0
  98. package/src/components/MasonryGrid.css +48 -0
  99. package/src/components/MasonryGrid.js +202 -0
  100. package/src/components/MediaGallery.css +37 -0
  101. package/src/components/MediaGallery.js +148 -0
  102. package/src/components/MediaGrid.css +72 -0
  103. package/src/components/MediaGrid.js +74 -0
  104. package/src/components/MediaList.css +3 -0
  105. package/src/components/MediaList.js +98 -0
  106. package/src/components/ModalDropdown.css +11 -0
  107. package/src/components/ModalDropdown.js +84 -0
  108. package/src/components/NestedAccordion.css +41 -0
  109. package/src/components/NestedAccordion.js +276 -0
  110. package/src/components/PhotoViewer.css +3 -0
  111. package/src/components/PhotoViewer.js +36 -0
  112. package/src/components/PlayButton.css +3 -0
  113. package/src/components/PlayButton.js +37 -0
  114. package/src/components/RemoteDropdown.css +13 -0
  115. package/src/components/RemoteDropdown.js +368 -0
  116. package/src/components/SaveButton.css +0 -0
  117. package/src/components/SaveButton.js +31 -0
  118. package/src/components/Section.css +0 -0
  119. package/src/components/Section.js +41 -0
  120. package/src/components/Selectize.css +11 -0
  121. package/src/components/Selectize.js +297 -0
  122. package/src/components/SelectizeHeader.css +3 -0
  123. package/src/components/SelectizeHeader.js +40 -0
  124. package/src/components/TabbedModal.css +14 -0
  125. package/src/components/TabbedModal.js +165 -0
  126. package/src/components/TabsMenu.css +0 -0
  127. package/src/components/TabsMenu.js +35 -0
  128. package/src/components/TagsList.css +0 -0
  129. package/src/components/TagsList.js +43 -0
  130. package/src/components/Thumbnail.css +0 -0
  131. package/src/components/Thumbnail.js +47 -0
  132. package/src/components/Toaster.css +9 -0
  133. package/src/components/Toaster.js +73 -0
  134. package/src/components/VideoFrameSelector.css +3 -0
  135. package/src/components/VideoFrameSelector.js +148 -0
  136. package/src/components/VideoPlayer.css +3 -0
  137. package/src/components/VideoPlayer.js +55 -0
  138. package/src/components/VideoPlayerButton.css +17 -0
  139. package/src/components/VideoPlayerButton.js +17 -0
  140. package/src/components/ViewXML.css +0 -0
  141. package/src/components/ViewXML.js +72 -0
  142. package/src/i18n/en.json +204 -0
  143. package/src/i18n/i18n.js +24 -0
  144. package/src/index.js +76 -0
  145. package/types/components/AccordionDataList.js.flow +224 -0
  146. package/types/components/AccordionList.js.flow +596 -0
  147. package/types/components/AccordionSelector.js.flow +359 -0
  148. package/types/components/ArrowButtons.js.flow +38 -0
  149. package/types/components/AssociatedDropdown.js.flow +338 -0
  150. package/types/components/BooleanIcon.js.flow +33 -0
  151. package/types/components/CancelButton.js.flow +25 -0
  152. package/types/components/ColorButton.js.flow +34 -0
  153. package/types/components/ColorPickerModal.js.flow +77 -0
  154. package/types/components/ColumnResize.js.flow +20 -0
  155. package/types/components/DataList.js.flow +531 -0
  156. package/types/components/DataTable.js.flow +596 -0
  157. package/types/components/DataTableColumnSelector.js.flow +146 -0
  158. package/types/components/DataView.js.flow +125 -0
  159. package/types/components/DateInput.js.flow +58 -0
  160. package/types/components/DatePicker.js.flow +81 -0
  161. package/types/components/DescriptorField.js.flow +42 -0
  162. package/types/components/DownloadButton.js.flow +23 -0
  163. package/types/components/Draggable.js.flow +94 -0
  164. package/types/components/DropdownButton.js.flow +65 -0
  165. package/types/components/DropdownMenu.js.flow +68 -0
  166. package/types/components/EditModal.js.flow +99 -0
  167. package/types/components/EditPage.js.flow +249 -0
  168. package/types/components/EmbeddedList.js.flow +278 -0
  169. package/types/components/FileInputButton.js.flow +54 -0
  170. package/types/components/FileUpload.js.flow +188 -0
  171. package/types/components/FileUploadModal.js.flow +408 -0
  172. package/types/components/FuzzyDate.js.flow +575 -0
  173. package/types/components/GoogleMap.js.flow +105 -0
  174. package/types/components/GooglePlacesSearch.js.flow +43 -0
  175. package/types/components/HorizontalCards.js.flow +226 -0
  176. package/types/components/ItemCollection.js.flow +159 -0
  177. package/types/components/ItemList.js.flow +126 -0
  178. package/types/components/Items.js.flow +365 -0
  179. package/types/components/ItemsToggle.js.flow +168 -0
  180. package/types/components/KeyboardField.js.flow +147 -0
  181. package/types/components/LazyDocument.js.flow +113 -0
  182. package/types/components/LazyImage.js.flow +119 -0
  183. package/types/components/LazyVideo.js.flow +131 -0
  184. package/types/components/LinkButton.js.flow +23 -0
  185. package/types/components/LinkLabel.js.flow +29 -0
  186. package/types/components/List.js.flow +761 -0
  187. package/types/components/ListFilters.js.flow +408 -0
  188. package/types/components/ListLoader.js.flow +32 -0
  189. package/types/components/ListTable.js.flow +86 -0
  190. package/types/components/LoginModal.js.flow +102 -0
  191. package/types/components/MasonryGrid.js.flow +202 -0
  192. package/types/components/MediaGallery.js.flow +148 -0
  193. package/types/components/MediaGrid.js.flow +74 -0
  194. package/types/components/MediaList.js.flow +98 -0
  195. package/types/components/MenuBar.js.flow +77 -0
  196. package/types/components/MenuSidebar.js.flow +72 -0
  197. package/types/components/ModalDropdown.js.flow +84 -0
  198. package/types/components/NestedAccordion.js.flow +276 -0
  199. package/types/components/PhotoViewer.js.flow +36 -0
  200. package/types/components/PlayButton.js.flow +37 -0
  201. package/types/components/RemoteDropdown.js.flow +368 -0
  202. package/types/components/SaveButton.js.flow +31 -0
  203. package/types/components/Section.js.flow +41 -0
  204. package/types/components/Selectize.js.flow +297 -0
  205. package/types/components/SelectizeHeader.js.flow +40 -0
  206. package/types/components/TabbedModal.js.flow +165 -0
  207. package/types/components/TabsMenu.js.flow +35 -0
  208. package/types/components/TagsList.js.flow +43 -0
  209. package/types/components/Thumbnail.js.flow +47 -0
  210. package/types/components/Toaster.js.flow +73 -0
  211. package/types/components/VideoFrameSelector.js.flow +148 -0
  212. package/types/components/VideoPlayer.js.flow +55 -0
  213. package/types/components/VideoPlayerButton.js.flow +17 -0
  214. package/types/components/ViewXML.js.flow +72 -0
  215. package/types/hooks/Imageable.js.flow +54 -0
  216. package/types/i18n/i18n.js.flow +24 -0
  217. package/types/index.js.flow +78 -0
  218. package/webpack.config.js +3 -0
@@ -0,0 +1,575 @@
1
+ // @flow
2
+
3
+ import { Calendar, Browser } from '@performant-software/shared-components';
4
+ import React, { Component } from 'react';
5
+ import {
6
+ Button,
7
+ Checkbox,
8
+ Dropdown,
9
+ Form,
10
+ Input,
11
+ Modal,
12
+ TextArea
13
+ } from 'semantic-ui-react';
14
+ import _ from 'underscore';
15
+ import i18n from '../i18n/i18n';
16
+ import DateField from './DateInput';
17
+ import './FuzzyDate.css';
18
+
19
+ type DateInput = {
20
+ accuracy: number,
21
+ description: string,
22
+ endDate: string,
23
+ range: boolean,
24
+ startDate: string
25
+ };
26
+
27
+ type DateOutput = {
28
+ accuracy: number,
29
+ description: string,
30
+ endDate?: Date,
31
+ range: boolean,
32
+ startDate?: Date
33
+ };
34
+
35
+ type DateComponent = {
36
+ date: number,
37
+ month: number,
38
+ year: number
39
+ };
40
+
41
+ type Props = {
42
+ calendar?: string,
43
+ date: DateInput,
44
+ description?: boolean,
45
+ locale?: string,
46
+ onChange: (data: DateOutput) => void,
47
+ title?: string
48
+ };
49
+
50
+ type State = {
51
+ accuracy: number,
52
+ calendar: Calendar,
53
+ description: string,
54
+ display: string,
55
+ endDate: DateComponent,
56
+ modal: boolean,
57
+ range: boolean,
58
+ startDate: DateComponent
59
+ };
60
+
61
+ const ACCURACY_DATE = 2;
62
+ const ACCURACY_MONTH = 1;
63
+ const ACCURACY_YEAR = 0;
64
+
65
+ const INTEGER_BASE = 10;
66
+
67
+ const MAX_YEAR_LENGTH = 6;
68
+
69
+ class FuzzyDate extends Component<Props, State> {
70
+ static defaultProps: any;
71
+
72
+ /**
73
+ * Constructs a new FuzzyDate component.
74
+ *
75
+ * @param props
76
+ */
77
+ constructor(props: Props) {
78
+ super(props);
79
+
80
+ this.state = {
81
+ ...this.getInitialState(),
82
+ calendar: new Calendar(props.locale, props.calendar)
83
+ };
84
+ }
85
+
86
+ /**
87
+ * Initializes the FuzzyDate component.
88
+ */
89
+ componentDidMount() {
90
+ this.initializeDate();
91
+ }
92
+
93
+ /**
94
+ * Sets the state based on prop changes.
95
+ *
96
+ * @param prevProps
97
+ */
98
+ componentDidUpdate(prevProps: Props) {
99
+ if (this.props.locale !== prevProps.locale || this.props.calendar !== prevProps.calendar) {
100
+ this.setState({
101
+ calendar: new Calendar(this.props.locale, this.props.calendar)
102
+ });
103
+ }
104
+
105
+ if (this.props.date
106
+ && (this.props.date.startDate !== prevProps.date.startDate
107
+ || this.props.date.endDate !== prevProps.date.endDate)) {
108
+ this.initializeDate();
109
+ }
110
+ }
111
+
112
+ /**
113
+ * Returns the display string for the passed date component.
114
+ *
115
+ * @param dateComponent
116
+ *
117
+ * @returns {*}
118
+ */
119
+ getDisplayDate(dateComponent: DateComponent) {
120
+ const date = this.state.calendar.convertToDate(dateComponent);
121
+ return this.state.calendar.format(date, this.state.accuracy);
122
+ }
123
+
124
+ /**
125
+ * Returns the initial state object.
126
+ *
127
+ * @returns {{endDate: {}, display: string, accuracy: number, description: string, range: boolean,
128
+ * modal: boolean, startDate: {}}}
129
+ */
130
+ getInitialState() {
131
+ return {
132
+ accuracy: ACCURACY_YEAR,
133
+ description: '',
134
+ display: '',
135
+ endDate: {},
136
+ modal: false,
137
+ range: false,
138
+ startDate: {}
139
+ };
140
+ }
141
+
142
+ /**
143
+ * Initializes the date.
144
+ */
145
+ initializeDate() {
146
+ if (this.props.date) {
147
+ const { accuracy = ACCURACY_YEAR, description = '', range = false } = this.props.date;
148
+
149
+ let startDate = {};
150
+ let endDate = {};
151
+
152
+ if (this.props.date.startDate) {
153
+ startDate = this.state.calendar.parseDate(this.props.date.startDate);
154
+ }
155
+
156
+ if (this.props.date.endDate) {
157
+ endDate = this.state.calendar.parseDate(this.props.date.endDate);
158
+ }
159
+
160
+ this.setState({
161
+ accuracy,
162
+ description,
163
+ range,
164
+ startDate,
165
+ endDate
166
+ }, this.setDisplay.bind(this));
167
+ } else {
168
+ this.onAccuracyChange(null, { value: ACCURACY_YEAR });
169
+ }
170
+ }
171
+
172
+ /**
173
+ * Sets the accuracy value on the state. The start and end date values are also adjusted based on the accuracy.
174
+ *
175
+ * @param e
176
+ * @param value
177
+ */
178
+ onAccuracyChange(e: ?Event, { value }: { value: number }) {
179
+ const accuracy = value;
180
+
181
+ this.setState((state) => {
182
+ let endDate = { ...state.endDate };
183
+ let startDate = { ...state.startDate };
184
+
185
+ if (accuracy === ACCURACY_MONTH) {
186
+ endDate = { ...endDate, date: state.calendar.getDefaultDate() };
187
+ startDate = { ...startDate, date: state.calendar.getDefaultDate() };
188
+ } else if (value === ACCURACY_YEAR) {
189
+ endDate = { ...endDate, date: state.calendar.getDefaultDate(), month: state.calendar.getDefaultMonth() };
190
+ startDate = { ...startDate, date: state.calendar.getDefaultDate(), month: state.calendar.getDefaultMonth() };
191
+ }
192
+
193
+ return {
194
+ accuracy,
195
+ endDate,
196
+ startDate
197
+ };
198
+ }, this.setEndDate.bind(this));
199
+ }
200
+
201
+ /**
202
+ * Clears the input date(s).
203
+ */
204
+ onClear() {
205
+ this.setState(this.getInitialState(), this.onSave.bind(this));
206
+ }
207
+
208
+ /**
209
+ * Closes the edit modal.
210
+ */
211
+ onClose() {
212
+ this.setState({ modal: false }, this.initializeDate.bind(this));
213
+ }
214
+
215
+ /**
216
+ * Sets the date value on the state.
217
+ *
218
+ * @param property
219
+ * @param e
220
+ * @param value
221
+ */
222
+ onDateChange(property: string, e: ?Event, { value }: { value: string }) {
223
+ this.setState((state) => ({
224
+ [property]: {
225
+ ...state[property],
226
+ date: value
227
+ }
228
+ }), this.setEndDate.bind(this));
229
+ }
230
+
231
+ /**
232
+ * Sets the date description on the state.
233
+ *
234
+ * @param e
235
+ * @param value
236
+ */
237
+ onDescriptionChange(e: Event, { value }: { value: string }) {
238
+ this.setState({ description: value });
239
+ }
240
+
241
+ /**
242
+ * Opens the edit modal.
243
+ */
244
+ onEdit() {
245
+ this.setState({ modal: true });
246
+ }
247
+
248
+ /**
249
+ * Sets the month value on the state.
250
+ *
251
+ * @param property
252
+ * @param e
253
+ * @param value
254
+ */
255
+ onMonthChange(property: string, e: ?Event, { value }: { value: string }) {
256
+ this.setState((state) => ({
257
+ [property]: {
258
+ ...state[property],
259
+ month: value
260
+ }
261
+ }), this.setEndDate.bind(this));
262
+ }
263
+
264
+ /**
265
+ * Sets the range value on the set.
266
+ */
267
+ onRangeChange() {
268
+ this.setState((state) => ({ range: !state.range }), this.setEndDate.bind(this));
269
+ }
270
+
271
+ /**
272
+ * Sets the display value and closes the edit modal.
273
+ */
274
+ onSave() {
275
+ // Set the display value
276
+ this.setDisplay();
277
+
278
+ // Convert the state date and end date to Date objects can call the onChange prop
279
+ let startDate;
280
+ let endDate;
281
+
282
+ if (!_.isEmpty(this.state.startDate)) {
283
+ startDate = this.state.calendar.convertToDate(this.state.startDate).toDate();
284
+ }
285
+
286
+ if (!_.isEmpty(this.state.endDate)) {
287
+ endDate = this.state.calendar.convertToDate(this.state.endDate).toDate();
288
+ }
289
+
290
+ const { accuracy, description, range } = this.state;
291
+
292
+ this.props.onChange({
293
+ accuracy,
294
+ description,
295
+ range,
296
+ startDate,
297
+ endDate
298
+ });
299
+
300
+ // Close the modal
301
+ this.setState({ modal: false });
302
+ }
303
+
304
+ /**
305
+ * Sets the year value on the state.
306
+ *
307
+ * @param property
308
+ * @param e
309
+ * @param value
310
+ */
311
+ onYearChange(property: string, e: ?Event, { value }: { value: string }) {
312
+ if (value && value.length > MAX_YEAR_LENGTH) {
313
+ return;
314
+ }
315
+
316
+ this.setState((state) => ({
317
+ [property]: {
318
+ ...state[property],
319
+ year: parseInt(value, INTEGER_BASE)
320
+ }
321
+ }), this.setEndDate.bind(this));
322
+ }
323
+
324
+ /**
325
+ * Renders the FuzzyDate component.
326
+ *
327
+ * @returns {*}
328
+ */
329
+ render() {
330
+ return (
331
+ <>
332
+ <DateField
333
+ display={this.state.display}
334
+ onClick={this.onEdit.bind(this)}
335
+ onChange={this.onClear.bind(this)}
336
+ />
337
+ <Modal
338
+ as={Form}
339
+ className='fuzzy-date-modal'
340
+ open={this.state.modal}
341
+ onClose={this.onClose.bind(this)}
342
+ >
343
+ <Modal.Header
344
+ content={this.props.title || i18n.t('FuzzyDate.title')}
345
+ />
346
+ <Modal.Content>
347
+ <Form.Input
348
+ className='accuracy-container'
349
+ label={i18n.t('FuzzyDate.labels.accuracy')}
350
+ >
351
+ <Checkbox
352
+ checked={this.state.accuracy === ACCURACY_YEAR}
353
+ label={i18n.t('FuzzyDate.accuracy.year')}
354
+ name='accuracy'
355
+ onChange={this.onAccuracyChange.bind(this)}
356
+ radio
357
+ value={ACCURACY_YEAR}
358
+ />
359
+ <Checkbox
360
+ checked={this.state.accuracy === ACCURACY_MONTH}
361
+ label={i18n.t('FuzzyDate.accuracy.month')}
362
+ name='accuracy'
363
+ onChange={this.onAccuracyChange.bind(this)}
364
+ radio
365
+ value={ACCURACY_MONTH}
366
+ />
367
+ <Checkbox
368
+ checked={this.state.accuracy === ACCURACY_DATE}
369
+ label={i18n.t('FuzzyDate.accuracy.date')}
370
+ name='accuracy'
371
+ onChange={this.onAccuracyChange.bind(this)}
372
+ radio
373
+ value={ACCURACY_DATE}
374
+ />
375
+ </Form.Input>
376
+ <Form.Group>
377
+ { this.renderYear('startDate') }
378
+ { this.renderMonth('startDate') }
379
+ { this.renderDate('startDate') }
380
+ { !this.state.range && (
381
+ <div
382
+ className='button-container'
383
+ >
384
+ <Button
385
+ basic
386
+ content={i18n.t('FuzzyDate.buttons.addRange')}
387
+ icon='plus'
388
+ onClick={this.onRangeChange.bind(this)}
389
+ />
390
+ </div>
391
+ )}
392
+ </Form.Group>
393
+ { this.state.range && (
394
+ <Form.Group>
395
+ { this.renderYear('endDate') }
396
+ { this.renderMonth('endDate') }
397
+ { this.renderDate('endDate') }
398
+ <div
399
+ className='button-container'
400
+ >
401
+ <Button
402
+ basic
403
+ content={i18n.t('FuzzyDate.buttons.removeRange')}
404
+ icon='times'
405
+ onClick={this.onRangeChange.bind(this)}
406
+ />
407
+ </div>
408
+ </Form.Group>
409
+ )}
410
+ { this.props.description && (
411
+ <Form.Input
412
+ label={i18n.t('FuzzyDate.labels.description')}
413
+ >
414
+ <TextArea
415
+ onChange={this.onDescriptionChange.bind(this)}
416
+ value={this.state.description}
417
+ />
418
+ </Form.Input>
419
+ )}
420
+ </Modal.Content>
421
+ <Modal.Actions>
422
+ <Button
423
+ onClick={this.onSave.bind(this)}
424
+ primary
425
+ size='medium'
426
+ type='submit'
427
+ >
428
+ { i18n.t('Common.buttons.save') }
429
+ </Button>
430
+ <Button
431
+ inverted
432
+ onClick={this.onClose.bind(this)}
433
+ primary
434
+ size='medium'
435
+ type='button'
436
+ >
437
+ { i18n.t('Common.buttons.cancel') }
438
+ </Button>
439
+ </Modal.Actions>
440
+ </Modal>
441
+ </>
442
+ );
443
+ }
444
+
445
+ /**
446
+ * Renders the date dropdown for the passed property.
447
+ *
448
+ * @param property
449
+ *
450
+ * @returns {null|*}
451
+ */
452
+ renderDate(property: string) {
453
+ if (this.state.accuracy !== ACCURACY_DATE) {
454
+ return null;
455
+ }
456
+
457
+ const date = this.state[property];
458
+ const daysInMonth = this.state.calendar.daysInMonth(date.year, date.month);
459
+
460
+ return (
461
+ <Form.Input
462
+ label={i18n.t('FuzzyDate.labels.date')}
463
+ >
464
+ <Dropdown
465
+ id='date-dropdown'
466
+ onChange={this.onDateChange.bind(this, property)}
467
+ options={_.range(1, daysInMonth + 1).map((i) => ({ key: i, value: i, text: i }))}
468
+ selection
469
+ value={date.date || this.state.calendar.getDefaultDate()}
470
+ />
471
+ </Form.Input>
472
+ );
473
+ }
474
+
475
+ /**
476
+ * Renders the month dropdown for the passed property.
477
+ *
478
+ * @param property
479
+ *
480
+ * @returns {null|*}
481
+ */
482
+ renderMonth(property: string) {
483
+ if (this.state.accuracy !== ACCURACY_DATE && this.state.accuracy !== ACCURACY_MONTH) {
484
+ return null;
485
+ }
486
+
487
+ return (
488
+ <Form.Input
489
+ label={i18n.t('FuzzyDate.labels.month')}
490
+ >
491
+ <Dropdown
492
+ onChange={this.onMonthChange.bind(this, property)}
493
+ options={_.map(this.state.calendar.listMonths(), (m, i) => ({ key: i, value: i, text: m }))}
494
+ selection
495
+ value={this.state[property].month || this.state.calendar.getDefaultMonth()}
496
+ />
497
+ </Form.Input>
498
+ );
499
+ }
500
+
501
+ /**
502
+ * Renders the year dropdown for the passed property.
503
+ *
504
+ * @param property
505
+ *
506
+ * @returns {*}
507
+ */
508
+ renderYear(property: string) {
509
+ return (
510
+ <Form.Input
511
+ label={i18n.t('FuzzyDate.labels.year')}
512
+ >
513
+ <Input
514
+ onChange={this.onYearChange.bind(this, property)}
515
+ type='number'
516
+ value={this.state[property].year || ''}
517
+ />
518
+ </Form.Input>
519
+ );
520
+ }
521
+
522
+ /**
523
+ * Sets the display value.
524
+ */
525
+ setDisplay() {
526
+ const display = [];
527
+
528
+ if (this.state.startDate && !_.isEmpty(this.state.startDate)) {
529
+ display.push(this.getDisplayDate(this.state.startDate));
530
+ }
531
+
532
+ if (this.state.range && this.state.endDate && !_.isEmpty(this.state.endDate)) {
533
+ display.push(' - ');
534
+ display.push(this.getDisplayDate(this.state.endDate));
535
+ }
536
+
537
+ this.setState({
538
+ display: display.join(' ')
539
+ });
540
+ }
541
+
542
+ /**
543
+ * Sets the end date value.
544
+ */
545
+ setEndDate() {
546
+ if (this.state.range || !this.state.startDate || _.isEmpty(this.state.startDate)) {
547
+ return;
548
+ }
549
+
550
+ this.setState((state) => {
551
+ let endDate = state.calendar.convertToDate(state.startDate);
552
+
553
+ if (state.accuracy === ACCURACY_YEAR) {
554
+ endDate = state.calendar.addYear(endDate, 1);
555
+ } else if (state.accuracy === ACCURACY_MONTH) {
556
+ endDate = state.calendar.addMonth(endDate, 1);
557
+ } else if (state.accuracy === ACCURACY_DATE) {
558
+ endDate = state.calendar.addDate(endDate, 1);
559
+ }
560
+
561
+ return {
562
+ endDate: state.calendar.parseDate(endDate)
563
+ };
564
+ });
565
+ }
566
+ }
567
+
568
+ FuzzyDate.defaultProps = {
569
+ calendar: Calendar.Calendars.gregorian,
570
+ description: true,
571
+ locale: Browser.isBrowser() && navigator.language,
572
+ title: undefined
573
+ };
574
+
575
+ export default FuzzyDate;
File without changes
@@ -0,0 +1,105 @@
1
+ // @flow
2
+
3
+ import { Map } from '@performant-software/shared-components';
4
+ import {
5
+ GoogleMap as MapComponent,
6
+ Marker
7
+ } from '@react-google-maps/api';
8
+ import React, { useCallback, useEffect, useState } from 'react';
9
+
10
+ type LatLng = {
11
+ lat: () => number,
12
+ lng: () => number
13
+ };
14
+
15
+ type Props = {
16
+ className?: string,
17
+ containerStyle?: any,
18
+ defaultCenter?: {
19
+ lat: number,
20
+ lng: number
21
+ },
22
+ defaultZoom?: number,
23
+ onDragEnd?: (latLng: LatLng) => void,
24
+ position?: {
25
+ lat: number,
26
+ lng: number
27
+ }
28
+ };
29
+
30
+ const DEFAULT_ZOOM = 1;
31
+ const DEFAULT_ZOOM_MARKER = 12;
32
+
33
+ const GoogleMap = (props: Props) => {
34
+ // Convert the position props to floats to avoid Javascript errors.
35
+ const position = Map.getPosition(props.position);
36
+
37
+ const [center, setCenter] = useState(position || props.defaultCenter);
38
+ const [map, setMap] = useState();
39
+
40
+ // If no default zoom is provided and a position is provided, set the default zoom to 12.
41
+ let { defaultZoom } = props;
42
+
43
+ if (!defaultZoom) {
44
+ if (position) {
45
+ defaultZoom = DEFAULT_ZOOM_MARKER;
46
+ } else {
47
+ defaultZoom = DEFAULT_ZOOM;
48
+ }
49
+ }
50
+
51
+ // Call the onDragEnd prop, passing the new location.
52
+ const onDragEnd = ({ latLng }) => {
53
+ if (props.onDragEnd) {
54
+ // $FlowFixMe - Not actually fixing, we're checking for presence here.
55
+ props.onDragEnd({
56
+ lat: latLng.lat(),
57
+ lng: latLng.lng()
58
+ });
59
+ }
60
+ };
61
+
62
+ const onLoad = useCallback((m) => setMap(m), []);
63
+
64
+ // If the position is changed manually and the new location is outside of the current bounds, re-center the map.
65
+ useEffect(() => {
66
+ if (map && position) {
67
+ const bounds = map.getBounds();
68
+ if (bounds && !bounds.contains(position)) {
69
+ setCenter(position);
70
+ }
71
+ }
72
+ }, [props.position]);
73
+
74
+ return (
75
+ <MapComponent
76
+ center={center}
77
+ mapContainerClassName={props.className}
78
+ mapContainerStyle={props.containerStyle}
79
+ onClick={onDragEnd}
80
+ onLoad={onLoad}
81
+ zoom={defaultZoom}
82
+ >
83
+ { position && (
84
+ <Marker
85
+ draggable={!!props.onDragEnd}
86
+ onDragEnd={onDragEnd}
87
+ position={position}
88
+ visible
89
+ />
90
+ )}
91
+ </MapComponent>
92
+ );
93
+ };
94
+
95
+ GoogleMap.defaultProps = {
96
+ containerStyle: {
97
+ height: '400px'
98
+ },
99
+ defaultCenter: {
100
+ lat: 0,
101
+ lng: 0
102
+ }
103
+ };
104
+
105
+ export default GoogleMap;
File without changes
@@ -0,0 +1,43 @@
1
+ // @flow
2
+
3
+ import { StandaloneSearchBox } from '@react-google-maps/api';
4
+ import React, { useCallback, useState, type Element } from 'react';
5
+ import _ from 'underscore';
6
+
7
+ type Props = {
8
+ children: Element<any>,
9
+ onLocationSelection: ({ lat: number, lng: number }) => void
10
+ }
11
+
12
+ const GooglePlacesSearch = (props: Props) => {
13
+ const [searchBox, setSearchBox] = useState();
14
+
15
+ const onPlacesChanged = useCallback(() => {
16
+ if (searchBox) {
17
+ const place = _.first(searchBox.getPlaces());
18
+ const { location } = place.geometry;
19
+
20
+ const name = place.formatted_address;
21
+ const lat = location.lat();
22
+ const lng = location.lng();
23
+
24
+ props.onLocationSelection({
25
+ name,
26
+ lat,
27
+ lng,
28
+ result: place
29
+ });
30
+ }
31
+ }, [searchBox, props.onLocationSelection]);
32
+
33
+ return (
34
+ <StandaloneSearchBox
35
+ onLoad={(s) => setSearchBox(s)}
36
+ onPlacesChanged={onPlacesChanged}
37
+ >
38
+ { props.children }
39
+ </StandaloneSearchBox>
40
+ );
41
+ };
42
+
43
+ export default GooglePlacesSearch;