@extrachill/components 0.4.22 → 0.4.24
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 +35 -0
- package/dist/ResponsiveTabs.d.ts +3 -1
- package/dist/ResponsiveTabs.d.ts.map +1 -1
- package/dist/ResponsiveTabs.js +37 -3
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/initResponsiveTabsDom.d.ts +8 -0
- package/dist/initResponsiveTabsDom.d.ts.map +1 -0
- package/dist/initResponsiveTabsDom.js +139 -0
- package/package.json +1 -1
- package/src/ResponsiveTabs.tsx +58 -10
- package/src/index.tsx +1 -0
- package/src/initResponsiveTabsDom.ts +184 -0
- package/styles/components.scss +1 -13
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,40 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.4.23] - 2026-03-25
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
- Refine responsive accordion spacing and interaction styling
|
|
7
|
+
|
|
8
|
+
## [0.4.22] - 2026-03-25
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
- Preserve local accordion collapse state on mobile
|
|
12
|
+
|
|
13
|
+
## [0.4.21] - 2026-03-25
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
- Allow responsive accordion items to collapse on second tap
|
|
17
|
+
|
|
18
|
+
## [0.4.20] - 2026-03-25
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
- Keep responsive tabs root structural only
|
|
22
|
+
|
|
23
|
+
## [0.4.19] - 2026-03-25
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
- Limit responsive tabs accordion mode to phone breakpoint
|
|
27
|
+
|
|
28
|
+
## [0.4.18] - 2026-03-25
|
|
29
|
+
|
|
30
|
+
### Changed
|
|
31
|
+
- Remove mobile color overrides from shared surfaces
|
|
32
|
+
|
|
33
|
+
## [0.4.17] - 2026-03-25
|
|
34
|
+
|
|
35
|
+
### Changed
|
|
36
|
+
- Fix responsive tabs mobile interaction and shared hover styling
|
|
37
|
+
|
|
3
38
|
## [0.4.16] - 2026-03-25
|
|
4
39
|
|
|
5
40
|
### Changed
|
package/dist/ResponsiveTabs.d.ts
CHANGED
|
@@ -12,6 +12,8 @@ export interface ResponsiveTabsProps {
|
|
|
12
12
|
mobileBreakpoint?: number;
|
|
13
13
|
accordionClassName?: string;
|
|
14
14
|
showDesktopTabs?: boolean;
|
|
15
|
+
syncWithHash?: boolean;
|
|
16
|
+
hashPrefix?: string;
|
|
15
17
|
}
|
|
16
|
-
export declare function ResponsiveTabs({ tabs, active, onChange, renderPanel, className, classPrefix, tabsClassName, tabsClassPrefix, mobileBreakpoint, accordionClassName, showDesktopTabs, }: ResponsiveTabsProps): import("react/jsx-runtime").JSX.Element | null;
|
|
18
|
+
export declare function ResponsiveTabs({ tabs, active, onChange, renderPanel, className, classPrefix, tabsClassName, tabsClassPrefix, mobileBreakpoint, accordionClassName, showDesktopTabs, syncWithHash, hashPrefix, }: ResponsiveTabsProps): import("react/jsx-runtime").JSX.Element | null;
|
|
17
19
|
//# sourceMappingURL=ResponsiveTabs.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ResponsiveTabs.d.ts","sourceRoot":"","sources":["../src/ResponsiveTabs.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,EAAQ,KAAK,OAAO,EAAE,MAAM,YAAY,CAAC;AAEhD,MAAM,WAAW,mBAAmB;IACnC,IAAI,EAAE,OAAO,EAAE,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,CAAE,EAAE,EAAE,MAAM,KAAM,IAAI,CAAC;IACjC,WAAW,EAAE,CAAE,EAAE,EAAE,MAAM,KAAM,SAAS,CAAC;IACzC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,eAAe,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"ResponsiveTabs.d.ts","sourceRoot":"","sources":["../src/ResponsiveTabs.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,EAAQ,KAAK,OAAO,EAAE,MAAM,YAAY,CAAC;AAEhD,MAAM,WAAW,mBAAmB;IACnC,IAAI,EAAE,OAAO,EAAE,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,CAAE,EAAE,EAAE,MAAM,KAAM,IAAI,CAAC;IACjC,WAAW,EAAE,CAAE,EAAE,EAAE,MAAM,KAAM,SAAS,CAAC;IACzC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,cAAc,CAAE,EAC/B,IAAI,EACJ,MAAM,EACN,QAAQ,EACR,WAAW,EACX,SAAc,EACd,WAAkC,EAClC,aAAkB,EAClB,eAA2B,EAC3B,gBAAsB,EACtB,kBAAuB,EACvB,eAAsB,EACtB,YAAoB,EACpB,UAAmB,GACnB,EAAE,mBAAmB,kDA6IrB"}
|
package/dist/ResponsiveTabs.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useMemo, useState } from 'react';
|
|
3
3
|
import { Tabs } from "./Tabs.js";
|
|
4
|
-
export function ResponsiveTabs({ tabs, active, onChange, renderPanel, className = '', classPrefix = 'ec-responsive-tabs', tabsClassName = '', tabsClassPrefix = 'ec-tabs', mobileBreakpoint = 480, accordionClassName = '', showDesktopTabs = true, }) {
|
|
4
|
+
export function ResponsiveTabs({ tabs, active, onChange, renderPanel, className = '', classPrefix = 'ec-responsive-tabs', tabsClassName = '', tabsClassPrefix = 'ec-tabs', mobileBreakpoint = 480, accordionClassName = '', showDesktopTabs = true, syncWithHash = false, hashPrefix = 'tab-', }) {
|
|
5
5
|
const [isMobile, setIsMobile] = useState(() => {
|
|
6
6
|
if (typeof window === 'undefined') {
|
|
7
7
|
return false;
|
|
@@ -9,6 +9,24 @@ export function ResponsiveTabs({ tabs, active, onChange, renderPanel, className
|
|
|
9
9
|
return window.innerWidth < mobileBreakpoint;
|
|
10
10
|
});
|
|
11
11
|
const [mobileActive, setMobileActive] = useState(active);
|
|
12
|
+
const resolveTabIdFromHash = (hash) => {
|
|
13
|
+
const normalized = hash.replace(/^#/, '');
|
|
14
|
+
if (!normalized.startsWith(hashPrefix)) {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
const id = normalized.slice(hashPrefix.length);
|
|
18
|
+
return tabs.some((tab) => tab.id === id) ? id : null;
|
|
19
|
+
};
|
|
20
|
+
const updateHash = (id) => {
|
|
21
|
+
if (!syncWithHash || typeof window === 'undefined') {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
window.location.hash = `${hashPrefix}${id}`;
|
|
25
|
+
};
|
|
26
|
+
const handleChange = (id) => {
|
|
27
|
+
onChange(id);
|
|
28
|
+
updateHash(id);
|
|
29
|
+
};
|
|
12
30
|
useEffect(() => {
|
|
13
31
|
if (typeof window === 'undefined') {
|
|
14
32
|
return undefined;
|
|
@@ -25,6 +43,22 @@ export function ResponsiveTabs({ tabs, active, onChange, renderPanel, className
|
|
|
25
43
|
setMobileActive(active);
|
|
26
44
|
}
|
|
27
45
|
}, [active, isMobile]);
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
if (!syncWithHash || typeof window === 'undefined' || tabs.length === 0) {
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
const syncFromHash = () => {
|
|
51
|
+
const id = resolveTabIdFromHash(window.location.hash);
|
|
52
|
+
if (!id) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
onChange(id);
|
|
56
|
+
setMobileActive(id);
|
|
57
|
+
};
|
|
58
|
+
syncFromHash();
|
|
59
|
+
window.addEventListener('hashchange', syncFromHash);
|
|
60
|
+
return () => window.removeEventListener('hashchange', syncFromHash);
|
|
61
|
+
}, [hashPrefix, onChange, syncWithHash, tabs]);
|
|
28
62
|
const rootClass = useMemo(() => [
|
|
29
63
|
classPrefix,
|
|
30
64
|
isMobile ? `${classPrefix}--mobile` : `${classPrefix}--desktop`,
|
|
@@ -36,7 +70,7 @@ export function ResponsiveTabs({ tabs, active, onChange, renderPanel, className
|
|
|
36
70
|
return null;
|
|
37
71
|
}
|
|
38
72
|
if (!isMobile) {
|
|
39
|
-
return (_jsxs("div", { className: rootClass, children: [showDesktopTabs && (_jsx(Tabs, { tabs: tabs, active: active, onChange:
|
|
73
|
+
return (_jsxs("div", { className: rootClass, children: [showDesktopTabs && (_jsx(Tabs, { tabs: tabs, active: active, onChange: handleChange, className: tabsClassName, classPrefix: tabsClassPrefix })), _jsx("div", { className: `${classPrefix}__desktop-panel`, children: renderPanel(active) })] }));
|
|
40
74
|
}
|
|
41
75
|
return (_jsx("div", { className: rootClass, children: _jsx("div", { className: [`${classPrefix}__accordion`, accordionClassName].filter(Boolean).join(' '), children: tabs.map((tab) => {
|
|
42
76
|
const isActive = tab.id === mobileActive;
|
|
@@ -46,7 +80,7 @@ export function ResponsiveTabs({ tabs, active, onChange, renderPanel, className
|
|
|
46
80
|
return;
|
|
47
81
|
}
|
|
48
82
|
setMobileActive(tab.id);
|
|
49
|
-
|
|
83
|
+
handleChange(tab.id);
|
|
50
84
|
}, children: [_jsx("span", { children: tab.label }), _jsx("span", { className: `${classPrefix}__arrow`, "aria-hidden": "true", children: isActive ? '▲' : '▼' })] }), isActive && _jsx("div", { className: `${classPrefix}__panel`, children: renderPanel(tab.id) })] }, tab.id));
|
|
51
85
|
}) }) }));
|
|
52
86
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export { SearchBox, type SearchBoxProps } from './SearchBox.tsx';
|
|
|
9
9
|
export { Modal, type ModalProps } from './Modal.tsx';
|
|
10
10
|
export { Tabs, type TabsProps, type TabItem } from './Tabs.tsx';
|
|
11
11
|
export { ResponsiveTabs, type ResponsiveTabsProps } from './ResponsiveTabs.tsx';
|
|
12
|
+
export { initResponsiveTabsDom, type ResponsiveTabsDomOptions } from './initResponsiveTabsDom.ts';
|
|
12
13
|
export { ShellTabs, type ShellTabsProps } from './ShellTabs.tsx';
|
|
13
14
|
export { Panel, type PanelProps } from './Panel.tsx';
|
|
14
15
|
export { Surface, type SurfaceProps } from './Surface.tsx';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACvF,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACpE,OAAO,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjE,OAAO,EAAE,KAAK,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,IAAI,EAAE,KAAK,SAAS,EAAE,KAAK,OAAO,EAAE,MAAM,YAAY,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,KAAK,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAChF,OAAO,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjE,OAAO,EAAE,KAAK,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACvE,OAAO,EAAE,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACpE,OAAO,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjE,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC1E,OAAO,EAAE,KAAK,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC1E,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACpE,OAAO,EAAE,gBAAgB,EAAE,KAAK,qBAAqB,EAAE,MAAM,wBAAwB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACvF,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACpE,OAAO,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjE,OAAO,EAAE,KAAK,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,IAAI,EAAE,KAAK,SAAS,EAAE,KAAK,OAAO,EAAE,MAAM,YAAY,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,KAAK,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAChF,OAAO,EAAE,qBAAqB,EAAE,KAAK,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AAClG,OAAO,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjE,OAAO,EAAE,KAAK,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACvE,OAAO,EAAE,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACpE,OAAO,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjE,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC1E,OAAO,EAAE,KAAK,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC1E,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACpE,OAAO,EAAE,gBAAgB,EAAE,KAAK,qBAAqB,EAAE,MAAM,wBAAwB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -9,6 +9,7 @@ export { SearchBox } from "./SearchBox.js";
|
|
|
9
9
|
export { Modal } from "./Modal.js";
|
|
10
10
|
export { Tabs } from "./Tabs.js";
|
|
11
11
|
export { ResponsiveTabs } from "./ResponsiveTabs.js";
|
|
12
|
+
export { initResponsiveTabsDom } from "./initResponsiveTabsDom.js";
|
|
12
13
|
export { ShellTabs } from "./ShellTabs.js";
|
|
13
14
|
export { Panel } from "./Panel.js";
|
|
14
15
|
export { Surface } from "./Surface.js";
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface ResponsiveTabsDomOptions {
|
|
2
|
+
selector?: string;
|
|
3
|
+
mobileBreakpoint?: number;
|
|
4
|
+
hashPrefix?: string;
|
|
5
|
+
onPanelRender?: (panel: HTMLElement, tabId: string, root: HTMLElement) => void;
|
|
6
|
+
}
|
|
7
|
+
export declare function initResponsiveTabsDom(options?: ResponsiveTabsDomOptions): void;
|
|
8
|
+
//# sourceMappingURL=initResponsiveTabsDom.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"initResponsiveTabsDom.d.ts","sourceRoot":"","sources":["../src/initResponsiveTabsDom.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,wBAAwB;IACxC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,CAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,KAAM,IAAI,CAAC;CACjF;AA2KD,wBAAgB,qBAAqB,CAAE,OAAO,GAAE,wBAA6B,QAO5E"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
const DEFAULT_SELECTOR = '[data-ec-responsive-tabs]';
|
|
2
|
+
function createNodeFromTemplate(template) {
|
|
3
|
+
const fragment = template.content.cloneNode(true);
|
|
4
|
+
const wrapper = document.createElement('div');
|
|
5
|
+
wrapper.appendChild(fragment);
|
|
6
|
+
return wrapper.firstElementChild instanceof HTMLElement ? wrapper.firstElementChild : wrapper;
|
|
7
|
+
}
|
|
8
|
+
function renderDesktopPanel(root, tabId, onPanelRender) {
|
|
9
|
+
const desktopPanel = root.querySelector('.ec-responsive-tabs__desktop-panel');
|
|
10
|
+
const template = root.querySelector(`template[data-tab-panel="${tabId}"]`);
|
|
11
|
+
if (!desktopPanel || !template) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
desktopPanel.innerHTML = '';
|
|
15
|
+
const content = createNodeFromTemplate(template);
|
|
16
|
+
if (!content) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
desktopPanel.appendChild(content);
|
|
20
|
+
onPanelRender?.(desktopPanel, tabId, root);
|
|
21
|
+
}
|
|
22
|
+
function renderAccordion(root, activeTabId, hashPrefix, onPanelRender) {
|
|
23
|
+
const accordion = root.querySelector('.ec-responsive-tabs__accordion');
|
|
24
|
+
const tabs = Array.from(root.querySelectorAll('.ec-tabs__tab[data-tab-id]'));
|
|
25
|
+
if (!accordion) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
accordion.innerHTML = '';
|
|
29
|
+
tabs.forEach((tab) => {
|
|
30
|
+
const tabId = tab.dataset.tabId;
|
|
31
|
+
if (!tabId) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const label = tab.textContent?.trim() || tabId;
|
|
35
|
+
const item = document.createElement('div');
|
|
36
|
+
item.className = `ec-responsive-tabs__item${tabId === activeTabId ? ' is-active' : ''}`;
|
|
37
|
+
const trigger = document.createElement('button');
|
|
38
|
+
trigger.type = 'button';
|
|
39
|
+
trigger.className = `ec-responsive-tabs__trigger${tabId === activeTabId ? ' is-active' : ''}`;
|
|
40
|
+
trigger.setAttribute('aria-expanded', tabId === activeTabId ? 'true' : 'false');
|
|
41
|
+
trigger.dataset.tabId = tabId;
|
|
42
|
+
trigger.innerHTML = `<span>${label}</span><span class="ec-responsive-tabs__arrow" aria-hidden="true">${tabId === activeTabId ? '▲' : '▼'}</span>`;
|
|
43
|
+
item.appendChild(trigger);
|
|
44
|
+
if (tabId === activeTabId) {
|
|
45
|
+
const template = root.querySelector(`template[data-tab-panel="${tabId}"]`);
|
|
46
|
+
if (template) {
|
|
47
|
+
const panel = document.createElement('div');
|
|
48
|
+
panel.className = 'ec-responsive-tabs__panel';
|
|
49
|
+
const content = createNodeFromTemplate(template);
|
|
50
|
+
if (content) {
|
|
51
|
+
panel.appendChild(content);
|
|
52
|
+
item.appendChild(panel);
|
|
53
|
+
onPanelRender?.(panel, tabId, root);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
trigger.addEventListener('click', () => {
|
|
58
|
+
const isActive = root.dataset.activeTab === tabId;
|
|
59
|
+
if (isActive) {
|
|
60
|
+
root.dataset.activeTab = '';
|
|
61
|
+
window.location.hash = '';
|
|
62
|
+
renderAccordion(root, null, hashPrefix, onPanelRender);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
root.dataset.activeTab = tabId;
|
|
66
|
+
window.location.hash = `${hashPrefix}${tabId}`;
|
|
67
|
+
renderDesktopPanel(root, tabId, onPanelRender);
|
|
68
|
+
renderAccordion(root, tabId, hashPrefix, onPanelRender);
|
|
69
|
+
setActiveDesktopTab(root, tabId);
|
|
70
|
+
});
|
|
71
|
+
accordion.appendChild(item);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
function setActiveDesktopTab(root, activeTabId) {
|
|
75
|
+
root.querySelectorAll('.ec-tabs__tab[data-tab-id]').forEach((tab) => {
|
|
76
|
+
tab.classList.toggle('is-active', tab.dataset.tabId === activeTabId);
|
|
77
|
+
if (tab.dataset.tabId === activeTabId) {
|
|
78
|
+
tab.setAttribute('aria-selected', 'true');
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
tab.setAttribute('aria-selected', 'false');
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
function resolveHashTabId(root, hashPrefix) {
|
|
86
|
+
const normalized = window.location.hash.replace(/^#/, '');
|
|
87
|
+
if (!normalized.startsWith(hashPrefix)) {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
const id = normalized.slice(hashPrefix.length);
|
|
91
|
+
return root.querySelector(`.ec-tabs__tab[data-tab-id="${id}"]`) ? id : null;
|
|
92
|
+
}
|
|
93
|
+
function initResponsiveTabsRoot(root, options) {
|
|
94
|
+
if (root.dataset.ecResponsiveTabsInitialized === '1') {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
root.dataset.ecResponsiveTabsInitialized = '1';
|
|
98
|
+
const hashPrefix = root.dataset.hashPrefix || options.hashPrefix || 'tab-';
|
|
99
|
+
const tabs = Array.from(root.querySelectorAll('.ec-tabs__tab[data-tab-id]'));
|
|
100
|
+
const firstTabId = tabs[0]?.dataset.tabId || '';
|
|
101
|
+
const initialTabId = resolveHashTabId(root, hashPrefix) || root.dataset.activeTab || firstTabId;
|
|
102
|
+
if (!initialTabId) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
root.dataset.activeTab = initialTabId;
|
|
106
|
+
setActiveDesktopTab(root, initialTabId);
|
|
107
|
+
renderDesktopPanel(root, initialTabId, options.onPanelRender);
|
|
108
|
+
renderAccordion(root, initialTabId, hashPrefix, options.onPanelRender);
|
|
109
|
+
tabs.forEach((tab) => {
|
|
110
|
+
tab.addEventListener('click', () => {
|
|
111
|
+
const tabId = tab.dataset.tabId;
|
|
112
|
+
if (!tabId) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
root.dataset.activeTab = tabId;
|
|
116
|
+
window.location.hash = `${hashPrefix}${tabId}`;
|
|
117
|
+
setActiveDesktopTab(root, tabId);
|
|
118
|
+
renderDesktopPanel(root, tabId, options.onPanelRender);
|
|
119
|
+
renderAccordion(root, tabId, hashPrefix, options.onPanelRender);
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
window.addEventListener('hashchange', () => {
|
|
123
|
+
const tabId = resolveHashTabId(root, hashPrefix);
|
|
124
|
+
if (!tabId) {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
root.dataset.activeTab = tabId;
|
|
128
|
+
setActiveDesktopTab(root, tabId);
|
|
129
|
+
renderDesktopPanel(root, tabId, options.onPanelRender);
|
|
130
|
+
renderAccordion(root, tabId, hashPrefix, options.onPanelRender);
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
export function initResponsiveTabsDom(options = {}) {
|
|
134
|
+
if (typeof document === 'undefined') {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
const selector = options.selector || DEFAULT_SELECTOR;
|
|
138
|
+
document.querySelectorAll(selector).forEach((root) => initResponsiveTabsRoot(root, options));
|
|
139
|
+
}
|
package/package.json
CHANGED
package/src/ResponsiveTabs.tsx
CHANGED
|
@@ -14,6 +14,8 @@ export interface ResponsiveTabsProps {
|
|
|
14
14
|
mobileBreakpoint?: number;
|
|
15
15
|
accordionClassName?: string;
|
|
16
16
|
showDesktopTabs?: boolean;
|
|
17
|
+
syncWithHash?: boolean;
|
|
18
|
+
hashPrefix?: string;
|
|
17
19
|
}
|
|
18
20
|
|
|
19
21
|
export function ResponsiveTabs( {
|
|
@@ -28,6 +30,8 @@ export function ResponsiveTabs( {
|
|
|
28
30
|
mobileBreakpoint = 480,
|
|
29
31
|
accordionClassName = '',
|
|
30
32
|
showDesktopTabs = true,
|
|
33
|
+
syncWithHash = false,
|
|
34
|
+
hashPrefix = 'tab-',
|
|
31
35
|
}: ResponsiveTabsProps ) {
|
|
32
36
|
const [ isMobile, setIsMobile ] = useState( () => {
|
|
33
37
|
if ( typeof window === 'undefined' ) {
|
|
@@ -38,6 +42,29 @@ export function ResponsiveTabs( {
|
|
|
38
42
|
} );
|
|
39
43
|
const [ mobileActive, setMobileActive ] = useState<string | null>( active );
|
|
40
44
|
|
|
45
|
+
const resolveTabIdFromHash = ( hash: string ) => {
|
|
46
|
+
const normalized = hash.replace( /^#/, '' );
|
|
47
|
+
if ( ! normalized.startsWith( hashPrefix ) ) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const id = normalized.slice( hashPrefix.length );
|
|
52
|
+
return tabs.some( ( tab ) => tab.id === id ) ? id : null;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const updateHash = ( id: string ) => {
|
|
56
|
+
if ( ! syncWithHash || typeof window === 'undefined' ) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
window.location.hash = `${ hashPrefix }${ id }`;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const handleChange = ( id: string ) => {
|
|
64
|
+
onChange( id );
|
|
65
|
+
updateHash( id );
|
|
66
|
+
};
|
|
67
|
+
|
|
41
68
|
useEffect( () => {
|
|
42
69
|
if ( typeof window === 'undefined' ) {
|
|
43
70
|
return undefined;
|
|
@@ -59,6 +86,27 @@ export function ResponsiveTabs( {
|
|
|
59
86
|
}
|
|
60
87
|
}, [ active, isMobile ] );
|
|
61
88
|
|
|
89
|
+
useEffect( () => {
|
|
90
|
+
if ( ! syncWithHash || typeof window === 'undefined' || tabs.length === 0 ) {
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const syncFromHash = () => {
|
|
95
|
+
const id = resolveTabIdFromHash( window.location.hash );
|
|
96
|
+
if ( ! id ) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
onChange( id );
|
|
101
|
+
setMobileActive( id );
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
syncFromHash();
|
|
105
|
+
window.addEventListener( 'hashchange', syncFromHash );
|
|
106
|
+
|
|
107
|
+
return () => window.removeEventListener( 'hashchange', syncFromHash );
|
|
108
|
+
}, [ hashPrefix, onChange, syncWithHash, tabs ] );
|
|
109
|
+
|
|
62
110
|
const rootClass = useMemo(
|
|
63
111
|
() => [
|
|
64
112
|
classPrefix,
|
|
@@ -81,7 +129,7 @@ export function ResponsiveTabs( {
|
|
|
81
129
|
<Tabs
|
|
82
130
|
tabs={ tabs }
|
|
83
131
|
active={ active }
|
|
84
|
-
onChange={
|
|
132
|
+
onChange={ handleChange }
|
|
85
133
|
className={ tabsClassName }
|
|
86
134
|
classPrefix={ tabsClassPrefix }
|
|
87
135
|
/>
|
|
@@ -104,15 +152,15 @@ export function ResponsiveTabs( {
|
|
|
104
152
|
className={ `${ classPrefix }__trigger${ isActive ? ' is-active' : '' }` }
|
|
105
153
|
aria-expanded={ isActive }
|
|
106
154
|
onClick={ () => {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
155
|
+
if ( isActive ) {
|
|
156
|
+
setMobileActive( null );
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
setMobileActive( tab.id );
|
|
161
|
+
handleChange( tab.id );
|
|
162
|
+
} }
|
|
163
|
+
>
|
|
116
164
|
<span>{ tab.label }</span>
|
|
117
165
|
<span className={ `${ classPrefix }__arrow` } aria-hidden="true">
|
|
118
166
|
{ isActive ? '▲' : '▼' }
|
package/src/index.tsx
CHANGED
|
@@ -10,6 +10,7 @@ export { SearchBox, type SearchBoxProps } from './SearchBox.tsx';
|
|
|
10
10
|
export { Modal, type ModalProps } from './Modal.tsx';
|
|
11
11
|
export { Tabs, type TabsProps, type TabItem } from './Tabs.tsx';
|
|
12
12
|
export { ResponsiveTabs, type ResponsiveTabsProps } from './ResponsiveTabs.tsx';
|
|
13
|
+
export { initResponsiveTabsDom, type ResponsiveTabsDomOptions } from './initResponsiveTabsDom.ts';
|
|
13
14
|
export { ShellTabs, type ShellTabsProps } from './ShellTabs.tsx';
|
|
14
15
|
export { Panel, type PanelProps } from './Panel.tsx';
|
|
15
16
|
export { Surface, type SurfaceProps } from './Surface.tsx';
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
export interface ResponsiveTabsDomOptions {
|
|
2
|
+
selector?: string;
|
|
3
|
+
mobileBreakpoint?: number;
|
|
4
|
+
hashPrefix?: string;
|
|
5
|
+
onPanelRender?: ( panel: HTMLElement, tabId: string, root: HTMLElement ) => void;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const DEFAULT_SELECTOR = '[data-ec-responsive-tabs]';
|
|
9
|
+
|
|
10
|
+
function createNodeFromTemplate( template: HTMLTemplateElement ): HTMLElement | null {
|
|
11
|
+
const fragment = template.content.cloneNode( true );
|
|
12
|
+
const wrapper = document.createElement( 'div' );
|
|
13
|
+
wrapper.appendChild( fragment );
|
|
14
|
+
|
|
15
|
+
return wrapper.firstElementChild instanceof HTMLElement ? wrapper.firstElementChild : wrapper;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function renderDesktopPanel(
|
|
19
|
+
root: HTMLElement,
|
|
20
|
+
tabId: string,
|
|
21
|
+
onPanelRender?: ResponsiveTabsDomOptions['onPanelRender']
|
|
22
|
+
) {
|
|
23
|
+
const desktopPanel = root.querySelector<HTMLElement>( '.ec-responsive-tabs__desktop-panel' );
|
|
24
|
+
const template = root.querySelector<HTMLTemplateElement>( `template[data-tab-panel="${ tabId }"]` );
|
|
25
|
+
|
|
26
|
+
if ( ! desktopPanel || ! template ) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
desktopPanel.innerHTML = '';
|
|
31
|
+
const content = createNodeFromTemplate( template );
|
|
32
|
+
if ( ! content ) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
desktopPanel.appendChild( content );
|
|
37
|
+
onPanelRender?.( desktopPanel, tabId, root );
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function renderAccordion(
|
|
41
|
+
root: HTMLElement,
|
|
42
|
+
activeTabId: string | null,
|
|
43
|
+
hashPrefix: string,
|
|
44
|
+
onPanelRender?: ResponsiveTabsDomOptions['onPanelRender']
|
|
45
|
+
) {
|
|
46
|
+
const accordion = root.querySelector<HTMLElement>( '.ec-responsive-tabs__accordion' );
|
|
47
|
+
const tabs = Array.from( root.querySelectorAll<HTMLElement>( '.ec-tabs__tab[data-tab-id]' ) );
|
|
48
|
+
|
|
49
|
+
if ( ! accordion ) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
accordion.innerHTML = '';
|
|
54
|
+
|
|
55
|
+
tabs.forEach( ( tab ) => {
|
|
56
|
+
const tabId = tab.dataset.tabId;
|
|
57
|
+
if ( ! tabId ) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const label = tab.textContent?.trim() || tabId;
|
|
62
|
+
const item = document.createElement( 'div' );
|
|
63
|
+
item.className = `ec-responsive-tabs__item${ tabId === activeTabId ? ' is-active' : '' }`;
|
|
64
|
+
|
|
65
|
+
const trigger = document.createElement( 'button' );
|
|
66
|
+
trigger.type = 'button';
|
|
67
|
+
trigger.className = `ec-responsive-tabs__trigger${ tabId === activeTabId ? ' is-active' : '' }`;
|
|
68
|
+
trigger.setAttribute( 'aria-expanded', tabId === activeTabId ? 'true' : 'false' );
|
|
69
|
+
trigger.dataset.tabId = tabId;
|
|
70
|
+
trigger.innerHTML = `<span>${ label }</span><span class="ec-responsive-tabs__arrow" aria-hidden="true">${ tabId === activeTabId ? '▲' : '▼' }</span>`;
|
|
71
|
+
item.appendChild( trigger );
|
|
72
|
+
|
|
73
|
+
if ( tabId === activeTabId ) {
|
|
74
|
+
const template = root.querySelector<HTMLTemplateElement>( `template[data-tab-panel="${ tabId }"]` );
|
|
75
|
+
if ( template ) {
|
|
76
|
+
const panel = document.createElement( 'div' );
|
|
77
|
+
panel.className = 'ec-responsive-tabs__panel';
|
|
78
|
+
const content = createNodeFromTemplate( template );
|
|
79
|
+
if ( content ) {
|
|
80
|
+
panel.appendChild( content );
|
|
81
|
+
item.appendChild( panel );
|
|
82
|
+
onPanelRender?.( panel, tabId, root );
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
trigger.addEventListener( 'click', () => {
|
|
88
|
+
const isActive = root.dataset.activeTab === tabId;
|
|
89
|
+
if ( isActive ) {
|
|
90
|
+
root.dataset.activeTab = '';
|
|
91
|
+
window.location.hash = '';
|
|
92
|
+
renderAccordion( root, null, hashPrefix, onPanelRender );
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
root.dataset.activeTab = tabId;
|
|
97
|
+
window.location.hash = `${ hashPrefix }${ tabId }`;
|
|
98
|
+
renderDesktopPanel( root, tabId, onPanelRender );
|
|
99
|
+
renderAccordion( root, tabId, hashPrefix, onPanelRender );
|
|
100
|
+
setActiveDesktopTab( root, tabId );
|
|
101
|
+
} );
|
|
102
|
+
|
|
103
|
+
accordion.appendChild( item );
|
|
104
|
+
} );
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function setActiveDesktopTab( root: HTMLElement, activeTabId: string ) {
|
|
108
|
+
root.querySelectorAll<HTMLElement>( '.ec-tabs__tab[data-tab-id]' ).forEach( ( tab ) => {
|
|
109
|
+
tab.classList.toggle( 'is-active', tab.dataset.tabId === activeTabId );
|
|
110
|
+
if ( tab.dataset.tabId === activeTabId ) {
|
|
111
|
+
tab.setAttribute( 'aria-selected', 'true' );
|
|
112
|
+
} else {
|
|
113
|
+
tab.setAttribute( 'aria-selected', 'false' );
|
|
114
|
+
}
|
|
115
|
+
} );
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function resolveHashTabId( root: HTMLElement, hashPrefix: string ) {
|
|
119
|
+
const normalized = window.location.hash.replace( /^#/, '' );
|
|
120
|
+
if ( ! normalized.startsWith( hashPrefix ) ) {
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const id = normalized.slice( hashPrefix.length );
|
|
125
|
+
return root.querySelector<HTMLElement>( `.ec-tabs__tab[data-tab-id="${ id }"]` ) ? id : null;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function initResponsiveTabsRoot( root: HTMLElement, options: ResponsiveTabsDomOptions ) {
|
|
129
|
+
if ( root.dataset.ecResponsiveTabsInitialized === '1' ) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
root.dataset.ecResponsiveTabsInitialized = '1';
|
|
134
|
+
|
|
135
|
+
const hashPrefix = root.dataset.hashPrefix || options.hashPrefix || 'tab-';
|
|
136
|
+
const tabs = Array.from( root.querySelectorAll<HTMLElement>( '.ec-tabs__tab[data-tab-id]' ) );
|
|
137
|
+
const firstTabId = tabs[ 0 ]?.dataset.tabId || '';
|
|
138
|
+
const initialTabId = resolveHashTabId( root, hashPrefix ) || root.dataset.activeTab || firstTabId;
|
|
139
|
+
|
|
140
|
+
if ( ! initialTabId ) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
root.dataset.activeTab = initialTabId;
|
|
145
|
+
setActiveDesktopTab( root, initialTabId );
|
|
146
|
+
renderDesktopPanel( root, initialTabId, options.onPanelRender );
|
|
147
|
+
renderAccordion( root, initialTabId, hashPrefix, options.onPanelRender );
|
|
148
|
+
|
|
149
|
+
tabs.forEach( ( tab ) => {
|
|
150
|
+
tab.addEventListener( 'click', () => {
|
|
151
|
+
const tabId = tab.dataset.tabId;
|
|
152
|
+
if ( ! tabId ) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
root.dataset.activeTab = tabId;
|
|
157
|
+
window.location.hash = `${ hashPrefix }${ tabId }`;
|
|
158
|
+
setActiveDesktopTab( root, tabId );
|
|
159
|
+
renderDesktopPanel( root, tabId, options.onPanelRender );
|
|
160
|
+
renderAccordion( root, tabId, hashPrefix, options.onPanelRender );
|
|
161
|
+
} );
|
|
162
|
+
} );
|
|
163
|
+
|
|
164
|
+
window.addEventListener( 'hashchange', () => {
|
|
165
|
+
const tabId = resolveHashTabId( root, hashPrefix );
|
|
166
|
+
if ( ! tabId ) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
root.dataset.activeTab = tabId;
|
|
171
|
+
setActiveDesktopTab( root, tabId );
|
|
172
|
+
renderDesktopPanel( root, tabId, options.onPanelRender );
|
|
173
|
+
renderAccordion( root, tabId, hashPrefix, options.onPanelRender );
|
|
174
|
+
} );
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export function initResponsiveTabsDom( options: ResponsiveTabsDomOptions = {} ) {
|
|
178
|
+
if ( typeof document === 'undefined' ) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const selector = options.selector || DEFAULT_SELECTOR;
|
|
183
|
+
document.querySelectorAll<HTMLElement>( selector ).forEach( ( root ) => initResponsiveTabsRoot( root, options ) );
|
|
184
|
+
}
|
package/styles/components.scss
CHANGED
|
@@ -160,11 +160,6 @@
|
|
|
160
160
|
box-sizing: border-box;
|
|
161
161
|
}
|
|
162
162
|
|
|
163
|
-
.ec-responsive-tabs__accordion {
|
|
164
|
-
display: grid;
|
|
165
|
-
gap: var(--spacing-md, 1rem);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
163
|
.ec-responsive-tabs__item {
|
|
169
164
|
border: 1px solid var(--border-color, #ddd);
|
|
170
165
|
border-radius: var(--border-radius-md, 8px);
|
|
@@ -186,13 +181,11 @@
|
|
|
186
181
|
gap: var(--spacing-sm, 0.5rem);
|
|
187
182
|
padding: var(--spacing-sm, 0.5rem) var(--spacing-md, 1rem);
|
|
188
183
|
border: 0;
|
|
189
|
-
border-bottom: 1px solid var(--border-color, #ddd);
|
|
190
184
|
background: var(--card-background, #f8f8f8);
|
|
191
185
|
color: var(--text-color, #111);
|
|
192
186
|
font-family: var(--font-family-body, inherit);
|
|
193
187
|
font-size: var(--font-size-body, 1.125rem);
|
|
194
188
|
font-weight: 600;
|
|
195
|
-
line-height: 1.4;
|
|
196
189
|
cursor: pointer;
|
|
197
190
|
text-align: left;
|
|
198
191
|
transition: background 0.15s ease, color 0.15s ease;
|
|
@@ -207,11 +200,6 @@
|
|
|
207
200
|
color: var(--link-color, #0b5394);
|
|
208
201
|
}
|
|
209
202
|
|
|
210
|
-
.ec-responsive-tabs__trigger:hover {
|
|
211
|
-
background: var(--background-color, #fff);
|
|
212
|
-
color: var(--link-color-hover, var(--link-color, #0b5394));
|
|
213
|
-
}
|
|
214
|
-
|
|
215
203
|
.ec-responsive-tabs__trigger:focus-visible {
|
|
216
204
|
outline: 2px solid var(--focus-border-color, #53940b);
|
|
217
205
|
outline-offset: -2px;
|
|
@@ -219,7 +207,7 @@
|
|
|
219
207
|
}
|
|
220
208
|
|
|
221
209
|
.ec-responsive-tabs__panel {
|
|
222
|
-
padding: var(--spacing-
|
|
210
|
+
padding: 0 var(--spacing-md, 1rem);
|
|
223
211
|
background: var(--background-color, #fff);
|
|
224
212
|
margin-bottom: 0;
|
|
225
213
|
}
|