refator(ui): merge of ResourceList part 2
This commit is contained in:
@@ -178,6 +178,16 @@
|
|||||||
onfavorite?: (item: FileItem | FolderItem) => void;
|
onfavorite?: (item: FileItem | FolderItem) => void;
|
||||||
/** Selection changed (set of selected item ids). */
|
/** Selection changed (set of selected item ids). */
|
||||||
onselectionchange?: (ids: Set<string>) => void;
|
onselectionchange?: (ids: Set<string>) => void;
|
||||||
|
/**
|
||||||
|
* Right-click / long-press handler. When provided, ResourceList
|
||||||
|
* forwards the row's `contextmenu` event to this callback and
|
||||||
|
* SKIPS its built-in menu — the page renders and positions its
|
||||||
|
* own. Useful when the page needs conditional entries (WOPI
|
||||||
|
* editability, audio-only actions) that don't fit the flat
|
||||||
|
* `contextActions` array. If both `oncontextmenu` and
|
||||||
|
* `contextActions` are provided, `oncontextmenu` wins.
|
||||||
|
*/
|
||||||
|
oncontextmenu?: (e: MouseEvent, item: FileItem | FolderItem) => void;
|
||||||
/**
|
/**
|
||||||
* Per-item action cell (renders at the end of a row). Kept as a
|
* Per-item action cell (renders at the end of a row). Kept as a
|
||||||
* distinct slot from the action-bar snippets below so callers
|
* distinct slot from the action-bar snippets below so callers
|
||||||
@@ -320,6 +330,7 @@
|
|||||||
onopen,
|
onopen,
|
||||||
onfavorite,
|
onfavorite,
|
||||||
onselectionchange,
|
onselectionchange,
|
||||||
|
oncontextmenu: onContextMenuOverride,
|
||||||
itemActions,
|
itemActions,
|
||||||
actions,
|
actions,
|
||||||
batchActions,
|
batchActions,
|
||||||
@@ -393,6 +404,13 @@
|
|||||||
let gridWidth = $state(0);
|
let gridWidth = $state(0);
|
||||||
const gridCols = $derived(gridColumns(gridWidth));
|
const gridCols = $derived(gridColumns(gridWidth));
|
||||||
|
|
||||||
|
// Whether an action-cell renders per row — matches the row-template
|
||||||
|
// gate below. Feeds both the list-view column track and the header
|
||||||
|
// row's trailing placeholder so the layout stays in sync.
|
||||||
|
const hasActionCell = $derived(
|
||||||
|
!!onfavorite || !!itemActions || !!onContextMenuOverride || !!contextActions?.length
|
||||||
|
);
|
||||||
|
|
||||||
// Build the list-view column track from the enabled cells.
|
// Build the list-view column track from the enabled cells.
|
||||||
const columns = $derived(
|
const columns = $derived(
|
||||||
[
|
[
|
||||||
@@ -403,7 +421,7 @@
|
|||||||
showType ? '120px' : '',
|
showType ? '120px' : '',
|
||||||
showSize ? '110px' : '',
|
showSize ? '110px' : '',
|
||||||
showDate ? '160px' : '',
|
showDate ? '160px' : '',
|
||||||
itemActions ? '120px' : ''
|
hasActionCell ? '120px' : ''
|
||||||
]
|
]
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.join(' ')
|
.join(' ')
|
||||||
@@ -693,8 +711,8 @@
|
|||||||
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
|
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
|
||||||
<div
|
<div
|
||||||
class="file-item"
|
class="file-item"
|
||||||
class:file-item--selected={selectable && selected.has(item.id)}
|
class:selected={selectable && selected.has(item.id)}
|
||||||
class:file-item--drop-target={dropTarget && dropTargetId === item.id}
|
class:drop-target={dropTarget && dropTargetId === item.id}
|
||||||
role={onopen ? 'button' : undefined}
|
role={onopen ? 'button' : undefined}
|
||||||
tabindex={onopen ? 0 : undefined}
|
tabindex={onopen ? 0 : undefined}
|
||||||
aria-label={onopen ? item.name : undefined}
|
aria-label={onopen ? item.name : undefined}
|
||||||
@@ -715,10 +733,14 @@
|
|||||||
: undefined}
|
: undefined}
|
||||||
ondblclick={onopen && openOnDoubleClick ? () => onopen(item) : undefined}
|
ondblclick={onopen && openOnDoubleClick ? () => onopen(item) : undefined}
|
||||||
onkeydown={onopen ? (e) => e.key === 'Enter' && onopen(item) : undefined}
|
onkeydown={onopen ? (e) => e.key === 'Enter' && onopen(item) : undefined}
|
||||||
oncontextmenu={contextActions?.length ? (e) => openContext(e, item) : undefined}
|
oncontextmenu={onContextMenuOverride
|
||||||
|
? (e) => onContextMenuOverride(e, item)
|
||||||
|
: contextActions?.length
|
||||||
|
? (e) => openContext(e, item)
|
||||||
|
: undefined}
|
||||||
>
|
>
|
||||||
{#if selectable}
|
{#if selectable}
|
||||||
<div class="select-cell" role="presentation" onclick={(e) => e.stopPropagation()}>
|
<div class="checkbox-cell" role="presentation" onclick={(e) => e.stopPropagation()}>
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
aria-label={t('common.select', 'Select')}
|
aria-label={t('common.select', 'Select')}
|
||||||
@@ -788,10 +810,22 @@
|
|||||||
{#if dateVal != null}<span class="grid-meta__date">{formatDate(dateVal)}</span>{/if}
|
{#if dateVal != null}<span class="grid-meta__date">{formatDate(dateVal)}</span>{/if}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
<!--
|
||||||
|
Every row that surfaces an action puts everything into a single
|
||||||
|
`.action-cell` — the shared `ported/resourceList.css` styles both
|
||||||
|
the favorite-star and the `.file-actions` kebab expecting them to
|
||||||
|
live inside `.action-cell` (grid view uses the corner-overlay CSS
|
||||||
|
to float `.file-actions` into the top-right; list view flexes them
|
||||||
|
inline). The cell renders when ANY of favorite / itemActions /
|
||||||
|
context-menu is enabled; a row with none of those still lays out
|
||||||
|
cleanly because the columns collapse via the grid track.
|
||||||
|
-->
|
||||||
|
{#if onfavorite || itemActions || onContextMenuOverride || contextActions?.length}
|
||||||
|
<div class="action-cell">
|
||||||
{#if onfavorite}
|
{#if onfavorite}
|
||||||
<button
|
<button
|
||||||
class="rl-star"
|
class="favorite-star"
|
||||||
class:rl-star--on={isFav}
|
class:active={isFav}
|
||||||
data-testid={`resource-list-favorite-${item.id}-btn`}
|
data-testid={`resource-list-favorite-${item.id}-btn`}
|
||||||
title={isFav
|
title={isFav
|
||||||
? t('files.unfavorite', 'Remove favorite')
|
? t('files.unfavorite', 'Remove favorite')
|
||||||
@@ -800,11 +834,29 @@
|
|||||||
onclick={(e) => {
|
onclick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
onfavorite(item);
|
onfavorite(item);
|
||||||
}}><Icon name={isFav ? 'star' : 'star-outline'} /></button
|
}}
|
||||||
>
|
>
|
||||||
|
<Icon name={isFav ? 'star' : 'star-outline'} />
|
||||||
|
</button>
|
||||||
{/if}
|
{/if}
|
||||||
{#if itemActions}
|
{#if itemActions}{@render itemActions(item)}{/if}
|
||||||
<div class="action-cell">{@render itemActions(item)}</div>
|
{#if onContextMenuOverride || contextActions?.length}
|
||||||
|
<button
|
||||||
|
class="file-actions"
|
||||||
|
data-testid={`resource-list-more-${item.id}-btn`}
|
||||||
|
title={t('files.more_actions', 'More actions')}
|
||||||
|
aria-label={t('files.more_actions', 'More actions')}
|
||||||
|
aria-haspopup="menu"
|
||||||
|
onclick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (onContextMenuOverride) onContextMenuOverride(e, item);
|
||||||
|
else openContext(e, item);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon name="ellipsis-v" />
|
||||||
|
</button>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
@@ -836,20 +888,27 @@
|
|||||||
{/if}
|
{/if}
|
||||||
<ActionBar>
|
<ActionBar>
|
||||||
{#snippet start()}
|
{#snippet start()}
|
||||||
<div class="action-buttons">
|
|
||||||
<!--
|
<!--
|
||||||
The action-bar left cluster has two states:
|
The action-bar left cluster has two states:
|
||||||
1. `batchActions` — when the user has selected items, the
|
1. `batchActions` — when the user has selected items, the
|
||||||
page's batch buttons (Move / Delete / Restore / …)
|
page's batch buttons (Move / Delete / Restore / …)
|
||||||
replace the default cluster, prefixed with a "clear
|
replace the default cluster, prefixed with a "clear
|
||||||
selection" close button + count label so the batch is
|
selection" close button + count label so the batch is
|
||||||
dismissable without unchecking every row by hand.
|
dismissable without unchecking every row by hand. The
|
||||||
|
wrapper carries the `.batch-selection-bar` class so
|
||||||
|
the shared `batchToolbar.css` styling (muted
|
||||||
|
background, count typography, action hover states)
|
||||||
|
applies — matching the pre-migration `/files` look.
|
||||||
2. `actions` — the page's default cluster
|
2. `actions` — the page's default cluster
|
||||||
(Upload / New folder / Empty trash / Clear recent).
|
(Upload / New folder / Empty trash / Clear recent).
|
||||||
-->
|
-->
|
||||||
|
<div
|
||||||
|
class="action-buttons"
|
||||||
|
class:batch-selection-bar={selectable && selected.size > 0 && batchActions}
|
||||||
|
>
|
||||||
{#if selectable && selected.size > 0 && batchActions}
|
{#if selectable && selected.size > 0 && batchActions}
|
||||||
<button
|
<button
|
||||||
class="rl-batch-close"
|
class="batch-bar-close"
|
||||||
title={t('common.clear', 'Clear selection')}
|
title={t('common.clear', 'Clear selection')}
|
||||||
aria-label={t('common.clear', 'Clear selection')}
|
aria-label={t('common.clear', 'Clear selection')}
|
||||||
data-testid="resource-list-batch-close-btn"
|
data-testid="resource-list-batch-close-btn"
|
||||||
@@ -857,10 +916,12 @@
|
|||||||
>
|
>
|
||||||
<Icon name="times" />
|
<Icon name="times" />
|
||||||
</button>
|
</button>
|
||||||
<span class="rl-batch-count"
|
<span class="batch-bar-count"
|
||||||
>{t('files.selected_count', { count: selected.size }, '{{count}} selected')}</span
|
>{t('files.selected_count', { count: selected.size }, '{{count}} selected')}</span
|
||||||
>
|
>
|
||||||
|
<div class="batch-bar-actions">
|
||||||
{@render batchActions(selectedItems)}
|
{@render batchActions(selectedItems)}
|
||||||
|
</div>
|
||||||
{:else if actions}
|
{:else if actions}
|
||||||
{@render actions()}
|
{@render actions()}
|
||||||
{/if}
|
{/if}
|
||||||
@@ -975,7 +1036,7 @@
|
|||||||
{#snippet listHeader()}
|
{#snippet listHeader()}
|
||||||
<div class="list-header">
|
<div class="list-header">
|
||||||
{#if selectable}
|
{#if selectable}
|
||||||
<div class="select-cell">
|
<div class="checkbox-cell">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
aria-label={t('common.select_all', 'Select all')}
|
aria-label={t('common.select_all', 'Select all')}
|
||||||
@@ -991,7 +1052,7 @@
|
|||||||
{#if showType}<div>{t('files.col_type', 'Type')}</div>{/if}
|
{#if showType}<div>{t('files.col_type', 'Type')}</div>{/if}
|
||||||
{#if showSize}<div>{t('files.col_size', 'Size')}</div>{/if}
|
{#if showSize}<div>{t('files.col_size', 'Size')}</div>{/if}
|
||||||
{#if showDate}<div>{dateLabel ?? t('files.col_modified', 'Date')}</div>{/if}
|
{#if showDate}<div>{dateLabel ?? t('files.col_modified', 'Date')}</div>{/if}
|
||||||
{#if onfavorite || itemActions}<div></div>{/if}
|
{#if hasActionCell}<div></div>{/if}
|
||||||
</div>
|
</div>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
|
|
||||||
@@ -1073,51 +1134,6 @@
|
|||||||
margin-left: var(--space-2);
|
margin-left: var(--space-2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── Selection controls inside the action bar ──
|
|
||||||
Replaces the deprecated `.rl-batch` floating strip. When items
|
|
||||||
are selected, the close button + count sit before the page's
|
|
||||||
`batchActions` snippet inside `.action-buttons`, so the whole
|
|
||||||
cluster reads as one row of the action bar. */
|
|
||||||
.rl-batch-close {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
width: 28px;
|
|
||||||
height: 28px;
|
|
||||||
border: none;
|
|
||||||
border-radius: var(--radius-sm);
|
|
||||||
background: transparent;
|
|
||||||
color: var(--color-text-secondary);
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rl-batch-close:hover {
|
|
||||||
background: var(--color-bg-hover);
|
|
||||||
}
|
|
||||||
|
|
||||||
.rl-batch-count {
|
|
||||||
font-weight: var(--weight-semibold);
|
|
||||||
color: var(--color-text);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── Selection column ── */
|
|
||||||
.select-cell {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-item--selected {
|
|
||||||
background: var(--color-accent-bg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Drop-target highlight — mirrors the legacy files browser's cue when
|
|
||||||
dragging a row over a folder row. */
|
|
||||||
.file-item--drop-target {
|
|
||||||
outline: 2px dashed var(--color-accent);
|
|
||||||
outline-offset: -2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── Owner vignette ── */
|
/* ── Owner vignette ── */
|
||||||
.owner-cell {
|
.owner-cell {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -1138,29 +1154,6 @@
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── Favorite star ── */
|
|
||||||
.rl-star {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
width: 32px;
|
|
||||||
height: 32px;
|
|
||||||
border: none;
|
|
||||||
border-radius: var(--radius-sm);
|
|
||||||
background: transparent;
|
|
||||||
color: var(--color-text-faint);
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rl-star:hover {
|
|
||||||
background: var(--color-bg-hover);
|
|
||||||
color: var(--color-text-secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.rl-star--on {
|
|
||||||
color: var(--color-warning-text-amber);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── Swimlane section header ── */
|
/* ── Swimlane section header ── */
|
||||||
.rl-swimlane-header {
|
.rl-swimlane-header {
|
||||||
grid-column: 1 / -1;
|
grid-column: 1 / -1;
|
||||||
|
|||||||
@@ -21,13 +21,11 @@
|
|||||||
margin-right: var(--space-3);
|
margin-right: var(--space-3);
|
||||||
height: 60px;
|
height: 60px;
|
||||||
transform: translateY(-8px);
|
transform: translateY(-8px);
|
||||||
transition:
|
|
||||||
opacity 0.2s,
|
|
||||||
max-height 0.25s,
|
|
||||||
transform 0.2s,
|
|
||||||
margin 0.2s,
|
|
||||||
padding 0.2s;
|
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
|
/* Note: previous versions of this rule animated the bar's
|
||||||
|
appearance (opacity / max-height / transform / margin / padding
|
||||||
|
transitions on the class-add). Dropped intentionally — the bar
|
||||||
|
just appears/disappears with the selection state now. */
|
||||||
}
|
}
|
||||||
|
|
||||||
.batch-bar-close {
|
.batch-bar-close {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user