├── .editorconfig ├── .github ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .yarnrc ├── CHANGELOG.md ├── LICENSE ├── NOTICE ├── README.md ├── config ├── ttmdx-core.json ├── ttmdx-react.json ├── ttmdx-svelte.json ├── ttmdx-vue2.json ├── ttmdx-vue3.json ├── typedoc-core.json ├── typedoc-react.json ├── typedoc-svelte.json ├── typedoc-vue2.json └── typedoc-vue3.json ├── images ├── cfcs-compatible.png ├── cfcs-dom.png ├── cfcs-lifecycle.png ├── cfcs-reactive.png └── logo.png ├── lerna.json ├── package.json ├── packages ├── angular │ ├── .browserslistrc │ ├── .editorconfig │ ├── .gitignore │ ├── .vscode │ │ ├── extensions.json │ │ ├── launch.json │ │ └── tasks.json │ ├── LICENSE │ ├── README.md │ ├── angular.json │ ├── karma.conf.js │ ├── package.json │ ├── projects │ │ └── angular │ │ │ ├── CHANGELOG.md │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── karma.conf.js │ │ │ ├── ng-package.json │ │ │ ├── package.json │ │ │ ├── src │ │ │ ├── public-api.ts │ │ │ ├── types.ts │ │ │ └── useReactive.ts │ │ │ ├── tsconfig.lib.json │ │ │ ├── tsconfig.lib.prod.json │ │ │ └── tsconfig.spec.json │ ├── src │ │ ├── app │ │ │ ├── app.component.css │ │ │ ├── app.component.html │ │ │ ├── app.component.spec.ts │ │ │ ├── app.component.ts │ │ │ └── app.module.ts │ │ ├── assets │ │ │ └── .gitkeep │ │ ├── environments │ │ │ ├── environment.prod.ts │ │ │ └── environment.ts │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── main.ts │ │ ├── polyfills.ts │ │ ├── styles.css │ │ └── test.ts │ ├── tsconfig.app.json │ ├── tsconfig.json │ └── tsconfig.spec.json ├── cli │ ├── LICENSE │ ├── README.md │ ├── index.js │ ├── package.json │ ├── rollup.config.js │ ├── src │ │ └── lerna.ts │ └── tsconfig.json ├── core │ ├── CHANGELOG.md │ ├── LICENSE │ ├── README.md │ ├── jest.config.js │ ├── package.json │ ├── rollup.config.js │ ├── src │ │ ├── core │ │ │ ├── index.ts │ │ │ ├── types.ts │ │ │ └── utils.ts │ │ ├── dom │ │ │ ├── index.ts │ │ │ └── utils.ts │ │ ├── index.ts │ │ ├── index.umd.ts │ │ └── reactive │ │ │ ├── ComputedObserver.ts │ │ │ ├── Observer.ts │ │ │ ├── ReactiveAdapter.ts │ │ │ ├── adaptReactive.ts │ │ │ ├── const.ts │ │ │ ├── decorators │ │ │ ├── Computed.ts │ │ │ ├── Observe.ts │ │ │ └── ReactiveSubscribe.ts │ │ │ ├── detectDependencies.ts │ │ │ ├── hooks.ts │ │ │ ├── index.ts │ │ │ ├── inline.ts │ │ │ ├── types.ts │ │ │ └── utils.ts │ ├── test │ │ ├── manual │ │ │ └── index.html │ │ └── unit │ │ │ └── computed.spec.ts │ ├── tsconfig.declaration.json │ ├── tsconfig.json │ └── tsconfig.test.json ├── docs │ ├── .eslintrc.json │ ├── .gitignore │ ├── docs │ │ ├── examples │ │ │ ├── components │ │ │ │ ├── GestureApp.tsx │ │ │ │ ├── ImageOnLoadApp.tsx │ │ │ │ ├── KeyApp.tsx │ │ │ │ ├── ScrollApp.tsx │ │ │ │ └── WindowSizeApp.tsx │ │ │ ├── useGesture.mdx │ │ │ ├── useImageOnLoad.mdx │ │ │ ├── useKey.mdx │ │ │ ├── useScroll.mdx │ │ │ └── useWindowSize.mdx │ │ └── tutorials │ │ │ ├── best-practice │ │ │ ├── best-practice.mdx │ │ │ ├── use-events.mdx │ │ │ ├── use-lifecycle.mdx │ │ │ ├── use-methods.mdx │ │ │ ├── use-props.mdx │ │ │ ├── use-state.mdx │ │ │ └── use-typescript.mdx │ │ │ ├── cfcs-dom.mdx │ │ │ ├── cfcs-reactive-lifecycle.mdx │ │ │ ├── cfcs-reactive-state.mdx │ │ │ ├── cfcs-reactive-support-frameworks.mdx │ │ │ ├── cfcs-reactive.mdx │ │ │ └── what-is-cfcs.mdx │ ├── docusaurus.config.js │ ├── package.json │ ├── plugin │ │ ├── glslify.js │ │ └── preload-font.js │ ├── polyfill.js │ ├── showcase │ │ ├── data.ts │ │ ├── layout │ │ │ ├── index.tsx │ │ │ └── styles.module.css │ │ └── pages │ │ │ ├── index.module.css │ │ │ └── index.tsx │ ├── sidebars.js │ ├── src │ │ ├── components │ │ │ ├── EasilyReactiveComponent.tsx │ │ │ ├── FrameworkTabs.tsx │ │ │ └── VueFrameworkTabItems.tsx │ │ ├── pages │ │ │ ├── Home.tsx │ │ │ └── index.tsx │ │ ├── shim.d.ts │ │ └── styles │ │ │ ├── custom.css │ │ │ └── global.css │ ├── static │ │ ├── font │ │ │ └── Staatliches │ │ │ │ ├── OFL.txt │ │ │ │ └── Staatliches-Regular.ttf │ │ ├── img │ │ │ ├── egjs.svg │ │ │ ├── egjs_white.svg │ │ │ ├── favicon.ico │ │ │ └── nopensource.png │ │ ├── logos │ │ │ ├── Untitled.png │ │ │ ├── axes.png │ │ │ ├── conveyer.png │ │ │ ├── flicking.png │ │ │ ├── grid.png │ │ │ ├── imready.png │ │ │ ├── infinitegrid.png │ │ │ ├── view360.png │ │ │ └── view3d.png │ │ └── tutorials │ │ │ ├── cfcs-compatible.png │ │ │ ├── cfcs-dom.png │ │ │ ├── cfcs-lifecycle.png │ │ │ ├── cfcs-reactive.png │ │ │ ├── cfcs-typescript.png │ │ │ └── logo.png │ └── tsconfig.json ├── react │ ├── CHANGELOG.md │ ├── LICENSE │ ├── README.md │ ├── package.json │ ├── rollup.config.js │ ├── src │ │ ├── index.ts │ │ └── reactive │ │ │ ├── index.ts │ │ │ ├── types.ts │ │ │ └── useReactive.ts │ ├── tsconfig.declaration.json │ └── tsconfig.json ├── svelte │ ├── .gitignore │ ├── CHANGELOG.md │ ├── LICENSE │ ├── README.md │ ├── global.d.ts │ ├── package.json │ ├── rollup.build.config.js │ ├── rollup.config.js │ ├── src │ │ ├── index.ts │ │ └── reactive │ │ │ ├── index.ts │ │ │ ├── types.ts │ │ │ └── useReactive.ts │ ├── tsconfig.declaration.json │ └── tsconfig.json ├── vue2 │ ├── CHANGELOG.md │ ├── LICENSE │ ├── README.md │ ├── package.json │ ├── rollup.config.js │ ├── src │ │ ├── index.ts │ │ └── reactive │ │ │ ├── index.ts │ │ │ ├── types.ts │ │ │ └── useReactive.ts │ ├── tsconfig.declaration.json │ └── tsconfig.json └── vue3 │ ├── CHANGELOG.md │ ├── README.md │ ├── package.json │ ├── rollup.config.js │ ├── src │ ├── index.ts │ └── reactive │ │ ├── index.ts │ │ ├── types.ts │ │ └── useReactive.ts │ ├── tsconfig.declaration.json │ └── tsconfig.json ├── reactive.md ├── tsconfig.api.json ├── tsconfig.json └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | insert_final_newline = true 6 | trim_trailing_whitespace = true 7 | charset = utf-8 8 | end_of_line = lf 9 | indent_style = space 10 | indent_size = 2 11 | max_line_length = 80 12 | 13 | [{*.json,.travis.yml}] 14 | indent_style = space 15 | indent_size = 2 16 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Environments 2 | * Framework name: 3 | * Framework version: 4 | * Component version: 5 | * Testable Address(optional): 6 | 7 | ## Description 8 | 9 | 10 | ## Steps to check or reproduce 11 | 12 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Issue 2 | 3 | 4 | ## Details 5 | 6 | -------------------------------------------------------------------------------- /.yarnrc: -------------------------------------------------------------------------------- 1 | workspaces-experimental true 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## 0.1.0 (2023-02-24) 7 | ### :sparkles: Packages 8 | * `@cfcs/core` 0.1.0 9 | * `@cfcs/react` 0.1.0 10 | * `@cfcs/svelte` 0.1.0 11 | * `@cfcs/vue2` 0.1.0 12 | * `@cfcs/vue3` 0.1.0 13 | * `@cfcs/angular` 0.1.0 14 | 15 | 16 | ### :rocket: New Features 17 | 18 | * All 19 | * v0.1 c33732a 20 | 21 | 22 | ### :memo: Documentation 23 | 24 | * All 25 | * fix README 6d479bd 26 | * Other 27 | * fix url a68c3dd 28 | 29 | 30 | ### :mega: Other 31 | 32 | * All 33 | * fix packages' url 846fcf2 34 | * update packages versions ae79c40 35 | * Other 36 | * add github config 3164542 37 | * add gitignore 8f0e1c5 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2022-present NAVER Corp. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | CFCs 2 | Copyright (c) 2022-present NAVER Corp. 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | 22 | -------------------------------------------------------------------------------------- 23 | 24 | This project contains subcomponents with separate copyright notices and license terms. 25 | Your use of the source code for these subcomponents is subject to the terms and conditions of the following licenses. 26 | 27 | ===== 28 | 29 | angular/angular-cli 30 | https://github.com/angular/angular-cli 31 | 32 | 33 | The MIT License 34 | 35 | Copyright (c) 2017 Google, Inc. 36 | 37 | Permission is hereby granted, free of charge, to any person obtaining a copy 38 | of this software and associated documentation files (the "Software"), to deal 39 | in the Software without restriction, including without limitation the rights 40 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 41 | copies of the Software, and to permit persons to whom the Software is 42 | furnished to do so, subject to the following conditions: 43 | 44 | The above copyright notice and this permission notice shall be included in all 45 | copies or substantial portions of the Software. 46 | 47 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 48 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 49 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 50 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 51 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 52 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 53 | SOFTWARE. 54 | 55 | ===== 56 | -------------------------------------------------------------------------------- /config/ttmdx-core.json: -------------------------------------------------------------------------------- 1 | { 2 | "locales": ["ko"], 3 | "outDir": "./packages/docs/docs/api/Core", 4 | "localesDir": "./packages/docs/i18n/{locale}/docusaurus-plugin-content-docs/current/api/Core", 5 | "typedoc": "./config/typedoc-core.json", 6 | "tsconfig": "./tsconfig.api.json", 7 | "baseUrl": "https://github.com/naver/cfcs/" 8 | } 9 | -------------------------------------------------------------------------------- /config/ttmdx-react.json: -------------------------------------------------------------------------------- 1 | { 2 | "locales": ["ko"], 3 | "outDir": "./packages/docs/docs/api/React", 4 | "localesDir": "../packages/docs/i18n/{locale}/docusaurus-plugin-content-docs/current/api/React", 5 | "typedoc": "./config/typedoc-react.json", 6 | "tsconfig": "./tsconfig.api.json", 7 | "baseUrl": "https://github.com/naver/cfcs/" 8 | } 9 | -------------------------------------------------------------------------------- /config/ttmdx-svelte.json: -------------------------------------------------------------------------------- 1 | { 2 | "locales": ["ko"], 3 | "outDir": "./packages/docs/docs/api/Svelte", 4 | "localesDir": "./packages/docs/i18n/{locale}/docusaurus-plugin-content-docs/current/api/Svelte", 5 | "typedoc": "./config/typedoc-svelte.json", 6 | "tsconfig": "./tsconfig.api.json", 7 | "baseUrl": "https://github.com/naver/cfcs/" 8 | } 9 | -------------------------------------------------------------------------------- /config/ttmdx-vue2.json: -------------------------------------------------------------------------------- 1 | { 2 | "locales": ["ko"], 3 | "outDir": "./packages/docs/docs/api/Vue2", 4 | "localesDir": "./packages/docs/i18n/{locale}/docusaurus-plugin-content-docs/current/api/Vue 2", 5 | "typedoc": "./config/typedoc-vue2.json", 6 | "tsconfig": "./tsconfig.api.json", 7 | "baseUrl": "https://github.com/naver/cfcs/" 8 | } 9 | -------------------------------------------------------------------------------- /config/ttmdx-vue3.json: -------------------------------------------------------------------------------- 1 | { 2 | "locales": ["ko"], 3 | "outDir": "./packages/docs/docs/api/Vue3", 4 | "localesDir": "./packages/docs/i18n/{locale}/docusaurus-plugin-content-docs/current/api/Vue3", 5 | "typedoc": "./config/typedoc-vue3.json", 6 | "tsconfig": "./tsconfig.api.json", 7 | "baseUrl": "https://github.com/naver/cfcs/" 8 | } 9 | -------------------------------------------------------------------------------- /config/typedoc-core.json: -------------------------------------------------------------------------------- 1 | { 2 | "entryPoints": [ 3 | "../packages/core/src/index.ts", 4 | ], 5 | "exclude": ["**/*+(.spec|.e2e|.d).ts"], 6 | "sourceLinkTemplate": "https://github.com/naver/cfcs/blob/{gitRevision}/{path}#L{line}", 7 | "excludeExternals": true, 8 | "excludePrivate": true, 9 | "excludeProtected": true, 10 | "externalSymbolLinkMappings": { 11 | "@cfcs/core": {}, 12 | "@egjs/component": { 13 | "Component": "https://naver.github.io/egjs-component/" 14 | }, 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /config/typedoc-react.json: -------------------------------------------------------------------------------- 1 | { 2 | "entryPoints": [ 3 | "../packages/react/src/index.ts", 4 | ], 5 | "exclude": ["**/*+(.spec|.e2e|.d).ts"], 6 | "sourceLinkTemplate": "https://github.com/naver/cfcs/blob/{gitRevision}/{path}#L{line}", 7 | "excludeExternals": true, 8 | "excludePrivate": true, 9 | "excludeProtected": true, 10 | "externalSymbolLinkMappings": { 11 | "@cfcs/core": {}, 12 | "@egjs/component": { 13 | "Component": "https://naver.github.io/egjs-component/" 14 | }, 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /config/typedoc-svelte.json: -------------------------------------------------------------------------------- 1 | { 2 | "entryPoints": [ 3 | "../packages/svelte/src/index.ts", 4 | ], 5 | "exclude": ["**/*+(.spec|.e2e|.d).ts"], 6 | "sourceLinkTemplate": "https://github.com/naver/cfcs/blob/{gitRevision}/{path}#L{line}", 7 | "excludeExternals": true, 8 | "excludePrivate": true, 9 | "excludeProtected": true, 10 | "externalSymbolLinkMappings": { 11 | "@cfcs/core": {}, 12 | "@egjs/component": { 13 | "Component": "https://naver.github.io/egjs-component/" 14 | }, 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /config/typedoc-vue2.json: -------------------------------------------------------------------------------- 1 | { 2 | "entryPoints": [ 3 | "../packages/vue2/src/index.ts", 4 | ], 5 | "exclude": ["**/*+(.spec|.e2e|.d).ts"], 6 | "sourceLinkTemplate": "https://github.com/naver/cfcs/blob/{gitRevision}/{path}#L{line}", 7 | "excludeExternals": true, 8 | "excludePrivate": true, 9 | "excludeProtected": true, 10 | "externalSymbolLinkMappings": { 11 | "@cfcs/core": {}, 12 | "@egjs/component": { 13 | "Component": "https://naver.github.io/egjs-component/" 14 | }, 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /config/typedoc-vue3.json: -------------------------------------------------------------------------------- 1 | { 2 | "entryPoints": [ 3 | "../packages/vue3/src/index.ts", 4 | ], 5 | "exclude": ["**/*+(.spec|.e2e|.d).ts"], 6 | "sourceLinkTemplate": "https://github.com/naver/cfcs/blob/{gitRevision}/{path}#L{line}", 7 | "excludeExternals": true, 8 | "excludePrivate": true, 9 | "excludeProtected": true, 10 | "externalSymbolLinkMappings": { 11 | "@cfcs/core": {}, 12 | "@egjs/component": { 13 | "Component": "https://naver.github.io/egjs-component/" 14 | }, 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /images/cfcs-compatible.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naver/cfcs/798b4fb2220cf0562313d2f87c36d7173cd070e1/images/cfcs-compatible.png -------------------------------------------------------------------------------- /images/cfcs-dom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naver/cfcs/798b4fb2220cf0562313d2f87c36d7173cd070e1/images/cfcs-dom.png -------------------------------------------------------------------------------- /images/cfcs-lifecycle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naver/cfcs/798b4fb2220cf0562313d2f87c36d7173cd070e1/images/cfcs-lifecycle.png -------------------------------------------------------------------------------- /images/cfcs-reactive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naver/cfcs/798b4fb2220cf0562313d2f87c36d7173cd070e1/images/cfcs-reactive.png -------------------------------------------------------------------------------- /images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naver/cfcs/798b4fb2220cf0562313d2f87c36d7173cd070e1/images/logo.png -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "npmClient": "yarn", 3 | "useWorkspaces": true, 4 | "packages": [ 5 | "packages/*", 6 | "packages/angular/projects/angular" 7 | ], 8 | "useNx": false, 9 | "version": "independent", 10 | "lernaHelperOptions": { 11 | "deployFileMap": [ 12 | { 13 | "basePath": "packages/core/dist", 14 | "dists": [ 15 | "demo/release/{{version}}/dist", 16 | "demo/release/latest/dist" 17 | ] 18 | } 19 | ], 20 | "beforeReleaseScripts": [ 21 | "npm run packages:build", 22 | "npm run demo:build", 23 | "npm run demo:deploy" 24 | ] 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cfcs-root", 3 | "description": "Write once, create framework components that supports React, Vue, Svelte, and more.", 4 | "private": true, 5 | "scripts": { 6 | "packages": "npm run packages:update && npm run packages:build && npm run packages:publish", 7 | "packages:update": "lerna-helper version", 8 | "packages:build": "npm run build --prefix packages/core && lerna run build --ignore @cfcs/core --ignore @cfcs/angular --ignore docs --stream", 9 | "packages:publish": "lerna-helper publish --ignore @cfcs/angular --commit 'chore: update packages versions'", 10 | "changelog": "lerna-helper changelog --type all --base @cfcs/core", 11 | "docs:build": "yarn docs:core && yarn docs:react && yarn docs:vue2 && yarn docs:vue3 && yarn docs:svelte", 12 | "docs:core": "typedoc-to-mdx -c ./config/ttmdx-core.json", 13 | "docs:react": "typedoc-to-mdx -c ./config/ttmdx-react.json", 14 | "docs:vue2": "typedoc-to-mdx -c ./config/ttmdx-vue2.json", 15 | "docs:vue3": "typedoc-to-mdx -c ./config/ttmdx-vue3.json", 16 | "docs:svelte": "typedoc-to-mdx -c ./config/ttmdx-svelte.json", 17 | "demo:build": "npm run docs:build && npm run packages:build && npm run build --prefix packages/docs", 18 | "demo:deploy": "lerna-helper deploy --base @cfcs/core --remote upstream", 19 | "release": "lerna-helper release --base @cfcs/core --remote upstream --branch main" 20 | }, 21 | "devDependencies": { 22 | "@egjs/release-helper": "^0.2.9", 23 | "lerna": "^4.0.0", 24 | "typedoc-to-mdx": "^0.1.0" 25 | }, 26 | "workspaces": { 27 | "packages": [ 28 | "packages/*", 29 | "packages/angular/projects/angular" 30 | ] 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /packages/angular/.browserslistrc: -------------------------------------------------------------------------------- 1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below. 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | 5 | # For the full list of supported browsers by the Angular framework, please see: 6 | # https://angular.io/guide/browser-support 7 | 8 | # You can see what browsers were selected by your queries by running: 9 | # npx browserslist 10 | 11 | last 1 Chrome version 12 | last 1 Firefox version 13 | last 2 Edge major versions 14 | last 2 Safari major versions 15 | last 2 iOS major versions 16 | Firefox ESR 17 | -------------------------------------------------------------------------------- /packages/angular/.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.ts] 12 | quote_type = single 13 | 14 | [*.md] 15 | max_line_length = off 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /packages/angular/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # Compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | /bazel-out 8 | 9 | # Node 10 | /node_modules 11 | npm-debug.log 12 | yarn-error.log 13 | 14 | # IDEs and editors 15 | .idea/ 16 | .project 17 | .classpath 18 | .c9/ 19 | *.launch 20 | .settings/ 21 | *.sublime-workspace 22 | 23 | # Visual Studio Code 24 | .vscode/* 25 | !.vscode/settings.json 26 | !.vscode/tasks.json 27 | !.vscode/launch.json 28 | !.vscode/extensions.json 29 | .history/* 30 | 31 | # Miscellaneous 32 | /.angular/cache 33 | .sass-cache/ 34 | /connect.lock 35 | /coverage 36 | /libpeerconnection.log 37 | testem.log 38 | /typings 39 | 40 | # System files 41 | .DS_Store 42 | Thumbs.db 43 | -------------------------------------------------------------------------------- /packages/angular/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846 3 | "recommendations": ["angular.ng-template"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/angular/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 3 | "version": "0.2.0", 4 | "configurations": [ 5 | { 6 | "name": "ng serve", 7 | "type": "pwa-chrome", 8 | "request": "launch", 9 | "preLaunchTask": "npm: start", 10 | "url": "http://localhost:4200/" 11 | }, 12 | { 13 | "name": "ng test", 14 | "type": "chrome", 15 | "request": "launch", 16 | "preLaunchTask": "npm: test", 17 | "url": "http://localhost:9876/debug.html" 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /packages/angular/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558 3 | "version": "2.0.0", 4 | "tasks": [ 5 | { 6 | "type": "npm", 7 | "script": "start", 8 | "isBackground": true, 9 | "problemMatcher": { 10 | "owner": "typescript", 11 | "pattern": "$tsc", 12 | "background": { 13 | "activeOnStart": true, 14 | "beginsPattern": { 15 | "regexp": "(.*?)" 16 | }, 17 | "endsPattern": { 18 | "regexp": "bundle generation complete" 19 | } 20 | } 21 | } 22 | }, 23 | { 24 | "type": "npm", 25 | "script": "test", 26 | "isBackground": true, 27 | "problemMatcher": { 28 | "owner": "typescript", 29 | "pattern": "$tsc", 30 | "background": { 31 | "activeOnStart": true, 32 | "beginsPattern": { 33 | "regexp": "(.*?)" 34 | }, 35 | "endsPattern": { 36 | "regexp": "bundle generation complete" 37 | } 38 | } 39 | } 40 | } 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /packages/angular/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2022-present NAVER Corp. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /packages/angular/README.md: -------------------------------------------------------------------------------- 1 | # AngularProject 2 | 3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 14.1.0. 4 | 5 | ## Development server 6 | 7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files. 8 | 9 | ## Code scaffolding 10 | 11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. 12 | 13 | ## Build 14 | 15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. 16 | 17 | ## Running unit tests 18 | 19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). 20 | 21 | ## Running end-to-end tests 22 | 23 | Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities. 24 | 25 | ## Further help 26 | 27 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page. 28 | -------------------------------------------------------------------------------- /packages/angular/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | jasmine: { 17 | // you can add configuration options for Jasmine here 18 | // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html 19 | // for example, you can disable the random execution with `random: false` 20 | // or set a specific seed with `seed: 4321` 21 | }, 22 | clearContext: false // leave Jasmine Spec Runner output visible in browser 23 | }, 24 | jasmineHtmlReporter: { 25 | suppressAll: true // removes the duplicated traces 26 | }, 27 | coverageReporter: { 28 | dir: require('path').join(__dirname, './coverage/angular-project'), 29 | subdir: '.', 30 | reporters: [ 31 | { type: 'html' }, 32 | { type: 'text-summary' } 33 | ] 34 | }, 35 | reporters: ['progress', 'kjhtml'], 36 | port: 9876, 37 | colors: true, 38 | logLevel: config.LOG_INFO, 39 | autoWatch: true, 40 | browsers: ['Chrome'], 41 | singleRun: false, 42 | restartOnFileChange: true 43 | }); 44 | }; 45 | -------------------------------------------------------------------------------- /packages/angular/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-project", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "ng serve", 7 | "build": "ng build angular", 8 | "watch": "ng build --watch --configuration development", 9 | "test": "ng test" 10 | }, 11 | "lernaHelperPublishPath": "./dist/angular", 12 | "private": true, 13 | "dependencies": { 14 | "@angular/animations": "^14.1.0", 15 | "@angular/common": "^14.1.0", 16 | "@angular/compiler": "^14.1.0", 17 | "@angular/core": "^14.1.0", 18 | "@angular/forms": "^14.1.0", 19 | "@angular/platform-browser": "^14.1.0", 20 | "@angular/platform-browser-dynamic": "^14.1.0", 21 | "@angular/router": "^14.1.0", 22 | "rxjs": "~7.5.0", 23 | "tslib": "^2.3.0", 24 | "zone.js": "~0.11.4" 25 | }, 26 | "devDependencies": { 27 | "@angular-devkit/build-angular": "^14.1.0", 28 | "@angular/cli": "~14.1.0", 29 | "@angular/compiler-cli": "^14.1.0", 30 | "@types/jasmine": "~4.0.0", 31 | "jasmine-core": "~4.2.0", 32 | "karma": "~6.4.0", 33 | "karma-chrome-launcher": "~3.1.0", 34 | "karma-coverage": "~2.2.0", 35 | "karma-jasmine": "~5.1.0", 36 | "karma-jasmine-html-reporter": "~2.0.0", 37 | "ng-packagr": "^14.1.0", 38 | "typescript": "~4.7.2" 39 | } 40 | } -------------------------------------------------------------------------------- /packages/angular/projects/angular/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## 0.1.0 (2023-02-24) 7 | 8 | 9 | ### :rocket: New Features 10 | 11 | * v0.1 ([c33732a](https://github.com/naver/cfcs/commit/c33732a6bc7fba6e3e5746ecc5b3d94df582b39e)) 12 | 13 | 14 | ### :memo: Documentation 15 | 16 | * fix README ([6d479bd](https://github.com/naver/cfcs/commit/6d479bda55fbcbdd10dd7d90595070003e9d0f22)) 17 | 18 | 19 | ### :mega: Other 20 | 21 | * fix packages' url ([846fcf2](https://github.com/naver/cfcs/commit/846fcf2a666f3085778d9e8b7f8f533a32c4eb4c)) 22 | * update packages versions ([ae79c40](https://github.com/naver/cfcs/commit/ae79c402ac11c307f78a1371689e3e4d0eaa7f20)) 23 | -------------------------------------------------------------------------------- /packages/angular/projects/angular/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2022-present NAVER Corp. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /packages/angular/projects/angular/README.md: -------------------------------------------------------------------------------- 1 | # Angular 2 | 3 | This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 14.1.0. 4 | 5 | ## Code scaffolding 6 | 7 | Run `ng generate component component-name --project angular` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project angular`. 8 | > Note: Don't forget to add `--project angular` or else it will be added to the default project in your `angular.json` file. 9 | 10 | ## Build 11 | 12 | Run `ng build angular` to build the project. The build artifacts will be stored in the `dist/` directory. 13 | 14 | ## Publishing 15 | 16 | After building your library with `ng build angular`, go to the dist folder `cd dist/angular` and run `npm publish`. 17 | 18 | ## Running unit tests 19 | 20 | Run `ng test angular` to execute the unit tests via [Karma](https://karma-runner.github.io). 21 | 22 | ## Further help 23 | 24 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page. 25 | -------------------------------------------------------------------------------- /packages/angular/projects/angular/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | jasmine: { 17 | // you can add configuration options for Jasmine here 18 | // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html 19 | // for example, you can disable the random execution with `random: false` 20 | // or set a specific seed with `seed: 4321` 21 | }, 22 | clearContext: false // leave Jasmine Spec Runner output visible in browser 23 | }, 24 | jasmineHtmlReporter: { 25 | suppressAll: true // removes the duplicated traces 26 | }, 27 | coverageReporter: { 28 | dir: require('path').join(__dirname, '../../coverage/angular'), 29 | subdir: '.', 30 | reporters: [ 31 | { type: 'html' }, 32 | { type: 'text-summary' } 33 | ] 34 | }, 35 | reporters: ['progress', 'kjhtml'], 36 | port: 9876, 37 | colors: true, 38 | logLevel: config.LOG_INFO, 39 | autoWatch: true, 40 | browsers: ['Chrome'], 41 | singleRun: false, 42 | restartOnFileChange: true 43 | }); 44 | }; 45 | -------------------------------------------------------------------------------- /packages/angular/projects/angular/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", 3 | "dest": "../../dist/angular", 4 | "lib": { 5 | "entryFile": "src/public-api.ts" 6 | }, 7 | "allowedNonPeerDependencies": [ 8 | "@cfcs/core" 9 | ] 10 | } -------------------------------------------------------------------------------- /packages/angular/projects/angular/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@cfcs/angular", 3 | "version": "0.1.1", 4 | "description": "Write once, create framework components that supports React, Vue, Svelte, and more.", 5 | "author": { 6 | "name": "NAVER Corp." 7 | }, 8 | "license": "MIT", 9 | "homepage": "https://naver.github.io/cfcs", 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/naver/cfcs/tree/main/packages/angular" 13 | }, 14 | "keywords": [ 15 | "cfcs", 16 | "cfc", 17 | "angular", 18 | "ngx", 19 | "reactive", 20 | "hooks", 21 | "directive" 22 | ], 23 | "dependencies": { 24 | "@cfcs/core": "~0.1.0", 25 | "tslib": "^2.3.0" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /packages/angular/projects/angular/src/public-api.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * cfcs 3 | * Copyright (c) 2022-present NAVER Corp. 4 | * MIT license 5 | */ 6 | export * from './types'; 7 | export * from './useReactive'; 8 | -------------------------------------------------------------------------------- /packages/angular/projects/angular/src/types.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * cfcs 3 | * Copyright (c) 2022-present NAVER Corp. 4 | * MIT license 5 | */ 6 | import { EventEmitter } from "@angular/core"; 7 | import { ReactiveAdapterParam, ReactiveEventParameters, ReactiveState, ReactiveSubscribe } from "@cfcs/core"; 8 | 9 | type WithPrefix = Prefix extends "" ? Name : `${Prefix}${Capitalize}`; 10 | 11 | export type AngularEventsOutputs< 12 | Events extends readonly any[], 13 | PropertyPrefix extends string = "", 14 | EventPrefix extends string = "", 15 | > = Events extends readonly [infer Name, ...infer Args] ? [`${WithPrefix}: ${WithPrefix}`, ...AngularEventsOutputs] : []; 16 | 17 | export type AngularBindingProperties< 18 | Events extends Record, 19 | PropertyPrefix extends string = "", 20 | > = { 21 | [Key in keyof Events as WithPrefix]: Events[Key]; 22 | }; 23 | 24 | 25 | export type ReactiveEvents< 26 | Events extends Record 27 | > = { 28 | [Key in keyof Events]: EventEmitter[0]>; 29 | }; 30 | 31 | export type ReactiveResult< 32 | Instance extends ReactiveSubscribe>, 33 | State extends Record = ReactiveState, 34 | Methods extends keyof Partial = any, 35 | Events extends Record = {}, 36 | > = State & { [key in Methods]: Instance[key] } & ReactiveEvents; 37 | 38 | export type ReactiveAdapterResult< 39 | Adapter extends ReactiveAdapterParam, 40 | PropertyPrefix extends string = "", 41 | > 42 | = Adapter extends ReactiveAdapterParam 43 | ? ReactiveResult> : {}; 44 | 45 | 46 | 47 | // Names using framework prefix 48 | export type AngularReactiveEvents> = ReactiveEvents; 49 | export type AngularReactiveResult< 50 | Instance extends ReactiveSubscribe>, 51 | State extends Record = ReactiveState, 52 | Methods extends keyof Partial = any, 53 | Events extends Record = {}, 54 | > = ReactiveResult< 55 | Instance, 56 | State, 57 | Methods, 58 | Events 59 | >; 60 | export type AngularReactiveAdapterResult< 61 | Adapter extends ReactiveAdapterParam 62 | > = ReactiveAdapterResult; 63 | -------------------------------------------------------------------------------- /packages/angular/projects/angular/src/useReactive.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * cfcs 3 | * Copyright (c) 2022-present NAVER Corp. 4 | * MIT license 5 | */ 6 | import { EventEmitter } from "@angular/core"; 7 | import { ReactiveSubscribe, ReactiveAdapterParam, camelize, adaptReactive, ReactiveState } from "@cfcs/core"; 8 | 9 | export function useReactive< 10 | Instance extends ReactiveSubscribe>, 11 | State extends Record = ReactiveState, 12 | Methods extends keyof Partial = any, 13 | Props = any, 14 | Events extends Record = {}, 15 | >(self: State, reactiveAdapter: ReactiveAdapterParam, props?: () => Props) { 16 | const adaptResult = adaptReactive(reactiveAdapter, props); 17 | const reactiveState = adaptResult.state(); 18 | const names = Object.keys(reactiveState); 19 | const events = adaptResult.events(); 20 | const methods = adaptResult.methods(); 21 | 22 | for (const name in reactiveState) { 23 | (self as any)[name] = reactiveState[name]; 24 | } 25 | for (const name in methods) { 26 | (self as any)[name] = methods[name]; 27 | } 28 | 29 | events.forEach(name => { 30 | (self as any)[camelize(`ngx ${name as string}`)] = new EventEmitter(); 31 | }); 32 | let callbacks: Array<(...args: any[]) => void> = []; 33 | 34 | function mounted() { 35 | adaptResult.mounted(); 36 | const inst = adaptResult.instance(); 37 | 38 | names.forEach((name) => { 39 | inst.subscribe(name as any, (value: any) => { 40 | setTimeout(() => { 41 | (self as any)[name] = value; 42 | }); 43 | }); 44 | }); 45 | 46 | callbacks = events.map(name => { 47 | const callback = (...args: any[]) => { 48 | ((self as any)[camelize(`ngx ${name as string}`)] as EventEmitter).emit(...args); 49 | }; 50 | 51 | adaptResult.on(name as any, callback); 52 | return callback; 53 | }); 54 | adaptResult.init(); 55 | } 56 | function destroy() { 57 | events.forEach((name, i) => { 58 | adaptResult.off(name as any, callbacks[i]); 59 | }); 60 | 61 | adaptResult.destroy(); 62 | } 63 | 64 | return { 65 | mounted, 66 | destroy, 67 | }; 68 | } 69 | -------------------------------------------------------------------------------- /packages/angular/projects/angular/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "../../tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "../../out-tsc/lib", 6 | "declaration": true, 7 | "declarationMap": true, 8 | "inlineSources": true, 9 | "types": [] 10 | }, 11 | "exclude": [ 12 | "src/test.ts", 13 | "**/*.spec.ts" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /packages/angular/projects/angular/tsconfig.lib.prod.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.lib.json", 4 | "compilerOptions": { 5 | "declarationMap": false 6 | }, 7 | "angularCompilerOptions": { 8 | "compilationMode": "partial" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/angular/projects/angular/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "../../tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "../../out-tsc/spec", 6 | "types": [ 7 | "jasmine" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts" 12 | ], 13 | "include": [ 14 | "**/*.spec.ts", 15 | "**/*.d.ts" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /packages/angular/src/app/app.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naver/cfcs/798b4fb2220cf0562313d2f87c36d7173cd070e1/packages/angular/src/app/app.component.css -------------------------------------------------------------------------------- /packages/angular/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | import { AppComponent } from './app.component'; 3 | 4 | describe('AppComponent', () => { 5 | beforeEach(async () => { 6 | await TestBed.configureTestingModule({ 7 | declarations: [ 8 | AppComponent 9 | ], 10 | }).compileComponents(); 11 | }); 12 | 13 | it('should create the app', () => { 14 | const fixture = TestBed.createComponent(AppComponent); 15 | const app = fixture.componentInstance; 16 | expect(app).toBeTruthy(); 17 | }); 18 | 19 | it(`should have as title 'angular-project'`, () => { 20 | const fixture = TestBed.createComponent(AppComponent); 21 | const app = fixture.componentInstance; 22 | expect(app.title).toEqual('angular-project'); 23 | }); 24 | 25 | it('should render title', () => { 26 | const fixture = TestBed.createComponent(AppComponent); 27 | fixture.detectChanges(); 28 | const compiled = fixture.nativeElement as HTMLElement; 29 | expect(compiled.querySelector('.content span')?.textContent).toContain('angular-project app is running!'); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /packages/angular/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-root', 5 | templateUrl: './app.component.html', 6 | styleUrls: ['./app.component.css'] 7 | }) 8 | export class AppComponent { 9 | title = 'angular-project'; 10 | } 11 | -------------------------------------------------------------------------------- /packages/angular/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { BrowserModule } from '@angular/platform-browser'; 3 | 4 | import { AppComponent } from './app.component'; 5 | 6 | @NgModule({ 7 | declarations: [ 8 | AppComponent 9 | ], 10 | imports: [ 11 | BrowserModule 12 | ], 13 | providers: [], 14 | bootstrap: [AppComponent] 15 | }) 16 | export class AppModule { } 17 | -------------------------------------------------------------------------------- /packages/angular/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naver/cfcs/798b4fb2220cf0562313d2f87c36d7173cd070e1/packages/angular/src/assets/.gitkeep -------------------------------------------------------------------------------- /packages/angular/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /packages/angular/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false 7 | }; 8 | 9 | /* 10 | * For easier debugging in development mode, you can import the following file 11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. 12 | * 13 | * This import should be commented out in production mode because it will have a negative impact 14 | * on performance if an error is thrown. 15 | */ 16 | // import 'zone.js/plugins/zone-error'; // Included with Angular CLI. 17 | -------------------------------------------------------------------------------- /packages/angular/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naver/cfcs/798b4fb2220cf0562313d2f87c36d7173cd070e1/packages/angular/src/favicon.ico -------------------------------------------------------------------------------- /packages/angular/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AngularProject 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /packages/angular/src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic().bootstrapModule(AppModule) 12 | .catch(err => console.error(err)); 13 | -------------------------------------------------------------------------------- /packages/angular/src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes recent versions of Safari, Chrome (including 12 | * Opera), Edge on the desktop, and iOS and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/guide/browser-support 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** 22 | * By default, zone.js will patch all possible macroTask and DomEvents 23 | * user can disable parts of macroTask/DomEvents patch by setting following flags 24 | * because those flags need to be set before `zone.js` being loaded, and webpack 25 | * will put import in the top of bundle, so user need to create a separate file 26 | * in this directory (for example: zone-flags.ts), and put the following flags 27 | * into that file, and then add the following code before importing zone.js. 28 | * import './zone-flags'; 29 | * 30 | * The flags allowed in zone-flags.ts are listed here. 31 | * 32 | * The following flags will work for all browsers. 33 | * 34 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 35 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 36 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 37 | * 38 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 39 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 40 | * 41 | * (window as any).__Zone_enable_cross_context_check = true; 42 | * 43 | */ 44 | 45 | /*************************************************************************************************** 46 | * Zone JS is required by default for Angular itself. 47 | */ 48 | import 'zone.js'; // Included with Angular CLI. 49 | 50 | 51 | /*************************************************************************************************** 52 | * APPLICATION IMPORTS 53 | */ 54 | -------------------------------------------------------------------------------- /packages/angular/src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | -------------------------------------------------------------------------------- /packages/angular/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: { 11 | context(path: string, deep?: boolean, filter?: RegExp): { 12 | (id: string): T; 13 | keys(): string[]; 14 | }; 15 | }; 16 | 17 | // First, initialize the Angular testing environment. 18 | getTestBed().initTestEnvironment( 19 | BrowserDynamicTestingModule, 20 | platformBrowserDynamicTesting(), 21 | ); 22 | 23 | // Then we find all the tests. 24 | const context = require.context('./', true, /\.spec\.ts$/); 25 | // And load the modules. 26 | context.keys().forEach(context); 27 | -------------------------------------------------------------------------------- /packages/angular/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/app", 6 | "types": [] 7 | }, 8 | "files": [ 9 | "src/main.ts", 10 | "src/polyfills.ts" 11 | ], 12 | "include": [ 13 | "src/**/*.d.ts" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /packages/angular/tsconfig.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "compileOnSave": false, 4 | "compilerOptions": { 5 | "paths": { 6 | "angular": [ 7 | "dist/angular" 8 | ] 9 | }, 10 | "baseUrl": "./", 11 | "outDir": "./dist/out-tsc", 12 | "forceConsistentCasingInFileNames": true, 13 | "strict": true, 14 | "noImplicitOverride": true, 15 | "noPropertyAccessFromIndexSignature": true, 16 | "noImplicitReturns": true, 17 | "noFallthroughCasesInSwitch": true, 18 | "sourceMap": true, 19 | "declaration": false, 20 | "downlevelIteration": true, 21 | "experimentalDecorators": true, 22 | "moduleResolution": "node", 23 | "importHelpers": true, 24 | "target": "es2020", 25 | "module": "es2020", 26 | "lib": [ 27 | "es2020", 28 | "dom" 29 | ] 30 | }, 31 | "angularCompilerOptions": { 32 | "enableI18nLegacyMessageIdFormat": false, 33 | "strictInjectionParameters": true, 34 | "strictInputAccessModifiers": true, 35 | "strictTemplates": true 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /packages/angular/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/spec", 6 | "types": [ 7 | "jasmine" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts", 12 | "src/polyfills.ts" 13 | ], 14 | "include": [ 15 | "src/**/*.spec.ts", 16 | "src/**/*.d.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /packages/cli/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2022-present NAVER Corp. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /packages/cli/README.md: -------------------------------------------------------------------------------- 1 | ## CFCs CLI Test 2 | -------------------------------------------------------------------------------- /packages/cli/index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 'use strict'; 3 | 4 | const { program } = require('commander'); 5 | 6 | // cfcs lerna update 7 | program 8 | .command("lerna", "") 9 | .argument('', 'Lerna를 위한 CFCs 서비스(테스트)') 10 | .action(service => { 11 | import("./dist/lerna.js").then(lerna => { 12 | lerna.lernaUpdate(); 13 | }); 14 | }); 15 | 16 | program.parse(); 17 | -------------------------------------------------------------------------------- /packages/cli/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@cfcs/cli", 3 | "version": "0.0.3", 4 | "private": true, 5 | "description": "Cross Framework Components CLI", 6 | "main": "dist/cli.cjs.js", 7 | "module": "dist/cli.esm.js", 8 | "types": "declaration/index.d.ts", 9 | "sideEffects": false, 10 | "scripts": { 11 | "start": "react-scripts start", 12 | "build": "tsc -p tsconfig.json && print-sizes ./dist" 13 | }, 14 | "license": "MIT", 15 | "homepage": "https://naver.github.io/cfcs", 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/naver/cfcs" 19 | }, 20 | "author": { 21 | "name": "NAVER Corp." 22 | }, 23 | "bin": { 24 | "cfcs": "./index.js" 25 | }, 26 | "keywords": [ 27 | "cfc", 28 | "cfcs", 29 | "react-hook", 30 | "react-hooks", 31 | "react-use", 32 | "reactive", 33 | "hooks" 34 | ], 35 | "files": [ 36 | "./*", 37 | "dist/*" 38 | ], 39 | "devDependencies": { 40 | "@egjs/build-helper": "^0.1.2", 41 | "@types/lerna__package": "^4.0.0", 42 | "@types/lerna__project": "^4.0.0", 43 | "lerna": "^4.0.0", 44 | "typescript": "^4.7.4" 45 | }, 46 | "dependencies": { 47 | "commander": "^9.4.1", 48 | "sync-exec": "^0.6.2" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /packages/cli/rollup.config.js: -------------------------------------------------------------------------------- 1 | const buildHelper = require("@egjs/build-helper"); 2 | 3 | export default buildHelper([ 4 | { 5 | input: "./src/lerna.ts", 6 | output: "./dist/lerna.cjs.js", 7 | format: "cjs", 8 | exports: "named", 9 | }, 10 | { 11 | input: "./src/lerna.ts", 12 | output: "./dist/lerna.esm.js", 13 | format: "esm", 14 | exports: "named", 15 | }, 16 | ]); 17 | 18 | -------------------------------------------------------------------------------- /packages/cli/src/lerna.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * cfcs 3 | * Copyright (c) 2022-present NAVER Corp. 4 | * MIT license 5 | */ 6 | import { Project } from "@lerna/project"; 7 | import { Package } from "@lerna/package"; 8 | 9 | const exec = require('sync-exec'); 10 | 11 | let project!: Project; 12 | let rootPackage!: Package; 13 | let packages!: Package[]; 14 | 15 | function syncExec(command: string) { 16 | const result = exec(command); 17 | 18 | if (result.stderr) { 19 | console.error(result.stderr); 20 | } else if (result.stdout) { 21 | console.log(result.stdout); 22 | } 23 | } 24 | 25 | export function getProject() { 26 | if (!project) { 27 | project = new Project(); 28 | } 29 | 30 | return project; 31 | } 32 | export function getRootPackage() { 33 | if (!rootPackage) { 34 | rootPackage = Package.lazy("./"); 35 | } 36 | return rootPackage; 37 | } 38 | 39 | export function getPackages() { 40 | if (!packages) { 41 | packages = getProject().getPackagesSync(); 42 | } 43 | return packages; 44 | }; 45 | 46 | export function lernaUpdate() { 47 | const useWorkspaces = getProject().config.useWorkspaces; 48 | 49 | getPackages().forEach(async pkg => { 50 | const dependencies = pkg.dependencies || {}; 51 | const devDependencies = pkg.devDependencies || {}; 52 | const updatedDependencies: string[] = []; 53 | const updatedDevDependencies: string[] = []; 54 | 55 | Object.keys(dependencies).forEach(moduleName => { 56 | if (moduleName.startsWith("@cfcs/")) { 57 | updatedDependencies.push(moduleName); 58 | } 59 | }); 60 | Object.keys(devDependencies).forEach(moduleName => { 61 | if (moduleName.startsWith("@cfcs/")) { 62 | updatedDevDependencies.push(moduleName); 63 | } 64 | }); 65 | 66 | const moduleArgs = updatedDependencies.map(name => { 67 | return `${name}@latest`; 68 | }).join(" "); 69 | const devModuleArgs = updatedDevDependencies.map(name => { 70 | return `${name}@latest`; 71 | }).join(" "); 72 | if (moduleArgs || devModuleArgs) { 73 | console.log(`Update the CFCs modules of ${pkg.name}`); 74 | } else { 75 | return; 76 | } 77 | if (useWorkspaces) { 78 | if (moduleArgs) { 79 | syncExec(`yarn workspace ${pkg.name} add ${moduleArgs}`); 80 | } 81 | if (devModuleArgs) { 82 | syncExec(`yarn workspace ${pkg.name} add ${devModuleArgs} -D`); 83 | } 84 | } else { 85 | if (moduleArgs) { 86 | syncExec(`npm install ${moduleArgs} --prefix ${pkg.location}`); 87 | } 88 | if (devModuleArgs) { 89 | syncExec(`npm install ${devModuleArgs} -D --prefix ${pkg.location}`); 90 | } 91 | } 92 | }); 93 | } 94 | -------------------------------------------------------------------------------- /packages/cli/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": false, 12 | "allowSyntheticDefaultImports": false, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "noFallthroughCasesInSwitch": true, 16 | "module": "CommonJS", 17 | "moduleResolution": "node", 18 | "resolveJsonModule": true, 19 | "isolatedModules": true, 20 | "outDir": "./dist", 21 | "sourceMap": true, 22 | }, 23 | "include": [ 24 | "src" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /packages/core/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## 0.1.0 (2023-02-24) 7 | 8 | 9 | ### :rocket: New Features 10 | 11 | * v0.1 ([c33732a](https://github.com/naver/cfcs/commit/c33732a6bc7fba6e3e5746ecc5b3d94df582b39e)) 12 | 13 | 14 | ### :memo: Documentation 15 | 16 | * fix README ([6d479bd](https://github.com/naver/cfcs/commit/6d479bda55fbcbdd10dd7d90595070003e9d0f22)) 17 | 18 | 19 | ### :mega: Other 20 | 21 | * fix packages' url ([846fcf2](https://github.com/naver/cfcs/commit/846fcf2a666f3085778d9e8b7f8f533a32c4eb4c)) 22 | * update packages versions ([ae79c40](https://github.com/naver/cfcs/commit/ae79c402ac11c307f78a1371689e3e4d0eaa7f20)) 23 | -------------------------------------------------------------------------------- /packages/core/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2022-present NAVER Corp. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /packages/core/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'ts-jest', 3 | testEnvironment: 'node', 4 | roots: [ 5 | "", 6 | ], 7 | transform: { 8 | "^.+\\.tsx?$": "ts-jest", 9 | }, 10 | testMatch: ["/test/**/*.spec.ts"], 11 | // "testRegex": "spec\\.ts$", 12 | moduleFileExtensions: [ 13 | "ts", 14 | "tsx", 15 | "js", 16 | "jsx", 17 | "json", 18 | "node", 19 | ], 20 | transformIgnorePatterns: ['/node_modules/'], 21 | }; 22 | -------------------------------------------------------------------------------- /packages/core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@cfcs/core", 3 | "version": "0.1.0", 4 | "description": "Write once, create framework components that supports React, Vue, Svelte, and more.", 5 | "main": "dist/cfcs.cjs.js", 6 | "module": "dist/cfcs.esm.js", 7 | "types": "declaration/index.d.ts", 8 | "scripts": { 9 | "start": "rollup -c -w", 10 | "build": "rm -rf ./declaration && rollup -c && npm run declaration", 11 | "declaration": "rm -rf declaration && tsc -p tsconfig.declaration.json", 12 | "test": "jest --watchAll", 13 | "lint": "eslint ./src/ --ext .ts" 14 | }, 15 | "homepage": "https://naver.github.io/cfcs", 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/naver/cfcs/tree/main/packages/core" 19 | }, 20 | "author": "NAVER Crop.", 21 | "license": "MIT", 22 | "keywords": [ 23 | "cfc", 24 | "cfcs", 25 | "react", 26 | "angular", 27 | "svelte", 28 | "vue", 29 | "vue2", 30 | "vue3" 31 | ], 32 | "files": [ 33 | "./*", 34 | "dist/*" 35 | ], 36 | "dependencies": { 37 | "@egjs/component": "^3.0.4" 38 | }, 39 | "devDependencies": { 40 | "@egjs/build-helper": "^0.1.2", 41 | "@types/sinon": "^10.0.13", 42 | "jest": "^29.3.1", 43 | "sinon": "^15.0.0", 44 | "ts-jest": "^29.0.3", 45 | "typescript": "^4.7.4" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /packages/core/rollup.config.js: -------------------------------------------------------------------------------- 1 | 2 | const buildHelper = require("@egjs/build-helper"); 3 | const name = "cfcs"; 4 | 5 | export default buildHelper([ 6 | { 7 | name, 8 | input: "./src/index.umd.ts", 9 | output: "./dist/cfcs.js", 10 | format: "umd", 11 | resolve: true, 12 | }, 13 | { 14 | name, 15 | input: "./src/index.umd.ts", 16 | output: "./dist/cfcs.min.js", 17 | format: "umd", 18 | uglify: true, 19 | resolve: true, 20 | }, 21 | { 22 | input: "./src/index.ts", 23 | output: "./dist/cfcs.cjs.js", 24 | format: "cjs", 25 | exports: "named", 26 | }, 27 | { 28 | input: "./src/index.ts", 29 | output: "./dist/cfcs.esm.js", 30 | format: "esm", 31 | exports: "named", 32 | }, 33 | ]); 34 | 35 | -------------------------------------------------------------------------------- /packages/core/src/core/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * cfcs 3 | * Copyright (c) 2022-present NAVER Corp. 4 | * MIT license 5 | */ 6 | export * from "./utils"; 7 | export * from "./types"; 8 | -------------------------------------------------------------------------------- /packages/core/src/core/types.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * cfcs 3 | * Copyright (c) 2022-present NAVER Corp. 4 | * MIT license 5 | */ 6 | 7 | /** 8 | * Reference object type 9 | * @category Common 10 | */ 11 | export interface Ref { 12 | current?: T | undefined | null; 13 | value?: T | undefined | null; 14 | } 15 | 16 | /** 17 | * @hidden 18 | */ 19 | export type ExtractNever = Pick; 20 | -------------------------------------------------------------------------------- /packages/core/src/core/utils.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * cfcs 3 | * Copyright (c) 2022-present NAVER Corp. 4 | * MIT license 5 | */ 6 | 7 | 8 | /** 9 | * @hidden 10 | */ 11 | export function keys>(obj: T): Array { 12 | return Object.keys(obj); 13 | } 14 | 15 | /** 16 | * @hidden 17 | */ 18 | export function camelize(str: string) { 19 | return str.replace(/[\s-_]([a-z])/g, (all, letter) => letter.toUpperCase()); 20 | } 21 | 22 | /** 23 | * @hidden 24 | */ 25 | export function isString(val: any): val is string { 26 | return typeof val === "string"; 27 | } 28 | 29 | /** 30 | * @hidden 31 | */ 32 | export function isObject(val: any): val is object { 33 | return typeof val === "object"; 34 | } 35 | 36 | /** 37 | * @hidden 38 | */ 39 | export function isFunction(val: any): val is Function { 40 | return typeof val === "function"; 41 | } 42 | -------------------------------------------------------------------------------- /packages/core/src/dom/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * cfcs 3 | * Copyright (c) 2022-present NAVER Corp. 4 | * MIT license 5 | */ 6 | export * from "./utils"; 7 | -------------------------------------------------------------------------------- /packages/core/src/dom/utils.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * cfcs 3 | * Copyright (c) 2022-present NAVER Corp. 4 | * MIT license 5 | */ 6 | import { isString, Ref } from "../core"; 7 | 8 | /** 9 | * @hidden 10 | */ 11 | export function findTarget(target: string | Target | Ref | null): Target | null { 12 | let el!: Target; 13 | 14 | if (!target) { 15 | return null; 16 | } if (isString(target)) { 17 | el = document.querySelector(target)!; 18 | } else if (target instanceof Element) { 19 | el = target; 20 | } else if ("value" in target || "current" in target) { 21 | el = target.value! || target.current!; 22 | } 23 | 24 | return el; 25 | } 26 | 27 | /** 28 | * @description Sets the name of the class method to be exposed to the outside. 29 | * @category DOM 30 | * @return Property Decorator 31 | * @example 32 | * ```ts 33 | * import { withClassMethods } from "@cfcs/core"; 34 | * 35 | * class YourFrameworkComponent { 36 | * @withClassMethod(METHOD_NAMES) 37 | * inst = new YourComponent(); 38 | * } 39 | * ``` 40 | */ 41 | export function withClassMethods(methods: readonly string[]) { 42 | return function (prototype: any, memberName: string) { 43 | methods.forEach((name: string) => { 44 | if (name in prototype) { 45 | return; 46 | } 47 | prototype[name] = function (...args) { 48 | const result = this[memberName][name](...args); 49 | 50 | // fix `this` type to return your own `class` instance to the instance using the decorator. 51 | if (result === this[memberName]) { 52 | return this; 53 | } else { 54 | return result; 55 | } 56 | }; 57 | }); 58 | }; 59 | } 60 | -------------------------------------------------------------------------------- /packages/core/src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * cfcs 3 | * Copyright (c) 2022-present NAVER Corp. 4 | * MIT license 5 | */ 6 | export * from "./core"; 7 | export * from "./dom"; 8 | export * from "./reactive"; 9 | -------------------------------------------------------------------------------- /packages/core/src/index.umd.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * cfcs 3 | * Copyright (c) 2022-present NAVER Corp. 4 | * MIT license 5 | */ 6 | import * as modules from "./index"; 7 | 8 | // co 9 | // for (const name in modules) { 10 | // (cfcs as any)[name] = (modules as any)[name]; 11 | // } 12 | 13 | export default modules; 14 | -------------------------------------------------------------------------------- /packages/core/src/reactive/ComputedObserver.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * cfcs 3 | * Copyright (c) 2022-present NAVER Corp. 4 | * MIT license 5 | */ 6 | import { detectDependencies, endDetectDependencies } from "./detectDependencies"; 7 | import { Observer } from "./Observer"; 8 | 9 | /** 10 | * @category Reactive 11 | * @hidden 12 | */ 13 | export class ComputedObserver extends Observer { 14 | private _registered: Array> = []; 15 | /** 16 | * @description Creates a new computed observer from the values of other observers. 17 | * It is read-only and if you change the value of the observer used inside the callback, its value will be automatically updated. 18 | * @param _computedCallback A function for observers to be computed. 19 | */ 20 | constructor(private _computedCallback: () => T) { 21 | super(); 22 | 23 | this._current = this.current; 24 | } 25 | 26 | get current() { 27 | detectDependencies(this); 28 | const value = this._computedCallback(); 29 | const results = endDetectDependencies()!; 30 | 31 | this._registered.forEach(observer => { 32 | observer.unsubscribe(this._onCheckUpdate); 33 | }); 34 | results.observers.forEach(observer => { 35 | observer.subscribe(this._onCheckUpdate); 36 | }); 37 | this._registered = results.observers; 38 | 39 | return value; 40 | } 41 | 42 | private _onCheckUpdate = () => { 43 | this._setCurrent(this.current); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /packages/core/src/reactive/Observer.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * cfcs 3 | * Copyright (c) 2022-present NAVER Corp. 4 | * MIT license 5 | */ 6 | import Component from "@egjs/component"; 7 | import { getCurrentDetected } from "./detectDependencies"; 8 | 9 | interface EmitterEvents { 10 | update: (value: Value, prevValue: Value) => void; 11 | } 12 | 13 | /** 14 | * Creates a mutable ref object. You can access the `.current` value and detect the value change through `.subscribe`. 15 | * @category Reactive 16 | * @see observe 17 | */ 18 | export class Observer { 19 | protected _current: Value; 20 | protected _emitter = new Component>(); 21 | /** 22 | * 23 | */ 24 | constructor(value?: Value) { 25 | this._current = value as any; 26 | } 27 | /** 28 | * return the current value. 29 | */ 30 | public get current(): Value { 31 | const currentDetected = getCurrentDetected(); 32 | 33 | currentDetected?.push(this); 34 | return this._current as Value; 35 | } 36 | public set current(value: Value) { 37 | this._setCurrent(value); 38 | } 39 | /** 40 | * When the current value changes, the callback function is called. 41 | */ 42 | public subscribe(callback: (value: Value, prevValue: Value) => void) { 43 | this.current; 44 | this._emitter.on("update", callback); 45 | return this; 46 | } 47 | /** 48 | * Cancel the registered subscription through callback. 49 | */ 50 | public unsubscribe(callback?: (value: Value, prevValue: Value) => void) { 51 | this._emitter.off("update", callback); 52 | return this; 53 | } 54 | protected _setCurrent(value: Value) { 55 | const prevValue = this._current; 56 | const isUpdate = value !== prevValue; 57 | 58 | this._current = value; 59 | 60 | if (isUpdate) { 61 | this._emitter.trigger("update", value, prevValue); 62 | } 63 | } 64 | /** 65 | * @hidden 66 | */ 67 | public toString() { 68 | return `${this.current}`; 69 | } 70 | /** 71 | * @hidden 72 | */ 73 | public valueOf() { 74 | return this.current; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /packages/core/src/reactive/const.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * cfcs 3 | * Copyright (c) 2022-present NAVER Corp. 4 | * MIT license 5 | */ 6 | export const OBSERVERS_PATH = "__observers__"; 7 | export const COMPUTED_PATH = "__computed__"; 8 | export const CFCS_DETECTED_DEPENDENCIES_VERSION = 1; 9 | export const CFCS_DETECTED_DEPENDENCIES = "__CFCS_DETECTED_DEPENDENCIES__"; 10 | -------------------------------------------------------------------------------- /packages/core/src/reactive/decorators/Computed.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * cfcs 3 | * Copyright (c) 2022-present NAVER Corp. 4 | * MIT license 5 | */ 6 | import { COMPUTED_PATH } from "../const"; 7 | import { computed } from "../inline"; 8 | import { getObserver, getObservers } from "../utils"; 9 | 10 | 11 | /** 12 | * @description `Computed` is a property decorator. 13 | * Changes in computed state values are also recognized according to changes in observers used within the getter function. 14 | * You can detect its status through `.subscribe`. 15 | * @hidden 16 | * @category Reactive-Decorator 17 | * @see ReactiveSubscribe 18 | * @example 19 | * ```ts 20 | const ob1 = observe(0); 21 | const ob2 = observe(1); 22 | 23 | // When 24 | @ReactiveSubscribe 25 | class TestComputed { 26 | @Computed 27 | get ob3() { 28 | return ob1.current + ob2.current; 29 | } 30 | } 31 | const inst = new TestComputed(); 32 | 33 | inst.subscribe("ob3", ob3 => { 34 | console.log(ob3); 35 | }); 36 | 37 | ob1.current = 1; 38 | ``` 39 | */ 40 | export function Computed(prototype: any, memberName: string, attributes: PropertyDescriptor): PropertyDescriptor { 41 | const get = attributes.get!; 42 | 43 | function getComputed() { 44 | const observers = getObservers(this, true); 45 | 46 | if (!(memberName in observers)) { 47 | observers[memberName] = computed(get.bind(this)); 48 | } 49 | return getObserver(this, memberName).current; 50 | } 51 | const nextAttributes: PropertyDescriptor = { 52 | configurable: true, 53 | get: getComputed, 54 | } 55 | 56 | prototype[COMPUTED_PATH] ||= []; 57 | const computedList = prototype[COMPUTED_PATH]; 58 | 59 | if (computedList.indexOf(memberName) === -1) { 60 | computedList.push(memberName); 61 | } 62 | Object.defineProperty(prototype, memberName, nextAttributes); 63 | 64 | return nextAttributes; 65 | } 66 | -------------------------------------------------------------------------------- /packages/core/src/reactive/decorators/Observe.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * cfcs 3 | * Copyright (c) 2022-present NAVER Corp. 4 | * MIT license 5 | */ 6 | import { getObserver } from "../utils"; 7 | 8 | 9 | function injectObserve(prototype: any, memberName: string, publicName = memberName) { 10 | const nextAttributes: PropertyDescriptor = { 11 | configurable: true, 12 | get: function () { 13 | return getObserver(this, publicName).current; 14 | }, 15 | set: function (value: any) { 16 | getObserver(this, publicName, value).current = value; 17 | }, 18 | }; 19 | Object.defineProperty(prototype, memberName, nextAttributes); 20 | if (publicName !== memberName) { 21 | Object.defineProperty(prototype, publicName, { 22 | configurable: true, 23 | get: function () { 24 | return getObserver(this, publicName).current; 25 | }, 26 | }); 27 | } 28 | } 29 | 30 | export function Observe(protoype: any, memberName: string): void; 31 | export function Observe(name?: string): (protoype: any, memberName: string) => void; 32 | /** 33 | * @description `Observe` is a property decorator and converts the property into a `reactive state`. You can detect its status through `.subscribe`. 34 | * @category Reactive-Decorator 35 | * @see ReactiveSubscribe 36 | * @example 37 | * ```ts 38 | import { ReactiveSubscribe, Observe } from "@cfcs/core"; 39 | 40 | @ReactiveSubscribe 41 | class Component { 42 | // The public name and state name are the same. 43 | @Observe value1 = 1; 44 | // If you want to set public name and private properties separately 45 | @Observe("value2") _value2 = 1; 46 | 47 | constructor() { 48 | requestAnimationFrame(() => { 49 | this.value1 = 2; 50 | }); 51 | } 52 | } 53 | interface C 54 | ``` 55 | */ 56 | export function Observe(...args: any[]) { 57 | if (args.length > 1) { 58 | return injectObserve(args[0], args[1]); 59 | } 60 | 61 | return (prototype: any, memberName: string) => injectObserve(prototype, memberName, args[0]); 62 | } 63 | 64 | 65 | export function Reactive(protoype: any, memberName: string): void; 66 | export function Reactive(name?: string): (protoype: any, memberName: string) => void; 67 | /** 68 | * @hidden 69 | */ 70 | export function Reactive(...args: any[]) { 71 | return Observe(...args); 72 | } 73 | 74 | -------------------------------------------------------------------------------- /packages/core/src/reactive/decorators/ReactiveSubscribe.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * cfcs 3 | * Copyright (c) 2022-present NAVER Corp. 4 | * MIT license 5 | */ 6 | import { keys } from "../../core"; 7 | import { getObserver, getObservers } from "../utils"; 8 | 9 | /** 10 | * @hidden 11 | */ 12 | export function injectReactiveSubscribe(object: Record,) { 13 | object["subscribe"] = function (name: string, callback: (value: any) => void) { 14 | this[name]; 15 | getObserver(this, name).subscribe(callback); 16 | }; 17 | object["unsubscribe"] = function (name?: string, callback?: (value: any) => void) { 18 | if (!name) { 19 | keys(getObservers(this)).forEach((observerName) => { 20 | this.unsubscribe(observerName); 21 | }); 22 | return; 23 | } 24 | if (!(name in this)) { 25 | return; 26 | } 27 | getObserver(this, name).unsubscribe(callback); 28 | }; 29 | } 30 | 31 | 32 | /** 33 | * @description `ReactiveSubscribe` is a class decorator and adds `.subscribe` and `.unsubscribe` methods. 34 | * @category Reactive-Decorator 35 | * @see Observe 36 | * @example 37 | * ```ts 38 | import { ReactiveSubscribe, Observe } from "@cfcs/core"; 39 | 40 | @ReactiveSubscribe 41 | class Component { 42 | @Observe value1 = 1; 43 | 44 | constructor() { 45 | requestAnimationFrame(() => { 46 | this.value1 = 2; 47 | }); 48 | } 49 | } 50 | 51 | interface Component extends ReactiveSubscribe<{ 52 | value1: number; 53 | value2: number; 54 | }> {} 55 | 56 | const component = new Component(); 57 | 58 | // 1 59 | console.log(component.value1); 60 | 61 | component.subscribe("value1", nextValue => { 62 | // When the change event occurs => (2, 2) 63 | console.log(nextValue, component.value2); 64 | }); 65 | ``` 66 | */ 67 | export function ReactiveSubscribe(Constructor: any) { 68 | const prototype = Constructor.prototype; 69 | 70 | injectReactiveSubscribe(prototype); 71 | } 72 | 73 | /** 74 | * `ReactiveSubscribe` is a class decorator and adds `.subscribe` and `.unsubscribe` methods. 75 | * @category Reactive 76 | */ 77 | export interface ReactiveSubscribe> { 78 | /** 79 | * When the value of the property changes, the callback function is called. 80 | */ 81 | subscribe( 82 | name: Name, callback: (value: State[Name]) => void): void; 83 | /** 84 | * Unregister the callback function corresponding to the property. 85 | */ 86 | unsubscribe( 87 | name?: Name, callback?: (value: State[Name]) => void): void; 88 | } 89 | -------------------------------------------------------------------------------- /packages/core/src/reactive/detectDependencies.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * cfcs 3 | * Copyright (c) 2022-present NAVER Corp. 4 | * MIT license 5 | */ 6 | import { 7 | CFCS_DETECTED_DEPENDENCIES, 8 | CFCS_DETECTED_DEPENDENCIES_VERSION, 9 | } from "./const"; 10 | import { Observer } from "./Observer"; 11 | 12 | export interface Detected { 13 | host: Observer; 14 | observers: Array>; 15 | push(observer: Observer): void; 16 | } 17 | 18 | export function getDetectedStack(): Array { 19 | // Version issues do not occur when you access the native object in the global. 20 | (Object as any)[CFCS_DETECTED_DEPENDENCIES] = (Object as any)[CFCS_DETECTED_DEPENDENCIES] || {}; 21 | const versionList = (Object as any)[CFCS_DETECTED_DEPENDENCIES]; 22 | 23 | versionList[CFCS_DETECTED_DEPENDENCIES_VERSION] = versionList[CFCS_DETECTED_DEPENDENCIES_VERSION] || []; 24 | 25 | return versionList[CFCS_DETECTED_DEPENDENCIES_VERSION]; 26 | } 27 | 28 | export function getCurrentDetected(): Detected | undefined { 29 | const stack = getDetectedStack(); 30 | 31 | return stack[stack.length - 1]; 32 | } 33 | 34 | export function detectDependencies(host: Observer) { 35 | const stack = getDetectedStack(); 36 | const observers: Array = []; 37 | const detected: Detected = { 38 | host, 39 | observers, 40 | push(observer: Observer) { 41 | if (host !== observer && observers.indexOf(observer) === -1) { 42 | observers.push(observer); 43 | } 44 | }, 45 | }; 46 | 47 | stack.push(detected); 48 | return detected; 49 | } 50 | 51 | export function endDetectDependencies() { 52 | const stack = getDetectedStack(); 53 | 54 | return stack.pop(); 55 | } 56 | -------------------------------------------------------------------------------- /packages/core/src/reactive/hooks.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * cfcs 3 | * Copyright (c) 2022-present NAVER Corp. 4 | * MIT license 5 | */ 6 | import { 7 | CFCS_DETECTED_DEPENDENCIES, 8 | CFCS_DETECTED_DEPENDENCIES_VERSION, 9 | } from "./const"; 10 | import { Observer } from "./Observer"; 11 | 12 | export interface Detected { 13 | host: Observer; 14 | observers: Array>; 15 | push(observer: Observer): void; 16 | } 17 | 18 | export function getDetectedStack(): Array { 19 | // Version issues do not occur when you access the native object in the global. 20 | (Object as any)[CFCS_DETECTED_DEPENDENCIES] = (Object as any)[CFCS_DETECTED_DEPENDENCIES] || {}; 21 | const versionList = (Object as any)[CFCS_DETECTED_DEPENDENCIES]; 22 | 23 | versionList[CFCS_DETECTED_DEPENDENCIES_VERSION] = versionList[CFCS_DETECTED_DEPENDENCIES_VERSION] || []; 24 | 25 | return versionList[CFCS_DETECTED_DEPENDENCIES_VERSION]; 26 | } 27 | 28 | export function getCurrentDetected(): Detected | undefined { 29 | const stack = getDetectedStack(); 30 | 31 | return stack[stack.length - 1]; 32 | } 33 | 34 | export function detectDependencies(host: Observer) { 35 | const stack = getDetectedStack(); 36 | const observers: Array = []; 37 | const detected: Detected = { 38 | host, 39 | observers, 40 | push(observer: Observer) { 41 | if (host !== observer && observers.indexOf(observer) === -1) { 42 | observers.push(observer); 43 | } 44 | }, 45 | }; 46 | 47 | stack.push(detected); 48 | return detected; 49 | } 50 | 51 | export function endDetectDependencies() { 52 | const stack = getDetectedStack(); 53 | 54 | return stack.pop(); 55 | } 56 | -------------------------------------------------------------------------------- /packages/core/src/reactive/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * cfcs 3 | * Copyright (c) 2022-present NAVER Corp. 4 | * MIT license 5 | */ 6 | export * from "./utils"; 7 | export * from "./types"; 8 | export * from "./Observer"; 9 | export * from "./ComputedObserver"; 10 | export * from "./inline"; 11 | export * from "./ReactiveAdapter"; 12 | export * from "./adaptReactive"; 13 | 14 | export * from "./decorators/Observe"; 15 | export * from "./decorators/Computed"; 16 | export * from "./decorators/ReactiveSubscribe"; 17 | -------------------------------------------------------------------------------- /packages/core/src/reactive/types.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * cfcs 3 | * Copyright (c) 2022-present NAVER Corp. 4 | * MIT license 5 | */ 6 | import { ComponentEvent } from "@egjs/component"; 7 | import { Observer } from "./Observer"; 8 | import { ReactiveSubscribe } from "./decorators/ReactiveSubscribe"; 9 | import { ReactiveObject } from "./inline"; 10 | 11 | type AnyFunction = (...args: any[]) => any; 12 | type NoArguments = undefined | null | void | never; 13 | type EventKey> = string & keyof T; 14 | type UnknwonToAnyArray = unknown[] extends Arr ? any[] : Arr; 15 | 16 | /** 17 | * @category Reactive 18 | */ 19 | export type ReactiveEventCallback< 20 | T extends Record, 21 | K extends EventKey> 22 | = T[K] extends NoArguments 23 | ? () => any : T[K] extends AnyFunction 24 | ? T[K] 25 | : T[K] extends ComponentEvent 26 | ? (event: ComponentEvent) => any 27 | : (event: T[K]) => any; 28 | 29 | /** 30 | * @category Reactive 31 | */ 32 | export type ReactiveEventParameters< 33 | Events extends Record, 34 | EventName extends EventKey> = UnknwonToAnyArray>>; 35 | 36 | /** 37 | * @category Reactive 38 | */ 39 | export type ReactiveMethods = { 40 | [key in Names]: Instance[key]; 41 | }; 42 | 43 | /** 44 | * @category Reactive 45 | */ 46 | export type ReactiveState> = Instance extends ReactiveObject | ReactiveSubscribe ? Omit : never; 47 | -------------------------------------------------------------------------------- /packages/core/src/reactive/utils.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * cfcs 3 | * Copyright (c) 2022-present NAVER Corp. 4 | * MIT license 5 | */ 6 | import { COMPUTED_PATH, OBSERVERS_PATH } from "./const"; 7 | import { Observer } from "./Observer"; 8 | import { ReactiveMethods } from "./types"; 9 | import { isObject, Ref } from "../core"; 10 | import { observe } from "./inline"; 11 | import { ReactiveSubscribe } from "./decorators/ReactiveSubscribe"; 12 | 13 | /** 14 | * @hidden 15 | */ 16 | export function withReactiveMethods< 17 | Instance, 18 | Names extends keyof Partial, 19 | Return extends ReactiveMethods 20 | >(ref: Ref, methods?: readonly Names[]): Return { 21 | const obj: Record = {}; 22 | 23 | if (!methods) { 24 | return obj; 25 | } 26 | 27 | methods.forEach(name => { 28 | obj[name] = function (...args: any[]) { 29 | const current: any = ref.current || ref.value; 30 | 31 | return current[name](...args); 32 | }; 33 | }); 34 | return obj as Return; 35 | } 36 | 37 | /** 38 | * @hidden 39 | */ 40 | export function defineObservers(instance: any) { 41 | const observers: Record> = {}; 42 | 43 | Object.defineProperty(instance, OBSERVERS_PATH, { 44 | get() { 45 | return observers; 46 | }, 47 | }); 48 | 49 | return observers; 50 | } 51 | 52 | /** 53 | * @hidden 54 | */ 55 | export function getObservers(instance: any, isComputed?: boolean): Record> { 56 | if (!instance[OBSERVERS_PATH]) { 57 | defineObservers(instance); 58 | } 59 | const observers = instance[OBSERVERS_PATH]; 60 | 61 | if (!isComputed) { 62 | const computedList = instance?.constructor?.prototype?.[COMPUTED_PATH]; 63 | 64 | if (computedList) { 65 | computedList.forEach(name => { 66 | if (!(name in observers) && name in instance) { 67 | instance[name]; 68 | } 69 | }); 70 | } 71 | } 72 | return observers; 73 | } 74 | 75 | /** 76 | * @hidden 77 | */ 78 | export function getObserver(instance: any, name: string, defaultValue?: any): Observer { 79 | const observers = getObservers(instance); 80 | 81 | if (!observers[name]) { 82 | observers[name] = observe(defaultValue); 83 | } 84 | return observers[name]; 85 | } 86 | 87 | /** 88 | * @hidden 89 | */ 90 | export function setObserver(instance: any, name: string, observer: Observer) { 91 | const observers = getObservers(instance); 92 | 93 | observers[name] = observer; 94 | } 95 | 96 | /** 97 | * @description Whether that object is an observer instance 98 | * @category Reactive 99 | */ 100 | export function isObserver(val: any): val is Observer { 101 | return val && isObject(val) && "current" in val && "subscribe" in val && "unsubscribe" in val; 102 | } 103 | 104 | /** 105 | * @description Whether the object is reactive 106 | * @category Reactive 107 | */ 108 | export function isReactive(val: any): val is ReactiveSubscribe { 109 | return val && !isObserver(val) && "subscribe" in val && "unsubscribe" in val; 110 | } 111 | -------------------------------------------------------------------------------- /packages/core/test/manual/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 14 | -------------------------------------------------------------------------------- /packages/core/tsconfig.declaration.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig", 3 | "compilerOptions": { 4 | "removeComments": false, 5 | "declaration": true, 6 | "emitDeclarationOnly": true, 7 | "declarationDir": "declaration", 8 | "strictNullChecks": false, 9 | }, 10 | "include": [ 11 | "./src/**/*.ts", 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /packages/core/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./outjs/", 4 | "sourceMap": true, 5 | "module": "es2015", 6 | "target": "es5", 7 | "strictNullChecks": true, 8 | "moduleResolution": "node", 9 | "experimentalDecorators": true, 10 | "skipLibCheck": true, 11 | "jsx": "react", 12 | "lib": [ 13 | "es2015", 14 | "dom" 15 | ], 16 | "baseUrl": ".", 17 | "paths": { 18 | "conveyer/cfcs": [ 19 | "./cfcs" 20 | ] 21 | } 22 | }, 23 | "include": [ 24 | "./src/**/*.ts", 25 | "./test/unit/**/*.ts", 26 | "./stories/**/*.ts", 27 | "./global.d.ts" 28 | ], 29 | "exclude": [ 30 | "node_modules" 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /packages/core/tsconfig.test.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig", 3 | "compilerOptions": { 4 | "module": "commonjs", 5 | "noImplicitAny": false, 6 | "strictNullChecks": false, 7 | "types": [ 8 | "karma-chai", 9 | "mocha", 10 | ] 11 | }, 12 | "include": [ 13 | "./src/**/*.ts", 14 | "./test/unit/**/*.ts" 15 | ], 16 | "exclude": [ 17 | "./node_modules/**/*.ts" 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /packages/docs/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "../.eslintrc.js" 4 | ], 5 | "parser": "@typescript-eslint/parser", 6 | "parserOptions": { 7 | "project": "./tsconfig.eslint.json", 8 | "sourceType": "module" 9 | }, 10 | "rules": { 11 | "import/order": "off", 12 | "guard-for-in": "off", 13 | "max-classes-per-file": "off", 14 | "prefer-arrow/prefer-arrow-functions": "off", 15 | "@typescript-eslint/no-unused-expressions": "off", 16 | "@typescript-eslint/no-unsafe-assignment": "off", 17 | "@typescript-eslint/ban-ts-comment": "off", 18 | "@typescript-eslint/no-empty-function": "off", 19 | "@typescript-eslint/no-unsafe-member-access": "off", 20 | "@typescript-eslint/no-unsafe-call": "off", 21 | "@typescript-eslint/no-unsafe-return": "off", 22 | "@typescript-eslint/naming-convention": "off", 23 | "@typescript-eslint/no-unused-vars": "off", 24 | "@typescript-eslint/restrict-template-expressions": "off", 25 | "@typescript-eslint/require-await": "off", 26 | "@typescript-eslint/restrict-plus-operands": "off", 27 | "@typescript-eslint/no-unnecessary-type-assertion": "off", 28 | "@jsdoc/check-indentation": "off" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/docs/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | 4 | # Production 5 | /build 6 | 7 | # Generated files 8 | .docusaurus 9 | .cache-loader 10 | 11 | # Misc 12 | .DS_Store 13 | .env.local 14 | .env.development.local 15 | .env.test.local 16 | .env.production.local 17 | 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | 22 | # API 23 | docs/api/ 24 | i18n/ko/docusaurus-plugin-content-docs/current/api/ 25 | sidebars-api.js 26 | static/release/ 27 | -------------------------------------------------------------------------------- /packages/docs/docs/examples/components/GestureApp.tsx: -------------------------------------------------------------------------------- 1 | import React, { useRef } from "react"; 2 | import { useReactive } from "@cfcs/react"; 3 | import { reactive } from "@cfcs/core"; 4 | 5 | const REACTIVE_ADAPTER = ({ setEvents, emit, onInit, onDestroy, getProps }) => { 6 | setEvents(["dragStart", "drag", "dragEnd"]); 7 | const obj = reactive({ 8 | x: 0, 9 | y: 0, 10 | holding: false, 11 | }); 12 | const ref = getProps().ref; 13 | 14 | let startClientX = 0; 15 | let startClientY = 0; 16 | let startX = 0; 17 | let startY = 0; 18 | 19 | const onDragStart = (e) => { 20 | obj.holding = true; 21 | startClientX = e.clientX; 22 | startClientY = e.clientY; 23 | startX = obj.x; 24 | startY = obj.y; 25 | emit("dragStart", e); 26 | }; 27 | const onDrag = e => { 28 | if (!obj.holding) { 29 | return; 30 | } 31 | obj.x = startX + e.clientX - startClientX; 32 | obj.y = startY + e.clientY - startClientY; 33 | emit("drag", e); 34 | }; 35 | const onDragEnd = e => { 36 | if (!obj.holding) { 37 | return; 38 | } 39 | emit("dragEnd", e); 40 | obj.holding = false; 41 | }; 42 | onInit(() => { 43 | const element = ref.value || ref.current; 44 | element?.addEventListener("mousedown", onDragStart); 45 | window.addEventListener("mousemove", onDrag); 46 | window.addEventListener("mouseup", onDragEnd); 47 | }); 48 | 49 | onDestroy(() => { 50 | const element = ref.value || ref.current; 51 | element?.removeEventListener("mousedown", onDragStart); 52 | window.removeEventListener("mousemove", onDrag); 53 | window.removeEventListener("mouseup", onDragEnd); 54 | }); 55 | return obj; 56 | }; 57 | 58 | function useGesture() { 59 | const ref = useRef(null); 60 | 61 | return Object.assign( 62 | useReactive(REACTIVE_ADAPTER, () => ({ ref })), 63 | { 64 | ref, 65 | } 66 | ); 67 | } 68 | 69 | export function GestureApp() { 70 | const { 71 | ref, 72 | x, y, holding, 73 | onDragStart, 74 | onDrag, 75 | onDragEnd, 76 | } = useGesture(); 77 | 78 | onDragStart(() => { 79 | console.log("drag start"); 80 | }, []); 81 | onDrag(() => { 82 | console.log("drag"); 83 | }, []); 84 | onDragEnd(() => { 85 | console.log("drag end"); 86 | }, []); 87 | 88 | return ( 89 |
90 |
102 |
103 | ); 104 | } 105 | -------------------------------------------------------------------------------- /packages/docs/docs/examples/components/ImageOnLoadApp.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useRef, useState } from "react"; 2 | import { useBaseUrlUtils } from "@docusaurus/useBaseUrl"; 3 | import { useReactive } from "@cfcs/react"; 4 | import { reactive } from "@cfcs/core"; 5 | 6 | const REACTIVE_ADAPTER = ({ onInit, onDestroy, getProps }) => { 7 | const obj = reactive({ isLoaded: false }); 8 | const ref = getProps().ref; 9 | 10 | const onLoaded = () => { 11 | const element = ref.value || ref.current; 12 | 13 | obj.isLoaded = !!(element.naturalWidth && element.complete); 14 | }; 15 | onInit(() => { 16 | const element = ref.value || ref.current; 17 | 18 | element?.addEventListener("load", onLoaded); 19 | onLoaded(); 20 | }); 21 | 22 | onDestroy(() => { 23 | const element = ref.value || ref.current; 24 | 25 | element?.removeEventListener("load", onLoaded); 26 | }); 27 | return obj; 28 | }; 29 | 30 | function useImageOnLoad() { 31 | const ref = useRef(null); 32 | 33 | return Object.assign(useReactive(REACTIVE_ADAPTER, () => ({ ref })), { 34 | ref, 35 | }); 36 | } 37 | 38 | 39 | export function ImageOnLoadApp() { 40 | const { isLoaded, ref } = useImageOnLoad(); 41 | const [src, setSrc] = useState(""); 42 | const { withBaseUrl } = useBaseUrlUtils(); 43 | 44 | useEffect(() => { 45 | const id = setTimeout(() => { 46 | setSrc(withBaseUrl("./tutorials/logo.png")); 47 | }, 1000); 48 | 49 | return () => { 50 | clearTimeout(id); 51 | }; 52 | }, []); 53 | return
54 | Loaded: {isLoaded ? "loaded" : "loading..."}
55 | 56 |
; 57 | } 58 | -------------------------------------------------------------------------------- /packages/docs/docs/examples/components/KeyApp.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useRef, useState } from "react"; 2 | import { useBaseUrlUtils } from "@docusaurus/useBaseUrl"; 3 | import { useReactive } from "@cfcs/react"; 4 | import { reactive } from "@cfcs/core"; 5 | 6 | const REACTIVE_ADAPTER = ({ onInit, onDestroy, getProps }) => { 7 | const obj = reactive({ 8 | keys: [] as string[], 9 | isKeydown: false, 10 | }); 11 | const ref = getProps().ref; 12 | 13 | const onKeydown = (e: KeyboardEvent) => { 14 | const keys = obj.keys; 15 | const key = e.key; 16 | 17 | if (keys.includes(key)) { 18 | return; 19 | } 20 | obj.keys = [...keys, e.key].sort((a, b) => a < b ? -1 : 1); 21 | obj.isKeydown = true; 22 | }; 23 | const onKeyup = (e: KeyboardEvent) => { 24 | const keys = obj.keys; 25 | const key = e.key; 26 | const index = keys.indexOf(key); 27 | 28 | if (index === -1) { 29 | return; 30 | } 31 | keys.splice(index, 1); 32 | obj.keys = [...keys]; 33 | obj.isKeydown = !!keys.length; 34 | }; 35 | const onBlur = () => { 36 | if (!obj.keys.length) { 37 | return; 38 | } 39 | obj.keys = []; 40 | obj.isKeydown = false; 41 | } 42 | onInit(() => { 43 | const element = ref.value || ref.current; 44 | 45 | 46 | element?.addEventListener("keydown", onKeydown); 47 | element?.addEventListener("keyup", onKeyup); 48 | element?.addEventListener("keyup", onBlur); 49 | }); 50 | 51 | onDestroy(() => { 52 | const element = ref.value || ref.current; 53 | 54 | element?.removeEventListener("keydown", onKeydown); 55 | element?.removeEventListener("keyup", onKeyup); 56 | element?.removeEventListener("keyup", onBlur); 57 | }); 58 | return obj; 59 | }; 60 | 61 | function useKey() { 62 | const ref = useRef(null); 63 | 64 | return Object.assign(useReactive(REACTIVE_ADAPTER, () => ({ ref })), { 65 | ref, 66 | }); 67 | } 68 | 69 | 70 | export function KeyApp() { 71 | const { 72 | isKeydown, 73 | keys, 74 | ref, 75 | } = useKey(); 76 | 77 | return
78 | * isKeydown: {isKeydown ? "true" : "false"}
79 | * selected keys: {keys.join(" + ")}
80 | 81 |
; 82 | } 83 | -------------------------------------------------------------------------------- /packages/docs/docs/examples/components/ScrollApp.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useRef, useState } from "react"; 2 | import { useBaseUrlUtils } from "@docusaurus/useBaseUrl"; 3 | import { useReactive } from "@cfcs/react"; 4 | import { reactive } from "@cfcs/core"; 5 | 6 | const REACTIVE_ADAPTER = ({ onInit, onDestroy, getProps }) => { 7 | const obj = reactive({ 8 | scrollPos: 0, 9 | isReachStart: false, 10 | isReachEnd: false, 11 | }); 12 | const ref = getProps().ref; 13 | 14 | const onScroll = () => { 15 | const element = ref.value || ref.current; 16 | const scrollLeft = element.scrollLeft; 17 | obj.scrollPos = scrollLeft; 18 | obj.isReachStart = scrollLeft === 0; 19 | obj.isReachEnd = scrollLeft === element.scrollWidth - element.clientWidth; 20 | }; 21 | onInit(() => { 22 | const element = ref.value || ref.current; 23 | 24 | element?.addEventListener("scroll", onScroll); 25 | onScroll(); 26 | }); 27 | 28 | onDestroy(() => { 29 | const element = ref.value || ref.current; 30 | 31 | element?.removeEventListener("scroll", onScroll); 32 | }); 33 | return obj; 34 | }; 35 | 36 | function useScroll() { 37 | const ref = useRef(null); 38 | 39 | return Object.assign(useReactive(REACTIVE_ADAPTER, () => ({ ref })), { 40 | ref, 41 | }); 42 | } 43 | 44 | 45 | export function ScrollApp() { 46 | const { 47 | scrollPos, 48 | isReachStart, 49 | isReachEnd, 50 | ref, 51 | } = useScroll(); 52 | const [src, setSrc] = useState(""); 53 | const { withBaseUrl } = useBaseUrlUtils(); 54 | 55 | useEffect(() => { 56 | const id = setTimeout(() => { 57 | setSrc(withBaseUrl("./tutorials/logo.png")); 58 | }, 1000); 59 | 60 | return () => { 61 | clearTimeout(id); 62 | }; 63 | }, []); 64 | return
65 | * pos: {scrollPos}
66 | * start: {isReachStart ? "reached": "not reached"}
67 | * end: {isReachEnd ? "reached" : "not reached"} 68 |
73 |
78 |
79 |
; 80 | } 81 | -------------------------------------------------------------------------------- /packages/docs/docs/examples/components/WindowSizeApp.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useReactive } from "@cfcs/react"; 3 | import { reactive } from "@cfcs/core"; 4 | 5 | const REACTIVE_ADAPTER = ({ onInit, onDestroy }) => { 6 | const obj = reactive({ width: 0, height: 0 }); 7 | const callback = () => { 8 | obj.width = window.innerWidth; 9 | obj.height = window.innerHeight; 10 | }; 11 | onInit(() => { 12 | window.addEventListener("resize", callback); 13 | callback(); 14 | }); 15 | 16 | onDestroy(() => { 17 | window.removeEventListener("resize", callback); 18 | }); 19 | 20 | return obj; 21 | }; 22 | 23 | function useWindowSize() { 24 | return useReactive(REACTIVE_ADAPTER); 25 | } 26 | 27 | 28 | export function WindowSizeApp() { 29 | const { width, height } = useWindowSize(); 30 | 31 | return
{width} x {height}
35 | } 36 | -------------------------------------------------------------------------------- /packages/docs/docs/examples/useWindowSize.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: useWindowSize 3 | slug: /examples 4 | sidebar_position: 1 5 | --- 6 | import Tabs from "@theme/Tabs"; 7 | import TabItem from "@theme/TabItem"; 8 | import CodeBlock from "@theme/CodeBlock"; 9 | import { FrameworkTabs } from "@site/src/components/FrameworkTabs"; 10 | 11 | 12 | import { WindowSizeApp } from "./components/WindowSizeApp"; 13 | 14 | 15 | 16 | An example of a Reactive component that gets the browser's window size. 17 | 18 | 19 | If the size of the browser changes, the `width` and `height` state values change. 20 | 21 | 22 |
23 | 24 |
25 | 26 | ### Reactive Adapter 27 | 28 | ```js 29 | import { reactive } from "@cfcs/core"; 30 | 31 | const REACTIVE_ADAPTER = ({ onInit, onDestroy }) => { 32 | const obj = reactive({ width: 0, height: 0 }); 33 | const callback = () => { 34 | obj.width = window.innerWidth; 35 | obj.height = window.innerHeight; 36 | }; 37 | onInit(() => { 38 | window.addEventListener("resize", callback); 39 | callback(); 40 | }); 41 | 42 | onDestroy(() => { 43 | window.removeEventListener("resize", callback); 44 | }); 45 | 46 | return obj; 47 | }; 48 | ``` 49 | 50 | 51 | ### Frameworks 52 | 53 | 54 | 55 | 56 | 57 | 58 | You can create a Reactive Component by importing the `@cfcs/react` module and using the `useReactive` function. 59 |

60 | 61 | ```jsx 62 | import { useReactive } from "@cfcs/react"; 63 | 64 | function useWindowSize() { 65 | return useReactive(REACTIVE_ADAPTER); 66 | } 67 | ``` 68 | ```jsx 69 | export function App() { 70 | const { 71 | width, 72 | height, 73 | onResize, 74 | } = useWindowSize(); 75 | 76 | return

{width}x{height}
; 77 | } 78 | ``` 79 | 80 |
81 | 82 | 83 | You can create a Reactive Component by importing the `@cfcs/vue2` module and using the `useReactive` function. 84 | 85 |

86 | 87 | 88 | Use [`@vue/composition-api`](https://github.com/vuejs/composition-api) if you are going to use it in Vue 2. 89 | 90 | 91 |

92 | 93 | ```sh 94 | # < 2.7 95 | $ npm install @cfcs/vue2 96 | ``` 97 | 98 | ```jsx 99 | import { useReactive } from "@cfcs/vue2"; 100 | 101 | function useWindowSize() { 102 | return useReactive(REACTIVE_ADAPTER); 103 | } 104 | ``` 105 | ```html 106 | 109 | 124 | ``` 125 | 126 | 127 | 128 | 129 | You can create a Reactive Component by importing the `@cfcs/vue3` module and using the `useReactive` function. 130 | 131 |

132 | 133 | 134 | ```sh 135 | # >= 2.7 136 | $ npm install @cfcs/vue3 137 | ``` 138 | ```jsx 139 | import { useReactive } from "@cfcs/vue3"; 140 | 141 | function useWindowSize() { 142 | return useReactive(REACTIVE_ADAPTER); 143 | } 144 | ``` 145 | ```html 146 | 149 | 164 | ``` 165 | 166 | 167 | 168 | 169 | You can create a Reactive Component by importing the `@cfcs/svelte` module and using the `useReactive` function. 170 | 171 | 172 |

173 | 174 | ```sh 175 | $ npm install @cfcs/svelte 176 | ``` 177 | ```jsx 178 | import { useReactive } from "@cfcs/svelte"; 179 | 180 | function useWindowSize() { 181 | return useReactive(REACTIVE_ADAPTER); 182 | } 183 | ``` 184 | ```html 185 | 192 |

{$width}x{$height}
193 | ``` 194 |
195 |
196 | -------------------------------------------------------------------------------- /packages/docs/docs/tutorials/best-practice/best-practice.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Best Practice 3 | slug: /best-practice 4 | sidebar_position: 6 5 | --- 6 | 7 | 8 | It tells you how to write `Adapter` to support frameworks in order. 9 | 10 | 11 | 1. [Use State And Adapter](/docs/best-practice/use-state) 12 | 2. [Use Lifecycle](/docs/best-practice/use-lifecycle) 13 | 3. [Use Events](/docs/best-practice/use-events) 14 | 4. [Use Methods](/docs/best-practice/use-methods) 15 | 5. [Use Props](/docs/best-practice/use-props) 16 | 6. [Use TypeScript](/docs/best-practice/use-typescript) 17 | -------------------------------------------------------------------------------- /packages/docs/docs/tutorials/best-practice/use-lifecycle.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: 2. Use Lifecycle 3 | slug: /best-practice/use-lifecycle 4 | sidebar_position: 3 5 | --- 6 | 7 | import Tabs from "@theme/Tabs"; 8 | import TabItem from "@theme/TabItem"; 9 | import CodeBlock from "@theme/CodeBlock"; 10 | import { FrameworkTabs } from "@site/src/components/FrameworkTabs"; 11 | 12 | Lifecycle Hooks are provided for the [first argument](/docs/api/Core/Reactive/Interface/ReactiveSetup) of the function. 13 | 14 | * `created` is equivalent to calling a function. 15 | * `mounted` => `onMounted` 16 | * `init` => `onInit` 17 | * `destroy` => `onDestroy` 18 | 19 | 20 | 21 | 22 | `onInit` (or `onMounted`) and `onDestroy` must match. If it is registered in `onInit`, it must be released in `onDestroy`. 23 | 24 | [See Lifecycle Documents](/docs/cfcs-reactive-lifecycle) 25 | 26 | In Strict Mode development environment of React 18 will occur in the order. 27 | 28 | 1. `created` => `created` 29 | 2. `mounted` => `init` 30 | 3. `destroy` 31 | 4. `mounted` => `init` 32 | 33 | 34 | 35 | If you don't return an instance in the `created` lifecycle, you should return an instance in `mounted`. 36 | And you have to tell it the initial value of the state via the `setInitialState` function. 37 | 38 | ```js 39 | import { reactive } from "@cfcs/core"; 40 | 41 | const REACTIVE_ADAPTER = ({ setInitialState, onMounted }) => { 42 | setInitialState({ 43 | width: 0, 44 | height: 0, 45 | }); 46 | 47 | onMounted(() => { 48 | const obj = reactive({ 49 | width: 0, 50 | height: 0, 51 | }); 52 | 53 | return obj; 54 | }); 55 | ``` 56 | 57 | The following code changes the `width` and `height` state to 100 after 1 second. 58 | 59 | ```js 60 | import { reactive } from "@cfcs/core"; 61 | 62 | const REACTIVE_ADAPTER = ({ onInit, onDestroy }) => { 63 | const obj = reactive({ 64 | width: 0, 65 | height: 0, 66 | }); 67 | 68 | let timerId = 0; 69 | 70 | onInit(() => { 71 | // enable timer 72 | timerId = setTimeout(() => { 73 | obj.width = 100; 74 | obj.height = 100; 75 | }, 1000); 76 | }); 77 | 78 | onDestroy(() => { 79 | // disable timer 80 | clearTimeout(timerId); 81 | }); 82 | 83 | 84 | return obj; 85 | }; 86 | ``` 87 | 88 | 89 | ### Frameworks 90 | 91 | 92 | 93 | 94 | ```jsx 95 | import { useReactive } from "@cfcs/react"; 96 | 97 | export function App() { 98 | const { 99 | width, 100 | height, 101 | } = useReactive(REACTIVE_ADAPTER); 102 | 103 | return
{width}x{height}
; 104 | } 105 | ``` 106 |
107 | 108 | 109 | State in vue is [`ref`](https://vuejs.org/api/reactivity-core.html#ref), and in `setup`, you need to access it as `.value`. 110 | 111 | 112 | ```html 113 | 116 | 133 | ``` 134 | 135 | 136 | 137 | 138 | State in vue is [`ref`](https://vuejs.org/api/reactivity-core.html#ref), and in `setup`, you need to access it as `.value`. 139 | 140 | 141 | 142 | ```html 143 | 146 | 163 | ``` 164 | 165 | 166 | 167 | The state in svelte uses [store](https://svelte.dev/docs#component-format-script-4-prefix-stores-with-$-to-access-their-values) and accesses it with $ to get the value. 168 | 169 | ```html 170 | 179 |
{$width}x{$height}
180 | ``` 181 | 182 | 183 |
184 |
185 | 186 | -------------------------------------------------------------------------------- /packages/docs/docs/tutorials/best-practice/use-methods.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: 4. Use Methods 3 | slug: /best-practice/use-methods 4 | sidebar_position: 5 5 | --- 6 | 7 | import Tabs from "@theme/Tabs"; 8 | import TabItem from "@theme/TabItem"; 9 | import CodeBlock from "@theme/CodeBlock"; 10 | import { FrameworkTabs } from "@site/src/components/FrameworkTabs"; 11 | 12 | 13 | In addition to states and events, a method that can be manually controlled is considered essential to a component. 14 | You can set which functions are exposed with the `setMethods` function. 15 | 16 | [See Function Adapter's setup](/docs/api/Core/Reactive/Interface/ReactiveSetup) 17 | 18 | 19 | 20 | The following code is an example of manually changing the width value through the `setWidth` function. 21 | 22 | ```js 23 | import { reactive } from "@cfcs/core"; 24 | 25 | const REACTIVE_ADAPTER = ({ setMethods, onInit, onDestroy }) => { 26 | setMethods(["setWidth"]); 27 | 28 | const obj = reactive({ 29 | width: 0, 30 | height: 0, 31 | setWidth(width) { 32 | obj.width = width; 33 | }, 34 | }); 35 | const onResize = e => { 36 | obj.width = window.innerWidth; 37 | obj.height = window.innerHeight; 38 | }; 39 | 40 | onInit(() => { 41 | window.addEventListener("resize", onResize); 42 | }); 43 | 44 | onDestroy(() => { 45 | window.removeEventListener("resize", onResize); 46 | }); 47 | 48 | return obj; 49 | }; 50 | ``` 51 | 52 | 53 | ### Frameworks 54 | 55 | 56 | 57 | 58 | ```jsx 59 | import { useReactive } from "@cfcs/react"; 60 | 61 | export function App() { 62 | const { 63 | width, 64 | height, 65 | setWidth, 66 | } = useReactive(REACTIVE_ADAPTER); 67 | 68 | 69 | return
{ 70 | setWidth(200); 71 | }}>{width}x{height}
; 72 | } 73 | ``` 74 |
75 | 76 | 77 | ```html 78 | 81 | 103 | ``` 104 | 105 | 106 | 107 | 108 | ```html 109 | 112 | 134 | ``` 135 | 136 | 137 | 138 | ```html 139 | 149 |
{ 150 | setWidth(200); 151 | }}>{$width}x{$height}
152 | ``` 153 | 154 | 155 |
156 |
157 | -------------------------------------------------------------------------------- /packages/docs/docs/tutorials/best-practice/use-props.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: 5. Use Props 3 | slug: /best-practice/use-props 4 | sidebar_position: 5 5 | --- 6 | 7 | import Tabs from "@theme/Tabs"; 8 | import TabItem from "@theme/TabItem"; 9 | import CodeBlock from "@theme/CodeBlock"; 10 | import { FrameworkTabs } from "@site/src/components/FrameworkTabs"; 11 | 12 | When you use a component in the framework, users can pass `props` to the component. 13 | 14 | You can read the props passed from the user through the `getProps` function. 15 | 16 | [See Function Adapter's setup](/docs/api/Core/Reactive/Interface/ReactiveSetup) 17 | 18 | 19 | The following code is an example of setting initial values through props passed through `getProps`. 20 | 21 | ```ts 22 | import { reactive } from "@cfcs/core"; 23 | 24 | 25 | const REACTIVE_ADAPTER = ({ getProps }) => { 26 | const props = getProps(); 27 | const obj = reactive({ 28 | width: props?.startWidth ?? 0, 29 | height: props?.startHeight ?? 0, 30 | }); 31 | 32 | return obj; 33 | }; 34 | ``` 35 | 36 | 37 | ### Frameworks 38 | `props` can be passed to Adapter through the second parameter of `useReactive`. 39 | 40 | 41 | 42 | 43 | 44 | ```jsx 45 | import { useReactive } from "@cfcs/react"; 46 | 47 | function useReactiveComponent(props) { 48 | return useReactive(REACTIVE_ADAPTER, () => props); 49 | } 50 | export function App() { 51 | const { 52 | width, 53 | height, 54 | } = useReactiveComponent({ 55 | startWidth: 100, 56 | startHeight: 10, 57 | }); 58 | 59 | return
{width}x{height}
; 60 | } 61 | ``` 62 |
63 | 64 | 65 | State in vue is [`ref`](https://vuejs.org/api/reactivity-core.html#ref), and in `setup`, you need to access it as `.value`. 66 | 67 | 68 | ```html 69 | 72 | 96 | ``` 97 | 98 | 99 | 100 | 101 | State in vue is [`ref`](https://vuejs.org/api/reactivity-core.html#ref), and in `setup`, you need to access it as `.value`. 102 | 103 | 104 | 105 | ```html 106 | 109 | 133 | ``` 134 | 135 | 136 | 137 | The state in svelte uses [store](https://svelte.dev/docs#component-format-script-4-prefix-stores-with-$-to-access-their-values) and accesses it with $ to get the value. 138 | 139 | ```html 140 | 156 |
{$width}x{$height}
157 | ``` 158 | 159 | 160 |
161 |
162 | 163 | -------------------------------------------------------------------------------- /packages/docs/docs/tutorials/cfcs-dom.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: CFCs DOM (Soon) 3 | id: cfcs-dom 4 | slug: /concepts-cfcs-dom 5 | sidebar_position: 999 6 | --- 7 | 8 | 9 | CFCs DOM supports various frameworks by delegating the DOM render function to the framework. (Soon) 10 | 11 | ![](/tutorials/cfcs-dom.png) 12 | 13 | 14 | #### CFCs DOM-style components 15 | * [Flicking](https://github.com/naver/egjs-flicking): It's reliable, flexible and extendable carousel. 16 | * [InfiniteGrid](https://github.com/naver/egjs-infinitegrid): Arrange elements including content infinitely according to grid type. 17 | * [Grid](https://github.com/naver/egjs-grid): Arrange items according to the type of grids. 18 | * [View 360](https://github.com/naver/egjs-view360): 360° panorama image / video viewer. 19 | * [View 3D](https://github.com/naver/egjs-view3d): Fast & Customizable glTF 3D model viewer. 20 | 21 | -------------------------------------------------------------------------------- /packages/docs/docs/tutorials/cfcs-reactive-lifecycle.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Reactive Lifecycle 3 | id: cfcs-reactive-lifecycle 4 | slug: /cfcs-reactive-lifecycle 5 | sidebar_position: 4 6 | --- 7 | 8 | import Tabs from "@theme/Tabs"; 9 | import TabItem from "@theme/TabItem"; 10 | import CodeBlock from "@theme/CodeBlock"; 11 | import { FrameworkTabs } from "@site/src/components/FrameworkTabs"; 12 | 13 | Even if a vanilla component is created, it cannot be applied to the framework as it is. This is because the usage method is different for each framework. 14 | 15 | So, to support vanilla components in the framework, CFCs provide compatible adapters. 16 | 17 | CFCs provide several lifecycles and functions, and can be applied to various frameworks by writing usage accordingly. 18 | 19 | ![](/tutorials/cfcs-lifecycle.png) 20 | 21 | * `created`: Lifecycle that occurs when a component is called (or created). 22 | * Initialize `state`, `methods`, and `events` to be exposed. 23 | * `mounted`: Lifecycle that occurs when a component is mounted. 24 | * Connect the events exposed with the events of the instance. 25 | * `init`: Lifecycle that occurs after registering an event in the mounted Lifecycle. 26 | * Initialize the instance. 27 | * `destroy`: Lifecycle that occurs when a component is destroying. 28 | * Disconnect the exposed event and remove the instance. 29 | -------------------------------------------------------------------------------- /packages/docs/docs/tutorials/cfcs-reactive-state.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: How to make Reactive State 3 | id: cfcs-reactive-state 4 | slug: /cfcs-reactive-state 5 | sidebar_position: 3 6 | --- 7 | 8 | import Tabs from "@theme/Tabs"; 9 | import TabItem from "@theme/TabItem"; 10 | import CodeBlock from "@theme/CodeBlock"; 11 | import { FrameworkTabs } from "@site/src/components/FrameworkTabs"; 12 | 13 | 14 | **Reactive State** refers to a state that changes according to a specific condition. 15 | 16 | ### ⚙️ Installation 17 | ```sh 18 | $ npm install @cfcs/core 19 | ``` 20 | 21 | ### Reactive State 22 | 23 | `Reactive State` is a great extension for `Vanilla` as well. 24 | 25 | Because you can get properties that can be obtained from events in one `Reactive State`. 26 | 27 | If you can get properties through events you would use something like this: 28 | 29 | ```js 30 | // AS-IS 31 | inst.on("event1", e => { 32 | console.log(e.prop1); 33 | }); 34 | inst.on("event2", e => { 35 | console.log(e.prop1); 36 | }); 37 | ``` 38 | 39 | If you want to directly detect the state value, you can use it in the following way. 40 | 41 | ```js 42 | // TO-BE 43 | inst.subscribe("prop1", nextValue => { 44 | console.log(nextValue); 45 | }); 46 | ``` 47 | 48 | In this case, state detection is more intuitive than event detection. 49 | 50 | 51 | * Class 52 | * [`ReactiveSubscribe`](/docs/api/Core/Reactive-Decorator/Function/ReactiveSubscribe) is a class decorator and adds `.subscribe` and `.unsubscribe` methods. 53 | * [`Observe`](/docs/api/Core/Reactive-Decorator/Function/Observe) is a property decorator and converts the property into a `reactive state`. You can detect its status through `.subscribe`. 54 | ```ts 55 | import { ReactiveSubscribe, Observe } from "@cfcs/core"; 56 | 57 | @ReactiveSubscribe 58 | class Component { 59 | @Observe value1 = 1; 60 | 61 | constructor() { 62 | requestAnimationFrame(() => { 63 | this.value1 = 2; 64 | }); 65 | } 66 | } 67 | 68 | interface Component extends ReactiveSubscribe<{ 69 | value1: number; 70 | value2: number; 71 | }> {} 72 | 73 | const component = new Component(); 74 | 75 | // 1 76 | console.log(component.value1); 77 | 78 | component.subscribe("value1", nextValue => { 79 | // When the change event occurs => (2, 2) 80 | console.log(nextValue, component.value2); 81 | }); 82 | ``` 83 | 84 | * Inline Object 85 | * [`reactive`](/docs/api/Core/Reactive/Function/reactive) converts the object into a reactive object, and values can be changed through `.subscribe`. 86 | 87 | 88 | ```js 89 | import { reactive } from "@cfcs/core"; 90 | 91 | const obj = reactive({ 92 | value1: 1, 93 | }); 94 | 95 | 96 | // 1 97 | console.log(obj.value1); 98 | 99 | obj.subscribe("value1", nextValue => { 100 | // When the change event occurs => (2, 2) 101 | console.log(nextValue, obj.value1); 102 | }); 103 | 104 | 105 | obj.value1 = 2; 106 | ``` 107 | -------------------------------------------------------------------------------- /packages/docs/docs/tutorials/cfcs-reactive.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: CFCs Reactive 3 | id: cfcs-reactive 4 | slug: /cfcs-reactive 5 | sidebar_position: 2 6 | --- 7 | 8 | import Tabs from "@theme/Tabs"; 9 | import TabItem from "@theme/TabItem"; 10 | import CodeBlock from "@theme/CodeBlock"; 11 | import { FrameworkTabs } from "@site/src/components/FrameworkTabs"; 12 | 13 | 14 | CFCs Reactive can support multiple frameworks as a Reactive Component, a utility component that is state-based and does not create a UI. 15 | 16 | CFCs Reactive consists of `methods`, `events`, and **`state`**. 17 | 18 | ![](/tutorials/cfcs-reactive.png) 19 | 20 | 21 | **Reactive State** is a state that changes according to a specific condition. 22 | You can detect state changes and also create a UI that changes based on conditions. 23 | 24 | 25 | If you can get properties through events you would use something like this: 26 | 27 | ```js 28 | // AS-IS 29 | inst.on("event1", e => { 30 | console.log(e.prop1); 31 | }); 32 | inst.on("event2", e => { 33 | console.log(e.prop1); 34 | }); 35 | ``` 36 | 37 | However, if you want to read the status value directly rather than an event, 38 | ```js 39 | // TO-BE 40 | console.log(inst.prop1); 41 | ``` 42 | 43 | If you want to directly detect the state value, you can use it in the following way. 44 | 45 | ```js 46 | // TO-BE 47 | inst.subscribe("prop1", nextValue => { 48 | console.log(nextValue); 49 | }); 50 | ``` 51 | 52 | In this case, state detection is more intuitive than event detection. 53 | 54 | 55 | #### CFCs Reactive-style components 56 | * [ImReady](https://github.com/naver/egjs-imready): I'm Ready to check if the images or videos are loaded. 57 | * [Axes](https://github.com/naver/egjs-axes): You can easily create a UI that responds to user actions. 58 | * [Conveyer](https://github.com/naver/egjs-conveyer): Drag gestures to your Native Scroll. 59 | -------------------------------------------------------------------------------- /packages/docs/docs/tutorials/what-is-cfcs.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: What is CFCs 3 | id: what-is-cfcs 4 | slug: / 5 | sidebar_position: 1 6 | --- 7 | 8 | import Tabs from "@theme/Tabs"; 9 | import TabItem from "@theme/TabItem"; 10 | import CodeBlock from "@theme/CodeBlock"; 11 | 12 | 13 | Write once, create framework components that supports React, Vue, Svelte, and more. 14 | 15 | 16 | ## Description 17 | 18 | Many users use JavaScript frameworks and create or use components available in the framework. 19 | 20 | However, if the component you want doesn't exist, you may have to find it externally or create it yourself. 21 | 22 | Have you ever wanted to use Vanilla components in React or React components in Vue? 23 | 24 | In order to support these JavaScript frameworks, the developers of JS libraries had to learn how to use each framework, and write and maintain code for each framework separately. 25 | 26 | **CFCs**(Cross Framework Components) were developed to solve these problems. 27 | With **CFCs**, **one code** can support multiple frameworks according to the framework's usage. 28 | 29 | ## Who should use this library? 30 | 31 | In today's web front-end development, JavaScript frameworks have become very important. 32 | 33 | The problem is that even though there are many great components, there are cases where JavaScript frameworks are not supported. Even newer JavaScript frameworks stand out even more. 34 | 35 | Typically, you can call these libraries using the `mounted` lifecycle supported by frameworks, but often you won't be able to use some of their features or even use them at all. 36 | 37 | 38 | 39 | For those who have had such experiences, we recommend using CFC. 40 | In particular, we recommend **CFCs** if you: 41 | 42 | 43 | 1. **You want to develop a component to support various JavaScript frameworks.** 44 | 2. **You want to support existing components to various JavasScript frameworks.**** 45 | 46 | 47 | 48 | 49 | ## Concepts 50 | 51 | Previously, if you wanted to support different JavaScript frameworks, you had to write code for each one separately. 52 | 53 | This meant that if you doubled the number of frameworks you wanted to support, the maintenance cost is also doubled. 54 | 55 | With **CFCs**, you can use Compatible to support multiple frameworks with **just one code**. 56 | 57 | 58 | ![](/tutorials/cfcs-compatible.png) 59 | 60 | ### CFCs Reactive 61 | CFCs Reactive can support multiple frameworks as a Reactive Component, a utility component that is state-based and does not create a UI. 62 | 63 | 64 | ![](/tutorials/cfcs-reactive.png) 65 | 66 | 67 | 68 | **Reactive State** is a state that changes according to a specific condition. 69 | 70 | You can detect state changes and also create a UI that changes based on conditions. 71 | 72 | 73 | * [**See Reactive Documents**](https://github.com/naver/cfcs/blob/main/reactive.md) 74 | 75 | 76 | #### CFCs Reactive-style components 77 | * [ImReady](https://github.com/naver/egjs-imready): I'm Ready to check if the images or videos are loaded. 78 | * [Axes](https://github.com/naver/egjs-axes): You can easily create a UI that responds to user actions. 79 | * [Conveyer](https://github.com/naver/egjs-conveyer): Drag gestures to your Native Scroll. 80 | 81 | 82 | 83 | ### CFCs DOM 84 | 85 | CFCs DOM supports various frameworks by delegating the DOM render function to the framework. (Soon) 86 | 87 | ![](/tutorials/cfcs-dom.png) 88 | 89 | 90 | #### CFCs DOM-style components 91 | * [Flicking](https://github.com/naver/egjs-flicking): It's reliable, flexible and extendable carousel. 92 | * [InfiniteGrid](https://github.com/naver/egjs-infinitegrid): Arrange elements including content infinitely according to grid type. 93 | * [Grid](https://github.com/naver/egjs-grid): Arrange items according to the type of grids. 94 | * [View 360](https://github.com/naver/egjs-view360): 360° panorama image / video viewer. 95 | * [View 3D](https://github.com/naver/egjs-view3d): Fast & Customizable glTF 3D model viewer. 96 | 97 | -------------------------------------------------------------------------------- /packages/docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docs", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "docusaurus": "docusaurus", 7 | "start": "docusaurus start --host 0.0.0.0", 8 | "start:ssl": "HTTPS=true SSL_CRT_FILE=cert.pem SSL_KEY_FILE=key.pem docusaurus start --host 0.0.0.0", 9 | "build": "docusaurus build --out-dir ../../demo", 10 | "swizzle": "docusaurus swizzle", 11 | "deploy": "docusaurus deploy", 12 | "clear": "docusaurus clear", 13 | "serve": "docusaurus serve", 14 | "write-translations": "docusaurus write-translations", 15 | "write-heading-ids": "docusaurus write-heading-ids" 16 | }, 17 | "dependencies": { 18 | "@cfcs/core": "~0.1.0", 19 | "@cfcs/react": "~0.1.0", 20 | "@docusaurus/core": "^2.0.0", 21 | "@docusaurus/preset-classic": "^2.0.0", 22 | "@mdx-js/react": "^1.6.21", 23 | "@svgr/webpack": "^5.5.0", 24 | "@tabler/icons": "^1.119.0", 25 | "clsx": "^1.1.1", 26 | "file-loader": "^6.2.0", 27 | "hls.js": "^1.2.9", 28 | "prism-react-renderer": "^1.2.1", 29 | "react": "^18.2.0", 30 | "react-dom": "^18.2.0", 31 | "scenejs": "^1.9.4", 32 | "sweetalert2": "^11.3.1", 33 | "url-loader": "^4.1.1", 34 | "video.js": "^7.20.3", 35 | "webxr-polyfill": "^2.0.3" 36 | }, 37 | "browserslist": { 38 | "production": [ 39 | ">0.5%", 40 | "not dead", 41 | "not op_mini all" 42 | ], 43 | "development": [ 44 | "last 1 chrome version", 45 | "last 1 firefox version", 46 | "last 1 safari version" 47 | ] 48 | }, 49 | "devDependencies": { 50 | "@docusaurus/module-type-aliases": "^2.1.0", 51 | "@tsconfig/docusaurus": "^1.0.6", 52 | "docusaurus-plugin-sass": "^0.2.2", 53 | "glslify-loader": "^2.0.0", 54 | "raw-loader": "^4.0.2", 55 | "remark-breaks": "^2.0.2", 56 | "sass": "^1.43.4", 57 | "sass-to-string": "^1.6.3", 58 | "webpack-font-preload-plugin": "^1.5.0" 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /packages/docs/plugin/glslify.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | 3 | module.exports = function() { 4 | return { 5 | name: "docusaurus-plugin-glslify", 6 | configureWebpack() { 7 | return { 8 | module: { 9 | rules: [ 10 | { 11 | test: /\.(glsl|vs|fs|vert|frag)$/, 12 | exclude: /node_modules/, 13 | use: ["raw-loader", "glslify-loader"] 14 | } 15 | ] 16 | } 17 | }; 18 | } 19 | }; 20 | }; 21 | -------------------------------------------------------------------------------- /packages/docs/plugin/preload-font.js: -------------------------------------------------------------------------------- 1 | const FontPreloadPlugin = require("webpack-font-preload-plugin"); 2 | 3 | module.exports = function() { 4 | return { 5 | name: "docusaurus-plugin-preload-font", 6 | configureWebpack() { 7 | return { 8 | plugins: [new FontPreloadPlugin({ 9 | extensions: ["woff2"] 10 | })] 11 | }; 12 | } 13 | }; 14 | }; 15 | -------------------------------------------------------------------------------- /packages/docs/polyfill.js: -------------------------------------------------------------------------------- 1 | // FIXME: This fails on deploy 2 | // import WebXRPolyfill from "webxr-polyfill"; 3 | 4 | // new WebXRPolyfill({ 5 | // webvr: false 6 | // }); 7 | -------------------------------------------------------------------------------- /packages/docs/showcase/data.ts: -------------------------------------------------------------------------------- 1 | const data: Array<{ 2 | repo: string; 3 | title: string; 4 | desc: string; 5 | thumb?: string; 6 | type?: "reactive" | "dom"; 7 | corePath?: string; 8 | reactPath?: string; 9 | vue2Path?: string; 10 | vue3Path?: string; 11 | sveltePath?: string; 12 | }> = [ 13 | { 14 | repo: "egjs-conveyer", 15 | title: "Conveyer", 16 | desc: "Drag gestures to your Native Scroll.", 17 | thumb: "conveyer.png", 18 | type: "reactive", 19 | corePath: "main/packages/conveyer/src/reactive.ts", 20 | reactPath: "main/packages/react-conveyer/src/react-conveyer/useConveyer.ts", 21 | vue2Path: "main/packages/vue2-conveyer/src/useConveyer.ts", 22 | vue3Path: "main/packages/vue-conveyer/src/vue-conveyer/useConveyer.ts", 23 | sveltePath: "main/packages/svelte-conveyer/src/svelte-conveyer/useConveyer.ts", 24 | }, 25 | { 26 | repo: "egjs-imready", 27 | title: "ImReady", 28 | desc: "I'm Ready to check if the images or videos are loaded.", 29 | thumb: "imready.png", 30 | type: "reactive", 31 | corePath: "main/packages/imready/src/reactive.ts", 32 | reactPath: "main/packages/react-imready/src/react-imready/useImReady.tsx", 33 | vue2Path: "main/packages/vue2-imready/src/useImReady.ts", 34 | vue3Path: "main/packages/vue-imready/src/vue-imready/useImReady.ts", 35 | sveltePath: "main/packages/svelte-imready/src/svelte-imready/useImReady.ts", 36 | }, 37 | { 38 | repo: "egjs-axes", 39 | title: "Axes", 40 | desc: "You can easily create a UI that responds to user actions.", 41 | thumb: "axes.png", 42 | type: "reactive", 43 | corePath: "master/packages/axes/src/reactive.ts", 44 | reactPath: "master/packages/react-axes/src/react-axes/useAxes.ts", 45 | vue2Path: "master/packages/vue2-axes/src/useAxes.ts", 46 | vue3Path: "master/packages/vue-axes/src/vue-axes/useAxes.ts", 47 | sveltePath: "master/packages/svelte-axes/src/svelte-axes/useAxes.ts", 48 | }, 49 | { 50 | repo: "egjs-flicking", 51 | title: "Flicking", 52 | desc: "It's reliable, flexible and extendable carousel.", 53 | thumb: "flicking.png", 54 | type: "dom", 55 | }, 56 | { 57 | repo: "egjs-infinitegrid", 58 | title: "InfiniteGrid", 59 | desc: "Arrange elements including content infinitely according to grid type.", 60 | thumb: "infinitegrid.png", 61 | type: "dom", 62 | }, 63 | { 64 | repo: "egjs-grid", 65 | title: "Grid", 66 | desc: "Arrange items according to the type of grids.", 67 | thumb: "grid.png", 68 | type: "dom", 69 | }, 70 | { 71 | repo: "egjs-view360", 72 | title: "View 360", 73 | desc: "360° panorama image / video viewer.", 74 | thumb: "view360.png", 75 | type: "dom", 76 | }, 77 | { 78 | repo: "egjs-view3d", 79 | title: "View 3D", 80 | desc: "Fast & Customizable glTF 3D model viewer.", 81 | thumb: "view3d.png", 82 | type: "dom", 83 | }, 84 | ]; 85 | 86 | export default data; 87 | -------------------------------------------------------------------------------- /packages/docs/showcase/layout/index.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | import React from "react"; 9 | import clsx from "clsx"; 10 | import Layout from "@theme/Layout"; 11 | import MDXContent from "@theme/MDXContent"; 12 | import type {Props} from "@theme/MDXPage"; 13 | import TOC from "@theme/TOC"; 14 | import Link from "@docusaurus/Link"; 15 | import { 16 | PageMetadata, 17 | HtmlClassNameProvider, 18 | ThemeClassNames 19 | } from "@docusaurus/theme-common"; 20 | 21 | import BackIcon from "@site/static/icon/back.svg"; 22 | import styles from "./styles.module.css"; 23 | 24 | export default function MDXPage(props: Props): JSX.Element { 25 | const {content: MDXPageContent} = props; 26 | const { 27 | metadata: {title, description, frontMatter} 28 | } = MDXPageContent; 29 | const {wrapperClassName, hide_table_of_contents: hideTableOfContents} = 30 | frontMatter; 31 | 32 | return ( 33 | 38 | 39 | 40 |
41 |
42 |
43 | 44 | 45 |

