Skip to content

Commit dc50f76

Browse files
committed
feat: sync free-side changes for Documentation Layout and Section Tree (Pro)
- docs_mode setting and wizard pro-features gate (setting stub present in free; feature activates only with a Pro licence) - TOC anchor injection enabled when docs_mode is on - wzkb_get_section_tree() / wzkb_the_section_tree() public helper stubs (return '' when Pro class is absent) - Nav CSS updated to use flexbox for article pagination - JS repeater unique-field sync refactored to shared outer listener - Section Tree block icon added to block icon registry
1 parent 71e7487 commit dc50f76

20 files changed

Lines changed: 163 additions & 63 deletions

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ Effortlessly create a powerful, multi-product knowledge base. Boost your support
5757
- 🎨 __Premium Layout Pack__ — Unlock seven additional frontend styles (Modern, Minimal, Boxed, Gradient, Compact, Magazine, Professional).
5858
- 📑 __Enhanced Table of Contents__ — Three Pro TOC delivery surfaces: a sidebar widget that renders the TOC for the current article, a Gutenberg block for inline insertion, and a floating/sticky panel that follows the reader down the page.
5959
- 🛠️ __Advanced Admin Tools__ — Control knowledge base caching with expiry settings, on-demand cache clearing, and other productivity enhancements.
60+
- 📄 __Documentation Layout Mode__ — Transform any KB page into a three-column docs site with a sticky section-tree sidebar on the left, article content in the centre, and an "On this page" TOC rail on the right. Collapsible accordion navigation adapts to the current product, section, or article automatically.
61+
- 🌳 __Section Tree Block & Widget__ — Display a context-aware hierarchical navigation tree of your KB products, sections, and articles anywhere — as a Gutenberg block or a classic sidebar widget. The tree collapses and expands sections with an accessible accordion, and highlights the current page automatically.
6062
- 🤖 __GitHub Integration__ — Sync markdown documentation from a GitHub repo. Push changes via webhooks and articles are created or updated automatically. YAML frontmatter controls slug, title, products, and sections.
6163
- 📦 __Article Export & Import__ — Export all Knowledge Base articles as a Markdown ZIP (with YAML frontmatter), a SQL INSERT dump, or an XLSX metadata spreadsheet. Re-import Markdown ZIPs to restore or migrate articles, with automatic taxonomy mapping and overwrite/skip control.
6264

