diff --git a/modules/widgets/src/button-group-widget.tsx b/modules/widgets/src/button-group-widget.tsx new file mode 100644 index 00000000000..3991c5e4cc3 --- /dev/null +++ b/modules/widgets/src/button-group-widget.tsx @@ -0,0 +1,86 @@ +// deck.gl +// SPDX-License-Identifier: MIT +// Copyright (c) vis.gl contributors + +import {Widget} from '@deck.gl/core'; +import {Viewport, WidgetProps, WidgetPlacement} from '@deck.gl/core'; +import {render} from 'preact'; +import {ButtonGroup} from './lib/components/button-group'; +import {GroupedIconButton} from './lib/components/grouped-icon-button'; + +/** Defined one button in the menu */ +export type Button = + | { + id: string; + label: string; + icon?: () => JSX.Element; + } + | { + id: string; + label: string; + className: string; + }; + +export type ButtonGroupWidgetProps = WidgetProps & { + /** Widget positioning within the view. Default 'top-left'. */ + placement?: WidgetPlacement; + /** View to attach to and interact with. Required when using multiple views. */ + viewId?: string | null; + /** Button orientation. */ + orientation?: 'vertical' | 'horizontal'; + /** List of buttons to show */ + buttons: Button[]; + /** Tooltip message on zoom out button. */ + onButtonClick?: (id: string, widget: ButtonGroupWidget) => void; +}; + +/** + * A widget that lets the user add custom icon buttons to deck + * The buttons participate in widget positioning and theming, + * however the functionality is defined by the props.onButtonClick callback + */ +export class ButtonGroupWidget extends Widget { + static defaultProps: Required = { + ...Widget.defaultProps, + id: 'button-group', + placement: 'top-left', + orientation: 'vertical', + viewId: undefined!, + buttons: [], + // eslint-disable-next-line no-console + onButtonClick: (id, widget) => console.log(`Button ${id} clicked`, widget) + }; + + className = 'deck-widget-zoom'; + placement: WidgetPlacement = 'top-left'; + viewId?: string | null = null; + viewports: {[id: string]: Viewport} = {}; + + constructor(props: ButtonGroupWidgetProps) { + super(props, ButtonGroupWidget.defaultProps); + this.setProps(props); + } + + setProps(props: Partial) { + this.placement = props.placement ?? this.placement; + this.viewId = props.viewId ?? this.viewId; + super.setProps(props); + } + + onRenderHTML(rootElement: HTMLElement): void { + const ui = ( + + {this.props.buttons.map(button => ( + this.props.onButtonClick(button.id, this)} + /> + ))} + + ); + render(ui, rootElement); + } +} diff --git a/modules/widgets/src/index.ts b/modules/widgets/src/index.ts index dd144ea8fd8..2dff0faa607 100644 --- a/modules/widgets/src/index.ts +++ b/modules/widgets/src/index.ts @@ -22,6 +22,10 @@ export {ContextMenuWidget as _ContextMenuWidget} from './context-menu-widget'; export {SplitterWidget as _SplitterWidget} from './splitter-widget'; export {TimelineWidget as _TimelineWidget} from './timeline-widget'; export {ViewSelectorWidget as _ViewSelectorWidget} from './view-selector-widget'; +export { + ButtonGroupWidget as _ButtonGroupWidget, + type ButtonGroupWidgetProps +} from './button-group-widget'; export type {FullscreenWidgetProps} from './fullscreen-widget'; export type {CompassWidgetProps} from './compass-widget'; diff --git a/modules/widgets/src/lib/components/grouped-icon-button.tsx b/modules/widgets/src/lib/components/grouped-icon-button.tsx index a358c12e02b..cce1afd0bd9 100644 --- a/modules/widgets/src/lib/components/grouped-icon-button.tsx +++ b/modules/widgets/src/lib/components/grouped-icon-button.tsx @@ -3,14 +3,18 @@ // Copyright (c) vis.gl contributors export type GroupedIconButtonProps = { - className?: string; label: string; + /** Icons can be loaded from style sheet using class name */ + className: string; + /** Alterhnatively an SVG icon element can be provided */ + icon?: () => JSX.Element; + /** Action to take when button was clicked */ onClick: () => void; }; /** Renders an icon button as part of a ButtonGroup */ export const GroupedIconButton = props => { - const {className, label, onClick} = props; + const {className, label, icon, onClick} = props; return ( ); };