@jobber/components 6.6.0 → 6.7.0

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.
@@ -31,6 +31,7 @@ interface InternalTabProps {
31
31
  readonly selected: boolean;
32
32
  activateTab(): void;
33
33
  onClick?(event: React.MouseEvent<HTMLButtonElement>): void;
34
+ readonly tabIndex: number;
34
35
  }
35
- export declare function InternalTab({ label, selected, activateTab, onClick, }: InternalTabProps): JSX.Element;
36
- export {};
36
+ declare const InternalTab: React.ForwardRefExoticComponent<InternalTabProps & React.RefAttributes<HTMLButtonElement>>;
37
+ export { InternalTab };
@@ -0,0 +1,6 @@
1
+ interface UseArrowKeyNavigationProps {
2
+ elementsRef: React.RefObject<(HTMLElement | null)[]>;
3
+ onActivate: (index: number) => void;
4
+ }
5
+ export declare const useArrowKeyNavigation: ({ elementsRef, onActivate, }: UseArrowKeyNavigationProps) => (event: React.KeyboardEvent<HTMLElement>) => void;
6
+ export {};
package/dist/Tabs-cjs.js CHANGED
@@ -38,6 +38,35 @@ function useTabsOverflow() {
38
38
  };
39
39
  }
40
40
 
41
+ const useArrowKeyNavigation = ({ elementsRef, onActivate, }) => {
42
+ const handleKeyDown = React.useCallback((event) => {
43
+ const elements = elementsRef.current;
44
+ if (!elements)
45
+ return;
46
+ const currentIndex = elements.findIndex(element => element === document.activeElement);
47
+ if (currentIndex === -1)
48
+ return;
49
+ const focusAndActivateTab = (index) => {
50
+ const element = elements[index];
51
+ if (element) {
52
+ element.focus();
53
+ onActivate(index);
54
+ }
55
+ };
56
+ if (event.key === "ArrowRight") {
57
+ event.preventDefault();
58
+ const nextIndex = (currentIndex + 1) % elements.length;
59
+ focusAndActivateTab(nextIndex);
60
+ }
61
+ else if (event.key === "ArrowLeft") {
62
+ event.preventDefault();
63
+ const prevIndex = (currentIndex - 1 + elements.length) % elements.length;
64
+ focusAndActivateTab(prevIndex);
65
+ }
66
+ }, [elementsRef, onActivate]);
67
+ return handleKeyDown;
68
+ };
69
+
41
70
  function Tabs({ children, defaultTab = 0, activeTab: controlledActiveTab, onTabChange, }) {
42
71
  var _a;
43
72
  const activeTabInitialValue = defaultTab < React.Children.count(children) ? defaultTab : 0;
@@ -48,6 +77,7 @@ function Tabs({ children, defaultTab = 0, activeTab: controlledActiveTab, onTabC
48
77
  [styles.overflowRight]: overflowRight,
49
78
  [styles.overflowLeft]: overflowLeft,
50
79
  });
80
+ const tabRefs = React.useRef([]);
51
81
  const activateTab = (index) => {
52
82
  return () => {
53
83
  if (controlledActiveTab === undefined) {
@@ -58,6 +88,10 @@ function Tabs({ children, defaultTab = 0, activeTab: controlledActiveTab, onTabC
58
88
  }
59
89
  };
60
90
  };
91
+ const handleKeyDown = useArrowKeyNavigation({
92
+ elementsRef: tabRefs,
93
+ onActivate: index => activateTab(index)(),
94
+ });
61
95
  const activeTabProps = (_a = React.Children.toArray(children)[activeTab]) === null || _a === void 0 ? void 0 : _a.props;
62
96
  React.useEffect(() => {
63
97
  if (activeTab > React.Children.count(children) - 1) {
@@ -66,22 +100,21 @@ function Tabs({ children, defaultTab = 0, activeTab: controlledActiveTab, onTabC
66
100
  }, [React.Children.count(children)]);
67
101
  return (React.createElement("div", { className: styles.tabs },
68
102
  React.createElement("div", { className: overflowClassNames },
69
- React.createElement("ul", { role: "tablist", className: styles.tabRow, ref: tabRow }, React.Children.map(children, (tab, index) => (React.createElement(InternalTab, { label: tab.props.label, selected: activeTab === index, activateTab: activateTab(index), onClick: tab.props.onClick }))))),
103
+ React.createElement("ul", { role: "tablist", className: styles.tabRow, ref: tabRow, onKeyDown: handleKeyDown }, React.Children.map(children, (tab, index) => (React.createElement(InternalTab, { label: tab.props.label, selected: activeTab === index, activateTab: activateTab(index), onClick: tab.props.onClick, ref: el => (tabRefs.current[index] = el), tabIndex: activeTab === index ? 0 : -1 }))))),
70
104
  React.createElement("section", { role: "tabpanel", className: styles.tabContent, "aria-label": activeTabProps === null || activeTabProps === void 0 ? void 0 : activeTabProps.label }, activeTabProps === null || activeTabProps === void 0 ? void 0 : activeTabProps.children)));
71
105
  }
72
106
  function Tab({ label }) {
73
107
  return React.createElement(React.Fragment, null, label);
74
108
  }
75
- function InternalTab({ label, selected, activateTab, onClick = () => {
76
- return;
77
- }, }) {
109
+ const InternalTab = React.forwardRef(({ label, selected, activateTab, onClick, tabIndex }, ref) => {
78
110
  const className = classnames(styles.tab, { [styles.selected]: selected });
79
111
  return (React.createElement("li", { role: "presentation" },
80
112
  React.createElement("button", { type: "button", role: "tab", className: className, onClick: event => {
81
113
  activateTab();
82
- onClick(event);
83
- } }, typeof label === "string" ? (React.createElement(Typography.Typography, { element: "span", size: "large", fontWeight: "semiBold" }, label)) : (label))));
84
- }
114
+ onClick === null || onClick === void 0 ? void 0 : onClick(event);
115
+ }, ref: ref, tabIndex: tabIndex }, typeof label === "string" ? (React.createElement(Typography.Typography, { element: "span", size: "large", fontWeight: "semiBold" }, label)) : (label))));
116
+ });
117
+ InternalTab.displayName = "InternalTab";
85
118
 
