superdesk-ui-framework 3.1.4 → 3.1.5

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.
@@ -82,7 +82,7 @@ tags-input,
82
82
  }
83
83
 
84
84
  &:hover {
85
- cursor: pointer;
85
+ cursor: default;
86
86
  }
87
87
  .tags-input__remove-button {
88
88
  height: 1.8rem;
@@ -13,6 +13,15 @@ import {TreeSelectPill} from './TreeSelectPill';
13
13
  import {getPrefixedItemId, TreeSelectItem} from './TreeSelectItem';
14
14
  import {keyboardNavigation} from './KeyboardNavigation';
15
15
  import {WithPortal} from '../WithPortal';
16
+ import { DragDropContext, Droppable, Draggable, DropResult } from "react-beautiful-dnd";
17
+
18
+ const reorder = (list: Array<any>, startIndex: number, endIndex: number) => {
19
+ const result = Array.from(list);
20
+ const [removed] = result.splice(startIndex, 1);
21
+ result.splice(endIndex, 0, removed);
22
+
23
+ return result;
24
+ };
16
25
 
17
26
  interface IState<T> {
18
27
  value: Array<T>;
@@ -42,7 +51,10 @@ interface IPropsBase<T> extends IInputWrapper {
42
51
  singleLevelSearch?: boolean;
43
52
  placeholder?: string;
44
53
  searchPlaceholder?: string;
54
+ noResultsFoundMessage?: string;
55
+ dropdownInitiallyOpen?: boolean;
45
56
  zIndex?: number;
57
+ sortable?: boolean;
46
58
  'data-test-id'?: string;
47
59
  getLabel(item: T): string;
48
60
  getId(item: T): string;
@@ -112,7 +124,6 @@ export class TreeSelect<T> extends React.Component<IProps<T>, IState<T>> {
112
124
  this.onMouseDown = this.onMouseDown.bind(this);
113
125
  this.onKeyDown = this.onKeyDown.bind(this);
114
126
  this.onPressEsc = this.onPressEsc.bind(this);
115
-
116
127
  this.dropdownRef = React.createRef();
117
128
  this.ref = React.createRef();
118
129
  this.inputRef = React.createRef();
@@ -120,6 +131,7 @@ export class TreeSelect<T> extends React.Component<IProps<T>, IState<T>> {
120
131
  this.openDropdownRef = React.createRef();
121
132
  this.treeSelectRef = React.createRef();
122
133
  this.popperInstance = null;
134
+ this.onDragEnd = this.onDragEnd.bind(this);
123
135
  }
124
136
 
125
137
  inputFocus = () => {
@@ -185,6 +197,10 @@ export class TreeSelect<T> extends React.Component<IProps<T>, IState<T>> {
185
197
  document.addEventListener("mousedown", this.onMouseDown);
186
198
  document.addEventListener("keydown", this.onKeyDown);
187
199
  document.addEventListener("keydown", this.onPressEsc);
200
+
201
+ if (this.props.dropdownInitiallyOpen) {
202
+ this.setState({openDropdown: true});
203
+ }
188
204
  }
189
205
 
190
206
  componentWillUnmount(): void {
@@ -501,34 +517,39 @@ export class TreeSelect<T> extends React.Component<IProps<T>, IState<T>> {
501
517
  });
502
518
  }
503
519
  } else if (this.props.kind === 'asynchronous') {
504
- return this.state.options.map((item, i) => {
505
- let selectedItem = this.state.value.some((obj) =>
506
- this.props.getId(obj) === this.props.getId(item.value),
507
- );
520
+ if (this.state.options.length > 0) {
521
+ return this.state.options.map((item, i) => {
522
+ let selectedItem = this.state.value.some((obj) =>
523
+ this.props.getId(obj) === this.props.getId(item.value),
524
+ );
508
525
 
509
- return (
510
- <li
511
- key={i}
512
- className='suggestion-item suggestion-item--multi-select'
513
- onClick={(event) => {
514
- this.handleValue(event, item);
515
- }}
516
- >
517
- <button className="suggestion-item--btn" data-test-id="option">
518
- {this.props.optionTemplate
519
- ? this.props.optionTemplate(item.value)
520
- : (
521
- <span
522
- className={selectedItem ? 'suggestion-item--selected' : undefined}
523
- >
524
- {this.props.getLabel(item.value)}
525
- </span>
526
- )
527
- }
528
- </button>
529
- </li>
530
- );
531
- });
526
+ return (
527
+ <li
528
+ key={i}
529
+ className='suggestion-item suggestion-item--multi-select'
530
+ onClick={(event) => {
531
+ this.handleValue(event, item);
532
+ }}
533
+ >
534
+ <button className="suggestion-item--btn" data-test-id="option">
535
+ {this.props.optionTemplate
536
+ ? this.props.optionTemplate(item.value)
537
+ : (
538
+ <span
539
+ className={selectedItem ? 'suggestion-item--selected' : undefined}
540
+ >
541
+ {this.props.getLabel(item.value)}
542
+ </span>
543
+ )
544
+ }
545
+ </button>
546
+ </li>
547
+ );
548
+ });
549
+
550
+ } else {
551
+ return <li className="suggestion-item--nothing-found">{this.props.noResultsFoundMessage ?? 'Nothing found'}</li>;
552
+ }
532
553
  } else {
533
554
  return;
534
555
  }
@@ -591,18 +612,31 @@ export class TreeSelect<T> extends React.Component<IProps<T>, IState<T>> {
591
612
 
592
613
  if (this.props.kind === 'asynchronous') {
593
614
  if (this.state.searchFieldValue) {
594
- this.setState({
595
- loading: true,
596
- });
597
-
598
615
  this.ICancelFn = this.props.searchOptions(this.state.searchFieldValue, (items) => {
599
616
  this.setState({options: items, loading: false});
600
617
  this.popperInstance?.update();
601
618
  });
619
+ } else {
620
+ this.setState({options: this.state.firstBranchOptions, loading: false});
602
621
  }
603
622
  }
604
623
  }
605
624
 
625
+ onDragEnd(result: DropResult) {
626
+ if (!result.destination) {
627
+ return;
628
+ }
629
+
630
+ const value = reorder(
631
+ this.state.value,
632
+ result.source.index,
633
+ result.destination.index,
634
+ );
635
+ this.setState({
636
+ value: value,
637
+ });
638
+ }
639
+
606
640
  render() {
607
641
  if (this.props.preview) {
608
642
  return (
@@ -624,6 +658,43 @@ export class TreeSelect<T> extends React.Component<IProps<T>, IState<T>> {
624
658
  );
625
659
  }
626
660
 
661
+ const ListWrapper = this.props.sortable
662
+ ? ({children}: {children: React.ReactNode}) => (
663
+ <DragDropContext onDragEnd={this.onDragEnd}>
664
+ <Droppable droppableId="droppable" direction="horizontal">
665
+ {(provided, _snapshot) => (
666
+ <ul
667
+ className="tags-input__tag-list"
668
+ ref={provided.innerRef}
669
+ {...provided.droppableProps}
670
+ >
671
+ {children}
672
+ {provided.placeholder}
673
+ </ul>
674
+ )}
675
+ </Droppable>
676
+ </DragDropContext>
677
+ )
678
+ : ({children}: {children: React.ReactNode}) => <ul className="tags-input__tag-list">{children}</ul>;
679
+
680
+ const ItemWrapper = this.props.sortable
681
+ ? ({children, itemId, i}: {children: React.ReactNode, itemId: string, i: number}) => {
682
+ return (
683
+ <Draggable draggableId={itemId} index={i}>
684
+ {(provided2) => (
685
+ <div
686
+ ref={provided2.innerRef}
687
+ {...provided2.draggableProps}
688
+ {...provided2.dragHandleProps}
689
+ >
690
+ {children}
691
+ </div>
692
+ )}
693
+ </Draggable>
694
+ );
695
+ }
696
+ : ({children}: {children: React.ReactNode}) => <React.Fragment>{children}</React.Fragment>;
697
+
627
698
  return (
628
699
  <InputWrapper
629
700
  label={this.props.label}
@@ -672,7 +743,7 @@ export class TreeSelect<T> extends React.Component<IProps<T>, IState<T>> {
672
743
  </button>
673
744
  }
674
745
 
675
- <ul className="tags-input__tag-list">
746
+ <ListWrapper>
676
747
  {this.state.value.map((item, i: number) => {
677
748
  const Wrapper: React.ComponentType<{backgroundColor?: string}>
678
749
  = ({backgroundColor, children}) => (
@@ -684,25 +755,30 @@ export class TreeSelect<T> extends React.Component<IProps<T>, IState<T>> {
684
755
  backgroundColor={backgroundColor}
685
756
  onRemove={() => this.removeClick(i)}
686
757
  getBackgroundColor={this.props.getBackgroundColor}
758
+ draggable={this.props.sortable}
687
759
  >
688
760
  {children}
689
761
  </TreeSelectPill>
690
762
  );
691
763
 
764
+ const itemId = this.props.getId(item);
765
+
692
766
  return (
693
- <React.Fragment key={i}>
767
+ <ItemWrapper itemId={itemId} key={itemId} i={i}>
694
768
  {this.props.valueTemplate
695
769
  ? this.props.valueTemplate(item, Wrapper)
696
770
  : (
697
771
  <Wrapper>
698
- <span>{this.props.getLabel(item)}</span>
772
+ <span>
773
+ {this.props.getLabel(item)}
774
+ </span>
699
775
  </Wrapper>
700
776
  )
701
777
  }
702
- </React.Fragment>
778
+ </ItemWrapper>
703
779
  );
704
780
  })}
705
- </ul>
781
+ </ListWrapper>
706
782
 
707
783
  {this.state.value.length > 0
708
784
  ? (this.props.readOnly || this.props.disabled)
@@ -842,7 +918,11 @@ export class TreeSelect<T> extends React.Component<IProps<T>, IState<T>> {
842
918
  this.ICancelFn();
843
919
  }
844
920
 
845
- this.setState({searchFieldValue: event.target.value, options: []});
921
+ this.setState({
922
+ searchFieldValue: event.target.value,
923
+ options: [],
924
+ loading: true,
925
+ });
846
926
  this.popperInstance?.update();
847
927
  this.debounceFn();
848
928
  } else {
@@ -1,4 +1,6 @@
1
+ import classNames from 'classnames';
1
2
  import * as React from "react";
3
+ import {DragHandle} from '../DragHandle';
2
4
  import {Icon} from "../Icon";
3
5
  import {getTextColor} from '../Label';
4
6
 
@@ -10,19 +12,19 @@ interface IProps<T> {
10
12
  onRemove(): void;
11
13
  valueTemplate?(item: T, Wrapper: React.ElementType): React.ComponentType<T> | JSX.Element;
12
14
  getBackgroundColor?(item: T): string;
15
+ draggable?: boolean;
13
16
  }
14
17
 
15
18
  export class TreeSelectPill<T> extends React.Component<IProps<T>> {
16
19
  render() {
20
+ const classes = classNames('tags-input__tag-item tags-input__tag-item--multi-select', {
21
+ 'tags-input__tag-item--readonly': this.props.readOnly,
22
+ 'tags-input__tag-item--draggable': this.props.draggable,
23
+ });
24
+
17
25
  return (
18
26
  <li
19
- className={
20
- "tags-input__tag-item tags-input__tag-item--multi-select"
21
- + (this.props.readOnly ? ' tags-input__tag-item--readonly' : '')
22
- }
23
- onClick={() => (!this.props.readOnly && !this.props.disabled)
24
- && this.props.onRemove()
25
- }
27
+ className={classes}
26
28
  style={
27
29
  this.props.valueTemplate
28
30
  ? {backgroundColor: this.props.backgroundColor}
@@ -31,6 +33,9 @@ export class TreeSelectPill<T> extends React.Component<IProps<T>> {
31
33
  }
32
34
  data-test-id="item"
33
35
  >
36
+ {this.props.draggable && (
37
+ <DragHandle blank={true} dotsInRow='3' dotRows='4' />
38
+ )}
34
39
  <span
35
40
  className="tags-input__helper-box"
36
41
  style={{
@@ -43,9 +48,15 @@ export class TreeSelectPill<T> extends React.Component<IProps<T>> {
43
48
  {this.props.children}
44
49
 
45
50
  {!this.props.readOnly
46
- && <span className="tags-input__remove-button" data-test-id="remove">
51
+ && <button
52
+ className="tags-input__remove-button"
53
+ data-test-id="remove"
54
+ onClick={() => (!this.props.readOnly && !this.props.disabled)
55
+ && this.props.onRemove()
56
+ }
57
+ >
47
58
  <Icon name="close-small"></Icon>
48
- </span>
59
+ </button>
49
60
  }
50
61
  </span>
51
62
  </li>