From d57257138d5a9f900bfecc2c3fecd96dbfc72a85 Mon Sep 17 00:00:00 2001 From: fancyzhong Date: Tue, 3 Sep 2024 11:20:10 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E9=A6=96=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../theme/builtins/ContactAuthor/Features.tsx | 26 +++ .../theme/builtins/ContactAuthor/index.less | 8 + .../theme/builtins/ContactAuthor/index.tsx | 44 +++++ docs/docs/api/atom-ctx/use-state-x.md | 2 +- docs/docs/api/base/index.md | 4 +- docs/docs/api/hooks/index.md | 2 +- docs/docs/api/index.md | 6 +- docs/docs/index.md | 18 +- .../playground/demos/Playground/ApiMenus.tsx | 1 + .../playground/demos/Playground/Console.tsx | 3 +- .../playground/demos/Playground/TopBar.tsx | 3 - .../playground/demos/Playground/codes/atom.ts | 1 - .../demos/Playground/codes/index.ts | 1 + .../demos/Playground/codes/quickStart.ts | 171 ++++++++++++++++++ .../playground/demos/Playground/index.less | 49 ++++- .../playground/demos/Playground/index.tsx | 39 +++- pnpm-lock.yaml | 4 +- 17 files changed, 350 insertions(+), 32 deletions(-) create mode 100644 docs/.dumi/theme/builtins/ContactAuthor/Features.tsx create mode 100644 docs/.dumi/theme/builtins/ContactAuthor/index.less create mode 100644 docs/docs/playground/demos/Playground/codes/quickStart.ts diff --git a/docs/.dumi/theme/builtins/ContactAuthor/Features.tsx b/docs/.dumi/theme/builtins/ContactAuthor/Features.tsx new file mode 100644 index 00000000..5770695b --- /dev/null +++ b/docs/.dumi/theme/builtins/ContactAuthor/Features.tsx @@ -0,0 +1,26 @@ +import './index.less'; + +function Feature(props: any) { + const { title, description, imgSrc } = props.item; + return ( +
+
+ +
+ +

{title}

+
+ +

{description}

+
+
+ ) +} + +export function Features(props: any) { + return ( +
+ {props.featureList.map((v: any) => )} +
+ ); +} diff --git a/docs/.dumi/theme/builtins/ContactAuthor/index.less b/docs/.dumi/theme/builtins/ContactAuthor/index.less new file mode 100644 index 00000000..d272a066 --- /dev/null +++ b/docs/.dumi/theme/builtins/ContactAuthor/index.less @@ -0,0 +1,8 @@ +.hx-feature-item { + box-shadow: rgba(0, 0, 255, 0.2) 1px 2px 2px 1px; + background-color: rgb(255, 255, 255); + border-radius: 6px; + padding: 24px; + box-sizing: border-box; + height: 270px; +} diff --git a/docs/.dumi/theme/builtins/ContactAuthor/index.tsx b/docs/.dumi/theme/builtins/ContactAuthor/index.tsx index 5639b87d..d17a877e 100644 --- a/docs/.dumi/theme/builtins/ContactAuthor/index.tsx +++ b/docs/.dumi/theme/builtins/ContactAuthor/index.tsx @@ -1,7 +1,51 @@ import React, { type FC } from 'react'; +import { SimplePlayground } from '../../../../docs/playground/demos/Playground'; +import { Features } from './Features'; + + +const imgs = [ + 'https://tnfe.gtimg.com/image/harzqyxcgz_1651755973579.png', + 'https://tnfe.gtimg.com/image/p40w0k40pt_1651755965504.png', + 'https://tnfe.gtimg.com/image/fxy2nbeh43_1651755969439.png', + 'https://tnfe.gtimg.com/image/bxzj46o32k_1651755962175.png', + 'https://tnfe.gtimg.com/image/ngex07gcez_1651755956158.png', + 'https://tnfe.gtimg.com/image/harzqyxcgz_1651755973579.png', +]; +function getImg(idx: number) { + return imgs[idx] || imgs[0]; +} +const featureList = [ + { + title: 'atom', + description: 'atom 支持任意数据结构,对非原始类型数据内置依赖收集功能, 意味着 atom 不用拆分的很细,天然对 DDD 领域驱动设计友好', + }, + { + title: 'signal', + description: '内置 signal 响应机制,可实现 0 hook 编码 + dom 粒度的更新', + }, + { + title: '依赖追踪', + description: '基于最快的不可变 js 库 limu 做到运行时对视图渲染实时收集数据依赖,提供超强渲染性能', + }, + { + title: 'reactive', + description: '提供全局响应式对象,数据变更直接驱动关联ui渲染(默认在下一个事件循环微任务开始前提交,支持人工提交变更数据)', + }, + { + title: 'modular', + description: '支持对状态模块化抽象,并内置 actions、derive、watch、loading 等特性,轻松驾驭大型前端应用架构', + }, + { + title: 'middleware&plugin', + description: '内置中间件和插件系统,无缝衔接redux生态优秀组件', + }, +]; +featureList.forEach((v: any, idx: number) => v.imgSrc = getImg(idx)); const ContactAuthor: FC = () => (
+ +

📦 了解更多

diff --git a/docs/docs/api/atom-ctx/use-state-x.md b/docs/docs/api/atom-ctx/use-state-x.md index a4d2d2d2..e718d76a 100644 --- a/docs/docs/api/atom-ctx/use-state-x.md +++ b/docs/docs/api/atom-ctx/use-state-x.md @@ -12,5 +12,5 @@ order: 1 --- :::info -更多用法查阅[Hooks/useAtomX](/api/hooks/use-atom-x) +更多用法查阅[Hooks/useAtomX](/api/hooks/use-atomx) ::: diff --git a/docs/docs/api/base/index.md b/docs/docs/api/base/index.md index 787cc3fd..239fe5f6 100644 --- a/docs/docs/api/base/index.md +++ b/docs/docs/api/base/index.md @@ -8,9 +8,9 @@ nav: 包含以下基础函数 - [atom](/api/base/atom) 创建`atom`对象,返回元组 -- [atomx](/api/base/atom-x) 创建`atom`对象,返回字典 +- [atomx](/api/base/atomx) 创建`atom`对象,返回字典 - [share](/api/base/share) 创建字典型`atom`对象,返回元组 -- [sharex](/api/base/share-x) 创建字典型`atom`对象,返回字典 +- [sharex](/api/base/sharex) 创建字典型`atom`对象,返回字典 - [signal](/api/base/signal) 创建`signal`响应区域,实现dom粒度更新 - [block](/api/base/block) 创建`block`块响应区域,实现块粒度更新 - [dynamicBlock](/api/base/dynamic-block) 组件渲染过程中创建动态`block`块响应区域,实现块粒度更新 diff --git a/docs/docs/api/hooks/index.md b/docs/docs/api/hooks/index.md index 4b7aa45f..12aeed2c 100644 --- a/docs/docs/api/hooks/index.md +++ b/docs/docs/api/hooks/index.md @@ -7,7 +7,7 @@ order: 0 包含以下常用钩子 - [useAtom](/api/hooks/use-atom) 使用`atom`对象,返回元组 -- [useAtomX](/api/hooks/use-atom-x) 使用`atom`对象,返回字典 +- [useAtomX](/api/hooks/use-atomx) 使用`atom`对象,返回字典 - [useReactive](/api/hooks/use-reactive) 使用`reactive`对象,返回元组 - [useReactiveX](/api/hooks/use-reactive-x) 使用`reactive`对象,返回字典 - [useDerived](/api/hooks/use-derived) 使用全量派生结果 diff --git a/docs/docs/api/index.md b/docs/docs/api/index.md index 2d734dc3..dc32073a 100644 --- a/docs/docs/api/index.md +++ b/docs/docs/api/index.md @@ -9,9 +9,9 @@ nav: - [基础](/api/base/atom) > - [atom](/api/base/atom) 创建`atom`对象,返回元组 -> - [atomx](/api/base/atom-x) 创建`atom`对象,返回字典 +> - [atomx](/api/base/atomx) 创建`atom`对象,返回字典 > - [share](/api/base/share) 创建字典型`atom`对象,返回元组 -> - [sharex](/api/base/share-x) 创建字典型`atom`对象,返回字典 +> - [sharex](/api/base/sharex) 创建字典型`atom`对象,返回字典 > - [signal](/api/base/signal) 创建`signal`响应区域,实现dom粒度更新 > - [block](/api/base/block) 创建`block`块响应区域,实现块粒度更新 > - [dynamicBlock](/api/base/dynamic-block) 组件渲染过程中创建动态`block`块响应区域,实现块粒度更新 @@ -51,7 +51,7 @@ ctx.aciton()(/** action 函数定义 */) - [Hooks](/api/hooks/) > - [useAtom](/api/hooks/use-atom) 使用`atom`对象,返回元组 -> - [useAtomX](/api/hooks/use-atom-x) 使用`atom`对象,返回字典 +> - [useAtomX](/api/hooks/use-atomx) 使用`atom`对象,返回字典 > - [useReactive](/api/hooks/use-reactive) 使用`reactive`对象,返回元组 > - [useReactiveX](/api/hooks/use-reactive-x) 使用`reactive`对象,返回字典 > - [useDerived](/api/hooks/use-derived) 使用全量派生结果 diff --git a/docs/docs/index.md b/docs/docs/index.md index 44662f0b..683b2da7 100644 --- a/docs/docs/index.md +++ b/docs/docs/index.md @@ -9,19 +9,11 @@ hero: link: /playground - text: Github link: https://github.com/heluxjs/helux -features: - - title: atom - description: atom 支持任意数据结构,对非原始类型数据内置依赖收集功能, 意味着 atom 不用拆分的很细,天然对 DDD 领域驱动设计友好 - - title: signal - description: 内置 signal 响应机制,可实现 0 hook 编码 + dom 粒度的更新 - - title: 依赖追踪 - description: 基于最快的不可变 js 库 limu 做到运行时对视图渲染实时收集数据依赖,提供超强渲染性能 - - title: reactive - description: 提供全局响应式对象,数据变更直接驱动关联ui渲染(默认在下一个事件循环微任务开始前提交,支持人工提交变更数据) - - title: modular - description: 支持对状态模块化抽象,并内置 actions、derive、watch、loading 等特性,轻松驾驭大型前端应用架构 - - title: middleware&plugin - description: 内置中间件和插件系统,无缝衔接redux生态优秀组件 +# 此处已转移到 ContactAuthor 里的 Features 渲染 +# features: + # - title: atom + # description: atom 支持任意数据结构,对非原始类型数据内置依赖收集功能, 意味着 atom 不用拆分的很细,天然对 DDD 领域驱动设计友好 + # ... --- diff --git a/docs/docs/playground/demos/Playground/ApiMenus.tsx b/docs/docs/playground/demos/Playground/ApiMenus.tsx index 99a524bc..067134b2 100644 --- a/docs/docs/playground/demos/Playground/ApiMenus.tsx +++ b/docs/docs/playground/demos/Playground/ApiMenus.tsx @@ -6,6 +6,7 @@ import * as codes from './codes'; const c = '#e8ae56'; const orderedKeys = [ + 'quickStart', 'atom', 'derive', 'watch', diff --git a/docs/docs/playground/demos/Playground/Console.tsx b/docs/docs/playground/demos/Playground/Console.tsx index 1ad371a3..a6dc9698 100644 --- a/docs/docs/playground/demos/Playground/Console.tsx +++ b/docs/docs/playground/demos/Playground/Console.tsx @@ -1,4 +1,5 @@ import React from 'react' +import * as helux from 'helux' import { Hook, Console, Decode } from 'console-feed'; import './index.less'; @@ -11,7 +12,7 @@ class HeluxConsole extends React.Component { Hook(window.console, (log) => { this.setState(({ logs }) => ({ logs: [...logs, Decode(log)] })) }); - console.log('Welcome to helux playground ^_^'); + console.log(`Welcome to helux playground (helux ver: ${helux.cst.VER})^_^`); } render() { diff --git a/docs/docs/playground/demos/Playground/TopBar.tsx b/docs/docs/playground/demos/Playground/TopBar.tsx index 3306a775..4b70a661 100644 --- a/docs/docs/playground/demos/Playground/TopBar.tsx +++ b/docs/docs/playground/demos/Playground/TopBar.tsx @@ -32,10 +32,7 @@ function renderItems(name: string, subName: string) { )); } - - export default React.memo(({ onClick, name, subName }: any) => { - const handleClick = e => { const subName = e.target.dataset.name; if (subName) { diff --git a/docs/docs/playground/demos/Playground/codes/atom.ts b/docs/docs/playground/demos/Playground/codes/atom.ts index 7b597e40..cd14b78a 100644 --- a/docs/docs/playground/demos/Playground/codes/atom.ts +++ b/docs/docs/playground/demos/Playground/codes/atom.ts @@ -1,5 +1,4 @@ - const primitive = ` const [ num, setNum ] = atom(1); diff --git a/docs/docs/playground/demos/Playground/codes/index.ts b/docs/docs/playground/demos/Playground/codes/index.ts index 1c16b629..c6648034 100644 --- a/docs/docs/playground/demos/Playground/codes/index.ts +++ b/docs/docs/playground/demos/Playground/codes/index.ts @@ -1,3 +1,4 @@ +export { default as quickStart } from './quickStart'; export { default as atom } from './atom'; export { default as derive } from './derive'; export { default as watch } from './watch'; diff --git a/docs/docs/playground/demos/Playground/codes/quickStart.ts b/docs/docs/playground/demos/Playground/codes/quickStart.ts new file mode 100644 index 00000000..6454b0f0 --- /dev/null +++ b/docs/docs/playground/demos/Playground/codes/quickStart.ts @@ -0,0 +1,171 @@ +const HelloHelux = ` +const [str, setStr, ctx] = atom('hello helux'); // define atom, str is boxed with {val:T} strcture +const reversedStr = derive(() => str.val.split('').reverse().join('')); // define derive + +function HelloHelux(){ + const [strVal] = useAtom(str); // strVal is auto unboxed + const [reversedStrVal] = useDerived(reversedStr); + return (

+ setStr(e.target.value)} /> + +

