superdesk-ui-framework 3.0.1-beta.12 → 3.0.1-beta.14

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 (29) hide show
  1. package/app/styles/_table-list.scss +1 -0
  2. package/app/styles/dropdowns/_basic-dropdown.scss +6 -0
  3. package/app-typescript/components/Dropdown.tsx +106 -78
  4. package/app-typescript/components/DurationInput.tsx +14 -4
  5. package/app-typescript/components/Lists/ContentList.tsx +28 -4
  6. package/app-typescript/components/Lists/TableList.tsx +11 -9
  7. package/dist/examples.bundle.js +2267 -1761
  8. package/dist/playgrounds/react-playgrounds/RundownEditor.tsx +3 -9
  9. package/dist/playgrounds/react-playgrounds/Rundowns.tsx +9 -19
  10. package/dist/react/ContentList.tsx +2 -10
  11. package/dist/react/Dropdowns.tsx +580 -48
  12. package/dist/react/TableList.tsx +28 -30
  13. package/dist/superdesk-ui.bundle.css +8 -3
  14. package/dist/superdesk-ui.bundle.js +1294 -1219
  15. package/dist/vendor.bundle.js +14 -14
  16. package/examples/pages/playgrounds/react-playgrounds/RundownEditor.tsx +3 -9
  17. package/examples/pages/playgrounds/react-playgrounds/Rundowns.tsx +9 -19
  18. package/examples/pages/react/ContentList.tsx +2 -10
  19. package/examples/pages/react/Dropdowns.tsx +580 -48
  20. package/examples/pages/react/TableList.tsx +28 -30
  21. package/package.json +1 -1
  22. package/react/components/Dropdown.d.ts +4 -4
  23. package/react/components/Dropdown.js +44 -26
  24. package/react/components/DurationInput.d.ts +1 -1
  25. package/react/components/DurationInput.js +14 -4
  26. package/react/components/Lists/ContentList.d.ts +5 -0
  27. package/react/components/Lists/ContentList.js +36 -15
  28. package/react/components/Lists/TableList.d.ts +5 -5
  29. package/react/components/Lists/TableList.js +6 -4
@@ -5,6 +5,7 @@
5
5
  display: flex;
6
6
  flex-direction: column;
7
7
  align-self: stretch;
8
+ overflow: hidden !important;
8
9
  //gap: $sd-base-increment * 1.5;
