<commit_before>/*
 *  This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.
 *  License: AGPLv3 s.t. "Commons Clause" – see LICENSE.md for details
 */

/*
We use so little of react-bootstrap in CoCalc that for a first quick round
of switching to antd, I'm going to see if it isn't easy to re-implement
much of the same functionality on top of antd

Obviously, this is meant to be temporary, since it is far better if our
code consistently uses the antd api explicitly.  However, there are
some serious problems / bug /issues with using our stupid old react-bootstrap
*at all*, hence this.
*/

// TODO: What we haven't converted yet, but do use in CoCalc:
export {
  FormControl,
  FormGroup,
  Form,
  InputGroup,
  Navbar,
  Nav,
  NavItem,
  Table,
} from "react-bootstrap";

import { MouseEventHandler } from "react";
import { Rendered } from "./app-framework";
import { r_join} from "./components/r_join";
import { Space } from "./components/space";

import {
  Alert as AntdAlert,
  Button as AntdButton,
  Card as AntdCard,
  Tabs as AntdTabs,
  Modal as AntdModal,
  Checkbox as AntdCheckbox,
  Row as AntdRow,
  Col as AntdCol,
  Tooltip,
} from "antd";

// Note regarding buttons -- there are 6 semantics meanings in bootstrap, but
// only four in antd, and it we can't automatically collapse them down in a meaningful
// way without fundamentally removing information and breaking our UI (e.g., buttons
// change look after an assignment is sent successfully in a course).
export type ButtonStyle =
  | "primary"
  | "success"
  | "default"
  | "info"
  | "warning"
  | "danger"
  | "link";

const BS_STYLE_TO_TYPE: {
  [name in ButtonStyle]: "primary" | "default" | "dashed" | "danger" | "link";
} = {
  primary: "primary",
  success: "default", // antd doesn't have this so we do it via style below.
  default: "default",
  info: "default", // antd doesn't have this so we do it via style below.
  warning: "default", // antd doesn't have this so we do it via style below.
  danger: "danger",
  link: "link",
};

export type ButtonSize = "large" | "small" | "xsmall";

function parse_bsStyle(props: {
  bsStyle?: ButtonStyle;
  style?: React.CSSProperties;
  disabled?: boolean;
}): {
  type: "primary" | "default" | "dashed" | "link";
  style: React.CSSProperties;
  danger?: boolean;
  ghost?: boolean;
  disabled?: boolean;
  loading?: boolean;
} {
  let type =
    props.bsStyle == null
      ? "default"
      : BS_STYLE_TO_TYPE[props.bsStyle] ?? "default";

  let style: React.CSSProperties | undefined = undefined;
  // antd has no analogue of "success" & "warning", it's not clear to me what
  // it should be so for now just copy the style from react-bootstrap.
  if (props.bsStyle === "warning") {
    // antd has no analogue of "warning", it's not clear to me what
    // it should be so for
    // now just copy the style.
    style = {
      backgroundColor: "#f0ad4e",
      borderColor: "#eea236",
      color: "#ffffff",
    };
  } else if (props.bsStyle === "success") {
    style = {
      backgroundColor: "#5cb85c",
      borderColor: "#4cae4c",
      color: "#ffffff",
    };
  } else if (props.bsStyle == "info") {
    style = {
      backgroundColor: "rgb(91, 192, 222)",
      borderColor: "rgb(70, 184, 218)",
      color: "#ffffff",
    };
  }
  if (props.disabled && style != null) {
    style.opacity = 0.65;
  }

  style = { ...style, ...props.style };
  let danger: boolean | undefined = undefined;
  let loading: boolean | undefined = undefined; // nothing mapped to this yet
  let ghost: boolean | undefined = undefined; // nothing mapped to this yet
  if (type == "danger") {
    type = "default";
    danger = true;
  }
  return { type, style, danger, ghost, loading };
}

