Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions src/components/color_picker/color_picker.css
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,12 @@
}
}
}

--color-picker-item-size: calc(var(--os-item-edge-length) + (2 * var(--os-item-border-width)));

.o-color-picker-line-item {
width: calc(var(--os-item-edge-length) + (2 * var(--os-item-border-width)));
height: calc(var(--os-item-edge-length) + (2 * var(--os-item-border-width)));
width: var(--color-picker-item-size);
height: var(--color-picker-item-size);
margin: 0px;
border-radius: 50px;
border: var(--os-item-border-width) solid light-dark(var(--os-gray-600), #c0c0c0);
Expand All @@ -45,6 +48,18 @@
cursor: pointer;
}
}
.eyedropper {
width: var(--color-picker-item-size);
height: var(--color-picker-item-size);
&:hover {
background-color: var(--os-hovered-menu-item-color);
color: var(--os-button-active-text-color);
cursor: pointer;
}
.fa {
font-size: 13px;
}
}
.o-buttons {
padding: var(--os-picker-padding);
display: flex;
Expand Down
21 changes: 21 additions & 0 deletions src/components/color_picker/color_picker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,4 +212,25 @@ export class ColorPicker extends Component<ColorPickerProps, SpreadsheetChildEnv
isSameColor(color1: Color, color2: Color): boolean {
return isSameColor(color1, color2);
}

get canUseEyeDropper(): boolean {
return !!globalThis.EyeDropper && !this.env.model.getters.isDarkMode();
}

async activateEyedropper() {
if (!globalThis.EyeDropper) {
return;
}

try {
const result = await new globalThis.EyeDropper().open();
if (result && result.sRGBHex) {
this.props.onColorPicked(toHex(result.sRGBHex));
}
} catch (error) {
if (error.name !== "AbortError") {
throw error;
}
}
}
}
16 changes: 11 additions & 5 deletions src/components/color_picker/color_picker.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,6 @@
<span>Custom</span>
</div>
<div class="colors-grid o-color-picker-toggler" t-on-click.stop="toggleColorPicker">
<div class="o-color-picker-line-item bg-white o-color-picker-toggler-button">
<div class="o-color-picker-toggler-sign">
<t t-call="o-spreadsheet-Icon.PLUS"/>
</div>
</div>
<div
t-foreach="env.model.getters.getCustomColors()"
t-as="color"
Expand All @@ -47,6 +42,17 @@
</div>
</div>
<div class="o-color-picker-line-item bg-white o-color-picker-toggler-button">
<div class="o-color-picker-toggler-sign">
<t t-call="o-spreadsheet-Icon.PLUS"/>
</div>
</div>
<div
class="eyedropper d-flex align-items-center justify-content-center rounded-circle"
t-if="canUseEyeDropper"
t-on-click.stop="activateEyedropper">
<i class="fa fa-eyedropper"/>
</div>
</div>
<div t-if="state.showGradient" class="o-custom-selector">
<div
Expand Down
13 changes: 11 additions & 2 deletions src/global_augmentation.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@
// Provides global type augmentation for TypeScript type checking without being
// included in the rollup-plugin-dts bundle (unreachable from module graph).
declare var Chart: import("./types/chart/chartjs").GlobalChart | undefined;
interface Window {
ChartGeo: typeof import("chartjs-chart-geo");
declare var ChartGeo: typeof import("chartjs-chart-geo") | undefined;

// The eye dropper API is still experimental, and thus not yet included in ts's typing
interface EyeDropper {
open(): Promise<{ sRGBHex: string }>;
}

interface EyeDropperConstructor {
new (): EyeDropper;
}

declare var EyeDropper: EyeDropperConstructor | undefined;
2 changes: 1 addition & 1 deletion src/helpers/figures/charts/runtime/chartjs_scales.ts
Original file line number Diff line number Diff line change
Expand Up @@ -464,7 +464,7 @@ export function getFunnelChartScales(
}

function getGeoChartProjection(projection: GeoChartProjection) {
if (projection === "conicConformal") {
if (globalThis.ChartGeo && projection === "conicConformal") {
return globalThis.ChartGeo.geoConicConformal().rotate([100, 0]); // Centered on the US
}
return projection;
Expand Down
8 changes: 4 additions & 4 deletions src/plugins/ui_feature/geo_features.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,10 +132,10 @@ export class GeoFeaturePlugin extends UIPlugin {
}
// TopoJSON
if (json.type === "Topology") {
const features = (globalThis as any).ChartGeo.topojson.feature(
json,
Object.values(json.objects)[0]
);
if (!globalThis.ChartGeo) {
return null;
}
const features = globalThis.ChartGeo.topojson.feature(json, Object.values(json.objects)[0]);
return features.type === "FeatureCollection" ? features.features : [features];
}
// GeoJSON
Expand Down
4 changes: 0 additions & 4 deletions src/types/chart/chartjs_global_augmentation.d.ts

This file was deleted.

10 changes: 9 additions & 1 deletion tests/colors/__snapshots__/color_picker_component.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,8 @@ exports[`Color Picker buttons Full component rendering 1`] = `
<div
class="colors-grid o-color-picker-toggler"
>


<div
class="o-color-picker-line-item bg-white o-color-picker-toggler-button"
>
Expand All @@ -453,7 +455,13 @@ exports[`Color Picker buttons Full component rendering 1`] = `
</svg>
</div>
</div>

<div
class="eyedropper d-flex align-items-center justify-content-center rounded-circle"
>
<i
class="fa fa-eyedropper"
/>
</div>

</div>
<div
Expand Down
39 changes: 38 additions & 1 deletion tests/colors/color_picker_component.test.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,26 @@
import { Color, Model } from "../../src";
import { ColorPicker, ColorPickerProps } from "../../src/components/color_picker/color_picker";
import { toHex } from "../../src/helpers/color";
import { registerCleanup } from "../setup/jest.setup";
import { setFormatting } from "../test_helpers/commands_helpers";
import {
getElComputedStyle,
setInputValueAndTrigger,
simulateClick,
} from "../test_helpers/dom_helper";
import { mountComponentWithPortalTarget } from "../test_helpers/helpers";
import { mountComponentWithPortalTarget, nextTick } from "../test_helpers/helpers";

let fixture: HTMLElement;

function addMockEyeDropperSupport(mockColor: Color) {
globalThis.EyeDropper = class {
open() {
return Promise.resolve({ sRGBHex: mockColor });
}
};
registerCleanup(() => delete globalThis.EyeDropper);
}

async function mountColorPicker(partialProps: Partial<ColorPickerProps> = {}, model = new Model()) {
const props = {
onColorPicked: partialProps.onColorPicked || (() => {}),
Expand All @@ -20,6 +30,7 @@ async function mountColorPicker(partialProps: Partial<ColorPickerProps> = {}, mo
disableNoColor: partialProps.disableNoColor || false,
};
({ fixture } = await mountComponentWithPortalTarget(ColorPicker, { model, props }));
return model;
}

test("Color picker is correctly positioned", async () => {
Expand Down Expand Up @@ -48,6 +59,7 @@ describe("Color Picker buttons", () => {
});

test("Full component rendering", async () => {
addMockEyeDropperSupport("#123456");
await mountColorPicker();
await simulateClick(".o-color-picker-toggler-sign");
expect(fixture.querySelector(".o-color-picker")).toMatchSnapshot();
Expand Down Expand Up @@ -203,4 +215,29 @@ describe("Color Picker buttons", () => {
const addButton = fixture.querySelector(".o-add-button")!;
expect(addButton.classList).toContain("o-disabled");
});

test("Eye dropper button is not there if the EyeDropper API is not present", async () => {
await mountColorPicker();
expect(".eyedropper").toHaveCount(0);
});

test("Eye dropper button is not there in dark mode", async () => {
// In dark mode we'd have to do the opposite color inversion than then dark mode color inversion to get the correct color.
// But this mathematically cannot always be done, because our color inversion lose information with clipping and rounding.
addMockEyeDropperSupport("#abc123");
const model = await mountColorPicker({});
expect(".eyedropper").toHaveCount(1);

model.dispatch("UPDATE_COLOR_SCHEME", { colorScheme: "dark" });
await nextTick();
expect(".eyedropper").toHaveCount(0);
});

test("Can pick a custom color with the eye dropper", async () => {
addMockEyeDropperSupport("#abc123");
const onColorPicked = jest.fn();
await mountColorPicker({ onColorPicked });
await simulateClick(".eyedropper");
expect(onColorPicked).toHaveBeenCalledWith("#ABC123");
});
});