refactor: update SCSS syntax & remove open-color dep (#10633)

This commit is contained in:
David Luzar
2026-01-10 18:15:14 +01:00
committed by GitHub
parent cc6c29c0b9
commit b9a255407f
78 changed files with 526 additions and 410 deletions
@@ -1,46 +0,0 @@
import { THEME } from "@excalidraw/common";
import oc from "open-color";
import React from "react";
import type { Theme } from "@excalidraw/element/types";
// https://github.com/tholman/github-corners
export const GitHubCorner = React.memo(
({ theme, dir }: { theme: Theme; dir: string }) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width="40"
height="40"
viewBox="0 0 250 250"
className="rtl-mirror"
style={{
marginTop: "calc(var(--space-factor) * -1)",
[dir === "rtl" ? "marginLeft" : "marginRight"]:
"calc(var(--space-factor) * -1)",
}}
>
<a
href="https://github.com/excalidraw/excalidraw"
target="_blank"
rel="noopener noreferrer"
aria-label="GitHub repository"
>
<path
d="M0 0l115 115h15l12 27 108 108V0z"
fill={theme === THEME.LIGHT ? oc.gray[6] : oc.gray[7]}
/>
<path
className="octo-arm"
d="M128 109c-15-9-9-19-9-19 3-7 2-11 2-11-1-7 3-2 3-2 4 5 2 11 2 11-3 10 5 15 9 16"
style={{ transformOrigin: "130px 106px" }}
fill={theme === THEME.LIGHT ? oc.white : "var(--default-bg-color)"}
/>
<path
className="octo-body"
d="M115 115s4 2 5 0l14-14c3-2 6-3 8-3-8-11-15-24 2-41 5-5 10-7 16-7 1-2 3-7 12-11 0 0 5 3 7 16 4 2 8 5 12 9s7 8 9 12c14 3 17 7 17 7-4 8-9 11-11 11 0 6-2 11-7 16-16 16-30 10-41 2 0 3-1 7-5 11l-12 11c-1 1 1 5 1 5z"
fill={theme === THEME.LIGHT ? oc.white : "var(--default-bg-color)"}
/>
</a>
</svg>
),
);
@@ -1,5 +1,97 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`COLOR_PALETTE > color palette doesn't regress 1`] = `
{
"black": "#1e1e1e",
"blue": [
"#e7f5ff",
"#a5d8ff",
"#4dabf7",
"#228be6",
"#1971c2",
],
"bronze": [
"#f8f1ee",
"#eaddd7",
"#d2bab0",
"#a18072",
"#846358",
],
"cyan": [
"#e3fafc",
"#99e9f2",
"#3bc9db",
"#15aabf",
"#0c8599",
],
"grape": [
"#f8f0fc",
"#eebefa",
"#da77f2",
"#be4bdb",
"#9c36b5",
],
"gray": [
"#f8f9fa",
"#e9ecef",
"#ced4da",
"#868e96",
"#343a40",
],
"green": [
"#ebfbee",
"#b2f2bb",
"#69db7c",
"#40c057",
"#2f9e44",
],
"orange": [
"#fff4e6",
"#ffd8a8",
"#ffa94d",
"#fd7e14",
"#e8590c",
],
"pink": [
"#fff0f6",
"#fcc2d7",
"#f783ac",
"#e64980",
"#c2255c",
],
"red": [
"#fff5f5",
"#ffc9c9",
"#ff8787",
"#fa5252",
"#e03131",
],
"teal": [
"#e6fcf5",
"#96f2d7",
"#38d9a9",
"#12b886",
"#099268",
],
"transparent": "transparent",
"violet": [
"#f3f0ff",
"#d0bfff",
"#9775fa",
"#7950f2",
"#6741d9",
],
"white": "#ffffff",
"yellow": [
"#fff9db",
"#ffec99",
"#ffd43b",
"#fab005",
"#f08c00",
],
}
`;
exports[`applyDarkModeFilter > COLOR_PALETTE regression tests > matches snapshot for all palette colors 1`] = ` exports[`applyDarkModeFilter > COLOR_PALETTE regression tests > matches snapshot for all palette colors 1`] = `
{ {
"black": "#d3d3d3", "black": "#d3d3d3",
+6
View File
@@ -4,6 +4,12 @@ import {
rgbToHex, rgbToHex,
} from "@excalidraw/common"; } from "@excalidraw/common";
describe("COLOR_PALETTE", () => {
it("color palette doesn't regress", () => {
expect(COLOR_PALETTE).toMatchSnapshot();
});
});
describe("applyDarkModeFilter", () => { describe("applyDarkModeFilter", () => {
describe("basic transformations", () => { describe("basic transformations", () => {
it("transforms black to near-white", () => { it("transforms black to near-white", () => {
+102 -48
View File
@@ -1,4 +1,3 @@
import oc from "open-color";
import tinycolor from "tinycolor2"; import tinycolor from "tinycolor2";
import { clamp } from "@excalidraw/math"; import { clamp } from "@excalidraw/math";
@@ -6,18 +5,14 @@ import { degreesToRadians } from "@excalidraw/math";
import type { Degrees } from "@excalidraw/math"; import type { Degrees } from "@excalidraw/math";
import type { Merge } from "./utility-types"; // ---------------------------------------------------------------------------
// Dark mode color transformation
export { tinycolor }; // ---------------------------------------------------------------------------
// Browser-only cache to avoid memory leaks on server // Browser-only cache to avoid memory leaks on server
const DARK_MODE_COLORS_CACHE: Map<string, string> | null = const DARK_MODE_COLORS_CACHE: Map<string, string> | null =
typeof window !== "undefined" ? new Map() : null; typeof window !== "undefined" ? new Map() : null;
// ---------------------------------------------------------------------------
// Dark mode color transformation
// ---------------------------------------------------------------------------
function cssHueRotate( function cssHueRotate(
red: number, red: number,
green: number, green: number,
@@ -115,8 +110,8 @@ export const applyDarkModeFilter = (color: string): string => {
}; };
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Color palette
export const COLOR_OUTLINE_CONTRAST_THRESHOLD = 240; // ---------------------------------------------------------------------------
// FIXME can't put to utils.ts rn because of circular dependency // FIXME can't put to utils.ts rn because of circular dependency
const pick = <R extends Record<string, any>, K extends readonly (keyof R)[]>( const pick = <R extends Record<string, any>, K extends readonly (keyof R)[]>(
@@ -131,15 +126,7 @@ const pick = <R extends Record<string, any>, K extends readonly (keyof R)[]>(
}, {} as Pick<R, K[number]>) as Pick<R, K[number]>; }, {} as Pick<R, K[number]>) as Pick<R, K[number]>;
}; };
export type ColorPickerColor =
| Exclude<keyof oc, "indigo" | "lime">
| "transparent"
| "bronze";
export type ColorTuple = readonly [string, string, string, string, string]; export type ColorTuple = readonly [string, string, string, string, string];
export type ColorPalette = Merge<
Record<ColorPickerColor, ColorTuple>,
{ black: "#1e1e1e"; white: "#ffffff"; transparent: "transparent" }
>;
// used general type instead of specific type (ColorPalette) to support custom colors // used general type instead of specific type (ColorPalette) to support custom colors
export type ColorPaletteCustom = { [key: string]: ColorTuple | string }; export type ColorPaletteCustom = { [key: string]: ColorTuple | string };
@@ -152,38 +139,30 @@ export const DEFAULT_CHART_COLOR_INDEX = 4;
export const DEFAULT_ELEMENT_STROKE_COLOR_INDEX = 4; export const DEFAULT_ELEMENT_STROKE_COLOR_INDEX = 4;
export const DEFAULT_ELEMENT_BACKGROUND_COLOR_INDEX = 1; export const DEFAULT_ELEMENT_BACKGROUND_COLOR_INDEX = 1;
export const ELEMENTS_PALETTE_SHADE_INDEXES = [0, 2, 4, 6, 8] as const;
export const CANVAS_PALETTE_SHADE_INDEXES = [0, 1, 2, 3, 4] as const;
export const getSpecificColorShades = (
color: Exclude<
ColorPickerColor,
"transparent" | "white" | "black" | "bronze"
>,
indexArr: Readonly<ColorShadesIndexes>,
) => {
return indexArr.map((index) => oc[color][index]) as any as ColorTuple;
};
export const COLOR_PALETTE = { export const COLOR_PALETTE = {
transparent: "transparent", transparent: "transparent",
black: "#1e1e1e", black: "#1e1e1e",
white: "#ffffff", white: "#ffffff",
// open-colors // open-color from https://github.com/yeun/open-color/blob/master/open-color.js
gray: getSpecificColorShades("gray", ELEMENTS_PALETTE_SHADE_INDEXES), // corresponds to indexes [0,2,4,6,8] (weights: 50, 200, 400, 600, 800)
red: getSpecificColorShades("red", ELEMENTS_PALETTE_SHADE_INDEXES), gray: ["#f8f9fa", "#e9ecef", "#ced4da", "#868e96", "#343a40"],
pink: getSpecificColorShades("pink", ELEMENTS_PALETTE_SHADE_INDEXES), red: ["#fff5f5", "#ffc9c9", "#ff8787", "#fa5252", "#e03131"],
grape: getSpecificColorShades("grape", ELEMENTS_PALETTE_SHADE_INDEXES), pink: ["#fff0f6", "#fcc2d7", "#f783ac", "#e64980", "#c2255c"],
violet: getSpecificColorShades("violet", ELEMENTS_PALETTE_SHADE_INDEXES), grape: ["#f8f0fc", "#eebefa", "#da77f2", "#be4bdb", "#9c36b5"],
blue: getSpecificColorShades("blue", ELEMENTS_PALETTE_SHADE_INDEXES), violet: ["#f3f0ff", "#d0bfff", "#9775fa", "#7950f2", "#6741d9"],
cyan: getSpecificColorShades("cyan", ELEMENTS_PALETTE_SHADE_INDEXES), blue: ["#e7f5ff", "#a5d8ff", "#4dabf7", "#228be6", "#1971c2"],
teal: getSpecificColorShades("teal", ELEMENTS_PALETTE_SHADE_INDEXES), cyan: ["#e3fafc", "#99e9f2", "#3bc9db", "#15aabf", "#0c8599"],
green: getSpecificColorShades("green", ELEMENTS_PALETTE_SHADE_INDEXES), teal: ["#e6fcf5", "#96f2d7", "#38d9a9", "#12b886", "#099268"],
yellow: getSpecificColorShades("yellow", ELEMENTS_PALETTE_SHADE_INDEXES), green: ["#ebfbee", "#b2f2bb", "#69db7c", "#40c057", "#2f9e44"],
orange: getSpecificColorShades("orange", ELEMENTS_PALETTE_SHADE_INDEXES), yellow: ["#fff9db", "#ffec99", "#ffd43b", "#fab005", "#f08c00"],
// radix bronze shades 3,5,7,9,11 orange: ["#fff4e6", "#ffd8a8", "#ffa94d", "#fd7e14", "#e8590c"],
// radix bronze shades [3,5,7,9,11]
bronze: ["#f8f1ee", "#eaddd7", "#d2bab0", "#a18072", "#846358"], bronze: ["#f8f1ee", "#eaddd7", "#d2bab0", "#a18072", "#846358"],
} as ColorPalette; } as const;
export type ColorPalette = typeof COLOR_PALETTE;
export type ColorPickerColor = keyof typeof COLOR_PALETTE;
const COMMON_ELEMENT_SHADES = pick(COLOR_PALETTE, [ const COMMON_ELEMENT_SHADES = pick(COLOR_PALETTE, [
"cyan", "cyan",
@@ -198,7 +177,6 @@ const COMMON_ELEMENT_SHADES = pick(COLOR_PALETTE, [
"red", "red",
]); ]);
// -----------------------------------------------------------------------------
// quick picks defaults // quick picks defaults
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@@ -233,7 +211,6 @@ export const DEFAULT_CANVAS_BACKGROUND_PICKS = [
"#fdf8f6", "#fdf8f6",
] as ColorTuple; ] as ColorTuple;
// -----------------------------------------------------------------------------
// palette defaults // palette defaults
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@@ -259,8 +236,7 @@ export const DEFAULT_ELEMENT_BACKGROUND_COLOR_PALETTE = {
...COMMON_ELEMENT_SHADES, ...COMMON_ELEMENT_SHADES,
} as const; } as const;
// ----------------------------------------------------------------------------- // color palette helpers
// helpers
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// !!!MUST BE WITHOUT GRAY, TRANSPARENT AND BLACK!!! // !!!MUST BE WITHOUT GRAY, TRANSPARENT AND BLACK!!!
@@ -281,6 +257,10 @@ export const getAllColorsSpecificShade = (index: 0 | 1 | 2 | 3 | 4) =>
COLOR_PALETTE.red[index], COLOR_PALETTE.red[index],
] as const; ] as const;
// -----------------------------------------------------------------------------
// other helpers
// -----------------------------------------------------------------------------
export const rgbToHex = (r: number, g: number, b: number, a?: number) => { export const rgbToHex = (r: number, g: number, b: number, a?: number) => {
// (1 << 24) adds 0x1000000 to ensure the hex string is always 7 chars, // (1 << 24) adds 0x1000000 to ensure the hex string is always 7 chars,
// then slice(1) removes the leading "1" to get exactly 6 hex digits // then slice(1) removes the leading "1" to get exactly 6 hex digits
@@ -299,4 +279,78 @@ export const rgbToHex = (r: number, g: number, b: number, a?: number) => {
return hex6; return hex6;
}; };
/**
* @returns #RRGGBB or #RRGGBBAA based on color containing non-opaque alpha,
* null if not valid color
*/
export const colorToHex = (color: string): string | null => {
const tc = tinycolor(color);
if (!tc.isValid()) {
return null;
}
const { r, g, b, a } = tc.toRgb();
return rgbToHex(r, g, b, a);
};
export const isTransparent = (color: string) => {
return tinycolor(color).getAlpha() === 0;
};
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// color contract helpers
// -----------------------------------------------------------------------------
export const COLOR_OUTLINE_CONTRAST_THRESHOLD = 240;
const calculateContrast = (r: number, g: number, b: number): number => {
const yiq = (r * 299 + g * 587 + b * 114) / 1000;
return yiq;
};
// YIQ algo, inspiration from https://stackoverflow.com/a/11868398
export const isColorDark = (color: string, threshold = 160): boolean => {
// no color ("") -> assume it default to black
if (!color) {
return true;
}
if (isTransparent(color)) {
return false;
}
const tc = tinycolor(color);
if (!tc.isValid()) {
// invalid color -> assume it defaults to black
return true;
}
const { r, g, b } = tc.toRgb();
return calculateContrast(r, g, b) < threshold;
};
// -----------------------------------------------------------------------------
// normalization
// -----------------------------------------------------------------------------
/**
* tries to keep the input color as-is if it's valid, making minimal adjustments
* (trimming whitespace or adding `#` to hex colors)
*/
export const normalizeInputColor = (color: string): string | null => {
color = color.trim();
if (isTransparent(color)) {
return color;
}
const tc = tinycolor(color);
if (tc.isValid()) {
// testing for `#` first fixes a bug on Electron (more specfically, an
// Obsidian popout window), where a hex color without `#` is considered valid
if (tc.getFormat() === "hex" && !color.startsWith("#")) {
return `#${color}`;
}
return color;
}
return null;
};
-5
View File
@@ -10,7 +10,6 @@ import type {
Zoom, Zoom,
} from "@excalidraw/excalidraw/types"; } from "@excalidraw/excalidraw/types";
import { tinycolor } from "./colors";
import { import {
DEFAULT_VERSION, DEFAULT_VERSION,
ENV, ENV,
@@ -548,10 +547,6 @@ export const mapFind = <T, K>(
return undefined; return undefined;
}; };
export const isTransparent = (color: string) => {
return tinycolor(color).getAlpha() === 0;
};
export type ResolvablePromise<T> = Promise<T> & { export type ResolvablePromise<T> = Promise<T> & {
resolve: [T] extends [undefined] resolve: [T] extends [undefined]
? (value?: MaybePromise<Awaited<T>>) => void ? (value?: MaybePromise<Awaited<T>>) => void
+1 -1
View File
@@ -1,4 +1,4 @@
@import "../css/variables.module.scss"; @use "../css/variables.module" as *;
.excalidraw { .excalidraw {
.Avatar { .Avatar {
+1 -1
View File
@@ -1,4 +1,4 @@
@import "../css/theme"; @use "../css/theme" as *;
.excalidraw { .excalidraw {
.excalidraw-button { .excalidraw-button {
@@ -1,4 +1,4 @@
@import "../css/theme"; @use "../css/theme" as *;
.excalidraw { .excalidraw {
button.standalone { button.standalone {
+3 -3
View File
@@ -1,4 +1,4 @@
@import "../css/variables.module.scss"; @use "../css/variables.module.scss" as *;
.excalidraw { .excalidraw {
.Card { .Card {
@@ -19,7 +19,7 @@
padding: 1.4rem; padding: 1.4rem;
border-radius: 50%; border-radius: 50%;
background: var(--card-color); background: var(--card-color);
color: $oc-white; color: #fff;
svg { svg {
width: 2.8rem; width: 2.8rem;
@@ -46,7 +46,7 @@
background-color: var(--card-color-darkest); background-color: var(--card-color-darkest);
} }
.ToolIcon__label { .ToolIcon__label {
color: $oc-white; color: #fff;
} }
.Spinner { .Spinner {
+23 -13
View File
@@ -1,25 +1,35 @@
import OpenColor from "open-color";
import "./Card.scss"; import "./Card.scss";
// for open-color see https://github.com/yeun/open-color/blob/master/open-color.scss
const COLOR_MAP = {
primary: {
base: "var(--color-primary)",
darker: "var(--color-primary-darker)",
darkest: "var(--color-primary-darkest)",
},
lime: {
base: "#74b816", // open-color lime[7]
darker: "#66a80f", // open-color lime[8]
darkest: "#5c940d", // open-color lime[9]
},
pink: {
base: "#d6336c", // open-color pink[7]
darker: "#c2255c", // open-color pink[8]
darkest: "#a61e4d", // open-color pink[9]
},
};
export const Card: React.FC<{ export const Card: React.FC<{
color: keyof OpenColor | "primary"; color: "primary" | "lime" | "pink";
children?: React.ReactNode; children?: React.ReactNode;
}> = ({ children, color }) => { }> = ({ children, color }) => {
return ( return (
<div <div
className="Card" className="Card"
style={{ style={{
["--card-color" as any]: ["--card-color" as any]: COLOR_MAP[color].base,
color === "primary" ? "var(--color-primary)" : OpenColor[color][7], ["--card-color-darker" as any]: COLOR_MAP[color].darker,
["--card-color-darker" as any]: ["--card-color-darkest" as any]: COLOR_MAP[color].darkest,
color === "primary"
? "var(--color-primary-darker)"
: OpenColor[color][8],
["--card-color-darkest" as any]:
color === "primary"
? "var(--color-primary-darkest)"
: OpenColor[color][9],
}} }}
> >
{children} {children}
@@ -1,4 +1,5 @@
@import "../css/variables.module.scss"; @use "sass:color";
@use "../css/variables.module" as *;
.excalidraw { .excalidraw {
.Checkbox { .Checkbox {
@@ -12,7 +13,7 @@
-webkit-tap-highlight-color: transparent; -webkit-tap-highlight-color: transparent;
&:hover:not(.is-checked) .Checkbox-box:not(:focus) { &:hover:not(.is-checked) .Checkbox-box:not(:focus) {
box-shadow: 0 0 0 2px #{$oc-blue-4}; box-shadow: 0 0 0 2px #{$color-blue-4};
} }
&:hover:not(.is-checked) .Checkbox-box:not(:focus) { &:hover:not(.is-checked) .Checkbox-box:not(:focus) {
@@ -24,25 +25,25 @@
&:active { &:active {
.Checkbox-box { .Checkbox-box {
box-shadow: 0 0 2px 1px inset #{$oc-blue-7} !important; box-shadow: 0 0 2px 1px inset #{$color-blue-7} !important;
} }
} }
&:hover { &:hover {
.Checkbox-box { .Checkbox-box {
background-color: fade-out($oc-blue-1, 0.8); background-color: color.adjust($color-blue-1, $alpha: -0.8);
} }
} }
&.is-checked { &.is-checked {
.Checkbox-box { .Checkbox-box {
background-color: #{$oc-blue-1}; background-color: #{$color-blue-1};
svg { svg {
display: block; display: block;
} }
} }
&:hover .Checkbox-box { &:hover .Checkbox-box {
background-color: #{$oc-blue-2}; background-color: #{$color-blue-2};
} }
} }
@@ -58,16 +59,16 @@
align-items: center; align-items: center;
justify-content: center; justify-content: center;
box-shadow: 0 0 0 2px #{$oc-blue-7}; box-shadow: 0 0 0 2px #{$color-blue-7};
background-color: transparent; background-color: transparent;
border-radius: 4px; border-radius: 4px;
color: #{$oc-blue-7}; color: #{$color-blue-7};
border: 0; border: 0;
&:focus { &:focus {
box-shadow: 0 0 0 3px #{$oc-blue-7}; box-shadow: 0 0 0 3px #{$color-blue-7};
} }
svg { svg {
@@ -1,9 +1,7 @@
import clsx from "clsx"; import clsx from "clsx";
import { useCallback, useEffect, useRef, useState } from "react"; import { useCallback, useEffect, useRef, useState } from "react";
import { isTransparent, KEYS } from "@excalidraw/common"; import { KEYS, normalizeInputColor } from "@excalidraw/common";
import tinycolor from "tinycolor2";
import { getShortcutKey } from "../..//shortcut"; import { getShortcutKey } from "../..//shortcut";
import { useAtom } from "../../editor-jotai"; import { useAtom } from "../../editor-jotai";
@@ -16,29 +14,6 @@ import { activeColorPickerSectionAtom } from "./colorPickerUtils";
import type { ColorPickerType } from "./colorPickerUtils"; import type { ColorPickerType } from "./colorPickerUtils";
/**
* tries to keep the input color as-is if it's valid, making minimal adjustments
* (trimming whitespace or adding `#` to hex colors)
*/
export const normalizeInputColor = (color: string): string | null => {
color = color.trim();
if (isTransparent(color)) {
return color;
}
const tc = tinycolor(color);
if (tc.isValid()) {
// testing for `#` first fixes a bug on Electron (more specfically, an
// Obsidian popout window), where a hex color without `#` is considered valid
if (tc.getFormat() === "hex" && !color.startsWith("#")) {
return `#${color}`;
}
return color;
}
return null;
};
export const ColorInput = ({ export const ColorInput = ({
color, color,
onChange, onChange,
@@ -1,4 +1,5 @@
@import "../../css/variables.module.scss"; @use "sass:color";
@use "../../css/variables.module" as *;
.excalidraw { .excalidraw {
.focus-visible-none { .focus-visible-none {
@@ -185,8 +186,8 @@
.color-picker { .color-picker {
background: var(--popup-bg-color); background: var(--popup-bg-color);
border: 0 solid transparentize($oc-white, 0.75); border: 0 solid color.adjust(#fff, $alpha: -0.75);
box-shadow: transparentize($oc-black, 0.75) 0 1px 4px; box-shadow: color.adjust(#000, $alpha: -0.75) 0 1px 4px;
border-radius: 4px; border-radius: 4px;
position: absolute; position: absolute;
@@ -243,7 +244,7 @@
} }
.color-picker-triangle-shadow { .color-picker-triangle-shadow {
border-color: transparent transparent transparentize($oc-black, 0.9); border-color: transparent transparent color.adjust(#000, $alpha: -0.9);
:root[dir="ltr"] & { :root[dir="ltr"] & {
left: -14px; left: -14px;
@@ -280,7 +281,7 @@
padding: 0.25rem; padding: 0.25rem;
&-title { &-title {
color: $oc-gray-6; color: $color-gray-6;
font-size: 12px; font-size: 12px;
padding: 0 0.25rem; padding: 0 0.25rem;
} }
@@ -319,7 +320,7 @@
.color-picker-transparent { .color-picker-transparent {
border-radius: 4px; border-radius: 4px;
box-shadow: transparentize($oc-black, 0.9) 0 0 0 1px inset; box-shadow: color.adjust(#000, $alpha: -0.9) 0 0 0 1px inset;
position: absolute; position: absolute;
top: 0; top: 0;
right: 0; right: 0;
@@ -473,7 +474,7 @@
} }
.color-picker-type-elementBackground .color-picker-keybinding { .color-picker-type-elementBackground .color-picker-keybinding {
color: $oc-white; color: #fff;
} }
.color-picker-swatch[aria-label="transparent"] .color-picker-keybinding { .color-picker-swatch[aria-label="transparent"] .color-picker-keybinding {
@@ -486,10 +487,10 @@
&.theme--dark { &.theme--dark {
.color-picker-type-elementBackground .color-picker-keybinding { .color-picker-type-elementBackground .color-picker-keybinding {
color: $oc-black; color: #000;
} }
.color-picker-swatch[aria-label="transparent"] .color-picker-keybinding { .color-picker-swatch[aria-label="transparent"] .color-picker-keybinding {
color: $oc-black; color: #000;
} }
} }
} }
@@ -5,6 +5,7 @@ import { useRef, useEffect } from "react";
import { import {
COLOR_OUTLINE_CONTRAST_THRESHOLD, COLOR_OUTLINE_CONTRAST_THRESHOLD,
COLOR_PALETTE, COLOR_PALETTE,
isColorDark,
isWritableElement, isWritableElement,
} from "@excalidraw/common"; } from "@excalidraw/common";
@@ -29,7 +30,7 @@ import { ColorInput } from "./ColorInput";
import { Picker } from "./Picker"; import { Picker } from "./Picker";
import PickerHeading from "./PickerHeading"; import PickerHeading from "./PickerHeading";
import { TopPicks } from "./TopPicks"; import { TopPicks } from "./TopPicks";
import { activeColorPickerSectionAtom, isColorDark } from "./colorPickerUtils"; import { activeColorPickerSectionAtom } from "./colorPickerUtils";
import "./ColorPicker.scss"; import "./ColorPicker.scss";
@@ -1,6 +1,5 @@
import React from "react"; import React from "react";
import { isColorDark } from "@excalidraw/common";
import { isColorDark } from "./colorPickerUtils";
interface HotkeyLabelProps { interface HotkeyLabelProps {
color: string; color: string;
@@ -5,10 +5,9 @@ import {
DEFAULT_CANVAS_BACKGROUND_PICKS, DEFAULT_CANVAS_BACKGROUND_PICKS,
DEFAULT_ELEMENT_BACKGROUND_PICKS, DEFAULT_ELEMENT_BACKGROUND_PICKS,
DEFAULT_ELEMENT_STROKE_PICKS, DEFAULT_ELEMENT_STROKE_PICKS,
isColorDark,
} from "@excalidraw/common"; } from "@excalidraw/common";
import { isColorDark } from "./colorPickerUtils";
import type { ColorPickerType } from "./colorPickerUtils"; import type { ColorPickerType } from "./colorPickerUtils";
interface TopPicksProps { interface TopPicksProps {
@@ -1,8 +1,4 @@
import { import { MAX_CUSTOM_COLORS_USED_IN_CANVAS } from "@excalidraw/common";
isTransparent,
MAX_CUSTOM_COLORS_USED_IN_CANVAS,
tinycolor,
} from "@excalidraw/common";
import type { ExcalidrawElement } from "@excalidraw/element/types"; import type { ExcalidrawElement } from "@excalidraw/element/types";
@@ -100,32 +96,6 @@ export type ActiveColorPickerSectionAtomType =
export const activeColorPickerSectionAtom = export const activeColorPickerSectionAtom =
atom<ActiveColorPickerSectionAtomType>(null); atom<ActiveColorPickerSectionAtomType>(null);
const calculateContrast = (r: number, g: number, b: number): number => {
const yiq = (r * 299 + g * 587 + b * 114) / 1000;
return yiq;
};
// YIQ algo, inspiration from https://stackoverflow.com/a/11868398
export const isColorDark = (color: string, threshold = 160): boolean => {
// no color ("") -> assume it default to black
if (!color) {
return true;
}
if (isTransparent(color)) {
return false;
}
const tc = tinycolor(color);
if (!tc.isValid()) {
// invalid color -> assume it defaults to black
return true;
}
const { r, g, b } = tc.toRgb();
return calculateContrast(r, g, b) < threshold;
};
export type ColorPickerType = export type ColorPickerType =
| "canvasBackground" | "canvasBackground"
| "elementBackground" | "elementBackground"
@@ -1,4 +1,4 @@
@import "../../css/variables.module.scss"; @use "../../css/variables.module" as *;
$verticalBreakpoint: 861px; $verticalBreakpoint: 861px;
@@ -1,4 +1,4 @@
@import "../css/variables.module.scss"; @use "../css/variables.module" as *;
.excalidraw { .excalidraw {
.confirm-dialog { .confirm-dialog {
@@ -1,4 +1,5 @@
@import "../css/variables.module.scss"; @use "sass:color";
@use "../css/variables.module" as *;
.excalidraw { .excalidraw {
.context-menu-popover { .context-menu-popover {
@@ -8,7 +9,7 @@
.context-menu { .context-menu {
position: relative; position: relative;
border-radius: 4px; border-radius: 4px;
box-shadow: 0 3px 10px transparentize($oc-black, 0.8); box-shadow: 0 3px 10px color.adjust(#000, $alpha: -0.8);
padding: 0; padding: 0;
list-style: none; list-style: none;
user-select: none; user-select: none;
@@ -49,7 +50,7 @@
&.dangerous { &.dangerous {
.context-menu-item__label { .context-menu-item__label {
color: $oc-red-7; color: $color-red-7;
} }
} }
@@ -73,7 +74,7 @@
.context-menu-item__label { .context-menu-item__label {
color: var(--popup-bg-color); color: var(--popup-bg-color);
} }
background-color: $oc-red-6; background-color: $color-red-6;
} }
} }
@@ -97,6 +98,6 @@
.context-menu-item-separator { .context-menu-item-separator {
border: none; border: none;
border-top: 1px solid $oc-gray-5; border-top: 1px solid $color-gray-5;
} }
} }
@@ -1,4 +1,4 @@
@import "../css//variables.module.scss"; @use "../css/variables.module" as *;
.excalidraw { .excalidraw {
.ConvertElementTypePopup { .ConvertElementTypePopup {
+1 -1
View File
@@ -1,4 +1,4 @@
@import "../css/variables.module.scss"; @use "../css/variables.module" as *;
.excalidraw { .excalidraw {
.Dialog { .Dialog {
@@ -1,4 +1,4 @@
@import "../css/variables.module.scss"; @use "../css/variables.module" as *;
.excalidraw { .excalidraw {
.ElementLinkDialog { .ElementLinkDialog {
@@ -59,7 +59,7 @@
} }
.ElementLinkDialog__remove { .ElementLinkDialog__remove {
color: $oc-red-9; color: $color-red-9;
margin-left: 1rem; margin-left: 1rem;
.ToolIcon__icon { .ToolIcon__icon {
@@ -68,7 +68,7 @@
} }
.ToolIcon__icon svg { .ToolIcon__icon svg {
color: $oc-red-6; color: $color-red-6;
} }
} }
} }
@@ -1,4 +1,4 @@
@import "../css/variables.module.scss"; @use "../css/variables.module" as *;
.excalidraw { .excalidraw {
.ExportDialog__preview { .ExportDialog__preview {
@@ -112,7 +112,7 @@
font-family: Cascadia; font-family: Cascadia;
font-size: 1.8em; font-size: 1.8em;
color: $oc-white; color: #fff;
&:hover { &:hover {
background-color: var(--button-color-darker); background-color: var(--button-color-darker);
@@ -1,4 +1,4 @@
@import "../css/variables.module.scss"; @use "../css/variables.module" as *;
@keyframes successStatusAnimation { @keyframes successStatusAnimation {
0% { 0% {
@@ -24,6 +24,14 @@
background-color: var(--back-color); background-color: var(--back-color);
border-color: var(--border-color); border-color: var(--border-color);
border-radius: 0.5rem;
border-width: 1px;
border-style: solid;
font-family: var(--font-family);
user-select: none;
&:hover { &:hover {
transition: all 150ms ease-out; transition: all 150ms ease-out;
} }
@@ -266,14 +274,6 @@
} }
} }
border-radius: 0.5rem;
border-width: 1px;
border-style: solid;
font-family: var(--font-family);
user-select: none;
&--size-large { &--size-large {
font-weight: 600; font-weight: 600;
font-size: 0.875rem; font-size: 0.875rem;
@@ -1,4 +1,4 @@
@import "../css/variables.module.scss"; @use "../css/variables.module" as *;
.excalidraw { .excalidraw {
.FixedSideContainer { .FixedSideContainer {
@@ -1,4 +1,4 @@
@import "../../css/variables.module.scss"; @use "../../css/variables.module" as *;
.excalidraw { .excalidraw {
.FontPicker__container { .FontPicker__container {
@@ -1,4 +1,4 @@
@import "../css/variables.module.scss"; @use "../css/variables.module" as *;
.excalidraw { .excalidraw {
.HelpDialog { .HelpDialog {
@@ -60,11 +60,12 @@
&__islands-container { &__islands-container {
display: grid; display: grid;
grid-column-gap: 1.5rem;
grid-row-gap: 2rem;
@media screen and (min-width: 1024px) { @media screen and (min-width: 1024px) {
grid-template-columns: 1fr 1fr; grid-template-columns: 1fr 1fr;
} }
grid-column-gap: 1.5rem;
grid-row-gap: 2rem;
} }
@media screen and (min-width: 1024px) { @media screen and (min-width: 1024px) {
@@ -1,4 +1,4 @@
@import "../css/variables.module.scss"; @use "../css/variables.module" as *;
// this is loosely based on the longest hint text // this is loosely based on the longest hint text
$wide-viewport-width: 1000px; $wide-viewport-width: 1000px;
@@ -1,10 +1,11 @@
@import "../css/variables.module.scss"; @use "sass:color";
@use "../css/variables.module" as *;
.excalidraw { .excalidraw {
.picker { .picker {
padding: 0.5rem; padding: 0.5rem;
background: var(--popup-bg-color); background: var(--popup-bg-color);
border: 0 solid transparentize($oc-white, 0.75); border: 0 solid color.adjust(#fff, $alpha: -0.75);
box-shadow: var(--shadow-island); box-shadow: var(--shadow-island);
border-radius: 4px; border-radius: 4px;
position: absolute; position: absolute;
@@ -87,7 +88,7 @@
} }
.picker-type-elementBackground .picker-keybinding { .picker-type-elementBackground .picker-keybinding {
color: $oc-white; color: #fff;
} }
.picker-swatch[aria-label="transparent"] .picker-keybinding { .picker-swatch[aria-label="transparent"] .picker-keybinding {
@@ -100,10 +101,10 @@
&.theme--dark { &.theme--dark {
.picker-type-elementBackground .picker-keybinding { .picker-type-elementBackground .picker-keybinding {
color: $oc-black; color: #000;
} }
.picker-swatch[aria-label="transparent"] .picker-keybinding { .picker-swatch[aria-label="transparent"] .picker-keybinding {
color: $oc-black; color: #000;
} }
} }
} }
@@ -1,4 +1,4 @@
@import "../css/variables.module.scss"; @use "../css/variables.module" as *;
.excalidraw { .excalidraw {
--ImageExportModal-preview-border: #d6d6d6; --ImageExportModal-preview-border: #d6d6d6;
+1 -2
View File
@@ -1,5 +1,4 @@
@import "open-color/open-color"; @use "../css/variables.module" as *;
@import "../css/variables.module.scss";
.excalidraw { .excalidraw {
.layer-ui__wrapper.animate { .layer-ui__wrapper.animate {
@@ -1,4 +1,4 @@
@import "open-color/open-color"; @use "../css/variables.module" as *;
.excalidraw { .excalidraw {
.layer-ui__library { .layer-ui__library {
@@ -46,15 +46,15 @@
} }
&-close.ToolIcon_type_button { &-close.ToolIcon_type_button {
background-color: $oc-blue-6; background-color: $color-blue-6;
align-self: flex-end; align-self: flex-end;
&:hover { &:hover {
background-color: $oc-blue-8; background-color: $color-blue-8;
} }
.ToolIcon__icon { .ToolIcon__icon {
width: auto; width: auto;
font-size: 1rem; font-size: 1rem;
color: $oc-white; color: #fff;
padding: 0 0.5rem; padding: 0 0.5rem;
} }
} }
@@ -90,7 +90,7 @@
border-radius: var(--border-radius-lg); border-radius: var(--border-radius-lg);
background-color: var(--color-primary); background-color: var(--color-primary);
color: $oc-white; color: #fff;
text-align: center; text-align: center;
white-space: nowrap; white-space: nowrap;
text-decoration: none !important; text-decoration: none !important;
@@ -1,4 +1,4 @@
@import "open-color/open-color"; @use "../css/variables.module" as *;
.excalidraw { .excalidraw {
--container-padding-y: 1rem; --container-padding-y: 1rem;
@@ -1,4 +1,4 @@
@import "../css/variables.module.scss"; @use "../css/variables.module" as *;
.excalidraw { .excalidraw {
.library-unit { .library-unit {
@@ -1,5 +1,4 @@
@import "open-color/open-color.scss"; @use "../css/variables.module" as *;
@import "../css/variables.module.scss";
.excalidraw { .excalidraw {
.mobile-toolbar { .mobile-toolbar {
+1 -1
View File
@@ -1,4 +1,4 @@
@import "../css/variables.module.scss"; @use "../css/variables.module" as *;
.excalidraw { .excalidraw {
&.excalidraw-modal-container { &.excalidraw-modal-container {
@@ -1,4 +1,4 @@
@import "../../css/variables.module.scss"; @use "../../css/variables.module" as *;
.excalidraw { .excalidraw {
.OverwriteConfirm { .OverwriteConfirm {
@@ -1,4 +1,4 @@
@import "../css/variables.module.scss"; @use "../css/variables.module" as *;
.excalidraw { .excalidraw {
.PasteChartDialog { .PasteChartDialog {
@@ -25,7 +25,7 @@
height: 128px; height: 128px;
border-radius: 2px; border-radius: 2px;
padding: 1px; padding: 1px;
border: 1px solid $oc-gray-4; border: 1px solid $color-gray-4;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
@@ -39,7 +39,7 @@
} }
&:hover { &:hover {
padding: 0; padding: 0;
border: 2px solid $oc-blue-5; border: 2px solid $color-blue-5;
} }
} }
} }
@@ -1,4 +1,3 @@
import oc from "open-color";
import React, { useLayoutEffect, useRef, useState } from "react"; import React, { useLayoutEffect, useRef, useState } from "react";
import type { ChartType } from "@excalidraw/element/types"; import type { ChartType } from "@excalidraw/element/types";
@@ -49,7 +48,7 @@ const ChartPreviewBtn = (props: {
elements, elements,
{ {
exportBackground: false, exportBackground: false,
viewBackgroundColor: oc.white, viewBackgroundColor: "#fff",
}, },
null, // files null, // files
{ {
@@ -1,4 +1,4 @@
@import "../css/variables.module.scss"; @use "../css/variables.module" as *;
.excalidraw { .excalidraw {
.publish-library { .publish-library {
@@ -14,7 +14,7 @@
span { span {
font-weight: 500; font-weight: 500;
font-size: 1rem; font-size: 1rem;
color: $oc-gray-6; color: $color-gray-6;
} }
input, input,
textarea { textarea {
@@ -24,7 +24,7 @@
} }
.required { .required {
color: $oc-red-8; color: $color-red-8;
margin: 0.2rem; margin: 0.2rem;
} }
} }
@@ -48,22 +48,22 @@
} }
&--confirm.ToolIcon_type_button { &--confirm.ToolIcon_type_button {
background-color: $oc-blue-6; background-color: $color-blue-6;
&:hover { &:hover {
background-color: $oc-blue-8; background-color: $color-blue-8;
} }
} }
&--cancel.ToolIcon_type_button { &--cancel.ToolIcon_type_button {
background-color: $oc-gray-5; background-color: $color-gray-5;
&:hover { &:hover {
background-color: $oc-gray-6; background-color: $color-gray-6;
} }
} }
.ToolIcon__icon { .ToolIcon__icon {
color: $oc-white; color: #fff;
.Spinner { .Spinner {
--spinner-color: #fff; --spinner-color: #fff;
svg { svg {
@@ -83,7 +83,7 @@
} }
&-warning { &-warning {
color: $oc-red-6; color: $color-red-6;
} }
&-note { &-note {
@@ -102,14 +102,14 @@
top: 0.3rem; top: 0.3rem;
left: 0.3rem; left: 0.3rem;
font-size: 0.7rem; font-size: 0.7rem;
color: $oc-red-7; color: $color-red-7;
background: rgba(255, 255, 255, 0.9); background: rgba(255, 255, 255, 0.9);
padding: 0.1rem 0.2rem; padding: 0.1rem 0.2rem;
border-radius: 0.2rem; border-radius: 0.2rem;
} }
&__svg { &__svg {
background-color: $oc-white; background-color: #fff;
padding: 0.3rem; padding: 0.3rem;
width: 7.5rem; width: 7.5rem;
height: 7.5rem; height: 7.5rem;
@@ -121,7 +121,7 @@
} }
.ToolIcon__icon { .ToolIcon__icon {
background-color: $oc-white; background-color: #fff;
width: auto; width: auto;
height: auto; height: auto;
margin: 0 0.5rem; margin: 0 0.5rem;
@@ -132,7 +132,7 @@
} }
.required, .required,
.error { .error {
color: $oc-red-8; color: $color-red-8;
font-weight: 700; font-weight: 700;
font-size: 1rem; font-size: 1rem;
margin: 0.2rem; margin: 0.2rem;
@@ -152,16 +152,16 @@
margin: 0; margin: 0;
} }
.ToolIcon__icon { .ToolIcon__icon {
background-color: $oc-red-6; background-color: $color-red-6;
&:hover { &:hover {
background-color: $oc-red-7; background-color: $color-red-7;
} }
&:active { &:active {
background-color: $oc-red-8; background-color: $color-red-8;
} }
} }
svg { svg {
color: $oc-white; color: #fff;
padding: 0.26rem; padding: 0.26rem;
border-radius: 0.3em; border-radius: 0.3em;
width: 1rem; width: 1rem;
@@ -1,5 +1,4 @@
import { exportToCanvas, exportToSvg } from "@excalidraw/utils/export"; import { exportToCanvas, exportToSvg } from "@excalidraw/utils/export";
import OpenColor from "open-color";
import { useCallback, useEffect, useRef, useState } from "react"; import { useCallback, useEffect, useRef, useState } from "react";
import { import {
@@ -57,7 +56,7 @@ const generatePreviewImage = async (libraryItems: LibraryItems) => {
const ctx = canvas.getContext("2d")!; const ctx = canvas.getContext("2d")!;
ctx.fillStyle = OpenColor.white; ctx.fillStyle = "#fff";
ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.fillRect(0, 0, canvas.width, canvas.height);
// draw items // draw items
@@ -87,7 +86,7 @@ const generatePreviewImage = async (libraryItems: LibraryItems) => {
// draw item border // draw item border
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
ctx.lineWidth = BORDER_WIDTH; ctx.lineWidth = BORDER_WIDTH;
ctx.strokeStyle = OpenColor.gray[4]; ctx.strokeStyle = "#ced4da";
ctx.strokeRect( ctx.strokeRect(
colOffset + BOX_PADDING / 2, colOffset + BOX_PADDING / 2,
rowOffset + BOX_PADDING / 2, rowOffset + BOX_PADDING / 2,
@@ -131,7 +130,7 @@ const SingleLibraryItem = ({
elements: libItem.elements, elements: libItem.elements,
appState: { appState: {
...appState, ...appState,
viewBackgroundColor: OpenColor.white, viewBackgroundColor: "#fff",
exportBackground: true, exportBackground: true,
}, },
files: null, files: null,
@@ -175,7 +174,7 @@ const SingleLibraryItem = ({
}} }}
> >
<div style={{ padding: "0.5em 0" }}> <div style={{ padding: "0.5em 0" }}>
<span style={{ fontWeight: 500, color: OpenColor.gray[6] }}> <span style={{ fontWeight: 500, color: "#868e96" }}>
{t("publishDialog.itemName")} {t("publishDialog.itemName")}
</span> </span>
<span aria-hidden="true" className="required"> <span aria-hidden="true" className="required">
@@ -1,4 +1,4 @@
@import "../css/variables.module.scss"; @use "../css/variables.module" as *;
.excalidraw { .excalidraw {
--RadioGroup-background: var(--island-bg-color); --RadioGroup-background: var(--island-bg-color);
+1 -1
View File
@@ -1,4 +1,4 @@
@import "../css/variables.module.scss"; @use "../css/variables.module" as *;
.excalidraw { .excalidraw {
--slider-thumb-size: 16px; --slider-thumb-size: 16px;
+1 -1
View File
@@ -1,4 +1,4 @@
@import "../css/variables.module.scss"; @use "../css/variables.module" as *;
.excalidraw { .excalidraw {
.SVGLayer { .SVGLayer {
@@ -1,5 +1,4 @@
@import "open-color/open-color"; @use "../css/variables.module" as *;
@import "../css//variables.module.scss";
.excalidraw { .excalidraw {
.layer-ui__search { .layer-ui__search {
@@ -1,4 +1,4 @@
@import "../css/variables.module.scss"; @use "../css/variables.module" as *;
.excalidraw { .excalidraw {
.ShareableLinkDialog { .ShareableLinkDialog {
@@ -1,5 +1,4 @@
@import "open-color/open-color"; @use "../../css/variables.module" as *;
@import "../../css/variables.module.scss";
.excalidraw { .excalidraw {
.sidebar { .sidebar {
@@ -19,6 +18,12 @@
pointer-events: var(--ui-pointerEvents); pointer-events: var(--ui-pointerEvents);
overflow: hidden;
border-radius: 0;
width: calc(var(--right-sidebar-width) - var(--space-factor) * 2);
border-left: 1px solid var(--sidebar-border-color);
:root[dir="rtl"] & { :root[dir="rtl"] & {
left: 0; left: 0;
right: auto; right: auto;
@@ -28,12 +33,6 @@
box-shadow: none; box-shadow: none;
} }
overflow: hidden;
border-radius: 0;
width: calc(var(--right-sidebar-width) - var(--space-factor) * 2);
border-left: 1px solid var(--sidebar-border-color);
:root[dir="rtl"] & { :root[dir="rtl"] & {
border-right: 1px solid var(--sidebar-border-color); border-right: 1px solid var(--sidebar-border-color);
border-left: 0; border-left: 0;
@@ -1,4 +1,4 @@
@import "../../css/variables.module.scss"; @use "../../css/variables.module" as *;
.excalidraw { .excalidraw {
.sidebar-trigger { .sidebar-trigger {
+1 -1
View File
@@ -1,4 +1,4 @@
@import "open-color/open-color.scss"; @use "../css/variables.module" as *;
$duration: 1.6s; $duration: 1.6s;
+1 -1
View File
@@ -1,4 +1,4 @@
@import "../css/variables.module.scss"; @use "../css/variables.module" as *;
.excalidraw { .excalidraw {
--Switch-disabled-color: var(--color-border-outline); --Switch-disabled-color: var(--color-border-outline);
@@ -1,4 +1,4 @@
@import "../../css/variables.module.scss"; @use "../../css/variables.module" as *;
$verticalBreakpoint: 861px; $verticalBreakpoint: 861px;
@@ -241,7 +241,7 @@ $verticalBreakpoint: 861px;
height: 2.5rem; height: 2.5rem;
font-size: 12px; font-size: 12px;
color: $oc-white; color: #fff;
background-color: var(--color-primary); background-color: var(--color-primary);
width: 100%; width: 100%;
@@ -271,7 +271,9 @@ $verticalBreakpoint: 861px;
} }
} }
position: relative; & {
position: relative;
}
div { div {
display: contents; display: contents;
@@ -1,4 +1,4 @@
@import "../css/variables.module.scss"; @use "../css/variables.module" as *;
.excalidraw { .excalidraw {
--ExcTextField--color: var(--color-on-surface); --ExcTextField--color: var(--color-on-surface);
@@ -1,4 +1,4 @@
@import "../css/variables.module.scss"; @use "../css/variables.module" as *;
.excalidraw { .excalidraw {
.TextInput { .TextInput {
+1 -1
View File
@@ -1,4 +1,4 @@
@import "../css/variables.module.scss"; @use "../css/variables.module" as *;
.excalidraw { .excalidraw {
.Toast { .Toast {
+1 -2
View File
@@ -1,5 +1,4 @@
@import "open-color/open-color.scss"; @use "../css/variables.module" as *;
@import "../css/variables.module.scss";
.excalidraw { .excalidraw {
.ToolIcon { .ToolIcon {
@@ -1,4 +1,4 @@
@import "../css/variables.module.scss"; @use "../css/variables.module" as *;
.excalidraw { .excalidraw {
.tool-popover-content { .tool-popover-content {
+1 -2
View File
@@ -1,5 +1,4 @@
@import "open-color/open-color.scss"; @use "../css/variables.module" as *;
@import "../css/variables.module.scss";
.excalidraw { .excalidraw {
.App-toolbar { .App-toolbar {
+3 -3
View File
@@ -1,4 +1,4 @@
@import "../css/variables.module.scss"; @use "../css/variables.module" as *;
// container in body where the actual tooltip is appended to // container in body where the actual tooltip is appended to
.excalidraw-tooltip { .excalidraw-tooltip {
@@ -14,13 +14,13 @@
pointer-events: none; pointer-events: none;
word-wrap: break-word; word-wrap: break-word;
background: $oc-black; background: #000;
line-height: 1.5; line-height: 1.5;
text-align: center; text-align: center;
font-size: 13px; font-size: 13px;
font-weight: 500; font-weight: 500;
color: $oc-white; color: #fff;
display: none; display: none;
@@ -1,4 +1,4 @@
@import "../css/variables.module.scss"; @use "../css/variables.module" as *;
.excalidraw { .excalidraw {
.UnlockPopup { .UnlockPopup {
+23 -20
View File
@@ -1,10 +1,22 @@
@import "../css/variables.module.scss"; @use "../css/variables.module" as *;
.excalidraw { .excalidraw {
--avatar-size: 1.75rem; --avatar-size: 1.75rem;
--avatarList-gap: 0.625rem; --avatarList-gap: 0.625rem;
--userList-padding: var(--space-factor); --userList-padding: var(--space-factor);
--userlist-hint-bg-color: var(--color-gray-10);
--userlist-hint-heading-color: var(--color-gray-80);
--userlist-hint-text-color: var(--color-gray-60);
--userlist-collaborators-border-color: var(--color-gray-20);
&.theme--dark {
--userlist-hint-bg-color: var(--color-gray-90);
--userlist-hint-heading-color: var(--color-gray-30);
--userlist-hint-text-color: var(--color-gray-40);
--userlist-collaborators-border-color: var(--color-gray-80);
}
.UserList__wrapper { .UserList__wrapper {
display: flex; display: flex;
width: 100%; width: 100%;
@@ -50,13 +62,16 @@
.UserList__more { .UserList__more {
@include avatarStyles; @include avatarStyles;
background-color: var(--color-gray-20);
border: 0 !important; & {
font-size: 0.625rem; background-color: var(--color-gray-20);
font-weight: 400; border: 0 !important;
flex-shrink: 0; font-size: 0.625rem;
color: var(--color-gray-100); font-weight: 400;
font-weight: bold; flex-shrink: 0;
color: var(--color-gray-100);
font-weight: bold;
}
} }
.UserList__collaborator-name { .UserList__collaborator-name {
@@ -141,18 +156,6 @@
} }
} }
--userlist-hint-bg-color: var(--color-gray-10);
--userlist-hint-heading-color: var(--color-gray-80);
--userlist-hint-text-color: var(--color-gray-60);
--userlist-collaborators-border-color: var(--color-gray-20);
&.theme--dark {
--userlist-hint-bg-color: var(--color-gray-90);
--userlist-hint-heading-color: var(--color-gray-30);
--userlist-hint-text-color: var(--color-gray-40);
--userlist-collaborators-border-color: var(--color-gray-80);
}
.UserList__collaborators { .UserList__collaborators {
top: auto; top: auto;
max-height: 50vh; max-height: 50vh;
@@ -1,4 +1,4 @@
@import "../../css/variables.module.scss"; @use "../../css/variables.module" as *;
.excalidraw { .excalidraw {
.dropdown-menu { .dropdown-menu {
@@ -1,11 +1,12 @@
.footer-center { .footer-center {
pointer-events: none; pointer-events: none;
& > * {
pointer-events: var(--ui-pointerEvents);
}
display: flex; display: flex;
width: 100%; width: 100%;
justify-content: flex-start; justify-content: flex-start;
margin-inline-end: 0.6rem; margin-inline-end: 0.6rem;
& > * {
pointer-events: var(--ui-pointerEvents);
}
} }
@@ -1,4 +1,4 @@
@import "../../css/variables.module.scss"; @use "../../css/variables.module" as *;
.excalidraw-hyperlinkContainer { .excalidraw-hyperlinkContainer {
display: flex; display: flex;
@@ -47,16 +47,16 @@
} }
button { button {
color: $oc-blue-6; color: $color-blue-6;
background-color: transparent !important; background-color: transparent !important;
font-weight: 500; font-weight: 500;
&.excalidraw-hyperlinkContainer--remove { &.excalidraw-hyperlinkContainer--remove {
color: $oc-red-9; color: $color-red-9;
} }
} }
&--remove .ToolIcon__icon svg { &--remove .ToolIcon__icon svg {
color: $oc-red-6; color: $color-red-6;
} }
.ToolIcon__icon { .ToolIcon__icon {
+1 -2
View File
@@ -7,7 +7,6 @@
// to determine whether or not the icons should be mirrored in right-to-left languages. // to determine whether or not the icons should be mirrored in right-to-left languages.
import clsx from "clsx"; import clsx from "clsx";
import oc from "open-color";
import React from "react"; import React from "react";
import { THEME } from "@excalidraw/common"; import { THEME } from "@excalidraw/common";
@@ -17,7 +16,7 @@ import type { Theme } from "@excalidraw/element/types";
export const iconFillColor = (theme: Theme) => "var(--icon-fill-color)"; export const iconFillColor = (theme: Theme) => "var(--icon-fill-color)";
const handlerColor = (theme: Theme) => const handlerColor = (theme: Theme) =>
theme === THEME.LIGHT ? oc.white : "#1e1e1e"; theme === THEME.LIGHT ? "#fff" : "#1e1e1e";
type Opts = { type Opts = {
width?: number; width?: number;
@@ -1,4 +1,4 @@
@import "../../css/variables.module.scss"; @use "../../css/variables.module" as *;
.excalidraw { .excalidraw {
.collab-button { .collab-button {
@@ -58,8 +58,8 @@
bottom: -5px; bottom: -5px;
padding: 3px; padding: 3px;
border-radius: 50%; border-radius: 50%;
background-color: $oc-green-2; background-color: $color-green-2;
color: $oc-green-9; color: $color-green-9;
font-size: 0.6rem; font-size: 0.6rem;
font-family: "Cascadia"; font-family: "Cascadia";
} }
-2
View File
@@ -1,5 +1,3 @@
@import "open-color/open-color.scss";
.visually-hidden { .visually-hidden {
position: absolute !important; position: absolute !important;
height: 1px; height: 1px;
+5 -5
View File
@@ -1,5 +1,5 @@
@import "./variables.module.scss"; @use "./variables.module" as *;
@import "./theme"; @use "./theme";
:root { :root {
--zIndex-canvas: 1; --zIndex-canvas: 1;
@@ -210,7 +210,7 @@ body.excalidraw-cursor-resize * {
.divider { .divider {
width: 1px; width: 1px;
background-color: $oc-gray-2; background-color: $color-gray-2;
margin: 1px; margin: 1px;
} }
@@ -729,8 +729,8 @@ body.excalidraw-cursor-resize * {
justify-content: center; justify-content: center;
padding: 40px; padding: 40px;
background-color: $oc-red-1; background-color: $color-red-1;
border: 3px solid $oc-red-9; border: 3px solid $color-red-9;
} }
.ErrorSplash-paragraph { .ErrorSplash-paragraph {
+44 -43
View File
@@ -1,35 +1,36 @@
@import "open-color/open-color.scss"; @use "sass:color";
@import "./variables.module.scss"; @use "./variables.module" as *;
@forward "./variables.module";
.excalidraw { .excalidraw {
--theme-filter: none; --theme-filter: none;
--button-destructive-bg-color: #{$oc-red-1}; --button-destructive-bg-color: #{$color-red-1};
--button-destructive-color: #{$oc-red-9}; --button-destructive-color: #{$color-red-9};
--button-gray-1: #{$oc-gray-2}; --button-gray-1: #{$color-gray-2};
--button-gray-2: #{$oc-gray-4}; --button-gray-2: #{$color-gray-4};
--button-gray-3: #{$oc-gray-5}; --button-gray-3: #{$color-gray-5};
--mobile-action-button-bg: rgba(255, 255, 255, 0.35); --mobile-action-button-bg: rgba(255, 255, 255, 0.35);
--mobile-color-border: var(--default-border-color); --mobile-color-border: var(--default-border-color);
--button-special-active-bg-color: #{$oc-green-0}; --button-special-active-bg-color: #{$color-green-0};
--dialog-border-color: var(--color-gray-20); --dialog-border-color: var(--color-gray-20);
--dropdown-icon: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="292.4" height="292.4" viewBox="0 0 292 292"><path d="M287 197L159 69c-4-3-8-5-13-5s-9 2-13 5L5 197c-3 4-5 8-5 13s2 9 5 13c4 4 8 5 13 5h256c5 0 9-1 13-5s5-8 5-13-1-9-5-13z"/></svg>'); --dropdown-icon: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="292.4" height="292.4" viewBox="0 0 292 292"><path d="M287 197L159 69c-4-3-8-5-13-5s-9 2-13 5L5 197c-3 4-5 8-5 13s2 9 5 13c4 4 8 5 13 5h256c5 0 9-1 13-5s5-8 5-13-1-9-5-13z"/></svg>');
--focus-highlight-color: #{$oc-blue-2}; --focus-highlight-color: #{$color-blue-2};
--icon-fill-color: var(--color-on-surface); --icon-fill-color: var(--color-on-surface);
--icon-green-fill-color: #{$oc-green-9}; --icon-green-fill-color: #{$color-green-9};
--default-bg-color: #{$oc-white}; --default-bg-color: #fff;
--input-bg-color: #{$oc-white}; --input-bg-color: #fff;
--input-border-color: #{$oc-gray-4}; --input-border-color: #{$color-gray-4};
--input-hover-bg-color: #{$oc-gray-1}; --input-hover-bg-color: #{$color-gray-1};
--input-label-color: #{$oc-gray-7}; --input-label-color: #{$color-gray-7};
--island-bg-color: #ffffff; --island-bg-color: #ffffff;
--keybinding-color: var(--color-gray-40); --keybinding-color: var(--color-gray-40);
--link-color: #{$oc-blue-7}; --link-color: #{$color-blue-7};
--overlay-bg-color: #{transparentize($oc-white, 0.12)}; --overlay-bg-color: #{color.adjust(#fff, $alpha: -0.12)};
--popup-bg-color: var(--island-bg-color); --popup-bg-color: var(--island-bg-color);
--popup-secondary-bg-color: #{$oc-gray-1}; --popup-secondary-bg-color: #{$color-gray-1};
--popup-text-color: #{$oc-black}; --popup-text-color: #000;
--popup-text-inverted-color: #{$oc-white}; --popup-text-inverted-color: #fff;
--select-highlight-color: #{$oc-blue-5}; --select-highlight-color: #{$color-blue-5};
--shadow-island: 0px 0px 0.9310142993927002px 0px rgba(0, 0, 0, 0.17), --shadow-island: 0px 0px 0.9310142993927002px 0px rgba(0, 0, 0, 0.17),
0px 0px 3.1270833015441895px 0px rgba(0, 0, 0, 0.08), 0px 0px 3.1270833015441895px 0px rgba(0, 0, 0, 0.08),
0px 7px 14px 0px rgba(0, 0, 0, 0.05); 0px 7px 14px 0px rgba(0, 0, 0, 0.05);
@@ -46,17 +47,6 @@
--editor-container-padding: 1rem; --editor-container-padding: 1rem;
--mobile-action-button-size: 2rem; --mobile-action-button-size: 2rem;
@include isMobile {
--editor-container-padding: 0.75rem;
}
@media screen and (min-device-width: 1921px) {
--lg-button-size: 2.5rem;
--lg-icon-size: 1.25rem;
--default-button-size: 2.25rem;
--default-icon-size: 1.25rem;
}
--scrollbar-thumb: var(--button-gray-2); --scrollbar-thumb: var(--button-gray-2);
--scrollbar-thumb-hover: var(--button-gray-3); --scrollbar-thumb-hover: var(--button-gray-3);
@@ -87,7 +77,7 @@
--color-selection: #6965db; --color-selection: #6965db;
--color-icon-white: #{$oc-white}; --color-icon-white: #fff;
--color-primary: #6965db; --color-primary: #6965db;
--color-primary-darker: #5b57d1; --color-primary-darker: #5b57d1;
@@ -170,6 +160,17 @@
--color-badge: #0b6513; --color-badge: #0b6513;
--background-color-badge: #d3ffd2; --background-color-badge: #d3ffd2;
@include isMobile {
--editor-container-padding: 0.75rem;
}
@media screen and (min-device-width: 1921px) {
--lg-button-size: 2.5rem;
--lg-icon-size: 1.25rem;
--default-button-size: 2.25rem;
--default-icon-size: 1.25rem;
}
&.theme--dark { &.theme--dark {
&.theme--dark-background-none { &.theme--dark-background-none {
background: none; background: none;
@@ -179,7 +180,7 @@
&.theme--dark { &.theme--dark {
--theme-filter: invert(93%) hue-rotate(180deg); --theme-filter: invert(93%) hue-rotate(180deg);
--button-destructive-bg-color: #5a0000; --button-destructive-bg-color: #5a0000;
--button-destructive-color: #{$oc-red-3}; --button-destructive-color: #{$color-red-3};
--button-gray-1: #363636; --button-gray-1: #363636;
--button-gray-2: #272727; --button-gray-2: #272727;
@@ -189,21 +190,21 @@
--button-special-active-bg-color: #204624; --button-special-active-bg-color: #204624;
--dialog-border-color: var(--color-gray-80); --dialog-border-color: var(--color-gray-80);
--dropdown-icon: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="292.4" height="292.4" viewBox="0 0 292 292"><path fill="%23ced4da" d="M287 197L159 69c-4-3-8-5-13-5s-9 2-13 5L5 197c-3 4-5 8-5 13s2 9 5 13c4 4 8 5 13 5h256c5 0 9-1 13-5s5-8 5-13-1-9-5-13z"/></svg>'); --dropdown-icon: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="292.4" height="292.4" viewBox="0 0 292 292"><path fill="%23ced4da" d="M287 197L159 69c-4-3-8-5-13-5s-9 2-13 5L5 197c-3 4-5 8-5 13s2 9 5 13c4 4 8 5 13 5h256c5 0 9-1 13-5s5-8 5-13-1-9-5-13z"/></svg>');
--focus-highlight-color: #{$oc-blue-6}; --focus-highlight-color: #{$color-blue-6};
--icon-green-fill-color: #{$oc-green-4}; --icon-green-fill-color: #{$color-green-4};
--default-bg-color: #121212; --default-bg-color: #121212;
--input-bg-color: #121212; --input-bg-color: #121212;
--input-border-color: #2e2e2e; --input-border-color: #2e2e2e;
--input-hover-bg-color: #181818; --input-hover-bg-color: #181818;
--input-label-color: #{$oc-gray-2}; --input-label-color: #{$color-gray-2};
--island-bg-color: #232329; --island-bg-color: #232329;
--keybinding-color: var(--color-gray-60); --keybinding-color: var(--color-gray-60);
--link-color: #{$oc-blue-4}; --link-color: #{$color-blue-4};
--overlay-bg-color: #{transparentize($oc-gray-8, 0.88)}; --overlay-bg-color: #{color.adjust($color-gray-8, $alpha: -0.88)};
--popup-secondary-bg-color: #222; --popup-secondary-bg-color: #222;
--popup-text-color: #{$oc-gray-4}; --popup-text-color: #{$color-gray-4};
--popup-text-inverted-color: #2c2c2c; --popup-text-inverted-color: #2c2c2c;
--select-highlight-color: #{$oc-blue-4}; --select-highlight-color: #{$color-blue-4};
--shadow-island: 0px 0px 0.9310142993927002px 0px rgba(0, 0, 0, 0.17), --shadow-island: 0px 0px 0.9310142993927002px 0px rgba(0, 0, 0, 0.17),
0px 0px 3.1270833015441895px 0px rgba(0, 0, 0, 0.08), 0px 0px 3.1270833015441895px 0px rgba(0, 0, 0, 0.08),
0px 7px 14px 0px rgba(0, 0, 0, 0.05); 0px 7px 14px 0px rgba(0, 0, 0, 0.05);
@@ -216,8 +217,8 @@
0px 2.76726px 2.21381px rgba(0, 0, 0, 0.0196802); 0px 2.76726px 2.21381px rgba(0, 0, 0, 0.0196802);
--avatar-border-color: var(--color-gray-85); --avatar-border-color: var(--color-gray-85);
--scrollbar-thumb: #{$oc-gray-8}; --scrollbar-thumb: #{$color-gray-8};
--scrollbar-thumb-hover: #{$oc-gray-7}; --scrollbar-thumb-hover: #{$color-gray-7};
--color-slider-track: hsl(244, 23%, 39%); --color-slider-track: hsl(244, 23%, 39%);
+28 -2
View File
@@ -1,4 +1,27 @@
@import "open-color/open-color.scss"; $color-red-1: #ffe3e3;
$color-red-3: #ffa8a8;
$color-red-6: #fa5252;
$color-red-7: #f03e3e;
$color-red-8: #e03131;
$color-red-9: #c92a2a;
$color-gray-1: #f1f3f5;
$color-gray-2: #e9ecef;
$color-gray-4: #ced4da;
$color-gray-5: #adb5bd;
$color-gray-6: #868e96;
$color-gray-7: #495057;
$color-gray-8: #343a40;
$color-green-0: #ebfbee;
$color-green-2: #b2f2bb;
$color-green-4: #69db7c;
$color-green-9: #2b8a3e;
$color-blue-1: #d0ebff;
$color-blue-2: #a5d8ff;
$color-blue-4: #4dabf7;
$color-blue-5: #339af0;
$color-blue-6: #228be6;
$color-blue-7: #1c7ed6;
$color-blue-8: #1971c2;
@mixin isMobile() { @mixin isMobile() {
@at-root .excalidraw--mobile#{&} { @at-root .excalidraw--mobile#{&} {
@@ -137,7 +160,10 @@
@mixin outlineButtonIconStyles { @mixin outlineButtonIconStyles {
@include outlineButtonStyles; @include outlineButtonStyles;
padding: 0;
& {
padding: 0;
}
svg { svg {
width: var(--default-icon-size); width: var(--default-icon-size);
+2 -4
View File
@@ -1,5 +1,3 @@
import OpenColor from "open-color";
import { CURSOR_TYPE, MIME_TYPES, THEME } from "@excalidraw/common"; import { CURSOR_TYPE, MIME_TYPES, THEME } from "@excalidraw/common";
import { isHandToolActive, isEraserActive } from "./appState"; import { isHandToolActive, isEraserActive } from "./appState";
@@ -60,9 +58,9 @@ export const setEraserCursor = (
0, 0,
2 * Math.PI, 2 * Math.PI,
); );
context.fillStyle = isDarkTheme ? OpenColor.black : OpenColor.white; context.fillStyle = isDarkTheme ? "#000" : "#fff";
context.fill(); context.fill();
context.strokeStyle = isDarkTheme ? OpenColor.white : OpenColor.black; context.strokeStyle = isDarkTheme ? "#fff" : "#000";
context.stroke(); context.stroke();
previewDataURL = eraserCanvasCache.toDataURL(MIME_TYPES.svg) as DataURL; previewDataURL = eraserCanvasCache.toDataURL(MIME_TYPES.svg) as DataURL;
}; };
-1
View File
@@ -100,7 +100,6 @@
"lodash.debounce": "4.0.8", "lodash.debounce": "4.0.8",
"lodash.throttle": "4.1.1", "lodash.throttle": "4.1.1",
"nanoid": "3.3.3", "nanoid": "3.3.3",
"open-color": "1.9.1",
"pako": "2.0.3", "pako": "2.0.3",
"perfect-freehand": "1.2.0", "perfect-freehand": "1.2.0",
"pica": "7.1.1", "pica": "7.1.1",
@@ -6,7 +6,6 @@ import {
type LocalPoint, type LocalPoint,
type Radians, type Radians,
} from "@excalidraw/math"; } from "@excalidraw/math";
import oc from "open-color";
import { import {
arrayToMap, arrayToMap,
@@ -1301,7 +1300,7 @@ const _renderInteractiveScene = ({
elementsMap, elementsMap,
); );
} }
const selectionColor = renderConfig.selectionColor || oc.black; const selectionColor = renderConfig.selectionColor || "#000";
if (showBoundingBox) { if (showBoundingBox) {
// Optimisation for finding quickly relevant element ids // Optimisation for finding quickly relevant element ids
@@ -1384,7 +1383,7 @@ const _renderInteractiveScene = ({
y2, y2,
selectionColors: groupElements.some((el) => el.locked) selectionColors: groupElements.some((el) => el.locked)
? ["#ced4da"] ? ["#ced4da"]
: [oc.black], : ["#000"],
dashed: true, dashed: true,
cx: x1 + (x2 - x1) / 2, cx: x1 + (x2 - x1) / 2,
cy: y1 + (y2 - y1) / 2, cy: y1 + (y2 - y1) / 2,
@@ -1410,7 +1409,7 @@ const _renderInteractiveScene = ({
context.translate(appState.scrollX, appState.scrollY); context.translate(appState.scrollX, appState.scrollY);
if (selectedElements.length === 1) { if (selectedElements.length === 1) {
context.fillStyle = oc.white; context.fillStyle = "#fff";
const transformHandles = getTransformHandles( const transformHandles = getTransformHandles(
selectedElements[0], selectedElements[0],
appState.zoom, appState.zoom,
@@ -1455,7 +1454,7 @@ const _renderInteractiveScene = ({
) { ) {
const dashedLinePadding = const dashedLinePadding =
(DEFAULT_TRANSFORM_HANDLE_SPACING * 2) / appState.zoom.value; (DEFAULT_TRANSFORM_HANDLE_SPACING * 2) / appState.zoom.value;
context.fillStyle = oc.white; context.fillStyle = "#fff";
const [x1, y1, x2, y2] = getCommonBounds(selectedElements, elementsMap); const [x1, y1, x2, y2] = getCommonBounds(selectedElements, elementsMap);
const initialLineDash = context.getLineDash(); const initialLineDash = context.getLineDash();
context.setLineDash([2 / appState.zoom.value]); context.setLineDash([2 / appState.zoom.value]);
+1 -1
View File
@@ -1,4 +1,4 @@
import { normalizeInputColor } from "../components/ColorPicker/ColorInput"; import { normalizeInputColor } from "@excalidraw/common";
describe("normalizeInputColor", () => { describe("normalizeInputColor", () => {
describe("hex colors", () => { describe("hex colors", () => {
@@ -5,13 +5,13 @@ import { getOriginalContainerHeightFromCache } from "@excalidraw/element";
import { import {
CODES, CODES,
colorToHex,
KEYS, KEYS,
FONT_FAMILY, FONT_FAMILY,
TEXT_ALIGN, TEXT_ALIGN,
THEME, THEME,
VERTICAL_ALIGN, VERTICAL_ALIGN,
applyDarkModeFilter, applyDarkModeFilter,
tinycolor,
} from "@excalidraw/common"; } from "@excalidraw/common";
import type { import type {
@@ -1672,7 +1672,7 @@ describe("textWysiwyg", () => {
// Helper to compare colors (browser may return rgb format) // Helper to compare colors (browser may return rgb format)
const colorsAreEqual = (color1: string, color2: string) => { const colorsAreEqual = (color1: string, color2: string) => {
return tinycolor(color1).toHex() === tinycolor(color2).toHex(); return colorToHex(color1) === colorToHex(color2);
}; };
beforeEach(async () => { beforeEach(async () => {
-1
View File
@@ -51,7 +51,6 @@
"@braintree/sanitize-url": "6.0.2", "@braintree/sanitize-url": "6.0.2",
"@excalidraw/laser-pointer": "1.3.1", "@excalidraw/laser-pointer": "1.3.1",
"browser-fs-access": "0.29.1", "browser-fs-access": "0.29.1",
"open-color": "1.9.1",
"pako": "2.0.3", "pako": "2.0.3",
"perfect-freehand": "1.2.0", "perfect-freehand": "1.2.0",
"png-chunk-text": "1.0.0", "png-chunk-text": "1.0.0",
+45 -1
View File
@@ -1,4 +1,6 @@
const path = require("path"); const path = require("path");
const fs = require("fs");
const { pathToFileURL } = require("url");
const { build } = require("esbuild"); const { build } = require("esbuild");
const { sassPlugin } = require("esbuild-sass-plugin"); const { sassPlugin } = require("esbuild-sass-plugin");
@@ -16,6 +18,44 @@ const ENV_VARS = {
}, },
}; };
// Resolve a relative path from the source file's directory
const resolveRelativePath = (importPath, sourceFile) => {
const sourceDir = path.dirname(sourceFile);
const extensions = [".scss", ".css", ""];
for (const ext of extensions) {
const fullPath = path.resolve(sourceDir, importPath + ext);
if (fs.existsSync(fullPath)) {
return fullPath;
}
// Try with underscore prefix for partials
const partialPath = path.join(
path.dirname(fullPath),
`_${path.basename(fullPath)}`,
);
if (fs.existsSync(partialPath)) {
return partialPath;
}
}
return null;
};
// Precompile function to convert relative paths to absolute paths
const precompile = (source, sourcePath) => {
// Match @use and @forward statements with relative paths
const importRegex = /(@use|@forward)\s+["'](\.[^"']+)["']/g;
return source.replace(importRegex, (match, directive, importPath) => {
const resolvedPath = resolveRelativePath(importPath, sourcePath);
if (resolvedPath) {
// Convert to file:// URL format for sass
const fileUrl = pathToFileURL(resolvedPath).href;
return `${directive} "${fileUrl}"`;
}
return match;
});
};
// excludes all external dependencies and bundles only the source code // excludes all external dependencies and bundles only the source code
const getConfig = (outdir) => ({ const getConfig = (outdir) => ({
outdir, outdir,
@@ -23,7 +63,11 @@ const getConfig = (outdir) => ({
splitting: true, splitting: true,
format: "esm", format: "esm",
packages: "external", packages: "external",
plugins: [sassPlugin()], plugins: [
sassPlugin({
precompile,
}),
],
target: "es2020", target: "es2020",
assetNames: "[dir]/[name]", assetNames: "[dir]/[name]",
chunkNames: "[dir]/[name]-[hash]", chunkNames: "[dir]/[name]-[hash]",
-5
View File
@@ -7616,11 +7616,6 @@ onetime@^5.1.0, onetime@^5.1.2:
dependencies: dependencies:
mimic-fn "^2.1.0" mimic-fn "^2.1.0"
open-color@1.9.1:
version "1.9.1"
resolved "https://registry.yarnpkg.com/open-color/-/open-color-1.9.1.tgz#a6e6328f60eff7aa60e3e8fcfa50f53ff3eece35"
integrity sha512-vCseG/EQ6/RcvxhUcGJiHViOgrtz4x0XbZepXvKik66TMGkvbmjeJrKFyBEx6daG5rNyyd14zYXhz0hZVwQFOw==
opener@^1.5.1: opener@^1.5.1:
version "1.5.2" version "1.5.2"
resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598" resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598"