tree.js
18526
src/test_compare.html
Normal file
3197
src/test_snip.js
Normal file
BIN
src/tree/icons/array.png
Normal file
After Width: | Height: | Size: 183 B |
BIN
src/tree/icons/binary.png
Normal file
After Width: | Height: | Size: 203 B |
BIN
src/tree/icons/boolean.png
Normal file
After Width: | Height: | Size: 351 B |
BIN
src/tree/icons/bprocess.png
Normal file
After Width: | Height: | Size: 179 B |
BIN
src/tree/icons/catalog.png
Normal file
After Width: | Height: | Size: 180 B |
BIN
src/tree/icons/close.png
Normal file
After Width: | Height: | Size: 282 B |
BIN
src/tree/icons/close_hover.png
Normal file
After Width: | Height: | Size: 284 B |
BIN
src/tree/icons/date.png
Normal file
After Width: | Height: | Size: 198 B |
BIN
src/tree/icons/document.png
Normal file
After Width: | Height: | Size: 213 B |
BIN
src/tree/icons/empty.png
Normal file
After Width: | Height: | Size: 126 B |
BIN
src/tree/icons/enum.png
Normal file
After Width: | Height: | Size: 189 B |
BIN
src/tree/icons/int.png
Normal file
After Width: | Height: | Size: 272 B |
BIN
src/tree/icons/keyvalue.png
Normal file
After Width: | Height: | Size: 179 B |
BIN
src/tree/icons/null.png
Normal file
After Width: | Height: | Size: 201 B |
BIN
src/tree/icons/open.png
Normal file
After Width: | Height: | Size: 281 B |
BIN
src/tree/icons/open_hover.png
Normal file
After Width: | Height: | Size: 282 B |
BIN
src/tree/icons/picture.png
Normal file
After Width: | Height: | Size: 231 B |
BIN
src/tree/icons/query.png
Normal file
After Width: | Height: | Size: 422 B |
BIN
src/tree/icons/storage.png
Normal file
After Width: | Height: | Size: 180 B |
BIN
src/tree/icons/string.png
Normal file
After Width: | Height: | Size: 248 B |
BIN
src/tree/icons/structure.png
Normal file
After Width: | Height: | Size: 180 B |
BIN
src/tree/icons/table.png
Normal file
After Width: | Height: | Size: 181 B |
BIN
src/tree/icons/tabular.png
Normal file
After Width: | Height: | Size: 201 B |
BIN
src/tree/icons/task.png
Normal file
After Width: | Height: | Size: 180 B |
BIN
src/tree/icons/text.png
Normal file
After Width: | Height: | Size: 434 B |
BIN
src/tree/icons/tree.png
Normal file
After Width: | Height: | Size: 191 B |
BIN
src/tree/icons/undefined.png
Normal file
After Width: | Height: | Size: 269 B |
BIN
src/tree/icons/uuid.png
Normal file
After Width: | Height: | Size: 175 B |
BIN
src/tree/loading.gif
Normal file
After Width: | Height: | Size: 1.8 KiB |
235
src/tree/tree.css
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
#display {
|
||||||
|
height: 0;
|
||||||
|
display: none;
|
||||||
|
font-family: "Courier new";
|
||||||
|
}
|
||||||
|
|
||||||
|
#display.dark {
|
||||||
|
background-color: #1e1e1e;
|
||||||
|
}
|
||||||
|
|
||||||
|
#display-header {
|
||||||
|
position: relative;
|
||||||
|
height: 22px;
|
||||||
|
width: 100%;
|
||||||
|
border-top: solid 1px #ddd;
|
||||||
|
border-bottom: solid 1px #ddd;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
#display.dark > #display-header {
|
||||||
|
background-color: #1e1e1e;
|
||||||
|
border-color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
#display-title {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #161616;
|
||||||
|
margin-left: 10px;
|
||||||
|
line-height: 22px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
#display.dark #display-title {
|
||||||
|
color: #d4d4d4;
|
||||||
|
}
|
||||||
|
|
||||||
|
#variables-display {
|
||||||
|
overflow: auto;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#variables-display::-webkit-scrollbar {
|
||||||
|
width: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#variables-display::-webkit-scrollbar-thumb {
|
||||||
|
border-radius: 100px;
|
||||||
|
background-color: #aaa;
|
||||||
|
border: 4px solid rgba(0, 0, 0, 0);
|
||||||
|
background-clip: padding-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
#variables-tree {
|
||||||
|
padding: 15px;
|
||||||
|
width: 100%;
|
||||||
|
font-size: 10pt;
|
||||||
|
overflow: auto;
|
||||||
|
cursor: pointer;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
#variables-tree details > summary {
|
||||||
|
list-style: none;
|
||||||
|
padding: 3px;
|
||||||
|
margin: 2px 0;
|
||||||
|
color: #720501;
|
||||||
|
}
|
||||||
|
|
||||||
|
#display.dark #variables-tree details > summary {
|
||||||
|
color: #c3602c;
|
||||||
|
}
|
||||||
|
|
||||||
|
#variables-tree details > summary:hover {
|
||||||
|
background-color: #b8d9ff;
|
||||||
|
transition: all .1s
|
||||||
|
}
|
||||||
|
|
||||||
|
#display.dark #variables-tree details > summary:hover {
|
||||||
|
background-color: #424242;
|
||||||
|
}
|
||||||
|
|
||||||
|
#variables-tree details > summary::marker, #variables-tree details > summary::-webkit-details-marker {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#variables-tree summary::marker, #variables-tree summary::-webkit-details-marker {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
#variables-tree summary.final:before {
|
||||||
|
background-image: url('./icons/empty.png');
|
||||||
|
}
|
||||||
|
|
||||||
|
#variables-tree summary:before {
|
||||||
|
content: '.';
|
||||||
|
padding-left: 7px;
|
||||||
|
background-image: url('./icons/close.png');
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
color: transparent;
|
||||||
|
margin: 0 5px 0 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#variables-tree summary.final:hover:before {
|
||||||
|
background-image: url('./icons/empty.png');
|
||||||
|
}
|
||||||
|
|
||||||
|
#variables-tree summary:hover:before {
|
||||||
|
background-image: url('./icons/close_hover.png');
|
||||||
|
}
|
||||||
|
|
||||||
|
#variables-tree details > summary.loading:before {
|
||||||
|
background-image: url("./loading.gif") !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#variables-tree details[open] > summary:before {
|
||||||
|
background-image: url('./icons/open.png');
|
||||||
|
}
|
||||||
|
|
||||||
|
#variables-tree details[open] > summary:hover:before {
|
||||||
|
background-image: url('./icons/open_hover.png');
|
||||||
|
}
|
||||||
|
|
||||||
|
#variables-tree summary .icon {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
#variables-tree summary:only-child::-webkit-details-marker {
|
||||||
|
color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
#variables-tree details details {
|
||||||
|
margin-left: 20px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
#variables-tree .label {
|
||||||
|
font-size: 14pt;
|
||||||
|
font-weight:bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
#variables-tree a {
|
||||||
|
color: #0000ee;
|
||||||
|
text-decoration: none;
|
||||||
|
padding: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#variables-tree a:hover, #variables-tree a:visited:hover {
|
||||||
|
color: #ad0404;
|
||||||
|
}
|
||||||
|
|
||||||
|
#variables-tree a:visited {
|
||||||
|
color: #0000ee;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#display.dark #variables-tree a {
|
||||||
|
color: #9acd32;
|
||||||
|
}
|
||||||
|
|
||||||
|
#display.dark #variables-tree a:visited {
|
||||||
|
color: #9acd32;
|
||||||
|
}
|
||||||
|
|
||||||
|
#display.dark #variables-tree a:hover, #display.dark #variables-tree a:visited:hover {
|
||||||
|
color: #ff2c2c;
|
||||||
|
}
|
||||||
|
|
||||||
|
#variables-tree .value, .equal {
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
#variables-tree .value, .equal {
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
#display.dark #variables-tree .value {
|
||||||
|
color: #b8d9ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
#display.dark #variables-tree .equal {
|
||||||
|
color: #959595;
|
||||||
|
}
|
||||||
|
|
||||||
|
#variables-tree .type {
|
||||||
|
color: #959595;
|
||||||
|
}
|
||||||
|
|
||||||
|
#variables-tree .type:before {
|
||||||
|
content: '{';
|
||||||
|
}
|
||||||
|
|
||||||
|
#variables-tree .type:after {
|
||||||
|
content: '}';
|
||||||
|
}
|
||||||
|
|
||||||
|
#display-close {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 3px;
|
||||||
|
width: 15px;
|
||||||
|
height: 20px;
|
||||||
|
opacity: 0.3;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
#display-close:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#display-close:before, #display-close:after {
|
||||||
|
position: absolute;
|
||||||
|
content: ' ';
|
||||||
|
height: 12px;
|
||||||
|
width: 2px;
|
||||||
|
top: 2px;
|
||||||
|
background-color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark #display-close:before, .dark #display-close:after {
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
#display-close:before {
|
||||||
|
transform: rotate(45deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#display-close:after {
|
||||||
|
transform: rotate(-45deg);
|
||||||
|
}
|
114
src/tree/tree.html
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>Treeview</title>
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
|
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
|
||||||
|
<link href="./tree.css" rel="stylesheet">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<h1>Variables tree</h1>
|
||||||
|
<section>
|
||||||
|
<div id="variables-tree"></div>
|
||||||
|
</section>
|
||||||
|
<script src="./tree.js"></script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
let treeview = new Treeview("#variables-tree", null, "./icons/");
|
||||||
|
let cat = {
|
||||||
|
ПриходнаяНакладная: {
|
||||||
|
label: "ПриходнаяНакладная",
|
||||||
|
value: '<a href="#1">Приходная накладная 000-2398 от 22.08.2022 21:32:59</a>',
|
||||||
|
type: "ДокументСсылка.ПриходнаяНакладаная",
|
||||||
|
children: {
|
||||||
|
"Номер": { label: "Код", value: "000-2398", type: "Число", icon: "int.png" },
|
||||||
|
"Дата": { label: "Дата", value: "22.08.2022 21:32:59", type: "Дата", icon: "date.png" },
|
||||||
|
"Организация": {
|
||||||
|
label: "Организация",
|
||||||
|
value: "Рога и копыта",
|
||||||
|
type: "СправочникСсылка.Организации",
|
||||||
|
children: {
|
||||||
|
"Код": { label: "Код", value: "0000001", type: "Число", icon: "int.png" },
|
||||||
|
"ИНН": { label: "ИНН", value: "771111111", type: "Строка", icon: "string.png" },
|
||||||
|
"КПП": { label: "КПП", value: "770000001", type: "Строка", icon: "string.png" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Контрагент": {
|
||||||
|
label: "Контрагент",
|
||||||
|
value: '<a href="#1">Иванов И.И."</a>',
|
||||||
|
type: "СправочникСсылка.Контрагенты",
|
||||||
|
children: {
|
||||||
|
"Код": { label: "Код", value: "0000052", type: "Число" },
|
||||||
|
"ИНН": { label: "ИНН", value: "781111111", type: "Строка" },
|
||||||
|
"КПП": { label: "КПП", value: "780000001", type: "Строка" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Товары": {
|
||||||
|
label: "Товары",
|
||||||
|
type: "ТабличнаяЧасть",
|
||||||
|
children: {
|
||||||
|
"Строка 0": {
|
||||||
|
label: "Строка 0",
|
||||||
|
type: "СтрокаТабличнойЧасти",
|
||||||
|
children: {
|
||||||
|
"НомерСтроки": { label: "НомерСтроки", value: "1", type: "Число" },
|
||||||
|
"Товар": { label: "Товар", value: "Товар №1", type: "СправочникСсылка.Товары" },
|
||||||
|
"Количество": { label: "Количество", value: "5", type: "Число" },
|
||||||
|
"Цена": { label: "Цена", value: "100", type: "Число" },
|
||||||
|
"Сумма": { label: "Сумма", value: "500", type: "Число" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Строка 1": {
|
||||||
|
label: "Строка 1",
|
||||||
|
type: "СтрокаТабличнойЧасти",
|
||||||
|
children: {
|
||||||
|
"НомерСтроки": { label: "НомерСтроки", value: "2", type: "Число" },
|
||||||
|
"Товар": { label: "Товар", value: "Товар №2", type: "СправочникСсылка.Товары" },
|
||||||
|
"Количество": { label: "Количество", value: "3", type: "Число" },
|
||||||
|
"Цена": { label: "Цена", value: "50", type: "Число" },
|
||||||
|
"Сумма": { label: "Сумма", value: "150", type: "Число" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Строка 2": {
|
||||||
|
label: "Строка 2",
|
||||||
|
type: "СтрокаТабличнойЧасти",
|
||||||
|
children: {
|
||||||
|
"НомерСтроки": { label: "НомерСтроки", value: "3", type: "Число" },
|
||||||
|
"Товар": { label: "Товар", value: "Товар №3", type: "СправочникСсылка.Товары" },
|
||||||
|
"Количество": { label: "Количество", value: "6", type: "Число" },
|
||||||
|
"Цена": { label: "Цена", value: "90", type: "Число" },
|
||||||
|
"Сумма": { label: "Сумма", value: "540", type: "Число" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
НачалоПериода: {
|
||||||
|
label: "НачалоПериода",
|
||||||
|
value: "01.08.2022 00:00:00",
|
||||||
|
type: "Дата"
|
||||||
|
},
|
||||||
|
КоличествоТоваров: {
|
||||||
|
label: "КоличествоТоваров",
|
||||||
|
value: "14",
|
||||||
|
type: "Число"
|
||||||
|
},
|
||||||
|
ТекущийПартнер: {
|
||||||
|
label: "ТекущийПартнер",
|
||||||
|
value: "",
|
||||||
|
type: ""
|
||||||
|
},
|
||||||
|
Строка: {
|
||||||
|
label: "Строка",
|
||||||
|
value: "Это какое-то предложение",
|
||||||
|
type: "Строка",
|
||||||
|
icon: "string.png"
|
||||||
|
},
|
||||||
|
|
||||||
|
};
|
||||||
|
treeview.replaceData(cat);
|
||||||
|
</script>
|
||||||
|
</body>
|
100
src/tree/tree.js
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
class Treeview {
|
||||||
|
constructor(treeviewId, editor, imageBase) {
|
||||||
|
this.treeviewId = treeviewId;
|
||||||
|
this.editor = editor;
|
||||||
|
this.selected = null;
|
||||||
|
this.imageBase = imageBase;
|
||||||
|
document.querySelector(this.treeviewId).addEventListener("click", (event) => {
|
||||||
|
this.on("click", event);
|
||||||
|
});
|
||||||
|
document.querySelector(this.treeviewId)
|
||||||
|
};
|
||||||
|
on(eventName, eventData) {
|
||||||
|
switch (eventName) {
|
||||||
|
case "click": {
|
||||||
|
if (eventData.target.tagName == 'A') {
|
||||||
|
eventData.preventDefault();
|
||||||
|
let element = eventData.target;
|
||||||
|
if (this.editor) {
|
||||||
|
this.editor.sendEvent("EVENT_ON_LINK_CLICK", { label: element.innerText, href: element.getAttribute('href') });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (eventData.target.nodeName == 'SUMMARY' && !eventData.target.parentNode.hasAttribute("open")) {
|
||||||
|
if (eventData.target.dataset.requested == "false" && !eventData.target.classList.contains('final')) {
|
||||||
|
eventData.target.classList.add('loading');
|
||||||
|
eventData.preventDefault();
|
||||||
|
if (this.editor) {
|
||||||
|
let request = {
|
||||||
|
variableName: eventData.target.dataset.label,
|
||||||
|
variableId: eventData.target.id,
|
||||||
|
variablePath: eventData.target.dataset.path
|
||||||
|
};
|
||||||
|
this.editor.sendEvent("EVENT_GET_VARIABLE_DATA", request);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setTimeout(() => {
|
||||||
|
eventData.target.dataset.requested = true;
|
||||||
|
this.open(eventData.target.id);
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
appendData(data, targetId) {
|
||||||
|
if (targetId != null) {
|
||||||
|
let target = document.getElementById(targetId);
|
||||||
|
target.parentNode.innerHTML += this.parseData(data)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let target = document.querySelector(this.treeviewId);
|
||||||
|
target.innerHTML += this.parseData(data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
replaceData(data, targetId) {
|
||||||
|
if (targetId != null) {
|
||||||
|
let target = document.getElementById(targetId);
|
||||||
|
target.parentNode.outerHTML = this.parseData(data)
|
||||||
|
target.dataset.requested = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let target = document.querySelector(this.treeviewId);
|
||||||
|
target.innerHTML = this.parseData(data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
parseData(data) {
|
||||||
|
let me = this;
|
||||||
|
let buf = Object.keys(data).map((key) =>
|
||||||
|
`<details><summary id="${key}" data-label="${data[key].label}" data-requested="false" data-path="${data[key].path}" class="${data[key].class}">
|
||||||
|
<img class="icon" src="${me.imageBase}${data[key].icon ? data[key].icon : data[key].children ? 'structure.png' : 'undefined.png'}"> </img>
|
||||||
|
${data[key].label}<span class="equal"> = </span>
|
||||||
|
${Object.keys(data[key]).map((subkey) => {
|
||||||
|
return subkey == 'type' || subkey == 'value' ? `<span class="${subkey}">${data[key][subkey]}</span>` : ' '
|
||||||
|
}).join(' ')}
|
||||||
|
</summary>
|
||||||
|
${data[key].children ? me.parseData(data[key].children) : ""}</details>`
|
||||||
|
);
|
||||||
|
return buf.join("\n")
|
||||||
|
};
|
||||||
|
open(id) {
|
||||||
|
let node = document.getElementById(id);
|
||||||
|
while (node.parentNode.nodeName == "DETAILS") {
|
||||||
|
node.classList.remove('loading');
|
||||||
|
node = node.parentNode;
|
||||||
|
node.setAttribute("open", "true");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
close(id) {
|
||||||
|
let node = document.getElementById(id).parentNode;
|
||||||
|
node.removeAttribute("open");
|
||||||
|
let detailNodes = node.querySelectorAll("DETAILS");
|
||||||
|
console.log(detailNodes); detailNodes.forEach((node) => node.removeAttribute("open"));
|
||||||
|
};
|
||||||
|
select(id) {
|
||||||
|
this.open(id);
|
||||||
|
document.getElementById(id).focus();
|
||||||
|
document.getElementById(id).click();
|
||||||
|
}
|
||||||
|
}
|