django-fast-treenode 2.0.11__py3-none-any.whl → 2.1.0__py3-none-any.whl
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.
- {django_fast_treenode-2.0.11.dist-info → django_fast_treenode-2.1.0.dist-info}/LICENSE +2 -2
- django_fast_treenode-2.1.0.dist-info/METADATA +161 -0
- django_fast_treenode-2.1.0.dist-info/RECORD +75 -0
- {django_fast_treenode-2.0.11.dist-info → django_fast_treenode-2.1.0.dist-info}/WHEEL +1 -1
- treenode/admin/__init__.py +9 -0
- treenode/admin/admin.py +295 -0
- treenode/admin/changelist.py +65 -0
- treenode/admin/mixins.py +302 -0
- treenode/apps.py +12 -1
- treenode/cache.py +2 -2
- treenode/docs/.gitignore +0 -0
- treenode/docs/about.md +36 -0
- treenode/docs/admin.md +104 -0
- treenode/docs/api.md +739 -0
- treenode/docs/cache.md +187 -0
- treenode/docs/import_export.md +35 -0
- treenode/docs/index.md +30 -0
- treenode/docs/installation.md +74 -0
- treenode/docs/migration.md +145 -0
- treenode/docs/models.md +128 -0
- treenode/docs/roadmap.md +45 -0
- treenode/forms.py +8 -10
- treenode/managers/__init__.py +21 -0
- treenode/managers/adjacency.py +203 -0
- treenode/managers/closure.py +278 -0
- treenode/models/__init__.py +2 -1
- treenode/models/adjacency.py +343 -0
- treenode/models/classproperty.py +3 -0
- treenode/models/closure.py +23 -24
- treenode/models/factory.py +12 -2
- treenode/models/mixins/__init__.py +23 -0
- treenode/models/mixins/ancestors.py +65 -0
- treenode/models/mixins/children.py +81 -0
- treenode/models/mixins/descendants.py +66 -0
- treenode/models/mixins/family.py +63 -0
- treenode/models/mixins/logical.py +68 -0
- treenode/models/mixins/node.py +210 -0
- treenode/models/mixins/properties.py +156 -0
- treenode/models/mixins/roots.py +96 -0
- treenode/models/mixins/siblings.py +99 -0
- treenode/models/mixins/tree.py +344 -0
- treenode/signals.py +26 -0
- treenode/static/treenode/css/tree_widget.css +201 -31
- treenode/static/treenode/css/treenode_admin.css +48 -41
- treenode/static/treenode/js/tree_widget.js +269 -131
- treenode/static/treenode/js/treenode_admin.js +131 -171
- treenode/templates/admin/tree_node_changelist.html +6 -0
- treenode/templates/admin/treenode_ajax_rows.html +7 -0
- treenode/tests/tests.py +488 -0
- treenode/urls.py +10 -6
- treenode/utils/__init__.py +2 -0
- treenode/utils/aid.py +46 -0
- treenode/utils/base16.py +38 -0
- treenode/utils/base36.py +3 -1
- treenode/utils/db.py +116 -0
- treenode/utils/exporter.py +2 -0
- treenode/utils/importer.py +0 -1
- treenode/utils/radix.py +61 -0
- treenode/version.py +2 -2
- treenode/views.py +118 -43
- treenode/widgets.py +91 -43
- django_fast_treenode-2.0.11.dist-info/METADATA +0 -698
- django_fast_treenode-2.0.11.dist-info/RECORD +0 -42
- treenode/admin.py +0 -439
- treenode/docs/Documentation +0 -636
- treenode/managers.py +0 -419
- treenode/models/proxy.py +0 -669
- {django_fast_treenode-2.0.11.dist-info → django_fast_treenode-2.1.0.dist-info}/top_level.txt +0 -0
@@ -1,171 +1,131 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
(
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
}
|
133
|
-
|
134
|
-
// Expand a node and reveal its immediate children
|
135
|
-
function expandNode($tr) {
|
136
|
-
var parentDepth = parseInt($tr.attr('data-treenode-depth'), 10);
|
137
|
-
var $nextRows = $tr.nextAll('tr');
|
138
|
-
$nextRows.each(function() {
|
139
|
-
var $row = $(this);
|
140
|
-
var rowDepth = parseInt($row.attr('data-treenode-depth'), 10);
|
141
|
-
if (rowDepth > parentDepth) {
|
142
|
-
if (rowDepth === parentDepth + 1) {
|
143
|
-
$row.slideDown('slow');
|
144
|
-
var childPk = $row.attr('data-treenode-pk');
|
145
|
-
if (loadState(childPk)) {
|
146
|
-
expandNode($row);
|
147
|
-
}
|
148
|
-
}
|
149
|
-
} else {
|
150
|
-
return false;
|
151
|
-
}
|
152
|
-
});
|
153
|
-
}
|
154
|
-
|
155
|
-
// Save expansion state to localStorage
|
156
|
-
function saveState(pk, isExpanded) {
|
157
|
-
if (window.localStorage) {
|
158
|
-
localStorage.setItem('treenode_state_' + pk, isExpanded ? '1' : '0');
|
159
|
-
}
|
160
|
-
}
|
161
|
-
|
162
|
-
// Load expansion state from localStorage
|
163
|
-
function loadState(pk) {
|
164
|
-
if (window.localStorage) {
|
165
|
-
var state = localStorage.getItem('treenode_state_' + pk);
|
166
|
-
return state === '1';
|
167
|
-
}
|
168
|
-
return true;
|
169
|
-
}
|
170
|
-
});
|
171
|
-
})(django.jQuery || window.jQuery);
|
1
|
+
(function($) {
|
2
|
+
// Function "debounce" to delay execution
|
3
|
+
function debounce(func, wait) {
|
4
|
+
var timeout;
|
5
|
+
return function() {
|
6
|
+
var context = this, args = arguments;
|
7
|
+
clearTimeout(timeout);
|
8
|
+
timeout = setTimeout(function() {
|
9
|
+
func.apply(context, args);
|
10
|
+
}, wait);
|
11
|
+
};
|
12
|
+
}
|
13
|
+
|
14
|
+
// Function to recursively delete descendants of the current child
|
15
|
+
function removeAllDescendants(nodeId) {
|
16
|
+
var $children = $tableBody.find(`tr[data-parent-of="${nodeId}"]`);
|
17
|
+
|
18
|
+
$children.each(function () {
|
19
|
+
var childId = $(this).data('node-id');
|
20
|
+
removeAllDescendants(childId);
|
21
|
+
});
|
22
|
+
|
23
|
+
$children.remove();
|
24
|
+
}
|
25
|
+
|
26
|
+
// Function to reveal nodes stored in storage
|
27
|
+
function restoreExpandedNodes() {
|
28
|
+
var expandedNodes = JSON.parse(localStorage.getItem('expandedNodes')) || [];
|
29
|
+
if (expandedNodes.length === 0) return;
|
30
|
+
|
31
|
+
function expandNext(nodes) {
|
32
|
+
if (nodes.length === 0) return;
|
33
|
+
|
34
|
+
var nodeId = nodes.shift();
|
35
|
+
|
36
|
+
$.getJSON('change_list/', { tn_parent_id: nodeId }, function (response) {
|
37
|
+
if (response.html) {
|
38
|
+
var $btn = $tableBody.find(`.treenode-toggle[data-node-id="${nodeId}"]`);
|
39
|
+
var $parentRow = $btn.closest('tr');
|
40
|
+
$parentRow.after(response.html);
|
41
|
+
$btn.html('▼').data('expanded', true);
|
42
|
+
// Раскрываем следующий узел после завершения AJAX-запроса
|
43
|
+
expandNext(nodes);
|
44
|
+
}
|
45
|
+
});
|
46
|
+
}
|
47
|
+
|
48
|
+
// Начинаем раскрытие узлов по порядку
|
49
|
+
expandNext([...expandedNodes]);
|
50
|
+
}
|
51
|
+
|
52
|
+
// Global variables ---------------------------------
|
53
|
+
var $tableBody;
|
54
|
+
var originalTableHtml;
|
55
|
+
|
56
|
+
var expandedNodes = JSON.parse(localStorage.getItem('expandedNodes')) || [];
|
57
|
+
|
58
|
+
// Events -------------------------------------------
|
59
|
+
|
60
|
+
$(document).ready(function () {
|
61
|
+
// Сохраняем оригинальное содержимое таблицы (корневые узлы)
|
62
|
+
$tableBody = $('table#result_list tbody');
|
63
|
+
originalTableHtml = $tableBody.html();
|
64
|
+
restoreExpandedNodes();
|
65
|
+
});
|
66
|
+
|
67
|
+
// Обработчик клика для кнопок treenode-toggle через делегирование на document
|
68
|
+
$(document).on('click', '.treenode-toggle', function(e) {
|
69
|
+
e.preventDefault();
|
70
|
+
var $btn = $(this);
|
71
|
+
var nodeId = $btn.data('node-id');
|
72
|
+
|
73
|
+
// Если узел уже развёрнут, сворачиваем его
|
74
|
+
if ($btn.data('expanded')) {
|
75
|
+
removeAllDescendants(nodeId);
|
76
|
+
$btn.html('►').data('expanded', false);
|
77
|
+
|
78
|
+
// Убираем узел из списка сохранённых
|
79
|
+
expandedNodes = expandedNodes.filter(id => id !== nodeId);
|
80
|
+
|
81
|
+
} else {
|
82
|
+
// Иначе запрашиваем дочерние узлы через AJAX
|
83
|
+
$.getJSON('change_list/', { tn_parent_id: nodeId }, function(response) {
|
84
|
+
if (response.html) {
|
85
|
+
var $parentRow = $btn.closest('tr');
|
86
|
+
$parentRow.after(response.html);
|
87
|
+
$btn.html('▼').data('expanded', true);
|
88
|
+
|
89
|
+
// Сохраняем узел в localStorage
|
90
|
+
if (!expandedNodes.includes(nodeId)) {
|
91
|
+
expandedNodes.push(nodeId);
|
92
|
+
}
|
93
|
+
localStorage.setItem('expandedNodes', JSON.stringify(expandedNodes));
|
94
|
+
}
|
95
|
+
});
|
96
|
+
}
|
97
|
+
localStorage.setItem('expandedNodes', JSON.stringify(expandedNodes));
|
98
|
+
});
|
99
|
+
|
100
|
+
|
101
|
+
// Обработчик ввода для поля поиска с делегированием на document
|
102
|
+
$(document).on('keyup', 'input[name="q"]', debounce(function(e) {
|
103
|
+
var query = $.trim($(this).val());
|
104
|
+
|
105
|
+
if (query === '') {
|
106
|
+
$tableBody.html(originalTableHtml);
|
107
|
+
return;
|
108
|
+
}
|
109
|
+
|
110
|
+
$.getJSON('search/', { q: query }, function(response) {
|
111
|
+
var rowsHtml = '';
|
112
|
+
if (response.results && response.results.length > 0) {
|
113
|
+
$.each(response.results, function(index, node) {
|
114
|
+
var dragCell = '<td class="drag-cell"><span class="treenode-drag-handle">↕</span></td>';
|
115
|
+
var toggleCell = '';
|
116
|
+
if (!node.is_leaf) {
|
117
|
+
toggleCell = '<td class="toggle-cell"><button class="treenode-toggle" data-node-id="' + node.id + '">▶</button></td>';
|
118
|
+
} else {
|
119
|
+
toggleCell = '<td class="toggle-cell"><div class="treenode-space"> </div></td>';
|
120
|
+
}
|
121
|
+
var displayCell = '<td class="display-cell">' + node.text + '</td>';
|
122
|
+
rowsHtml += '<tr>' + dragCell + toggleCell + displayCell + '</tr>';
|
123
|
+
});
|
124
|
+
} else {
|
125
|
+
rowsHtml = '<tr><td colspan="3">Ничего не найдено</td></tr>';
|
126
|
+
}
|
127
|
+
$tableBody.html(rowsHtml);
|
128
|
+
});
|
129
|
+
}, 500));
|
130
|
+
|
131
|
+
})(django.jQuery || window.jQuery);
|