@redneckz/wildless-cms-uni-blocks 0.7.5 → 0.7.6
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/bundle/blocks.schema.json +1 -1
- package/bundle/bundle.umd.js +103 -19
- package/bundle/bundle.umd.min.js +1 -1
- package/bundle/components/Tabs/LinkTab.d.ts +1 -0
- package/bundle/components/Tabs/ScrollableTabs.d.ts +8 -0
- package/bundle/components/Tabs/TabsContent.d.ts +12 -0
- package/bundle/components/Tabs/renderGroupTab.d.ts +2 -2
- package/bundle/components/Tabs/renderTab.d.ts +2 -1
- package/bundle/components/Tabs/tabItemClasses.d.ts +6 -1
- package/dist/components/Tabs/LinkTab.d.ts +1 -0
- package/dist/components/Tabs/LinkTab.js +2 -2
- package/dist/components/Tabs/LinkTab.js.map +1 -1
- package/dist/components/Tabs/ScrollableTabs.d.ts +8 -0
- package/dist/components/Tabs/ScrollableTabs.js +24 -0
- package/dist/components/Tabs/ScrollableTabs.js.map +1 -0
- package/dist/components/Tabs/Tabs.js +9 -2
- package/dist/components/Tabs/Tabs.js.map +1 -1
- package/dist/components/Tabs/TabsContent.d.ts +12 -0
- package/dist/components/Tabs/renderGroupTab.d.ts +2 -2
- package/dist/components/Tabs/renderGroupTab.js +12 -4
- package/dist/components/Tabs/renderGroupTab.js.map +1 -1
- package/dist/components/Tabs/renderTab.d.ts +2 -1
- package/dist/components/Tabs/renderTab.js +4 -6
- package/dist/components/Tabs/renderTab.js.map +1 -1
- package/dist/components/Tabs/tabItemClasses.d.ts +6 -1
- package/dist/components/Tabs/tabItemClasses.js +61 -2
- package/dist/components/Tabs/tabItemClasses.js.map +1 -1
- package/dist/setup-fixture.js +0 -1
- package/dist/setup-fixture.js.map +1 -1
- package/lib/common.css +1 -1
- package/lib/components/Tabs/LinkTab.d.ts +1 -0
- package/lib/components/Tabs/LinkTab.js +3 -3
- package/lib/components/Tabs/LinkTab.js.map +1 -1
- package/lib/components/Tabs/ScrollableTabs.d.ts +8 -0
- package/lib/components/Tabs/ScrollableTabs.js +22 -0
- package/lib/components/Tabs/ScrollableTabs.js.map +1 -0
- package/lib/components/Tabs/Tabs.fixture.d.ts +1 -0
- package/lib/components/Tabs/Tabs.js +9 -2
- package/lib/components/Tabs/Tabs.js.map +1 -1
- package/lib/components/Tabs/TabsContent.d.ts +12 -0
- package/lib/components/Tabs/renderGroupTab.d.ts +2 -2
- package/lib/components/Tabs/renderGroupTab.js +13 -5
- package/lib/components/Tabs/renderGroupTab.js.map +1 -1
- package/lib/components/Tabs/renderTab.d.ts +2 -1
- package/lib/components/Tabs/renderTab.js +4 -6
- package/lib/components/Tabs/renderTab.js.map +1 -1
- package/lib/components/Tabs/tabItemClasses.d.ts +6 -1
- package/lib/components/Tabs/tabItemClasses.js +58 -1
- package/lib/components/Tabs/tabItemClasses.js.map +1 -1
- package/lib/setup-fixture.js +0 -1
- package/lib/setup-fixture.js.map +1 -1
- package/mobile/bundle/bundle.umd.js +1 -1
- package/mobile/bundle/bundle.umd.min.js +1 -1
- package/mobile/bundle/components/Tabs/LinkTab.d.ts +1 -0
- package/mobile/bundle/components/Tabs/ScrollableTabs.d.ts +8 -0
- package/mobile/bundle/components/Tabs/TabsContent.d.ts +12 -0
- package/mobile/bundle/components/Tabs/renderGroupTab.d.ts +2 -2
- package/mobile/bundle/components/Tabs/renderTab.d.ts +2 -1
- package/mobile/bundle/components/Tabs/tabItemClasses.d.ts +6 -1
- package/mobile/dist/components/Tabs/LinkTab.d.ts +1 -0
- package/mobile/dist/components/Tabs/LinkTab.js +2 -2
- package/mobile/dist/components/Tabs/LinkTab.js.map +1 -1
- package/mobile/dist/components/Tabs/ScrollableTabs.d.ts +8 -0
- package/mobile/dist/components/Tabs/ScrollableTabs.js +24 -0
- package/mobile/dist/components/Tabs/ScrollableTabs.js.map +1 -0
- package/mobile/dist/components/Tabs/Tabs.js +9 -2
- package/mobile/dist/components/Tabs/Tabs.js.map +1 -1
- package/mobile/dist/components/Tabs/TabsContent.d.ts +12 -0
- package/mobile/dist/components/Tabs/renderGroupTab.d.ts +2 -2
- package/mobile/dist/components/Tabs/renderGroupTab.js +12 -4
- package/mobile/dist/components/Tabs/renderGroupTab.js.map +1 -1
- package/mobile/dist/components/Tabs/renderTab.d.ts +2 -1
- package/mobile/dist/components/Tabs/renderTab.js +4 -6
- package/mobile/dist/components/Tabs/renderTab.js.map +1 -1
- package/mobile/dist/components/Tabs/tabItemClasses.d.ts +6 -1
- package/mobile/dist/components/Tabs/tabItemClasses.js +61 -2
- package/mobile/dist/components/Tabs/tabItemClasses.js.map +1 -1
- package/mobile/dist/setup-fixture.js +0 -1
- package/mobile/dist/setup-fixture.js.map +1 -1
- package/mobile/lib/components/Tabs/LinkTab.d.ts +1 -0
- package/mobile/lib/components/Tabs/LinkTab.js +3 -3
- package/mobile/lib/components/Tabs/LinkTab.js.map +1 -1
- package/mobile/lib/components/Tabs/ScrollableTabs.d.ts +8 -0
- package/mobile/lib/components/Tabs/ScrollableTabs.js +22 -0
- package/mobile/lib/components/Tabs/ScrollableTabs.js.map +1 -0
- package/mobile/lib/components/Tabs/Tabs.js +9 -2
- package/mobile/lib/components/Tabs/Tabs.js.map +1 -1
- package/mobile/lib/components/Tabs/TabsContent.d.ts +12 -0
- package/mobile/lib/components/Tabs/renderGroupTab.d.ts +2 -2
- package/mobile/lib/components/Tabs/renderGroupTab.js +13 -5
- package/mobile/lib/components/Tabs/renderGroupTab.js.map +1 -1
- package/mobile/lib/components/Tabs/renderTab.d.ts +2 -1
- package/mobile/lib/components/Tabs/renderTab.js +4 -6
- package/mobile/lib/components/Tabs/renderTab.js.map +1 -1
- package/mobile/lib/components/Tabs/tabItemClasses.d.ts +6 -1
- package/mobile/lib/components/Tabs/tabItemClasses.js +58 -1
- package/mobile/lib/components/Tabs/tabItemClasses.js.map +1 -1
- package/mobile/lib/setup-fixture.js +0 -1
- package/mobile/lib/setup-fixture.js.map +1 -1
- package/mobile/src/components/Tabs/LinkTab.tsx +5 -13
- package/mobile/src/components/Tabs/ScrollableTabs.tsx +55 -0
- package/mobile/src/components/Tabs/Tabs.tsx +26 -4
- package/mobile/src/components/Tabs/TabsContent.ts +13 -0
- package/mobile/src/components/Tabs/renderGroupTab.tsx +30 -20
- package/mobile/src/components/Tabs/renderTab.tsx +8 -5
- package/mobile/src/components/Tabs/tabItemClasses.ts +65 -1
- package/mobile/src/setup-fixture.ts +0 -1
- package/package.json +1 -1
- package/src/components/Tabs/LinkTab.tsx +5 -13
- package/src/components/Tabs/ScrollableTabs.tsx +55 -0
- package/src/components/Tabs/Tabs.fixture.tsx +22 -0
- package/src/components/Tabs/Tabs.tsx +26 -4
- package/src/components/Tabs/TabsContent.ts +13 -0
- package/src/components/Tabs/renderGroupTab.tsx +30 -20
- package/src/components/Tabs/renderTab.tsx +8 -5
- package/src/components/Tabs/tabItemClasses.ts +65 -1
- package/src/setup-fixture.ts +0 -1
|
@@ -2,9 +2,10 @@ import { JSX } from '@redneckz/uni-jsx';
|
|
|
2
2
|
import { useCallback, useEffect, useState } from '@redneckz/uni-jsx/lib/hooks';
|
|
3
3
|
import { EventBus } from '../../EventBus/EventBus';
|
|
4
4
|
import type { UniBlockProps } from '../../model/JSXBlock';
|
|
5
|
+
import type { TabData, TabsContent } from './TabsContent';
|
|
5
6
|
import { BlockWrapper } from '../../ui-kit/BlockWrapper';
|
|
6
7
|
import { renderTab } from './renderTab';
|
|
7
|
-
import
|
|
8
|
+
import { ScrollableTabs } from './ScrollableTabs';
|
|
8
9
|
|
|
9
10
|
export interface TabActivationEvent {
|
|
10
11
|
type?: TabData['type'];
|
|
@@ -14,7 +15,7 @@ export interface TabActivationEvent {
|
|
|
14
15
|
export interface TabsBlockProps extends TabsContent, UniBlockProps {}
|
|
15
16
|
|
|
16
17
|
export const Tabs = JSX<TabsBlockProps>((props) => {
|
|
17
|
-
const { className = '', tabs, showCounter, isSticky, ...rest } = props;
|
|
18
|
+
const { className = '', tabs, showCounter, isSticky, tabsMode, ...rest } = props;
|
|
18
19
|
|
|
19
20
|
const [currentTab, setCurrentTab] = useState(tabs ? tabs[0] : undefined);
|
|
20
21
|
|
|
@@ -25,6 +26,8 @@ export const Tabs = JSX<TabsBlockProps>((props) => {
|
|
|
25
26
|
if (event.type === 'link' && tab) {
|
|
26
27
|
setCurrentTab(tab);
|
|
27
28
|
}
|
|
29
|
+
|
|
30
|
+
return [];
|
|
28
31
|
}),
|
|
29
32
|
[tabs],
|
|
30
33
|
);
|
|
@@ -46,11 +49,30 @@ export const Tabs = JSX<TabsBlockProps>((props) => {
|
|
|
46
49
|
|
|
47
50
|
return (
|
|
48
51
|
<BlockWrapper
|
|
49
|
-
className={
|
|
52
|
+
className={`relative box-border flex gap-x-1 w-full ${
|
|
53
|
+
isSticky ? 'sticky top-1 z-20 ' : ''
|
|
54
|
+
} ${className}`}
|
|
50
55
|
role="tablist"
|
|
51
56
|
{...rest}
|
|
52
57
|
>
|
|
53
|
-
{
|
|
58
|
+
{tabsMode === 'scrollable' ? (
|
|
59
|
+
<ScrollableTabs
|
|
60
|
+
tabs={tabs}
|
|
61
|
+
onClick={handleClick}
|
|
62
|
+
currentTab={currentTab}
|
|
63
|
+
showCounter={showCounter}
|
|
64
|
+
{...rest}
|
|
65
|
+
/>
|
|
66
|
+
) : (
|
|
67
|
+
tabs?.map(
|
|
68
|
+
renderTab({
|
|
69
|
+
onClick: handleClick,
|
|
70
|
+
currentTab,
|
|
71
|
+
showCounter,
|
|
72
|
+
...rest,
|
|
73
|
+
}),
|
|
74
|
+
)
|
|
75
|
+
)}
|
|
54
76
|
</BlockWrapper>
|
|
55
77
|
);
|
|
56
78
|
});
|
|
@@ -27,6 +27,14 @@ export type LinkTabData = LinkProps & {
|
|
|
27
27
|
*/
|
|
28
28
|
export type TabData = GroupTabData | LinkTabData;
|
|
29
29
|
|
|
30
|
+
/**
|
|
31
|
+
* @enumNames [
|
|
32
|
+
* "По ширине экрана",
|
|
33
|
+
* "С прокруткой"
|
|
34
|
+
* ]
|
|
35
|
+
*/
|
|
36
|
+
export type TabsMode = 'wideScreen' | 'scrollable';
|
|
37
|
+
|
|
30
38
|
/**
|
|
31
39
|
* @title Вкладки
|
|
32
40
|
*/
|
|
@@ -37,4 +45,9 @@ export interface TabsContent {
|
|
|
37
45
|
isSticky?: boolean;
|
|
38
46
|
/** @title Показывать счетчик блоков во вкладках */
|
|
39
47
|
showCounter?: boolean;
|
|
48
|
+
/**
|
|
49
|
+
* @title Режим вкладок
|
|
50
|
+
* @default "wideScreen"
|
|
51
|
+
*/
|
|
52
|
+
tabsMode?: TabsMode;
|
|
40
53
|
}
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
+
import type { GroupTabData } from './TabsContent';
|
|
1
2
|
import type { RenderTabProps } from './renderTab';
|
|
2
3
|
import {
|
|
3
|
-
ACTIVE_LINK_CLASSES,
|
|
4
4
|
ACTIVE_TEXT_CLASSES,
|
|
5
|
-
INACTIVE_LINK_CLASSES,
|
|
6
5
|
INACTIVE_TEXT_CLASSES,
|
|
7
|
-
LINK_CLASSES,
|
|
8
6
|
TEXT_CLASSES,
|
|
7
|
+
WHITE_ACTIVE_TEXT_CLASSES,
|
|
8
|
+
getGroupTabClassName,
|
|
9
9
|
} from './tabItemClasses';
|
|
10
|
-
import type { GroupTabData } from './TabsContent';
|
|
11
10
|
|
|
12
11
|
const BADGE_CLASSES =
|
|
13
12
|
'min-w-[22px] h-[22px] rounded-full text-xs ml-2 flex justify-center items-center';
|
|
@@ -16,7 +15,7 @@ const INACTIVE_BADGE_CLASSES =
|
|
|
16
15
|
'bg-main-divider text-secondary-text group-hover/btn:text-primary-main';
|
|
17
16
|
|
|
18
17
|
export const renderGroupTab =
|
|
19
|
-
({ page, currentTab, showCounter, onClick }: RenderTabProps) =>
|
|
18
|
+
({ page, currentTab, showCounter, onClick, isScrollable = false }: RenderTabProps) =>
|
|
20
19
|
(tab: GroupTabData, i: number) => {
|
|
21
20
|
const active = currentTab === tab;
|
|
22
21
|
|
|
@@ -27,28 +26,39 @@ export const renderGroupTab =
|
|
|
27
26
|
|
|
28
27
|
return (
|
|
29
28
|
<div
|
|
30
|
-
key={String(i)}
|
|
31
|
-
className={
|
|
29
|
+
key={`group-${String(i)}`}
|
|
30
|
+
className={getGroupTabClassName('div', active, isScrollable)}
|
|
32
31
|
role="tab"
|
|
33
32
|
aria-selected={active}
|
|
34
33
|
onClick={() => onClick(tab)}
|
|
35
34
|
>
|
|
36
|
-
<div
|
|
37
|
-
{
|
|
38
|
-
|
|
35
|
+
<div
|
|
36
|
+
className={`flex flex-1 items-center justify-center ${getGroupTabClassName(
|
|
37
|
+
'text',
|
|
38
|
+
active,
|
|
39
|
+
isScrollable,
|
|
40
|
+
)}`}
|
|
41
|
+
>
|
|
42
|
+
{renderTitle(tab.title, active, isScrollable)}
|
|
43
|
+
{renderCount(showCounter, count, active)}
|
|
39
44
|
</div>
|
|
40
45
|
</div>
|
|
41
46
|
);
|
|
42
47
|
};
|
|
43
48
|
|
|
44
|
-
const renderTitle = (title: string, active: boolean) =>
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
+
const renderTitle = (title: string | undefined, active: boolean, isScrollable: boolean) => {
|
|
50
|
+
const activeClassName = active ? WHITE_ACTIVE_TEXT_CLASSES : INACTIVE_TEXT_CLASSES;
|
|
51
|
+
const titleClassName = isScrollable
|
|
52
|
+
? `${TEXT_CLASSES} ${activeClassName}`
|
|
53
|
+
: `${TEXT_CLASSES} ${active ? ACTIVE_TEXT_CLASSES : INACTIVE_TEXT_CLASSES}`;
|
|
54
|
+
|
|
55
|
+
return title ? <h3 className={titleClassName}>{title}</h3> : null;
|
|
56
|
+
};
|
|
49
57
|
|
|
50
|
-
const renderCount = (count: number, active: boolean) =>
|
|
51
|
-
|
|
52
|
-
{
|
|
53
|
-
|
|
54
|
-
|
|
58
|
+
const renderCount = (showCounter: boolean | undefined, count: number, active: boolean) => {
|
|
59
|
+
return showCounter && count ? (
|
|
60
|
+
<div className={`${BADGE_CLASSES} ${active ? ACTIVE_BADGE_CLASSES : INACTIVE_BADGE_CLASSES}`}>
|
|
61
|
+
{count}
|
|
62
|
+
</div>
|
|
63
|
+
) : null;
|
|
64
|
+
};
|
|
@@ -9,12 +9,11 @@ export interface RenderTabProps {
|
|
|
9
9
|
currentTab?: TabData;
|
|
10
10
|
showCounter?: boolean;
|
|
11
11
|
onClick: (tab: TabData) => void;
|
|
12
|
+
isScrollable?: boolean;
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
export const renderTab =
|
|
15
|
-
({ page, currentTab, showCounter, onClick }: RenderTabProps) =>
|
|
16
|
-
// Linter doesn't understand that switch processes all cases
|
|
17
|
-
// eslint-disable-next-line consistent-return
|
|
16
|
+
({ page, currentTab, showCounter, onClick, isScrollable = false }: RenderTabProps) =>
|
|
18
17
|
(tab: TabData, i: number) => {
|
|
19
18
|
const type = tab.type;
|
|
20
19
|
|
|
@@ -24,11 +23,15 @@ export const renderTab =
|
|
|
24
23
|
|
|
25
24
|
switch (type) {
|
|
26
25
|
case 'group':
|
|
27
|
-
return renderGroupTab({ page, currentTab, showCounter, onClick })(tab, i);
|
|
26
|
+
return renderGroupTab({ page, currentTab, showCounter, onClick, isScrollable })(tab, i);
|
|
28
27
|
case 'link': {
|
|
29
|
-
return
|
|
28
|
+
return (
|
|
29
|
+
<LinkTab key={String(i)} tab={tab} currentTab={currentTab} isScrollable={isScrollable} />
|
|
30
|
+
);
|
|
30
31
|
}
|
|
31
32
|
default:
|
|
32
33
|
assertUnreachable(type);
|
|
33
34
|
}
|
|
35
|
+
|
|
36
|
+
return null;
|
|
34
37
|
};
|
|
@@ -2,6 +2,70 @@ export const LINK_CLASSES = 'h-12 flex-1 flex items-center justify-center cursor
|
|
|
2
2
|
export const ACTIVE_LINK_CLASSES = 'bg-primary-main';
|
|
3
3
|
export const INACTIVE_LINK_CLASSES = 'group/btn bg-white';
|
|
4
4
|
|
|
5
|
-
export const
|
|
5
|
+
export const SCROLLABLE_ACTIVE_LINK_CLASSES = 'bg-white text-primary-main';
|
|
6
|
+
export const WIDESCREEN_ACTIVE_LINK_CLASSES = 'bg-primary-main text-white';
|
|
7
|
+
|
|
8
|
+
export const TEXT_CLASSES = 'font-sans text-m text-center';
|
|
6
9
|
export const ACTIVE_TEXT_CLASSES = 'text-white';
|
|
7
10
|
export const INACTIVE_TEXT_CLASSES = 'text-secondary-text group-hover/btn:text-primary-main';
|
|
11
|
+
|
|
12
|
+
export const WHITE_ACTIVE_TEXT_CLASSES = 'text-primary-main';
|
|
13
|
+
|
|
14
|
+
const GROUP_TAB_CLASSES = {
|
|
15
|
+
div: {
|
|
16
|
+
scrollable: {
|
|
17
|
+
active: `inline-flex px-4 grow whitespace-nowrap ${LINK_CLASSES} ${SCROLLABLE_ACTIVE_LINK_CLASSES}`,
|
|
18
|
+
default: `inline-flex px-4 grow whitespace-nowrap ${LINK_CLASSES} ${INACTIVE_LINK_CLASSES}`,
|
|
19
|
+
},
|
|
20
|
+
default: {
|
|
21
|
+
active: `${LINK_CLASSES} ${WIDESCREEN_ACTIVE_LINK_CLASSES} whitespace-nowrap`,
|
|
22
|
+
default: `${LINK_CLASSES} ${INACTIVE_LINK_CLASSES} whitespace-nowrap`,
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
text: {
|
|
26
|
+
scrollable: {
|
|
27
|
+
active: `h-full border-0 border-b-2`,
|
|
28
|
+
default: ``,
|
|
29
|
+
},
|
|
30
|
+
default: {
|
|
31
|
+
active: '',
|
|
32
|
+
default: '',
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
const LINK_TAB_CLASSES = {
|
|
37
|
+
link: {
|
|
38
|
+
scrollable: {
|
|
39
|
+
active: `inline-flex px-4 grow whitespace-nowrap ${LINK_CLASSES} ${SCROLLABLE_ACTIVE_LINK_CLASSES}`,
|
|
40
|
+
default: `inline-flex px-4 grow whitespace-nowrap ${LINK_CLASSES} ${INACTIVE_LINK_CLASSES}`,
|
|
41
|
+
},
|
|
42
|
+
default: {
|
|
43
|
+
active: `${LINK_CLASSES} ${WIDESCREEN_ACTIVE_LINK_CLASSES}`,
|
|
44
|
+
default: `${LINK_CLASSES} ${INACTIVE_LINK_CLASSES}`,
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
text: {
|
|
48
|
+
scrollable: {
|
|
49
|
+
active: `${TEXT_CLASSES} flex items-center h-full border-0 border-b-2 ${WHITE_ACTIVE_TEXT_CLASSES}`,
|
|
50
|
+
default: `${TEXT_CLASSES} ${INACTIVE_TEXT_CLASSES}`,
|
|
51
|
+
},
|
|
52
|
+
default: {
|
|
53
|
+
active: `${TEXT_CLASSES} ${ACTIVE_TEXT_CLASSES}`,
|
|
54
|
+
default: `${TEXT_CLASSES} ${INACTIVE_TEXT_CLASSES}`,
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export const getGroupTabClassName = (type: string, active: boolean, isScrollable: boolean) => {
|
|
60
|
+
const styleType = isScrollable ? 'scrollable' : 'default';
|
|
61
|
+
const styleActive = active ? 'active' : 'default';
|
|
62
|
+
|
|
63
|
+
return GROUP_TAB_CLASSES[type][styleType][styleActive];
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export const getLinkTabClassName = (type: string, active: boolean, isScrollable: boolean) => {
|
|
67
|
+
const styleType = isScrollable ? 'scrollable' : 'default';
|
|
68
|
+
const styleActive = active ? 'active' : 'default';
|
|
69
|
+
|
|
70
|
+
return LINK_TAB_CLASSES[type][styleType][styleActive];
|
|
71
|
+
};
|
package/package.json
CHANGED
|
@@ -1,22 +1,16 @@
|
|
|
1
1
|
import { JSX } from '@redneckz/uni-jsx';
|
|
2
2
|
import { useLink } from '../../hooks/useLink';
|
|
3
|
-
import {
|
|
4
|
-
ACTIVE_LINK_CLASSES,
|
|
5
|
-
ACTIVE_TEXT_CLASSES,
|
|
6
|
-
INACTIVE_LINK_CLASSES,
|
|
7
|
-
INACTIVE_TEXT_CLASSES,
|
|
8
|
-
LINK_CLASSES,
|
|
9
|
-
TEXT_CLASSES,
|
|
10
|
-
} from './tabItemClasses';
|
|
3
|
+
import { getLinkTabClassName } from './tabItemClasses';
|
|
11
4
|
import type { LinkTabData, TabData } from './TabsContent';
|
|
12
5
|
|
|
13
6
|
interface LinkTabProps {
|
|
14
7
|
tab: LinkTabData;
|
|
15
8
|
currentTab?: TabData;
|
|
9
|
+
isScrollable?: boolean;
|
|
16
10
|
}
|
|
17
11
|
|
|
18
12
|
export const LinkTab = JSX<LinkTabProps>((props) => {
|
|
19
|
-
const { tab, currentTab } = props;
|
|
13
|
+
const { tab, currentTab, isScrollable = false } = props;
|
|
20
14
|
const active = currentTab?.type === 'link' && currentTab.href === tab.href;
|
|
21
15
|
|
|
22
16
|
const link = useLink();
|
|
@@ -25,14 +19,12 @@ export const LinkTab = JSX<LinkTabProps>((props) => {
|
|
|
25
19
|
return (
|
|
26
20
|
<a
|
|
27
21
|
role="tab"
|
|
28
|
-
className={
|
|
22
|
+
className={getLinkTabClassName('link', active, isScrollable)}
|
|
29
23
|
target={tab.target}
|
|
30
24
|
href={adjustedProps.href}
|
|
31
25
|
onClick={adjustedProps.onClick}
|
|
32
26
|
>
|
|
33
|
-
<span className={
|
|
34
|
-
{tab.text}
|
|
35
|
-
</span>
|
|
27
|
+
<span className={getLinkTabClassName('text', active, isScrollable)}>{tab.text}</span>
|
|
36
28
|
</a>
|
|
37
29
|
);
|
|
38
30
|
});
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { JSX } from '@redneckz/uni-jsx';
|
|
2
|
+
import type { RenderTabProps } from './renderTab';
|
|
3
|
+
import type { TabData } from './TabsContent';
|
|
4
|
+
import { SwipeListContainer } from '../../ui-kit/SwipeListControl/SwipeListContainer';
|
|
5
|
+
import { useSwipeListScroll } from '../../ui-kit/SwipeListControl/useSwipeListScroll';
|
|
6
|
+
import { ScrollLeftButton, ScrollRightButton } from '../../ui-kit/Button/ArrowButton';
|
|
7
|
+
import { renderTab } from './renderTab';
|
|
8
|
+
|
|
9
|
+
interface ScrollableTabsProps extends RenderTabProps {
|
|
10
|
+
tabs?: TabData[];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export type IsScrollButtonsShown = boolean[];
|
|
14
|
+
|
|
15
|
+
const scrollButtonStyle = 'absolute z-50 top-0';
|
|
16
|
+
|
|
17
|
+
export const ScrollableTabs = JSX<ScrollableTabsProps>((props) => {
|
|
18
|
+
const { tabs, onClick, currentTab, page, showCounter } = props;
|
|
19
|
+
|
|
20
|
+
const [containerRef, scroll] = useSwipeListScroll<HTMLDivElement>({
|
|
21
|
+
itemCount: tabs?.length || 0,
|
|
22
|
+
isCyclic: false,
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<div className="relative max-w-full m-auto">
|
|
27
|
+
<div className={`font-sans max-w-full w-full overflow-hidden relative bg-white`}>
|
|
28
|
+
<SwipeListContainer
|
|
29
|
+
containerRef={containerRef}
|
|
30
|
+
snapAlign="snap-start"
|
|
31
|
+
onVisibleIndicesChange={scroll.handleVisibleIndicesChange}
|
|
32
|
+
>
|
|
33
|
+
{tabs?.map(
|
|
34
|
+
renderTab({
|
|
35
|
+
onClick,
|
|
36
|
+
currentTab,
|
|
37
|
+
showCounter,
|
|
38
|
+
page,
|
|
39
|
+
isScrollable: true,
|
|
40
|
+
}),
|
|
41
|
+
)}
|
|
42
|
+
</SwipeListContainer>
|
|
43
|
+
</div>
|
|
44
|
+
{scroll.canScrollLeft ? (
|
|
45
|
+
<ScrollLeftButton className={`${scrollButtonStyle} left-6`} onClick={scroll.scrollLeft} />
|
|
46
|
+
) : null}
|
|
47
|
+
{scroll.canScrollRight ? (
|
|
48
|
+
<ScrollRightButton
|
|
49
|
+
className={`${scrollButtonStyle} rotate-180 right-6`}
|
|
50
|
+
onClick={scroll.scrollRight}
|
|
51
|
+
/>
|
|
52
|
+
) : null}
|
|
53
|
+
</div>
|
|
54
|
+
);
|
|
55
|
+
});
|
|
@@ -10,8 +10,30 @@ export default {
|
|
|
10
10
|
{ type: 'group', ref: 'tab1', title: 'Вкладка 1' },
|
|
11
11
|
{ type: 'group', ref: 'tab2', title: 'Вкладка 2' },
|
|
12
12
|
{ type: 'link', href: 'https://google.com', text: 'Вкладка 3' },
|
|
13
|
+
{ type: 'group', ref: 'tab3', title: 'Вкладка 4' },
|
|
13
14
|
]}
|
|
14
15
|
/>
|
|
15
16
|
</div>
|
|
16
17
|
),
|
|
18
|
+
scrollable: (
|
|
19
|
+
<div className="container grid grid-cols-12">
|
|
20
|
+
<Tabs
|
|
21
|
+
className="col-span-12"
|
|
22
|
+
tabs={[
|
|
23
|
+
{ type: 'group', ref: 'tab1', title: 'Вкладка 1' },
|
|
24
|
+
{ type: 'group', ref: 'tab2', title: 'Вкладка 2' },
|
|
25
|
+
{ type: 'link', href: 'https://google.com', text: 'Вкладка 3' },
|
|
26
|
+
{ type: 'link', href: 'https://yandex.com', text: 'Вкладка 4' },
|
|
27
|
+
{ type: 'link', href: 'https://ya.com', text: 'Вкладка 5' },
|
|
28
|
+
{ type: 'link', href: 'https://habr.com', text: 'Вкладка за номером 6' },
|
|
29
|
+
{ type: 'link', href: 'https://kremlin.com', text: 'Вкладка 7' },
|
|
30
|
+
{ type: 'link', href: 'https://gov.com', text: 'Вкладка 8' },
|
|
31
|
+
{ type: 'group', ref: 'tab3', title: 'Вкладка 9' },
|
|
32
|
+
{ type: 'link', href: 'https://vc.com', text: 'Вкладка номер 10' },
|
|
33
|
+
{ type: 'link', href: 'https://ria.com', text: 'Вкладка номер 11' },
|
|
34
|
+
]}
|
|
35
|
+
tabsMode="scrollable"
|
|
36
|
+
/>
|
|
37
|
+
</div>
|
|
38
|
+
),
|
|
17
39
|
};
|
|
@@ -2,9 +2,10 @@ import { JSX } from '@redneckz/uni-jsx';
|
|
|
2
2
|
import { useCallback, useEffect, useState } from '@redneckz/uni-jsx/lib/hooks';
|
|
3
3
|
import { EventBus } from '../../EventBus/EventBus';
|
|
4
4
|
import type { UniBlockProps } from '../../model/JSXBlock';
|
|
5
|
+
import type { TabData, TabsContent } from './TabsContent';
|
|
5
6
|
import { BlockWrapper } from '../../ui-kit/BlockWrapper';
|
|
6
7
|
import { renderTab } from './renderTab';
|
|
7
|
-
import
|
|
8
|
+
import { ScrollableTabs } from './ScrollableTabs';
|
|
8
9
|
|
|
9
10
|
export interface TabActivationEvent {
|
|
10
11
|
type?: TabData['type'];
|
|
@@ -14,7 +15,7 @@ export interface TabActivationEvent {
|
|
|
14
15
|
export interface TabsBlockProps extends TabsContent, UniBlockProps {}
|
|
15
16
|
|
|
16
17
|
export const Tabs = JSX<TabsBlockProps>((props) => {
|
|
17
|
-
const { className = '', tabs, showCounter, isSticky, ...rest } = props;
|
|
18
|
+
const { className = '', tabs, showCounter, isSticky, tabsMode, ...rest } = props;
|
|
18
19
|
|
|
19
20
|
const [currentTab, setCurrentTab] = useState(tabs ? tabs[0] : undefined);
|
|
20
21
|
|
|
@@ -25,6 +26,8 @@ export const Tabs = JSX<TabsBlockProps>((props) => {
|
|
|
25
26
|
if (event.type === 'link' && tab) {
|
|
26
27
|
setCurrentTab(tab);
|
|
27
28
|
}
|
|
29
|
+
|
|
30
|
+
return [];
|
|
28
31
|
}),
|
|
29
32
|
[tabs],
|
|
30
33
|
);
|
|
@@ -46,11 +49,30 @@ export const Tabs = JSX<TabsBlockProps>((props) => {
|
|
|
46
49
|
|
|
47
50
|
return (
|
|
48
51
|
<BlockWrapper
|
|
49
|
-
className={
|
|
52
|
+
className={`relative box-border flex gap-x-1 w-full ${
|
|
53
|
+
isSticky ? 'sticky top-1 z-20 ' : ''
|
|
54
|
+
} ${className}`}
|
|
50
55
|
role="tablist"
|
|
51
56
|
{...rest}
|
|
52
57
|
>
|
|
53
|
-
{
|
|
58
|
+
{tabsMode === 'scrollable' ? (
|
|
59
|
+
<ScrollableTabs
|
|
60
|
+
tabs={tabs}
|
|
61
|
+
onClick={handleClick}
|
|
62
|
+
currentTab={currentTab}
|
|
63
|
+
showCounter={showCounter}
|
|
64
|
+
{...rest}
|
|
65
|
+
/>
|
|
66
|
+
) : (
|
|
67
|
+
tabs?.map(
|
|
68
|
+
renderTab({
|
|
69
|
+
onClick: handleClick,
|
|
70
|
+
currentTab,
|
|
71
|
+
showCounter,
|
|
72
|
+
...rest,
|
|
73
|
+
}),
|
|
74
|
+
)
|
|
75
|
+
)}
|
|
54
76
|
</BlockWrapper>
|
|
55
77
|
);
|
|
56
78
|
});
|
|
@@ -27,6 +27,14 @@ export type LinkTabData = LinkProps & {
|
|
|
27
27
|
*/
|
|
28
28
|
export type TabData = GroupTabData | LinkTabData;
|
|
29
29
|
|
|
30
|
+
/**
|
|
31
|
+
* @enumNames [
|
|
32
|
+
* "По ширине экрана",
|
|
33
|
+
* "С прокруткой"
|
|
34
|
+
* ]
|
|
35
|
+
*/
|
|
36
|
+
export type TabsMode = 'wideScreen' | 'scrollable';
|
|
37
|
+
|
|
30
38
|
/**
|
|
31
39
|
* @title Вкладки
|
|
32
40
|
*/
|
|
@@ -37,4 +45,9 @@ export interface TabsContent {
|
|
|
37
45
|
isSticky?: boolean;
|
|
38
46
|
/** @title Показывать счетчик блоков во вкладках */
|
|
39
47
|
showCounter?: boolean;
|
|
48
|
+
/**
|
|
49
|
+
* @title Режим вкладок
|
|
50
|
+
* @default "wideScreen"
|
|
51
|
+
*/
|
|
52
|
+
tabsMode?: TabsMode;
|
|
40
53
|
}
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
+
import type { GroupTabData } from './TabsContent';
|
|
1
2
|
import type { RenderTabProps } from './renderTab';
|
|
2
3
|
import {
|
|
3
|
-
ACTIVE_LINK_CLASSES,
|
|
4
4
|
ACTIVE_TEXT_CLASSES,
|
|
5
|
-
INACTIVE_LINK_CLASSES,
|
|
6
5
|
INACTIVE_TEXT_CLASSES,
|
|
7
|
-
LINK_CLASSES,
|
|
8
6
|
TEXT_CLASSES,
|
|
7
|
+
WHITE_ACTIVE_TEXT_CLASSES,
|
|
8
|
+
getGroupTabClassName,
|
|
9
9
|
} from './tabItemClasses';
|
|
10
|
-
import type { GroupTabData } from './TabsContent';
|
|
11
10
|
|
|
12
11
|
const BADGE_CLASSES =
|
|
13
12
|
'min-w-[22px] h-[22px] rounded-full text-xs ml-2 flex justify-center items-center';
|
|
@@ -16,7 +15,7 @@ const INACTIVE_BADGE_CLASSES =
|
|
|
16
15
|
'bg-main-divider text-secondary-text group-hover/btn:text-primary-main';
|
|
17
16
|
|
|
18
17
|
export const renderGroupTab =
|
|
19
|
-
({ page, currentTab, showCounter, onClick }: RenderTabProps) =>
|
|
18
|
+
({ page, currentTab, showCounter, onClick, isScrollable = false }: RenderTabProps) =>
|
|
20
19
|
(tab: GroupTabData, i: number) => {
|
|
21
20
|
const active = currentTab === tab;
|
|
22
21
|
|
|
@@ -27,28 +26,39 @@ export const renderGroupTab =
|
|
|
27
26
|
|
|
28
27
|
return (
|
|
29
28
|
<div
|
|
30
|
-
key={String(i)}
|
|
31
|
-
className={
|
|
29
|
+
key={`group-${String(i)}`}
|
|
30
|
+
className={getGroupTabClassName('div', active, isScrollable)}
|
|
32
31
|
role="tab"
|
|
33
32
|
aria-selected={active}
|
|
34
33
|
onClick={() => onClick(tab)}
|
|
35
34
|
>
|
|
36
|
-
<div
|
|
37
|
-
{
|
|
38
|
-
|
|
35
|
+
<div
|
|
36
|
+
className={`flex flex-1 items-center justify-center ${getGroupTabClassName(
|
|
37
|
+
'text',
|
|
38
|
+
active,
|
|
39
|
+
isScrollable,
|
|
40
|
+
)}`}
|
|
41
|
+
>
|
|
42
|
+
{renderTitle(tab.title, active, isScrollable)}
|
|
43
|
+
{renderCount(showCounter, count, active)}
|
|
39
44
|
</div>
|
|
40
45
|
</div>
|
|
41
46
|
);
|
|
42
47
|
};
|
|
43
48
|
|
|
44
|
-
const renderTitle = (title: string, active: boolean) =>
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
+
const renderTitle = (title: string | undefined, active: boolean, isScrollable: boolean) => {
|
|
50
|
+
const activeClassName = active ? WHITE_ACTIVE_TEXT_CLASSES : INACTIVE_TEXT_CLASSES;
|
|
51
|
+
const titleClassName = isScrollable
|
|
52
|
+
? `${TEXT_CLASSES} ${activeClassName}`
|
|
53
|
+
: `${TEXT_CLASSES} ${active ? ACTIVE_TEXT_CLASSES : INACTIVE_TEXT_CLASSES}`;
|
|
54
|
+
|
|
55
|
+
return title ? <h3 className={titleClassName}>{title}</h3> : null;
|
|
56
|
+
};
|
|
49
57
|
|
|
50
|
-
const renderCount = (count: number, active: boolean) =>
|
|
51
|
-
|
|
52
|
-
{
|
|
53
|
-
|
|
54
|
-
|
|
58
|
+
const renderCount = (showCounter: boolean | undefined, count: number, active: boolean) => {
|
|
59
|
+
return showCounter && count ? (
|
|
60
|
+
<div className={`${BADGE_CLASSES} ${active ? ACTIVE_BADGE_CLASSES : INACTIVE_BADGE_CLASSES}`}>
|
|
61
|
+
{count}
|
|
62
|
+
</div>
|
|
63
|
+
) : null;
|
|
64
|
+
};
|
|
@@ -9,12 +9,11 @@ export interface RenderTabProps {
|
|
|
9
9
|
currentTab?: TabData;
|
|
10
10
|
showCounter?: boolean;
|
|
11
11
|
onClick: (tab: TabData) => void;
|
|
12
|
+
isScrollable?: boolean;
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
export const renderTab =
|
|
15
|
-
({ page, currentTab, showCounter, onClick }: RenderTabProps) =>
|
|
16
|
-
// Linter doesn't understand that switch processes all cases
|
|
17
|
-
// eslint-disable-next-line consistent-return
|
|
16
|
+
({ page, currentTab, showCounter, onClick, isScrollable = false }: RenderTabProps) =>
|
|
18
17
|
(tab: TabData, i: number) => {
|
|
19
18
|
const type = tab.type;
|
|
20
19
|
|
|
@@ -24,11 +23,15 @@ export const renderTab =
|
|
|
24
23
|
|
|
25
24
|
switch (type) {
|
|
26
25
|
case 'group':
|
|
27
|
-
return renderGroupTab({ page, currentTab, showCounter, onClick })(tab, i);
|
|
26
|
+
return renderGroupTab({ page, currentTab, showCounter, onClick, isScrollable })(tab, i);
|
|
28
27
|
case 'link': {
|
|
29
|
-
return
|
|
28
|
+
return (
|
|
29
|
+
<LinkTab key={String(i)} tab={tab} currentTab={currentTab} isScrollable={isScrollable} />
|
|
30
|
+
);
|
|
30
31
|
}
|
|
31
32
|
default:
|
|
32
33
|
assertUnreachable(type);
|
|
33
34
|
}
|
|
35
|
+
|
|
36
|
+
return null;
|
|
34
37
|
};
|