@dvrd/dvr-controls 1.0.44 → 1.0.46

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.
@@ -0,0 +1,389 @@
1
+ /*
2
+ * Copyright (c) 2024. Dave van Rijn Development
3
+ */
4
+ import './style/dvrdSelect.scss';
5
+
6
+ import React, {MouseEventHandler, ReactElement, useEffect, useMemo, useRef, useState} from 'react';
7
+ import classNames from 'classnames';
8
+ import {ChangeFunction, ErrorType, SelectItemShape} from '../util/interfaces';
9
+ import {hasHover, stopPropagation} from '../util/controlUtil';
10
+ import AwesomeIcon from '../icon/awesomeIcon';
11
+ import delay from 'lodash.delay';
12
+
13
+ interface Props {
14
+ onChange: (selected: Array<string | number>) => MouseEventHandler;
15
+ onChangeSearch: ChangeFunction;
16
+ selected: Array<string | number>;
17
+ items: SelectItemShape[];
18
+ disabled?: boolean;
19
+ error?: ErrorType;
20
+ label?: string;
21
+ className?: string;
22
+ labelClassName?: string;
23
+ valueClassName?: string;
24
+ arrowClassName?: string;
25
+ errorClassName?: string;
26
+ itemContainerClassName?: string;
27
+ itemClassName?: string;
28
+ itemsPosition: 'top' | 'bottom';
29
+ selectOnly: boolean;
30
+ searchValue: string;
31
+ optionsContainerHeight: number | string;
32
+ }
33
+
34
+ // interface State {
35
+ // open: boolean;
36
+ // searchFocused: boolean;
37
+ // }
38
+
39
+ const transitionDuration = 200; // ms
40
+
41
+ export default function DvrdMultiSelect(props: Props) {
42
+ const {
43
+ error, disabled, selected, onChange, onChangeSearch, items, optionsContainerHeight, label, labelClassName,
44
+ selectOnly, valueClassName, searchValue, arrowClassName, itemClassName, itemsPosition, itemContainerClassName,
45
+ errorClassName, className
46
+ } = props;
47
+ const [open, setOpen] = useState(false);
48
+ const [searchFocused, setSearchFocused] = useState(false);
49
+ const container = useRef<HTMLDivElement>(null);
50
+ const hasError = useMemo(() => !!error, [error]);
51
+ const itemsContainerStyle = useMemo(() => {
52
+ let maxHeight: string | number = optionsContainerHeight;
53
+ if (typeof maxHeight === 'number') maxHeight = `${maxHeight}px`;
54
+ return {maxHeight: maxHeight};
55
+ }, [optionsContainerHeight])
56
+
57
+ function onClickElement(evt: React.MouseEvent) {
58
+ stopPropagation(evt);
59
+ if (disabled) return;
60
+ setOpen(!open);
61
+ }
62
+
63
+ function onClickItem(_value: string | number) {
64
+ return function (evt: React.MouseEvent) {
65
+ stopPropagation(evt);
66
+ if (selected.includes(_value))
67
+ onChange(selected.filter((value: string | number) => value !== _value))(evt);
68
+ else onChange(selected.concat(_value))(evt);
69
+ }
70
+ }
71
+
72
+ function onFocusInput() {
73
+ setSearchFocused(true);
74
+ setOpen(true);
75
+ }
76
+
77
+ function onBlurInput() {
78
+ delay(() => {
79
+ setSearchFocused(false);
80
+ onChangeSearch({target: {value: ''}});
81
+ }, transitionDuration);
82
+ }
83
+
84
+ function getLongestValue(): string {
85
+ let longest: string = '';
86
+ for (const item of items) {
87
+ const stringLabel = item.label.toString();
88
+ if (stringLabel.length > longest.length)
89
+ longest = stringLabel;
90
+ }
91
+ return longest;
92
+ }
93
+
94
+ function getValueLabel(): string {
95
+ let valueLabel = '';
96
+ let extra = 0;
97
+ for (const item of items) {
98
+ if (selected.includes(item.value)) {
99
+ if (valueLabel.length) extra++;
100
+ else valueLabel = getItemLabel(item);
101
+ }
102
+ }
103
+ if (!valueLabel.length) return '';
104
+ if (extra) valueLabel += ` +${extra}`
105
+ return valueLabel;
106
+ }
107
+
108
+ function getItemLabel(item: SelectItemShape): string {
109
+ const {label} = item;
110
+ if (['string', 'number'].includes(typeof label)) return label.toString();
111
+ else if (!!item.valueLabel) return item.valueLabel;
112
+ return item.value.toString();
113
+ }
114
+
115
+ function renderLabel() {
116
+ if (!label) return null;
117
+ return <label className={classNames('dvrd-select-label', labelClassName)}
118
+ onClick={onClickElement}>{label}</label>
119
+ }
120
+
121
+ function renderValue() {
122
+ const valueLabel = getValueLabel();
123
+ if (selectOnly)
124
+ return (
125
+ <div style={{display: 'flex', flexDirection: 'column'}}>
126
+ <label className={classNames('dvrd-select-value', valueClassName)}
127
+ onClick={onClickElement}>{valueLabel}</label>
128
+ <label style={{height: 0, visibility: 'hidden'}}>{getLongestValue()}</label>
129
+ </div>
130
+ );
131
+ const value = searchFocused ? searchValue : valueLabel;
132
+ return <input className='dvrd-select-search' value={value} onChange={onChangeSearch} disabled={disabled}
133
+ onFocus={onFocusInput} onBlur={onBlurInput} onClick={stopPropagation}/>
134
+ }
135
+
136
+ function renderArrow() {
137
+ return <AwesomeIcon name='chevron-down' onClick={onClickElement}
138
+ className={classNames('dvrd-select-arrow', open && 'open', arrowClassName)}/>;
139
+ }
140
+
141
+ function renderItems() {
142
+ const _items = items.filter((item: SelectItemShape) => {
143
+ if (item.selectable === false) return false;
144
+ if (typeof item.label === 'string') return item.label.length > 0;
145
+ return true;
146
+ });
147
+ return (
148
+ <div
149
+ className={classNames('dvrd-select-items', (open && !disabled) && 'open', itemsPosition,
150
+ itemContainerClassName)} style={itemsContainerStyle}>
151
+ {_items.map(renderItem)}
152
+ </div>
153
+ )
154
+ }
155
+
156
+ function renderItem(item: SelectItemShape, index: number) {
157
+ const {value, selectableItemLabel} = item;
158
+ const isSelected = selected.includes(value);
159
+ const label = selectableItemLabel ?? item.label;
160
+ if (['number', 'string'].includes(typeof label)) return (
161
+ <label key={index} className={classNames('dvrd-select-item', isSelected && 'selected', itemClassName)}
162
+ onClick={onClickItem(value)}>{label}</label>
163
+ );
164
+ const labelElement: ReactElement = label as ReactElement;
165
+ const onClick = (evt: React.MouseEvent) => {
166
+ onClickItem(value)(evt);
167
+ labelElement.props.onClick?.(evt);
168
+ };
169
+ const className = classNames(labelElement.props.className, 'dvrd-select-item', isSelected && 'selected', itemClassName);
170
+ return React.cloneElement(labelElement, {...labelElement.props, onClick, className, key: index});
171
+ }
172
+
173
+ function renderError() {
174
+ if (!hasError) return null;
175
+ return <label className={classNames('dvrd-select-error', errorClassName)}>{error}</label>
176
+ }
177
+
178
+ function addClickListener() {
179
+ document.addEventListener('click', clickListener);
180
+ }
181
+
182
+ function removeClickListener() {
183
+ document.removeEventListener('click', clickListener);
184
+ }
185
+
186
+ function clickListener() {
187
+ if (!hasHover(container.current)) setOpen(false);
188
+ }
189
+
190
+ useEffect(() => {
191
+ if (open) addClickListener();
192
+ else removeClickListener();
193
+ return function () {
194
+ removeClickListener();
195
+ }
196
+ }, [open]);
197
+
198
+ return (
199
+ <div className={classNames('dvrd-select-container', 'multi', disabled && 'disabled', open && 'open',
200
+ hasError && 'error', className)} ref={container} onClick={onClickElement}>
201
+ {renderLabel()}
202
+ <div className={classNames('content-container', !selectOnly && 'search')}>
203
+ {renderValue()}
204
+ {renderArrow()}
205
+ </div>
206
+ {renderItems()}
207
+ {renderError()}
208
+ </div>
209
+ )
210
+ }
211
+
212
+ // class DvrdSelect extends PureComponent<Props, State> {
213
+ // private container: HTMLDivElement;
214
+ // state: State = {
215
+ // open: false,
216
+ // searchFocused: false,
217
+ // };
218
+ //
219
+ // hasError = (): boolean => this.props.error !== undefined && this.props.error !== null;
220
+ //
221
+ // onClickElement = (evt: React.MouseEvent) => {
222
+ // evt.stopPropagation();
223
+ // if (!this.props.disabled)
224
+ // this.setState({open: !this.state.open});
225
+ // };
226
+ //
227
+ // onClickItem = (value: string | number) => (evt: React.MouseEvent) => {
228
+ // evt.stopPropagation();
229
+ // this.props.onChange(value)(evt);
230
+ // this.setState({open: false});
231
+ // };
232
+ //
233
+ // onFocusInput = () => {
234
+ // this.setState({searchFocused: true, open: true});
235
+ // };
236
+ //
237
+ // onBlurInput = () => {
238
+ // window.setTimeout(() => {
239
+ // this.setState({searchFocused: false});
240
+ // this.props.onChangeSearch({target: {value: ''}});
241
+ // }, 200);
242
+ // };
243
+ //
244
+ // getLongestValue = (): string => {
245
+ // const {items} = this.props;
246
+ // let longest: string = '';
247
+ // for (const item of items) {
248
+ // if (item.label.toString().length > longest.length) {
249
+ // longest = item.label.toString();
250
+ // }
251
+ // }
252
+ // return longest;
253
+ // };
254
+ //
255
+ // getValueLabel = (): string | number => {
256
+ // const {value, items} = this.props;
257
+ // for (const item of items) {
258
+ // if (item.value === value) return this.getItemLabel(item);
259
+ // }
260
+ // return '';
261
+ // }
262
+ //
263
+ // getItemLabel = (item: SelectItemShape): string | number => {
264
+ // const {label} = item;
265
+ // if (['string', 'number'].includes(typeof label)) return label as string | number;
266
+ // else if (!!item.valueLabel) return item.valueLabel;
267
+ // return item.value;
268
+ // }
269
+ //
270
+ // getItemsContainerStyle = (): CSSProperties => {
271
+ // let {optionsContainerHeight} = this.props;
272
+ // if (typeof optionsContainerHeight === 'number') optionsContainerHeight = `${optionsContainerHeight}px`;
273
+ // return {maxHeight: optionsContainerHeight};
274
+ // }
275
+ //
276
+ // renderLabel = () => {
277
+ // const {labelClassName, label} = this.props;
278
+ // if (!label) return null;
279
+ // return <label className={classNames('dvrd-select-label', labelClassName)}
280
+ // onClick={this.onClickElement}>{label}</label>
281
+ // };
282
+ //
283
+ // renderValue = () => {
284
+ // const {valueClassName, selectOnly, searchValue, onChangeSearch, disabled} = this.props,
285
+ // {searchFocused} = this.state, valueLabel = this.getValueLabel();
286
+ // if (selectOnly)
287
+ // return (
288
+ // <div style={{display: 'flex', flexDirection: 'column'}}>
289
+ // <label className={classNames('dvrd-select-value', valueClassName)}
290
+ // onClick={this.onClickElement}>{valueLabel}</label>
291
+ // <label style={{height: 0, visibility: 'hidden'}}>{this.getLongestValue()}</label>
292
+ // </div>
293
+ // );
294
+ // const value = searchFocused ? searchValue : valueLabel;
295
+ // return <input className='dvrd-select-search' value={value} onChange={onChangeSearch} disabled={disabled}
296
+ // onFocus={this.onFocusInput} onBlur={this.onBlurInput} onClick={stopPropagation}/>
297
+ // };
298
+ //
299
+ // renderArrow = () => {
300
+ // const {arrowClassName} = this.props, {open} = this.state;
301
+ // return <AwesomeIcon name='chevron-down' onClick={this.onClickElement}
302
+ // className={classNames('dvrd-select-arrow', open && 'open', arrowClassName)}/>;
303
+ // };
304
+ //
305
+ // renderItems = () => {
306
+ // const {disabled, itemContainerClassName, itemsPosition} = this.props, {open} = this.state;
307
+ // const items = this.props.items.filter((item: SelectItemShape) => {
308
+ // if (item.selectable === false) return false;
309
+ // if (typeof item.label === 'string') return item.label.length > 0;
310
+ // return true;
311
+ // })
312
+ // return (
313
+ // <div
314
+ // className={classNames('dvrd-select-items', (open && !disabled) && 'open', itemsPosition,
315
+ // itemContainerClassName)} style={this.getItemsContainerStyle()}>
316
+ // {items.map(this.renderItem)}
317
+ // </div>
318
+ // )
319
+ // };
320
+ //
321
+ // renderItem = (item: SelectItemShape, index: number) => {
322
+ // const {value, selectableItemLabel} = item;
323
+ // const label = selectableItemLabel ?? item.label;
324
+ // const {itemClassName} = this.props;
325
+ // if (['number', 'string'].includes(typeof label)) return (
326
+ // <label key={index} className={classNames('dvrd-select-item', itemClassName)}
327
+ // onClick={this.onClickItem(value)}>{label}</label>
328
+ // );
329
+ // const labelElement: ReactElement = label as ReactElement;
330
+ // const onClick = (evt: React.MouseEvent) => {
331
+ // this.onClickItem(value)(evt);
332
+ // labelElement.props.onClick?.(evt);
333
+ // };
334
+ // const className = classNames(labelElement.props.className, 'dvrd-select-item', itemClassName);
335
+ // return React.cloneElement(labelElement, Object.assign({}, labelElement.props, {
336
+ // onClick,
337
+ // className,
338
+ // key: index
339
+ // }));
340
+ // }
341
+ //
342
+ // renderError = () => {
343
+ // if (!this.hasError()) return null;
344
+ // const {error, errorClassName} = this.props;
345
+ // return <label className={classNames('dvrd-select-error', errorClassName)}>{error}</label>
346
+ // };
347
+ //
348
+ // addClickListener = () => {
349
+ // document.addEventListener('click', this.clickListener);
350
+ // };
351
+ //
352
+ // removeClickListener = () => {
353
+ // document.removeEventListener('click', this.clickListener);
354
+ // }
355
+ //
356
+ // clickListener = () => {
357
+ // if (!hasHover(this.container)) this.setState({open: false});
358
+ // };
359
+ //
360
+ // componentDidUpdate = (props: Props, prevState: State) => {
361
+ // const {open} = this.state, prevOpen = prevState.open;
362
+ // if (open && !prevOpen) this.addClickListener();
363
+ // else if (!open && prevOpen) this.removeClickListener();
364
+ // };
365
+ //
366
+ // componentWillUnmount = () => {
367
+ // this.removeClickListener();
368
+ // };
369
+ //
370
+ // render = () => {
371
+ // const {className, disabled, selectOnly} = this.props, {open} = this.state;
372
+ // return (
373
+ // <div
374
+ // className={classNames('dvrd-select-container', disabled && 'disabled', open && 'open',
375
+ // this.hasError() && 'error', className)}
376
+ // ref={(ref: HTMLDivElement) => {
377
+ // this.container = ref
378
+ // }} onClick={this.onClickElement}>
379
+ // {this.renderLabel()}
380
+ // <div className={classNames('content-container', !selectOnly && 'search')}>
381
+ // {this.renderValue()}
382
+ // {this.renderArrow()}
383
+ // </div>
384
+ // {this.renderItems()}
385
+ // {this.renderError()}
386
+ // </div>
387
+ // )
388
+ // };
389
+ // };
@@ -2,14 +2,17 @@
2
2
  * Copyright (c) 2021. Dave van Rijn Development
