@dvrd/dvr-controls 1.0.14 → 1.0.16

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 (180) hide show
  1. package/index.ts +8 -1
  2. package/package.json +29 -23
  3. package/src/js/button/button.tsx +102 -0
  4. package/src/js/button/buttonController.tsx +179 -0
  5. package/src/js/button/closeButton.tsx +29 -0
  6. package/src/js/button/dvrdButton.tsx +128 -0
  7. package/src/js/button/outlinedButton.tsx +105 -0
  8. package/src/js/button/simpleButton.tsx +163 -0
  9. package/src/js/button/style/button.scss +95 -0
  10. package/src/js/button/style/closeButton.scss +15 -0
  11. package/src/js/button/style/dvrdButton.scss +30 -0
  12. package/src/js/button/style/outlinedButton.scss +84 -0
  13. package/src/js/button/style/simpleButton.scss +80 -0
  14. package/src/js/carousel/DVRCarousel.tsx +163 -0
  15. package/src/js/carousel/DVRCarouselController.tsx +95 -0
  16. package/src/js/carousel/style/DVRCarousel.scss +38 -0
  17. package/src/js/checkbox/checkbox.tsx +148 -0
  18. package/src/js/checkbox/checkboxController.tsx +131 -0
  19. package/src/js/checkbox/style/checkbox.scss +109 -0
  20. package/src/js/colorPicker/colorPicker.tsx +118 -0
  21. package/src/js/colorPicker/style/colorPicker.scss +20 -0
  22. package/src/js/date/dvrdDatePicker.tsx +357 -0
  23. package/src/js/date/style/dvrdDatePicker.scss +307 -0
  24. package/src/js/dialog/dialog.tsx +207 -0
  25. package/src/js/dialog/dialogController.tsx +70 -0
  26. package/src/js/dialog/inlineDialog.tsx +127 -0
  27. package/src/js/dialog/style/dialog.scss +61 -0
  28. package/src/js/events/withEvents.tsx +40 -0
  29. package/src/js/head/DVRHead.tsx +49 -0
  30. package/src/js/header/DVRHeader.tsx +417 -0
  31. package/src/js/header/style/header.scss +206 -0
  32. package/src/js/icon/awesomeIcon.tsx +20 -0
  33. package/src/js/image/imageUpload.tsx +69 -0
  34. package/src/js/image/style/imageUpload.scss +11 -0
  35. package/src/js/info/info.tsx +136 -0
  36. package/src/js/info/style/info.scss +39 -0
  37. package/src/js/input/animated/animatedTextField.tsx +159 -0
  38. package/src/js/input/date/dateField.tsx +360 -0
  39. package/src/js/input/date/dateFieldController.tsx +245 -0
  40. package/src/js/input/date/datePicker/datePicker.tsx +186 -0
  41. package/src/js/input/date/datePicker/style/datePicker.scss +102 -0
  42. package/src/js/input/date/input/dateInput.tsx +214 -0
  43. package/src/js/input/date/style/dateField.scss +40 -0
  44. package/src/js/input/date/timePicker/style/timePicker.scss +95 -0
  45. package/src/js/input/date/timePicker/timePicker.tsx +143 -0
  46. package/src/js/input/editor/DVREditor.tsx +21 -0
  47. package/src/js/input/number/numberInput.tsx +157 -0
  48. package/src/js/input/password/passwordInput.tsx +140 -0
  49. package/src/js/input/password/passwordRules.tsx +48 -0
  50. package/src/js/input/password/style/passwordInput.scss +39 -0
  51. package/src/js/input/password/style/passwordRules.scss +41 -0
  52. package/src/js/input/simple/style/simpleInput.scss +98 -0
  53. package/src/js/input/simple/v2/simpleInputV2.tsx +178 -0
  54. package/src/js/input/style/input.scss +138 -0
  55. package/src/js/input/v2/inputController_v2.tsx +250 -0
  56. package/src/js/input/v2/input_v2.tsx +7 -0
  57. package/src/js/label/label.tsx +196 -0
  58. package/src/js/label/style/label.scss +4 -0
  59. package/src/js/link/link.tsx +38 -0
  60. package/src/js/link/style/link.scss +30 -0
  61. package/src/js/loader/loader.tsx +79 -0
  62. package/src/js/loader/loaderController.tsx +61 -0
  63. package/src/js/loader/style/loader.scss +53 -0
  64. package/src/js/media/media.tsx +72 -0
  65. package/src/js/navigator/navigator.tsx +51 -0
  66. package/src/js/optionsList/dvrdOptionsList.tsx +112 -0
  67. package/src/js/optionsList/style/dvrdOptionsList.scss +84 -0
  68. package/src/js/optionsMenu/optionsMenu.tsx +187 -0
  69. package/src/js/optionsMenu/style/optionsMenu.scss +70 -0
  70. package/src/js/pdf/element/pdfElement.tsx +315 -0
  71. package/src/js/pdf/element/style/pdfElement.scss +111 -0
  72. package/src/js/pdf/history/pdfHistory.ts +57 -0
  73. package/src/js/pdf/image/pdfImage.tsx +175 -0
  74. package/src/js/pdf/image/style/pdfImage.scss +34 -0
  75. package/src/js/pdf/invoiceTable/pdfInvoiceTable.tsx +176 -0
  76. package/src/js/pdf/invoiceTable/style/pdfInvoiceTable.scss +32 -0
  77. package/src/js/pdf/pdfTemplateCreator.tsx +279 -0
  78. package/src/js/pdf/settings/buttons/iconButton.tsx +49 -0
  79. package/src/js/pdf/settings/buttons/style/iconButton.scss +50 -0
  80. package/src/js/pdf/settings/image/pdfImageSettings.tsx +82 -0
  81. package/src/js/pdf/settings/image/style/pdfImageSettings.scss +9 -0
  82. package/src/js/pdf/settings/invoiceTable/pdfInvoiceTableSettings.tsx +141 -0
  83. package/src/js/pdf/settings/invoiceTable/style/pdfInvoiceTableSettings.scss +38 -0
  84. package/src/js/pdf/settings/pdfElementSettings.tsx +86 -0
  85. package/src/js/pdf/settings/style/pdfElementSettings.scss +56 -0
  86. package/src/js/pdf/settings/text/pdfTextSettings.tsx +202 -0
  87. package/src/js/pdf/settings/text/style/pdfTextSettings.scss +94 -0
  88. package/src/js/pdf/style/pdfTemplateCreator.scss +118 -0
  89. package/src/js/pdf/text/pdfText.tsx +267 -0
  90. package/src/js/pdf/text/style/pdfText.scss +22 -0
  91. package/src/js/pdf/v2/pdfElement/pdfDraggableElement.tsx +193 -0
  92. package/src/js/pdf/v2/types/pdfTemplateTypes.ts +27 -0
  93. package/src/js/popup/style/withBackground.scss +29 -0
  94. package/src/js/popup/withBackground.tsx +92 -0
  95. package/src/js/select/async/asyncSelect.tsx +46 -0
  96. package/src/js/select/async/style/asyncSelect.scss +23 -0
  97. package/src/js/select/dvrdSelect.tsx +214 -0
  98. package/src/js/select/dvrdSelectController.tsx +81 -0
  99. package/src/js/select/select.tsx +310 -0
  100. package/src/js/select/selectController.tsx +341 -0
  101. package/src/js/select/style/dvrdSelect.scss +140 -0
  102. package/src/js/select/style/select.scss +199 -0
  103. package/src/js/sidebarMenu/sidebarMenu.tsx +167 -0
  104. package/src/js/sidebarMenu/style/sidebarMenu.scss +167 -0
  105. package/src/js/slider/DVRSlider.tsx +107 -0
  106. package/src/js/slider/style/DVRSlider.scss +88 -0
  107. package/src/js/snackbar/snackbar.tsx +72 -0
  108. package/src/js/snackbar/snackbarController.tsx +104 -0
  109. package/src/js/snackbar/style/snackbar.scss +46 -0
  110. package/src/js/switch/dvrdSwitch.tsx +53 -0
  111. package/src/js/switch/style/dvrdSwitch.scss +47 -0
  112. package/src/js/switch/style/switch.scss +84 -0
  113. package/src/js/switch/switch.tsx +115 -0
  114. package/src/js/switch/switchController.tsx +97 -0
  115. package/src/js/textField/dvrdInput.tsx +219 -0
  116. package/src/js/textField/dvrdInputController.tsx +97 -0
  117. package/src/js/textField/dvrdNumberInput.tsx +141 -0
  118. package/src/js/textField/dvrdPasswordInput.tsx +40 -0
  119. package/src/js/textField/style/dvrdInput.scss +114 -0
  120. package/src/js/textField/style/dvrdPassword.scss +15 -0
  121. package/src/js/topButton/style/topButton.scss +54 -0
  122. package/src/js/topButton/topButton.tsx +136 -0
  123. package/src/js/util/analyticsUtil.ts +41 -0
  124. package/src/js/util/colorUtil.ts +230 -0
  125. package/src/js/util/componentUtil.tsx +59 -0
  126. package/src/js/util/constants.ts +12 -0
  127. package/src/js/util/controlContext.tsx +46 -0
  128. package/src/js/util/controlUtil.ts +107 -0
  129. package/src/js/util/cookieUtil.ts +17 -0
  130. package/src/js/util/eventUtil.ts +65 -0
  131. package/src/js/util/googleUtil.ts +88 -0
  132. package/src/js/util/interfaces.ts +180 -0
  133. package/src/js/util/jwtUtil.ts +72 -0
  134. package/src/js/util/miscUtil.ts +170 -0
  135. package/src/js/util/momentUtil.ts +45 -0
  136. package/src/js/util/pdfUtil.ts +124 -0
  137. package/src/js/util/requestUtil.ts +145 -0
  138. package/src/js/util/responsiveUtil.ts +37 -0
  139. package/src/js/util/validationUtil.ts +13 -0
  140. package/src/res/img/lock-handle.png +0 -0
  141. package/src/res/img/lock-handle.webp +0 -0
  142. package/src/res/img/lock.png +0 -0
  143. package/src/res/img/lock.webp +0 -0
  144. package/src/style/common-icons-variables.scss +140 -0
  145. package/src/style/common-icons.scss +714 -0
  146. package/src/style/common-variables.scss +243 -0
  147. package/src/style/display-breakpoints.scss +141 -0
  148. package/src/style/fonts/common-icons.eot +0 -0
  149. package/src/style/fonts/common-icons.svg +150 -0
  150. package/src/style/fonts/common-icons.ttf +0 -0
  151. package/src/style/fonts/common-icons.woff +0 -0
  152. package/src/style/fonts/common-icons.woff2 +0 -0
  153. package/src/style/fonts/fontAwesome/css/all.css +7003 -0
  154. package/src/style/fonts/fontAwesome/css/all.min.css +6 -0
  155. package/src/style/fonts/fontAwesome/css/brands.css +1423 -0
  156. package/src/style/fonts/fontAwesome/css/brands.min.css +6 -0
  157. package/src/style/fonts/fontAwesome/css/fontawesome.css +5519 -0
  158. package/src/style/fonts/fontAwesome/css/fontawesome.min.css +6 -0
  159. package/src/style/fonts/fontAwesome/css/regular.css +19 -0
  160. package/src/style/fonts/fontAwesome/css/regular.min.css +6 -0
  161. package/src/style/fonts/fontAwesome/css/solid.css +19 -0
  162. package/src/style/fonts/fontAwesome/css/solid.min.css +6 -0
  163. package/src/style/fonts/fontAwesome/css/svg-with-js.css +634 -0
  164. package/src/style/fonts/fontAwesome/css/svg-with-js.min.css +6 -0
  165. package/src/style/fonts/fontAwesome/css/v4-font-face.css +26 -0
  166. package/src/style/fonts/fontAwesome/css/v4-font-face.min.css +6 -0
  167. package/src/style/fonts/fontAwesome/css/v4-shims.css +2146 -0
  168. package/src/style/fonts/fontAwesome/css/v4-shims.min.css +6 -0
  169. package/src/style/fonts/fontAwesome/css/v5-font-face.css +22 -0
  170. package/src/style/fonts/fontAwesome/css/v5-font-face.min.css +6 -0
  171. package/src/style/fonts/fontAwesome/webfonts/fa-brands-400.ttf +0 -0
  172. package/src/style/fonts/fontAwesome/webfonts/fa-brands-400.woff2 +0 -0
  173. package/src/style/fonts/fontAwesome/webfonts/fa-regular-400.ttf +0 -0
  174. package/src/style/fonts/fontAwesome/webfonts/fa-regular-400.woff2 +0 -0
  175. package/src/style/fonts/fontAwesome/webfonts/fa-solid-900.ttf +0 -0
  176. package/src/style/fonts/fontAwesome/webfonts/fa-solid-900.woff2 +0 -0
  177. package/src/style/fonts/fontAwesome/webfonts/fa-v4compatibility.ttf +0 -0
  178. package/src/style/fonts/fontAwesome/webfonts/fa-v4compatibility.woff2 +0 -0
  179. package/src/style/variables.scss +11 -0
  180. package/.gitignore +0 -31