9
10
  &--comfortable {
10
11
  gap: $sd-base-increment * 2;
@@ -109,6 +109,10 @@ $focus-box-shadow: 0 0 0 3px $sd-colour--focus-shadow;
109
109
  border-radius: $dropdownMenuBorderRadius;
110
110
  text-align: start;
111
111
 
112
+ .dropdown {
113
+ width: 100%;
114
+ }
115
+
112
116
  // Links within the dropdown menu
113
117
  li, .dropdown__menu-item {
114
118
  a:not(.btn), button:not(.btn):not(.dropdown__menu-close):not(.toggle-button) {
@@ -193,8 +197,10 @@ $focus-box-shadow: 0 0 0 3px $sd-colour--focus-shadow;
193
197
  }
194
198
  .dropdown__menu-body {
195
199
  overflow-y: auto;
200
+ overflow-x: hidden;
196
201
  flex: 0 1;
197
202
  padding: 0.6rem 0;
203
+ max-height: 320px;
198
204
  }
199
205
  .dropdown__menu-body:first-child {
200
206
  .dropdown__menu-label {
@@ -4,7 +4,7 @@ import { createPopper } from '@popperjs/core';
4
4
  import { useId } from "react-id-generator";
5
5
 
6
6
  export interface IMenuItem {
7
- label: string;
7
+ label: string | React.ReactNode;
8
8
  icon?: string;
9
9
  active?: boolean;
10
10
  onSelect(): void;
@@ -16,19 +16,19 @@ interface IMenuItemRes extends IMenuItem {
16
16
 
17
17
  export interface ISubmenu {
18
18
  type: 'submenu';
19
- label: string;
19
+ label: string | React.ReactNode;
20
20
  icon?: string;
21
21
  items: Array<IMenuItem | ISubmenu | IMenuGroup | 'divider'>;
22
22
  }
23
23
 
24
24
  export interface IMenuGroup {
25
25
  type: 'group';
26
- label?: string;
26
+ label?: string | React.ReactNode;
27
27
  items: Array<IMenuItem | ISubmenu | IMenuGroup | 'divider'>;
28
28
  }
29
29
 
30
30
  interface IMenu {
31
- label?: string;
31
+ label?: string | React.ReactNode;
32
32
  align?: 'left' | 'right';
33
33
  items: Array<IMenuItem | ISubmenu | IMenuGroup | 'divider'>;
34
34
  header?: Array<IMenuItem | ISubmenu | IMenuGroup | 'divider'>;
@@ -38,6 +38,8 @@ interface IMenu {
38
38
  onChange?(event?: any): void;
39
39
  }
40
40
 
41
+ const DROPDOWN_ID_CONTAINER = "sd-dropdown-constainer";
42
+
41
43
  export const Dropdown = ({
42
44
  items,
43
45
  header,
@@ -50,11 +52,8 @@ export const Dropdown = ({
50
52
  const [open, setOpen] = React.useState(false);
51
53
  const [change, setChange] = React.useState(false);
52
54
  const [menuID] = useId();
53
- const DROPDOWN_ID = "react-placeholder";
54
55
  const ref = React.useRef(null);
55
- const refSubMenu = React.useRef(null);
56
56
  const buttonRef = React.useRef(null);
57
- const refButtonSubMenu = React.useRef(null);
58
57
  const headerElements = header?.map((el, index) => {
59
58
  return each(el, index);
60
59
  });
@@ -66,12 +65,12 @@ export const Dropdown = ({
66
65
  const footerElements = footer?.map((el, index) => {
67
66
  return each(el, index);
68
67
  });
68
+
69
69
  React.useEffect(() => {
70
- const existingElement = document.getElementById(DROPDOWN_ID);
70
+ const existingElement = document.getElementById(DROPDOWN_ID_CONTAINER);
71
71
  if (!existingElement) {
72
72
  const el = document.createElement("div");
73
- el.id = DROPDOWN_ID;
74
- // style placeholder
73
+ el.id = DROPDOWN_ID_CONTAINER;
75
74
  el.style.position = 'absolute';
76
75
  el.style.top = '0';
77
76
  el.style.left = '0';
@@ -80,7 +79,6 @@ export const Dropdown = ({
80
79
 
81
80
  document.body.appendChild(el);
82
81
  }
83
-
84
82
  }, [change]);
85
83
 
86
84
  React.useLayoutEffect(() => {
@@ -88,17 +86,12 @@ export const Dropdown = ({
88
86
  addInPlaceholder();
89
87
  }
90
88
  setChange(true);
91
-
92
89
  }, [open]);
93
90
 
94
- // structure for append menu
95
91
  function createAppendMenu() {
96
92
  if (header && footer) {
97
93
  return (
98
- <div className='dropdown__menu dropdown__menu--has-head-foot'
99
- id={menuID}
100
- role='menu'
101
- ref={ref}>
94
+ <div className='dropdown__menu dropdown__menu--has-head-foot' id={menuID} role='menu' ref={ref}>
102
95
  <ul className='dropdown__menu-header'>
103
96
  {headerElements}
104
97
  </ul>
@@ -112,10 +105,7 @@ export const Dropdown = ({
112
105
  );
113
106
  } else if (header) {
114
107
  return (
115
- <div className='dropdown__menu dropdown__menu--has-head-foot'
116
- id={menuID}
117
- role='menu'
118
- ref={ref}>
108
+ <div className='dropdown__menu dropdown__menu--has-head-foot' id={menuID} role='menu' ref={ref}>
119
109
  <ul className='dropdown__menu-header'>
120
110
  {headerElements}
121
111
  </ul>
@@ -126,10 +116,7 @@ export const Dropdown = ({
126
116
  );
127
117
  } else if (footer) {
128
118
  return (
129
- <div className='dropdown__menu dropdown__menu--has-head-foot'
130
- id={menuID}
131
- role='menu'
132
- ref={ref}>
119
+ <div className='dropdown__menu dropdown__menu--has-head-foot' id={menuID} role='menu' ref={ref}>
133
120
  <ul className='dropdown__menu-body'>
134
121
  {dropdownElements}
135
122
  </ul>
@@ -140,17 +127,13 @@ export const Dropdown = ({
140
127
  );
141
128
  } else {
142
129
  return (
143
- <ul className='dropdown__menu '
144
- id={menuID}
145
- role='menu'
146
- ref={ref}>
130
+ <ul className='dropdown__menu ' id={menuID} role='menu' ref={ref}>
147
131
  {dropdownElements}
148
132
  </ul>
149
133
  );
150
134
  }
151
135
  }
152
136
 
153
- // toggle menu
154
137
  function toggleDisplay() {
155
138
  if (!open) {
156
139
  let menuRef: any;
@@ -199,14 +182,13 @@ export const Dropdown = ({
199
182
  }
200
183
 
201
184
  function addInPlaceholder() {
202
- const placeholder = document.getElementById(DROPDOWN_ID);
185
+ const placeholder = document.getElementById(DROPDOWN_ID_CONTAINER);
203
186
  let menu = createAppendMenu();
204
187
  if (open) {
205
188
  return ReactDOM.render(menu, placeholder);
206
189
  } else {
207
- const menuDOM = document.getElementById(menuID);
208
- if (menuDOM) {
209
- menuDOM.style.display = 'none';
190
+ if (placeholder) {
191
+ ReactDOM.unmountComponentAtNode(placeholder);
210
192
  }
211
193
  }
212
194
  }
@@ -218,33 +200,13 @@ export const Dropdown = ({
218
200
  submenuItems.push(each(el, key));
219
201
  });
220
202
  return (
221
- <li key={index}>
222
- <div className='dropdown' >
223
- <button
224
- ref={refButtonSubMenu}
225
- className='dropdown__toggle dropdown-toggle'
226
- aria-haspopup="menu"
227
- tabIndex={0}
228
- onMouseOver={() => {
229
- let subMenuRef = refSubMenu.current;
230
- let subToggleRef = refButtonSubMenu.current;
231
- if (subMenuRef && subToggleRef) {
232
- createPopper(subToggleRef, subMenuRef, {
233
- placement: 'right-start',
234
- });
235
- }
236
- }}
237
- onClick={item['onSelect']}>
238
- {item['icon'] ? <i className={'icon-' + item['icon']}></i> : null}
239
- {item['label']}
240
- </button>
241
- <ul ref={refSubMenu}
242
- role='menu'
243
- className='dropdown__menu'>
244
- {submenuItems}
245
- </ul>
246
- </div>
247
- </li>
203
+ <DropdownItemWithSubmenu
204
+ key={index}
205
+ index={index}
206
+ item={item}
207
+ menuID={menuID}
208
+ subMenuItems={submenuItems}
209
+ onChange={onChange} />
248
210
  );
249
211
  } else if (item['type'] === 'group') {
250
212
  let groupItems: any = [];
@@ -264,17 +226,18 @@ export const Dropdown = ({
264
226
  } else {
265
227
  return (
266
228
  <DropdownItem
267
- key={index}
268
- label={item['label']}
269
- icon={item['icon']}
270
- active={item['active']}
271
- onSelect={item['onSelect']}
272
- onChange={onChange} />);
229
+ key={index}
230
+ label={item['label']}
231
+ icon={item['icon']}
232
+ active={item['active']}
233
+ onSelect={item['onSelect']}
234
+ onChange={onChange} />
235
+ );
273
236
  }
274
237
  }
275
238
 
276
239
  return (
277
- <div className={'dropdown ' + (open ? 'open' : '')} >
240
+ <div className={'dropdown ' + (open ? 'open' : '')}>
278
241
  {typeof children === 'object' ?
279
242
  (React.isValidElement(children) ?
280
243
  <div ref={buttonRef} style={{ display: 'content' }}>
@@ -288,11 +251,11 @@ export const Dropdown = ({
288
251
  </div> : null)
289
252
  :
290
253
  <button ref={buttonRef}
291
- className=' dropdown__toggle dropdown-toggle'
292
- aria-haspopup="menu"
293
- tabIndex={0}
294
- aria-expanded={open}
295
- onClick={toggleDisplay}>
254
+ className=' dropdown__toggle dropdown-toggle'
255
+ aria-haspopup="menu"
256
+ tabIndex={0}
257
+ aria-expanded={open}
258
+ onClick={toggleDisplay}>
296
259
  {children}
297
260
  <span className="dropdown__caret"></span>
298
261
  </button>}
@@ -301,7 +264,7 @@ export const Dropdown = ({
301
264
  null : (function() {
302
265
  if (header && footer) {
303
266
  return (
304
- <div className='dropdown__menu dropdown__menu--has-head-foot' role='menu' ref={ref} >
267
+ <div className='dropdown__menu dropdown__menu--has-head-foot' role='menu' ref={ref}>
305
268
  <ul className='dropdown__menu-header'>
306
269
  {headerElements}
307
270
  </ul>
@@ -315,7 +278,7 @@ export const Dropdown = ({
315
278
  );
316
279
  } else if (header) {
317
280
  return (
318
- <div className='dropdown__menu dropdown__menu--has-head-foot' role='menu' ref={ref} >
281
+ <div className='dropdown__menu dropdown__menu--has-head-foot' role='menu' ref={ref}>
319
282
  <ul className='dropdown__menu-header'>
320
283
  {headerElements}
321
284
  </ul>
@@ -326,7 +289,7 @@ export const Dropdown = ({
326
289
  );
327
290
  } else if (footer) {
328
291
  return (
329
- <div className='dropdown__menu dropdown__menu--has-head-foot' role='menu' ref={ref} >
292
+ <div className='dropdown__menu dropdown__menu--has-head-foot' role='menu' ref={ref}>
330
293
  <ul className='dropdown__menu-body'>
331
294
  {dropdownElements}
332
295
  </ul>
@@ -337,7 +300,7 @@ export const Dropdown = ({
337
300
  );
338
301
  } else {
339
302
  return (
340
- <ul className='dropdown__menu' role='menu' ref={ref} >
303
+ <ul className='dropdown__menu' role='menu' ref={ref}>
341
304
  {dropdownElements}
342
305
  </ul>
343
306
  );
@@ -356,7 +319,9 @@ onChange,
356
319
  }: IMenuItemRes) => {
357
320
  return (
358
321
  <li role='none' className={active ? 'dropdown__menu-item--active' : ''}>
359
- <button tabIndex={0} role='menuitem' onClick={() => {
322
+ <button tabIndex={0}
323
+ role='menuitem'
324
+ onClick={() => {
360
325
  onSelect();
361
326
  if (onChange) {
362
327
  onChange();
@@ -367,5 +332,68 @@ onChange,
367
332
  </button>
368
333
  </li>
369
334
  );
335
+ };
336
+
337
+ const DropdownItemWithSubmenu = ({
338
+ index,
339
+ item,
340
+ menuID,
341
+ subMenuItems,
342
+ onChange,
343
+ }: IMenuItem | any) => {
344
+ const [open, setOpen] = React.useState<undefined | boolean>(undefined);
370
345
 
346
+ const refButtonSubMenu = React.useRef(null);
347
+ const refSubMenu = React.useRef(null);
348
+ const placeholder = document.getElementById(menuID);
349
+
350
+ React.useEffect(() => {
351
+ let subMenuRef: any = refSubMenu.current;
352
+ let subToggleRef = refButtonSubMenu.current;
353
+
354
+ if (open === true) {
355
+ placeholder?.appendChild(subMenuRef);
356
+ subMenuRef.style.display = 'block';
357
+ } else if (open === false) {
358
+ placeholder?.removeChild(subMenuRef);
359
+ subMenuRef.style.display = 'none';
360
+ }
361
+
362
+ if (subMenuRef && subToggleRef) {
363
+ createPopper(subToggleRef, subMenuRef, {
364
+ placement: 'right-start',
365
+ });
366
+ }
367
+ }, [open]);
368
+
369
+ return (
370
+ <li key={index} ref={refButtonSubMenu}>
371
+ <div className='dropdown'
372
+ onMouseLeave={() => setOpen(false)}>
373
+ <button
374
+ className='dropdown__toggle dropdown-toggle'
375
+ aria-haspopup="menu"
376
+ tabIndex={0}
377
+ onClick={() => {
378
+ if (item.onSelect) {
379
+ item.onSelect();
380
+ }
381
+ if (onChange) {
382
+ onChange();
383
+ }
384
+ }}
385
+ onMouseOver={() => setOpen(true) }>
386
+ {item['icon'] ? <i className={'icon-' + item['icon']}></i> : null}
387
+ {item['label']}
388
+ </button>
389
+ <ul
390
+ role='menu'
391
+ ref={refSubMenu}
392
+ style={{display: 'none'}}
393
+ className='dropdown__menu'>
394
+ {subMenuItems}
395
+ </ul>
396
+ </div>
397
+ </li>
398
+ );
371
399
  };
@@ -356,7 +356,7 @@ export class DurationInput extends React.PureComponent<IProps, IState> {
356
356
  }
357
357
  }
358
358
 
359
- export function getDurationString(seconds: number) {
359
+ export function getDurationString(seconds: number, zero?: boolean) {
360
360
  function zeroPad(value: number | string) {
361
361
  if (value.toString().length === 1 || value === 0) {
362
362
  return `0${value}`;
@@ -367,9 +367,19 @@ export function getDurationString(seconds: number) {
367
367
  }
368
368
  }
369
369
 
370
- let hour = zeroPad(Math.floor(seconds / 3600));
371
- let minute = zeroPad(Math.floor((seconds % 3600) / 60));
372
- let second = zeroPad(Math.floor(seconds % 60));
370
+ let hour;
371
+ let minute;
372
+ let second;
373
+
374
+ if (zero) {
375
+ hour = zeroPad(Math.floor(seconds / 3600));
376
+ minute = zeroPad(Math.floor((seconds % 3600) / 60));
377
+ second = zeroPad(Math.floor(seconds % 60));
378
+ } else {
379
+ hour = Math.floor(seconds / 3600);
380
+ minute = Math.floor((seconds % 3600) / 60);
381
+ second = Math.floor(seconds % 60);
382
+ }
373
383
 
374
384
  if (Number(hour) === 0 && Number(minute) > 0) {
375
385
  return `${minute}m ${second}s`;
@@ -14,8 +14,34 @@ interface IPropsItem {
14
14
  }
15
15
 
16
16
  class ContentListItem extends React.PureComponent<IPropsItem> {
17
+
18
+ private timer: any;
19
+ private delay = 200;
20
+ private prevent = false;
21
+
22
+ onSingleClick = () => {
23
+ this.timer = setTimeout(() => {
24
+ if (!this.prevent) {
25
+ if (this.props.onClick) {
26
+ this.props.onClick();
27
+ }
28
+ }
29
+ }, this.delay);
30
+ }
31
+
32
+ onDoubleClick = () => {
33
+ clearTimeout(this.timer);
34
+ this.prevent = true;
35
+ if (this.props.onDoubleClick) {
36
+ this.props.onDoubleClick();
37
+ }
38
+ setTimeout(() => {
39
+ this.prevent = false;
40
+ }, this.delay);
41
+ }
42
+
17
43
  render() {
18
- let classes = classNames('sd-list-item sd-list-item-group sd-list-item-group--space-between-items', {
44
+ let classes = classNames('sd-list-item sd-shadow--z1', {
19
45
  'sd-list-item--activated': this.props.activated,
20
46
  'sd-list-item--selected': this.props.selected,
21
47
  'fetched': this.props.archived,
@@ -23,8 +49,7 @@ class ContentListItem extends React.PureComponent<IPropsItem> {
23
49
  });
24
50
 
25
51
  return (
26
- <div className={classes} onClick={this.props.onClick} onDoubleClick={this.props.onDoubleClick}>
27
- <div className="sd-list-item sd-shadow--z1">
52
+ <div className={classes} onClick={this.onSingleClick} onDoubleClick={this.onDoubleClick}>
28
53
  {this.props.locked
29
54
  ? <div className="sd-list-item__border sd-list-item__border--locked"></div>
30
55
  : <div className="sd-list-item__border"></div>}
@@ -47,7 +72,6 @@ class ContentListItem extends React.PureComponent<IPropsItem> {
47
72
  {this.props.action}
48
73
  </div>
49
74
  </div>
50
- </div>
51
75
  );
52
76
  }
53
77
  }
@@ -9,12 +9,12 @@ export interface IProps {
9
9
  array: Array<IPropsArrayItem>;
10
10
  addItem?: boolean;
11
11
  dragAndDrop?: boolean;
12
- itemsDropdown?: Array<IMenuItem | ISubmenu | IMenuGroup | 'divider'>;
13
12
  className?: string;
14
13
  readOnly?: boolean;
15
14
  showDragHandle?: 'always' | 'onHover' | 'none'; // always default
16
15
  onDrag?(start: number, end: number): void;
17
16
  onAddItem?(index: number, item?: IPropsArrayItem ): void;
17
+ itemsDropdown?(index?: number): Array<IMenuItem | ISubmenu | IMenuGroup | 'divider'>;
18
18
  }
19
19
 
20
20
  export interface IPropsArrayItem {
@@ -22,9 +22,9 @@ export interface IPropsArrayItem {
22
22
  center?: React.ReactNode;
23
23
  end?: React.ReactNode;
24
24
  action?: React.ReactNode;
25
+ hexColor?: string;
25
26
  onClick?(): void;
26
27
  onDoubleClick?(): void;
27
- hexColor?: string;
28
28
  }
29
29
 
30
30
  interface IState {
@@ -86,7 +86,7 @@ class TableList extends React.PureComponent<IProps, IState> {
86
86
  dropDown() {
87
87
  return (
88
88
  <Dropdown
89
- items={this.props.itemsDropdown ? this.props.itemsDropdown : []}>
89
+ items={this.props.itemsDropdown ? this.props.itemsDropdown() : []}>
90
90
  <Button
91
91
  type="primary"
92
92
  icon="plus-large"
@@ -135,7 +135,9 @@ class TableList extends React.PureComponent<IProps, IState> {
135
135
  ? item.onDoubleClick
136
136
  : undefined}
137
137
  addItem={this.props.addItem}
138
- itemsDropdown={this.props.itemsDropdown}
138
+ itemsDropdown={() => this.props.itemsDropdown
139
+ ? this.props.itemsDropdown(index)
140
+ : []}
139
141
  hexColor={item.hexColor}
140
142
  onAddItem={() => this.props.onAddItem
141
143
  && this.props.onAddItem(index, item)}
@@ -172,7 +174,7 @@ class TableList extends React.PureComponent<IProps, IState> {
172
174
  ? item.onDoubleClick
173
175
  : undefined}
174
176
  addItem={this.props.addItem}
175
- itemsDropdown={this.props.itemsDropdown}
177
+ itemsDropdown={() => this.props.itemsDropdown ? this.props.itemsDropdown(index) : []}
176
178
  hexColor={item.hexColor}
177
179
  onAddItem={() => this.props.onAddItem
178
180
  && this.props.onAddItem(index, item)}
@@ -208,14 +210,14 @@ export interface IPropsItem {
208
210
  end?: React.ReactNode;
209
211
  action?: React.ReactNode;
210
212
  addItem?: boolean;
211
- itemsDropdown?: any;
212
213
  dragAndDrop?: boolean;
214
+ hexColor?: string;
215
+ showDragHandle?: 'always' | 'onHover' | 'none';
213
216
  onClick?(): void;
214
217
  onDoubleClick?(): void;
215
218
  onSelect?(): void;
216
219
  onAddItem?(e: number): void;
217
- hexColor?: string;
218
- showDragHandle?: 'always' | 'onHover' | 'none';
220
+ itemsDropdown?(index?: number): Array<IMenuItem | ISubmenu | IMenuGroup | 'divider'>;
219
221
  }
220
222
 
221
223
  class TableListItem extends React.PureComponent<IPropsItem> {
@@ -281,7 +283,7 @@ class TableListItem extends React.PureComponent<IPropsItem> {
281
283
  <div className='table-list__add-bar'>
282
284
  <Dropdown
283
285
  onChange={this.props.onAddItem}
284
- items={this.props.itemsDropdown ? this.props.itemsDropdown : []}>
286
+ items={this.props.itemsDropdown ? this.props.itemsDropdown() : []}>
285
287
  <Button
286
288
  type="primary"
287
289
  icon="plus-large"