TextInput.tsx 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. /*
  2. Copyright (C) 2017 Cloudbase Solutions SRL
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU Affero General Public License as
  5. published by the Free Software Foundation, either version 3 of the
  6. License, or (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Affero General Public License for more details.
  11. You should have received a copy of the GNU Affero General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. */
  14. import * as React from "react";
  15. import styled, { css } from "styled-components";
  16. import { ThemePalette, ThemeProps } from "@src/components/Theme";
  17. import closeImage from "./images/close.svg";
  18. import requiredImage from "./images/required.svg";
  19. const Wrapper = styled.div<any>`
  20. position: relative;
  21. ${props =>
  22. props.disabledLoading ? ThemeProps.animations.disabledLoading : ""}
  23. `;
  24. const Required = styled.div<any>`
  25. position: absolute;
  26. width: 8px;
  27. height: 8px;
  28. right: ${props => props.right}px;
  29. top: 12px;
  30. background: url("${requiredImage}") center no-repeat;
  31. `;
  32. const getInputWidth = (props: any) => {
  33. if (props.width) {
  34. return typeof props.width === "number" ? `${props.width}px` : props.width;
  35. }
  36. if (props.large) {
  37. return `${ThemeProps.inputSizes.large.width}px`;
  38. }
  39. return `${ThemeProps.inputSizes.regular.width}px`;
  40. };
  41. const borderColor = (
  42. props: any,
  43. defaultColor: string | undefined | null = ThemePalette.grayscale[3]
  44. ) => (props.highlight ? ThemePalette.alert : defaultColor);
  45. const Input = styled.input<any>`
  46. width: ${props => getInputWidth(props)};
  47. height: ${props =>
  48. props.height || `${ThemeProps.inputSizes.regular.height}px`};
  49. line-height: ${props => props.lineHeight || "normal"};
  50. border-radius: ${ThemeProps.borderRadius};
  51. background-color: #fff;
  52. border: ${props =>
  53. props.embedded ? 0 : css`1px solid ${borderColor(props)}`};
  54. border-top-left-radius: ${props =>
  55. props.embedded ? 0 : ThemeProps.borderRadius};
  56. border-top-right-radius: ${ThemeProps.borderRadius};
  57. border-bottom-left-radius: ${props =>
  58. props.embedded ? 0 : ThemeProps.borderRadius};
  59. border-bottom-right-radius: ${ThemeProps.borderRadius};
  60. color: ${ThemePalette.black};
  61. padding: 0 8px 0 ${props => (props.embedded ? 0 : "16px")};
  62. font-size: inherit;
  63. transition: all ${ThemeProps.animations.swift};
  64. box-sizing: border-box;
  65. &:hover {
  66. border-color: ${props =>
  67. borderColor(props, props.disablePrimary ? null : ThemePalette.primary)};
  68. }
  69. &:focus {
  70. border-color: ${props =>
  71. borderColor(props, props.disablePrimary ? null : ThemePalette.primary)};
  72. outline: none;
  73. }
  74. &:disabled {
  75. color: ${props =>
  76. !props.embedded ? ThemePalette.grayscale[3] : "inherit"};
  77. border-color: ${props =>
  78. !props.embedded ? ThemePalette.grayscale[0] : "inherit"};
  79. background-color: ${props =>
  80. !props.embedded ? ThemePalette.grayscale[0] : "inherit"};
  81. }
  82. &::placeholder {
  83. color: ${ThemePalette.grayscale[3]};
  84. }
  85. `;
  86. export const Close = styled.div<any>`
  87. display: ${props => (props.show ? "block" : "none")};
  88. width: 16px;
  89. height: 16px;
  90. background: url("${closeImage}") center no-repeat;
  91. position: absolute;
  92. top: 8px;
  93. right: 8px;
  94. cursor: pointer;
  95. `;
  96. type Props = {
  97. _ref?: (ref: HTMLElement) => void;
  98. disabled?: boolean;
  99. highlight?: boolean;
  100. large?: boolean;
  101. onChange?: (
  102. e: React.ChangeEvent<HTMLInputElement> | { target: { value: string } }
  103. ) => void;
  104. placeholder?: string;
  105. type?: string;
  106. value?: string;
  107. showClose?: boolean;
  108. onCloseClick?: () => void;
  109. embedded?: boolean;
  110. width?: string | number;
  111. height?: string;
  112. style?: React.CSSProperties;
  113. lineHeight?: string;
  114. required?: boolean;
  115. disabledLoading?: boolean;
  116. onInputKeyDown?: (e: React.KeyboardEvent<HTMLInputElement>) => void;
  117. onFocus?: () => void;
  118. onBlur?: () => void;
  119. autoComplete?: string;
  120. };
  121. const TextInput = (props: Props) => {
  122. const {
  123. // eslint-disable-next-line @typescript-eslint/naming-convention
  124. _ref,
  125. value,
  126. onChange,
  127. showClose,
  128. onCloseClick,
  129. disabled,
  130. disabledLoading,
  131. onInputKeyDown,
  132. ...otherProps
  133. } = props;
  134. const actualDisabled = disabled || disabledLoading;
  135. let input: { focus: () => void };
  136. return (
  137. <Wrapper disabledLoading={disabledLoading}>
  138. <Input
  139. ref={(ref: HTMLElement) => {
  140. input = ref;
  141. if (_ref) _ref(ref);
  142. }}
  143. type="text"
  144. value={value}
  145. onChange={onChange}
  146. // eslint-disable-next-line react/jsx-props-no-spreading
  147. {...otherProps}
  148. onKeyDown={onInputKeyDown}
  149. disabled={actualDisabled}
  150. />
  151. {props.required ? <Required right={props.embedded ? -24 : -16} /> : null}
  152. <Close
  153. show={showClose && value !== "" && value !== undefined}
  154. onClick={() => {
  155. input.focus();
  156. if (onChange) onChange({ target: { value: "" } });
  157. if (onCloseClick) onCloseClick();
  158. }}
  159. />
  160. </Wrapper>
  161. );
  162. };
  163. export default TextInput;