Skip to content

Commit

Permalink
rework CrdtMultiOptionField to support editing object lists
Browse files Browse the repository at this point in the history
  • Loading branch information
hahn-kev committed Jul 1, 2024
1 parent db913e8 commit 23add28
Show file tree
Hide file tree
Showing 8 changed files with 57 additions and 27 deletions.
25 changes: 20 additions & 5 deletions backend/FwDataMiniLcmBridge/Api/UpdateProxy/UpdateSenseProxy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,17 +55,32 @@ public override Guid? PartOfSpeechId
}
}

public override IList<SemanticDomain> SemanticDomains
//the frontend may sometimes try to issue patches to remove Domain.Code or Name, but all we care about is Id
//when those cases happen then Id will be default, so we ignore them.
public new IList<UpdateProxySemanticDomain> SemanticDomains
{
get => new UpdateListProxy<SemanticDomain>(
semanticDomain => sense.SemanticDomainsRC.Add(lexboxLcmApi.GetLcmSemanticDomain(semanticDomain)),
semanticDomain => sense.SemanticDomainsRC.Remove(sense.SemanticDomainsRC.First(sd => sd.Guid == semanticDomain.Id)),
i => new SemanticDomain { Id = sense.SemanticDomainsRC.ElementAt(i).Guid, Code = "", Name = new MultiString() },
get => new UpdateListProxy<UpdateProxySemanticDomain>(
semanticDomain =>
{
if (semanticDomain.Id != default) sense.SemanticDomainsRC.Add(lexboxLcmApi.GetLcmSemanticDomain(semanticDomain.Id));
},
semanticDomain =>
{
if (semanticDomain.Id != default) sense.SemanticDomainsRC.Remove(sense.SemanticDomainsRC.First(sd => sd.Guid == semanticDomain.Id));
},
i => new UpdateProxySemanticDomain { Id = sense.SemanticDomainsRC.ElementAt(i).Guid },
sense.SemanticDomainsRC.Count
);
set => throw new NotImplementedException();
}

public class UpdateProxySemanticDomain
{
public Guid Id { get; set; }
public string? Code { get; set; }
public MultiString? Name { get; set; }
}

public override IList<ExampleSentence> ExampleSentences
{
get =>
Expand Down
2 changes: 1 addition & 1 deletion frontend/viewer/src/ProjectView.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@
});
const optionProvider: OptionProvider = {
partsOfSpeech: derived([writingSystems, partsOfSpeech], ([ws, pos]) => pos?.map(option => ({ value: option.id, label: pickBestAlternative(option.name, ws?.analysis[0]) })) ?? []),
semanticDomains: derived([writingSystems, semanticDomains], ([ws, sd]) => sd?.map(option => ({ value: option, label: pickBestAlternative(option.name, ws?.analysis[0]) })) ?? []),
semanticDomains: derived([writingSystems, semanticDomains], ([ws, sd]) => sd?.map(option => ({ value: option.id, label: pickBestAlternative(option.name, ws?.analysis[0]) })) ?? []),
};
setContext('optionProvider', optionProvider);
Expand Down
2 changes: 2 additions & 0 deletions frontend/viewer/src/lib/config-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ export type OptionFieldConfig = {
ws: `first-${WritingSystemType}`;
}

export type OptionFieldValue = {id: string};

