├── docs ├── jsdoc │ ├── lint.json │ ├── image │ │ ├── search.png │ │ ├── esdoc-logo-mini.png │ │ ├── esdoc-logo-mini-black.png │ │ ├── badge.svg │ │ └── manual-badge.svg │ ├── script │ │ ├── patch-for-local.js │ │ ├── manual.js │ │ ├── pretty-print.js │ │ ├── inherited-summary.js │ │ ├── inner-link.js │ │ ├── test-summary.js │ │ ├── search.js │ │ ├── search_index.js │ │ └── prettify │ │ │ ├── Apache-License-2.0.txt │ │ │ └── prettify.js │ ├── coverage.json │ ├── css │ │ ├── identifiers.css │ │ ├── source.css │ │ ├── test.css │ │ ├── github.css │ │ ├── search.css │ │ ├── prettify-tomorrow.css │ │ ├── manual.css │ │ └── style.css │ ├── badge.svg │ ├── source.html │ ├── index.html │ ├── identifiers.html │ ├── typedef │ │ └── index.html │ ├── file │ │ └── src │ │ │ ├── defaults.js.html │ │ │ ├── icons.js.html │ │ │ ├── utils.js.html │ │ │ └── tree.js.html │ ├── variable │ │ └── index.html │ └── ast │ │ └── source │ │ └── icons.js.json ├── index.html ├── index.css └── code.js ├── .gitignore ├── example.api.json ├── images ├── tree.afdesign ├── partial.svg ├── open.svg └── closed.svg ├── scripts ├── cleanDocs.js ├── images.js ├── live.js └── compile.js ├── .esdoc.json ├── src ├── indicator.js ├── defaults.js ├── icons.js ├── utils.js ├── input.js └── tree.js ├── package.json ├── LICENSE └── README.md /docs/jsdoc/lint.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /example.api.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "", 3 | "type": "RootDoc" 4 | } -------------------------------------------------------------------------------- /images/tree.afdesign: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidfig/tree/HEAD/images/tree.afdesign -------------------------------------------------------------------------------- /docs/jsdoc/image/search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidfig/tree/HEAD/docs/jsdoc/image/search.png -------------------------------------------------------------------------------- /docs/jsdoc/image/esdoc-logo-mini.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidfig/tree/HEAD/docs/jsdoc/image/esdoc-logo-mini.png -------------------------------------------------------------------------------- /docs/jsdoc/image/esdoc-logo-mini-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidfig/tree/HEAD/docs/jsdoc/image/esdoc-logo-mini-black.png -------------------------------------------------------------------------------- /scripts/cleanDocs.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs-extra') 2 | 3 | const dir = 'docs/jsdoc' 4 | 5 | async function clean() { 6 | console.log(`Cleaning director ${dir}...`) 7 | await fs.emptyDir(dir) 8 | process.exit(0) 9 | } 10 | 11 | clean() -------------------------------------------------------------------------------- /docs/jsdoc/script/patch-for-local.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | if (location.protocol === 'file:') { 3 | var elms = document.querySelectorAll('a[href="./"]'); 4 | for (var i = 0; i < elms.length; i++) { 5 | elms[i].href = './index.html'; 6 | } 7 | } 8 | })(); 9 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
Example for 8 | github.com/davidfig/tree/ 9 |
10 |
11 | -------------------------------------------------------------------------------- /.esdoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "source": "./src", 3 | "includes": [ 4 | "tree.js" 5 | ], 6 | "destination": "./docs/jsdoc", 7 | "plugins": [ 8 | { 9 | "name": "esdoc-standard-plugin", 10 | "option": { 11 | "accessor": { 12 | "access": [ 13 | "public" 14 | ] 15 | } 16 | } 17 | } 18 | ] 19 | } -------------------------------------------------------------------------------- /scripts/images.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs-extra') 2 | 3 | let s = 'export const icons={' 4 | 5 | const files = ['closed', 'open'] 6 | 7 | async function convert() { 8 | for (let file of files) { 9 | console.log(`Copying ${file}...`) 10 | const svg = await fs.readFile(`images/${file}.svg`) 11 | s += `${file}:'${svg}',` 12 | } 13 | s = s.substr(0, s.length - 1) + '}' 14 | await fs.writeFile('./src/icons.js', s) 15 | process.exit() 16 | } 17 | 18 | convert() -------------------------------------------------------------------------------- /docs/jsdoc/script/manual.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | var matched = location.pathname.match(/\/(manual\/.*\.html)$/); 3 | if (!matched) return; 4 | 5 | var currentName = matched[1]; 6 | var cssClass = '.navigation .manual-toc li[data-link="' + currentName + '"]'; 7 | var styleText = cssClass + '{ display: block; }\n'; 8 | styleText += cssClass + '.indent-h1 a { color: #039BE5 }'; 9 | var style = document.createElement('style'); 10 | style.textContent = styleText; 11 | document.querySelector('head').appendChild(style); 12 | })(); 13 | -------------------------------------------------------------------------------- /docs/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | .title { 7 | margin-top: 1%; 8 | margin-left: 15%; 9 | font-size: 200%; 10 | } 11 | 12 | #code { 13 | left: 15%; 14 | width: 80%; 15 | position: absolute; 16 | word-wrap: break-word; 17 | z-index: -1; 18 | } 19 | 20 | .hljs { 21 | display: block; 22 | overflow-x: auto; 23 | padding: 0.5em; 24 | background: rgba(0, 0, 100, 0.02); 25 | } 26 | 27 | .hljs-comment { 28 | color: blue; 29 | } 30 | 31 | .font { 32 | font-family: 'Trebuchet MS', 'Lucida Sans Unicode', 'Lucida Grande', 'Lucida Sans', Arial, sans-serif; 33 | font-size: 1.25em; 34 | } -------------------------------------------------------------------------------- /images/partial.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /scripts/live.js: -------------------------------------------------------------------------------- 1 | const port = 1235 2 | let first = true 3 | 4 | function createSocket() { 5 | const socket = new WebSocket(`ws://localhost:${port}`) 6 | socket.addEventListener('message', () => window.location.reload()) 7 | socket.addEventListener('close', () => { 8 | setTimeout(createSocket, 0) 9 | }) 10 | socket.addEventListener('open', () => { 11 | if (!first) { 12 | window.location.reload() 13 | } else { 14 | first = false 15 | } 16 | }) 17 | } 18 | 19 | window.addEventListener('load', () => { 20 | createSocket() 21 | console.log(`Live reload initialized on port ${port}...`) 22 | }) -------------------------------------------------------------------------------- /images/open.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/jsdoc/coverage.json: -------------------------------------------------------------------------------- 1 | { 2 | "coverage": "45.65%", 3 | "expectCount": 46, 4 | "actualCount": 21, 5 | "files": { 6 | "src/tree.js": { 7 | "expectCount": 46, 8 | "actualCount": 21, 9 | "undocumentLines": [ 10 | 9, 11 | 41, 12 | 42, 13 | 70, 14 | 80, 15 | 94, 16 | 109, 17 | 122, 18 | 133, 19 | 144, 20 | 155, 21 | 159, 22 | 189, 23 | 200, 24 | 207, 25 | 219, 26 | 231, 27 | 320, 28 | 321, 29 | 343, 30 | 346, 31 | 392, 32 | 399, 33 | 406, 34 | 414 35 | ] 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /images/closed.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/jsdoc/css/identifiers.css: -------------------------------------------------------------------------------- 1 | .identifiers-wrap { 2 | display: flex; 3 | align-items: flex-start; 4 | } 5 | 6 | .identifier-dir-tree { 7 | background: #fff; 8 | border: solid 1px #ddd; 9 | border-radius: 0.25em; 10 | top: 52px; 11 | position: -webkit-sticky; 12 | position: sticky; 13 | max-height: calc(100vh - 155px); 14 | overflow-y: scroll; 15 | min-width: 200px; 16 | margin-left: 1em; 17 | } 18 | 19 | .identifier-dir-tree-header { 20 | padding: 0.5em; 21 | background-color: #fafafa; 22 | border-bottom: solid 1px #ddd; 23 | } 24 | 25 | .identifier-dir-tree-content { 26 | padding: 0 0.5em 0; 27 | } 28 | 29 | .identifier-dir-tree-content > div { 30 | padding-top: 0.25em; 31 | padding-bottom: 0.25em; 32 | } 33 | 34 | .identifier-dir-tree-content a { 35 | color: inherit; 36 | } 37 | 38 | -------------------------------------------------------------------------------- /docs/jsdoc/badge.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | document 13 | document 14 | 45% 15 | 45% 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/indicator.js: -------------------------------------------------------------------------------- 1 | import * as utils from './utils' 2 | 3 | export class Indicator { 4 | constructor(tree) { 5 | this._indicator = utils.html() 6 | this._indicator.style.marginLeft = tree.indentation + 'px' 7 | const content = utils.html({ parent: this._indicator }) 8 | content.style.display = 'flex' 9 | this._indicator.indentation = utils.html({ parent: content }) 10 | this._indicator.icon = utils.html({ parent: content, className: `${tree.prefixClassName}-expand` }) 11 | this._indicator.icon.style.height = 0 12 | this._indicator.line = utils.html({ parent: content, className: `${tree.prefixClassName}-indicator` }) 13 | } 14 | 15 | get() { 16 | return this._indicator 17 | } 18 | 19 | set _marginLeft(value) { 20 | this._indicator.style.marginLeft = value + 'px' 21 | } 22 | } -------------------------------------------------------------------------------- /docs/jsdoc/image/badge.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | document 13 | document 14 | @ratio@ 15 | @ratio@ 16 | 17 | 18 | -------------------------------------------------------------------------------- /docs/jsdoc/image/manual-badge.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | manual 13 | manual 14 | @value@ 15 | @value@ 16 | 17 | 18 | -------------------------------------------------------------------------------- /docs/jsdoc/script/pretty-print.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | prettyPrint(); 3 | var lines = document.querySelectorAll('.prettyprint.linenums li[class^="L"]'); 4 | for (var i = 0; i < lines.length; i++) { 5 | lines[i].id = 'lineNumber' + (i + 1); 6 | } 7 | 8 | var matched = location.hash.match(/errorLines=([\d,]+)/); 9 | if (matched) { 10 | var lines = matched[1].split(','); 11 | for (var i = 0; i < lines.length; i++) { 12 | var id = '#lineNumber' + lines[i]; 13 | var el = document.querySelector(id); 14 | el.classList.add('error-line'); 15 | } 16 | return; 17 | } 18 | 19 | if (location.hash) { 20 | // ``[ ] . ' " @`` are not valid in DOM id. so must escape these. 21 | var id = location.hash.replace(/([\[\].'"@$])/g, '\\$1'); 22 | var line = document.querySelector(id); 23 | if (line) line.classList.add('active'); 24 | } 25 | })(); 26 | -------------------------------------------------------------------------------- /docs/code.js: -------------------------------------------------------------------------------- 1 | import forkMe from 'fork-me-github' 2 | 3 | import { Tree } from '../src/tree' 4 | 5 | const data = { 6 | children: [ 7 | { name: 'fruits', children: [ 8 | { name: 'apples', children: [] }, 9 | { name: 'oranges', children: [ 10 | { name: 'tangerines', children: [] }, 11 | { name: 'mandarins', children: [] }, 12 | { name: 'pomelo', children: [] }, 13 | { name: 'blood orange', children: [] }, 14 | ] } 15 | ]}, 16 | { name: 'vegetables', children: [ 17 | { name: 'brocolli', children: [] }, 18 | ] }, 19 | ] 20 | } 21 | 22 | function test() { 23 | const tree = new Tree(data, { parent: document.body }) 24 | tree.expandAll() 25 | } 26 | 27 | window.onload = function () { 28 | test() 29 | forkMe('https://github.com/davidfig/tree') 30 | } -------------------------------------------------------------------------------- /docs/jsdoc/script/inherited-summary.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | function toggle(ev) { 3 | var button = ev.target; 4 | var parent = ev.target.parentElement; 5 | while(parent) { 6 | if (parent.tagName === 'TABLE' && parent.classList.contains('summary')) break; 7 | parent = parent.parentElement; 8 | } 9 | 10 | if (!parent) return; 11 | 12 | var tbody = parent.querySelector('tbody'); 13 | if (button.classList.contains('opened')) { 14 | button.classList.remove('opened'); 15 | button.classList.add('closed'); 16 | tbody.style.display = 'none'; 17 | } else { 18 | button.classList.remove('closed'); 19 | button.classList.add('opened'); 20 | tbody.style.display = 'block'; 21 | } 22 | } 23 | 24 | var buttons = document.querySelectorAll('.inherited-summary thead .toggle'); 25 | for (var i = 0; i < buttons.length; i++) { 26 | buttons[i].addEventListener('click', toggle); 27 | } 28 | })(); 29 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yy-tree", 3 | "version": "0.6.2", 4 | "description": "vanilla drag-and-drop UI tree", 5 | "module": "src/tree.js", 6 | "scripts": { 7 | "start": "node scripts/compile", 8 | "build-demo": "node scripts/compile --production", 9 | "docs": "esdoc", 10 | "test-docs": "esdoc && serve docs/jsdoc", 11 | "icons": "node scripts/images.js", 12 | "prepublishOnly": "npm run icons && npm run build-demo && npm run docs" 13 | }, 14 | "author": "", 15 | "license": "MIT", 16 | "dependencies": { 17 | "clicked": "^2.0.0", 18 | "eventemitter3": "^3.0.1" 19 | }, 20 | "devDependencies": { 21 | "chokidar": "^3.4.3", 22 | "esbuild": "^0.8.26", 23 | "esdoc": "^1.1.0", 24 | "esdoc-standard-plugin": "^1.0.0", 25 | "express": "^4.17.1", 26 | "fork-me-github": "^1.1.0", 27 | "fs-extra": "^5.0.0", 28 | "n-readlines": "^1.0.1", 29 | "serve": "^11.3.2", 30 | "ws": "^7.4.1" 31 | } 32 | } -------------------------------------------------------------------------------- /src/defaults.js: -------------------------------------------------------------------------------- 1 | export const defaults = { 2 | move: true, 3 | select: true, 4 | indentation: 20, 5 | threshold: 10, 6 | holdTime: 1000, 7 | expandOnClick: true, 8 | dragOpacity: 0.75, 9 | prefixClassName: 'yy-tree', 10 | cursorName: 'grab -webkit-grab pointer', 11 | cursorExpand: 'pointer' 12 | } 13 | 14 | export const styleDefaults = { 15 | nameStyles: { 16 | padding: '0.5em 1em', 17 | margin: '0.1em', 18 | background: 'rgb(230,230,230)', 19 | 'user-select': 'none', 20 | cursor: 'pointer', 21 | width: '100px', 22 | }, 23 | indicatorStyles: { 24 | background: 'rgb(150,150,255)', 25 | height: '5px', 26 | width: '100px', 27 | padding: '0 1em', 28 | }, 29 | contentStyles: { 30 | display: 'flex', 31 | 'align-items': 'center', 32 | }, 33 | expandStyles: { 34 | width: '15px', 35 | height: '15px', 36 | // cursor: 'pointer', 37 | }, 38 | selectStyles: { 39 | background: 'rgb(200, 200, 200)', 40 | }, 41 | } -------------------------------------------------------------------------------- /docs/jsdoc/script/inner-link.js: -------------------------------------------------------------------------------- 1 | // inner link(#foo) can not correctly scroll, because page has fixed header, 2 | // so, I manually scroll. 3 | (function(){ 4 | var matched = location.hash.match(/errorLines=([\d,]+)/); 5 | if (matched) return; 6 | 7 | function adjust() { 8 | window.scrollBy(0, -55); 9 | var el = document.querySelector('.inner-link-active'); 10 | if (el) el.classList.remove('inner-link-active'); 11 | 12 | // ``[ ] . ' " @`` are not valid in DOM id. so must escape these. 13 | var id = location.hash.replace(/([\[\].'"@$])/g, '\\$1'); 14 | var el = document.querySelector(id); 15 | if (el) el.classList.add('inner-link-active'); 16 | } 17 | 18 | window.addEventListener('hashchange', adjust); 19 | 20 | if (location.hash) { 21 | setTimeout(adjust, 0); 22 | } 23 | })(); 24 | 25 | (function(){ 26 | var els = document.querySelectorAll('[href^="#"]'); 27 | var href = location.href.replace(/#.*$/, ''); // remove existed hash 28 | for (var i = 0; i < els.length; i++) { 29 | var el = els[i]; 30 | el.href = href + el.getAttribute('href'); // because el.href is absolute path 31 | } 32 | })(); 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 YOPEY YOPEY LLC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /docs/jsdoc/css/source.css: -------------------------------------------------------------------------------- 1 | table.files-summary { 2 | width: 100%; 3 | margin: 10px 0; 4 | border-spacing: 0; 5 | border: 0; 6 | border-collapse: collapse; 7 | text-align: right; 8 | } 9 | 10 | table.files-summary tbody tr:hover { 11 | background: #eee; 12 | } 13 | 14 | table.files-summary td:first-child, 15 | table.files-summary td:nth-of-type(2) { 16 | text-align: left; 17 | } 18 | 19 | table.files-summary[data-use-coverage="false"] td.coverage { 20 | display: none; 21 | } 22 | 23 | table.files-summary thead { 24 | background: #fafafa; 25 | } 26 | 27 | table.files-summary td { 28 | border: solid 1px #ddd; 29 | padding: 4px 10px; 30 | vertical-align: top; 31 | } 32 | 33 | table.files-summary td.identifiers > span { 34 | display: block; 35 | margin-top: 4px; 36 | } 37 | table.files-summary td.identifiers > span:first-child { 38 | margin-top: 0; 39 | } 40 | 41 | table.files-summary .coverage-count { 42 | font-size: 12px; 43 | color: #aaa; 44 | display: inline-block; 45 | min-width: 40px; 46 | } 47 | 48 | .total-coverage-count { 49 | position: relative; 50 | bottom: 2px; 51 | font-size: 12px; 52 | color: #666; 53 | font-weight: 500; 54 | padding-left: 5px; 55 | } 56 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tree 2 | Vanilla drag-and-drop UI tree 3 | 4 | ## Rationale 5 | I needed a tree components for my tools. Most of the available visual tree APIs require vue or react. And so yy-tree was created. 6 | 7 | ## Super Simple Example 8 | ```js 9 | const data = { 10 | children: [ 11 | { name: 'fruits', children: [ 12 | { name: 'apples', children: [] }, 13 | { name: 'oranges', children: [ 14 | { name: 'tangerines', children: [] }, 15 | { name: 'mandarins', children: [] }, 16 | { name: 'pomelo', children: [] }, 17 | { name: 'blood orange', children: [] }, 18 | ] } 19 | ]}, 20 | { name: 'vegetables', children: [ 21 | { name: 'brocolli', children: [] }, 22 | ] }, 23 | ] 24 | } 25 | 26 | const tree = new Tree(data, { parent: document.body }) 27 | ``` 28 | 29 | ## Live Examples 30 | https://davidfig.github.io/tree/ 31 | 32 | ## API Documentation 33 | https://davidfig.github.io/tree/jsdoc/ 34 | 35 | ## Installation 36 | 37 | npm i yy-tree 38 | 39 | ## license 40 | MIT License 41 | (c) 2021 [YOPEY YOPEY LLC](https://yopeyopey.com/) by [David Figatner](https://twitter.com/yopey_yopey/) 42 | -------------------------------------------------------------------------------- /docs/jsdoc/css/test.css: -------------------------------------------------------------------------------- 1 | table.test-summary thead { 2 | background: #fafafa; 3 | } 4 | 5 | table.test-summary thead .test-description { 6 | width: 50%; 7 | } 8 | 9 | table.test-summary { 10 | width: 100%; 11 | margin: 10px 0; 12 | border-spacing: 0; 13 | border: 0; 14 | border-collapse: collapse; 15 | } 16 | 17 | table.test-summary thead .test-count { 18 | width: 3em; 19 | } 20 | 21 | table.test-summary tbody tr:hover { 22 | background-color: #eee; 23 | } 24 | 25 | table.test-summary td { 26 | border: solid 1px #ddd; 27 | padding: 4px 10px; 28 | vertical-align: top; 29 | } 30 | 31 | table.test-summary td p { 32 | margin: 0; 33 | } 34 | 35 | table.test-summary tr.test-interface .toggle { 36 | display: inline-block; 37 | float: left; 38 | margin-right: 4px; 39 | cursor: pointer; 40 | font-size: 0.8em; 41 | padding-top: 0.25em; 42 | } 43 | 44 | table.test-summary tr.test-interface .toggle.opened:before { 45 | content: '▼'; 46 | } 47 | 48 | table.test-summary tr.test-interface .toggle.closed:before { 49 | content: '▶'; 50 | } 51 | 52 | table.test-summary .test-target > span { 53 | display: block; 54 | margin-top: 4px; 55 | } 56 | table.test-summary .test-target > span:first-child { 57 | margin-top: 0; 58 | } 59 | -------------------------------------------------------------------------------- /src/icons.js: -------------------------------------------------------------------------------- 1 | export const icons={closed:'',open:''} -------------------------------------------------------------------------------- /docs/jsdoc/css/github.css: -------------------------------------------------------------------------------- 1 | /* github markdown */ 2 | .github-markdown { 3 | font-size: 16px; 4 | } 5 | 6 | .github-markdown h1, 7 | .github-markdown h2, 8 | .github-markdown h3, 9 | .github-markdown h4, 10 | .github-markdown h5 { 11 | margin-top: 1em; 12 | margin-bottom: 16px; 13 | font-weight: bold; 14 | padding: 0; 15 | } 16 | 17 | .github-markdown h1:nth-of-type(1) { 18 | margin-top: 0; 19 | } 20 | 21 | .github-markdown h1 { 22 | font-size: 2em; 23 | padding-bottom: 0.3em; 24 | } 25 | 26 | .github-markdown h2 { 27 | font-size: 1.75em; 28 | padding-bottom: 0.3em; 29 | } 30 | 31 | .github-markdown h3 { 32 | font-size: 1.5em; 33 | } 34 | 35 | .github-markdown h4 { 36 | font-size: 1.25em; 37 | } 38 | 39 | .github-markdown h5 { 40 | font-size: 1em; 41 | } 42 | 43 | .github-markdown ul, .github-markdown ol { 44 | padding-left: 2em; 45 | } 46 | 47 | .github-markdown pre > code { 48 | font-size: 0.85em; 49 | } 50 | 51 | .github-markdown table { 52 | margin-bottom: 1em; 53 | border-collapse: collapse; 54 | border-spacing: 0; 55 | } 56 | 57 | .github-markdown table tr { 58 | background-color: #fff; 59 | border-top: 1px solid #ccc; 60 | } 61 | 62 | .github-markdown table th, 63 | .github-markdown table td { 64 | padding: 6px 13px; 65 | border: 1px solid #ddd; 66 | } 67 | 68 | .github-markdown table tr:nth-child(2n) { 69 | background-color: #f8f8f8; 70 | } 71 | 72 | .github-markdown hr { 73 | border-right: 0; 74 | border-bottom: 1px solid #e5e5e5; 75 | border-left: 0; 76 | border-top: 0; 77 | } 78 | 79 | /** badge(.svg) does not have border */ 80 | .github-markdown img:not([src*=".svg"]) { 81 | max-width: 100%; 82 | box-shadow: 1px 1px 1px rgba(0,0,0,0.5); 83 | } 84 | -------------------------------------------------------------------------------- /docs/jsdoc/css/search.css: -------------------------------------------------------------------------------- 1 | /* search box */ 2 | .search-box { 3 | position: absolute; 4 | top: 10px; 5 | right: 50px; 6 | padding-right: 8px; 7 | padding-bottom: 10px; 8 | line-height: normal; 9 | font-size: 12px; 10 | } 11 | 12 | .search-box img { 13 | width: 20px; 14 | vertical-align: top; 15 | } 16 | 17 | .search-input { 18 | display: inline; 19 | visibility: hidden; 20 | width: 0; 21 | padding: 2px; 22 | height: 1.5em; 23 | outline: none; 24 | background: transparent; 25 | border: 1px #0af; 26 | border-style: none none solid none; 27 | vertical-align: bottom; 28 | } 29 | 30 | .search-input-edge { 31 | display: none; 32 | width: 1px; 33 | height: 5px; 34 | background-color: #0af; 35 | vertical-align: bottom; 36 | } 37 | 38 | .search-result { 39 | position: absolute; 40 | display: none; 41 | height: 600px; 42 | width: 100%; 43 | padding: 0; 44 | margin-top: 5px; 45 | margin-left: 24px; 46 | background: white; 47 | box-shadow: 1px 1px 4px rgb(0,0,0); 48 | white-space: nowrap; 49 | overflow-y: scroll; 50 | } 51 | 52 | .search-result-import-path { 53 | color: #aaa; 54 | font-size: 12px; 55 | } 56 | 57 | .search-result li { 58 | list-style: none; 59 | padding: 2px 4px; 60 | } 61 | 62 | .search-result li a { 63 | display: block; 64 | } 65 | 66 | .search-result li.selected { 67 | background: #ddd; 68 | } 69 | 70 | .search-result li.search-separator { 71 | background: rgb(37, 138, 175); 72 | color: white; 73 | } 74 | 75 | .search-box.active .search-input { 76 | visibility: visible; 77 | transition: width 0.2s ease-out; 78 | width: 300px; 79 | } 80 | 81 | .search-box.active .search-input-edge { 82 | display: inline-block; 83 | } 84 | 85 | -------------------------------------------------------------------------------- /docs/jsdoc/script/test-summary.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | function toggle(ev) { 3 | var button = ev.target; 4 | var parent = ev.target.parentElement; 5 | while(parent) { 6 | if (parent.tagName === 'TR' && parent.classList.contains('test-interface')) break; 7 | parent = parent.parentElement; 8 | } 9 | 10 | if (!parent) return; 11 | 12 | var direction; 13 | if (button.classList.contains('opened')) { 14 | button.classList.remove('opened'); 15 | button.classList.add('closed'); 16 | direction = 'closed'; 17 | } else { 18 | button.classList.remove('closed'); 19 | button.classList.add('opened'); 20 | direction = 'opened'; 21 | } 22 | 23 | var targetDepth = parseInt(parent.dataset.testDepth, 10) + 1; 24 | var nextElement = parent.nextElementSibling; 25 | while (nextElement) { 26 | var depth = parseInt(nextElement.dataset.testDepth, 10); 27 | if (depth >= targetDepth) { 28 | if (direction === 'opened') { 29 | if (depth === targetDepth) nextElement.style.display = ''; 30 | } else if (direction === 'closed') { 31 | nextElement.style.display = 'none'; 32 | var innerButton = nextElement.querySelector('.toggle'); 33 | if (innerButton && innerButton.classList.contains('opened')) { 34 | innerButton.classList.remove('opened'); 35 | innerButton.classList.add('closed'); 36 | } 37 | } 38 | } else { 39 | break; 40 | } 41 | nextElement = nextElement.nextElementSibling; 42 | } 43 | } 44 | 45 | var buttons = document.querySelectorAll('.test-summary tr.test-interface .toggle'); 46 | for (var i = 0; i < buttons.length; i++) { 47 | buttons[i].addEventListener('click', toggle); 48 | } 49 | 50 | var topDescribes = document.querySelectorAll('.test-summary tr[data-test-depth="0"]'); 51 | for (var i = 0; i < topDescribes.length; i++) { 52 | topDescribes[i].style.display = ''; 53 | } 54 | })(); 55 | -------------------------------------------------------------------------------- /docs/jsdoc/css/prettify-tomorrow.css: -------------------------------------------------------------------------------- 1 | /* Tomorrow Theme */ 2 | /* Original theme - https://github.com/chriskempson/tomorrow-theme */ 3 | /* Pretty printing styles. Used with prettify.js. */ 4 | /* SPAN elements with the classes below are added by prettyprint. */ 5 | /* plain text */ 6 | .pln { 7 | color: #4d4d4c; } 8 | 9 | @media screen { 10 | /* string content */ 11 | .str { 12 | color: #718c00; } 13 | 14 | /* a keyword */ 15 | .kwd { 16 | color: #8959a8; } 17 | 18 | /* a comment */ 19 | .com { 20 | color: #8e908c; } 21 | 22 | /* a type name */ 23 | .typ { 24 | color: #4271ae; } 25 | 26 | /* a literal value */ 27 | .lit { 28 | color: #f5871f; } 29 | 30 | /* punctuation */ 31 | .pun { 32 | color: #4d4d4c; } 33 | 34 | /* lisp open bracket */ 35 | .opn { 36 | color: #4d4d4c; } 37 | 38 | /* lisp close bracket */ 39 | .clo { 40 | color: #4d4d4c; } 41 | 42 | /* a markup tag name */ 43 | .tag { 44 | color: #c82829; } 45 | 46 | /* a markup attribute name */ 47 | .atn { 48 | color: #f5871f; } 49 | 50 | /* a markup attribute value */ 51 | .atv { 52 | color: #3e999f; } 53 | 54 | /* a declaration */ 55 | .dec { 56 | color: #f5871f; } 57 | 58 | /* a variable name */ 59 | .var { 60 | color: #c82829; } 61 | 62 | /* a function name */ 63 | .fun { 64 | color: #4271ae; } } 65 | /* Use higher contrast and text-weight for printable form. */ 66 | @media print, projection { 67 | .str { 68 | color: #060; } 69 | 70 | .kwd { 71 | color: #006; 72 | font-weight: bold; } 73 | 74 | .com { 75 | color: #600; 76 | font-style: italic; } 77 | 78 | .typ { 79 | color: #404; 80 | font-weight: bold; } 81 | 82 | .lit { 83 | color: #044; } 84 | 85 | .pun, .opn, .clo { 86 | color: #440; } 87 | 88 | .tag { 89 | color: #006; 90 | font-weight: bold; } 91 | 92 | .atn { 93 | color: #404; } 94 | 95 | .atv { 96 | color: #060; } } 97 | /* Style */ 98 | /* 99 | pre.prettyprint { 100 | background: white; 101 | font-family: Consolas, Monaco, 'Andale Mono', monospace; 102 | font-size: 12px; 103 | line-height: 1.5; 104 | border: 1px solid #ccc; 105 | padding: 10px; } 106 | */ 107 | 108 | /* Specify class=linenums on a pre to get line numbering */ 109 | ol.linenums { 110 | margin-top: 0; 111 | margin-bottom: 0; } 112 | 113 | /* IE indents via margin-left */ 114 | li.L0, 115 | li.L1, 116 | li.L2, 117 | li.L3, 118 | li.L4, 119 | li.L5, 120 | li.L6, 121 | li.L7, 122 | li.L8, 123 | li.L9 { 124 | /* */ } 125 | 126 | /* Alternate shading for lines */ 127 | li.L1, 128 | li.L3, 129 | li.L5, 130 | li.L7, 131 | li.L9 { 132 | /* */ } 133 | -------------------------------------------------------------------------------- /docs/jsdoc/css/manual.css: -------------------------------------------------------------------------------- 1 | .github-markdown .manual-toc { 2 | padding-left: 0; 3 | } 4 | 5 | .manual-index .manual-cards { 6 | display: flex; 7 | flex-wrap: wrap; 8 | } 9 | 10 | .manual-index .manual-card-wrap { 11 | width: 280px; 12 | padding: 10px 20px 10px 0; 13 | box-sizing: border-box; 14 | } 15 | 16 | .manual-index .manual-card-wrap > h1 { 17 | margin: 0; 18 | font-size: 1em; 19 | font-weight: 600; 20 | padding: 0.2em 0 0.2em 0.5em; 21 | border-radius: 0.1em 0.1em 0 0; 22 | border: none; 23 | } 24 | 25 | .manual-index .manual-card-wrap > h1 span { 26 | color: #555; 27 | } 28 | 29 | .manual-index .manual-card { 30 | height: 200px; 31 | overflow: hidden; 32 | border: solid 1px rgba(230, 230, 230, 0.84); 33 | border-radius: 0 0 0.1em 0.1em; 34 | padding: 8px; 35 | position: relative; 36 | } 37 | 38 | .manual-index .manual-card > div { 39 | transform: scale(0.4); 40 | transform-origin: 0 0; 41 | width: 250%; 42 | } 43 | 44 | .manual-index .manual-card > a { 45 | position: absolute; 46 | top: 0; 47 | left: 0; 48 | width: 100%; 49 | height: 100%; 50 | background: rgba(210, 210, 210, 0.1); 51 | } 52 | 53 | .manual-index .manual-card > a:hover { 54 | background: none; 55 | } 56 | 57 | .manual-index .manual-badge { 58 | margin: 0; 59 | } 60 | 61 | .manual-index .manual-user-index { 62 | margin-bottom: 1em; 63 | border-bottom: solid 1px #ddd; 64 | } 65 | 66 | .manual-root .navigation { 67 | padding-left: 4px; 68 | margin-top: 4px; 69 | } 70 | 71 | .navigation .manual-toc-root > div { 72 | padding-left: 0.25em; 73 | padding-right: 0.75em; 74 | } 75 | 76 | .github-markdown .manual-toc-title a { 77 | color: inherit; 78 | } 79 | 80 | .manual-breadcrumb-list { 81 | font-size: 0.8em; 82 | margin-bottom: 1em; 83 | } 84 | 85 | .manual-toc-title a:hover { 86 | color: #039BE5; 87 | } 88 | 89 | .manual-toc li { 90 | margin: 0.75em 0; 91 | list-style-type: none; 92 | } 93 | 94 | .navigation .manual-toc [class^="indent-h"] a { 95 | color: #666; 96 | } 97 | 98 | .navigation .manual-toc .indent-h1 a { 99 | color: #555; 100 | font-weight: 600; 101 | display: block; 102 | } 103 | 104 | .manual-toc .indent-h1 { 105 | display: block; 106 | margin: 0.4em 0 0 0.25em; 107 | padding: 0.2em 0 0.2em 0.5em; 108 | border-radius: 0.1em; 109 | } 110 | 111 | .manual-root .navigation .manual-toc li:not(.indent-h1) { 112 | margin-top: 0.5em; 113 | } 114 | 115 | .manual-toc .indent-h2 { 116 | display: none; 117 | margin-left: 1.5em; 118 | } 119 | .manual-toc .indent-h3 { 120 | display: none; 121 | margin-left: 2.5em; 122 | } 123 | .manual-toc .indent-h4 { 124 | display: none; 125 | margin-left: 3.5em; 126 | } 127 | .manual-toc .indent-h5 { 128 | display: none; 129 | margin-left: 4.5em; 130 | } 131 | 132 | .manual-nav li { 133 | margin: 0.75em 0; 134 | } 135 | -------------------------------------------------------------------------------- /docs/jsdoc/source.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Source | yy-tree 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | Home 16 | 17 | Reference 18 | Source 19 | 20 | 27 |
28 | 29 | 37 | 38 |

Source 21/46

39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 |
FileIdentifierDocumentSizeLinesUpdated
src/tree.jsTree45 %21/4615998 byte5132020-12-07 01:09:13 (UTC)
63 |
64 | 65 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /docs/jsdoc/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Home | yy-tree 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | Home 16 | 17 | Reference 18 | Source 19 | 20 | 27 |
28 | 29 | 37 | 38 |

tree

Vanilla drag-and-drop UI tree

39 |

Rationale

I needed a tree components for my tools. Most of the available visual tree APIs require vue or react. And so yy-tree was created.

40 |

Super Simple Example

const data = {
41 |     children: [
42 |         { name: 'fruits', children: [
43 |             { name: 'apples', children: [] },
44 |             { name: 'oranges', children: [
45 |                 { name: 'tangerines', children: [] },
46 |                 { name: 'mandarins', children: [] },
47 |                 { name: 'pomelo', children: [] },
48 |                 { name: 'blood orange', children: [] },
49 |             ] }
50 |         ]},
51 |         { name: 'vegetables', children: [
52 |             { name: 'brocolli', children: [] },
53 |         ] },
54 |     ]
55 | }
56 | 
57 | const tree = new Tree(data, { parent: document.body })
58 | 
59 |

Live Examples

https://davidfig.github.io/tree/

60 |

API Documentation

https://davidfig.github.io/tree/jsdoc/

61 |

Installation

npm i yy-tree
62 | 

license

MIT License 63 | (c) 2021 YOPEY YOPEY LLC by David Figatner

64 |
65 |
66 | 67 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /docs/jsdoc/script/search.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | var searchIndex = window.esdocSearchIndex; 3 | var searchBox = document.querySelector('.search-box'); 4 | var input = document.querySelector('.search-input'); 5 | var result = document.querySelector('.search-result'); 6 | var selectedIndex = -1; 7 | var prevText; 8 | 9 | // active search box and focus when mouse enter on search box. 10 | searchBox.addEventListener('mouseenter', function(){ 11 | searchBox.classList.add('active'); 12 | input.focus(); 13 | }); 14 | 15 | // search with text when key is upped. 16 | input.addEventListener('keyup', function(ev){ 17 | var text = ev.target.value.toLowerCase(); 18 | if (!text) { 19 | result.style.display = 'none'; 20 | result.innerHTML = ''; 21 | return; 22 | } 23 | 24 | if (text === prevText) return; 25 | prevText = text; 26 | 27 | var html = {class: [], method: [], member: [], function: [], variable: [], typedef: [], external: [], file: [], test: [], testFile: []}; 28 | var len = searchIndex.length; 29 | var kind; 30 | for (var i = 0; i < len; i++) { 31 | var pair = searchIndex[i]; 32 | if (pair[0].indexOf(text) !== -1) { 33 | kind = pair[3]; 34 | html[kind].push('
  • ' + pair[2] + '
  • '); 35 | } 36 | } 37 | 38 | var innerHTML = ''; 39 | for (kind in html) { 40 | var list = html[kind]; 41 | if (!list.length) continue; 42 | innerHTML += '
  • ' + kind + '
  • \n' + list.join('\n'); 43 | } 44 | result.innerHTML = innerHTML; 45 | if (innerHTML) result.style.display = 'block'; 46 | selectedIndex = -1; 47 | }); 48 | 49 | // down, up and enter key are pressed, select search result. 50 | input.addEventListener('keydown', function(ev){ 51 | if (ev.keyCode === 40) { 52 | // arrow down 53 | var current = result.children[selectedIndex]; 54 | var selected = result.children[selectedIndex + 1]; 55 | if (selected && selected.classList.contains('search-separator')) { 56 | var selected = result.children[selectedIndex + 2]; 57 | selectedIndex++; 58 | } 59 | 60 | if (selected) { 61 | if (current) current.classList.remove('selected'); 62 | selectedIndex++; 63 | selected.classList.add('selected'); 64 | } 65 | } else if (ev.keyCode === 38) { 66 | // arrow up 67 | var current = result.children[selectedIndex]; 68 | var selected = result.children[selectedIndex - 1]; 69 | if (selected && selected.classList.contains('search-separator')) { 70 | var selected = result.children[selectedIndex - 2]; 71 | selectedIndex--; 72 | } 73 | 74 | if (selected) { 75 | if (current) current.classList.remove('selected'); 76 | selectedIndex--; 77 | selected.classList.add('selected'); 78 | } 79 | } else if (ev.keyCode === 13) { 80 | // enter 81 | var current = result.children[selectedIndex]; 82 | if (current) { 83 | var link = current.querySelector('a'); 84 | if (link) location.href = link.href; 85 | } 86 | } else { 87 | return; 88 | } 89 | 90 | ev.preventDefault(); 91 | }); 92 | 93 | // select search result when search result is mouse over. 94 | result.addEventListener('mousemove', function(ev){ 95 | var current = result.children[selectedIndex]; 96 | if (current) current.classList.remove('selected'); 97 | 98 | var li = ev.target; 99 | while (li) { 100 | if (li.nodeName === 'LI') break; 101 | li = li.parentElement; 102 | } 103 | 104 | if (li) { 105 | selectedIndex = Array.prototype.indexOf.call(result.children, li); 106 | li.classList.add('selected'); 107 | } 108 | }); 109 | 110 | // clear search result when body is clicked. 111 | document.body.addEventListener('click', function(ev){ 112 | selectedIndex = -1; 113 | result.style.display = 'none'; 114 | result.innerHTML = ''; 115 | }); 116 | 117 | })(); 118 | -------------------------------------------------------------------------------- /docs/jsdoc/identifiers.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Reference | yy-tree 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
    15 | Home 16 | 17 | Reference 18 | Source 19 | 20 | 27 |
    28 | 29 | 37 | 38 |

    References

    39 | 40 |
    41 |
    42 | 43 |
    44 | 45 |
    46 | 47 | 48 | 49 | 50 | 57 | 72 | 76 | 77 | 78 | 85 | 100 | 104 | 105 | 106 |
    summary
    51 | public 52 | 53 | 54 | 55 | 56 | 58 |
    59 |

    60 | C 61 | 62 | 63 | Tree 64 |

    65 |
    66 |
    67 | 68 | 69 | 70 |
    71 |
    73 | 74 | 75 |
    79 | public 80 | 81 | 82 | 83 | 84 | 86 |
    87 |

    88 | T 89 | 90 | 91 | Tree~TreeData: Object 92 |

    93 |
    94 |
    95 | 96 | 97 | 98 |
    99 |
    101 | 102 | 103 |
    107 |
    108 |
    109 |
    110 | 111 | 112 |
    113 |
    114 | 115 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | -------------------------------------------------------------------------------- /scripts/compile.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2020 YOPEY YOPEY LLC */ 2 | 3 | const { performance } = require('perf_hooks') 4 | const fs = require('fs-extra') 5 | const readLines = require('n-readlines') 6 | const chokidar = require('chokidar') 7 | const esbuild = require('esbuild').build 8 | const express = require('express') 9 | const WebSocket = require('ws') 10 | 11 | const packageJSON = require('../package.json') 12 | 13 | const port = 1234 14 | const websocketPort = 1235 15 | const linesToShow = 3 16 | 17 | let _wss 18 | 19 | function addZero(s) { 20 | if (s.length !== 2) { 21 | return `0${s}` 22 | } else { 23 | return s 24 | } 25 | } 26 | 27 | function log(s) { 28 | const time = new Date() 29 | let hours = time.getHours() 30 | let pm = false 31 | if (hours > 12) { 32 | hours -= 12 33 | pm = true 34 | } 35 | console.log(`[${hours}:${addZero(time.getMinutes())}${pm ? 'pm' : 'am'}]`, s) 36 | } 37 | 38 | // from https://stackoverflow.com/a/43532829/1955997 39 | function roundTo(value, digits) { 40 | value = value * Math.pow(10, digits) 41 | value = Math.round(value) 42 | value = value / Math.pow(10, digits) 43 | return value 44 | } 45 | 46 | async function build(minify) { 47 | const now = performance.now() 48 | try { 49 | await esbuild({ 50 | entryPoints: ['docs/code.js'], 51 | inject: ['scripts/live.js'], 52 | bundle: true, 53 | outfile: 'docs/index.js', 54 | minify, 55 | sourcemap: minify ? false : 'external', 56 | }) 57 | log(`packaged javascript (${roundTo(performance.now() - now, 2)}ms).`) 58 | } catch(e) { 59 | log('error compiling javascript.') 60 | let s = '' 61 | for (const error of e.errors) { 62 | const lines = new readLines(error.location.file) 63 | let i = 1, line 64 | while (line = lines.next()) { 65 | if (i >= error.location.line - linesToShow && i <= error.location.line + linesToShow) { 66 | if (i === error.location.line) { 67 | let actual = line.toString() 68 | actual = `${actual.substr(0, error.location.column)}` + 69 | `${actual.substr(error.location.column, error.location.length)}` + 70 | actual.substr(error.location.column + error.location.length) 71 | s += `
    ${actual}
    ` 72 | } else { 73 | s += line.toString() + '
    ' 74 | } 75 | } 76 | i++ 77 | } 78 | } 79 | const script = `window.addEventListener('load', () => { 80 | document.body.style.background = 'white' 81 | document.body.style.fontFamily = 'Consolas,monaco,monospace' 82 | document.body.style.margin = '1rem' 83 | document.body.style.width = 'auto' 84 | document.body.style.height = 'auto' 85 | document.body.innerHTML = \`${e.toString().replaceAll('\n', '
    ')}

    ${s}\` 86 | });` + await fs.readFile('scripts/live.js') 87 | await fs.outputFile('docs/index.js', script) 88 | } 89 | } 90 | 91 | async function production() { 92 | await build(true) 93 | log(`${packageJSON.name} v${packageJSON.version} deployed to docs`) 94 | process.exit(0) 95 | } 96 | 97 | function httpServer() { 98 | const app = express() 99 | app.use('/', express.static('docs')) 100 | app.listen(port, () => log(`${packageJSON.name} v${packageJSON.version} running at http://localhost:${port}`)) 101 | } 102 | 103 | function webSocketServer() { 104 | _wss = new WebSocket.Server({ 105 | port: websocketPort, 106 | }) 107 | log(`Live reload socket server running on port ${websocketPort}`) 108 | } 109 | 110 | function signalSockets() { 111 | _wss.clients.forEach(client => { 112 | if (client.readyState === WebSocket.OPEN) { 113 | client.send() 114 | } 115 | }) 116 | } 117 | 118 | async function serve() { 119 | await build() 120 | const jsWatch = chokidar.watch(['docs/code.js', 'src/*.js']) 121 | jsWatch.on('change', async () => { 122 | await build() 123 | signalSockets() 124 | }) 125 | httpServer() 126 | webSocketServer() 127 | } 128 | 129 | if (process.argv[2] === '--production') { 130 | production() 131 | } else { 132 | serve() 133 | } 134 | -------------------------------------------------------------------------------- /docs/jsdoc/typedef/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Typedef | yy-tree 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
    15 | Home 16 | 17 | Reference 18 | Source 19 | 20 | 27 |
    28 | 29 | 37 | 38 |

    Typedef

    39 |
    40 | 41 | 42 | 43 | 44 | 51 | 66 | 70 | 71 | 72 |
    Static Public Summary
    45 | public 46 | 47 | 48 | 49 | 50 | 52 |
    53 |

    54 | 55 | 56 | 57 | Tree~TreeData: Object 58 |

    59 |
    60 |
    61 | 62 | 63 | 64 |
    65 |
    67 | 68 | 69 |
    73 |
    74 |

    Static Public

    75 | 76 |
    77 |

    78 | public 79 | 80 | 81 | 82 | 83 | 84 | Tree~TreeData: Object 85 | 86 | 87 | 88 | source 89 | 90 |

    91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 |
    100 |

    Properties:

    101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 125 | 126 | 127 |
    NameTypeAttributeDescription
    childrenTreeData[]
    namestring
    parentparent
    • optional

    if not provided then will traverse tree to find parent

    124 |
    128 |
    129 |
    130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 |
    148 |
    149 |
    150 | 151 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | /** 2 | * converts a string to an HTMLElement if necessary 3 | * @param {(HTMLElement|string)} element 4 | */ 5 | export function el(element) { 6 | if (typeof element === 'string') { 7 | return document.querySelector(element) 8 | } 9 | return element 10 | 11 | } 12 | 13 | /** 14 | * measure distance between two points 15 | * @param {number} x1 16 | * @param {number} y1 17 | * @param {number} x2 18 | * @param {number} y2 19 | */ 20 | export function distance(x1, y1, x2, y2) { 21 | return Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2)) 22 | } 23 | 24 | /** 25 | * find shortest distance from point to HTMLElement's bounding box 26 | * from: https://gamedev.stackexchange.com/questions/44483/how-do-i-calculate-distance-between-a-point-and-an-axis-aligned-rectangle 27 | * @param {number} x 28 | * @param {number} y 29 | * @param {HTMLElement} element 30 | */ 31 | export function distancePointElement(px, py, element) { 32 | const pos = toGlobal(element) 33 | const width = element.offsetWidth 34 | const height = element.offsetHeight 35 | const x = pos.x + width / 2 36 | const y = pos.y + height / 2 37 | const dx = Math.max(Math.abs(px - x) - width / 2, 0) 38 | const dy = Math.max(Math.abs(py - y) - height / 2, 0) 39 | return dx * dx + dy * dy 40 | } 41 | 42 | /** 43 | * determine whether the mouse is inside an element 44 | * @param {HTMLElement} dragging 45 | * @param {HTMLElement} element 46 | */ 47 | export function inside(x, y, element) { 48 | const pos = toGlobal(element) 49 | const x1 = pos.x 50 | const y1 = pos.y 51 | const w1 = element.offsetWidth 52 | const h1 = element.offsetHeight 53 | return x >= x1 && x <= x1 + w1 && y >= y1 && y <= y1 + h1 54 | } 55 | 56 | /** 57 | * determines global location of a div 58 | * from https://stackoverflow.com/a/26230989/1955997 59 | * @param {HTMLElement} e 60 | * @returns {PointLike} 61 | */ 62 | export function toGlobal(e) { 63 | const box = e.getBoundingClientRect() 64 | 65 | const body = document.body 66 | const docEl = document.documentElement 67 | 68 | const scrollTop = window.pageYOffset || docEl.scrollTop || body.scrollTop 69 | const scrollLeft = window.pageXOffset || docEl.scrollLeft || body.scrollLeft 70 | 71 | const clientTop = docEl.clientTop || body.clientTop || 0 72 | const clientLeft = docEl.clientLeft || body.clientLeft || 0 73 | 74 | const top = box.top + scrollTop - clientTop 75 | const left = box.left + scrollLeft - clientLeft 76 | 77 | return { y: Math.round(top), x: Math.round(left) } 78 | } 79 | 80 | /** 81 | * @typedef {object} PointLike 82 | * @property {number} x 83 | * @property {number} y 84 | */ 85 | 86 | /** 87 | * combines options and default options 88 | * @param {object} options 89 | * @param {object} defaults 90 | * @returns {object} options+defaults 91 | */ 92 | export function options(options, defaults) { 93 | options = options || {} 94 | for (let option in defaults) { 95 | options[option] = typeof options[option] !== 'undefined' ? options[option] : defaults[option] 96 | } 97 | return options 98 | } 99 | 100 | /** 101 | * set a style on an element 102 | * @param {HTMLElement} element 103 | * @param {string} style 104 | * @param {(string|string[])} value - single value or list of possible values (test each one in order to see if it works) 105 | */ 106 | export function style(element, style, value) { 107 | if (Array.isArray(value)) { 108 | for (let entry of value) { 109 | element.style[style] = entry 110 | if (element.style[style] === entry) { 111 | break 112 | } 113 | } 114 | } else { 115 | element.style[style] = value 116 | } 117 | } 118 | 119 | /** 120 | * calculate percentage of overlap between two boxes 121 | * from https://stackoverflow.com/a/21220004/1955997 122 | * @param {number} xa1 123 | * @param {number} ya1 124 | * @param {number} xa2 125 | * @param {number} xa2 126 | * @param {number} xb1 127 | * @param {number} yb1 128 | * @param {number} xb2 129 | * @param {number} yb2 130 | */ 131 | export function percentage(xa1, ya1, xa2, ya2, xb1, yb1, xb2, yb2) { 132 | const sa = (xa2 - xa1) * (ya2 - ya1) 133 | const sb = (xb2 - xb1) * (yb2 - yb1) 134 | const si = Math.max(0, Math.min(xa2, xb2) - Math.max(xa1, xb1)) * Math.max(0, Math.min(ya2, yb2) - Math.max(ya1, yb1)) 135 | const union = sa + sb - si 136 | if (union !== 0) { 137 | return si / union 138 | } else { 139 | return 0 140 | } 141 | } 142 | 143 | export function removeChildren(element) { 144 | while (element.firstChild) { 145 | element.firstChild.remove() 146 | } 147 | } 148 | 149 | export function html(options) { 150 | options = options || {} 151 | const object = document.createElement(options.type || 'div') 152 | if (options.parent) { 153 | options.parent.appendChild(object) 154 | } 155 | if (options.className) { 156 | object.classList.add(options.className) 157 | } 158 | if (options.html) { 159 | object.innerHTML = options.html 160 | } 161 | if (options.id) { 162 | object.id = options.id 163 | } 164 | return object 165 | } 166 | 167 | export function getChildIndex(parent, child) { 168 | let index = 0 169 | for (let entry of parent.children) { 170 | if (entry === child) { 171 | return index 172 | } 173 | index++ 174 | } 175 | return -1 176 | } 177 | 178 | export function attachStyles(styles) { 179 | const s = document.createElement('style') 180 | s.innerHTML = styles 181 | document.head.appendChild(s) 182 | } -------------------------------------------------------------------------------- /docs/jsdoc/script/search_index.js: -------------------------------------------------------------------------------- 1 | window.esdocSearchIndex = [ 2 | [ 3 | "yy-tree/src/tree.js~tree", 4 | "class/src/tree.js~Tree.html", 5 | "Tree yy-tree/src/tree.js", 6 | "class" 7 | ], 8 | [ 9 | "src/tree.js", 10 | "file/src/tree.js.html", 11 | "src/tree.js", 12 | "file" 13 | ], 14 | [ 15 | "src/tree.js~tree#collapse", 16 | "class/src/tree.js~Tree.html#instance-method-collapse", 17 | "src/tree.js~Tree#collapse", 18 | "method" 19 | ], 20 | [ 21 | "src/tree.js~tree#collapseall", 22 | "class/src/tree.js~Tree.html#instance-method-collapseAll", 23 | "src/tree.js~Tree#collapseAll", 24 | "method" 25 | ], 26 | [ 27 | "src/tree.js~tree#constructor", 28 | "class/src/tree.js~Tree.html#instance-constructor-constructor", 29 | "src/tree.js~Tree#constructor", 30 | "method" 31 | ], 32 | [ 33 | "src/tree.js~tree#dragopacity", 34 | "class/src/tree.js~Tree.html#instance-get-dragOpacity", 35 | "src/tree.js~Tree#dragOpacity", 36 | "member" 37 | ], 38 | [ 39 | "src/tree.js~tree#dragopacity", 40 | "class/src/tree.js~Tree.html#instance-set-dragOpacity", 41 | "src/tree.js~Tree#dragOpacity", 42 | "member" 43 | ], 44 | [ 45 | "src/tree.js~tree#edit", 46 | "class/src/tree.js~Tree.html#instance-method-edit", 47 | "src/tree.js~Tree#edit", 48 | "method" 49 | ], 50 | [ 51 | "src/tree.js~tree#editdata", 52 | "class/src/tree.js~Tree.html#instance-method-editData", 53 | "src/tree.js~Tree#editData", 54 | "method" 55 | ], 56 | [ 57 | "src/tree.js~tree#element", 58 | "class/src/tree.js~Tree.html#instance-member-element", 59 | "src/tree.js~Tree#element", 60 | "member" 61 | ], 62 | [ 63 | "src/tree.js~tree#expand", 64 | "class/src/tree.js~Tree.html#instance-method-expand", 65 | "src/tree.js~Tree#expand", 66 | "method" 67 | ], 68 | [ 69 | "src/tree.js~tree#expandall", 70 | "class/src/tree.js~Tree.html#instance-method-expandAll", 71 | "src/tree.js~Tree#expandAll", 72 | "method" 73 | ], 74 | [ 75 | "src/tree.js~tree#expandonclick", 76 | "class/src/tree.js~Tree.html#instance-get-expandOnClick", 77 | "src/tree.js~Tree#expandOnClick", 78 | "member" 79 | ], 80 | [ 81 | "src/tree.js~tree#expandonclick", 82 | "class/src/tree.js~Tree.html#instance-set-expandOnClick", 83 | "src/tree.js~Tree#expandOnClick", 84 | "member" 85 | ], 86 | [ 87 | "src/tree.js~tree#findintree", 88 | "class/src/tree.js~Tree.html#instance-method-findInTree", 89 | "src/tree.js~Tree#findInTree", 90 | "method" 91 | ], 92 | [ 93 | "src/tree.js~tree#getleaf", 94 | "class/src/tree.js~Tree.html#instance-method-getLeaf", 95 | "src/tree.js~Tree#getLeaf", 96 | "method" 97 | ], 98 | [ 99 | "src/tree.js~tree#holdtime", 100 | "class/src/tree.js~Tree.html#instance-get-holdTime", 101 | "src/tree.js~Tree#holdTime", 102 | "member" 103 | ], 104 | [ 105 | "src/tree.js~tree#holdtime", 106 | "class/src/tree.js~Tree.html#instance-set-holdTime", 107 | "src/tree.js~Tree#holdTime", 108 | "member" 109 | ], 110 | [ 111 | "src/tree.js~tree#indentation", 112 | "class/src/tree.js~Tree.html#instance-get-indentation", 113 | "src/tree.js~Tree#indentation", 114 | "member" 115 | ], 116 | [ 117 | "src/tree.js~tree#indentation", 118 | "class/src/tree.js~Tree.html#instance-set-indentation", 119 | "src/tree.js~Tree#indentation", 120 | "member" 121 | ], 122 | [ 123 | "src/tree.js~tree#move", 124 | "class/src/tree.js~Tree.html#instance-get-move", 125 | "src/tree.js~Tree#move", 126 | "member" 127 | ], 128 | [ 129 | "src/tree.js~tree#move", 130 | "class/src/tree.js~Tree.html#instance-set-move", 131 | "src/tree.js~Tree#move", 132 | "member" 133 | ], 134 | [ 135 | "src/tree.js~tree#namechange", 136 | "class/src/tree.js~Tree.html#instance-method-nameChange", 137 | "src/tree.js~Tree#nameChange", 138 | "method" 139 | ], 140 | [ 141 | "src/tree.js~tree#prefixclassname", 142 | "class/src/tree.js~Tree.html#instance-get-prefixClassName", 143 | "src/tree.js~Tree#prefixClassName", 144 | "member" 145 | ], 146 | [ 147 | "src/tree.js~tree#prefixclassname", 148 | "class/src/tree.js~Tree.html#instance-set-prefixClassName", 149 | "src/tree.js~Tree#prefixClassName", 150 | "member" 151 | ], 152 | [ 153 | "src/tree.js~tree#select", 154 | "class/src/tree.js~Tree.html#instance-get-select", 155 | "src/tree.js~Tree#select", 156 | "member" 157 | ], 158 | [ 159 | "src/tree.js~tree#select", 160 | "class/src/tree.js~Tree.html#instance-set-select", 161 | "src/tree.js~Tree#select", 162 | "member" 163 | ], 164 | [ 165 | "src/tree.js~tree#selection", 166 | "class/src/tree.js~Tree.html#instance-get-selection", 167 | "src/tree.js~Tree#selection", 168 | "member" 169 | ], 170 | [ 171 | "src/tree.js~tree#selection", 172 | "class/src/tree.js~Tree.html#instance-set-selection", 173 | "src/tree.js~Tree#selection", 174 | "member" 175 | ], 176 | [ 177 | "src/tree.js~tree#toggleexpand", 178 | "class/src/tree.js~Tree.html#instance-method-toggleExpand", 179 | "src/tree.js~Tree#toggleExpand", 180 | "method" 181 | ], 182 | [ 183 | "src/tree.js~tree#update", 184 | "class/src/tree.js~Tree.html#instance-method-update", 185 | "src/tree.js~Tree#update", 186 | "method" 187 | ], 188 | [ 189 | "src/tree.js~tree~treedata", 190 | "typedef/index.html#static-typedef-Tree~TreeData", 191 | "src/tree.js~Tree~TreeData", 192 | "typedef" 193 | ] 194 | ] -------------------------------------------------------------------------------- /docs/jsdoc/file/src/defaults.js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | src/defaults.js | yy-tree 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
    15 | Home 16 | 17 | Reference 18 | Source 19 | 20 | 27 |
    28 | 29 | 51 | 52 |

    src/defaults.js

    53 |
    export const defaults = {
     54 |     children: 'children',
     55 |     parent: 'parent',
     56 |     name: 'name',
     57 |     data: 'data',
     58 |     expanded: 'expanded',
     59 |     move: true,
     60 |     indentation: 20,
     61 |     nameStyles: {
     62 |         padding: '0.5em 1em',
     63 |         margin: '0.1em',
     64 |         background: 'rgba(230,230,230)',
     65 |         userSelect: 'none',
     66 |         cursor: ['grab', '-webkit-grab', 'pointer'],
     67 |         width: '100px'
     68 |     },
     69 |     threshold: 10,
     70 |     indicatorStyles: {
     71 |         background: 'rgb(150,150,255)',
     72 |         height: '5px',
     73 |         width: '100px',
     74 |         padding: '0 1em'
     75 |     },
     76 |     expandStyles: {
     77 |         width: '15px',
     78 |         height: '15px',
     79 |         cursor: 'pointer'
     80 |     },
     81 |     holdTime: 1000,
     82 |     expandOnClick: true,
     83 |     dragOpacity: 0.75
     84 | }
    85 | 86 |
    87 | 88 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /docs/jsdoc/file/src/icons.js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | src/icons.js | yy-tree 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
    15 | Home 16 | 17 | Reference 18 | Source 19 | 20 | 27 |
    28 | 29 | 51 | 52 |

    src/icons.js

    53 |
    export const icons={closed:'<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;"><rect id="closed" x="0" y="0" width="100" height="100" style="fill:none;"/><rect x="10" y="10" width="80" height="80" style="fill:none;stroke:#000;stroke-width:2px;"/><path d="M25,50l50,0" style="fill:none;stroke:#000;stroke-width:2px;"/><path d="M50,75l0,-50" style="fill:none;stroke:#000;stroke-width:2px;"/></svg>',open:'<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;"><rect id="open" x="0" y="0" width="100" height="100" style="fill:none;"/><rect x="10" y="10" width="80" height="80" style="fill:none;stroke:#000;stroke-width:2px;"/><path d="M25,50l50,0" style="fill:none;stroke:#000;stroke-width:2px;"/></svg>'}
    54 | 55 |
    56 | 57 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /src/input.js: -------------------------------------------------------------------------------- 1 | import * as utils from './utils' 2 | import { Indicator } from './indicator' 3 | 4 | export class Input { 5 | constructor(tree) { 6 | this._tree = tree 7 | this._indicator = new Indicator(tree) 8 | document.body.addEventListener('mousemove', e => this._move(e)) 9 | document.body.addEventListener('touchmove', e => this._move(e)) 10 | document.body.addEventListener('mouseup', e => this._up(e)) 11 | document.body.addEventListener('touchend', e => this._up(e)) 12 | document.body.addEventListener('mouseleave', e => this._up(e)) 13 | } 14 | 15 | _down(e) { 16 | this._target = e.currentTarget.parentNode.parentNode 17 | let alreadySelected = false 18 | if (this._tree._selection === this._target) { 19 | alreadySelected = true 20 | } else { 21 | if (this._tree._selection) { 22 | this._tree._selection.name.classList.remove(`${this._tree.prefixClassName}-select`) 23 | } 24 | this._tree._selection = this._target 25 | this._tree._selection.name.classList.add(`${this._tree.prefixClassName}-select`) 26 | } 27 | this._isDown = { x: e.pageX, y: e.pageY, alreadySelected } 28 | const pos = utils.toGlobal(this._target) 29 | this._offset = { x: e.pageX - pos.x, y: e.pageY - pos.y } 30 | if (this._tree.holdTime) { 31 | this._holdTimeout = window.setTimeout(() => this._hold(), this._tree.holdTime) 32 | } 33 | e.preventDefault() 34 | e.stopPropagation() 35 | } 36 | 37 | _hold() { 38 | this._holdTimeout = null 39 | this._tree.edit(this._target) 40 | } 41 | 42 | _checkThreshold(e) { 43 | if (!this._tree.move) { 44 | return false 45 | } else if (this._moving) { 46 | return true 47 | } else { 48 | if (utils.distance(this._isDown.x, this._isDown.y, e.pageX, e.pageY)) { 49 | this._moving = true 50 | this._pickup() 51 | return true 52 | } else { 53 | return false 54 | } 55 | } 56 | } 57 | 58 | _pickup() { 59 | if (this._holdTimeout) { 60 | window.clearTimeout(this._holdTimeout) 61 | this._holdTimeout = null 62 | } 63 | this._tree.emit('move-pending', this._target, this._tree) 64 | const parent = this._target.parentNode 65 | parent.insertBefore(this._indicator.get(), this._target) 66 | const pos = utils.toGlobal(this._target) 67 | document.body.appendChild(this._target) 68 | this._old = { 69 | opacity: this._target.style.opacity || 'unset', 70 | position: this._target.style.position || 'unset', 71 | boxShadow: this._target.name.style.boxShadow || 'unset' 72 | } 73 | this._target.style.position = 'absolute' 74 | this._target.name.style.boxShadow = '3px 3px 5px rgba(0,0,0,0.25)' 75 | this._target.style.left = pos.x + 'px' 76 | this._target.style.top = pos.y + 'px' 77 | this._target.style.opacity = this._tree.dragOpacity 78 | if (this._tree._getChildren(parent, true).length === 0) { 79 | this._tree._hideIcon(parent) 80 | } 81 | } 82 | 83 | _findClosest(e, entry) { 84 | const pos = utils.toGlobal(entry.name) 85 | if (pos.y + entry.name.offsetHeight / 2 <= e.pageY) { 86 | if (!this._closest.foundAbove) { 87 | if (utils.inside(e.pageX, e.pageY, entry.name)) { 88 | this._closest.foundAbove = true 89 | this._closest.above = entry 90 | } else { 91 | const distance = utils.distancePointElement(e.pageX, e.pageY, entry.name) 92 | if (distance < this._closest.distanceAbove) { 93 | this._closest.distanceAbove = distance 94 | this._closest.above = entry 95 | } 96 | } 97 | } 98 | } else if (!this._closest.foundBelow) { 99 | if (utils.inside(e.pageX, e.pageY, entry.name)) { 100 | this._closest.foundBelow = true 101 | this._closest.below = entry 102 | } else { 103 | const distance = utils.distancePointElement(e.pageX, e.pageY, entry.name) 104 | if (distance < this._closest.distanceBelow) { 105 | this._closest.distanceBelow = distance 106 | this._closest.below = entry 107 | } 108 | } 109 | } 110 | for (let child of this._tree._getChildren(entry)) { 111 | this._findClosest(e, child) 112 | } 113 | } 114 | 115 | _move(e) { 116 | if (this._target && this._checkThreshold(e)) { 117 | const element = this._tree.element 118 | const indicator = this._indicator.get() 119 | const indentation = this._tree.indentation 120 | indicator.remove() 121 | this._target.style.left = e.pageX - this._offset.x + 'px' 122 | this._target.style.top = e.pageY - this._offset.y + 'px' 123 | const x = utils.toGlobal(this._target.name).x 124 | this._closest = { distanceAbove: Infinity, distanceBelow: Infinity } 125 | for (let child of this._tree._getChildren()) { 126 | this._findClosest(e, child) 127 | } 128 | if (!this._closest.above && !this._closest.below) { 129 | element.appendChild(indicator) 130 | } else if (!this._closest.above) { 131 | // null [] leaf 132 | element.insertBefore(indicator, this._tree._getFirstChild(element)) 133 | } else if (!this._closest.below) { 134 | // leaf [] null 135 | let pos = utils.toGlobal(this._closest.above.name) 136 | if (x > pos.x + indentation) { 137 | this._closest.above.insertBefore(indicator, this._tree._getFirstChild(this._closest.above, true)) 138 | } else if (x > pos.x - indentation) { 139 | this._closest.above.parentNode.appendChild(indicator) 140 | } else { 141 | let parent = this._closest.above 142 | while (parent !== element && x < pos.x) { 143 | parent = this._tree._getParent(parent) 144 | if (parent !== element) { 145 | pos = utils.toGlobal(parent.name) 146 | } 147 | } 148 | parent.appendChild(indicator) 149 | } 150 | } else if (this._closest.below.parentNode === this._closest.above) { 151 | // parent [] child 152 | this._closest.above.insertBefore(indicator, this._closest.below) 153 | } else if (this._closest.below.parentNode === this._closest.above.parentNode) { 154 | // sibling [] sibling 155 | const pos = utils.toGlobal(this._closest.above.name) 156 | if (x > pos.x + indentation) { 157 | this._closest.above.insertBefore(indicator, this._tree._getLastChild(this._closest.above, true)) 158 | } else { 159 | this._closest.above.parentNode.insertBefore(indicator, this._closest.below) 160 | } 161 | } else { 162 | // child [] parent^ 163 | let pos = utils.toGlobal(this._closest.above.name) 164 | if (x > pos.x + indentation) { 165 | this._closest.above.insertBefore(indicator, this._tree._getLastChild(this._closest.above, true)) 166 | } else if (x > pos.x - indentation) { 167 | this._closest.above.parentNode.appendChild(indicator) 168 | } else if (x < utils.toGlobal(this._closest.below.name).x) { 169 | this._closest.below.parentNode.insertBefore(indicator, this._closest.below) 170 | } else { 171 | let parent = this._closest.above 172 | while (parent.parentNode !== this._closest.below.parentNode && x < pos.x) { 173 | parent = this._tree._getParent(parent) 174 | pos = utils.toGlobal(parent.name) 175 | } 176 | parent.appendChild(indicator) 177 | } 178 | } 179 | } 180 | } 181 | 182 | _up(e) { 183 | if (this._target) { 184 | if (!this._moving) { 185 | if (this._tree.expandOnClick && (!this._tree.select || this._isDown.alreadySelected)) { 186 | this._tree.toggleExpand(this._target) 187 | } 188 | this._tree.emit('clicked', this._target, e, this._tree) 189 | } else { 190 | const indicator = this._indicator.get() 191 | indicator.parentNode.insertBefore(this._target, indicator) 192 | this._tree.expand(indicator.parentNode) 193 | this._tree._showIcon(indicator.parentNode) 194 | this._target.style.position = this._old.position === 'unset' ? '' : this._old.position 195 | this._target.name.style.boxShadow = this._old.boxShadow === 'unset' ? '' : this._old.boxShadow 196 | this._target.style.opacity = this._old.opacity === 'unset' ? '' : this._old.opacity 197 | indicator.remove() 198 | this._moveData() 199 | this._tree.emit('move', this._target, this._tree) 200 | this._tree.emit('update', this._target, this._tree) 201 | } 202 | if (this._holdTimeout) { 203 | window.clearTimeout(this._holdTimeout) 204 | this._holdTimeout = null 205 | } 206 | this._target = this._moving = null 207 | } 208 | } 209 | 210 | _moveData() { 211 | this._target.data.parent.children.splice(this._target.data.parent.children.indexOf(this._target.data), 1) 212 | this._target.parentNode.data.children.splice(utils.getChildIndex(this._target.parentNode, this._target), 0, this._target.data) 213 | this._target.data.parent = this._target.parentNode.data 214 | } 215 | 216 | _indicatorMarginLeft(value) { 217 | this._indicator.marginLeft = value 218 | } 219 | } -------------------------------------------------------------------------------- /docs/jsdoc/file/src/utils.js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | src/utils.js | yy-tree 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
    15 | Home 16 | 17 | Reference 18 | Source 19 | 20 | 27 |
    28 | 29 | 51 | 52 |

    src/utils.js

    53 |
    /**
     54 |  * measure distance between two points
     55 |  * @param {number} x1
     56 |  * @param {number} y1
     57 |  * @param {number} x2
     58 |  * @param {number} y2
     59 |  */
     60 | export function distance(x1, y1, x2, y2) {
     61 |     return Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2))
     62 | }
     63 | 
     64 | /**
     65 |  * find shortest distance from point to HTMLElement's bounding box
     66 |  * from: https://gamedev.stackexchange.com/questions/44483/how-do-i-calculate-distance-between-a-point-and-an-axis-aligned-rectangle
     67 |  * @param {number} x
     68 |  * @param {number} y
     69 |  * @param {HTMLElement} element
     70 |  */
     71 | export function distancePointElement(px, py, element) {
     72 |     const pos = toGlobal(element)
     73 |     const width = element.offsetWidth
     74 |     const height = element.offsetHeight
     75 |     const x = pos.x + width / 2
     76 |     const y = pos.y + height / 2
     77 |     const dx = Math.max(Math.abs(px - x) - width / 2, 0)
     78 |     const dy = Math.max(Math.abs(py - y) - height / 2, 0)
     79 |     return dx * dx + dy * dy
     80 | }
     81 | 
     82 | /**
     83 |  * determine whether the mouse is inside an element
     84 |  * @param {HTMLElement} dragging
     85 |  * @param {HTMLElement} element
     86 |  */
     87 | export function inside(x, y, element) {
     88 |     const pos = toGlobal(element)
     89 |     const x1 = pos.x
     90 |     const y1 = pos.y
     91 |     const w1 = element.offsetWidth
     92 |     const h1 = element.offsetHeight
     93 |     return x >= x1 && x <= x1 + w1 && y >= y1 && y <= y1 + h1
     94 | }
     95 | 
     96 | /**
     97 |  * determines global location of a div
     98 |  * from https://stackoverflow.com/a/26230989/1955997
     99 |  * @param {HTMLElement} e
    100 |  * @returns {PointLike}
    101 |  */
    102 | export function toGlobal(e) {
    103 |     const box = e.getBoundingClientRect()
    104 | 
    105 |     const body = document.body
    106 |     const docEl = document.documentElement
    107 | 
    108 |     const scrollTop = window.pageYOffset || docEl.scrollTop || body.scrollTop
    109 |     const scrollLeft = window.pageXOffset || docEl.scrollLeft || body.scrollLeft
    110 | 
    111 |     const clientTop = docEl.clientTop || body.clientTop || 0
    112 |     const clientLeft = docEl.clientLeft || body.clientLeft || 0
    113 | 
    114 |     const top = box.top + scrollTop - clientTop
    115 |     const left = box.left + scrollLeft - clientLeft
    116 | 
    117 |     return { y: Math.round(top), x: Math.round(left) }
    118 | }
    119 | 
    120 | /**
    121 |  * @typedef {object} PointLike
    122 |  * @property {number} x
    123 |  * @property {number} y
    124 |  */
    125 | 
    126 | /**
    127 |  * combines options and default options
    128 |  * @param {object} options
    129 |  * @param {object} defaults
    130 |  * @returns {object} options+defaults
    131 |  */
    132 | export function options(options, defaults) {
    133 |     options = options || {}
    134 |     for (let option in defaults) {
    135 |         options[option] = typeof options[option] !== 'undefined' ? options[option] : defaults[option]
    136 |     }
    137 |     return options
    138 | }
    139 | 
    140 | /**
    141 |  * set a style on an element
    142 |  * @param {HTMLElement} element
    143 |  * @param {string} style
    144 |  * @param {(string|string[])} value - single value or list of possible values (test each one in order to see if it works)
    145 |  */
    146 | export function style(element, style, value) {
    147 |     if (Array.isArray(value)) {
    148 |         for (let entry of value) {
    149 |             element.style[style] = entry
    150 |             if (element.style[style] === entry) {
    151 |                 break
    152 |             }
    153 |         }
    154 |     } else {
    155 |         element.style[style] = value
    156 |     }
    157 | }
    158 | 
    159 | /**
    160 |  * calculate percentage of overlap between two boxes
    161 |  * from https://stackoverflow.com/a/21220004/1955997
    162 |  * @param {number} xa1
    163 |  * @param {number} ya1
    164 |  * @param {number} xa2
    165 |  * @param {number} xa2
    166 |  * @param {number} xb1
    167 |  * @param {number} yb1
    168 |  * @param {number} xb2
    169 |  * @param {number} yb2
    170 |  */
    171 | export function percentage(xa1, ya1, xa2, ya2, xb1, yb1, xb2, yb2) {
    172 |     const sa = (xa2 - xa1) * (ya2 - ya1)
    173 |     const sb = (xb2 - xb1) * (yb2 - yb1)
    174 |     const si = Math.max(0, Math.min(xa2, xb2) - Math.max(xa1, xb1)) * Math.max(0, Math.min(ya2, yb2) - Math.max(ya1, yb1))
    175 |     const union = sa + sb - si
    176 |     if (union !== 0) {
    177 |         return si / union
    178 |     } else {
    179 |         return 0
    180 |     }
    181 | }
    182 | 
    183 | export function removeChildren(element) {
    184 |     while (element.firstChild) {
    185 |         element.firstChild.remove()
    186 |     }
    187 | }
    188 | 
    189 | export function html(options) {
    190 |     options = options || {}
    191 |     const object = document.createElement(options.type || 'div')
    192 |     if (options.parent) {
    193 |         options.parent.appendChild(object)
    194 |     }
    195 |     if (options.defaultStyles) {
    196 |         styles(object, options.defaultStyles)
    197 |     }
    198 |     if (options.styles) {
    199 |         styles(object, options.styles)
    200 |     }
    201 |     if (options.html) {
    202 |         object.innerHTML = options.html
    203 |     }
    204 |     if (options.id) {
    205 |         object.id = options.id
    206 |     }
    207 |     return object
    208 | }
    209 | 
    210 | export function styles(object, styles) {
    211 |     for (let style in styles) {
    212 |         if (Array.isArray(styles[style])) {
    213 |             for (let entry of styles[style]) {
    214 |                 object.style[style] = entry
    215 |                 if (object.style[style] === entry) {
    216 |                     break
    217 |                 }
    218 |             }
    219 |         } else {
    220 |             object.style[style] = styles[style]
    221 |         }
    222 |     }
    223 | }
    224 | 
    225 | export function getChildIndex(parent, child) {
    226 |     let index = 0
    227 |     for (let entry of parent.children) {
    228 |         if (entry === child) {
    229 |             return index
    230 |         }
    231 |         index++
    232 |     }
    233 |     return -1
    234 | }
    235 | 236 |
    237 | 238 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | -------------------------------------------------------------------------------- /docs/jsdoc/script/prettify/Apache-License-2.0.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /docs/jsdoc/css/style.css: -------------------------------------------------------------------------------- 1 | @import url(https://fonts.googleapis.com/css?family=Roboto:400,300,700); 2 | @import url(https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400italic,600,700); 3 | @import url(./manual.css); 4 | @import url(./source.css); 5 | @import url(./test.css); 6 | @import url(./identifiers.css); 7 | @import url(./github.css); 8 | @import url(./search.css); 9 | 10 | * { 11 | margin: 0; 12 | padding: 0; 13 | text-decoration: none; 14 | } 15 | 16 | html 17 | { 18 | font-family: 'Source Sans Pro', 'Roboto', sans-serif; 19 | overflow: auto; 20 | /*font-size: 14px;*/ 21 | /*color: #4d4e53;*/ 22 | /*color: rgba(0, 0, 0, .68);*/ 23 | color: #555; 24 | background-color: #fff; 25 | } 26 | 27 | a { 28 | /*color: #0095dd;*/ 29 | /*color:rgb(37, 138, 175);*/ 30 | color: #039BE5; 31 | } 32 | 33 | code a:hover { 34 | text-decoration: underline; 35 | } 36 | 37 | ul, ol { 38 | padding-left: 20px; 39 | } 40 | 41 | ul li { 42 | list-style: disc; 43 | margin: 4px 0; 44 | } 45 | 46 | ol li { 47 | margin: 4px 0; 48 | } 49 | 50 | h1 { 51 | margin-bottom: 10px; 52 | font-size: 34px; 53 | font-weight: 300; 54 | border-bottom: solid 1px #ddd; 55 | } 56 | 57 | h2 { 58 | margin-top: 24px; 59 | margin-bottom: 10px; 60 | font-size: 20px; 61 | border-bottom: solid 1px #ddd; 62 | font-weight: 300; 63 | } 64 | 65 | h3 { 66 | position: relative; 67 | font-size: 16px; 68 | margin-bottom: 12px; 69 | padding: 4px; 70 | font-weight: 300; 71 | } 72 | 73 | details { 74 | cursor: pointer; 75 | } 76 | 77 | del { 78 | text-decoration: line-through; 79 | } 80 | 81 | p { 82 | margin-bottom: 15px; 83 | line-height: 1.5; 84 | } 85 | 86 | code { 87 | font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace; 88 | } 89 | 90 | pre > code { 91 | display: block; 92 | } 93 | 94 | pre.prettyprint, pre > code { 95 | padding: 4px; 96 | margin: 1em 0; 97 | background-color: #f5f5f5; 98 | border-radius: 3px; 99 | } 100 | 101 | pre.prettyprint > code { 102 | margin: 0; 103 | } 104 | 105 | p > code, 106 | li > code { 107 | padding: 0.2em 0.5em; 108 | margin: 0; 109 | font-size: 85%; 110 | background-color: rgba(0,0,0,0.04); 111 | border-radius: 3px; 112 | } 113 | 114 | .code { 115 | font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace; 116 | font-size: 13px; 117 | } 118 | 119 | .import-path pre.prettyprint, 120 | .import-path pre.prettyprint code { 121 | margin: 0; 122 | padding: 0; 123 | border: none; 124 | background: white; 125 | } 126 | 127 | .layout-container { 128 | /*display: flex;*/ 129 | /*flex-direction: row;*/ 130 | /*justify-content: flex-start;*/ 131 | /*align-items: stretch;*/ 132 | } 133 | 134 | .layout-container > header { 135 | display: flex; 136 | height: 40px; 137 | line-height: 40px; 138 | font-size: 16px; 139 | padding: 0 10px; 140 | margin: 0; 141 | position: fixed; 142 | width: 100%; 143 | z-index: 1; 144 | background-color: #fafafa; 145 | top: 0; 146 | border-bottom: solid 1px #ddd; 147 | } 148 | .layout-container > header > a{ 149 | margin: 0 5px; 150 | color: #444; 151 | } 152 | 153 | .layout-container > header > a.repo-url-github { 154 | font-size: 0; 155 | display: inline-block; 156 | width: 20px; 157 | height: 38px; 158 | background: url("../image/github.png") no-repeat center; 159 | background-size: 20px; 160 | vertical-align: top; 161 | } 162 | 163 | .navigation { 164 | position: fixed; 165 | top: 0; 166 | left: 0; 167 | box-sizing: border-box; 168 | width: 250px; 169 | height: 100%; 170 | padding-top: 40px; 171 | padding-left: 15px; 172 | padding-bottom: 2em; 173 | margin-top:1em; 174 | overflow-x: scroll; 175 | box-shadow: rgba(255, 255, 255, 1) -1px 0 0 inset; 176 | border-right: 1px solid #ddd; 177 | } 178 | 179 | .navigation ul { 180 | padding: 0; 181 | } 182 | 183 | .navigation li { 184 | list-style: none; 185 | margin: 4px 0; 186 | white-space: nowrap; 187 | } 188 | 189 | .navigation li a { 190 | color: #666; 191 | } 192 | 193 | .navigation .nav-dir-path { 194 | display: block; 195 | margin-top: 0.7em; 196 | margin-bottom: 0.25em; 197 | font-weight: 600; 198 | } 199 | 200 | .kind-class, 201 | .kind-interface, 202 | .kind-function, 203 | .kind-typedef, 204 | .kind-variable, 205 | .kind-external { 206 | margin-left: 0.75em; 207 | width: 1.2em; 208 | height: 1.2em; 209 | display: inline-block; 210 | text-align: center; 211 | border-radius: 0.2em; 212 | margin-right: 0.2em; 213 | font-weight: bold; 214 | line-height: 1.2em; 215 | } 216 | 217 | .kind-class { 218 | color: #009800; 219 | background-color: #bfe5bf; 220 | } 221 | 222 | .kind-interface { 223 | color: #fbca04; 224 | background-color: #fef2c0; 225 | } 226 | 227 | .kind-function { 228 | color: #6b0090; 229 | background-color: #d6bdde; 230 | } 231 | 232 | .kind-variable { 233 | color: #eb6420; 234 | background-color: #fad8c7; 235 | } 236 | 237 | .kind-typedef { 238 | color: #db001e; 239 | background-color: #edbec3; 240 | } 241 | 242 | .kind-external { 243 | color: #0738c3; 244 | background-color: #bbcbea; 245 | } 246 | 247 | .summary span[class^="kind-"] { 248 | margin-left: 0; 249 | } 250 | 251 | h1 .version, 252 | h1 .url a { 253 | font-size: 14px; 254 | color: #aaa; 255 | } 256 | 257 | .content { 258 | margin-top: 40px; 259 | margin-left: 250px; 260 | padding: 10px 50px 10px 20px; 261 | } 262 | 263 | .header-notice { 264 | font-size: 14px; 265 | color: #aaa; 266 | margin: 0; 267 | } 268 | 269 | .expression-extends .prettyprint { 270 | margin-left: 10px; 271 | background: white; 272 | } 273 | 274 | .extends-chain { 275 | border-bottom: 1px solid#ddd; 276 | padding-bottom: 10px; 277 | margin-bottom: 10px; 278 | } 279 | 280 | .extends-chain span:nth-of-type(1) { 281 | padding-left: 10px; 282 | } 283 | 284 | .extends-chain > div { 285 | margin: 5px 0; 286 | } 287 | 288 | .description table { 289 | font-size: 14px; 290 | border-spacing: 0; 291 | border: 0; 292 | border-collapse: collapse; 293 | } 294 | 295 | .description thead { 296 | background: #999; 297 | color: white; 298 | } 299 | 300 | .description table td, 301 | .description table th { 302 | border: solid 1px #ddd; 303 | padding: 4px; 304 | font-weight: normal; 305 | } 306 | 307 | .flat-list ul { 308 | padding-left: 0; 309 | } 310 | 311 | .flat-list li { 312 | display: inline; 313 | list-style: none; 314 | } 315 | 316 | table.summary { 317 | width: 100%; 318 | margin: 10px 0; 319 | border-spacing: 0; 320 | border: 0; 321 | border-collapse: collapse; 322 | } 323 | 324 | table.summary thead { 325 | background: #fafafa; 326 | } 327 | 328 | table.summary td { 329 | border: solid 1px #ddd; 330 | padding: 4px 10px; 331 | } 332 | 333 | table.summary tbody td:nth-child(1) { 334 | text-align: right; 335 | white-space: nowrap; 336 | min-width: 64px; 337 | vertical-align: top; 338 | } 339 | 340 | table.summary tbody td:nth-child(2) { 341 | width: 100%; 342 | border-right: none; 343 | } 344 | 345 | table.summary tbody td:nth-child(3) { 346 | white-space: nowrap; 347 | border-left: none; 348 | vertical-align: top; 349 | } 350 | 351 | table.summary td > div:nth-of-type(2) { 352 | padding-top: 4px; 353 | padding-left: 15px; 354 | } 355 | 356 | table.summary td p { 357 | margin-bottom: 0; 358 | } 359 | 360 | .inherited-summary thead td { 361 | padding-left: 2px; 362 | } 363 | 364 | .inherited-summary thead a { 365 | color: white; 366 | } 367 | 368 | .inherited-summary .summary tbody { 369 | display: none; 370 | } 371 | 372 | .inherited-summary .summary .toggle { 373 | padding: 0 4px; 374 | font-size: 12px; 375 | cursor: pointer; 376 | } 377 | .inherited-summary .summary .toggle.closed:before { 378 | content: "▶"; 379 | } 380 | .inherited-summary .summary .toggle.opened:before { 381 | content: "▼"; 382 | } 383 | 384 | .member, .method { 385 | margin-bottom: 24px; 386 | } 387 | 388 | table.params { 389 | width: 100%; 390 | margin: 10px 0; 391 | border-spacing: 0; 392 | border: 0; 393 | border-collapse: collapse; 394 | } 395 | 396 | table.params thead { 397 | background: #eee; 398 | color: #aaa; 399 | } 400 | 401 | table.params td { 402 | padding: 4px; 403 | border: solid 1px #ddd; 404 | } 405 | 406 | table.params td p { 407 | margin: 0; 408 | } 409 | 410 | .content .detail > * { 411 | margin: 15px 0; 412 | } 413 | 414 | .content .detail > h3 { 415 | color: black; 416 | background-color: #f0f0f0; 417 | } 418 | 419 | .content .detail > div { 420 | margin-left: 10px; 421 | } 422 | 423 | .content .detail > .import-path { 424 | margin-top: -8px; 425 | } 426 | 427 | .content .detail + .detail { 428 | margin-top: 30px; 429 | } 430 | 431 | .content .detail .throw td:first-child { 432 | padding-right: 10px; 433 | } 434 | 435 | .content .detail h4 + :not(pre) { 436 | padding-left: 0; 437 | margin-left: 10px; 438 | } 439 | 440 | .content .detail h4 + ul li { 441 | list-style: none; 442 | } 443 | 444 | .return-param * { 445 | display: inline; 446 | } 447 | 448 | .argument-params { 449 | margin-bottom: 20px; 450 | } 451 | 452 | .return-type { 453 | padding-right: 10px; 454 | font-weight: normal; 455 | } 456 | 457 | .return-desc { 458 | margin-left: 10px; 459 | margin-top: 4px; 460 | } 461 | 462 | .return-desc p { 463 | margin: 0; 464 | } 465 | 466 | .deprecated, .experimental, .instance-docs { 467 | border-left: solid 5px orange; 468 | padding-left: 4px; 469 | margin: 4px 0; 470 | } 471 | 472 | tr.listen p, 473 | tr.throw p, 474 | tr.emit p{ 475 | margin-bottom: 10px; 476 | } 477 | 478 | .version, .since { 479 | color: #aaa; 480 | } 481 | 482 | h3 .right-info { 483 | position: absolute; 484 | right: 4px; 485 | font-size: 14px; 486 | } 487 | 488 | .version + .since:before { 489 | content: '| '; 490 | } 491 | 492 | .see { 493 | margin-top: 10px; 494 | } 495 | 496 | .see h4 { 497 | margin: 4px 0; 498 | } 499 | 500 | .content .detail h4 + .example-doc { 501 | margin: 6px 0; 502 | } 503 | 504 | .example-caption { 505 | position: relative; 506 | bottom: -1px; 507 | display: inline-block; 508 | padding: 4px; 509 | font-style: italic; 510 | background-color: #f5f5f5; 511 | font-weight: bold; 512 | border-radius: 3px; 513 | border-bottom-left-radius: 0; 514 | border-bottom-right-radius: 0; 515 | } 516 | 517 | .example-caption + pre.source-code { 518 | margin-top: 0; 519 | border-top-left-radius: 0; 520 | } 521 | 522 | footer, .file-footer { 523 | text-align: right; 524 | font-style: italic; 525 | font-weight: 100; 526 | font-size: 13px; 527 | margin-right: 50px; 528 | margin-left: 270px; 529 | border-top: 1px solid #ddd; 530 | padding-top: 30px; 531 | margin-top: 20px; 532 | padding-bottom: 10px; 533 | } 534 | 535 | footer img { 536 | width: 24px; 537 | vertical-align: middle; 538 | padding-left: 4px; 539 | position: relative; 540 | top: -3px; 541 | opacity: 0.6; 542 | } 543 | 544 | pre.source-code { 545 | padding: 4px; 546 | } 547 | 548 | pre.raw-source-code > code { 549 | padding: 0; 550 | margin: 0; 551 | font-size: 12px; 552 | background: #fff; 553 | border: solid 1px #ddd; 554 | line-height: 1.5; 555 | } 556 | 557 | pre.raw-source-code > code > ol { 558 | counter-reset:number; 559 | list-style:none; 560 | margin:0; 561 | padding:0; 562 | overflow: hidden; 563 | } 564 | 565 | pre.raw-source-code > code > ol li:before { 566 | counter-increment: number; 567 | content: counter(number); 568 | display: inline-block; 569 | min-width: 3em; 570 | color: #aaa; 571 | text-align: right; 572 | padding-right: 1em; 573 | } 574 | 575 | pre.source-code.line-number { 576 | padding: 0; 577 | } 578 | 579 | pre.source-code ol { 580 | background: #eee; 581 | padding-left: 40px; 582 | } 583 | 584 | pre.source-code li { 585 | background: white; 586 | padding-left: 4px; 587 | list-style: decimal; 588 | margin: 0; 589 | } 590 | 591 | pre.source-code.line-number li.active { 592 | background: rgb(255, 255, 150) !important; 593 | } 594 | 595 | pre.source-code.line-number li.error-line { 596 | background: #ffb8bf; 597 | } 598 | 599 | .inner-link-active { 600 | /*background: rgb(255, 255, 150) !important;*/ 601 | background: #039BE5 !important; 602 | color: #fff !important; 603 | padding-left: 0.1em !important; 604 | } 605 | 606 | .inner-link-active a { 607 | color: inherit; 608 | } 609 | -------------------------------------------------------------------------------- /docs/jsdoc/variable/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Variable | yy-tree 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
    15 | Home 16 | 17 | Reference 18 | Source 19 | 20 | 27 |
    28 | 29 | 51 | 52 |

    Variable

    53 |
    54 | 55 | 56 | 57 | 58 | 65 | 80 | 84 | 85 | 86 | 93 | 108 | 112 | 113 | 114 |
    Static Public Summary
    59 | public 60 | 61 | 62 | 63 | 64 | 66 |
    67 |

    68 | 69 | 70 | 71 | defaults: {"children": string, "parent": string, "name": string, "data": string, "expanded": string, "move": boolean, "indentation": number, "nameStyles": *, "threshold": number, "indicatorStyles": *, "expandStyles": *, "holdTime": number, "expandOnClick": boolean, "dragOpacity": number} 72 |

    73 |
    74 |
    75 | 76 | 77 | 78 |
    79 |
    81 | 82 | 83 |
    87 | public 88 | 89 | 90 | 91 | 92 | 94 |
    95 |

    96 | 97 | 98 | 99 | icons: {"closed": string, "open": string} 100 |

    101 |
    102 |
    103 | 104 | 105 | 106 |
    107 |
    109 | 110 | 111 |
    115 |
    116 |

    Static Public

    117 | 118 |
    119 |

    120 | public 121 | 122 | 123 | 124 | 125 | 126 | defaults: {"children": string, "parent": string, "name": string, "data": string, "expanded": string, "move": boolean, "indentation": number, "nameStyles": *, "threshold": number, "indicatorStyles": *, "expandStyles": *, "holdTime": number, "expandOnClick": boolean, "dragOpacity": number} 127 | 128 | 129 | 130 | source 131 | 132 |

    133 | 134 |
    import {defaults} from 'yy-tree/src/defaults.js'
    135 | 136 | 137 | 138 | 139 | 140 | 141 |
    142 |
    143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 |
    161 |
    162 |

    163 | public 164 | 165 | 166 | 167 | 168 | 169 | icons: {"closed": string, "open": string} 170 | 171 | 172 | 173 | source 174 | 175 |

    176 | 177 |
    import {icons} from 'yy-tree/src/icons.js'
    178 | 179 | 180 | 181 | 182 | 183 | 184 |
    185 |
    186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 |
    204 |
    205 |
    206 | 207 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | -------------------------------------------------------------------------------- /docs/jsdoc/script/prettify/prettify.js: -------------------------------------------------------------------------------- 1 | !function(){/* 2 | 3 | Copyright (C) 2006 Google Inc. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | window.PR_SHOULD_USE_CONTINUATION=!0; 18 | (function(){function T(a){function d(e){var b=e.charCodeAt(0);if(92!==b)return b;var a=e.charAt(1);return(b=w[a])?b:"0"<=a&&"7">=a?parseInt(e.substring(1),8):"u"===a||"x"===a?parseInt(e.substring(2),16):e.charCodeAt(1)}function f(e){if(32>e)return(16>e?"\\x0":"\\x")+e.toString(16);e=String.fromCharCode(e);return"\\"===e||"-"===e||"]"===e||"^"===e?"\\"+e:e}function b(e){var b=e.substring(1,e.length-1).match(/\\u[0-9A-Fa-f]{4}|\\x[0-9A-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\s\S]|-|[^-\\]/g);e= 19 | [];var a="^"===b[0],c=["["];a&&c.push("^");for(var a=a?1:0,g=b.length;ak||122k||90k||122h[0]&&(h[1]+1>h[0]&&c.push("-"),c.push(f(h[1])));c.push("]");return c.join("")}function v(e){for(var a=e.source.match(/(?:\[(?:[^\x5C\x5D]|\\[\s\S])*\]|\\u[A-Fa-f0-9]{4}|\\x[A-Fa-f0-9]{2}|\\[0-9]+|\\[^ux0-9]|\(\?[:!=]|[\(\)\^]|[^\x5B\x5C\(\)\^]+)/g),c=a.length,d=[],g=0,h=0;g/,null])):d.push(["com",/^#[^\r\n]*/,null,"#"]));a.cStyleComments&&(f.push(["com",/^\/\/[^\r\n]*/,null]),f.push(["com",/^\/\*[\s\S]*?(?:\*\/|$)/,null]));if(b=a.regexLiterals){var v=(b=1|\\/=?|::?|<>?>?=?|,|;|\\?|@|\\[|~|{|\\^\\^?=?|\\|\\|?=?|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*("+ 28 | ("/(?=[^/*"+b+"])(?:[^/\\x5B\\x5C"+b+"]|\\x5C"+v+"|\\x5B(?:[^\\x5C\\x5D"+b+"]|\\x5C"+v+")*(?:\\x5D|$))+/")+")")])}(b=a.types)&&f.push(["typ",b]);b=(""+a.keywords).replace(/^ | $/g,"");b.length&&f.push(["kwd",new RegExp("^(?:"+b.replace(/[\s,]+/g,"|")+")\\b"),null]);d.push(["pln",/^\s+/,null," \r\n\t\u00a0"]);b="^.[^\\s\\w.$@'\"`/\\\\]*";a.regexLiterals&&(b+="(?!s*/)");f.push(["lit",/^@[a-z_$][a-z_$@0-9]*/i,null],["typ",/^(?:[@_]?[A-Z]+[a-z][A-Za-z_$@0-9]*|\w+_t\b)/,null],["pln",/^[a-z_$][a-z_$@0-9]*/i, 29 | null],["lit",/^(?:0x[a-f0-9]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+\-]?\d+)?)[a-z]*/i,null,"0123456789"],["pln",/^\\[\s\S]?/,null],["pun",new RegExp(b),null]);return G(d,f)}function L(a,d,f){function b(a){var c=a.nodeType;if(1==c&&!A.test(a.className))if("br"===a.nodeName)v(a),a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)b(a);else if((3==c||4==c)&&f){var d=a.nodeValue,q=d.match(n);q&&(c=d.substring(0,q.index),a.nodeValue=c,(d=d.substring(q.index+q[0].length))&& 30 | a.parentNode.insertBefore(l.createTextNode(d),a.nextSibling),v(a),c||a.parentNode.removeChild(a))}}function v(a){function b(a,c){var d=c?a.cloneNode(!1):a,k=a.parentNode;if(k){var k=b(k,1),e=a.nextSibling;k.appendChild(d);for(var f=e;f;f=e)e=f.nextSibling,k.appendChild(f)}return d}for(;!a.nextSibling;)if(a=a.parentNode,!a)return;a=b(a.nextSibling,0);for(var d;(d=a.parentNode)&&1===d.nodeType;)a=d;c.push(a)}for(var A=/(?:^|\s)nocode(?:\s|$)/,n=/\r\n?|\n/,l=a.ownerDocument,m=l.createElement("li");a.firstChild;)m.appendChild(a.firstChild); 31 | for(var c=[m],p=0;p=+v[1],d=/\n/g,A=a.a,n=A.length,f=0,l=a.c,m=l.length,b=0,c=a.g,p=c.length,w=0;c[p]=n;var r,e;for(e=r=0;e=h&&(b+=2);f>=k&&(w+=2)}}finally{g&&(g.style.display=a)}}catch(x){E.console&&console.log(x&&x.stack||x)}}var E=window,C=["break,continue,do,else,for,if,return,while"], 34 | F=[[C,"auto,case,char,const,default,double,enum,extern,float,goto,inline,int,long,register,restrict,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"],"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],H=[F,"alignas,alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,delegate,dynamic_cast,explicit,export,friend,generic,late_check,mutable,namespace,noexcept,noreturn,nullptr,property,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"], 35 | O=[F,"abstract,assert,boolean,byte,extends,finally,final,implements,import,instanceof,interface,null,native,package,strictfp,super,synchronized,throws,transient"],P=[F,"abstract,add,alias,as,ascending,async,await,base,bool,by,byte,checked,decimal,delegate,descending,dynamic,event,finally,fixed,foreach,from,get,global,group,implicit,in,interface,internal,into,is,join,let,lock,null,object,out,override,orderby,params,partial,readonly,ref,remove,sbyte,sealed,select,set,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,value,var,virtual,where,yield"], 36 | F=[F,"abstract,async,await,constructor,debugger,enum,eval,export,function,get,implements,instanceof,interface,let,null,set,undefined,var,with,yield,Infinity,NaN"],Q=[C,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"],R=[C,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],C=[C,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"], 37 | S=/^(DIR|FILE|array|vector|(de|priority_)?queue|(forward_)?list|stack|(const_)?(reverse_)?iterator|(unordered_)?(multi)?(set|map)|bitset|u?(int|float)\d*)\b/,W=/\S/,X=y({keywords:[H,P,O,F,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",Q,R,C],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),I={};t(X,["default-code"]);t(G([],[["pln",/^[^]*(?:>|$)/],["com",/^<\!--[\s\S]*?(?:-\->|$)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),"default-markup htm html mxml xhtml xml xsl".split(" "));t(G([["pln",/^[\s]+/,null," \t\r\n"],["atv",/^(?:\"[^\"]*\"?|\'[^\']*\'?)/,null, 39 | "\"'"]],[["tag",/^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],["pun",/^[=<>\/]+/],["lang-js",/^on\w+\s*=\s*\"([^\"]+)\"/i],["lang-js",/^on\w+\s*=\s*\'([^\']+)\'/i],["lang-js",/^on\w+\s*=\s*([^\"\'>\s]+)/i],["lang-css",/^style\s*=\s*\"([^\"]+)\"/i],["lang-css",/^style\s*=\s*\'([^\']+)\'/i],["lang-css",/^style\s*=\s*([^\"\'>\s]+)/i]]),["in.tag"]);t(G([],[["atv",/^[\s\S]+/]]),["uq.val"]);t(y({keywords:H, 40 | hashComments:!0,cStyleComments:!0,types:S}),"c cc cpp cxx cyc m".split(" "));t(y({keywords:"null,true,false"}),["json"]);t(y({keywords:P,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:S}),["cs"]);t(y({keywords:O,cStyleComments:!0}),["java"]);t(y({keywords:C,hashComments:!0,multiLineStrings:!0}),["bash","bsh","csh","sh"]);t(y({keywords:Q,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}),["cv","py","python"]);t(y({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END", 41 | hashComments:!0,multiLineStrings:!0,regexLiterals:2}),["perl","pl","pm"]);t(y({keywords:R,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb","ruby"]);t(y({keywords:F,cStyleComments:!0,regexLiterals:!0}),["javascript","js","ts","typescript"]);t(y({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,throw,true,try,unless,until,when,while,yes",hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0, 42 | regexLiterals:!0}),["coffee"]);t(G([],[["str",/^[\s\S]+/]]),["regex"]);var Y=E.PR={createSimpleLexer:G,registerLangHandler:t,sourceDecorator:y,PR_ATTRIB_NAME:"atn",PR_ATTRIB_VALUE:"atv",PR_COMMENT:"com",PR_DECLARATION:"dec",PR_KEYWORD:"kwd",PR_LITERAL:"lit",PR_NOCODE:"nocode",PR_PLAIN:"pln",PR_PUNCTUATION:"pun",PR_SOURCE:"src",PR_STRING:"str",PR_TAG:"tag",PR_TYPE:"typ",prettyPrintOne:E.prettyPrintOne=function(a,d,f){f=f||!1;d=d||null;var b=document.createElement("div");b.innerHTML="
    "+a+"
    "; 43 | b=b.firstChild;f&&L(b,f,!0);M({j:d,m:f,h:b,l:1,a:null,i:null,c:null,g:null});return b.innerHTML},prettyPrint:E.prettyPrint=function(a,d){function f(){for(var b=E.PR_SHOULD_USE_CONTINUATION?c.now()+250:Infinity;p this._input._down(e)) 172 | leaf.name.addEventListener('touchstart', e => this._input._down(e)) 173 | for (let child of data.children) { 174 | const add = this._leaf(child, level + 1) 175 | add.data.parent = data 176 | leaf.appendChild(add) 177 | if (!data.expanded) { 178 | add.style.display = 'none' 179 | } 180 | } 181 | if (this._getChildren(leaf, true).length === 0) { 182 | this._hideIcon(leaf) 183 | } 184 | clicked(leaf.icon, () => this.toggleExpand(leaf)) 185 | this.emit('render', leaf, this) 186 | return leaf 187 | } 188 | 189 | _getChildren(leaf, all) { 190 | leaf = leaf || this.element 191 | const children = [] 192 | for (let child of leaf.children) { 193 | if (child.isLeaf && (all || child.style.display !== 'none')) { 194 | children.push(child) 195 | } 196 | } 197 | return children 198 | } 199 | 200 | _hideIcon(leaf) { 201 | if (leaf.isLeaf) { 202 | leaf.icon.style.opacity = 0 203 | leaf.icon.style.cursor = 'unset' 204 | } 205 | } 206 | 207 | _showIcon(leaf) { 208 | if (leaf.isLeaf) { 209 | leaf.icon.style.opacity = 1 210 | leaf.icon.style.cursor = this._options.cursorExpand 211 | } 212 | } 213 | 214 | /** Expands all leaves */ 215 | expandAll() { 216 | this._expandChildren(this.element) 217 | } 218 | 219 | _expandChildren(leaf) { 220 | for (let child of this._getChildren(leaf, true)) { 221 | this.expand(child) 222 | this._expandChildren(child) 223 | } 224 | } 225 | 226 | /** Collapses all leaves */ 227 | collapseAll() { 228 | this._collapseChildren(this) 229 | } 230 | 231 | _collapseChildren(leaf) { 232 | for (let child of this._getChildren(leaf, true)) { 233 | this.collapse(child) 234 | this._collapseChildren(child) 235 | } 236 | } 237 | 238 | /** 239 | * Toggles a leaf 240 | * @param {HTMLElement} leaf 241 | */ 242 | toggleExpand(leaf) { 243 | if (leaf.icon.style.opacity !== '0') { 244 | if (leaf.data.expanded) { 245 | this.collapse(leaf) 246 | } else { 247 | this.expand(leaf) 248 | } 249 | } 250 | } 251 | 252 | /** 253 | * Expands a leaf 254 | * @param {HTMLElement} leaf 255 | */ 256 | expand(leaf) { 257 | if (leaf.isLeaf) { 258 | const children = this._getChildren(leaf, true) 259 | if (children.length) { 260 | for (let child of children) { 261 | child.style.display = 'block' 262 | } 263 | leaf.data.expanded = true 264 | leaf.icon.innerHTML = icons.open 265 | this.emit('expand', leaf, this) 266 | this.emit('update', leaf, this) 267 | } 268 | } 269 | } 270 | 271 | /** 272 | * Collapses a leaf 273 | * @param {HTMLElement} leaf 274 | */ 275 | collapse(leaf) { 276 | if (leaf.isLeaf) { 277 | const children = this._getChildren(leaf, true) 278 | if (children.length) { 279 | for (let child of children) { 280 | child.style.display = 'none' 281 | } 282 | leaf.data.expanded = false 283 | leaf.icon.innerHTML = icons.closed 284 | this.emit('collapse', leaf, this) 285 | this.emit('update', leaf, this) 286 | } 287 | } 288 | } 289 | 290 | /** call this after tree's data has been updated outside of this library */ 291 | update() { 292 | const scroll = this.element.scrollTop 293 | utils.removeChildren(this.element) 294 | for (let leaf of this.element.data.children) { 295 | const add = this._leaf(leaf, 0) 296 | add.data.parent = this.element.data 297 | this.element.appendChild(add) 298 | } 299 | this.element.scrollTop = scroll + 'px' 300 | } 301 | 302 | /** 303 | * edit the name entry using the data 304 | * @param {object} data element of leaf 305 | */ 306 | editData(data) { 307 | const children = this._getChildren(null, true) 308 | for (let child of children) { 309 | if (child.data === data) { 310 | this.edit(child) 311 | } 312 | } 313 | } 314 | 315 | /** 316 | * edit the name entry using the created element 317 | * @param {HTMLElement} leaf 318 | */ 319 | edit(leaf) { 320 | this._editing = leaf 321 | this._editInput = utils.html({ parent: this._editing.name.parentNode, type: 'input', className: `${this.prefixClassName}-name` }) 322 | const computed = window.getComputedStyle(this._editing.name) 323 | this._editInput.style.boxSizing = 'content-box' 324 | this._editInput.style.fontFamily = computed.getPropertyValue('font-family') 325 | this._editInput.style.fontSize = computed.getPropertyValue('font-size') 326 | this._editInput.value = this._editing.name.innerText 327 | this._editInput.setSelectionRange(0, this._editInput.value.length) 328 | this._editInput.focus() 329 | this._editInput.addEventListener('update', () => { 330 | this.nameChange(this._editing, this._editInput.value) 331 | this._holdClose() 332 | }) 333 | this._editInput.addEventListener('keyup', (e) => { 334 | if (e.code === 'Escape') { 335 | this._holdClose() 336 | } 337 | if (e.code === 'Enter') { 338 | this.nameChange(this._editing, this._editInput.value) 339 | this._holdClose() 340 | } 341 | }) 342 | this._editing.name.style.display = 'none' 343 | this._target = null 344 | } 345 | 346 | _holdClose() { 347 | if (this._editing) { 348 | this._editInput.remove() 349 | this._editing.name.style.display = 'block' 350 | this._editing = this._editInput = null 351 | } 352 | } 353 | 354 | /** 355 | * change the name of a leaf 356 | * @param {HTMLElement} leaf 357 | * @param {string} name 358 | */ 359 | nameChange(leaf, name) { 360 | leaf.data.name = this._input.value 361 | leaf.name.innerHTML = name 362 | this.emit('name-change', leaf, this._input.value, this) 363 | this.emit('update', leaf, this) 364 | } 365 | 366 | /** 367 | * Find a leaf based using its data 368 | * @param {object} leaf 369 | * @param {HTMLElement} [root=this.element] 370 | */ 371 | getLeaf(leaf, root = this.element) { 372 | this.findInTree(root, data => data === leaf) 373 | } 374 | 375 | /** 376 | * call the callback function on each node; returns the node if callback === true 377 | * @param {*} leaf data 378 | * @param {function} callback 379 | */ 380 | findInTree(leaf, callback) { 381 | for (const child of leaf.children) { 382 | if (callback(child)) { 383 | return child 384 | } 385 | const find = this.findInTree(child, callback) 386 | if (find) { 387 | return find 388 | } 389 | } 390 | } 391 | 392 | _getFirstChild(element, all) { 393 | const children = this._getChildren(element, all) 394 | if (children.length) { 395 | return children[0] 396 | } 397 | } 398 | 399 | _getLastChild(element, all) { 400 | const children = this._getChildren(element, all) 401 | if (children.length) { 402 | return children[children.length - 1] 403 | } 404 | } 405 | 406 | _getParent(element) { 407 | element = element.parentNode 408 | while (element.style.display === 'none') { 409 | element = element.parentNode 410 | } 411 | return element 412 | } 413 | 414 | _addStyles(userStyles) { 415 | const styles = utils.options(userStyles, styleDefaults) 416 | let s = `.${this.prefixClassName}-name{` 417 | for (const key in styles.nameStyles) { 418 | s += `${key}:${styles.nameStyles[key]};` 419 | } 420 | s += `}.${this.prefixClassName}-content{` 421 | for (const key in styles.contentStyles) { 422 | s += `${key}:${styles.contentStyles[key]};` 423 | } 424 | s += `}.${this.prefixClassName}-indicator{` 425 | for (const key in styles.indicatorStyles) { 426 | s += `${key}:${styles.indicatorStyles[key]};` 427 | } 428 | s += `}.${this.prefixClassName}-expand{` 429 | for (const key in styles.expandStyles) { 430 | s += `${key}:${styles.expandStyles[key]};` 431 | } 432 | s += `}.${this.prefixClassName}-select{` 433 | for (const key in styles.selectStyles) { 434 | s += `${key}:${styles.selectStyles[key]};` 435 | } 436 | s + '}' 437 | const style = document.createElement('style') 438 | style.innerHTML = s 439 | document.head.appendChild(style) 440 | } 441 | } 442 | 443 | /** 444 | * @typedef {Object} Tree~TreeData 445 | * @property {TreeData[]} children 446 | * @property {string} name 447 | * @property {parent} [parent] if not provided then will traverse tree to find parent 448 | */ 449 | 450 | /** 451 | * trigger when expand is called either through UI interaction or Tree.expand() 452 | * @event Tree~expand 453 | * @type {object} 454 | * @property {HTMLElement} tree element 455 | * @property {Tree} Tree 456 | */ 457 | 458 | /** 459 | * trigger when collapse is called either through UI interaction or Tree.expand() 460 | * @event Tree~collapse 461 | * @type {object} 462 | * @property {HTMLElement} tree element 463 | * @property {Tree} Tree 464 | */ 465 | 466 | /** 467 | * trigger when name is change either through UI interaction or Tree.nameChange() 468 | * @event Tree~name-change 469 | * @type {object} 470 | * @property {HTMLElement} tree element 471 | * @property {string} name 472 | * @property {Tree} Tree 473 | */ 474 | 475 | /** 476 | * trigger when a leaf is picked up through UI interaction 477 | * @event Tree~move-pending 478 | * @type {object} 479 | * @property {HTMLElement} tree element 480 | * @property {Tree} Tree 481 | */ 482 | 483 | /** 484 | * trigger when a leaf's location is changed 485 | * @event Tree~move 486 | * @type {object} 487 | * @property {HTMLElement} tree element 488 | * @property {Tree} Tree 489 | */ 490 | 491 | /** 492 | * trigger when a leaf is clicked and not dragged or held 493 | * @event Tree~clicked 494 | * @type {object} 495 | * @property {HTMLElement} tree element 496 | * @property {UIEvent} event 497 | * @property {Tree} Tree 498 | */ 499 | 500 | /** 501 | * trigger when a leaf is changed (i.e., moved, name-change) 502 | * @event Tree~update 503 | * @type {object} 504 | * @property {HTMLElement} tree element 505 | * @property {Tree} Tree 506 | */ 507 | 508 | /** 509 | * trigger when a leaf's div is created 510 | * @event Tree~render 511 | * @type {object} 512 | * @property {HTMLElement} tree element 513 | * @property {Tree} Tree 514 | */ -------------------------------------------------------------------------------- /docs/jsdoc/file/src/tree.js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | src/tree.js | yy-tree 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
    15 | Home 16 | 17 | Reference 18 | Source 19 | 20 | 27 |
    28 | 29 | 37 | 38 |

    src/tree.js

    39 |
    import Events from 'eventemitter3'
     40 | import clicked from 'clicked'
     41 | 
     42 | import { Input } from './input'
     43 | import { defaults, styleDefaults } from './defaults'
     44 | import * as utils from './utils'
     45 | import { icons } from './icons'
     46 | 
     47 | export class Tree extends Events {
     48 |     /**
     49 |      * Create Tree
     50 |      * @param {TreeData} tree - data for tree (see readme for description)
     51 |      * @param {object} [options]
     52 |      * @param {(HTMLElement|string)} [options.element] if a string then document.querySelector(element); if empty, it creates an element
     53 |      * @param {(HTMLElement|string)} [options.parent] appends the element to this parent (if string then document.querySelector(parent))
     54 |      * @param {boolean} [options.move=true] drag tree to rearrange
     55 |      * @param {boolean} [options.select=true] click to select node (if false then nodes are not selected and tree.selection is always null)
     56 |      * @param {number} [options.indentation=20] number of pixels to indent for each level
     57 |      * @param {number} [options.threshold=10] number of pixels to move to start a drag
     58 |      * @param {number} [options.holdTime=2000] number of milliseconds to press and hold name before editing starts (set to 0 to disable)
     59 |      * @param {boolean} [options.expandOnClick=true] expand and collapse node on click without drag except (will select before expanding if select=true)
     60 |      * @param {number} [options.dragOpacity=0.75] opacity setting for dragged item
     61 |      * @param {string} [options.prefixClassName=yy-tree] first part of className for all DOM objects (e.g., yy-tree, yy-tree-indicator)
     62 |      * @param {boolean} [options.addStyles=true] attaches a style sheet with default and overridden styles; set to false to use your own stylesheet
     63 |      * @param {object} [styles]
     64 |      * @param {string[]} [styles.nameStyles] use these to override individual styles for the name (will be included in the attached stylesheet)
     65 |      * @param {string[]} [styles.contentStyles] use these to override individual styles for the content (will be included in the attached stylesheet)
     66 |      * @param {string[]} [styles.indicatorStyles] use these to override individual styles for the move-line indicator (will be included in the attached stylesheet)
     67 |      * @param {string[]} [styles.selectedStyles] use these to override individual styles for the selected item (will be included in the attached stylesheet)
     68 |      * @fires render
     69 |      * @fires clicked
     70 |      * @fires expand
     71 |      * @fires collapse
     72 |      * @fires name-change
     73 |      * @fires move
     74 |      * @fires move-pending
     75 |      * @fires update
     76 |      */
     77 |     constructor(tree, options, styles) {
     78 |         super()
     79 |         this._options = utils.options(options, defaults)
     80 |         this._input = new Input(this)
     81 |         if (typeof this._options.element === 'undefined') {
     82 |             /**
     83 |              * Main div holding tree
     84 |              * @type {HTMLElement}
     85 |              */
     86 |             this.element = document.createElement('div')
     87 |         } else {
     88 |             this.element = utils.el(this._options.element)
     89 |         }
     90 |         if (this._options.parent) {
     91 |             utils.el(this._options.parent).appendChild(this.element)
     92 |         }
     93 |         this.element.classList.add(this.prefixClassName)
     94 |         this.element.data = tree
     95 |         if (this._options.addStyles !== false) {
     96 |             this._addStyles(styles)
     97 |         }
     98 |         this.update()
     99 |     }
    100 | 
    101 |     /**
    102 |      * Selected data
    103 |      * @type {*}
    104 |      */
    105 |     get selection() {
    106 |         return this._selection.data
    107 |     }
    108 |     set selection(data) {
    109 |     }
    110 | 
    111 |     /**
    112 |      * className's prefix (e.g., "yy-tree"-content)
    113 |      * @type {string}
    114 |      */
    115 |     get prefixClassName() {
    116 |         return this._options.prefixClassName
    117 |     }
    118 |     set prefixClassName(value) {
    119 |         if (value !== this._options.prefixClassName) {
    120 |             this._options.prefixClassName = value
    121 |             this.update()
    122 |         }
    123 |     }
    124 | 
    125 |     /**
    126 |      * indentation for tree
    127 |      * @type {number}
    128 |      */
    129 |     get indentation() {
    130 |         return this._options.indentation
    131 |     }
    132 |     set indentation(value) {
    133 |         if (value !== this._options.indentation) {
    134 |             this._options.indentation = value
    135 |             this._input._indicatorMarginLeft = value + 'px'
    136 |             this.update()
    137 |         }
    138 |     }
    139 | 
    140 |     /**
    141 |      * number of milliseconds to press and hold name before editing starts (set to 0 to disable)
    142 |      * @type {number}
    143 |      */
    144 |     get holdTime() {
    145 |         return this._options.holdTime
    146 |     }
    147 |     set holdTime(value) {
    148 |         if (value !== this._options.holdTime) {
    149 |             this._options.holdTime = value
    150 |         }
    151 |     }
    152 | 
    153 |     /**
    154 |      * whether tree may be rearranged
    155 |      * @type {boolean}
    156 |      */
    157 |     get move() {
    158 |         return this._options.move
    159 |     }
    160 |     set move(value) {
    161 |         this._options.move = value
    162 |     }
    163 | 
    164 |     /**
    165 |      * expand and collapse node on click without drag except (will select before expanding if select=true)
    166 |      * @type {boolean}
    167 |      */
    168 |     get expandOnClick() {
    169 |         return this._options.expandOnClick
    170 |     }
    171 |     set expandOnClick(value) {
    172 |         this._options.expandOnClick = value
    173 |     }
    174 | 
    175 |     /**
    176 |      * click to select node (if false then nodes are not selected and tree.selection is always null)
    177 |      * @type {boolean}
    178 |      */
    179 |     get select() {
    180 |         return this._options.select
    181 |     }
    182 |     set select(value) {
    183 |         this._options.select = value
    184 |     }
    185 | 
    186 |     /**
    187 |      * opacity setting for dragged item
    188 |      * @type {number}
    189 |      */
    190 |     get dragOpacity() {
    191 |         return this._options.dragOpacity
    192 |     }
    193 |     set dragOpacity(value) {
    194 |         this._options.dragOpacity = value
    195 |     }
    196 | 
    197 |     _leaf(data, level) {
    198 |         const leaf = utils.html({ className: `${this.prefixClassName}-leaf` })
    199 |         leaf.isLeaf = true
    200 |         leaf.data = data
    201 |         leaf.content = utils.html({ parent: leaf, className: `${this.prefixClassName}-content` })
    202 |         leaf.style.marginLeft = this.indentation + 'px'
    203 |         leaf.icon = utils.html({
    204 |             parent: leaf.content,
    205 |             html: data.expanded ? icons.open : icons.closed,
    206 |             className: `${this.prefixClassName}-expand`
    207 |         })
    208 |         leaf.name = utils.html({ parent: leaf.content, html: data.name, className: `${this.prefixClassName}-name` })
    209 |         leaf.name.addEventListener('mousedown', e => this._input._down(e))
    210 |         leaf.name.addEventListener('touchstart', e => this._input._down(e))
    211 |         for (let child of data.children) {
    212 |             const add = this._leaf(child, level + 1)
    213 |             add.data.parent = data
    214 |             leaf.appendChild(add)
    215 |             if (!data.expanded) {
    216 |                 add.style.display = 'none'
    217 |             }
    218 |         }
    219 |         if (this._getChildren(leaf, true).length === 0) {
    220 |             this._hideIcon(leaf)
    221 |         }
    222 |         clicked(leaf.icon, () => this.toggleExpand(leaf))
    223 |         this.emit('render', leaf, this)
    224 |         return leaf
    225 |     }
    226 | 
    227 |     _getChildren(leaf, all) {
    228 |         leaf = leaf || this.element
    229 |         const children = []
    230 |         for (let child of leaf.children) {
    231 |             if (child.isLeaf && (all || child.style.display !== 'none')) {
    232 |                 children.push(child)
    233 |             }
    234 |         }
    235 |         return children
    236 |     }
    237 | 
    238 |     _hideIcon(leaf) {
    239 |         if (leaf.isLeaf) {
    240 |             leaf.icon.style.opacity = 0
    241 |             leaf.icon.style.cursor = 'unset'
    242 |         }
    243 |     }
    244 | 
    245 |     _showIcon(leaf) {
    246 |         if (leaf.isLeaf) {
    247 |             leaf.icon.style.opacity = 1
    248 |             leaf.icon.style.cursor = this._options.cursorExpand
    249 |         }
    250 |     }
    251 | 
    252 |     /** Expands all leaves */
    253 |     expandAll() {
    254 |         this._expandChildren(this.element)
    255 |     }
    256 | 
    257 |     _expandChildren(leaf) {
    258 |         for (let child of this._getChildren(leaf, true)) {
    259 |             this.expand(child)
    260 |             this._expandChildren(child)
    261 |         }
    262 |     }
    263 | 
    264 |     /** Collapses all leaves */
    265 |     collapseAll() {
    266 |         this._collapseChildren(this)
    267 |     }
    268 | 
    269 |     _collapseChildren(leaf) {
    270 |         for (let child of this._getChildren(leaf, true)) {
    271 |             this.collapse(child)
    272 |             this._collapseChildren(child)
    273 |         }
    274 |     }
    275 | 
    276 |     /**
    277 |      * Toggles a leaf
    278 |      * @param {HTMLElement} leaf
    279 |      */
    280 |     toggleExpand(leaf) {
    281 |         if (leaf.icon.style.opacity !== '0') {
    282 |             if (leaf.data.expanded) {
    283 |                 this.collapse(leaf)
    284 |             } else {
    285 |                 this.expand(leaf)
    286 |             }
    287 |         }
    288 |     }
    289 | 
    290 |     /**
    291 |      * Expands a leaf
    292 |      * @param {HTMLElement} leaf
    293 |      */
    294 |     expand(leaf) {
    295 |         if (leaf.isLeaf) {
    296 |             const children = this._getChildren(leaf, true)
    297 |             if (children.length) {
    298 |                 for (let child of children) {
    299 |                     child.style.display = 'block'
    300 |                 }
    301 |                 leaf.data.expanded = true
    302 |                 leaf.icon.innerHTML = icons.open
    303 |                 this.emit('expand', leaf, this)
    304 |                 this.emit('update', leaf, this)
    305 |             }
    306 |         }
    307 |     }
    308 | 
    309 |     /**
    310 |      * Collapses a leaf
    311 |      * @param {HTMLElement} leaf
    312 |      */
    313 |     collapse(leaf) {
    314 |         if (leaf.isLeaf) {
    315 |             const children = this._getChildren(leaf, true)
    316 |             if (children.length) {
    317 |                 for (let child of children) {
    318 |                     child.style.display = 'none'
    319 |                 }
    320 |                 leaf.data.expanded = false
    321 |                 leaf.icon.innerHTML = icons.closed
    322 |                 this.emit('collapse', leaf, this)
    323 |                 this.emit('update', leaf, this)
    324 |             }
    325 |         }
    326 |     }
    327 | 
    328 |     /** call this after tree's data has been updated outside of this library */
    329 |     update() {
    330 |         const scroll = this.element.scrollTop
    331 |         utils.removeChildren(this.element)
    332 |         for (let leaf of this.element.data.children) {
    333 |             const add = this._leaf(leaf, 0)
    334 |             add.data.parent = this.element.data
    335 |             this.element.appendChild(add)
    336 |         }
    337 |         this.element.scrollTop = scroll + 'px'
    338 |     }
    339 | 
    340 |     /**
    341 |      * edit the name entry using the data
    342 |      * @param {object} data element of leaf
    343 |      */
    344 |     editData(data) {
    345 |         const children = this._getChildren(null, true)
    346 |         for (let child of children) {
    347 |             if (child.data === data) {
    348 |                 this.edit(child)
    349 |             }
    350 |         }
    351 |     }
    352 | 
    353 |     /**
    354 |      * edit the name entry using the created element
    355 |      * @param {HTMLElement} leaf
    356 |      */
    357 |     edit(leaf) {
    358 |         this._editing = leaf
    359 |         this._editInput = utils.html({ parent: this._editing.name.parentNode, type: 'input', className: `${this.prefixClassName}-name` })
    360 |         const computed = window.getComputedStyle(this._editing.name)
    361 |         this._editInput.style.boxSizing = 'content-box'
    362 |         this._editInput.style.fontFamily = computed.getPropertyValue('font-family')
    363 |         this._editInput.style.fontSize = computed.getPropertyValue('font-size')
    364 |         this._editInput.value = this._editing.name.innerText
    365 |         this._editInput.setSelectionRange(0, this._editInput.value.length)
    366 |         this._editInput.focus()
    367 |         this._editInput.addEventListener('update', () => {
    368 |             this.nameChange(this._editing, this._editInput.value)
    369 |             this._holdClose()
    370 |         })
    371 |         this._editInput.addEventListener('keyup', (e) => {
    372 |             if (e.code === 'Escape') {
    373 |                 this._holdClose()
    374 |             }
    375 |             if (e.code === 'Enter') {
    376 |                 this.nameChange(this._editing, this._editInput.value)
    377 |                 this._holdClose()
    378 |             }
    379 |         })
    380 |         this._editing.name.style.display = 'none'
    381 |         this._target = null
    382 |     }
    383 | 
    384 |     _holdClose() {
    385 |         if (this._editing) {
    386 |             this._editInput.remove()
    387 |             this._editing.name.style.display = 'block'
    388 |             this._editing = this._editInput = null
    389 |         }
    390 |     }
    391 | 
    392 |     /**
    393 |      * change the name of a leaf
    394 |      * @param {HTMLElement} leaf
    395 |      * @param {string} name
    396 |      */
    397 |     nameChange(leaf, name) {
    398 |         leaf.data.name = this._input.value
    399 |         leaf.name.innerHTML = name
    400 |         this.emit('name-change', leaf, this._input.value, this)
    401 |         this.emit('update', leaf, this)
    402 |     }
    403 | 
    404 |     /**
    405 |      * Find a leaf based using its data
    406 |      * @param {object} leaf
    407 |      * @param {HTMLElement} [root=this.element]
    408 |      */
    409 |     getLeaf(leaf, root = this.element) {
    410 |         this.findInTree(root, data => data === leaf)
    411 |     }
    412 | 
    413 |     /**
    414 |      * call the callback function on each node; returns the node if callback === true
    415 |      * @param {*} leaf data
    416 |      * @param {function} callback
    417 |      */
    418 |     findInTree(leaf, callback) {
    419 |         for (const child of leaf.children) {
    420 |             if (callback(child)) {
    421 |                 return child
    422 |             }
    423 |             const find = this.findInTree(child, callback)
    424 |             if (find) {
    425 |                 return find
    426 |             }
    427 |         }
    428 |     }
    429 | 
    430 |     _getFirstChild(element, all) {
    431 |         const children = this._getChildren(element, all)
    432 |         if (children.length) {
    433 |             return children[0]
    434 |         }
    435 |     }
    436 | 
    437 |     _getLastChild(element, all) {
    438 |         const children = this._getChildren(element, all)
    439 |         if (children.length) {
    440 |             return children[children.length - 1]
    441 |         }
    442 |     }
    443 | 
    444 |     _getParent(element) {
    445 |         element = element.parentNode
    446 |         while (element.style.display === 'none') {
    447 |             element = element.parentNode
    448 |         }
    449 |         return element
    450 |     }
    451 | 
    452 |     _addStyles(userStyles) {
    453 |         const styles = utils.options(userStyles, styleDefaults)
    454 |         let s = `.${this.prefixClassName}-name{`
    455 |         for (const key in styles.nameStyles) {
    456 |             s += `${key}:${styles.nameStyles[key]};`
    457 |         }
    458 |         s += `}.${this.prefixClassName}-content{`
    459 |         for (const key in styles.contentStyles) {
    460 |             s += `${key}:${styles.contentStyles[key]};`
    461 |         }
    462 |         s += `}.${this.prefixClassName}-indicator{`
    463 |         for (const key in styles.indicatorStyles) {
    464 |             s += `${key}:${styles.indicatorStyles[key]};`
    465 |         }
    466 |         s += `}.${this.prefixClassName}-expand{`
    467 |         for (const key in styles.expandStyles) {
    468 |             s += `${key}:${styles.expandStyles[key]};`
    469 |         }
    470 |         s += `}.${this.prefixClassName}-select{`
    471 |         for (const key in styles.selectStyles) {
    472 |             s += `${key}:${styles.selectStyles[key]};`
    473 |         }
    474 |         s + '}'
    475 |         const style = document.createElement('style')
    476 |         style.innerHTML = s
    477 |         document.head.appendChild(style)
    478 |     }
    479 | }
    480 | 
    481 | /**
    482 |  * @typedef {Object} Tree~TreeData
    483 |  * @property {TreeData[]} children
    484 |  * @property {string} name
    485 |  * @property {parent} [parent] if not provided then will traverse tree to find parent
    486 |  */
    487 | 
    488 | /**
    489 |   * trigger when expand is called either through UI interaction or Tree.expand()
    490 |   * @event Tree~expand
    491 |   * @type {object}
    492 |   * @property {HTMLElement} tree element
    493 |   * @property {Tree} Tree
    494 |   */
    495 | 
    496 | /**
    497 |   * trigger when collapse is called either through UI interaction or Tree.expand()
    498 |   * @event Tree~collapse
    499 |   * @type {object}
    500 |   * @property {HTMLElement} tree element
    501 |   * @property {Tree} Tree
    502 |   */
    503 | 
    504 | /**
    505 |   * trigger when name is change either through UI interaction or Tree.nameChange()
    506 |   * @event Tree~name-change
    507 |   * @type {object}
    508 |   * @property {HTMLElement} tree element
    509 |   * @property {string} name
    510 |   * @property {Tree} Tree
    511 |   */
    512 | 
    513 | /**
    514 |   * trigger when a leaf is picked up through UI interaction
    515 |   * @event Tree~move-pending
    516 |   * @type {object}
    517 |   * @property {HTMLElement} tree element
    518 |   * @property {Tree} Tree
    519 |   */
    520 | 
    521 | /**
    522 |   * trigger when a leaf's location is changed
    523 |   * @event Tree~move
    524 |   * @type {object}
    525 |   * @property {HTMLElement} tree element
    526 |   * @property {Tree} Tree
    527 |   */
    528 | 
    529 | /**
    530 |   * trigger when a leaf is clicked and not dragged or held
    531 |   * @event Tree~clicked
    532 |   * @type {object}
    533 |   * @property {HTMLElement} tree element
    534 |   * @property {UIEvent} event
    535 |   * @property {Tree} Tree
    536 |   */
    537 | 
    538 | /**
    539 |   * trigger when a leaf is changed (i.e., moved, name-change)
    540 |   * @event Tree~update
    541 |   * @type {object}
    542 |   * @property {HTMLElement} tree element
    543 |   * @property {Tree} Tree
    544 |   */
    545 | 
    546 | /**
    547 |   * trigger when a leaf's div is created
    548 |   * @event Tree~render
    549 |   * @type {object}
    550 |   * @property {HTMLElement} tree element
    551 |   * @property {Tree} Tree
    552 |   */
    553 | 554 |
    555 | 556 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 | -------------------------------------------------------------------------------- /docs/jsdoc/ast/source/icons.js.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "File", 3 | "start": 0, 4 | "end": 1526, 5 | "loc": { 6 | "start": { 7 | "line": 1, 8 | "column": 0 9 | }, 10 | "end": { 11 | "line": 1, 12 | "column": 1526 13 | } 14 | }, 15 | "program": { 16 | "type": "Program", 17 | "start": 0, 18 | "end": 1526, 19 | "loc": { 20 | "start": { 21 | "line": 1, 22 | "column": 0 23 | }, 24 | "end": { 25 | "line": 1, 26 | "column": 1526 27 | } 28 | }, 29 | "sourceType": "module", 30 | "body": [ 31 | { 32 | "type": "ExportNamedDeclaration", 33 | "start": 0, 34 | "end": 1526, 35 | "loc": { 36 | "start": { 37 | "line": 1, 38 | "column": 0 39 | }, 40 | "end": { 41 | "line": 1, 42 | "column": 1526 43 | } 44 | }, 45 | "specifiers": [], 46 | "source": null, 47 | "declaration": { 48 | "type": "VariableDeclaration", 49 | "start": 7, 50 | "end": 1526, 51 | "loc": { 52 | "start": { 53 | "line": 1, 54 | "column": 7 55 | }, 56 | "end": { 57 | "line": 1, 58 | "column": 1526 59 | } 60 | }, 61 | "declarations": [ 62 | { 63 | "type": "VariableDeclarator", 64 | "start": 13, 65 | "end": 1526, 66 | "loc": { 67 | "start": { 68 | "line": 1, 69 | "column": 13 70 | }, 71 | "end": { 72 | "line": 1, 73 | "column": 1526 74 | } 75 | }, 76 | "id": { 77 | "type": "Identifier", 78 | "start": 13, 79 | "end": 18, 80 | "loc": { 81 | "start": { 82 | "line": 1, 83 | "column": 13 84 | }, 85 | "end": { 86 | "line": 1, 87 | "column": 18 88 | }, 89 | "identifierName": "icons" 90 | }, 91 | "name": "icons" 92 | }, 93 | "init": { 94 | "type": "ObjectExpression", 95 | "start": 19, 96 | "end": 1526, 97 | "loc": { 98 | "start": { 99 | "line": 1, 100 | "column": 19 101 | }, 102 | "end": { 103 | "line": 1, 104 | "column": 1526 105 | } 106 | }, 107 | "properties": [ 108 | { 109 | "type": "ObjectProperty", 110 | "start": 20, 111 | "end": 810, 112 | "loc": { 113 | "start": { 114 | "line": 1, 115 | "column": 20 116 | }, 117 | "end": { 118 | "line": 1, 119 | "column": 810 120 | } 121 | }, 122 | "method": false, 123 | "shorthand": false, 124 | "computed": false, 125 | "key": { 126 | "type": "Identifier", 127 | "start": 20, 128 | "end": 26, 129 | "loc": { 130 | "start": { 131 | "line": 1, 132 | "column": 20 133 | }, 134 | "end": { 135 | "line": 1, 136 | "column": 26 137 | }, 138 | "identifierName": "closed" 139 | }, 140 | "name": "closed" 141 | }, 142 | "value": { 143 | "type": "StringLiteral", 144 | "start": 27, 145 | "end": 810, 146 | "loc": { 147 | "start": { 148 | "line": 1, 149 | "column": 27 150 | }, 151 | "end": { 152 | "line": 1, 153 | "column": 810 154 | } 155 | }, 156 | "extra": { 157 | "rawValue": "", 158 | "raw": "''" 159 | }, 160 | "value": "" 161 | } 162 | }, 163 | { 164 | "type": "ObjectProperty", 165 | "start": 811, 166 | "end": 1525, 167 | "loc": { 168 | "start": { 169 | "line": 1, 170 | "column": 811 171 | }, 172 | "end": { 173 | "line": 1, 174 | "column": 1525 175 | } 176 | }, 177 | "method": false, 178 | "shorthand": false, 179 | "computed": false, 180 | "key": { 181 | "type": "Identifier", 182 | "start": 811, 183 | "end": 815, 184 | "loc": { 185 | "start": { 186 | "line": 1, 187 | "column": 811 188 | }, 189 | "end": { 190 | "line": 1, 191 | "column": 815 192 | }, 193 | "identifierName": "open" 194 | }, 195 | "name": "open" 196 | }, 197 | "value": { 198 | "type": "StringLiteral", 199 | "start": 816, 200 | "end": 1525, 201 | "loc": { 202 | "start": { 203 | "line": 1, 204 | "column": 816 205 | }, 206 | "end": { 207 | "line": 1, 208 | "column": 1525 209 | } 210 | }, 211 | "extra": { 212 | "rawValue": "", 213 | "raw": "''" 214 | }, 215 | "value": "" 216 | } 217 | } 218 | ] 219 | } 220 | } 221 | ], 222 | "kind": "const", 223 | "leadingComments": [], 224 | "trailingComments": [] 225 | } 226 | } 227 | ], 228 | "directives": [] 229 | }, 230 | "comments": [], 231 | "tokens": [ 232 | { 233 | "type": { 234 | "label": "export", 235 | "keyword": "export", 236 | "beforeExpr": false, 237 | "startsExpr": false, 238 | "rightAssociative": false, 239 | "isLoop": false, 240 | "isAssign": false, 241 | "prefix": false, 242 | "postfix": false, 243 | "binop": null, 244 | "updateContext": null 245 | }, 246 | "value": "export", 247 | "start": 0, 248 | "end": 6, 249 | "loc": { 250 | "start": { 251 | "line": 1, 252 | "column": 0 253 | }, 254 | "end": { 255 | "line": 1, 256 | "column": 6 257 | } 258 | } 259 | }, 260 | { 261 | "type": { 262 | "label": "const", 263 | "keyword": "const", 264 | "beforeExpr": false, 265 | "startsExpr": false, 266 | "rightAssociative": false, 267 | "isLoop": false, 268 | "isAssign": false, 269 | "prefix": false, 270 | "postfix": false, 271 | "binop": null, 272 | "updateContext": null 273 | }, 274 | "value": "const", 275 | "start": 7, 276 | "end": 12, 277 | "loc": { 278 | "start": { 279 | "line": 1, 280 | "column": 7 281 | }, 282 | "end": { 283 | "line": 1, 284 | "column": 12 285 | } 286 | } 287 | }, 288 | { 289 | "type": { 290 | "label": "name", 291 | "beforeExpr": false, 292 | "startsExpr": true, 293 | "rightAssociative": false, 294 | "isLoop": false, 295 | "isAssign": false, 296 | "prefix": false, 297 | "postfix": false, 298 | "binop": null 299 | }, 300 | "value": "icons", 301 | "start": 13, 302 | "end": 18, 303 | "loc": { 304 | "start": { 305 | "line": 1, 306 | "column": 13 307 | }, 308 | "end": { 309 | "line": 1, 310 | "column": 18 311 | } 312 | } 313 | }, 314 | { 315 | "type": { 316 | "label": "=", 317 | "beforeExpr": true, 318 | "startsExpr": false, 319 | "rightAssociative": false, 320 | "isLoop": false, 321 | "isAssign": true, 322 | "prefix": false, 323 | "postfix": false, 324 | "binop": null, 325 | "updateContext": null 326 | }, 327 | "value": "=", 328 | "start": 18, 329 | "end": 19, 330 | "loc": { 331 | "start": { 332 | "line": 1, 333 | "column": 18 334 | }, 335 | "end": { 336 | "line": 1, 337 | "column": 19 338 | } 339 | } 340 | }, 341 | { 342 | "type": { 343 | "label": "{", 344 | "beforeExpr": true, 345 | "startsExpr": true, 346 | "rightAssociative": false, 347 | "isLoop": false, 348 | "isAssign": false, 349 | "prefix": false, 350 | "postfix": false, 351 | "binop": null 352 | }, 353 | "start": 19, 354 | "end": 20, 355 | "loc": { 356 | "start": { 357 | "line": 1, 358 | "column": 19 359 | }, 360 | "end": { 361 | "line": 1, 362 | "column": 20 363 | } 364 | } 365 | }, 366 | { 367 | "type": { 368 | "label": "name", 369 | "beforeExpr": false, 370 | "startsExpr": true, 371 | "rightAssociative": false, 372 | "isLoop": false, 373 | "isAssign": false, 374 | "prefix": false, 375 | "postfix": false, 376 | "binop": null 377 | }, 378 | "value": "closed", 379 | "start": 20, 380 | "end": 26, 381 | "loc": { 382 | "start": { 383 | "line": 1, 384 | "column": 20 385 | }, 386 | "end": { 387 | "line": 1, 388 | "column": 26 389 | } 390 | } 391 | }, 392 | { 393 | "type": { 394 | "label": ":", 395 | "beforeExpr": true, 396 | "startsExpr": false, 397 | "rightAssociative": false, 398 | "isLoop": false, 399 | "isAssign": false, 400 | "prefix": false, 401 | "postfix": false, 402 | "binop": null, 403 | "updateContext": null 404 | }, 405 | "start": 26, 406 | "end": 27, 407 | "loc": { 408 | "start": { 409 | "line": 1, 410 | "column": 26 411 | }, 412 | "end": { 413 | "line": 1, 414 | "column": 27 415 | } 416 | } 417 | }, 418 | { 419 | "type": { 420 | "label": "string", 421 | "beforeExpr": false, 422 | "startsExpr": true, 423 | "rightAssociative": false, 424 | "isLoop": false, 425 | "isAssign": false, 426 | "prefix": false, 427 | "postfix": false, 428 | "binop": null, 429 | "updateContext": null 430 | }, 431 | "value": "", 432 | "start": 27, 433 | "end": 810, 434 | "loc": { 435 | "start": { 436 | "line": 1, 437 | "column": 27 438 | }, 439 | "end": { 440 | "line": 1, 441 | "column": 810 442 | } 443 | } 444 | }, 445 | { 446 | "type": { 447 | "label": ",", 448 | "beforeExpr": true, 449 | "startsExpr": false, 450 | "rightAssociative": false, 451 | "isLoop": false, 452 | "isAssign": false, 453 | "prefix": false, 454 | "postfix": false, 455 | "binop": null, 456 | "updateContext": null 457 | }, 458 | "start": 810, 459 | "end": 811, 460 | "loc": { 461 | "start": { 462 | "line": 1, 463 | "column": 810 464 | }, 465 | "end": { 466 | "line": 1, 467 | "column": 811 468 | } 469 | } 470 | }, 471 | { 472 | "type": { 473 | "label": "name", 474 | "beforeExpr": false, 475 | "startsExpr": true, 476 | "rightAssociative": false, 477 | "isLoop": false, 478 | "isAssign": false, 479 | "prefix": false, 480 | "postfix": false, 481 | "binop": null 482 | }, 483 | "value": "open", 484 | "start": 811, 485 | "end": 815, 486 | "loc": { 487 | "start": { 488 | "line": 1, 489 | "column": 811 490 | }, 491 | "end": { 492 | "line": 1, 493 | "column": 815 494 | } 495 | } 496 | }, 497 | { 498 | "type": { 499 | "label": ":", 500 | "beforeExpr": true, 501 | "startsExpr": false, 502 | "rightAssociative": false, 503 | "isLoop": false, 504 | "isAssign": false, 505 | "prefix": false, 506 | "postfix": false, 507 | "binop": null, 508 | "updateContext": null 509 | }, 510 | "start": 815, 511 | "end": 816, 512 | "loc": { 513 | "start": { 514 | "line": 1, 515 | "column": 815 516 | }, 517 | "end": { 518 | "line": 1, 519 | "column": 816 520 | } 521 | } 522 | }, 523 | { 524 | "type": { 525 | "label": "string", 526 | "beforeExpr": false, 527 | "startsExpr": true, 528 | "rightAssociative": false, 529 | "isLoop": false, 530 | "isAssign": false, 531 | "prefix": false, 532 | "postfix": false, 533 | "binop": null, 534 | "updateContext": null 535 | }, 536 | "value": "", 537 | "start": 816, 538 | "end": 1525, 539 | "loc": { 540 | "start": { 541 | "line": 1, 542 | "column": 816 543 | }, 544 | "end": { 545 | "line": 1, 546 | "column": 1525 547 | } 548 | } 549 | }, 550 | { 551 | "type": { 552 | "label": "}", 553 | "beforeExpr": false, 554 | "startsExpr": false, 555 | "rightAssociative": false, 556 | "isLoop": false, 557 | "isAssign": false, 558 | "prefix": false, 559 | "postfix": false, 560 | "binop": null 561 | }, 562 | "start": 1525, 563 | "end": 1526, 564 | "loc": { 565 | "start": { 566 | "line": 1, 567 | "column": 1525 568 | }, 569 | "end": { 570 | "line": 1, 571 | "column": 1526 572 | } 573 | } 574 | }, 575 | { 576 | "type": { 577 | "label": "eof", 578 | "beforeExpr": false, 579 | "startsExpr": false, 580 | "rightAssociative": false, 581 | "isLoop": false, 582 | "isAssign": false, 583 | "prefix": false, 584 | "postfix": false, 585 | "binop": null, 586 | "updateContext": null 587 | }, 588 | "start": 1526, 589 | "end": 1526, 590 | "loc": { 591 | "start": { 592 | "line": 1, 593 | "column": 1526 594 | }, 595 | "end": { 596 | "line": 1, 597 | "column": 1526 598 | } 599 | } 600 | } 601 | ] 602 | } --------------------------------------------------------------------------------