Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 51 additions & 4 deletions src/components/side_panel/carousel_panel/carousel_panel.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
import { onWillUpdateProps, signal } from "@odoo/owl";
import { onWillUpdateProps, proxy, signal } from "@odoo/owl";
import { ActionSpec } from "../../../actions/action";
import { DEFAULT_CAROUSEL_TITLE_STYLE } from "../../../constants";
import { getCarouselItemPreview, getCarouselItemTitle } from "../../../helpers/carousel_helpers";
import { SpreadsheetChart } from "../../../helpers/figures/chart";
import { deepEquals } from "../../../helpers/misc";
import { UuidGenerator } from "../../../helpers/uuid";
import { Component } from "../../../owl3_compatibility_layer";
import { chartDataSourceRegistry } from "../../../registries/chart_data_source_registry";
import { chartTypeRegistry } from "../../../registries/chart_registry";
import { chartSubtypeRegistry } from "../../../registries/chart_subtype_registry";
import { _t } from "../../../translation";
import { TitleDesign } from "../../../types/chart/chart";
import { CHART_TYPES, ChartDefinition, TitleDesign } from "../../../types/chart/chart";
import { CarouselItem } from "../../../types/figure";
import { UID } from "../../../types/misc";
import { SpreadsheetChildEnv } from "../../../types/spreadsheet_env";
import { getBoundingRectAsPOJO } from "../../helpers/dom_helpers";
import { useDragAndDropListItems } from "../../helpers/drag_and_drop_dom_items_hook";
import { PopoverProps } from "../../popover/popover";
import { TextInput } from "../../text_input/text_input";
import { TextStyler } from "../chart/building_blocks/text_styler/text_styler";
import { ChartTypePickerPopover } from "../chart/chart_type_picker_popover/chart_type_picker_popover";
import { CogWheelMenu } from "../components/cog_wheel_menu/cog_wheel_menu";
import { Section } from "../components/section/section";

Expand All @@ -22,15 +28,22 @@ interface Props {
figureId: UID;
}

interface CarouselPanelState {
popoverProps: PopoverProps | undefined;
}

