superdesk-ui-framework 2.4.17 → 2.4.20

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 (40) hide show
  1. package/app/styles/_accessibility.scss +8 -3
  2. package/app/styles/_buttons.scss +52 -1
  3. package/app/styles/_spinner.scss +46 -0
  4. package/app-typescript/components/Button.tsx +7 -1
  5. package/app-typescript/components/Input.tsx +22 -7
  6. package/app-typescript/components/Select.tsx +13 -3
  7. package/app-typescript/components/Spinner.tsx +33 -0
  8. package/app-typescript/components/TabCustom.tsx +89 -40
  9. package/app-typescript/components/TabList.tsx +43 -18
  10. package/app-typescript/components/Tag.tsx +2 -2
  11. package/app-typescript/index.ts +1 -1
  12. package/dist/examples.bundle.css +36 -1
  13. package/dist/examples.bundle.js +901 -632
  14. package/dist/react/Buttons.tsx +25 -0
  15. package/dist/react/Inputs.tsx +24 -3
  16. package/dist/react/Selects.tsx +23 -1
  17. package/dist/react/Tabs.tsx +225 -72
  18. package/dist/superdesk-ui.bundle.css +75 -13
  19. package/dist/superdesk-ui.bundle.js +560 -400
  20. package/dist/vendor.bundle.js +13 -13
  21. package/examples/pages/react/Buttons.tsx +25 -0
  22. package/examples/pages/react/Inputs.tsx +24 -3
  23. package/examples/pages/react/Selects.tsx +23 -1
  24. package/examples/pages/react/Tabs.tsx +225 -72
  25. package/package.json +2 -2
  26. package/react/components/Button.d.ts +2 -0
  27. package/react/components/Button.js +4 -2
  28. package/react/components/Input.d.ts +4 -1
  29. package/react/components/Input.js +17 -5
  30. package/react/components/Select.d.ts +3 -1
  31. package/react/components/Select.js +11 -2
  32. package/react/components/Spinner.d.ts +12 -0
  33. package/react/components/Spinner.js +70 -0
  34. package/react/components/TabCustom.d.ts +22 -12
  35. package/react/components/TabCustom.js +52 -23
  36. package/react/components/TabList.d.ts +11 -2
  37. package/react/components/TabList.js +32 -11
  38. package/react/components/Tag.js +1 -1
  39. package/react/index.d.ts +1 -1
  40. package/react/index.js +2 -2
@@ -1,9 +1,14 @@
1
1
  .a11y-only {
2
- position: absolute;
2
+ position: absolute !important;
3
3
  top: 0;
4
4
  z-index: -1;
5
5
  pointer-events: none;
6
- opacity: 0;
7
- height: 1px;
6
+ opacity: 0 !important;
7
+ height: 0;
8
+ width: 0;
9
+ min-width: 0;
8
10
  overflow: hidden;
11
+ font-size: 0.01px !important;
12
+ padding: 0 !important;
13
+ margin: 0 !important;
9
14
  }
@@ -53,7 +53,7 @@ $button-sizes: (
53
53
 
54
54
  /// opacity for a disabled button.
55
55
  /// @type List
56
- $button-opacity-disabled: 0.25 !default;
56
+ $button-opacity-disabled: 0.6 !default;
57
57
 
58
58
  /// Background color lightness on hover for buttons.
59
59
  /// @type Number
@@ -99,6 +99,7 @@ $icn-button-focus-box-shadow: 0 0 0 2px rgba(94, 169, 200, 0.30);
99
99
  text-align: center;
100
100
  text-decoration: none;
101
101
  cursor: pointer;
102
+ gap: 0.6rem;
102
103
  }
103
104
 
104
105
  /// Expands a button to make it full-width.
@@ -477,6 +478,19 @@ $icn-button-focus-box-shadow: 0 0 0 2px rgba(94, 169, 200, 0.30);
477
478
  }
478
479
  }
479
480
  }
481
+
482
+ &.btn--primary,
483
+ &.btn--success,
484
+ &.btn--warning,
485
+ &.btn--alert,
486
+ &.btn--highlight,
487
+ &.btn--sd-green,
488
+ &.btn--secondary {
489
+ color: var(--sd-btn-txt);
490
+ .sd-spinner__path {
491
+ stroke: var(--sd-btn-txt);
492
+ }
493
+ }
480
494
  }
