From 371027386125a88606440d3945ea63672f7d495d Mon Sep 17 00:00:00 2001 From: "Adrien Minne (adrm)" Date: Mon, 18 May 2026 10:36:06 +0200 Subject: [PATCH 1/2] [IMP] typing: improve `globalThis` typing - `globalThis.Chart` was defined twice - `ChartGeo` was defined in `window` instead of `globalThis` Task: 6226285 --- src/global_augmentation.d.ts | 4 +--- src/helpers/figures/charts/runtime/chartjs_scales.ts | 2 +- src/plugins/ui_feature/geo_features.ts | 8 ++++---- src/types/chart/chartjs_global_augmentation.d.ts | 4 ---- 4 files changed, 6 insertions(+), 12 deletions(-) delete mode 100644 src/types/chart/chartjs_global_augmentation.d.ts diff --git a/src/global_augmentation.d.ts b/src/global_augmentation.d.ts index 00f02c4731..7689b98ba9 100644 --- a/src/global_augmentation.d.ts +++ b/src/global_augmentation.d.ts @@ -2,6 +2,4 @@ // 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; diff --git a/src/helpers/figures/charts/runtime/chartjs_scales.ts b/src/helpers/figures/charts/runtime/chartjs_scales.ts index ab30d72452..94d50480c0 100644 --- a/src/helpers/figures/charts/runtime/chartjs_scales.ts +++ b/src/helpers/figures/charts/runtime/chartjs_scales.ts @@ -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; diff --git a/src/plugins/ui_feature/geo_features.ts b/src/plugins/ui_feature/geo_features.ts index 2d2293bbbb..c7c2db0172 100644 --- a/src/plugins/ui_feature/geo_features.ts +++ b/src/plugins/ui_feature/geo_features.ts @@ -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 diff --git a/src/types/chart/chartjs_global_augmentation.d.ts b/src/types/chart/chartjs_global_augmentation.d.ts deleted file mode 100644 index bdf0c7568f..0000000000 --- a/src/types/chart/chartjs_global_augmentation.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -// This file is a script-mode ambient declaration (no imports/exports). -// It augments the global scope for TypeScript type checking without being -// included in the rollup-plugin-dts bundle (unreachable from module graph). -declare var Chart: import("./chartjs").GlobalChart | undefined; From b3ce44143ff747cfd2f4a420a0e4c87e7ae55aee Mon Sep 17 00:00:00 2001 From: "Adrien Minne (adrm)" Date: Mon, 18 May 2026 10:59:27 +0200 Subject: [PATCH 2/2] [IMP] color picker: add eye dropper support This commit adds a button to pick a color from anywhere on your screen using the EyeDropper API. The EyeDropper API is still experimental and not widely supported (only on desktop chromium browser at the time of writing this), so we make sure the button is only there when the API is avilable. Task: 6226285 --- src/components/color_picker/color_picker.css | 19 ++++++++- src/components/color_picker/color_picker.ts | 21 ++++++++++ src/components/color_picker/color_picker.xml | 16 +++++--- src/global_augmentation.d.ts | 11 ++++++ .../color_picker_component.test.ts.snap | 10 ++++- tests/colors/color_picker_component.test.ts | 39 ++++++++++++++++++- 6 files changed, 107 insertions(+), 9 deletions(-) diff --git a/src/components/color_picker/color_picker.css b/src/components/color_picker/color_picker.css index 5637ab2786..2e55e0f8f5 100644 --- a/src/components/color_picker/color_picker.css +++ b/src/components/color_picker/color_picker.css @@ -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); @@ -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; diff --git a/src/components/color_picker/color_picker.ts b/src/components/color_picker/color_picker.ts index 1aa06081b9..f1cd78fc9b 100644 --- a/src/components/color_picker/color_picker.ts +++ b/src/components/color_picker/color_picker.ts @@ -212,4 +212,25 @@ export class ColorPicker extends ComponentCustom
-
-
- -
-
+
+
+ +
+
+
+ +
; +} + +interface EyeDropperConstructor { + new (): EyeDropper; +} + +declare var EyeDropper: EyeDropperConstructor | undefined; diff --git a/tests/colors/__snapshots__/color_picker_component.test.ts.snap b/tests/colors/__snapshots__/color_picker_component.test.ts.snap index b3ba35ea36..308aef3928 100644 --- a/tests/colors/__snapshots__/color_picker_component.test.ts.snap +++ b/tests/colors/__snapshots__/color_picker_component.test.ts.snap @@ -435,6 +435,8 @@ exports[`Color Picker buttons Full component rendering 1`] = `
+ +
@@ -453,7 +455,13 @@ exports[`Color Picker buttons Full component rendering 1`] = `
- +
+ +
delete globalThis.EyeDropper); +} + async function mountColorPicker(partialProps: Partial = {}, model = new Model()) { const props = { onColorPicked: partialProps.onColorPicked || (() => {}), @@ -20,6 +30,7 @@ async function mountColorPicker(partialProps: Partial = {}, mo disableNoColor: partialProps.disableNoColor || false, }; ({ fixture } = await mountComponentWithPortalTarget(ColorPicker, { model, props })); + return model; } test("Color picker is correctly positioned", async () => { @@ -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(); @@ -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"); + }); });