export const Button = (props: {
  bsStyle?: ButtonStyle;
  bsSize?: ButtonSize;
  style?: React.CSSProperties;
  disabled?: boolean;
  onClick?: (e?: any) => void;
  key?: string;
  children?: any;
  className?: string;
  href?: string;
  target?: string;
  title?: string;
  tabIndex?: number;
  active?: boolean;
  id?: string;
  autoFocus?: boolean;
}) => {
  // The span is needed inside below, otherwise icons and labels get squashed together
  // due to button having word-spacing 0.
  const { type, style, danger, ghost, loading } = parse_bsStyle(props);
  let size: "middle" | "large" | "small" | undefined = undefined;
  if (props.bsSize == "large") {
    size = "large";
  } else if (props.bsSize == "small") {
    size = "middle";
  } else if (props.bsSize == "xsmall") {
    size = "small";
  }
  if (props.active) {
    style.backgroundColor = "#d4d4d4";
    style.boxShadow = "inset 0 3px 5px rgb(0 0 0 / 13%)";
  }
  const btn = (
    <AntdButton
      onClick={props.onClick}
      type={type}
      disabled={props.disabled}
      style={style}
      size={size}
      className={props.className}
      href={props.href}
      target={props.target}
      danger={danger}
      ghost={ghost}
      loading={loading}
      tabIndex={props.tabIndex}
      id={props.id}
      autoFocus={props.autoFocus}
    >
      <>{props.children}</>
    </AntdButton>
  );
  if (props.title) {
    return <Tooltip title={props.title}>{btn}</Tooltip>;
  } else {
    return btn;
  }
};

export function ButtonGroup(props: {
  style?: React.CSSProperties;
  children?: any;
  className?: string;
}) {
  return (
    <AntdButton.Group className={props.className} style={props.style}>
      {props.children}
    </AntdButton.Group>
  );
}

export function ButtonToolbar(props: {
  style?: React.CSSProperties;
  children?: any;
  className?: string;
}) {
  return (
    <div className={props.className} style={props.style}>
      {r_join(props.children, <Space />)}
    </div>
  );
}<commit_msg>antd-bootstrap button tooltip was too aggressive and annoying<commit_after>/*
 *  This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.
 *  License: AGPLv3 s.t. "Commons Clause" – see LICENSE.md for details
 */

/*
We use so little of react-bootstrap in CoCalc that for a first quick round
of switching to antd, I'm going to see if it isn't easy to re-implement
much of the same functionality on top of antd

Obviously, this is meant to be temporary, since it is far better if our
code consistently uses the antd api explicitly.  However, there are
some serious problems / bug /issues with using our stupid old react-bootstrap
*at all*, hence this.
*/

// TODO: What we haven't converted yet, but do use in CoCalc:
export {
  FormControl,
  FormGroup,
  Form,
  InputGroup,
  Navbar,
  Nav,
  NavItem,
  Table,
} from "react-bootstrap";

import { MouseEventHandler } from "react";
import { Rendered } from "./app-framework";
import { r_join } from "./components/r_join";
import { Space } from "./components/space";

import {
  Alert as AntdAlert,
  Button as AntdButton,
  Card as AntdCard,
  Tabs as AntdTabs,
  Modal as AntdModal,
  Checkbox as AntdCheckbox,
  Row as AntdRow,
  Col as AntdCol,
  Tooltip,
} from "antd";

// Note regarding buttons -- there are 6 semantics meanings in bootstrap, but
// only four in antd, and it we can't automatically collapse them down in a meaningful
// way without fundamentally removing information and breaking our UI (e.g., buttons
// change look after an assignment is sent successfully in a course).
export type ButtonStyle =
  | "primary"
  | "success"
  | "default"
  | "info"
  | "warning"
  | "danger"
  | "link";

const BS_STYLE_TO_TYPE: {
  [name in ButtonStyle]: "primary" | "default" | "dashed" | "danger" | "link";
} = {
  primary: "primary",
  success: "default", // antd doesn't have this so we do it via style below.
  default: "default",
  info: "default", // antd doesn't have this so we do it via style below.
  warning: "default", // antd doesn't have this so we do it via style below.
  danger: "danger",
  link: "link",
};

export type ButtonSize = "large" | "small" | "xsmall";

