@atlaskit/editor-plugin-breakout 2.6.1 → 2.7.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 CHANGED
@@ -1,5 +1,18 @@
1
1
  # @atlaskit/editor-plugin-breakout
2
2
 
3
+ ## 2.7.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#166997](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/pull-requests/166997)
8
+ [`826de0a17dc7e`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/826de0a17dc7e) -
9
+ Introduce new breakout oprational event in editor-common, export the type and use in
10
+ editor-plugin-breakout. Add performance measurement for FPS for resizing
11
+
12
+ ### Patch Changes
13
+
14
+ - Updated dependencies
15
+
3
16
  ## 2.6.1
4
17
 
5
18
  ### Patch Changes
@@ -10,6 +10,8 @@ var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
10
10
  var _setBreakoutWidth = require("../editor-commands/set-breakout-width");
11
11
  var _getGuidelines = require("./get-guidelines");
12
12
  var _resizingMarkView = require("./resizing-mark-view");
13
+ var _analytics = require("./utils/analytics");
14
+ var _measureFramerate2 = require("./utils/measure-framerate");
13
15
  var RESIZE_RATIO = 2;
14
16
  var SNAP_GAP = 10;
15
17
  var WIDTHS = {
@@ -48,22 +50,38 @@ function createResizerCallbacks(_ref2) {
48
50
  api = _ref2.api;
49
51
  var node = null;
50
52
  var guidelines = [];
53
+ var _measureFramerate = (0, _measureFramerate2.measureFramerate)(),
54
+ startMeasure = _measureFramerate.startMeasure,
55
+ endMeasure = _measureFramerate.endMeasure,
56
+ countFrames = _measureFramerate.countFrames;
51
57
  var getEditorWidth = function getEditorWidth() {
52
58
  var _api$width;
53
59
  return api === null || api === void 0 || (_api$width = api.width) === null || _api$width === void 0 ? void 0 : _api$width.sharedState.currentState();
54
60
  };
55
61
  return {
56
62
  onDragStart: function onDragStart() {
57
- var _api$userIntent;
58
- api === null || api === void 0 || api.core.actions.execute((_api$userIntent = api.userIntent) === null || _api$userIntent === void 0 ? void 0 : _api$userIntent.commands.setCurrentUserIntent('dragging'));
59
- view.dispatch(view.state.tr.setMeta('is-resizer-resizing', true));
63
+ if ((0, _platformFeatureFlags.fg)('platform_editor_breakout_resizing_hello_release')) {
64
+ startMeasure();
65
+ }
66
+ api === null || api === void 0 || api.core.actions.execute(function (_ref3) {
67
+ var _api$userIntent;
68
+ var tr = _ref3.tr;
69
+ (_api$userIntent = api.userIntent) === null || _api$userIntent === void 0 || _api$userIntent.commands.setCurrentUserIntent('dragging')({
70
+ tr: tr
71
+ });
72
+ tr.setMeta('is-resizer-resizing', true);
73
+ return tr;
74
+ });
60
75
  var pos = view.posAtDOM(dom, 0);
61
76
  node = view.state.doc.nodeAt(pos);
62
77
  },
63
- onDrag: function onDrag(_ref3) {
78
+ onDrag: function onDrag(_ref4) {
64
79
  var _node, _api$guideline;
65
- var location = _ref3.location,
66
- source = _ref3.source;
80
+ var location = _ref4.location,
81
+ source = _ref4.source;
82
+ if ((0, _platformFeatureFlags.fg)('platform_editor_breakout_resizing_hello_release')) {
83
+ countFrames();
84
+ }
67
85
  var initialWidth = mark.attrs.width;
68
86
  var newWidth = getProposedWidth({
69
87
  initialWidth: initialWidth,
@@ -77,10 +95,19 @@ function createResizerCallbacks(_ref2) {
77
95
  });
78
96
  contentDOM.style.setProperty(_resizingMarkView.LOCAL_RESIZE_PROPERTY, "".concat(newWidth, "px"));
79
97
  },
80
- onDrop: function onDrop(_ref4) {
81
- var _api$guideline2, _api$userIntent2;
82
- var location = _ref4.location,
83
- source = _ref4.source;
98
+ onDrop: function onDrop(_ref5) {
99
+ var _api$guideline2;
100
+ var location = _ref5.location,
101
+ source = _ref5.source;
102
+ var payloads = [];
103
+ if ((0, _platformFeatureFlags.fg)('platform_editor_breakout_resizing_hello_release')) {
104
+ var frameRateSamples = endMeasure();
105
+ payloads = (0, _analytics.generateResizeFrameRatePayloads)({
106
+ docSize: view.state.doc.nodeSize,
107
+ frameRateSamples: (0, _measureFramerate2.reduceResizeFrameRateSamples)(frameRateSamples),
108
+ originalNode: node
109
+ });
110
+ }
84
111
  var isResizedToFullWidth = !!guidelines.find(function (guideline) {
85
112
  return guideline.key.includes('full_width') && guideline.active;
86
113
  });
@@ -99,8 +126,19 @@ function createResizerCallbacks(_ref2) {
99
126
  });
100
127
  (0, _setBreakoutWidth.setBreakoutWidth)(newWidth, mode, pos)(view.state, view.dispatch);
101
128
  contentDOM.style.removeProperty(_resizingMarkView.LOCAL_RESIZE_PROPERTY);
102
- view.dispatch(view.state.tr.setMeta('is-resizer-resizing', false).setMeta('scrollIntoView', false));
103
- api === null || api === void 0 || api.core.actions.execute((_api$userIntent2 = api.userIntent) === null || _api$userIntent2 === void 0 ? void 0 : _api$userIntent2.commands.setCurrentUserIntent('default'));
129
+ api === null || api === void 0 || api.core.actions.execute(function (_ref6) {
130
+ var _api$userIntent2;
131
+ var tr = _ref6.tr;
132
+ (_api$userIntent2 = api.userIntent) === null || _api$userIntent2 === void 0 || _api$userIntent2.commands.setCurrentUserIntent('default')({
133
+ tr: tr
134
+ });
135
+ tr.setMeta('is-resizer-resizing', false).setMeta('scrollIntoView', false);
136
+ payloads.forEach(function (payload) {
137
+ var _api$analytics;
138
+ (_api$analytics = api.analytics) === null || _api$analytics === void 0 || (_api$analytics = _api$analytics.actions) === null || _api$analytics === void 0 || _api$analytics.attachAnalyticsEvent(payload)(tr);
139
+ });
140
+ return tr;
141
+ });
104
142
  }
105
143
  };
106
144
  }
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.generateResizeFrameRatePayloads = void 0;
7
+ var _analytics = require("@atlaskit/editor-common/analytics");
8
+ var generateResizeFrameRatePayloads = exports.generateResizeFrameRatePayloads = function generateResizeFrameRatePayloads(props) {
9
+ return props.frameRateSamples.map(function (frameRateSample, index) {
10
+ return {
11
+ action: _analytics.ACTION.RESIZED_PERF_SAMPLING,
12
+ actionSubject: _analytics.ACTION_SUBJECT.ELEMENT,
13
+ eventType: _analytics.EVENT_TYPE.OPERATIONAL,
14
+ attributes: {
15
+ nodeType: props.originalNode.type.name,
16
+ frameRate: frameRateSample,
17
+ nodeSize: props.originalNode.nodeSize,
18
+ docSize: props.docSize,
19
+ isInitialSample: index === 0
20
+ }
21
+ };
22
+ });
23
+ };
@@ -0,0 +1,122 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.reduceResizeFrameRateSamples = exports.measureFramerate = void 0;
7
+ /**
8
+ * Measure the FPS of a resizing component.
9
+ *
10
+ * This is a simplified version of the `useMeasureFramerate` hook from `editor-plugin-table`.
11
+ * (packages/editor/editor-plugin-table/src/pm-plugins/utils/analytics.ts)
12
+ */
13
+
14
+ var reduceResizeFrameRateSamples = exports.reduceResizeFrameRateSamples = function reduceResizeFrameRateSamples(frameRateSamples) {
15
+ if (frameRateSamples.length > 1) {
16
+ var frameRateSum = frameRateSamples.reduce(function (sum, frameRate, index) {
17
+ if (index === 0) {
18
+ return sum;
19
+ } else {
20
+ return sum + frameRate;
21
+ }
22
+ }, 0);
23
+ var averageFrameRate = Math.round(frameRateSum / (frameRateSamples.length - 1));
24
+ return [frameRateSamples[0], averageFrameRate];
25
+ } else {
26
+ return frameRateSamples;
27
+ }
28
+ };
29
+ /**
30
+ * Measures the framerate of a component over a given time period.
31
+ * @param {object} [config] - Configuration options for framerate measurement.
32
+ * @returns {object} An object containing startMeasure, endMeasure, and countFrames methods.
33
+ * @example
34
+ * const { startMeasure, endMeasure, countFrames } = measureFramerate();
35
+ * startMeasure();
36
+ * // ... animation loop with countFrames() calls
37
+ * const samples = endMeasure(); // [60, 58, 62]
38
+ */
39
+ var measureFramerate = exports.measureFramerate = function measureFramerate(config) {
40
+ var _ref = config || {},
41
+ _ref$maxSamples = _ref.maxSamples,
42
+ maxSamples = _ref$maxSamples === void 0 ? 10 : _ref$maxSamples,
43
+ _ref$minFrames = _ref.minFrames,
44
+ minFrames = _ref$minFrames === void 0 ? 5 : _ref$minFrames,
45
+ _ref$minTimeMs = _ref.minTimeMs,
46
+ minTimeMs = _ref$minTimeMs === void 0 ? 500 : _ref$minTimeMs,
47
+ _ref$sampleRateMs = _ref.sampleRateMs,
48
+ sampleRateMs = _ref$sampleRateMs === void 0 ? 1000 : _ref$sampleRateMs,
49
+ _ref$timeoutMs = _ref.timeoutMs,
50
+ timeoutMs = _ref$timeoutMs === void 0 ? 200 : _ref$timeoutMs;
51
+ var frameCount = 0;
52
+ var lastTime = 0;
53
+ var timeoutId;
54
+ var frameRateSamples = [];
55
+ var startMeasure = function startMeasure() {
56
+ frameCount = 0;
57
+ lastTime = performance.now();
58
+ };
59
+
60
+ /**
61
+ * Returns an array of frame rate samples as integers.
62
+ * @returns {number[]} An array of frame rate samples as integers.
63
+ * @example
64
+ * const samples = endMeasure(); // [60, 58, 62]
65
+ */
66
+ var endMeasure = function endMeasure() {
67
+ var samples = frameRateSamples;
68
+ frameRateSamples = [];
69
+ return samples;
70
+ };
71
+ var sampleFrameRate = function sampleFrameRate() {
72
+ var delay = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
73
+ var currentTime = performance.now();
74
+ var deltaTime = currentTime - lastTime - delay;
75
+ var isValidSample = deltaTime > minTimeMs && frameCount >= minFrames;
76
+ if (isValidSample) {
77
+ var frameRate = Math.round(frameCount / (deltaTime / 1000));
78
+ frameRateSamples.push(frameRate);
79
+ }
80
+ frameCount = 0;
81
+ lastTime = 0;
82
+ };
83
+
84
+ /**
85
+ * Counts the number of frames that occur within a given time period. Intended to be called
86
+ * inside a `requestAnimationFrame` callback.
87
+ * @example
88
+ * const animate = () => {
89
+ * countFrames();
90
+ * requestAnimationFrame(animate);
91
+ * };
92
+ */
93
+ var countFrames = function countFrames() {
94
+ if (frameRateSamples.length >= maxSamples && timeoutId) {
95
+ clearTimeout(timeoutId);
96
+ return;
97
+ }
98
+
99
+ /**
100
+ * Allows us to keep counting frames even if `startMeasure` is not called
101
+ */
102
+ if (lastTime === 0) {
103
+ lastTime = performance.now();
104
+ }
105
+ frameCount++;
106
+ if (timeoutId) {
107
+ clearTimeout(timeoutId);
108
+ }
109
+ if (performance.now() - lastTime > sampleRateMs) {
110
+ sampleFrameRate();
111
+ } else {
112
+ timeoutId = setTimeout(function () {
113
+ return sampleFrameRate(timeoutMs);
114
+ }, timeoutMs);
115
+ }
116
+ };
117
+ return {
118
+ startMeasure: startMeasure,
119
+ endMeasure: endMeasure,
120
+ countFrames: countFrames
121
+ };
122
+ };
@@ -3,6 +3,8 @@ import { fg } from '@atlaskit/platform-feature-flags';
3
3
  import { setBreakoutWidth } from '../editor-commands/set-breakout-width';
4
4
  import { getGuidelines } from './get-guidelines';
5
5
  import { LOCAL_RESIZE_PROPERTY } from './resizing-mark-view';
6
+ import { generateResizeFrameRatePayloads } from './utils/analytics';
7
+ import { measureFramerate, reduceResizeFrameRateSamples } from './utils/measure-framerate';
6
8
  const RESIZE_RATIO = 2;
7
9
  const SNAP_GAP = 10;
8
10
  const WIDTHS = {
@@ -42,15 +44,30 @@ export function createResizerCallbacks({
42
44
  }) {
43
45
  let node = null;
44
46
  let guidelines = [];
47
+ const {
48
+ startMeasure,
49
+ endMeasure,
50
+ countFrames
51
+ } = measureFramerate();
45
52
  const getEditorWidth = () => {
46
53
  var _api$width;
47
54
  return api === null || api === void 0 ? void 0 : (_api$width = api.width) === null || _api$width === void 0 ? void 0 : _api$width.sharedState.currentState();
48
55
  };
49
56
  return {
50
57
  onDragStart: () => {
51
- var _api$userIntent;
52
- api === null || api === void 0 ? void 0 : api.core.actions.execute((_api$userIntent = api.userIntent) === null || _api$userIntent === void 0 ? void 0 : _api$userIntent.commands.setCurrentUserIntent('dragging'));
53
- view.dispatch(view.state.tr.setMeta('is-resizer-resizing', true));
58
+ if (fg('platform_editor_breakout_resizing_hello_release')) {
59
+ startMeasure();
60
+ }
61
+ api === null || api === void 0 ? void 0 : api.core.actions.execute(({
62
+ tr
63
+ }) => {
64
+ var _api$userIntent;
65
+ (_api$userIntent = api.userIntent) === null || _api$userIntent === void 0 ? void 0 : _api$userIntent.commands.setCurrentUserIntent('dragging')({
66
+ tr
67
+ });
68
+ tr.setMeta('is-resizer-resizing', true);
69
+ return tr;
70
+ });
54
71
  const pos = view.posAtDOM(dom, 0);
55
72
  node = view.state.doc.nodeAt(pos);
56
73
  },
@@ -59,6 +76,9 @@ export function createResizerCallbacks({
59
76
  source
60
77
  }) => {
61
78
  var _node, _api$guideline, _api$guideline$action;
79
+ if (fg('platform_editor_breakout_resizing_hello_release')) {
80
+ countFrames();
81
+ }
62
82
  const initialWidth = mark.attrs.width;
63
83
  const newWidth = getProposedWidth({
64
84
  initialWidth,
@@ -76,7 +96,16 @@ export function createResizerCallbacks({
76
96
  location,
77
97
  source
78
98
  }) {
79
- var _api$guideline2, _api$guideline2$actio, _api$userIntent2;
99
+ var _api$guideline2, _api$guideline2$actio;
100
+ let payloads = [];
101
+ if (fg('platform_editor_breakout_resizing_hello_release')) {
102
+ const frameRateSamples = endMeasure();
103
+ payloads = generateResizeFrameRatePayloads({
104
+ docSize: view.state.doc.nodeSize,
105
+ frameRateSamples: reduceResizeFrameRateSamples(frameRateSamples),
106
+ originalNode: node
107
+ });
108
+ }
80
109
  const isResizedToFullWidth = !!guidelines.find(guideline => guideline.key.includes('full_width') && guideline.active);
81
110
  guidelines = getGuidelines(false, 0, getEditorWidth);
82
111
  api === null || api === void 0 ? void 0 : (_api$guideline2 = api.guideline) === null || _api$guideline2 === void 0 ? void 0 : (_api$guideline2$actio = _api$guideline2.actions) === null || _api$guideline2$actio === void 0 ? void 0 : _api$guideline2$actio.displayGuideline(view)({
@@ -93,8 +122,20 @@ export function createResizerCallbacks({
93
122
  });
94
123
  setBreakoutWidth(newWidth, mode, pos)(view.state, view.dispatch);
95
124
  contentDOM.style.removeProperty(LOCAL_RESIZE_PROPERTY);
96
- view.dispatch(view.state.tr.setMeta('is-resizer-resizing', false).setMeta('scrollIntoView', false));
97
- api === null || api === void 0 ? void 0 : api.core.actions.execute((_api$userIntent2 = api.userIntent) === null || _api$userIntent2 === void 0 ? void 0 : _api$userIntent2.commands.setCurrentUserIntent('default'));
125
+ api === null || api === void 0 ? void 0 : api.core.actions.execute(({
126
+ tr
127
+ }) => {
128
+ var _api$userIntent2;
129
+ (_api$userIntent2 = api.userIntent) === null || _api$userIntent2 === void 0 ? void 0 : _api$userIntent2.commands.setCurrentUserIntent('default')({
130
+ tr
131
+ });
132
+ tr.setMeta('is-resizer-resizing', false).setMeta('scrollIntoView', false);
133
+ payloads.forEach(payload => {
134
+ var _api$analytics, _api$analytics$action;
135
+ (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : (_api$analytics$action = _api$analytics.actions) === null || _api$analytics$action === void 0 ? void 0 : _api$analytics$action.attachAnalyticsEvent(payload)(tr);
136
+ });
137
+ return tr;
138
+ });
98
139
  }
99
140
  };
100
141
  }
@@ -0,0 +1,15 @@
1
+ import { ACTION, ACTION_SUBJECT, EVENT_TYPE } from '@atlaskit/editor-common/analytics';
2
+ export const generateResizeFrameRatePayloads = props => {
3
+ return props.frameRateSamples.map((frameRateSample, index) => ({
4
+ action: ACTION.RESIZED_PERF_SAMPLING,
5
+ actionSubject: ACTION_SUBJECT.ELEMENT,
6
+ eventType: EVENT_TYPE.OPERATIONAL,
7
+ attributes: {
8
+ nodeType: props.originalNode.type.name,
9
+ frameRate: frameRateSample,
10
+ nodeSize: props.originalNode.nodeSize,
11
+ docSize: props.docSize,
12
+ isInitialSample: index === 0
13
+ }
14
+ }));
15
+ };
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Measure the FPS of a resizing component.
3
+ *
4
+ * This is a simplified version of the `useMeasureFramerate` hook from `editor-plugin-table`.
5
+ * (packages/editor/editor-plugin-table/src/pm-plugins/utils/analytics.ts)
6
+ */
7
+
8
+ export const reduceResizeFrameRateSamples = frameRateSamples => {
9
+ if (frameRateSamples.length > 1) {
10
+ const frameRateSum = frameRateSamples.reduce((sum, frameRate, index) => {
11
+ if (index === 0) {
12
+ return sum;
13
+ } else {
14
+ return sum + frameRate;
15
+ }
16
+ }, 0);
17
+ const averageFrameRate = Math.round(frameRateSum / (frameRateSamples.length - 1));
18
+ return [frameRateSamples[0], averageFrameRate];
19
+ } else {
20
+ return frameRateSamples;
21
+ }
22
+ };
23
+ /**
24
+ * Measures the framerate of a component over a given time period.
25
+ * @param {object} [config] - Configuration options for framerate measurement.
26
+ * @returns {object} An object containing startMeasure, endMeasure, and countFrames methods.
27
+ * @example
28
+ * const { startMeasure, endMeasure, countFrames } = measureFramerate();
29
+ * startMeasure();
30
+ * // ... animation loop with countFrames() calls
31
+ * const samples = endMeasure(); // [60, 58, 62]
32
+ */
33
+ export const measureFramerate = config => {
34
+ const {
35
+ maxSamples = 10,
36
+ minFrames = 5,
37
+ minTimeMs = 500,
38
+ sampleRateMs = 1000,
39
+ timeoutMs = 200
40
+ } = config || {};
41
+ let frameCount = 0;
42
+ let lastTime = 0;
43
+ let timeoutId;
44
+ let frameRateSamples = [];
45
+ const startMeasure = () => {
46
+ frameCount = 0;
47
+ lastTime = performance.now();
48
+ };
49
+
50
+ /**
51
+ * Returns an array of frame rate samples as integers.
52
+ * @returns {number[]} An array of frame rate samples as integers.
53
+ * @example
54
+ * const samples = endMeasure(); // [60, 58, 62]
55
+ */
56
+ const endMeasure = () => {
57
+ const samples = frameRateSamples;
58
+ frameRateSamples = [];
59
+ return samples;
60
+ };
61
+ const sampleFrameRate = (delay = 0) => {
62
+ const currentTime = performance.now();
63
+ const deltaTime = currentTime - lastTime - delay;
64
+ const isValidSample = deltaTime > minTimeMs && frameCount >= minFrames;
65
+ if (isValidSample) {
66
+ const frameRate = Math.round(frameCount / (deltaTime / 1000));
67
+ frameRateSamples.push(frameRate);
68
+ }
69
+ frameCount = 0;
70
+ lastTime = 0;
71
+ };
72
+
73
+ /**
74
+ * Counts the number of frames that occur within a given time period. Intended to be called
75
+ * inside a `requestAnimationFrame` callback.
76
+ * @example
77
+ * const animate = () => {
78
+ * countFrames();
79
+ * requestAnimationFrame(animate);
80
+ * };
81
+ */
82
+ const countFrames = () => {
83
+ if (frameRateSamples.length >= maxSamples && timeoutId) {
84
+ clearTimeout(timeoutId);
85
+ return;
86
+ }
87
+
88
+ /**
89
+ * Allows us to keep counting frames even if `startMeasure` is not called
90
+ */
91
+ if (lastTime === 0) {
92
+ lastTime = performance.now();
93
+ }
94
+ frameCount++;
95
+ if (timeoutId) {
96
+ clearTimeout(timeoutId);
97
+ }
98
+ if (performance.now() - lastTime > sampleRateMs) {
99
+ sampleFrameRate();
100
+ } else {
101
+ timeoutId = setTimeout(() => sampleFrameRate(timeoutMs), timeoutMs);
102
+ }
103
+ };
104
+ return {
105
+ startMeasure,
106
+ endMeasure,
107
+ countFrames
108
+ };
109
+ };
@@ -3,6 +3,8 @@ import { fg } from '@atlaskit/platform-feature-flags';
3
3
  import { setBreakoutWidth } from '../editor-commands/set-breakout-width';
4
4
  import { getGuidelines } from './get-guidelines';
5
5
  import { LOCAL_RESIZE_PROPERTY } from './resizing-mark-view';
6
+ import { generateResizeFrameRatePayloads } from './utils/analytics';
7
+ import { measureFramerate, reduceResizeFrameRateSamples } from './utils/measure-framerate';
6
8
  var RESIZE_RATIO = 2;
7
9
  var SNAP_GAP = 10;
8
10
  var WIDTHS = {
@@ -41,22 +43,38 @@ export function createResizerCallbacks(_ref2) {
41
43
  api = _ref2.api;
42
44
  var node = null;
43
45
  var guidelines = [];
46
+ var _measureFramerate = measureFramerate(),
47
+ startMeasure = _measureFramerate.startMeasure,
48
+ endMeasure = _measureFramerate.endMeasure,
49
+ countFrames = _measureFramerate.countFrames;
44
50
  var getEditorWidth = function getEditorWidth() {
45
51
  var _api$width;
46
52
  return api === null || api === void 0 || (_api$width = api.width) === null || _api$width === void 0 ? void 0 : _api$width.sharedState.currentState();
47
53
  };
48
54
  return {
49
55
  onDragStart: function onDragStart() {
50
- var _api$userIntent;
51
- api === null || api === void 0 || api.core.actions.execute((_api$userIntent = api.userIntent) === null || _api$userIntent === void 0 ? void 0 : _api$userIntent.commands.setCurrentUserIntent('dragging'));
52
- view.dispatch(view.state.tr.setMeta('is-resizer-resizing', true));
56
+ if (fg('platform_editor_breakout_resizing_hello_release')) {
57
+ startMeasure();
58
+ }
59
+ api === null || api === void 0 || api.core.actions.execute(function (_ref3) {
60
+ var _api$userIntent;
61
+ var tr = _ref3.tr;
62
+ (_api$userIntent = api.userIntent) === null || _api$userIntent === void 0 || _api$userIntent.commands.setCurrentUserIntent('dragging')({
63
+ tr: tr
64
+ });
65
+ tr.setMeta('is-resizer-resizing', true);
66
+ return tr;
67
+ });
53
68
  var pos = view.posAtDOM(dom, 0);
54
69
  node = view.state.doc.nodeAt(pos);
55
70
  },
56
- onDrag: function onDrag(_ref3) {
71
+ onDrag: function onDrag(_ref4) {
57
72
  var _node, _api$guideline;
58
- var location = _ref3.location,
59
- source = _ref3.source;
73
+ var location = _ref4.location,
74
+ source = _ref4.source;
75
+ if (fg('platform_editor_breakout_resizing_hello_release')) {
76
+ countFrames();
77
+ }
60
78
  var initialWidth = mark.attrs.width;
61
79
  var newWidth = getProposedWidth({
62
80
  initialWidth: initialWidth,
@@ -70,10 +88,19 @@ export function createResizerCallbacks(_ref2) {
70
88
  });
71
89
  contentDOM.style.setProperty(LOCAL_RESIZE_PROPERTY, "".concat(newWidth, "px"));
72
90
  },
73
- onDrop: function onDrop(_ref4) {
74
- var _api$guideline2, _api$userIntent2;
75
- var location = _ref4.location,
76
- source = _ref4.source;
91
+ onDrop: function onDrop(_ref5) {
92
+ var _api$guideline2;
93
+ var location = _ref5.location,
94
+ source = _ref5.source;
95
+ var payloads = [];
96
+ if (fg('platform_editor_breakout_resizing_hello_release')) {
97
+ var frameRateSamples = endMeasure();
98
+ payloads = generateResizeFrameRatePayloads({
99
+ docSize: view.state.doc.nodeSize,
100
+ frameRateSamples: reduceResizeFrameRateSamples(frameRateSamples),
101
+ originalNode: node
102
+ });
103
+ }
77
104
  var isResizedToFullWidth = !!guidelines.find(function (guideline) {
78
105
  return guideline.key.includes('full_width') && guideline.active;
79
106
  });
@@ -92,8 +119,19 @@ export function createResizerCallbacks(_ref2) {
92
119
  });
93
120
  setBreakoutWidth(newWidth, mode, pos)(view.state, view.dispatch);
94
121
  contentDOM.style.removeProperty(LOCAL_RESIZE_PROPERTY);
95
- view.dispatch(view.state.tr.setMeta('is-resizer-resizing', false).setMeta('scrollIntoView', false));
96
- api === null || api === void 0 || api.core.actions.execute((_api$userIntent2 = api.userIntent) === null || _api$userIntent2 === void 0 ? void 0 : _api$userIntent2.commands.setCurrentUserIntent('default'));
122
+ api === null || api === void 0 || api.core.actions.execute(function (_ref6) {
123
+ var _api$userIntent2;
124
+ var tr = _ref6.tr;
125
+ (_api$userIntent2 = api.userIntent) === null || _api$userIntent2 === void 0 || _api$userIntent2.commands.setCurrentUserIntent('default')({
126
+ tr: tr
127
+ });
128
+ tr.setMeta('is-resizer-resizing', false).setMeta('scrollIntoView', false);
129
+ payloads.forEach(function (payload) {
130
+ var _api$analytics;
131
+ (_api$analytics = api.analytics) === null || _api$analytics === void 0 || (_api$analytics = _api$analytics.actions) === null || _api$analytics === void 0 || _api$analytics.attachAnalyticsEvent(payload)(tr);
132
+ });
133
+ return tr;
134
+ });
97
135
  }
98
136
  };
99
137
  }
@@ -0,0 +1,17 @@
1
+ import { ACTION, ACTION_SUBJECT, EVENT_TYPE } from '@atlaskit/editor-common/analytics';
2
+ export var generateResizeFrameRatePayloads = function generateResizeFrameRatePayloads(props) {
3
+ return props.frameRateSamples.map(function (frameRateSample, index) {
4
+ return {
5
+ action: ACTION.RESIZED_PERF_SAMPLING,
6
+ actionSubject: ACTION_SUBJECT.ELEMENT,
7
+ eventType: EVENT_TYPE.OPERATIONAL,
8
+ attributes: {
9
+ nodeType: props.originalNode.type.name,
10
+ frameRate: frameRateSample,
11
+ nodeSize: props.originalNode.nodeSize,
12
+ docSize: props.docSize,
13
+ isInitialSample: index === 0
14
+ }
15
+ };
16
+ });
17
+ };
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Measure the FPS of a resizing component.
3
+ *
4
+ * This is a simplified version of the `useMeasureFramerate` hook from `editor-plugin-table`.
5
+ * (packages/editor/editor-plugin-table/src/pm-plugins/utils/analytics.ts)
6
+ */
7
+
8
+ export var reduceResizeFrameRateSamples = function reduceResizeFrameRateSamples(frameRateSamples) {
9
+ if (frameRateSamples.length > 1) {
10
+ var frameRateSum = frameRateSamples.reduce(function (sum, frameRate, index) {
11
+ if (index === 0) {
12
+ return sum;
13
+ } else {
14
+ return sum + frameRate;
15
+ }
16
+ }, 0);
17
+ var averageFrameRate = Math.round(frameRateSum / (frameRateSamples.length - 1));
18
+ return [frameRateSamples[0], averageFrameRate];
19
+ } else {
20
+ return frameRateSamples;
21
+ }
22
+ };
23
+ /**
24
+ * Measures the framerate of a component over a given time period.
25
+ * @param {object} [config] - Configuration options for framerate measurement.
26
+ * @returns {object} An object containing startMeasure, endMeasure, and countFrames methods.
27
+ * @example
28
+ * const { startMeasure, endMeasure, countFrames } = measureFramerate();
29
+ * startMeasure();
30
+ * // ... animation loop with countFrames() calls
31
+ * const samples = endMeasure(); // [60, 58, 62]
32
+ */
33
+ export var measureFramerate = function measureFramerate(config) {
34
+ var _ref = config || {},
35
+ _ref$maxSamples = _ref.maxSamples,
36
+ maxSamples = _ref$maxSamples === void 0 ? 10 : _ref$maxSamples,
37
+ _ref$minFrames = _ref.minFrames,
38
+ minFrames = _ref$minFrames === void 0 ? 5 : _ref$minFrames,
39
+ _ref$minTimeMs = _ref.minTimeMs,
40
+ minTimeMs = _ref$minTimeMs === void 0 ? 500 : _ref$minTimeMs,
41
+ _ref$sampleRateMs = _ref.sampleRateMs,
42
+ sampleRateMs = _ref$sampleRateMs === void 0 ? 1000 : _ref$sampleRateMs,
43
+ _ref$timeoutMs = _ref.timeoutMs,
44
+ timeoutMs = _ref$timeoutMs === void 0 ? 200 : _ref$timeoutMs;
45
+ var frameCount = 0;
46
+ var lastTime = 0;
47
+ var timeoutId;
48
+ var frameRateSamples = [];
49
+ var startMeasure = function startMeasure() {
50
+ frameCount = 0;
51
+ lastTime = performance.now();
52
+ };
53
+
54
+ /**
55
+ * Returns an array of frame rate samples as integers.
56
+ * @returns {number[]} An array of frame rate samples as integers.
57
+ * @example
58
+ * const samples = endMeasure(); // [60, 58, 62]
59
+ */
60
+ var endMeasure = function endMeasure() {
61
+ var samples = frameRateSamples;
62
+ frameRateSamples = [];
63
+ return samples;
64
+ };
65
+ var sampleFrameRate = function sampleFrameRate() {
66
+ var delay = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
67
+ var currentTime = performance.now();
68
+ var deltaTime = currentTime - lastTime - delay;
69
+ var isValidSample = deltaTime > minTimeMs && frameCount >= minFrames;
70
+ if (isValidSample) {
71
+ var frameRate = Math.round(frameCount / (deltaTime / 1000));
72
+ frameRateSamples.push(frameRate);
73
+ }
74
+ frameCount = 0;
75
+ lastTime = 0;
76
+ };
77
+
78
+ /**
79
+ * Counts the number of frames that occur within a given time period. Intended to be called
80
+ * inside a `requestAnimationFrame` callback.
81
+ * @example
82
+ * const animate = () => {
83
+ * countFrames();
84
+ * requestAnimationFrame(animate);
85
+ * };
86
+ */
87
+ var countFrames = function countFrames() {
88
+ if (frameRateSamples.length >= maxSamples && timeoutId) {
89
+ clearTimeout(timeoutId);
90
+ return;
91
+ }
92
+
93
+ /**
94
+ * Allows us to keep counting frames even if `startMeasure` is not called
95
+ */
96
+ if (lastTime === 0) {
97
+ lastTime = performance.now();
98
+ }
99
+ frameCount++;
100
+ if (timeoutId) {
101
+ clearTimeout(timeoutId);
102
+ }
103
+ if (performance.now() - lastTime > sampleRateMs) {
104
+ sampleFrameRate();
105
+ } else {
106
+ timeoutId = setTimeout(function () {
107
+ return sampleFrameRate(timeoutMs);
108
+ }, timeoutMs);
109
+ }
110
+ };
111
+ return {
112
+ startMeasure: startMeasure,
113
+ endMeasure: endMeasure,
114
+ countFrames: countFrames
115
+ };
116
+ };
@@ -1,4 +1,5 @@
1
1
  import type { NextEditorPlugin, OptionalPlugin } from '@atlaskit/editor-common/types';
2
+ import type { AnalyticsPlugin } from '@atlaskit/editor-plugin-analytics';
2
3
  import { type BlockControlsPlugin } from '@atlaskit/editor-plugin-block-controls';
3
4
  import { type EditorDisabledPlugin } from '@atlaskit/editor-plugin-editor-disabled';
4
5
  import type { EditorViewModePlugin } from '@atlaskit/editor-plugin-editor-viewmode';
@@ -20,7 +21,8 @@ export type BreakoutPluginDependencies = [
20
21
  OptionalPlugin<BlockControlsPlugin>,
21
22
  OptionalPlugin<InteractionPlugin>,
22
23
  OptionalPlugin<UserIntentPlugin>,
23
- OptionalPlugin<GuidelinePlugin>
24
+ OptionalPlugin<GuidelinePlugin>,
25
+ OptionalPlugin<AnalyticsPlugin>
24
26
  ];
25
27
  export type BreakoutPlugin = NextEditorPlugin<'breakout', {
26
28
  pluginConfiguration: BreakoutPluginOptions | undefined;
@@ -0,0 +1,7 @@
1
+ import type { BreakoutEventPayload } from '@atlaskit/editor-common/analytics';
2
+ import { Node as PMNode } from '@atlaskit/editor-prosemirror/model';
3
+ export declare const generateResizeFrameRatePayloads: (props: {
4
+ docSize: number;
5
+ frameRateSamples: number[];
6
+ originalNode: PMNode;
7
+ }) => BreakoutEventPayload[];
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Measure the FPS of a resizing component.
3
+ *
4
+ * This is a simplified version of the `useMeasureFramerate` hook from `editor-plugin-table`.
5
+ * (packages/editor/editor-plugin-table/src/pm-plugins/utils/analytics.ts)
6
+ */
7
+ export declare const reduceResizeFrameRateSamples: (frameRateSamples: number[]) => number[];
8
+ type MeasureFramerateConfig = {
9
+ maxSamples?: number;
10
+ minFrames?: number;
11
+ minTimeMs?: number;
12
+ sampleRateMs?: number;
13
+ timeoutMs?: number;
14
+ };
15
+ /**
16
+ * Measures the framerate of a component over a given time period.
17
+ * @param {object} [config] - Configuration options for framerate measurement.
18
+ * @returns {object} An object containing startMeasure, endMeasure, and countFrames methods.
19
+ * @example
20
+ * const { startMeasure, endMeasure, countFrames } = measureFramerate();
21
+ * startMeasure();
22
+ * // ... animation loop with countFrames() calls
23
+ * const samples = endMeasure(); // [60, 58, 62]
24
+ */
25
+ export declare const measureFramerate: (config?: MeasureFramerateConfig) => {
26
+ startMeasure: () => void;
27
+ endMeasure: () => number[];
28
+ countFrames: () => void;
29
+ };
30
+ export {};
@@ -1,4 +1,5 @@
1
1
  import type { NextEditorPlugin, OptionalPlugin } from '@atlaskit/editor-common/types';
2
+ import type { AnalyticsPlugin } from '@atlaskit/editor-plugin-analytics';
2
3
  import { type BlockControlsPlugin } from '@atlaskit/editor-plugin-block-controls';
3
4
  import { type EditorDisabledPlugin } from '@atlaskit/editor-plugin-editor-disabled';
4
5
  import type { EditorViewModePlugin } from '@atlaskit/editor-plugin-editor-viewmode';
@@ -20,7 +21,8 @@ export type BreakoutPluginDependencies = [
20
21
  OptionalPlugin<BlockControlsPlugin>,
21
22
  OptionalPlugin<InteractionPlugin>,
22
23
  OptionalPlugin<UserIntentPlugin>,
23
- OptionalPlugin<GuidelinePlugin>
24
+ OptionalPlugin<GuidelinePlugin>,
25
+ OptionalPlugin<AnalyticsPlugin>
24
26
  ];
25
27
  export type BreakoutPlugin = NextEditorPlugin<'breakout', {
26
28
  pluginConfiguration: BreakoutPluginOptions | undefined;
@@ -0,0 +1,7 @@
1
+ import type { BreakoutEventPayload } from '@atlaskit/editor-common/analytics';
2
+ import { Node as PMNode } from '@atlaskit/editor-prosemirror/model';
3
+ export declare const generateResizeFrameRatePayloads: (props: {
4
+ docSize: number;
5
+ frameRateSamples: number[];
6
+ originalNode: PMNode;
7
+ }) => BreakoutEventPayload[];
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Measure the FPS of a resizing component.
3
+ *
4
+ * This is a simplified version of the `useMeasureFramerate` hook from `editor-plugin-table`.
5
+ * (packages/editor/editor-plugin-table/src/pm-plugins/utils/analytics.ts)
6
+ */
7
+ export declare const reduceResizeFrameRateSamples: (frameRateSamples: number[]) => number[];
8
+ type MeasureFramerateConfig = {
9
+ maxSamples?: number;
10
+ minFrames?: number;
11
+ minTimeMs?: number;
12
+ sampleRateMs?: number;
13
+ timeoutMs?: number;
14
+ };
15
+ /**
16
+ * Measures the framerate of a component over a given time period.
17
+ * @param {object} [config] - Configuration options for framerate measurement.
18
+ * @returns {object} An object containing startMeasure, endMeasure, and countFrames methods.
19
+ * @example
20
+ * const { startMeasure, endMeasure, countFrames } = measureFramerate();
21
+ * startMeasure();
22
+ * // ... animation loop with countFrames() calls
23
+ * const samples = endMeasure(); // [60, 58, 62]
24
+ */
25
+ export declare const measureFramerate: (config?: MeasureFramerateConfig) => {
26
+ startMeasure: () => void;
27
+ endMeasure: () => number[];
28
+ countFrames: () => void;
29
+ };
30
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-breakout",
3
- "version": "2.6.1",
3
+ "version": "2.7.0",
4
4
  "description": "Breakout plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -34,7 +34,7 @@
34
34
  },
35
35
  "dependencies": {
36
36
  "@atlaskit/adf-schema": "^47.6.0",
37
- "@atlaskit/editor-common": "^106.4.0",
37
+ "@atlaskit/editor-common": "^106.5.0",
38
38
  "@atlaskit/editor-plugin-block-controls": "^3.16.0",
39
39
  "@atlaskit/editor-plugin-editor-disabled": "^2.0.0",
40
40
  "@atlaskit/editor-plugin-editor-viewmode": "^4.0.0",
@@ -48,7 +48,7 @@
48
48
  "@atlaskit/platform-feature-flags": "^1.1.0",
49
49
  "@atlaskit/pragmatic-drag-and-drop": "^1.7.0",
50
50
  "@atlaskit/theme": "^18.0.0",
51
- "@atlaskit/tmp-editor-statsig": "^5.14.0",
51
+ "@atlaskit/tmp-editor-statsig": "^6.0.0",
52
52
  "@atlaskit/tokens": "^5.1.0",
53
53
  "@atlaskit/tooltip": "^20.3.0",
54
54
  "@babel/runtime": "^7.0.0",