@performant-software/shared-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 (58) 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 +11 -0
  6. package/index.js +1 -0
  7. package/package.json +36 -0
  8. package/src/api/Attachments.js +28 -0
  9. package/src/api/BaseService.js +127 -0
  10. package/src/api/BaseTransform.js +55 -0
  11. package/src/api/FormDataTransform.js +30 -0
  12. package/src/api/NestedAttributesTransform.js +63 -0
  13. package/src/components/EditContainer.css +0 -0
  14. package/src/components/EditContainer.js +448 -0
  15. package/src/components/GoogleAnalytics.css +0 -0
  16. package/src/components/GoogleAnalytics.js +118 -0
  17. package/src/components/GoogleScript.js +5 -0
  18. package/src/components/InfiniteScroll.css +1 -0
  19. package/src/components/InfiniteScroll.js +120 -0
  20. package/src/components/Keyboard.css +11 -0
  21. package/src/components/Keyboard.js +55 -0
  22. package/src/i18n/en.json +204 -0
  23. package/src/i18n/i18n.js +24 -0
  24. package/src/index.js +34 -0
  25. package/src/utils/Browser.js +8 -0
  26. package/src/utils/Calendar.js +232 -0
  27. package/src/utils/Date.js +10 -0
  28. package/src/utils/DragDrop.js +17 -0
  29. package/src/utils/Element.js +36 -0
  30. package/src/utils/Map.js +27 -0
  31. package/src/utils/Object.js +114 -0
  32. package/src/utils/String.js +20 -0
  33. package/src/utils/Timer.js +32 -0
  34. package/src/utils/Utility.js +14 -0
  35. package/test/api/Attachments.spec.js +32 -0
  36. package/types/api/Attachments.js.flow +28 -0
  37. package/types/api/BaseService.js.flow +127 -0
  38. package/types/api/BaseTransform.js.flow +55 -0
  39. package/types/api/FormDataTransform.js.flow +30 -0
  40. package/types/api/NestedAttributesTransform.js.flow +63 -0
  41. package/types/components/EditContainer.js.flow +448 -0
  42. package/types/components/GoogleAnalytics.js.flow +118 -0
  43. package/types/components/GoogleScript.js.flow +5 -0
  44. package/types/components/InfiniteScroll.js.flow +120 -0
  45. package/types/components/Keyboard.js.flow +55 -0
  46. package/types/i18n/i18n.js.flow +24 -0
  47. package/types/index.js.flow +34 -0
  48. package/types/utils/Browser.js.flow +8 -0
  49. package/types/utils/Calendar.js.flow +232 -0
  50. package/types/utils/Date.js.flow +10 -0
  51. package/types/utils/DragDrop.js.flow +17 -0
  52. package/types/utils/Element.js.flow +36 -0
  53. package/types/utils/Map.js.flow +27 -0
  54. package/types/utils/Object.js.flow +114 -0
  55. package/types/utils/String.js.flow +20 -0
  56. package/types/utils/Timer.js.flow +32 -0
  57. package/types/utils/Utility.js.flow +14 -0
  58. package/webpack.config.js +3 -0
