Skip to content

Commit

Permalink
feat: limit (#128)
Browse files Browse the repository at this point in the history
Co-authored-by: islxyqwe <[email protected]>
  • Loading branch information
islxyqwe and islxyqwe authored Aug 4, 2023
1 parent 6917cbd commit f46c0d7
Show file tree
Hide file tree
Showing 13 changed files with 221 additions and 34 deletions.
38 changes: 38 additions & 0 deletions packages/graphic-walker/src/components/limitSetting.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React from 'react';
import { useTranslation } from 'react-i18next';

export default function LimitSetting(props: { value: number; setValue: (v: number) => void }) {
const { t } = useTranslation('translation', { keyPrefix: 'main.tabpanel.settings' });

return (
<div className=" mt-2">
<input
className="w-full h-2 bg-blue-100 appearance-none"
type="range"
name="limit"
value={props.value > 0 ? props.value : 0}
min="1"
max="50"
disabled={props.value < 0}
step="1"
onChange={(e) => {
const v = parseInt(e.target.value);
if (!isNaN(v)) {
props.setValue(v);
}
}}
/>
<output className="text-sm ml-1" htmlFor="height">
<input
type="checkbox"
className="h-4 w-4 mr-1 rounded border-gray-300 text-indigo-600 focus:ring-indigo-600"
checked={props.value > 0}
onChange={(e) => {
props.setValue(e.target.checked ? 30 : -1);
}}
></input>
{`${t('limit')}${props.value > 0 ? `: ${props.value}` : ''}`}
</output>
</div>
);
}
10 changes: 10 additions & 0 deletions packages/graphic-walker/src/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { useState, useEffect } from 'react';

export function useDebounceValue<T>(value: T, timeout = 200): T {
const [innerValue, setInnerValue] = useState(value);
useEffect(() => {
const handler = setTimeout(() => setInnerValue(value), timeout);
return () => clearTimeout(handler);
}, [value]);
return innerValue;
}
1 change: 1 addition & 0 deletions packages/graphic-walker/src/locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@
"export_chart_as": "Export as {{type}}",
"export_code": "Export Code"
},
"limit": "Limit",
"size": "Resize",
"size_setting": {
"width": "Width",
Expand Down
1 change: 1 addition & 0 deletions packages/graphic-walker/src/locales/ja-JP.json
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@
"export_chart_as": "{{type}}としてエクスポート",
"export_code": "コードをエクスポート"
},
"limit": "上限",
"size": "サイズ変更",
"size_setting": {
"width": "",
Expand Down
1 change: 1 addition & 0 deletions packages/graphic-walker/src/locales/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@
"export_chart_as": "导出 {{type}}",
"export_code": "导出代码"
},
"limit": "上限",
"size": "调整尺寸",
"size_setting": {
"width": "宽度",
Expand Down
44 changes: 37 additions & 7 deletions packages/graphic-walker/src/renderer/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { useState, useEffect, useMemo, useRef } from 'react';
import { unstable_batchedUpdates } from 'react-dom';
import type { DeepReadonly, IFilterField, IRow, IViewField } from '../interfaces';
import { applyFilter, applyViewQuery, transformDataService } from '../services';
import { applyFilter, applySort, applyViewQuery, transformDataService } from '../services';
import { getMeaAggKey } from '../utils';
import { useAppRootContext } from '../components/appRoot';

import { useDebounceValue } from '../hooks';

interface UseRendererProps {
data: IRow[];
Expand All @@ -13,6 +13,8 @@ interface UseRendererProps {
viewMeasures: IViewField[];
filters: readonly DeepReadonly<IFilterField>[];
defaultAggregated: boolean;
sort: 'none' | 'ascending' | 'descending';
limit: number;
}

interface UseRendererResult {
Expand All @@ -21,10 +23,21 @@ interface UseRendererResult {
}

export const useRenderer = (props: UseRendererProps): UseRendererResult => {
const { data, allFields, viewDimensions, viewMeasures, filters, defaultAggregated } = props;
const {
data,
allFields,
viewDimensions,
viewMeasures,
filters,
defaultAggregated,
sort,
limit: storeLimit,
} = props;
const [computing, setComputing] = useState(false);
const taskIdRef = useRef(0);

const limit = useDebounceValue(storeLimit);

const [viewData, setViewData] = useState<IRow[]>([]);

const appRef = useAppRootContext();
Expand All @@ -50,10 +63,26 @@ export const useRenderer = (props: UseRendererProps): UseRendererResult => {
return applyViewQuery(d, dims.concat(meas), {
op: defaultAggregated ? 'aggregate' : 'raw',
groupBy: dims.map((f) => f.fid),
measures: meas.map((f) => ({ field: f.fid, agg: f.aggName as any, asFieldKey: getMeaAggKey(f.fid, f.aggName!) })),
measures: meas.map((f) => ({
field: f.fid,
agg: f.aggName as any,
asFieldKey: getMeaAggKey(f.fid, f.aggName!),
})),
});
})
.then(data => {
.then((data) => {
if (limit > 0 && sort !== 'none' && viewMeasures.length > 0) {
return applySort(data, viewMeasures, sort);
}
return data;
})
.then((data) => {
if (limit > 0) {
return data.slice(0, limit);
}
return data;
})
.then((data) => {
if (taskId !== taskIdRef.current) {
return;
}
Expand All @@ -62,7 +91,8 @@ export const useRenderer = (props: UseRendererProps): UseRendererResult => {
setComputing(false);
setViewData(data);
});
}).catch((err) => {
})
.catch((err) => {
if (taskId !== taskIdRef.current) {
return;
}
Expand All @@ -76,7 +106,7 @@ export const useRenderer = (props: UseRendererProps): UseRendererResult => {
return () => {
taskIdRef.current++;
};
}, [data, filters, viewDimensions, viewMeasures, defaultAggregated]);
}, [data, filters, viewDimensions, viewMeasures, defaultAggregated, limit]);

return useMemo(() => {
return {
Expand Down
34 changes: 22 additions & 12 deletions packages/graphic-walker/src/renderer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,18 @@ interface RendererProps {
const Renderer = forwardRef<IReactVegaHandler, RendererProps>(function (props, ref) {
const { themeKey, dark } = props;
const { vizStore, commonStore } = useGlobalStore();
const { allFields, viewFilters, viewDimensions, viewMeasures, visualConfig, draggableFieldState, visList, visIndex } = vizStore;
const {
allFields,
viewFilters,
viewDimensions,
viewMeasures,
visualConfig,
draggableFieldState,
visList,
visIndex,
sort,
limit,
} = vizStore;
const chart = visList[visIndex];
const { currentDataset } = commonStore;
const { dataSource } = currentDataset;
Expand All @@ -37,6 +48,8 @@ const Renderer = forwardRef<IReactVegaHandler, RendererProps>(function (props, r
viewMeasures,
filters: viewFilters,
defaultAggregated: visualConfig.defaultAggregated,
sort,
limit: limit,
});

// Dependencies that should not trigger effect individually
Expand Down Expand Up @@ -64,19 +77,16 @@ const Renderer = forwardRef<IReactVegaHandler, RendererProps>(function (props, r
useChartIndexControl({
count: visList.length,
index: visIndex,
onChange: idx => vizStore.selectVisualization(idx),
onChange: (idx) => vizStore.selectVisualization(idx),
});

const handleGeomClick = useCallback(
(values: any, e: any) => {
e.stopPropagation();
runInAction(() => {
commonStore.showEmbededMenu([e.pageX, e.pageY]);
commonStore.setFilters(values);
});
},
[]
);
const handleGeomClick = useCallback((values: any, e: any) => {
e.stopPropagation();
runInAction(() => {
commonStore.showEmbededMenu([e.pageX, e.pageY]);
commonStore.setFilters(values);
});
}, []);

const handleChartResize = useCallback(
(width: number, height: number) => {
Expand Down
18 changes: 7 additions & 11 deletions packages/graphic-walker/src/renderer/pureRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,33 +9,27 @@ import type { IReactVegaHandler } from '../vis/react-vega';
import SpecRenderer from './specRenderer';
import { useRenderer } from './hooks';


interface IPureRendererProps {
name?: string;
themeKey?: IThemeKey;
dark?: IDarkMode;
rawData?: IRow[];
visualState: DraggableFieldState;
visualConfig: IVisualConfig;
sort?: 'none' | 'ascending' | 'descending';
limit?: number;
}

/**
* Render a readonly chart with given visualization schema.
* This is a pure component, which means it will not depend on any global state.
*/
const PureRenderer = forwardRef<IReactVegaHandler, IPureRendererProps>(function PureRenderer (props, ref) {
const {
name,
themeKey,
dark,
rawData,
visualState,
visualConfig,
} = props;
const PureRenderer = forwardRef<IReactVegaHandler, IPureRendererProps>(function PureRenderer(props, ref) {
const { name, themeKey, dark, rawData, visualState, visualConfig, sort, limit } = props;
const defaultAggregated = visualConfig?.defaultAggregated ?? false;

const [viewData, setViewData] = useState<IRow[]>([]);

const { allFields, viewDimensions, viewMeasures, filters } = useMemo(() => {
const viewDimensions: IViewField[] = [];
const viewMeasures: IViewField[] = [];
Expand Down Expand Up @@ -64,6 +58,8 @@ const PureRenderer = forwardRef<IReactVegaHandler, IPureRendererProps>(function
viewMeasures,
filters,
defaultAggregated,
sort: sort ?? 'none',
limit: limit ?? -1,
});

// Dependencies that should not trigger effect individually
Expand Down
28 changes: 25 additions & 3 deletions packages/graphic-walker/src/services.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { toJS } from 'mobx';
import { IRow, IMutField, IField, IFilterField, Specification } from './interfaces';
import { IRow, IMutField, IField, IFilterField, Specification, IViewField } from './interfaces';
/* eslint import/no-webpack-loader-syntax:0 */
// @ts-ignore
// eslint-disable-next-line
Expand All @@ -11,6 +11,8 @@ import { IRow, IMutField, IField, IFilterField, Specification } from './interfac
import FilterWorker from './workers/filter.worker?worker&inline';
import TransformDataWorker from './workers/transform.worker?worker&inline';
import ViewQueryWorker from './workers/viewQuery.worker?worker&inline';
import SortWorker from './workers/sort.worker?worker&inline';

import { IViewQuery } from './lib/viewQuery';

// interface WorkerState {
Expand Down Expand Up @@ -154,7 +156,7 @@ export const transformDataService = async (data: IRow[], columns: IField[]): Pro
} finally {
worker.terminate();
}
}
};

export const applyViewQuery = async (data: IRow[], metas: IField[], query: IViewQuery): Promise<IRow[]> => {
const worker = new ViewQueryWorker();
Expand All @@ -170,4 +172,24 @@ export const applyViewQuery = async (data: IRow[], metas: IField[], query: IView
} finally {
worker.terminate();
}
}
};

export const applySort = async (
data: IRow[],
viewMeasures: IField[],
sort: 'ascending' | 'descending'
): Promise<IRow[]> => {
const worker = new SortWorker();
try {
const res: IRow[] = await workerService(worker, {
data,
viewMeasures: viewMeasures.map((x) => toJS(x)),
sort,
});
return res;
} catch (err) {
throw new Error('Uncaught error in ViewQueryWorker', { cause: err });
} finally {
worker.terminate();
}
};
12 changes: 12 additions & 0 deletions packages/graphic-walker/src/store/visualSpecStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -771,4 +771,16 @@ export class VizSpecStore {
});
return updatedVisList;
}
public limit = -1;

public get sort() {
const { rows, columns } = this.draggableFieldState;
if (rows.length && !rows.find((x) => x.analyticType === 'measure')) {
return rows[rows.length - 1].sort || 'none';
}
if (columns.length && !columns.find((x) => x.analyticType === 'measure')) {
return columns[columns.length - 1].sort || 'none';
}
return 'none';
}
}
24 changes: 23 additions & 1 deletion packages/graphic-walker/src/visualSettings/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
LightBulbIcon,
CodeBracketSquareIcon,
Cog6ToothIcon,
HashtagIcon,
} from '@heroicons/react/24/outline';
import { observer } from 'mobx-react-lite';
import React, { SVGProps, useCallback, useMemo } from 'react';
Expand All @@ -35,6 +36,8 @@ import { useCurrentMediaTheme } from '../utils/media';
import throttle from '../utils/throttle';
import KanariesLogo from '../assets/kanaries.png';
import { ImageWithFallback } from '../components/timeoutImg';
import LimitSetting from '../components/limitSetting';
import { runInAction } from 'mobx';

const Invisible = styled.div`
clip: rect(1px, 1px, 1px, 1px);
Expand Down Expand Up @@ -73,7 +76,7 @@ const VisualSettings: React.FC<IVisualSettings> = ({
exclude = [],
}) => {
const { vizStore, commonStore } = useGlobalStore();
const { visualConfig, canUndo, canRedo } = vizStore;
const { visualConfig, canUndo, canRedo, limit } = vizStore;
const { t: tGlobal } = useTranslation();
const { t } = useTranslation('translation', { keyPrefix: 'main.tabpanel.settings' });

Expand Down Expand Up @@ -493,6 +496,24 @@ const VisualSettings: React.FC<IVisualSettings> = ({
},
...(extra.length === 0 ? [] : ['-', ...extra]),
'-',
{
key: 'limit_axis',
label: t('limit'),
icon: HashtagIcon,
form: (
<FormContainer>
<LimitSetting
value={limit}
setValue={(v) => {
runInAction(() => {
vizStore.limit = v;
});
}}
/>
</FormContainer>
),
},
'-',
{
key: 'kanaries',
label: 'kanaries',
Expand Down Expand Up @@ -532,6 +553,7 @@ const VisualSettings: React.FC<IVisualSettings> = ({
dark,
extra,
exclude,
limit,
]);

return (
Expand Down
Loading

1 comment on commit f46c0d7

@vercel
Copy link

@vercel vercel bot commented on f46c0d7 Aug 4, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.