diff --git a/src/App/Views/Universal/BasicInputDialog.tsx b/src/App/Views/Universal/BasicInputDialog.tsx deleted file mode 100644 index a1a08da0..00000000 --- a/src/App/Views/Universal/BasicInputDialog.tsx +++ /dev/null @@ -1,106 +0,0 @@ -import * as React from "react" -import { notNullStr } from "../../../Data/List" -import { bindF, ensure, isJust, isNothing, Maybe, normalize, Nothing } from "../../../Data/Maybe" -import { InputKeyEvent } from "../../Models/Hero/heroTypeHelpers" -import { addKeybinding, removeKeybinding } from "../../Utilities/Keybindings" -import { pipe_ } from "../../Utilities/pipe" -import { Dialog } from "../Universal/Dialog" -import { TextField } from "../Universal/TextField" - -interface Props { - id: string - isOpen: boolean - title: string - description: string - value: string | undefined - invalid?: Maybe - acceptLabel: string - rejectLabel: string - rejectDisabled?: boolean - onClose: () => void - onAccept: () => void - onReject?: () => void - onChange: (new_text: string) => void -} - -export const BasicInputDialog: React.FC = props => { - const { - id, - isOpen, - title, - description, - value, - invalid = Nothing, - acceptLabel, - rejectLabel, - rejectDisabled = false, - onClose, - onAccept, - onReject = () => undefined, - onChange, - } = props - - const input_empty = pipe_ (value, normalize, bindF (ensure (notNullStr)), isNothing) - - const accept_disabled = input_empty || isJust (invalid) - - const handleKeyUp = React.useCallback ( - (event: InputKeyEvent) => { - if (event.key === "Enter" && !accept_disabled) { - onAccept () - onClose () - } - else if (event.key === "Enter" && input_empty && !rejectDisabled) { - onReject () - onClose () - } - else if (event.key === "Escape") { - onClose () - } - }, - [ accept_disabled, input_empty, rejectDisabled, onAccept, onReject, onClose ] - ) - - React.useEffect (() => { - addKeybinding ("esc", () => { - onClose () - }) - - return () => { - removeKeybinding ("esc") - } - }) - - return ( - -

{description}

- -
- ) -} diff --git a/src/App/Views/Universal/BorderButton.scss b/src/App/Views/Universal/BorderButton.scss deleted file mode 100644 index 137e3232..00000000 --- a/src/App/Views/Universal/BorderButton.scss +++ /dev/null @@ -1,52 +0,0 @@ -.btn { - text-align: center; - padding: 6px 16px; - letter-spacing: 0.1em; - display: inline-block; - font: bold 13px/19px "Alegreya SC"; - text-transform: uppercase; - min-width: 140px; - text-decoration: none; - - &:hover, &:active { - cursor: pointer; - } - - &:hover { - border-color: var(--headings-color); - color: var(--headings-color); - } - - &:active { - border-color: var(--accent-color-intense); - color: var(--accent-color-intense); - } - - // &.btn-primary { - // border-image: linear-gradient(to bottom, #0abdb7 0%, #0abdb7 60%, #0c4550 100%) 1 1 1 1; - // color: #a3c7c7; - // background-color: transparentize(#101721, .5); - // min-width: 160px; - - // &:hover { - // border-image: linear-gradient(to bottom, #b4fbfb 0%, #4bb8d9 100%) 1 1 1 1; - // color: #caf8f7; - // text-shadow: 0 0 3px #4be3de; - // box-shadow: 0 0 4px transparentize(#4be3de, .4), 0 0 4px transparentize(#4be3de, .4) inset; - // background-image: linear-gradient(transparentize(#4bb8d9, 1) 0%, transparentize(#4bb8d9, .75) 100%); - // } - - // &:active { - // background-color: transparent; - // color: #0d404c; - // text-shadow: none; - // border: 2px solid #0c4550; - // box-shadow: 0 0 4px transparentize(#4be3de, .8), 0 0 4px transparentize(#4be3de, .8) inset; - // background-image: linear-gradient(transparentize(#4bb8d9, 1) 0%, transparentize(#4bb8d9, .875) 100%); - // } - // } - - &.fullWidth { - display: block; - } -} diff --git a/src/App/Views/Universal/BorderButton.tsx b/src/App/Views/Universal/BorderButton.tsx deleted file mode 100644 index 1e37fa11..00000000 --- a/src/App/Views/Universal/BorderButton.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import * as React from "react" -import { notNullStrUndef } from "../../../Data/List" -import { Maybe } from "../../../Data/Maybe" -import { Button } from "./Button" -import { Text } from "./Text" - -interface Props { - active?: boolean - autoWidth?: boolean - className?: string - disabled?: boolean | Maybe - flat?: boolean - fullWidth?: boolean - hint?: Maybe - label: string | undefined - primary?: boolean - onClick? (): void -} - -export const BorderButton: React.FC = props => { - const { - active, - autoWidth, - children, - className, - disabled, - flat, - fullWidth, - hint, - primary, - onClick, - label, - } = props - - return ( - - ) -} diff --git a/src/App/Views/Universal/Dialog.scss b/src/App/Views/Universal/Dialog.scss deleted file mode 100644 index 4a731df0..00000000 --- a/src/App/Views/Universal/Dialog.scss +++ /dev/null @@ -1,211 +0,0 @@ -.modal-backdrop { - --modal-padding: 24px; - position: fixed; - top: 1px; - bottom: 1px; - left: 1px; - right: 1px; - display: flex; - background: var(--background-color-transparent06); - z-index: 10000; - justify-content: center; - align-items: center; - - .modal-container { - height: auto; - background: var(--background-color); - border: 1px solid var(--accent-color); - position: relative; - - &:before, &:after { - position: absolute; - display: block; - content: ""; - border: 1px solid var(--accent-color); - pointer-events: none; - } - - &:before { - top: -5px; - width: calc(100% - 8px); - height: calc(100% + 8px); - left: 3px; - } - - &:after { - left: -5px; - width: calc(100% + 8px); - height: calc(100% - 8px); - top: 3px; - } - - .modal-close { - width: 36px; - height: 36px; - display: block; - position: absolute; - border-radius: 50%; - background: var(--background-color); - border: 1px solid var(--accent-color); - right: -21px; - top: -21px; - z-index: 1100; - - &::before, &::after { - display: block; - position: absolute; - content: ""; - border: 1px solid var(--accent-color); - z-index: 1099; - } - - &::before { - height: 11px; - width: 22px; - top: 11px; - right: 16px; - } - - &::after { - height: 22px; - width: 11px; - top: 16px; - right: 11px; - } - - > div { - border: 2px solid var(--accent-color); - border-radius: 50%; - color: var(--accent-color); - margin: 2px; - font: 18px/26px Material; - text-align: center; - background: var(--background-color); - position: absolute; - z-index: 1100; - width: 30px; - } - - &:hover { - cursor: pointer; - - > div { - border-color: var(--headings-color); - color: var(--headings-color); - } - } - } - - .modal-header { - padding: var(--modal-padding) var(--modal-padding) calc(var(--modal-padding) * 5 / 8); - - .modal-header-inner { - font: bold 20px/24px Alegreya SC; - color: var(--headings-color); - letter-spacing: 0.1em; - text-transform: uppercase; - } - } - - .modal-close + .modal-content, .modal-content:nth-child(1) { - width: 298px; - - .modal-content-inner { - padding: 16px 0 47px; - font: bold 16px/24px Alegreya SC; - color: var(--headings-color); - letter-spacing: 0.1em; - text-transform: uppercase; - text-align: center; - } - } - - .modal-content { - position: relative; - height: calc(100% - 77px); - width: 378px; - - .modal-content-inner { - color: var(--text-color); - font: 500 11px/16px Alegreya Sans; - letter-spacing: 0.05em; - padding: 0 var(--modal-padding) 55px; - - p { - margin-top: 14px; - - span.link { - color: #015175; - display: block; - letter-spacing: 0.03em; - line-height: 22px; - - &:hover { - color: #3685a7; - cursor: pointer; - } - } - } - - > div { - margin-top: 14px; - } - - > div:first-child, > .checkbox + .checkbox { - margin-top: 0; - } - } - } - - .dialog-buttons { - display: flex; - justify-content: center; - position: absolute; - width: 100%; - height: 32px; - left: 0; - bottom: -1px; - - > .dialog-buttons-inner { - background: var(--background-color); - position: relative; - padding: 0 2px; - margin: 0 24px; - border-width: 0 1px; - border-style: solid; - border-color: var(--accent-color); - z-index: 10001; - - > div { - box-shadow: 0 none; - margin: 0 1px; - - &:first-child:before { - left: -1px; - } - - &:last-child:after { - right: -1px; - } - } - } - } - - &.more-buttons .dialog-buttons { - height: auto; - - > .dialog-buttons-inner { - display: flex; - flex-direction: column; - - > div:not(:first-child) { - margin-top: 1px; - } - - > div:not(:last-child) { - margin-bottom: 1px; - } - } - } - } -} diff --git a/src/App/Views/Universal/Dialog.tsx b/src/App/Views/Universal/Dialog.tsx deleted file mode 100644 index 8822ead0..00000000 --- a/src/App/Views/Universal/Dialog.tsx +++ /dev/null @@ -1,159 +0,0 @@ -import * as React from "react" -import * as ReactDOM from "react-dom" -import { connect } from "react-redux" -import { List, notNullStrUndef } from "../../../Data/List" -import { guardReplace, Just, Maybe } from "../../../Data/Maybe" -import { abs, max } from "../../../Data/Num" -import { AppStateRecord } from "../../Models/AppState" -import { Theme } from "../../Models/Config" -import { getTheme } from "../../Selectors/uisettingsSelectors" -import { classListMaybe } from "../../Utilities/CSS" -import { DialogButtonProps } from "./DialogButton" -import { DialogButtons } from "./DialogButtons" - -const modals_root = document.querySelector ("#modals-root") - -export interface DialogOwnProps { - isOpen: boolean - buttons?: DialogButtonProps[] - className?: string - id?: string - noCloseButton?: boolean - title?: string - close (canceled: boolean): void - onClose? (): void -} - -export interface DialogDispatchProps { } - -export interface DialogStateProps { - theme: Theme -} - -type Props = DialogOwnProps & DialogDispatchProps & DialogStateProps - -export const DialogComp: React.FC = props => { - const { - buttons = [], - className, - close, - noCloseButton, - title, - onClose, - children, - isOpen, - id, - theme, - } = props - - const element = React.useMemo (() => document.createElement ("div"), []) - - React.useEffect ( - () => { - if (modals_root !== null) { - modals_root.appendChild (element) - - return () => { - modals_root.removeChild (element) - } - } - - return undefined - }, - [ element ] - ) - - const handleButtonClick = React.useCallback ( - (f: () => void) => { - if (typeof f === "function") { - f () - } - - if (typeof onClose === "function") { - onClose () - } - - close (false) - }, - [ close, onClose ] - ) - - const contentStyle: React.CSSProperties = buttons.length === 0 ? { paddingBottom: 26 } : {} - const height_diff_base = 77 - const height_diff_add = 33 - const padding_base = 55 - const button_count = buttons .length - const more_button_space = max (0) (button_count - 1) * height_diff_add - const height_diff = button_count > 2 ? height_diff_base - more_button_space : height_diff_base - const height_diff_abs = abs (height_diff) - const height_diff_sign = height_diff < 0 ? "+" : "-" - contentStyle.paddingBottom = button_count > 2 ? padding_base + more_button_space : padding_base - - const handleCloseClick = React.useCallback ( - () => close (true), - [ close ] - ) - - return isOpen - ? ReactDOM.createPortal ( -
-
2) ("more-buttons") - )) - } - > - {noCloseButton === true - ? null - :
{"\uE5CD"}
} - {notNullStrUndef (title) - ? ( -
-
{title}
-
- ) - : null} -
-
- {children} -
-
- {buttons.length > 0 - ? - : null} -
-
, - element - ) - : null -} - -const mapStateToProps = (state: AppStateRecord) => ({ - theme: getTheme (state), -}) - -const connectDialog = - connect< - DialogStateProps, - DialogDispatchProps, - React.PropsWithChildren, - AppStateRecord - > ( - mapStateToProps - ) - -export const Dialog = connectDialog (DialogComp) diff --git a/src/App/Views/Universal/Label.tsx b/src/App/Views/Universal/Label.tsx deleted file mode 100644 index a772ce59..00000000 --- a/src/App/Views/Universal/Label.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import * as React from "react" -import { List } from "../../../Data/List" -import { guardReplace, Maybe, orN } from "../../../Data/Maybe" -import { classListMaybe } from "../../Utilities/CSS" - -interface Props { - className?: string - disabled?: boolean - text?: string -} - -export const Label: React.FC = props => { - const { className, disabled, text } = props - - return ( - - ) -} diff --git a/src/App/Views/Universal/NumberBox.tsx b/src/App/Views/Universal/NumberBox.tsx deleted file mode 100644 index 7f9ca907..00000000 --- a/src/App/Views/Universal/NumberBox.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import * as React from "react" -import { Maybe, maybe, normalize } from "../../../Data/Maybe" - -interface Props { - current?: Maybe | number - max?: Maybe | number -} - -export const NumberBox: React.FC = ({ current, max }) => { - const mcurrent = normalize (current) - const mmax = normalize (max) - - return ( -
- {maybe (<>) - ((x: number) => ({x})) - (mcurrent)} - {maybe (<>) - ((x: number) => ({x})) - (mmax)} -
- ) -} diff --git a/src/App/Views/Universal/Overlay.tsx b/src/App/Views/Universal/Overlay.tsx deleted file mode 100644 index ceced4c7..00000000 --- a/src/App/Views/Universal/Overlay.tsx +++ /dev/null @@ -1,276 +0,0 @@ -/** - * The main axis is the axis that crosses both trigger and overlay: - * - * ``` - * | - * | - * | - * [overlay] - * | - * [trigger] - * | - * | - * | - * ``` - * - * The cross axis is orthogonal to the main axis. - */ - -import * as React from "react" -import { ident } from "../../../Data/Function" -import { over } from "../../../Data/Lens" -import { List } from "../../../Data/List" -import { Just, Maybe } from "../../../Data/Maybe" -import { fromDefault, makeLenses, Record, toObject } from "../../../Data/Record" -import { classListMaybe } from "../../Utilities/CSS" - -type Position = "top" | "bottom" | "left" | "right" - -// Minimum offset from window border in pixel -const MIN_WINDOW_OFFSET = 10 - -interface OverlayPosition { - "@@name": "OverlayPosition" - top: number - left: number -} - -// eslint-disable-next-line @typescript-eslint/no-redeclare -const OverlayPosition = fromDefault ("OverlayPosition") ({ top: 0, left: 0 }) - -const OPA = OverlayPosition.A -const OPL = makeLenses (OverlayPosition) - -const getTopGapMainAxis: (triggerCoord: ClientRect | DOMRect) => - (overlayCoord: ClientRect | DOMRect) => - (margin: number) => number = - trg => ovl => m => trg .top - m - ovl .height - MIN_WINDOW_OFFSET - -const getBottomGapMainAxis: (triggerCoord: ClientRect | DOMRect) => - (overlayCoord: ClientRect | DOMRect) => - (margin: number) => number = - trg => ovl => m => window .innerHeight - - (trg .top + trg .height + m + ovl .height + MIN_WINDOW_OFFSET) - -const getLeftGapMainAxis: (triggerCoord: ClientRect | DOMRect) => - (overlayCoord: ClientRect | DOMRect) => - (margin: number) => number = - trg => ovl => m => trg .left - m - ovl .width - MIN_WINDOW_OFFSET - -const getRightGapMainAxis: (triggerCoord: ClientRect | DOMRect) => - (overlayCoord: ClientRect | DOMRect) => - (margin: number) => number = - trg => ovl => m => window .innerWidth - - (trg .left + trg .width + m + ovl .width + MIN_WINDOW_OFFSET) - -const getPositionIfNotEnoughFreeSpace: (triggerCoord: ClientRect | DOMRect) => - (overlayCoord: ClientRect | DOMRect) => - (margin: number) => - (defPosition: Position) => Position = - trg => ovl => m => pos => { - switch (pos) { - case "top": - return getTopGapMainAxis (trg) (ovl) (m) < 0 && getBottomGapMainAxis (trg) (ovl) (m) >= 0 - ? "bottom" - : "top" - - case "bottom": - return getBottomGapMainAxis (trg) (ovl) (m) < 0 && getTopGapMainAxis (trg) (ovl) (m) >= 0 - ? "top" - : "bottom" - - case "left": - return getLeftGapMainAxis (trg) (ovl) (m) < 0 && getRightGapMainAxis (trg) (ovl) (m) >= 0 - ? "right" - : "left" - - case "right": - return getRightGapMainAxis (trg) (ovl) (m) < 0 && getLeftGapMainAxis (trg) (ovl) (m) >= 0 - ? "left" - : "right" - - default: - return pos - } - } - -const getCenteredOverlayPosition: (triggerCoord: ClientRect | DOMRect) => - (overlayCoord: ClientRect | DOMRect) => - (margin: number) => - (position: Position) => Record = - trg => ovl => m => pos => { - switch (pos) { - case "top": - return OverlayPosition ({ - left: trg .left + trg .width / 2 - ovl .width / 2, - top: trg .top - m - ovl .height, - }) - - case "bottom": - return OverlayPosition ({ - left: trg .left + trg .width / 2 - ovl .width / 2, - top: trg .top + trg .height + m, - }) - - case "left": - return OverlayPosition ({ - left: trg .left - m - ovl .width, - top: trg .top + trg .height / 2 - ovl .height / 2, - }) - - case "right": - return OverlayPosition ({ - left: trg .left + trg .width + m, - top: trg .top + trg .height / 2 - ovl .height / 2, - }) - - default: - return OverlayPosition.default - } - } - -const adjustCrossAxis: (overlayCoord: ClientRect | DOMRect) => - (position: Position) => - (centeredCoord: Record) => Record = - ovl => pos => { - switch (pos) { - case "top": - case "bottom": - return over (OPL.left) - (left => left < MIN_WINDOW_OFFSET - ? MIN_WINDOW_OFFSET - : left + ovl .width > window .innerWidth - MIN_WINDOW_OFFSET - ? window .innerWidth - MIN_WINDOW_OFFSET - ovl .width - : left) - - case "left": - case "right": - return over (OPL.top) - (top => top < MIN_WINDOW_OFFSET - ? MIN_WINDOW_OFFSET - : top + ovl .height > window .innerHeight - MIN_WINDOW_OFFSET - ? window .innerHeight - MIN_WINDOW_OFFSET - ovl .height - : top) - - default: - return ident - } - } - -const getArrowPosition: (arrow_size: number) => - (overlay_coords: ClientRect | DOMRect) => - (position: Position) => - (centered_coords: Record) => - (adjusted_coords: Record) => Record = - arrow_size => ovl => pos => centered => adjusted => { - switch (pos) { - case "top": - return OverlayPosition ({ - left: ovl .width * 0.5 - arrow_size * 0.5 - 1 - OPA.left (adjusted) + OPA.left (centered), - top: ovl .height - (arrow_size * 0.5 + 1.5), - }) - - case "bottom": - return OverlayPosition ({ - left: ovl .width * 0.5 - arrow_size * 0.5 - 1 - OPA.left (adjusted) + OPA.left (centered), - top: -arrow_size * 0.5 - 0.5, - }) - - case "left": - return OverlayPosition ({ - left: ovl .width - (arrow_size * 0.5 + 1.5), - top: ovl .height * 0.5 - arrow_size * 0.5 - 1 - OPA.top (adjusted) + OPA.top (centered), - }) - - case "right": - return OverlayPosition ({ - left: -arrow_size * 0.5 - 0.5, - top: ovl .height * 0.5 - arrow_size * 0.5 - 1 - OPA.top (adjusted) + OPA.top (centered), - }) - - default: - return OverlayPosition.default - } - } - -const alignToElement: (arrow_size: number) => - (ref: React.RefObject) => - (trigger: Element) => - (defPosition: Position) => - (margin: number) => [Position, React.CSSProperties, React.CSSProperties] = - arrow_size => ref => trg => def_pos => m => { - if (ref .current !== null) { - const triggerCoord = trg .getBoundingClientRect () - const overlayCoord = ref .current .getBoundingClientRect () - - const pos = getPositionIfNotEnoughFreeSpace (triggerCoord) (overlayCoord) (m) (def_pos) - - const centered_coords = getCenteredOverlayPosition (triggerCoord) (overlayCoord) (m) (pos) - - const adjusted_coords = adjustCrossAxis (overlayCoord) (pos) (centered_coords) - - const arrow_position = getArrowPosition (arrow_size) - (overlayCoord) - (pos) - (centered_coords) - (adjusted_coords) - - return [ pos, toObject (adjusted_coords), toObject (arrow_position) ] - } - - return [ def_pos, {}, {} ] - } - -interface Props { - className?: string - margin?: number - position?: Position - small?: boolean - trigger: Element -} - -export const Overlay: React.FC = props => { - const { - children, - className, - margin = 0, - position: defPosition = "top", - small, - trigger, - } = props - - const [ position, setPosition ] = React.useState (defPosition) - const [ style, setStyle ] = React.useState ({ visibility: "hidden" }) - const [ arrowStyle, setArrowStyle ] = React.useState ({}) - const overlayRef = React.useRef (null) - - const arrow_size = small === true ? 6 : 12 - - React.useEffect ( - () => { - const [ new_pos, new_coords, new_arrow_coords ] = - alignToElement (arrow_size) (overlayRef) (trigger) (defPosition) (margin) - - setPosition (new_pos) - setArrowStyle ({ height: arrow_size, width: arrow_size, ...new_arrow_coords }) - setStyle (new_coords) - }, - [ setPosition, setStyle, defPosition, margin, trigger, setArrowStyle, arrow_size ] - ) - - return ( -
- {children} -
-
- ) -} diff --git a/src/App/Views/Universal/TextFieldContainer.tsx b/src/App/Views/Universal/TextFieldContainer.tsx deleted file mode 100644 index 3922072a..00000000 --- a/src/App/Views/Universal/TextFieldContainer.tsx +++ /dev/null @@ -1,60 +0,0 @@ -// import { TextareaAutosize } from 'react-textarea-autosize' -import * as React from "react" -import { Either, isEither, isLeft } from "../../../Data/Either" -import { List } from "../../../Data/List" -import { guardReplace, isJust, isMaybe, Just, Maybe, orN } from "../../../Data/Maybe" -import { classListMaybe } from "../../Utilities/CSS" -import { TextFieldCounter } from "./TextFieldCounter" -import { TextFieldError } from "./TextFieldError" -import { TextFieldHint } from "./TextFieldHint" -import { TextFieldLabel } from "./TextFieldLabel" - -interface Props { - className?: string - countCurrent?: number - countMax?: number - disabled?: boolean - error?: Maybe | Either - fullWidth?: boolean - hint?: Maybe | string - isFieldEmpty: boolean - label?: Maybe | string - valid?: boolean -} - -export const TextFieldContainer: React.FC = props => { - const { - children, - className, - countCurrent, - countMax, - disabled, - error, - fullWidth, - hint, - isFieldEmpty, - label, - valid, - } = props - - return ( -
- - {children} - - - -
- ) -} diff --git a/src/App/Views/Universal/TextFieldCounter.tsx b/src/App/Views/Universal/TextFieldCounter.tsx deleted file mode 100644 index 12b0685c..00000000 --- a/src/App/Views/Universal/TextFieldCounter.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import * as React from "react" -import { isNumber } from "../../Utilities/typeCheckUtils" - -interface Props { - current: number | undefined - max: number | undefined -} - -export const TextFieldCounter: React.FC = ({ current, max }) => - isNumber (max) - ? ( -
- {current} - {" / "} - {max} -
- ) - : null diff --git a/src/App/Views/Universal/TextFieldDeferred.tsx b/src/App/Views/Universal/TextFieldDeferred.tsx deleted file mode 100644 index f86b761a..00000000 --- a/src/App/Views/Universal/TextFieldDeferred.tsx +++ /dev/null @@ -1,78 +0,0 @@ -// import { TextareaAutosize } from 'react-textarea-autosize' -import * as React from "react" -import { orN } from "../../../Data/Maybe" -import { InputKeyEvent } from "../../Models/Hero/heroTypeHelpers" - -interface Props { - autoFocus?: boolean - disabled?: boolean - type: string - value: string - onChange (newText: string): void - onKeyDown? (event: InputKeyEvent): void - onKeyUp? (event: InputKeyEvent): void -} - -export const TextFieldDeferred: React.FC = props => { - const { - autoFocus, - disabled, - type, - value: defaultValue, - onChange, - onKeyDown, - onKeyUp, - } = props - - const inputRef = React.useRef (null) - - const [ value, setValue ] = React.useState (defaultValue) - const [ prevValue, setPrevValue ] = React.useState (defaultValue) - - if (prevValue !== defaultValue) { - setValue (defaultValue) - setPrevValue (defaultValue) - } - - React.useEffect ( - () => { - if (orN (autoFocus) && inputRef.current !== null) { - inputRef.current.focus () - } - }, - [ autoFocus ] - ) - - const handleChange = - React.useCallback ( - (e: React.ChangeEvent) => { - if (!orN (disabled)) { - setValue (e.target.value) - } - }, - [ disabled ] - ) - - const handleBlur = - React.useCallback ( - (e: React.FocusEvent) => { - if (!orN (disabled) && defaultValue !== value) { - onChange (e.target.value) - } - }, - [ defaultValue, disabled, onChange, value ] - ) - - return ( - - ) -} diff --git a/src/App/Views/Universal/TextFieldError.tsx b/src/App/Views/Universal/TextFieldError.tsx deleted file mode 100644 index 94d5f98d..00000000 --- a/src/App/Views/Universal/TextFieldError.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import * as React from "react" -import { Either, eitherToMaybe, invertEither, isEither } from "../../../Data/Either" -import { fmap } from "../../../Data/Functor" -import { notNullStr } from "../../../Data/List" -import { bindF, ensure, Maybe, maybeToNullable, normalize } from "../../../Data/Maybe" -import { pipe_ } from "../../Utilities/pipe" - -interface Props { - error: Maybe | Either | undefined -} - -export const TextFieldError: React.FC = - ({ error }) => pipe_ ( - error, - x => isEither (x) ? eitherToMaybe (invertEither (x)) : x, - normalize, - bindF (ensure (notNullStr)), - fmap ((msg: string) => ( -

