Skip to content

Commit 2a8bdca

Browse files
committed
feat: support passing visConfig param to reproduce charts
1 parent a437beb commit 2a8bdca

File tree

17 files changed

+444
-1399
lines changed

17 files changed

+444
-1399
lines changed

R/gwalkr.R

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#' "gender" = list(analyticalType = "dimension", semanticType = "nominal"),
1616
#' "age" = list(analyticalType = "measure", semanticType = "quantitative")
1717
#' )}
18+
#' @param visConfig An optional config string to reproduce your chart. You can copy the string by clicking "export config" button on the GWalkR interface.
1819
#'
1920
#' @return An \code{htmlwidget} object that can be rendered in R environments
2021
#'
@@ -23,7 +24,7 @@
2324
#' gwalkr(mtcars)
2425
#'
2526
#' @export
26-
gwalkr <- function(data, lang = "en", columnSpecs = list()) {
27+
gwalkr <- function(data, lang = "en", columnSpecs = list(), visConfig = NULL) {
2728
if (!is.data.frame(data)) stop("data must be a data frame")
2829
lang <- match.arg(lang, choices = c("en", "ja", "zh"))
2930

@@ -34,7 +35,8 @@ gwalkr <- function(data, lang = "en", columnSpecs = list()) {
3435
dataSource = jsonlite::toJSON(data),
3536
rawFields = rawFields,
3637
i18nLang = lang,
37-
hideDataSourceConfig = TRUE
38+
hideDataSourceConfig = TRUE,
39+
visSpec = visConfig
3840
)
3941

4042
# create widget

inst/htmlwidgets/gwalkr.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@ dependencies:
44
src: htmlwidgets/lib/gwalkr
55
script:
66
- gwalkr-app.iife.js
7+
stylesheet:
78
- style.css
8-
- vite.svg

man/gwalkr.Rd

Lines changed: 4 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

web_app/index.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
<!DOCTYPE html>
2-
<html lang="en">
2+
<html lang="en" class="dark">
33
<head>
44
<meta charset="UTF-8" />
55
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
66
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7-
<title>Vite + React + TS</title>
7+
<title>GWalkR App</title>
88
</head>
99
<body>
1010
<div id="rwalker-app"></div>

web_app/package.json

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,26 @@
1818
"mobx-react-lite": "^3.4.3",
1919
"react": "^18.2.0",
2020
"react-dom": "^18.2.0",
21-
"styled-components": "^5.3.6",
22-
"tailwind": "^4.0.0"
21+
"styled-components": "^5.3.6"
2322
},
2423
"devDependencies": {
2524
"@types/react": "^18.2.14",
2625
"@types/react-dom": "^18.2.6",
26+
"@types/styled-components": "^5.1.26",
2727
"@typescript-eslint/eslint-plugin": "^5.61.0",
2828
"@typescript-eslint/parser": "^5.61.0",
2929
"@vitejs/plugin-react": "^4.0.1",
30+
"autoprefixer": "^10.4.14",
3031
"eslint": "^8.44.0",
3132
"eslint-plugin-react-hooks": "^4.6.0",
3233
"eslint-plugin-react-refresh": "^0.4.1",
34+
"postcss": "^8.4.26",
35+
"tailwindcss": "^3.3.3",
3336
"typescript": "^5.0.2",
3437
"vite": "^4.4.0"
38+
},
39+
"prettier": {
40+
"tabWidth": 4,
41+
"printWidth": 160
3542
}
3643
}

web_app/postcss.config.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export default {
2+
plugins: {
3+
tailwindcss: {},
4+
autoprefixer: {},
5+
},
6+
};
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import React from "react";
2+
3+
export interface ButtonBaseProps {
4+
onClick: (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
5+
text: string;
6+
disabled?: boolean;
7+
className?: string;
8+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import React from "react";
2+
import { ButtonBaseProps } from "./base";
3+
4+
const DefaultButton: React.FC<ButtonBaseProps> = (props) => {
5+
const { text, onClick, disabled, className } = props;
6+
let btnClassName =
7+
"inline-flex items-center rounded border border-gray-300 bg-white dark:bg-zinc-900 px-2.5 py-1.5 text-xs font-medium text-gray-700 dark:text-gray-200 shadow-sm hover:bg-gray-50 dark:hover:bg-gray-800 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 disabled:opacity-50";
8+
if (className) {
9+
btnClassName = btnClassName + " " + className;
10+
}
11+
return (
12+
<button className={btnClassName} onClick={onClick} disabled={disabled}>
13+
{text}
14+
</button>
15+
);
16+
};
17+
18+
export default DefaultButton;
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import React from "react";
2+
import { ButtonBaseProps } from "./base";
3+
4+
const PrimaryButton: React.FC<ButtonBaseProps> = (props) => {
5+
const { text, onClick, disabled, className } = props;
6+
let btnClassName =
7+
"inline-flex items-center rounded border border-transparent bg-indigo-600 px-2.5 py-1.5 text-xs font-medium text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50";
8+
if (className) {
9+
btnClassName = btnClassName + " " + className;
10+
}
11+
return (
12+
<button className={btnClassName} onClick={onClick} disabled={disabled}>
13+
{text}
14+
</button>
15+
);
16+
};
17+
18+
export default PrimaryButton;
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import React, { useEffect, useState } from "react";
2+
import Modal from "../modal";
3+
import { observer } from "mobx-react-lite";
4+
import DefaultButton from "../button/default";
5+
import { encodeSpec } from "../../utils/graphicWalkerParser";
6+
7+
import type { IGlobalStore } from "@kanaries/graphic-walker/dist/store";
8+
import type { IVisSpec } from "@kanaries/graphic-walker/dist/interfaces";
9+
10+
interface ICodeExport {
11+
globalStore: React.MutableRefObject<IGlobalStore | null>;
12+
open: boolean;
13+
setOpen: (open: boolean) => void;
14+
}
15+
16+
const CodeExport: React.FC<ICodeExport> = observer((props) => {
17+
const [code, setCode] = useState<IVisSpec[]>([]);
18+
19+
useEffect(() => {
20+
if (props.open) {
21+
const res = props.globalStore.current?.vizStore.exportViewSpec();
22+
if (res) setCode(res);
23+
}
24+
}, [props.open]);
25+
26+
return (
27+
<Modal
28+
show={props.open}
29+
onClose={() => {
30+
props.setOpen(false);
31+
}}
32+
>
33+
<div className="dark:text-white">
34+
<h1 className="mb-4 font-bold text-base">Config Export</h1>
35+
<div className="text-sm max-h-64 overflow-auto w-full">
36+
<code className="font-mono text-xs whitespace-nowrap w-full">
37+
visConfig &lt;- '{JSON.stringify(JSON.parse(encodeSpec(code)))}'
38+
<br />
39+
gwalkr(data="name of your data frame", visConfig=visConfig)
40+
</code>
41+
</div>
42+
<div className="mt-4 flex justify-start">
43+
<DefaultButton
44+
text="Cancel"
45+
className="mr-2 px-6"
46+
onClick={() => {
47+
props.setOpen(false);
48+
}}
49+
/>
50+
</div>
51+
<div className="text-sm max-h-56 mt-4 text-right">Please copy the R code above and paste it into your script.</div>
52+
</div>
53+
</Modal>
54+
);
55+
});
56+
57+
export default CodeExport;

0 commit comments

Comments
 (0)