Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ feat: 撤销重做中间件 #80

Merged
merged 3 commits into from
Nov 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions .dumirc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,6 @@ const isProd = process.env.NODE_ENV === 'production';
export default defineConfig({
outputPath: 'docs-dist',
mfsu: false,
// apiParser: {},
// resolve: {
// // 配置入口文件路径,API 解析将从这里开始
// entryFile: './src/index.ts',
// },
favicons: ['https://gw.alipayobjects.com/zos/antfincdn/upvrAjAPQX/Logo_Tech%252520UI.svg'],
// @ts-ignore
ssr: false,
Expand Down
33 changes: 33 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,38 @@
# Changelog

## [Version 0.28.0-alpha.1](https://github.com/ant-design/pro-editor/compare/v0.27.0...v0.28.0-alpha.1)

<sup>Released on **2023-11-24**</sup>

#### ✨ 新特性

- Undo/redo Middleware.

#### 🐛 修复

- Fix compatible with subscribeWithSelector middleware.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's improved

- Undo/redo Middleware, closes [#74](https://github.com/ant-design/pro-editor/issues/74) ([44551aa](https://github.com/ant-design/pro-editor/commit/44551aa))

#### What's fixed

- Fix compatible with subscribeWithSelector middleware, closes [#89](https://github.com/ant-design/pro-editor/issues/89) ([b11cb36](https://github.com/ant-design/pro-editor/commit/b11cb36))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>

## [Version&nbsp;0.27.0](https://github.com/ant-design/pro-editor/compare/v0.26.0...v0.27.0)

<sup>Released on **2023-11-24**</sup>
Expand Down
4 changes: 2 additions & 2 deletions docs/guide/data-management.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
---
title: 数据流最佳实践
title: 编辑器数据流最佳实践
group:
title: 状态管理研发
order: 10
---

## 数据流最佳实践

编辑器场景不同于网页,存在大量的富交互能力如何设计一个易于开发与易于维护的数据流架构非常重要。
编辑器场景不同于 CRUD 的网页,存在大量的富交互能力如何设计一个易于开发与易于维护的数据流架构非常重要。

## 概念要素

Expand Down
43 changes: 43 additions & 0 deletions docs/guide/demos/Redo/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Button, Card, Divider, Tabs } from 'antd';
import { useTheme } from 'antd-style';
import { Flexbox } from 'react-layout-kit';

import Toolbar from './Toolbar';
import { useStore } from './store';

const App = () => {
const { data, plus, tabs, switchTabs, plusWithoutHistory } = useStore();

const theme = useTheme();

return (
<Flexbox padding={16} style={{ background: theme.colorBgLayout }}>
<Toolbar />
<Divider />
<Flexbox gap={12}>
<Card title={'历史记录里的数据'}>
<Button onClick={plus}>+1</Button>

<div>data: {data}</div>
</Card>
<Card title={'不在历史记录里的数据'}>
<div>
<Tabs
activeKey={tabs}
onChange={switchTabs}
items={[
{ key: '1', label: '数据' },
{ key: '2', label: '配置' },
]}
/>

<div>下面的 +2 可使得 在历史记录外添加让 data +2 </div>
<Button onClick={plusWithoutHistory}>+2</Button>
</div>
</Card>
</Flexbox>
</Flexbox>
);
};

export default App;
45 changes: 45 additions & 0 deletions docs/guide/demos/Redo/Toolbar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { RedoOutlined, UndoOutlined } from '@ant-design/icons';
import { useProEditor } from '@ant-design/pro-editor';
import { Badge, Button } from 'antd';
import { Flexbox } from 'react-layout-kit';

const Toolbar = () => {
const { undo, redo, undoStack, redoStack } = useProEditor();

const undoStackList = undoStack();
const redoStackList = redoStack();

const lastAction = undoStackList.at(-1);

return (
<Flexbox padding={12} gap={8}>
<Flexbox horizontal gap={8}>
<Badge count={undoStackList.length}>
<Button icon={<UndoOutlined />} onClick={undo} disabled={undoStackList.length === 0}>
撤销
</Button>
</Badge>

<Badge count={redoStackList.length}>
<Button icon={<RedoOutlined />} onClick={redo} disabled={redoStackList.length === 0}>
重做
</Button>
</Badge>
</Flexbox>
<Flexbox>
上次操作时间:
{lastAction ? new Date(lastAction.timestamp).toLocaleTimeString() : '-'}
</Flexbox>
<Flexbox>
上次操作名称:
{lastAction?.name ?? '-'}
</Flexbox>{' '}
<Flexbox>
上次操作类型:
{lastAction?.type ?? '-'}
</Flexbox>
</Flexbox>
);
};

export default Toolbar;
15 changes: 15 additions & 0 deletions docs/guide/demos/Redo/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/**
* compact: true
*/
import { ProEditorProvider } from '@ant-design/pro-editor';
import App from './App';

import { useStore } from './store';

export default () => {
return (
<ProEditorProvider devtoolOptions={{ name: 'redo-pro-editor-store' }} store={[useStore]}>
<App />
</ProEditorProvider>
);
};
57 changes: 57 additions & 0 deletions docs/guide/demos/Redo/store.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { proEditorMiddleware, ProEditorOptions } from '@ant-design/pro-editor';
import { create, StateCreator } from 'zustand';
import { devtools, subscribeWithSelector } from 'zustand/middleware';

interface Store {
tabs: string;
plus: () => void;
plusWithoutHistory: () => void;
data: number;
switchTabs: (key: string) => void;
}

const createStore: StateCreator<Store, [['zustand/devtools', never], ['pro-editor', never]]> = (
set,
get,
) => ({
tabs: '1',
switchTabs: (key) => {
set({ tabs: key });
},
plusWithoutHistory: () => {
set((s) => ({ ...s, data: s.data + 2 }), false, {
type: 'plusWithoutHistory',
recordHistory: false,
});
},

plus: () => {
const nextData = get().data + 1;

set({ data: nextData }, false, {
type: 'plus',
payload: nextData,
name: '+1',
});
},
data: 3,
});

interface ProEditorStore {
data: number;
}

const storeName = 'redo-demo-app';

const proEditorOptions: ProEditorOptions<Store, ProEditorStore> = {
name: storeName,
partialize: (s) => ({ data: s.data }),
};

export const useStore = create<Store>()(
devtools(proEditorMiddleware(subscribeWithSelector(createStore), proEditorOptions), {
name: storeName,
}),
);

useStore.subscribe((s) => s.data, console.log);
113 changes: 113 additions & 0 deletions docs/guide/redo-undo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
---
title: 撤销重做
group: 状态管理研发
order: 2
---

# 撤销重做

撤销重做是编辑器场景保障用户体验的一个重要特性。ProEditor 作为编辑器框架,为上层的应用编辑器提供了撤销重做的原子化能力。

## 立即上手

<code src="./demos/Redo/index.tsx" ></code>

## 使用方式

### 初始化

1. 外层包裹 ProEditorProvider,传入相应的 zustand store

```tsx | pure
import { ProEditorProvider } from '@ant-design/pro-editor';

import { useStore } from './store';

export default () => {
return (
<ProEditorProvider store={[useStore]}>
<App />
</ProEditorProvider>
);
};
```

2. zustand store 包裹 ProEditorMiddleware

```ts
import { proEditorMiddleware, ProEditorOptions } from '@ant-design/pro-editor';

interface ProEditorStore extends Partial<Store> { }


const proEditorOptions: ProEditorOptions<Store, ProEditorStore> = {
name: 'store-name', // 每个 store 需要有自己的唯一名称
partialize: (s) => ({ data: s.data }), // 支持按需接入
};

export const useStore = create<Store>()(

// createStore 是 StoreCreator 类型的对象
proEditorMiddleware(createStore, proEditorOptions)),
);
```

多个 Store 使用的方式:

```tsx | pure
import { ProEditorProvider } from '@ant-design/pro-editor';

import { useAStore } from './storeA';
import { useBStore } from './storeB';

export default () => {
return (
<ProEditorProvider store={[useAStore, useBStore]}>
<App />
</ProEditorProvider>
);
};
```

多 Store 撤销重做互相隔离

```tsx | pure
<div>
<ProEditorProvider store={[cStore]}>
<A />
</ProEditorProvider>

<ProEditorProvider store={[dStore]}>
<B />
</ProEditorProvider>
</div>
```

### 设定历史记录

```ts
const createStore: StateCreator<Store, [['zustand/devtools', never], ['pro-editor', never]]> = (
set,
get,
) => ({
tabs: '1',
switchTabs: (key) => {
set({ tabs: key });
},
plusWithoutHistory: () => {
set((s) => ({ ...s, data: s.data + 2 }), false, {
type: 'plusWithoutHistory',
// 不进入历史记录
recordHistory: false,
});
},

plus: () => {
const nextData = get().data + 1;

// 默认进入历史记录
set({ data: nextData }, false, { type: 'plus' });
},
data: 3,
});
```
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@ant-design/pro-editor",
"version": "0.27.0",
"version": "0.28.0-alpha.1",
"description": "🌟 Lightweight Editor UI Framework",
"homepage": "https://github.com/ant-design/pro-editor",
"bugs": {
Expand Down Expand Up @@ -121,7 +121,7 @@
},
"devDependencies": {
"@emotion/jest": "^11.11.0",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/jest-dom": "^6.1.3",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^14.5.1",
"@types/color": "^3.0.4",
Expand Down Expand Up @@ -153,7 +153,7 @@
"semantic-release": "^21.1.2",
"semantic-release-config-gitmoji": "^1.5.3",
"stylelint": "^15.10.3",
"typescript": "~5.1.6",
"typescript": "^5.2.2",
"vitest": "latest",
"wait-on": "^6.0.1",
"y-protocols": "^1.0.5",
Expand Down
4 changes: 2 additions & 2 deletions src/ConfigProvider/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
title: ConfigProvider 全局容器
atomId: ConfigProvider
group:
title: 基础组件
order: 0
title: 其他
order: 1000
---

# ConfigProvider 全局容器
1 change: 0 additions & 1 deletion src/IconPicker/features/PickerPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ const PickerPanel = () => {
</Flexbox>
) : undefined}
<Segmented
size={'small'}
options={[
{ label: 'Ant Design', value: 'antd', icon: <AntDesignOutlined /> },
{ label: 'Iconfont', value: 'iconfont' },
Expand Down
Loading