import React, { useEffect, useState, useMemo, useCallback, useContext } from 'react';
import { observer } from 'mobx-react-lite';
import { runInAction, set } from 'mobx';
import { useTranslation } from 'react-i18next';
import { useVizStore } from '../../store';
import { GLOBAL_CONFIG } from '../../config';
import { IConfigScale, IVisualConfig, IVisualLayout, IThemeKey } from '../../interfaces';
import Toggle from '../toggle';
import { ColorSchemes, extractRGBA } from './colorScheme';
import { DomainScale, RangeScale } from './range-scale';
import { ConfigItemContainer, ConfigItemContent, ConfigItemDescription, ConfigItemHeader, ConfigItemTitle } from './config-item';
import { KVTuple } from '../../models/utils';
import { isNotEmpty } from '../../utils';
import { timezones } from './timezone';
import { Input } from '../ui/input';
import { Button } from '../ui/button';
import { Dialog, DialogContent, DialogFooter, DialogNormalContent } from '../ui/dialog';
import Combobox from '../dropdownSelect/combobox';
import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuTrigger } from '../ui/dropdown-menu';
import { vegaThemeContext } from '../../store/theme';
import { ColorPickerComponent } from './color-picker';
import { Switch } from '../ui/switch';
function SettingRow(props: { title: React.ReactNode; description?: React.ReactNode; htmlFor?: string; children?: React.ReactNode }) {
return (
{props.description &&
{props.description}
}
{props.children}
);
}
function useDomainScale() {
const [enableMinDomain, setEnableMinDomain] = useState(false);
const [enableMaxDomain, setEnableMaxDomain] = useState(false);
const [domainMin, setDomainMin] = useState(0);
const [domainMax, setDomainMax] = useState(100);
const [enableType, setEnableType] = useState(false);
const [type, setType] = useState<'linear' | 'log' | 'pow' | 'sqrt' | 'symlog'>('linear');
const setValue = useCallback((value: IConfigScale) => {
setEnableMaxDomain(isNotEmpty(value.domainMax));
setEnableMinDomain(isNotEmpty(value.domainMin));
setEnableType(isNotEmpty(value.type));
setDomainMin(value.domainMin ?? 0);
setDomainMax(value.domainMax ?? 100);
setType(value.type ?? 'linear');
}, []);
const value = useMemo(
() => ({
...(enableType ? { type } : {}),
...(enableMaxDomain ? { domainMax } : {}),
...(enableMinDomain ? { domainMin } : {}),
}),
[enableMaxDomain && domainMax, enableMinDomain && domainMin, enableType && type]
);
return {
value,
setValue,
enableMaxDomain,
enableMinDomain,
domainMax,
domainMin,
setEnableMinDomain,
setEnableMaxDomain,
setDomainMin,
setDomainMax,
enableType,
type,
setType,
setEnableType,
};
}
function useScale(minRange: number, maxRange: number, defaultMinRange?: number, defaultMaxRange?: number) {
const [enableMinDomain, setEnableMinDomain] = useState(false);
const [enableMaxDomain, setEnableMaxDomain] = useState(false);
const [enableRange, setEnableRange] = useState(false);
const [domainMin, setDomainMin] = useState(0);
const [domainMax, setDomainMax] = useState(100);
const [rangeMin, setRangeMin] = useState(defaultMinRange ?? minRange);
const [rangeMax, setRangeMax] = useState(defaultMaxRange ?? maxRange);
const [enableType, setEnableType] = useState(false);
const [type, setType] = useState<'linear' | 'log' | 'pow' | 'sqrt' | 'symlog'>('linear');
const setValue = useCallback(
(value: IConfigScale) => {
setEnableMaxDomain(isNotEmpty(value.domainMax));
setEnableMinDomain(isNotEmpty(value.domainMin));
setEnableRange(isNotEmpty(value.rangeMax) || isNotEmpty(value.rangeMin));
setEnableType(isNotEmpty(value.type));
setDomainMin(value.domainMin ?? 0);
setDomainMax(value.domainMax ?? 100);
setRangeMax(value.rangeMax ?? defaultMaxRange ?? maxRange);
setRangeMin(value.rangeMin ?? defaultMinRange ?? minRange);
setType(value.type ?? 'linear');
},
[defaultMaxRange, defaultMinRange, maxRange, minRange]
);
const value = useMemo(
() => ({
...(enableType ? { type } : {}),
...(enableMaxDomain ? { domainMax } : {}),
...(enableMinDomain ? { domainMin } : {}),
...(enableRange ? { rangeMax, rangeMin } : {}),
}),
[enableMaxDomain && domainMax, enableMinDomain && domainMin, enableRange && rangeMax, enableRange && rangeMin, enableType && type]
);
return {
type,
setType,
enableType,
setEnableType,
value,
setValue,
enableMaxDomain,
enableMinDomain,
enableRange,
rangeMax,
rangeMin,
domainMax,
domainMin,
setEnableMinDomain,
setEnableMaxDomain,
setEnableRange,
setDomainMin,
setDomainMax,
setRangeMin,
setRangeMax,
};
}
const VisualConfigPanel: React.FC = () => {
const vizStore = useVizStore();
const { layout, showVisualConfigPanel, config } = vizStore;
const { t } = useTranslation();
const formatConfigList: (keyof IVisualConfig['format'])[] = ['numberFormat', 'timeFormat', 'normalizedNumberFormat'];
const [format, setFormat] = useState({
numberFormat: layout.format.numberFormat,
timeFormat: layout.format.timeFormat,
normalizedNumberFormat: layout.format.normalizedNumberFormat,
});
const [resolve, setResolve] = useState({
x: layout.resolve.x,
y: layout.resolve.y,
color: layout.resolve.color,
opacity: layout.resolve.opacity,
shape: layout.resolve.shape,
size: layout.resolve.size,
});
const [zeroScale, setZeroScale] = useState(layout.zeroScale);
const [svg, setSvg] = useState(layout.useSvg ?? false);
const [scaleIncludeUnmatchedChoropleth, setScaleIncludeUnmatchedChoropleth] = useState(layout.scaleIncludeUnmatchedChoropleth ?? false);
const [showAllGeoshapeInChoropleth, setShowAllGeoshapeInChoropleth] = useState(layout.showAllGeoshapeInChoropleth ?? false);
const [renderer, setRenderer] = useState<'vega-lite' | 'observable-plot'>(layout.renderer ?? 'vega-lite');
const [background, setBackground] = useState({ r: 255, g: 255, b: 255, a: 0 });
const [defaultColor, setDefaultColor] = useState({ r: 91, g: 143, b: 249, a: 1 });
const [primaryColorEdited, setPrimaryColorEdited] = useState(false);
const [backgroundEdited, setBackgroundEdited] = useState(false);
const [displayColorPicker, setDisplayColorPicker] = useState(false);
const [displayBackgroundPicker, setDisplayBackgroundPicker] = useState(false);
const [colorPalette, setColorPalette] = useState('');
const { vizThemeConfig, setVizThemeConfig } = useContext(vegaThemeContext);
const [themeKey, setThemeKey] = useState(
typeof vizThemeConfig === 'string' ? vizThemeConfig : 'vega'
);
const [geoMapTileUrl, setGeoMapTileUrl] = useState(undefined);
const [displayOffset, setDisplayOffset] = useState(undefined);
const [displayOffsetEdited, setDisplayOffsetEdited] = useState(false);
const [enabledScales, setEnabledScales] = useState([]);
const columnValue = useDomainScale();
const rowValue = useDomainScale();
const colorValue = useDomainScale();
const thetaValue = useDomainScale();
const radiusValue = useDomainScale();
const opacityValue = useScale(0, 1, 0.3, 0.8);
const sizeValue = useScale(0, 100);
const scalesSet = new Set(enabledScales);
useEffect(() => {
setZeroScale(layout.zeroScale);
setSvg(layout.useSvg ?? false);
setRenderer(layout.renderer ?? 'vega-lite');
setBackground(
extractRGBA(
{
r: 255,
g: 255,
b: 255,
a: 0,
},
layout.background
)
);
setResolve(layout.resolve);
setDefaultColor(extractRGBA({ r: 91, g: 143, b: 249, a: 1 }, layout.primaryColor));
setPrimaryColorEdited(false);
setBackgroundEdited(false);
setScaleIncludeUnmatchedChoropleth(layout.scaleIncludeUnmatchedChoropleth ?? false);
setFormat({
numberFormat: layout.format.numberFormat,
timeFormat: layout.format.timeFormat,
normalizedNumberFormat: layout.format.normalizedNumberFormat,
});
setColorPalette(layout.colorPalette ?? '');
setThemeKey(typeof vizThemeConfig === 'string' ? vizThemeConfig : 'vega');
const enabledScales = Object.entries(layout.scale ?? {})
.filter(([_k, scale]) => Object.entries(scale).filter(([_k, v]) => !!v).length > 0)
.map(([k]) => k);
setEnabledScales(enabledScales.length === 0 ? ['opacity', 'size'] : enabledScales);
columnValue.setValue(layout.scale?.column ?? {});
rowValue.setValue(layout.scale?.row ?? {});
colorValue.setValue(layout.scale?.color ?? {});
thetaValue.setValue(layout.scale?.theta ?? {});
radiusValue.setValue(layout.scale?.radius ?? {});
opacityValue.setValue(layout.scale?.opacity ?? {});
sizeValue.setValue(layout.scale?.size ?? {});
setGeoMapTileUrl(layout.geoMapTileUrl);
setDisplayOffset(config.timezoneDisplayOffset);
setDisplayOffsetEdited(false);
}, [showVisualConfigPanel, vizThemeConfig]);
return (
);
};
export default observer(VisualConfigPanel);