reversed: {reversedStrVal}

+
); +} + +render(); +`; + +const HelloHeluxDict = ` +// no { val: T } wrapped with share api, so using share is better than atom at this situation +const [dict, setDict, ctx] = share({str: 'hello helux'}); // define share +const reversedStr = derive(() => dict.str.split('').reverse().join('')); // define derive + +function HelloHeluxDict(){ + const [dictState] = useAtom(dict); + const [reversedStrVal] = useDerived(reversedStr); + return (
+ setDict(draft=>draft.str=e.target.value)} /> + +

reversed: {reversedStrVal}

+
); +} + +render(); +`; + +const DataBindWidthHook = ` +const [num, setNum] = atom(1); +const numDouble = derive(() => num.val * 2); // derive num +const change = () => { // change num + setNum(prev => prev + 1); +}; + +function DataBindWidthHook() { + const [numVal] = useAtom(num); + const [numDoubleVal] = useDerived(numDouble); + + return ( +
+

numVal {numVal}

+

numDoubleVal {numDoubleVal}

+ +
+ ); +} + +render(<>); +`; + +const DataBindWidthNoHook = ` +const [num, setNum] = atom(1); +const numDouble = derive(() => num.val * 2); // derive num +const change = () => { // change num + setNum(prev => prev + 1); +}; +// pass primitive data to ui with $ +function DataBindWidthNoHook() { // DOM granularity update + return ( +
+

