2024-02-05 10:07:49 +01:00
import {
DeesElement ,
css ,
cssManager ,
customElement ,
html ,
property ,
type TemplateResult ,
} from '@design.estate/dees-element' ;
import * as domtools from '@design.estate/dees-domtools' ;
import { demoFunc } from './dees-chart-log.demo.js' ;
declare global {
interface HTMLElementTagNameMap {
'dees-chart-log' : DeesChartLog ;
}
}
2025-06-12 10:44:21 +00:00
export interface ILogEntry {
timestamp : string ;
level : 'debug' | 'info' | 'warn' | 'error' | 'success' ;
message : string ;
source? : string ;
}
2024-02-05 10:07:49 +01:00
@customElement ( 'dees-chart-log' )
export class DeesChartLog extends DeesElement {
public static demo = demoFunc ;
@property ( )
2025-06-12 10:44:21 +00:00
public label : string = 'Server Logs' ;
@property ( { type : Array } )
public logEntries : ILogEntry [ ] = [ ] ;
@property ( { type : Boolean } )
public autoScroll : boolean = true ;
@property ( { type : Number } )
public maxEntries : number = 1000 ;
private logContainer : HTMLDivElement ;
2024-02-05 10:07:49 +01:00
constructor ( ) {
super ( ) ;
domtools . elementBasic . setup ( ) ;
2025-06-12 10:44:21 +00:00
2024-02-05 10:07:49 +01:00
}
public static styles = [
cssManager . defaultStyles ,
css `
: host {
2025-06-12 10:44:21 +00:00
font - family : 'Geist Mono' , 'Consolas' , 'Monaco' , monospace ;
2024-02-05 10:07:49 +01:00
color : # ccc ;
font - size : 12px ;
2025-06-12 10:44:21 +00:00
line - height : 1.4 ;
2024-02-05 10:07:49 +01:00
}
. mainbox {
position : relative ;
width : 100 % ;
height : 400px ;
2025-06-12 10:44:21 +00:00
background : $ { cssManager . bdTheme ( '#f8f9fa' , '#0a0a0a' ) } ;
border : 1px solid $ { cssManager . bdTheme ( '#dee2e6' , '#333' ) } ;
2024-02-05 10:07:49 +01:00
border - radius : 8px ;
2025-06-12 10:44:21 +00:00
display : flex ;
flex - direction : column ;
overflow : hidden ;
2024-02-05 10:07:49 +01:00
}
2025-06-12 10:44:21 +00:00
. header {
background : $ { cssManager . bdTheme ( '#e9ecef' , '#1a1a1a' ) } ;
padding : 8px 16 px ;
border - bottom : 1px solid $ { cssManager . bdTheme ( '#dee2e6' , '#333' ) } ;
display : flex ;
justify - content : space - between ;
align - items : center ;
flex - shrink : 0 ;
2024-02-05 10:07:49 +01:00
}
2025-06-12 10:44:21 +00:00
. title {
font - weight : 600 ;
color : $ { cssManager . bdTheme ( '#212529' , '#fff' ) } ;
}
. controls {
display : flex ;
gap : 8px ;
}
. control - button {
background : $ { cssManager . bdTheme ( '#e9ecef' , '#2a2a2a' ) } ;
border : 1px solid $ { cssManager . bdTheme ( '#ced4da' , '#444' ) } ;
border - radius : 4px ;
padding : 4px 8 px ;
color : $ { cssManager . bdTheme ( '#495057' , '#ccc' ) } ;
cursor : pointer ;
font - size : 11px ;
transition : all 0.2 s ;
}
. control - button :hover {
background : $ { cssManager . bdTheme ( '#dee2e6' , '#3a3a3a' ) } ;
border - color : $ { cssManager . bdTheme ( '#adb5bd' , '#555' ) } ;
}
. control - button . active {
background : $ { cssManager . bdTheme ( '#007bff' , '#4a4a4a' ) } ;
color : $ { cssManager . bdTheme ( '#fff' , '#fff' ) } ;
}
. logContainer {
flex : 1 ;
overflow - y : auto ;
overflow - x : hidden ;
padding : 8px 16 px ;
font - size : 12px ;
}
. logEntry {
margin - bottom : 2px ;
display : flex ;
white - space : pre - wrap ;
word - break : break - all ;
}
. timestamp {
color : $ { cssManager . bdTheme ( '#6c757d' , '#666' ) } ;
margin - right : 8px ;
flex - shrink : 0 ;
}
. level {
margin - right : 8px ;
padding : 0 6 px ;
border - radius : 3px ;
font - weight : 600 ;
text - transform : uppercase ;
font - size : 10px ;
flex - shrink : 0 ;
}
. level . debug {
color : $ { cssManager . bdTheme ( '#6c757d' , '#999' ) } ;
background : $ { cssManager . bdTheme ( 'rgba(108, 117, 125, 0.1)' , '#333' ) } ;
}
. level . info {
color : $ { cssManager . bdTheme ( '#0066cc' , '#4a9eff' ) } ;
background : $ { cssManager . bdTheme ( 'rgba(0, 102, 204, 0.1)' , 'rgba(74, 158, 255, 0.1)' ) } ;
}
. level . warn {
color : $ { cssManager . bdTheme ( '#ff8800' , '#ffb84a' ) } ;
background : $ { cssManager . bdTheme ( 'rgba(255, 136, 0, 0.1)' , 'rgba(255, 184, 74, 0.1)' ) } ;
}
. level . error {
color : $ { cssManager . bdTheme ( '#dc3545' , '#ff4a4a' ) } ;
background : $ { cssManager . bdTheme ( 'rgba(220, 53, 69, 0.1)' , 'rgba(255, 74, 74, 0.1)' ) } ;
}
. level . success {
color : $ { cssManager . bdTheme ( '#28a745' , '#4aff88' ) } ;
background : $ { cssManager . bdTheme ( 'rgba(40, 167, 69, 0.1)' , 'rgba(74, 255, 136, 0.1)' ) } ;
}
. source {
color : $ { cssManager . bdTheme ( '#6c757d' , '#888' ) } ;
margin - right : 8px ;
flex - shrink : 0 ;
}
. message {
color : $ { cssManager . bdTheme ( '#212529' , '#ddd' ) } ;
flex : 1 ;
}
. empty - state {
display : flex ;
align - items : center ;
justify - content : center ;
2024-02-05 10:07:49 +01:00
height : 100 % ;
2025-06-12 10:44:21 +00:00
color : $ { cssManager . bdTheme ( '#6c757d' , '#666' ) } ;
font - style : italic ;
}
/* Custom scrollbar */
. logContainer : : - webkit - scrollbar {
width : 8px ;
}
. logContainer : : - webkit - scrollbar - track {
background : $ { cssManager . bdTheme ( '#e9ecef' , '#1a1a1a' ) } ;
}
. logContainer : : - webkit - scrollbar - thumb {
background : $ { cssManager . bdTheme ( '#adb5bd' , '#444' ) } ;
border - radius : 4px ;
}
. logContainer : : - webkit - scrollbar - thumb :hover {
background : $ { cssManager . bdTheme ( '#6c757d' , '#555' ) } ;
2024-02-05 10:07:49 +01:00
}
` ,
] ;
public render ( ) : TemplateResult {
2025-06-12 10:44:21 +00:00
return html `
< div class = "mainbox" >
< div class = "header" >
< div class = "title" > $ { this . label } < / div >
< div class = "controls" >
< button
class = "control-button ${this.autoScroll ? 'active' : ''}"
@click = $ { ( ) = > { this . autoScroll = ! this . autoScroll ; } }
>
Auto Scroll
< / button >
< button
class = "control-button"
@click = $ { ( ) = > { this . clearLogs ( ) ; } }
>
Clear
< / button >
< / div >
< / div >
< div class = "logContainer" >
$ { this . logEntries . length === 0
? html ` <div class="empty-state">No logs to display</div> `
: this . logEntries . map ( entry = > this . renderLogEntry ( entry ) )
}
< / div >
< / div >
` ;
}
private renderLogEntry ( entry : ILogEntry ) : TemplateResult {
const timestamp = new Date ( entry . timestamp ) . toLocaleTimeString ( 'en-US' , {
hour12 : false ,
hour : '2-digit' ,
minute : '2-digit' ,
second : '2-digit' ,
fractionalSecondDigits : 3
} ) ;
return html `
< div class = "logEntry" >
< span class = "timestamp" > $ { timestamp } < / span >
< span class = "level ${entry.level}" > $ { entry . level } < / span >
$ { entry . source ? html ` <span class="source">[ ${ entry . source } ]</span> ` : '' }
< span class = "message" > $ { entry . message } < / span >
< / div >
` ;
2024-02-05 10:07:49 +01:00
}
public async firstUpdated() {
2025-06-12 11:00:33 +00:00
await this . domtoolsPromise ;
2025-06-12 10:44:21 +00:00
this . logContainer = this . shadowRoot . querySelector ( '.logContainer' ) ;
// Initialize with demo server logs
const demoLogs : ILogEntry [ ] = [
{ timestamp : new Date ( ) . toISOString ( ) , level : 'info' , message : 'Server started on port 3000' , source : 'Server' } ,
{ timestamp : new Date ( ) . toISOString ( ) , level : 'debug' , message : 'Loading configuration from /etc/app/config.json' , source : 'Config' } ,
{ timestamp : new Date ( ) . toISOString ( ) , level : 'info' , message : 'Connected to MongoDB at mongodb://localhost:27017' , source : 'Database' } ,
{ timestamp : new Date ( ) . toISOString ( ) , level : 'success' , message : 'Database connection established successfully' , source : 'Database' } ,
{ timestamp : new Date ( ) . toISOString ( ) , level : 'warn' , message : 'No SSL certificate found, using self-signed certificate' , source : 'Security' } ,
{ timestamp : new Date ( ) . toISOString ( ) , level : 'info' , message : 'API routes initialized: GET /api/users, POST /api/users, DELETE /api/users/:id' , source : 'Router' } ,
{ timestamp : new Date ( ) . toISOString ( ) , level : 'debug' , message : 'Middleware stack: cors, bodyParser, authentication, errorHandler' , source : 'Middleware' } ,
{ timestamp : new Date ( ) . toISOString ( ) , level : 'info' , message : 'WebSocket server listening on ws://localhost:3001' , source : 'WebSocket' } ,
] ;
this . logEntries = demoLogs ;
this . scrollToBottom ( ) ;
2024-02-05 10:07:49 +01:00
2025-06-12 10:44:21 +00:00
// For demo purposes, store reference globally
if ( ( window as any ) . __demoLogElement === undefined ) {
( window as any ) . __demoLogElement = this ;
}
2024-02-05 10:07:49 +01:00
}
2025-06-12 10:44:21 +00:00
public async updateLog ( entries? : ILogEntry [ ] ) {
if ( entries ) {
// Add new entries
this . logEntries = [ . . . this . logEntries , . . . entries ] ;
// Trim if exceeds max entries
if ( this . logEntries . length > this . maxEntries ) {
this . logEntries = this . logEntries . slice ( - this . maxEntries ) ;
}
// Trigger re-render
this . requestUpdate ( ) ;
// Auto-scroll if enabled
await this . updateComplete ;
if ( this . autoScroll ) {
this . scrollToBottom ( ) ;
}
}
}
public clearLogs() {
this . logEntries = [ ] ;
this . requestUpdate ( ) ;
}
private scrollToBottom() {
if ( this . logContainer ) {
this . logContainer . scrollTop = this . logContainer . scrollHeight ;
}
}
public addLog ( level : ILogEntry [ 'level' ] , message : string , source? : string ) {
const newEntry : ILogEntry = {
timestamp : new Date ( ) . toISOString ( ) ,
level ,
message ,
source
} ;
this . updateLog ( [ newEntry ] ) ;
2024-02-05 10:07:49 +01:00
}
}