ソースを参照

One sidebar title for conductor and wiki pages; Persons gets its section icon (#5896)

NEO: the conductor's left-column title (icon, name, Add menu) moves into
ui/molecules/sidebar-title.tsx and reads new conductor.sidebar_title_* settings
instead of hardcoded classes. The wiki (Docs) left column now uses the same
chrome as the conductor: sidebar_inner_container, SidebarTitle and the
conductor.sidebar_nav inset, so a fork restyles both from settings. Its
hardcoded border-r is gone; the column line comes from the layout's
panel-line, as in conductor layouts.

UNA: getPageAPI sends the module name, title and icon in the page menu for
profile modules too (Persons), so NEO shows the section icon on persons-home.
The Add URL stays limited to content and context modules.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Andrey Yasko 5 日 前
親
コミット
42f978a189

+ 4 - 2
neo/packages/app/components/elements/wiki/nav.js

@@ -138,7 +138,9 @@ export function ActiveBranchExpander({ currentPathComparable, items, setExpanded
     return null;
 }
 
-export function MenuWiki({ onNavigate, block, url }) {
+// `className`: the list's classes. The wiki sidebar passes `conductor.sidebar_nav`, so its
+// items line up with the sidebar title as in conductor sidebars.
+export function MenuWiki({ onNavigate, block, url, className = 'gap-0.5 lg:-mx-2' }) {
     const data = block.content[0].data;
     const routePathComparable = useCurrentPathComparable();
     // Prefer pageData.url so sidebar pushState (web) and in-layout swaps (native)
@@ -210,7 +212,7 @@ export function MenuWiki({ onNavigate, block, url }) {
                 items={topLevelItems}
                 setExpandedMap={setExpandedMap}
             />
-            <View className='gap-0.5 lg:-mx-2'>
+            <View className={className}>
                 {renderItems(topLevelItems)}
             </View>
         </>

+ 11 - 6
neo/packages/app/components/page-layout/wiki.js

@@ -12,6 +12,7 @@ import {
 } from 'app/ui/molecules/page/resizable-panels'
 import { useBreakpoint, useBreakpointName, useIsDesktop } from 'app/context/measure'
 import { BlockWrapper } from 'app/components/block-wrapper'
+import { SidebarTitle } from 'app/ui/molecules/sidebar-title'
 import { BlockByData } from 'app/components/block'
 import DropdownPopup from 'app/ui/atoms/dropdown-popup'
 import { useHeaderHeight, DEFAULT_HEADER_HEIGHT } from 'app/context/jotai/layout'
@@ -366,21 +367,25 @@ function PageContentWiki({ data, scrollRef, url }) {
                             matching negative margin cancels the in-flow header spacer so content
                             lines up with the center column at rest. Panel group is the sticky
                             bound — sidebars release above the footer with no footer measurement. */}
-                        <View className="h-full border-r border-border/60">
+                        {/* Same chrome as the conductor's left column (sidebar_inner_container,
+                            SidebarTitle, sidebar_nav); the column line is the layout's panel-line. */}
+                        <View className="h-full">
                             <View
                                 className={`web:sticky web:top-0 web:max-h-screen web:overflow-y-auto gap-3 ${appSetting('conductor', 'sidebar_container')}`}
                                 style={isWeb ? stickySidebarScrollStyle : undefined}
                             >
-                                <BlockWrapper
-                                    block={leftMenu}
-                                    config={leftMenu?.config_api}
-                                >
+                                <View className={appSetting('conductor', 'sidebar_inner_container')}>
+                                    <SidebarTitle
+                                        title={stripTags(leftMenu?.title)}
+                                        icon={leftMenu?.icon}
+                                    />
                                     <MenuWiki
                                         onNavigate={navigateToWikiPath}
                                         block={leftMenu}
                                         url={pageData.url}
+                                        className={appSetting('conductor', 'sidebar_nav')}
                                     />
-                                </BlockWrapper>
+                                </View>
                             </View>
                         </View>
                     </Panel>

+ 9 - 0
neo/packages/app/settings/elements.js

@@ -73,6 +73,15 @@ export const settingsElements = {
         sidebar_container: ' ns--sidebar-container-- h-full ne--  ',
         sidebar_inner_container: ' xl:mx-4 p-3 gap-3 overflow-y-auto flex flex-col bg-card/80 shadow-card-outline dark:shadow-card-outline-deep rounded-2xl mt-4',
         sidebar_title: 'sticky justify-between items-center z-10 gap-2 h-11 px-1 ',
+        // Sidebar title (section icon + name) of conductor and wiki pages (ui/molecules/sidebar-title).
+        sidebar_title_name: 'items-center mr-auto min-w-0 hidden lg:flex gap-2',
+        sidebar_title_icon: 'h-8 w-8 items-center justify-center flex',
+        sidebar_title_icon_class: 'text-card-foreground shrink-0',
+        sidebar_title_icon_size: 29,
+        sidebar_title_text: 'text-xl tracking-tight truncate font-bold leading-9 text-card-foreground',
+        // Section nav under the title. The negative margin and the items' 8px inset put the
+        // icons 4px inside sidebar_inner_container's padding, in line with the title icon.
+        sidebar_nav: 'gap-0.5 -mx-1 -mb-1',
         sidebar_position: ' z-50  ',
         bgrDecorator: true, // Enable/disable decorator background globally for conductor buttons
         add_menu_native:false,

+ 4 - 18
neo/packages/app/ui/molecules/sections/conductor/web-ui.js

@@ -2,6 +2,7 @@ import { useCallback, useState, useEffect, useLayoutEffect, useRef, useMemo, mem
 import { Text } from 'app/design/typography'
 import { View, Row, Pressable } from 'app/design/view'
 import { Icon } from 'app/ui/atoms/icon'
+import { SidebarTitle } from 'app/ui/molecules/sidebar-title'
 import {
     appSetting,
     decodeTextNode,
@@ -958,27 +959,12 @@ export const LeftColumnContainer = ({
                 style={stickyColumnStyle}
             >
                 <View className={layoutName == 'profile' ? '' : appSetting('conductor', 'sidebar_inner_container')}>
-                     {((!!title || addMenuItems.length > 0) && layoutName != 'profile') && (
-                        <Row className={appSetting('conductor', 'sidebar_title')}>
-                            <Row className="items-center mr-auto min-w-0 hidden lg:flex gap-2">
-                                {!!icon && (
-                                    <View className="h-8 w-8 items-center justify-center flex">
-                                    <Icon
-                                        icon={icon}
-                                        size={29}
-                                        className="text-card-foreground shrink-0"
-                                    /></View>
-                                )}
-                                <Text className="text-xl  tracking-tight truncate font-bold leading-9 text-card-foreground">
-                                    {t(title)}
-                                </Text>
-                            </Row>
-                            <Row>{addButtons}</Row>
-                        </Row>
+                    {((!!title || addMenuItems.length > 0) && layoutName != 'profile') && (
+                        <SidebarTitle title={title} icon={icon} actions={addButtons} />
                     )}
                     <View className="flex-1 gap-4">
                         {layoutName == 'navigator' && routes.length > 1 && (
-                            <View className=' gap-0.5 -mx-1 -mb-1'>
+                            <View className={appSetting('conductor', 'sidebar_nav')}>
                                 {hasConductorSubitems(routes) ? (
                                     <SidebarNav
                                         routes={routes}

+ 42 - 0
neo/packages/app/ui/molecules/sidebar-title.tsx

@@ -0,0 +1,42 @@
+import type { ReactNode } from 'react'
+import { Text } from 'app/design/typography'
+import { Row, View } from 'app/design/view'
+import { Icon } from 'app/ui/atoms/icon'
+import { appSetting } from 'app/lib/util'
+
+type SidebarTitleProps = {
+    title?: string
+    icon?: string
+    /** Buttons at the end of the row, e.g. the conductor's "Add" menu. */
+    actions?: ReactNode
+}
+
+/**
+ * Title row of a left sidebar: section icon, name and optional actions.
+ * Conductor and wiki pages share it, so `conductor.sidebar_title*` settings style both.
+ */
+export function SidebarTitle({ title, icon, actions }: SidebarTitleProps) {
+    if (!title && !actions) return null
+
+    return (
+        <Row className={appSetting('conductor', 'sidebar_title')}>
+            <Row className={appSetting('conductor', 'sidebar_title_name')}>
+                {!!icon && (
+                    <View className={appSetting('conductor', 'sidebar_title_icon')}>
+                        <Icon
+                            icon={icon}
+                            size={appSetting('conductor', 'sidebar_title_icon_size')}
+                            className={appSetting('conductor', 'sidebar_title_icon_class')}
+                        />
+                    </View>
+                )}
+                {!!title && (
+                    <Text className={appSetting('conductor', 'sidebar_title_text')}>
+                        {title}
+                    </Text>
+                )}
+            </Row>
+            {actions ? <Row>{actions}</Row> : null}
+        </Row>
+    )
+}

+ 7 - 3
template/scripts/BxBasePage.php

@@ -591,19 +591,23 @@ class BxBasePage extends BxDolPage
                 $oMenuSubmenu->setObjectSubmenu($sSubmenu, []);
 
             $a['menu'] = $oMenuSubmenu->getCodeAPI();
-            if($sModule && $sModule != 'system' && (bx_srv('system', 'is_module_content', [$sModule]) || bx_srv('system', 'is_module_context', [$sModule])) && ($oModule = BxDolModule::getInstance($sModule))) {
+            $bModuleEntries = $sModule && $sModule != 'system' && (bx_srv('system', 'is_module_content', [$sModule]) || bx_srv('system', 'is_module_context', [$sModule]));
+            $bModuleProfiles = !$bModuleEntries && $sModule && $sModule != 'system' && bx_srv('system', 'is_module_profile', [$sModule]);
+            if(($bModuleEntries || $bModuleProfiles) && ($oModule = BxDolModule::getInstance($sModule))) {
                 $CNF = &$oModule->_oConfig->CNF;
 
+                // Section name and icon for NEO's sidebar title; profile modules (Persons) get them too.
                 $aMenuAdd = [
                     'name' => $oModule->getName(),
                     'title' => BxDolModule::getTitle($oModule->_oConfig->getUri()),
                     'icon' => !empty($CNF['ICON']) ? $CNF['ICON'] : '',
                 ];
 
+                // "Add" button: content and context entries only, a profile isn't added from its list.
                 $sUrlAdd = '';
-                if(($sKey = 'URI_ADD_ENTRY') && !empty($CNF[$sKey]))
+                if($bModuleEntries && ($sKey = 'URI_ADD_ENTRY') && !empty($CNF[$sKey]))
                     $sUrlAdd = $CNF[$sKey];
-                if(!$sUrlAdd && ($sKey = 'URI_EDIT_ENTRY') && !empty($CNF[$sKey]))
+                if($bModuleEntries && !$sUrlAdd && ($sKey = 'URI_EDIT_ENTRY') && !empty($CNF[$sKey]))
                     $sUrlAdd = str_replace('edit-', 'create-', $CNF[$sKey]);
                 if($sUrlAdd)
                     $aMenuAdd['add_url'] = $sUrlAdd;