export type BaseEntityFieldConfig<T> = (({
type: 'multi';
id: ConditionalKeys<T, IMultiString>;
Expand Down
22 changes: 10 additions & 12 deletions frontend/viewer/src/lib/entry-editor/CrdtMultiOptionField.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
import type { ComponentProps } from 'svelte';
import CrdtField from './CrdtField.svelte';
import { TextField, type MenuOption, MultiSelectField } from 'svelte-ux';
import type {OptionFieldValue} from '../config-types';
export let value: any[];
export let value: OptionFieldValue[];
export let unsavedChanges = false;
export let options: MenuOption[] = [];
Expand All @@ -13,25 +14,21 @@
export let readonly: boolean | undefined = undefined;
let append: HTMLElement;
function asOption(value: any): MenuOption {
if (!(typeof value === 'object' && 'label' in value && 'value' in value)) {
throw new Error('Invalid option');
}
return value;
}
function asOptions(values: any[]): MenuOption[] {
return values?.map(asOption) ?? [];
function asMultiSelectValues(values: any[]): string[] {
return values?.map(v => v.id) ?? [];
}
function asObjectValues(values: string[]) {
return values.map(v => ({id: v}));
}
</script>

<CrdtField on:change bind:value bind:unsavedChanges let:editorValue let:onEditorValueChange viewMergeButtonPortal={append}>
<MultiSelectField
on:change={(e) => {
console.log(e);
onEditorValueChange(e.detail.value, true);
onEditorValueChange(asObjectValues(e.detail.value), true);
}}
value={editorValue}
value={asMultiSelectValues(editorValue)}
disabled={readonly}
{options}
valueProp="value"
Expand All @@ -49,6 +46,7 @@
<span bind:this={append} slot="append" />
</MultiSelectField>
</CrdtField>
{@debug value}

<style lang="postcss">
:global(.unresolved-merge .field-container) {
Expand Down
4 changes: 2 additions & 2 deletions frontend/viewer/src/lib/entry-editor/FieldEditor.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script lang="ts">
import type { MultiString } from '../mini-lcm';
import type { FieldConfig, OptionFieldConfig } from '../config-types';
import type {FieldConfig, OptionFieldConfig, OptionFieldValue} from '../config-types';
import MultiOptionEditor from './MultiOptionEditor.svelte';
import SingleOptionEditor from './SingleOptionEditor.svelte';
import SingleFieldEditor from './SingleFieldEditor.svelte';
Expand Down Expand Up @@ -32,7 +32,7 @@
function isSingleOption(state: {value: unknown, field: FieldConfig}): state is {value: string, field: FieldConfig & OptionFieldConfig} {
return field.type === 'option';
}
function isMultiOption(state: {value: unknown, field: FieldConfig}): state is {value: string[], field: FieldConfig & OptionFieldConfig} {
function isMultiOption(state: {value: unknown, field: FieldConfig}): state is {value: OptionFieldValue[], field: FieldConfig & OptionFieldConfig} {
return field.type === 'multi-option';
}
</script>
Expand Down
4 changes: 2 additions & 2 deletions frontend/viewer/src/lib/entry-editor/MultiOptionEditor.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@
import { readable, type Readable } from 'svelte/store';
import { getContext } from 'svelte';
import { pickWritingSystems } from '../utils';
import type { FieldConfig, OptionFieldConfig, ViewConfig } from '../config-types';
import type {FieldConfig, OptionFieldConfig, OptionFieldValue, ViewConfig} from '../config-types';
type T = $$Generic<{}>;
export let field: FieldConfig & OptionFieldConfig;
export let value: string[];
export let value: OptionFieldValue[];
let options: Readable<MenuOption[]> = readable([]);
$: options = pickOptions(field);
Expand Down
24 changes: 20 additions & 4 deletions frontend/viewer/src/lib/sandbox/Sandbox.svelte
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<script lang="ts">
import { MultiSelectField, } from 'svelte-ux';
import {Button, type MenuOption, MultiSelectField,} from 'svelte-ux';
import CrdtMultiOptionField from '../entry-editor/CrdtMultiOptionField.svelte';
const options = [
{ name: 'One', value: 1 },
Expand All @@ -9,8 +10,23 @@
];
let value = [3];
</script>
<MultiSelectField {options} {value} on:change={(e) => (value = e.detail.value)} />
const crdtOptions:MenuOption[] = [
{value: 'a', label: 'Alpha'},
{value: 'b', label: 'Beta'},
{value: 'c', label: 'Charlie'},
];
let crdtValue = [{id: 'a'}];
</script>

<div class="bg-green-400 h-[3000px]"></div>
<div class="grid gap-3 justify-items-start">
<MultiSelectField {options} {value} on:change={(e) => (value = e.detail.value)}/>
<p>selected: {value.join('|')}</p>
<Button on:click={() => value = [4]}>Select Four only</Button>
</div>
<div class="grid gap-3 justify-items-start">
<CrdtMultiOptionField bind:value={crdtValue} options={crdtOptions}/>
<p>selected: {crdtValue.map(c => c.id).join('|')}</p>
<Button on:click={() => crdtValue = [{id: 'c'}]}>Select Charlie only</Button>
</div>
1 change: 0 additions & 1 deletion frontend/viewer/src/lib/services/save-event-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ export const saveHandler: SaveHandler = async <T>(saveAction: () => Promise<T>):
saveEventDispatcher.set({ saving: true });
try {
const result = await saveAction();
throw '';
saveEventDispatcher.set({ saved: true });
return result;
} catch (e) {
Expand Down

0 comments on commit 23add28

Please sign in to comment.