1041 lines
35 KiB
HTML
1041 lines
35 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Cyberpunk Future Data Explorer</title>
|
|
<style>
|
|
@import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700;900&family=Share+Tech+Mono&display=swap');
|
|
|
|
* {
|
|
margin: 0;
|
|
padding: 0;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
body {
|
|
font-family: 'Share Tech Mono', monospace;
|
|
background: #000;
|
|
color: #0ff;
|
|
min-height: 100vh;
|
|
overflow-x: hidden;
|
|
position: relative;
|
|
}
|
|
|
|
/* Animated background grid */
|
|
body::before {
|
|
content: '';
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
background-image:
|
|
linear-gradient(rgba(0, 255, 255, 0.03) 1px, transparent 1px),
|
|
linear-gradient(90deg, rgba(0, 255, 255, 0.03) 1px, transparent 1px);
|
|
background-size: 50px 50px;
|
|
animation: grid-move 20s linear infinite;
|
|
pointer-events: none;
|
|
}
|
|
|
|
@keyframes grid-move {
|
|
0% { transform: translate(0, 0); }
|
|
100% { transform: translate(50px, 50px); }
|
|
}
|
|
|
|
/* Digital rain effect */
|
|
.rain-container {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
overflow: hidden;
|
|
pointer-events: none;
|
|
opacity: 0.1;
|
|
}
|
|
|
|
.rain-column {
|
|
position: absolute;
|
|
top: -100%;
|
|
width: 20px;
|
|
height: 100%;
|
|
text-align: center;
|
|
font-size: 20px;
|
|
animation: rain-fall linear infinite;
|
|
}
|
|
|
|
@keyframes rain-fall {
|
|
to { transform: translateY(200%); }
|
|
}
|
|
|
|
main {
|
|
max-width: 1400px;
|
|
margin: 0 auto;
|
|
padding: 40px 20px;
|
|
position: relative;
|
|
z-index: 1;
|
|
}
|
|
|
|
h1 {
|
|
font-family: 'Orbitron', monospace;
|
|
font-size: 3em;
|
|
font-weight: 900;
|
|
text-align: center;
|
|
margin-bottom: 40px;
|
|
text-transform: uppercase;
|
|
letter-spacing: 5px;
|
|
position: relative;
|
|
text-shadow:
|
|
0 0 10px #0ff,
|
|
0 0 20px #0ff,
|
|
0 0 30px #0ff;
|
|
animation: title-glitch 3s infinite;
|
|
}
|
|
|
|
@keyframes title-glitch {
|
|
0%, 90% {
|
|
text-shadow:
|
|
0 0 10px #0ff,
|
|
0 0 20px #0ff,
|
|
0 0 30px #0ff;
|
|
}
|
|
92% {
|
|
text-shadow:
|
|
-2px 0 #f0f,
|
|
2px 0 #0ff,
|
|
0 0 10px #f0f;
|
|
}
|
|
94% {
|
|
text-shadow:
|
|
2px 0 #ff0,
|
|
-2px 0 #0ff,
|
|
0 0 10px #ff0;
|
|
}
|
|
100% {
|
|
text-shadow:
|
|
0 0 10px #0ff,
|
|
0 0 20px #0ff,
|
|
0 0 30px #0ff;
|
|
}
|
|
}
|
|
|
|
/* Data Explorer Container */
|
|
.data-explorer {
|
|
background: rgba(0, 0, 0, 0.9);
|
|
border: 2px solid #0ff;
|
|
border-radius: 0;
|
|
padding: 30px;
|
|
position: relative;
|
|
overflow: hidden;
|
|
box-shadow:
|
|
0 0 20px rgba(0, 255, 255, 0.5),
|
|
inset 0 0 20px rgba(0, 255, 255, 0.1);
|
|
}
|
|
|
|
.data-explorer::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: -2px;
|
|
left: -2px;
|
|
right: -2px;
|
|
bottom: -2px;
|
|
background: linear-gradient(45deg, #0ff, #f0f, #ff0, #0ff);
|
|
z-index: -1;
|
|
animation: border-glow 3s linear infinite;
|
|
filter: blur(4px);
|
|
}
|
|
|
|
@keyframes border-glow {
|
|
0%, 100% { opacity: 1; }
|
|
50% { opacity: 0.5; }
|
|
}
|
|
|
|
/* Control Panel */
|
|
.control-panel {
|
|
display: grid;
|
|
grid-template-columns: 2fr 1fr 1fr;
|
|
gap: 20px;
|
|
margin-bottom: 30px;
|
|
}
|
|
|
|
/* Search with glitch effect */
|
|
.search-container {
|
|
position: relative;
|
|
}
|
|
|
|
.search-input {
|
|
width: 100%;
|
|
padding: 15px 20px;
|
|
background: rgba(0, 255, 255, 0.1);
|
|
border: 1px solid #0ff;
|
|
color: #0ff;
|
|
font-family: 'Share Tech Mono', monospace;
|
|
font-size: 16px;
|
|
transition: all 0.3s;
|
|
position: relative;
|
|
}
|
|
|
|
.search-input:focus {
|
|
outline: none;
|
|
background: rgba(0, 255, 255, 0.2);
|
|
box-shadow:
|
|
0 0 20px rgba(0, 255, 255, 0.5),
|
|
inset 0 0 10px rgba(0, 255, 255, 0.2);
|
|
animation: input-glitch 0.2s;
|
|
}
|
|
|
|
@keyframes input-glitch {
|
|
0%, 100% { transform: translateX(0); }
|
|
20% { transform: translateX(-2px); }
|
|
40% { transform: translateX(2px); }
|
|
60% { transform: translateX(-1px); }
|
|
80% { transform: translateX(1px); }
|
|
}
|
|
|
|
.search-input::placeholder {
|
|
color: rgba(0, 255, 255, 0.5);
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
/* Filter Controls - Circuit Board Style */
|
|
.filter-controls {
|
|
display: flex;
|
|
gap: 10px;
|
|
align-items: center;
|
|
position: relative;
|
|
}
|
|
|
|
.filter-controls::before {
|
|
content: '';
|
|
position: absolute;
|
|
inset: -10px;
|
|
background: url("data:image/svg+xml,%3Csvg width='100' height='100' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M10 10h80v80h-80z' fill='none' stroke='%230ff' stroke-width='0.5' opacity='0.1'/%3E%3Cpath d='M30 10v80M70 10v80M10 30h80M10 70h80' stroke='%230ff' stroke-width='0.5' opacity='0.1'/%3E%3C/svg%3E");
|
|
background-size: 100px 100px;
|
|
opacity: 0.3;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.filter-select {
|
|
flex: 1;
|
|
padding: 12px;
|
|
background: rgba(0, 255, 255, 0.05);
|
|
border: 1px solid #0ff;
|
|
color: #0ff;
|
|
font-family: 'Share Tech Mono', monospace;
|
|
cursor: pointer;
|
|
transition: all 0.3s;
|
|
}
|
|
|
|
.filter-select:hover {
|
|
background: rgba(0, 255, 255, 0.1);
|
|
box-shadow: 0 0 10px rgba(0, 255, 255, 0.5);
|
|
}
|
|
|
|
.filter-select option {
|
|
background: #000;
|
|
color: #0ff;
|
|
}
|
|
|
|
/* Sort Controls */
|
|
.sort-controls {
|
|
display: flex;
|
|
gap: 10px;
|
|
}
|
|
|
|
.sort-btn {
|
|
flex: 1;
|
|
padding: 12px;
|
|
background: transparent;
|
|
border: 1px solid #0ff;
|
|
color: #0ff;
|
|
font-family: 'Share Tech Mono', monospace;
|
|
cursor: pointer;
|
|
position: relative;
|
|
overflow: hidden;
|
|
transition: all 0.3s;
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
.sort-btn::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: 0;
|
|
left: -100%;
|
|
width: 100%;
|
|
height: 100%;
|
|
background: linear-gradient(90deg, transparent, rgba(0, 255, 255, 0.3), transparent);
|
|
transition: left 0.5s;
|
|
}
|
|
|
|
.sort-btn:hover::before {
|
|
left: 100%;
|
|
}
|
|
|
|
.sort-btn.active {
|
|
background: rgba(0, 255, 255, 0.2);
|
|
box-shadow: 0 0 15px rgba(0, 255, 255, 0.5);
|
|
}
|
|
|
|
/* Data Table - Neon Grid */
|
|
.data-table-container {
|
|
overflow-x: auto;
|
|
margin-bottom: 30px;
|
|
position: relative;
|
|
}
|
|
|
|
.data-table {
|
|
width: 100%;
|
|
border-collapse: separate;
|
|
border-spacing: 0;
|
|
position: relative;
|
|
}
|
|
|
|
.data-table th,
|
|
.data-table td {
|
|
padding: 15px;
|
|
text-align: left;
|
|
border: 1px solid rgba(0, 255, 255, 0.3);
|
|
position: relative;
|
|
}
|
|
|
|
.data-table th {
|
|
background: rgba(0, 255, 255, 0.1);
|
|
color: #0ff;
|
|
text-transform: uppercase;
|
|
font-weight: 700;
|
|
letter-spacing: 2px;
|
|
position: sticky;
|
|
top: 0;
|
|
z-index: 10;
|
|
}
|
|
|
|
.data-table tbody tr {
|
|
transition: all 0.3s;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.data-table tbody tr:hover {
|
|
background: rgba(0, 255, 255, 0.05);
|
|
transform: translateX(5px);
|
|
}
|
|
|
|
.data-table tbody tr.selected {
|
|
background: rgba(255, 0, 255, 0.1);
|
|
box-shadow:
|
|
0 0 20px rgba(255, 0, 255, 0.3),
|
|
inset 0 0 10px rgba(255, 0, 255, 0.1);
|
|
}
|
|
|
|
.data-table tbody tr.selected td {
|
|
border-color: #f0f;
|
|
color: #f0f;
|
|
}
|
|
|
|
/* Cell animations */
|
|
.data-table td {
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.data-table td::after {
|
|
content: '';
|
|
position: absolute;
|
|
top: 0;
|
|
left: -100%;
|
|
width: 100%;
|
|
height: 100%;
|
|
background: linear-gradient(90deg, transparent, rgba(0, 255, 255, 0.2), transparent);
|
|
transition: left 0.5s;
|
|
}
|
|
|
|
.data-table tr:hover td::after {
|
|
left: 100%;
|
|
transition-delay: calc(var(--col-index) * 0.1s);
|
|
}
|
|
|
|
/* Status indicators */
|
|
.status {
|
|
display: inline-block;
|
|
padding: 4px 12px;
|
|
border-radius: 2px;
|
|
font-size: 12px;
|
|
text-transform: uppercase;
|
|
letter-spacing: 1px;
|
|
position: relative;
|
|
}
|
|
|
|
.status.active {
|
|
background: rgba(0, 255, 0, 0.2);
|
|
border: 1px solid #0f0;
|
|
color: #0f0;
|
|
animation: status-pulse 2s infinite;
|
|
}
|
|
|
|
.status.pending {
|
|
background: rgba(255, 255, 0, 0.2);
|
|
border: 1px solid #ff0;
|
|
color: #ff0;
|
|
}
|
|
|
|
.status.inactive {
|
|
background: rgba(255, 0, 0, 0.2);
|
|
border: 1px solid #f00;
|
|
color: #f00;
|
|
}
|
|
|
|
@keyframes status-pulse {
|
|
0%, 100% { opacity: 1; }
|
|
50% { opacity: 0.5; }
|
|
}
|
|
|
|
/* Bottom Controls */
|
|
.bottom-controls {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
gap: 20px;
|
|
}
|
|
|
|
/* Pagination - Holographic Interface */
|
|
.pagination {
|
|
display: flex;
|
|
gap: 10px;
|
|
align-items: center;
|
|
perspective: 1000px;
|
|
}
|
|
|
|
.page-btn {
|
|
padding: 10px 15px;
|
|
background: rgba(0, 255, 255, 0.1);
|
|
border: 1px solid #0ff;
|
|
color: #0ff;
|
|
font-family: 'Share Tech Mono', monospace;
|
|
cursor: pointer;
|
|
transition: all 0.3s;
|
|
transform-style: preserve-3d;
|
|
position: relative;
|
|
}
|
|
|
|
.page-btn::before {
|
|
content: attr(data-page);
|
|
position: absolute;
|
|
inset: 0;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
background: rgba(255, 0, 255, 0.1);
|
|
border: 1px solid #f0f;
|
|
color: #f0f;
|
|
transform: rotateY(180deg) translateZ(1px);
|
|
backface-visibility: hidden;
|
|
}
|
|
|
|
.page-btn:hover {
|
|
transform: rotateY(180deg);
|
|
}
|
|
|
|
.page-btn.active {
|
|
background: rgba(0, 255, 255, 0.3);
|
|
box-shadow: 0 0 20px rgba(0, 255, 255, 0.5);
|
|
transform: translateZ(10px);
|
|
}
|
|
|
|
.page-info {
|
|
color: rgba(0, 255, 255, 0.7);
|
|
font-size: 14px;
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
/* Export Controls - Data Stream Effect */
|
|
.export-controls {
|
|
display: flex;
|
|
gap: 10px;
|
|
align-items: center;
|
|
}
|
|
|
|
.export-btn {
|
|
padding: 12px 20px;
|
|
background: transparent;
|
|
border: 1px solid #ff0;
|
|
color: #ff0;
|
|
font-family: 'Share Tech Mono', monospace;
|
|
cursor: pointer;
|
|
position: relative;
|
|
overflow: hidden;
|
|
text-transform: uppercase;
|
|
transition: all 0.3s;
|
|
}
|
|
|
|
.export-btn::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: 0;
|
|
left: -100%;
|
|
width: 100%;
|
|
height: 100%;
|
|
background: repeating-linear-gradient(
|
|
90deg,
|
|
transparent,
|
|
transparent 10px,
|
|
rgba(255, 255, 0, 0.3) 10px,
|
|
rgba(255, 255, 0, 0.3) 20px
|
|
);
|
|
animation: data-stream 2s linear infinite;
|
|
}
|
|
|
|
.export-btn:hover {
|
|
background: rgba(255, 255, 0, 0.1);
|
|
box-shadow: 0 0 20px rgba(255, 255, 0, 0.5);
|
|
}
|
|
|
|
.export-btn:disabled {
|
|
opacity: 0.5;
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
@keyframes data-stream {
|
|
to { left: 100%; }
|
|
}
|
|
|
|
/* Selection counter */
|
|
.selection-info {
|
|
color: #f0f;
|
|
font-size: 14px;
|
|
text-transform: uppercase;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
}
|
|
|
|
.selection-info::before {
|
|
content: '';
|
|
width: 10px;
|
|
height: 10px;
|
|
background: #f0f;
|
|
display: inline-block;
|
|
animation: blink 1s infinite;
|
|
}
|
|
|
|
@keyframes blink {
|
|
0%, 50% { opacity: 1; }
|
|
51%, 100% { opacity: 0; }
|
|
}
|
|
|
|
/* Loading overlay */
|
|
.loading-overlay {
|
|
position: absolute;
|
|
inset: 0;
|
|
background: rgba(0, 0, 0, 0.9);
|
|
display: none;
|
|
align-items: center;
|
|
justify-content: center;
|
|
z-index: 100;
|
|
}
|
|
|
|
.loading-overlay.active {
|
|
display: flex;
|
|
}
|
|
|
|
.loading-text {
|
|
font-size: 24px;
|
|
color: #0ff;
|
|
text-transform: uppercase;
|
|
letter-spacing: 3px;
|
|
animation: loading-glitch 0.5s infinite;
|
|
}
|
|
|
|
@keyframes loading-glitch {
|
|
0%, 100% {
|
|
text-shadow:
|
|
0 0 10px #0ff,
|
|
0 0 20px #0ff;
|
|
}
|
|
50% {
|
|
text-shadow:
|
|
-2px 0 #f0f,
|
|
2px 0 #ff0,
|
|
0 0 20px #f0f;
|
|
}
|
|
}
|
|
|
|
/* Responsive */
|
|
@media (max-width: 768px) {
|
|
.control-panel {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
|
|
h1 {
|
|
font-size: 2em;
|
|
}
|
|
|
|
.data-table {
|
|
font-size: 14px;
|
|
}
|
|
|
|
.bottom-controls {
|
|
flex-direction: column;
|
|
}
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<!-- Digital rain background -->
|
|
<div class="rain-container" id="rainContainer"></div>
|
|
|
|
<main>
|
|
<h1>Data Explorer - Cyberpunk Future</h1>
|
|
|
|
<div class="data-explorer">
|
|
<!-- Loading overlay -->
|
|
<div class="loading-overlay" id="loadingOverlay">
|
|
<div class="loading-text">ACCESSING DATABASE...</div>
|
|
</div>
|
|
|
|
<!-- Control Panel -->
|
|
<div class="control-panel">
|
|
<!-- Search with glitch effect -->
|
|
<div class="search-container">
|
|
<input type="text" class="search-input" id="searchInput" placeholder="[SEARCH DATABASE]">
|
|
</div>
|
|
|
|
<!-- Filter Controls - Circuit Board -->
|
|
<div class="filter-controls">
|
|
<select class="filter-select" id="statusFilter">
|
|
<option value="">ALL STATUS</option>
|
|
<option value="active">ACTIVE</option>
|
|
<option value="pending">PENDING</option>
|
|
<option value="inactive">INACTIVE</option>
|
|
</select>
|
|
<select class="filter-select" id="typeFilter">
|
|
<option value="">ALL TYPES</option>
|
|
<option value="neural">NEURAL</option>
|
|
<option value="quantum">QUANTUM</option>
|
|
<option value="cyber">CYBER</option>
|
|
<option value="hybrid">HYBRID</option>
|
|
</select>
|
|
</div>
|
|
|
|
<!-- Sort Controls -->
|
|
<div class="sort-controls">
|
|
<button class="sort-btn" data-sort="id">ID</button>
|
|
<button class="sort-btn" data-sort="name">NAME</button>
|
|
<button class="sort-btn" data-sort="value">VALUE</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Data Table - Neon Grid -->
|
|
<div class="data-table-container">
|
|
<table class="data-table">
|
|
<thead>
|
|
<tr>
|
|
<th style="width: 50px;">SEL</th>
|
|
<th>ID</th>
|
|
<th>NAME</th>
|
|
<th>TYPE</th>
|
|
<th>VALUE</th>
|
|
<th>STATUS</th>
|
|
<th>UPDATED</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="dataTableBody">
|
|
<!-- Dynamic content -->
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<!-- Bottom Controls -->
|
|
<div class="bottom-controls">
|
|
<!-- Pagination - Holographic -->
|
|
<div class="pagination" id="pagination">
|
|
<button class="page-btn" data-page="<<"><<</button>
|
|
<button class="page-btn active" data-page="1">1</button>
|
|
<button class="page-btn" data-page="2">2</button>
|
|
<button class="page-btn" data-page="3">3</button>
|
|
<button class="page-btn" data-page="4">4</button>
|
|
<button class="page-btn" data-page="5">5</button>
|
|
<button class="page-btn" data-page=">>">>></button>
|
|
</div>
|
|
|
|
<div class="page-info">
|
|
Page <span id="currentPage">1</span> of <span id="totalPages">10</span>
|
|
</div>
|
|
|
|
<!-- Selection info -->
|
|
<div class="selection-info">
|
|
<span id="selectedCount">0</span> SELECTED
|
|
</div>
|
|
|
|
<!-- Export Controls - Data Stream -->
|
|
<div class="export-controls">
|
|
<button class="export-btn" id="exportBtn" disabled>EXPORT DATA</button>
|
|
<select class="filter-select" id="exportFormat" style="width: auto;">
|
|
<option value="json">JSON</option>
|
|
<option value="csv">CSV</option>
|
|
<option value="xml">XML</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
|
|
<script>
|
|
// Generate digital rain
|
|
function createRain() {
|
|
const container = document.getElementById('rainContainer');
|
|
const columns = Math.floor(window.innerWidth / 20);
|
|
|
|
for (let i = 0; i < columns; i++) {
|
|
const column = document.createElement('div');
|
|
column.className = 'rain-column';
|
|
column.style.left = i * 20 + 'px';
|
|
column.style.animationDuration = (Math.random() * 10 + 5) + 's';
|
|
column.style.animationDelay = Math.random() * 5 + 's';
|
|
|
|
// Random binary/hex characters
|
|
const chars = '01ABCDEF'.split('');
|
|
for (let j = 0; j < 50; j++) {
|
|
column.innerHTML += chars[Math.floor(Math.random() * chars.length)] + '<br>';
|
|
}
|
|
|
|
container.appendChild(column);
|
|
}
|
|
}
|
|
|
|
// Data model
|
|
const dataGenerator = {
|
|
types: ['neural', 'quantum', 'cyber', 'hybrid'],
|
|
statuses: ['active', 'pending', 'inactive'],
|
|
names: [
|
|
'NEXUS_CORE', 'SYNTH_WAVE', 'CYBER_LINK', 'NEURO_NET',
|
|
'QUANTUM_FLUX', 'DATA_STREAM', 'MATRIX_NODE', 'GRID_PULSE',
|
|
'NEON_DRIVE', 'HOLO_DECK', 'CHROME_JACK', 'PIXEL_STORM',
|
|
'GLITCH_MOD', 'RETRO_BURN', 'VAPOR_TRAIL', 'TECH_NOIR'
|
|
],
|
|
|
|
generate(count) {
|
|
const data = [];
|
|
for (let i = 1; i <= count; i++) {
|
|
data.push({
|
|
id: `CYB-${String(i).padStart(4, '0')}`,
|
|
name: this.names[Math.floor(Math.random() * this.names.length)] + '_' + i,
|
|
type: this.types[Math.floor(Math.random() * this.types.length)],
|
|
value: Math.floor(Math.random() * 9999) + 1000,
|
|
status: this.statuses[Math.floor(Math.random() * this.statuses.length)],
|
|
updated: new Date(Date.now() - Math.random() * 7 * 24 * 60 * 60 * 1000).toISOString().split('T')[0]
|
|
});
|
|
}
|
|
return data;
|
|
}
|
|
};
|
|
|
|
// Application state
|
|
const app = {
|
|
data: [],
|
|
filteredData: [],
|
|
currentPage: 1,
|
|
itemsPerPage: 10,
|
|
sortColumn: null,
|
|
sortDirection: 'asc',
|
|
selectedRows: new Set(),
|
|
|
|
init() {
|
|
this.data = dataGenerator.generate(100);
|
|
this.filteredData = [...this.data];
|
|
this.bindEvents();
|
|
this.render();
|
|
createRain();
|
|
},
|
|
|
|
bindEvents() {
|
|
// Search
|
|
document.getElementById('searchInput').addEventListener('input', (e) => {
|
|
this.handleSearch(e.target.value);
|
|
});
|
|
|
|
// Filters
|
|
document.getElementById('statusFilter').addEventListener('change', () => {
|
|
this.applyFilters();
|
|
});
|
|
|
|
document.getElementById('typeFilter').addEventListener('change', () => {
|
|
this.applyFilters();
|
|
});
|
|
|
|
// Sort buttons
|
|
document.querySelectorAll('.sort-btn').forEach(btn => {
|
|
btn.addEventListener('click', (e) => {
|
|
this.handleSort(e.target.dataset.sort);
|
|
});
|
|
});
|
|
|
|
// Export button
|
|
document.getElementById('exportBtn').addEventListener('click', () => {
|
|
this.exportData();
|
|
});
|
|
},
|
|
|
|
handleSearch(query) {
|
|
this.showLoading();
|
|
setTimeout(() => {
|
|
this.applyFilters();
|
|
this.hideLoading();
|
|
}, 300);
|
|
},
|
|
|
|
applyFilters() {
|
|
const searchQuery = document.getElementById('searchInput').value.toLowerCase();
|
|
const statusFilter = document.getElementById('statusFilter').value;
|
|
const typeFilter = document.getElementById('typeFilter').value;
|
|
|
|
this.filteredData = this.data.filter(item => {
|
|
const matchesSearch = !searchQuery ||
|
|
item.id.toLowerCase().includes(searchQuery) ||
|
|
item.name.toLowerCase().includes(searchQuery);
|
|
|
|
const matchesStatus = !statusFilter || item.status === statusFilter;
|
|
const matchesType = !typeFilter || item.type === typeFilter;
|
|
|
|
return matchesSearch && matchesStatus && matchesType;
|
|
});
|
|
|
|
this.currentPage = 1;
|
|
this.render();
|
|
},
|
|
|
|
handleSort(column) {
|
|
const buttons = document.querySelectorAll('.sort-btn');
|
|
buttons.forEach(btn => btn.classList.remove('active'));
|
|
|
|
if (this.sortColumn === column) {
|
|
this.sortDirection = this.sortDirection === 'asc' ? 'desc' : 'asc';
|
|
} else {
|
|
this.sortColumn = column;
|
|
this.sortDirection = 'asc';
|
|
}
|
|
|
|
document.querySelector(`[data-sort="${column}"]`).classList.add('active');
|
|
|
|
// Trigger matrix rain effect during sort
|
|
this.showMatrixEffect();
|
|
|
|
this.filteredData.sort((a, b) => {
|
|
const aVal = a[column];
|
|
const bVal = b[column];
|
|
|
|
if (typeof aVal === 'number') {
|
|
return this.sortDirection === 'asc' ? aVal - bVal : bVal - aVal;
|
|
}
|
|
|
|
return this.sortDirection === 'asc'
|
|
? aVal.localeCompare(bVal)
|
|
: bVal.localeCompare(aVal);
|
|
});
|
|
|
|
this.render();
|
|
},
|
|
|
|
showMatrixEffect() {
|
|
const cells = document.querySelectorAll('.data-table td');
|
|
cells.forEach((cell, i) => {
|
|
setTimeout(() => {
|
|
cell.style.color = '#0f0';
|
|
cell.style.textShadow = '0 0 10px #0f0';
|
|
setTimeout(() => {
|
|
cell.style.color = '';
|
|
cell.style.textShadow = '';
|
|
}, 300);
|
|
}, i * 10);
|
|
});
|
|
},
|
|
|
|
render() {
|
|
this.renderTable();
|
|
this.renderPagination();
|
|
this.updateSelectionInfo();
|
|
},
|
|
|
|
renderTable() {
|
|
const tbody = document.getElementById('dataTableBody');
|
|
const start = (this.currentPage - 1) * this.itemsPerPage;
|
|
const end = start + this.itemsPerPage;
|
|
const pageData = this.filteredData.slice(start, end);
|
|
|
|
tbody.innerHTML = pageData.map((item, index) => `
|
|
<tr data-id="${item.id}" class="${this.selectedRows.has(item.id) ? 'selected' : ''}">
|
|
<td style="--col-index: 0">
|
|
<input type="checkbox" class="row-checkbox"
|
|
${this.selectedRows.has(item.id) ? 'checked' : ''}>
|
|
</td>
|
|
<td style="--col-index: 1">${item.id}</td>
|
|
<td style="--col-index: 2">${item.name}</td>
|
|
<td style="--col-index: 3">${item.type.toUpperCase()}</td>
|
|
<td style="--col-index: 4">${item.value.toLocaleString()}</td>
|
|
<td style="--col-index: 5">
|
|
<span class="status ${item.status}">${item.status.toUpperCase()}</span>
|
|
</td>
|
|
<td style="--col-index: 6">${item.updated}</td>
|
|
</tr>
|
|
`).join('');
|
|
|
|
// Bind row events
|
|
tbody.querySelectorAll('tr').forEach(row => {
|
|
row.addEventListener('click', (e) => {
|
|
if (e.target.type !== 'checkbox') {
|
|
const checkbox = row.querySelector('.row-checkbox');
|
|
checkbox.checked = !checkbox.checked;
|
|
this.toggleRowSelection(row.dataset.id, checkbox.checked);
|
|
}
|
|
});
|
|
|
|
row.querySelector('.row-checkbox').addEventListener('change', (e) => {
|
|
this.toggleRowSelection(row.dataset.id, e.target.checked);
|
|
});
|
|
});
|
|
},
|
|
|
|
renderPagination() {
|
|
const totalPages = Math.ceil(this.filteredData.length / this.itemsPerPage);
|
|
document.getElementById('currentPage').textContent = this.currentPage;
|
|
document.getElementById('totalPages').textContent = totalPages;
|
|
|
|
const pagination = document.getElementById('pagination');
|
|
pagination.innerHTML = '';
|
|
|
|
// Previous button
|
|
const prevBtn = this.createPageButton('<<', () => {
|
|
if (this.currentPage > 1) {
|
|
this.currentPage--;
|
|
this.render();
|
|
}
|
|
});
|
|
pagination.appendChild(prevBtn);
|
|
|
|
// Page buttons
|
|
const maxButtons = 5;
|
|
let start = Math.max(1, this.currentPage - Math.floor(maxButtons / 2));
|
|
let end = Math.min(totalPages, start + maxButtons - 1);
|
|
|
|
if (end - start < maxButtons - 1) {
|
|
start = Math.max(1, end - maxButtons + 1);
|
|
}
|
|
|
|
for (let i = start; i <= end; i++) {
|
|
const btn = this.createPageButton(i, () => {
|
|
this.currentPage = i;
|
|
this.render();
|
|
});
|
|
if (i === this.currentPage) {
|
|
btn.classList.add('active');
|
|
}
|
|
pagination.appendChild(btn);
|
|
}
|
|
|
|
// Next button
|
|
const nextBtn = this.createPageButton('>>', () => {
|
|
if (this.currentPage < totalPages) {
|
|
this.currentPage++;
|
|
this.render();
|
|
}
|
|
});
|
|
pagination.appendChild(nextBtn);
|
|
},
|
|
|
|
createPageButton(text, onClick) {
|
|
const btn = document.createElement('button');
|
|
btn.className = 'page-btn';
|
|
btn.textContent = text;
|
|
btn.setAttribute('data-page', text);
|
|
btn.addEventListener('click', onClick);
|
|
return btn;
|
|
},
|
|
|
|
toggleRowSelection(id, selected) {
|
|
if (selected) {
|
|
this.selectedRows.add(id);
|
|
} else {
|
|
this.selectedRows.delete(id);
|
|
}
|
|
|
|
const row = document.querySelector(`tr[data-id="${id}"]`);
|
|
if (row) {
|
|
row.classList.toggle('selected', selected);
|
|
}
|
|
|
|
this.updateSelectionInfo();
|
|
},
|
|
|
|
updateSelectionInfo() {
|
|
const count = this.selectedRows.size;
|
|
document.getElementById('selectedCount').textContent = count;
|
|
document.getElementById('exportBtn').disabled = count === 0;
|
|
},
|
|
|
|
exportData() {
|
|
const format = document.getElementById('exportFormat').value;
|
|
const exportData = this.filteredData.filter(item =>
|
|
this.selectedRows.has(item.id)
|
|
);
|
|
|
|
this.showDataStream();
|
|
|
|
setTimeout(() => {
|
|
let content = '';
|
|
|
|
switch(format) {
|
|
case 'json':
|
|
content = JSON.stringify(exportData, null, 2);
|
|
break;
|
|
case 'csv':
|
|
const headers = Object.keys(exportData[0]).join(',');
|
|
const rows = exportData.map(item =>
|
|
Object.values(item).join(',')
|
|
).join('\n');
|
|
content = headers + '\n' + rows;
|
|
break;
|
|
case 'xml':
|
|
content = '<?xml version="1.0" encoding="UTF-8"?>\n<data>\n';
|
|
exportData.forEach(item => {
|
|
content += ' <record>\n';
|
|
Object.entries(item).forEach(([key, value]) => {
|
|
content += ` <${key}>${value}</${key}>\n`;
|
|
});
|
|
content += ' </record>\n';
|
|
});
|
|
content += '</data>';
|
|
break;
|
|
}
|
|
|
|
// Create download
|
|
const blob = new Blob([content], { type: 'text/plain' });
|
|
const url = URL.createObjectURL(blob);
|
|
const a = document.createElement('a');
|
|
a.href = url;
|
|
a.download = `cyberpunk_data_${Date.now()}.${format}`;
|
|
a.click();
|
|
URL.revokeObjectURL(url);
|
|
|
|
this.hideLoading();
|
|
}, 1500);
|
|
},
|
|
|
|
showDataStream() {
|
|
this.showLoading();
|
|
const overlay = document.getElementById('loadingOverlay');
|
|
overlay.querySelector('.loading-text').textContent = 'STREAMING DATA...';
|
|
},
|
|
|
|
showLoading() {
|
|
document.getElementById('loadingOverlay').classList.add('active');
|
|
},
|
|
|
|
hideLoading() {
|
|
document.getElementById('loadingOverlay').classList.remove('active');
|
|
}
|
|
};
|
|
|
|
// Initialize
|
|
app.init();
|
|
</script>
|
|
</body>
|
|
</html> |