numVal {$(num)}

+

numDoubleVal {$(numDouble)}

+ +
+ ); +} + +render(<>

dom粒度更新

); +`; + +const DictDataBindWidthHook = ` +// const [dict, setState] = atom({ a: 1, b: { b1: 1, b2: { b2_1: 'cool' } } }); +// const numDouble = derive(() => dict.val.a + 100 ); // dict.val.a + +// no { val: T } wrapped with share api, so using share is better than atom at this situation +const [dict, setState] = share({ a: 1, b: { b1: 1, b2: { b2_1: 'cool' } } }); +const aPlus = derive(() => dict.a + 100 ); // dict.a + +const change = () => { // change num + setState(prev => prev.a += 100 ); +}; + +function DictDataBindWidthHook() { + const [dictState] = useAtom(dict); + const [aPlusVal] = useDerived(aPlus); + + return ( +
+

dictState {dictState.a}

+

aPlusVal {aPlusVal}

+ +
+ ); +} + +render(); +` + +const DictDataBindWidthNoHookUsingBlock = ` +const [dict, setState] = share({ a: 1, b: { b1: 1, b2: { b2_1: 'cool' } } }); +const aPlus = derive(() => dict.a + 100 ); // dict.a + +const change = () => { // change num + setState(prev => prev.a += 100 ); +}; +// create a block component +const DictDataBindWidthNoHookUsingBlock = block(()=>( +
+

