Skip to content

Commit

Permalink
Feature/f 155 charts download button should be available on minimized (
Browse files Browse the repository at this point in the history
…#121)

* feat: chart download button to own componentn - and added button to LineChart component

* fix: added colors to LineChartOperations as hex - fixing the chart png and svg download

* fix: using tailwind-util.getTailwindColor to use nextui colors for highcharts options - fixes the chart export color problem

---------

Co-authored-by: Bohdan Garchu <[email protected]>
  • Loading branch information
plaume8 and bohdangarchu authored Dec 12, 2024
1 parent d88d7b1 commit 9249760
Show file tree
Hide file tree
Showing 10 changed files with 166 additions and 150 deletions.
2 changes: 1 addition & 1 deletion src/app/elements/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ export default async function Elements() {
<AccordionContainer items={AccordionsOperations.getAccordionData()} />
<div className="w-full h-fit flex flex-row flex-wrap gap-10 justify-around px-8 pt-40 pb-16 border-b border-gray-800">
<div className="w-250px h-fit">
<LineChart data={simpleAndSmallLineChartData} small />
<LineChart data={simpleAndSmallLineChartData} small disableDownload />
</div>
<div className="w-250px h-fit">
<LineChart
Expand Down
55 changes: 25 additions & 30 deletions src/components/Charts/LineChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@
import { Button } from '@nextui-org/button';
import { useDisclosure } from '@nextui-org/modal';
import Highcharts from 'highcharts';
import Exporting from 'highcharts/modules/exporting';
import OfflineExporting from 'highcharts/modules/offline-exporting';
import HighchartsReact from 'highcharts-react-official';
import { Maximize4 } from 'iconsax-react';
import { useTheme } from 'next-themes';
import { useEffect, useState } from 'react';
import { useEffect, useRef, useState } from 'react';

import LineChartBarLineSwitchButton from '@/components/Charts/helpers/LineChartBarLineSwitchButton';
import LineChartDownloadButton from '@/components/Charts/helpers/LineChartDownloadButton';
import LineChartSliderButton from '@/components/Charts/helpers/LineChartSliderButton';
import LineChartXAxisSlider from '@/components/Charts/helpers/LineChartXAxisSlider';
import { LineChartModal } from '@/components/Charts/LineChartModal';
Expand All @@ -19,12 +18,6 @@ import { LineChartData } from '@/domain/entities/charts/LineChartData';
import LineChartProps from '@/domain/props/LineChartProps';
import LineChartOperations from '@/operations/charts/LineChartOperations';

// initialize the exporting module
if (typeof Highcharts === 'object') {
Exporting(Highcharts);
OfflineExporting(Highcharts);
}

/**
* The LineChart component is a box that primarily renders a title, description text, and a line chart.
* This component has a width of 100%, so it adjusts to the width of its parent element in which it is used.
Expand All @@ -44,6 +37,7 @@ if (typeof Highcharts === 'object') {
* @param barChartSwitch when selected, the user is given the option to switch to a bar chart (optional)
* @param xAxisSlider when selected, the user is given the option to change the x-axis range via a slider (optional)
* @param small when selected, all components in the line chart box become slightly smaller (optional)
* @param small when selected, the download button dropdown is not shown (optional)
* @param roundLines when selected, all plotted lines will be rounded (optional)
* @param noPadding when selected, the main box has no padding on all sides (optional)
* @param transparentBackground when selected, the background of the entire component is transparent (optional)
Expand All @@ -56,6 +50,7 @@ export function LineChart({
barChartSwitch,
xAxisSlider,
small,
disableDownload,
roundLines,
noPadding,
transparentBackground,
Expand Down Expand Up @@ -88,6 +83,8 @@ export function LineChart({
// handling the x-axis range slider visibility
const [showXAxisSlider, setShowXAxisSlider] = useState(false);

const chartRef = useRef<HighchartsReact.RefObject | null>(null);

// handling the line and bar chart switch and the theme switch;
// also handling changing the x-axis range using the `LineChartXAxisSlider`;
// special: if the selected x-axis range has length 1 -> bar chart is displayed
Expand Down Expand Up @@ -121,27 +118,24 @@ export function LineChart({
<div
className={`flex flex-row gap-0.5 pt-${0.5 * MAIN_BOX_PADDING_FACTOR} pr-${0.5 * MAIN_BOX_PADDING_FACTOR}`}
>
{
// button to hide/show the slider to manipulate the plotted x-axis range of the chart;
// can be disabled via `xAxisSlider`
xAxisSlider && (
<LineChartSliderButton
showXAxisSlider={showXAxisSlider}
setShowXAxisSlider={setShowXAxisSlider}
size={ICON_BUTTON_SIZE}
/>
)
}
{
// button to switch between line and bar chart; can be disabled via `barChartSwitch`
barChartSwitch && (
<LineChartBarLineSwitchButton
showBarChart={showBarChart}
setShowBarChart={setShowBarChart}
size={ICON_BUTTON_SIZE}
/>
)
}
{xAxisSlider && (
<LineChartSliderButton
showXAxisSlider={showXAxisSlider}
setShowXAxisSlider={setShowXAxisSlider}
size={ICON_BUTTON_SIZE}
/>
)}

{barChartSwitch && (
<LineChartBarLineSwitchButton
showBarChart={showBarChart}
setShowBarChart={setShowBarChart}
size={ICON_BUTTON_SIZE}
/>
)}

{!disableDownload && <LineChartDownloadButton chartRef={chartRef} lineChartData={lineChartData} />}

{
// button to trigger the full screen modal; rendered if `expandable`
expandable && (
Expand All @@ -166,6 +160,7 @@ export function LineChart({
<HighchartsReact
highcharts={Highcharts}
options={chartOptions}
ref={chartRef}
containerProps={{
style: {
width: '100%',
Expand Down
106 changes: 15 additions & 91 deletions src/components/Charts/LineChartModal.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,16 @@
import { Button } from '@nextui-org/button';
import { Modal, ModalBody, ModalContent, ModalFooter, ModalHeader } from '@nextui-org/modal';
import { Popover, PopoverContent, PopoverTrigger } from '@nextui-org/popover';
import Highcharts from 'highcharts';
import ExportDataModule from 'highcharts/modules/export-data';
import Exporting from 'highcharts/modules/exporting';
import OfflineExporting from 'highcharts/modules/offline-exporting';
import HighchartsReact from 'highcharts-react-official';
import { DocumentDownload, GalleryImport, Minus } from 'iconsax-react';
import { Minus } from 'iconsax-react';
import { useRef } from 'react';

import LineChartBarLineSwitchButton from '@/components/Charts/helpers/LineChartBarLineSwitchButton';
import LineChartDownloadButton from '@/components/Charts/helpers/LineChartDownloadButton';
import LineChartSliderButton from '@/components/Charts/helpers/LineChartSliderButton';
import LineChartXAxisSlider from '@/components/Charts/helpers/LineChartXAxisSlider';
import { Tooltip } from '@/components/Tooltip/Tooltip';
import LineChartModalProps from '@/domain/props/LineChartModalProps';
import LineChartOperations from '@/operations/charts/LineChartOperations.ts';

// initialize the exporting module
if (typeof Highcharts === 'object') {
Exporting(Highcharts);
ExportDataModule(Highcharts);
OfflineExporting(Highcharts);
}

/**
* This component is tied to the `LineChart` component and should not be used independently.
Expand All @@ -32,6 +21,7 @@ if (typeof Highcharts === 'object') {
export function LineChartModal({
title,
description,
disableDownload,
barChartSwitch,
xAxisSlider,
lineChartData,
Expand All @@ -46,7 +36,6 @@ export function LineChartModal({
selectedXAxisRange,
setSelectedXAxisRange,
}: LineChartModalProps) {
// referencing the Highcharts chart object (needed for download the chart as a png)
const chartRef = useRef<HighchartsReact.RefObject | null>(null);

// full screen modal by the 'LineChart' component that can be opened if `expandable==true`;
Expand All @@ -66,84 +55,19 @@ export function LineChartModal({
<div className="flex flex-row justify-between w-full h-full">
<h2 className="flex flex-col justify-center font-normal text-sm sm:text-md md:text-lg"> {title} </h2>
<div className="flex flex-row w-fit h-full gap-0.5 sm:gap-4 md:gap-6">
{
// button to hide/show the slider to manipulate the plotted x-axis range of the chart;
// can be disabled via `xAxisSlider`
xAxisSlider && (
<LineChartSliderButton
showXAxisSlider={showXAxisSlider}
setShowXAxisSlider={setShowXAxisSlider}
size={4}
/>
)
}
{
// button to switch between line and bar chart; can be disabled via `barChartSwitch`
barChartSwitch && (
<LineChartBarLineSwitchButton
showBarChart={showBarChart}
setShowBarChart={setShowBarChart}
size={4}
/>
)
}
{xAxisSlider && (
<LineChartSliderButton
showXAxisSlider={showXAxisSlider}
setShowXAxisSlider={setShowXAxisSlider}
size={4}
/>
)}

{barChartSwitch && (
<LineChartBarLineSwitchButton showBarChart={showBarChart} setShowBarChart={setShowBarChart} size={4} />
)}

{/* chart download dropdown */}
<Popover placement="bottom" offset={10} backdrop="opaque">
<PopoverTrigger>
<Button isIconOnly variant="light" size="sm">
<Tooltip text="Export Chart / Data">
<DocumentDownload className="h-4 w-4" />
</Tooltip>
</Button>
</PopoverTrigger>
<PopoverContent className="w-fit h-fit p-2 flex flex-col gap-1 items-start">
<Button
variant="light"
size="sm"
className="w-full justify-start"
onPress={() => {
if (chartRef.current) LineChartOperations.downloadChartPNG(chartRef.current);
}}
startContent={<GalleryImport className="h-4 w-4" />}
>
Chart as PNG
</Button>
<Button
variant="light"
size="sm"
className="w-full justify-start"
onPress={() => {
if (chartRef.current) LineChartOperations.downloadChartDataSVG(chartRef.current);
}}
startContent={<GalleryImport className="h-4 w-4" />}
>
Chart as SVG
</Button>
<Button
variant="light"
size="sm"
className="w-full justify-start"
onPress={() => {
if (chartRef.current) LineChartOperations.downloadChartDataCSV(chartRef.current);
}}
startContent={<DocumentDownload className="h-4 w-4" />}
>
Data as CSV
</Button>
<Button
variant="light"
size="sm"
className="w-full justify-start"
onPress={() => {
if (chartRef.current) LineChartOperations.downloadDataJSON(lineChartData);
}}
startContent={<DocumentDownload className="h-4 w-4" />}
>
Data as JSON
</Button>
</PopoverContent>
</Popover>
{!disableDownload && <LineChartDownloadButton chartRef={chartRef} lineChartData={lineChartData} />}

{/* close model button */}
<Tooltip text="Close">
Expand Down
72 changes: 72 additions & 0 deletions src/components/Charts/helpers/LineChartDownloadButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { Button } from '@nextui-org/button';
import { Popover, PopoverContent, PopoverTrigger } from '@nextui-org/popover';
import { DocumentDownload, GalleryImport } from 'iconsax-react';

import { Tooltip } from '@/components/Tooltip/Tooltip';
import { LineChartDownloadButtonProps } from '@/domain/props/LineChartProps';
import LineChartOperations from '@/operations/charts/LineChartOperations.ts';

/**
* This component is tied to the `LineChart` and `LineChartModal` component
* and should not be used independently.
* It renders a button to open a dropdown menu to download the chart as csv, png, etc.
*/
export default function LineChartDownloadButton({ chartRef, lineChartData }: LineChartDownloadButtonProps) {
return (
<Popover placement="bottom" offset={10} backdrop="opaque">
<PopoverTrigger>
<Button isIconOnly variant="light" size="sm">
<Tooltip text="Export Chart / Data">
<DocumentDownload className="h-4 w-4" />
</Tooltip>
</Button>
</PopoverTrigger>
<PopoverContent className="w-fit h-fit p-2 flex flex-col gap-1 items-start">
<Button
variant="light"
size="sm"
className="w-full justify-start"
onPress={() => {
if (chartRef.current) LineChartOperations.downloadChartPNG(chartRef.current);
}}
startContent={<GalleryImport className="h-4 w-4" />}
>
Chart as PNG
</Button>
<Button
variant="light"
size="sm"
className="w-full justify-start"
onPress={() => {
if (chartRef.current) LineChartOperations.downloadChartDataSVG(chartRef.current);
}}
startContent={<GalleryImport className="h-4 w-4" />}
>
Chart as SVG
</Button>
<Button
variant="light"
size="sm"
className="w-full justify-start"
onPress={() => {
if (chartRef.current) LineChartOperations.downloadChartDataCSV(chartRef.current);
}}
startContent={<DocumentDownload className="h-4 w-4" />}
>
Data as CSV
</Button>
<Button
variant="light"
size="sm"
className="w-full justify-start"
onPress={() => {
LineChartOperations.downloadDataJSON(lineChartData);
}}
startContent={<DocumentDownload className="h-4 w-4" />}
>
Data as JSON
</Button>
</PopoverContent>
</Popover>
);
}
4 changes: 2 additions & 2 deletions src/components/Charts/helpers/LineChartXAxisSlider.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Slider } from '@nextui-org/slider';

