Add and use clsx (classnames alternative) (#2249)
Co-authored-by: David Luzar <luzar.david@gmail.com>
This commit is contained in:
parent
1484c5a63b
commit
b50c54f855
5
package-lock.json
generated
5
package-lock.json
generated
@ -5399,6 +5399,11 @@
|
||||
"mimic-response": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"clsx": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz",
|
||||
"integrity": "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA=="
|
||||
},
|
||||
"co": {
|
||||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
|
||||
|
@ -29,6 +29,7 @@
|
||||
"@types/react-dom": "16.9.8",
|
||||
"@types/socket.io-client": "1.4.34",
|
||||
"browser-nativefs": "0.11.0",
|
||||
"clsx": "1.1.1",
|
||||
"firebase": "7.23.0",
|
||||
"i18next-browser-languagedetector": "6.0.1",
|
||||
"lodash.throttle": "4.1.1",
|
||||
|
@ -9,7 +9,7 @@ type AvatarProps = {
|
||||
};
|
||||
|
||||
export const Avatar = ({ children, color, onClick }: AvatarProps) => (
|
||||
<div className={`Avatar`} style={{ background: color }} onClick={onClick}>
|
||||
<div className="Avatar" style={{ background: color }} onClick={onClick}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
@ -1,4 +1,5 @@
|
||||
import React from "react";
|
||||
import clsx from "clsx";
|
||||
|
||||
export const ButtonSelect = <T extends Object>({
|
||||
options,
|
||||
@ -15,7 +16,7 @@ export const ButtonSelect = <T extends Object>({
|
||||
{options.map((option) => (
|
||||
<label
|
||||
key={option.text}
|
||||
className={value === option.value ? "active" : ""}
|
||||
className={clsx({ active: value === option.value })}
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
|
@ -1,6 +1,7 @@
|
||||
import React from "react";
|
||||
import { Popover } from "./Popover";
|
||||
import { render, unmountComponentAtNode } from "react-dom";
|
||||
import clsx from "clsx";
|
||||
import { Popover } from "./Popover";
|
||||
|
||||
import "./ContextMenu.scss";
|
||||
|
||||
@ -20,11 +21,13 @@ const ContextMenu = ({ options, onCloseRequest, top, left }: Props) => {
|
||||
const isDarkTheme = !!document
|
||||
.querySelector(".excalidraw")
|
||||
?.classList.contains("Appearance_dark");
|
||||
const wrapperClasses = `excalidraw ${
|
||||
isDarkTheme ? "Appearance_dark Appearance_dark-background-none" : ""
|
||||
}`;
|
||||
|
||||
return (
|
||||
<div className={wrapperClasses}>
|
||||
<div
|
||||
className={clsx("excalidraw", {
|
||||
"Appearance_dark Appearance_dark-background-none": isDarkTheme,
|
||||
})}
|
||||
>
|
||||
<Popover
|
||||
onCloseRequest={onCloseRequest}
|
||||
top={top}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import React, { useEffect, useRef } from "react";
|
||||
import clsx from "clsx";
|
||||
import { Modal } from "./Modal";
|
||||
import { Island } from "./Island";
|
||||
import { t } from "../i18n";
|
||||
@ -68,7 +69,7 @@ export const Dialog = (props: {
|
||||
|
||||
return (
|
||||
<Modal
|
||||
className={`${props.className ?? ""} Dialog`}
|
||||
className={clsx("Dialog", props.className)}
|
||||
labelledBy="dialog-title"
|
||||
maxWidth={props.maxWidth}
|
||||
onCloseRequest={props.onCloseRequest}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import "./FixedSideContainer.scss";
|
||||
|
||||
import React from "react";
|
||||
import clsx from "clsx";
|
||||
|
||||
type FixedSideContainerProps = {
|
||||
children: React.ReactNode;
|
||||
@ -14,7 +15,11 @@ export const FixedSideContainer = ({
|
||||
className,
|
||||
}: FixedSideContainerProps) => (
|
||||
<div
|
||||
className={`FixedSideContainer FixedSideContainer_side_${side} ${className}`}
|
||||
className={clsx(
|
||||
"FixedSideContainer",
|
||||
`FixedSideContainer_side_${side}`,
|
||||
className,
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
|
@ -1,6 +1,7 @@
|
||||
import "./Island.scss";
|
||||
|
||||
import React from "react";
|
||||
import clsx from "clsx";
|
||||
|
||||
type IslandProps = {
|
||||
children: React.ReactNode;
|
||||
@ -12,7 +13,7 @@ type IslandProps = {
|
||||
export const Island = React.forwardRef<HTMLDivElement, IslandProps>(
|
||||
({ children, padding, className, style }, ref) => (
|
||||
<div
|
||||
className={`${className ?? ""} Island`}
|
||||
className={clsx("Island", className)}
|
||||
style={{ "--padding": padding, ...style } as React.CSSProperties}
|
||||
ref={ref}
|
||||
>
|
||||
|
@ -1,4 +1,5 @@
|
||||
import React from "react";
|
||||
import clsx from "clsx";
|
||||
import * as i18n from "../i18n";
|
||||
|
||||
export const LanguageList = ({
|
||||
@ -14,9 +15,9 @@ export const LanguageList = ({
|
||||
}) => (
|
||||
<React.Fragment>
|
||||
<select
|
||||
className={`dropdown-select dropdown-select__language${
|
||||
floating ? " dropdown-select--floating" : ""
|
||||
}`}
|
||||
className={clsx("dropdown-select dropdown-select__language", {
|
||||
"dropdown-select--floating": floating,
|
||||
})}
|
||||
onChange={({ target }) => onChange(target.value)}
|
||||
value={currentLanguage}
|
||||
aria-label={i18n.t("buttons.selectLanguage")}
|
||||
|
@ -44,6 +44,7 @@ import { ToolButton } from "./ToolButton";
|
||||
import { saveLibraryAsJSON, importLibraryFromJSON } from "../data/json";
|
||||
import { muteFSAbortError } from "../utils";
|
||||
import { BackgroundPickerAndDarkModeToggle } from "./BackgroundPickerAndDarkModeToggle";
|
||||
import clsx from "clsx";
|
||||
|
||||
interface LayerUIProps {
|
||||
actionManager: ActionManager;
|
||||
@ -294,9 +295,9 @@ const LayerUI = ({
|
||||
// TODO: Extend tooltip component and use here.
|
||||
const renderEncryptedIcon = () => (
|
||||
<a
|
||||
className={`encrypted-icon tooltip zen-mode-visibility ${
|
||||
zenModeEnabled ? "zen-mode-visibility--hidden" : ""
|
||||
}`}
|
||||
className={clsx("encrypted-icon tooltip zen-mode-visibility", {
|
||||
"zen-mode-visibility--hidden": zenModeEnabled,
|
||||
})}
|
||||
href="https://blog.excalidraw.com/end-to-end-encryption/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
@ -362,7 +363,9 @@ const LayerUI = ({
|
||||
const renderCanvasActions = () => (
|
||||
<Section
|
||||
heading="canvasActions"
|
||||
className={`zen-mode-transition ${zenModeEnabled && "transition-left"}`}
|
||||
className={clsx("zen-mode-transition", {
|
||||
"transition-left": zenModeEnabled,
|
||||
})}
|
||||
>
|
||||
{/* the zIndex ensures this menu has higher stacking order,
|
||||
see https://github.com/excalidraw/excalidraw/pull/1445 */}
|
||||
@ -399,7 +402,9 @@ const LayerUI = ({
|
||||
const renderSelectedShapeActions = () => (
|
||||
<Section
|
||||
heading="selectedShapeActions"
|
||||
className={`zen-mode-transition ${zenModeEnabled && "transition-left"}`}
|
||||
className={clsx("zen-mode-transition", {
|
||||
"transition-left": zenModeEnabled,
|
||||
})}
|
||||
>
|
||||
<Island className={CLASSES.SHAPE_ACTIONS_MENU} padding={2}>
|
||||
<SelectedShapeActions
|
||||
@ -447,7 +452,7 @@ const LayerUI = ({
|
||||
<div className="App-menu App-menu_top">
|
||||
<Stack.Col
|
||||
gap={4}
|
||||
className={zenModeEnabled && "disable-pointerEvents"}
|
||||
className={clsx({ "disable-pointerEvents": zenModeEnabled })}
|
||||
>
|
||||
{renderCanvasActions()}
|
||||
{shouldRenderSelectedShapeActions && renderSelectedShapeActions()}
|
||||
@ -456,7 +461,10 @@ const LayerUI = ({
|
||||
{(heading) => (
|
||||
<Stack.Col gap={4} align="start">
|
||||
<Stack.Row gap={1}>
|
||||
<Island padding={1} className={zenModeEnabled && "zen-mode"}>
|
||||
<Island
|
||||
padding={1}
|
||||
className={clsx({ "zen-mode": zenModeEnabled })}
|
||||
>
|
||||
<HintViewer appState={appState} elements={elements} />
|
||||
{heading}
|
||||
<Stack.Row gap={1}>
|
||||
@ -479,9 +487,9 @@ const LayerUI = ({
|
||||
)}
|
||||
</Section>
|
||||
<UserList
|
||||
className={`zen-mode-transition ${
|
||||
zenModeEnabled && "transition-right"
|
||||
}`}
|
||||
className={clsx("zen-mode-transition", {
|
||||
"transition-right": zenModeEnabled,
|
||||
})}
|
||||
>
|
||||
{Array.from(appState.collaborators)
|
||||
// Collaborator is either not initialized or is actually the current user.
|
||||
@ -503,9 +511,9 @@ const LayerUI = ({
|
||||
const renderBottomAppMenu = () => {
|
||||
return (
|
||||
<div
|
||||
className={`App-menu App-menu_bottom zen-mode-transition ${
|
||||
zenModeEnabled && "App-menu_bottom--transition-left"
|
||||
}`}
|
||||
className={clsx("App-menu App-menu_bottom zen-mode-transition", {
|
||||
"App-menu_bottom--transition-left": zenModeEnabled,
|
||||
})}
|
||||
>
|
||||
<Stack.Col gap={2}>
|
||||
<Section heading="canvasActions">
|
||||
@ -525,9 +533,9 @@ const LayerUI = ({
|
||||
const renderFooter = () => (
|
||||
<footer role="contentinfo" className="layer-ui__wrapper__footer">
|
||||
<div
|
||||
className={`zen-mode-transition ${
|
||||
zenModeEnabled && "transition-right disable-pointerEvents"
|
||||
}`}
|
||||
className={clsx("zen-mode-transition", {
|
||||
"transition-right disable-pointerEvents": zenModeEnabled,
|
||||
})}
|
||||
>
|
||||
<LanguageList
|
||||
onChange={async (lng) => {
|
||||
@ -540,9 +548,9 @@ const LayerUI = ({
|
||||
{actionManager.renderAction("toggleShortcuts")}
|
||||
</div>
|
||||
<button
|
||||
className={`disable-zen-mode ${
|
||||
zenModeEnabled && "disable-zen-mode--visible"
|
||||
}`}
|
||||
className={clsx("disable-zen-mode", {
|
||||
"disable-zen-mode--visible": zenModeEnabled,
|
||||
})}
|
||||
onClick={toggleZenMode}
|
||||
>
|
||||
{t("buttons.exitZenMode")}
|
||||
@ -594,9 +602,12 @@ const LayerUI = ({
|
||||
{renderBottomAppMenu()}
|
||||
{
|
||||
<aside
|
||||
className={`layer-ui__wrapper__github-corner zen-mode-transition ${
|
||||
zenModeEnabled && "transition-right"
|
||||
}`}
|
||||
className={clsx(
|
||||
"layer-ui__wrapper__github-corner zen-mode-transition",
|
||||
{
|
||||
"transition-right": zenModeEnabled,
|
||||
},
|
||||
)}
|
||||
>
|
||||
<GitHubCorner appearance={appState.appearance} />
|
||||
</aside>
|
||||
|
@ -1,4 +1,5 @@
|
||||
import React, { useRef, useEffect, useState } from "react";
|
||||
import clsx from "clsx";
|
||||
import { exportToSvg } from "../scene/export";
|
||||
import { close } from "../components/icons";
|
||||
|
||||
@ -63,16 +64,16 @@ export const LibraryUnit = ({
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`library-unit ${
|
||||
elements || pendingElements ? "library-unit__active" : ""
|
||||
}`}
|
||||
className={clsx("library-unit", {
|
||||
"library-unit__active": elements || pendingElements,
|
||||
})}
|
||||
onMouseEnter={() => setIsHovered(true)}
|
||||
onMouseLeave={() => setIsHovered(false)}
|
||||
>
|
||||
<div
|
||||
className={`library-unit__dragger ${
|
||||
!!pendingElements ? "library-unit__pulse" : ""
|
||||
}`}
|
||||
className={clsx("library-unit__dragger", {
|
||||
"library-unit__pulse": !!pendingElements,
|
||||
})}
|
||||
ref={ref}
|
||||
draggable={!!elements}
|
||||
onClick={!!elements || !!pendingElements ? onClick : undefined}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import "./ToolIcon.scss";
|
||||
|
||||
import React from "react";
|
||||
import clsx from "clsx";
|
||||
|
||||
type LockIconSize = "s" | "m";
|
||||
|
||||
@ -41,13 +42,15 @@ const ICONS = {
|
||||
};
|
||||
|
||||
export const LockIcon = (props: LockIconProps) => {
|
||||
const sizeCn = `ToolIcon_size_${props.size || DEFAULT_SIZE}`;
|
||||
|
||||
return (
|
||||
<label
|
||||
className={`ToolIcon ToolIcon__lock ToolIcon_type_floating ${sizeCn} zen-mode-visibility ${
|
||||
props.zenModeEnabled ? "zen-mode-visibility--hidden" : ""
|
||||
}`}
|
||||
className={clsx(
|
||||
"ToolIcon ToolIcon__lock ToolIcon_type_floating zen-mode-visibility",
|
||||
`ToolIcon_size_${props.size || DEFAULT_SIZE}`,
|
||||
{
|
||||
"zen-mode-visibility--hidden": props.zenModeEnabled,
|
||||
},
|
||||
)}
|
||||
title={`${props.title} — Q`}
|
||||
>
|
||||
<input
|
||||
|
@ -2,6 +2,7 @@ import "./Modal.scss";
|
||||
|
||||
import React, { useState, useLayoutEffect } from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
import clsx from "clsx";
|
||||
import { KEYS } from "../keys";
|
||||
|
||||
export const Modal = (props: {
|
||||
@ -26,7 +27,7 @@ export const Modal = (props: {
|
||||
|
||||
return createPortal(
|
||||
<div
|
||||
className={`Modal ${props.className ?? ""}`}
|
||||
className={clsx("Modal", props.className)}
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
onKeyDown={handleKeydown}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import React, { useState, useEffect, useRef } from "react";
|
||||
import clsx from "clsx";
|
||||
import { ToolButton } from "./ToolButton";
|
||||
import { t } from "../i18n";
|
||||
import useIsMobile from "../is-mobile";
|
||||
@ -154,9 +155,9 @@ export const RoomDialog = ({
|
||||
return (
|
||||
<>
|
||||
<ToolButton
|
||||
className={`RoomDialog-modalButton ${
|
||||
isCollaborating ? "is-collaborating" : ""
|
||||
}`}
|
||||
className={clsx("RoomDialog-modalButton", {
|
||||
"is-collaborating": isCollaborating,
|
||||
})}
|
||||
onClick={() => setModalIsShown(true)}
|
||||
icon={users}
|
||||
type="button"
|
||||
|
@ -1,6 +1,7 @@
|
||||
import "./Stack.scss";
|
||||
|
||||
import React from "react";
|
||||
import clsx from "clsx";
|
||||
|
||||
type StackProps = {
|
||||
children: React.ReactNode;
|
||||
@ -19,7 +20,7 @@ const RowStack = ({
|
||||
}: StackProps) => {
|
||||
return (
|
||||
<div
|
||||
className={`Stack Stack_horizontal ${className || ""}`}
|
||||
className={clsx("Stack Stack_horizontal", className)}
|
||||
style={
|
||||
{
|
||||
"--gap": gap,
|
||||
@ -42,7 +43,7 @@ const ColStack = ({
|
||||
}: StackProps) => {
|
||||
return (
|
||||
<div
|
||||
className={`Stack Stack_vertical ${className || ""}`}
|
||||
className={clsx("Stack Stack_vertical", className)}
|
||||
style={
|
||||
{
|
||||
"--gap": gap,
|
||||
|
@ -1,6 +1,7 @@
|
||||
import "./ToolIcon.scss";
|
||||
|
||||
import React from "react";
|
||||
import clsx from "clsx";
|
||||
|
||||
type ToolIconSize = "s" | "m";
|
||||
|
||||
@ -45,15 +46,18 @@ export const ToolButton = React.forwardRef((props: ToolButtonProps, ref) => {
|
||||
if (props.type === "button") {
|
||||
return (
|
||||
<button
|
||||
className={`ToolIcon_type_button ${
|
||||
!props.hidden ? "ToolIcon" : ""
|
||||
} ${sizeCn}${props.selected ? " ToolIcon--selected" : ""} ${
|
||||
props.className
|
||||
} ${
|
||||
className={clsx(
|
||||
"ToolIcon_type_button",
|
||||
sizeCn,
|
||||
props.className,
|
||||
props.visible && !props.hidden
|
||||
? "ToolIcon_type_button--show"
|
||||
: "ToolIcon_type_button--hide"
|
||||
}`}
|
||||
: "ToolIcon_type_button--hide",
|
||||
{
|
||||
ToolIcon: !props.hidden,
|
||||
"ToolIcon--selected": props.selected,
|
||||
},
|
||||
)}
|
||||
hidden={props.hidden}
|
||||
title={props.title}
|
||||
aria-label={props["aria-label"]}
|
||||
@ -78,7 +82,7 @@ export const ToolButton = React.forwardRef((props: ToolButtonProps, ref) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<label className={`ToolIcon ${props.className ?? ""}`} title={props.title}>
|
||||
<label className={clsx("ToolIcon", props.className)} title={props.title}>
|
||||
<input
|
||||
className={`ToolIcon_type_radio ${sizeCn}`}
|
||||
type="radio"
|
||||
|
@ -1,6 +1,7 @@
|
||||
import "./UserList.scss";
|
||||
|
||||
import React from "react";
|
||||
import clsx from "clsx";
|
||||
|
||||
type UserListProps = {
|
||||
children: React.ReactNode;
|
||||
@ -9,15 +10,9 @@ type UserListProps = {
|
||||
};
|
||||
|
||||
export const UserList = ({ children, className, mobile }: UserListProps) => {
|
||||
let compClassName = "UserList";
|
||||
|
||||
if (className) {
|
||||
compClassName += ` ${className}`;
|
||||
}
|
||||
|
||||
if (mobile) {
|
||||
compClassName += " UserList_mobile";
|
||||
}
|
||||
|
||||
return <div className={compClassName}>{children}</div>;
|
||||
return (
|
||||
<div className={clsx("UserList", className, { UserList_mobile: mobile })}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -6,6 +6,7 @@
|
||||
import React from "react";
|
||||
|
||||
import oc from "open-color";
|
||||
import clsx from "clsx";
|
||||
|
||||
const activeElementColor = (appearance: "light" | "dark") =>
|
||||
appearance === "light" ? oc.orange[4] : oc.orange[9];
|
||||
@ -27,7 +28,7 @@ const createIcon = (d: string | React.ReactNode, opts: number | Opts = 512) => {
|
||||
focusable="false"
|
||||
role="img"
|
||||
viewBox={`0 0 ${width} ${height}`}
|
||||
className={mirror && "rtl-mirror"}
|
||||
className={clsx({ "rtl-mirror": mirror })}
|
||||
style={style}
|
||||
>
|
||||
{typeof d === "string" ? <path fill="currentColor" d={d} /> : d}
|
||||
|
Loading…
x
Reference in New Issue
Block a user