- Integrated Unsplash API for image search functionality with environment configuration - Added new Nimbus Editor page component with navigation from sidebar and mobile drawer - Enhanced TOC with highlight animation for editor heading navigation - Improved CDK overlay z-index hierarchy for proper menu layering - Removed obsolete logging validation script
90 lines
3.0 KiB
TypeScript
90 lines
3.0 KiB
TypeScript
import { Component, Input, Output, EventEmitter, ViewChild, ElementRef, AfterViewInit, inject } from '@angular/core';
|
|
import { CommonModule } from '@angular/common';
|
|
import { FormsModule } from '@angular/forms';
|
|
import { Block, CodeProps } from '../../../core/models/block.model';
|
|
import { CodeThemeService } from '../../../services/code-theme.service';
|
|
|
|
@Component({
|
|
selector: 'app-code-block',
|
|
standalone: true,
|
|
imports: [CommonModule, FormsModule],
|
|
styleUrls: ['./code-themes.css'],
|
|
template: `
|
|
<div
|
|
class="rounded-xl overflow-hidden text-neutral-100 transition-colors duration-200"
|
|
[ngClass]="getThemeClass()"
|
|
>
|
|
<div class="flex items-center gap-2 px-3 py-2 bg-black/10 dark:bg-white/5">
|
|
<select
|
|
class="bg-transparent border border-transparent text-xs outline-none cursor-pointer hover:text-primary transition"
|
|
[value]="props.lang || ''"
|
|
(change)="onLangChange($event)"
|
|
>
|
|
<option value="">Plain text</option>
|
|
@for (lang of codeThemeService.getLanguages(); track lang) {
|
|
<option [value]="lang">{{ codeThemeService.getLanguageDisplay(lang) }}</option>
|
|
}
|
|
</select>
|
|
</div>
|
|
<div class="relative">
|
|
<pre
|
|
class="p-3 overflow-auto max-h-96 text-sm leading-6 m-0"
|
|
[class.whitespace-pre-wrap]="props.enableWrap"
|
|
><code
|
|
#editable
|
|
contenteditable="true"
|
|
class="focus:outline-none bg-transparent"
|
|
[class.with-line-numbers]="props.showLineNumbers"
|
|
(input)="onInput($event)"
|
|
></code></pre>
|
|
|
|
@if (props.showLineNumbers) {
|
|
<div class="absolute top-0 left-0 px-3 py-3 text-xs leading-6 text-neutral-500 pointer-events-none select-none">
|
|
@for (line of getLineNumbers(); track $index) {
|
|
<div>{{ line }}</div>
|
|
}
|
|
</div>
|
|
}
|
|
</div>
|
|
</div>
|
|
`
|
|
})
|
|
export class CodeBlockComponent implements AfterViewInit {
|
|
@Input({ required: true }) block!: Block<CodeProps>;
|
|
@Output() update = new EventEmitter<CodeProps>();
|
|
@ViewChild('editable') editable?: ElementRef<HTMLElement>;
|
|
|
|
readonly codeThemeService = inject(CodeThemeService);
|
|
|
|
get props(): CodeProps {
|
|
return this.block.props;
|
|
}
|
|
|
|
ngAfterViewInit(): void {
|
|
if (this.editable?.nativeElement) {
|
|
this.editable.nativeElement.textContent = this.props.code || '';
|
|
}
|
|
}
|
|
|
|
onInput(event: Event): void {
|
|
const target = event.target as HTMLElement;
|
|
this.update.emit({ ...this.props, code: target.textContent || '' });
|
|
}
|
|
|
|
onLangChange(event: Event): void {
|
|
const target = event.target as HTMLSelectElement;
|
|
this.update.emit({ ...this.props, lang: target.value });
|
|
}
|
|
|
|
getThemeClass(): string {
|
|
return this.codeThemeService.getThemeClass(this.props.theme);
|
|
}
|
|
|
|
getLineNumbers(): number[] {
|
|
if (!this.props.showLineNumbers) return [];
|
|
|
|
const lines = (this.props.code || '').split('\n');
|
|
return Array.from({ length: lines.length }, (_, i) => i + 1);
|
|
}
|
|
}
|