@@ -0,0 +1,360 @@
1
+ /*
2
+ * Copyright (c) 2021. Dave van Rijn Development
3
+ */
4
+
5
+ import './style/dateField.scss';
6
+
7
+ import React, {ChangeEvent, CSSProperties, KeyboardEventHandler, PureComponent} from 'react';
8
+ import {ChangeFunction, ControlVariant} from "../../util/interfaces";
9
+ import {DateTimeParts, DateType} from "./dateFieldController";
10
+ import classNames from "classnames";
11
+ import {Moment} from 'moment';
12
+ import {ControlContext} from "../../util/controlContext";
13
+ import {BACKSPACE_CODE} from "../../util/constants";
14
+ import DatePicker from "./datePicker/datePicker";
15
+ import TimePicker from "./timePicker/timePicker";
16
+ import DateInput from "./input/dateInput";
17
+ import {DVRInputControllerProps} from "../v2/inputController_v2";
18
+ import mergeWith from 'lodash/mergeWith';
19
+ import {colorIsWhite, convertColor, editColor} from '../../util/colorUtil';
20
+ import {toMoment} from '../../util/momentUtil';
21
+ import {pad} from '../../util/controlUtil';
22
+ import AwesomeIcon from "../../icon/awesomeIcon";
23
+ import WithBackground from '../../popup/withBackground';
24
+ import ButtonController from "../../button/buttonController";
25
+
26
+ export interface DateNumParts {
27
+ day: number;
28
+ month: number;
29
+ year: number;
30
+ hour: number;
31
+ minute: number;
32
+ }
33
+
34
+ interface Props extends DVRInputControllerProps {
35
+ onChange: ChangeFunction;
36
+ onChangeText: (value: any, evt?: ChangeEvent, callback?: Function) => void;
37
+ onKeyDown: KeyboardEventHandler;
38
+ dateType: DateType;
39
+ dateParts: DateTimeParts;
40
+ pickerClass: string;
41
+ disableFuture: boolean;
42
+ disablePast: boolean;
43
+ disableAfter?: Date | Moment | string;
44
+ disableBefore?: Date | Moment | string;
45
+ value: string;
46
+ mask: string;
47
+ }
48
+
49
+ enum PickerState {DATE, TIME}
50
+
51
+ interface State {
52
+ pickerOpen: boolean;
53
+ pickerState: PickerState;
54
+ pickerValue: DateNumParts;
55
+ }
56
+
57
+ export enum PickerKey {DAY = 'day', MONTH = 'month', YEAR = 'year'}
58
+
59
+ export default class DateField extends PureComponent<Props, State> {
60
+ declare context: React.ContextType<typeof ControlContext>;
61
+ static contextType = ControlContext;
62
+
63
+ constructor(props: Props) {
64
+ super(props);
65
+ this.state = {
66
+ pickerOpen: false,
67
+ pickerState: this.getDefaultPickerState(),
68
+ pickerValue: this.getInitPickerValue(),
69
+ }
70
+ }
71
+
72
+ onMouseEnterSelector = (forState: PickerState) => (evt: MouseEvent) => {
73
+ const {pickerState} = this.state;
74
+ if (forState !== pickerState) {
75
+ const target = evt.target as HTMLElement;
76
+ target.style.color = editColor(.2, this.getBaseColor(false));
77
+ }
78
+ };
79
+
80
+ onMouseLeaveSelector = (forState: PickerState) => (evt: MouseEvent) => {
81
+ const {pickerState} = this.state;
82
+ if (forState !== pickerState) {
83
+ const target = evt.target as HTMLElement;
84
+ target.style.color = this.getSelectorStyle(forState).color as string;
85
+ }
86
+ };
87
+
88
+ onClosePicker = () => {
89
+ this.setState({pickerOpen: false, pickerState: this.getDefaultPickerState()});
90
+ };
91
+
92
+ onOpenPicker = () => {
93
+ this.setState({pickerOpen: true, pickerValue: this.getInitPickerValue()});
94
+ };
95
+
96
+ onChangeInternalValue = (key: PickerKey, value: number, add: boolean = true) => () => {
97
+ const {pickerValue} = this.state;
98
+ let nextValue: DateNumParts;
99
+ if (add) {
100
+ const currentMoment = this.getCurrentMoment(pickerValue).add(value, key);
101
+ nextValue = {
102
+ day: Math.min(pickerValue.day, currentMoment.daysInMonth()),
103
+ month: currentMoment.month(),
104
+ year: currentMoment.year(),
105
+ hour: currentMoment.hour(),
106
+ minute: currentMoment.minute(),
107
+ };
108
+ } else {
109
+ nextValue = Object.assign({}, this.state.pickerValue, {[key]: value});
110
+ const nextMoment = toMoment().month(nextValue.month).year(nextValue.year).date(1),
111
+ maxDays = nextMoment.daysInMonth();
112
+ if (nextValue.day > maxDays)
113
+ nextValue.day = maxDays;
114
+ }
115
+ this.setState({pickerValue: nextValue}, () => {
116
+ const {dateType} = this.props;
117
+ if (key === PickerKey.DAY) {
118
+ if (dateType === DateType.DATE)
119
+ this.onSubmit();
120
+ else if (dateType === DateType.DATETIME)
121
+ this.onChangePickerState(PickerState.TIME)();
122
+ }
123
+ });
124
+ };
125
+
126
+ onChangeTime = (hour: number, minute: number) => {
127
+ this.setState({pickerValue: Object.assign({}, this.state.pickerValue, {hour, minute})});
128
+ };
129
+
130
+ onSubmit = () => {
131
+ const {dateType, onChange} = this.props, {pickerValue} = this.state,
132
+ date = `${pad(pickerValue.day)}-${pad(pickerValue.month + 1)}-${pickerValue.year}`,
133
+ time = `${pad(pickerValue.hour)}:${pad(pickerValue.minute)}`;
134
+
135
+ let value: string;
136
+ switch (dateType) {
137
+ case DateType.DATE:
138
+ value = date;
139
+ break;
140
+ case DateType.DATETIME:
141
+ value = `${date} ${time}`;
142
+ break;
143
+ case DateType.TIME:
144
+ value = time;
145
+ break;
146
+ }
147
+ onChange(value);
148
+ this.onClosePicker();
149
+ };
150
+
151
+ onKeyDown = (evt: React.KeyboardEvent<HTMLInputElement>) => {
152
+ if (evt.key === BACKSPACE_CODE)
153
+ this.onBackSpace(evt);
154
+ else if (!/\d|ArrowRight|ArrowLeft|Escape|Esc|Enter|Tab/.test(evt.key)) {
155
+ // Prevent everything else then allowed keys
156
+ evt.preventDefault();
157
+ } else {
158
+ this.props.onKeyDown(evt);
159
+ }
160
+ };
161
+
162
+ onBackSpace = (evt: React.KeyboardEvent<HTMLInputElement>) => {
163
+ const target = evt.target as HTMLInputElement, caretPos = target.selectionStart, selEnd = target.selectionEnd,
164
+ currentValue = target.value;
165
+ if (caretPos !== null && selEnd !== null && selEnd > caretPos) this.onDeleteSelection(evt);
166
+ else if (caretPos && caretPos > 0) {
167
+ const charToDelete = currentValue.charAt(caretPos - 1);
168
+ evt.preventDefault();
169
+ if (/[-: ]/.test(charToDelete)) {
170
+ const nextValue = currentValue.substr(0, caretPos - 2) +
171
+ currentValue.substr(caretPos, currentValue.length);
172
+ this.props.onChangeText(nextValue, undefined, () => {
173
+ target.selectionStart = caretPos - 2;
174
+ target.selectionEnd = caretPos - 2;
175
+ });
176
+ } else {
177
+ const nextValue = currentValue.substr(0, caretPos - 1) +
178
+ currentValue.substr(caretPos, currentValue.length);
179
+ this.props.onChangeText(nextValue, undefined, () => {
180
+ target.selectionStart = caretPos - 1;
181
+ target.selectionEnd = caretPos - 1;
182
+ });
183
+ }
184
+ }
185
+ };
186
+
187
+ onDeleteSelection = (evt: React.KeyboardEvent<HTMLInputElement>) => {
188
+ evt.preventDefault();
189
+ const target: HTMLInputElement = evt.target as HTMLInputElement, start = target.selectionStart,
190
+ end = target.selectionEnd, currentValue: string = target.value;
191
+ if (start !== null && end !== null) {
192
+ let nextValue = '';
193
+ for (let i = 0; i < currentValue.length; i++) {
194
+ if (i >= start && i <= end) nextValue += ' ';
195
+ else nextValue += currentValue.charAt(i);
196
+ }
197
+ this.props.onChangeText(nextValue, undefined, () => {
198
+ target.selectionStart = start;
199
+ target.selectionEnd = start;
200
+ });
201
+ }
202
+ };
203
+
204
+ onClickToday = () => {
205
+ const today = toMoment();
206
+ this.setState({
207
+ pickerValue: {
208
+ month: today.month(),
209
+ day: today.date(),
210
+ minute: today.minute(),
211
+ hour: today.hour(),
212
+ year: today.year(),
213
+ }
214
+ }, () => {
215
+ if (this.props.dateType === DateType.DATE) {
216
+ this.onSubmit();
217
+ } else if (this.state.pickerState === PickerState.TIME) this.scrollToCurrent();
218
+ });
219
+ };
220
+
221
+ onChangePickerState = (nextState: PickerState) => () => {
222
+ if (nextState !== this.state.pickerState)
223
+ this.setState({pickerState: nextState});
224
+ };
225
+
226
+ onChangeInput = (value: Moment) => {
227
+ this.setState({
228
+ pickerValue: Object.assign({}, this.state.pickerValue, {
229
+ day: value.date(),
230
+ hour: value.hour(),
231
+ minute: value.minute(),
232
+ month: value.month(),
233
+ year: value.year()
234
+ })
235
+ }, () => {
236
+ this.onSubmit();
237
+ });
238
+ };
239
+
240
+ getDefaultPickerState = () => {
241
+ const {dateType} = this.props;
242
+ if (dateType === DateType.TIME) return PickerState.TIME;
243
+ return PickerState.DATE;
244
+ };
245
+
246
+ getInitPickerValue = () => {
247
+ const dateParts = Object.assign({}, this.props.dateParts), now = toMoment();
248
+ if (!isNaN(Number(dateParts.month))) dateParts.month = (Number(dateParts.month) - 1).toString();
249
+ return mergeWith(dateParts, {
250
+ day: now.date(),
251
+ month: now.month(),
252
+ year: now.year(),
253
+ hour: now.hour(),
254
+ minute: now.minute(),
255
+ }, (source1: string, source2: number) => {
256
+ if (!source1 || source1.length === 0 || ['dd', 'mm', 'yyyy', 'hh'].includes(source1)) return source2;
257
+ return Number(source1);
258
+ });
259
+ };
260
+
261
+ scrollToCurrent = () => {
262
+ const currentMoment = this.getCurrentMoment(), hour = currentMoment.hour(), minute = currentMoment.minute();
263
+ this.scrollTime('hour', hour);
264
+ this.scrollTime('minute', minute);
265
+ };
266
+
267
+ getSelectorStyle = (forState: PickerState): CSSProperties => {
268
+ const {pickerState} = this.state, styles: CSSProperties = {color: convertColor('color-gray-4')};
269
+ if (pickerState === forState) styles.color = this.getBaseColor(false);
270
+ return styles;
271
+ };
272
+
273
+ getBaseColor = (allowWhite: boolean = true): string => {
274
+ let color = convertColor(this.context.baseColor);
275
+ if (!allowWhite && colorIsWhite(color)) color = convertColor(this.context.contrastColor);
276
+ return color;
277
+ };
278
+
279
+ getCurrentMoment = (value?: DateNumParts): Moment => {
280
+ const {pickerValue} = this.state, toUse: DateNumParts = value !== undefined ? value : pickerValue;
281
+ return toMoment().year(toUse.year).month(toUse.month).date(toUse.day).hour(toUse.hour).minute(toUse.minute);
282
+ };
283
+
284
+ getInputValue = (): Moment | null => {
285
+ const {value, dateType} = this.props;
286
+ if (value.length) {
287
+ if (dateType === DateType.DATE)
288
+ return toMoment(value, 'DD-MM-YYYY');
289
+ if (dateType === DateType.DATETIME)
290
+ return toMoment(value, 'DD-MM-YYYY HH:mm');
291
+ return toMoment(value, 'HH:mm');
292
+ }
293
+ return null;
294
+ };
295
+
296
+ scrollTime = (type: 'hour' | 'minute', id: number) => {
297
+ const element = document.getElementById(`${type}-${pad(id)}`);
298
+ if (element) element.scrollIntoView({block: 'center'});
299
+ };
300
+
301
+ getTodayText = () => this.state.pickerState === PickerState.TIME ? 'Nu' : 'Vandaag';
302
+
303
+ renderPicker = () => {
304
+ const {pickerState, pickerValue} = this.state,
305
+ {pickerClass, dateType, dateParts, disablePast, disableFuture, disableBefore, disableAfter} = this.props;
306
+ return (
307
+ <div className={classNames('dvr-datepicker-container', pickerClass)}>
308
+ {pickerState === PickerState.DATE &&
309
+ <DatePicker onChange={this.onChangeInternalValue} getCurrentMoment={this.getCurrentMoment}
310
+ pickerValue={pickerValue} dateParts={dateParts} disableAfter={disableAfter}
311
+ disableBefore={disableBefore} disableFuture={disableFuture} disablePast={disablePast}/>}
312
+ {pickerState === PickerState.TIME &&
313
+ <TimePicker pickerValue={pickerValue} dateType={dateType} onChange={this.onChangeTime}/>}
314
+ {dateType === DateType.DATETIME && this.renderStateSelector()}
315
+ <div className='dvr-datepicker-footer'>
316
+ <ButtonController onClick={this.onClickToday} label={this.getTodayText()}
317
+ type={ControlVariant.SIMPLE}
318
+ primary={false}/>
319
+ {dateType != DateType.DATE &&
320
+ <ButtonController onClick={this.onSubmit} label='Oké' type={ControlVariant.SIMPLE}
321
+ primary={false}/>}
322
+ </div>
323
+ </div>
324
+ )
325
+ };
326
+
327
+ renderStateSelector = () => {
328
+ return (
329
+ <div className='dvr-pickerstate-container'>
330
+ <AwesomeIcon name='calendar-alt' title='Datum' onClick={this.onChangePickerState(PickerState.DATE)}
331
+ className='selector' style={this.getSelectorStyle(PickerState.DATE)}
332
+ onMouseEnter={this.onMouseEnterSelector(PickerState.DATE)}
333
+ onMouseLeave={this.onMouseLeaveSelector(PickerState.DATE)}/>
334
+ <AwesomeIcon name='clock' title='Tijd' onClick={this.onChangePickerState(PickerState.TIME)}
335
+ className='selector' style={this.getSelectorStyle(PickerState.TIME)}
336
+ onMouseEnter={this.onMouseEnterSelector(PickerState.TIME)}
337
+ onMouseLeave={this.onMouseLeaveSelector(PickerState.TIME)}/>
338
+ </div>
339
+ )
340
+ };
341
+
342
+ componentDidUpdate = (prevProps: Props, prevState: State) => {
343
+ const {pickerState, pickerOpen} = this.state, prevPickerState = prevState.pickerState;
344
+ if (pickerState === PickerState.TIME &&
345
+ (prevPickerState === PickerState.DATE || (!prevState.pickerOpen && pickerOpen)))
346
+ this.scrollToCurrent();
347
+ };
348
+
349
+ render = () => {
350
+ const {dateType, label, error, fullWidth, onEnter, disabled} = this.props, {pickerOpen} = this.state;
351
+ return (
352
+ <>
353
+ <DateInput onChange={this.onChangeInput} value={this.getInputValue()} dateType={dateType}
354
+ onEnter={onEnter} onClickOrnament={this.onOpenPicker} label={label} error={error}
355
+ fullWidth={fullWidth} disabled={disabled}/>
356
+ <WithBackground onClose={this.onClosePicker} active={pickerOpen}>{this.renderPicker()}</WithBackground>
357
+ </>
358
+ )
359
+ };
360
+ }
@@ -0,0 +1,245 @@
1
+ /*
2
+ * Copyright (c) 2021. Dave van Rijn Development
3
+ */
4
+
5
+ import React, {PureComponent} from 'react';
6
+ import moment, {Moment} from 'moment';
7
+ import {ControlVariant} from "../../util/interfaces";
8
+ import DateField from "./dateField";
9
+ import {DefaultInputProps, DVRInputControllerProps} from "../v2/inputController_v2";
10
+ import {toMoment} from "../../util/momentUtil";
11
+
12
+ export enum DateType {DATETIME, DATE, TIME}
13
+
14
+ export enum DateReturnType {DATE, MOMENT, STRING}
15
+
16
+ interface Props<T extends string | Moment | Date = string> extends Omit<DVRInputControllerProps, 'value' | 'onChange'> {
17
+ onChange: (value: T) => void;
18
+ dateType: DateType;
19
+ disableFuture: boolean;
20
+ disablePast: boolean;
21
+ disableAfter?: T;
22
+ disableBefore?: T;
23
+ value?: T;
24
+ returnType: DateReturnType;
25
+ pickerClass: string;
26
+ }
27
+
28
+ interface State {
29
+ value: string;
30
+ }
31
+
32
+ export interface DateTimeParts {
33
+ day: string;
34
+ month: string;
35
+ year: string;
36
+ hour: string;
37
+ minute: string;
38
+ }
39
+
40
+ export default class DateFieldController<T extends string | Moment | Date> extends PureComponent<Props<T>, State> {
41
+ static defaultProps = Object.assign(DefaultInputProps, {
42
+ dateType: DateType.DATE,
43
+ disableFuture: false,
44
+ disablePast: false,
45
+ returnType: DateReturnType.STRING,
46
+ variant: ControlVariant.SIMPLE,
47
+ clearAble: true,
48
+ disabled: false,
49
+ readOnly: false,
50
+ fullWidth: false,
51
+ multiLine: false,
52
+ error: null,
53
+ label: null,
54
+ className: '',
55
+ containerClass: '',
56
+ inputContainerClass: '',
57
+ inputClass: '',
58
+ labelClass: '',
59
+ errorClass: '',
60
+ pickerClass: '',
61
+ ornaments: [],
62
+ rows: 3,
63
+ autoFocus: false,
64
+ type: 'text',
65
+ step: 1,
66
+ inputProps: {},
67
+ unControlled: false,
68
+ autoSelect: false,
69
+ });
70
+
71
+ constructor(props: Props<T>) {
72
+ super(props);
73
+ this.state = {
74
+ value: this.getInitValue(props),
75
+ };
76
+ }
77
+
78
+ private getInitValue = (props: Props<T>): string => {
79
+ let momentValue: Moment;
80
+ const {value, dateType} = props;
81
+ if (!value || (typeof value === 'string' && !value.length)) {
82
+ return '';
83
+ }
84
+ if (value) {
85
+ if (moment.isMoment(value)) momentValue = value;
86
+ else {
87
+ if (value instanceof Date) momentValue = moment(value);
88
+ else {
89
+ momentValue = toMoment(value);
90
+ }
91
+ }
92
+ } else momentValue = moment();
93
+ let format: string;
94
+ if (dateType === DateType.DATE) format = 'DD-MM-YYYY';
95
+ else if (dateType === DateType.DATETIME) format = 'DD-MM-YYYY HH:mm';
96
+ else format = 'HH:mm';
97
+ return momentValue.format(format);
98
+ };
99
+
100
+ onKeyDown = (evt: React.KeyboardEvent) => {
101
+ if (/\d/.test(evt.key)) {
102
+ const target = evt.target as HTMLInputElement, charPos = target.selectionStart, currentValue = target.value;
103
+ if (charPos !== null) {
104
+ let nextValue: string;
105
+ if (charPos === 0) {
106
+ nextValue = evt.key + currentValue.substr(1);
107
+ } else if (charPos === currentValue.length - 1) {
108
+ nextValue = currentValue.substr(0, currentValue.length - 1) + evt.key;
109
+ } else nextValue =
110
+ currentValue.substr(0, charPos) + evt.key + currentValue.substr(charPos + 1, currentValue.length);
111
+ this.onChangeText(nextValue, evt);
112
+ }
113
+ }
114
+ if (this.props.onKeyDown) this.props.onKeyDown(evt);
115
+ };
116
+
117
+ onChangeText = (value: string, evt?: React.ChangeEvent | React.KeyboardEvent, callback?: Function) => {
118
+ if (!/\d/.test(value)) {
119
+ this.onChange('');
120
+ this.setState({value: ''}, () => {
121
+ if (callback) callback();
122
+ });
123
+ } else {
124
+ let target: HTMLInputElement | null = null, currentCaret: number | null = null;
125
+ if (evt) {
126
+ target = evt.target as HTMLInputElement;
127
+ currentCaret = target.selectionStart;
128
+ }
129
+ let mask: string = this.getMask(), newValue = mask,
130
+ valueChars = value.replace(/[-:a-z ]/g, '').split('');
131
+ if (valueChars.length) {
132
+ for (let i = 0; i < mask.length; i++) {
133
+ if (/[-: ]/.test(mask.charAt(i))) continue;
134
+ newValue = newValue.substr(0, i) + valueChars[0] + newValue.substr(i + 1, newValue.length);
135
+ valueChars = valueChars.slice(Math.min(1, valueChars.length), valueChars.length);
136
+ if (!valueChars.length) break;
137
+ }
138
+ }
139
+ this.onChange(newValue);
140
+ this.setState({value: newValue}, () => {
141
+ if (callback) callback();
142
+ else if (target && currentCaret !== null) {
143
+ // Skip mask chars
144
+ currentCaret++;
145
+ if (/[-: ]/.test(newValue.charAt(currentCaret))) currentCaret++;
146
+ window.setTimeout(() => {
147
+ if (target) {
148
+ target.selectionStart = currentCaret;
149
+ target.selectionEnd = currentCaret;
150
+ }
151
+ });
152
+ }
153
+ });
154
+ }
155
+ };
156
+
157
+ onChange = (value: string) => {
158
+ const {returnType, onChange, dateType} = this.props;
159
+ this.setState({value});
160
+ // Only call onChange when the date is validly (only containing numbers)
161
+ if (!/[a-zA-Z]/.test(value)) {
162
+ let momentValue: Moment;
163
+ if (dateType === DateType.DATE) momentValue = toMoment(value, 'DD-MM-YYYY');
164
+ else if (dateType === DateType.TIME) momentValue = toMoment(value, 'HH:mm');
165
+ else momentValue = toMoment(value, 'DD-MM-YYYY HH:mm');
166
+ switch (returnType) {
167
+ case DateReturnType.STRING:
168
+ switch (dateType) {
169
+ case DateType.TIME:
170
+ onChange(momentValue.format('HH:mm') as T);
171
+ break;
172
+ case DateType.DATETIME:
173
+ onChange(momentValue.toISOString(true) as T);
174
+ break;
175
+ case DateType.DATE:
176
+ onChange(momentValue.format('YYYY-MM-DD') as T);
177
+ break;
178
+ }
179
+ break;
180
+ case DateReturnType.DATE:
181
+ onChange(momentValue.toDate() as T);
182
+ break;
183
+ case DateReturnType.MOMENT:
184
+ onChange(momentValue as T);
185
+ break;
186
+ }
187
+ }
188
+ };
189
+
190
+ getMask = (): string => {
191
+ const {dateType} = this.props;
192
+ switch (dateType) {
193
+ case DateType.TIME:
194
+ return 'uu:mm';
195
+ case DateType.DATETIME:
196
+ return 'dd-mm-yyyy uu:mm';
197
+ case DateType.DATE:
198
+ return 'dd-mm-yyyy';
199
+ }
200
+ };
201
+
202
+ getDateTimeParts = (): DateTimeParts => {
203
+ const {value} = this.state, {dateType} = this.props, dateTimeParts: DateTimeParts = {
204
+ day: 'dd',
205
+ hour: 'hh',
206
+ minute: 'mm',
207
+ month: 'mm',
208
+ year: 'yyyy',
209
+ };
210
+ if (value && value.length) {
211
+ let dateParts: string[], timeParts: string[];
212
+ switch (dateType) {
213
+ case DateType.DATE:
214
+ dateParts = value.split('-');
215
+ timeParts = [];
216
+ break;
217
+ case DateType.DATETIME:
218
+ const date = value.split(' ')[0], time = value.split(' ')[1];
219
+ dateParts = date.split('-');
220
+ timeParts = time.split(':');
221
+ break;
222
+ case DateType.TIME:
223
+ timeParts = value.split(':');
224
+ dateParts = [];
225
+ break;
226
+ }
227
+ if (dateParts.length) {
228
+ dateTimeParts.day = dateParts[0];
229
+ dateTimeParts.month = dateParts[1];
230
+ dateTimeParts.year = dateParts[2];
231
+ }
232
+ if (timeParts.length) {
233
+ dateTimeParts.hour = timeParts[0];
234
+ dateTimeParts.minute = timeParts[1];
235
+ }
236
+ }
237
+ return dateTimeParts;
238
+ };
239
+
240
+ render = () => {
241
+ const {value} = this.state;
242
+ return <DateField dateParts={this.getDateTimeParts()} {...this.props} value={value} onChange={this.onChange}
243
+ onChangeText={this.onChangeText} mask={this.getMask()} onKeyDown={this.onKeyDown}/>
244
+ };
245
+ }