Newer
Older
CrypticOreWallet / src / shared-components / list-view / ListPagination.tsx
@Drew Lemmy Drew Lemmy on 17 Sep 2020 3 KB feat: basic table in 'my wallets' view
import React, { ReactNode, Component, createRef, ChangeEvent } from "react";

import InputGroup from "react-bootstrap/InputGroup";
import Button from "react-bootstrap/Button";
import FormControl from "react-bootstrap/FormControl";

import "./ListPagination.scss";

interface Props {
  defaultPage: number;
  pages: number;
}

interface State {
  focus: boolean;
  value: string;
}

export class ListPagination extends Component<Props, State> {
  private textInput = createRef<HTMLInputElement>();
  private selected = false;

  constructor(props: Props) {
    super(props);

    this.state = { 
      focus: false,
      value: this.props.defaultPage.toString()
    };
  }

  private handleFocus() {
    // First of all, assign the focus (triggering re-render)
    this.setState({ focus: true });
  }

  private handleUnfocus() {
    // First of all, unassign the focus (triggering re-render)
    this.setState({ focus: false });
    this.selected = false;
  }

  private handleChange(event: ChangeEvent<HTMLInputElement>) {
    if (!this.state.focus) return; // Don't care if not focused (it was us)

    const attemptedValue = event.target.value; 
    if (!attemptedValue.match(/^[0-9]+$/)) return; // Ensure it's a number

    const value = Math.min(Math.max(parseInt(attemptedValue), 1), this.props.pages).toString();

    // Let the user actually type in the input
    this.setState({ value });
  }

  componentDidUpdate(): void {
    // Highlight the text in the input if we just focused it
    const input = this.textInput.current;
    if (this.state.focus && input && !this.selected) {
      this.selected = true;
      input.select();
      input.setSelectionRange(0, input.value.length);
    }
  }

  private getFriendlyText() {
    const pageNumber = parseInt(this.state.value);
    return `Page ${pageNumber.toLocaleString()} of ${this.props.pages}`;
  }

  render(): ReactNode {
    return (
      <InputGroup className="list-view-pagination">
        {/* Group the prev/next buttons with the page text/input */}
        {/* Prev button */}
        <InputGroup.Prepend>
          <Button variant="secondary"><span className="icon-left-open"></span></Button>
        </InputGroup.Prepend>

        {/* Used to contain the real input and the fake input */}
        <div className="list-view-pagination-inner">
          {/* Dummy text used to auto-grow the input */}
          <span className="form-control list-view-pagination-grower hidden">
            {this.getFriendlyText()}
          </span>
    
          {/* Page number input */}
          <FormControl 
            type="text"
            pattern="[0-9]*" /* iOS, Firefox */
            inputMode="numeric" /* Chrome */
            value={this.state.focus ? this.state.value : this.getFriendlyText()}
            onChange={this.handleChange.bind(this)}
            onFocus={this.handleFocus.bind(this)}
            onBlur={this.handleUnfocus.bind(this)}
            ref={this.textInput}
          />
        </div>
  
        {/* Next button */}
        <InputGroup.Append>
          <Button variant="secondary"><span className="icon-right-open"></span></Button>
        </InputGroup.Append>    
      </InputGroup>
    );
  }
};