better explorer
This commit is contained in:
parent
2855ba7133
commit
148a01079b
File diff suppressed because one or more lines are too long
@ -55,20 +55,47 @@ jQuery(function ($) {
|
||||
initExplr();
|
||||
};
|
||||
|
||||
$(document).on('click', '.explr-item-edit', function () {
|
||||
const $item = $('.explr-dirview .highlight-clicked');
|
||||
const is_folder = $item.attr('data-folder') === '1';
|
||||
const selectEpxlrLink = function ($link) {
|
||||
$('a.explr-link').removeClass('highlight-clicked');
|
||||
$('a.explr-link').parent().removeClass('highlight-clicked');
|
||||
$('body').removeClass('explr-selection explr-selection-actionable explr-selection-entity explr-selection-folder');
|
||||
|
||||
if (is_folder) {
|
||||
$item.addClass('renaming');
|
||||
$item.find('input').focus().select();
|
||||
} else {
|
||||
document.location.href = $(this).attr('data-entity-route').replace('!c!', $item.attr('data-id'));
|
||||
if ($link.hasClass('explr-item-selectable')) {
|
||||
$link.addClass('highlight-clicked');
|
||||
$link.parent().addClass('highlight-clicked');
|
||||
$('body').addClass('explr-selection');
|
||||
if ($link.hasClass('explr-item-actionable')) {
|
||||
$('body').addClass('explr-selection-actionable');
|
||||
}
|
||||
if ($link.hasClass('explr-item-entity')) {
|
||||
$('body').addClass('explr-selection-entity');
|
||||
}
|
||||
if ($link.hasClass('explr-item-folder')) {
|
||||
$('body').addClass('explr-selection-folder');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const getExplrSelection = function () {
|
||||
return $('.explr-dirview .highlight-clicked');
|
||||
};
|
||||
|
||||
const renameExplrItem = function($item) {
|
||||
$('.dirview .renaming').removeClass('renaming');
|
||||
$item.addClass('renaming');
|
||||
$item.find('input').focus().select();
|
||||
}
|
||||
|
||||
$(document).on('click', '.explr-item-edit', function () {
|
||||
document.location.href = $(this).attr('data-entity-route').replace('!c!', getExplrSelection().attr('data-id'));
|
||||
});
|
||||
|
||||
$(document).on('click', '.explr-item-rename', function () {
|
||||
renameExplrItem(getExplrSelection());
|
||||
});
|
||||
|
||||
$(document).on('click', '.explr-item-delete', function () {
|
||||
const $item = $('.explr-dirview .highlight-clicked');
|
||||
const $item = getExplrSelection();
|
||||
const is_folder = $item.attr('data-folder') === '1';
|
||||
let route;
|
||||
|
||||
@ -84,10 +111,118 @@ jQuery(function ($) {
|
||||
});
|
||||
|
||||
$(document).keyup(function (e) {
|
||||
const $selectedLink = $('.explr-item-selectable.highlight-clicked');
|
||||
const $selectedLi = $selectedLink.parents('li:eq(0)');
|
||||
|
||||
if (e.key === "Escape") {
|
||||
$('.dirview .new-folder').addClass('hidden');
|
||||
$('.dirview .renaming').removeClass('renaming');
|
||||
} else if (e.code === "Space") {
|
||||
renameExplrItem($selectedLi);
|
||||
} else if ($selectedLink.length) {
|
||||
const $prevLi = $selectedLi.prev('li:visible');
|
||||
const $nextLi = $selectedLi.next('li:visible');
|
||||
const verticalNeighbors = getAboveBelowElement($selectedLi);
|
||||
|
||||
if (e.key === "Enter") {
|
||||
$selectedLink.trigger('dblclick');
|
||||
} else if (e.key === "ArrowLeft" && $prevLi.length) {
|
||||
selectEpxlrLink($prevLi.find('.explr-link'));
|
||||
} else if (e.key === "ArrowRight" && $nextLi.length) {
|
||||
selectEpxlrLink($nextLi.find('.explr-link'));
|
||||
} else if (e.key === "ArrowUp" && verticalNeighbors.above) {
|
||||
selectEpxlrLink(verticalNeighbors.above.find('.explr-link'));
|
||||
} else if (e.key === "ArrowDown" && verticalNeighbors.below) {
|
||||
selectEpxlrLink(verticalNeighbors.below.find('.explr-link'));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Explorer item selection
|
||||
$(document).on('click', 'a.explr-link', function (event) {
|
||||
event.preventDefault();
|
||||
selectEpxlrLink($(this));
|
||||
});
|
||||
$(document).on('dblclick', 'a.explr-link', function (event) {
|
||||
event.preventDefault();
|
||||
$(this).off('click');
|
||||
const href = $(this).attr('href');
|
||||
|
||||
if ($(this).attr('target') === '_blank') {
|
||||
window.open(href);
|
||||
} else {
|
||||
window.location.href = href;
|
||||
}
|
||||
});
|
||||
$(document).on('click', function (event) {
|
||||
const $parentClickable = $(event.target).parents('a, button');
|
||||
if ($parentClickable.length === 0) {
|
||||
$('a.explr-link').removeClass('highlight-clicked');
|
||||
$('a.explr-link').parent().removeClass('highlight-clicked');
|
||||
$('body').removeClass('explr-selection explr-selection-entity explr-selection-folder explr-selection-actionable');
|
||||
}
|
||||
});
|
||||
|
||||
const getAboveBelowElement = function ($elem) {
|
||||
const $liElements = $elem.parents('ul:eq(0)').find('li');
|
||||
const positions = [];
|
||||
|
||||
// Get the Y positions of each element
|
||||
$liElements.each(function () {
|
||||
const $this = $(this);
|
||||
positions.push({
|
||||
element: $this,
|
||||
y: $this.offset().top,
|
||||
x: $this.offset().left
|
||||
});
|
||||
});
|
||||
|
||||
// Group elements by their Y position
|
||||
const groupedByY = positions.reduce((acc, pos) => {
|
||||
if (!acc[pos.y]) {
|
||||
acc[pos.y] = [];
|
||||
}
|
||||
acc[pos.y].push(pos);
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
// Convert groupedByY to an array of arrays
|
||||
const rows = Object.values(groupedByY);
|
||||
|
||||
let targetRowIndex = -1;
|
||||
let targetColIndex = -1;
|
||||
|
||||
// Find the row and column index of the target element
|
||||
rows.forEach((row, rowIndex) => {
|
||||
row.forEach((pos, colIndex) => {
|
||||
if (pos.element.is($elem)) {
|
||||
targetRowIndex = rowIndex;
|
||||
targetColIndex = colIndex;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const result = {
|
||||
above: null,
|
||||
below: null
|
||||
};
|
||||
|
||||
if (targetRowIndex > 0) {
|
||||
const aboveRow = rows[targetRowIndex - 1];
|
||||
if (targetColIndex < aboveRow.length) {
|
||||
result.above = aboveRow[targetColIndex].element;
|
||||
}
|
||||
}
|
||||
|
||||
if (targetRowIndex < rows.length - 1) {
|
||||
const belowRow = rows[targetRowIndex + 1];
|
||||
if (targetColIndex < belowRow.length) {
|
||||
result.below = belowRow[targetColIndex].element;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
main();
|
||||
});
|
||||
@ -112,40 +112,6 @@ jQuery(document).ready(function ($) {
|
||||
$('#entity-utrack-updated-at').val(prettyTimestamp(entity.updated_at * 1000));
|
||||
});
|
||||
|
||||
|
||||
// Explorer item selection
|
||||
$(document).on('click', 'a.explr-link', function (event) {
|
||||
event.preventDefault();
|
||||
$('a.explr-link').removeClass('highlight-clicked');
|
||||
$('a.explr-link').parent().removeClass('highlight-clicked');
|
||||
$('body').removeClass('explr-selection');
|
||||
|
||||
if ($(this).hasClass('explr-item-selectable')) {
|
||||
$(this).addClass('highlight-clicked');
|
||||
$(this).parent().addClass('highlight-clicked');
|
||||
$('body').addClass('explr-selection');
|
||||
}
|
||||
});
|
||||
$(document).on('dblclick', 'a.explr-link', function (event) {
|
||||
event.preventDefault();
|
||||
$(this).off('click');
|
||||
const href = $(this).attr('href');
|
||||
|
||||
if ($(this).attr('target') === '_blank') {
|
||||
window.open(href);
|
||||
} else {
|
||||
window.location.href = href;
|
||||
}
|
||||
});
|
||||
$(document).on('click', function (event) {
|
||||
const $parentClickable = $(event.target).parents('a, button');
|
||||
if ($parentClickable.length === 0) {
|
||||
$('a.explr-link').removeClass('highlight-clicked');
|
||||
$('a.explr-link').parent().removeClass('highlight-clicked');
|
||||
$('body').removeClass('explr-selection');
|
||||
}
|
||||
});
|
||||
|
||||
setTimeout(function() {
|
||||
$('.alert-timeout').remove();
|
||||
}, 3000);
|
||||
|
||||
@ -45,12 +45,36 @@ ul.explr-tree {
|
||||
margin-right: 10px;
|
||||
border-right: 1px solid #222;
|
||||
padding-right: 20px;
|
||||
|
||||
button {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.explr-selection .explr-selection-actions {
|
||||
display: flex;
|
||||
body.explr-selection-actionable {
|
||||
.explr-selection-actions {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
&.explr-selection-folder {
|
||||
.explr-selection-actions {
|
||||
button.explr-selection-folder {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.explr-selection-entity {
|
||||
.explr-selection-actions {
|
||||
button.explr-selection-entity {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
ul.explr-dirview {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
@ -21,6 +21,7 @@ class ContentController(ObController):
|
||||
self._app.add_url_rule('/slideshow/content/edit/<content_id>', 'slideshow_content_edit', self._auth(self.slideshow_content_edit), methods=['GET'])
|
||||
self._app.add_url_rule('/slideshow/content/save/<content_id>', 'slideshow_content_save', self._auth(self.slideshow_content_save), methods=['POST'])
|
||||
self._app.add_url_rule('/slideshow/content/delete', 'slideshow_content_delete', self._auth(self.slideshow_content_delete), methods=['GET'])
|
||||
self._app.add_url_rule('/slideshow/content/rename', 'slideshow_content_rename', self._auth(self.slideshow_content_rename), methods=['POST'])
|
||||
self._app.add_url_rule('/slideshow/content/cd', 'slideshow_content_cd', self._auth(self.slideshow_content_cd), methods=['GET'])
|
||||
self._app.add_url_rule('/slideshow/content/add-folder', 'slideshow_content_folder_add', self._auth(self.slideshow_content_folder_add), methods=['POST'])
|
||||
self._app.add_url_rule('/slideshow/content/move-folder', 'slideshow_content_folder_move', self._auth(self.slideshow_content_folder_move), methods=['POST'])
|
||||
@ -105,6 +106,14 @@ class ContentController(ObController):
|
||||
self._post_update()
|
||||
return redirect(url_for('slideshow_content_list'))
|
||||
|
||||
def slideshow_content_rename(self):
|
||||
self._model_store.content().update_form(
|
||||
id=request.form['id'],
|
||||
name=request.form['name'],
|
||||
)
|
||||
|
||||
return redirect(url_for('slideshow_content_list'))
|
||||
|
||||
def slideshow_content_cd(self):
|
||||
path = request.args.get('path')
|
||||
|
||||
|
||||
@ -25,6 +25,7 @@ class FleetNodePlayerController(ObController):
|
||||
self._app.add_url_rule('/fleet/node-player/edit/<node_player_id>', 'fleet_node_player_edit', self._auth(self.fleet_node_player_edit), methods=['GET'])
|
||||
self._app.add_url_rule('/fleet/node-player/save/<node_player_id>', 'fleet_node_player_save', self._auth(self.fleet_node_player_save), methods=['POST'])
|
||||
self._app.add_url_rule('/fleet/node-player/delete', 'fleet_node_player_delete', self.guard_fleet(self._auth(self.fleet_node_player_delete)), methods=['GET'])
|
||||
self._app.add_url_rule('/fleet/node-player/rename', 'fleet_node_player_rename', self.guard_fleet(self._auth(self.fleet_node_player_rename)), methods=['POST'])
|
||||
self._app.add_url_rule('/fleet/node-player/cd', 'fleet_node_player_cd', self._auth(self.fleet_node_player_cd), methods=['GET'])
|
||||
self._app.add_url_rule('/fleet/node-player/add-folder', 'fleet_node_player_folder_add', self._auth(self.fleet_node_player_folder_add), methods=['POST'])
|
||||
self._app.add_url_rule('/fleet/node-player/move-folder', 'fleet_node_player_folder_move', self._auth(self.fleet_node_player_folder_move), methods=['POST'])
|
||||
@ -103,6 +104,14 @@ class FleetNodePlayerController(ObController):
|
||||
self._post_update()
|
||||
return redirect(url_for('fleet_node_player_list'))
|
||||
|
||||
def fleet_node_player_rename(self):
|
||||
self._model_store.node_player().update_form(
|
||||
id=request.form['id'],
|
||||
name=request.form['name'],
|
||||
)
|
||||
|
||||
return redirect(url_for('fleet_node_player_list'))
|
||||
|
||||
def fleet_node_player_cd(self):
|
||||
path = request.args.get('path')
|
||||
|
||||
|
||||
@ -113,7 +113,7 @@ class NodePlayerManager(ModelManager):
|
||||
def post_delete(self, node_player_id: str) -> str:
|
||||
return node_player_id
|
||||
|
||||
def update_form(self, id: int, name: str, host: str, operating_system: Optional[OperatingSystem] = None, group_id: Optional[int] = None) -> NodePlayer:
|
||||
def update_form(self, id: int, name: str, host: Optional[str] = None, operating_system: Optional[OperatingSystem] = None, group_id: Optional[int] = None) -> NodePlayer:
|
||||
node_player = self.get(id)
|
||||
|
||||
if not node_player:
|
||||
@ -121,9 +121,9 @@ class NodePlayerManager(ModelManager):
|
||||
|
||||
form = {
|
||||
"name": name,
|
||||
"host": host,
|
||||
"operating_system": operating_system.value if operating_system else None,
|
||||
"group_id": group_id if group_id else None
|
||||
"host": host if host else node_player.host,
|
||||
"operating_system": operating_system.value if operating_system else node_player.operating_system.value,
|
||||
"group_id": group_id if group_id else node_player.group_id
|
||||
}
|
||||
|
||||
self._db.update_by_id(self.TABLE_NAME, id, self.pre_update(form))
|
||||
|
||||
@ -30,10 +30,13 @@
|
||||
{{ HOOK(H_SLIDESHOW_CONTENT_TOOLBAR_ACTIONS_START) }}
|
||||
|
||||
<div class="explr-selection-actions">
|
||||
<button class="explr-item-edit btn-info" data-entity-route="{{ url_for('fleet_node_player_edit', node_player_id='!c!') }}">
|
||||
<button class="explr-item-edit explr-selection-entity btn-info" data-entity-route="{{ url_for('fleet_node_player_edit', node_player_id='!c!') }}">
|
||||
<i class="fa fa-eye"></i>
|
||||
</button>
|
||||
<button class="explr-item-rename explr-selection-entity explr-selection-folder btn-info">
|
||||
<i class="fa fa-pencil"></i>
|
||||
</button>
|
||||
<button class="explr-item-delete btn-danger-alt" data-folder-route="{{ url_for('fleet_node_player_folder_delete') }}" data-entity-route="{{ url_for('fleet_node_player_delete') }}">
|
||||
<button class="explr-item-delete explr-selection-entity explr-selection-folder btn-danger-alt" data-folder-route="{{ url_for('fleet_node_player_folder_delete') }}" data-entity-route="{{ url_for('fleet_node_player_delete') }}">
|
||||
<i class="fa fa-trash-alt"></i>
|
||||
</button>
|
||||
</div>
|
||||
@ -148,7 +151,7 @@
|
||||
{% set parent_path = '/'.join(working_folder_path.rstrip('/').split('/')[:-1]) %}
|
||||
{% if parent_path %}
|
||||
<li class="previous-folder droppable" data-path="{{ parent_path }}" data-id="{{ working_folder.parent_id }}" data-folder="1">
|
||||
<a href="{{ url_for('fleet_node_player_cd', path=parent_path) }}" class="explr-link">
|
||||
<a href="{{ url_for('fleet_node_player_cd', path=parent_path) }}" class="explr-link explr-item-selectable explr-item-folder">
|
||||
<i class="fa fa-folder"></i>
|
||||
..
|
||||
</a>
|
||||
@ -158,7 +161,7 @@
|
||||
{% for folder in working_folder_children %}
|
||||
{% set folder_path = working_folder_path ~ '/' ~ folder.name %}
|
||||
<li class="draggable droppable" data-path="{{ folder_path }}" data-id="{{ folder.id }}" data-folder="1">
|
||||
<a href="{{ url_for('fleet_node_player_cd', path=folder_path) }}" class="explr-link explr-item-selectable">
|
||||
<a href="{{ url_for('fleet_node_player_cd', path=folder_path) }}" class="explr-link explr-item-selectable explr-item-actionable explr-item-folder">
|
||||
<i class="fa fa-folder"></i>
|
||||
<span>{{ truncate(folder.name, 25, '...') }}</span>
|
||||
<form action="{{ url_for('fleet_node_player_folder_rename') }}" method="POST">
|
||||
@ -174,9 +177,13 @@
|
||||
{% set icon = enum_operating_system.get_fa_icon(node_player.operating_system) %}
|
||||
{% set color = node_player.operating_system.value %}
|
||||
<li class="draggable" data-path="{{ working_folder_path }}" data-id="{{ node_player.id }}" data-folder="0">
|
||||
<a href="{{ url_for('fleet_node_player_edit', node_player_id=node_player.id) }}" target="_blank" class="explr-link explr-item-selectable">
|
||||
<a href="{{ url_for('fleet_node_player_edit', node_player_id=node_player.id) }}" class="explr-link explr-item-selectable explr-item-actionable explr-item-entity">
|
||||
<i class="fa {{ icon }} {{ color }}"></i>
|
||||
{{ truncate(node_player.name, 25, '...') }}
|
||||
<span>{{ truncate(node_player.name, 25, '...') }}</span>
|
||||
<form action="{{ url_for('fleet_node_player_rename') }}" method="POST">
|
||||
<input type="text" name="name" value="{{ node_player.name }}" autocomplete="off" />
|
||||
<input type="hidden" name="id" value="{{ node_player.id }}" />
|
||||
</form>
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
|
||||
@ -30,10 +30,13 @@
|
||||
{{ HOOK(H_SLIDESHOW_CONTENT_TOOLBAR_ACTIONS_START) }}
|
||||
|
||||
<div class="explr-selection-actions">
|
||||
<button class="explr-item-edit btn-info" data-entity-route="{{ url_for('slideshow_content_edit', content_id='!c!') }}">
|
||||
<button class="explr-item-edit explr-selection-entity btn-info" data-entity-route="{{ url_for('slideshow_content_edit', content_id='!c!') }}">
|
||||
<i class="fa fa-eye"></i>
|
||||
</button>
|
||||
<button class="explr-item-rename explr-selection-entity explr-selection-folder btn-info">
|
||||
<i class="fa fa-pencil"></i>
|
||||
</button>
|
||||
<button class="explr-item-delete btn-danger-alt" data-folder-route="{{ url_for('slideshow_content_folder_delete') }}" data-entity-route="{{ url_for('slideshow_content_delete') }}">
|
||||
<button class="explr-item-delete explr-selection-entity explr-selection-folder btn-danger-alt" data-folder-route="{{ url_for('slideshow_content_folder_delete') }}" data-entity-route="{{ url_for('slideshow_content_delete') }}">
|
||||
<i class="fa fa-trash-alt"></i>
|
||||
</button>
|
||||
</div>
|
||||
@ -148,7 +151,7 @@
|
||||
{% set parent_path = '/'.join(working_folder_path.rstrip('/').split('/')[:-1]) %}
|
||||
{% if parent_path %}
|
||||
<li class="previous-folder droppable" data-path="{{ parent_path }}" data-id="{{ working_folder.parent_id }}" data-folder="1">
|
||||
<a href="{{ url_for('slideshow_content_cd', path=parent_path) }}" class="explr-link">
|
||||
<a href="{{ url_for('slideshow_content_cd', path=parent_path) }}" class="explr-link explr-item-selectable explr-item-folder">
|
||||
<i class="fa fa-folder"></i>
|
||||
..
|
||||
</a>
|
||||
@ -158,7 +161,7 @@
|
||||
{% for folder in working_folder_children %}
|
||||
{% set folder_path = working_folder_path ~ '/' ~ folder.name %}
|
||||
<li class="draggable droppable" data-path="{{ folder_path }}" data-id="{{ folder.id }}" data-folder="1">
|
||||
<a href="{{ url_for('slideshow_content_cd', path=folder_path) }}" class="explr-link explr-item-selectable">
|
||||
<a href="{{ url_for('slideshow_content_cd', path=folder_path) }}" class="explr-link explr-item-selectable explr-item-actionable explr-item-folder">
|
||||
<i class="fa fa-folder"></i>
|
||||
<span>{{ truncate(folder.name, 25, '...') }}</span>
|
||||
<form action="{{ url_for('slideshow_content_folder_rename') }}" method="POST">
|
||||
@ -175,9 +178,13 @@
|
||||
{% set color = enum_content_type.get_color_icon(content.type) %}
|
||||
|
||||
<li class="draggable" data-path="{{ working_folder_path }}" data-id="{{ content.id }}" data-folder="0">
|
||||
<a href="{{ url_for('slideshow_content_edit', content_id=content.id) }}" class="explr-link explr-item-selectable">
|
||||
<a href="{{ url_for('slideshow_content_edit', content_id=content.id) }}" class="explr-link explr-item-selectable explr-item-actionable explr-item-entity">
|
||||
<i class="fa {{ icon }} {{ color }}"></i>
|
||||
{{ truncate(content.name, 25, '...') }}
|
||||
<span>{{ truncate(content.name, 25, '...') }}</span>
|
||||
<form action="{{ url_for('slideshow_content_rename') }}" method="POST">
|
||||
<input type="text" name="name" value="{{ content.name }}" autocomplete="off" />
|
||||
<input type="hidden" name="id" value="{{ content.id }}" />
|
||||
</form>
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user