You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by hn...@apache.org on 2025/04/25 08:10:31 UTC

(myfaces-tobago) 01/02: fix(sheet): column width FR-values

This is an automated email from the ASF dual-hosted git repository.

hnoeth pushed a commit to branch tobago-5.x
in repository https://gitbox.apache.org/repos/asf/myfaces-tobago.git

commit 002a794bd9c7645b9e0a6c74f0f7d3ebefc1e175
Author: Henning Noeth <hn...@apache.org>
AuthorDate: Thu Apr 24 13:50:27 2025 +0200

    fix(sheet): column width FR-values
    
    * add tests
    * fix column width in TS
    
    Issue: TOBAGO-2407
---
 .../Sheet_Column_Width.test.js                     | 24 +++++++
 .../20-sheet-column-width/Sheet_Column_Width.xhtml | 40 ++++++++++++
 .../src/main/ts/tobago-sheet.ts                    | 73 ++++++++++++++++++++++
 3 files changed, 137 insertions(+)

diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/3000-sheet/20-sheet-column-width/Sheet_Column_Width.test.js b/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/3000-sheet/20-sheet-column-width/Sheet_Column_Width.test.js
index 756a13ab74..ab34c2a546 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/3000-sheet/20-sheet-column-width/Sheet_Column_Width.test.js
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/3000-sheet/20-sheet-column-width/Sheet_Column_Width.test.js
@@ -93,6 +93,30 @@ it("Only auto value set for a 900px-sheet", function (done) {
   test.start();
 });
 
