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/block-editor/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
- `BlockAlignmentControl`: Hide the control again when none of a block's alignments are available. Paragraph, Heading, List and other blocks that support only wide and full were showing a menu of unavailable options that could not change anything ([#83265](https://github.com/WordPress/gutenberg/pull/83265)).
- Inline images in rich text are selected by clicking them again. A block captures a drag that starts on its own image through the `dragstart` target instead of the `pointer-events: none` rule that also kept the image from any click ([#83370](https://github.com/WordPress/gutenberg/pull/83370)).
- `RichText`: The edit UI of a format follows the active object when the selection moves from one object straight to another, for example from one inline image to the next, so the popover shows the attributes of that object at its position ([#83370](https://github.com/WordPress/gutenberg/pull/83370)).
- `FontAppearanceControl`: List only the weights inside a variable font's range, reading both ends of the range as whole numbers or as the keywords `normal` and `bold` ([#83128](https://github.com/WordPress/gutenberg/pull/83128)).
- `LinkControl`: Only label pages as the front page or blog home. Posts, terms and media are separate tables and can share an id, so an unscoped search could label a term "Front page" ([#83082](https://github.com/WordPress/gutenberg/pull/83082)).
- `InnerBlocks`: Resolve a container's legacy layout markup (`inherit: true`, or a bare `contentSize` / `wideSize` with no `type`) to a constrained layout for its inner blocks, so they are offered the wide and full alignments. Previously only the container's styles honoured the legacy form, and the inner blocks resolved to the flow layout ([#82637](https://github.com/WordPress/gutenberg/pull/82637)).
- Block Patterns, Block Visibility, and Block Lock: Preserve the intended colors of icons converted to strokes. ([#82540](https://github.com/WordPress/gutenberg/pull/82540), [#82754](https://github.com/WordPress/gutenberg/pull/82754))
Expand Down
69 changes: 52 additions & 17 deletions packages/block-editor/src/utils/get-font-styles-and-weights.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,34 @@ import type {
CombinedStyleAndWeightOption,
} from './types';

/*
* The absolute keywords a `@font-face` weight may use. `lighter` and `bolder` are
* relative to the parent and are not allowed there, so they are not listed.
*/
const FONT_WEIGHT_KEYWORDS: Record< string, number | undefined > = {
normal: 400,
bold: 700,
};

function isValidWeight( weight: number | undefined ): weight is number {
return (
weight !== undefined &&
Number.isFinite( weight ) &&
weight >= 1 &&
weight <= 1000
);
}

/*
* Read one end of a `@font-face` weight range as a number, or undefined when it is
* neither a number nor a keyword the property accepts.
*/
function parseWeightValue( value: string ): number | undefined {
const token = value.trim().toLowerCase();
const weight = FONT_WEIGHT_KEYWORDS[ token ] ?? Number( token );
return isValidWeight( weight ) ? weight : undefined;
}

const FONT_STYLES = [
{
name: _x( 'Regular', 'font style' ),
Expand Down Expand Up @@ -83,23 +111,30 @@ export function getFontStylesAndWeights(
'string' === typeof face.fontWeight &&
/\s/.test( face.fontWeight.trim() )
) {
isVariableFont = true;

// Find font weight start and end values.
const [ startStr, endStr ] = face.fontWeight.split( ' ' );
const startValue = parseInt( startStr.slice( 0, 1 ) );
const endValue =
endStr === '1000' ? 10 : parseInt( endStr.slice( 0, 1 ) );

// Create font weight options for available variable weights.
for ( let i = startValue; i <= endValue; i++ ) {
const fontWeightValue = `${ i.toString() }00`;
if (
! fontWeights.some(
( weight ) => weight.value === fontWeightValue
)
) {
fontWeights.push( formatFontWeight( fontWeightValue ) );
// Read both ends, which may be keywords: "normal 900" is 400 to 900.
const [ startStr, endStr ] = face.fontWeight.trim().split( /\s+/ );
const start = parseWeightValue( startStr );
const end = parseWeightValue( endStr );

// A range this property cannot express is left to the face's own
// formatting below rather than offering weights nobody declared.
if ( start !== undefined && end !== undefined ) {
isVariableFont = true;

// Find the hundreds inside the range, e.g. 300 to 700 for "250 750".
const startValue = Math.ceil( start / 100 );
const endValue = Math.floor( end / 100 );

// Create font weight options for available variable weights.
for ( let i = startValue; i <= endValue; i++ ) {
const fontWeightValue = `${ i.toString() }00`;
if (
! fontWeights.some(
( weight ) => weight.value === fontWeightValue
)
) {
fontWeights.push( formatFontWeight( fontWeightValue ) );
}
}
}
}
Expand Down
107 changes: 107 additions & 0 deletions packages/block-editor/src/utils/test/get-font-styles-and-weights.js
Original file line number Diff line number Diff line change
Expand Up @@ -656,4 +656,111 @@ describe( 'getFontStylesAndWeights', () => {
isVariableFont: false,
} );
} );

describe( 'variable font weight ranges', () => {
const weightsFor = ( fontWeight ) =>
getFontStylesAndWeights( [
{
fontFamily: 'Example Variable',
fontStyle: 'normal',
fontWeight,
src: 'https://example.org/example.woff2',
},
] ).fontWeights.map( ( weight ) => weight.value );

it( 'lists the hundreds inside a range that starts between hundreds', () => {
expect( weightsFor( '250 750' ) ).toEqual( [
'300',
'400',
'500',
'600',
'700',
] );
} );

it( 'lists the hundreds inside a range that starts below 100', () => {
expect( weightsFor( '50 900' ) ).toEqual( [
'100',
'200',
'300',
'400',
'500',
'600',
'700',
'800',
'900',
] );
} );

it( 'lists the hundreds up to a range that ends between hundreds', () => {
expect( weightsFor( '100 950' ) ).toEqual( [
'100',
'200',
'300',
'400',
'500',
'600',
'700',
'800',
'900',
] );
} );

it( 'reads the absolute keywords a range may use', () => {
expect( weightsFor( 'normal 900' ) ).toEqual( [
'400',
'500',
'600',
'700',
'800',
'900',
] );
expect( weightsFor( '100 bold' ) ).toEqual( [
'100',
'200',
'300',
'400',
'500',
'600',
'700',
] );
expect( weightsFor( 'NORMAL BOLD' ) ).toEqual( [
'400',
'500',
'600',
'700',
] );
} );

it( 'offers no weights for a range it cannot read, and does not call the face variable', () => {
const result = getFontStylesAndWeights( [
{
fontFamily: 'Example Variable',
fontStyle: 'normal',
fontWeight: 'lighter bolder',
src: 'https://example.org/example.woff2',
},
] );

expect( result.isVariableFont ).toBe( false );
expect(
result.fontWeights.map( ( weight ) => weight.value )
).toEqual( [ 'lighter bolder' ] );
} );

it( 'includes 1000 when the range reaches it', () => {
expect( weightsFor( '100 1000' ) ).toEqual( [
'100',
'200',
'300',
'400',
'500',
'600',
'700',
'800',
'900',
'1000',
] );
} );
} );
} );
Loading