@khanacademy/math-input 19.0.0 → 19.2.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.
package/dist/index.js CHANGED
@@ -44,7 +44,7 @@ var PropTypes__default = /*#__PURE__*/_interopDefaultLegacy(PropTypes);
44
44
 
45
45
  // This file is processed by a Rollup plugin (replace) to inject the production
46
46
  const libName = "@khanacademy/math-input";
47
- const libVersion = "19.0.0";
47
+ const libVersion = "19.2.0";
48
48
  perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
49
49
 
50
50
  function _extends() {
@@ -2659,41 +2659,65 @@ function imageTintColor(itemState, hovered, focused, pressed) {
2659
2659
  }
2660
2660
  return wonderBlocksTokens.color.offBlack64;
2661
2661
  }
2662
- class TabbarItem extends React__namespace.Component {
2663
- render() {
2664
- const {
2665
- onClick,
2666
- itemType,
2667
- itemState
2668
- } = this.props;
2669
- return /*#__PURE__*/React__namespace.createElement(Clickable__default["default"], {
2670
- onClick: onClick,
2671
- disabled: itemState === "disabled",
2672
- "aria-label": itemType,
2673
- style: styles$6.clickable,
2674
- "aria-selected": itemState === "active",
2675
- role: "tab"
2676
- }, _ref => {
2677
- let {
2678
- hovered,
2679
- focused,
2680
- pressed
2681
- } = _ref;
2682
- const tintColor = imageTintColor(itemState, hovered, focused, pressed);
2683
- return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
2684
- style: [styles$6.base, itemState !== "disabled" && hovered && styles$6.hovered, focused && styles$6.focused, pressed && styles$6.pressed]
2685
- }, /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
2686
- style: [styles$6.innerBox, pressed && styles$6.innerBoxPressed]
2687
- }, /*#__PURE__*/React__namespace.createElement(IconAsset, {
2688
- type: itemType,
2689
- tintColor: tintColor
2690
- })), itemState === "active" && /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
2691
- style: [styles$6.activeIndicator, {
2692
- backgroundColor: tintColor
2693
- }]
2694
- }));
2695
- });
2696
- }
2662
+ function TabbarItem(props) {
2663
+ const {
2664
+ onClick,
2665
+ itemType,
2666
+ itemState,
2667
+ focus,
2668
+ role
2669
+ } = props;
2670
+ const tabRef = React.useRef(null);
2671
+ React.useEffect(() => {
2672
+ let timeout;
2673
+ if (role === "tab" && focus) {
2674
+ /**
2675
+ * When tabs are within a WonderBlocks Popover component, the
2676
+ * manner in which the component is rendered and moved causes
2677
+ * focus to snap to the bottom of the page on first focus.
2678
+ *
2679
+ * This timeout moves around that by delaying the focus enough
2680
+ * to wait for the WonderBlock Popover to move to the correct
2681
+ * location and scroll the user to the correct location.
2682
+ * */
2683
+ timeout = setTimeout(() => {
2684
+ if (tabRef?.current) {
2685
+ // Move element into view when it is focused
2686
+ tabRef?.current.focus();
2687
+ }
2688
+ }, 0);
2689
+ }
2690
+ return () => clearTimeout(timeout);
2691
+ }, [role, focus, tabRef]);
2692
+ return /*#__PURE__*/React__namespace.createElement(Clickable__default["default"], {
2693
+ onClick: onClick,
2694
+ disabled: itemState === "disabled",
2695
+ "aria-label": itemType,
2696
+ style: styles$6.clickable,
2697
+ "aria-selected": itemState === "active",
2698
+ tabIndex: role === "button" ? 0 : focus ? 0 : -1,
2699
+ role: role,
2700
+ ref: tabRef
2701
+ }, _ref => {
2702
+ let {
2703
+ hovered,
2704
+ focused,
2705
+ pressed
2706
+ } = _ref;
2707
+ const tintColor = imageTintColor(itemState, hovered, focused, pressed);
2708
+ return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
2709
+ style: [styles$6.base, itemState !== "disabled" && hovered && styles$6.hovered, focused && styles$6.focused, pressed && styles$6.pressed]
2710
+ }, /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
2711
+ style: [styles$6.innerBox, pressed && styles$6.innerBoxPressed]
2712
+ }, /*#__PURE__*/React__namespace.createElement(IconAsset, {
2713
+ type: itemType,
2714
+ tintColor: tintColor
2715
+ })), itemState === "active" && /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
2716
+ style: [styles$6.activeIndicator, {
2717
+ backgroundColor: tintColor
2718
+ }]
2719
+ }));
2720
+ });
2697
2721
  }
2698
2722
 
2699
2723
  const styles$5 = aphrodite.StyleSheet.create({
@@ -2717,19 +2741,42 @@ function Tabbar(props) {
2717
2741
  onSelectItem,
2718
2742
  style
2719
2743
  } = props;
2744
+ const selectedIndex = items.indexOf(selectedItem);
2745
+ const [focus, setFocus] = React.useState(selectedIndex === -1 ? 0 : selectedIndex);
2746
+ /**
2747
+ * Custom function to handle arrow key navigation for the TabBar for each TabItem.
2748
+ * This implementation also circular in that if the user goes past the end of
2749
+ * the list they will go back to the beginning and vise versa.
2750
+ * This is the recommended pattern per WCAG implementation:
2751
+ * https://www.w3.org/WAI/ARIA/apg/patterns/tabs/examples/tabs-manual/
2752
+ * @param e - onKeyDown event data.
2753
+ */
2754
+ const onArrowKeyFocus = e => {
2755
+ if (e.keyCode === 39) {
2756
+ // Right arrow
2757
+ setFocus(focus === items.length - 1 ? 0 : focus + 1);
2758
+ } else if (e.keyCode === 37) {
2759
+ // Left arrow
2760
+ setFocus(focus === 0 ? items.length - 1 : focus - 1);
2761
+ }
2762
+ };
2720
2763
  return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
2721
- style: [styles$5.tabbar, style],
2722
- role: "tablist"
2723
- }, /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
2724
- style: [styles$5.pages]
2725
- }, items.map(item => /*#__PURE__*/React__namespace.createElement(TabbarItem, {
2764
+ style: [styles$5.tabbar, style]
2765
+ }, items.length > 0 && /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
2766
+ style: [styles$5.pages],
2767
+ role: "tablist",
2768
+ onKeyDown: onArrowKeyFocus
2769
+ }, items.map((item, index) => /*#__PURE__*/React__namespace.createElement(TabbarItem, {
2770
+ role: "tab",
2726
2771
  key: `tabbar-item-${item}`,
2727
2772
  itemState: item === selectedItem ? "active" : "inactive",
2728
2773
  itemType: item,
2774
+ focus: focus === index,
2729
2775
  onClick: () => {
2730
2776
  onSelectItem(item);
2731
2777
  }
2732
2778
  }))), /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, null, onClickClose && /*#__PURE__*/React__namespace.createElement(TabbarItem, {
2779
+ role: "button",
2733
2780
  itemState: "inactive",
2734
2781
  itemType: "Dismiss",
2735
2782
  onClick: onClickClose
@@ -5587,7 +5634,6 @@ function Keypad(props) {
5587
5634
  style: styles$1.keypadInnerContainer
5588
5635
  }, /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
5589
5636
  style: [styles$1.keypadGrid, gridStyle],
5590
- tabIndex: 0,
5591
5637
  "aria-label": "Keypad"
5592
5638
  }, selectedPage === "Fractions" && /*#__PURE__*/React__namespace.createElement(FractionsPage, {
5593
5639
  onClickKey: onClickKey,