diff --git a/src/component/panels/SpectraPanel/base/setting/ApplyToAllSelected.tsx b/src/component/panels/SpectraPanel/base/setting/ApplyToAllSelected.tsx
new file mode 100644
index 000000000..3b5c84cc1
--- /dev/null
+++ b/src/component/panels/SpectraPanel/base/setting/ApplyToAllSelected.tsx
@@ -0,0 +1,112 @@
+import { Icon, Switch, Tag, Tooltip } from '@blueprintjs/core';
+import styled from '@emotion/styled';
+import { Controller, useFormContext, useWatch } from 'react-hook-form';
+
+import { useSelectedSpectra } from '../../../../hooks/useSelectedSpectra.ts';
+
+const Container = styled.div<{ isActive: boolean; isDisabled: boolean }>`
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 5px;
+ padding: 5px 10px;
+ background: ${({ isActive }) =>
+ isActive ? 'rgba(232, 98, 26, 0.07)' : 'rgba(0,0,0,0.03)'};
+ opacity: ${({ isDisabled }) => (isDisabled ? 0.4 : 1)};
+ pointer-events: ${({ isDisabled }) => (isDisabled ? 'none' : 'auto')};
+ cursor: ${({ isDisabled }) => (isDisabled ? 'not-allowed' : 'default')};
+ user-select: none;
+`;
+
+const LabelGroup = styled.div`
+ display: flex;
+ align-items: center;
+ gap: 5px;
+ flex: 1;
+ min-width: 0;
+`;
+
+const LabelIcon = styled(Icon)<{ isActive: boolean }>`
+ color: ${({ isActive }) => (isActive ? '#E8621A' : '#888')};
+ flex-shrink: 0;
+`;
+
+const LabelText = styled.span<{ isActive: boolean }>`
+ font-size: 13px;
+ color: ${({ isActive }) => (isActive ? '#C05215' : '#555')};
+ white-space: nowrap;
+`;
+
+const StyledSwitch = styled(Switch)`
+ margin: 0;
+`;
+
+const CountTag = styled(Tag)<{ isActive: boolean }>`
+ color: ${({ isActive }) => (isActive ? '#E8621A' : '#666')} !important;
+`;
+
+export function ApplyToAllSelected() {
+ const spectra = useSelectedSpectra();
+ const { control, setValue } = useFormContext();
+ const applyToAllSelected = useWatch({ control, name: 'applyToAll' });
+ const selectedCount = spectra?.length || 0;
+ const isDisabled = selectedCount < 2;
+ const label =
+ selectedCount === 0
+ ? '0 selected'
+ : selectedCount === 1
+ ? '1 spectrum'
+ : `${selectedCount} spectra`;
+
+ const tooltipContent = isDisabled
+ ? 'Select at least 2 spectra to apply color to all'
+ : applyToAllSelected
+ ? `Color will be applied to all ${selectedCount} selected spectra`
+ : 'Enable to apply the chosen color to all selected spectra';
+ return (
+
+
+ !isDisabled && setValue('applyToAll', !applyToAllSelected)
+ }
+ >
+
+
+
+ Apply to all selected
+
+
+ {label}
+
+
+ {
+ const { value, onChange } = field;
+ return (
+ {
+ e.stopPropagation();
+ }}
+ onChange={(e) => {
+ onChange(!e.currentTarget.checked);
+ e.stopPropagation();
+ }}
+ />
+ );
+ }}
+ />
+
+
+ );
+}
diff --git a/src/component/panels/SpectraPanel/base/setting/Spectrum1DHistogram.tsx b/src/component/panels/SpectraPanel/base/setting/Spectrum1DHistogram.tsx
index 7520c2b7c..3e64733ee 100644
--- a/src/component/panels/SpectraPanel/base/setting/Spectrum1DHistogram.tsx
+++ b/src/component/panels/SpectraPanel/base/setting/Spectrum1DHistogram.tsx
@@ -1,4 +1,5 @@
import { memo, useMemo } from 'react';
+import { useWatch } from 'react-hook-form';
import PlotChart from './PlotChart.js';
import { processSnapPlot } from './processSnapPlot.js';
@@ -17,6 +18,10 @@ function Spectrum1DHistogram({
return processSnapPlot('1D', data, yLogBase);
}, [data]);
+ const isApplyToAllSelected = useWatch({ name: 'applyToAll' });
+
+ if (isApplyToAllSelected) return null;
+
return (
-
-
{
- const { value, onChange } = field;
- return (
- {
- onChange(colorToHexWithAlpha(color));
- void handleSubmit(onSubmit)();
- }}
- color={{ hex: value || '#000' }}
- presetColors={COLORS}
- style={{ boxShadow: 'none' }}
- />
- );
- }}
- />
+
+
+
+
+
+ {
+ const { value, onChange } = field;
+ return (
+ {
+ onChange(colorToHexWithAlpha(color));
+ void handleSubmit(onSubmit)();
+ }}
+ color={{ hex: value || '#000' }}
+ presetColors={COLORS}
+ style={{ boxShadow: 'none', width: 250 }}
+ />
+ );
+ }}
+ />
+
+
-
-
+
);
}
diff --git a/src/component/panels/SpectraPanel/base/setting/Spectrum2DHistogram.tsx b/src/component/panels/SpectraPanel/base/setting/Spectrum2DHistogram.tsx
index 9cf78e280..862a3c994 100644
--- a/src/component/panels/SpectraPanel/base/setting/Spectrum2DHistogram.tsx
+++ b/src/component/panels/SpectraPanel/base/setting/Spectrum2DHistogram.tsx
@@ -1,4 +1,5 @@
import { memo, useMemo } from 'react';
+import { useWatch } from 'react-hook-form';
import PlotChart from './PlotChart.js';
import { processSnapPlot } from './processSnapPlot.js';
@@ -18,6 +19,10 @@ function Spectrum2DHistogram({
return processSnapPlot('2D', data.rr, yLogBase);
}, [data]);
+ const isApplyToAllSelected = useWatch({ name: 'applyToAll' });
+
+ if (isApplyToAllSelected) return null;
+
return (
diff --git a/src/component/panels/SpectraPanel/base/setting/Spectrum2DSetting.tsx b/src/component/panels/SpectraPanel/base/setting/Spectrum2DSetting.tsx
index 92c3e7f2f..20fccfd1d 100644
--- a/src/component/panels/SpectraPanel/base/setting/Spectrum2DSetting.tsx
+++ b/src/component/panels/SpectraPanel/base/setting/Spectrum2DSetting.tsx
@@ -20,6 +20,7 @@ import { NumberInput2 } from '../../../../elements/NumberInput2.js';
import { useFormValidateField } from '../../../../elements/useFormValidateField.js';
import { colorToHexWithAlpha } from '../../../../utility/colorToHexWithAlpha.js';
+import { ApplyToAllSelected } from './ApplyToAllSelected.tsx';
import Spectrum2DHistogram from './Spectrum2DHistogram.js';
const StyledRangeSlider = styled(RangeSlider)<{
@@ -57,22 +58,29 @@ export function Spectrum2DSetting({ data, onSubmit }: Spectrum2DSettingProps) {
view: { spectraContourLevels },
} = useChartData();
const methods = useForm({
- defaultValues: { contourOptions: spectraContourLevels[id], display },
+ defaultValues: {
+ contourOptions: spectraContourLevels[id],
+ display,
+ applyToAll: false,
+ },
});
const { positiveColor, negativeColor } = display;
return (
-
-
- Positive
-
-
-
- Negative
-
-
-
+
+
+
+
+ Positive
+
+
+
+ Negative
+
+
+
+
);
diff --git a/src/component/panels/SpectraPanel/base/setting/SpectrumSetting.tsx b/src/component/panels/SpectraPanel/base/setting/SpectrumSetting.tsx
index 8642d6faa..14727f3b9 100644
--- a/src/component/panels/SpectraPanel/base/setting/SpectrumSetting.tsx
+++ b/src/component/panels/SpectraPanel/base/setting/SpectrumSetting.tsx
@@ -3,13 +3,14 @@ import styled from '@emotion/styled';
import type { Display1D, Display2D } from '@zakodium/nmrium-core';
import { useDispatch } from '../../../../context/DispatchContext.js';
+import { useActiveSpectra } from '../../../../hooks/useActiveSpectra.ts';
import { ColorIndicator } from '../ColorIndicator.js';
import { Spectrum1DSetting } from './Spectrum1DSetting.js';
import { Spectrum2DSetting } from './Spectrum2DSetting.js';
const SpectrumSettingContent = styled.div`
- max-height: 360px;
+ max-height: 390px;
overflow-y: auto;
`;
interface SpectrumSettingProps {
@@ -25,11 +26,18 @@ export function SpectrumSetting({
}: SpectrumSettingProps) {
const dispatch = useDispatch();
const { id, info } = data;
+ const activeSpectra = useActiveSpectra();
function submitHandler(values: any) {
+ const { applyToAll, ...other } = values;
+ const ids =
+ activeSpectra && activeSpectra.length >= 2 && applyToAll
+ ? activeSpectra.map((s) => s.id)
+ : [id];
+
dispatch({
type: 'CHANGE_SPECTRUM_SETTING',
- payload: { id, ...values },
+ payload: { ids, ...other },
});
}
diff --git a/src/component/reducer/actions/SpectraActions.ts b/src/component/reducer/actions/SpectraActions.ts
index 9a7d8b129..023694852 100644
--- a/src/component/reducer/actions/SpectraActions.ts
+++ b/src/component/reducer/actions/SpectraActions.ts
@@ -90,11 +90,11 @@ type ChangeActiveSpectrumAction = ActionType<
type ChangeSpectrumSettingAction = ActionType<
'CHANGE_SPECTRUM_SETTING',
| {
- id: string;
+ ids: string[];
display: Display1D | Display2D;
}
| {
- id: string;
+ ids: string[];
display: Display2D;
contourOptions: ContourLevel;
}
@@ -445,16 +445,22 @@ function handleChangeSpectrumSetting(
draft: Draft
,
action: ChangeSpectrumSettingAction,
) {
- const id = action.payload.id;
+ const ids = action.payload.ids;
- const spectrum = getSpectrum(draft, id);
- if (!spectrum) return;
+ const spectraById = new Map(draft.data.map((s) => [s.id, s]));
+
+ for (const id of ids) {
+ const spectrum = spectraById.get(id);
+ if (!spectrum) continue;
- spectrum.display = action.payload.display;
- if (isFt2DSpectrum(spectrum) && 'contourOptions' in action.payload) {
- draft.view.spectraContourLevels[id] = action.payload.contourOptions;
- const { checkLevel } = contoursManager(draft.view.spectraContourLevels[id]);
- checkLevel();
+ spectrum.display = action.payload.display;
+ if (isFt2DSpectrum(spectrum) && 'contourOptions' in action.payload) {
+ draft.view.spectraContourLevels[id] = action.payload.contourOptions;
+ const { checkLevel } = contoursManager(
+ draft.view.spectraContourLevels[id],
+ );
+ checkLevel();
+ }
}
}
diff --git a/test-e2e/panels/spectra.test.ts b/test-e2e/panels/spectra.test.ts
index 477682a8d..711efa18d 100644
--- a/test-e2e/panels/spectra.test.ts
+++ b/test-e2e/panels/spectra.test.ts
@@ -68,11 +68,21 @@ test('Check change spectrum color, Should be white', async ({ page }) => {
// Open Change color modal
await nmrium.page.click('_react=ColorIndicator');
-
- // Click on the top-left of the color picker (white)
- await nmrium.page.click('_react=Saturation', {
- position: { x: 0, y: 0 },
- });
+ await nmrium.page.hover('_react=Saturation');
+ // Change color by move to the top-left of the color picker (white)
+ const saturationBox = nmrium.page.locator('_react=Saturation');
+ const box = await saturationBox.boundingBox();
+
+ if (box) {
+ const startX = box.x + box.width - 1;
+ const startY = box.y + box.height - 1;
+ const targetX = box.x;
+ const targetY = box.y;
+ await nmrium.page.mouse.move(startX, startY, { steps: 15 });
+ await nmrium.page.mouse.down();
+ await nmrium.page.mouse.move(targetX, targetY, { steps: 15 });
+ await nmrium.page.mouse.up();
+ }
// The line should now be white.
await expect(whiteSpectrumLine).toBeVisible();
@@ -127,20 +137,20 @@ test('2d spectrum', async ({ page }) => {
// Open Change color modal
await nmrium.page.click('_react=ColorIndicator');
- // change the color to #ddb1c9ff
+ // change the color to #e4c0d3
await nmrium.page.click('_react=Saturation', {
position: { x: 40, y: 20 },
});
// Check that ColorIndicator color changed
await expect(
- nmrium.page.locator('_react=ColorIndicator[display.color="#ddb1c9ff"]'),
+ nmrium.page.locator('_react=ColorIndicator[display.color="#e4c0d3ff"]'),
).toBeVisible();
// Check that spectrum color changed
await expect(
nmrium.page
.getByTestId('spectrum-line')
- .locator('_react=Line[display.color="#ddb1c9ff"]'),
+ .locator('_react=Line[display.color="#e4c0d3ff"]'),
).toBeVisible();
// Close color picker