file uploads
This commit is contained in:
@@ -1,68 +0,0 @@
|
||||
<script>
|
||||
import {getContext} from "svelte";
|
||||
import Preview from "../files/Preview.svelte";
|
||||
import {selectRecord} from "./functions/recordSelect.js";
|
||||
|
||||
const channel = getContext("channel");
|
||||
|
||||
export let schema;
|
||||
export let records;
|
||||
export let isWritable;
|
||||
export let selected = [];
|
||||
|
||||
function select(record) {
|
||||
selected = selectRecord(record, selected)
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
<div class="row" style="max-width:1000px">
|
||||
{#each records as record (record.id)}
|
||||
<div class="col-6 col-md-4">
|
||||
<div
|
||||
class="file-wrapper rounded p-2 mb-4 bg-light"
|
||||
class:selected={selected.includes(record)}
|
||||
>
|
||||
{#if isWritable}
|
||||
<div class="form-check">
|
||||
<input
|
||||
on:change={() => select(record)}
|
||||
class="form-check-input "
|
||||
type="checkbox"
|
||||
checked={selected.find(
|
||||
(r) => r.id === record.id
|
||||
)}
|
||||
value={record}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="d-flex justify-content-center">
|
||||
<Preview {record} size="medium"/>
|
||||
</div>
|
||||
|
||||
<a
|
||||
href="{channel.lucentUrl}/records/{record.id}"
|
||||
title={record._file.path}
|
||||
class="d-block text-center overflow-hidden text-nowrap my-2 "
|
||||
style="
|
||||
text-overflow: ellipsis;
|
||||
font-size: 13px;
|
||||
color: #333;
|
||||
">{record._file.path}</a
|
||||
>
|
||||
<span
|
||||
class="lx-small-text text-muted d-block text-center"
|
||||
>{record._file.mime}</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
|
||||
<style>
|
||||
.form-check {
|
||||
display: inline-block;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
</style>
|
||||
@@ -3,7 +3,7 @@
|
||||
import Pagination from "./pagination/Pagination.svelte";
|
||||
import ActionsOnSelected from "./ActionsOnSelected.svelte";
|
||||
import Table from "./Table.svelte";
|
||||
import {getContext} from "svelte";
|
||||
import { getContext } from "svelte";
|
||||
|
||||
const axios = getContext("axios");
|
||||
export let schema;
|
||||
@@ -47,51 +47,49 @@
|
||||
</script>
|
||||
|
||||
<div class="">
|
||||
<div class="{inModal ? 'mt-0' : 'mt-5'}">
|
||||
<h3 class="header-normal mb-5 ">
|
||||
<div class={inModal ? "mt-0" : "mt-5"}>
|
||||
<h3 class="header-normal mb-5">
|
||||
{schema.label}
|
||||
</h3>
|
||||
{#if selected.length > 0 && !inModal && isWritable}
|
||||
<ActionsOnSelected {schema} {selected} {filter}/>
|
||||
<ActionsOnSelected {schema} {selected} {filter} />
|
||||
{:else}
|
||||
<Tools
|
||||
bind:schema
|
||||
bind:records
|
||||
{systemFields}
|
||||
{sortParam}
|
||||
{sortField}
|
||||
{operators}
|
||||
{filter}
|
||||
{graph}
|
||||
{inModal}
|
||||
{modalUrl}
|
||||
{isWritable}
|
||||
on:refresh={refresh}
|
||||
bind:schema
|
||||
bind:records
|
||||
{systemFields}
|
||||
{sortParam}
|
||||
{sortField}
|
||||
{operators}
|
||||
{filter}
|
||||
{graph}
|
||||
{inModal}
|
||||
{modalUrl}
|
||||
{isWritable}
|
||||
on:refresh={refresh}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<Table
|
||||
{records}
|
||||
{graph}
|
||||
{schema}
|
||||
{sortParam}
|
||||
{sortField}
|
||||
{systemFields}
|
||||
{inModal}
|
||||
{users}
|
||||
{isWritable}
|
||||
bind:selected
|
||||
{records}
|
||||
{graph}
|
||||
{schema}
|
||||
{sortParam}
|
||||
{sortField}
|
||||
{systemFields}
|
||||
{inModal}
|
||||
{users}
|
||||
{isWritable}
|
||||
bind:selected
|
||||
/>
|
||||
|
||||
</div>
|
||||
|
||||
<Pagination
|
||||
{limit}
|
||||
{skip}
|
||||
{total}
|
||||
on:refresh={refresh}
|
||||
{inModal}
|
||||
{modalUrl}
|
||||
{limit}
|
||||
{skip}
|
||||
{total}
|
||||
on:refresh={refresh}
|
||||
{inModal}
|
||||
{modalUrl}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
import RenderField from "./RenderField.svelte";
|
||||
import Avatar from "../account/Avatar.svelte";
|
||||
import Status from "../records/Status.svelte";
|
||||
import {usernameById} from "../account/users";
|
||||
import {friendlyDate} from "../../helpers";
|
||||
import { usernameById } from "../account/users";
|
||||
import { friendlyDate } from "../../helpers";
|
||||
|
||||
export let schema;
|
||||
export let users;
|
||||
@@ -12,7 +12,6 @@
|
||||
export let sortParam;
|
||||
export let sortField;
|
||||
export let visibleColumns;
|
||||
|
||||
</script>
|
||||
|
||||
{#each visibleColumns as field, index}
|
||||
@@ -20,7 +19,7 @@
|
||||
class="field-ui-{field.info.name}"
|
||||
class:is-sort={field.name === sortField.name}
|
||||
>
|
||||
<RenderField {record} {schema} {graph} {field}/>
|
||||
<RenderField {record} {schema} {graph} {field} />
|
||||
</td>
|
||||
{/each}
|
||||
{#if schema.visible?.includes("status")}
|
||||
@@ -28,32 +27,40 @@
|
||||
class="text-center"
|
||||
class:is-sort={"-status" == sortParam || "status" == sortParam}
|
||||
>
|
||||
<Status status={record.status}/>
|
||||
<Status status={record.status} />
|
||||
</td>
|
||||
{/if}
|
||||
{#if schema.visible?.includes("_sys.createdBy")}
|
||||
<td
|
||||
class="text-center"
|
||||
class:is-sort={"-_sys.createdBy" == sortParam || "_sys.createdBy" == sortParam}
|
||||
class:is-sort={"-_sys.createdBy" == sortParam ||
|
||||
"_sys.createdBy" == sortParam}
|
||||
>
|
||||
<Avatar name={usernameById(users, record._sys.createdBy)} side={24}/>
|
||||
<Avatar name={usernameById(users, record.createdBy)} side={24} />
|
||||
</td>
|
||||
{/if}
|
||||
{#if schema.visible?.includes("_sys.updatedBy")}
|
||||
<td
|
||||
class="text-center"
|
||||
class:is-sort={"-_sys.updatedBy" == sortParam || "_sys.updatedBy" == sortParam}
|
||||
class:is-sort={"-_sys.updatedBy" == sortParam ||
|
||||
"_sys.updatedBy" == sortParam}
|
||||
>
|
||||
<Avatar name={usernameById(users, record._sys.updatedBy)} side={24}/>
|
||||
<Avatar name={usernameById(users, record.updatedBy)} side={24} />
|
||||
</td>
|
||||
{/if}
|
||||
{#if schema.visible?.includes("_sys.createdAt")}
|
||||
<td class:is-sort={"-_sys.createdAt" == sortParam || "_sys.createdAt" == sortParam}>
|
||||
{friendlyDate(record._sys.createdAt)}
|
||||
<td
|
||||
class:is-sort={"-_sys.createdAt" == sortParam ||
|
||||
"_sys.createdAt" == sortParam}
|
||||
>
|
||||
{friendlyDate(record.createdAt)}
|
||||
</td>
|
||||
{/if}
|
||||
{#if schema.visible?.includes("_sys.updatedAt")}
|
||||
<td class:is-sort={"-_sys.updatedAt" == sortParam || "_sys.updatedAt" == sortParam}>
|
||||
{friendlyDate(record._sys.updatedAt)}
|
||||
<td
|
||||
class:is-sort={"-_sys.updatedAt" == sortParam ||
|
||||
"_sys.updatedAt" == sortParam}
|
||||
>
|
||||
{friendlyDate(record.updatedAt)}
|
||||
</td>
|
||||
{/if}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<script>
|
||||
import RecordRow from "./RecordRow.svelte";
|
||||
import {previewTitle} from "../records/Preview";
|
||||
import {usernameById} from "../account/users";
|
||||
import {getContext} from "svelte";
|
||||
import { previewTitle } from "../records/Preview";
|
||||
import { usernameById } from "../account/users";
|
||||
import { getContext } from "svelte";
|
||||
import Avatar from "../account/Avatar.svelte";
|
||||
import {selectRecord, toggleAll} from "./functions/recordSelect.js";
|
||||
import { selectRecord, toggleAll } from "./functions/recordSelect.js";
|
||||
import Checkbox from "../common/Checkbox.svelte";
|
||||
import Preview from "../files/Preview.svelte";
|
||||
import {fileurl} from "../files/imageserver.js";
|
||||
import { fileurl } from "../files/imageserver.js";
|
||||
|
||||
const channel = getContext("channel");
|
||||
|
||||
@@ -23,107 +23,80 @@
|
||||
export let selected = [];
|
||||
|
||||
function eventToggleAll(e) {
|
||||
selected = toggleAll(e, records, selected)
|
||||
selected = toggleAll(e, records, selected);
|
||||
}
|
||||
|
||||
function select(record) {
|
||||
selected = selectRecord(record, selected)
|
||||
selected = selectRecord(record, selected);
|
||||
}
|
||||
|
||||
$: visibleColumns = schema.fields.filter(c => schema.visible?.includes(c.name) ?? [])
|
||||
$: visibleColumns = schema.fields.filter(
|
||||
(c) => schema.visible?.includes(c.name) ?? [],
|
||||
);
|
||||
</script>
|
||||
|
||||
<div class="table mt-5 ">
|
||||
<div class="table mt-5">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
{#if isWritable}
|
||||
<th>
|
||||
<Checkbox
|
||||
<tr>
|
||||
{#if isWritable}
|
||||
<th>
|
||||
<Checkbox
|
||||
value=""
|
||||
on:change={eventToggleAll}
|
||||
indeterminate={selected.length > 0 && selected.length < records.length}
|
||||
indeterminate={selected.length > 0 &&
|
||||
selected.length < records.length}
|
||||
checked={selected.length === records.length}
|
||||
>
|
||||
</Checkbox>
|
||||
</th>
|
||||
{/if}
|
||||
></Checkbox>
|
||||
</th>
|
||||
{/if}
|
||||
|
||||
{#each visibleColumns as field}
|
||||
<th
|
||||
{#each visibleColumns as field}
|
||||
<th
|
||||
class="field-ui-{field.info.name ?? field.ui}"
|
||||
class:is-sort={field.name === sortField.name}
|
||||
scope="col"
|
||||
title={field.help}
|
||||
>{field.label}</th
|
||||
>
|
||||
{/each}
|
||||
{#each systemFields.filter(c => schema.visible?.includes(c.name)) as sysField}
|
||||
<th class:is-sort={sysField.name === sortField.name}>{sysField.label}</th>
|
||||
{/each}
|
||||
<th></th>
|
||||
</tr>
|
||||
title={field.help}>{field.label}</th
|
||||
>
|
||||
{/each}
|
||||
{#each systemFields.filter( (c) => schema.visible?.includes(c.name), ) as sysField}
|
||||
<th class:is-sort={sysField.name === sortField.name}
|
||||
>{sysField.label}</th
|
||||
>
|
||||
{/each}
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each records as record (record.id)}
|
||||
<tr>
|
||||
<td class="title-td">
|
||||
<div
|
||||
class="title-td-contents"
|
||||
>
|
||||
{#if isWritable}
|
||||
<Checkbox
|
||||
{#each records as record (record.id)}
|
||||
<tr>
|
||||
<td class="title-td">
|
||||
<div class="title-td-contents">
|
||||
{#if isWritable}
|
||||
<Checkbox
|
||||
on:change={() => select(record)}
|
||||
checked={selected.find((r) => r.id === record.id)}
|
||||
checked={selected.find(
|
||||
(r) => r.id === record.id,
|
||||
)}
|
||||
value={record}
|
||||
>
|
||||
</Checkbox>
|
||||
></Checkbox>
|
||||
{/if}
|
||||
|
||||
{/if}
|
||||
{#if record._file?.path}
|
||||
<div class="file-table-row">
|
||||
<Preview record={record} size={record._file?.width > 0 ? "medium" : "small"}/>
|
||||
|
||||
<div>
|
||||
{#if record.status === "draft"}
|
||||
<span style="text-transform: uppercase;font-size:10px">{record.status}</span>
|
||||
{/if}
|
||||
<a
|
||||
href="{channel.lucentUrl}/records/{record.id}"
|
||||
target={inModal ? "_blank" : "_self"}
|
||||
>
|
||||
{previewTitle(channel.schemas, record, graph)}
|
||||
</a>
|
||||
<span>{(record._file.size / 1024).toFixed(1)}kB</span>
|
||||
|
||||
{#if record._file.width > 0}
|
||||
<span>{record._file.width + "x" + record._file.height}</span>
|
||||
{/if}
|
||||
<a
|
||||
href="{fileurl(channel,record)}"
|
||||
target="_blank"
|
||||
>
|
||||
Download
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{:else}
|
||||
<a
|
||||
href="{channel.lucentUrl}/records/{record.id}"
|
||||
target={inModal ? "_blank" : "_self"}
|
||||
href="{channel.lucentUrl}/records/{record.id}"
|
||||
target={inModal ? "_blank" : "_self"}
|
||||
>
|
||||
{#if record.status === "draft"}
|
||||
<span style="text-transform: uppercase;font-size:10px">{record.status}</span>
|
||||
<span
|
||||
style="text-transform: uppercase;font-size:10px"
|
||||
>{record.status}</span
|
||||
>
|
||||
{/if}
|
||||
{previewTitle(channel.schemas, record, graph)}
|
||||
</a>
|
||||
{/if}
|
||||
|
||||
|
||||
</div>
|
||||
</td>
|
||||
<RecordRow
|
||||
</div>
|
||||
</td>
|
||||
<RecordRow
|
||||
{record}
|
||||
{graph}
|
||||
{schema}
|
||||
@@ -131,19 +104,15 @@
|
||||
{sortParam}
|
||||
{sortField}
|
||||
{users}
|
||||
/>
|
||||
<td>
|
||||
<Avatar
|
||||
name={usernameById(
|
||||
users,
|
||||
record._sys.updatedBy
|
||||
)}
|
||||
side={24}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
<td>
|
||||
<Avatar
|
||||
name={usernameById(users, record.updatedBy)}
|
||||
side={24}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<script>
|
||||
import FilterFields from "./FilterFields.svelte";
|
||||
import Uploader from "../../files/Uploader.svelte";
|
||||
import Icon from "../../common/Icon.svelte";
|
||||
import SortFields from "./SortFields.svelte";
|
||||
import AppliedFilter from "./AppliedFilter.svelte";
|
||||
|
||||
@@ -33,18 +33,12 @@
|
||||
|
||||
function insert(e) {
|
||||
e.preventDefault();
|
||||
dispatch("insert", {
|
||||
records: selectedRecords,
|
||||
action: "insert",
|
||||
});
|
||||
dispatch("insert_files", selectedRecords);
|
||||
}
|
||||
|
||||
function replace(e) {
|
||||
e.preventDefault();
|
||||
dispatch("insert", {
|
||||
records: selectedRecords,
|
||||
action: "replace",
|
||||
});
|
||||
dispatch("replace_files", selectedRecords);
|
||||
}
|
||||
|
||||
export function open(recordId) {
|
||||
|
||||
@@ -97,21 +97,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<!-- <RecordRow
|
||||
{record}
|
||||
{graph}
|
||||
{schema}
|
||||
{visibleColumns}
|
||||
{sortParam}
|
||||
{sortField}
|
||||
{users}
|
||||
/> -->
|
||||
<!-- <td>
|
||||
<Avatar
|
||||
name={usernameById(users, record._sys.updatedBy)}
|
||||
side={24}
|
||||
/>
|
||||
</td> -->
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
|
||||
@@ -1,25 +1,29 @@
|
||||
|
||||
|
||||
|
||||
export function sortByField(from, to, edges, fieldName, references) {
|
||||
if (from === to) {
|
||||
return edges;
|
||||
}
|
||||
let referenceIds = references.map(r => r.id);
|
||||
let edgesTosort = edges?.filter((ed) => ed.field === fieldName && ed.depth === 1 && referenceIds.includes(ed.target)) ?? [];
|
||||
let remainingEdge = edges?.filter((ed) => !(ed.field === fieldName && ed.depth === 1)) ?? [];
|
||||
if (from === to) {
|
||||
return edges;
|
||||
}
|
||||
let referenceIds = references.map((r) => r.id);
|
||||
let edgesTosort =
|
||||
edges?.filter(
|
||||
(ed) =>
|
||||
ed.field === fieldName &&
|
||||
ed.depth === 1 &&
|
||||
referenceIds.includes(ed.target),
|
||||
) ?? [];
|
||||
let remainingEdge =
|
||||
edges?.filter((ed) => !(ed.field === fieldName && ed.depth === 1)) ?? [];
|
||||
|
||||
edgesTosort = array_move(edgesTosort,from, to);
|
||||
return [...remainingEdge, ...edgesTosort];
|
||||
edgesTosort = array_move(edgesTosort, from, to);
|
||||
return [...remainingEdge, ...edgesTosort];
|
||||
}
|
||||
|
||||
function array_move(arr, old_index, new_index) {
|
||||
if (new_index >= arr.length) {
|
||||
var k = new_index - arr.length + 1;
|
||||
while (k--) {
|
||||
arr.push(undefined);
|
||||
}
|
||||
export function array_move(arr, old_index, new_index) {
|
||||
if (new_index >= arr.length) {
|
||||
var k = new_index - arr.length + 1;
|
||||
while (k--) {
|
||||
arr.push(undefined);
|
||||
}
|
||||
arr.splice(new_index, 0, arr.splice(old_index, 1)[0]);
|
||||
return arr; // for testing
|
||||
};
|
||||
}
|
||||
arr.splice(new_index, 0, arr.splice(old_index, 1)[0]);
|
||||
return arr; // for testing
|
||||
}
|
||||
|
||||
@@ -1,48 +1,40 @@
|
||||
<script>
|
||||
import {formatDistanceToNow, parseJSON} from "date-fns";
|
||||
import { formatDistanceToNow, parseJSON } from "date-fns";
|
||||
import Avatar from "../account/Avatar.svelte";
|
||||
import {previewTitle} from "../records/Preview";
|
||||
import { previewTitle } from "../records/Preview";
|
||||
import Preview from "../files/Preview.svelte";
|
||||
import {usernameById} from "../account/users";
|
||||
import {getContext} from "svelte";
|
||||
import { usernameById } from "../account/users";
|
||||
import { getContext } from "svelte";
|
||||
|
||||
const channel = getContext("channel");
|
||||
export let users;
|
||||
export let graph;
|
||||
export let record;
|
||||
let schema = channel.schemas.find((s) => s.name === record.schema);
|
||||
let frieldlyUpdatedAt = formatDistanceToNow(
|
||||
parseJSON(record._sys.updatedAt),
|
||||
{addSuffix: true}
|
||||
);
|
||||
let frieldlyUpdatedAt = formatDistanceToNow(parseJSON(record.updatedAt), {
|
||||
addSuffix: true,
|
||||
});
|
||||
</script>
|
||||
|
||||
<td>
|
||||
<div class="row-name">
|
||||
{#if record.status === "draft"}
|
||||
<span class="status">DRAFT</span>
|
||||
{/if}
|
||||
{#if schema.type === "files"}
|
||||
<Preview {record} size="tiny" showFilename={true}/>
|
||||
{:else}
|
||||
<a
|
||||
href="{channel.lucentUrl}/records/{record.id}"
|
||||
|
||||
>
|
||||
{previewTitle(channel.schemas, record, graph)}
|
||||
</a>
|
||||
{/if}
|
||||
{#if record.status === "draft"}
|
||||
<span class="status">DRAFT</span>
|
||||
{/if}
|
||||
{#if schema.type === "files"}
|
||||
<Preview {record} size="tiny" showFilename={true} />
|
||||
{:else}
|
||||
<a href="{channel.lucentUrl}/records/{record.id}">
|
||||
{previewTitle(channel.schemas, record, graph)}
|
||||
</a>
|
||||
{/if}
|
||||
</div>
|
||||
</td>
|
||||
<td><a
|
||||
href="{channel.lucentUrl}/content/{schema.name}">{schema.label}</a
|
||||
>
|
||||
</td>
|
||||
|
||||
<td><a href="{channel.lucentUrl}/content/{schema.name}">{schema.label}</a> </td>
|
||||
|
||||
<td>
|
||||
<div style="display: flex;gap: 14px">
|
||||
<Avatar name={usernameById(users, record._sys.updatedBy)} side={24}/>
|
||||
<Avatar name={usernameById(users, record.updatedBy)} side={24} />
|
||||
<div class="ms-2">
|
||||
{frieldlyUpdatedAt}
|
||||
</div>
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
<script>
|
||||
|
||||
// https://codesandbox.io/s/codemirror-remark-editor-4m4z9?file=/src/CodeEditor.js:374-387
|
||||
import {onDestroy, onMount} from "svelte";
|
||||
import {basicSetup, EditorView} from "codemirror";
|
||||
import {autocompletion, completionKeymap} from "@codemirror/autocomplete";
|
||||
import {Compartment, EditorState} from "@codemirror/state";
|
||||
import {keymap} from "@codemirror/view";
|
||||
import {indentWithTab} from "@codemirror/commands";
|
||||
import {markdown} from "@codemirror/lang-markdown";
|
||||
import {lintKeymap} from "@codemirror/lint";
|
||||
import { onDestroy, onMount } from "svelte";
|
||||
import { basicSetup, EditorView } from "codemirror";
|
||||
import { autocompletion, completionKeymap } from "@codemirror/autocomplete";
|
||||
import { Compartment, EditorState } from "@codemirror/state";
|
||||
import { keymap } from "@codemirror/view";
|
||||
import { indentWithTab } from "@codemirror/commands";
|
||||
import { markdown } from "@codemirror/lang-markdown";
|
||||
import { lintKeymap } from "@codemirror/lint";
|
||||
|
||||
let parentElement;
|
||||
let codeMirrorView;
|
||||
@@ -17,10 +16,10 @@
|
||||
|
||||
export function insertMedia(info) {
|
||||
let insertText = "";
|
||||
if (info.record._file.width > 0) {
|
||||
insertText = ``;
|
||||
if (info.file.width > 0) {
|
||||
insertText = ``;
|
||||
} else {
|
||||
insertText = `[${info.record._file.originalName}](${info.originalUrl})`;
|
||||
insertText = `[${info.file.filename}](${info.originalUrl})`;
|
||||
}
|
||||
const cursor = codeMirrorView.state.selection.main.head;
|
||||
const transaction = codeMirrorView.state.update({
|
||||
@@ -29,7 +28,7 @@
|
||||
insert: insertText,
|
||||
},
|
||||
// the next 2 lines will set the appropriate cursor position after inserting the new text.
|
||||
selection: {anchor: cursor + 1},
|
||||
selection: { anchor: cursor + 1 },
|
||||
scrollIntoView: true,
|
||||
});
|
||||
|
||||
@@ -46,11 +45,7 @@
|
||||
doc: value,
|
||||
extensions: [
|
||||
basicSetup,
|
||||
keymap.of([
|
||||
indentWithTab,
|
||||
...lintKeymap,
|
||||
...completionKeymap
|
||||
]),
|
||||
keymap.of([indentWithTab, ...lintKeymap, ...completionKeymap]),
|
||||
language.of(markdown()),
|
||||
markdown(),
|
||||
autocompletion(),
|
||||
@@ -63,17 +58,14 @@
|
||||
}
|
||||
}),
|
||||
EditorView.lineWrapping,
|
||||
EditorView.contentAttributes.of({spellcheck: "true"})
|
||||
EditorView.contentAttributes.of({ spellcheck: "true" }),
|
||||
],
|
||||
});
|
||||
|
||||
|
||||
codeMirrorView = new EditorView({
|
||||
state,
|
||||
parent: parentElement,
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
@@ -83,4 +75,4 @@
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="is-editable-{editable}" bind:this={parentElement}/>
|
||||
<div class="is-editable-{editable}" bind:this={parentElement} />
|
||||
|
||||
@@ -1,68 +1,66 @@
|
||||
<script>
|
||||
import {onDestroy, onMount} from "svelte";
|
||||
import Trix from "trix"
|
||||
import "trix/dist/trix.css"
|
||||
import { onDestroy, onMount } from "svelte";
|
||||
import Trix from "trix";
|
||||
import "trix/dist/trix.css";
|
||||
|
||||
export let value = "";
|
||||
export let field;
|
||||
let editor;
|
||||
|
||||
|
||||
function updateValue(e) {
|
||||
value = e.target.value;
|
||||
}
|
||||
|
||||
export function insertMedia(info){
|
||||
if(info.record._file.width > 0){
|
||||
var attachment = new Trix.Attachment({ content: info.html })
|
||||
editor.editor.insertAttachment(attachment)
|
||||
}else{
|
||||
editor.editor.insertHTML(`<a href="${info.originalUrl}">${info.record._file.originalName}</a>`)
|
||||
|
||||
export function insertMedia(info) {
|
||||
if (info.file.width > 0) {
|
||||
var attachment = new Trix.Attachment({ content: info.html });
|
||||
editor.editor.insertAttachment(attachment);
|
||||
} else {
|
||||
editor.editor.insertHTML(
|
||||
`<a href="${info.originalUrl}">${info.file.filename}</a>`,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
editor.addEventListener("trix-file-accept", (e) => {
|
||||
e.preventDefault();
|
||||
})
|
||||
});
|
||||
|
||||
editor.addEventListener("trix-before-initialize", (e) => {
|
||||
Trix.config.blockAttributes.heading1.tagName = 'h2';
|
||||
const { toolbarElement } = e.target
|
||||
const h1Button = toolbarElement.querySelector("[data-trix-attribute=heading1]")
|
||||
h1Button.insertAdjacentHTML("afterend", `<button style="text-indent: initial;padding: 14px 10px !important;" type="button" class="trix-button trix-button--icon" data-trix-attribute="heading3" title="Heading 3" tabindex="-1" data-trix-active="">H3</button>`)
|
||||
})
|
||||
|
||||
|
||||
})
|
||||
Trix.config.blockAttributes.heading1.tagName = "h2";
|
||||
const { toolbarElement } = e.target;
|
||||
const h1Button = toolbarElement.querySelector(
|
||||
"[data-trix-attribute=heading1]",
|
||||
);
|
||||
h1Button.insertAdjacentHTML(
|
||||
"afterend",
|
||||
`<button style="text-indent: initial;padding: 14px 10px !important;" type="button" class="trix-button trix-button--icon" data-trix-attribute="heading3" title="Heading 3" tabindex="-1" data-trix-active="">H3</button>`,
|
||||
);
|
||||
});
|
||||
});
|
||||
// onDestroy(() => {
|
||||
// editor.removeEventListener("trix-before-initialize")
|
||||
// })
|
||||
|
||||
|
||||
Trix.config.blockAttributes.default.breakOnReturn = false
|
||||
Trix.config.blockAttributes.default.breakOnReturn = false;
|
||||
Trix.config.blockAttributes.heading3 = {
|
||||
tagName: 'h3',
|
||||
tagName: "h3",
|
||||
terminal: true,
|
||||
breakOnReturn: true,
|
||||
group: false
|
||||
}
|
||||
group: false,
|
||||
};
|
||||
// console.log(Trix.config)
|
||||
|
||||
</script>
|
||||
|
||||
<div class="tox-wrapper">
|
||||
<input id="x-{field.name}" {value} type="hidden">
|
||||
<input id="x-{field.name}" {value} type="hidden" />
|
||||
<trix-editor
|
||||
bind:this={editor}
|
||||
class=" content"
|
||||
input="x-{field.name}"
|
||||
role="textbox"
|
||||
tabindex="0"
|
||||
on:trix-change={updateValue}
|
||||
|
||||
bind:this={editor}
|
||||
class=" content"
|
||||
input="x-{field.name}"
|
||||
role="textbox"
|
||||
tabindex="0"
|
||||
on:trix-change={updateValue}
|
||||
></trix-editor>
|
||||
</div>
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
<script>
|
||||
import {afterUpdate, getContext, onMount} from "svelte";
|
||||
import {isEqual} from "lodash";
|
||||
import { afterUpdate, getContext, onMount } from "svelte";
|
||||
import { isEqual } from "lodash";
|
||||
import axios from "axios";
|
||||
import EditHeader from "./header/EditHeader.svelte"
|
||||
import FilePreview from "./FilePreview.svelte"
|
||||
import ContentTabs from "./header/ContentTabs.svelte"
|
||||
import FormField from "./FormField.svelte"
|
||||
import Graph from "./Graph.svelte"
|
||||
import Info from "./Info.svelte"
|
||||
import ErrorAlert from "../common/ErrorAlert.svelte"
|
||||
import EditHeader from "./header/EditHeader.svelte";
|
||||
import ContentTabs from "./header/ContentTabs.svelte";
|
||||
import FormField from "./FormField.svelte";
|
||||
import Graph from "./Graph.svelte";
|
||||
import Info from "./Info.svelte";
|
||||
import ErrorAlert from "../common/ErrorAlert.svelte";
|
||||
import Title from "./header/Title.svelte";
|
||||
|
||||
const channel = getContext("channel");
|
||||
@@ -17,7 +16,7 @@
|
||||
export let record;
|
||||
export let graph = {
|
||||
records: [],
|
||||
edges: []
|
||||
edges: [],
|
||||
};
|
||||
// export let recordHistory;
|
||||
export let isCreateMode;
|
||||
@@ -29,14 +28,11 @@
|
||||
$: validationErrors = null;
|
||||
$: errorMessage = validationErrors
|
||||
? `Record submission failed. ${
|
||||
Object.entries(validationErrors).length
|
||||
} error(s)`
|
||||
Object.entries(validationErrors).length
|
||||
} error(s)`
|
||||
: null;
|
||||
|
||||
let activeFields = schema.fields.filter(
|
||||
(f) => f.name !== "id"
|
||||
);
|
||||
|
||||
let activeFields = schema.fields.filter((f) => f.name !== "id");
|
||||
|
||||
onMount(() => {
|
||||
setOriginalContent();
|
||||
@@ -45,10 +41,7 @@
|
||||
function setOriginalContent() {
|
||||
originalContent = {
|
||||
data: JSON.parse(JSON.stringify(record.data)),
|
||||
schema: record.schema,
|
||||
status: record.status,
|
||||
_sys: JSON.parse(JSON.stringify(record._sys)),
|
||||
_file: JSON.parse(JSON.stringify(record._file)),
|
||||
edges: JSON.parse(JSON.stringify(graph.edges)),
|
||||
};
|
||||
}
|
||||
@@ -79,10 +72,7 @@
|
||||
}
|
||||
return !isEqual(originalContent, {
|
||||
data: record.data,
|
||||
schema: record.schema,
|
||||
status: record.status,
|
||||
_sys: record._sys,
|
||||
_file: record._file,
|
||||
edges: graph.edges,
|
||||
});
|
||||
}
|
||||
@@ -104,7 +94,9 @@
|
||||
}
|
||||
|
||||
// remove trashed edges
|
||||
graph.edges = graph.edges?.filter((edge) => !edge._isTrashed && edge.source === record.id);
|
||||
graph.edges = graph.edges?.filter(
|
||||
(edge) => !edge._isTrashed && edge.source === record.id,
|
||||
);
|
||||
axios
|
||||
.post(channel.lucentUrl + "/records", {
|
||||
record: record,
|
||||
@@ -115,7 +107,8 @@
|
||||
console.log("SAVE: SAVED");
|
||||
|
||||
if (isCreateMode) {
|
||||
window.location = channel.lucentUrl + "/records/" + record.id;
|
||||
window.location =
|
||||
channel.lucentUrl + "/records/" + record.id;
|
||||
} else {
|
||||
record = response.data.records[0] ?? null;
|
||||
if (!record) {
|
||||
@@ -137,7 +130,7 @@
|
||||
errorMessage = error.response.data.error;
|
||||
} else {
|
||||
validationErrors = error.response.data.error;
|
||||
console.log(validationErrors)
|
||||
console.log(validationErrors);
|
||||
}
|
||||
}
|
||||
resolve(null);
|
||||
@@ -149,70 +142,60 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window on:beforeunload={beforeUnload}/>
|
||||
<svelte:window on:beforeunload={beforeUnload} />
|
||||
|
||||
<div class="record-edit">
|
||||
<div class="tools-header">
|
||||
<!-- <Manager managerRecords={recordHistory} {graph}/>-->
|
||||
<EditHeader {schema} bind:record {isCreateMode} bind:activeContentTab/>
|
||||
<EditHeader {schema} bind:record {isCreateMode} bind:activeContentTab />
|
||||
{#if isCreateMode}
|
||||
<button
|
||||
class="button primary btn-spinner"
|
||||
on:click={save}
|
||||
>
|
||||
<span
|
||||
class="spinner-border spinner-border-sm"
|
||||
role="status"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<button class="button primary btn-spinner" on:click={save}>
|
||||
<span
|
||||
class="spinner-border spinner-border-sm"
|
||||
role="status"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
Create
|
||||
</button>
|
||||
{:else if hasUnsavedData}
|
||||
<button
|
||||
type="button"
|
||||
class="button primary ms-2 btn btn-primary btn-spinner"
|
||||
on:click={save}
|
||||
type="button"
|
||||
class="button primary ms-2 btn btn-primary btn-spinner"
|
||||
on:click={save}
|
||||
>
|
||||
<span
|
||||
class="spinner-border spinner-border-sm"
|
||||
role="status"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<span
|
||||
class="spinner-border spinner-border-sm"
|
||||
role="status"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
Save
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
</div>
|
||||
<Title {schema} {record} {isCreateMode}/>
|
||||
<Title {schema} {record} {isCreateMode} />
|
||||
|
||||
|
||||
<ErrorAlert message={errorMessage}/>
|
||||
<ErrorAlert message={errorMessage} />
|
||||
|
||||
<div class=" mt-4" style="margin-bottom:150px;position:relative;">
|
||||
<ContentTabs
|
||||
{schema}
|
||||
{isCreateMode}
|
||||
bind:active={activeContentTab}
|
||||
/>
|
||||
<ContentTabs {schema} {isCreateMode} bind:active={activeContentTab} />
|
||||
{#if !["_graph", "_info"].includes(activeContentTab)}
|
||||
<FilePreview {record} {schema}/>
|
||||
{#each activeFields as field (field.name)}
|
||||
{#if activeContentTab === field.group}
|
||||
<FormField
|
||||
bind:data={record.data}
|
||||
bind:graph={graph}
|
||||
{field}
|
||||
{schema}
|
||||
{record}
|
||||
{validationErrors}
|
||||
{isCreateMode}
|
||||
bind:data={record.data}
|
||||
bind:graph
|
||||
{field}
|
||||
{schema}
|
||||
{record}
|
||||
{validationErrors}
|
||||
{isCreateMode}
|
||||
/>
|
||||
{/if}
|
||||
{/each}
|
||||
{:else if activeContentTab === "_graph"}
|
||||
<Graph {graph} {record}/>
|
||||
<Graph {graph} {record} />
|
||||
{:else if activeContentTab === "_info"}
|
||||
<Info {record} {graph} {users} {schema}/>
|
||||
<Info {record} {graph} {users} {schema} />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -45,25 +45,20 @@
|
||||
</script>
|
||||
|
||||
<div class="editor-field">
|
||||
<FieldHeader {field} {id}/>
|
||||
<FieldHeader {field} {id} />
|
||||
{#if field.info.name === "reference" && field.layout === "tags"}
|
||||
<ReferenceTags
|
||||
bind:graph
|
||||
{id}
|
||||
{record}
|
||||
{field}
|
||||
{validationErrors}
|
||||
/>
|
||||
<ReferenceTags bind:graph {id} {record} {field} {validationErrors} />
|
||||
{:else if field.info.name === "reference"}
|
||||
<Reference
|
||||
bind:graph
|
||||
{id}
|
||||
<Reference bind:graph {id} {record} {field} {validationErrors} />
|
||||
{:else if field.info.name === "file"}
|
||||
<!-- <File bind:graph {record} {field} {validationErrors} /> -->
|
||||
<File
|
||||
bind:value={data[field.name]}
|
||||
{record}
|
||||
{id}
|
||||
{field}
|
||||
{validationErrors}
|
||||
/>
|
||||
{:else if field.info.name === "file"}
|
||||
<File bind:graph {record} {field} {validationErrors}/>
|
||||
{:else if field.info.name === "text"}
|
||||
<Text
|
||||
bind:value={data[field.name]}
|
||||
@@ -74,11 +69,11 @@
|
||||
/>
|
||||
{:else if field.info.name === "slug"}
|
||||
<Slug
|
||||
bind:value={data[field.name]}
|
||||
{field}
|
||||
{id}
|
||||
{validationErrors}
|
||||
{isCreateMode}
|
||||
bind:value={data[field.name]}
|
||||
{field}
|
||||
{id}
|
||||
{validationErrors}
|
||||
{isCreateMode}
|
||||
/>
|
||||
{:else if field.info.name === "textarea"}
|
||||
<Textarea
|
||||
@@ -90,23 +85,23 @@
|
||||
/>
|
||||
{:else if field.info.name === "rich"}
|
||||
<RichEditor
|
||||
bind:value={data[field.name]}
|
||||
{schema}
|
||||
{field}
|
||||
{validationErrors}
|
||||
{isCreateMode}
|
||||
bind:graph
|
||||
{record}
|
||||
bind:value={data[field.name]}
|
||||
{schema}
|
||||
{field}
|
||||
{validationErrors}
|
||||
{isCreateMode}
|
||||
bind:graph
|
||||
{record}
|
||||
/>
|
||||
{:else if field.info.name === "markdown"}
|
||||
<Markdown
|
||||
bind:value={data[field.name]}
|
||||
{schema}
|
||||
{field}
|
||||
{validationErrors}
|
||||
{isCreateMode}
|
||||
bind:graph
|
||||
{record}
|
||||
bind:value={data[field.name]}
|
||||
{schema}
|
||||
{field}
|
||||
{validationErrors}
|
||||
{isCreateMode}
|
||||
bind:graph
|
||||
{record}
|
||||
/>
|
||||
{:else}
|
||||
<svelte:component
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<script>
|
||||
import {friendlyDate} from "../../helpers";
|
||||
import { friendlyDate } from "../../helpers";
|
||||
import Avatar from "../account/Avatar.svelte";
|
||||
import {usernameById} from "../account/users";
|
||||
import {isEqual} from "lodash";
|
||||
import { usernameById } from "../account/users";
|
||||
import { isEqual } from "lodash";
|
||||
import Icon from "../common/Icon.svelte";
|
||||
import RevisionCell from "./revisions/RevisionCell.svelte";
|
||||
import {getContext} from "svelte";
|
||||
import { getContext } from "svelte";
|
||||
import RevisionEdgeRow from "./revisions/RevisionEdgeRow.svelte";
|
||||
|
||||
const channel = getContext("channel");
|
||||
@@ -30,27 +30,27 @@
|
||||
});
|
||||
|
||||
function getEdgesByField(fieldsWithDiff, revision) {
|
||||
|
||||
edgeFieldsDiff = graph.edges.filter((e) => e.depth === 1).reduce((c, e) => {
|
||||
if (!c[e.field]) {
|
||||
c[e.field] = {
|
||||
record: [],
|
||||
revision: [],
|
||||
edgeFieldsDiff = graph.edges
|
||||
.filter((e) => e.depth === 1)
|
||||
.reduce((c, e) => {
|
||||
if (!c[e.field]) {
|
||||
c[e.field] = {
|
||||
record: [],
|
||||
revision: [],
|
||||
};
|
||||
}
|
||||
}
|
||||
c[e.field]["record"].push(e)
|
||||
return c;
|
||||
}, {});
|
||||
|
||||
c[e.field]["record"].push(e);
|
||||
return c;
|
||||
}, {});
|
||||
|
||||
edgeFieldsDiff = revision._edges.reduce((c, e) => {
|
||||
if (!c[e.field]) {
|
||||
c[e.field] = {
|
||||
record: [],
|
||||
revision: [],
|
||||
}
|
||||
};
|
||||
}
|
||||
c[e.field]["revision"].push(e)
|
||||
c[e.field]["revision"].push(e);
|
||||
return c;
|
||||
}, edgeFieldsDiff);
|
||||
}
|
||||
@@ -62,7 +62,7 @@
|
||||
fieldsWithDiff = schema.fields.filter((f) => {
|
||||
return !isEqual(selectedRevision.data[f.name], record.data[f.name]);
|
||||
});
|
||||
getEdgesByField(fieldsWithDiff, revision)
|
||||
getEdgesByField(fieldsWithDiff, revision);
|
||||
revisionSection.scrollIntoView();
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
rollbackError = "";
|
||||
axios
|
||||
.post(
|
||||
`${channel.lucentUrl}/records/${record.id}/rollback/${selectedRevision._sys.version}`
|
||||
`${channel.lucentUrl}/records/${record.id}/rollback/${selectedRevision.version}`,
|
||||
)
|
||||
.then((response) => {
|
||||
window.location.reload();
|
||||
@@ -84,7 +84,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="lx-card ">
|
||||
<div class="lx-card">
|
||||
<div class="row">
|
||||
<div class="col-8">
|
||||
<div>
|
||||
@@ -93,29 +93,27 @@
|
||||
</div>
|
||||
<div>
|
||||
<span class="label text-end text-muted">current version </span>
|
||||
{record._sys.version}
|
||||
{record.version}
|
||||
</div>
|
||||
<div>
|
||||
<span class="label text-end text-muted"> created </span>
|
||||
<Avatar
|
||||
name={usernameById(users, record._sys.createdBy)}
|
||||
side={24}
|
||||
name={usernameById(users, record.createdBy)}
|
||||
side={24}
|
||||
/>
|
||||
{friendlyDate(record._sys.createdAt)}
|
||||
{friendlyDate(record.createdAt)}
|
||||
</div>
|
||||
<div>
|
||||
<span class="label text-end text-muted">updated </span>
|
||||
<span class="label text-end text-muted">updated </span>
|
||||
<Avatar
|
||||
name={usernameById(users, record._sys.updatedBy)}
|
||||
side={24}
|
||||
name={usernameById(users, record.updatedBy)}
|
||||
side={24}
|
||||
/>
|
||||
{friendlyDate(record._sys.updatedAt)}
|
||||
{friendlyDate(record.updatedAt)}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<span class="label d-block text-muted "
|
||||
>Rules for this schema
|
||||
</span>
|
||||
<span class="label d-block text-muted">Rules for this schema </span>
|
||||
<small>
|
||||
Each record maintains the last {schema.revisions}
|
||||
versions
|
||||
@@ -125,33 +123,31 @@
|
||||
</div>
|
||||
<div class="revisions">
|
||||
{#if schema.revisions > 0}
|
||||
<div class="header-small mb-3">Revisions</div>
|
||||
<div class="header-small mb-3">Revisions</div>
|
||||
{#each revisions as revision}
|
||||
{#if revision._sys.version !== record._sys.version}
|
||||
{#if revision.version !== record.version}
|
||||
<div
|
||||
class="revision"
|
||||
class:active={revision._sys.version ===
|
||||
selectedRevision?._sys.version}
|
||||
class="revision"
|
||||
class:active={revision.version ===
|
||||
selectedRevision?.version}
|
||||
>
|
||||
|
||||
<div class="version">
|
||||
<span>version {revision._sys.version}</span>
|
||||
<span>version {revision.version}</span>
|
||||
<Avatar
|
||||
name={usernameById(users, revision._sys.updatedBy)}
|
||||
side={24}
|
||||
name={usernameById(users, revision.updatedBy)}
|
||||
side={24}
|
||||
/>
|
||||
{friendlyDate(revision._sys.updatedAt)}
|
||||
{friendlyDate(revision.updatedAt)}
|
||||
</div>
|
||||
|
||||
<div class="col-3 text-center">
|
||||
<button
|
||||
disabled={revision._sys.version ===
|
||||
selectedRevision?._sys.version}
|
||||
class="button"
|
||||
on:click={(e) => compare(e, revision)}
|
||||
>Compare
|
||||
</button
|
||||
>
|
||||
disabled={revision.version ===
|
||||
selectedRevision?.version}
|
||||
class="button"
|
||||
on:click={(e) => compare(e, revision)}
|
||||
>Compare
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
@@ -169,15 +165,13 @@
|
||||
<p class="text-center fw-bold mb-3 mt-5">
|
||||
If you choose to rollback to this revision
|
||||
</p>
|
||||
<button
|
||||
on:click={rollback}
|
||||
class="button"
|
||||
>
|
||||
Rollback to version {selectedRevision._sys.version}
|
||||
<button on:click={rollback} class="button">
|
||||
Rollback to version {selectedRevision.version}
|
||||
</button>
|
||||
|
||||
{#if rollbackError}
|
||||
<span class="d-block text-danger mt-3">{rollbackError}</span>
|
||||
<span class="d-block text-danger mt-3">{rollbackError}</span
|
||||
>
|
||||
{/if}
|
||||
<div class="mt-3">
|
||||
{#each fieldsWithDiff as field}
|
||||
@@ -188,31 +182,28 @@
|
||||
<!-- <div class="d-block" style="width:200px;">
|
||||
{field.label}
|
||||
</div> -->
|
||||
<div
|
||||
class="revision-field"
|
||||
style="overflow:hidden"
|
||||
>
|
||||
<div class="revision-field" style="overflow:hidden">
|
||||
<div class="compare-left">
|
||||
<RevisionCell
|
||||
{field}
|
||||
side={record.data[field.name]}
|
||||
colorClass="text-danger"
|
||||
{field}
|
||||
side={record.data[field.name]}
|
||||
colorClass="text-danger"
|
||||
/>
|
||||
</div>
|
||||
<div class="compare-center">
|
||||
<span class="me-1">{field.label}</span>
|
||||
<Icon
|
||||
icon="angle-right"
|
||||
width="12"
|
||||
height="12"
|
||||
icon="angle-right"
|
||||
width="12"
|
||||
height="12"
|
||||
/>
|
||||
</div>
|
||||
<div class="compare-right">
|
||||
<RevisionCell
|
||||
edges={selectedRevision._edges}
|
||||
{field}
|
||||
side={selectedRevision.data[field.name]}
|
||||
colorClass="text-success"
|
||||
edges={selectedRevision._edges}
|
||||
{field}
|
||||
side={selectedRevision.data[field.name]}
|
||||
colorClass="text-success"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -226,22 +217,16 @@
|
||||
{/if}
|
||||
|
||||
<div class="mt-3">
|
||||
<p class="text-center fw-bold mb-3 mt-5">
|
||||
Record References
|
||||
</p>
|
||||
<p class="text-center fw-bold mb-3 mt-5">Record References</p>
|
||||
{#each Object.entries(edgeFieldsDiff) as [field, edges]}
|
||||
<div
|
||||
class="revision-references"
|
||||
style="overflow:hidden"
|
||||
>
|
||||
<div class="revision-references" style="overflow:hidden">
|
||||
<div class="reference-field">
|
||||
{field}:
|
||||
</div>
|
||||
<div class="reference-compare">
|
||||
|
||||
<p class="">Record</p>
|
||||
{#each edges.record as edge}
|
||||
<RevisionEdgeRow {edge}/>
|
||||
<RevisionEdgeRow {edge} />
|
||||
{:else}
|
||||
<p>No references</p>
|
||||
{/each}
|
||||
@@ -249,7 +234,7 @@
|
||||
<div class="reference-compare">
|
||||
<p class="text-success">Revision</p>
|
||||
{#each edges.revision as edge}
|
||||
<RevisionEdgeRow {edge}/>
|
||||
<RevisionEdgeRow {edge} />
|
||||
{:else}
|
||||
<p>No references</p>
|
||||
{/each}
|
||||
@@ -258,7 +243,5 @@
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
<script>
|
||||
import {afterUpdate, createEventDispatcher, getContext, onMount} from "svelte";
|
||||
import {
|
||||
afterUpdate,
|
||||
createEventDispatcher,
|
||||
getContext,
|
||||
onMount,
|
||||
} from "svelte";
|
||||
|
||||
import {isEqual} from "lodash";
|
||||
import { isEqual } from "lodash";
|
||||
import FormField from "./FormField.svelte";
|
||||
import FilePreview from "./FilePreview.svelte";
|
||||
import ContentTabs from "./header/ContentTabs.svelte";
|
||||
@@ -16,7 +21,7 @@
|
||||
export let record;
|
||||
export let graph = {
|
||||
records: [],
|
||||
edges: []
|
||||
edges: [],
|
||||
};
|
||||
export let isCreateMode;
|
||||
let originalContent;
|
||||
@@ -25,13 +30,11 @@
|
||||
$: validationErrors = null;
|
||||
$: errorMessage = validationErrors
|
||||
? `Record submission failed. ${
|
||||
Object.entries(validationErrors).length
|
||||
} error(s)`
|
||||
Object.entries(validationErrors).length
|
||||
} error(s)`
|
||||
: null;
|
||||
|
||||
let activeFields = schema.fields.filter(
|
||||
(f) => f.name !== "id"
|
||||
);
|
||||
let activeFields = schema.fields.filter((f) => f.name !== "id");
|
||||
|
||||
let tabname = "_default";
|
||||
let fieldToTabs = schema.fields.reduce((c, f) => {
|
||||
@@ -53,8 +56,6 @@
|
||||
data: JSON.parse(JSON.stringify(record.data)),
|
||||
schema: record.schema,
|
||||
status: record.status,
|
||||
_sys: JSON.parse(JSON.stringify(record._sys)),
|
||||
_file: JSON.parse(JSON.stringify(record._file)),
|
||||
edges: JSON.parse(JSON.stringify(graph.edges)),
|
||||
};
|
||||
}
|
||||
@@ -87,8 +88,6 @@
|
||||
data: record.data,
|
||||
schema: record.schema,
|
||||
status: record.status,
|
||||
_sys: record._sys,
|
||||
_file: record._file,
|
||||
edges: graph.edges,
|
||||
});
|
||||
}
|
||||
@@ -114,8 +113,10 @@
|
||||
return;
|
||||
}
|
||||
// remove trashed edges
|
||||
graph.edges = graph.edges?.filter((edge) => !edge._isTrashed && edge.source === record.id) ?? [];
|
||||
|
||||
graph.edges =
|
||||
graph.edges?.filter(
|
||||
(edge) => !edge._isTrashed && edge.source === record.id,
|
||||
) ?? [];
|
||||
|
||||
axios
|
||||
.post(channel.lucentUrl + "/records", {
|
||||
@@ -150,64 +151,55 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window on:beforeunload={beforeUnload}/>
|
||||
<svelte:window on:beforeunload={beforeUnload} />
|
||||
|
||||
<div class="inline-edit record-edit">
|
||||
<div class="tools-header">
|
||||
<EditHeader {schema} bind:record {isCreateMode} bind:activeContentTab/>
|
||||
<EditHeader {schema} bind:record {isCreateMode} bind:activeContentTab />
|
||||
{#if isCreateMode}
|
||||
<button
|
||||
class="button primary btn-spinner"
|
||||
on:click={save}
|
||||
>
|
||||
<span
|
||||
class="spinner-border spinner-border-sm"
|
||||
role="status"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<button class="button primary btn-spinner" on:click={save}>
|
||||
<span
|
||||
class="spinner-border spinner-border-sm"
|
||||
role="status"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
Create
|
||||
</button>
|
||||
{:else if hasUnsavedData}
|
||||
<button
|
||||
type="button"
|
||||
class="button primary ms-2 btn btn-primary btn-spinner"
|
||||
on:click={save}
|
||||
type="button"
|
||||
class="button primary ms-2 btn btn-primary btn-spinner"
|
||||
on:click={save}
|
||||
>
|
||||
<span
|
||||
class="spinner-border spinner-border-sm"
|
||||
role="status"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<span
|
||||
class="spinner-border spinner-border-sm"
|
||||
role="status"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
Save
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
</div>
|
||||
<Title {schema} {record} {isCreateMode}/>
|
||||
<ErrorAlert message={errorMessage}/>
|
||||
<Title {schema} {record} {isCreateMode} />
|
||||
<ErrorAlert message={errorMessage} />
|
||||
|
||||
<div class=" mt-4" style="margin-bottom:150px;position:relative;">
|
||||
<ContentTabs
|
||||
{schema}
|
||||
{isCreateMode}
|
||||
bind:active={activeContentTab}
|
||||
/>
|
||||
<FilePreview {record} {schema}/>
|
||||
<ContentTabs {schema} {isCreateMode} bind:active={activeContentTab} />
|
||||
<FilePreview {record} {schema} />
|
||||
<!-- <fieldset disabled="disabled"> -->
|
||||
{#each activeFields as field (field.name)}
|
||||
{#if activeContentTab === field.group}
|
||||
<FormField
|
||||
bind:data={record.data}
|
||||
bind:graph={graph}
|
||||
{field}
|
||||
{schema}
|
||||
{record}
|
||||
{validationErrors}
|
||||
{isCreateMode}
|
||||
bind:data={record.data}
|
||||
bind:graph
|
||||
{field}
|
||||
{schema}
|
||||
{record}
|
||||
{validationErrors}
|
||||
{isCreateMode}
|
||||
/>
|
||||
{/if}
|
||||
{/each}
|
||||
<!-- </fieldset> -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,33 +1,33 @@
|
||||
import Mustache from "mustache";
|
||||
import {stripHtml} from "../../helpers";
|
||||
import { stripHtml } from "../../helpers";
|
||||
|
||||
export function previewTitle(schemas, record, graph) {
|
||||
let schema = schemas.find((aSchema) => aSchema.name === record?.schema);
|
||||
if (!schema?.cardTitle) {
|
||||
return noTemplate(schema, record);
|
||||
}
|
||||
let schema = schemas.find((aSchema) => aSchema.name === record?.schema);
|
||||
if (!schema?.cardTitle) {
|
||||
return noTemplate(schema, record);
|
||||
}
|
||||
|
||||
let recordData = record.data;
|
||||
let render = Mustache.render(schema.cardTitle, recordData);
|
||||
if (!render || render === "") {
|
||||
return noTemplate(schema, record);
|
||||
}
|
||||
let recordData = record.data;
|
||||
let render = Mustache.render(schema.cardTitle, recordData);
|
||||
if (!render || render === "") {
|
||||
return noTemplate(schema, record);
|
||||
}
|
||||
|
||||
return stripHtml(render.slice(0, 300));
|
||||
return stripHtml(render.slice(0, 300));
|
||||
}
|
||||
|
||||
function noTemplate(schema, record) {
|
||||
if (schema?.type === "files") {
|
||||
return record._file.path;
|
||||
}
|
||||
if (schema?.type === "files") {
|
||||
return file.path;
|
||||
}
|
||||
|
||||
let title = stripHtml(
|
||||
record?.data[schema.fields.filter((f) => f.info.name === "text")[0]?.name]
|
||||
).slice(0, 300);
|
||||
let title = stripHtml(
|
||||
record?.data[schema.fields.filter((f) => f.info.name === "text")[0]?.name],
|
||||
).slice(0, 300);
|
||||
|
||||
if(title.trim() === ""){
|
||||
return "~Untitled~";
|
||||
}
|
||||
if (title.trim() === "") {
|
||||
return "~Untitled~";
|
||||
}
|
||||
|
||||
return title;
|
||||
return title;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
<script>
|
||||
import { sortByField } from "../../edges/sortEdges";
|
||||
import { array_move } from "../../edges/sortEdges";
|
||||
import Sortable from "../../libs/Sortable.svelte";
|
||||
import PreviewFile from "../previews/PreviewFile.svelte";
|
||||
import Dropdown from "../../common/Dropdown.svelte";
|
||||
import Dialog from "../../dialog/Dialog.svelte";
|
||||
import { insertEdges } from "./reference.js";
|
||||
import { getContext } from "svelte";
|
||||
import FileDialog from "../../dialog/FileDialog.svelte";
|
||||
import Uploader from "../../files/Uploader.svelte";
|
||||
@@ -12,36 +9,28 @@
|
||||
const channel = getContext("channel");
|
||||
export let field;
|
||||
export let record;
|
||||
export let graph;
|
||||
export let value = [];
|
||||
let browseModal;
|
||||
|
||||
function removeReference(e) {
|
||||
function removeFile(e) {
|
||||
e.preventDefault();
|
||||
graph.edges = graph.edges.filter(
|
||||
(edge) => !(edge.target === e.detail && edge.field === field.name),
|
||||
);
|
||||
value = value.filter((f) => !(f.id === e.detail));
|
||||
}
|
||||
|
||||
async function reorder(e) {
|
||||
graph.edges = await sortByField(
|
||||
e.detail.source,
|
||||
e.detail.target,
|
||||
graph.edges,
|
||||
field.name,
|
||||
references,
|
||||
);
|
||||
value = await array_move(value, e.detail.source, e.detail.target);
|
||||
}
|
||||
|
||||
function insert(e) {
|
||||
function insertFiles(e) {
|
||||
e.preventDefault();
|
||||
browseModal.close();
|
||||
graph = insertEdges(
|
||||
graph,
|
||||
record,
|
||||
e.detail.records,
|
||||
field.name,
|
||||
e.detail.action,
|
||||
);
|
||||
value = [...(value ?? []), ...(e.detail ?? [])];
|
||||
}
|
||||
|
||||
function replaceFiles(e) {
|
||||
e.preventDefault();
|
||||
browseModal.close();
|
||||
value = e.detail ?? [];
|
||||
}
|
||||
|
||||
function uploadComplete(e) {
|
||||
@@ -61,18 +50,22 @@
|
||||
<Uploader recordId={record.id} on:uploadComplete={uploadComplete} />
|
||||
</div>
|
||||
</div>
|
||||
<!-- {#if references.length > 0}
|
||||
{#if value.length > 0}
|
||||
<Sortable sortableClass="mt-3" on:update={reorder}>
|
||||
{#each references as reference (reference.id)}
|
||||
{#each value ?? [] as aFile (aFile.id)}
|
||||
<!--This div helps the sorting thing-->
|
||||
<!-- <div>
|
||||
<div>
|
||||
<PreviewFile
|
||||
record={reference}
|
||||
file={aFile}
|
||||
hasDelete={true}
|
||||
on:remove={removeReference}
|
||||
on:remove_file={removeFile}
|
||||
></PreviewFile>
|
||||
</div>
|
||||
{/each}
|
||||
</Sortable>
|
||||
{/if} -->
|
||||
<FileDialog bind:this={browseModal} on:insert={insert}></FileDialog>
|
||||
{/if}
|
||||
<FileDialog
|
||||
bind:this={browseModal}
|
||||
on:insert_files={insertFiles}
|
||||
on:replace_files={replaceFiles}
|
||||
></FileDialog>
|
||||
|
||||
@@ -1,91 +1,80 @@
|
||||
<script>
|
||||
import Icon from "../../common/Icon.svelte";
|
||||
|
||||
import {createEventDispatcher, getContext} from "svelte";
|
||||
import { createEventDispatcher, getContext } from "svelte";
|
||||
import Preview from "../../files/Preview.svelte";
|
||||
import {previewTitle} from "./../Preview";
|
||||
import {fileurl, htmlurl} from "../../files/imageserver.js"
|
||||
import { previewTitle } from "./../Preview";
|
||||
import { fileurl, htmlurl } from "../../files/imageserver.js";
|
||||
import Status from "./../Status.svelte";
|
||||
import Dropdown from "../../common/Dropdown.svelte";
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
const channel = getContext("channel");
|
||||
export let record;
|
||||
export let file;
|
||||
export let hasDelete = false;
|
||||
export let hasInsert = false;
|
||||
|
||||
let schema = channel.schemas.find((aschema) => aschema.name === record.schema);
|
||||
let cardTitle = previewTitle(channel.schemas, record);
|
||||
let imagePresets = Object.keys(channel.imageFilters);
|
||||
|
||||
function remove(e) {
|
||||
e.preventDefault();
|
||||
dispatch("remove", record.id);
|
||||
dispatch("remove_file", file.id);
|
||||
}
|
||||
|
||||
function insert(e, preset) {
|
||||
e.preventDefault();
|
||||
let html = htmlurl(channel, record, preset)
|
||||
let url = !preset ? `/${record._file.path}` : `/templates/${preset}/${record._file.path}`;
|
||||
dispatch("editor-insert", {
|
||||
html: html,
|
||||
url: channel.filesUrl + url,
|
||||
originalUrl: channel.filesUrl + "/" + record._file.path,
|
||||
record: record
|
||||
});
|
||||
// let html = htmlurl(channel, record, preset);
|
||||
// let url = !preset
|
||||
// ? `/${record._file.path}`
|
||||
// : `/templates/${preset}/${record._file.path}`;
|
||||
// dispatch("editor-insert", {
|
||||
// html: html,
|
||||
// url: channel.filesUrl + url,
|
||||
// originalUrl: channel.filesUrl + "/" + record._file.path,
|
||||
// record: record,
|
||||
// });
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<div class="preview-file">
|
||||
<div style="display: flex;align-items: center;gap: 10px;">
|
||||
<div class="image">
|
||||
<Preview {record} size="small"/>
|
||||
<Preview {file} size="small" />
|
||||
</div>
|
||||
<div class="title">
|
||||
<div>
|
||||
<a
|
||||
class="record-title"
|
||||
href="{channel.lucentUrl}/records/{record.id}"
|
||||
>
|
||||
{cardTitle}
|
||||
</a>
|
||||
<small class="d-block">
|
||||
from {schema.label}
|
||||
{#if record.status === "draft"}
|
||||
<Status status={record.status}/>
|
||||
{/if}
|
||||
</small>
|
||||
|
||||
{file.filename}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: flex;gap:4px; align-items: center; margin-right: 10px;">
|
||||
<div
|
||||
style="display: flex;gap:4px; align-items: center; margin-right: 10px;"
|
||||
>
|
||||
{#if hasInsert}
|
||||
<div class="reference-action">
|
||||
<Dropdown>
|
||||
<div slot="button">
|
||||
<Icon icon="photo-film"/>
|
||||
<Icon icon="photo-film" />
|
||||
</div>
|
||||
<button class="dropdown-item button" on:click={e => insert(e,null)}>original</button>
|
||||
<button
|
||||
class="dropdown-item button"
|
||||
on:click={(e) => insert(e, null)}>original</button
|
||||
>
|
||||
{#each imagePresets as preset}
|
||||
<button class="dropdown-item button" on:click={e => insert(e,preset)}>{preset}</button>
|
||||
<button
|
||||
class="dropdown-item button"
|
||||
on:click={(e) => insert(e, preset)}>{preset}</button
|
||||
>
|
||||
{/each}
|
||||
</Dropdown>
|
||||
|
||||
</div>
|
||||
{/if}
|
||||
{#if hasDelete}
|
||||
<div class="reference-action">
|
||||
<button
|
||||
class="button"
|
||||
on:click={remove}
|
||||
>
|
||||
<Icon icon="trash-can"/>
|
||||
<button class="button" on:click={remove}>
|
||||
<Icon icon="trash-can" />
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -12,22 +12,9 @@
|
||||
<div class="{colorClass} field-content">
|
||||
<div class="d-flex align-items-center text-center flex-wrap">
|
||||
{#each edges[field.name] as edgeRecord}
|
||||
{#if edgeRecord._file?.path}
|
||||
<div
|
||||
class="ms-2 "
|
||||
style="max-width:64px;overflow:hidden;white-space: nowrap;text-overflow: ellipsis;"
|
||||
>
|
||||
<Preview
|
||||
record={edgeRecord}
|
||||
size="small"
|
||||
showFilename={true}
|
||||
/>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="ms-2 ">
|
||||
<PreviewCardSmall record={edgeRecord}/>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="ms-2">
|
||||
<PreviewCardSmall record={edgeRecord} />
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
@@ -43,7 +30,6 @@
|
||||
|
||||
<!-- {/if} -->
|
||||
<style>
|
||||
|
||||
.field-content {
|
||||
max-height: 200px;
|
||||
overflow-y: scroll;
|
||||
|
||||
Reference in New Issue
Block a user