Skip to content

Commit

Permalink
Merge pull request #64 from xmindltd/feat/tree_model
Browse files Browse the repository at this point in the history
  • Loading branch information
danielsss authored Mar 13, 2023
2 parents 74bc84e + 5048736 commit 911cebf
Show file tree
Hide file tree
Showing 10 changed files with 313 additions and 110 deletions.
3 changes: 2 additions & 1 deletion .nycrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
"scripts/",
"**/*.d.ts",
"docs",
"coverage"
"coverage",
"rollup.config.ts"
],
"extension": [".ts"],
"reporter": ["html", "text", "lcov"],
Expand Down
20 changes: 11 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,26 +198,28 @@ In the UI client, you also need to draw the mind map on the sheet.
Set the component to be parent node. If there isn't component ID, the `Central Topic` will become as parent node.
#### .cid(title?) => String
#### .cid(title?, options?: { customId?: string, parentId?: string }) => String
Use .cid to get component ID corresponding to the `title`.
> _!!! NOTE THAT:_ You should avoid duplicating the component `title` if you use `title` to search the component ID.
Use this method to get componentId.
If none of the components has been added, at least `Central Topic`'ID could be returned.
> You should use `customId` or `parentId` for getting the `componentId` if there are some duplicated topic titles.
If you don't specify the title in the period of calling .cid, the last added component ID would be returned.
If you don't specify the title in the period of calling `.cid()`,
the last `componentId` that you've added would be returned.
#### .cids() => {$cid: $title}
That will return all added components.
It will return all the `key/value`s in once.
#### .add(options) => Topic
Add a topic component under parent node.
| Name | Type | Default | Required |
|:----:|:----:|:-------:|:--------:|
| options.title | String | null | Y |
| Name | Type | Default | Required |
|:----:|:----:|:---------------------------------------------------:|:--------:|
| options.title | String | null | Y |
| options.parentId | String | The previous topic that you've operated on | N |
| options.customId | String | It would be useful if you have the same topic title | N |
#### .note(text, del?) => Topic
Expand Down
39 changes: 39 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"ajv": "^6.10.0",
"debug": "^4.1.1",
"jszip": "^3.2.1",
"tree-model": "^1.0.7",
"uuid": "^3.3.2",
"xmind-model": "^1.1.12"
},
Expand Down
117 changes: 117 additions & 0 deletions src/core/base.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import * as TreeModel from 'tree-model';
import { Node } from 'tree-model';

const v4 = require('uuid/v4');
const Debug = require('debug');

Expand All @@ -6,17 +9,104 @@ export interface BaseOptions {
instance?: any;
}

export interface TreeNodeOptions {
id: string;
title: string;
customId?: number | string | null;
parentId?: number | string | null;
}

export interface ConflictedOnDifferentBranchOptions extends Pick<TreeNodeOptions, 'title' | 'parentId'> {}

export interface ConflictedOnSameBranchOptions extends Pick<TreeNodeOptions, 'title' | 'customId'> {}

const DEFAULT_DEBUG_SCOPE = 'xmind-sdk';

export default class Base {
private readonly _debug;

protected tree = new TreeModel();

protected rootNode: Node<TreeNodeOptions>;

/* istanbul ignore next */
constructor(protected options: BaseOptions = <BaseOptions>{}) {
this.options = options;
this._debug = Debug(this.options.debug || DEFAULT_DEBUG_SCOPE);
}

protected isValidComponentId(componentId: string): boolean {
if (!componentId || typeof componentId !== 'string') {
return false;
}
const node = this.rootNode.first((node: Node<TreeNodeOptions>) => node.model.id === componentId);
return !!node;
}

protected setRoot(options: TreeNodeOptions) {
this.rootNode = this.tree.parse(Object.assign(options, { children: [] }));
return this;
}

protected destroyNode(options: Pick<TreeNodeOptions, 'id'>): boolean {
const node = this.rootNode.first((node: Node<TreeNodeOptions>) => {
return node.model.id === options.id;
});
node.drop();
return true;
}

protected addChildNode(options: TreeNodeOptions) {
const node = this.rootNode.first((node: Node<TreeNodeOptions>) => {
return node.model.id === options.parentId;
});
node.addChild(this.tree.parse(options));
}

protected exist(componentId: string): boolean {
const n = this.rootNode.first((node: Node<TreeNodeOptions>) => {
return node.model.id === componentId;
});
return !!n;

}

protected findComponentIdBy(title: string): string | null {
const n = this.rootNode.first((node: Node<TreeNodeOptions>) => {
return node.model.title === title;
});
if (!n) return null;
return n.model.id;
}

protected all() {
const nodes = this.rootNode.all(() => true);
const map = {};
nodes.forEach(node => {
map[String(node.model.id)] = node.model.title;
});
return map;
}

/**
*
* @param { ConflictedOnDifferentBranchOptions | ConflictedOnSameBranchOptions } options
* @return { String | Null }
*/
protected getConflictedComponentId(
options: ConflictedOnDifferentBranchOptions & ConflictedOnSameBranchOptions
): string | null {
if (options.title && options.parentId) {
return this.different(options);
}

if (options.title && options.customId) {
return this.identical(options);
}

return null;
}

/**
* @description Print debug information
* @param {Array} args - the rest arguments
Expand All @@ -31,4 +121,31 @@ export default class Base {
get id(): string {
return v4();
}

private different(options: ConflictedOnDifferentBranchOptions): string | null {
const finder = (node: Node<TreeNodeOptions>) => {
return node.model.title === options.title;
};
for (const node of this.rootNode.all(finder)) {
if (node.parent.model.id === options.parentId) {
return node.model.id;
}
}

return null;
}

private identical(options: ConflictedOnSameBranchOptions) {
const finder = (node: Node<TreeNodeOptions>) => {
return node.model.title === options.title &&
node.model.customId === options.customId;
};

const nodes = this.rootNode.all(finder);
if (nodes.length <= 0) {
return null;
}

return nodes[0].model.id;
}
}
Loading

0 comments on commit 911cebf

Please sign in to comment.