export class CarouselPanel extends Component<Props, SpreadsheetChildEnv> {
static template = "o-spreadsheet-CarouselPanel";
static props = { onCloseSidePanel: Function, figureId: String };
static components = { Section, TextInput, TextStyler, CogWheelMenu };
static components = { Section, TextInput, TextStyler, CogWheelMenu, ChartTypePickerPopover };

DEFAULT_CAROUSEL_TITLE_STYLE = DEFAULT_CAROUSEL_TITLE_STYLE;

private dragAndDrop = useDragAndDropListItems();
private previewListRef = signal<HTMLElement | null>(null);
addChartButton = signal<HTMLElement | null>(null);

state = proxy<CarouselPanelState>({ popoverProps: undefined });

setup() {
let lastCarouselItems: CarouselItem[] = [...this.carouselItems];
Expand Down Expand Up @@ -62,11 +75,45 @@ export class CarouselPanel extends Component<Props, SpreadsheetChildEnv> {
return item.type === "chart" ? item.chartId : "transparent-carousel";
}

addNewChartToCarousel() {
addNewChartToCarousel(ev: MouseEvent) {
if (this.state.popoverProps) {
this.state.popoverProps = undefined;
return;
}
const target = ev.currentTarget as HTMLElement;
const { bottom, right } = target.getBoundingClientRect();
this.state.popoverProps = {
anchorRect: { x: right, y: bottom, width: 0, height: 0 },
positioning: "top-right",
verticalOffset: 0,
};
}

get supportedChartTypes() {
return new Set(CHART_TYPES);
}

closePopover() {
this.state.popoverProps = undefined;
}

onSelectChartType(type: string) {
const newChartInfo = chartSubtypeRegistry.get(type);
const ChartTypeBuilder = chartTypeRegistry.get(newChartInfo.chartType);
const DataSourceBuilder = chartDataSourceRegistry.get("range");
const definition = SpreadsheetChart.deleteInvalidKeys({
...ChartTypeBuilder.getDefinitionFromContextCreation({}, DataSourceBuilder),
...newChartInfo.subtypeDefinition,
} as ChartDefinition);

this.env.model.dispatch("ADD_NEW_CHART_TO_CAROUSEL", {
figureId: this.props.figureId,
sheetId: this.carouselSheetId,
newChartId: UuidGenerator.smallUuid(),
chartDefinition: definition,
});
this.env.model.dispatch("SELECT_FIGURE", { figureId: this.props.figureId });
this.env.openSidePanel("ChartPanel");
}

get hasDataView(): boolean {
Expand Down
14 changes: 12 additions & 2 deletions src/components/side_panel/carousel_panel/carousel_panel.xml
Original file line number Diff line number Diff line change
Expand Up @@ -52,18 +52,28 @@
</t>
</div>
<div
class="o-button-link o-carousel-add-chart float-end d-flex align-items-center py-4 pe-4 gap-2"
class="o-button-link o-carousel-add-chart float-end d-flex align-items-center my-4 me-4 gap-2"
t-on-click="this.addNewChartToCarousel"
t-ref="this.addChartButton"
t-att-title="this.carouselAddChartInfoMessage">
+ Add chart
</div>
<div
t-if="!this.hasDataView"
class="o-button-link o-carousel-add-data-view float-end py-4 pe-4"
class="o-button-link o-carousel-add-data-view float-end my-4 me-4"
t-on-click="this.addDataViewToCarousel"
t-att-title="this.carouselDataViewMessage">
+ Add data view
</div>
</div>
<ChartTypePickerPopover
t-if="this.state.popoverProps"
popoverProps="this.state.popoverProps"
width="300"
onClose.bind="this.closePopover"
onSelectSubType.bind="this.onSelectChartType"
parentRef="this.addChartButton"
supportedTypes="this.supportedChartTypes"
/>
</t>
</templates>
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,4 @@
height: 24px;
}
}

.o-popover .o-chart-select-popover {
.o-chart-type-item {
cursor: pointer;
padding: 2px 5px;
margin: 1px 2px;
border: 1px solid transparent;
&.selected,
&:hover {
border-color: var(--os-action-color);
background: var(--os-badge-selected-color);
}
.o-chart-preview {
width: 48px;
height: 48px;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
import { proxy, signal } from "@odoo/owl";
import { Component, useExternalListener } from "../../../../owl3_compatibility_layer";
import { Component } from "../../../../owl3_compatibility_layer";
import { chartDataSourceRegistry } from "../../../../registries/chart_data_source_registry";
import { chartSubtypeRegistry } from "../../../../registries/chart_subtype_registry";
import { CHART_TYPES, ChartDefinition, ChartType } from "../../../../types/chart/chart";
import {
chartCategories,
ChartSubtypeProperties,
} from "../../../../types/chart_subtype_properties";
import { ChartSubtypeProperties } from "../../../../types/chart_subtype_properties";
import { UID } from "../../../../types/misc";
import { SpreadsheetChildEnv } from "../../../../types/spreadsheet_env";
import { cssPropertiesToCss } from "../../../helpers/css";
import { isChildEvent } from "../../../helpers/dom_helpers";
import { Popover, PopoverProps } from "../../../popover/popover";
import { PopoverProps } from "../../../popover/popover";
import { Section } from "../../components/section/section";
import { ChartTypePickerPopover } from "../chart_type_picker_popover/chart_type_picker_popover";
import { MainChartPanelStore } from "../main_chart_panel/main_chart_panel_store";

interface Props {
Expand All @@ -22,43 +18,22 @@ interface Props {

interface ChartTypePickerState {
popoverProps: PopoverProps | undefined;
popoverStyle: string;
popoverWidth: number;
}

export class ChartTypePicker extends Component<Props, SpreadsheetChildEnv> {
static template = "o-spreadsheet-ChartTypePicker";
static components = { Section, Popover };
static components = { Section, ChartTypePickerPopover };
static props = { chartId: String, chartPanelStore: Object };

categories = chartCategories;
chartTypeByCategories: Record<string, ChartSubtypeProperties[]> = {};

popoverRef = signal<HTMLElement | null>(null);
selectRef = signal<HTMLElement | null>(null);

state = proxy<ChartTypePickerState>({ popoverProps: undefined, popoverStyle: "" });

setup(): void {
useExternalListener(window, "pointerdown", this.onExternalClick, { capture: true });

const definition = this.env.model.getters.getChartDefinition(this.props.chartId);
const supportedTypes = this.getSupportedChartTypes(definition);

for (const subtypeProperties of chartSubtypeRegistry.getAll()) {
if (!supportedTypes.has(subtypeProperties.chartType)) {
continue;
}
if (this.chartTypeByCategories[subtypeProperties.category]) {
this.chartTypeByCategories[subtypeProperties.category].push(subtypeProperties);
} else {
this.chartTypeByCategories[subtypeProperties.category] = [subtypeProperties];
}
}
}
state = proxy<ChartTypePickerState>({ popoverProps: undefined, popoverWidth: 0 });

private getSupportedChartTypes(definition: ChartDefinition): Set<ChartType> {
getSupportedChartTypes(): Set<ChartType> {
let supportedTypes: Set<ChartType>;

const definition = this.getChartDefinition(this.props.chartId);
if (definition.dataSource) {
const dataSourceBuilder = chartDataSourceRegistry.get(definition.dataSource.type);
supportedTypes = new Set(dataSourceBuilder.supportedChartTypes);
Expand All @@ -76,16 +51,8 @@ export class ChartTypePicker extends Component<Props, SpreadsheetChildEnv> {
return supportedTypes;
}

onExternalClick(ev: MouseEvent) {
if (isChildEvent(this.popoverRef()?.parentElement, ev) || isChildEvent(this.selectRef(), ev)) {
return;
}
this.closePopover();
}

onTypeChange(type: ChartType) {
this.props.chartPanelStore.changeChartType(this.props.chartId, type);
this.closePopover();
}

private getChartDefinition(chartId: UID): ChartDefinition {
Expand Down Expand Up @@ -113,10 +80,10 @@ export class ChartTypePicker extends Component<Props, SpreadsheetChildEnv> {
verticalOffset: 0,
};

this.state.popoverStyle = cssPropertiesToCss({ width: `${width}px` });
this.state.popoverWidth = width;
}

private closePopover() {
closePopover() {
this.state.popoverProps = undefined;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,32 +14,15 @@
</div>
</div>
</Section>
<Popover t-if="this.state.popoverProps" t-props="this.state.popoverProps">
<div
t-ref="this.popoverRef"
class="o-chart-select-popover px-3 pb-4"
t-att-style="this.state.popoverStyle">
<t t-foreach="this.categories" t-as="category" t-key="category">
<t t-if="this.chartTypeByCategories[category]">
<h5 class="my-3" t-out="category_value"/>
<div class="d-flex flex-wrap">
<t
t-foreach="this.chartTypeByCategories[category]"
t-as="properties"
t-key="properties.chartSubtype">
<div
class="o-chart-type-item"
t-att-title="properties.displayName"
t-on-click="() => this.onTypeChange(properties.chartSubtype)"
t-att-data-id="properties.chartSubtype"
t-att-class="{'selected': properties === selectedChartProperties}">
<t t-call="{{properties.preview}}"/>
</div>
</t>
</div>
</t>
</t>
</div>
</Popover>
<ChartTypePickerPopover
t-if="this.state.popoverProps"
popoverProps="this.state.popoverProps"
width="this.state.popoverWidth"
onClose.bind="this.closePopover"
onSelectSubType.bind="this.onTypeChange"
parentRef="this.selectRef"
supportedTypes="this.getSupportedChartTypes()"
selectedChartSubType="selectedChartProperties.chartSubtype"
/>
</t>
</templates>
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
.o-spreadsheet {
.o-popover .o-chart-select-popover {
.o-chart-type-item {
cursor: pointer;
padding: 2px 5px;
margin: 1px 2px;
border: 1px solid transparent;
&.selected,
&:hover {
border-color: var(--os-action-color);
background: var(--os-badge-selected-color);
}
.o-chart-preview {
width: 48px;
height: 48px;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { Signal, signal } from "@odoo/owl";
import { Component, useExternalListener } from "../../../../owl3_compatibility_layer";
import { chartSubtypeRegistry } from "../../../../registries/chart_subtype_registry";
import { ChartType } from "../../../../types/chart/chart";
import {
chartCategories,
ChartSubtypeProperties,
} from "../../../../types/chart_subtype_properties";
import { SpreadsheetChildEnv } from "../../../../types/spreadsheet_env";
import { cssPropertiesToCss } from "../../../helpers/css";
import { isChildEvent } from "../../../helpers/dom_helpers";
import { Popover } from "../../../popover/popover";

interface Props {
supportedTypes: Set<ChartType>;
width: number;
onClose: () => void;
onSelectSubType: (type: string) => void;
parentRef?: Signal<HTMLElement | null>;
selectedChartSubType?: string;
}

export class ChartTypePickerPopover extends Component<Props, SpreadsheetChildEnv> {
static template = "o-spreadsheet-ChartTypePickerPopover";
static components = { Popover };
static props = {
width: Number,
parentRef: { type: Object, optional: true },
onClose: Function,
onSelectSubType: Function,
chartDataSourceType: { type: String, optional: true },
supportedTypes: Object,
selectedChartSubType: { type: String, optional: true },
};

categories = chartCategories;
chartTypeByCategories: Record<string, ChartSubtypeProperties[]> = {};

popoverRef = signal<HTMLElement | null>(null);

setup(): void {
useExternalListener(window, "pointerdown", this.onExternalClick, { capture: true });

for (const subtypeProperties of chartSubtypeRegistry.getAll()) {
if (!this.props.supportedTypes.has(subtypeProperties.chartType)) {
continue;
}
if (this.chartTypeByCategories[subtypeProperties.category]) {
this.chartTypeByCategories[subtypeProperties.category].push(subtypeProperties);
} else {
this.chartTypeByCategories[subtypeProperties.category] = [subtypeProperties];
}
}
}

onExternalClick(ev: MouseEvent) {
if (
isChildEvent(this.popoverRef()?.parentElement, ev) ||
isChildEvent(this.props.parentRef?.(), ev)
) {
return;
}
this.props.onClose();
}

onTypeChange(type: ChartType) {
this.props.onSelectSubType(type);
this.props.onClose();
}

get popoverStyle() {
return cssPropertiesToCss({ width: `${this.props.width}px` });
}
}
Loading