ui optimizations
This commit is contained in:
parent
afb330ed5d
commit
6116069fff
|
|
@ -1 +1 @@
|
||||||
1.0.41
|
1.0.50
|
||||||
|
|
@ -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'))
|
.filter(c => !c.deletedAt && !c.archivedAt && (c.type === 'person' || c.type === 'project' || c.type === 'company'))
|
||||||
.toArray();
|
.toArray();
|
||||||
|
|
||||||
const q = query.toLowerCase();
|
const q = query.replace(/_/g, ' ').toLowerCase();
|
||||||
const items = all
|
const items = all
|
||||||
.map(buildMentionItem)
|
.map(buildMentionItem)
|
||||||
.filter(m => m.mentionName.toLowerCase().includes(q))
|
.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 exactMatch = items.some(m => m.mentionName.toLowerCase() === q);
|
||||||
const createOptions: CreateOption[] = [];
|
const createOptions: CreateOption[] = [];
|
||||||
if (query.length > 0 && !exactMatch) {
|
if (query.length > 0 && !exactMatch) {
|
||||||
|
const displayName = query.replace(/_/g, ' ');
|
||||||
createOptions.push(
|
createOptions.push(
|
||||||
{ type: 'person', label: `+ Person "${query}" anlegen`, query },
|
{ type: 'person', label: `+ Person "${displayName}" anlegen`, query },
|
||||||
{ type: 'project', label: `+ Projekt "${query}" anlegen`, query },
|
{ type: 'project', label: `+ Projekt "${displayName}" anlegen`, query },
|
||||||
{ type: 'company', label: `+ Firma "${query}" 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> {
|
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 slug = name.toLowerCase().replace(/\s+/g, '-');
|
||||||
const id = opt.type === 'company' ? `f-${slug}` : opt.type === 'person' ? `u-${slug}` : `p-${slug}`;
|
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}`;
|
const contextName = opt.type === 'company' ? `Firma ${name}` : opt.type === 'person' ? `Person ${name}` : `Project ${name}`;
|
||||||
|
|
|
||||||
|
|
@ -158,7 +158,8 @@ function showCreatePopup(name: string, type: 'person' | 'project' | 'company', x
|
||||||
async function handlePersonClick(name: string, event: MouseEvent, sourceEl: HTMLElement) {
|
async function handlePersonClick(name: string, event: MouseEvent, sourceEl: HTMLElement) {
|
||||||
const ctx = await findContextByMentionName(name, 'person');
|
const ctx = await findContextByMentionName(name, 'person');
|
||||||
if (ctx) {
|
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);
|
showPersonPopup(name, ctx.id, ratingCtx, event.clientX, event.clientY, sourceEl);
|
||||||
} else {
|
} else {
|
||||||
showCreatePopup(name, 'person', event.clientX, event.clientY);
|
showCreatePopup(name, 'person', event.clientX, event.clientY);
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,7 @@
|
||||||
{placeholder}
|
{placeholder}
|
||||||
{minHeight}
|
{minHeight}
|
||||||
onchange={(md) => editorContent = md}
|
onchange={(md) => editorContent = md}
|
||||||
|
onsave={save}
|
||||||
/>
|
/>
|
||||||
{:else}
|
{:else}
|
||||||
<!-- svelte-ignore a11y_click_events_have_key_events a11y_no_static_element_interactions -->
|
<!-- svelte-ignore a11y_click_events_have_key_events a11y_no_static_element_interactions -->
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@
|
||||||
<MarkdownEditor
|
<MarkdownEditor
|
||||||
content={editText}
|
content={editText}
|
||||||
onchange={(md) => editText = md}
|
onchange={(md) => editText = md}
|
||||||
|
onsave={saveEdit}
|
||||||
minHeight="100px"
|
minHeight="100px"
|
||||||
/>
|
/>
|
||||||
<div class="flex justify-end gap-1 mt-1">
|
<div class="flex justify-end gap-1 mt-1">
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,10 @@
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
minHeight?: string;
|
minHeight?: string;
|
||||||
onchange?: (markdown: string) => void;
|
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 element: HTMLDivElement;
|
||||||
let editor: Editor | null = null;
|
let editor: Editor | null = null;
|
||||||
|
|
@ -42,7 +44,7 @@
|
||||||
onchange?.(md);
|
onchange?.(md);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handlePaste(_view: any, event: ClipboardEvent): Promise<boolean> {
|
function handlePaste(_view: any, event: ClipboardEvent): boolean {
|
||||||
const items = event.clipboardData?.items;
|
const items = event.clipboardData?.items;
|
||||||
if (!items) return false;
|
if (!items) return false;
|
||||||
|
|
||||||
|
|
@ -50,12 +52,14 @@
|
||||||
if (item.type.startsWith('image/')) {
|
if (item.type.startsWith('image/')) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const blob = item.getAsFile();
|
const blob = item.getAsFile();
|
||||||
if (!blob) continue;
|
if (blob) {
|
||||||
|
storeImage(blob)
|
||||||
const id = await storeImage(blob);
|
.then((id) => getImageUrl(id))
|
||||||
const url = await getImageUrl(id);
|
.then((url) => {
|
||||||
if (url && editor) {
|
if (url && editor) {
|
||||||
editor.chain().focus().setImage({ src: url }).run();
|
editor.chain().focus().setImage({ src: url }).run();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -82,6 +86,17 @@
|
||||||
content: initialContent,
|
content: initialContent,
|
||||||
editorProps: {
|
editorProps: {
|
||||||
handlePaste,
|
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: {
|
attributes: {
|
||||||
style: `min-height: ${minHeight}`
|
style: `min-height: ${minHeight}`
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -187,9 +187,11 @@
|
||||||
>
|
>
|
||||||
{/if}
|
{/if}
|
||||||
{#if isProcessed}
|
{#if isProcessed}
|
||||||
<span
|
<button
|
||||||
class="ml-2.5 rounded bg-[#444] px-1.5 py-0.5 text-[0.7em] text-[#aaa]"
|
class="ml-2.5 rounded bg-[#444] px-1.5 py-0.5 text-[0.7em] text-[#aaa] hover:bg-[#555] hover:text-white"
|
||||||
>BESPROCHEN</span
|
title="Als aktiv markieren"
|
||||||
|
onclick={(e) => { e.stopPropagation(); processedTopicIds.remove(topic.id); }}
|
||||||
|
>BESPROCHEN</button
|
||||||
>
|
>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -212,6 +214,8 @@
|
||||||
bind:this={noteEditor}
|
bind:this={noteEditor}
|
||||||
placeholder="- Notiz... (z.B. '-> NAME' or '@P:PROJECT')"
|
placeholder="- Notiz... (z.B. '-> NAME' or '@P:PROJECT')"
|
||||||
onchange={(md) => (noteText = md)}
|
onchange={(md) => (noteText = md)}
|
||||||
|
onsave={handleSaveOnly}
|
||||||
|
onsavearchive={handleDone}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{#if showDailyControls}
|
{#if showDailyControls}
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,8 @@ export const TiptapMention = Extension.create({
|
||||||
const opt = createOptions[index - items.length];
|
const opt = createOptions[index - items.length];
|
||||||
if (!opt) return;
|
if (!opt) return;
|
||||||
await createMentionContext(opt);
|
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);
|
console.log('[tiptap-mention] inserting after create:', tag);
|
||||||
editor.chain().focus()
|
editor.chain().focus()
|
||||||
.deleteRange({ from: savedFrom, to: savedTo })
|
.deleteRange({ from: savedFrom, to: savedTo })
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue