From 66ffd4f64975713b9cdbec4c843399878eb1bc5a Mon Sep 17 00:00:00 2001 From: Dobrunia Kostrigin <48620984+Dobrunia@users.noreply.github.com> Date: Mon, 30 Mar 2026 19:19:08 +0300 Subject: [PATCH 1/5] PR title: Fix deep page nesting in sidebar, prev/next order, and parent dropdown indent --- package.json | 2 +- src/backend/build-static.ts | 3 +- src/backend/controllers/pages.ts | 36 +++++++++++++++ src/backend/models/pagesFlatArray.ts | 8 ++-- src/backend/routes/index.ts | 2 +- src/backend/routes/middlewares/pages.ts | 2 +- src/backend/routes/pages.ts | 10 ++--- src/backend/utils/menu.ts | 45 +++++++++++-------- .../views/components/sidebar-branch.twig | 18 ++++++++ src/backend/views/components/sidebar.twig | 14 +----- src/backend/views/pages/form.twig | 9 ++-- src/frontend/js/modules/sidebar.js | 10 ++++- src/frontend/styles/components/sidebar.pcss | 5 +++ 13 files changed, 111 insertions(+), 53 deletions(-) create mode 100644 src/backend/views/components/sidebar-branch.twig diff --git a/package.json b/package.json index f0395845..fa3a380f 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "codex.docs", "license": "Apache-2.0", - "version": "2.2.4", + "version": "2.3.0", "type": "module", "bin": { "codex.docs": "dist/backend/app.js" diff --git a/src/backend/build-static.ts b/src/backend/build-static.ts index ca0894aa..019c6397 100644 --- a/src/backend/build-static.ts +++ b/src/backend/build-static.ts @@ -105,8 +105,7 @@ export default async function buildStatic(): Promise { const parentIdOfRootPages = '0' as EntityId; const previousPage = await PagesFlatArray.getPageBefore(pageId); const nextPage = await PagesFlatArray.getPageAfter(pageId); - const menu = createMenuTree(parentIdOfRootPages, allPages, pagesOrder, 2); - + const menu = createMenuTree(parentIdOfRootPages, allPages, pagesOrder); const result = await renderTemplate('./views/pages/page.twig', { page, pageParent, diff --git a/src/backend/controllers/pages.ts b/src/backend/controllers/pages.ts index 89f133ba..855c5f64 100644 --- a/src/backend/controllers/pages.ts +++ b/src/backend/controllers/pages.ts @@ -86,6 +86,42 @@ class Pages { return pagesMap; } + /** + * Depth in parent chain: 0 for root pages, +1 per ancestor below root (for select indent). + */ + private static computePageDepth(page: Page, pagesMap: Map): number { + let depth = 0; + let cur: Page | undefined = page; + + while (cur?._parent && !isEqualIds(cur._parent, '0' as EntityId)) { + depth++; + cur = pagesMap.get(cur._parent.toString()); + } + + return depth; + } + + /** + * Ordered pages for the parent ` - {% for _page in pagesAvailableGrouped %} + {% for entry in parentSelectOptions %} + {% set _page = entry.page %} {% if toString(_page._id) != toString(currentPageId) %} {% endif %} {% endfor %} diff --git a/src/frontend/js/modules/sidebar.js b/src/frontend/js/modules/sidebar.js index e6dee56e..482bf3b6 100644 --- a/src/frontend/js/modules/sidebar.js +++ b/src/frontend/js/modules/sidebar.js @@ -128,9 +128,15 @@ export default class Sidebar { return; } - const itemsCount = sectionList.children.length; + /** + * Section has overflow:hidden; max-height must fit nested lists or deeper levels get clipped. + */ + requestAnimationFrame(() => { + const natural = sectionList.scrollHeight; + const fallback = sectionList.querySelectorAll('li').length * ITEM_HEIGHT; - sectionList.style.maxHeight = `${itemsCount * ITEM_HEIGHT}px`; + sectionList.style.maxHeight = `${Math.max(natural, fallback)}px`; + }); } /** diff --git a/src/frontend/styles/components/sidebar.pcss b/src/frontend/styles/components/sidebar.pcss index 04f6f83a..75aaf013 100644 --- a/src/frontend/styles/components/sidebar.pcss +++ b/src/frontend/styles/components/sidebar.pcss @@ -249,6 +249,11 @@ margin: 0; z-index: 1; position: relative; + + &--nested { + padding-left: 12px; + margin-top: 2px; + } } &__section-toggler { From 56d8a79b3b832fc20a3a461105d74909ac410c7e Mon Sep 17 00:00:00 2001 From: Dobrunia Kostrigin <48620984+Dobrunia@users.noreply.github.com> Date: Mon, 30 Mar 2026 20:29:11 +0300 Subject: [PATCH 2/5] Refactor sidebar component structure --- .../views/components/sidebar-branch.twig | 18 --- .../views/components/sidebar-section.twig | 26 ++++ src/backend/views/components/sidebar.twig | 20 +-- src/frontend/js/classes/sidebar-filter.js | 117 ++++++++++-------- src/frontend/js/modules/sidebar.js | 85 +++++++++---- src/frontend/styles/components/sidebar.pcss | 79 ++++++------ 6 files changed, 191 insertions(+), 154 deletions(-) delete mode 100644 src/backend/views/components/sidebar-branch.twig create mode 100644 src/backend/views/components/sidebar-section.twig diff --git a/src/backend/views/components/sidebar-branch.twig b/src/backend/views/components/sidebar-branch.twig deleted file mode 100644 index 79d97000..00000000 --- a/src/backend/views/components/sidebar-branch.twig +++ /dev/null @@ -1,18 +0,0 @@ -{% if nodes is not empty %} - -{% endif %} diff --git a/src/backend/views/components/sidebar-section.twig b/src/backend/views/components/sidebar-section.twig new file mode 100644 index 00000000..cb60214a --- /dev/null +++ b/src/backend/views/components/sidebar-section.twig @@ -0,0 +1,26 @@ +{% set is_leaf = node.children is not defined or node.children is empty %} +
+ +
+ + {{ node.title | striptags }} + + {% if node.children is defined and node.children is not empty %} + + {% endif %} +
+
+ {% if node.children is defined and node.children is not empty %} +
    + {% for child in node.children %} +
  • + {% include 'components/sidebar-section.twig' with { node: child, nested: true } %} +
  • + {% endfor %} +
+ {% endif %} +
diff --git a/src/backend/views/components/sidebar.twig b/src/backend/views/components/sidebar.twig index c5b26d29..21aa2178 100644 --- a/src/backend/views/components/sidebar.twig +++ b/src/backend/views/components/sidebar.twig @@ -10,25 +10,7 @@ {% for firstLevelPage in menu %} -
- -
- - {{ firstLevelPage.title | striptags }} - - {% if firstLevelPage.children is not empty %} - - {% endif %} -
-
- {% if firstLevelPage.children is not empty %} - {% include 'components/sidebar-branch.twig' with { nodes: firstLevelPage.children, ulClass: '' } %} - {% endif %} -
+ {% include 'components/sidebar-section.twig' with { node: firstLevelPage, nested: false } %} {% endfor %}