build: decouple package deps and introduce yarn workspaces (#7415)

* feat: decouple package deps and introduce yarn workspaces

* update root directory

* fix

* fix scripts

* fix lint

* update path in scripts

* remove yarn.lock files from packages

* ignore workspace

* dummy

* dummy

* remove comment check

* revert workflow changes

* ignore ws when installing gh actions

* remove log

* update path

* fix

* fix typo
This commit is contained in:
Aakansha Doshi 2023-12-12 11:32:51 +05:30 committed by GitHub
parent b7d7ccc929
commit d6cd8b78f1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
567 changed files with 5066 additions and 8648 deletions

View file

@ -0,0 +1,148 @@
@import "../../css/variables.module";
.excalidraw {
.dropdown-menu {
position: absolute;
top: 100%;
margin-top: 0.25rem;
&--mobile {
left: 0;
width: 100%;
row-gap: 0.75rem;
.dropdown-menu-container {
padding: 8px 8px;
box-sizing: border-box;
// background-color: var(--island-bg-color);
box-shadow: var(--shadow-island);
border-radius: var(--border-radius-lg);
position: relative;
transition: box-shadow 0.5s ease-in-out;
&.zen-mode {
box-shadow: none;
}
}
}
.dropdown-menu-container {
background-color: var(--island-bg-color);
max-height: calc(100vh - 150px);
overflow-y: auto;
--gap: 2;
}
.dropdown-menu-item-base {
display: flex;
padding: 0 0.625rem;
column-gap: 0.625rem;
font-size: 0.875rem;
color: var(--color-on-surface);
width: 100%;
box-sizing: border-box;
font-weight: normal;
font-family: inherit;
}
.dropdown-menu-item {
background-color: transparent;
border: 1px solid transparent;
align-items: center;
height: 2rem;
cursor: pointer;
border-radius: var(--border-radius-md);
@media screen and (min-width: 1921px) {
height: 2.25rem;
}
&--selected {
background: var(--color-primary-light);
--icon-fill-color: var(--color-primary-darker);
}
&__text {
display: flex;
align-items: center;
width: 100%;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
gap: 0.75rem;
}
&__shortcut {
margin-inline-start: auto;
opacity: 0.5;
}
&:hover {
background-color: var(--button-hover-bg);
text-decoration: none;
}
&:active {
background-color: var(--button-hover-bg);
border-color: var(--color-brand-active);
}
svg {
width: 1rem;
height: 1rem;
display: block;
}
}
.dropdown-menu-item-custom {
margin-top: 0.5rem;
}
.dropdown-menu-group-title {
font-size: 14px;
text-align: left;
margin: 10px 0;
font-weight: 500;
}
}
.dropdown-menu-button {
@include outlineButtonStyles;
width: var(--lg-button-size);
height: var(--lg-button-size);
--background: var(--color-surface-mid);
background-color: var(--background);
@at-root .excalidraw.theme--dark#{&} {
--background: var(--color-surface-high);
&:hover {
--background: #363541;
}
}
&:hover {
--background: var(--color-surface-high);
background-color: var(--background);
text-decoration: none;
}
&:active {
border-color: var(--color-primary);
}
svg {
width: var(--lg-icon-size);
height: var(--lg-icon-size);
}
&--mobile {
border: none;
margin: 0;
padding: 0;
width: var(--default-button-size);
height: var(--default-button-size);
}
}
}

View file

@ -0,0 +1,43 @@
import React from "react";
import DropdownMenuTrigger from "./DropdownMenuTrigger";
import DropdownMenuItem from "./DropdownMenuItem";
import MenuSeparator from "./DropdownMenuSeparator";
import DropdownMenuGroup from "./DropdownMenuGroup";
import DropdownMenuContent from "./DropdownMenuContent";
import DropdownMenuItemLink from "./DropdownMenuItemLink";
import DropdownMenuItemCustom from "./DropdownMenuItemCustom";
import {
getMenuContentComponent,
getMenuTriggerComponent,
} from "./dropdownMenuUtils";
import "./DropdownMenu.scss";
const DropdownMenu = ({
children,
open,
}: {
children?: React.ReactNode;
open: boolean;
}) => {
const MenuTriggerComp = getMenuTriggerComponent(children);
const MenuContentComp = getMenuContentComponent(children);
return (
<>
{MenuTriggerComp}
{open && MenuContentComp}
</>
);
};
DropdownMenu.Trigger = DropdownMenuTrigger;
DropdownMenu.Content = DropdownMenuContent;
DropdownMenu.Item = DropdownMenuItem;
DropdownMenu.ItemLink = DropdownMenuItemLink;
DropdownMenu.ItemCustom = DropdownMenuItemCustom;
DropdownMenu.Group = DropdownMenuGroup;
DropdownMenu.Separator = MenuSeparator;
export default DropdownMenu;
DropdownMenu.displayName = "DropdownMenu";

View file

@ -0,0 +1,63 @@
import { Island } from "../Island";
import { useDevice } from "../App";
import clsx from "clsx";
import Stack from "../Stack";
import React, { useRef } from "react";
import { DropdownMenuContentPropsContext } from "./common";
import { useOutsideClick } from "../../hooks/useOutsideClick";
const MenuContent = ({
children,
onClickOutside,
className = "",
onSelect,
style,
}: {
children?: React.ReactNode;
onClickOutside?: () => void;
className?: string;
/**
* Called when any menu item is selected (clicked on).
*/
onSelect?: (event: Event) => void;
style?: React.CSSProperties;
}) => {
const device = useDevice();
const menuRef = useRef<HTMLDivElement>(null);
useOutsideClick(menuRef, () => {
onClickOutside?.();
});
const classNames = clsx(`dropdown-menu ${className}`, {
"dropdown-menu--mobile": device.editor.isMobile,
}).trim();
return (
<DropdownMenuContentPropsContext.Provider value={{ onSelect }}>
<div
ref={menuRef}
className={classNames}
style={style}
data-testid="dropdown-menu"
>
{/* the zIndex ensures this menu has higher stacking order,
see https://github.com/excalidraw/excalidraw/pull/1445 */}
{device.editor.isMobile ? (
<Stack.Col className="dropdown-menu-container">{children}</Stack.Col>
) : (
<Island
className="dropdown-menu-container"
padding={2}
style={{ zIndex: 2 }}
>
{children}
</Island>
)}
</div>
</DropdownMenuContentPropsContext.Provider>
);
};
MenuContent.displayName = "DropdownMenuContent";
export default MenuContent;

View file

@ -0,0 +1,23 @@
import React from "react";
const MenuGroup = ({
children,
className = "",
style,
title,
}: {
children: React.ReactNode;
className?: string;
style?: React.CSSProperties;
title?: string;
}) => {
return (
<div className={`dropdown-menu-group ${className}`} style={style}>
{title && <p className="dropdown-menu-group-title">{title}</p>}
{children}
</div>
);
};
export default MenuGroup;
MenuGroup.displayName = "DropdownMenuGroup";

View file

@ -0,0 +1,68 @@
import React from "react";
import {
getDropdownMenuItemClassName,
useHandleDropdownMenuItemClick,
} from "./common";
import MenuItemContent from "./DropdownMenuItemContent";
const DropdownMenuItem = ({
icon,
onSelect,
children,
shortcut,
className,
selected,
...rest
}: {
icon?: JSX.Element;
onSelect: (event: Event) => void;
children: React.ReactNode;
shortcut?: string;
selected?: boolean;
className?: string;
} & Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "onSelect">) => {
const handleClick = useHandleDropdownMenuItemClick(rest.onClick, onSelect);
return (
<button
{...rest}
onClick={handleClick}
type="button"
className={getDropdownMenuItemClassName(className, selected)}
title={rest.title ?? rest["aria-label"]}
>
<MenuItemContent icon={icon} shortcut={shortcut}>
{children}
</MenuItemContent>
</button>
);
};
DropdownMenuItem.displayName = "DropdownMenuItem";
export const DropDownMenuItemBadge = ({
children,
}: {
children: React.ReactNode;
}) => {
return (
<div
style={{
display: "inline-flex",
marginLeft: "auto",
padding: "2px 4px",
background: "pink",
borderRadius: 6,
fontSize: 9,
color: "black",
fontFamily: "Cascadia, monospace",
}}
>
{children}
</div>
);
};
DropDownMenuItemBadge.displayName = "DropdownMenuItemBadge";
DropdownMenuItem.Badge = DropDownMenuItemBadge;
export default DropdownMenuItem;

View file

@ -0,0 +1,23 @@
import { useDevice } from "../App";
const MenuItemContent = ({
icon,
shortcut,
children,
}: {
icon?: JSX.Element;
shortcut?: string;
children: React.ReactNode;
}) => {
const device = useDevice();
return (
<>
<div className="dropdown-menu-item__icon">{icon}</div>
<div className="dropdown-menu-item__text">{children}</div>
{shortcut && !device.editor.isMobile && (
<div className="dropdown-menu-item__shortcut">{shortcut}</div>
)}
</>
);
};
export default MenuItemContent;

View file

