+
${schema.helpText ? `
${escapeHtml(schema.helpText)}
` : ""}
-
`;
- header.querySelector("button").addEventListener("click", function () {
- parent[key].push(createDefaultValue(schema.itemSchema));
- applyAutoSequenceToArray(schema, parent[key]);
- renderSection(tabKey);
- });
-
card.appendChild(header);
if (parent[key].length === 0) {
@@ -145,11 +136,11 @@
parent[key].forEach((item, index) => {
const itemCard = document.createElement("div");
- itemCard.className = "card shadow-sm border-0 mb-3";
+ itemCard.className = "card cms-item-card mb-3";
itemCard.dataset.index = String(index);
const itemHeader = document.createElement("div");
- itemHeader.className = "bg-white border-bottom px-3 py-3 d-flex justify-content-between align-items-center gap-2 flex-wrap";
+ itemHeader.className = "card-header d-flex justify-content-between align-items-center gap-2 flex-wrap";
const title = getArrayItemTitle(schema, item, index);
const subtitle = getArrayItemSubtitle(schema, item);
@@ -157,7 +148,7 @@
${
schema.sortable
- ? '
'
+ ? '
'
: ""
}
@@ -166,13 +157,22 @@
+
${renderItemActions(schema.itemActions, item)}
-
`;
+ itemHeader
+ .querySelector('[data-toggle-item="true"]')
+ .addEventListener("click", function () {
+ itemCard.classList.toggle("is-collapsed");
+ });
+
itemHeader
.querySelector('[data-remove-item="true"]')
.addEventListener("click", function () {
@@ -224,6 +224,17 @@
list.appendChild(itemCard);
});
+ const addButton = document.createElement("button");
+ addButton.type = "button";
+ addButton.className = "cms-add-button mt-2";
+ addButton.innerHTML = `
${escapeHtml(schema.addLabel || `Add ${schema.itemLabel || "Item"}`)}`;
+ addButton.addEventListener("click", function () {
+ parent[key].push(createDefaultValue(schema.itemSchema));
+ applyAutoSequenceToArray(schema, parent[key]);
+ renderSection(tabKey);
+ });
+ card.appendChild(addButton);
+
if (schema.sortable && window.Sortable) {
window.Sortable.create(list, {
animation: 150,
@@ -598,7 +609,7 @@
function appendHelp(col, schema, value, extraHint) {
const wrapper = document.createElement("div");
- wrapper.className = "d-flex justify-content-between gap-3";
+ wrapper.className = "field-meta-row";
const help = document.createElement("div");
help.className = "form-text";
@@ -608,7 +619,7 @@
let counter = null;
if (schema.maxLength) {
counter = document.createElement("div");
- counter.className = "form-text text-end ms-auto";
+ counter.className = "field-char-count";
updateCounter(counter, String(value || "").length, schema.maxLength);
wrapper.appendChild(counter);
}
diff --git a/views/layouts/main.ejs b/views/layouts/main.ejs
index a6b1f69..3c59693 100644
--- a/views/layouts/main.ejs
+++ b/views/layouts/main.ejs
@@ -283,18 +283,22 @@
.fixed-bottom-buttons {
position: fixed;
- bottom: 0;
+ left: 0;
right: 0;
- padding: 1rem;
+ bottom: 0;
+ padding: 0.875rem 1.5rem;
z-index: 1000;
- width: 25%;
display: flex;
justify-content: flex-end;
- gap: 0.5rem;
+ gap: 0.75rem;
+ background: rgba(255, 255, 255, 0.96);
+ border-top: 1px solid rgba(15, 23, 42, 0.08);
+ box-shadow: 0 -4px 6px -1px rgba(0, 0, 0, 0.08);
+ backdrop-filter: blur(8px);
}
.fixed-bottom-buttons .btn {
- min-width: 120px;
+ min-width: 136px;
display: inline-flex;
align-items: center;
justify-content: center;
@@ -303,7 +307,174 @@
/* Add padding to prevent content from being hidden behind fixed buttons */
.content-with-fixed-buttons {
- padding-bottom: 80px;
+ padding-bottom: 110px;
+ }
+
+ .content-with-fixed-buttons .card {
+ border: 1px solid rgba(148, 163, 184, 0.16) !important;
+ box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04), 0 8px 24px rgba(15, 23, 42, 0.04) !important;
+ }
+
+ .content-with-fixed-buttons .card-header {
+ background: #fff;
+ border-bottom: 1px solid rgba(148, 163, 184, 0.14);
+ padding: 0.875rem 1rem;
+ }
+
+ .content-with-fixed-buttons .card-body {
+ padding: 1rem;
+ }
+
+ .content-with-fixed-buttons .form-control,
+ .content-with-fixed-buttons .form-select {
+ background: #fff;
+ border: 1px solid rgba(148, 163, 184, 0.18);
+ box-shadow: none;
+ }
+
+ .content-with-fixed-buttons .form-control:focus,
+ .content-with-fixed-buttons .form-select:focus {
+ border-color: rgba(184, 183, 106, 0.55);
+ box-shadow: 0 0 0 0.2rem rgba(184, 183, 106, 0.12);
+ }
+
+ .content-with-fixed-buttons .form-text {
+ color: #94a3b8;
+ font-size: 0.75rem;
+ margin-top: 0.4rem;
+ }
+
+ .content-with-fixed-buttons .card-header-tabs {
+ margin-bottom: -0.875rem;
+ }
+
+ .content-with-fixed-buttons .nav-tabs {
+ border-bottom: 0;
+ gap: 0.25rem;
+ }
+
+ .content-with-fixed-buttons .nav-tabs .nav-link {
+ border: 0;
+ border-radius: 8px 8px 0 0;
+ color: #64748b;
+ padding: 0.75rem 0.95rem;
+ }
+
+ .content-with-fixed-buttons .nav-tabs .nav-link.active {
+ color: #0f172a;
+ background: rgba(248, 250, 252, 0.96);
+ box-shadow: inset 0 -2px 0 var(--primary-color);
+ }
+
+ .cms-editor-group {
+ background: #f8fafc;
+ border: 1px solid rgba(148, 163, 184, 0.12);
+ border-radius: 12px;
+ padding: 1rem;
+ }
+
+ .cms-item-card {
+ background: #f8fafc;
+ border: 1px solid rgba(148, 163, 184, 0.12) !important;
+ border-radius: 12px;
+ box-shadow: none !important;
+ }
+
+ .cms-item-card .card-header {
+ background: transparent;
+ border-bottom: 1px solid rgba(148, 163, 184, 0.1);
+ padding: 0.75rem 0.875rem;
+ }
+
+ .cms-item-card .card-body {
+ background: transparent;
+ padding: 0.875rem;
+ }
+
+ .cms-item-card.is-collapsed .card-body {
+ display: none;
+ }
+
+ .cms-item-card .drag-handle {
+ background: transparent;
+ border: 0;
+ color: #94a3b8;
+ padding: 0.25rem 0.375rem;
+ box-shadow: none;
+ }
+
+ .cms-item-card .drag-handle:hover {
+ color: #475569;
+ background: rgba(255, 255, 255, 0.75);
+ }
+
+ .cms-collapse-toggle {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ width: 30px;
+ height: 30px;
+ border: 0;
+ background: transparent;
+ color: #94a3b8;
+ border-radius: 999px;
+ }
+
+ .cms-collapse-toggle:hover {
+ background: rgba(148, 163, 184, 0.12);
+ color: #475569;
+ }
+
+ .cms-collapse-toggle i {
+ transition: transform 0.18s ease;
+ }
+
+ .cms-item-card.is-collapsed .cms-collapse-toggle i {
+ transform: rotate(-90deg);
+ }
+
+ .cms-remove-button {
+ border: 0;
+ background: transparent;
+ color: #94a3b8;
+ padding: 0.25rem;
+ box-shadow: none;
+ }
+
+ .cms-remove-button:hover {
+ color: #ef4444;
+ background: transparent;
+ }
+
+ .cms-add-button {
+ width: 100%;
+ border: 1px dashed rgba(148, 163, 184, 0.55);
+ border-radius: 10px;
+ background: rgba(255, 255, 255, 0.7);
+ color: #475569;
+ padding: 0.8rem 1rem;
+ font-weight: 600;
+ }
+
+ .cms-add-button:hover {
+ background: #fff;
+ border-color: rgba(184, 183, 106, 0.65);
+ color: var(--primary-dark);
+ }
+
+ .field-meta-row {
+ display: flex;
+ justify-content: space-between;
+ align-items: flex-start;
+ gap: 0.75rem;
+ margin-top: 0.35rem;
+ }
+
+ .field-char-count {
+ color: #94a3b8;
+ font-size: 0.75rem;
+ white-space: nowrap;
+ margin-left: auto;
}
main {