dictState {dict.a}

+

aPlusVal {aPlus.val}

+ +
+)); + +render(<>

block粒度更新

); +` + + +const ChangeStateWithActions = ` +const ctx = sharex({str: 'hello helux', asyncClicked: 0}); +const delay = (ms=1000)=> new Promise(r=>setTimeout(r, ms)); +const { actions, useLoading } = ctx.defineActions()({ + // sync action + changeStr({ draft }) { + draft.str = \`changed at \${Date.now()}\`; + }, + // async action + async changeStrAsync({ draft }) { + await delay(); + draft.str = \`async changed at \${Date.now()}\`; + draft.asyncClicked += 1; + if(draft.asyncClicked === 3){ + throw new Error('a fake error occurred!'); + } + }, +}); + +function ChangeStateWithActions(){ + const [state] = ctx.useState(); + const ret = useLoading(); + const { changeStrAsync } = useLoading(); + + return (
+ {changeStrAsync.loading && 'loading'} + {changeStrAsync.ok &&

str: {state.str}

} + {changeStrAsync.err &&

err: {changeStrAsync.err.message}

} + + +
); +} + +render(); +`; + +export default { + HelloHelux, + HelloHeluxDict, + DataBindWidthHook, + DataBindWidthNoHook, + DictDataBindWidthHook, + DictDataBindWidthNoHookUsingBlock, + ChangeStateWithActions, +} as Record; diff --git a/docs/docs/playground/demos/Playground/index.less b/docs/docs/playground/demos/Playground/index.less index c13e3bdb..81a3aebd 100644 --- a/docs/docs/playground/demos/Playground/index.less +++ b/docs/docs/playground/demos/Playground/index.less @@ -1,3 +1,49 @@ +.simple-playground-wrap { + height: 800px; + background-color: #ffffff; + padding: 12px 0; +} + +.simple-playground-wrap .prism-code { + height: 740px; +} + +.simple-playground-wrap .topBarItem { + display: inline-block; + height: 22px; + color: #fff; + padding: 0 6px; + font-size: 14px; + line-height: 22px; + margin: 2px 5px; +} + +.simple-playground-wrap .topBarItem:hover { + cursor: pointer; +} + +.simple-playground-wrap .menuWrap:hover { + cursor: pointer; +} + +.simple-playground-wrap .liveConsoleWrap { + position: relative; + height: 50%; +} + +.simple-playground-wrap .liveConsole { + background-color: #242424; + height: 100%; + overflow-y: auto; +} + +.leftMenuWrap { + width: 120px; + display: inline-block; + vertical-align: top; + padding: 38px 0 0 12px; + box-sizing: border-box; +} .playground-wrap { width: 100vw; @@ -79,12 +125,11 @@ display: flex; } -.playground-wrap .topBar>.samples { +.playground-wrap .topBar>.samples { display: flex; flex-grow: 1; } - .playground-wrap .topBarItem { display: inline-block; height: 22px; diff --git a/docs/docs/playground/demos/Playground/index.tsx b/docs/docs/playground/demos/Playground/index.tsx index 45243922..53be08fe 100644 --- a/docs/docs/playground/demos/Playground/index.tsx +++ b/docs/docs/playground/demos/Playground/index.tsx @@ -42,7 +42,8 @@ function loadCode(name: any, subName: any, setCode: any) { } }) } -export default () => { + +function useLogic(name = 'atom', subName = 'primitive') { const [info, setInfo] = React.useState({ name, subName }); const [code, setCode] = React.useState(initCode); @@ -57,8 +58,6 @@ export default () => { } }, () => [codeContext.code]) - - const changeCode = useCallback((name: string) => { const subName = cachedSubNames[name] || subNames[name] || 'primitive'; setCodeContext(draft => { draft.key = `${name}_${subName}` }) @@ -77,6 +76,40 @@ export default () => { loadCode(name, subName, setCode); }, [info.name, info.subName]); + return { info, code, changeCode, changeSubName }; +} + +export function SimplePlayground() { + const { info, code, changeCode, changeSubName } = useLogic('quickStart', 'HelloHelux'); + + return ( + +
+
+ +
+
+ +
+
+ +
+
+
+ + +
+ +
+
+
+
+
+ ); +} + +export default () => { + const { info, code, changeCode, changeSubName } = useLogic(name, subName); return (
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index accc80b8..31b1276a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -223,7 +223,7 @@ importers: hel-micro: ^4.8.11 hel-micro-core: ^4.8.7 hel-types: ^4.3.3 - helux: ^4.3.6 + helux: ^4.4.0 react: '>=16.10.2' react-dom: '>=16.10.2' rollup: ^2.23.0 @@ -284,7 +284,7 @@ importers: packages/helux-plugin-devtool: specifiers: - helux: ^4.3.6 + helux: ^4.4.0 redux: ^4.2.1 terser: ^5.29.1 dependencies: