Introduce new TreeView UI component
Key highlights: - Written from scratch to cater specifically to privacy.sexy's needs and requirements. - The visual look mimics the previous component with minimal changes, but its internal code is completely rewritten. - Lays groundwork for future functionalities like the "expand all" button a flat view mode as discussed in #158. - Facilitates the transition to Vue 3 by omitting the Vue 2.0 dependent `liquour-tree` as part of #230. Improvements and features: - Caching for quicker node queries. - Gradual rendering of nodes that introduces a noticable boost in performance, particularly during search/filtering. - `TreeView` solely governs the check states of branch nodes. Changes: - Keyboard interactions now alter the background color to highlight the focused item. Previously, it was changing the color of the text. - Better state management with clear separation of concerns: - `TreeView` exclusively manages indeterminate states. - `TreeView` solely governs the check states of branch nodes. - Introduce transaction pattern to update state in batches to minimize amount of events handled. - Improve keyboard focus, style background instead of foreground. Use hover/touch color on keyboard focus. - `SelectableTree` has been removed. Instead, `TreeView` is now directly integrated with `ScriptsTree`. - `ScriptsTree` has been refactored to incorporate hooks for clearer code and separation of duties. - Adopt Vue-idiomatic bindings instead of keeping a reference of the tree component. - Simplify and change filter event management. - Abandon global styles in favor of class-scoped styles. - Use global mixins with descriptive names to clarify indended functionality.
This commit is contained in:
@@ -11,14 +11,14 @@ import { CategoryCollectionStateStub } from './CategoryCollectionStateStub';
|
||||
import { EventSubscriptionCollectionStub } from './EventSubscriptionCollectionStub';
|
||||
import { ApplicationContextStub } from './ApplicationContextStub';
|
||||
import { UserFilterStub } from './UserFilterStub';
|
||||
import { StubWithObservableMethodCalls } from './StubWithObservableMethodCalls';
|
||||
|
||||
export class UseCollectionStateStub {
|
||||
export class UseCollectionStateStub
|
||||
extends StubWithObservableMethodCalls<ReturnType<typeof useCollectionState>> {
|
||||
private currentContext: IApplicationContext = new ApplicationContextStub();
|
||||
|
||||
private readonly currentState = ref<ICategoryCollectionState>(new CategoryCollectionStateStub());
|
||||
|
||||
private readonly onStateChangeHandlers = new Array<NewStateEventHandler>();
|
||||
|
||||
public withFilter(filter: IUserFilter) {
|
||||
const state = new CategoryCollectionStateStub()
|
||||
.withFilter(filter);
|
||||
@@ -49,10 +49,18 @@ export class UseCollectionStateStub {
|
||||
return this.currentState.value;
|
||||
}
|
||||
|
||||
public triggerOnStateChange(newState: ICategoryCollectionState): void {
|
||||
this.currentState.value = newState;
|
||||
this.onStateChangeHandlers.forEach(
|
||||
(handler) => handler(newState, undefined),
|
||||
public triggerOnStateChange(scenario: {
|
||||
readonly newState: ICategoryCollectionState,
|
||||
readonly immediateOnly: boolean,
|
||||
}): void {
|
||||
this.currentState.value = scenario.newState;
|
||||
let calls = this.callHistory.filter((call) => call.methodName === 'onStateChange');
|
||||
if (scenario.immediateOnly) {
|
||||
calls = calls.filter((call) => call.args[1].immediate === true);
|
||||
}
|
||||
const handlers = calls.map((call) => call.args[0] as NewStateEventHandler);
|
||||
handlers.forEach(
|
||||
(handler) => handler(scenario.newState, undefined),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -63,15 +71,26 @@ export class UseCollectionStateStub {
|
||||
if (settings?.immediate) {
|
||||
handler(this.currentState.value, undefined);
|
||||
}
|
||||
this.onStateChangeHandlers.push(handler);
|
||||
this.registerMethodCall({
|
||||
methodName: 'onStateChange',
|
||||
args: [handler, settings],
|
||||
});
|
||||
}
|
||||
|
||||
private modifyCurrentState(mutator: StateModifier) {
|
||||
mutator(this.currentState.value);
|
||||
this.registerMethodCall({
|
||||
methodName: 'modifyCurrentState',
|
||||
args: [mutator],
|
||||
});
|
||||
}
|
||||
|
||||
private modifyCurrentContext(mutator: ContextModifier) {
|
||||
mutator(this.currentContext);
|
||||
this.registerMethodCall({
|
||||
methodName: 'modifyCurrentContext',
|
||||
args: [mutator],
|
||||
});
|
||||
}
|
||||
|
||||
public get(): ReturnType<typeof useCollectionState> {
|
||||
|
||||
Reference in New Issue
Block a user