Skip to content

Commit

Permalink
feat: add actionBar override for adding component controls
Browse files Browse the repository at this point in the history
  • Loading branch information
xaviemirmon authored Aug 20, 2024
1 parent 1beaf6b commit 48ec0d7
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 26 deletions.
27 changes: 25 additions & 2 deletions apps/demo/app/custom-ui/[...puckPath]/client.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
"use client";

import { Button, Data, Puck, Render } from "@/core";
import { ActionBar, Button, Data, Puck, Render } from "@/core";
import { HeadingAnalyzer } from "@/plugin-heading-analyzer/src/HeadingAnalyzer";
import config, { UserConfig } from "../../../config";
import { useDemoData } from "../../../lib/use-demo-data";
import { IconButton, usePuck } from "@/core";
import { ReactNode, useEffect, useRef, useState } from "react";
import { Drawer } from "@/core/components/Drawer";
import { ChevronUp, ChevronDown, Globe } from "lucide-react";
import { ChevronUp, ChevronDown, Globe, Bug } from "lucide-react";

const CustomHeader = ({ onPublish }: { onPublish: (data: Data) => void }) => {
const { appState, dispatch } = usePuck();
Expand Down Expand Up @@ -276,6 +276,26 @@ const CustomPuck = ({ dataKey }: { dataKey: string }) => {
);
};

const CustomActionBar = ({ children, label }) => {
const { appState, selectedItem } = usePuck();

const onClick = () => {
alert(
`Index: ${appState.ui.itemSelector.index} \nZone: ${
appState.ui.itemSelector.zone
} \nData: ${JSON.stringify(selectedItem)}`
);
};
return (
<ActionBar label={label}>
<ActionBar.Action onClick={onClick} label="Debug information">
<Bug size={16} />
</ActionBar.Action>
{children}
</ActionBar>
);
};

export function Client({ path, isEdit }: { path: string; isEdit: boolean }) {
const { data, resolvedData, key } = useDemoData({
path,
Expand All @@ -293,6 +313,9 @@ export function Client({ path, isEdit }: { path: string; isEdit: boolean }) {
outline: ({ children }) => (
<div style={{ padding: 16 }}>{children}</div>
),
actionBar: ({ children, label }) => (
<CustomActionBar label={label}>{children}</CustomActionBar>
),
components: () => {
return (
<Drawer direction="horizontal">
Expand Down
4 changes: 3 additions & 1 deletion packages/core/components/ActionBar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@ export const ActionBar = ({

export const Action = ({
children,
label,
onClick,
}: {
children: ReactNode;
label?: string;
onClick: (e: SyntheticEvent) => void;
}) => (
<button className={getClassName("action")} onClick={onClick}>
<button className={getClassName("action")} onClick={onClick} title={label}>
{children}
</button>
);
Expand Down
71 changes: 48 additions & 23 deletions packages/core/components/DraggableComponent/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
ReactNode,
SyntheticEvent,
useEffect,
useMemo,
useState,
} from "react";
import { Draggable } from "@measured/dnd";
Expand All @@ -15,6 +16,8 @@ import { useAppContext } from "../Puck/context";
import { DefaultDraggable } from "../Draggable";
import { Loader } from "../Loader";
import { ActionBar } from "../ActionBar";
import { DefaultOverride } from "../DefaultOverride";
import { useLoadedOverrides } from "../../lib/use-loaded-overrides";

const getClassName = getClassNameFactory("DraggableComponent", styles);

Expand All @@ -24,6 +27,18 @@ const actionsOverlayTop = space * 6.5;
const actionsTop = -(actionsOverlayTop - 8);
const actionsRight = space;

const DefaultActionBar = ({
label,
children,
}: {
label: string | undefined;
children: ReactNode;
}) => (
<ActionBar label={label}>
<DefaultOverride>{children}</DefaultOverride>
</ActionBar>
);

export const DraggableComponent = ({
children,
id,
Expand Down Expand Up @@ -67,11 +82,9 @@ export const DraggableComponent = ({
indicativeHover?: boolean;
style?: CSSProperties;
}) => {
const { zoomConfig } = useAppContext();
const { zoomConfig, status, overrides, plugins } = useAppContext();
const isModifierHeld = useModifierHeld("Alt");

const { status } = useAppContext();

const El = status !== "LOADING" ? Draggable : DefaultDraggable;

useEffect(onMount, []);
Expand All @@ -86,6 +99,16 @@ export const DraggableComponent = ({
}
}, []);

const loadedOverrides = useLoadedOverrides({
overrides: overrides,
plugins: plugins,
});

const CustomActionBar = useMemo(
() => loadedOverrides.actionBar || DefaultActionBar,
[loadedOverrides]
);

return (
<El
key={id}
Expand Down Expand Up @@ -124,31 +147,33 @@ export const DraggableComponent = ({
<Loader />
</div>
)}

<div
className={getClassName("actionsOverlay")}
style={{
top: actionsOverlayTop / zoomConfig.zoom,
}}
>
{isSelected && (
<div
className={getClassName("actions")}
className={getClassName("actionsOverlay")}
style={{
transform: `scale(${1 / zoomConfig.zoom}`,
top: actionsTop / zoomConfig.zoom,
right: actionsRight / zoomConfig.zoom,
top: actionsOverlayTop / zoomConfig.zoom,
}}
>
<ActionBar label={label}>
<ActionBar.Action onClick={onDuplicate}>
<Copy size={16} />
</ActionBar.Action>
<ActionBar.Action onClick={onDelete}>
<Trash size={16} />
</ActionBar.Action>
</ActionBar>
<div
className={getClassName("actions")}
style={{
transform: `scale(${1 / zoomConfig.zoom}`,
top: actionsTop / zoomConfig.zoom,
right: actionsRight / zoomConfig.zoom,
}}
>
<CustomActionBar label={label}>
<ActionBar.Action onClick={onDuplicate} label="Duplicate">
<Copy size={16} />
</ActionBar.Action>
<ActionBar.Action onClick={onDelete} label="Delete">
<Trash size={16} />
</ActionBar.Action>
</CustomActionBar>
</div>
</div>
</div>
)}

<div className={getClassName("overlay")} />
<div className={getClassName("contents")}>{children}</div>
</div>
Expand Down
4 changes: 4 additions & 0 deletions packages/core/types/Overrides.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ type OverridesGeneric<Shape extends { [key in OverrideKey]: any }> = Shape;
export type Overrides = OverridesGeneric<{
fieldTypes: Partial<FieldRenderFunctions>;
header: RenderFunc<{ actions: ReactNode; children: ReactNode }>;
actionBar: RenderFunc<{
label?: string;
children: ReactNode;
}>;
headerActions: RenderFunc<{ children: ReactNode }>;
preview: RenderFunc;
fields: RenderFunc<{
Expand Down

0 comments on commit 48ec0d7

Please sign in to comment.