colors and filters

This commit is contained in:
2024-08-17 19:23:19 +03:00
parent db37653748
commit 322962403d
38 changed files with 648 additions and 556 deletions
@@ -52,9 +52,7 @@
on:click={(e) => select(e, suggestion)}
on:keypress={(e) => select(e, suggestion)}
>
<span class="dropdown-item">
{suggestion.label}
</span>
</div>
{/each}
{/if}
+4
View File
@@ -8,6 +8,10 @@
dropdownMenu.classList.remove("hide")
}
export function close() {
dropdownMenu.classList.add("hide")
}
function handleClickOutside() {
dropdownMenu.classList.add("hide")
}
+4 -1
View File
@@ -109,9 +109,12 @@
path: '<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18 17.94 6M18 18 6.06 6"/>',
viewBox: "0 0 24 24",
},
"arrow-left": {
path: '<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 12h14M5 12l4-4m-4 4 4 4"/>',
viewBox: "0 0 24 24",
},
};
export let width = 16;
export let height = 16;
export let icon = "";
+170 -84
View File
@@ -1,8 +1,8 @@
<script>
import Icon from "../../common/Icon.svelte";
import {createEventDispatcher} from "svelte";
import FilterReferenceInput from "./FilterReferenceInput.svelte";
import Dropdown from "../../common/Dropdown.svelte";
import FilterReferenceInput from "./FilterReferenceInput.svelte";
const dispatch = createEventDispatcher();
export let schema;
@@ -11,53 +11,14 @@
export let inModal;
export let modalUrl;
let dropdown;
let search = "";
let systemFieldsFiltered = systemFields;
if (schema.type == "collection") {
if (schema.type === "collection") {
systemFieldsFiltered = systemFields.filter((f) => f.files === false);
}
let filterableFields = [...schema.fields, ...systemFieldsFiltered].filter(
(f) => !["file", "json"].includes(f.info?.name ?? f.ui)
);
let selectedField;
let selectedInput = "";
$: operatorsFiltered = operators.filter(
(o) => o.uis.includes(selectedField?.info?.name) || o.uis[0] == "*"
);
$: selectedOperator = operatorsFiltered[0];
function addFilter(e) {
e.preventDefault();
let filterPrefix = "";
let filterKey;
if (schema.fields.find(f => f.name === selectedField.name)) {
if (selectedField.info.name == "reference" && selectedOperator.name == "eq") {
filterPrefix = "children." + selectedField.name + ".id";
filterKey = `filter[${filterPrefix}]`;
} else {
filterPrefix = "data.";
filterKey = `filter[${filterPrefix + selectedField.name}_${selectedOperator.name}]`;
}
}
const url = new URL(modalUrl ?? window.location.href);
url.searchParams.set("skip", "0");
url.searchParams.set(filterKey, selectedInput);
if (inModal) {
dispatch("refresh", url);
dropdown.hide()
} else {
window.location = url;
}
}
function submitSearch(e) {
e.preventDefault();
let filterKeyValue = search.split("=")[0] ?? "";
@@ -79,63 +40,188 @@
} else {
window.location.replace(url);
}
resetFilters();
}
// New Start
let selectedInput = null;
let activeField = null;
let activeReference = null;
let activeOperator = null;
let activeMenu = "main";
let activeOperators = null;
let dataFields = [...schema.fields, ...systemFieldsFiltered].filter(
(f) => !["file", "json", "reference"].includes(f.info?.name ?? f.ui)
);
let referenceFields = [...schema.fields].filter(
(f) => ["reference"].includes(f.info?.name ?? f.ui)
);
function selectField(e, field) {
activeField = field;
activeOperators = operators.filter(
(o) => o.uis.includes(activeField?.info?.name) || o.uis[0] === "*"
);
}
function selectReference(e, field) {
activeReference = field;
activeOperator = operators.find(o => o.name === "eq")
}
function applyFilter(e) {
e.preventDefault();
let filterPrefix = "";
let filterKey;
let selectedField = activeField ?? activeReference;
if (schema.fields.find(f => f.name === selectedField.name)) {
if (selectedField.info.name === "reference" && activeOperator.name === "eq") {
filterPrefix = "children." + selectedField.name + ".id";
filterKey = `filter[${filterPrefix}]`;
} else {
filterPrefix = "data.";
filterKey = `filter[${filterPrefix + selectedField.name}_${activeOperator.name}]`;
}
}
const url = new URL(modalUrl ?? window.location.href);
url.searchParams.set("skip", "0");
url.searchParams.set(filterKey, selectedInput);
if (inModal) {
dispatch("refresh", url);
dropdown.close()
} else {
window.location.href = url.toString();
}
resetFilters();
}
function resetFilters() {
activeField = null;
activeOperator = null;
activeMenu = "main";
activeReference = null;
}
</script>
<div class="mx-2 d-flex align-items-center">
<Dropdown bind:this={dropdown} width="300">
<div slot="button">
<Icon icon="filter"/>
<span class="ms-1">Filter</span>
</div>
<div class="px-3 py-1 d-flex align-items-center">
<select bind:value={selectedField} class="form-select">
{#each filterableFields as field}
<option value={field}>{field.label}</option>
{/each}
</select>
</div>
<div class="px-3 py-1 d-flex align-items-center">
<select class="form-select" bind:value={selectedOperator}>
{#each operatorsFiltered as operator}
<option value={operator}>{operator.label}</option>
{/each}
</select>
</div>
<div class="px-3 py-1 d-flex align-items-center">
{#if selectedField?.info?.name === "reference" && selectedOperator.name === "eq"}
<FilterReferenceInput field={selectedField} bind:value={selectedInput} on:addFilter={addFilter}/>
{:else}
<div>
<Dropdown bind:this={dropdown}>
<div slot="button">
<Icon icon="filter"/>
<span class="ms-1">Filter</span>
</div>
<div class:hide={activeMenu !== "main"}>
<button class="dropdown-item button" on:click={e => activeMenu = "byField" }>
Filter by field
</button>
<button class="dropdown-item button" on:click={e => activeMenu = "byReference" }>
Filter by Reference
</button>
<button class="dropdown-item button" on:click={e => activeMenu = "advanced" }>
Advanced filter
</button>
</div>
<div class:hide={activeMenu !== "byField"}>
{#if !activeField}
<button class="dropdown-item button" on:click={e => activeMenu = "main" }>
<Icon icon="arrow-left"></Icon>
Back
</button>
{#each dataFields as field}
<button class="dropdown-item button" on:click={e => selectField(e,field)}>
{field.label}
</button>
{/each}
{/if}
{#if activeField && !activeOperator}
<button class="dropdown-item button" on:click={e => activeField = null }>
<Icon icon="arrow-left"></Icon>
Back
</button>
<div class="selected-filter">field: {activeField.label}</div>
{#each activeOperators as operator}
<button class="dropdown-item button" on:click={e => activeOperator = operator }>
{operator.label}
</button>
{/each}
{/if}
{#if activeField && activeOperator}
<button class="dropdown-item button" on:click={e => activeOperator = null }>
<Icon icon="arrow-left"></Icon>
Back
</button>
<div class="selected-filter">field: {activeField.label} operator: {activeOperator.label}</div>
<div class="filter-input">
<input
type="text"
class="form-control"
bind:value={selectedInput}
/>
{/if}
</div>
<div class="px-3 py-1 d-flex align-items-center">
</div>
<button
on:click={addFilter}
class="btn btn-outline-primary"
on:click={applyFilter}
class="button applied-filter"
type="button"
>
Add filter
</button>
</div>
{/if}
<hr/>
<div><h6 class="dropdown-header">Advanced filters</h6></div>
<form on:submit={submitSearch}>
<div class="px-3 py-1 d-flex align-items-center">
<input
bind:value={search}
type="search"
class="form-control"
placeholder="Advanced filters"
required
/>
</div>
<div class:hide={activeMenu !== "byReference"}>
{#if !activeReference}
<button class="dropdown-item button" on:click={e => activeMenu = "main" }>
<Icon icon="arrow-left"></Icon>
Back
</button>
{#each referenceFields as field}
<button class="dropdown-item button" on:click={e => selectReference(e,field)}>
{field.label}
</button>
{/each}
{/if}
{#if activeReference}
<button class="dropdown-item button" on:click={e => activeReference = null }>
<Icon icon="arrow-left"></Icon>
Back
</button>
<div class="selected-filter">field: {activeReference.label}</div>
<div class="mt-2">
<FilterReferenceInput field={activeReference} bind:value={selectedInput}
on:addFilter={applyFilter}/>
</div>
{/if}
</div>
<div class:hide={activeMenu !== "advanced"}>
<button class="dropdown-item button" on:click={e => activeMenu = "main" }>
<Icon icon="arrow-left"></Icon>
Back
</button>
<form on:submit={submitSearch}>
<input
bind:value={search}
type="search"
class="mb-2 mt-2"
placeholder="Advanced filters"
required
/>
<button class="button applied-filter">
Submit
</button>
</form>
</Dropdown>
</div>
</Dropdown>
</div>
@@ -42,35 +42,37 @@
}
</script>
<div class="reference-tags">
<input
type="search"
on:keyup={updateResults}
bind:value={search}
placeholder={"Search for "+field.label}
autocomplete="off"
/>
<input
type="search"
on:keyup={updateResults}
class="form-control dropdown-toggle"
bind:value={search}
placeholder={"Search for "+field.label}
data-bs-toggle="dropdown"
autocomplete="off"
/>
<div class="reference-tags-results">
<div class="dropdown-menu w-100">
{#if searchOptions}
{#each searchOptions as option (option.id)}
<div
class="reference-tags-option"
role="button"
tabindex="0"
on:click={(e) => apply(e, option)}
on:keypress={(e) => apply(e, option)}
>
{previewTitle(channel.schemas, option)}
</div>
{#if searchOptions}
{#each searchOptions as option (option.id)}
<div
on:click={(e) => apply(e, option)}
on:keypress={(e) => apply(e, option)}
>
<span class="dropdown-item">
{previewTitle(channel.schemas, option)}
</span>
</div>
{:else}
Start typing...
{/each}
{/if}
{:else}
<div
class="start-typing">
Start typing...
</div>
{/each}
{/if}
</div>
</div>
-3
View File
@@ -58,10 +58,7 @@
</script>
<dialog bind:this={dialogEl}>
{#if data.schema}
<div class="dialog-header">
<button
type="button"
class="button"
@@ -0,0 +1,38 @@
<script>
import Icon from "../common/Icon.svelte";
let dialogEl;
$: data = {};
export function close(e) {
if (e) {
e.preventDefault();
}
dialogEl.close()
}
export function open() {
dialogEl.showModal()
}
</script>
<dialog bind:this={dialogEl}>
<div class="dialog-header">
<button
on:click|preventDefault={close}
type="button"
class="button close"
aria-label="Close"
>
<Icon icon="close"></Icon>
</button>
</div>
<div class="dialog-body" style="min-width: 900px">
<slot/>
</div>
</dialog>
+1 -10
View File
@@ -73,14 +73,5 @@
border-radius: 12px;
padding: 4px;
}
.file-preview-small{
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 2px;
border-radius: 12px;
padding: 4px;
background: var(--background);
}
</style>
+1 -1
View File
@@ -154,7 +154,7 @@
<div class="record-edit">
<div class="tools-header">
<!-- <Manager managerRecords={recordHistory} {graph}/>-->
<EditHeader {schema} bind:record {isCreateMode} {graph} bind:activeContentTab/>
<EditHeader {schema} bind:record {isCreateMode} bind:activeContentTab/>
{#if isCreateMode}
<button
class="button primary btn-spinner"
+4 -6
View File
@@ -21,19 +21,16 @@
}
})
</script>
<div class="editor-field">
{#each backlinks as backlink}
<div style="margin: 0 0 15px;position: relative;">
<span style="
font-size: 14px;
margin-bottom: 5px;
display: block;
position: absolute;
top: -9px;
left: 9px;
z-index: 9;
"
>In <i>{backlink.field}</i></span>
>In <i>{backlink.field}</i> of</span>
<PreviewReference
record={backlink.record}
hasDelete={false}
@@ -41,3 +38,4 @@
/>
</div>
{/each}
</div>
+37 -52
View File
@@ -1,12 +1,14 @@
<script>
import {afterUpdate, createEventDispatcher, onMount,getContext} from "svelte";
import {afterUpdate, createEventDispatcher, getContext, onMount} from "svelte";
import {isEqual} from "lodash";
import FormField from "./FormField.svelte";
import FilePreview from "./FilePreview.svelte";
import ContentTabs from "./header/ContentTabs.svelte";
import StatusSelect from "./header/StatusSelect.svelte";
import ErrorAlert from "../common/ErrorAlert.svelte";
import EditHeader from "./header/EditHeader.svelte";
import axios from "axios";
import Title from "./header/Title.svelte";
const channel = getContext("channel");
const dispatch = createEventDispatcher();
@@ -135,7 +137,6 @@
resolve(null);
})
.catch(function (error) {
// setOriginalContent();
if (error.response) {
if (typeof error.response.data.error === "string") {
errorMessage = error.response.data.error;
@@ -144,9 +145,6 @@
}
}
resolve(null);
// msgSuccess = null;
// msgError = error.response.data.error;
// submitted = false;
});
});
}
@@ -154,15 +152,45 @@
<svelte:window on:beforeunload={beforeUnload}/>
<div class="inline-edit my-4">
<div class="inline-edit record-edit">
<div class="tools-header">
<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"
/>
Create
</button>
{:else if hasUnsavedData}
<button
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"
/>
Save
</button>
{/if}
</div>
<Title {schema} {record} {isCreateMode}/>
<ErrorAlert message={errorMessage}/>
<div class=" mt-1">
<div class=" mt-4" style="margin-bottom:150px;position:relative;">
<ContentTabs
{schema}
{isCreateMode}
bind:active={activeContentTab}
{record}
/>
<FilePreview {record} {schema}/>
<!-- <fieldset disabled="disabled"> -->
@@ -181,48 +209,5 @@
{/each}
<!-- </fieldset> -->
</div>
<div>
<div class="d-flex mt-3 align-items-center justify-content-center">
{#if schema.hasDrafts}
<StatusSelect bind:status={record.status} {schema}/>
{/if}
{#if isCreateMode}
<button
class="ms-2 btn btn-primary btn-spinner"
on:click={save}
>
<span
class="spinner-border spinner-border-sm"
role="status"
aria-hidden="true"
/>
Add
</button>
{:else}
<button
disabled={!hasUnsavedData}
class="ms-2 btn btn-primary btn-spinner"
on:click={save}
>
<span
class="spinner-border spinner-border-sm"
role="status"
aria-hidden="true"
/>
Save
</button>
{/if}
<button class="ms-2 btn btn-link" on:click={cancel}>
cancel
</button>
</div>
</div>
</div>
<style>
.inline-edit {
padding: 44px;
background-color: #eee;
border-radius: 32px;
}
</style>
@@ -10,7 +10,7 @@
>{field.label}</label
>
{#if field.help}
<small class="help-text">{field.help}</small>
<small class="help-text light-text">{field.help}</small>
{/if}
</div>
<span
@@ -51,7 +51,6 @@
{/if}
<div class="inline-card-wrapper">
<ReferenceInlineButtons
buttonClass="mt-2"
recordId={null}
schemas={collections}
on:insert={insert}
@@ -3,17 +3,15 @@
import Icon from "../../common/Icon.svelte";
import InlineEdit from "../InlineEdit.svelte";
import Dialog from "../../dialog/Dialog.svelte";
import DialogRecord from "../../dialog/DialogRecord.svelte";
import axios from "axios";
const dispatch = createEventDispatcher();
// export let field;
// export let buttonLabel = "";
// export let buttonClass = "";
const channel = getContext("channel");
export let schemas;
export let recordId;
$: showOptions = false;
let browseModal;
let dialogRecord;
let inLineCreateRecord;
function openBrowseModal(e, schema) {
@@ -25,6 +23,7 @@
e.preventDefault();
console.log("Save inline");
inLineCreateRecord = null;
dialogRecord.close()
dispatch("save", {
records: e.detail.records,
after: recordId,
@@ -47,6 +46,7 @@
.get(channel.lucentUrl + "/records/newInline?schema=" + schemaUId)
.then((response) => {
inLineCreateRecord = response.data;
dialogRecord.open()
showOptions = false;
})
.catch((error) => {
@@ -95,53 +95,31 @@
{/if}
{:else}
<div style="display:flex;align-items: center;gap: 4px">
<button
class="button"
on:click={(e) => createInlineReference(e, schemas[0].name)}
>New
</button>
<button
class="button"
on:click={(e) => openBrowseModal(e, schemas[0].name)}
>
<Icon icon="magnifying-glass"/>
</button
>
<button
class="button"
on:click={(e) => createInlineReference(e, schemas[0].name)}
>New
</button>
<button
class="button"
on:click={(e) => openBrowseModal(e, schemas[0].name)}
>
<Icon icon="magnifying-glass"/>
</button
>
</div>
{/if}
{#if inLineCreateRecord}
<InlineEdit
{...inLineCreateRecord}
on:cancel={(e) => (inLineCreateRecord = null)}
on:inlinesaved={save}
/>
{/if}
<DialogRecord bind:this={dialogRecord}>
{#if inLineCreateRecord}
<InlineEdit
{...inLineCreateRecord}
on:cancel={(e) => (inLineCreateRecord = null)}
on:inlinesaved={save}
/>
{/if}
</DialogRecord>
<Dialog bind:this={browseModal} on:insert={insert}/>
<style>
:global(.inline-card-wrapper) {
display: flex;
flex-direction: column;
}
:global(.inline-card-wrapper .inline-card-button) {
visibility: hidden;
}
:global(.inline-card-wrapper .inline-card-button.is-first) {
visibility: visible;
}
:global(.inline-card-wrapper:hover .inline-card-button) {
visibility: visible;
}
.inline-card-button {
/* padding: 0 5px; */
display: inline-block;
z-index: 1;
margin: 10px auto 0;
}
</style>
@@ -114,14 +114,14 @@
on:click={(e) => insert(e, option)}
on:keypress={(e) => insert(e, option)}
>
<span class="dropdown-item">
{previewTitle(channel.schemas, option)}
</span>
{previewTitle(channel.schemas, option)}
</div>
{:else}
Start typing...
<div
class="start-typing">
Start typing...
</div>
{/each}
{/if}
{#if search }
@@ -132,9 +132,7 @@
on:click={(e) => saveNew(e,search)}
on:keypress={(e) => saveNew(e,search)}
>
<span class="dropdown-item">
Add "{search}"
</span>
Add "{search}"
</div>
{/if}
</div>
@@ -143,7 +141,7 @@
{#if references.length > 0}
<div style="display: flex;align-items: center;gap: 4px">
{#each references as record (record.id)}
<span class="autocomplete-selected-value">
<span class="reference-tags-selected-value">
<a
class="record-title"
href="{channel.lucentUrl}/records/{record.id}"
+1 -1
View File
@@ -20,7 +20,7 @@
autocomplete="off"
readonly={field.readonly && !isCreateMode}
/>
<div class="system-help-text">
<div class="system-help-text light-text">
Leave this empty to autogenerate from <i>{field.source}</i>
</div>
{#if errorMessage}
@@ -24,7 +24,7 @@
<div style="display: flex;align-items: center; gap:10px;">
{#if !isCreateMode}
<Dropdown orientation="right">
<Dropdown >
<div slot="button">
<Icon icon="ellipsis"/>
</div>