@blankdotpage/cake 0.1.37 → 0.1.39

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.
@@ -13,7 +13,9 @@ function toEngineSelection(selection) {
13
13
  };
14
14
  }
15
15
  export const CakeEditor = forwardRef(function CakeEditor(props, outerRef) {
16
+ const rootRef = useRef(null);
16
17
  const containerRef = useRef(null);
18
+ const extensionsRootRef = useRef(null);
17
19
  const engineRef = useRef(null);
18
20
  const onChangeRef = useRef(props.onChange);
19
21
  const onSelectionChangeRef = useRef(props.onSelectionChange);
@@ -33,8 +35,49 @@ export const CakeEditor = forwardRef(function CakeEditor(props, outerRef) {
33
35
  if (!container) {
34
36
  return;
35
37
  }
38
+ const extensionsRoot = document.createElement("div");
39
+ extensionsRoot.className = "cake-extension-overlay";
40
+ extensionsRoot.contentEditable = "false";
41
+ document.body.appendChild(extensionsRoot);
42
+ extensionsRootRef.current = extensionsRoot;
43
+ let rafId = null;
44
+ const syncOverlayPosition = () => {
45
+ rafId = null;
46
+ if (!extensionsRootRef.current) {
47
+ return;
48
+ }
49
+ const rect = container.getBoundingClientRect();
50
+ const overlay = extensionsRootRef.current;
51
+ overlay.style.position = "fixed";
52
+ overlay.style.top = `${rect.top}px`;
53
+ overlay.style.left = `${rect.left}px`;
54
+ overlay.style.width = `${rect.width}px`;
55
+ overlay.style.height = `${rect.height}px`;
56
+ overlay.style.pointerEvents = "none";
57
+ // Important: allow popovers (e.g. mentions) to overflow editor bounds without being clipped.
58
+ overlay.style.overflow = "visible";
59
+ };
60
+ const scheduleSync = () => {
61
+ if (rafId !== null) {
62
+ return;
63
+ }
64
+ rafId = window.requestAnimationFrame(syncOverlayPosition);
65
+ };
66
+ // Initial positioning before engine mounts overlays.
67
+ syncOverlayPosition();
68
+ document.addEventListener("scroll", scheduleSync, {
69
+ capture: true,
70
+ passive: true,
71
+ });
72
+ window.addEventListener("resize", scheduleSync, { passive: true });
73
+ let resizeObserver = null;
74
+ if (typeof ResizeObserver !== "undefined") {
75
+ resizeObserver = new ResizeObserver(scheduleSync);
76
+ resizeObserver.observe(container);
77
+ }
36
78
  const engine = new CakeEditorEngine({
37
79
  container,
80
+ extensionsRoot,
38
81
  value: props.value,
39
82
  selection: props.selection ?? undefined,
40
83
  extensions: extensionsRef.current,
@@ -54,9 +97,17 @@ export const CakeEditor = forwardRef(function CakeEditor(props, outerRef) {
54
97
  engineRef.current = engine;
55
98
  setUiComponents(engine.getUIComponents());
56
99
  return () => {
100
+ document.removeEventListener("scroll", scheduleSync, { capture: true });
101
+ window.removeEventListener("resize", scheduleSync);
102
+ if (rafId !== null) {
103
+ window.cancelAnimationFrame(rafId);
104
+ }
105
+ resizeObserver?.disconnect();
57
106
  engine.destroy();
58
107
  engineRef.current = null;
59
108
  setUiComponents([]);
109
+ extensionsRootRef.current = null;
110
+ extensionsRoot.remove();
60
111
  };
61
112
  }, []);
62
113
  useEffect(() => {
@@ -179,16 +230,25 @@ export const CakeEditor = forwardRef(function CakeEditor(props, outerRef) {
179
230
  getActiveMarks: () => engineRef.current?.getActiveMarks() ?? [],
180
231
  };
181
232
  }, [props.value]);
182
- const containerStyle = props.style
183
- ? { ...props.style }
184
- : {};
185
- if (!containerStyle.position) {
186
- containerStyle.position = "relative";
187
- }
188
- const containerClassName = props.className
189
- ? `cake ${props.className}`
190
- : "cake";
191
- return (_jsxs("div", { style: { position: "relative", height: "100%" }, children: [_jsx("div", { ref: containerRef, className: containerClassName, style: containerStyle, "data-placeholder": props.placeholder, onBlur: (event) => {
233
+ const rootStyle = props.style
234
+ ? { ...props.style, position: props.style.position ?? "relative" }
235
+ : { position: "relative" };
236
+ const rootClassName = props.className
237
+ ? `cake-root ${props.className}`
238
+ : "cake-root";
239
+ const scrollerClassName = props.scrollerClassName
240
+ ? `cake-scroller ${props.scrollerClassName}`
241
+ : "cake-scroller";
242
+ const scrollerBaselineStyle = {
243
+ height: "100%",
244
+ width: "100%",
245
+ overflowY: "auto",
246
+ overflowX: "hidden",
247
+ };
248
+ const scrollerStyle = props.scrollerStyle
249
+ ? { ...scrollerBaselineStyle, ...props.scrollerStyle }
250
+ : scrollerBaselineStyle;
251
+ return (_jsxs("div", { ref: rootRef, className: rootClassName, style: rootStyle, children: [_jsx("div", { ref: containerRef, className: scrollerClassName, "data-placeholder": props.placeholder, style: scrollerStyle, onBlur: (event) => {
192
252
  props.onBlur?.(event.nativeEvent);
193
253
  } }), engineRef.current && uiComponents.length > 0
194
254
  ? createPortal(uiComponents.map((Component, index) => (_jsx(Component, { editor: engineRef.current }, index))), engineRef.current.getOverlayRoot())
@@ -1 +1 @@
1
- {"version":3,"file":"harness.d.ts","sourceRoot":"","sources":["../../../src/cake/test/harness.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"harness.d.ts","sourceRoot":"","sources":["../../../src/cake/test/harness.ts"],"names":[],"mappings":"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"}
@@ -9,7 +9,8 @@ export function createTestHarness(valueOrOptions) {
9
9
  ? { value: valueOrOptions }
10
10
  : valueOrOptions;
11
11
  const container = document.createElement("div");
12
- container.className = "cake";
12
+ // Keep `cake` for backwards-compat in tests, but the production scroller class is `cake-scroller`.
13
+ container.className = "cake cake-scroller";
13
14
  container.style.width = "400px";
14
15
  container.style.height = "200px";
15
16
  container.style.position = "absolute";
@@ -225,6 +226,9 @@ export function createTestHarness(valueOrOptions) {
225
226
  if (!caret) {
226
227
  return null;
227
228
  }
229
+ if (caret.style.display === "none") {
230
+ return null;
231
+ }
228
232
  return {
229
233
  top: parseFloat(caret.style.top),
230
234
  left: parseFloat(caret.style.left),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blankdotpage/cake",
3
- "version": "0.1.37",
3
+ "version": "0.1.39",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -28,6 +28,7 @@
28
28
  "test": "vitest run",
29
29
  "test:browser": "vitest run --project browser",
30
30
  "test:browser:ios": "vitest run --project browser-ios",
31
+ "test:e2e": "playwright test",
31
32
  "typecheck": "tsc -p tsconfig.json --noEmit",
32
33
  "lint": "eslint .",
33
34
  "format": "prettier --write .",