import React, { useCallback, useState } from 'react';
import Tabs from 'devextreme-react/tabs';
import SelectBox from 'devextreme-react/select-box';
import type { SelectBoxTypes } from 'devextreme-react/select-box';
import CheckBox from 'devextreme-react/check-box';
import type { CheckBoxTypes } from 'devextreme-react/check-box';
import type { Orientation, TabsIconPosition, TabsStyle } from 'devextreme/common';
import {
orientations,
tabsText,
stylingModeLabel,
iconPositionLabel,
tabsIconAndText,
stylingModes,
iconPositions,
tabsIcon,
orientationLabel,
} from './data.ts';
const STRICT_WIDTH_CLASS = 'strict-width';
interface OptionWrapperProps {
caption?: string;
children?: React.ReactNode;
}
function OptionWrapper({ caption, children }: OptionWrapperProps) {
return (
<div className="option">
{caption && <span>{caption}</span>}
{children}
</div>
);
}
const App = () => {
const [orientation, setOrientation] = useState<Orientation>(orientations[0]);
const [stylingMode, setStylingMode] = useState<TabsStyle>(stylingModes[1]);
const [iconPosition, setIconPosition] = useState<TabsIconPosition>(iconPositions[0]);
const [showNavigation, setShowNavigation] = useState<boolean>(false);
const [scrollContent, setScrollContent] = useState<boolean>(false);
const [fullWidth, setFullWidth] = useState<boolean>(false);
const [width, setWidth] = useState<string>('auto');
const [rtlEnabled, setRtlEnabled] = useState<boolean>(false);
const [widgetWrapperClasses, setWidgetWrapperClasses] = useState<string>('widget-wrapper widget-wrapper-horizontal');
const enforceWidthConstraint = useCallback(
(shouldRestrictWidth: boolean): void => {
const callback = (prevClasses: string): string => {
const restClasses = prevClasses.split(' ').filter((className: string): boolean => className !== STRICT_WIDTH_CLASS).join(' ');
const strictWidthClass = shouldRestrictWidth ? STRICT_WIDTH_CLASS : '';
return `${restClasses} ${strictWidthClass}`;
};
setWidgetWrapperClasses(callback);
}, [],
);
const stylingModeChanged = useCallback(({ value }: SelectBoxTypes.ValueChangedEvent): void => {
setStylingMode(value);
}, []);
const iconPositionChanged = useCallback(({ value }: SelectBoxTypes.ValueChangedEvent): void => {
setIconPosition(value);
}, []);
const orientationChanged = useCallback(({ value }: SelectBoxTypes.ValueChangedEvent): void => {
const isVertical = value === 'vertical';
const callback = (prevClasses: string): string => {
const restClasses = prevClasses
.split(' ')
.filter((className) => (className !== (isVertical ? 'widget-wrapper-horizontal' : 'widget-wrapper-vertical')))
.join(' ');
return `${restClasses} widget-wrapper-${value}`;
};
setWidgetWrapperClasses(callback);
setOrientation(value);
}, []);
const showNavigationChanged = useCallback(({ value }: CheckBoxTypes.ValueChangedEvent): void => {
const shouldRestrictWidth = value || scrollContent;
enforceWidthConstraint(shouldRestrictWidth);
setShowNavigation(value);
}, [scrollContent, enforceWidthConstraint]);
const scrollContentChanged = useCallback(({ value }: CheckBoxTypes.ValueChangedEvent): void => {
const shouldRestrictWidth = value || showNavigation;
enforceWidthConstraint(shouldRestrictWidth);
setScrollContent(value);
}, [showNavigation, enforceWidthConstraint]);
const fullWidthChanged = useCallback(({ value }: CheckBoxTypes.ValueChangedEvent): void => {
setFullWidth(value);
setWidth(value ? '100%' : 'auto');
}, []);
const rtlEnabledChanged = useCallback(({ value }: CheckBoxTypes.ValueChangedEvent): void => {
setRtlEnabled(value);
}, []);
return (
<div className="tabs-demo">
<div className="widget-container">
<div className={widgetWrapperClasses}>
<Tabs
id="withText"
width={width}
defaultSelectedIndex={0}
rtlEnabled={rtlEnabled}
dataSource={tabsText}
scrollByContent={scrollContent}
showNavButtons={showNavigation}
orientation={orientation}
stylingMode={stylingMode}
iconPosition={iconPosition}
/>
<Tabs
id="withIconAndText"
width={width}
defaultSelectedIndex={0}
rtlEnabled={rtlEnabled}
dataSource={tabsIconAndText}
scrollByContent={scrollContent}
showNavButtons={showNavigation}
orientation={orientation}
stylingMode={stylingMode}
iconPosition={iconPosition}
/>
<Tabs
id="withIcon"
width={width}
defaultSelectedIndex={0}
rtlEnabled={rtlEnabled}
dataSource={tabsIcon}
scrollByContent={scrollContent}
showNavButtons={showNavigation}
orientation={orientation}
stylingMode={stylingMode}
iconPosition={iconPosition}
/>
</div>
</div>
<div className="options">
<div className="caption">Options</div>
<OptionWrapper caption='Orientation'>
<SelectBox
items={orientations}
inputAttr={orientationLabel}
value={orientation}
onValueChanged={orientationChanged}
/>
</OptionWrapper>
<OptionWrapper caption='Styling mode'>
<SelectBox
items={stylingModes}
inputAttr={stylingModeLabel}
value={stylingMode}
onValueChanged={stylingModeChanged}
/>
</OptionWrapper>
<OptionWrapper caption='Icon position'>
<SelectBox
items={iconPositions}
inputAttr={iconPositionLabel}
value={iconPosition}
onValueChanged={iconPositionChanged}
/>
</OptionWrapper>
<div className="option">
<CheckBox
id="show-navigation-buttons"
text="Show navigation buttons"
value={showNavigation}
onValueChanged={showNavigationChanged}
/>
</div>
<div className="option">
<CheckBox
text="Scroll content"
value={scrollContent}
onValueChanged={scrollContentChanged}
/>
</div>
<div className="option">
<CheckBox
text="Full width"
value={fullWidth}
onValueChanged={fullWidthChanged}
/>
</div>
<div className="option">
<CheckBox
text="Right-to-left mode"
value={rtlEnabled}
onValueChanged={rtlEnabledChanged}
/>
</div>
</div>
</div>
);
};
export default App;
import React, { useCallback, useState } from 'react';
import Tabs from 'devextreme-react/tabs';
import SelectBox from 'devextreme-react/select-box';
import CheckBox from 'devextreme-react/check-box';
import {
orientations,
tabsText,
stylingModeLabel,
iconPositionLabel,
tabsIconAndText,
stylingModes,
iconPositions,
tabsIcon,
orientationLabel,
} from './data.js';
const STRICT_WIDTH_CLASS = 'strict-width';
function OptionWrapper({ caption, children }) {
return (
<div className="option">
{caption && <span>{caption}</span>}
{children}
</div>
);
}
const App = () => {
const [orientation, setOrientation] = useState(orientations[0]);
const [stylingMode, setStylingMode] = useState(stylingModes[1]);
const [iconPosition, setIconPosition] = useState(iconPositions[0]);
const [showNavigation, setShowNavigation] = useState(false);
const [scrollContent, setScrollContent] = useState(false);
const [fullWidth, setFullWidth] = useState(false);
const [width, setWidth] = useState('auto');
const [rtlEnabled, setRtlEnabled] = useState(false);
const [widgetWrapperClasses, setWidgetWrapperClasses] = useState(
'widget-wrapper widget-wrapper-horizontal',
);
const enforceWidthConstraint = useCallback((shouldRestrictWidth) => {
const callback = (prevClasses) => {
const restClasses = prevClasses
.split(' ')
.filter((className) => className !== STRICT_WIDTH_CLASS)
.join(' ');
const strictWidthClass = shouldRestrictWidth ? STRICT_WIDTH_CLASS : '';
return `${restClasses} ${strictWidthClass}`;
};
setWidgetWrapperClasses(callback);
}, []);
const stylingModeChanged = useCallback(({ value }) => {
setStylingMode(value);
}, []);
const iconPositionChanged = useCallback(({ value }) => {
setIconPosition(value);
}, []);
const orientationChanged = useCallback(({ value }) => {
const isVertical = value === 'vertical';
const callback = (prevClasses) => {
const restClasses = prevClasses
.split(' ')
.filter(
(className) =>
className !== (isVertical ? 'widget-wrapper-horizontal' : 'widget-wrapper-vertical'),
)
.join(' ');
return `${restClasses} widget-wrapper-${value}`;
};
setWidgetWrapperClasses(callback);
setOrientation(value);
}, []);
const showNavigationChanged = useCallback(
({ value }) => {
const shouldRestrictWidth = value || scrollContent;
enforceWidthConstraint(shouldRestrictWidth);
setShowNavigation(value);
},
[scrollContent, enforceWidthConstraint],
);
const scrollContentChanged = useCallback(
({ value }) => {
const shouldRestrictWidth = value || showNavigation;
enforceWidthConstraint(shouldRestrictWidth);
setScrollContent(value);
},
[showNavigation, enforceWidthConstraint],
);
const fullWidthChanged = useCallback(({ value }) => {
setFullWidth(value);
setWidth(value ? '100%' : 'auto');
}, []);
const rtlEnabledChanged = useCallback(({ value }) => {
setRtlEnabled(value);
}, []);
return (
<div className="tabs-demo">
<div className="widget-container">
<div className={widgetWrapperClasses}>
<Tabs
id="withText"
width={width}
defaultSelectedIndex={0}
rtlEnabled={rtlEnabled}
dataSource={tabsText}
scrollByContent={scrollContent}
showNavButtons={showNavigation}
orientation={orientation}
stylingMode={stylingMode}
iconPosition={iconPosition}
/>
<Tabs
id="withIconAndText"
width={width}
defaultSelectedIndex={0}
rtlEnabled={rtlEnabled}
dataSource={tabsIconAndText}
scrollByContent={scrollContent}
showNavButtons={showNavigation}
orientation={orientation}
stylingMode={stylingMode}
iconPosition={iconPosition}
/>
<Tabs
id="withIcon"
width={width}
defaultSelectedIndex={0}
rtlEnabled={rtlEnabled}
dataSource={tabsIcon}
scrollByContent={scrollContent}
showNavButtons={showNavigation}
orientation={orientation}
stylingMode={stylingMode}
iconPosition={iconPosition}
/>
</div>
</div>
<div className="options">
<div className="caption">Options</div>
<OptionWrapper caption="Orientation">
<SelectBox
items={orientations}
inputAttr={orientationLabel}
value={orientation}
onValueChanged={orientationChanged}
/>
</OptionWrapper>
<OptionWrapper caption="Styling mode">
<SelectBox
items={stylingModes}
inputAttr={stylingModeLabel}
value={stylingMode}
onValueChanged={stylingModeChanged}
/>
</OptionWrapper>
<OptionWrapper caption="Icon position">
<SelectBox
items={iconPositions}
inputAttr={iconPositionLabel}
value={iconPosition}
onValueChanged={iconPositionChanged}
/>
</OptionWrapper>
<div className="option">
<CheckBox
id="show-navigation-buttons"
text="Show navigation buttons"
value={showNavigation}
onValueChanged={showNavigationChanged}
/>
</div>
<div className="option">
<CheckBox
text="Scroll content"
value={scrollContent}
onValueChanged={scrollContentChanged}
/>
</div>
<div className="option">
<CheckBox
text="Full width"
value={fullWidth}
onValueChanged={fullWidthChanged}
/>
</div>
<div className="option">
<CheckBox
text="Right-to-left mode"
value={rtlEnabled}
onValueChanged={rtlEnabledChanged}
/>
</div>
</div>
</div>
);
};
export default App;
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App.tsx';
ReactDOM.render(
<App />,
document.getElementById('app'),
);
import type { Orientation, TabsIconPosition, TabsStyle } from 'devextreme/common';
import type { TabItem } from './types.ts';
export const tabsText: TabItem[] = [
{
id: 0,
text: 'User',
},
{
id: 1,
text: 'Analytics',
},
{
id: 2,
text: 'Clients',
},
{
id: 3,
text: 'Orders',
},
{
id: 4,
text: 'Favorites',
},
{
id: 5,
text: 'Search',
},
];
export const tabsIconAndText: TabItem[] = [
{
id: 0,
text: 'User',
icon: 'user',
},
{
id: 1,
text: 'Analytics',
icon: 'chart',
},
{
id: 2,
text: 'Clients',
icon: 'accountbox',
},
{
id: 3,
text: 'Orders',
icon: 'ordersbox',
},
{
id: 4,
text: 'Favorites',
icon: 'bookmark',
},
{
id: 5,
text: 'Search',
icon: 'search',
},
];
export const tabsIcon: TabItem[] = [
{
id: 0,
icon: 'user',
},
{
id: 1,
icon: 'chart',
},
{
id: 2,
icon: 'accountbox',
},
{
id: 3,
icon: 'ordersbox',
},
{
id: 4,
icon: 'bookmark',
},
{
id: 5,
icon: 'search',
},
];
export const orientations: Orientation[] = ['horizontal', 'vertical'];
export const stylingModes: TabsStyle[] = [
'primary',
'secondary',
];
export const orientationLabel = { 'aria-label': 'Orientation' };
export const stylingModeLabel = { 'aria-label': 'Styling Mode' };
export const iconPositionLabel = { 'aria-label': 'Icon Position' };
export const iconPositions: TabsIconPosition[] = [
'top',
'start',
'end',
'bottom',
];
import type { Item } from 'devextreme/ui/tabs';
export type TabItem = Item & { id: number };
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App.js';
ReactDOM.render(<App />, document.getElementById('app'));
export const tabsText = [
{
id: 0,
text: 'User',
},
{
id: 1,
text: 'Analytics',
},
{
id: 2,
text: 'Clients',
},
{
id: 3,
text: 'Orders',
},
{
id: 4,
text: 'Favorites',
},
{
id: 5,
text: 'Search',
},
];
export const tabsIconAndText = [
{
id: 0,
text: 'User',
icon: 'user',
},
{
id: 1,
text: 'Analytics',
icon: 'chart',
},
{
id: 2,
text: 'Clients',
icon: 'accountbox',
},
{
id: 3,
text: 'Orders',
icon: 'ordersbox',
},
{
id: 4,
text: 'Favorites',
icon: 'bookmark',
},
{
id: 5,
text: 'Search',
icon: 'search',
},
];
export const tabsIcon = [
{
id: 0,
icon: 'user',
},
{
id: 1,
icon: 'chart',
},
{
id: 2,
icon: 'accountbox',
},
{
id: 3,
icon: 'ordersbox',
},
{
id: 4,
icon: 'bookmark',
},
{
id: 5,
icon: 'search',
},
];
export const orientations = ['horizontal', 'vertical'];
export const stylingModes = ['primary', 'secondary'];
export const orientationLabel = { 'aria-label': 'Orientation' };
export const stylingModeLabel = { 'aria-label': 'Styling Mode' };
export const iconPositionLabel = { 'aria-label': 'Icon Position' };
export const iconPositions = ['top', 'start', 'end', 'bottom'];
<!DOCTYPE html>
<html lang="en">
<head>
<title>DevExtreme Demo</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0" />
<link rel="stylesheet" type="text/css" href="https://cdn3.devexpress.com/jslib/26.1.5/css/dx.light.css" />
<link rel="stylesheet" type="text/css" href="./bundle.css" />
</head>
<body class="dx-viewport">
<div class="demo-container">
<div id="app"></div>
</div>
<script src="bundles/vendor/react.vendor.js"></script>
<script src="./bundle.js"></script>
</body>
</html>
.tabs-demo {
display: flex;
}
.strict-width {
max-width: 340px;
}
.dx-theme-generic .strict-width {
max-width: 250px;
}
.widget-container {
display: flex;
align-items: center;
justify-content: center;
flex-grow: 1;
width: 100%;
min-width: 200px;
padding: 16px 32px;
overflow: clip;
}
.widget-wrapper {
display: inline-flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 80px;
width: 100%;
}
.widget-wrapper-vertical {
width: 100%;
flex-direction: row;
align-items: center;
}
.options {
display: flex;
flex-direction: column;
flex-shrink: 0;
padding: 20px;
background-color: rgba(191, 191, 191, 0.15);
}
.caption {
font-weight: 500;
font-size: 18px;
}
#show-navigation-buttons {
margin-top: 22px;
}
.option {
margin-top: 20px;
}
.dx-tabs {
max-width: 100%;
}
.dx-tabs-vertical {
height: 216px;
}
.dx-viewport:not(.dx-theme-generic) .dx-tabs-horizontal {
border-block-end: 1px solid rgb(225, 225, 225, 0.4);
}
.dx-viewport:not(.dx-theme-generic) .dx-tabs-vertical {
height: 232px;
border-inline-end: 1px solid rgb(225, 225, 225, 0.4);
}