parent
ea38ac7d2a
commit
456ddf7e30
@ -0,0 +1,130 @@ |
|||||||
|
<!-- |
||||||
|
// Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers |
||||||
|
// All rights reserved. |
||||||
|
// Licensed under the Apache License, Version 2.0 |
||||||
|
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0> |
||||||
|
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>, |
||||||
|
// at your option. All files in the project carrying such |
||||||
|
// notice may not be copied, modified, or distributed except |
||||||
|
// according to those terms. |
||||||
|
--> |
||||||
|
|
||||||
|
<script lang="ts"> |
||||||
|
import { onMount, tick, onDestroy } from "svelte"; |
||||||
|
import { Button, Progressbar, Spinner, Alert } from "flowbite-svelte"; |
||||||
|
import { |
||||||
|
toast_error, |
||||||
|
toast_success, |
||||||
|
reset_toasts, |
||||||
|
display_error, |
||||||
|
live_discrete_update, |
||||||
|
discrete_update |
||||||
|
} from "../store"; |
||||||
|
import { |
||||||
|
cur_tab_register_on_save, |
||||||
|
cur_tab_deregister_on_save, |
||||||
|
cur_tab_branch_class |
||||||
|
} from "../tab"; |
||||||
|
import { t } from "svelte-i18n"; |
||||||
|
import wasmUrl from "@automerge/automerge/automerge.wasm?url"; |
||||||
|
import { next as A } from "@automerge/automerge/slim"; |
||||||
|
|
||||||
|
import AMap from "./automerge/AMap.svelte"; |
||||||
|
|
||||||
|
export let commits = {}; |
||||||
|
|
||||||
|
export let readonly = false; |
||||||
|
|
||||||
|
let doc = {}; |
||||||
|
|
||||||
|
let safari_error = false; |
||||||
|
|
||||||
|
function concatenate(uint8arrays) { |
||||||
|
const totalLength = uint8arrays.reduce( |
||||||
|
(total, uint8array) => total + uint8array.byteLength, |
||||||
|
0 |
||||||
|
); |
||||||
|
|
||||||
|
const result = new Uint8Array(totalLength); |
||||||
|
|
||||||
|
let offset = 0; |
||||||
|
uint8arrays.forEach((uint8array) => { |
||||||
|
result.set(uint8array, offset); |
||||||
|
offset += uint8array.byteLength; |
||||||
|
}); |
||||||
|
|
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
let root_proxy; |
||||||
|
|
||||||
|
onMount(async ()=>{ |
||||||
|
try { |
||||||
|
await A.initializeWasm(wasmUrl); |
||||||
|
} catch (e) { |
||||||
|
toast_error($t("errors.no_wasm_on_old_safari")); |
||||||
|
safari_error = true; |
||||||
|
return; |
||||||
|
} |
||||||
|
doc = A.init(); |
||||||
|
if (!readonly) { |
||||||
|
cur_tab_register_on_save(async (updates)=>{ |
||||||
|
|
||||||
|
let update = concatenate(updates); |
||||||
|
await live_discrete_update(update, "Automerge", commits.heads); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
let history = commits.discrete?.registerOnUpdate((update) => { |
||||||
|
doc = A.loadIncremental(doc, update.Automerge); |
||||||
|
}); |
||||||
|
for (const h of history) { |
||||||
|
doc = A.loadIncremental(doc, h.Automerge); |
||||||
|
} |
||||||
|
|
||||||
|
A.change(doc, (d) => { |
||||||
|
root_proxy = d; |
||||||
|
}); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
async function update(event) { |
||||||
|
//console.log("got update", event) |
||||||
|
doc = event.detail.d; |
||||||
|
try { |
||||||
|
await discrete_update(event.detail.u, "Automerge", commits.heads); |
||||||
|
} catch (e){ |
||||||
|
toast_error(display_error(e)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
onDestroy(async ()=>{ |
||||||
|
commits.discrete?.deregisterOnUpdate(); |
||||||
|
if (!readonly) { |
||||||
|
await cur_tab_deregister_on_save(); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
async function updateText(event) { |
||||||
|
doc = A.change(doc, (d) => { |
||||||
|
A.updateText(d, event.detail.p, event.detail.s) |
||||||
|
}); |
||||||
|
let update = A.getLastLocalChange(doc); |
||||||
|
try { |
||||||
|
await discrete_update(update, "Automerge", commits.heads); |
||||||
|
} catch (e){ |
||||||
|
toast_error(display_error(e)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
</script> |
||||||
|
{#if safari_error} |
||||||
|
<Alert class="m-2" color="red">{$t("errors.no_wasm_on_old_safari")}</Alert> |
||||||
|
{:else} |
||||||
|
<div class="grow mb-20" style="min-height:300px;"> |
||||||
|
<AMap {readonly} value={doc} {doc} on:update={update} on:updateText={updateText} proxy={root_proxy}/> |
||||||
|
</div> |
||||||
|
{/if} |
||||||
|
<style> |
||||||
|
|
||||||
|
</style> |
@ -0,0 +1,74 @@ |
|||||||
|
<!-- |
||||||
|
// Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers |
||||||
|
// All rights reserved. |
||||||
|
// Licensed under the Apache License, Version 2.0 |
||||||
|
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0> |
||||||
|
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>, |
||||||
|
// at your option. All files in the project carrying such |
||||||
|
// notice may not be copied, modified, or distributed except |
||||||
|
// according to those terms. |
||||||
|
--> |
||||||
|
|
||||||
|
<script lang="ts"> |
||||||
|
import { onMount, tick, onDestroy } from "svelte"; |
||||||
|
import { Button, Progressbar, Spinner, Alert } from "flowbite-svelte"; |
||||||
|
import { |
||||||
|
toast_error, |
||||||
|
toast_success, |
||||||
|
reset_toasts, |
||||||
|
display_error, |
||||||
|
live_discrete_update, |
||||||
|
discrete_update |
||||||
|
} from "../store"; |
||||||
|
import { |
||||||
|
cur_tab_register_on_save, |
||||||
|
cur_tab_deregister_on_save, |
||||||
|
cur_tab_branch_class |
||||||
|
} from "../tab"; |
||||||
|
import { t } from "svelte-i18n"; |
||||||
|
import wasmUrl from "@automerge/automerge/automerge.wasm?url"; |
||||||
|
import { next as A } from "@automerge/automerge/slim"; |
||||||
|
import Highlight, { LineNumbers } from "svelte-highlight"; |
||||||
|
import json from "svelte-highlight/languages/json"; |
||||||
|
import "svelte-highlight/styles/github.css"; |
||||||
|
|
||||||
|
export let commits = {}; |
||||||
|
|
||||||
|
let doc = {}; |
||||||
|
let source = ""; |
||||||
|
|
||||||
|
let safari_error = false; |
||||||
|
|
||||||
|
onMount(async ()=>{ |
||||||
|
try { |
||||||
|
await A.initializeWasm(wasmUrl); |
||||||
|
} catch (e) { |
||||||
|
toast_error($t("errors.no_wasm_on_old_safari")); |
||||||
|
safari_error = true; |
||||||
|
return; |
||||||
|
} |
||||||
|
doc = A.init(); |
||||||
|
|
||||||
|
let history = commits.discrete?.registerOnUpdate((update) => { |
||||||
|
doc = A.loadIncremental(doc, update.Automerge); |
||||||
|
source = JSON.stringify(doc,null , 4 ); |
||||||
|
}); |
||||||
|
for (const h of history) { |
||||||
|
doc = A.loadIncremental(doc, h.Automerge); |
||||||
|
} |
||||||
|
source = JSON.stringify(doc,null , 4 ); |
||||||
|
}); |
||||||
|
|
||||||
|
onDestroy(async ()=>{ |
||||||
|
commits.discrete?.deregisterOnUpdate(); |
||||||
|
}); |
||||||
|
|
||||||
|
</script> |
||||||
|
|
||||||
|
{#if safari_error} |
||||||
|
<Alert class="m-2" color="red">{$t("errors.no_wasm_on_old_safari")}</Alert> |
||||||
|
{:else if source} |
||||||
|
<Highlight language={json} code={source} class="mb-10" let:highlighted > |
||||||
|
<LineNumbers {highlighted} wrapLines hideBorder /> |
||||||
|
</Highlight> |
||||||
|
{/if} |
@ -0,0 +1,19 @@ |
|||||||
|
<!-- |
||||||
|
// Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers |
||||||
|
// All rights reserved. |
||||||
|
// Licensed under the Apache License, Version 2.0 |
||||||
|
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0> |
||||||
|
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>, |
||||||
|
// at your option. All files in the project carrying such |
||||||
|
// notice may not be copied, modified, or distributed except |
||||||
|
// according to those terms. |
||||||
|
--> |
||||||
|
|
||||||
|
<script lang="ts"> |
||||||
|
import AutomergeEditor from "./AutomergeEditor.svelte"; |
||||||
|
|
||||||
|
export let commits = {}; |
||||||
|
|
||||||
|
</script> |
||||||
|
|
||||||
|
<AutomergeEditor {commits} readonly={true}/> |
@ -0,0 +1,36 @@ |
|||||||
|
|
||||||
|
<!-- |
||||||
|
// Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers |
||||||
|
// All rights reserved. |
||||||
|
// Licensed under the Apache License, Version 2.0 |
||||||
|
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0> |
||||||
|
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>, |
||||||
|
// at your option. All files in the project carrying such |
||||||
|
// notice may not be copied, modified, or distributed except |
||||||
|
// according to those terms. |
||||||
|
--> |
||||||
|
|
||||||
|
<script lang="ts"> |
||||||
|
|
||||||
|
import { createEventDispatcher } from 'svelte'; |
||||||
|
import { Toggle } from 'flowbite-svelte'; |
||||||
|
const dispatch = createEventDispatcher(); |
||||||
|
|
||||||
|
export let value; |
||||||
|
|
||||||
|
function update() { |
||||||
|
temp_val = value; |
||||||
|
} |
||||||
|
|
||||||
|
let temp_val; |
||||||
|
$: value, update(); |
||||||
|
|
||||||
|
const change = (event) => { |
||||||
|
dispatch('updateScalar', { |
||||||
|
v: temp_val, |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
</script> |
||||||
|
|
||||||
|
<Toggle bind:checked={temp_val} on:change={change} /> |
@ -0,0 +1,65 @@ |
|||||||
|
|
||||||
|
<!-- |
||||||
|
// Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers |
||||||
|
// All rights reserved. |
||||||
|
// Licensed under the Apache License, Version 2.0 |
||||||
|
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0> |
||||||
|
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>, |
||||||
|
// at your option. All files in the project carrying such |
||||||
|
// notice may not be copied, modified, or distributed except |
||||||
|
// according to those terms. |
||||||
|
--> |
||||||
|
|
||||||
|
<script lang="ts"> |
||||||
|
import { createEventDispatcher } from 'svelte'; |
||||||
|
const dispatch = createEventDispatcher(); |
||||||
|
|
||||||
|
import { next as A } from "@automerge/automerge/slim"; |
||||||
|
|
||||||
|
export let value; |
||||||
|
|
||||||
|
export let proxy; |
||||||
|
|
||||||
|
export let doc; |
||||||
|
|
||||||
|
async function increment(val) { |
||||||
|
|
||||||
|
doc = A.change(doc, (d) => { |
||||||
|
proxy.increment(val); |
||||||
|
}); |
||||||
|
let update = A.getLastLocalChange(doc); |
||||||
|
dispatch('update', { |
||||||
|
u: update, |
||||||
|
d: doc, |
||||||
|
}); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
function update() { |
||||||
|
temp_val = value.value; |
||||||
|
previous_val = value.value; |
||||||
|
} |
||||||
|
|
||||||
|
let temp_val; |
||||||
|
let previous_val; |
||||||
|
$: value, update(); |
||||||
|
|
||||||
|
const inc = async () => { temp_val+=1; await increment(1) } |
||||||
|
const dec = async () => { temp_val-=1; await increment(-1) } |
||||||
|
const change = async () => { let diff = Math.round(temp_val - previous_val); if (diff !==0) await increment(diff); else temp_val = previous_val; } |
||||||
|
|
||||||
|
</script> |
||||||
|
|
||||||
|
<div class="relative flex items-center max-w-[8rem]"> |
||||||
|
<button type="button" on:click={dec} class="bg-gray-100 dark:bg-gray-700 dark:hover:bg-gray-600 dark:border-gray-600 hover:bg-gray-200 border border-gray-300 rounded-s-lg p-2 h-7 focus:ring-gray-100 dark:focus:ring-gray-700 focus:ring-2 focus:outline-none"> |
||||||
|
<svg class="w-3 h-3 text-gray-900 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 18 2"> |
||||||
|
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M1 1h16"/> |
||||||
|
</svg> |
||||||
|
</button> |
||||||
|
<input bind:value={temp_val} on:change={change} type="text" id="quantity-input" data-input-counter aria-describedby="helper-text-explanation" style="max-width:70px;" class="bg-gray-50 border-x-0 border-gray-300 h-7 text-center text-gray-900 text-sm focus:ring-blue-500 focus:border-blue-500 block w-full py-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="999" required /> |
||||||
|
<button type="button" on:click={inc} class="bg-gray-100 dark:bg-gray-700 dark:hover:bg-gray-600 dark:border-gray-600 hover:bg-gray-200 border border-gray-300 rounded-e-lg p-2 h-7 focus:ring-gray-100 dark:focus:ring-gray-700 focus:ring-2 focus:outline-none"> |
||||||
|
<svg class="w-3 h-3 text-gray-900 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 18 18"> |
||||||
|
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 1v16M1 9h16"/> |
||||||
|
</svg> |
||||||
|
</button> |
||||||
|
</div> |
@ -0,0 +1,73 @@ |
|||||||
|
|
||||||
|
<!-- |
||||||
|
// Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers |
||||||
|
// All rights reserved. |
||||||
|
// Licensed under the Apache License, Version 2.0 |
||||||
|
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0> |
||||||
|
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>, |
||||||
|
// at your option. All files in the project carrying such |
||||||
|
// notice may not be copied, modified, or distributed except |
||||||
|
// according to those terms. |
||||||
|
--> |
||||||
|
|
||||||
|
<script lang="ts"> |
||||||
|
|
||||||
|
import { onMount } from "svelte"; |
||||||
|
import { createEventDispatcher } from 'svelte'; |
||||||
|
const dispatch = createEventDispatcher(); |
||||||
|
import "flowbite/dist/flowbite.min.js" |
||||||
|
|
||||||
|
export let value; |
||||||
|
|
||||||
|
function update() { |
||||||
|
time = value.toLocaleTimeString([],{ |
||||||
|
hour: "2-digit", |
||||||
|
minute: "2-digit" |
||||||
|
}); |
||||||
|
date = value.toLocaleDateString([],{ |
||||||
|
year: "numeric", |
||||||
|
month: "numeric", |
||||||
|
day: "numeric", |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
let time; |
||||||
|
let date; |
||||||
|
$: value, update(); |
||||||
|
|
||||||
|
const change = (event) => { |
||||||
|
|
||||||
|
let newval = new Date(date.split('/').reverse().join('/')+" "+time); |
||||||
|
console.log(time, date, newval) |
||||||
|
|
||||||
|
dispatch('updateScalar', { |
||||||
|
v: newval, |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
</script> |
||||||
|
|
||||||
|
<div class="flex flex-wrap"> |
||||||
|
|
||||||
|
<div class="relative" style="max-width: 129px;"> |
||||||
|
|
||||||
|
<div class="absolute inset-y-0 start-0 flex items-center ps-3 pointer-events-none"> |
||||||
|
<svg class="w-4 h-4 text-primary-700 dark:text-gray-400" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20"> |
||||||
|
<path d="M20 4a2 2 0 0 0-2-2h-2V1a1 1 0 0 0-2 0v1h-3V1a1 1 0 0 0-2 0v1H6V1a1 1 0 0 0-2 0v1H2a2 2 0 0 0-2 2v2h20V4ZM0 18a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V8H0v10Zm5-8h10a1 1 0 0 1 0 2H5a1 1 0 0 1 0-2Z"/> |
||||||
|
</svg> |
||||||
|
</div> |
||||||
|
<input type="text" style="max-width: 129px;cursor:pointer;" on:change={change} bind:value={date} datepicker-format="dd/mm/yyyy" |
||||||
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full ps-10 p-2.5 px-2 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="Select date"> |
||||||
|
|
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="relative" style="max-width: 129px;"> |
||||||
|
<div class="absolute inset-y-0 end-0 top-0 flex items-center pe-3.5 pointer-events-none"> |
||||||
|
<svg class="w-4 h-4 text-primary-700 dark:text-gray-400" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24"> |
||||||
|
<path fill-rule="evenodd" d="M2 12C2 6.477 6.477 2 12 2s10 4.477 10 10-4.477 10-10 10S2 17.523 2 12Zm11-4a1 1 0 1 0-2 0v4a1 1 0 0 0 .293.707l3 3a1 1 0 0 0 1.414-1.414L13 11.586V8Z" clip-rule="evenodd"/> |
||||||
|
</svg> |
||||||
|
</div> |
||||||
|
<input bind:value={time} on:change={change} type="time" class="bg-gray-50 border leading-none border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" /> |
||||||
|
</div> |
||||||
|
</div> |
@ -0,0 +1,118 @@ |
|||||||
|
|
||||||
|
<!-- |
||||||
|
// Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers |
||||||
|
// All rights reserved. |
||||||
|
// Licensed under the Apache License, Version 2.0 |
||||||
|
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0> |
||||||
|
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>, |
||||||
|
// at your option. All files in the project carrying such |
||||||
|
// notice may not be copied, modified, or distributed except |
||||||
|
// according to those terms. |
||||||
|
--> |
||||||
|
|
||||||
|
<script lang="ts"> |
||||||
|
import { new_value, find_type, new_prop_types } from "./utils"; |
||||||
|
|
||||||
|
import AValue from "./AValue.svelte"; |
||||||
|
import { createEventDispatcher } from 'svelte'; |
||||||
|
const dispatch = createEventDispatcher(); |
||||||
|
|
||||||
|
import { next as A } from "@automerge/automerge/slim"; |
||||||
|
|
||||||
|
export let value; |
||||||
|
|
||||||
|
export let proxy; |
||||||
|
|
||||||
|
export let doc; |
||||||
|
|
||||||
|
export let path = undefined; |
||||||
|
|
||||||
|
export let readonly = false; |
||||||
|
|
||||||
|
let props = []; |
||||||
|
$: props = value.map((v,i)=> { |
||||||
|
let ar = [i]; |
||||||
|
ar.push(v); |
||||||
|
let type = find_type(v); |
||||||
|
ar.push(type); |
||||||
|
const with_proxy = type == "counter" || type == "map" || type == "list" ; |
||||||
|
if (with_proxy) { |
||||||
|
ar.push(proxy[i]); |
||||||
|
} |
||||||
|
return ar; |
||||||
|
}); |
||||||
|
|
||||||
|
function add_prop() { |
||||||
|
|
||||||
|
doc = A.change(doc, (d) => { |
||||||
|
proxy.push(new_value(new_prop_type_selected)) |
||||||
|
}); |
||||||
|
let update = A.getLastLocalChange(doc); |
||||||
|
dispatch('update', { |
||||||
|
u: update, |
||||||
|
d: doc, |
||||||
|
}); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
let new_prop_type_selected = 'text'; |
||||||
|
|
||||||
|
function updateText(event) { |
||||||
|
if (path !== undefined) event.detail.p.unshift(path); |
||||||
|
dispatch('updateText', { |
||||||
|
s: event.detail.s, |
||||||
|
p: event.detail.p, |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
function updateScalar(prop, event) { |
||||||
|
doc = A.change(doc, (d) => { |
||||||
|
proxy[prop] = event.detail.v; |
||||||
|
}); |
||||||
|
let update = A.getLastLocalChange(doc); |
||||||
|
dispatch('update', { |
||||||
|
u: update, |
||||||
|
d: doc, |
||||||
|
}); |
||||||
|
} |
||||||
|
</script> |
||||||
|
|
||||||
|
<table class="border-collapse border border-slate-400"> |
||||||
|
<thead> |
||||||
|
<tr class="bg-slate-100"> |
||||||
|
<th>List</th> |
||||||
|
<th class="text-sm"> |
||||||
|
{#if !readonly} |
||||||
|
<span class="ml-2">Push entry at the end of list:</span> |
||||||
|
<select bind:value={new_prop_type_selected}> |
||||||
|
{#each new_prop_types as value}<option value={value.value}>{value.name}</option>{/each} |
||||||
|
</select> |
||||||
|
<button on:click={add_prop}>Add</button> |
||||||
|
{/if} |
||||||
|
</th> |
||||||
|
</tr> |
||||||
|
</thead> |
||||||
|
<tbody> |
||||||
|
{#each props as prop} |
||||||
|
<tr> |
||||||
|
<td>{prop[0]}</td> |
||||||
|
<!-- <td>{prop[2]}</td> --> |
||||||
|
<td> |
||||||
|
<AValue {readonly} type={prop[2]} value={prop[1]} {doc} on:updateText={updateText} on:update proxy={prop[3]} path={prop[0]} on:updateScalar={(event)=>updateScalar(prop[0],event)} /> |
||||||
|
</td> |
||||||
|
<!-- <td>{prop[3]?.constructor.name || ""}</td> --> |
||||||
|
</tr> |
||||||
|
{/each} |
||||||
|
</tbody> |
||||||
|
</table> |
||||||
|
|
||||||
|
<style> |
||||||
|
td { |
||||||
|
padding:5px; |
||||||
|
} |
||||||
|
tr { |
||||||
|
border-bottom: 1px; |
||||||
|
border-style: dashed; |
||||||
|
border-top: none; |
||||||
|
} |
||||||
|
</style> |
@ -0,0 +1,131 @@ |
|||||||
|
|
||||||
|
<!-- |
||||||
|
// Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers |
||||||
|
// All rights reserved. |
||||||
|
// Licensed under the Apache License, Version 2.0 |
||||||
|
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0> |
||||||
|
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>, |
||||||
|
// at your option. All files in the project carrying such |
||||||
|
// notice may not be copied, modified, or distributed except |
||||||
|
// according to those terms. |
||||||
|
--> |
||||||
|
|
||||||
|
<script lang="ts"> |
||||||
|
|
||||||
|
import AValue from "./AValue.svelte"; |
||||||
|
import { createEventDispatcher } from 'svelte'; |
||||||
|
import { new_value, find_type, new_prop_types } from "./utils"; |
||||||
|
|
||||||
|
const dispatch = createEventDispatcher(); |
||||||
|
|
||||||
|
import { next as A } from "@automerge/automerge/slim"; |
||||||
|
|
||||||
|
export let value; |
||||||
|
|
||||||
|
export let proxy; |
||||||
|
|
||||||
|
export let doc; |
||||||
|
|
||||||
|
export let path = undefined; |
||||||
|
|
||||||
|
export let readonly = false; |
||||||
|
|
||||||
|
let props = []; |
||||||
|
$: props = Object.entries(value).map((ar)=> { |
||||||
|
|
||||||
|
let type = find_type(ar[1]); |
||||||
|
ar.push(type); |
||||||
|
const with_proxy = type == "counter" || type == "map" || type == "list" ; |
||||||
|
if (with_proxy) { |
||||||
|
ar.push(proxy[ar[0]]); |
||||||
|
} |
||||||
|
return ar; |
||||||
|
}); |
||||||
|
|
||||||
|
let new_prop = ""; |
||||||
|
function add_prop() { |
||||||
|
if (new_prop.trim().length > 0) { |
||||||
|
doc = A.change(doc, (d) => { |
||||||
|
proxy[new_prop] = new_value(new_prop_type_selected); |
||||||
|
}); |
||||||
|
let update = A.getLastLocalChange(doc); |
||||||
|
dispatch('update', { |
||||||
|
u: update, |
||||||
|
d: doc, |
||||||
|
}); |
||||||
|
new_prop = ""; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
let new_prop_type_selected = 'text'; |
||||||
|
|
||||||
|
function updateText(event) { |
||||||
|
if (path!== undefined) event.detail.p.unshift(path); |
||||||
|
dispatch('updateText', { |
||||||
|
s: event.detail.s, |
||||||
|
p: event.detail.p, |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
function updateScalar(prop, event) { |
||||||
|
|
||||||
|
doc = A.change(doc, (d) => { |
||||||
|
proxy[prop] = event.detail.v; |
||||||
|
}); |
||||||
|
let update = A.getLastLocalChange(doc); |
||||||
|
dispatch('update', { |
||||||
|
u: update, |
||||||
|
d: doc, |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
</script> |
||||||
|
|
||||||
|
<table class="border-collapse border border-slate-400"> |
||||||
|
<thead> |
||||||
|
<tr class="bg-slate-100"> |
||||||
|
<th>Map</th> |
||||||
|
<th class="text-sm"> |
||||||
|
{#if !readonly} |
||||||
|
<span>Add property:</span> |
||||||
|
<input placeholder="Enter the name of property" bind:value={new_prop} class="prop-input"/> |
||||||
|
<select bind:value={new_prop_type_selected}> |
||||||
|
{#each new_prop_types as value}<option value={value.value}>{value.name}</option>{/each} |
||||||
|
</select> |
||||||
|
<button on:click={add_prop}>Add</button> |
||||||
|
{/if} |
||||||
|
</th> |
||||||
|
</tr> |
||||||
|
</thead> |
||||||
|
<tbody> |
||||||
|
{#each props as prop} |
||||||
|
<tr> |
||||||
|
<td>{prop[0]}</td> |
||||||
|
<!-- <td>{prop[2]}</td> --> |
||||||
|
<td> |
||||||
|
<AValue {readonly} type={prop[2]} value={prop[1]} {doc} on:updateText={updateText} on:update proxy={prop[3]} path={prop[0]} on:updateScalar={(event)=>updateScalar(prop[0],event)} /> |
||||||
|
</td> |
||||||
|
<!-- <td>{prop[3]?.constructor.name || ""}</td> --> |
||||||
|
</tr> |
||||||
|
{/each} |
||||||
|
</tbody> |
||||||
|
</table> |
||||||
|
|
||||||
|
|
||||||
|
<style> |
||||||
|
td { |
||||||
|
padding:5px; |
||||||
|
min-width:80px; |
||||||
|
} |
||||||
|
tr { |
||||||
|
border-bottom: 1px; |
||||||
|
border-style: dashed; |
||||||
|
border-top: none; |
||||||
|
|
||||||
|
} |
||||||
|
@screen xs { |
||||||
|
.prop-input { |
||||||
|
min-width: 250px; |
||||||
|
} |
||||||
|
} |
||||||
|
</style> |
@ -0,0 +1,42 @@ |
|||||||
|
|
||||||
|
<!-- |
||||||
|
// Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers |
||||||
|
// All rights reserved. |
||||||
|
// Licensed under the Apache License, Version 2.0 |
||||||
|
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0> |
||||||
|
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>, |
||||||
|
// at your option. All files in the project carrying such |
||||||
|
// notice may not be copied, modified, or distributed except |
||||||
|
// according to those terms. |
||||||
|
--> |
||||||
|
|
||||||
|
<script lang="ts"> |
||||||
|
|
||||||
|
import { createEventDispatcher } from 'svelte'; |
||||||
|
import { Input } from 'flowbite-svelte'; |
||||||
|
const dispatch = createEventDispatcher(); |
||||||
|
|
||||||
|
export let value; |
||||||
|
|
||||||
|
function update() { |
||||||
|
temp_val = value; |
||||||
|
previous_val = value; |
||||||
|
} |
||||||
|
|
||||||
|
let temp_val; |
||||||
|
let previous_val; |
||||||
|
$: value, update(); |
||||||
|
|
||||||
|
const change = (event) => { |
||||||
|
|
||||||
|
let newval = parseFloat(event.target.value.replace(",", ".")); |
||||||
|
//console.log(previous_val, temp_val, newval) |
||||||
|
if (isNaN(newval) || previous_val === newval) return; |
||||||
|
dispatch('updateScalar', { |
||||||
|
v: newval, |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
</script> |
||||||
|
|
||||||
|
<Input style="max-width: 129px;" bind:value={temp_val} on:change={change} on:keyup={change} type="number" /> |
@ -0,0 +1,32 @@ |
|||||||
|
|
||||||
|
<!-- |
||||||
|
// Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers |
||||||
|
// All rights reserved. |
||||||
|
// Licensed under the Apache License, Version 2.0 |
||||||
|
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0> |
||||||
|
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>, |
||||||
|
// at your option. All files in the project carrying such |
||||||
|
// notice may not be copied, modified, or distributed except |
||||||
|
// according to those terms. |
||||||
|
--> |
||||||
|
|
||||||
|
<script lang="ts"> |
||||||
|
|
||||||
|
import { createEventDispatcher } from 'svelte'; |
||||||
|
import { Input } from 'flowbite-svelte'; |
||||||
|
const dispatch = createEventDispatcher(); |
||||||
|
|
||||||
|
export let value; |
||||||
|
|
||||||
|
export let path; |
||||||
|
|
||||||
|
const change = (event) => { |
||||||
|
dispatch('updateText', { |
||||||
|
s: event.target.value, |
||||||
|
p: [path] |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
</script> |
||||||
|
|
||||||
|
<Input bind:value={value} on:keyup={change} type="text" placeholder="Enter some text" /> |
@ -0,0 +1,87 @@ |
|||||||
|
|
||||||
|
<!-- |
||||||
|
// Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers |
||||||
|
// All rights reserved. |
||||||
|
// Licensed under the Apache License, Version 2.0 |
||||||
|
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0> |
||||||
|
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>, |
||||||
|
// at your option. All files in the project carrying such |
||||||
|
// notice may not be copied, modified, or distributed except |
||||||
|
// according to those terms. |
||||||
|
--> |
||||||
|
|
||||||
|
<script lang="ts"> |
||||||
|
|
||||||
|
import AMap from "./AMap.svelte"; |
||||||
|
import AList from "./AList.svelte"; |
||||||
|
import ACounter from "./ACounter.svelte"; |
||||||
|
import AString from "./AString.svelte"; |
||||||
|
import ABoolean from "./ABoolean.svelte"; |
||||||
|
import ANumber from "./ANumber.svelte"; |
||||||
|
import ADate from "./ADate.svelte"; |
||||||
|
|
||||||
|
export let value; |
||||||
|
|
||||||
|
export let type; |
||||||
|
|
||||||
|
export let doc; |
||||||
|
|
||||||
|
export let proxy; |
||||||
|
|
||||||
|
export let path; |
||||||
|
|
||||||
|
export let readonly = false; |
||||||
|
|
||||||
|
function render_date(value) { |
||||||
|
let time = value.toLocaleTimeString([],{ |
||||||
|
hour: "2-digit", |
||||||
|
minute: "2-digit" |
||||||
|
}); |
||||||
|
let date = value.toLocaleDateString([],{ |
||||||
|
year: "numeric", |
||||||
|
month: "numeric", |
||||||
|
day: "numeric", |
||||||
|
}); |
||||||
|
return `${date} ${time}`; |
||||||
|
} |
||||||
|
|
||||||
|
</script> |
||||||
|
|
||||||
|
|
||||||
|
{#if type==="map"} |
||||||
|
<AMap {readonly} {value} {doc} on:updateText on:update {proxy} {path}/> |
||||||
|
{:else if type==="list"} |
||||||
|
<AList {readonly} {value} {doc} on:updateText on:update {proxy} {path}/> |
||||||
|
{:else if type==="counter"} |
||||||
|
{#if !readonly} |
||||||
|
<ACounter {value} {doc} on:update {proxy} /> |
||||||
|
{:else} |
||||||
|
: {value} |
||||||
|
{/if} |
||||||
|
{:else if type==="text"} |
||||||
|
{#if !readonly} |
||||||
|
<AString {value} on:updateText {path}/> |
||||||
|
{:else} |
||||||
|
: {value} |
||||||
|
{/if} |
||||||
|
{:else if type==="boolean"} |
||||||
|
{#if !readonly} |
||||||
|
<ABoolean {value} on:updateScalar/> |
||||||
|
{:else} |
||||||
|
: {value} |
||||||
|
{/if} |
||||||
|
{:else if type==="number"} |
||||||
|
{#if !readonly} |
||||||
|
<ANumber {value} on:updateScalar/> |
||||||
|
{:else} |
||||||
|
: {value} |
||||||
|
{/if} |
||||||
|
{:else if value?.toDateString || type==="timestamp"} |
||||||
|
{#if !readonly} |
||||||
|
<ADate {value} on:updateScalar/> |
||||||
|
{:else} |
||||||
|
: {render_date(value)} |
||||||
|
{/if} |
||||||
|
{:else} |
||||||
|
: {value} |
||||||
|
{/if} |
@ -0,0 +1,81 @@ |
|||||||
|
// Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers
|
||||||
|
// All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0
|
||||||
|
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
|
||||||
|
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
|
||||||
|
// at your option. All files in the project carrying such
|
||||||
|
// notice may not be copied, modified, or distributed except
|
||||||
|
// according to those terms.
|
||||||
|
|
||||||
|
import { next as A } from "@automerge/automerge/slim"; |
||||||
|
|
||||||
|
const UINT = Symbol.for("_am_uint") |
||||||
|
const INT = Symbol.for("_am_int") |
||||||
|
const F64 = Symbol.for("_am_f64") |
||||||
|
const COUNTER = Symbol.for("_am_counter") |
||||||
|
const TEXT = Symbol.for("_am_text") |
||||||
|
|
||||||
|
export function find_type(value) { |
||||||
|
switch (typeof value) { |
||||||
|
case "object": |
||||||
|
if (value == null) { |
||||||
|
return "null" |
||||||
|
} else if (value[UINT]) { |
||||||
|
return "uint" |
||||||
|
} else if (value[INT]) { |
||||||
|
return "number" |
||||||
|
} else if (value[F64]) { |
||||||
|
return "number" |
||||||
|
} else if (value[COUNTER]) { |
||||||
|
return "counter" |
||||||
|
} else if (value instanceof Date) { |
||||||
|
return "timestamp" |
||||||
|
} else if (value instanceof A.RawString) { |
||||||
|
return "str" |
||||||
|
} else if (value instanceof Text) { |
||||||
|
return "text" |
||||||
|
} else if (value instanceof Uint8Array) { |
||||||
|
return "bytes" |
||||||
|
} else if (value instanceof Array) { |
||||||
|
return "list" |
||||||
|
} else if (Object.getPrototypeOf(value) === Object.getPrototypeOf({})) { |
||||||
|
return "map" |
||||||
|
} |
||||||
|
case "boolean": |
||||||
|
return "boolean" |
||||||
|
case "number": |
||||||
|
if (Number.isInteger(value)) { |
||||||
|
return "number" |
||||||
|
} else { |
||||||
|
return "number" |
||||||
|
} |
||||||
|
case "string": |
||||||
|
return "text" |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export const new_prop_types = [ |
||||||
|
{value:'text',name:"text"}, |
||||||
|
{value:'number',name:"number"}, |
||||||
|
{value:'counter',name:"counter"}, |
||||||
|
{value:'boolean',name:"boolean"}, |
||||||
|
{value:'null',name:"null"}, |
||||||
|
{value:'timestamp',name:"timestamp"}, |
||||||
|
{value:'map',name:"map"}, |
||||||
|
{value:'list',name:"list"}, |
||||||
|
{value:'bytes',name:"bytes"} |
||||||
|
]; |
||||||
|
|
||||||
|
export function new_value(new_prop_type_selected) { |
||||||
|
switch (new_prop_type_selected) { |
||||||
|
case 'text': return ''; |
||||||
|
case 'map': return {}; |
||||||
|
case 'list': return []; |
||||||
|
case 'counter': return new A.Counter(); |
||||||
|
case 'number': return 0; |
||||||
|
case 'boolean': return false; |
||||||
|
case 'null': return null; |
||||||
|
case 'timestamp': return new Date(); |
||||||
|
case 'bytes': return new Uint8Array(0); |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue