working on address card editor

This commit is contained in:
2025-10-08 22:16:35 +02:00
parent 2f3a29d606
commit c888845191
14 changed files with 148 additions and 50 deletions

View File

@@ -8,6 +8,7 @@
href = '#',
onclick = evt => { evt.preventDefault(); startEdit(); return false },
onSet = newVal => {return true;},
title = t('long_click_to_edit'),
type = 'div',
value = $bindable(null)
} = $props();
@@ -103,5 +104,5 @@
{#if editable && editing}
<input bind:value={editValue} onkeyup={typed} autofocus />
{:else}
<svelte:element this={type} href={href} onclick={ignore} {onmousedown} {onmouseup} {ontouchstart} {ontouchend} {oncontextmenu} class={{editable}} title={t('long_click_to_edit')} >{value}</svelte:element>
<svelte:element this={type} href={href} onclick={ignore} {onmousedown} {onmouseup} {ontouchstart} {ontouchend} {oncontextmenu} class={{editable}} {title} >{value}</svelte:element>
{/if}

View File

@@ -1,31 +1,46 @@
<script>
import LineEditor from '../../Components/LineEditor.svelte';
import { addr } from '../../vcard.js';
import { t } from '../../translations.svelte';
let { code } = $props();
let { code, patch = (from, to) => true } = $props();
let address = $derived(addr(code));
let home = $derived(code.toLowerCase().includes('type=home'));
let work = $derived(code.toLowerCase().includes('type=work'));
function onSet(oldVal,newVal){
const newCode = code.replace(oldVal,newVal);
return patch(code,newCode);
}
function toggleHome(){
toggleType('HOME');
}
function toggleType(key){
key = key.toUpperCase();
if (code.toUpperCase().includes(';TYPE='+key)) {
const regex = new RegExp(';TYPE='+key, "ig");
patch(code,code.replace(regex,''));
} else patch(code,code.replace('ADR','ADR;TYPE='+key));
}
function toggleWork(){
toggleType('WORK');
}
</script>
<div class="address">
{#if address.box}
<span class="post_box">{address.box}</span>
{/if}
{#if address.ext}
<span class="extended">{address.ext}</span>
{/if}
{#if address.street}
<span class="street">{address.street}</span>
{/if}
{#if address.code}
<span class="code">{address.code}</span>
{/if}
{#if address.loc}
<span class="locality">{address.loc}</span>
{/if}
{#if address.region}
<span class="region">{address.region}</span>
{/if}
{#if address.country}
<span class="country">{address.country}</span>
{/if}
<div>
<span class="symbol {home?'':'inactive'}" onclick={toggleHome}></span>
<span class="symbol {work?'':'inactive'}" onclick={toggleWork} ></span>
</div>
<LineEditor type="span" editable={true} value={address.box} onSet={newVal => onSet(address.box,newVal)} title={t('post_box')} />
<LineEditor type="span" editable={true} value={address.ext} onSet={newVal => onSet(address.ext,newVal)} title={t('extended_address')} />
<LineEditor type="span" editable={true} value={address.street} onSet={newVal => onSet(address.street,newVal)} title={t('street')} />
<LineEditor type="span" editable={true} value={address.code} onSet={newVal => onSet(address.code,newVal)} title={t('post_code')} />
<LineEditor type="span" editable={true} value={address.loc} onSet={newVal => onSet(address.loc,newVal)} title={t('locality')} />
<LineEditor type="span" editable={true} value={address.region} onSet={newVal => onSet(address.region,newVal)} title={t('region')} />
<LineEditor type="span" editable={true} value={address.country} onSet={newVal => onSet(address.country,newVal)} title={t('country')} />
</div>

View File

@@ -15,12 +15,12 @@
let addresses = $derived(contact.vcard.match(/^ADR.*:.+$/gm));
let code = $state(false);
let fns = $derived(contact.vcard.match(/^FN.*:.+$/gm));
let emails = $derived(contact.vcard.match(/^EMAIL.*:.+$/gm));
let extra_fields = $derived(contact.vcard.match(/^X-.*:.+/gm));
let fns = $derived(contact.vcard.match(/^FN.*:.+$/gm));
let orgs = $derived(contact.vcard.match(/^ORG.*:.+$/gm));
async function patch(from,to){
console.log(`patch(${contact.id}: ${from}${to})`);
if (from == to) return;
const url = api(`contact/${contact.id}`);
const res = await fetch(url,{
@@ -64,7 +64,9 @@
</tr>
<tr>
<td>
<Org vcard={contact.vcard} />
{#each orgs as code}
<Org {code} {patch} />
{/each}
</td>
</tr>
<tr>
@@ -75,21 +77,21 @@
<tr>
<td>
{#each addresses as code}
<Address {code} />
<Address {code} {patch} />
{/each}
</td>
</tr>
<tr>
<td>
{#each emails as code}
<Email {code} />
<Email {code} {patch} />
{/each}
</td>
</tr>
<tr>
<td>
{#each extra_fields as code}
<ExtraField {code} />
<ExtraField {code} {patch} />
{/each}
</td>
</tr>

View File

@@ -1,9 +1,37 @@
<script>
import LineEditor from '../../Components/LineEditor.svelte';
import { email } from '../../vcard.js';
let { code } = $props();
let { code, patch = (from, to) => true } = $props();
let adr = $derived(email(code));
let home = $derived(code.toLowerCase().includes('type=home'));
let work = $derived(code.toLowerCase().includes('type=work'));
let value = $derived(email(code));
function onSet(newVal){
const newCode = code.replace(value,newVal);
return patch(code,newCode);
}
function toggleHome(){
toggleType('HOME');
}
function toggleType(key){
key = key.toUpperCase();
if (code.toUpperCase().includes(';TYPE='+key)) {
const regex = new RegExp(';TYPE='+key, "ig");
patch(code,code.replace(regex,''));
} else patch(code,code.replace('EMAIL','EMAIL;TYPE='+key));
}
function toggleWork(){
toggleType('WORK');
}
</script>
<span class="email">{adr}</span>
{#if value}
<span class="symbol {home?'':'inactive'}" onclick={toggleHome}></span>
<span class="symbol {work?'':'inactive'}" onclick={toggleWork} ></span>
<LineEditor type="span" editable={true} {value} {onSet} /><br/>
{/if}

View File

@@ -1,14 +1,25 @@
<script>
import LineEditor from '../../Components/LineEditor.svelte';
import MultiLineEditor from '../../Components/MultilineEditor.svelte';
import { extra } from '../../vcard.js';
import { t } from '../../translations.svelte';
let { code } = $props();
let { code, patch = (from, to) => true } = $props();
let field = $derived(extra(code));
function onSet(newVal){
const newCode = code.replace(field.value,newVal.replaceAll('\n','\\n'));
return patch(code,newCode);
}
</script>
{#if field}
<span class={field.name}>
{field.value}
</span>
<div class={field.name}>
{#if field.value.includes('\\n')}
<MultiLineEditor type="div" editable={true} value={field.value.replaceAll('\\n','\n')} {onSet} title={t(field.name)+' '+t('long_click_to_edit')} />
{:else}
<LineEditor type="div" editable={true} value={field.value} {onSet} title={t(field.name)+' '+t('long_click_to_edit')} />
{/if}
</div>
{/if}

View File

@@ -1,18 +1,18 @@
<script>
import LineEditor from '../../Components/LineEditor.svelte';
import { fn } from '../../vcard.js';
import { t } from '../../translations.svelte';
import { t } from '../../translations.svelte';
let { code, patch = (from, to) => true } = $props();
let name = $derived(fn(code));
let value = $derived(fn(code));
function onSet(newVal){
const newCode = code.replace(name,newVal);
const newCode = code.replace(value,newVal);
return patch(code,newCode);
}
</script>
{#if name}
<LineEditor type="span" editable={true} value={name} {onSet} />
{#if value}
<LineEditor type="span" editable={true} {value} {onSet} title={t('formatted_name')}/>
{/if}

View File

@@ -18,7 +18,6 @@
yikes();
var data = await res.json();
contacts = Object.values(data).sort(byName);
console.log(contacts);
} else {
error(res);
}

View File

@@ -1,12 +1,18 @@
<script>
import LineEditor from '../../Components/LineEditor.svelte';
import { org } from '../../vcard.js';
import { t } from '../../translations.svelte';
import { t } from '../../translations.svelte';
let { vcard } = $props();
let { code, patch = (from, to) => true } = $props();
let o = $derived(org(vcard));
let value = $derived(org(code));
function onSet(newVal){
const newCode = code.replace(value,newVal);
return patch(code,newCode);
}
</script>
{#if o}
<span class="organization">{o}</span>
{#if value}
<LineEditor type="span" editable={true} {value} {onSet} title={t('organization')}/>
{/if}

View File

@@ -28,7 +28,7 @@ export function email(vcard){
}
export function extra(code){
const match = code.match(/^X-(.+):(.+)/)
const match = code.match(/^X-([^:]+):(.+)/)
return match ? {name:match[1],value:match[2]} : null
}