@udecode/plate-toolbar
Advanced tools
Comparing version 4.4.0 to 5.0.0
# @udecode/plate-toolbar | ||
## 5.0.0 | ||
### Major Changes | ||
- [#1086](https://github.com/udecode/plate/pull/1086) [`9a091446`](https://github.com/udecode/plate/commit/9a091446ae393c23f64f0b011e431fb2d002aaf8) Thanks [@zbeyens](https://github.com/zbeyens)! - changes: | ||
- removed `setPositionAtSelection` in favor of `useBalloonToolbarPopper` | ||
- removed `useBalloonMove` in favor of `useBalloonToolbarPopper` | ||
- removed `usePopupPosition` in favor of `useBalloonToolbarPopper` | ||
- removed `useBalloonShow` in favor of `useBalloonToolbarPopper` | ||
`BalloonToolbar` props: | ||
- removed `direction` in favor of `popperOptions.placement` | ||
- renamed `scrollContainer` to `popperContainer` | ||
### Minor Changes | ||
- [#1086](https://github.com/udecode/plate/pull/1086) [`9a091446`](https://github.com/udecode/plate/commit/9a091446ae393c23f64f0b011e431fb2d002aaf8) Thanks [@zbeyens](https://github.com/zbeyens)! - `BalloonToolbar` | ||
- new prop `popperOptions`: allow overriding `usePopper` options | ||
- now uses `useBalloonToolbarPopper` | ||
### Patch Changes | ||
- Updated dependencies [[`9a091446`](https://github.com/udecode/plate/commit/9a091446ae393c23f64f0b011e431fb2d002aaf8)]: | ||
- @udecode/plate-popper@5.0.0 | ||
## 4.4.0 | ||
@@ -4,0 +28,0 @@ |
import { ReactNode } from 'react'; | ||
import { Modifier } from '@popperjs/core'; | ||
import * as PopperJS from '@popperjs/core'; | ||
import { TEditor } from '@udecode/plate-core'; | ||
import { UsePopperPositionOptions } from '@udecode/plate-popper'; | ||
import { StyledProps } from '@udecode/plate-styled-components'; | ||
import { ToolbarProps } from '../Toolbar/Toolbar.types'; | ||
export interface BalloonToolbarStyleProps extends BalloonToolbarProps { | ||
hidden?: boolean; | ||
} | ||
@@ -13,6 +10,2 @@ export interface BalloonToolbarProps extends StyledProps<ToolbarProps> { | ||
/** | ||
* Below of above the selection. | ||
*/ | ||
direction?: UsePopupPositionProps['placement']; | ||
/** | ||
* Color theme for the background/foreground. | ||
@@ -26,29 +19,4 @@ */ | ||
portalElement?: Element; | ||
/** | ||
* Parent scroll container element of the editor, | ||
* if no scroll container provided, it will take document.documentElement as scrolling container | ||
*/ | ||
scrollContainer?: HTMLElement; | ||
popperOptions?: Partial<UsePopperPositionOptions>; | ||
} | ||
export interface UsePopupPositionProps { | ||
editor: TEditor; | ||
popupElem: HTMLElement | null; | ||
scrollContainer?: HTMLElement | null; | ||
modifiers?: Array<Partial<Modifier<string, any>>>; | ||
placement?: PopperJS.Placement; | ||
} | ||
export declare type UsePopupPositionReturnType = [ | ||
{ | ||
[key: string]: React.CSSProperties; | ||
}, | ||
{ | ||
[key: string]: { | ||
[key: string]: string; | ||
} | undefined; | ||
}, | ||
boolean | ||
]; | ||
export interface UsePopupPosition { | ||
({ editor, popupElem, scrollContainer, }: UsePopupPositionProps): UsePopupPositionReturnType; | ||
} | ||
//# sourceMappingURL=BalloonToolbar.types.d.ts.map |
@@ -7,6 +7,3 @@ /** | ||
export * from './BalloonToolbar.types'; | ||
export * from './setPositionAtSelection'; | ||
export * from './useBalloonMove'; | ||
export * from './useBalloonPosition'; | ||
export * from './useBalloonShow'; | ||
export * from './useBalloonToolbarPopper'; | ||
//# sourceMappingURL=index.d.ts.map |
import { createStyles, PortalBody } from '@udecode/plate-styled-components'; | ||
import _styled, { css } from 'styled-components'; | ||
import * as React from 'react'; | ||
import React__default, { useEffect as useEffect$1, useState as useState$1, useCallback as useCallback$1 } from 'react'; | ||
import { useStoreEditorState, useEventEditorId } from '@udecode/plate-core'; | ||
import { useState, useCallback, useEffect, useRef } from 'react'; | ||
import { jsx, jsxs, Fragment } from 'react/jsx-runtime'; | ||
import { usePopper } from 'react-popper'; | ||
import { isSelectionExpanded, getSelectionText, someNode, getPreventDefaultHandler, toggleNodeType, isMarkActive, toggleMark } from '@udecode/plate-common'; | ||
import { useStoreEditorState, useEventEditorId } from '@udecode/plate-core'; | ||
import { usePopperPosition, getSelectionBoundingClientRect } from '@udecode/plate-popper'; | ||
import Tippy from '@tippyjs/react'; | ||
@@ -27,2 +27,4 @@ | ||
const getBalloonToolbarStyles = props => { | ||
var _props$popperOptions; | ||
let color = 'rgb(157, 170, 182)'; | ||
@@ -39,4 +41,8 @@ let colorActive = 'white'; | ||
} | ||
const arrowStyle = [props.arrow && css(["::after{left:50%;content:' ';position:absolute;margin-top:-1px;transform:translateX(-50%);border-color:", " transparent;border-style:solid;}"], background), props.arrow && props.direction === 'top' && css(["::after{top:100%;bottom:auto;border-width:8px 8px 0;}"]), props.arrow && props.direction !== 'top' && css(["::after{top:auto;bottom:100%;border-width:0 8px 8px;}"])]; | ||
const arrowBorderStyle = [props.arrow && props.direction === 'top' && props.theme === 'light' && css(["::before{margin-top:0;border-width:9px 9px 0;border-color:", " transparent;}"], borderColor), props.arrow && props.direction !== 'top' && props.theme === 'light' && css(["::before{margin-top:0;border-width:0 9px 9px;border-color:", " transparent;}"], borderColor)]; | ||
const { | ||
placement = 'top' | ||
} = (_props$popperOptions = props.popperOptions) !== null && _props$popperOptions !== void 0 ? _props$popperOptions : {}; | ||
const arrowStyle = [props.arrow && css(["::after{left:50%;content:' ';position:absolute;margin-top:-1px;transform:translateX(-50%);border-color:", " transparent;border-style:solid;}"], background), props.arrow && placement.includes('top') && css(["::after{top:100%;bottom:auto;border-width:8px 8px 0;}"]), props.arrow && !placement.includes('top') && css(["::after{top:auto;bottom:100%;border-width:0 8px 8px;}"])]; | ||
const arrowBorderStyle = [props.arrow && placement.includes('top') && props.theme === 'light' && css(["::before{margin-top:0;border-width:9px 9px 0;border-color:", " transparent;}"], borderColor), props.arrow && !placement.includes('top') && props.theme === 'light' && css(["::before{margin-top:0;border-width:0 9px 9px;border-color:", " transparent;}"], borderColor)]; | ||
return createStyles({ | ||
@@ -51,5 +57,2 @@ prefixClassNames: 'BalloonToolbar', | ||
"transition": "opacity .2s ease-in-out" | ||
}, props.hidden && { | ||
"visibility": "hidden", | ||
"opacity": "0" | ||
}, css(["color:", ";background:", ";z-index:500;border:1px solid ", ";border-radius:4px;.slate-ToolbarButton-active,.slate-ToolbarButton:hover{color:", ";}::before{", "}"], color, background, borderColor, colorActive, arrowBorderStyle), ...arrowStyle, ...arrowBorderStyle] | ||
@@ -65,9 +68,3 @@ }); | ||
}); | ||
}); // export const withStyles = <T,>( | ||
// Component: FunctionComponent<T>, | ||
// styles | ||
// ) => | ||
// React.forwardRef<HTMLElement, T>((props: T, ref) => ( | ||
// <Component {...props} ref={ref} styles={getToolbarStyles(props)} /> | ||
// )); | ||
}); | ||
@@ -90,98 +87,30 @@ var _StyledToolbarBase$2 = _styled(ToolbarBase).withConfig({ | ||
const { | ||
useEffect, | ||
useState, | ||
useCallback | ||
} = React; | ||
const virtualReference = { | ||
getBoundingClientRect() { | ||
return { | ||
top: 10, | ||
left: 10, | ||
bottom: 20, | ||
right: 100, | ||
width: 90, | ||
height: 10, | ||
x: 0, | ||
y: 0, | ||
toJSON: () => null | ||
}; | ||
} | ||
}; | ||
const usePopupPosition = ({ | ||
editor, | ||
popupElem, | ||
scrollContainer = document.documentElement, | ||
modifiers = [], | ||
placement = 'top' | ||
}) => { | ||
const [isHide, setIsHide] = useState(true); | ||
const useBalloonToolbarPopper = options => { | ||
const editor = useStoreEditorState(useEventEditorId('focus')); | ||
const [isHidden, setIsHidden] = useState(true); | ||
const selectionExpanded = editor && isSelectionExpanded(editor); | ||
const selectionText = editor && getSelectionText(editor); | ||
const { | ||
styles, | ||
attributes, | ||
update | ||
} = usePopper(virtualReference, popupElem, { | ||
placement, | ||
modifiers: [// default modifiers to position the popup correctly | ||
{ | ||
name: 'preventOverflow', | ||
enabled: true, | ||
options: { | ||
boundary: scrollContainer !== null && scrollContainer !== void 0 ? scrollContainer : undefined | ||
} | ||
}, { | ||
name: 'flip', | ||
enabled: true, | ||
options: { | ||
padding: 8 | ||
} | ||
}, { | ||
name: 'eventListeners', | ||
enabled: true, | ||
options: { | ||
scroll: !isHide, | ||
resize: true | ||
} | ||
}, { | ||
name: 'offset', | ||
options: { | ||
offset: [0, 8] | ||
} | ||
}, // user modifiers to override the default | ||
...modifiers], | ||
strategy: 'absolute' | ||
}); | ||
const show = useCallback(() => { | ||
if (isHide && selectionExpanded) { | ||
setIsHide(false); | ||
if (isHidden && selectionExpanded) { | ||
setIsHidden(false); | ||
} | ||
}, [isHide, selectionExpanded, setIsHide]); | ||
}, [isHidden, selectionExpanded]); | ||
useEffect(() => { | ||
if (!selectionText) { | ||
setIsHide(true); | ||
setIsHidden(true); | ||
} else if (selectionText && selectionExpanded) { | ||
setIsHide(false); | ||
setIsHidden(false); | ||
} | ||
}, [selectionText, show, selectionExpanded, setIsHide]); | ||
const setPosition = useCallback(() => { | ||
const domSelection = window.getSelection(); | ||
if (!domSelection || domSelection.rangeCount < 1) return; | ||
const domRange = domSelection.getRangeAt(0); | ||
const rect = domRange.getBoundingClientRect(); | ||
}, [selectionExpanded, selectionText, show]); | ||
const popperResult = usePopperPosition({ | ||
isHidden, | ||
getBoundingClientRect: getSelectionBoundingClientRect, | ||
...options | ||
}); | ||
useEffect(() => { | ||
var _popperResult$update; | ||
virtualReference.getBoundingClientRect = () => rect; | ||
update === null || update === void 0 ? void 0 : update(); | ||
}, [update]); | ||
useEffect(() => { | ||
scrollContainer === null || scrollContainer === void 0 ? void 0 : scrollContainer.addEventListener('scroll', setPosition); | ||
return () => scrollContainer === null || scrollContainer === void 0 ? void 0 : scrollContainer.removeEventListener('scroll', setPosition); | ||
}, [setPosition, scrollContainer]); | ||
useEffect(() => { | ||
popupElem && selectionExpanded && setPosition(); | ||
}, [selectionText === null || selectionText === void 0 ? void 0 : selectionText.length, selectionExpanded, popupElem, setPosition]); | ||
return [styles, attributes, isHide]; | ||
selectionExpanded && ((_popperResult$update = popperResult.update) === null || _popperResult$update === void 0 ? void 0 : _popperResult$update.call(popperResult)); | ||
}, [selectionText === null || selectionText === void 0 ? void 0 : selectionText.length, selectionExpanded, popperResult, selectionText]); | ||
return popperResult; | ||
}; | ||
@@ -192,21 +121,22 @@ | ||
children, | ||
direction = 'top', | ||
theme = 'dark', | ||
arrow = false, | ||
portalElement, | ||
scrollContainer | ||
popperOptions: _popperOptions = {} | ||
} = props; | ||
const popupRef = React.useRef(null); | ||
const editor = useStoreEditorState(useEventEditorId('focus')); | ||
const [popperStyles, attributes, hidden] = usePopupPosition({ | ||
editor, | ||
popupElem: popupRef.current, | ||
scrollContainer, | ||
placement: direction | ||
}); | ||
const popperRef = useRef(null); | ||
const popperOptions = { | ||
popperElement: popperRef.current, | ||
placement: 'top', | ||
offset: [0, 8], | ||
..._popperOptions | ||
}; | ||
const { | ||
styles: popperStyles, | ||
attributes | ||
} = useBalloonToolbarPopper(popperOptions); | ||
const styles = getBalloonToolbarStyles({ | ||
direction, | ||
popperOptions, | ||
theme, | ||
arrow, | ||
hidden, | ||
...props | ||
@@ -217,3 +147,3 @@ }); | ||
children: /*#__PURE__*/jsx(_StyledToolbarBase$1, { | ||
ref: popupRef, | ||
ref: popperRef, | ||
className: styles.root.className, | ||
@@ -233,130 +163,2 @@ style: popperStyles.popper, | ||
const setPositionAtSelection = (el, direction = 'top') => { | ||
const domSelection = window.getSelection(); | ||
if (!domSelection || domSelection.rangeCount < 1) return; | ||
const domRange = domSelection.getRangeAt(0); | ||
const rect = domRange.getBoundingClientRect(); | ||
if (direction === 'top') { | ||
el.style.top = `${rect.top + window.pageYOffset - el.offsetHeight}px`; | ||
} else { | ||
el.style.top = `${rect.bottom + window.pageYOffset}px`; | ||
} | ||
el.style.left = `${rect.left + window.pageXOffset - el.offsetWidth / 2 + rect.width / 2}px`; | ||
}; | ||
/** | ||
* Move when the selection changes. | ||
*/ | ||
const useBalloonMove = ({ | ||
editor, | ||
ref, | ||
direction | ||
}) => { | ||
const selectionExpanded = editor && isSelectionExpanded(editor); | ||
const selectionText = editor && getSelectionText(editor); | ||
useEffect$1(() => { | ||
ref.current && selectionExpanded && setPositionAtSelection(ref.current, direction); | ||
}, [direction, selectionText === null || selectionText === void 0 ? void 0 : selectionText.length, selectionExpanded, ref]); | ||
}; | ||
function unwrapExports (x) { | ||
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x; | ||
} | ||
function createCommonjsModule(fn, module) { | ||
return module = { exports: {} }, fn(module, module.exports), module.exports; | ||
} | ||
var useTimeoutFn_1 = createCommonjsModule(function (module, exports) { | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
function useTimeoutFn(fn, ms) { | ||
if (ms === void 0) { ms = 0; } | ||
var ready = React__default.useRef(false); | ||
var timeout = React__default.useRef(); | ||
var callback = React__default.useRef(fn); | ||
var isReady = React__default.useCallback(function () { return ready.current; }, []); | ||
var set = React__default.useCallback(function () { | ||
ready.current = false; | ||
timeout.current && clearTimeout(timeout.current); | ||
timeout.current = setTimeout(function () { | ||
ready.current = true; | ||
callback.current(); | ||
}, ms); | ||
}, [ms]); | ||
var clear = React__default.useCallback(function () { | ||
ready.current = null; | ||
timeout.current && clearTimeout(timeout.current); | ||
}, []); | ||
// update ref when function changes | ||
React__default.useEffect(function () { | ||
callback.current = fn; | ||
}, [fn]); | ||
// set on mount, clear on unmount | ||
React__default.useEffect(function () { | ||
set(); | ||
return clear; | ||
}, [ms]); | ||
return [isReady, clear, set]; | ||
} | ||
exports.default = useTimeoutFn; | ||
}); | ||
var _useTimeoutFn = unwrapExports(useTimeoutFn_1); | ||
/** | ||
* Hide if not selecting. | ||
* If hiddenDelay = 0 and the selection changes: show. | ||
* If hiddenDelay > 0: hide when the selection length changes. | ||
*/ | ||
const useBalloonShow = ({ | ||
editor, | ||
ref, | ||
hiddenDelay | ||
}) => { | ||
const [hidden, setHidden] = useState$1(true); | ||
const selectionExpanded = editor && isSelectionExpanded(editor); | ||
const selectionText = editor && getSelectionText(editor); | ||
const show = useCallback$1(() => { | ||
if (ref.current && hidden && selectionExpanded) { | ||
setHidden(false); | ||
} | ||
}, [hidden, ref, selectionExpanded]); | ||
const [,, reset] = _useTimeoutFn(show, hiddenDelay); | ||
useEffect$1(() => { | ||
if (!hiddenDelay) { | ||
show(); | ||
} | ||
}, [selectionText === null || selectionText === void 0 ? void 0 : selectionText.length, reset, hiddenDelay, show]); | ||
/** | ||
* Hide if not selecting. | ||
*/ | ||
useEffect$1(() => { | ||
if (!hidden && !selectionExpanded) { | ||
setHidden(true); | ||
if (ref.current) { | ||
ref.current.removeAttribute('style'); | ||
} | ||
} | ||
}, [hidden, hiddenDelay, reset, selectionExpanded, show, selectionText === null || selectionText === void 0 ? void 0 : selectionText.length, ref]); | ||
/** | ||
* If hiddenDelay > 0: | ||
* Hide when the selection length changes. | ||
*/ | ||
useEffect$1(() => { | ||
if (!hiddenDelay) return; | ||
reset(); | ||
setHidden(true); | ||
}, [hiddenDelay, selectionText === null || selectionText === void 0 ? void 0 : selectionText.length, reset]); | ||
return [hidden]; | ||
}; | ||
const getHeadingToolbarStyles = props => createStyles({ | ||
@@ -504,6 +306,6 @@ prefixClassNames: 'HeadingToolbar', | ||
}) => { | ||
const [referenceElement, setReferenceElement] = useState$1(null); | ||
const [popperElement, setPopperElement] = useState$1(null); | ||
const [open, setOpen] = useState$1(false); | ||
useEffect$1(() => { | ||
const [referenceElement, setReferenceElement] = useState(null); | ||
const [popperElement, setPopperElement] = useState(null); | ||
const [open, setOpen] = useState(false); | ||
useEffect(() => { | ||
const listener = ev => { | ||
@@ -594,3 +396,3 @@ if (open) { | ||
export { BalloonToolbar, HeadingToolbar, Toolbar, ToolbarBase, ToolbarButton, ToolbarDropdown, ToolbarElement, ToolbarMark, getBalloonToolbarStyles, getHeadingToolbarStyles, getToolbarButtonStyles, getToolbarStyles, setPositionAtSelection, useBalloonMove, useBalloonShow, usePopupPosition }; | ||
export { BalloonToolbar, HeadingToolbar, Toolbar, ToolbarBase, ToolbarButton, ToolbarDropdown, ToolbarElement, ToolbarMark, getBalloonToolbarStyles, getHeadingToolbarStyles, getToolbarButtonStyles, getToolbarStyles, useBalloonToolbarPopper }; | ||
//# sourceMappingURL=index.es.js.map |
@@ -8,6 +8,6 @@ 'use strict'; | ||
var React = require('react'); | ||
var plateCore = require('@udecode/plate-core'); | ||
var jsxRuntime = require('react/jsx-runtime'); | ||
var reactPopper = require('react-popper'); | ||
var plateCommon = require('@udecode/plate-common'); | ||
var plateCore = require('@udecode/plate-core'); | ||
var platePopper = require('@udecode/plate-popper'); | ||
var Tippy = require('@tippyjs/react'); | ||
@@ -39,3 +39,2 @@ | ||
var React__namespace = /*#__PURE__*/_interopNamespace(React); | ||
var React__default = /*#__PURE__*/_interopDefaultLegacy(React); | ||
var Tippy__default = /*#__PURE__*/_interopDefaultLegacy(Tippy); | ||
@@ -59,2 +58,4 @@ | ||
const getBalloonToolbarStyles = props => { | ||
var _props$popperOptions; | ||
let color = 'rgb(157, 170, 182)'; | ||
@@ -71,4 +72,8 @@ let colorActive = 'white'; | ||
} | ||
const arrowStyle = [props.arrow && _styled.css(["::after{left:50%;content:' ';position:absolute;margin-top:-1px;transform:translateX(-50%);border-color:", " transparent;border-style:solid;}"], background), props.arrow && props.direction === 'top' && _styled.css(["::after{top:100%;bottom:auto;border-width:8px 8px 0;}"]), props.arrow && props.direction !== 'top' && _styled.css(["::after{top:auto;bottom:100%;border-width:0 8px 8px;}"])]; | ||
const arrowBorderStyle = [props.arrow && props.direction === 'top' && props.theme === 'light' && _styled.css(["::before{margin-top:0;border-width:9px 9px 0;border-color:", " transparent;}"], borderColor), props.arrow && props.direction !== 'top' && props.theme === 'light' && _styled.css(["::before{margin-top:0;border-width:0 9px 9px;border-color:", " transparent;}"], borderColor)]; | ||
const { | ||
placement = 'top' | ||
} = (_props$popperOptions = props.popperOptions) !== null && _props$popperOptions !== void 0 ? _props$popperOptions : {}; | ||
const arrowStyle = [props.arrow && _styled.css(["::after{left:50%;content:' ';position:absolute;margin-top:-1px;transform:translateX(-50%);border-color:", " transparent;border-style:solid;}"], background), props.arrow && placement.includes('top') && _styled.css(["::after{top:100%;bottom:auto;border-width:8px 8px 0;}"]), props.arrow && !placement.includes('top') && _styled.css(["::after{top:auto;bottom:100%;border-width:0 8px 8px;}"])]; | ||
const arrowBorderStyle = [props.arrow && placement.includes('top') && props.theme === 'light' && _styled.css(["::before{margin-top:0;border-width:9px 9px 0;border-color:", " transparent;}"], borderColor), props.arrow && !placement.includes('top') && props.theme === 'light' && _styled.css(["::before{margin-top:0;border-width:0 9px 9px;border-color:", " transparent;}"], borderColor)]; | ||
return plateStyledComponents.createStyles({ | ||
@@ -83,5 +88,2 @@ prefixClassNames: 'BalloonToolbar', | ||
"transition": "opacity .2s ease-in-out" | ||
}, props.hidden && { | ||
"visibility": "hidden", | ||
"opacity": "0" | ||
}, _styled.css(["color:", ";background:", ";z-index:500;border:1px solid ", ";border-radius:4px;.slate-ToolbarButton-active,.slate-ToolbarButton:hover{color:", ";}::before{", "}"], color, background, borderColor, colorActive, arrowBorderStyle), ...arrowStyle, ...arrowBorderStyle] | ||
@@ -97,9 +99,3 @@ }); | ||
}); | ||
}); // export const withStyles = <T,>( | ||
// Component: FunctionComponent<T>, | ||
// styles | ||
// ) => | ||
// React.forwardRef<HTMLElement, T>((props: T, ref) => ( | ||
// <Component {...props} ref={ref} styles={getToolbarStyles(props)} /> | ||
// )); | ||
}); | ||
@@ -122,98 +118,30 @@ var _StyledToolbarBase$2 = _styled__default['default'](ToolbarBase).withConfig({ | ||
const { | ||
useEffect, | ||
useState, | ||
useCallback | ||
} = React__namespace; | ||
const virtualReference = { | ||
getBoundingClientRect() { | ||
return { | ||
top: 10, | ||
left: 10, | ||
bottom: 20, | ||
right: 100, | ||
width: 90, | ||
height: 10, | ||
x: 0, | ||
y: 0, | ||
toJSON: () => null | ||
}; | ||
} | ||
}; | ||
const usePopupPosition = ({ | ||
editor, | ||
popupElem, | ||
scrollContainer = document.documentElement, | ||
modifiers = [], | ||
placement = 'top' | ||
}) => { | ||
const [isHide, setIsHide] = useState(true); | ||
const useBalloonToolbarPopper = options => { | ||
const editor = plateCore.useStoreEditorState(plateCore.useEventEditorId('focus')); | ||
const [isHidden, setIsHidden] = React.useState(true); | ||
const selectionExpanded = editor && plateCommon.isSelectionExpanded(editor); | ||
const selectionText = editor && plateCommon.getSelectionText(editor); | ||
const { | ||
styles, | ||
attributes, | ||
update | ||
} = reactPopper.usePopper(virtualReference, popupElem, { | ||
placement, | ||
modifiers: [// default modifiers to position the popup correctly | ||
{ | ||
name: 'preventOverflow', | ||
enabled: true, | ||
options: { | ||
boundary: scrollContainer !== null && scrollContainer !== void 0 ? scrollContainer : undefined | ||
} | ||
}, { | ||
name: 'flip', | ||
enabled: true, | ||
options: { | ||
padding: 8 | ||
} | ||
}, { | ||
name: 'eventListeners', | ||
enabled: true, | ||
options: { | ||
scroll: !isHide, | ||
resize: true | ||
} | ||
}, { | ||
name: 'offset', | ||
options: { | ||
offset: [0, 8] | ||
} | ||
}, // user modifiers to override the default | ||
...modifiers], | ||
strategy: 'absolute' | ||
}); | ||
const show = useCallback(() => { | ||
if (isHide && selectionExpanded) { | ||
setIsHide(false); | ||
const show = React.useCallback(() => { | ||
if (isHidden && selectionExpanded) { | ||
setIsHidden(false); | ||
} | ||
}, [isHide, selectionExpanded, setIsHide]); | ||
useEffect(() => { | ||
}, [isHidden, selectionExpanded]); | ||
React.useEffect(() => { | ||
if (!selectionText) { | ||
setIsHide(true); | ||
setIsHidden(true); | ||
} else if (selectionText && selectionExpanded) { | ||
setIsHide(false); | ||
setIsHidden(false); | ||
} | ||
}, [selectionText, show, selectionExpanded, setIsHide]); | ||
const setPosition = useCallback(() => { | ||
const domSelection = window.getSelection(); | ||
if (!domSelection || domSelection.rangeCount < 1) return; | ||
const domRange = domSelection.getRangeAt(0); | ||
const rect = domRange.getBoundingClientRect(); | ||
}, [selectionExpanded, selectionText, show]); | ||
const popperResult = platePopper.usePopperPosition({ | ||
isHidden, | ||
getBoundingClientRect: platePopper.getSelectionBoundingClientRect, | ||
...options | ||
}); | ||
React.useEffect(() => { | ||
var _popperResult$update; | ||
virtualReference.getBoundingClientRect = () => rect; | ||
update === null || update === void 0 ? void 0 : update(); | ||
}, [update]); | ||
useEffect(() => { | ||
scrollContainer === null || scrollContainer === void 0 ? void 0 : scrollContainer.addEventListener('scroll', setPosition); | ||
return () => scrollContainer === null || scrollContainer === void 0 ? void 0 : scrollContainer.removeEventListener('scroll', setPosition); | ||
}, [setPosition, scrollContainer]); | ||
useEffect(() => { | ||
popupElem && selectionExpanded && setPosition(); | ||
}, [selectionText === null || selectionText === void 0 ? void 0 : selectionText.length, selectionExpanded, popupElem, setPosition]); | ||
return [styles, attributes, isHide]; | ||
selectionExpanded && ((_popperResult$update = popperResult.update) === null || _popperResult$update === void 0 ? void 0 : _popperResult$update.call(popperResult)); | ||
}, [selectionText === null || selectionText === void 0 ? void 0 : selectionText.length, selectionExpanded, popperResult, selectionText]); | ||
return popperResult; | ||
}; | ||
@@ -224,21 +152,22 @@ | ||
children, | ||
direction = 'top', | ||
theme = 'dark', | ||
arrow = false, | ||
portalElement, | ||
scrollContainer | ||
popperOptions: _popperOptions = {} | ||
} = props; | ||
const popupRef = React__namespace.useRef(null); | ||
const editor = plateCore.useStoreEditorState(plateCore.useEventEditorId('focus')); | ||
const [popperStyles, attributes, hidden] = usePopupPosition({ | ||
editor, | ||
popupElem: popupRef.current, | ||
scrollContainer, | ||
placement: direction | ||
}); | ||
const popperRef = React.useRef(null); | ||
const popperOptions = { | ||
popperElement: popperRef.current, | ||
placement: 'top', | ||
offset: [0, 8], | ||
..._popperOptions | ||
}; | ||
const { | ||
styles: popperStyles, | ||
attributes | ||
} = useBalloonToolbarPopper(popperOptions); | ||
const styles = getBalloonToolbarStyles({ | ||
direction, | ||
popperOptions, | ||
theme, | ||
arrow, | ||
hidden, | ||
...props | ||
@@ -249,3 +178,3 @@ }); | ||
children: /*#__PURE__*/jsxRuntime.jsx(_StyledToolbarBase$1, { | ||
ref: popupRef, | ||
ref: popperRef, | ||
className: styles.root.className, | ||
@@ -265,130 +194,2 @@ style: popperStyles.popper, | ||
const setPositionAtSelection = (el, direction = 'top') => { | ||
const domSelection = window.getSelection(); | ||
if (!domSelection || domSelection.rangeCount < 1) return; | ||
const domRange = domSelection.getRangeAt(0); | ||
const rect = domRange.getBoundingClientRect(); | ||
if (direction === 'top') { | ||
el.style.top = `${rect.top + window.pageYOffset - el.offsetHeight}px`; | ||
} else { | ||
el.style.top = `${rect.bottom + window.pageYOffset}px`; | ||
} | ||
el.style.left = `${rect.left + window.pageXOffset - el.offsetWidth / 2 + rect.width / 2}px`; | ||
}; | ||
/** | ||
* Move when the selection changes. | ||
*/ | ||
const useBalloonMove = ({ | ||
editor, | ||
ref, | ||
direction | ||
}) => { | ||
const selectionExpanded = editor && plateCommon.isSelectionExpanded(editor); | ||
const selectionText = editor && plateCommon.getSelectionText(editor); | ||
React.useEffect(() => { | ||
ref.current && selectionExpanded && setPositionAtSelection(ref.current, direction); | ||
}, [direction, selectionText === null || selectionText === void 0 ? void 0 : selectionText.length, selectionExpanded, ref]); | ||
}; | ||
function unwrapExports (x) { | ||
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x; | ||
} | ||
function createCommonjsModule(fn, module) { | ||
return module = { exports: {} }, fn(module, module.exports), module.exports; | ||
} | ||
var useTimeoutFn_1 = createCommonjsModule(function (module, exports) { | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
function useTimeoutFn(fn, ms) { | ||
if (ms === void 0) { ms = 0; } | ||
var ready = React__default['default'].useRef(false); | ||
var timeout = React__default['default'].useRef(); | ||
var callback = React__default['default'].useRef(fn); | ||
var isReady = React__default['default'].useCallback(function () { return ready.current; }, []); | ||
var set = React__default['default'].useCallback(function () { | ||
ready.current = false; | ||
timeout.current && clearTimeout(timeout.current); | ||
timeout.current = setTimeout(function () { | ||
ready.current = true; | ||
callback.current(); | ||
}, ms); | ||
}, [ms]); | ||
var clear = React__default['default'].useCallback(function () { | ||
ready.current = null; | ||
timeout.current && clearTimeout(timeout.current); | ||
}, []); | ||
// update ref when function changes | ||
React__default['default'].useEffect(function () { | ||
callback.current = fn; | ||
}, [fn]); | ||
// set on mount, clear on unmount | ||
React__default['default'].useEffect(function () { | ||
set(); | ||
return clear; | ||
}, [ms]); | ||
return [isReady, clear, set]; | ||
} | ||
exports.default = useTimeoutFn; | ||
}); | ||
var _useTimeoutFn = unwrapExports(useTimeoutFn_1); | ||
/** | ||
* Hide if not selecting. | ||
* If hiddenDelay = 0 and the selection changes: show. | ||
* If hiddenDelay > 0: hide when the selection length changes. | ||
*/ | ||
const useBalloonShow = ({ | ||
editor, | ||
ref, | ||
hiddenDelay | ||
}) => { | ||
const [hidden, setHidden] = React.useState(true); | ||
const selectionExpanded = editor && plateCommon.isSelectionExpanded(editor); | ||
const selectionText = editor && plateCommon.getSelectionText(editor); | ||
const show = React.useCallback(() => { | ||
if (ref.current && hidden && selectionExpanded) { | ||
setHidden(false); | ||
} | ||
}, [hidden, ref, selectionExpanded]); | ||
const [,, reset] = _useTimeoutFn(show, hiddenDelay); | ||
React.useEffect(() => { | ||
if (!hiddenDelay) { | ||
show(); | ||
} | ||
}, [selectionText === null || selectionText === void 0 ? void 0 : selectionText.length, reset, hiddenDelay, show]); | ||
/** | ||
* Hide if not selecting. | ||
*/ | ||
React.useEffect(() => { | ||
if (!hidden && !selectionExpanded) { | ||
setHidden(true); | ||
if (ref.current) { | ||
ref.current.removeAttribute('style'); | ||
} | ||
} | ||
}, [hidden, hiddenDelay, reset, selectionExpanded, show, selectionText === null || selectionText === void 0 ? void 0 : selectionText.length, ref]); | ||
/** | ||
* If hiddenDelay > 0: | ||
* Hide when the selection length changes. | ||
*/ | ||
React.useEffect(() => { | ||
if (!hiddenDelay) return; | ||
reset(); | ||
setHidden(true); | ||
}, [hiddenDelay, selectionText === null || selectionText === void 0 ? void 0 : selectionText.length, reset]); | ||
return [hidden]; | ||
}; | ||
const getHeadingToolbarStyles = props => plateStyledComponents.createStyles({ | ||
@@ -637,6 +438,3 @@ prefixClassNames: 'HeadingToolbar', | ||
exports.getToolbarStyles = getToolbarStyles; | ||
exports.setPositionAtSelection = setPositionAtSelection; | ||
exports.useBalloonMove = useBalloonMove; | ||
exports.useBalloonShow = useBalloonShow; | ||
exports.usePopupPosition = usePopupPosition; | ||
exports.useBalloonToolbarPopper = useBalloonToolbarPopper; | ||
//# sourceMappingURL=index.js.map |
{ | ||
"name": "@udecode/plate-toolbar", | ||
"version": "4.4.0", | ||
"version": "5.0.0", | ||
"description": "Toolbar UI for Plate", | ||
@@ -35,3 +35,2 @@ "keywords": [ | ||
"dependencies": { | ||
"@popperjs/core": "^2.8.3", | ||
"@tippyjs/react": "^4.2.0", | ||
@@ -41,2 +40,3 @@ "@udecode/plate-common": "4.4.0", | ||
"react-popper": "^2.2.4", | ||
"@udecode/plate-popper": "5.0.0", | ||
"@udecode/plate-styled-components": "4.4.0", | ||
@@ -43,0 +43,0 @@ "react-use": "^17.1.1" |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
101687
57
906
+ Added@udecode/plate-popper@5.0.0
+ Added@popperjs/core@2.10.2(transitive)
+ Added@udecode/plate-popper@5.0.0(transitive)
+ Addedobject-assign@4.1.1(transitive)
+ Addedreact@17.0.2(transitive)
+ Addedreact-popper@2.2.5(transitive)
- Removed@popperjs/core@^2.8.3
- Removed@popperjs/core@2.11.8(transitive)