mirror of
https://github.com/vrana/adminer.git
synced 2025-09-05 20:02:54 +02:00
Add drag-n-drop moving of rows in table editing
This commit is contained in:
@@ -418,7 +418,7 @@ class Adminer {
|
|||||||
);
|
);
|
||||||
|
|
||||||
echo "<div ", ($key != "" ? "" : "class='no-sort'"), ">",
|
echo "<div ", ($key != "" ? "" : "class='no-sort'"), ">",
|
||||||
"<span class='jsonly handle'>=</span>";
|
"<span class='jsonly handle'></span>";
|
||||||
|
|
||||||
if ($functions || $grouping) {
|
if ($functions || $grouping) {
|
||||||
echo "<select name='columns[$i][fun]'>",
|
echo "<select name='columns[$i][fun]'>",
|
||||||
@@ -499,7 +499,7 @@ class Adminer {
|
|||||||
if ($key != "" && $val == "") continue;
|
if ($key != "" && $val == "") continue;
|
||||||
|
|
||||||
echo "<div ", ($key != "" ? "" : "class='no-sort'"), ">",
|
echo "<div ", ($key != "" ? "" : "class='no-sort'"), ">",
|
||||||
"<span class='jsonly handle'>=</span>",
|
"<span class='jsonly handle'></span>",
|
||||||
select_input("name='order[$i]'", $columns, $val, $key !== "" ? "selectFieldChange" : "selectAddRow"),
|
select_input("name='order[$i]'", $columns, $val, $key !== "" ? "selectFieldChange" : "selectAddRow"),
|
||||||
checkbox("desc[$i]", 1, isset($_GET["desc"][$key]), lang('descending')),
|
checkbox("desc[$i]", 1, isset($_GET["desc"][$key]), lang('descending')),
|
||||||
" <input type='image' src='../adminer/static/cross.gif' class='jsonly icon remove' title='" . h(lang('Remove')) . "' alt='x'>",
|
" <input type='image' src='../adminer/static/cross.gif' class='jsonly icon remove' title='" . h(lang('Remove')) . "' alt='x'>",
|
||||||
|
@@ -288,7 +288,14 @@ function edit_fields(array $fields, array $collations, $type = "TABLE", $foreign
|
|||||||
?>
|
?>
|
||||||
|
|
||||||
<thead><tr>
|
<thead><tr>
|
||||||
<?php if ($type == "PROCEDURE") { ?><td></td><?php } ?>
|
<?php
|
||||||
|
if (support("move_col")) {
|
||||||
|
echo "<td class='jsonly'></td>";
|
||||||
|
}
|
||||||
|
if ($type == "PROCEDURE") {
|
||||||
|
echo "<td></td>";
|
||||||
|
}
|
||||||
|
?>
|
||||||
<th id="label-name"><?php echo ($type == "TABLE" ? lang('Column name') : lang('Parameter name')); ?></th>
|
<th id="label-name"><?php echo ($type == "TABLE" ? lang('Column name') : lang('Parameter name')); ?></th>
|
||||||
<td id="label-type"><?php echo lang('Type'); ?><textarea id="enum-edit" rows="4" cols="12" wrap="off" style="display: none;"></textarea><?php echo script("qs('#enum-edit').onblur = editingLengthBlur;"); ?></td>
|
<td id="label-type"><?php echo lang('Type'); ?><textarea id="enum-edit" rows="4" cols="12" wrap="off" style="display: none;"></textarea><?php echo script("qs('#enum-edit').onblur = editingLengthBlur;"); ?></td>
|
||||||
<td id="label-length"><?php echo lang('Length'); ?></td>
|
<td id="label-length"><?php echo lang('Length'); ?></td>
|
||||||
@@ -308,9 +315,9 @@ function edit_fields(array $fields, array $collations, $type = "TABLE", $foreign
|
|||||||
<?php } ?>
|
<?php } ?>
|
||||||
<td><?php echo "<input type='image' class='icon' name='add[" . (support("move_col") ? 0 : count($fields)) . "]' src='../adminer/static/plus.gif' alt='+' title='" . lang('Add next') . "'>" . script("row_count = " . count($fields) . ";"); ?></td>
|
<td><?php echo "<input type='image' class='icon' name='add[" . (support("move_col") ? 0 : count($fields)) . "]' src='../adminer/static/plus.gif' alt='+' title='" . lang('Add next') . "'>" . script("row_count = " . count($fields) . ";"); ?></td>
|
||||||
</tr></thead>
|
</tr></thead>
|
||||||
<tbody>
|
|
||||||
<?php
|
<?php
|
||||||
echo script("mixin(qsl('tbody'), {onclick: editingClick, onkeydown: editingKeydown, oninput: editingInput});");
|
echo "<tbody>\n";
|
||||||
|
|
||||||
foreach ($fields as $i => $field) {
|
foreach ($fields as $i => $field) {
|
||||||
$i++;
|
$i++;
|
||||||
$orig = $field[($_POST ? "orig" : "field")];
|
$orig = $field[($_POST ? "orig" : "field")];
|
||||||
@@ -319,6 +326,9 @@ function edit_fields(array $fields, array $collations, $type = "TABLE", $foreign
|
|||||||
$style = $display ? "" : "style='display: none;'";
|
$style = $display ? "" : "style='display: none;'";
|
||||||
echo "<tr $style>\n";
|
echo "<tr $style>\n";
|
||||||
|
|
||||||
|
if (support("move_col")) {
|
||||||
|
echo "<th class='handle jsonly'></th>";
|
||||||
|
}
|
||||||
if ($type == "PROCEDURE") {
|
if ($type == "PROCEDURE") {
|
||||||
echo "<td>", html_select("fields[$i][inout]", explode("|", $inout), $field["inout"]), "</td>\n";
|
echo "<td>", html_select("fields[$i][inout]", explode("|", $inout), $field["inout"]), "</td>\n";
|
||||||
}
|
}
|
||||||
@@ -353,14 +363,17 @@ function edit_fields(array $fields, array $collations, $type = "TABLE", $foreign
|
|||||||
echo "<td>";
|
echo "<td>";
|
||||||
if (support("move_col")) {
|
if (support("move_col")) {
|
||||||
echo "<input type='image' class='icon' name='add[$i]' src='../adminer/static/plus.gif' alt='+' title='" . lang('Add next') . "'> ",
|
echo "<input type='image' class='icon' name='add[$i]' src='../adminer/static/plus.gif' alt='+' title='" . lang('Add next') . "'> ",
|
||||||
"<input type='image' class='icon' name='up[$i]' src='../adminer/static/up.gif' alt='↑' title='" . lang('Move up') . "'> ",
|
"<input type='image' class='icon hidden' name='up[$i]' src='../adminer/static/up.gif' alt='↑' title='" . lang('Move up') . "'> ",
|
||||||
"<input type='image' class='icon' name='down[$i]' src='../adminer/static/down.gif' alt='↓' title='" . lang('Move down') . "'> ";
|
"<input type='image' class='icon hidden' name='down[$i]' src='../adminer/static/down.gif' alt='↓' title='" . lang('Move down') . "'> ";
|
||||||
}
|
}
|
||||||
if ($orig == "" || support("drop_col")) {
|
if ($orig == "" || support("drop_col")) {
|
||||||
echo "<input type='image' class='icon' name='drop_col[$i]' src='../adminer/static/cross.gif' alt='x' title='" . lang('Remove') . "'>";
|
echo "<input type='image' class='icon' name='drop_col[$i]' src='../adminer/static/cross.gif' alt='x' title='" . lang('Remove') . "'>";
|
||||||
}
|
}
|
||||||
echo "</td>\n</tr>\n";
|
echo "</td>\n</tr>\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
echo "</tbody>";
|
||||||
|
echo script("mixin(qs('#edit-fields tbody'), {onclick: editingClick, onkeydown: editingKeydown, oninput: editingInput}); initSortable('#edit-fields tbody');");
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Move fields up and down or add field
|
/** Move fields up and down or add field
|
||||||
|
@@ -250,11 +250,11 @@ function confirm($message = "", $selector = "qsl('input')") {
|
|||||||
return script("$selector.onclick = function () { return confirm('" . ($message ? js_escape($message) : lang('Are you sure?')) . "'); };", "");
|
return script("$selector.onclick = function () { return confirm('" . ($message ? js_escape($message) : lang('Are you sure?')) . "'); };", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Print header for hidden fieldset (close by </div></fieldset>)
|
/**
|
||||||
* @param string
|
* Prints header for hidden fieldset (close by </div></fieldset>)
|
||||||
* @param string
|
* @param $id string
|
||||||
* @param bool
|
* @param $legend string
|
||||||
*/
|
*/
|
||||||
function print_fieldset($id, $legend, $visible = false, $sortable = false) {
|
function print_fieldset($id, $legend, $visible = false, $sortable = false) {
|
||||||
echo "<fieldset><legend>";
|
echo "<fieldset><legend>";
|
||||||
echo "<a href='#fieldset-$id'>$legend</a>";
|
echo "<a href='#fieldset-$id'>$legend</a>";
|
||||||
|
@@ -10,7 +10,7 @@ h3 { font-weight: normal; font-size: 130%; margin: 1em 0 0; }
|
|||||||
form { margin: 0; }
|
form { margin: 0; }
|
||||||
td table { width: 100%; margin: 0; }
|
td table { width: 100%; margin: 0; }
|
||||||
table { margin: 1em 20px 0 0; border-collapse: collapse; font-size: 90%; }
|
table { margin: 1em 20px 0 0; border-collapse: collapse; font-size: 90%; }
|
||||||
td, th { border: 1px solid #999; padding: .2em .3em; }
|
td, th { box-sizing: border-box; border: 1px solid #999; padding: .2em .3em; }
|
||||||
th { background: #eee; text-align: left; }
|
th { background: #eee; text-align: left; }
|
||||||
thead th { text-align: center; padding: .2em .5em; }
|
thead th { text-align: center; padding: .2em .5em; }
|
||||||
thead td, thead th { background: #ddf; } /* position: sticky; causes Firefox to lose borders */
|
thead td, thead th { background: #ddf; } /* position: sticky; causes Firefox to lose borders */
|
||||||
@@ -72,12 +72,15 @@ p.nowrap { white-space: pre; }
|
|||||||
.logout { margin-top: .5em; position: absolute; top: 0; right: 0; }
|
.logout { margin-top: .5em; position: absolute; top: 0; right: 0; }
|
||||||
.loadmore { margin-left: 1ex; }
|
.loadmore { margin-left: 1ex; }
|
||||||
.tables-filter { padding: .8em 1em 0; }
|
.tables-filter { padding: .8em 1em 0; }
|
||||||
.handle { display: inline-block; width: 18px; height: 18px; padding-right: 5px; vertical-align: middle; overflow: hidden; font-size: 130%; text-align: center; line-height: 16px; opacity: 0.2; cursor: grab; }
|
.handle { cursor: grab; vertical-align: middle; }
|
||||||
.sortable { position: relative; }
|
.handle:before { content: "="; display: inline-block; width: 18px; height: 18px; overflow: hidden; font-size: 130%; text-align: center; line-height: 16px; opacity: 0.2; }
|
||||||
.sortable .no-sort .handle { opacity: 0; cursor: default; }
|
span.handle { display: inline-block; width: 18px; height: 18px; padding-right: .3em; }
|
||||||
|
th.handle:before { vertical-align: middle; }
|
||||||
|
.no-sort .handle { cursor: default; opacity: 0; }
|
||||||
.placeholder { opacity: 0; }
|
.placeholder { opacity: 0; }
|
||||||
.dragging { position: absolute; z-index: 1; }
|
.dragging { position: absolute; margin: 0; background: #fff; }
|
||||||
.dragging .handle { cursor: grabbing; }
|
.dragging * { cursor: grabbing; }
|
||||||
|
table.dragging { background: #eee; }
|
||||||
/* .edit used in designs */
|
/* .edit used in designs */
|
||||||
#menu { position: absolute; margin: 10px 0 0; padding: 0 0 30px 0; top: 2em; left: 0; width: 19em; }
|
#menu { position: absolute; margin: 10px 0 0; padding: 0 0 30px 0; top: 2em; left: 0; width: 19em; }
|
||||||
#menu p, #logins, #tables { padding: .8em 1em; margin: 0; border-bottom: 1px solid #ccc; }
|
#menu p, #logins, #tables { padding: .8em 1em; margin: 0; border-bottom: 1px solid #ccc; }
|
||||||
|
@@ -253,10 +253,6 @@ function editingClick(event) {
|
|||||||
var name = el.name;
|
var name = el.name;
|
||||||
if (/^add\[/.test(name)) {
|
if (/^add\[/.test(name)) {
|
||||||
editingAddRow.call(el, 1);
|
editingAddRow.call(el, 1);
|
||||||
} else if (/^up\[/.test(name)) {
|
|
||||||
editingMoveRow.call(el, 1);
|
|
||||||
} else if (/^down\[/.test(name)) {
|
|
||||||
editingMoveRow.call(el);
|
|
||||||
} else if (/^drop_col\[/.test(name)) {
|
} else if (/^drop_col\[/.test(name)) {
|
||||||
editingRemoveRow.call(el, 'fields\$1[field]');
|
editingRemoveRow.call(el, 'fields\$1[field]');
|
||||||
} else {
|
} else {
|
||||||
@@ -353,7 +349,10 @@ function editingAddRow(focus) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
tags[0].oninput = editingNameChange;
|
tags[0].oninput = editingNameChange;
|
||||||
|
|
||||||
|
initSortableRow(row2);
|
||||||
row.parentNode.insertBefore(row2, row.nextSibling);
|
row.parentNode.insertBefore(row2, row.nextSibling);
|
||||||
|
|
||||||
if (focus) {
|
if (focus) {
|
||||||
input.oninput = editingNameChange;
|
input.oninput = editingNameChange;
|
||||||
input.focus();
|
input.focus();
|
||||||
@@ -375,22 +374,6 @@ function editingRemoveRow(name) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Move table row for field
|
|
||||||
* @param [boolean]
|
|
||||||
* @return boolean false for success
|
|
||||||
* @this HTMLInputElement
|
|
||||||
*/
|
|
||||||
function editingMoveRow(up){
|
|
||||||
var row = parentTag(this, 'tr');
|
|
||||||
if (!('nextElementSibling' in row)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
row.parentNode.insertBefore(row, up
|
|
||||||
? row.previousElementSibling
|
|
||||||
: row.nextElementSibling ? row.nextElementSibling.nextElementSibling : row.parentNode.firstChild);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var lastType = '';
|
var lastType = '';
|
||||||
|
|
||||||
/** Clear length and hide collation or unsigned
|
/** Clear length and hide collation or unsigned
|
||||||
|
@@ -544,8 +544,8 @@ function selectSearchSearch() {
|
|||||||
|
|
||||||
// Sorting.
|
// Sorting.
|
||||||
(function() {
|
(function() {
|
||||||
let placeholderRow, draggingRow, nextRow;
|
let placeholderRow, nextRow, dragHelper;
|
||||||
let startY, maxY;
|
let startY, minY, maxY;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes sortable list of DIV elements.
|
* Initializes sortable list of DIV elements.
|
||||||
@@ -576,27 +576,52 @@ function selectSearchSearch() {
|
|||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
const parent = row.parentNode;
|
const parent = row.parentNode;
|
||||||
startY = event.clientY - row.offsetTop;
|
startY = event.clientY - getOffsetTop(row);
|
||||||
maxY = parent.offsetHeight - row.offsetHeight;
|
minY = getOffsetTop(parent);
|
||||||
|
maxY = minY + parent.offsetHeight - row.offsetHeight;
|
||||||
|
|
||||||
placeholderRow = row.cloneNode(true);
|
placeholderRow = row.cloneNode(true);
|
||||||
placeholderRow.classList.add("placeholder");
|
placeholderRow.classList.add("placeholder");
|
||||||
placeholderRow.style.top = (event.clientY - startY) + "px";
|
|
||||||
parent.insertBefore(placeholderRow, row);
|
parent.insertBefore(placeholderRow, row);
|
||||||
|
|
||||||
draggingRow = row;
|
nextRow = row.nextElementSibling;
|
||||||
draggingRow.classList.add("dragging");
|
|
||||||
parent.insertBefore(draggingRow, parent.firstChild);
|
|
||||||
|
|
||||||
nextRow = placeholderRow.nextElementSibling;
|
let top = event.clientY - startY;
|
||||||
|
let left = getOffsetLeft(row);
|
||||||
|
let width = row.getBoundingClientRect().width;
|
||||||
|
|
||||||
updateSorting(event);
|
if (row.tagName === "TR") {
|
||||||
|
const firstChild = row.firstElementChild;
|
||||||
|
const borderWidth = (firstChild.offsetWidth - firstChild.clientWidth) / 2;
|
||||||
|
const borderHeight = (firstChild.offsetHeight - firstChild.clientHeight) / 2;
|
||||||
|
|
||||||
|
minY -= borderHeight;
|
||||||
|
maxY -= borderHeight;
|
||||||
|
top -= borderHeight;
|
||||||
|
left -= borderWidth;
|
||||||
|
width += 2 * borderWidth;
|
||||||
|
|
||||||
|
for (const child of row.children) {
|
||||||
|
child.style.width = child.getBoundingClientRect().width + "px";
|
||||||
|
}
|
||||||
|
|
||||||
|
dragHelper = document.createElement("table");
|
||||||
|
dragHelper.appendChild(row);
|
||||||
|
} else {
|
||||||
|
dragHelper = row;
|
||||||
|
}
|
||||||
|
|
||||||
|
dragHelper.style.top = `${top}px`;
|
||||||
|
dragHelper.style.left = `${left}px`;
|
||||||
|
dragHelper.style.width = `${width}px`;
|
||||||
|
dragHelper.classList.add("dragging");
|
||||||
|
document.body.appendChild(dragHelper);
|
||||||
|
|
||||||
window.addEventListener("mousemove", updateSorting);
|
window.addEventListener("mousemove", updateSorting);
|
||||||
|
|
||||||
window.addEventListener("mouseup", () => {
|
window.addEventListener("mouseup", () => {
|
||||||
draggingRow.classList.remove("dragging");
|
dragHelper.classList.remove("dragging");
|
||||||
parent.insertBefore(draggingRow, placeholderRow);
|
parent.insertBefore(dragHelper.tagName === "TABLE" ? dragHelper.firstChild : dragHelper, placeholderRow);
|
||||||
placeholderRow.remove();
|
placeholderRow.remove();
|
||||||
|
|
||||||
window.removeEventListener("mousemove", updateSorting);
|
window.removeEventListener("mousemove", updateSorting);
|
||||||
@@ -605,8 +630,11 @@ function selectSearchSearch() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function updateSorting(event) {
|
function updateSorting(event) {
|
||||||
let top = Math.min(Math.max(event.clientY - startY, 0), maxY);
|
let top = Math.min(Math.max(event.clientY - startY, minY), maxY);
|
||||||
draggingRow.style.top = top + "px";
|
dragHelper.style.top = `${top}px`;
|
||||||
|
|
||||||
|
const parent = placeholderRow.parentNode;
|
||||||
|
top = top - minY + parent.offsetTop;
|
||||||
|
|
||||||
let sibling;
|
let sibling;
|
||||||
if (top > placeholderRow.offsetTop + placeholderRow.offsetHeight / 2) {
|
if (top > placeholderRow.offsetTop + placeholderRow.offsetHeight / 2) {
|
||||||
@@ -1149,6 +1177,18 @@ function cloneNode(el) {
|
|||||||
return el2;
|
return el2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getOffsetTop(element) {
|
||||||
|
let box = element.getBoundingClientRect();
|
||||||
|
|
||||||
|
return box.top + window.scrollY;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getOffsetLeft(element) {
|
||||||
|
let box = element.getBoundingClientRect();
|
||||||
|
|
||||||
|
return box.left + window.scrollX;
|
||||||
|
}
|
||||||
|
|
||||||
oninput = function (event) {
|
oninput = function (event) {
|
||||||
var target = event.target;
|
var target = event.target;
|
||||||
var maxLength = target.getAttribute('data-maxlength');
|
var maxLength = target.getAttribute('data-maxlength');
|
||||||
|
Reference in New Issue
Block a user