- 由 wyn_writer创建, 最后修改于七月 27, 2022
1. 内容概述
Visual api 用于呈现自定义的可视化效果。它提供了一些有用的界面帮助作者做一些工作,例如显示提示、交叉筛选、管理选择状态和 i18n。
2. Interface
interface IFilterTarget {
// dataset: string;
column: string;
}
enum VisualFilterType {
Basic = 'basic',
Advanced = 'advanced',
Tuple = 'tuple',
}
interface IFilterBase {
target: IFilterTarget;
filterType: VisualFilterType;
}
enum BasicFilterOperator {
In = 'In',
NotIn = 'NotIn',
}
interface IBasicFilter extends IFilterBase {
operator: BasicFilterOperator;
values: any[];
}
enum AdvancedFilterOperator {
LessThan = 'LessThan',
LessThanOrEqual = 'LessThanOrEqual',
GreaterThan = 'GreaterThan',
GreaterThanOrEqual = 'GreaterThanOrEqual',
}
interface IAdvancedFilterCondition {
value: any;
operator: AdvancedFilterOperator;
}
enum AdvancedFilterLogicalOperator {
And = 'And',
Or = 'Or',
}
interface IAdvancedFilter extends IFilterBase {
conditions: IAdvancedFilterCondition[];
logicalOperator: AdvancedFilterLogicalOperator;
}
type ITuple = { value: any }[];
interface ITupleFilter {
target: IFilterTarget[];
filterType: VisualFilterType;
operator: BasicFilterOperator;
values: ITuple[];
}
type IFilter = IBasicFilter | IAdvancedFilter | ITupleFilter;
interface IVisualUpdateOptions {
isViewer: boolean;
isMobile: boolean;
isEditing: boolean;
isFocus: boolean;
dataViews: any;
updateType: string;
properties: any;
// docTheme: RuntimeNS.IDocTheme;
language: string;
scale: number;
filters: IFilter[];
}
VisualUpdateOption是可视化更新时的数据包(包含聚合更改或属性更改)。
- isViewer:表示在预览仪表板时是否呈现该组件。
- isMobile::表示是否在移动端显示该组件。
- isEditing:表示组件的编辑状态。当触发了编辑操作时,值为true。
- isFocus: 表示组件的聚焦状态。当触发了聚焦操作时,值为true。
- dataViews: 在capabilities.json中定义的计算数据视图(dataViewMapping)。
- properties: 在 capabilities.json 中定义的属性模型(options.visual)。
- docTheme: 选择的文档主题。
- language: 当前语言。
- scale: 比例因子。
- filters: 用来影响其他组件。
3. Visual
要实现的主类。
declare class WynVisual {
static Models = {
Filter: {
BasicFilter, AdvancedFilter, TupleFilter,
},
};
static Enums = {
FilterType,
BasicFilterOperator,
AdvancedFilterOperator,
AdvancedFilterLogicalOperator,
UpdateType
};
constructor(dom: HTMLDivElement, host: VisualNS.VisualHost, updateOptions: VisualNS.IVisualUpdateOptions);
update(options: VisualNS.IVisualUpdateOptions): void;
getInspectorHiddenState(updateOptions: VisualNS.IVisualUpdateOptions): string[];
getActionBarHiddenState(updateOptions: VisualNS.IVisualUpdateOptions): string[];
getColorAssignmentConfigMapping(dataViews: VisualNS.IDataView[]): VisualNS.IColorAssignmentConfigMapping;
onResize(): void;
onDestroy(): void;
}
更新将调用每个聚合更改和属性更改。
getInspectorHiddenState返回值路径列表,表示此属性不应显示在属性设置中。
- getActionBarHiddenState返回值路径列表,表示此属性不应显示在操作栏中。
- getColorAssignmentConfigMapping 返回一个颜色赋值映射对象,表示应该给哪个值赋值颜色。
- 在调整组件对象的大小时将调用onResize。
- 卸载时将调用onDestroy。
- 静态属性Models和Enums为自定义可视化插件提供一些类或枚举。
4. Color Assignment 示例
//capabilities.json
{
options: {
visual: [
{
properties: [{
"name": "mainColorAssignment",
"type": "ColorAssignment",
"displayName": "Enable Color Assignment",
"defaultValue": true
}]
}
]
}
}
// visual.ts
export default class Visual extends WynVisual {
//...others method
private render() {
//...
const circle = node.append('circle')
.attr('r', function (d: any) { return d.r; })
.style('fill', function (d: any) {
//consume the color assignment property
if (options.mainColorAssignment) {
return options.mainColorAssignment[d.data.size] || color(d.package);
} else {
return color(d.package);
}
});
//...
}
public getColorAssignmentConfigMapping(dataViews: VisualNS.IDataView[]): VisualNS.IColorAssignmentConfigMapping {
if (!dataViews.length) {
return null;
}
const plain = dataViews[0].plain;
const colorProfile = plain.profile.series.values[0];
const dimProfile = plain.profile.dimensions.values[0];
if (!colorProfile || !dimProfile) {
return null;
}
// make the values in series color assignmentable
const colorValues = plain.data.map(d => d[colorProfile.display]);
return {
mainColorAssignment: {
values: Array.from(new Set(colorValues)),
type: 'dimension',
columns: [colorProfile],
},
};
}
}
5. VisualHost
eventService
EventService 用于调度一些渲染事件,并且用户可以注册操作栏自定义事件处理程序。
| 方法 | interface | 说明 | 示例 |
|---|---|---|---|
| renderStart | renderStart(): void | 用于通知仪表板该组件的渲染状态 | // Update method will be invoke while something changed.
public update(options: VisualNS.IVisualUpdateOptions) {
this.visualHost.eventService.renderStart();
try {
this.render(); // render method is used to update UI
this.visualHost.eventService.renderFinish();
} catch (e) {
this.visualHost.eventService.renderError();
}
}
|
| renderError | renderError(): void | ||
| renderFinish | renderFinish(): void | ||
registerOnCustomEventCallback | registerOnCustomEventCallback(fn: (name: string) => void) | 用于注册操作栏事件 callback 是自定义事件处理程序 | // Register the callback in visual constructor. It will print the action name when you click the corresponding button on action bar.
this.visualHost.eventService.registerOnCustomEventCallback((name: string) => {
console.log(name);
});
|
localizationManager
LocalizationManger 用于在此处提供一些本地化的字符串。
| 方法 | interface | 说明 | 示例 |
|---|---|---|---|
| getDisplay | getDisplay(displayNameKey: string): string | 返回本地化字符串 | const noDataText = this.visualHost.localizationManager.getDisplay('NoData');
this.container.textContent = noDataText;
|
toolTipService
TooltipService 用来控制提示的显示/隐藏。
| 方法 | interface | 说明 | 示例 |
|---|---|---|---|
| show | interface ITootipPosition {
x: number;
y: number;
}
interface ILabelFields {
label: string;
value: string;
}
interface ITooltipConfig {
position: ITootipPosition;
title?: string;
fields?: ILabelFields[];
selectionId?: SelectionId;
selected?: SelectionId[];
menu?: boolean;
}
show(config: VisualImplNS.ITooltipConfig): void
| 显示提示(tooltip)
| // this function will bind to the chart mouse enter event. It will show a tooltip with title, fields, selection info and menu buttons.
private mouseEnterHandler = (node: any) => {
this.visualHost.toolTipService.show({
position: {
x: d3.event.x,
y: d3.event.y,
},
title: node.data.color,
fields: [{
label: this.valueField,
value: node.data.value,
}],
selectionId: node.data.selectionId,
selected: this.selectionManager.getSelectionIds(),
menu: true,
});
}
|
| hide | hide(): void | 隐藏提示(tooltip) | // hide the tooltip when the mouse leave the chart shape
private mouseLeaveHandler = () => {
this.visualHost.toolTipService.hide();
}
|
| move | move(pos: VisualImplNS.ITootipPosition): void | 移动提示(tooltip) | // update the tooltip position while mouse move on chart
private mouseMoveHandler = (e: any) => {
this.visualHost.toolTipService.move({
x: d3.event.x,
y: d3.event.y,
});
}
|
propertyService
PropertyService 用于更新在 capabilities.json 中定义的属性(options.visual)。
| 方法 | interface | 说明 | 示例 |
|---|---|---|---|
| setProperty | setProperty(propertyName: string, value: any): void | 更新属性 | // when the button be clicked. The property of showBorder will be change.
private onToggle = () => {
this.visualHost.propertyService.setProperty('showBorder', !this.properties.showBorder);
}
|
formatService
FormatService用来设置格式化。
| 方法 | inferface | 说明 | 示例 |
|---|---|---|---|
| isAutoDisplayUnit | isAutoDisplayUnit(displayUnit: DisplayUnit): boolean | 判断 displayUnit 是否为DisplayUnit.Auto | enum DisplayUnit {
Auto = 'auto',
None = 'none',
Hundreds = 'hundreds',
Thousands = 'thousands',
TenThousand = 'tenThousands',
HundredThousand = 'hundredThousand',
Millions = 'millions',
TenMillion = 'tenMillion',
HundredMillion = 'hundredMillion',
Billions = 'billions',
Trillions = 'trillions',
}
|
| getAutoDisplayUnit | getAutoDisplayUnit(values: number[]): DisplayUnit | 当displayUnit为DisplayUnit.Auto时,得到realDisplayUnit。 | |
| format | format(format: string, value: number): string | 格式化数值。 第一个参数格式来自 Format Property或 DataView Profile 项目。 默认 displayUnit 为 DisplayUnit.None; | const formatService = this.visualHost.formatService;
const { displayUnit, labelFormat } = updateOptions.properties;
let realDisplayUnit = displayUnit;
if (formatService.isAutoDisplayUnit(displayUnit)) {
const values = dataPoints.map(dataPoint => dataPoint.value);
realDisplayUnit = formatService.getAutoDisplayUnit(values);
}
dataPoints.forEach((dataPoint) => {
// prepare a formated text for label.
dataPoint.labelText = formatService.format(labelFormat, dataPoint.value, realDisplayUnit);
})
|
selectionService
| 方法 | interface | 说明 | 示例 |
|---|---|---|---|
| createSelectionManager | createSelectionManager(): SelectionManager | 创建一个SelectionManager。 | this.selectionManager = this.visualHost.selectionService.createSelectionManager(); |
| createSelectionId | createSelectionId(): SelectionId | 为数据点创建SelectionId。 | const selectionId = this.visualHost.selectionService.createSelectionId(); |
SelectionId
SelectionId就像一个向量。它包含绑定到它的维度和度量。将 selection id 附加到数据点后,在图表中单击时就可以触发。
还可以用SelectionId 做很多事情,例如交叉筛选、呈现选择状态、显示提示信息和链接文档。
| 方法 | interface | 说明 | 示例 |
|---|---|---|---|
| withMeasure | withMeasure(profile: RuntimeNS.IFieldProfile): SelectionId | 将度量字段绑定到selection id。 | public update(options: VisualNS.IVisualUpdateOptions) {
console.log(options);
const dataView = options.dataViews[0];
// convert the data view to datas which can be used in chart
if (dataView) {
const plainData = dataView.plain;
// get the fields via different data role.
const valueField = plainData.profile.values.values[0].display;
const sizeField = plainData.profile.series.values[0].display;
const colorField = plainData.profile.dimensions.values[0].display;
const items = plainData.data.reduce((result: any, item: any, i: number) => {
if (item[valueField]) {
// create a selection id for a data point.
const selectionId = this.visualHost.selectionService.createSelectionId();
// set the dimensions of the selection id. This will affect on the crossfilter result and others.
selectionId
.withDimension(plainData.profile.dimensions.values[0], item)
.withDimension(plainData.profile.series.values[0], item);
// attach the selection id on data model. so that you can access it on hit test
result.push({
size: (item[sizeField] || '-') + '',
value: item[valueField],
color: (item[colorField] || '') + '',
id: i,
selectionId,
});
}
return result;
}, []);
// refresh the chart with new data items
this.render(items);
}
};
|
| withDimension | withDimension(profile: RuntimeNS.IFieldProfile, dataPoint: RuntimeNS.IDataPoint): SelectionId | 将纬度字段绑定到selection id。 |
SelectionManager
Selection manager 用于管理选择状态,并触发交叉筛选。
| 方法 | interface | 说明 | 示例 |
|---|---|---|---|
setSelectionIds | setSelectionIds(ids: Array<SelectionId>) | setter | |
getSelectionIds | getSelectionIds(): Array<SelectionId> | getter | |
getCount | getCount(): number | 获取所选selectionId的计数 | |
select | select(id: SelectionId | Array<SelectionId>, multiSelect: boolean = false): Promise<void> | 选择一个或多个selectionId。multiselect表示应该支持选择多个selectionId。它返回一个许可,并将在选择完成后解决。 | private clickHandler = (node: any) => {
d3.event.stopPropagation();
// get selection id which bind on data model
const selectionId = node.data.selectionId
// if it has been selected, unselect it.
if (!this.selectionManager.contains(selectionId)) {
this.selectionManager.select(selectionId, true);
} else {
this.selectionManager.clear(selectionId);
}
// show tooltip
this.visualHost.toolTipService.show({
position: {
x: d3.event.x,
y: d3.event.y,
},
title: node.data.color,
fields: [{
label: this.valueField,
value: node.data.value,
}],
selectionId: node.data.selectionId, // the field which display in tooltip
selected: this.selectionManager.getSelectionIds(), // all the selected selectionId, will be use in keep/exclude and drill.
menu: true,
});
}
|
clear | clear(id?: SelectionId): Promise<void> | 清除选中的或者全部 SelectionId。 | |
registerOnSelectCallback | registerOnSelectCallback(onSelectCallback: (ids: SelectionId[]) => void) | Register a callback, the callback will be invoke after every selection change. 注册 callback,每次选择更改后都将调用callback。 | this.selectionManager.registerOnSelectCallback(() => {
// while the selection state changed, should update ui, to highlight the shape which has been selected
this.render();
});
|
contains | contains(id: SelectionId): boolean | 检查目标id 是否被选中。 | |
isEmpty | isEmpty(): boolean | 检查 selection manager 是否包含选中的selectionId。 |
configurationManager
ConfigurationManger should work with the configuration in visual.json. If you defined a configuration item. you can access it via this.
ConfigurationManger 应与 visual.json中的配置项一起使用。如果定义一个配置项目,就可以通过这个进行访问。
| method | interface | description | sample |
|---|---|---|---|
| get | get(key: string): string | 获取配置项 | // to access the amapToken in configuration
const amapToken = this.visualHost.configurationManager.get('amapToken');
|
assetsManager
AssetsManager 应与 visual.json 中的 assets 一起使用。当前您可以在assets 中定义图片资源,它们将被打包。可以通过这个访问图片。
| method | interface | description | sample |
|---|---|---|---|
| getImage | getImage(imageName: string): string | 返回base64字符串 | // to access the image in assets
this.container.style.backgroundImage = `url(${host.assetsManager.getImage('testImg')})`;
|
commandService
CommandService 提供了一些仪表板命令,例如切换选项卡容器。
| 方法 | interface | 说明 | 示例 |
|---|---|---|---|
| execute | interface ICommandDescription {
name: string;
payload: {
target: string;
};
}
execute(commandsDesc: Array<ICommandDescription>): void
| 执行命令 | // run a command to active a tab container
this.visualHost.commandService.execute([{
name: 'SelectTab',
payload: {
target: 'myTabContainer',
index: 0,
}
}])
|
SwitchTab
| Command Name | 描述 | 说明 | 示例 |
|---|---|---|---|
| SwitchTab | 用户可以使用这个命令来切换选项卡组件中的标签。 |
|
|
SwitchPage
| Command Name | 描述 | 说明 | 示例 |
|---|---|---|---|
| SwitchPage | 用户可以使用这个命令来切换仪表板页面。 |
|
|
Keep/Exclude
| Command Name | 描述 | 说明 | 示例 |
|---|---|---|---|
| Keep/Exclude | 用户可以使用这个命令来执行保留或排除操作。 | // Keep & Exclude
this.host.commandService.execute([{
name: 'Keep', // or 'Exclude'
payload: {
selectionIds,
},
}]);
| — |
Drill/Jump
| Command Name | 描述 | 说明 | 示例 |
|---|---|---|---|
| Drill/Jump | 用户可以使用这个命令来执行钻取或跳转操作。 | // Drill & Jump
this.host.commandService.execute([{
name: 'Drill', // or 'Jump'
payload: {
selectionIds, // optional
position: { x: event.clientX, y: event.clientY },
},
}]);
| — |
filterService
FilterService用于派遣过滤器影响其他场景。过滤器将被列在 updateOptions.filters 中。
目前,您可以创建Basic,Tuple和Advanced Filter。
首先,你应该通过设置options.common.filter为 "true "来启用过滤器。
| 方法 | interface | description | 示例 |
|---|---|---|---|
| applyFilter | interface IFilterTarget {
// dataset: string;
column: string;
}
enum VisualFilterType {
Basic = 'basic',
Advanced = 'advanced',
Tuple = 'tuple',
}
interface IFilterBase {
target: IFilterTarget;
filterType: VisualFilterType;
}
applyFilter(filter: BasicFilter | TupleFilter | AdvancedFilter): void
| 这个方法用来唤醒过滤器。 | dom.addEventListener('click', (e) => {
const it = e.target as HTMLLIElement;
if (it.hasAttribute('data-value')) {
const val = it.dataset.value;
if (this.filter.contains(val)) {
this.filter.remove(val);
} else {
this.filter.add(val);
}
host.filterService.applyFilter(this.filter);
} else if (it.getAttribute('data-role') === 'toggle') {
this.filter.setOperator(this.filter.getOperator() === Enums.BasicFilterOperator.In ?
Enums.BasicFilterOperator.NotIn :
Enums.BasicFilterOperator.In
);
host.filterService.applyFilter(this.filter);
}
})
|
| clean | clean(): void | 清除过滤器 | host.filterService.clean(); |
BasicFilter
与内置的标签列表类似,相当于SQL:
SELECT * FROM table WHERE Dept IN ( DTD1 , DTD2 , DTD3 )
| 方法 | interface | description | 示例 |
|---|---|---|---|
| constructor | enum BasicFilterOperator {
In = 'In',
NotIn = 'NotIn',
}
interface IBasicFilter extends IFilterBase {
operator: BasicFilterOperator;
values: any[];
}
constructor(targetProfile: RuntimeNS.IFieldProfile, operator: BasicFilterOperator = BasicFilterOperator.In, values: any[] = [])
| targetProfile是数据视图中的过滤区域。 | const profile = dv.plain.profile.dimensions.values[0]; const BasicFilter = WynVisual.Models.Filter.BasicFilter; const filter = new BasicFilter(profile); |
| setOperator/getOperator | setOperator(operator: BasicFilterOperator) getOperator(): BasicFilterOperator | this.filter.setOperator(this.filter.getOperator() === WynVisual.Enums.BasicFilterOperator.In ?
WynVisual.Enums.BasicFilterOperator.NotIn :
WynVisual.Enums.BasicFilterOperator.In
)
| |
| setValues/getValues | getValues(): any[] setValues(vals: any[]) | filter.setValues(['DTD1', 'DTD2']) | |
| toJSON/fromJSON | toJSON(): BasicNS.IBasicFilter fromJSON(obj: BasicNS.IBasicFilter) | 从updateOptions.filters中恢复过滤器。 | const profile = dv.plain.profile.dimensions.values[0]; const filter = new BasicFilter(profile); filter.fromJSON(options.filters[0]); |
| contains | contains(value: any): boolean | 是否包含在过滤器中的值 | if (this.filter.contains(val)) {
this.filter.remove(val);
} else {
this.filter.add(val);
}
|
| remove/add | remove(val: any) add(val: any) | 添加或移除值 | if (this.filter.contains(val)) {
this.filter.remove(val);
} else {
this.filter.add(val);
}
|
| isEmpty | isEmpty(): boolean |
TupleFilter
它类似于BasicFilter,但它可以在多维度时生效.相当于SQL:
SELECT * FROM DataTable WHERE ( Team = "DTD1" AND Prod = "Wyn" ) OR ( Team = "DTD2" AND Prod = "SpreadJS" );
| 方法 | interface | description | 示例 |
|---|---|---|---|
| constructor | type ITuple = { value: any }[];
interface ITupleFilter {
target: IFilterTarget[];
filterType: VisualFilterType;
operator: BasicFilterOperator;
values: ITuple[];
}
constructor(targetProfiles: RuntimeNS.IFieldProfile[], operator: BasicFilterOperator = BasicFilterOperator.In, values: BasicNS.ITuple[] = [])
| const dimensionsProfiles = dv.plain.profile.dimensions.values; const filter = new TupleFilter(dimensionsProfiles); | |
| setOperator/getOperator | setOperator(operator: BasicFilterOperator) getOperator(): BasicFilterOperator | ||
| getValues/setValues | getValues(): BasicNS.ITuple[] setValues(vals: BasicNS.ITuple[]) | ||
| toJSON/fromJSON | toJSON(): BasicNS.ITupleFilter fromJSON(obj: BasicNS.ITupleFilter) | ||
| contains | contains(tuple: BasicNS.ITuple): boolean | ||
| remove/add | remove(tuple: BasicNS.ITuple) add(tuple: BasicNS.ITuple) | ||
| createTuple | createTuple(dp: RuntimeNS.IDataPoint, depth?: number): BasicNS.ITuple | 按选定的数据点创建元组 | this.items = dv.plain.data.reduce((res, dp) => {
const label = Object.keys(dp).map(key => dp[key]).join('-');
const tuple = filter.createTuple(dp);
res.push({
label,
className: filter.contains(tuple) ? 'selected' : '',
tuple: JSON.stringify(tuple),
});
return res;
}, []);
|
| isEmpty | isEmpty(): boolean |
AdvancesFilter
高级过滤器提供了lessThan、greatThan和其他运算符来查询,目前不支持逻辑运算符 "Or"。
| 方法 | interface | description | 示例 |
|---|---|---|---|
| constructor | enum AdvancedFilterLogicalOperator {
And = 'And',
Or = 'Or',
}
enum AdvancedFilterOperator {
LessThan = 'LessThan',
LessThanOrEqual = 'LessThanOrEqual',
GreaterThan = 'GreaterThan',
GreaterThanOrEqual = 'GreaterThanOrEqual',
}
interface IAdvancedFilterCondition {
value: any;
operator: AdvancedFilterOperator;
}
interface IAdvancedFilter extends IFilterBase {
conditions: IAdvancedFilterCondition[];
logicalOperator: AdvancedFilterLogicalOperator;
}
constructor(targetProfile: RuntimeNS.IFieldProfile, logicalOperator: AdvancedFilterLogicalOperator = AdvancedFilterLogicalOperator.And, conditions: BasicNS.IAdvancedFilterCondition[] = [])
| ||
| setLogicalOperator/getLogicalOperator | setLogicalOperator(operator: AdvancedFilterLogicalOperator) getLogicalOperator(): AdvancedFilterLogicalOperator | ||
| setConditions/getConditions | setConditions(conditions: BasicNS.IAdvancedFilterCondition[]) getConditions(): BasicNS.IAdvancedFilterCondition[] | this.filter.setConditions([{
value: min,
operator: this.includeMinEle.checked ? Enums.AdvancedFilterOperator.GreaterThanOrEqual : Enums.AdvancedFilterOperator.GreaterThan,
}, {
value: max,
operator: this.includeMaxEle.checked ? Enums.AdvancedFilterOperator.LessThanOrEqual : Enums.AdvancedFilterOperator.LessThan,
}]);
| |
| toJSON/fromJSON | toJSON(): BasicNS.IAdvancedFilter fromJSON(obj: BasicNS.IAdvancedFilter) | ||
| remove/add | remove(condtion: BasicNS.IAdvancedFilterCondition) add(condtion: BasicNS.IAdvancedFilterCondition) | ||
| isEmpty | isEmpty(): boolean |
- 无标签