481
495
 
482
496
  // Hollow UI dark
@@ -489,6 +503,19 @@ $icn-button-focus-box-shadow: 0 0 0 2px rgba(94, 169, 200, 0.30);
489
503
  @include button-hollow-style($color);
490
504
  }
491
505
  }
506
+
507
+ &.btn--primary,
508
+ &.btn--success,
509
+ &.btn--warning,
510
+ &.btn--alert,
511
+ &.btn--highlight,
512
+ &.btn--sd-green,
513
+ &.btn--secondary {
514
+ color: var(--sd-btn-color);
515
+ .sd-spinner__path {
516
+ stroke: var(--sd-btn-color);
517
+ }
518
+ }
492
519
 
493
520
  // Hollow disabled UI dark
494
521
  &.btn--disabled,
@@ -582,6 +609,16 @@ $icn-button-focus-box-shadow: 0 0 0 2px rgba(94, 169, 200, 0.30);
582
609
  }
583
610
  }
584
611
  }
612
+
613
+ &--primary, &--success, &--warning, &--alert, &--highlight, &--sd-green, &--secondary {
614
+ color: $white;
615
+ [class^="icon-"], [class*=" icon-"] {
616
+ color: $white;
617
+ }
618
+ .sd-spinner__path {
619
+ stroke: $white;
620
+ }
621
+ }
585
622
  }
586
623
 
587
624
  // SD create button
@@ -1123,3 +1160,17 @@ $button-nav-color: currentColor;
1123
1160
  position: absolute;
1124
1161
  width: 1px;
1125
1162
  }
1163
+
1164
+ .btn {
1165
+ &[data-loading="true"] {
1166
+ pointer-events: none;
1167
+ .sd-spinner {
1168
+ margin-inline-start: -0.3rem;
1169
+ }
1170
+ &.btn--icon-only {
1171
+ .sd-spinner {
1172
+ margin-inline-start: 0;
1173
+ }
1174
+ }
1175
+ }
1176
+ }
@@ -0,0 +1,46 @@
1
+ .sd-spinner {
2
+ animation: rotate 2s linear infinite;
3
+ z-index: 1;
4
+ &--mini {
5
+ width: 1.8rem;
6
+ height: 1.8rem;
7
+ }
8
+ &--small {
9
+ width: 2.4rem;
10
+ height: 2.4rem;
11
+ }
12
+ &--medium {
13
+ width: 3.2rem;
14
+ height: 3.2rem;
15
+ }
16
+ &--large {
17
+ width: 4.8rem;
18
+ height: 4.8rem;
19
+ }
20
+ }
21
+ .sd-spinner__path {
22
+ stroke: var(--color-text-light);
23
+ stroke-linecap: round;
24
+ animation: dash 1.5s ease-in-out infinite;
25
+ }
26
+
27
+ @keyframes rotate {
28
+ 100% {
29
+ transform: rotate(360deg);
30
+ }
31
+ }
32
+
33
+ @keyframes dash {
34
+ 0% {
35
+ stroke-dasharray: 1, 150;
36
+ stroke-dashoffset: 0;
37
+ }
38
+ 50% {
39
+ stroke-dasharray: 90, 150;
40
+ stroke-dashoffset: -32;
41
+ }
42
+ 100% {
43
+ stroke-dasharray: 90, 150;
44
+ stroke-dashoffset: -124;
45
+ }
46
+ }
@@ -1,6 +1,7 @@
1
1
  import * as React from 'react';
2
2
  import classNames from 'classnames';
3
3
  import { Icon } from './Icon';
4
+ import { Spinner } from './Spinner';
4
5
 
