luci-base: add ability to hide section titles
authorDavid Härdeman <[email protected]>
Wed, 22 Oct 2025 21:22:14 +0000 (23:22 +0200)
committerPaul Donald <[email protected]>
Thu, 23 Oct 2025 15:05:03 +0000 (17:05 +0200)
The rationale here is that tabbed CBIMaps were introduced in commit
082fd9ff10b.

With tabbed maps, code could typically look like this:

m = new form.Map('foobar', _('FooBar'));
m.tabbed = true;
s = m.section(form.TypedSection, 'foo', _('foo Settings'));

The problem is that the title of "s" will be used as the name of the tab
rendered in "m", but also rendered as an <h3> right below the tab. IOW,
the same information will be presented twice, which looks weird.

Doing this instead...

m = new form.Map('foobar', _('FooBar'));
m.tabbed = true;
s = m.section(form.TypedSection, 'foo');

...means that the superfluous <h3> won't be rendered (since "s" has no
title), but the tab will then simply have the name of the section
("foo"), which can't be translated (bad).

After this change, the tabbed map can be written like this:

m = new form.Map('foobar', _('FooBar'));
m.tabbed = true;
s = m.section(form.TypedSection, 'foo', _('foo Settings'));
s.hidetitle = true;

Which will give the Map tab the name "foo Settings", but won't add a
title for the TypedSection right under the tab.

Signed-off-by: David Härdeman <[email protected]>
modules/luci-base/htdocs/luci-static/resources/form.js

index 0ae54e7c83013fc493f218b54249ca04f72cf0f3..c7308e2fbabc483e9ad4b9654dfa1bdba6dcfc04 100644 (file)
@@ -2155,6 +2155,17 @@ const CBITypedSection = CBIAbstractSection.extend(/** @lends LuCI.form.TypedSect
         * @default false
         */
 
+       /**
+        * If set to true, the title caption of the form section element which
+        * is normally rendered before the start of the section content will
+        * not be rendered in the UI. The default is false, meaning that the
+        * title is rendered.
+        *
+        * @name LuCI.form.TypedSection.prototype#hidetitle
+        * @type boolean
+        * @default false
+        */
+
        /**
         * If set to `true`, mapped section instances are treated as anonymous
         * UCI sections, which means that section instance elements will be
@@ -2300,7 +2311,7 @@ const CBITypedSection = CBIAbstractSection.extend(/** @lends LuCI.form.TypedSect
                        'data-tab-title': (this.map.tabbed && !this.parentoption) ? this.title || this.sectiontype : null
                });
 
-               if (this.title != null && this.title != '')
+               if (this.title != null && this.title != '' && !this.hidetitle)
                        sectionEl.appendChild(E('h3', {}, this.title));
 
                if (this.description != null && this.description != '')
@@ -2530,7 +2541,7 @@ const CBITableSection = CBITypedSection.extend(/** @lends LuCI.form.TableSection
                        'class': 'table cbi-section-table'
                });
 
-               if (this.title != null && this.title != '')
+               if (this.title != null && this.title != '' && !this.hidetitle)
                        sectionEl.appendChild(E('h3', {}, this.title));
 
                if (this.description != null && this.description != '')
@@ -3513,6 +3524,17 @@ const CBINamedSection = CBIAbstractSection.extend(/** @lends LuCI.form.NamedSect
         * @default false
         */
 
+       /**
+        * If set to true, the title caption of the form section element which
+        * is normally rendered before the start of the section content will
+        * not be rendered in the UI. The default is false, meaning that the
+        * title is rendered.
+        *
+        * @name LuCI.form.NamedSection.prototype#hidetitle
+        * @type boolean
+        * @default false
+        */
+
        /**
         * Override the UCI configuration name to read the section IDs from. By
         * default, the configuration name is inherited from the parent `Map`.
@@ -3568,7 +3590,7 @@ const CBINamedSection = CBIAbstractSection.extend(/** @lends LuCI.form.NamedSect
                        'data-tab-title': (this.map.tabbed && !this.parentoption) ? this.title || this.sectiontype : null
                });
 
-               if (typeof(this.title) === 'string' && this.title !== '')
+               if (typeof(this.title) === 'string' && this.title !== '' && !this.hidetitle)
                        sectionEl.appendChild(E('h3', {}, this.title));
 
                if (typeof(this.description) === 'string' && this.description !== '')