ver. 0.299: Table column visibility toggle with localStorage persistence
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
180
admin/layout/style-css/table-list.css
Normal file
180
admin/layout/style-css/table-list.css
Normal file
@@ -0,0 +1,180 @@
|
||||
.table-list-table th,
|
||||
.table-list-table td {
|
||||
vertical-align: middle !important;
|
||||
}
|
||||
|
||||
.table-list-table th.text-right,
|
||||
.table-list-table td.text-right {
|
||||
display: table-cell !important;
|
||||
text-align: right !important;
|
||||
justify-content: initial !important;
|
||||
align-items: initial !important;
|
||||
}
|
||||
|
||||
.table-list-table th:first-child,
|
||||
.table-list-table td:first-child {
|
||||
width: 70px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.js-table-filters-form .js-filter-compact-select {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.table-list-table th.table-col-compact,
|
||||
.table-list-table td.table-col-compact {
|
||||
width: 120px;
|
||||
min-width: 120px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.table-list-per-page-form {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.jconfirm.table-list-confirm-dialog .jconfirm-row {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.jconfirm.table-list-confirm-dialog .jconfirm-box-container {
|
||||
width: 100%;
|
||||
max-width: 560px;
|
||||
padding-left: 12px;
|
||||
padding-right: 12px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.jconfirm.table-list-confirm-dialog .jconfirm-box {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
/* --- Column visibility toggle --- */
|
||||
|
||||
.table-list-header-actions {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.table-col-toggle-wrapper {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.table-col-toggle-dropdown {
|
||||
display: none;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 100%;
|
||||
z-index: 1050;
|
||||
min-width: 220px;
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
background: #fff;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
|
||||
padding: 0;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.table-col-toggle-dropdown.open {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.table-col-toggle-header {
|
||||
padding: 8px 12px;
|
||||
font-weight: 600;
|
||||
font-size: 12px;
|
||||
color: #555;
|
||||
border-bottom: 1px solid #eee;
|
||||
background: #f8f8f8;
|
||||
}
|
||||
|
||||
.table-col-toggle-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 5px 12px;
|
||||
margin: 0;
|
||||
font-weight: normal;
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.table-col-toggle-item:hover {
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.table-col-toggle-footer {
|
||||
padding: 6px 12px;
|
||||
border-top: 1px solid #eee;
|
||||
background: #f8f8f8;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.table-list-table th.table-col-hidden,
|
||||
.table-list-table td.table-col-hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Toggle switch */
|
||||
|
||||
.table-col-switch {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 34px;
|
||||
min-width: 34px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
.table-col-switch input {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.table-col-switch-slider {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: #ccc;
|
||||
border-radius: 18px;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.table-col-switch-slider:before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
height: 14px;
|
||||
width: 14px;
|
||||
left: 2px;
|
||||
bottom: 2px;
|
||||
background-color: #fff;
|
||||
border-radius: 50%;
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
|
||||
.table-col-switch input:checked + .table-col-switch-slider {
|
||||
background-color: #5cb85c;
|
||||
}
|
||||
|
||||
.table-col-switch input:checked + .table-col-switch-slider:before {
|
||||
transform: translateX(16px);
|
||||
}
|
||||
@@ -46,7 +46,30 @@ $isCompactColumn = function(array $column): bool {
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div class="col-sm-4 text-right">
|
||||
<span class="text-muted">Wyników: <?= $total; ?></span>
|
||||
<div class="table-list-header-actions">
|
||||
<span class="text-muted">Wyników: <?= $total; ?></span>
|
||||
<div class="table-col-toggle-wrapper">
|
||||
<button type="button" class="btn btn-default btn-sm js-col-toggle-btn" title="Widoczność kolumn">
|
||||
<i class="fa fa-columns"></i>
|
||||
</button>
|
||||
<div class="table-col-toggle-dropdown js-col-toggle-dropdown">
|
||||
<div class="table-col-toggle-header">Widoczność kolumn</div>
|
||||
<?php foreach ($list->columns as $colIndex => $column): ?>
|
||||
<?php $colKey = (string)($column['key'] ?? 'col_' . $colIndex); ?>
|
||||
<label class="table-col-toggle-item">
|
||||
<span class="table-col-switch">
|
||||
<input type="checkbox" class="js-col-toggle-checkbox" data-col-key="<?= htmlspecialchars($colKey, ENT_QUOTES, 'UTF-8'); ?>" checked />
|
||||
<span class="table-col-switch-slider"></span>
|
||||
</span>
|
||||
<?= htmlspecialchars((string)($column['label'] ?? $colKey), ENT_QUOTES, 'UTF-8'); ?>
|
||||
</label>
|
||||
<?php endforeach; ?>
|
||||
<div class="table-col-toggle-footer">
|
||||
<button type="button" class="btn btn-default btn-xs js-col-toggle-reset">Pokaż wszystkie</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -130,8 +153,9 @@ $isCompactColumn = function(array $column): bool {
|
||||
<table class="table table-hover table-striped table-bordered mbn table-list-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<?php foreach ($list->columns as $column): ?>
|
||||
<?php foreach ($list->columns as $colIndex => $column): ?>
|
||||
<?php
|
||||
$colKey = (string)($column['key'] ?? 'col_' . $colIndex);
|
||||
$sortKey = (string)($column['sort_key'] ?? ($column['key'] ?? ''));
|
||||
$isAllowedSortKey = empty($list->sortableColumns) || in_array($sortKey, $list->sortableColumns, true);
|
||||
$isSortable = !empty($column['sortable']) && $sortKey !== '' && $isAllowedSortKey;
|
||||
@@ -147,7 +171,7 @@ $isCompactColumn = function(array $column): bool {
|
||||
$headerClass = trim($headerClass . ' table-col-compact');
|
||||
}
|
||||
?>
|
||||
<th class="<?= htmlspecialchars($headerClass, ENT_QUOTES, 'UTF-8'); ?>">
|
||||
<th class="<?= htmlspecialchars($headerClass, ENT_QUOTES, 'UTF-8'); ?>" data-col-key="<?= htmlspecialchars($colKey, ENT_QUOTES, 'UTF-8'); ?>">
|
||||
<?php if ($isSortable): ?>
|
||||
<a href="<?= htmlspecialchars($sortUrl, ENT_QUOTES, 'UTF-8'); ?>">
|
||||
<?= htmlspecialchars((string)($column['label'] ?? ''), ENT_QUOTES, 'UTF-8'); ?>
|
||||
@@ -169,9 +193,10 @@ $isCompactColumn = function(array $column): bool {
|
||||
<?php if (is_array($list->rows) && !empty($list->rows)): ?>
|
||||
<?php foreach ($list->rows as $row): ?>
|
||||
<tr>
|
||||
<?php foreach ($list->columns as $column): ?>
|
||||
<?php foreach ($list->columns as $colIndex => $column): ?>
|
||||
<?php
|
||||
$key = $column['key'] ?? '';
|
||||
$colKey = (string)($column['key'] ?? 'col_' . $colIndex);
|
||||
$raw = !empty($column['raw']);
|
||||
$value = $row[$key] ?? '';
|
||||
$cellClass = trim((string)($column['class'] ?? ''));
|
||||
@@ -179,7 +204,7 @@ $isCompactColumn = function(array $column): bool {
|
||||
$cellClass = trim($cellClass . ' table-col-compact');
|
||||
}
|
||||
?>
|
||||
<td class="<?= htmlspecialchars($cellClass, ENT_QUOTES, 'UTF-8'); ?>">
|
||||
<td class="<?= htmlspecialchars($cellClass, ENT_QUOTES, 'UTF-8'); ?>" data-col-key="<?= htmlspecialchars($colKey, ENT_QUOTES, 'UTF-8'); ?>">
|
||||
<?php if ($raw): ?>
|
||||
<?= (string)$value; ?>
|
||||
<?php else: ?>
|
||||
@@ -269,67 +294,6 @@ $isCompactColumn = function(array $column): bool {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style type="text/css">
|
||||
.table-list-table th,
|
||||
.table-list-table td {
|
||||
vertical-align: middle !important;
|
||||
}
|
||||
|
||||
.table-list-table th.text-right,
|
||||
.table-list-table td.text-right {
|
||||
display: table-cell !important;
|
||||
text-align: right !important;
|
||||
justify-content: initial !important;
|
||||
align-items: initial !important;
|
||||
}
|
||||
|
||||
.table-list-table th:first-child,
|
||||
.table-list-table td:first-child {
|
||||
width: 70px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.js-table-filters-form .js-filter-compact-select {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.table-list-table th.table-col-compact,
|
||||
.table-list-table td.table-col-compact {
|
||||
width: 120px;
|
||||
min-width: 120px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.table-list-per-page-form {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.jconfirm.table-list-confirm-dialog .jconfirm-row {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.jconfirm.table-list-confirm-dialog .jconfirm-box-container {
|
||||
width: 100%;
|
||||
max-width: 560px;
|
||||
padding-left: 12px;
|
||||
padding-right: 12px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.jconfirm.table-list-confirm-dialog .jconfirm-box {
|
||||
width: 100% !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script type="text/javascript">
|
||||
(function($) {
|
||||
if (!$) {
|
||||
@@ -414,5 +378,96 @@ $isCompactColumn = function(array $column): bool {
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// --- Column visibility toggle ---
|
||||
var storageKey = 'tableListCols_' + <?= json_encode($list->basePath); ?>;
|
||||
|
||||
function getHiddenCols() {
|
||||
try {
|
||||
var data = localStorage.getItem(storageKey);
|
||||
if (data) {
|
||||
var parsed = JSON.parse(data);
|
||||
if (Array.isArray(parsed)) {
|
||||
return parsed;
|
||||
}
|
||||
}
|
||||
} catch (e) {}
|
||||
return [];
|
||||
}
|
||||
|
||||
function saveHiddenCols(hiddenArr) {
|
||||
try {
|
||||
localStorage.setItem(storageKey, JSON.stringify(hiddenArr));
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
function applyColumnVisibility(hiddenCols) {
|
||||
var $table = $('.table-list-table');
|
||||
$table.find('th[data-col-key], td[data-col-key]').each(function() {
|
||||
var key = $(this).attr('data-col-key');
|
||||
if (hiddenCols.indexOf(key) !== -1) {
|
||||
$(this).addClass('table-col-hidden');
|
||||
} else {
|
||||
$(this).removeClass('table-col-hidden');
|
||||
}
|
||||
});
|
||||
|
||||
$('.js-col-toggle-checkbox').each(function() {
|
||||
var key = $(this).attr('data-col-key');
|
||||
$(this).prop('checked', hiddenCols.indexOf(key) === -1);
|
||||
});
|
||||
}
|
||||
|
||||
// Apply saved state on load
|
||||
var hiddenCols = getHiddenCols();
|
||||
if (hiddenCols.length) {
|
||||
applyColumnVisibility(hiddenCols);
|
||||
}
|
||||
|
||||
// Toggle dropdown open/close
|
||||
$(document).off('click.colToggleBtn', '.js-col-toggle-btn');
|
||||
$(document).on('click.colToggleBtn', '.js-col-toggle-btn', function(e) {
|
||||
e.stopPropagation();
|
||||
var $dropdown = $(this).siblings('.js-col-toggle-dropdown');
|
||||
$dropdown.toggleClass('open');
|
||||
});
|
||||
|
||||
// Prevent closing when clicking inside dropdown
|
||||
$(document).off('click.colToggleDropdown', '.js-col-toggle-dropdown');
|
||||
$(document).on('click.colToggleDropdown', '.js-col-toggle-dropdown', function(e) {
|
||||
e.stopPropagation();
|
||||
});
|
||||
|
||||
// Close dropdown on outside click
|
||||
$(document).off('click.colToggleClose');
|
||||
$(document).on('click.colToggleClose', function() {
|
||||
$('.js-col-toggle-dropdown').removeClass('open');
|
||||
});
|
||||
|
||||
// Checkbox change — toggle column
|
||||
$(document).off('change.colToggle', '.js-col-toggle-checkbox');
|
||||
$(document).on('change.colToggle', '.js-col-toggle-checkbox', function() {
|
||||
var key = $(this).attr('data-col-key');
|
||||
var isChecked = $(this).is(':checked');
|
||||
var current = getHiddenCols();
|
||||
|
||||
if (isChecked) {
|
||||
current = $.grep(current, function(v) { return v !== key; });
|
||||
} else {
|
||||
if (current.indexOf(key) === -1) {
|
||||
current.push(key);
|
||||
}
|
||||
}
|
||||
|
||||
saveHiddenCols(current);
|
||||
applyColumnVisibility(current);
|
||||
});
|
||||
|
||||
// Reset — show all columns
|
||||
$(document).off('click.colToggleReset', '.js-col-toggle-reset');
|
||||
$(document).on('click.colToggleReset', '.js-col-toggle-reset', function() {
|
||||
saveHiddenCols([]);
|
||||
applyColumnVisibility([]);
|
||||
});
|
||||
})(window.jQuery);
|
||||
</script>
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
<script type="text/javascript" src="/libraries/functions.js"></script>
|
||||
<script type="text/javascript" src="/admin/js/functions.js"></script>
|
||||
<link rel="stylesheet" href="/admin/layout/style-css/style.css" />
|
||||
<link rel="stylesheet" href="/admin/layout/style-css/table-list.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="admin-page">
|
||||
|
||||
@@ -4,6 +4,16 @@ Logi zmian z migracji na Domain-Driven Architecture. Najnowsze na gorze.
|
||||
|
||||
---
|
||||
|
||||
## ver. 0.299 (2026-02-21) - Widoczność kolumn w tabelach
|
||||
|
||||
- **NEW**: Toggle widoczności kolumn w komponentach `table-list` — przycisk z ikoną kolumn, dropdown z toggle switchami
|
||||
- **NEW**: Stan widoczności kolumn zapisywany w `localStorage` per tabela (klucz na bazie `basePath`)
|
||||
- **NEW**: Przycisk "Pokaż wszystkie" resetujący widoczność
|
||||
- **UPDATE**: Style z `table-list.php` wyekstrahowane do osobnego pliku `admin/layout/style-css/table-list.css`
|
||||
- **UPDATE**: `admin/templates/site/main-layout.php` — podłączenie `table-list.css`
|
||||
|
||||
---
|
||||
|
||||
## ver. 0.297 (2026-02-19) - REST API produktów
|
||||
|
||||
- **NEW**: Endpoint `products` w REST API — lista, szczegóły, tworzenie, aktualizacja produktów
|
||||
|
||||
@@ -18,14 +18,14 @@ Aktualizacje znajdują się w folderze `updates/0.XX/` gdzie XX oznacza dziesią
|
||||
|
||||
## Procedura tworzenia nowej aktualizacji
|
||||
|
||||
## Status biezacej aktualizacji (ver. 0.297)
|
||||
## Status biezacej aktualizacji (ver. 0.299)
|
||||
|
||||
- Wersja udostepniona: `0.297` (data: 2026-02-19).
|
||||
- Wersja udostepniona: `0.299` (data: 2026-02-21).
|
||||
- Pliki publikacyjne:
|
||||
- `updates/0.20/ver_0.297.zip`
|
||||
- `updates/0.20/ver_0.299.zip`
|
||||
- Pliki metadanych aktualizacji:
|
||||
- `updates/changelog.php`
|
||||
- `updates/versions.php` (`$current_ver = 297`)
|
||||
- `updates/versions.php` (`$current_ver = 299`)
|
||||
- Weryfikacja testow przed publikacja:
|
||||
- `OK (687 tests, 1971 assertions)`
|
||||
|
||||
|
||||
BIN
updates/0.20/ver_0.299.zip
Normal file
BIN
updates/0.20/ver_0.299.zip
Normal file
Binary file not shown.
@@ -1,3 +1,6 @@
|
||||
<b>ver. 0.299 - 21.02.2026</b><br />
|
||||
- NEW - Ukrywanie/pokazywanie kolumn w tabelach admina (toggle switch + localStorage)
|
||||
<hr>
|
||||
<b>ver. 0.298 - 20.02.2026</b><br />
|
||||
- FIX - kilka poprawek po aktualizacji
|
||||
<hr>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?
|
||||
$current_ver = 298;
|
||||
$current_ver = 299;
|
||||
|
||||
for ($i = 1; $i <= $current_ver; $i++)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user