+it("'100px 20% 4fr 1fr auto' for a 1000px-sheet", function (done) {
+  const columnHeads = querySelectorAllFn("#page\\:mainForm\\:fixFr100px20p4fr1frAuto header table thead tr th");
+
+  const test = new JasmineTestTool(done);
+  test.do(() => expect(getComputedStyle(columnHeads()[0]).width).toBe("100px"));
+  test.do(() => expect(getComputedStyle(columnHeads()[1]).width).toBe("200px"));
+  test.do(() => expect(getComputedStyle(columnHeads()[2]).width).toBe("400px"));
+  test.do(() => expect(getComputedStyle(columnHeads()[3]).width).toBe("100px"));
+  test.do(() => expect(getComputedStyle(columnHeads()[4]).width).toBe("200px"));
+  test.start();
+});
+
+it("'150px 20% 4fr 1fr' for a 1000px-sheet", function (done) {
+  const columnHeads = querySelectorAllFn("#page\\:mainForm\\:fixFr150px20p4fr1fr header table thead tr th");
+
+  const test = new JasmineTestTool(done);
+  test.do(() => expect(getComputedStyle(columnHeads()[0]).width).toBe("150px"));
+  test.do(() => expect(getComputedStyle(columnHeads()[1]).width).toBe("200px"));
+  test.do(() => expect(getComputedStyle(columnHeads()[2]).width).toBe("400px"));
+  test.do(() => expect(getComputedStyle(columnHeads()[3]).width).toBe("100px"));
+  test.do(() => expect(getComputedStyle(columnHeads()[4]).width).toBe("150px"));
+  test.start();
+});
+
 it("There must be no horizontal scrollbar", function (done) {
   const sheetBody = querySelectorFn("#page\\:mainForm\\:testSheetColumnWidth .tobago-body");
 
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/3000-sheet/20-sheet-column-width/Sheet_Column_Width.xhtml b/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/3000-sheet/20-sheet-column-width/Sheet_Column_Width.xhtml
index 3a06b118a5..d63c8873cc 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/3000-sheet/20-sheet-column-width/Sheet_Column_Width.xhtml
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/3000-sheet/20-sheet-column-width/Sheet_Column_Width.xhtml
@@ -80,6 +80,46 @@
     </tc:column>
   </tc:sheet>
 
+  <tc:sheet id="fixFr100px20p4fr1frAuto" value="#{columnWidthController.solarList}" var="object" rows="2"
+            columns="100px 20% 4fr 1fr auto">
+    <tc:style width="1000px"/>
+    <tc:column label="Name">
+      <tc:out value="#{object.name}"/>
+    </tc:column>
+    <tc:column label="Orbit">
+      <tc:out value="#{object.orbit}"/>
+    </tc:column>
+    <tc:column label="Period (Days)">
+      <tc:out value="#{object.period}"/>
+    </tc:column>
+    <tc:column id="discoverer" label="Discoverer">
+      <tc:out value="#{object.discoverer}"/>
+    </tc:column>
+    <tc:column id="year" label="Year">
+      <tc:out value="Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt."/>
+    </tc:column>
+  </tc:sheet>
+
+  <tc:sheet id="fixFr150px20p4fr1fr" value="#{columnWidthController.solarList}" var="object" rows="2"
+            columns="150px 20% 4fr 1fr">
+    <tc:style width="1000px"/>
+    <tc:column label="Name">
+      <tc:out value="#{object.name}"/>
+    </tc:column>
+    <tc:column label="Orbit">
+      <tc:out value="#{object.orbit}"/>
+    </tc:column>
+    <tc:column label="Period (Days)">
+      <tc:out value="#{object.period}"/>
+    </tc:column>
+    <tc:column id="discoverer" label="Discoverer">
+      <tc:out value="#{object.discoverer}"/>
+    </tc:column>
+    <tc:column id="year" label="Year">
+      <tc:out value="Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt."/>
+    </tc:column>
+  </tc:sheet>
+
   <tc:sheet id="testSheetColumnWidth" value="#{columnWidthController.solarList}" var="luminary"
             columns="200px 1fr 1fr" rows="5" markup="bordered">
     <tc:style customClass="vertical-border-big"/>
diff --git a/tobago-theme/tobago-theme-standard/src/main/ts/tobago-sheet.ts b/tobago-theme/tobago-theme-standard/src/main/ts/tobago-sheet.ts
index 64e06b33a7..700d88972f 100644
--- a/tobago-theme/tobago-theme-standard/src/main/ts/tobago-sheet.ts
+++ b/tobago-theme/tobago-theme-standard/src/main/ts/tobago-sheet.ts
@@ -92,6 +92,8 @@ export class Sheet extends HTMLElement {
     }
 
     this.initScrollbarFiller();
+    const resizeObserver = new ResizeObserver((entries) => this.fixColumnWidth());
+    resizeObserver.observe(this);
 
     // resize column: mouse events -------------------------------------------------------------------------------- //
 
@@ -402,6 +404,46 @@ Type: ${data.type}`);
     }
   }
 
+  /**
+   * Fix column width for columns with fr-value if the layout contains both fr-values and px-values.
+   * The CSS function calc(100% - [number]px) doesn't work in table.
+   */
+  private fixColumnWidth(): void {
+    const renderedColumnWidth: string[] = this.renderedColumnWidth;
+
+    const hasPxAndFrValues = renderedColumnWidth.some(value => value.endsWith("px") || value.endsWith("fr"));
+    if (hasPxAndFrValues) {
+      const percent100InPx = parseFloat(getComputedStyle(this).width);
+      if (percent100InPx > 0) {
+        let frBaseInPx = percent100InPx;
+        renderedColumnWidth.forEach((value) => {
+          if (value.endsWith("px")) {
+            frBaseInPx -= parseFloat(value);
+          } else if (value.endsWith("%")) {
+            frBaseInPx -= (percent100InPx * parseFloat(value) / 100);
+          } else if (value === "auto") {
+            const autoInPercent = 100 / (this.headerCols.length - 2);
+            frBaseInPx -= (percent100InPx * autoInPercent / 100);
+          }
+        });
+
+        const sumOfFr = renderedColumnWidth
+            .filter(value => value.endsWith("fr"))
+            .map(value => parseFloat(value))
+            .reduce((previousValue, currentValue) => previousValue + currentValue, 0);
+
+        renderedColumnWidth.forEach((value, index) => {
+          if (value.endsWith("fr")) {
+            // Use percent value instead of px value to prioritize the sheet width over column (percentage) width.
+            const widthPercent = 100 * frBaseInPx / percent100InPx / sumOfFr * parseFloat(value) + "%";
+            this.headerCols.item(index).style.width = widthPercent;
+            this.bodyCols.item(index).style.width = widthPercent;
+          }
+        });
+      }
+    }
+  }
+
   mousedown(event: MouseEvent): void {
 
     Page.page(this).dataset.SheetMousedownData = this.id;
@@ -757,6 +799,37 @@ Type: ${data.type}`);
     return rootNode.getElementById(this.id + "::rendered") as HTMLInputElement;
   }
 
+  /**
+   * Return the width of all rendered col tags as a string array. If the user has set the width manually, the exact
+   * pixel value is used, otherwise the layout value.
+   */
+  private get renderedColumnWidth(): string[] {
+    const renderedColumnWidth: string[] = [];
+    const layoutValues: any[] = JSON.parse(this.dataset.tobagoLayout).columns;
+    this.hiddenInputRenderedValue.forEach((rendered, i) => {
+      if (rendered) {
+        let pxValue = -1;
+        if (this.hiddenInputWidthValue.length > i) {
+          pxValue = this.hiddenInputWidthValue[i];
+        }
+
+        if (pxValue > -1) {
+          renderedColumnWidth.push(pxValue + "px");
+        } else {
+          const layoutValue = layoutValues[i % layoutValues.length];
+          if (typeof layoutValue === "number") {
+            renderedColumnWidth.push(layoutValue + "fr");
+          } else if (typeof layoutValue === "object" && layoutValue.measure !== undefined) {
+            renderedColumnWidth.push(layoutValue.measure);
+          } else {
+            renderedColumnWidth.push(layoutValue); //auto
+          }
+        }
+      }
+    });
+    return renderedColumnWidth;
+  }
+
   get scrollPosition(): number[] {
     return JSON.parse(this.hiddenInputScrollPosition.value);
   }