mirror of
https://github.com/deunix-educ/Fail2banMqttActionBanishment.git
synced 2026-08-24 03:11:58 +02:00
First commit
This commit is contained in:
@@ -0,0 +1,199 @@
|
||||
/* ============================================================
|
||||
Emitter — Django Admin Theme Override
|
||||
Thème sombre, couleurs depuis main.css
|
||||
============================================================ */
|
||||
|
||||
/* --- Variables Django admin 4.x ----------------------------- */
|
||||
:root {
|
||||
--primary : #00d4aa;
|
||||
--secondary : #00a884;
|
||||
--accent : #00d4aa;
|
||||
--primary-fg : #0d1117;
|
||||
|
||||
--body-fg : #e6edf3;
|
||||
--body-bg : #0d1117;
|
||||
--body-quiet-color : #8b949e;
|
||||
--body-loud-color : #e6edf3;
|
||||
|
||||
--header-color : #e6edf3;
|
||||
--header-branding-color: #00d4aa;
|
||||
--header-bg : #161b22;
|
||||
--header-link-color : #e6edf3;
|
||||
|
||||
--breadcrumbs-fg : #8b949e;
|
||||
--breadcrumbs-link-fg : #00d4aa;
|
||||
--breadcrumbs-bg : #161b22;
|
||||
|
||||
--link-fg : #00d4aa;
|
||||
--link-hover-color : #00a884;
|
||||
--link-selected-fg : #00d4aa;
|
||||
|
||||
--hairline-color : #1f2a36;
|
||||
--border-color : #1f2a36;
|
||||
|
||||
--error-fg : #f85149;
|
||||
|
||||
--message-success-bg : rgba(63,185,80,.15);
|
||||
--message-warning-bg : rgba(210,153,34,.15);
|
||||
--message-error-bg : rgba(248,81,73,.15);
|
||||
|
||||
--darkened-bg : #161b22;
|
||||
--selected-bg : #21262d;
|
||||
--selected-row : #2d333b;
|
||||
|
||||
--button-fg : #0d1117;
|
||||
--button-bg : #00d4aa;
|
||||
--button-hover-bg : #00a884;
|
||||
--default-button-fg : #0d1117;
|
||||
--default-button-bg : #00d4aa;
|
||||
--default-button-hover-bg: #00a884;
|
||||
--close-button-bg : #21262d;
|
||||
--close-button-hover-bg : #2d333b;
|
||||
|
||||
--object-tools-fg : #0d1117;
|
||||
--object-tools-bg : #00d4aa;
|
||||
--object-tools-hover-bg : #00a884;
|
||||
|
||||
--font-family : 'Segoe UI', system-ui, -apple-system, sans-serif;
|
||||
}
|
||||
|
||||
/* --- Global ------------------------------------------------- */
|
||||
html, body { font-family: var(--font-family); background: var(--body-bg); color: var(--body-fg); }
|
||||
a { color: var(--link-fg); }
|
||||
a:hover { color: var(--link-hover-color); }
|
||||
|
||||
/* --- Header ------------------------------------------------- */
|
||||
#header {
|
||||
background : var(--header-bg);
|
||||
color : var(--header-color);
|
||||
border-bottom: 1px solid #1f2a36;
|
||||
}
|
||||
#header a:link, #header a:visited { color: var(--header-color); }
|
||||
#branding h1, #branding h1 a:link, #branding h1 a:visited {
|
||||
color: var(--accent);
|
||||
font-weight: 700;
|
||||
}
|
||||
#user-tools a { color: #8b949e; }
|
||||
#user-tools a:hover { color: var(--accent); }
|
||||
|
||||
/* --- Breadcrumbs -------------------------------------------- */
|
||||
div.breadcrumbs {
|
||||
background : var(--breadcrumbs-bg);
|
||||
color : var(--breadcrumbs-fg);
|
||||
border-bottom: 1px solid #1f2a36;
|
||||
}
|
||||
div.breadcrumbs a { color: var(--accent); }
|
||||
div.breadcrumbs a:hover { color: var(--link-hover-color); }
|
||||
|
||||
/* --- Sidebar / nav ------------------------------------------ */
|
||||
#nav-sidebar { background: #161b22; border-right: 1px solid #1f2a36; }
|
||||
#nav-sidebar .current-app .section:link,
|
||||
#nav-sidebar .current-app .section:visited { color: var(--accent); }
|
||||
|
||||
/* --- Content area ------------------------------------------- */
|
||||
#content { background: var(--body-bg); }
|
||||
#content-main { background: var(--body-bg); }
|
||||
.colMS #content-main { border-right: 1px solid #1f2a36; }
|
||||
|
||||
/* --- Module headers (blocs gris-bleu → sombre+accent) ------- */
|
||||
.module caption,
|
||||
.inline-group h2,
|
||||
fieldset.module h2,
|
||||
div.submit-row {
|
||||
background : #161b22;
|
||||
color : var(--body-fg);
|
||||
border-bottom: 1px solid #1f2a36;
|
||||
}
|
||||
.module { background: #161b22; border: 1px solid #1f2a36; }
|
||||
fieldset.module { background: #161b22; border: 1px solid #1f2a36; }
|
||||
|
||||
/* --- Tables ------------------------------------------------- */
|
||||
#result_list thead th { background: #161b22; color: var(--body-quiet-color); border-bottom: 1px solid #1f2a36; }
|
||||
#result_list tr.row1 { background: #0d1117; }
|
||||
#result_list tr.row2 { background: #111820; }
|
||||
#result_list tr:hover td { background: #21262d !important; }
|
||||
#result_list td, #result_list th { border-right-color: #1f2a36; }
|
||||
table { border-color: #1f2a36; }
|
||||
td, th { border-color: #1f2a36; }
|
||||
|
||||
/* --- Filtres (colonne droite) -------------------------------- */
|
||||
#changelist-filter { background: #161b22; border-left: 1px solid #1f2a36; }
|
||||
#changelist-filter h2 { background: #161b22; color: var(--accent); border-bottom: 1px solid #1f2a36; }
|
||||
#changelist-filter h3 { color: var(--body-quiet-color); border-bottom: 1px solid #1f2a36; }
|
||||
#changelist-filter li.selected a { color: var(--accent); font-weight: 600; }
|
||||
#changelist-filter a { color: var(--body-fg); }
|
||||
#changelist-filter a:hover { color: var(--accent); }
|
||||
|
||||
/* --- Formulaires -------------------------------------------- */
|
||||
input, select, textarea {
|
||||
background : #21262d;
|
||||
color : var(--body-fg);
|
||||
border : 1px solid #2d3748;
|
||||
border-radius: 4px;
|
||||
}
|
||||
input:focus, select:focus, textarea:focus {
|
||||
border-color: var(--accent);
|
||||
outline : none;
|
||||
box-shadow : 0 0 0 2px rgba(0,212,170,.2);
|
||||
}
|
||||
.form-row { border-bottom-color: #1f2a36; }
|
||||
.aligned label { color: var(--body-quiet-color); }
|
||||
.help { color: #8b949e; }
|
||||
|
||||
/* --- Boutons ------------------------------------------------ */
|
||||
.button, input[type=submit], input[type=button], .submit-row input, a.button {
|
||||
background : var(--button-bg);
|
||||
color : var(--button-fg);
|
||||
border : none;
|
||||
border-radius: 4px;
|
||||
font-weight : 600;
|
||||
}
|
||||
.button:hover, input[type=submit]:hover, input[type=button]:hover {
|
||||
background: var(--button-hover-bg);
|
||||
color : var(--button-fg);
|
||||
}
|
||||
.button.default, input[type=submit].default {
|
||||
background: var(--accent);
|
||||
color : #0d1117;
|
||||
}
|
||||
a.deletelink { background: #7f1d1d; color: #fca5a5; border-radius: 4px; padding: 4px 8px; }
|
||||
a.deletelink:hover { background: #991b1b; color: #fca5a5; }
|
||||
|
||||
/* --- Object tools (haut des formulaires) ------------------- */
|
||||
ul.object-tools li a { background: #21262d; color: var(--accent); border: 1px solid #1f2a36; }
|
||||
ul.object-tools li a:hover { background: #2d333b; }
|
||||
ul.object-tools li a.addlink { background: var(--accent); color: #0d1117; border: none; }
|
||||
ul.object-tools li a.addlink:hover { background: var(--link-hover-color); }
|
||||
|
||||
/* --- Pagination -------------------------------------------- */
|
||||
.paginator a, .paginator span {
|
||||
background : #21262d;
|
||||
color : var(--body-fg);
|
||||
border : 1px solid #1f2a36;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.paginator a:hover { background: #2d333b; }
|
||||
.paginator .this-page { background: var(--accent); color: #0d1117; border-color: var(--accent); }
|
||||
|
||||
/* --- Messages (success/warning/error) ---------------------- */
|
||||
.messagelist li { border-radius: 4px; padding: 10px 14px; }
|
||||
.messagelist li.success { background: rgba(63,185,80,.15); color: #3fb950; border: 1px solid rgba(63,185,80,.3); }
|
||||
.messagelist li.warning { background: rgba(210,153,34,.15); color: #d29922; border: 1px solid rgba(210,153,34,.3); }
|
||||
.messagelist li.error { background: rgba(248,81,73,.15); color: #f85149; border: 1px solid rgba(248,81,73,.3); }
|
||||
|
||||
/* --- Dashboard (index admin) -------------------------------- */
|
||||
#content-main .module h2 { color: var(--body-fg); }
|
||||
.dashboard #content { background: var(--body-bg); }
|
||||
|
||||
/* --- Inlines ------------------------------------------------ */
|
||||
.inline-group { border: 1px solid #1f2a36; }
|
||||
.inline-group thead tr { background: #161b22; }
|
||||
.inline-related h3 { background: #161b22; border: 1px solid #1f2a36; color: var(--accent); }
|
||||
|
||||
/* --- Misc --------------------------------------------------- */
|
||||
.errornote { background: rgba(248,81,73,.1); color: #f85149; border: 1px solid rgba(248,81,73,.4); }
|
||||
.errorlist li { color: #f85149; }
|
||||
.selector { background: #161b22; border: 1px solid #1f2a36; }
|
||||
.selector select { background: #0d1117; color: var(--body-fg); border: none; }
|
||||
.selector-available h2, .selector-chosen h2 { background: #161b22; color: var(--body-quiet-color); }
|
||||
.selector-filter input { background: #21262d; color: var(--body-fg); border-color: #2d3748; }
|
||||
@@ -0,0 +1,760 @@
|
||||
/* ============================================================
|
||||
Feuille de style principale — main
|
||||
Thème sombre, zéro dépendance externe
|
||||
============================================================ */
|
||||
|
||||
/* --- Variables -------------------------------------------- */
|
||||
:root {
|
||||
--bg-base : #0d1117;
|
||||
--bg-surface : #1c2128;
|
||||
--bg-elevated : #2d333b;
|
||||
--bg-hover : #373e47;
|
||||
|
||||
--accent : #00d4aa;
|
||||
--accent-dim : #00a884;
|
||||
--accent-glow : rgba(0,212,170,.15);
|
||||
|
||||
--danger : #f85149;
|
||||
--warning : #e3b341;
|
||||
--info : #58a6ff;
|
||||
--success : #56d364;
|
||||
|
||||
--text-primary : #f0f6fc;
|
||||
--text-secondary:#b1bac4;
|
||||
--text-muted : #768390;
|
||||
|
||||
--border : #30363d;
|
||||
|
||||
--radius-sm : 6px;
|
||||
--radius-md : 10px;
|
||||
--radius-lg : 16px;
|
||||
|
||||
--shadow-sm : 0 1px 3px rgba(0,0,0,.4);
|
||||
--shadow-md : 0 4px 16px rgba(0,0,0,.5);
|
||||
--shadow-lg : 0 8px 32px rgba(0,0,0,.6);
|
||||
|
||||
--sidebar-w : 220px;
|
||||
--font : 'Segoe UI', system-ui, -apple-system, sans-serif;
|
||||
}
|
||||
|
||||
/* --- Reset ------------------------------------------------- */
|
||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
html { font-size: 15px; scroll-behavior: smooth; }
|
||||
body {
|
||||
font-family : var(--font);
|
||||
background : var(--bg-base);
|
||||
color : var(--text-primary);
|
||||
min-height : 100vh;
|
||||
display : flex;
|
||||
flex-direction : row;
|
||||
line-height : 1.6;
|
||||
}
|
||||
a { color: var(--accent); text-decoration: none; }
|
||||
a:hover { color: var(--accent-dim); }
|
||||
img { display: block; }
|
||||
button { font-family: var(--font); cursor: pointer; }
|
||||
|
||||
/* --- Sidebar ---------------------------------------------- */
|
||||
.vc-sidebar {
|
||||
position : fixed;
|
||||
top: 0; left: 0; bottom: 0;
|
||||
width : var(--sidebar-w);
|
||||
background : var(--bg-surface);
|
||||
border-right : 1px solid var(--border);
|
||||
display : flex;
|
||||
flex-direction : column;
|
||||
z-index : 1000;
|
||||
overflow-y : auto;
|
||||
}
|
||||
.vc-brand {
|
||||
display : flex;
|
||||
flex-direction : column;
|
||||
align-items : flex-start;
|
||||
gap : 2px;
|
||||
font-size : 1.1rem;
|
||||
font-weight : 700;
|
||||
color : var(--accent);
|
||||
padding : 20px 16px 16px;
|
||||
border-bottom : 1px solid var(--border);
|
||||
white-space : nowrap;
|
||||
}
|
||||
.vc-brand:hover { color: var(--accent); }
|
||||
.vc-brand small {
|
||||
display : block;
|
||||
font-size : 0.7rem;
|
||||
font-weight : 400;
|
||||
color : var(--text-muted);
|
||||
}
|
||||
|
||||
.vc-sidebar-nav {
|
||||
display : flex;
|
||||
flex-direction : column;
|
||||
gap : 2px;
|
||||
padding : 12px 8px;
|
||||
flex : 1;
|
||||
}
|
||||
.vc-nav-link {
|
||||
display : flex;
|
||||
align-items : center;
|
||||
gap : 10px;
|
||||
padding : 9px 12px;
|
||||
border-radius : var(--radius-sm);
|
||||
color : var(--text-secondary);
|
||||
font-size : .9rem;
|
||||
transition : background .15s, color .15s;
|
||||
}
|
||||
.vc-nav-link:hover,
|
||||
.vc-nav-link--active {
|
||||
background : var(--bg-hover);
|
||||
color : var(--text-primary);
|
||||
}
|
||||
.vc-nav-link--active {
|
||||
color : var(--accent);
|
||||
}
|
||||
.vc-nav-link { position: relative; }
|
||||
button.vc-nav-link {
|
||||
width : 100%;
|
||||
background : transparent;
|
||||
border : none;
|
||||
cursor : pointer;
|
||||
text-align : left;
|
||||
font-family : inherit;
|
||||
}
|
||||
.vc-nav-alert-badge {
|
||||
margin-left : auto;
|
||||
display : inline-flex;
|
||||
align-items : center;
|
||||
justify-content: center;
|
||||
min-width : 18px;
|
||||
height : 18px;
|
||||
padding : 0 5px;
|
||||
border-radius : 9px;
|
||||
font-size : .7rem;
|
||||
font-weight : 700;
|
||||
background : var(--danger, #f85149);
|
||||
color : #fff;
|
||||
line-height : 1;
|
||||
}
|
||||
|
||||
.vc-sidebar-bottom {
|
||||
border-top : 1px solid var(--border);
|
||||
padding : 12px 8px;
|
||||
display : flex;
|
||||
flex-direction : column;
|
||||
gap : 4px;
|
||||
}
|
||||
.vc-sidebar-user {
|
||||
display : flex;
|
||||
align-items : center;
|
||||
gap : 8px;
|
||||
padding : 8px 12px;
|
||||
border-radius : var(--radius-sm);
|
||||
color : var(--text-secondary);
|
||||
font-size : .88rem;
|
||||
}
|
||||
.vc-logout-btn {
|
||||
display : flex;
|
||||
align-items : center;
|
||||
gap : 8px;
|
||||
width : 100%;
|
||||
padding : 8px 12px;
|
||||
background : transparent;
|
||||
border : none;
|
||||
border-radius : var(--radius-sm);
|
||||
color : var(--danger);
|
||||
font-size : .88rem;
|
||||
text-align : left;
|
||||
transition : background .12s;
|
||||
}
|
||||
.vc-logout-btn:hover { background: rgba(248,81,73,.1); }
|
||||
|
||||
/* --- Navbar pagination ------------------------------------ */
|
||||
.vc-nav-page {
|
||||
display : none;
|
||||
flex-direction : column;
|
||||
gap : 2px;
|
||||
}
|
||||
.vc-nav-page--active {
|
||||
display : flex;
|
||||
animation : vc-page-in .18s ease;
|
||||
}
|
||||
@keyframes vc-page-in {
|
||||
from { opacity: 0; transform: translateY(5px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
.vc-nav-pager {
|
||||
display : flex;
|
||||
align-items : center;
|
||||
justify-content: center;
|
||||
gap : 6px;
|
||||
padding : 5px 0 2px;
|
||||
}
|
||||
.vc-pager-btn {
|
||||
background : transparent;
|
||||
border : none;
|
||||
color : var(--text-muted);
|
||||
font-size : 1.15rem;
|
||||
line-height : 1;
|
||||
padding : 1px 5px;
|
||||
border-radius : var(--radius-sm);
|
||||
cursor : pointer;
|
||||
transition : color .12s, background .12s;
|
||||
}
|
||||
.vc-pager-btn:hover:not(:disabled) {
|
||||
color : var(--text-primary);
|
||||
background : var(--bg-hover);
|
||||
}
|
||||
.vc-pager-btn:disabled { opacity: .25; cursor: default; }
|
||||
.vc-pager-dots {
|
||||
display : flex;
|
||||
gap : 5px;
|
||||
align-items : center;
|
||||
}
|
||||
.vc-pager-dot {
|
||||
width : 5px;
|
||||
height : 5px;
|
||||
border-radius : 50%;
|
||||
background : var(--text-muted);
|
||||
cursor : pointer;
|
||||
transition : background .15s, transform .15s;
|
||||
}
|
||||
.vc-pager-dot:hover { background: var(--text-secondary); }
|
||||
.vc-pager-dot--active {
|
||||
background : var(--accent);
|
||||
transform : scale(1.4);
|
||||
}
|
||||
|
||||
/* --- Sidebar collapse (desktop) --------------------------- */
|
||||
.vc-sidebar {
|
||||
transition : transform .25s ease;
|
||||
}
|
||||
.vc-sidebar--collapsed {
|
||||
transform : translateX(-100%);
|
||||
}
|
||||
.vc-sidebar-collapse-btn {
|
||||
display : flex;
|
||||
align-items : center;
|
||||
justify-content: center;
|
||||
width : 100%;
|
||||
padding : 7px 12px;
|
||||
background : transparent;
|
||||
border : none;
|
||||
border-top : 1px solid var(--border);
|
||||
color : var(--text-muted);
|
||||
font-size : .8rem;
|
||||
gap : 6px;
|
||||
cursor : pointer;
|
||||
transition : color .15s, background .15s;
|
||||
font-family : inherit;
|
||||
}
|
||||
.vc-sidebar-collapse-btn:hover { color: var(--text-primary); background: var(--bg-hover); }
|
||||
|
||||
/* Bouton flottant pour ré-ouvrir le sidebar (desktop) */
|
||||
.vc-sidebar-open-btn {
|
||||
display : none;
|
||||
position : fixed;
|
||||
top : 12px;
|
||||
left : 8px;
|
||||
z-index : 1100;
|
||||
background : var(--bg-surface);
|
||||
border : 1px solid var(--border);
|
||||
border-radius : var(--radius-sm);
|
||||
padding : 6px 8px;
|
||||
color : var(--text-primary);
|
||||
cursor : pointer;
|
||||
}
|
||||
.vc-sidebar-open-btn:hover { background: var(--bg-hover); }
|
||||
.vc-main-wrap { transition: margin-left .25s ease; }
|
||||
|
||||
/* --- Hamburger (mobile) ----------------------------------- */
|
||||
.vc-sidebar-toggle {
|
||||
display : none;
|
||||
position : fixed;
|
||||
top : 12px;
|
||||
left : 12px;
|
||||
z-index : 1100;
|
||||
background : var(--bg-surface);
|
||||
border : 1px solid var(--border);
|
||||
border-radius : var(--radius-sm);
|
||||
padding : 6px 8px;
|
||||
color : var(--text-primary);
|
||||
}
|
||||
|
||||
/* --- Layout wrapper --------------------------------------- */
|
||||
.vc-main-wrap {
|
||||
flex : 1;
|
||||
display : flex;
|
||||
flex-direction : column;
|
||||
min-height : 100vh;
|
||||
min-width : 0;
|
||||
}
|
||||
.vc-main-wrap--with-sidebar {
|
||||
margin-left : var(--sidebar-w);
|
||||
}
|
||||
|
||||
/* --- Main / layout ---------------------------------------- */
|
||||
.vc-main { flex: 1; }
|
||||
.vc-container { max-width: 1280px; margin: 0 auto; padding: 32px 24px; }
|
||||
.vc-container--narrow { max-width: 900px; }
|
||||
.vc-grid-2col { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
|
||||
|
||||
/* --- En-tête de page -------------------------------------- */
|
||||
.vc-page-header {
|
||||
display : flex;
|
||||
align-items : center;
|
||||
justify-content: space-between;
|
||||
margin-bottom : 28px;
|
||||
gap : 16px;
|
||||
flex-wrap : wrap;
|
||||
}
|
||||
.vc-page-header h2 { font-size: 1.5rem; font-weight: 700; display: flex; align-items: center; gap: 10px; }
|
||||
.vc-page-header h2 svg { color: var(--accent); }
|
||||
.vc-header-actions { display: flex; gap: 8px; }
|
||||
|
||||
/* --- Cartes ----------------------------------------------- */
|
||||
.vc-card {
|
||||
background : var(--bg-surface);
|
||||
border : 1px solid var(--border);
|
||||
border-radius : var(--radius-lg);
|
||||
padding : 24px;
|
||||
margin-bottom : 20px;
|
||||
}
|
||||
.vc-card-title {
|
||||
font-size : 1rem;
|
||||
font-weight : 600;
|
||||
margin-bottom : 16px;
|
||||
display : flex;
|
||||
align-items : center;
|
||||
gap : 8px;
|
||||
}
|
||||
.vc-card-title svg { color: var(--accent); }
|
||||
|
||||
/* --- Boutons ---------------------------------------------- */
|
||||
.vc-btn {
|
||||
display : inline-flex;
|
||||
align-items : center;
|
||||
justify-content: center;
|
||||
gap : 7px;
|
||||
padding : 8px 18px;
|
||||
border-radius : var(--radius-sm);
|
||||
font-size : .9rem;
|
||||
font-weight : 500;
|
||||
line-height : 1.4;
|
||||
border : 1px solid transparent;
|
||||
transition : all .15s;
|
||||
white-space : nowrap;
|
||||
text-decoration: none;
|
||||
}
|
||||
.vc-btn:focus { outline: 2px solid var(--accent); outline-offset: 2px; }
|
||||
.vc-btn--primary { background: var(--accent); color: #0d1117; border-color: var(--accent); }
|
||||
.vc-btn--primary:hover { background: var(--accent-dim); border-color: var(--accent-dim); color: #0d1117; }
|
||||
.vc-btn--ghost { background: var(--bg-surface); color: var(--text-primary); border-color: #6e7681; }
|
||||
.vc-btn--ghost:hover { background: var(--bg-hover); color: var(--text-primary); border-color: #8b949e; }
|
||||
.vc-btn--danger { background: transparent; color: var(--danger); border-color: var(--danger); }
|
||||
.vc-btn--danger:hover { background: var(--danger); color: #fff; }
|
||||
.vc-btn--large { padding: 12px 28px; font-size: 1rem; }
|
||||
.vc-btn--block { width: 100%; }
|
||||
.vc-btn--sm { padding: 5px 10px; font-size: .82rem; }
|
||||
|
||||
/* --- Formulaires ------------------------------------------ */
|
||||
.vc-form-group { margin-bottom: 18px; }
|
||||
.vc-form-group label {
|
||||
display : flex;
|
||||
align-items : center;
|
||||
gap : 6px;
|
||||
font-size : .85rem;
|
||||
font-weight : 500;
|
||||
color : var(--text-secondary);
|
||||
margin-bottom : 6px;
|
||||
}
|
||||
.vc-form-group input[type="text"],
|
||||
.vc-form-group input[type="email"],
|
||||
.vc-form-group input[type="password"],
|
||||
.vc-form-group input[type="number"],
|
||||
.vc-form-group textarea,
|
||||
.vc-form-group select {
|
||||
width : 100%;
|
||||
padding : 9px 12px;
|
||||
background : var(--bg-elevated);
|
||||
border : 1px solid var(--border);
|
||||
border-radius : var(--radius-sm);
|
||||
color : var(--text-primary);
|
||||
font-size : .9rem;
|
||||
font-family : var(--font);
|
||||
transition : border-color .15s, box-shadow .15s;
|
||||
}
|
||||
.vc-form-group input:focus,
|
||||
.vc-form-group textarea:focus,
|
||||
.vc-form-group select:focus {
|
||||
outline : none;
|
||||
border-color : var(--accent);
|
||||
box-shadow : 0 0 0 3px var(--accent-glow);
|
||||
}
|
||||
.vc-form-group input::placeholder,
|
||||
.vc-form-group textarea::placeholder { color: var(--text-muted); }
|
||||
.vc-field-error { color: var(--danger); font-size: .82rem; margin-top: 4px; }
|
||||
.vc-field-hint { color: var(--text-muted); font-size: .8rem; margin-top: 4px; }
|
||||
.vc-form-actions {
|
||||
display : flex;
|
||||
gap : 10px;
|
||||
justify-content: flex-end;
|
||||
padding-top : 16px;
|
||||
border-top : 1px solid var(--border);
|
||||
margin-top : 20px;
|
||||
}
|
||||
|
||||
/* --- Badges ----------------------------------------------- */
|
||||
.vc-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 3px 10px;
|
||||
border-radius: 20px;
|
||||
font-size: .78rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
.vc-badge--success { background: rgba(63,185,80,.15); color: var(--success); }
|
||||
.vc-badge--danger { background: rgba(248,81,73,.15); color: var(--danger); }
|
||||
.vc-badge--info { background: rgba(56,139,253,.15); color: var(--info); }
|
||||
.vc-badge--warning { background: rgba(210,153,34,.15); color: var(--warning); }
|
||||
|
||||
/* --- Messages système ------------------------------------- */
|
||||
.vc-messages {
|
||||
position : fixed;
|
||||
top : 12px;
|
||||
right : 16px;
|
||||
z-index : 2000;
|
||||
display : flex;
|
||||
flex-direction : column;
|
||||
gap : 8px;
|
||||
max-width : 360px;
|
||||
}
|
||||
.vc-alert {
|
||||
display : flex;
|
||||
align-items : center;
|
||||
justify-content: space-between;
|
||||
gap : 12px;
|
||||
padding : 12px 16px;
|
||||
border-radius : var(--radius-md);
|
||||
background : var(--bg-elevated);
|
||||
border-left : 4px solid var(--accent);
|
||||
box-shadow : var(--shadow-md);
|
||||
font-size : .88rem;
|
||||
}
|
||||
.vc-alert--error { border-color: var(--danger); }
|
||||
.vc-alert--warning { border-color: var(--warning); }
|
||||
.vc-alert--info { border-color: var(--info); }
|
||||
.vc-alert button { background: none; border: none; color: var(--text-muted); font-size: .9rem; padding: 2px; flex-shrink: 0; }
|
||||
.vc-alert button:hover { color: var(--text-primary); }
|
||||
|
||||
/* --- Authentification ------------------------------------- */
|
||||
.vc-auth-wrap {
|
||||
min-height : 100vh;
|
||||
display : flex;
|
||||
align-items : center;
|
||||
justify-content: center;
|
||||
padding : 32px 16px;
|
||||
background : radial-gradient(ellipse at 60% 30%, rgba(0,212,170,.06) 0%, transparent 70%);
|
||||
}
|
||||
.vc-auth-card {
|
||||
width : 100%;
|
||||
max-width : 420px;
|
||||
background : var(--bg-surface);
|
||||
border : 1px solid var(--border);
|
||||
border-radius : var(--radius-lg);
|
||||
padding : 40px 36px;
|
||||
box-shadow : var(--shadow-lg);
|
||||
}
|
||||
.vc-auth-header { text-align: center; margin-bottom: 28px; }
|
||||
.vc-auth-icon {
|
||||
width : 56px; height: 56px;
|
||||
border-radius : 50%;
|
||||
background : var(--accent-glow);
|
||||
border : 1px solid var(--accent);
|
||||
display : flex;
|
||||
align-items : center;
|
||||
justify-content: center;
|
||||
margin : 0 auto 16px;
|
||||
color : var(--accent);
|
||||
}
|
||||
.vc-auth-header h1 { font-size: 1.6rem; margin-bottom: 6px; }
|
||||
.vc-auth-header p { color: var(--text-secondary); font-size: .88rem; }
|
||||
|
||||
/* --- Table générique -------------------------------------- */
|
||||
.vc-table { width: 100%; border-collapse: collapse; font-size: .9rem; }
|
||||
.vc-table th {
|
||||
text-align: left; padding: 10px 12px;
|
||||
border-bottom: 2px solid var(--border);
|
||||
color: var(--text-secondary); font-weight: 600; font-size: .8rem;
|
||||
text-transform: uppercase; letter-spacing: .05em; white-space: nowrap;
|
||||
}
|
||||
.vc-table td { padding: 10px 12px; border-bottom: 1px solid var(--border); vertical-align: middle; }
|
||||
.vc-table tr:last-child td { border-bottom: none; }
|
||||
.vc-table tr:hover td { background: var(--bg-elevated); }
|
||||
|
||||
/* --- État vide -------------------------------------------- */
|
||||
.vc-empty-state { text-align: center; padding: 72px 24px; }
|
||||
.vc-empty-state svg { color: var(--bg-elevated); display: block; margin: 0 auto 16px; }
|
||||
.vc-empty-state h3 { color: var(--text-secondary); margin-bottom: 8px; }
|
||||
.vc-empty-state p { color: var(--text-muted); margin-bottom: 16px; }
|
||||
|
||||
/* --- Dashboard grille ------------------------------------- */
|
||||
.vc-dashboard-grid {
|
||||
display : grid;
|
||||
grid-template-columns: repeat(12, 1fr);
|
||||
grid-auto-rows : 120px;
|
||||
gap : 12px;
|
||||
padding : 24px;
|
||||
align-items : start;
|
||||
}
|
||||
.vc-widget {
|
||||
background : var(--bg-surface);
|
||||
border : 1px solid var(--border);
|
||||
border-radius : var(--radius-lg);
|
||||
overflow : hidden;
|
||||
display : flex;
|
||||
flex-direction : column;
|
||||
transition : border-color .2s, box-shadow .2s;
|
||||
height : 100%;
|
||||
}
|
||||
.vc-widget:hover { border-color: var(--accent); box-shadow: 0 4px 20px rgba(0,212,170,.08); }
|
||||
.vc-widget--dragging {
|
||||
opacity : .5;
|
||||
border-color : var(--accent);
|
||||
}
|
||||
.vc-widget--drag-over {
|
||||
border-color : var(--accent);
|
||||
box-shadow : 0 0 0 2px var(--accent-glow);
|
||||
}
|
||||
.vc-widget[draggable="true"] { cursor: grab; }
|
||||
.vc-widget[draggable="true"]:active { cursor: grabbing; }
|
||||
.vc-widget-header {
|
||||
display : flex;
|
||||
align-items : center;
|
||||
justify-content: space-between;
|
||||
padding : 10px 14px;
|
||||
border-bottom : 1px solid var(--border);
|
||||
font-size : .85rem;
|
||||
font-weight : 600;
|
||||
flex-shrink : 0;
|
||||
gap : 8px;
|
||||
}
|
||||
.vc-widget-header svg { color: var(--accent); flex-shrink: 0; }
|
||||
.vc-widget-body {
|
||||
padding : 14px;
|
||||
flex : 1;
|
||||
overflow : hidden;
|
||||
}
|
||||
|
||||
/* Widget : texte / boîte */
|
||||
.vc-widget-text {
|
||||
font-size : .9rem;
|
||||
line-height : 1.7;
|
||||
color : var(--text-secondary);
|
||||
word-break : break-word;
|
||||
}
|
||||
|
||||
/* Widget : Grafana iframe */
|
||||
.vc-widget-iframe {
|
||||
width : 100%;
|
||||
height : 100%;
|
||||
border : none;
|
||||
display : block;
|
||||
}
|
||||
.vc-widget-body--iframe {
|
||||
padding : 0;
|
||||
}
|
||||
|
||||
/* Widget : placeholder (caméra / device) */
|
||||
.vc-widget-placeholder {
|
||||
height : 100%;
|
||||
display : flex;
|
||||
flex-direction : column;
|
||||
align-items : center;
|
||||
justify-content: center;
|
||||
gap : 8px;
|
||||
color : var(--text-muted);
|
||||
font-size : .82rem;
|
||||
}
|
||||
.vc-widget-placeholder svg { color: var(--bg-elevated); }
|
||||
|
||||
/* Widget : grille imbriquée */
|
||||
.vc-widget-grid {
|
||||
display : grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap : 8px;
|
||||
height : 100%;
|
||||
}
|
||||
.vc-widget-grid-cell {
|
||||
background : var(--bg-elevated);
|
||||
border-radius : var(--radius-sm);
|
||||
display : flex;
|
||||
align-items : center;
|
||||
justify-content: center;
|
||||
color : var(--text-muted);
|
||||
font-size : .8rem;
|
||||
}
|
||||
|
||||
/* --- Footer ----------------------------------------------- */
|
||||
.vc-footer {
|
||||
background : var(--bg-surface);
|
||||
border-top : 1px solid var(--border);
|
||||
padding : 12px 24px;
|
||||
font-size : .8rem;
|
||||
color : var(--text-muted);
|
||||
display : flex;
|
||||
align-items : center;
|
||||
gap : 12px;
|
||||
}
|
||||
.vc-footer a { color: var(--text-muted); }
|
||||
.vc-footer a:hover { color: var(--accent); }
|
||||
|
||||
/* --- Texte utilitaire ------------------------------------- */
|
||||
.vc-text-muted { color: var(--text-muted); font-size: .88rem; display: flex; align-items: center; gap: 6px; }
|
||||
.vc-section-title { font-size: 1rem; font-weight: 600; color: var(--text-secondary); margin: 0 0 16px; display: flex; align-items: center; gap: 8px; }
|
||||
.vc-section-title svg { color: var(--accent); }
|
||||
|
||||
/* --- Sélecteur de langue (Phase 5) -------------------------- */
|
||||
.vc-language-switcher {
|
||||
padding: 10px 12px;
|
||||
border-top: 1px solid var(--border);
|
||||
margin-top: auto;
|
||||
}
|
||||
.vc-language-switcher select {
|
||||
width: 100%;
|
||||
background: transparent;
|
||||
color: var(--text-muted);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-sm);
|
||||
padding: 4px 8px;
|
||||
font-size: .85rem;
|
||||
}
|
||||
|
||||
/* --- Stats système sidebar --------------------------------- */
|
||||
.vc-sidebar-stats {
|
||||
padding : 10px 12px 8px;
|
||||
border-bottom : 1px solid var(--border);
|
||||
display : flex;
|
||||
flex-direction : column;
|
||||
gap : 6px;
|
||||
}
|
||||
.vc-stat-row {
|
||||
display : flex;
|
||||
align-items : center;
|
||||
gap : 7px;
|
||||
color : var(--text-muted);
|
||||
cursor : default;
|
||||
}
|
||||
.vc-stat-icon {
|
||||
width : 13px;
|
||||
height : 13px;
|
||||
flex-shrink : 0;
|
||||
color : var(--text-secondary);
|
||||
}
|
||||
.vc-stat-track {
|
||||
flex : 1;
|
||||
height : 3px;
|
||||
background : var(--bg-elevated);
|
||||
border-radius : 2px;
|
||||
overflow : hidden;
|
||||
}
|
||||
.vc-stat-fill {
|
||||
height : 100%;
|
||||
width : 0%;
|
||||
border-radius : 2px;
|
||||
transition : width .6s ease;
|
||||
}
|
||||
.vc-stat-fill--cpu { background: #6366f1; }
|
||||
.vc-stat-fill--mem { background: #f97316; }
|
||||
.vc-stat-fill--disk { background: #eab308; }
|
||||
.vc-stat-fill--warn { background: #ef4444; }
|
||||
.vc-stat-pct {
|
||||
width : 30px;
|
||||
text-align : right;
|
||||
font-size : 10px;
|
||||
font-variant-numeric: tabular-nums;
|
||||
color : var(--text-muted);
|
||||
flex-shrink : 0;
|
||||
}
|
||||
|
||||
/* Compteurs top-jail/top-pays : réutilisent le mécanisme track+fill+pct
|
||||
ci-dessus (barre relative au plus grand compte de la liste, pas un
|
||||
pourcentage réel), avec un libellé texte à la place de l'icône —
|
||||
catégories nominales sans ordre naturel, donc UNE seule couleur par
|
||||
widget plutôt qu'une rotation par ligne (cf. skill dataviz : ne pas
|
||||
double-encoder la longueur de barre avec une teinte différente par
|
||||
catégorie quand rien ne justifie un ordre). */
|
||||
.vc-stat-label {
|
||||
width : 84px;
|
||||
flex-shrink : 0;
|
||||
overflow : hidden;
|
||||
text-overflow : ellipsis;
|
||||
white-space : nowrap;
|
||||
font-size : 11px;
|
||||
}
|
||||
.vc-stat-label--link { color: var(--text-secondary); text-decoration: none; }
|
||||
.vc-stat-label--link:hover { color: var(--accent); text-decoration: underline; }
|
||||
.vc-stat-fill--jail { background: var(--accent); }
|
||||
.vc-stat-fill--country { background: var(--info); }
|
||||
.vc-sidebar-stats-title {
|
||||
font-size : .68rem;
|
||||
text-transform : uppercase;
|
||||
letter-spacing : .05em;
|
||||
color : var(--text-muted);
|
||||
margin-top : 2px;
|
||||
}
|
||||
.vc-sidebar-stats-title:first-child { margin-top: 0; }
|
||||
/* Plafonne le bloc "Noeuds" à ~6 lignes avec défilement propre à lui —
|
||||
sans ça, beaucoup de noeuds (roster, Phase 3) repoussent indéfiniment
|
||||
"Top jails"/"Top pays attaquants" plus bas, jusqu'à devoir scroller
|
||||
toute la sidebar (nav comprise) pour les atteindre. */
|
||||
.vc-node-list {
|
||||
display : flex;
|
||||
flex-direction : column;
|
||||
gap : 6px;
|
||||
max-height : 150px;
|
||||
overflow-y : auto;
|
||||
}
|
||||
.vc-stat-dot {
|
||||
width : 7px;
|
||||
height : 7px;
|
||||
border-radius : 50%;
|
||||
flex-shrink : 0;
|
||||
}
|
||||
.vc-stat-dot--online { background: var(--success); }
|
||||
.vc-stat-dot--offline { background: var(--danger); }
|
||||
.vc-stat-time {
|
||||
margin-left : auto;
|
||||
font-size : 10px;
|
||||
color : var(--text-muted);
|
||||
flex-shrink : 0;
|
||||
}
|
||||
|
||||
/* --- Responsive ------------------------------------------- */
|
||||
@media (max-width: 1200px) {
|
||||
.vc-dashboard-grid {
|
||||
grid-template-columns: repeat(8, 1fr);
|
||||
}
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
.vc-sidebar {
|
||||
transform : translateX(-100%);
|
||||
transition : transform .25s ease;
|
||||
}
|
||||
.vc-sidebar--open {
|
||||
transform : translateX(0);
|
||||
}
|
||||
.vc-sidebar-toggle { display: flex; align-items: center; }
|
||||
.vc-sidebar-collapse-btn { display: none; }
|
||||
.vc-sidebar-open-btn { display: none !important; }
|
||||
.vc-main-wrap--with-sidebar { margin-left: 0; }
|
||||
.vc-grid-2col { grid-template-columns: 1fr; }
|
||||
.vc-container { padding: 16px; }
|
||||
.vc-auth-card { padding: 28px 20px; }
|
||||
.vc-page-header { flex-direction: column; align-items: flex-start; }
|
||||
.vc-dashboard-grid {
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
padding: 12px;
|
||||
gap: 8px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const selectedNode = document.body.dataset.selectedNode || '';
|
||||
const matchesFilter = (banEvent) => !selectedNode || banEvent.node === selectedNode;
|
||||
|
||||
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||
const stream = new BanEventStream(`${protocol}//${window.location.host}/ws/banevents/`);
|
||||
|
||||
const notifications = new NotificationCenter('#banevents-messages');
|
||||
stream.onEvent((message) => {
|
||||
if (message.kind === 'command') {
|
||||
notifications.show(message);
|
||||
}
|
||||
});
|
||||
|
||||
const table = new BanEventTable('#banevents-table-body', '#banevents-empty-state');
|
||||
stream.onEvent((banEvent) => {
|
||||
if (banEvent.kind === 'ban' && matchesFilter(banEvent)) {
|
||||
table.prependRow(banEvent);
|
||||
}
|
||||
});
|
||||
|
||||
const mapElement = document.getElementById('banevents-map');
|
||||
if (mapElement) {
|
||||
const map = new BanEventMap('banevents-map');
|
||||
stream.onEvent((banEvent) => {
|
||||
if (banEvent.kind === 'ban' && matchesFilter(banEvent)) {
|
||||
map.addMarker(banEvent);
|
||||
}
|
||||
});
|
||||
|
||||
const initialDataElement = document.getElementById('banevents-initial-data');
|
||||
if (initialDataElement) {
|
||||
const initialEvents = JSON.parse(initialDataElement.textContent);
|
||||
initialEvents.forEach((banEvent) => map.addMarker(banEvent));
|
||||
}
|
||||
|
||||
const resetButton = document.getElementById('banevents-map-reset');
|
||||
if (resetButton) {
|
||||
resetButton.addEventListener('click', () => map.resetView());
|
||||
}
|
||||
}
|
||||
|
||||
stream.connect();
|
||||
});
|
||||
@@ -0,0 +1,19 @@
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const mapElement = document.getElementById('banevents-map');
|
||||
if (!mapElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
const map = new BanEventMap('banevents-map');
|
||||
|
||||
const initialDataElement = document.getElementById('banevents-initial-data');
|
||||
if (initialDataElement) {
|
||||
const initialEvents = JSON.parse(initialDataElement.textContent);
|
||||
initialEvents.forEach((banEvent) => map.addMarker(banEvent));
|
||||
}
|
||||
|
||||
const resetButton = document.getElementById('banevents-map-reset');
|
||||
if (resetButton) {
|
||||
resetButton.addEventListener('click', () => map.resetView());
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,42 @@
|
||||
/* Carte Leaflet : un marqueur par BanEvent géolocalisé. Les événements
|
||||
* sans latitude/longitude (géolocalisation pas encore terminée) sont
|
||||
* ignorés jusqu'à la mise à jour poussée par la tâche Celery. */
|
||||
class BanEventMap {
|
||||
static DEFAULT_CENTER = [20, 0];
|
||||
static DEFAULT_ZOOM = 2;
|
||||
|
||||
constructor(elementId) {
|
||||
this.map = L.map(elementId).setView(BanEventMap.DEFAULT_CENTER, BanEventMap.DEFAULT_ZOOM);
|
||||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>',
|
||||
maxZoom: 18,
|
||||
}).addTo(this.map);
|
||||
this.markers = L.layerGroup().addTo(this.map);
|
||||
this.markersByEventId = new Map();
|
||||
}
|
||||
|
||||
resetView() {
|
||||
this.map.setView(BanEventMap.DEFAULT_CENTER, BanEventMap.DEFAULT_ZOOM);
|
||||
}
|
||||
|
||||
addMarker(banEvent) {
|
||||
if (banEvent.latitude == null || banEvent.longitude == null) {
|
||||
return;
|
||||
}
|
||||
this.removeMarker(banEvent.id);
|
||||
|
||||
const marker = L.marker([banEvent.latitude, banEvent.longitude]);
|
||||
const location = [banEvent.city, banEvent.country].filter(Boolean).join(', ');
|
||||
marker.bindPopup(`<strong>${banEvent.ip_address}</strong><br>${banEvent.jail_name} — ${banEvent.action}<br>${location}`);
|
||||
marker.addTo(this.markers);
|
||||
this.markersByEventId.set(banEvent.id, marker);
|
||||
}
|
||||
|
||||
removeMarker(eventId) {
|
||||
const existing = this.markersByEventId.get(eventId);
|
||||
if (existing) {
|
||||
this.markers.removeLayer(existing);
|
||||
this.markersByEventId.delete(eventId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/* Notifications transitoires (.vc-messages/.vc-alert, main.css) pour les
|
||||
* commandes reçues du master (banevents.consumers.command_notification) —
|
||||
* un SYNC_BAN réussi apparaît déjà indirectement via la ligne de table
|
||||
* qu'il déclenche ; ceci couvre les échecs et commandes non gérées, qui
|
||||
* autrement ne laissent aucune trace visible hors journalctl. */
|
||||
const ALERT_CLASSES = {
|
||||
ok: '',
|
||||
error: 'vc-alert--error',
|
||||
unhandled: 'vc-alert--warning',
|
||||
};
|
||||
const AUTO_DISMISS_MS = 8000;
|
||||
|
||||
// Chaînes traduites (Phase 5) posées par dashboard.html (#banevents-i18n,
|
||||
// json_script-style) — jamais codées en dur ici, pour rester cohérentes
|
||||
// avec la langue active côté serveur (fr/en).
|
||||
const I18N_EL = document.getElementById('banevents-i18n');
|
||||
const I18N = I18N_EL ? JSON.parse(I18N_EL.textContent) : {
|
||||
unhandled: 'Commande reçue du master (non gérée)', ok: 'appliqué', error: 'échec', close: 'Fermer',
|
||||
};
|
||||
|
||||
function commandNotificationText(notification) {
|
||||
const { cmd, ip, status, detail } = notification;
|
||||
if (status === 'unhandled') {
|
||||
return `${I18N.unhandled} : ${cmd}`;
|
||||
}
|
||||
const outcome = status === 'ok' ? I18N.ok : `${I18N.error}${detail ? ` (${detail})` : ''}`;
|
||||
return `${cmd} ${ip} → ${outcome}`;
|
||||
}
|
||||
|
||||
class NotificationCenter {
|
||||
constructor(containerSelector) {
|
||||
this.container = document.querySelector(containerSelector);
|
||||
}
|
||||
|
||||
show(notification) {
|
||||
if (!this.container) {
|
||||
return;
|
||||
}
|
||||
const alertClass = ALERT_CLASSES[notification.status] || '';
|
||||
const alert = document.createElement('div');
|
||||
alert.className = `vc-alert ${alertClass}`.trim();
|
||||
alert.innerHTML = `
|
||||
<span>${commandNotificationText(notification)}</span>
|
||||
<button type="button" aria-label="${I18N.close}">✕</button>
|
||||
`;
|
||||
alert.querySelector('button').addEventListener('click', () => alert.remove());
|
||||
this.container.appendChild(alert);
|
||||
setTimeout(() => alert.remove(), AUTO_DISMISS_MS);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/* Connexion WebSocket unique au tableau de bord, redistribuée aux écouteurs
|
||||
* (tableau, carte) via onEvent(). Reconnexion automatique sur coupure. */
|
||||
class BanEventStream {
|
||||
constructor(url) {
|
||||
this.url = url;
|
||||
this.listeners = [];
|
||||
this.socket = null;
|
||||
}
|
||||
|
||||
onEvent(callback) {
|
||||
this.listeners.push(callback);
|
||||
}
|
||||
|
||||
connect() {
|
||||
this.socket = new WebSocket(this.url);
|
||||
this.socket.addEventListener('message', (event) => {
|
||||
const banEvent = JSON.parse(event.data);
|
||||
this.listeners.forEach((callback) => callback(banEvent));
|
||||
});
|
||||
this.socket.addEventListener('close', () => this.scheduleReconnect());
|
||||
}
|
||||
|
||||
scheduleReconnect() {
|
||||
setTimeout(() => this.connect(), 3000);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/* Ajoute chaque BanEvent reçu en tête du tableau du tableau de bord. */
|
||||
const ACTION_BADGE_CLASSES = {
|
||||
BAN: 'vc-badge--danger',
|
||||
UNBAN: 'vc-badge--success',
|
||||
};
|
||||
|
||||
function actionBadgeClass(action) {
|
||||
return ACTION_BADGE_CLASSES[action] || 'vc-badge--info';
|
||||
}
|
||||
|
||||
class BanEventTable {
|
||||
constructor(tableBodySelector, emptyStateSelector) {
|
||||
this.tableBody = document.querySelector(tableBodySelector);
|
||||
this.emptyState = document.querySelector(emptyStateSelector);
|
||||
}
|
||||
|
||||
prependRow(banEvent) {
|
||||
if (this.emptyState) {
|
||||
this.emptyState.remove();
|
||||
this.emptyState = null;
|
||||
}
|
||||
if (!this.tableBody) {
|
||||
return;
|
||||
}
|
||||
const row = document.createElement('tr');
|
||||
row.innerHTML = `
|
||||
<td>${banEvent.received_at}</td>
|
||||
<td>${banEvent.node_alias}</td>
|
||||
<td>${banEvent.jail_name}</td>
|
||||
<td><span class="vc-badge ${actionBadgeClass(banEvent.action)}">${banEvent.action}</span></td>
|
||||
<td>${banEvent.ip_address}</td>
|
||||
<td>${banEvent.port || '—'}/${banEvent.protocol || '—'}</td>
|
||||
<td>${banEvent.bantime ?? '—'}</td>
|
||||
<td>${banEvent.city || banEvent.country || '—'}</td>
|
||||
`;
|
||||
this.tableBody.prepend(row);
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 696 B |
Binary file not shown.
|
After Width: | Height: | Size: 2.4 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 618 B |
@@ -0,0 +1,661 @@
|
||||
/* required styles */
|
||||
|
||||
.leaflet-pane,
|
||||
.leaflet-tile,
|
||||
.leaflet-marker-icon,
|
||||
.leaflet-marker-shadow,
|
||||
.leaflet-tile-container,
|
||||
.leaflet-pane > svg,
|
||||
.leaflet-pane > canvas,
|
||||
.leaflet-zoom-box,
|
||||
.leaflet-image-layer,
|
||||
.leaflet-layer {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
.leaflet-container {
|
||||
overflow: hidden;
|
||||
}
|
||||
.leaflet-tile,
|
||||
.leaflet-marker-icon,
|
||||
.leaflet-marker-shadow {
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
user-select: none;
|
||||
-webkit-user-drag: none;
|
||||
}
|
||||
/* Prevents IE11 from highlighting tiles in blue */
|
||||
.leaflet-tile::selection {
|
||||
background: transparent;
|
||||
}
|
||||
/* Safari renders non-retina tile on retina better with this, but Chrome is worse */
|
||||
.leaflet-safari .leaflet-tile {
|
||||
image-rendering: -webkit-optimize-contrast;
|
||||
}
|
||||
/* hack that prevents hw layers "stretching" when loading new tiles */
|
||||
.leaflet-safari .leaflet-tile-container {
|
||||
width: 1600px;
|
||||
height: 1600px;
|
||||
-webkit-transform-origin: 0 0;
|
||||
}
|
||||
.leaflet-marker-icon,
|
||||
.leaflet-marker-shadow {
|
||||
display: block;
|
||||
}
|
||||
/* .leaflet-container svg: reset svg max-width decleration shipped in Joomla! (joomla.org) 3.x */
|
||||
/* .leaflet-container img: map is broken in FF if you have max-width: 100% on tiles */
|
||||
.leaflet-container .leaflet-overlay-pane svg {
|
||||
max-width: none !important;
|
||||
max-height: none !important;
|
||||
}
|
||||
.leaflet-container .leaflet-marker-pane img,
|
||||
.leaflet-container .leaflet-shadow-pane img,
|
||||
.leaflet-container .leaflet-tile-pane img,
|
||||
.leaflet-container img.leaflet-image-layer,
|
||||
.leaflet-container .leaflet-tile {
|
||||
max-width: none !important;
|
||||
max-height: none !important;
|
||||
width: auto;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.leaflet-container img.leaflet-tile {
|
||||
/* See: https://bugs.chromium.org/p/chromium/issues/detail?id=600120 */
|
||||
mix-blend-mode: plus-lighter;
|
||||
}
|
||||
|
||||
.leaflet-container.leaflet-touch-zoom {
|
||||
-ms-touch-action: pan-x pan-y;
|
||||
touch-action: pan-x pan-y;
|
||||
}
|
||||
.leaflet-container.leaflet-touch-drag {
|
||||
-ms-touch-action: pinch-zoom;
|
||||
/* Fallback for FF which doesn't support pinch-zoom */
|
||||
touch-action: none;
|
||||
touch-action: pinch-zoom;
|
||||
}
|
||||
.leaflet-container.leaflet-touch-drag.leaflet-touch-zoom {
|
||||
-ms-touch-action: none;
|
||||
touch-action: none;
|
||||
}
|
||||
.leaflet-container {
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
.leaflet-container a {
|
||||
-webkit-tap-highlight-color: rgba(51, 181, 229, 0.4);
|
||||
}
|
||||
.leaflet-tile {
|
||||
filter: inherit;
|
||||
visibility: hidden;
|
||||
}
|
||||
.leaflet-tile-loaded {
|
||||
visibility: inherit;
|
||||
}
|
||||
.leaflet-zoom-box {
|
||||
width: 0;
|
||||
height: 0;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
z-index: 800;
|
||||
}
|
||||
/* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */
|
||||
.leaflet-overlay-pane svg {
|
||||
-moz-user-select: none;
|
||||
}
|
||||
|
||||
.leaflet-pane { z-index: 400; }
|
||||
|
||||
.leaflet-tile-pane { z-index: 200; }
|
||||
.leaflet-overlay-pane { z-index: 400; }
|
||||
.leaflet-shadow-pane { z-index: 500; }
|
||||
.leaflet-marker-pane { z-index: 600; }
|
||||
.leaflet-tooltip-pane { z-index: 650; }
|
||||
.leaflet-popup-pane { z-index: 700; }
|
||||
|
||||
.leaflet-map-pane canvas { z-index: 100; }
|
||||
.leaflet-map-pane svg { z-index: 200; }
|
||||
|
||||
.leaflet-vml-shape {
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
}
|
||||
.lvml {
|
||||
behavior: url(#default#VML);
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
|
||||
/* control positioning */
|
||||
|
||||
.leaflet-control {
|
||||
position: relative;
|
||||
z-index: 800;
|
||||
pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
|
||||
pointer-events: auto;
|
||||
}
|
||||
.leaflet-top,
|
||||
.leaflet-bottom {
|
||||
position: absolute;
|
||||
z-index: 1000;
|
||||
pointer-events: none;
|
||||
}
|
||||
.leaflet-top {
|
||||
top: 0;
|
||||
}
|
||||
.leaflet-right {
|
||||
right: 0;
|
||||
}
|
||||
.leaflet-bottom {
|
||||
bottom: 0;
|
||||
}
|
||||
.leaflet-left {
|
||||
left: 0;
|
||||
}
|
||||
.leaflet-control {
|
||||
float: left;
|
||||
clear: both;
|
||||
}
|
||||
.leaflet-right .leaflet-control {
|
||||
float: right;
|
||||
}
|
||||
.leaflet-top .leaflet-control {
|
||||
margin-top: 10px;
|
||||
}
|
||||
.leaflet-bottom .leaflet-control {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.leaflet-left .leaflet-control {
|
||||
margin-left: 10px;
|
||||
}
|
||||
.leaflet-right .leaflet-control {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
|
||||
/* zoom and fade animations */
|
||||
|
||||
.leaflet-fade-anim .leaflet-popup {
|
||||
opacity: 0;
|
||||
-webkit-transition: opacity 0.2s linear;
|
||||
-moz-transition: opacity 0.2s linear;
|
||||
transition: opacity 0.2s linear;
|
||||
}
|
||||
.leaflet-fade-anim .leaflet-map-pane .leaflet-popup {
|
||||
opacity: 1;
|
||||
}
|
||||
.leaflet-zoom-animated {
|
||||
-webkit-transform-origin: 0 0;
|
||||
-ms-transform-origin: 0 0;
|
||||
transform-origin: 0 0;
|
||||
}
|
||||
svg.leaflet-zoom-animated {
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
.leaflet-zoom-anim .leaflet-zoom-animated {
|
||||
-webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1);
|
||||
-moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1);
|
||||
transition: transform 0.25s cubic-bezier(0,0,0.25,1);
|
||||
}
|
||||
.leaflet-zoom-anim .leaflet-tile,
|
||||
.leaflet-pan-anim .leaflet-tile {
|
||||
-webkit-transition: none;
|
||||
-moz-transition: none;
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.leaflet-zoom-anim .leaflet-zoom-hide {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
|
||||
/* cursors */
|
||||
|
||||
.leaflet-interactive {
|
||||
cursor: pointer;
|
||||
}
|
||||
.leaflet-grab {
|
||||
cursor: -webkit-grab;
|
||||
cursor: -moz-grab;
|
||||
cursor: grab;
|
||||
}
|
||||
.leaflet-crosshair,
|
||||
.leaflet-crosshair .leaflet-interactive {
|
||||
cursor: crosshair;
|
||||
}
|
||||
.leaflet-popup-pane,
|
||||
.leaflet-control {
|
||||
cursor: auto;
|
||||
}
|
||||
.leaflet-dragging .leaflet-grab,
|
||||
.leaflet-dragging .leaflet-grab .leaflet-interactive,
|
||||
.leaflet-dragging .leaflet-marker-draggable {
|
||||
cursor: move;
|
||||
cursor: -webkit-grabbing;
|
||||
cursor: -moz-grabbing;
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
/* marker & overlays interactivity */
|
||||
.leaflet-marker-icon,
|
||||
.leaflet-marker-shadow,
|
||||
.leaflet-image-layer,
|
||||
.leaflet-pane > svg path,
|
||||
.leaflet-tile-container {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.leaflet-marker-icon.leaflet-interactive,
|
||||
.leaflet-image-layer.leaflet-interactive,
|
||||
.leaflet-pane > svg path.leaflet-interactive,
|
||||
svg.leaflet-image-layer.leaflet-interactive path {
|
||||
pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
/* visual tweaks */
|
||||
|
||||
.leaflet-container {
|
||||
background: #ddd;
|
||||
outline-offset: 1px;
|
||||
}
|
||||
.leaflet-container a {
|
||||
color: #0078A8;
|
||||
}
|
||||
.leaflet-zoom-box {
|
||||
border: 2px dotted #38f;
|
||||
background: rgba(255,255,255,0.5);
|
||||
}
|
||||
|
||||
|
||||
/* general typography */
|
||||
.leaflet-container {
|
||||
font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;
|
||||
font-size: 12px;
|
||||
font-size: 0.75rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
|
||||
/* general toolbar styles */
|
||||
|
||||
.leaflet-bar {
|
||||
box-shadow: 0 1px 5px rgba(0,0,0,0.65);
|
||||
border-radius: 4px;
|
||||
}
|
||||
.leaflet-bar a {
|
||||
background-color: #fff;
|
||||
border-bottom: 1px solid #ccc;
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
line-height: 26px;
|
||||
display: block;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
color: black;
|
||||
}
|
||||
.leaflet-bar a,
|
||||
.leaflet-control-layers-toggle {
|
||||
background-position: 50% 50%;
|
||||
background-repeat: no-repeat;
|
||||
display: block;
|
||||
}
|
||||
.leaflet-bar a:hover,
|
||||
.leaflet-bar a:focus {
|
||||
background-color: #f4f4f4;
|
||||
}
|
||||
.leaflet-bar a:first-child {
|
||||
border-top-left-radius: 4px;
|
||||
border-top-right-radius: 4px;
|
||||
}
|
||||
.leaflet-bar a:last-child {
|
||||
border-bottom-left-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
border-bottom: none;
|
||||
}
|
||||
.leaflet-bar a.leaflet-disabled {
|
||||
cursor: default;
|
||||
background-color: #f4f4f4;
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
.leaflet-touch .leaflet-bar a {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
}
|
||||
.leaflet-touch .leaflet-bar a:first-child {
|
||||
border-top-left-radius: 2px;
|
||||
border-top-right-radius: 2px;
|
||||
}
|
||||
.leaflet-touch .leaflet-bar a:last-child {
|
||||
border-bottom-left-radius: 2px;
|
||||
border-bottom-right-radius: 2px;
|
||||
}
|
||||
|
||||
/* zoom control */
|
||||
|
||||
.leaflet-control-zoom-in,
|
||||
.leaflet-control-zoom-out {
|
||||
font: bold 18px 'Lucida Console', Monaco, monospace;
|
||||
text-indent: 1px;
|
||||
}
|
||||
|
||||
.leaflet-touch .leaflet-control-zoom-in, .leaflet-touch .leaflet-control-zoom-out {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
|
||||
/* layers control */
|
||||
|
||||
.leaflet-control-layers {
|
||||
box-shadow: 0 1px 5px rgba(0,0,0,0.4);
|
||||
background: #fff;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.leaflet-control-layers-toggle {
|
||||
background-image: url(images/layers.png);
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
}
|
||||
.leaflet-retina .leaflet-control-layers-toggle {
|
||||
background-image: url(images/layers-2x.png);
|
||||
background-size: 26px 26px;
|
||||
}
|
||||
.leaflet-touch .leaflet-control-layers-toggle {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
}
|
||||
.leaflet-control-layers .leaflet-control-layers-list,
|
||||
.leaflet-control-layers-expanded .leaflet-control-layers-toggle {
|
||||
display: none;
|
||||
}
|
||||
.leaflet-control-layers-expanded .leaflet-control-layers-list {
|
||||
display: block;
|
||||
position: relative;
|
||||
}
|
||||
.leaflet-control-layers-expanded {
|
||||
padding: 6px 10px 6px 6px;
|
||||
color: #333;
|
||||
background: #fff;
|
||||
}
|
||||
.leaflet-control-layers-scrollbar {
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
padding-right: 5px;
|
||||
}
|
||||
.leaflet-control-layers-selector {
|
||||
margin-top: 2px;
|
||||
position: relative;
|
||||
top: 1px;
|
||||
}
|
||||
.leaflet-control-layers label {
|
||||
display: block;
|
||||
font-size: 13px;
|
||||
font-size: 1.08333em;
|
||||
}
|
||||
.leaflet-control-layers-separator {
|
||||
height: 0;
|
||||
border-top: 1px solid #ddd;
|
||||
margin: 5px -10px 5px -6px;
|
||||
}
|
||||
|
||||
/* Default icon URLs */
|
||||
.leaflet-default-icon-path { /* used only in path-guessing heuristic, see L.Icon.Default */
|
||||
background-image: url(images/marker-icon.png);
|
||||
}
|
||||
|
||||
|
||||
/* attribution and scale controls */
|
||||
|
||||
.leaflet-container .leaflet-control-attribution {
|
||||
background: #fff;
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
margin: 0;
|
||||
}
|
||||
.leaflet-control-attribution,
|
||||
.leaflet-control-scale-line {
|
||||
padding: 0 5px;
|
||||
color: #333;
|
||||
line-height: 1.4;
|
||||
}
|
||||
.leaflet-control-attribution a {
|
||||
text-decoration: none;
|
||||
}
|
||||
.leaflet-control-attribution a:hover,
|
||||
.leaflet-control-attribution a:focus {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.leaflet-attribution-flag {
|
||||
display: inline !important;
|
||||
vertical-align: baseline !important;
|
||||
width: 1em;
|
||||
height: 0.6669em;
|
||||
}
|
||||
.leaflet-left .leaflet-control-scale {
|
||||
margin-left: 5px;
|
||||
}
|
||||
.leaflet-bottom .leaflet-control-scale {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.leaflet-control-scale-line {
|
||||
border: 2px solid #777;
|
||||
border-top: none;
|
||||
line-height: 1.1;
|
||||
padding: 2px 5px 1px;
|
||||
white-space: nowrap;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
text-shadow: 1px 1px #fff;
|
||||
}
|
||||
.leaflet-control-scale-line:not(:first-child) {
|
||||
border-top: 2px solid #777;
|
||||
border-bottom: none;
|
||||
margin-top: -2px;
|
||||
}
|
||||
.leaflet-control-scale-line:not(:first-child):not(:last-child) {
|
||||
border-bottom: 2px solid #777;
|
||||
}
|
||||
|
||||
.leaflet-touch .leaflet-control-attribution,
|
||||
.leaflet-touch .leaflet-control-layers,
|
||||
.leaflet-touch .leaflet-bar {
|
||||
box-shadow: none;
|
||||
}
|
||||
.leaflet-touch .leaflet-control-layers,
|
||||
.leaflet-touch .leaflet-bar {
|
||||
border: 2px solid rgba(0,0,0,0.2);
|
||||
background-clip: padding-box;
|
||||
}
|
||||
|
||||
|
||||
/* popup */
|
||||
|
||||
.leaflet-popup {
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.leaflet-popup-content-wrapper {
|
||||
padding: 1px;
|
||||
text-align: left;
|
||||
border-radius: 12px;
|
||||
}
|
||||
.leaflet-popup-content {
|
||||
margin: 13px 24px 13px 20px;
|
||||
line-height: 1.3;
|
||||
font-size: 13px;
|
||||
font-size: 1.08333em;
|
||||
min-height: 1px;
|
||||
}
|
||||
.leaflet-popup-content p {
|
||||
margin: 17px 0;
|
||||
margin: 1.3em 0;
|
||||
}
|
||||
.leaflet-popup-tip-container {
|
||||
width: 40px;
|
||||
height: 20px;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
margin-top: -1px;
|
||||
margin-left: -20px;
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
}
|
||||
.leaflet-popup-tip {
|
||||
width: 17px;
|
||||
height: 17px;
|
||||
padding: 1px;
|
||||
|
||||
margin: -10px auto 0;
|
||||
pointer-events: auto;
|
||||
|
||||
-webkit-transform: rotate(45deg);
|
||||
-moz-transform: rotate(45deg);
|
||||
-ms-transform: rotate(45deg);
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
.leaflet-popup-content-wrapper,
|
||||
.leaflet-popup-tip {
|
||||
background: white;
|
||||
color: #333;
|
||||
box-shadow: 0 3px 14px rgba(0,0,0,0.4);
|
||||
}
|
||||
.leaflet-container a.leaflet-popup-close-button {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
border: none;
|
||||
text-align: center;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
font: 16px/24px Tahoma, Verdana, sans-serif;
|
||||
color: #757575;
|
||||
text-decoration: none;
|
||||
background: transparent;
|
||||
}
|
||||
.leaflet-container a.leaflet-popup-close-button:hover,
|
||||
.leaflet-container a.leaflet-popup-close-button:focus {
|
||||
color: #585858;
|
||||
}
|
||||
.leaflet-popup-scrolled {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.leaflet-oldie .leaflet-popup-content-wrapper {
|
||||
-ms-zoom: 1;
|
||||
}
|
||||
.leaflet-oldie .leaflet-popup-tip {
|
||||
width: 24px;
|
||||
margin: 0 auto;
|
||||
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)";
|
||||
filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678);
|
||||
}
|
||||
|
||||
.leaflet-oldie .leaflet-control-zoom,
|
||||
.leaflet-oldie .leaflet-control-layers,
|
||||
.leaflet-oldie .leaflet-popup-content-wrapper,
|
||||
.leaflet-oldie .leaflet-popup-tip {
|
||||
border: 1px solid #999;
|
||||
}
|
||||
|
||||
|
||||
/* div icon */
|
||||
|
||||
.leaflet-div-icon {
|
||||
background: #fff;
|
||||
border: 1px solid #666;
|
||||
}
|
||||
|
||||
|
||||
/* Tooltip */
|
||||
/* Base styles for the element that has a tooltip */
|
||||
.leaflet-tooltip {
|
||||
position: absolute;
|
||||
padding: 6px;
|
||||
background-color: #fff;
|
||||
border: 1px solid #fff;
|
||||
border-radius: 3px;
|
||||
color: #222;
|
||||
white-space: nowrap;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
pointer-events: none;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.4);
|
||||
}
|
||||
.leaflet-tooltip.leaflet-interactive {
|
||||
cursor: pointer;
|
||||
pointer-events: auto;
|
||||
}
|
||||
.leaflet-tooltip-top:before,
|
||||
.leaflet-tooltip-bottom:before,
|
||||
.leaflet-tooltip-left:before,
|
||||
.leaflet-tooltip-right:before {
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
border: 6px solid transparent;
|
||||
background: transparent;
|
||||
content: "";
|
||||
}
|
||||
|
||||
/* Directions */
|
||||
|
||||
.leaflet-tooltip-bottom {
|
||||
margin-top: 6px;
|
||||
}
|
||||
.leaflet-tooltip-top {
|
||||
margin-top: -6px;
|
||||
}
|
||||
.leaflet-tooltip-bottom:before,
|
||||
.leaflet-tooltip-top:before {
|
||||
left: 50%;
|
||||
margin-left: -6px;
|
||||
}
|
||||
.leaflet-tooltip-top:before {
|
||||
bottom: 0;
|
||||
margin-bottom: -12px;
|
||||
border-top-color: #fff;
|
||||
}
|
||||
.leaflet-tooltip-bottom:before {
|
||||
top: 0;
|
||||
margin-top: -12px;
|
||||
margin-left: -6px;
|
||||
border-bottom-color: #fff;
|
||||
}
|
||||
.leaflet-tooltip-left {
|
||||
margin-left: -6px;
|
||||
}
|
||||
.leaflet-tooltip-right {
|
||||
margin-left: 6px;
|
||||
}
|
||||
.leaflet-tooltip-left:before,
|
||||
.leaflet-tooltip-right:before {
|
||||
top: 50%;
|
||||
margin-top: -6px;
|
||||
}
|
||||
.leaflet-tooltip-left:before {
|
||||
right: 0;
|
||||
margin-right: -12px;
|
||||
border-left-color: #fff;
|
||||
}
|
||||
.leaflet-tooltip-right:before {
|
||||
left: 0;
|
||||
margin-left: -12px;
|
||||
border-right-color: #fff;
|
||||
}
|
||||
|
||||
/* Printing */
|
||||
|
||||
@media print {
|
||||
/* Prevent printers from removing background-images of controls. */
|
||||
.leaflet-control {
|
||||
-webkit-print-color-adjust: exact;
|
||||
print-color-adjust: exact;
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user