From 06919fc9b5fe38b085e25f46f061e966b8d07df2 Mon Sep 17 00:00:00 2001 From: scespinoza Date: Wed, 13 Nov 2024 15:11:29 -0300 Subject: [PATCH 1/2] adds share button --- src/hooks/permalink.tsx | 15 +++++++++--- src/hooks/translation.ts | 2 ++ src/state/queries.ts | 5 ++++ src/state/thunks.ts | 1 + src/utils/structs.ts | 2 ++ src/vizbuilder/components/ChartCard.tsx | 31 +++++++++++++++++++++++- src/vizbuilder/components/Vizbuilder.tsx | 20 ++++++++++----- src/vizbuilder/hooks/useD3plusConfig.ts | 2 +- 8 files changed, 67 insertions(+), 11 deletions(-) diff --git a/src/hooks/permalink.tsx b/src/hooks/permalink.tsx index cf25337..983a90c 100644 --- a/src/hooks/permalink.tsx +++ b/src/hooks/permalink.tsx @@ -14,6 +14,9 @@ export function serializePermalink(item: QueryItem): string { Object.entries(request).filter(entry => entry[1] != null && entry[1] !== "") ); search.set("panel", item.panel || "table"); + if (item.chart !== "" && item.chart){ + search.set("chart", item.chart) + } return search.toString(); } @@ -22,9 +25,10 @@ export function parsePermalink(cube: TesseractCube, value: string | URLSearchPar const search = new URLSearchParams(value); const params = requestToQueryParams(cube, search); - + console.log(search.get("chart")); return buildQuery({ panel: search.get("panel") || "table", + chart: search.get("chart") || "", params }); } @@ -65,8 +69,12 @@ export function usePermalink( if (currPermalink !== nextPermalink) { const nextLocation = `${window.location.pathname}?${nextPermalink}`; const oldPanel = new URLSearchParams(window.location.search).get("panel"); - // If only the panel changed, use replaceState - if (oldPanel && oldPanel[1] !== panel) { + const oldChart = new URLSearchParams(window.location.search).get("chart"); + // If only the panel or chartchanged, use replaceState + if ( + (oldPanel && oldPanel[1] !== panel) + || (oldChart && oldChart[1] !== queryItem.chart) + ) { window.history.replaceState(queryItem, "", nextLocation); } else { window.history.pushState(queryItem, "", nextLocation); @@ -103,6 +111,7 @@ export function useUpdatePermaLink({ export function useKey(params: Partial = {}) { const queryItem = useSelector(selectCurrentQueryItem); + console.log(queryItem); if (isValidQuery(queryItem.params)) { return serializePermalink({...queryItem, params: {...queryItem.params, ...params}}); } diff --git a/src/hooks/translation.ts b/src/hooks/translation.ts index 8c85f21..cabf6b7 100644 --- a/src/hooks/translation.ts +++ b/src/hooks/translation.ts @@ -267,6 +267,8 @@ export const defaultTranslation = { }, vizbuilder: { action_close: "Close", + action_share: "Share", + share_copied: "Copied", action_enlarge: "Enlarge", action_fileissue: "Report an issue", action_retry: "Retry", diff --git a/src/state/queries.ts b/src/state/queries.ts index e6e120c..55fa0a7 100644 --- a/src/state/queries.ts +++ b/src/state/queries.ts @@ -88,6 +88,11 @@ export const queriesSlice = createSlice({ const current = state.itemMap[state.current]; current.panel = action.payload; }, + + updateChart(state, action: Action) { + const current = state.itemMap[state.current]; + current.chart = action.payload; + }, /** * Remove a single CutItem from the current QueryItem. diff --git a/src/state/thunks.ts b/src/state/thunks.ts index 1408c34..d097ccf 100644 --- a/src/state/thunks.ts +++ b/src/state/thunks.ts @@ -259,6 +259,7 @@ export function willParseQueryUrl(url: string | URL): ExplorerThunk): QueryItem { label: props.label || "", isDirty: true, panel: props.panel || null, + chart: props.chart || null, params: buildQueryParams(props.params || {}), result: { data: [], diff --git a/src/vizbuilder/components/ChartCard.tsx b/src/vizbuilder/components/ChartCard.tsx index 5d7ce09..f530716 100644 --- a/src/vizbuilder/components/ChartCard.tsx +++ b/src/vizbuilder/components/ChartCard.tsx @@ -3,17 +3,20 @@ import {Box, Button, Group, Paper, Stack} from "@mantine/core"; import { IconArrowsMaximize, IconArrowsMinimize, + IconCheck, IconDownload, IconPhotoDown, + IconShare, IconVectorTriangle, } from "@tabler/icons-react"; import {saveElement} from "d3plus-export"; -import React, {useMemo, useRef} from "react"; +import React, {useMemo, useRef, useState} from "react"; import type {TesseractMeasure} from "../../api/tesseract/schema"; import {useTranslation} from "../../hooks/translation"; import {asArray as castArray} from "../../utils/array"; import {useD3plusConfig} from "../hooks/useD3plusConfig"; import {ErrorBoundary} from "./ErrorBoundary"; +import {useClipboard} from '@mantine/hooks'; const iconByFormat = { jpg: IconPhotoDown, @@ -66,6 +69,10 @@ export function ChartCard(props: { t: translate, }); + const clipboard = useClipboard(); + + const [isShared, setIsShared] = useState(false); + const downloadButtons = useMemo(() => { // Sanitize filename for Windows and Unix const filename = ( @@ -119,6 +126,27 @@ export function ChartCard(props: { ); }, [isFullMode, translate, onFocus]); + const shareButton = useMemo(() => { + return ( + + ); + }, [clipboard, translate, isShared]); + const height = isFullMode ? "calc(100vh - 3rem)" : 300; if (!ChartComponent) return null; @@ -128,6 +156,7 @@ export function ChartCard(props: { + {isFullMode && shareButton} {downloadButtons} {onFocus && focusButton} diff --git a/src/vizbuilder/components/Vizbuilder.tsx b/src/vizbuilder/components/Vizbuilder.tsx index 6c7b52a..b21d261 100644 --- a/src/vizbuilder/components/Vizbuilder.tsx +++ b/src/vizbuilder/components/Vizbuilder.tsx @@ -8,12 +8,15 @@ import { } from "@datawheel/vizbuilder"; import {Modal, SimpleGrid} from "@mantine/core"; import cls from "clsx"; -import React, {useCallback, useMemo, useState} from "react"; +import React, {useCallback, useMemo} from "react"; import type {TesseractLevel, TesseractMeasure} from "../../api/tesseract/schema"; import {asArray as castArray} from "../../utils/array"; import {ChartCard} from "./ChartCard"; import {ErrorBoundary} from "./ErrorBoundary"; import {NonIdealState} from "./NonIdealState"; +import {useSelector} from "react-redux"; +import {selectCurrentQueryItem} from "../../state/queries"; +import {useSettings} from "../../hooks/settings"; export type VizbuilderProps = React.ComponentProps; @@ -125,7 +128,13 @@ export function Vizbuilder(props: { userConfig, } = props; - const [currentChart, setCurrentChart] = useState(""); + const queryItem = useSelector(selectCurrentQueryItem); + const currentChart = queryItem?.chart || ""; + const {actions} = useSettings(); + + const setCurrentChart = useCallback((chart: string) => { + actions.updateChart(chart); + }, [actions]); // Normalize measureConfig to function type const getMeasureConfig = useMemo(() => { @@ -142,7 +151,7 @@ export function Vizbuilder(props: { // Compute possible charts const charts = useMemo(() => { const charts = generateCharts(castArray(datasets), { - chartLimits, + chartLimits: chartLimits as ChartLimits | undefined, chartTypes, datacap, getTopojsonConfig, @@ -150,7 +159,6 @@ export function Vizbuilder(props: { return Object.fromEntries(charts.map(chart => [chart.key, chart])); }, [chartLimits, chartTypes, datacap, datasets, getTopojsonConfig]); - console.log("charts", charts); const content = useMemo(() => { const Notice = nonIdealState || NonIdealState; @@ -183,7 +191,7 @@ export function Vizbuilder(props: { setCurrentChart(chart.key)} showConfidenceInt={showConfidenceInt} @@ -212,7 +220,7 @@ export function Vizbuilder(props: { setCurrentChart("")} showConfidenceInt={showConfidenceInt} diff --git a/src/vizbuilder/hooks/useD3plusConfig.ts b/src/vizbuilder/hooks/useD3plusConfig.ts index c549229..b0bdcdd 100644 --- a/src/vizbuilder/hooks/useD3plusConfig.ts +++ b/src/vizbuilder/hooks/useD3plusConfig.ts @@ -374,7 +374,7 @@ function _buildTitle(t: TranslateFunction, chart: Chart) { const [mainSeries, otherSeries] = series; const {measure} = values; const timeline = chart.timeline || chart.time; - console.log({chart}) + const seriesStr = (series: Chart["series"][number]) => { if(!series) return ""; const {members} = series.captions[series.level.name]; From 43bed1aff78c9232ed829adf85348ebb3d188d45 Mon Sep 17 00:00:00 2001 From: scespinoza Date: Wed, 13 Nov 2024 15:37:49 -0300 Subject: [PATCH 2/2] adds experimental topic_order support --- src/components/SelectCubes.tsx | 8 +++++++- src/hooks/buildGraph.tsx | 4 ++++ src/utils/graph.js | 9 +++++++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/components/SelectCubes.tsx b/src/components/SelectCubes.tsx index cb4f999..9b3ca76 100644 --- a/src/components/SelectCubes.tsx +++ b/src/components/SelectCubes.tsx @@ -168,11 +168,17 @@ function useBuildGraph(items, locale) { .map(item => { const {name} = item; const topic = getAnnotation(item, "topic", locale); + const topic_order = getAnnotation(item, "topic_order", locale); const subtopic = getAnnotation(item, "subtopic", locale); const table = getAnnotation(item, "table", locale); const hide = getAnnotation(item, "hide_in_ui", locale); + console.log(topic, topic_order); if (!yn(hide)) { graph.addNode(topic); + if(topic_order) { + console.log(topic, topic_order); + graph.addTopicOrder(topic, topic_order) + }; graph.addNode(subtopic); graph.addNode(name); graph.addEdge(topic, subtopic); @@ -301,7 +307,7 @@ function RootAccordions({items, graph, locale, selectedItem, onSelectCube}) { } })} > - {items.map(item => { + {items.sort((a, b) => graph.topicOrder[a] - graph.topicOrder[b]).map(item => { return ( {item} diff --git a/src/hooks/buildGraph.tsx b/src/hooks/buildGraph.tsx index 4ee55f7..f09ec1b 100644 --- a/src/hooks/buildGraph.tsx +++ b/src/hooks/buildGraph.tsx @@ -17,6 +17,7 @@ export default function useBuildGraph(locale: string): Graph { .map(item => { const {name} = item; const topic = getAnnotation(item, "topic", locale); + const topic_order = getAnnotation(item, "topic_order", locale); const subtopic = getAnnotation(item, "subtopic", locale); const table = getAnnotation(item, "table", locale); const hide = getAnnotation(item, "hide_in_ui", locale); @@ -27,6 +28,9 @@ export default function useBuildGraph(locale: string): Graph { graph.addNode(name); graph.addEdge(topic, subtopic); graph.addEdge(subtopic, name); + if(topic_order) { + graph.addTopicOrder(topic, topic_order) + } return item; } diff --git a/src/utils/graph.js b/src/utils/graph.js index 32c1793..3cc5f60 100644 --- a/src/utils/graph.js +++ b/src/utils/graph.js @@ -6,6 +6,15 @@ class Graph { this.nodes = new Set([]); this.adjList = {}; this.items = []; + this.topicOrder = {}; + } + + addTopicOrder(topic, order) { + this.topicOrder[topic] = Number(order); + } + + getTopicOrder(topic) { + return this.topicOrder[topic] || 0; } addNode(node) {