hoàn thành block editor của Pages
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
<header id="l--main-header" th:fragment="header">
|
||||
<header id="l--main-header" th:fragment="header">
|
||||
<h1 class="visually-hidden">The University of Massachusetts Amherst</h1>
|
||||
|
||||
<style>
|
||||
@@ -1354,7 +1354,7 @@
|
||||
|
||||
</div>
|
||||
<script defer src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
|
||||
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/lazysizes/5.3.2/lazysizes.min.js"></script>
|
||||
<script defer th:src="@{/js/lazysizes.min.js}"></script>
|
||||
<script defer th:src="@{/js/custom-umass.js}"></script>
|
||||
|
||||
<style>
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
|
||||
<!-- Custom styles for this template-->
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/startbootstrap-sb-admin-2/4.1.4/css/sb-admin-2.min.css" rel="stylesheet">
|
||||
|
||||
<!-- Project-wide CSS Variables & Custom Styles -->
|
||||
<link rel="stylesheet" th:href="@{/css/custom.css}">
|
||||
</head>
|
||||
|
||||
<body id="page-top">
|
||||
@@ -47,6 +50,30 @@
|
||||
<!-- Divider -->
|
||||
<hr class="sidebar-divider">
|
||||
|
||||
<!-- Heading -->
|
||||
<div class="sidebar-heading">
|
||||
Content
|
||||
</div>
|
||||
|
||||
<!-- Nav Item - Pages Collapse Menu -->
|
||||
<li class="nav-item">
|
||||
<a class="nav-link collapsed" href="#" data-toggle="collapse" data-target="#collapsePages"
|
||||
aria-expanded="true" aria-controls="collapsePages">
|
||||
<i class="fas fa-fw fa-file-alt"></i>
|
||||
<span>Pages</span>
|
||||
</a>
|
||||
<div id="collapsePages" class="collapse" aria-labelledby="headingPages" data-parent="#accordionSidebar">
|
||||
<div class="bg-white py-2 collapse-inner rounded">
|
||||
<h6 class="collapse-header">Page Management:</h6>
|
||||
<a class="collapse-item" th:href="@{/manage/pages}">All Pages</a>
|
||||
<a class="collapse-item" th:href="@{/manage/pages/new}">Add New</a>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<!-- Divider -->
|
||||
<hr class="sidebar-divider">
|
||||
|
||||
<!-- Heading -->
|
||||
<div class="sidebar-heading">
|
||||
Interface
|
||||
@@ -184,6 +211,9 @@
|
||||
<!-- Custom scripts for all pages-->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/startbootstrap-sb-admin-2/4.1.4/js/sb-admin-2.min.js"></script>
|
||||
|
||||
<!-- Page-specific scripts injected by child templates -->
|
||||
<section layout:fragment="scripts"></section>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,307 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||
layout:decorate="~{fragments/manage-layout}">
|
||||
|
||||
<head>
|
||||
<title th:text="${isNew} ? 'Add New Page' : 'Edit Page'">Page Form</title>
|
||||
<style>
|
||||
/* Editor.js container styling */
|
||||
#editorjs {
|
||||
border: 1px solid #d1d3e2;
|
||||
border-radius: 0.35rem;
|
||||
padding: 16px 12px;
|
||||
min-height: 350px;
|
||||
background: #fff;
|
||||
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
|
||||
}
|
||||
|
||||
#editorjs:focus-within {
|
||||
border-color: #bac8f3;
|
||||
box-shadow: 0 0 0 0.2rem rgba(78, 115, 223, 0.25);
|
||||
}
|
||||
|
||||
/* Block tool styling overrides */
|
||||
.ce-block__content {
|
||||
max-width: 100% !important;
|
||||
}
|
||||
|
||||
.ce-toolbar__content {
|
||||
max-width: 100% !important;
|
||||
}
|
||||
|
||||
/* Make the editor look clean inside the card */
|
||||
.codex-editor__redactor {
|
||||
padding-bottom: 80px !important;
|
||||
}
|
||||
|
||||
/* Plugin badge info */
|
||||
.editor-plugin-info {
|
||||
font-size: 0.75rem;
|
||||
color: #858796;
|
||||
}
|
||||
|
||||
.editor-plugin-info .badge {
|
||||
font-weight: 400;
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
|
||||
/* Full Screen Editor Mode */
|
||||
.editor-fullscreen {
|
||||
position: fixed !important;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
z-index: 9999 !important;
|
||||
margin: 0 !important;
|
||||
border-radius: 0 !important;
|
||||
border: none !important;
|
||||
overflow-y: auto !important;
|
||||
padding: 40px !important;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div layout:fragment="content">
|
||||
<!-- Page Heading -->
|
||||
<div class="d-sm-flex align-items-center justify-content-between mb-4">
|
||||
<h1 class="h3 mb-0 text-gray-800" th:text="${isNew} ? 'Add New Page' : 'Edit Page'">Page Form</h1>
|
||||
<a th:href="@{/manage/pages}" class="d-none d-sm-inline-block btn btn-sm btn-secondary shadow-sm">
|
||||
<i class="fas fa-arrow-left fa-sm text-white-50"></i> Back to All Pages
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Form Card -->
|
||||
<div class="card shadow mb-4">
|
||||
<div class="card-header py-3">
|
||||
<h6 class="m-0 font-weight-bold text-primary"
|
||||
th:text="${isNew} ? 'Create a New Page' : 'Update Page Details'">Form</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form id="pageForm" th:action="${isNew} ? @{/manage/pages} : @{/manage/pages/{id}(id=${page.id})}"
|
||||
th:object="${page}" method="post">
|
||||
|
||||
<!-- Validation errors summary -->
|
||||
<div th:if="${#fields.hasErrors('*')}" class="alert alert-danger">
|
||||
<ul class="mb-0">
|
||||
<li th:each="err : ${#fields.errors('*')}" th:text="${err}"></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Title -->
|
||||
<div class="form-group">
|
||||
<label for="pageTitle" class="font-weight-bold">Title <span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control" id="pageTitle" th:field="*{title}"
|
||||
th:classappend="${#fields.hasErrors('title')} ? 'is-invalid' : ''"
|
||||
placeholder="Enter page title (e.g. About Us, Contact)" required>
|
||||
<div class="invalid-feedback" th:if="${#fields.hasErrors('title')}" th:errors="*{title}"></div>
|
||||
</div>
|
||||
|
||||
<!-- Slug -->
|
||||
<div class="form-group">
|
||||
<label for="pageSlug" class="font-weight-bold">Slug (URL)</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">/page/</span>
|
||||
</div>
|
||||
<input type="text" class="form-control" id="pageSlug" th:field="*{slug}"
|
||||
th:classappend="${#fields.hasErrors('slug')} ? 'is-invalid' : ''"
|
||||
placeholder="auto-generated-from-title">
|
||||
<div class="invalid-feedback" th:if="${#fields.hasErrors('slug')}" th:errors="*{slug}">
|
||||
</div>
|
||||
</div>
|
||||
<small class="form-text text-muted">Leave blank to auto-generate from the title. Use lowercase
|
||||
letters, numbers, and hyphens only.</small>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<!-- Status -->
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="pageStatus" class="font-weight-bold">Status <span
|
||||
class="text-danger">*</span></label>
|
||||
<select class="form-control" id="pageStatus" th:field="*{status}">
|
||||
<option th:each="s : ${statuses}" th:value="${s}" th:text="${s}"></option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Display Order -->
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="pageDisplayOrder" class="font-weight-bold">Display Order</label>
|
||||
<input type="number" class="form-control" id="pageDisplayOrder"
|
||||
th:field="*{displayOrder}" placeholder="0" min="0">
|
||||
<small class="form-text text-muted">Lower numbers appear first.</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Meta Description -->
|
||||
<div class="form-group">
|
||||
<label for="pageMetaDescription" class="font-weight-bold">Meta Description (SEO)</label>
|
||||
<textarea class="form-control" id="pageMetaDescription" th:field="*{metaDescription}" rows="2"
|
||||
maxlength="500"
|
||||
placeholder="Brief description for search engines (max 500 characters)"></textarea>
|
||||
<small class="form-text text-muted">This appears in Google search results below the page
|
||||
title.</small>
|
||||
</div>
|
||||
|
||||
<!-- Block Editor Content -->
|
||||
<div class="form-group">
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<label class="font-weight-bold mb-0">
|
||||
<i class="fas fa-cubes"></i> Page Content (Block Editor)
|
||||
</label>
|
||||
<button type="button" class="btn btn-sm btn-outline-primary" id="toggleFullscreenBtn">
|
||||
<i class="fas fa-expand"></i> Full Screen Mode
|
||||
</button>
|
||||
</div>
|
||||
<div class="editor-plugin-info mb-2">
|
||||
Active blocks:
|
||||
<span class="badge badge-light">Heading</span>
|
||||
<span class="badge badge-light">List</span>
|
||||
<span class="badge badge-light">Quote</span>
|
||||
<span class="badge badge-light">Table</span>
|
||||
<span class="badge badge-light">Code</span>
|
||||
<span class="badge badge-light">Delimiter</span>
|
||||
<span class="badge badge-light">Warning</span>
|
||||
<span id="pluginBadges"></span>
|
||||
</div>
|
||||
|
||||
<!-- The Editor.js container -->
|
||||
<div id="editorjs"></div>
|
||||
|
||||
<!-- Hidden input to store the JSON content for form submission -->
|
||||
<input type="hidden" id="editorContent" name="content" th:value="*{content}">
|
||||
|
||||
<small class="form-text text-muted mt-2">
|
||||
<i class="fas fa-info-circle"></i>
|
||||
Click the <strong>+</strong> button or press <kbd>Tab</kbd> to add new blocks.
|
||||
Use the block menu (☰) to change block types or reorder.
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<!-- Submit Buttons -->
|
||||
<hr>
|
||||
<div class="d-flex justify-content-between">
|
||||
<a th:href="@{/manage/pages}" class="btn btn-secondary">
|
||||
<i class="fas fa-times"></i> Cancel
|
||||
</a>
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fas fa-save"></i>
|
||||
<span th:text="${isNew} ? 'Create Page' : 'Update Page'">Save</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Editor.js Scripts (injected via layout:fragment="scripts") -->
|
||||
<section layout:fragment="scripts">
|
||||
<!-- Editor.js Core -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/@editorjs/editorjs@2.30.8/dist/editorjs.umd.js"></script>
|
||||
|
||||
<!-- Built-in Block Tools -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/@editorjs/header@2.8.8/dist/header.umd.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/@editorjs/nested-list@1.4.3/dist/nested-list.umd.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/@editorjs/quote@2.7.4/dist/quote.umd.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/@editorjs/delimiter@1.4.2/dist/delimiter.umd.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/@editorjs/table@2.4.2/dist/table.umd.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/@editorjs/code@2.9.3/dist/code.umd.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/@editorjs/warning@1.4.1/dist/warning.umd.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/@editorjs/marker@1.4.0/dist/marker.umd.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/@editorjs/inline-code@1.5.1/dist/inline-code.umd.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/@editorjs/underline@1.1.0/dist/bundle.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/@editorjs/image@2.9.0/dist/image.umd.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/@editorjs/attaches@1.3.0/dist/bundle.js"></script>
|
||||
|
||||
<!--
|
||||
============================================================
|
||||
FUTURE PLUGINS: Load your custom block plugin scripts HERE.
|
||||
They must be loaded BEFORE editor-config.js so they can
|
||||
register into window.SISEditorPlugins.
|
||||
|
||||
Example:
|
||||
<script th:src="@{/js/manage/editor-plugins/my-video-block.js}"></script>
|
||||
<script th:src="@{/js/manage/editor-plugins/my-gallery-block.js}"></script>
|
||||
============================================================
|
||||
-->
|
||||
|
||||
<!-- Custom SIS Editor Plugins -->
|
||||
<script th:src="@{/js/manage/editor-plugins/html-snippet.js}"></script>
|
||||
|
||||
<!-- Init Editor -->
|
||||
<script th:src="@{/js/manage/editor-config.js}"></script>
|
||||
|
||||
<!-- Initialize the editor with existing content (if editing) -->
|
||||
<script th:inline="javascript">
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
var existingContent = document.getElementById('editorContent').value;
|
||||
var initialData = null;
|
||||
|
||||
if (existingContent && existingContent.trim() !== '') {
|
||||
try {
|
||||
initialData = JSON.parse(existingContent);
|
||||
} catch (e) {
|
||||
console.warn('[SIS Editor] Existing content is not valid JSON, starting fresh.');
|
||||
}
|
||||
}
|
||||
|
||||
window.sisEditor = initSISEditor('editorjs', 'editorContent', initialData);
|
||||
|
||||
// Show badges for any injected plugins
|
||||
var pluginBadges = document.getElementById('pluginBadges');
|
||||
var pluginKeys = Object.keys(window.SISEditorPlugins || {});
|
||||
if (pluginBadges && pluginKeys.length > 0) {
|
||||
pluginKeys.forEach(function (key) {
|
||||
var badge = document.createElement('span');
|
||||
badge.className = 'badge badge-info ml-1';
|
||||
badge.textContent = key + ' (plugin)';
|
||||
pluginBadges.appendChild(badge);
|
||||
});
|
||||
}
|
||||
// Warn user before leaving page if they have unsaved changes
|
||||
var isFormSubmitted = false;
|
||||
document.getElementById('pageForm').addEventListener('submit', function () {
|
||||
isFormSubmitted = true;
|
||||
});
|
||||
|
||||
window.addEventListener('beforeunload', function (e) {
|
||||
if (!isFormSubmitted) {
|
||||
var confirmationMessage = 'You may have unsaved changes. Are you sure you want to leave?';
|
||||
e.returnValue = confirmationMessage;
|
||||
return confirmationMessage;
|
||||
}
|
||||
});
|
||||
|
||||
// Full Screen Mode Toggle
|
||||
var editorContainer = document.getElementById('editorjs');
|
||||
var fsBtn = document.getElementById('toggleFullscreenBtn');
|
||||
var fsIcon = fsBtn.querySelector('i');
|
||||
|
||||
fsBtn.addEventListener('click', function () {
|
||||
editorContainer.classList.toggle('editor-fullscreen');
|
||||
if (editorContainer.classList.contains('editor-fullscreen')) {
|
||||
fsIcon.classList.remove('fa-expand');
|
||||
fsIcon.classList.add('fa-compress');
|
||||
fsBtn.style.position = 'fixed';
|
||||
fsBtn.style.top = '10px';
|
||||
fsBtn.style.right = '20px';
|
||||
fsBtn.style.zIndex = '10000';
|
||||
} else {
|
||||
fsIcon.classList.remove('fa-compress');
|
||||
fsIcon.classList.add('fa-expand');
|
||||
fsBtn.style.position = 'static';
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</section>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,95 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{fragments/manage-layout}">
|
||||
|
||||
<head>
|
||||
<title>All Pages</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div layout:fragment="content">
|
||||
<!-- Page Heading -->
|
||||
<div class="d-sm-flex align-items-center justify-content-between mb-4">
|
||||
<h1 class="h3 mb-0 text-gray-800">All Pages</h1>
|
||||
<a th:href="@{/manage/pages/new}" class="d-none d-sm-inline-block btn btn-sm btn-primary shadow-sm">
|
||||
<i class="fas fa-plus fa-sm text-white-50"></i> Add New Page
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Success Message -->
|
||||
<div th:if="${successMessage}" class="alert alert-success alert-dismissible fade show" role="alert">
|
||||
<span th:text="${successMessage}"></span>
|
||||
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Error Message -->
|
||||
<div th:if="${errorMessage}" class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||
<span th:text="${errorMessage}"></span>
|
||||
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Pages Table -->
|
||||
<div class="card shadow mb-4">
|
||||
<div class="card-header py-3">
|
||||
<h6 class="m-0 font-weight-bold text-primary">Manage Static Pages</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered" id="pagesTable" width="100%" cellspacing="0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="5%">#</th>
|
||||
<th width="25%">Title</th>
|
||||
<th width="20%">Slug</th>
|
||||
<th width="10%">Status</th>
|
||||
<th width="8%">Order</th>
|
||||
<th width="17%">Last Modified</th>
|
||||
<th width="15%">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="page, iterStat : ${pages}">
|
||||
<td th:text="${iterStat.count}"></td>
|
||||
<td th:text="${page.title}"></td>
|
||||
<td>
|
||||
<code th:text="${page.slug}"></code>
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge"
|
||||
th:classappend="${page.status.name() == 'PUBLISHED'} ? 'badge-success' : (${page.status.name() == 'DRAFT'} ? 'badge-warning' : 'badge-secondary')"
|
||||
th:text="${page.status}">
|
||||
</span>
|
||||
</td>
|
||||
<td th:text="${page.displayOrder}"></td>
|
||||
<td th:text="${page.lastModifiedDate != null} ? ${#temporals.format(page.lastModifiedDate, 'yyyy-MM-dd HH:mm')} : '-'"></td>
|
||||
<td>
|
||||
<a th:href="@{/manage/pages/{id}/edit(id=${page.id})}" class="btn btn-sm btn-info" title="Edit">
|
||||
<i class="fas fa-edit"></i> Edit
|
||||
</a>
|
||||
<form th:action="@{/manage/pages/{id}/delete(id=${page.id})}" method="post" style="display:inline;"
|
||||
onsubmit="return confirm('Are you sure you want to delete this page?');">
|
||||
<button type="submit" class="btn btn-sm btn-danger" title="Delete">
|
||||
<i class="fas fa-trash"></i> Delete
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<tr th:if="${#lists.isEmpty(pages)}">
|
||||
<td colspan="7" class="text-center text-muted py-4">
|
||||
<i class="fas fa-file-alt fa-2x mb-2 d-block"></i>
|
||||
No pages found. Click "Add New Page" to create one.
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,110 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{fragments/layout}">
|
||||
|
||||
<head>
|
||||
<title th:text="${page.title}">Page Title</title>
|
||||
<!-- Add Meta Description for SEO -->
|
||||
<meta name="description" th:if="${page.metaDescription != null}" th:content="${page.metaDescription}" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!--
|
||||
Because the user wants absolute freedom to design the page using snippets,
|
||||
we will render the blocks directly into the content fragment with NO constraining containers.
|
||||
If they want a container, they can use a standard snippet.
|
||||
-->
|
||||
<div layout:fragment="content">
|
||||
|
||||
<!-- Render Editor.js Blocks -->
|
||||
<th:block th:each="block : ${blocks}">
|
||||
|
||||
<!-- 1. HTML Snippet Block (Custom) -->
|
||||
<th:block th:if="${block.type == 'snippet'}">
|
||||
<div th:insert="~{'snippets/' + ${block.data.id}}"></div>
|
||||
</th:block>
|
||||
|
||||
<!-- 2. Header Block -->
|
||||
<th:block th:if="${block.type == 'header'}">
|
||||
<!-- Editor.js header block has level (1-6) and text -->
|
||||
<div class="container my-3">
|
||||
<th:block th:switch="${block.data.level}">
|
||||
<h1 th:case="1" th:utext="${block.data.text}"></h1>
|
||||
<h2 th:case="2" th:utext="${block.data.text}"></h2>
|
||||
<h3 th:case="3" th:utext="${block.data.text}"></h3>
|
||||
<h4 th:case="4" th:utext="${block.data.text}"></h4>
|
||||
<h5 th:case="5" th:utext="${block.data.text}"></h5>
|
||||
<h6 th:case="6" th:utext="${block.data.text}"></h6>
|
||||
<h2 th:case="*" th:utext="${block.data.text}"></h2>
|
||||
</th:block>
|
||||
</div>
|
||||
</th:block>
|
||||
|
||||
<!-- 3. Paragraph Block -->
|
||||
<th:block th:if="${block.type == 'paragraph'}">
|
||||
<div class="container">
|
||||
<p th:utext="${block.data.text}"></p>
|
||||
</div>
|
||||
</th:block>
|
||||
|
||||
<!-- 4. List Block -->
|
||||
<th:block th:if="${block.type == 'list'}">
|
||||
<div class="container">
|
||||
<ul th:if="${block.data.style == 'unordered'}">
|
||||
<li th:each="item : ${block.data.items}" th:utext="${item}"></li>
|
||||
</ul>
|
||||
<ol th:if="${block.data.style == 'ordered'}">
|
||||
<li th:each="item : ${block.data.items}" th:utext="${item}"></li>
|
||||
</ol>
|
||||
</div>
|
||||
</th:block>
|
||||
|
||||
<!-- 5. Image Block -->
|
||||
<th:block th:if="${block.type == 'image'}">
|
||||
<div class="container text-center my-4">
|
||||
<img th:src="${block.data.file.url}" class="img-fluid" th:alt="${block.data.caption}" />
|
||||
<p class="text-muted small mt-1" th:if="${block.data.caption}" th:utext="${block.data.caption}"></p>
|
||||
</div>
|
||||
</th:block>
|
||||
|
||||
<!-- 6. Quote Block -->
|
||||
<th:block th:if="${block.type == 'quote'}">
|
||||
<div class="container my-4">
|
||||
<blockquote class="blockquote">
|
||||
<p class="mb-0" th:utext="${block.data.text}"></p>
|
||||
<footer class="blockquote-footer" th:if="${block.data.caption}" th:utext="${block.data.caption}"></footer>
|
||||
</blockquote>
|
||||
</div>
|
||||
</th:block>
|
||||
|
||||
<!-- 7. Delimiter Block -->
|
||||
<th:block th:if="${block.type == 'delimiter'}">
|
||||
<div class="container text-center my-4">
|
||||
<span style="font-size: 24px; letter-spacing: 10px; color: #ccc;">***</span>
|
||||
</div>
|
||||
</th:block>
|
||||
|
||||
<!-- 8. Table Block -->
|
||||
<th:block th:if="${block.type == 'table'}">
|
||||
<div class="container my-4">
|
||||
<table class="table table-bordered">
|
||||
<tbody>
|
||||
<tr th:each="row, rowStat : ${block.data.content}">
|
||||
<!-- If withHeadings is true, make first row <th> -->
|
||||
<th:block th:if="${block.data.withHeadings == true and rowStat.index == 0}">
|
||||
<th th:each="cell : ${row}" th:utext="${cell}"></th>
|
||||
</th:block>
|
||||
<th:block th:unless="${block.data.withHeadings == true and rowStat.index == 0}">
|
||||
<td th:each="cell : ${row}" th:utext="${cell}"></td>
|
||||
</th:block>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</th:block>
|
||||
|
||||
</th:block>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,5 @@
|
||||
<div style="background-color: var(--color-primary-light); border-left: 4px solid var(--color-primary); padding: var(--spacing-md); margin: var(--spacing-md) 0; border-radius: var(--radius-md); font-family: var(--font-family-base);">
|
||||
<h2 style="margin-top: 0; color: var(--color-primary); font-family: var(--font-family-heading);">This is a predefined Snippet!</h2>
|
||||
<p style="margin-bottom: 0; color: var(--color-text-main);">You have successfully loaded the <strong>test_banner</strong> HTML snippet via the Editor.js custom block.</p>
|
||||
<button style="margin-top: 15px; background: var(--color-primary); color: var(--color-text-light); border: none; padding: 10px 20px; border-radius: var(--radius-md); cursor: pointer;">Action Button</button>
|
||||
</div>
|
||||
Reference in New Issue
Block a user