@dvrd/dvr-controls 0.0.28 → 0.0.33

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dvrd/dvr-controls",
3
- "version": "0.0.28",
3
+ "version": "0.0.33",
4
4
  "description": "Custom web controls",
5
5
  "main": "index.ts",
6
6
  "scripts": {},
@@ -10,98 +10,33 @@
10
10
  },
11
11
  "author": "Dave van Rijn",
12
12
  "license": "ISC",
13
- "dependencies": {
14
- "@sentry/browser": "^6.2.1",
15
- "@types/js-cookie": "^2.2.6",
16
- "@types/lodash": "^4.14.168",
17
- "@types/node": "14.14.32",
18
- "@types/react": "~17.0.2",
19
- "@types/react-dom": "17.0.1",
20
- "@types/react-router-dom": "~5.1.6",
21
- "@types/uuid": "^8.3.0",
22
- "@types/react-color": "^3.0.4",
23
- "@types/dompurify": "2.2.1",
24
- "awesome-typescript-loader": "^5.2.1",
25
- "classnames": "^2.2.6",
26
- "compression-webpack-plugin": "^7.1.2",
27
- "cross-env": "^7.0.3",
28
- "css-loader": "^5.1.1",
29
- "cssnano": "^4.1.10",
30
- "cssnano-preset-advanced": "^4.0.7",
31
- "file-loader": "^6.2.0",
32
- "html-webpack-plugin": "^5.3.0",
33
- "js-cookie": "^2.2.1",
34
- "lodash": "^4.17.21",
35
- "mini-css-extract-plugin": "^1.3.9",
36
- "moment": "^2.29.1",
37
- "optimize-css-assets-webpack-plugin": "^5.0.4",
38
- "prerender-spa-plugin": "^3.4.0",
39
- "query-string": "^6.14.1",
40
- "react": "^17.0.1",
41
- "react-color": "^2.19.3",
42
- "react-dom": "^17.0.1",
43
- "react-router-dom": "^5.2.0",
44
- "sass-loader": "^11.0.1",
45
- "seamless-scroll-polyfill": "^1.2.3",
46
- "style-loader": "^2.0.0",
47
- "terser-webpack-plugin": "^5.1.1",
48
- "typescript": "^4.2.3",
49
- "uglifyjs-webpack-plugin": "^2.2.0",
50
- "uuid": "^8.3.2",
51
- "webpack": "^5.24.4",
52
- "webpack-cli": "^4.5.0",
53
- "source-map-loader": "^2.0.1",
54
- "@types/react-helmet": "^6.1.0",
55
- "react-helmet": "^6.1.0",
56
- "@sentry/react": "^6.2.1",
57
- "dompurify": "^2.2.6",
58
- "react-rnd": "^10.2.4"
59
- },
60
13
  "browserslist": {
61
14
  "0": ">0.2%",
62
15
  "1": "not dead",
63
16
  "2": "ie >= 11"
64
17
  },
