vanillatree 0.0.3 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +40 -0
- package/LICENSE +1 -1
- package/README.md +160 -71
- package/dist/vanillatree.css +199 -0
- package/dist/vanillatree.d.cts +118 -0
- package/dist/vanillatree.d.mts +108 -0
- package/dist/vanillatree.min.js +2 -0
- package/dist/vanillatree.min.mjs +2 -0
- package/package.json +74 -13
- package/demo.html +0 -79
- package/vanillatree.css +0 -96
- package/vanillatree.js +0 -212
- package/vanillatree.min.js +0 -1
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project uses [Semantic Versioning](https://semver.org/).
|
|
4
|
+
|
|
5
|
+
## [Unreleased]
|
|
6
|
+
|
|
7
|
+
## [1.0.0] - 2026-10-20
|
|
8
|
+
|
|
9
|
+
The library is rewritten in TypeScript, with no dependencies. The script-tag bundle still runs in Internet Explorer 10 and 11.
|
|
10
|
+
|
|
11
|
+
### Breaking
|
|
12
|
+
|
|
13
|
+
- A label is written as text. `html: true` on a leaf or a context menu item writes it as HTML; `placeholder` is always text ([#6](https://github.com/finom/vanillatree/pull/6)).
|
|
14
|
+
- Adding a leaf with an id that is already there throws ([#8](https://github.com/finom/vanillatree/issues/8)).
|
|
15
|
+
- `add` no longer fires `vtree-close` for each closed leaf it adds.
|
|
16
|
+
- The files are in `dist/`: `vanillatree.min.js` (UMD: script tag, CommonJS, AMD), `vanillatree.min.mjs` (ES module), `vanillatree.css`, and the types. `vanillatree/vanillatree.css` still resolves through the package's exports. `vanillatree.js` (unminified) is gone.
|
|
17
|
+
- The root list has no padding of its own.
|
|
18
|
+
- Generated ids are `vtree-1`, `vtree-2`, … instead of random numbers.
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- Keyboard and screen reader support (the WAI-ARIA tree view pattern): ARIA roles and states, one leaf in the tab order, arrow keys, Home, End, Enter and Space; the context menu opens with the context menu key or Shift+F10 and works from the keyboard.
|
|
23
|
+
- `selectable: false` for a leaf that can't be selected; a click on its label opens or closes it ([#11](https://github.com/finom/vanillatree/issues/11)).
|
|
24
|
+
- `getPath(id)` and `getParent(id)` ([#10](https://github.com/finom/vanillatree/issues/10)), `hasChildren(id)` ([#11](https://github.com/finom/vanillatree/issues/11)), `getSelected()`.
|
|
25
|
+
- `destroy()` removes the tree and every listener it added, including the `document` listener of the context menu.
|
|
26
|
+
- A dark theme: the `theme` option (`'light'`, `'dark'`, `'auto'`) or a `vtree-dark` class on any ancestor. The colors are CSS variables.
|
|
27
|
+
- An ES module build, TypeScript types for `import` and `require`, and the `VanillaTree` named export.
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
|
|
31
|
+
- A click on HTML inside a label (a `<b>`, an icon) selects the leaf ([#7](https://github.com/finom/vanillatree/issues/7)).
|
|
32
|
+
- `move` and `remove` set `vtree-has-children` right in IE 10 and 11, where `classList.toggle` ignores its second argument.
|
|
33
|
+
- `move` and `remove` of a root leaf no longer change the container's classes.
|
|
34
|
+
- `remove` forgets the ids of the removed leaf's children, so they can be added again.
|
|
35
|
+
- Moving a leaf under itself or its own child throws a clear error instead of a DOM error.
|
|
36
|
+
- An id with a quote or a bracket in it works: leaves are found by id, not by a selector.
|
|
37
|
+
- A tree inside another tree's label no longer has its clicks and keys handled by the outer tree.
|
|
38
|
+
|
|
39
|
+
[Unreleased]: https://github.com/finom/vanillatree/compare/v1.0.0...HEAD
|
|
40
|
+
[1.0.0]: https://github.com/finom/vanillatree/releases/tag/v1.0.0
|
package/LICENSE
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
Copyright (c) 2014-
|
|
1
|
+
Copyright (c) 2014-2026 Andrey Gubanov <andrey@gubanov.eu>
|
|
2
2
|
|
|
3
3
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
4
|
of this software and associated documentation files (the "Software"), to deal
|
package/README.md
CHANGED
|
@@ -1,96 +1,185 @@
|
|
|
1
|
-
|
|
2
|
-
===========
|
|
1
|
+
# VanillaTree
|
|
3
2
|
|
|
4
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/vanillatree)
|
|
4
|
+
[](https://github.com/finom/vanillatree/actions/workflows/ci.yml)
|
|
5
|
+
[](https://scorecard.dev/viewer/?uri=github.com/finom/vanillatree)
|
|
6
|
+
[](https://www.bestpractices.dev/projects/15742)
|
|
5
7
|
|
|
6
|
-
|
|
8
|
+
A tree view with no dependencies: one minified script (3.4 kB gzipped) and a stylesheet. Keyboard and screen reader support, a dark theme, TypeScript types. Runs in every current browser and in Internet Explorer 10 and 11.
|
|
7
9
|
|
|
8
|
-
|
|
10
|
+
**[Live demo](https://finom.github.io/vanillatree/)**
|
|
9
11
|
|
|
10
|
-
|
|
11
|
-
For CJS env run `npm install --save vanillatree`:
|
|
12
|
-
```js
|
|
13
|
-
const VanillaTree = require('vanillatree');
|
|
14
|
-
```
|
|
12
|
+

|
|
15
13
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
### npm
|
|
17
|
+
|
|
18
|
+
```sh
|
|
19
|
+
npm install vanillatree
|
|
19
20
|
```
|
|
20
|
-
## Options
|
|
21
|
-
- ``placeholder`` (string) -- shows if none of leafs is added (optional)
|
|
22
|
-
- ``contextmenu`` (array) -- contextual menu items (optional)
|
|
23
21
|
|
|
24
|
-
### Examples
|
|
25
22
|
```js
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
contextmenu: [{
|
|
29
|
-
label: 'Label 1',
|
|
30
|
-
action(id) {
|
|
31
|
-
// someAction
|
|
32
|
-
}
|
|
33
|
-
},{
|
|
34
|
-
label: 'Label 2',
|
|
35
|
-
action(id) {
|
|
36
|
-
// someAction
|
|
37
|
-
}
|
|
38
|
-
}]
|
|
39
|
-
});
|
|
23
|
+
import VanillaTree from 'vanillatree';
|
|
24
|
+
import 'vanillatree/vanillatree.css';
|
|
40
25
|
```
|
|
41
26
|
|
|
27
|
+
CommonJS works too: `const VanillaTree = require('vanillatree');`. The types come with the package, for both.
|
|
42
28
|
|
|
43
|
-
|
|
44
|
-
- ``add(options)`` -- Adds a leaf. ``id`` option is optional
|
|
45
|
-
- ``move(id, parentId)`` -- Moves a leaf to another parent
|
|
46
|
-
- ``remove(id)`` -- Removes a leaf with given id
|
|
47
|
-
- ``open(id)`` -- Expands child tree
|
|
48
|
-
- ``close(id)`` -- Closes child tree
|
|
49
|
-
- ``toggle(id)`` -- Expands or closes child tree depending on current state
|
|
50
|
-
- ``select(id) ``-- Selects a leaf with given id
|
|
29
|
+
### Script tag
|
|
51
30
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
});
|
|
31
|
+
```html
|
|
32
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/vanillatree@1/dist/vanillatree.css">
|
|
33
|
+
<script src="https://cdn.jsdelivr.net/npm/vanillatree@1/dist/vanillatree.min.js"></script>
|
|
34
|
+
<script>
|
|
35
|
+
var tree = new VanillaTree('#tree');
|
|
36
|
+
</script>
|
|
37
|
+
```
|
|
60
38
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
39
|
+
The script defines a `VanillaTree` global. It also works with AMD loaders.
|
|
40
|
+
|
|
41
|
+
### ES module from a CDN
|
|
42
|
+
|
|
43
|
+
```html
|
|
44
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/vanillatree@1/dist/vanillatree.css">
|
|
45
|
+
<script type="module">
|
|
46
|
+
import VanillaTree from 'https://cdn.jsdelivr.net/npm/vanillatree@1/dist/vanillatree.min.mjs';
|
|
66
47
|
|
|
67
|
-
tree
|
|
48
|
+
const tree = new VanillaTree('#tree');
|
|
49
|
+
</script>
|
|
68
50
|
```
|
|
69
51
|
|
|
70
|
-
##
|
|
71
|
-
|
|
52
|
+
## Browsers
|
|
53
|
+
|
|
54
|
+
| Build | Browsers |
|
|
55
|
+
| --- | --- |
|
|
56
|
+
| `vanillatree.min.js` (script tag, CommonJS, AMD) | Chrome, Edge, Firefox, Safari, iOS Safari, Chrome for Android, Internet Explorer 10 and 11 |
|
|
57
|
+
| `vanillatree.min.mjs` (ES module) | Browsers that run ES modules: Chrome 61+, Edge 16+, Firefox 60+, Safari 10.1+ |
|
|
58
|
+
|
|
59
|
+
In IE 10 and 11 the stylesheet uses plain colors, so the CSS variables below have no effect there.
|
|
72
60
|
|
|
73
|
-
|
|
74
|
-
- ``vtree-add``
|
|
75
|
-
- ``vtree-move``
|
|
76
|
-
- ``vtree-remove``
|
|
77
|
-
- ``vtree-open``
|
|
78
|
-
- ``vtree-close``
|
|
79
|
-
- ``vtree-select``
|
|
61
|
+
## Usage
|
|
80
62
|
|
|
81
|
-
### Examples
|
|
82
63
|
```js
|
|
83
|
-
|
|
84
|
-
|
|
64
|
+
const tree = new VanillaTree('#tree', {
|
|
65
|
+
placeholder: 'Nothing here yet',
|
|
66
|
+
contextmenu: [
|
|
67
|
+
{ label: 'Rename', action(id) { /* … */ } },
|
|
68
|
+
{ label: 'Remove', action(id) { tree.remove(id); } }
|
|
69
|
+
]
|
|
85
70
|
});
|
|
86
71
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
});
|
|
72
|
+
tree.add({ label: 'src', id: 'src', opened: true, selectable: false });
|
|
73
|
+
tree.add({ label: 'index.ts', id: 'index', parent: 'src', selected: true });
|
|
74
|
+
tree.add({ label: 'README.md' });
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Options
|
|
78
|
+
|
|
79
|
+
- `placeholder` (string): text shown while the tree has no leaves.
|
|
80
|
+
- `contextmenu` (array): a menu that opens on a right click on a label, on the context menu key, or on Shift+F10. Each item is `{ label, action(id), html? }`. `action` gets the leaf's id, and `this` is the item.
|
|
81
|
+
- `theme` (`'light'`, `'dark'` or `'auto'`): `'dark'` adds the `vtree-dark` class to the tree, and `'auto'` follows the system's `prefers-color-scheme`. The default is light. You can also put `vtree-dark` on any ancestor, such as `<html>`.
|
|
90
82
|
|
|
91
|
-
|
|
92
|
-
|
|
83
|
+
The first argument is a selector, an element, or an array-like of elements (the first one is used).
|
|
84
|
+
|
|
85
|
+
### Leaf options
|
|
86
|
+
|
|
87
|
+
`add(options)` takes:
|
|
88
|
+
|
|
89
|
+
- `label` (string, required): the text of the leaf.
|
|
90
|
+
- `id` (string or number): unique. When omitted, it is generated (`vtree-1`, `vtree-2`, …). An id that is already there throws.
|
|
91
|
+
- `parent` (id): the parent leaf. Without it, the leaf is a root leaf.
|
|
92
|
+
- `opened` (boolean): show the children. Leaves are closed by default.
|
|
93
|
+
- `selected` (boolean): select the leaf once it is added.
|
|
94
|
+
- `selectable` (boolean): `false` for a leaf that can't be selected, like a folder in a file picker. A click on its label opens or closes it.
|
|
95
|
+
- `html` (boolean): write `label` as HTML. Labels are text by default.
|
|
96
|
+
|
|
97
|
+
### Methods
|
|
98
|
+
|
|
99
|
+
Each method that changes the tree returns the tree, so calls chain. A method that gets an unknown id throws.
|
|
100
|
+
|
|
101
|
+
- `add(options)`: adds a leaf.
|
|
102
|
+
- `move(id, parentId)`: moves a leaf under another parent, or to the root without `parentId`. Moving a leaf under itself or its own child throws.
|
|
103
|
+
- `remove(id)`: removes a leaf and its children.
|
|
104
|
+
- `open(id)`, `close(id)`, `toggle(id)`: show or hide the children.
|
|
105
|
+
- `select(id)`: selects a leaf. Does nothing for a leaf added with `selectable: false`.
|
|
106
|
+
- `getSelected()`: the id of the selected leaf, or `null`.
|
|
107
|
+
- `getParent(id)`: the parent's id, or `null` for a root leaf.
|
|
108
|
+
- `getPath(id)`: ids from the root leaf down to this one, as an array.
|
|
109
|
+
- `hasChildren(id)`: whether the leaf has children.
|
|
110
|
+
- `getLeaf(id, notThrow)`: the leaf's `li` element. With `notThrow`, an unknown id gives `undefined`.
|
|
111
|
+
- `getChildList(id)`: the `ul` with the leaf's children, made when missing. Without an id, the root list.
|
|
112
|
+
- `destroy()`: removes the tree from the page and every listener it added, including the one on `document` for the context menu. Call it before you drop a tree that has a context menu, or the listener stays.
|
|
113
|
+
|
|
114
|
+
Properties: `container` (the element you passed), `tree` (the root `ul.vtree`), `leafs` (the options each leaf was added with, by id).
|
|
115
|
+
|
|
116
|
+
### Events
|
|
117
|
+
|
|
118
|
+
Events are dispatched on the leaf, or on the root list for `vtree-remove`, and bubble. The leaf's id is in `evt.detail.id`.
|
|
119
|
+
|
|
120
|
+
- `vtree-add`
|
|
121
|
+
- `vtree-move`
|
|
122
|
+
- `vtree-remove`
|
|
123
|
+
- `vtree-open`
|
|
124
|
+
- `vtree-close`
|
|
125
|
+
- `vtree-select`
|
|
126
|
+
|
|
127
|
+
```js
|
|
128
|
+
const container = document.querySelector('#tree');
|
|
129
|
+
container.addEventListener('vtree-select', (evt) => {
|
|
130
|
+
console.log(`${evt.detail.id} is selected`);
|
|
93
131
|
});
|
|
94
132
|
```
|
|
95
133
|
|
|
96
|
-
|
|
134
|
+
## Keyboard and screen readers
|
|
135
|
+
|
|
136
|
+
The tree follows the [WAI-ARIA tree view pattern](https://www.w3.org/WAI/ARIA/apg/patterns/treeview/): `role="tree"`, `treeitem` and `group`, with `aria-expanded` and `aria-selected`. One leaf is in the tab order at a time.
|
|
137
|
+
|
|
138
|
+
| Key | Action |
|
|
139
|
+
| --- | --- |
|
|
140
|
+
| Tab | Into the tree: to the selected leaf, or the first one |
|
|
141
|
+
| ↓ ↑ | Next and previous visible leaf |
|
|
142
|
+
| → | Open a closed leaf; on an open one, go to its first child |
|
|
143
|
+
| ← | Close an open leaf; otherwise go to its parent |
|
|
144
|
+
| Home, End | First and last visible leaf |
|
|
145
|
+
| Enter, Space | Select; open or close a leaf that can't be selected |
|
|
146
|
+
| Shift+F10, context menu key | Open the context menu; arrows, Home, End, Enter, Space and Esc work inside it |
|
|
147
|
+
|
|
148
|
+
Give the tree an accessible name, for example `tree.tree.setAttribute('aria-label', 'Project files')`.
|
|
149
|
+
|
|
150
|
+
## Styling
|
|
151
|
+
|
|
152
|
+
Every color is a CSS variable, set on `.vtree` or any ancestor:
|
|
153
|
+
|
|
154
|
+
```css
|
|
155
|
+
.vtree {
|
|
156
|
+
--vtree-hover-background: #e7f4f9;
|
|
157
|
+
--vtree-hover-outline: #d8f0fa;
|
|
158
|
+
--vtree-selected-background: #beebff;
|
|
159
|
+
--vtree-selected-outline: #99defd;
|
|
160
|
+
--vtree-focus: #0b6bcb;
|
|
161
|
+
--vtree-menu-background: #eee;
|
|
162
|
+
--vtree-menu-border: #ccc;
|
|
163
|
+
--vtree-menu-color: #333;
|
|
164
|
+
--vtree-menu-active-background: #3399ff;
|
|
165
|
+
--vtree-menu-active-color: #fff;
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
A variable set this way applies to both themes; set it inside `.vtree-dark` for the dark one only. The class names (`vtree-leaf`, `vtree-leaf-label`, `vtree-toggle`, `vtree-selected`, `closed`, `vtree-has-children`, `vtree-contextmenu`) are stable.
|
|
170
|
+
|
|
171
|
+
## Upgrading from 0.0.x
|
|
172
|
+
|
|
173
|
+
1.0.0 changes:
|
|
174
|
+
|
|
175
|
+
- A label is text. Pass `html: true` for a label written as HTML. The same goes for `placeholder` (always text) and context menu items.
|
|
176
|
+
- Adding a leaf with an id that is already there throws.
|
|
177
|
+
- `add` no longer fires `vtree-close` for each closed leaf it adds.
|
|
178
|
+
- The files moved to `dist/`: `vanillatree/vanillatree.css` still resolves through the package's exports; on a CDN use `dist/vanillatree.min.js` and `dist/vanillatree.css`.
|
|
179
|
+
- The root list has no padding of its own now.
|
|
180
|
+
|
|
181
|
+
The [CHANGELOG](./CHANGELOG.md) has the full list.
|
|
182
|
+
|
|
183
|
+
## License
|
|
184
|
+
|
|
185
|
+
MIT. The image sprite comes from the [jsTree](https://www.jstree.com/) project.
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/*! VanillaTree | MIT | https://github.com/finom/vanillatree
|
|
2
|
+
* The colors are CSS variables, each with a plain value before it for browsers without them (IE 10 and 11).
|
|
3
|
+
* The dark theme applies inside `.vtree-dark` (the `theme` option sets it on the tree, or put it on any ancestor). */
|
|
4
|
+
|
|
5
|
+
.vtree ul.vtree-subtree,
|
|
6
|
+
.vtree li.vtree-leaf {
|
|
7
|
+
margin: 0;
|
|
8
|
+
padding: 0;
|
|
9
|
+
list-style-type: none;
|
|
10
|
+
position: relative;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.vtree {
|
|
14
|
+
margin: 0;
|
|
15
|
+
padding: 0;
|
|
16
|
+
list-style-type: none;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.vtree li.vtree-leaf {
|
|
20
|
+
background-position: -90px 0;
|
|
21
|
+
background-repeat: repeat-y;
|
|
22
|
+
min-height: 18px;
|
|
23
|
+
line-height: 18px;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.vtree li.vtree-leaf::before {
|
|
27
|
+
content: "";
|
|
28
|
+
width: 18px;
|
|
29
|
+
height: 18px;
|
|
30
|
+
position: absolute;
|
|
31
|
+
background-position: -36px 0;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.vtree li.vtree-leaf li.vtree-leaf {
|
|
35
|
+
margin-left: 18px;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.vtree li.vtree-leaf:last-child {
|
|
39
|
+
background-image: none;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.vtree li.vtree-leaf.closed ul.vtree-subtree {
|
|
43
|
+
display: none;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.vtree li.vtree-leaf.vtree-has-children > span.vtree-toggle {
|
|
47
|
+
display: block;
|
|
48
|
+
width: 18px;
|
|
49
|
+
height: 18px;
|
|
50
|
+
background-position: -72px 0;
|
|
51
|
+
position: absolute;
|
|
52
|
+
left: 0;
|
|
53
|
+
top: 0;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.vtree li.vtree-leaf.vtree-has-children.closed > span.vtree-toggle {
|
|
57
|
+
background-position: -54px 0;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.vtree a.vtree-leaf-label {
|
|
61
|
+
line-height: 18px;
|
|
62
|
+
display: inline-block;
|
|
63
|
+
vertical-align: top;
|
|
64
|
+
cursor: pointer;
|
|
65
|
+
max-width: 100%;
|
|
66
|
+
margin-left: 18px;
|
|
67
|
+
padding: 0 2px;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.vtree li.vtree-leaf a.vtree-leaf-label:hover {
|
|
71
|
+
background-color: #e7f4f9;
|
|
72
|
+
background-color: var(--vtree-hover-background, #e7f4f9);
|
|
73
|
+
outline: 1px solid #d8f0fa;
|
|
74
|
+
outline: 1px solid var(--vtree-hover-outline, #d8f0fa);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.vtree li.vtree-leaf.vtree-selected > a.vtree-leaf-label {
|
|
78
|
+
background-color: #beebff;
|
|
79
|
+
background-color: var(--vtree-selected-background, #beebff);
|
|
80
|
+
outline: 1px solid #99defd;
|
|
81
|
+
outline: 1px solid var(--vtree-selected-outline, #99defd);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/* keyboard focus: the ring goes on the label, not around the leaf and its children */
|
|
85
|
+
.vtree li.vtree-leaf:focus {
|
|
86
|
+
outline: none;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.vtree li.vtree-leaf:focus > a.vtree-leaf-label {
|
|
90
|
+
outline: 2px solid #0b6bcb;
|
|
91
|
+
outline: 2px solid var(--vtree-focus, #0b6bcb);
|
|
92
|
+
outline-offset: 1px;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/* no ring after a click, where the browser can tell */
|
|
96
|
+
.vtree li.vtree-leaf:focus:not(:focus-visible) > a.vtree-leaf-label {
|
|
97
|
+
outline: none;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.vtree li.vtree-leaf.vtree-selected:focus:not(:focus-visible) > a.vtree-leaf-label {
|
|
101
|
+
outline: 1px solid #99defd;
|
|
102
|
+
outline: 1px solid var(--vtree-selected-outline, #99defd);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.vtree-contextmenu {
|
|
106
|
+
position: absolute;
|
|
107
|
+
z-index: 9999999;
|
|
108
|
+
border: solid 1px #ccc;
|
|
109
|
+
border: solid 1px var(--vtree-menu-border, #ccc);
|
|
110
|
+
background: #eee;
|
|
111
|
+
background: var(--vtree-menu-background, #eee);
|
|
112
|
+
padding: 0;
|
|
113
|
+
margin: 0;
|
|
114
|
+
display: none;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.vtree-contextmenu li {
|
|
118
|
+
list-style: none;
|
|
119
|
+
padding: 1px 5px;
|
|
120
|
+
margin: 0;
|
|
121
|
+
color: #333;
|
|
122
|
+
color: var(--vtree-menu-color, #333);
|
|
123
|
+
line-height: 20px;
|
|
124
|
+
height: 20px;
|
|
125
|
+
cursor: default;
|
|
126
|
+
white-space: nowrap;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.vtree-contextmenu li:hover,
|
|
130
|
+
.vtree-contextmenu li:focus {
|
|
131
|
+
outline: none;
|
|
132
|
+
color: #fff;
|
|
133
|
+
color: var(--vtree-menu-active-color, #fff);
|
|
134
|
+
background-color: #3399ff;
|
|
135
|
+
background-color: var(--vtree-menu-active-background, #3399ff);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.vtree li.vtree-leaf,
|
|
139
|
+
.vtree li.vtree-leaf::before,
|
|
140
|
+
.vtree li.vtree-leaf.vtree-has-children > span.vtree-toggle {
|
|
141
|
+
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGwAAAASCAYAAAC+Tjt8AAAACXBIWXMAAAsTAAALEwEAmpwYAAAA30lEQVRYw+2YQQqDMBBFvUdxGe+U+3gFs3WTkJsJ5goKv1QodFHTiqNm8C8+2Y0+Zl7QqQBUjJ6oe+G6fuDOdTi1NIyG0TDmd8NCCDjigU3T4MqJfnNJm3EWVxYsxgjpZhljcOWEaufKgqWU0Pc9pKCstSjBMM1cWbBpmha4rusgASUBJmGYZq4s2DzPS4ZhQNu22AslOYlr5z8N+8a1tc4a19Y6hxjmnMOeu/11fqYUwzRyZcHGcRS760tJqVy7DfPei39NlRDtXKf/h5VgmOYNDrcH3CUyNIxhw7Qa9gQbuFdIMXobzAAAAABJRU5ErkJggg==);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/* the dark theme */
|
|
145
|
+
|
|
146
|
+
.vtree-dark li.vtree-leaf,
|
|
147
|
+
.vtree-dark li.vtree-leaf::before,
|
|
148
|
+
.vtree-dark li.vtree-leaf.vtree-has-children > span.vtree-toggle {
|
|
149
|
+
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGwAAAASCAYAAAC+Tjt8AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAzElEQVRYw+2YQQrDIBBFA8kVIngz0Y1bvYL38U5xGXqPKR0odJNC6ohi/wNxN/Bw/qAuCwAtOc8HoQ4ASBgSBr5hrW1y4sdxUM9OfHtJJ6O3F4sZY0j6sEop1LsRZ/RisX3fyXtPUlI5ZxohYTN6sdi2bSwXYyQJKQkxiUac0YvF1nXlpbWmlBLVSkl24tX+q9fdOlded+s0SVgIgWpm+2v/XKMkbCYvFlNKic36kW6/I3pVJ8w5J36bGoFZvZq9w+CFnw4AkDAA/jNhTwd6SF35vQQ/AAAAAElFTkSuQmCC);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.vtree-dark li.vtree-leaf:last-child {
|
|
153
|
+
background-image: none;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.vtree-dark li.vtree-leaf a.vtree-leaf-label:hover {
|
|
157
|
+
background-color: #1d3a47;
|
|
158
|
+
background-color: var(--vtree-hover-background, #1d3a47);
|
|
159
|
+
outline: 1px solid #2b5466;
|
|
160
|
+
outline: 1px solid var(--vtree-hover-outline, #2b5466);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
.vtree-dark li.vtree-leaf.vtree-selected > a.vtree-leaf-label {
|
|
164
|
+
background-color: #0f4c69;
|
|
165
|
+
background-color: var(--vtree-selected-background, #0f4c69);
|
|
166
|
+
outline: 1px solid #1f6f94;
|
|
167
|
+
outline: 1px solid var(--vtree-selected-outline, #1f6f94);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.vtree-dark li.vtree-leaf:focus > a.vtree-leaf-label {
|
|
171
|
+
outline: 2px solid #6cb6ff;
|
|
172
|
+
outline: 2px solid var(--vtree-focus, #6cb6ff);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/* a separate rule: a browser without :focus-visible drops the whole selector list */
|
|
176
|
+
.vtree-dark li.vtree-leaf.vtree-selected:focus:not(:focus-visible) > a.vtree-leaf-label {
|
|
177
|
+
outline: 1px solid #1f6f94;
|
|
178
|
+
outline: 1px solid var(--vtree-selected-outline, #1f6f94);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.vtree-dark .vtree-contextmenu {
|
|
182
|
+
border-color: #444;
|
|
183
|
+
border-color: var(--vtree-menu-border, #444);
|
|
184
|
+
background: #2a2a2a;
|
|
185
|
+
background: var(--vtree-menu-background, #2a2a2a);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.vtree-dark .vtree-contextmenu li {
|
|
189
|
+
color: #ddd;
|
|
190
|
+
color: var(--vtree-menu-color, #ddd);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.vtree-dark .vtree-contextmenu li:hover,
|
|
194
|
+
.vtree-dark .vtree-contextmenu li:focus {
|
|
195
|
+
color: #fff;
|
|
196
|
+
color: var(--vtree-menu-active-color, #fff);
|
|
197
|
+
background-color: #1f6fbf;
|
|
198
|
+
background-color: var(--vtree-menu-active-background, #1f6fbf);
|
|
199
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
interface ContextMenuItem {
|
|
2
|
+
/** Text of the menu item. */
|
|
3
|
+
label: string;
|
|
4
|
+
/** Write `label` as HTML instead of text. Only for HTML you trust. */
|
|
5
|
+
html?: boolean;
|
|
6
|
+
/** Called with the id of the leaf the menu was opened on; `this` is the item. */
|
|
7
|
+
action(this: ContextMenuItem, id: string): void;
|
|
8
|
+
}
|
|
9
|
+
interface VanillaTreeOptions {
|
|
10
|
+
/** Text shown while the tree has no leaves. */
|
|
11
|
+
placeholder?: string;
|
|
12
|
+
/** Items of the menu that opens on a right click, the context menu key or Shift+F10 on a leaf. */
|
|
13
|
+
contextmenu?: ContextMenuItem[];
|
|
14
|
+
/** `'dark'` adds the `vtree-dark` class to the tree; `'auto'` follows `prefers-color-scheme`. */
|
|
15
|
+
theme?: 'light' | 'dark' | 'auto';
|
|
16
|
+
}
|
|
17
|
+
interface LeafOptions {
|
|
18
|
+
/** Text of the leaf. */
|
|
19
|
+
label: string;
|
|
20
|
+
/** Unique id; generated (`vtree-1`, `vtree-2`, …) when omitted. */
|
|
21
|
+
id?: string | number;
|
|
22
|
+
/** Id of the parent leaf; a root leaf when omitted. */
|
|
23
|
+
parent?: string | number;
|
|
24
|
+
/** Write `label` as HTML instead of text. Only for HTML you trust. */
|
|
25
|
+
html?: boolean;
|
|
26
|
+
/** Show the children; closed by default. */
|
|
27
|
+
opened?: boolean;
|
|
28
|
+
/** Select the leaf once it is added. */
|
|
29
|
+
selected?: boolean;
|
|
30
|
+
/** `false`: the leaf can't be selected; a click on its label opens or closes it instead. */
|
|
31
|
+
selectable?: boolean;
|
|
32
|
+
}
|
|
33
|
+
interface VanillaTreeEventDetail {
|
|
34
|
+
id: string;
|
|
35
|
+
}
|
|
36
|
+
type Key = string | number;
|
|
37
|
+
type Root = string | Element | ArrayLike<Element>;
|
|
38
|
+
declare class VanillaTree {
|
|
39
|
+
/** The element the tree is rendered into. */
|
|
40
|
+
container: Element;
|
|
41
|
+
/** The tree's root list, `ul.vtree`. */
|
|
42
|
+
tree: HTMLElement;
|
|
43
|
+
placeholder?: string;
|
|
44
|
+
/** The options each leaf was added with, by id. */
|
|
45
|
+
leafs: Record<string, LeafOptions>;
|
|
46
|
+
_options: VanillaTreeOptions;
|
|
47
|
+
_nodes: Record<string, HTMLElement>;
|
|
48
|
+
_prefix: string;
|
|
49
|
+
_count: number;
|
|
50
|
+
_selected: string | null;
|
|
51
|
+
_tabbable: string | null;
|
|
52
|
+
_menu: HTMLElement | null;
|
|
53
|
+
_onClick: (evt: MouseEvent) => void;
|
|
54
|
+
_onKeyDown: (evt: KeyboardEvent) => void;
|
|
55
|
+
_onFocus: (evt: FocusEvent) => void;
|
|
56
|
+
_onContextMenu: (evt: MouseEvent) => void;
|
|
57
|
+
_onDocumentClick: (evt: MouseEvent) => void;
|
|
58
|
+
_onScheme: () => void;
|
|
59
|
+
_scheme: MediaQueryList | null;
|
|
60
|
+
constructor(container: Root, options?: VanillaTreeOptions);
|
|
61
|
+
/** The leaf's `li` element; throws for an unknown id unless `notThrow` is set. */
|
|
62
|
+
getLeaf(id: Key, notThrow: true): HTMLElement | undefined;
|
|
63
|
+
getLeaf(id: Key, notThrow?: boolean): HTMLElement;
|
|
64
|
+
/** The `ul` holding the leaf's children (created when missing), or the root list without an id. */
|
|
65
|
+
getChildList(id?: Key | null): HTMLElement;
|
|
66
|
+
/** The id of the leaf's parent, `null` for a root leaf. */
|
|
67
|
+
getParent(id: Key): string | null;
|
|
68
|
+
/** Ids from the root leaf down to the leaf itself. */
|
|
69
|
+
getPath(id: Key): string[];
|
|
70
|
+
/** The id of the selected leaf, `null` when none is. */
|
|
71
|
+
getSelected(): string | null;
|
|
72
|
+
/** Whether the leaf has children. */
|
|
73
|
+
hasChildren(id: Key): boolean;
|
|
74
|
+
add(options: LeafOptions): this;
|
|
75
|
+
/** Moves the leaf under another parent, or to the root without `parentId`. */
|
|
76
|
+
move(id: Key, parentId?: Key | null): this;
|
|
77
|
+
/** Removes the leaf and its children. */
|
|
78
|
+
remove(id: Key): this;
|
|
79
|
+
/** Shows the leaf's children. */
|
|
80
|
+
open(id: Key): this;
|
|
81
|
+
/** Hides the leaf's children. */
|
|
82
|
+
close(id: Key): this;
|
|
83
|
+
/** Opens a closed leaf, closes an open one. */
|
|
84
|
+
toggle(id: Key): this;
|
|
85
|
+
/** Selects the leaf; does nothing for a leaf added with `selectable: false`. */
|
|
86
|
+
select(id: Key): this;
|
|
87
|
+
/** Removes the tree from the page and every listener it added; the instance can't be used after it. */
|
|
88
|
+
destroy(): void;
|
|
89
|
+
_id(leaf: Element): string;
|
|
90
|
+
_newId(): string;
|
|
91
|
+
_subtree(leaf: Element): HTMLElement | undefined;
|
|
92
|
+
_sync(leaf: HTMLElement): void;
|
|
93
|
+
_syncList(list: HTMLElement): void;
|
|
94
|
+
_placeholder(): this;
|
|
95
|
+
_visible(leaf: HTMLElement): HTMLElement;
|
|
96
|
+
_visibleLeaves(): HTMLElement[];
|
|
97
|
+
_setTabbable(id: string): void;
|
|
98
|
+
_fixTabbable(): void;
|
|
99
|
+
_focus(leaf: HTMLElement): void;
|
|
100
|
+
_leafOf(target: EventTarget | null, className?: string): HTMLElement | undefined;
|
|
101
|
+
_click(evt: MouseEvent): void;
|
|
102
|
+
_focusIn(evt: FocusEvent): void;
|
|
103
|
+
_keyDown(evt: KeyboardEvent): void;
|
|
104
|
+
_contextMenu(evt: MouseEvent): void;
|
|
105
|
+
_openMenu(leaf: HTMLElement): void;
|
|
106
|
+
_closeMenu(): void;
|
|
107
|
+
}
|
|
108
|
+
type _ContextMenuItem = ContextMenuItem;
|
|
109
|
+
type _VanillaTreeOptions = VanillaTreeOptions;
|
|
110
|
+
type _LeafOptions = LeafOptions;
|
|
111
|
+
type _VanillaTreeEventDetail = VanillaTreeEventDetail;
|
|
112
|
+
declare namespace VanillaTree {
|
|
113
|
+
export type ContextMenuItem = _ContextMenuItem;
|
|
114
|
+
export type VanillaTreeOptions = _VanillaTreeOptions;
|
|
115
|
+
export type LeafOptions = _LeafOptions;
|
|
116
|
+
export type VanillaTreeEventDetail = _VanillaTreeEventDetail;
|
|
117
|
+
}
|
|
118
|
+
export = VanillaTree;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
export interface ContextMenuItem {
|
|
2
|
+
/** Text of the menu item. */
|
|
3
|
+
label: string;
|
|
4
|
+
/** Write `label` as HTML instead of text. Only for HTML you trust. */
|
|
5
|
+
html?: boolean;
|
|
6
|
+
/** Called with the id of the leaf the menu was opened on; `this` is the item. */
|
|
7
|
+
action(this: ContextMenuItem, id: string): void;
|
|
8
|
+
}
|
|
9
|
+
export interface VanillaTreeOptions {
|
|
10
|
+
/** Text shown while the tree has no leaves. */
|
|
11
|
+
placeholder?: string;
|
|
12
|
+
/** Items of the menu that opens on a right click, the context menu key or Shift+F10 on a leaf. */
|
|
13
|
+
contextmenu?: ContextMenuItem[];
|
|
14
|
+
/** `'dark'` adds the `vtree-dark` class to the tree; `'auto'` follows `prefers-color-scheme`. */
|
|
15
|
+
theme?: 'light' | 'dark' | 'auto';
|
|
16
|
+
}
|
|
17
|
+
export interface LeafOptions {
|
|
18
|
+
/** Text of the leaf. */
|
|
19
|
+
label: string;
|
|
20
|
+
/** Unique id; generated (`vtree-1`, `vtree-2`, …) when omitted. */
|
|
21
|
+
id?: string | number;
|
|
22
|
+
/** Id of the parent leaf; a root leaf when omitted. */
|
|
23
|
+
parent?: string | number;
|
|
24
|
+
/** Write `label` as HTML instead of text. Only for HTML you trust. */
|
|
25
|
+
html?: boolean;
|
|
26
|
+
/** Show the children; closed by default. */
|
|
27
|
+
opened?: boolean;
|
|
28
|
+
/** Select the leaf once it is added. */
|
|
29
|
+
selected?: boolean;
|
|
30
|
+
/** `false`: the leaf can't be selected; a click on its label opens or closes it instead. */
|
|
31
|
+
selectable?: boolean;
|
|
32
|
+
}
|
|
33
|
+
export interface VanillaTreeEventDetail {
|
|
34
|
+
id: string;
|
|
35
|
+
}
|
|
36
|
+
type Key = string | number;
|
|
37
|
+
type Root = string | Element | ArrayLike<Element>;
|
|
38
|
+
export default class VanillaTree {
|
|
39
|
+
/** The element the tree is rendered into. */
|
|
40
|
+
container: Element;
|
|
41
|
+
/** The tree's root list, `ul.vtree`. */
|
|
42
|
+
tree: HTMLElement;
|
|
43
|
+
placeholder?: string;
|
|
44
|
+
/** The options each leaf was added with, by id. */
|
|
45
|
+
leafs: Record<string, LeafOptions>;
|
|
46
|
+
_options: VanillaTreeOptions;
|
|
47
|
+
_nodes: Record<string, HTMLElement>;
|
|
48
|
+
_prefix: string;
|
|
49
|
+
_count: number;
|
|
50
|
+
_selected: string | null;
|
|
51
|
+
_tabbable: string | null;
|
|
52
|
+
_menu: HTMLElement | null;
|
|
53
|
+
_onClick: (evt: MouseEvent) => void;
|
|
54
|
+
_onKeyDown: (evt: KeyboardEvent) => void;
|
|
55
|
+
_onFocus: (evt: FocusEvent) => void;
|
|
56
|
+
_onContextMenu: (evt: MouseEvent) => void;
|
|
57
|
+
_onDocumentClick: (evt: MouseEvent) => void;
|
|
58
|
+
_onScheme: () => void;
|
|
59
|
+
_scheme: MediaQueryList | null;
|
|
60
|
+
constructor(container: Root, options?: VanillaTreeOptions);
|
|
61
|
+
/** The leaf's `li` element; throws for an unknown id unless `notThrow` is set. */
|
|
62
|
+
getLeaf(id: Key, notThrow: true): HTMLElement | undefined;
|
|
63
|
+
getLeaf(id: Key, notThrow?: boolean): HTMLElement;
|
|
64
|
+
/** The `ul` holding the leaf's children (created when missing), or the root list without an id. */
|
|
65
|
+
getChildList(id?: Key | null): HTMLElement;
|
|
66
|
+
/** The id of the leaf's parent, `null` for a root leaf. */
|
|
67
|
+
getParent(id: Key): string | null;
|
|
68
|
+
/** Ids from the root leaf down to the leaf itself. */
|
|
69
|
+
getPath(id: Key): string[];
|
|
70
|
+
/** The id of the selected leaf, `null` when none is. */
|
|
71
|
+
getSelected(): string | null;
|
|
72
|
+
/** Whether the leaf has children. */
|
|
73
|
+
hasChildren(id: Key): boolean;
|
|
74
|
+
add(options: LeafOptions): this;
|
|
75
|
+
/** Moves the leaf under another parent, or to the root without `parentId`. */
|
|
76
|
+
move(id: Key, parentId?: Key | null): this;
|
|
77
|
+
/** Removes the leaf and its children. */
|
|
78
|
+
remove(id: Key): this;
|
|
79
|
+
/** Shows the leaf's children. */
|
|
80
|
+
open(id: Key): this;
|
|
81
|
+
/** Hides the leaf's children. */
|
|
82
|
+
close(id: Key): this;
|
|
83
|
+
/** Opens a closed leaf, closes an open one. */
|
|
84
|
+
toggle(id: Key): this;
|
|
85
|
+
/** Selects the leaf; does nothing for a leaf added with `selectable: false`. */
|
|
86
|
+
select(id: Key): this;
|
|
87
|
+
/** Removes the tree from the page and every listener it added; the instance can't be used after it. */
|
|
88
|
+
destroy(): void;
|
|
89
|
+
_id(leaf: Element): string;
|
|
90
|
+
_newId(): string;
|
|
91
|
+
_subtree(leaf: Element): HTMLElement | undefined;
|
|
92
|
+
_sync(leaf: HTMLElement): void;
|
|
93
|
+
_syncList(list: HTMLElement): void;
|
|
94
|
+
_placeholder(): this;
|
|
95
|
+
_visible(leaf: HTMLElement): HTMLElement;
|
|
96
|
+
_visibleLeaves(): HTMLElement[];
|
|
97
|
+
_setTabbable(id: string): void;
|
|
98
|
+
_fixTabbable(): void;
|
|
99
|
+
_focus(leaf: HTMLElement): void;
|
|
100
|
+
_leafOf(target: EventTarget | null, className?: string): HTMLElement | undefined;
|
|
101
|
+
_click(evt: MouseEvent): void;
|
|
102
|
+
_focusIn(evt: FocusEvent): void;
|
|
103
|
+
_keyDown(evt: KeyboardEvent): void;
|
|
104
|
+
_contextMenu(evt: MouseEvent): void;
|
|
105
|
+
_openMenu(leaf: HTMLElement): void;
|
|
106
|
+
_closeMenu(): void;
|
|
107
|
+
}
|
|
108
|
+
export { VanillaTree };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
/*! VanillaTree v1.0.0 | MIT | https://github.com/finom/vanillatree */
|
|
2
|
+
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).VanillaTree=t()}(this,function(){"use strict";var e=function(e,t){var i=[];e&&(i=e.dispatchEvent?[e]:"string"==typeof e?(t||document).querySelectorAll(e):e);for(var n=[],s=0;s<i.length;s++)n.push(i[s]);return n},t={Down:"ArrowDown",Up:"ArrowUp",Left:"ArrowLeft",Right:"ArrowRight",Spacebar:" ",Esc:"Escape",Apps:"ContextMenu"},i=function(e){return Object.prototype.hasOwnProperty.call(t,e.key)?t[e.key]:e.key},n=0,s=function(e,t,i){var n=document.createElement(e);for(var s in n.className=t,i)n.setAttribute(s,i[s]);return n},r=function(e,t,i){i?e.innerHTML=t:e.textContent=t},l=function(e,t){return e.classList.contains(t)},o=function(e,t,i){i?e.classList.add(t):e.classList.remove(t)},a=function(e,t,i){var n,s="vtree-"+t,r={id:i};"function"==typeof CustomEvent?n=new CustomEvent(s,{bubbles:!0,cancelable:!0,detail:r}):(n=document.createEvent("CustomEvent")).initCustomEvent(s,!0,!0,r),e.dispatchEvent(n)};return function(){function VanillaTree(t,i){var r=this,l=e(t)[0];if(!l)throw Error("VanillaTree: no element for "+String(t));this.container=l,this._options=i||{},this.placeholder=this._options.placeholder,this.leafs=Object.create(null),this._nodes=Object.create(null),this._prefix="vtree-"+ ++n+"-",this._count=0,this._selected=null,this._tabbable=null,this._menu=null,this._scheme=null;var a=s("ul","vtree",{role:"tree"});this.tree=a,this._onClick=function(e){return r._click(e)},this._onKeyDown=function(e){return r._keyDown(e)},this._onFocus=function(e){return r._focusIn(e)},this._onContextMenu=function(e){return r._contextMenu(e)},this._onDocumentClick=function(e){2!==e.button&&r._closeMenu()},this._onScheme=function(){var e;return o(a,"vtree-dark",!(null==(e=r._scheme)||!e.matches))},a.addEventListener("click",this._onClick),a.addEventListener("keydown",this._onKeyDown),a.addEventListener("focus",this._onFocus,!0),this._options.contextmenu&&(a.addEventListener("contextmenu",this._onContextMenu),document.addEventListener("click",this._onDocumentClick));var h=this._options.theme;"dark"===h?a.classList.add("vtree-dark"):"auto"===h&&"function"==typeof matchMedia&&(this._scheme=matchMedia("(prefers-color-scheme: dark)"),this._scheme.addListener(this._onScheme),this._onScheme()),l.appendChild(a),this._placeholder()}var t,h=VanillaTree.prototype;return h.getLeaf=function(e,t){var i=this._nodes[e];if(!i&&!t)throw Error('No VanillaTree leaf with id "'+e+'"');return i},h.getChildList=function(e){if(null==e)return this.tree;var t=this.getLeaf(e),i=this._subtree(t);return i||(i=t.appendChild(s("ul","vtree-subtree",{role:"group"}))),i},h.getParent=function(e){var t=this.getLeaf(e).parentNode;return t===this.tree?null:this._id(t.parentNode)},h.getPath=function(e){for(var t=[],i=this._id(this.getLeaf(e));null!==i;i=this.getParent(i))t.unshift(i);return t},h.getSelected=function(){return this._selected},h.hasChildren=function(e){var t=this._subtree(this.getLeaf(e));return!!t&&t.children.length>0},h.add=function(e){var t=null==e.id||""===e.id?this._newId():String(e.id);if(this._nodes[t])throw Error('VanillaTree leaf with id "'+t+'" already exists');var i=this.getChildList(e.parent),n=this._prefix+ ++this._count,l=s("li","vtree-leaf",{"data-vtree-id":t,role:"treeitem",tabindex:"-1","aria-labelledby":n});return l.appendChild(s("span","vtree-toggle",{"aria-hidden":"true"})),r(l.appendChild(s("a","vtree-leaf-label",{id:n})),e.label,e.html),!1!==e.selectable&&l.setAttribute("aria-selected","false"),e.opened||l.classList.add("closed"),i.appendChild(l),this._nodes[t]=l,this.leafs[t]=e,i!==this.tree&&this._sync(i.parentNode),e.selected&&this.select(t),this._placeholder()._fixTabbable(),a(l,"add",t),this},h.move=function(e,t){var i=this.getLeaf(e),n=this._id(i);if(null!=t)for(var s=this._id(this.getLeaf(t));null!==s;s=this.getParent(s))if(s===n)throw Error("VanillaTree can't move leaf \""+n+'" into itself');var r=i.parentNode;return this.getChildList(t).appendChild(i),this.leafs[n].parent=null==t?void 0:String(t),this._syncList(r),this._syncList(i.parentNode),this._placeholder()._fixTabbable(),a(i,"move",n),this},h.remove=function(t){var i=this.getLeaf(t),n=this._id(i),s=i.parentNode;this._menu&&i.contains(this._menu)&&this._closeMenu();for(var r=[i].concat(e(i.querySelectorAll("li.vtree-leaf"))),l=0;l<r.length;l++){var o=r[l],h=this._id(o);delete this._nodes[h],delete this.leafs[h],this._selected===h&&(this._selected=null),this._tabbable===h&&(this._tabbable=null)}var c=i.contains(document.activeElement);return s.removeChild(i),this._syncList(s),this._placeholder()._fixTabbable(),c&&null!==this._tabbable&&this._nodes[this._tabbable].focus(),a(this.tree,"remove",n),this},h.open=function(e){var t=this.getLeaf(e);return t.classList.remove("closed"),this._sync(t),a(t,"open",this._id(t)),this},h.close=function(e){var t=this.getLeaf(e);t.classList.add("closed"),this._sync(t);var i=document.activeElement;return i!==t&&t.contains(i)&&t.focus(),this._fixTabbable(),a(t,"close",this._id(t)),this},h.toggle=function(e){return l(this.getLeaf(e),"closed")?this.open(e):this.close(e)},h.select=function(e){var t=this.getLeaf(e),i=this._id(t);if(!1===this.leafs[i].selectable||this._selected===i)return this;if(null!==this._selected){var n=this._nodes[this._selected];n.classList.remove("vtree-selected"),n.setAttribute("aria-selected","false")}return t.classList.add("vtree-selected"),t.setAttribute("aria-selected","true"),this._selected=i,this._setTabbable(i),this._fixTabbable(),a(t,"select",i),this},h.destroy=function(){var e=this.tree;this._closeMenu(),e.removeEventListener("click",this._onClick),e.removeEventListener("keydown",this._onKeyDown),e.removeEventListener("focus",this._onFocus,!0),e.removeEventListener("contextmenu",this._onContextMenu),document.removeEventListener("click",this._onDocumentClick),this._scheme&&this._scheme.removeListener(this._onScheme),this._scheme=null,e.parentNode&&e.parentNode.removeChild(e),this.leafs=Object.create(null),this._nodes=Object.create(null),this._selected=null,this._tabbable=null},h._id=function(e){return e.getAttribute("data-vtree-id")},h._newId=function(){var e;do{e="vtree-"+ ++this._count}while(this._nodes[e]);return e},h._subtree=function(e){for(var t=e.children,i=0;i<t.length;i++)if(l(t[i],"vtree-subtree"))return t[i]},h._sync=function(e){var t=this._id(e),i=this.hasChildren(t);o(e,"vtree-has-children",i),i?e.setAttribute("aria-expanded",String(!l(e,"closed"))):e.removeAttribute("aria-expanded")},h._syncList=function(e){e!==this.tree&&this._sync(e.parentNode)},h._placeholder=function(){var t=this.tree,i=e(".vtree-placeholder",t)[0],n=!e("li.vtree-leaf",t).length;return n&&this.placeholder&&!i?r(t.appendChild(s("li","vtree-placeholder",{role:"none"})),this.placeholder):!n&&i&&t.removeChild(i),this},h._visible=function(e){for(var t=e,i=e.parentNode;i!==this.tree;i=i.parentNode)l(i,"vtree-leaf")&&l(i,"closed")&&(t=i);return t},h._visibleLeaves=function(){var e=this,t=[],i=function(n){for(var s=n.children,r=0;r<s.length;r++){var o=s[r];t.push(o);var a=e._subtree(o);a&&!l(o,"closed")&&i(a)}};return i(this.tree),t},h._setTabbable=function(e){this._tabbable!==e&&(null!==this._tabbable&&this._nodes[this._tabbable].setAttribute("tabindex","-1"),this._tabbable=e,this._nodes[e].setAttribute("tabindex","0"))},h._fixTabbable=function(){var t,i=null!=(t=this._tabbable)?t:this._selected;if(null!==i)this._setTabbable(this._id(this._visible(this._nodes[i])));else{var n=e("li.vtree-leaf",this.tree)[0];n&&this._setTabbable(this._id(n))}},h._focus=function(e){this._setTabbable(this._id(e)),e.focus()},h._leafOf=function(e,t){for(var i=!t,n=e;n&&n!==this.tree;n=n.parentNode){if(l(n,"vtree-contextmenu"))return;if(t&&l(n,t)&&(i=!0),l(n,"vtree-leaf"))return i&&this._nodes[this._id(n)]===n?n:void 0}},h._click=function(e){var t=this._leafOf(e.target,"vtree-toggle");if(t)this.toggle(this._id(t));else{var i=this._leafOf(e.target,"vtree-leaf-label");if(i){var n=this._id(i);!1===this.leafs[n].selectable?this.hasChildren(n)&&this.toggle(n):this.select(n)}}},h._focusIn=function(e){var t=this._leafOf(e.target);t&&t===e.target&&this._setTabbable(this._id(t))},h._keyDown=function(e){var t=e.target;if(l(t,"vtree-leaf")&&this._leafOf(t)===t){var n=i(e),s=this._id(t),r=this.hasChildren(s),o=r&&!l(t,"closed"),a=this._visibleLeaves(),h=a.indexOf(t);if("ArrowDown"===n)h<a.length-1&&this._focus(a[h+1]);else if("ArrowUp"===n)h>0&&this._focus(a[h-1]);else if("Home"===n)this._focus(a[0]);else if("End"===n)this._focus(a[a.length-1]);else if("ArrowRight"===n)o?this._focus(a[h+1]):r&&this.open(s);else if("ArrowLeft"===n){var c=this.getParent(s);o?this.close(s):null!==c&&this._focus(this._nodes[c])}else if("Enter"===n||" "===n)!1!==this.leafs[s].selectable?this.select(s):r&&this.toggle(s);else{if(!("ContextMenu"===n||"F10"===n&&e.shiftKey))return;if(!this._options.contextmenu)return;this._openMenu(t)}e.preventDefault()}},h._contextMenu=function(e){var t=this._leafOf(e.target,"vtree-leaf-label");t&&(e.preventDefault(),e.stopPropagation(),this._openMenu(t))},h._openMenu=function(t){for(var n=this,l=0,o=e(".vtree-contextmenu");l<o.length;l++){var a=o[l];a.parentNode.removeChild(a)}var h=this._id(t),c=e(".vtree-leaf-label",t)[0],u=s("menu","vtree-contextmenu",{role:"menu"});this._menu=u,u.style.top=c.offsetTop+c.getBoundingClientRect().height+"px",u.style.left=c.offsetLeft+"px",u.style.display="block";var d=this._options.contextmenu.map(function(e){var i=u.appendChild(s("li","vtree-contextmenu-item",{role:"menuitem",tabindex:"-1"}));return r(i,e.label,e.html),i.addEventListener("click",function(){n._closeMenu(),t.focus(),e.action.call(e,h)}),i});u.addEventListener("keydown",function(e){var s=i(e),r=d.indexOf(e.target);if("ArrowDown"===s)d[(r+1)%d.length].focus();else if("ArrowUp"===s)d[(r+d.length-1)%d.length].focus();else if("Home"===s)d[0].focus();else if("End"===s)d[d.length-1].focus();else if("Enter"===s||" "===s)d[r].click();else{if("Escape"!==s&&"Tab"!==s)return;n._closeMenu(),t.focus()}e.preventDefault(),e.stopPropagation()}),t.appendChild(u),d.length&&d[0].focus()},h._closeMenu=function(){var e=this._menu;null!=e&&e.parentNode&&e.parentNode.removeChild(e),this._menu=null},t=VanillaTree,Object.defineProperty(t,"prototype",{writable:!1}),t}()});
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
/*! VanillaTree v1.0.0 | MIT | https://github.com/finom/vanillatree */
|
|
2
|
+
const e=(e,t)=>{let s=[];e&&(s=e.dispatchEvent?[e]:"string"==typeof e?(t||document).querySelectorAll(e):e);const i=[];for(let e=0;e<s.length;e++)i.push(s[e]);return i},t={Down:"ArrowDown",Up:"ArrowUp",Left:"ArrowLeft",Right:"ArrowRight",Spacebar:" ",Esc:"Escape",Apps:"ContextMenu"},s=e=>Object.prototype.hasOwnProperty.call(t,e.key)?t[e.key]:e.key;let i=0;const n=(e,t,s)=>{const i=document.createElement(e);i.className=t;for(const e in s)i.setAttribute(e,s[e]);return i},l=(e,t,s)=>{s?e.innerHTML=t:e.textContent=t},o=(e,t)=>e.classList.contains(t),r=(e,t,s)=>{s?e.classList.add(t):e.classList.remove(t)},h=(e,t,s)=>{const i=`vtree-${t}`,n={id:s};let l;"function"==typeof CustomEvent?l=new CustomEvent(i,{bubbles:!0,cancelable:!0,detail:n}):(l=document.createEvent("CustomEvent"),l.initCustomEvent(i,!0,!0,n)),e.dispatchEvent(l)};class VanillaTree{constructor(t,s){const l=e(t)[0];if(!l)throw Error(`VanillaTree: no element for ${String(t)}`);this.container=l,this._options=s||{},this.placeholder=this._options.placeholder,this.leafs=Object.create(null),this._nodes=Object.create(null),this._prefix=`vtree-${++i}-`,this._count=0,this._selected=null,this._tabbable=null,this._menu=null,this._scheme=null;const o=n("ul","vtree",{role:"tree"});this.tree=o,this._onClick=e=>this._click(e),this._onKeyDown=e=>this._keyDown(e),this._onFocus=e=>this._focusIn(e),this._onContextMenu=e=>this._contextMenu(e),this._onDocumentClick=e=>{2!==e.button&&this._closeMenu()},this._onScheme=()=>r(o,"vtree-dark",!!this._scheme?.matches),o.addEventListener("click",this._onClick),o.addEventListener("keydown",this._onKeyDown),o.addEventListener("focus",this._onFocus,!0),this._options.contextmenu&&(o.addEventListener("contextmenu",this._onContextMenu),document.addEventListener("click",this._onDocumentClick));const{theme:h}=this._options;"dark"===h?o.classList.add("vtree-dark"):"auto"===h&&"function"==typeof matchMedia&&(this._scheme=matchMedia("(prefers-color-scheme: dark)"),this._scheme.addListener(this._onScheme),this._onScheme()),l.appendChild(o),this._placeholder()}getLeaf(e,t){const s=this._nodes[e];if(!s&&!t)throw Error(`No VanillaTree leaf with id "${e}"`);return s}getChildList(e){if(null==e)return this.tree;const t=this.getLeaf(e);let s=this._subtree(t);return s||(s=t.appendChild(n("ul","vtree-subtree",{role:"group"}))),s}getParent(e){const t=this.getLeaf(e).parentNode;return t===this.tree?null:this._id(t.parentNode)}getPath(e){const t=[];for(let s=this._id(this.getLeaf(e));null!==s;s=this.getParent(s))t.unshift(s);return t}getSelected(){return this._selected}hasChildren(e){const t=this._subtree(this.getLeaf(e));return!!t&&t.children.length>0}add(e){const t=null==e.id||""===e.id?this._newId():String(e.id);if(this._nodes[t])throw Error(`VanillaTree leaf with id "${t}" already exists`);const s=this.getChildList(e.parent),i=this._prefix+ ++this._count,o=n("li","vtree-leaf",{"data-vtree-id":t,role:"treeitem",tabindex:"-1","aria-labelledby":i});return o.appendChild(n("span","vtree-toggle",{"aria-hidden":"true"})),l(o.appendChild(n("a","vtree-leaf-label",{id:i})),e.label,e.html),!1!==e.selectable&&o.setAttribute("aria-selected","false"),e.opened||o.classList.add("closed"),s.appendChild(o),this._nodes[t]=o,this.leafs[t]=e,s!==this.tree&&this._sync(s.parentNode),e.selected&&this.select(t),this._placeholder()._fixTabbable(),h(o,"add",t),this}move(e,t){const s=this.getLeaf(e),i=this._id(s);if(null!=t)for(let e=this._id(this.getLeaf(t));null!==e;e=this.getParent(e))if(e===i)throw Error(`VanillaTree can't move leaf "${i}" into itself`);const n=s.parentNode;return this.getChildList(t).appendChild(s),this.leafs[i].parent=null==t?void 0:String(t),this._syncList(n),this._syncList(s.parentNode),this._placeholder()._fixTabbable(),h(s,"move",i),this}remove(t){const s=this.getLeaf(t),i=this._id(s),n=s.parentNode;this._menu&&s.contains(this._menu)&&this._closeMenu();const l=[s,...e(s.querySelectorAll("li.vtree-leaf"))];for(const e of l){const t=this._id(e);delete this._nodes[t],delete this.leafs[t],this._selected===t&&(this._selected=null),this._tabbable===t&&(this._tabbable=null)}const o=s.contains(document.activeElement);return n.removeChild(s),this._syncList(n),this._placeholder()._fixTabbable(),o&&null!==this._tabbable&&this._nodes[this._tabbable].focus(),h(this.tree,"remove",i),this}open(e){const t=this.getLeaf(e);return t.classList.remove("closed"),this._sync(t),h(t,"open",this._id(t)),this}close(e){const t=this.getLeaf(e);t.classList.add("closed"),this._sync(t);const s=document.activeElement;return s!==t&&t.contains(s)&&t.focus(),this._fixTabbable(),h(t,"close",this._id(t)),this}toggle(e){return o(this.getLeaf(e),"closed")?this.open(e):this.close(e)}select(e){const t=this.getLeaf(e),s=this._id(t);if(!1===this.leafs[s].selectable||this._selected===s)return this;if(null!==this._selected){const e=this._nodes[this._selected];e.classList.remove("vtree-selected"),e.setAttribute("aria-selected","false")}return t.classList.add("vtree-selected"),t.setAttribute("aria-selected","true"),this._selected=s,this._setTabbable(s),this._fixTabbable(),h(t,"select",s),this}destroy(){const{tree:e}=this;this._closeMenu(),e.removeEventListener("click",this._onClick),e.removeEventListener("keydown",this._onKeyDown),e.removeEventListener("focus",this._onFocus,!0),e.removeEventListener("contextmenu",this._onContextMenu),document.removeEventListener("click",this._onDocumentClick),this._scheme&&this._scheme.removeListener(this._onScheme),this._scheme=null,e.parentNode&&e.parentNode.removeChild(e),this.leafs=Object.create(null),this._nodes=Object.create(null),this._selected=null,this._tabbable=null}_id(e){return e.getAttribute("data-vtree-id")}_newId(){let e;do{e="vtree-"+ ++this._count}while(this._nodes[e]);return e}_subtree(e){const{children:t}=e;for(let e=0;e<t.length;e++)if(o(t[e],"vtree-subtree"))return t[e]}_sync(e){const t=this._id(e),s=this.hasChildren(t);r(e,"vtree-has-children",s),s?e.setAttribute("aria-expanded",String(!o(e,"closed"))):e.removeAttribute("aria-expanded")}_syncList(e){e!==this.tree&&this._sync(e.parentNode)}_placeholder(){const{tree:t}=this,s=e(".vtree-placeholder",t)[0],i=!e("li.vtree-leaf",t).length;return i&&this.placeholder&&!s?l(t.appendChild(n("li","vtree-placeholder",{role:"none"})),this.placeholder):!i&&s&&t.removeChild(s),this}_visible(e){let t=e;for(let s=e.parentNode;s!==this.tree;s=s.parentNode)o(s,"vtree-leaf")&&o(s,"closed")&&(t=s);return t}_visibleLeaves(){const e=[],t=s=>{const{children:i}=s;for(let s=0;s<i.length;s++){const n=i[s];e.push(n);const l=this._subtree(n);l&&!o(n,"closed")&&t(l)}};return t(this.tree),e}_setTabbable(e){this._tabbable!==e&&(null!==this._tabbable&&this._nodes[this._tabbable].setAttribute("tabindex","-1"),this._tabbable=e,this._nodes[e].setAttribute("tabindex","0"))}_fixTabbable(){const t=this._tabbable??this._selected;if(null!==t)this._setTabbable(this._id(this._visible(this._nodes[t])));else{const t=e("li.vtree-leaf",this.tree)[0];t&&this._setTabbable(this._id(t))}}_focus(e){this._setTabbable(this._id(e)),e.focus()}_leafOf(e,t){let s=!t;for(let i=e;i&&i!==this.tree;i=i.parentNode){if(o(i,"vtree-contextmenu"))return;if(t&&o(i,t)&&(s=!0),o(i,"vtree-leaf"))return s&&this._nodes[this._id(i)]===i?i:void 0}}_click(e){const t=this._leafOf(e.target,"vtree-toggle");if(t)return void this.toggle(this._id(t));const s=this._leafOf(e.target,"vtree-leaf-label");if(!s)return;const i=this._id(s);!1===this.leafs[i].selectable?this.hasChildren(i)&&this.toggle(i):this.select(i)}_focusIn(e){const t=this._leafOf(e.target);t&&t===e.target&&this._setTabbable(this._id(t))}_keyDown(e){const t=e.target;if(!o(t,"vtree-leaf")||this._leafOf(t)!==t)return;const i=s(e),n=this._id(t),l=this.hasChildren(n),r=l&&!o(t,"closed"),h=this._visibleLeaves(),a=h.indexOf(t);if("ArrowDown"===i)a<h.length-1&&this._focus(h[a+1]);else if("ArrowUp"===i)a>0&&this._focus(h[a-1]);else if("Home"===i)this._focus(h[0]);else if("End"===i)this._focus(h[h.length-1]);else if("ArrowRight"===i)r?this._focus(h[a+1]):l&&this.open(n);else if("ArrowLeft"===i){const e=this.getParent(n);r?this.close(n):null!==e&&this._focus(this._nodes[e])}else if("Enter"===i||" "===i)!1!==this.leafs[n].selectable?this.select(n):l&&this.toggle(n);else{if(!("ContextMenu"===i||"F10"===i&&e.shiftKey))return;if(!this._options.contextmenu)return;this._openMenu(t)}e.preventDefault()}_contextMenu(e){const t=this._leafOf(e.target,"vtree-leaf-label");t&&(e.preventDefault(),e.stopPropagation(),this._openMenu(t))}_openMenu(t){for(const t of e(".vtree-contextmenu"))t.parentNode.removeChild(t);const i=this._id(t),o=e(".vtree-leaf-label",t)[0],r=n("menu","vtree-contextmenu",{role:"menu"});this._menu=r,r.style.top=`${o.offsetTop+o.getBoundingClientRect().height}px`,r.style.left=`${o.offsetLeft}px`,r.style.display="block";const h=this._options.contextmenu.map(e=>{const s=r.appendChild(n("li","vtree-contextmenu-item",{role:"menuitem",tabindex:"-1"}));return l(s,e.label,e.html),s.addEventListener("click",()=>{this._closeMenu(),t.focus(),e.action.call(e,i)}),s});r.addEventListener("keydown",e=>{const i=s(e),n=h.indexOf(e.target);if("ArrowDown"===i)h[(n+1)%h.length].focus();else if("ArrowUp"===i)h[(n+h.length-1)%h.length].focus();else if("Home"===i)h[0].focus();else if("End"===i)h[h.length-1].focus();else if("Enter"===i||" "===i)h[n].click();else{if("Escape"!==i&&"Tab"!==i)return;this._closeMenu(),t.focus()}e.preventDefault(),e.stopPropagation()}),t.appendChild(r),h.length&&h[0].focus()}_closeMenu(){const e=this._menu;e?.parentNode&&e.parentNode.removeChild(e),this._menu=null}}export{VanillaTree,VanillaTree as default};
|
package/package.json
CHANGED
|
@@ -1,24 +1,85 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vanillatree",
|
|
3
|
-
"version": "0.0
|
|
4
|
-
"description": "
|
|
5
|
-
"main": "vanillatree.js",
|
|
6
|
-
"scripts": {
|
|
7
|
-
"test": "echo \"Error: no test specified\" && exit 1"
|
|
8
|
-
},
|
|
9
|
-
"repository": {
|
|
10
|
-
"type": "git",
|
|
11
|
-
"url": "git+https://github.com/finom/vanillatree.git"
|
|
12
|
-
},
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A tree view with no dependencies: one small script, keyboard and screen reader support, IE 10 and up",
|
|
13
5
|
"keywords": [
|
|
14
6
|
"treeview",
|
|
7
|
+
"tree-view",
|
|
8
|
+
"tree",
|
|
15
9
|
"jstree",
|
|
16
|
-
"vanilla"
|
|
10
|
+
"vanilla",
|
|
11
|
+
"vanilla-js",
|
|
12
|
+
"accessibility",
|
|
13
|
+
"no-dependencies",
|
|
14
|
+
"typescript"
|
|
17
15
|
],
|
|
18
|
-
"
|
|
16
|
+
"homepage": "https://finom.github.io/vanillatree/",
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/finom/vanillatree.git"
|
|
20
|
+
},
|
|
21
|
+
"author": "Andrey Gubanov <andrey.a.gubanov@gmail.com>",
|
|
19
22
|
"license": "MIT",
|
|
20
23
|
"bugs": {
|
|
21
24
|
"url": "https://github.com/finom/vanillatree/issues"
|
|
22
25
|
},
|
|
23
|
-
"
|
|
26
|
+
"exports": {
|
|
27
|
+
".": {
|
|
28
|
+
"import": {
|
|
29
|
+
"types": "./dist/vanillatree.d.mts",
|
|
30
|
+
"default": "./dist/vanillatree.min.mjs"
|
|
31
|
+
},
|
|
32
|
+
"require": {
|
|
33
|
+
"types": "./dist/vanillatree.d.cts",
|
|
34
|
+
"default": "./dist/vanillatree.min.js"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"./vanillatree.css": "./dist/vanillatree.css",
|
|
38
|
+
"./package.json": "./package.json"
|
|
39
|
+
},
|
|
40
|
+
"main": "./dist/vanillatree.min.js",
|
|
41
|
+
"module": "./dist/vanillatree.min.mjs",
|
|
42
|
+
"types": "./dist/vanillatree.d.cts",
|
|
43
|
+
"style": "./dist/vanillatree.css",
|
|
44
|
+
"unpkg": "./dist/vanillatree.min.js",
|
|
45
|
+
"jsdelivr": "./dist/vanillatree.min.js",
|
|
46
|
+
"sideEffects": [
|
|
47
|
+
"*.css"
|
|
48
|
+
],
|
|
49
|
+
"files": [
|
|
50
|
+
"dist",
|
|
51
|
+
"CHANGELOG.md"
|
|
52
|
+
],
|
|
53
|
+
"scripts": {
|
|
54
|
+
"lint": "biome check --error-on-warnings .",
|
|
55
|
+
"typecheck": "tsc -p tsconfig.json",
|
|
56
|
+
"test": "vitest run",
|
|
57
|
+
"test:watch": "vitest",
|
|
58
|
+
"test:coverage": "vitest run --coverage",
|
|
59
|
+
"build": "node scripts/build.mjs",
|
|
60
|
+
"check:dist": "node scripts/check-dist.mjs && node scripts/check-legacy.mjs && node scripts/check-legacy-run.mjs && npm run test:dist",
|
|
61
|
+
"test:dist": "VANILLATREE_DIST=1 vitest run",
|
|
62
|
+
"build:demo": "node scripts/build-demo.mjs",
|
|
63
|
+
"patch": "node scripts/release.mjs patch",
|
|
64
|
+
"minor": "node scripts/release.mjs minor",
|
|
65
|
+
"major": "node scripts/release.mjs major",
|
|
66
|
+
"beta": "node scripts/release.mjs beta",
|
|
67
|
+
"beta-minor": "node scripts/release.mjs beta-minor",
|
|
68
|
+
"beta-major": "node scripts/release.mjs beta-major",
|
|
69
|
+
"prepublishOnly": "node scripts/ci-only.mjs"
|
|
70
|
+
},
|
|
71
|
+
"devDependencies": {
|
|
72
|
+
"@babel/core": "^8.0.7",
|
|
73
|
+
"@babel/preset-env": "^8.0.7",
|
|
74
|
+
"@biomejs/biome": "^2.5.15",
|
|
75
|
+
"@rollup/plugin-terser": "^1.0.0",
|
|
76
|
+
"@vitest/coverage-v8": "^5.0.4",
|
|
77
|
+
"acorn": "^8.19.0",
|
|
78
|
+
"acorn-walk": "^8.3.5",
|
|
79
|
+
"fast-check": "^4.10.2",
|
|
80
|
+
"jsdom": "^30.1.2",
|
|
81
|
+
"rollup": "^4.64.5",
|
|
82
|
+
"typescript": "^7.0.2",
|
|
83
|
+
"vitest": "^5.0.4"
|
|
84
|
+
}
|
|
24
85
|
}
|
package/demo.html
DELETED
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html>
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8">
|
|
5
|
-
<title>JS Bin</title>
|
|
6
|
-
<link rel="stylesheet" href="./vanillatree.css">
|
|
7
|
-
<script src="./vanillatree.js"></script>
|
|
8
|
-
|
|
9
|
-
</head>
|
|
10
|
-
<body>
|
|
11
|
-
<main></main>
|
|
12
|
-
<footer></footer>
|
|
13
|
-
<script>
|
|
14
|
-
const main = document.querySelector('main');
|
|
15
|
-
const info = document.querySelector('footer');
|
|
16
|
-
const tree = new VanillaTree( main, {
|
|
17
|
-
contextmenu: [{
|
|
18
|
-
label: 'Hey',
|
|
19
|
-
action: function(id) {
|
|
20
|
-
alert('Hey ' + id);
|
|
21
|
-
}
|
|
22
|
-
}, {
|
|
23
|
-
label: 'Blah',
|
|
24
|
-
action: function(id) {
|
|
25
|
-
alert('Blah ' + id);
|
|
26
|
-
}
|
|
27
|
-
}]
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
tree.add({
|
|
33
|
-
label: 'Label A',
|
|
34
|
-
id: 'a',
|
|
35
|
-
opened: true
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
tree.add({
|
|
39
|
-
label: 'Label B',
|
|
40
|
-
id: 'b'
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
tree.add({
|
|
44
|
-
label: 'Label A.A',
|
|
45
|
-
parent: 'a',
|
|
46
|
-
id: 'a.a',
|
|
47
|
-
opened: true,
|
|
48
|
-
selected: true
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
tree.add({
|
|
52
|
-
label: 'Label A.A.A',
|
|
53
|
-
parent: 'a.a'
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
tree.add({
|
|
57
|
-
label: 'Label A.A.B',
|
|
58
|
-
parent: 'a.a'
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
tree.add({
|
|
62
|
-
label: 'Label B.A',
|
|
63
|
-
parent: 'b'
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
main.addEventListener('vtree-open', function(evt) {
|
|
67
|
-
info.innerHTML = evt.detail.id + ' is opened';
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
main.addEventListener('vtree-close', function(evt) {
|
|
71
|
-
info.innerHTML = evt.detail.id + ' is closed';
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
main.addEventListener('vtree-select', function(evt) {
|
|
75
|
-
info.innerHTML = evt.detail.id + ' is selected';
|
|
76
|
-
});
|
|
77
|
-
</script>
|
|
78
|
-
</body>
|
|
79
|
-
</html>
|
package/vanillatree.css
DELETED
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
.vtree ul.vtree-subtree, .vtree li.vtree-leaf {
|
|
2
|
-
margin: 0;
|
|
3
|
-
padding: 0;
|
|
4
|
-
list-style-type: none;
|
|
5
|
-
position: relative;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
.vtree li.vtree-leaf {
|
|
9
|
-
background-position: -90px 0;
|
|
10
|
-
background-repeat: repeat-y;
|
|
11
|
-
min-height: 18px;
|
|
12
|
-
line-height: 18px;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
.vtree li.vtree-leaf::before {
|
|
16
|
-
content: '';
|
|
17
|
-
width: 18px;
|
|
18
|
-
height: 18px;
|
|
19
|
-
position: absolute;
|
|
20
|
-
background-position: -36px 0;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
.vtree li.vtree-leaf li.vtree-leaf {
|
|
24
|
-
margin-left: 18px;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
.vtree li.vtree-leaf:last-child {
|
|
28
|
-
background-image: none;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
.vtree li.vtree-leaf.closed ul.vtree-subtree {
|
|
32
|
-
display: none;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
.vtree li.vtree-leaf.vtree-has-children > span.vtree-toggle {
|
|
36
|
-
display: block;
|
|
37
|
-
width: 18px;
|
|
38
|
-
height: 18px;
|
|
39
|
-
background-position: -72px 0;
|
|
40
|
-
position: absolute;
|
|
41
|
-
left: 0;
|
|
42
|
-
top: 0;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
.vtree li.vtree-leaf.vtree-has-children.closed > span.vtree-toggle {
|
|
46
|
-
background-position: -54px 0;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
.vtree a.vtree-leaf-label {
|
|
50
|
-
line-height: 18px;
|
|
51
|
-
display: inline-block;
|
|
52
|
-
vertical-align: top;
|
|
53
|
-
cursor: pointer;
|
|
54
|
-
max-width: 100%;
|
|
55
|
-
margin-left: 18px;
|
|
56
|
-
padding: 0 2px;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
.vtree li.vtree-leaf a.vtree-leaf-label:hover {
|
|
60
|
-
background-color: #e7f4f9;
|
|
61
|
-
outline: 1px solid #d8f0fa;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
.vtree li.vtree-leaf.vtree-selected > a.vtree-leaf-label {
|
|
65
|
-
background-color: #beebff;
|
|
66
|
-
outline: 1px solid #99defd;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
.vtree-contextmenu {
|
|
70
|
-
position: absolute;
|
|
71
|
-
z-index: 9999999;
|
|
72
|
-
border: solid 1px #ccc;
|
|
73
|
-
background: #eee;
|
|
74
|
-
padding: 0px;
|
|
75
|
-
margin: 0px;
|
|
76
|
-
display: none;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
.vtree-contextmenu li {
|
|
80
|
-
list-style: none;
|
|
81
|
-
padding: 1px 5px;
|
|
82
|
-
margin: 0px;
|
|
83
|
-
color: #333;
|
|
84
|
-
line-height: 20px;
|
|
85
|
-
height: 20px;
|
|
86
|
-
cursor: default;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
.vtree-contextmenu li:hover {
|
|
90
|
-
color: #fff;
|
|
91
|
-
background-color: #3399ff;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
.vtree li.vtree-leaf, .vtree li.vtree-leaf::before, .vtree li.vtree-leaf.vtree-has-children > span.vtree-toggle {
|
|
95
|
-
background-image: url(data:image/png;base64,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);
|
|
96
|
-
}
|
package/vanillatree.js
DELETED
|
@@ -1,212 +0,0 @@
|
|
|
1
|
-
(function (root, factory) {
|
|
2
|
-
if (typeof define == 'function' && define.amd) {
|
|
3
|
-
define( factory );
|
|
4
|
-
} else if (typeof module === 'object' && module.exports) {
|
|
5
|
-
module.exports = factory();
|
|
6
|
-
} else {
|
|
7
|
-
root.VanillaTree = factory();
|
|
8
|
-
}
|
|
9
|
-
}(this, function () {
|
|
10
|
-
"use strict";
|
|
11
|
-
// Look at the Balalaika https://github.com/finom/balalaika
|
|
12
|
-
var $=function(n,e,k,h,p,m,l,b,d,g,f,c){c=function(a,b){return new c.i(a,b)};c.i=function(a,d){k.push.apply(this,a?a.nodeType||a==n?[a]:""+a===a?/</.test(a)?((b=e.createElement(d||"q")).innerHTML=a,b.children):(d&&c(d)[0]||e).querySelectorAll(a):/f/.test(typeof a)?/c/.test(e.readyState)?a():c(e).on("DOMContentLoaded",a):a:k)};c.i[f="prototype"]=(c.extend=function(a){g=arguments;for(b=1;b<g.length;b++)if(f=g[b])for(d in f)a[d]=f[d];return a})(c.fn=c[f]=k,{on:function(a,d){a=a.split(h);this.map(function(c){(h[b=a[0]+(c.b$=c.b$||++p)]=h[b]||[]).push([d,a[1]]);c["add"+m](a[0],d)});return this},off:function(a,c){a=a.split(h);f="remove"+m;this.map(function(e){if(b=(g=h[a[0]+e.b$])&&g.length)for(;d=g[--b];)c&&c!=d[0]||a[1]&&a[1]!=d[1]||(e[f](a[0],d[0]),g.splice(b,1));else!a[1]&&e[f](a[0],c)});return this},is:function(a){d=(b=this[0])&&(b.matches||b["webkit"+l]||b["moz"+l]||b["ms"+l]);return!!d&&d.call(b,a)}});return c}(window,document,[],/\.(.+)/,0,"EventListener","MatchesSelector");
|
|
13
|
-
|
|
14
|
-
var create = function( tagName, props ) {
|
|
15
|
-
return $.extend( document.createElement( tagName ), props );
|
|
16
|
-
},
|
|
17
|
-
Tree = function( s, options ) {
|
|
18
|
-
var _this = this,
|
|
19
|
-
container = _this.container = $( s )[ 0 ],
|
|
20
|
-
tree = _this.tree = container.appendChild( create( 'ul', {
|
|
21
|
-
className: 'vtree'
|
|
22
|
-
}) );
|
|
23
|
-
|
|
24
|
-
_this.placeholder = options && options.placeholder;
|
|
25
|
-
_this._placeholder();
|
|
26
|
-
_this.leafs = {};
|
|
27
|
-
tree.addEventListener( 'click', function( evt ) {
|
|
28
|
-
if( $( evt.target ).is( '.vtree-leaf-label' ) ) {
|
|
29
|
-
_this.select( evt.target.parentNode.getAttribute('data-vtree-id') );
|
|
30
|
-
} else if( $( evt.target ).is( '.vtree-toggle' ) ) {
|
|
31
|
-
_this.toggle( evt.target.parentNode.getAttribute('data-vtree-id') );
|
|
32
|
-
}
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
if( options && options.contextmenu ) {
|
|
36
|
-
tree.addEventListener( 'contextmenu', function( evt ) {
|
|
37
|
-
var menu;
|
|
38
|
-
$( '.vtree-contextmenu' ).forEach( function( menu ) {
|
|
39
|
-
menu.parentNode.removeChild( menu );
|
|
40
|
-
});
|
|
41
|
-
if( $( evt.target ).is( '.vtree-leaf-label' ) ) {
|
|
42
|
-
evt.preventDefault();
|
|
43
|
-
evt.stopPropagation();
|
|
44
|
-
menu = create( 'menu', {
|
|
45
|
-
className: 'vtree-contextmenu'
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
var rect = evt.target.getBoundingClientRect();
|
|
49
|
-
$.extend(menu.style, {
|
|
50
|
-
top: (evt.target.offsetTop + rect.height).toString() + "px",
|
|
51
|
-
left: evt.target.offsetLeft.toString() + "px",
|
|
52
|
-
display: 'block'
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
options.contextmenu.forEach( function( item ) {
|
|
56
|
-
menu.appendChild( create( 'li', {
|
|
57
|
-
className: 'vtree-contextmenu-item',
|
|
58
|
-
innerHTML: item.label
|
|
59
|
-
}) ).addEventListener( 'click', item.action.bind( item, evt.target.parentNode.getAttribute('data-vtree-id') ) );
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
evt.target.parentNode.appendChild( menu );
|
|
63
|
-
}
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
document.addEventListener( 'click', function( evt ) {
|
|
67
|
-
if(evt.button === 2) return;
|
|
68
|
-
$( '.vtree-contextmenu' ).forEach( function( menu ) {
|
|
69
|
-
menu.parentNode.removeChild( menu );
|
|
70
|
-
});
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
Tree.prototype = {
|
|
76
|
-
constructor: Tree,
|
|
77
|
-
_dispatch: function( name, id ) {
|
|
78
|
-
var event;
|
|
79
|
-
try {
|
|
80
|
-
event = new CustomEvent( 'vtree-' + name, {
|
|
81
|
-
bubbles: true,
|
|
82
|
-
cancelable: true,
|
|
83
|
-
detail: {
|
|
84
|
-
id: id
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
|
-
} catch(e) {
|
|
88
|
-
event = document.createEvent( 'CustomEvent' );
|
|
89
|
-
event.initCustomEvent( 'vtree-' + name, true, true, { id: id });
|
|
90
|
-
}
|
|
91
|
-
( this.getLeaf( id, true ) || this.tree )
|
|
92
|
-
.dispatchEvent( event );
|
|
93
|
-
return this;
|
|
94
|
-
},
|
|
95
|
-
_placeholder: function() {
|
|
96
|
-
var p;
|
|
97
|
-
if( !this.tree.children.length && this.placeholder ) {
|
|
98
|
-
this.tree.innerHTML = '<li class="vtree-placeholder">' + this.placeholder + '</li>'
|
|
99
|
-
} else if( p = this.tree.querySelector( '.vtree-placeholder' ) ) {
|
|
100
|
-
this.tree.removeChild( p );
|
|
101
|
-
}
|
|
102
|
-
return this;
|
|
103
|
-
},
|
|
104
|
-
getLeaf: function( id, notThrow ) {
|
|
105
|
-
var leaf = $( '[data-vtree-id="' + id + '"]', this.tree )[ 0 ];
|
|
106
|
-
if( !notThrow && !leaf ) throw Error( 'No VanillaTree leaf with id "' + id + '"' )
|
|
107
|
-
return leaf;
|
|
108
|
-
},
|
|
109
|
-
getChildList: function( id ) {
|
|
110
|
-
var list,
|
|
111
|
-
parent;
|
|
112
|
-
if( id ) {
|
|
113
|
-
parent = this.getLeaf( id );
|
|
114
|
-
if( !( list = $( 'ul', parent )[ 0 ] ) ) {
|
|
115
|
-
list = parent.appendChild( create( 'ul', {
|
|
116
|
-
className: 'vtree-subtree'
|
|
117
|
-
}) );
|
|
118
|
-
}
|
|
119
|
-
} else {
|
|
120
|
-
list = this.tree;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
return list;
|
|
124
|
-
},
|
|
125
|
-
add: function( options ) {
|
|
126
|
-
var id,
|
|
127
|
-
leaf = create( 'li', {
|
|
128
|
-
className: 'vtree-leaf'
|
|
129
|
-
}),
|
|
130
|
-
parentList = this.getChildList( options.parent );
|
|
131
|
-
|
|
132
|
-
leaf.setAttribute( 'data-vtree-id', id = options.id || Math.random() );
|
|
133
|
-
|
|
134
|
-
leaf.appendChild( create( 'span', {
|
|
135
|
-
className: 'vtree-toggle'
|
|
136
|
-
}) );
|
|
137
|
-
|
|
138
|
-
leaf.appendChild( create( 'a', {
|
|
139
|
-
className: 'vtree-leaf-label',
|
|
140
|
-
innerHTML: options.label
|
|
141
|
-
}) );
|
|
142
|
-
|
|
143
|
-
parentList.appendChild( leaf );
|
|
144
|
-
|
|
145
|
-
if( parentList !== this.tree ) {
|
|
146
|
-
parentList.parentNode.classList.add( 'vtree-has-children' );
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
this.leafs[ id ] = options;
|
|
150
|
-
|
|
151
|
-
if( !options.opened ) {
|
|
152
|
-
this.close( id );
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
if( options.selected ) {
|
|
156
|
-
this.select( id );
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
return this._placeholder()._dispatch( 'add', id );
|
|
160
|
-
},
|
|
161
|
-
move: function( id, parentId ) {
|
|
162
|
-
var leaf = this.getLeaf( id ),
|
|
163
|
-
oldParent = leaf.parentNode,
|
|
164
|
-
newParent = this.getLeaf( parentId, true );
|
|
165
|
-
|
|
166
|
-
if( newParent ) {
|
|
167
|
-
newParent.classList.add( 'vtree-has-children' );
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
this.getChildList( parentId ).appendChild( leaf );
|
|
171
|
-
oldParent.parentNode.classList.toggle( 'vtree-has-children', !!oldParent.children.length );
|
|
172
|
-
|
|
173
|
-
return this._dispatch( 'move', id );
|
|
174
|
-
},
|
|
175
|
-
remove: function( id ) {
|
|
176
|
-
var leaf = this.getLeaf( id ),
|
|
177
|
-
oldParent = leaf.parentNode;
|
|
178
|
-
oldParent.removeChild( leaf );
|
|
179
|
-
oldParent.parentNode.classList.toggle( 'vtree-has-children', !!oldParent.children.length );
|
|
180
|
-
|
|
181
|
-
return this._placeholder()._dispatch( 'remove', id );
|
|
182
|
-
},
|
|
183
|
-
open: function( id ) {
|
|
184
|
-
this.getLeaf( id ).classList.remove( 'closed' );
|
|
185
|
-
return this._dispatch( 'open', id );
|
|
186
|
-
},
|
|
187
|
-
close: function( id ) {
|
|
188
|
-
this.getLeaf( id ).classList.add( 'closed' );
|
|
189
|
-
return this._dispatch( 'close', id );
|
|
190
|
-
},
|
|
191
|
-
toggle: function( id ) {
|
|
192
|
-
return this[ this.getLeaf( id ).classList.contains( 'closed' ) ? 'open' : 'close' ]( id );
|
|
193
|
-
},
|
|
194
|
-
select: function( id ) {
|
|
195
|
-
var leaf = this.getLeaf( id );
|
|
196
|
-
|
|
197
|
-
if( !leaf.classList.contains( 'vtree-selected' ) ) {
|
|
198
|
-
$( 'li.vtree-leaf', this.tree ).forEach( function( leaf ) {
|
|
199
|
-
leaf.classList.remove( 'vtree-selected' );
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
leaf.classList.add( 'vtree-selected' );
|
|
203
|
-
this._dispatch( 'select', id );
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
return this;
|
|
207
|
-
}
|
|
208
|
-
};
|
|
209
|
-
|
|
210
|
-
return Tree;
|
|
211
|
-
// Look at the Balalaika https://github.com/finom/balalaika
|
|
212
|
-
}));
|
package/vanillatree.min.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
!function(e,t){"function"==typeof define&&define.amd?define(t):"object"==typeof module&&module.exports?module.exports=t():e.VanillaTree=t()}(this,function(){"use strict";var n,i,r,a,s,l,t,o,c,d,h,u,p=(n=window,i=document,r=[],a=/\.(.+)/,s=0,l="EventListener",t="MatchesSelector",(u=function(e,t){return new u.i(e,t)}).i=function(e,t){r.push.apply(this,e?e.nodeType||e==n?[e]:""+e===e?/</.test(e)?((o=i.createElement(t||"q")).innerHTML=e,o.children):(t&&u(t)[0]||i).querySelectorAll(e):/f/.test(typeof e)?/c/.test(i.readyState)?e():u(i).on("DOMContentLoaded",e):e:r)},u.i[h="prototype"]=(u.extend=function(e){for(d=arguments,o=1;o<d.length;o++)if(h=d[o])for(c in h)e[c]=h[c];return e})(u.fn=u[h]=r,{on:function(t,n){return t=t.split(a),this.map(function(e){(a[o=t[0]+(e.b$=e.b$||++s)]=a[o]||[]).push([n,t[1]]),e["add"+l](t[0],n)}),this},off:function(t,n){return t=t.split(a),h="remove"+l,this.map(function(e){if(o=(d=a[t[0]+e.b$])&&d.length)for(;c=d[--o];)n&&n!=c[0]||t[1]&&t[1]!=c[1]||(e[h](t[0],c[0]),d.splice(o,1));else!t[1]&&e[h](t[0],n)}),this},is:function(e){return!!(c=(o=this[0])&&(o.matches||o["webkit"+t]||o["moz"+t]||o["ms"+t]))&&c.call(o,e)}}),u),f=function(e,t){return p.extend(document.createElement(e),t)},e=function(e,i){var t=this,n=t.container=p(e)[0],r=t.tree=n.appendChild(f("ul",{className:"vtree"}));t.placeholder=i&&i.placeholder,t._placeholder(),t.leafs={},r.addEventListener("click",function(e){p(e.target).is(".vtree-leaf-label")?t.select(e.target.parentNode.getAttribute("data-vtree-id")):p(e.target).is(".vtree-toggle")&&t.toggle(e.target.parentNode.getAttribute("data-vtree-id"))}),i&&i.contextmenu&&(r.addEventListener("contextmenu",function(t){var n;if(p(".vtree-contextmenu").forEach(function(e){e.parentNode.removeChild(e)}),p(t.target).is(".vtree-leaf-label")){t.preventDefault(),t.stopPropagation(),n=f("menu",{className:"vtree-contextmenu"});var e=t.target.getBoundingClientRect();p.extend(n.style,{top:(t.target.offsetTop+e.height).toString()+"px",left:t.target.offsetLeft.toString()+"px",display:"block"}),i.contextmenu.forEach(function(e){n.appendChild(f("li",{className:"vtree-contextmenu-item",innerHTML:e.label})).addEventListener("click",e.action.bind(e,t.target.parentNode.getAttribute("data-vtree-id")))}),t.target.parentNode.appendChild(n)}}),document.addEventListener("click",function(e){2!==e.button&&p(".vtree-contextmenu").forEach(function(e){e.parentNode.removeChild(e)})}))};return e.prototype={constructor:e,_dispatch:function(t,n){var i;try{i=new CustomEvent("vtree-"+t,{bubbles:!0,cancelable:!0,detail:{id:n}})}catch(e){(i=document.createEvent("CustomEvent")).initCustomEvent("vtree-"+t,!0,!0,{id:n})}return(this.getLeaf(n,!0)||this.tree).dispatchEvent(i),this},_placeholder:function(){var e;return!this.tree.children.length&&this.placeholder?this.tree.innerHTML='<li class="vtree-placeholder">'+this.placeholder+"</li>":(e=this.tree.querySelector(".vtree-placeholder"))&&this.tree.removeChild(e),this},getLeaf:function(e,t){var n=p('[data-vtree-id="'+e+'"]',this.tree)[0];if(!t&&!n)throw Error('No VanillaTree leaf with id "'+e+'"');return n},getChildList:function(e){var t,n;return e?(n=this.getLeaf(e),(t=p("ul",n)[0])||(t=n.appendChild(f("ul",{className:"vtree-subtree"})))):t=this.tree,t},add:function(e){var t,n=f("li",{className:"vtree-leaf"}),i=this.getChildList(e.parent);return n.setAttribute("data-vtree-id",t=e.id||Math.random()),n.appendChild(f("span",{className:"vtree-toggle"})),n.appendChild(f("a",{className:"vtree-leaf-label",innerHTML:e.label})),i.appendChild(n),i!==this.tree&&i.parentNode.classList.add("vtree-has-children"),(this.leafs[t]=e).opened||this.close(t),e.selected&&this.select(t),this._placeholder()._dispatch("add",t)},move:function(e,t){var n=this.getLeaf(e),i=n.parentNode,r=this.getLeaf(t,!0);return r&&r.classList.add("vtree-has-children"),this.getChildList(t).appendChild(n),i.parentNode.classList.toggle("vtree-has-children",!!i.children.length),this._dispatch("move",e)},remove:function(e){var t=this.getLeaf(e),n=t.parentNode;return n.removeChild(t),n.parentNode.classList.toggle("vtree-has-children",!!n.children.length),this._placeholder()._dispatch("remove",e)},open:function(e){return this.getLeaf(e).classList.remove("closed"),this._dispatch("open",e)},close:function(e){return this.getLeaf(e).classList.add("closed"),this._dispatch("close",e)},toggle:function(e){return this[this.getLeaf(e).classList.contains("closed")?"open":"close"](e)},select:function(e){var t=this.getLeaf(e);return t.classList.contains("vtree-selected")||(p("li.vtree-leaf",this.tree).forEach(function(e){e.classList.remove("vtree-selected")}),t.classList.add("vtree-selected"),this._dispatch("select",e)),this}},e});
|