@gechiui/block-editor
Version:
327 lines (279 loc) • 10.8 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = WrappedBlockPopover;
var _element = require("@gechiui/element");
var _lodash = require("lodash");
var _classnames = _interopRequireDefault(require("classnames"));
var _blocks = require("@gechiui/blocks");
var _components = require("@gechiui/components");
var _data = require("@gechiui/data");
var _keyboardShortcuts = require("@gechiui/keyboard-shortcuts");
var _compose = require("@gechiui/compose");
var _dom = require("@gechiui/dom");
var _blockSelectionButton = _interopRequireDefault(require("./block-selection-button"));
var _blockContextualToolbar = _interopRequireDefault(require("./block-contextual-toolbar"));
var _inserter = _interopRequireDefault(require("../inserter"));
var _store = require("../../store");
var _useBlockRefs = require("../block-list/use-block-props/use-block-refs");
var _usePopoverScroll = require("./use-popover-scroll");
/**
* External dependencies
*/
/**
* GeChiUI dependencies
*/
/**
* Internal dependencies
*/
function selector(select) {
const {
isNavigationMode,
isMultiSelecting,
hasMultiSelection,
isTyping,
isCaretWithinFormattedText,
getSettings,
getLastMultiSelectedBlockClientId
} = select(_store.store);
return {
isNavigationMode: isNavigationMode(),
isMultiSelecting: isMultiSelecting(),
isTyping: isTyping(),
isCaretWithinFormattedText: isCaretWithinFormattedText(),
hasMultiSelection: hasMultiSelection(),
hasFixedToolbar: getSettings().hasFixedToolbar,
lastClientId: getLastMultiSelectedBlockClientId()
};
}
function BlockPopover(_ref) {
let {
clientId,
rootClientId,
isValid,
isEmptyDefaultBlock,
capturingClientId,
__unstablePopoverSlot,
__unstableContentRef
} = _ref;
const {
isNavigationMode,
isMultiSelecting,
isTyping,
isCaretWithinFormattedText,
hasMultiSelection,
hasFixedToolbar,
lastClientId
} = (0, _data.useSelect)(selector, []);
const isInsertionPointVisible = (0, _data.useSelect)(select => {
const {
isBlockInsertionPointVisible,
getBlockInsertionPoint,
getBlockOrder
} = select(_store.store);
if (!isBlockInsertionPointVisible()) {
return false;
}
const insertionPoint = getBlockInsertionPoint();
const order = getBlockOrder(insertionPoint.rootClientId);
return order[insertionPoint.index] === clientId;
}, [clientId]);
const isLargeViewport = (0, _compose.useViewportMatch)('medium');
const [isToolbarForced, setIsToolbarForced] = (0, _element.useState)(false);
const [isInserterShown, setIsInserterShown] = (0, _element.useState)(false);
const {
stopTyping
} = (0, _data.useDispatch)(_store.store); // Controls when the side inserter on empty lines should
// be shown, including writing and selection modes.
const showEmptyBlockSideInserter = !isTyping && !isNavigationMode && isEmptyDefaultBlock && isValid;
const shouldShowBreadcrumb = isNavigationMode;
const shouldShowContextualToolbar = !isNavigationMode && !hasFixedToolbar && isLargeViewport && !showEmptyBlockSideInserter && !isMultiSelecting && (!isTyping || isCaretWithinFormattedText);
const canFocusHiddenToolbar = !isNavigationMode && !shouldShowContextualToolbar && !hasFixedToolbar && !isEmptyDefaultBlock;
(0, _keyboardShortcuts.useShortcut)('core/block-editor/focus-toolbar', () => {
setIsToolbarForced(true);
stopTyping(true);
}, {
isDisabled: !canFocusHiddenToolbar
});
(0, _element.useEffect)(() => {
if (!shouldShowContextualToolbar) {
setIsToolbarForced(false);
}
}, [shouldShowContextualToolbar]); // Stores the active toolbar item index so the block toolbar can return focus
// to it when re-mounting.
const initialToolbarItemIndexRef = (0, _element.useRef)();
const selectedElement = (0, _useBlockRefs.__unstableUseBlockElement)(clientId);
const lastSelectedElement = (0, _useBlockRefs.__unstableUseBlockElement)(lastClientId);
const capturingElement = (0, _useBlockRefs.__unstableUseBlockElement)(capturingClientId);
const popoverScrollRef = (0, _usePopoverScroll.usePopoverScroll)(__unstableContentRef);
if (!shouldShowBreadcrumb && !shouldShowContextualToolbar && !isToolbarForced && !showEmptyBlockSideInserter) {
return null;
}
let node = selectedElement;
if (!node) {
return null;
}
if (capturingClientId) {
node = capturingElement;
}
let anchorRef = node;
if (hasMultiSelection) {
// Wait to render the popover until the bottom reference is available
// as well.
if (!lastSelectedElement) {
return null;
}
anchorRef = {
top: node,
bottom: lastSelectedElement
};
}
function onFocus() {
setIsInserterShown(true);
}
function onBlur() {
setIsInserterShown(false);
} // Position above the anchor, pop out towards the right, and position in the
// left corner. For the side inserter, pop out towards the left, and
// position in the right corner.
// To do: refactor `Popover` to make this prop clearer.
const popoverPosition = showEmptyBlockSideInserter ? 'top left right' : 'top right left';
const {
ownerDocument
} = node;
const stickyBoundaryElement = showEmptyBlockSideInserter ? undefined : // The sticky boundary element should be the boundary at which the
// the block toolbar becomes sticky when the block scolls out of view.
// In case of an iframe, this should be the iframe boundary, otherwise
// the scroll container.
ownerDocument.defaultView.frameElement || (0, _dom.getScrollContainer)(node) || ownerDocument.body;
return (0, _element.createElement)(_components.Popover, {
ref: popoverScrollRef,
noArrow: true,
animate: false,
position: popoverPosition,
focusOnMount: false,
anchorRef: anchorRef,
className: (0, _classnames.default)('block-editor-block-list__block-popover', {
'is-insertion-point-visible': isInsertionPointVisible
}),
__unstableStickyBoundaryElement: stickyBoundaryElement // Render in the old slot if needed for backward compatibility,
// otherwise render in place (not in the the default popover slot).
,
__unstableSlotName: __unstablePopoverSlot || null,
__unstableBoundaryParent: true // Observe movement for block animations (especially horizontal).
,
__unstableObserveElement: node,
shouldAnchorIncludePadding: true // Used to safeguard sticky position behavior against cases where it would permanently
// obscure specific sections of a block.
,
__unstableEditorCanvasWrapper: __unstableContentRef === null || __unstableContentRef === void 0 ? void 0 : __unstableContentRef.current
}, (shouldShowContextualToolbar || isToolbarForced) && (0, _element.createElement)("div", {
onFocus: onFocus,
onBlur: onBlur // While ideally it would be enough to capture the
// bubbling focus event from the Inserter, due to the
// characteristics of click focusing of `button`s in
// Firefox and Safari, it is not reliable.
//
// See: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#Clicking_and_focus
,
tabIndex: -1,
className: (0, _classnames.default)('block-editor-block-list__block-popover-inserter', {
'is-visible': isInserterShown
})
}, (0, _element.createElement)(_inserter.default, {
clientId: clientId,
rootClientId: rootClientId,
__experimentalIsQuick: true
})), (shouldShowContextualToolbar || isToolbarForced) && (0, _element.createElement)(_blockContextualToolbar.default // If the toolbar is being shown because of being forced
// it should focus the toolbar right after the mount.
, {
focusOnMount: isToolbarForced,
__experimentalInitialIndex: initialToolbarItemIndexRef.current,
__experimentalOnIndexChange: index => {
initialToolbarItemIndexRef.current = index;
} // Resets the index whenever the active block changes so
// this is not persisted. See https://github.com/GeChiUI/gutenberg/pull/25760#issuecomment-717906169
,
key: clientId
}), shouldShowBreadcrumb && (0, _element.createElement)(_blockSelectionButton.default, {
clientId: clientId,
rootClientId: rootClientId,
blockElement: node
}), showEmptyBlockSideInserter && (0, _element.createElement)("div", {
className: "block-editor-block-list__empty-block-inserter"
}, (0, _element.createElement)(_inserter.default, {
position: "bottom right",
rootClientId: rootClientId,
clientId: clientId,
__experimentalIsQuick: true
})));
}
function wrapperSelector(select) {
const {
getSelectedBlockClientId,
getFirstMultiSelectedBlockClientId,
getBlockRootClientId,
getBlock,
getBlockParents,
__experimentalGetBlockListSettingsForBlocks
} = select(_store.store);
const clientId = getSelectedBlockClientId() || getFirstMultiSelectedBlockClientId();
if (!clientId) {
return;
}
const {
name,
attributes = {},
isValid
} = getBlock(clientId) || {};
const blockParentsClientIds = getBlockParents(clientId); // Get Block List Settings for all ancestors of the current Block clientId
const parentBlockListSettings = __experimentalGetBlockListSettingsForBlocks(blockParentsClientIds); // Get the clientId of the topmost parent with the capture toolbars setting.
const capturingClientId = (0, _lodash.find)(blockParentsClientIds, parentClientId => {
var _parentBlockListSetti;
return (_parentBlockListSetti = parentBlockListSettings[parentClientId]) === null || _parentBlockListSetti === void 0 ? void 0 : _parentBlockListSetti.__experimentalCaptureToolbars;
});
return {
clientId,
rootClientId: getBlockRootClientId(clientId),
name,
isValid,
isEmptyDefaultBlock: name && (0, _blocks.isUnmodifiedDefaultBlock)({
name,
attributes
}),
capturingClientId
};
}
function WrappedBlockPopover(_ref2) {
let {
__unstablePopoverSlot,
__unstableContentRef
} = _ref2;
const selected = (0, _data.useSelect)(wrapperSelector, []);
if (!selected) {
return null;
}
const {
clientId,
rootClientId,
name,
isValid,
isEmptyDefaultBlock,
capturingClientId
} = selected;
if (!name) {
return null;
}
return (0, _element.createElement)(BlockPopover, {
clientId: clientId,
rootClientId: rootClientId,
isValid: isValid,
isEmptyDefaultBlock: isEmptyDefaultBlock,
capturingClientId: capturingClientId,
__unstablePopoverSlot: __unstablePopoverSlot,
__unstableContentRef: __unstableContentRef
});
}
//# sourceMappingURL=block-popover.js.map