feat: modeless group operations

feat/axum
Tomáš Mládek 2023-09-03 11:56:03 +02:00
parent a5e33a5061
commit 0a5398b0a7
3 changed files with 140 additions and 55 deletions

View File

@ -12,10 +12,14 @@
const dispatch = createEventDispatcher();
export let entity: Readable<UpObject>;
export let editable = false;
let adding = false;
let groupSelector: Selector;
$: if (adding && groupSelector) groupSelector.focus();
$: groups = ($entity?.attr[ATTR_IN] || []).map(
(e) => [e.value.c as string, e.address] as [string, string]
(e) => [e.value.c as string, e.address] as [string, string],
);
let groupToAdd: IValue | undefined;
@ -38,64 +42,100 @@
}
async function removeGroup(groupAddress: string) {
await api.deleteEntry(groupAddress);
dispatch("change");
if (confirm($i18n.t("Are you sure you want to remove this group?"))) {
await api.deleteEntry(groupAddress);
dispatch("change");
}
}
</script>
{#if groups.length || editable}
<section class="groups labelborder">
<header><h3>{$i18n.t("Groups")}</h3></header>
<div class="content">
{#each groups as [groupAddress, groupEntryAddress]}
<div
class="tag"
on:mouseenter={() => dispatch("highlighted", groupAddress)}
on:mouseleave={() => dispatch("highlighted", undefined)}
>
<UpObjectDisplay address={groupAddress} link />
{#if editable}
<section class="groups labelborder">
<header><h3>{$i18n.t("Groups")}</h3></header>
<div class="content">
{#if adding}
<div class="selector">
<Selector
bind:this={groupSelector}
type="value"
valueTypes={["Address"]}
bind:value={groupToAdd}
on:input={addGroup}
on:focus={(ev) => {
if (!ev.detail) adding = false;
}}
placeholder={$i18n.t("Choose an entity...")}
/>
</div>
{/if}
<div class="body">
<div class="group-list">
{#each groups as [groupAddress, groupEntryAddress]}
<div
class="group"
on:mouseenter={() => dispatch("highlighted", groupAddress)}
on:mouseleave={() => dispatch("highlighted", undefined)}
>
<UpObjectDisplay address={groupAddress} link />
<IconButton
subdued
name="x-circle"
on:click={() => removeGroup(groupEntryAddress)}
/>
{/if}
</div>
{/each}
{#if editable}
<div class="selector">
<Selector
type="value"
valueTypes={["Address"]}
bind:value={groupToAdd}
on:input={addGroup}
placeholder="Choose an entity..."
</div>
{:else}
<div class="no-groups">
{$i18n.t("Object is not in any groups.")}
</div>
{/each}
</div>
{#if !adding}
<div class="add-button">
<IconButton
outline
small
name="folder-plus"
on:click={() => (adding = true)}
/>
</div>
{/if}
</div>
</section>
{/if}
</div>
</section>
<style lang="scss">
@use "./util";
.groups {
margin: 0.25rem 0;
.content {
.group-list {
display: flex;
flex-wrap: wrap;
gap: 0.5rem 0.5rem;
gap: 0.25rem 0.2rem;
align-items: center;
}
.tag {
.group {
display: inline-flex;
align-items: center;
}
.body {
display: flex;
align-items: start;
.group-list {
flex-grow: 1;
}
}
.selector {
width: 100%;
margin-bottom: 0.5rem;
}
}
.no-groups {
opacity: 0.66;
}
</style>

View File

@ -11,7 +11,11 @@
const dispatch = createEventDispatcher();
export let entity: Readable<UpObject>;
export let editable = false;
let adding = false;
let typeSelector: Selector;
$: if (adding && typeSelector) typeSelector.focus();
$: typeEntries = $entity?.attr[`~${ATTR_OF}`] || [];
@ -45,35 +49,53 @@
}
</script>
{#if typeEntries.length || (editable && $entity?.attr["~IN"]?.length)}
{#if typeEntries.length || $entity?.attr["~IN"]?.length}
<section class="labelborder">
<header><h3>{$i18n.t("Type Attributes")}</h3></header>
<div class="content">
<ul class="attributes">
{#each typeEntries as typeEntry}
<li class="attribute">
<div class="label">
<UpObjectDisplay address={typeEntry.entity} link />
</div>
<div class="controls">
{#if editable}
{#if adding}
<div class="selector">
<Selector
bind:this={typeSelector}
type="attribute"
bind:attribute={attributeToAdd}
on:input={add}
placeholder={$i18n.t("Assign an attribute to this type...")}
on:focus={(ev) => {
if (!ev.detail) adding = false;
}}
/>
</div>
{/if}
<div class="body">
<ul class="attributes">
{#each typeEntries as typeEntry}
<li class="attribute">
<div class="label">
<UpObjectDisplay address={typeEntry.entity} link />
</div>
<div class="controls">
<IconButton
name="x-circle"
on:click={() => remove(typeEntry)}
/>
{/if}
</div>
</li>
{/each}
</ul>
{#if editable}
<Selector
type="attribute"
bind:attribute={attributeToAdd}
on:input={add}
placeholder={$i18n.t("Assign an attribute to this type...")}
/>
{/if}
</div>
</li>
{:else}
<li class="no-attributes">
{$i18n.t("No attributes assigned to this type.")}
</li>
{/each}
</ul>
<div class="add-button">
<IconButton
outline
small
name="plus-circle"
on:click={() => (adding = true)}
/>
</div>
</div>
</div>
</section>
{/if}
@ -92,6 +114,24 @@
display: flex;
}
.body {
display: flex;
align-items: start;
.attributes {
flex-grow: 1;
}
}
.selector {
width: 100%;
margin-bottom: 0.5rem;
}
.no-attributes {
opacity: 0.66;
}
ul {
list-style: none;
padding: 0;

View File

@ -7,6 +7,7 @@
export let title: string | undefined = undefined;
export let outline = false;
export let subdued = false;
export let small = false;
export let color: string | undefined = "var(--active-color, var(--primary))";
</script>
@ -15,6 +16,7 @@
class:active
class:outline
class:subdued
class:small
{disabled}
{title}
style="--color: {color}"
@ -55,6 +57,9 @@
border-radius: 4px;
padding: 0.25em 1em;
&.small {
padding: 0.1em 0.8em;
}
}
.active,