Fix code highlighting and optimize category select
This commit introduces a batched debounce mechanism for managing user
selection state changes. It effectively reduces unnecessary processing
during rapid script checking, preventing multiple triggers for code
compilation and UI rendering.
Key improvements include:
- Enhanced performance, especially noticeable when selecting large
categories. This update resolves minor UI freezes experienced when
selecting categories with numerous scripts.
- Correction of a bug where the code area only highlighted the last
selected script when multiple scripts were chosen.
Other changes include:
- Timing functions:
- Create a `Timing` folder for `throttle` and the new
`batchedDebounce` functions.
- Move these functions to the application layer from the presentation
layer, reflecting their application-wide use.
- Refactor existing code for improved clarity, naming consistency, and
adherence to new naming conventions.
- Add missing unit tests.
- `UserSelection`:
- State modifications in `UserSelection` now utilize a singular object
inspired by the CQRS pattern, enabling batch updates and flexible
change configurations, thereby simplifying change management.
- Remove the `I` prefix from related interfaces to align with new coding
standards.
- Refactor related code for better testability in isolation with
dependency injection.
- Repository:
- Move repository abstractions to the application layer.
- Improve repository abstraction to combine `ReadonlyRepository` and
`MutableRepository` interfaces.
- E2E testing:
- Introduce E2E tests to validate the correct batch selection
behavior.
- Add a specialized data attribute in `TheCodeArea.vue` for improved
testability.
- Reorganize shared Cypress functions for a more idiomatic Cypress
approach.
- Improve test documentation with related information.
- `SelectedScript`:
- Create an abstraction for simplified testability.
- Introduce `SelectedScriptStub` in tests as a substitute for the
actual object.
This commit is contained in:
33
tests/unit/shared/Stubs/BatchedDebounceStub.ts
Normal file
33
tests/unit/shared/Stubs/BatchedDebounceStub.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { batchedDebounce } from '@/application/Common/Timing/BatchedDebounce';
|
||||
|
||||
export class BatchedDebounceStub<T> {
|
||||
public readonly callHistory = new Array<Parameters<typeof batchedDebounce>>();
|
||||
|
||||
public readonly collectedArgs = new Array<T>();
|
||||
|
||||
private executeImmediately = false;
|
||||
|
||||
public func = (
|
||||
callback: (batches: readonly T[]) => void,
|
||||
waitInMs: number,
|
||||
): (arg: T) => void => {
|
||||
this.callHistory.push([callback, waitInMs]);
|
||||
return (arg: T) => {
|
||||
this.collectedArgs.push(arg);
|
||||
if (this.executeImmediately) {
|
||||
callback([arg]);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
public withImmediateDebouncing(executeImmediately: boolean): this {
|
||||
this.executeImmediately = executeImmediately;
|
||||
return this;
|
||||
}
|
||||
|
||||
public execute() {
|
||||
this.callHistory
|
||||
.map((call) => call[0])
|
||||
.forEach((callback) => callback(this.collectedArgs));
|
||||
}
|
||||
}
|
||||
@@ -2,16 +2,17 @@ import { IApplicationCode } from '@/application/Context/State/Code/IApplicationC
|
||||
import { IUserFilter } from '@/application/Context/State/Filter/IUserFilter';
|
||||
import { ICategoryCollectionState } from '@/application/Context/State/ICategoryCollectionState';
|
||||
import { OperatingSystem } from '@/domain/OperatingSystem';
|
||||
import { SelectedScript } from '@/application/Context/State/Selection/SelectedScript';
|
||||
import { IScript } from '@/domain/IScript';
|
||||
import { ScriptStub } from '@tests/unit/shared/Stubs/ScriptStub';
|
||||
import { ICategoryCollection } from '@/domain/ICategoryCollection';
|
||||
import { IUserSelection } from '@/application/Context/State/Selection/IUserSelection';
|
||||
import { UserSelection } from '@/application/Context/State/Selection/UserSelection';
|
||||
import { SelectedScript } from '@/application/Context/State/Selection/Script/SelectedScript';
|
||||
import { CategoryCollectionStub } from './CategoryCollectionStub';
|
||||
import { UserSelectionStub } from './UserSelectionStub';
|
||||
import { UserFilterStub } from './UserFilterStub';
|
||||
import { ApplicationCodeStub } from './ApplicationCodeStub';
|
||||
import { CategoryStub } from './CategoryStub';
|
||||
import { ScriptSelectionStub } from './ScriptSelectionStub';
|
||||
|
||||
export class CategoryCollectionStateStub implements ICategoryCollectionState {
|
||||
public code: IApplicationCode = new ApplicationCodeStub();
|
||||
@@ -24,10 +25,11 @@ export class CategoryCollectionStateStub implements ICategoryCollectionState {
|
||||
|
||||
public collection: ICategoryCollection = new CategoryCollectionStub().withSomeActions();
|
||||
|
||||
public selection: IUserSelection = new UserSelectionStub([]);
|
||||
public selection: UserSelection = new UserSelectionStub();
|
||||
|
||||
constructor(readonly allScripts: IScript[] = [new ScriptStub('script-id')]) {
|
||||
this.selection = new UserSelectionStub(allScripts);
|
||||
this.selection = new UserSelectionStub()
|
||||
.withScripts(new ScriptSelectionStub());
|
||||
this.collection = new CategoryCollectionStub()
|
||||
.withOs(this.os)
|
||||
.withTotalScripts(this.allScripts.length)
|
||||
@@ -60,11 +62,14 @@ export class CategoryCollectionStateStub implements ICategoryCollectionState {
|
||||
|
||||
public withSelectedScripts(initialScripts: readonly SelectedScript[]): this {
|
||||
return this.withSelection(
|
||||
new UserSelectionStub([]).withSelectedScripts(initialScripts),
|
||||
new UserSelectionStub().withScripts(
|
||||
new ScriptSelectionStub()
|
||||
.withSelectedScripts(initialScripts),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public withSelection(selection: IUserSelection) {
|
||||
public withSelection(selection: UserSelection) {
|
||||
this.selection = selection;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -33,6 +33,13 @@ export class CategoryCollectionStub implements ICategoryCollection {
|
||||
return this;
|
||||
}
|
||||
|
||||
public withActions(...actions: readonly ICategory[]): this {
|
||||
for (const action of actions) {
|
||||
this.withAction(action);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public withOs(os: OperatingSystem): this {
|
||||
this.os = os;
|
||||
return this;
|
||||
|
||||
33
tests/unit/shared/Stubs/CategorySelectionStub.ts
Normal file
33
tests/unit/shared/Stubs/CategorySelectionStub.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { CategorySelection } from '@/application/Context/State/Selection/Category/CategorySelection';
|
||||
import { CategorySelectionChangeCommand } from '@/application/Context/State/Selection/Category/CategorySelectionChange';
|
||||
import { StubWithObservableMethodCalls } from './StubWithObservableMethodCalls';
|
||||
|
||||
export class CategorySelectionStub
|
||||
extends StubWithObservableMethodCalls<CategorySelection>
|
||||
implements CategorySelection {
|
||||
public isCategorySelected(categoryId: number, revert: boolean): boolean {
|
||||
const call = this.callHistory.find(
|
||||
(c) => c.methodName === 'processChanges'
|
||||
&& c.args[0].changes.some((change) => (
|
||||
change.newStatus.isSelected === true
|
||||
&& change.newStatus.isReverted === revert
|
||||
&& change.categoryId === categoryId)),
|
||||
);
|
||||
return call !== undefined;
|
||||
}
|
||||
|
||||
public areAllScriptsSelected(): boolean {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
public isAnyScriptSelected(): boolean {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
public processChanges(action: CategorySelectionChangeCommand): void {
|
||||
this.registerMethodCall({
|
||||
methodName: 'processChanges',
|
||||
args: [action],
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,8 @@ export class CategoryStub extends BaseEntity<number> implements ICategory {
|
||||
|
||||
public readonly docs = new Array<string>();
|
||||
|
||||
private allScriptsRecursively: (readonly IScript[]) | undefined;
|
||||
|
||||
public constructor(id: number) {
|
||||
super(id);
|
||||
}
|
||||
@@ -21,13 +23,16 @@ export class CategoryStub extends BaseEntity<number> implements ICategory {
|
||||
}
|
||||
|
||||
public getAllScriptsRecursively(): readonly IScript[] {
|
||||
return [
|
||||
...this.scripts,
|
||||
...this.subCategories.flatMap((c) => c.getAllScriptsRecursively()),
|
||||
];
|
||||
if (this.allScriptsRecursively === undefined) {
|
||||
return [
|
||||
...this.scripts,
|
||||
...this.subCategories.flatMap((c) => c.getAllScriptsRecursively()),
|
||||
];
|
||||
}
|
||||
return this.allScriptsRecursively;
|
||||
}
|
||||
|
||||
public withScriptIds(...scriptIds: string[]): this {
|
||||
public withScriptIds(...scriptIds: readonly string[]): this {
|
||||
return this.withScripts(
|
||||
...scriptIds.map((id) => new ScriptStub(id)),
|
||||
);
|
||||
@@ -40,6 +45,15 @@ export class CategoryStub extends BaseEntity<number> implements ICategory {
|
||||
return this;
|
||||
}
|
||||
|
||||
public withAllScriptIdsRecursively(...scriptIds: readonly string[]): this {
|
||||
return this.withAllScriptsRecursively(...scriptIds.map((id) => new ScriptStub(id)));
|
||||
}
|
||||
|
||||
public withAllScriptsRecursively(...scripts: IScript[]): this {
|
||||
this.allScriptsRecursively = [...scripts];
|
||||
return this;
|
||||
}
|
||||
|
||||
public withMandatoryScripts(): this {
|
||||
return this
|
||||
.withScript(new ScriptStub(`[${CategoryStub.name}] script-1`).withLevel(RecommendationLevel.Standard))
|
||||
|
||||
89
tests/unit/shared/Stubs/ScriptSelectionStub.ts
Normal file
89
tests/unit/shared/Stubs/ScriptSelectionStub.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
import { ScriptSelection } from '@/application/Context/State/Selection/Script/ScriptSelection';
|
||||
import { SelectedScript } from '@/application/Context/State/Selection/Script/SelectedScript';
|
||||
import { IScript } from '@/domain/IScript';
|
||||
import { ScriptSelectionChangeCommand } from '@/application/Context/State/Selection/Script/ScriptSelectionChange';
|
||||
import { StubWithObservableMethodCalls } from './StubWithObservableMethodCalls';
|
||||
import { EventSourceStub } from './EventSourceStub';
|
||||
import { SelectedScriptStub } from './SelectedScriptStub';
|
||||
|
||||
export class ScriptSelectionStub
|
||||
extends StubWithObservableMethodCalls<ScriptSelection>
|
||||
implements ScriptSelection {
|
||||
public readonly changed = new EventSourceStub<readonly SelectedScript[]>();
|
||||
|
||||
public selectedScripts: readonly SelectedScript[] = [];
|
||||
|
||||
public isSelectedResult: boolean | undefined;
|
||||
|
||||
public withSelectedScripts(selectedScripts: readonly SelectedScript[]): this {
|
||||
this.selectedScripts = selectedScripts;
|
||||
return this;
|
||||
}
|
||||
|
||||
public triggerSelectionChangedEvent(scripts: readonly SelectedScript[]): this {
|
||||
this.changed.notify(scripts);
|
||||
return this;
|
||||
}
|
||||
|
||||
public withIsSelectedResult(isSelected: boolean): this {
|
||||
this.isSelectedResult = isSelected;
|
||||
return this;
|
||||
}
|
||||
|
||||
public isScriptSelected(scriptId: string, revert: boolean): boolean {
|
||||
const call = this.callHistory.find(
|
||||
(c) => c.methodName === 'processChanges'
|
||||
&& c.args[0].changes.some((change) => (
|
||||
change.newStatus.isSelected === true
|
||||
&& change.newStatus.isReverted === revert
|
||||
&& change.scriptId === scriptId)),
|
||||
);
|
||||
return call !== undefined;
|
||||
}
|
||||
|
||||
public isScriptDeselected(scriptId: string): boolean {
|
||||
const call = this.callHistory.find(
|
||||
(c) => c.methodName === 'processChanges'
|
||||
&& c.args[0].changes.some((change) => (
|
||||
change.newStatus.isSelected === false
|
||||
&& change.scriptId === scriptId)),
|
||||
);
|
||||
return call !== undefined;
|
||||
}
|
||||
|
||||
public processChanges(action: ScriptSelectionChangeCommand): void {
|
||||
this.registerMethodCall({
|
||||
methodName: 'processChanges',
|
||||
args: [action],
|
||||
});
|
||||
}
|
||||
|
||||
public selectOnly(scripts: ReadonlyArray<IScript>): void {
|
||||
this.registerMethodCall({
|
||||
methodName: 'selectOnly',
|
||||
args: [scripts],
|
||||
});
|
||||
this.selectedScripts = scripts.map((s) => new SelectedScriptStub(s));
|
||||
}
|
||||
|
||||
public selectAll(): void {
|
||||
this.registerMethodCall({
|
||||
methodName: 'selectAll',
|
||||
args: [],
|
||||
});
|
||||
}
|
||||
|
||||
public deselectAll(): void {
|
||||
this.registerMethodCall({
|
||||
methodName: 'deselectAll',
|
||||
args: [],
|
||||
});
|
||||
}
|
||||
|
||||
public isSelected(): boolean {
|
||||
if (this.isSelectedResult === undefined) {
|
||||
throw new Error('Method not configured.');
|
||||
}
|
||||
return this.isSelectedResult;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
import { BaseEntity } from '@/infrastructure/Entity/BaseEntity';
|
||||
import { IScript } from '@/domain/IScript';
|
||||
import { RecommendationLevel } from '@/domain/RecommendationLevel';
|
||||
import { SelectedScript } from '@/application/Context/State/Selection/SelectedScript';
|
||||
import { IScriptCode } from '@/domain/IScriptCode';
|
||||
import { SelectedScriptStub } from './SelectedScriptStub';
|
||||
|
||||
export class ScriptStub extends BaseEntity<string> implements IScript {
|
||||
public name = `name${this.id}`;
|
||||
@@ -50,7 +50,7 @@ export class ScriptStub extends BaseEntity<string> implements IScript {
|
||||
return this;
|
||||
}
|
||||
|
||||
public toSelectedScript(isReverted = false): SelectedScript {
|
||||
return new SelectedScript(this, isReverted);
|
||||
public toSelectedScript(): SelectedScriptStub {
|
||||
return new SelectedScriptStub(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,26 @@
|
||||
import { SelectedScript } from '@/application/Context/State/Selection/SelectedScript';
|
||||
import { ScriptStub } from './ScriptStub';
|
||||
import { SelectedScript } from '@/application/Context/State/Selection/Script/SelectedScript';
|
||||
import { IScript } from '@/domain/IScript';
|
||||
|
||||
export class SelectedScriptStub extends SelectedScript {
|
||||
constructor(id: string, revert = false) {
|
||||
super(new ScriptStub(id), revert);
|
||||
export class SelectedScriptStub implements SelectedScript {
|
||||
public readonly script: IScript;
|
||||
|
||||
public readonly id: string;
|
||||
|
||||
public revert: boolean;
|
||||
|
||||
constructor(
|
||||
script: IScript,
|
||||
) {
|
||||
this.id = script.id;
|
||||
this.script = script;
|
||||
}
|
||||
|
||||
public withRevert(revert: boolean): this {
|
||||
this.revert = revert;
|
||||
return this;
|
||||
}
|
||||
|
||||
public equals(): boolean {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ export abstract class StubWithObservableMethodCalls<T> {
|
||||
}
|
||||
}
|
||||
|
||||
type MethodCall<T> = {
|
||||
export type MethodCall<T> = {
|
||||
[K in FunctionKeys<T>]: {
|
||||
readonly methodName: K;
|
||||
readonly args: T[K] extends (...args: infer A) => unknown ? A : never;
|
||||
|
||||
42
tests/unit/shared/Stubs/TimerStub.ts
Normal file
42
tests/unit/shared/Stubs/TimerStub.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { IEventSubscription } from '@/infrastructure/Events/IEventSource';
|
||||
import { EventSource } from '@/infrastructure/Events/EventSource';
|
||||
import { TimeoutType, Timer } from '@/application/Common/Timing/Timer';
|
||||
import { createMockTimeout } from './TimeoutStub';
|
||||
|
||||
export class TimerStub implements Timer {
|
||||
private timeChanged = new EventSource<number>();
|
||||
|
||||
private subscriptions = new Array<IEventSubscription>();
|
||||
|
||||
private currentTime = 0;
|
||||
|
||||
public setTimeout(callback: () => void, ms: number): TimeoutType {
|
||||
const runTime = this.currentTime + ms;
|
||||
const subscription = this.timeChanged.on((time) => {
|
||||
if (time >= runTime) {
|
||||
callback();
|
||||
subscription.unsubscribe();
|
||||
}
|
||||
});
|
||||
this.subscriptions.push(subscription);
|
||||
const id = this.subscriptions.length - 1;
|
||||
return createMockTimeout(id);
|
||||
}
|
||||
|
||||
public clearTimeout(timeoutId: TimeoutType): void {
|
||||
this.subscriptions[+timeoutId].unsubscribe();
|
||||
}
|
||||
|
||||
public dateNow(): number {
|
||||
return this.currentTime;
|
||||
}
|
||||
|
||||
public tickNext(ms: number): void {
|
||||
this.setCurrentTime(this.currentTime + ms);
|
||||
}
|
||||
|
||||
public setCurrentTime(ms: number): void {
|
||||
this.currentTime = ms;
|
||||
this.timeChanged.notify(this.currentTime);
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,15 @@
|
||||
import { shallowRef } from 'vue';
|
||||
import type { SelectionModifier, useUserSelectionState } from '@/presentation/components/Shared/Hooks/UseUserSelectionState';
|
||||
import { IUserSelection } from '@/application/Context/State/Selection/IUserSelection';
|
||||
import { SelectedScript } from '@/application/Context/State/Selection/SelectedScript';
|
||||
import { UserSelection } from '@/application/Context/State/Selection/UserSelection';
|
||||
import { SelectedScript } from '@/application/Context/State/Selection/Script/SelectedScript';
|
||||
import { StubWithObservableMethodCalls } from './StubWithObservableMethodCalls';
|
||||
import { UserSelectionStub } from './UserSelectionStub';
|
||||
import { ScriptSelectionStub } from './ScriptSelectionStub';
|
||||
|
||||
export class UseUserSelectionStateStub
|
||||
extends StubWithObservableMethodCalls<ReturnType<typeof useUserSelectionState>> {
|
||||
private readonly currentSelection = shallowRef<IUserSelection>(
|
||||
new UserSelectionStub([]),
|
||||
private readonly currentSelection = shallowRef<UserSelection>(
|
||||
new UserSelectionStub(),
|
||||
);
|
||||
|
||||
private modifyCurrentSelection(mutator: SelectionModifier) {
|
||||
@@ -19,19 +20,21 @@ export class UseUserSelectionStateStub
|
||||
});
|
||||
}
|
||||
|
||||
public withUserSelection(userSelection: IUserSelection): this {
|
||||
public withUserSelection(userSelection: UserSelection): this {
|
||||
this.currentSelection.value = userSelection;
|
||||
return this;
|
||||
}
|
||||
|
||||
public withSelectedScripts(selectedScripts: readonly SelectedScript[]): this {
|
||||
return this.withUserSelection(
|
||||
new UserSelectionStub(selectedScripts.map((s) => s.script))
|
||||
.withSelectedScripts(selectedScripts),
|
||||
new UserSelectionStub()
|
||||
.withScripts(
|
||||
new ScriptSelectionStub().withSelectedScripts(selectedScripts),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public get selection(): IUserSelection {
|
||||
public get selection(): UserSelection {
|
||||
return this.currentSelection.value;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,91 +1,21 @@
|
||||
import { IUserSelection } from '@/application/Context/State/Selection/IUserSelection';
|
||||
import { SelectedScript } from '@/application/Context/State/Selection/SelectedScript';
|
||||
import { IScript } from '@/domain/IScript';
|
||||
import { StubWithObservableMethodCalls } from './StubWithObservableMethodCalls';
|
||||
import { EventSourceStub } from './EventSourceStub';
|
||||
import { CategorySelection } from '@/application/Context/State/Selection/Category/CategorySelection';
|
||||
import { ScriptSelection } from '@/application/Context/State/Selection/Script/ScriptSelection';
|
||||
import { UserSelection } from '@/application/Context/State/Selection/UserSelection';
|
||||
import { CategorySelectionStub } from './CategorySelectionStub';
|
||||
import { ScriptSelectionStub } from './ScriptSelectionStub';
|
||||
|
||||
export class UserSelectionStub
|
||||
extends StubWithObservableMethodCalls<IUserSelection>
|
||||
implements IUserSelection {
|
||||
public readonly changed = new EventSourceStub<readonly SelectedScript[]>();
|
||||
export class UserSelectionStub implements UserSelection {
|
||||
public categories: CategorySelection = new CategorySelectionStub();
|
||||
|
||||
public selectedScripts: readonly SelectedScript[] = [];
|
||||
public scripts: ScriptSelection = new ScriptSelectionStub();
|
||||
|
||||
constructor(private readonly allScripts: readonly IScript[]) {
|
||||
super();
|
||||
}
|
||||
|
||||
public withSelectedScripts(selectedScripts: readonly SelectedScript[]): this {
|
||||
this.selectedScripts = selectedScripts;
|
||||
public withCategories(categories: CategorySelection): this {
|
||||
this.categories = categories;
|
||||
return this;
|
||||
}
|
||||
|
||||
public triggerSelectionChangedEvent(scripts: readonly SelectedScript[]): this {
|
||||
this.changed.notify(scripts);
|
||||
public withScripts(scripts: ScriptSelection): this {
|
||||
this.scripts = scripts;
|
||||
return this;
|
||||
}
|
||||
|
||||
public isScriptAdded(scriptId: string): boolean {
|
||||
const call = this.callHistory.find(
|
||||
(c) => c.methodName === 'addSelectedScript' && c.args[0] === scriptId,
|
||||
);
|
||||
return call !== undefined;
|
||||
}
|
||||
|
||||
public isScriptRemoved(scriptId: string): boolean {
|
||||
const call = this.callHistory.find(
|
||||
(c) => c.methodName === 'removeSelectedScript' && c.args[0] === scriptId,
|
||||
);
|
||||
return call !== undefined;
|
||||
}
|
||||
|
||||
public areAllSelected(): boolean {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
public isAnySelected(): boolean {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
public removeAllInCategory(): void {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
public addOrUpdateAllInCategory(): void {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
public addSelectedScript(scriptId: string, revert: boolean): void {
|
||||
this.registerMethodCall({
|
||||
methodName: 'addSelectedScript',
|
||||
args: [scriptId, revert],
|
||||
});
|
||||
}
|
||||
|
||||
public addOrUpdateSelectedScript(): void {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
public removeSelectedScript(scriptId: string): void {
|
||||
this.registerMethodCall({
|
||||
methodName: 'removeSelectedScript',
|
||||
args: [scriptId],
|
||||
});
|
||||
}
|
||||
|
||||
public selectOnly(scripts: ReadonlyArray<IScript>): void {
|
||||
this.selectedScripts = scripts.map((s) => new SelectedScript(s, false));
|
||||
}
|
||||
|
||||
public isSelected(): boolean {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
public selectAll(): void {
|
||||
this.selectOnly(this.allScripts);
|
||||
}
|
||||
|
||||
public deselectAll(): void {
|
||||
this.selectedScripts = [];
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user