From e99e12cc7324a85ab335c8cae4f920326a2e9ea1 Mon Sep 17 00:00:00 2001 From: Armaan Gupta Date: Wed, 10 Jun 2026 13:10:54 +0530 Subject: [PATCH 1/5] added mobile layout handling --- .../table/clientlibs/site/css/tableview.css | 80 ++++++++++++++++++- .../v1/table/clientlibs/site/js/tableview.js | 45 +++++++++++ .../clientlibs/site/css/tablerowview.css | 25 +++++- 3 files changed, 146 insertions(+), 4 deletions(-) diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/table/v1/table/clientlibs/site/css/tableview.css b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/table/v1/table/clientlibs/site/css/tableview.css index f45e61ae0a..b55c3edd81 100644 --- a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/table/v1/table/clientlibs/site/css/tableview.css +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/table/v1/table/clientlibs/site/css/tableview.css @@ -70,9 +70,83 @@ .cell-wrapper {} +/* + * Mobile card layout (<= 768px). + * + * Each row is transformed into a self-contained card. The transform is achieved + * purely by overriding the CSS table display values to `block` — no DOM mutation, + * no field disabling, no JS layout switching. The header row is visually removed + * and each cell instead shows its column header via a ::before label sourced from + * the data-label attribute stamped by tableview.js (#stampMobileLabels). + */ @media (max-width: 768px) { - .cmp-adaptiveform-table__widget {} - + .cmp-adaptiveform-table__widget, + .cmp-adaptiveform-table__head, + .cmp-adaptiveform-table__body, + .cmp-adaptiveform-tableheader, + .cmp-adaptiveform-tablerow, .cmp-adaptiveform-tablecell, - .cmp-adaptiveform-tablehead {} + .cmp-adaptiveform-tablehead { + display: block; + width: auto; + } + + /* + * Visually hide the header row — labels are surfaced per-cell via ::before. + * Kept in the accessibility tree (off-screen, not display:none) so the + * semantic header association and any sort controls remain reachable. + */ + .cmp-adaptiveform-table__widget .cmp-adaptiveform-table__head { + position: absolute; + top: -9999px; + left: -9999px; + } + + /* Each body row becomes a card. */ + .cmp-adaptiveform-table__body .cmp-adaptiveform-tablerow { + border: 1px solid #DDDDDD; + border-radius: 4px; + margin-bottom: 1rem; + padding: 0.25rem 0.5rem; + font-size: 0.875rem; + } + + /* Cells stack vertically; reserve the left half for the header label. */ + .cmp-adaptiveform-table__body .cmp-adaptiveform-tablecell { + border: none; + border-bottom: 1px solid #F0F0F0; + position: relative; + padding: 0.5rem 0.5rem 0.5rem 45%; + text-align: left; + min-height: 1.5rem; + } + + .cmp-adaptiveform-table__body .cmp-adaptiveform-tablerow > .cmp-adaptiveform-tablecell:last-child { + border-bottom: none; + } + + /* Header label rendered from the stamped data-label attribute. */ + .cmp-adaptiveform-table__body .cmp-adaptiveform-tablecell::before { + content: attr(data-label); + position: absolute; + left: 0.5rem; + top: 0.5rem; + width: 40%; + padding-right: 0.5rem; + font-weight: 600; + white-space: normal; + overflow-wrap: break-word; + box-sizing: border-box; + } + + /* Cells with no header text (e.g. the repeatable-controls column) span full width. */ + .cmp-adaptiveform-table__body .cmp-adaptiveform-tablecell:not([data-label])::before, + .cmp-adaptiveform-table__body .cmp-adaptiveform-tablecell[data-label=""]::before { + content: none; + } + + .cmp-adaptiveform-table__body .cmp-adaptiveform-tablecell:not([data-label]), + .cmp-adaptiveform-table__body .cmp-adaptiveform-tablecell[data-label=""] { + padding-left: 0.5rem; + } } diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/table/v1/table/clientlibs/site/js/tableview.js b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/table/v1/table/clientlibs/site/js/tableview.js index daca571477..4dc02e3737 100644 --- a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/table/v1/table/clientlibs/site/js/tableview.js +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/table/v1/table/clientlibs/site/js/tableview.js @@ -40,6 +40,7 @@ super.setModel(model); queueMicrotask(() => { this.#initColumnSortingIfEnabled(); + this.#stampMobileLabels(); }); } @@ -113,6 +114,47 @@ this.updateLabel(state.label); } + /** + * Mobile card layout (CSS-driven, via @media max-width:768px) stacks each + * row into a card and renders the column header as a ::before label sourced + * from each cell's data-label attribute. This method is the single point that + * stamps that attribute: it reads the header text from each and copies + * it onto the matching column index in every body . + * + * Layout itself is pure CSS — this only supplies the label text. It runs on + * init and again after rows are added so dynamically-cloned rows are covered. + * It never touches field state, visibility, or DOM structure. + * + * @param {HTMLElement} [scope] - Optional row element to limit stamping to + * (used after a single row is added); defaults to the whole tbody. + */ + #stampMobileLabels(scope) { + const widget = this.element.querySelector(Table.selectors.widget); + if (!widget) { + return; + } + const thead = widget.querySelector("thead"); + const tbody = widget.querySelector("tbody"); + if (!thead || !tbody) { + return; + } + const headers = Array.from(thead.querySelectorAll("th.cmp-adaptiveform-tablehead")) + .map((th) => th.innerText.replace(/\s+/g, " ").trim()); + if (headers.length === 0) { + return; + } + const rows = scope && scope.matches && scope.matches("tr") + ? [scope] + : Array.from(tbody.querySelectorAll(":scope > tr")); + rows.forEach((row) => { + Array.from(row.cells).forEach((cell, index) => { + if (index < headers.length && headers[index]) { + cell.setAttribute("data-label", headers[index]); + } + }); + }); + } + /** * Get the element for row insertion. */ @@ -177,6 +219,9 @@ // the wrong model index when clicked. this.#syncTableRowHooks(htmlElement, addedModel.id); + // Stamp mobile card labels on the freshly added row. + this.#stampMobileLabels(htmlElement); + return htmlElement; } diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/tablerow/v1/tablerow/clientlibs/site/css/tablerowview.css b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/tablerow/v1/tablerow/clientlibs/site/css/tablerowview.css index dcb91ae672..ac65205ebd 100644 --- a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/tablerow/v1/tablerow/clientlibs/site/css/tablerowview.css +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/tablerow/v1/tablerow/clientlibs/site/css/tablerowview.css @@ -35,4 +35,27 @@ .cmp-adaptiveform-tablerow__remove-button[data-cmp-visible="false"] {} .cmp-adaptiveform-tablerow__add-button:focus-visible, -.cmp-adaptiveform-tablerow__remove-button:focus-visible {} +.cmp-adaptiveform-tablerow__remove-button:focus-visible { + outline: 2px solid #2680EB; + outline-offset: 2px; +} + +/* + * Mobile card layout (<= 768px): the table.css media query stacks every cell as a + * block with a ::before header label. The last cell carries the add/remove controls + * via a flex layout that fights that block model and the -1px margin, so reset it + * here and lay the controls out as a footer row inside the card. + */ +@media (max-width: 768px) { + .cmp-adaptiveform-tablecell.cmp-adaptiveform-tablecell--with-row-controls { + display: block; + margin: 0; + border: none; + } + + .cmp-adaptiveform-tablerow__runtime-controls { + display: flex; + justify-content: flex-end; + margin-top: 0.5rem; + } +} From d589df7947903db071b9f701df1cc78f476d1f1d Mon Sep 17 00:00:00 2001 From: Armaan Gupta Date: Mon, 20 Jul 2026 15:06:39 +0530 Subject: [PATCH 2/5] fixed mobile layout css --- .../form/table/v1/table/clientlibs/site/css/tableview.css | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/table/v1/table/clientlibs/site/css/tableview.css b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/table/v1/table/clientlibs/site/css/tableview.css index b55c3edd81..e71307bf11 100644 --- a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/table/v1/table/clientlibs/site/css/tableview.css +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/table/v1/table/clientlibs/site/css/tableview.css @@ -104,6 +104,7 @@ /* Each body row becomes a card. */ .cmp-adaptiveform-table__body .cmp-adaptiveform-tablerow { + display: block; border: 1px solid #DDDDDD; border-radius: 4px; margin-bottom: 1rem; @@ -113,6 +114,7 @@ /* Cells stack vertically; reserve the left half for the header label. */ .cmp-adaptiveform-table__body .cmp-adaptiveform-tablecell { + display: block; border: none; border-bottom: 1px solid #F0F0F0; position: relative; From 48b06be7f69efdc8be48e6568d4ea45cf5abd84b Mon Sep 17 00:00:00 2001 From: Armaan Gupta Date: Mon, 20 Jul 2026 16:20:06 +0530 Subject: [PATCH 3/5] added sorting feature in mobile layout --- .../table/clientlibs/site/css/tableview.css | 128 +++++++++++ .../v1/table/clientlibs/site/js/tableview.js | 201 +++++++++++++++++- 2 files changed, 326 insertions(+), 3 deletions(-) diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/table/v1/table/clientlibs/site/css/tableview.css b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/table/v1/table/clientlibs/site/css/tableview.css index e71307bf11..8df6936264 100644 --- a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/table/v1/table/clientlibs/site/css/tableview.css +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/table/v1/table/clientlibs/site/css/tableview.css @@ -70,6 +70,96 @@ .cell-wrapper {} +/* Mobile sort bar — hidden on desktop. */ +.cmp-adaptiveform-table__mobile-bar { + display: none; +} + +/* Active state on filter button when a direction is applied. */ +.cmp-adaptiveform-table__mobile-bar-btn--filter.is-active { + color: #2680EB; + font-weight: 600; +} + +/* + * Sort sheet overlay — appended to document.body, must be outside the media + * query so it is available at all viewport sizes (JS creates it once on init). + */ +.cmp-adaptiveform-table__sort-overlay { + display: none; + position: fixed; + inset: 0; + background-color: rgba(0, 0, 0, 0.45); + z-index: 1000; + align-items: flex-end; +} + +.cmp-adaptiveform-table__sort-overlay.is-open { + display: flex; +} + +.cmp-adaptiveform-table__sort-sheet { + width: 100%; + background: #FFFFFF; + border-radius: 1rem 1rem 0 0; + padding-bottom: 2rem; + max-height: 80vh; + overflow-y: auto; + transform: translateY(100%); + transition: transform 0.25s ease-out; +} + +.cmp-adaptiveform-table__sort-overlay.is-open .cmp-adaptiveform-table__sort-sheet { + transform: translateY(0); +} + +.cmp-adaptiveform-table__sort-sheet-handle { + width: 2.5rem; + height: 0.25rem; + background: #CCCCCC; + border-radius: 0.125rem; + margin: 0.75rem auto 1rem; +} + +.cmp-adaptiveform-table__sort-sheet-title { + font-size: 0.875rem; + color: #6B6B6B; + padding: 0.5rem 1rem; + border-bottom: 1px solid #F0F0F0; + margin: 0; +} + +.cmp-adaptiveform-table__sort-options { + list-style: none; + margin: 0; + padding: 0; +} + +.cmp-adaptiveform-table__sort-option { + display: flex; + align-items: center; + justify-content: space-between; + padding: 0.875rem 1rem; + font-size: 1rem; + cursor: pointer; + border-bottom: 1px solid #F5F5F5; +} + +.cmp-adaptiveform-table__sort-option:hover { + background-color: #F9F9F9; +} + +.cmp-adaptiveform-table__sort-option[aria-selected="true"] { + font-weight: 600; +} + +.cmp-adaptiveform-table__sort-option-indicator { + color: #505050; + font-size: 0.875rem; + min-width: 1rem; + text-align: right; +} + /* * Mobile card layout (<= 768px). * @@ -151,4 +241,42 @@ .cmp-adaptiveform-table__body .cmp-adaptiveform-tablecell[data-label=""] { padding-left: 0.5rem; } + + /* Mobile sort/filter action bar rendered below the table widget. */ + .cmp-adaptiveform-table__mobile-bar { + display: flex; + align-items: center; + border-top: 1px solid #E0E0E0; + padding: 0.25rem 0; + } + + .cmp-adaptiveform-table__mobile-bar-btn { + flex: 1; + display: flex; + align-items: center; + justify-content: center; + gap: 0.375rem; + background: none; + border: none; + cursor: pointer; + font-size: 0.875rem; + color: #333333; + padding: 0.625rem 0; + } + + .cmp-adaptiveform-table__mobile-bar-btn:focus-visible { + outline: 2px solid #2680EB; + outline-offset: 1px; + } + + .cmp-adaptiveform-table__mobile-bar-btn:disabled { + color: #AAAAAA; + cursor: default; + } + + .cmp-adaptiveform-table__mobile-bar-divider { + width: 1px; + height: 1.25rem; + background: #E0E0E0; + } } diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/table/v1/table/clientlibs/site/js/tableview.js b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/table/v1/table/clientlibs/site/js/tableview.js index 4dc02e3737..81f8a063f9 100644 --- a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/table/v1/table/clientlibs/site/js/tableview.js +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/table/v1/table/clientlibs/site/js/tableview.js @@ -34,6 +34,10 @@ this.children = []; /** @type {{ col: number, dir: 'asc'|'desc' }|null} */ this._tableSortState = null; + /** @type {HTMLElement|null} */ + this._mobileSortOverlay = null; + /** @type {HTMLElement|null} */ + this._mobileFilterOverlay = null; } setModel(model) { @@ -41,6 +45,7 @@ queueMicrotask(() => { this.#initColumnSortingIfEnabled(); this.#stampMobileLabels(); + this.#initMobileSortBar(); }); } @@ -308,13 +313,13 @@ * @param {HTMLTableSectionElement} thead * @param {number} colIndex */ - #sortTableByColumn(tbody, thead, colIndex) { + #sortTableByColumn(tbody, thead, colIndex, forceDir = null) { const rows = Array.from(tbody.querySelectorAll(":scope > tr")); if (rows.length <= 1) { return; } - let dir = "asc"; - if (this._tableSortState && this._tableSortState.col === colIndex) { + let dir = forceDir ?? "asc"; + if (!forceDir && this._tableSortState && this._tableSortState.col === colIndex) { dir = this._tableSortState.dir === "asc" ? "desc" : "asc"; } this._tableSortState = { col: colIndex, dir: dir }; @@ -366,6 +371,196 @@ } } + /** + * Builds the mobile action bar (Sort | Filter) inserted before the table widget. + * Bar is hidden on desktop via CSS. Overlays appended to document.body so + * position:fixed works freely. Sort requires enableSorting; filter is always on. + */ + #initMobileSortBar() { + const widget = this.element.querySelector(Table.selectors.widget); + if (!widget) return; + const thead = widget.querySelector('thead'); + const tbody = widget.querySelector('tbody'); + if (!thead || !tbody) return; + + const esc = (s) => s.replace(/&/g, '&').replace(//g, '>'); + const headers = Array.from(thead.querySelectorAll('th.cmp-adaptiveform-tablehead')) + .map((th) => th.innerText.replace(/\s+/g, ' ').trim()); + if (headers.length === 0) return; + + const sortingEnabled = this.element.dataset.cmpSortingEnabled === 'true'; + + // --- Action bar (inserted BEFORE the widget) --- + const bar = document.createElement('div'); + bar.className = 'cmp-adaptiveform-table__mobile-bar'; + bar.innerHTML = ` + + + `; + widget.before(bar); + + // --- Sort overlay --- + if (sortingEnabled) { + const optionsHtml = headers.map((h, i) => ` +
  • + ${esc(h)} + +
  • `).join(''); + + const sortOverlay = document.createElement('div'); + sortOverlay.className = 'cmp-adaptiveform-table__sort-overlay'; + sortOverlay.setAttribute('role', 'dialog'); + sortOverlay.setAttribute('aria-modal', 'true'); + sortOverlay.setAttribute('aria-label', 'Sort options'); + sortOverlay.innerHTML = ` +
    + +

    Sort by

    +
      + ${optionsHtml} +
    +
    `; + document.body.appendChild(sortOverlay); + this._mobileSortOverlay = sortOverlay; + + bar.querySelector('.cmp-adaptiveform-table__mobile-bar-btn--sort').addEventListener('click', () => { + this.#openMobileSortSheet(); + }); + sortOverlay.addEventListener('click', (e) => { + if (e.target === sortOverlay) this.#closeMobileSortSheet(); + }); + sortOverlay.addEventListener('keydown', (e) => { + if (e.key === 'Escape') this.#closeMobileSortSheet(); + }); + sortOverlay.querySelectorAll('.cmp-adaptiveform-table__sort-option').forEach((opt) => { + const activate = () => { + const colIndex = parseInt(opt.dataset.colIndex, 10); + this.#sortTableByColumn(tbody, thead, colIndex); + this.#updateMobileSortIndicators(); + this.#closeMobileSortSheet(); + }; + opt.addEventListener('click', activate); + opt.addEventListener('keydown', (e) => { + if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); activate(); } + }); + }); + } + + // --- Filter overlay (Ascending / Descending direction picker) --- + const filterOverlay = document.createElement('div'); + filterOverlay.className = 'cmp-adaptiveform-table__sort-overlay'; + filterOverlay.setAttribute('role', 'dialog'); + filterOverlay.setAttribute('aria-modal', 'true'); + filterOverlay.setAttribute('aria-label', 'Sort order'); + filterOverlay.innerHTML = ` +
    + +

    Sort order

    +
      +
    • + Ascending + +
    • +
    • + Descending + +
    • +
    +
    `; + document.body.appendChild(filterOverlay); + this._mobileFilterOverlay = filterOverlay; + + bar.querySelector('.cmp-adaptiveform-table__mobile-bar-btn--filter').addEventListener('click', () => { + this.#openMobileFilterSheet(); + }); + filterOverlay.addEventListener('click', (e) => { + if (e.target === filterOverlay) this.#closeMobileFilterSheet(); + }); + filterOverlay.addEventListener('keydown', (e) => { + if (e.key === 'Escape') this.#closeMobileFilterSheet(); + }); + filterOverlay.querySelectorAll('.cmp-adaptiveform-table__sort-option').forEach((opt) => { + const activate = () => { + const dir = opt.dataset.dir; + const colIndex = this._tableSortState ? this._tableSortState.col : 0; + this.#sortTableByColumn(tbody, thead, colIndex, dir); + this.#updateMobileFilterIndicators(); + this.#closeMobileFilterSheet(); + }; + opt.addEventListener('click', activate); + opt.addEventListener('keydown', (e) => { + if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); activate(); } + }); + }); + } + + #openMobileSortSheet() { + if (!this._mobileSortOverlay) return; + this.#updateMobileSortIndicators(); + this._mobileSortOverlay.classList.add('is-open'); + document.body.style.overflow = 'hidden'; + const first = this._mobileSortOverlay.querySelector('.cmp-adaptiveform-table__sort-option'); + if (first) first.focus(); + } + + #closeMobileSortSheet() { + if (!this._mobileSortOverlay) return; + this._mobileSortOverlay.classList.remove('is-open'); + document.body.style.overflow = ''; + } + + #openMobileFilterSheet() { + if (!this._mobileFilterOverlay) return; + this.#updateMobileFilterIndicators(); + this._mobileFilterOverlay.classList.add('is-open'); + document.body.style.overflow = 'hidden'; + const first = this._mobileFilterOverlay.querySelector('.cmp-adaptiveform-table__sort-option'); + if (first) first.focus(); + } + + #closeMobileFilterSheet() { + if (!this._mobileFilterOverlay) return; + this._mobileFilterOverlay.classList.remove('is-open'); + document.body.style.overflow = ''; + } + + #updateMobileFilterIndicators() { + if (!this._mobileFilterOverlay) return; + this._mobileFilterOverlay.querySelectorAll('.cmp-adaptiveform-table__sort-option').forEach((opt) => { + const active = !!this._tableSortState && this._tableSortState.dir === opt.dataset.dir; + opt.setAttribute('aria-selected', active ? 'true' : 'false'); + const indicator = opt.querySelector('.cmp-adaptiveform-table__sort-option-indicator'); + if (indicator) indicator.textContent = active ? '✓' : ''; + }); + } + + #updateMobileSortIndicators() { + if (!this._mobileSortOverlay) return; + this._mobileSortOverlay.querySelectorAll('.cmp-adaptiveform-table__sort-option').forEach((opt) => { + const colIndex = parseInt(opt.dataset.colIndex, 10); + const indicator = opt.querySelector('.cmp-adaptiveform-table__sort-option-indicator'); + const active = this._tableSortState && this._tableSortState.col === colIndex; + opt.setAttribute('aria-selected', active ? 'true' : 'false'); + if (indicator) { + indicator.textContent = active ? (this._tableSortState.dir === 'asc' ? '↑' : '↓') : ''; + } + }); + } + /** * @param {HTMLTableCellElement|undefined} cell * @returns {string} From f71c40d9367107c64b9d3b1622333b433b23f9f2 Mon Sep 17 00:00:00 2001 From: Armaan Gupta Date: Tue, 11 Aug 2026 19:49:51 +0530 Subject: [PATCH 4/5] resolved merge conflicts --- .../components/form/table/v1/table/README.md | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/table/v1/table/README.md b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/table/v1/table/README.md index d3221a18b1..79a8b3042e 100644 --- a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/table/v1/table/README.md +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/table/v1/table/README.md @@ -22,6 +22,8 @@ Adaptive Form Table component written in HTL that allows authors to capture data * Ability to contain `tableheader` and `tablerow` child components * Configurable proportional column widths via comma-separated values * Optional column sorting with ascending/descending toggle (per-column sort can be individually disabled) +* Mobile card layout — at `max-width: 768px` rows stack into cards; column headers are shown as inline labels via CSS `::before` using `data-label` attributes stamped on each `` +* Mobile action bar — Sort and Filter overlays accessible via bottom-sheet UI on narrow viewports; Sort requires `enableSorting` * Document of Record (DoR) support — table structure and column widths exported for XFA-based DoR rendering * Short description / long description / question mark help pattern * Visible and enabled state binding for rules engine @@ -68,6 +70,18 @@ BLOCK cmp-adaptiveform-table ELEMENT cmp-adaptiveform-tablerow__runtime-controls ELEMENT cmp-adaptiveform-tablerow__add-button ELEMENT cmp-adaptiveform-tablerow__remove-button + ELEMENT cmp-adaptiveform-table__mobile-bar (injected by JS; hidden on desktop) + ELEMENT cmp-adaptiveform-table__mobile-bar-btn + MODIFIER cmp-adaptiveform-table__mobile-bar-btn--sort + MODIFIER cmp-adaptiveform-table__mobile-bar-btn--filter + ELEMENT cmp-adaptiveform-table__mobile-bar-divider + ELEMENT cmp-adaptiveform-table__sort-overlay (shared by sort and filter bottom-sheet overlays) + ELEMENT cmp-adaptiveform-table__sort-sheet + ELEMENT cmp-adaptiveform-table__sort-sheet-handle + ELEMENT cmp-adaptiveform-table__sort-sheet-title + ELEMENT cmp-adaptiveform-table__sort-options + ELEMENT cmp-adaptiveform-table__sort-option + ELEMENT cmp-adaptiveform-table__sort-option-indicator ``` ## Theme Editor Support @@ -94,7 +108,10 @@ The following attributes are required for initialization: The following are optional attributes that can be added to the component: 1. `data-cmp-visible` - boolean indicating whether the component is currently visible 2. `data-cmp-enabled` - boolean indicating whether the component is currently enabled -3. `data-cmp-sorting-enabled` - set to `"true"` when `./enableSorting` is authored; controls sort button rendering in `tableheader.html` +3. `data-cmp-sorting-enabled` - set to `"true"` when `./enableSorting` is authored; controls sort button rendering in `tableheader.html` and enables the Sort button in the mobile action bar + +The following attribute is stamped by JavaScript on each `` in the table body at runtime: +1. `data-label` - set to the corresponding column header text; used by CSS `::before` to render inline labels in the mobile card layout (no author action required) ## Information * **Vendor**: Adobe From a548febcc7a998892bbe0599114b2d69f16846f2 Mon Sep 17 00:00:00 2001 From: Armaan Gupta Date: Thu, 30 Jul 2026 15:22:01 +0530 Subject: [PATCH 5/5] moved the css part from the core components to the theme canvas --- .../table/clientlibs/site/css/tableview.css | 210 +----------------- .../clientlibs/site/css/tablerowview.css | 25 +-- 2 files changed, 7 insertions(+), 228 deletions(-) diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/table/v1/table/clientlibs/site/css/tableview.css b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/table/v1/table/clientlibs/site/css/tableview.css index 8df6936264..f45e61ae0a 100644 --- a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/table/v1/table/clientlibs/site/css/tableview.css +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/table/v1/table/clientlibs/site/css/tableview.css @@ -70,213 +70,9 @@ .cell-wrapper {} -/* Mobile sort bar — hidden on desktop. */ -.cmp-adaptiveform-table__mobile-bar { - display: none; -} - -/* Active state on filter button when a direction is applied. */ -.cmp-adaptiveform-table__mobile-bar-btn--filter.is-active { - color: #2680EB; - font-weight: 600; -} - -/* - * Sort sheet overlay — appended to document.body, must be outside the media - * query so it is available at all viewport sizes (JS creates it once on init). - */ -.cmp-adaptiveform-table__sort-overlay { - display: none; - position: fixed; - inset: 0; - background-color: rgba(0, 0, 0, 0.45); - z-index: 1000; - align-items: flex-end; -} - -.cmp-adaptiveform-table__sort-overlay.is-open { - display: flex; -} - -.cmp-adaptiveform-table__sort-sheet { - width: 100%; - background: #FFFFFF; - border-radius: 1rem 1rem 0 0; - padding-bottom: 2rem; - max-height: 80vh; - overflow-y: auto; - transform: translateY(100%); - transition: transform 0.25s ease-out; -} - -.cmp-adaptiveform-table__sort-overlay.is-open .cmp-adaptiveform-table__sort-sheet { - transform: translateY(0); -} - -.cmp-adaptiveform-table__sort-sheet-handle { - width: 2.5rem; - height: 0.25rem; - background: #CCCCCC; - border-radius: 0.125rem; - margin: 0.75rem auto 1rem; -} - -.cmp-adaptiveform-table__sort-sheet-title { - font-size: 0.875rem; - color: #6B6B6B; - padding: 0.5rem 1rem; - border-bottom: 1px solid #F0F0F0; - margin: 0; -} - -.cmp-adaptiveform-table__sort-options { - list-style: none; - margin: 0; - padding: 0; -} - -.cmp-adaptiveform-table__sort-option { - display: flex; - align-items: center; - justify-content: space-between; - padding: 0.875rem 1rem; - font-size: 1rem; - cursor: pointer; - border-bottom: 1px solid #F5F5F5; -} - -.cmp-adaptiveform-table__sort-option:hover { - background-color: #F9F9F9; -} - -.cmp-adaptiveform-table__sort-option[aria-selected="true"] { - font-weight: 600; -} - -.cmp-adaptiveform-table__sort-option-indicator { - color: #505050; - font-size: 0.875rem; - min-width: 1rem; - text-align: right; -} - -/* - * Mobile card layout (<= 768px). - * - * Each row is transformed into a self-contained card. The transform is achieved - * purely by overriding the CSS table display values to `block` — no DOM mutation, - * no field disabling, no JS layout switching. The header row is visually removed - * and each cell instead shows its column header via a ::before label sourced from - * the data-label attribute stamped by tableview.js (#stampMobileLabels). - */ @media (max-width: 768px) { - .cmp-adaptiveform-table__widget, - .cmp-adaptiveform-table__head, - .cmp-adaptiveform-table__body, - .cmp-adaptiveform-tableheader, - .cmp-adaptiveform-tablerow, - .cmp-adaptiveform-tablecell, - .cmp-adaptiveform-tablehead { - display: block; - width: auto; - } - - /* - * Visually hide the header row — labels are surfaced per-cell via ::before. - * Kept in the accessibility tree (off-screen, not display:none) so the - * semantic header association and any sort controls remain reachable. - */ - .cmp-adaptiveform-table__widget .cmp-adaptiveform-table__head { - position: absolute; - top: -9999px; - left: -9999px; - } - - /* Each body row becomes a card. */ - .cmp-adaptiveform-table__body .cmp-adaptiveform-tablerow { - display: block; - border: 1px solid #DDDDDD; - border-radius: 4px; - margin-bottom: 1rem; - padding: 0.25rem 0.5rem; - font-size: 0.875rem; - } + .cmp-adaptiveform-table__widget {} - /* Cells stack vertically; reserve the left half for the header label. */ - .cmp-adaptiveform-table__body .cmp-adaptiveform-tablecell { - display: block; - border: none; - border-bottom: 1px solid #F0F0F0; - position: relative; - padding: 0.5rem 0.5rem 0.5rem 45%; - text-align: left; - min-height: 1.5rem; - } - - .cmp-adaptiveform-table__body .cmp-adaptiveform-tablerow > .cmp-adaptiveform-tablecell:last-child { - border-bottom: none; - } - - /* Header label rendered from the stamped data-label attribute. */ - .cmp-adaptiveform-table__body .cmp-adaptiveform-tablecell::before { - content: attr(data-label); - position: absolute; - left: 0.5rem; - top: 0.5rem; - width: 40%; - padding-right: 0.5rem; - font-weight: 600; - white-space: normal; - overflow-wrap: break-word; - box-sizing: border-box; - } - - /* Cells with no header text (e.g. the repeatable-controls column) span full width. */ - .cmp-adaptiveform-table__body .cmp-adaptiveform-tablecell:not([data-label])::before, - .cmp-adaptiveform-table__body .cmp-adaptiveform-tablecell[data-label=""]::before { - content: none; - } - - .cmp-adaptiveform-table__body .cmp-adaptiveform-tablecell:not([data-label]), - .cmp-adaptiveform-table__body .cmp-adaptiveform-tablecell[data-label=""] { - padding-left: 0.5rem; - } - - /* Mobile sort/filter action bar rendered below the table widget. */ - .cmp-adaptiveform-table__mobile-bar { - display: flex; - align-items: center; - border-top: 1px solid #E0E0E0; - padding: 0.25rem 0; - } - - .cmp-adaptiveform-table__mobile-bar-btn { - flex: 1; - display: flex; - align-items: center; - justify-content: center; - gap: 0.375rem; - background: none; - border: none; - cursor: pointer; - font-size: 0.875rem; - color: #333333; - padding: 0.625rem 0; - } - - .cmp-adaptiveform-table__mobile-bar-btn:focus-visible { - outline: 2px solid #2680EB; - outline-offset: 1px; - } - - .cmp-adaptiveform-table__mobile-bar-btn:disabled { - color: #AAAAAA; - cursor: default; - } - - .cmp-adaptiveform-table__mobile-bar-divider { - width: 1px; - height: 1.25rem; - background: #E0E0E0; - } + .cmp-adaptiveform-tablecell, + .cmp-adaptiveform-tablehead {} } diff --git a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/tablerow/v1/tablerow/clientlibs/site/css/tablerowview.css b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/tablerow/v1/tablerow/clientlibs/site/css/tablerowview.css index ac65205ebd..541d56d587 100644 --- a/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/tablerow/v1/tablerow/clientlibs/site/css/tablerowview.css +++ b/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/tablerow/v1/tablerow/clientlibs/site/css/tablerowview.css @@ -35,27 +35,10 @@ .cmp-adaptiveform-tablerow__remove-button[data-cmp-visible="false"] {} .cmp-adaptiveform-tablerow__add-button:focus-visible, -.cmp-adaptiveform-tablerow__remove-button:focus-visible { - outline: 2px solid #2680EB; - outline-offset: 2px; -} +.cmp-adaptiveform-tablerow__remove-button:focus-visible {} -/* - * Mobile card layout (<= 768px): the table.css media query stacks every cell as a - * block with a ::before header label. The last cell carries the add/remove controls - * via a flex layout that fights that block model and the -1px margin, so reset it - * here and lay the controls out as a footer row inside the card. - */ @media (max-width: 768px) { - .cmp-adaptiveform-tablecell.cmp-adaptiveform-tablecell--with-row-controls { - display: block; - margin: 0; - border: none; - } - - .cmp-adaptiveform-tablerow__runtime-controls { - display: flex; - justify-content: flex-end; - margin-top: 0.5rem; - } + .cmp-adaptiveform-tablecell.cmp-adaptiveform-tablecell--with-row-controls {} + + .cmp-adaptiveform-tablerow__runtime-controls {} }