Skip to content

Nmc/6618 UI collection ticket 1 - #504

Merged
memurats merged 4 commits into
stable33from
nmc/6618-UI-collection-ticket-1
Sep 18, 2026
Merged

memurats merged 4 commits into
stable33from
nmc/6618-UI-collection-ticket-1

Conversation

@suet-kei-chan

Copy link
Copy Markdown
Contributor

No description provided.

NC33 renders the Modified filter's "Custom range" picker as an
`append-to-body` NcDateTimePicker, so vue2-datepicker leaves its panel as a
direct child of <body>. That broke the calendar two ways:

- Stacking: @nextcloud/vue styles the panel at z-index 2000 while
  floating-vue popovers sit at 100000, so it painted behind the menu it
  belongs to.
- Auto-close: floating-vue detects outside clicks with
  popperContent.contains(event.target). A <body> child never matches, so
  every day-cell click closed the popover and unmounted the picker
  mid-selection - meaning a z-index fix alone would have left it visible
  but still unusable.

Adopt the panel into the popover's .v-popper__inner instead. That settles
both at once: there is no separate stacking context left to fight, and the
containment check passes. The accompanying style rules reset vue2-datepicker's
inline absolute positioning so the panel flows inside, and drop its border and
background so it does not double up on the popover's own chrome.

The calendar roughly doubles the popover's height, and floating-vue's boundary
(#app-content-vue) reaches past the fold, so the popover is also capped to the
room measured below its trigger. That measurement is taken after shrinking it,
because an oversized popper has already been shifted upwards by then and its
position would over-report the space available.

Verified against NC 33.0.7 at 1440x900, 1440x768, 1440x640 and 480x900: the
popover stays within the viewport, the calendar is the topmost element at its
centre, range selection completes without closing the menu, and no panels leak
onto <body> across repeated open/close cycles.
Selecting "Custom range" pushed the menu from 231px to 349px, because
vue2-datepicker hardcodes .mx-datepicker-range to 320px and the wide preset
buttons then stretched to match it.

Let the picker fill the popover instead of dictating its width, with a floor
of 232px - measured as what "YYYY-MM-DD ~ YYYY-MM-DD" needs alongside the
calendar icon, below which the end date truncates.

The menu is now 261px with the input showing, and the picker still expands
with the popover when the calendar is open.
The range picker accepted dates in the future. NC does not constrain this
anywhere - five of its six presets have no upper bound at all - so this is a
deliberate tightening rather than a bug fix.

vue2-datepicker has a `disabledDate` prop that would cover both the grid and
typed input, but it belongs to a server-owned component and is reset on every
parent render, so the guard is applied at the DOM level in two parts:

- Day cells past today get a data attribute and aria-disabled, and CSS turns
  off pointer events, which blocks the click and the hover range preview at
  once. A data attribute rather than a class, because the cells have a dynamic
  class binding that Vue overwrites on re-render. Month navigation patches the
  cells in place, so the observer also watches characterData and class.
- The field is typable, so a capture-phase listener on document validates the
  text before vue2-datepicker sees it, restores the last committed value, and
  replays an input event so the picker's own draft state stays in sync.

Today remains selectable, and navigating back to a past month clears the marks
rather than leaving them stale.

Verified against NC 33.0.7: all 64 future cells of the current view marked and
none of the 20 past ones, every cell marked a month forward, none stale three
months back, future clicks inert, typed future ranges reverted and not
restored on re-render, typed past ranges still committing.
Scrolling the list left only 24px of the 56px Name/Size/Modified header
showing, and nothing at all once files were selected.

NC33 put a sticky filters row above the table and derives every sticky offset
from one custom property, --fixed-block-start-position: the filters row takes
its height from it, and the table header and selection overlay take their
sticky top from it. Three rules here predate that contract and broke it:

- `thead { top: 0 }` outranked NC's `top: var(--fixed-block-start-position)`
  on specificity, so the header pinned to the top of the scroll container,
  underneath the filters row. Dropped, leaving the offset to upstream.
- `.files-list__filters { padding-block: 1rem }` fought NC's fixed 28px
  height: 32px of padding collapsed the content box, leaving the chip
  container 0px tall with its chips overflowing 16px above the row. Replaced
  with flex: 0 0 auto, without which the row shrinks below its declared
  height and opens a gap for rows to scroll through.
- The selection overlay kept NC's 44px row height while the header here is
  56px, so a 12px sliver of header stayed visible under it. Both the overlay
  and the table's compensating negative margin now use 56px.

The row height is declared once, as --fixed-block-start-position: 32px, which
is what the filters row already rendered at, so nothing moves. Public share
pages hide the filters row outright, and NC's own empty-row escape hatch does
not fire for display:none, so they set it to 0 explicitly.

Verified against NC 33.0.7, scrolled and at rest: header 56/56px visible in
All files, Favorites, Deleted files and grid view; no gap between the filters
row and the header anywhere; with files selected the overlay covers the full
56px band; the chip container is 28px and inside the row; public share pages
pin the header flush to the top.
@suet-kei-chan suet-kei-chan self-assigned this Sep 18, 2026
@memurats
memurats merged commit 5cdb4eb into stable33 Sep 18, 2026
4 of 6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants