@khanacademy/math-input 8.1.0 → 8.1.2

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 (34) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/components/keypad-legacy/compute-layout-parameters.d.ts +6 -17
  3. package/dist/components/keypad-legacy/compute-layout-parameters.js.flow +13 -22
  4. package/dist/components/keypad-legacy/keypad-container.d.ts +2 -1
  5. package/dist/components/keypad-legacy/keypad-container.js.flow +7 -1
  6. package/dist/components/keypad-legacy/store/actions.d.ts +5 -3
  7. package/dist/components/keypad-legacy/store/actions.js.flow +8 -4
  8. package/dist/components/keypad-legacy/store/types.d.ts +14 -14
  9. package/dist/components/keypad-legacy/store/types.js.flow +14 -14
  10. package/dist/es/index.css +1 -1
  11. package/dist/es/index.js +132 -132
  12. package/dist/es/index.js.map +1 -1
  13. package/dist/fake-react-native-web/view.d.ts +1 -0
  14. package/dist/fake-react-native-web/view.js.flow +1 -0
  15. package/dist/index.css +1 -1
  16. package/dist/index.js +130 -134
  17. package/dist/index.js.map +1 -1
  18. package/index.html +17 -16
  19. package/less/echo.less +1 -1
  20. package/less/overrides.less +1 -1
  21. package/package.json +2 -2
  22. package/src/components/input/math-input.tsx +1 -0
  23. package/src/components/keypad/keypad-button.tsx +5 -3
  24. package/src/components/keypad/mobile-keypad.tsx +4 -0
  25. package/src/components/keypad-legacy/compute-layout-parameters.ts +127 -78
  26. package/src/components/keypad-legacy/keypad-button.tsx +2 -2
  27. package/src/components/keypad-legacy/keypad-container.tsx +30 -4
  28. package/src/components/keypad-legacy/store/actions.ts +12 -6
  29. package/src/components/keypad-legacy/store/layout-reducer.test.ts +171 -0
  30. package/src/components/keypad-legacy/store/layout-reducer.ts +46 -51
  31. package/src/components/keypad-legacy/store/types.ts +16 -14
  32. package/src/fake-react-native-web/view.tsx +2 -0
  33. package/src/full-math-input.stories.tsx +1 -1
  34. package/tsconfig-build.tsbuildinfo +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # @khanacademy/math-input
2
2
 
3
+ ## 8.1.2
4
+
5
+ ### Patch Changes
6
+
7
+ - ea9dac75: Bugfix: allow legacy keypad to be positioned absolutely in column
8
+ - 30a99b82: Allow v2 keypad to receive style prop like v1 keypad
9
+ - 3fa556a3: Fixes bug where mouse hover causes grid to expand
10
+
11
+ ## 8.1.1
12
+
13
+ ### Patch Changes
14
+
15
+ - Updated dependencies [57f75510]
16
+ - @khanacademy/perseus-core@0.1.1
17
+
3
18
  ## 8.1.0
4
19
 
5
20
  ### Minor Changes
@@ -18,22 +18,11 @@
18
18
  * to be conservative in our measurements and make things smaller than they
19
19
  * might need to be.
20
20
  */
