diff --git a/app/components/InputColor.tsx b/app/components/InputColor.tsx index 786c08aff4..1444d777e7 100644 --- a/app/components/InputColor.tsx +++ b/app/components/InputColor.tsx @@ -1,85 +1,40 @@ import * as React from "react"; -import { useTranslation } from "react-i18next"; import styled from "styled-components"; -import { s } from "@shared/styles"; -import lazyWithRetry from "~/utils/lazyWithRetry"; -import DelayedMount from "./DelayedMount"; import Input, { Props as InputProps } from "./Input"; -import NudeButton from "./NudeButton"; import Relative from "./Sidebar/components/Relative"; -import Text from "./Text"; -import { Popover, PopoverContent, PopoverTrigger } from "./primitives/Popover"; +import { SwatchButton } from "./SwatchButton"; +/** + * Props for the InputColor component. + */ type Props = Omit & { + /** The current color value in hex format */ value: string | undefined; + /** Callback function invoked when the color value changes */ onChange: (value: string) => void; }; -const InputColor: React.FC = ({ value, onChange, ...rest }: Props) => { - const { t } = useTranslation(); +/** + * A color input component that combines a text input with a color picker swatch button. + * Automatically formats hex color values with a leading # character. + */ +const InputColor: React.FC = ({ value, onChange, ...rest }: Props) => ( + + onChange(event.target.value.replace(/^#?/, "#"))} + placeholder="#" + maxLength={7} + {...rest} + /> + + +); - return ( - - onChange(event.target.value.replace(/^#?/, "#"))} - placeholder="#" - maxLength={7} - {...rest} - /> - - - - - - - {t("Loading")}… - - } - > - onChange(color.hex)} - /> - - - - - ); -}; - -const SwatchButton = styled(NudeButton)<{ $background: string | undefined }>` - background: ${(props) => props.$background}; - border: 1px solid ${s("inputBorder")}; - border-radius: 50%; +const PositionedSwatchButton = styled(SwatchButton)` position: absolute; bottom: 20px; right: 6px; `; -const StyledContent = styled(PopoverContent)` - width: auto; - padding: 8px; -`; - -const ColorPicker = lazyWithRetry( - () => import("react-color/lib/components/chrome/Chrome") -); - -const StyledColorPicker = styled(ColorPicker)` - background: inherit !important; - box-shadow: none !important; - border: 0 !important; - border-radius: 0 !important; - user-select: none; - - input { - user-select: text; - color: ${s("text")} !important; - } -`; - export default InputColor; diff --git a/app/components/SwatchButton.tsx b/app/components/SwatchButton.tsx new file mode 100644 index 0000000000..835180fcda --- /dev/null +++ b/app/components/SwatchButton.tsx @@ -0,0 +1,92 @@ +import * as React from "react"; +import { useTranslation } from "react-i18next"; +import styled from "styled-components"; +import { s } from "@shared/styles"; +import lazyWithRetry from "~/utils/lazyWithRetry"; +import DelayedMount from "./DelayedMount"; +import NudeButton from "./NudeButton"; +import { Popover, PopoverTrigger, PopoverContent } from "./primitives/Popover"; +import Text from "./Text"; + +/** + * Props for the SwatchButton component. + */ +type SwatchButtonProps = { + /** The current color value in hex format */ + color?: string; + /** Callback function invoked when the color is changed */ + onChange: (color: string) => void; + /** Additional CSS class name to apply to the button */ + className?: string; + /** Whether to render the color picker in a modal popover. Defaults to true */ + pickerInModal?: boolean; +}; + +export const SwatchButton: React.FC = ({ + color, + onChange, + className, + pickerInModal = true, +}) => { + const { t } = useTranslation(); + + return ( + + + + + + + {t("Loading")}… + + } + > + onChange(c.hex)} + /> + + + + ); +}; + +const StyledSwatchButton = styled(NudeButton)` + background: ${s("menuBackground")}; + border: 1px solid ${s("inputBorder")}; + border-radius: 50%; +`; + +const StyledContent = styled(PopoverContent)` + width: auto; + padding: 8px; +`; + +const ColorPicker = lazyWithRetry( + () => import("react-color/lib/components/chrome/Chrome") +); + +const StyledColorPicker = styled(ColorPicker)` + background: inherit !important; + box-shadow: none !important; + border: 0 !important; + border-radius: 0 !important; + user-select: none; + + input { + user-select: text; + color: ${s("text")} !important; + } +`; diff --git a/shared/i18n/locales/en_US/translation.json b/shared/i18n/locales/en_US/translation.json index 5cb71d6d2c..daa801a50e 100644 --- a/shared/i18n/locales/en_US/translation.json +++ b/shared/i18n/locales/en_US/translation.json @@ -314,8 +314,6 @@ "Objects": "Objects", "Symbols": "Symbols", "Flags": "Flags", - "Select a color": "Select a color", - "Loading": "Loading", "View only": "View only", "Can edit": "Can edit", "No access": "No access", @@ -450,6 +448,8 @@ "Installation": "Installation", "Unstar document": "Unstar document", "Star document": "Star document", + "Select a color": "Select a color", + "Loading": "Loading", "Template created, go ahead and customize it": "Template created, go ahead and customize it", "Creating a template from {{titleWithDefault}} is a non-destructive action – we'll make a copy of the document and turn it into a template that can be used as a starting point for new documents.": "Creating a template from {{titleWithDefault}} is a non-destructive action – we'll make a copy of the document and turn it into a template that can be used as a starting point for new documents.", "Enable other members to use the template immediately": "Enable other members to use the template immediately",