superdesk-ui-framework 2.4.14 → 2.4.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/app/index.js +1 -0
  2. package/app/scripts/modals.js +23 -9
  3. package/app/styles/_buttons.scss +17 -0
  4. package/app/styles/_modals.scss +23 -17
  5. package/app/styles/_tooltips.scss +66 -0
  6. package/app/styles/app.scss +1 -0
  7. package/app/styles/components/_sd-toaster.scss +6 -5
  8. package/app/styles/form-elements/_inputs.scss +1 -0
  9. package/app/styles/form-elements/_select-grid.scss +77 -0
  10. package/app/styles/pr-superdesk-theme.scss +3 -1
  11. package/app/styles/primereact/_pr-menu.scss +38 -0
  12. package/app/styles/primereact/_pr-skeleton.scss +35 -0
  13. package/app/styles/variables/_colors.scss +30 -18
  14. package/app-typescript/components/Button.tsx +2 -2
  15. package/app-typescript/components/DropdownFirst.tsx +2 -2
  16. package/app-typescript/components/IconPicker.tsx +277 -0
  17. package/app-typescript/components/ListItemLoader.tsx +30 -0
  18. package/app-typescript/components/Menu.tsx +149 -0
  19. package/app-typescript/components/PropsList.tsx +2 -2
  20. package/app-typescript/components/Radio.tsx +19 -10
  21. package/app-typescript/components/SelectGrid.tsx +233 -0
  22. package/app-typescript/components/SelectWithTemplate.tsx +0 -2
  23. package/app-typescript/components/Skeleton.tsx +48 -0
  24. package/app-typescript/components/Toast.tsx +31 -5
  25. package/app-typescript/components/ToastMessage.tsx +9 -16
  26. package/app-typescript/components/ToastText.tsx +2 -4
  27. package/app-typescript/components/ToastWrapper.tsx +4 -4
  28. package/app-typescript/components/Togglebox.tsx +108 -0
  29. package/app-typescript/components/Tooltip.tsx +25 -1
  30. package/app-typescript/index.ts +8 -0
  31. package/dist/components/modals.html +180 -4
  32. package/dist/examples.bundle.css +52 -36
  33. package/dist/examples.bundle.js +5527 -2989
  34. package/dist/react/Alerts.tsx +4 -4
  35. package/dist/react/Autocomplete.tsx +17 -17
  36. package/dist/react/Badges.tsx +4 -4
  37. package/dist/react/BigIconFont.tsx +3 -3
  38. package/dist/react/ButtonGroups.tsx +6 -6
  39. package/dist/react/Buttons.tsx +11 -11
  40. package/dist/react/Carousel.tsx +15 -15
  41. package/dist/react/Checkboxs.tsx +10 -10
  42. package/dist/react/DatePicker.tsx +6 -6
  43. package/dist/react/Dropdowns.tsx +15 -15
  44. package/dist/react/EmptyStates.tsx +4 -4
  45. package/dist/react/GridItem.tsx +8 -8
  46. package/dist/react/GridList.tsx +3 -3
  47. package/dist/react/IconButtons.tsx +3 -3
  48. package/dist/react/IconFont.tsx +10 -9
  49. package/dist/react/IconLabels.tsx +4 -4
  50. package/dist/react/IconPicker.tsx +65 -0
  51. package/dist/react/Index.tsx +27 -2
  52. package/dist/react/Inputs.tsx +9 -9
  53. package/dist/react/Labels.tsx +6 -6
  54. package/dist/react/LeftNavigations.tsx +6 -6
  55. package/dist/react/ListItems.tsx +34 -0
  56. package/dist/react/Menu.tsx +159 -0
  57. package/dist/react/Modal.tsx +9 -9
  58. package/dist/react/NavButtons.tsx +7 -7
  59. package/dist/react/Popover.tsx +5 -5
  60. package/dist/react/Radios.tsx +29 -29
  61. package/dist/react/SelectGrid.tsx +121 -0
  62. package/dist/react/Selects.tsx +8 -8
  63. package/dist/react/Switch.tsx +5 -5
  64. package/dist/react/Tabs.tsx +12 -12
  65. package/dist/react/TimePicker.tsx +4 -4
  66. package/dist/react/Toasts.tsx +44 -56
  67. package/dist/react/Togglebox.tsx +51 -0
  68. package/dist/react/Tooltips.tsx +48 -4
  69. package/dist/superdesk-ui.bundle.css +3771 -94
  70. package/dist/superdesk-ui.bundle.js +4468 -2037
  71. package/dist/vendor.bundle.js +53380 -53380
  72. package/examples/pages/components/modals.html +180 -4
  73. package/examples/pages/react/Alerts.tsx +4 -4
  74. package/examples/pages/react/Autocomplete.tsx +17 -17
  75. package/examples/pages/react/Badges.tsx +4 -4
  76. package/examples/pages/react/BigIconFont.tsx +3 -3
  77. package/examples/pages/react/ButtonGroups.tsx +6 -6
  78. package/examples/pages/react/Buttons.tsx +11 -11
  79. package/examples/pages/react/Carousel.tsx +15 -15
  80. package/examples/pages/react/Checkboxs.tsx +10 -10
  81. package/examples/pages/react/DatePicker.tsx +6 -6
  82. package/examples/pages/react/Dropdowns.tsx +15 -15
  83. package/examples/pages/react/EmptyStates.tsx +4 -4
  84. package/examples/pages/react/GridItem.tsx +8 -8
  85. package/examples/pages/react/GridList.tsx +3 -3
  86. package/examples/pages/react/IconButtons.tsx +3 -3
  87. package/examples/pages/react/IconFont.tsx +10 -9
  88. package/examples/pages/react/IconLabels.tsx +4 -4
  89. package/examples/pages/react/IconPicker.tsx +65 -0
  90. package/examples/pages/react/Index.tsx +27 -2
  91. package/examples/pages/react/Inputs.tsx +9 -9
  92. package/examples/pages/react/Labels.tsx +6 -6
  93. package/examples/pages/react/LeftNavigations.tsx +6 -6
  94. package/examples/pages/react/ListItems.tsx +34 -0
  95. package/examples/pages/react/Menu.tsx +159 -0
  96. package/examples/pages/react/Modal.tsx +9 -9
  97. package/examples/pages/react/NavButtons.tsx +7 -7
  98. package/examples/pages/react/Popover.tsx +5 -5
  99. package/examples/pages/react/Radios.tsx +29 -29
  100. package/examples/pages/react/SelectGrid.tsx +121 -0
  101. package/examples/pages/react/Selects.tsx +8 -8
  102. package/examples/pages/react/Switch.tsx +5 -5
  103. package/examples/pages/react/Tabs.tsx +12 -12
  104. package/examples/pages/react/TimePicker.tsx +4 -4
  105. package/examples/pages/react/Toasts.tsx +44 -56
  106. package/examples/pages/react/Togglebox.tsx +51 -0
  107. package/examples/pages/react/Tooltips.tsx +48 -4
  108. package/package.json +2 -2
  109. package/patches/@superdesk+primereact+5.0.2-4.patch +13 -0
  110. package/react/components/Button.d.ts +1 -1
  111. package/react/components/Button.js +2 -1
  112. package/react/components/DropdownFirst.js +1 -1
  113. package/react/components/IconPicker.d.ts +24 -0
  114. package/react/components/IconPicker.js +283 -0
  115. package/react/components/ListItemLoader.d.ts +4 -0
  116. package/react/components/ListItemLoader.js +62 -0
  117. package/react/components/Menu.d.ts +59 -0
  118. package/react/components/Menu.js +92 -0
  119. package/react/components/PropsList.d.ts +1 -1
  120. package/react/components/PropsList.js +1 -1
  121. package/react/components/Radio.d.ts +8 -7
  122. package/react/components/Radio.js +1 -1
  123. package/react/components/SelectGrid.d.ts +45 -0
  124. package/react/components/SelectGrid.js +179 -0
  125. package/react/components/SelectWithTemplate.js +0 -1
  126. package/react/components/Skeleton.d.ts +30 -0
  127. package/react/components/Skeleton.js +55 -0
  128. package/react/components/Toast.d.ts +15 -0
  129. package/react/components/Toast.js +69 -0
  130. package/react/components/ToastMessage.d.ts +18 -0
  131. package/react/components/ToastMessage.js +66 -0
  132. package/react/components/ToastText.d.ts +9 -0
  133. package/react/components/ToastText.js +42 -0
  134. package/react/components/ToastWrapper.d.ts +31 -0
  135. package/react/components/ToastWrapper.js +116 -0
  136. package/react/components/Togglebox.d.ts +27 -0
  137. package/react/components/Togglebox.js +76 -0
  138. package/react/components/Tooltip.d.ts +2 -5
  139. package/react/components/Tooltip.js +48 -7
  140. package/react/index.d.ts +7 -0
  141. package/react/index.js +14 -0