21
- import { LayoutMode } from "../../enums";
22
- export declare const computeLayoutParameters: ({ numColumns, numMaxVisibleRows, numPages }: {
23
- numColumns: any;
24
- numMaxVisibleRows: any;
25
- numPages: any;
26
- }, { pageWidthPx, pageHeightPx }: {
27
- pageWidthPx: any;
28
- pageHeightPx: any;
29
- }, { deviceOrientation, deviceType }: {
30
- deviceOrientation: any;
31
- deviceType: any;
32
- }, { navigationPadEnabled, paginationEnabled, toolbarEnabled }: {
33
- navigationPadEnabled: any;
34
- paginationEnabled: any;
35
- toolbarEnabled: any;
36
- }) => {
37
- buttonDimensions: any;
21
+ import { DeviceOrientation, LayoutMode } from "../../enums";
22
+ import { GridDimensions, WidthHeight } from "./store/types";
23
+ type ComputedLayoutProperty = {
24
+ buttonDimensions: WidthHeight;
38
25
  layoutMode: LayoutMode;
39
26
  };
27
+ export declare const computeLayoutParameters: (gridDimensions: GridDimensions, pageDimensions: WidthHeight, containerDimensions: WidthHeight, deviceOrientation: DeviceOrientation, navigationPadEnabled: boolean, paginationEnabled: boolean, toolbarEnabled: boolean) => ComputedLayoutProperty;
28
+ export {};
@@ -4,27 +4,18 @@
4
4
  * Flowgen v1.21.0
5
5
  * @flow
6
6
  */
7
- import { LayoutMode } from "../../enums";
8
- declare export var computeLayoutParameters: (
9
- x: {|
10
- numColumns: any,
11
- numMaxVisibleRows: any,
12
- numPages: any,
13
- |},
14
- x: {|
15
- pageWidthPx: any,
16
- pageHeightPx: any,
17
- |},
18
- x: {|
19
- deviceOrientation: any,
20
- deviceType: any,
21
- |},
22
- x: {|
23
- navigationPadEnabled: any,
24
- paginationEnabled: any,
25
- toolbarEnabled: any,
26
- |}
27
- ) => {|
28
- buttonDimensions: any,
7
+ import { DeviceOrientation, LayoutMode } from "../../enums";
8
+ import { GridDimensions, WidthHeight } from "./store/types";
9
+ declare type ComputedLayoutProperty = {|
10
+ buttonDimensions: WidthHeight,
29
11
  layoutMode: LayoutMode,
30
12
  |};
13
+ declare export var computeLayoutParameters: (
14
+ gridDimensions: GridDimensions,
15
+ pageDimensions: WidthHeight,
16
+ containerDimensions: WidthHeight,
17
+ deviceOrientation: DeviceOrientation,
18
+ navigationPadEnabled: boolean,
19
+ paginationEnabled: boolean,
20
+ toolbarEnabled: boolean
21
+ ) => ComputedLayoutProperty;
@@ -11,7 +11,7 @@ interface ReduxProps {
11
11
  interface Props extends ReduxProps {
12
12
  onDismiss?: () => void;
13
13
  onElementMounted: (element: any) => void;
14
- onPageSizeChange?: (width: number, height: number) => void;
14
+ onPageSizeChange?: (pageWidth: number, pageHeight: number, containerWidth: number, containerHeight: number) => void;
15
15
  style?: StyleType;
16
16
  }
17
17
  type State = {
@@ -19,6 +19,7 @@ type State = {
19
19
  viewportWidth: string | number;
20
20
  };
21
21
  declare class KeypadContainer extends React.Component<Props, State> {
22
+ _containerRef: React.RefObject<HTMLDivElement>;
22
23
  _resizeTimeout: number | null | undefined;
23
24
  hasMounted: boolean | undefined;
24
25
  state: {
@@ -18,7 +18,12 @@ declare interface ReduxProps {
18
18
  declare type Props = {
19
19
  onDismiss?: () => void,
20
20
  onElementMounted: (element: any) => void,
21
- onPageSizeChange?: (width: number, height: number) => void,
21
+ onPageSizeChange?: (
22
+ pageWidth: number,
23
+ pageHeight: number,
24
+ containerWidth: number,
25
+ containerHeight: number
26
+ ) => void,
22
27
  style?: StyleType,
23
28
  } & ReduxProps;
24
29
  declare type State = {|
@@ -26,6 +31,7 @@ declare type State = {|
26
31
  viewportWidth: string | number,
27
32
  |};
28
33
  declare class KeypadContainer extends React.Component<Props, State> {
34
+ _containerRef: React.RefObject<HTMLDivElement>;
29
35
  _resizeTimeout: number | null | void;
30
36
  hasMounted: boolean | void;
31
37
  state: {|
@@ -18,10 +18,12 @@ type ConfigureKeypadAction = {
18
18
  export declare const configureKeypad: (configuration: KeypadConfiguration) => ConfigureKeypadAction;
19
19
  type SetPageSizeAction = {
20
20
  type: "SetPageSize";
21
- pageWidthPx: number;
22
- pageHeightPx: number;
21
+ pageWidth: number;
22
+ pageHeight: number;
23
+ containerWidth: number;
24
+ containerHeight: number;
23
25
  };
24
- export declare const setPageSize: (pageWidthPx: number, pageHeightPx: number) => SetPageSizeAction;
26
+ export declare const setPageSize: (pageWidth: number, pageHeight: number, containerWidth: number, containerHeight: number) => SetPageSizeAction;
25
27
  type RemoveEchoAction = {
26
28
  type: "RemoveEcho";
27
29
  animationId: string;
@@ -32,12 +32,16 @@ declare export var configureKeypad: (
32
32
  ) => ConfigureKeypadAction;
33
33
  declare type SetPageSizeAction = {|
34
34
  type: "SetPageSize",
35
- pageWidthPx: number,
36
- pageHeightPx: number,
35
+ pageWidth: number,
36
+ pageHeight: number,
37
+ containerWidth: number,
38
+ containerHeight: number,
37
39
  |};
38
40
  declare export var setPageSize: (
39
- pageWidthPx: number,
40
- pageHeightPx: number
41
+ pageWidth: number,
42
+ pageHeight: number,
43
+ containerWidth: number,
44
+ containerHeight: number
41
45
  ) => SetPageSizeAction;
42
46
  declare type RemoveEchoAction = {|
43
47
  type: "RemoveEcho",
@@ -19,21 +19,21 @@ export interface GestureState {
19
19
  export interface EchoState {
20
20
  echoes: ReadonlyArray<Echo>;
21
21
  }
22
+ export type GridDimensions = {
23
+ numRows: number;
24
+ numColumns: number;
25
+ numMaxVisibleRows: number;
26
+ numPages: number;
27
+ };
28
+ export type WidthHeight = {
29
+ width: number;
30
+ height: number;
31
+ };
22
32
  export interface LayoutState {
23
- gridDimensions: {
24
- numRows: number;
25
- numColumns: number;
26
- numMaxVisibleRows: number;
27
- numPages: number;
28
- };
29
- buttonDimensions: {
30
- widthPx: number;
31
- heightPx: number;
32
- };
33
- pageDimensions: {
34
- pageWidthPx: number;
35
- pageHeightPx: number;
36
- };
33
+ gridDimensions: GridDimensions;
34
+ buttonDimensions: WidthHeight;
35
+ containerDimensions: WidthHeight;
36
+ pageDimensions: WidthHeight;
37
37
  layoutMode: LayoutMode;
38
38
  paginationEnabled: boolean;
39
39
  navigationPadEnabled: boolean;
@@ -25,21 +25,21 @@ export interface GestureState {
25
25
  export interface EchoState {
26
26
  echoes: $ReadOnlyArray<Echo>;
27
27
  }
28
+ export type GridDimensions = {|
29
+ numRows: number,
30
+ numColumns: number,
31
+ numMaxVisibleRows: number,
32
+ numPages: number,
33
+ |};
34
+ export type WidthHeight = {|
35
+ width: number,
36
+ height: number,
37
+ |};
28
38
  export interface LayoutState {
29
- gridDimensions: {|
30
- numRows: number,
31
- numColumns: number,
32
- numMaxVisibleRows: number,
33
- numPages: number,
34
- |};
35
- buttonDimensions: {|
36
- widthPx: number,
37
- heightPx: number,
38
- |};
39
- pageDimensions: {|
40
- pageWidthPx: number,
41
- pageHeightPx: number,
42
- |};
39
+ gridDimensions: GridDimensions;
40
+ buttonDimensions: WidthHeight;
41
+ containerDimensions: WidthHeight;
42
+ pageDimensions: WidthHeight;
43
43
  layoutMode: LayoutMode;
44
44
  paginationEnabled: boolean;
45
45
  navigationPadEnabled: boolean;
package/dist/es/index.css CHANGED
@@ -102,7 +102,7 @@
102
102
  border-color: white;
103
103
  }
104
104
  .keypad-input .mq-hasCursor:empty:not(.mq-root-block):after {
105
- content: 'c';
105
+ content: "c";
106
106
  }
107
107
  .keypad-input .mq-math-mode .mq-selection .mq-non-leaf,
108
108
  .keypad-input .mq-editable-field .mq-selection .mq-non-leaf {
package/dist/es/index.js CHANGED
@@ -79,7 +79,8 @@ class View extends React.Component {
79
79
  onTouchMove: this.props.onTouchMove,
80
80
  onTouchStart: this.props.onTouchStart,
81
81
  "aria-label": this.props.ariaLabel,
82
- role: this.props.role
82
+ role: this.props.role,
83
+ ref: this.props.forwardRef
83
84
  }, this.props.children);
84
85
  }
85
86
  }
@@ -142,10 +143,6 @@ const innerBorderColor = offBlack16;
142
143
  const innerBorderStyle = "solid";
143
144
  const innerBorderWidthPx = 1;
144
145
 
145
- // The width at which a device is classified as a "tablet" for the purposes
146
- // of the keypad layout.
147
- const tabletCutoffPx = 600;
148
-
149
146
  // The dimensions that define various components in the tree, which may be
150
147
  // needed outside of those components in order to determine various layout
151
148
  // parameters.
@@ -862,11 +859,6 @@ let DeviceOrientation = /*#__PURE__*/function (DeviceOrientation) {
862
859
  DeviceOrientation["PORTRAIT"] = "PORTRAIT";
863
860
  return DeviceOrientation;
864
861
  }({});
865
- let DeviceType = /*#__PURE__*/function (DeviceType) {
866
- DeviceType["PHONE"] = "PHONE";
867
- DeviceType["TABLET"] = "TABLET";
868
- return DeviceType;
869
- }({});
870
862
  let LayoutMode = /*#__PURE__*/function (LayoutMode) {
871
863
  LayoutMode["FULLSCREEN"] = "FULLSCREEN";
872
864
  LayoutMode["COMPACT"] = "COMPACT";
@@ -3833,19 +3825,21 @@ const styles$e = StyleSheet.create({
3833
3825
  boxSizing: "border-box",
3834
3826
  background: Color.white,
3835
3827
  borderRadius: 4,
3836
- border: `1px solid transparent`,
3828
+ borderWidth: 2,
3829
+ borderStyle: "solid",
3830
+ borderColor: "transparent",
3837
3831
  flex: 1,
3838
3832
  minHeight: 42,
3839
3833
  minWidth: 42,
3840
3834
  padding: 1
3841
3835
  },
3842
3836
  hovered: {
3843
- border: `2px solid ${Color.blue}`,
3837
+ borderColor: Color.blue,
3844
3838
  padding: 1,
3845
3839
  boxShadow: "none"
3846
3840
  },
3847
3841
  focused: {
3848
- border: `2px solid ${Color.blue}`,
3842
+ borderColor: Color.blue,
3849
3843
  padding: 0,
3850
3844
  boxShadow: "none"
3851
3845
  },
@@ -4299,12 +4293,19 @@ class MobileKeypad extends React.Component {
4299
4293
  });
4300
4294
  }
4301
4295
  render() {
4296
+ const {
4297
+ style
4298
+ } = this.props;
4302
4299
  const {
4303
4300
  active,
4304
4301
  cursor,
4305
4302
  keypadConfig
4306
4303
  } = this.state;
4307
- const containerStyle = [styles$c.keypadContainer, active ? styles$c.activeKeypadContainer : null];
4304
+ const containerStyle = [
4305
+ // internal styles
4306
+ styles$c.keypadContainer, active ? styles$c.activeKeypadContainer : null,
4307
+ // styles passed as props
4308
+ ...(Array.isArray(style) ? style : [style])];
4308
4309
  const isExpression = (keypadConfig == null ? void 0 : keypadConfig.keypadType) === "EXPRESSION";
4309
4310
  return /*#__PURE__*/React.createElement(View, {
4310
4311
  style: containerStyle,
@@ -6221,8 +6222,8 @@ const styleForButtonDimensions = (heightPx, widthPx) => {
6221
6222
  };
6222
6223
  const mapStateToProps$7 = state => {
6223
6224
  return {
6224
- heightPx: state.layout.buttonDimensions.heightPx,
6225
- widthPx: state.layout.buttonDimensions.widthPx
6225
+ heightPx: state.layout.buttonDimensions.height,
6226
+ widthPx: state.layout.buttonDimensions.width
6226
6227
  };
6227
6228
  };
6228
6229
  var KeypadButton$1 = connect(mapStateToProps$7, null, null, {
@@ -6631,11 +6632,13 @@ const configureKeypad = configuration => {
6631
6632
  configuration
6632
6633
  };
6633
6634
  };
6634
- const setPageSize = (pageWidthPx, pageHeightPx) => {
6635
+ const setPageSize = (pageWidth, pageHeight, containerWidth, containerHeight) => {
6635
6636
  return {
6636
6637
  type: "SetPageSize",
6637
- pageWidthPx,
6638
- pageHeightPx
6638
+ pageWidth,
6639
+ pageHeight,
6640
+ containerWidth,
6641
+ containerHeight
6639
6642
  };
6640
6643
  };
6641
6644
  const removeEcho = animationId => {
@@ -7362,6 +7365,7 @@ const {
7362
7365
  class KeypadContainer extends React.Component {
7363
7366
  constructor(...args) {
7364
7367
  super(...args);
7368
+ this._containerRef = /*#__PURE__*/React.createRef();
7365
7369
  this._resizeTimeout = void 0;
7366
7370
  this.hasMounted = void 0;
7367
7371
  this.state = {
@@ -7379,13 +7383,15 @@ class KeypadContainer extends React.Component {
7379
7383
  }
7380
7384
  };
7381
7385
  this._onResize = () => {
7382
- var _this$props$onPageSiz, _this$props;
7386
+ var _this$_containerRef$c, _this$_containerRef$c2, _this$props$onPageSiz, _this$props;
7383
7387
  // Whenever the page resizes, we need to force an update, as the button
7384
7388
  // heights and keypad width are computed based on horizontal space.
7385
7389
  this.setState({
7386
7390
  viewportWidth: window.innerWidth
7387
7391
  });
7388
- (_this$props$onPageSiz = (_this$props = this.props).onPageSizeChange) == null ? void 0 : _this$props$onPageSiz.call(_this$props, window.innerWidth, window.innerHeight);
7392
+ const containerWidth = ((_this$_containerRef$c = this._containerRef.current) == null ? void 0 : _this$_containerRef$c.clientWidth) || 0;
7393
+ const containerHeight = ((_this$_containerRef$c2 = this._containerRef.current) == null ? void 0 : _this$_containerRef$c2.clientHeight) || 0;
7394
+ (_this$props$onPageSiz = (_this$props = this.props).onPageSizeChange) == null ? void 0 : _this$props$onPageSiz.call(_this$props, window.innerWidth, window.innerHeight, containerWidth, containerHeight);
7389
7395
  };
7390
7396
  this.renderKeypad = () => {
7391
7397
  const {
@@ -7480,7 +7486,8 @@ class KeypadContainer extends React.Component {
7480
7486
  return /*#__PURE__*/React.createElement(View, {
7481
7487
  style: keypadContainerStyle,
7482
7488
  dynamicStyle: dynamicStyle,
7483
- extraClassName: "keypad-container"
7489
+ extraClassName: "keypad-container",
7490
+ forwardRef: this._containerRef
7484
7491
  }, /*#__PURE__*/React.createElement(View, {
7485
7492
  style: keypadStyle,
7486
7493
  ref: element => {
@@ -7566,8 +7573,8 @@ const mapStateToProps = state => {
7566
7573
  };
7567
7574
  const mapDispatchToProps = dispatch => {
7568
7575
  return {
7569
- onPageSizeChange: (pageWidthPx, pageHeightPx) => {
7570
- dispatch(setPageSize(pageWidthPx, pageHeightPx));
7576
+ onPageSizeChange: (pageWidth, pageHeight, containerWidth, containerHeight) => {
7577
+ dispatch(setPageSize(pageWidth, pageHeight, containerWidth, containerHeight));
7571
7578
  }
7572
7579
  };
7573
7580
  };
@@ -8494,89 +8501,91 @@ const maxPortraitBrowserChrome = safariToolbar + (safariNavBarWhenExpanded - saf
8494
8501
  // (We don't need to account for the expanded navbar, since we include the
8495
8502
  // difference when reserving space above the keypad.)
8496
8503
  const worstCaseAspectRatio = 320 / (480 - safariNavBarWhenShrunk);
8497
- const computeLayoutParameters = ({
8498
- numColumns,
8499
- numMaxVisibleRows,
8500
- numPages
8501
- }, {
8502
- pageWidthPx,
8503
- pageHeightPx
8504
- }, {
8505
- deviceOrientation,
8506
- deviceType
8507
- }, {
8508
- navigationPadEnabled,
8509
- paginationEnabled,
8510
- toolbarEnabled
8511
- }) => {
8504
+ function getButtonWidth(gridDimensions, containerDimensions, navigationPadEnabled, paginationEnabled, isLandscape) {
8505
+ const {
8506
+ numColumns,
8507
+ numPages
8508
+ } = gridDimensions;
8509
+
8510
+ // We can use the container width as the effective width.
8511
+ let effectiveWidth = containerDimensions.width;
8512
+ if (navigationPadEnabled) {
8513
+ effectiveWidth -= navigationPadWidthPx;
8514
+ }
8515
+ let buttonWidthPx;
8516
+ if (numPages > 1) {
8517
+ const effectiveNumColumns = paginationEnabled ? numColumns : numColumns * numPages;
8518
+ buttonWidthPx = effectiveWidth / effectiveNumColumns;
8519
+ } else {
8520
+ buttonWidthPx = isLandscape ? maxButtonSize : effectiveWidth / numColumns;
8521
+ }
8522
+ return buttonWidthPx;
8523
+ }
8524
+ function getButtonHeight(gridDimensions, pageDimensions, containerDimensions, paginationEnabled, toolbarEnabled, isLandscape) {
8525
+ const {
8526
+ numMaxVisibleRows
8527
+ } = gridDimensions;
8528
+
8529
+ // In many cases, the browser chrome will already have been factored
8530
+ // into `pageHeight`. But we have no way of knowing if that's
8531
+ // the case or not. As such, we take a conservative approach and
8532
+ // assume that the chrome is _never_ included in `pageHeight`.
8533
+ const browserChromeHeight = isLandscape ? maxLandscapeBrowserChrome : maxPortraitBrowserChrome;
8534
+
8535
+ // Count up all the space that we need to reserve on the page.
8536
+ // Namely, we need to account for:
8537
+ // 1. Space between the keypad and the top of the page.
8538
+ // 2. The presence of the exercise toolbar.
8539
+ // 3. The presence of the view pager indicator.
8540
+ // 4. Any browser chrome that may appear later.
8541
+ const reservedSpace = minSpaceAboveKeypad + browserChromeHeight + (toolbarEnabled ? toolbarHeightPx : 0) + (paginationEnabled ? pageIndicatorHeightPx : 0);
8542
+
8543
+ // For the height, we take
8544
+ // another conservative measure when in portrait by assuming that
8545
+ // the device has the worst possible aspect ratio. In other words,
8546
+ // we ignore the device height in portrait and assume the worst.
8547
+ // This prevents the keypad from changing size when browser chrome
8548
+ // appears and disappears.
8549
+ const effectiveHeight = isLandscape ? pageDimensions.height : containerDimensions.width / worstCaseAspectRatio;
8550
+
8551
+ // In computing the
8552
+ // height, accommodate for the maximum number of rows that will ever be
8553
+ // visible (since the toggling of popovers can increase the number of
8554
+ // visible rows).
8555
+ const maxKeypadHeight = effectiveHeight - reservedSpace;
8556
+ const buttonHeightPx = Math.max(Math.min(maxKeypadHeight / numMaxVisibleRows, maxButtonSize), minButtonHeight);
8557
+ return buttonHeightPx;
8558
+ }
8559
+ const computeLayoutParameters = (gridDimensions, pageDimensions, containerDimensions, deviceOrientation, navigationPadEnabled, paginationEnabled, toolbarEnabled) => {
8560
+ const {
8561
+ numColumns,
8562
+ numPages
8563
+ } = gridDimensions;
8564
+
8512
8565
  // First, compute some values that will be used in multiple computations.
8513
8566
  const effectiveNumColumns = paginationEnabled ? numColumns : numColumns * numPages;
8514
8567
 
8515
8568
  // Then, compute the button dimensions based on the provided parameters.
8516
- let buttonDimensions;
8517
- if (deviceType === DeviceType.PHONE) {
8518
- const isLandscape = deviceOrientation === DeviceOrientation.LANDSCAPE;
8519
-
8520
- // In many cases, the browser chrome will already have been factored
8521
- // into `pageHeightPx`. But we have no way of knowing if that's
8522
- // the case or not. As such, we take a conservative approach and
8523
- // assume that the chrome is _never_ included in `pageHeightPx`.
8524
- const browserChromeHeight = isLandscape ? maxLandscapeBrowserChrome : maxPortraitBrowserChrome;
8525
-
8526
- // Count up all the space that we need to reserve on the page.
8527
- // Namely, we need to account for:
8528
- // 1. Space between the keypad and the top of the page.
8529
- // 2. The presence of the exercise toolbar.
8530
- // 3. The presence of the view pager indicator.
8531
- // 4. Any browser chrome that may appear later.
8532
- const reservedSpace = minSpaceAboveKeypad + browserChromeHeight + (toolbarEnabled ? toolbarHeightPx : 0) + (paginationEnabled ? pageIndicatorHeightPx : 0);
8533
-
8534
- // Next, compute the effective width and height. We can use the page
8535
- // width as the effective width. For the height, though, we take
8536
- // another conservative measure when in portrait by assuming that
8537
- // the device has the worst possible aspect ratio. In other words,
8538
- // we ignore the device height in portrait and assume the worst.
8539
- // This prevents the keypad from changing size when browser chrome
8540
- // appears and disappears.
8541
- const effectiveWidth = pageWidthPx;
8542
- const effectiveHeight = isLandscape ? pageHeightPx : pageWidthPx / worstCaseAspectRatio;
8543
- const maxKeypadHeight = effectiveHeight - reservedSpace;
8544
-
8545
- // Finally, compute the button height and width. In computing the
8546
- // height, accommodate for the maximum number of rows that will ever be
8547
- // visible (since the toggling of popovers can increase the number of
8548
- // visible rows).
8549
- const buttonHeightPx = Math.max(Math.min(maxKeypadHeight / numMaxVisibleRows, maxButtonSize), minButtonHeight);
8550
- let buttonWidthPx;
8551
- if (numPages > 1) {
8552
- const _effectiveNumColumns = paginationEnabled ? numColumns : numColumns * numPages;
8553
- buttonWidthPx = effectiveWidth / _effectiveNumColumns;
8554
- } else {
8555
- buttonWidthPx = isLandscape ? maxButtonSize : effectiveWidth / numColumns;
8556
- }
8557
- buttonDimensions = {
8558
- widthPx: buttonWidthPx,
8559
- heightPx: buttonHeightPx
8560
- };
8561
- } else if (deviceType === DeviceType.TABLET) {
8562
- buttonDimensions = {
8563
- widthPx: maxButtonSize,
8564
- heightPx: maxButtonSize
8565
- };
8566
- } else {
8567
- throw new Error("Invalid device type: " + deviceType);
8568
- }
8569
+ const isLandscape = deviceOrientation === DeviceOrientation.LANDSCAPE;
8570
+ const buttonWidth = getButtonWidth(gridDimensions, containerDimensions, navigationPadEnabled, paginationEnabled, isLandscape);
8571
+ const buttonHeight = getButtonHeight(gridDimensions, pageDimensions, containerDimensions, paginationEnabled, toolbarEnabled, isLandscape);
8572
+ const buttonDimensions = {
8573
+ width: buttonWidth,
8574
+ height: buttonHeight
8575
+ };
8569
8576
 
8570
8577
  // Finally, determine whether the keypad should be rendered in the
8571
8578
  // fullscreen layout by determining its resultant width.
8572
8579
  const numSeparators = (navigationPadEnabled ? 1 : 0) + (!paginationEnabled ? numPages - 1 : 0);
8573
- const keypadWidth = effectiveNumColumns * buttonDimensions.widthPx + (navigationPadEnabled ? navigationPadWidthPx : 0) + numSeparators * innerBorderWidthPx;
8580
+ const keypadWidth = effectiveNumColumns * buttonDimensions.width + (navigationPadEnabled ? navigationPadWidthPx : 0) + numSeparators * innerBorderWidthPx;
8574
8581
  return {
8575
8582
  buttonDimensions,
8576
- layoutMode: keypadWidth >= pageWidthPx ? LayoutMode.FULLSCREEN : LayoutMode.COMPACT
8583
+ layoutMode: keypadWidth >= containerDimensions.width ? LayoutMode.FULLSCREEN : LayoutMode.COMPACT
8577
8584
  };
8578
8585
  };
8579
8586
 
8587
+ const expandedViewThreshold = 682;
8588
+ const navigationViewThreshold = 800;
8580
8589
  const initialLayoutState = {
8581
8590
  gridDimensions: {
8582
8591
  numRows: keypadForType[defaultKeypadType].rows,
@@ -8585,12 +8594,16 @@ const initialLayoutState = {
8585
8594
  numPages: keypadForType[defaultKeypadType].numPages
8586
8595
  },
8587
8596
  buttonDimensions: {
8588
- widthPx: 48,
8589
- heightPx: 48
8597
+ width: 48,
8598
+ height: 48
8590
8599
  },
8591
8600
  pageDimensions: {
8592
- pageWidthPx: 0,
8593
- pageHeightPx: 0
8601
+ width: 0,
8602
+ height: 0
8603
+ },
8604
+ containerDimensions: {
8605
+ width: 0,
8606
+ height: 0
8594
8607
  },
8595
8608
  layoutMode: LayoutMode.FULLSCREEN,
8596
8609
  paginationEnabled: false,
@@ -8601,37 +8614,16 @@ const initialLayoutState = {
8601
8614
  * Compute the additional layout state based on the provided page and grid
8602
8615
  * dimensions.
8603
8616
  */
8604
- const layoutParametersForDimensions = (pageDimensions, gridDimensions) => {
8605
- const {
8606
- pageWidthPx,
8607
- pageHeightPx
8608
- } = pageDimensions;
8609
-
8617
+ const layoutParametersForDimensions = (pageDimensions, containerDimensions, gridDimensions) => {
8610
8618
  // Determine the device type and orientation.
8611
- const deviceOrientation = pageWidthPx > pageHeightPx ? DeviceOrientation.LANDSCAPE : DeviceOrientation.PORTRAIT;
8612
- const deviceType = Math.min(pageWidthPx, pageHeightPx) > tabletCutoffPx ? DeviceType.TABLET : DeviceType.PHONE;
8619
+ const deviceOrientation = containerDimensions.width > containerDimensions.height ? DeviceOrientation.LANDSCAPE : DeviceOrientation.PORTRAIT;
8613
8620
 
8614
8621
  // Using that information, make some decisions (or assumptions)
8615
8622
  // about the resulting layout.
8616
- const navigationPadEnabled = deviceType === DeviceType.TABLET;
8617
- const paginationEnabled = deviceType === DeviceType.PHONE && deviceOrientation === DeviceOrientation.PORTRAIT;
8618
- const deviceInfo = {
8619
- deviceOrientation,
8620
- deviceType
8621
- };
8622
- const layoutOptions = {
8623
- navigationPadEnabled,
8624
- paginationEnabled,
8625
- // HACK(charlie): It's not great that we're making assumptions about
8626
- // the toolbar (which is rendered by webapp, and should always be
8627
- // visible and anchored to the bottom of the page for phone and
8628
- // tablet exercises). But this is primarily a heuristic (the goal is
8629
- // to preserve a 'good' amount of space between the top of the
8630
- // keypad and the top of the page) so we afford to have some margin
8631
- // of error.
8632
- toolbarEnabled: true
8633
- };
8634
- return _extends({}, computeLayoutParameters(gridDimensions, pageDimensions, deviceInfo, layoutOptions), {
8623
+ const navigationPadEnabled = containerDimensions.width > navigationViewThreshold;
8624
+ const paginationEnabled = containerDimensions.width < expandedViewThreshold;
8625
+ const toolbarEnabled = true;
8626
+ return _extends({}, computeLayoutParameters(gridDimensions, pageDimensions, containerDimensions, deviceOrientation, navigationPadEnabled, paginationEnabled, toolbarEnabled), {
8635
8627
  // Pass along some of the layout information, so that other
8636
8628
  // components in the heirarchy can adapt appropriately.
8637
8629
  navigationPadEnabled,
@@ -8650,20 +8642,28 @@ const layoutReducer = function layoutReducer(state = initialLayoutState, action)
8650
8642
  numMaxVisibleRows: keypadForType[keypadType].maxVisibleRows,
8651
8643
  numPages: keypadForType[keypadType].numPages
8652
8644
  };
8653
- return _extends({}, state, layoutParametersForDimensions(state.pageDimensions, gridDimensions), {
8645
+ const layoutParams = layoutParametersForDimensions(state.pageDimensions, state.containerDimensions, gridDimensions);
8646
+ return _extends({}, state, layoutParams, {
8654
8647
  gridDimensions
8655
8648
  });
8656
8649
  case "SetPageSize":
8657
8650
  const {
8658
- pageWidthPx,
8659
- pageHeightPx
8651
+ pageWidth,
8652
+ pageHeight,
8653
+ containerWidth,
8654
+ containerHeight
8660
8655
  } = action;
8661
8656
  const pageDimensions = {
8662
- pageWidthPx,
8663
- pageHeightPx
8657
+ width: pageWidth,
8658
+ height: pageHeight
8659
+ };
8660
+ const containerDimensions = {
8661
+ width: containerWidth,
8662
+ height: containerHeight
8664
8663
  };
8665
- return _extends({}, state, layoutParametersForDimensions(pageDimensions, state.gridDimensions), {
8666
- pageDimensions
8664
+ return _extends({}, state, layoutParametersForDimensions(pageDimensions, containerDimensions, state.gridDimensions), {
8665
+ pageDimensions,
8666
+ containerDimensions
8667
8667
  });
8668
8668
  default:
8669
8669
  return state;