ui optimizations

This commit is contained in:
beo3000 2026-02-22 16:13:25 +01:00
parent afb330ed5d
commit 6116069fff
8 changed files with 43 additions and 19 deletions

View File

@ -1 +1 @@
1.0.41
1.0.50

View File

@ -39,7 +39,7 @@ export async function fetchMentionItems(query: string): Promise<{ items: Mention
.filter(c => !c.deletedAt && !c.archivedAt && (c.type === 'person' || c.type === 'project' || c.type === 'company'))
.toArray();
const q = query.toLowerCase();
const q = query.replace(/_/g, ' ').toLowerCase();
const items = all
.map(buildMentionItem)
.filter(m => m.mentionName.toLowerCase().includes(q))
@ -48,10 +48,11 @@ export async function fetchMentionItems(query: string): Promise<{ items: Mention
const exactMatch = items.some(m => m.mentionName.toLowerCase() === q);
const createOptions: CreateOption[] = [];
if (query.length > 0 && !exactMatch) {
const displayName = query.replace(/_/g, ' ');
createOptions.push(
{ type: 'person', label: `+ Person "${query}" anlegen`, query },
{ type: 'project', label: `+ Projekt "${query}" anlegen`, query },
{ type: 'company', label: `+ Firma "${query}" anlegen`, query }
{ type: 'person', label: `+ Person "${displayName}" anlegen`, query },
{ type: 'project', label: `+ Projekt "${displayName}" anlegen`, query },
{ type: 'company', label: `+ Firma "${displayName}" anlegen`, query }
);
}
@ -59,7 +60,7 @@ export async function fetchMentionItems(query: string): Promise<{ items: Mention
}
export async function createMentionContext(opt: CreateOption): Promise<string> {
const name = opt.query;
const name = opt.query.replace(/_/g, ' ');
const slug = name.toLowerCase().replace(/\s+/g, '-');
const id = opt.type === 'company' ? `f-${slug}` : opt.type === 'person' ? `u-${slug}` : `p-${slug}`;
const contextName = opt.type === 'company' ? `Firma ${name}` : opt.type === 'person' ? `Person ${name}` : `Project ${name}`;

View File

@ -158,7 +158,8 @@ function showCreatePopup(name: string, type: 'person' | 'project' | 'company', x
async function handlePersonClick(name: string, event: MouseEvent, sourceEl: HTMLElement) {
const ctx = await findContextByMentionName(name, 'person');
if (ctx) {
const ratingCtx = findRatingContext(sourceEl);
const isEmployee = (ctx.meta as Record<string, unknown> | null)?.personSubType === 'employee';
const ratingCtx = isEmployee ? findRatingContext(sourceEl) : null;
showPersonPopup(name, ctx.id, ratingCtx, event.clientX, event.clientY, sourceEl);
} else {
showCreatePopup(name, 'person', event.clientX, event.clientY);

View File

@ -63,6 +63,7 @@
{placeholder}
{minHeight}
onchange={(md) => editorContent = md}
onsave={save}
/>
{:else}
<!-- svelte-ignore a11y_click_events_have_key_events a11y_no_static_element_interactions -->

View File

@ -43,6 +43,7 @@
<MarkdownEditor
content={editText}
onchange={(md) => editText = md}
onsave={saveEdit}
minHeight="100px"
/>
<div class="flex justify-end gap-1 mt-1">

View File

@ -14,8 +14,10 @@
placeholder?: string;
minHeight?: string;
onchange?: (markdown: string) => void;
onsave?: () => void;
onsavearchive?: () => void;
}
let { content = '', placeholder = '', minHeight = '80px', onchange }: Props = $props();
let { content = '', placeholder = '', minHeight = '80px', onchange, onsave, onsavearchive }: Props = $props();
let element: HTMLDivElement;
let editor: Editor | null = null;
@ -42,7 +44,7 @@
onchange?.(md);
}
async function handlePaste(_view: any, event: ClipboardEvent): Promise<boolean> {
function handlePaste(_view: any, event: ClipboardEvent): boolean {
const items = event.clipboardData?.items;
if (!items) return false;
@ -50,12 +52,14 @@
if (item.type.startsWith('image/')) {
event.preventDefault();
const blob = item.getAsFile();
if (!blob) continue;
const id = await storeImage(blob);
const url = await getImageUrl(id);
if (url && editor) {
editor.chain().focus().setImage({ src: url }).run();
if (blob) {
storeImage(blob)
.then((id) => getImageUrl(id))
.then((url) => {
if (url && editor) {
editor.chain().focus().setImage({ src: url }).run();
}
});
}
return true;
}
@ -82,6 +86,17 @@
content: initialContent,
editorProps: {
handlePaste,
handleKeyDown: (_view, event) => {
if (event.key === 'Enter' && (event.ctrlKey || event.metaKey) && event.shiftKey) {
onsavearchive?.();
return true;
}
if (event.key === 'Enter' && (event.ctrlKey || event.metaKey)) {
onsave?.();
return true;
}
return false;
},
attributes: {
style: `min-height: ${minHeight}`
}

View File

@ -187,9 +187,11 @@
>
{/if}
{#if isProcessed}
<span
class="ml-2.5 rounded bg-[#444] px-1.5 py-0.5 text-[0.7em] text-[#aaa]"
>BESPROCHEN</span
<button
class="ml-2.5 rounded bg-[#444] px-1.5 py-0.5 text-[0.7em] text-[#aaa] hover:bg-[#555] hover:text-white"
title="Als aktiv markieren"
onclick={(e) => { e.stopPropagation(); processedTopicIds.remove(topic.id); }}
>BESPROCHEN</button
>
{/if}
</div>
@ -212,6 +214,8 @@
bind:this={noteEditor}
placeholder="- Notiz... (z.B. '-> NAME' or '@P:PROJECT')"
onchange={(md) => (noteText = md)}
onsave={handleSaveOnly}
onsavearchive={handleDone}
/>
{#if showDailyControls}

View File

@ -93,7 +93,8 @@ export const TiptapMention = Extension.create({
const opt = createOptions[index - items.length];
if (!opt) return;
await createMentionContext(opt);
const tag = opt.type === 'company' ? quoteMention('@F:', opt.query) : opt.type === 'person' ? quoteMention('@', opt.query) : quoteMention('@P:', opt.query);
const displayName = opt.query.replace(/_/g, ' ');
const tag = opt.type === 'company' ? quoteMention('@F:', displayName) : opt.type === 'person' ? quoteMention('@', displayName) : quoteMention('@P:', displayName);
console.log('[tiptap-mention] inserting after create:', tag);
editor.chain().focus()
.deleteRange({ from: savedFrom, to: savedTo })