diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..abe8b76 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,3 @@ +ko_fi: jaywcjlove +buy_me_a_coffee: jaywcjlove +custom: ["https://www.paypal.me/kennyiseeyou", "https://jaywcjlove.github.io/#/sponsor"] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 11a6c05..9bddfda 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,64 +7,76 @@ on: jobs: build-deploy: runs-on: ubuntu-latest + permissions: + contents: write + id-token: write steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 - with: - node-version: 16 - registry-url: 'https://registry.npmjs.org' + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 20 + registry-url: 'https://registry.npmjs.org' - - run: npm install - - run: npm run build - - run: npm run doc - - run: npm run test:coverage + - run: npm install + - run: npm run build + - run: npm run doc + - run: npm run coverage + - run: cp -rp ./coverage www/build - - name: Create Tag - id: create_tag - uses: jaywcjlove/create-tag-action@main - with: - package-path: ./package.json + - name: Create Tag + id: create_tag + uses: jaywcjlove/create-tag-action@main + with: + package-path: ./core/package.json - - name: Generate Changelog - id: changelog - uses: jaywcjlove/changelog-generator@main - with: - token: ${{ secrets.GITHUB_TOKEN }} - head-ref: ${{steps.create_tag.outputs.version}} - filter-author: (renovate-bot|Renovate Bot) - filter: '[R|r]elease[d]\s+[v|V]\d(\.\d+){0,2}' + - name: Generate Contributors Images + uses: jaywcjlove/github-action-contributors@main + with: + filter-author: (renovate\[bot\]|renovate\-bot|dependabot\[bot\]) + output: www/build/CONTRIBUTORS.svg + avatarSize: 42 - - name: Deploy - uses: peaceiris/actions-gh-pages@v3 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: ./build + - name: Create Coverage Badges + uses: jaywcjlove/coverage-badges-cli@main + with: + output: www/build/badges.svg - - name: Create Release - uses: ncipollo/release-action@v1 - if: steps.create_tag.outputs.successful - with: - name: ${{ steps.create_tag.outputs.version }} - tag: ${{ steps.create_tag.outputs.version }} - token: ${{ secrets.GITHUB_TOKEN }} - body: | - [![](https://img.shields.io/badge/Open%20in-unpkg-blue)](https://uiwjs.github.io/npm-unpkg/#/pkg/@uiw/react-only-when@${{steps.changelog.outputs.version}}/file/README.md) [![Coverage Status](https://coveralls.io/repos/github/uiwjs/react-only-when/badge.svg?branch=main)](https://coveralls.io/github/uiwjs/react-only-when?branch=main) [![npm bundle size](https://img.shields.io/bundlephobia/minzip/@uiw/react-only-when)](https://www.npmjs.com/package/@uiw/react-only-when) + - name: Generate Changelog + id: changelog + uses: jaywcjlove/changelog-generator@main + with: + token: ${{ secrets.GITHUB_TOKEN }} + head-ref: ${{steps.create_tag.outputs.version}} + filter-author: (renovate-bot|Renovate Bot) + filter: '[R|r]elease[d]\s+[v|V]\d(\.\d+){0,2}' - ```bash - npm i @uiw/react-only-when@${{steps.changelog.outputs.version}} - ``` - ${{ steps.changelog.outputs.compareurl }} - ${{ steps.changelog.outputs.changelog }} + - name: Deploy + uses: peaceiris/actions-gh-pages@v4 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./www/build - - name: Coveralls - uses: coverallsapp/github-action@master - with: - github-token: ${{ secrets.GITHUB_TOKEN }} + - name: Create Release + uses: ncipollo/release-action@v1 + if: steps.create_tag.outputs.successful + with: + allowUpdates: true + name: ${{ steps.create_tag.outputs.version }} + tag: ${{ steps.create_tag.outputs.version }} + token: ${{ secrets.GITHUB_TOKEN }} + body: | + [![Buy me a coffee](https://img.shields.io/badge/Buy%20me%20a%20coffee-048754?logo=buymeacoffee)](https://jaywcjlove.github.io/#/sponsor) [![](https://img.shields.io/badge/Open%20in-unpkg-blue)](https://uiwjs.github.io/npm-unpkg/#/pkg/@uiw/react-only-when@${{steps.changelog.outputs.version}}/file/README.md) [![Coverage Status](https://coveralls.io/repos/github/uiwjs/react-only-when/badge.svg?branch=main)](https://coveralls.io/github/uiwjs/react-only-when?branch=main) [![npm bundle size](https://img.shields.io/bundlephobia/minzip/@uiw/react-only-when)](https://www.npmjs.com/package/@uiw/react-only-when) - - run: npm publish --access public - name: 📦 @uiw/react-only-when publish to NPM - continue-on-error: true - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - # - run: npm install @jsdevtools/npm-publish -g - # - run: npm-publish --token="${{ secrets.NPM_TOKEN }}" ./package.json \ No newline at end of file + ```bash + npm i @uiw/react-only-when@${{steps.changelog.outputs.version}} + ``` + + ${{ steps.changelog.outputs.compareurl }} + ${{ steps.changelog.outputs.changelog }} + + - run: npm publish --access public --provenance + name: 📦 @uiw/react-only-when publish to NPM + continue-on-error: true + working-directory: core + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} \ No newline at end of file diff --git a/.husky/pre-commit b/.husky/pre-commit index d37daa0..041c660 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,4 +1 @@ -#!/bin/sh -. "$(dirname "$0")/_/husky.sh" - npx --no-install lint-staged diff --git a/.kktrc.ts b/.kktrc.ts deleted file mode 100644 index ac2db34..0000000 --- a/.kktrc.ts +++ /dev/null @@ -1,53 +0,0 @@ -import path from 'path'; -import webpack, { Configuration } from 'webpack'; -import { LoaderConfOptions } from 'kkt'; -import lessModules from '@kkt/less-modules'; -import rawModules from '@kkt/raw-modules'; -import scopePluginOptions from '@kkt/scope-plugin-options'; -import pkg from './package.json'; - -export default (conf: Configuration, env: 'development' | 'production', options: LoaderConfOptions) => { - conf = rawModules(conf, env, { ...options }); - conf = scopePluginOptions(conf, env, { - ...options, - allowedFiles: [path.resolve(process.cwd(), 'README.md'), path.resolve(process.cwd(), 'src')], - }); - conf = lessModules(conf, env, options); - // Get the project version. - conf.plugins!.push( - new webpack.DefinePlugin({ - VERSION: JSON.stringify(pkg.version), - }), - ); - if (env === 'production') { - conf.output = { ...conf.output, publicPath: './' }; - conf.optimization = { - ...conf.optimization, - splitChunks: { - cacheGroups: { - reactvendor: { - test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/, - name: 'react-vendor', - chunks: 'all', - }, - refractor: { - test: /[\\/]node_modules[\\/](refractor)[\\/]/, - name: 'refractor-prismjs-vendor', - chunks: 'all', - }, - runtime: { - test: /[\\/]node_modules[\\/](@babel)[\\/]/, - name: 'babel-vendor', - chunks: 'all', - }, - parse5: { - test: /[\\/]node_modules[\\/](parse5)[\\/]/, - name: 'parse5-vendor', - chunks: 'all', - }, - } - } - } - } - return conf; -}; diff --git a/.lintstagedrc b/.lintstagedrc new file mode 100644 index 0000000..a3e5f8b --- /dev/null +++ b/.lintstagedrc @@ -0,0 +1,5 @@ +{ + "*.{js,jsx,tsx,ts,less,md,json}": [ + "prettier --write" + ] +} \ No newline at end of file diff --git a/.prettierignore b/.prettierignore index bd19942..74583c1 100644 --- a/.prettierignore +++ b/.prettierignore @@ -6,6 +6,7 @@ package.json node_modules dist build +lib cjs esm test diff --git a/.prettierrc b/.prettierrc index 764fbde..1aa6835 100644 --- a/.prettierrc +++ b/.prettierrc @@ -6,6 +6,22 @@ { "files": ".prettierrc", "options": { "parser": "json" } + }, + { + "files": "*.{js,jsx}", + "options": { "parser": "babel" } + }, + { + "files": "*.{ts,tsx}", + "options": { "parser": "babel-ts" } + }, + { + "files": "*.{ts,tsx}", + "options": { "parser": "typescript" } + }, + { + "files": "*.{less,css}", + "options": { "parser": "css" } } ] } diff --git a/LICENSE b/LICENSE index c8c4416..6e04f96 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,7 @@ MIT License Copyright (c) 2021 uiw +Copyright (c) sag1v https://github.com/sag1v Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md deleted file mode 100644 index e48cb66..0000000 --- a/README.md +++ /dev/null @@ -1,118 +0,0 @@ -react-only-when -=== - -[![Build & Deploy](https://github.com/uiwjs/react-only-when/actions/workflows/ci.yml/badge.svg)](https://github.com/uiwjs/react-only-when/actions/workflows/ci.yml) [![npm bundle size](https://img.shields.io/bundlephobia/minzip/@uiw/react-only-when)](https://www.npmjs.com/package/@uiw/react-only-when) [![npm version](https://img.shields.io/npm/v/@uiw/react-only-when.svg)](https://www.npmjs.com/package/@uiw/react-only-when) [![Coverage Status](https://coveralls.io/repos/github/uiwjs/react-only-when/badge.svg?branch=main)](https://coveralls.io/github/uiwjs/react-only-when?branch=main) - -A declarative component for conditional rendering. Copy [`react-only-when`](https://github.com/sag1v/react-only-when), let it support TypeScript. - -## Quick Start - -```bash -$ npm install --save @uiw/react-only-when -``` - -## Usage - -```jsx -import Only from '@uiw/react-only-when' - - -

Here I Am

-
-``` - -## \ - -React component that renders the children if the `condition` prop is `true`. - -```jsx -import { If } from '@uiw/react-only-when'; - -
- ( -

{props.error}

- )} - /> - -

{props.error}

-
-
-``` - -Or you could just use plain JavaScript: - -```jsx -
- {props.error && ( -

{props.error}

- )} -
-``` - -## Example - -```jsx -import React from 'react'; -import Only from '@uiw/react-only-when'; - -function App() { - const [show, setShow] = useState(true) - return ( -
- - -

Here I Am

-
-
- ) -} -``` - -## props - -| prop name | type | default | isRequired | description | -| ----- | ----- | ----- | ----- | ----- | -| children | react element | `null` | `true` | A single child element | -| when | bool | `false` | `true` | When true, children will rendered as is | -| hiddenMode | string | `null` | `false` | Determines how children should be hidden | -| className | string | `r-o_hidden` | `false` | This is working in combination with `hiddenMode={"css"}` | - -### hiddenMode enum - -| hiddenMode | description | -| ----- | ----- | -| `null` | Will not render the child | -| `display` | Will render the child with `display:none` | -| `visibility` | Will render the child with `visibility:hidden` | -| `css` | Will render the child with a CSS class (you can pass it a custom `className` prop) | - - -## Development - -Runs the project in development mode. - -```bash -# Step 1, run first, listen to the component compile and output the .js file -# listen for compilation output type .d.ts file -npm run watch -# Step 2, development mode, listen to compile preview website instance -npm run start -``` - -**production** - -Builds the app for production to the build folder. - -```bash -npm run build -``` - -The build is minified and the filenames include the hashes. -Your app is ready to be deployed! - - -## License - -MIT © [`sag1v`](https://github.com/sag1v) & [`uiwjs`](https://github.com/uiwjs) \ No newline at end of file diff --git a/README.md b/README.md new file mode 120000 index 0000000..a79a213 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +./core/README.md \ No newline at end of file diff --git a/core/README.md b/core/README.md new file mode 100644 index 0000000..5ca9b56 --- /dev/null +++ b/core/README.md @@ -0,0 +1,233 @@ +react-only-when +=== + +[![Buy me a coffee](https://img.shields.io/badge/Buy%20me%20a%20coffee-048754?logo=buymeacoffee)](https://jaywcjlove.github.io/#/sponsor) +[![Build & Deploy](https://github.com/uiwjs/react-only-when/actions/workflows/ci.yml/badge.svg)](https://github.com/uiwjs/react-only-when/actions/workflows/ci.yml) +[![npm bundle size](https://img.shields.io/bundlephobia/minzip/@uiw/react-only-when)](https://www.npmjs.com/package/@uiw/react-only-when) +[![npm version](https://img.shields.io/npm/v/@uiw/react-only-when.svg)](https://www.npmjs.com/package/@uiw/react-only-when) +[![Coverage Status](https://uiwjs.github.io/react-only-when/badges.svg)](https://uiwjs.github.io/react-only-when/coverage/lcov-report/) + +A declarative component for conditional rendering. Copy [`react-only-when`](https://github.com/sag1v/react-only-when), let it support TypeScript. + +## Quick Start + +```bash +$ npm install --save @uiw/react-only-when +``` + +## Usage + +```jsx +import Only from '@uiw/react-only-when' + + +

Here I Am

+
+``` + +```jsx +import { If } from '@uiw/react-only-when/if' + + +

{props.error}

+
+``` + +```jsx +import { Switch, Case, Default } from '@uiw/react-only-when/switch' + + + preschool + = 6}>primary school + you graduated + +``` + +## \ + +React component that renders the children if the `condition` prop is `true`. + +```jsx +import { If } from '@uiw/react-only-when'; +// Or +import { If } from '@uiw/react-only-when/if' + +
+ ( +

{props.error}

+ )} + /> + +

{props.error}

+
+
+``` + +Or you could just use plain JavaScript: + +```jsx +
+ {props.error && ( +

{props.error}

+ )} +
+``` + +Only Example + +```jsx mdx:preview&background=#fff&codePen=true +import React, { useState } from 'react'; +import Only from '@uiw/react-only-when'; + +export default function App() { + const [show, setShow] = useState(true) + return ( +
+ + +

Here I Am

+
+
+ ) +} +``` + +## \ + +```jsx +import { Switch, Case, Default } from '@uiw/react-only-when/switch' + + + preschool + = 6}>primary school + you graduated + +``` + +```jsx mdx:preview&background=#fff&codePen=true +import React, { useState, Fragment } from 'react'; +import { Switch, Case, Default } from '@uiw/react-only-when/switch' + +export default function App() { + const [age, setAge] = useState(19) + return ( + + setAge(Number(evn.target.value))} /> {age}
+ + Preschool + = 6 && age < 18}>Primary school + = 18 && age < 60}>Went to college + you graduated + +
+ ); +} +``` + +Defaults to specifying a wrapped HTML Element. + +```jsx mdx:preview&background=#fff&codePen=true +import React, { useState, Fragment } from 'react'; +import { Switch, Case, Default } from '@uiw/react-only-when/switch' + +export default function App() { + const [age, setAge] = useState(19) + return ( + + setAge(Number(evn.target.value))} /> {age} +
+ + Preschool + = 6 && age < 18}>Primary school + = 18 && age < 60}>Went to college + you graduated + +
+ ); +} +``` + +## `` props + +| prop name | type | default | isRequired | description | +| ----- | ----- | ----- | ----- | ----- | +| children | react element | `null` | `true` | A single child element | +| when | bool | `false` | `true` | When true, children will rendered as is | +| hiddenMode | string | `null` | `false` | Determines how children should be hidden | +| className | string | `w-hidden` | `false` | This is working in combination with `hiddenMode={"css"}` | + +**`hiddenMode` enum** + +| hiddenMode | description | +| ----- | ----- | +| `null` | Will not render the child | +| `display` | Will render the child with `display:none` | +| `visibility` | Will render the child with `visibility:hidden` | +| `css` | Will render the child with a CSS class (you can pass it a custom `className` prop) | + +## `` Props + +```tsx +import { ReactElement } from 'react'; +import { FC, PropsWithChildren } from 'react'; +export interface IfProps { + readonly condition?: boolean; + readonly render?: () => ReactElement; +} +export declare const If: FC>; +``` + +## `` `` `` Props + +```tsx +import type { FC, PropsWithChildren } from 'react'; +export const Switch: FC>; +type TagType = React.ElementType | keyof JSX.IntrinsicElements; +interface CaseElementProps { + as?: T; + readonly condition?: boolean; +} +export type CaseProps = CaseElementProps & React.ComponentPropsWithoutRef; +export const Case: (props: CaseProps) => any; +export const Default: (props: Omit, 'condition'>) => import("react/jsx-runtime").JSX.Element; +``` + +## Development + +Runs the project in development mode. + +```bash +# Step 1, run first, listen to the component compile and output the .js file +# listen for compilation output type .d.ts file +npm run watch +# Step 2, development mode, listen to compile preview website instance +npm run start +``` + +**production** + +Builds the app for production to the build folder. + +```bash +npm run build +``` + +The build is minified and the filenames include the hashes. +Your app is ready to be deployed! + +## Contributors + +As always, thanks to our amazing contributors! + + + + + +Made with [contributors](https://github.com/jaywcjlove/github-action-contributors). + + +## License + +MIT © [`sag1v`](https://github.com/sag1v) & [`uiwjs`](https://github.com/uiwjs) \ No newline at end of file diff --git a/core/if.d.ts b/core/if.d.ts new file mode 100644 index 0000000..49748c4 --- /dev/null +++ b/core/if.d.ts @@ -0,0 +1,9 @@ +declare module '@uiw/react-only-when/if' { + import { ReactElement } from 'react'; + import { FC, PropsWithChildren } from 'react'; + export interface IfProps { + readonly condition?: boolean; + readonly render?: () => ReactElement; + } + export const If: FC>; +} diff --git a/core/package.json b/core/package.json new file mode 100644 index 0000000..097f447 --- /dev/null +++ b/core/package.json @@ -0,0 +1,58 @@ +{ + "name": "@uiw/react-only-when", + "version": "3.0.2", + "description": "A declarative component for conditional rendering.", + "main": "cjs/index.js", + "module": "esm/index.js", + "homepage": "https://uiwjs.github.io/react-only-when", + "funding": "https://jaywcjlove.github.io/#/sponsor", + "repository": { + "type": "git", + "url": "https://github.com/uiwjs/react-only-when.git" + }, + "exports": { + ".": { + "import": "./esm/index.js", + "require": "./cjs/index.js", + "types": "./esm/index.d.ts" + }, + "./if": { + "import": "./esm/If.js", + "require": "./cjs/If.js", + "types": "./esm/If.d.ts" + }, + "./switch": { + "import": "./esm/switch.js", + "require": "./cjs/switch.js", + "types": "./esm/switch.d.ts" + } + }, + "author": "Kenny Wang", + "license": "MIT", + "peerDependencies": { + "@babel/runtime": ">=7.10.0", + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + }, + "files": [ + "src", + "cjs", + "esm" + ], + "keywords": [ + "component", + "only-when", + "react", + "react-component" + ], + "dependencies": { + "@babel/runtime": "^7.22.6" + }, + "devDependencies": { + "@testing-library/react": "^14.0.0", + "@types/react-test-renderer": "^18.0.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-test-renderer": "^18.2.0" + } +} diff --git a/core/src/If.tsx b/core/src/If.tsx new file mode 100644 index 0000000..1d70147 --- /dev/null +++ b/core/src/If.tsx @@ -0,0 +1,10 @@ +import type { ReactElement } from 'react'; +import type { FC, PropsWithChildren } from 'react'; + +export interface IfProps { + readonly condition?: boolean; + readonly render?: () => ReactElement; +} + +export const If: FC> = (props) => + props.condition ? (props.render ? props.render() : (props.children as ReactElement)) : null; diff --git a/src/index.tsx b/core/src/index.tsx similarity index 91% rename from src/index.tsx rename to core/src/index.tsx index a64eb20..7be0ff8 100644 --- a/src/index.tsx +++ b/core/src/index.tsx @@ -1,4 +1,4 @@ -import React, { PropsWithChildren } from 'react'; +import React, { type PropsWithChildren } from 'react'; import { If } from './If'; export * from './If'; @@ -21,7 +21,7 @@ export interface OnlyWhenProps { } export default function OnlyWhen(props: PropsWithChildren) { - const { children, when, hiddenMode, className } = props; + const { children, when, hiddenMode = 'null', className = 'w-hidden' } = props; const singleChild = React.Children.only(children); const { style, ...restOfChildProps } = singleChild.props; const extendedProps = { ...restOfChildProps }; diff --git a/core/src/switch.tsx b/core/src/switch.tsx new file mode 100644 index 0000000..8e29e05 --- /dev/null +++ b/core/src/switch.tsx @@ -0,0 +1,42 @@ +import type { FC, PropsWithChildren } from 'react'; + +type Child = typeof Case | typeof Default; +export const Switch: FC> = ({ children }) => { + let matchChild: Child | null = null; + let defaultCase: typeof Default | null = null; + + const childs = Array.isArray(children) ? children : [children]; + childs.some((child) => { + if (!defaultCase && child && child.type === Default) { + defaultCase = child; + } + if (child && child.type === Case) { + const { condition } = child.props; + const conditionIsTrue = Boolean(condition); + if (conditionIsTrue) { + matchChild = child; + return true; + } + } + return false; + }); + return matchChild ?? defaultCase ?? null; +}; + +type TagType = React.ElementType | keyof JSX.IntrinsicElements; +interface CaseElementProps { + as?: T; + readonly condition?: boolean; +} + +export type CaseProps = CaseElementProps & React.ComponentPropsWithoutRef; + +export const Case = (props: CaseProps) => { + const { children, condition, as: Comp, ...reset } = props; + const Elm = Comp as TagType; + return Elm ? {children} : children; +}; + +export const Default = (props: Omit, 'condition'>) => ( + )} /> +); diff --git a/core/switch.d.ts b/core/switch.d.ts new file mode 100644 index 0000000..b54cebe --- /dev/null +++ b/core/switch.d.ts @@ -0,0 +1,14 @@ +declare module '@uiw/react-only-when/switch' { + import type { FC, PropsWithChildren } from 'react'; + export const Switch: FC>; + type TagType = React.ElementType | keyof JSX.IntrinsicElements; + interface CaseElementProps { + as?: T; + readonly condition?: boolean; + } + export type CaseProps = CaseElementProps & React.ComponentPropsWithoutRef; + export const Case: (props: CaseProps) => any; + export const Default: ( + props: Omit, 'condition'>, + ) => import('react/jsx-runtime').JSX.Element; +} diff --git a/core/tsconfig.json b/core/tsconfig.json new file mode 100644 index 0000000..424075b --- /dev/null +++ b/core/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../tsconfig", + "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.d.ts"], + "compilerOptions": { + "outDir": "../cjs", + "baseUrl": ".", + "noEmit": false, + "paths": { + "*": ["*", "types/*"] + } + } +} diff --git a/lerna.json b/lerna.json new file mode 100644 index 0000000..0eebc1f --- /dev/null +++ b/lerna.json @@ -0,0 +1,4 @@ +{ + "version": "3.0.2", + "packages": ["core", "www"] +} diff --git a/package.json b/package.json index 2033fbb..3e9b191 100644 --- a/package.json +++ b/package.json @@ -1,74 +1,47 @@ { - "name": "@uiw/react-only-when", - "version": "1.1.0", - "description": "A declarative component for conditional rendering.", - "main": "cjs/index.js", - "module": "esm/index.js", + "private": true, "scripts": { - "prepack": "npm run build", - "doc": "kkt build --app-src ./website", - "start": "kkt start --app-src ./website", - "watch": "tsbb watch src/*tsx --use-babel --cjs cjs", - "build": "tsbb build src/*tsx --use-babel --cjs cjs", - "prettier": "prettier --write \"**/*.{js,jsx,tsx,ts,less,md,json}\"", - "test": "kkt test --env=jsdom --app-src=./website", - "test:coverage": "kkt test --env=jsdom --coverage --app-src=./website" + "start": "lerna exec --scope website -- npm run start", + "doc": "lerna exec --scope website -- npm run build", + "⬇️⬇️⬇️⬇️⬇️ package ⬇️⬇️⬇️⬇️⬇️": "▼▼▼▼▼ package ▼▼▼▼▼", + "watch": "lerna exec --scope @uiw/react-only-when -- tsbb watch \"src/*.{ts,tsx}\" --use-babel --cjs cjs", + "build": "lerna exec --scope @uiw/react-only-when -- tsbb build \"src/*.{ts,tsx}\" --use-babel --cjs cjs", + "⬆️⬆️⬆️⬆️⬆️ package ⬆️⬆️⬆️⬆️⬆️": "▲▲▲▲▲ package ▲▲▲▲▲", + "type-check": "tsc --noEmit", + "test": "tsbb test", + "coverage": "tsbb test --coverage --bail", + "prepare": "husky", + "publish": "lerna publish from-package --yes --no-verify-access", + "version": "lerna version --exact --force-publish --no-push --no-git-tag-version", + "prettier": "prettier --write '**/*.{js,jsx,tsx,ts,less,md,json}'", + "remove": "npm run clean && lerna exec \"rm -rf package-lock.json\" --scope @uiw/react-only-when --scope website", + "clean": "lerna clean --yes" }, - "repository": { - "type": "git", - "url": "https://github.com/uiwjs/react-only-when.git" - }, - "author": "kenny wang ", "license": "MIT", - "lint-staged": { - "*.{js,jsx,tsx,ts,less,md,json}": [ - "prettier --write \"**/*.{js,jsx,tsx,ts,less,md,json}\"" - ] - }, - "files": [ - "src", - "cjs", - "esm" + "workspaces": [ + "core", + "www" ], - "peerDependencies": { - "@babel/runtime": ">=7.10.0", - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - }, - "devDependencies": { - "@kkt/less-modules": "^7.4.9", - "@kkt/raw-modules": "^7.4.9", - "@kkt/scope-plugin-options": "^7.4.9", - "@types/react": "^18.2.6", - "@types/react-dom": "^18.2.4", - "@types/react-test-renderer": "^18.0.0", - "@uiw/react-github-corners": "^1.5.15", - "@uiw/react-markdown-preview": "^4.1.13", - "husky": "^8.0.3", - "kkt": "^7.4.9", - "lint-staged": "^13.2.2", - "prettier": "^2.8.8", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "react-test-renderer": "^18.2.0", - "tsbb": "^4.1.5" + "engines": { + "node": ">=16.0.0" }, - "eslintConfig": { - "extends": [ - "react-app", - "react-app/jest" - ] - }, - "browserslist": { - "production": [ - ">0.2%", - "not dead", - "not op_mini all" + "jest": { + "collectCoverageFrom": [ + "/core/src/*.{tsx,ts}" ], - "development": [ - "last 1 chrome version", - "last 1 firefox version", - "last 1 safari version" + "coverageReporters": [ + "lcov", + "json-summary" ] + }, + "overrides": { + "typescript": "^5.1.3" + }, + "devDependencies": { + "husky": "^9.0.0", + "lint-staged": "^15.0.0", + "lerna": "^8.0.0", + "prettier": "^3.0.0", + "tsbb": "^4.5.1" } } diff --git a/renovate.json b/renovate.json index 4f39080..89c98f0 100644 --- a/renovate.json +++ b/renovate.json @@ -1,3 +1,9 @@ { - "extends": ["config:base"] + "extends": ["config:base"], + "packageRules": [ + { + "matchPackagePatterns": ["*"], + "rangeStrategy": "replace" + } + ] } diff --git a/src/If.tsx b/src/If.tsx deleted file mode 100644 index acec504..0000000 --- a/src/If.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import { ReactElement } from "react"; -import { FC, PropsWithChildren } from "react"; - -export interface IfProps { - readonly condition?: boolean; - readonly render?: () => ReactElement; -} - -export const If: FC> = (props) => props.condition ? (props.render ? props.render() : props.children as ReactElement) : null; \ No newline at end of file diff --git a/src/tsconfig.json b/src/tsconfig.json deleted file mode 100644 index 59fc6d6..0000000 --- a/src/tsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": "../tsconfig", - "include": ["../src"], - "compilerOptions": { - "baseUrl": ".", - "outDir": "../cjs", - "emitDeclarationOnly": true, - "noEmit": false - } -} diff --git a/test/If.test.tsx b/test/If.test.tsx new file mode 100644 index 0000000..8967b5f --- /dev/null +++ b/test/If.test.tsx @@ -0,0 +1,39 @@ +/* eslint-disable jest/no-conditional-expect */ +import TestRenderer from 'react-test-renderer'; +import { If } from '../core/src/If'; + +describe('', () => { + it('Not rendering children', () => { + const component = TestRenderer.create( + + + , + ); + let only = component.toJSON(); + expect(only).toBeNull(); + }); + + it('rendering children', () => { + const component = TestRenderer.create( + + + , + ); + let only = component.toJSON(); + + if (only && !Array.isArray(only)) { + expect(only.type).toEqual('span'); + expect(only.props.id).toEqual('child'); + } + }); + + it('render props', () => { + const component = TestRenderer.create( } />); + let only = component.toJSON(); + + if (only && !Array.isArray(only)) { + expect(only.type).toEqual('span'); + expect(only.props.id).toEqual('child'); + } + }); +}); diff --git a/src/__test__/index.test.tsx b/test/index.test.tsx similarity index 76% rename from src/__test__/index.test.tsx rename to test/index.test.tsx index f427070..2c4483b 100644 --- a/src/__test__/index.test.tsx +++ b/test/index.test.tsx @@ -1,47 +1,6 @@ /* eslint-disable jest/no-conditional-expect */ import TestRenderer from 'react-test-renderer'; -import Only from '../'; -import { If } from '../'; - -describe('', () => { - - it('Not rendering children', () => { - const component = TestRenderer.create( - - - , - ); - let only = component.toJSON(); - expect(only).toBeNull(); - }); - - it('rendering children', () => { - const component = TestRenderer.create( - - - , - ); - let only = component.toJSON(); - - if (only && !Array.isArray(only)) { - expect(only.type).toEqual('span'); - expect(only.props.id).toEqual('child'); - } - }); - - it('render props', () => { - const component = TestRenderer.create( - } />, - ); - let only = component.toJSON(); - - if (only && !Array.isArray(only)) { - expect(only.type).toEqual('span'); - expect(only.props.id).toEqual('child'); - } - }); - -}) +import Only from '../core/src'; describe('', () => { it('Not rendering children', () => { @@ -118,7 +77,7 @@ describe('', () => { expect(only.type).toEqual('span'); expect(only.props.id).toEqual('child'); expect(only.props.children).toBeUndefined(); - expect(only.props.className).toEqual('test-className'); + expect(only.props.className).toEqual('test-className w-hidden'); expect(only.props.style).toEqual({ color: 'green' }); } }); @@ -141,7 +100,7 @@ describe('', () => { } }); - it('Joining className = undefined', () => { + it('Joining className default value', () => { const component = TestRenderer.create( @@ -153,7 +112,7 @@ describe('', () => { expect(only.type).toEqual('span'); expect(only.props.id).toEqual('child'); expect(only.props.children).toBeUndefined(); - expect(only.props.className).toEqual(''); + expect(only.props.className).toEqual('w-hidden'); expect(only.props.style).toEqual({ color: 'green' }); } }); diff --git a/test/switch.test.tsx b/test/switch.test.tsx new file mode 100644 index 0000000..7c43f12 --- /dev/null +++ b/test/switch.test.tsx @@ -0,0 +1,77 @@ +import renderer from 'react-test-renderer'; +import { render, screen } from '@testing-library/react'; +import { Switch, Case, Default } from '../core/src/switch'; + +it('', () => { + const component = renderer.create( + + ); + const only = component.toJSON(); + expect(only).toBeNull(); +}); + +it('', () => { + const { container } = render( + + you graduated + + ); + expect(container.innerHTML).toEqual('you graduated'); +}); + +it('', () => { + const { container } = render( + + preschool + you graduated + + ); + expect(container.innerHTML).toEqual('preschool'); +}); + +it(' 1', () => { + const { container } = render( + + preschool + primary school + you graduated + + ); + expect(container.innerHTML).toEqual('preschool'); +}); + +it(' 2', () => { + const { container } = render( + + preschool + primary school + you graduated + + ); + expect(container.innerHTML).toEqual('you graduated'); +}); + + +it('', () => { + render( + + preschool + + ); + const span = screen.getByTestId('span'); + expect(span.tagName).toEqual('SPAN'); + expect(span.innerHTML).toEqual('preschool'); +}); + + +it('', () => { + render( + + you graduated + + ); + const elm = screen.getByTestId('elm'); + expect(elm.tagName).toEqual('P'); + expect(elm.innerHTML).toEqual('you graduated'); + expect(elm.title).toEqual('test case'); +}); diff --git a/tsconfig.json b/tsconfig.json index 6a882f3..b1fca47 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,25 +1,21 @@ { "compilerOptions": { - "sourceMap": false, - "target": "esnext", - "module": "esnext", - "lib": ["esnext", "dom"], - "moduleResolution": "node", - "allowJs": false, + "target": "es5", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, "skipLibCheck": true, "esModuleInterop": true, "allowSyntheticDefaultImports": true, "strict": true, "forceConsistentCasingInFileNames": true, + "module": "esnext", + "moduleResolution": "node", "resolveJsonModule": true, "isolatedModules": true, "declaration": true, + "baseUrl": ".", "jsx": "react-jsx", "noFallthroughCasesInSwitch": true, - "noUnusedLocals": true, - "experimentalDecorators": true, - "removeComments": false, "noEmit": true - }, - "include": ["src"], + } } diff --git a/website/App.css b/website/App.css deleted file mode 100644 index 3e41c3e..0000000 --- a/website/App.css +++ /dev/null @@ -1,45 +0,0 @@ -body { - margin: 0; - padding: 0; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", - "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", - sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -.App-logo { - height: 120px; -} - -.App-header { - background-color: #282c34; - /* min-height: 100vh; */ - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - /* font-size: calc(10px + 2vmin); */ - color: white; - padding-bottom: 100px; - padding-top: 60px; -} - -.App-link { - color: #09d3ac; -} - -.info { - width: 620px;; - margin: 0 auto; - padding-top: 35px; - padding-bottom: 60px; -} - -.w-only-hidden { - display: none; -} - -label { - user-select: none; -} \ No newline at end of file diff --git a/website/index.tsx b/website/index.tsx deleted file mode 100644 index ee7ab34..0000000 --- a/website/index.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { createRoot } from 'react-dom/client'; -import MarkdownPreview from '@uiw/react-markdown-preview'; -import GitHubCorners from '@uiw/react-github-corners'; -import logo from './logo.svg'; -import Example from './Example'; -import MDStr from '../README.md'; -import './App.css'; - -const container = document.getElementById('root'); -const root = createRoot(container!); - -root.render( -
- -
- logo -

A declarative component for conditional rendering.

-
- -
-
- -
-); diff --git a/website/logo.svg b/website/logo.svg deleted file mode 100644 index 3f454eb..0000000 --- a/website/logo.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/website/react-app-env.d.ts b/website/react-app-env.d.ts deleted file mode 100644 index 51fa39e..0000000 --- a/website/react-app-env.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -/// - -declare module '*.module.less' { - const classes: { readonly [key: string]: string }; - export default classes; -} - -declare module '*.md' { - const src: string; - export default src; -} diff --git a/website/tsconfig.json b/website/tsconfig.json deleted file mode 100644 index 308cfea..0000000 --- a/website/tsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "../tsconfig", - "include": ["../website"], - "compilerOptions": { - "baseUrl": "../website" - } -} \ No newline at end of file diff --git a/www/.kktrc.ts b/www/.kktrc.ts new file mode 100644 index 0000000..520bb28 --- /dev/null +++ b/www/.kktrc.ts @@ -0,0 +1,50 @@ +import webpack from 'webpack'; +import { LoaderConfOptions, WebpackConfiguration } from 'kkt'; +import rawModules from '@kkt/raw-modules'; +import { disableScopePlugin } from '@kkt/scope-plugin-options'; +import { mdCodeModulesLoader } from 'markdown-react-code-preview-loader'; +import pkg from './package.json'; + +export default (conf: WebpackConfiguration, env: 'production' | 'development', options: LoaderConfOptions) => { + conf = rawModules(conf, env, { ...options }); + conf = mdCodeModulesLoader(conf); + conf = disableScopePlugin(conf); + // Get the project version. + conf.plugins!.push( + new webpack.DefinePlugin({ + VERSION: JSON.stringify(pkg.version), + }), + ); + conf.module!.exprContextCritical = false; + conf.ignoreWarnings = [ + { + module: /node_modules[\\/]parse5[\\/]/, + }, + ]; + if (env === 'production') { + conf.output = { ...conf.output, publicPath: './' }; + conf.optimization = { + ...conf.optimization, + splitChunks: { + automaticNameDelimiter: '.', + maxSize: 500000, + minSize: 100000, + cacheGroups: { + reactvendor: { + test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/, + name: 'react-vendor', + reuseExistingChunk: true, + chunks: 'all', + priority: -10, + }, + refractor: { + test: /[\\/]node_modules[\\/](refractor)[\\/]/, + name: 'refractor-prismjs-vendor', + chunks: 'all', + }, + }, + }, + }; + } + return conf; +}; diff --git a/www/package.json b/www/package.json new file mode 100644 index 0000000..a100f45 --- /dev/null +++ b/www/package.json @@ -0,0 +1,44 @@ +{ + "name": "website", + "version": "3.0.2", + "preview": true, + "scripts": { + "build": "kkt build", + "start": "kkt start", + "map": "source-map-explorer build/static/js/*.js --html build/website-result.html" + }, + "dependencies": { + "@uiw/react-markdown-preview-example": "^2.1.4", + "@uiw/react-only-when": "3.0.2", + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "devDependencies": { + "@babel/plugin-proposal-private-property-in-object": "^7.21.11", + "@kkt/raw-modules": "^7.5.1", + "@kkt/scope-plugin-options": "^7.5.1", + "@types/react": "^18.0.31", + "@types/react-dom": "^18.0.11", + "kkt": "^7.5.1", + "markdown-react-code-preview-loader": "^2.1.2", + "source-map-explorer": "^2.5.3" + }, + "eslintConfig": { + "extends": [ + "react-app", + "react-app/jest" + ] + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + } +} diff --git a/public/favicon.ico b/www/public/favicon.ico similarity index 100% rename from public/favicon.ico rename to www/public/favicon.ico diff --git a/public/index.html b/www/public/index.html similarity index 100% rename from public/index.html rename to www/public/index.html diff --git a/website/Example.tsx b/www/src/Example.tsx similarity index 96% rename from website/Example.tsx rename to www/src/Example.tsx index b4be4d1..cce5405 100644 --- a/website/Example.tsx +++ b/www/src/Example.tsx @@ -1,5 +1,5 @@ import { useState } from 'react'; -import Only, { OnlyWhenProps } from '../'; +import Only, { OnlyWhenProps } from '@uiw/react-only-when'; type CheckboxProps = { hiddenMode?: OnlyWhenProps['hiddenMode']; diff --git a/www/src/index.tsx b/www/src/index.tsx new file mode 100644 index 0000000..9a17d97 --- /dev/null +++ b/www/src/index.tsx @@ -0,0 +1,26 @@ +import { createRoot } from 'react-dom/client'; +import MarkdownPreviewExample from '@uiw/react-markdown-preview-example'; +import data from '../../core/README.md'; +import pkg from '../../core/package.json'; +import OnlyWhenExample from './Example'; + +const Github = MarkdownPreviewExample.Github; +const Example = MarkdownPreviewExample.Example; + +const container = document.getElementById('root'); +const root = createRoot(container!); +root.render( + + + + + + , +); diff --git a/www/src/react-app-env.d.ts b/www/src/react-app-env.d.ts new file mode 100644 index 0000000..5ebd9ad --- /dev/null +++ b/www/src/react-app-env.d.ts @@ -0,0 +1,9 @@ +/// + +declare var VERSION: string; + +declare module '*.md' { + import { CodeBlockData } from 'markdown-react-code-preview-loader'; + const src: CodeBlockData; + export default src; +} diff --git a/www/tsconfig.json b/www/tsconfig.json new file mode 100644 index 0000000..81e0a44 --- /dev/null +++ b/www/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../tsconfig", + "include": ["src/*", ".kktrc.ts"], + "compilerOptions": { + "noEmit": false + } +}