5
6
  interface IButtonBase {
6
7
  id?: string;
@@ -20,6 +21,8 @@ interface IPropsButton extends IButtonBase {
20
21
  expand?: boolean;
21
22
  style?: 'filled' | 'hollow' | 'text-only'; // defaults to 'filled'
22
23
  shape?: 'square' | 'round'; // defaults to 'square'
24
+ isLoading?: boolean;
25
+ loadingLabel?: string;
23
26
  }
24
27
 
25
28
  export class Button extends React.PureComponent<IPropsButton> {
@@ -40,10 +43,13 @@ export class Button extends React.PureComponent<IPropsButton> {
40
43
  id={this.props.id}
41
44
  className={classes}
42
45
  tabIndex={0}
46
+ disabled={this.props.isLoading}
47
+ data-loading={this.props.isLoading}
43
48
  onClick={this.props.disabled ? () => false : (event) => this.props.onClick(event)}
44
49
  aria-label={this.props.iconOnly ? this.props.text : ''}
45
50
  data-test-id={this.props['data-test-id']}>
46
- {this.props.icon ? <Icon name={this.props.icon} /> : null}
51
+ {this.props.isLoading ? <Spinner size="mini" /> : null}
52
+ {this.props.icon && !this.props.isLoading ? <Icon name={this.props.icon} /> : null}
47
53
  {this.props.iconOnly ? null : this.props.text}
48
54
  </button>
49
55
  );
@@ -4,7 +4,7 @@ import nextId from "react-id-generator";
4
4
 
5
5
  interface IProps {
6
6
  value?: string;
7
- label?: string;
7
+ label: string;
8
8
  maxLength?: number;
9
9
  info?: string;
10
10
  error?: string;
@@ -12,6 +12,7 @@ interface IProps {
12
12
  disabled?: boolean;
13
13
  invalid?: boolean;
14
14
  inlineLabel?: boolean;
15
+ labelHidden?: boolean;
15
16
  onChange(newValue: string): void;
16
17
  }
17
18
 
@@ -34,15 +35,25 @@ export class Input extends React.Component<IProps, IState> {
34
35
 
35
36
  htmlId = nextId();
36
37
 
37
- handleChange(event: React.ChangeEvent<HTMLInputElement>) {
38
- this.setState({ value: event.target.value });
39
- this.props.onChange(event.target.value);
38
+ handleData(value: string) {
39
+ this.setState({ value: value ?? '' });
40
+ this.props.onChange(value ?? '');
40
41
 
41
42
  if (this.props.maxLength && !this.props.invalid) {
42
- this.setState({ invalid: event.target.value.length > this.props.maxLength });
43
+ this.setState({ invalid: value.length > this.props.maxLength });
43
44
  }
44
45
  }
45
46
 
47
+ handleChange(event: React.ChangeEvent<HTMLInputElement>) {
48
+ this.handleData(event.target.value);
49
+ }
50
+
51
+ componentDidUpdate(prevProps: IProps) {
52
+ if (this.props.value !== prevProps.value) {
53
+ this.handleData(this.props.value ?? '');
54
+ }
55
+ }
56
+
46
57
  render() {
47
58
  const classes = classNames('sd-input', {
48
59
  'sd-input--inline-label': this.props.inlineLabel,
@@ -50,11 +61,15 @@ export class Input extends React.Component<IProps, IState> {
50
61
  'sd-input--disabled': this.props.disabled,
51
62
  'sd-input--invalid': this.props.invalid || this.state.invalid,
52
63
  });
64
+ const labelClasses = classNames('sd-input__label', {
65
+ 'a11y-only': this.props.labelHidden,
66
+ });
53
67
 
54
68
  return (
55
69
  <div className={classes}>
56
- {this.props.label ? <label className='sd-input__label'
57
- htmlFor={this.htmlId} id={this.htmlId + 'label'}>{this.props.label}</label> : null}
70
+ <label className={labelClasses} htmlFor={this.htmlId} id={this.htmlId + 'label'}>
71
+ {this.props.label}
72
+ </label>
58
73
 
59
74
  <input className='sd-input__input'
60
75
  type='text'
@@ -4,13 +4,14 @@ import nextId from "react-id-generator";
4
4
 
5
5
  interface ISelect {
6
6
  value?: string;
7
- label?: string;
7
+ label: string;
8
8
  info?: string;
9
9
  error?: string;
10
10
  required?: boolean;
11
11
  disabled?: boolean;
12
12
  invalid?: boolean;
13
13
  inlineLabel?: boolean;
14
+ labelHidden?: boolean;
14
15
  onChange(newValue: string): void;
15
16
  }
16
17
 
@@ -38,6 +39,13 @@ class Select extends React.Component<ISelect, IState> {
38
39
  this.props.onChange(event.target.value);
39
40
  }
40
41
 
42
+ componentDidUpdate(prevProps: any) {
43
+ if (this.props.value !== prevProps.value) {
44
+ this.setState({ value: this.props.value ?? '' });
45
+ this.props.onChange(this.props.value ?? '');
46
+ }
47
+ }
48
+
41
49
  render() {
42
50
  const classes = classNames('sd-input sd-input--is-select', {
43
51
  'sd-input--inline-label': this.props.inlineLabel,
@@ -45,11 +53,13 @@ class Select extends React.Component<ISelect, IState> {
45
53
  'sd-input--disabled': this.props.disabled,
46
54
  'sd-input--invalid': this.props.invalid || this.state.invalid,
47
55
  });
56
+ const labelClasses = classNames('sd-input__label', {
57
+ 'a11y-only': this.props.labelHidden,
58
+ });
48
59
 
49
60
  return (
50
61
  <div className={classes}>
51
- {this.props.label ?
52
- <label className='sd-input__label' htmlFor={this.htmlId}>{this.props.label}</label> : null}
62
+ <label className={labelClasses} htmlFor={this.htmlId}>{this.props.label}</label>
53
63
 
54
64
  <select className='sd-input__select'
55
65
  id={this.htmlId}
@@ -0,0 +1,33 @@
1
+ import * as React from 'react';
2
+ import classNames from 'classnames';
3
+ import './../../app/styles/_spinner.scss';
4
+
5
+ class LoadingOverlay extends React.PureComponent {
6
+ render() {
7
+ return (
8
+ <div className="sd-loading-overlay--plain">
9
+ {this.props.children}
10
+ </div>
11
+ );
12
+ }
13
+ }
14
+
15
+ interface IProps {
16
+ size?: 'mini' | 'small' | 'medium' | 'large'; // defaults to 'small'
17
+ }
18
+
19
+ class Spinner extends React.PureComponent<IProps> {
20
+ render() {
21
+ let classes = classNames('sd-spinner', {
22
+ 'sd-spinner--small': this.props.size === undefined,
23
+ [`sd-spinner--${this.props.size}`]: this.props.size || this.props.size !== undefined,
24
+ });
25
+ return (
26
+ <svg role="progressbar" className={classes} viewBox="0 0 50 50">
27
+ <circle className="sd-spinner__path" cx="25" cy="25" r="20" fill="none" stroke-width="5"></circle>
28
+ </svg>
29
+ );
30
+ }
31
+ }
32
+
33
+ export { Spinner, LoadingOverlay };
@@ -1,84 +1,133 @@
1
1
  import * as React from 'react';
2
2
  import classNames from 'classnames';
3
+
3
4
  interface ITabs {
4
5
  size?: 'normal' | 'large' | 'small'; // defaults to 'normal'
5
6
  theme?: 'light' | 'dark'; // defaults to 'light'
6
7
  ariaLabel?: string;
7
8
  children: Array<any>;
8
- onClick(i: number): void;
9
+ onClick(id: string): void;
10
+ tabs?: Array<{label: string, id: string}>;
11
+ activePanel: string;
9
12
  }
10
13
 
11
14
  interface ITabLabel {
12
- label: string;
13
- indexValue: number;
15
+ label?: string;
16
+ id: string;
17
+ children: Array<any>;
14
18
  }
15
19
 
16
20
  interface ITabContent {
17
21
  theme?: 'light' | 'dark'; // defaults to 'light'
18
- children: any;
19
- activePanel: number;
22
+ children: Array<any>;
23
+ activePanel: string;
24
+ tabs?: Array<{content: React.ReactNode | string, id: string}>;
20
25
  }
21
26
 
22
27
  interface ITabPanel {
23
- indexValue: number;
24
- children: any;
28
+ id: string;
29
+ children: Array<any>;
25
30
  }
26
31
 
27
- export const TabLabel = ({ label }: ITabLabel) => {
28
- return (
29
- <span>{label}</span>
30
- );
31
- };
32
-
33
- export const Tabs = ({ size, theme, ariaLabel, children, onClick }: ITabs) => {
32
+ export const TabNav = ({ size, theme, ariaLabel, children, onClick, tabs, activePanel }: ITabs) => {
34
33
  const [index, setIndex] = React.useState(0);
35
34
 
36
- function handleSelected(i: number) {
37
- setIndex(i);
38
- handleClick(i);
35
+ function goTo(id: string) {
36
+ if (tabs) {
37
+ const refLabel = tabs?.find((item) => item.id === id);
38
+
39
+ if (refLabel) {
40
+ setIndex(tabs.indexOf(refLabel));
41
+ }
42
+ } else {
43
+ const refLabel = children.find((item) => item.props.id === id);
44
+ setIndex(children.indexOf(refLabel));
45
+ }
39
46
  }
40
47
 
41
- const handleClick = (i: number) => {
42
- onClick(i);
43
- };
48
+ React.useEffect(() => {
49
+ goTo(activePanel ? activePanel : (tabs ? tabs[0].id : children[0].props.id));
50
+ }, []);
51
+
52
+ function handleSelected(id: string) {
53
+ goTo(id);
54
+ onClick(id);
55
+ }
44
56
 
45
57
  let classes = classNames('sd-nav-tabs', {
46
58
  [`sd-nav-tabs--${size}`]: size && size !== undefined,
47
59
  'sd-nav-tabs--ui-dark': theme === 'dark',
48
60
  });
49
- return (
61
+ return (
50
62
  <div className={classes} role='tablist' aria-label={ariaLabel ? ariaLabel : 'tabs'}>
51
- {children.map((item, i) =>
52
- <button
53
- key={i}
54
- aria-controls={'tabpanel-' + i}
55
- className={'sd-nav-tabs__tab' + (index === i ? ' sd-nav-tabs__tab--active' : '')}
56
- onClick={() => handleSelected(i)}
57
- role='tab'
58
- aria-selected={index === i ? 'true' : 'false'} >
59
- {item}
60
- </button>)
61
- }
63
+ {tabs ?
64
+ tabs.map((item, i) =>
65
+ <button
66
+ id={`tab-${item.id}`}
67
+ key={i}
68
+ aria-controls={'tabpanel-' + item.id}
69
+ className={'sd-nav-tabs__tab' + (index === i ? ' sd-nav-tabs__tab--active' : '')}
70
+ onClick={() => handleSelected(item.id)}
71
+ role='tab'
72
+ aria-selected={index === i ? 'true' : 'false'} >
73
+ {item.label}
74
+ </button>)
75
+ :
76
+ children.map((item, i) =>
77
+ <button
78
+ id={`tab-${item.props.id}`}
79
+ key={i}
80
+ aria-controls={'tabpanel-' + item.props.id}
81
+ className={'sd-nav-tabs__tab' + (index === i ? ' sd-nav-tabs__tab--active' : '')}
82
+ onClick={() => handleSelected(item.props.id)}
83
+ role='tab'
84
+ aria-selected={index === i ? 'true' : 'false'} >
85
+ {item}
86
+ </button>)
87
+ }
62
88
  </div>
63
89
  );
64
90
  };
65
91
 
66
- export const TabContent = ({ theme, children, activePanel }: ITabContent) => {
92
+ export const TabContent = ({ theme, children, activePanel, tabs }: ITabContent) => {
93
+ if (!activePanel && tabs) {
94
+ activePanel = tabs[0].id;
95
+ } else if (!activePanel && children) {
96
+ activePanel = children[0].props.id;
97
+ }
98
+
67
99
  return (
68
100
  <div className={'sd-nav-tabs__content' +
69
101
  (theme === 'dark' ? ' sd-nav-tabs__content--ui-dark' : '')}>
70
- {children.map((panel: any, i: number) =>
71
- panel.props.indexValue === activePanel ?
72
- <div className='sd-nav-tabs__pane' role='tabpanel' aria-labelledby={'tab-' + activePanel} key={i}>
73
- {panel}
74
- </div> : null)}
102
+ {tabs ?
103
+ tabs.map((panel: any, i: number) =>
104
+ panel.id === activePanel ?
105
+ <div className='sd-nav-tabs__pane' role='tabpanel' aria-labelledby={`tab-${panel.id}`} key={i}>
106
+ {panel.content}
107
+ </div> : null)
108
+ :
109
+ children.map((panel: any, i: number) =>
110
+ panel.props.id === activePanel ?
111
+ <div className='sd-nav-tabs__pane'
112
+ role='tabpanel'
113
+ aria-labelledby={`tab-${panel.props.id}`}
114
+ key={i}>
115
+ {panel}
116
+ </div> : null)
117
+ }
75
118
  </div>
76
119
  );
77
120
  };
78
121
 
79
- export const TabPanel = ({ children, indexValue }: ITabPanel) => {
122
+ export const TabItem = ({ children }: ITabLabel) => {
123
+ return (
124
+ <span>{children}</span>
125
+ );
126
+ };
127
+
128
+ export const TabPanel = ({ children, id }: ITabPanel) => {
80
129
  return (
81
- <React.Fragment key={indexValue}>
130
+ <React.Fragment key={id}>
82
131
  {children}
83
132
  </React.Fragment>
84
133
  );
@@ -5,6 +5,8 @@ interface ITabList {
5
5
  size?: 'normal' | 'large' | 'small'; // defaults to 'normal'
6
6
  theme?: 'light' | 'dark'; // defaults to 'light'
7
7
  children: Array<any>;
8
+ selected?: string;
9
+ tabs?: Array<{label: string, content: React.ReactNode | string, id?: string}>;
8
10
  }
9
11
 
10
12
  interface IState {
@@ -12,7 +14,8 @@ interface IState {
12
14
  }
13
15
 
14
16
  interface ITab {
15
- label: string;
17
+ id?: string;
18
+ label?: string;
16
19
  }
17
20
 
18
21
  class Tab extends React.PureComponent<ITab> {
@@ -35,17 +38,45 @@ class TabList extends React.PureComponent<ITabList, IState> {
35
38
  this.goTo = this.goTo.bind(this);
36
39
  }
37
40
 
41
+ componentDidMount() {
42
+ if (this.props.selected) {
43
+ this.goTo(this.props.selected ?? '');
44
+ }
45
+ }
46
+
38
47
  private handleChange(index: number) {
39
48
  this.setState({
40
49
  index: index,
41
50
  });
42
51
  }
43
52
 
44
- public goTo(label: string) {
45
- const refLabel = this.props.children.find((item) => item.props.label === label);
46
- this.setState({
47
- index: this.props.children.indexOf(refLabel),
48
- });
53
+ public goTo(index: string) {
54
+ if (this.props.tabs) {
55
+ const refLabel = this.props.tabs?.find((item) => item.id === index);
56
+ this.setState({
57
+ index: refLabel ? this.props.tabs?.indexOf(refLabel) : 0,
58
+ });
59
+ } else {
60
+ const refLabel = this.props.children.find((item) => item.props.id === index);
61
+ this.setState({
62
+ index: this.props.children.indexOf(refLabel),
63
+ });
64
+ }
65
+ }
66
+
67
+ private navBar(arr: any) {
68
+ return arr.map((item: any, index: any) => {
69
+ let label = this.props.tabs ? item.label : item.props.label;
70
+ return <button
71
+ id={`TabList-${index}`}
72
+ key={index}
73
+ onClick={() => this.handleChange(index)}
74
+ role='tab'
75
+ aria-selected={this.state.index === index ? 'true' : 'false'}
76
+ className={'sd-nav-tabs__tab' + (this.state.index === index ? ' sd-nav-tabs__tab--active' : '')}>
77
+ <span>{label}</span>
78
+ </button>;
79
+ });
49
80
  }
50
81
 
51
82
  render() {
@@ -53,23 +84,17 @@ class TabList extends React.PureComponent<ITabList, IState> {
53
84
  [`sd-nav-tabs--${this.props.size}`]: this.props.size && this.props.size !== undefined,
54
85
  'sd-nav-tabs--ui-dark': this.props.theme === 'dark',
55
86
  });
56
-
57
87
  return (
58
88
  <React.Fragment>
59
89
  <div className={classes} role='tablist'>
60
- {this.props.children.map((item, index) =>
61
- <button
62
- key={index}
63
- onClick={() => this.handleChange(index)}
64
- role='tab'
65
- aria-selected={this.state.index === index ? 'true' : 'false'}
66
- className={'sd-nav-tabs__tab' + (this.state.index === index ? ' sd-nav-tabs__tab--active' : '')}>
67
- <span>{item.props.label}</span>
68
- </button>)}
90
+ {this.props.tabs ? this.navBar(this.props.tabs) : this.navBar(this.props.children)}
69
91
  </div>
70
- <div className={'sd-nav-tabs__content' +
92
+ <div aria-labelledby={`TabList-${this.state.index}`}
93
+ role='tabpanel' className={'sd-nav-tabs__content' +
71
94
  (this.props.theme === 'dark' ? ' sd-nav-tabs__content--ui-dark' : '')}>
72
- {this.props.children[this.state.index]}
95
+ {this.props.tabs
96
+ ? <Tab id={this.props.tabs[this.state.index].id}>{this.props.tabs[this.state.index].content}</Tab>
97
+ : this.props.children[this.state.index]}
73
98
  </div>
74
99
  </React.Fragment>
75
100
  );
@@ -16,11 +16,11 @@ export const Tag = ({ text, keyValue, shade, shape, readOnly, onClick }: IProps)
16
16
  'tag-label--square': shape === 'square',
17
17
  });
18
18
  return (
19
- <div className={classes} key={keyValue}>
19
+ <span className={classes} key={keyValue}>
20
20
  {text}
21
21
  {!readOnly ? <button className='tag-label__remove' onClick={onClick}>
22
22
  <i className='icon-close-small'></i>
23
23
  </button> : null}
24
- </div>
24
+ </span>
25
25
  );
26
26
  };
@@ -47,7 +47,7 @@ export { DropdownLabel } from './components/DropdownFirst';
47
47
  export { DropdownDivider } from './components/DropdownFirst';
48
48
  export { Dropdown } from './components/Dropdown';
49
49
  export { Tag } from './components/Tag';
50
- export { TabLabel, TabPanel, TabContent, Tabs } from './components/TabCustom';
50
+ export { TabItem, TabPanel, TabContent, TabNav } from './components/TabCustom';
51
51
  export { EmptyState } from './components/EmptyState';
52
52
  export { Autocomplete } from './components/Autocomplete';
53
53
  export { DonutChart } from './components/DonutChart';
@@ -1162,7 +1162,42 @@ pre[class*="language-"] {
1162
1162
  .docs-page__section {
1163
1163
  max-width: 80%;
1164
1164
  }
1165
- }@charset "UTF-8";
1165
+ }.sd-spinner {
1166
+ animation: rotate 2s linear infinite;
1167
+ z-index: 1; }
1168
+ .sd-spinner--mini {
1169
+ width: 1.8rem;
1170
+ height: 1.8rem; }
1171
+ .sd-spinner--small {
1172
+ width: 2.4rem;
1173
+ height: 2.4rem; }
1174
+ .sd-spinner--medium {
1175
+ width: 3.2rem;
1176
+ height: 3.2rem; }
1177
+ .sd-spinner--large {
1178
+ width: 4.8rem;
1179
+ height: 4.8rem; }
1180
+
1181
+ .sd-spinner__path {
1182
+ stroke: var(--color-text-light);
1183
+ stroke-linecap: round;
1184
+ animation: dash 1.5s ease-in-out infinite; }
1185
+
1186
+ @keyframes rotate {
1187
+ 100% {
1188
+ transform: rotate(360deg); } }
1189
+
1190
+ @keyframes dash {
1191
+ 0% {
1192
+ stroke-dasharray: 1, 150;
1193
+ stroke-dashoffset: 0; }
1194
+ 50% {
1195
+ stroke-dasharray: 90, 150;
1196
+ stroke-dashoffset: -32; }
1197
+ 100% {
1198
+ stroke-dasharray: 90, 150;
1199
+ stroke-dashoffset: -124; } }
1200
+ @charset "UTF-8";
1166
1201
  :root {
1167
1202
  --sd-slugline-color: #005b7f; }
1168
1203