@dvrd/dvr-controls 1.0.40 → 1.0.42
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/index.ts +2 -0
- package/package.json +25 -10
- package/src/js/carousel/DVRCarousel.tsx +1 -2
- package/src/js/colorPicker/colorPicker.tsx +1 -0
- package/src/js/date/dvrdDatePicker.tsx +33 -9
- package/src/js/date/style/dvrdDatePicker.scss +6 -0
- package/src/js/input/date/dateField.tsx +1 -1
- package/src/js/label/label.tsx +255 -124
- package/src/js/navigation/mobileNavigation.tsx +150 -0
- package/src/js/navigation/style/mobileNavigation.scss +88 -0
- package/src/js/pdf/history/pdfHistory.ts +0 -1
- package/src/js/pdf/image/pdfImage.tsx +1 -1
- package/src/js/pdf/invoiceTable/pdfInvoiceTable.tsx +1 -1
- package/src/js/pdf/pdfTemplateCreator.tsx +9 -2
- package/src/js/pdf/text/pdfText.tsx +1 -1
- package/src/js/pdf/v2/pdfElement/pdfDraggableElement.tsx +2 -1
- package/src/js/sidebarMenu/sidebarMenu.tsx +1 -1
- package/src/js/snackbar/snackbarController.tsx +1 -1
- package/src/js/util/controlContext.tsx +1 -1
- package/src/js/util/interfaces.ts +15 -1
- package/src/style/common-variables.scss +5 -0
- package/src/style/display-breakpoints.scss +1 -1
package/index.ts
CHANGED
|
@@ -56,6 +56,7 @@ import DvrdSwitch from './src/js/switch/dvrdSwitch';
|
|
|
56
56
|
import DvrdHeaderController from './src/js/header/v2/dvrdHeaderController';
|
|
57
57
|
import FileUpload from './src/js/fileUpload/fileUpload';
|
|
58
58
|
import DvrdRadioController from './src/js/radio/dvrdRadioController';
|
|
59
|
+
import MobileNavigation from "./src/js/navigation/mobileNavigation";
|
|
59
60
|
|
|
60
61
|
export {
|
|
61
62
|
// Components
|
|
@@ -104,6 +105,7 @@ export {
|
|
|
104
105
|
DvrdHeaderController as DvrdHeader,
|
|
105
106
|
FileUpload,
|
|
106
107
|
DvrdRadioController as DvrdRadio,
|
|
108
|
+
MobileNavigation,
|
|
107
109
|
|
|
108
110
|
// Interfaces / Enums
|
|
109
111
|
DialogActionShape,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dvrd/dvr-controls",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.42",
|
|
4
4
|
"description": "Custom web controls",
|
|
5
5
|
"main": "index.ts",
|
|
6
6
|
"files": [
|
|
@@ -13,21 +13,33 @@
|
|
|
13
13
|
"author": "Dave van Rijn",
|
|
14
14
|
"license": "ISC",
|
|
15
15
|
"browserslist": {
|
|
16
|
-
"
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
"production": [
|
|
17
|
+
">0.2%",
|
|
18
|
+
"not dead",
|
|
19
|
+
"ie >= 11"
|
|
20
|
+
],
|
|
21
|
+
"development": [
|
|
22
|
+
">0.2%",
|
|
23
|
+
"not dead",
|
|
24
|
+
"ie >= 11"
|
|
25
|
+
]
|
|
19
26
|
},
|
|
20
27
|
"peerDependencies": {
|
|
21
28
|
"react": "18.2.0",
|
|
22
|
-
"react-dom": "18.2.0"
|
|
29
|
+
"react-dom": "18.2.0",
|
|
30
|
+
"react-router-dom": "6.15.0"
|
|
23
31
|
},
|
|
24
32
|
"devDependencies": {
|
|
25
33
|
"@types/dompurify": "2.4.0",
|
|
26
34
|
"@types/js-cookie": "3.0.3",
|
|
27
|
-
"@types/lodash": "4.
|
|
35
|
+
"@types/lodash.defer": "^4.1.7",
|
|
36
|
+
"@types/lodash.delay": "^4.1.7",
|
|
37
|
+
"@types/lodash.isequal": "^4.5.6",
|
|
38
|
+
"@types/lodash.merge": "^4.6.7",
|
|
39
|
+
"@types/lodash.mergewith": "^4.6.7",
|
|
28
40
|
"@types/node": "18.14.0",
|
|
29
|
-
"@types/react": "18.2.
|
|
30
|
-
"@types/react-color": "
|
|
41
|
+
"@types/react": "^18.2.28",
|
|
42
|
+
"@types/react-color": "2.13.5",
|
|
31
43
|
"@types/react-dom": "18.0.11",
|
|
32
44
|
"@types/uuid": "9.0.0",
|
|
33
45
|
"typescript": "4.9.5"
|
|
@@ -41,11 +53,14 @@
|
|
|
41
53
|
"classnames": "2.3.2",
|
|
42
54
|
"dompurify": "3.0.0",
|
|
43
55
|
"js-cookie": "3.0.1",
|
|
44
|
-
"lodash": "4.
|
|
56
|
+
"lodash.defer": "^4.1.0",
|
|
57
|
+
"lodash.delay": "^4.1.1",
|
|
58
|
+
"lodash.isequal": "^4.5.0",
|
|
59
|
+
"lodash.merge": "^4.6.2",
|
|
60
|
+
"lodash.mergewith": "^4.6.2",
|
|
45
61
|
"moment": "2.29.4",
|
|
46
62
|
"react-color": "2.19.3",
|
|
47
63
|
"react-rnd": "10.4.1",
|
|
48
|
-
"react-router-dom": "6.15.0",
|
|
49
64
|
"uuid": "9.0.0"
|
|
50
65
|
}
|
|
51
66
|
}
|
|
@@ -8,8 +8,7 @@ import {PureComponent, ReactElement} from "react";
|
|
|
8
8
|
import AwesomeIcon from "../icon/awesomeIcon";
|
|
9
9
|
import classNames from "classnames";
|
|
10
10
|
import {voidFunction} from "../util/controlUtil";
|
|
11
|
-
import isEqual from 'lodash
|
|
12
|
-
import {debug} from "../../..";
|
|
11
|
+
import isEqual from 'lodash.isequal';
|
|
13
12
|
|
|
14
13
|
interface Props {
|
|
15
14
|
onClickNext: MouseEventHandler;
|
|
@@ -5,12 +5,13 @@ import './style/dvrdDatePicker.scss';
|
|
|
5
5
|
|
|
6
6
|
import classNames from 'classnames';
|
|
7
7
|
import {Moment} from 'moment';
|
|
8
|
-
import React, {PureComponent, useState} from 'react';
|
|
8
|
+
import React, {PureComponent, useMemo, useState} from 'react';
|
|
9
9
|
import {ChangeFunction, ErrorType} from '../util/interfaces';
|
|
10
10
|
import AwesomeIcon from '../icon/awesomeIcon';
|
|
11
11
|
import {toMoment} from '../util/momentUtil';
|
|
12
12
|
import WithBackground from '../popup/withBackground';
|
|
13
13
|
import DvrdButton from "../button/dvrdButton";
|
|
14
|
+
import {generateComponentId} from "../util/componentUtil";
|
|
14
15
|
|
|
15
16
|
interface Props {
|
|
16
17
|
onChange: ChangeFunction<Moment>;
|
|
@@ -23,13 +24,26 @@ interface Props {
|
|
|
23
24
|
dateFormat?: string;
|
|
24
25
|
disabled?: boolean;
|
|
25
26
|
alwaysShowArrows?: boolean;
|
|
27
|
+
useMobileNative?: boolean;
|
|
28
|
+
id?: string;
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
export default function DvrdDatePicker(props: Props) {
|
|
29
32
|
const {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
+
onChange, className, closeOnChange, error, label, value, placeholder, disabled, alwaysShowArrows,
|
|
34
|
+
useMobileNative
|
|
35
|
+
} = props;
|
|
36
|
+
const [pickerOpen, setPickerOpen] = useState(false);
|
|
37
|
+
const dateFormat = useMemo(() => {
|
|
38
|
+
return props.dateFormat ?? 'YYYY-MM-DD';
|
|
39
|
+
}, [props.dateFormat]);
|
|
40
|
+
const id = useMemo(() => {
|
|
41
|
+
return generateComponentId(props.id);
|
|
42
|
+
}, [props.id]);
|
|
43
|
+
|
|
44
|
+
function onChangeNative(evt: React.ChangeEvent<HTMLInputElement>) {
|
|
45
|
+
onChange(toMoment(evt.target.value), evt);
|
|
46
|
+
}
|
|
33
47
|
|
|
34
48
|
function onClickContainer() {
|
|
35
49
|
if (!disabled)
|
|
@@ -45,14 +59,24 @@ export default function DvrdDatePicker(props: Props) {
|
|
|
45
59
|
setPickerOpen(false)
|
|
46
60
|
}
|
|
47
61
|
|
|
48
|
-
|
|
62
|
+
function renderMobilePicker() {
|
|
63
|
+
if (!useMobileNative) return null;
|
|
64
|
+
return (
|
|
65
|
+
<div className='native-picker-container'>
|
|
66
|
+
<input type='date' onChange={onChangeNative} value={value?.format('YYYY-MM-DD') ?? ''}
|
|
67
|
+
placeholder={placeholder} className='native-date-field' id={`${id}-native`}/>
|
|
68
|
+
<AwesomeIcon name='calendar-alt' className='calendar-icon' htmlFor={`${id}-native`}/>
|
|
69
|
+
</div>
|
|
70
|
+
)
|
|
71
|
+
}
|
|
49
72
|
|
|
50
73
|
return (
|
|
51
74
|
<div className={classNames('dvrd-date-picker', className, error && 'error', disabled && 'disabled')}>
|
|
52
75
|
<label className='field-label'>{label}</label>
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
76
|
+
{renderMobilePicker()}
|
|
77
|
+
<div className={classNames('value-container', useMobileNative && 'no-mob')} onClick={onClickContainer}>
|
|
78
|
+
<label className={classNames('value', !value && 'placeholder')}>{value?.format(dateFormat) ??
|
|
79
|
+
placeholder ?? ''}</label>
|
|
56
80
|
<AwesomeIcon name='calendar-alt' className='calendar-icon'/>
|
|
57
81
|
</div>
|
|
58
82
|
<Picker onClose={onClosePicker} onChange={onChangePicker} open={pickerOpen} value={value}
|
|
@@ -250,7 +274,7 @@ class Picker extends PureComponent<PickerProps> {
|
|
|
250
274
|
if (open && !prevOpen) {
|
|
251
275
|
document.addEventListener('keydown', this.keyListener);
|
|
252
276
|
this.addMountClass();
|
|
253
|
-
window.setTimeout(this.removeMountClass,
|
|
277
|
+
window.setTimeout(this.removeMountClass, 1000);
|
|
254
278
|
} else if (!open && prevOpen) document.removeEventListener('keydown', this.keyListener);
|
|
255
279
|
};
|
|
256
280
|
|
|
@@ -15,7 +15,7 @@ import DatePicker from "./datePicker/datePicker";
|
|
|
15
15
|
import TimePicker from "./timePicker/timePicker";
|
|
16
16
|
import DateInput from "./input/dateInput";
|
|
17
17
|
import {DVRInputControllerProps} from "../v2/inputController_v2";
|
|
18
|
-
import mergeWith from 'lodash
|
|
18
|
+
import mergeWith from 'lodash.mergewith';
|
|
19
19
|
import {colorIsWhite, convertColor, editColor} from '../../util/colorUtil';
|
|
20
20
|
import {toMoment} from '../../util/momentUtil';
|
|
21
21
|
import {pad} from '../../util/controlUtil';
|
package/src/js/label/label.tsx
CHANGED
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
* Copyright (c) 2021. Dave van Rijn Development
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import React from 'react';
|
|
5
|
+
import React, {useContext, useEffect, useMemo, useRef, useState} from 'react';
|
|
6
6
|
import classNames from 'classnames';
|
|
7
7
|
|
|
8
8
|
import {ControlContext} from "../util/controlContext";
|
|
9
9
|
import {convertColor} from "../util/colorUtil";
|
|
10
10
|
import {calculateTextWidth, getComputedProperty, remToPx} from "../util/controlUtil";
|
|
11
|
+
import defer from 'lodash.defer';
|
|
11
12
|
|
|
12
13
|
export enum LabelType {
|
|
13
14
|
LABEL, SPAN, PAR,
|
|
@@ -17,137 +18,112 @@ export enum BreakType {
|
|
|
17
18
|
CHAR, WORD
|
|
18
19
|
}
|
|
19
20
|
|
|
21
|
+
type LabelProps = React.DetailedHTMLProps<React.HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement> &
|
|
22
|
+
React.DetailedHTMLProps<React.LabelHTMLAttributes<HTMLLabelElement>, HTMLLabelElement> &
|
|
23
|
+
React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>;
|
|
24
|
+
|
|
20
25
|
interface Props {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
color?: string
|
|
25
|
-
maxLines
|
|
26
|
-
width?: string | number
|
|
27
|
-
labelProps
|
|
28
|
-
breakMode
|
|
26
|
+
text: string;
|
|
27
|
+
labelType?: LabelType;
|
|
28
|
+
className?: string;
|
|
29
|
+
color?: string;
|
|
30
|
+
maxLines?: number;
|
|
31
|
+
width?: string | number;
|
|
32
|
+
labelProps?: LabelProps;
|
|
33
|
+
breakMode?: BreakType;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function getCurrentFontSize(element: HTMLLabelElement & HTMLSpanElement & HTMLParagraphElement): number {
|
|
37
|
+
let fontSize: number | string = getComputedProperty('font-size', element);
|
|
38
|
+
fontSize = parseInt(fontSize.slice(0, fontSize.length - 2), 10);
|
|
39
|
+
return fontSize;
|
|
29
40
|
}
|
|
30
41
|
|
|
31
|
-
|
|
32
|
-
|
|
42
|
+
/**
|
|
43
|
+
* Break a line by removing characters from the end until a whitespace or dash is found. Removed characters are
|
|
44
|
+
* returned as remainingChars.
|
|
45
|
+
* @param validLine Text to break
|
|
46
|
+
* @return Object containing the remainder of the the line and the removed characters.
|
|
47
|
+
*/
|
|
48
|
+
function breakLine(validLine: string): { validLine: string, remainingChars: string } {
|
|
49
|
+
let remainingChars = '';
|
|
50
|
+
if (/[ -]$/.test(validLine)) {
|
|
51
|
+
validLine = validLine.slice(0, -1);
|
|
52
|
+
remainingChars = ' ';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
while (!/[ -]$/.test(validLine) && validLine.length) {
|
|
56
|
+
remainingChars = validLine.slice(-1) + remainingChars;
|
|
57
|
+
validLine = validLine.slice(0, -1);
|
|
58
|
+
}
|
|
59
|
+
return {validLine, remainingChars};
|
|
33
60
|
}
|
|
34
61
|
|
|
35
62
|
/**
|
|
36
63
|
* Label component which breaks its contents automatically. If maxLines is specified, the label will only break the
|
|
37
64
|
* text until the number of lines is reached. Remaining text will be shown in the title.
|
|
38
65
|
*/
|
|
39
|
-
export default
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
text: '',
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
getColor = (): string => {
|
|
58
|
-
const {contrastColor} = this.context, {color} = this.props, finalColor: string = color ? color : contrastColor;
|
|
59
|
-
return convertColor(finalColor);
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
getWidth = (): string => {
|
|
63
|
-
const {width} = this.props;
|
|
64
|
-
let finalWidth: string = 'initial';
|
|
65
|
-
if (width !== undefined && width !== null) {
|
|
66
|
-
if (typeof width === 'string') finalWidth = width;
|
|
67
|
-
else finalWidth = width + 'px';
|
|
66
|
+
export default function Label(props: Props) {
|
|
67
|
+
const context = useContext(ControlContext);
|
|
68
|
+
const [text, setText] = useState(props.text);
|
|
69
|
+
const labelRef = useRef<HTMLLabelElement & HTMLParagraphElement & HTMLSpanElement>(null);
|
|
70
|
+
const labelType = props.labelType ?? LabelType.LABEL;
|
|
71
|
+
const breakMode = props.breakMode ?? BreakType.CHAR;
|
|
72
|
+
const maxLines = props.maxLines ?? 0;
|
|
73
|
+
const color = useMemo(() => {
|
|
74
|
+
return convertColor(props.color ?? context.contrastColor);
|
|
75
|
+
}, [props.color, context.contrastColor]);
|
|
76
|
+
const width = useMemo(() => {
|
|
77
|
+
let width = 'initial';
|
|
78
|
+
if (props.width) {
|
|
79
|
+
if (typeof props.width === 'string') width = props.width;
|
|
80
|
+
else width = `${width}px`;
|
|
68
81
|
}
|
|
69
|
-
return
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
getLabelProps = () => {
|
|
73
|
-
const {className, labelProps, text} = this.props;
|
|
82
|
+
return width;
|
|
83
|
+
}, [props.width]);
|
|
84
|
+
const labelProps: LabelProps = useMemo(() => {
|
|
74
85
|
return {
|
|
75
|
-
className: classNames('dvr-label', className),
|
|
86
|
+
className: classNames('dvr-label', props.className),
|
|
76
87
|
style: {
|
|
77
|
-
color
|
|
78
|
-
width
|
|
88
|
+
color,
|
|
89
|
+
width,
|
|
79
90
|
display: 'inline-block',
|
|
80
91
|
},
|
|
81
92
|
title: text,
|
|
82
|
-
ref:
|
|
83
|
-
|
|
84
|
-
},
|
|
85
|
-
...labelProps,
|
|
93
|
+
ref: labelRef,
|
|
94
|
+
...(props.labelProps ?? {}),
|
|
86
95
|
};
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
getFontSize = (): number | undefined => {
|
|
90
|
-
if (this.label) {
|
|
91
|
-
let fontSize: number | string = getComputedProperty('font-size', this.label);
|
|
92
|
-
fontSize = parseInt(fontSize.slice(0, fontSize.length - 2), 10);
|
|
93
|
-
return fontSize;
|
|
94
|
-
}
|
|
95
|
-
return undefined;
|
|
96
|
-
};
|
|
96
|
+
}, [props.className, color, width, props.labelProps]);
|
|
97
97
|
|
|
98
98
|
/**
|
|
99
99
|
* Truncate given text to given maxWidth. Truncating is done by removing characters from the end until the text + ...
|
|
100
|
-
* width is less
|
|
101
|
-
* @param text Text to truncate
|
|
102
|
-
* @param maxWidth Max width in px
|
|
100
|
+
* width is less than the given maxWidth. Trailing white spaces are removed and ... are appended.
|
|
103
101
|
*/
|
|
104
|
-
|
|
102
|
+
function truncateText(text: string, maxWidth: number): string {
|
|
105
103
|
let fontSize, fontFamily;
|
|
106
|
-
if (
|
|
107
|
-
fontSize =
|
|
108
|
-
fontFamily = getComputedProperty('font-family',
|
|
104
|
+
if (labelRef.current) {
|
|
105
|
+
fontSize = getCurrentFontSize(labelRef.current);
|
|
106
|
+
fontFamily = getComputedProperty('font-family', labelRef.current);
|
|
109
107
|
}
|
|
110
108
|
while (calculateTextWidth(text + '...', fontSize, fontFamily) >= maxWidth)
|
|
111
109
|
text = text.slice(0, -1);
|
|
112
|
-
return text.
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Break a line by removing characters from the end until a whitespace or dash is found. Removed characters are
|
|
117
|
-
* returned as remainingChars.
|
|
118
|
-
* @param validLine Text to break
|
|
119
|
-
* @return Object containing the remainder of the the line and the removed characters.
|
|
120
|
-
*/
|
|
121
|
-
breakLine = (validLine: string): { validLine: string, remainingChars: string } => {
|
|
122
|
-
let remainingChars = '';
|
|
123
|
-
if (/[ -]$/.test(validLine)) {
|
|
124
|
-
validLine = validLine.slice(0, -1);
|
|
125
|
-
remainingChars = ' ';
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
while (!/[ -]$/.test(validLine) && validLine.length) {
|
|
129
|
-
remainingChars = validLine.slice(-1) + remainingChars;
|
|
130
|
-
validLine = validLine.slice(0, -1);
|
|
131
|
-
}
|
|
132
|
-
return {validLine, remainingChars};
|
|
133
|
-
};
|
|
110
|
+
return text.trimEnd() + '...';
|
|
111
|
+
}
|
|
134
112
|
|
|
135
113
|
/**
|
|
136
114
|
* Process the given text. This function divides the text into lines, by breaking the text according to the break
|
|
137
115
|
* mode given in the props. The width of the text is calculating by using a non-mounted canvas. The font family and
|
|
138
116
|
* font size are calculated from the computed properties of the rendered label.
|
|
139
117
|
*/
|
|
140
|
-
renderText
|
|
141
|
-
const
|
|
142
|
-
if (width === 'initial') return text;
|
|
118
|
+
function renderText() {
|
|
119
|
+
const text = props.text;
|
|
120
|
+
if (width === 'initial' || !labelRef.current) return text;
|
|
143
121
|
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
fontFamily = getComputedProperty('font-family', this.label);
|
|
150
|
-
}
|
|
122
|
+
const chars = text.split('');
|
|
123
|
+
const pxWidth: number = width.endsWith('px') ? Number(width.slice(0, -2)) : remToPx(Number(width.slice(0, -3)));
|
|
124
|
+
const lines: Array<string> = [];
|
|
125
|
+
const fontSize = getCurrentFontSize(labelRef.current);
|
|
126
|
+
const fontFamily = getComputedProperty('font-family', labelRef.current);
|
|
151
127
|
|
|
152
128
|
let line = '';
|
|
153
129
|
for (let i = 0; i < chars.length; i++) {
|
|
@@ -157,7 +133,7 @@ export default class Label extends React.Component<Props, State> {
|
|
|
157
133
|
else {
|
|
158
134
|
let processedLine = line;
|
|
159
135
|
if (breakMode === BreakType.WORD) {
|
|
160
|
-
const {validLine, remainingChars} =
|
|
136
|
+
const {validLine, remainingChars} = breakLine(line);
|
|
161
137
|
processedLine = validLine;
|
|
162
138
|
line = remainingChars;
|
|
163
139
|
} else line = '';
|
|
@@ -165,8 +141,8 @@ export default class Label extends React.Component<Props, State> {
|
|
|
165
141
|
lines.push(processedLine);
|
|
166
142
|
if (maxLines > 0 && lines.length === maxLines) {
|
|
167
143
|
if (i < chars.length)
|
|
168
|
-
|
|
169
|
-
lines[lines.length - 1] =
|
|
144
|
+
// Truncate last line if chars remaining
|
|
145
|
+
lines[lines.length - 1] = truncateText(lines[lines.length - 1], pxWidth);
|
|
170
146
|
line = '';
|
|
171
147
|
break;
|
|
172
148
|
}
|
|
@@ -174,23 +150,178 @@ export default class Label extends React.Component<Props, State> {
|
|
|
174
150
|
}
|
|
175
151
|
}
|
|
176
152
|
if (line.length) lines.push(line);
|
|
177
|
-
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
153
|
+
setText(lines.join('\n'));
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
useEffect(() => {
|
|
157
|
+
defer(renderText);
|
|
158
|
+
}, []);
|
|
159
|
+
|
|
160
|
+
switch (labelType) {
|
|
161
|
+
case LabelType.LABEL:
|
|
162
|
+
return <label {...labelProps}>{text}</label>;
|
|
163
|
+
case LabelType.PAR:
|
|
164
|
+
return <p {...labelProps}>{text}</p>;
|
|
165
|
+
case LabelType.SPAN:
|
|
166
|
+
return <span {...labelProps}>{text}</span>;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// class _Label extends React.Component<Props, State> {
|
|
171
|
+
// declare context: React.ContextType<typeof ControlContext>;
|
|
172
|
+
// static contextType = ControlContext;
|
|
173
|
+
//
|
|
174
|
+
// static defaultProps = {
|
|
175
|
+
// labelType: LabelType.LABEL,
|
|
176
|
+
// className: '',
|
|
177
|
+
// labelProps: {},
|
|
178
|
+
// breakMode: BreakType.CHAR,
|
|
179
|
+
// maxLines: 0,
|
|
180
|
+
// };
|
|
181
|
+
//
|
|
182
|
+
// label: HTMLElement | null = null;
|
|
183
|
+
//
|
|
184
|
+
// state = {
|
|
185
|
+
// text: '',
|
|
186
|
+
// };
|
|
187
|
+
//
|
|
188
|
+
// getColor = (): string => {
|
|
189
|
+
// const {contrastColor} = this.context, {color} = this.props, finalColor: string = color ? color : contrastColor;
|
|
190
|
+
// return convertColor(finalColor);
|
|
191
|
+
// };
|
|
192
|
+
//
|
|
193
|
+
// getWidth = (): string => {
|
|
194
|
+
// const {width} = this.props;
|
|
195
|
+
// let finalWidth: string = 'initial';
|
|
196
|
+
// if (width !== undefined && width !== null) {
|
|
197
|
+
// if (typeof width === 'string') finalWidth = width;
|
|
198
|
+
// else finalWidth = width + 'px';
|
|
199
|
+
// }
|
|
200
|
+
// return finalWidth;
|
|
201
|
+
// };
|
|
202
|
+
//
|
|
203
|
+
// getLabelProps = (): LabelProps => {
|
|
204
|
+
// const {className, labelProps, text} = this.props;
|
|
205
|
+
// return {
|
|
206
|
+
// className: classNames('dvr-label', className),
|
|
207
|
+
// style: {
|
|
208
|
+
// color: this.getColor(),
|
|
209
|
+
// width: this.getWidth(),
|
|
210
|
+
// display: 'inline-block',
|
|
211
|
+
// },
|
|
212
|
+
// title: text,
|
|
213
|
+
// ref: (ref: HTMLElement | null) => {
|
|
214
|
+
// this.label = ref
|
|
215
|
+
// },
|
|
216
|
+
// ...labelProps,
|
|
217
|
+
// };
|
|
218
|
+
// };
|
|
219
|
+
//
|
|
220
|
+
// getFontSize = (): number | undefined => {
|
|
221
|
+
// if (this.label) {
|
|
222
|
+
// let fontSize: number | string = getComputedProperty('font-size', this.label);
|
|
223
|
+
// fontSize = parseInt(fontSize.slice(0, fontSize.length - 2), 10);
|
|
224
|
+
// return fontSize;
|
|
225
|
+
// }
|
|
226
|
+
// return undefined;
|
|
227
|
+
// };
|
|
228
|
+
//
|
|
229
|
+
// /**
|
|
230
|
+
// * Truncate given text to given maxWidth. Truncating is done by removing characters from the end until the text + ...
|
|
231
|
+
// * width is less then the given maxWidth. Trailing white spaces are removed and ... are appended.
|
|
232
|
+
// * @param text Text to truncate
|
|
233
|
+
// * @param maxWidth Max width in px
|
|
234
|
+
// */
|
|
235
|
+
// reduceText = (text: string, maxWidth: number): string => {
|
|
236
|
+
// let fontSize, fontFamily;
|
|
237
|
+
// if (this.label) {
|
|
238
|
+
// fontSize = this.getFontSize();
|
|
239
|
+
// fontFamily = getComputedProperty('font-family', this.label);
|
|
240
|
+
// }
|
|
241
|
+
// while (calculateTextWidth(text + '...', fontSize, fontFamily) >= maxWidth)
|
|
242
|
+
// text = text.slice(0, -1);
|
|
243
|
+
// return text.trimEnd() + '...';
|
|
244
|
+
// };
|
|
245
|
+
//
|
|
246
|
+
// /**
|
|
247
|
+
// * Break a line by removing characters from the end until a whitespace or dash is found. Removed characters are
|
|
248
|
+
// * returned as remainingChars.
|
|
249
|
+
// * @param validLine Text to break
|
|
250
|
+
// * @return Object containing the remainder of the the line and the removed characters.
|
|
251
|
+
// */
|
|
252
|
+
// breakLine = (validLine: string): { validLine: string, remainingChars: string } => {
|
|
253
|
+
// let remainingChars = '';
|
|
254
|
+
// if (/[ -]$/.test(validLine)) {
|
|
255
|
+
// validLine = validLine.slice(0, -1);
|
|
256
|
+
// remainingChars = ' ';
|
|
257
|
+
// }
|
|
258
|
+
//
|
|
259
|
+
// while (!/[ -]$/.test(validLine) && validLine.length) {
|
|
260
|
+
// remainingChars = validLine.slice(-1) + remainingChars;
|
|
261
|
+
// validLine = validLine.slice(0, -1);
|
|
262
|
+
// }
|
|
263
|
+
// return {validLine, remainingChars};
|
|
264
|
+
// };
|
|
265
|
+
//
|
|
266
|
+
// /**
|
|
267
|
+
// * Process the given text. This function divides the text into lines, by breaking the text according to the break
|
|
268
|
+
// * mode given in the props. The width of the text is calculating by using a non-mounted canvas. The font family and
|
|
269
|
+
// * font size are calculated from the computed properties of the rendered label.
|
|
270
|
+
// */
|
|
271
|
+
// renderText = () => {
|
|
272
|
+
// const {text, maxLines, breakMode} = this.props, chars = text.split(''), width = this.getWidth();
|
|
273
|
+
// if (width === 'initial') return text;
|
|
274
|
+
//
|
|
275
|
+
// const pxWidth: number = width.endsWith('px') ? Number(width.slice(0, -2)) : remToPx(Number(width.slice(0, -3))),
|
|
276
|
+
// lines: string[] = new Array<string>();
|
|
277
|
+
// let fontSize, fontFamily;
|
|
278
|
+
// if (this.label) {
|
|
279
|
+
// fontSize = this.getFontSize();
|
|
280
|
+
// fontFamily = getComputedProperty('font-family', this.label);
|
|
281
|
+
// }
|
|
282
|
+
//
|
|
283
|
+
// let line = '';
|
|
284
|
+
// for (let i = 0; i < chars.length; i++) {
|
|
285
|
+
// const char = chars[i];
|
|
286
|
+
// if (calculateTextWidth(line + char, fontSize, fontFamily) < pxWidth)
|
|
287
|
+
// line += char;
|
|
288
|
+
// else {
|
|
289
|
+
// let processedLine = line;
|
|
290
|
+
// if (breakMode === BreakType.WORD) {
|
|
291
|
+
// const {validLine, remainingChars} = this.breakLine(line);
|
|
292
|
+
// processedLine = validLine;
|
|
293
|
+
// line = remainingChars;
|
|
294
|
+
// } else line = '';
|
|
295
|
+
//
|
|
296
|
+
// lines.push(processedLine);
|
|
297
|
+
// if (maxLines > 0 && lines.length === maxLines) {
|
|
298
|
+
// if (i < chars.length)
|
|
299
|
+
// // Truncate last line if chars remaining
|
|
300
|
+
// lines[lines.length - 1] = this.reduceText(lines[lines.length - 1], pxWidth);
|
|
301
|
+
// line = '';
|
|
302
|
+
// break;
|
|
303
|
+
// }
|
|
304
|
+
// line += char;
|
|
305
|
+
// }
|
|
306
|
+
// }
|
|
307
|
+
// if (line.length) lines.push(line);
|
|
308
|
+
// this.setState({text: lines.join('\n')});
|
|
309
|
+
// };
|
|
310
|
+
//
|
|
311
|
+
// componentDidMount = () => {
|
|
312
|
+
// // Make sure the label has mounted before calculating the text
|
|
313
|
+
// window.setTimeout(this.renderText, 0);
|
|
314
|
+
// };
|
|
315
|
+
//
|
|
316
|
+
// render = () => {
|
|
317
|
+
// const {labelType} = this.props, {text} = this.state, labelProps = this.getLabelProps();
|
|
318
|
+
// switch (labelType) {
|
|
319
|
+
// case LabelType.LABEL:
|
|
320
|
+
// return <label {...labelProps}>{text}</label>;
|
|
321
|
+
// case LabelType.PAR:
|
|
322
|
+
// return <p {...labelProps}>{text}</p>;
|
|
323
|
+
// case LabelType.SPAN:
|
|
324
|
+
// return <span {...labelProps}>{text}</span>;
|
|
325
|
+
// }
|
|
326
|
+
// };
|
|
327
|
+
// }
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import './style/mobileNavigation.scss';
|
|
2
|
+
|
|
3
|
+
import classNames from 'classnames';
|
|
4
|
+
import React, {
|
|
5
|
+
ForwardedRef,
|
|
6
|
+
forwardRef,
|
|
7
|
+
MouseEventHandler,
|
|
8
|
+
ReactElement, useEffect,
|
|
9
|
+
useImperativeHandle,
|
|
10
|
+
useMemo, useRef,
|
|
11
|
+
useState
|
|
12
|
+
} from 'react';
|
|
13
|
+
import {useMatch, useNavigate} from 'react-router';
|
|
14
|
+
import {NavigationItem} from "../util/interfaces";
|
|
15
|
+
import {AwesomeIcon, hasHover} from "../../../index";
|
|
16
|
+
import defer from 'lodash.defer';
|
|
17
|
+
|
|
18
|
+
interface Props {
|
|
19
|
+
onClickItem?: (item: NavigationItem) => MouseEventHandler;
|
|
20
|
+
navigationItems: Array<NavigationItem>;
|
|
21
|
+
className?: string;
|
|
22
|
+
menuIcon?: ReactElement;
|
|
23
|
+
id?: string;
|
|
24
|
+
keepOpen?: true;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface MobileNavigationRef {
|
|
28
|
+
toggle: (forceState?: boolean) => void;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function MobileNavigation(props: Props, ref: ForwardedRef<MobileNavigationRef>) {
|
|
32
|
+
const {navigationItems, onClickItem, className, menuIcon, id, keepOpen} = props;
|
|
33
|
+
const [open, setOpen] = useState(false);
|
|
34
|
+
const [topItems, bottomItems] = useMemo(() => {
|
|
35
|
+
const topItems: Array<NavigationItem> = [];
|
|
36
|
+
const bottomItems: Array<NavigationItem> = [];
|
|
37
|
+
for (const item of navigationItems) {
|
|
38
|
+
if (item.placement === 'bottom') bottomItems.push(item);
|
|
39
|
+
// Default placement top
|
|
40
|
+
else topItems.push(item);
|
|
41
|
+
}
|
|
42
|
+
return [topItems, bottomItems];
|
|
43
|
+
}, [navigationItems]);
|
|
44
|
+
const navigate = useNavigate();
|
|
45
|
+
const menuRef = useRef<HTMLDivElement>(null);
|
|
46
|
+
|
|
47
|
+
function toggle(forceState?: boolean) {
|
|
48
|
+
if (forceState !== undefined) setOpen(forceState);
|
|
49
|
+
else setOpen(!open);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function onClickIcon() {
|
|
53
|
+
toggle();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function _onClick(item: NavigationItem) {
|
|
57
|
+
return function (evt: React.MouseEvent) {
|
|
58
|
+
if (!item.passPropagation) evt.stopPropagation();
|
|
59
|
+
if (!keepOpen) toggle(false);
|
|
60
|
+
if (onClickItem) return onClickItem(item)(evt);
|
|
61
|
+
else if (item.onClick) item.onClick(evt);
|
|
62
|
+
else if (item.route) _navigate(item.route);
|
|
63
|
+
else throw new Error(`Mobile navigation item ${item.id} has no click handler.`)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function _navigate(route: string) {
|
|
68
|
+
if (route.startsWith('http'))
|
|
69
|
+
window.open(route, '_blank');
|
|
70
|
+
else navigate(route);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function renderNavigationItem(item: NavigationItem) {
|
|
74
|
+
return (
|
|
75
|
+
<MobileNavigationItem key={item.id} item={item} onClick={_onClick}/>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function renderIcon() {
|
|
80
|
+
let icon: React.ReactElement;
|
|
81
|
+
if (!!menuIcon) {
|
|
82
|
+
const className = classNames(menuIcon.props.className, 'mobile-navigation-icon');
|
|
83
|
+
icon = React.cloneElement(menuIcon, {...menuIcon.props, className});
|
|
84
|
+
} else icon = <AwesomeIcon name='bars' className='mobile-navigation-icon'/>;
|
|
85
|
+
return (
|
|
86
|
+
<div className='mobile-navigation-icon-container' onClick={onClickIcon}>
|
|
87
|
+
{icon}
|
|
88
|
+
</div>
|
|
89
|
+
)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function renderMenu() {
|
|
93
|
+
return (
|
|
94
|
+
<>
|
|
95
|
+
{open && <div className='click-catcher'/>}
|
|
96
|
+
<div className='mobile-navigation-container' ref={menuRef}>
|
|
97
|
+
<AwesomeIcon name='times' className='mobile-navigation-close' onClick={onClickIcon}/>
|
|
98
|
+
<div className='mobile-navigation-items'>
|
|
99
|
+
{topItems.map(renderNavigationItem)}
|
|
100
|
+
</div>
|
|
101
|
+
<div className='mobile-navigation-items'>
|
|
102
|
+
{bottomItems.map(renderNavigationItem)}
|
|
103
|
+
</div>
|
|
104
|
+
</div>
|
|
105
|
+
</>
|
|
106
|
+
)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function outsideClickListener() {
|
|
110
|
+
const menu = menuRef.current;
|
|
111
|
+
if (!hasHover(menu)) toggle(false);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
useEffect(() => {
|
|
115
|
+
defer(() => {
|
|
116
|
+
if (open) document.addEventListener('click', outsideClickListener);
|
|
117
|
+
else document.removeEventListener('click', outsideClickListener);
|
|
118
|
+
});
|
|
119
|
+
return function () {
|
|
120
|
+
document.removeEventListener('click', outsideClickListener);
|
|
121
|
+
}
|
|
122
|
+
}, [open]);
|
|
123
|
+
|
|
124
|
+
useImperativeHandle(ref, () => ({toggle}));
|
|
125
|
+
|
|
126
|
+
return (
|
|
127
|
+
<div className={classNames('dvrd-mobile-navigation', open && 'open', className)} id={id}>
|
|
128
|
+
{renderIcon()}
|
|
129
|
+
{renderMenu()}
|
|
130
|
+
</div>
|
|
131
|
+
)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
interface ItemProps {
|
|
135
|
+
item: NavigationItem;
|
|
136
|
+
onClick: (item: NavigationItem) => MouseEventHandler;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function MobileNavigationItem(props: ItemProps) {
|
|
140
|
+
const {item, onClick} = props;
|
|
141
|
+
const isActive = item.route && Boolean(useMatch(item.route));
|
|
142
|
+
return (
|
|
143
|
+
<div key={item.id} className={classNames('mobile-navigation-item', isActive && 'active', item.className)}
|
|
144
|
+
id={item.id} onClick={onClick(item)}>
|
|
145
|
+
{item.content}
|
|
146
|
+
</div>
|
|
147
|
+
)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export default forwardRef<MobileNavigationRef, Props>(MobileNavigation);
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
@import '../../../style/variables';
|
|
2
|
+
|
|
3
|
+
.dvrd-mobile-navigation {
|
|
4
|
+
@media(min-width: 750px) {
|
|
5
|
+
display: none;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
color: $color-blue-text;
|
|
9
|
+
position: fixed;
|
|
10
|
+
z-index: 2000;
|
|
11
|
+
|
|
12
|
+
.mobile-navigation-icon-container {
|
|
13
|
+
visibility: visible;
|
|
14
|
+
opacity: 1;
|
|
15
|
+
position: fixed;
|
|
16
|
+
top: 1rem;
|
|
17
|
+
left: 1rem;
|
|
18
|
+
padding: .5rem; // For better UX, makes the touch surface bigger
|
|
19
|
+
|
|
20
|
+
.mobile-navigation-icon {
|
|
21
|
+
@include backgroundShadow2;
|
|
22
|
+
font-size: 2rem;
|
|
23
|
+
padding: .25rem;
|
|
24
|
+
background-color: white;
|
|
25
|
+
border-radius: .5rem;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.click-catcher {
|
|
30
|
+
position: fixed;
|
|
31
|
+
top: 0;
|
|
32
|
+
left: 0;
|
|
33
|
+
width: 100dvw;
|
|
34
|
+
height: 100dvh;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.mobile-navigation-container {
|
|
38
|
+
@include backgroundShadow3($borderRadius: 0);
|
|
39
|
+
position: fixed;
|
|
40
|
+
top: 0;
|
|
41
|
+
left: 0;
|
|
42
|
+
height: 100dvh;
|
|
43
|
+
max-width: 100dvw;
|
|
44
|
+
overflow-y: auto;
|
|
45
|
+
background-color: white;
|
|
46
|
+
transform: translateX(-100%);
|
|
47
|
+
transition: transform .2s ease-in-out;
|
|
48
|
+
display: grid;
|
|
49
|
+
grid-template-rows: fit-content(100%) 1fr fit-content(100%);
|
|
50
|
+
|
|
51
|
+
.mobile-navigation-close {
|
|
52
|
+
position: sticky;
|
|
53
|
+
top: 1rem;
|
|
54
|
+
left: 1rem;
|
|
55
|
+
font-size: 2rem;
|
|
56
|
+
padding: .5rem;
|
|
57
|
+
z-index: 1;
|
|
58
|
+
margin-bottom: 1rem;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.mobile-navigation-items {
|
|
62
|
+
display: grid;
|
|
63
|
+
align-content: start;
|
|
64
|
+
|
|
65
|
+
.mobile-navigation-item {
|
|
66
|
+
padding: 1rem;
|
|
67
|
+
font-size: 1.1rem;
|
|
68
|
+
|
|
69
|
+
&.active {
|
|
70
|
+
color: $color-blue-1;
|
|
71
|
+
font-weight: 500;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
&.open {
|
|
78
|
+
.mobile-navigation-icon-container {
|
|
79
|
+
visibility: hidden;
|
|
80
|
+
opacity: 0;
|
|
81
|
+
transition: visibility .2s ease-in-out, opacity .2s ease-in-out;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.mobile-navigation-container {
|
|
85
|
+
transform: none;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -13,7 +13,6 @@ export const addToHistory = (items: PDFElementParams<any, any>[], mainFont: PdfF
|
|
|
13
13
|
if (history !== HISTORY[currentIdx]) {
|
|
14
14
|
HISTORY.push(history);
|
|
15
15
|
currentIdx++;
|
|
16
|
-
console.debug('Current history', HISTORY.map((hist: string) => JSON.parse(hist)));
|
|
17
16
|
}
|
|
18
17
|
};
|
|
19
18
|
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
} from "../../util/interfaces";
|
|
10
10
|
import {PDFDraggable, PdfElement} from "../element/pdfElement";
|
|
11
11
|
import {nullify} from "../../util/miscUtil";
|
|
12
|
-
import
|
|
12
|
+
import merge from 'lodash.merge';
|
|
13
13
|
import {AwesomeIcon, directTimeout, PAGE_HEIGHT, PAGE_WIDTH} from "../../../../index";
|
|
14
14
|
|
|
15
15
|
interface Props {
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
PDFElementParams, PDFElementPersist, PDFElementType, PdfFont, PDFInvoiceTableParams
|
|
10
10
|
} from "../../util/interfaces";
|
|
11
11
|
import {PDFDraggable, PdfElement} from "../element/pdfElement";
|
|
12
|
-
import
|
|
12
|
+
import merge from 'lodash.merge';
|
|
13
13
|
import {parsePrice, pxToPt} from "../../util/pdfUtil";
|
|
14
14
|
import {WithEvents} from "../../../../index";
|
|
15
15
|
|
|
@@ -78,6 +78,14 @@ export default class PDFTemplateCreator extends PureComponent<Props, State> {
|
|
|
78
78
|
this.state = {elements, focusedElement: null, canRedo: false, canUndo: false};
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
+
reloadTemplate(items: DefaultPDFElementParams<any, any>[]) {
|
|
82
|
+
const elements: IndexedObject<{
|
|
83
|
+
element: React.ReactElement,
|
|
84
|
+
config: DefaultPDFElementParams<any, any>
|
|
85
|
+
}> = this.processItems(items);
|
|
86
|
+
this.setState({elements, canRedo: false, canUndo: false, focusedElement: null});
|
|
87
|
+
}
|
|
88
|
+
|
|
81
89
|
processItems = (items?: DefaultPDFElementParams<any, any>[],
|
|
82
90
|
convertUnits: boolean = true): IndexedObject<{
|
|
83
91
|
element: React.ReactElement,
|
|
@@ -276,8 +284,7 @@ export default class PDFTemplateCreator extends PureComponent<Props, State> {
|
|
|
276
284
|
const supportMultiPage = this.props.options?.supportMultiPage ?? this.props.supportMultiPage;
|
|
277
285
|
const enableTableHeaderColor = this.props.options?.enableTableHeaderColor;
|
|
278
286
|
return (
|
|
279
|
-
<div className='pdf-creator' onClick={this.onResetElement}
|
|
280
|
-
style={{maxWidth: maxWidth ?? '100%'}}>
|
|
287
|
+
<div className='pdf-creator' onClick={this.onResetElement} style={{maxWidth: maxWidth ?? '100%'}}>
|
|
281
288
|
<div className='footer-container'>
|
|
282
289
|
<div className='left-buttons'>
|
|
283
290
|
{onPreview !== undefined ? <DvrdButton onClick={this.onPreview} label='Voorbeeld'/> : <div/>}
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
} from "../../util/interfaces";
|
|
18
18
|
import {PDFDraggable, PdfElement} from "../element/pdfElement";
|
|
19
19
|
import {debug, sanitizeHtml} from "../../util/miscUtil";
|
|
20
|
-
import
|
|
20
|
+
import merge from 'lodash.merge';
|
|
21
21
|
import {PAGE_WIDTH, pxToPt, setPdfVariables, waitForFonts} from "../../util/pdfUtil";
|
|
22
22
|
import {directTimeout} from "../../util/componentUtil";
|
|
23
23
|
import {dispatchCustomEvent, showDialog} from "../../util/eventUtil";
|
|
@@ -16,7 +16,8 @@ import React, {
|
|
|
16
16
|
import {ElementPosition, PDFElementPersist, RNDDimensions} from "../../../util/interfaces";
|
|
17
17
|
import {generateComponentId} from "../../../util/componentUtil";
|
|
18
18
|
import {DraggableData, HandleStyles, Position, ResizableDelta, ResizeEnable, Rnd, RndDragCallback} from 'react-rnd';
|
|
19
|
-
import
|
|
19
|
+
import defer from 'lodash.defer';
|
|
20
|
+
import delay from 'lodash.delay';
|
|
20
21
|
import {PDFDraggableType, ResizeDirection} from "../types/pdfTemplateTypes";
|
|
21
22
|
import {PAGE_WIDTH} from "../../../util/pdfUtil";
|
|
22
23
|
import classNames from 'classnames';
|
|
@@ -9,7 +9,7 @@ import {SidebarItem, SideMenuMode} from "../util/interfaces";
|
|
|
9
9
|
import classNames from 'classnames';
|
|
10
10
|
import {AwesomeIcon, generateComponentId, isAbsoluteLink} from "../../../index";
|
|
11
11
|
import {ControlContext} from "../util/controlContext";
|
|
12
|
-
import
|
|
12
|
+
import defer from 'lodash.defer';
|
|
13
13
|
import {IconName} from '@fortawesome/fontawesome-svg-core';
|
|
14
14
|
|
|
15
15
|
interface Props {
|
|
@@ -6,7 +6,7 @@ import React from 'react';
|
|
|
6
6
|
import {Snackbar} from "./snackbar";
|
|
7
7
|
import {CustomAppEvent, DialogConfig, Snack} from "../util/interfaces";
|
|
8
8
|
import WithEvents from '../events/withEvents';
|
|
9
|
-
import
|
|
9
|
+
import delay from 'lodash.delay';
|
|
10
10
|
|
|
11
11
|
interface Props {
|
|
12
12
|
containerClass: string;
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import React, {Context, PropsWithChildren, useEffect} from 'react';
|
|
6
6
|
import {ControlVariant} from "./interfaces";
|
|
7
|
-
import
|
|
7
|
+
import defer from 'lodash.defer';
|
|
8
8
|
|
|
9
9
|
// noinspection JSUnusedGlobalSymbols
|
|
10
10
|
export interface ThemeContextShape {
|
|
@@ -14,6 +14,20 @@ export interface MenuItem {
|
|
|
14
14
|
normalCase?: boolean;
|
|
15
15
|
id?: string;
|
|
16
16
|
allowPropagation?: boolean;
|
|
17
|
+
index?: number;
|
|
18
|
+
route?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface NavigationItem {
|
|
22
|
+
content: React.ReactNode;
|
|
23
|
+
children?: Array<NavigationItem>;
|
|
24
|
+
onClick?: MouseEventHandler;
|
|
25
|
+
id: string;
|
|
26
|
+
passPropagation?: true;
|
|
27
|
+
index?: number;
|
|
28
|
+
route?: string;
|
|
29
|
+
placement?: 'top' | 'bottom';
|
|
30
|
+
className?: string;
|
|
17
31
|
}
|
|
18
32
|
|
|
19
33
|
export interface PasswordRule {
|
|
@@ -78,7 +92,7 @@ export interface HeaderItem {
|
|
|
78
92
|
|
|
79
93
|
export enum ModeEnum {DETAIL = 'detail', EDIT = 'edit', NEW = 'new'}
|
|
80
94
|
|
|
81
|
-
export enum Breakpoint {XS = 0, SM =
|
|
95
|
+
export enum Breakpoint {XS = 0, SM = 750, MD = 960, LG = 1280, XL = 1920}
|
|
82
96
|
|
|
83
97
|
export enum MenuPlacement {TOP_LEFT, TOP_RIGHT, BOTTOM_RIGHT, BOTTOM_LEFT}
|
|
84
98
|
|
|
@@ -220,6 +220,11 @@ $hoverColor: white, $duration: .2s) {
|
|
|
220
220
|
border-radius: .5rem;
|
|
221
221
|
}
|
|
222
222
|
|
|
223
|
+
@mixin backgroundShadow3($color: black, $alpha: .1, $borderRadius: .5rem) {
|
|
224
|
+
box-shadow: 0 4px 4px rgba($color, $alpha), 0 0 4px rgba($color, $alpha);
|
|
225
|
+
border-radius: $borderRadius;
|
|
226
|
+
}
|
|
227
|
+
|
|
223
228
|
@mixin popupContainer() {
|
|
224
229
|
position: fixed;
|
|
225
230
|
top: 0;
|