3
3
  */
4
4
 
5
- import React, {PureComponent} from 'react';
5
+ import React, {useEffect, useMemo, useState} from 'react';
6
6
  import DvrdSelect from "./dvrdSelect";
7
7
  import {ChangeFunction, ErrorType, SelectItemShape} from "../util/interfaces";
8
8
  import {stringContains} from '../util/controlUtil';
9
+ import DvrdMultiSelect from "./dvrdMultiSelect";
10
+
11
+ type ValueType = string | number | Array<string | number>;
9
12
 
10
13
  interface Props {
11
- onChange: ChangeFunction;
12
- value: string | number;
14
+ onChange: ChangeFunction<ValueType>;
15
+ value: string | number | Array<string | number>;
13
16
  items: SelectItemShape[];
14
17
  disabled?: boolean;
15
18
  label?: string;
@@ -21,66 +24,124 @@ interface Props {
21
24
  errorClassName?: string;
22
25
  itemContainerClassName?: string;
23
26
  itemClassName?: string;
24
- itemsPosition: 'top' | 'bottom';
25
- selectOnly: boolean;
26
- optionsContainerHeight: number | string;
27
- unControlled?: boolean;
28
- }
29
-
30
- interface State {
31
- searchValue: string;
32
- value: string | number;
27
+ itemsPosition?: 'top' | 'bottom';
28
+ selectOnly?: false;
29
+ optionsContainerHeight?: number | string;
30
+ unControlled?: true;
31
+ multi?: true;
33
32
  }
34
33
 
35
- export default class DvrdSelectController extends PureComponent<Props, State> {
36
- private select: DvrdSelect;
34
+ // interface State {
35
+ // searchValue: string;
36
+ // value: string | number | Array<string | number>;
37
+ // }
37
38
 
38
- static defaultProps = {
39
- itemsPosition: 'bottom',
40
- selectOnly: true,
41
- optionsContainerHeight: '15rem',
42
- };
39
+ export default function DvrdSelectController(props: Props) {
40
+ const {
41
+ value, multi, unControlled, onChange, items, error, className, labelClassName, valueClassName,
42
+ itemContainerClassName, arrowClassName, errorClassName, itemClassName, label, disabled
43
+ } = props;
44
+ const [search, setSearch] = useState('');
45
+ const [internalValue, setInternalValue] = useState<ValueType>('');
46
+ const _items = useMemo(() => {
47
+ if (!search) return items;
48
+ return items.filter((item) => stringContains(item.label.toString(), search));
49
+ }, [items, search]);
50
+ const _value = useMemo(() => unControlled ? internalValue : value,
51
+ [unControlled, internalValue, value]);
43
52
 
44
- state: State = {
45
- searchValue: '',
46
- value: this.props.value,
47
- };
48
-
49
- onChange = (value: string | number) => () => {
50
- this.props.onChange(value);
51
- this.setState({value});
52
- };
53
+ function _onChange(value: ValueType) {
54
+ return function () {
55
+ onChange(value);
56
+ setInternalValue(value);
57
+ }
58
+ }
53
59
 
54
- onChangeSearch = (evt: React.ChangeEvent<HTMLInputElement>) => {
55
- const {value} = evt.target;
56
- this.setState({searchValue: value});
57
- };
60
+ function onChangeSearch(evt: React.ChangeEvent<HTMLInputElement>) {
61
+ setSearch(evt.target.value);
62
+ }
58
63
 
59
- onOpen = (evt: React.MouseEvent) => {
60
- if (this.select) this.select.onClickElement(evt);
61
- };
64
+ useEffect(() => {
65
+ if (multi && !Array.isArray(value)) throw new TypeError('Value must be an array in multi mode');
66
+ else if (!multi && Array.isArray(value)) throw new TypeError('Value must be a string or number in single mode');
67
+ if (!unControlled && value !== internalValue) setInternalValue(value);
68
+ }, [value, multi, unControlled]);
62
69
 
63
- getItems = () => {
64
- const items = this.props.items.slice(), {searchValue} = this.state;
65
- if (!searchValue) return items;
66
- return items.filter((item) => stringContains(item.label.toString(), searchValue))
67
- }
70
+ const itemsPosition = props.itemsPosition ?? 'bottom';
71
+ const optionsContainerHeight = props.optionsContainerHeight ?? '15rem';
72
+ const selectOnly = props.selectOnly !== false;
73
+ if (multi) return (
74
+ <DvrdMultiSelect onChange={_onChange} onChangeSearch={onChangeSearch}
75
+ selected={_value as Array<string | number>} items={_items} itemsPosition={itemsPosition}
76
+ selectOnly={selectOnly} searchValue={search} optionsContainerHeight={optionsContainerHeight}
77
+ className={className} error={error} label={label} arrowClassName={arrowClassName}
78
+ errorClassName={errorClassName} itemClassName={itemClassName}
79
+ itemContainerClassName={itemContainerClassName} valueClassName={valueClassName}
80
+ labelClassName={labelClassName} disabled={disabled}/>
81
+ );
82
+ return (
83
+ <DvrdSelect onChange={_onChange} onChangeSearch={onChangeSearch} value={_value as number | string}
84
+ items={_items} itemsPosition={itemsPosition} selectOnly={selectOnly} searchValue={search}
85
+ optionsContainerHeight={optionsContainerHeight} className={className} error={error} label={label}
86
+ arrowClassName={arrowClassName} errorClassName={errorClassName} itemClassName={itemClassName}
87
+ itemContainerClassName={itemContainerClassName} valueClassName={valueClassName}
88
+ labelClassName={labelClassName} disabled={disabled}/>
89
+ );
90
+ }
68
91
 
69
- render = () => {
70
- const {
71
- arrowClassName, label, error, className, labelClassName, valueClassName, itemContainerClassName,
72
- itemClassName, errorClassName, disabled, itemsPosition, selectOnly, optionsContainerHeight, unControlled
73
- } = this.props, {searchValue} = this.state;
74
- const value = !!unControlled ? this.state.value : this.props.value;
75
- return (
76
- <DvrdSelect onChange={this.onChange} value={value} items={this.getItems()} disabled={disabled}
77
- errorClassName={errorClassName} itemClassName={itemClassName}
78
- itemContainerClassName={itemContainerClassName} valueClassName={valueClassName}
79
- labelClassName={labelClassName} className={className} error={error} label={label}
80
- arrowClassName={arrowClassName} itemsPosition={itemsPosition} ref={(ref: DvrdSelect) => {
81
- this.select = ref
82
- }} searchValue={searchValue} onChangeSearch={this.onChangeSearch} selectOnly={selectOnly}
83
- optionsContainerHeight={optionsContainerHeight}/>
84
- );
85
- }
86
- };
92
+ // class _DvrdSelectController extends PureComponent<Props, State> {
93
+ // private select: DvrdSelect;
94
+ //
95
+ // static defaultProps = {
96
+ // itemsPosition: 'bottom',
97
+ // selectOnly: true,
98
+ // optionsContainerHeight: '15rem',
99
+ // };
100
+ //
101
+ // state: State = {
102
+ // searchValue: '',
103
+ // value: this.props.value,
104
+ // };
105
+ //
106
+ // onChange = (value: string | number) => () => {
107
+ // this.props.onChange(value);
108
+ // this.setState({value});
109
+ // };
110
+ //
111
+ // onChangeSearch = (evt: React.ChangeEvent<HTMLInputElement>) => {
112
+ // const {value} = evt.target;
113
+ // this.setState({searchValue: value});
114
+ // };
115
+ //
116
+ // onOpen = (evt: React.MouseEvent) => {
117
+ // if (this.select) this.select.onClickElement(evt);
118
+ // };
119
+ //
120
+ // getItems = () => {
121
+ // const items = this.props.items.slice(), {searchValue} = this.state;
122
+ // if (!searchValue) return items;
123
+ // return items.filter((item) => stringContains(item.label.toString(), searchValue))
124
+ // }
125
+ //
126
+ // render = () => {
127
+ // const {
128
+ // arrowClassName, label, error, className, labelClassName, valueClassName, itemContainerClassName,
129
+ // itemClassName, errorClassName, disabled, itemsPosition, selectOnly, optionsContainerHeight, unControlled,
130
+ // multi
131
+ // } = this.props, {searchValue} = this.state;
132
+ // const value = !!unControlled ? this.state.value : this.props.value;
133
+ // if (!multi && Array.isArray(value)) throw new TypeError('Value cannot be an array in single mode');
134
+ // else if (multi && !Array.isArray(value)) throw new TypeError('Value must be an array in multi mode');
135
+ // return (
136
+ // <DvrdSelect onChange={this.onChange} value={value as string | number} items={this.getItems()}
137
+ // disabled={disabled}
138
+ // errorClassName={errorClassName} itemClassName={itemClassName}
139
+ // itemContainerClassName={itemContainerClassName} valueClassName={valueClassName}
140
+ // labelClassName={labelClassName} className={className} error={error} label={label}
141
+ // arrowClassName={arrowClassName} itemsPosition={itemsPosition} ref={(ref: DvrdSelect) => {
142
+ // this.select = ref
143
+ // }} searchValue={searchValue} onChangeSearch={this.onChangeSearch} selectOnly={selectOnly}
144
+ // optionsContainerHeight={optionsContainerHeight}/>
145
+ // );
146
+ // }
147
+ // };
@@ -4,7 +4,7 @@
4
4
  import './style/sidebarMenu.scss';
5
5
 
6
6
  import React, {MouseEventHandler, ReactNode, useContext, useEffect, useRef, useState} from 'react';
7
- import {useLocation} from 'react-router-dom';
7
+ import {NavLink, useLocation} from 'react-router-dom';
8
8
  import {SidebarItem, SideMenuMode} from "../util/interfaces";
9
9
  import classNames from 'classnames';
10
10
  import {AwesomeIcon, generateComponentId, isAbsoluteLink} from "../../../index";
@@ -82,21 +82,33 @@ export default function SidebarMenu(props: Props) {
82
82
 
83
83
  function renderItem(isChild: boolean = false) {
84
84
  return function (item: SidebarItem) {
85
- const {className, icon, id, label, children} = item,
85
+ const {className, icon, id, label, children, asNavLink} = item,
86
86
  isActive = itemHasActiveId(item, activeItem),
87
87
  cls = classNames(className, mode === SideMenuMode.COMPACT ? 'side-bar-item' : 'side-bar-item-full',
88
88
  isChild && 'child', children !== undefined && 'with-children');
89
- return (
90
- <div key={id} className={cls} onClick={_onClickItem(item)} id={id}>
91
- {renderIcon(isActive, isChild, label, icon)}
89
+
90
+ const content = (
91
+ <>
92
+ {renderIcon(isChild, label, icon)}
92
93
  {mode === SideMenuMode.COMPACT && <div className='active-indicator'/>}
93
- <span className={classNames('item-label', isActive && 'active')}>{label}</span>
94
+ <span className='item-label'>{label}</span>
94
95
  {children !== undefined && (
95
96
  <>
96
97
  {isActive && <div className='line'/>}
97
98
  {renderChildren(isActive)(children)}
98
99
  </>
99
100
  )}
101
+ </>
102
+ );
103
+
104
+ if (asNavLink) return (
105
+ <NavLink key={id} to={item.route as string} className={cls} id={id}>
106
+ {content}
107
+ </NavLink>
108
+ );
109
+ return (
110
+ <div key={id} className={classNames(cls, isActive && 'active')} onClick={_onClickItem(item)} id={id}>
111
+ {content}
100
112
  </div>
101
113
  )
102
114
  }
@@ -113,8 +125,8 @@ export default function SidebarMenu(props: Props) {
113
125
  }
114
126
  }
115
127
 
116
- function renderIcon(isActive: boolean, isChild: boolean, label: string, icon?: IconName | ReactNode): ReactNode {
117
- const className = classNames('item-icon', isActive && 'active');
128
+ function renderIcon(isChild: boolean, label: string, icon?: IconName | ReactNode): ReactNode {
129
+ const className = 'item-icon'
118
130
  if (icon) {
119
131
  if (typeof icon === 'string') return <AwesomeIcon name={icon as IconName} className={className}/>;
120
132
  else if (React.isValidElement(icon))