refactor edit and edges
This commit is contained in:
@@ -1,114 +0,0 @@
|
||||
<script>
|
||||
import {createEventDispatcher, getContext} from "svelte";
|
||||
import Index from "../../content/Index.svelte";
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
const channel = getContext("channel");
|
||||
$: data = {};
|
||||
let isOpen = false;
|
||||
let selectedRecords = [];
|
||||
// onMount(() => {
|
||||
// load();
|
||||
// });
|
||||
|
||||
export function open(schema) {
|
||||
isOpen = true;
|
||||
load(schema);
|
||||
}
|
||||
|
||||
export function close() {
|
||||
isOpen = false;
|
||||
selectedRecords = [];
|
||||
}
|
||||
|
||||
function load(schema) {
|
||||
axios
|
||||
.get(channel.lucentUrl + "/content/" + schema)
|
||||
.then((response) => {
|
||||
data = response.data;
|
||||
})
|
||||
.catch((error) => console.log(error));
|
||||
}
|
||||
|
||||
function insert(e) {
|
||||
e.preventDefault();
|
||||
dispatch("insert", {
|
||||
records: selectedRecords,
|
||||
action: "insert",
|
||||
});
|
||||
}
|
||||
|
||||
function replace(e) {
|
||||
e.preventDefault();
|
||||
dispatch("insert", {
|
||||
records: selectedRecords,
|
||||
action: "replace",
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if data.schema}
|
||||
<div
|
||||
class="modal fade show"
|
||||
tabindex="-1"
|
||||
class:d-block={isOpen}
|
||||
aria-modal="true"
|
||||
role="dialog"
|
||||
style="background: rgba(100,100,100,.6);"
|
||||
>
|
||||
<div class="modal-dialog modal-dialog-centered modal-dialog-scrollable">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<div class="d-flex align-items-center">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-primary me-1"
|
||||
on:click={insert}
|
||||
disabled={selectedRecords.length === 0}
|
||||
>
|
||||
Insert
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-outline-primary me-3"
|
||||
on:click={replace}
|
||||
disabled={selectedRecords.length === 0}
|
||||
>
|
||||
Replace
|
||||
</button>
|
||||
{#if selectedRecords.length > 0}
|
||||
<span class="">
|
||||
{selectedRecords.length} records selected
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<button
|
||||
on:click|preventDefault={(e) => (isOpen = false)}
|
||||
type="button"
|
||||
class="btn-close"
|
||||
data-bs-dismiss="modal"
|
||||
aria-label="Close"
|
||||
/>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<Index {...data} bind:selected={selectedRecords}/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.modal-dialog {
|
||||
width: auto;
|
||||
max-width: 100%;
|
||||
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
margin: 40px auto;
|
||||
width: auto;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -1,56 +0,0 @@
|
||||
<script>
|
||||
import { getErrorMessage } from "./errorMessage";
|
||||
export let id;
|
||||
export let field;
|
||||
export let value;
|
||||
export let isCreateMode;
|
||||
export let validationErrors;
|
||||
$: errorMessage = getErrorMessage(validationErrors, field.name);
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="radio"
|
||||
class:is-invalid={errorMessage}
|
||||
bind:group={value}
|
||||
id="{id}-1"
|
||||
value={true}
|
||||
disabled={field.readonly && !isCreateMode}
|
||||
/>
|
||||
<label class="form-check-label" for="{id}-1">Yes</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="radio"
|
||||
id="{id}-2"
|
||||
class:is-invalid={errorMessage}
|
||||
bind:group={value}
|
||||
value={false}
|
||||
disabled={field.readonly && !isCreateMode}
|
||||
/>
|
||||
<label class="form-check-label" for="{id}-2">No</label>
|
||||
</div>
|
||||
{#if field.nullable}
|
||||
<div class="form-check form-check-inline">
|
||||
<input
|
||||
class="form-check-input"
|
||||
class:is-invalid={errorMessage}
|
||||
id="{id}-3"
|
||||
type="radio"
|
||||
bind:group={value}
|
||||
value={null}
|
||||
disabled={field.readonly && !isCreateMode}
|
||||
/>
|
||||
<label class="form-check-label" for="{id}-3">Don't Know</label>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if errorMessage}
|
||||
<div class="invalid-feedback d-block">
|
||||
{errorMessage}
|
||||
</div>
|
||||
{/if}
|
||||
@@ -1,37 +0,0 @@
|
||||
<script>
|
||||
import { getErrorMessage } from "./errorMessage";
|
||||
export let field;
|
||||
export let value;
|
||||
export let isCreateMode;
|
||||
export let validationErrors;
|
||||
export let id;
|
||||
$: errorMessage = getErrorMessage(validationErrors, field.name);
|
||||
</script>
|
||||
|
||||
<div class="mb-0">
|
||||
<div class="input-group ">
|
||||
<div style="width:64px;">
|
||||
<input
|
||||
type="color"
|
||||
{id}
|
||||
class="form-control form-control-color"
|
||||
disabled={field.readonly && !isCreateMode}
|
||||
bind:value
|
||||
/>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
class:is-invalid={errorMessage}
|
||||
{id}
|
||||
class="form-control"
|
||||
bind:value
|
||||
readonly={field.readonly && !isCreateMode}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{#if errorMessage}
|
||||
<div class="invalid-feedback d-block">
|
||||
{errorMessage}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -1,70 +0,0 @@
|
||||
<script>
|
||||
import {getContext} from "svelte";
|
||||
import {debounce} from "lodash";
|
||||
import {previewTitle} from "../Preview";
|
||||
|
||||
const channel = getContext("channel");
|
||||
export let field;
|
||||
|
||||
export let value;
|
||||
export let search;
|
||||
$: options = [];
|
||||
export const update = debounce((e) => {
|
||||
axios
|
||||
.get("/records/suggestions", {
|
||||
params: {
|
||||
schema: field.optionsFrom,
|
||||
field: field.optionsField,
|
||||
value: search,
|
||||
ui: field.ui,
|
||||
},
|
||||
})
|
||||
.then((response) => {
|
||||
options = response.data;
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
});
|
||||
}, 500);
|
||||
|
||||
function select(e, option) {
|
||||
e.preventDefault();
|
||||
value = option.data[field.optionsField];
|
||||
search = "";
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if field.optionsFrom}
|
||||
{#each options as option (option.id)}
|
||||
<div
|
||||
on:click={(e) => select(e, option)}
|
||||
on:keypress={(e) => select(e, option)}
|
||||
>
|
||||
<span class="dropdown-item">
|
||||
{previewTitle(channel.schemas, option)}
|
||||
<small class="text-muted "
|
||||
>{option.data[field.optionsField]}</small
|
||||
>
|
||||
</span>
|
||||
</div>
|
||||
{:else}
|
||||
{#if search && field.optionsSuggest}
|
||||
<div
|
||||
on:click={(e) => {
|
||||
value = search;
|
||||
search = "";
|
||||
}}
|
||||
on:keypress={(e) => {
|
||||
value = search;
|
||||
search = "";
|
||||
}}
|
||||
>
|
||||
<span class="dropdown-item">
|
||||
Add "{search}"
|
||||
</span>
|
||||
</div>
|
||||
{:else}
|
||||
No results
|
||||
{/if}
|
||||
{/each}
|
||||
{/if}
|
||||
@@ -1,112 +0,0 @@
|
||||
<script>
|
||||
import Datalist from "./Datalist.svelte";
|
||||
import {onMount} from "svelte";
|
||||
import flatpickr from "flatpickr";
|
||||
import "flatpickr/dist/flatpickr.css";
|
||||
import "flatpickr/dist/themes/light.css";
|
||||
import {getErrorMessage} from "./errorMessage";
|
||||
import Icon from "../../common/Icon.svelte";
|
||||
|
||||
export let field;
|
||||
export let value;
|
||||
export let id;
|
||||
export let isCreateMode;
|
||||
export let validationErrors;
|
||||
$: search = "";
|
||||
$: listMode = field.optionsFrom && !(field.readonly && !isCreateMode);
|
||||
$: errorMessage = getErrorMessage(validationErrors, field.name);
|
||||
let list;
|
||||
let pickerInput;
|
||||
let pickerInstance;
|
||||
let flatpickrOptions = {
|
||||
enableTime: false,
|
||||
allowInput: true,
|
||||
dateFormat: "Y-m-d",
|
||||
};
|
||||
|
||||
if (field.min) {
|
||||
flatpickrOptions.minDate = field.min;
|
||||
}
|
||||
|
||||
if (field.max) {
|
||||
flatpickrOptions.maxDate = field.max;
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
if (!field.readonly || isCreateMode) {
|
||||
if (listMode) {
|
||||
flatpickrOptions.clickOpens = false;
|
||||
}
|
||||
pickerInstance = flatpickr(pickerInput, flatpickrOptions);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="mb-0">
|
||||
|
||||
{#if listMode}
|
||||
<div class="dropdown d-flex">
|
||||
<input
|
||||
type="search"
|
||||
{id}
|
||||
on:keyup={list.update}
|
||||
on:focus={list.update}
|
||||
class="form-control dropdown-toggle"
|
||||
class:is-invalid={errorMessage}
|
||||
bind:value={search}
|
||||
bind:this={pickerInput}
|
||||
placeholder="Search for options"
|
||||
data-bs-toggle="dropdown"
|
||||
autocomplete="off"
|
||||
readonly={field.readonly && !isCreateMode}
|
||||
/>
|
||||
<button
|
||||
class="btn btn-light ms-1"
|
||||
on:click|preventDefault={(e) => pickerInstance.open()}
|
||||
>
|
||||
<Icon icon="calendar"/>
|
||||
</button>
|
||||
<ul class="dropdown-menu w-100">
|
||||
{#if field.optionsFrom}
|
||||
<Datalist
|
||||
{field}
|
||||
bind:this={list}
|
||||
bind:value
|
||||
bind:search
|
||||
/>
|
||||
{/if}
|
||||
</ul>
|
||||
</div>
|
||||
{#if value}
|
||||
<span class="badge rounded-pill bg-light text-dark fs-6 mt-3">
|
||||
<div class="d-flex align-items-center ">
|
||||
{value}
|
||||
<button
|
||||
on:click|preventDefault={(e) => (value = "")}
|
||||
type="button"
|
||||
class="btn-close btn-sm ms-1"
|
||||
style="font-size:10px"
|
||||
aria-label="Close"
|
||||
/>
|
||||
</div>
|
||||
</span>
|
||||
{/if}
|
||||
{:else}
|
||||
<input
|
||||
type="text"
|
||||
{id}
|
||||
class="form-control"
|
||||
class:is-invalid={errorMessage}
|
||||
bind:value
|
||||
bind:this={pickerInput}
|
||||
autocomplete="off"
|
||||
readonly={field.readonly && !isCreateMode}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{#if errorMessage}
|
||||
<div class="invalid-feedback d-block">
|
||||
{errorMessage}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -1,121 +0,0 @@
|
||||
<script>
|
||||
import Datalist from "./Datalist.svelte";
|
||||
import {onMount} from "svelte";
|
||||
import flatpickr from "flatpickr";
|
||||
import "flatpickr/dist/flatpickr.css";
|
||||
import "flatpickr/dist/themes/light.css";
|
||||
import {getErrorMessage} from "./errorMessage";
|
||||
import Icon from "../../common/Icon.svelte";
|
||||
|
||||
export let field;
|
||||
export let value;
|
||||
export let isCreateMode;
|
||||
export let validationErrors;
|
||||
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
||||
$: search = "";
|
||||
$: listMode = field.optionsFrom && !(field.readonly && !isCreateMode);
|
||||
$: errorMessage = getErrorMessage(validationErrors, field.name);
|
||||
|
||||
export let id;
|
||||
let list;
|
||||
let pickerInput;
|
||||
let pickerInstance;
|
||||
let flatpickrOptions = {
|
||||
enableTime: false,
|
||||
allowInput: true,
|
||||
altInput: true,
|
||||
altFormat: "Y-m-d H:i:S",
|
||||
dateFormat: "Z",
|
||||
enableTime: true,
|
||||
time_24hr: true,
|
||||
enableSeconds: true,
|
||||
};
|
||||
|
||||
if (field.min) {
|
||||
flatpickrOptions.minDate = field.min;
|
||||
}
|
||||
|
||||
if (field.max) {
|
||||
flatpickrOptions.maxDate = field.max;
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
if (!field.readonly || isCreateMode) {
|
||||
if (listMode) {
|
||||
flatpickrOptions.clickOpens = false;
|
||||
}
|
||||
pickerInstance = flatpickr(pickerInput, flatpickrOptions);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="mb-0">
|
||||
{#if listMode}
|
||||
<div class="dropdown d-flex">
|
||||
<input
|
||||
type="search"
|
||||
{id}
|
||||
on:keyup={list.update}
|
||||
on:focus={list.update}
|
||||
class="form-control dropdown-toggle"
|
||||
class:is-invalid={errorMessage}
|
||||
bind:value={search}
|
||||
bind:this={pickerInput}
|
||||
placeholder="Search for options"
|
||||
data-bs-toggle="dropdown"
|
||||
autocomplete="off"
|
||||
readonly={field.readonly && !isCreateMode}
|
||||
/>
|
||||
<button
|
||||
class="btn btn-light ms-1"
|
||||
on:click|preventDefault={(e) => pickerInstance.open()}
|
||||
>
|
||||
<Icon icon="calendar"/>
|
||||
</button>
|
||||
<ul class="dropdown-menu w-100">
|
||||
{#if field.optionsFrom}
|
||||
<Datalist
|
||||
{field}
|
||||
bind:this={list}
|
||||
bind:value
|
||||
bind:search
|
||||
/>
|
||||
{/if}
|
||||
</ul>
|
||||
</div>
|
||||
{#if value}
|
||||
<span class="badge rounded-pill bg-light text-dark fs-6 mt-3">
|
||||
<div class="d-flex align-items-center ">
|
||||
{value}
|
||||
<button
|
||||
on:click|preventDefault={(e) => (value = "")}
|
||||
type="button"
|
||||
class="btn-close btn-sm ms-1"
|
||||
style="font-size:10px"
|
||||
aria-label="Close"
|
||||
/>
|
||||
</div>
|
||||
</span>
|
||||
{/if}
|
||||
{:else}
|
||||
<input
|
||||
type="text"
|
||||
{id}
|
||||
class="form-control"
|
||||
class:is-invalid={errorMessage}
|
||||
bind:value
|
||||
bind:this={pickerInput}
|
||||
autocomplete="off"
|
||||
readonly={field.readonly && !isCreateMode}
|
||||
/>
|
||||
{/if}
|
||||
<small class=" text-primary opacity-50"
|
||||
>Dates are displayed according to your timezone: {timezone}</small
|
||||
>
|
||||
|
||||
{#if errorMessage}
|
||||
<div class="invalid-feedback d-block">
|
||||
{errorMessage}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -1,22 +0,0 @@
|
||||
<script>
|
||||
export let field;
|
||||
export let id;
|
||||
</script>
|
||||
|
||||
<div class="mb-1">
|
||||
<div class="d-flex justify-content-between">
|
||||
<div>
|
||||
<label for={id} class="form-label"
|
||||
>{field.label}</label
|
||||
>
|
||||
{#if field.help}
|
||||
<small class=" text-primary opacity-50">{field.help}</small>
|
||||
{/if}
|
||||
</div>
|
||||
<span
|
||||
tabindex="-1"
|
||||
class="text-decoration-none"
|
||||
><code class="text-primary opacity-50">{field.name}</code>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,122 +0,0 @@
|
||||
<script>
|
||||
import {getContext} from "svelte";
|
||||
import {uniqBy} from "lodash";
|
||||
import {sortByField} from "../../edges/sortEdges";
|
||||
import PreviewCard from "../PreviewCard.svelte";
|
||||
import Sortable from "../../libs/Sortable.svelte";
|
||||
import BrowseModal from "./BrowseModal.svelte";
|
||||
import Preview from "../../newPreview/Preview.svelte";
|
||||
|
||||
const channel = getContext("channel");
|
||||
export let field;
|
||||
export let record;
|
||||
export let graph
|
||||
|
||||
let browseModal;
|
||||
|
||||
$: currentReferences = graph._children[field.name] ?? [];
|
||||
|
||||
let collections = channel.schemas.filter((aschema) =>
|
||||
field.collections.includes(aschema.name)
|
||||
);
|
||||
|
||||
function removeReference(e) {
|
||||
e.preventDefault();
|
||||
// graph.edges = graph.edges.filter(
|
||||
// (edge) => !(edge.target === e.detail && edge.field === field.name)
|
||||
// );
|
||||
}
|
||||
|
||||
function openBrowseModal(e, schema) {
|
||||
e.preventDefault();
|
||||
browseModal.open(schema);
|
||||
}
|
||||
|
||||
async function reorder(e) {
|
||||
// graph.edges = await sortByField(e.detail.source, e.detail.target, graph.edges, field.name);
|
||||
}
|
||||
|
||||
function insert(e) {
|
||||
e.preventDefault();
|
||||
browseModal.close();
|
||||
const recordsToInsert = e.detail.records;
|
||||
console.log({recordsToInsert})
|
||||
const action = e.detail.action;
|
||||
let newEdges = recordsToInsert.map((r) => {
|
||||
return {
|
||||
target: r.id,
|
||||
source: record.id,
|
||||
sourceSchema: record.schema,
|
||||
targetSchema: r.schema,
|
||||
field: field.name,
|
||||
rank: ""
|
||||
};
|
||||
});
|
||||
|
||||
let replacedEdges = graph.edges ?? [];
|
||||
if (action === "replace") {
|
||||
replacedEdges = replacedEdges.filter((e) => e.field !== field.name);
|
||||
}
|
||||
|
||||
// graph.records = uniqBy([...graph.records, ...recordsToInsert], (r) => r.id);
|
||||
// graph.edges = uniqBy([...replacedEdges, ...newEdges], (e) => e.target + e.field);
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="mb-0">
|
||||
{#if field.collections.length === 1}
|
||||
<button
|
||||
class="btn btn-outline-primary"
|
||||
on:click={(e) => openBrowseModal(e, collections[0].name)}
|
||||
>
|
||||
Browse
|
||||
</button>
|
||||
{:else}
|
||||
<div class="dropdown d-inline-block">
|
||||
<button
|
||||
class="btn btn-outline-primary btn-sm"
|
||||
type="button"
|
||||
data-bs-toggle="dropdown"
|
||||
aria-expanded="false"
|
||||
>
|
||||
Browse
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
{#each collections as collection}
|
||||
<li>
|
||||
<!-- {`${channelurl}/content/${collection.name}?parent=${record.id}&parentfield=${field.name}`} -->
|
||||
<a
|
||||
class="dropdown-item"
|
||||
on:click={(e) =>
|
||||
openBrowseModal(e, collection.name)}
|
||||
href="/">{collection.label}</a
|
||||
>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{#if currentReferences.length > 0}
|
||||
<Sortable sortableClass="row row-cols-3 mt-3" on:update={reorder}>
|
||||
{#each currentReferences as reference (reference.record.id)}
|
||||
<div class="col mb-3">
|
||||
<PreviewCard
|
||||
classes="h-100"
|
||||
record={reference.record}
|
||||
hasDelete={true}
|
||||
on:remove={removeReference}
|
||||
/>
|
||||
<!-- <Preview-->
|
||||
<!-- classes="h-100"-->
|
||||
<!-- record={reference.record}-->
|
||||
<!-- edge={reference.edge}-->
|
||||
<!-- hasDelete={true}-->
|
||||
<!-- {field}-->
|
||||
<!-- on:remove={removeReference}-->
|
||||
<!-- />-->
|
||||
</div>
|
||||
{/each}
|
||||
</Sortable>
|
||||
{/if}
|
||||
<BrowseModal bind:this={browseModal} on:insert={insert}/>
|
||||
@@ -1,24 +0,0 @@
|
||||
<script>
|
||||
import Codemirror from "../../libs/Codemirror.svelte";
|
||||
import { getErrorMessage } from "./errorMessage";
|
||||
|
||||
|
||||
export let value;
|
||||
export let field;
|
||||
export let isCreateMode;
|
||||
// export let id;
|
||||
export let validationErrors;
|
||||
$: errorMessage = getErrorMessage(validationErrors, field.name);
|
||||
|
||||
</script>
|
||||
|
||||
<div class="mb-3">
|
||||
|
||||
<Codemirror bind:value editable={!field.readonly || isCreateMode} />
|
||||
|
||||
{#if errorMessage}
|
||||
<div class="invalid-feedback d-block">
|
||||
{errorMessage}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -1,24 +0,0 @@
|
||||
<script>
|
||||
import Codemirror from "../../libs/CodemirrorMarkdown.svelte";
|
||||
import { getErrorMessage } from "./errorMessage";
|
||||
|
||||
|
||||
export let value;
|
||||
export let field;
|
||||
export let isCreateMode;
|
||||
// export let id;
|
||||
export let validationErrors;
|
||||
$: errorMessage = getErrorMessage(validationErrors, field.name);
|
||||
|
||||
</script>
|
||||
|
||||
<div class="mb-3">
|
||||
|
||||
<Codemirror bind:value editable={!field.readonly || isCreateMode} />
|
||||
|
||||
{#if errorMessage}
|
||||
<div class="invalid-feedback d-block">
|
||||
{errorMessage}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -1,92 +0,0 @@
|
||||
<script>
|
||||
import Datalist from "./Datalist.svelte";
|
||||
import {getErrorMessage} from "./errorMessage";
|
||||
|
||||
export let field;
|
||||
export let value;
|
||||
export let schemas;
|
||||
export let validationErrors;
|
||||
export let isCreateMode;
|
||||
export let id;
|
||||
$: search = "";
|
||||
$: errorMessage = getErrorMessage(validationErrors, field.name);
|
||||
|
||||
let list;
|
||||
|
||||
function fixDecimals(e) {
|
||||
const number = e.currentTarget.value;
|
||||
const formattedNumber = formatNumber(number);
|
||||
value = isNaN(formattedNumber) ? null : formattedNumber;
|
||||
}
|
||||
|
||||
function formatNumber(number) {
|
||||
return parseFloat(number).toFixed(field.decimals);
|
||||
}
|
||||
|
||||
$: listMode = field.optionsFrom && !(field.readonly && !isCreateMode);
|
||||
</script>
|
||||
|
||||
<div class="mb-0">
|
||||
|
||||
{#if listMode}
|
||||
<div class="dropdown">
|
||||
<input
|
||||
type="number"
|
||||
{id}
|
||||
on:keyup={list.update}
|
||||
on:focus={list.update}
|
||||
bind:value={search}
|
||||
placeholder="Search for options"
|
||||
class="form-control dropdown-toggle"
|
||||
class:is-invalid={errorMessage}
|
||||
data-bs-toggle="dropdown"
|
||||
autocomplete="off"
|
||||
readonly={field.readonly && !isCreateMode}
|
||||
/>
|
||||
|
||||
<ul class="dropdown-menu w-100">
|
||||
{#if field.optionsFrom}
|
||||
<Datalist
|
||||
{field}
|
||||
bind:this={list}
|
||||
{schemas}
|
||||
bind:value
|
||||
bind:search
|
||||
/>
|
||||
{/if}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{#if value}
|
||||
<span class="badge rounded-pill bg-light text-dark fs-6 mt-3">
|
||||
<div class="d-flex align-items-center ">
|
||||
{value}
|
||||
<button
|
||||
on:click|preventDefault={(e) => (value = "")}
|
||||
type="button"
|
||||
class="btn-close btn-sm ms-1"
|
||||
style="font-size:10px"
|
||||
aria-label="Close"
|
||||
/>
|
||||
</div>
|
||||
</span>
|
||||
{/if}
|
||||
{:else}
|
||||
<input
|
||||
type="number"
|
||||
{id}
|
||||
class="form-control"
|
||||
class:is-invalid={errorMessage}
|
||||
on:change={fixDecimals}
|
||||
bind:value
|
||||
autocomplete="off"
|
||||
readonly={field.readonly && !isCreateMode}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{#if errorMessage}
|
||||
<div class="invalid-feedback d-block">
|
||||
{errorMessage}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -1,8 +1,8 @@
|
||||
<script>
|
||||
import {getContext} from "svelte";
|
||||
import {insertEdges} from "./reference";
|
||||
import {insertEdges} from "../form/references/reference.js";
|
||||
import PreviewCard from "../PreviewCard.svelte";
|
||||
import {getErrorMessage} from "./errorMessage";
|
||||
import {getErrorMessage} from "../form/errorMessage.js";
|
||||
import {sortByField} from "../../edges/sortEdges";
|
||||
import ReferenceInlineButtons from "./ReferenceInlineButtons.svelte";
|
||||
import Sortable from "../../libs/Sortable.svelte";
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import {getContext} from "svelte";
|
||||
import {uniqBy} from "lodash";
|
||||
import PreviewCardInline from "../PreviewCardInline.svelte";
|
||||
import {getErrorMessage} from "./errorMessage";
|
||||
import {getErrorMessage} from "../form/errorMessage.js";
|
||||
import {sortByField} from "../../edges/sortEdges";
|
||||
import ReferenceInlineButtons from "./ReferenceInlineButtons.svelte";
|
||||
import {flip} from "svelte/animate";
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import {createEventDispatcher, getContext} from "svelte";
|
||||
import Icon from "../../common/Icon.svelte";
|
||||
import InlineEdit from "../InlineEdit.svelte";
|
||||
import BrowseModal from "./BrowseModal.svelte";
|
||||
import BrowseModal from "../form/references/BrowseModal.svelte";
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
// export let field;
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<script>
|
||||
import {getContext} from "svelte";
|
||||
import {previewTitle} from "../Preview";
|
||||
import {getErrorMessage} from "./errorMessage";
|
||||
import {getErrorMessage} from "../form/errorMessage.js";
|
||||
import {sortByField} from "../../edges/sortEdges";
|
||||
import ReferenceInlineButtons from "./ReferenceInlineButtons.svelte";
|
||||
import Sortable from "../../libs/Sortable.svelte";
|
||||
import RenderField from "../../content/RenderField.svelte";
|
||||
import Icon from "../../common/Icon.svelte";
|
||||
import {insertEdges} from "./reference.js";
|
||||
import {insertEdges} from "../form/references/reference.js";
|
||||
|
||||
const channel = getContext("channel");
|
||||
export let field;
|
||||
|
||||
@@ -1,161 +0,0 @@
|
||||
<script>
|
||||
import {getContext} from "svelte";
|
||||
import {uniqBy, debounce} from "lodash";
|
||||
import {previewTitle} from "../Preview";
|
||||
import {getErrorMessage} from "./errorMessage";
|
||||
import {sortByField} from "../../edges/sortEdges";
|
||||
import ReferenceInlineButtons from "./ReferenceInlineButtons.svelte";
|
||||
import Sortable from "../../libs/Sortable.svelte";
|
||||
import RenderField from "../../content/RenderField.svelte";
|
||||
import Icon from "../../common/Icon.svelte";
|
||||
import Datalist from "./Datalist.svelte";
|
||||
import {insertEdges} from "./reference.js";
|
||||
|
||||
const channel = getContext("channel");
|
||||
export let field;
|
||||
export let id;
|
||||
export let record;
|
||||
export let graph;
|
||||
|
||||
export let validationErrors;
|
||||
$: errorMessage = getErrorMessage(validationErrors, field.name);
|
||||
|
||||
$: references = graph.edges
|
||||
.filter((edge) => edge.field === field.name)
|
||||
.map((edge) => {
|
||||
return graph.records.find((increc) => increc.id == edge.target && record.id == edge.source);
|
||||
}).filter((rec) => (rec?.id ? true : false)) ?? [];
|
||||
|
||||
let search = ""
|
||||
$: searchOptions = []
|
||||
|
||||
|
||||
function removeReference(e, recordId) {
|
||||
e.preventDefault();
|
||||
graph.edges = graph.edges.filter(
|
||||
(edge) => !(edge.target === recordId && edge.field === field.name)
|
||||
);
|
||||
}
|
||||
|
||||
function saveNew(e, newValue) {
|
||||
e.preventDefault();
|
||||
axios
|
||||
.post(channel.lucentUrl + "/records", {
|
||||
isCreateMode: true,
|
||||
record: {
|
||||
schema: field.collections[0],
|
||||
status: "published",
|
||||
data: {
|
||||
[field.searchField]: newValue
|
||||
}
|
||||
},
|
||||
})
|
||||
.then((response) => {
|
||||
searchOptions = [];
|
||||
insert(e,response.data.records[0]);
|
||||
console.log(response)
|
||||
})
|
||||
.catch((error) => {
|
||||
searchOptions = [];
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
|
||||
function insert(e, insertRecord) {
|
||||
e.preventDefault();
|
||||
graph = insertEdges(graph,record,[insertRecord],field.name,e.detail.action);
|
||||
}
|
||||
|
||||
const updateResults = debounce((e) => {
|
||||
axios
|
||||
.get(channel.lucentUrl + "/records/suggestions", {
|
||||
params: {
|
||||
schema: field.collections[0],
|
||||
field: field.searchField,
|
||||
value: search,
|
||||
ui: "text",
|
||||
},
|
||||
})
|
||||
.then((response) => {
|
||||
searchOptions = response.data;
|
||||
})
|
||||
.catch((error) => {
|
||||
searchOptions = [];
|
||||
console.log(error);
|
||||
});
|
||||
}, 500);
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
{#if errorMessage}
|
||||
<div class="invalid-feedback d-block mb-3">
|
||||
{errorMessage}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<input
|
||||
type="search"
|
||||
{id}
|
||||
on:keyup={updateResults}
|
||||
class="form-control dropdown-toggle"
|
||||
class:is-invalid={errorMessage}
|
||||
bind:value={search}
|
||||
placeholder={"Search for "+field.label}
|
||||
data-bs-toggle="dropdown"
|
||||
autocomplete="off"
|
||||
readonly={field.readonly && !isCreateMode}
|
||||
/>
|
||||
|
||||
<div class="dropdown-menu w-100">
|
||||
|
||||
{#if searchOptions}
|
||||
{#each searchOptions as option (option.id)}
|
||||
<div
|
||||
on:click={(e) => insert(e, option)}
|
||||
on:keypress={(e) => insert(e, option)}
|
||||
>
|
||||
<span class="dropdown-item">
|
||||
{previewTitle(channel.schemas, option)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{:else}
|
||||
|
||||
Start typing...
|
||||
{/each}
|
||||
{/if}
|
||||
{#if search }
|
||||
<div
|
||||
on:click={(e) => saveNew(e,search)}
|
||||
on:keypress={(e) => saveNew(e,search)}
|
||||
>
|
||||
<span class="dropdown-item">
|
||||
Add "{search}"
|
||||
</span>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
||||
{#if references.length > 0}
|
||||
<div class="d-flex">
|
||||
{#each references as record (record.id)}
|
||||
<span class="badge rounded-pill bg-light text-dark fs-6 mt-3">
|
||||
<div class="d-flex align-items-center ">
|
||||
{previewTitle(channel.schemas, record)}
|
||||
<button
|
||||
on:click|preventDefault={(e) => removeReference(e, record.id)}
|
||||
type="button"
|
||||
class="btn-close btn-sm ms-1"
|
||||
style="font-size:10px"
|
||||
aria-label="Close"
|
||||
/>
|
||||
</div>
|
||||
</span>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
<script>
|
||||
import Tinymce from "../../libs/Tinymce.svelte";
|
||||
import {getErrorMessage} from "./errorMessage";
|
||||
|
||||
export let value;
|
||||
export let field;
|
||||
export let isCreateMode;
|
||||
export let schema;
|
||||
export let validationErrors;
|
||||
$: errorMessage = getErrorMessage(validationErrors, field.name);
|
||||
let additionalConfig = {
|
||||
readonly: field.readonly && !isCreateMode,
|
||||
};
|
||||
</script>
|
||||
|
||||
<div class="mb-0">
|
||||
<Tinymce bind:value {additionalConfig} {schema}/>
|
||||
|
||||
{#if errorMessage}
|
||||
<div class="invalid-feedback d-block">
|
||||
{errorMessage}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -1,39 +0,0 @@
|
||||
<script>
|
||||
export let field;
|
||||
export let value;
|
||||
export let search;
|
||||
|
||||
function select(e, option) {
|
||||
e.preventDefault();
|
||||
value = option;
|
||||
search = "";
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
{#if field.selectOptions}
|
||||
{#if Array.isArray(field.selectOptions)}
|
||||
{#each field.selectOptions as suggestion (suggestion)}
|
||||
<div
|
||||
on:click={(e) => select(e, suggestion)}
|
||||
on:keypress={(e) => select(e, suggestion)}
|
||||
>
|
||||
<span class="dropdown-item">
|
||||
{suggestion}
|
||||
</span>
|
||||
</div>
|
||||
{/each}
|
||||
{:else}
|
||||
{#each Object.entries(field.selectOptions) as [k, v] (k)}
|
||||
<div
|
||||
on:click={(e) => select(e, k)}
|
||||
on:keypress={(e) => select(e, k)}
|
||||
>
|
||||
<span class="dropdown-item">
|
||||
{v}
|
||||
</span>
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
{/if}
|
||||
@@ -1,30 +0,0 @@
|
||||
<script>
|
||||
import { getErrorMessage } from "./errorMessage";
|
||||
export let field;
|
||||
export let value;
|
||||
export let isCreateMode;
|
||||
export let validationErrors;
|
||||
let thisEl;
|
||||
$: errorMessage = getErrorMessage(validationErrors, field.name);
|
||||
export let id;
|
||||
|
||||
</script>
|
||||
|
||||
<div class="mb-0">
|
||||
<input
|
||||
type="text"
|
||||
{id}
|
||||
class="form-control"
|
||||
class:is-invalid={errorMessage}
|
||||
bind:value
|
||||
autocomplete="off"
|
||||
readonly={field.readonly && !isCreateMode}
|
||||
/>
|
||||
|
||||
{#if errorMessage}
|
||||
<div class="invalid-feedback d-block">
|
||||
{errorMessage}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -1,122 +0,0 @@
|
||||
<script>
|
||||
import Datalist from "./Datalist.svelte";
|
||||
import Selectlist from "./Selectlist.svelte";
|
||||
import {getErrorMessage} from "./errorMessage";
|
||||
|
||||
export let field;
|
||||
export let value;
|
||||
export let isCreateMode;
|
||||
export let validationErrors;
|
||||
|
||||
$: errorMessage = getErrorMessage(validationErrors, field.name);
|
||||
$: search = "";
|
||||
export let id;
|
||||
let list;
|
||||
|
||||
$: listMode = field.optionsFrom && !(field.readonly && !isCreateMode);
|
||||
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<div class="mb-0">
|
||||
{#if listMode}
|
||||
<div class="dropdown">
|
||||
<input
|
||||
type="search"
|
||||
{id}
|
||||
on:keyup={list.update}
|
||||
on:focus={list.update}
|
||||
class="form-control dropdown-toggle"
|
||||
class:is-invalid={errorMessage}
|
||||
bind:value={search}
|
||||
placeholder="Search for options"
|
||||
data-bs-toggle="dropdown"
|
||||
autocomplete="off"
|
||||
readonly={field.readonly && !isCreateMode}
|
||||
/>
|
||||
|
||||
<div class="dropdown-menu w-100">
|
||||
{#if field.optionsFrom}
|
||||
<Datalist
|
||||
{field}
|
||||
bind:this={list}
|
||||
bind:value
|
||||
bind:search
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{#if value}
|
||||
<span class="badge rounded-pill bg-light text-dark fs-6 mt-3">
|
||||
<div class="d-flex align-items-center ">
|
||||
{value}
|
||||
<button
|
||||
on:click|preventDefault={(e) => (value = "")}
|
||||
type="button"
|
||||
class="btn-close btn-sm ms-1"
|
||||
style="font-size:10px"
|
||||
aria-label="Close"
|
||||
/>
|
||||
</div>
|
||||
</span>
|
||||
{/if}
|
||||
{:else if field.selectOptions}
|
||||
<div class="dropdown">
|
||||
<input
|
||||
type="search"
|
||||
{id}
|
||||
class="form-control dropdown-toggle"
|
||||
class:is-invalid={errorMessage}
|
||||
bind:value={search}
|
||||
placeholder="Search for options"
|
||||
data-bs-toggle="dropdown"
|
||||
autocomplete="off"
|
||||
readonly={field.readonly && !isCreateMode}
|
||||
/>
|
||||
<div class="dropdown-menu w-100">
|
||||
<Selectlist
|
||||
{field}
|
||||
bind:value
|
||||
bind:search
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{#if value}
|
||||
<span class="badge rounded-pill bg-light text-dark fs-6 mt-3">
|
||||
<div class="d-flex align-items-center ">
|
||||
{#if Array.isArray(field.selectOptions)}
|
||||
{value}
|
||||
{:else}
|
||||
{field.selectOptions[value]}
|
||||
{/if}
|
||||
|
||||
<button
|
||||
on:click|preventDefault={(e) => (value = "")}
|
||||
type="button"
|
||||
class="btn-close btn-sm ms-1"
|
||||
style="font-size:10px"
|
||||
aria-label="Close"
|
||||
/>
|
||||
</div>
|
||||
</span>
|
||||
{/if}
|
||||
{:else}
|
||||
<input
|
||||
type="text"
|
||||
{id}
|
||||
class="form-control"
|
||||
class:is-invalid={errorMessage}
|
||||
bind:value
|
||||
autocomplete="off"
|
||||
readonly={field.readonly && !isCreateMode}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{#if errorMessage}
|
||||
<div class="invalid-feedback d-block">
|
||||
{errorMessage}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -1,53 +0,0 @@
|
||||
<script>
|
||||
import { onMount } from "svelte";
|
||||
import { getErrorMessage } from "./errorMessage";
|
||||
export let field;
|
||||
export let value;
|
||||
export let isCreateMode;
|
||||
export let validationErrors;
|
||||
let thisEl;
|
||||
$: errorMessage = getErrorMessage(validationErrors, field.name);
|
||||
export let id;
|
||||
|
||||
function resize(e) {
|
||||
let el;
|
||||
if (e.target) {
|
||||
el = e.target;
|
||||
} else {
|
||||
el = e;
|
||||
}
|
||||
el.style.overflow = "hidden";
|
||||
el.style.height = "1px";
|
||||
el.style.height = +el.scrollHeight + "px";
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
resize(thisEl);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="mb-0">
|
||||
<textarea
|
||||
bind:value
|
||||
bind:this={thisEl}
|
||||
{id}
|
||||
class="form-control"
|
||||
on:input={resize}
|
||||
on:focus={resize}
|
||||
rows="2"
|
||||
class:is-invalid={errorMessage}
|
||||
readonly={field.readonly && !isCreateMode}
|
||||
/>
|
||||
|
||||
{#if errorMessage}
|
||||
<div class="invalid-feedback d-block">
|
||||
{errorMessage}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
textarea {
|
||||
resize: none;
|
||||
}
|
||||
</style>
|
||||
@@ -1,49 +0,0 @@
|
||||
<script>
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { getContext } from "svelte";
|
||||
import Icon from "../../common/Icon.svelte";
|
||||
import { getErrorMessage } from "./errorMessage";
|
||||
const channelurl = getContext("channelurl");
|
||||
export let validationErrors;
|
||||
$: errorMessage = getErrorMessage(validationErrors, field.name);
|
||||
export let field;
|
||||
export let value;
|
||||
export let id;
|
||||
export let isCreateMode;
|
||||
let readonly = field.readonly && !isCreateMode;
|
||||
|
||||
function generateId(e) {
|
||||
e.preventDefault();
|
||||
value = uuidv4();
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="mb-0">
|
||||
|
||||
<div class="d-flex justify-content-between">
|
||||
<input
|
||||
type="text"
|
||||
{id}
|
||||
class="form-control"
|
||||
class:is-invalid={errorMessage}
|
||||
bind:value
|
||||
autocomplete="off"
|
||||
{readonly}
|
||||
/>
|
||||
{#if !readonly}
|
||||
<button
|
||||
class="btn btn-primary ms-2"
|
||||
title="Generate a new UUIDv4"
|
||||
on:click={generateId}
|
||||
>
|
||||
<Icon icon="dice" />
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if errorMessage}
|
||||
<div class="invalid-feedback d-block">
|
||||
{errorMessage}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -1,30 +0,0 @@
|
||||
<script>
|
||||
import { uniqueId } from "lodash";
|
||||
import { getContext } from "svelte";
|
||||
const channelurl = getContext("channelurl");
|
||||
export let field;
|
||||
export let value;
|
||||
export let schema;
|
||||
let id = uniqueId();
|
||||
</script>
|
||||
|
||||
<div class="mb-0">
|
||||
<div class="d-flex justify-content-between">
|
||||
<label for={id} class="form-label">{field.label}</label>
|
||||
<a
|
||||
class="text-decoration-none"
|
||||
href="{channelurl}/schemas/{schema.name}/fields/edit/{field.name}"
|
||||
><code class="text-primary opacity-50">{field.name}</code></a
|
||||
>
|
||||
</div>
|
||||
<input
|
||||
type="url"
|
||||
{id}
|
||||
class="form-control"
|
||||
bind:value
|
||||
placeholder="https://www.example.com"
|
||||
/>
|
||||
{#if field.help}
|
||||
<small class=" text-primary opacity-50">{field.help}</small>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -1,3 +0,0 @@
|
||||
export function getErrorMessage(validationErrors, fieldName) {
|
||||
return validationErrors?.find((e) => e.fieldName === fieldName)?.message;
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
import {uniqBy} from "lodash";
|
||||
|
||||
export function insertEdges(graph, sourceRecord, targetRecords, fieldName, action = "") {
|
||||
let newEdges = targetRecords.map((r) => {
|
||||
return {
|
||||
target: r.id,
|
||||
source: sourceRecord.id,
|
||||
sourceSchema: sourceRecord.schema,
|
||||
targetSchema: r.schema,
|
||||
field: fieldName,
|
||||
depth: 1,
|
||||
rank: ""
|
||||
};
|
||||
});
|
||||
|
||||
let replacedEdges = graph.edges;
|
||||
if (action === "replace") {
|
||||
replacedEdges = replacedEdges.filter((edge) => edge.field !== field.name);
|
||||
}
|
||||
|
||||
graph.records = uniqBy([...graph.records, ...targetRecords], (r) => r.id);
|
||||
graph.edges = uniqBy([...replacedEdges, ...newEdges], (edge) => edge.source + edge.target + edge.field + edge.depth);
|
||||
return graph;
|
||||
}
|
||||
Reference in New Issue
Block a user