Fix note color swatches outside teleported editor panel.
Use hex palettes so Keep-style colors render when the create panel is portaled to body. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -67,6 +67,7 @@ const draftActivityId = ref('');
|
||||
const draftReminder = ref('');
|
||||
const draftLocation = ref('');
|
||||
const draftAnalyze = ref(true);
|
||||
const draftPinned = ref(false);
|
||||
const draftChecklist = ref<NoteChecklistItemDto[]>([]);
|
||||
const draftFiles = ref<File[]>([]);
|
||||
const existingAttachments = ref<NoteAttachmentDto[]>([]);
|
||||
@@ -130,17 +131,18 @@ const draftColorCss = computed(() => colorToCss(draftColor.value));
|
||||
const personName = (id: string) =>
|
||||
people.value.find((p) => p.id === id)?.fullName ?? '';
|
||||
|
||||
/** Hex only — panel is Teleported outside `.notes-page`, so CSS vars would not inherit. */
|
||||
const NOTE_COLOR_HEX: Record<ColorKey, string> = {
|
||||
sand: '#fff8e1',
|
||||
rose: '#fce4ec',
|
||||
sky: '#e3f2fd',
|
||||
mint: '#e0f2f1',
|
||||
lavender: '#f3e5f5',
|
||||
default: '#ffffff',
|
||||
};
|
||||
|
||||
function colorToCss(raw: string | null | undefined): string {
|
||||
const key = normalizeColor(raw);
|
||||
const map: Record<ColorKey, string> = {
|
||||
sand: 'var(--note-sand)',
|
||||
rose: 'var(--note-rose)',
|
||||
sky: 'var(--note-sky)',
|
||||
mint: 'var(--note-mint)',
|
||||
lavender: 'var(--note-lavender)',
|
||||
default: 'var(--note-default)',
|
||||
};
|
||||
return map[key];
|
||||
return NOTE_COLOR_HEX[normalizeColor(raw)];
|
||||
}
|
||||
|
||||
function normalizeColor(raw: string | null | undefined): ColorKey {
|
||||
@@ -359,6 +361,7 @@ function resetDraft() {
|
||||
draftReminder.value = '';
|
||||
draftLocation.value = '';
|
||||
draftAnalyze.value = true;
|
||||
draftPinned.value = false;
|
||||
draftChecklist.value = [];
|
||||
draftFiles.value = [];
|
||||
existingAttachments.value = [];
|
||||
@@ -381,6 +384,7 @@ function openEditor(mode: OpenMode = 'default', note?: NoteDto) {
|
||||
draftReminder.value = note.reminderAt ? note.reminderAt.slice(0, 16) : '';
|
||||
draftLocation.value = note.locationText ?? '';
|
||||
draftAnalyze.value = note.analyzeEnabled !== false;
|
||||
draftPinned.value = note.pinned;
|
||||
draftChecklist.value = [...(note.checklist ?? [])];
|
||||
draftFiles.value = [];
|
||||
existingAttachments.value = [...note.attachments];
|
||||
@@ -496,6 +500,7 @@ async function saveNote() {
|
||||
: null,
|
||||
locationText: draftLocation.value.trim() || null,
|
||||
analyzeEnabled: draftAnalyze.value,
|
||||
pinned: draftPinned.value,
|
||||
checklist,
|
||||
};
|
||||
const pending = [...draftFiles.value];
|
||||
@@ -534,6 +539,24 @@ async function togglePin(id: string) {
|
||||
menuOpenId.value = null;
|
||||
await api.patch(`/notes/${id}/pin`);
|
||||
await load();
|
||||
if (editingId.value === id) {
|
||||
const n = notes.value.find((x) => x.id === id);
|
||||
if (n) draftPinned.value = n.pinned;
|
||||
}
|
||||
}
|
||||
|
||||
async function onPinClick() {
|
||||
const next = !draftPinned.value;
|
||||
draftPinned.value = next;
|
||||
if (!editingId.value) return;
|
||||
const note = notes.value.find((n) => n.id === editingId.value);
|
||||
if (note && note.pinned === next) return;
|
||||
try {
|
||||
await api.patch(`/notes/${editingId.value}/pin`);
|
||||
if (note) note.pinned = next;
|
||||
} catch {
|
||||
draftPinned.value = !next;
|
||||
}
|
||||
}
|
||||
|
||||
async function removeNote(id: string) {
|
||||
@@ -864,7 +887,13 @@ onBeforeUnmount(() => {
|
||||
class="panel-root desktop-panel"
|
||||
>
|
||||
<div class="panel-backdrop" @click="closeEditor" />
|
||||
<aside class="editor-panel" :style="{ '--canvas': draftColorCss }">
|
||||
<aside
|
||||
class="editor-panel"
|
||||
:style="{
|
||||
'--canvas': draftColorCss,
|
||||
background: draftColorCss,
|
||||
}"
|
||||
>
|
||||
<header class="panel-head">
|
||||
<h2>{{ editorTitle }}</h2>
|
||||
<button type="button" class="icon-btn" @click="closeEditor">
|
||||
@@ -887,18 +916,38 @@ onBeforeUnmount(() => {
|
||||
placeholder="یادداشت خود را بنویسید…"
|
||||
/>
|
||||
|
||||
<div class="swatches">
|
||||
<div class="palette-row">
|
||||
<div class="swatches" role="listbox" aria-label="رنگ یادداشت">
|
||||
<button
|
||||
v-for="sw in colorSwatches"
|
||||
:key="sw.value"
|
||||
type="button"
|
||||
class="swatch"
|
||||
:class="[
|
||||
`tone-${sw.value}`,
|
||||
{ selected: draftColor === sw.value },
|
||||
]"
|
||||
:style="{ backgroundColor: NOTE_COLOR_HEX[sw.value] }"
|
||||
:title="sw.label"
|
||||
:aria-label="sw.label"
|
||||
:aria-selected="draftColor === sw.value"
|
||||
@click="draftColor = sw.value"
|
||||
>
|
||||
<UiIcon
|
||||
v-if="draftColor === sw.value"
|
||||
name="check-plain"
|
||||
:size="14"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
v-for="sw in colorSwatches"
|
||||
:key="sw.value"
|
||||
type="button"
|
||||
class="swatch"
|
||||
:class="{ selected: draftColor === sw.value }"
|
||||
:style="{ background: colorToCss(sw.value) }"
|
||||
:title="sw.label"
|
||||
@click="draftColor = sw.value"
|
||||
class="media-btn pin-btn"
|
||||
:class="{ on: draftPinned }"
|
||||
title="سنجاق"
|
||||
@click="onPinClick"
|
||||
>
|
||||
<UiIcon v-if="draftColor === sw.value" name="check-plain" :size="14" />
|
||||
سنجاق
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -1059,7 +1108,7 @@ onBeforeUnmount(() => {
|
||||
<div
|
||||
v-if="editorOpen && mobileEditor"
|
||||
class="mobile-editor"
|
||||
:style="{ '--canvas': draftColorCss }"
|
||||
:style="{ '--canvas': draftColorCss, background: draftColorCss }"
|
||||
>
|
||||
<header class="mob-head">
|
||||
<button type="button" class="icon-btn" @click="closeEditor">
|
||||
@@ -1089,17 +1138,34 @@ onBeforeUnmount(() => {
|
||||
placeholder="یادداشت خود را بنویسید…"
|
||||
/>
|
||||
|
||||
<div class="swatches">
|
||||
<div class="palette-row">
|
||||
<div class="swatches" role="listbox" aria-label="رنگ یادداشت">
|
||||
<button
|
||||
v-for="sw in colorSwatches"
|
||||
:key="`m-${sw.value}`"
|
||||
type="button"
|
||||
class="swatch"
|
||||
:class="[`tone-${sw.value}`, { selected: draftColor === sw.value }]"
|
||||
:style="{ backgroundColor: NOTE_COLOR_HEX[sw.value] }"
|
||||
:title="sw.label"
|
||||
:aria-label="sw.label"
|
||||
:aria-selected="draftColor === sw.value"
|
||||
@click="draftColor = sw.value"
|
||||
>
|
||||
<UiIcon
|
||||
v-if="draftColor === sw.value"
|
||||
name="check-plain"
|
||||
:size="14"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
v-for="sw in colorSwatches"
|
||||
:key="`m-${sw.value}`"
|
||||
type="button"
|
||||
class="swatch"
|
||||
:class="{ selected: draftColor === sw.value }"
|
||||
:style="{ background: colorToCss(sw.value) }"
|
||||
@click="draftColor = sw.value"
|
||||
class="media-btn pin-btn"
|
||||
:class="{ on: draftPinned }"
|
||||
@click="onPinClick"
|
||||
>
|
||||
<UiIcon v-if="draftColor === sw.value" name="check-plain" :size="14" />
|
||||
سنجاق
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -1825,23 +1891,60 @@ onBeforeUnmount(() => {
|
||||
|
||||
.swatches {
|
||||
display: flex;
|
||||
gap: 0.45rem;
|
||||
gap: 0.5rem;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
}
|
||||
.palette-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 0.65rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.swatch {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
border: 1px solid rgba(15, 23, 42, 0.12);
|
||||
border: 1.5px solid rgba(15, 23, 42, 0.18);
|
||||
cursor: pointer;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--teal);
|
||||
color: #0f766e;
|
||||
padding: 0;
|
||||
flex-shrink: 0;
|
||||
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.08);
|
||||
}
|
||||
.swatch.tone-default {
|
||||
border-color: #94a3b8;
|
||||
background-image: linear-gradient(
|
||||
45deg,
|
||||
#f1f5f9 25%,
|
||||
transparent 25%,
|
||||
transparent 75%,
|
||||
#f1f5f9 75%
|
||||
);
|
||||
background-size: 8px 8px;
|
||||
}
|
||||
.swatch.selected {
|
||||
box-shadow: 0 0 0 2px var(--teal);
|
||||
box-shadow: 0 0 0 2px #fff, 0 0 0 4px var(--teal, #0d9488);
|
||||
}
|
||||
.pin-btn.on {
|
||||
border-color: #f59e0b;
|
||||
color: #b45309;
|
||||
background: #fffbeb;
|
||||
}
|
||||
|
||||
.panel-root {
|
||||
--note-sand: #fff8e1;
|
||||
--note-rose: #fce4ec;
|
||||
--note-sky: #e3f2fd;
|
||||
--note-mint: #e0f2f1;
|
||||
--note-lavender: #f3e5f5;
|
||||
--note-default: #ffffff;
|
||||
--teal: #0d9488;
|
||||
--teal-soft: #ccfbf1;
|
||||
}
|
||||
|
||||
.media-row {
|
||||
|
||||
Reference in New Issue
Block a user