@@ -0,0 +1,55 @@
1
+ // @flow
2
+
3
+ import React, { useEffect, useRef } from 'react';
4
+ import Keyboard from 'simple-keyboard';
5
+ import './Keyboard.css';
6
+
7
+ type Props = {
8
+ layout: any,
9
+ onChange: (value: string) => void,
10
+ value: string,
11
+ keyboardClass: string,
12
+ };
13
+
14
+ const KEY_CAPS_LOCK = '{lock}';
15
+ const KEY_SHIFT = '{shift}';
16
+
17
+ const LAYOUT_DEFAULT = 'default';
18
+ const LAYOUT_SHIFT = 'shift';
19
+
20
+ const KeyboardSimple = (props: Props) => {
21
+ const keyboardRef = useRef();
22
+
23
+ // Toggles the layout name for Shift and CapsLock keys.
24
+ const onKeyPress = (value) => {
25
+ const isCaps = value === KEY_CAPS_LOCK || value === KEY_SHIFT;
26
+
27
+ if (isCaps && keyboardRef.current && keyboardRef.current.options.layoutName === LAYOUT_SHIFT) {
28
+ keyboardRef.current.setOptions({ layoutName: LAYOUT_DEFAULT });
29
+ } else if (isCaps && keyboardRef.current && keyboardRef.current.options.layoutName !== LAYOUT_SHIFT) {
30
+ keyboardRef.current.setOptions({ layoutName: LAYOUT_SHIFT });
31
+ }
32
+ };
33
+
34
+ // Sets up the keyboard reference and the initial value when the component is first rendered.
35
+ useEffect(() => {
36
+ const { layout, onChange } = props;
37
+ keyboardRef.current = new Keyboard(`.${props.keyboardClass}`, { ...layout, onChange, onKeyPress });
38
+ keyboardRef.current.setInput(props.value);
39
+ }, []);
40
+
41
+ // Sets the keyboard value when the value property changes.
42
+ useEffect(() => {
43
+ if (keyboardRef.current) {
44
+ keyboardRef.current.setInput(props.value);
45
+ }
46
+ }, [props.value]);
47
+
48
+ return <div className={props.keyboardClass} />;
49
+ };
50
+
51
+ KeyboardSimple.defaultProps = {
52
+ keyboardClass: 'simple-keyboard'
53
+ };
54
+
55
+ export default KeyboardSimple;
@@ -0,0 +1,204 @@
1
+ {
2
+ "AccordionList": {
3
+ "deleteContent": "Are you sure you want to remove this record?",
4
+ "deleteHeader": "Confirm Remove",
5
+ "record": "Record",
6
+ "record_plural": "Records"
7
+ },
8
+ "AccordionSelector": {
9
+ "title": "Select Items"
10
+ },
11
+ "Common": {
12
+ "buttons": {
13
+ "add": "Add",
14
+ "cancel": "Cancel",
15
+ "clear": "Clear",
16
+ "edit": "Edit",
17
+ "ok": "OK",
18
+ "save": "Save"
19
+ },
20
+ "errors": {
21
+ "title": "Oops!"
22
+ },
23
+ "messages": {
24
+ "error": {
25
+ "header": "Oops!"
26
+ },
27
+ "loading": "Loading",
28
+ "save": {
29
+ "content": "Your changes have been saved.",
30
+ "header": "Success!"
31
+ }
32
+ }
33
+ },
34
+ "EditContainer": {
35
+ "errors": {
36
+ "general": "Something went wrong while saving the record. Please make sure all required fields are filled out.",
37
+ "required": "The {{key}} field is required.",
38
+ "system": "A system error occurred while saving the record. Please contact your system administrator.",
39
+ "unique": "A record with the {{key}} \"{{value}}\" already exists."
40
+ }
41
+ },
42
+ "DataTable": {
43
+ "actions": {
44
+ "copy": {
45
+ "content": "Creates an editable copy to be saved as a new record",
46
+ "title": "Copy"
47
+ },
48
+ "delete": {
49
+ "content": "Removes the record, permanently",
50
+ "title": "Remove"
51
+ },
52
+ "edit": {
53
+ "content": "Opens the modal to edit the record",
54
+ "title": "Edit"
55
+ }
56
+ },
57
+ "columns": {
58
+ "actions": "Actions",
59
+ "select": "Select"
60
+ },
61
+ "loading": "Loading"
62
+ },
63
+ "EditProvider": {
64
+ "errors": {
65
+ "general": "Something went wrong while saving the record. Please make sure all required fields are filled out.",
66
+ "required": "The {{key}} field is required.",
67
+ "unique": "A record with the {{key}} \"{{value}}\" already exists."
68
+ }
69
+ },
70
+ "FileUpload": {
71
+ "add": "<0>Add files</0> or drop files here",
72
+ "errors": {
73
+ "fileType": "Unable to upload {{name}} because {{type}} files are not allowed.",
74
+ "maxSize": "Unable to upload {{name}} because it exceeds 10 MB."
75
+ }
76
+ },
77
+ "FileUploadModal": {
78
+ "errors": {
79
+ "required": "{{filename}} - The following fields are required: {{fields}}"
80
+ },
81
+ "loader": "Uploading files...",
82
+ "title": "Upload Files"
83
+ },
84
+ "FuzzyDate": {
85
+ "accuracy": {
86
+ "date": "Date",
87
+ "month": "Month",
88
+ "year": "Year"
89
+ },
90
+ "buttons": {
91
+ "addRange": "Add Range",
92
+ "removeRange": "Remove Range"
93
+ },
94
+ "labels": {
95
+ "accuracy": "Accuracy",
96
+ "date": "Date",
97
+ "description": "Description",
98
+ "month": "Month",
99
+ "year": "Year"
100
+ },
101
+ "title": "Fuzzy Date"
102
+ },
103
+ "ItemList": {
104
+ "actions": {
105
+ "copy": "Copy",
106
+ "delete": "Remove",
107
+ "edit": "Edit"
108
+ },
109
+ "buttons": {
110
+ "clear": "Clear selected",
111
+ "deselectAll": "Deselect all",
112
+ "selectAll": "Select all"
113
+ }
114
+ },
115
+ "KeyboardField": {
116
+ "labels": {
117
+ "hideKeyboard": "Hide Keyboard",
118
+ "showKeyboard": "Show Keyboard"
119
+ }
120
+ },
121
+ "LazyDocument": {
122
+ "buttons": {
123
+ "download": "Download"
124
+ }
125
+ },
126
+ "LazyImage": {
127
+ "buttons": {
128
+ "view": "View image"
129
+ }
130
+ },
131
+ "LazyVideo": {
132
+ "buttons": {
133
+ "play": "Play video"
134
+ }
135
+ },
136
+ "List": {
137
+ "buttons": {
138
+ "add": "Add",
139
+ "deleteAll": "Delete all"
140
+ },
141
+ "deleteAllContent": "Are you sure you want to remove all records? This action cannot be undone.",
142
+ "deleteAllHeader": "Confirm Remove All",
143
+ "deleteContent": "Are you sure you want to remove this record?",
144
+ "deleteHeader": "Confirm Remove",
145
+ "emptyList": "No matching records found.",
146
+ "emptyListAdd": "You haven't added any yet. Click <1><0><0></0><1></1></0></1> to get started.",
147
+ "labels": {
148
+ "perPage": "Show {{perPage}}"
149
+ },
150
+ "record": "Record",
151
+ "record_plural": "Records"
152
+ },
153
+ "ListFilters": {
154
+ "buttons": {
155
+ "add": "Add",
156
+ "reset": "Reset filters"
157
+ },
158
+ "operators": {
159
+ "contain": "Contains",
160
+ "empty": "Is empty",
161
+ "equal": "Equals",
162
+ "greaterThan": "Is greater than",
163
+ "lessThan": "Is less than",
164
+ "notContain": "Does not contain",
165
+ "notEmpty": "Is not empty",
166
+ "notEqual": "Does not equal"
167
+ },
168
+ "title": "Filters"
169
+ },
170
+ "LoginModal": {
171
+ "buttonCancel": "Cancel",
172
+ "buttonLogin": "Login",
173
+ "email": "Email",
174
+ "header": "Login",
175
+ "loginErrorContent": "The username and/or password you entered is invalid. Please double check and try again.",
176
+ "loginErrorHeader": "Invalid Credentials",
177
+ "password": "Password"
178
+ },
179
+ "RemoteDropdown": {
180
+ "labels": {
181
+ "add": "Add",
182
+ "loadMore": "Load more"
183
+ }
184
+ },
185
+ "Selectize": {
186
+ "noRecords": "No matching records."
187
+ },
188
+ "VideoFrameSelector": {
189
+ "buttons": {
190
+ "select": "Select frame"
191
+ },
192
+ "labels": {
193
+ "interval": "Interval: {{count}} second",
194
+ "interval_plural": "Interval: {{count}} seconds"
195
+ },
196
+ "title": "Select Frame"
197
+ },
198
+ "ViewXML": {
199
+ "buttons": {
200
+ "view": "View XML"
201
+ },
202
+ "title": "XML"
203
+ }
204
+ }
@@ -0,0 +1,24 @@
1
+ import i18next from 'i18next';
2
+
3
+ import en from './en.json';
4
+
5
+ const resources = {
6
+ en: {
7
+ translation: en
8
+ }
9
+ };
10
+
11
+ const i18n = i18next.createInstance();
12
+
13
+ i18n
14
+ .init({
15
+ debug: true,
16
+ fallbackLng: 'en',
17
+ lng: 'en',
18
+ interpolation: {
19
+ escapeValue: false,
20
+ },
21
+ resources
22
+ });
23
+
24
+ export default i18n;
package/src/index.js ADDED
@@ -0,0 +1,34 @@
1
+ // @flow
2
+
3
+ // API
4
+ export { default as Attachments } from './api/Attachments';
5
+ export { default as BaseService } from './api/BaseService';
6
+ export { default as BaseTransform } from './api/BaseTransform';
7
+ export { default as FormDataTransform } from './api/FormDataTransform';
8
+ export { default as NestedAttributesTransform } from './api/NestedAttributesTransform';
9
+
10
+ // Components
11
+ export { default as useEditContainer } from './components/EditContainer';
12
+ export { default as withGoogleAnalytics } from './components/GoogleAnalytics';
13
+ export { default as GoogleScript } from './components/GoogleScript';
14
+ export { default as InfiniteScroll } from './components/InfiniteScroll';
15
+ export { default as Keyboard } from './components/Keyboard';
16
+
17
+ // I18n
18
+ export { default as i18n } from './i18n/i18n';
19
+
20
+ // Utils
21
+ export * as Browser from './utils/Browser';
22
+ export { default as Calendar } from './utils/Calendar';
23
+ export { default as Date } from './utils/Date';
24
+ export { default as useDragDrop } from './utils/DragDrop';
25
+ export { default as Element } from './utils/Element';
26
+ export { default as Map } from './utils/Map';
27
+ export { default as Object } from './utils/Object';
28
+ export { default as String } from './utils/String';
29
+ export { default as Timer } from './utils/Timer';
30
+ export { default as Utility } from './utils/Utility'; // TODO: Rename me
31
+
32
+ // Types
33
+ export type { EditContainerProps as EditModalProps } from './components/EditContainer'; // Backwards compatability
34
+ export type { EditContainerProps } from './components/EditContainer';
@@ -0,0 +1,8 @@
1
+ // @flow
2
+
3
+ /**
4
+ * Returns true if the window object is present.
5
+ *
6
+ * @returns {boolean}
7
+ */
8
+ export const isBrowser = () => (typeof window !== 'undefined');
@@ -0,0 +1,232 @@
1
+ // @flow
2
+
3
+ import moment from 'moment-islamic-civil';
4
+ import 'moment/min/locales';
5
+
6
+ const DEFAULT_LOCALE = 'en';
7
+
8
+ const MAX_DAYS_IN_MONTH = 31;
9
+
10
+ /**
11
+ * Wrapper class to handle momentJS dates to multiple calendars.
12
+ */
13
+ class Calendar {
14
+ static Calendars: any;
15
+ static DateFormats: any;
16
+ static Defaults: any;
17
+
18
+ locale: string;
19
+ name: string;
20
+
21
+ /**
22
+ * Constructs a new calendar instance for the passed locale and calendar.
23
+ *
24
+ * @param locale
25
+ * @param name
26
+ */
27
+ constructor(locale: string = DEFAULT_LOCALE, name: string = Calendar.Calendars.gregorian) {
28
+ this.locale = locale;
29
+ this.name = name;
30
+ }
31
+
32
+ /**
33
+ * Increments the date by the passed value.
34
+ *
35
+ * @param date
36
+ * @param value
37
+ *
38
+ * @returns {*}
39
+ */
40
+ addDate(date: Date, value: number) {
41
+ const m = this.moment(date);
42
+ return this.isHijri() ? m.add(value, 'iDate') : m.add(value, 'date');
43
+ }
44
+
45
+ /**
46
+ * Increments the month by the passed value.
47
+ *
48
+ * @param date
49
+ * @param value
50
+ *
51
+ * @returns {*}
52
+ */
53
+ addMonth(date: Date, value: number) {
54
+ const m = this.moment(date);
55
+ return this.isHijri() ? m.add(value, 'iMonth') : m.add(value, 'month');
56
+ }
57
+
58
+ /**
59
+ * Increments the year by the passed value.
60
+ *
61
+ * @param date
62
+ * @param value
63
+ *
64
+ * @returns {*}
65
+ */
66
+ addYear(date: Date, value: number) {
67
+ const m = this.moment(date);
68
+ return this.isHijri() ? m.add(value, 'iYear') : m.add(value, 'year');
69
+ }
70
+
71
+ /**
72
+ * Converts the passed year, month, and date to a moment Date object.
73
+ *
74
+ * @param year
75
+ * @param month
76
+ * @param date
77
+ *
78
+ * @returns {moment.Moment}
79
+ */
80
+ convertToDate({ year, month, date }: { year: number, month: number, date: number }) {
81
+ let m = this.moment().hours(0).minutes(0).seconds(0);
82
+
83
+ if (this.isHijri()) {
84
+ m = m
85
+ .iYear(year || this.getDefaultYear())
86
+ .iMonth(month || this.getDefaultMonth())
87
+ .iDate(date || this.getDefaultDate());
88
+ } else {
89
+ m = m
90
+ .year(year || this.getDefaultYear())
91
+ .month(month || this.getDefaultMonth())
92
+ .date(date || this.getDefaultDate());
93
+ }
94
+
95
+ return m;
96
+ }
97
+
98
+ /**
99
+ * Returns the days in the month for the passed year/month.
100
+ *
101
+ * @param year
102
+ * @param month
103
+ *
104
+ * @returns {number}
105
+ */
106
+ daysInMonth(year: number, month: number) {
107
+ const y = year || this.getDefaultYear();
108
+ const m = month || this.getDefaultMonth();
109
+
110
+ const date = this.convertToDate({ year: y, month: m, date: this.getDefaultDate() });
111
+ return (this.isHijri() ? date.iDaysInMonth() : date.daysInMonth()) || MAX_DAYS_IN_MONTH;
112
+ }
113
+
114
+ /**
115
+ * Formats the passed date.
116
+ *
117
+ * @param date
118
+ * @param format
119
+ *
120
+ * @returns {*}
121
+ */
122
+ format(date: moment, format: number = Calendar.DateFormats.gregorian) {
123
+ return this.moment(date).format(Calendar.DateFormats[this.name][format]);
124
+ }
125
+
126
+ /**
127
+ * Returns the default date for the current calendar.
128
+ *
129
+ * @returns {*}
130
+ */
131
+ getDefaultDate() {
132
+ return Calendar.Defaults[this.name].date;
133
+ }
134
+
135
+ /**
136
+ * Returns the default month for the current calendar.
137
+ *
138
+ * @returns {*}
139
+ */
140
+ getDefaultMonth() {
141
+ return Calendar.Defaults[this.name].month;
142
+ }
143
+
144
+ /**
145
+ * Returns the default year for the current calendar.
146
+ *
147
+ * @returns {number|*|"numeric"|"2-digit"|string|((y: number) => moment.Moment)|(() => number)|moment.numberlike}
148
+ */
149
+ getDefaultYear() {
150
+ return Calendar.Defaults[this.name].year;
151
+ }
152
+
153
+ /**
154
+ * Returns true if the current calendar is Hijri.
155
+ *
156
+ * @returns {boolean}
157
+ */
158
+ isHijri() {
159
+ return this.name === Calendar.Calendars.hijri;
160
+ }
161
+
162
+ /**
163
+ * Returns the list of months for the current calendar.
164
+ *
165
+ * @returns {*}
166
+ */
167
+ listMonths() {
168
+ const localeData = this.moment().localeData();
169
+ return this.isHijri() ? localeData._iMonths : localeData.months();
170
+ }
171
+
172
+ /**
173
+ * Wraps the passed date/string in a moment object.
174
+ *
175
+ * @param date
176
+ */
177
+ moment(date: ?any = null) {
178
+ const m = date ? moment(date) : moment();
179
+ m.locale(this.locale);
180
+ return m;
181
+ }
182
+
183
+ /**
184
+ * Parses the passed date into year, month, and date components.
185
+ *
186
+ * @param date
187
+ *
188
+ * @returns {{date: (*), month: (*), year: (*)}}
189
+ */
190
+ parseDate(date: Date | string) {
191
+ const m = this.moment(date);
192
+
193
+ return {
194
+ year: this.isHijri() ? m.iYear() : m.year(),
195
+ month: this.isHijri() ? m.iMonth() : m.month(),
196
+ date: this.isHijri() ? m.iDate() : m.date()
197
+ };
198
+ }
199
+ }
200
+
201
+ Calendar.Calendars = {
202
+ gregorian: 'gregorian',
203
+ hijri: 'hijri'
204
+ };
205
+
206
+ Calendar.DateFormats = {
207
+ [Calendar.Calendars.gregorian]: {
208
+ '0': 'YYYY',
209
+ '1': 'MMMM YYYY',
210
+ '2': 'L'
211
+ },
212
+ [Calendar.Calendars.hijri]: {
213
+ '0': 'iYYYY',
214
+ '1': 'iMMMM iYYYY',
215
+ '2': 'iM/iD/iYYYY'
216
+ }
217
+ };
218
+
219
+ Calendar.Defaults = {
220
+ [Calendar.Calendars.gregorian]: {
221
+ year: 0,
222
+ month: 0,
223
+ date: 1
224
+ },
225
+ [Calendar.Calendars.hijri]: {
226
+ year: 1410,
227
+ month: 0,
228
+ date: 1
229
+ }
230
+ };
231
+
232
+ export default Calendar;
@@ -0,0 +1,10 @@
1
+ // @flow
2
+
3
+ const formatDate = (value: any, locale?: string, options?: any) => {
4
+ const date = new Date(value);
5
+ return date.toLocaleDateString(locale, options);
6
+ };
7
+
8
+ export default {
9
+ formatDate
10
+ };
@@ -0,0 +1,17 @@
1
+ // @flow
2
+
3
+ import React, { type ComponentType } from 'react';
4
+ import { DndProvider } from 'react-dnd';
5
+ import { HTML5Backend, type BackendFactory } from 'react-dnd-html5-backend';
6
+
7
+ const useDragDrop = (WrappedComponent: ComponentType<any>, backend: BackendFactory = HTML5Backend) => (
8
+ () => (
9
+ <DndProvider
10
+ backend={backend}
11
+ >
12
+ <WrappedComponent />
13
+ </DndProvider>
14
+ )
15
+ );
16
+
17
+ export default useDragDrop;
@@ -0,0 +1,36 @@
1
+ // @flow
2
+
3
+ import React from 'react';
4
+
5
+ /**
6
+ * Returns the subset of children matching the passed component type. This function is useful for designing components
7
+ * using a sub-component type syntax:
8
+ *
9
+ * <MyComponent ...>
10
+ * <MyComponent.Subcomponent ... />
11
+ * <MyComponent.AnotherSubcomponent ... />
12
+ * </MyComponent>
13
+ *
14
+ * @param children
15
+ * @param component
16
+ *
17
+ * @returns {[]}
18
+ */
19
+ const findByType = (children: any, component: any) => {
20
+ const components = [];
21
+
22
+ const type = [component.displayName || component.name];
23
+
24
+ React.Children.forEach(children, (child) => {
25
+ const childType = child && child.type && (child.type.displayName || child.type.name);
26
+ if (type.includes(childType)) {
27
+ components.push(child);
28
+ }
29
+ });
30
+
31
+ return components;
32
+ };
33
+
34
+ export default {
35
+ findByType
36
+ };
@@ -0,0 +1,27 @@
1
+ // @flow
2
+
3
+ /**
4
+ * Returns the numeric latitude and longitude for the passed coordinates.
5
+ *
6
+ * @param coords
7
+ *
8
+ * @returns {*}
9
+ */
10
+ const getPosition = (coords: ?{ lat: any, lng: any }) => {
11
+ let position;
12
+
13
+ if (coords) {
14
+ const lat = Number.parseFloat(coords.lat);
15
+ const lng = Number.parseFloat(coords.lng);
16
+
17
+ if (!Number.isNaN(lat) && !Number.isNaN(lng)) {
18
+ position = { lat, lng };
19
+ }
20
+ }
21
+
22
+ return position;
23
+ };
24
+
25
+ export default {
26
+ getPosition
27
+ };