Skip to content

Commit

Permalink
Merge pull request #81 from Alex-NRCan/fix-sliders
Browse files Browse the repository at this point in the history
fix(axis) - Fixes the axis overlapping, changed the way the markers/label appear to realign on ChartJS design
  • Loading branch information
jolevesq authored Oct 2, 2024
2 parents 62b1611 + 665d171 commit c4a471e
Showing 6 changed files with 368 additions and 351 deletions.
9 changes: 9 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -15,6 +15,13 @@
<!-- <script src="https://alex-nrcan.github.io/geoview/cgpv-main.js"></script> -->
<script src="https://canadian-geospatial-platform.github.io/geoview/public/cgpv-main.js"></script>

<style>
/* To help with display */
.MuiGrid2-container {
width: 600px;
}
</style>

<script>
const DATA_INPUT_LINE_1 = {
chart: 'line',
@@ -861,6 +868,8 @@

<!--------------- CHART IS HERE ----------------->
<div id="root2aca7b6b288c" style="position: relative; width: 800px;"></div>
<!--------------- CHART IS HERE ----------------->

<div>
<button onclick="redrawChart()" style="padding:5px;"><strong>Looks weird? Redraw!</strong></button>
<button onclick="isLoadingState(1)" style="padding:5px;"><strong>Sim. loading whole chart</strong></button>
342 changes: 171 additions & 171 deletions src/app.tsx
Original file line number Diff line number Diff line change
@@ -1,171 +1,171 @@
import { GeoChart } from './chart';
import { GeoChartConfig, ChartType, ChartOptions, ChartData, GeoChartAction, DefaultDataPoint } from './types';
import { SchemaValidator } from './chart-schema-validator';

/**
* Main props for the Application
*/
export interface TypeAppProps {
schemaValidator: SchemaValidator;
}

/**
* Create a container to visualize a GeoChart in a standalone manner.
*
* @returns {JSX.Element} the element that has the GeoChart
*/
export function App(props: TypeAppProps): JSX.Element {
// Can't type the window object to a 'TypeWindow', because we don't have access to the cgpv library when this line runs.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const w = window as any;
// Fetch the cgpv module
const { cgpv } = w;
const { react, ui } = cgpv;
const { useEffect, useState, useCallback } = react;
const { Box } = ui.elements;
const { schemaValidator } = props;

// #region USE STATE SECTION ****************************************************************************************

const [inputs, setInputs] = useState() as [
GeoChartConfig<ChartType> | undefined,
React.Dispatch<React.SetStateAction<GeoChartConfig<ChartType> | undefined>>
];
const [chart, setChart] = useState() as [ChartType, React.Dispatch<React.SetStateAction<ChartType>>];
const [data, setData] = useState() as [
ChartData<ChartType, DefaultDataPoint<ChartType>, string> | undefined,
React.Dispatch<React.SetStateAction<ChartData<ChartType, DefaultDataPoint<ChartType>, string> | undefined>>
];
const [options, setOptions] = useState() as [ChartOptions | undefined, React.Dispatch<React.SetStateAction<ChartOptions> | undefined>];
const [action, setAction] = useState() as [GeoChartAction, React.Dispatch<React.SetStateAction<GeoChartAction>>];
const [language, setLanguage] = useState() as [string, React.Dispatch<React.SetStateAction<string>>];
const [isLoadingChart, setIsLoadingChart] = useState() as [boolean, React.Dispatch<React.SetStateAction<boolean>>];
const [isLoadingDatasource, setIsLoadingDatasource] = useState() as [boolean, React.Dispatch<React.SetStateAction<boolean>>];

// #endregion

// #region EVENT HANDLERS SECTION ***********************************************************************************

/**
* Handles when the Chart has to be loaded with data or options.
*/
const handleChartLoad = (e: Event): void => {
const ev = e as CustomEvent;

// If inputs provided
if (ev.detail.inputs) {
setInputs(ev.detail.inputs);
} else {
setInputs(undefined); // Clear
if (ev.detail.chart) {
setChart(ev.detail.chart);
}
if (ev.detail.options) {
setOptions(ev.detail.options);
}
if (ev.detail.data) {
setData(ev.detail.data);
}
setAction({ shouldRedraw: true });
}
};

/**
* Handles when the Chart has to be redrawn.
*/
const handleChartRedraw = (): void => {
setAction({ shouldRedraw: true });
};

/**
* Handles when the Chart has to show a loading state.
*/
const handleChartLoading = (e: Event): void => {
const ev = e as CustomEvent;

setIsLoadingChart(false);
setIsLoadingDatasource(false);
if (ev.detail.state === 1) setIsLoadingChart(true);
if (ev.detail.state === 2) setIsLoadingDatasource(true);
};

// #endregion

// #region HOOKS SECTION ********************************************************************************************

/**
* Handles when the Chart has parsed inputs.
* @param theChart ChartType The chart type
* @param theOptions ChartOptions The chart options
* @param theData ChartData The chart data
*/
const handleParsed = useCallback((theChart: ChartType, theOptions: ChartOptions, theData: ChartData): void => {
// Raise event higher
window.dispatchEvent(new CustomEvent('chart/parsed', { detail: { chart: theChart, options: theOptions, data: theData } }));
}, []);

/**
* Handles a generic error that happened in the Chart component.
* @param error The error message
* @param exception The exception that happened (if any)
*/
const handleError = useCallback((error: string, exception: unknown): void => {
// Show the error using an alert. We can't use the cgpv SnackBar as that component is attached to
// a map and we're not even running a cgpv.init() at all here.
// eslint-disable-next-line no-console
console.error(error, exception);
// eslint-disable-next-line no-alert
alert(error);
}, []);

/**
* Handles when the Chart language is changed.
*/
const handleChartLanguage = useCallback((e: Event): void => {
const ev = e as CustomEvent;
setLanguage(ev.detail.language);
}, []);

// Effect hook to add and remove event listeners.
// Using window.addEventListener is unconventional here, but this is strictly for the 'app' logic with the index.html.
// It's not something to be used by the developers when using the Chart component in their projects.
useEffect(() => {
window.addEventListener('chart/load', handleChartLoad);
window.addEventListener('chart/redraw', handleChartRedraw);
window.addEventListener('chart/language', handleChartLanguage);
window.addEventListener('chart/isLoading', handleChartLoading);
return () => {
window.removeEventListener('chart/load', handleChartLoad);
window.removeEventListener('chart/redraw', handleChartRedraw);
window.removeEventListener('chart/language', handleChartLanguage);
window.removeEventListener('chart/isLoading', handleChartLoading);
};
}, [handleChartLanguage]);

// #endregion

// #region RENDER SECTION START *************************************************************************************

// Render the Chart
return (
<Box sx={{ padding: '10px' }}>
<GeoChart
inputs={inputs}
schemaValidator={schemaValidator}
chart={chart}
data={data}
options={options}
action={action}
language={language}
isLoadingChart={isLoadingChart}
isLoadingDatasource={isLoadingDatasource}
onParsed={handleParsed}
onError={handleError}
/>
</Box>
);

// #endregion
}

export default App;
import { GeoChart } from './chart';
import { GeoChartConfig, ChartType, ChartOptions, ChartData, GeoChartAction, DefaultDataPoint } from './types';
import { SchemaValidator } from './chart-schema-validator';

/**
* Main props for the Application
*/
export interface TypeAppProps {
schemaValidator: SchemaValidator;
}

/**
* Create a container to visualize a GeoChart in a standalone manner.
*
* @returns {JSX.Element} the element that has the GeoChart
*/
export function App(props: TypeAppProps): JSX.Element {
// Can't type the window object to a 'TypeWindow', because we don't have access to the cgpv library when this line runs.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const w = window as any;
// Fetch the cgpv module
const { cgpv } = w;
const { react, ui } = cgpv;
const { useEffect, useState, useCallback } = react;
const { Box } = ui.elements;
const { schemaValidator } = props;

// #region USE STATE SECTION ****************************************************************************************

const [inputs, setInputs] = useState() as [
GeoChartConfig<ChartType> | undefined,
React.Dispatch<React.SetStateAction<GeoChartConfig<ChartType> | undefined>>
];
const [chart, setChart] = useState() as [ChartType, React.Dispatch<React.SetStateAction<ChartType>>];
const [data, setData] = useState() as [
ChartData<ChartType, DefaultDataPoint<ChartType>, string> | undefined,
React.Dispatch<React.SetStateAction<ChartData<ChartType, DefaultDataPoint<ChartType>, string> | undefined>>
];
const [options, setOptions] = useState() as [ChartOptions | undefined, React.Dispatch<React.SetStateAction<ChartOptions> | undefined>];
const [action, setAction] = useState() as [GeoChartAction, React.Dispatch<React.SetStateAction<GeoChartAction>>];
const [language, setLanguage] = useState() as [string, React.Dispatch<React.SetStateAction<string>>];
const [isLoadingChart, setIsLoadingChart] = useState() as [boolean, React.Dispatch<React.SetStateAction<boolean>>];
const [isLoadingDatasource, setIsLoadingDatasource] = useState() as [boolean, React.Dispatch<React.SetStateAction<boolean>>];

// #endregion

// #region EVENT HANDLERS SECTION ***********************************************************************************

/**
* Handles when the Chart has to be loaded with data or options.
*/
const handleChartLoad = (e: Event): void => {
const ev = e as CustomEvent;

// If inputs provided
if (ev.detail.inputs) {
setInputs(ev.detail.inputs);
} else {
setInputs(undefined); // Clear
if (ev.detail.chart) {
setChart(ev.detail.chart);
}
if (ev.detail.options) {
setOptions(ev.detail.options);
}
if (ev.detail.data) {
setData(ev.detail.data);
}
setAction({ shouldRedraw: true });
}
};

/**
* Handles when the Chart has to be redrawn.
*/
const handleChartRedraw = (): void => {
setAction({ shouldRedraw: true });
};

/**
* Handles when the Chart has to show a loading state.
*/
const handleChartLoading = (e: Event): void => {
const ev = e as CustomEvent;

setIsLoadingChart(false);
setIsLoadingDatasource(false);
if (ev.detail.state === 1) setIsLoadingChart(true);
if (ev.detail.state === 2) setIsLoadingDatasource(true);
};

// #endregion

// #region HOOKS SECTION ********************************************************************************************

/**
* Handles when the Chart has parsed inputs.
* @param theChart ChartType The chart type
* @param theOptions ChartOptions The chart options
* @param theData ChartData The chart data
*/
const handleParsed = useCallback((theChart: ChartType, theOptions: ChartOptions, theData: ChartData): void => {
// Raise event higher
window.dispatchEvent(new CustomEvent('chart/parsed', { detail: { chart: theChart, options: theOptions, data: theData } }));
}, []);

/**
* Handles a generic error that happened in the Chart component.
* @param error The error message
* @param exception The exception that happened (if any)
*/
const handleError = useCallback((error: string, exception: unknown): void => {
// Show the error using an alert. We can't use the cgpv SnackBar as that component is attached to
// a map and we're not even running a cgpv.init() at all here.
// eslint-disable-next-line no-console
console.error(error, exception);
// eslint-disable-next-line no-alert
alert(error);
}, []);

/**
* Handles when the Chart language is changed.
*/
const handleChartLanguage = useCallback((e: Event): void => {
const ev = e as CustomEvent;
setLanguage(ev.detail.language);
}, []);

// Effect hook to add and remove event listeners.
// Using window.addEventListener is unconventional here, but this is strictly for the 'app' logic with the index.html.
// It's not something to be used by the developers when using the Chart component in their projects.
useEffect(() => {
window.addEventListener('chart/load', handleChartLoad);
window.addEventListener('chart/redraw', handleChartRedraw);
window.addEventListener('chart/language', handleChartLanguage);
window.addEventListener('chart/isLoading', handleChartLoading);
return () => {
window.removeEventListener('chart/load', handleChartLoad);
window.removeEventListener('chart/redraw', handleChartRedraw);
window.removeEventListener('chart/language', handleChartLanguage);
window.removeEventListener('chart/isLoading', handleChartLoading);
};
}, [handleChartLanguage]);

// #endregion

// #region RENDER SECTION START *************************************************************************************

// Render the Chart
return (
<Box sx={{ padding: '10px' }}>
<GeoChart
inputs={inputs}
schemaValidator={schemaValidator}
chart={chart}
data={data}
options={options}
action={action}
language={language}
isLoadingChart={isLoadingChart}
isLoadingDatasource={isLoadingDatasource}
onParsed={handleParsed}
onError={handleError}
/>
</Box>
);

// #endregion
}

export default App;
2 changes: 1 addition & 1 deletion src/chart-parsing.ts
Original file line number Diff line number Diff line change
@@ -624,14 +624,14 @@ export function createChartJSOptions<TType extends ChartType>(

// If line and using a time series
if (chartConfig.chart === 'line' && (chartConfig.geochart.xAxis?.type === 'time' || chartConfig.geochart.xAxis?.type === 'timeseries')) {
// Generate the options object
const optionsLine = options as ChartOptions<'line'>;
optionsLine.scales = {
...optionsLine.scales,
x: {
type: chartConfig.geochart.xAxis?.type,
ticks: {
autoSkip: true,
maxTicksLimit: 20,
major: {
enabled: true,
},
Loading

0 comments on commit c4a471e

Please sign in to comment.