diff --git a/.gitignore b/.gitignore index 0c38614..55157fd 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,6 @@ package /playwright-report/ /blob-report/ /playwright/.cache/ +packages/*/dist/ +packages/*/package/ + diff --git a/.vscode/settings.json b/.vscode/settings.json index cec2c64..a23e019 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,6 +3,7 @@ "astro", "astrojs", "autodocs", + "codesandbox", "egjs", "flutterjs", "lerp", @@ -16,6 +17,7 @@ "publint", "renderobject", "rgba", + "sandpack", "Shiki", "tailwindcss", "tsup", diff --git a/package-lock.json b/package-lock.json index 4f89a0d..0512c36 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2233,6 +2233,165 @@ "yarn": ">=1.22.18" } }, + "node_modules/@codemirror/autocomplete": { + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.16.0.tgz", + "integrity": "sha512-P/LeCTtZHRTCU4xQsa89vSKWecYv1ZqwzOd5topheGRf+qtacFgBeIMQi3eL8Kt/BUNvxUWkx+5qP2jlGoARrg==", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0" + }, + "peerDependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@codemirror/commands": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.5.0.tgz", + "integrity": "sha512-rK+sj4fCAN/QfcY9BEzYMgp4wwL/q5aj/VfNSoH1RWPF9XS/dUwBkvlL3hpWgEjOqlpdN1uLC9UkjJ4tmyjJYg==", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.4.0", + "@codemirror/view": "^6.0.0", + "@lezer/common": "^1.1.0" + } + }, + "node_modules/@codemirror/lang-css": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/@codemirror/lang-css/-/lang-css-6.2.1.tgz", + "integrity": "sha512-/UNWDNV5Viwi/1lpr/dIXJNWiwDxpw13I4pTUAsNxZdg6E0mI2kTQb0P2iHczg1Tu+H4EBgJR+hYhKiHKko7qg==", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@lezer/common": "^1.0.2", + "@lezer/css": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-html": { + "version": "6.4.9", + "resolved": "https://registry.npmjs.org/@codemirror/lang-html/-/lang-html-6.4.9.tgz", + "integrity": "sha512-aQv37pIMSlueybId/2PVSP6NPnmurFDVmZwzc7jszd2KAF8qd4VBbvNYPXWQq90WIARjsdVkPbw29pszmHws3Q==", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/lang-css": "^6.0.0", + "@codemirror/lang-javascript": "^6.0.0", + "@codemirror/language": "^6.4.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0", + "@lezer/css": "^1.1.0", + "@lezer/html": "^1.3.0" + } + }, + "node_modules/@codemirror/lang-javascript": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.2.tgz", + "integrity": "sha512-VGQfY+FCc285AhWuwjYxQyUQcYurWlxdKYT4bqwr3Twnd5wP5WSeu52t4tvvuWmljT4EmgEgZCqSieokhtY8hg==", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.6.0", + "@codemirror/lint": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0", + "@lezer/javascript": "^1.0.0" + } + }, + "node_modules/@codemirror/language": { + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.10.1.tgz", + "integrity": "sha512-5GrXzrhq6k+gL5fjkAwt90nYDmjlzTIJV8THnxNFtNKWotMIlzzN+CpqxqwXOECnUdOndmSeWntVrVcv5axWRQ==", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.23.0", + "@lezer/common": "^1.1.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0", + "style-mod": "^4.0.0" + } + }, + "node_modules/@codemirror/lint": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.8.0.tgz", + "integrity": "sha512-lsFofvaw0lnPRJlQylNsC4IRt/1lI4OD/yYslrSGVndOJfStc58v+8p9dgGiD90ktOfL7OhBWns1ZETYgz0EJA==", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "crelt": "^1.0.5" + } + }, + "node_modules/@codemirror/state": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.4.1.tgz", + "integrity": "sha512-QkEyUiLhsJoZkbumGZlswmAhA7CBU02Wrz7zvH4SrcifbsqwlXShVXg65f3v/ts57W3dqyamEriMhij1Z3Zz4A==" + }, + "node_modules/@codemirror/view": { + "version": "6.26.3", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.26.3.tgz", + "integrity": "sha512-gmqxkPALZjkgSxIeeweY/wGQXBfwTUaLs8h7OKtSwfbj9Ct3L11lD+u1sS7XHppxFQoMDiMDp07P9f3I2jWOHw==", + "dependencies": { + "@codemirror/state": "^6.4.0", + "style-mod": "^4.1.0", + "w3c-keyname": "^2.2.4" + } + }, + "node_modules/@codesandbox/nodebox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/@codesandbox/nodebox/-/nodebox-0.1.8.tgz", + "integrity": "sha512-2VRS6JDSk+M+pg56GA6CryyUSGPjBEe8Pnae0QL3jJF1mJZJVMDKr93gJRtBbLkfZN6LD/DwMtf+2L0bpWrjqg==", + "dependencies": { + "outvariant": "^1.4.0", + "strict-event-emitter": "^0.4.3" + } + }, + "node_modules/@codesandbox/sandpack-client": { + "version": "2.13.8", + "resolved": "https://registry.npmjs.org/@codesandbox/sandpack-client/-/sandpack-client-2.13.8.tgz", + "integrity": "sha512-IjVlqfVK0fascNyUVH9hs5UZBx4KhKtyZyDrxdiDyQBtLmgESNaFWelAf4a/PX4gJp+H+WW6/iC6KzR7XtK//w==", + "dependencies": { + "@codesandbox/nodebox": "0.1.8", + "buffer": "^6.0.3", + "dequal": "^2.0.2", + "outvariant": "1.4.0", + "static-browser-server": "1.0.3" + } + }, + "node_modules/@codesandbox/sandpack-react": { + "version": "2.13.10", + "resolved": "https://registry.npmjs.org/@codesandbox/sandpack-react/-/sandpack-react-2.13.10.tgz", + "integrity": "sha512-0ipH4gbMqS/o4tyv7pApXehklTqEJi4qNNtKuL6UJd7Tc05fHLqlbEqG8VQ0AkrJ+e+CEL4R26B0k7WqLMLiIg==", + "dependencies": { + "@codemirror/autocomplete": "^6.4.0", + "@codemirror/commands": "^6.1.3", + "@codemirror/lang-css": "^6.0.1", + "@codemirror/lang-html": "^6.4.0", + "@codemirror/lang-javascript": "^6.1.2", + "@codemirror/language": "^6.3.2", + "@codemirror/state": "^6.2.0", + "@codemirror/view": "^6.7.1", + "@codesandbox/sandpack-client": "^2.13.8", + "@lezer/highlight": "^1.1.3", + "@react-hook/intersection-observer": "^3.1.1", + "@stitches/core": "^1.2.6", + "anser": "^2.1.1", + "clean-set": "^1.1.2", + "dequal": "^2.0.2", + "escape-carriage": "^1.3.1", + "lz-string": "^1.4.4", + "react-devtools-inline": "4.4.0", + "react-is": "^17.0.2" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18", + "react-dom": "^16.8.0 || ^17 || ^18" + } + }, "node_modules/@colors/colors": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", @@ -2980,6 +3139,57 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@lezer/common": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.1.tgz", + "integrity": "sha512-yemX0ZD2xS/73llMZIK6KplkjIjf2EvAHcinDi/TfJ9hS25G0388+ClHt6/3but0oOxinTcQHJLDXh6w1crzFQ==" + }, + "node_modules/@lezer/css": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/@lezer/css/-/css-1.1.8.tgz", + "integrity": "sha512-7JhxupKuMBaWQKjQoLtzhGj83DdnZY9MckEOG5+/iLKNK2ZJqKc6hf6uc0HjwCX7Qlok44jBNqZhHKDhEhZYLA==", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/highlight": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.0.tgz", + "integrity": "sha512-WrS5Mw51sGrpqjlh3d4/fOwpEV2Hd3YOkp9DBt4k8XZQcoTHZFB7sx030A6OcahF4J1nDQAa3jXlTVVYH50IFA==", + "dependencies": { + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@lezer/html": { + "version": "1.3.9", + "resolved": "https://registry.npmjs.org/@lezer/html/-/html-1.3.9.tgz", + "integrity": "sha512-MXxeCMPyrcemSLGaTQEZx0dBUH0i+RPl8RN5GwMAzo53nTsd/Unc/t5ZxACeQoyPUM5/GkPLRUs2WliOImzkRA==", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/javascript": { + "version": "1.4.16", + "resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.4.16.tgz", + "integrity": "sha512-84UXR3N7s11MPQHWgMnjb9571fr19MmXnr5zTv2XX0gHXXUvW3uPJ8GCjKrfTXmSdfktjRK0ayKklw+A13rk4g==", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.1.3", + "@lezer/lr": "^1.3.0" + } + }, + "node_modules/@lezer/lr": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.0.tgz", + "integrity": "sha512-Wst46p51km8gH0ZUmeNrtpRYmdlRHUpN1DQd3GFAyKANi8WVz8c2jHYTf1CVScFaCjQw1iO3ZZdqGDxQPRErTg==", + "dependencies": { + "@lezer/common": "^1.0.0" + } + }, "node_modules/@mdx-js/mdx": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-3.0.1.tgz", @@ -3180,6 +3390,30 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/@monaco-editor/loader": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@monaco-editor/loader/-/loader-1.4.0.tgz", + "integrity": "sha512-00ioBig0x642hytVspPl7DbQyaSWRaolYie/UFNjoTdvoKPzo6xrXLhTk9ixgIKcLH5b5vDOjVNiGyY+uDCUlg==", + "dependencies": { + "state-local": "^1.0.6" + }, + "peerDependencies": { + "monaco-editor": ">= 0.21.0 < 1" + } + }, + "node_modules/@monaco-editor/react": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@monaco-editor/react/-/react-4.6.0.tgz", + "integrity": "sha512-RFkU9/i7cN2bsq/iTkurMWOEErmYcY6JiQI3Jn+WeR/FGISH8JbHERjpS9oRuSOPvDMJI0Z8nJeKkbOs9sBYQw==", + "dependencies": { + "@monaco-editor/loader": "^1.4.0" + }, + "peerDependencies": { + "monaco-editor": ">= 0.25.0 < 1", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/@ndelangen/get-tarball": { "version": "3.0.9", "resolved": "https://registry.npmjs.org/@ndelangen/get-tarball/-/get-tarball-3.0.9.tgz", @@ -3292,6 +3526,11 @@ "node": ">= 8" } }, + "node_modules/@open-draft/deferred-promise": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz", + "integrity": "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==" + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -3359,6 +3598,26 @@ } } }, + "node_modules/@react-hook/intersection-observer": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@react-hook/intersection-observer/-/intersection-observer-3.1.1.tgz", + "integrity": "sha512-OTDx8/wFaRvzFtKl1dEUEXSOqK2zVJHporiTTdC2xO++0e9FEx9wIrPis5q3lqtXeZH9zYGLbk+aB75qNFbbuw==", + "dependencies": { + "@react-hook/passive-layout-effect": "^1.2.0", + "intersection-observer": "^0.10.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/@react-hook/passive-layout-effect": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@react-hook/passive-layout-effect/-/passive-layout-effect-1.2.1.tgz", + "integrity": "sha512-IwEphTD75liO8g+6taS+4oqz+nnroocNfWVHWz7j+N+ZO2vYrc6PV1q7GQhuahL0IOR7JccFTsFKQ/mb6iZWAg==", + "peerDependencies": { + "react": ">=16.8" + } + }, "node_modules/@rollup/pluginutils": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz", @@ -3673,6 +3932,11 @@ "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", "dev": true }, + "node_modules/@stitches/core": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@stitches/core/-/core-1.2.8.tgz", + "integrity": "sha512-Gfkvwk9o9kE9r9XNBmJRfV8zONvXThnm1tcuojL04Uy5uRyqg93DC83lDebl0rocZCfKSjUv+fWYtMQmEDJldg==" + }, "node_modules/@storybook/addon-actions": { "version": "8.0.4", "resolved": "https://registry.npmjs.org/@storybook/addon-actions/-/addon-actions-8.0.4.tgz", @@ -6645,6 +6909,11 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/anser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/anser/-/anser-2.1.1.tgz", + "integrity": "sha512-nqLm4HxOTpeLOxcmB3QWmV5TcDFhW9y/fyQ+hivtDFcK4OQ+pQ5fzPnXHM1Mfcm0VkLtvVi1TCPr++Qy0Q/3EQ==" + }, "node_modules/ansi-align": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", @@ -8419,6 +8688,11 @@ "consola": "^3.2.3" } }, + "node_modules/clean-set": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/clean-set/-/clean-set-1.1.2.tgz", + "integrity": "sha512-cA8uCj0qSoG9e0kevyOWXwPaELRPVg5Pxp6WskLMwerx257Zfnh8Nl0JBH59d7wQzij2CK7qEfJQK3RjuKKIug==" + }, "node_modules/clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", @@ -8902,6 +9176,11 @@ "integrity": "sha512-4ftCvShHjIZG/zzomHyunNpBof3sOFTTmU6s6q9DdqAL/ANqrKV3pr6Z6kVfBI4hjn59DFLImrBqn7GuuMqSZA==", "dev": true }, + "node_modules/crelt": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", + "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==" + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -8989,6 +9268,18 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, + "node_modules/d": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.2.tgz", + "integrity": "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==", + "dependencies": { + "es5-ext": "^0.10.64", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.12" + } + }, "node_modules/de-indent": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", @@ -9420,7 +9711,6 @@ "version": "16.4.5", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", - "dev": true, "engines": { "node": ">=12" }, @@ -9772,12 +10062,49 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/es5-ext": { + "version": "0.10.64", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", + "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", + "hasInstallScript": true, + "dependencies": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, "node_modules/es6-promise": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", "integrity": "sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==", "dev": true }, + "node_modules/es6-symbol": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz", + "integrity": "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==", + "dependencies": { + "d": "^1.0.2", + "ext": "^1.7.0" + }, + "engines": { + "node": ">=0.12" + } + }, "node_modules/esbuild": { "version": "0.20.1", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.1.tgz", @@ -9842,6 +10169,11 @@ "node": ">=6" } }, + "node_modules/escape-carriage": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/escape-carriage/-/escape-carriage-1.3.1.tgz", + "integrity": "sha512-GwBr6yViW3ttx1kb7/Oh+gKQ1/TrhYwxKqVmg5gS+BK+Qe2KrOa/Vh7w3HPBvgGf0LfcDGoY9I6NHKoA5Hozhw==" + }, "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -10773,6 +11105,20 @@ "integrity": "sha512-Cf6VksWPsTuW01vU9Mk/3vRue91Zevka5SjyNf3nEpokFRuqt/KjUQoGAwq9qMmhpLTHmXzSIrFRw8zxWzmFBA==", "dev": true }, + "node_modules/esniff": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", + "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.62", + "event-emitter": "^0.3.5", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/espree": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", @@ -10924,6 +11270,15 @@ "node": ">= 0.6" } }, + "node_modules/event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", + "dependencies": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, "node_modules/eventemitter3": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", @@ -11039,6 +11394,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/ext": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", + "dependencies": { + "type": "^2.7.2" + } + }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -12318,6 +12681,15 @@ "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz", "integrity": "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==" }, + "node_modules/html-url-attributes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.0.tgz", + "integrity": "sha512-/sXbVCWayk6GDVg3ctOX6nxaVj7So40FcFAnWlWGNAB1LpYKcV5Cd10APjPjW80O7zYW2MsjBV4zZ7IZO5fVow==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/html-void-elements": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", @@ -12649,6 +13021,11 @@ "node": ">= 0.4" } }, + "node_modules/intersection-observer": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/intersection-observer/-/intersection-observer-0.10.0.tgz", + "integrity": "sha512-fn4bQ0Xq8FTej09YC/jqKZwtijpvARlRp6wxL5WTA6yPe2YWSJ5RJh7Nm79rK2qB0wr6iDQzH60XGq5V/7u8YQ==" + }, "node_modules/ip": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.1.tgz", @@ -14214,7 +14591,6 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", - "dev": true, "bin": { "lz-string": "bin/bin.js" } @@ -15338,7 +15714,6 @@ "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, "engines": { "node": ">= 0.6" } @@ -15476,6 +15851,12 @@ "ufo": "^1.3.2" } }, + "node_modules/monaco-editor": { + "version": "0.48.0", + "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.48.0.tgz", + "integrity": "sha512-goSDElNqFfw7iDHMg8WDATkfcyeLTNpBHQpO8incK6p5qZt5G/1j41X0xdGzpIkGojGXM+QiRQyLjnfDVvrpwA==", + "peer": true + }, "node_modules/mri": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", @@ -15564,6 +15945,11 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, + "node_modules/next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" + }, "node_modules/nlcst-to-string": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/nlcst-to-string/-/nlcst-to-string-3.1.1.tgz", @@ -16264,6 +16650,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/outvariant": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.0.tgz", + "integrity": "sha512-AlWY719RF02ujitly7Kk/0QlV+pXGFDHrHf9O2OKqyqgBieaPOIeuSkL8sRK6j2WK+/ZAURq2kZsY0d8JapUiw==" + }, "node_modules/p-limit": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-5.0.0.tgz", @@ -17424,6 +17815,14 @@ "react": "^16.3.0 || ^17.0.1 || ^18.0.0" } }, + "node_modules/react-devtools-inline": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/react-devtools-inline/-/react-devtools-inline-4.4.0.tgz", + "integrity": "sha512-ES0GolSrKO8wsKbsEkVeiR/ZAaHQTY4zDh1UW8DImVmm8oaGLl3ijJDvSGe+qDRKPZdPRnDtWWnSvvrgxXdThQ==", + "dependencies": { + "es6-symbol": "^3" + } + }, "node_modules/react-dom": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", @@ -17439,8 +17838,32 @@ "node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + }, + "node_modules/react-markdown": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-9.0.1.tgz", + "integrity": "sha512-186Gw/vF1uRkydbsOIkcGXw7aHq0sZOCRFFjGrr7b9+nVZg4UfA4enXCaxm4fUzecU38sWfrNDitGhshuU7rdg==", + "dependencies": { + "@types/hast": "^3.0.0", + "devlop": "^1.0.0", + "hast-util-to-jsx-runtime": "^2.0.0", + "html-url-attributes": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.0.0", + "unified": "^11.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "@types/react": ">=18", + "react": ">=18" + } }, "node_modules/react-refresh": { "version": "0.14.0", @@ -19113,6 +19536,22 @@ "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", "dev": true }, + "node_modules/state-local": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/state-local/-/state-local-1.0.7.tgz", + "integrity": "sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==" + }, + "node_modules/static-browser-server": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/static-browser-server/-/static-browser-server-1.0.3.tgz", + "integrity": "sha512-ZUyfgGDdFRbZGGJQ1YhiM930Yczz5VlbJObrQLlk24+qNHVQx4OlLcYswEUo3bIyNAbQUIUR9Yr5/Hqjzqb4zA==", + "dependencies": { + "@open-draft/deferred-promise": "^2.1.0", + "dotenv": "^16.0.3", + "mime-db": "^1.52.0", + "outvariant": "^1.3.0" + } + }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -19196,6 +19635,11 @@ "bare-events": "^2.2.0" } }, + "node_modules/strict-event-emitter": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.4.6.tgz", + "integrity": "sha512-12KWeb+wixJohmnwNFerbyiBrAlq5qJLwIt38etRtKtmmHyDSoGlIqFE9wx+4IwG0aDjI7GV8tc8ZccjWZZtTg==" + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -19421,6 +19865,11 @@ "integrity": "sha512-UfJMcSJc+SEXEl9lH/VLHSZbThQyLpw1vLO1Lb+j4RWDvG3N2f7yj3PVQA3cmkTBNldJ9eFnM+xEXxHIXrYiJw==", "dev": true }, + "node_modules/style-mod": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.2.tgz", + "integrity": "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==" + }, "node_modules/style-to-object": { "version": "0.4.4", "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-0.4.4.tgz", @@ -21041,6 +21490,11 @@ "integrity": "sha512-PZBtLYcCLtEcjL14Fzb1gSxPBeL7nWvGhO5ZFPGqziCcr8uvHp0NDmdjBchp6KHL+tExcg0m3NISmKxhU394dA==", "dev": true }, + "node_modules/type": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", + "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -22755,6 +23209,11 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, + "node_modules/w3c-keyname": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", + "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==" + }, "node_modules/watchpack": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.1.tgz", @@ -23269,14 +23728,17 @@ "@astrojs/react": "^3.0.10", "@astrojs/svelte": "^5.2.0", "@astrojs/tailwind": "^5.1.0", + "@codesandbox/sandpack-react": "^2.13.10", "@egjs/react-flicking": "^4.11.2", "@meursyphus/flitter-chart": "^0.0.3", + "@monaco-editor/react": "^4.6.0", "@tailwindcss/typography": "^0.5.10", "@types/react": "^18.2.64", "@types/react-dom": "^18.2.21", "astro": "^4.4.15", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-markdown": "^9.0.1", "svelte": "^4.2.12", "tailwindcss": "^3.4.1", "typescript": "^5.4.2" @@ -23294,7 +23756,7 @@ }, "packages/flitter": { "name": "@meursyphus/flitter", - "version": "2.0.0-alpha.1", + "version": "2.0.0-alpha.2", "license": "MIT", "dependencies": { "color-rgba": "^3.0.0", @@ -23797,7 +24259,7 @@ }, "packages/flitter-svelte": { "name": "@meursyphus/flitter-svelte", - "version": "1.0.0", + "version": "2.0.0-alpha.1", "license": "MIT", "dependencies": { "linkedom": "^0.14.21" diff --git a/packages/docs/astro.config.mjs b/packages/docs/astro.config.mjs index 6fb0893..0e82eb6 100644 --- a/packages/docs/astro.config.mjs +++ b/packages/docs/astro.config.mjs @@ -11,6 +11,7 @@ export default defineConfig({ integrations: [react(), svelte(), tailwind(), mdx()], redirects: { "/docs": "/docs/introduction", + "/tutorial": "/tutorial/introduction", }, markdown: { shikiConfig: { diff --git a/packages/docs/package.json b/packages/docs/package.json index ea9abc4..0a3d068 100644 --- a/packages/docs/package.json +++ b/packages/docs/package.json @@ -15,14 +15,17 @@ "@astrojs/react": "^3.0.10", "@astrojs/svelte": "^5.2.0", "@astrojs/tailwind": "^5.1.0", + "@codesandbox/sandpack-react": "^2.13.10", "@egjs/react-flicking": "^4.11.2", "@meursyphus/flitter-chart": "^0.0.3", + "@monaco-editor/react": "^4.6.0", "@tailwindcss/typography": "^0.5.10", "@types/react": "^18.2.64", "@types/react-dom": "^18.2.21", "astro": "^4.4.15", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-markdown": "^9.0.1", "svelte": "^4.2.12", "tailwindcss": "^3.4.1", "typescript": "^5.4.2" diff --git a/packages/docs/src/components/ui/Header.astro b/packages/docs/src/components/ui/Header.astro index af53061..3ad0375 100644 --- a/packages/docs/src/components/ui/Header.astro +++ b/packages/docs/src/components/ui/Header.astro @@ -1,6 +1,12 @@ --- const url = new URL(Astro.request.url); const pathname = url.pathname; // 현재 페이지의 경로 + +let current: "docs" | "tutorial" | "none" = pathname.startsWith("/docs") + ? "docs" + : pathname.startsWith("/tutorial") + ? "tutorial" + : "none"; ---
docs
- tutorialtutorial
diff --git a/packages/docs/src/content/config.ts b/packages/docs/src/content/config.ts index dc7a0b2..ffc1ec0 100644 --- a/packages/docs/src/content/config.ts +++ b/packages/docs/src/content/config.ts @@ -1,7 +1,7 @@ import { defineCollection, z } from "astro:content"; const docs = defineCollection({ - type: "content", + type: "content", schema: z.object({ nav_group: z.string(), nav_group_order: z.number(), @@ -11,9 +11,18 @@ const docs = defineCollection({ description: z.string().optional(), tags: z.array(z.string()).optional(), image: z.string().optional(), - }) + }), +}); + +const tutorial = defineCollection({ + type: "content", + schema: z.object({ + files: z.record(z.string()), + title: z.string(), + }), }); export const collections = { docs: docs, + tutorial: tutorial, }; diff --git a/packages/docs/src/content/tutorial/000_introduction.mdx b/packages/docs/src/content/tutorial/000_introduction.mdx new file mode 100644 index 0000000..75adcd9 --- /dev/null +++ b/packages/docs/src/content/tutorial/000_introduction.mdx @@ -0,0 +1,60 @@ +--- +category: How to start +title: "Introduction" +files: + App.js: | + import { useEffect, useRef } from "react"; + import { + Alignment, + AppRunner, + Container, + Text, + } from "@meursyphus/flitter"; + + export default function App() { + let runner + const svgRef = useRef(null); + + const widget = Container({ + width: Infinity, + height: Infinity, + alignment: Alignment.center, + color: "red", + child: Text("Hello World") + }); + + useEffect(() => { + runner = new AppRunner({ + view: svgRef.current + }); + + runner.onMount({ + resizeTarget: svgRef.current + }); + + runner.runApp(widget); + + return () => { + runner.dispose(); + }; + }, []); + + return ( + + ); + } +--- + +# Introduction + +```bash +npm install @meursyphus/flitter +``` + +This example demonstrates how to render widgets using the `AppRunner` class. `AppRunner` is initialized with an SVG element as the `view` property, which is referenced through React's `useRef`. The widget is created using the `Container` function, which includes a `Text` widget as a child. + +Within the `useEffect` hook, an `AppRunner` instance is created, and a resize event is set up through the `onMount` method. Afterwards, the `runApp` method is called to render the widget. When the component unmounts, the `dispose` method is called to clean up resources. + +Through this process, the widget defined within the SVG element is rendered, and the layout is automatically updated according to screen size adjustments. This is handled by the `AppRunner`'s `handleViewResize` method. + +Using the `@meursyphus/flitter` library in this way allows for declarative and reactive data visualization without complex DOM manipulation. diff --git a/packages/docs/src/content/tutorial/001_react-helper.mdx b/packages/docs/src/content/tutorial/001_react-helper.mdx new file mode 100644 index 0000000..b027306 --- /dev/null +++ b/packages/docs/src/content/tutorial/001_react-helper.mdx @@ -0,0 +1,37 @@ +--- +category: How to start +title: Flitter-React +files: + App.js: | + import {Text,Container,Alignment} from '@meursyphus/flitter' + import Widget from '@meursyphus/flitter-react' + + export default function App() { + return ( + + ); + } +--- + +# Flitter-React + +Flitter-React is a library that facilitates the easy use of Flitter widgets in a React environment. +Through this, developers can integrate Flitter's widgets into their React projects without complex configurations. +The example below demonstrates how to implement a simple widget as a React component using Flitter-React. diff --git a/packages/docs/src/content/tutorial/002_container-text.mdx b/packages/docs/src/content/tutorial/002_container-text.mdx new file mode 100644 index 0000000..8b84e44 --- /dev/null +++ b/packages/docs/src/content/tutorial/002_container-text.mdx @@ -0,0 +1,67 @@ +--- +category: Basic +title: Container Text +files: + App.js: | + import { + Text, + Container, + Alignment, + EdgesInset, + Border, + BoxDecoration, + BorderRadius, + TextStyle + } from '@meursyphus/flitter' + import Widget from '@meursyphus/flitter-react' + + export default function App() { + return ( + + ); + } +--- + +# Container Text + +The descriptions of each prop used in this example are as follows: + +`Container` widget props: + +- `alignment`: Sets the alignment of child elements within the container. `Alignment.center` positions the child elements at the center. +- `decoration`: Defines the style of the container. `BoxDecoration` is used to set the background color, border, and the style of the border's edges. + - `border`: `Border.all` is used to apply a border to all sides. The color is "black", and the width is 3px. + - `borderRadius`: `BorderRadius.circular(10)` is used to round the edges. The value in the parentheses represents the radius of the corners. + - `color`: Sets the background color. In this example, it is set to "yellow". + +`Text` widget props: + +- `style`: Defines the style of the text. `TextStyle` is used to set the text color and font size. + - `color`: Sets the color of the text. In this example, it is set to "blue". + - `fontSize`: Sets the size of the text. In this example, it is set to 30. + +These settings allow users to freely adjust the appearance and layout of the widget. diff --git a/packages/docs/src/content/tutorial/003_column-row.mdx b/packages/docs/src/content/tutorial/003_column-row.mdx new file mode 100644 index 0000000..252daa4 --- /dev/null +++ b/packages/docs/src/content/tutorial/003_column-row.mdx @@ -0,0 +1,107 @@ +--- +category: Basic +title: Container Text +files: + App.js: | + import { + Column, + Row, + Container, + Text, + Alignment, + Flexible, + CrossAxisAlignment, + MainAxisAlignment, + SizedBox + } from '@meursyphus/flitter'; + import Widget from '@meursyphus/flitter-react'; + + export default function App() { + return ( + + ); + } +--- + +# Column Row + +The descriptions of each prop used in this example are as follows: +`Row` and `Container` widget prop descriptions: + +`Row` widget props: + +- `children`: This array defines the child widgets that will be included within the `Row`. Each child can be a `Flexible` widget. +- `mainAxis`: Defines the main axis of the `Row` widget, along which the child widgets will be arranged. It is horizontal by default. +- `crossAxis`: Defines the cross axis of the `Row` widget, determining how the child widgets will be aligned along this axis. For example, `crossAxisAlignment: CrossAxisAlignment.center` positions the children at the center of the cross axis. + +`Column` widget props: + +- `children`: This array defines the child widgets that will be included within the `Column`. Each child can be a `Flexible` widget. +- `mainAxis`: Defines the main axis of the `Column` widget, along which the child widgets will be arranged. It is vertical by default. +- `crossAxis`: Defines the cross axis of the `Column` widget, determining how the child widgets will be aligned along this axis. For example, `crossAxisAlignment: CrossAxisAlignment.center` positions the children at the center of the cross axis. + +`Flexible` widget props: + +- `flex`: Defines the proportion of space this widget will occupy within the `Row`. For example, `flex: 1` indicates a size relative to other `flex` values. +- `child`: The child element to be included within the `Flexible`, typically a `Container`. + +Layout description: + +- The first `Row` contains two `Container` children, each with a background color of red and blue respectively, and dimensions of 200x100. +- The second `Row` uses `Flexible` widgets to provide a flexible layout. The first `Flexible` has a `flex: 1` value, and the second has a `flex: 2` value. This means the second container occupies twice the space of the first. Each contains a container with heights of 100 and backgrounds of green and purple respectively. + +This configuration allows users to flexibly arrange containers of various sizes and colors, and easily adjust text alignment. diff --git a/packages/docs/src/content/tutorial/004_stack-positioned.mdx b/packages/docs/src/content/tutorial/004_stack-positioned.mdx new file mode 100644 index 0000000..b5da100 --- /dev/null +++ b/packages/docs/src/content/tutorial/004_stack-positioned.mdx @@ -0,0 +1,105 @@ +--- +category: Basic +title: Stack Positioned +files: + App.js: | + import { + Stack, + Positioned, + Container, + Text, + Alignment, + Flexible, + TextStyle, + } from '@meursyphus/flitter'; + import Widget from '@meursyphus/flitter-react'; + + export default function App() { + return ( + + ); + } +--- + +# Stack Positioned + +The descriptions of each prop used in this example are as follows: +`Stack` widget props: + +- `children`: This array defines the child widgets that will be layered on top of each other. Each child can be a `Positioned` widget to specify its exact position within the stack. + +`Positioned` widget props: + +- `top`: The distance from the top of the stack to the top of the widget. If specified, it positions the widget from the top. +- `bottom`: The distance from the bottom of the stack to the bottom of the widget. If specified, it positions the widget from the bottom. +- `left`: The distance from the left side of the stack to the left side of the widget. If specified, it positions the widget from the left. +- `right`: The distance from the right side of the stack to the right side of the widget. If specified, it positions the widget from the right. + +Layout description: + +- The `Stack` contains multiple `Positioned` widgets, each specifying its position within the stack: + - A blue container is positioned at the top-left corner. + - A red container is positioned at the top-right corner. + - A green container is positioned at the bottom-left corner. + - A yellow container is positioned at the bottom-right corner. + +This configuration allows precise control over the placement of widgets within a two-dimensional space, enabling complex layouts. diff --git a/packages/docs/src/content/tutorial/005_gesture-detector.mdx b/packages/docs/src/content/tutorial/005_gesture-detector.mdx new file mode 100644 index 0000000..311be1a --- /dev/null +++ b/packages/docs/src/content/tutorial/005_gesture-detector.mdx @@ -0,0 +1,53 @@ +--- +category: Basic +title: Gesture Detector +files: + App.js: | + import { + Container, + Text, + Alignment, + TextStyle, + GestureDetector, + Center + } from '@meursyphus/flitter'; + import Widget from '@meursyphus/flitter-react'; + + export default function App() { + return ( + alert("clicked"), + child: Container({ + alignment: Alignment.center, + height: 100, + width: 100, + color: 'yellow', + child: Text("Click me", { + style: new TextStyle({ + fontSize: 20, + fontWeight: 'bold', + }) + }) + }) + }) + }) + } + /> + ); + } +--- + +# Gesture Detector + +In this tutorial, we will learn how to handle user click events using the `GestureDetector` widget. `GestureDetector` can detect various mouse events and provides callbacks to respond to these events. In the example, we use the `onClick` event to display a notification when the user clicks on the widget. + +In the code example above, the `GestureDetector` is used to handle the click event. When the user clicks on the yellow box, an alert window containing the message "clicked" is displayed. This helps easily understand the basic usage of `GestureDetector`. + +Additionally, `GestureDetector` supports various events such as `onMouseDown`, `onMouseUp`, `onMouseMove`, etc., allowing for the implementation of more complex interactions. You can define callback functions for each event type to execute specific actions. + +Through this tutorial, learn the various ways to use `GestureDetector` and apply it in real applications. diff --git a/packages/docs/src/content/tutorial/006_stateless-widget.mdx b/packages/docs/src/content/tutorial/006_stateless-widget.mdx new file mode 100644 index 0000000..d07ffdb --- /dev/null +++ b/packages/docs/src/content/tutorial/006_stateless-widget.mdx @@ -0,0 +1,53 @@ +--- +category: Basic +title: Stack Positioned +files: + App.js: | + import { + StatelessWidget, + TextStyle, + Container, + Text, + EdgeInsets + } from '@meursyphus/flitter'; + import Widget from '@meursyphus/flitter-react'; + + class CustomWidget extends StatelessWidget { + + initState(context) { + // You can write code here to initialize state when the widget mounts. + } + + build(context) { + return Container({ + color: 'blue', + margin: EdgeInsets.all(10), + padding: EdgeInsets.all(10), + child: Text('lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.', { + style: new TextStyle({ + color: 'white', + fontSize: 20 + }) + }) + }) + } + } + + export default function App() { + return ( + + ) + } +--- + +# Stateless Widget + +A stateless widget does not hold any state. It is set up once when the widget is created and used to display the same information without any change in data. For example, it is commonly used to implement UI elements like static text, icons, or simple buttons. + +Such widgets construct their UI through the `build` method, which is called every time the widget needs to be drawn on the screen. However, since there is no state, the UI once built does not change with user interactions. + +In the example below, the `CustomWidget` class is implemented by inheriting from `StatelessWidget`. This class overrides the `build` method to return a container and text. This text maintains the style and content defined at the time of creation, and does not update with state changes. diff --git a/packages/docs/src/content/tutorial/007_stateful-widget.mdx b/packages/docs/src/content/tutorial/007_stateful-widget.mdx new file mode 100644 index 0000000..b7416e6 --- /dev/null +++ b/packages/docs/src/content/tutorial/007_stateful-widget.mdx @@ -0,0 +1,66 @@ +--- +category: Basic +title: Stack Positioned +files: + App.js: | + import { + StatefulWidget, + State, + TextStyle, + Container, + Text, + GestureDetector, + EdgeInsets + } from '@meursyphus/flitter'; + import Widget from '@meursyphus/flitter-react'; + + class CustomWidget extends StatefulWidget { + createState() { + return new CustomState(); + } + } + + + class CustomState extends State { + count = 1 + handleClick = () => { + this.setState(() => { + this.count++ + }) + } + build(context) { + return GestureDetector({ + onClick: this.handleClick, + child: Container({ + color: 'blue', + margin: EdgeInsets.all(20), + padding: EdgeInsets.all(20), + child: Text(`${this.count}`, { + style: new TextStyle({ + color: 'white', + fontSize: 20 + }) + }) + }) + }) + } + } + + export default function App() { + return ( + + ) + } +--- + +# Stateful Widget + +In this tutorial, we will learn how to create a widget with state management using `StatefulWidget`. The `CustomWidget` class inherits from `StatefulWidget` and creates a `CustomState` state through the `createState` method. Within this state class, a `count` variable is managed, and a `handleClick` method is defined to increment this `count` value whenever a click event occurs. +The UI of the widget is handled by using a `GestureDetector` to process click events, and the actual UI is constructed using a `Container`. Inside the `Container`, a `Text` widget displays the current `count` value. All these processes are defined in the `build` method. +This example helps understand how to structure a stateful widget and how the UI updates in response to state changes. +Each call to `setState` changes the state, which triggers a re-rendering of the UI. +This allows the user interface to reflect the latest state. diff --git a/packages/docs/src/content/tutorial/008_provider.mdx b/packages/docs/src/content/tutorial/008_provider.mdx new file mode 100644 index 0000000..070fd2a --- /dev/null +++ b/packages/docs/src/content/tutorial/008_provider.mdx @@ -0,0 +1,64 @@ +--- +category: Basic +title: Stack Positioned +files: + App.js: | + import {Provider} from '@meursyphus/flitter'; + import Widget from '@meursyphus/flitter-react'; + import CustomWidget from './CustomWidget.js'; + + export default function App() { + return ( + + ); + } + CustomWidget.js: | + import { + StatelessWidget, + TextStyle, + Container, + Text, + EdgeInsets, + Provider + } from '@meursyphus/flitter'; + import Widget from '@meursyphus/flitter-react'; + + export default class CustomWidget extends StatelessWidget { + + build(context) { + const text = Provider.of('provider-key', context); + + return Container({ + color: 'yellow', + margin: EdgeInsets.all(10), + padding: EdgeInsets.all(10), + child: Text(text, { + style: new TextStyle({ + color: 'black', + fontSize: 20 + }) + }) + }) + } + } +--- + +# Provider + +In this tutorial, we will learn how to use the `Provider` widget to pass data to child widgets. The `Provider` supplies specific data to its child widgets, and these child widgets can access the data using the `Provider.of` method. + +In the example above, the `Provider` holds a key called `provider-key` and a value called `value from provider`, and uses `CustomWidget` as its child widget. Inside the `CustomWidget`, it calls `Provider.of` to retrieve the value associated with `provider-key` and displays it on the screen. + +This approach allows child widgets to easily access the state managed by parent widgets, enabling efficient data management and enhancing component reusability. + +The `Provider` widget can be very useful in various scenarios, especially when data needs to be shared across multiple levels of an application. diff --git a/packages/docs/src/layouts/TutorialLayout.astro b/packages/docs/src/layouts/TutorialLayout.astro new file mode 100644 index 0000000..91fae9b --- /dev/null +++ b/packages/docs/src/layouts/TutorialLayout.astro @@ -0,0 +1,20 @@ +--- +import Layout from "./Layout.astro"; +interface Props { + title?: string; + description?: string; + image?: string; +} + +const { title } = Astro.props; +--- + + + + + + diff --git a/packages/docs/src/pages/tutorial/SandPack.tsx b/packages/docs/src/pages/tutorial/SandPack.tsx new file mode 100644 index 0000000..c5a4c1c --- /dev/null +++ b/packages/docs/src/pages/tutorial/SandPack.tsx @@ -0,0 +1,120 @@ +import Editor from "@monaco-editor/react"; +import { + useActiveCode, + SandpackStack, + FileTabs, + useSandpack, + SandpackProvider, + SandpackLayout, + SandpackPreview, +} from "@codesandbox/sandpack-react"; +import { useEffect, useRef } from "react"; +import { useState, useCallback } from "react"; + +function useDebounce any>( + callback: T, + delay: number, +): T { + const timer = useRef(null); + + const debouncedCallback = useCallback( + (...args: Parameters) => { + if (timer.current) { + clearTimeout(timer.current); + } + timer.current = setTimeout(() => { + callback(...args); + }, delay); + }, + [callback, delay], + ) as T; + + return debouncedCallback; +} + +function MonacoEditor() { + const { code, updateCode } = useActiveCode(); + const { sandpack, dispatch } = useSandpack(); + const refresh = useDebounce(() => dispatch({ type: "refresh" }), 1000); + + const handleChange = (value: string | undefined) => { + updateCode(value ?? ""); + if (sandpack.activeFile !== "/App.js") { + refresh(); + } + }; + + const editorRef = useRef(); + + useEffect(() => { + const handleResize = () => { + if (editorRef.current == null) return; + editorRef.current.layout({}); + console.log(editorRef.current.layout); + }; + window.addEventListener("resize", handleResize); + return () => window.removeEventListener("resize", handleResize); + }, []); + + return ( + + +
+ { + editorRef.current = editor; + }} + /> +
+
+ ); +} + +const customSetup = { + dependencies: { + "@meursyphus/flitter-react": "0.0.8", + "@meursyphus/flitter": "2.0.0-alpha.4", + "react-dom": "latest", + react: "latest", + "react-markdown": "latest", + }, +}; + +export default function MySandpack({ + files, +}: Readonly<{ + files: Record; +}>) { + return ( + + + + + + + ); +} diff --git a/packages/docs/src/pages/tutorial/[...slug].astro b/packages/docs/src/pages/tutorial/[...slug].astro new file mode 100644 index 0000000..8873fe3 --- /dev/null +++ b/packages/docs/src/pages/tutorial/[...slug].astro @@ -0,0 +1,79 @@ +--- +import SandPack from "./SandPack.tsx"; +import Layout from "../../layouts/TutorialLayout.astro"; +import { getCollection } from "astro:content"; + +export const getStaticPaths = async () => { + const tutorialEntries = await getCollection("tutorial"); + function resolveSlug(slug: string) { + return slug.replace(/^\d+_/, ""); + } + + return tutorialEntries + .map((entry) => ({ ...entry, slug: resolveSlug(entry.slug) })) + .map((entry, i, arr) => ({ + params: { + slug: entry.slug, + }, + props: { + prev: arr[i - 1], + next: arr[i + 1], + entry, + }, + })); +}; + +const { entry, prev, next } = Astro.props; +const { Content } = await entry.render(); +--- + + +
+
+ +
+ +
+
+
+ +
+
+
+ + diff --git a/packages/flitter-react/.gitignore b/packages/flitter-react/.gitignore new file mode 100644 index 0000000..08dc7b0 --- /dev/null +++ b/packages/flitter-react/.gitignore @@ -0,0 +1,3 @@ +node_modules/ +dist/ + diff --git a/packages/flitter-react/dist/Widget.d.ts b/packages/flitter-react/dist/Widget.d.ts index 64f85db..c38755c 100644 --- a/packages/flitter-react/dist/Widget.d.ts +++ b/packages/flitter-react/dist/Widget.d.ts @@ -3,6 +3,7 @@ type WidgetComponentProps = { widget?: Widget; width?: string; height?: string; + renderer?: "canvas" | "svg"; }; -declare function WidgetComponent({ width, height, widget, }: WidgetComponentProps): import("react/jsx-runtime").JSX.Element; +declare function WidgetComponent({ width, height, renderer, widget, }: WidgetComponentProps): import("react/jsx-runtime").JSX.Element; export default WidgetComponent; diff --git a/packages/flitter-react/dist/flitter-react.js b/packages/flitter-react/dist/flitter-react.js index e450140..d4a451c 100644 --- a/packages/flitter-react/dist/flitter-react.js +++ b/packages/flitter-react/dist/flitter-react.js @@ -1,28 +1,43 @@ -import { jsx as r } from "react/jsx-runtime"; -import { useRef as i, useEffect as u } from "react"; -import { AppRunner as c, Container as s, Alignment as d, Text as m } from "@meursyphus/flitter"; -function a({ - width: o = "100%", - height: f = "300px", - widget: l = s({ +import { jsx as t } from "react/jsx-runtime"; +import { useRef as s, useEffect as c } from "react"; +import { AppRunner as f, Container as h, Alignment as p, Text as a } from "@meursyphus/flitter"; +function v({ + width: l = "100%", + height: u = "300px", + renderer: i = "svg", + widget: r = h({ width: 1 / 0, height: 1 / 0, - alignment: d.center, - child: m("Hello World") + alignment: p.center, + child: a("Hello World") }) }) { - const e = i(null), n = i(null); - return u(() => { - const t = new c({ + const o = s(null), n = s(null); + return c(() => { + const e = new f({ view: n.current, window, document }); - t.runApp(l), t.onMount({ - resizeTarget: e.current - }); - }, []), /* @__PURE__ */ r("div", { style: { width: o, height: f }, ref: e, children: /* @__PURE__ */ r("svg", { style: { width: "100%", height: "100%" }, ref: n }) }); + return e.runApp(r), e.onMount({ + resizeTarget: o.current + }), () => { + e.dispose(); + }; + }, [r, i]), /* @__PURE__ */ t("div", { style: { width: l, height: u }, ref: o, children: i === "canvas" ? /* @__PURE__ */ t( + "canvas", + { + style: { width: "100%", height: "100%" }, + ref: n + } + ) : /* @__PURE__ */ t( + "svg", + { + style: { width: "100%", height: "100%" }, + ref: n + } + ) }); } export { - a as default + v as default }; diff --git a/packages/flitter-react/package.json b/packages/flitter-react/package.json index 15bdcf1..2315ef0 100644 --- a/packages/flitter-react/package.json +++ b/packages/flitter-react/package.json @@ -1,7 +1,7 @@ { "name": "@meursyphus/flitter-react", "private": false, - "version": "0.0.3", + "version": "0.0.8", "type": "module", "scripts": { "dev": "vite", diff --git a/packages/flitter-react/src/.DS_Store b/packages/flitter-react/src/.DS_Store new file mode 100644 index 0000000..c4fb4d7 Binary files /dev/null and b/packages/flitter-react/src/.DS_Store differ diff --git a/packages/flitter-react/src/lib/Widget.tsx b/packages/flitter-react/src/lib/Widget.tsx index b26c72d..c9d39e4 100644 --- a/packages/flitter-react/src/lib/Widget.tsx +++ b/packages/flitter-react/src/lib/Widget.tsx @@ -11,11 +11,13 @@ type WidgetComponentProps = { widget?: Widget; width?: string; height?: string; + renderer?: "canvas" | "svg"; }; function WidgetComponent({ width = "100%", height = "300px", + renderer = "svg", widget = Container({ width: Infinity, height: Infinity, @@ -24,11 +26,11 @@ function WidgetComponent({ }), }: WidgetComponentProps) { const containerRef = useRef(null); - const svgRef = useRef(null); + const ref = useRef(null); useEffect(() => { const runner = new AppRunner({ - view: svgRef.current!, + view: ref.current!, window: window, document: document, }); @@ -36,11 +38,25 @@ function WidgetComponent({ runner.onMount({ resizeTarget: containerRef.current!, }); - }, []); + + return () => { + runner.dispose(); + }; + }, [widget, renderer]); return (
- + {renderer === "canvas" ? ( + } + /> + ) : ( + } + /> + )}
); } diff --git a/packages/flitter/package.json b/packages/flitter/package.json index 2c45124..d0a7f04 100644 --- a/packages/flitter/package.json +++ b/packages/flitter/package.json @@ -1,6 +1,6 @@ { "name": "@meursyphus/flitter", - "version": "2.0.0-alpha.2", + "version": "2.0.0-alpha.4", "description": "A declarative, widget-based library built on SVG for simplifying data visualization with a Flutter-like syntax.", "keywords": [ "flitter", diff --git a/packages/flitter/src/framework/renderer/renderer.ts b/packages/flitter/src/framework/renderer/renderer.ts index 48ac89c..a37fb76 100644 --- a/packages/flitter/src/framework/renderer/renderer.ts +++ b/packages/flitter/src/framework/renderer/renderer.ts @@ -64,7 +64,10 @@ export class RenderContext { } dispose() { - this.resizeObserver.disconnect(); + if (this.resizeObserver) { + this.resizeObserver.disconnect(); + this.resizeObserver = null; + } } observeSize(target: HTMLElement) { diff --git a/packages/flitter/src/runApp.ts b/packages/flitter/src/runApp.ts index dd25c96..85acdff 100644 --- a/packages/flitter/src/runApp.ts +++ b/packages/flitter/src/runApp.ts @@ -10,7 +10,7 @@ import { } from "./framework"; import { HitTestDispatcher } from "./hit-test/HitTestDispatcher"; import { RenderContext } from "./framework/renderer/renderer"; -import { Constraints, type Size } from "./type"; +import { Constraints } from "./type"; type AppRunnerProps = { document?: Document; @@ -39,7 +39,7 @@ export class AppRunner { document: _document, window: _window, }); - this.renderContext.addResizeHandler(size => this.handleViewResize(size)); + this.renderContext.addResizeHandler(() => this.handleViewResize()); const renderFrameDispatcher = new RenderFrameDispatcher(); this.scheduler = new Scheduler({ renderFrameDispatcher }); this.buildOwner = new BuildOwner({ @@ -93,9 +93,7 @@ export class AppRunner { resizeTarget && this.renderContext.observeSize(resizeTarget); } - handleViewResize = (size: Size) => { - if (this.rendererType === "canvas") { - } + handleViewResize = () => { if (this.didRun) { this.draw(); } else { @@ -109,7 +107,10 @@ export class AppRunner { } dispose() { - this.root.unmount(); + if (this.root) { + this.root.unmount(); + this.root = null; + } this.renderContext.dispose(); } }