86
119
  exports.Tab = Tab;
87
120
  exports.Tabs = Tabs;
package/dist/Tabs-es.js CHANGED
@@ -36,6 +36,35 @@ function useTabsOverflow() {
36
36
  };
37
37
  }
38
38
 
39
+ const useArrowKeyNavigation = ({ elementsRef, onActivate, }) => {
40
+ const handleKeyDown = useCallback((event) => {
41
+ const elements = elementsRef.current;
42
+ if (!elements)
43
+ return;
44
+ const currentIndex = elements.findIndex(element => element === document.activeElement);
45
+ if (currentIndex === -1)
46
+ return;
47
+ const focusAndActivateTab = (index) => {
48
+ const element = elements[index];
49
+ if (element) {
50
+ element.focus();
51
+ onActivate(index);
52
+ }
53
+ };
54
+ if (event.key === "ArrowRight") {
55
+ event.preventDefault();
56
+ const nextIndex = (currentIndex + 1) % elements.length;
57
+ focusAndActivateTab(nextIndex);
58
+ }
59
+ else if (event.key === "ArrowLeft") {
60
+ event.preventDefault();
61
+ const prevIndex = (currentIndex - 1 + elements.length) % elements.length;
62
+ focusAndActivateTab(prevIndex);
63
+ }
64
+ }, [elementsRef, onActivate]);
65
+ return handleKeyDown;
66
+ };
67
+
39
68
  function Tabs({ children, defaultTab = 0, activeTab: controlledActiveTab, onTabChange, }) {
40
69
  var _a;
41
70
  const activeTabInitialValue = defaultTab < React.Children.count(children) ? defaultTab : 0;
@@ -46,6 +75,7 @@ function Tabs({ children, defaultTab = 0, activeTab: controlledActiveTab, onTabC
46
75
  [styles.overflowRight]: overflowRight,
47
76
  [styles.overflowLeft]: overflowLeft,
48
77
  });
78
+ const tabRefs = useRef([]);
49
79
  const activateTab = (index) => {
50
80
  return () => {
51
81
  if (controlledActiveTab === undefined) {
@@ -56,6 +86,10 @@ function Tabs({ children, defaultTab = 0, activeTab: controlledActiveTab, onTabC
56
86
  }
57
87
  };
58
88
  };
89
+ const handleKeyDown = useArrowKeyNavigation({
90
+ elementsRef: tabRefs,
91
+ onActivate: index => activateTab(index)(),
92
+ });
59
93
  const activeTabProps = (_a = React.Children.toArray(children)[activeTab]) === null || _a === void 0 ? void 0 : _a.props;
60
94
  useEffect(() => {
61
95
  if (activeTab > React.Children.count(children) - 1) {
@@ -64,21 +98,20 @@ function Tabs({ children, defaultTab = 0, activeTab: controlledActiveTab, onTabC
64
98
  }, [React.Children.count(children)]);
65
99
  return (React.createElement("div", { className: styles.tabs },
66
100
  React.createElement("div", { className: overflowClassNames },
67
- React.createElement("ul", { role: "tablist", className: styles.tabRow, ref: tabRow }, React.Children.map(children, (tab, index) => (React.createElement(InternalTab, { label: tab.props.label, selected: activeTab === index, activateTab: activateTab(index), onClick: tab.props.onClick }))))),
101
+ React.createElement("ul", { role: "tablist", className: styles.tabRow, ref: tabRow, onKeyDown: handleKeyDown }, React.Children.map(children, (tab, index) => (React.createElement(InternalTab, { label: tab.props.label, selected: activeTab === index, activateTab: activateTab(index), onClick: tab.props.onClick, ref: el => (tabRefs.current[index] = el), tabIndex: activeTab === index ? 0 : -1 }))))),
68
102
  React.createElement("section", { role: "tabpanel", className: styles.tabContent, "aria-label": activeTabProps === null || activeTabProps === void 0 ? void 0 : activeTabProps.label }, activeTabProps === null || activeTabProps === void 0 ? void 0 : activeTabProps.children)));
69
103
  }
70
104
  function Tab({ label }) {
71
105
  return React.createElement(React.Fragment, null, label);
72
106
  }
73
- function InternalTab({ label, selected, activateTab, onClick = () => {
74
- return;
75
- }, }) {
107
+ const InternalTab = React.forwardRef(({ label, selected, activateTab, onClick, tabIndex }, ref) => {
76
108
  const className = classnames(styles.tab, { [styles.selected]: selected });
77
109
  return (React.createElement("li", { role: "presentation" },
78
110
  React.createElement("button", { type: "button", role: "tab", className: className, onClick: event => {
79
111
  activateTab();
80
- onClick(event);
81
- } }, typeof label === "string" ? (React.createElement(Typography, { element: "span", size: "large", fontWeight: "semiBold" }, label)) : (label))));
82
- }
112
+ onClick === null || onClick === void 0 ? void 0 : onClick(event);
113
+ }, ref: ref, tabIndex: tabIndex }, typeof label === "string" ? (React.createElement(Typography, { element: "span", size: "large", fontWeight: "semiBold" }, label)) : (label))));
114
+ });
115
+ InternalTab.displayName = "InternalTab";
83
116
 
84
117
  export { Tabs as T, Tab as a };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jobber/components",
3
- "version": "6.6.0",
3
+ "version": "6.7.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -487,5 +487,5 @@
487
487
  "> 1%",
488
488
  "IE 10"
489
489
  ],
490
- "gitHead": "8cb3c997c952f9319a5881b5cf0cb0a7aecb4d49"
490
+ "gitHead": "79e357ec4d4fa986b23ef2337525ab34d0f4b471"
491
491
  }