@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.
Files changed (117) hide show
  1. package/bundle/blocks.schema.json +1 -1
  2. package/bundle/bundle.umd.js +103 -19
  3. package/bundle/bundle.umd.min.js +1 -1
  4. package/bundle/components/Tabs/LinkTab.d.ts +1 -0
  5. package/bundle/components/Tabs/ScrollableTabs.d.ts +8 -0
  6. package/bundle/components/Tabs/TabsContent.d.ts +12 -0
  7. package/bundle/components/Tabs/renderGroupTab.d.ts +2 -2
  8. package/bundle/components/Tabs/renderTab.d.ts +2 -1
  9. package/bundle/components/Tabs/tabItemClasses.d.ts +6 -1
  10. package/dist/components/Tabs/LinkTab.d.ts +1 -0
  11. package/dist/components/Tabs/LinkTab.js +2 -2
  12. package/dist/components/Tabs/LinkTab.js.map +1 -1
  13. package/dist/components/Tabs/ScrollableTabs.d.ts +8 -0
  14. package/dist/components/Tabs/ScrollableTabs.js +24 -0
  15. package/dist/components/Tabs/ScrollableTabs.js.map +1 -0
  16. package/dist/components/Tabs/Tabs.js +9 -2
  17. package/dist/components/Tabs/Tabs.js.map +1 -1
  18. package/dist/components/Tabs/TabsContent.d.ts +12 -0
  19. package/dist/components/Tabs/renderGroupTab.d.ts +2 -2
  20. package/dist/components/Tabs/renderGroupTab.js +12 -4
  21. package/dist/components/Tabs/renderGroupTab.js.map +1 -1
  22. package/dist/components/Tabs/renderTab.d.ts +2 -1
  23. package/dist/components/Tabs/renderTab.js +4 -6
  24. package/dist/components/Tabs/renderTab.js.map +1 -1
  25. package/dist/components/Tabs/tabItemClasses.d.ts +6 -1
  26. package/dist/components/Tabs/tabItemClasses.js +61 -2
  27. package/dist/components/Tabs/tabItemClasses.js.map +1 -1
  28. package/dist/setup-fixture.js +0 -1
  29. package/dist/setup-fixture.js.map +1 -1
  30. package/lib/common.css +1 -1
  31. package/lib/components/Tabs/LinkTab.d.ts +1 -0
  32. package/lib/components/Tabs/LinkTab.js +3 -3
  33. package/lib/components/Tabs/LinkTab.js.map +1 -1
  34. package/lib/components/Tabs/ScrollableTabs.d.ts +8 -0
  35. package/lib/components/Tabs/ScrollableTabs.js +22 -0
  36. package/lib/components/Tabs/ScrollableTabs.js.map +1 -0
  37. package/lib/components/Tabs/Tabs.fixture.d.ts +1 -0
  38. package/lib/components/Tabs/Tabs.js +9 -2
  39. package/lib/components/Tabs/Tabs.js.map +1 -1
  40. package/lib/components/Tabs/TabsContent.d.ts +12 -0
  41. package/lib/components/Tabs/renderGroupTab.d.ts +2 -2
  42. package/lib/components/Tabs/renderGroupTab.js +13 -5
  43. package/lib/components/Tabs/renderGroupTab.js.map +1 -1
  44. package/lib/components/Tabs/renderTab.d.ts +2 -1
  45. package/lib/components/Tabs/renderTab.js +4 -6
  46. package/lib/components/Tabs/renderTab.js.map +1 -1
  47. package/lib/components/Tabs/tabItemClasses.d.ts +6 -1
  48. package/lib/components/Tabs/tabItemClasses.js +58 -1
  49. package/lib/components/Tabs/tabItemClasses.js.map +1 -1
  50. package/lib/setup-fixture.js +0 -1
  51. package/lib/setup-fixture.js.map +1 -1
  52. package/mobile/bundle/bundle.umd.js +1 -1
  53. package/mobile/bundle/bundle.umd.min.js +1 -1
  54. package/mobile/bundle/components/Tabs/LinkTab.d.ts +1 -0
  55. package/mobile/bundle/components/Tabs/ScrollableTabs.d.ts +8 -0
  56. package/mobile/bundle/components/Tabs/TabsContent.d.ts +12 -0
  57. package/mobile/bundle/components/Tabs/renderGroupTab.d.ts +2 -2
  58. package/mobile/bundle/components/Tabs/renderTab.d.ts +2 -1
  59. package/mobile/bundle/components/Tabs/tabItemClasses.d.ts +6 -1
  60. package/mobile/dist/components/Tabs/LinkTab.d.ts +1 -0
  61. package/mobile/dist/components/Tabs/LinkTab.js +2 -2
  62. package/mobile/dist/components/Tabs/LinkTab.js.map +1 -1
  63. package/mobile/dist/components/Tabs/ScrollableTabs.d.ts +8 -0
  64. package/mobile/dist/components/Tabs/ScrollableTabs.js +24 -0
  65. package/mobile/dist/components/Tabs/ScrollableTabs.js.map +1 -0
  66. package/mobile/dist/components/Tabs/Tabs.js +9 -2
  67. package/mobile/dist/components/Tabs/Tabs.js.map +1 -1
  68. package/mobile/dist/components/Tabs/TabsContent.d.ts +12 -0
  69. package/mobile/dist/components/Tabs/renderGroupTab.d.ts +2 -2
  70. package/mobile/dist/components/Tabs/renderGroupTab.js +12 -4
  71. package/mobile/dist/components/Tabs/renderGroupTab.js.map +1 -1
  72. package/mobile/dist/components/Tabs/renderTab.d.ts +2 -1
  73. package/mobile/dist/components/Tabs/renderTab.js +4 -6
  74. package/mobile/dist/components/Tabs/renderTab.js.map +1 -1
  75. package/mobile/dist/components/Tabs/tabItemClasses.d.ts +6 -1
  76. package/mobile/dist/components/Tabs/tabItemClasses.js +61 -2
  77. package/mobile/dist/components/Tabs/tabItemClasses.js.map +1 -1
  78. package/mobile/dist/setup-fixture.js +0 -1
  79. package/mobile/dist/setup-fixture.js.map +1 -1
  80. package/mobile/lib/components/Tabs/LinkTab.d.ts +1 -0
  81. package/mobile/lib/components/Tabs/LinkTab.js +3 -3
  82. package/mobile/lib/components/Tabs/LinkTab.js.map +1 -1
  83. package/mobile/lib/components/Tabs/ScrollableTabs.d.ts +8 -0
  84. package/mobile/lib/components/Tabs/ScrollableTabs.js +22 -0
  85. package/mobile/lib/components/Tabs/ScrollableTabs.js.map +1 -0
  86. package/mobile/lib/components/Tabs/Tabs.js +9 -2
  87. package/mobile/lib/components/Tabs/Tabs.js.map +1 -1
  88. package/mobile/lib/components/Tabs/TabsContent.d.ts +12 -0
  89. package/mobile/lib/components/Tabs/renderGroupTab.d.ts +2 -2
  90. package/mobile/lib/components/Tabs/renderGroupTab.js +13 -5
  91. package/mobile/lib/components/Tabs/renderGroupTab.js.map +1 -1
  92. package/mobile/lib/components/Tabs/renderTab.d.ts +2 -1
  93. package/mobile/lib/components/Tabs/renderTab.js +4 -6
  94. package/mobile/lib/components/Tabs/renderTab.js.map +1 -1
  95. package/mobile/lib/components/Tabs/tabItemClasses.d.ts +6 -1
  96. package/mobile/lib/components/Tabs/tabItemClasses.js +58 -1
  97. package/mobile/lib/components/Tabs/tabItemClasses.js.map +1 -1
  98. package/mobile/lib/setup-fixture.js +0 -1
  99. package/mobile/lib/setup-fixture.js.map +1 -1
  100. package/mobile/src/components/Tabs/LinkTab.tsx +5 -13
  101. package/mobile/src/components/Tabs/ScrollableTabs.tsx +55 -0
  102. package/mobile/src/components/Tabs/Tabs.tsx +26 -4
  103. package/mobile/src/components/Tabs/TabsContent.ts +13 -0
  104. package/mobile/src/components/Tabs/renderGroupTab.tsx +30 -20
  105. package/mobile/src/components/Tabs/renderTab.tsx +8 -5
  106. package/mobile/src/components/Tabs/tabItemClasses.ts +65 -1
  107. package/mobile/src/setup-fixture.ts +0 -1
  108. package/package.json +1 -1
  109. package/src/components/Tabs/LinkTab.tsx +5 -13
  110. package/src/components/Tabs/ScrollableTabs.tsx +55 -0
  111. package/src/components/Tabs/Tabs.fixture.tsx +22 -0
  112. package/src/components/Tabs/Tabs.tsx +26 -4
  113. package/src/components/Tabs/TabsContent.ts +13 -0
  114. package/src/components/Tabs/renderGroupTab.tsx +30 -20
  115. package/src/components/Tabs/renderTab.tsx +8 -5
  116. package/src/components/Tabs/tabItemClasses.ts +65 -1
  117. 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 type { TabData, TabsContent } from './TabsContent';
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={`${isSticky ? 'sticky top-1 z-20 ' : ''}box-border flex gap-x-1 ${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
- {tabs?.map(renderTab({ onClick: handleClick, currentTab, showCounter, ...rest }))}
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={`${LINK_CLASSES} ${active ? ACTIVE_LINK_CLASSES : INACTIVE_LINK_CLASSES}`}
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 className="flex flex-1 items-center justify-center">
37
- {tab?.title ? renderTitle(tab.title, active) : null}
38
- {showCounter && count ? renderCount(count, active) : null}
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
- <h3 className={`${TEXT_CLASSES} ${active ? ACTIVE_TEXT_CLASSES : INACTIVE_TEXT_CLASSES}`}>
46
- {title}
47
- </h3>
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
- <div className={`${BADGE_CLASSES} ${active ? ACTIVE_BADGE_CLASSES : INACTIVE_BADGE_CLASSES}`}>
52
- {count}
53
- </div>
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 <LinkTab key={String(i)} tab={tab} currentTab={currentTab} />;
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 TEXT_CLASSES = 'font-sans text-m';
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
+ };
@@ -27,6 +27,5 @@ useRouter.setup(() => {
27
27
 
28
28
  handlerDecorator.setup((handler, targetContent): any => (ev) => {
29
29
  ev.preventDefault();
30
- console.log(ev.target, ev, targetContent);
31
30
  handler(ev);
32
31
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redneckz/wildless-cms-uni-blocks",
3
- "version": "0.7.5",
3
+ "version": "0.7.6",
4
4
  "private": false,
5
5
  "license": "MIT",
6
6
  "author": "ЦК",
@@ -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={`${LINK_CLASSES} ${active ? ACTIVE_LINK_CLASSES : INACTIVE_LINK_CLASSES}`}
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={`${TEXT_CLASSES} ${active ? ACTIVE_TEXT_CLASSES : INACTIVE_TEXT_CLASSES}`}>
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 type { TabData, TabsContent } from './TabsContent';
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={`${isSticky ? 'sticky top-1 z-20 ' : ''}box-border flex gap-x-1 ${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
- {tabs?.map(renderTab({ onClick: handleClick, currentTab, showCounter, ...rest }))}
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={`${LINK_CLASSES} ${active ? ACTIVE_LINK_CLASSES : INACTIVE_LINK_CLASSES}`}
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 className="flex flex-1 items-center justify-center">
37
- {tab?.title ? renderTitle(tab.title, active) : null}
38
- {showCounter && count ? renderCount(count, active) : null}
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
- <h3 className={`${TEXT_CLASSES} ${active ? ACTIVE_TEXT_CLASSES : INACTIVE_TEXT_CLASSES}`}>
46
- {title}
47
- </h3>
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
- <div className={`${BADGE_CLASSES} ${active ? ACTIVE_BADGE_CLASSES : INACTIVE_BADGE_CLASSES}`}>
52
- {count}
53
- </div>
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 <LinkTab key={String(i)} tab={tab} currentTab={currentTab} />;
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
  };