forked from UKSOURCE/cms.lams
Replace the legacy section-based policy editor with a flexible block-based system. This transition enables real-time previews, a wider variety of content types (headings, quotes, callouts), and improved data normalization. - Implement `policies-block-editor.js` for dynamic content management - Add `normalizePoliciesDocument` and `validateContent` utilities to handle data migration and integrity - Update `policiesController.js` to support the new block structure and validation logic - Migrate `policies.json` from `sections` array to a `content.blocks` structure - Remove obsolete section editor views and partials - Update `policiesConfig.js` to reflect the new editor capabilities
660 lines
18 KiB
Plaintext
660 lines
18 KiB
Plaintext
<style>
|
|
#policyBlockEditor.container {
|
|
max-width: 85%;
|
|
}
|
|
|
|
.policy-block-editor {
|
|
--editor-border: rgba(148, 163, 184, 0.22);
|
|
--editor-muted: #64748b;
|
|
--editor-bg: #f8fafc;
|
|
--editor-surface: #ffffff;
|
|
--editor-soft: #eef2ff;
|
|
--editor-danger: #dc2626;
|
|
--editor-warning: #d97706;
|
|
--editor-success: #15803d;
|
|
--editor-shadow: 0 18px 40px rgba(15, 23, 42, 0.08);
|
|
padding-bottom: 140px;
|
|
}
|
|
|
|
.policy-block-editor .editor-shell {
|
|
display: grid;
|
|
grid-template-columns: minmax(0, 1.18fr) minmax(320px, 0.82fr);
|
|
gap: 1.5rem;
|
|
align-items: start;
|
|
}
|
|
|
|
.policy-block-editor .editor-panel,
|
|
.policy-block-editor .preview-panel {
|
|
border: 1px solid var(--editor-border);
|
|
border-radius: 24px;
|
|
background: rgba(255, 255, 255, 0.96);
|
|
box-shadow: var(--editor-shadow);
|
|
}
|
|
|
|
.policy-block-editor .preview-mode-toggle {
|
|
display: inline-flex;
|
|
border: 1px solid var(--editor-border);
|
|
border-radius: 999px;
|
|
padding: 0.25rem;
|
|
background: var(--editor-bg);
|
|
gap: 0.25rem;
|
|
}
|
|
|
|
.policy-block-editor .segment-button {
|
|
border: 0;
|
|
background: transparent;
|
|
color: var(--editor-muted);
|
|
padding: 0.55rem 0.95rem;
|
|
border-radius: 999px;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.policy-block-editor .segment-button.is-active {
|
|
background: #0f172a;
|
|
color: #fff;
|
|
}
|
|
|
|
.policy-block-editor .editor-panel,
|
|
.policy-block-editor .preview-panel {
|
|
padding: 1.25rem;
|
|
position: sticky;
|
|
top: 92px;
|
|
}
|
|
|
|
.policy-block-editor .editor-panel {
|
|
min-height: calc(100vh - 210px);
|
|
}
|
|
|
|
.policy-block-editor .preview-panel {
|
|
min-height: calc(100vh - 210px);
|
|
}
|
|
|
|
.policy-block-editor .panel-toolbar {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
justify-content: space-between;
|
|
gap: 0.75rem;
|
|
padding-bottom: 1rem;
|
|
border-bottom: 1px solid rgba(148, 163, 184, 0.14);
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.policy-block-editor .editor-toolbar-main,
|
|
.policy-block-editor .preview-toolbar-actions {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 0.65rem;
|
|
align-items: center;
|
|
}
|
|
|
|
.policy-block-editor .editor-toolbar-main {
|
|
flex: 1 1 360px;
|
|
}
|
|
|
|
.policy-block-editor .editor-toolbar-secondary {
|
|
display: none;
|
|
}
|
|
|
|
.policy-block-editor .preview-toolbar-actions {
|
|
justify-content: flex-end;
|
|
}
|
|
|
|
.policy-block-editor .editor-toolbar-title {
|
|
min-width: 220px;
|
|
margin-right: 0.35rem;
|
|
}
|
|
|
|
.policy-block-editor .editor-scroll {
|
|
max-height: calc(100vh - 340px);
|
|
overflow: auto;
|
|
padding-right: 0.35rem;
|
|
}
|
|
|
|
.policy-block-editor .validation-banner {
|
|
display: none;
|
|
gap: 0.75rem;
|
|
align-items: flex-start;
|
|
padding: 0.9rem 1rem;
|
|
border-radius: 18px;
|
|
border: 1px solid rgba(217, 119, 6, 0.25);
|
|
background: rgba(251, 191, 36, 0.12);
|
|
color: #92400e;
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.policy-block-editor .validation-banner.is-visible {
|
|
display: flex;
|
|
}
|
|
|
|
.policy-block-editor .block-list {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 1rem;
|
|
}
|
|
|
|
.policy-block-editor .block-card {
|
|
position: relative;
|
|
border: 1px solid var(--editor-border);
|
|
border-radius: 22px;
|
|
background: var(--editor-surface);
|
|
overflow: hidden;
|
|
transition: border-color 0.16s ease, box-shadow 0.16s ease, transform 0.16s ease;
|
|
}
|
|
|
|
.policy-block-editor .block-card:hover,
|
|
.policy-block-editor .block-card.is-selected {
|
|
border-color: rgba(184, 183, 106, 0.62);
|
|
box-shadow: 0 18px 36px rgba(15, 23, 42, 0.08);
|
|
}
|
|
|
|
.policy-block-editor .block-card.is-invalid {
|
|
border-color: rgba(220, 38, 38, 0.55);
|
|
box-shadow: 0 0 0 3px rgba(220, 38, 38, 0.08);
|
|
}
|
|
|
|
.policy-block-editor .block-card.is-collapsed .block-body {
|
|
display: none;
|
|
}
|
|
|
|
.policy-block-editor .block-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
gap: 0.75rem;
|
|
align-items: center;
|
|
padding: 0.9rem 1rem;
|
|
background: linear-gradient(180deg, rgba(248, 250, 252, 0.95), rgba(255, 255, 255, 0.96));
|
|
border-bottom: 1px solid rgba(148, 163, 184, 0.12);
|
|
}
|
|
|
|
.policy-block-editor .block-title {
|
|
display: flex;
|
|
gap: 0.75rem;
|
|
align-items: center;
|
|
min-width: 0;
|
|
}
|
|
|
|
.policy-block-editor .block-index {
|
|
width: 34px;
|
|
height: 34px;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
border-radius: 12px;
|
|
background: rgba(184, 183, 106, 0.12);
|
|
color: #0f172a;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.policy-block-editor .block-actions {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.45rem;
|
|
opacity: 0.18;
|
|
transition: opacity 0.16s ease;
|
|
}
|
|
|
|
.policy-block-editor .block-card:hover .block-actions,
|
|
.policy-block-editor .block-card.is-selected .block-actions {
|
|
opacity: 1;
|
|
}
|
|
|
|
.policy-block-editor .icon-button {
|
|
width: 34px;
|
|
height: 34px;
|
|
border: 1px solid rgba(148, 163, 184, 0.18);
|
|
border-radius: 12px;
|
|
background: #fff;
|
|
color: #334155;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.policy-block-editor .icon-button:hover {
|
|
border-color: rgba(184, 183, 106, 0.65);
|
|
color: #0f172a;
|
|
}
|
|
|
|
.policy-block-editor .icon-button.is-danger:hover {
|
|
border-color: rgba(220, 38, 38, 0.55);
|
|
color: var(--editor-danger);
|
|
}
|
|
|
|
.policy-block-editor .drag-handle {
|
|
cursor: grab;
|
|
}
|
|
|
|
.policy-block-editor .drag-handle:active {
|
|
cursor: grabbing;
|
|
}
|
|
|
|
.policy-block-editor .block-body {
|
|
padding: 1rem;
|
|
}
|
|
|
|
.policy-block-editor .block-content {
|
|
border: 1px solid rgba(148, 163, 184, 0.18);
|
|
border-radius: 18px;
|
|
background: #fff;
|
|
min-height: 108px;
|
|
padding: 0.9rem 1rem;
|
|
outline: none;
|
|
}
|
|
|
|
.policy-block-editor .block-content:focus {
|
|
border-color: rgba(184, 183, 106, 0.65);
|
|
box-shadow: 0 0 0 3px rgba(184, 183, 106, 0.12);
|
|
}
|
|
|
|
.policy-block-editor .block-content[data-placeholder]:empty::before,
|
|
.policy-block-editor .list-item-content[data-placeholder]:empty::before {
|
|
content: attr(data-placeholder);
|
|
color: #94a3b8;
|
|
}
|
|
|
|
.policy-block-editor .block-meta-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
gap: 0.75rem;
|
|
margin-bottom: 0.9rem;
|
|
}
|
|
|
|
.policy-block-editor .list-items {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.75rem;
|
|
}
|
|
|
|
.policy-block-editor .list-item {
|
|
display: grid;
|
|
grid-template-columns: auto 1fr auto;
|
|
gap: 0.75rem;
|
|
align-items: start;
|
|
padding: 0.75rem;
|
|
border-radius: 18px;
|
|
background: var(--editor-bg);
|
|
border: 1px solid rgba(148, 163, 184, 0.12);
|
|
}
|
|
|
|
.policy-block-editor .list-item-content {
|
|
min-height: 70px;
|
|
padding: 0.65rem 0.8rem;
|
|
border-radius: 16px;
|
|
border: 1px solid rgba(148, 163, 184, 0.18);
|
|
background: #fff;
|
|
outline: none;
|
|
}
|
|
|
|
.policy-block-editor .preview-frame {
|
|
border-radius: 28px;
|
|
background: linear-gradient(180deg, #f8fafc, #ffffff);
|
|
border: 1px solid rgba(148, 163, 184, 0.16);
|
|
padding: 1rem;
|
|
overflow-x: auto;
|
|
overflow-y: hidden;
|
|
scroll-behavior: smooth;
|
|
}
|
|
|
|
.policy-block-editor .preview-device {
|
|
margin: 0 auto;
|
|
min-width: 0;
|
|
}
|
|
|
|
.policy-block-editor .preview-device[data-mode="desktop"] {
|
|
width: 1120px;
|
|
}
|
|
|
|
.policy-block-editor .preview-device[data-mode="tablet"] {
|
|
width: 820px;
|
|
}
|
|
|
|
.policy-block-editor .preview-device[data-mode="mobile"] {
|
|
width: 390px;
|
|
}
|
|
|
|
.policy-block-editor .preview-canvas {
|
|
background: #fff;
|
|
border-radius: 24px;
|
|
border: 1px solid rgba(148, 163, 184, 0.14);
|
|
padding: 2rem;
|
|
min-height: 480px;
|
|
}
|
|
|
|
.policy-block-editor .preview-device[data-mode="desktop"] .preview-canvas {
|
|
min-height: 720px;
|
|
padding: 2.5rem;
|
|
}
|
|
|
|
.policy-block-editor .preview-device[data-mode="desktop"] #previewContent {
|
|
max-width: none;
|
|
}
|
|
|
|
.policy-block-editor .preview-block {
|
|
padding: 0.65rem 0;
|
|
border-radius: 14px;
|
|
scroll-margin-top: 140px;
|
|
}
|
|
|
|
.policy-block-editor .preview-block.is-highlighted {
|
|
background: rgba(184, 183, 106, 0.12);
|
|
outline: 1px solid rgba(184, 183, 106, 0.35);
|
|
}
|
|
|
|
.policy-block-editor .preview-block.is-targeted {
|
|
background: rgba(37, 99, 235, 0.1);
|
|
outline: 1px solid rgba(37, 99, 235, 0.45);
|
|
box-shadow: 0 0 0 6px rgba(37, 99, 235, 0.08);
|
|
animation: preview-target-pulse 0.9s ease;
|
|
}
|
|
|
|
@keyframes preview-target-pulse {
|
|
0% {
|
|
transform: scale(0.985);
|
|
box-shadow: 0 0 0 0 rgba(37, 99, 235, 0.18);
|
|
}
|
|
|
|
55% {
|
|
transform: scale(1);
|
|
box-shadow: 0 0 0 10px rgba(37, 99, 235, 0.08);
|
|
}
|
|
|
|
100% {
|
|
transform: scale(1);
|
|
box-shadow: 0 0 0 6px rgba(37, 99, 235, 0.08);
|
|
}
|
|
}
|
|
|
|
.policy-block-editor .callout-block {
|
|
border-radius: 22px;
|
|
padding: 1.1rem 1.2rem;
|
|
border: 1px solid rgba(148, 163, 184, 0.16);
|
|
background: #f8fafc;
|
|
}
|
|
|
|
.policy-block-editor .callout-block[data-tone="warning"] {
|
|
background: rgba(251, 191, 36, 0.1);
|
|
border-color: rgba(217, 119, 6, 0.28);
|
|
}
|
|
|
|
.policy-block-editor .callout-block[data-tone="success"] {
|
|
background: rgba(34, 197, 94, 0.1);
|
|
border-color: rgba(21, 128, 61, 0.22);
|
|
}
|
|
|
|
#floatingToolbar,
|
|
#linkPopover,
|
|
#slashMenu {
|
|
position: fixed;
|
|
z-index: 1045;
|
|
border-radius: 18px;
|
|
border: 1px solid rgba(148, 163, 184, 0.18);
|
|
background: rgba(15, 23, 42, 0.96);
|
|
color: #fff;
|
|
box-shadow: 0 18px 38px rgba(15, 23, 42, 0.24);
|
|
padding: 0.55rem;
|
|
display: none;
|
|
}
|
|
|
|
#floatingToolbar.is-visible,
|
|
#linkPopover.is-visible,
|
|
#slashMenu.is-visible {
|
|
display: block;
|
|
}
|
|
|
|
#floatingToolbar .floating-toolbar-group {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 0.35rem;
|
|
}
|
|
|
|
#floatingToolbar button,
|
|
#slashMenu button {
|
|
border: 0;
|
|
border-radius: 12px;
|
|
padding: 0.5rem 0.65rem;
|
|
background: transparent;
|
|
color: #fff;
|
|
}
|
|
|
|
#floatingToolbar button:hover,
|
|
#slashMenu button:hover {
|
|
background: rgba(255, 255, 255, 0.12);
|
|
}
|
|
|
|
#slashMenu .slash-menu-list {
|
|
display: flex;
|
|
flex-direction: column;
|
|
min-width: 210px;
|
|
gap: 0.25rem;
|
|
}
|
|
|
|
.policy-block-editor .editor-footer {
|
|
position: fixed;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
z-index: 1035;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
gap: 1rem;
|
|
padding: 0.9rem 1.25rem;
|
|
background: rgba(255, 255, 255, 0.96);
|
|
border-top: 1px solid rgba(148, 163, 184, 0.14);
|
|
box-shadow: 0 -18px 36px rgba(15, 23, 42, 0.08);
|
|
backdrop-filter: blur(10px);
|
|
}
|
|
|
|
.policy-block-editor .editor-footer .btn {
|
|
min-width: 136px;
|
|
}
|
|
|
|
.policy-block-editor .drop-indicator {
|
|
height: 4px;
|
|
border-radius: 999px;
|
|
background: linear-gradient(90deg, #0f172a, rgba(184, 183, 106, 0.92));
|
|
margin: -0.35rem 0 0.65rem;
|
|
display: none;
|
|
}
|
|
|
|
.policy-block-editor .drop-indicator.is-visible {
|
|
display: block;
|
|
}
|
|
|
|
.policy-block-editor .sortable-ghost {
|
|
opacity: 0.35;
|
|
}
|
|
|
|
.policy-block-editor .sortable-chosen {
|
|
box-shadow: 0 18px 36px rgba(15, 23, 42, 0.16);
|
|
}
|
|
|
|
@media (max-width: 1200px) {
|
|
.policy-block-editor .editor-shell {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
|
|
.policy-block-editor .editor-panel,
|
|
.policy-block-editor .preview-panel {
|
|
position: static;
|
|
min-height: auto;
|
|
}
|
|
|
|
.policy-block-editor .editor-scroll {
|
|
max-height: none;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.policy-block-editor .block-meta-grid {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
|
|
.policy-block-editor .editor-toolbar-secondary,
|
|
.policy-block-editor .preview-toolbar-actions {
|
|
justify-content: flex-start;
|
|
}
|
|
|
|
.policy-block-editor .editor-footer {
|
|
flex-direction: column;
|
|
}
|
|
|
|
.policy-block-editor .editor-footer .btn,
|
|
.policy-block-editor .editor-footer a {
|
|
width: 100%;
|
|
}
|
|
}
|
|
</style>
|
|
|
|
<div
|
|
class="container policy-block-editor"
|
|
id="policyBlockEditor"
|
|
data-policy-id="<%= editorConfig.policyId %>"
|
|
data-route-base="<%= editorConfig.routeBase %>"
|
|
>
|
|
<form action="<%= editorConfig.routeBase %>/update" method="POST" id="policyBlockEditorForm" novalidate>
|
|
<input type="hidden" name="pageJson" id="pageJson" />
|
|
<input type="hidden" name="intent" id="editorIntent" value="save" />
|
|
|
|
<div class="editor-shell">
|
|
<section class="editor-panel">
|
|
<div class="panel-toolbar">
|
|
<div class="editor-toolbar-main">
|
|
<div class="editor-toolbar-title">
|
|
<p class="text-uppercase text-muted small fw-semibold mb-1">Modern CMS Editor</p>
|
|
<h1 class="h4 mb-0" style="color: var(--primary-dark)"><%= data.policy.title %></h1>
|
|
</div>
|
|
<select class="form-select" id="blockTypeSelect" style="min-width: 180px">
|
|
<option value="paragraph">Paragraph</option>
|
|
<option value="heading">Heading</option>
|
|
<option value="list">List</option>
|
|
<option value="quote">Quote</option>
|
|
<option value="callout">Callout</option>
|
|
<option value="divider">Divider</option>
|
|
</select>
|
|
<button type="button" class="btn btn-primary" id="addBlockButton">
|
|
<i class="fas fa-plus me-2"></i>Add block
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="validation-banner" id="validationBanner">
|
|
<i class="fas fa-triangle-exclamation mt-1"></i>
|
|
<div>
|
|
<div class="fw-semibold">Validation needed before saving</div>
|
|
<div id="validationSummary" class="small"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="visual-editor">
|
|
<div class="editor-scroll">
|
|
<div class="drop-indicator" id="dropIndicator"></div>
|
|
<div class="block-list" id="blockList"></div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<aside class="preview-panel">
|
|
<div class="panel-toolbar">
|
|
<div>
|
|
<h2 class="h6 mb-1">Realtime Preview</h2>
|
|
<p class="text-muted small mb-0">Semantic rendering of the current content.</p>
|
|
</div>
|
|
<div class="preview-toolbar-actions">
|
|
<div class="preview-mode-toggle" data-role="preview-mode-toggle">
|
|
<button type="button" class="segment-button is-active" data-preview-mode="desktop">Desktop</button>
|
|
<button type="button" class="segment-button" data-preview-mode="tablet">Tablet</button>
|
|
<button type="button" class="segment-button" data-preview-mode="mobile">Mobile</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="preview-frame">
|
|
<div class="preview-device" id="previewDevice" data-mode="desktop">
|
|
<div class="preview-canvas">
|
|
<div class="mb-4">
|
|
<div class="text-uppercase small fw-semibold text-muted mb-2"><%= data.policy.navLabel %></div>
|
|
<h2 class="h3 mb-2"><%= data.policy.title %></h2>
|
|
<% if (data.policy.effectiveDate) { %>
|
|
<p class="text-muted mb-2"><%= data.policy.effectiveDate %></p>
|
|
<% } %>
|
|
<% if (data.policy.intro) { %>
|
|
<p class="text-muted mb-0"><%= data.policy.intro %></p>
|
|
<% } %>
|
|
</div>
|
|
<div id="previewContent"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</aside>
|
|
</div>
|
|
|
|
<div class="editor-footer">
|
|
<div class="d-flex gap-2 flex-wrap">
|
|
<a href="/admin/policies?tab=policies" class="btn btn-outline-secondary">
|
|
<i class="fas fa-arrow-left me-2"></i>Back
|
|
</a>
|
|
</div>
|
|
<div class="d-flex gap-2 flex-wrap">
|
|
<button type="submit" class="btn btn-outline-primary" data-intent="save-back">
|
|
<i class="fas fa-arrow-turn-left me-2"></i>Save & Back
|
|
</button>
|
|
<button type="submit" class="btn btn-primary" data-intent="save">
|
|
<i class="fas fa-save me-2"></i>Save Content
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<div class="floating-toolbar" id="floatingToolbar">
|
|
<div class="floating-toolbar-group">
|
|
<button type="button" data-command="bold"><i class="fas fa-bold"></i></button>
|
|
<button type="button" data-command="italic"><i class="fas fa-italic"></i></button>
|
|
<button type="button" data-command="underline"><i class="fas fa-underline"></i></button>
|
|
<button type="button" data-command="strikeThrough"><i class="fas fa-strikethrough"></i></button>
|
|
<button type="button" data-command="toggleCode"><i class="fas fa-code"></i></button>
|
|
<button type="button" data-command="toggleHighlight"><i class="fas fa-highlighter"></i></button>
|
|
<button type="button" data-command="subscript"><i class="fas fa-subscript"></i></button>
|
|
<button type="button" data-command="superscript"><i class="fas fa-superscript"></i></button>
|
|
<input type="color" title="Text color" data-command="foreColor" value="#0f172a" />
|
|
<input type="color" title="Background color" data-command="hiliteColor" value="#fef08a" />
|
|
<button type="button" data-command="openLink"><i class="fas fa-link"></i></button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="link-popover" id="linkPopover">
|
|
<div class="mb-2 fw-semibold">Edit link</div>
|
|
<div class="mb-2">
|
|
<label class="form-label text-white-50 small">URL</label>
|
|
<input type="text" class="form-control" id="linkUrlInput" placeholder="https://example.com" />
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label text-white-50 small">Internal page</label>
|
|
<select class="form-select" id="linkPolicySelect">
|
|
<option value="">None</option>
|
|
<% (editorConfig.policyOptions || []).forEach((option) => { %>
|
|
<option value="<%= option.value %>"><%= option.label %></option>
|
|
<% }) %>
|
|
</select>
|
|
</div>
|
|
<div class="d-flex gap-2 justify-content-end">
|
|
<button type="button" class="btn btn-outline-light btn-sm" id="removeLinkButton">Remove</button>
|
|
<button type="button" class="btn btn-primary btn-sm" id="saveLinkButton">Apply</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="slash-menu" id="slashMenu">
|
|
<div class="slash-menu-list">
|
|
<button type="button" data-slash-type="heading">Heading</button>
|
|
<button type="button" data-slash-type="list">List</button>
|
|
<button type="button" data-slash-type="divider">Divider</button>
|
|
<button type="button" data-slash-type="quote">Quote</button>
|
|
<button type="button" data-slash-type="link">Link</button>
|
|
<button type="button" disabled>Image (Coming soon)</button>
|
|
</div>
|
|
</div>
|
|
|
|
<script type="application/json" id="policyBlockEditorDataPayload"><%- JSON.stringify(data) %></script>
|
|
<script type="application/json" id="policyBlockEditorConfigData"><%- JSON.stringify(editorConfig) %></script>
|
|
<script src="/js/policies-block-editor.js"></script>
|