diff --git a/.changeset/witty-tips-work.md b/.changeset/witty-tips-work.md new file mode 100644 index 000000000..ea02ff2b1 --- /dev/null +++ b/.changeset/witty-tips-work.md @@ -0,0 +1,5 @@ +--- +'slate-react': patch +--- + +Improve compatibility for browsers that do not support ResizeObserver or :where selector diff --git a/packages/slate-react/package.json b/packages/slate-react/package.json index 8cbe696b1..37fd8415c 100644 --- a/packages/slate-react/package.json +++ b/packages/slate-react/package.json @@ -14,6 +14,7 @@ "dist/" ], "dependencies": { + "@juggle/resize-observer": "^3.4.0", "@types/is-hotkey": "^0.1.1", "@types/lodash": "^4.14.149", "direction": "^1.0.3", diff --git a/packages/slate-react/src/components/editable.tsx b/packages/slate-react/src/components/editable.tsx index 09d6598a9..adad6b3dd 100644 --- a/packages/slate-react/src/components/editable.tsx +++ b/packages/slate-react/src/components/editable.tsx @@ -66,6 +66,7 @@ import { NODE_TO_ELEMENT, PLACEHOLDER_SYMBOL, } from '../utils/weak-maps' +import { whereIfSupported } from '../utils/where-if-supported' import { RestoreDOM } from './restore-dom/restore-dom' import { useAndroidInputManager } from '../hooks/android-input-manager/use-android-input-manager' import { useTrackUserInput } from '../hooks/use-track-user-input' @@ -812,9 +813,8 @@ export const Editable = (props: EditableProps) => { // Set global default styles for editors. const defaultStylesElement = document.createElement('style') defaultStylesElement.setAttribute('data-slate-default-styles', 'true') - defaultStylesElement.innerHTML = - // :where is used to give these rules lower specificity so user stylesheets can override them. - `:where([data-slate-editor]) {` + + const selector = '[data-slate-editor]' + const defaultStyles = // Allow positioning relative to the editable element. `position: relative;` + // Prevent the default outline styles. @@ -822,8 +822,9 @@ export const Editable = (props: EditableProps) => { // Preserve adjacent whitespace and new lines. `white-space: pre-wrap;` + // Allow words to break if they are too long. - `word-wrap: break-word;` + - `}` + `word-wrap: break-word;` + defaultStylesElement.innerHTML = whereIfSupported(selector, defaultStyles) + document.head.appendChild(defaultStylesElement) } diff --git a/packages/slate-react/src/components/leaf.tsx b/packages/slate-react/src/components/leaf.tsx index 823a56c9b..bdf2c5575 100644 --- a/packages/slate-react/src/components/leaf.tsx +++ b/packages/slate-react/src/components/leaf.tsx @@ -1,5 +1,6 @@ import React, { useRef, useEffect } from 'react' import { Element, Text } from 'slate' +import { ResizeObserver as ResizeObserverPolyfill } from '@juggle/resize-observer' import String from './string' import { PLACEHOLDER_SYMBOL, @@ -8,6 +9,7 @@ import { } from '../utils/weak-maps' import { RenderLeafProps, RenderPlaceholderProps } from './editable' import { useSlateStatic } from '../hooks/use-slate-static' +import { whereIfSupported } from '../utils/where-if-supported' /** * Individual leaves in a text node with unique formatting. @@ -59,12 +61,14 @@ const Leaf = (props: { placeholderResizeObserver.current.observe(placeholderEl) } else if (placeholderEl) { // Create a new observer and observe the placeholder element. + const ResizeObserver = window.ResizeObserver || ResizeObserverPolyfill placeholderResizeObserver.current = new ResizeObserver(([{ target }]) => { const styleElement = EDITOR_TO_STYLE_ELEMENT.get(editor) if (styleElement) { // Make the min-height the height of the placeholder. - const minHeight = `${target.clientHeight}px` - styleElement.innerHTML = `:where([data-slate-editor-id="${editor.id}"]) { min-height: ${minHeight}; }` + const selector = `[data-slate-editor-id="${editor.id}"]` + const styles = `min-height: ${target.clientHeight}px;` + styleElement.innerHTML = whereIfSupported(selector, styles) } }) diff --git a/packages/slate-react/src/utils/where-if-supported.ts b/packages/slate-react/src/utils/where-if-supported.ts new file mode 100644 index 000000000..df6ae7b72 --- /dev/null +++ b/packages/slate-react/src/utils/where-if-supported.ts @@ -0,0 +1,22 @@ +/** + * Returns a set of rules that use the `:where` selector if it is supported, + * otherwise it falls back to the provided selector on its own. + * + * The `:where` selector is used to give a selector a lower specificity, + * allowing the rule to be overridden by a user-defined stylesheet. + * + * Older browsers do not support the `:where` selector. + * If it is not supported, the selector will be used without `:where`, + * which means that the rule will have a higher specificity and a user-defined + * stylesheet will not be able to override it easily. + */ +export function whereIfSupported(selector: string, styles: string): string { + return ( + `@supports (selector(:where(${selector}))) {` + + `:where(${selector}) { ${styles} }` + + `}` + + `@supports not (selector(:where(${selector}))) {` + + `${selector} { ${styles} }` + + `}` + ) +} diff --git a/yarn.lock b/yarn.lock index 663298006..c393a3c5c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2268,6 +2268,13 @@ __metadata: languageName: node linkType: hard +"@juggle/resize-observer@npm:^3.4.0": + version: 3.4.0 + resolution: "@juggle/resize-observer@npm:3.4.0" + checksum: 2505028c05cc2e17639fcad06218b1c4b60f932a4ebb4b41ab546ef8c157031ae377e3f560903801f6d01706dbefd4943b6c4704bf19ed86dfa1c62f1473a570 + languageName: node + linkType: hard + "@lerna/add@npm:3.21.0": version: 3.21.0 resolution: "@lerna/add@npm:3.21.0" @@ -13904,6 +13911,7 @@ resolve@^2.0.0-next.3: resolution: "slate-react@workspace:packages/slate-react" dependencies: "@babel/runtime": ^7.7.4 + "@juggle/resize-observer": ^3.4.0 "@types/is-hotkey": ^0.1.1 "@types/jest": ^27.4.1 "@types/jsdom": ^16.2.14