folding unfolding frontmatter..

This commit is contained in:
Marcell Mars 2021-06-08 12:39:07 +02:00
parent 0c7164b05e
commit b24f80e496
7 changed files with 361 additions and 236 deletions

View file

@ -1,66 +1,66 @@
html, body { html, body {
position: relative; position: relative;
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
body { body {
color: #333; color: #333;
margin: 0; margin: 0;
padding: 8px; padding: 8px;
box-sizing: border-box; box-sizing: border-box;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
} }
a { a {
color: rgb(0,100,200); color: rgb(0,100,200);
text-decoration: none; text-decoration: none;
} }
a:hover { a:hover {
text-decoration: underline; text-decoration: underline;
} }
a:visited { a:visited {
color: rgb(0,80,160); color: rgb(0,80,160);
} }
input, button, select, textarea { input, button, select, textarea {
font-family: inherit; font-family: inherit;
font-size: inherit; font-size: inherit;
-webkit-padding: 0.4em 0; -webkit-padding: 0.4em 0;
padding: 0.4em; padding: 0.4em;
margin: 0 0 0.5em 0; margin: 0 0 0.5em 0;
box-sizing: border-box; box-sizing: border-box;
border: 1px solid #ccc; border: 1px solid #ccc;
border-radius: 2px; border-radius: 2px;
} }
input:disabled { input:disabled {
color: #ccc; color: #ccc;
} }
button { button {
color: #333; color: #333;
background-color: #f4f4f4; background-color: #f4f4f4;
outline: none; outline: none;
} }
button:disabled { button:disabled {
color: #999; color: #999;
} }
button:not(:disabled):active { button:not(:disabled):active {
background-color: #ddd; background-color: #ddd;
} }
button:focus { button:focus {
border-color: #666; border-color: #666;
} }
/* custom part */ /* custom part */
form { .formgrid {
display: grid; display: grid;
grid-template-columns: auto 1fr; grid-template-columns: auto 1fr;
grid-gap: 1rem; grid-gap: 1rem;
@ -145,4 +145,17 @@ select {
margin-top: 0.5rem; margin-top: 0.5rem;
} }
.fmHidden {
display: none;
}
.fmShown {
display: grid;
grid-template-columns: auto 1fr;
grid-gap: 1rem;
}
.togglefold {
margin-top: 1rem;
margin-bottom: 1rem;
}

View file

@ -1,136 +1,203 @@
<script> <script>
import SpTitle from './SpTitle.svelte' import SpTitle from "./SpTitle.svelte";
import SpTiers from './SpTiers.svelte' import SpJournal from "./SpJournal.svelte";
import SpKeys from './SpKeys.svelte' import SpTiers from "./SpTiers.svelte";
import SpCandidates from './SpCandidates.svelte' import SpKeys from "./SpKeys.svelte";
import { onMount } from 'svelte'; import SpCandidates from "./SpCandidates.svelte";
let v = "loading..."; import { onMount } from "svelte";
let relpath = ""; let v = "loading...";
let relpermalink = ""; let relpath = "";
let protocol = ""; let relpermalink = "";
let title = "Foo bar"; let protocol = "";
let title = "Foo bar";
let hases = [];
let hasesCandidates = [];
let frontmatter = {};
frontmatter["triad"] = [];
frontmatter["ascriptions"] = [];
frontmatter["notsand"] = [];
let hases = []; function keyUp(e) {
let hasesCandidates = []; if (e.key == "Escape") {
let frontmatter = {} window.history.back();
frontmatter['triad'] = [];
frontmatter['ascriptions'] = [];
function keyUp(e) {
if (e.key == "Escape") {
window.history.back()
}
}
function diffArrr(arr, arrr) {
let diff = new Set(arr)
for (let e of arrr) {
diff.delete(e)
} }
return Array.from(diff) }
}
function newHasTiers(arrgs) { function diffArrr(arr, arrr) {
let [fmKeyType, index, oldFmKey, newFmKey] = arrgs.detail; let diff = new Set(arr);
frontmatter[fmKeyType][index] = {'fmKey': newFmKey, 'tiers': [], 'candidates': METASP[newFmKey].tiers} for (let e of arrr) {
hasesCandidates = [...hasesCandidates, oldFmKey]; diff.delete(e);
hasesCandidates = hasesCandidates.filter(h => h != newFmKey); }
} return Array.from(diff);
}
function loadHugoPageMetadata() {
var el = document.createElement('script');
el.onload = ()=> {
v = repo.content;
if (v.startsWith("\n")) {
v = v.substring(1)
}
relpath = repo.path; function newHasTiers(arrgs) {
relpermalink = repo.relpermalink let [fmKeyType, index, oldFmKey, newFmKey] = arrgs.detail;
protocol = document.location.protocol.substring(0,4) frontmatter[fmKeyType][index] = {
hases = Object.keys(METASP) fmKey: newFmKey,
Object.entries(repo.frontmatter).forEach(([fmKey,fmValue]) => tiers: [],
{ candidates: METASP[newFmKey].tiers,
let fmKeyType = '' };
if (fmKey.toLowerCase().startsWith("has_")) { hasesCandidates = [...hasesCandidates, oldFmKey];
if (hases.includes(fmKey.substr(4))) { hasesCandidates = hasesCandidates.filter((h) => h != newFmKey);
fmKeyType = 'triad' }
fmKey = fmKey.substr(4)
}
} else if (hases.includes(fmKey.toLowerCase())) {
fmKeyType = 'ascriptions'
} else {
fmKeyType = 'notSandpoints'
}
if (['triad', 'ascriptions'].includes(fmKeyType)) { function loadHugoPageMetadata() {
if (Array.isArray(fmValue)) { var el = document.createElement("script");
let tiers = [] el.onload = () => {
let candidates = [] v = repo.content;
METASP[fmKey].tiers.forEach(t => { if (v.startsWith("\n")) {
if (fmValue.includes(t.file)) { v = v.substring(1);
tiers.push(t) }
} else {
candidates.push(t)
}
})
frontmatter[fmKeyType].push({'fmKey': fmKey, 'tiers': tiers, 'candidates': candidates})
}
}
}
)
hasesCandidates = diffArrr(hases, [...frontmatter['triad'].map(t => { return t.fmKey }), ...frontmatter['ascriptions'].map(t => { return t.fmKey })])
let tkey = Object.keys(repo.frontmatter).filter(t => t.toLowerCase() == "title")[0] relpath = repo.path;
title = repo.frontmatter[tkey] relpermalink = repo.relpermalink;
} protocol = document.location.protocol.substring(0, 4);
el.src = `../js/repo/${location.hash.substring(1)}.js` hases = Object.keys(METASP);
document.body.appendChild(el) Object.entries(repo.frontmatter).forEach(([fmKey, fmValue]) => {
} let fmKeyType = "";
if (fmKey.toLowerCase().startsWith("has_")) {
function addToCandidatesRemoveFromTiers(arrgs) { if (hases.includes(fmKey.substr(4))) {
let [fmKeyType, index, fmKey, tier] = arrgs.detail; fmKeyType = "triad";
frontmatter[fmKeyType][index]['fmKey'] = fmKey; fmKey = fmKey.substr(4);
frontmatter[fmKeyType][index]['tiers'] = frontmatter[fmKeyType][index]['tiers'].filter(t => t !== tier); }
frontmatter[fmKeyType][index]['candidates'] = [...frontmatter[fmKeyType][index]['candidates'], tier] } else if (hases.includes(fmKey.toLowerCase())) {
} fmKeyType = "ascriptions";
} else if (["abstract", "keywords"].includes(fmKey.toLowerCase())) {
fmKeyType = "journal";
} else {
fmKeyType = "notsand";
}
function addToTiersRemoveFromCandidates(arrgs) { if (["triad", "ascriptions"].includes(fmKeyType)) {
let [fmKeyType, index, fmKey, value] = arrgs.detail; if (Array.isArray(fmValue)) {
const tier = frontmatter[fmKeyType][index]['candidates'].filter(t => t.file == value) let tiers = [];
if (tier.length > 0) { let candidates = [];
frontmatter[fmKeyType][index]['tiers'] = [...frontmatter[fmKeyType][index]['tiers'], tier[0]] METASP[fmKey].tiers.forEach((t) => {
frontmatter[fmKeyType][index]['candidates'] = frontmatter[fmKeyType][index]['candidates'].filter(t=> t != tier[0]) if (fmValue.includes(t.file)) {
} tiers.push(t);
} } else {
candidates.push(t);
onMount(()=> {loadHugoPageMetadata()}) }
});
frontmatter[fmKeyType].push({
fmKey: fmKey,
tiers: tiers,
candidates: candidates,
});
}
} else if (fmKeyType == "journal") {
frontmatter["journal"] || (frontmatter["journal"] = []);
frontmatter[fmKeyType].push({ fmKey: fmKey, fmValue: JSON.stringify(fmValue) });
}
});
hasesCandidates = diffArrr(hases, [
...frontmatter["triad"].map((t) => {
return t.fmKey;
}),
...frontmatter["ascriptions"].map((t) => {
return t.fmKey;
}),
]);
let tkey = Object.keys(repo.frontmatter).filter((t) => t.toLowerCase() == "title")[0];
title = repo.frontmatter[tkey];
};
el.src = `../js/repo/${location.hash.substring(1)}.js`;
document.body.appendChild(el);
}
function addToCandidatesRemoveFromTiers(arrgs) {
let [fmKeyType, index, fmKey, tier] = arrgs.detail;
frontmatter[fmKeyType][index]["fmKey"] = fmKey;
frontmatter[fmKeyType][index]["tiers"] = frontmatter[fmKeyType][index]["tiers"].filter(
(t) => t !== tier
);
frontmatter[fmKeyType][index]["candidates"] = [...frontmatter[fmKeyType][index]["candidates"], tier];
}
function addToTiersRemoveFromCandidates(arrgs) {
let [fmKeyType, index, , value] = arrgs.detail;
const tier = frontmatter[fmKeyType][index]["candidates"].filter((t) => t.file == value);
if (tier.length > 0) {
frontmatter[fmKeyType][index]["tiers"] = [...frontmatter[fmKeyType][index]["tiers"], tier[0]];
frontmatter[fmKeyType][index]["candidates"] = frontmatter[fmKeyType][index]["candidates"].filter(
(t) => t != tier[0]
);
}
}
function toggleFold() {
let e = document.getElementById("frontmatter");
if (e && e.classList.contains("fmHidden")) {
e.classList.remove("fmHidden");
e.classList.add("fmShown");
} else if (e && e.classList.contains("fmShown")) {
e.classList.remove("fmShown");
e.classList.add("fmHidden");
}
}
onMount(() => {
loadHugoPageMetadata();
console.dir(frontmatter);
});
</script> </script>
<svelte:window on:keyup={keyUp}/> <svelte:window on:keyup={keyUp} />
<main id="sandpoints"> <main id="sandpoints">
<form> <form>
<SpTitle relpath={relpath} title={title} /> <div class="formgrid">
{#each Object.entries(frontmatter) as fmItems} <SpTitle {relpath} {title} />
{#if ['ascriptions', 'triad'].includes(fmItems[0])} </div>
<div on:click={toggleFold} class="togglefold">[fold/unfold frontmatter]</div>
<span id="frontmatter" class="fmHidden">
{#each Object.entries(frontmatter) as fmItems}
{#each fmItems[1] as fmItem, i} {#each fmItems[1] as fmItem, i}
<SpKeys index={i} fmKeyType={fmItems[0]} fmItem={fmItem} hases={hases} hasesCandidates={hasesCandidates} on:hasTiersSelected={newHasTiers} /> {#if ["ascriptions", "triad"].includes(fmItems[0])}
<SpTiers on:addToCandidatesRemoveFromTiers={addToCandidatesRemoveFromTiers} index={i} fmKeyType={fmItems[0]} fmItem={fmItem} /> <SpKeys
<SpCandidates on:addToTiersRemoveFromCandidates={addToTiersRemoveFromCandidates} index={i} fmKeyType={fmItems[0]} fmItem={fmItem} /> index={i}
fmKeyType={fmItems[0]}
{fmItem}
{hases}
{hasesCandidates}
on:hasTiersSelected={newHasTiers}
/>
<SpTiers
on:addToCandidatesRemoveFromTiers={addToCandidatesRemoveFromTiers}
index={i}
fmKeyType={fmItems[0]}
{fmItem}
/>
<SpCandidates
on:addToTiersRemoveFromCandidates={addToTiersRemoveFromCandidates}
index={i}
fmKeyType={fmItems[0]}
{fmItem}
/>
{:else if fmItems[0] == "journal"}
<label for={fmItem.fmKey}>{fmItem.fmKey}:</label>
<input type="text" name="journal.fmKey" value={fmItem.fmValue} />
{/if}
{/each} {/each}
{/each}
</span>
<div class="formgrid">
<label for="content">Content:</label>
<textarea
bind:value={v}
oninput="this.style.height = '';this.style.height = this.scrollHeight + 3 + 'px'"
/>
{#if document.location.protocol.substring(0, 4) != "file"}
<label for="publish">Publish</label>
<input type="checkbox" id="publish" name="publish" />
{/if} {/if}
{/each} <label for="offline">Offline</label>
<label for="content">Content:</label> <input type="checkbox" id="offline" name="offline" />
<textarea bind:value={v} oninput='this.style.height = "";this.style.height = this.scrollHeight + 3 + "px"'></textarea> <input type="hidden" name="relpermalink" bind:value={relpermalink} />
{#if document.location.protocol.substring(0,4) != "file"} <input type="hidden" name="relpath" bind:value={relpath} />
<label for="publish">Publish</label> <input type="hidden" name="protocol" bind:value={protocol} />
<input type="checkbox" id="publish" name="publish" /> <button id="sandpointsButton">COMMIT/SAVE</button>
{/if} </div>
<label for="offline">Offline</label>
<input type="checkbox" id="offline" name="offline" />
<input type="hidden" name="relpermalink" bind:value={relpermalink} />
<input type="hidden" name="relpath" bind:value={relpath} />
<input type="hidden" name="protocol" bind:value={protocol} />
<button id="sandpointsButton">COMMIT/SAVE</button>
</form> </form>
</main> </main>

View file

@ -1,31 +1,42 @@
<script> <script>
import { createEventDispatcher } from 'svelte'; import { createEventDispatcher } from "svelte";
import { afterUpdate } from 'svelte'; import { afterUpdate } from "svelte";
export let index; export let index;
export let fmItem; export let fmItem;
export let fmKeyType; export let fmKeyType;
export let value; export let value;
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
afterUpdate(() => { afterUpdate(() => {
let addnew = document.getElementById('addnew') let addnew = document.getElementById("addnew");
if (addnew) { if (addnew) {
addnew.selected = 'selected'; addnew.selected = "selected";
} }
}); });
$: { $: {
dispatch("addToTiersRemoveFromCandidates", [fmKeyType, index, fmItem.fmKey, value]) dispatch("addToTiersRemoveFromCandidates", [
value = 1; fmKeyType,
} index,
fmItem.fmKey,
value,
]);
value = 1;
}
</script> </script>
{#if fmItem.candidates && fmItem.candidates.length > 0} {#if fmItem.candidates && fmItem.candidates.length > 0}
<select class="selecttier" bind:value> <select class="selecttier" bind:value>
<option id="addnew" value="1" disabled selected>Add new...</option> <option id="addnew" value="1" disabled selected>Add new...</option>
{#each fmItem.candidates as tier (tier.file)} {#each fmItem.candidates as tier (tier.file)}
<option title="{tier.title}" value={tier.file}>{tier.file} {#if tier.title.length < 70}({tier.title}){:else}({tier.title.slice(0, 68) + '…'}){/if}</option> <option title={tier.title} value={tier.file}
{/each} >{tier.file}
</select> {#if tier.title.length < 70}({tier.title}){:else}({tier.title.slice(
0,
68
) + "…"}){/if}</option
>
{/each}
</select>
{/if} {/if}

View file

@ -1,36 +1,55 @@
<script> <script>
import { createEventDispatcher } from 'svelte'; import { createEventDispatcher } from "svelte";
import { onMount } from 'svelte'; import { onMount } from "svelte";
export let fmItem; export let fmItem;
export let fmKeyType; export let fmKeyType;
export let index; export let index;
export let hases; export let hases;
export let hasesCandidates; export let hasesCandidates;
let value; let value;
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
function optselName(has) {
function optselName(has) { return `_${has}_${fmKeyType}`;
return `_${has}_${fmKeyType}` }
}
onMount(()=> {
const selectEvent = document.getElementById(`${fmKeyType}_${fmItem.fmKey}`)
if (selectEvent) {
selectEvent.addEventListener('change', (e) => {
dispatch("hasTiersSelected", [fmKeyType, index, fmItem.fmKey, e.target.value.split("_")[1]]);})
}
})
onMount(() => {
const selectEvent = document.getElementById(
`${fmKeyType}_${fmItem.fmKey}`
);
if (selectEvent) {
selectEvent.addEventListener("change", (e) => {
dispatch("hasTiersSelected", [
fmKeyType,
index,
fmItem.fmKey,
e.target.value.split("_")[1],
]);
});
}
});
</script> </script>
<select id="{fmKeyType}_{fmItem.fmKey}" bind:value> <select id="{fmKeyType}_{fmItem.fmKey}" bind:value>
{#each hases as has} {#each hases as has}
{#if has == fmItem.fmKey} {#if has == fmItem.fmKey}
<option id="{optselName(has)}" class="hasoption" selected title="{has}" value={optselName(has)}>{'triad' == fmKeyType ? "Has ":""}{has}:</option> <option
{/if} id={optselName(has)}
{/each} class="hasoption"
{#each hasesCandidates as has} selected
<option id="{optselName(has)}" class="hasoption" title="{has}" value={optselName(has)}>{'triad' == fmKeyType ? "Has ":""}{has}:</option> title={has}
{/each} value={optselName(has)}
>{"triad" == fmKeyType ? "Has " : ""}{has}:</option
>
{/if}
{/each}
{#each hasesCandidates as has}
<option
id={optselName(has)}
class="hasoption"
title={has}
value={optselName(has)}
>{"triad" == fmKeyType ? "Has " : ""}{has}:</option
>
{/each}
</select> </select>

View file

@ -1,39 +1,54 @@
<script> <script>
import { createEventDispatcher } from 'svelte'; import { createEventDispatcher } from "svelte";
export let index; export let index;
export let fmItem; export let fmItem;
export let fmKeyType; export let fmKeyType;
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
function removeTier(tier) {
dispatch("addToCandidatesRemoveFromTiers", [fmKeyType, index, fmItem.fmKey, tier])
}
function swapTier(tier, n) { function removeTier(tier) {
fmItem.tiers.some((t, i)=> { dispatch("addToCandidatesRemoveFromTiers", [fmKeyType, index, fmItem.fmKey, tier]);
if (t==tier) { }
fmItem.tiers[i] = fmItem.tiers[i+n]
fmItem.tiers[i+n] = tier
return true
}
})
}
function swapTier(tier, n) {
fmItem.tiers.some((t, i) => {
if (t == tier) {
fmItem.tiers[i] = fmItem.tiers[i + n];
fmItem.tiers[i + n] = tier;
return true;
}
});
}
</script> </script>
{#if fmItem.tiers && fmItem.tiers.length >0} {#if fmItem.tiers && fmItem.tiers.length > 0}
<div class="tierbox"> <div class="tierbox">
{#each fmItem.tiers as tier, i (tier.file)} {#each fmItem.tiers as tier, i (tier.file)}
<div class="tierline"> <div class="tierline">
<div class="filetitle"> <div class="filetitle">
<input readonly type="text" name="{'triad' == fmKeyType ? "has_":"" }{fmItem.fmKey}[]" class="hasinput" value="{tier.file}"><span class="tiertitle">({tier.title})</span> <input
readonly
type="text"
name="{'triad' == fmKeyType ? 'has_' : ''}{fmItem.fmKey}[]"
class="hasinput"
value={tier.file}
/><span class="tiertitle">({tier.title})</span>
</div>
<div class="buttons">
<button
type="button"
class="buttondown"
on:click={swapTier(tier, 1)}
disabled={i + 1 == fmItem.tiers.length ? true : false}>⇓</button
>
<button
type="button"
class="buttonup"
on:click={swapTier(tier, -1)}
disabled={i == 0 ? true : false}>⇑</button
>
<button type="button" on:click={removeTier(tier)}>×</button>
</div>
</div> </div>
<div class="buttons"> {/each}
<button type="button" class="buttondown" on:click="{swapTier(tier, 1)}" disabled={i+1 == fmItem.tiers.length ? true:false}>⇓</button> </div>
<button type="button" class="buttonup" on:click="{swapTier(tier, -1)}" disabled={i == 0 ? true:false}>⇑</button>
<button type="button" on:click="{removeTier(tier)}">×</button>
</div>
</div>
{/each}
</div>
{/if} {/if}

View file

@ -1,9 +1,9 @@
<script> <script>
export let title; export let title;
export let relpath; export let relpath;
</script> </script>
<label for="relpath">File path:</label> <label for="relpath">File path:</label>
<input type="text" name="relpath" value="{relpath}"> <input type="text" name="relpath" value={relpath} />
<label for="title">Title:</label> <label for="title">Title:</label>
<input type="text" name="title" value="{title}"> <input type="text" name="title" value={title} />

View file

@ -7,4 +7,4 @@ const app = new App({
} }
}); });
export default app; export default app;