65
18
  "devDependencies": {
66
- "@sentry/react": "^6.2.2",
67
19
  "@types/js-cookie": "^2.2.6",
68
20
  "@types/lodash": "^4.14.168",
69
21
  "@types/node": "^14.14.34",
70
22
  "@types/react": "^17.0.3",
71
23
  "@types/react-dom": "^17.0.2",
72
- "@types/react-helmet": "^6.1.0",
73
24
  "@types/react-router-dom": "^5.1.7",
74
25
  "@types/uuid": "^8.3.0",
75
- "awesome-typescript-loader": "^5.2.1",
26
+ "@types/react-color": "^3.0.6",
27
+ "@types/dompurify": "^2.2.3",
76
28
  "classnames": "^2.2.6",
77
- "compression-webpack-plugin": "^7.1.2",
78
29
  "cross-env": "^7.0.3",
79
- "css-loader": "^5.1.2",
80
- "cssnano": "^4.1.10",
81
- "cssnano-preset-advanced": "^4.0.7",
82
- "file-loader": "^6.2.0",
83
- "html-webpack-plugin": "^5.3.1",
84
30
  "js-cookie": "^2.2.1",
85
31
  "lodash": "^4.17.21",
86
- "mini-css-extract-plugin": "^1.3.9",
87
32
  "moment": "^2.29.1",
88
- "optimize-css-assets-webpack-plugin": "^5.0.4",
89
- "prerender-spa-plugin-next": "^4.1.5",
90
- "query-string": "^6.14.1",
91
33
  "react": "^17.0.1",
92
34
  "react-dom": "^17.0.1",
93
- "react-helmet": "^6.1.0",
35
+ "react-color": "^2.19.3",
94
36
  "react-router-dom": "^5.2.0",
95
- "sass-loader": "^11.1.0",
96
- "seamless-scroll-polyfill": "^1.2.3",
97
- "source-map-loader": "^2.0.1",
98
- "style-loader": "^2.0.0",
99
- "terser-webpack-plugin": "^5.1.1",
37
+ "react-rnd": "^10.3.5",
100
38
  "typescript": "^4.2.3",
101
- "uglifyjs-webpack-plugin": "^2.2.0",
102
39
  "uuid": "^8.3.2",
103
- "webpack": "^5.25.1",
104
- "webpack-cli": "^4.5.0",
105
- "webpack-dev-server": "^4.3.1"
40
+ "dompurify": "^2.3.3"
106
41
  }
107
42
  }
@@ -67,7 +67,7 @@ export default function DvrdButton(props: DvrdButtonProps) {
67
67
  else if (customBaseColor) color = customBaseColor;
68
68
  else if (red) {
69
69
  if (secondary) color = 'color-red-secondary';
70
- else color = 'red';
70
+ else color = 'rgb(255,0,0)';
71
71
  } else if (secondary) color = context.contrastColor;
72
72
  else color = context.baseColor;
73
73
  color = convertColor(color);
@@ -3,7 +3,7 @@
3
3
  */
4
4
  import './style/dvrdSelect.scss';
5
5
 
6
- import React, {MouseEventHandler, PureComponent} from 'react';
6
+ import React, {CSSProperties, MouseEventHandler, PureComponent} from 'react';
7
7
  import classNames from 'classnames';
8
8
  import {ChangeFunction, ErrorType, SelectItemShape} from '../util/interfaces';
9
9
  import {hasHover, stopPropagation } from '../util/controlUtil';
@@ -27,6 +27,7 @@ interface Props {
27
27
  itemsPosition: 'top' | 'bottom';
28
28
  selectOnly: boolean;
29
29
  searchValue: string;
30
+ optionsContainerHeight: number | string;
30
31
  }
31
32
 
32
33
  interface State {
@@ -84,6 +85,12 @@ export default class DvrdSelect extends PureComponent<Props, State> {
84
85
  return '';
85
86
  }
86
87
 
88
+ getItemsContainerStyle = (): CSSProperties => {
89
+ let {optionsContainerHeight} = this.props;
90
+ if(typeof optionsContainerHeight === 'number') optionsContainerHeight = `${optionsContainerHeight}px`;
91
+ return {maxHeight: optionsContainerHeight};
92
+ }
93
+
87
94
  renderLabel = () => {
88
95
  const {labelClassName, label} = this.props;
89
96
  if (!label) return null;
@@ -118,7 +125,7 @@ export default class DvrdSelect extends PureComponent<Props, State> {
118
125
  return (
119
126
  <div
120
127
  className={classNames('dvrd-select-items', (open && !disabled) && 'open', itemsPosition,
121
- itemContainerClassName)}>
128
+ itemContainerClassName)} style={this.getItemsContainerStyle()}>
122
129
  {items.filter((item) => item.label.toString().length > 0 && item.selectable !== false)
123
130
  .map((item: SelectItemShape, index: number) => (
124
131
  <label key={index} className={classNames('dvrd-select-item', itemClassName)}
@@ -23,6 +23,7 @@ interface Props {
23
23
  itemClassName?: string;
24
24
  itemsPosition: 'top' | 'bottom';
25
25
  selectOnly: boolean;
26
+ optionsContainerHeight?: number | string;
26
27
  }
27
28
 
28
29
  interface State {
@@ -35,6 +36,7 @@ export default class DvrdSelectController extends PureComponent<Props, State> {
35
36
  static defaultProps = {
36
37
  itemsPosition: 'bottom',
37
38
  selectOnly: true,
39
+ optionsContainerHeight: '15rem',
38
40
  };
39
41
 
40
42
  state: State = {
@@ -63,7 +65,7 @@ export default class DvrdSelectController extends PureComponent<Props, State> {
63
65
  render = () => {
64
66
  const {
65
67
  arrowClassName, label, error, className, labelClassName, valueClassName, itemContainerClassName,
66
- itemClassName, errorClassName, disabled, value, itemsPosition, selectOnly
68
+ itemClassName, errorClassName, disabled, value, itemsPosition, selectOnly, optionsContainerHeight
67
69
  } = this.props, {searchValue} = this.state;
68
70
  return (
69
71
  <DvrdSelect onChange={this.onChange} value={value} items={this.getItems()} disabled={disabled}
@@ -72,7 +74,8 @@ export default class DvrdSelectController extends PureComponent<Props, State> {
72
74
  labelClassName={labelClassName} className={className} error={error} label={label}
73
75
  arrowClassName={arrowClassName} itemsPosition={itemsPosition} ref={(ref: DvrdSelect) => {
74
76
  this.select = ref
75
- }} searchValue={searchValue} onChangeSearch={this.onChangeSearch} selectOnly={selectOnly}/>
77
+ }} searchValue={searchValue} onChangeSearch={this.onChangeSearch} selectOnly={selectOnly}
78
+ optionsContainerHeight={optionsContainerHeight}/>
76
79
  );
77
80
  }
78
81
  };
@@ -65,6 +65,7 @@
65
65
  flex-direction: column;
66
66
  min-width: 100%;
67
67
  border-radius: .5rem;
68
+ overflow-y: auto;
68
69
 
69
70
  .dvrd-select-item {
70
71
  padding: .75rem;
@@ -6,6 +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 { delay } from 'lodash';
9
10
 
10
11
  interface Props {
11
12
  containerClass: string;
@@ -13,6 +14,8 @@ interface Props {
13
14
  activeTime: number;
14
15
  backgroundColor?: string;
15
16
  textColor?: string;
17
+ maxSnacks?: number;
18
+ noDuplicates?: boolean;
16
19
  }
17
20
 
18
21
  interface State {
@@ -28,7 +31,7 @@ export default class SnackbarController extends React.Component<Props, State> {
28
31
  textClass: '',
29
32
  };
30
33
 
31
- state = {
34
+ state: State = {
32
35
  active: false,
33
36
  snack: null,
34
37
  config: {}
@@ -38,7 +41,12 @@ export default class SnackbarController extends React.Component<Props, State> {
38
41
  timeout: number | null = null;
39
42
 
40
43
  onAddSnack = (snack: Snack) => {
44
+ const {maxSnacks, noDuplicates} = this.props;
45
+ if (noDuplicates && this.isDuplicate(snack)) return;
41
46
  this.snackQueue.push(snack);
47
+ if (maxSnacks !== undefined)
48
+ while (this.snackQueue.length > maxSnacks - 1)
49
+ this.snackQueue.shift();
42
50
  if (!this.state.active)
43
51
  this.activate();
44
52
  };
@@ -49,10 +57,19 @@ export default class SnackbarController extends React.Component<Props, State> {
49
57
  this.deactivate();
50
58
  };
51
59
 
60
+ isDuplicate = (snack: Snack): boolean => {
61
+ return this.state.snack?.text === snack.text || this.snackQueue.pop()?.text === snack.text;
62
+ }
63
+
52
64
  deactivate = () => {
53
65
  this.setState({active: false}, () => {
54
- if (this.snackQueue.length > 0)
55
- this.timeout = window.setTimeout(this.activate, 500);
66
+ delay(() => {
67
+ // Clear snack after 200ms (css transition)
68
+ this.setState({snack: null}, () => {
69
+ if (this.snackQueue.length > 0)
70
+ this.timeout = window.setTimeout(this.activate, 500);
71
+ })
72
+ }, 200);
56
73
  })
57
74
  };
58
75
 
@@ -8,14 +8,14 @@ import React, {
8
8
  PureComponent
9
9
  } from 'react';
10
10
  import classNames from "classnames";
11
- import {ElementPosition, ErrorType, OrnamentShape } from '../util/interfaces';
11
+ import {ElementPosition, ErrorType, OrnamentShape} from '../util/interfaces';
12
12
  import AwesomeIcon from '../icon/awesomeIcon';
13
- import { directTimeout } from '../util/componentUtil';
13
+ import {directTimeout} from '../util/componentUtil';
14
14
 
15
15
  interface Props {
16
16
  onChange: ChangeEventHandler;
17
- onFocus: FocusEventHandler;
18
- onBlur: FocusEventHandler;
17
+ onFocus?: FocusEventHandler;
18
+ onBlur?: FocusEventHandler;
19
19
  onKeyDown: KeyboardEventHandler;
20
20
  onClearInput?: MouseEventHandler;
21
21
  value: string | number;
@@ -62,13 +62,15 @@ export default class DvrdInput extends PureComponent<Props, State> {
62
62
  this.input.selectionEnd = value.toString().length;
63
63
  }
64
64
  }
65
- onFocus(evt);
65
+ if (onFocus)
66
+ onFocus(evt);
66
67
  };
67
68
 
68
69
  onBlurInput = (evt: React.FocusEvent) => {
69
70
  const {disabled, onBlur} = this.props;
70
71
  if (!disabled) this.setState({active: false});
71
- onBlur(evt);
72
+ if (onBlur)
73
+ onBlur(evt);
72
74
  };
73
75
 
74
76
  hasError = (): boolean => this.props.error !== undefined && this.props.error !== null;
@@ -3,8 +3,14 @@
3
3
  */
4
4
 
5
5
  import React, {
6
- FocusEventHandler, InputHTMLAttributes, KeyboardEventHandler, MouseEventHandler, PureComponent,
7
- TextareaHTMLAttributes
6
+ FocusEventHandler,
7
+ InputHTMLAttributes,
8
+ KeyboardEventHandler,
9
+ MouseEventHandler,
10
+ TextareaHTMLAttributes,
11
+ useEffect,
12
+ useRef,
13
+ useState
8
14
  } from 'react';
9
15
  import {generateComponentId} from '../util/componentUtil';
10
16
  import {enterPressed} from '../util/controlUtil';
@@ -40,62 +46,52 @@ export interface InputControllerProps {
40
46
  step?: number;
41
47
  area?: boolean;
42
48
  noResize?: boolean;
49
+ unControlled?: boolean;
43
50
  }
44
51
 
45
- export default class DvrdInputController extends PureComponent<InputControllerProps> {
46
- private readonly id: string = generateComponentId(this.props.id);
52
+ export default function DvrdInputController(props: InputControllerProps) {
53
+ const {
54
+ disabled, onChange, onEnter, onKeyDown, inputProps, placeholder, type, max, min, step, onFocus, onBlur,
55
+ autoFocus, error, label, ornaments, className, inputClassName, labelClassName, ornamentClassName, fullWidth,
56
+ errorClassName, autoSelect, area, noResize, onClearInput, unControlled
57
+ } = props,
58
+ componentId = useRef(generateComponentId(props.id)),
59
+ [value, setValue] = useState(props.value);
47
60
 
48
- // noinspection JSUnusedGlobalSymbols
49
- public getInput = (): HTMLInputElement | null =>
50
- document.getElementById(`${this.id}-input`) as HTMLInputElement | null;
51
-
52
- onChange = (evt: React.ChangeEvent<HTMLInputElement>) => {
53
- const {value} = evt.target, {disabled, onChange} = this.props;
54
- if (!disabled) onChange(value, evt);
55
- };
56
-
57
- onKeyDown = (evt: React.KeyboardEvent) => {
58
- const {onEnter, disabled, onKeyDown} = this.props;
59
- if (!disabled) {
60
- if (enterPressed(evt) && onEnter) onEnter(evt);
61
- else if (onKeyDown) onKeyDown(evt);
62
- }
63
- };
64
-
65
- onFocus = (evt: React.FocusEvent) => {
66
- const {onFocus} = this.props;
67
- if (onFocus) onFocus(evt);
68
- };
61
+ function _onChange(evt: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) {
62
+ if (disabled) return;
63
+ const {value} = evt.target;
64
+ setValue(value);
65
+ onChange(value, evt);
66
+ }
69
67
 
70
- onBlur = (evt: React.FocusEvent) => {
71
- const {onBlur} = this.props;
72
- if (onBlur) onBlur(evt);
73
- };
68
+ function _onKeyDown(evt: React.KeyboardEvent<HTMLInputElement | HTMLTextAreaElement>) {
69
+ if (disabled) return;
70
+ if (onEnter && enterPressed(evt)) onEnter(evt);
71
+ else if (onKeyDown) onKeyDown(evt);
72
+ }
74
73
 
75
- getInputProps = (): InputHTMLAttributes<any> | undefined => {
76
- const {inputProps, placeholder, type, max, min, step} = this.props;
74
+ function getInputProps(): InputHTMLAttributes<any> | undefined {
77
75
  if (!inputProps && !placeholder && !type) return undefined;
78
76
  const props: InputHTMLAttributes<any> = inputProps ?? {};
79
- props.placeholder = placeholder ?? props.placeholder;
80
- props.type = type ?? props.type;
81
- props.max = max ?? props.max;
82
- props.min = min ?? props.min;
83
- props.step = step ?? props.step;
77
+ if (placeholder !== undefined) props.placeholder = placeholder;
78
+ if (type !== undefined) props.type = type;
79
+ if (max !== undefined) props.max = max;
80
+ if (min !== undefined) props.min = min;
81
+ if (step !== undefined) props.step = step;
84
82
  return props;
85
- };
86
-
87
- render = () => {
88
- const {
89
- value, label, ornaments, disabled, autoFocus, className, inputClassName, labelClassName,
90
- ornamentClassName, error, errorClassName, autoSelect, fullWidth, area, noResize, onClearInput
91
- } = this.props;
92
- return (
93
- <DvrdInput onChange={this.onChange} onFocus={this.onFocus} onKeyDown={this.onKeyDown} value={value}
94
- disabled={disabled} autoFocus={autoFocus} error={error} label={label} ornaments={ornaments}
95
- inputProps={this.getInputProps()} className={className} inputClassName={inputClassName}
96
- labelClassName={labelClassName} ornamentClassName={ornamentClassName} fullWidth={fullWidth}
97
- errorClassName={errorClassName} id={this.id} onBlur={this.onBlur} autoSelect={autoSelect}
98
- area={area} noResize={noResize} onClearInput={onClearInput}/>
99
- )
100
83
  }
84
+
85
+ useEffect(() => {
86
+ if(!unControlled) setValue(props.value);
87
+ }, [props.value])
88
+
89
+ return (
90
+ <DvrdInput onChange={_onChange} onFocus={onFocus} onKeyDown={_onKeyDown} value={value}
91
+ disabled={disabled} autoFocus={autoFocus} error={error} label={label} ornaments={ornaments}
92
+ inputProps={getInputProps()} className={className} inputClassName={inputClassName}
93
+ labelClassName={labelClassName} ornamentClassName={ornamentClassName} fullWidth={fullWidth}
94
+ errorClassName={errorClassName} id={componentId.current} onBlur={onBlur} autoSelect={autoSelect}
95
+ area={area} noResize={noResize} onClearInput={onClearInput}/>
96
+ );
101
97
  }
@@ -31,6 +31,7 @@ interface Props {
31
31
  wholeNumbers: boolean;
32
32
  asNumber?: boolean;
33
33
  asCurrency: boolean;
34
+ autoSelect?: boolean;
34
35
  }
35
36
 
36
37
  export default class DvrdNumberInput extends PureComponent<Props> {
@@ -156,14 +156,11 @@ export const copyClipboard = (text: string): Promise<void> => {
156
156
  return window.navigator.clipboard.writeText(text);
157
157
  }
158
158
 
159
- export const getImg = (imgName: string, fallbackExtension: string = 'png'): string => {
160
- const webpSupported = webpIsSupported(), extension = webpSupported ? 'webp' : fallbackExtension;
159
+ export function webpSupported(): boolean {
161
160
  try {
162
- return require(`../../res/img/${imgName}.${extension}`).default;
161
+ return document.createElement('canvas').toDataURL('image/webp')
162
+ .indexOf('data:image/webp') === 0;
163
163
  } catch {
164
- return require(`../../res/img/${imgName}.${fallbackExtension}`).default;
164
+ return false;
165
165
  }
166
- };
167
-
168
- const webpIsSupported = (): boolean => document.createElement('canvas').toDataURL('image/webp')
169
- .indexOf('data:image/webp') === 0;
166
+ }
@@ -26,16 +26,21 @@ let abortController: AbortController;
26
26
  let signal: AbortSignal;
27
27
  let defaultHeaders: OutgoingHttpHeaders = {};
28
28
 
29
- const getSignal = (): AbortSignal | null => {
30
- if ("AbortController" in window) {
31
- if (!abortController)
32
- abortController = new AbortController;
33
- if (!signal)
34
- signal = abortController.signal;
35
- return signal;
36
- }
37
- return null;
38
- };
29
+ // const getSignal = (): AbortSignal | null => {
30
+ // if ("AbortController" in window) {
31
+ // if (!abortController)
32
+ // abortController = new AbortController;
33
+ // if (!signal)
34
+ // signal = abortController.signal;
35
+ // return signal;
36
+ // }
37
+ // return null;
38
+ // };
39
+
40
+ function getAbortController(): AbortController | null {
41
+ if (!('AbortController' in window)) return null;
42
+ return new AbortController();
43
+ }
39
44
 
40
45
  const responseDataIsSuccess = (data: ResponseData): boolean => data.status === 'success';
41
46
 
@@ -54,7 +59,7 @@ export const cancelAllFetch = () => {
54
59
  }
55
60
  };
56
61
 
57
- export const sendFetch = (config: FetchOptions, legacySupport: boolean = false) => {
62
+ export const sendFetch = (config: FetchOptions, legacySupport: boolean = false): AbortController | null => {
58
63
  config = createFetchConfig(config, legacySupport);
59
64
  const {url, method, headers, data} = config, options: { [index: string]: any } = {
60
65
  headers,
@@ -63,7 +68,9 @@ export const sendFetch = (config: FetchOptions, legacySupport: boolean = false)
63
68
  cache: 'no-store',
64
69
  }, baseUrl = config.baseUrl || window.settings.platformUrl;
65
70
  if (!baseUrl) throw new Error('Base url is not set!');
66
- if (getSignal()) options['signal'] = getSignal();
71
+ const abortController = getAbortController();
72
+ if(abortController) options.signal = abortController.signal;
73
+ // if (getSignal()) options['signal'] = getSignal();
67
74
  options['mode'] = 'cors';
68
75
  fetch(baseUrl + url, options).then((response: Response) => {
69
76
  const {status} = response;
@@ -101,10 +108,12 @@ export const sendFetch = (config: FetchOptions, legacySupport: boolean = false)
101
108
  }
102
109
  }
103
110
  }).catch((reason: any) => {
104
- if (reason.code === DOMException.ABORT_ERR) {/*Request aborted*/} else if (config.errorCallback) {
111
+ if (reason.code === DOMException.ABORT_ERR) {/*Request aborted*/
112
+ } else if (config.errorCallback) {
105
113
  config.errorCallback(reason);
106
114
  }
107
115
  });
116
+ return abortController;
108
117
  };
109
118
 
110
119
  const createFetchConfig = (options: FetchOptions, legacySupport: boolean = true): FetchOptions => {