Skip to content

Commit

Permalink
feat: element-select support multiple triggerOff
Browse files Browse the repository at this point in the history
  • Loading branch information
xile611 committed Apr 16, 2024
1 parent 9852ffb commit ab3ce55
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 42 deletions.
61 changes: 21 additions & 40 deletions packages/vgrammar-core/src/interactions/element-select.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { isNumber, isString } from '@visactor/vutils';
import { isArray } from '@visactor/vutils';
import { InteractionStateEnum } from '../graph/enums';
import type {
ElementSelectOptions,
Expand All @@ -10,7 +10,7 @@ import type {
IView,
InteractionEvent
} from '../types';
import { groupMarksByState } from './utils';
import { groupMarksByState, parseTriggerOffOfSelect } from './utils';
import { BaseInteraction } from './base';

export interface ElementSelect extends IToggleStateMixin, BaseInteraction<ElementSelectOptions> {}
Expand All @@ -23,7 +23,7 @@ export class ElementSelect extends BaseInteraction<ElementSelectOptions> {
state: InteractionStateEnum.selected,
trigger: 'click'
};
protected _resetType?: 'view' | 'self' | 'timeout';
protected _resetType: ('view' | 'self' | 'timeout')[] = [];
protected _marks?: IMark[];
protected _stateMarks: Record<string, IMark[]>;
private _timer?: number;
Expand Down Expand Up @@ -52,35 +52,15 @@ export class ElementSelect extends BaseInteraction<ElementSelectOptions> {
}
];

let eventName = triggerOff;
const { eventNames, resetType } = parseTriggerOffOfSelect(triggerOff);

if (triggerOff === 'empty') {
eventName = trigger as EventType;

this._resetType = 'view';
} else if (triggerOff === 'none') {
eventName = null;
this._resetType = null;
} else if (isString(triggerOff)) {
if ((triggerOff as string).includes('view:')) {
eventName = (triggerOff as string).replace('view:', '') as EventType;

this._resetType = 'view';
} else {
eventName = triggerOff;

this._resetType = 'self';
eventNames.forEach(evt => {
if (evt && (isArray(trigger) ? !trigger.includes(evt) : evt !== trigger)) {
events.push({ type: evt as EventType, handler: this.handleReset });
}
} else if (isNumber(triggerOff)) {
eventName = null;
this._resetType = 'timeout';
} else {
this._resetType = null;
}
});

if (eventName && eventName !== trigger) {
events.push({ type: eventName as EventType, handler: this.handleReset });
}
this._resetType = resetType;

return events;
}
Expand Down Expand Up @@ -108,13 +88,14 @@ export class ElementSelect extends BaseInteraction<ElementSelectOptions> {
const { state, reverseState, isMultiple } = this.options;
if (element && this._marks && this._marks.includes(element.mark)) {
if (element.hasState(state)) {
if (this._resetType === 'self') {
this._statedElements = this.updateStates(
this._statedElements && this._statedElements.filter(el => el !== element),
this._statedElements,
state,
reverseState
);
if (this._resetType.includes('self')) {
const newStatedElements = this._statedElements && this._statedElements.filter(el => el !== element);

if (newStatedElements && newStatedElements.length) {
this._statedElements = this.updateStates(newStatedElements, this._statedElements, state, reverseState);
} else {
this.clearPrevElements();
}
}
} else {
if (this._timer) {
Expand All @@ -130,13 +111,13 @@ export class ElementSelect extends BaseInteraction<ElementSelectOptions> {
);
this.dispatchEvent('start', { elements: this._statedElements, options: this.options });

if (this._resetType === 'timeout') {
if (this._resetType.includes('timeout')) {
this._timer = setTimeout(() => {
this.clearPrevElements();
}, this.options.triggerOff as number) as unknown as number;
}
}
} else if (this._resetType === 'view' && this._statedElements && this._statedElements.length) {
} else if (this._resetType.includes('view') && this._statedElements && this._statedElements.length) {
this.clearPrevElements();
}
}
Expand All @@ -148,9 +129,9 @@ export class ElementSelect extends BaseInteraction<ElementSelectOptions> {

const hasActiveElement = element && this._marks && this._marks.includes(element.mark);

if (this._resetType === 'view' && !hasActiveElement) {
if (this._resetType.includes('view') && !hasActiveElement) {
this.clearPrevElements();
} else if (this._resetType === 'self' && hasActiveElement) {
} else if (this._resetType.includes('self') && hasActiveElement) {
this.clearPrevElements();
}
}
Expand Down
32 changes: 31 additions & 1 deletion packages/vgrammar-core/src/interactions/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { ElementFilterOptions, IElement, IMark, MarkSpec } from '../types';
import { array, isNumber, isString } from '@visactor/vutils';
import type { ElementFilterOptions, ElementSelectTriggerOff, EventType, IElement, IMark, MarkSpec } from '../types';

export const generateFilterValue = (options: ElementFilterOptions) => {
if (options.filterField) {
Expand Down Expand Up @@ -40,3 +41,32 @@ export const groupMarksByState = (marks: IMark[], states: string[]): Record<stri

return res;
};

export const parseTriggerOffOfSelect = (triggerOff: ElementSelectTriggerOff | ElementSelectTriggerOff[]) => {
const triggerOffArray = array(triggerOff);
const resetType: ('view' | 'self' | 'timeout')[] = [];
const eventNames: EventType[] = [];

triggerOffArray.forEach(off => {
if (off === 'empty') {
resetType.push('view');
} else if (isString(off) && off !== 'none') {
if ((off as string).includes('view:')) {
eventNames.push((off as string).replace('view:', '') as EventType);

resetType.push('view');
} else {
eventNames.push(off as EventType);

resetType.push('self');
}
} else if (isNumber(off)) {
resetType.push('timeout');
}
});

return {
eventNames,
resetType
};
};
4 changes: 3 additions & 1 deletion packages/vgrammar-core/src/types/interaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ export interface ElementActiveOptions extends IBaseInteractionOptions {
state?: string;
}

export type ElementSelectTriggerOff = EventType | ViewEventType | 'empty' | 'none' | number;

/**
* the interaction to set the seleted state of specified marks
*/
Expand All @@ -104,7 +106,7 @@ export interface ElementSelectOptions extends IBaseInteractionOptions {
/**
* the reset trigger event name
*/
triggerOff?: EventType | EventType[] | ViewEventType | 'empty' | 'none' | number;
triggerOff?: ElementSelectTriggerOff | ElementSelectTriggerOff[];
/**
* whether or not support multiple selected
*/
Expand Down

0 comments on commit ab3ce55

Please sign in to comment.