Remove windmill

This commit is contained in:
Fergal Moran
2022-04-11 17:10:49 +01:00
parent d3ff410ace
commit 565f00c8d3
22 changed files with 1031 additions and 99 deletions

View File

@@ -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",

View File

@@ -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"
>
<MenuIcon className="w-6 h-6" aria-hidden="true" />
<AiOutlineMenu className="w-6 h-6" aria-hidden="true" />
</button>
<div className="flex justify-center flex-1 lg:mr-32">
<div className="relative w-full max-w-xl mr-6 focus-within:text-purple-500">
<div className="absolute inset-y-0 flex items-center pl-2">
<SearchIcon className="w-4 h-4" aria-hidden="true" />
<BsSearch className="w-4 h-4" aria-hidden="true" />
</div>
<Input
css=""
className="h-8 pl-8 text-gray-700"
placeholder="Search for channels"
aria-label="Search"
@@ -85,9 +73,9 @@ const Header = () => {
aria-label="Toggle color mode"
>
{theme === "dark" ? (
<SunIcon className="w-5 h-5" aria-hidden="true" />
<BsFillSunFill className="w-5 h-5" aria-hidden="true" />
) : (
<MoonIcon className="w-5 h-5" aria-hidden="true" />
<BsFillMoonStarsFill className="w-5 h-5" aria-hidden="true" />
)}
</button>
</li>
@@ -99,7 +87,7 @@ const Header = () => {
aria-label="Notifications"
aria-haspopup="true"
>
<BellIcon className="w-5 h-5" aria-hidden="true" />
<AiOutlineBell className="w-5 h-5" aria-hidden="true" />
{/* <!-- Notification badge --> */}
<span
aria-hidden="true"
@@ -146,21 +134,15 @@ const Header = () => {
onClose={() => setIsProfileMenuOpen(false)}
>
<DropdownItem tag="a" href="#">
<OutlinePersonIcon
className="w-4 h-4 mr-3"
aria-hidden="true"
/>
<IoMdPerson className="w-4 h-4 mr-3" aria-hidden="true" />
<span>Profile</span>
</DropdownItem>
<DropdownItem tag="a" href="#">
<OutlineCogIcon className="w-4 h-4 mr-3" aria-hidden="true" />
<BiCog className="w-4 h-4 mr-3" aria-hidden="true" />
<span>Settings</span>
</DropdownItem>
<DropdownItem onClick={() => alert("Log out!")}>
<OutlineLogoutIcon
className="w-4 h-4 mr-3"
aria-hidden="true"
/>
<BiLogOutCircle className="w-4 h-4 mr-3" aria-hidden="true" />
<span>Log out</span>
</DropdownItem>
</Dropdown>

View File

@@ -0,0 +1,43 @@
import React from "react";
import classNames from "classnames";
import { defaultTheme } from "../../constants";
export interface AvatarProps extends React.HTMLAttributes<HTMLDivElement> {
size?: "large" | "regular" | "small";
alt?: string;
src: string;
}
const Avatar = React.forwardRef<HTMLDivElement, AvatarProps>(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 (
<div className={cls} ref={ref} {...other}>
<img
className="object-cover w-full h-full rounded-full"
src={src}
alt={alt}
loading="lazy"
/>
<div
className="absolute inset-0 rounded-full shadow-inner"
aria-hidden="true"
></div>
</div>
);
});
export default Avatar;

View File

@@ -0,0 +1,34 @@
import React, { useContext } from "react";
import classNames from "classnames";
import { defaultTheme } from "../../constants";
export interface BadgeProps extends React.HTMLAttributes<HTMLSpanElement> {
type?: "success" | "danger" | "warning" | "neutral" | "primary";
}
const Badge = React.forwardRef<HTMLSpanElement, BadgeProps>(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 (
<span className={cls} ref={ref} {...other}>
{children}
</span>
);
});
export default Badge;

View File

@@ -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<HTMLButtonElement> {
tag?: "button";
type?: "button" | "submit" | "reset";
}
export interface ButtonAsAnchorProps
extends Props,
React.AnchorHTMLAttributes<HTMLAnchorElement> {
tag: "a";
}
export interface ButtonAsOtherProps
extends Props,
React.AnchorHTMLAttributes<HTMLAnchorElement> {
tag: string;
}
export type ButtonProps =
| ButtonAsButtonProps
| ButtonAsAnchorProps
| ButtonAsOtherProps;
type Ref = ReactNode | HTMLElement | string;
const Button = React.forwardRef<Ref, ButtonProps>(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;

View File

@@ -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<HTMLUListElement> {
/**
* 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<HTMLDivElement, DropdownProps>(
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<HTMLUListElement>(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 (
<Transition
show={isOpen}
leave="transition ease-out duration-150"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div ref={ref}>
<FocusLock returnFocus>
<ul
className={cls}
ref={dropdownRef}
aria-label="submenu"
{...other}
>
{children}
</ul>
</FocusLock>
</div>
</Transition>
);
}
);
type Ref = typeof Button;
const DropdownItem = React.forwardRef<Ref, ButtonProps>(function DropdownItem(
props,
ref
) {
// Note: className is passed to the inner Button
const { children, ...other } = props;
const { dropdownItem } = defaultTheme;
const baseStyle = dropdownItem.base;
return (
<li className={baseStyle}>
<Button layout="__dropdownItem" ref={ref} {...other}>
{children}
</Button>
</li>
);
});
export { Dropdown, DropdownItem };

View File

@@ -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 };

View File

@@ -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<HTMLInputElement, InputProps>(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 (
<input
className={cls}
type={type}
ref={ref}
disabled={disabled}
{...other}
/>
);
});
export default Input;

View File

@@ -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,
};

View File

@@ -0,0 +1,24 @@
import React from "react";
import classNames from "classnames";
import { defaultTheme } from "../../../constants";
interface Props extends React.HTMLAttributes<HTMLTableSectionElement> {}
const TableBody = React.forwardRef<HTMLTableSectionElement, Props>(
function TableBody(props, ref) {
const { className, children, ...other } = props;
const { tableBody } = defaultTheme;
const baseStyle = tableBody.base;
const cls = classNames(baseStyle, className);
return (
<tbody className={cls} ref={ref} {...other}>
{children}
</tbody>
);
}
);
export default TableBody;

View File

@@ -0,0 +1,25 @@
import React from "react";
import classNames from "classnames";
import {defaultTheme} from '../../../constants';
interface Props extends React.TdHTMLAttributes<HTMLTableCellElement> {}
const TableCell = React.forwardRef<HTMLTableCellElement, Props>(
function TableCell(props, ref) {
const { className, children, ...other } = props;
const { tableCell } = defaultTheme;
const baseStyle = tableCell.base;
const cls = classNames(baseStyle, className);
return (
<td className={cls} ref={ref} {...other}>
{children}
</td>
);
}
);
export default TableCell;

View File

@@ -0,0 +1,23 @@
import React from "react";
import classNames from "classnames";
import { defaultTheme } from "../../../constants";
interface Props extends React.HTMLAttributes<HTMLDivElement> {}
const TableContainer = React.forwardRef<HTMLDivElement, Props>(
function TableContainer(props, ref) {
const { className, children, ...other } = props;
const { tableContainer } = defaultTheme;
const baseStyle = tableContainer.base;
const cls = classNames(baseStyle, className);
return (
<div className={cls} ref={ref} {...other}>
{children}
</div>
);
}
);
export default TableContainer;

View File

@@ -0,0 +1,24 @@
import React from "react";
import classNames from "classnames";
import { defaultTheme } from "../../../constants";
interface Props extends React.HTMLAttributes<HTMLDivElement> {}
const TableFooter = React.forwardRef<HTMLDivElement, Props>(
function TableFooter(props, ref) {
const { className, children, ...other } = props;
const { tableFooter } = defaultTheme;
const baseStyle = tableFooter.base;
const cls = classNames(baseStyle, className);
return (
<div className={cls} ref={ref} {...other}>
{children}
</div>
);
}
);
export default TableFooter;

View File

@@ -0,0 +1,24 @@
import React from "react";
import classNames from "classnames";
import { defaultTheme } from "../../../constants";
interface Props extends React.HTMLAttributes<HTMLTableSectionElement> {}
const TableHeader = React.forwardRef<HTMLTableSectionElement, Props>(
function TableHeader(props, ref) {
const { className, children, ...other } = props;
const { tableHeader } = defaultTheme;
const baseStyle = tableHeader.base;
const cls = classNames(baseStyle, className);
return (
<thead className={cls} ref={ref} {...other}>
{children}
</thead>
);
}
);
export default TableHeader;

View File

@@ -0,0 +1,25 @@
import React from "react";
import classNames from "classnames";
import { defaultTheme } from "../../../constants";
interface Props extends React.HTMLAttributes<HTMLTableRowElement> {}
const TableRow = React.forwardRef<HTMLTableRowElement, Props>(function TableRow(
props,
ref
) {
const { className, children, ...other } = props;
const { tableRow } = defaultTheme;
const baseStyle = tableRow.base;
const cls = classNames(baseStyle, className);
return (
<tr className={cls} ref={ref} {...other}>
{children}
</tr>
);
});
export default TableRow;

View File

@@ -0,0 +1,20 @@
import React from "react";
export interface TableProps
extends React.TableHTMLAttributes<HTMLTableElement> {}
const Table = React.forwardRef<HTMLTableElement, TableProps>(function Table(
props,
ref
) {
const { children, ...other } = props;
return (
<div className="w-full overflow-x-auto">
<table className="w-full whitespace-nowrap" ref={ref} {...other}>
{children}
</table>
</div>
);
});
export default Table;

View File

@@ -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<TransitionContext>({
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<TransitionProps> = 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 (
<ReactCSSTransition
appear={appear}
unmountOnExit
in={show}
addEndListener={(node: HTMLElement, done: any) => {
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}
</ReactCSSTransition>
);
};
const Transition: React.FC<TransitionProps> = function Transition({
show,
appear,
...rest
}) {
const { parent } = useContext(transitionContext);
const isInitialRender = useIsInitialRender();
const isChild = show === undefined;
if (isChild) {
return (
<CSSTransition appear={parent.appear} show={parent.show} {...rest} />
);
} else
return (
<transitionContext.Provider
value={{
parent: {
show,
isInitialRender,
appear,
},
}}
>
<CSSTransition appear={appear} show={show} {...rest} />
</transitionContext.Provider>
);
};
export default Transition;

View File

@@ -0,0 +1,3 @@
import defaultTheme from "./theme";
export { defaultTheme };

View File

@@ -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 <li> that lives inside the Dropdown <ul>
// 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;

View File

@@ -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 = () => {
<TableContainer className="mt-5 mb-8">
<Table>
<TableHeader>
<tr>
<TableRow>
<TableCell>Channel</TableCell>
<TableCell>Type</TableCell>
<TableCell></TableCell>
</tr>
</TableRow>
</TableHeader>
<TableBody>
{streams.map((stream: Stream) => [

View File

@@ -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;

View File

@@ -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==