@kanaries/graphic-walker 0.2.7 → 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.
@@ -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 declare type IReasonType = 'selection_dim_distribution' | 'selection_mea_distribution' | 'children_major_factor' | 'children_outlier';
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
  };
@@ -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 declare type DeepReadonly<T extends Record<keyof any, any>> = {
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 declare type SemanticType = 'quantitative' | 'nominal' | 'ordinal' | 'temporal';
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 declare type IFilterRule = {
105
+ export type IFilterRule = {
106
106
  type: 'range';
107
107
  value: readonly [number, number];
108
108
  } | {
@@ -112,11 +112,15 @@ export declare type IFilterRule = {
112
112
  type: 'one of';
113
113
  value: Set<string | number>;
114
114
  };
115
- export declare type IStackMode = 'none' | 'stack' | 'normalize';
115
+ export declare const EXPLORATION_TYPES: readonly ["none", "brush", "point"];
116
+ export declare const BRUSH_DIRECTIONS: readonly ["default", "x", "y"];
117
+ export type IStackMode = 'none' | 'stack' | 'normalize';
118
+ export type IExplorationType = (typeof EXPLORATION_TYPES)[number];
119
+ export type IBrushDirection = (typeof BRUSH_DIRECTIONS)[number];
116
120
  export interface IVisualConfig {
117
121
  defaultAggregated: boolean;
118
122
  geoms: string[];
119
- stack: 'none' | 'stack' | 'normalize';
123
+ stack: IStackMode;
120
124
  showActions: boolean;
121
125
  interactiveScale: boolean;
122
126
  sorted: 'none' | 'ascending' | 'descending';
@@ -125,6 +129,11 @@ export interface IVisualConfig {
125
129
  width: number;
126
130
  height: number;
127
131
  };
132
+ exploration: {
133
+ mode: IExplorationType;
134
+ /** works when mode is 'brush' */
135
+ brushDirection: IBrushDirection;
136
+ };
128
137
  }
129
138
  export interface IVisSpec {
130
139
  readonly visId: string;
@@ -1,3 +1,4 @@
1
1
  import React from 'react';
2
- declare const _default: React.FunctionComponent<{}>;
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
- declare type DeepReadonly<T extends Record<keyof any, any>> = {
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 {
@@ -89,6 +89,7 @@ export declare class VizSpecStore {
89
89
  width?: number;
90
90
  height?: number;
91
91
  }): void;
92
+ setExploration(value: Partial<IVisualConfig['exploration']>): void;
92
93
  reorderField(stateKey: keyof DraggableFieldState, sourceIndex: number, destinationIndex: number): void;
93
94
  moveField(sourceKey: keyof DraggableFieldState, sourceIndex: number, destinationKey: keyof DraggableFieldState, destinationIndex: number): void;
94
95
  removeField(sourceKey: keyof DraggableFieldState, sourceIndex: number): void;
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}
@@ -4,7 +4,9 @@ export declare function dumpsGWPureSpec(list: VisSpecWithHistory[]): IVisSpec[];
4
4
  export declare function parseGWPureSpec(list: IVisSpec[]): VisSpecWithHistory[];
5
5
  interface IStoInfo {
6
6
  datasets: IDataSet[];
7
- specList: IVisSpec[];
7
+ specList: ({
8
+ [K in keyof IVisSpec]: K extends 'config' ? Partial<IVisSpec[K]> : IVisSpec[K];
9
+ })[];
8
10
  dataSources: IDataSource[];
9
11
  }
10
12
  export declare function stringifyGWContent(info: IStoInfo): string;
@@ -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[]>;
@@ -19,6 +25,29 @@ interface ReactVegaProps {
19
25
  width: number;
20
26
  height: number;
21
27
  onGeomClick?: (values: any, e: any) => void;
28
+ selectEncoding: SingleViewProps['selectEncoding'];
29
+ brushEncoding: SingleViewProps['brushEncoding'];
30
+ }
31
+ interface SingleViewProps {
32
+ x: IViewField;
33
+ y: IViewField;
34
+ color: IViewField;
35
+ opacity: IViewField;
36
+ size: IViewField;
37
+ shape: IViewField;
38
+ xOffset: IViewField;
39
+ yOffset: IViewField;
40
+ row: IViewField;
41
+ column: IViewField;
42
+ theta: IViewField;
43
+ radius: IViewField;
44
+ defaultAggregated: boolean;
45
+ stack: IStackMode;
46
+ geomType: string;
47
+ enableCrossFilter: boolean;
48
+ asCrossFilterTrigger: boolean;
49
+ selectEncoding: 'default' | 'none';
50
+ brushEncoding: 'x' | 'y' | 'default' | 'none';
22
51
  }
23
- declare const ReactVega: React.FC<ReactVegaProps>;
52
+ declare const ReactVega: React.ForwardRefExoticComponent<ReactVegaProps & React.RefAttributes<IReactVegaHandler>>;
24
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
- declare const _default: React.FunctionComponent<{}>;
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.7",
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]}>
@@ -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: 880px;
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
- <NestContainer className="flex flex-col" style={{ height: "680px", paddingBlock: 0 }}>
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
- </NestContainer>
31
+ </DSContainer>
25
32
  );
26
33
  };
27
34
 
@@ -55,5 +55,10 @@ export const DRAGGABLE_STATE_KEYS: Readonly<IDraggableStateKey[]> = [
55
55
  export const AGGREGATOR_LIST: Readonly<string[]> = [
56
56
  'sum',
57
57
  'mean',
58
+ 'median',
58
59
  'count',
60
+ 'min',
61
+ 'max',
62
+ 'variance',
63
+ 'stdev'
59
64
  ] as const;
package/src/interfaces.ts CHANGED
@@ -129,13 +129,26 @@ export type IFilterRule = {
129
129
  value: Set<string | number>;
130
130
  };
131
131
 
132
+ export const EXPLORATION_TYPES = [
133
+ 'none',
134
+ 'brush',
135
+ 'point',
136
+ ] as const;
137
+
138
+ export const BRUSH_DIRECTIONS = [
139
+ 'default',
140
+ 'x',
141
+ 'y',
142
+ ] as const;
132
143
 
133
144
  export type IStackMode = 'none' | 'stack' | 'normalize';
145
+ export type IExplorationType = (typeof EXPLORATION_TYPES)[number];
146
+ export type IBrushDirection = (typeof BRUSH_DIRECTIONS)[number];
134
147
 
135
148
  export interface IVisualConfig {
136
149
  defaultAggregated: boolean;
137
150
  geoms: string[];
138
- stack: 'none' | 'stack' | 'normalize';
151
+ stack: IStackMode;
139
152
  showActions: boolean;
140
153
  interactiveScale: boolean;
141
154
  sorted: 'none' | 'ascending' | 'descending';
@@ -144,6 +157,11 @@ export interface IVisualConfig {
144
157
  width: number;
145
158
  height: number;
146
159
  }
160
+ exploration: {
161
+ mode: IExplorationType;
162
+ /** works when mode is 'brush' */
163
+ brushDirection: IBrushDirection;
164
+ };
147
165
  }
148
166
 
149
167
  export interface IVisSpec {
@@ -30,6 +30,18 @@
30
30
  "auto": "Auto",
31
31
  "fixed": "Fixed"
32
32
  },
33
+ "exploration_mode": {
34
+ "__enum__": "Exploration Mode",
35
+ "none": "Off",
36
+ "brush": "Brush",
37
+ "point": "Point"
38
+ },
39
+ "brush_mode": {
40
+ "__enum__": "Brush Direction",
41
+ "default": "Both",
42
+ "x": "X-Only",
43
+ "y": "Y-Only"
44
+ },
33
45
  "draggable_key": {
34
46
  "fields": "Fields",
35
47
  "columns": "Columns",
@@ -45,7 +57,14 @@
45
57
  "aggregator": {
46
58
  "sum": "Sum",
47
59
  "mean": "Mean",
48
- "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"
49
68
  },
50
69
  "filter_type": {
51
70
  "one_of": "One-Of",
@@ -104,7 +123,9 @@
104
123
  "button": {
105
124
  "ascending": "Sort in Ascending Order",
106
125
  "descending": "Sort in Descending Order",
107
- "transpose": "Transpose"
126
+ "transpose": "Transpose",
127
+ "export_chart": "Export",
128
+ "export_chart_as": "Export as {{type}}"
108
129
  },
109
130
  "size": "Resize",
110
131
  "size_setting": {
@@ -24,6 +24,18 @@
24
24
  "auto": "自动",
25
25
  "fixed": "固定"
26
26
  },
27
+ "exploration_mode": {
28
+ "__enum__": "探索交互",
29
+ "none": "关",
30
+ "brush": "范围",
31
+ "point": "目标"
32
+ },
33
+ "brush_mode": {
34
+ "__enum__": "框选方向",
35
+ "default": "所有",
36
+ "x": "仅 X 轴",
37
+ "y": "仅 Y 轴"
38
+ },
27
39
  "stack_mode": {
28
40
  "__enum__": "堆叠模式",
29
41
  "none": "关闭",
@@ -45,7 +57,14 @@
45
57
  "aggregator": {
46
58
  "sum": "求和",
47
59
  "mean": "平均值",
48
- "count": "计数"
60
+ "count": "计数",
61
+ "median": "中位数",
62
+ "min": "最小值",
63
+ "max": "最大值",
64
+ "q1": "Q1",
65
+ "q3": "Q3",
66
+ "stdev": "标准差",
67
+ "variance": "方差"
49
68
  },
50
69
  "filter_type": {
51
70
  "one_of": "按值筛选",
@@ -104,7 +123,9 @@
104
123
  "button": {
105
124
  "ascending": "升序排序",
106
125
  "descending": "降序排序",
107
- "transpose": "转置"
126
+ "transpose": "转置",
127
+ "export_chart": "导出",
128
+ "export_chart_as": "导出 {{type}}"
108
129
  },
109
130
  "size": "调整尺寸",
110
131
  "size_setting": {
@@ -1,16 +1,16 @@
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: React.FC = props => {
10
+ const ReactiveRenderer = forwardRef<IReactVegaHandler, {}>(function ReactiveRenderer (props, ref) {
11
11
  const { vizStore, commonStore } = useGlobalStore();
12
12
  const { draggableFieldState, visualConfig } = vizStore;
13
- const { geoms, interactiveScale, defaultAggregated, stack, showActions, size } = visualConfig;
13
+ const { geoms, interactiveScale, defaultAggregated, stack, showActions, size, exploration } = visualConfig;
14
14
  const { currentDataset } = commonStore;
15
15
  const { filters } = draggableFieldState;
16
16
 
@@ -27,13 +27,17 @@ const ReactiveRenderer: React.FC = props => {
27
27
  const colLeftFacetFields = columns.slice(0, -1).filter(f => f.analyticType === 'dimension');
28
28
 
29
29
  const hasFacet = rowLeftFacetFields.length > 0 || colLeftFacetFields.length > 0;
30
+
31
+ const shouldTriggerMenu = exploration.mode === 'none';
30
32
 
31
- const onGeomClick = useCallback((values: any, e: any) => {
32
- runInAction(() => {
33
- commonStore.showEmbededMenu([e.pageX, e.pageY])
34
- commonStore.setFilters(values);
35
- })
36
- }, [])
33
+ const handleGeomClick = useCallback((values: any, e: any) => {
34
+ if (shouldTriggerMenu) {
35
+ runInAction(() => {
36
+ commonStore.showEmbededMenu([e.pageX, e.pageY])
37
+ commonStore.setFilters(values);
38
+ });
39
+ }
40
+ }, [shouldTriggerMenu]);
37
41
 
38
42
  // apply filters
39
43
  const { dataSource } = currentDataset;
@@ -93,12 +97,15 @@ const ReactiveRenderer: React.FC = props => {
93
97
  shape={shape[0]}
94
98
  opacity={opacity[0]}
95
99
  size={sizeChannel[0]}
96
- onGeomClick={onGeomClick}
97
100
  showActions={showActions}
98
101
  width={size.width - 12 * 4}
99
102
  height={size.height - 12 * 4}
103
+ ref={ref}
104
+ brushEncoding={exploration.mode === 'brush' ? exploration.brushDirection : 'none'}
105
+ selectEncoding={exploration.mode === 'point' ? 'default' : 'none'}
106
+ onGeomClick={handleGeomClick}
100
107
  />
101
108
  </Resizable>
102
- }
109
+ });
103
110
 
104
111
  export default observer(ReactiveRenderer);
@@ -2,7 +2,7 @@ import { IReactionDisposer, makeAutoObservable, observable, reaction, toJS } fro
2
2
  import produce from 'immer';
3
3
  import { v4 as uuidv4 } from 'uuid';
4
4
  import { Specification } from "visual-insights";
5
- import { DataSet, DraggableFieldState, IFilterRule, IViewField, IVisualConfig } from "../interfaces";
5
+ import { DataSet, DraggableFieldState, IFilterRule, IViewField, IVisSpec, IVisualConfig } from "../interfaces";
6
6
  import { CHANNEL_LIMIT, GEMO_TYPES, MetaFieldKeys } from "../config";
7
7
  import { makeBinField, makeLogField } from "../utils/normalization";
8
8
  import { VisSpecWithHistory } from "../models/visSpecHistory";
@@ -69,7 +69,11 @@ function initVisualConfig (): IVisualConfig {
69
69
  mode: 'auto',
70
70
  width: 320,
71
71
  height: 200
72
- }
72
+ },
73
+ exploration: {
74
+ mode: 'brush',
75
+ brushDirection: 'default',
76
+ },
73
77
  }
74
78
  }
75
79
 
@@ -77,6 +81,16 @@ type DeepReadonly<T extends Record<keyof any, any>> = {
77
81
  readonly [K in keyof T]: T[K] extends Record<keyof any, any> ? DeepReadonly<T[K]> : T[K];
78
82
  };
79
83
 
84
+ const forwardVisualConfigs = (backwards: ReturnType<typeof parseGWContent>['specList']): IVisSpec[] => {
85
+ return backwards.map(content => ({
86
+ ...content,
87
+ config: {
88
+ ...initVisualConfig(),
89
+ ...content.config,
90
+ },
91
+ }));
92
+ };
93
+
80
94
 
81
95
  export class VizSpecStore {
82
96
  // public fields: IViewField[] = [];
@@ -363,6 +377,16 @@ export class VizSpecStore {
363
377
  config.size.height = height;
364
378
  });
365
379
  }
380
+ public setExploration(value: Partial<IVisualConfig['exploration']>) {
381
+ this.useMutable(({ config }) => {
382
+ if (value.mode) {
383
+ config.exploration.mode = value.mode;
384
+ }
385
+ if (value.brushDirection) {
386
+ config.exploration.brushDirection = value.brushDirection;
387
+ }
388
+ });
389
+ }
366
390
  public reorderField(stateKey: keyof DraggableFieldState, sourceIndex: number, destinationIndex: number) {
367
391
  if (MetaFieldKeys.includes(stateKey)) return;
368
392
  if (sourceIndex === destinationIndex) return;
@@ -580,7 +604,8 @@ export class VizSpecStore {
580
604
  this.commonStore.datasets = content.datasets;
581
605
  this.commonStore.dataSources = content.dataSources;
582
606
  this.commonStore.dsIndex = Math.max(content.datasets.length - 1, 0);
583
- this.visList = parseGWPureSpec(content.specList)
607
+ // 补上初始化新版本特性
608
+ this.visList = parseGWPureSpec(forwardVisualConfigs(content.specList));
584
609
  this.visIndex = 0;
585
610
  }
586
611
  }
package/src/utils/save.ts CHANGED
@@ -12,7 +12,10 @@ export function parseGWPureSpec (list: IVisSpec[]): VisSpecWithHistory[] {
12
12
 
13
13
  interface IStoInfo {
14
14
  datasets: IDataSet[];
15
- specList: IVisSpec[];
15
+ specList: ({
16
+ /** 由于 gw 内部实现暂时未锁版本,这里获取到的信息可能会与当前实例内容有偏差,需要用初始值合入 */
17
+ [K in keyof IVisSpec]: K extends 'config' ? Partial<IVisSpec[K]> : IVisSpec[K];
18
+ })[];
16
19
  dataSources: IDataSource[]
17
20
  }
18
21