Compare commits

..

6 Commits

Author SHA1 Message Date
lexx ecba85129d wip editor 2024-10-03 21:17:52 +03:00
lexx b8c5f82e47 wip create/edit 2024-10-02 18:58:30 +03:00
lexx 4389dba49d visible fields for fileschema 2024-10-01 22:41:10 +03:00
lexx fa388ea302 wip content index 2024-10-01 22:31:07 +03:00
lexx 39e7a3aed4 wip file field 2024-09-28 18:36:18 +03:00
lexx 5ed57838fc WIP moving to vanilla js from svelte
Editing record edit and i am in the middle of creating the dropdown component
2024-09-27 23:58:32 +03:00
215 changed files with 4361 additions and 5322 deletions
+32
View File
@@ -0,0 +1,32 @@
# Upgrade from 1.1.* to 1.2.0
## lucent.php config file
There is now an array of commands, accepting more than one.
from
```php
"generateCommand" => env("LUCENT_GENERATE_COMMAND", "generate:static"),
```
to
```php
"commands" => [
"generate:static" => "Build Website",
],
```
## config/filesystems.php
Lucent has its own filesystem.
You should now add:
```
'lucent' => [
'driver' => 'local',
'root' => storage_path('app/public'),
'url' => env('APP_URL').'/storage',
'visibility' => 'public',
'throw' => false,
],
```
+6 -5
View File
@@ -10,15 +10,16 @@
"ext-imagick": "*", "ext-imagick": "*",
"ext-pdo": "*", "ext-pdo": "*",
"php": "^8.3", "php": "^8.3",
"guzzlehttp/guzzle": "^7.2",
"intervention/image": "^2.7",
"phpoption/phpoption": "^1.9", "phpoption/phpoption": "^1.9",
"spatie/image-optimizer": "^1.6", "spatie/image-optimizer": "^1.6",
"staudenmeir/laravel-cte": "^1.10", "staudenmeir/laravel-cte": "^1.0"
"intervention/image": "^3.8",
"guzzlehttp/guzzle": "^7.9"
}, },
"require-dev": { "require-dev": {
"laravel/framework": "^10.48", "phpstan/phpstan": "^1.8",
"phpstan/phpstan": "^1.12" "laravel/framework": "^10.10"
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {
Generated
+342 -278
View File
File diff suppressed because it is too large Load Diff
+41 -6
View File
@@ -3,11 +3,10 @@
return [ return [
"env" => env("LUCENT_ENV", "production"), "env" => env("LUCENT_ENV", "production"),
"schemas_path" => env("LUCENT_SCHEMAS_PATH", "resources/lucent/schemas"), "schemas_path" => env("LUCENT_SCHEMAS_PATH", "resources/lucent/schemas"),
"image_filters_path" => "app/Filters",
"database" => env('LUCENT_DB_CONNECTION', env('DB_CONNECTION', "sqlite")), "database" => env('LUCENT_DB_CONNECTION', env('DB_CONNECTION', "sqlite")),
"name" => env("LUCENT_NAME", "Stoic"), "name" => env("LUCENT_NAME", "Lucent"),
"url" => env("LUCENT_URL", env('APP_URL')), "url" => env("LUCENT_URL", env('APP_URL')),
"preview_target" => env("LUCENT_PREVIEW_TARGET", "previewTarget"), "previewTarget" => env("LUCENT_PREVIEW_TARGET", "previewTarget"),
/* /*
* Make available laravel artisan commands for admin users * Make available laravel artisan commands for admin users
* example: * example:
@@ -18,7 +17,43 @@ return [
* *
* */ * */
"commands" => [], "commands" => [],
"can_invite" => ["admin"], /*
"can_run_commands" => ["admin"], * Image filter will be available both for rich editor fields
"system_user_id" => "" * and throughout your application
*
* example:
* [
* "filterName" => Filter::class
* ]
*
* */
"imageFilters" => [],
"canInvite" => ["admin"],
"canBuild" => ["admin"],
"systemUserId" => "",
"schemaFields" => [
\Lucent\Schema\Ui\Checkbox::class,
\Lucent\Schema\Ui\Color::class,
\Lucent\Schema\Ui\Date::class,
\Lucent\Schema\Ui\Datetime::class,
\Lucent\Schema\Ui\File::class,
\Lucent\Schema\Ui\Json::class,
\Lucent\Schema\Ui\Markdown::class,
\Lucent\Schema\Ui\Number::class,
\Lucent\Schema\Ui\Reference::class,
\Lucent\Schema\Ui\Rich::class,
\Lucent\Schema\Ui\Slug::class,
\Lucent\Schema\Ui\Text::class,
\Lucent\Schema\Ui\Textarea::class
],
"renderers" => [
"row" => [
"file" => \Lucent\Schema\Renderer\Row\File::class,
"slug" => \Lucent\Schema\Renderer\Row\Text::class,
"text" => \Lucent\Schema\Renderer\Row\Text::class,
"checkbox" => \Lucent\Schema\Renderer\Row\Text::class,
"number" => \Lucent\Schema\Renderer\Row\Text::class,
"rich" => \Lucent\Schema\Renderer\Row\Text::class,
]
]
]; ];
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -1,11 +1,11 @@
{ {
"main.js": { "main.js": {
"file": "assets/main-C4XTQmaY.js", "file": "assets/main-BJyanQ7P.js",
"name": "main", "name": "main",
"src": "main.js", "src": "main.js",
"isEntry": true, "isEntry": true,
"css": [ "css": [
"assets/main-BJijircB.css" "assets/main-Dk7njt4m.css"
] ]
} }
} }
+46
View File
@@ -0,0 +1,46 @@
import {onClickOutside} from "./../helpers/clickOutside.js";
export function dropdown() {
document.querySelectorAll(".dropdown").forEach(el => {
dropdownInit(el);
})
}
function dropdownInit(el) {
const button = el.querySelector("button");
const menu = el.querySelector(".dropdown-menu");
button.addEventListener('click', function () {
if (menu.hasAttribute('hidden')) {
this.setAttribute('aria-expanded', 'true');
menu.removeAttribute('hidden');
// Set focus on first link
// will be highlighted for keyboard users
menu.querySelector(".dropdown-item:first-child")?.focus();
} else {
menu.setAttribute('hidden', 'true');
this.setAttribute('aria-expanded', 'false');
}
});
document.addEventListener('keydown', (event) => {
// Ignore IME composition
if (event.isComposing || event.key === "esc") {
return;
}
// Close menu with ESC key
if (event.keyCode === 27) {
if (!menu.hasAttribute('hidden')) {
menu.setAttribute('aria-expanded', 'false');
menu.setAttribute('hidden', 'true');
}
}
});
onClickOutside(menu, ".dropdown", () => menu.hidden = true);
}
-11
View File
@@ -1,11 +0,0 @@
export function debounce(callback, wait) {
let timeoutId = null;
return (...args) => {
window.clearTimeout(timeoutId);
timeoutId = window.setTimeout(() => {
callback.apply(null, args);
}, wait);
};
}
+3 -19
View File
@@ -1,18 +1,18 @@
import {format, formatDistanceToNow, parseJSON} from "date-fns"; import {formatDistanceToNow, parseJSON, format, parse} from "date-fns";
export function friendlyDate(date) { export function friendlyDate(date) {
return formatDistanceToNow(parseJSON(date), {addSuffix: true}); return formatDistanceToNow(parseJSON(date), {addSuffix: true});
} }
export function readableDate(date) { export function readableDate(date) {
if (!date) { if(!date){
return ""; return "";
} }
return format(parseJSON(date), "dd MMM yyyy"); return format(parseJSON(date), "dd MMM yyyy");
} }
export function readableDatetime(date) { export function readableDatetime(date) {
if (!date) { if(!date){
return ""; return "";
} }
@@ -50,19 +50,3 @@ export function clickOutside(node) {
} }
} }
export function uniqueBy(list, callback) {
const itemMap = list.reduce((c, item) => {
c[callback(item)] = item;
return c;
}, {});
return Object.values(itemMap);
}
export function range(start, end) {
var ans = [];
for (let i = start; i <= end; i++) {
ans.push(i);
}
return ans;
}
+8
View File
@@ -0,0 +1,8 @@
export function onClickOutside(ele, closest, cb) {
document.addEventListener('click', function (event) {
if (!event.target.closest(closest)) {
cb(event)
}
}, false);
};
+72
View File
@@ -0,0 +1,72 @@
export function throttle(delay, callback, options) {
const {
noTrailing = false,
noLeading = false,
debounceMode = undefined
} = options || {};
let timeoutID;
let cancelled = false;
let lastExec = 0;
function clearExistingTimeout() {
if (timeoutID) {
clearTimeout(timeoutID);
}
}
function cancel(options) {
const {upcomingOnly = false} = options || {};
clearExistingTimeout();
cancelled = !upcomingOnly;
}
function wrapper(...arguments_) {
let self = this;
let elapsed = Date.now() - lastExec;
if (cancelled) {
return;
}
function exec() {
lastExec = Date.now();
callback.apply(self, arguments_);
}
function clear() {
timeoutID = undefined;
}
if (!noLeading && debounceMode && !timeoutID) {
exec();
}
clearExistingTimeout();
if (debounceMode === undefined && elapsed > delay) {
if (noLeading) {
lastExec = Date.now();
if (!noTrailing) {
timeoutID = setTimeout(debounceMode ? clear : exec, delay);
}
} else {
exec();
}
} else if (noTrailing !== true) {
timeoutID = setTimeout(
debounceMode ? clear : exec,
debounceMode === undefined ? delay - elapsed : delay
);
}
}
wrapper.cancel = cancel;
return wrapper;
}
export function debounce(delay, callback, options) {
const {atBegin = false} = options || {};
return throttle(delay, callback, {debounceMode: atBegin !== false});
}
+16
View File
@@ -1,8 +1,24 @@
import {axiosInstance} from "./bootstrap"; import {axiosInstance} from "./bootstrap";
import "../sass/app.scss";
import Account from "./svelte/Account.svelte"; import Account from "./svelte/Account.svelte";
import Channel from "./svelte/Channel.svelte"; import Channel from "./svelte/Channel.svelte";
import Mustache from "mustache"; import Mustache from "mustache";
import 'htmx.org'; import 'htmx.org';
import {dropdown} from "./components/dropdown.js";
import {colorPicker} from "./recordEditor/colorPicker.js";
import {sortReferences} from "./recordEditor/sortReferences.js";
import {recordDialog} from "./recordEditor/recordDialog.js";
import {createRecordEntry} from "./recordEditor/createRecordEntry.js";
import {editRecordEntry} from "./recordEditor/editRecordEntry.js";
addEventListener("load", (event) => {
dropdown()
colorPicker()
sortReferences()
recordDialog()
createRecordEntry()
editRecordEntry()
});
Mustache.escape = function (value) { Mustache.escape = function (value) {
return value; return value;
+13
View File
@@ -0,0 +1,13 @@
export function colorPicker() {
document.querySelectorAll(".color-picker").forEach(el => {
colorPickerInit(el);
})
}
function colorPickerInit(el){
const colorInput = el.querySelector("[type=color]");
const textInput = el.querySelector("[type=text]");
colorInput.addEventListener("change",(e) => textInput.value = colorInput.value);
textInput.addEventListener("change",(e) => colorInput.value = textInput.value);
}
@@ -0,0 +1,49 @@
import axios from "axios";
export function createRecordEntry() {
const createButton = document.getElementById("record-create-button");
if(!createButton){
return;
}
createButton.addEventListener("click", save)
}
function save(e) {
e.preventDefault();
const recordForm = document.getElementById("record-form");
let validationErrors = null;
let errorMessage = "";
const urlParams = new URLSearchParams(window.location.search);
const schemaName = urlParams.get("schema")
console.log("SAVE: Attempt");
let formData = new FormData(recordForm)
axios
.post("/lucent/records", {
schema: schemaName,
data: Object.fromEntries(formData),
status: "draft",
// edges: graph.edges,
isCreateMode: true,
})
.then(function (response) {
console.log("SAVE: SAVED");
window.location = "/lucent/recordss/" + record.id;
return;
})
.catch(function (error) {
if (!error?.response) {
}
if (typeof error?.response.data.error === "string") {
errorMessage = error.response.data.error;
} else {
validationErrors = error.response.data.error;
console.log(validationErrors)
}
});
}
+44
View File
@@ -0,0 +1,44 @@
import axios from "axios";
export function editRecordEntry() {
const saveButton = document.getElementById("record-save-button");
if(!saveButton){
return;
}
saveButton.addEventListener("click", save)
}
function save(e) {
e.preventDefault();
const recordForm = document.getElementById("record-form");
// let validationErrors = null;
// let errorMessage = "";
console.log("SAVE: Attempt");
//
let formData = new FormData(recordForm)
//
axios
.post("/lucent/records", {
id: recordForm.dataset.recordId,
data: Object.fromEntries(formData),
status: "draft",
// edges: graph.edges,
isCreateMode: false,
})
.then(function (response) {
console.log("SAVE: SAVED");
})
.catch(function (error) {
// if (!error?.response) {
// }
// if (typeof error?.response.data.error === "string") {
// errorMessage = error.response.data.error;
// } else {
// validationErrors = error.response.data.error;
// console.log(validationErrors)
// }
});
}
+32
View File
@@ -0,0 +1,32 @@
import axios from "axios";
export function recordDialog() {
document.querySelectorAll("[data-open-modal]").forEach(el => {
const schema = el.dataset.openModal
el.addEventListener("click", e => {
e.preventDefault()
load(schema)
})
})
}
function load(schema) {
axios
.get("/lucent/content/" + schema)
.then((response) => {
const dialogWrapperEl = document.createElement("div");
dialogWrapperEl.innerHTML = response.data;
document.body.appendChild(dialogWrapperEl);
const dialogEl = dialogWrapperEl.querySelector("dialog");
dialogEl.showModal();
dialogWrapperEl.querySelector(".close").addEventListener("click", e => dialogEl.close());
dialogEl.addEventListener("close", (event) => {
dialogWrapperEl.remove();
});
})
.catch((error) => console.log(error));
}
+19
View File
@@ -0,0 +1,19 @@
import Sortable from "sortablejs";
export function sortReferences() {
document.querySelectorAll(".color-picker").forEach(el => {
let options = {
animation: 150, // ms, animation speed moving items when sorting, `0` — without animation
easing: "cubic-bezier(1, 0, 0, 1)",
direction: 'vertical',
onUpdate: function (/**Event*/ evt) {
// dispatch("update", {
// source: evt.oldIndex,
// target: evt.newIndex,
// });
}
};
Sortable.create(el, options);
})
}
+2
View File
@@ -3,6 +3,7 @@
import Login from "./account/Login.svelte"; import Login from "./account/Login.svelte";
import Verify from "./account/Verify.svelte"; import Verify from "./account/Verify.svelte";
import Profile from "./account/Profile.svelte"; import Profile from "./account/Profile.svelte";
import SetupIndex from "./setup/Index.svelte";
import {setContext} from "svelte"; import {setContext} from "svelte";
const components = { const components = {
@@ -10,6 +11,7 @@
login: Login, login: Login,
verify: Verify, verify: Verify,
profile: Profile, profile: Profile,
setup: SetupIndex,
}; };
export let title; export let title;
+5 -5
View File
@@ -38,13 +38,13 @@
<div class="main-wrapper"> <div class="main-wrapper">
<div class="sidebar-content"> <div class="sidebar-content">
<Navbar schema={data.schema}/> <Navbar schema={data.schema}/>
</div> </div>
<div class="main-content"> <div class="main-content">
<Header /> <Header/>
<svelte:component this={components[view]} {title} {...data}/> <svelte:component this={components[view]} {title} {...data}/>
</div> </div>
</div> </div>
+1 -1
View File
@@ -1,7 +1,6 @@
<script> <script>
import {getContext} from "svelte"; import {getContext} from "svelte";
import SpinnerButton from "../common/SpinnerButton.svelte"; import SpinnerButton from "../common/SpinnerButton.svelte";
import axios from "axios";
const channel = getContext("channel"); const channel = getContext("channel");
let email = ""; let email = "";
@@ -15,6 +14,7 @@
email: email, email: email,
}) })
.then((response) => { .then((response) => {
console.log(response)
message = "You will receive an email with a login link" message = "You will receive an email with a login link"
}) })
.catch((error) => { .catch((error) => {
+2 -1
View File
@@ -4,7 +4,6 @@
import Avatar from "./Avatar.svelte"; import Avatar from "./Avatar.svelte";
import {getContext} from "svelte"; import {getContext} from "svelte";
import SuccessAlert from "../common/SuccessAlert.svelte"; import SuccessAlert from "../common/SuccessAlert.svelte";
import axios from "axios";
const user = getContext("user"); const user = getContext("user");
const channel = getContext("channel"); const channel = getContext("channel");
@@ -26,6 +25,7 @@
}) })
.catch((error) => { .catch((error) => {
errorMessage = error.response?.data.error; errorMessage = error.response?.data.error;
console.log({errorMessage});
}); });
} }
@@ -42,6 +42,7 @@
}) })
.catch((error) => { .catch((error) => {
errorMessage = error.response?.data.error; errorMessage = error.response?.data.error;
console.log({errorMessage});
}); });
} }
</script> </script>
+1
View File
@@ -22,6 +22,7 @@
anchorEl.scrollIntoView() anchorEl.scrollIntoView()
} }
eventSource.onerror = (e) => { eventSource.onerror = (e) => {
console.log(e)
eventSource.close(); eventSource.close();
inProgress = false; inProgress = false;
} }
+1 -8
View File
@@ -124,17 +124,10 @@
"italic": { "italic": {
path: '<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m8.874 19 6.143-14M6 19h6.33m-.66-14H18"/>', path: '<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m8.874 19 6.143-14M6 19h6.33m-.66-14H18"/>',
viewBox: "0 0 24 24", viewBox: "0 0 24 24",
},
"undo": {
path: '<path fill-rule="evenodd" clip-rule="evenodd" d="M7.53033 3.46967C7.82322 3.76256 7.82322 4.23744 7.53033 4.53033L5.81066 6.25H15C18.1756 6.25 20.75 8.82436 20.75 12C20.75 15.1756 18.1756 17.75 15 17.75H8.00001C7.58579 17.75 7.25001 17.4142 7.25001 17C7.25001 16.5858 7.58579 16.25 8.00001 16.25H15C17.3472 16.25 19.25 14.3472 19.25 12C19.25 9.65279 17.3472 7.75 15 7.75H5.81066L7.53033 9.46967C7.82322 9.76256 7.82322 10.2374 7.53033 10.5303C7.23744 10.8232 6.76256 10.8232 6.46967 10.5303L3.46967 7.53033C3.17678 7.23744 3.17678 6.76256 3.46967 6.46967L6.46967 3.46967C6.76256 3.17678 7.23744 3.17678 7.53033 3.46967Z" fill="#1C274C"/>',
viewBox: "0 0 24 24",
},
"destroy": {
path: '<path d="M17 7L15 9" stroke="#1C274C" stroke-width="1.5" stroke-linecap="round"/><path d="M19.5 7.5L20.5 8" stroke="#1C274C" stroke-width="1.5" stroke-linecap="round"/><path d="M16 3.5L16.5 4.5" stroke="#1C274C" stroke-width="1.5" stroke-linecap="round"/><path d="M19 5L20 4" stroke="#1C274C" stroke-width="1.5" stroke-linecap="round"/><path\n'+' d="M5.75 8.00337C6.85315 7.36523 8.13392 7 9.5 7C13.6421 7 17 10.3579 17 14.5C17 18.6421 13.6421 22 9.5 22C5.35786 22 2 18.6421 2 14.5C2 13.1339 2.36523 11.8532 3.00337 10.75" stroke="#1C274C" stroke-width="1.5" stroke-linecap="round"/>',
viewBox: "0 0 24 24",
} }
}; };
export let width = 16; export let width = 16;
export let height = 16; export let height = 16;
export let icon = ""; export let icon = "";
@@ -1,6 +1,5 @@
<script> <script>
import {getContext} from "svelte"; import {getContext} from "svelte";
import axios from "axios";
const channel = getContext("channel"); const channel = getContext("channel");
export let selected; export let selected;
@@ -17,7 +16,7 @@
window.location.reload(); window.location.reload();
}) })
.catch((error) => { .catch((error) => {
console.error(error); console.log(error);
}); });
} }
@@ -25,13 +24,13 @@
axios axios
.post(channel.lucentUrl + "/records/status/" + status, { .post(channel.lucentUrl + "/records/status/" + status, {
schemaName: schema.name, schemaName: schema.name,
records: selected.map((s) => s.id), records: selected
}) })
.then((response) => { .then((response) => {
window.location.reload(); window.location.reload();
}) })
.catch((error) => { .catch((error) => {
console.error(error); console.log(error);
}); });
} }
</script> </script>
@@ -51,6 +50,20 @@
</button </button
> >
{#if filter["status_in"] === "trashed"} {#if filter["status_in"] === "trashed"}
<button
on:click|preventDefault={(e) => changeStatus(e, "published")}
type="button"
class="button">Publish
</button
>
{#if schema.hasDrafts}
<button
on:click|preventDefault={(e) => changeStatus(e, "draft")}
type="button"
class="button">Make Draft
</button
>
{/if}
<button <button
on:click|preventDefault={deleteRecords} on:click|preventDefault={deleteRecords}
type="button" type="button"
@@ -6,9 +6,11 @@
import Number from "./elements/Number.svelte"; import Number from "./elements/Number.svelte";
import Text from "./elements/Text.svelte"; import Text from "./elements/Text.svelte";
import Url from "./elements/Url.svelte";
import Date from "./elements/Date.svelte"; import Date from "./elements/Date.svelte";
import Datetime from "./elements/Datetime.svelte"; import Datetime from "./elements/Datetime.svelte";
import File from "./elements/File.svelte"; import File from "./elements/File.svelte";
import Uuid from "./elements/UUID.svelte";
import Rich from "./elements/Rich.svelte"; import Rich from "./elements/Rich.svelte";
const renderElements = { const renderElements = {
@@ -20,8 +22,10 @@
checkbox: Checkbox, checkbox: Checkbox,
reference: Reference, reference: Reference,
number: Number, number: Number,
url: Url,
date: Date, date: Date,
datetime: Datetime, datetime: Datetime,
uuid: Uuid,
file: File, file: File,
}; };
export let field; export let field;
+2 -1
View File
@@ -11,7 +11,8 @@
/* max-width: 128px; */ /* max-width: 128px; */
max-height: 24px; max-height: 24px;
text-overflow: ellipsis; text-overflow: ellipsis;
/* white-space: nowrap; */
overflow: hidden; overflow: hidden;
/* white-space: nowrap; */
} }
</style> </style>
@@ -0,0 +1,10 @@
<script>
export let value;
</script>
<span
class="badge rounded-pill bg-primary bg-opacity-75"
style="max-width:64px; overflow:hidden; white-space: nowrap; text-overflow: ellipsis;"
title={value}
data-bs-toggle="tooltip"
>{value}</span
>
@@ -0,0 +1,5 @@
<script>
export let value;
</script>
<a href={value} target="_blank">{value}</a>
@@ -1,7 +1,7 @@
<script> <script>
import { createEventDispatcher } from "svelte"; import { createEventDispatcher } from "svelte";
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
import { range } from "../../../helpers.js"; import { range } from "lodash";
import NavItem from "./NavItem.svelte"; import NavItem from "./NavItem.svelte";
export let inModal; export let inModal;
export let modalUrl; export let modalUrl;
@@ -1,8 +1,8 @@
<script> <script>
import {createEventDispatcher, getContext} from "svelte"; import {createEventDispatcher, getContext} from "svelte";
import {debounce} from "../../../debounce.js"; import {debounce} from "lodash";
import {previewTitle} from "../../records/Preview"; import {previewTitle} from "../../records/Preview";
import axios from "axios";
const channel = getContext("channel"); const channel = getContext("channel");
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
+1 -1
View File
@@ -5,7 +5,7 @@ export function sortByField(from, to, edges, fieldName, references) {
if (from === to) { if (from === to) {
return edges; return edges;
} }
let referenceIds = references.map(r => r.record.id); let referenceIds = references.map(r => r.id);
let edgesTosort = edges?.filter((ed) => ed.field === fieldName && ed.depth === 1 && referenceIds.includes(ed.target)) ?? []; 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)) ?? []; let remainingEdge = edges?.filter((ed) => !(ed.field === fieldName && ed.depth === 1)) ?? [];
+2 -4
View File
@@ -2,8 +2,7 @@ export function imgurl(channel, record) {
if (record._file.mime === "image/svg+xml") { if (record._file.mime === "image/svg+xml") {
return fileurl(channel, record); return fileurl(channel, record);
} }
const pathAr = record._file.path.split("/"); return channel.disks[record._file.disk] + `/thumbs/${record._file.path}`;
return channel.disks[record._file.disk] + `/${pathAr[0]}/thumbs/${pathAr[1]}`;
} }
export function fileurl(channel, record) { export function fileurl(channel, record) {
@@ -18,8 +17,7 @@ export function htmlurl(channel, record, preset) {
if (record._file.width > 0) { if (record._file.width > 0) {
let presetUrl = url; let presetUrl = url;
if (preset) { if (preset) {
const pathAr = record._file.path.split("/"); presetUrl = channel.disks[record._file.disk] + `/templates/${preset}/${record._file.path}`;
presetUrl = channel.disks[record._file.disk] + `/${pathAr[0]}/templates/${preset}/${pathAr[1]}`;
} }
html = `<img src="${presetUrl}" alt="${record._file.path}" />` html = `<img src="${presetUrl}" alt="${record._file.path}" />`
} else if (record._file.mime === "image/svg+xml") { } else if (record._file.mime === "image/svg+xml") {
-39
View File
@@ -1,39 +0,0 @@
<script>
import {getContext} from "svelte";
import Icon from "../common/Icon.svelte";
import Folder from "./Folder.svelte";
const channel = getContext("channel");
export let folder;
export let schema;
export let expanded = folder.shouldExpand;
function toggleExpand() {
expanded = !expanded;
}
</script>
<div class="sidebar-folder">
{#if folder.name !== ""}
<button class="sidebar-header" tabindex="0" on:click={toggleExpand}>
{folder.name.replaceAll("_", " ") ?? "Main"}
{#if expanded}
<Icon icon="circle-chevron-up"></Icon>
{:else}
<Icon icon="circle-chevron-down"></Icon>
{/if}
</button>
{/if}
{#if expanded}
{#each folder.folders as aFolder}
<Folder folder={aFolder} schema={schema}></Folder>
{/each}
{#each folder.files as aSchema}
<a class="sidebar-item" class:active={aSchema.name === schema?.name}
aria-current="page"
href="{channel.lucentUrl}/content/{aSchema.name}">{aSchema.label}</a>
{/each}
{/if}
</div>
+7 -1
View File
@@ -5,6 +5,7 @@
const channel = getContext("channel"); const channel = getContext("channel");
const user = getContext("user"); const user = getContext("user");
console.log( channel.commands)
</script> </script>
@@ -20,7 +21,12 @@
</Dropdown> </Dropdown>
{/if} {/if}
<!-- <div>-->
<!-- <form method="GET">-->
<!-- <input type="search" name="filter[search_regex]" placeholder="Search"-->
<!-- class="form-control" required/>-->
<!-- </form>-->
<!-- </div>-->
<a href="{channel.lucentUrl}/profile"> <a href="{channel.lucentUrl}/profile">
<Avatar side="28" name={user.name}/> <Avatar side="28" name={user.name}/>
</a> </a>
+23 -29
View File
@@ -1,39 +1,14 @@
<script> <script>
import NavbarMenu from "./NavbarMenu.svelte";
import {getContext} from "svelte"; import {getContext} from "svelte";
import Folder from "./Folder.svelte";
export let schema; export let schema;
const channel = getContext("channel"); const channel = getContext("channel");
const readableSchemas = getContext("readableSchemas"); const readableSchemas = getContext("readableSchemas");
function addToFolder(tree, folderPath, aSchema) { const fileSchemas = readableSchemas.filter((sc) => sc.type === "files");
let shouldExpand = aSchema.name === schema?.name; const otherSchemas = readableSchemas.filter((sc) => !sc.isEntry && sc.type === "collection");
if (folderPath === "") {
tree.files.push(aSchema)
return tree
}
const folderNames = folderPath.split(".");
folderNames.forEach(folderName => {
let queriedFolder = tree.folders.find(folder => folder.name === folderName)
if (!queriedFolder) {
queriedFolder = {name: folderName, files: [], folders: [], shouldExpand: shouldExpand};
}
folderNames.shift()
let remainingFolderPath = folderNames.join(".");
queriedFolder = addToFolder(queriedFolder, remainingFolderPath, aSchema)
tree.folders = tree.folders.filter(f => f.name !== queriedFolder.name)
tree.folders.push(queriedFolder);
})
return tree;
}
const schemaTree = readableSchemas.reduce((carry, schema) => {
carry = addToFolder(carry, schema.folder,schema)
return carry;
}, {name: "", files: [], folders: [], shouldExpand:true});
</script> </script>
<div class="sidebar-top"> <div class="sidebar-top">
<a class="logo" href="{channel.lucentUrl}">{channel.name}</a> <a class="logo" href="{channel.lucentUrl}">{channel.name}</a>
@@ -41,5 +16,24 @@
</a> </a>
</div> </div>
<div class="sidebar"> <div class="sidebar">
<Folder folder={schemaTree} {schema} ></Folder>
<NavbarMenu
title="Content"
schemas={ readableSchemas.filter((sc) => sc.isEntry)}
schema={schema}
expanded={true}
/>
<NavbarMenu
title="Files"
schemas={ fileSchemas}
schema={schema}
/>
<NavbarMenu
title="Other"
schemas={ otherSchemas}
schema={schema}
/>
</div> </div>
+34
View File
@@ -0,0 +1,34 @@
<script>
import {getContext} from "svelte";
import Icon from "../common/Icon.svelte";
const channel = getContext("channel");
export let schemas;
export let title;
export let schema;
export let expanded = false;
if(schemas.find(s => s.name === schema?.name)){
expanded = true;
}
function toggleExpand(){
expanded = !expanded;
}
</script>
<button class="sidebar-header" tabindex="0" on:click={toggleExpand}>
{title}
{#if expanded}
<Icon icon="circle-chevron-up"></Icon>
{:else}
<Icon icon="circle-chevron-down"></Icon>
{/if}
</button>
{#if expanded}
{#each schemas as aschema}
<a class="sidebar-item" class:active={aschema.name === schema?.name}
aria-current="page"
href="{channel.lucentUrl}/content/{aschema.name}">{aschema.label}</a>
{/each}
{/if}
+174
View File
@@ -0,0 +1,174 @@
<script>
import {onDestroy, onMount} from 'svelte';
import {Editor} from '@tiptap/core'
import Document from '@tiptap/extension-document'
import Paragraph from '@tiptap/extension-paragraph'
import Dropcursor from '@tiptap/extension-dropcursor'
import Text from '@tiptap/extension-text'
import Heading from '@tiptap/extension-heading'
import HardBreak from '@tiptap/extension-hard-break'
import Blockquote from '@tiptap/extension-blockquote';
import CodeBlock from '@tiptap/extension-code-block';
import Bold from '@tiptap/extension-bold';
import BulletList from '@tiptap/extension-bullet-list';
import Code from '@tiptap/extension-code';
import History from '@tiptap/extension-history';
import Italic from '@tiptap/extension-italic';
import ListItem from '@tiptap/extension-list-item';
import OrderedList from '@tiptap/extension-ordered-list';
import Strike from '@tiptap/extension-strike';
import Table from '@tiptap/extension-table';
import TableRow from '@tiptap/extension-table-row';
import TableCell from '@tiptap/extension-table-cell';
import TableHeader from '@tiptap/extension-table-header';
import Underline from '@tiptap/extension-underline';
import Image from '@tiptap/extension-image';
import Icon from "../common/Icon.svelte";
let element;
let editor;
export let value = "";
onMount(() => {
editor = new Editor({
element: element,
extensions: [
Document,
Paragraph,
Text,
Bold,
ListItem,
BulletList,
Code,
CodeBlock,
History,
Italic,
HardBreak,
OrderedList,
Strike,
Table,
TableRow,
TableCell,
TableHeader,
Underline,
Dropcursor,
Image,
Heading.configure({
levels: [1, 2, 3],
}),
Blockquote
],
content: value,
editable: true,
onTransaction: () => {
// force re-render so `editor.isActive` works as expected
editor = editor;
},
onUpdate: ({editor}) => {
value = editor.getHTML()
},
});
});
onDestroy(() => {
if (editor) {
editor.destroy();
}
});
export function insertMedia(info){
editor.chain().focus().setImage({ src: info.url }).run()
}
</script>
{#if editor}
<div class="editor-toolbar">
<button
class="button"
on:click={() => editor.chain().focus().toggleHeading({ level: 1 }).run()}
class:active={editor.isActive('heading', { level: 1 })}
>
H1
</button>
<button
class="button"
on:click={() => editor.chain().focus().toggleHeading({ level: 2 }).run()}
class:active={editor.isActive('heading', { level: 2 })}
>
H2
</button>
<button
class="button"
on:click={() => editor.chain().focus().toggleBold().run()}
class:active={editor.isActive('bold')}
>
B
</button>
<button
class="button"
on:click={() => editor.chain().focus().toggleItalic().run()}
class:active={editor.isActive('italic')}
>
<em>IT</em>
</button>
<button
class="button"
on:click={() => editor.chain().focus().toggleUnderline().run()}
class:active={editor.isActive('underline')}
>
<u>U</u>
</button>
<button
class="button"
on:click={() => editor.chain().focus().toggleStrike().run()}
class:active={editor.isActive('strike')}
>
<s>S</s>
</button>
<button
class="button"
on:click={() => editor.commands.unsetAllMarks()}
>
Clear
</button>
<button
class="button"
on:click={() => editor.chain().focus().toggleCode().run()}
class:active={editor.isActive('code')}
>
Code
</button>
<button
class="button"
on:click={() => editor.chain().focus().toggleBulletList().run()}
class:active={editor.isActive('bulletList')}
>
<Icon icon="list"></Icon>
</button>
<button
class="button"
on:click={() => editor.chain().focus().toggleOrderedList().run()}
class:active={editor.isActive('orderedList')}
>
<Icon icon="ordered-list"></Icon>
</button>
<button
class="button"
on:click={() => editor.chain().focus().toggleBlockquote().run()}
class:active={editor.isActive('blockquote')}
>
""
</button>
<button
class="button"
on:click={() => editor.chain().focus().toggleCodeBlock().run()}
class:active={editor.isActive('codeBlock')}
>
cb
</button>
</div>
{/if}
<div bind:this={element} class="content"/>
+1
View File
@@ -1,6 +1,7 @@
<script> <script>
import {onDestroy, onMount} from "svelte"; import {onDestroy, onMount} from "svelte";
import Trix from "trix" import Trix from "trix"
import "trix/dist/trix.css"
export let value = ""; export let value = "";
export let field; export let field;
@@ -22,6 +22,9 @@
e.preventDefault(); e.preventDefault();
let newRoles = [...member.roles, aRole]; let newRoles = [...member.roles, aRole];
console.log(member.roles)
console.log(aRole)
console.log(newRoles)
dispatch("update", { dispatch("update", {
user: member.id, user: member.id,
roles: newRoles, roles: newRoles,
+5 -2
View File
@@ -1,5 +1,6 @@
<script> <script>
import {afterUpdate, getContext, onMount} from "svelte"; import {afterUpdate, getContext, onMount} from "svelte";
import {isEqual} from "lodash";
import axios from "axios"; import axios from "axios";
import EditHeader from "./header/EditHeader.svelte" import EditHeader from "./header/EditHeader.svelte"
import FilePreview from "./FilePreview.svelte" import FilePreview from "./FilePreview.svelte"
@@ -9,7 +10,6 @@
import Info from "./Info.svelte" import Info from "./Info.svelte"
import ErrorAlert from "../common/ErrorAlert.svelte" import ErrorAlert from "../common/ErrorAlert.svelte"
import Title from "./header/Title.svelte"; import Title from "./header/Title.svelte";
import {hasDataChanged} from "./editor.js";
const channel = getContext("channel"); const channel = getContext("channel");
@@ -74,7 +74,10 @@
} }
function checkUnsavedData() { function checkUnsavedData() {
return hasDataChanged(isCreateMode,originalContent,{ if (isCreateMode) {
return false;
}
return !isEqual(originalContent, {
data: record.data, data: record.data,
schema: record.schema, schema: record.schema,
status: record.status, status: record.status,
+4
View File
@@ -5,7 +5,9 @@
import Color from "./elements/Color.svelte"; import Color from "./elements/Color.svelte";
import Checkbox from "./elements/Checkbox.svelte"; import Checkbox from "./elements/Checkbox.svelte";
import Number from "./elements/Number.svelte"; import Number from "./elements/Number.svelte";
import Url from "./elements/Url.svelte";
import Date from "./elements/Date.svelte"; import Date from "./elements/Date.svelte";
import UUID from "./elements/UUID.svelte";
import File from "./elements/File.svelte"; import File from "./elements/File.svelte";
import Textarea from "./elements/Textarea.svelte"; import Textarea from "./elements/Textarea.svelte";
import Datetime from "./elements/Datetime.svelte"; import Datetime from "./elements/Datetime.svelte";
@@ -23,8 +25,10 @@
color: Color, color: Color,
checkbox: Checkbox, checkbox: Checkbox,
number: Number, number: Number,
url: Url,
date: Date, date: Date,
datetime: Datetime, datetime: Datetime,
uuid: UUID,
json: Json, json: Json,
markdown: Markdown, markdown: Markdown,
}; };
+2 -4
View File
@@ -13,16 +13,14 @@
} }
let backlinks = graph.parentEdges.map(edge => { let backlinks = graph.parentEdges.map(edge => {
const parentRecord = graph.records.find( record => record.id === edge.source); let schema = channel.schemas.find((s) => s.name === edge.sourceSchema);
let schema = channel.schemas.find((s) => s.name === parentRecord.schema);
let edgeField = findEdgeField(schema,edge.field); let edgeField = findEdgeField(schema,edge.field);
if(!edgeField){ if(!edgeField){
return null; return null;
} }
return { return {
field: edgeField.label, field: edgeField.label,
record: parentRecord record: graph.records.find( record => record.id === edge.source)
} }
}).filter( edgeOrNull => !!edgeOrNull) }).filter( edgeOrNull => !!edgeOrNull)
</script> </script>
+2 -3
View File
@@ -2,12 +2,11 @@
import {friendlyDate} from "../../helpers"; import {friendlyDate} from "../../helpers";
import Avatar from "../account/Avatar.svelte"; import Avatar from "../account/Avatar.svelte";
import {usernameById} from "../account/users"; import {usernameById} from "../account/users";
import {isEqual} from "lodash";
import Icon from "../common/Icon.svelte"; import Icon from "../common/Icon.svelte";
import RevisionCell from "./revisions/RevisionCell.svelte"; import RevisionCell from "./revisions/RevisionCell.svelte";
import {getContext} from "svelte"; import {getContext} from "svelte";
import RevisionEdgeRow from "./revisions/RevisionEdgeRow.svelte"; import RevisionEdgeRow from "./revisions/RevisionEdgeRow.svelte";
import axios from "axios";
import {hasDataChanged} from "./editor.js";
const channel = getContext("channel"); const channel = getContext("channel");
export let record; export let record;
@@ -61,7 +60,7 @@
selectedRevision = revision; selectedRevision = revision;
fieldsWithDiff = schema.fields.filter((f) => { fieldsWithDiff = schema.fields.filter((f) => {
return hasDataChanged(false,selectedRevision.data[f.name], record.data[f.name]); return !isEqual(selectedRevision.data[f.name], record.data[f.name]);
}); });
getEdgesByField(fieldsWithDiff, revision) getEdgesByField(fieldsWithDiff, revision)
revisionSection.scrollIntoView(); revisionSection.scrollIntoView();
+6 -2
View File
@@ -1,6 +1,7 @@
<script> <script>
import {afterUpdate, createEventDispatcher, getContext, onMount} from "svelte"; import {afterUpdate, createEventDispatcher, getContext, onMount} from "svelte";
import {hasDataChanged} from "./editor.js";
import {isEqual} from "lodash";
import FormField from "./FormField.svelte"; import FormField from "./FormField.svelte";
import FilePreview from "./FilePreview.svelte"; import FilePreview from "./FilePreview.svelte";
import ContentTabs from "./header/ContentTabs.svelte"; import ContentTabs from "./header/ContentTabs.svelte";
@@ -79,7 +80,10 @@
} }
function checkUnsavedData() { function checkUnsavedData() {
return hasDataChanged(isCreateMode, originalContent, { if (isCreateMode) {
return false;
}
return !isEqual(originalContent, {
data: record.data, data: record.data,
schema: record.schema, schema: record.schema,
status: record.status, status: record.status,
-6
View File
@@ -1,6 +0,0 @@
export function hasDataChanged(isCreateMode, originalContent, newContent){
if (isCreateMode) {
return false;
}
return JSON.stringify(originalContent) !== JSON.stringify(newContent);
}
@@ -1,6 +1,8 @@
<script> <script>
import {onMount} from "svelte"; import {onMount} from "svelte";
import flatpickr from "flatpickr"; import flatpickr from "flatpickr";
import "flatpickr/dist/flatpickr.css";
import "flatpickr/dist/themes/light.css";
import {getErrorMessage} from "./errorMessage"; import {getErrorMessage} from "./errorMessage";
export let field; export let field;
@@ -1,6 +1,8 @@
<script> <script>
import {onMount} from "svelte"; import {onMount} from "svelte";
import flatpickr from "flatpickr"; import flatpickr from "flatpickr";
import "flatpickr/dist/flatpickr.css";
import "flatpickr/dist/themes/light.css";
import {getErrorMessage} from "./errorMessage"; import {getErrorMessage} from "./errorMessage";
export let field; export let field;
+11 -28
View File
@@ -4,13 +4,7 @@
import PreviewFile from "../previews/PreviewFile.svelte"; import PreviewFile from "../previews/PreviewFile.svelte";
import Dropdown from "../../common/Dropdown.svelte"; import Dropdown from "../../common/Dropdown.svelte";
import Dialog from "../../dialog/Dialog.svelte"; import Dialog from "../../dialog/Dialog.svelte";
import { import {insertEdges} from "./reference.js";
fullDeleteRecord,
graphToReferences,
insertEdges,
removeReferenceFromGraph,
restoreReferenceToGraph
} from "./reference.js";
import {getContext} from "svelte"; import {getContext} from "svelte";
const channel = getContext("channel"); const channel = getContext("channel");
@@ -18,7 +12,11 @@
export let record; export let record;
export let graph export let graph
let browseModal; let browseModal;
$: references = graphToReferences(graph, record, field) $: 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 collections = channel.schemas.filter((aschema) => let collections = channel.schemas.filter((aschema) =>
field.collections.includes(aschema.name) field.collections.includes(aschema.name)
@@ -26,17 +24,9 @@
function removeReference(e) { function removeReference(e) {
e.preventDefault(); e.preventDefault();
graph.edges = removeReferenceFromGraph(graph, field, e.detail) graph.edges = graph.edges.filter(
} (edge) => !(edge.target === e.detail && edge.field === field.name)
);
function restoreReference(e) {
e.preventDefault();
graph.edges = restoreReferenceToGraph(graph, field, e.detail)
}
function fullDelete(e) {
e.preventDefault();
graph.edges = fullDeleteRecord(channel,graph, field, e.detail)
} }
function openBrowseModal(e, schema) { function openBrowseModal(e, schema) {
@@ -83,17 +73,10 @@
</div> </div>
{#if references.length > 0} {#if references.length > 0}
<Sortable sortableClass="mt-3" on:update={reorder}> <Sortable sortableClass="mt-3" on:update={reorder}>
{#each references as reference (reference.record.id)} {#each references as reference (reference.id)}
<!--This div helps the sorting thing--> <!--This div helps the sorting thing-->
<div> <div>
<PreviewFile <PreviewFile record={reference} hasDelete={true} on:remove={removeReference}></PreviewFile>
record={reference.record}
edge={reference.edge}
hasDelete={true}
on:remove={removeReference}
on:restore={restoreReference}
on:fulldelete={fullDelete}
></PreviewFile>
</div> </div>
{/each} {/each}
</Sortable> </Sortable>
@@ -1,17 +1,12 @@
<script> <script>
import {getContext} from "svelte"; import {getContext} from "svelte";
import { import {insertEdges} from "./reference";
fullDeleteRecord,
graphToReferences,
insertEdges,
removeReferenceFromGraph,
restoreReferenceToGraph
} from "./reference";
import {getErrorMessage} from "./errorMessage"; import {getErrorMessage} from "./errorMessage";
import {sortByField} from "../../edges/sortEdges"; import {sortByField} from "../../edges/sortEdges";
import ReferenceInlineButtons from "./ReferenceInlineButtons.svelte"; import ReferenceInlineButtons from "./ReferenceInlineButtons.svelte";
import Sortable from "../../libs/Sortable.svelte"; import Sortable from "../../libs/Sortable.svelte";
import PreviewReference from "../previews/PreviewReference.svelte"; import PreviewReference from "../previews/PreviewReference.svelte";
import axios from "axios";
const channel = getContext("channel"); const channel = getContext("channel");
export let record; export let record;
@@ -21,7 +16,11 @@
$: errorMessage = getErrorMessage(validationErrors, field.name); $: errorMessage = getErrorMessage(validationErrors, field.name);
$: references = graphToReferences(graph,record,field) $: 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 collections = channel.schemas.filter((aschema) => let collections = channel.schemas.filter((aschema) =>
field.collections.includes(aschema.name) field.collections.includes(aschema.name)
@@ -29,25 +28,27 @@
function removeReference(e) { function removeReference(e) {
e.preventDefault(); e.preventDefault();
graph.edges = removeReferenceFromGraph(graph,field,e.detail) graph.edges = graph.edges.filter(
} (edge) => !(edge.target === e.detail && edge.field === field.name)
);
function restoreReference(e) {
e.preventDefault();
graph.edges = restoreReferenceToGraph(graph,field,e.detail)
}
function fullDelete(e) {
e.preventDefault();
graph.edges = fullDeleteRecord(channel,graph, field, e.detail)
} }
function reorder(e) { function reorder(e) {
graph.edges = sortByField(e.detail.source, e.detail.target, graph.edges, field.name, references); graph.edges = sortByField(e.detail.source, e.detail.target, graph.edges, field.name, references);
} }
function insert(e) { function insert(e) {
e.preventDefault(); e.preventDefault();
// axios.post(channel.lucentUrl + "/edges/insert-many", {
// source: record.id,
// sourceSchema: record.schema,
// targetSchema: e.detail.schema,
// field: field.name,
// targets: e.detail.records.map(r => r.id),
// }).then(function (response) {
// graph = response.data.graph;
// })
graph = insertEdges(graph, record, e.detail.records, field.name, e.detail.action); graph = insertEdges(graph, record, e.detail.records, field.name, e.detail.action);
} }
@@ -68,16 +69,13 @@
</div> </div>
{#if references.length > 0} {#if references.length > 0}
<Sortable sortableClass="row row-cols-3 mt-3" on:update={reorder}> <Sortable sortableClass="row row-cols-3 mt-3" on:update={reorder}>
{#each references as reference (reference.record.id)} {#each references as reference (reference.id)}
<div> <div>
<PreviewReference <PreviewReference
{graph} {graph}
record={reference.record} record={reference}
edge={reference.edge}
hasDelete={true} hasDelete={true}
on:remove={removeReference} on:remove={removeReference}
on:restore={restoreReference}
on:fulldelete={fullDelete}
/> />
</div> </div>
{/each} {/each}
@@ -1,11 +1,10 @@
<script> <script>
import {getContext} from "svelte"; import {getContext} from "svelte";
import {debounce} from "../../../debounce.js"; import {debounce} from "lodash";
import {previewTitle} from "../Preview"; import {previewTitle} from "../Preview";
import {getErrorMessage} from "./errorMessage"; import {getErrorMessage} from "./errorMessage";
import {insertEdges} from "./reference.js"; import {insertEdges} from "./reference.js";
import Icon from "../../common/Icon.svelte"; import Icon from "../../common/Icon.svelte";
import axios from "axios";
const channel = getContext("channel"); const channel = getContext("channel");
export let field; export let field;
@@ -19,7 +18,7 @@
$: references = graph.edges $: references = graph.edges
.filter((edge) => edge.field === field.name) .filter((edge) => edge.field === field.name)
.map((edge) => { .map((edge) => {
return graph.records.find((increc) => increc.id === edge.target && record.id === edge.source); return graph.records.find((increc) => increc.id == edge.target && record.id == edge.source);
}).filter((rec) => (rec?.id ? true : false)) ?? []; }).filter((rec) => (rec?.id ? true : false)) ?? [];
let search = "" let search = ""
@@ -49,9 +48,11 @@
.then((response) => { .then((response) => {
searchOptions = []; searchOptions = [];
insert(e, response.data.records[0]); insert(e, response.data.records[0]);
console.log(response)
}) })
.catch((error) => { .catch((error) => {
searchOptions = []; searchOptions = [];
console.log(error);
}); });
} }
@@ -78,6 +79,7 @@
}) })
.catch((error) => { .catch((error) => {
searchOptions = []; searchOptions = [];
console.log(error);
}); });
}, 500); }, 500);
@@ -1,4 +1,5 @@
<script> <script>
import Tinymce from "../../libs/Tinymce.svelte";
import RichEditorFiles from "./RichEditorFiles.svelte"; import RichEditorFiles from "./RichEditorFiles.svelte";
import {getErrorMessage} from "./errorMessage"; import {getErrorMessage} from "./errorMessage";
import Trix from "../../libs/Trix.svelte"; import Trix from "../../libs/Trix.svelte";
@@ -25,6 +26,7 @@
<div class="mb-0"> <div class="mb-0">
<Trix {field} bind:this={editor} bind:value></Trix> <Trix {field} bind:this={editor} bind:value></Trix>
<!-- <Tinymce bind:this={editor} bind:value {additionalConfig}/>-->
{#if field.collections.length > 0} {#if field.collections.length > 0}
<RichEditorFiles <RichEditorFiles
bind:graph bind:graph
@@ -0,0 +1,49 @@
<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>
@@ -0,0 +1,30 @@
<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>
+5 -50
View File
@@ -1,11 +1,12 @@
import {uniqueBy} from "../../../helpers.js"; import {uniqBy} from "lodash";
import axios from "axios";
export function insertEdges(graph, sourceRecord, targetRecords, fieldName, action = "") { export function insertEdges(graph, sourceRecord, targetRecords, fieldName, action = "") {
let newEdges = targetRecords.map((r) => { let newEdges = targetRecords.map((r) => {
return { return {
target: r.id, target: r.id,
source: sourceRecord.id, source: sourceRecord.id,
sourceSchema: sourceRecord.schema,
targetSchema: r.schema,
field: fieldName, field: fieldName,
depth: 1, depth: 1,
rank: "" rank: ""
@@ -17,53 +18,7 @@ export function insertEdges(graph, sourceRecord, targetRecords, fieldName, actio
replacedEdges = replacedEdges.filter((edge) => edge.field !== field.name); replacedEdges = replacedEdges.filter((edge) => edge.field !== field.name);
} }
graph.records = uniqueBy([...graph.records, ...targetRecords], (r) => r.id); graph.records = uniqBy([...graph.records, ...targetRecords], (r) => r.id);
graph.edges = uniqueBy([...replacedEdges, ...newEdges], (edge) => edge.source + edge.target + edge.field + edge.depth); graph.edges = uniqBy([...replacedEdges, ...newEdges], (edge) => edge.source + edge.target + edge.field + edge.depth);
return graph; return graph;
} }
export function graphToReferences(graph,record,field){
return graph.edges
.filter((edge) => edge.field === field.name)
.map((edge) => {
return {
record: graph.records.find((increc) => increc.id === edge.target && record.id === edge.source),
edge: edge
};
}).filter((rec) => (rec.record?.id ? true : false)) ?? [];
}
export function removeReferenceFromGraph(graph,field,id){
return graph.edges.map(
(edge) => {
if(edge.target === id && edge.field === field.name){
edge._isTrashed = true;
}
return edge;
}
);
}
export function restoreReferenceToGraph(graph,field,id){
return graph.edges.map(
(edge) => {
if(edge.target === id && edge.field === field.name){
edge._isTrashed = false;
}
return edge;
}
);
}
export function fullDeleteRecord(channel,graph,field,id){
axios
.post(channel.lucentUrl + "/records/status/trashed" , {
records: [id]
});
return graph.edges.filter(
(edge) => !(edge.target === id && edge.field === field.name)
);
}
@@ -24,7 +24,7 @@
<div style="display: flex;align-items: center; gap:10px;"> <div style="display: flex;align-items: center; gap:10px;">
{#if !isCreateMode} {#if !isCreateMode}
<Dropdown > <Dropdown>
<div slot="button"> <div slot="button">
<Icon icon="ellipsis"/> <Icon icon="ellipsis"/>
</div> </div>
@@ -11,7 +11,6 @@
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
const channel = getContext("channel"); const channel = getContext("channel");
export let record; export let record;
export let edge;
export let hasDelete = false; export let hasDelete = false;
export let hasInsert = false; export let hasInsert = false;
@@ -24,16 +23,6 @@
dispatch("remove", record.id); dispatch("remove", record.id);
} }
function restore(e) {
e.preventDefault();
dispatch("restore", record.id);
}
function fullDelete(e) {
e.preventDefault();
dispatch("fulldelete", record.id);
}
function insert(e, preset) { function insert(e, preset) {
e.preventDefault(); e.preventDefault();
let html = htmlurl(channel, record, preset) let html = htmlurl(channel, record, preset)
@@ -48,7 +37,7 @@
</script> </script>
<div class="preview-file" class:is-trashed={edge?._isTrashed}> <div class="preview-file">
<div style="display: flex;align-items: center;gap: 10px;"> <div style="display: flex;align-items: center;gap: 10px;">
<div class="image"> <div class="image">
<Preview {record} size="small"/> <Preview {record} size="small"/>
@@ -60,9 +49,6 @@
href="{channel.lucentUrl}/records/{record.id}" href="{channel.lucentUrl}/records/{record.id}"
> >
{cardTitle} {cardTitle}
{#if edge?._isTrashed}
<span class="trashed-text">will remove on save</span>
{/if}
</a> </a>
<small class="d-block"> <small class="d-block">
from {schema.label} from {schema.label}
@@ -92,30 +78,12 @@
{/if} {/if}
{#if hasDelete} {#if hasDelete}
<div class="reference-action"> <div class="reference-action">
{#if edge?._isTrashed}
<button <button
title="Restore"
class="button"
on:click={restore}
>
<Icon icon="undo"/>
</button>
<button
title="Delete from everywhere"
class="button"
on:click={fullDelete}
>
<Icon icon="destroy"/>
</button>
{:else}
<button
title="Remove"
class="button" class="button"
on:click={remove} on:click={remove}
> >
<Icon icon="trash-can"/> <Icon icon="trash-can"/>
</button> </button>
{/if}
</div> </div>
{/if} {/if}
</div> </div>
@@ -10,7 +10,6 @@
const channel = getContext("channel"); const channel = getContext("channel");
export let graph; export let graph;
export let record; export let record;
export let edge;
export let hasDelete = false; export let hasDelete = false;
let schema = channel.schemas.find((aschema) => aschema.name === record.schema); let schema = channel.schemas.find((aschema) => aschema.name === record.schema);
@@ -22,20 +21,10 @@
e.preventDefault(); e.preventDefault();
dispatch("remove", record.id); dispatch("remove", record.id);
} }
function restore(e) {
e.preventDefault();
dispatch("restore", record.id);
}
function fullDelete(e) {
e.preventDefault();
dispatch("fulldelete", record.id);
}
</script> </script>
<div class="preview-reference" class:is-trashed={edge?._isTrashed}> <div class="preview-reference">
<div style="display: flex;align-items: center;gap: 10px;"> <div style="display: flex;align-items: center;gap: 10px;">
{#if cardImageRecord} {#if cardImageRecord}
@@ -49,12 +38,7 @@
class="record-title" class="record-title"
href="{channel.lucentUrl}/records/{record.id}" href="{channel.lucentUrl}/records/{record.id}"
> >
{cardTitle} {cardTitle}
{#if edge?._isTrashed}
<span class="trashed-text">will remove on save</span>
{/if}
</a> </a>
<small class="d-block"> <small class="d-block">
from {schema.label} from {schema.label}
@@ -69,30 +53,12 @@
</div> </div>
{#if hasDelete} {#if hasDelete}
<div class="reference-action"> <div class="reference-action">
{#if edge?._isTrashed}
<button <button
title="Restore"
class="button"
on:click={restore}
>
<Icon icon="undo"/>
</button>
<button
title="Delete from everywhere"
class="button"
on:click={fullDelete}
>
<Icon icon="destroy"/>
</button>
{:else}
<button
title="Remove"
class="button" class="button"
on:click={remove} on:click={remove}
> >
<Icon icon="trash-can"/> <Icon icon="trash-can"/>
</button> </button>
{/if}
</div> </div>
{/if} {/if}
</div> </div>
+20
View File
@@ -0,0 +1,20 @@
<script>
import Step from "./Step.svelte"
export let steps;
export let allSuccess = false;
console.log(steps);
</script>
<div class="wrapper-tiny">
{#each steps as step}
<Step {step}></Step>
{/each}
<div style="text-align: center;margin-top: 30px;">
{#if allSuccess}
<a href="/lucent/register" class="bt">Create the first user</a>
{/if}
</div>
</div>
+67
View File
@@ -0,0 +1,67 @@
<script>
import Icon from "../common/Icon.svelte"
export let step;
</script>
<div class="step step-{step.status}">
<div class="step-icon">
{#if step.status === "success"}
<Icon icon="check"></Icon>
{:else}
<Icon icon="close"></Icon>
{/if}
</div>
<div style="width:100%">
<h4>{step.name}</h4>
<details>
<summary>Instuctions</summary>
<code class="instructions">{step.instructions}</code>
</details>
</div>
</div>
<style>
.step-success .step-icon{
background: var(--suc10);
color: var(--suc100);
}
.step-fail .step-icon{
background: var(--err10);
color: var(--err100);
}
.step-icon{
padding: 12px;
border-radius: 12px;
}
.step {
width: 100%;
display: flex;
align-items: start;
gap: 10px;
justify-content: space-between;
padding: 12px;
border-radius: 12px;
}
details {
width: 100%;
}
.instructions {
margin-top: 20px;
padding: 12px;
border-radius: 12px;
background: var(--p10);
white-space: break-spaces;
display: block;
}
</style>
+862 -1257
View File
File diff suppressed because it is too large Load Diff
+5 -1
View File
@@ -19,12 +19,16 @@
"htmx.org": "^2.0.1", "htmx.org": "^2.0.1",
"install": "^0.13.0", "install": "^0.13.0",
"laravel-vite-plugin": "^1.0.5", "laravel-vite-plugin": "^1.0.5",
"lodash": "^4.17.21",
"mustache": "^4.2.0", "mustache": "^4.2.0",
"npm": "^10.8.2", "npm": "^10.8.2",
"postcss": "8.4.31",
"sass": "^1.77.8",
"sortablejs": "^1.15.2", "sortablejs": "^1.15.2",
"svelte": "^4.2.18", "svelte": "^4.2.18",
"tinymce": "^6.8.4", "tinymce": "^6.8.4",
"trix": "^2.1.5", "trix": "^2.1.5",
"vite": "5.4.6" "uuid": "^10.0.0",
"vite": "5.2.6"
} }
} }
-22
View File
@@ -1,22 +0,0 @@
.scope-login {
display: flex;
height: 100vh;
.bg-image {
width: 50%;
background: url("/vendor/lucent/public/art.jpg");
background-repeat: no-repeat;
background-size: cover;
background-repeat: no-repeat;
background-position: center center;
}
.login-form{
width: 50%;
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
}
-51
View File
@@ -1,51 +0,0 @@
.autocomplete {
position: relative;
z-index: 1000;
overflow: visible;
.autocomplete-option {
cursor: pointer;
font-size: 14px;
padding: 3px 10px;
&:hover {
background: var(--p40);
border-radius: 12px;
}
}
&:focus-within {
.autocomplete-results{
display: flex;
}
}
}
.autocomplete-selected-value {
font-size: 13px;
margin-top: 10px;
border-radius: 12px;
background: var(--p30);
padding: 3px 10px;
display: inline-flex;
justify-content: center;
gap: 4px;
line-height: 22px;
&:hover {
background: var(--p40);
}
}
.autocomplete-results {
display: none;
flex-direction: column;
padding: 10px;
overflow: visible;
position: absolute;
border-radius: 12px;
z-index: 20;
background: var(--p30);
//border: 1px solid var(--p40);
transition: 600ms;
flex-grow: 1;
top: 45px;
width: 100%;
}
-23
View File
@@ -1,23 +0,0 @@
.avatar {
/* Center the content */
display: inline-block;
vertical-align: middle;
/* Used to position the content */
position: relative;
/* Colors */
color: #fff;
/* Rounded border */
border-radius: 50%;
}
.avatar__letters {
/* Center the content */
left: 50%;
position: absolute;
top: 50%;
transform: translate(-50%, -50%);
}
-94
View File
@@ -1,94 +0,0 @@
.button {
border-radius: 12px;
background: var(--p20);
padding: 3px 10px;
cursor: pointer;
border: 0px solid var(--p30);
font-size: 14px;
min-height: 27px;
display: flex;
align-items: center;
gap: 4px;
color: var(--text);
&:focus {
}
&:hover {
background: var(--p30);
}
&:active {
background: var(--p50) !important;
box-shadow: none;
}
&.active {
background: var(--p30);
}
&.secondary {
background: var(--p30);
&:hover {
background: var(--p40);
}
}
&.primary {
background: var(--p70);
color: var(--p10);
&:hover {
background: var(--p90);
}
}
&[disabled] {
pointer-events: none;
opacity: .7;
color: var(--text);
}
}
.upload-button {
padding: 0;
border: none;
label {
font-size: 14px;
line-height: 14px;
font-weight: normal;
background: var(--p80) !important;
color: var(--p10);
}
}
.button-text {
border: none;
padding: 0;
background: transparent;
cursor: pointer;
}
.spinner-border {
width: 12px;
height: 12px;
border: 2px solid var(--p10);
border-bottom-color: var(--p30);
border-radius: 50%;
display: inline-block;
box-sizing: border-box;
animation: rotation 1s linear infinite;
}
@keyframes rotation {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
-104
View File
@@ -1,104 +0,0 @@
@supports (-webkit-appearance: none) or (-moz-appearance: none) {
.checkbox-wrapper input[type=checkbox] {
--active-inner: var(--p10);
--focus: 2px var(--p30);
--border-hover: var(--p30);
--disabled: #F6F8FF;
--disabled-inner: #E1E6F9;
-webkit-appearance: none;
-moz-appearance: none;
height: 21px;
outline: none;
display: inline-block;
vertical-align: top;
position: relative;
margin: 0;
cursor: pointer;
border: 1px solid var(--bc, var(--p30));
background: var(--b, var(--p10));
transition: background 0.3s, border-color 0.3s, box-shadow 0.2s;
}
.checkbox-wrapper input[type=checkbox]:after {
content: "";
display: block;
left: 0;
top: 0;
position: absolute;
transition: transform var(--d-t, 0.3s) var(--d-t-e, ease), opacity var(--d-o, 0.2s);
}
.checkbox-wrapper input[type=checkbox]:checked {
--b: var(--p40);
--bc: var(--p40);
--d-o: .3s;
--d-t: .6s;
--d-t-e: cubic-bezier(.2, .85, .32, 1.2);
}
.checkbox-wrapper input[type=checkbox]:disabled {
--b: var(--disabled);
cursor: not-allowed;
opacity: 0.9;
}
.checkbox-wrapper input[type=checkbox]:disabled:checked {
--b: var(--disabled-inner);
--bc: var(--p40);
}
.checkbox-wrapper input[type=checkbox]:disabled + label {
cursor: not-allowed;
}
.checkbox-wrapper input[type=checkbox]:hover:not(:checked):not(:disabled) {
--bc: var(--border-hover);
}
.checkbox-wrapper input[type=checkbox]:focus {
box-shadow: 0 0 0 var(--focus);
}
.checkbox-wrapper input[type=checkbox]:not(.switch) {
width: 21px;
}
.checkbox-wrapper input[type=checkbox]:not(.switch):after {
opacity: var(--o, 0);
}
.checkbox-wrapper input[type=checkbox]:not(.switch):checked {
--o: 1;
}
.checkbox-wrapper input[type=checkbox] + label {
display: inline-block;
vertical-align: middle;
cursor: pointer;
margin-left: 4px;
}
.checkbox-wrapper input[type=checkbox]:not(.switch) {
border-radius: 7px;
}
.checkbox-wrapper input[type=checkbox]:not(.switch):after {
width: 5px;
height: 9px;
border: 2px solid var(--active-inner);
border-top: 0;
border-left: 0;
left: 7px;
top: 4px;
transform: rotate(var(--r, 20deg));
}
.checkbox-wrapper input[type=checkbox]:not(.switch):checked {
--r: 43deg;
}
}
.checkbox-wrapper * {
box-sizing: inherit;
}
.checkbox-wrapper *:before,
.checkbox-wrapper *:after {
box-sizing: inherit;
}
.checkbox-wrapper input[type=checkbox]:indeterminate {
--b: var(--p40);
--bc: var(--p40);
--d-o: .3s;
--d-t: .6s;
--d-t-e: cubic-bezier(.2, .85, .32, 1.2);
}
-24
View File
@@ -1,24 +0,0 @@
.is-editable-false{
.cm-content{
background-color: var(--p10);
}
}
.cm-focused{
.cm-content{
background-color: var(--p10);
color: var(--p100);
}
}
.cm-content{
background-color: var(--p20);
}
.ͼ4 .cm-line ::selection, .ͼ4 .cm-line::selection{
background: var(--p40) !important;
}
.cm-activeLine{
background-color: var(--p20)!important;
}
-43
View File
@@ -1,43 +0,0 @@
.flatpickr-wrapper {
display: block !important;
}
.editor-field {
.flatpickr-calendar {
border-radius: 12px !important;
}
.flatpickr-months .flatpickr-month {
background: var(--p30);
color: var(--text);
font-size: 12px;
}
.flatpickr-current-month .flatpickr-monthDropdown-months {
background: var(--p30);
}
.flatpickr-weekdays{
background: var(--p30);
color: var(--text);
}
.flatpickr-weekdaycontainer .flatpickr-weekday{
background: var(--p30);
color: var(--text);
}
.flatpickr-days{
background: var(--p10);
color: var(--text);
}
.flatpickr-time{
background: var(--p10);
color: var(--text);
}
}
-54
View File
@@ -1,54 +0,0 @@
//
//:modal {
// background-color: beige;
// border: 2px solid burlywood;
// border-radius: 5px;
//}
html {
//scrollbar-gutter: stable;
}
body:has(dialog[open]) {
overflow: hidden;
}
dialog {
margin: 2vh auto;
background-color: var(--p10);
padding: 34px;
border: none;
border-radius: 12px;
overflow: auto;
max-height: 96vh;
box-shadow: none!important;
//position: relative;
.close {
position: absolute;
top: 10px;
right: 0px;
}
.dialog-body {
width: fit-content;
}
}
dialog::backdrop {
backdrop-filter: blur(3px);
}
.dialog-header {
margin-bottom: 20px;
display: flex;
align-items: center;
gap: 8px;
position: sticky;
top: -34px;
z-index: 999;
background-color: var(--p10);
padding: 10px 0;
}
-73
View File
@@ -1,73 +0,0 @@
.dropdown {
position: relative;
overflow: visible;
}
.dropdown-button > div {
display: flex;
align-items: center;
gap: 3px;
}
.dropdown-menu {
display: flex;
flex-direction: column;
padding: 10px;
overflow: visible;
position: absolute;
border-radius: 12px;
z-index: 22;
background: var(--p20);
transition: 600ms;
flex-grow: 1;
top: 35px;
min-width: max-content;
border: 1px solid var(--p30);
&.orientation-right {
right: 0;
}
&.orientation-left {
left: 0;
}
}
.dropdown-header, .dropdown-item {
display: flex;
align-items: center;
gap: 3px;
text-wrap: nowrap;
}
.dropdown-header {
padding: 10px;
}
.dropdown-item {
font-size: 14px;
padding: 3px 10px;
&:hover {
background: var(--p30);
border-radius: 12px;
button {
background: var(--p30);
}
}
.button-icon {
flex-shrink: 0;
}
}
.editor-field{
.dropdown-menu {
background: var(--p30);
}
}
-95
View File
@@ -1,95 +0,0 @@
label {
display: block;
font-weight: 700;
margin-bottom: 4px;
}
input[type=text],input[type=number],input[type=search],input[type=email],textarea{
width: 100%;
background: var(--p20);
border: 1px solid var(--p50);
border-radius: 5px;
padding: 5px 7px;
font-size: 16px;
&:focus{
background: var(--p10);
}
}
textarea{
resize: none;
}
select{
width: 100%;
background: var(--p20);
border: 1px solid var(--p50);
border-radius: 5px;
padding: 5px 7px;
font-size: 16px;
&:focus{
background: var(--p10);
}
}
.htmx-indicator {
display: none;
}
.htmx-request .htmx-indicator {
display: inline;
}
.htmx-request.htmx-indicator {
display: inline;
}
.bt {
appearance: none;
background-color: #000;
background-image: none;
border: 1px solid #000;
border-radius: 4px;
box-shadow: #fff 4px 4px 0 0, #000 4px 4px 0 1px;
box-sizing: border-box;
color: #fff;
cursor: pointer;
display: inline-block;
font-family: ITCAvantGardeStd-Bk, Arial, sans-serif;
font-size: 16px;
font-weight: 400;
line-height: 20px;
margin: 0 5px 10px 0;
overflow: visible;
padding: 8px 40px;
text-align: center;
text-transform: none;
touch-action: manipulation;
user-select: none;
-webkit-user-select: none;
vertical-align: middle;
white-space: nowrap;
}
.bt:focus {
text-decoration: none;
}
.bt:hover {
text-decoration: none;
}
.bt:active {
box-shadow: rgba(0, 0, 0, .125) 0 3px 5px inset;
outline: 0;
}
.bt:not([disabled]):active {
box-shadow: #fff 2px 2px 0 0, #000 2px 2px 0 1px;
transform: translate(2px, 2px);
}
-52
View File
@@ -1,52 +0,0 @@
.mt-1{margin-top: 4px}
.mt-2{margin-top: 8px}
.mt-3{margin-top: 12px}
.mt-4{margin-top: 16px}
.mt-5{margin-top: 20px}
.mb-1{margin-bottom: 4px}
.mb-2{margin-bottom: 8px}
.mb-3{margin-bottom: 12px}
.mb-4{margin-bottom: 16px}
.mb-5{margin-bottom: 20px}
.pt-1{padding-top: 4px}
.pt-2{padding-top: 8px}
.pt-3{padding-top: 12px}
.pt-4{padding-top: 16px}
.pt-5{padding-top: 20px}
.pb-1{padding-bottom: 4px}
.pb-2{padding-bottom: 8px}
.pb-3{padding-bottom: 12px}
.pb-4{padding-bottom: 16px}
.pb-5{padding-bottom: 20px}
.gap-1{gap: 4px}
.gap-2{gap: 8px}
.gap-3{gap: 12px}
.gap-4{gap: 16px}
.gap-5{gap: 20px}
.hide{
display: none!important;
}
.hidden{
visibility: hidden;
}
.d-block{
display: block;
}
.d-inline-block{
display: inline-block;
}
.is-bold{
font-weight: 700;
}
.in-place{
padding: 36px;
}
-19
View File
@@ -1,19 +0,0 @@
.sidebar-content{
min-width: 300px;
max-width: 400px;
position: relative;
}
.main-content {
position: relative;
width: fit-content;
min-width: 900px;
}
.main-wrapper {
display: flex;
justify-content: center;
gap: 40px;
padding: 20px;
position: relative;
}
-19
View File
@@ -1,19 +0,0 @@
.member-list{
display: flex;
flex-direction: column;
gap: 5px;
}
.member-item{
background: var(--p30);
border-radius: 12px;
padding: 12px;
display: flex;
justify-content: space-between;
align-items: center;
.member-name{
display: flex;
align-items: center;
gap: 10px;
}
}
-26
View File
@@ -1,26 +0,0 @@
.notice {
background-color: var(--p20);
padding: 14px;
margin: 2rem 0;
position: relative;
font-size: 16px;
line-height: 24px;
border-radius: 12px;
//border: 3px solid var(--border-base);
}
.notice .title {
content: "NOTE";
//position: absolute;
border-radius: 12px;
display: block;
font-weight: bold;
}
.notice.notice-success{
background: var(--suc20);
}
.notice.notice-error{
background: var(--err10);
}
-38
View File
@@ -1,38 +0,0 @@
.pagination {
margin: 20px auto 10px ;
display: flex;
justify-content: center;
align-items: center;
gap: 4px;
list-style: none;
padding: 0;
li {
a,span{
font-size: 14px;
border-radius: 12px;
padding: 4px 18px;
background: var(--p20);
&:hover{
background: var(--p30);
}
}
&.disabled {
pointer-events: none;
opacity: .7;
}
&.active {
span{
background: var(--p30);
}
}
}
}
-68
View File
@@ -1,68 +0,0 @@
.preview-file,.preview-reference{
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
background: var(--p10);
border-radius: 12px;
&.is-trashed{
border: 2px solid var(--err10);
background: var(--p20);
}
.trashed-text{
background: var(--err10);
font-size: 12px;
padding:2px 10px;
}
.image{
display: flex;
}
.reference-action{
display: none;
}
&:hover{
background: var(--p30);
.reference-action{
display: flex;
align-items: center;
gap: 3px;
}
}
}
.file-preview-small{
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 2px;
border-radius: 12px;
padding: 4px;
//background: var(--p10);
}
.preview-reference{
background: var(--p10);
padding: 10px 20px;
}
.sortable-container {
display: flex;
flex-direction: column;
gap: 5px;
}
.sortable-ghost{
border: 2px dashed var(--p60);
}
.sortable-drag { opacity: 0 !important; } .sortable-ghost { opacity: 1 !important; }
-147
View File
@@ -1,147 +0,0 @@
.record-edit {
position: relative;
max-width: 900px;
.invalid-feedback {
color: var(--text-error);
font-size: 15px;
line-height: 20px;
margin-top: 10px;
}
}
.record-header {
margin: 10px 0 0;
.schema-name {
font-size: 14px;
}
.record-title {
font-size: 18px;
display: block;
}
}
.tools-header {
margin: 30px 0 0;
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
font-size: 14px;
position: relative;
z-index: 20;
padding: 10px;
border-radius: 12px;
background: var(--p20);
}
.editor-field {
background: var(--p20);
padding: 18px;
position: relative;
border-radius: 12px;
margin: 6px 0;
border-color: transparent;
.button:not(.primary) {
background: var(--p30);
&:hover {
background: var(--p40);
}
}
dialog {
.button:not(.primary) {
background: var(--p20);
&:hover {
background: var(--p30);
}
}
}
}
.field-header {
margin-bottom: 4px;
position: relative;
.labels {
display: flex;
justify-content: space-between;
align-items: center;
}
.label-and-help {
display: flex;
align-items: center;
gap: 6px;
}
label {
font-size: 14px;
line-height: 14px;
margin: 0;
font-weight: 700;
}
.help-text {
font-size: 14px;
line-height: 14px
}
code {
}
}
.system-help-text {
font-size: 14px;
line-height: 14px;
margin-top: 10px;
}
.field-checkbox {
display: flex;
gap: 20px;
align-items: center;
.form-check-inline {
display: flex;
align-items: center;
gap: 4px;
}
.form-check-label {
font-size: 14px;
line-height: 14px;
}
}
.record-edit-file-preview {
display: flex;
gap: 20px;
.file-details {
width: 50%;
display: flex;
flex-direction: column;
gap: 5px;
}
.file-details-item {
.text-muted {
color: var(--grey-dark);
}
}
}
-60
View File
@@ -1,60 +0,0 @@
.reference-tags {
position: relative;
z-index: 20;
.reference-tags-option {
cursor: pointer;
font-size: 14px;
padding: 3px 10px;
&:hover {
background: var(--p40);
border-radius: 12px;
}
}
&:focus-within {
.reference-tags-results {
display: flex;
}
}
}
.reference-tags-selected-value {
font-size: 13px;
margin-top: 10px;
border-radius: 12px;
background: var(--p30);
padding: 3px 10px;
display: inline-flex;
justify-content: center;
gap: 4px;
line-height: 22px;
&:hover {
background: var(--p40);
}
}
.reference-tags-results {
display: none;
flex-direction: column;
padding: 10px;
overflow: visible;
position: absolute;
border-radius: 12px;
z-index: 20;
background: var(--p30);
//border: 2px solid var(--background-2);
transition: 600ms;
flex-grow: 1;
top: 45px;
width: 100%;
.start-typing {
font-style: italic;
font-size: 13px;
}
}
-46
View File
@@ -1,46 +0,0 @@
/*
1. Use a more-intuitive box-sizing model.
*/
*, *::before, *::after {
box-sizing: border-box;
}
/*
2. Remove default margin
*/
* {
margin: 0;
}
/*
Typographic tweaks!
3. Add accessible line-height
4. Improve text rendering
*/
body {
line-height: 1.5;
-webkit-font-smoothing: antialiased;
}
/*
5. Improve media defaults
*/
img, picture, video, canvas, svg {
display: block;
max-width: 100%;
}
/*
6. Remove built-in form typography styles
*/
input, button, textarea, select {
font: inherit;
}
/*
7. Avoid text overflows
*/
p, h1, h2, h3, h4, h5, h6 {
overflow-wrap: break-word;
}
/*
8. Create a root stacking context
*/
#root, #__next {
isolation: isolate;
}
-87
View File
@@ -1,87 +0,0 @@
.revisions{
display: flex;
flex-direction: column;
gap: 5px;
.revision{
justify-content: space-between;
display: flex;
gap: 20px;
align-items: center;
background: var(--p20);
padding: 12px;
border-radius: 12px;
.version{
display: flex;
gap: 10px;
}
&.active{
background: var(--p30);
}
}
}
.selected-revision{
margin-top: 30px;
align-items: center;
background: var(--p20);
padding: 12px;
border-radius: 12px;
.button{
background: var(--p30);
}
.revision-field{
display: flex;
gap: 20px;
align-items: center;
padding: 20px 0;
border-bottom: 1px solid var(--p30);
flex: 1;
.compare-left{
width: 45%;
border-radius: 12px;
padding: 20px;
background: var(--p30);
}
.compare-right{
width: 45%;
border-radius: 12px;
padding: 20px;
background: var(--p30);
}
.compare-center{
width: 10%;
height: 100%;
display: flex;
gap: 20px;
align-items: center;
}
}
}
.reference-field{
width: 100px;
}
.revision-references{
display: flex;
gap: 20px;
align-items: center;
padding: 20px 0;
border-bottom: 1px solid var(--p30);
}
.reference-compare{
width: 45%;
border-radius: 12px;
padding: 20px;
background: var(--p30);
}
-150
View File
@@ -1,150 +0,0 @@
.tiptap {
width: 100%;
background: var(--p20);
border: 1px solid var(--p50);
border-radius: 0 0 5px 5px;
padding: 15px 15px;
font-size: 16px;
:first-child {
margin-top: 0;
}
&:focus {
background: var(--p10);
}
img {
&.ProseMirror-selectednode {
box-shadow: 0 0 1px 2px var(--p70);
}
}
}
.editor-field {
.editor-toolbar {
display: flex;
gap: 4px;
background: var(--p30);
border-radius: 5px 5px 0 0;
padding: 5px 7px;
.button:not(.primary) {
font-weight: 700;
&.active {
background: var(--p40);
}
}
}
}
.content {
.tiptap {
li > p {
display: inline;
}
}
}
trix-editor {
background: var(--p20)!important;
border: 1px solid var(--p50)!important;
border-radius: 0 0 5px 5px!important;
padding: 15px 15px!important;
& > div {
margin-bottom: 14px;
font-size: 16px;
line-height: 23px;
}
&:focus {
background: var(--p10)!important;
}
figure.attachment{
display: flex!important;
flex-direction: column!important;
justify-content: center;
align-items: center;
gap: 10px;
}
.attachment {
background: var(--p20);
padding: 12px 0;
text-align: center;
display: flex;
justify-content: center;
img {
margin-bottom: 0;
}
}
[data-trix-mutable].attachment img {
box-shadow: 0 0 1px 2px var(--p70) !important;
}
.trix-button--remove {
box-shadow: none !important;
border: 2px solid var(--p40) !important;
}
.trix-button--remove:hover {
border: 2px solid var(--p40);
}
a {
color: var(--p80);
}
}
trix-toolbar {
.trix-button-row {
display: flex;
}
.trix-button-group {
background: transparent !important;
border: none !important;
display: flex !important;
gap: 4px;
}
.trix-button-group--history-tools,.trix-button-group--file-tools
{
display: none !important;
}
.trix-button {
border-radius: 6px !important;
background: var(--p30) !important;
padding: 14px 22px !important;
margin: 0 !important;
cursor: pointer;
border: 0px solid var(--p30) !important;
font-size: 14px !important;
min-height: 27px !important;
display: flex !important;
align-items: center !important;
gap: 4px;
color: var(--text) !important;
&:before{
background-size: 22px!important;
}
&:hover{
background: var(--p40) !important;
}
&.trix-active{
background: var(--p50) !important;
}
}
}
-98
View File
@@ -1,98 +0,0 @@
.sidebar-top{
border: 0px solid var(--p30);
font-size: 18px;
padding: 20px 20px ;
display: flex;
align-items: center;
justify-content: space-between;
background: var(--p20);
margin-bottom: 15px;
border-radius: 12px;
}
.sidebar {
//border: 1px solid var(--border-context);
border-radius: 12px;
font-size: 15px;
line-height: 28px;
padding: 20px;
background: var(--p20);
display: flex;
flex-direction: column;
gap: 3px;
}
.sidebar-folder{
width: 100%;
margin: 3px 12px 3px;
.sidebar-folder{
margin-left: 5px;
}
}
.sidebar-header {
width: 100%;
display: flex;
cursor: pointer;
justify-content: space-between;
align-items: center;
background: var(--p30);
font-size: 16px;
padding: 3px 12px 3px;
color: var(--text);
border: none;
border-radius: 12px;
&:focus{
box-shadow: none;
}
&:hover {
background: var(--p40);
}
&:first-child {
}
&:last-child {
border-bottom: none;
}
}
.sidebar-item {
color: var(--text);
display: block;
font-size: 14px;
padding: 3px 12px;
text-decoration: none;
transition: 600ms;
border-radius: 12px;
&:last-child {
border-bottom: none;
}
&:hover {
background: var(--p30);
}
&.active {
background: var(--p40);
}
}
.top-nav{
display: flex;
justify-content: end;
align-items: center;
gap: 10px;
}
.top-nav-item{
border-radius: 12px;
font-size: 14px;
background: var(--p20);
padding: 3px 10px ;
&:hover {
background: var(--p30);
}
}
-34
View File
@@ -1,34 +0,0 @@
input.switch {
-webkit-appearance: none;
width: 34px;
height: 18px;
border: 1px solid var(--p40);
position: relative;
border-radius: 50px;
box-sizing: content-box;
cursor: pointer;
transition: background 150ms ease-in-out;
background: white;
}
input.switch::after {
top: 2px;
left: 2px;
transition: left 150ms ease-in-out;
content: " ";
width: 14px;
height: 14px;
background: var(--p40);
box-shadow: inset 0 0 0px 1px var(--p40);
position: absolute;
border-radius: 50px;
}
input.switch:checked {
background: var(--p50);
}
input.switch:checked::after {
left: calc(100% - 17px);
background: var(--p10);
}
-140
View File
@@ -1,140 +0,0 @@
.table {
min-width: 600px;
overflow: auto;
background: var(--p20);
padding: 1px;
font-size: 14px;
border-radius: 12px;
table {
background: var(--p20);
width: 100%;
border-collapse: separate;
border: none;
border-spacing: 0;
}
thead {
border-radius: 12px;
tr {
border-radius: 12px;
}
}
th {
font-size: 14px;
font-weight: normal;
white-space: nowrap;
max-width: 400px;
border: none;
background: var(--p20);
text-align: left;
padding: 8px 16px;
&.is-sort {
font-weight: 700;
}
&:first-child {
border-radius: 12px 0 0 0;
}
&:last-child {
border-radius: 0 12px 0 0;
}
}
td {
font-weight: normal;
white-space: nowrap;
max-width: 400px;
height: 48px;
padding: 4px 16px;
border: none;
overflow: hidden;
//img{
// width: 48px;
//}
.status {
color: var(--text);
font-size: 80%;
}
.row-name {
display: flex;
align-items: center;
gap: 6px;
}
.title-td-contents {
display: flex;
align-items: center;
gap: 6px;
font-size: 14px;
line-height: 14px;
}
}
tbody {
tr {
border-radius: 12px;
background: var(--p10);
border: none;
&:has(input:checked) {
background: var(--p30);
}
&:hover {
background: var(--p20);
}
}
}
tr:nth-child(odd) {
//background-color: #f9f9f9;
}
td:nth-child(odd) {
// border-left: 1px solid #e4e4e4;
// border-right: 1px solid #e4e4e4;
}
th:nth-child(odd) {
}
.field-ui-number {
text-align: right;
}
.references{
display: flex;
gap: 4px;
.reference{
font-size: 13px;
border-radius: 12px;
background: var(--p30);
padding: 1px 5px;
}
}
}
.file-table-row {
display: flex;
align-items: center;
gap: 5px;
& > div {
display: flex;
flex-flow: column;
gap: 5px;
}
}
-11
View File
@@ -1,11 +0,0 @@
.tabs{
padding: 0;
margin: 20px 0 20px;
display: flex;
gap: 4px;
flex-wrap: wrap;
.tab{
list-style: none;
}
}
-64
View File
@@ -1,64 +0,0 @@
.toolbar{
display: flex;
align-items: center;
gap: 5px;
justify-content: space-between;
input.search{
border-radius: 12px;
background: var(--p20);
padding: 4px 10px;
cursor: pointer;
border: none;
font-size: 14px;
}
.selected-filter{
font-size: 13px;
border-radius: 12px;
margin: 2px 0;
background: var(--p30);
padding: 3px 10px;
display: flex;
gap: 4px;
line-height: 22px;
}
.filter-input{
margin: 10px 0 ;
input{
font-size: 13px;
}
}
.applied-filter{
background: var(--p30);
}
}
.toolbar-filters{ display: flex;
align-items: center;
gap: 5px;}
.applied-filters{
display: flex;
gap: 4px;
margin-top: 10px;
.applied-filter {
font-size: 13px;
border-radius: 12px;
background: var(--p20);
padding: 3px 10px;
display: flex;
justify-content: center;
gap: 4px;
line-height: 22px;
}
.applied-filter:hover {
background-color: var(--p30);
}
}
-106
View File
@@ -1,106 +0,0 @@
.content {
font-size: 16px;
line-height: 20px;
font-family: var(--main-font);
color: var(--text);
p{
margin-bottom: 14px;
&:last-child{
margin-bottom: 0;
}
}
h1{
font-size: 24px;
line-height: 34px;
}
h2{
font-size: 20px;
line-height: 30px;
}
h3{
font-size: 18px;
line-height: 28px;
}
ul {
padding: 0 0 0 16px;
list-style: none outside none;
li::before {
content: "—";
opacity: .5;
font-size: 12px;
padding-right: 6px;
vertical-align: 10%;
}
li {
list-style: none;
padding: 0;
}
}
code{
background: var(--p30);
padding: 0 6px;
border-radius: 12px;
}
img{
margin-bottom: 14px;
}
blockquote{
border:1px solid var(--p30);
border-radius: 12px;
padding: 12px 40px;
position: relative;
&::before{
content: "\201C";
color: var(--p60);
font-size:4em;
position: absolute;
left: 10px;
top: 20px;
}
&::after{
content: '';
}
}
pre {
background: var(--grey-light);
border-radius: 0.5rem;
color: var(--white);
font-family: 'JetBrainsMono', monospace;
margin: 1.5rem 0;
padding: 0.75rem 1rem;
code {
background: none;
color: inherit;
font-size: 0.8rem;
padding: 0;
}
}
}
.lx-small-text {
font-size: 12px;
line-height: 15px;
}
.light-text{
color: var(--text-light);
}
-114
View File
@@ -1,114 +0,0 @@
.wrapper-tiny {
background-color: var(--p20);
border-radius: 12px;
margin: 44px auto;
width: 600px;
padding: 44px;
}
.common-wrapper {
background-color: var(--p20);
margin: 20px 0;
padding: 20px;
border-radius: 12px;
}
.wrapper-normal {
background-color: #fff;
border-radius: 32px;
margin: 44px auto;
width: 1000px;
padding: 44px;
&.transparent {
margin: 0px auto;
padding: 0px;
background-color: transparent;
}
}
.wrapper-large {
background-color: #fff;
border-radius: 32px;
margin: 44px auto;
max-width: 1920px;
min-width: 1000px;
padding: 44px;
width: fit-content;
&.transparent {
padding: 0px;
margin: 0px auto;
background-color: transparent;
}
}
@media only screen and (max-width: 1800px) {
.wrapper-normal {
margin: 0px 0px 0px auto;
padding: 20px;
&.transparent {
margin: 0px 0px 0px auto;
padding: 40px;
}
}
.wrapper-large {
margin: 44px 0px 0px auto;
padding: 44px;
&.transparent {
margin: 0px 0px 0px auto;
padding: 40px;
}
}
}
@media only screen and (max-width: 1390px) {
.wrapper-normal {
margin: 0px auto;
padding: 20px;
&.transparent {
margin: 0px auto;
padding: 40px;
}
}
.wrapper-large {
margin: 44px 0px 0px auto;
padding: 44px;
&.transparent {
margin: 0px 0px 0px auto;
padding: 40px;
}
}
}
.section-actions {
text-align: center;
padding: 32px 0;
}
.header-normal {
text-align: left;
font-weight: 400;
font-size: 20px;
}
.header-small {
text-align: left;
font-weight: 400;
font-size: 20px;
}
-5
View File
@@ -1,5 +0,0 @@
document.addEventListener("DOMContentLoaded", onReady);
function onReady(){
console.log("ready yo")
}
-108
View File
@@ -1,108 +0,0 @@
@import "./css/reset.css";
@import "./css/helpers.css";
@import "./css/notice.css";
@import "./css/auth.css";
@import "./css/typography.css";
@import "./css/sidebar.css";
@import "./css/form.css";
@import "./css/table.css";
@import "./css/avatar.css";
@import "./css/codemirror.css";
@import "./css/rich.css";
@import "./css/layout.css";
@import "./css/wrappers.css";
@import "./css/toolbar.css";
@import "./css/dropdown.css";
@import "./css/button.css";
@import "./css/checkbox.css";
@import "./css/pagination.css";
@import "./css/record-edit.css";
@import "./css/tabs.css";
@import "./css/switch.css";
@import "./css/preview.css";
@import "./css/dialog.css";
@import "./css/autocomplete.css";
@import "./css/reference-tags.css";
@import "./css/members.css";
@import "./css/revisions.css";
@import "./css/datepicker.css";
:root {
--p10: #f4f9ff;
--p20: #eaf1f9;
--p30: #b3ceff;
--p40: #8db5ff;
--p50: #70a2ff;
--p60: #679cff;
--p70: #4284ff;
--p80: #1c6bff;
--p90: #002b7a;
--p100: #000C23;
--suc10: #d1ffb8;
--suc20: #d1ffb8;
--suc30: #b5ff8d;
--suc40: #a2ff70;
--suc50: #82cc5a;
--suc80: #71b34e;
--suc90: #314c22;
--err10: #ffb9d0;
--err20: #ff9bb3;
--err30: #fe7e97;
--err40: #de617b;
--err50: #be4461;
--err80: #61001a;
--err90: #560012;
--grey-dark: #424656;
--grey-light: #a6abbd;
--text: var(--p100);
--text-light: var(--grey-dark);
--text-error: var(--err50);
--main-font: Open Sans, Arial, Helvetica, sans-serif;
}
body {
background-color: var(--p10);
font-family: var(--main-font), sans-serif;
color: var(--text);
:focus {
outline: none;
box-shadow: 0 0 1px 2px var(--p70);
}
}
.btn-spinner .spinner-border {
display: none;
}
.btn-spinner.spinner-on .spinner-border {
display: inline-block;
}
.cursor-pointer {
cursor: pointer;
}
a {
color: var(--text);
text-decoration: none;
}
.lucent-component {
position: relative;
}
+1 -14
View File
@@ -5,17 +5,6 @@
gap: 10px; gap: 10px;
background: var(--p10); background: var(--p10);
border-radius: 12px; border-radius: 12px;
&.is-trashed{
border: 2px solid var(--err10);
background: var(--p20);
}
.trashed-text{
background: var(--err10);
font-size: 12px;
padding:2px 10px;
}
.image{ .image{
@@ -31,9 +20,7 @@
&:hover{ &:hover{
background: var(--p30); background: var(--p30);
.reference-action{ .reference-action{
display: flex; display: block;
align-items: center;
gap: 3px;
} }
} }
} }
-9
View File
@@ -21,16 +21,8 @@
flex-direction: column; flex-direction: column;
gap: 3px; gap: 3px;
} }
.sidebar-folder{
width: 100%;
margin: 3px 12px 3px;
.sidebar-folder{
margin-left: 5px;
}
}
.sidebar-header { .sidebar-header {
width: 100%;
display: flex; display: flex;
cursor: pointer; cursor: pointer;
justify-content: space-between; justify-content: space-between;
@@ -38,7 +30,6 @@
background: var(--p30); background: var(--p30);
font-size: 16px; font-size: 16px;
padding: 3px 12px 3px; padding: 3px 12px 3px;
color: var(--text); color: var(--text);
border: none; border: none;
border-radius: 12px; border-radius: 12px;
+5 -1
View File
@@ -55,7 +55,11 @@
border: none; border: none;
overflow: hidden; overflow: hidden;
&.field-ui-number,&.field-ui-slug,&.field-ui-text,&.field-ui-rich,&.field-ui-url{
max-height: 24px;
text-overflow: ellipsis;
overflow: hidden;
}
//img{ //img{
// width: 48px; // width: 48px;
//} //}
+4
View File
@@ -105,3 +105,7 @@ a {
.lucent-component { .lucent-component {
position: relative; position: relative;
} }
[hidden] {
display: none;
}
-3
View File
@@ -1,8 +1,5 @@
@extends("lucent::layouts.account") @extends("lucent::layouts.account")
@section("title")
Log in
@endsection
@section("content") @section("content")
<div class="scope-login"> <div class="scope-login">
<div class="bg-image"> <div class="bg-image">
+1 -3
View File
@@ -1,7 +1,5 @@
@extends("lucent::layouts.account") @extends("lucent::layouts.account")
@section("title")
Enter
@endsection
@section("content") @section("content")
<div class="scope-login"> <div class="scope-login">
<div class="bg-image"> <div class="bg-image">

Some files were not shown because too many files have changed in this diff Show More