diff --git a/src/ActionGroup/demos/basic.tsx b/src/ActionGroup/demos/basic.tsx
new file mode 100644
index 00000000..281124b8
--- /dev/null
+++ b/src/ActionGroup/demos/basic.tsx
@@ -0,0 +1,5 @@
+import { ActionGroup } from '@ant-design/pro-editor';
+
+export default () => {
+ return ;
+};
diff --git a/src/ActionGroup/demos/config.tsx b/src/ActionGroup/demos/config.tsx
new file mode 100644
index 00000000..4d038371
--- /dev/null
+++ b/src/ActionGroup/demos/config.tsx
@@ -0,0 +1,44 @@
+import { CopyOutlined, DragOutlined, ZoomInOutlined, ZoomOutOutlined } from '@ant-design/icons';
+import { ActionGroup } from '@ant-design/pro-editor';
+import { message } from 'antd';
+
+export default () => {
+ const [messageApi, contextHolder] = message.useMessage();
+
+ return (
+ <>
+ {contextHolder}
+ ,
+ onClick: () => {
+ messageApi.info('复制!');
+ },
+ },
+ {
+ icon: ,
+ onClick: () => {
+ messageApi.success('放大!');
+ },
+ },
+ {
+ icon: ,
+ style: {
+ color: '#1890ff',
+ },
+ onClick: () => {
+ messageApi.success('缩小!');
+ },
+ },
+ {
+ icon: ,
+ onClick: () => {
+ messageApi.loading('快速定位ing');
+ },
+ },
+ ]}
+ />
+ >
+ );
+};
diff --git a/src/ActionGroup/demos/custom.tsx b/src/ActionGroup/demos/custom.tsx
new file mode 100644
index 00000000..904c806b
--- /dev/null
+++ b/src/ActionGroup/demos/custom.tsx
@@ -0,0 +1,26 @@
+import { ActionGroup } from '@ant-design/pro-editor';
+import { Card, Input, Rate, Switch } from 'antd';
+
+export default () => {
+ return (
+ {
+ return (
+ }
+ style={{ width: 300 }}
+ size="small"
+ >
+ 工具
+ {defalutDom}
+ 内容
+
+ 评分
+
+
+ );
+ }}
+ />
+ );
+};
diff --git a/src/ActionGroup/demos/withPanel.tsx b/src/ActionGroup/demos/withPanel.tsx
new file mode 100644
index 00000000..cfd436c5
--- /dev/null
+++ b/src/ActionGroup/demos/withPanel.tsx
@@ -0,0 +1,42 @@
+import type { Position } from '@ant-design/pro-editor';
+import { ActionGroup, DraggablePanel } from '@ant-design/pro-editor';
+import { useLocalStorageState } from 'ahooks';
+
+export default () => {
+ const [position, setPos] = useLocalStorageState('demo-pos');
+
+ return (
+
+ );
+};
diff --git a/src/ActionGroup/index.md b/src/ActionGroup/index.md
new file mode 100644
index 00000000..25ae6ed2
--- /dev/null
+++ b/src/ActionGroup/index.md
@@ -0,0 +1,29 @@
+---
+title: ActionGroup 工具面板
+atomId: ActionGroup
+group: 基础组件
+---
+
+# ActionGroup 工具面板
+
+## 何时使用
+
+当你需要一个通用面板用于承载全局通用的「全屏、重做、撤销、删除」这一系列工具的地方,可以使用。
+
+## 代码演示
+
+### 基础
+
+
+
+### 使用配置
+
+
+
+### 高度自定义
+
+
+
+### 使用浮动面板
+
+
diff --git a/src/ActionGroup/index.tsx b/src/ActionGroup/index.tsx
new file mode 100644
index 00000000..2d053294
--- /dev/null
+++ b/src/ActionGroup/index.tsx
@@ -0,0 +1,108 @@
+import { DeleteOutlined, FullscreenOutlined, RedoOutlined, UndoOutlined } from '@ant-design/icons';
+import { getPrefixCls } from '@ant-design/pro-editor';
+import { Button, Space } from 'antd';
+import { useStyle } from './style';
+
+type ButtonConfig = {
+ /**
+ * @description 展示的 icon
+ */
+ icon: React.ReactNode;
+ /**
+ * @description 样式
+ * @ignore
+ */
+ style?: React.CSSProperties;
+ /**
+ * @description 每个按钮单独的key
+ */
+ key?: string;
+ /**
+ * @description 按钮点击事件的回掉
+ */
+ onClick?: () => void;
+};
+
+interface ActionGroupProps {
+ /**
+ * @description 自定义的classname
+ * @ignore
+ */
+ className?: string;
+ /**
+ * @description 样式
+ * @ignore
+ */
+ style?: React.CSSProperties;
+ /**
+ * @description 生成按钮的配置config
+ */
+ config?: Array;
+ /**
+ * @description 用于渲染自定义能力的render方法
+ */
+ render?: (defalutDom: React.ReactNode, config: Array) => React.ReactNode;
+ /**
+ * @description 全屏按钮点击的回掉
+ */
+ onFullScreenClick?: () => void;
+ /**
+ * @description 撤销按钮点击的回掉
+ */
+ onUndoClick?: () => void;
+ /**
+ * @description 重做按钮点击的回掉
+ */
+ onRedoClick?: () => void;
+ /**
+ * @description 删除按钮点击的回掉
+ */
+ onDeleteClick?: () => void;
+}
+
+const ActionGroup = (props: ActionGroupProps) => {
+ const prefixCls = getPrefixCls('action-group');
+ const { styles, cx } = useStyle(prefixCls);
+ const {
+ className,
+ style,
+ render,
+ config: outConfig,
+ onFullScreenClick,
+ onUndoClick,
+ onRedoClick,
+ onDeleteClick,
+ } = props;
+ const config = outConfig
+ ? outConfig
+ : [
+ { icon: , onClick: onFullScreenClick },
+ { icon: , onClick: onUndoClick },
+ { icon: , onClick: onRedoClick },
+ { icon: , onClick: onDeleteClick },
+ ];
+
+ const ActionDomList = () => {
+ const defalutDom = (
+ <>
+ {config.map((item: ButtonConfig, index) => {
+ return ;
+ })}
+ >
+ );
+ if (render) {
+ return render(defalutDom, config);
+ }
+ return defalutDom;
+ };
+
+ return (
+
+ );
+};
+
+export default ActionGroup;
diff --git a/src/ActionGroup/style.ts b/src/ActionGroup/style.ts
new file mode 100644
index 00000000..6acdb79b
--- /dev/null
+++ b/src/ActionGroup/style.ts
@@ -0,0 +1,24 @@
+import { createStyles } from '../theme';
+
+export const useStyle = createStyles(({ token, css, cx }, prefixCls) => {
+ return {
+ content: cx(
+ `${prefixCls}-content`,
+ css`
+ width: fit-content;
+ padding: ${token.padding / 4}px ${token.padding / 2}px;
+ `,
+ ),
+ button: cx(
+ `${prefixCls}-action-btn`,
+ css`
+ box-shadow: none;
+ border: none;
+ background-color: transparent;
+ &:hover {
+ color: ${token.colorIconHover} !important;
+ }
+ `,
+ ),
+ };
+});
diff --git a/src/index.ts b/src/index.ts
index 6d061936..f1d8c178 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,4 +1,5 @@
export { default as yjsMiddleware } from 'zustand-middleware-yjs';
+export { default as ActionGroup } from './ActionGroup';
export * from './ActionIcon';
export { default as Awareness } from './Awareness';
export type { AwarenessProps } from './Awareness';