diff --git a/packages/block-editor/src/components/block-toolbar/index.js b/packages/block-editor/src/components/block-toolbar/index.js index 6727851e1647b..9d628be1a009c 100644 --- a/packages/block-editor/src/components/block-toolbar/index.js +++ b/packages/block-editor/src/components/block-toolbar/index.js @@ -120,13 +120,12 @@ const BlockToolbar = ( { hideDragHandle } ) => { const classes = classnames( 'block-editor-block-toolbar', { 'is-showing-movers': shouldShowMovers, 'is-synced': isSynced, + 'is-fixed': hasFixedToolbar, } ); return (
- { ! isMultiToolbar && - ! displayHeaderToolbar && - ! isContentLocked && } + { ! isMultiToolbar && ! isContentLocked && }
{ ( shouldShowVisualToolbar || isMultiToolbar ) && ! isContentLocked && ( diff --git a/packages/block-editor/src/components/block-toolbar/style.scss b/packages/block-editor/src/components/block-toolbar/style.scss index 41faa1a46eaed..c92ea9e7c0040 100644 --- a/packages/block-editor/src/components/block-toolbar/style.scss +++ b/packages/block-editor/src/components/block-toolbar/style.scss @@ -20,6 +20,33 @@ overflow: inherit; } + &.is-fixed > .block-editor-block-parent-selector .block-editor-block-parent-selector__button { + border: 0; + &::after { + content: "\00B7"; + font-size: 16px; + line-height: $grid-unit-40 + $grid-unit-10; + position: absolute; + left: $grid-unit-40 + $grid-unit-15 + 2px; + bottom: $grid-unit-05; + } + } + + &:not(.is-fixed) > .block-editor-block-parent-selector { + position: absolute; + top: -$border-width; + left: calc(-#{$grid-unit-60} - #{$grid-unit-10} - #{$border-width}); + + .show-icon-labels & { + position: relative; + left: auto; + top: auto; + margin-top: -$border-width; + margin-left: -$border-width; + margin-bottom: -$border-width; + } + } + // Borders around toolbar segments. .components-toolbar-group, .components-toolbar { @@ -64,20 +91,6 @@ } } -.block-editor-block-parent-selector { - position: absolute; - top: -$border-width; - left: calc(-#{$grid-unit-60} - #{$grid-unit-10} - #{$border-width}); - - .show-icon-labels & { - position: relative; - left: auto; - top: auto; - margin-top: -$border-width; - margin-left: -$border-width; - margin-bottom: -$border-width; - } -} // Block controls. .block-editor-block-toolbar__block-controls { diff --git a/packages/block-editor/src/components/block-tools/index.js b/packages/block-editor/src/components/block-tools/index.js index 519d8f7d82bff..3baab2072f19c 100644 --- a/packages/block-editor/src/components/block-tools/index.js +++ b/packages/block-editor/src/components/block-tools/index.js @@ -46,10 +46,7 @@ export default function BlockTools( { ...props } ) { const isLargeViewport = useViewportMatch( 'medium' ); - const { hasFixedToolbar, isZoomOutMode, isTyping } = useSelect( - selector, - [] - ); + const { isZoomOutMode, isTyping } = useSelect( selector, [] ); const isMatch = useShortcutEventMatch(); const { getSelectedBlockClientIds, getBlockRootClientId } = useSelect( blockEditorStore ); @@ -129,10 +126,9 @@ export default function BlockTools( { __unstableContentRef={ __unstableContentRef } /> ) } - { ! isZoomOutMode && - ( hasFixedToolbar || ! isLargeViewport ) && ( - - ) } + { ! isLargeViewport && ! isZoomOutMode && ( + + ) } { /* Even if the toolbar is fixed, the block popover is still needed for navigation and zoom-out mode. */ } { - event.preventDefault(); -}; - -function HeaderToolbar() { - const inserterButton = useRef(); - const { setIsInserterOpened, setIsListViewOpened } = - useDispatch( editPostStore ); +function HeaderToolbar( + { BlockToolbarToggle, hasSelectedBlocks }, + inserterButtonRef +) { + const { setIsListViewOpened } = useDispatch( editPostStore ); const { - isInserterEnabled, - isInserterOpened, isTextModeEnabled, showIconLabels, isListViewOpen, listViewShortcut, } = useSelect( ( select ) => { - const { hasInserterItems, getBlockRootClientId, getBlockSelectionEnd } = - select( blockEditorStore ); - const { getEditorSettings } = select( editorStore ); const { getEditorMode, isFeatureActive, isListViewOpened } = select( editPostStore ); const { getShortcutRepresentation } = select( keyboardShortcutsStore ); return { - // This setting (richEditingEnabled) should not live in the block editor's setting. - isInserterEnabled: - getEditorMode() === 'visual' && - getEditorSettings().richEditingEnabled && - hasInserterItems( - getBlockRootClientId( getBlockSelectionEnd() ) - ), - isInserterOpened: select( editPostStore ).isInserterOpened(), isTextModeEnabled: getEditorMode() === 'text', showIconLabels: isFeatureActive( 'showIconLabels' ), isListViewOpen: isListViewOpened(), @@ -91,44 +68,14 @@ function HeaderToolbar() { /> ); - const toggleInserter = useCallback( () => { - if ( isInserterOpened ) { - // Focusing the inserter button should close the inserter popover. - // However, there are some cases it won't close when the focus is lost. - // See https://github.com/WordPress/gutenberg/issues/43090 for more details. - inserterButton.current.focus(); - setIsInserterOpened( false ); - } else { - setIsInserterOpened( true ); - } - }, [ isInserterOpened, setIsInserterOpened ] ); - - /* translators: button label text should, if possible, be under 16 characters. */ - const longLabel = _x( - 'Toggle block inserter', - 'Generic label for block inserter button' - ); - const shortLabel = ! isInserterOpened ? __( 'Add' ) : __( 'Close' ); return ( -
- +
+ { ( isWideViewport || ! showIconLabels ) && ( <> { isLargeViewport && ( @@ -152,6 +99,7 @@ function HeaderToolbar() { variant={ showIconLabels ? 'tertiary' : undefined } /> { overflowItems } + { hasSelectedBlocks && } ) }
@@ -159,4 +107,4 @@ function HeaderToolbar() { ); } -export default HeaderToolbar; +export default forwardRef( HeaderToolbar ); diff --git a/packages/edit-post/src/components/header/header-toolbar/style.scss b/packages/edit-post/src/components/header/header-toolbar/style.scss index 87aec00004c02..577e244b53efd 100644 --- a/packages/edit-post/src/components/header/header-toolbar/style.scss +++ b/packages/edit-post/src/components/header/header-toolbar/style.scss @@ -1,11 +1,11 @@ -.edit-post-header-toolbar { +.edit-post-header-document-toolbar { display: inline-flex; flex-grow: 1; align-items: center; border: none; // Hide all action buttons except the inserter on mobile. - .edit-post-header-toolbar__left > .components-button { + .edit-post-header-document-toolbar__left > .components-button { display: none; @include break-small() { @@ -13,7 +13,7 @@ } } - .edit-post-header-toolbar__left > .edit-post-header-toolbar__inserter-toggle { + .edit-post-header-document-toolbar__left > .edit-post-header-toolbar__inserter-toggle { display: inline-flex; svg { @@ -39,11 +39,94 @@ // The Toolbar component adds different styles to buttons, so we reset them // here to the original button styles - .edit-post-header-toolbar__left > .components-button.has-icon, - .edit-post-header-toolbar__left > .components-dropdown > .components-button.has-icon { + .edit-post-header-document-toolbar__left > .components-button.has-icon:first-child { + margin-right: $grid-unit-10; + } + + .edit-post-header-document-toolbar__left > .components-button.has-icon, + .edit-post-header-document-toolbar__left > .components-dropdown > .components-button.has-icon { + height: $grid-unit-40; + min-width: $grid-unit-40; + margin-right: $grid-unit-05; + padding: 0; + width: 32px; + + &.is-pressed { + background: $gray-900; + } + + &:focus:not(:disabled) { + box-shadow: 0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color), inset 0 0 0 $border-width $white; + outline: 1px solid transparent; + } + + &::before { + display: none; + } + } + + & > .edit-post-header-document-toolbar__left button.components-button.edit-post-header-toolbar__block-tools-toggle.is-primary.has-icon { + height: $button-size; + min-width: $button-size; + background-color: #fff; + color: #000; + margin-left: $grid-unit-10; + border-left: 1px solid #ddd; + padding-left: $grid-unit-15; + + &:hover { + color: var(--wp-components-color-accent, var(--wp-admin-theme-color, #007cba)); + } + } +} + +.edit-post-header-block-toolbar { + position: absolute; + left: $grid-unit-70 + $grid-unit-05; + display: inline-flex; + flex-grow: 1; + align-items: center; + border: none; + padding-left: 24px; //compensates for the edit-post-header-document-toolbar__left left padding + // height: 32px; + // overflow: hidden; //limits the height of the separators + + > .edit-post-header-toolbar__inserter-toggle { + display: inline-flex; + + svg { + transition: transform cubic-bezier(0.165, 0.84, 0.44, 1) 0.2s; + @include reduce-motion("transition"); + } + + &.is-pressed { + svg { + transform: rotate(45deg); + } + } + } + + & > button.components-button.edit-post-header-toolbar__document-tools-toggle.is-primary.has-icon { height: $button-size; min-width: $button-size; padding: 6px; + background-color: #fff; + color: #000; + border-right: 1px solid #ddd; + + &:hover { + color: var(--wp-components-color-accent, var(--wp-admin-theme-color, #007cba)); + } + } + + // The Toolbar component adds different styles to buttons, so we reset them + // here to the original button styles + & > button.components-button.edit-post-header-toolbar__inserter-toggle.is-primary.has-icon { + height: 32px; + margin-right: 8px; + min-width: 32px; + padding: 0; + width: 32px; &.is-pressed { background: $gray-900; @@ -57,28 +140,35 @@ &::before { display: none; } + + .show-icon-labels & { + width: auto; + height: 36px; + padding: 0 $grid-unit-10; + } } + } // Reduced UI. .edit-post-header.has-reduced-ui { @include break-small () { // Apply transition to every button but the first one. - .edit-post-header-toolbar__left > * + .components-button, - .edit-post-header-toolbar__left > * + .components-dropdown > [aria-expanded="false"] { + .edit-post-header-document-toolbar__left > * + .components-button, + .edit-post-header-document-toolbar__left > * + .components-dropdown > [aria-expanded="false"] { transition: opacity 0.1s linear; @include reduce-motion("transition"); } // Zero out opacity unless hovered. - &:not(:hover) .edit-post-header-toolbar__left > * + .components-button, - &:not(:hover) .edit-post-header-toolbar__left > * + .components-dropdown > [aria-expanded="false"] { + &:not(:hover) .edit-post-header-document-toolbar__left > * + .components-button, + &:not(:hover) .edit-post-header-document-toolbar__left > * + .components-dropdown > [aria-expanded="false"] { opacity: 0; } } } -.edit-post-header-toolbar__left { +.edit-post-header-document-toolbar__left { display: inline-flex; align-items: center; padding-left: $grid-unit-10; @@ -92,7 +182,7 @@ } } -.edit-post-header-toolbar .edit-post-header-toolbar__left > .edit-post-header-toolbar__inserter-toggle.has-icon { +.edit-post-header-document-toolbar .edit-post-header-document-toolbar__left > .edit-post-header-document-toolbar__inserter-toggle.has-icon { margin-right: $grid-unit-10; // Special dimensions for this button. min-width: 32px; @@ -107,6 +197,6 @@ } } -.show-icon-labels .edit-post-header-toolbar__left > * + * { +.show-icon-labels .edit-post-header-document-toolbar__left > * + * { margin-left: $grid-unit-10; } diff --git a/packages/edit-post/src/components/header/index.js b/packages/edit-post/src/components/header/index.js index a7bdccbfe44d9..9a6f3791a8161 100644 --- a/packages/edit-post/src/components/header/index.js +++ b/packages/edit-post/src/components/header/index.js @@ -1,11 +1,25 @@ /** * WordPress dependencies */ +import { __ } from '@wordpress/i18n'; import { PostSavedState, PostPreviewButton } from '@wordpress/editor'; +import { useEffect, useState, useRef } from '@wordpress/element'; import { useSelect } from '@wordpress/data'; import { PinnedItems } from '@wordpress/interface'; import { useViewportMatch } from '@wordpress/compose'; -import { __unstableMotion as motion } from '@wordpress/components'; +import { + __unstableMotion as motion, + Button, + ToolbarItem, +} from '@wordpress/components'; +import { + BlockIcon, + NavigableToolbar, + store as blockEditorStore, + BlockToolbar, + useBlockDisplayInformation, +} from '@wordpress/block-editor'; +import { levelUp } from '@wordpress/icons'; /** * Internal dependencies @@ -18,17 +32,68 @@ import { default as DevicePreview } from '../device-preview'; import MainDashboardButton from './main-dashboard-button'; import { store as editPostStore } from '../../store'; import TemplateTitle from './template-title'; +import InserterButton from './inserter-button'; + +function MaybeHide( { children, isHidden, onFocus } ) { + if ( isHidden ) { + return ( +
+ { children } +
+ ); + } + return children; +} + +function ShowDocumentToolbarButton( { onClick } ) { + return ( + + ); +} + +function ShowBlockToolbarButton( { onClick, icon } ) { + return ( + } + onClick={ onClick } + /> + ); +} function Header( { setEntitiesSavedStatesCallback } ) { const isLargeViewport = useViewportMatch( 'large' ); + const isMobileViewPort = useViewportMatch( 'medium', '<' ); + const isDesktopViewport = useViewportMatch( 'medium', '>=' ); const { hasActiveMetaboxes, isPublishSidebarOpened, isSaving, + hasFixedToolbar, + hasSelectedBlocks, showIconLabels, isDistractionFreeMode, - } = useSelect( - ( select ) => ( { + isNavigationMode, + selectedBlockClientId, + } = useSelect( ( select ) => { + const { + getSettings, + getSelectedBlockClientIds, + getSelectedBlockClientId, + isNavigationMode: _isNavigationMode, + } = select( blockEditorStore ); + const settings = getSettings(); + const _selectedBlockClientIds = getSelectedBlockClientIds(); + return { + hasSelectedBlocks: !! _selectedBlockClientIds.length, hasActiveMetaboxes: select( editPostStore ).hasMetaBoxes(), isPublishSidebarOpened: select( editPostStore ).isPublishSidebarOpened(), @@ -37,12 +102,46 @@ function Header( { setEntitiesSavedStatesCallback } ) { select( editPostStore ).isFeatureActive( 'showIconLabels' ), isDistractionFreeMode: select( editPostStore ).isFeatureActive( 'distractionFree' ), - } ), - [] - ); + hasFixedToolbar: settings.hasFixedToolbar, + isNavigationMode: _isNavigationMode(), + selectedBlockClientId: getSelectedBlockClientId(), + }; + } ); const isDistractionFree = isDistractionFreeMode && isLargeViewport; + const [ headerToolbar, setHeaderToolbar ] = useState( 'document' ); + + const documentToolsInserterButton = useRef(); + const blockToolsInserterButton = useRef(); + + const blockInformation = useBlockDisplayInformation( + selectedBlockClientId + ); + + useEffect( () => { + if ( + headerToolbar === 'document' && + documentToolsInserterButton.current + ) { + documentToolsInserterButton.current.focus(); + } + + if ( headerToolbar === 'block' && blockToolsInserterButton.current ) { + blockToolsInserterButton.current.focus(); + } + }, [ headerToolbar ] ); + + useEffect( () => { + if ( isNavigationMode ) { + setHeaderToolbar( 'document' ); + } else if ( hasSelectedBlocks ) { + setHeaderToolbar( 'block' ); + } else { + setHeaderToolbar( 'document' ); + } + }, [ hasSelectedBlocks, isNavigationMode ] ); + const slideY = { hidden: isDistractionFree ? { y: '-50' } : { y: 0 }, hover: { y: 0, transition: { type: 'tween', delay: 0.2 } }, @@ -53,6 +152,8 @@ function Header( { setEntitiesSavedStatesCallback } ) { hover: { x: 0, transition: { type: 'tween', delay: 0.2 } }, }; + const blockToolbarAriaLabel = __( 'Block tools' ); + return (
@@ -68,7 +169,30 @@ function Header( { setEntitiesSavedStatesCallback } ) { transition={ { type: 'tween', delay: 0.8 } } className="edit-post-header__toolbar" > - + { ( ! hasFixedToolbar || + isNavigationMode || + isMobileViewPort ) && } + { hasFixedToolbar && + ! isNavigationMode && + isDesktopViewport && ( + setHeaderToolbar( 'document' ) } + > + ( + + setHeaderToolbar( 'block' ) + } + icon={ blockInformation.icon } + /> + ) } + /> + + ) } ) } + setHeaderToolbar( 'block' ) } + > + + + setHeaderToolbar( 'document' ) } + /> + + +
); } diff --git a/packages/edit-post/src/components/header/inserter-button/index.js b/packages/edit-post/src/components/header/inserter-button/index.js new file mode 100644 index 0000000000000..8f21de3145c65 --- /dev/null +++ b/packages/edit-post/src/components/header/inserter-button/index.js @@ -0,0 +1,83 @@ +/** + * WordPress dependencies + */ +import { useCallback, forwardRef } from '@wordpress/element'; +import { __, _x } from '@wordpress/i18n'; +import { Button, ToolbarItem } from '@wordpress/components'; +import { plus } from '@wordpress/icons'; +import { useSelect, useDispatch } from '@wordpress/data'; +import { store as blockEditorStore } from '@wordpress/block-editor'; +import { store as editorStore } from '@wordpress/editor'; +/** + * Internal dependencies + */ +import { store as editPostStore } from '../../../store'; + +const preventDefault = ( event ) => { + event.preventDefault(); +}; + +function InserterButton( {}, inserterButton ) { + const { setIsInserterOpened } = useDispatch( editPostStore ); + const { isInserterEnabled, isInserterOpened, showIconLabels } = useSelect( + ( select ) => { + const { + hasInserterItems, + getBlockRootClientId, + getBlockSelectionEnd, + } = select( blockEditorStore ); + const { getEditorSettings } = select( editorStore ); + const { getEditorMode, isFeatureActive } = select( editPostStore ); + + return { + // This setting (richEditingEnabled) should not live in the block editor's setting. + isInserterEnabled: + getEditorMode() === 'visual' && + getEditorSettings().richEditingEnabled && + hasInserterItems( + getBlockRootClientId( getBlockSelectionEnd() ) + ), + isInserterOpened: select( editPostStore ).isInserterOpened(), + showIconLabels: isFeatureActive( 'showIconLabels' ), + }; + }, + [] + ); + + const toggleInserter = useCallback( () => { + if ( isInserterOpened ) { + // Focusing the inserter button should close the inserter popover. + // However, there are some cases it won't close when the focus is lost. + // See https://github.com/WordPress/gutenberg/issues/43090 for more details. + inserterButton.current.focus(); + setIsInserterOpened( false ); + } else { + setIsInserterOpened( true ); + } + }, [ isInserterOpened, setIsInserterOpened ] ); + + /* translators: button label text should, if possible, be under 16 characters. */ + const longLabel = _x( + 'Toggle block inserter', + 'Generic label for block inserter button' + ); + const shortLabel = ! isInserterOpened ? __( 'Add' ) : __( 'Close' ); + + return ( + + ); +} + +export default forwardRef( InserterButton ); diff --git a/packages/edit-post/src/components/header/style.scss b/packages/edit-post/src/components/header/style.scss index 160543684a702..545d57edb0f3c 100644 --- a/packages/edit-post/src/components/header/style.scss +++ b/packages/edit-post/src/components/header/style.scss @@ -238,3 +238,23 @@ z-index: 35; } } + +.maybeHide { + position: fixed; + top: -9999px; +} + +.maybeHide:focus-within { + position: relative; + top: 0; +} + +.maybeHide:focus-within > .edit-post-header-document-toolbar .edit-post-header-document-toolbar__left > .components-button.edit-post-header-toolbar__block-tools-toggle, +.maybeHide:focus-within + .edit-post-header-block-toolbar > .edit-post-header-toolbar__document-tools-toggle, +.maybeHide:focus-within + .edit-post-header-block-toolbar > .edit-post-header-toolbar__inserter-toggle { + display: none; +} + +// .maybeHide:focus-within + .edit-post-header-block-toolbar { +// padding-left: 0; +// } diff --git a/packages/edit-post/src/components/header/writing-menu/index.js b/packages/edit-post/src/components/header/writing-menu/index.js index 6cea56392381e..ac21030cc9cd6 100644 --- a/packages/edit-post/src/components/header/writing-menu/index.js +++ b/packages/edit-post/src/components/header/writing-menu/index.js @@ -6,10 +6,7 @@ import { MenuGroup } from '@wordpress/components'; import { __, _x } from '@wordpress/i18n'; import { useViewportMatch } from '@wordpress/compose'; import { displayShortcut } from '@wordpress/keycodes'; -import { - PreferenceToggleMenuItem, - store as preferencesStore, -} from '@wordpress/preferences'; +import { PreferenceToggleMenuItem } from '@wordpress/preferences'; import { store as blockEditorStore } from '@wordpress/block-editor'; /** @@ -32,13 +29,11 @@ function WritingMenu() { const { setIsInserterOpened, setIsListViewOpened, closeGeneralSidebar } = useDispatch( postEditorStore ); - const { set: setPreference } = useDispatch( preferencesStore ); const { selectBlock } = useDispatch( blockEditorStore ); const toggleDistractionFree = () => { registry.batch( () => { - setPreference( 'core/edit-post', 'fixedToolbar', false ); setIsInserterOpened( false ); setIsListViewOpened( false ); closeGeneralSidebar(); diff --git a/packages/edit-post/src/components/keyboard-shortcuts/index.js b/packages/edit-post/src/components/keyboard-shortcuts/index.js index 13a989de7fd07..c330c7aac250d 100644 --- a/packages/edit-post/src/components/keyboard-shortcuts/index.js +++ b/packages/edit-post/src/components/keyboard-shortcuts/index.js @@ -11,7 +11,6 @@ import { __ } from '@wordpress/i18n'; import { store as editorStore } from '@wordpress/editor'; import { store as blockEditorStore } from '@wordpress/block-editor'; import { store as noticesStore } from '@wordpress/notices'; -import { store as preferencesStore } from '@wordpress/preferences'; import { createBlock } from '@wordpress/blocks'; /** @@ -45,10 +44,7 @@ function KeyboardShortcuts() { } = useDispatch( editPostStore ); const { registerShortcut } = useDispatch( keyboardShortcutsStore ); - const { set: setPreference } = useDispatch( preferencesStore ); - const toggleDistractionFree = () => { - setPreference( 'core/edit-post', 'fixedToolbar', false ); setIsInserterOpened( false ); setIsListViewOpened( false ); closeGeneralSidebar(); diff --git a/packages/edit-post/src/components/preferences-modal/index.js b/packages/edit-post/src/components/preferences-modal/index.js index 77c6383b13f32..41aaa12356042 100644 --- a/packages/edit-post/src/components/preferences-modal/index.js +++ b/packages/edit-post/src/components/preferences-modal/index.js @@ -19,7 +19,6 @@ import { PreferencesModalTabs, PreferencesModalSection, } from '@wordpress/interface'; -import { store as preferencesStore } from '@wordpress/preferences'; /** * Internal dependencies @@ -65,10 +64,7 @@ export default function EditPostPreferencesModal() { const { closeGeneralSidebar, setIsListViewOpened, setIsInserterOpened } = useDispatch( editPostStore ); - const { set: setPreference } = useDispatch( preferencesStore ); - const toggleDistractionFree = () => { - setPreference( 'core/edit-post', 'fixedToolbar', false ); setIsInserterOpened( false ); setIsListViewOpened( false ); closeGeneralSidebar(); diff --git a/packages/icons/src/index.js b/packages/icons/src/index.js index 45ead66387db7..7c4be01f3fb95 100644 --- a/packages/icons/src/index.js +++ b/packages/icons/src/index.js @@ -119,6 +119,7 @@ export { default as key } from './library/key'; export { default as keyboardClose } from './library/keyboard-close'; export { default as keyboardReturn } from './library/keyboard-return'; export { default as layout } from './library/layout'; +export { default as levelUp } from './library/level-up'; export { default as lifesaver } from './library/lifesaver'; export { default as lineDashed } from './library/line-dashed'; export { default as lineDotted } from './library/line-dotted'; diff --git a/packages/icons/src/library/level-up.js b/packages/icons/src/library/level-up.js new file mode 100644 index 0000000000000..fc992c8dbada5 --- /dev/null +++ b/packages/icons/src/library/level-up.js @@ -0,0 +1,12 @@ +/** + * WordPress dependencies + */ +import { SVG, Path } from '@wordpress/primitives'; + +const levelUp = ( + + + +); + +export default levelUp;