Skip to content
Open
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
36 changes: 32 additions & 4 deletions src/process/render.php
Original file line number Diff line number Diff line change
Expand Up @@ -79,22 +79,50 @@ class="wp-block-process-blocks-process__item"
<?php
};

$wrapper_attributes = get_block_wrapper_attributes( [
$wrapper_options = [
'id' => $attributes['anchor'] ?? $process['id'],
'data-wp-interactive' => Process_Blocks\STORE_NAMESPACE,
'data-wp-context' => wp_json_encode( $context, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP ),
'data-wp-init' => 'callbacks.load',
'data-wp-watch' => 'callbacks.save',
'data-wp-class--is-all-complete' => 'state.isAllComplete',
] );
];

// Completion icons need both authored colours, including when their number is
// made transparent. Resolve presets through the native block colour style engine.
$background = isset( $attributes['backgroundColor'] )
? 'var:preset|color|' . $attributes['backgroundColor']
: ( $attributes['style']['color']['background'] ?? null );
$text = isset( $attributes['textColor'] )
? 'var:preset|color|' . $attributes['textColor']
: ( $attributes['style']['color']['text'] ?? null );
if ( $background || $text ) {
$styles = wp_style_engine_get_styles( [
'color' => [
'background' => $background,
'text' => $text,
],
] );
$colours = [];
if ( ! empty( $styles['declarations']['background-color'] ) ) {
$colours[] = '--process-blocks--surface:' . $styles['declarations']['background-color'];
}
if ( ! empty( $styles['declarations']['color'] ) ) {
$colours[] = '--process-blocks--accent:' . $styles['declarations']['color'];
}
// Filter raw declarations and terminate them before core appends its styles.
$wrapper_options['style'] = safecss_filter_attr( implode( ';', $colours ) ) . ';';
}
$wrapper_attributes = get_block_wrapper_attributes( $wrapper_options );
?>
<div <?php echo $wrapper_attributes; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>>
<?php if ( $show_summary ) : ?>
<nav
class="wp-block-process-blocks-process__summary"
aria-label="<?php esc_attr_e( 'Process steps', 'process-blocks' ); ?>"
tabindex="-1"
>
<div class="wp-block-process-blocks-process__summary-header">
<div class="wp-block-process-blocks-process__summary-header" hidden data-wp-bind--hidden="!context.isLoaded">
<p class="wp-block-process-blocks-process__count">
<?php echo $progress_count; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</p>
Expand Down Expand Up @@ -146,7 +174,7 @@ class="wp-block-process-blocks-process__checklist-section"
</nav>
<?php endif ?>

<div class="wp-block-process-blocks-process__bar">
<div class="wp-block-process-blocks-process__bar" hidden data-wp-bind--hidden="!context.isLoaded">
<div class="wp-block-process-blocks-process__bar-inner">
<div class="wp-block-process-blocks-process__status">
<span class="wp-block-process-blocks-process__status-label">
Expand Down
9 changes: 9 additions & 0 deletions src/process/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@
--process-blocks--scroll-offset: calc( var( --process-blocks--sticky-top ) + 6rem );
}

// Keep JavaScript-only controls hidden until the process has loaded.
.wp-block-process-blocks-process__summary-header[hidden],
.wp-block-process-blocks-process__bar[hidden],
.wp-block-process-blocks-step__toggle[hidden] {
display: none;
}

/*
* Summary checklist.
*/
Expand Down Expand Up @@ -384,6 +391,8 @@
}

