Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/dataviews/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
- DataViews: Generalize the ordering filter operators (`on`, `notOn`, `before`, `after`, `beforeInc`, `afterInc`, `between`) from dates to temporal values, so they also compare times of day. Comparisons for `date` and `datetime` are unchanged. [#80830](https://github.com/WordPress/gutenberg/pull/80830)
- DataViews: Add Shift+Click range selection through a shared `useSelectionProps` hook that layouts can adopt, wired up in the table and grid layouts.[#80046](https://github.com/WordPress/gutenberg/pull/80046)
- DataViewsPicker: Add Shift+Click range selection to the `picker-table`, `picker-grid`, and `picker-activity` layouts. [#80413](https://github.com/WordPress/gutenberg/pull/80413)
- DataViews: Add an `aspectRatio` layout option to the `grid` and `table` layouts so consumers can configure the aspect ratio of item media previews from a set of preset ratios, instead of the hard-coded square. Defaults to `1/1`, so existing consumers are unaffected. [#79329](https://github.com/WordPress/gutenberg/pull/79329)

### Bug Fix

Expand Down
3 changes: 3 additions & 0 deletions packages/dataviews/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -257,12 +257,14 @@ Properties:
| `styles` | ✓ | ✓ | | | | |
| `badgeFields` | | | ✓ | ✓ | | |
| `previewSize` | | | ✓ | ✓ | | |
| `aspectRatio` | ✓ | | ✓ | | | |

`table` and `pickerTable` layouts:

- `density`: one of `comfortable`, `balanced`, or `compact`. Configures the size and spacing of the layout.
- `enableMoving`: whether the table columns should display moving controls.
- `styles`: additional `width`, `maxWidth`, `minWidth`, `align` styles for each field column. The `align` property accepts `'start'`, `'center'`, or `'end'`.
- `aspectRatio` (`table` only): one of the preset ratios `'1/1'`, `'4/3'`, `'3/4'`, `'3/2'`, `'2/3'`, `'16/9'`, or `'9/16'`, applied to the primary column's media preview. Defaults to `'1/1'`.

**For column alignment (`align` property), follow these guidelines:**
Right-align (`'end'`) whenever the cell value is fundamentally quantitative—numbers, decimals, currency, percentages—so that digits and decimal points line up, aiding comparison and calculation. Otherwise, default to left-alignment (`'start'`) for all other types (text, codes, labels, dates).
Expand All @@ -272,6 +274,7 @@ Right-align (`'end'`) whenever the cell value is fundamentally quantitative—nu
- `badgeFields`: a list of field's `id` to render without label and styled as badges.
- `density`: one of `comfortable`, `balanced`, or `compact`. Configures the gap between items in the grid.
- `previewSize`: a `number` representing the size of the preview.
- `aspectRatio` (`grid` only): one of the preset ratios `'1/1'`, `'4/3'`, `'3/4'`, `'3/2'`, `'2/3'`, `'16/9'`, or `'9/16'`, applied uniformly to every item preview, keeping rows aligned. Defaults to `'1/1'`.

`list` layout:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@
* External dependencies
*/
import clsx from 'clsx';
import type { ComponentProps, ReactElement, HTMLAttributes } from 'react';
import type {
ComponentProps,
ReactElement,
HTMLAttributes,
CSSProperties,
} from 'react';

/**
* WordPress dependencies
Expand All @@ -27,6 +32,7 @@ import {
* Internal dependencies
*/
import { unlock } from '../../../lock-unlock';
import { MEDIA_ASPECT_RATIOS } from '../../../constants';
import ItemActions from '../../dataviews-item-actions';
import DataViewsSelectionCheckbox from '../../dataviews-selection-checkbox';
import DataViewsContext from '../../dataviews-context';
Expand Down Expand Up @@ -364,6 +370,19 @@ export default function CompositeGrid< Item >( {
const { paginationInfo, resizeObserverRef } =
useContext( DataViewsContext );
const gridColumns = useGridColumns();
// Consumer-configured aspect ratio for item previews, validated against
// the presets (like `density`) so arbitrary values are ignored, and
// surfaced to CSS as a custom property the media field's stylesheet
// reads. Always set (with the square default), so an identically-named
// variable set by a consumer on an ancestor can't leak into the previews
// when the view doesn't configure a ratio.
const gridStyle = {
'--wp-dataviews-media-aspect-ratio':
view.layout?.aspectRatio &&
MEDIA_ASPECT_RATIOS.includes( view.layout.aspectRatio )
? view.layout.aspectRatio
: '1/1',
} as CSSProperties;
const hasBulkActions = useSomeItemHasAPossibleBulkAction( actions, data );
const titleField = fields.find(
( field ) => field.id === view?.titleField
Expand Down Expand Up @@ -432,6 +451,7 @@ export default function CompositeGrid< Item >( {
}
) }
previewSize={ view.layout?.previewSize }
style={ gridStyle }
aria-busy={ isLoading }
ref={ resizeObserverRef }
/>
Expand Down Expand Up @@ -523,6 +543,7 @@ export default function CompositeGrid< Item >( {
! isInfiniteScroll && (
<Composite
role="grid"
style={ gridStyle }
className={ clsx( 'dataviews-view-grid', className, {
[ `has-${ view.layout?.density }-density` ]:
view.layout?.density &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@

.dataviews-view-grid__media {
width: 100%;
aspect-ratio: 1/1;
aspect-ratio: var(--wp-dataviews-media-aspect-ratio, 1/1);
background-color: var(--wpds-color-background-surface-neutral-strong);
border-radius: var(--wpds-border-radius-md);
overflow: hidden;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@ import { Stack } from '@wordpress/ui';
/**
* Internal dependencies
*/
import type { NormalizedField } from '../../../types';
import type { MediaAspectRatio, NormalizedField } from '../../../types';
import { ItemClickWrapper } from '../utils/item-click-wrapper';

function ColumnPrimary< Item >( {
item,
level,
titleField,
mediaField,
mediaAspectRatio,
descriptionField,
onClickItem,
renderItemLink,
Expand All @@ -28,6 +29,7 @@ function ColumnPrimary< Item >( {
level?: number;
titleField?: NormalizedField< Item >;
mediaField?: NormalizedField< Item >;
mediaAspectRatio?: MediaAspectRatio;
descriptionField?: NormalizedField< Item >;
onClickItem?: ( item: Item ) => void;
renderItemLink?: (
Expand All @@ -37,6 +39,21 @@ function ColumnPrimary< Item >( {
) => ReactElement;
isItemClickable: ( item: Item ) => boolean;
} ) {
// Srcset/size hint for the media render. The preview box is 32px square
// by default; when the view configures `layout.aspectRatio`, the box
// keeps the 32px height while the ratio derives its width, clamped by
// the stylesheet's 60px `max-width`, so widen the hint to match the
// rendered size and avoid picking an undersized (blurry) source.
let mediaSizes = '32px';
if ( mediaAspectRatio ) {
const [ ratioWidth, ratioHeight ] = mediaAspectRatio
.split( '/' )
.map( Number );
mediaSizes = `${ Math.min(
60,
Math.round( ( 32 * ratioWidth ) / ratioHeight )
) }px`;
}
return (
<Stack direction="row" gap="md" align="flex-start" justify="flex-start">
{ mediaField && (
Expand All @@ -57,7 +74,7 @@ function ColumnPrimary< Item >( {
<mediaField.render
item={ item }
field={ mediaField }
config={ { sizes: '32px' } }
config={ { sizes: mediaSizes } }
/>
</ItemClickWrapper>
) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* External dependencies
*/
import clsx from 'clsx';
import type { ComponentProps, ReactElement } from 'react';
import type { ComponentProps, CSSProperties, ReactElement } from 'react';

/**
* WordPress dependencies
Expand All @@ -24,7 +24,7 @@ import { isAppleOS } from '@wordpress/keycodes';
import DataViewsContext from '../../dataviews-context';
import DataViewsSelectionCheckbox from '../../dataviews-selection-checkbox';
import ItemActions from '../../dataviews-item-actions';
import { sortValues } from '../../../constants';
import { MEDIA_ASPECT_RATIOS, sortValues } from '../../../constants';
import {
useSomeItemHasAPossibleBulkAction,
useHasAPossibleBulkAction,
Expand All @@ -33,6 +33,7 @@ import {
} from '../../dataviews-bulk-actions';
import type {
Action,
MediaAspectRatio,
NormalizedField,
ViewTable as ViewTableType,
ViewTableProps,
Expand Down Expand Up @@ -76,6 +77,7 @@ interface TableRowProps< Item > {
view: ViewTableType;
titleField?: NormalizedField< Item >;
mediaField?: NormalizedField< Item >;
mediaAspectRatio?: MediaAspectRatio;
descriptionField?: NormalizedField< Item >;
selection: string[];
getItemId: ( item: Item ) => string;
Expand Down Expand Up @@ -127,6 +129,7 @@ function TableRow< Item >( {
view,
titleField,
mediaField,
mediaAspectRatio,
descriptionField,
selection,
getItemId,
Expand Down Expand Up @@ -204,6 +207,7 @@ function TableRow< Item >( {
level={ level }
titleField={ showTitle ? titleField : undefined }
mediaField={ showMedia ? mediaField : undefined }
mediaAspectRatio={ mediaAspectRatio }
descriptionField={
showDescription ? descriptionField : undefined
}
Expand Down Expand Up @@ -390,6 +394,21 @@ function ViewTable< Item >( {
};
const isInfiniteScroll = view.infiniteScrollEnabled && ! dataByGroup;
const isRtl = isRTL();
// Consumer-configured aspect ratio for the primary column's media preview,
// validated against the presets (like `density`) so arbitrary values are
// ignored, and surfaced to CSS as a custom property the media stylesheet
// reads. The property is always set (with the square default), so an
// identically-named variable set by a consumer on an ancestor can't leak
// into the previews when the view doesn't configure a ratio. The sizing
// itself only engages behind the `has-media-aspect-ratio` modifier below.
const mediaAspectRatio =
view.layout?.aspectRatio &&
MEDIA_ASPECT_RATIOS.includes( view.layout.aspectRatio )
? view.layout.aspectRatio
: undefined;
const tableStyle = {
'--wp-dataviews-media-aspect-ratio': mediaAspectRatio ?? '1/1',
} as CSSProperties;
Comment thread
ntsekouras marked this conversation as resolved.
if ( ! hasData ) {
return (
<div
Expand All @@ -414,7 +433,9 @@ function ViewTable< Item >( {
),
'has-bulk-actions': hasBulkActions,
'is-refreshing': ! isInfiniteScroll && isDelayedLoading,
'has-media-aspect-ratio': !! mediaAspectRatio,
} ) }
style={ tableStyle }
aria-busy={ isLoading }
aria-describedby={ tableNoticeId }
role={ isInfiniteScroll ? 'feed' : undefined }
Expand Down Expand Up @@ -618,6 +639,9 @@ function ViewTable< Item >( {
view={ view }
titleField={ titleField }
mediaField={ mediaField }
mediaAspectRatio={
mediaAspectRatio
}
descriptionField={
descriptionField
}
Expand Down Expand Up @@ -662,6 +686,7 @@ function ViewTable< Item >( {
view={ view }
titleField={ titleField }
mediaField={ mediaField }
mediaAspectRatio={ mediaAspectRatio }
descriptionField={ descriptionField }
selection={ selection }
getItemId={ getItemId }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,27 @@
}
}

// Only when the view configures `layout.aspectRatio` (the table then carries
// the `has-media-aspect-ratio` modifier): the preview box takes a fixed
// height while the configured ratio sets its width, keeping rows uniform.
// Sized on the wrapper rather than the img so custom media field renders are
// covered too; ratios wider than ~17/9 at this height are clipped by the base
// rule's `max-width` rather than allowed to crowd out the title. Views
// without `aspectRatio` keep the base sizing above, unchanged.
.dataviews-view-table.has-media-aspect-ratio .dataviews-column-primary__media {
height: var(--wpds-dimension-size-md);
Comment thread
ntsekouras marked this conversation as resolved.
aspect-ratio: var(--wp-dataviews-media-aspect-ratio, 1/1);
// Release the base rule's `min-width`: it equals the fixed height, so for
// ratios narrower than 1/1 the ratio-derived width would fall below it
// and the minimum would win, forcing the box back to a square.
min-width: 0;

img {
width: 100%;
height: 100%;
}
}

.dataviews-view-table__cell-content-wrapper,
.dataviews-view-table__primary-column-content {
&:not(.dataviews-column-primary__media) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export const GridItems = forwardRef<
className?: string;
previewSize: number | undefined;
} & ComponentPropsWithoutRef< 'div' >
>( ( { className, previewSize, ...props }, ref ) => {
>( ( { className, previewSize, style, ...props }, ref ) => {
return (
<div
ref={ ref }
Expand All @@ -28,6 +28,7 @@ export const GridItems = forwardRef<
gridTemplateColumns:
previewSize &&
`repeat(auto-fill, minmax(${ previewSize }px, 1fr))`,
...style,
} }
{ ...props }
/>
Expand Down
14 changes: 14 additions & 0 deletions packages/dataviews/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,17 @@ export const LAYOUT_PICKER_TABLE = 'pickerTable';
export const LAYOUT_PICKER_ACTIVITY = 'pickerActivity';

export const DAYS_OF_WEEK: DayNumber[] = [ 0, 1, 2, 3, 4, 5, 6 ];

// The preset aspect ratios available for item media previews. Source of
// truth for the `MediaAspectRatio` type (derived from this array), and used
// by layouts to validate the configured `layout.aspectRatio` before
// applying it.
export const MEDIA_ASPECT_RATIOS = [
'1/1',
'4/3',
'3/4',
'3/2',
'2/3',
'16/9',
'9/16',
] as const;
23 changes: 23 additions & 0 deletions packages/dataviews/src/types/dataviews.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import type {
SortDirection,
} from './field-api';
import type { SetSelection } from './private';
import type { MEDIA_ASPECT_RATIOS } from '../constants';

/**
* The filters applied to the dataset.
Expand Down Expand Up @@ -236,6 +237,14 @@ export interface ColumnStyle {

export type Density = 'compact' | 'balanced' | 'comfortable';

/**
* The preset aspect ratios available for item media previews, mirroring
* Core's default `aspect-ratio` presets. Derived from the
* `MEDIA_ASPECT_RATIOS` constant, which layouts also use to validate the
* configured value at runtime, so the two can't drift apart.
*/
export type MediaAspectRatio = ( typeof MEDIA_ASPECT_RATIOS )[ number ];

export interface ViewTable extends ViewBase {
type: 'table';

Expand All @@ -254,6 +263,13 @@ export interface ViewTable extends ViewBase {
* Whether the view allows column moving.
*/
enableMoving?: boolean;

/**
* A fixed aspect ratio for the primary column's media preview, one of
* the preset ratios. Applied uniformly to every row. Defaults to
* `'1/1'`.
*/
aspectRatio?: MediaAspectRatio;
};
}

Expand Down Expand Up @@ -297,6 +313,13 @@ export interface ViewGrid extends ViewBase {
* The density of the grid layout.
*/
density?: Density;

/**
* A fixed aspect ratio for the grid item previews (the media field),
* one of the preset ratios. Applied uniformly to every item so rows
* stay aligned. Defaults to `'1/1'`.
*/
aspectRatio?: MediaAspectRatio;
};
}

Expand Down
Loading