refactor: redesign editor buttons and improve scrollbar theming
This commit is contained in:
@@ -54,42 +54,42 @@ import { EditorHighlightService } from '../../shared/editor/editor-highlight.ser
|
||||
<!-- Save Button -->
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-solid btn-sm"
|
||||
class="editor-btn editor-btn--primary"
|
||||
[disabled]="isSaving()"
|
||||
(click)="save()"
|
||||
[attr.aria-label]="'Save (Ctrl+S)'">
|
||||
<svg *ngIf="!isSaving()" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<svg *ngIf="!isSaving()" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="editor-btn__icon">
|
||||
<path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/>
|
||||
<polyline points="17 21 17 13 7 13 7 21"/>
|
||||
<polyline points="7 3 7 8 15 8"/>
|
||||
</svg>
|
||||
<svg *ngIf="isSaving()" class="animate-spin" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<svg *ngIf="isSaving()" class="animate-spin editor-btn__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<circle cx="12" cy="12" r="10" stroke-dasharray="60" stroke-dashoffset="15"/>
|
||||
</svg>
|
||||
<span class="ml-1 hidden sm:inline">{{ isSaving() ? 'Saving...' : 'Save' }}</span>
|
||||
<span class="editor-btn__label hidden sm:inline">{{ isSaving() ? 'Saving...' : 'Save' }}</span>
|
||||
</button>
|
||||
|
||||
<!-- Wrap Toggle -->
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-outline btn-sm"
|
||||
class="editor-btn editor-btn--neutral"
|
||||
(click)="toggleWordWrap()"
|
||||
[attr.aria-label]="'Toggle word wrap'">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<svg class="editor-btn__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="4 7 4 4 20 4 20 7"/>
|
||||
<line x1="9" y1="20" x2="15" y2="20"/>
|
||||
<line x1="12" y1="4" x2="12" y2="20"/>
|
||||
</svg>
|
||||
<span class="ml-1 hidden md:inline">Wrap</span>
|
||||
<span class="editor-btn__label hidden md:inline">Wrap</span>
|
||||
</button>
|
||||
|
||||
<!-- Undo -->
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-ghost btn-sm hidden sm:flex"
|
||||
class="editor-btn editor-btn--ghost editor-btn--icon hidden sm:inline-flex"
|
||||
(click)="undo()"
|
||||
[attr.aria-label]="'Undo (Ctrl+Z)'">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<svg class="editor-btn__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="1 4 1 10 7 10"/>
|
||||
<path d="M3.51 15a9 9 0 1 0 2.13-9.36L1 10"/>
|
||||
</svg>
|
||||
@@ -98,10 +98,10 @@ import { EditorHighlightService } from '../../shared/editor/editor-highlight.ser
|
||||
<!-- Redo -->
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-ghost btn-sm hidden sm:flex"
|
||||
class="editor-btn editor-btn--ghost editor-btn--icon hidden sm:inline-flex"
|
||||
(click)="redo()"
|
||||
[attr.aria-label]="'Redo (Ctrl+Y)'">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<svg class="editor-btn__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="23 4 23 10 17 10"/>
|
||||
<path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"/>
|
||||
</svg>
|
||||
@@ -110,14 +110,14 @@ import { EditorHighlightService } from '../../shared/editor/editor-highlight.ser
|
||||
<!-- Close/Cancel -->
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-outline btn-sm"
|
||||
class="editor-btn editor-btn--neutral"
|
||||
(click)="close()"
|
||||
[attr.aria-label]="'Close editor (Esc)'">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<svg class="editor-btn__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="18" y1="6" x2="6" y2="18"/>
|
||||
<line x1="6" y1="6" x2="18" y2="18"/>
|
||||
</svg>
|
||||
<span class="ml-1 hidden sm:inline">Close</span>
|
||||
<span class="editor-btn__label hidden sm:inline">Close</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -171,7 +171,108 @@ import { EditorHighlightService } from '../../shared/editor/editor-highlight.ser
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
|
||||
.editor-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.45rem;
|
||||
padding: 0.45rem 0.85rem;
|
||||
border-radius: 9999px;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.01em;
|
||||
transition: transform 0.18s ease, box-shadow 0.18s ease, background 0.18s ease, border-color 0.18s ease, color 0.18s ease;
|
||||
border: 1px solid transparent;
|
||||
box-shadow: 0 6px 14px -12px color-mix(in oklab, var(--brand) 70%, transparent);
|
||||
}
|
||||
|
||||
.editor-btn:focus-visible {
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 3px color-mix(in oklab, var(--brand) 35%, transparent);
|
||||
}
|
||||
|
||||
.editor-btn:disabled {
|
||||
opacity: 0.7;
|
||||
cursor: not-allowed;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.editor-btn__icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.editor-btn__label {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.editor-btn--icon {
|
||||
padding: 0.4rem;
|
||||
border-radius: 9999px;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.editor-btn--primary {
|
||||
color: #0f172a;
|
||||
background: linear-gradient(135deg,
|
||||
color-mix(in oklab, var(--brand) 55%, transparent) 0%,
|
||||
color-mix(in oklab, var(--brand-700) 45%, transparent) 100%);
|
||||
border-color: color-mix(in oklab, var(--brand-700) 40%, transparent);
|
||||
}
|
||||
|
||||
.editor-btn--primary:hover:not(:disabled) {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 12px 22px -12px color-mix(in oklab, var(--brand) 80%, transparent);
|
||||
}
|
||||
|
||||
.editor-btn--neutral {
|
||||
color: inherit;
|
||||
background: linear-gradient(135deg,
|
||||
color-mix(in oklab, var(--surface-1, #e5e7eb) 65%, transparent) 0%,
|
||||
color-mix(in oklab, var(--surface-2, #d1d5db) 55%, transparent) 100%);
|
||||
border-color: color-mix(in oklab, var(--border, #cbd5e1) 55%, transparent);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.editor-btn--neutral:hover:not(:disabled) {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 8px 18px -14px color-mix(in oklab, var(--border, #cbd5e1) 65%, transparent);
|
||||
}
|
||||
|
||||
.editor-btn--ghost {
|
||||
color: var(--brand-500, var(--btn-bg));
|
||||
background: transparent;
|
||||
border-color: transparent;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.editor-btn--ghost:hover:not(:disabled) {
|
||||
background: color-mix(in srgb, var(--brand) 10%, transparent);
|
||||
}
|
||||
|
||||
:host-context(.dark) .editor-btn--primary {
|
||||
color: #e2e8f0;
|
||||
background: linear-gradient(135deg,
|
||||
color-mix(in oklab, var(--brand-700) 55%, transparent) 0%,
|
||||
color-mix(in oklab, var(--brand-900, #1e1b4b) 55%, transparent) 100%);
|
||||
border-color: color-mix(in oklab, var(--brand-700) 45%, transparent);
|
||||
}
|
||||
|
||||
:host-context(.dark) .editor-btn--neutral {
|
||||
color: #e2e8f0;
|
||||
background: linear-gradient(135deg,
|
||||
color-mix(in oklab, var(--card, #111827) 65%, transparent) 0%,
|
||||
color-mix(in oklab, var(--surface2, #0b1220) 55%, transparent) 100%);
|
||||
border-color: color-mix(in oklab, var(--border, #334155) 60%, transparent);
|
||||
}
|
||||
|
||||
:host-context(.dark) .editor-btn--ghost {
|
||||
color: color-mix(in oklab, var(--brand-200, #c7d2fe) 75%, #93c5fd 25%);
|
||||
}
|
||||
|
||||
.editor-btn--ghost.editor-btn--icon:hover:not(:disabled) {
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.markdown-editor__container {
|
||||
flex: 1;
|
||||
|
||||
@@ -30,8 +30,20 @@ import { ScrollableOverlayDirective } from '../../shared/overlay-scrollbar/scrol
|
||||
aria-modal="true"
|
||||
(click)="$event.stopPropagation()">
|
||||
|
||||
<div class="flex items-center justify-center py-3 sm:py-2">
|
||||
<div class="h-1.5 w-12 rounded-full bg-muted dark:bg-surface2"></div>
|
||||
<div class="flex items-center justify-between px-4 pt-3 pb-2 sm:pt-3 sm:pb-2">
|
||||
<div class="flex-1 flex justify-center">
|
||||
<div class="h-1.5 w-12 rounded-full bg-muted dark:bg-surface2"></div>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
class="ml-3 inline-flex items-center justify-center w-9 h-9 rounded-full bg-transparent hover:bg-surface1 dark:hover:bg-card transition"
|
||||
aria-label="Fermer le sommaire"
|
||||
(click)="close.emit()">
|
||||
<svg class="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M18 6 6 18" />
|
||||
<path d="m6 6 12 12" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="px-4 pb-4 overflow-y-auto max-h-[75vh] sm:max-h-[70vh]" appScrollableOverlay>
|
||||
|
||||
@@ -168,6 +168,7 @@ export class NoteHeaderComponent implements AfterViewInit, OnDestroy {
|
||||
const note = this.vaultService.getNoteById(this.noteId);
|
||||
const props = this.frontmatterService.get(note);
|
||||
compRef.instance.props = props;
|
||||
compRef.instance.noteId = this.noteId;
|
||||
|
||||
compRef.instance.requestClose.subscribe(() => this.scheduleClose());
|
||||
compRef.instance.cancelClose.subscribe(() => clearTimeout(this.closeTimer));
|
||||
|
||||
+17
-11
@@ -1,11 +1,12 @@
|
||||
<section
|
||||
id="note-props-popover"
|
||||
class="max-w-[420px] w-[min(92vw,420px)] p-4 text-sm leading-5 bg-popover text-popover-foreground border border-border rounded-2xl shadow-xl backdrop-blur"
|
||||
class="max-w-[420px] w-[min(92vw,420px)] p-5 text-[0.9375rem] leading-6 bg-card text-popover-foreground border border-border rounded-2xl shadow-2xl"
|
||||
(mouseenter)="cancelClose.emit()"
|
||||
(mouseleave)="requestClose.emit()"
|
||||
role="dialog"
|
||||
aria-label="Propriétés du document">
|
||||
<h3 class="font-semibold mb-3">Propriétés du document</h3>
|
||||
<h3 class="font-semibold text-xl mb-3">Propriétés du document</h3>
|
||||
<div class="border-b border-border mb-4"></div>
|
||||
|
||||
<ng-container *ngIf="props as current; else emptyState">
|
||||
<div class="space-y-4">
|
||||
@@ -13,7 +14,7 @@
|
||||
<section class="not-prose">
|
||||
<dl class="grid grid-cols-[auto_1fr] gap-x-3 gap-y-1.5">
|
||||
<ng-container *ngFor="let row of summaryRows">
|
||||
<dt class="text-muted-foreground whitespace-nowrap">{{ row.label }}</dt>
|
||||
<dt class="text-muted-foreground whitespace-nowrap font-semibold">{{ row.label }}</dt>
|
||||
<dd class="font-medium break-words">{{ row.value }}</dd>
|
||||
</ng-container>
|
||||
</dl>
|
||||
@@ -22,26 +23,31 @@
|
||||
|
||||
<ng-container *ngIf="current.tags.length">
|
||||
<section class="not-prose">
|
||||
<h4 class="text-xs uppercase tracking-wide text-muted-foreground mb-1">Tags</h4>
|
||||
<div class="flex flex-wrap gap-1">
|
||||
<span *ngFor="let tag of current.tags" class="px-2 py-0.5 text-xs border rounded-full bg-muted/40">{{ tag }}</span>
|
||||
<h4 class="text-xs uppercase tracking-wide text-muted-foreground font-semibold mb-1">Tags</h4>
|
||||
<div class="flex flex-wrap gap-1.5">
|
||||
<span *ngFor="let tag of current.tags" class="px-2.5 py-0.5 text-xs font-medium border border-border rounded-full bg-muted/70">{{ tag }}</span>
|
||||
</div>
|
||||
</section>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="current.aliases.length">
|
||||
<section class="not-prose">
|
||||
<h4 class="text-xs uppercase tracking-wide text-muted-foreground mb-1">Aliases</h4>
|
||||
<h4 class="text-xs uppercase tracking-wide text-muted-foreground font-semibold mb-1">Aliases</h4>
|
||||
<p class="text-sm leading-5 text-muted-foreground">{{ current.aliases.join(' · ') }}</p>
|
||||
</section>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="hasStates()">
|
||||
<section class="not-prose">
|
||||
<h4 class="text-xs uppercase tracking-wide text-muted-foreground mb-1">États</h4>
|
||||
<h4 class="text-xs uppercase tracking-wide text-muted-foreground font-semibold mb-1">États</h4>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<ng-container *ngFor="let st of stateEntries">
|
||||
<app-state-chip [state]="st.key" [value]="st.value"></app-state-chip>
|
||||
<button type="button"
|
||||
class="focus:outline-none"
|
||||
(click)="toggleState(st)"
|
||||
title="Basculer l'état">
|
||||
<app-state-chip [state]="st.key" [value]="st.value"></app-state-chip>
|
||||
</button>
|
||||
</ng-container>
|
||||
</div>
|
||||
</section>
|
||||
@@ -49,10 +55,10 @@
|
||||
|
||||
<ng-container *ngIf="hasAdditional()">
|
||||
<section class="not-prose">
|
||||
<h4 class="text-xs uppercase tracking-wide text-muted-foreground mb-2">Autres propriétés</h4>
|
||||
<h4 class="text-xs uppercase tracking-wide text-muted-foreground font-semibold mb-2">Autres propriétés</h4>
|
||||
<dl class="space-y-2">
|
||||
<ng-container *ngFor="let entry of additionalEntries">
|
||||
<div class="rounded-lg border border-border/60 bg-card/60 px-3 py-2">
|
||||
<div class="rounded-lg border border-border bg-card px-3 py-2">
|
||||
<dt class="text-xs font-medium text-muted-foreground uppercase tracking-wide">{{ entry.label }}</dt>
|
||||
<dd class="text-sm leading-5 mt-1" [class.whitespace-pre-wrap]="entry.multiline">
|
||||
{{ entry.displayValue }}
|
||||
|
||||
+22
-7
@@ -1,4 +1,4 @@
|
||||
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
import { Component, EventEmitter, Input, Output, inject } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { StateChipComponent } from '../state-chip/state-chip.component';
|
||||
import {
|
||||
@@ -7,6 +7,8 @@ import {
|
||||
NotePropertyStates,
|
||||
NotePropertySummary,
|
||||
} from '../../shared/note-properties.model';
|
||||
import { VaultService } from '../../../../../services/vault.service';
|
||||
import { FrontmatterPropertiesService } from '../../shared/frontmatter-properties.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-properties-popover',
|
||||
@@ -16,9 +18,13 @@ import {
|
||||
})
|
||||
export class PropertiesPopoverComponent {
|
||||
@Input() props: NoteProperties | null = null;
|
||||
@Input() noteId: string | null = null;
|
||||
@Output() requestClose = new EventEmitter<void>();
|
||||
@Output() cancelClose = new EventEmitter<void>();
|
||||
|
||||
private vault = inject(VaultService);
|
||||
private frontmatter = inject(FrontmatterPropertiesService);
|
||||
|
||||
private readonly summaryConfig: Array<{
|
||||
key: keyof NotePropertySummary;
|
||||
label: string;
|
||||
@@ -40,6 +46,8 @@ export class PropertiesPopoverComponent {
|
||||
{ key: 'archive', label: 'Archivé' },
|
||||
{ key: 'draft', label: 'Brouillon' },
|
||||
{ key: 'private', label: 'Privé' },
|
||||
{ key: 'template', label: 'Template' },
|
||||
{ key: 'task', label: 'Tâche' },
|
||||
];
|
||||
|
||||
get summaryRows(): Array<{ label: string; value: string }> {
|
||||
@@ -69,12 +77,9 @@ export class PropertiesPopoverComponent {
|
||||
return this.props?.aliases ?? [];
|
||||
}
|
||||
|
||||
get stateEntries(): Array<{ key: keyof NotePropertyStates; value: boolean | undefined }> {
|
||||
const states = this.props?.states;
|
||||
if (!states) return [];
|
||||
return this.stateOrder
|
||||
.map(({ key }) => ({ key, value: states[key] }))
|
||||
.filter(entry => entry.value !== undefined);
|
||||
get stateEntries(): Array<{ key: keyof NotePropertyStates; value: boolean }> {
|
||||
const states = this.props?.states || {};
|
||||
return this.stateOrder.map(({ key }) => ({ key, value: states[key] ?? false }));
|
||||
}
|
||||
|
||||
get additionalEntries(): NotePropertyEntry[] {
|
||||
@@ -97,4 +102,14 @@ export class PropertiesPopoverComponent {
|
||||
hasAdditional(): boolean {
|
||||
return this.additionalEntries.length > 0;
|
||||
}
|
||||
|
||||
async toggleState(entry: { key: keyof NotePropertyStates; value: boolean }): Promise<void> {
|
||||
if (!this.noteId) return;
|
||||
const next = !entry.value;
|
||||
const ok = await this.vault.updateNoteStates(this.noteId, entry.key, next);
|
||||
if (!ok) return;
|
||||
// Refresh props from updated note
|
||||
const note = this.vault.getNoteById(this.noteId);
|
||||
this.props = this.frontmatter.get(note);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<span class="inline-flex items-center gap-1 rounded-full border px-2 py-0.5 text-xs"
|
||||
[class.opacity-60]="value === false">
|
||||
[ngClass]="chipClass">
|
||||
<span class="w-4 h-4 text-current inline-flex items-center justify-center">
|
||||
<!-- Minimal Lucide-like inline SVGs to avoid extra deps -->
|
||||
<svg *ngIf="icon==='globe'" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M2 12h20"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>
|
||||
@@ -9,6 +9,8 @@
|
||||
<svg *ngIf="icon==='file'" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="4" width="18" height="16" rx="2"/></svg>
|
||||
<svg *ngIf="icon==='lock'" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>
|
||||
<svg *ngIf="icon==='lock-open'" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 9.9-1"/></svg>
|
||||
<svg *ngIf="icon==='template'" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="6" rx="2"/><rect x="3" y="11" width="18" height="10" rx="2"/></svg>
|
||||
<svg *ngIf="icon==='task'" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="6" width="16" height="12" rx="2"/><path *ngIf="value" d="M8 12l3 3 5-5"/></svg>
|
||||
</span>
|
||||
<span class="capitalize">{{ label }}</span>
|
||||
</span>
|
||||
|
||||
@@ -8,7 +8,7 @@ import { CommonModule } from '@angular/common';
|
||||
templateUrl: './state-chip.component.html'
|
||||
})
|
||||
export class StateChipComponent {
|
||||
@Input() state!: 'publish' | 'favoris' | 'archive' | 'draft' | 'private';
|
||||
@Input() state!: 'publish' | 'favoris' | 'archive' | 'draft' | 'private' | 'template' | 'task';
|
||||
@Input() value: boolean | null | undefined;
|
||||
|
||||
get label(): string {
|
||||
@@ -18,14 +18,38 @@ export class StateChipComponent {
|
||||
case 'archive': return this.value ? 'Archivé' : 'Non archivé';
|
||||
case 'draft': return 'Brouillon';
|
||||
case 'private': return 'Privé';
|
||||
case 'template': return 'Template';
|
||||
case 'task': return this.value ? 'Tâche' : 'Tâche';
|
||||
}
|
||||
}
|
||||
|
||||
get icon(): 'archive' | 'box' | 'globe' | 'heart' | 'file' | 'lock' | 'lock-open' {
|
||||
get icon(): 'archive' | 'box' | 'globe' | 'heart' | 'file' | 'lock' | 'lock-open' | 'template' | 'task' {
|
||||
if (this.state === 'archive') return this.value ? 'archive' : 'box';
|
||||
if (this.state === 'publish') return 'globe';
|
||||
if (this.state === 'favoris') return 'heart';
|
||||
if (this.state === 'draft') return 'file';
|
||||
if (this.state === 'template') return 'template';
|
||||
if (this.state === 'task') return 'task';
|
||||
return this.value ? 'lock' : 'lock-open';
|
||||
}
|
||||
|
||||
get chipClass(): string {
|
||||
if (this.value === false) return 'text-muted-foreground border-border bg-transparent';
|
||||
switch (this.state) {
|
||||
case 'publish':
|
||||
return 'text-emerald-400 border-emerald-600/40 bg-emerald-500/10';
|
||||
case 'favoris':
|
||||
return 'text-rose-400 border-rose-600/40 bg-rose-500/10';
|
||||
case 'archive':
|
||||
return 'text-amber-400 border-amber-600/40 bg-amber-500/10';
|
||||
case 'draft':
|
||||
return 'text-sky-400 border-sky-600/40 bg-sky-500/10';
|
||||
case 'private':
|
||||
return 'text-violet-400 border-violet-600/40 bg-violet-500/10';
|
||||
case 'template':
|
||||
return 'text-amber-400 border-amber-600/40 bg-amber-500/10';
|
||||
case 'task':
|
||||
return 'text-sky-400 border-sky-600/40 bg-sky-500/10';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,15 +32,20 @@ export class FrontmatterPropertiesService {
|
||||
category: this.toStr(frontmatter, consumedRawKeys, ['catégorie', 'categorie', 'category']),
|
||||
};
|
||||
|
||||
const tags = this.toArray(frontmatter, consumedRawKeys, ['tags', 'tag']) ?? [];
|
||||
// Merge tags from frontmatter and from the parsed note content to ensure completeness
|
||||
const fmTags = this.toArray(frontmatter, consumedRawKeys, ['tags', 'tag']) ?? [];
|
||||
const noteTags = Array.isArray(note.tags) ? note.tags : [];
|
||||
const tags = Array.from(new Set([...(noteTags ?? []), ...fmTags]));
|
||||
const aliases = this.toArray(frontmatter, consumedRawKeys, ['aliases', 'alias']) ?? [];
|
||||
|
||||
const states: NotePropertyStates = {};
|
||||
this.assignState(states, 'publish', frontmatter, consumedRawKeys, ['publish', 'publié']);
|
||||
this.assignState(states, 'favoris', frontmatter, consumedRawKeys, ['favoris', 'favorite', 'favourite']);
|
||||
this.assignState(states, 'archive', frontmatter, consumedRawKeys, ['archive', 'archived']);
|
||||
this.assignState(states, 'publish', frontmatter, consumedRawKeys, ['publish', 'publié', 'publie', 'published']);
|
||||
this.assignState(states, 'favoris', frontmatter, consumedRawKeys, ['favoris', 'favori', 'favorite', 'favourite', 'fav', 'star', 'starred']);
|
||||
this.assignState(states, 'archive', frontmatter, consumedRawKeys, ['archive', 'archived', 'archivé', 'archivee']);
|
||||
this.assignState(states, 'draft', frontmatter, consumedRawKeys, ['draft', 'brouillon']);
|
||||
this.assignState(states, 'private', frontmatter, consumedRawKeys, ['private', 'privé', 'prive']);
|
||||
this.assignState(states, 'private', frontmatter, consumedRawKeys, ['private', 'privé', 'prive', 'privée', 'privee']);
|
||||
this.assignState(states, 'template', frontmatter, consumedRawKeys, ['template', 'modèle', 'modele']);
|
||||
this.assignState(states, 'task', frontmatter, consumedRawKeys, ['task', 'tâche', 'tache']);
|
||||
|
||||
const additional = this.buildAdditionalEntries(frontmatter, consumedRawKeys);
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@ export interface NotePropertyStates {
|
||||
archive?: boolean;
|
||||
draft?: boolean;
|
||||
private?: boolean;
|
||||
template?: boolean;
|
||||
task?: boolean;
|
||||
}
|
||||
|
||||
export interface NotePropertyEntry {
|
||||
|
||||
@@ -79,7 +79,7 @@ import { VaultService } from '../../../services/vault.service';
|
||||
<div *ngIf="open.tags" class="px-2 py-2">
|
||||
<ul class="space-y-0.5 text-sm">
|
||||
<li *ngFor="let t of tags" class="flex items-center gap-2">
|
||||
<button (click)="tagSelected.emit(t.name)" class="flex-1 text-left px-2 py-1 rounded hover:bg-surface1 dark:hover:bg-card truncate">
|
||||
<button (click)="tagSelected.emit(t.name)" class="flex-1 text-left px-2.5 py-1.5 rounded-lg transition-colors hover:bg-slate-500/10 dark:hover:bg-surface2/15 truncate">
|
||||
<span>🏷️</span>
|
||||
<span class="ml-1">{{ t.name }}</span>
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user