diff --git a/packages/core/components/DropZone/index.tsx b/packages/core/components/DropZone/index.tsx index c50b263fe1..c029546df5 100644 --- a/packages/core/components/DropZone/index.tsx +++ b/packages/core/components/DropZone/index.tsx @@ -487,7 +487,14 @@ export const DropZoneEdit = forwardRef( [dropRef] ); - const El = as ?? "div"; + const config = useAppStore((s) => s.config); + + const Wrapper = useMemo( + () => config.overrides?.slot || "div", + [config.overrides] + ); + + const El = as ?? Wrapper; return ( ( let zoneCompound = `${areaId}:${zone}`; let content = data?.content || []; + const Wrapper = useMemo( + () => config.overrides?.slot || "div", + [config.overrides] + ); + // Register zones if running Render mode inside editor (i.e. previewMode === "interactive") useEffect(() => { // Only register zones, not slots @@ -593,7 +605,7 @@ const DropZoneRender = forwardRef( } }, [content]); - const El = as ?? "div"; + const El = as ?? Wrapper; if (!data || !config) { return null; diff --git a/packages/core/components/SlotRender/server.tsx b/packages/core/components/SlotRender/server.tsx index ad6cb21bb2..6a55c434e6 100644 --- a/packages/core/components/SlotRender/server.tsx +++ b/packages/core/components/SlotRender/server.tsx @@ -1,4 +1,4 @@ -import { forwardRef } from "react"; +import { forwardRef, useMemo } from "react"; import { DropZoneProps } from "../DropZone/types"; import { ComponentData, @@ -60,7 +60,12 @@ export const SlotRender = forwardRef( { className, style, content, config, metadata, as }, ref ) { - const El = as ?? "div"; + const Wrapper = useMemo( + () => config.overrides?.slot || "div", + [config.overrides] + ); + + const El = as ?? Wrapper; return ( diff --git a/packages/core/lib/bubble-pointer-event.ts b/packages/core/lib/bubble-pointer-event.ts index 2bc125d413..13c551c535 100644 --- a/packages/core/lib/bubble-pointer-event.ts +++ b/packages/core/lib/bubble-pointer-event.ts @@ -2,8 +2,40 @@ export interface BubbledPointerEventType extends PointerEvent { originalTarget: EventTarget | null; } +// Necessary for environments without DOM +class EventShim { + type: string; + bubbles: boolean; + cancelable: boolean; + defaultPrevented: boolean; + + constructor( + type: string, + data: { bubbles?: boolean; cancelable?: boolean } = {} + ) { + this.type = type; + this.bubbles = !!data.bubbles; + this.cancelable = !!data.cancelable; + this.defaultPrevented = false; + } + + preventDefault() { + if (this.cancelable) { + this.defaultPrevented = true; + } + } + + stopPropagation() {} + stopImmediatePropagation() {} +} + // Necessary to enable server build -const BaseEvent = typeof PointerEvent !== "undefined" ? PointerEvent : Event; +const BaseEvent = + typeof PointerEvent !== "undefined" + ? PointerEvent + : typeof Event !== "undefined" + ? Event + : EventShim; export class BubbledPointerEvent extends BaseEvent { _originalTarget: EventTarget | null = null; diff --git a/packages/core/types/API/Overrides.ts b/packages/core/types/API/Overrides.ts index 2e7022a7fc..1e2b31e1ab 100644 --- a/packages/core/types/API/Overrides.ts +++ b/packages/core/types/API/Overrides.ts @@ -81,3 +81,11 @@ export type FieldRenderFunctions< }, "custom" >; + +export interface RenderOverrides { + slot?: RenderFunc<{ + children?: ReactNode; + className?: string; + style?: React.CSSProperties; + }>; +} diff --git a/packages/core/types/API/index.ts b/packages/core/types/API/index.ts index 033c660692..c7beda9827 100644 --- a/packages/core/types/API/index.ts +++ b/packages/core/types/API/index.ts @@ -3,7 +3,7 @@ import { WithDeepSlots } from "../Internal"; import { DefaultComponentProps } from "../Props"; import { AppState } from "./../AppState"; import { ComponentDataOptionalId, Content, Data } from "./../Data"; -import { Overrides } from "./Overrides"; +import { Overrides, RenderOverrides } from "./Overrides"; import { FieldTransforms } from "./FieldTransforms"; import { Config, DefaultComponents } from "../Config"; import { ReactNode } from "react"; @@ -75,6 +75,6 @@ export type RichText = string | ReactNode; export * from "./DropZone"; export * from "./Viewports"; -export type { Overrides }; +export type { Overrides, RenderOverrides }; export * from "./FieldTransforms"; diff --git a/packages/core/types/Config.tsx b/packages/core/types/Config.tsx index d0c2be6c89..4aa1df347a 100644 --- a/packages/core/types/Config.tsx +++ b/packages/core/types/Config.tsx @@ -5,7 +5,7 @@ import { ComponentData, ComponentMetadata, RootData } from "./Data"; import { AsFieldProps, WithChildren, WithId, WithPuckProps } from "./Utils"; import { AppState } from "./AppState"; import { DefaultComponentProps, DefaultRootFieldProps } from "./Props"; -import { Permissions } from "./API"; +import { Permissions, RenderOverrides } from "./API"; import { DropZoneProps } from "../components/DropZone/types"; import { AssertHasValue, @@ -175,6 +175,7 @@ type ConfigInternal< >; }; root?: RootConfigInternal; + overrides?: RenderOverrides; }; // This _deliberately_ casts as any so the user can pass in something that widens the types