Skip to content

Commit

Permalink
change api names and update readme
Browse files Browse the repository at this point in the history
  • Loading branch information
artemnih committed Mar 1, 2024
1 parent c5901ad commit a7d466d
Show file tree
Hide file tree
Showing 12 changed files with 114 additions and 122 deletions.
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,18 @@ See list of available components [here](docs/COMPONENTS.md)
@import 'ngx-explorer/src/assets/icons/css/nxe.css';
```

## APIs
All the communication with the server is done through the `ExplorerService` APIs. It provides methods for fetching data, creating, renaming, deleting files and directories.
```Typescript
import { ExplorerService } from 'ngx-explorer';
...
constructor(private explorerService: ExplorerService) {
explorerService.root$.subscribe((root) => {
console.log('Root:', root);
});
}
```

## Customization

See [Customization](docs/CUSTOMIZATION.md) for more details.
Expand Down
85 changes: 2 additions & 83 deletions projects/ngx-explorer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,90 +3,9 @@
Lightweight and easy-to-use Angular File Explorer module.
This is a front-end implementation only. There are no services at this point.

[DEMO](https://artemnih.github.io/ngx-explorer/)

## Prerequisites

- Angular 17+

## How to use

- Install package

```bash
npm i ngx-explorer
```

- Implement `IDataService` provider interface which contains API for fetching data from the server.

```Typescript
import { IDataService } from 'ngx-explorer';

export class MyDataService implements IDataService<MyNodeType> {
...
}
```

And provide the implementation:

```
{ provide: DataService, useClass: ExampleDataService },
```

### Standalone usage:

```Typescript
import { ExplorerComponent } from 'ngx-explorer';