function parse_bsStyle(props: {
  bsStyle?: ButtonStyle;
  style?: React.CSSProperties;
  disabled?: boolean;
}): {
  type: "primary" | "default" | "dashed" | "link";
  style: React.CSSProperties;
  danger?: boolean;
  ghost?: boolean;
  disabled?: boolean;
  loading?: boolean;
} {
  let type =
    props.bsStyle == null
      ? "default"
      : BS_STYLE_TO_TYPE[props.bsStyle] ?? "default";

  let style: React.CSSProperties | undefined = undefined;
  // antd has no analogue of "success" & "warning", it's not clear to me what
  // it should be so for now just copy the style from react-bootstrap.
  if (props.bsStyle === "warning") {
    // antd has no analogue of "warning", it's not clear to me what
    // it should be so for
    // now just copy the style.
    style = {
      backgroundColor: "#f0ad4e",
      borderColor: "#eea236",
      color: "#ffffff",
    };
  } else if (props.bsStyle === "success") {
    style = {
      backgroundColor: "#5cb85c",
      borderColor: "#4cae4c",
      color: "#ffffff",
    };
  } else if (props.bsStyle == "info") {
    style = {
      backgroundColor: "rgb(91, 192, 222)",
      borderColor: "rgb(70, 184, 218)",
      color: "#ffffff",
    };
  }
  if (props.disabled && style != null) {
    style.opacity = 0.65;
  }

  style = { ...style, ...props.style };
  let danger: boolean | undefined = undefined;
  let loading: boolean | undefined = undefined; // nothing mapped to this yet
  let ghost: boolean | undefined = undefined; // nothing mapped to this yet
  if (type == "danger") {
    type = "default";
    danger = true;
  }
  return { type, style, danger, ghost, loading };
}

export const Button = (props: {
  bsStyle?: ButtonStyle;
  bsSize?: ButtonSize;
  style?: React.CSSProperties;
  disabled?: boolean;
  onClick?: (e?: any) => void;
  key?: string;
  children?: any;
  className?: string;
  href?: string;
  target?: string;
  title?: string;
  tabIndex?: number;
  active?: boolean;
  id?: string;
  autoFocus?: boolean;
}) => {
  // The span is needed inside below, otherwise icons and labels get squashed together
  // due to button having word-spacing 0.
  const { type, style, danger, ghost, loading } = parse_bsStyle(props);
  let size: "middle" | "large" | "small" | undefined = undefined;
  if (props.bsSize == "large") {
    size = "large";
  } else if (props.bsSize == "small") {
    size = "middle";
  } else if (props.bsSize == "xsmall") {
    size = "small";
  }
  if (props.active) {
    style.backgroundColor = "#d4d4d4";
    style.boxShadow = "inset 0 3px 5px rgb(0 0 0 / 13%)";
  }
  const btn = (
    <AntdButton
      onClick={props.onClick}
      type={type}
      disabled={props.disabled}
      style={style}
      size={size}
      className={props.className}
      href={props.href}
      target={props.target}
      danger={danger}
      ghost={ghost}
      loading={loading}
      tabIndex={props.tabIndex}
      id={props.id}
      autoFocus={props.autoFocus}
    >
      <>{props.children}</>
    </AntdButton>
  );
  if (props.title) {
    return (
      <Tooltip title={props.title} mouseEnterDelay={0.7} placement="bottom">
        {btn}
      </Tooltip>
    );
  } else {
    return btn;
  }
};

export function ButtonGroup(props: {
  style?: React.CSSProperties;
  children?: any;
  className?: string;
}) {
  return (
    <AntdButton.Group className={props.className} style={props.style}>
      {props.children}
    </AntdButton.Group>
  );
}

export function ButtonToolbar(props: {
  style?: React.CSSProperties;
  children?: any;
  className?: string;
}) {
  return (
    <div className={props.className} style={props.style}>
      {r_join(props.children, <Space />)}
    </div>
  );
}
{'fraction_non_alphanumeric': 0.13670185153140682, 'fraction_numerical': 0.00830593528292092, 'mean_word_length': 3.109136153572698, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 29, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2126480', 'n_tokens_mistral': 3799, 'n_tokens_neox': 3665, 'n_words': 1225}