.wp-block-process-blocks-step {
// Let wide inner blocks scroll inside the step instead of widening its grid track.
min-width: 0;
padding: 1.25em 1.5em;
border: 1px solid var( --process-blocks--border );
border-radius: var( --process-blocks--radius );
Expand Down
43 changes: 38 additions & 5 deletions src/process/view.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ import {
} from '@wordpress/interactivity';

/**
* Sort step indexes numerically.
* Deduplicate and sort step indexes numerically.
*
* @param {number[]} indexes Step indexes.
* @returns {number[]} Sorted indexes.
*/
const sortIndexes = indexes => [ ...indexes ].sort( ( a, b ) => a - b );
const sortIndexes = indexes => [ ...new Set( indexes ) ].sort( ( a, b ) => a - b );

/**
* Get the index of the current (first incomplete) step.
Expand Down Expand Up @@ -105,7 +105,9 @@ const goToStep = ( context, index ) => {
* @returns {number} Step index, or -1 if no step has been reached.
*/
const getViewingIndex = ( elements, bar ) => {
const barBottom = bar.getBoundingClientRect().bottom;
// On short screens the theme lets the bar scroll away. Track the viewport
// once it is off screen, rather than a line above the visible steps.
const barBottom = Math.max( 0, bar.getBoundingClientRect().bottom );
const line = barBottom + Math.min( 120, ( window.innerHeight - barBottom ) / 4 );
const tops = elements.map( element => element?.getBoundingClientRect().top ?? Infinity );

Expand Down Expand Up @@ -285,6 +287,7 @@ const { state } = store( 'process-blocks', {
*/
completeActive() {
const context = getContext();
const { ref } = getElement();
const index = state.activeIndex;
if ( index === -1 || context.completed.includes( index ) ) {
return;
Expand All @@ -295,6 +298,11 @@ const { state } = store( 'process-blocks', {
const next = getNextIncomplete( context, index );
if ( next === -1 ) {
context.announcement = state.i18n.finished;
// The completion button disappears; focus its replacement after rendering.
window.requestAnimationFrame( () => {
ref.closest( '.wp-block-process-blocks-process' )
?.querySelector( '.wp-block-process-blocks-process__restart' )?.focus( { preventScroll: true } );
} );
return;
}

Expand All @@ -307,9 +315,15 @@ const { state } = store( 'process-blocks', {
*/
uncompleteActive() {
const context = getContext();
const { ref } = getElement();
const index = state.activeIndex;
context.completed = context.completed.filter( completed => completed !== index );
context.announcement = format( state.i18n.incomplete, index + 1 );
// Undo is hidden when the action is incomplete. Keep focus in the step bar.
window.requestAnimationFrame( () => {
ref.closest( '.wp-block-process-blocks-process__bar' )
?.querySelector( '.wp-block-process-blocks-process__complete' )?.focus( { preventScroll: true } );
} );
},

/**
Expand Down Expand Up @@ -349,14 +363,18 @@ const { state } = store( 'process-blocks', {
*/
reset() {
const context = getContext();
const { ref } = getElement();
const process = ref.closest( '.wp-block-process-blocks-process' );
context.completed = [];
context.announcement = state.i18n.reset;

const { ref } = getElement();
ref.closest( '.wp-block-process-blocks-process' )?.scrollIntoView( {
process?.scrollIntoView( {
behavior: prefersReducedMotion() ? 'auto' : 'smooth',
block: 'start',
} );
// Reset becomes disabled and Start again disappears; return to the overview.
process?.querySelector( '.wp-block-process-blocks-process__summary, .wp-block-process-blocks-step' )
?.focus( { preventScroll: true } );
},
},
callbacks: {
Expand Down Expand Up @@ -385,11 +403,26 @@ const { state } = store( 'process-blocks', {
return;
}

// Native fragment navigation happens before the hydrated bar is measured.
// Align a linked step once the real clearance is available, without taking focus.
const linkedIndex = context.steps.findIndex( step => window.location.hash === `#${ step.id }` );
let linkedStep = linkedIndex === -1 ? null : document.getElementById( context.steps[ linkedIndex ].id );
if ( linkedStep ) {
context.viewing = linkedIndex;
}

// Track the sticky bar's height, so steps scroll clear of it (with
// a little breathing room).
const resizeObserver = new window.ResizeObserver( () => {
const offset = `calc( var( --process-blocks--sticky-top ) + ${ bar.offsetHeight + 16 }px )`;
ref.style.setProperty( '--process-blocks--scroll-offset', offset );
if ( linkedStep ) {
linkedStep.scrollIntoView( {
behavior: 'instant',
block: 'start',
} );
linkedStep = null;
}
} );
resizeObserver.observe( bar );

Expand Down
2 changes: 2 additions & 0 deletions src/step/render.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@
<button
type="button"
class="wp-block-process-blocks-step__toggle"
hidden
data-wp-bind--hidden="!context.isLoaded"
aria-pressed="false"
data-wp-bind--aria-pressed="state.isStepComplete"
data-wp-on--click="actions.toggleStep"
Expand Down
Loading