@db-ux/react-core-components 4.7.0-tabs-34782eb → 4.7.1
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.
- package/CHANGELOG.md +5 -0
- package/agent/Tabs.md +30 -30
- package/dist/components/tab-item/model.d.ts +11 -28
- package/dist/components/tab-item/tab-item.d.ts +1 -1
- package/dist/components/tab-item/tab-item.js +50 -123
- package/dist/components/tab-list/model.d.ts +5 -15
- package/dist/components/tab-list/tab-list.d.ts +1 -1
- package/dist/components/tab-list/tab-list.js +4 -8
- package/dist/components/tab-panel/model.d.ts +3 -13
- package/dist/components/tab-panel/tab-panel.d.ts +1 -1
- package/dist/components/tab-panel/tab-panel.js +3 -2
- package/dist/components/tabs/model.d.ts +17 -51
- package/dist/components/tabs/tabs.d.ts +1 -1
- package/dist/components/tabs/tabs.js +115 -357
- package/dist/components/tooltip/tooltip.js +2 -1
- package/dist/shared/model.d.ts +5 -5
- package/dist/shared/model.js +1 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
package/agent/Tabs.md
CHANGED
|
@@ -16,56 +16,56 @@ function Tabs(props: any) {
|
|
|
16
16
|
<h2>1. Default Tabs</h2>
|
|
17
17
|
<DBTabs>
|
|
18
18
|
<DBTabList>
|
|
19
|
-
<DBTabItem>
|
|
20
|
-
<DBTabItem>
|
|
21
|
-
<DBTabItem>
|
|
19
|
+
<DBTabItem>Tab 1</DBTabItem>
|
|
20
|
+
<DBTabItem>Tab 2</DBTabItem>
|
|
21
|
+
<DBTabItem>Tab 3</DBTabItem>
|
|
22
22
|
</DBTabList>
|
|
23
|
-
<DBTabPanel>
|
|
24
|
-
<DBTabPanel>
|
|
25
|
-
<DBTabPanel>
|
|
23
|
+
<DBTabPanel>Tab Panel 1</DBTabPanel>
|
|
24
|
+
<DBTabPanel>Tab Panel 2</DBTabPanel>
|
|
25
|
+
<DBTabPanel>Tab Panel 3</DBTabPanel>
|
|
26
26
|
</DBTabs>
|
|
27
27
|
<h2>2. Behavior Variants</h2>
|
|
28
28
|
<DBTabs behavior="scrollbar">
|
|
29
29
|
<DBTabList>
|
|
30
|
-
<DBTabItem>
|
|
31
|
-
<DBTabItem>
|
|
32
|
-
<DBTabItem>
|
|
30
|
+
<DBTabItem>Tab 1</DBTabItem>
|
|
31
|
+
<DBTabItem>Tab 2</DBTabItem>
|
|
32
|
+
<DBTabItem>Tab 3</DBTabItem>
|
|
33
33
|
</DBTabList>
|
|
34
|
-
<DBTabPanel>
|
|
35
|
-
<DBTabPanel>
|
|
36
|
-
<DBTabPanel>
|
|
34
|
+
<DBTabPanel>Tab Panel 1</DBTabPanel>
|
|
35
|
+
<DBTabPanel>Tab Panel 2</DBTabPanel>
|
|
36
|
+
<DBTabPanel>Tab Panel 3</DBTabPanel>
|
|
37
37
|
</DBTabs>
|
|
38
38
|
<DBTabs behavior="arrows">
|
|
39
39
|
<DBTabList>
|
|
40
|
-
<DBTabItem>
|
|
41
|
-
<DBTabItem>
|
|
42
|
-
<DBTabItem>
|
|
40
|
+
<DBTabItem>Tab 1</DBTabItem>
|
|
41
|
+
<DBTabItem>Tab 2</DBTabItem>
|
|
42
|
+
<DBTabItem>Tab 3</DBTabItem>
|
|
43
43
|
</DBTabList>
|
|
44
|
-
<DBTabPanel>
|
|
45
|
-
<DBTabPanel>
|
|
46
|
-
<DBTabPanel>
|
|
44
|
+
<DBTabPanel>Tab Panel 1</DBTabPanel>
|
|
45
|
+
<DBTabPanel>Tab Panel 2</DBTabPanel>
|
|
46
|
+
<DBTabPanel>Tab Panel 3</DBTabPanel>
|
|
47
47
|
</DBTabs>
|
|
48
48
|
<h2>3. Initial Selected Index</h2>
|
|
49
49
|
<DBTabs initialSelectedIndex={1}>
|
|
50
50
|
<DBTabList>
|
|
51
|
-
<DBTabItem>
|
|
52
|
-
<DBTabItem>
|
|
53
|
-
<DBTabItem>
|
|
51
|
+
<DBTabItem>Tab 1</DBTabItem>
|
|
52
|
+
<DBTabItem>Tab 2</DBTabItem>
|
|
53
|
+
<DBTabItem>Tab 3</DBTabItem>
|
|
54
54
|
</DBTabList>
|
|
55
|
-
<DBTabPanel>
|
|
56
|
-
<DBTabPanel>
|
|
57
|
-
<DBTabPanel>
|
|
55
|
+
<DBTabPanel>Tab Panel 1</DBTabPanel>
|
|
56
|
+
<DBTabPanel>Tab Panel 2</DBTabPanel>
|
|
57
|
+
<DBTabPanel>Tab Panel 3</DBTabPanel>
|
|
58
58
|
</DBTabs>
|
|
59
59
|
<h2>4. Initial Selected Mode</h2>
|
|
60
60
|
<DBTabs initialSelectedMode="manually">
|
|
61
61
|
<DBTabList>
|
|
62
|
-
<DBTabItem>
|
|
63
|
-
<DBTabItem>
|
|
64
|
-
<DBTabItem>
|
|
62
|
+
<DBTabItem>Tab 1</DBTabItem>
|
|
63
|
+
<DBTabItem>Tab 2</DBTabItem>
|
|
64
|
+
<DBTabItem>Tab 3</DBTabItem>
|
|
65
65
|
</DBTabList>
|
|
66
|
-
<DBTabPanel>
|
|
67
|
-
<DBTabPanel>
|
|
68
|
-
<DBTabPanel>
|
|
66
|
+
<DBTabPanel>Tab Panel 1</DBTabPanel>
|
|
67
|
+
<DBTabPanel>Tab Panel 2</DBTabPanel>
|
|
68
|
+
<DBTabPanel>Tab Panel 3</DBTabPanel>
|
|
69
69
|
</DBTabs>
|
|
70
70
|
</>
|
|
71
71
|
);
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
import { ActiveProps,
|
|
1
|
+
import { ActiveProps, ChangeEventProps, ChangeEventState, GlobalProps, GlobalState, IconLeadingProps, IconProps, IconTrailingProps, InitializedState, NameProps, NameState, ShowIconLeadingProps, ShowIconProps, ShowIconTrailingProps } from '../../shared/model';
|
|
2
2
|
export type DBTabItemDefaultProps = {
|
|
3
|
+
/**
|
|
4
|
+
* To control the component
|
|
5
|
+
*/
|
|
6
|
+
checked?: boolean | string;
|
|
3
7
|
/**
|
|
4
8
|
* The disabled attribute can be set to keep a user from clicking on the tab-item.
|
|
5
9
|
*/
|
|
@@ -12,33 +16,12 @@ export type DBTabItemDefaultProps = {
|
|
|
12
16
|
* Define the text next to the icon specified via the icon Property to get hidden.
|
|
13
17
|
*/
|
|
14
18
|
noText?: boolean | string;
|
|
15
|
-
/**
|
|
16
|
-
* Set the tabIndex manually (internal use for roving tabindex).
|
|
17
|
-
*/
|
|
18
|
-
tabIndex?: number | string;
|
|
19
|
-
/**
|
|
20
|
-
* The id of the panel this tab controls (WAI-ARIA).
|
|
21
|
-
*/
|
|
22
|
-
ariaControls?: string;
|
|
23
|
-
/**
|
|
24
|
-
* Semantic value of this tab item. When set, onIndexChange will emit this value
|
|
25
|
-
* (via the onValueChange event) instead of only the numeric index.
|
|
26
|
-
* Useful for form binding (e.g. Angular FormControl, React useState).
|
|
27
|
-
*/
|
|
28
|
-
value?: string;
|
|
29
19
|
};
|
|
30
|
-
export type DBTabItemProps =
|
|
20
|
+
export type DBTabItemProps = GlobalProps & DBTabItemDefaultProps & IconProps & IconTrailingProps & IconLeadingProps & ShowIconLeadingProps & ShowIconTrailingProps & ActiveProps & ChangeEventProps<HTMLInputElement> & ShowIconProps & NameProps;
|
|
31
21
|
export type DBTabItemDefaultState = {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
_ariaSelectedListener: {
|
|
37
|
-
fn: (event: any) => void;
|
|
38
|
-
} | null;
|
|
39
|
-
handleClick: (event: any) => void;
|
|
40
|
-
isTruncated: boolean;
|
|
41
|
-
checkTruncation: () => void;
|
|
42
|
-
tooltipText: string;
|
|
22
|
+
_selected: boolean;
|
|
23
|
+
_listenerAdded: boolean;
|
|
24
|
+
boundSetSelectedOnChange?: (event: any) => void;
|
|
25
|
+
setSelectedOnChange: (event: any) => void;
|
|
43
26
|
};
|
|
44
|
-
export type DBTabItemState = DBTabItemDefaultState & GlobalState & InitializedState;
|
|
27
|
+
export type DBTabItemState = DBTabItemDefaultState & GlobalState & ChangeEventState<HTMLInputElement> & InitializedState & NameState;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
declare const DBTabItem: React.ForwardRefExoticComponent<Omit<React.InputHTMLAttributes<
|
|
2
|
+
declare const DBTabItem: React.ForwardRefExoticComponent<Omit<React.InputHTMLAttributes<any>, keyof import("../..").GlobalProps | "name" | "icon" | "showIcon" | "showIconLeading" | "showIconTrailing" | "iconLeading" | "iconTrailing" | keyof import("../..").ChangeEventProps<HTMLInputElement> | "active" | keyof import("./model").DBTabItemDefaultProps> & import("../..").GlobalProps & import("./model").DBTabItemDefaultProps & import("../..").IconProps & import("../..").IconTrailingProps & import("../..").IconLeadingProps & import("../..").ShowIconLeadingProps & import("../..").ShowIconTrailingProps & import("../..").ActiveProps & import("../..").ChangeEventProps<HTMLInputElement> & import("../..").ShowIconProps & import("../..").NameProps & React.RefAttributes<any>>;
|
|
3
3
|
export default DBTabItem;
|
|
@@ -2,150 +2,77 @@
|
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
import { filterPassingProps, getRootProps } from "../../utils/react";
|
|
4
4
|
import { useState, useRef, useEffect, forwardRef } from "react";
|
|
5
|
-
import { cls, getBoolean } from "../../utils";
|
|
6
|
-
import DBTooltip from "../tooltip/tooltip";
|
|
5
|
+
import { cls, getBoolean, getBooleanAsString } from "../../utils";
|
|
7
6
|
function DBTabItemFn(props, component) {
|
|
8
|
-
var _a, _b, _c, _d;
|
|
7
|
+
var _a, _b, _c, _d, _e, _f;
|
|
9
8
|
const _ref = component || useRef(component);
|
|
10
|
-
const
|
|
9
|
+
const [_selected, set_selected] = useState(() => false);
|
|
10
|
+
const [_name, set_name] = useState(() => undefined);
|
|
11
11
|
const [initialized, setInitialized] = useState(() => false);
|
|
12
|
-
const [
|
|
13
|
-
const [
|
|
14
|
-
function
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
: internalTabIndex;
|
|
12
|
+
const [_listenerAdded, set_listenerAdded] = useState(() => false);
|
|
13
|
+
const [boundSetSelectedOnChange, setBoundSetSelectedOnChange] = useState(() => undefined);
|
|
14
|
+
function setSelectedOnChange(event) {
|
|
15
|
+
event.stopPropagation();
|
|
16
|
+
set_selected(event.target === _ref.current);
|
|
18
17
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
props.onClick(event);
|
|
18
|
+
function handleNameAttribute() {
|
|
19
|
+
if (_ref.current) {
|
|
20
|
+
const setAttribute = _ref.current.setAttribute;
|
|
21
|
+
_ref.current.setAttribute = (attribute, value) => {
|
|
22
|
+
setAttribute.call(_ref.current, attribute, value);
|
|
23
|
+
if (attribute === "name") {
|
|
24
|
+
set_name(value);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
29
27
|
}
|
|
30
28
|
}
|
|
31
|
-
function
|
|
32
|
-
if (
|
|
33
|
-
|
|
34
|
-
const clientWidth = Math.ceil(_labelRef.current.clientWidth);
|
|
35
|
-
const truncated = scrollWidth > clientWidth + 1;
|
|
36
|
-
if (isTruncated !== truncated) {
|
|
37
|
-
setIsTruncated(truncated);
|
|
38
|
-
if (!truncated && _ref.current) {
|
|
39
|
-
if (_ref.current.hasAttribute("data-has-tooltip")) {
|
|
40
|
-
_ref.current.removeAttribute("data-has-tooltip");
|
|
41
|
-
}
|
|
42
|
-
if (_ref.current.hasAttribute("aria-describedby")) {
|
|
43
|
-
_ref.current.removeAttribute("aria-describedby");
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
setTooltipText(truncated
|
|
48
|
-
? props.label ||
|
|
49
|
-
_labelRef.current.innerText ||
|
|
50
|
-
_labelRef.current.textContent ||
|
|
51
|
-
""
|
|
52
|
-
: "");
|
|
29
|
+
function handleChange(event) {
|
|
30
|
+
if (props.onChange) {
|
|
31
|
+
props.onChange(event);
|
|
53
32
|
}
|
|
54
33
|
}
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
setBoundSetSelectedOnChange(() => setSelectedOnChange);
|
|
36
|
+
setInitialized(true);
|
|
37
|
+
}, []);
|
|
55
38
|
useEffect(() => {
|
|
56
39
|
var _a;
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
labelEl.dataset.label =
|
|
66
|
-
props.label || labelEl.innerText || labelEl.textContent || "";
|
|
67
|
-
}
|
|
68
|
-
if (_labelRef.current) {
|
|
69
|
-
const resizeObserver = new ResizeObserver(() => {
|
|
70
|
-
requestAnimationFrame(() => {
|
|
71
|
-
checkTruncation();
|
|
72
|
-
});
|
|
73
|
-
});
|
|
74
|
-
resizeObserver.observe(_labelRef.current);
|
|
75
|
-
set_resizeObserver(resizeObserver);
|
|
76
|
-
}
|
|
77
|
-
});
|
|
78
|
-
};
|
|
79
|
-
const hasIcon = props.showIcon && props.icon;
|
|
80
|
-
if (hasIcon && ((_a = document.fonts) === null || _a === void 0 ? void 0 : _a.ready)) {
|
|
81
|
-
document.fonts.ready.then(setupObserverAndCheck);
|
|
40
|
+
if (_ref.current && initialized && boundSetSelectedOnChange) {
|
|
41
|
+
handleNameAttribute();
|
|
42
|
+
setInitialized(false);
|
|
43
|
+
// deselect this tab when another tab in tablist is selected
|
|
44
|
+
if (!_listenerAdded) {
|
|
45
|
+
(_a = _ref.current
|
|
46
|
+
.closest("[role=tablist]")) === null || _a === void 0 ? void 0 : _a.addEventListener("change", boundSetSelectedOnChange);
|
|
47
|
+
set_listenerAdded(true);
|
|
82
48
|
}
|
|
83
|
-
|
|
84
|
-
|
|
49
|
+
// Initialize selected state from either active prop (set by parent) or checked attribute
|
|
50
|
+
if (props.active || _ref.current.checked) {
|
|
51
|
+
set_selected(true);
|
|
52
|
+
_ref.current.click();
|
|
85
53
|
}
|
|
86
54
|
}
|
|
87
|
-
|
|
88
|
-
const listener = (event) => {
|
|
89
|
-
setInternalActive(event.detail.selected);
|
|
90
|
-
if (props.tabIndex === undefined) {
|
|
91
|
-
if (event.detail.tabIndex !== undefined) {
|
|
92
|
-
setInternalTabIndex(event.detail.tabIndex);
|
|
93
|
-
}
|
|
94
|
-
else {
|
|
95
|
-
setInternalTabIndex(event.detail.selected ? 0 : -1);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
};
|
|
99
|
-
set_ariaSelectedListener({
|
|
100
|
-
fn: listener,
|
|
101
|
-
});
|
|
102
|
-
_ref.current.addEventListener("aria-selected-changed", listener);
|
|
103
|
-
}
|
|
104
|
-
}, []);
|
|
105
|
-
useEffect(() => {
|
|
106
|
-
if (props.active !== undefined) {
|
|
107
|
-
setInternalActive(getBoolean(props.active) || false);
|
|
108
|
-
}
|
|
109
|
-
}, [props.active]);
|
|
55
|
+
}, [_ref.current, initialized, boundSetSelectedOnChange]);
|
|
110
56
|
useEffect(() => {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
const isDisabled = getBoolean(props.disabled);
|
|
114
|
-
const disabledStr = isDisabled ? "true" : "false";
|
|
115
|
-
if (((_a = _ref.current) === null || _a === void 0 ? void 0 : _a.getAttribute("aria-disabled")) !== disabledStr) {
|
|
116
|
-
(_b = _ref.current) === null || _b === void 0 ? void 0 : _b.setAttribute("aria-disabled", disabledStr);
|
|
117
|
-
}
|
|
118
|
-
if (!isTruncated) {
|
|
119
|
-
if (_ref.current.hasAttribute("data-has-tooltip")) {
|
|
120
|
-
_ref.current.removeAttribute("data-has-tooltip");
|
|
121
|
-
}
|
|
122
|
-
if (_ref.current.hasAttribute("aria-describedby")) {
|
|
123
|
-
_ref.current.removeAttribute("aria-describedby");
|
|
124
|
-
}
|
|
125
|
-
}
|
|
57
|
+
if (props.name) {
|
|
58
|
+
set_name(props.name);
|
|
126
59
|
}
|
|
127
|
-
});
|
|
60
|
+
}, [props.name]);
|
|
128
61
|
useEffect(() => {
|
|
129
62
|
return () => {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
63
|
+
var _a;
|
|
64
|
+
if (_listenerAdded && _ref.current && boundSetSelectedOnChange) {
|
|
65
|
+
(_a = _ref.current
|
|
66
|
+
.closest("[role=tablist]")) === null || _a === void 0 ? void 0 : _a.removeEventListener("change", boundSetSelectedOnChange);
|
|
67
|
+
set_listenerAdded(false);
|
|
134
68
|
}
|
|
135
69
|
};
|
|
136
70
|
}, []);
|
|
137
|
-
return (React.createElement("
|
|
138
|
-
|
|
139
|
-
: "
|
|
140
|
-
!props.noText ? (React.createElement("span", { className: "db-tab-label", title: "", ref: _labelRef, "data-icon": getBoolean((_a = props.showIconLeading) !== null && _a !== void 0 ? _a : props.showIcon)
|
|
141
|
-
? (_b = props.iconLeading) !== null && _b !== void 0 ? _b : props.icon
|
|
142
|
-
: undefined, "data-icon-trailing": getBoolean(props.showIconTrailing) ? props.iconTrailing : undefined },
|
|
71
|
+
return (React.createElement("li", Object.assign({ role: "none" }, getRootProps(props, ["data-icon-variant", "data-icon-variant-before", "data-icon-variant-after", "data-icon-weight", "data-icon-weight-before", "data-icon-weight-after", "data-interactive", "data-force-mobile", "data-color", "data-container-color", "data-bg-color", "data-on-bg-color", "data-color-scheme", "data-font-size", "data-headline-size", "data-divider", "data-focus", "data-font", "data-density"]), { className: cls("db-tab-item", props.className) }),
|
|
72
|
+
React.createElement("label", { htmlFor: (_a = props.id) !== null && _a !== void 0 ? _a : (_b = props.propOverrides) === null || _b === void 0 ? void 0 : _b.id, "data-icon": (_c = props.iconLeading) !== null && _c !== void 0 ? _c : props.icon, "data-icon-trailing": props.iconTrailing, "data-show-icon": getBooleanAsString((_d = props.showIconLeading) !== null && _d !== void 0 ? _d : props.showIcon), "data-show-icon-trailing": getBooleanAsString(props.showIconTrailing), "data-no-text": getBooleanAsString(props.noText) },
|
|
73
|
+
React.createElement("input", Object.assign({ type: "radio", role: "tab", disabled: getBoolean(props.disabled, "disabled"), "aria-selected": _selected, checked: getBoolean(props.checked, "checked"), ref: _ref }, filterPassingProps(props, ["data-icon-variant", "data-icon-variant-before", "data-icon-variant-after", "data-icon-weight", "data-icon-weight-before", "data-icon-weight-after", "data-interactive", "data-force-mobile", "data-color", "data-container-color", "data-bg-color", "data-on-bg-color", "data-color-scheme", "data-font-size", "data-headline-size", "data-divider", "data-focus", "data-font", "data-density"]), { name: _name, id: (_e = props.id) !== null && _e !== void 0 ? _e : (_f = props.propOverrides) === null || _f === void 0 ? void 0 : _f.id, onInput: (event) => handleChange(event) })),
|
|
143
74
|
props.label ? React.createElement(React.Fragment, null, props.label) : null,
|
|
144
|
-
|
|
145
|
-
getBoolean(props.noText) ? (React.createElement("span", { className: "db-tab-label", "aria-hidden": "true", "data-icon": getBoolean((_c = props.showIconLeading) !== null && _c !== void 0 ? _c : props.showIcon)
|
|
146
|
-
? (_d = props.iconLeading) !== null && _d !== void 0 ? _d : props.icon
|
|
147
|
-
: undefined })) : null,
|
|
148
|
-
isTruncated && tooltipText ? (React.createElement(DBTooltip, { placement: "right" }, tooltipText)) : null));
|
|
75
|
+
props.children)));
|
|
149
76
|
}
|
|
150
77
|
const DBTabItem = forwardRef(DBTabItemFn);
|
|
151
78
|
export default DBTabItem;
|
|
@@ -1,15 +1,5 @@
|
|
|
1
|
-
import { GlobalProps
|
|
2
|
-
export type DBTabListDefaultProps = {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
ariaLabel?: string;
|
|
7
|
-
/**
|
|
8
|
-
* Identifies the element (or elements) that labels the current element (WAI-ARIA).
|
|
9
|
-
*/
|
|
10
|
-
ariaLabelledby?: string;
|
|
11
|
-
};
|
|
12
|
-
export type DBTabListProps = DBTabListDefaultProps & GlobalProps & OrientationProps;
|
|
13
|
-
export interface DBTabListState extends GlobalState {
|
|
14
|
-
_id?: string;
|
|
15
|
-
}
|
|
1
|
+
import { GlobalProps } from '../../shared/model';
|
|
2
|
+
export type DBTabListDefaultProps = {};
|
|
3
|
+
export type DBTabListProps = DBTabListDefaultProps & GlobalProps;
|
|
4
|
+
export type DBTabListDefaultState = {};
|
|
5
|
+
export type DBTabListState = DBTabListDefaultState;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
declare const DBTabList: React.ForwardRefExoticComponent<Omit<React.HTMLAttributes<
|
|
2
|
+
declare const DBTabList: React.ForwardRefExoticComponent<Omit<React.HTMLAttributes<any>, keyof import("../..").GlobalProps> & import("../..").GlobalProps & React.RefAttributes<any>>;
|
|
3
3
|
export default DBTabList;
|
|
@@ -1,17 +1,13 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
import { filterPassingProps, getRootProps } from "../../utils/react";
|
|
4
|
-
import {
|
|
4
|
+
import { useRef, forwardRef } from "react";
|
|
5
5
|
import { cls } from "../../utils";
|
|
6
|
-
import { useId } from "react";
|
|
7
6
|
function DBTabListFn(props, component) {
|
|
8
|
-
|
|
7
|
+
var _a, _b;
|
|
9
8
|
const _ref = component || useRef(component);
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
set_id(props.id || "tab-list-" + uuid);
|
|
13
|
-
}, []);
|
|
14
|
-
return (React.createElement("div", Object.assign({ role: "tablist", ref: _ref }, filterPassingProps(props, ["data-icon-variant", "data-icon-variant-before", "data-icon-variant-after", "data-icon-weight", "data-icon-weight-before", "data-icon-weight-after", "data-interactive", "data-force-mobile", "data-color", "data-container-color", "data-bg-color", "data-on-bg-color", "data-color-scheme", "data-font-size", "data-headline-size", "data-divider", "data-focus", "data-font", "data-density"]), { id: _id }, getRootProps(props, ["data-icon-variant", "data-icon-variant-before", "data-icon-variant-after", "data-icon-weight", "data-icon-weight-before", "data-icon-weight-after", "data-interactive", "data-force-mobile", "data-color", "data-container-color", "data-bg-color", "data-on-bg-color", "data-color-scheme", "data-font-size", "data-headline-size", "data-divider", "data-focus", "data-font", "data-density"]), { className: cls("db-tab-list", props.className), "aria-orientation": props.orientation || "horizontal", "aria-label": props.ariaLabelledby ? undefined : props.ariaLabel, "aria-labelledby": props.ariaLabelledby }), props.children));
|
|
9
|
+
return (React.createElement("div", Object.assign({ ref: _ref }, filterPassingProps(props, ["data-icon-variant", "data-icon-variant-before", "data-icon-variant-after", "data-icon-weight", "data-icon-weight-before", "data-icon-weight-after", "data-interactive", "data-force-mobile", "data-color", "data-container-color", "data-bg-color", "data-on-bg-color", "data-color-scheme", "data-font-size", "data-headline-size", "data-divider", "data-focus", "data-font", "data-density"]), { id: (_a = props.id) !== null && _a !== void 0 ? _a : (_b = props.propOverrides) === null || _b === void 0 ? void 0 : _b.id }, getRootProps(props, ["data-icon-variant", "data-icon-variant-before", "data-icon-variant-after", "data-icon-weight", "data-icon-weight-before", "data-icon-weight-after", "data-interactive", "data-force-mobile", "data-color", "data-container-color", "data-bg-color", "data-on-bg-color", "data-color-scheme", "data-font-size", "data-headline-size", "data-divider", "data-focus", "data-font", "data-density"]), { className: cls("db-tab-list", props.className) }),
|
|
10
|
+
React.createElement("ul", { role: "tablist" }, props.children)));
|
|
15
11
|
}
|
|
16
12
|
const DBTabList = forwardRef(DBTabListFn);
|
|
17
13
|
export default DBTabList;
|
|
@@ -1,20 +1,10 @@
|
|
|
1
|
-
import { GlobalProps } from '../../shared/model';
|
|
1
|
+
import { GlobalProps, GlobalState } from '../../shared/model';
|
|
2
2
|
export type DBTabPanelDefaultProps = {
|
|
3
3
|
/**
|
|
4
4
|
* The content if you don't want to use children.
|
|
5
5
|
*/
|
|
6
6
|
content?: string;
|
|
7
|
-
/**
|
|
8
|
-
* If the panel is hidden.
|
|
9
|
-
*/
|
|
10
|
-
hidden?: boolean;
|
|
11
|
-
/**
|
|
12
|
-
* The id of the tab that labels this panel (WAI-ARIA).
|
|
13
|
-
*/
|
|
14
|
-
ariaLabelledby?: string;
|
|
15
|
-
/**
|
|
16
|
-
* Accessible label for the panel, overrides ariaLabelledby for the accessible name.
|
|
17
|
-
*/
|
|
18
|
-
ariaLabel?: string;
|
|
19
7
|
};
|
|
20
8
|
export type DBTabPanelProps = DBTabPanelDefaultProps & GlobalProps;
|
|
9
|
+
export type DBTabPanelDefaultState = {};
|
|
10
|
+
export type DBTabPanelState = DBTabPanelDefaultState & GlobalState;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
declare const DBTabPanel: React.ForwardRefExoticComponent<Omit<React.HTMLAttributes<
|
|
2
|
+
declare const DBTabPanel: React.ForwardRefExoticComponent<Omit<React.HTMLAttributes<any>, "content" | keyof import("../..").GlobalProps> & import("./model").DBTabPanelDefaultProps & import("../..").GlobalProps & React.RefAttributes<any>>;
|
|
3
3
|
export default DBTabPanel;
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
import { filterPassingProps, getRootProps } from "../../utils/react";
|
|
4
|
-
import { useRef, forwardRef } from "react";
|
|
4
|
+
import { useRef, useEffect, forwardRef } from "react";
|
|
5
5
|
import { cls } from "../../utils";
|
|
6
6
|
function DBTabPanelFn(props, component) {
|
|
7
7
|
var _a, _b;
|
|
8
8
|
const _ref = component || useRef(component);
|
|
9
|
-
|
|
9
|
+
useEffect(() => { }, []);
|
|
10
|
+
return (React.createElement("section", Object.assign({ role: "tabpanel", ref: _ref }, filterPassingProps(props, ["data-icon-variant", "data-icon-variant-before", "data-icon-variant-after", "data-icon-weight", "data-icon-weight-before", "data-icon-weight-after", "data-interactive", "data-force-mobile", "data-color", "data-container-color", "data-bg-color", "data-on-bg-color", "data-color-scheme", "data-font-size", "data-headline-size", "data-divider", "data-focus", "data-font", "data-density"]), getRootProps(props, ["data-icon-variant", "data-icon-variant-before", "data-icon-variant-after", "data-icon-weight", "data-icon-weight-before", "data-icon-weight-after", "data-interactive", "data-force-mobile", "data-color", "data-container-color", "data-bg-color", "data-on-bg-color", "data-color-scheme", "data-font-size", "data-headline-size", "data-divider", "data-focus", "data-font", "data-density"]), { className: cls("db-tab-panel", props.className), id: (_a = props.id) !== null && _a !== void 0 ? _a : (_b = props.propOverrides) === null || _b === void 0 ? void 0 : _b.id }),
|
|
10
11
|
props.content ? React.createElement(React.Fragment, null, props.content) : null,
|
|
11
12
|
props.children));
|
|
12
13
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { GlobalProps, InitializedState,
|
|
1
|
+
import { AlignmentProps, GlobalProps, InitializedState, InputEvent, OrientationProps, WidthProps } from '../../shared/model';
|
|
2
2
|
import { DBTabItemProps } from '../tab-item/model';
|
|
3
3
|
import { DBTabPanelProps } from '../tab-panel/model';
|
|
4
4
|
export declare const TabsBehaviorList: readonly ["scrollbar", "arrows"];
|
|
@@ -19,12 +19,6 @@ export type DBTabsDefaultProps = {
|
|
|
19
19
|
* Default behavior is auto selecting the first tab, change selected tab by index
|
|
20
20
|
*/
|
|
21
21
|
initialSelectedIndex?: number | string;
|
|
22
|
-
/**
|
|
23
|
-
* Controlled active tab index. When set, the component becomes controlled:
|
|
24
|
-
* the consumer is responsible for updating this value in the onIndexChange handler.
|
|
25
|
-
* Takes precedence over initialSelectedIndex after mount.
|
|
26
|
-
*/
|
|
27
|
-
activeIndex?: number | string;
|
|
28
22
|
/**
|
|
29
23
|
* Default behavior is auto selecting the first tab, disable it with 'manually'
|
|
30
24
|
*/
|
|
@@ -37,18 +31,6 @@ export type DBTabsDefaultProps = {
|
|
|
37
31
|
* Provide simple tabs with label + text as content
|
|
38
32
|
*/
|
|
39
33
|
tabs?: DBSimpleTabProps[] | string;
|
|
40
|
-
/**
|
|
41
|
-
* Width of the tab-items. Auto width based on tab-item size, full width based on parent elements width.
|
|
42
|
-
*/
|
|
43
|
-
tabItemWidth?: WidthType | string;
|
|
44
|
-
/**
|
|
45
|
-
* Accessible label for the "scroll towards start" button (i18n). Only used with behavior="arrows".
|
|
46
|
-
*/
|
|
47
|
-
scrollStartLabel?: string;
|
|
48
|
-
/**
|
|
49
|
-
* Accessible label for the "scroll towards end" button (i18n). Only used with behavior="arrows".
|
|
50
|
-
*/
|
|
51
|
-
scrollEndLabel?: string;
|
|
52
34
|
};
|
|
53
35
|
export type DBTabsEventProps = {
|
|
54
36
|
/**
|
|
@@ -60,42 +42,26 @@ export type DBTabsEventProps = {
|
|
|
60
42
|
*/
|
|
61
43
|
onIndexChange?: (index?: number) => void;
|
|
62
44
|
/**
|
|
63
|
-
*
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
45
|
+
* Informs the user if another tab has been selected.
|
|
46
|
+
*/
|
|
47
|
+
onTabSelect?: (event?: InputEvent<HTMLElement>) => void;
|
|
48
|
+
/**
|
|
49
|
+
* Informs the user if another tab has been selected.
|
|
67
50
|
*/
|
|
68
|
-
|
|
51
|
+
tabSelect?: (event?: InputEvent<HTMLElement>) => void;
|
|
69
52
|
};
|
|
70
|
-
export type DBTabsProps = DBTabsDefaultProps & GlobalProps & OrientationProps &
|
|
53
|
+
export type DBTabsProps = DBTabsDefaultProps & GlobalProps & OrientationProps & WidthProps & AlignmentProps & DBTabsEventProps;
|
|
71
54
|
export type DBTabsDefaultState = {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
scroll: (toStart?: boolean) => void;
|
|
78
|
-
showScrollStart?: boolean;
|
|
79
|
-
showScrollEnd?: boolean;
|
|
80
|
-
_isRtl: () => boolean;
|
|
55
|
+
_name: string;
|
|
56
|
+
scrollContainer?: Element | null;
|
|
57
|
+
scroll: (left?: boolean) => void;
|
|
58
|
+
showScrollLeft?: boolean;
|
|
59
|
+
showScrollRight?: boolean;
|
|
81
60
|
evaluateScrollButtons: (tabList: Element) => void;
|
|
82
|
-
|
|
83
|
-
_updateCachedTabs: () => void;
|
|
61
|
+
convertTabs: () => DBSimpleTabProps[];
|
|
84
62
|
initTabList: () => void;
|
|
85
|
-
initTabs: (
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
_pendingRafId: number | null;
|
|
89
|
-
_scrollListener: {
|
|
90
|
-
fn: () => void;
|
|
91
|
-
} | null;
|
|
92
|
-
activeTabIndex: number;
|
|
93
|
-
activateTab: (index: number) => void;
|
|
94
|
-
getTabId: (index: number | string) => string;
|
|
95
|
-
getPanelId: (index: number | string) => string;
|
|
96
|
-
handleClick: (event: any) => void;
|
|
97
|
-
handleKeyDown: (event: any) => void;
|
|
98
|
-
isIndexActive: (index: number | string) => boolean;
|
|
99
|
-
getTabItemTabIndex: (index: number | string) => 0 | -1;
|
|
63
|
+
initTabs: (init?: boolean) => void;
|
|
64
|
+
handleChange: (event: InputEvent<HTMLElement>) => void;
|
|
65
|
+
_resizeObserver?: ResizeObserver;
|
|
100
66
|
};
|
|
101
67
|
export type DBTabsState = DBTabsDefaultState & InitializedState;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
declare const DBTabs: React.ForwardRefExoticComponent<Omit<React.HTMLAttributes<
|
|
2
|
+
declare const DBTabs: React.ForwardRefExoticComponent<Omit<React.HTMLAttributes<any>, keyof import("../../shared/model").GlobalProps | "width" | "alignment" | keyof import("./model").DBTabsDefaultProps | "orientation" | keyof import("./model").DBTabsEventProps> & import("./model").DBTabsDefaultProps & import("../../shared/model").GlobalProps & import("../../shared/model").OrientationProps & import("../../shared/model").WidthProps & import("../../shared/model").AlignmentProps & import("./model").DBTabsEventProps & React.RefAttributes<any>>;
|
|
3
3
|
export default DBTabs;
|
|
@@ -9,414 +9,172 @@ import DBTabList from "../tab-list/tab-list";
|
|
|
9
9
|
import DBTabPanel from "../tab-panel/tab-panel";
|
|
10
10
|
import { useId } from "react";
|
|
11
11
|
function DBTabsFn(props, component) {
|
|
12
|
-
var _a, _b, _c;
|
|
13
|
-
props = Object.assign({ tabItemWidth: "auto", tabItemAlignment: "start", scrollStartLabel: "Scroll start", scrollEndLabel: "Scroll end" }, props);
|
|
12
|
+
var _a, _b, _c, _d, _e, _f;
|
|
14
13
|
const uuid = useId();
|
|
15
14
|
const _ref = component || useRef(component);
|
|
16
|
-
const [
|
|
17
|
-
const [_generatedName, set_generatedName] = useState(() => uuid);
|
|
18
|
-
const [activeTabIndex, setActiveTabIndex] = useState(() => 0);
|
|
15
|
+
const [_name, set_name] = useState(() => "");
|
|
19
16
|
const [initialized, setInitialized] = useState(() => false);
|
|
20
|
-
const [
|
|
21
|
-
const [
|
|
22
|
-
const [
|
|
23
|
-
const [
|
|
24
|
-
|
|
25
|
-
const [_scrollListener, set_scrollListener] = useState(() => null);
|
|
26
|
-
function _id() {
|
|
27
|
-
return props.id || _generatedId;
|
|
28
|
-
}
|
|
29
|
-
function _name() {
|
|
30
|
-
return "tabs-" + (props.name || _generatedName);
|
|
31
|
-
}
|
|
32
|
-
function getTabId(index) {
|
|
33
|
-
return `${_name()}-tab-${index}`;
|
|
34
|
-
}
|
|
35
|
-
function getPanelId(index) {
|
|
36
|
-
return `${_name()}-tab-panel-${index}`;
|
|
37
|
-
}
|
|
38
|
-
function activateTab(index) {
|
|
39
|
-
var _a, _b, _c;
|
|
40
|
-
// Prevent activating a disabled tab
|
|
41
|
-
if (_ref.current) {
|
|
42
|
-
const tabList = _ref.current.querySelector('[role="tablist"]');
|
|
43
|
-
if (tabList) {
|
|
44
|
-
const tabs = Array.from(tabList.querySelectorAll('[role="tab"]'));
|
|
45
|
-
const tab = tabs[index];
|
|
46
|
-
if ((tab === null || tab === void 0 ? void 0 : tab.disabled) || (tab === null || tab === void 0 ? void 0 : tab.getAttribute("aria-disabled")) === "true") {
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
if (activeTabIndex !== index) {
|
|
52
|
-
setActiveTabIndex(index);
|
|
53
|
-
if (props.onIndexChange) {
|
|
54
|
-
props.onIndexChange(index);
|
|
55
|
-
}
|
|
56
|
-
// Emit value of the newly active tab item if value props are set
|
|
57
|
-
if (props.onValueChange) {
|
|
58
|
-
const tabList = (_a = _ref.current) === null || _a === void 0 ? void 0 : _a.querySelector('[role="tablist"]');
|
|
59
|
-
const tabs = tabList
|
|
60
|
-
? Array.from(tabList.querySelectorAll('[role="tab"]'))
|
|
61
|
-
: [];
|
|
62
|
-
const value = (_c = (_b = tabs[index]) === null || _b === void 0 ? void 0 : _b.dataset) === null || _c === void 0 ? void 0 : _c["value"];
|
|
63
|
-
props.onValueChange(value);
|
|
64
|
-
}
|
|
65
|
-
initTabs(index);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
function handleClick(event) {
|
|
69
|
-
var _a;
|
|
70
|
-
// In props-mode (props.tabs), tab activation is handled via onClick on each DBTabItem directly.
|
|
71
|
-
// In slot-mode (!props.tabs), clicks bubble up and are handled here via DOM traversal.
|
|
72
|
-
if (props.tabs) {
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
const target = event.target;
|
|
76
|
-
const button = target.closest('[role="tab"]');
|
|
77
|
-
if (!button || !_ref.current)
|
|
78
|
-
return;
|
|
79
|
-
const tabList = (_a = _ref.current) === null || _a === void 0 ? void 0 : _a.querySelector('[role="tablist"]');
|
|
80
|
-
if (!tabList)
|
|
81
|
-
return;
|
|
82
|
-
const buttons = Array.from(tabList.querySelectorAll('[role="tab"]'));
|
|
83
|
-
const index = buttons.indexOf(button);
|
|
84
|
-
if (index !== -1) {
|
|
85
|
-
event.preventDefault();
|
|
86
|
-
activateTab(index);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
function handleKeyDown(event) {
|
|
90
|
-
var _a;
|
|
91
|
-
if (!_ref.current)
|
|
92
|
-
return;
|
|
93
|
-
const key = event.key;
|
|
94
|
-
const navigationKeys = [
|
|
95
|
-
"ArrowRight",
|
|
96
|
-
"ArrowDown",
|
|
97
|
-
"ArrowLeft",
|
|
98
|
-
"ArrowUp",
|
|
99
|
-
"Home",
|
|
100
|
-
"End",
|
|
101
|
-
"Enter",
|
|
102
|
-
" ",
|
|
103
|
-
];
|
|
104
|
-
if (!navigationKeys.includes(key)) {
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
const tabList = _ref.current.querySelector('[role="tablist"]');
|
|
108
|
-
if (!tabList)
|
|
109
|
-
return;
|
|
110
|
-
const buttons = Array.from(tabList.querySelectorAll('[role="tab"]'));
|
|
111
|
-
// find currently focused element within the buttons list
|
|
112
|
-
let currentIndex = -1;
|
|
113
|
-
if (typeof document !== "undefined") {
|
|
114
|
-
// Traverse Shadow DOM boundaries to find the truly focused element.
|
|
115
|
-
// document.activeElement only returns the shadow host when focus is inside a Shadow DOM,
|
|
116
|
-
// so we must walk through each shadowRoot to reach the actual focused element.
|
|
117
|
-
let activeEl = document.activeElement;
|
|
118
|
-
while ((_a = activeEl === null || activeEl === void 0 ? void 0 : activeEl.shadowRoot) === null || _a === void 0 ? void 0 : _a.activeElement) {
|
|
119
|
-
activeEl = activeEl.shadowRoot.activeElement;
|
|
120
|
-
}
|
|
121
|
-
if (activeEl) {
|
|
122
|
-
const focusedButton = activeEl.closest('[role="tab"]');
|
|
123
|
-
if (focusedButton) {
|
|
124
|
-
currentIndex = buttons.indexOf(focusedButton);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
if (currentIndex === -1) {
|
|
129
|
-
currentIndex = activeTabIndex;
|
|
130
|
-
}
|
|
131
|
-
if (buttons.length > 0) {
|
|
132
|
-
// handle activation (enter / space) -> change panel
|
|
133
|
-
if (key === "Enter" || key === " ") {
|
|
134
|
-
event.preventDefault();
|
|
135
|
-
activateTab(currentIndex);
|
|
136
|
-
return;
|
|
137
|
-
}
|
|
138
|
-
// handle navigation (arrows) -> moves focus
|
|
139
|
-
let nextIndex;
|
|
140
|
-
const length = buttons.length;
|
|
141
|
-
if (key === "ArrowRight" || key === "ArrowDown") {
|
|
142
|
-
nextIndex = (currentIndex + 1) % length;
|
|
143
|
-
}
|
|
144
|
-
else if (key === "ArrowLeft" || key === "ArrowUp") {
|
|
145
|
-
nextIndex = (currentIndex - 1 + length) % length;
|
|
146
|
-
}
|
|
147
|
-
else if (key === "Home") {
|
|
148
|
-
nextIndex = 0;
|
|
149
|
-
}
|
|
150
|
-
else if (key === "End") {
|
|
151
|
-
nextIndex = length - 1;
|
|
152
|
-
}
|
|
153
|
-
if (nextIndex !== undefined) {
|
|
154
|
-
event.preventDefault();
|
|
155
|
-
// Skip disabled tabs when navigating with arrow keys
|
|
156
|
-
const isForward = key === "ArrowRight" || key === "ArrowDown";
|
|
157
|
-
const maxAttempts = length;
|
|
158
|
-
for (let i = 0; i < maxAttempts; i++) {
|
|
159
|
-
const candidate = buttons[nextIndex];
|
|
160
|
-
if (!(candidate === null || candidate === void 0 ? void 0 : candidate.disabled) &&
|
|
161
|
-
(candidate === null || candidate === void 0 ? void 0 : candidate.getAttribute("aria-disabled")) !== "true") {
|
|
162
|
-
break;
|
|
163
|
-
}
|
|
164
|
-
if (isForward) {
|
|
165
|
-
nextIndex = (nextIndex + 1) % length;
|
|
166
|
-
}
|
|
167
|
-
else {
|
|
168
|
-
nextIndex = (nextIndex - 1 + length) % length;
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
// do not activateTab here for manual activation, just move the focus
|
|
172
|
-
const nextButton = buttons[nextIndex];
|
|
173
|
-
if (nextButton &&
|
|
174
|
-
!nextButton.disabled &&
|
|
175
|
-
nextButton.getAttribute("aria-disabled") !== "true") {
|
|
176
|
-
nextButton.focus();
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
function isIndexActive(index) {
|
|
182
|
-
return activeTabIndex === Number(index);
|
|
183
|
-
}
|
|
184
|
-
function getTabItemTabIndex(index) {
|
|
185
|
-
const i = Number(index);
|
|
186
|
-
// only the active tab should be reachable via Tab key
|
|
187
|
-
return activeTabIndex === i || (activeTabIndex === -1 && i === 0) ? 0 : -1;
|
|
188
|
-
}
|
|
189
|
-
const [_cachedTabs, set_cachedTabs] = useState(() => []);
|
|
190
|
-
function _updateCachedTabs() {
|
|
17
|
+
const [showScrollLeft, setShowScrollLeft] = useState(() => false);
|
|
18
|
+
const [showScrollRight, setShowScrollRight] = useState(() => false);
|
|
19
|
+
const [scrollContainer, setScrollContainer] = useState(() => null);
|
|
20
|
+
const [_resizeObserver, set_resizeObserver] = useState(() => undefined);
|
|
21
|
+
function convertTabs() {
|
|
191
22
|
try {
|
|
192
23
|
if (typeof props.tabs === "string") {
|
|
193
|
-
|
|
194
|
-
}
|
|
195
|
-
else if (props.tabs) {
|
|
196
|
-
set_cachedTabs(props.tabs);
|
|
197
|
-
}
|
|
198
|
-
else {
|
|
199
|
-
set_cachedTabs([]);
|
|
24
|
+
return JSON.parse(props.tabs);
|
|
200
25
|
}
|
|
26
|
+
return props.tabs;
|
|
201
27
|
}
|
|
202
28
|
catch (error) {
|
|
203
29
|
console.error(error);
|
|
204
|
-
set_cachedTabs([]);
|
|
205
30
|
}
|
|
206
|
-
|
|
207
|
-
function _getScrollContainer() {
|
|
208
|
-
var _a, _b;
|
|
209
|
-
return (_b = (_a = _ref.current) === null || _a === void 0 ? void 0 : _a.querySelector('[role="tablist"]')) !== null && _b !== void 0 ? _b : null;
|
|
210
|
-
}
|
|
211
|
-
function _isRtl() {
|
|
212
|
-
const container = _getScrollContainer();
|
|
213
|
-
return (!!container &&
|
|
214
|
-
typeof getComputedStyle !== "undefined" &&
|
|
215
|
-
getComputedStyle(container).direction === "rtl");
|
|
31
|
+
return [];
|
|
216
32
|
}
|
|
217
33
|
function evaluateScrollButtons(tList) {
|
|
218
34
|
const needsScroll = tList.scrollWidth > tList.clientWidth;
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
setShowScrollEnd(false);
|
|
222
|
-
return;
|
|
223
|
-
}
|
|
224
|
-
const scrollPos = Math.abs(tList.scrollLeft);
|
|
225
|
-
const maxScroll = tList.scrollWidth - tList.clientWidth;
|
|
226
|
-
const tolerance = 2;
|
|
227
|
-
// scrollPos=0 means "at inline-start" in both LTR and RTL
|
|
228
|
-
setShowScrollStart(scrollPos > tolerance);
|
|
229
|
-
setShowScrollEnd(scrollPos < maxScroll - tolerance);
|
|
35
|
+
setShowScrollLeft(needsScroll && tList.scrollLeft > 1);
|
|
36
|
+
setShowScrollRight(needsScroll && tList.scrollLeft < tList.scrollWidth - tList.clientWidth);
|
|
230
37
|
}
|
|
231
|
-
function scroll(
|
|
232
|
-
|
|
233
|
-
if (
|
|
234
|
-
return;
|
|
235
|
-
}
|
|
236
|
-
let step = Number(props.arrowScrollDistance) || 120;
|
|
237
|
-
const isLeft = !!toStart;
|
|
238
|
-
const isRtl = _isRtl();
|
|
239
|
-
// Map logical direction (start/end) to physical direction.
|
|
240
|
-
// In LTR: toStart=true → scroll left (negative), toEnd → scroll right (positive).
|
|
241
|
-
// In RTL: directions are inverted physically.
|
|
242
|
-
if (isLeft !== isRtl) {
|
|
38
|
+
function scroll(left) {
|
|
39
|
+
let step = Number(props.arrowScrollDistance) || 100;
|
|
40
|
+
if (left) {
|
|
243
41
|
step *= -1;
|
|
244
42
|
}
|
|
245
|
-
|
|
43
|
+
scrollContainer === null || scrollContainer === void 0 ? void 0 : scrollContainer.scrollBy({
|
|
44
|
+
top: 0,
|
|
246
45
|
left: step,
|
|
247
46
|
behavior: "smooth",
|
|
248
47
|
});
|
|
249
48
|
}
|
|
250
49
|
function initTabList() {
|
|
251
|
-
var _a, _b;
|
|
252
50
|
if (_ref.current) {
|
|
253
|
-
const
|
|
254
|
-
if (
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
container.removeEventListener("scroll", _listener.fn);
|
|
263
|
-
set_scrollListener(null);
|
|
264
|
-
}
|
|
265
|
-
const onScroll = () => evaluateScrollButtons(container);
|
|
266
|
-
set_scrollListener({
|
|
267
|
-
fn: onScroll,
|
|
268
|
-
});
|
|
269
|
-
container.addEventListener("scroll", onScroll);
|
|
270
|
-
if (!_resizeObserver) {
|
|
271
|
-
const observer = new ResizeObserver(() => {
|
|
51
|
+
const tabList = _ref.current.querySelector(".db-tab-list");
|
|
52
|
+
if (tabList) {
|
|
53
|
+
const container = tabList.querySelector('[role="tablist"]');
|
|
54
|
+
if (container) {
|
|
55
|
+
container.setAttribute("aria-orientation", props.orientation || "horizontal");
|
|
56
|
+
if (props.behavior === "arrows") {
|
|
57
|
+
setScrollContainer(container);
|
|
58
|
+
evaluateScrollButtons(container);
|
|
59
|
+
container.addEventListener("scroll", () => {
|
|
272
60
|
evaluateScrollButtons(container);
|
|
273
61
|
});
|
|
274
|
-
|
|
275
|
-
|
|
62
|
+
// Use ResizeObserver to re-evaluate scroll buttons because it provides more accurate, container-specific resize detection than global window resize events.
|
|
63
|
+
if (!_resizeObserver) {
|
|
64
|
+
const observer = new ResizeObserver(() => {
|
|
65
|
+
evaluateScrollButtons(container);
|
|
66
|
+
});
|
|
67
|
+
observer.observe(container);
|
|
68
|
+
set_resizeObserver(observer);
|
|
69
|
+
}
|
|
276
70
|
}
|
|
277
71
|
}
|
|
278
|
-
if (props.name && !container.getAttribute("aria-label")) {
|
|
279
|
-
container.setAttribute("aria-label", (_b = props.name) !== null && _b !== void 0 ? _b : "");
|
|
280
|
-
}
|
|
281
72
|
}
|
|
282
73
|
}
|
|
283
74
|
}
|
|
284
|
-
function initTabs(
|
|
285
|
-
var _a, _b;
|
|
286
|
-
const currentIndex = activeIndex !== undefined ? activeIndex : activeTabIndex;
|
|
75
|
+
function initTabs(init) {
|
|
287
76
|
if (_ref.current) {
|
|
288
|
-
const
|
|
289
|
-
const
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
}
|
|
304
|
-
button.dispatchEvent(new CustomEvent("aria-selected-changed", {
|
|
305
|
-
detail: {
|
|
306
|
-
selected: isSelected,
|
|
307
|
-
tabIndex: currentIndex === index || (currentIndex === -1 && index === 0)
|
|
308
|
-
? 0
|
|
309
|
-
: -1,
|
|
310
|
-
},
|
|
311
|
-
}));
|
|
312
|
-
if (panel) {
|
|
313
|
-
if (!panel.id) {
|
|
314
|
-
panel.id = panelId;
|
|
77
|
+
const tabItems = Array.from(_ref.current.getElementsByClassName("db-tab-item"));
|
|
78
|
+
const tabPanels = Array.from(_ref.current.querySelectorAll(":is(:scope > .db-tab-panel, :scope > db-tab-panel > .db-tab-panel)"));
|
|
79
|
+
for (const tabItem of tabItems) {
|
|
80
|
+
const index = tabItems.indexOf(tabItem);
|
|
81
|
+
const label = tabItem.querySelector("label");
|
|
82
|
+
const input = tabItem.querySelector("input");
|
|
83
|
+
if (input && label) {
|
|
84
|
+
if (!input.id) {
|
|
85
|
+
const tabId = `${_name}-tab-${index}`;
|
|
86
|
+
label.setAttribute("for", tabId);
|
|
87
|
+
input.id = tabId;
|
|
88
|
+
input.setAttribute("name", _name);
|
|
89
|
+
if (tabPanels.length > index) {
|
|
90
|
+
input.setAttribute("aria-controls", `${_name}-tab-panel-${index}`);
|
|
91
|
+
}
|
|
315
92
|
}
|
|
316
|
-
if (
|
|
317
|
-
|
|
318
|
-
|
|
93
|
+
if (init) {
|
|
94
|
+
// Auto select
|
|
95
|
+
const autoSelect = !props.initialSelectedMode ||
|
|
96
|
+
props.initialSelectedMode === "auto";
|
|
97
|
+
const shouldAutoSelect = (props.initialSelectedIndex == null && index === 0) ||
|
|
98
|
+
Number(props.initialSelectedIndex) === index;
|
|
99
|
+
if (autoSelect && shouldAutoSelect) {
|
|
100
|
+
input.click();
|
|
101
|
+
}
|
|
319
102
|
}
|
|
320
|
-
// toggle visibility
|
|
321
|
-
panel.hidden = !isSelected;
|
|
322
103
|
}
|
|
323
|
-
}
|
|
104
|
+
}
|
|
105
|
+
for (const panel of tabPanels) {
|
|
106
|
+
if (panel.id)
|
|
107
|
+
continue;
|
|
108
|
+
const index = tabPanels.indexOf(panel);
|
|
109
|
+
panel.id = `${_name}-tab-panel-${index}`;
|
|
110
|
+
panel.setAttribute("aria-labelledby", `${_name}-tab-${index}`);
|
|
111
|
+
}
|
|
324
112
|
}
|
|
325
113
|
}
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
if (
|
|
330
|
-
const
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
114
|
+
function handleChange(event) {
|
|
115
|
+
var _a;
|
|
116
|
+
event.stopPropagation();
|
|
117
|
+
if (event.target) {
|
|
118
|
+
const target = event.target;
|
|
119
|
+
const parent = target.parentElement;
|
|
120
|
+
if (parent &&
|
|
121
|
+
parent.parentElement &&
|
|
122
|
+
((_a = parent.parentElement) === null || _a === void 0 ? void 0 : _a.nodeName) === "LI") {
|
|
123
|
+
const tabItem = parent.parentElement;
|
|
124
|
+
if (tabItem) {
|
|
125
|
+
const list = tabItem.parentElement;
|
|
126
|
+
if (list) {
|
|
127
|
+
const tabIndex = Array.from(list.children).indexOf(tabItem);
|
|
128
|
+
if (props.onIndexChange) {
|
|
129
|
+
props.onIndexChange(tabIndex);
|
|
130
|
+
}
|
|
131
|
+
if (props.onTabSelect) {
|
|
132
|
+
props.onTabSelect(event);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
346
135
|
}
|
|
347
136
|
}
|
|
348
137
|
}
|
|
349
|
-
|
|
350
|
-
|
|
138
|
+
}
|
|
139
|
+
useEffect(() => {
|
|
140
|
+
set_name(`tabs-${props.name || uuid}`);
|
|
351
141
|
setInitialized(true);
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
if (
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
cancelAnimationFrame(rafId);
|
|
367
|
-
set_pendingRafId(requestAnimationFrame(() => {
|
|
368
|
-
set_pendingRafId(null);
|
|
369
|
-
initTabList();
|
|
370
|
-
initTabs(activeTabIndex);
|
|
371
|
-
}));
|
|
142
|
+
}, []);
|
|
143
|
+
useEffect(() => {
|
|
144
|
+
if (_ref.current && initialized) {
|
|
145
|
+
initTabList();
|
|
146
|
+
initTabs(true);
|
|
147
|
+
const tabList = _ref.current.querySelector(".db-tab-list");
|
|
148
|
+
if (tabList) {
|
|
149
|
+
const observer = new MutationObserver((mutations) => {
|
|
150
|
+
mutations.forEach((mutation) => {
|
|
151
|
+
if (mutation.removedNodes.length || mutation.addedNodes.length) {
|
|
152
|
+
initTabList();
|
|
153
|
+
initTabs();
|
|
154
|
+
}
|
|
155
|
+
});
|
|
372
156
|
});
|
|
373
|
-
|
|
374
|
-
// re-evaluations when user content inside panels changes.
|
|
375
|
-
// childList only – attribute changes (set by initTabs) are not observed, preventing infinite loops.
|
|
376
|
-
observer.observe(tabListEl, {
|
|
157
|
+
observer.observe(tabList, {
|
|
377
158
|
childList: true,
|
|
378
159
|
subtree: true,
|
|
379
160
|
});
|
|
380
|
-
set_observer(observer);
|
|
381
161
|
}
|
|
162
|
+
setInitialized(false);
|
|
382
163
|
}
|
|
383
|
-
}, []);
|
|
384
|
-
useEffect(() => {
|
|
385
|
-
_updateCachedTabs();
|
|
386
|
-
}, [props.tabs]);
|
|
387
|
-
useEffect(() => {
|
|
388
|
-
if (props.activeIndex !== undefined) {
|
|
389
|
-
const newIndex = Number(props.activeIndex);
|
|
390
|
-
if (!isNaN(newIndex) && newIndex !== activeTabIndex) {
|
|
391
|
-
activateTab(newIndex);
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
}, [props.activeIndex]);
|
|
164
|
+
}, [_ref.current, initialized]);
|
|
395
165
|
useEffect(() => {
|
|
396
166
|
return () => {
|
|
397
|
-
const rafId = _pendingRafId;
|
|
398
|
-
if (rafId !== null) {
|
|
399
|
-
cancelAnimationFrame(rafId);
|
|
400
|
-
set_pendingRafId(null);
|
|
401
|
-
}
|
|
402
|
-
const _listener = _scrollListener;
|
|
403
|
-
const _container = _getScrollContainer();
|
|
404
|
-
if (_listener && _container) {
|
|
405
|
-
_container.removeEventListener("scroll", _listener.fn);
|
|
406
|
-
}
|
|
407
167
|
_resizeObserver === null || _resizeObserver === void 0 ? void 0 : _resizeObserver.disconnect();
|
|
408
|
-
set_resizeObserver(
|
|
409
|
-
_observer === null || _observer === void 0 ? void 0 : _observer.disconnect();
|
|
410
|
-
set_observer(null);
|
|
168
|
+
set_resizeObserver(undefined);
|
|
411
169
|
};
|
|
412
170
|
}, []);
|
|
413
|
-
return (React.createElement("div", Object.assign({ ref: _ref }, filterPassingProps(props, ["data-icon-variant", "data-icon-variant-before", "data-icon-variant-after", "data-icon-weight", "data-icon-weight-before", "data-icon-weight-after", "data-interactive", "data-force-mobile", "data-color", "data-container-color", "data-bg-color", "data-on-bg-color", "data-color-scheme", "data-font-size", "data-headline-size", "data-divider", "data-focus", "data-font", "data-density", "onTabSelect", "onIndexChange"]), { id: (
|
|
414
|
-
|
|
171
|
+
return (React.createElement("div", Object.assign({ ref: _ref }, filterPassingProps(props, ["data-icon-variant", "data-icon-variant-before", "data-icon-variant-after", "data-icon-weight", "data-icon-weight-before", "data-icon-weight-after", "data-interactive", "data-force-mobile", "data-color", "data-container-color", "data-bg-color", "data-on-bg-color", "data-color-scheme", "data-font-size", "data-headline-size", "data-divider", "data-focus", "data-font", "data-density", "onTabSelect", "onIndexChange"]), { id: (_a = props.id) !== null && _a !== void 0 ? _a : (_b = props.propOverrides) === null || _b === void 0 ? void 0 : _b.id }, getRootProps(props, ["data-icon-variant", "data-icon-variant-before", "data-icon-variant-after", "data-icon-weight", "data-icon-weight-before", "data-icon-weight-after", "data-interactive", "data-force-mobile", "data-color", "data-container-color", "data-bg-color", "data-on-bg-color", "data-color-scheme", "data-font-size", "data-headline-size", "data-divider", "data-focus", "data-font", "data-density"]), { className: cls("db-tabs", props.className), "data-orientation": props.orientation, "data-scroll-behavior": props.behavior, "data-alignment": (_c = props.alignment) !== null && _c !== void 0 ? _c : "start", "data-width": (_d = props.width) !== null && _d !== void 0 ? _d : "auto", onInput: (event) => handleChange(event), onChange: (event) => handleChange(event) }),
|
|
172
|
+
showScrollLeft ? (React.createElement(DBButton, { className: "tabs-scroll-left", variant: "ghost", icon: "chevron_left", type: "button", noText: true, onClick: (event) => scroll(true) }, "Scroll left")) : null,
|
|
415
173
|
props.tabs ? (React.createElement(React.Fragment, null,
|
|
416
|
-
React.createElement(DBTabList,
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
174
|
+
React.createElement(DBTabList, null, (_e = convertTabs()) === null || _e === void 0 ? void 0 : _e.map((tab, index) => (React.createElement(DBTabItem, { key: props.name + "tab-item" + index, active: tab.active, label: tab.label, iconTrailing: tab.iconTrailing, icon: tab.icon, noText: tab.noText })))), (_f = convertTabs()) === null || _f === void 0 ? void 0 :
|
|
175
|
+
_f.map((tab, index) => (React.createElement(DBTabPanel, { key: props.name + "tab-panel" + index, content: tab.content }, tab.children))))) : null,
|
|
176
|
+
showScrollRight ? (React.createElement(DBButton, { className: "tabs-scroll-right", variant: "ghost", icon: "chevron_right", type: "button", noText: true, onClick: (event) => scroll() }, "Scroll right")) : null,
|
|
177
|
+
props.children));
|
|
420
178
|
}
|
|
421
179
|
const DBTabs = forwardRef(DBTabsFn);
|
|
422
180
|
export default DBTabs;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
import { filterPassingProps, getRootProps } from "../../utils/react";
|
|
4
4
|
import { useState, useRef, useEffect, forwardRef } from "react";
|
|
5
|
+
import { DEFAULT_ID } from "../../shared/constants";
|
|
5
6
|
import { cls, getBooleanAsString, delay as utilsDelay } from "../../utils";
|
|
6
7
|
import { DocumentScrollListener } from "../../utils/document-scroll-listener";
|
|
7
8
|
import { handleFixedPopover } from "../../utils/floating-components";
|
|
@@ -10,7 +11,7 @@ function DBTooltipFn(props, component) {
|
|
|
10
11
|
var _a, _b, _c;
|
|
11
12
|
const uuid = useId();
|
|
12
13
|
const _ref = component || useRef(component);
|
|
13
|
-
const [_id, set_id] = useState(() =>
|
|
14
|
+
const [_id, set_id] = useState(() => DEFAULT_ID);
|
|
14
15
|
const [initialized, setInitialized] = useState(() => false);
|
|
15
16
|
const [_documentScrollListenerCallbackId, set_documentScrollListenerCallbackId,] = useState(() => undefined);
|
|
16
17
|
const [_observer, set_observer] = useState(() => undefined);
|
package/dist/shared/model.d.ts
CHANGED
|
@@ -461,13 +461,13 @@ export type CloseEventProps<T> = {
|
|
|
461
461
|
export type CloseEventState<T> = {
|
|
462
462
|
handleClose: (event?: T | void, forceClose?: boolean) => void;
|
|
463
463
|
};
|
|
464
|
-
export declare const
|
|
465
|
-
export type
|
|
466
|
-
export type
|
|
464
|
+
export declare const AlignmentList: readonly ["start", "center"];
|
|
465
|
+
export type AlignmentType = (typeof AlignmentList)[number];
|
|
466
|
+
export type AlignmentProps = {
|
|
467
467
|
/**
|
|
468
|
-
* Define the
|
|
468
|
+
* Define the content alignment in full width
|
|
469
469
|
*/
|
|
470
|
-
|
|
470
|
+
alignment?: AlignmentType | string;
|
|
471
471
|
};
|
|
472
472
|
export type ActiveProps = {
|
|
473
473
|
/**
|
package/dist/shared/model.js
CHANGED
|
@@ -19,4 +19,4 @@ export const LabelVariantHorizontalList = ['leading', 'trailing'];
|
|
|
19
19
|
export const AutoCompleteList = ['off', 'on', 'name', 'honorific-prefix', 'given-name', 'additional-name', 'family-name', 'honorific-suffix', 'nickname', 'email', 'username', 'new-password', 'current-password', 'one-time-code', 'organization-title', 'organization', 'street-address', 'shipping', 'billing', 'address-line1', 'address-line2', 'address-line3', 'address-level4', 'address-level3', 'address-level2', 'address-level1', 'country', 'country-name', 'postal-code', 'cc-name', 'cc-given-name', 'cc-additional-name', 'cc-family-name', 'cc-number', 'cc-exp', 'cc-exp-month', 'cc-exp-year', 'cc-csc', 'cc-type', 'transaction-currency', 'transaction-amount', 'language', 'bday', 'bday-day', 'bday-month', 'bday-year', 'sex', 'tel', 'tel-country-code', 'tel-national', 'tel-area-code', 'tel-local', 'tel-extension', 'impp', 'url', 'photo', 'webauthn'];
|
|
20
20
|
export const LinkTargetList = ['_self', '_blank', '_parent', '_top'];
|
|
21
21
|
export const LinkReferrerPolicyList = ['no-referrer', 'no-referrer-when-downgrade', 'origin', 'origin-when-cross-origin', 'same-origin', 'strict-origin', 'strict-origin-when-cross-origin', 'unsafe-url'];
|
|
22
|
-
export const
|
|
22
|
+
export const AlignmentList = ['start', 'center'];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@db-ux/react-core-components",
|
|
3
|
-
"version": "4.7.
|
|
3
|
+
"version": "4.7.1",
|
|
4
4
|
"description": "React components for @db-ux/core-components",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
},
|
|
42
42
|
"sideEffects": false,
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@db-ux/core-components": "4.7.
|
|
45
|
-
"@db-ux/core-foundations": "4.7.
|
|
44
|
+
"@db-ux/core-components": "4.7.1",
|
|
45
|
+
"@db-ux/core-foundations": "4.7.1"
|
|
46
46
|
}
|
|
47
47
|
}
|