From 898f29da29c37573b4014cbb7acd569ff2c5ea9c Mon Sep 17 00:00:00 2001 From: Dani Guardiola Date: Mon, 8 Apr 2024 12:18:15 +0200 Subject: [PATCH 01/23] Initial tab indicator animation implementation --- packages/components/src/tabs/styles.ts | 47 ++++++++++-------------- packages/components/src/tabs/tablist.tsx | 32 ++++++++++++++-- 2 files changed, 48 insertions(+), 31 deletions(-) diff --git a/packages/components/src/tabs/styles.ts b/packages/components/src/tabs/styles.ts index 6abca79aa51ae..ea8aaa7e2647a 100644 --- a/packages/components/src/tabs/styles.ts +++ b/packages/components/src/tabs/styles.ts @@ -13,12 +13,31 @@ import { space } from '../utils/space'; import { reduceMotion } from '../utils/reduce-motion'; export const TabListWrapper = styled.div` + position: relative; display: flex; align-items: stretch; flex-direction: row; &[aria-orientation='vertical'] { flex-direction: column; } + &::after { + content: ''; + position: absolute; + left: var( --indicator-left ); + bottom: 0; + width: var( --indicator-width ); + height: 0; + border-bottom: var( --wp-admin-border-width-focus ) solid + ${ COLORS.theme.accent }; + pointer-events: none; + @media not ( prefers-reduced-motion: reduce ) { + transition: left 0.2s ease-out; + } + + // Windows high contrast mode. + outline: 2px solid transparent; + outline-offset: -1px; + } `; export const Tab = styled( Ariakit.Tab )` @@ -51,34 +70,6 @@ export const Tab = styled( Ariakit.Tab )` outline: none; } - // Tab indicator - &::after { - content: ''; - position: absolute; - right: 0; - bottom: 0; - left: 0; - pointer-events: none; - - // Draw the indicator. - background: ${ COLORS.theme.accent }; - height: calc( 0 * var( --wp-admin-border-width-focus ) ); - border-radius: 0; - - // Animation - transition: all 0.1s linear; - ${ reduceMotion( 'transition' ) }; - } - - // Active. - &[aria-selected='true']::after { - height: calc( 1 * var( --wp-admin-border-width-focus ) ); - - // Windows high contrast mode. - outline: 2px solid transparent; - outline-offset: -1px; - } - // Focus. &::before { content: ''; diff --git a/packages/components/src/tabs/tablist.tsx b/packages/components/src/tabs/tablist.tsx index afa2d8283e6d6..d33adf5981864 100644 --- a/packages/components/src/tabs/tablist.tsx +++ b/packages/components/src/tabs/tablist.tsx @@ -8,7 +8,7 @@ import * as Ariakit from '@ariakit/react'; * WordPress dependencies */ import warning from '@wordpress/warning'; -import { forwardRef } from '@wordpress/element'; +import { forwardRef, useEffect, useState } from '@wordpress/element'; /** * Internal dependencies @@ -17,19 +17,36 @@ import type { TabListProps } from './types'; import { useTabsContext } from './context'; import { TabListWrapper } from './styles'; import type { WordPressComponentProps } from '../context'; +import type { CSSProperties } from 'react'; export const TabList = forwardRef< HTMLDivElement, WordPressComponentProps< TabListProps, 'div', false > >( function TabList( { children, ...otherProps }, ref ) { const context = useTabsContext(); + + const [ indicatorPosition, setIndicatorPosition ] = useState( { + left: 0, + width: 0, + } ); + const selectedId = context?.store.useState( 'selectedId' ); + const selectedTabEl = context?.store.item( selectedId )?.element; + + useEffect( () => { + if ( selectedTabEl ) + setIndicatorPosition( { + left: selectedTabEl.offsetLeft, + width: selectedTabEl.getBoundingClientRect().width, + } ); + }, [ selectedTabEl ] ); + if ( ! context ) { warning( '`Tabs.TabList` must be wrapped in a `Tabs` component.' ); return null; } const { store } = context; - const { selectedId, activeId, selectOnMove } = store.useState(); + const { activeId, selectOnMove } = store.useState(); const { setActiveId } = store; const onBlur = () => { @@ -50,7 +67,16 @@ export const TabList = forwardRef< } + render={ + + } onBlur={ onBlur } { ...otherProps } > From ce628d3d3273469cf6e550df702c8135c19842f9 Mon Sep 17 00:00:00 2001 From: Dani Guardiola Date: Mon, 8 Apr 2024 12:27:12 +0200 Subject: [PATCH 02/23] Add changelog entry --- packages/components/CHANGELOG.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 1e1ed19c12224..8cc5deae43a34 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Enhancements + +- `Tabs`: Animate indicator ([#60560](https://github.com/WordPress/gutenberg/pull/60560)). + ## 27.3.0 (2024-04-03) ### Bug Fix @@ -1072,7 +1076,7 @@ ### Enhancements -- `FontSizePicker`: Updated to take up full width of its parent and have a 40px Reset button when `size` is `__unstable-large` ((44559)[https://github.com/WordPress/gutenberg/pull/44559]). +- `FontSizePicker`: Updated to take up full width of its parent and have a 40px Reset button when `size` is `__unstable-large` ([44559](https://github.com/WordPress/gutenberg/pull/44559)). - `BorderBoxControl`: Omit unit select when width values are mixed ([#44592](https://github.com/WordPress/gutenberg/pull/44592)) - `BorderControl`: Add ability to disable unit selection ([#44592](https://github.com/WordPress/gutenberg/pull/44592)) @@ -1902,8 +1906,8 @@ ### Breaking Changes -- Drop support for Internet Explorer 11 ([#31110](https://github.com/WordPress/gutenberg/pull/31110)). Learn more at https://make.wordpress.org/core/2021/04/22/ie-11-support-phase-out-plan/. -- Increase the minimum Node.js version to v12 matching Long Term Support releases ([#31270](https://github.com/WordPress/gutenberg/pull/31270)). Learn more at https://nodejs.org/en/about/releases/. +- Drop support for Internet Explorer 11 ([#31110](https://github.com/WordPress/gutenberg/pull/31110)). Learn more at . +- Increase the minimum Node.js version to v12 matching Long Term Support releases ([#31270](https://github.com/WordPress/gutenberg/pull/31270)). Learn more at . - The experimental `Text` component has been completely re-written and enhanced with truncation support and separate variant, size, and weight props to allow for greater control. The previous `variant` prop has been completely removed. ### Deprecation @@ -2236,7 +2240,7 @@ - `withAPIData` has been removed. Please use the Core Data module or `@wordpress/api-fetch` directly instead. - `Draggable` as a DOM node drag handler has been deprecated. Please, use `Draggable` as a wrap component for your DOM node drag handler. - Change how required built-ins are polyfilled with Babel 7 ([#9171](https://github.com/WordPress/gutenberg/pull/9171)). If you're using an environment that has limited or no support for ES2015+ such as lower versions of IE then using [core-js](https://github.com/zloirock/core-js) or [@babel/polyfill](https://babeljs.io/docs/en/next/babel-polyfill) will add support for these methods. -- `withContext` has been removed. Please use `wp.element.createContext` instead. See: https://reactjs.org/docs/context.html. +- `withContext` has been removed. Please use `wp.element.createContext` instead. See: . ### New Feature From 7e43c02cef727fc9173c411db82393f41922d12b Mon Sep 17 00:00:00 2001 From: Dani Guardiola Date: Mon, 8 Apr 2024 12:29:12 +0200 Subject: [PATCH 03/23] Minor tweak --- packages/components/src/tabs/tablist.tsx | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/packages/components/src/tabs/tablist.tsx b/packages/components/src/tabs/tablist.tsx index d33adf5981864..1090547b12091 100644 --- a/packages/components/src/tabs/tablist.tsx +++ b/packages/components/src/tabs/tablist.tsx @@ -67,16 +67,13 @@ export const TabList = forwardRef< + style={ + { + '--indicator-left': `${ indicatorPosition.left }px`, + '--indicator-width': `${ indicatorPosition.width }px`, + } as CSSProperties } + render={ } onBlur={ onBlur } { ...otherProps } > From 0a66bbd217c45f73d6e7144d02aeaf3248fd207d Mon Sep 17 00:00:00 2001 From: Dani Guardiola Date: Fri, 12 Apr 2024 16:56:09 +0200 Subject: [PATCH 04/23] Fix downstream issues. --- .../src/components/preferences-modal-tabs/style.scss | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/preferences/src/components/preferences-modal-tabs/style.scss b/packages/preferences/src/components/preferences-modal-tabs/style.scss index d3afd4174cd0c..e20b9aa9064ed 100644 --- a/packages/preferences/src/components/preferences-modal-tabs/style.scss +++ b/packages/preferences/src/components/preferences-modal-tabs/style.scss @@ -1,12 +1,15 @@ $vertical-tabs-width: 160px; .preferences__tabs-tablist { - position: absolute; + position: absolute !important; top: $header-height + $grid-unit-30; // Aligns button text instead of button box. left: $grid-unit-20; width: $vertical-tabs-width; + &::after { + content: none !important; + } } .preferences__tabs-tab { @@ -19,10 +22,6 @@ $vertical-tabs-width: 160px; font-weight: 500; } - &[aria-selected="true"]::after { - content: none; - } - &[role="tab"]:focus:not(:disabled) { box-shadow: inset 0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color); // Windows high contrast mode. From 3dae51c9da6599754012eedf0ab178a4cf649ef2 Mon Sep 17 00:00:00 2001 From: Dani Guardiola Date: Fri, 12 Apr 2024 16:56:21 +0200 Subject: [PATCH 05/23] Use ResizeObserver. --- packages/components/src/tabs/tablist.tsx | 33 +++++++++++++++++++++--- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/packages/components/src/tabs/tablist.tsx b/packages/components/src/tabs/tablist.tsx index 1090547b12091..31161389929da 100644 --- a/packages/components/src/tabs/tablist.tsx +++ b/packages/components/src/tabs/tablist.tsx @@ -8,7 +8,7 @@ import * as Ariakit from '@ariakit/react'; * WordPress dependencies */ import warning from '@wordpress/warning'; -import { forwardRef, useEffect, useState } from '@wordpress/element'; +import { forwardRef, useEffect, useRef, useState } from '@wordpress/element'; /** * Internal dependencies @@ -31,13 +31,38 @@ export const TabList = forwardRef< } ); const selectedId = context?.store.useState( 'selectedId' ); const selectedTabEl = context?.store.item( selectedId )?.element; + const resizeObserverRef = useRef< ResizeObserver >(); + const observedElementRef = useRef< HTMLElement >(); useEffect( () => { - if ( selectedTabEl ) + if ( selectedTabEl === observedElementRef.current ) return; + observedElementRef.current = selectedTabEl ?? undefined; + + function updateIndicator( element: HTMLElement ) { setIndicatorPosition( { - left: selectedTabEl.offsetLeft, - width: selectedTabEl.getBoundingClientRect().width, + left: element.offsetLeft, + width: element.getBoundingClientRect().width, + } ); + } + + // Set up a ResizeObserver. + if ( ! resizeObserverRef.current ) { + resizeObserverRef.current = new ResizeObserver( () => { + if ( observedElementRef.current ) + updateIndicator( observedElementRef.current ); } ); + } + const { current: resizeObserver } = resizeObserverRef; + + // Unobserve previous element. + const { current: observedElement } = observedElementRef; + if ( observedElement ) resizeObserver.unobserve( observedElement ); + + // Observe new element. + if ( selectedTabEl ) { + updateIndicator( selectedTabEl ); + resizeObserver.observe( selectedTabEl ); + } }, [ selectedTabEl ] ); if ( ! context ) { From a0b058181d225dcfe08cb3e90553a3726090ec2f Mon Sep 17 00:00:00 2001 From: Dani Guardiola Date: Fri, 12 Apr 2024 16:56:43 +0200 Subject: [PATCH 06/23] Add width transition. --- packages/components/src/tabs/styles.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/components/src/tabs/styles.ts b/packages/components/src/tabs/styles.ts index ea8aaa7e2647a..430a8008a5ad6 100644 --- a/packages/components/src/tabs/styles.ts +++ b/packages/components/src/tabs/styles.ts @@ -31,7 +31,9 @@ export const TabListWrapper = styled.div` ${ COLORS.theme.accent }; pointer-events: none; @media not ( prefers-reduced-motion: reduce ) { - transition: left 0.2s ease-out; + transition-property: left width; + transition-duration: 0.2s; + transition-timing-function: ease-out; } // Windows high contrast mode. From a5d5d277aef4ce222dc49c424f2d03ab293140b1 Mon Sep 17 00:00:00 2001 From: Dani Guardiola Date: Wed, 17 Apr 2024 18:44:23 +0200 Subject: [PATCH 07/23] Simplify and use framer motion --- packages/components/src/tabs/styles.ts | 38 ++++++-------- packages/components/src/tabs/tab.tsx | 6 ++- packages/components/src/tabs/tablist.tsx | 52 +------------------ .../preferences-modal-tabs/style.scss | 9 ++-- 4 files changed, 29 insertions(+), 76 deletions(-) diff --git a/packages/components/src/tabs/styles.ts b/packages/components/src/tabs/styles.ts index 430a8008a5ad6..6b81ed32fc72e 100644 --- a/packages/components/src/tabs/styles.ts +++ b/packages/components/src/tabs/styles.ts @@ -11,39 +11,20 @@ import * as Ariakit from '@ariakit/react'; import { COLORS } from '../utils'; import { space } from '../utils/space'; import { reduceMotion } from '../utils/reduce-motion'; +import { __unstableMotion } from '../animation'; export const TabListWrapper = styled.div` - position: relative; display: flex; align-items: stretch; flex-direction: row; &[aria-orientation='vertical'] { flex-direction: column; } - &::after { - content: ''; - position: absolute; - left: var( --indicator-left ); - bottom: 0; - width: var( --indicator-width ); - height: 0; - border-bottom: var( --wp-admin-border-width-focus ) solid - ${ COLORS.theme.accent }; - pointer-events: none; - @media not ( prefers-reduced-motion: reduce ) { - transition-property: left width; - transition-duration: 0.2s; - transition-timing-function: ease-out; - } - - // Windows high contrast mode. - outline: 2px solid transparent; - outline-offset: -1px; - } `; export const Tab = styled( Ariakit.Tab )` & { + isolation: isolate; display: inline-flex; align-items: center; position: relative; @@ -101,6 +82,21 @@ export const Tab = styled( Ariakit.Tab )` } `; +export const TabIndicator = styled( __unstableMotion.div )` + position: absolute; + left: 0; + bottom: 0; + width: 100%; + height: 0; + border-bottom: var( --wp-admin-border-width-focus ) solid + ${ COLORS.theme.accent }; + pointer-events: none; + + // Windows high contrast mode. + outline: 2px solid transparent; + outline-offset: -1px; +`; + export const TabPanel = styled( Ariakit.TabPanel )` &:focus { box-shadow: none; diff --git a/packages/components/src/tabs/tab.tsx b/packages/components/src/tabs/tab.tsx index e1aa85c636cdd..281d62ee4d191 100644 --- a/packages/components/src/tabs/tab.tsx +++ b/packages/components/src/tabs/tab.tsx @@ -10,7 +10,7 @@ import { forwardRef } from '@wordpress/element'; import type { TabProps } from './types'; import warning from '@wordpress/warning'; import { useTabsContext } from './context'; -import { Tab as StyledTab } from './styles'; +import { Tab as StyledTab, TabIndicator } from './styles'; import type { WordPressComponentProps } from '../context'; export const Tab = forwardRef< @@ -18,6 +18,7 @@ export const Tab = forwardRef< Omit< WordPressComponentProps< TabProps, 'button', false >, 'id' > >( function Tab( { children, tabId, disabled, render, ...otherProps }, ref ) { const context = useTabsContext(); + const activeId = context?.store.useState( 'activeId' ); if ( ! context ) { warning( '`Tabs.Tab` must be wrapped in a `Tabs` component.' ); return null; @@ -34,6 +35,9 @@ export const Tab = forwardRef< { ...otherProps } > { children } + { activeId === instancedTabId && ( + + ) } ); } ); diff --git a/packages/components/src/tabs/tablist.tsx b/packages/components/src/tabs/tablist.tsx index 31161389929da..afa2d8283e6d6 100644 --- a/packages/components/src/tabs/tablist.tsx +++ b/packages/components/src/tabs/tablist.tsx @@ -8,7 +8,7 @@ import * as Ariakit from '@ariakit/react'; * WordPress dependencies */ import warning from '@wordpress/warning'; -import { forwardRef, useEffect, useRef, useState } from '@wordpress/element'; +import { forwardRef } from '@wordpress/element'; /** * Internal dependencies @@ -17,61 +17,19 @@ import type { TabListProps } from './types'; import { useTabsContext } from './context'; import { TabListWrapper } from './styles'; import type { WordPressComponentProps } from '../context'; -import type { CSSProperties } from 'react'; export const TabList = forwardRef< HTMLDivElement, WordPressComponentProps< TabListProps, 'div', false > >( function TabList( { children, ...otherProps }, ref ) { const context = useTabsContext(); - - const [ indicatorPosition, setIndicatorPosition ] = useState( { - left: 0, - width: 0, - } ); - const selectedId = context?.store.useState( 'selectedId' ); - const selectedTabEl = context?.store.item( selectedId )?.element; - const resizeObserverRef = useRef< ResizeObserver >(); - const observedElementRef = useRef< HTMLElement >(); - - useEffect( () => { - if ( selectedTabEl === observedElementRef.current ) return; - observedElementRef.current = selectedTabEl ?? undefined; - - function updateIndicator( element: HTMLElement ) { - setIndicatorPosition( { - left: element.offsetLeft, - width: element.getBoundingClientRect().width, - } ); - } - - // Set up a ResizeObserver. - if ( ! resizeObserverRef.current ) { - resizeObserverRef.current = new ResizeObserver( () => { - if ( observedElementRef.current ) - updateIndicator( observedElementRef.current ); - } ); - } - const { current: resizeObserver } = resizeObserverRef; - - // Unobserve previous element. - const { current: observedElement } = observedElementRef; - if ( observedElement ) resizeObserver.unobserve( observedElement ); - - // Observe new element. - if ( selectedTabEl ) { - updateIndicator( selectedTabEl ); - resizeObserver.observe( selectedTabEl ); - } - }, [ selectedTabEl ] ); - if ( ! context ) { warning( '`Tabs.TabList` must be wrapped in a `Tabs` component.' ); return null; } const { store } = context; - const { activeId, selectOnMove } = store.useState(); + const { selectedId, activeId, selectOnMove } = store.useState(); const { setActiveId } = store; const onBlur = () => { @@ -92,12 +50,6 @@ export const TabList = forwardRef< } onBlur={ onBlur } { ...otherProps } diff --git a/packages/preferences/src/components/preferences-modal-tabs/style.scss b/packages/preferences/src/components/preferences-modal-tabs/style.scss index e20b9aa9064ed..d3afd4174cd0c 100644 --- a/packages/preferences/src/components/preferences-modal-tabs/style.scss +++ b/packages/preferences/src/components/preferences-modal-tabs/style.scss @@ -1,15 +1,12 @@ $vertical-tabs-width: 160px; .preferences__tabs-tablist { - position: absolute !important; + position: absolute; top: $header-height + $grid-unit-30; // Aligns button text instead of button box. left: $grid-unit-20; width: $vertical-tabs-width; - &::after { - content: none !important; - } } .preferences__tabs-tab { @@ -22,6 +19,10 @@ $vertical-tabs-width: 160px; font-weight: 500; } + &[aria-selected="true"]::after { + content: none; + } + &[role="tab"]:focus:not(:disabled) { box-shadow: inset 0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color); // Windows high contrast mode. From 22909d8f91ef0e517b8220bbc5a24d6653dc47af Mon Sep 17 00:00:00 2001 From: Dani Guardiola Date: Wed, 17 Apr 2024 19:22:28 +0200 Subject: [PATCH 08/23] vertical indicator --- packages/components/src/tabs/styles.ts | 23 +++++++++++++++++------ packages/components/src/tabs/tab.tsx | 8 +++++++- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/packages/components/src/tabs/styles.ts b/packages/components/src/tabs/styles.ts index 6b81ed32fc72e..f4363b4d4461d 100644 --- a/packages/components/src/tabs/styles.ts +++ b/packages/components/src/tabs/styles.ts @@ -84,14 +84,25 @@ export const Tab = styled( Ariakit.Tab )` export const TabIndicator = styled( __unstableMotion.div )` position: absolute; - left: 0; - bottom: 0; - width: 100%; - height: 0; - border-bottom: var( --wp-admin-border-width-focus ) solid - ${ COLORS.theme.accent }; pointer-events: none; + &:not( .is-vertical ) { + left: 0; + bottom: 0; + width: 100%; + border-bottom: var( --wp-admin-border-width-focus ) solid + ${ COLORS.theme.accent }; + } + + &.is-vertical { + right: 0; + top: 0; + height: 100%; + width: 0; + border-left: var( --wp-admin-border-width-focus ) solid + ${ COLORS.theme.accent }; + } + // Windows high contrast mode. outline: 2px solid transparent; outline-offset: -1px; diff --git a/packages/components/src/tabs/tab.tsx b/packages/components/src/tabs/tab.tsx index 281d62ee4d191..7d76740eccfd8 100644 --- a/packages/components/src/tabs/tab.tsx +++ b/packages/components/src/tabs/tab.tsx @@ -19,6 +19,7 @@ export const Tab = forwardRef< >( function Tab( { children, tabId, disabled, render, ...otherProps }, ref ) { const context = useTabsContext(); const activeId = context?.store.useState( 'activeId' ); + const orientation = context?.store.useState( 'orientation' ); if ( ! context ) { warning( '`Tabs.Tab` must be wrapped in a `Tabs` component.' ); return null; @@ -36,7 +37,12 @@ export const Tab = forwardRef< > { children } { activeId === instancedTabId && ( - + ) } ); From 77d208fdca291589b84d3450df8486054ede9ab7 Mon Sep 17 00:00:00 2001 From: Dani Guardiola Date: Tue, 23 Apr 2024 00:47:07 +0200 Subject: [PATCH 09/23] Revert to previous implementation. --- packages/components/src/tabs/styles.ts | 49 ++++++++--------- packages/components/src/tabs/tab.tsx | 12 +---- packages/components/src/tabs/tablist.tsx | 52 ++++++++++++++++++- .../preferences-modal-tabs/style.scss | 9 ++-- 4 files changed, 76 insertions(+), 46 deletions(-) diff --git a/packages/components/src/tabs/styles.ts b/packages/components/src/tabs/styles.ts index f4363b4d4461d..430a8008a5ad6 100644 --- a/packages/components/src/tabs/styles.ts +++ b/packages/components/src/tabs/styles.ts @@ -11,20 +11,39 @@ import * as Ariakit from '@ariakit/react'; import { COLORS } from '../utils'; import { space } from '../utils/space'; import { reduceMotion } from '../utils/reduce-motion'; -import { __unstableMotion } from '../animation'; export const TabListWrapper = styled.div` + position: relative; display: flex; align-items: stretch; flex-direction: row; &[aria-orientation='vertical'] { flex-direction: column; } + &::after { + content: ''; + position: absolute; + left: var( --indicator-left ); + bottom: 0; + width: var( --indicator-width ); + height: 0; + border-bottom: var( --wp-admin-border-width-focus ) solid + ${ COLORS.theme.accent }; + pointer-events: none; + @media not ( prefers-reduced-motion: reduce ) { + transition-property: left width; + transition-duration: 0.2s; + transition-timing-function: ease-out; + } + + // Windows high contrast mode. + outline: 2px solid transparent; + outline-offset: -1px; + } `; export const Tab = styled( Ariakit.Tab )` & { - isolation: isolate; display: inline-flex; align-items: center; position: relative; @@ -82,32 +101,6 @@ export const Tab = styled( Ariakit.Tab )` } `; -export const TabIndicator = styled( __unstableMotion.div )` - position: absolute; - pointer-events: none; - - &:not( .is-vertical ) { - left: 0; - bottom: 0; - width: 100%; - border-bottom: var( --wp-admin-border-width-focus ) solid - ${ COLORS.theme.accent }; - } - - &.is-vertical { - right: 0; - top: 0; - height: 100%; - width: 0; - border-left: var( --wp-admin-border-width-focus ) solid - ${ COLORS.theme.accent }; - } - - // Windows high contrast mode. - outline: 2px solid transparent; - outline-offset: -1px; -`; - export const TabPanel = styled( Ariakit.TabPanel )` &:focus { box-shadow: none; diff --git a/packages/components/src/tabs/tab.tsx b/packages/components/src/tabs/tab.tsx index 7d76740eccfd8..e1aa85c636cdd 100644 --- a/packages/components/src/tabs/tab.tsx +++ b/packages/components/src/tabs/tab.tsx @@ -10,7 +10,7 @@ import { forwardRef } from '@wordpress/element'; import type { TabProps } from './types'; import warning from '@wordpress/warning'; import { useTabsContext } from './context'; -import { Tab as StyledTab, TabIndicator } from './styles'; +import { Tab as StyledTab } from './styles'; import type { WordPressComponentProps } from '../context'; export const Tab = forwardRef< @@ -18,8 +18,6 @@ export const Tab = forwardRef< Omit< WordPressComponentProps< TabProps, 'button', false >, 'id' > >( function Tab( { children, tabId, disabled, render, ...otherProps }, ref ) { const context = useTabsContext(); - const activeId = context?.store.useState( 'activeId' ); - const orientation = context?.store.useState( 'orientation' ); if ( ! context ) { warning( '`Tabs.Tab` must be wrapped in a `Tabs` component.' ); return null; @@ -36,14 +34,6 @@ export const Tab = forwardRef< { ...otherProps } > { children } - { activeId === instancedTabId && ( - - ) } ); } ); diff --git a/packages/components/src/tabs/tablist.tsx b/packages/components/src/tabs/tablist.tsx index afa2d8283e6d6..31161389929da 100644 --- a/packages/components/src/tabs/tablist.tsx +++ b/packages/components/src/tabs/tablist.tsx @@ -8,7 +8,7 @@ import * as Ariakit from '@ariakit/react'; * WordPress dependencies */ import warning from '@wordpress/warning'; -import { forwardRef } from '@wordpress/element'; +import { forwardRef, useEffect, useRef, useState } from '@wordpress/element'; /** * Internal dependencies @@ -17,19 +17,61 @@ import type { TabListProps } from './types'; import { useTabsContext } from './context'; import { TabListWrapper } from './styles'; import type { WordPressComponentProps } from '../context'; +import type { CSSProperties } from 'react'; export const TabList = forwardRef< HTMLDivElement, WordPressComponentProps< TabListProps, 'div', false > >( function TabList( { children, ...otherProps }, ref ) { const context = useTabsContext(); + + const [ indicatorPosition, setIndicatorPosition ] = useState( { + left: 0, + width: 0, + } ); + const selectedId = context?.store.useState( 'selectedId' ); + const selectedTabEl = context?.store.item( selectedId )?.element; + const resizeObserverRef = useRef< ResizeObserver >(); + const observedElementRef = useRef< HTMLElement >(); + + useEffect( () => { + if ( selectedTabEl === observedElementRef.current ) return; + observedElementRef.current = selectedTabEl ?? undefined; + + function updateIndicator( element: HTMLElement ) { + setIndicatorPosition( { + left: element.offsetLeft, + width: element.getBoundingClientRect().width, + } ); + } + + // Set up a ResizeObserver. + if ( ! resizeObserverRef.current ) { + resizeObserverRef.current = new ResizeObserver( () => { + if ( observedElementRef.current ) + updateIndicator( observedElementRef.current ); + } ); + } + const { current: resizeObserver } = resizeObserverRef; + + // Unobserve previous element. + const { current: observedElement } = observedElementRef; + if ( observedElement ) resizeObserver.unobserve( observedElement ); + + // Observe new element. + if ( selectedTabEl ) { + updateIndicator( selectedTabEl ); + resizeObserver.observe( selectedTabEl ); + } + }, [ selectedTabEl ] ); + if ( ! context ) { warning( '`Tabs.TabList` must be wrapped in a `Tabs` component.' ); return null; } const { store } = context; - const { selectedId, activeId, selectOnMove } = store.useState(); + const { activeId, selectOnMove } = store.useState(); const { setActiveId } = store; const onBlur = () => { @@ -50,6 +92,12 @@ export const TabList = forwardRef< } onBlur={ onBlur } { ...otherProps } diff --git a/packages/preferences/src/components/preferences-modal-tabs/style.scss b/packages/preferences/src/components/preferences-modal-tabs/style.scss index d3afd4174cd0c..e20b9aa9064ed 100644 --- a/packages/preferences/src/components/preferences-modal-tabs/style.scss +++ b/packages/preferences/src/components/preferences-modal-tabs/style.scss @@ -1,12 +1,15 @@ $vertical-tabs-width: 160px; .preferences__tabs-tablist { - position: absolute; + position: absolute !important; top: $header-height + $grid-unit-30; // Aligns button text instead of button box. left: $grid-unit-20; width: $vertical-tabs-width; + &::after { + content: none !important; + } } .preferences__tabs-tab { @@ -19,10 +22,6 @@ $vertical-tabs-width: 160px; font-weight: 500; } - &[aria-selected="true"]::after { - content: none; - } - &[role="tab"]:focus:not(:disabled) { box-shadow: inset 0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color); // Windows high contrast mode. From e9ccc027c73eb871d2ed3121de4baf5bf48cfb61 Mon Sep 17 00:00:00 2001 From: Dani Guardiola Date: Tue, 23 Apr 2024 00:51:29 +0200 Subject: [PATCH 10/23] Fix bug due to some animations breaking measurement of the tab element. --- packages/components/src/tabs/tablist.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/src/tabs/tablist.tsx b/packages/components/src/tabs/tablist.tsx index 31161389929da..fe7e2fa3627cf 100644 --- a/packages/components/src/tabs/tablist.tsx +++ b/packages/components/src/tabs/tablist.tsx @@ -41,7 +41,7 @@ export const TabList = forwardRef< function updateIndicator( element: HTMLElement ) { setIndicatorPosition( { left: element.offsetLeft, - width: element.getBoundingClientRect().width, + width: element.offsetWidth, } ); } From 283f10952c38cf35c184042ed9e11134e2fd1d71 Mon Sep 17 00:00:00 2001 From: Dani Guardiola Date: Tue, 23 Apr 2024 01:55:16 +0200 Subject: [PATCH 11/23] Abstracted and fixed all previous issues. --- packages/components/src/tabs/styles.ts | 12 +-- packages/components/src/tabs/tablist.tsx | 95 +++++++++++++++++++----- 2 files changed, 84 insertions(+), 23 deletions(-) diff --git a/packages/components/src/tabs/styles.ts b/packages/components/src/tabs/styles.ts index 430a8008a5ad6..dd1a07a1f2695 100644 --- a/packages/components/src/tabs/styles.ts +++ b/packages/components/src/tabs/styles.ts @@ -20,6 +20,13 @@ export const TabListWrapper = styled.div` &[aria-orientation='vertical'] { flex-direction: column; } + &.isAnimationEnabled::after { + @media not ( prefers-reduced-motion: reduce ) { + transition-property: left width; + transition-duration: 0.2s; + transition-timing-function: ease-out; + } + } &::after { content: ''; position: absolute; @@ -30,11 +37,6 @@ export const TabListWrapper = styled.div` border-bottom: var( --wp-admin-border-width-focus ) solid ${ COLORS.theme.accent }; pointer-events: none; - @media not ( prefers-reduced-motion: reduce ) { - transition-property: left width; - transition-duration: 0.2s; - transition-timing-function: ease-out; - } // Windows high contrast mode. outline: 2px solid transparent; diff --git a/packages/components/src/tabs/tablist.tsx b/packages/components/src/tabs/tablist.tsx index fe7e2fa3627cf..605f86c61b1e1 100644 --- a/packages/components/src/tabs/tablist.tsx +++ b/packages/components/src/tabs/tablist.tsx @@ -8,7 +8,13 @@ import * as Ariakit from '@ariakit/react'; * WordPress dependencies */ import warning from '@wordpress/warning'; -import { forwardRef, useEffect, useRef, useState } from '@wordpress/element'; +import { + forwardRef, + useEffect, + useLayoutEffect, + useRef, + useState, +} from '@wordpress/element'; /** * Internal dependencies @@ -19,30 +25,37 @@ import { TabListWrapper } from './styles'; import type { WordPressComponentProps } from '../context'; import type { CSSProperties } from 'react'; -export const TabList = forwardRef< - HTMLDivElement, - WordPressComponentProps< TabListProps, 'div', false > ->( function TabList( { children, ...otherProps }, ref ) { - const context = useTabsContext(); - +function useTrackElementOffset( + targetElement?: HTMLElement | null, + onUpdate?: () => void +) { const [ indicatorPosition, setIndicatorPosition ] = useState( { left: 0, + top: 0, width: 0, + height: 0, + } ); + + // TODO: replace with useEventCallback or similar when officially available. + const updateCallbackRef = useRef( onUpdate ); + useLayoutEffect( () => { + updateCallbackRef.current = onUpdate; } ); - const selectedId = context?.store.useState( 'selectedId' ); - const selectedTabEl = context?.store.item( selectedId )?.element; - const resizeObserverRef = useRef< ResizeObserver >(); - const observedElementRef = useRef< HTMLElement >(); + const observedElementRef = useRef< HTMLElement >(); + const resizeObserverRef = useRef< ResizeObserver >(); useEffect( () => { - if ( selectedTabEl === observedElementRef.current ) return; - observedElementRef.current = selectedTabEl ?? undefined; + if ( targetElement === observedElementRef.current ) return; + observedElementRef.current = targetElement ?? undefined; function updateIndicator( element: HTMLElement ) { setIndicatorPosition( { left: element.offsetLeft, + top: element.offsetTop, width: element.offsetWidth, + height: element.offsetHeight, } ); + updateCallbackRef.current?.(); } // Set up a ResizeObserver. @@ -59,11 +72,49 @@ export const TabList = forwardRef< if ( observedElement ) resizeObserver.unobserve( observedElement ); // Observe new element. - if ( selectedTabEl ) { - updateIndicator( selectedTabEl ); - resizeObserver.observe( selectedTabEl ); + if ( targetElement ) { + updateIndicator( targetElement ); + resizeObserver.observe( targetElement ); } - }, [ selectedTabEl ] ); + }, [ targetElement ] ); + + return indicatorPosition; +} + +type ValueUpdateContext = { + previousValue: unknown; +}; + +function useOnValueUpdate( + value: unknown, + onUpdate: ( context: ValueUpdateContext ) => void +) { + const [ previousValue, setPreviousValue ] = useState( value ); + + useEffect( () => { + if ( previousValue !== value ) { + onUpdate( { previousValue } ); + setPreviousValue( value ); + } + }, [ previousValue, value, onUpdate ] ); +} + +export const TabList = forwardRef< + HTMLDivElement, + WordPressComponentProps< TabListProps, 'div', false > +>( function TabList( { children, ...otherProps }, ref ) { + const context = useTabsContext(); + + const selectedId = context?.store.useState( 'selectedId' ); + const indicatorPosition = useTrackElementOffset( + context?.store.item( selectedId )?.element + ); + + const [ animationEnabled, setAnimationEnabled ] = useState( false ); + useOnValueUpdate( + selectedId, + ( { previousValue } ) => previousValue && setAnimationEnabled( true ) + ); if ( ! context ) { warning( '`Tabs.TabList` must be wrapped in a `Tabs` component.' ); @@ -92,13 +143,21 @@ export const TabList = forwardRef< } + render={ + { + if ( event.pseudoElement === '::after' ) + setAnimationEnabled( false ); + } } + /> + } onBlur={ onBlur } { ...otherProps } > From b3ab95fb475b04f34e414b1d94f681f1461f98fc Mon Sep 17 00:00:00 2001 From: Dani Guardiola Date: Tue, 23 Apr 2024 20:44:01 +0200 Subject: [PATCH 12/23] Follow naming convention for classes. --- packages/components/src/tabs/styles.ts | 2 +- packages/components/src/tabs/tablist.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/components/src/tabs/styles.ts b/packages/components/src/tabs/styles.ts index dd1a07a1f2695..1141682c9b0c4 100644 --- a/packages/components/src/tabs/styles.ts +++ b/packages/components/src/tabs/styles.ts @@ -20,7 +20,7 @@ export const TabListWrapper = styled.div` &[aria-orientation='vertical'] { flex-direction: column; } - &.isAnimationEnabled::after { + &.is-animation-enabled::after { @media not ( prefers-reduced-motion: reduce ) { transition-property: left width; transition-duration: 0.2s; diff --git a/packages/components/src/tabs/tablist.tsx b/packages/components/src/tabs/tablist.tsx index 605f86c61b1e1..5ed9baed96a8a 100644 --- a/packages/components/src/tabs/tablist.tsx +++ b/packages/components/src/tabs/tablist.tsx @@ -143,7 +143,7 @@ export const TabList = forwardRef< Date: Wed, 24 Apr 2024 12:21:57 +0200 Subject: [PATCH 13/23] Support vertical orientation + misc fixes and improvements. --- packages/components/src/tabs/styles.ts | 13 ++++++++- packages/components/src/tabs/tablist.tsx | 36 ++++++++++++++++-------- 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/packages/components/src/tabs/styles.ts b/packages/components/src/tabs/styles.ts index 1141682c9b0c4..309a71194e02e 100644 --- a/packages/components/src/tabs/styles.ts +++ b/packages/components/src/tabs/styles.ts @@ -22,7 +22,7 @@ export const TabListWrapper = styled.div` } &.is-animation-enabled::after { @media not ( prefers-reduced-motion: reduce ) { - transition-property: left width; + transition-property: left, top, width, height; transition-duration: 0.2s; transition-timing-function: ease-out; } @@ -42,6 +42,17 @@ export const TabListWrapper = styled.div` outline: 2px solid transparent; outline-offset: -1px; } + &[aria-orientation='vertical']::after { + left: unset; + right: 0; + bottom: unset; + top: var( --indicator-top ); + width: unset; + height: var( --indicator-height ); + border-bottom: unset; + border-right: var( --wp-admin-border-width-focus ) solid + ${ COLORS.theme.accent }; + } `; export const Tab = styled( Ariakit.Tab )` diff --git a/packages/components/src/tabs/tablist.tsx b/packages/components/src/tabs/tablist.tsx index 5ed9baed96a8a..4e30224f722fb 100644 --- a/packages/components/src/tabs/tablist.tsx +++ b/packages/components/src/tabs/tablist.tsx @@ -24,6 +24,7 @@ import { useTabsContext } from './context'; import { TabListWrapper } from './styles'; import type { WordPressComponentProps } from '../context'; import type { CSSProperties } from 'react'; +import classNames from 'classnames'; function useTrackElementOffset( targetElement?: HTMLElement | null, @@ -81,22 +82,30 @@ function useTrackElementOffset( return indicatorPosition; } -type ValueUpdateContext = { - previousValue: unknown; +type ValueUpdateContext< T > = { + previousValue: T; }; -function useOnValueUpdate( - value: unknown, - onUpdate: ( context: ValueUpdateContext ) => void +function useOnValueUpdate< T >( + value: T, + onUpdate: ( context: ValueUpdateContext< T > ) => void ) { - const [ previousValue, setPreviousValue ] = useState( value ); + const previousValueRef = useRef( value ); + + // TODO: replace with useEventCallback or similar when officially available. + const updateCallbackRef = useRef( onUpdate ); + useLayoutEffect( () => { + updateCallbackRef.current = onUpdate; + } ); useEffect( () => { - if ( previousValue !== value ) { - onUpdate( { previousValue } ); - setPreviousValue( value ); + if ( previousValueRef.current !== value ) { + updateCallbackRef.current( { + previousValue: previousValueRef.current, + } ); + previousValueRef.current = value; } - }, [ previousValue, value, onUpdate ] ); + }, [ value ] ); } export const TabList = forwardRef< @@ -143,11 +152,12 @@ export const TabList = forwardRef< { children } From f364cc278b92ae251d4eea895bac8bd1d88a9db4 Mon Sep 17 00:00:00 2001 From: Dani Guardiola Date: Wed, 24 Apr 2024 12:24:04 +0200 Subject: [PATCH 14/23] Clean up styles a bit. --- packages/components/src/tabs/styles.ts | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/packages/components/src/tabs/styles.ts b/packages/components/src/tabs/styles.ts index 309a71194e02e..9772d2950567b 100644 --- a/packages/components/src/tabs/styles.ts +++ b/packages/components/src/tabs/styles.ts @@ -30,26 +30,24 @@ export const TabListWrapper = styled.div` &::after { content: ''; position: absolute; + pointer-events: none; + + // Windows high contrast mode. + outline: 2px solid transparent; + outline-offset: -1px; + } + &:not( [aria-orientation='vertical'] )::after { left: var( --indicator-left ); bottom: 0; width: var( --indicator-width ); height: 0; border-bottom: var( --wp-admin-border-width-focus ) solid ${ COLORS.theme.accent }; - pointer-events: none; - - // Windows high contrast mode. - outline: 2px solid transparent; - outline-offset: -1px; } &[aria-orientation='vertical']::after { - left: unset; right: 0; - bottom: unset; top: var( --indicator-top ); - width: unset; height: var( --indicator-height ); - border-bottom: unset; border-right: var( --wp-admin-border-width-focus ) solid ${ COLORS.theme.accent }; } From 539be1e2e1f3b45531e7bf59b96fa5a302d6a098 Mon Sep 17 00:00:00 2001 From: Dani Guardiola Date: Wed, 24 Apr 2024 13:13:44 +0200 Subject: [PATCH 15/23] Better focus ring animation + minor style cleanup. --- packages/components/src/tabs/styles.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/components/src/tabs/styles.ts b/packages/components/src/tabs/styles.ts index 9772d2950567b..8a41415bfe1b7 100644 --- a/packages/components/src/tabs/styles.ts +++ b/packages/components/src/tabs/styles.ts @@ -20,8 +20,8 @@ export const TabListWrapper = styled.div` &[aria-orientation='vertical'] { flex-direction: column; } - &.is-animation-enabled::after { - @media not ( prefers-reduced-motion: reduce ) { + @media not ( prefers-reduced-motion: reduce ) { + &.is-animation-enabled::after { transition-property: left, top, width, height; transition-duration: 0.2s; transition-timing-function: ease-out; @@ -94,17 +94,18 @@ export const Tab = styled( Ariakit.Tab )` pointer-events: none; // Draw the indicator. - box-shadow: 0 0 0 0 transparent; + box-shadow: 0 0 0 var( --wp-admin-border-width-focus ) + ${ COLORS.theme.accent }; border-radius: 2px; // Animation - transition: all 0.1s linear; + transition: opacity 0.1s linear; ${ reduceMotion( 'transition' ) }; + opacity: 0; } &:focus-visible::before { - box-shadow: 0 0 0 var( --wp-admin-border-width-focus ) - ${ COLORS.theme.accent }; + opacity: 1; // Windows high contrast mode. outline: 2px solid transparent; From fa9f34e05dbadb1bbf0f1a721618b65ee88028b2 Mon Sep 17 00:00:00 2001 From: Dani Guardiola Date: Wed, 24 Apr 2024 13:16:31 +0200 Subject: [PATCH 16/23] Fix changelog (oops). --- packages/components/CHANGELOG.md | 52 +------------------------------- 1 file changed, 1 insertion(+), 51 deletions(-) diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 477c94d983e44..01692bc6d999e 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -5,58 +5,9 @@ ### Enhancements - `Tabs`: Animate indicator ([#60560](https://github.com/WordPress/gutenberg/pull/60560)). -- `InputControl`: Add a password visibility toggle story ([#60898](https://github.com/WordPress/gutenberg/pull/60898)). -- `BoxControl`: Allow negative values for margin controls ([#60347](https://github.com/WordPress/gutenberg/pull/60347)). - -### Bug Fix - -- `SlotFill`: fixed missing `getServerSnapshot` parameter in slot map ([#60943](https://github.com/WordPress/gutenberg/pull/60943)). - -### Enhancements - -- `DropZone`: Avoid a media query on mount [#60546](https://github.com/WordPress/gutenberg/pull/60546)). -- `ComboboxControl`: Simplify string normalization ([#60893](https://github.com/WordPress/gutenberg/pull/60893)). - -### Internal - -- `FontSizerPicker`: Improve docs for default units ([#60996](https://github.com/WordPress/gutenberg/pull/60996)). - -## 27.4.0 (2024-04-19) - -### Deprecation - -- `Navigation`: Soft deprecate component ([#59182](https://github.com/WordPress/gutenberg/pull/59182)). - -### Enhancements - -- `Tooltip`: Make tests faster ([#60897](https://github.com/WordPress/gutenberg/pull/60897)). - `ExternalLink`: Use unicode arrow instead of svg icon ([#60255](https://github.com/WordPress/gutenberg/pull/60255)). - `ProgressBar`: Move the indicator width styles from emotion to a CSS variable ([#60388](https://github.com/WordPress/gutenberg/pull/60388)). -- `Text`: Add `text-wrap: pretty;` to improve wrapping ([#60164](https://github.com/WordPress/gutenberg/pull/60164)). -- `Navigator`: Navigation to the active path doesn't create a new location history ([#60561](https://github.com/WordPress/gutenberg/pull/60561)). -- `FormToggle`: Forwards ref to input ([#60234](https://github.com/WordPress/gutenberg/pull/60234)). -- `ToggleControl`: Forwards ref to FormToggle ([#60234](https://github.com/WordPress/gutenberg/pull/60234)). -- `CheckboxControl`: Update help text alignment ([#60787](https://github.com/WordPress/gutenberg/pull/60787)). - -### Bug Fix - -- `Truncate`: Fix link control link preview when it displays long URLs ([#60890](https://github.com/WordPress/gutenberg/pull/60890)). - -- `ProgressBar`: Fix CSS variable with invalid value ([#60576](https://github.com/WordPress/gutenberg/pull/60576)). -- `CheckboxControl`: Fix label text wrap ([#60787](https://github.com/WordPress/gutenberg/pull/60787)). - -### Experimental - -- `Tabs`: Fallback to first enabled tab if no active tab id ([#60681](https://github.com/WordPress/gutenberg/pull/60681)). - -### Internal - -- Remove CSS hack for Internet Explorer 11 ([#60727](https://github.com/WordPress/gutenberg/pull/60727)). -- `CheckboxControl`: Streamline size styles ([#60475](https://github.com/WordPress/gutenberg/pull/60475)). -- Deprecate `reduceMotion` util ([#60839](https://github.com/WordPress/gutenberg/pull/60839)). -- `InputBase`: Simplify management of focus styles. Affects all components based on `InputControl` (e.g. `SearchControl`, `NumberControl`, `UnitControl`), as well as `SelectControl`, `CustomSelectControl`, and `TreeSelect` ([#60226](https://github.com/WordPress/gutenberg/pull/60226)). -- Removed dependency on `valtio`, replaced its usage in `SlotFill` with a custom object [#60879](https://github.com/WordPress/gutenberg/pull/60879)). -- `CustomSelectControlV2`: Support disabled in item types ([#60896](https://github.com/WordPress/gutenberg/pull/60896)). +- `Text`: Add `text-wrap: pretty;` to improve wrapping. ([#60164](https://github.com/WordPress/gutenberg/pull/60164)). ## 27.3.0 (2024-04-03) @@ -91,7 +42,6 @@ - `TextControl`: Add typings for `date`, `time` and `datetime-local` ([#59666](https://github.com/WordPress/gutenberg/pull/59666)). - `Text`, `Heading`, `ItemGroup` : Update the line height from 1.2 to 1.4 ([#60041](https://github.com/WordPress/gutenberg/pull/60041)). -- `Autocomplete` : match the autocomplete styling to that of List View and Command Palette([#60131](https://github.com/WordPress/gutenberg/pull/60131)). ### Deprecation From 16fac9f83cfaee6ec997c9affc7ed1877d71c00e Mon Sep 17 00:00:00 2001 From: Dani Guardiola Date: Wed, 24 Apr 2024 13:17:41 +0200 Subject: [PATCH 17/23] Actually fix changelog. --- packages/components/CHANGELOG.md | 52 +++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 01692bc6d999e..477c94d983e44 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -5,9 +5,58 @@ ### Enhancements - `Tabs`: Animate indicator ([#60560](https://github.com/WordPress/gutenberg/pull/60560)). +- `InputControl`: Add a password visibility toggle story ([#60898](https://github.com/WordPress/gutenberg/pull/60898)). +- `BoxControl`: Allow negative values for margin controls ([#60347](https://github.com/WordPress/gutenberg/pull/60347)). + +### Bug Fix + +- `SlotFill`: fixed missing `getServerSnapshot` parameter in slot map ([#60943](https://github.com/WordPress/gutenberg/pull/60943)). + +### Enhancements + +- `DropZone`: Avoid a media query on mount [#60546](https://github.com/WordPress/gutenberg/pull/60546)). +- `ComboboxControl`: Simplify string normalization ([#60893](https://github.com/WordPress/gutenberg/pull/60893)). + +### Internal + +- `FontSizerPicker`: Improve docs for default units ([#60996](https://github.com/WordPress/gutenberg/pull/60996)). + +## 27.4.0 (2024-04-19) + +### Deprecation + +- `Navigation`: Soft deprecate component ([#59182](https://github.com/WordPress/gutenberg/pull/59182)). + +### Enhancements + +- `Tooltip`: Make tests faster ([#60897](https://github.com/WordPress/gutenberg/pull/60897)). - `ExternalLink`: Use unicode arrow instead of svg icon ([#60255](https://github.com/WordPress/gutenberg/pull/60255)). - `ProgressBar`: Move the indicator width styles from emotion to a CSS variable ([#60388](https://github.com/WordPress/gutenberg/pull/60388)). -- `Text`: Add `text-wrap: pretty;` to improve wrapping. ([#60164](https://github.com/WordPress/gutenberg/pull/60164)). +- `Text`: Add `text-wrap: pretty;` to improve wrapping ([#60164](https://github.com/WordPress/gutenberg/pull/60164)). +- `Navigator`: Navigation to the active path doesn't create a new location history ([#60561](https://github.com/WordPress/gutenberg/pull/60561)). +- `FormToggle`: Forwards ref to input ([#60234](https://github.com/WordPress/gutenberg/pull/60234)). +- `ToggleControl`: Forwards ref to FormToggle ([#60234](https://github.com/WordPress/gutenberg/pull/60234)). +- `CheckboxControl`: Update help text alignment ([#60787](https://github.com/WordPress/gutenberg/pull/60787)). + +### Bug Fix + +- `Truncate`: Fix link control link preview when it displays long URLs ([#60890](https://github.com/WordPress/gutenberg/pull/60890)). + +- `ProgressBar`: Fix CSS variable with invalid value ([#60576](https://github.com/WordPress/gutenberg/pull/60576)). +- `CheckboxControl`: Fix label text wrap ([#60787](https://github.com/WordPress/gutenberg/pull/60787)). + +### Experimental + +- `Tabs`: Fallback to first enabled tab if no active tab id ([#60681](https://github.com/WordPress/gutenberg/pull/60681)). + +### Internal + +- Remove CSS hack for Internet Explorer 11 ([#60727](https://github.com/WordPress/gutenberg/pull/60727)). +- `CheckboxControl`: Streamline size styles ([#60475](https://github.com/WordPress/gutenberg/pull/60475)). +- Deprecate `reduceMotion` util ([#60839](https://github.com/WordPress/gutenberg/pull/60839)). +- `InputBase`: Simplify management of focus styles. Affects all components based on `InputControl` (e.g. `SearchControl`, `NumberControl`, `UnitControl`), as well as `SelectControl`, `CustomSelectControl`, and `TreeSelect` ([#60226](https://github.com/WordPress/gutenberg/pull/60226)). +- Removed dependency on `valtio`, replaced its usage in `SlotFill` with a custom object [#60879](https://github.com/WordPress/gutenberg/pull/60879)). +- `CustomSelectControlV2`: Support disabled in item types ([#60896](https://github.com/WordPress/gutenberg/pull/60896)). ## 27.3.0 (2024-04-03) @@ -42,6 +91,7 @@ - `TextControl`: Add typings for `date`, `time` and `datetime-local` ([#59666](https://github.com/WordPress/gutenberg/pull/59666)). - `Text`, `Heading`, `ItemGroup` : Update the line height from 1.2 to 1.4 ([#60041](https://github.com/WordPress/gutenberg/pull/60041)). +- `Autocomplete` : match the autocomplete styling to that of List View and Command Palette([#60131](https://github.com/WordPress/gutenberg/pull/60131)). ### Deprecation From 6b2f5411e7a09fcefcfd72bf52fbadf012370c64 Mon Sep 17 00:00:00 2001 From: Dani Guardiola Date: Fri, 26 Apr 2024 09:50:51 +0200 Subject: [PATCH 18/23] Remove deprecated `reduceMotion` utility. --- packages/components/src/tabs/styles.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/components/src/tabs/styles.ts b/packages/components/src/tabs/styles.ts index 8a41415bfe1b7..f011a4fb6389e 100644 --- a/packages/components/src/tabs/styles.ts +++ b/packages/components/src/tabs/styles.ts @@ -10,7 +10,6 @@ import * as Ariakit from '@ariakit/react'; */ import { COLORS } from '../utils'; import { space } from '../utils/space'; -import { reduceMotion } from '../utils/reduce-motion'; export const TabListWrapper = styled.div` position: relative; @@ -99,9 +98,11 @@ export const Tab = styled( Ariakit.Tab )` border-radius: 2px; // Animation - transition: opacity 0.1s linear; - ${ reduceMotion( 'transition' ) }; opacity: 0; + + @media not ( prefers-reduced-motion ) { + transition: opacity 0.1s linear; + } } &:focus-visible::before { From 2f14f6dad6896aaaaf171f11eb6c359418c135ca Mon Sep 17 00:00:00 2001 From: Dani Guardiola Date: Wed, 22 May 2024 12:30:10 +0200 Subject: [PATCH 19/23] Fix open/closed --- packages/components/src/tabs/tablist.tsx | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/packages/components/src/tabs/tablist.tsx b/packages/components/src/tabs/tablist.tsx index 59f4d0e2c40e9..c43b58994ac68 100644 --- a/packages/components/src/tabs/tablist.tsx +++ b/packages/components/src/tabs/tablist.tsx @@ -158,14 +158,6 @@ export const TabList = forwardRef< { @@ -177,6 +169,15 @@ export const TabList = forwardRef< } onBlur={ onBlur } { ...otherProps } + style={ + { + '--indicator-left': `${ indicatorPosition.left }px`, + '--indicator-top': `${ indicatorPosition.top }px`, + '--indicator-width': `${ indicatorPosition.width }px`, + '--indicator-height': `${ indicatorPosition.height }px`, + ...otherProps.style, + } as CSSProperties + } className={ clsx( animationEnabled ? 'is-animation-enabled' : '', otherProps.className From 02099acb30d81b2402edaad451d0d2ae60756251 Mon Sep 17 00:00:00 2001 From: Dani Guardiola Date: Wed, 22 May 2024 12:30:23 +0200 Subject: [PATCH 20/23] Add vertical tabs story --- .../components/src/tabs/stories/index.story.tsx | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/packages/components/src/tabs/stories/index.story.tsx b/packages/components/src/tabs/stories/index.story.tsx index 558daf9143c25..16f8ac956ccf7 100644 --- a/packages/components/src/tabs/stories/index.story.tsx +++ b/packages/components/src/tabs/stories/index.story.tsx @@ -68,6 +68,20 @@ const Template: StoryFn< typeof Tabs > = ( props ) => { export const Default = Template.bind( {} ); +const VerticalTemplate: StoryFn< typeof Tabs > = ( props ) => { + return ( + + + Tab 1 + Tab 2 + Tab 3 + + + ); +}; + +export const Vertical = VerticalTemplate.bind( {} ); + const DisabledTabTemplate: StoryFn< typeof Tabs > = ( props ) => { return ( From 2c4d085a7fd2d72c4467e966b2e88bd6fa36d75d Mon Sep 17 00:00:00 2001 From: Dani Guardiola Date: Wed, 22 May 2024 16:23:12 +0200 Subject: [PATCH 21/23] Move ResizeObserver unobserve to effect cleanup --- packages/components/src/tabs/tablist.tsx | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/components/src/tabs/tablist.tsx b/packages/components/src/tabs/tablist.tsx index c43b58994ac68..2cfad6a03e631 100644 --- a/packages/components/src/tabs/tablist.tsx +++ b/packages/components/src/tabs/tablist.tsx @@ -72,17 +72,18 @@ function useTrackElementOffset( } const { current: resizeObserver } = resizeObserverRef; - // Unobserve previous element. - const { current: observedElement } = observedElementRef; - if ( observedElement ) { - resizeObserver.unobserve( observedElement ); - } - // Observe new element. if ( targetElement ) { updateIndicator( targetElement ); resizeObserver.observe( targetElement ); } + + return () => { + // Unobserve previous element. + if ( observedElementRef.current ) { + resizeObserver.unobserve( observedElementRef.current ); + } + }; }, [ targetElement ] ); return indicatorPosition; From bed8a363f2a97d61b2f9e5c48f688b86e7e5d5d0 Mon Sep 17 00:00:00 2001 From: Dani Guardiola Date: Thu, 23 May 2024 16:07:14 +0200 Subject: [PATCH 22/23] Remove outdated type cast. --- packages/components/src/tabs/tablist.tsx | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/packages/components/src/tabs/tablist.tsx b/packages/components/src/tabs/tablist.tsx index 2cfad6a03e631..cbd4290f06bff 100644 --- a/packages/components/src/tabs/tablist.tsx +++ b/packages/components/src/tabs/tablist.tsx @@ -23,7 +23,6 @@ import type { TabListProps } from './types'; import { useTabsContext } from './context'; import { TabListWrapper } from './styles'; import type { WordPressComponentProps } from '../context'; -import type { CSSProperties } from 'react'; import clsx from 'clsx'; function useTrackElementOffset( @@ -170,15 +169,13 @@ export const TabList = forwardRef< } onBlur={ onBlur } { ...otherProps } - style={ - { - '--indicator-left': `${ indicatorPosition.left }px`, - '--indicator-top': `${ indicatorPosition.top }px`, - '--indicator-width': `${ indicatorPosition.width }px`, - '--indicator-height': `${ indicatorPosition.height }px`, - ...otherProps.style, - } as CSSProperties - } + style={ { + '--indicator-left': `${ indicatorPosition.left }px`, + '--indicator-top': `${ indicatorPosition.top }px`, + '--indicator-width': `${ indicatorPosition.width }px`, + '--indicator-height': `${ indicatorPosition.height }px`, + ...otherProps.style, + } } className={ clsx( animationEnabled ? 'is-animation-enabled' : '', otherProps.className From f0b896b0466b17dc2427fc2b46224a8f1437f769 Mon Sep 17 00:00:00 2001 From: Dani Guardiola Date: Thu, 23 May 2024 16:20:43 +0200 Subject: [PATCH 23/23] Hide vertical indicator for now. --- packages/components/src/tabs/styles.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/components/src/tabs/styles.ts b/packages/components/src/tabs/styles.ts index f011a4fb6389e..ce575c9f31779 100644 --- a/packages/components/src/tabs/styles.ts +++ b/packages/components/src/tabs/styles.ts @@ -44,6 +44,9 @@ export const TabListWrapper = styled.div` ${ COLORS.theme.accent }; } &[aria-orientation='vertical']::after { + /* Temporarily hidden, context: https://github.com/WordPress/gutenberg/pull/60560#issuecomment-2126670072 */ + opacity: 0; + right: 0; top: var( --indicator-top ); height: var( --indicator-height );