mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-05-03 10:00:07 -04:00
feat: introduce font picker (#8012)
Co-authored-by: dwelle <5153846+dwelle@users.noreply.github.com>
This commit is contained in:
parent
4c5408263c
commit
62228e0bbb
120 changed files with 3390 additions and 1106 deletions
|
@ -4,7 +4,7 @@
|
|||
.dropdown-menu {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
margin-top: 0.25rem;
|
||||
margin-top: 0.5rem;
|
||||
|
||||
&--mobile {
|
||||
left: 0;
|
||||
|
@ -35,21 +35,69 @@
|
|||
|
||||
.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-weight: 400;
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
&.manual-hover {
|
||||
// disable built-in hover due to keyboard navigation
|
||||
.dropdown-menu-item {
|
||||
&:hover {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
&--hovered {
|
||||
background-color: var(--button-hover-bg) !important;
|
||||
}
|
||||
|
||||
&--selected {
|
||||
background-color: var(--color-primary-light) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.fonts {
|
||||
margin-top: 1rem;
|
||||
// display max 7 items per list, where each has 2rem (2.25) height and 1px margin top & bottom
|
||||
// count in 2 groups, where each allocates 1.3*0.75rem font-size and 0.5rem margin bottom, plus one extra 1rem margin top
|
||||
max-height: calc(7 * (2rem + 2px) + 2 * (0.5rem + 1.3 * 0.75rem) + 1rem);
|
||||
|
||||
@media screen and (min-width: 1921px) {
|
||||
max-height: calc(
|
||||
7 * (2.25rem + 2px) + 2 * (0.5rem + 1.3 * 0.75rem) + 1rem
|
||||
);
|
||||
}
|
||||
|
||||
.dropdown-menu-item-base {
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.dropdown-menu-group:not(:first-child) {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.dropdown-menu-group-title {
|
||||
font-size: 0.75rem;
|
||||
text-align: left;
|
||||
font-weight: 400;
|
||||
margin: 0 0 0.5rem;
|
||||
line-height: 1.3;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-menu-item {
|
||||
height: 2rem;
|
||||
margin: 1px;
|
||||
padding: 0 0.5rem;
|
||||
width: calc(100% - 2px);
|
||||
background-color: transparent;
|
||||
border: 1px solid transparent;
|
||||
align-items: center;
|
||||
height: 2rem;
|
||||
cursor: pointer;
|
||||
border-radius: var(--border-radius-md);
|
||||
|
||||
|
@ -57,11 +105,6 @@
|
|||
height: 2.25rem;
|
||||
}
|
||||
|
||||
&--selected {
|
||||
background: var(--color-primary-light);
|
||||
--icon-fill-color: var(--color-primary-darker);
|
||||
}
|
||||
|
||||
&__text {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
@ -83,6 +126,11 @@
|
|||
}
|
||||
}
|
||||
|
||||
&--selected {
|
||||
background: var(--color-primary-light);
|
||||
--icon-fill-color: var(--color-primary-darker);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: var(--button-hover-bg);
|
||||
text-decoration: none;
|
||||
|
|
|
@ -1,37 +1,62 @@
|
|||
import React from "react";
|
||||
import React, { useEffect, useRef } from "react";
|
||||
import {
|
||||
getDropdownMenuItemClassName,
|
||||
useHandleDropdownMenuItemClick,
|
||||
} from "./common";
|
||||
import MenuItemContent from "./DropdownMenuItemContent";
|
||||
import { useExcalidrawAppState } from "../App";
|
||||
import { THEME } from "../../constants";
|
||||
import type { ValueOf } from "../../utility-types";
|
||||
|
||||
const DropdownMenuItem = ({
|
||||
icon,
|
||||
onSelect,
|
||||
value,
|
||||
order,
|
||||
children,
|
||||
shortcut,
|
||||
className,
|
||||
hovered,
|
||||
selected,
|
||||
textStyle,
|
||||
onSelect,
|
||||
onClick,
|
||||
...rest
|
||||
}: {
|
||||
icon?: JSX.Element;
|
||||
onSelect: (event: Event) => void;
|
||||
value?: string | number | undefined;
|
||||
order?: number;
|
||||
onSelect?: (event: Event) => void;
|
||||
children: React.ReactNode;
|
||||
shortcut?: string;
|
||||
hovered?: boolean;
|
||||
selected?: boolean;
|
||||
textStyle?: React.CSSProperties;
|
||||
className?: string;
|
||||
} & Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "onSelect">) => {
|
||||
const handleClick = useHandleDropdownMenuItemClick(rest.onClick, onSelect);
|
||||
const handleClick = useHandleDropdownMenuItemClick(onClick, onSelect);
|
||||
const ref = useRef<HTMLButtonElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (hovered) {
|
||||
if (order === 0) {
|
||||
// scroll into the first item differently, so it's visible what is above (i.e. group title)
|
||||
ref.current?.scrollIntoView({ block: "end" });
|
||||
} else {
|
||||
ref.current?.scrollIntoView({ block: "nearest" });
|
||||
}
|
||||
}
|
||||
}, [hovered, order]);
|
||||
|
||||
return (
|
||||
<button
|
||||
{...rest}
|
||||
ref={ref}
|
||||
value={value}
|
||||
onClick={handleClick}
|
||||
type="button"
|
||||
className={getDropdownMenuItemClassName(className, selected)}
|
||||
className={getDropdownMenuItemClassName(className, selected, hovered)}
|
||||
title={rest.title ?? rest["aria-label"]}
|
||||
>
|
||||
<MenuItemContent icon={icon} shortcut={shortcut}>
|
||||
<MenuItemContent textStyle={textStyle} icon={icon} shortcut={shortcut}>
|
||||
{children}
|
||||
</MenuItemContent>
|
||||
</button>
|
||||
|
@ -39,24 +64,53 @@ const DropdownMenuItem = ({
|
|||
};
|
||||
DropdownMenuItem.displayName = "DropdownMenuItem";
|
||||
|
||||
export const DropDownMenuItemBadgeType = {
|
||||
GREEN: "green",
|
||||
RED: "red",
|
||||
BLUE: "blue",
|
||||
} as const;
|
||||
|
||||
export const DropDownMenuItemBadge = ({
|
||||
type = DropDownMenuItemBadgeType.BLUE,
|
||||
children,
|
||||
}: {
|
||||
type?: ValueOf<typeof DropDownMenuItemBadgeType>;
|
||||
children: React.ReactNode;
|
||||
}) => {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
display: "inline-flex",
|
||||
marginLeft: "auto",
|
||||
padding: "2px 4px",
|
||||
const { theme } = useExcalidrawAppState();
|
||||
const style = {
|
||||
display: "inline-flex",
|
||||
marginLeft: "auto",
|
||||
padding: "2px 4px",
|
||||
borderRadius: 6,
|
||||
fontSize: 9,
|
||||
fontFamily: "Cascadia, monospace",
|
||||
border: theme === THEME.LIGHT ? "1.5px solid white" : "none",
|
||||
};
|
||||
|
||||
switch (type) {
|
||||
case DropDownMenuItemBadgeType.GREEN:
|
||||
Object.assign(style, {
|
||||
backgroundColor: "var(--background-color-badge)",
|
||||
color: "var(--color-badge)",
|
||||
});
|
||||
break;
|
||||
case DropDownMenuItemBadgeType.RED:
|
||||
Object.assign(style, {
|
||||
backgroundColor: "pink",
|
||||
color: "darkred",
|
||||
});
|
||||
break;
|
||||
case DropDownMenuItemBadgeType.BLUE:
|
||||
default:
|
||||
Object.assign(style, {
|
||||
background: "var(--color-promo)",
|
||||
color: "var(--color-surface-lowest)",
|
||||
borderRadius: 6,
|
||||
fontSize: 9,
|
||||
fontFamily: "Cascadia, monospace",
|
||||
}}
|
||||
>
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="DropDownMenuItemBadge" style={style}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -1,19 +1,23 @@
|
|||
import { useDevice } from "../App";
|
||||
|
||||
const MenuItemContent = ({
|
||||
textStyle,
|
||||
icon,
|
||||
shortcut,
|
||||
children,
|
||||
}: {
|
||||
icon?: JSX.Element;
|
||||
shortcut?: string;
|
||||
textStyle?: React.CSSProperties;
|
||||
children: React.ReactNode;
|
||||
}) => {
|
||||
const device = useDevice();
|
||||
return (
|
||||
<>
|
||||
<div className="dropdown-menu-item__icon">{icon}</div>
|
||||
<div className="dropdown-menu-item__text">{children}</div>
|
||||
{icon && <div className="dropdown-menu-item__icon">{icon}</div>}
|
||||
<div style={textStyle} className="dropdown-menu-item__text">
|
||||
{children}
|
||||
</div>
|
||||
{shortcut && !device.editor.isMobile && (
|
||||
<div className="dropdown-menu-item__shortcut">{shortcut}</div>
|
||||
)}
|
||||
|
|
|
@ -9,9 +9,11 @@ export const DropdownMenuContentPropsContext = React.createContext<{
|
|||
export const getDropdownMenuItemClassName = (
|
||||
className = "",
|
||||
selected = false,
|
||||
hovered = false,
|
||||
) => {
|
||||
return `dropdown-menu-item dropdown-menu-item-base ${className} ${
|
||||
selected ? "dropdown-menu-item--selected" : ""
|
||||
return `dropdown-menu-item dropdown-menu-item-base ${className}
|
||||
${selected ? "dropdown-menu-item--selected" : ""} ${
|
||||
hovered ? "dropdown-menu-item--hovered" : ""
|
||||
}`.trim();
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue