@kanaries/graphic-walker 0.2.8 → 0.2.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/explainer.worker.90990e9a.js.map +1 -0
- package/dist/fields/filterField/tabs.d.ts +1 -1
- package/dist/graphic-walker.es.js +16912 -16789
- package/dist/graphic-walker.es.js.map +1 -1
- package/dist/graphic-walker.umd.js +137 -113
- package/dist/graphic-walker.umd.js.map +1 -1
- package/dist/insightBoard/std2vegaSpec.d.ts +1 -1
- package/dist/interfaces.d.ts +6 -6
- package/dist/renderer/index.d.ts +2 -1
- package/dist/store/visualSpecStore.d.ts +1 -1
- package/dist/style.css +1 -1
- package/dist/vis/react-vega.d.ts +7 -1
- package/dist/visualSettings/index.d.ts +5 -1
- package/package.json +2 -1
- package/src/App.tsx +10 -7
- package/src/components/modal.tsx +7 -2
- package/src/fields/datasetFields/index.tsx +10 -3
- package/src/fields/fieldsContext.tsx +5 -0
- package/src/locales/en-US.json +11 -2
- package/src/locales/zh-CN.json +11 -2
- package/src/renderer/index.tsx +5 -4
- package/src/vis/react-vega.tsx +55 -4
- package/src/visualSettings/index.tsx +63 -2
- package/dist/assets/explainer.worker.0cf1948d.js.map +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Specification } from 'visual-insights';
|
|
2
2
|
import { IField, IRow } from '../interfaces';
|
|
3
3
|
import { IPredicate } from '../utils';
|
|
4
|
-
export
|
|
4
|
+
export type IReasonType = 'selection_dim_distribution' | 'selection_mea_distribution' | 'children_major_factor' | 'children_outlier';
|
|
5
5
|
export declare const geomTypeMap: {
|
|
6
6
|
[key: string]: any;
|
|
7
7
|
};
|
package/dist/interfaces.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { StatFuncName } from "visual-insights/build/esm/statistics";
|
|
2
2
|
import { AggFC } from 'cube-core/built/types';
|
|
3
3
|
import { IAnalyticType, ISemanticType } from 'visual-insights';
|
|
4
|
-
export
|
|
4
|
+
export type DeepReadonly<T extends Record<keyof any, any>> = {
|
|
5
5
|
readonly [K in keyof T]: T[K] extends Record<keyof any, any> ? DeepReadonly<T[K]> : T[K];
|
|
6
6
|
};
|
|
7
7
|
export interface IRow {
|
|
@@ -10,7 +10,7 @@ export interface IRow {
|
|
|
10
10
|
/**
|
|
11
11
|
* @deprecated
|
|
12
12
|
*/
|
|
13
|
-
export
|
|
13
|
+
export type SemanticType = 'quantitative' | 'nominal' | 'ordinal' | 'temporal';
|
|
14
14
|
export interface Filters {
|
|
15
15
|
[key: string]: any[];
|
|
16
16
|
}
|
|
@@ -102,7 +102,7 @@ export interface IDraggableStateKey {
|
|
|
102
102
|
id: keyof DraggableFieldState;
|
|
103
103
|
mode: number;
|
|
104
104
|
}
|
|
105
|
-
export
|
|
105
|
+
export type IFilterRule = {
|
|
106
106
|
type: 'range';
|
|
107
107
|
value: readonly [number, number];
|
|
108
108
|
} | {
|
|
@@ -114,9 +114,9 @@ export declare type IFilterRule = {
|
|
|
114
114
|
};
|
|
115
115
|
export declare const EXPLORATION_TYPES: readonly ["none", "brush", "point"];
|
|
116
116
|
export declare const BRUSH_DIRECTIONS: readonly ["default", "x", "y"];
|
|
117
|
-
export
|
|
118
|
-
export
|
|
119
|
-
export
|
|
117
|
+
export type IStackMode = 'none' | 'stack' | 'normalize';
|
|
118
|
+
export type IExplorationType = (typeof EXPLORATION_TYPES)[number];
|
|
119
|
+
export type IBrushDirection = (typeof BRUSH_DIRECTIONS)[number];
|
|
120
120
|
export interface IVisualConfig {
|
|
121
121
|
defaultAggregated: boolean;
|
|
122
122
|
geoms: string[];
|
package/dist/renderer/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
|
|
2
|
+
import { IReactVegaHandler } from '../vis/react-vega';
|
|
3
|
+
declare const _default: React.MemoExoticComponent<React.ForwardRefExoticComponent<Pick<React.RefAttributes<IReactVegaHandler>, "key"> & React.RefAttributes<IReactVegaHandler>>>;
|
|
3
4
|
export default _default;
|
|
@@ -2,7 +2,7 @@ import { Specification } from "visual-insights";
|
|
|
2
2
|
import { DataSet, DraggableFieldState, IFilterRule, IViewField, IVisualConfig } from "../interfaces";
|
|
3
3
|
import { VisSpecWithHistory } from "../models/visSpecHistory";
|
|
4
4
|
import { CommonStore } from "./commonStore";
|
|
5
|
-
|
|
5
|
+
type DeepReadonly<T extends Record<keyof any, any>> = {
|
|
6
6
|
readonly [K in keyof T]: T[K] extends Record<keyof any, any> ? DeepReadonly<T[K]> : T[K];
|
|
7
7
|
};
|
|
8
8
|
export declare class VizSpecStore {
|
package/dist/style.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
/*! tailwindcss v2.2.19 | MIT License | https://tailwindcss.com *//*! modern-normalize v1.1.0 | MIT License | https://github.com/sindresorhus/modern-normalize */*,:before,:after{box-sizing:border-box}html{tab-size:4}html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}body{font-family:system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji"}hr{height:0;color:inherit}abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,select{text-transform:none}button,[type=button],[type=submit]{-webkit-appearance:button}::-moz-focus-inner{border-style:none;padding:0}legend{padding:0}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}button{background-color:transparent;background-image:none}fieldset{margin:0;padding:0}ol,ul{list-style:none;margin:0;padding:0}html{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";line-height:1.5}body{font-family:inherit;line-height:inherit}*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:currentColor}hr{border-top-width:1px}img{border-style:solid}textarea{resize:vertical}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}table{border-collapse:collapse}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}button,input,optgroup,select,textarea{padding:0;line-height:inherit;color:inherit}pre,code,kbd,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}*,:before,:after{--tw-border-opacity: 1;border-color:rgba(229,231,235,var(--tw-border-opacity))}.container{width:100%}@media (min-width: 640px){.container{max-width:640px}}@media (min-width: 768px){.container{max-width:768px}}@media (min-width: 1024px){.container{max-width:1024px}}@media (min-width: 1280px){.container{max-width:1280px}}@media (min-width: 1536px){.container{max-width:1536px}}.pointer-events-none{pointer-events:none}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.top-2{top:.5rem}.right-2{right:.5rem}.z-auto{z-index:auto}.col-span-1{grid-column:span 1 / span 1}.col-span-2{grid-column:span 2 / span 2}.col-span-3{grid-column:span 3 / span 3}.col-span-5{grid-column:span 5 / span 5}.col-span-7{grid-column:span 7 / span 7}.float-right{float:right}.m-1{margin:.25rem}.m-2{margin:.5rem}.mx-2{margin-left:.5rem;margin-right:.5rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-4{margin-top:1rem}.mr-0{margin-right:0}.mr-1{margin-right:.25rem}.mr-2{margin-right:.5rem}.mr-0\.5{margin-right:.125rem}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.-mb-px{margin-bottom:-1px}.ml-1{margin-left:.25rem}.ml-2{margin-left:.5rem}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.table{display:table}.grid{display:grid}.hidden{display:none}.h-2{height:.5rem}.h-4{height:1rem}.h-8{height:2rem}.h-9{height:2.25rem}.h-16{height:4rem}.h-48{height:12rem}.w-3{width:.75rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-16{width:4rem}.w-60{width:15rem}.w-full{width:100%}.min-w-96{min-width:96px}.flex-1{flex:1 1 0%}.flex-shrink-0{flex-shrink:0}.flex-shrink{flex-shrink:1}.flex-grow-0{flex-grow:0}.flex-grow{flex-grow:1}.border-collapse{border-collapse:collapse}.transform{--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;transform:translate(var(--tw-translate-x)) translateY(var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes spin{to{transform:rotate(360deg)}}@keyframes ping{75%,to{transform:scale(2);opacity:0}}@keyframes pulse{50%{opacity:.5}}@keyframes bounce{0%,to{transform:translateY(-25%);animation-timing-function:cubic-bezier(.8,0,1,1)}50%{transform:none;animation-timing-function:cubic-bezier(0,0,.2,1)}}.animate-spin{animation:spin 1s linear infinite}.cursor-default{cursor:default}.cursor-pointer{cursor:pointer}.cursor-not-allowed{cursor:not-allowed}.select-none{-webkit-user-select:none;user-select:none}.resize{resize:both}.appearance-none{-webkit-appearance:none;appearance:none}.grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}.grid-cols-12{grid-template-columns:repeat(12,minmax(0,1fr))}.flex-row{flex-direction:row}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-stretch{align-items:stretch}.justify-center{justify-content:center}.self-center{align-self:center}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.overflow-y-hidden{overflow-y:hidden}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-nowrap{white-space:nowrap}.rounded-sm{border-radius:.125rem}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.border-2{border-width:2px}.border{border-width:1px}.border-t-2{border-top-width:2px}.border-t{border-top-width:1px}.border-r{border-right-width:1px}.border-b{border-bottom-width:1px}.border-l-2{border-left-width:2px}.border-l{border-left-width:1px}.border-transparent{border-color:transparent}.border-gray-200{--tw-border-opacity: 1;border-color:rgba(229,231,235,var(--tw-border-opacity))}.border-gray-300{--tw-border-opacity: 1;border-color:rgba(209,213,219,var(--tw-border-opacity))}.border-gray-400{--tw-border-opacity: 1;border-color:rgba(156,163,175,var(--tw-border-opacity))}.border-gray-500{--tw-border-opacity: 1;border-color:rgba(107,114,128,var(--tw-border-opacity))}.border-blue-400{--tw-border-opacity: 1;border-color:rgba(96,165,250,var(--tw-border-opacity))}.border-blue-500{--tw-border-opacity: 1;border-color:rgba(59,130,246,var(--tw-border-opacity))}.hover\:border-gray-300:hover{--tw-border-opacity: 1;border-color:rgba(209,213,219,var(--tw-border-opacity))}.hover\:border-gray-600:hover{--tw-border-opacity: 1;border-color:rgba(75,85,99,var(--tw-border-opacity))}.focus\:border-gray-500:focus{--tw-border-opacity: 1;border-color:rgba(107,114,128,var(--tw-border-opacity))}.focus\:border-blue-500:focus{--tw-border-opacity: 1;border-color:rgba(59,130,246,var(--tw-border-opacity))}.bg-transparent{background-color:transparent}.bg-white{--tw-bg-opacity: 1;background-color:rgba(255,255,255,var(--tw-bg-opacity))}.bg-gray-100{--tw-bg-opacity: 1;background-color:rgba(243,244,246,var(--tw-bg-opacity))}.bg-gray-400{--tw-bg-opacity: 1;background-color:rgba(156,163,175,var(--tw-bg-opacity))}.bg-red-500{--tw-bg-opacity: 1;background-color:rgba(239,68,68,var(--tw-bg-opacity))}.bg-yellow-600{--tw-bg-opacity: 1;background-color:rgba(217,119,6,var(--tw-bg-opacity))}.bg-blue-100{--tw-bg-opacity: 1;background-color:rgba(219,234,254,var(--tw-bg-opacity))}.bg-blue-600{--tw-bg-opacity: 1;background-color:rgba(37,99,235,var(--tw-bg-opacity))}.bg-indigo-50{--tw-bg-opacity: 1;background-color:rgba(238,242,255,var(--tw-bg-opacity))}.hover\:bg-gray-50:hover{--tw-bg-opacity: 1;background-color:rgba(249,250,251,var(--tw-bg-opacity))}.hover\:bg-gray-100:hover{--tw-bg-opacity: 1;background-color:rgba(243,244,246,var(--tw-bg-opacity))}.hover\:bg-gray-200:hover{--tw-bg-opacity: 1;background-color:rgba(229,231,235,var(--tw-bg-opacity))}.hover\:bg-red-100:hover{--tw-bg-opacity: 1;background-color:rgba(254,226,226,var(--tw-bg-opacity))}.hover\:bg-yellow-100:hover{--tw-bg-opacity: 1;background-color:rgba(254,243,199,var(--tw-bg-opacity))}.hover\:bg-yellow-500:hover{--tw-bg-opacity: 1;background-color:rgba(245,158,11,var(--tw-bg-opacity))}.hover\:bg-green-50:hover{--tw-bg-opacity: 1;background-color:rgba(236,253,245,var(--tw-bg-opacity))}.hover\:bg-blue-100:hover{--tw-bg-opacity: 1;background-color:rgba(219,234,254,var(--tw-bg-opacity))}.disabled\:bg-gray-300:disabled{--tw-bg-opacity: 1;background-color:rgba(209,213,219,var(--tw-bg-opacity))}.p-1{padding:.25rem}.p-2{padding:.5rem}.p-4{padding:1rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.pt-0{padding-top:0}.pt-1{padding-top:.25rem}.pt-0\.5{padding-top:.125rem}.pr-2{padding-right:.5rem}.pr-6{padding-right:1.5rem}.pb-0{padding-bottom:0}.pb-1{padding-bottom:.25rem}.pb-2{padding-bottom:.5rem}.pb-0\.5{padding-bottom:.125rem}.pl-2{padding-left:.5rem}.pl-6{padding-left:1.5rem}.text-xs{font-size:.75rem;line-height:1rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.font-semibold{font-weight:600}.font-bold{font-weight:700}.ordinal{--tw-ordinal: var(--tw-empty, );--tw-slashed-zero: var(--tw-empty, );--tw-numeric-figure: var(--tw-empty, );--tw-numeric-spacing: var(--tw-empty, );--tw-numeric-fraction: var(--tw-empty, );font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.ordinal{--tw-ordinal: ordinal}.leading-none{line-height:1}.text-black{--tw-text-opacity: 1;color:rgba(0,0,0,var(--tw-text-opacity))}.text-white{--tw-text-opacity: 1;color:rgba(255,255,255,var(--tw-text-opacity))}.text-gray-300{--tw-text-opacity: 1;color:rgba(209,213,219,var(--tw-text-opacity))}.text-gray-400{--tw-text-opacity: 1;color:rgba(156,163,175,var(--tw-text-opacity))}.text-gray-500{--tw-text-opacity: 1;color:rgba(107,114,128,var(--tw-text-opacity))}.text-gray-600{--tw-text-opacity: 1;color:rgba(75,85,99,var(--tw-text-opacity))}.text-gray-700{--tw-text-opacity: 1;color:rgba(55,65,81,var(--tw-text-opacity))}.text-gray-800{--tw-text-opacity: 1;color:rgba(31,41,55,var(--tw-text-opacity))}.text-gray-900{--tw-text-opacity: 1;color:rgba(17,24,39,var(--tw-text-opacity))}.text-red-600{--tw-text-opacity: 1;color:rgba(220,38,38,var(--tw-text-opacity))}.text-yellow-500{--tw-text-opacity: 1;color:rgba(245,158,11,var(--tw-text-opacity))}.text-green-500{--tw-text-opacity: 1;color:rgba(16,185,129,var(--tw-text-opacity))}.text-blue-500{--tw-text-opacity: 1;color:rgba(59,130,246,var(--tw-text-opacity))}.hover\:text-gray-700:hover{--tw-text-opacity: 1;color:rgba(55,65,81,var(--tw-text-opacity))}.hover\:text-purple-600:hover{--tw-text-opacity: 1;color:rgba(124,58,237,var(--tw-text-opacity))}.underline{text-decoration:underline}*,:before,:after{--tw-shadow: 0 0 #0000}.shadow{--tw-shadow: 0 1px 3px 0 rgba(0, 0, 0, .1), 0 1px 2px 0 rgba(0, 0, 0, .06);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow: 0 10px 15px -3px rgba(0, 0, 0, .1), 0 4px 6px -2px rgba(0, 0, 0, .05);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.outline-none{outline:2px solid transparent;outline-offset:2px}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}*,:before,:after{--tw-ring-inset: var(--tw-empty, );--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgba(59, 130, 246, .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000}.filter{--tw-blur: var(--tw-empty, );--tw-brightness: var(--tw-empty, );--tw-contrast: var(--tw-empty, );--tw-grayscale: var(--tw-empty, );--tw-hue-rotate: var(--tw-empty, );--tw-invert: var(--tw-empty, );--tw-saturate: var(--tw-empty, );--tw-sepia: var(--tw-empty, );--tw-drop-shadow: var(--tw-empty, );filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.blur{--tw-blur: blur(8px)}@media (min-width: 1280px){.xl\:col-span-1{grid-column:span 1 / span 1}.xl\:col-span-4{grid-column:span 4 / span 4}.xl\:grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}code{font-family:source-code-pro,Menlo,Monaco,Consolas,Courier New,monospace}
|
|
1
|
+
/*! tailwindcss v2.2.19 | MIT License | https://tailwindcss.com *//*! modern-normalize v1.1.0 | MIT License | https://github.com/sindresorhus/modern-normalize */*,:before,:after{box-sizing:border-box}html{tab-size:4}html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}body{font-family:system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji"}hr{height:0;color:inherit}abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,select{text-transform:none}button,[type=button],[type=submit]{-webkit-appearance:button}::-moz-focus-inner{border-style:none;padding:0}legend{padding:0}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}button{background-color:transparent;background-image:none}fieldset{margin:0;padding:0}ol,ul{list-style:none;margin:0;padding:0}html{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";line-height:1.5}body{font-family:inherit;line-height:inherit}*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:currentColor}hr{border-top-width:1px}img{border-style:solid}textarea{resize:vertical}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}table{border-collapse:collapse}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}button,input,optgroup,select,textarea{padding:0;line-height:inherit;color:inherit}pre,code,kbd,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}*,:before,:after{--tw-border-opacity: 1;border-color:rgba(229,231,235,var(--tw-border-opacity))}.container{width:100%}@media (min-width: 640px){.container{max-width:640px}}@media (min-width: 768px){.container{max-width:768px}}@media (min-width: 1024px){.container{max-width:1024px}}@media (min-width: 1280px){.container{max-width:1280px}}@media (min-width: 1536px){.container{max-width:1536px}}.pointer-events-none{pointer-events:none}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.top-2{top:.5rem}.right-2{right:.5rem}.z-auto{z-index:auto}.col-span-1{grid-column:span 1 / span 1}.col-span-5{grid-column:span 5 / span 5}.float-right{float:right}.m-1{margin:.25rem}.m-2{margin:.5rem}.mx-2{margin-left:.5rem;margin-right:.5rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-4{margin-top:1rem}.mr-0{margin-right:0}.mr-1{margin-right:.25rem}.mr-2{margin-right:.5rem}.mr-0\.5{margin-right:.125rem}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.-mb-px{margin-bottom:-1px}.ml-1{margin-left:.25rem}.ml-2{margin-left:.5rem}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.table{display:table}.grid{display:grid}.hidden{display:none}.h-2{height:.5rem}.h-3{height:.75rem}.h-4{height:1rem}.h-8{height:2rem}.h-9{height:2.25rem}.h-16{height:4rem}.h-48{height:12rem}.w-3{width:.75rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-16{width:4rem}.w-60{width:15rem}.w-full{width:100%}.min-w-96{min-width:96px}.flex-1{flex:1 1 0%}.flex-shrink-0{flex-shrink:0}.flex-shrink{flex-shrink:1}.flex-grow-0{flex-grow:0}.flex-grow{flex-grow:1}.border-collapse{border-collapse:collapse}.transform{--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;transform:translate(var(--tw-translate-x)) translateY(var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes spin{to{transform:rotate(360deg)}}@keyframes ping{75%,to{transform:scale(2);opacity:0}}@keyframes pulse{50%{opacity:.5}}@keyframes bounce{0%,to{transform:translateY(-25%);animation-timing-function:cubic-bezier(.8,0,1,1)}50%{transform:none;animation-timing-function:cubic-bezier(0,0,.2,1)}}.animate-spin{animation:spin 1s linear infinite}.cursor-default{cursor:default}.cursor-pointer{cursor:pointer}.cursor-not-allowed{cursor:not-allowed}.select-none{-webkit-user-select:none;user-select:none}.resize{resize:both}.appearance-none{-webkit-appearance:none;appearance:none}.grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}.flex-row{flex-direction:row}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-stretch{align-items:stretch}.justify-center{justify-content:center}.self-center{align-self:center}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.overflow-y-hidden{overflow-y:hidden}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-nowrap{white-space:nowrap}.rounded-sm{border-radius:.125rem}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.border-2{border-width:2px}.border{border-width:1px}.border-t-2{border-top-width:2px}.border-t{border-top-width:1px}.border-r{border-right-width:1px}.border-b{border-bottom-width:1px}.border-l-2{border-left-width:2px}.border-l{border-left-width:1px}.border-transparent{border-color:transparent}.border-gray-200{--tw-border-opacity: 1;border-color:rgba(229,231,235,var(--tw-border-opacity))}.border-gray-300{--tw-border-opacity: 1;border-color:rgba(209,213,219,var(--tw-border-opacity))}.border-gray-400{--tw-border-opacity: 1;border-color:rgba(156,163,175,var(--tw-border-opacity))}.border-gray-500{--tw-border-opacity: 1;border-color:rgba(107,114,128,var(--tw-border-opacity))}.border-blue-400{--tw-border-opacity: 1;border-color:rgba(96,165,250,var(--tw-border-opacity))}.border-blue-500{--tw-border-opacity: 1;border-color:rgba(59,130,246,var(--tw-border-opacity))}.hover\:border-gray-300:hover{--tw-border-opacity: 1;border-color:rgba(209,213,219,var(--tw-border-opacity))}.hover\:border-gray-600:hover{--tw-border-opacity: 1;border-color:rgba(75,85,99,var(--tw-border-opacity))}.focus\:border-gray-500:focus{--tw-border-opacity: 1;border-color:rgba(107,114,128,var(--tw-border-opacity))}.focus\:border-blue-500:focus{--tw-border-opacity: 1;border-color:rgba(59,130,246,var(--tw-border-opacity))}.bg-transparent{background-color:transparent}.bg-white{--tw-bg-opacity: 1;background-color:rgba(255,255,255,var(--tw-bg-opacity))}.bg-gray-100{--tw-bg-opacity: 1;background-color:rgba(243,244,246,var(--tw-bg-opacity))}.bg-gray-400{--tw-bg-opacity: 1;background-color:rgba(156,163,175,var(--tw-bg-opacity))}.bg-red-500{--tw-bg-opacity: 1;background-color:rgba(239,68,68,var(--tw-bg-opacity))}.bg-yellow-600{--tw-bg-opacity: 1;background-color:rgba(217,119,6,var(--tw-bg-opacity))}.bg-blue-100{--tw-bg-opacity: 1;background-color:rgba(219,234,254,var(--tw-bg-opacity))}.bg-blue-600{--tw-bg-opacity: 1;background-color:rgba(37,99,235,var(--tw-bg-opacity))}.bg-indigo-50{--tw-bg-opacity: 1;background-color:rgba(238,242,255,var(--tw-bg-opacity))}.hover\:bg-gray-50:hover{--tw-bg-opacity: 1;background-color:rgba(249,250,251,var(--tw-bg-opacity))}.hover\:bg-gray-100:hover{--tw-bg-opacity: 1;background-color:rgba(243,244,246,var(--tw-bg-opacity))}.hover\:bg-gray-200:hover{--tw-bg-opacity: 1;background-color:rgba(229,231,235,var(--tw-bg-opacity))}.hover\:bg-red-100:hover{--tw-bg-opacity: 1;background-color:rgba(254,226,226,var(--tw-bg-opacity))}.hover\:bg-yellow-100:hover{--tw-bg-opacity: 1;background-color:rgba(254,243,199,var(--tw-bg-opacity))}.hover\:bg-yellow-500:hover{--tw-bg-opacity: 1;background-color:rgba(245,158,11,var(--tw-bg-opacity))}.hover\:bg-green-50:hover{--tw-bg-opacity: 1;background-color:rgba(236,253,245,var(--tw-bg-opacity))}.hover\:bg-blue-100:hover{--tw-bg-opacity: 1;background-color:rgba(219,234,254,var(--tw-bg-opacity))}.disabled\:bg-gray-300:disabled{--tw-bg-opacity: 1;background-color:rgba(209,213,219,var(--tw-bg-opacity))}.p-1{padding:.25rem}.p-2{padding:.5rem}.p-4{padding:1rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.pt-0{padding-top:0}.pt-1{padding-top:.25rem}.pt-0\.5{padding-top:.125rem}.pr-2{padding-right:.5rem}.pr-6{padding-right:1.5rem}.pb-0{padding-bottom:0}.pb-1{padding-bottom:.25rem}.pb-2{padding-bottom:.5rem}.pb-0\.5{padding-bottom:.125rem}.pl-2{padding-left:.5rem}.pl-6{padding-left:1.5rem}.text-xs{font-size:.75rem;line-height:1rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.font-semibold{font-weight:600}.font-bold{font-weight:700}.ordinal{--tw-ordinal: var(--tw-empty, );--tw-slashed-zero: var(--tw-empty, );--tw-numeric-figure: var(--tw-empty, );--tw-numeric-spacing: var(--tw-empty, );--tw-numeric-fraction: var(--tw-empty, );font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.ordinal{--tw-ordinal: ordinal}.leading-none{line-height:1}.text-black{--tw-text-opacity: 1;color:rgba(0,0,0,var(--tw-text-opacity))}.text-white{--tw-text-opacity: 1;color:rgba(255,255,255,var(--tw-text-opacity))}.text-gray-300{--tw-text-opacity: 1;color:rgba(209,213,219,var(--tw-text-opacity))}.text-gray-400{--tw-text-opacity: 1;color:rgba(156,163,175,var(--tw-text-opacity))}.text-gray-500{--tw-text-opacity: 1;color:rgba(107,114,128,var(--tw-text-opacity))}.text-gray-600{--tw-text-opacity: 1;color:rgba(75,85,99,var(--tw-text-opacity))}.text-gray-700{--tw-text-opacity: 1;color:rgba(55,65,81,var(--tw-text-opacity))}.text-gray-800{--tw-text-opacity: 1;color:rgba(31,41,55,var(--tw-text-opacity))}.text-gray-900{--tw-text-opacity: 1;color:rgba(17,24,39,var(--tw-text-opacity))}.text-red-600{--tw-text-opacity: 1;color:rgba(220,38,38,var(--tw-text-opacity))}.text-yellow-500{--tw-text-opacity: 1;color:rgba(245,158,11,var(--tw-text-opacity))}.text-green-500{--tw-text-opacity: 1;color:rgba(16,185,129,var(--tw-text-opacity))}.text-blue-500{--tw-text-opacity: 1;color:rgba(59,130,246,var(--tw-text-opacity))}.hover\:text-gray-700:hover{--tw-text-opacity: 1;color:rgba(55,65,81,var(--tw-text-opacity))}.hover\:text-purple-600:hover{--tw-text-opacity: 1;color:rgba(124,58,237,var(--tw-text-opacity))}.underline{text-decoration:underline}*,:before,:after{--tw-shadow: 0 0 #0000}.shadow{--tw-shadow: 0 1px 3px 0 rgba(0, 0, 0, .1), 0 1px 2px 0 rgba(0, 0, 0, .06);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow: 0 10px 15px -3px rgba(0, 0, 0, .1), 0 4px 6px -2px rgba(0, 0, 0, .05);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.outline-none{outline:2px solid transparent;outline-offset:2px}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}*,:before,:after{--tw-ring-inset: var(--tw-empty, );--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgba(59, 130, 246, .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000}.filter{--tw-blur: var(--tw-empty, );--tw-brightness: var(--tw-empty, );--tw-contrast: var(--tw-empty, );--tw-grayscale: var(--tw-empty, );--tw-hue-rotate: var(--tw-empty, );--tw-invert: var(--tw-empty, );--tw-saturate: var(--tw-empty, );--tw-sepia: var(--tw-empty, );--tw-drop-shadow: var(--tw-empty, );filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.blur{--tw-blur: blur(8px)}@media (min-width: 768px){.md\:col-span-2{grid-column:span 2 / span 2}.md\:col-span-3{grid-column:span 3 / span 3}.md\:col-span-7{grid-column:span 7 / span 7}.md\:grid{display:grid}.md\:grid-cols-12{grid-template-columns:repeat(12,minmax(0,1fr))}.md\:flex-col{flex-direction:column}}@media (min-width: 1280px){.xl\:col-span-1{grid-column:span 1 / span 1}.xl\:col-span-4{grid-column:span 4 / span 4}.xl\:grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}code{font-family:source-code-pro,Menlo,Monaco,Consolas,Courier New,monospace}
|
package/dist/vis/react-vega.d.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { IViewField, IRow, IStackMode } from '../interfaces';
|
|
3
|
+
export interface IReactVegaHandler {
|
|
4
|
+
getSVGData: () => Promise<string[]>;
|
|
5
|
+
getCanvasData: () => Promise<string[]>;
|
|
6
|
+
downloadSVG: (filename?: string) => Promise<string[]>;
|
|
7
|
+
downloadPNG: (filename?: string) => Promise<string[]>;
|
|
8
|
+
}
|
|
3
9
|
interface ReactVegaProps {
|
|
4
10
|
rows: Readonly<IViewField[]>;
|
|
5
11
|
columns: Readonly<IViewField[]>;
|
|
@@ -43,5 +49,5 @@ interface SingleViewProps {
|
|
|
43
49
|
selectEncoding: 'default' | 'none';
|
|
44
50
|
brushEncoding: 'x' | 'y' | 'default' | 'none';
|
|
45
51
|
}
|
|
46
|
-
declare const ReactVega: React.
|
|
52
|
+
declare const ReactVega: React.ForwardRefExoticComponent<ReactVegaProps & React.RefAttributes<IReactVegaHandler>>;
|
|
47
53
|
export default ReactVega;
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import { IReactVegaHandler } from '../vis/react-vega';
|
|
2
3
|
export declare const LiteContainer: import("styled-components").StyledComponent<"div", any, {}, never>;
|
|
3
|
-
|
|
4
|
+
interface IVisualSettings {
|
|
5
|
+
rendererHandler?: React.RefObject<IReactVegaHandler>;
|
|
6
|
+
}
|
|
7
|
+
declare const _default: React.FunctionComponent<IVisualSettings>;
|
|
4
8
|
export default _default;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kanaries/graphic-walker",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.9",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"dev:front_end": "vite --host",
|
|
6
6
|
"dev": "npm run dev:front_end",
|
|
@@ -61,6 +61,7 @@
|
|
|
61
61
|
"@types/styled-components": "^5.1.26",
|
|
62
62
|
"@types/uuid": "^8.3.1",
|
|
63
63
|
"@vitejs/plugin-react-refresh": "^1.3.6",
|
|
64
|
+
"styled-components": "^5.3.6",
|
|
64
65
|
"typescript": "^4.3.2",
|
|
65
66
|
"vite": "^3.1.0"
|
|
66
67
|
},
|
package/src/App.tsx
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import React, { useState, useEffect } from 'react';
|
|
1
|
+
import React, { useState, useEffect, useRef } from 'react';
|
|
2
2
|
import { Specification } from 'visual-insights';
|
|
3
3
|
import { observer } from 'mobx-react-lite';
|
|
4
4
|
import { LightBulbIcon } from '@heroicons/react/24/outline'
|
|
5
5
|
import { toJS } from 'mobx';
|
|
6
6
|
import { useTranslation } from 'react-i18next';
|
|
7
7
|
import { IMutField, IRow } from './interfaces';
|
|
8
|
+
import type { IReactVegaHandler } from './vis/react-vega';
|
|
8
9
|
import VisualSettings from './visualSettings';
|
|
9
10
|
import { Container, NestContainer } from './components/container';
|
|
10
11
|
import ClickMenu from './components/clickMenu';
|
|
@@ -88,6 +89,8 @@ const App: React.FC<EditorProps> = props => {
|
|
|
88
89
|
}
|
|
89
90
|
}, [currentDataset, spec]);
|
|
90
91
|
|
|
92
|
+
const rendererRef = useRef<IReactVegaHandler>(null);
|
|
93
|
+
|
|
91
94
|
return (
|
|
92
95
|
<div className="App">
|
|
93
96
|
{ !hideDataSourceConfig && <DataSourceSegment preWorkDone={insightReady} />}
|
|
@@ -97,23 +100,23 @@ const App: React.FC<EditorProps> = props => {
|
|
|
97
100
|
</div>
|
|
98
101
|
<Container style={{ marginTop: '0em', borderTop: 'none' }}>
|
|
99
102
|
<Menubar />
|
|
100
|
-
<VisualSettings />
|
|
101
|
-
<div className="grid grid-cols-12 xl:grid-cols-6">
|
|
102
|
-
<div className="col-span-3 xl:col-span-1">
|
|
103
|
+
<VisualSettings rendererHandler={rendererRef} />
|
|
104
|
+
<div className="md:grid md:grid-cols-12 xl:grid-cols-6">
|
|
105
|
+
<div className="md:col-span-3 xl:col-span-1">
|
|
103
106
|
<DatasetFields />
|
|
104
107
|
</div>
|
|
105
|
-
<div className="col-span-2 xl:col-span-1">
|
|
108
|
+
<div className="md:col-span-2 xl:col-span-1">
|
|
106
109
|
<FilterField />
|
|
107
110
|
<AestheticFields />
|
|
108
111
|
</div>
|
|
109
|
-
<div className="col-span-7 xl:col-span-4">
|
|
112
|
+
<div className="md:col-span-7 xl:col-span-4">
|
|
110
113
|
<div>
|
|
111
114
|
<PosFields />
|
|
112
115
|
</div>
|
|
113
116
|
<NestContainer style={{ minHeight: '600px', overflow: 'auto' }} onMouseLeave={() => {
|
|
114
117
|
vizEmbededMenu.show && commonStore.closeEmbededMenu();
|
|
115
118
|
}}>
|
|
116
|
-
{datasets.length > 0 && <ReactiveRenderer />}
|
|
119
|
+
{datasets.length > 0 && <ReactiveRenderer ref={rendererRef} />}
|
|
117
120
|
<InsightBoard />
|
|
118
121
|
{vizEmbededMenu.show && (
|
|
119
122
|
<ClickMenu x={vizEmbededMenu.position[0]} y={vizEmbededMenu.position[1]}>
|
package/src/components/modal.tsx
CHANGED
|
@@ -2,7 +2,6 @@ import React, { useRef } from 'react';
|
|
|
2
2
|
import styled from 'styled-components';
|
|
3
3
|
import { XCircleIcon } from '@heroicons/react/24/outline';
|
|
4
4
|
|
|
5
|
-
|
|
6
5
|
const Background = styled.div({
|
|
7
6
|
position: 'fixed',
|
|
8
7
|
left: 0,
|
|
@@ -14,7 +13,13 @@ const Background = styled.div({
|
|
|
14
13
|
});
|
|
15
14
|
|
|
16
15
|
const Container = styled.div`
|
|
17
|
-
width:
|
|
16
|
+
width: 98%;
|
|
17
|
+
@media (min-width: 600px) {
|
|
18
|
+
width: 80%;
|
|
19
|
+
}
|
|
20
|
+
@media (min-width: 1100px) {
|
|
21
|
+
width: 880px;
|
|
22
|
+
}
|
|
18
23
|
max-height: 800px;
|
|
19
24
|
overflow: auto;
|
|
20
25
|
> div.header {
|
|
@@ -1,17 +1,24 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { Droppable } from "react-beautiful-dnd";
|
|
3
3
|
import { useTranslation } from "react-i18next";
|
|
4
|
+
import styled from 'styled-components';
|
|
4
5
|
import { NestContainer } from "../../components/container";
|
|
5
6
|
import DimFields from "./dimFields";
|
|
6
7
|
import MeaFields from "./meaFields";
|
|
7
8
|
|
|
9
|
+
const DSContainer = styled(NestContainer)`
|
|
10
|
+
@media (min-width: 768px) {
|
|
11
|
+
height: 680px;
|
|
12
|
+
}
|
|
13
|
+
`
|
|
14
|
+
|
|
8
15
|
const DatasetFields: React.FC = (props) => {
|
|
9
16
|
const { t } = useTranslation("translation", { keyPrefix: "main.tabpanel.DatasetFields" });
|
|
10
17
|
|
|
11
18
|
return (
|
|
12
|
-
<
|
|
19
|
+
<DSContainer className="flex md:flex-col" style={{ paddingBlock: 0 }}>
|
|
13
20
|
<h4 className="text-xs mb-2 flex-grow-0 cursor-default select-none mt-2">{t("field_list")}</h4>
|
|
14
|
-
<div className="pd-1 overflow-y-auto" style={{ maxHeight: "380px" }}>
|
|
21
|
+
<div className="pd-1 overflow-y-auto" style={{ maxHeight: "380px", minHeight: '100px' }}>
|
|
15
22
|
<Droppable droppableId="dimensions" direction="vertical">
|
|
16
23
|
{(provided, snapshot) => <DimFields provided={provided} />}
|
|
17
24
|
</Droppable>
|
|
@@ -21,7 +28,7 @@ const DatasetFields: React.FC = (props) => {
|
|
|
21
28
|
{(provided, snapshot) => <MeaFields provided={provided} />}
|
|
22
29
|
</Droppable>
|
|
23
30
|
</div>
|
|
24
|
-
</
|
|
31
|
+
</DSContainer>
|
|
25
32
|
);
|
|
26
33
|
};
|
|
27
34
|
|
package/src/locales/en-US.json
CHANGED
|
@@ -57,7 +57,14 @@
|
|
|
57
57
|
"aggregator": {
|
|
58
58
|
"sum": "Sum",
|
|
59
59
|
"mean": "Mean",
|
|
60
|
-
"count": "Count"
|
|
60
|
+
"count": "Count",
|
|
61
|
+
"median": "Median",
|
|
62
|
+
"min": "Min",
|
|
63
|
+
"max": "Max",
|
|
64
|
+
"q1": "Q1",
|
|
65
|
+
"q3": "Q3",
|
|
66
|
+
"stdev": "Standard Deviation",
|
|
67
|
+
"variance": "Variance"
|
|
61
68
|
},
|
|
62
69
|
"filter_type": {
|
|
63
70
|
"one_of": "One-Of",
|
|
@@ -116,7 +123,9 @@
|
|
|
116
123
|
"button": {
|
|
117
124
|
"ascending": "Sort in Ascending Order",
|
|
118
125
|
"descending": "Sort in Descending Order",
|
|
119
|
-
"transpose": "Transpose"
|
|
126
|
+
"transpose": "Transpose",
|
|
127
|
+
"export_chart": "Export",
|
|
128
|
+
"export_chart_as": "Export as {{type}}"
|
|
120
129
|
},
|
|
121
130
|
"size": "Resize",
|
|
122
131
|
"size_setting": {
|
package/src/locales/zh-CN.json
CHANGED
|
@@ -57,7 +57,14 @@
|
|
|
57
57
|
"aggregator": {
|
|
58
58
|
"sum": "求和",
|
|
59
59
|
"mean": "平均值",
|
|
60
|
-
"count": "计数"
|
|
60
|
+
"count": "计数",
|
|
61
|
+
"median": "中位数",
|
|
62
|
+
"min": "最小值",
|
|
63
|
+
"max": "最大值",
|
|
64
|
+
"q1": "Q1",
|
|
65
|
+
"q3": "Q3",
|
|
66
|
+
"stdev": "标准差",
|
|
67
|
+
"variance": "方差"
|
|
61
68
|
},
|
|
62
69
|
"filter_type": {
|
|
63
70
|
"one_of": "按值筛选",
|
|
@@ -116,7 +123,9 @@
|
|
|
116
123
|
"button": {
|
|
117
124
|
"ascending": "升序排序",
|
|
118
125
|
"descending": "降序排序",
|
|
119
|
-
"transpose": "转置"
|
|
126
|
+
"transpose": "转置",
|
|
127
|
+
"export_chart": "导出",
|
|
128
|
+
"export_chart_as": "导出 {{type}}"
|
|
120
129
|
},
|
|
121
130
|
"size": "调整尺寸",
|
|
122
131
|
"size_setting": {
|
package/src/renderer/index.tsx
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { runInAction, toJS } from 'mobx';
|
|
2
2
|
import { observer } from 'mobx-react-lite';
|
|
3
3
|
import { Resizable } from 're-resizable';
|
|
4
|
-
import React, { useState, useCallback, useEffect, useRef } from 'react';
|
|
4
|
+
import React, { useState, useCallback, useEffect, useRef, forwardRef } from 'react';
|
|
5
5
|
import { applyFilter } from '../services';
|
|
6
6
|
import { useGlobalStore } from '../store';
|
|
7
|
-
import ReactVega from '../vis/react-vega';
|
|
7
|
+
import ReactVega, { IReactVegaHandler } from '../vis/react-vega';
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
const ReactiveRenderer
|
|
10
|
+
const ReactiveRenderer = forwardRef<IReactVegaHandler, {}>(function ReactiveRenderer (props, ref) {
|
|
11
11
|
const { vizStore, commonStore } = useGlobalStore();
|
|
12
12
|
const { draggableFieldState, visualConfig } = vizStore;
|
|
13
13
|
const { geoms, interactiveScale, defaultAggregated, stack, showActions, size, exploration } = visualConfig;
|
|
@@ -100,11 +100,12 @@ const ReactiveRenderer: React.FC = props => {
|
|
|
100
100
|
showActions={showActions}
|
|
101
101
|
width={size.width - 12 * 4}
|
|
102
102
|
height={size.height - 12 * 4}
|
|
103
|
+
ref={ref}
|
|
103
104
|
brushEncoding={exploration.mode === 'brush' ? exploration.brushDirection : 'none'}
|
|
104
105
|
selectEncoding={exploration.mode === 'point' ? 'default' : 'none'}
|
|
105
106
|
onGeomClick={handleGeomClick}
|
|
106
107
|
/>
|
|
107
108
|
</Resizable>
|
|
108
|
-
}
|
|
109
|
+
});
|
|
109
110
|
|
|
110
111
|
export default observer(ReactiveRenderer);
|
package/src/vis/react-vega.tsx
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import React, { useEffect, useState, useMemo, useRef } from 'react';
|
|
1
|
+
import React, { useEffect, useState, useMemo, forwardRef, useImperativeHandle, useRef } from 'react';
|
|
2
2
|
import embed from 'vega-embed';
|
|
3
3
|
import { Subject, Subscription } from 'rxjs'
|
|
4
4
|
import * as op from 'rxjs/operators';
|
|
5
|
-
import { ScenegraphEvent } from 'vega';
|
|
5
|
+
import type { ScenegraphEvent, View } from 'vega';
|
|
6
6
|
import { ISemanticType } from 'visual-insights';
|
|
7
7
|
import styled from 'styled-components';
|
|
8
8
|
import { autoMark } from '../utils/autoMark';
|
|
@@ -17,6 +17,12 @@ const CanvaContainer = styled.div<{rowSize: number; colSize: number;}>`
|
|
|
17
17
|
`
|
|
18
18
|
|
|
19
19
|
const SELECTION_NAME = 'geom';
|
|
20
|
+
export interface IReactVegaHandler {
|
|
21
|
+
getSVGData: () => Promise<string[]>;
|
|
22
|
+
getCanvasData: () => Promise<string[]>;
|
|
23
|
+
downloadSVG: (filename?: string) => Promise<string[]>;
|
|
24
|
+
downloadPNG: (filename?: string) => Promise<string[]>;
|
|
25
|
+
}
|
|
20
26
|
interface ReactVegaProps {
|
|
21
27
|
rows: Readonly<IViewField[]>;
|
|
22
28
|
columns: Readonly<IViewField[]>;
|
|
@@ -351,7 +357,7 @@ function getSingleView(props: SingleViewProps) {
|
|
|
351
357
|
} : encoding,
|
|
352
358
|
};
|
|
353
359
|
}
|
|
354
|
-
const ReactVega
|
|
360
|
+
const ReactVega = forwardRef<IReactVegaHandler, ReactVegaProps>(function ReactVega (props, ref) {
|
|
355
361
|
const {
|
|
356
362
|
dataSource = [],
|
|
357
363
|
rows = [],
|
|
@@ -408,7 +414,10 @@ const ReactVega: React.FC<ReactVegaProps> = props => {
|
|
|
408
414
|
})
|
|
409
415
|
}, [rowRepeatFields, colRepeatFields])
|
|
410
416
|
|
|
417
|
+
const vegaRefs = useRef<View[]>([]);
|
|
418
|
+
|
|
411
419
|
useEffect(() => {
|
|
420
|
+
vegaRefs.current = [];
|
|
412
421
|
|
|
413
422
|
const yField = rows.length > 0 ? rows[rows.length - 1] : NULL_FIELD;
|
|
414
423
|
const xField = columns.length > 0 ? columns[columns.length - 1] : NULL_FIELD;
|
|
@@ -495,6 +504,7 @@ const ReactVega: React.FC<ReactVegaProps> = props => {
|
|
|
495
504
|
// console.log(JSON.stringify(spec, undefined, 2));
|
|
496
505
|
if (viewPlaceholders.length > 0 && viewPlaceholders[0].current) {
|
|
497
506
|
embed(viewPlaceholders[0].current, spec, { mode: 'vega-lite', actions: showActions }).then(res => {
|
|
507
|
+
vegaRefs.current = [res.view];
|
|
498
508
|
try {
|
|
499
509
|
res.view.addEventListener('click', (e) => {
|
|
500
510
|
click$.next(e);
|
|
@@ -576,6 +586,7 @@ const ReactVega: React.FC<ReactVegaProps> = props => {
|
|
|
576
586
|
// console.log(JSON.stringify(ans, undefined, 2));
|
|
577
587
|
if (node) {
|
|
578
588
|
embed(node, ans, { mode: 'vega-lite', actions: showActions }).then(res => {
|
|
589
|
+
vegaRefs.current.push(res.view);
|
|
579
590
|
// 这种 case 下,我们来考虑联动的 params
|
|
580
591
|
// vega 使用 Data 来维护 params 的状态,只需要打通这些状态就可以实现联动
|
|
581
592
|
const paramStores = (res.vgSpec.data?.map(d => d.name) ?? []).filter(
|
|
@@ -670,12 +681,52 @@ const ReactVega: React.FC<ReactVegaProps> = props => {
|
|
|
670
681
|
crossFilterTriggerIdx,
|
|
671
682
|
]);
|
|
672
683
|
|
|
684
|
+
useImperativeHandle(ref, () => ({
|
|
685
|
+
getSVGData() {
|
|
686
|
+
return Promise.all(vegaRefs.current.map(view => view.toSVG()));
|
|
687
|
+
},
|
|
688
|
+
async getCanvasData() {
|
|
689
|
+
const canvases = await Promise.all(vegaRefs.current.map(view => view.toCanvas()));
|
|
690
|
+
return canvases.map(canvas => canvas.toDataURL('image/png'));
|
|
691
|
+
},
|
|
692
|
+
async downloadSVG(filename = `gw chart ${Date.now() % 1_000_000}`.padStart(6, '0')) {
|
|
693
|
+
const data = await Promise.all(vegaRefs.current.map(view => view.toSVG()));
|
|
694
|
+
const files: string[] = [];
|
|
695
|
+
for (let i = 0; i < data.length; i += 1) {
|
|
696
|
+
const d = data[i];
|
|
697
|
+
const file = new File([d], `${filename}${data.length > 1 ? `_${i + 1}` : ''}.svg`);
|
|
698
|
+
const url = URL.createObjectURL(file);
|
|
699
|
+
const a = document.createElement('a');
|
|
700
|
+
a.download = file.name;
|
|
701
|
+
a.href = url;
|
|
702
|
+
a.click();
|
|
703
|
+
requestAnimationFrame(() => {
|
|
704
|
+
URL.revokeObjectURL(url);
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
return files;
|
|
708
|
+
},
|
|
709
|
+
async downloadPNG(filename = `gw chart ${Date.now() % 1_000_000}`.padStart(6, '0')) {
|
|
710
|
+
const canvases = await Promise.all(vegaRefs.current.map(view => view.toCanvas(2)));
|
|
711
|
+
const data = canvases.map(canvas => canvas.toDataURL('image/png', 1));
|
|
712
|
+
const files: string[] = [];
|
|
713
|
+
for (let i = 0; i < data.length; i += 1) {
|
|
714
|
+
const d = data[i];
|
|
715
|
+
const a = document.createElement('a');
|
|
716
|
+
a.download = `${filename}${data.length > 1 ? `_${i + 1}` : ''}.png`;
|
|
717
|
+
a.href = d.replace(/^data:image\/[^;]/, 'data:application/octet-stream');
|
|
718
|
+
a.click();
|
|
719
|
+
}
|
|
720
|
+
return files;
|
|
721
|
+
},
|
|
722
|
+
}));
|
|
723
|
+
|
|
673
724
|
return <CanvaContainer rowSize={Math.max(rowRepeatFields.length, 1)} colSize={Math.max(colRepeatFields.length, 1)}>
|
|
674
725
|
{/* <div ref={container}></div> */}
|
|
675
726
|
{
|
|
676
727
|
viewPlaceholders.map((view, i) => <div key={i} ref={view}></div>)
|
|
677
728
|
}
|
|
678
729
|
</CanvaContainer>
|
|
679
|
-
}
|
|
730
|
+
});
|
|
680
731
|
|
|
681
732
|
export default ReactVega;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BarsArrowDownIcon, BarsArrowUpIcon } from '@heroicons/react/24/outline';
|
|
1
|
+
import { BarsArrowDownIcon, BarsArrowUpIcon, ChevronDownIcon, PhotoIcon } from '@heroicons/react/24/outline';
|
|
2
2
|
import { observer } from 'mobx-react-lite';
|
|
3
3
|
import React from 'react';
|
|
4
4
|
import styled from 'styled-components'
|
|
@@ -9,6 +9,7 @@ import SizeSetting from '../components/sizeSetting';
|
|
|
9
9
|
import { GEMO_TYPES, STACK_MODE, CHART_LAYOUT_TYPE } from '../config';
|
|
10
10
|
import { useGlobalStore } from '../store';
|
|
11
11
|
import { IStackMode, EXPLORATION_TYPES, IBrushDirection, BRUSH_DIRECTIONS } from '../interfaces';
|
|
12
|
+
import { IReactVegaHandler } from '../vis/react-vega';
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
export const LiteContainer = styled.div`
|
|
@@ -16,9 +17,27 @@ export const LiteContainer = styled.div`
|
|
|
16
17
|
border: 1px solid #d9d9d9;
|
|
17
18
|
padding: 1em;
|
|
18
19
|
background-color: #fff;
|
|
20
|
+
.menu-root {
|
|
21
|
+
position: relative;
|
|
22
|
+
& > *:not(.trigger) {
|
|
23
|
+
display: flex;
|
|
24
|
+
flex-direction: column;
|
|
25
|
+
position: absolute;
|
|
26
|
+
right: 0;
|
|
27
|
+
top: 100%;
|
|
28
|
+
border: 1px solid #8884;
|
|
29
|
+
}
|
|
30
|
+
&:not(:hover) > *:not(.trigger):not(:hover) {
|
|
31
|
+
display: none;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
19
34
|
`;
|
|
20
35
|
|
|
21
|
-
|
|
36
|
+
interface IVisualSettings {
|
|
37
|
+
rendererHandler?: React.RefObject<IReactVegaHandler>;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const VisualSettings: React.FC<IVisualSettings> = ({ rendererHandler }) => {
|
|
22
41
|
const { vizStore } = useGlobalStore();
|
|
23
42
|
const { visualConfig, sortCondition } = vizStore;
|
|
24
43
|
const { t: tGlobal } = useTranslation();
|
|
@@ -307,6 +326,48 @@ const VisualSettings: React.FC = () => {
|
|
|
307
326
|
{t('toggle.debug')}
|
|
308
327
|
</label>
|
|
309
328
|
</div>
|
|
329
|
+
<div className='item'>
|
|
330
|
+
<label
|
|
331
|
+
className="text-xs text-color-gray-700 mr-2"
|
|
332
|
+
htmlFor="button:transpose"
|
|
333
|
+
id="button:transpose:label"
|
|
334
|
+
>
|
|
335
|
+
{t('button.export_chart')}
|
|
336
|
+
</label>
|
|
337
|
+
<PhotoIcon
|
|
338
|
+
className="w-4 inline-block cursor-pointer"
|
|
339
|
+
role="button"
|
|
340
|
+
tabIndex={0}
|
|
341
|
+
id="button:export_chart"
|
|
342
|
+
aria-describedby="button:export_chart:label"
|
|
343
|
+
xlinkTitle={t('button.export_chart')}
|
|
344
|
+
aria-label={t('button.export_chart')}
|
|
345
|
+
onClick={() => rendererHandler?.current?.downloadPNG()}
|
|
346
|
+
/>
|
|
347
|
+
<div className="menu-root flex flex-col items-center justify-center">
|
|
348
|
+
<ChevronDownIcon
|
|
349
|
+
className="w-4 h-3 inline-block mr-1 cursor-pointer trigger"
|
|
350
|
+
role="button"
|
|
351
|
+
tabIndex={0}
|
|
352
|
+
/>
|
|
353
|
+
<div>
|
|
354
|
+
<button
|
|
355
|
+
className="text-xs min-w-96 w-full pt-1 pb-1 pl-6 pr-6 bg-white hover:bg-gray-200"
|
|
356
|
+
aria-label={t('button.export_chart_as', { type: 'png' })}
|
|
357
|
+
onClick={() => rendererHandler?.current?.downloadPNG()}
|
|
358
|
+
>
|
|
359
|
+
{t('button.export_chart_as', { type: 'png' })}
|
|
360
|
+
</button>
|
|
361
|
+
<button
|
|
362
|
+
className="text-xs min-w-96 w-full pt-1 pb-1 pl-6 pr-6 bg-white hover:bg-gray-200"
|
|
363
|
+
aria-label={t('button.export_chart_as', { type: 'svg' })}
|
|
364
|
+
onClick={() => rendererHandler?.current?.downloadSVG()}
|
|
365
|
+
>
|
|
366
|
+
{t('button.export_chart_as', { type: 'svg' })}
|
|
367
|
+
</button>
|
|
368
|
+
</div>
|
|
369
|
+
</div>
|
|
370
|
+
</div>
|
|
310
371
|
</LiteForm>
|
|
311
372
|
</LiteContainer>
|
|
312
373
|
}
|