Brand color without correct hex value compatibility (#1737)
* enabled fallback support and og-brand-color fix * --added react-colorful package * added colorpicker component and added it to settings/profile * lint fix * typo fix in server/viewer * clean-up * improved colorpicker component * added swatch component and integrated with brand color picker * improved ux for color picker swatch * added colorname integration Co-authored-by: Peer Richelsen <peeroke@gmail.com> Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
This commit is contained in:
parent
447def5849
commit
522875da74
7 changed files with 310 additions and 17 deletions
|
@ -1,5 +1,158 @@
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
|
|
||||||
|
const brandColor = "#292929";
|
||||||
|
const brandTextColor = "#ffffff";
|
||||||
|
|
||||||
|
export function colorNameToHex(color: string) {
|
||||||
|
const colors = {
|
||||||
|
aliceblue: "#f0f8ff",
|
||||||
|
antiquewhite: "#faebd7",
|
||||||
|
aqua: "#00ffff",
|
||||||
|
aquamarine: "#7fffd4",
|
||||||
|
azure: "#f0ffff",
|
||||||
|
beige: "#f5f5dc",
|
||||||
|
bisque: "#ffe4c4",
|
||||||
|
black: "#000000",
|
||||||
|
blanchedalmond: "#ffebcd",
|
||||||
|
blue: "#0000ff",
|
||||||
|
blueviolet: "#8a2be2",
|
||||||
|
brown: "#a52a2a",
|
||||||
|
burlywood: "#deb887",
|
||||||
|
cadetblue: "#5f9ea0",
|
||||||
|
chartreuse: "#7fff00",
|
||||||
|
chocolate: "#d2691e",
|
||||||
|
coral: "#ff7f50",
|
||||||
|
cornflowerblue: "#6495ed",
|
||||||
|
cornsilk: "#fff8dc",
|
||||||
|
crimson: "#dc143c",
|
||||||
|
cyan: "#00ffff",
|
||||||
|
darkblue: "#00008b",
|
||||||
|
darkcyan: "#008b8b",
|
||||||
|
darkgoldenrod: "#b8860b",
|
||||||
|
darkgray: "#a9a9a9",
|
||||||
|
darkgreen: "#006400",
|
||||||
|
darkkhaki: "#bdb76b",
|
||||||
|
darkmagenta: "#8b008b",
|
||||||
|
darkolivegreen: "#556b2f",
|
||||||
|
darkorange: "#ff8c00",
|
||||||
|
darkorchid: "#9932cc",
|
||||||
|
darkred: "#8b0000",
|
||||||
|
darksalmon: "#e9967a",
|
||||||
|
darkseagreen: "#8fbc8f",
|
||||||
|
darkslateblue: "#483d8b",
|
||||||
|
darkslategray: "#2f4f4f",
|
||||||
|
darkturquoise: "#00ced1",
|
||||||
|
darkviolet: "#9400d3",
|
||||||
|
deeppink: "#ff1493",
|
||||||
|
deepskyblue: "#00bfff",
|
||||||
|
dimgray: "#696969",
|
||||||
|
dodgerblue: "#1e90ff",
|
||||||
|
firebrick: "#b22222",
|
||||||
|
floralwhite: "#fffaf0",
|
||||||
|
forestgreen: "#228b22",
|
||||||
|
fuchsia: "#ff00ff",
|
||||||
|
gainsboro: "#dcdcdc",
|
||||||
|
ghostwhite: "#f8f8ff",
|
||||||
|
gold: "#ffd700",
|
||||||
|
goldenrod: "#daa520",
|
||||||
|
gray: "#808080",
|
||||||
|
green: "#008000",
|
||||||
|
greenyellow: "#adff2f",
|
||||||
|
honeydew: "#f0fff0",
|
||||||
|
hotpink: "#ff69b4",
|
||||||
|
"indianred ": "#cd5c5c",
|
||||||
|
indigo: "#4b0082",
|
||||||
|
ivory: "#fffff0",
|
||||||
|
khaki: "#f0e68c",
|
||||||
|
lavender: "#e6e6fa",
|
||||||
|
lavenderblush: "#fff0f5",
|
||||||
|
lawngreen: "#7cfc00",
|
||||||
|
lemonchiffon: "#fffacd",
|
||||||
|
lightblue: "#add8e6",
|
||||||
|
lightcoral: "#f08080",
|
||||||
|
lightcyan: "#e0ffff",
|
||||||
|
lightgoldenrodyellow: "#fafad2",
|
||||||
|
lightgrey: "#d3d3d3",
|
||||||
|
lightgreen: "#90ee90",
|
||||||
|
lightpink: "#ffb6c1",
|
||||||
|
lightsalmon: "#ffa07a",
|
||||||
|
lightseagreen: "#20b2aa",
|
||||||
|
lightskyblue: "#87cefa",
|
||||||
|
lightslategray: "#778899",
|
||||||
|
lightsteelblue: "#b0c4de",
|
||||||
|
lightyellow: "#ffffe0",
|
||||||
|
lime: "#00ff00",
|
||||||
|
limegreen: "#32cd32",
|
||||||
|
linen: "#faf0e6",
|
||||||
|
magenta: "#ff00ff",
|
||||||
|
maroon: "#800000",
|
||||||
|
mediumaquamarine: "#66cdaa",
|
||||||
|
mediumblue: "#0000cd",
|
||||||
|
mediumorchid: "#ba55d3",
|
||||||
|
mediumpurple: "#9370d8",
|
||||||
|
mediumseagreen: "#3cb371",
|
||||||
|
mediumslateblue: "#7b68ee",
|
||||||
|
mediumspringgreen: "#00fa9a",
|
||||||
|
mediumturquoise: "#48d1cc",
|
||||||
|
mediumvioletred: "#c71585",
|
||||||
|
midnightblue: "#191970",
|
||||||
|
mintcream: "#f5fffa",
|
||||||
|
mistyrose: "#ffe4e1",
|
||||||
|
moccasin: "#ffe4b5",
|
||||||
|
navajowhite: "#ffdead",
|
||||||
|
navy: "#000080",
|
||||||
|
oldlace: "#fdf5e6",
|
||||||
|
olive: "#808000",
|
||||||
|
olivedrab: "#6b8e23",
|
||||||
|
orange: "#ffa500",
|
||||||
|
orangered: "#ff4500",
|
||||||
|
orchid: "#da70d6",
|
||||||
|
palegoldenrod: "#eee8aa",
|
||||||
|
palegreen: "#98fb98",
|
||||||
|
paleturquoise: "#afeeee",
|
||||||
|
palevioletred: "#d87093",
|
||||||
|
papayawhip: "#ffefd5",
|
||||||
|
peachpuff: "#ffdab9",
|
||||||
|
peru: "#cd853f",
|
||||||
|
pink: "#ffc0cb",
|
||||||
|
plum: "#dda0dd",
|
||||||
|
powderblue: "#b0e0e6",
|
||||||
|
purple: "#800080",
|
||||||
|
rebeccapurple: "#663399",
|
||||||
|
red: "#ff0000",
|
||||||
|
rosybrown: "#bc8f8f",
|
||||||
|
royalblue: "#4169e1",
|
||||||
|
saddlebrown: "#8b4513",
|
||||||
|
salmon: "#fa8072",
|
||||||
|
sandybrown: "#f4a460",
|
||||||
|
seagreen: "#2e8b57",
|
||||||
|
seashell: "#fff5ee",
|
||||||
|
sienna: "#a0522d",
|
||||||
|
silver: "#c0c0c0",
|
||||||
|
skyblue: "#87ceeb",
|
||||||
|
slateblue: "#6a5acd",
|
||||||
|
slategray: "#708090",
|
||||||
|
snow: "#fffafa",
|
||||||
|
springgreen: "#00ff7f",
|
||||||
|
steelblue: "#4682b4",
|
||||||
|
tan: "#d2b48c",
|
||||||
|
teal: "#008080",
|
||||||
|
thistle: "#d8bfd8",
|
||||||
|
tomato: "#ff6347",
|
||||||
|
turquoise: "#40e0d0",
|
||||||
|
violet: "#ee82ee",
|
||||||
|
wheat: "#f5deb3",
|
||||||
|
white: "#ffffff",
|
||||||
|
whitesmoke: "#f5f5f5",
|
||||||
|
yellow: "#ffff00",
|
||||||
|
yellowgreen: "#9acd32",
|
||||||
|
};
|
||||||
|
|
||||||
|
return colors[color.toLowerCase() as keyof typeof colors] !== undefined
|
||||||
|
? colors[color.toLowerCase() as keyof typeof colors]
|
||||||
|
: false;
|
||||||
|
}
|
||||||
|
|
||||||
function computeContrastRatio(a: number[], b: number[]) {
|
function computeContrastRatio(a: number[], b: number[]) {
|
||||||
const lum1 = computeLuminance(a[0], a[1], a[2]);
|
const lum1 = computeLuminance(a[0], a[1], a[2]);
|
||||||
const lum2 = computeLuminance(b[0], b[1], b[2]);
|
const lum2 = computeLuminance(b[0], b[1], b[2]);
|
||||||
|
@ -22,14 +175,30 @@ function hexToRGB(hex: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getContrastingTextColor(bgColor: string | null): string {
|
function getContrastingTextColor(bgColor: string | null): string {
|
||||||
bgColor = bgColor == "" || bgColor == null ? "#292929" : bgColor;
|
bgColor = bgColor == "" || bgColor == null ? brandColor : bgColor;
|
||||||
const rgb = hexToRGB(bgColor);
|
const rgb = hexToRGB(bgColor);
|
||||||
const whiteContrastRatio = computeContrastRatio(rgb, [255, 255, 255]);
|
const whiteContrastRatio = computeContrastRatio(rgb, [255, 255, 255]);
|
||||||
const blackContrastRatio = computeContrastRatio(rgb, [41, 41, 41]); //#292929
|
const blackContrastRatio = computeContrastRatio(rgb, [41, 41, 41]); //#292929
|
||||||
return whiteContrastRatio > blackContrastRatio ? "#ffffff" : "#292929";
|
return whiteContrastRatio > blackContrastRatio ? brandTextColor : brandColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
const BrandColor = ({ val = "#292929" }: { val: string | undefined | null }) => {
|
export function isValidHexCode(val: string | null) {
|
||||||
|
if (val) {
|
||||||
|
val = val.indexOf("#") === 0 ? val : "#" + val;
|
||||||
|
const regex = new RegExp("^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$");
|
||||||
|
return regex.test(val);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function fallBackHex(val: string | null): string {
|
||||||
|
if (val) if (colorNameToHex(val)) return colorNameToHex(val) as string;
|
||||||
|
return brandColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
const BrandColor = ({ val = brandColor }: { val: string | undefined | null }) => {
|
||||||
|
// ensure acceptable hex-code
|
||||||
|
val = isValidHexCode(val) ? (val?.indexOf("#") === 0 ? val : "#" + val) : fallBackHex(val);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.documentElement.style.setProperty("--brand-color", val);
|
document.documentElement.style.setProperty("--brand-color", val);
|
||||||
document.documentElement.style.setProperty("--brand-text-color", getContrastingTextColor(val));
|
document.documentElement.style.setProperty("--brand-text-color", getContrastingTextColor(val));
|
||||||
|
|
26
components/Swatch.tsx
Normal file
26
components/Swatch.tsx
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import classNames from "@lib/classNames";
|
||||||
|
|
||||||
|
export type SwatchProps = {
|
||||||
|
size?: "base" | "sm" | "lg";
|
||||||
|
backgroundColor: string;
|
||||||
|
onClick: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Swatch = (props: SwatchProps) => {
|
||||||
|
const { size, backgroundColor, onClick } = props;
|
||||||
|
return (
|
||||||
|
<div className="p-1 border-2 border-gray-200 shadow-sm">
|
||||||
|
<div
|
||||||
|
onClick={onClick}
|
||||||
|
style={{ backgroundColor }}
|
||||||
|
className={classNames(
|
||||||
|
"cursor-pointer",
|
||||||
|
size === "sm" && "w-6 h-6 rounded-sm",
|
||||||
|
size === "base" && "w-16 h-16 rounded-sm",
|
||||||
|
size === "lg" && "w-24 h-24 rounded-sm"
|
||||||
|
)}></div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Swatch;
|
100
components/ui/colorpicker.tsx
Normal file
100
components/ui/colorpicker.tsx
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
import { useCallback, useRef, useState } from "react";
|
||||||
|
import { useEffect } from "react";
|
||||||
|
import { HexColorInput, HexColorPicker } from "react-colorful";
|
||||||
|
|
||||||
|
import { isValidHexCode, fallBackHex } from "@components/CustomBranding";
|
||||||
|
import Swatch from "@components/Swatch";
|
||||||
|
|
||||||
|
type Handler = (event: MouseEvent | Event) => void;
|
||||||
|
function useEventListener<
|
||||||
|
KW extends keyof WindowEventMap,
|
||||||
|
KH extends keyof HTMLElementEventMap,
|
||||||
|
T extends HTMLElement | void = void
|
||||||
|
>(
|
||||||
|
eventName: KW | KH,
|
||||||
|
handler: (event: WindowEventMap[KW] | HTMLElementEventMap[KH] | Event) => void,
|
||||||
|
element?: React.RefObject<T>
|
||||||
|
) {
|
||||||
|
// Create a ref that stores handler
|
||||||
|
const savedHandler = useRef<typeof handler>();
|
||||||
|
useEffect(() => {
|
||||||
|
// Define the listening target
|
||||||
|
const targetElement: T | Window = element?.current || window;
|
||||||
|
if (!(targetElement && targetElement.addEventListener)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Update saved handler if necessary
|
||||||
|
if (savedHandler.current !== handler) {
|
||||||
|
savedHandler.current = handler;
|
||||||
|
}
|
||||||
|
// Create event listener that calls handler function stored in ref
|
||||||
|
const eventListener: typeof handler = (event) => {
|
||||||
|
// eslint-disable-next-line no-extra-boolean-cast
|
||||||
|
if (!!savedHandler?.current) {
|
||||||
|
savedHandler.current(event);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
targetElement.addEventListener(eventName, eventListener);
|
||||||
|
// Remove event listener on cleanup
|
||||||
|
return () => {
|
||||||
|
targetElement.removeEventListener(eventName, eventListener);
|
||||||
|
};
|
||||||
|
}, [eventName, element, handler]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function useOnClickOutside<T extends HTMLElement = HTMLElement>(
|
||||||
|
ref: React.RefObject<T>,
|
||||||
|
handler: Handler,
|
||||||
|
mouseEvent: "mousedown" | "mouseup" = "mousedown"
|
||||||
|
): void {
|
||||||
|
useEventListener(mouseEvent, (event) => {
|
||||||
|
const el = ref?.current;
|
||||||
|
// Do nothing if clicking ref's element or descendent elements
|
||||||
|
if (!el || el.contains(event.target as Node)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
handler(event);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
export type ColorPickerProps = {
|
||||||
|
defaultValue: string;
|
||||||
|
onChange: (text: string) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ColorPicker = (props: ColorPickerProps) => {
|
||||||
|
const init = !isValidHexCode(props.defaultValue) ? fallBackHex(props.defaultValue) : props.defaultValue;
|
||||||
|
const [color, setColor] = useState(init);
|
||||||
|
const [isOpen, toggle] = useState(false);
|
||||||
|
const popover = useRef() as React.MutableRefObject<HTMLInputElement>;
|
||||||
|
const close = useCallback(() => toggle(false), []);
|
||||||
|
useOnClickOutside(popover, close);
|
||||||
|
return (
|
||||||
|
<div className="relative flex items-center justify-center mt-1">
|
||||||
|
<Swatch size="sm" backgroundColor={color} onClick={() => toggle(!isOpen)} />
|
||||||
|
|
||||||
|
{isOpen && (
|
||||||
|
<div className="popover" ref={popover}>
|
||||||
|
<HexColorPicker
|
||||||
|
className="!w-32 !h-32 !absolute !top-10 !left-0 !z-10"
|
||||||
|
color={color}
|
||||||
|
onChange={(val) => {
|
||||||
|
setColor(val);
|
||||||
|
props.onChange(val);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<HexColorInput
|
||||||
|
className="block w-full px-3 py-2 ml-1 border border-gray-300 rounded-sm shadow-sm focus:outline-none focus:ring-neutral-800 focus:border-neutral-800 sm:text-sm"
|
||||||
|
color={color}
|
||||||
|
onChange={(val) => {
|
||||||
|
setColor(val);
|
||||||
|
props.onChange(val);
|
||||||
|
}}
|
||||||
|
type="text"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ColorPicker;
|
|
@ -85,6 +85,7 @@
|
||||||
"otplib": "^12.0.1",
|
"otplib": "^12.0.1",
|
||||||
"qrcode": "^1.5.0",
|
"qrcode": "^1.5.0",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
|
"react-colorful": "^5.5.1",
|
||||||
"react-date-picker": "^8.3.6",
|
"react-date-picker": "^8.3.6",
|
||||||
"react-digit-input": "^2.1.0",
|
"react-digit-input": "^2.1.0",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
|
|
|
@ -30,6 +30,7 @@ import { Alert } from "@components/ui/Alert";
|
||||||
import Avatar from "@components/ui/Avatar";
|
import Avatar from "@components/ui/Avatar";
|
||||||
import Badge from "@components/ui/Badge";
|
import Badge from "@components/ui/Badge";
|
||||||
import Button from "@components/ui/Button";
|
import Button from "@components/ui/Button";
|
||||||
|
import ColorPicker from "@components/ui/colorpicker";
|
||||||
|
|
||||||
type Props = inferSSRProps<typeof getServerSideProps>;
|
type Props = inferSSRProps<typeof getServerSideProps>;
|
||||||
|
|
||||||
|
@ -151,7 +152,6 @@ function SettingsView(props: ComponentProps<typeof Settings> & { localeProp: str
|
||||||
const emailRef = useRef<HTMLInputElement>(null!);
|
const emailRef = useRef<HTMLInputElement>(null!);
|
||||||
const descriptionRef = useRef<HTMLTextAreaElement>(null!);
|
const descriptionRef = useRef<HTMLTextAreaElement>(null!);
|
||||||
const avatarRef = useRef<HTMLInputElement>(null!);
|
const avatarRef = useRef<HTMLInputElement>(null!);
|
||||||
const brandColorRef = useRef<HTMLInputElement>(null!);
|
|
||||||
const hideBrandingRef = useRef<HTMLInputElement>(null!);
|
const hideBrandingRef = useRef<HTMLInputElement>(null!);
|
||||||
const [selectedTheme, setSelectedTheme] = useState<typeof themeOptions[number] | undefined>();
|
const [selectedTheme, setSelectedTheme] = useState<typeof themeOptions[number] | undefined>();
|
||||||
const [selectedTimeZone, setSelectedTimeZone] = useState<ITimezone>(props.user.timeZone);
|
const [selectedTimeZone, setSelectedTimeZone] = useState<ITimezone>(props.user.timeZone);
|
||||||
|
@ -167,6 +167,7 @@ function SettingsView(props: ComponentProps<typeof Settings> & { localeProp: str
|
||||||
const [imageSrc, setImageSrc] = useState<string>(props.user.avatar || "");
|
const [imageSrc, setImageSrc] = useState<string>(props.user.avatar || "");
|
||||||
const [hasErrors, setHasErrors] = useState(false);
|
const [hasErrors, setHasErrors] = useState(false);
|
||||||
const [errorMessage, setErrorMessage] = useState("");
|
const [errorMessage, setErrorMessage] = useState("");
|
||||||
|
const [brandColor, setBrandColor] = useState(props.user.brandColor);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!props.user.theme) return;
|
if (!props.user.theme) return;
|
||||||
|
@ -184,7 +185,7 @@ function SettingsView(props: ComponentProps<typeof Settings> & { localeProp: str
|
||||||
const enteredEmail = emailRef.current.value;
|
const enteredEmail = emailRef.current.value;
|
||||||
const enteredDescription = descriptionRef.current.value;
|
const enteredDescription = descriptionRef.current.value;
|
||||||
const enteredAvatar = avatarRef.current.value;
|
const enteredAvatar = avatarRef.current.value;
|
||||||
const enteredBrandColor = brandColorRef.current.value;
|
const enteredBrandColor = brandColor;
|
||||||
const enteredTimeZone = typeof selectedTimeZone === "string" ? selectedTimeZone : selectedTimeZone.value;
|
const enteredTimeZone = typeof selectedTimeZone === "string" ? selectedTimeZone : selectedTimeZone.value;
|
||||||
const enteredWeekStartDay = selectedWeekStartDay.value;
|
const enteredWeekStartDay = selectedWeekStartDay.value;
|
||||||
const enteredHideBranding = hideBrandingRef.current.checked;
|
const enteredHideBranding = hideBrandingRef.current.checked;
|
||||||
|
@ -402,17 +403,7 @@ function SettingsView(props: ComponentProps<typeof Settings> & { localeProp: str
|
||||||
<label htmlFor="brandColor" className="block text-sm font-medium text-gray-700">
|
<label htmlFor="brandColor" className="block text-sm font-medium text-gray-700">
|
||||||
{t("brand_color")}
|
{t("brand_color")}
|
||||||
</label>
|
</label>
|
||||||
<div className="flex mt-1">
|
<ColorPicker defaultValue={props.user.brandColor} onChange={setBrandColor} />
|
||||||
<input
|
|
||||||
ref={brandColorRef}
|
|
||||||
type="text"
|
|
||||||
name="brandColor"
|
|
||||||
id="brandColor"
|
|
||||||
placeholder="#hex-code"
|
|
||||||
className="block w-full px-3 py-2 mt-1 border border-gray-300 rounded-sm shadow-sm focus:outline-none focus:ring-neutral-800 focus:border-neutral-800 sm:text-sm"
|
|
||||||
defaultValue={props.user.brandColor}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<hr className="mt-6" />
|
<hr className="mt-6" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
|
|
@ -601,6 +601,7 @@ const loggedInViewerRouter = createProtectedRouter()
|
||||||
input: z.object({
|
input: z.object({
|
||||||
username: z.string().optional(),
|
username: z.string().optional(),
|
||||||
name: z.string().optional(),
|
name: z.string().optional(),
|
||||||
|
email: z.string().optional(),
|
||||||
bio: z.string().optional(),
|
bio: z.string().optional(),
|
||||||
avatar: z.string().optional(),
|
avatar: z.string().optional(),
|
||||||
timeZone: z.string().optional(),
|
timeZone: z.string().optional(),
|
||||||
|
|
|
@ -9603,6 +9603,11 @@ react-calendar@^3.3.1:
|
||||||
merge-class-names "^1.1.1"
|
merge-class-names "^1.1.1"
|
||||||
prop-types "^15.6.0"
|
prop-types "^15.6.0"
|
||||||
|
|
||||||
|
react-colorful@^5.5.1:
|
||||||
|
version "5.5.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-colorful/-/react-colorful-5.5.1.tgz#29d9c4e496f2ca784dd2bb5053a3a4340cfaf784"
|
||||||
|
integrity sha512-M1TJH2X3RXEt12sWkpa6hLc/bbYS0H6F4rIqjQZ+RxNBstpY67d9TrFXtqdZwhpmBXcCwEi7stKqFue3ZRkiOg==
|
||||||
|
|
||||||
react-date-picker@^8.3.3:
|
react-date-picker@^8.3.3:
|
||||||
version "8.3.5"
|
version "8.3.5"
|
||||||
resolved "https://registry.yarnpkg.com/react-date-picker/-/react-date-picker-8.3.5.tgz#3972db3fc1f37cfd6b09c09bf4988817b4d2be72"
|
resolved "https://registry.yarnpkg.com/react-date-picker/-/react-date-picker-8.3.5.tgz#3972db3fc1f37cfd6b09c09bf4988817b4d2be72"
|
||||||
|
|
Loading…
Reference in a new issue