mirror of
				https://github.com/excalidraw/excalidraw.git
				synced 2025-11-04 04:44:31 +01:00 
			
		
		
		
	feat: library sidebar design tweaks (#6582)
This commit is contained in:
		@@ -1,11 +1,6 @@
 | 
			
		||||
@import "open-color/open-color";
 | 
			
		||||
 | 
			
		||||
.excalidraw {
 | 
			
		||||
  .library-menu-items-container {
 | 
			
		||||
    height: 100%;
 | 
			
		||||
    width: 100%;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .layer-ui__library {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
@@ -70,6 +65,16 @@
 | 
			
		||||
    align-items: center;
 | 
			
		||||
    justify-content: center;
 | 
			
		||||
    gap: 0.625rem;
 | 
			
		||||
    position: relative;
 | 
			
		||||
 | 
			
		||||
    &--at-bottom::before {
 | 
			
		||||
      content: "";
 | 
			
		||||
      width: calc(100% - 1.5rem);
 | 
			
		||||
      height: 1px;
 | 
			
		||||
      position: absolute;
 | 
			
		||||
      top: -1px;
 | 
			
		||||
      background: var(--sidebar-border-color);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .library-menu-browse-button {
 | 
			
		||||
@@ -126,4 +131,20 @@
 | 
			
		||||
      padding: 0.25rem 0.5rem;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .layer-ui__library .library-menu-dropdown-container {
 | 
			
		||||
    position: relative;
 | 
			
		||||
 | 
			
		||||
    &--in-heading {
 | 
			
		||||
      padding: 0;
 | 
			
		||||
      position: absolute;
 | 
			
		||||
      top: 1rem;
 | 
			
		||||
      right: 0.75rem;
 | 
			
		||||
      z-index: 1;
 | 
			
		||||
 | 
			
		||||
      .dropdown-menu {
 | 
			
		||||
        top: 100%;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -121,12 +121,11 @@ export const LibraryMenuContent = ({
 | 
			
		||||
      />
 | 
			
		||||
      {showBtn && (
 | 
			
		||||
        <LibraryMenuControlButtons
 | 
			
		||||
          className="library-menu-control-buttons--at-bottom"
 | 
			
		||||
          style={{ padding: "16px 12px 0 12px" }}
 | 
			
		||||
          id={id}
 | 
			
		||||
          libraryReturnUrl={libraryReturnUrl}
 | 
			
		||||
          theme={appState.theme}
 | 
			
		||||
          selectedItems={selectedItems}
 | 
			
		||||
          onSelectItems={onSelectItems}
 | 
			
		||||
        />
 | 
			
		||||
      )}
 | 
			
		||||
    </LibraryMenuWrapper>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,33 +1,33 @@
 | 
			
		||||
import { LibraryItem, ExcalidrawProps, UIAppState } from "../types";
 | 
			
		||||
import { ExcalidrawProps, UIAppState } from "../types";
 | 
			
		||||
import LibraryMenuBrowseButton from "./LibraryMenuBrowseButton";
 | 
			
		||||
import { LibraryDropdownMenu } from "./LibraryMenuHeaderContent";
 | 
			
		||||
import clsx from "clsx";
 | 
			
		||||
 | 
			
		||||
export const LibraryMenuControlButtons = ({
 | 
			
		||||
  selectedItems,
 | 
			
		||||
  onSelectItems,
 | 
			
		||||
  libraryReturnUrl,
 | 
			
		||||
  theme,
 | 
			
		||||
  id,
 | 
			
		||||
  style,
 | 
			
		||||
  children,
 | 
			
		||||
  className,
 | 
			
		||||
}: {
 | 
			
		||||
  selectedItems: LibraryItem["id"][];
 | 
			
		||||
  onSelectItems: (id: LibraryItem["id"][]) => void;
 | 
			
		||||
  libraryReturnUrl: ExcalidrawProps["libraryReturnUrl"];
 | 
			
		||||
  theme: UIAppState["theme"];
 | 
			
		||||
  id: string;
 | 
			
		||||
  style: React.CSSProperties;
 | 
			
		||||
  children?: React.ReactNode;
 | 
			
		||||
  className?: string;
 | 
			
		||||
}) => {
 | 
			
		||||
  return (
 | 
			
		||||
    <div className="library-menu-control-buttons" style={style}>
 | 
			
		||||
    <div
 | 
			
		||||
      className={clsx("library-menu-control-buttons", className)}
 | 
			
		||||
      style={style}
 | 
			
		||||
    >
 | 
			
		||||
      <LibraryMenuBrowseButton
 | 
			
		||||
        id={id}
 | 
			
		||||
        libraryReturnUrl={libraryReturnUrl}
 | 
			
		||||
        theme={theme}
 | 
			
		||||
      />
 | 
			
		||||
      <LibraryDropdownMenu
 | 
			
		||||
        selectedItems={selectedItems}
 | 
			
		||||
        onSelectItems={onSelectItems}
 | 
			
		||||
      />
 | 
			
		||||
      {children}
 | 
			
		||||
    </div>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -23,6 +23,7 @@ import { Dialog } from "./Dialog";
 | 
			
		||||
import DropdownMenu from "./dropdownMenu/DropdownMenu";
 | 
			
		||||
import { isLibraryMenuOpenAtom } from "./LibraryMenu";
 | 
			
		||||
import { useUIAppState } from "../context/ui-appState";
 | 
			
		||||
import clsx from "clsx";
 | 
			
		||||
 | 
			
		||||
const getSelectedItems = (
 | 
			
		||||
  libraryItems: LibraryItems,
 | 
			
		||||
@@ -37,6 +38,7 @@ export const LibraryDropdownMenuButton: React.FC<{
 | 
			
		||||
  resetLibrary: () => void;
 | 
			
		||||
  onSelectItems: (items: LibraryItem["id"][]) => void;
 | 
			
		||||
  appState: UIAppState;
 | 
			
		||||
  className?: string;
 | 
			
		||||
}> = ({
 | 
			
		||||
  setAppState,
 | 
			
		||||
  selectedItems,
 | 
			
		||||
@@ -45,6 +47,7 @@ export const LibraryDropdownMenuButton: React.FC<{
 | 
			
		||||
  resetLibrary,
 | 
			
		||||
  onSelectItems,
 | 
			
		||||
  appState,
 | 
			
		||||
  className,
 | 
			
		||||
}) => {
 | 
			
		||||
  const [libraryItemsData] = useAtom(libraryItemsAtom, jotaiScope);
 | 
			
		||||
  const [isLibraryMenuOpen, setIsLibraryMenuOpen] = useAtom(
 | 
			
		||||
@@ -236,7 +239,7 @@ export const LibraryDropdownMenuButton: React.FC<{
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <div style={{ position: "relative" }}>
 | 
			
		||||
    <div className={clsx("library-menu-dropdown-container", className)}>
 | 
			
		||||
      {renderLibraryMenu()}
 | 
			
		||||
      {selectedItems.length > 0 && (
 | 
			
		||||
        <div className="library-actions-counter">{selectedItems.length}</div>
 | 
			
		||||
@@ -270,9 +273,11 @@ export const LibraryDropdownMenuButton: React.FC<{
 | 
			
		||||
export const LibraryDropdownMenu = ({
 | 
			
		||||
  selectedItems,
 | 
			
		||||
  onSelectItems,
 | 
			
		||||
  className,
 | 
			
		||||
}: {
 | 
			
		||||
  selectedItems: LibraryItem["id"][];
 | 
			
		||||
  onSelectItems: (id: LibraryItem["id"][]) => void;
 | 
			
		||||
  className?: string;
 | 
			
		||||
}) => {
 | 
			
		||||
  const { library } = useApp();
 | 
			
		||||
  const appState = useUIAppState();
 | 
			
		||||
@@ -308,6 +313,7 @@ export const LibraryDropdownMenu = ({
 | 
			
		||||
        removeFromLibrary(libraryItemsData.libraryItems)
 | 
			
		||||
      }
 | 
			
		||||
      resetLibrary={resetLibrary}
 | 
			
		||||
      className={className}
 | 
			
		||||
    />
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -26,6 +26,7 @@
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .library-menu-items-container {
 | 
			
		||||
    width: 100%;
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-grow: 1;
 | 
			
		||||
    flex-shrink: 1;
 | 
			
		||||
@@ -35,10 +36,14 @@
 | 
			
		||||
    height: 100%;
 | 
			
		||||
    justify-content: center;
 | 
			
		||||
    margin: 0;
 | 
			
		||||
    border-bottom: 1px solid var(--sidebar-border-color);
 | 
			
		||||
 | 
			
		||||
    position: relative;
 | 
			
		||||
 | 
			
		||||
    & > div {
 | 
			
		||||
      padding-left: 0.75rem;
 | 
			
		||||
      padding-right: 0.75rem;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &__row {
 | 
			
		||||
      display: grid;
 | 
			
		||||
      grid-template-columns: repeat(4, 1fr);
 | 
			
		||||
@@ -59,6 +64,9 @@
 | 
			
		||||
      font-size: 1.125rem;
 | 
			
		||||
      font-weight: bold;
 | 
			
		||||
      margin-bottom: 0.75rem;
 | 
			
		||||
      width: 100%;
 | 
			
		||||
      padding-right: 4rem; // due to dropdown button
 | 
			
		||||
      box-sizing: border-box;
 | 
			
		||||
 | 
			
		||||
      &--excal {
 | 
			
		||||
        margin-top: 2rem;
 | 
			
		||||
@@ -75,4 +83,11 @@
 | 
			
		||||
      color: var(--text-primary-color);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .library-menu-items-private-library-container {
 | 
			
		||||
    // so that when you toggle between pending item and no items, there's
 | 
			
		||||
    // no layout shift (this is hardcoded and works only with ENG locale)
 | 
			
		||||
    min-height: 3.75rem;
 | 
			
		||||
    width: 100%;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -15,6 +15,7 @@ import { MIME_TYPES } from "../constants";
 | 
			
		||||
import Spinner from "./Spinner";
 | 
			
		||||
import { duplicateElements } from "../element/newElement";
 | 
			
		||||
import { LibraryMenuControlButtons } from "./LibraryMenuControlButtons";
 | 
			
		||||
import { LibraryDropdownMenu } from "./LibraryMenuHeaderContent";
 | 
			
		||||
 | 
			
		||||
import "./LibraryMenuItems.scss";
 | 
			
		||||
 | 
			
		||||
@@ -207,6 +208,11 @@ const LibraryMenuItems = ({
 | 
			
		||||
 | 
			
		||||
  const showBtn = !libraryItems.length && !pendingElements.length;
 | 
			
		||||
 | 
			
		||||
  const isLibraryEmpty =
 | 
			
		||||
    !pendingElements.length &&
 | 
			
		||||
    !unpublishedItems.length &&
 | 
			
		||||
    !publishedItems.length;
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <div
 | 
			
		||||
      className="library-menu-items-container"
 | 
			
		||||
@@ -218,6 +224,13 @@ const LibraryMenuItems = ({
 | 
			
		||||
          : { borderBottom: 0 }
 | 
			
		||||
      }
 | 
			
		||||
    >
 | 
			
		||||
      {!isLibraryEmpty && (
 | 
			
		||||
        <LibraryDropdownMenu
 | 
			
		||||
          selectedItems={selectedItems}
 | 
			
		||||
          onSelectItems={onSelectItems}
 | 
			
		||||
          className="library-menu-dropdown-container--in-heading"
 | 
			
		||||
        />
 | 
			
		||||
      )}
 | 
			
		||||
      <Stack.Col
 | 
			
		||||
        className="library-menu-items-container__items"
 | 
			
		||||
        align="start"
 | 
			
		||||
@@ -228,47 +241,45 @@ const LibraryMenuItems = ({
 | 
			
		||||
        }}
 | 
			
		||||
      >
 | 
			
		||||
        <>
 | 
			
		||||
          <div>
 | 
			
		||||
            {(pendingElements.length > 0 ||
 | 
			
		||||
              unpublishedItems.length > 0 ||
 | 
			
		||||
              publishedItems.length > 0) && (
 | 
			
		||||
              <div className="library-menu-items-container__header">
 | 
			
		||||
                {t("labels.personalLib")}
 | 
			
		||||
              </div>
 | 
			
		||||
            )}
 | 
			
		||||
            {isLoading && (
 | 
			
		||||
              <div
 | 
			
		||||
                style={{
 | 
			
		||||
                  position: "absolute",
 | 
			
		||||
                  top: "var(--container-padding-y)",
 | 
			
		||||
                  right: "var(--container-padding-x)",
 | 
			
		||||
                  transform: "translateY(50%)",
 | 
			
		||||
                }}
 | 
			
		||||
              >
 | 
			
		||||
                <Spinner />
 | 
			
		||||
          {!isLibraryEmpty && (
 | 
			
		||||
            <div className="library-menu-items-container__header">
 | 
			
		||||
              {t("labels.personalLib")}
 | 
			
		||||
            </div>
 | 
			
		||||
          )}
 | 
			
		||||
          {isLoading && (
 | 
			
		||||
            <div
 | 
			
		||||
              style={{
 | 
			
		||||
                position: "absolute",
 | 
			
		||||
                top: "var(--container-padding-y)",
 | 
			
		||||
                right: "var(--container-padding-x)",
 | 
			
		||||
                transform: "translateY(50%)",
 | 
			
		||||
              }}
 | 
			
		||||
            >
 | 
			
		||||
              <Spinner />
 | 
			
		||||
            </div>
 | 
			
		||||
          )}
 | 
			
		||||
          <div className="library-menu-items-private-library-container">
 | 
			
		||||
            {!pendingElements.length && !unpublishedItems.length ? (
 | 
			
		||||
              <div className="library-menu-items__no-items">
 | 
			
		||||
                <div className="library-menu-items__no-items__label">
 | 
			
		||||
                  {t("library.noItems")}
 | 
			
		||||
                </div>
 | 
			
		||||
                <div className="library-menu-items__no-items__hint">
 | 
			
		||||
                  {publishedItems.length > 0
 | 
			
		||||
                    ? t("library.hint_emptyPrivateLibrary")
 | 
			
		||||
                    : t("library.hint_emptyLibrary")}
 | 
			
		||||
                </div>
 | 
			
		||||
              </div>
 | 
			
		||||
            ) : (
 | 
			
		||||
              renderLibrarySection([
 | 
			
		||||
                // append pending library item
 | 
			
		||||
                ...(pendingElements.length
 | 
			
		||||
                  ? [{ id: null, elements: pendingElements }]
 | 
			
		||||
                  : []),
 | 
			
		||||
                ...unpublishedItems,
 | 
			
		||||
              ])
 | 
			
		||||
            )}
 | 
			
		||||
          </div>
 | 
			
		||||
          {!pendingElements.length && !unpublishedItems.length ? (
 | 
			
		||||
            <div className="library-menu-items__no-items">
 | 
			
		||||
              <div className="library-menu-items__no-items__label">
 | 
			
		||||
                {t("library.noItems")}
 | 
			
		||||
              </div>
 | 
			
		||||
              <div className="library-menu-items__no-items__hint">
 | 
			
		||||
                {publishedItems.length > 0
 | 
			
		||||
                  ? t("library.hint_emptyPrivateLibrary")
 | 
			
		||||
                  : t("library.hint_emptyLibrary")}
 | 
			
		||||
              </div>
 | 
			
		||||
            </div>
 | 
			
		||||
          ) : (
 | 
			
		||||
            renderLibrarySection([
 | 
			
		||||
              // append pending library item
 | 
			
		||||
              ...(pendingElements.length
 | 
			
		||||
                ? [{ id: null, elements: pendingElements }]
 | 
			
		||||
                : []),
 | 
			
		||||
              ...unpublishedItems,
 | 
			
		||||
            ])
 | 
			
		||||
          )}
 | 
			
		||||
        </>
 | 
			
		||||
 | 
			
		||||
        <>
 | 
			
		||||
@@ -304,9 +315,12 @@ const LibraryMenuItems = ({
 | 
			
		||||
            id={id}
 | 
			
		||||
            libraryReturnUrl={libraryReturnUrl}
 | 
			
		||||
            theme={theme}
 | 
			
		||||
            selectedItems={selectedItems}
 | 
			
		||||
            onSelectItems={onSelectItems}
 | 
			
		||||
          />
 | 
			
		||||
          >
 | 
			
		||||
            <LibraryDropdownMenu
 | 
			
		||||
              selectedItems={selectedItems}
 | 
			
		||||
              onSelectItems={onSelectItems}
 | 
			
		||||
            />
 | 
			
		||||
          </LibraryMenuControlButtons>
 | 
			
		||||
        )}
 | 
			
		||||
      </Stack.Col>
 | 
			
		||||
    </div>
 | 
			
		||||
 
 | 
			
		||||
@@ -46,8 +46,17 @@
 | 
			
		||||
    justify-content: space-between;
 | 
			
		||||
    align-items: center;
 | 
			
		||||
    width: 100%;
 | 
			
		||||
    padding-top: 1rem;
 | 
			
		||||
    padding-bottom: 1rem;
 | 
			
		||||
    padding: 1rem 0.75rem;
 | 
			
		||||
    position: relative;
 | 
			
		||||
 | 
			
		||||
    &::after {
 | 
			
		||||
      content: "";
 | 
			
		||||
      width: calc(100% - 1.5rem);
 | 
			
		||||
      height: 1px;
 | 
			
		||||
      background: var(--sidebar-border-color);
 | 
			
		||||
      position: absolute;
 | 
			
		||||
      bottom: -1px;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .sidebar__header__buttons {
 | 
			
		||||
@@ -89,7 +98,7 @@
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
    flex: 1 1 auto;
 | 
			
		||||
    padding: 1rem 0.75rem;
 | 
			
		||||
    padding: 1rem 0;
 | 
			
		||||
 | 
			
		||||
    [role="tabpanel"] {
 | 
			
		||||
      flex: 1;
 | 
			
		||||
@@ -161,9 +170,5 @@
 | 
			
		||||
        border: none;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .sidebar__header {
 | 
			
		||||
      border-bottom: 1px solid var(--sidebar-border-color);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user