@wordpress/editor 14.41.1-next.v.202603102151.0 → 14.42.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/build/components/collaborators-overlay/avatar-iframe-styles.cjs +12 -4
- package/build/components/collaborators-overlay/avatar-iframe-styles.cjs.map +2 -2
- package/build/components/collaborators-overlay/compute-selection.cjs +181 -0
- package/build/components/collaborators-overlay/compute-selection.cjs.map +7 -0
- package/build/components/collaborators-overlay/cursor-dom-utils.cjs +243 -0
- package/build/components/collaborators-overlay/cursor-dom-utils.cjs.map +7 -0
- package/build/components/collaborators-overlay/overlay-iframe-styles.cjs +6 -0
- package/build/components/collaborators-overlay/overlay-iframe-styles.cjs.map +2 -2
- package/build/components/collaborators-overlay/overlay.cjs +61 -37
- package/build/components/collaborators-overlay/overlay.cjs.map +2 -2
- package/build/components/collaborators-overlay/timing-utils.cjs +46 -0
- package/build/components/collaborators-overlay/timing-utils.cjs.map +7 -0
- package/build/components/collaborators-overlay/use-block-highlighting.cjs +5 -6
- package/build/components/collaborators-overlay/use-block-highlighting.cjs.map +2 -2
- package/build/components/collaborators-overlay/use-render-cursors.cjs +50 -140
- package/build/components/collaborators-overlay/use-render-cursors.cjs.map +3 -3
- package/build/components/collaborators-presence/index.cjs +38 -12
- package/build/components/collaborators-presence/index.cjs.map +2 -2
- package/build/components/collaborators-presence/list.cjs +27 -24
- package/build/components/collaborators-presence/list.cjs.map +2 -2
- package/build/components/collaborators-presence/use-collaborator-notifications.cjs +79 -107
- package/build/components/collaborators-presence/use-collaborator-notifications.cjs.map +3 -3
- package/build/components/editor-interface/index.cjs +9 -6
- package/build/components/editor-interface/index.cjs.map +2 -2
- package/build/components/inserter-sidebar/index.cjs +2 -1
- package/build/components/inserter-sidebar/index.cjs.map +2 -2
- package/build/components/page-attributes/parent.cjs +1 -1
- package/build/components/page-attributes/parent.cjs.map +2 -2
- package/build/components/post-locked-modal/index.cjs +16 -3
- package/build/components/post-locked-modal/index.cjs.map +2 -2
- package/build/components/post-revisions-preview/block-diff.cjs +39 -11
- package/build/components/post-revisions-preview/block-diff.cjs.map +2 -2
- package/build/components/post-revisions-preview/diff-markers.cjs +2 -2
- package/build/components/post-revisions-preview/diff-markers.cjs.map +2 -2
- package/build/components/post-revisions-preview/revisions-canvas.cjs +12 -75
- package/build/components/post-revisions-preview/revisions-canvas.cjs.map +3 -3
- package/build/components/post-revisions-preview/revisions-slider.cjs +5 -1
- package/build/components/post-revisions-preview/revisions-slider.cjs.map +2 -2
- package/build/components/post-template/block-theme.cjs +7 -4
- package/build/components/post-template/block-theme.cjs.map +2 -2
- package/build/components/post-template/hooks.cjs +39 -2
- package/build/components/post-template/hooks.cjs.map +2 -2
- package/build/components/post-template/panel.cjs +5 -42
- package/build/components/post-template/panel.cjs.map +3 -3
- package/build/components/preferences-modal/index.cjs +24 -0
- package/build/components/preferences-modal/index.cjs.map +2 -2
- package/build/components/provider/disable-non-page-content-blocks.cjs +31 -28
- package/build/components/provider/disable-non-page-content-blocks.cjs.map +3 -3
- package/build/components/provider/index.cjs +17 -5
- package/build/components/provider/index.cjs.map +2 -2
- package/build/components/provider/use-block-editor-settings.cjs +19 -5
- package/build/components/provider/use-block-editor-settings.cjs.map +3 -3
- package/build/components/provider/{use-post-content-blocks.cjs → use-post-content-block-types.cjs} +8 -19
- package/build/components/provider/use-post-content-block-types.cjs.map +7 -0
- package/build/components/provider/use-revision-blocks.cjs +106 -0
- package/build/components/provider/use-revision-blocks.cjs.map +7 -0
- package/build/components/revision-block-diff/index.cjs +84 -0
- package/build/components/revision-block-diff/index.cjs.map +7 -0
- package/build/components/sidebar/dataform-post-summary.cjs +36 -6
- package/build/components/sidebar/dataform-post-summary.cjs.map +2 -2
- package/build/components/sidebar/header.cjs +1 -1
- package/build/components/sidebar/header.cjs.map +2 -2
- package/build/components/sidebar/index.cjs +5 -1
- package/build/components/sidebar/index.cjs.map +3 -3
- package/build/components/{sync-connection-modal → sync-connection-error-modal}/index.cjs +90 -78
- package/build/components/sync-connection-error-modal/index.cjs.map +7 -0
- package/build/components/{sync-connection-modal → sync-connection-error-modal}/use-retry-countdown.cjs +14 -27
- package/build/components/sync-connection-error-modal/use-retry-countdown.cjs.map +7 -0
- package/build/components/template-content-panel/index.cjs +35 -31
- package/build/components/template-content-panel/index.cjs.map +3 -3
- package/build/components/visual-editor/index.cjs +2 -2
- package/build/components/visual-editor/index.cjs.map +2 -2
- package/build/store/actions.cjs +1 -3
- package/build/store/actions.cjs.map +2 -2
- package/build/store/private-actions.cjs +11 -2
- package/build/store/private-actions.cjs.map +2 -2
- package/build/store/private-selectors.cjs +52 -13
- package/build/store/private-selectors.cjs.map +2 -2
- package/build/store/reducer.cjs +12 -0
- package/build/store/reducer.cjs.map +2 -2
- package/build/utils/media-finalize/index.cjs +43 -0
- package/build/utils/media-finalize/index.cjs.map +7 -0
- package/build/utils/sync-error-messages.cjs +29 -16
- package/build/utils/sync-error-messages.cjs.map +3 -3
- package/build-module/components/collaborators-overlay/avatar-iframe-styles.mjs +12 -4
- package/build-module/components/collaborators-overlay/avatar-iframe-styles.mjs.map +2 -2
- package/build-module/components/collaborators-overlay/compute-selection.mjs +162 -0
- package/build-module/components/collaborators-overlay/compute-selection.mjs.map +7 -0
- package/build-module/components/collaborators-overlay/cursor-dom-utils.mjs +213 -0
- package/build-module/components/collaborators-overlay/cursor-dom-utils.mjs.map +7 -0
- package/build-module/components/collaborators-overlay/overlay-iframe-styles.mjs +6 -0
- package/build-module/components/collaborators-overlay/overlay-iframe-styles.mjs.map +2 -2
- package/build-module/components/collaborators-overlay/overlay.mjs +61 -37
- package/build-module/components/collaborators-overlay/overlay.mjs.map +2 -2
- package/build-module/components/collaborators-overlay/timing-utils.mjs +21 -0
- package/build-module/components/collaborators-overlay/timing-utils.mjs.map +7 -0
- package/build-module/components/collaborators-overlay/use-block-highlighting.mjs +5 -6
- package/build-module/components/collaborators-overlay/use-block-highlighting.mjs.map +2 -2
- package/build-module/components/collaborators-overlay/use-render-cursors.mjs +50 -140
- package/build-module/components/collaborators-overlay/use-render-cursors.mjs.map +2 -2
- package/build-module/components/collaborators-presence/index.mjs +39 -13
- package/build-module/components/collaborators-presence/index.mjs.map +2 -2
- package/build-module/components/collaborators-presence/list.mjs +27 -24
- package/build-module/components/collaborators-presence/list.mjs.map +2 -2
- package/build-module/components/collaborators-presence/use-collaborator-notifications.mjs +80 -108
- package/build-module/components/collaborators-presence/use-collaborator-notifications.mjs.map +2 -2
- package/build-module/components/editor-interface/index.mjs +10 -7
- package/build-module/components/editor-interface/index.mjs.map +2 -2
- package/build-module/components/inserter-sidebar/index.mjs +2 -1
- package/build-module/components/inserter-sidebar/index.mjs.map +2 -2
- package/build-module/components/page-attributes/parent.mjs +1 -1
- package/build-module/components/page-attributes/parent.mjs.map +2 -2
- package/build-module/components/post-locked-modal/index.mjs +16 -3
- package/build-module/components/post-locked-modal/index.mjs.map +2 -2
- package/build-module/components/post-revisions-preview/block-diff.mjs +39 -11
- package/build-module/components/post-revisions-preview/block-diff.mjs.map +2 -2
- package/build-module/components/post-revisions-preview/diff-markers.mjs +2 -2
- package/build-module/components/post-revisions-preview/diff-markers.mjs.map +2 -2
- package/build-module/components/post-revisions-preview/revisions-canvas.mjs +14 -80
- package/build-module/components/post-revisions-preview/revisions-canvas.mjs.map +2 -2
- package/build-module/components/post-revisions-preview/revisions-slider.mjs +5 -1
- package/build-module/components/post-revisions-preview/revisions-slider.mjs.map +2 -2
- package/build-module/components/post-template/block-theme.mjs +7 -4
- package/build-module/components/post-template/block-theme.mjs.map +2 -2
- package/build-module/components/post-template/hooks.mjs +37 -1
- package/build-module/components/post-template/hooks.mjs.map +2 -2
- package/build-module/components/post-template/panel.mjs +5 -42
- package/build-module/components/post-template/panel.mjs.map +2 -2
- package/build-module/components/preferences-modal/index.mjs +24 -0
- package/build-module/components/preferences-modal/index.mjs.map +2 -2
- package/build-module/components/provider/disable-non-page-content-blocks.mjs +31 -28
- package/build-module/components/provider/disable-non-page-content-blocks.mjs.map +2 -2
- package/build-module/components/provider/index.mjs +17 -5
- package/build-module/components/provider/index.mjs.map +2 -2
- package/build-module/components/provider/use-block-editor-settings.mjs +19 -5
- package/build-module/components/provider/use-block-editor-settings.mjs.map +2 -2
- package/build-module/components/provider/use-post-content-block-types.mjs +23 -0
- package/build-module/components/provider/use-post-content-block-types.mjs.map +7 -0
- package/build-module/components/provider/use-revision-blocks.mjs +81 -0
- package/build-module/components/provider/use-revision-blocks.mjs.map +7 -0
- package/build-module/components/revision-block-diff/index.mjs +53 -0
- package/build-module/components/revision-block-diff/index.mjs.map +7 -0
- package/build-module/components/sidebar/dataform-post-summary.mjs +36 -6
- package/build-module/components/sidebar/dataform-post-summary.mjs.map +2 -2
- package/build-module/components/sidebar/header.mjs +1 -1
- package/build-module/components/sidebar/header.mjs.map +2 -2
- package/build-module/components/sidebar/index.mjs +5 -1
- package/build-module/components/sidebar/index.mjs.map +2 -2
- package/build-module/components/sync-connection-error-modal/index.mjs +177 -0
- package/build-module/components/sync-connection-error-modal/index.mjs.map +7 -0
- package/build-module/components/sync-connection-error-modal/use-retry-countdown.mjs +36 -0
- package/build-module/components/sync-connection-error-modal/use-retry-countdown.mjs.map +7 -0
- package/build-module/components/template-content-panel/index.mjs +25 -31
- package/build-module/components/template-content-panel/index.mjs.map +2 -2
- package/build-module/components/visual-editor/index.mjs +2 -2
- package/build-module/components/visual-editor/index.mjs.map +2 -2
- package/build-module/store/actions.mjs +1 -3
- package/build-module/store/actions.mjs.map +2 -2
- package/build-module/store/private-actions.mjs +10 -2
- package/build-module/store/private-actions.mjs.map +2 -2
- package/build-module/store/private-selectors.mjs +50 -12
- package/build-module/store/private-selectors.mjs.map +2 -2
- package/build-module/store/reducer.mjs +11 -0
- package/build-module/store/reducer.mjs.map +2 -2
- package/build-module/utils/media-finalize/index.mjs +12 -0
- package/build-module/utils/media-finalize/index.mjs.map +7 -0
- package/build-module/utils/sync-error-messages.mjs +24 -16
- package/build-module/utils/sync-error-messages.mjs.map +3 -3
- package/build-style/style-rtl.css +95 -16
- package/build-style/style.css +95 -16
- package/build-types/components/collaborators-overlay/avatar-iframe-styles.d.ts +1 -1
- package/build-types/components/collaborators-overlay/avatar-iframe-styles.d.ts.map +1 -1
- package/build-types/components/collaborators-overlay/compute-selection.d.ts +24 -0
- package/build-types/components/collaborators-overlay/compute-selection.d.ts.map +1 -0
- package/build-types/components/collaborators-overlay/cursor-dom-utils.d.ts +72 -0
- package/build-types/components/collaborators-overlay/cursor-dom-utils.d.ts.map +1 -0
- package/build-types/components/collaborators-overlay/overlay-iframe-styles.d.ts +1 -1
- package/build-types/components/collaborators-overlay/overlay-iframe-styles.d.ts.map +1 -1
- package/build-types/components/collaborators-overlay/overlay.d.ts.map +1 -1
- package/build-types/components/collaborators-overlay/timing-utils.d.ts +11 -0
- package/build-types/components/collaborators-overlay/timing-utils.d.ts.map +1 -0
- package/build-types/components/collaborators-overlay/use-block-highlighting.d.ts.map +1 -1
- package/build-types/components/collaborators-overlay/use-render-cursors.d.ts +4 -0
- package/build-types/components/collaborators-overlay/use-render-cursors.d.ts.map +1 -1
- package/build-types/components/collaborators-presence/index.d.ts.map +1 -1
- package/build-types/components/collaborators-presence/list.d.ts +2 -1
- package/build-types/components/collaborators-presence/list.d.ts.map +1 -1
- package/build-types/components/collaborators-presence/use-collaborator-notifications.d.ts.map +1 -1
- package/build-types/components/editor-interface/index.d.ts.map +1 -1
- package/build-types/components/inserter-sidebar/index.d.ts.map +1 -1
- package/build-types/components/post-locked-modal/index.d.ts +2 -2
- package/build-types/components/post-locked-modal/index.d.ts.map +1 -1
- package/build-types/components/post-revisions-preview/block-diff.d.ts.map +1 -1
- package/build-types/components/post-revisions-preview/revisions-canvas.d.ts +2 -5
- package/build-types/components/post-revisions-preview/revisions-canvas.d.ts.map +1 -1
- package/build-types/components/post-revisions-preview/revisions-slider.d.ts.map +1 -1
- package/build-types/components/post-template/block-theme.d.ts +1 -3
- package/build-types/components/post-template/block-theme.d.ts.map +1 -1
- package/build-types/components/post-template/hooks.d.ts +1 -0
- package/build-types/components/post-template/hooks.d.ts.map +1 -1
- package/build-types/components/post-template/panel.d.ts.map +1 -1
- package/build-types/components/provider/disable-non-page-content-blocks.d.ts.map +1 -1
- package/build-types/components/provider/index.d.ts.map +1 -1
- package/build-types/components/provider/use-block-editor-settings.d.ts.map +1 -1
- package/build-types/components/provider/use-post-content-block-types.d.ts +9 -0
- package/build-types/components/provider/use-post-content-block-types.d.ts.map +1 -0
- package/build-types/components/provider/use-revision-blocks.d.ts +10 -0
- package/build-types/components/provider/use-revision-blocks.d.ts.map +1 -0
- package/build-types/components/revision-block-diff/index.d.ts +6 -0
- package/build-types/components/revision-block-diff/index.d.ts.map +1 -0
- package/build-types/components/sidebar/dataform-post-summary.d.ts.map +1 -1
- package/build-types/components/sidebar/index.d.ts.map +1 -1
- package/build-types/components/sync-connection-error-modal/index.d.ts +22 -0
- package/build-types/components/sync-connection-error-modal/index.d.ts.map +1 -0
- package/build-types/components/sync-connection-error-modal/use-retry-countdown.d.ts +11 -0
- package/build-types/components/sync-connection-error-modal/use-retry-countdown.d.ts.map +1 -0
- package/build-types/components/template-content-panel/index.d.ts.map +1 -1
- package/build-types/store/actions.d.ts.map +1 -1
- package/build-types/store/private-actions.d.ts +7 -0
- package/build-types/store/private-actions.d.ts.map +1 -1
- package/build-types/store/private-selectors.d.ts +7 -0
- package/build-types/store/private-selectors.d.ts.map +1 -1
- package/build-types/store/reducer.d.ts +14 -3
- package/build-types/store/reducer.d.ts.map +1 -1
- package/build-types/utils/media-finalize/index.d.ts +2 -0
- package/build-types/utils/media-finalize/index.d.ts.map +1 -0
- package/build-types/utils/sync-error-messages.d.ts +17 -3
- package/build-types/utils/sync-error-messages.d.ts.map +1 -1
- package/package.json +44 -44
- package/src/components/collaborators-overlay/avatar-iframe-styles.ts +12 -4
- package/src/components/collaborators-overlay/compute-selection.ts +307 -0
- package/src/components/collaborators-overlay/cursor-dom-utils.ts +382 -0
- package/src/components/collaborators-overlay/overlay-iframe-styles.ts +6 -0
- package/src/components/collaborators-overlay/overlay.tsx +59 -27
- package/src/components/collaborators-overlay/timing-utils.ts +30 -0
- package/src/components/collaborators-overlay/use-block-highlighting.ts +11 -10
- package/src/components/collaborators-overlay/use-render-cursors.ts +70 -242
- package/src/components/collaborators-presence/avatar/styles.scss +20 -4
- package/src/components/collaborators-presence/index.tsx +30 -5
- package/src/components/collaborators-presence/list.tsx +38 -24
- package/src/components/collaborators-presence/test/use-collaborator-notifications.ts +188 -246
- package/src/components/collaborators-presence/use-collaborator-notifications.ts +109 -166
- package/src/components/document-bar/style.scss +1 -1
- package/src/components/editor-interface/index.js +8 -6
- package/src/components/inserter-sidebar/index.js +4 -1
- package/src/components/page-attributes/parent.js +1 -1
- package/src/components/post-locked-modal/index.js +21 -3
- package/src/components/post-revisions-preview/block-diff.js +59 -20
- package/src/components/post-revisions-preview/diff-markers.js +2 -2
- package/src/components/post-revisions-preview/revisions-canvas.js +20 -98
- package/src/components/post-revisions-preview/revisions-slider.js +6 -1
- package/src/components/post-revisions-preview/test/block-diff.js +69 -31
- package/src/components/post-template/block-theme.js +4 -1
- package/src/components/post-template/hooks.js +42 -0
- package/src/components/post-template/panel.js +5 -59
- package/src/components/preferences-modal/index.js +18 -0
- package/src/components/provider/disable-non-page-content-blocks.js +42 -40
- package/src/components/provider/index.js +20 -2
- package/src/components/provider/use-block-editor-settings.js +21 -8
- package/src/components/provider/use-post-content-block-types.js +30 -0
- package/src/components/provider/use-revision-blocks.js +105 -0
- package/src/components/revision-block-diff/index.js +74 -0
- package/src/components/revision-block-diff/style.scss +13 -0
- package/src/components/sidebar/dataform-post-summary.js +61 -16
- package/src/components/sidebar/header.js +1 -1
- package/src/components/sidebar/index.js +2 -0
- package/src/components/sync-connection-error-modal/index.tsx +265 -0
- package/src/components/sync-connection-error-modal/style.scss +14 -0
- package/src/components/sync-connection-error-modal/use-retry-countdown.ts +57 -0
- package/src/components/template-content-panel/index.js +30 -38
- package/src/components/visual-editor/index.js +2 -2
- package/src/store/actions.js +1 -4
- package/src/store/private-actions.js +21 -2
- package/src/store/private-selectors.js +75 -10
- package/src/store/reducer.js +19 -0
- package/src/style.scss +2 -1
- package/src/utils/media-finalize/index.js +11 -0
- package/src/utils/media-finalize/test/index.js +34 -0
- package/src/utils/sync-error-messages.ts +72 -0
- package/src/utils/test/sync-error-messages.js +9 -32
- package/build/components/provider/use-post-content-blocks.cjs.map +0 -7
- package/build/components/sync-connection-modal/index.cjs.map +0 -7
- package/build/components/sync-connection-modal/use-retry-countdown.cjs.map +0 -7
- package/build-module/components/provider/use-post-content-blocks.mjs +0 -34
- package/build-module/components/provider/use-post-content-blocks.mjs.map +0 -7
- package/build-module/components/sync-connection-modal/index.mjs +0 -167
- package/build-module/components/sync-connection-modal/index.mjs.map +0 -7
- package/build-module/components/sync-connection-modal/use-retry-countdown.mjs +0 -49
- package/build-module/components/sync-connection-modal/use-retry-countdown.mjs.map +0 -7
- package/build-types/components/provider/use-post-content-blocks.d.ts +0 -2
- package/build-types/components/provider/use-post-content-blocks.d.ts.map +0 -1
- package/build-types/components/sync-connection-modal/index.d.ts +0 -8
- package/build-types/components/sync-connection-modal/index.d.ts.map +0 -1
- package/build-types/components/sync-connection-modal/use-retry-countdown.d.ts +0 -9
- package/build-types/components/sync-connection-modal/use-retry-countdown.d.ts.map +0 -1
- package/src/components/provider/use-post-content-blocks.js +0 -42
- package/src/components/sync-connection-modal/index.js +0 -200
- package/src/components/sync-connection-modal/style.scss +0 -9
- package/src/components/sync-connection-modal/use-retry-countdown.js +0 -70
- package/src/utils/sync-error-messages.js +0 -58
|
@@ -1,16 +1,23 @@
|
|
|
1
|
-
// @ts-expect-error No exported types
|
|
2
|
-
import { useStyleOverride } from '@wordpress/block-editor';
|
|
3
1
|
import { useResizeObserver, useMergeRefs } from '@wordpress/compose';
|
|
4
2
|
import { useCallback, useEffect, useState } from '@wordpress/element';
|
|
3
|
+
import { __ } from '@wordpress/i18n';
|
|
5
4
|
|
|
6
5
|
import Avatar from '../collaborators-presence/avatar';
|
|
7
6
|
import { AVATAR_IFRAME_STYLES } from './avatar-iframe-styles';
|
|
8
7
|
import { OVERLAY_IFRAME_STYLES } from './overlay-iframe-styles';
|
|
8
|
+
import { setDelayedInterval } from './timing-utils';
|
|
9
9
|
import { useBlockHighlighting } from './use-block-highlighting';
|
|
10
10
|
import { useRenderCursors } from './use-render-cursors';
|
|
11
11
|
|
|
12
|
+
// Milliseconds to wait after a change before recomputing cursor positions.
|
|
12
13
|
const RERENDER_DELAY_MS = 500;
|
|
13
14
|
|
|
15
|
+
// Periodically recompute cursor positions to account for DOM layout
|
|
16
|
+
// changes that don't trigger awareness state updates (e.g. a collaborator
|
|
17
|
+
// applying formatting shifts text but the cursor's logical position is
|
|
18
|
+
// unchanged). Only active when remote cursors are visible.
|
|
19
|
+
const CURSOR_REDRAW_INTERVAL_MS = 10_000;
|
|
20
|
+
|
|
14
21
|
interface OverlayProps {
|
|
15
22
|
blockEditorDocument?: Document;
|
|
16
23
|
postId: number | null;
|
|
@@ -31,11 +38,6 @@ export function Overlay( {
|
|
|
31
38
|
postId,
|
|
32
39
|
postType,
|
|
33
40
|
}: OverlayProps ) {
|
|
34
|
-
useStyleOverride( {
|
|
35
|
-
id: 'collaborators-overlay',
|
|
36
|
-
css: AVATAR_IFRAME_STYLES + OVERLAY_IFRAME_STYLES,
|
|
37
|
-
} );
|
|
38
|
-
|
|
39
41
|
// Use state for the overlay element so that the hook re-runs once the ref is attached.
|
|
40
42
|
const [ overlayElement, setOverlayElement ] =
|
|
41
43
|
useState< HTMLDivElement | null >( null );
|
|
@@ -74,6 +76,17 @@ export function Overlay( {
|
|
|
74
76
|
};
|
|
75
77
|
}, [ rerenderCursorsAfterDelay, rerenderHighlightsAfterDelay ] );
|
|
76
78
|
|
|
79
|
+
useEffect( () => {
|
|
80
|
+
if ( cursors.length === 0 ) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return setDelayedInterval(
|
|
85
|
+
rerenderCursorsAfterDelay,
|
|
86
|
+
CURSOR_REDRAW_INTERVAL_MS
|
|
87
|
+
);
|
|
88
|
+
}, [ cursors.length, rerenderCursorsAfterDelay ] );
|
|
89
|
+
|
|
77
90
|
// Merge the refs to use the same element for both overlay and resize observation
|
|
78
91
|
const mergedRef = useMergeRefs< HTMLDivElement | null >( [
|
|
79
92
|
setOverlayElement,
|
|
@@ -84,30 +97,49 @@ export function Overlay( {
|
|
|
84
97
|
// scrollable elements like cursor indicators.
|
|
85
98
|
return (
|
|
86
99
|
<div className="collaborators-overlay-full" ref={ mergedRef }>
|
|
100
|
+
<style>{ AVATAR_IFRAME_STYLES + OVERLAY_IFRAME_STYLES }</style>
|
|
87
101
|
{ cursors.map( ( cursor ) => (
|
|
88
|
-
<div
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
102
|
+
<div key={ cursor.clientId }>
|
|
103
|
+
{ ! cursor.isMe &&
|
|
104
|
+
cursor.selectionRects?.map( ( rect, index ) => (
|
|
105
|
+
<div
|
|
106
|
+
key={ `${ cursor.clientId }-sel-${ index }` }
|
|
107
|
+
className="collaborators-overlay-selection-rect"
|
|
108
|
+
style={ {
|
|
109
|
+
left: `${ rect.x }px`,
|
|
110
|
+
top: `${ rect.y }px`,
|
|
111
|
+
width: `${ rect.width }px`,
|
|
112
|
+
height: `${ rect.height }px`,
|
|
113
|
+
backgroundColor: cursor.color,
|
|
114
|
+
} }
|
|
115
|
+
/>
|
|
116
|
+
) ) }
|
|
96
117
|
<div
|
|
97
|
-
className="collaborators-overlay-user
|
|
118
|
+
className="collaborators-overlay-user"
|
|
98
119
|
style={ {
|
|
99
|
-
|
|
100
|
-
|
|
120
|
+
left: `${ cursor.x }px`,
|
|
121
|
+
top: `${ cursor.y }px`,
|
|
101
122
|
} }
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
123
|
+
>
|
|
124
|
+
{ ! cursor.isMe && (
|
|
125
|
+
<div
|
|
126
|
+
className="collaborators-overlay-user-cursor"
|
|
127
|
+
style={ {
|
|
128
|
+
backgroundColor: cursor.color,
|
|
129
|
+
height: `${ cursor.height }px`,
|
|
130
|
+
} }
|
|
131
|
+
/>
|
|
132
|
+
) }
|
|
133
|
+
<Avatar
|
|
134
|
+
className="collaborators-overlay-user-label"
|
|
135
|
+
variant="badge"
|
|
136
|
+
size="small"
|
|
137
|
+
src={ cursor.avatarUrl }
|
|
138
|
+
name={ cursor.userName }
|
|
139
|
+
label={ cursor.isMe ? __( 'You' ) : undefined }
|
|
140
|
+
borderColor={ cursor.color }
|
|
141
|
+
/>
|
|
142
|
+
</div>
|
|
111
143
|
</div>
|
|
112
144
|
) ) }
|
|
113
145
|
{ highlights.map( ( highlight ) => (
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Like setInterval but chains setTimeout calls, so the delay is measured from
|
|
3
|
+
* the end of one run to the start of the next. This prevents callbacks from
|
|
4
|
+
* stacking up when the main thread is busy.
|
|
5
|
+
*
|
|
6
|
+
* @param callback The function to call repeatedly.
|
|
7
|
+
* @param delayMs Milliseconds between runs.
|
|
8
|
+
* @return A cleanup function that stops the timer.
|
|
9
|
+
*/
|
|
10
|
+
export function setDelayedInterval( callback: () => void, delayMs: number ) {
|
|
11
|
+
let timerHandle: ReturnType< typeof setTimeout > | null = null;
|
|
12
|
+
|
|
13
|
+
const runner = () => {
|
|
14
|
+
try {
|
|
15
|
+
callback();
|
|
16
|
+
} catch ( error ) {
|
|
17
|
+
// Do nothing
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
timerHandle = setTimeout( runner, delayMs );
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
timerHandle = setTimeout( runner, delayMs );
|
|
24
|
+
|
|
25
|
+
return () => {
|
|
26
|
+
if ( timerHandle ) {
|
|
27
|
+
clearTimeout( timerHandle );
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import {
|
|
5
5
|
privateApis as coreDataPrivateApis,
|
|
6
6
|
SelectionType,
|
|
7
|
-
type PostEditorAwarenessState,
|
|
7
|
+
type PostEditorAwarenessState as ActiveCollaborator,
|
|
8
8
|
} from '@wordpress/core-data';
|
|
9
9
|
import { useEffect, useRef, useState } from '@wordpress/element';
|
|
10
10
|
|
|
@@ -50,7 +50,7 @@ export function useBlockHighlighting(
|
|
|
50
50
|
rerenderHighlightsAfterDelay: () => () => void;
|
|
51
51
|
} {
|
|
52
52
|
const highlightedBlockIds = useRef< Set< string > >( new Set() );
|
|
53
|
-
const userStates:
|
|
53
|
+
const userStates: ActiveCollaborator[] = useActiveCollaborators(
|
|
54
54
|
postId ?? null,
|
|
55
55
|
postType ?? null
|
|
56
56
|
);
|
|
@@ -82,12 +82,13 @@ export function useBlockHighlighting(
|
|
|
82
82
|
// same block, only the first one gets the highlight and avatar label.
|
|
83
83
|
const seen = new Set< string >();
|
|
84
84
|
const blocksToHighlight = userStates
|
|
85
|
-
.filter(
|
|
86
|
-
|
|
87
|
-
! userState.isMe &&
|
|
85
|
+
.filter( ( userState: ActiveCollaborator ) => {
|
|
86
|
+
const isWholeBlockSelected =
|
|
88
87
|
userState.editorState?.selection?.type ===
|
|
89
|
-
|
|
90
|
-
|
|
88
|
+
SelectionType.WholeBlock;
|
|
89
|
+
|
|
90
|
+
return ! userState.isMe && isWholeBlockSelected;
|
|
91
|
+
} )
|
|
91
92
|
.map( ( userState ) => {
|
|
92
93
|
let localClientId;
|
|
93
94
|
try {
|
|
@@ -104,9 +105,9 @@ export function useBlockHighlighting(
|
|
|
104
105
|
|
|
105
106
|
return {
|
|
106
107
|
blockId: localClientId,
|
|
107
|
-
color:
|
|
108
|
-
|
|
109
|
-
|
|
108
|
+
color: userState.isMe
|
|
109
|
+
? 'var(--wp-admin-theme-color)'
|
|
110
|
+
: getAvatarBorderColor( userState.collaboratorInfo.id ),
|
|
110
111
|
userName: userState.collaboratorInfo.name,
|
|
111
112
|
avatarUrl: getAvatarUrl(
|
|
112
113
|
userState.collaboratorInfo.avatar_urls
|
|
@@ -1,17 +1,25 @@
|
|
|
1
1
|
import {
|
|
2
2
|
privateApis as coreDataPrivateApis,
|
|
3
3
|
SelectionType,
|
|
4
|
+
type PostEditorAwarenessState as ActiveCollaborator,
|
|
4
5
|
} from '@wordpress/core-data';
|
|
6
|
+
import { useSelect } from '@wordpress/data';
|
|
5
7
|
import { useEffect, useState } from '@wordpress/element';
|
|
8
|
+
import { store as preferencesStore } from '@wordpress/preferences';
|
|
9
|
+
import type { ResolvedSelection } from '@wordpress/core-data';
|
|
6
10
|
|
|
7
11
|
import { unlock } from '../../lock-unlock';
|
|
8
12
|
import { getAvatarUrl } from './get-avatar-url';
|
|
9
13
|
import { getAvatarBorderColor } from '../collab-sidebar/utils';
|
|
14
|
+
import { computeSelectionVisual } from './compute-selection';
|
|
10
15
|
import { useDebouncedRecompute } from './use-debounced-recompute';
|
|
16
|
+
import type { SelectionRect } from './cursor-dom-utils';
|
|
11
17
|
|
|
12
18
|
const { useActiveCollaborators, useResolvedSelection } =
|
|
13
19
|
unlock( coreDataPrivateApis );
|
|
14
20
|
|
|
21
|
+
export type { SelectionRect };
|
|
22
|
+
|
|
15
23
|
export interface CursorData {
|
|
16
24
|
userName: string;
|
|
17
25
|
clientId: number;
|
|
@@ -20,6 +28,8 @@ export interface CursorData {
|
|
|
20
28
|
x: number;
|
|
21
29
|
y: number;
|
|
22
30
|
height: number;
|
|
31
|
+
isMe?: boolean;
|
|
32
|
+
selectionRects?: SelectionRect[];
|
|
23
33
|
}
|
|
24
34
|
|
|
25
35
|
/**
|
|
@@ -48,6 +58,12 @@ export function useRenderCursors(
|
|
|
48
58
|
postType ?? null
|
|
49
59
|
);
|
|
50
60
|
|
|
61
|
+
const showOwnCursor = useSelect(
|
|
62
|
+
( select ) =>
|
|
63
|
+
select( preferencesStore ).get( 'core', 'showCollaborationCursor' ),
|
|
64
|
+
[]
|
|
65
|
+
);
|
|
66
|
+
|
|
51
67
|
const [ cursorPositions, setCursorPositions ] = useState< CursorData[] >(
|
|
52
68
|
[]
|
|
53
69
|
);
|
|
@@ -63,76 +79,90 @@ export function useRenderCursors(
|
|
|
63
79
|
return;
|
|
64
80
|
}
|
|
65
81
|
|
|
82
|
+
// Pre-compute the overlay rect once, same for every user.
|
|
83
|
+
const overlayRect = overlayElement.getBoundingClientRect();
|
|
84
|
+
const overlayContext = {
|
|
85
|
+
editorDocument: blockEditorDocument,
|
|
86
|
+
overlayRect,
|
|
87
|
+
};
|
|
88
|
+
|
|
66
89
|
const results: CursorData[] = [];
|
|
67
90
|
|
|
68
|
-
sortedUsers.
|
|
69
|
-
|
|
91
|
+
const hasOtherCollaborators = sortedUsers.some(
|
|
92
|
+
( u: ActiveCollaborator ) => ! u.isMe
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
sortedUsers.forEach( ( user: ActiveCollaborator ) => {
|
|
96
|
+
if ( user.isMe && ( ! showOwnCursor || ! hasOtherCollaborators ) ) {
|
|
70
97
|
return;
|
|
71
98
|
}
|
|
72
99
|
|
|
73
100
|
const selection = user.editorState?.selection ?? {
|
|
74
101
|
type: SelectionType.None,
|
|
75
102
|
};
|
|
76
|
-
const userName = user.collaboratorInfo.name;
|
|
77
|
-
const clientId = user.clientId;
|
|
78
|
-
const color = getAvatarBorderColor( user.collaboratorInfo.id );
|
|
79
|
-
const avatarUrl = getAvatarUrl( user.collaboratorInfo.avatar_urls );
|
|
80
103
|
|
|
81
|
-
let
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
104
|
+
let start: ResolvedSelection = {
|
|
105
|
+
richTextOffset: null,
|
|
106
|
+
localClientId: null,
|
|
107
|
+
};
|
|
108
|
+
let end: ResolvedSelection | undefined;
|
|
86
109
|
|
|
87
|
-
if ( selection.type === SelectionType.
|
|
88
|
-
// Nothing selected.
|
|
89
|
-
} else if ( selection.type === SelectionType.WholeBlock ) {
|
|
90
|
-
// Don't draw a cursor for a whole block selection.
|
|
91
|
-
} else if ( selection.type === SelectionType.Cursor ) {
|
|
110
|
+
if ( selection.type === SelectionType.Cursor ) {
|
|
92
111
|
try {
|
|
93
|
-
|
|
94
|
-
resolveSelection( selection );
|
|
95
|
-
if ( localClientId ) {
|
|
96
|
-
coords = getCursorPosition(
|
|
97
|
-
textIndex,
|
|
98
|
-
localClientId,
|
|
99
|
-
blockEditorDocument,
|
|
100
|
-
overlayElement
|
|
101
|
-
);
|
|
102
|
-
}
|
|
112
|
+
start = resolveSelection( selection );
|
|
103
113
|
} catch {
|
|
104
114
|
// Selection may reference a stale Yjs position.
|
|
115
|
+
return;
|
|
105
116
|
}
|
|
106
117
|
} else if (
|
|
107
118
|
selection.type === SelectionType.SelectionInOneBlock ||
|
|
108
119
|
selection.type === SelectionType.SelectionInMultipleBlocks
|
|
109
120
|
) {
|
|
110
121
|
try {
|
|
111
|
-
|
|
122
|
+
start = resolveSelection( {
|
|
112
123
|
type: SelectionType.Cursor,
|
|
113
124
|
cursorPosition: selection.cursorStartPosition,
|
|
114
125
|
} );
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
overlayElement
|
|
121
|
-
);
|
|
122
|
-
}
|
|
126
|
+
|
|
127
|
+
end = resolveSelection( {
|
|
128
|
+
type: SelectionType.Cursor,
|
|
129
|
+
cursorPosition: selection.cursorEndPosition,
|
|
130
|
+
} );
|
|
123
131
|
} catch {
|
|
124
132
|
// Selection may reference a stale Yjs position.
|
|
133
|
+
return;
|
|
125
134
|
}
|
|
126
135
|
}
|
|
127
136
|
|
|
128
|
-
|
|
129
|
-
|
|
137
|
+
const userName = user.collaboratorInfo.name;
|
|
138
|
+
const clientId = user.clientId;
|
|
139
|
+
const color = user.isMe
|
|
140
|
+
? 'var(--wp-admin-theme-color)'
|
|
141
|
+
: getAvatarBorderColor( user.collaboratorInfo.id );
|
|
142
|
+
const avatarUrl = getAvatarUrl( user.collaboratorInfo.avatar_urls );
|
|
143
|
+
|
|
144
|
+
const selectionVisual = computeSelectionVisual(
|
|
145
|
+
selection,
|
|
146
|
+
start,
|
|
147
|
+
end,
|
|
148
|
+
overlayContext
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
if ( selectionVisual.coords ) {
|
|
152
|
+
const cursorData: CursorData = {
|
|
130
153
|
userName,
|
|
131
154
|
clientId,
|
|
132
155
|
color,
|
|
133
156
|
avatarUrl,
|
|
134
|
-
|
|
135
|
-
|
|
157
|
+
isMe: user.isMe,
|
|
158
|
+
...selectionVisual.coords,
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
if ( selectionVisual.selectionRects ) {
|
|
162
|
+
cursorData.selectionRects = selectionVisual.selectionRects;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
results.push( cursorData );
|
|
136
166
|
}
|
|
137
167
|
} );
|
|
138
168
|
|
|
@@ -142,211 +172,9 @@ export function useRenderCursors(
|
|
|
142
172
|
resolveSelection,
|
|
143
173
|
overlayElement,
|
|
144
174
|
sortedUsers,
|
|
175
|
+
showOwnCursor,
|
|
145
176
|
recomputeToken,
|
|
146
177
|
] );
|
|
147
178
|
|
|
148
179
|
return { cursors: cursorPositions, rerenderCursorsAfterDelay };
|
|
149
180
|
}
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Given a selection, returns the coordinates of the cursor in the block.
|
|
153
|
-
*
|
|
154
|
-
* @param absolutePositionIndex - The absolute position index
|
|
155
|
-
* @param blockId - The block ID
|
|
156
|
-
* @param editorDocument - The editor document
|
|
157
|
-
* @param overlay - The overlay element
|
|
158
|
-
* @return The position of the cursor
|
|
159
|
-
*/
|
|
160
|
-
const getCursorPosition = (
|
|
161
|
-
absolutePositionIndex: number | null,
|
|
162
|
-
blockId: string,
|
|
163
|
-
editorDocument: Document,
|
|
164
|
-
overlay: HTMLElement
|
|
165
|
-
): { x: number; y: number; height: number } | null => {
|
|
166
|
-
if ( absolutePositionIndex === null ) {
|
|
167
|
-
// An absolute position index can be null if a cursor was set in a block that
|
|
168
|
-
// has since been deleted.
|
|
169
|
-
// Return null so we don't try to draw it.
|
|
170
|
-
return null;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
const blockElement = editorDocument.querySelector(
|
|
174
|
-
`[data-block="${ blockId }"]`
|
|
175
|
-
) as HTMLElement;
|
|
176
|
-
|
|
177
|
-
if ( ! blockElement ) {
|
|
178
|
-
return null;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
return (
|
|
182
|
-
getOffsetPositionInBlock(
|
|
183
|
-
blockElement,
|
|
184
|
-
absolutePositionIndex,
|
|
185
|
-
editorDocument,
|
|
186
|
-
overlay
|
|
187
|
-
) ?? null
|
|
188
|
-
);
|
|
189
|
-
};
|
|
190
|
-
|
|
191
|
-
/**
|
|
192
|
-
* Given a block element and a character offset, returns the coordinates for drawing a visual cursor in the block.
|
|
193
|
-
*
|
|
194
|
-
* @param blockElement - The block element
|
|
195
|
-
* @param charOffset - The character offset
|
|
196
|
-
* @param editorDocument - The editor document
|
|
197
|
-
* @param overlay - The overlay element
|
|
198
|
-
* @return The position of the cursor
|
|
199
|
-
*/
|
|
200
|
-
const getOffsetPositionInBlock = (
|
|
201
|
-
blockElement: HTMLElement,
|
|
202
|
-
charOffset: number,
|
|
203
|
-
editorDocument: Document,
|
|
204
|
-
overlay: HTMLElement
|
|
205
|
-
) => {
|
|
206
|
-
const { node, offset } = findInnerBlockOffset(
|
|
207
|
-
blockElement,
|
|
208
|
-
charOffset,
|
|
209
|
-
editorDocument
|
|
210
|
-
);
|
|
211
|
-
|
|
212
|
-
const cursorRange = editorDocument.createRange();
|
|
213
|
-
|
|
214
|
-
try {
|
|
215
|
-
cursorRange.setStart( node, offset );
|
|
216
|
-
} catch ( error ) {
|
|
217
|
-
return null;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
// Ensure the range only represents single point in the DOM.
|
|
221
|
-
cursorRange.collapse( true );
|
|
222
|
-
|
|
223
|
-
const cursorRect = cursorRange.getBoundingClientRect();
|
|
224
|
-
const overlayRect = overlay.getBoundingClientRect();
|
|
225
|
-
const blockRect = blockElement.getBoundingClientRect();
|
|
226
|
-
|
|
227
|
-
let cursorX = 0;
|
|
228
|
-
let cursorY = 0;
|
|
229
|
-
|
|
230
|
-
if (
|
|
231
|
-
cursorRect.x === 0 &&
|
|
232
|
-
cursorRect.y === 0 &&
|
|
233
|
-
cursorRect.width === 0 &&
|
|
234
|
-
cursorRect.height === 0
|
|
235
|
-
) {
|
|
236
|
-
// This can happen for empty blocks.
|
|
237
|
-
cursorX = blockRect.left - overlayRect.left;
|
|
238
|
-
cursorY = blockRect.top - overlayRect.top;
|
|
239
|
-
} else {
|
|
240
|
-
cursorX = cursorRect.left - overlayRect.left;
|
|
241
|
-
cursorY = cursorRect.top - overlayRect.top;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
let cursorHeight = cursorRect.height;
|
|
245
|
-
if ( cursorHeight === 0 ) {
|
|
246
|
-
const view = editorDocument.defaultView ?? window;
|
|
247
|
-
cursorHeight =
|
|
248
|
-
parseInt( view.getComputedStyle( blockElement ).lineHeight, 10 ) ||
|
|
249
|
-
blockRect.height;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
return {
|
|
253
|
-
x: cursorX,
|
|
254
|
-
y: cursorY,
|
|
255
|
-
height: cursorHeight,
|
|
256
|
-
};
|
|
257
|
-
};
|
|
258
|
-
|
|
259
|
-
const MAX_NODE_OFFSET_COUNT = 1000;
|
|
260
|
-
|
|
261
|
-
/**
|
|
262
|
-
* Given a block element and a character offset, returns an exact inner node and offset for use in a range.
|
|
263
|
-
*
|
|
264
|
-
* @param blockElement - The block element
|
|
265
|
-
* @param offset - The character offset
|
|
266
|
-
* @param editorDocument - The editor document
|
|
267
|
-
* @return The node and offset of the character at the offset
|
|
268
|
-
*/
|
|
269
|
-
const findInnerBlockOffset = (
|
|
270
|
-
blockElement: HTMLElement,
|
|
271
|
-
offset: number,
|
|
272
|
-
editorDocument: Document
|
|
273
|
-
) => {
|
|
274
|
-
const treeWalker = editorDocument.createTreeWalker(
|
|
275
|
-
blockElement,
|
|
276
|
-
NodeFilter.SHOW_TEXT | NodeFilter.SHOW_ELEMENT // eslint-disable-line no-bitwise
|
|
277
|
-
);
|
|
278
|
-
|
|
279
|
-
let currentOffset = 0;
|
|
280
|
-
let lastTextNode: Node | null = null;
|
|
281
|
-
|
|
282
|
-
let node: Node | null = null;
|
|
283
|
-
let nodeCount = 1;
|
|
284
|
-
|
|
285
|
-
while ( ( node = treeWalker.nextNode() ) ) {
|
|
286
|
-
nodeCount++;
|
|
287
|
-
|
|
288
|
-
if ( nodeCount > MAX_NODE_OFFSET_COUNT ) {
|
|
289
|
-
// If we've walked too many nodes, return the last text node or the beginning of the block.
|
|
290
|
-
if ( lastTextNode ) {
|
|
291
|
-
return { node: lastTextNode, offset: 0 };
|
|
292
|
-
}
|
|
293
|
-
return { node: blockElement, offset: 0 };
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
const nodeLength = node.nodeValue?.length ?? 0;
|
|
297
|
-
|
|
298
|
-
if ( node.nodeType === Node.ELEMENT_NODE ) {
|
|
299
|
-
if ( node.nodeName === 'BR' ) {
|
|
300
|
-
// Treat <br> as a single "\n" character.
|
|
301
|
-
|
|
302
|
-
if ( currentOffset + 1 >= offset ) {
|
|
303
|
-
// If the <br> occurs right on the target offset, return the next text node.
|
|
304
|
-
const nodeAfterBr = treeWalker.nextNode();
|
|
305
|
-
|
|
306
|
-
if ( nodeAfterBr?.nodeType === Node.TEXT_NODE ) {
|
|
307
|
-
return { node: nodeAfterBr, offset: 0 };
|
|
308
|
-
} else if ( lastTextNode ) {
|
|
309
|
-
// If there's no text node after the <br>, return the end offset of the last text node.
|
|
310
|
-
return {
|
|
311
|
-
node: lastTextNode,
|
|
312
|
-
offset: lastTextNode.nodeValue?.length ?? 0,
|
|
313
|
-
};
|
|
314
|
-
}
|
|
315
|
-
// Just in case, if there's no last text node, return the beginning of the block.
|
|
316
|
-
return { node: blockElement, offset: 0 };
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
// The <br> is before the target offset. Count it as a single character.
|
|
320
|
-
currentOffset += 1;
|
|
321
|
-
continue;
|
|
322
|
-
} else {
|
|
323
|
-
// Skip other element types.
|
|
324
|
-
continue;
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
if ( nodeLength === 0 ) {
|
|
329
|
-
// Skip empty nodes.
|
|
330
|
-
continue;
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
if ( currentOffset + nodeLength >= offset ) {
|
|
334
|
-
// This node exceeds the target offset. Return the node and the position of the offset within it.
|
|
335
|
-
return { node, offset: offset - currentOffset };
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
currentOffset += nodeLength;
|
|
339
|
-
|
|
340
|
-
if ( node.nodeType === Node.TEXT_NODE ) {
|
|
341
|
-
lastTextNode = node;
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
if ( lastTextNode && lastTextNode.nodeValue?.length ) {
|
|
346
|
-
// We didn't reach the target offset. Return the last text node's last character.
|
|
347
|
-
return { node: lastTextNode, offset: lastTextNode.nodeValue.length };
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
// We didn't find any text nodes. Return the beginning of the block.
|
|
351
|
-
return { node: blockElement, offset: 0 };
|
|
352
|
-
};
|
|
@@ -5,13 +5,14 @@
|
|
|
5
5
|
// fallback matching the default WordPress admin blue.
|
|
6
6
|
$-accent-color: var(--wp-admin-theme-color, #3858e9);
|
|
7
7
|
|
|
8
|
+
// overflow: clip is intentionally omitted here — .editor-avatar__image handles
|
|
9
|
+
// its own circular clip. Removing overflow from this container allows the inset
|
|
10
|
+
// box-shadow on __image to render without being clipped by the parent.
|
|
8
11
|
.editor-avatar {
|
|
9
12
|
position: relative;
|
|
10
13
|
display: inline-flex;
|
|
11
14
|
align-items: center;
|
|
12
15
|
border-radius: $radius-full;
|
|
13
|
-
overflow: hidden;
|
|
14
|
-
overflow: clip;
|
|
15
16
|
flex-shrink: 0;
|
|
16
17
|
box-shadow: 0 0 0 var(--wp-admin-border-width-focus) $white, $elevation-x-small;
|
|
17
18
|
}
|
|
@@ -37,8 +38,20 @@ $-accent-color: var(--wp-admin-theme-color, #3858e9);
|
|
|
37
38
|
|
|
38
39
|
.has-avatar-border-color > & {
|
|
39
40
|
border: var(--wp-admin-border-width-focus) solid var(--editor-avatar-outline-color);
|
|
40
|
-
box-shadow: inset 0 0 0 var(--wp-admin-border-width-focus) $white;
|
|
41
41
|
background-clip: padding-box;
|
|
42
|
+
|
|
43
|
+
// Inner contrast ring rendered as a pseudo-element so it paints above
|
|
44
|
+
// the absolutely-positioned <img>. An inset box-shadow would be
|
|
45
|
+
// covered by the image content layer.
|
|
46
|
+
&::after {
|
|
47
|
+
content: "";
|
|
48
|
+
position: absolute;
|
|
49
|
+
inset: 0;
|
|
50
|
+
border-radius: inherit;
|
|
51
|
+
box-shadow: inset 0 0 0 var(--wp-admin-border-width-focus, 2px) $white;
|
|
52
|
+
pointer-events: none;
|
|
53
|
+
z-index: 1;
|
|
54
|
+
}
|
|
42
55
|
}
|
|
43
56
|
}
|
|
44
57
|
|
|
@@ -66,8 +79,11 @@ $-accent-color: var(--wp-admin-theme-color, #3858e9);
|
|
|
66
79
|
font-size: $font-size-x-small;
|
|
67
80
|
font-weight: $font-weight-medium;
|
|
68
81
|
border: 0;
|
|
69
|
-
box-shadow: none;
|
|
70
82
|
background-clip: border-box;
|
|
83
|
+
|
|
84
|
+
&::after {
|
|
85
|
+
content: none;
|
|
86
|
+
}
|
|
71
87
|
}
|
|
72
88
|
|
|
73
89
|
.editor-avatar:not(.has-src).has-avatar-border-color > .editor-avatar__image {
|