BACK

46 | 47 | 48 | 49 | 50 |
51 | {!hideTableOfContents && MDXPageContent.toc && ( 52 |
53 | 58 |
59 | )} 60 |
61 |
62 |
63 |
64 | ); 65 | } 66 | -------------------------------------------------------------------------------- /packages/docs/showcase/layout/styles.module.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | .mdxPageWrapper { 9 | justify-content: center; 10 | } 11 | 12 | .backLink { 13 | display: inline-flex; 14 | margin-bottom: 1rem; 15 | border-bottom: 2px solid transparent; 16 | } 17 | 18 | .backLink:hover { 19 | border-bottom-color: var(--ifm-color-primary); 20 | text-decoration: none; 21 | } 22 | 23 | .backIcon { 24 | width: 30px; 25 | height: 30px; 26 | margin-right: 0.5rem; 27 | fill: var(--ifm-color-primary); 28 | } 29 | 30 | .backLabel { 31 | display: inline-block; 32 | margin: 0; 33 | } 34 | 35 | .backLabel:hover { 36 | text-decoration: none; 37 | } 38 | -------------------------------------------------------------------------------- /packages/docs/showcase/pages/index.module.css: -------------------------------------------------------------------------------- 1 | .grid { 2 | display: grid; 3 | gap: 24px; 4 | grid-template-columns: repeat(auto-fill,minmax(280px,1fr)); 5 | } 6 | 7 | .title { 8 | font-size: var(--ifm-h4-font-size); 9 | font-weight: bold; 10 | margin-bottom: 0.5rem; 11 | } 12 | 13 | .desc { 14 | font-size: var(--ifm-h6-font-size); 15 | } 16 | 17 | .thumbWrapper { 18 | display: flex; 19 | background-size: cover; 20 | background-repeat: no-repeat; 21 | position: relative; 22 | } 23 | 24 | .thumb { 25 | width: 100%; 26 | height: 150px; 27 | object-fit: contain; 28 | } 29 | 30 | .codesTitle { 31 | margin-bottom: 10px; 32 | } 33 | .codes { 34 | display: flex; 35 | text-align: left; 36 | } 37 | .codes a { 38 | width: 100%; 39 | } 40 | [data-theme="dark"] .thumb { 41 | filter: invert(100%); 42 | } 43 | -------------------------------------------------------------------------------- /packages/docs/showcase/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Layout from "@theme/Layout"; 3 | import Link from "@docusaurus/Link"; 4 | import { useBaseUrlUtils } from "@docusaurus/useBaseUrl"; 5 | import data from "../data"; 6 | import styles from "./index.module.css"; 7 | 8 | function Card(props: typeof data[number]) { 9 | const { withBaseUrl } = useBaseUrlUtils(); 10 | 11 | return ( 12 |
13 |
14 |
15 | 23 |
24 |
25 |
26 |
27 | 28 | {props.title} 29 | 30 |
31 |
32 | {props.desc} 33 |
34 |
35 |
36 | {props.type === "reactive" && ( 37 | <> 38 |

Codes

39 |
40 | Core 41 | React 42 | Vue 2 43 | Vue 3 44 | Svelte 45 |
46 | 47 | )} 48 |
49 |
50 | ); 51 | } 52 | export default function Page(): JSX.Element { 53 | return ( 54 | 55 |
56 |

CFCs Reactive Components

57 |
58 | {data 59 | .filter((item) => item.type === "reactive") 60 | .map((item, idx) => ( 61 | 62 | ))} 63 |
64 |

65 |

CFCs DOM Components

66 |
67 | {data 68 | .filter((item) => item.type === "dom") 69 | .map((item, idx) => ( 70 | 71 | ))} 72 |
73 |
74 |
75 | ); 76 | } 77 | -------------------------------------------------------------------------------- /packages/docs/sidebars.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | docs: [ 3 | { 4 | type: "autogenerated", 5 | dirName: "tutorials" 6 | }, 7 | ], 8 | examples: [ 9 | { 10 | type: "autogenerated", 11 | dirName: "examples" 12 | }, 13 | ], 14 | api: [{ type: "autogenerated", dirName: "api" }], 15 | }; 16 | -------------------------------------------------------------------------------- /packages/docs/src/components/EasilyReactiveComponent.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import TabItem from "@theme/TabItem"; 3 | import CodeBlock from "@theme/CodeBlock"; 4 | import { FrameworkTabs } from "./FrameworkTabs"; 5 | 6 | export default () => { 7 | return ( 8 | 9 | 10 | 11 | npm install @cfcs/react 12 | 13 | 14 | {`import { useReactive } from "@cfcs/react"; 15 | 16 | export function useReactiveComponent() { 17 | return useReactive(REACTIVE_ADAPTER); 18 | }`} 19 | 20 | 21 | 22 | 23 | npm install @cfcs/vue2 24 | 25 | 26 | {`import { useReactive } from "@cfcs/vue2"; 27 | 28 | export function useReactiveComponent() { 29 | return useReactive(REACTIVE_ADAPTER); 30 | }`} 31 | 32 | 33 | 34 | 35 | npm install @cfcs/vue3 36 | 37 | 38 | {`import { useReactive } from "@cfcs/vue3"; 39 | 40 | export function useReactiveComponent() { 41 | return useReactive(REACTIVE_ADAPTER); 42 | }`} 43 | 44 | 45 | 46 | 47 | npm install @cfcs/svelte 48 | 49 | 50 | {`import { useReactive } from "@cfcs/svelte"; 51 | 52 | export function useReactiveComponent() { 53 | return useReactive(REACTIVE_ADAPTER); 54 | }`} 55 | 56 | 57 | 58 | ); 59 | }; 60 | -------------------------------------------------------------------------------- /packages/docs/src/components/FrameworkTabs.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Tabs from "@theme/Tabs"; 3 | import TabItem from "@theme/TabItem"; 4 | import CodeBlock from "@theme/CodeBlock"; 5 | import { 6 | IconBrandNpm, 7 | IconBrandReact, 8 | IconBrandAngular, 9 | IconBrandVue, 10 | IconBrandSvelte 11 | } from "@tabler/icons"; 12 | 13 | export interface FrameworkTabsProps { 14 | children: any[]; 15 | tabs?: string[]; 16 | } 17 | 18 | const FRAMEWORK_INFOS: Record = { 19 | react: { 20 | icon: , 21 | title: "React", 22 | }, 23 | angular: { 24 | icon: , 25 | title: "Angular", 26 | }, 27 | vue2: { 28 | icon: , 29 | title: "Vue@2", 30 | }, 31 | vue3: { 32 | icon: , 33 | title: "Vue@3", 34 | }, 35 | svelte: { 36 | icon: , 37 | title: "Svelte", 38 | }, 39 | } 40 | export function FrameworkTabs(props: FrameworkTabsProps) { 41 | const { 42 | tabs = ["react", "vue2", "vue3", "svelte"], 43 | } = props; 44 | return { 49 | const info = FRAMEWORK_INFOS[tabName]; 50 | 51 | return { 52 | label:
53 | {info.icon} {info.title} 54 |
, 55 | value: tabName, 56 | attributes: { 57 | "data-framework": tabName, 58 | } 59 | }; 60 | })} children={props.children} /> 61 | } 62 | -------------------------------------------------------------------------------- /packages/docs/src/components/VueFrameworkTabItems.tsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naver/cfcs/798b4fb2220cf0562313d2f87c36d7173cd070e1/packages/docs/src/components/VueFrameworkTabItems.tsx -------------------------------------------------------------------------------- /packages/docs/src/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Home from "./Home"; 3 | 4 | export default () => { 5 | return ; 6 | } 7 | -------------------------------------------------------------------------------- /packages/docs/src/shim.d.ts: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | declare global { 4 | declare module "*.mdx" { 5 | import type {ComponentType} from 'react'; 6 | 7 | const ReactComponent: ComponentType; 8 | 9 | export default ReactComponent; 10 | } 11 | declare module "*.md" { 12 | import type {ComponentType} from 'react'; 13 | 14 | const ReactComponent: ComponentType; 15 | 16 | export default ReactComponent; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/docs/src/styles/global.css: -------------------------------------------------------------------------------- 1 | @font-face 2 | { 3 | font-family: 'Staatliches'; 4 | font-weight: normal; 5 | font-style: normal; 6 | src: url('/font/Staatliches/Staatliches-Regular.ttf'); 7 | } 8 | 9 | html { 10 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; 11 | } 12 | 13 | main { 14 | font-size: 16px; 15 | font-weight: 500; 16 | line-height: 26px; 17 | letter-spacing: normal; 18 | } 19 | 20 | main a { 21 | text-decoration-thickness: 2px; 22 | text-underline-offset: 7px; 23 | } 24 | 25 | main a:hover { 26 | text-decoration-thickness: 2px; 27 | } 28 | 29 | .header-github-link:hover, 30 | .header-npm-link:hover { 31 | opacity: 0.6; 32 | } 33 | 34 | .header-github-link::before, 35 | .header-npm-link::before { 36 | content: ''; 37 | width: 24px; 38 | height: 24px; 39 | display: flex; 40 | } 41 | 42 | .header-github-link { 43 | margin-right: 0.5rem; 44 | } 45 | 46 | .header-github-link::before { 47 | background: url("../../static/icon/github.svg") no-repeat; 48 | } 49 | 50 | html[data-theme='dark'] .header-github-link::before { 51 | background: url("../../static/icon/github_white.svg") no-repeat; 52 | } 53 | 54 | .header-npm-link::before { 55 | background: url("../../static/icon/npm.svg") no-repeat; 56 | } 57 | 58 | 59 | .framework-wrapper { 60 | display: flex; 61 | align-items: center; 62 | justify-content: center; 63 | } 64 | 65 | .framework-wrapper svg { 66 | margin-right: 0.25rem; 67 | } 68 | -------------------------------------------------------------------------------- /packages/docs/static/font/Staatliches/Staatliches-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naver/cfcs/798b4fb2220cf0562313d2f87c36d7173cd070e1/packages/docs/static/font/Staatliches/Staatliches-Regular.ttf -------------------------------------------------------------------------------- /packages/docs/static/img/egjs_white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 9 | 10 | 11 | 14 | 17 | 19 | 20 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /packages/docs/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naver/cfcs/798b4fb2220cf0562313d2f87c36d7173cd070e1/packages/docs/static/img/favicon.ico -------------------------------------------------------------------------------- /packages/docs/static/img/nopensource.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naver/cfcs/798b4fb2220cf0562313d2f87c36d7173cd070e1/packages/docs/static/img/nopensource.png -------------------------------------------------------------------------------- /packages/docs/static/logos/Untitled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naver/cfcs/798b4fb2220cf0562313d2f87c36d7173cd070e1/packages/docs/static/logos/Untitled.png -------------------------------------------------------------------------------- /packages/docs/static/logos/axes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naver/cfcs/798b4fb2220cf0562313d2f87c36d7173cd070e1/packages/docs/static/logos/axes.png -------------------------------------------------------------------------------- /packages/docs/static/logos/conveyer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naver/cfcs/798b4fb2220cf0562313d2f87c36d7173cd070e1/packages/docs/static/logos/conveyer.png -------------------------------------------------------------------------------- /packages/docs/static/logos/flicking.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naver/cfcs/798b4fb2220cf0562313d2f87c36d7173cd070e1/packages/docs/static/logos/flicking.png -------------------------------------------------------------------------------- /packages/docs/static/logos/grid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naver/cfcs/798b4fb2220cf0562313d2f87c36d7173cd070e1/packages/docs/static/logos/grid.png -------------------------------------------------------------------------------- /packages/docs/static/logos/imready.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naver/cfcs/798b4fb2220cf0562313d2f87c36d7173cd070e1/packages/docs/static/logos/imready.png -------------------------------------------------------------------------------- /packages/docs/static/logos/infinitegrid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naver/cfcs/798b4fb2220cf0562313d2f87c36d7173cd070e1/packages/docs/static/logos/infinitegrid.png -------------------------------------------------------------------------------- /packages/docs/static/logos/view360.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naver/cfcs/798b4fb2220cf0562313d2f87c36d7173cd070e1/packages/docs/static/logos/view360.png -------------------------------------------------------------------------------- /packages/docs/static/logos/view3d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naver/cfcs/798b4fb2220cf0562313d2f87c36d7173cd070e1/packages/docs/static/logos/view3d.png -------------------------------------------------------------------------------- /packages/docs/static/tutorials/cfcs-compatible.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naver/cfcs/798b4fb2220cf0562313d2f87c36d7173cd070e1/packages/docs/static/tutorials/cfcs-compatible.png -------------------------------------------------------------------------------- /packages/docs/static/tutorials/cfcs-dom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naver/cfcs/798b4fb2220cf0562313d2f87c36d7173cd070e1/packages/docs/static/tutorials/cfcs-dom.png -------------------------------------------------------------------------------- /packages/docs/static/tutorials/cfcs-lifecycle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naver/cfcs/798b4fb2220cf0562313d2f87c36d7173cd070e1/packages/docs/static/tutorials/cfcs-lifecycle.png -------------------------------------------------------------------------------- /packages/docs/static/tutorials/cfcs-reactive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naver/cfcs/798b4fb2220cf0562313d2f87c36d7173cd070e1/packages/docs/static/tutorials/cfcs-reactive.png -------------------------------------------------------------------------------- /packages/docs/static/tutorials/cfcs-typescript.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naver/cfcs/798b4fb2220cf0562313d2f87c36d7173cd070e1/packages/docs/static/tutorials/cfcs-typescript.png -------------------------------------------------------------------------------- /packages/docs/static/tutorials/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naver/cfcs/798b4fb2220cf0562313d2f87c36d7173cd070e1/packages/docs/static/tutorials/logo.png -------------------------------------------------------------------------------- /packages/docs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/docusaurus/tsconfig.json", 3 | "compilerOptions": { 4 | "baseUrl": "." 5 | }, 6 | "include": [ 7 | "../src/*", 8 | "./**/*.ts", 9 | "./**/*.tsx" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /packages/react/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## 0.1.0 (2023-02-24) 7 | 8 | 9 | ### :rocket: New Features 10 | 11 | * v0.1 ([c33732a](https://github.com/naver/cfcs/commit/c33732a6bc7fba6e3e5746ecc5b3d94df582b39e)) 12 | 13 | 14 | ### :memo: Documentation 15 | 16 | * fix README ([6d479bd](https://github.com/naver/cfcs/commit/6d479bda55fbcbdd10dd7d90595070003e9d0f22)) 17 | 18 | 19 | ### :mega: Other 20 | 21 | * fix packages' url ([846fcf2](https://github.com/naver/cfcs/commit/846fcf2a666f3085778d9e8b7f8f533a32c4eb4c)) 22 | * update packages versions ([ae79c40](https://github.com/naver/cfcs/commit/ae79c402ac11c307f78a1371689e3e4d0eaa7f20)) 23 | -------------------------------------------------------------------------------- /packages/react/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2022-present NAVER Corp. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /packages/react/README.md: -------------------------------------------------------------------------------- 1 | 2 |

3 | 4 |

5 |
6 | Cross Framework Components for React 7 |

8 | 9 |

Write once, create framework components that supports React, Vue, Svelte, and more.

10 | 11 |

12 |   13 |   14 | GitHub  15 |

16 | 17 |

18 | Demo / API / Tutorials 19 |

20 | 21 | ## ⚙️ Installation 22 | ```sh 23 | $ npm install @cfcs/react 24 | ``` 25 | 26 | ## 🏃 How to use 27 | 28 | React Component can be written by using `useReactive` through `REACTIVE_ADAPTER`([made with @cfcs/core](https://github.com/naver/cfcs/blob/main/reactive.md)) constant. 29 | 30 | ### Adapter 31 | ```js 32 | import { reactive } from "@cfcs/core"; 33 | 34 | const REACTIVE_ADAPTER = ({ 35 | emit, 36 | onInit, 37 | setEvents, 38 | setMethods, 39 | }) => { 40 | setEvents(["event1"]); 41 | setEvents(["method1"]); 42 | 43 | const obj = reactive({ 44 | value1: 1, 45 | method1() { 46 | console.log("method1"); 47 | }, 48 | }); 49 | 50 | const inst = new YourModule(); 51 | 52 | onInit(() => { 53 | requestAnimationFrame(() => { 54 | value1.current = 2; 55 | 56 | // emit `event1` event externally 57 | emit("event1", e); 58 | }); 59 | }); 60 | 61 | // Returns a reactive value. 62 | return obj; 63 | }; 64 | ``` 65 | ### Component 66 | ```ts 67 | import { REACTIVE_ADAPTER } from "your-module"; 68 | import { useReactive, ReactiveAdapterResult } from "@cfcs/react"; 69 | 70 | // The result value of useReactive can be exposed as a type. 71 | export type ReactiveComponentResult = ReactiveAdapterResult; 72 | 73 | // Reactive Component can be created through useReactive. 74 | export function useReactiveComponent(props) { 75 | return useReactive(REACTIVE_ADAPTER, () => props); 76 | } 77 | ``` 78 | 79 | ### App 80 | ```js 81 | export function App() { 82 | const { 83 | // state 84 | value1, 85 | // events 86 | onEvent1, 87 | // methods 88 | method1, 89 | } = useReactiveComponent({}); 90 | 91 | // use state 92 | console.log(value1); 93 | 94 | // use event 95 | onEvent1(e => { 96 | console.log(e); 97 | }, []); 98 | 99 | // use method 100 | method1(); 101 | } 102 | ``` 103 | 104 | 105 | 106 | ## 📝 Feedback 107 | Please file an [Issue](https://github.com/naver/cfcs/issues). 108 | 109 | ## 📜 License 110 | `cfcs` is released under the [MIT license](https://github.com/naver/cfcs/blob/main/LICENSE). 111 | 112 | ``` 113 | CFCs 114 | Copyright (c) 2023-present NAVER Corp. 115 | 116 | Permission is hereby granted, free of charge, to any person obtaining a copy 117 | of this software and associated documentation files (the "Software"), to deal 118 | in the Software without restriction, including without limitation the rights 119 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 120 | copies of the Software, and to permit persons to whom the Software is 121 | furnished to do so, subject to the following conditions: 122 | 123 | The above copyright notice and this permission notice shall be included in 124 | all copies or substantial portions of the Software. 125 | 126 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 127 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 128 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 129 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 130 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 131 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 132 | THE SOFTWARE. 133 | ``` 134 | -------------------------------------------------------------------------------- /packages/react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@cfcs/react", 3 | "version": "0.1.0", 4 | "description": "Write once, create framework components that supports React, Vue, Svelte, and more.", 5 | "main": "dist/cfcs.cjs.js", 6 | "module": "dist/cfcs.esm.js", 7 | "types": "declaration/index.d.ts", 8 | "sideEffects": false, 9 | "scripts": { 10 | "start": "react-scripts start", 11 | "build": "rollup -c && npm run declaration && print-sizes ./dist ", 12 | "declaration": "rm -rf declaration && tsc -p tsconfig.declaration.json" 13 | }, 14 | "homepage": "https://naver.github.io/cfcs", 15 | "license": "MIT", 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/naver/cfcs/tree/main/packages/react" 19 | }, 20 | "author": { 21 | "name": "NAVER Corp." 22 | }, 23 | "keywords": [ 24 | "cfcs", 25 | "cfc", 26 | "react", 27 | "react-hook", 28 | "react-hooks", 29 | "react-use", 30 | "reactive", 31 | "hooks" 32 | ], 33 | "files": [ 34 | "./*", 35 | "dist/*" 36 | ], 37 | "dependencies": { 38 | "@cfcs/core": "~0.1.0" 39 | }, 40 | "devDependencies": { 41 | "@egjs/build-helper": "^0.1.2", 42 | "@types/react": "^18.0.15", 43 | "@types/react-dom": "^18.0.6", 44 | "print-sizes": "^0.1.0", 45 | "react": "^18.2.0", 46 | "react-dom": "^18.2.0", 47 | "typescript": "^4.7.4" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /packages/react/rollup.config.js: -------------------------------------------------------------------------------- 1 | const buildHelper = require("@egjs/build-helper"); 2 | 3 | export default buildHelper([ 4 | { 5 | input: "./src/index.ts", 6 | output: "./dist/cfcs.cjs.js", 7 | format: "cjs", 8 | exports: "named", 9 | }, 10 | { 11 | input: "./src/index.ts", 12 | output: "./dist/cfcs.esm.js", 13 | format: "esm", 14 | exports: "named", 15 | }, 16 | ]); 17 | 18 | -------------------------------------------------------------------------------- /packages/react/src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * cfcs 3 | * Copyright (c) 2022-present NAVER Corp. 4 | * MIT license 5 | */ 6 | export * from "./reactive"; 7 | -------------------------------------------------------------------------------- /packages/react/src/reactive/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * cfcs 3 | * Copyright (c) 2022-present NAVER Corp. 4 | * MIT license 5 | */ 6 | export * from "./types"; 7 | export * from "./useReactive"; 8 | -------------------------------------------------------------------------------- /packages/react/src/reactive/types.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * cfcs 3 | * Copyright (c) 2022-present NAVER Corp. 4 | * MIT license 5 | */ 6 | import { ReactiveAdapterParam, ReactiveEventParameters, ReactiveState, ReactiveSubscribe } from "@cfcs/core"; 7 | 8 | /** 9 | * @category Reactive 10 | */ 11 | export type ReactiveEvents< 12 | Events extends Record 13 | > = { 14 | [key in keyof Events as `on${Capitalize}`]: (effect: ((...args: ReactiveEventParameters) => void), deps?: readonly any[]) => void; 15 | }; 16 | 17 | /** 18 | * @category Reactive 19 | */ 20 | export type ReactiveResult< 21 | Instance extends ReactiveSubscribe>, 22 | State extends Record = ReactiveState, 23 | Methods extends keyof Partial = any, 24 | Events extends Record = {}, 25 | > = State & { [key in Methods]: Instance[key] } & ReactiveEvents; 26 | 27 | /** 28 | * Get the result type of reactive component through adapter. 29 | * @category Reactive 30 | * @see useReactive 31 | * @example 32 | * ```ts 33 | * import { ReactiveAdapterResult } from "@cfcs/react"; 34 | * 35 | * type ReactiveComponentResult = ReactiveAdapterResult; 36 | * ``` 37 | */ 38 | export type ReactiveAdapterResult< 39 | Adapter extends ReactiveAdapterParam, 40 | > 41 | = Adapter extends ReactiveAdapterParam 42 | ? ReactiveResult : {}; 43 | 44 | 45 | 46 | // Names using framework prefix 47 | /** 48 | * @category Reactive 49 | * @hidden 50 | */ 51 | export type ReactReactiveEvents> = ReactiveEvents; 52 | /** 53 | * @category Reactive 54 | * @hidden 55 | */ 56 | export type ReactReactiveResult< 57 | Instance extends ReactiveSubscribe>, 58 | State extends Record = ReactiveState, 59 | Methods extends keyof Partial = any, 60 | Events extends Record = {}, 61 | > = ReactiveResult< 62 | Instance, 63 | State, 64 | Methods, 65 | Events 66 | >; 67 | /** 68 | * @category Reactive 69 | * @hidden 70 | */ 71 | export type ReactReactiveAdapterResult< 72 | Adapter extends ReactiveAdapterParam 73 | > = ReactiveAdapterResult; 74 | -------------------------------------------------------------------------------- /packages/react/src/reactive/useReactive.ts: -------------------------------------------------------------------------------- 1 | import { ReactiveSubscribe, ReactiveAdapterParam, keys, camelize, adaptReactive, ReactiveState } from "@cfcs/core"; 2 | import { useEffect, useState, useMemo, useRef } from "react"; 3 | import { ReactiveResult } from "./types"; 4 | 5 | /** 6 | * @description In React, you can create reactive components through adapters. 7 | * @category Reactive 8 | * @example 9 | * ```ts 10 | * import { useReactive } from "@cfcs/react"; 11 | * 12 | * export function useReactiveComponent() { 13 | * return useReactive(REACTIVE_ADAPTER); 14 | * } 15 | * ``` 16 | */ 17 | export function useReactive< 18 | Instance extends ReactiveSubscribe>, 19 | State extends Record = ReactiveState, 20 | Methods extends keyof Partial = any, 21 | Props = any, 22 | Events extends Record = {}, 23 | >( 24 | reactiveAdapter: ReactiveAdapterParam, 25 | props?: () => Props, 26 | ): ReactiveResult { 27 | const readRef = useRef(true); 28 | const adaptResult = useMemo(() => adaptReactive(reactiveAdapter, props), []); 29 | const reactiveState = adaptResult.state(); 30 | const names = keys>(reactiveState); 31 | const [states] = useState void; 35 | }>>({}); 36 | for (const name in reactiveState) { 37 | // eslint-disable-next-line react-hooks/rules-of-hooks 38 | const state = useState(() => reactiveState[name]); 39 | states[name] = { 40 | getter: false, 41 | set: state[1], 42 | value: state[0], 43 | }; 44 | } 45 | 46 | 47 | readRef.current = true; 48 | 49 | const methods = useMemo(() => adaptResult.methods(), []); 50 | 51 | useEffect(() => { 52 | readRef.current = false; 53 | }); 54 | useEffect(() => { 55 | adaptResult.mounted(); 56 | 57 | const inst = adaptResult.instance(); 58 | 59 | names.forEach((name) => { 60 | (inst as any).subscribe(name, (value: any) => { 61 | states[name].value = value; 62 | 63 | if (states[name].getter) { 64 | states[name].set(value); 65 | } 66 | }); 67 | }); 68 | 69 | adaptResult.init(); 70 | return () => { 71 | adaptResult.destroy(); 72 | }; 73 | // eslint-disable-next-line react-hooks/exhaustive-deps 74 | }, []); 75 | 76 | 77 | 78 | const result = names.reduce((result, name) => { 79 | if (!methods[name]) { 80 | Object.defineProperty(result, name, { 81 | enumerable: true, 82 | get() { 83 | if (readRef.current) { 84 | states[name].getter = true; 85 | } 86 | return states[name].value; 87 | }, 88 | }); 89 | } 90 | return result; 91 | }, {}); 92 | 93 | const reactiveEvents = adaptResult.events(); 94 | 95 | reactiveEvents.forEach(name => { 96 | result[camelize(`on ${name as any}`)] = (callback: (e: any) => void, dependencies?: readonly any[]) => { 97 | // eslint-disable-next-line react-hooks/rules-of-hooks 98 | const listener = useMemo(() => { 99 | adaptResult.on(name as any, callback as any); 100 | return callback; 101 | }, dependencies); 102 | 103 | useEffect(() => { 104 | adaptResult.off(name as any, listener as any); 105 | adaptResult.on(name as any, listener as any); 106 | 107 | return () => { 108 | adaptResult.off(name as any, listener as any); 109 | }; 110 | // eslint-disable-next-line react-hooks/exhaustive-deps 111 | }, [listener]); 112 | }; 113 | }); 114 | 115 | keys(methods).forEach(name => { 116 | result[name] = methods[name]; 117 | }); 118 | return result; 119 | } 120 | -------------------------------------------------------------------------------- /packages/react/tsconfig.declaration.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig", 3 | "compilerOptions": { 4 | "allowJs": false, 5 | "noEmit": false, 6 | "isolatedModules": false, 7 | "removeComments": true, 8 | "declaration": true, 9 | "emitDeclarationOnly": true, 10 | "declarationDir": "declaration" 11 | }, 12 | "include": [ 13 | "./src" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /packages/react/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "noFallthroughCasesInSwitch": true, 16 | "module": "esnext", 17 | "moduleResolution": "node", 18 | "resolveJsonModule": true, 19 | "isolatedModules": true, 20 | "noEmit": true, 21 | "jsx": "react-jsx" 22 | }, 23 | "include": [ 24 | "src" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /packages/svelte/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | /public/build/ 3 | 4 | .DS_Store 5 | -------------------------------------------------------------------------------- /packages/svelte/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## 0.1.0 (2023-02-24) 7 | 8 | 9 | ### :rocket: New Features 10 | 11 | * v0.1 ([c33732a](https://github.com/naver/cfcs/commit/c33732a6bc7fba6e3e5746ecc5b3d94df582b39e)) 12 | 13 | 14 | ### :memo: Documentation 15 | 16 | * fix README ([6d479bd](https://github.com/naver/cfcs/commit/6d479bda55fbcbdd10dd7d90595070003e9d0f22)) 17 | 18 | 19 | ### :mega: Other 20 | 21 | * fix packages' url ([846fcf2](https://github.com/naver/cfcs/commit/846fcf2a666f3085778d9e8b7f8f533a32c4eb4c)) 22 | * update packages versions ([ae79c40](https://github.com/naver/cfcs/commit/ae79c402ac11c307f78a1371689e3e4d0eaa7f20)) 23 | -------------------------------------------------------------------------------- /packages/svelte/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2022-present NAVER Corp. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /packages/svelte/README.md: -------------------------------------------------------------------------------- 1 | 2 |

3 | 4 |

5 |
6 | Cross Framework Components for Svelte 7 |

8 | 9 |

10 |   11 |   12 | GitHub  13 |

14 |

Write once, create framework components that supports React, Vue, Svelte, and more.

15 | 16 | 17 | ## ⚙️ Installation 18 | ```sh 19 | $ npm install @cfcs/svelte 20 | ``` 21 | 22 | ## 🏃 How to use 23 | 24 | Svelte Component can be written by using `useReactive` through `REACTIVE_ADAPTER`([made with @cfcs/core](https://github.com/naver/cfcs/blob/main/reactive.md)) constant. 25 | 26 | ### Adapter 27 | ```js 28 | import { reactive } from "@cfcs/core"; 29 | 30 | const REACTIVE_ADAPTER = ({ 31 | emit, 32 | onInit, 33 | setEvents, 34 | setMethods, 35 | }) => { 36 | setEvents(["event1"]); 37 | setEvents(["method1"]); 38 | 39 | const obj = reactive({ 40 | value1: 1, 41 | method1() { 42 | console.log("method1"); 43 | }, 44 | }); 45 | 46 | const inst = new YourModule(); 47 | 48 | onInit(() => { 49 | requestAnimationFrame(() => { 50 | value1.current = 2; 51 | 52 | // emit `event1` event externally 53 | emit("event1", e); 54 | }); 55 | }); 56 | 57 | // Returns a reactive value. 58 | return obj; 59 | }; 60 | ``` 61 | ### Component 62 | ```ts 63 | import { REACTIVE_ADAPTER } from "your-module"; 64 | import { useReactive, ReactiveAdapterResult } from "@cfcs/svelte"; 65 | 66 | // The result value of useReactive can be exposed as a type. 67 | export type ReactiveComponentResult = ReactiveAdapterResult; 68 | 69 | // Reactive Component can be created through useReactive. 70 | export function useReactiveComponent(props) { 71 | return useReactive(REACTIVE_ADAPTER, () => props); 72 | } 73 | ``` 74 | 75 | ### App 76 | ```html 77 | 98 | ``` 99 | 100 | 101 | 102 | ## 📝 Feedback 103 | Please file an [Issue](https://github.com/naver/cfcs/issues). 104 | 105 | ## 📜 License 106 | `cfcs` is released under the [MIT license](https://github.com/naver/cfcs/blob/main/LICENSE). 107 | 108 | ``` 109 | CFCs 110 | Copyright (c) 2023-present NAVER Corp. 111 | 112 | Permission is hereby granted, free of charge, to any person obtaining a copy 113 | of this software and associated documentation files (the "Software"), to deal 114 | in the Software without restriction, including without limitation the rights 115 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 116 | copies of the Software, and to permit persons to whom the Software is 117 | furnished to do so, subject to the following conditions: 118 | 119 | The above copyright notice and this permission notice shall be included in 120 | all copies or substantial portions of the Software. 121 | 122 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 123 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 124 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 125 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 126 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 127 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 128 | THE SOFTWARE. 129 | ``` 130 | -------------------------------------------------------------------------------- /packages/svelte/global.d.ts: -------------------------------------------------------------------------------- 1 | declare module "!!raw-loader!*" { 2 | const content: string; 3 | export default content; 4 | } 5 | declare module "*.svelte" { 6 | const content: any; 7 | export default content; 8 | } 9 | -------------------------------------------------------------------------------- /packages/svelte/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@cfcs/svelte", 3 | "version": "0.1.0", 4 | "description": "Write once, create framework components that supports React, Vue, Svelte, and more.", 5 | "main": "dist/cfcs.cjs.js", 6 | "module": "dist/cfcs.esm.js", 7 | "svelte": "dist/cfcs.esm.js", 8 | "types": "declaration/index.d.ts", 9 | "sideEffects": false, 10 | "scripts": { 11 | "dev": "rollup -c -w", 12 | "start": "sirv public --no-clear", 13 | "build": "rollup -c rollup.build.config.js && npm run declaration", 14 | "declaration": "rm -rf declaration && tsc -p tsconfig.declaration.json" 15 | }, 16 | "license": "MIT", 17 | "homepage": "https://naver.github.io/cfcs", 18 | "repository": { 19 | "type": "git", 20 | "url": "https://github.com/naver/cfcs/tree/main/packages/svelte" 21 | }, 22 | "author": { 23 | "name": "NAVER Corp." 24 | }, 25 | "keywords": [ 26 | "cfcs", 27 | "cfc", 28 | "svelte", 29 | "svelte-hook", 30 | "svelte-hooks", 31 | "svelte-use", 32 | "scroll", 33 | "reactive", 34 | "hooks", 35 | "use" 36 | ], 37 | "files": [ 38 | "./*", 39 | "dist/*" 40 | ], 41 | "dependencies": { 42 | "@cfcs/core": "~0.1.0" 43 | }, 44 | "devDependencies": { 45 | "@babel/core": "^7.12.10", 46 | "@babel/preset-env": "^7.12.11", 47 | "@egjs/build-helper": "^0.1.2", 48 | "@pyoner/svelte-ts-preprocess": "^1.2.1", 49 | "@testing-library/jest-dom": "^5.11.8", 50 | "@testing-library/svelte": "^3.0.3", 51 | "@types/jest": "^26.0.19", 52 | "babel-jest": "^26.6.3", 53 | "jest": "^26.6.3", 54 | "print-coveralls": "^1.2.2", 55 | "rollup": "^1.12.0", 56 | "rollup-plugin-css-only": "^3.1.0", 57 | "rollup-plugin-livereload": "^1.0.0", 58 | "rollup-plugin-svelte": "^7.0.0", 59 | "rollup-plugin-terser": "^5.1.2", 60 | "sirv-cli": "^0.4.4", 61 | "svelte": "^3.31.0", 62 | "svelte-jester": "^1.3.0", 63 | "svelte-preprocess": "^4.6.1", 64 | "ts-jest": "^26.4.4", 65 | "tslib": "^1.10.0", 66 | "typescript": "^4.1.6" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /packages/svelte/rollup.build.config.js: -------------------------------------------------------------------------------- 1 | import buildHelper from "@egjs/build-helper"; 2 | import svelte from "rollup-plugin-svelte"; 3 | import sveltePreprocess from "svelte-preprocess"; 4 | 5 | const defaultOptions = { 6 | tsconfig: "", 7 | commonjs: true, 8 | external: { 9 | svelte: "svelte", 10 | }, 11 | plugins: [ 12 | svelte({ 13 | preprocess: sveltePreprocess(), 14 | }), 15 | ], 16 | }; 17 | 18 | export default buildHelper([ 19 | { 20 | ...defaultOptions, 21 | input: "./src/index.ts", 22 | output: "dist/cfcs.cjs.js", 23 | format: "cjs", 24 | exports: "named", 25 | }, 26 | { 27 | ...defaultOptions, 28 | input: "./src/index.ts", 29 | output: "dist/cfcs.esm.js", 30 | format: "es", 31 | exports: "named", 32 | }, 33 | ]); 34 | -------------------------------------------------------------------------------- /packages/svelte/rollup.config.js: -------------------------------------------------------------------------------- 1 | import svelte from 'rollup-plugin-svelte'; 2 | import commonjs from 'rollup-plugin-commonjs'; 3 | import resolve from 'rollup-plugin-node-resolve'; 4 | import livereload from 'rollup-plugin-livereload'; 5 | import { terser } from 'rollup-plugin-terser'; 6 | import sveltePreprocess from 'svelte-preprocess'; 7 | import typescript from 'rollup-plugin-typescript'; 8 | import css from 'rollup-plugin-css-only'; 9 | 10 | const production = !process.env.ROLLUP_WATCH; 11 | 12 | function serve() { 13 | let server; 14 | 15 | function toExit() { 16 | if (server) server.kill(0); 17 | } 18 | 19 | return { 20 | writeBundle() { 21 | if (server) return; 22 | server = require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], { 23 | stdio: ['ignore', 'inherit', 'inherit'], 24 | shell: true 25 | }); 26 | 27 | process.on('SIGTERM', toExit); 28 | process.on('exit', toExit); 29 | } 30 | }; 31 | } 32 | 33 | export default { 34 | input: 'src/main.js', 35 | output: { 36 | sourcemap: true, 37 | format: 'iife', 38 | name: 'app', 39 | file: 'public/build/bundle.js' 40 | }, 41 | plugins: [ 42 | svelte({ 43 | preprocess: sveltePreprocess(), 44 | compilerOptions: { 45 | // enable run-time checks when not in production 46 | dev: !production 47 | } 48 | }), 49 | // we'll extract any component CSS out into 50 | // a separate file - better for performance 51 | css({ output: 'bundle.css' }), 52 | 53 | // If you have external dependencies installed from 54 | // npm, you'll most likely need these plugins. In 55 | // some cases you'll need additional configuration - 56 | // consult the documentation for details: 57 | // https://github.com/rollup/plugins/tree/master/packages/commonjs 58 | resolve({ 59 | browser: true, 60 | dedupe: ['svelte'] 61 | }), 62 | commonjs(), 63 | typescript({ 64 | sourceMap: !production, 65 | inlineSources: !production 66 | }), 67 | 68 | // In dev mode, call `npm run start` once 69 | // the bundle has been generated 70 | !production && serve(), 71 | 72 | // Watch the `public` directory and refresh the 73 | // browser on changes when not in production 74 | !production && livereload('public'), 75 | 76 | // If we're building for production (npm run build 77 | // instead of npm run dev), minify 78 | production && terser() 79 | ], 80 | watch: { 81 | clearScreen: false 82 | } 83 | }; 84 | -------------------------------------------------------------------------------- /packages/svelte/src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * cfcs 3 | * Copyright (c) 2022-present NAVER Corp. 4 | * MIT license 5 | */ 6 | export * from "./reactive"; 7 | -------------------------------------------------------------------------------- /packages/svelte/src/reactive/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * cfcs 3 | * Copyright (c) 2022-present NAVER Corp. 4 | * MIT license 5 | */ 6 | export * from "./types"; 7 | export * from "./useReactive"; 8 | -------------------------------------------------------------------------------- /packages/svelte/src/reactive/types.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * cfcs 3 | * Copyright (c) 2022-present NAVER Corp. 4 | * MIT license 5 | */ 6 | import { Writable } from "svelte/store"; 7 | import { ReactiveAdapterParam, ReactiveEventParameters, ReactiveState, ReactiveSubscribe } from "@cfcs/core"; 8 | 9 | /** 10 | * @typedef 11 | * @category Reactive 12 | */ 13 | export type ReactiveEvents< 14 | Events extends Record 15 | > = { 16 | [K in keyof Events as `on${Capitalize}`]: (callback: ((...args: ReactiveEventParameters) => void)) => void; 17 | }; 18 | 19 | 20 | /** 21 | * Ref Function + Object type that can use `use:` directive in svelte 22 | * @category Common 23 | * @see useRef 24 | */ 25 | export type Ref = ((value: T) => { destroy(): void }) & { current?: T }; 26 | 27 | /** 28 | * @typedef 29 | * @category Reactive 30 | */ 31 | export type ReactiveResult< 32 | Instance extends ReactiveSubscribe>, 33 | State extends Record = ReactiveState, 34 | Methods extends keyof Partial = any, 35 | Events extends Record = {}, 36 | > = { 37 | [key in keyof State]: Writable 38 | } & { 39 | [key in Methods]: Instance[key] 40 | } & ReactiveEvents; 41 | 42 | /** 43 | * Get the result type of reactive component through adapter. 44 | * @category Reactive 45 | * @see useReactive 46 | * @example 47 | * ```ts 48 | * import { ReactiveAdapterResult } from "@cfcs/react"; 49 | * 50 | * type ReactiveComponentResult = ReactiveAdapterResult; 51 | * ``` 52 | */ 53 | export type ReactiveAdapterResult< 54 | Adapter extends ReactiveAdapterParam, 55 | > 56 | = Adapter extends ReactiveAdapterParam 57 | ? SvelteReactiveResult : {}; 58 | 59 | 60 | // Names using framework prefix 61 | /** 62 | * @hidden 63 | */ 64 | export type SvelteReactiveEvents> = ReactiveEvents; 65 | /** 66 | * @hidden 67 | */ 68 | export type SvelteReactiveResult< 69 | Instance extends ReactiveSubscribe>, 70 | State extends Record = ReactiveState, 71 | Methods extends keyof Partial = any, 72 | Events extends Record = {}, 73 | > = ReactiveResult< 74 | Instance, 75 | State, 76 | Methods, 77 | Events 78 | >; 79 | /** 80 | * @hidden 81 | */ 82 | export type SvelteReactiveAdapterResult< 83 | Adapter extends ReactiveAdapterParam 84 | > = ReactiveAdapterResult; 85 | -------------------------------------------------------------------------------- /packages/svelte/src/reactive/useReactive.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * cfcs 3 | * Copyright (c) 2022-present NAVER Corp. 4 | * MIT license 5 | */ 6 | import { ReactiveSubscribe, ReactiveAdapterParam, camelize, adaptReactive, ReactiveState } from "@cfcs/core"; 7 | import { onDestroy, onMount } from "svelte/internal"; 8 | import { writable, Writable } from "svelte/store"; 9 | import { ReactiveResult, Ref } from "./types"; 10 | 11 | /** 12 | * @description Instead of `bind:this`, you can bind this via the `use:` directive. 13 | * @category Common 14 | * @see Ref 15 | * @example 16 | * ```html 17 | * 27 | *
Element
28 | * ``` 29 | */ 30 | export function useRef(defaultValue?: T): Ref { 31 | const refFunction = (value: T) => { 32 | refFunction.current = value; 33 | 34 | return { 35 | destroy() { 36 | return; 37 | }, 38 | }; 39 | }; 40 | refFunction.current = defaultValue; 41 | 42 | return refFunction; 43 | } 44 | 45 | /** 46 | * @description In Svelte, you can create reactive components through adapters. 47 | * @category Reactive 48 | * @example 49 | * ```ts 50 | * import { useReactive } from "@cfcs/svelte"; 51 | * 52 | * export function useReactiveComponent() { 53 | * return useReactive(REACTIVE_ADAPTER); 54 | * } 55 | * ``` 56 | */ 57 | export function useReactive< 58 | Instance extends ReactiveSubscribe>, 59 | State extends Record = ReactiveState, 60 | Methods extends keyof Partial = any, 61 | Props = any, 62 | Events extends Record = {}, 63 | >( 64 | reactiveAdapter: ReactiveAdapterParam, 65 | props?: () => Props, 66 | ): ReactiveResult { 67 | const adaptResult = adaptReactive(reactiveAdapter, props); 68 | const reactiveState = adaptResult.state(); 69 | const writables: Record> = {}; 70 | const names = Object.keys(reactiveState); 71 | const methods = adaptResult.methods(); 72 | 73 | for (const name in reactiveState) { 74 | writables[name] = writable(reactiveState[name]); 75 | } 76 | 77 | onMount(() => { 78 | adaptResult.mounted(); 79 | const inst = adaptResult.instance(); 80 | 81 | names.forEach(name => { 82 | inst.subscribe(name as any, (value: any) => { 83 | writables[name].set(value); 84 | }); 85 | }); 86 | 87 | adaptResult.init(); 88 | }); 89 | 90 | onDestroy(() => { 91 | adaptResult.destroy(); 92 | }); 93 | const reactiveEvents = adaptResult.events(); 94 | const events = reactiveEvents.reduce((result, name) => { 95 | result[camelize(`on ${name as any}`)] = (callback: (...args: any[]) => void) => { 96 | adaptResult.on(name as any, callback as any); 97 | 98 | onDestroy(() => { 99 | adaptResult.off(name as any, callback as any); 100 | }); 101 | }; 102 | 103 | return result; 104 | }, {} as Record); 105 | 106 | return { 107 | ...writables, 108 | ...methods, 109 | ...events, 110 | } as any; 111 | } 112 | 113 | -------------------------------------------------------------------------------- /packages/svelte/tsconfig.declaration.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig", 3 | "compilerOptions": { 4 | "allowJs": false, 5 | "noEmit": false, 6 | "isolatedModules": false, 7 | "removeComments": true, 8 | "declaration": true, 9 | "emitDeclarationOnly": true, 10 | "declarationDir": "declaration" 11 | }, 12 | "include": ["src/**/*.ts"], 13 | "exclude": ["node_modules"], 14 | } 15 | -------------------------------------------------------------------------------- /packages/svelte/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./outjs/", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "noFallthroughCasesInSwitch": true, 16 | "module": "esnext", 17 | "moduleResolution": "node", 18 | "resolveJsonModule": true, 19 | "isolatedModules": true, 20 | "noEmit": true, 21 | "experimentalDecorators": true 22 | }, 23 | "include": [ 24 | "./src/**/*.ts" 25 | ], 26 | "exclude": ["node_modules", "__sapper__/*", "public/*"] 27 | } 28 | -------------------------------------------------------------------------------- /packages/vue2/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## 0.1.0 (2023-02-24) 7 | 8 | 9 | ### :rocket: New Features 10 | 11 | * v0.1 ([c33732a](https://github.com/naver/cfcs/commit/c33732a6bc7fba6e3e5746ecc5b3d94df582b39e)) 12 | 13 | 14 | ### :memo: Documentation 15 | 16 | * fix README ([6d479bd](https://github.com/naver/cfcs/commit/6d479bda55fbcbdd10dd7d90595070003e9d0f22)) 17 | 18 | 19 | ### :mega: Other 20 | 21 | * fix packages' url ([846fcf2](https://github.com/naver/cfcs/commit/846fcf2a666f3085778d9e8b7f8f533a32c4eb4c)) 22 | * update packages versions ([ae79c40](https://github.com/naver/cfcs/commit/ae79c402ac11c307f78a1371689e3e4d0eaa7f20)) 23 | -------------------------------------------------------------------------------- /packages/vue2/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2022-present NAVER Corp. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /packages/vue2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@cfcs/vue2", 3 | "version": "0.1.0", 4 | "description": "Write once, create framework components that supports React, Vue, Svelte, and more.", 5 | "main": "dist/cfcs.cjs.js", 6 | "module": "dist/cfcs.esm.js", 7 | "types": "declaration/index.d.ts", 8 | "sideEffects": false, 9 | "scripts": { 10 | "serve": "vue-cli-service serve", 11 | "lint": "vue-cli-service lint", 12 | "build": "rollup -c && npm run declaration && print-sizes ./dist ", 13 | "declaration": "rm -rf declaration && tsc -p tsconfig.declaration.json" 14 | }, 15 | "license": "MIT", 16 | "homepage": "https://naver.github.io/cfcs", 17 | "repository": { 18 | "type": "git", 19 | "url": "https://github.com/naver/cfcs/tree/main/packages/vue2" 20 | }, 21 | "author": { 22 | "name": "NAVER Corp." 23 | }, 24 | "keywords": [ 25 | "cfcs", 26 | "cfc", 27 | "vue2", 28 | "vue", 29 | "vue-hook", 30 | "vue-hooks", 31 | "vue-use", 32 | "scroll", 33 | "reactive", 34 | "hooks", 35 | "use" 36 | ], 37 | "files": [ 38 | "./*", 39 | "dist/*" 40 | ], 41 | "dependencies": { 42 | "@cfcs/core": "~0.1.0" 43 | }, 44 | "devDependencies": { 45 | "@babel/core": "^7.12.10", 46 | "@egjs/build-helper": "^0.1.2", 47 | "@typescript-eslint/parser": "^2.33.0", 48 | "@vue/cli-plugin-typescript": "~4.5.0", 49 | "@vue/cli-service": "~4.5.0", 50 | "@vue/composition-api": "^1.2.4", 51 | "@vue/eslint-config-airbnb": "^5.0.2", 52 | "@vue/eslint-config-prettier": "^6.0.0", 53 | "@vue/eslint-config-typescript": "^5.0.2", 54 | "@vue/test-utils": "^1.0.3", 55 | "babel-eslint": "^10.1.0", 56 | "babel-loader": "^8.2.2", 57 | "print-sizes": "^0.1.0", 58 | "rollup-plugin-vue": "^5.1.9", 59 | "tslib": "^2.3.1", 60 | "typescript": "^4.1.6", 61 | "vue": "^2.6.12", 62 | "vue-template-compiler": "^2.6.11" 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /packages/vue2/rollup.config.js: -------------------------------------------------------------------------------- 1 | const buildHelper = require("@egjs/build-helper"); 2 | 3 | export default buildHelper([ 4 | { 5 | input: "./src/index.ts", 6 | output: "./dist/cfcs.cjs.js", 7 | format: "cjs", 8 | exports: "named", 9 | external: { 10 | "vue": "vue", 11 | "@cfcs/core": "@cfcs/core", 12 | "@vue/composition-api": "@vue/composition-api", 13 | } 14 | }, 15 | { 16 | input: "./src/index.ts", 17 | output: "./dist/cfcs.esm.js", 18 | format: "esm", 19 | exports: "named", 20 | external: { 21 | "vue": "vue", 22 | "@cfcs/core": "@cfcs/core", 23 | "@vue/composition-api": "@vue/composition-api", 24 | } 25 | }, 26 | ]); 27 | 28 | -------------------------------------------------------------------------------- /packages/vue2/src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * cfcs 3 | * Copyright (c) 2022-present NAVER Corp. 4 | * MIT license 5 | */ 6 | export * from "./reactive"; 7 | -------------------------------------------------------------------------------- /packages/vue2/src/reactive/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * cfcs 3 | * Copyright (c) 2022-present NAVER Corp. 4 | * MIT license 5 | */ 6 | export * from "./types"; 7 | export * from "./useReactive"; 8 | -------------------------------------------------------------------------------- /packages/vue2/src/reactive/types.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * cfcs 3 | * Copyright (c) 2022-present NAVER Corp. 4 | * MIT license 5 | */ 6 | import { ReactiveAdapterParam, ReactiveEventParameters, ReactiveState, ReactiveSubscribe } from "@cfcs/core"; 7 | import { Ref } from "@vue/composition-api"; 8 | 9 | /** 10 | * @category Reactive 11 | */ 12 | export type ReactiveEvents< 13 | Events extends Record 14 | > = { 15 | [K in keyof Events as `on${Capitalize}`]: (callback: ((...args: ReactiveEventParameters) => void)) => void; 16 | }; 17 | 18 | /** 19 | * @category Reactive 20 | */ 21 | export type ReactiveResult< 22 | Instance extends ReactiveSubscribe>, 23 | State extends Record = ReactiveState, 24 | Methods extends keyof Partial = any, 25 | Events extends Record = {}, 26 | > = { 27 | [key in keyof State]: Ref 28 | } & { 29 | [key in Methods]: Instance[key] 30 | } & ReactiveEvents; 31 | 32 | /** 33 | * Get the result type of reactive component through adapter. 34 | * @category Reactive 35 | * @see useReactive 36 | * @example 37 | * ```ts 38 | * import { ReactiveAdapterResult } from "@cfcs/react"; 39 | * 40 | * type ReactiveComponentResult = ReactiveAdapterResult; 41 | * ``` 42 | */ 43 | export type ReactiveAdapterResult< 44 | Adapter extends ReactiveAdapterParam, 45 | > 46 | = Adapter extends ReactiveAdapterParam 47 | ? ReactiveResult : {}; 48 | 49 | 50 | 51 | /** 52 | * @category Reactive 53 | * @hidden 54 | */ 55 | export type ReactiveLegacyAdapterResult< 56 | Adapter extends ReactiveAdapterParam, 57 | > 58 | = Adapter extends ReactiveAdapterParam 59 | ? ReactiveLegacyResult : {}; 60 | 61 | /** 62 | * @category Reactive 63 | * @hidden 64 | */ 65 | export type ReactiveLegacyResult< 66 | Instance extends ReactiveSubscribe>, 67 | State extends Record = ReactiveState, 68 | Methods extends keyof Partial = any, 69 | Events extends Record = {}, 70 | > = State & { 71 | [key in Methods]: Instance[key] 72 | } & ReactiveEvents; 73 | 74 | // Names using framework prefix 75 | 76 | /** 77 | * @category Reactive 78 | * @hidden 79 | */ 80 | export type VueReactiveEvents> = ReactiveEvents; 81 | 82 | /** 83 | * @category Reactive 84 | * @hidden 85 | */ 86 | export type VueReactiveResult< 87 | Instance extends ReactiveSubscribe>, 88 | State extends Record = ReactiveState, 89 | Methods extends keyof Partial = any, 90 | Events extends Record = {}, 91 | > = ReactiveResult< 92 | Instance, 93 | State, 94 | Methods, 95 | Events 96 | >; 97 | 98 | /** 99 | * @hidden 100 | */ 101 | export type VueReactiveAdapterResult< 102 | Adapter extends ReactiveAdapterParam 103 | > = ReactiveAdapterResult; 104 | 105 | -------------------------------------------------------------------------------- /packages/vue2/src/reactive/useReactive.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * cfcs 3 | * Copyright (c) 2022-present NAVER Corp. 4 | * MIT license 5 | */ 6 | import { 7 | ReactiveAdapterParam, 8 | ReactiveSubscribe, 9 | camelize, 10 | adaptReactive, 11 | ReactiveState, 12 | } from "@cfcs/core"; 13 | import { onMounted, onUnmounted, Ref, ref } from "@vue/composition-api"; 14 | import { reactive } from "vue"; 15 | import { ReactiveLegacyResult, ReactiveResult } from "./types"; 16 | 17 | /** 18 | * @description In Vue 2, you can create reactive components through adapters. 19 | * @category Reactive 20 | * @example 21 | * ```ts 22 | * import { useReactive } from "@cfcs/vue2"; 23 | * 24 | * export function useReactiveComponent() { 25 | * return useReactive(REACTIVE_ADAPTER); 26 | * } 27 | * ``` 28 | */ 29 | export function useReactive< 30 | Instance extends ReactiveSubscribe>, 31 | State extends Record = ReactiveState, 32 | Methods extends keyof Partial = any, 33 | Props = any, 34 | Events extends Record = {}, 35 | >( 36 | reactiveAdapter: ReactiveAdapterParam, 37 | props?: () => Props, 38 | ): ReactiveResult { 39 | const adaptResult = adaptReactive(reactiveAdapter, props); 40 | const reactiveState = adaptResult.state(); 41 | const names = Object.keys(reactiveState); 42 | const refs: Record> = {}; 43 | for (const name in reactiveState) { 44 | refs[name] = ref(reactiveState[name]); 45 | } 46 | const methods = adaptResult.methods(); 47 | 48 | onMounted(() => { 49 | adaptResult.mounted(); 50 | 51 | const inst = adaptResult.instance(); 52 | 53 | names.forEach(name => { 54 | inst.subscribe(name as any, (value: any) => { 55 | refs[name].value = value; 56 | }); 57 | }); 58 | 59 | adaptResult.init(); 60 | }); 61 | 62 | onUnmounted(() => { 63 | adaptResult.destroy(); 64 | }); 65 | 66 | const reactiveEvents = adaptResult.events(); 67 | const events = reactiveEvents.reduce((eventResult, name) => { 68 | eventResult[camelize(`on ${name as any}`)] = (callback: (...args: any[]) => void) => { 69 | adaptResult.on(name as any, callback as any); 70 | 71 | onUnmounted(() => { 72 | adaptResult.off(name as any, callback as any); 73 | }); 74 | }; 75 | 76 | return eventResult; 77 | }, {} as Record); 78 | 79 | return { 80 | ...refs, 81 | ...methods, 82 | ...events, 83 | } as any; 84 | } 85 | 86 | /** 87 | * @hidden 88 | */ 89 | export function useLegacyReactive< 90 | Instance extends ReactiveSubscribe>, 91 | State extends Record = ReactiveState, 92 | Methods extends keyof Partial = any, 93 | Props = any, 94 | Events extends Record = {}, 95 | >( 96 | reactiveAdapter: ReactiveAdapterParam, 97 | props?: () => Props, 98 | ): ReactiveLegacyResult { 99 | const adaptResult = adaptReactive(reactiveAdapter, props); 100 | const reactiveState = adaptResult.state(); 101 | const names = Object.keys(reactiveState); 102 | let result!: Record; 103 | 104 | const methods = adaptResult.methods(); 105 | 106 | onMounted(() => { 107 | adaptResult.mounted(); 108 | 109 | const inst = adaptResult.instance(); 110 | 111 | names.forEach(name => { 112 | inst.subscribe(name as any, (value: any) => { 113 | result[name] = value; 114 | }); 115 | }); 116 | 117 | adaptResult.init(); 118 | }); 119 | 120 | onUnmounted(() => { 121 | adaptResult.destroy(); 122 | }); 123 | 124 | const reactiveEvents = adaptResult.events(); 125 | const events = reactiveEvents.reduce((eventResult, name) => { 126 | eventResult[camelize(`on ${name as any}`)] = (callback: (...args: any[]) => void) => { 127 | adaptResult.on(name as any, callback as any); 128 | 129 | onUnmounted(() => { 130 | adaptResult.off(name as any, callback as any); 131 | }); 132 | }; 133 | 134 | return eventResult; 135 | }, {} as Record); 136 | 137 | result = reactive({ 138 | ...reactiveState, 139 | ...methods, 140 | ...events, 141 | }); 142 | 143 | 144 | return result as any; 145 | } 146 | -------------------------------------------------------------------------------- /packages/vue2/tsconfig.declaration.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig", 3 | "compilerOptions": { 4 | "allowJs": false, 5 | "noEmit": false, 6 | "isolatedModules": false, 7 | "removeComments": true, 8 | "declaration": true, 9 | "emitDeclarationOnly": true, 10 | "declarationDir": "declaration" 11 | }, 12 | "include": [ 13 | "./src" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /packages/vue2/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "noFallthroughCasesInSwitch": true, 16 | "module": "esnext", 17 | "moduleResolution": "node", 18 | "resolveJsonModule": true, 19 | "isolatedModules": true, 20 | "noEmit": true, 21 | "jsx": "react-jsx" 22 | }, 23 | "include": [ 24 | "src" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /packages/vue3/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## 0.1.0 (2023-02-24) 7 | 8 | 9 | ### :rocket: New Features 10 | 11 | * v0.1 ([c33732a](https://github.com/naver/cfcs/commit/c33732a6bc7fba6e3e5746ecc5b3d94df582b39e)) 12 | 13 | 14 | ### :memo: Documentation 15 | 16 | * fix README ([6d479bd](https://github.com/naver/cfcs/commit/6d479bda55fbcbdd10dd7d90595070003e9d0f22)) 17 | 18 | 19 | ### :mega: Other 20 | 21 | * fix packages' url ([846fcf2](https://github.com/naver/cfcs/commit/846fcf2a666f3085778d9e8b7f8f533a32c4eb4c)) 22 | * update packages versions ([ae79c40](https://github.com/naver/cfcs/commit/ae79c402ac11c307f78a1371689e3e4d0eaa7f20)) 23 | -------------------------------------------------------------------------------- /packages/vue3/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@cfcs/vue3", 3 | "version": "0.1.0", 4 | "description": "Write once, create framework components that supports React, Vue, Svelte, and more.", 5 | "main": "dist/cfcs.cjs.js", 6 | "module": "dist/cfcs.esm.js", 7 | "types": "declaration/index.d.ts", 8 | "sideEffects": false, 9 | "scripts": { 10 | "serve": "vue-cli-service serve", 11 | "lint": "vue-cli-service lint", 12 | "build": "rollup -c && npm run declaration && print-sizes ./dist ", 13 | "declaration": "rm -rf declaration && tsc -p tsconfig.declaration.json" 14 | }, 15 | "license": "MIT", 16 | "homepage": "https://naver.github.io/cfcs", 17 | "repository": { 18 | "type": "git", 19 | "url": "https://github.com/naver/cfcs/tree/main/packages/vue3" 20 | }, 21 | "author": { 22 | "name": "NAVER Corp." 23 | }, 24 | "keywords": [ 25 | "cfcs", 26 | "cfc", 27 | "vue3", 28 | "vue", 29 | "vue-hook", 30 | "vue-hooks", 31 | "vue-use", 32 | "scroll", 33 | "reactive", 34 | "hooks", 35 | "use" 36 | ], 37 | "files": [ 38 | "./*", 39 | "dist/*" 40 | ], 41 | "dependencies": { 42 | "@cfcs/core": "~0.1.0" 43 | }, 44 | "devDependencies": { 45 | "@egjs/build-helper": "^0.1.2", 46 | "@typescript-eslint/eslint-plugin": "^2.33.0", 47 | "@typescript-eslint/parser": "^2.33.0", 48 | "@vue/cli-plugin-babel": "~4.5.0", 49 | "@vue/cli-plugin-eslint": "~4.5.0", 50 | "@vue/cli-plugin-typescript": "~4.5.0", 51 | "@vue/cli-service": "~4.5.0", 52 | "@vue/compiler-sfc": "^3.0.0", 53 | "@vue/eslint-config-typescript": "^5.0.2", 54 | "core-js": "^3.6.5", 55 | "eslint": "^6.7.2", 56 | "eslint-plugin-vue": "^7.0.0-0", 57 | "print-sizes": "^0.1.0", 58 | "typescript": "^4.7.4", 59 | "vue": "^3.2.21" 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /packages/vue3/rollup.config.js: -------------------------------------------------------------------------------- 1 | const buildHelper = require("@egjs/build-helper"); 2 | 3 | export default buildHelper([ 4 | { 5 | input: "./src/index.ts", 6 | output: "./dist/cfcs.cjs.js", 7 | format: "cjs", 8 | exports: "named", 9 | external: { 10 | "vue": "vue", 11 | "@cfcs/core": "@cfcs/core", 12 | } 13 | }, 14 | { 15 | input: "./src/index.ts", 16 | output: "./dist/cfcs.esm.js", 17 | format: "esm", 18 | exports: "named", 19 | external: { 20 | "vue": "vue", 21 | "@cfcs/core": "@cfcs/core", 22 | } 23 | }, 24 | ]); 25 | 26 | -------------------------------------------------------------------------------- /packages/vue3/src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * cfcs 3 | * Copyright (c) 2022-present NAVER Corp. 4 | * MIT license 5 | */ 6 | export * from "./reactive"; 7 | -------------------------------------------------------------------------------- /packages/vue3/src/reactive/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * cfcs 3 | * Copyright (c) 2022-present NAVER Corp. 4 | * MIT license 5 | */ 6 | export * from "./types"; 7 | export * from "./useReactive"; 8 | -------------------------------------------------------------------------------- /packages/vue3/src/reactive/types.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * cfcs 3 | * Copyright (c) 2022-present NAVER Corp. 4 | * MIT license 5 | */ 6 | import { ReactiveAdapterParam, ReactiveEventParameters, ReactiveState, ReactiveSubscribe } from "@cfcs/core"; 7 | import { Ref } from "vue"; 8 | 9 | 10 | /** 11 | * @category Reactive 12 | */ 13 | export type ReactiveEvents< 14 | Events extends Record 15 | > = { 16 | [K in keyof Events as `on${Capitalize}`]: (callback: ((...args: ReactiveEventParameters) => void)) => void; 17 | }; 18 | 19 | 20 | /** 21 | * @category Reactive 22 | */ 23 | export type ReactiveResult< 24 | Instance extends ReactiveSubscribe>, 25 | State extends Record = ReactiveState, 26 | Methods extends keyof Partial = any, 27 | Events extends Record = {}, 28 | > = { 29 | [key in keyof State]: Ref 30 | } & { 31 | [key in Methods]: Instance[key] 32 | } & ReactiveEvents; 33 | 34 | 35 | 36 | /** 37 | * @category Reactive 38 | * @hidden 39 | */ 40 | export type ReactiveLegacyResult< 41 | Instance extends ReactiveSubscribe>, 42 | State extends Record = ReactiveState, 43 | Methods extends keyof Partial = any, 44 | Events extends Record = {}, 45 | > = State & { 46 | [key in Methods]: Instance[key] 47 | } & ReactiveEvents; 48 | 49 | /** 50 | * @category Reactive 51 | * @hidden 52 | */ 53 | export type ReactiveLegacyAdapterResult< 54 | Adapter extends ReactiveAdapterParam, 55 | > 56 | = Adapter extends ReactiveAdapterParam 57 | ? ReactiveLegacyResult : {}; 58 | 59 | /** 60 | * Get the result type of reactive component through adapter. 61 | * @category Reactive 62 | * @see useReactive 63 | * @example 64 | * ```ts 65 | * import { ReactiveAdapterResult } from "@cfcs/react"; 66 | * 67 | * type ReactiveComponentResult = ReactiveAdapterResult; 68 | * ``` 69 | */ 70 | export type ReactiveAdapterResult< 71 | Adapter extends ReactiveAdapterParam, 72 | > 73 | = Adapter extends ReactiveAdapterParam 74 | ? VueReactiveResult : {}; 75 | 76 | // Names using framework prefix 77 | 78 | /** 79 | * @category Reactive 80 | * @hidden 81 | */ 82 | export type VueReactiveEvents> = ReactiveEvents; 83 | 84 | 85 | /** 86 | * @category Reactive 87 | * @hidden 88 | */ 89 | export type VueReactiveResult< 90 | Instance extends ReactiveSubscribe>, 91 | State extends Record = ReactiveState, 92 | Methods extends keyof Partial = any, 93 | Events extends Record = {}, 94 | > = ReactiveResult< 95 | Instance, 96 | State, 97 | Methods, 98 | Events 99 | >; 100 | 101 | /** 102 | * @category Reactive 103 | * @hidden 104 | */ 105 | export type VueReactiveAdapterResult< 106 | Adapter extends ReactiveAdapterParam 107 | > = ReactiveAdapterResult; 108 | -------------------------------------------------------------------------------- /packages/vue3/src/reactive/useReactive.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * cfcs 3 | * Copyright (c) 2022-present NAVER Corp. 4 | * MIT license 5 | */ 6 | import { 7 | ReactiveAdapterParam, 8 | ReactiveSubscribe, 9 | camelize, 10 | adaptReactive, 11 | ReactiveState, 12 | } from "@cfcs/core"; 13 | import { onMounted, onUnmounted, reactive, Ref, ref } from "vue"; 14 | import { ReactiveLegacyResult, ReactiveResult } from "./types"; 15 | 16 | 17 | /** 18 | * @description In Vue 3, you can create reactive components through adapters. 19 | * @category Reactive 20 | * @example 21 | * ```ts 22 | * import { useReactive } from "@cfcs/vue3"; 23 | * 24 | * export function useReactiveComponent() { 25 | * return useReactive(REACTIVE_ADAPTER); 26 | * } 27 | * ``` 28 | */ 29 | export function useReactive< 30 | Instance extends ReactiveSubscribe>, 31 | State extends Record = ReactiveState, 32 | Methods extends keyof Partial = any, 33 | Props = any, 34 | Events extends Record = {}, 35 | >( 36 | reactiveAdapter: ReactiveAdapterParam, 37 | props?: () => Props, 38 | ): ReactiveResult { 39 | const adaptResult = adaptReactive(reactiveAdapter, props); 40 | const reactiveState = adaptResult.state(); 41 | const names = Object.keys(reactiveState); 42 | const refs: Record> = {}; 43 | for (const name in reactiveState) { 44 | refs[name] = ref(reactiveState[name]); 45 | } 46 | const methods = adaptResult.methods(); 47 | 48 | onMounted(() => { 49 | adaptResult.mounted(); 50 | 51 | const inst = adaptResult.instance(); 52 | 53 | names.forEach(name => { 54 | inst.subscribe(name as any, (value: any) => { 55 | refs[name].value = value; 56 | }); 57 | }); 58 | 59 | adaptResult.init(); 60 | }); 61 | 62 | onUnmounted(() => { 63 | adaptResult.destroy(); 64 | }); 65 | 66 | const reactiveEvents = adaptResult.events(); 67 | const events = reactiveEvents.reduce((eventResult, name) => { 68 | eventResult[camelize(`on ${name as any}`)] = (callback: (...args: any[]) => void) => { 69 | adaptResult.on(name as any, callback as any); 70 | 71 | onUnmounted(() => { 72 | adaptResult.off(name as any, callback as any); 73 | }); 74 | }; 75 | 76 | return eventResult; 77 | }, {} as Record); 78 | 79 | return { 80 | ...refs, 81 | ...methods, 82 | ...events, 83 | } as any; 84 | } 85 | 86 | 87 | /** 88 | * @category Reactive 89 | * @hidden 90 | */ 91 | export function useLegacyReactive< 92 | Instance extends ReactiveSubscribe>, 93 | State extends Record = ReactiveState, 94 | Methods extends keyof Partial = any, 95 | Props = any, 96 | Events extends Record = {}, 97 | >( 98 | reactiveAdapter: ReactiveAdapterParam, 99 | props?: () => Props, 100 | ): ReactiveLegacyResult { 101 | const adaptResult = adaptReactive(reactiveAdapter, props); 102 | const reactiveState = adaptResult.state(); 103 | const names = Object.keys(reactiveState); 104 | let result!: Record; 105 | 106 | const methods = adaptResult.methods(); 107 | 108 | onMounted(() => { 109 | adaptResult.mounted(); 110 | 111 | const inst = adaptResult.instance(); 112 | 113 | names.forEach(name => { 114 | inst.subscribe(name as any, (value: any) => { 115 | result[name] = value; 116 | }); 117 | }); 118 | 119 | adaptResult.init(); 120 | }); 121 | 122 | onUnmounted(() => { 123 | adaptResult.destroy(); 124 | }); 125 | 126 | const reactiveEvents = adaptResult.events(); 127 | const events = reactiveEvents.reduce((eventResult, name) => { 128 | eventResult[camelize(`on ${name as any}`)] = (callback: (...args: any[]) => void) => { 129 | adaptResult.on(name as any, callback as any); 130 | 131 | onUnmounted(() => { 132 | adaptResult.off(name as any, callback as any); 133 | }); 134 | }; 135 | 136 | return eventResult; 137 | }, {} as Record); 138 | 139 | result = reactive({ 140 | ...reactiveState, 141 | ...methods, 142 | ...events, 143 | }); 144 | 145 | 146 | return result as any; 147 | } 148 | -------------------------------------------------------------------------------- /packages/vue3/tsconfig.declaration.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig", 3 | "compilerOptions": { 4 | "allowJs": false, 5 | "noEmit": false, 6 | "isolatedModules": false, 7 | "removeComments": true, 8 | "declaration": true, 9 | "emitDeclarationOnly": true, 10 | "declarationDir": "declaration" 11 | }, 12 | "include": [ 13 | "./src" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /packages/vue3/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "noFallthroughCasesInSwitch": true, 16 | "module": "esnext", 17 | "moduleResolution": "node", 18 | "resolveJsonModule": true, 19 | "isolatedModules": true, 20 | "noEmit": true, 21 | "jsx": "react-jsx" 22 | }, 23 | "include": [ 24 | "src" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /tsconfig.api.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "strict": false, 5 | "resolveJsonModule": true, 6 | "skipLibCheck": true 7 | }, 8 | "include": [ 9 | "./packages/core/src/**/*.ts", 10 | "./packages/react/src/**/*.ts", 11 | "./packages/vue2/src/**/*.ts", 12 | "./packages/vue3/src/**/*.ts", 13 | "./packages/svelte/src/**/*.ts" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./outjs/", 4 | "module": "es6", 5 | "target": "es5", 6 | "moduleResolution": "node", 7 | "lib": ["es6", "dom"], 8 | "sourceMap": true, 9 | "strictNullChecks": true, 10 | "downlevelIteration": true, 11 | "experimentalDecorators": true, 12 | "esModuleInterop": true 13 | }, 14 | "include": [ 15 | "./**/*.ts" 16 | ], 17 | "exclude": [ 18 | "node_modules" 19 | ] 20 | } 21 | --------------------------------------------------------------------------------