@ -0,0 +1,25 @@
import React from "react";
const DropdownMenuItemCustom = ({
children,
className = "",
selected,
...rest
}: {
children: React.ReactNode;
className?: string;
selected?: boolean;
} & React.HTMLAttributes<HTMLDivElement>) => {
return (
<div
{...rest}
className={`dropdown-menu-item-base dropdown-menu-item-custom ${className} ${
selected ? `dropdown-menu-item--selected` : ``
}`.trim()}
>
{children}
</div>
);
};
export default DropdownMenuItemCustom;

View file

@ -0,0 +1,46 @@
import MenuItemContent from "./DropdownMenuItemContent";
import React from "react";
import {
getDropdownMenuItemClassName,
useHandleDropdownMenuItemClick,
} from "./common";
const DropdownMenuItemLink = ({
icon,
shortcut,
href,
children,
onSelect,
className = "",
selected,
...rest
}: {
href: string;
icon?: JSX.Element;
children: React.ReactNode;
shortcut?: string;
className?: string;
selected?: boolean;
onSelect?: (event: Event) => void;
} & React.AnchorHTMLAttributes<HTMLAnchorElement>) => {
const handleClick = useHandleDropdownMenuItemClick(rest.onClick, onSelect);
return (
<a
{...rest}
href={href}
target="_blank"
rel="noreferrer"
className={getDropdownMenuItemClassName(className, selected)}
title={rest.title ?? rest["aria-label"]}
onClick={handleClick}
>
<MenuItemContent icon={icon} shortcut={shortcut}>
{children}
</MenuItemContent>
</a>
);
};
export default DropdownMenuItemLink;
DropdownMenuItemLink.displayName = "DropdownMenuItemLink";

View file

@ -0,0 +1,14 @@
import React from "react";
const MenuSeparator = () => (
<div
style={{
height: "1px",
backgroundColor: "var(--default-border-color)",
margin: ".5rem 0",
}}
/>
);
export default MenuSeparator;
MenuSeparator.displayName = "DropdownMenuSeparator";

View file

@ -0,0 +1,40 @@
import clsx from "clsx";
import { useDevice } from "../App";
const MenuTrigger = ({
className = "",
children,
onToggle,
title,
...rest
}: {
className?: string;
children: React.ReactNode;
onToggle: () => void;
title?: string;
} & Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "onSelect">) => {
const device = useDevice();
const classNames = clsx(
`dropdown-menu-button ${className}`,
"zen-mode-transition",
{
"dropdown-menu-button--mobile": device.editor.isMobile,
},
).trim();
return (
<button
data-prevent-outside-click
className={classNames}
onClick={onToggle}
type="button"
data-testid="dropdown-menu-button"
title={title}
{...rest}
>
{children}
</button>
);
};
export default MenuTrigger;
MenuTrigger.displayName = "DropdownMenuTrigger";

View file

@ -0,0 +1,36 @@
import React, { useContext } from "react";
import { EVENT } from "../../constants";
import { composeEventHandlers } from "../../utils";
export const DropdownMenuContentPropsContext = React.createContext<{
onSelect?: (event: Event) => void;
}>({});
export const getDropdownMenuItemClassName = (
className = "",
selected = false,
) => {
return `dropdown-menu-item dropdown-menu-item-base ${className} ${
selected ? "dropdown-menu-item--selected" : ""
}`.trim();
};
export const useHandleDropdownMenuItemClick = (
origOnClick:
| React.MouseEventHandler<HTMLAnchorElement | HTMLButtonElement>
| undefined,
onSelect: ((event: Event) => void) | undefined,
) => {
const DropdownMenuContentProps = useContext(DropdownMenuContentPropsContext);
return composeEventHandlers(origOnClick, (event) => {
const itemSelectEvent = new CustomEvent(EVENT.MENU_ITEM_SELECT, {
bubbles: true,
cancelable: true,
});
onSelect?.(itemSelectEvent);
if (!itemSelectEvent.defaultPrevented) {
DropdownMenuContentProps.onSelect?.(itemSelectEvent);
}
});
};

View file

@ -0,0 +1,35 @@
import React from "react";
export const getMenuTriggerComponent = (children: React.ReactNode) => {
const comp = React.Children.toArray(children).find(
(child) =>
React.isValidElement(child) &&
typeof child.type !== "string" &&
//@ts-ignore
child?.type.displayName &&
//@ts-ignore
child.type.displayName === "DropdownMenuTrigger",
);
if (!comp) {
return null;
}
//@ts-ignore
return comp;
};
export const getMenuContentComponent = (children: React.ReactNode) => {
const comp = React.Children.toArray(children).find(
(child) =>
React.isValidElement(child) &&
typeof child.type !== "string" &&
//@ts-ignore
child?.type.displayName &&
//@ts-ignore
child.type.displayName === "DropdownMenuContent",
);
if (!comp) {
return null;
}
//@ts-ignore
return comp;
};