includes/admin/class-settings-wizard.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ public function get_wizard_steps() {
132132
'columns',
133133
);
134134
$pro_features_keys = array(
135+
'docs_mode',
135136
'show_floating_toc',
136137
'rating_system',
137138
'rating_tracking_method',

includes/admin/class-settings.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -825,6 +825,14 @@ public static function settings_styles() {
825825
'options' => self::get_kb_styles(),
826826
'default' => 'classic',
827827
),
828+
'docs_mode' => array(
829+
'id' => 'docs_mode',
830+
'name' => esc_html__( 'Documentation layout', 'knowledgebase' ),
831+
'desc' => esc_html__( 'Display the entire knowledge base as a three-column documentation site: a categorized navigation sidebar, the article content, and an "On this page" outline. Works alongside your chosen style.', 'knowledgebase' ),
832+
'type' => 'checkbox',
833+
'default' => false,
834+
'pro' => true,
835+
),
828836
'columns' => array(
829837
'id' => 'columns',
830838
'name' => esc_html__( 'Number of columns', 'knowledgebase' ),

includes/admin/settings/class-settings-wizard-api.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* A reusable API class for creating multi-step settings wizards.
66
* This class provides the framework for creating guided setup experiences.
77
*
8-
* @package WebberZone\Knowledge_Base
8+
* @package WebberZone\Knowledge_Base
99
*/
1010

1111
namespace WebberZone\Knowledge_Base\Admin\Settings;

includes/admin/settings/css/wizard-rtl.min.css

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

includes/admin/settings/js/settings-admin-scripts.js

Lines changed: 40 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,33 @@ jQuery(document).ready(function ($) {
5757
formmodified = 0;
5858
});
5959

60+
// Shared helper: enforce unique selection within a repeater wrapper.
61+
function syncUniqueSelectsForWrapper(wrapper) {
62+
var uniqueField = wrapper.data('unique-field') || '';
63+
if (!uniqueField) {
64+
return;
65+
}
66+
var $selects = wrapper.find('.wz-repeater-item select[name$="[fields][' + uniqueField + ']"]');
67+
var usedValues = {};
68+
$selects.each(function () {
69+
var val = $(this).val();
70+
if (val) {
71+
usedValues[val] = true;
72+
}
73+
});
74+
$selects.each(function () {
75+
var $sel = $(this);
76+
var ownVal = $sel.val();
77+
$sel.find('option').each(function () {
78+
var optVal = $(this).val();
79+
if (!optVal) {
80+
return;
81+
}
82+
$(this).prop('disabled', optVal !== ownVal && usedValues[optVal]);
83+
});
84+
});
85+
}
86+
6087
// Initialize Repeater Fields.
6188
$('.wz-repeater-wrapper').each(function () {
6289
var wrapper = $(this);
@@ -141,44 +168,15 @@ jQuery(document).ready(function ($) {
141168

142169
// Enforce unique selection across rows for the field named in data-unique-field.
143170
var uniqueField = wrapper.data('unique-field') || '';
144-
function syncUniqueSelects() {
145-
if (!uniqueField) {
146-
return;
147-
}
148-
var $selects = itemsContainer.find('.wz-repeater-item select[name$="[fields][' + uniqueField + ']"]');
149-
var usedValues = {};
150-
$selects.each(function () {
151-
var val = $(this).val();
152-
if (val) {
153-
usedValues[val] = true;
154-
}
155-
});
156-
$selects.each(function () {
157-
var $sel = $(this);
158-
var ownVal = $sel.val();
159-
$sel.find('option').each(function () {
160-
var optVal = $(this).val();
161-
if (!optVal) {
162-
return;
163-
}
164-
$(this).prop('disabled', optVal !== ownVal && usedValues[optVal]);
165-
});
166-
});
167-
}
168171

169172
if (uniqueField) {
170-
syncUniqueSelects();
173+
syncUniqueSelectsForWrapper(wrapper);
171174
wrapper.on('change', '.wz-repeater-item select[name$="[fields][' + uniqueField + ']"]', function () {
172-
syncUniqueSelects();
175+
syncUniqueSelectsForWrapper(wrapper);
173176
});
174177
wrapper.on('click', '.remove-item', function () {
175178
// Sync after DOM removal; removal handler fires before remove, so defer.
176-
setTimeout(syncUniqueSelects, 0);
177-
});
178-
document.addEventListener('wz:repeater-item-added', function (e) {
179-
if (wrapper.get(0).contains(e.detail.container)) {
180-
syncUniqueSelects();
181-
}
179+
setTimeout(function () { syncUniqueSelectsForWrapper(wrapper); }, 0);
182180
});
183181
}
184182

@@ -202,4 +200,14 @@ jQuery(document).ready(function ($) {
202200
});
203201
});
204202

203+
// Single self-scoping listener: re-enforce unique selection for the wrapper
204+
// that owns the newly added item, regardless of how many repeaters exist.
205+
document.addEventListener('wz:repeater-item-added', function (e) {
206+
var wrapper = $(e.detail.container).closest('.wz-repeater-wrapper');
207+
if (!wrapper.length) {
208+
return;
209+
}
210+
syncUniqueSelectsForWrapper(wrapper);
211+
});
212+
205213
});

includes/admin/settings/js/settings-admin-scripts.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

includes/blocks/src/components/icons.js

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,15 @@ export const tocIcon = (props) => (
1212
fill="#FFBD59"
1313
rx={332.8}
1414
/>
15-
{ /* Bullet dots */}
15+
{/* Bullet dots */}
1616
<circle cx="80" cy="120" r="32" fill="#792a18" />
1717
<circle cx="80" cy="256" r="32" fill="#792a18" />
1818
<circle cx="80" cy="392" r="32" fill="#792a18" />
19-
{ /* Lines */}
19+
{/* Lines */}
2020
<rect x="152" y="100" width="240" height="40" rx="20" fill="#fff" />
2121
<rect x="152" y="236" width="200" height="40" rx="20" fill="#fff" />
2222
<rect x="152" y="372" width="220" height="40" rx="20" fill="#fff" />
23-
{ /* Indented sub-items */}
23+
{/* Indented sub-items */}
2424
<rect x="192" y="168" width="160" height="32" rx="16" fill="#f0ebea" />
2525
<rect x="192" y="304" width="140" height="32" rx="16" fill="#f0ebea" />
2626
</svg>
@@ -111,3 +111,35 @@ export const bookIcon = (props) => (
111111
<path d="M212.426 285.957h-128a8.169 8.169 0 0 0-8.17 8.17 8.169 8.169 0 0 0 8.17 8.17h128a8.169 8.169 0 0 0 8.17-8.17 8.169 8.169 0 0 0-8.17-8.17zM212.426 239.66h-128a8.169 8.169 0 0 0-8.17 8.17 8.169 8.169 0 0 0 8.17 8.17h128a8.169 8.169 0 0 0 8.17-8.17 8.169 8.169 0 0 0-8.17-8.17zM212.426 193.362h-128c-4.513 0-8.17 3.657-8.17 8.17s3.657 8.17 8.17 8.17h128c4.513 0 8.17-3.657 8.17-8.17s-3.657-8.17-8.17-8.17zM212.426 147.064h-128c-4.513 0-8.17 3.657-8.17 8.17s3.657 8.17 8.17 8.17h128c4.513 0 8.17-3.657 8.17-8.17s-3.657-8.17-8.17-8.17zM212.426 100.766h-128c-4.513 0-8.17 3.657-8.17 8.17s3.657 8.17 8.17 8.17h128c4.513 0 8.17-3.657 8.17-8.17s-3.657-8.17-8.17-8.17zM435.745 294.128a8.169 8.169 0 0 0-8.17-8.17h-128a8.169 8.169 0 0 0-8.17 8.17 8.169 8.169 0 0 0 8.17 8.17h128a8.169 8.169 0 0 0 8.17-8.17zM299.574 256h128a8.169 8.169 0 0 0 8.17-8.17v-53.106a8.169 8.169 0 0 0-8.17-8.17 8.169 8.169 0 0 0-8.17 8.17v44.936h-111.66V117.106h111.66v44.936a8.169 8.169 0 0 0 8.17 8.17 8.169 8.169 0 0 0 8.17-8.17v-53.106a8.169 8.169 0 0 0-8.17-8.17h-128a8.169 8.169 0 0 0-8.17 8.17V247.83a8.169 8.169 0 0 0 8.17 8.17z" />
112112
</svg>
113113
);
114+
115+
export const sectionTreeIcon = (props) => (
116+
<svg
117+
xmlns="http://www.w3.org/2000/svg"
118+
viewBox="-76.8 -76.8 665.6 665.6"
119+
{...props}
120+
>
121+
<rect
122+
width={665.6}
123+
height={665.6}
124+
x={-76.8}
125+
y={-76.8}
126+
fill="#FFBD59"
127+
rx={332.8}
128+
/>
129+
{/* Root */}
130+
<rect x="40" y="32" width="160" height="32" rx="12" fill="#792a18" />
131+
{/* Branch lines */}
132+
<rect x="120" y="64" width="8" height="64" rx="4" fill="#ab5d4a" />
133+
<rect x="120" y="120" width="40" height="8" rx="4" fill="#ab5d4a" />
134+
<rect x="120" y="184" width="40" height="8" rx="4" fill="#ab5d4a" />
135+
{/* Children */}
136+
<rect x="160" y="96" width="140" height="28" rx="10" fill="#fff" />
137+
<rect x="160" y="160" width="140" height="28" rx="10" fill="#fff" />
138+
{/* Grandchildren under second child */}
139+
<rect x="240" y="188" width="8" height="48" rx="4" fill="#ab5d4a" />
140+
<rect x="240" y="224" width="32" height="8" rx="4" fill="#ab5d4a" />
141+
<rect x="240" y="288" width="32" height="8" rx="4" fill="#ab5d4a" />
142+
<rect x="272" y="200" width="100" height="28" rx="10" fill="#f0ebea" />
143+
<rect x="272" y="264" width="100" height="28" rx="10" fill="#f0ebea" />
144+
</svg>
145+
);

includes/frontend/class-toc.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,15 +85,16 @@ public function inject_anchors( string $content ): string {
8585
/**
8686
* Whether any non-inline TOC consumer is active on the current page.
8787
*
88-
* Checks for: floating TOC option, the TOC sidebar widget, and the TOC block embedded in
89-
* the post content. The inline TOC is excluded because inject_toc() handles anchors itself.
88+
* Checks for: documentation layout, floating TOC option, the TOC sidebar widget, and the TOC
89+
* block embedded in the post content. The inline TOC is excluded because inject_toc() handles
90+
* anchors itself.
9091
*
9192
* @since 3.0.1
9293
*
9394
* @return bool
9495
*/
9596
private static function needs_anchor_injection(): bool {
96-
if ( \wzkb_get_option( 'show_floating_toc' ) ) {
97+
if ( \wzkb_get_option( 'docs_mode' ) || \wzkb_get_option( 'show_floating_toc' ) ) {
9798
return true;
9899
}
99100

includes/frontend/css/styles/classic-rtl.css

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -467,12 +467,15 @@ body.wzkb-sidebar-enabled #wzkb-sidebar-primary {
467467
}
468468

469469
/* Navigation */
470-
.nav-previous {
471-
float: right;
470+
body.single-wz_knowledgebase .nav-links {
471+
display: flex;
472+
justify-content: space-between;
473+
gap: 1rem;
472474
}
473475

474-
.nav-next {
475-
float: left;
476+
body.single-wz_knowledgebase .nav-next {
477+
text-align: left;
478+
margin-right: auto;
476479
}
477480

478481
/* Search form */

0 commit comments

Comments
 (0)