Refactor executable IDs to use strings #262

This commit unifies executable ID structure across categories and
scripts, paving the way for more complex ID solutions for #262.
It also refactors related code to adapt to the changes.

Key changes:

- Change numeric IDs to string IDs for categories
- Use named types for string IDs to improve code clarity
- Add unit tests to verify ID uniqueness

Other supporting changes:

- Separate concerns in entities for data access and executables by using
  separate abstractions (`Identifiable` and `RepositoryEntity`)
- Simplify usage and construction of entities.
- Remove `BaseEntity` for simplicity.
- Move creation of categories/scripts to domain layer
- Refactor CategoryCollection for better validation logic isolation
- Rename some categories to keep the names (used as pseudo-IDs) unique
  on Windows.
This commit is contained in:
undergroundwires
2024-08-03 16:54:14 +02:00
parent 6fbc81675f
commit ded55a66d6
124 changed files with 2286 additions and 1331 deletions

View File

@@ -1,7 +1,7 @@
import type { Script } from '@/domain/Executables/Script/Script';
import { RecommendationLevel } from '@/domain/Executables/Script/RecommendationLevel';
import { scrambledEqual } from '@/application/Common/Array';
import type { ICategoryCollection } from '@/domain/ICategoryCollection';
import type { ICategoryCollection } from '@/domain/Collection/ICategoryCollection';
import type { ReadonlyScriptSelection, ScriptSelection } from '@/application/Context/State/Selection/Script/ScriptSelection';
import type { SelectedScript } from '@/application/Context/State/Selection/Script/SelectedScript';
import { RecommendationStatusType } from './RecommendationStatusType';
@@ -99,6 +99,6 @@ function areAllSelected(
if (expectedScripts.length < selectedScriptIds.length) {
return false;
}
const expectedScriptIds = expectedScripts.map((script) => script.id);
const expectedScriptIds = expectedScripts.map((script) => script.executableId);
return scrambledEqual(selectedScriptIds, expectedScriptIds);
}

View File

@@ -90,7 +90,7 @@ import {
} from 'vue';
import { injectKey } from '@/presentation/injectionSymbols';
import TooltipWrapper from '@/presentation/components/Shared/TooltipWrapper.vue';
import type { ICategoryCollection } from '@/domain/ICategoryCollection';
import type { ICategoryCollection } from '@/domain/Collection/ICategoryCollection';
import MenuOptionList from '../MenuOptionList.vue';
import MenuOptionListItem from '../MenuOptionListItem.vue';
import { setCurrentRecommendationStatus, getCurrentRecommendationStatus } from './RecommendationStatusHandler';

View File