import { LineChartXAxisSlider } from '@/domain/props/LineChartProps';
import { LineChartXAxisSliderProps } from '@/domain/props/LineChartProps';
import LineChartOperations from '@/operations/charts/LineChartOperations.ts';

/**
Expand All @@ -13,7 +13,7 @@ export default function LineChartXAxisSlider({
selectedXAxisRange,
setSelectedXAxisRange,
data,
}: LineChartXAxisSlider) {
}: LineChartXAxisSliderProps) {
const xAxisValues: number[] = LineChartOperations.getDistinctXAxisValues(data);

return (
Expand Down
1 change: 1 addition & 0 deletions src/domain/props/LineChartModalProps.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { LineChartData } from '@/domain/entities/charts/LineChartData.ts';
export default interface LineChartModalProps {
title?: string;
description?: string;
disableDownload?: boolean;
barChartSwitch?: boolean;
xAxisSlider?: boolean;

Expand Down
11 changes: 10 additions & 1 deletion src/domain/props/LineChartProps.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import HighchartsReact from 'highcharts-react-official';
import { MutableRefObject } from 'react';

import { BalanceOfTradeGraph } from '@/domain/entities/charts/BalanceOfTradeGraph.ts';
import { CurrencyExchangeGraph } from '@/domain/entities/charts/CurrencyExchangeGraph.ts';
import { InflationGraphs } from '@/domain/entities/charts/InflationGraphs.ts';
Expand All @@ -10,6 +13,7 @@ export default interface LineChartProps {
barChartSwitch?: boolean;
xAxisSlider?: boolean;
small?: boolean;
disableDownload?: boolean;
roundLines?: boolean;
noPadding?: boolean;
transparentBackground?: boolean;
Expand All @@ -32,8 +36,13 @@ export interface LineChartBarLineSwitchButtonProps {
size: number;
}

export interface LineChartXAxisSlider {
export interface LineChartXAxisSliderProps {
selectedXAxisRange: number[]; // [xAxisRangeMinIndex, xAxisRangeMaxIndex]
setSelectedXAxisRange: (ns: number[]) => void;
data: LineChartData;
}

export interface LineChartDownloadButtonProps {
chartRef: MutableRefObject<HighchartsReact.RefObject | null>;
lineChartData: LineChartData;
}
Loading

0 comments on commit 9249760

Please sign in to comment.