Add and use clsx (classnames alternative) (#2249)

Co-authored-by: David Luzar <luzar.david@gmail.com>
This commit is contained in:
Danila 2020-10-19 17:14:28 +03:00 committed by GitHub
parent 1484c5a63b
commit b50c54f855
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 108 additions and 72 deletions

5
package-lock.json generated
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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")}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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