@nestjsvn/swagger-sse
Version:
OpenAPI documentation and interactive Swagger UI for NestJS Server-Sent Events endpoints
503 lines (433 loc) • 10.1 kB
CSS
/**
* Swagger UI SSE Plugin - Main Styles
* Enhanced vanilla JavaScript implementation with theme support
*/
/* CSS Variables for theming */
:root {
--sse-primary-color: #007bff;
--sse-success-color: #28a745;
--sse-danger-color: #dc3545;
--sse-warning-color: #ffc107;
--sse-info-color: #17a2b8;
--sse-light-color: #f8f9fa;
--sse-dark-color: #343a40;
--sse-border-color: #dee2e6;
--sse-text-color: #212529;
--sse-bg-color: #ffffff;
--sse-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
--sse-border-radius: 0.375rem;
--sse-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
--sse-mono-font: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
}
/* Dark theme variables */
[data-theme="dark"] {
--sse-primary-color: #0d6efd;
--sse-success-color: #198754;
--sse-danger-color: #dc3545;
--sse-warning-color: #fd7e14;
--sse-info-color: #0dcaf0;
--sse-light-color: #495057;
--sse-dark-color: #f8f9fa;
--sse-border-color: #495057;
--sse-text-color: #f8f9fa;
--sse-bg-color: #212529;
--sse-shadow: 0 0.125rem 0.25rem rgba(255, 255, 255, 0.075);
}
/* Main SSE Interface Container */
.sse-interface {
margin: 1rem 0;
padding: 1rem;
border: 1px solid var(--sse-border-color);
border-radius: var(--sse-border-radius);
background-color: var(--sse-bg-color);
color: var(--sse-text-color);
font-family: var(--sse-font-family);
box-shadow: var(--sse-shadow);
transition: all 0.3s ease;
}
/* Connection Controls */
.sse-controls {
display: flex;
align-items: center;
gap: 0.75rem;
margin-bottom: 1rem;
padding: 0.75rem;
background-color: var(--sse-light-color);
border-radius: var(--sse-border-radius);
border: 1px solid var(--sse-border-color);
}
.sse-controls button {
padding: 0.5rem 1rem;
border: none;
border-radius: var(--sse-border-radius);
font-size: 0.875rem;
font-weight: 500;
cursor: pointer;
transition: all 0.2s ease;
display: inline-flex;
align-items: center;
gap: 0.5rem;
}
.sse-connect-btn {
background-color: var(--sse-success-color);
color: white;
}
.sse-connect-btn:hover:not(:disabled) {
background-color: #218838;
transform: translateY(-1px);
}
.sse-disconnect-btn {
background-color: var(--sse-danger-color);
color: white;
}
.sse-disconnect-btn:hover:not(:disabled) {
background-color: #c82333;
transform: translateY(-1px);
}
.sse-controls button:disabled {
background-color: #6c757d;
cursor: not-allowed;
opacity: 0.6;
transform: none;
}
/* Connection Status Indicator */
.sse-status {
display: inline-flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem 0.75rem;
border-radius: 1rem;
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.05em;
position: relative;
}
.sse-status::before {
content: '';
width: 8px;
height: 8px;
border-radius: 50%;
display: inline-block;
}
.sse-status.connected {
background-color: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.sse-status.connected::before {
background-color: #28a745;
animation: pulse 2s infinite;
}
.sse-status.connecting {
background-color: #fff3cd;
color: #856404;
border: 1px solid #ffeaa7;
}
.sse-status.connecting::before {
background-color: #ffc107;
animation: blink 1s infinite;
}
.sse-status.disconnected {
background-color: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.sse-status.disconnected::before {
background-color: #dc3545;
}
.sse-status.error {
background-color: #f5c6cb;
color: #721c24;
border: 1px solid #f1b0b7;
}
.sse-status.error::before {
background-color: #dc3545;
animation: shake 0.5s infinite;
}
/* Connection Statistics */
.sse-stats {
display: flex;
gap: 1rem;
margin-left: auto;
font-size: 0.75rem;
color: var(--sse-text-color);
opacity: 0.7;
}
.sse-stat {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.25rem;
}
.sse-stat-label {
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.05em;
}
.sse-stat-value {
font-weight: 600;
color: var(--sse-primary-color);
}
/* Event Filters */
.sse-filters {
display: flex;
gap: 0.75rem;
margin-bottom: 1rem;
padding: 0.75rem;
background-color: var(--sse-light-color);
border-radius: var(--sse-border-radius);
border: 1px solid var(--sse-border-color);
flex-wrap: wrap;
}
.sse-filter-group {
display: flex;
flex-direction: column;
gap: 0.25rem;
min-width: 150px;
}
.sse-filter-label {
font-size: 0.75rem;
font-weight: 500;
color: var(--sse-text-color);
text-transform: uppercase;
letter-spacing: 0.05em;
}
.sse-filter-input,
.sse-filter-select {
padding: 0.5rem;
border: 1px solid var(--sse-border-color);
border-radius: var(--sse-border-radius);
background-color: var(--sse-bg-color);
color: var(--sse-text-color);
font-size: 0.875rem;
transition: border-color 0.2s ease;
}
.sse-filter-input:focus,
.sse-filter-select:focus {
outline: none;
border-color: var(--sse-primary-color);
box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
}
.sse-clear-filters-btn {
padding: 0.5rem 0.75rem;
background-color: var(--sse-warning-color);
color: white;
border: none;
border-radius: var(--sse-border-radius);
font-size: 0.75rem;
cursor: pointer;
align-self: flex-end;
transition: background-color 0.2s ease;
}
.sse-clear-filters-btn:hover {
background-color: #e0a800;
}
/* Event Log Container */
.sse-event-log {
border: 1px solid var(--sse-border-color);
border-radius: var(--sse-border-radius);
background-color: var(--sse-bg-color);
overflow: hidden;
}
.sse-event-log-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.75rem 1rem;
background-color: var(--sse-light-color);
border-bottom: 1px solid var(--sse-border-color);
}
.sse-event-log-title {
margin: 0;
font-size: 1rem;
font-weight: 600;
color: var(--sse-text-color);
}
.sse-event-log-controls {
display: flex;
gap: 0.5rem;
}
.sse-log-control-btn {
padding: 0.25rem 0.5rem;
border: 1px solid var(--sse-border-color);
border-radius: var(--sse-border-radius);
background-color: var(--sse-bg-color);
color: var(--sse-text-color);
font-size: 0.75rem;
cursor: pointer;
transition: all 0.2s ease;
}
.sse-log-control-btn:hover {
background-color: var(--sse-light-color);
}
.sse-auto-scroll-toggle.active {
background-color: var(--sse-primary-color);
color: white;
border-color: var(--sse-primary-color);
}
/* Virtual Scrolling Container */
.sse-events-container {
height: 400px;
overflow-y: auto;
position: relative;
}
.sse-events-viewport {
position: relative;
}
.sse-events-spacer {
height: 0;
}
/* Event Items */
.sse-event-item {
padding: 0.75rem;
border-bottom: 1px solid var(--sse-border-color);
font-family: var(--sse-mono-font);
font-size: 0.8125rem;
transition: background-color 0.2s ease;
cursor: pointer;
}
.sse-event-item:hover {
background-color: var(--sse-light-color);
}
.sse-event-item:last-child {
border-bottom: none;
}
.sse-event-item.expanded {
background-color: var(--sse-light-color);
}
.sse-event-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 0.5rem;
}
.sse-event-type {
font-weight: 600;
color: var(--sse-primary-color);
padding: 0.25rem 0.5rem;
background-color: rgba(0, 123, 255, 0.1);
border-radius: var(--sse-border-radius);
font-size: 0.75rem;
}
.sse-event-timestamp {
color: var(--sse-text-color);
opacity: 0.7;
font-size: 0.75rem;
}
.sse-event-data {
background-color: var(--sse-light-color);
padding: 0.75rem;
border-radius: var(--sse-border-radius);
white-space: pre-wrap;
word-break: break-word;
max-height: 100px;
overflow: hidden;
transition: max-height 0.3s ease;
}
.sse-event-item.expanded .sse-event-data {
max-height: none;
}
.sse-event-expand-btn {
background: none;
border: none;
color: var(--sse-primary-color);
cursor: pointer;
font-size: 0.75rem;
padding: 0.25rem;
margin-top: 0.5rem;
}
/* Loading States */
.sse-loading {
display: flex;
align-items: center;
justify-content: center;
padding: 2rem;
color: var(--sse-text-color);
opacity: 0.7;
}
.sse-spinner {
width: 20px;
height: 20px;
border: 2px solid var(--sse-border-color);
border-top: 2px solid var(--sse-primary-color);
border-radius: 50%;
animation: spin 1s linear infinite;
margin-right: 0.5rem;
}
/* Animations */
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
@keyframes blink {
0%, 50% { opacity: 1; }
51%, 100% { opacity: 0.3; }
}
@keyframes shake {
0%, 100% { transform: translateX(0); }
25% { transform: translateX(-2px); }
75% { transform: translateX(2px); }
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* Responsive Design */
@media (max-width: 768px) {
.sse-interface {
margin: 0.5rem 0;
padding: 0.75rem;
}
.sse-controls {
flex-direction: column;
gap: 0.5rem;
}
.sse-controls button {
width: 100%;
justify-content: center;
}
.sse-filters {
flex-direction: column;
}
.sse-filter-group {
min-width: auto;
}
.sse-event-log-header {
flex-direction: column;
gap: 0.5rem;
align-items: flex-start;
}
.sse-events-container {
height: 300px;
}
.sse-event-header {
flex-direction: column;
align-items: flex-start;
gap: 0.25rem;
}
.sse-stats {
flex-direction: column;
gap: 0.5rem;
}
}
/* High contrast mode support */
@media (prefers-contrast: high) {
.sse-interface {
border-width: 2px;
}
.sse-controls button {
border: 2px solid currentColor;
}
.sse-status {
border-width: 2px;
}
}
/* Reduced motion support */
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms ;
animation-iteration-count: 1 ;
transition-duration: 0.01ms ;
}
}