Skip to content

Commit

Permalink
[feat] Text layer: add outline width, outline color, background color (
Browse files Browse the repository at this point in the history
…#2342)

Signed-off-by: Ihor Dykhta <[email protected]>
  • Loading branch information
igorDykhta authored Oct 2, 2023
1 parent a59d834 commit 4383bff
Show file tree
Hide file tree
Showing 14 changed files with 565 additions and 239 deletions.
2 changes: 1 addition & 1 deletion src/actions/src/vis-state-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ export type LayerTextLabelChangeUpdaterAction = {
* Update layer text label
* @param oldLayer - layer to be updated
* @param idx -`idx` of text label to be updated
* @param prop - `prop` of text label, e,g, `anchor`, `alignment`, `color`, `size`, `field`
* @param prop - `prop` of text label, e,g, `anchor`, `alignment`, `color`, `size`, `field`, `outlineWidth`, `outlineColor`
* @param value - new value
* @returns action
* @public
Expand Down
21 changes: 16 additions & 5 deletions src/components/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ import FieldSelectorFactory, {FieldListItemFactoryFactory} from './common/field-
import FieldTokenFactory from './common/field-token';
import PanelHeaderActionFactory from './side-panel/panel-header-action';
import InfoHelperFactory from './common/info-helper';
import ColorSelectorFactory from './side-panel/layer-panel/color-selector';
import {
LayerColorSelectorFactory,
LayerColorRangeSelectorFactory,
ArcLayerColorSelectorFactory
} from './side-panel/layer-panel/layer-color-selector';
import {appInjector} from './container';

// Components
Expand Down Expand Up @@ -66,20 +72,17 @@ export {default as PanelToggleFactory} from './side-panel/panel-toggle';
export {default as PanelTabFactory} from './side-panel/panel-tab';

export {default as LayerManagerFactory} from './side-panel/layer-manager';
export {default as ColorSelector, ColorSelectorInput, ColorBlock} from './side-panel/layer-panel/color-selector';
export {ColorSelectorInput, ColorBlock} from './side-panel/layer-panel/color-selector';
export {default as CustomSelector} from './side-panel/layer-panel/color-selector';
export {default as ColorPalette} from './side-panel/layer-panel/color-palette';
export {default as ColorRangeSelector, PaletteConfig, ColorPaletteGroup, ALL_TYPES} from './side-panel/layer-panel/color-range-selector';
export {default as LayerPanelFactory} from './side-panel/layer-panel/layer-panel';
export {default as SingleColorPalette} from './side-panel/layer-panel/single-color-palette';
export {
default as LayerConfiguratorFactory,
LayerColorRangeSelector,
getLayerConfiguratorProps,
getLayerDataset,
getVisConfiguratorProps,
ArcLayerColorSelector,
LayerColorSelector
} from './side-panel/layer-panel/layer-configurator';
export {default as TextLabelPanelFactory} from './side-panel/layer-panel/text-label-panel';

Expand Down Expand Up @@ -352,6 +355,10 @@ export const PanelHeaderAction = appInjector.get(PanelHeaderActionFactory);
export const FieldListItemFactory = appInjector.get(FieldListItemFactoryFactory);
export const LayerTypeListItem = appInjector.get(LayerTypeListItemFactory);
export const InfoHelper = appInjector.get(InfoHelperFactory);
export const ColorSelector = appInjector.get(ColorSelectorFactory);
export const LayerColorSelector = appInjector.get(LayerColorSelectorFactory);
export const LayerColorRangeSelector = appInjector.get(LayerColorRangeSelectorFactory);
export const ArcLayerColorSelector = appInjector.get(ArcLayerColorSelectorFactory);
export {
appInjector,
TimeRangeSliderFactory,
Expand All @@ -363,5 +370,9 @@ export {
LayerTypeListItemFactory,
ChannelByValueSelectorFactory,
FieldListItemFactoryFactory,
InfoHelperFactory
InfoHelperFactory,
ColorSelectorFactory,
LayerColorSelectorFactory,
LayerColorRangeSelectorFactory,
ArcLayerColorSelectorFactory
};
291 changes: 171 additions & 120 deletions src/components/src/side-panel/layer-panel/color-selector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,36 +18,55 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import React, {Component, createRef, MouseEvent} from 'react';
import React, {Component, createRef, MouseEvent, ComponentType} from 'react';
import styled from 'styled-components';
import {FormattedMessage} from 'react-intl';
import {rgbToHex} from '@kepler.gl/utils';
import SingleColorPalette from './single-color-palette';
import ColorRangeSelector from './color-range-selector';
import ColorPalette from './color-palette';
import {StyledPanelDropdown} from '../../common/styled-components';
import {StyledPanelDropdown, PanelLabel} from '../../common/styled-components';
import onClickOutside from 'react-onclickoutside';
import {ColorRange} from '@kepler.gl/constants';
import {NestedPartial, RGBColor, ColorUI} from '@kepler.gl/types';
import RangeSliderFactory from '../../common/range-slider';
import {NestedPartial, RGBColor, RGBAColor, ColorUI} from '@kepler.gl/types';

type ColorSelectorInputProps = {
active: boolean;
disabled?: boolean;
inputTheme?: string;
};

export type ColorSet = {
selectedColor: RGBColor | RGBAColor | ColorRange;
setColor: (v: RGBColor | RGBAColor | ColorRange) => void;
isRange?: boolean;
label?: string;
};

type ColorSelectorProps = {
colorSets: {
selectedColor: RGBColor | ColorRange;
setColor: ((v: RGBColor) => void) | ((v: ColorRange) => void);
isRange?: boolean;
label?: string;
}[];
colorSets: ColorSet[];
colorUI?: ColorUI;
inputTheme?: string;
disabled?: boolean;
useOpacity?: boolean;
setColorUI?: (newConfig: NestedPartial<ColorUI>) => void;
};

const OpacitySliderWrapper = styled.div`
padding: 0px 12px 12px 12px;
`;

const OPACITY_SLIDER_PROPS = {
type: 'number',
range: [0, 1],
value0: 0,
step: 0.01,
isRanged: false,
label: 'Opacity',
showInput: true
};

export const ColorBlock = styled.div<{backgroundcolor: RGBColor}>`
width: 32px;
height: 18px;
Expand Down Expand Up @@ -82,118 +101,150 @@ export const InputBoxContainer = styled.div`
}
`;

class ColorSelector extends Component<ColorSelectorProps> {
static defaultProps = {
colorSets: []
};

state = {
showDropdown: false
};

node = createRef<HTMLDivElement>();

handleClickOutside = e => {
if (this.props.colorUI && Number.isInteger(this.props.colorUI.showSketcher)) {
// if sketcher is open, let sketch to close itself first
return;
}

this._closePanelDropdown();
};

_getEditing = () => {
return this.props.colorUI ? this.props.colorUI.showDropdown : this.state.showDropdown;
};

_closePanelDropdown = () => {
if (this._getEditing() === false) {
return;
}

if (this.props.setColorUI) {
this.props.setColorUI({showDropdown: false, showSketcher: false});
} else {
this.setState({showDropdown: false});
}
};

_onSelectColor = (color: RGBColor | ColorRange, e: MouseEvent) => {
e.stopPropagation();

const editing = this._getEditing();

if (typeof editing === 'number' && this.props.colorSets[editing]) {
// @ts-ignore
this.props.colorSets[editing].setColor(color);
}
};

_showDropdown = (e, i) => {
e.stopPropagation();
e.preventDefault();

if (this.props.setColorUI) {
this.props.setColorUI({showDropdown: i});
} else {
this.setState({showDropdown: i});
}
};

render() {
const {colorSets, disabled, inputTheme, colorUI} = this.props;

const editing = this._getEditing();
const currentEditing =
typeof editing === 'number' && colorSets[editing] && typeof colorSets[editing] === 'object';

return (
<div className="color-selector" ref={this.node}>
<InputBoxContainer>
{colorSets.map((cSet, i) => (
<div className="color-select__input-group" key={i}>
<ColorSelectorInput
className="color-selector__selector"
active={editing === i}
disabled={disabled}
inputTheme={inputTheme}
onMouseDown={e => this._showDropdown(e, i)}
>
{cSet.isRange ? (
<ColorPalette colors={(cSet.selectedColor as ColorRange).colors} />
) : (
<ColorBlock
className="color-selector__selector__block"
backgroundcolor={cSet.selectedColor as RGBColor}
ColorSelectorFactory.deps = [RangeSliderFactory];

function ColorSelectorFactory(RangeSlider): ComponentType<ColorSelectorProps> {
class ColorSelector extends Component<ColorSelectorProps> {
static defaultProps = {
colorSets: []
};

state = {
showDropdown: false
};

node = createRef<HTMLDivElement>();

handleClickOutside = e => {
if (this.props.colorUI && Number.isInteger(this.props.colorUI.showSketcher)) {
// if sketcher is open, let sketch to close itself first
return;
}

this._closePanelDropdown();
};

_getEditing = () => {
return this.props.colorUI ? this.props.colorUI.showDropdown : this.state.showDropdown;
};

_closePanelDropdown = () => {
if (this._getEditing() === false) {
return;
}

if (this.props.setColorUI) {
this.props.setColorUI({showDropdown: false, showSketcher: false});
} else {
this.setState({showDropdown: false});
}
};

_onSelectColor = (color: RGBColor | ColorRange, e: MouseEvent) => {
e.stopPropagation();
const editing = this._getEditing();
const colorSet = typeof editing === 'number' && this.props.colorSets[editing];
if (colorSet) {
this._setColor(colorSet, color, colorSet.selectedColor[3]);
}
};

_onSelectOpacity = (opacity: number[], e: MouseEvent) => {
if (e) e.stopPropagation();
const editing = this._getEditing();
const colorSet = typeof editing === 'number' && this.props.colorSets[editing];
if (colorSet) {
this._setColor(colorSet, colorSet.selectedColor, Math.round(opacity[1] * 255));
}
};

_setColor = (colorSet: ColorSet, color: RGBColor | RGBAColor | ColorRange, opacity: number) => {
if (this.props.useOpacity && Array.isArray(color)) {
colorSet.setColor([...color.slice(0, 3), opacity] as RGBAColor);
} else {
colorSet.setColor(color);
}
};

_showDropdown = (e, i) => {
e.stopPropagation();
e.preventDefault();

if (this.props.setColorUI) {
this.props.setColorUI({showDropdown: i});
} else {
this.setState({showDropdown: i});
}
};

render() {
const {colorSets, useOpacity, disabled, inputTheme, colorUI} = this.props;

const editing = this._getEditing();
const currentEditing =
typeof editing === 'number' && colorSets[editing] && typeof colorSets[editing] === 'object';

return (
<div className="color-selector" ref={this.node}>
<InputBoxContainer>
{colorSets.map((cSet, i) => (
<div className="color-select__input-group" key={i}>
<ColorSelectorInput
className="color-selector__selector"
active={editing === i}
disabled={disabled}
inputTheme={inputTheme}
onMouseDown={e => this._showDropdown(e, i)}
>
{cSet.isRange ? (
<ColorPalette colors={(cSet.selectedColor as ColorRange).colors} />
) : (
<ColorBlock
className="color-selector__selector__block"
backgroundcolor={cSet.selectedColor as RGBColor}
/>
)}
{cSet.label ? (
<div className="color-selector__selector__label">{cSet.label}</div>
) : null}
</ColorSelectorInput>
</div>
))}
</InputBoxContainer>
{currentEditing ? (
<StyledPanelDropdown className="color-selector__dropdown">
{colorSets[editing as number].isRange ? (
<ColorRangeSelector
selectedColorRange={colorSets[editing as number].selectedColor as ColorRange}
onSelectColorRange={this._onSelectColor}
setColorPaletteUI={this.props.setColorUI}
colorPaletteUI={colorUI as ColorUI}
/>
) : (
<SingleColorPalette
selectedColor={rgbToHex(colorSets[editing as number].selectedColor as RGBColor)}
onSelectColor={this._onSelectColor}
/>
)}
{useOpacity ? (
<OpacitySliderWrapper>
<PanelLabel>
<FormattedMessage id="color.opacity" />
</PanelLabel>
<RangeSlider
{...OPACITY_SLIDER_PROPS}
value1={colorSets[editing as number].selectedColor[3] / 255}
onChange={this._onSelectOpacity}
/>
)}
{cSet.label ? (
<div className="color-selector__selector__label">{cSet.label}</div>
) : null}
</ColorSelectorInput>
</div>
))}
</InputBoxContainer>
{currentEditing ? (
<StyledPanelDropdown className="color-selector__dropdown">
{colorSets[editing as number].isRange ? (
<ColorRangeSelector
selectedColorRange={colorSets[editing as number].selectedColor as ColorRange}
onSelectColorRange={this._onSelectColor}
setColorPaletteUI={this.props.setColorUI}
colorPaletteUI={colorUI as ColorUI}
/>
) : (
<SingleColorPalette
selectedColor={rgbToHex(colorSets[editing as number].selectedColor as RGBColor)}
onSelectColor={this._onSelectColor}
/>
)}
</StyledPanelDropdown>
) : null}
</div>
);
</OpacitySliderWrapper>
) : null}
</StyledPanelDropdown>
) : null}
</div>
);
}
}
return onClickOutside(ColorSelector) as ComponentType<ColorSelectorProps>;
}

export default onClickOutside(ColorSelector);
export default ColorSelectorFactory;
Loading

0 comments on commit 4383bff

Please sign in to comment.