@@ -0,0 +1,233 @@
1
+ import React from 'react';
2
+ import nextId from "react-id-generator";
3
+ import { OverlayPanel } from '@superdesk/primereact/overlaypanel';
4
+ import { Loader } from "./Loader";
5
+
6
+ /**
7
+ * @ngdoc react
8
+ * @name SelectGrid
9
+ * @description searchable select component with grid view of items
10
+ */
11
+
12
+ export interface IItem {
13
+ value: string;
14
+ label: string;
15
+ [extra: string]: any;
16
+ }
17
+
18
+ interface IProps {
19
+ getItems(searchString: string | null): Promise<Array<IItem>>;
20
+ onChange(value: IItem): void;
21
+ itemTemplate: React.ComponentType<{ item: IItem | null }>;
22
+ triggerTemplate: React.ComponentType<any>;
23
+ label: string;
24
+ filterPlaceholder?: string;
25
+ }
26
+
27
+ interface IState {
28
+ items: Array<IItem>;
29
+ loading: boolean;
30
+ isOpen: boolean;
31
+ }
32
+
33
+ export class SelectGrid extends React.PureComponent<IProps, IState> {
34
+ htmlId = nextId();
35
+ buttonContainer: React.RefObject<HTMLDivElement>;
36
+ overlayPanel: React.RefObject<OverlayPanel>;
37
+ searchInput: React.RefObject<HTMLInputElement>;
38
+ gridContainer: React.RefObject<HTMLDivElement>;
39
+
40
+ constructor(props: IProps) {
41
+ super(props);
42
+
43
+ this.state = { items: [], loading: true, isOpen: false };
44
+
45
+ this.buttonContainer = React.createRef();
46
+ this.overlayPanel = React.createRef();
47
+ this.searchInput = React.createRef();
48
+ this.gridContainer = React.createRef();
49
+ }
50
+
51
+ componentDidMount() {
52
+ this.props.getItems(null).then((items) => {
53
+ this.setState({ items, loading: false });
54
+ });
55
+ }
56
+
57
+ componentWillUnmount() {
58
+ document.removeEventListener('keydown', this.handleKeydown);
59
+ }
60
+
61
+ togglePopup = (event?: React.SyntheticEvent) => {
62
+ if (!event) {
63
+ // @ts-ignore
64
+ this.overlayPanel.current.hide();
65
+ } else {
66
+ // @ts-ignore
67
+ this.overlayPanel.current.toggle(event);
68
+ }
69
+
70
+ setTimeout(() => {
71
+ // Now that the popup has (dis)appeared handle items and events
72
+
73
+ if (this.state.isOpen) {
74
+ document.removeEventListener('keydown', this.handleKeydown);
75
+ // @ts-ignore
76
+ this.buttonContainer.current.querySelector('button')?.focus();
77
+ } else {
78
+ document.addEventListener('keydown', this.handleKeydown);
79
+ this.loadItems();
80
+ // @ts-ignore
81
+ this.searchInput.current.focus();
82
+ }
83
+
84
+ this.setState({ isOpen: !this.state.isOpen });
85
+ });
86
+
87
+ }
88
+
89
+ search = (event: React.ChangeEvent<HTMLInputElement>) => {
90
+ const searchString: string = event.target.value.toLowerCase();
91
+ this.loadItems(searchString);
92
+ }
93
+
94
+ loadItems = (searchString: string | null = null) => {
95
+ this.setState({ loading: true });
96
+ this.props.getItems(searchString).then((items) => {
97
+ this.setState({ items, loading: false });
98
+ });
99
+ }
100
+
101
+ select = (item: IItem) => {
102
+ this.props.onChange(item);
103
+ this.togglePopup();
104
+ }
105
+
106
+ getItemElement = (index: number): HTMLDivElement | null | undefined => {
107
+ return this.gridContainer.current?.querySelector(`[data-item-index="${index}"]`);
108
+ }
109
+
110
+ handleKeydown = (event: KeyboardEvent) => {
111
+ const navKeys = [
112
+ "Enter",
113
+ "ArrowRight",
114
+ "ArrowLeft",
115
+ "ArrowUp",
116
+ "ArrowDown",
117
+ "PageDown",
118
+ "PageUp",
119
+ ];
120
+ const activeElement = document.activeElement;
121
+
122
+ if (event.code === "Escape") {
123
+ event.preventDefault();
124
+ event.stopPropagation();
125
+ this.togglePopup();
126
+ } else if (activeElement === this.searchInput?.current) {
127
+ if (event.code === "ArrowDown") {
128
+ event.preventDefault();
129
+ this.getItemElement(0)?.focus();
130
+ } else if (event.code === "Enter" && this.state.items.length === 1) {
131
+ event.preventDefault();
132
+ this.select(this.state.items[0]);
133
+ }
134
+ // @ts-ignore
135
+ } else if (document.activeElement.getAttribute('data-item-index') && navKeys.includes(event.code)) {
136
+ // @ts-ignore
137
+ let itemIndex = parseInt(activeElement.getAttribute('data-item-index'), 10);
138
+
139
+ // Prevent default behaviour, such as scrolling
140
+ event.preventDefault();
141
+ if (event.code === "Enter") {
142
+ this.select(this.state.items[itemIndex]);
143
+ return;
144
+ } else if (event.code === "ArrowRight") {
145
+ itemIndex += 1;
146
+ } else if (event.code === "ArrowLeft") {
147
+ itemIndex -= 1;
148
+ } else if (event.code === "ArrowDown") {
149
+ itemIndex += 4;
150
+ } else if (event.code === "ArrowUp") {
151
+ if (itemIndex === 0) {
152
+ this.searchInput?.current?.focus();
153
+ return;
154
+ }
155
+ itemIndex -= 4;
156
+ } else if (event.code === "PageDown") {
157
+ itemIndex += 16;
158
+ } else if (event.code === "PageUp") {
159
+ itemIndex -= 16;
160
+ }
161
+
162
+ if (itemIndex < 0) {
163
+ itemIndex = 0;
164
+ } else if (itemIndex >= this.state.items.length) {
165
+ itemIndex = this.state.items.length - 1;
166
+ }
167
+
168
+ this.getItemElement(itemIndex)?.focus();
169
+ }
170
+ }
171
+
172
+ render() {
173
+ const ItemTemplate = this.props.itemTemplate;
174
+ const TriggerTemplate = this.props.triggerTemplate;
175
+
176
+ return (
177
+ <React.Fragment>
178
+ <div
179
+ className="sd-input sd-input--grid-select"
180
+ ref={this.buttonContainer}
181
+ aria-label={this.props.label}
182
+ >
183
+ <label className="sd-input__label">
184
+ {this.props.label}
185
+ </label>
186
+ <TriggerTemplate onClick={this.togglePopup} />
187
+
188
+ </div>
189
+ <OverlayPanel
190
+ ref={this.overlayPanel}
191
+ dismissable={true}
192
+ className="select-grid__overlay-panel"
193
+ appendTo={document.body} // making it work inside `overflow:hidden`
194
+ >
195
+ <div className="sd-shadow--z3 select-grid__panel">
196
+ <div className="select-grid__header">
197
+ <div className="sd-searchbar sd-searchbar--boxed">
198
+ <label className="sd-searchbar__icon" />
199
+ <input
200
+ className="sd-searchbar__input"
201
+ placeholder={this.props.filterPlaceholder || 'Search...'}
202
+ type="text"
203
+ onChange={this.search}
204
+ ref={this.searchInput}
205
+ />
206
+ </div>
207
+ </div>
208
+ <div
209
+ className="select-grid__body flex-grid flex-grid--wrap-items flex-grid--small-4 flex-grid--boxed"
210
+ ref={this.gridContainer}
211
+
212
+ >
213
+ <Loader overlay={this.state.loading} />
214
+ {this.state.items.map((item, index) => (
215
+ <div
216
+ key={this.htmlId + item.label}
217
+ data-item-index={index}
218
+ className="flex-grid__item select-grid__item sd-padding-y--2"
219
+ tabIndex={0}
220
+ role="button"
221
+ aria-label={item.name}
222
+ onClick={() => this.select(item)}
223
+ >
224
+ <ItemTemplate item={item} />
225
+ </div>
226
+ ))}
227
+ </div>
228
+ </div>
229
+ </OverlayPanel>
230
+ </React.Fragment>
231
+ );
232
+ }
233
+ }
@@ -109,8 +109,6 @@ export class SelectWithTemplate<T> extends React.Component<IProps<T>, IState<T>>
109
109
  onFilterInputChange={(searchString) => {
110
110
  this.setState({loading: true});
111
111
 
112
- console.log('change');
113
-
114
112
  getItems(searchString).then((_options) => {
115
113
  this.setState({options: _options, loading: false});
116
114
  });
@@ -0,0 +1,48 @@
1
+
2
+ import React from 'react';
3
+ import classNames from 'classnames';
4
+
5
+ interface IProps {
6
+ shape: string;
7
+ size: string;
8
+ width: string;
9
+ height: string;
10
+ borderRadius: string;
11
+ animation: string;
12
+ style: object;
13
+ className: string;
14
+ }
15
+
16
+ export class Skeleton extends React.Component<IProps> {
17
+ static defaultProps = {
18
+ shape: 'rectangle',
19
+ size: null,
20
+ width: '100%',
21
+ height: '1.2rem',
22
+ borderRadius: null,
23
+ animation: 'wave',
24
+ style: null,
25
+ className: null,
26
+ };
27
+
28
+ skeletonStyle() {
29
+ if (this.props.size) {
30
+ return { width: this.props.size, height: this.props.size, borderRadius: this.props.borderRadius };
31
+ } else {
32
+ return { width: this.props.width, height: this.props.height, borderRadius: this.props.borderRadius };
33
+ }
34
+ }
35
+
36
+ render() {
37
+ const skeletonClassName = classNames('p-skeleton p-component', {
38
+ 'p-skeleton-circle': this.props.shape === 'circle',
39
+ 'p-skeleton-none': this.props.animation === 'none',
40
+ }, this.props.className);
41
+
42
+ const style = this.skeletonStyle();
43
+
44
+ return (
45
+ <div style={style} className={skeletonClassName}></div>
46
+ );
47
+ }
48
+ }
@@ -1,14 +1,27 @@
1
1
  import * as React from 'react';
2
2
  import * as ReactDOM from "react-dom";
3
- import { MessageProp, IMessageOptions } from './ToastMessage';
3
+ import {MessageProp, IMessageOptions, Position} from './ToastMessage';
4
4
  import ToastWrapper from './ToastWrapper';
5
5
 
6
6
  const TOAST_ID = "react-toast";
7
7
 
8
- export class Toasted {
8
+ interface IMessageId {
9
+ id: string;
10
+ position: Position;
11
+ }
12
+
13
+ class Toasted {
9
14
  componentRef: ToastWrapper | null;
15
+
10
16
  constructor() {
11
17
  this.componentRef = null;
18
+ }
19
+
20
+ setup() {
21
+ if (this.componentRef != null) {
22
+ return;
23
+ }
24
+
12
25
  let element = null;
13
26
  const existingElement = document.getElementById(TOAST_ID);
14
27
 
@@ -17,19 +30,32 @@ export class Toasted {
17
30
  } else {
18
31
  const el = document.createElement("div");
19
32
  el.id = TOAST_ID;
20
- el.className = "sd-toast__container sd-toast__container--top ng-scope";
33
+ el.className = "sd-toast__container sd-toast__container--top";
21
34
  document.body.appendChild(el);
22
35
  element = el;
23
36
  }
37
+
24
38
  ReactDOM.render(
25
39
  <ToastWrapper ref={(ref) => {
26
40
  this.componentRef = ref;
27
41
  }} />, element);
28
42
  }
29
43
 
30
- notify(message: MessageProp, options: Omit<IMessageOptions, 'id'>) {
44
+ notify(message: MessageProp, options: Partial<IMessageOptions>): IMessageId | null {
45
+ this.setup();
46
+
47
+ if (this.componentRef != null) {
48
+ return this.componentRef.notify(message, options);
49
+ }
50
+
51
+ return null;
52
+ }
53
+
54
+ close(messageId: IMessageId) {
31
55
  if (this.componentRef != null) {
32
- this.componentRef.notify(message, options);
56
+ this.componentRef.requestClose(messageId.id, messageId.position);
33
57
  }
34
58
  }
35
59
  }
60
+
61
+ export const toasted = new Toasted();
@@ -11,11 +11,11 @@ export type Position = 'top' | 'bottom' | 'top-right' | 'top-left' | 'bottom-rig
11
11
  export type NotesType = 'default' | 'primary' | 'success' | 'warning' | 'alert' | 'highlight' | 'light';
12
12
 
13
13
  export interface IMessageOptions {
14
+ id: string;
15
+ position: Position;
14
16
  message?: MessageProp;
15
- id?: string;
16
17
  duration?: number | null;
17
18
  type?: NotesType;
18
- position?: Position;
19
19
  icon?: string;
20
20
  size?: 'fixed-s' | 'fixed-m' | 'fixed-l' | 'fixed-xl';
21
21
  }
@@ -36,19 +36,17 @@ export const ToastMessage = ({
36
36
  }: IProps) => {
37
37
  const [show, setShow] = React.useState(false);
38
38
  const [enter, setEnter] = React.useState(false);
39
- const [height, setHeight] = React.useState(0);
40
- let timer = null;
39
+ let timer: number;
41
40
  React.useEffect(() => setShow(true), []);
42
41
 
43
- React.useEffect(() => {
44
- if (typeof duration === "number") {
45
- setEnter(true);
46
- timer = setTimeout(() => {
42
+ if (typeof duration === "number") {
43
+ React.useEffect(() => {
44
+ timer = window.setTimeout(() => {
47
45
  close(id, position);
48
46
  }, duration);
49
47
  return () => clearTimeout(timer);
50
- }
51
- }, [enter]);
48
+ }, [enter]);
49
+ }
52
50
 
53
51
  function onMouseEnter() {
54
52
  clearTimeout(timer);
@@ -73,20 +71,15 @@ export const ToastMessage = ({
73
71
  ['sd-toast--exit-active']: !show,
74
72
  });
75
73
 
76
- function addHeight(textHeight: number) {
77
- setHeight(textHeight + 25);
78
- }
79
-
80
74
  return (
81
75
  <div
82
76
  className={classes}
83
77
  onMouseEnter={onMouseEnter}
84
78
  onMouseLeave={onMouseLeave}
85
- style={{ height: height }}
86
79
  aria-live="assertive"
87
80
  aria-atomic="true"
88
81
  >
89
- <ToastText id={id} title={message} icon={icon} onClose={() => close(id, position)} textHeight={addHeight} />
82
+ <ToastText id={id} title={message} icon={icon} onClose={() => close(id, position)} />
90
83
  </div>
91
84
  );
92
85
  };
@@ -2,16 +2,14 @@ import * as React from 'react';
2
2
  import { Icon } from './Icon';
3
3
 
4
4
  interface IProps {
5
- id: string;
5
+ id?: string;
6
6
  title?: string | React.ReactNode;
7
7
  icon?: string;
8
8
  onClose: () => void;
9
- textHeight(height: number): void;
10
9
  }
11
10
 
12
- const ToastText = ({ id, title, icon, onClose, textHeight }: IProps) => {
11
+ const ToastText = ({ id, title, icon, onClose}: IProps) => {
13
12
  const ref = React.useRef(null);
14
- React.useEffect(() => textHeight(ref.current.clientHeight), []);
15
13
  return (
16
14
  <React.Fragment>
17
15
  {icon ?
@@ -29,7 +29,7 @@ export default class ToastWrapper extends React.PureComponent<{}, State> {
29
29
  this.notify = this.notify.bind(this);
30
30
  }
31
31
 
32
- notify = (message: MessageProp, options: IMessageOptions) => {
32
+ notify = (message: MessageProp, options: Partial<IMessageOptions>) => {
33
33
  const toast = this.createToastState(message, options);
34
34
  const { position } = toast;
35
35
 
@@ -47,9 +47,9 @@ export default class ToastWrapper extends React.PureComponent<{}, State> {
47
47
 
48
48
  createToastState = (
49
49
  message: MessageProp,
50
- options: IMessageOptions,
50
+ options: Partial<IMessageOptions>,
51
51
  ) => {
52
- const id = ++ToastWrapper.idCounter;
52
+ const id = '' + ++ToastWrapper.idCounter;
53
53
  const position = options.position ?? 'top';
54
54
  return {
55
55
  id,
@@ -62,7 +62,7 @@ export default class ToastWrapper extends React.PureComponent<{}, State> {
62
62
  };
63
63
  }
64
64
 
65
- requestClose = (id: string, position: string) => {
65
+ requestClose = (id: string, position: keyof State) => {
66
66
  this.setState((prev) => {
67
67
  return {
68
68
  ...prev,
@@ -0,0 +1,108 @@
1
+ import React from 'react';
2
+ import classNames from 'classnames';
3
+ import nextId from "react-id-generator";
4
+
5
+ interface IProps {
6
+ title: string;
7
+ badge?: JSX.Element;
8
+ children: any;
9
+ hideUsingCSS?: boolean;
10
+ initiallyOpen?: boolean; // defaults to false
11
+ className?: string;
12
+ onOpen?(): void;
13
+ onClose?(): void;
14
+ }
15
+
16
+ interface IState {
17
+ isOpen: boolean;
18
+ }
19
+
20
+ /**
21
+ * @ngdoc react
22
+ * @name ToggleBox
23
+ * @description ToggleBox used to open/close a set of details
24
+ */
25
+
26
+ export class ToggleBox extends React.PureComponent<IProps, IState> {
27
+ htmlId = nextId();
28
+ constructor(props: IProps) {
29
+ super(props);
30
+ this.state = {
31
+ isOpen: this.props.initiallyOpen ?? false,
32
+ };
33
+ }
34
+
35
+ handleKeyDown = (event: React.KeyboardEvent<HTMLAnchorElement>): void => {
36
+ if (event.key === "ArrowRight" && !this.state.isOpen) {
37
+ this.setState({ isOpen: true });
38
+ } else if (event.key === "ArrowLeft" && this.state.isOpen) {
39
+ this.setState({ isOpen: false });
40
+ } else if (event.key === "Enter") {
41
+ this.toggle();
42
+ }
43
+ }
44
+
45
+ toggle = (): void => {
46
+ this.setState({ isOpen: !this.state.isOpen }, () => {
47
+ if (!this.state.isOpen && this.props.onClose) {
48
+ this.props.onClose();
49
+ } else if (this.props.onOpen) {
50
+ this.props.onOpen();
51
+ }
52
+ });
53
+ }
54
+
55
+ render() {
56
+ const { title, hideUsingCSS, className, children, badge } = this.props;
57
+ const { isOpen } = this.state;
58
+
59
+ return (
60
+ <div
61
+ className={classNames(
62
+ 'toggle-box',
63
+ className,
64
+ { hidden: !isOpen },
65
+ )}
66
+ >
67
+ <a
68
+ className="toggle-box__header"
69
+ onClick={this.toggle}
70
+ role="button"
71
+ tabIndex={0}
72
+ onKeyDown={this.handleKeyDown}
73
+ id={`togglebox-${this.htmlId}`}
74
+ >
75
+ <div className="toggle-box__chevron">
76
+ <i className="icon-chevron-right-thin" />
77
+ </div>
78
+ <div className="toggle-box__label">
79
+ {title}
80
+ </div>
81
+ <div
82
+ className="toggle-box__line"
83
+ />
84
+ {badge ? badge : null}
85
+ </a>
86
+ <div className="toggle-box__content-wraper">
87
+ {isOpen && !hideUsingCSS && (
88
+ <div className="toggle-box__content" aria-describedby={`togglebox-${this.htmlId}`}>
89
+ {children}
90
+ </div>
91
+ )}
92
+
93
+ {hideUsingCSS && (
94
+ <div
95
+ className={classNames(
96
+ 'toggle-box__content',
97
+ { 'toggle-box__content--hidden': !isOpen },
98
+ )}
99
+ aria-describedby={`togglebox-${this.htmlId}`}
100
+ >
101
+ {children}
102
+ </div>
103
+ )}
104
+ </div>
105
+ </div>
106
+ );
107
+ }
108
+ }
@@ -1,11 +1,19 @@
1
1
  import * as React from 'react';
2
2
  import nextId from "react-id-generator";
3
+ import { Tooltip as PRTooltip } from '@superdesk/primereact/tooltip';
3
4
  interface IProps {
4
5
  text: string;
5
6
  flow?: 'top' | 'left' | 'right' | 'down'; // defaults to 'top'
7
+ appendToBody?: boolean;
6
8
  }
7
9
 
8
- export class Tooltip extends React.PureComponent<IProps> {
10
+ export const Tooltip: React.FC<IProps> = ({appendToBody, children, ...otherProps}) =>
11
+ appendToBody ?
12
+ <TooltipAppended {...otherProps}>{children}</TooltipAppended>
13
+ :
14
+ <TooltipBasic {...otherProps}>{children}</TooltipBasic>;
15
+
16
+ class TooltipBasic extends React.PureComponent<IProps> {
9
17
  htmlId = nextId();
10
18
  componentDidMount() {
11
19
  let tooltip = document.getElementById('t' + this.htmlId);
@@ -26,3 +34,19 @@ export class Tooltip extends React.PureComponent<IProps> {
26
34
  }
27
35
  }
28
36
  }
37
+
38
+ const TooltipAppended: React.FC<IProps> = ({children, flow, text}) => {
39
+ const htmlId = nextId();
40
+ const triggerId = "t" + htmlId;
41
+ const position = flow === "down" ? "bottom" : flow;
42
+
43
+ return (
44
+ <React.Fragment>
45
+ <PRTooltip target={"#" + triggerId} content={text} position={position ?? 'top'}/>
46
+ {React.isValidElement(children) ?
47
+ React.cloneElement(children, { id: triggerId})
48
+ :
49
+ <React.Fragment />}
50
+ </React.Fragment>
51
+ );
52
+ };
@@ -60,6 +60,14 @@ export { GridItem, GridItemContent, GridItemMedia, GridItemFooter, GridItemConte
60
60
  GridItemTime, GridItemTitle, GridItemText, GridItemSlug, GridItemFooterBlock,
61
61
  GridItemFooterActions, GridItemTopActions, GridItemCheckWrapper
62
62
  } from './components/GridItem';
63
+ export { toasted } from './components/Toast';
64
+ export { Menu } from './components/Menu';
65
+ export { ToggleBox } from './components/Togglebox';
66
+ export { SelectGrid } from './components/SelectGrid';
67
+ export { IconPicker } from './components/IconPicker';
68
+
69
+ export { Skeleton } from './components/Skeleton';
70
+ export { ListItemLoader } from './components/ListItemLoader';
63
71
 
64
72
  // declare non-typescript exports to prevent errors
65
73
  export declare const ToggleBoxNext: any;