@atlaskit/renderer 148.1.0 → 149.0.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.
Files changed (53) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/cjs/actions/index.js +35 -3
  3. package/dist/cjs/react/index.js +8 -0
  4. package/dist/cjs/react/marks/annotation.js +3 -1
  5. package/dist/cjs/react/nodes/extension-comments.compiled.css +4 -0
  6. package/dist/cjs/react/nodes/extension-comments.js +144 -0
  7. package/dist/cjs/react/nodes/extension.js +86 -11
  8. package/dist/cjs/steps/index.js +23 -0
  9. package/dist/cjs/ui/Renderer/index.js +1 -1
  10. package/dist/cjs/ui/annotations/contexts/AnnotationRangeContext.js +29 -11
  11. package/dist/cjs/ui/annotations/element/extension.compiled.css +6 -0
  12. package/dist/cjs/ui/annotations/element/extension.js +34 -0
  13. package/dist/cjs/ui/annotations/element/index.js +4 -2
  14. package/dist/cjs/ui/annotations/element/mark.js +11 -1
  15. package/dist/cjs/ui/annotations/index.js +2 -1
  16. package/dist/es2019/actions/index.js +33 -3
  17. package/dist/es2019/react/index.js +8 -0
  18. package/dist/es2019/react/marks/annotation.js +3 -1
  19. package/dist/es2019/react/nodes/extension-comments.compiled.css +4 -0
  20. package/dist/es2019/react/nodes/extension-comments.js +115 -0
  21. package/dist/es2019/react/nodes/extension.js +83 -7
  22. package/dist/es2019/steps/index.js +25 -0
  23. package/dist/es2019/ui/Renderer/index.js +1 -1
  24. package/dist/es2019/ui/annotations/contexts/AnnotationRangeContext.js +28 -12
  25. package/dist/es2019/ui/annotations/element/extension.compiled.css +6 -0
  26. package/dist/es2019/ui/annotations/element/extension.js +26 -0
  27. package/dist/es2019/ui/annotations/element/index.js +4 -2
  28. package/dist/es2019/ui/annotations/element/mark.js +11 -1
  29. package/dist/es2019/ui/annotations/index.js +2 -1
  30. package/dist/esm/actions/index.js +35 -3
  31. package/dist/esm/react/index.js +8 -0
  32. package/dist/esm/react/marks/annotation.js +3 -1
  33. package/dist/esm/react/nodes/extension-comments.compiled.css +4 -0
  34. package/dist/esm/react/nodes/extension-comments.js +135 -0
  35. package/dist/esm/react/nodes/extension.js +86 -7
  36. package/dist/esm/steps/index.js +23 -0
  37. package/dist/esm/ui/Renderer/index.js +1 -1
  38. package/dist/esm/ui/annotations/contexts/AnnotationRangeContext.js +30 -12
  39. package/dist/esm/ui/annotations/element/extension.compiled.css +6 -0
  40. package/dist/esm/ui/annotations/element/extension.js +27 -0
  41. package/dist/esm/ui/annotations/element/index.js +4 -2
  42. package/dist/esm/ui/annotations/element/mark.js +11 -1
  43. package/dist/esm/ui/annotations/index.js +2 -1
  44. package/dist/types/actions/index.d.ts +13 -0
  45. package/dist/types/react/marks/annotation.d.ts +1 -1
  46. package/dist/types/react/nodes/extension-comments.d.ts +17 -0
  47. package/dist/types/react/nodes/extension.d.ts +12 -0
  48. package/dist/types/react/types.d.ts +1 -0
  49. package/dist/types/ui/annotations/contexts/AnnotationRangeContext.d.ts +4 -2
  50. package/dist/types/ui/annotations/element/extension.d.ts +17 -0
  51. package/dist/types/ui/annotations/element/index.d.ts +2 -1
  52. package/dist/types/ui/annotations/element/mark.d.ts +3 -107
  53. package/package.json +4 -4
@@ -20,7 +20,8 @@ var MarkElement = exports.MarkElement = function MarkElement(_ref) {
20
20
  children = _ref.children,
21
21
  dataAttributes = _ref.dataAttributes,
22
22
  id = _ref.id,
23
- useBlockLevel = _ref.useBlockLevel;
23
+ useBlockLevel = _ref.useBlockLevel,
24
+ isExtension = _ref.isExtension;
24
25
  var updateSubscriber = (0, _useInlineCommentSubscriber.useInlineCommentSubscriberContext)();
25
26
  var states = (0, _react.useContext)(_context.InlineCommentsStateContext);
26
27
  var _useHasFocusEvent = (0, _useEvents.useHasFocusEvent)({
@@ -98,6 +99,7 @@ var MarkElement = exports.MarkElement = function MarkElement(_ref) {
98
99
  hasFocus: hasFocus,
99
100
  isHovered: isHovered,
100
101
  state: states[id],
101
- useBlockLevel: useBlockLevel
102
+ useBlockLevel: useBlockLevel,
103
+ isExtension: isExtension
102
104
  }, children);
103
105
  };
@@ -14,6 +14,7 @@ var _annotation = require("@atlaskit/adf-schema/annotation");
14
14
  var _fg = require("@atlaskit/platform-feature-flags/fg");
15
15
  var _messages = require("../../../messages");
16
16
  var _AnnotationManagerContext = require("../contexts/AnnotationManagerContext");
17
+ var _extension = require("./extension");
17
18
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
18
19
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } /**
19
20
  * @jsxRuntime classic
@@ -123,7 +124,8 @@ var MarkComponent = exports.MarkComponent = function MarkComponent(_ref) {
123
124
  hasFocus = _ref.hasFocus,
124
125
  isHovered = _ref.isHovered,
125
126
  onClick = _ref.onClick,
126
- useBlockLevel = _ref.useBlockLevel;
127
+ useBlockLevel = _ref.useBlockLevel,
128
+ isExtension = _ref.isExtension;
127
129
  var intl = (0, _reactIntl.useIntl)();
128
130
  var annotationIds = (0, _react.useMemo)(function () {
129
131
  return (0, _toConsumableArray2.default)(new Set([].concat((0, _toConsumableArray2.default)(annotationParentIds), [id])));
@@ -213,6 +215,14 @@ var MarkComponent = exports.MarkComponent = function MarkComponent(_ref) {
213
215
  } : _objectSpread({
214
216
  'aria-details': annotationIds.join(', ')
215
217
  }, desktopAccessibilityAttributes);
218
+ if (isExtension && (0, _fg.fg)('cc_maui_annotations_on_extensions')) {
219
+ // The chart's badge owns interaction. Anchors only supply geometry and annotation state.
220
+ return (0, _react2.jsx)(_extension.ExtensionAnnotation, {
221
+ id: id,
222
+ dataAttributes: overriddenData,
223
+ markRef: id === currentSelectedAnnotationId ? markRef : undefined
224
+ });
225
+ }
216
226
  return (0, _react2.jsx)(useBlockLevel ? 'div' : 'mark', _objectSpread(_objectSpread(_objectSpread((0, _defineProperty2.default)({
217
227
  ref: id === currentSelectedAnnotationId ? markRef : undefined,
218
228
  id: id
@@ -37,7 +37,7 @@ var AnnotationsPositionContext = exports.AnnotationsPositionContext = /*#__PURE_
37
37
  startPos: 1
38
38
  });
39
39
  var AnnotationsWrapperInner = exports.AnnotationsWrapperInner = function AnnotationsWrapperInner(props) {
40
- var _providers$inlineComm, _providers$inlineComm2;
40
+ var _providers$inlineComm, _providers$inlineComm2, _providers$inlineComm3;
41
41
  var children = props.children,
42
42
  rendererRef = props.rendererRef,
43
43
  adfDocument = props.adfDocument,
@@ -55,6 +55,7 @@ var AnnotationsWrapperInner = exports.AnnotationsWrapperInner = function Annotat
55
55
  value: inlineCommentAnnotationsState
56
56
  }, /*#__PURE__*/_react.default.createElement(_AnnotationRangeContext.AnnotationRangeProvider, {
57
57
  allowCommentsOnMedia: (_providers$inlineComm = providers === null || providers === void 0 || (_providers$inlineComm2 = providers.inlineComment) === null || _providers$inlineComm2 === void 0 ? void 0 : _providers$inlineComm2.allowCommentsOnMedia) !== null && _providers$inlineComm !== void 0 ? _providers$inlineComm : false,
58
+ hasBlockNodeSupport: Boolean(providers === null || providers === void 0 || (_providers$inlineComm3 = providers.inlineComment) === null || _providers$inlineComm3 === void 0 ? void 0 : _providers$inlineComm3.isBlockNodeSupported),
58
59
  isNestedRender: isNestedRender
59
60
  }, /*#__PURE__*/_react.default.createElement(_AnnotationHoverContext.AnnotationHoverContext, null, /*#__PURE__*/_react.default.createElement(_wrapper.AnnotationsContextWrapper, {
60
61
  createAnalyticsEvent: createAnalyticsEvent,
@@ -75,9 +75,10 @@ export default class RendererActions {
75
75
  let to;
76
76
  let nodePos;
77
77
  let step;
78
+ const schema = this.schema;
78
79
  this.doc.descendants((node, pos) => {
79
80
  const found = mark.isInSet(node.marks);
80
- if (found && node.type.name === 'media') {
81
+ if (found && isBlockAnnotationTarget(node, schema)) {
81
82
  nodePos = pos;
82
83
  }
83
84
  if (found && !from) {
@@ -169,7 +170,6 @@ export default class RendererActions {
169
170
  }
170
171
  isRangeAnnotatable(range) {
171
172
  try {
172
- var _startContainer$paren, _endContainer$parentE;
173
173
  if (!range) {
174
174
  return false;
175
175
  }
@@ -177,7 +177,7 @@ export default class RendererActions {
177
177
  startContainer,
178
178
  endContainer
179
179
  } = range;
180
- if ((_startContainer$paren = startContainer.parentElement) !== null && _startContainer$paren !== void 0 && _startContainer$paren.closest('.ak-renderer-extension') || (_endContainer$parentE = endContainer.parentElement) !== null && _endContainer$parentE !== void 0 && _endContainer$parentE.closest('.ak-renderer-extension')) {
180
+ if (this.isInsideNestedExtensionRenderer(startContainer) || this.isInsideNestedExtensionRenderer(endContainer)) {
181
181
  return false;
182
182
  }
183
183
  return this.isValidAnnotationRange(range);
@@ -193,6 +193,36 @@ export default class RendererActions {
193
193
  }
194
194
  }
195
195
 
196
+ /**
197
+ * A range only threatens a nested renderer's own document (e.g. a bodiedExtension's macro
198
+ * body, which renders its own nested `<Renderer>`) when it sits inside that nested renderer's
199
+ * `.ak-renderer-document`. Landing directly on the extension's own outer wrapper -- with no
200
+ * nested document in between -- must stay annotatable so the whole chart/extension can be
201
+ * targeted (MAUI-1256), rather than being rejected outright the way any range touching
202
+ * `.ak-renderer-extension` previously was.
203
+ *
204
+ * Behind `cc_maui_annotations_on_extensions` -- the same gate MAUI-1255 added for extension
205
+ * node-mark support -- to preserve the previous behaviour (reject anything touching
206
+ * `.ak-renderer-extension`) while the feature is off.
207
+ */
208
+ isInsideNestedExtensionRenderer(container) {
209
+ // Preserve the legacy parent-based rejection while the gate is disabled.
210
+ if (!fg('cc_maui_annotations_on_extensions')) {
211
+ var _container$parentElem;
212
+ return !!((_container$parentElem = container.parentElement) !== null && _container$parentElem !== void 0 && _container$parentElem.closest('.ak-renderer-extension'));
213
+ }
214
+ const element = container instanceof Element ? container : container.parentElement;
215
+ if (!element) {
216
+ return false;
217
+ }
218
+ const extensionAncestor = element.closest('.ak-renderer-extension');
219
+ if (!extensionAncestor) {
220
+ return false;
221
+ }
222
+ const nestedDocument = element.closest('.ak-renderer-document');
223
+ return !!nestedDocument && extensionAncestor.contains(nestedDocument);
224
+ }
225
+
196
226
  // eslint-disable-next-line @repo/internal/deprecations/deprecation-ticket-required -- Ignored via go/ED-25883
197
227
  /**
198
228
  * This is replaced by `isRangeAnnotatable`.
@@ -85,6 +85,11 @@ export default class ReactSerializer {
85
85
  if (shouldSkipLinkMark(mark) || shouldSkipBorderMark(mark)) {
86
86
  return content;
87
87
  }
88
+ // Extension annotations are sibling anchors inside the stable extension shell.
89
+ // Wrapping the extension here would remount its iframe whenever marks change.
90
+ if (node.type.name === 'extension' && mark.type.name === 'annotation' && this.allowAnnotations && fg('cc_maui_annotations_on_extensions')) {
91
+ return content;
92
+ }
88
93
  return this.renderMark(markToReact(mark), this.withMediaMarkProps(node, mark, this.getMarkProps(mark, [], node)), `${mark.type.name}-${index}`, content);
89
94
  }, serializedContent);
90
95
  return withExpandBodyBlock(node, currentPath, index, serialized);
@@ -503,6 +508,9 @@ export default class ReactSerializer {
503
508
  var _this$getExtensionHei;
504
509
  return {
505
510
  ...this.getProps(node, path),
511
+ allowAnnotations: this.allowAnnotations,
512
+ allowAnnotationsDraftMode: this.surroundTextNodesWithTextWrapper,
513
+ extensionNode: node,
506
514
  extensionViewportSizes: this.extensionViewportSizes,
507
515
  hideExtensionKeysWhilePending: this.hideExtensionKeysWhilePending,
508
516
  nodeHeight: (_this$getExtensionHei = this.getExtensionHeight) === null || _this$getExtensionHei === void 0 ? void 0 : _this$getExtensionHei.call(this, node),
@@ -13,6 +13,7 @@ const AnnotationComponent = ({
13
13
  annotationParentIds = [],
14
14
  allowAnnotations,
15
15
  useBlockLevel,
16
+ isExtension,
16
17
  isMediaInline
17
18
  }) => {
18
19
  const data = {
@@ -36,7 +37,8 @@ const AnnotationComponent = ({
36
37
  dataAttributes: data,
37
38
  annotationParentIds: annotationParentIds,
38
39
  annotationType: annotationType,
39
- useBlockLevel: useBlockLevel
40
+ useBlockLevel: useBlockLevel,
41
+ isExtension: isExtension
40
42
  }, children);
41
43
  }
42
44
  return (
@@ -0,0 +1,4 @@
1
+ ._154iu2gc{top:var(--ds-space-100,8px)}
2
+ ._1xi2u2gc{right:var(--ds-space-100,8px)}
3
+ ._kqswh2mm{position:relative}
4
+ ._kqswstnw{position:absolute}
@@ -0,0 +1,115 @@
1
+ /* extension-comments.tsx generated by @compiled/babel-plugin v3.0.2 */
2
+ import "./extension-comments.compiled.css";
3
+ import { ax, ix } from "@compiled/react/runtime";
4
+ import React, { useCallback, useEffect, useMemo, useState } from 'react';
5
+ import { AnnotationMarkStates, AnnotationTypes } from '@atlaskit/adf-schema/annotation';
6
+ import { VIEW_METHOD } from '@atlaskit/editor-common/analytics';
7
+ import { CommentBadgeNext } from '@atlaskit/editor-common/media-single';
8
+ import { AnnotationUpdateEvent } from '@atlaskit/editor-common/types';
9
+ import { useAnnotationManagerState } from '../../ui/annotations/contexts/AnnotationManagerContext';
10
+ import { useAnnotationRangeState } from '../../ui/annotations/contexts/AnnotationRangeContext';
11
+ import { useInlineCommentSubscriberContext } from '../../ui/annotations/hooks/use-inline-comment-subscriber';
12
+ import { useInlineCommentsFilter } from '../../ui/annotations/hooks/use-inline-comments-filter';
13
+ import AnnotationComponent from '../marks/annotation';
14
+ const containerStyles = null;
15
+ const annotationParentIds = [];
16
+ const annotationDataAttributes = {
17
+ 'data-renderer-mark': true,
18
+ 'data-block-mark': true
19
+ };
20
+ const badgeStyles = null;
21
+ const ExtensionCommentsEnabled = ({
22
+ marks,
23
+ children,
24
+ enabled,
25
+ allowAnnotations,
26
+ allowAnnotationsDraftMode,
27
+ startPos
28
+ }) => {
29
+ const {
30
+ hoverDraftDocumentPosition
31
+ } = useAnnotationRangeState();
32
+ const isDraft = Boolean(enabled && allowAnnotationsDraftMode && hoverDraftDocumentPosition && hoverDraftDocumentPosition.from + 1 === startPos);
33
+ const updateSubscriber = useInlineCommentSubscriberContext();
34
+ const annotationIds = useMemo(() => {
35
+ var _marks$filter$map;
36
+ return (_marks$filter$map = marks === null || marks === void 0 ? void 0 : marks.filter(mark => mark.type.name === 'annotation').map(mark => mark.attrs.id)) !== null && _marks$filter$map !== void 0 ? _marks$filter$map : [];
37
+ }, [marks]);
38
+ const activeIds = useInlineCommentsFilter({
39
+ annotationIds,
40
+ filter: {
41
+ state: AnnotationMarkStates.ACTIVE
42
+ }
43
+ });
44
+ const [focusedId, setFocusedId] = useState();
45
+ const [entered, setEntered] = useState(false);
46
+ const {
47
+ currentSelectedAnnotationId
48
+ } = useAnnotationManagerState();
49
+ const active = activeIds.some(id => id === focusedId || id === currentSelectedAnnotationId);
50
+ useEffect(() => {
51
+ if (!updateSubscriber) {
52
+ return;
53
+ }
54
+ const onFocus = payload => setFocusedId(payload === null || payload === void 0 ? void 0 : payload.annotationId);
55
+ const onBlur = () => setFocusedId(undefined);
56
+ updateSubscriber.on(AnnotationUpdateEvent.SET_ANNOTATION_FOCUS, onFocus);
57
+ updateSubscriber.on(AnnotationUpdateEvent.REMOVE_ANNOTATION_FOCUS, onBlur);
58
+ return () => {
59
+ updateSubscriber.off(AnnotationUpdateEvent.SET_ANNOTATION_FOCUS, onFocus);
60
+ updateSubscriber.off(AnnotationUpdateEvent.REMOVE_ANNOTATION_FOCUS, onBlur);
61
+ };
62
+ }, [updateSubscriber]);
63
+ const onClick = useCallback(event => {
64
+ event.preventDefault();
65
+ event.stopPropagation();
66
+ // Anchor the thread to this chart, not an icon inside the badge or a nested renderer.
67
+ const extension = event.currentTarget.closest('.ak-renderer-extension');
68
+ if (extension && activeIds.length) {
69
+ updateSubscriber === null || updateSubscriber === void 0 ? void 0 : updateSubscriber.emit(AnnotationUpdateEvent.ON_ANNOTATION_CLICK, {
70
+ annotationIds: activeIds,
71
+ eventTarget: extension,
72
+ eventTargetType: 'extension',
73
+ viewMethod: VIEW_METHOD.BADGE
74
+ });
75
+ }
76
+ }, [activeIds, updateSubscriber]);
77
+ const onMouseEnter = useCallback(() => setEntered(true), []);
78
+ const onMouseLeave = useCallback(() => setEntered(false), []);
79
+ return /*#__PURE__*/React.createElement("div", {
80
+ "data-annotation-draft-mark": isDraft || undefined,
81
+ "data-renderer-mark": isDraft || undefined,
82
+ "data-renderer-start-pos": isDraft ? startPos : undefined,
83
+ className: ax(["_kqswh2mm"])
84
+ }, children, allowAnnotations && annotationIds.map(id => /*#__PURE__*/React.createElement(AnnotationComponent, {
85
+ key: id,
86
+ id: id,
87
+ annotationType: AnnotationTypes.INLINE_COMMENT,
88
+ annotationParentIds: annotationParentIds,
89
+ allowAnnotations: true,
90
+ useBlockLevel: true,
91
+ isExtension: true,
92
+ dataAttributes: annotationDataAttributes
93
+ })), enabled && activeIds.length > 0 && updateSubscriber && /*#__PURE__*/React.createElement("div", {
94
+ className: ax(["_kqswstnw _154iu2gc _1xi2u2gc"])
95
+ }, /*#__PURE__*/React.createElement(CommentBadgeNext, {
96
+ status: active ? 'active' : entered ? 'entered' : 'default',
97
+ onClick: onClick,
98
+ onMouseEnter: onMouseEnter,
99
+ onMouseLeave: onMouseLeave
100
+ })));
101
+ };
102
+
103
+ /** Only node-aware provider-approved extensions receive comment visuals. */
104
+ export const ExtensionComments = props => {
105
+ if (!props.enabled && !props.allowAnnotations) {
106
+ return /*#__PURE__*/React.createElement(React.Fragment, null, props.children);
107
+ }
108
+ return /*#__PURE__*/React.createElement(ExtensionCommentsEnabled, {
109
+ enabled: props.enabled,
110
+ allowAnnotations: props.allowAnnotations,
111
+ allowAnnotationsDraftMode: props.allowAnnotationsDraftMode,
112
+ startPos: props.startPos,
113
+ marks: props.marks
114
+ }, props.children);
115
+ };
@@ -4,7 +4,7 @@ import _extends from "@babel/runtime/helpers/extends";
4
4
  * @jsx jsx
5
5
  */
6
6
 
7
- import React, { useEffect, useRef } from 'react';
7
+ import React, { useCallback, useContext, useEffect, useMemo, useRef } from 'react';
8
8
 
9
9
  // eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766
10
10
  import { jsx, css } from '@emotion/react';
@@ -13,8 +13,12 @@ import { overflowShadow } from '@atlaskit/editor-common/ui';
13
13
  import { fg } from '@atlaskit/platform-feature-flags/fg';
14
14
  import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
15
15
  import { RendererCssClassName } from '../../consts';
16
+ import { ProvidersContext } from '../../ui/annotations/context';
17
+ import { useAnnotationHoverDispatch } from '../../ui/annotations/contexts/AnnotationHoverContext';
18
+ import { useAnnotationRangeDispatch } from '../../ui/annotations/contexts/AnnotationRangeContext';
16
19
  import ExtensionRenderer from '../../ui/ExtensionRenderer';
17
20
  import { calcBreakoutWidthCss } from '../utils/breakout';
21
+ import { ExtensionComments } from './extension-comments';
18
22
  const FORGE_EXTENSION_TYPE = 'com.atlassian.ecosystem';
19
23
  /**
20
24
  * Mirrors `FORGE_INLINE_BODIED_PARAM` in `@atlassian/xen-editor-provider`. Duplicated rather than
@@ -68,7 +72,7 @@ const FireExtensionAsInlineAnalytics = ({
68
72
  return null;
69
73
  };
70
74
  export const renderExtension = (content, layout, options = {}, removeOverflow, extensionId, extensionViewportSizes, nodeHeight, localId, shouldDisplayExtensionAsInline, node, isInsideOfInlineExtension) => {
71
- var _node$parameters, _node$parameters$gues, _node$parameters2;
75
+ var _node$parameters, _node$parameters$gues, _node$parameters2, _options$dataAttribut;
72
76
  const overflowContainerClass = !removeOverflow ? RendererCssClassName.EXTENSION_OVERFLOW_CONTAINER : '';
73
77
 
74
78
  // by default, we assume the extension is at top level, (direct child of doc node)
@@ -147,11 +151,17 @@ export const renderExtension = (content, layout, options = {}, removeOverflow, e
147
151
  },
148
152
  "data-layout": layout,
149
153
  "data-local-id": localId,
154
+ "data-renderer-start-pos": (_options$dataAttribut = options.dataAttributes) === null || _options$dataAttribut === void 0 ? void 0 : _options$dataAttribut['data-renderer-start-pos'],
150
155
  "data-testid": "extension--wrapper",
151
156
  "data-node-type": "extension",
157
+ "data-inline-comments-target": options.isCommentTarget || undefined,
152
158
  "data-top-level": isTopLevel || undefined,
153
159
  "data-forge-inline": isNativeForgeInline || undefined,
154
- "data-migrated-inline": isMigratedInlineBodied || undefined
160
+ "data-migrated-inline": isMigratedInlineBodied || undefined,
161
+ onBlur: options.onBlur,
162
+ onFocus: options.onFocus,
163
+ onMouseEnter: options.onMouseEnter,
164
+ onMouseLeave: options.onMouseLeave
155
165
  }, jsx("div", {
156
166
  tabIndex: options.tabIndex
157
167
  // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop
@@ -165,6 +175,7 @@ export const renderExtension = (content, layout, options = {}, removeOverflow, e
165
175
  }, extensionDiv) : extensionDiv;
166
176
  };
167
177
  const Extension = props => {
178
+ var _providers$inlineComm, _props$dataAttributes;
168
179
  const {
169
180
  text,
170
181
  layout = 'default',
@@ -177,6 +188,55 @@ const Extension = props => {
177
188
  localId,
178
189
  isInsideOfInlineExtension
179
190
  } = props;
191
+ const providers = useContext(ProvidersContext);
192
+ const {
193
+ setHoverTarget
194
+ } = useAnnotationRangeDispatch();
195
+ const {
196
+ cancelTimeout,
197
+ initiateTimeout,
198
+ setIsWithinRange
199
+ } = useAnnotationHoverDispatch();
200
+ const isBlockNodeSupported = providers === null || providers === void 0 ? void 0 : (_providers$inlineComm = providers.inlineComment) === null || _providers$inlineComm === void 0 ? void 0 : _providers$inlineComm.isBlockNodeSupported;
201
+ const isCommentTarget = useMemo(() => Boolean(fg('cc_maui_annotations_on_extensions') && props.extensionNode && (isBlockNodeSupported === null || isBlockNodeSupported === void 0 ? void 0 : isBlockNodeSupported(props.extensionNode)) === true), [props.extensionNode, isBlockNodeSupported]);
202
+ const isFullPageRenderer = props.rendererAppearance === 'full-page' || props.rendererAppearance === 'full-width';
203
+ const onMouseEnter = useCallback(event => {
204
+ cancelTimeout();
205
+ if (event.buttons === 0) {
206
+ setHoverTarget === null || setHoverTarget === void 0 ? void 0 : setHoverTarget(event.currentTarget);
207
+ setIsWithinRange(true);
208
+ }
209
+ }, [cancelTimeout, setHoverTarget, setIsWithinRange]);
210
+ const onMouseLeave = useCallback(() => initiateTimeout(), [initiateTimeout]);
211
+ const onFocus = useCallback(event => {
212
+ if (event.currentTarget.contains(event.relatedTarget)) {
213
+ return;
214
+ }
215
+ cancelTimeout();
216
+ setHoverTarget === null || setHoverTarget === void 0 ? void 0 : setHoverTarget(event.currentTarget);
217
+ setIsWithinRange(true);
218
+ }, [cancelTimeout, setHoverTarget, setIsWithinRange]);
219
+ const onBlur = useCallback(event => {
220
+ if (!event.currentTarget.contains(event.relatedTarget)) {
221
+ initiateTimeout();
222
+ }
223
+ }, [initiateTimeout]);
224
+ const commentTargetOptions = isCommentTarget && isFullPageRenderer ? {
225
+ isCommentTarget: true,
226
+ onBlur,
227
+ onFocus,
228
+ onMouseEnter,
229
+ onMouseLeave
230
+ } : {
231
+ isCommentTarget
232
+ };
233
+ const commentProps = {
234
+ enabled: isCommentTarget,
235
+ allowAnnotations: props.allowAnnotations && fg('cc_maui_annotations_on_extensions'),
236
+ allowAnnotationsDraftMode: props.allowAnnotationsDraftMode,
237
+ startPos: (_props$dataAttributes = props.dataAttributes) === null || _props$dataAttributes === void 0 ? void 0 : _props$dataAttributes['data-renderer-start-pos'],
238
+ marks: props.marks
239
+ };
180
240
  return jsx(ExtensionRenderer
181
241
  // Ignored via go/ees005
182
242
  // eslint-disable-next-line react/jsx-props-no-spreading
@@ -190,12 +250,20 @@ const Extension = props => {
190
250
  try {
191
251
  // Return the result directly if it's a valid JSX.Element
192
252
  if (result && /*#__PURE__*/React.isValidElement(result)) {
193
- return renderExtension(result, layout, {
253
+ return renderExtension(jsx(ExtensionComments, {
254
+ enabled: commentProps.enabled,
255
+ allowAnnotations: commentProps.allowAnnotations,
256
+ allowAnnotationsDraftMode: commentProps.allowAnnotationsDraftMode,
257
+ startPos: commentProps.startPos,
258
+ marks: commentProps.marks
259
+ }, result), layout, {
260
+ dataAttributes: props.dataAttributes,
194
261
  isTopLevel: path.length < 1,
195
262
  handleRef,
196
263
  shadowClassNames,
197
264
  tabIndex: props.tabIndex,
198
- rendererAppearance: props.rendererAppearance
265
+ rendererAppearance: props.rendererAppearance,
266
+ ...commentTargetOptions
199
267
  }, undefined, parameters === null || parameters === void 0 ? void 0 : parameters.extensionId, extensionViewportSizes, nodeHeight, localId, undefined, undefined, isInsideOfInlineExtension);
200
268
  }
201
269
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -208,12 +276,20 @@ const Extension = props => {
208
276
  }
209
277
 
210
278
  // Always return default content if anything goes wrong
211
- return renderExtension(text || 'extension', layout, {
279
+ return renderExtension(jsx(ExtensionComments, {
280
+ enabled: commentProps.enabled,
281
+ allowAnnotations: commentProps.allowAnnotations,
282
+ allowAnnotationsDraftMode: commentProps.allowAnnotationsDraftMode,
283
+ startPos: commentProps.startPos,
284
+ marks: commentProps.marks
285
+ }, text || 'extension'), layout, {
286
+ dataAttributes: props.dataAttributes,
212
287
  isTopLevel: path.length < 1,
213
288
  handleRef,
214
289
  shadowClassNames,
215
290
  tabIndex: props.tabIndex,
216
- rendererAppearance: props.rendererAppearance
291
+ rendererAppearance: props.rendererAppearance,
292
+ ...commentTargetOptions
217
293
  }, undefined, parameters === null || parameters === void 0 ? void 0 : parameters.extensionId, extensionViewportSizes, nodeHeight, localId, undefined, undefined, isInsideOfInlineExtension);
218
294
  });
219
295
  };
@@ -219,6 +219,24 @@ export function resolvePos(node, offset, findEnd = false) {
219
219
  }
220
220
  return resolvedPos + offset;
221
221
  }
222
+ function getSelectedCommentTargetPosition(range) {
223
+ const {
224
+ startContainer,
225
+ startOffset,
226
+ endContainer,
227
+ endOffset
228
+ } = range;
229
+ if (startContainer !== endContainer || endOffset !== startOffset + 1) {
230
+ return false;
231
+ }
232
+ const selectedNode = startContainer.childNodes[startOffset];
233
+ if (!selectedNode || !isElementNode(selectedNode) || selectedNode.dataset.inlineCommentsTarget !== 'true' || !isPositionPointer(selectedNode)) {
234
+ return false;
235
+ }
236
+
237
+ // Subtract 1 from the renderer start position to get the position before the extension node.
238
+ return getStartPos(selectedNode) - 1;
239
+ }
222
240
  export function getPosFromRange(range) {
223
241
  const {
224
242
  startContainer,
@@ -226,6 +244,13 @@ export function getPosFromRange(range) {
226
244
  endContainer,
227
245
  endOffset
228
246
  } = range;
247
+ const selectedCommentTargetPosition = fg('cc_maui_annotations_on_extensions') && getSelectedCommentTargetPosition(range);
248
+ if (selectedCommentTargetPosition !== false) {
249
+ return {
250
+ from: selectedCommentTargetPosition,
251
+ to: selectedCommentTargetPosition
252
+ };
253
+ }
229
254
  const possibleMediaOrMediaSingleElement = findParent(startContainer);
230
255
 
231
256
  // Video hover targets return media single, not media, thus, the extra check in condition.
@@ -57,7 +57,7 @@ export const DEGRADED_SEVERITY_THRESHOLD = 3000;
57
57
  const TABLE_INFO_TIMEOUT = 10000;
58
58
  const RENDER_EVENT_SAMPLE_RATE = 0.1;
59
59
  const packageName = "@atlaskit/renderer";
60
- const packageVersion = "148.0.4";
60
+ const packageVersion = "148.1.0";
61
61
  const setAsQueryContainerStyles = css({
62
62
  containerName: 'ak-renderer-wrapper',
63
63
  containerType: 'inline-size'
@@ -1,4 +1,5 @@
1
- import React, { createContext, useCallback, useContext, useMemo, useReducer } from 'react';
1
+ import React, { createContext, useCallback, useContext, useMemo, useReducer, useRef } from 'react';
2
+ import { fg } from '@atlaskit/platform-feature-flags/fg';
2
3
 
3
4
  // oxlint-disable-next-line eslint/no-redeclare
4
5
 
@@ -117,8 +118,10 @@ export const AnnotationRangeDispatchContext = /*#__PURE__*/createContext({
117
118
  });
118
119
  export const AnnotationRangeProviderInner = ({
119
120
  children,
120
- allowCommentsOnMedia
121
+ allowCommentsOnMedia,
122
+ hasBlockNodeSupport
121
123
  }) => {
124
+ const extensionCommentsEnabled = !!hasBlockNodeSupport && fg('cc_maui_annotations_on_extensions');
122
125
  const [{
123
126
  selectionRange,
124
127
  hoverRange,
@@ -138,21 +141,32 @@ export const AnnotationRangeProviderInner = ({
138
141
  type: 'setSelection',
139
142
  range
140
143
  }), []);
144
+ const hoverDraftRangeRef = useRef(hoverDraftRange);
145
+ hoverDraftRangeRef.current = hoverDraftRange;
141
146
  const setHoverTarget = useCallback(target => {
142
- // the HoverComponent expects an element deeply nested inside media, these classes work with the current implementation
143
- const mediaNode = target.querySelector('.media-card-inline-player, .media-file-card-view');
144
- if (!mediaNode) {
147
+ const draftRange = hoverDraftRangeRef.current;
148
+ const draftTarget = draftRange === null || draftRange === void 0 ? void 0 : draftRange.startContainer.childNodes[draftRange.startOffset];
149
+ if (extensionCommentsEnabled && draftTarget instanceof HTMLElement && draftTarget.matches('.ak-renderer-extension[data-inline-comments-target="true"]')) {
150
+ // Keep the composer callbacks bound to the chart chosen when its draft opened.
151
+ return;
152
+ }
153
+ // The HoverComponent expects an element deeply nested inside media or the eligible
154
+ // block node's wrapper. Each selector is only included when its comment type is
155
+ // supported, so hover targeting never activates on an ineligible block node.
156
+ const selectors = [allowCommentsOnMedia && '.media-card-inline-player, .media-file-card-view', extensionCommentsEnabled && '.ak-renderer-extension[data-inline-comments-target="true"]'].filter(selector => Boolean(selector)).join(', ');
157
+ const hoverableNode = extensionCommentsEnabled && target.matches('.ak-renderer-extension[data-inline-comments-target="true"]') ? target : selectors ? target.querySelector(selectors) : null;
158
+ if (!hoverableNode) {
145
159
  return;
146
160
  }
147
- // eslint-disable-next-line @atlaskit/platform/no-direct-document-usage -- range for media hover highlight
161
+ // eslint-disable-next-line @atlaskit/platform/no-direct-document-usage -- range for media/extension hover highlight
148
162
  const range = document.createRange();
149
- range.setStartBefore(mediaNode);
150
- range.setEndAfter(mediaNode);
163
+ range.setStartBefore(hoverableNode);
164
+ range.setEndAfter(hoverableNode);
151
165
  dispatch({
152
166
  type: 'setHover',
153
167
  range
154
168
  });
155
- }, []);
169
+ }, [allowCommentsOnMedia, extensionCommentsEnabled]);
156
170
  const promoteSelectionToDraft = useCallback(position => {
157
171
  dispatch({
158
172
  type: 'promoteSelectionToDraft',
@@ -191,12 +205,12 @@ export const AnnotationRangeProviderInner = ({
191
205
  clearSelectionRange,
192
206
  clearHoverRange,
193
207
  setSelectionRange,
194
- setHoverTarget: !!allowCommentsOnMedia ? setHoverTarget : undefined,
208
+ setHoverTarget: allowCommentsOnMedia || extensionCommentsEnabled ? setHoverTarget : undefined,
195
209
  promoteSelectionToDraft,
196
210
  promoteHoverToDraft,
197
211
  clearSelectionDraft,
198
212
  clearHoverDraft
199
- }), [allowCommentsOnMedia, clearSelectionRange, clearHoverRange, setSelectionRange, setHoverTarget, promoteSelectionToDraft, promoteHoverToDraft, clearSelectionDraft, clearHoverDraft]);
213
+ }), [allowCommentsOnMedia, extensionCommentsEnabled, clearSelectionRange, clearHoverRange, setSelectionRange, setHoverTarget, promoteSelectionToDraft, promoteHoverToDraft, clearSelectionDraft, clearHoverDraft]);
200
214
  return /*#__PURE__*/React.createElement(AnnotationRangeStateContext.Provider, {
201
215
  value: stateData
202
216
  }, /*#__PURE__*/React.createElement(AnnotationRangeDispatchContext.Provider, {
@@ -206,6 +220,7 @@ export const AnnotationRangeProviderInner = ({
206
220
  export const AnnotationRangeProvider = ({
207
221
  children,
208
222
  allowCommentsOnMedia,
223
+ hasBlockNodeSupport,
209
224
  isNestedRender
210
225
  }) => {
211
226
  /*
@@ -214,7 +229,8 @@ export const AnnotationRangeProvider = ({
214
229
  * and we need the original context to create annotations on extensions.
215
230
  */
216
231
  return isNestedRender ? /*#__PURE__*/React.createElement(React.Fragment, null, children) : /*#__PURE__*/React.createElement(AnnotationRangeProviderInner, {
217
- allowCommentsOnMedia: allowCommentsOnMedia
232
+ allowCommentsOnMedia: allowCommentsOnMedia,
233
+ hasBlockNodeSupport: hasBlockNodeSupport
218
234
  }, children);
219
235
  };
220
236
  export const useAnnotationRangeState = () => {
@@ -0,0 +1,6 @@
1
+ ._154iidpf{top:0}
2
+ ._1ltvidpf{left:0}
3
+ ._1xi2idpf{right:0}
4
+ ._94n5idpf{bottom:0}
5
+ ._kqswstnw{position:absolute}
6
+ ._lcxvglyw{pointer-events:none}
@@ -0,0 +1,26 @@
1
+ /* extension.tsx generated by @compiled/babel-plugin v3.0.2 */
2
+ import "./extension.compiled.css";
3
+ import { ax, ix } from "@compiled/react/runtime";
4
+ import React from 'react';
5
+ const styles = {
6
+ anchor: "_kqswstnw _154iidpf _1xi2idpf _94n5idpf _1ltvidpf _lcxvglyw"
7
+ };
8
+
9
+ /** Full-chart anchors for comment navigation, without wrapping or intercepting the iframe. */
10
+ export const ExtensionAnnotation = ({
11
+ id,
12
+ dataAttributes,
13
+ markRef
14
+ }) => /*#__PURE__*/React.createElement("div", {
15
+ id: id,
16
+ ref: markRef,
17
+ "data-id": dataAttributes['data-id'],
18
+ "data-mark-type": dataAttributes['data-mark-type'],
19
+ "data-mark-annotation-type": dataAttributes['data-mark-annotation-type'],
20
+ "data-mark-annotation-state": dataAttributes['data-mark-annotation-state'],
21
+ "data-block-mark": dataAttributes['data-block-mark'],
22
+ "data-renderer-mark": dataAttributes['data-renderer-mark'],
23
+ "data-has-focus": dataAttributes['data-has-focus'],
24
+ "data-is-hovered": dataAttributes['data-is-hovered'],
25
+ className: ax([styles.anchor])
26
+ });
@@ -12,7 +12,8 @@ const MarkElement = ({
12
12
  children,
13
13
  dataAttributes,
14
14
  id,
15
- useBlockLevel
15
+ useBlockLevel,
16
+ isExtension
16
17
  }) => {
17
18
  const updateSubscriber = useInlineCommentSubscriberContext();
18
19
  const states = useContext(InlineCommentsStateContext);
@@ -93,7 +94,8 @@ const MarkElement = ({
93
94
  hasFocus: hasFocus,
94
95
  isHovered: isHovered,
95
96
  state: states[id],
96
- useBlockLevel: useBlockLevel
97
+ useBlockLevel: useBlockLevel,
98
+ isExtension: isExtension
97
99
  }, children);
98
100
  };
99
101
  export { MarkElement };