aboutsummaryrefslogtreecommitdiff
path: root/vfs/vfs.ts
diff options
context:
space:
mode:
Diffstat (limited to 'vfs/vfs.ts')
-rw-r--r--vfs/vfs.ts238
1 files changed, 238 insertions, 0 deletions
diff --git a/vfs/vfs.ts b/vfs/vfs.ts
new file mode 100644
index 0000000..7219c3a
--- /dev/null
+++ b/vfs/vfs.ts
@@ -0,0 +1,238 @@
+export interface IVfsNode {
+ name: string
+ path: string
+ is_dir: boolean
+
+ mod_time_epoch?: number
+ mod_time_rfc3339?: string
+ size?: number
+ mode?: string
+ childs?: IVfsNode[]
+ content?: string
+}
+
+export interface Response {
+ code: number
+ message?: string
+ data?: IVfsNode | IPathNode
+}
+
+export interface VfsOptions {
+ id: string
+ is_editable: boolean
+
+ ListNodes: () => Response
+ GetNode: (path: string) => Response
+ UpdateNode: (node: Node) => Response
+ DeleteNode: (node: Node) => Response
+}
+
+export class Vfs {
+ private el: HTMLElement | null = null
+ private comPath!: VfsPath
+ private comList!: VfsList
+ private pathNode: PathNode = {}
+
+ constructor(public opts: VfsOptions) {
+ this.el = document.getElementById(opts.id)
+ if (!this.el) {
+ console.log("Vfs: element id", opts.id, "not found")
+ return
+ }
+
+ let res = this.opts.ListNodes()
+ if (res.code != 200) {
+ console.log("Vfs: ListNodes: ", res.message)
+ return
+ }
+
+ let resPathNode = res.data as IPathNode
+ for (const key in resPathNode) {
+ const value = resPathNode[key] as IVfsNode
+ const node = new VfsNode(value, (node: VfsNode) => {
+ this.onClickNode(node)
+ })
+ this.pathNode[key] = node
+ }
+
+ this.el.innerHTML = ""
+
+ this.comPath = new VfsPath((path: string) => {
+ this.onClickPath(path)
+ })
+ this.el.appendChild(this.comPath.el)
+
+ this.comList = new VfsList()
+ this.el.appendChild(this.comList.el)
+
+ this.open(this.pathNode["/"])
+ }
+
+ onClickNode(this: Vfs, node: VfsNode) {
+ if (!node.is_dir) {
+ const res = this.opts.GetNode(node.path)
+ console.log("GetNode: ", res)
+ return
+ }
+ this.comPath.open(node)
+ this.comList.open(node)
+ }
+
+ onClickPath(this: Vfs, path: string) {
+ const node = this.pathNode[path]
+ if (!node) {
+ console.log("Vfs: onClickPath: invalid path: ", path)
+ return
+ }
+ this.open(node)
+ }
+
+ open(node: VfsNode) {
+ this.comPath.open(node)
+ this.comList.open(node)
+ }
+}
+
+class VfsNode implements IVfsNode {
+ path: string
+ name: string
+ mod_time_epoch: number
+ mod_time_rfc3339: string
+ size: number
+ mode: string
+ is_dir: boolean
+ childs: VfsNode[]
+
+ el: HTMLElement
+
+ constructor(opts: IVfsNode, onClick: NodeClickHandler) {
+ this.path = opts.path || ""
+ this.name = opts.name || ""
+ this.mod_time_epoch = opts.mod_time_epoch || 0
+ this.mod_time_rfc3339 = opts.mod_time_rfc3339 || ""
+ this.size = opts.size || 0
+ this.mode = opts.mode || ""
+ this.is_dir = opts.is_dir || false
+
+ this.childs = []
+ if (opts.childs !== undefined) {
+ for (let c of opts.childs) {
+ this.childs.push(new VfsNode(c, onClick))
+ }
+ }
+
+ this.el = document.createElement("div")
+ this.el.style.padding = "1em"
+ this.el.style.cursor = "pointer"
+ this.el.innerHTML = this.name
+
+ this.el.onclick = (event) => {
+ onClick(this)
+ }
+ this.el.onmouseout = (event) => {
+ this.onMouseOut(this)
+ }
+ this.el.onmouseover = (event) => {
+ this.onMouseOver(this)
+ }
+ }
+
+ onMouseOut(t: VfsNode) {
+ t.el.style.backgroundColor = "white"
+ }
+ onMouseOver(t: VfsNode) {
+ t.el.style.backgroundColor = "aliceblue"
+ }
+}
+
+class VfsList {
+ el: HTMLElement
+
+ constructor() {
+ this.el = document.createElement("div")
+ this.el.style.borderWidth = "1px"
+ this.el.style.borderStyle = "solid"
+ this.el.style.borderColor = "black"
+ }
+
+ open(node: VfsNode) {
+ this.el.innerHTML = ""
+
+ if (node.childs === undefined) {
+ return
+ }
+
+ for (let c of node.childs) {
+ this.el.appendChild(c.el)
+ }
+ }
+}
+
+class VfsPath {
+ el: HTMLElement
+ private crumbs: string[]
+ private onClick: PathClickHandler
+
+ constructor(onClick: PathClickHandler) {
+ this.el = document.createElement("div")
+ this.el.style.borderWidth = "1px"
+ this.el.style.borderStyle = "solid"
+ this.el.style.borderColor = "black"
+ this.crumbs = []
+ this.onClick = onClick
+ }
+
+ open(node: VfsNode) {
+ this.el.innerHTML = ""
+ this.crumbs = []
+ let paths = []
+
+ if (node.path == "/") {
+ paths.push(node.path)
+ } else {
+ paths = node.path.split("/")
+ }
+
+ for (let p of paths) {
+ if (p == "") {
+ p = "/"
+ }
+
+ let crumb = document.createElement("span")
+ crumb.style.display = "inline-block"
+ crumb.style.padding = "1em"
+ crumb.style.cursor = "pointer"
+ crumb.innerHTML = p
+
+ crumb.onclick = (event) => {
+ this.onClick(p)
+ }
+ crumb.onmouseout = (event) => {
+ this.onMouseOut(crumb, event)
+ }
+ crumb.onmouseover = (event) => {
+ this.onMouseOver(crumb, event)
+ }
+
+ this.el.appendChild(crumb)
+ }
+ }
+
+ onMouseOut(crumb: HTMLElement, event: MouseEvent) {
+ crumb.style.backgroundColor = "white"
+ }
+ onMouseOver(crumb: HTMLElement, event: MouseEvent) {
+ crumb.style.backgroundColor = "aliceblue"
+ }
+}
+
+type IPathNode = {
+ [key: string]: IVfsNode
+}
+
+type PathNode = {
+ [key: string]: VfsNode
+}
+
+type NodeClickHandler = (node: VfsNode) => void
+type PathClickHandler = (path: string) => void