diff --git a/frontend/package.json b/frontend/package.json
index 7ad7496..7c2e183 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -3,31 +3,36 @@
"version": "0.1.0",
"private": true,
"dependencies": {
+ "@headlessui/react": "^1.5.0",
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^12.0.0",
"@testing-library/user-event": "^13.2.1",
- "@windmill/react-ui": "^0.6.0",
"autoprefixer": "^10.4.4",
+ "classnames": "^2.3.1",
"hls.js": "^1.1.5",
"postcss": "^8.4.12",
"react": "^17.0.2",
"react-dom": "^17.0.2",
+ "react-focus-lock": "^2.8.1",
"react-helmet": "^6.1.0",
"react-icons": "^4.3.1",
"react-router-dom": "6",
"react-scripts": "5.0.0",
"react-toastify": "^8.2.0",
+ "react-transition-group": "^4.4.2",
"tailwindcss": "^3.0.23",
"typescript": "^4.4.2",
"web-vitals": "^2.1.0"
},
"devDependencies": {
+ "@types/classnames": "^2.3.1",
"@types/hls.js": "^1.0.0",
"@types/jest": "^27.0.1",
"@types/node": "^16.7.13",
"@types/react": "^17.0.20",
"@types/react-dom": "^17.0.9",
- "@types/react-helmet": "^6.1.5"
+ "@types/react-helmet": "^6.1.5",
+ "@types/react-transition-group": "^4.4.4"
},
"scripts": {
"start": "react-scripts start",
diff --git a/frontend/src/components/header.component.tsx b/frontend/src/components/header.component.tsx
index 1ed0276..011398d 100644
--- a/frontend/src/components/header.component.tsx
+++ b/frontend/src/components/header.component.tsx
@@ -1,24 +1,13 @@
import React from "react";
import { VscDebug } from "react-icons/vsc";
-import {
- Avatar,
- Badge,
- Input,
- Dropdown,
- DropdownItem,
-} from "@windmill/react-ui";
+import { BsFillMoonStarsFill, BsFillSunFill, BsSearch } from "react-icons/bs";
+import { BiLogOutCircle, BiCog } from "react-icons/bi";
+import { IoMdPerson } from "react-icons/io";
+import { AiOutlineMenu, AiOutlineBell } from "react-icons/ai";
import { SidebarContext, ThemeContext } from "../context";
-import {
- BellIcon,
- MenuIcon,
- MoonIcon,
- OutlineCogIcon,
- OutlineLogoutIcon,
- OutlinePersonIcon,
- SearchIcon,
- SunIcon,
-} from "../icons";
+
import { toast } from "react-toastify";
+import { Avatar, Badge, Input, Dropdown, DropdownItem } from "./widgets";
const Header = () => {
const { toggleSidebar } = React.useContext(SidebarContext);
@@ -53,15 +42,14 @@ const Header = () => {
onClick={toggleSidebar}
aria-label="Menu"
>
-
+
-
+
{
aria-label="Toggle color mode"
>
{theme === "dark" ? (
-
+
) : (
-
+
)}
@@ -99,7 +87,7 @@ const Header = () => {
aria-label="Notifications"
aria-haspopup="true"
>
-
+
{/* */}
{
onClose={() => setIsProfileMenuOpen(false)}
>
-
+
Profile
-
+
Settings
alert("Log out!")}>
-
+
Log out
diff --git a/frontend/src/components/widgets/avatar.component.tsx b/frontend/src/components/widgets/avatar.component.tsx
new file mode 100644
index 0000000..a7488a5
--- /dev/null
+++ b/frontend/src/components/widgets/avatar.component.tsx
@@ -0,0 +1,43 @@
+import React from "react";
+import classNames from "classnames";
+import { defaultTheme } from "../../constants";
+
+export interface AvatarProps extends React.HTMLAttributes {
+ size?: "large" | "regular" | "small";
+ alt?: string;
+ src: string;
+}
+
+const Avatar = React.forwardRef(function Avatar(
+ props,
+ ref
+) {
+ const { size = "regular", src, alt, className, ...other } = props;
+ const { avatar } = defaultTheme;
+
+ const baseStyle = avatar.base;
+ const sizeStyles = {
+ large: avatar.size.large,
+ regular: avatar.size.regular,
+ small: avatar.size.small,
+ };
+
+ const cls = classNames(baseStyle, sizeStyles[size], className);
+
+ return (
+
+

+
+
+ );
+});
+
+export default Avatar;
diff --git a/frontend/src/components/widgets/badge.component.tsx b/frontend/src/components/widgets/badge.component.tsx
new file mode 100644
index 0000000..3144c93
--- /dev/null
+++ b/frontend/src/components/widgets/badge.component.tsx
@@ -0,0 +1,34 @@
+import React, { useContext } from "react";
+import classNames from "classnames";
+import { defaultTheme } from "../../constants";
+
+export interface BadgeProps extends React.HTMLAttributes {
+ type?: "success" | "danger" | "warning" | "neutral" | "primary";
+}
+
+const Badge = React.forwardRef(function Badge(
+ props,
+ ref
+) {
+ const { className, children, type = "primary", ...other } = props;
+
+ const { badge } = defaultTheme;
+ const baseStyle = badge.base;
+ const typeStyle = {
+ success: badge.success,
+ danger: badge.danger,
+ warning: badge.warning,
+ neutral: badge.neutral,
+ primary: badge.primary,
+ };
+
+ const cls = classNames(baseStyle, typeStyle[type], className);
+
+ return (
+
+ {children}
+
+ );
+});
+
+export default Badge;
diff --git a/frontend/src/components/widgets/button.component.tsx b/frontend/src/components/widgets/button.component.tsx
new file mode 100644
index 0000000..b5a6177
--- /dev/null
+++ b/frontend/src/components/widgets/button.component.tsx
@@ -0,0 +1,171 @@
+import classNames from "classnames";
+import React, { ReactNode, useContext } from "react";
+import { defaultTheme } from "../../constants";
+
+type IconType =
+ | string
+ | React.FunctionComponent<{ className: string; "aria-hidden": boolean }>
+ | React.ComponentClass<{ className: string; "aria-hidden": boolean }>;
+
+export interface Props {
+ children?: React.ReactNode;
+ disabled?: boolean;
+ size?: "larger" | "large" | "regular" | "small" | "pagination";
+ icon?: IconType;
+ iconLeft?: IconType;
+ iconRight?: IconType;
+ layout?: "outline" | "link" | "primary" | "__dropdownItem";
+ block?: boolean;
+}
+
+export interface ButtonAsButtonProps
+ extends Props,
+ React.ButtonHTMLAttributes {
+ tag?: "button";
+ type?: "button" | "submit" | "reset";
+}
+
+export interface ButtonAsAnchorProps
+ extends Props,
+ React.AnchorHTMLAttributes {
+ tag: "a";
+}
+
+export interface ButtonAsOtherProps
+ extends Props,
+ React.AnchorHTMLAttributes {
+ tag: string;
+}
+
+export type ButtonProps =
+ | ButtonAsButtonProps
+ | ButtonAsAnchorProps
+ | ButtonAsOtherProps;
+
+type Ref = ReactNode | HTMLElement | string;
+
+const Button = React.forwardRef[(function Button(props, ref) {
+ const {
+ tag = "button",
+ type = tag === "button" ? "button" : undefined,
+ disabled = false,
+ size = "regular",
+ layout = "primary",
+ block = false,
+ icon,
+ iconLeft,
+ iconRight,
+ className,
+ children,
+ ...other
+ } = props;
+
+ const { button } = defaultTheme;
+ function hasIcon() {
+ return !!icon || !!iconLeft || !!iconRight;
+ }
+
+ console.warn(
+ hasIcon() && !other["aria-label"] && !children,
+ "Button",
+ 'You are using an icon button, but no "aria-label" attribute was found. Add an "aria-label" attribute to work as a label for screen readers.'
+ );
+
+ const IconLeft = iconLeft || icon;
+ const IconRight = iconRight;
+
+ const baseStyle = button.base;
+ const blockStyle = button.block;
+ const sizeStyles = {
+ larger: button.size.larger,
+ large: button.size.large,
+ regular: button.size.regular,
+ small: button.size.small,
+ /**
+ * Only used in Pagination.
+ * Not meant for general use.
+ */
+ pagination: button.size.pagination,
+ };
+ const iconSizeStyles = {
+ larger: button.size.icon.larger,
+ large: button.size.icon.large,
+ regular: button.size.icon.regular,
+ small: button.size.icon.small,
+ pagination: button.size.icon.regular,
+ };
+ const iconStyle: string = button.icon[size as keyof typeof button.icon];
+ const layoutStyles = {
+ primary: button.primary.base,
+ outline: button.outline.base,
+ link: button.link.base,
+ };
+ const activeStyles = {
+ primary: button.primary.active,
+ outline: button.outline.active,
+ link: button.link.active,
+ };
+ const disabledStyles = {
+ primary: button.primary.disabled,
+ outline: button.outline.disabled,
+ link: button.link.disabled,
+ };
+
+ /**
+ * Only used in DropdownItem.
+ * Not meant for general use.
+ */
+ const dropdownItemStyle = button.dropdownItem.base;
+
+ const buttonStyles =
+ layout === "__dropdownItem"
+ ? classNames(dropdownItemStyle, className)
+ : classNames(
+ baseStyle,
+ // has icon but no children
+ hasIcon() && !children && iconSizeStyles[size],
+ // has icon and children
+ hasIcon() && children && sizeStyles[size],
+ // does not have icon
+ !hasIcon() && sizeStyles[size],
+ layoutStyles[layout],
+ disabled ? disabledStyles[layout] : activeStyles[layout],
+ block ? blockStyle : null,
+ className
+ );
+
+ const iconLeftStyles = classNames(
+ iconStyle,
+ children ? button.icon.left : ""
+ );
+ const iconRightStyles = classNames(
+ iconStyle,
+ children ? button.icon.right : ""
+ );
+
+ return React.createElement(
+ tag,
+ {
+ className: buttonStyles,
+ ref,
+ disabled,
+ type,
+ ...other,
+ },
+ IconLeft
+ ? React.createElement(IconLeft, {
+ className: iconLeftStyles,
+ "aria-hidden": true,
+ })
+ : null,
+ children,
+ IconRight
+ ? React.createElement(IconRight, {
+ className: iconRightStyles,
+ "aria-hidden": true,
+ })
+ : null
+ );
+});
+
+export default Button;
diff --git a/frontend/src/components/widgets/dropdown.component.tsx b/frontend/src/components/widgets/dropdown.component.tsx
new file mode 100644
index 0000000..a63657c
--- /dev/null
+++ b/frontend/src/components/widgets/dropdown.component.tsx
@@ -0,0 +1,108 @@
+import React, { useEffect, useContext, useRef } from "react";
+import classNames from "classnames";
+import FocusLock from "react-focus-lock";
+import { defaultTheme } from "../../constants";
+import Button, { ButtonProps } from "./button.component";
+import Transition from "./transition.component";
+
+export interface DropdownProps extends React.HTMLAttributes {
+ /**
+ * Function executed when the dropdown is closed
+ */
+ onClose: () => void;
+ /**
+ * Defines if the dropdown is open
+ */
+ isOpen: boolean;
+ /**
+ * Defines the alignement of the dropdown related to its parent
+ */
+ align?: "left" | "right";
+}
+
+const Dropdown = React.forwardRef(
+ function Dropdown(props, ref) {
+ const {
+ children,
+ onClose,
+ isOpen,
+ className,
+ align = "left",
+ ...other
+ } = props;
+ const { dropdown } = defaultTheme;
+ const baseStyle = dropdown.base;
+ const alignStyle = dropdown.align[align];
+
+ function handleEsc(e: KeyboardEvent) {
+ if (e.key === "Esc" || e.key === "Escape") {
+ onClose();
+ }
+ }
+
+ const dropdownRef = useRef(null);
+ function handleClickOutside(e: MouseEvent) {
+ if (
+ dropdownRef.current &&
+ !dropdownRef.current.contains(e.target as Node)
+ ) {
+ onClose();
+ }
+ }
+
+ useEffect(() => {
+ document.addEventListener("click", handleClickOutside, { capture: true });
+ document.addEventListener("keydown", handleEsc, { capture: true });
+ return () => {
+ document.removeEventListener("click", handleClickOutside);
+ document.removeEventListener("keydown", handleEsc);
+ };
+ }, [isOpen]);
+
+ const cls = classNames(baseStyle, alignStyle, className);
+
+ return (
+
+
+
+ );
+ }
+);
+
+type Ref = typeof Button;
+const DropdownItem = React.forwardRef][(function DropdownItem(
+ props,
+ ref
+) {
+ // Note: className is passed to the inner Button
+ const { children, ...other } = props;
+
+ const { dropdownItem } = defaultTheme;
+ const baseStyle = dropdownItem.base;
+
+ return (
+
+
+
+ );
+});
+
+export { Dropdown, DropdownItem };
diff --git a/frontend/src/components/widgets/index.ts b/frontend/src/components/widgets/index.ts
new file mode 100644
index 0000000..63b2503
--- /dev/null
+++ b/frontend/src/components/widgets/index.ts
@@ -0,0 +1,7 @@
+import Avatar from "./avatar.component";
+import Badge from "./badge.component";
+import Button from "./button.component";
+import { Dropdown, DropdownItem } from "./dropdown.component";
+import Input from "./input.component";
+
+export { Avatar, Badge, Button, Input, Dropdown, DropdownItem };
diff --git a/frontend/src/components/widgets/input.component.tsx b/frontend/src/components/widgets/input.component.tsx
new file mode 100644
index 0000000..9d6aff4
--- /dev/null
+++ b/frontend/src/components/widgets/input.component.tsx
@@ -0,0 +1,69 @@
+import React, { useContext } from "react";
+import classNames from "classnames";
+import { defaultTheme } from "../../constants";
+
+export interface InputProps extends React.ComponentPropsWithRef<"input"> {
+ valid?: boolean;
+ disabled?: boolean;
+ type?: string;
+}
+
+const Input = React.forwardRef(function Input(
+ props,
+ ref
+) {
+ const { valid, disabled, className, type = "text", ...other } = props;
+
+ const { input } = defaultTheme;
+ const baseStyle = input.base;
+ const activeStyle = input.active;
+ const disabledStyle = input.disabled;
+ const validStyle = input.valid;
+ const invalidStyle = input.invalid;
+ const radioStyle = input.radio;
+ const checkStyle = input.checkbox;
+
+ function hasValidation(valid: boolean | undefined) {
+ return valid !== undefined;
+ }
+
+ function validationStyle(valid: boolean | undefined): string {
+ if (hasValidation(valid)) {
+ return valid ? validStyle : invalidStyle;
+ }
+ return "";
+ }
+
+ function typeStyle(type: string): string {
+ switch (type) {
+ case "radio":
+ return radioStyle;
+ case "checkbox":
+ return checkStyle;
+ default:
+ return baseStyle;
+ }
+ }
+
+ const cls = classNames(
+ typeStyle(type),
+ // don't apply activeStyle if has valid or disabled
+ !hasValidation(valid) && !disabled && activeStyle,
+ // don't apply disabledStyle if has valid
+ !hasValidation(valid) && disabled && disabledStyle,
+ validationStyle(valid),
+ className
+ );
+
+ return (
+
+ );
+});
+
+export default Input;
diff --git a/frontend/src/components/widgets/table/index.ts b/frontend/src/components/widgets/table/index.ts
new file mode 100644
index 0000000..2968c97
--- /dev/null
+++ b/frontend/src/components/widgets/table/index.ts
@@ -0,0 +1,17 @@
+import TableBody from "./table-body.component";
+import TableCell from "./table-cell.component";
+import TableContainer from "./table-container.component";
+import TableFooter from "./table-footer.component";
+import TableHeader from "./table-header.component";
+import TableRow from "./table-row.component";
+import Table from "./table.component";
+
+export {
+ Table,
+ TableBody,
+ TableCell,
+ TableContainer,
+ TableFooter,
+ TableHeader,
+ TableRow,
+};
diff --git a/frontend/src/components/widgets/table/table-body.component.tsx b/frontend/src/components/widgets/table/table-body.component.tsx
new file mode 100644
index 0000000..0b146ff
--- /dev/null
+++ b/frontend/src/components/widgets/table/table-body.component.tsx
@@ -0,0 +1,24 @@
+import React from "react";
+import classNames from "classnames";
+import { defaultTheme } from "../../../constants";
+
+interface Props extends React.HTMLAttributes {}
+
+const TableBody = React.forwardRef(
+ function TableBody(props, ref) {
+ const { className, children, ...other } = props;
+
+ const { tableBody } = defaultTheme;
+ const baseStyle = tableBody.base;
+
+ const cls = classNames(baseStyle, className);
+
+ return (
+
+ {children}
+
+ );
+ }
+);
+
+export default TableBody;
diff --git a/frontend/src/components/widgets/table/table-cell.component.tsx b/frontend/src/components/widgets/table/table-cell.component.tsx
new file mode 100644
index 0000000..8dc356c
--- /dev/null
+++ b/frontend/src/components/widgets/table/table-cell.component.tsx
@@ -0,0 +1,25 @@
+import React from "react";
+import classNames from "classnames";
+import {defaultTheme} from '../../../constants';
+
+interface Props extends React.TdHTMLAttributes {}
+
+const TableCell = React.forwardRef(
+ function TableCell(props, ref) {
+ const { className, children, ...other } = props;
+
+ const { tableCell } = defaultTheme;
+
+ const baseStyle = tableCell.base;
+
+ const cls = classNames(baseStyle, className);
+
+ return (
+ ]|
+ {children}
+ |
+ );
+ }
+);
+
+export default TableCell;
diff --git a/frontend/src/components/widgets/table/table-container.component.tsx b/frontend/src/components/widgets/table/table-container.component.tsx
new file mode 100644
index 0000000..f0dc06b
--- /dev/null
+++ b/frontend/src/components/widgets/table/table-container.component.tsx
@@ -0,0 +1,23 @@
+import React from "react";
+import classNames from "classnames";
+import { defaultTheme } from "../../../constants";
+
+interface Props extends React.HTMLAttributes {}
+
+const TableContainer = React.forwardRef(
+ function TableContainer(props, ref) {
+ const { className, children, ...other } = props;
+ const { tableContainer } = defaultTheme;
+ const baseStyle = tableContainer.base;
+
+ const cls = classNames(baseStyle, className);
+
+ return (
+
+ {children}
+
+ );
+ }
+);
+
+export default TableContainer;
diff --git a/frontend/src/components/widgets/table/table-footer.component.tsx b/frontend/src/components/widgets/table/table-footer.component.tsx
new file mode 100644
index 0000000..c58398b
--- /dev/null
+++ b/frontend/src/components/widgets/table/table-footer.component.tsx
@@ -0,0 +1,24 @@
+import React from "react";
+import classNames from "classnames";
+import { defaultTheme } from "../../../constants";
+
+interface Props extends React.HTMLAttributes {}
+
+const TableFooter = React.forwardRef(
+ function TableFooter(props, ref) {
+ const { className, children, ...other } = props;
+
+ const { tableFooter } = defaultTheme;
+ const baseStyle = tableFooter.base;
+
+ const cls = classNames(baseStyle, className);
+
+ return (
+
+ {children}
+
+ );
+ }
+);
+
+export default TableFooter;
diff --git a/frontend/src/components/widgets/table/table-header.component.tsx b/frontend/src/components/widgets/table/table-header.component.tsx
new file mode 100644
index 0000000..87b1c85
--- /dev/null
+++ b/frontend/src/components/widgets/table/table-header.component.tsx
@@ -0,0 +1,24 @@
+import React from "react";
+import classNames from "classnames";
+import { defaultTheme } from "../../../constants";
+
+interface Props extends React.HTMLAttributes {}
+
+const TableHeader = React.forwardRef(
+ function TableHeader(props, ref) {
+ const { className, children, ...other } = props;
+
+ const { tableHeader } = defaultTheme;
+ const baseStyle = tableHeader.base;
+
+ const cls = classNames(baseStyle, className);
+
+ return (
+
+ {children}
+
+ );
+ }
+);
+
+export default TableHeader;
diff --git a/frontend/src/components/widgets/table/table-row.component.tsx b/frontend/src/components/widgets/table/table-row.component.tsx
new file mode 100644
index 0000000..8c25ccf
--- /dev/null
+++ b/frontend/src/components/widgets/table/table-row.component.tsx
@@ -0,0 +1,25 @@
+import React from "react";
+import classNames from "classnames";
+import { defaultTheme } from "../../../constants";
+
+interface Props extends React.HTMLAttributes {}
+
+const TableRow = React.forwardRef(function TableRow(
+ props,
+ ref
+) {
+ const { className, children, ...other } = props;
+
+ const { tableRow } = defaultTheme;
+ const baseStyle = tableRow.base;
+
+ const cls = classNames(baseStyle, className);
+
+ return (
+
+ {children}
+
+ );
+});
+
+export default TableRow;
diff --git a/frontend/src/components/widgets/table/table.component.tsx b/frontend/src/components/widgets/table/table.component.tsx
new file mode 100644
index 0000000..746991c
--- /dev/null
+++ b/frontend/src/components/widgets/table/table.component.tsx
@@ -0,0 +1,20 @@
+import React from "react";
+
+export interface TableProps
+ extends React.TableHTMLAttributes {}
+
+const Table = React.forwardRef(function Table(
+ props,
+ ref
+) {
+ const { children, ...other } = props;
+ return (
+
+ );
+});
+
+export default Table;
diff --git a/frontend/src/components/widgets/transition.component.tsx b/frontend/src/components/widgets/transition.component.tsx
new file mode 100644
index 0000000..d555946
--- /dev/null
+++ b/frontend/src/components/widgets/transition.component.tsx
@@ -0,0 +1,124 @@
+import React, { useContext, useEffect, useRef } from "react";
+import { CSSTransition as ReactCSSTransition } from "react-transition-group";
+
+interface TransitionContext {
+ parent: {
+ appear?: string;
+ show?: boolean;
+ isInitialRender?: boolean;
+ };
+}
+const transitionContext = React.createContext({
+ parent: {},
+});
+
+function useIsInitialRender() {
+ const isInitialRender = useRef(true);
+ useEffect(() => {
+ isInitialRender.current = false;
+ }, []);
+ return isInitialRender.current;
+}
+
+interface TransitionProps {
+ children?: React.ReactNode;
+ show?: boolean;
+ enter?: string;
+ enterFrom?: string;
+ enterTo?: string;
+ leave?: string;
+ leaveFrom?: string;
+ leaveTo?: string;
+ appear?: string;
+}
+
+const CSSTransition: React.FC = function CSSTransition({
+ show,
+ enter = "",
+ enterFrom = "",
+ enterTo = "",
+ leave = "",
+ leaveFrom = "",
+ leaveTo = "",
+ appear,
+ children,
+}) {
+ const enterClasses = enter.split(" ").filter((s) => s.length);
+ const enterFromClasses = enterFrom.split(" ").filter((s) => s.length);
+ const enterToClasses = enterTo.split(" ").filter((s) => s.length);
+ const leaveClasses = leave.split(" ").filter((s) => s.length);
+ const leaveFromClasses = leaveFrom.split(" ").filter((s) => s.length);
+ const leaveToClasses = leaveTo.split(" ").filter((s) => s.length);
+
+ function addClasses(node: HTMLElement, classes: string[]) {
+ classes.length && node.classList.add(...classes);
+ }
+
+ function removeClasses(node: HTMLElement, classes: string[]) {
+ classes.length && node.classList.remove(...classes);
+ }
+
+ return (
+ {
+ node.addEventListener("transitionend", done, false);
+ }}
+ onEnter={(node: HTMLElement) => {
+ addClasses(node, [...enterClasses, ...enterFromClasses]);
+ }}
+ onEntering={(node: HTMLElement) => {
+ removeClasses(node, enterFromClasses);
+ addClasses(node, enterToClasses);
+ }}
+ onEntered={(node: HTMLElement) => {
+ removeClasses(node, [...enterToClasses, ...enterClasses]);
+ }}
+ onExit={(node: HTMLElement) => {
+ addClasses(node, [...leaveClasses, ...leaveFromClasses]);
+ }}
+ onExiting={(node: HTMLElement) => {
+ removeClasses(node, leaveFromClasses);
+ addClasses(node, leaveToClasses);
+ }}
+ onExited={(node: HTMLElement) => {
+ removeClasses(node, [...leaveToClasses, ...leaveClasses]);
+ }}
+ >
+ {children}
+
+ );
+};
+
+const Transition: React.FC = function Transition({
+ show,
+ appear,
+ ...rest
+}) {
+ const { parent } = useContext(transitionContext);
+ const isInitialRender = useIsInitialRender();
+ const isChild = show === undefined;
+
+ if (isChild) {
+ return (
+
+ );
+ } else
+ return (
+
+
+
+ );
+};
+
+export default Transition;
diff --git a/frontend/src/constants/index.ts b/frontend/src/constants/index.ts
new file mode 100644
index 0000000..3404908
--- /dev/null
+++ b/frontend/src/constants/index.ts
@@ -0,0 +1,3 @@
+import defaultTheme from "./theme";
+
+export { defaultTheme };
diff --git a/frontend/src/constants/theme.ts b/frontend/src/constants/theme.ts
new file mode 100644
index 0000000..b263486
--- /dev/null
+++ b/frontend/src/constants/theme.ts
@@ -0,0 +1,210 @@
+const defaultTheme = {
+ // Alert
+ alert: {
+ base: "p-4 pl-12 relative rounded-lg leading-5",
+ withClose: "pr-12",
+ success: "bg-green-50 text-green-900 dark:bg-green-600 dark:text-white",
+ danger: "bg-red-50 text-red-900 dark:bg-red-600 dark:text-white",
+ warning: "bg-yellow-50 text-yellow-900 dark:bg-yellow-600 dark:text-white",
+ neutral: "bg-gray-50 text-gray-800 dark:bg-gray-700 dark:text-gray-300",
+ info: "bg-blue-50 text-blue-900 dark:bg-blue-600 dark:text-white",
+ icon: {
+ base: "h-5 w-5",
+ success: "text-green-400 dark:text-green-300",
+ danger: "text-red-400 dark:text-red-300",
+ warning: "text-yellow-400 dark:text-yellow-100",
+ neutral: "text-gray-400 dark:text-gray-500",
+ info: "text-blue-400 dark:text-blue-300",
+ },
+ },
+ // Pagination
+ pagination: {
+ base: "flex flex-col justify-between text-xs sm:flex-row text-gray-600 dark:text-gray-400",
+ },
+ // TableFooter
+ tableFooter: {
+ base: "px-4 py-3 border-t dark:border-gray-700 bg-gray-50 text-gray-500 dark:text-gray-400 dark:bg-gray-800",
+ },
+ // TableRow
+ tableRow: {
+ base: "",
+ },
+ // TableHeader
+ tableHeader: {
+ base: "text-xs font-semibold tracking-wide text-left text-gray-500 uppercase border-b dark:border-gray-700 bg-gray-50 dark:text-gray-400 dark:bg-gray-800",
+ },
+ // TableContainer
+ tableContainer: {
+ base: "w-full overflow-hidden rounded-lg ring-1 ring-black ring-opacity-5",
+ },
+ // TableCell
+ tableCell: {
+ base: "px-4 py-3",
+ },
+ // TableBody
+ tableBody: {
+ base: "bg-white divide-y dark:divide-gray-700 dark:bg-gray-800 text-gray-700 dark:text-gray-400",
+ },
+ // DropdownItem
+ // this is the that lives inside the Dropdown
+ // you're probably looking for the dropdownItem style inside button
+ dropdownItem: {
+ base: "mb-2 last:mb-0",
+ },
+ // Dropdown
+ dropdown: {
+ base: "absolute w-56 p-2 mt-2 text-gray-600 bg-white border border-gray-100 rounded-lg shadow-md min-w-max-content dark:text-gray-300 dark:border-gray-700 dark:bg-gray-700",
+ align: {
+ left: "left-0",
+ right: "right-0",
+ },
+ },
+ // Avatar
+ avatar: {
+ base: "relative rounded-full inline-block",
+ size: {
+ large: "w-10 h-10",
+ regular: "w-8 h-8",
+ small: "w-6 h-6",
+ },
+ },
+ // Modal
+ modal: {
+ base: "w-full px-6 py-4 overflow-hidden bg-white rounded-t-lg dark:bg-gray-800 sm:rounded-lg sm:m-4 sm:max-w-xl",
+ },
+ // ModalBody
+ modalBody: {
+ base: "mb-6 text-sm text-gray-700 dark:text-gray-400",
+ },
+ // ModalFooter
+ modalFooter: {
+ base: "flex flex-col items-center justify-end px-6 py-3 -mx-6 -mb-4 space-y-4 sm:space-y-0 sm:space-x-6 sm:flex-row bg-gray-50 dark:bg-gray-800",
+ },
+ // ModalHeader
+ modalHeader: {
+ base: "mt-4 mb-2 text-lg font-semibold text-gray-700 dark:text-gray-300",
+ },
+ // Badge
+ badge: {
+ base: "inline-flex px-2 text-xs font-medium leading-5 rounded-full",
+ success:
+ "text-green-700 bg-green-100 dark:bg-green-700 dark:text-green-100",
+ danger: "text-red-700 bg-red-100 dark:text-red-100 dark:bg-red-700",
+ warning: "text-orange-700 bg-orange-100 dark:text-white dark:bg-orange-600",
+ neutral: "text-gray-700 bg-gray-100 dark:text-gray-100 dark:bg-gray-700",
+ primary: "text-purple-700 bg-purple-100 dark:text-white dark:bg-purple-600",
+ },
+ // Backdrop
+ backdrop: {
+ base: "fixed inset-0 z-40 flex items-end bg-black bg-opacity-50 sm:items-center sm:justify-center",
+ },
+ // Textarea
+ textarea: {
+ base: "block w-full text-sm dark:text-gray-300 rounded-md focus:outline-none",
+ active:
+ "focus:border-purple-400 border-gray-300 dark:border-gray-600 dark:focus:border-gray-600 dark:bg-gray-700 dark:focus:ring-gray-300 focus:ring focus:ring-purple-300",
+ disabled: "cursor-not-allowed opacity-50 bg-gray-300 dark:bg-gray-800",
+ valid:
+ "border-green-600 dark:bg-gray-700 focus:border-green-400 dark:focus:border-green-400 focus:ring focus:ring-green-200 dark:focus:ring-green-200",
+ invalid:
+ "border-red-600 dark:bg-gray-700 focus:border-red-400 dark:focus:border-red-400 focus:ring focus:ring-red-200 dark:focus:ring-red-200",
+ },
+ // Select
+ select: {
+ base: "block w-full text-sm dark:text-gray-300 focus:outline-none rounded-md",
+ active:
+ "focus:border-purple-400 border-gray-300 dark:border-gray-600 dark:bg-gray-700 focus:ring focus:ring-purple-300 dark:focus:ring-gray-300 dark:focus:border-gray-600",
+ select: "leading-5",
+ disabled: "cursor-not-allowed opacity-50 bg-gray-300 dark:bg-gray-800",
+ valid:
+ "border-green-600 dark:bg-gray-700 focus:border-green-400 dark:focus:border-green-400 focus:ring focus:ring-green-200 dark:focus:ring-green-200",
+ invalid:
+ "border-red-600 dark:bg-gray-700 focus:border-red-400 dark:focus:border-red-400 focus:ring focus:ring-red-200 dark:focus:ring-red-200",
+ },
+ // Label
+ label: {
+ base: "block text-sm text-gray-700 dark:text-gray-400",
+ // check and radio get this same style
+ check: "inline-flex items-center",
+ disabled: "opacity-50 cursor-not-allowed",
+ },
+ // Input
+ input: {
+ base: "block w-full text-sm focus:outline-none dark:text-gray-300 leading-5 rounded-md",
+ active:
+ "focus:border-purple-400 border-gray-300 dark:border-gray-600 focus:ring focus:ring-purple-300 dark:focus:border-gray-600 dark:focus:ring-gray-300 dark:bg-gray-700",
+ disabled: "cursor-not-allowed opacity-50 bg-gray-300 dark:bg-gray-800",
+ valid:
+ "border-green-600 dark:bg-gray-700 focus:border-green-400 dark:focus:border-green-400 focus:ring focus:ring-green-200 dark:focus:ring-green-200",
+ invalid:
+ "border-red-600 dark:bg-gray-700 focus:border-red-400 dark:focus:border-red-400 focus:ring focus:ring-red-200 dark:focus:ring-red-200",
+ radio:
+ "text-purple-600 form-radio focus:border-purple-400 focus:outline-none focus:ring focus:ring-purple-300 focus:ring-offset-0 dark:focus:ring-gray-300",
+ checkbox:
+ "text-purple-600 form-checkbox focus:border-purple-400 focus:outline-none focus:ring focus:ring-purple-300 focus:ring-offset-0 rounded dark:focus:ring-gray-300",
+ },
+ // HelperText
+ helperText: {
+ base: "text-xs",
+ valid: "text-green-600 dark:text-green-400",
+ invalid: "text-red-600 dark:text-red-400",
+ },
+ // Card
+ card: {
+ base: "min-w-0 rounded-lg ring-1 ring-black ring-opacity-5 overflow-hidden",
+ default: "bg-white dark:bg-gray-800",
+ },
+ cardBody: {
+ base: "p-4",
+ },
+ // Button
+ button: {
+ base: "align-bottom inline-flex items-center justify-center cursor-pointer leading-5 transition-colors duration-150 font-medium focus:outline-none",
+ block: "w-full",
+ size: {
+ larger: "px-10 py-4 rounded-lg",
+ large: "px-5 py-3 rounded-lg",
+ regular: "px-4 py-2 rounded-lg text-sm",
+ small: "px-3 py-1 rounded-md text-sm",
+ icon: {
+ larger: "p-4 rounded-lg",
+ large: "p-3 rounded-lg",
+ regular: "p-2 rounded-lg",
+ small: "p-2 rounded-md",
+ },
+ pagination: "px-3 py-1 rounded-md text-xs",
+ },
+ // styles applied to the SVG icon
+ icon: {
+ larger: "h-5 w-5",
+ large: "h-5 w-5",
+ regular: "h-5 w-5",
+ small: "h-3 w-3",
+ left: "mr-2 -ml-1",
+ right: "ml-2 -mr-1",
+ },
+ primary: {
+ base: "text-white bg-purple-600 border border-transparent",
+ active:
+ "active:bg-purple-600 hover:bg-purple-700 focus:ring focus:ring-purple-300",
+ disabled: "opacity-50 cursor-not-allowed",
+ },
+ outline: {
+ base: "text-gray-600 border-gray-300 border dark:text-gray-400 focus:outline-none",
+ active:
+ "active:bg-transparent hover:border-gray-500 focus:border-gray-500 active:text-gray-500 focus:ring focus:ring-gray-300",
+ disabled: "opacity-50 cursor-not-allowed bg-gray-300",
+ },
+ link: {
+ base: "text-gray-600 dark:text-gray-400 focus:outline-none border border-transparent",
+ active:
+ "active:bg-transparent hover:bg-gray-100 focus:ring focus:ring-gray-300 dark:hover:bg-gray-500 dark:hover:text-gray-300 dark:hover:bg-opacity-10",
+ disabled: "opacity-50 cursor-not-allowed",
+ },
+ // this is the button that lives inside the DropdownItem
+ dropdownItem: {
+ base: "inline-flex items-center cursor-pointer w-full px-2 py-1 text-sm font-medium transition-colors duration-150 rounded-md hover:bg-gray-100 hover:text-gray-800 dark:hover:bg-gray-800 dark:hover:text-gray-200",
+ },
+ },
+};
+export default defaultTheme;
diff --git a/frontend/src/pages/channel.page.tsx b/frontend/src/pages/channel.page.tsx
index df17c7b..effaf75 100644
--- a/frontend/src/pages/channel.page.tsx
+++ b/frontend/src/pages/channel.page.tsx
@@ -1,18 +1,6 @@
-import {
- Table,
- TableHeader,
- TableCell,
- TableBody,
- TableRow,
- TableContainer,
- Badge,
- Avatar,
- Button,
-} from "@windmill/react-ui";
import React, { Suspense } from "react";
import { useParams } from "react-router-dom";
import { AiOutlinePlayCircle } from "react-icons/ai";
-import { FaChromecast } from "react-icons/fa";
import { Stream } from "../models/stream";
import { convertEpochToSpecificTimezone } from "../utils/date-utils";
import { EPGComponent } from "../components";
@@ -23,6 +11,8 @@ import {
useMedia,
} from "../utils/chromecast";
import { toast } from "react-toastify";
+import { Table, TableBody, TableCell, TableContainer, TableHeader, TableRow } from "../components/widgets/table";
+import { Avatar, Badge, Button } from "../components/widgets";
const ChannelPage = () => {
let params = useParams();
@@ -142,11 +132,11 @@ const ChannelPage = () => {
-
+
Channel
Type
-
+
{streams.map((stream: Stream) => [
diff --git a/frontend/src/utils/chromecast/CastButton.tsx b/frontend/src/utils/chromecast/CastButton.tsx
index 0c11e1a..905abe3 100644
--- a/frontend/src/utils/chromecast/CastButton.tsx
+++ b/frontend/src/utils/chromecast/CastButton.tsx
@@ -1,7 +1,7 @@
import React from "react";
import useCast from "./useCast";
-import { Button } from "@windmill/react-ui";
import { FaChromecast } from "react-icons/fa";
+import { Button } from "../../components/widgets";
interface ICastButtonProps {
streamId: number;
diff --git a/frontend/yarn.lock b/frontend/yarn.lock
index e7f4789..c5f7aa9 100644
--- a/frontend/yarn.lock
+++ b/frontend/yarn.lock
@@ -1024,7 +1024,14 @@
core-js-pure "^3.20.2"
regenerator-runtime "^0.13.4"
-"@babel/runtime@^7.0.0", "@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.3", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2":
+"@babel/runtime@^7.0.0", "@babel/runtime@^7.12.13":
+ version "7.17.9"
+ resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.9.tgz#d19fbf802d01a8cb6cf053a64e472d42c434ba72"
+ integrity sha512-lSiBBvodq29uShpWGNbgFdKYNiFDo5/HIYsaCEY9ff4sb10x9jizo2+pRrSyF4jKZCXqgzuqBOQKbUm90gQwJg==
+ dependencies:
+ regenerator-runtime "^0.13.4"
+
+"@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.3", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2":
version "7.17.8"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.8.tgz#3e56e4aff81befa55ac3ac6a0967349fd1c5bca2"
integrity sha512-dQpEpK0O9o6lj6oPu0gRDbbnk+4LeHlNcBpspf6Olzt3GIX4P1lWF1gS+pHLDFlaJvbR6q7jCfQ08zA4QJBnmA==
@@ -1148,6 +1155,11 @@
minimatch "^3.0.4"
strip-json-comments "^3.1.1"
+"@headlessui/react@^1.5.0":
+ version "1.5.0"
+ resolved "https://registry.yarnpkg.com/@headlessui/react/-/react-1.5.0.tgz#483b44ba2c8b8d4391e1d2c863898d7dd0cc0296"
+ integrity sha512-aaRnYxBb3MU2FNJf3Ut9RMTUqqU3as0aI1lQhgo2n9Fa67wRu14iOGqx93xB+uMNVfNwZ5B3y/Ndm7qZGuFeMQ==
+
"@humanwhocodes/config-array@^0.9.2":
version "0.9.5"
resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.9.5.tgz#2cbaf9a89460da24b5ca6531b8bbfc23e1df50c7"
@@ -1570,13 +1582,6 @@
"@svgr/plugin-svgo" "^5.5.0"
loader-utils "^2.0.0"
-"@tailwindcss/forms@^0.3.2":
- version "0.3.4"
- resolved "https://registry.yarnpkg.com/@tailwindcss/forms/-/forms-0.3.4.tgz#e4939dc16450eccf4fd2029770096f38cbb556d4"
- integrity sha512-vlAoBifNJUkagB+PAdW4aHMe4pKmSLroH398UPgIogBFc91D2VlHUxe4pjxQhiJl0Nfw53sHSJSQBSTQBZP3vA==
- dependencies:
- mini-svg-data-uri "^1.2.3"
-
"@testing-library/dom@^8.0.0":
version "8.12.0"
resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.12.0.tgz#fef5e545533fb084175dda6509ee71d7d2f72e23"
@@ -1685,6 +1690,13 @@
dependencies:
"@types/node" "*"
+"@types/classnames@^2.3.1":
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/@types/classnames/-/classnames-2.3.1.tgz#3c2467aa0f1a93f1f021e3b9bcf938bd5dfdc0dd"
+ integrity sha512-zeOWb0JGBoVmlQoznvqXbE0tEC/HONsnoUNH19Hc96NFsTAwTXbTqb8FMYkru1F/iqp7a18Ws3nWJvtA1sHD1A==
+ dependencies:
+ classnames "*"
+
"@types/connect-history-api-fallback@^1.3.5":
version "1.3.5"
resolved "https://registry.yarnpkg.com/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz#d1f7a8a09d0ed5a57aee5ae9c18ab9b803205dae"
@@ -1875,6 +1887,13 @@
dependencies:
"@types/react" "*"
+"@types/react-transition-group@^4.4.4":
+ version "4.4.4"
+ resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.4.tgz#acd4cceaa2be6b757db61ed7b432e103242d163e"
+ integrity sha512-7gAPz7anVK5xzbeQW9wFBDg7G++aPLAFY0QaSMOou9rJZpbuI58WAuJrgu+qR92l61grlnCUe7AFX8KGahAgug==
+ dependencies:
+ "@types/react" "*"
+
"@types/react@*", "@types/react@^17.0.20":
version "17.0.43"
resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.43.tgz#4adc142887dd4a2601ce730bc56c3436fdb07a55"
@@ -2167,18 +2186,6 @@
"@webassemblyjs/ast" "1.11.1"
"@xtuc/long" "4.2.2"
-"@windmill/react-ui@^0.6.0":
- version "0.6.0"
- resolved "https://registry.yarnpkg.com/@windmill/react-ui/-/react-ui-0.6.0.tgz#259ee5d8b08088d4b0258fdc7595760dda6b09fe"
- integrity sha512-VjvRC0YI8V/uUMWU70XL0jHzBYmRGPMlvauLjdHJ0h60cSFm2ZrcvwIVktPi9eWw9au2cXov13rkwvVmPM/Yww==
- dependencies:
- "@tailwindcss/forms" "^0.3.2"
- classnames "2.2.6"
- deepmerge "4.2.2"
- postcss "^8.2.15"
- react-focus-lock "2.4.1"
- react-transition-group "4.4.1"
-
"@xtuc/ieee754@^1.2.0":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790"
@@ -2898,10 +2905,10 @@ cjs-module-lexer@^1.0.0:
resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40"
integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==
-classnames@2.2.6:
- version "2.2.6"
- resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce"
- integrity sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==
+classnames@*, classnames@^2.3.1:
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.1.tgz#dfcfa3891e306ec1dad105d0e88f4417b8535e8e"
+ integrity sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==
clean-css@^5.2.2:
version "5.2.4"
@@ -3413,7 +3420,7 @@ deep-is@^0.1.3, deep-is@~0.1.3:
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831"
integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==
-deepmerge@4.2.2, deepmerge@^4.2.2:
+deepmerge@^4.2.2:
version "4.2.2"
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955"
integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==
@@ -4286,10 +4293,12 @@ flatted@^3.1.0:
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.5.tgz#76c8584f4fc843db64702a6bd04ab7a8bd666da3"
integrity sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==
-focus-lock@^0.7.0:
- version "0.7.0"
- resolved "https://registry.yarnpkg.com/focus-lock/-/focus-lock-0.7.0.tgz#b2bfb0ca7beacc8710a1ff74275fe0dc60a1d88a"
- integrity sha512-LI7v2mH02R55SekHYdv9pRHR9RajVNyIJ2N5IEkWbg7FT5ZmJ9Hw4mWxHeEUcd+dJo0QmzztHvDvWcc7prVFsw==
+focus-lock@^0.10.2:
+ version "0.10.2"
+ resolved "https://registry.yarnpkg.com/focus-lock/-/focus-lock-0.10.2.tgz#561c62bae8387ecba1dd8e58a6df5ec29835c644"
+ integrity sha512-DSaI/UHZ/02sg1P616aIWgToQcrKKBmcCvomDZ1PZvcJFj350PnWhSJxJ76T3e5/GbtQEARIACtbrdlrF9C5kA==
+ dependencies:
+ tslib "^2.0.3"
follow-redirects@^1.0.0:
version "1.14.9"
@@ -5895,11 +5904,6 @@ mini-css-extract-plugin@^2.4.5:
dependencies:
schema-utils "^4.0.0"
-mini-svg-data-uri@^1.2.3:
- version "1.4.4"
- resolved "https://registry.yarnpkg.com/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz#8ab0aabcdf8c29ad5693ca595af19dd2ead09939"
- integrity sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==
-
minimalistic-assert@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7"
@@ -6911,7 +6915,7 @@ postcss@^7.0.35:
picocolors "^0.2.1"
source-map "^0.6.1"
-postcss@^8.2.15, postcss@^8.3.5, postcss@^8.4.12, postcss@^8.4.4, postcss@^8.4.6, postcss@^8.4.7:
+postcss@^8.3.5, postcss@^8.4.12, postcss@^8.4.4, postcss@^8.4.6, postcss@^8.4.7:
version "8.4.12"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.12.tgz#1e7de78733b28970fa4743f7da6f3763648b1905"
integrity sha512-lg6eITwYe9v6Hr5CncVbK70SoioNQIq81nsaG86ev5hAidQvmOeETBqs7jm43K2F5/Ley3ytDtriImV6TpNiSg==
@@ -7060,7 +7064,7 @@ react-app-polyfill@^3.0.0:
regenerator-runtime "^0.13.9"
whatwg-fetch "^3.6.2"
-react-clientside-effect@^1.2.2:
+react-clientside-effect@^1.2.5:
version "1.2.5"
resolved "https://registry.yarnpkg.com/react-clientside-effect/-/react-clientside-effect-1.2.5.tgz#e2c4dc3c9ee109f642fac4f5b6e9bf5bcd2219a3"
integrity sha512-2bL8qFW1TGBHozGGbVeyvnggRpMjibeZM2536AKNENLECutp2yfs44IL8Hmpn8qjFQ2K7A9PnYf3vc7aQq/cPA==
@@ -7116,17 +7120,17 @@ react-fast-compare@^3.1.1:
resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.0.tgz#641a9da81b6a6320f270e89724fb45a0b39e43bb"
integrity sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==
-react-focus-lock@2.4.1:
- version "2.4.1"
- resolved "https://registry.yarnpkg.com/react-focus-lock/-/react-focus-lock-2.4.1.tgz#e842cc93da736b5c5d331799012544295cbcee4f"
- integrity sha512-c5ZP56KSpj9EAxzScTqQO7bQQNPltf/W1ZEBDqNDOV1XOIwvAyHX0O7db9ekiAtxyKgnqZjQlLppVg94fUeL9w==
+react-focus-lock@^2.8.1:
+ version "2.8.1"
+ resolved "https://registry.yarnpkg.com/react-focus-lock/-/react-focus-lock-2.8.1.tgz#a28f06a4ef5eab7d4ef0d859512772ec1331d529"
+ integrity sha512-4kb9I7JIiBm0EJ+CsIBQ+T1t5qtmwPRbFGYFQ0t2q2qIpbFbYTHDjnjJVFB7oMBtXityEOQehblJPjqSIf3Amg==
dependencies:
"@babel/runtime" "^7.0.0"
- focus-lock "^0.7.0"
+ focus-lock "^0.10.2"
prop-types "^15.6.2"
- react-clientside-effect "^1.2.2"
- use-callback-ref "^1.2.1"
- use-sidecar "^1.0.1"
+ react-clientside-effect "^1.2.5"
+ use-callback-ref "^1.2.5"
+ use-sidecar "^1.0.5"
react-helmet@^6.1.0:
version "6.1.0"
@@ -7240,10 +7244,10 @@ react-toastify@^8.2.0:
dependencies:
clsx "^1.1.1"
-react-transition-group@4.4.1:
- version "4.4.1"
- resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.1.tgz#63868f9325a38ea5ee9535d828327f85773345c9"
- integrity sha512-Djqr7OQ2aPUiYurhPalTrVy9ddmFCCzwhqQmtN+J3+3DzLO209Fdr70QrN8Z3DsglWql6iY1lDWAfpFiBtuKGw==
+react-transition-group@^4.4.2:
+ version "4.4.2"
+ resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.2.tgz#8b59a56f09ced7b55cbd53c36768b922890d5470"
+ integrity sha512-/RNYfRAMlZwDSr6z4zNKV6xu53/e2BuaBbGhbyYIXTrmgu/bGHzmqOs7mJSJBHy9Ud+ApHx3QjrkKSp1pxvlFg==
dependencies:
"@babel/runtime" "^7.5.5"
dom-helpers "^5.0.1"
@@ -8363,12 +8367,12 @@ uri-js@^4.2.2:
dependencies:
punycode "^2.1.0"
-use-callback-ref@^1.2.1:
+use-callback-ref@^1.2.5:
version "1.2.5"
resolved "https://registry.yarnpkg.com/use-callback-ref/-/use-callback-ref-1.2.5.tgz#6115ed242cfbaed5915499c0a9842ca2912f38a5"
integrity sha512-gN3vgMISAgacF7sqsLPByqoePooY3n2emTH59Ur5d/M8eg4WTWu1xp8i8DHjohftIyEx0S08RiYxbffr4j8Peg==
-use-sidecar@^1.0.1:
+use-sidecar@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/use-sidecar/-/use-sidecar-1.0.5.tgz#ffff2a17c1df42e348624b699ba6e5c220527f2b"
integrity sha512-k9jnrjYNwN6xYLj1iaGhonDghfvmeTmYjAiGvOr7clwKfPjMXJf4/HOr7oT5tJwYafgp2tG2l3eZEOfoELiMcA==