@@ -44,6 +44,7 @@ import {
} from 'vue';
import { injectKey } from '@/presentation/injectionSymbols';
import SizeObserver from '@/presentation/components/Shared/SizeObserver.vue';
import type { ExecutableId } from '@/domain/Executables/Identifiable';
import { hasDirective } from './NonCollapsingDirective';
import CardListItem from './CardListItem.vue';
@@ -58,12 +59,12 @@ export default defineComponent({
const width = ref<number | undefined>();
const categoryIds = computed<readonly number[]>(
() => currentState.value.collection.actions.map((category) => category.id),
const categoryIds = computed<readonly ExecutableId[]>(
() => currentState.value.collection.actions.map((category) => category.executableId),
);
const activeCategoryId = ref<number | undefined>(undefined);
const activeCategoryId = ref<ExecutableId | undefined>(undefined);
function onSelected(categoryId: number, isExpanded: boolean) {
function onSelected(categoryId: ExecutableId, isExpanded: boolean) {
activeCategoryId.value = isExpanded ? categoryId : undefined;
}

View File

@@ -56,12 +56,14 @@
<script lang="ts">
import {
defineComponent, computed, shallowRef,
type PropType,
} from 'vue';
import AppIcon from '@/presentation/components/Shared/Icon/AppIcon.vue';
import FlatButton from '@/presentation/components/Shared/FlatButton.vue';
import { injectKey } from '@/presentation/injectionSymbols';
import ScriptsTree from '@/presentation/components/Scripts/View/Tree/ScriptsTree.vue';
import { sleep } from '@/infrastructure/Threading/AsyncSleep';
import type { ExecutableId } from '@/domain/Executables/Identifiable';
import CardSelectionIndicator from './CardSelectionIndicator.vue';
import CardExpandTransition from './CardExpandTransition.vue';
import CardExpansionArrow from './CardExpansionArrow.vue';
@@ -77,11 +79,11 @@ export default defineComponent({
},
props: {
categoryId: {
type: Number,
type: String as PropType<ExecutableId>,
required: true,
},
activeCategoryId: {
type: Number,
type: String as PropType<ExecutableId>,
default: undefined,
},
},

View File

@@ -12,11 +12,12 @@
</template>
<script lang="ts">
import { defineComponent, computed } from 'vue';
import { defineComponent, computed, type PropType } from 'vue';
import AppIcon from '@/presentation/components/Shared/Icon/AppIcon.vue';
import { injectKey } from '@/presentation/injectionSymbols';
import type { Category } from '@/domain/Executables/Category/Category';
import type { ICategoryCollection } from '@/domain/ICategoryCollection';
import type { ICategoryCollection } from '@/domain/Collection/ICategoryCollection';
import type { ExecutableId } from '@/domain/Executables/Identifiable';
export default defineComponent({
components: {
@@ -24,7 +25,7 @@ export default defineComponent({
},
props: {
categoryId: {
type: Number,
type: String as PropType<ExecutableId>,
required: true,
},
},

View File

@@ -1,10 +1,12 @@
import type { ExecutableId } from '@/domain/Executables/Identifiable';
export enum NodeType {
Script,
Category,
}
export interface NodeMetadata {
readonly id: string;
readonly executableId: ExecutableId;
readonly text: string;
readonly isReversible: boolean;
readonly docs: ReadonlyArray<string>;

View File

@@ -12,7 +12,7 @@ import {
} from 'vue';
import { injectKey } from '@/presentation/injectionSymbols';
import type { NodeMetadata } from '@/presentation/components/Scripts/View/Tree/NodeContent/NodeMetadata';
import type { ICategoryCollection } from '@/domain/ICategoryCollection';
import type { ICategoryCollection } from '@/domain/Collection/ICategoryCollection';
import { getReverter } from './Reverter/ReverterFactory';
import ToggleSwitch from './ToggleSwitch.vue';
import type { Reverter } from './Reverter/Reverter';

View File

@@ -1,17 +1,19 @@
import type { UserSelection } from '@/application/Context/State/Selection/UserSelection';
import type { ICategoryCollection } from '@/domain/ICategoryCollection';
import type { ICategoryCollection } from '@/domain/Collection/ICategoryCollection';
import type { SelectedScript } from '@/application/Context/State/Selection/Script/SelectedScript';
import { getCategoryId } from '../../TreeViewAdapter/CategoryNodeMetadataConverter';
import type { ExecutableId } from '@/domain/Executables/Identifiable';
import { createExecutableIdFromNodeId } from '../../TreeViewAdapter/CategoryNodeMetadataConverter';
import { ScriptReverter } from './ScriptReverter';
import type { Reverter } from './Reverter';
import type { TreeNodeId } from '../../TreeView/Node/TreeNode';
export class CategoryReverter implements Reverter {
private readonly categoryId: number;
private readonly categoryId: ExecutableId;
private readonly scriptReverters: ReadonlyArray<ScriptReverter>;
constructor(nodeId: string, collection: ICategoryCollection) {
this.categoryId = getCategoryId(nodeId);
constructor(nodeId: TreeNodeId, collection: ICategoryCollection) {
this.categoryId = createExecutableIdFromNodeId(nodeId);
this.scriptReverters = createScriptReverters(this.categoryId, collection);
}
@@ -37,12 +39,12 @@ export class CategoryReverter implements Reverter {
}
function createScriptReverters(
categoryId: number,
categoryId: ExecutableId,
collection: ICategoryCollection,
): ScriptReverter[] {
const category = collection.getCategory(categoryId);
const scripts = category
.getAllScriptsRecursively()
.filter((script) => script.canRevert());
return scripts.map((script) => new ScriptReverter(script.id));
return scripts.map((script) => new ScriptReverter(script.executableId));
}

View File

@@ -1,15 +1,18 @@
import type { ICategoryCollection } from '@/domain/ICategoryCollection';
import type { ICategoryCollection } from '@/domain/Collection/ICategoryCollection';
import { type NodeMetadata, NodeType } from '../NodeMetadata';
import { ScriptReverter } from './ScriptReverter';
import { CategoryReverter } from './CategoryReverter';
import type { Reverter } from './Reverter';
export function getReverter(node: NodeMetadata, collection: ICategoryCollection): Reverter {
export function getReverter(
node: NodeMetadata,
collection: ICategoryCollection,
): Reverter {
switch (node.type) {
case NodeType.Category:
return new CategoryReverter(node.id, collection);
return new CategoryReverter(node.executableId, collection);
case NodeType.Script:
return new ScriptReverter(node.id);
return new ScriptReverter(node.executableId);
default:
throw new Error('Unknown script type');
}

View File

@@ -1,13 +1,15 @@
import type { UserSelection } from '@/application/Context/State/Selection/UserSelection';
import type { SelectedScript } from '@/application/Context/State/Selection/Script/SelectedScript';
import { getScriptId } from '../../TreeViewAdapter/CategoryNodeMetadataConverter';
import type { ExecutableId } from '@/domain/Executables/Identifiable';
import { createExecutableIdFromNodeId } from '../../TreeViewAdapter/CategoryNodeMetadataConverter';
import type { Reverter } from './Reverter';
import type { TreeNodeId } from '../../TreeView/Node/TreeNode';
export class ScriptReverter implements Reverter {
private readonly scriptId: string;
private readonly scriptId: ExecutableId;
constructor(nodeId: string) {
this.scriptId = getScriptId(nodeId);
constructor(nodeId: TreeNodeId) {
this.scriptId = createExecutableIdFromNodeId(nodeId);
}
public getState(selectedScripts: ReadonlyArray<SelectedScript>): boolean {

View File

@@ -24,8 +24,9 @@
</template>
<script lang="ts">
import { defineComponent, toRef } from 'vue';
import { defineComponent, toRef, type PropType } from 'vue';
import { injectKey } from '@/presentation/injectionSymbols';
import type { ExecutableId } from '@/domain/Executables/Identifiable';
import TreeView from './TreeView/TreeView.vue';
import NodeContent from './NodeContent/NodeContent.vue';
import { useTreeViewFilterEvent } from './TreeViewAdapter/UseTreeViewFilterEvent';
@@ -41,7 +42,7 @@ export default defineComponent({
},
props: {
categoryId: {
type: [Number],
type: String as PropType<ExecutableId>,
default: undefined,
},
hasTopPadding: {

View File

@@ -1,5 +1,7 @@
export type TreeInputNodeDataId = string;
export interface TreeInputNodeData {
readonly id: string;
readonly id: TreeInputNodeDataId;
readonly children?: readonly TreeInputNodeData[];
readonly parent?: TreeInputNodeData | null;
readonly data?: object;

View File

@@ -56,7 +56,7 @@ import { useNodeState } from './UseNodeState';
import LeafTreeNode from './LeafTreeNode.vue';
import InteractableNode from './InteractableNode.vue';
import type { TreeRoot } from '../TreeRoot/TreeRoot';
import type { TreeNode } from './TreeNode';
import type { TreeNode, TreeNodeId } from './TreeNode';
import type { NodeRenderingStrategy } from '../Rendering/Scheduling/NodeRenderingStrategy';
import type { PropType } from 'vue';
@@ -69,7 +69,7 @@ export default defineComponent({
},
props: {
nodeId: {
type: String,
type: String as PropType<TreeNodeId>,
required: true,
},
treeRoot: {

View File

@@ -18,13 +18,13 @@ import { useCurrentTreeNodes } from '../UseCurrentTreeNodes';
import { useNodeState } from './UseNodeState';
import { useKeyboardInteractionState } from './UseKeyboardInteractionState';
import type { TreeRoot } from '../TreeRoot/TreeRoot';
import type { TreeNode } from './TreeNode';
import type { TreeNode, TreeNodeId } from './TreeNode';
import type { PropType } from 'vue';
export default defineComponent({
props: {
nodeId: {
type: String,
type: String as PropType<TreeNodeId>,
required: true,
},
treeRoot: {

View File

@@ -28,7 +28,7 @@ import { defineComponent, computed, toRef } from 'vue';
import { useCurrentTreeNodes } from '../UseCurrentTreeNodes';
import NodeCheckbox from './NodeCheckbox.vue';
import InteractableNode from './InteractableNode.vue';
import type { TreeNode } from './TreeNode';
import type { TreeNode, TreeNodeId } from './TreeNode';
import type { TreeRoot } from '../TreeRoot/TreeRoot';
import type { PropType } from 'vue';
@@ -39,7 +39,7 @@ export default defineComponent({
},
props: {
nodeId: {
type: String,
type: String as PropType<TreeNodeId>,
required: true,
},
treeRoot: {

View File

@@ -14,13 +14,13 @@ import { useCurrentTreeNodes } from '../UseCurrentTreeNodes';
import { useNodeState } from './UseNodeState';
import { TreeNodeCheckState } from './State/CheckState';
import type { TreeRoot } from '../TreeRoot/TreeRoot';
import type { TreeNode } from './TreeNode';
import type { TreeNode, TreeNodeId } from './TreeNode';
import type { PropType } from 'vue';
export default defineComponent({
props: {
nodeId: {
type: String,
type: String as PropType<TreeNodeId>,
required: true,
},
treeRoot: {

View File

@@ -1,8 +1,10 @@
import type { HierarchyAccess, HierarchyReader } from './Hierarchy/HierarchyAccess';
import type { TreeNodeStateAccess, TreeNodeStateReader } from './State/StateAccess';
export type TreeNodeId = string;
export interface ReadOnlyTreeNode {
readonly id: string;
readonly id: TreeNodeId;
readonly state: TreeNodeStateReader;
readonly hierarchy: HierarchyReader;
readonly metadata?: object;

View File

@@ -1,6 +1,6 @@
import { TreeNodeHierarchy } from './Hierarchy/TreeNodeHierarchy';
import { TreeNodeState } from './State/TreeNodeState';
import type { TreeNode } from './TreeNode';
import type { TreeNode, TreeNodeId } from './TreeNode';
import type { TreeNodeStateAccess } from './State/StateAccess';
import type { HierarchyAccess } from './Hierarchy/HierarchyAccess';
@@ -9,7 +9,7 @@ export class TreeNodeManager implements TreeNode {
public readonly hierarchy: HierarchyAccess;
constructor(public readonly id: string, public readonly metadata?: object) {
constructor(public readonly id: TreeNodeId, public readonly metadata?: object) {
if (!id) {
throw new Error('missing id');
}

View File

@@ -1,15 +1,15 @@
import type { ReadOnlyTreeNode, TreeNode } from '../../../Node/TreeNode';
import type { ReadOnlyTreeNode, TreeNode, TreeNodeId } from '../../../Node/TreeNode';
export interface ReadOnlyQueryableNodes {
readonly rootNodes: readonly ReadOnlyTreeNode[];
readonly flattenedNodes: readonly ReadOnlyTreeNode[];
getNodeById(id: string): ReadOnlyTreeNode;
getNodeById(nodeId: TreeNodeId): ReadOnlyTreeNode;
}
export interface QueryableNodes extends ReadOnlyQueryableNodes {
readonly rootNodes: readonly TreeNode[];
readonly flattenedNodes: readonly TreeNode[];
getNodeById(id: string): TreeNode;
getNodeById(nodeId: TreeNodeId): TreeNode;
}

View File

@@ -1,5 +1,5 @@
import type { QueryableNodes } from './QueryableNodes';
import type { TreeNode } from '../../../Node/TreeNode';
import type { TreeNode, TreeNodeId } from '../../../Node/TreeNode';
export class TreeNodeNavigator implements QueryableNodes {
public readonly flattenedNodes: readonly TreeNode[];
@@ -8,10 +8,10 @@ export class TreeNodeNavigator implements QueryableNodes {
this.flattenedNodes = flattenNodes(rootNodes);
}
public getNodeById(id: string): TreeNode {
const foundNode = this.flattenedNodes.find((node) => node.id === id);
public getNodeById(nodeId: TreeNodeId): TreeNode {
const foundNode = this.flattenedNodes.find((node) => node.id === nodeId);
if (!foundNode) {
throw new Error(`Node could not be found: ${id}`);
throw new Error(`Node could not be found: ${nodeId}`);
}
return foundNode;
}

View File

@@ -22,6 +22,7 @@ import {
} from 'vue';
import HierarchicalTreeNode from '../Node/HierarchicalTreeNode.vue';
import { useCurrentTreeNodes } from '../UseCurrentTreeNodes';
import { type TreeNodeId } from '../Node/TreeNode';
import type { NodeRenderingStrategy } from '../Rendering/Scheduling/NodeRenderingStrategy';
import type { TreeRoot } from './TreeRoot';
import type { PropType } from 'vue';
@@ -43,7 +44,7 @@ export default defineComponent({
setup(props) {
const { nodes } = useCurrentTreeNodes(toRef(props, 'treeRoot'));
const renderedNodeIds = computed<string[]>(() => {
const renderedNodeIds = computed<TreeNodeId[]>(() => {
return nodes
.value
.rootNodes

View File

@@ -26,6 +26,7 @@ import { useLeafNodeCheckedStateUpdater } from './UseLeafNodeCheckedStateUpdater
import { useAutoUpdateParentCheckState } from './UseAutoUpdateParentCheckState';
import { useAutoUpdateChildrenCheckState } from './UseAutoUpdateChildrenCheckState';
import { useGradualNodeRendering, type NodeRenderingControl } from './Rendering/UseGradualNodeRendering';
import { type TreeNodeId } from './Node/TreeNode';
import type { TreeNodeStateChangedEmittedEvent } from './Bindings/TreeNodeStateChangedEmittedEvent';
import type { TreeInputNodeData } from './Bindings/TreeInputNodeData';
import type { TreeViewFilterEvent } from './Bindings/TreeInputFilterEvent';
@@ -45,7 +46,7 @@ export default defineComponent({
default: () => undefined,
},
selectedLeafNodeIds: {
type: Array as PropType<ReadonlyArray<string>>,
type: Array as PropType<ReadonlyArray<TreeNodeId>>,
default: () => [],
},
},

View File

@@ -1,14 +1,17 @@
import type { Category } from '@/domain/Executables/Category/Category';
import type { ICategoryCollection } from '@/domain/ICategoryCollection';
import type { ICategoryCollection } from '@/domain/Collection/ICategoryCollection';
import type { Script } from '@/domain/Executables/Script/Script';
import type { ExecutableId } from '@/domain/Executables/Identifiable';
import type { Executable } from '@/domain/Executables/Executable';
import { type NodeMetadata, NodeType } from '../NodeContent/NodeMetadata';
import type { TreeNodeId } from '../TreeView/Node/TreeNode';
export function parseAllCategories(collection: ICategoryCollection): NodeMetadata[] {
return createCategoryNodes(collection.actions);
}
export function parseSingleCategory(
categoryId: number,
categoryId: ExecutableId,
collection: ICategoryCollection,
): NodeMetadata[] {
const category = collection.getCategory(categoryId);
@@ -16,27 +19,19 @@ export function parseSingleCategory(
return tree;
}
export function getScriptNodeId(script: Script): string {
return script.id;
export function createNodeIdForExecutable(executable: Executable): TreeNodeId {
return executable.executableId;
}
export function getScriptId(nodeId: string): string {
export function createExecutableIdFromNodeId(nodeId: TreeNodeId): ExecutableId {
return nodeId;
}
export function getCategoryId(nodeId: string): number {
return +nodeId;
}
export function getCategoryNodeId(category: Category): string {
return `${category.id}`;
}
function parseCategoryRecursively(
parentCategory: Category,
): NodeMetadata[] {
return [
...createCategoryNodes(parentCategory.subCategories),
...createCategoryNodes(parentCategory.subcategories),
...createScriptNodes(parentCategory.scripts),
];
}
@@ -57,7 +52,7 @@ function convertCategoryToNode(
children: readonly NodeMetadata[],
): NodeMetadata {
return {
id: getCategoryNodeId(category),
executableId: createNodeIdForExecutable(category),
type: NodeType.Category,
text: category.name,
children,
@@ -68,7 +63,7 @@ function convertCategoryToNode(
function convertScriptToNode(script: Script): NodeMetadata {
return {
id: getScriptNodeId(script),
executableId: createNodeIdForExecutable(script),
type: NodeType.Script,
text: script.name,
children: [],

View File

@@ -14,7 +14,7 @@ export function getNodeMetadata(
export function convertToNodeInput(metadata: NodeMetadata): TreeInputNodeData {
return {
id: metadata.id,
id: metadata.executableId,
children: convertChildren(metadata.children, convertToNodeInput),
data: metadata,
};

View File

@@ -2,20 +2,21 @@ import {
computed, shallowReadonly,
} from 'vue';
import type { useUserSelectionState } from '@/presentation/components/Shared/Hooks/UseUserSelectionState';
import { getScriptNodeId } from './CategoryNodeMetadataConverter';
import { createNodeIdForExecutable } from './CategoryNodeMetadataConverter';
import type { TreeNodeId } from '../TreeView/Node/TreeNode';
export function useSelectedScriptNodeIds(
useSelectionStateHook: ReturnType<typeof useUserSelectionState>,
scriptNodeIdParser = getScriptNodeId,
convertToNodeId = createNodeIdForExecutable,
) {
const { currentSelection } = useSelectionStateHook;
const selectedNodeIds = computed<readonly string[]>(() => {
const selectedNodeIds = computed<readonly TreeNodeId[]>(() => {
return currentSelection
.value
.scripts
.selectedScripts
.map((selected) => scriptNodeIdParser(selected.script));
.map((selected) => convertToNodeId(selected.script));
});
return {

View File

@@ -1,16 +1,15 @@
import {
type Ref, shallowReadonly, shallowRef,
} from 'vue';
import type { Script } from '@/domain/Executables/Script/Script';
import type { Category } from '@/domain/Executables/Category/Category';
import { injectKey } from '@/presentation/injectionSymbols';
import type { ReadonlyFilterContext } from '@/application/Context/State/Filter/FilterContext';
import type { FilterResult } from '@/application/Context/State/Filter/Result/FilterResult';
import type { ExecutableId } from '@/domain/Executables/Identifiable';
import type { Executable } from '@/domain/Executables/Executable';
import { type TreeViewFilterEvent, createFilterRemovedEvent, createFilterTriggeredEvent } from '../TreeView/Bindings/TreeInputFilterEvent';
import { getNodeMetadata } from './TreeNodeMetadataConverter';
import { getCategoryNodeId, getScriptNodeId } from './CategoryNodeMetadataConverter';
import type { NodeMetadata } from '../NodeContent/NodeMetadata';
import type { ReadOnlyTreeNode } from '../TreeView/Node/TreeNode';
import { createExecutableIdFromNodeId } from './CategoryNodeMetadataConverter';
import type { ReadOnlyTreeNode, TreeNodeId } from '../TreeView/Node/TreeNode';
type TreeNodeFilterResultPredicate = (
node: ReadOnlyTreeNode,
@@ -24,7 +23,7 @@ export function useTreeViewFilterEvent() {
const latestFilterEvent = shallowRef<TreeViewFilterEvent | undefined>(undefined);
const treeNodePredicate: TreeNodeFilterResultPredicate = (node, filterResult) => filterMatches(
getNodeMetadata(node),
node.id,
filterResult,
);
@@ -71,15 +70,17 @@ function createFilterEvent(
);
}
function filterMatches(node: NodeMetadata, filter: FilterResult): boolean {
return containsScript(node, filter.scriptMatches)
|| containsCategory(node, filter.categoryMatches);
function filterMatches(nodeId: TreeNodeId, filter: FilterResult): boolean {
const executableId = createExecutableIdFromNodeId(nodeId);
return containsExecutable(executableId, filter.scriptMatches)
|| containsExecutable(executableId, filter.categoryMatches);
}
function containsScript(expected: NodeMetadata, scripts: readonly Script[]) {
return scripts.some((existing: Script) => expected.id === getScriptNodeId(existing));
}
function containsCategory(expected: NodeMetadata, categories: readonly Category[]) {
return categories.some((existing: Category) => expected.id === getCategoryNodeId(existing));
function containsExecutable(
expectedId: ExecutableId,
executables: readonly Executable[],
): boolean {
return executables.some(
(existing: Category) => existing.executableId === expectedId,
);
}

View File

@@ -1,15 +1,16 @@
import {
type Ref, computed, shallowReadonly,
} from 'vue';
import type { ICategoryCollection } from '@/domain/ICategoryCollection';
import type { ICategoryCollection } from '@/domain/Collection/ICategoryCollection';
import { injectKey } from '@/presentation/injectionSymbols';
import type { ExecutableId } from '@/domain/Executables/Identifiable';
import { parseSingleCategory, parseAllCategories } from './CategoryNodeMetadataConverter';
import { convertToNodeInput } from './TreeNodeMetadataConverter';
import type { TreeInputNodeData } from '../TreeView/Bindings/TreeInputNodeData';
import type { NodeMetadata } from '../NodeContent/NodeMetadata';
export function useTreeViewNodeInput(
categoryIdRef: Readonly<Ref<number | undefined>>,
categoryIdRef: Readonly<Ref<ExecutableId | undefined>>,
parser: CategoryNodeParser = {
parseSingle: parseSingleCategory,
parseAll: parseAllCategories,
@@ -30,7 +31,7 @@ export function useTreeViewNodeInput(
}
function parseNodes(
categoryId: number | undefined,
categoryId: ExecutableId | undefined,
categoryCollection: ICategoryCollection,
parser: CategoryNodeParser,
): NodeMetadata[] {