- {msg} -

- )), - maybeToNullable - ) diff --git a/src/App/Views/Universal/TextFieldHint.tsx b/src/App/Views/Universal/TextFieldHint.tsx deleted file mode 100644 index 7b02058f..00000000 --- a/src/App/Views/Universal/TextFieldHint.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import * as React from "react" -import { fmap } from "../../../Data/Functor" -import { List } from "../../../Data/List" -import { guardReplace, Just, Maybe, maybeToNullable, normalize } from "../../../Data/Maybe" -import { classListMaybe } from "../../Utilities/CSS" -import { pipe_ } from "../../Utilities/pipe" - -interface Props { - hint: Maybe | string | undefined - isFieldEmpty: boolean -} - -export const TextFieldHint: React.FC = - ({ hint: mhint, isFieldEmpty }) => pipe_ ( - mhint, - normalize, - fmap ((hint: string) => ( -
- {hint} -
- )), - maybeToNullable - ) diff --git a/src/App/Views/Universal/TextFieldLabel.tsx b/src/App/Views/Universal/TextFieldLabel.tsx deleted file mode 100644 index 8cfc6fac..00000000 --- a/src/App/Views/Universal/TextFieldLabel.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import * as React from "react" -import { fmap } from "../../../Data/Functor" -import { notNullStr } from "../../../Data/List" -import { bindF, ensure, Maybe, maybeToNullable, normalize } from "../../../Data/Maybe" -import { pipe_ } from "../../Utilities/pipe" -import { Label } from "./Label" - -interface Props { - label: Maybe | string | undefined -} - -export const TextFieldLabel: React.FC = - ({ label }) => pipe_ ( - label, - normalize, - bindF (ensure (notNullStr)), - fmap ((l: string) =>