@Component({
selector: 'my-component',
templateUrl: './my.component.html',
styleUrls: ['./my.component.scss'],
standalone: true,
imports: [ExplorerComponent],

// if you want to provide inside the component instead of main.ts or mnodule
// providers: [
// { provide: DataService, useClass: MyDataService }
// ]
})
export class MyComponent {
```
You may also provide `DataService` in `main.ts` instead.
### Non-Standalone usage:
```Typescript
import { ExplorerComponent, DataService } from 'ngx-explorer';

@NgModule({
imports: [
...
ExplorerComponent
],
providers: [
{ provide: DataService, useClass: MyDataService }
]
})
export class AppModule { }
```
### Template and styles
- Add tag to the template:
```html
<h1>My Component</h1>
<div>
<nxe-explorer></nxe-explorer>
</div>
```
- Add css import `styles.scss`:
```scss
@import '~ngx-explorer/src/assets/icons/css/nxe.css';
```
![explorer](docs/ss.png)
## How to use this
[More information can be found here](https://github.com/artemnih/ngx-explorer)
2 changes: 1 addition & 1 deletion projects/ngx-explorer/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ngx-explorer",
"version": "4.1.1",
"version": "4.2.0",
"peerDependencies": {
"@angular/common": "^17.2.0",
"@angular/core": "^17.2.0"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ export class BaseView implements OnDestroy {
@Inject(NAME_FUNCTION) protected getName: (node: INode) => string
) {
this.subs.add(
this.explorerService.openedNode.subscribe((nodes) => {
this.explorerService.openedDir$.subscribe((nodes) => {
this.items = nodes ? nodes.children : [];
})
);

this.subs.add(
this.explorerService.selectedNodes.subscribe((nodes) => {
this.explorerService.selection$.subscribe((nodes) => {
this.selection.clear();
if (nodes) {
this.selection = new Set(nodes.map((n) => n.id));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ interface Breadcrumb {
imports: [AsyncPipe],
})
export class BreadcrumbsComponent {
public breadcrumbs$ = this.explorerService.openedNode.pipe(
public breadcrumbs$ = this.explorerService.openedDir$.pipe(
map((n) => {
if (!n) {
return [];
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<div class="nxe-menu-bar">
<div class="nxe-menu-bar-left">
<button class="nxe-menu-bar-button" (click)="createFolder()"><i class="nxe-folder" aria-hidden="true"></i>New Folder</button>
<button class="nxe-menu-bar-button" (click)="createDir()"><i class="nxe-folder" aria-hidden="true"></i>New Folder</button>
<button class="nxe-menu-bar-button" (click)="refresh()"><i class="nxe-arrows-cw" aria-hidden="true"></i> Refresh</button>
<button class="nxe-menu-bar-button" (click)="openUploader()"><i class="nxe-upload" aria-hidden="true"></i> Upload</button>
<button class="nxe-menu-bar-button" [disabled]="!(canDownload$ | async)" (click)="download()">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,17 @@ import { AsyncPipe } from '@angular/common';
export class MenuBarComponent {
@ViewChild('uploader', { static: true }) uploader!: ElementRef;

protected canDownload$ = this.explorerService.selectedNodes.pipe(map((n) => n.length === 1 && n[0].isLeaf));
protected canDelete$ = this.explorerService.selectedNodes.pipe(map((n) => n.length > 0));
protected canRename$ = this.explorerService.selectedNodes.pipe(map((n) => n.length === 1));
protected canDownload$ = this.explorerService.selection$.pipe(map((n) => n.length === 1 && n[0].isLeaf));
protected canDelete$ = this.explorerService.selection$.pipe(map((n) => n.length > 0));
protected canRename$ = this.explorerService.selection$.pipe(map((n) => n.length === 1));

constructor(
private explorerService: ExplorerService,
@Inject(NAME_FUNCTION) private getName: (node: INode) => string
) {}

createFolder() {
const name = prompt('Enter new folder name');
createDir() {
const name = prompt('Enter new name');
if (name) {
this.explorerService.createDir(name);
}
Expand All @@ -38,7 +38,7 @@ export class MenuBarComponent {
}

rename() {
this.explorerService.selectedNodes
this.explorerService.selection$
.pipe(
take(1),
map((n) => n[0])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,14 @@ export class TreeComponent implements OnDestroy {
@Inject(NAME_FUNCTION) protected getName: (node: INode) => string
) {
this.sub.add(
this.explorerService.tree.pipe(filter((x) => !!x)).subscribe((root) => {
this.explorerService.root$.pipe(filter((x) => !!x)).subscribe((root) => {
this.expnadedIds.add(root.id); // always expand root
this.treeNodes = this.buildTree(root).children;
})
);

this.sub.add(
this.explorerService.openedNode.pipe(filter((x) => !!x)).subscribe((node) => {
this.explorerService.openedDir$.pipe(filter((x) => !!x)).subscribe((node) => {
this.selectedId = node!.id;
})
);
Expand Down
89 changes: 70 additions & 19 deletions projects/ngx-explorer/src/lib/services/explorer.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,27 @@ export class ExplorerService {
private internalTree = Utils.createNode();
private flatPointers: Dictionary<INode> = { [this.internalTree.id]: this.internalTree };

private readonly selectedNodes$ = new BehaviorSubject<INode[]>([]);
private readonly openedNode$ = new BehaviorSubject<INode | undefined>(undefined);
private readonly tree$ = new BehaviorSubject<INode>(this.internalTree);

public readonly selectedNodes = this.selectedNodes$.asObservable();
public readonly openedNode = this.openedNode$.asObservable();
public readonly tree = this.tree$.asObservable();
private readonly selectedNodes$$ = new BehaviorSubject<INode[]>([]);
private readonly openedNode$$ = new BehaviorSubject<INode | undefined>(undefined);
private readonly root$$ = new BehaviorSubject<INode>(this.internalTree);

/**
* An Observable that emits the currently selected nodes in the explorer.
* Subscribers can use this to react to changes in the selection.
*/
public readonly selection$ = this.selectedNodes$$.asObservable();

/**
* An Observable that emits the currently opened directory in the explorer.
* Subscribers can use this to react to changes in the opened directory.
*/
public readonly openedDir$ = this.openedNode$$.asObservable();

/**
* An Observable that emits the root node of the explorer.
* Subscribers can use this to react to changes in the root node.
*/
public readonly root$ = this.root$$.asObservable();

constructor(
private dataService: DataService,
Expand All @@ -34,39 +48,66 @@ export class ExplorerService {
}
}

/**
* Returns the node with the given id.
* @param id The id of the node to retrieve.
*/
public getNode(id: number) {
return this.flatPointers[id];
}

/**
* Sets the selected nodes in the explorer.
* @param nodes The nodes to select.
*/
public select(nodes: INode[]) {
this.selectedNodes$.next(nodes);
this.selectedNodes$$.next(nodes);
}

/**
* Opens the node with the given id.
* @param id The id of the node to open.
*/
public openNode(id: number) {
this.getContent(id).subscribe(() => {
const parent = this.flatPointers[id];
this.openedNode$.next(parent);
this.selectedNodes$.next([]);
this.openedNode$$.next(parent);
this.selectedNodes$$.next([]);
});
}

/**
* Expands the node with the given id.
* @param id The id of the node to expand.
*/
public expand(id: number) {
this.getContent(id).subscribe();
}

/**
* Creates a new directory with the given name in the currently opened directory.
* @param name The name of the new directory.
*/
public createDir(name: string) {
const parent = this.openedNode$.value;
const parent = this.openedNode$$.value;
this.dataService.createDir(parent!.data, name).subscribe(() => {
this.refresh();
});
}

/**
* Refreshes the currently opened node.
*/
public refresh() {
this.openNode(this.openedNode$.value!.id);
this.openNode(this.openedNode$$.value!.id);
}

/**
* Renames the currently selected node.
* @param name The new name for the node.
*/
public rename(name: string) {
const nodes = this.selectedNodes$.value;
const nodes = this.selectedNodes$$.value;
if (nodes.length > 1) {
throw new Error('Multiple selection rename not supported');
}
Expand All @@ -80,8 +121,11 @@ export class ExplorerService {
});
}

/**
* Removes the currently selected nodes.
*/
public remove() {
const selection = this.selectedNodes$.value;
const selection = this.selectedNodes$$.value;
if (selection.length === 0) {
throw new Error('Nothing selected to remove');
}
Expand All @@ -92,15 +136,22 @@ export class ExplorerService {
});
}

/**
* Uploads the given files to the currently opened directory.
* @param files The files to upload.
*/
public upload(files: FileList) {
const node = this.openedNode$.value!;
const node = this.openedNode$$.value!;
this.dataService.uploadFiles(node.data, files).subscribe(() => {
this.refresh();
});
}

/**
* Downloads the currently selected file.
*/
public download() {
const target = this.selectedNodes$.value[0];
const target = this.selectedNodes$$.value[0];
this.dataService.downloadFile(target.data).subscribe(() => {
this.refresh();
});
Expand All @@ -119,9 +170,9 @@ export class ExplorerService {

return this.dataService.getContent(parent.data).pipe(
tap(({ files, dirs }) => {
const newFolderNodes = dirs.map((data) => Utils.createNode(id, false, data));
const newDirNodes = dirs.map((data) => Utils.createNode(id, false, data));
const newFileNodes = files.map((data) => Utils.createNode(id, true, data));
const newChildren = newFolderNodes.concat(newFileNodes);
const newChildren = newDirNodes.concat(newFileNodes);
const added = newChildren.filter((c) => !parent.children.find((o) => Utils.compareObjects(o.data, c.data)));
const removed = parent.children.filter((o) => !newChildren.find((c) => Utils.compareObjects(o.data, c.data)));

Expand All @@ -140,7 +191,7 @@ export class ExplorerService {
const leafChildren = parent.children.filter((c) => c.isLeaf);
parent.children = nodeChildren.concat(leafChildren);

this.tree$.next(this.internalTree);
this.root$$.next(this.internalTree);
})
);
}
Expand Down
8 changes: 6 additions & 2 deletions src/app/app.component.html
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
<div class="demo-main">
<div class="header">ngx-explorer example</div>
<div class="header">
<div>Opened directoy name: {{ openedDir$ | async }}</div>
<div>Selection length: {{ selectionLength$ | async }}</div>
<div>Root children length: {{ rootLen$ | async }}</div>
</div>
<div class="demo-container">
<div class="inner">
<nxe-explorer></nxe-explorer>
</div>
<!-- <nxe-content></nxe-content> -->
<!-- <nxe-tree></nxe-tree> -->
</div>
</div>
</div>
4 changes: 1 addition & 3 deletions src/app/app.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,8 @@
background-color: #215366;
align-items: center;
justify-content: center;
padding-left: 1%;
font-size: 1.5rem;
padding: 10px;
color: white;
display: flex;
}

.demo-container {
Expand Down
Loading

0 comments on commit a7d466d

Please sign in to comment.