diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 00000000..ecdd4cdc --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,49 @@ +module.exports = { + env: { + browser: true, + es2021: true, + node: true, + }, + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:react/recommended', + 'plugin:react-hooks/recommended', + 'plugin:jsx-a11y/recommended', + 'plugin:storybook/recommended', + ], + parser: '@typescript-eslint/parser', + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + ecmaVersion: 12, + sourceType: 'module', + }, + plugins: ['@typescript-eslint', 'react'], + rules: { + 'react/prop-types': 'off', + 'react/react-in-jsx-scope': 'off', + '@typescript-eslint/no-empty-interface': 'off', + '@typescript-eslint/no-unused-vars': [ + 'error', + { + ignoreRestSiblings: true, + }, + ], + }, + overrides: [ + { + files: ['*.js', '*.jsx', '*.cjs'], + rules: { + '@typescript-eslint/no-var-requires': 'off', + '@typescript-eslint/explicit-module-boundary-types': 'off', + }, + }, + ], + settings: { + react: { + version: 'detect', + }, + }, +} diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index c9cd5203..00000000 --- a/.eslintrc.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "env": { - "browser": true, - "es2021": true, - "node": true - }, - "extends": [ - "eslint:recommended", - "plugin:@typescript-eslint/recommended", - "plugin:react/recommended", - "plugin:react-hooks/recommended", - "plugin:jsx-a11y/recommended", - "plugin:prettier/recommended" - ], - "parser": "@typescript-eslint/parser", - "parserOptions": { - "ecmaFeatures": { - "jsx": true - }, - "ecmaVersion": 12, - "sourceType": "module" - }, - "plugins": ["@typescript-eslint", "react"], - "rules": { - "prettier/prettier": ["error", {}, { "usePrettierrc": true }], - "react/prop-types": "off", - "@typescript-eslint/no-empty-interface": "off", - "@typescript-eslint/no-unused-vars": [ - "error", - { "ignoreRestSiblings": true } - ] - }, - "overrides": [ - { - "files": ["*.ts", "*.tsx"] - }, - { - "files": ["*.stories.*", ".storybook/**"], - "rules": { - "react/react-in-jsx-scope": "off" - } - }, - { - "files": ["*.js", "*.jsx", "*.cjs"], - "rules": { - "@typescript-eslint/no-var-requires": "off", - "@typescript-eslint/explicit-module-boundary-types": "off" - } - } - ], - "settings": { - "react": { - "version": "detect" - } - }, - "ignorePatterns": ["!.storybook"] -} diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index b8aebf39..4e510b3a 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -31,15 +31,9 @@ jobs: - name: Install Dependencies run: yarn install --immutable - - name: Run lint - run: yarn lint - - name: Build Components run: yarn build - - name: Run tests - run: yarn test - - name: Publish to NPM env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 61817b0a..313463f3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,15 +8,41 @@ permissions: contents: read security-events: write +env: + NODE_OPTIONS: --max_old_space_size=4096 + jobs: security: uses: lidofinance/linters/.github/workflows/security.yml@master + actions: uses: lidofinance/linters/.github/workflows/actions.yml@master - test-components: + + cache-deps: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v3 + with: + persist-credentials: false + + - name: Set up node + uses: actions/setup-node@v3 + with: + node-version: '16' + + - name: Restore cache + uses: actions/cache@v3 + with: + path: node_modules + key: ${{ runner.os }}-node_modules-${{ hashFiles('**/yarn.lock') }} + + - name: Install dependencies + run: yarn install --immutable + + check-types: runs-on: ubuntu-latest - env: - NODE_OPTIONS: --max_old_space_size=4096 + needs: cache-deps steps: - name: Checkout repo uses: actions/checkout@v3 @@ -27,19 +53,145 @@ jobs: uses: actions/setup-node@v3 with: node-version: '16' - cache: 'yarn' + + - name: Restore cache + uses: actions/cache@v3 + with: + path: node_modules + key: ${{ runner.os }}-node_modules-${{ hashFiles('**/yarn.lock') }} - name: Install dependencies run: yarn install --immutable - - name: Check typescript + - name: Check types run: yarn types - - name: Run lint - run: yarn lint + check-format: + runs-on: ubuntu-latest + needs: cache-deps + steps: + - name: Checkout repo + uses: actions/checkout@v3 + with: + persist-credentials: false + + - name: Set up node + uses: actions/setup-node@v3 + with: + node-version: '16' + + - name: Restore cache + uses: actions/cache@v3 + with: + path: node_modules + key: ${{ runner.os }}-node_modules-${{ hashFiles('**/yarn.lock') }} - - name: Build Components - run: yarn build + - name: Install dependencies + run: yarn install --immutable + + - name: Check format + run: yarn format + + lint-js: + runs-on: ubuntu-latest + needs: cache-deps + steps: + - name: Checkout repo + uses: actions/checkout@v3 + with: + persist-credentials: false + + - name: Set up node + uses: actions/setup-node@v3 + with: + node-version: '16' + + - name: Restore cache + uses: actions/cache@v3 + with: + path: node_modules + key: ${{ runner.os }}-node_modules-${{ hashFiles('**/yarn.lock') }} + + - name: Install dependencies + run: yarn install --immutable + + - name: Lint JS/TS + run: yarn lint:js + + lint-css: + runs-on: ubuntu-latest + needs: cache-deps + steps: + - name: Checkout repo + uses: actions/checkout@v3 + with: + persist-credentials: false + + - name: Set up node + uses: actions/setup-node@v3 + with: + node-version: '16' + + - name: Restore cache + uses: actions/cache@v3 + with: + path: node_modules + key: ${{ runner.os }}-node_modules-${{ hashFiles('**/yarn.lock') }} + + - name: Install dependencies + run: yarn install --immutable + + - name: Lint CSS + run: yarn lint:css + + run-tests: + runs-on: ubuntu-latest + needs: cache-deps + steps: + - name: Checkout repo + uses: actions/checkout@v3 + with: + persist-credentials: false + + - name: Set up node + uses: actions/setup-node@v3 + with: + node-version: '16' + + - name: Restore cache + uses: actions/cache@v3 + with: + path: node_modules + key: ${{ runner.os }}-node_modules-${{ hashFiles('**/yarn.lock') }} + + - name: Install dependencies + run: yarn install --immutable - name: Run tests run: yarn test + + demo-build: + runs-on: ubuntu-latest + needs: cache-deps + steps: + - name: Checkout repo + uses: actions/checkout@v3 + with: + persist-credentials: false + + - name: Set up node + uses: actions/setup-node@v3 + with: + node-version: '16' + + - name: Restore cache + uses: actions/cache@v3 + with: + path: node_modules + key: ${{ runner.os }}-node_modules-${{ hashFiles('**/yarn.lock') }} + + - name: Install dependencies + run: yarn install --immutable + + - name: Try to build + run: yarn build diff --git a/.nova/Configuration.json b/.nova/Configuration.json deleted file mode 100644 index badd3f0d..00000000 --- a/.nova/Configuration.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "apexskier.eslint.config.eslintPath": ".yarn/sdks/eslint/lib", - "apexskier.typescript.config.tslibPath": ".yarn/sdks/typescript/lib", - "index.ignored_file_patterns": [".yarn"] -} diff --git a/.releaserc.js b/.releaserc.js index e9f038b9..a2849ab3 100644 --- a/.releaserc.js +++ b/.releaserc.js @@ -1,7 +1,7 @@ module.exports = { branches: [ - {name: 'main'}, - {name: 'next', channel: 'next', prerelease: true}, + { name: 'main' }, + { name: 'next', channel: 'next', prerelease: true }, ], plugins: [ ['@semantic-release/commit-analyzer', { preset: 'conventionalcommits' }], diff --git a/.storybook/components/WithThemeProvider.tsx b/.storybook/components/WithThemeProvider.tsx index b5ed43d5..b619ab0c 100644 --- a/.storybook/components/WithThemeProvider.tsx +++ b/.storybook/components/WithThemeProvider.tsx @@ -1,7 +1,7 @@ -import { BaseDecorators } from '@storybook/addons' -import '@lidofinance/theme' +import '../../packages/theme' import { createGlobalStyle } from 'styled-components' -import { CookieThemeProvider } from '@lidofinance/theme' +import { CookieThemeProvider } from '../../packages/theme' +import { Decorator } from '@storybook/react' const GlobalStyle = createGlobalStyle` body { @@ -14,9 +14,9 @@ const GlobalStyle = createGlobalStyle` } ` -export const WithThemeProvider: BaseDecorators[number] = ( +export const WithThemeProvider: Decorator = ( Story, - { args } + { args }, ): JSX.Element => { return ( { - config.resolve.alias = { - ...config.resolve.alias, - ...Object.fromEntries( - packages.map((dir) => [`@lidofinance/${dir}`, resolve(basepath, dir)]) - ), - } - return config - }, stories: ['../packages/**/*.stories.@(js|jsx|ts|tsx)'], - reactOptions: { - // FIXME: replace with true - // https://github.com/lidofinance/ui/issues/198 - strictMode: false, - }, addons: [ '@storybook/addon-docs', { @@ -30,12 +8,6 @@ module.exports = { backgrounds: false, }, }, - { - name: 'storybook-addon-swc', - options: { - enableSwcMinify: false, - }, - }, ], typescript: { check: false, @@ -47,4 +19,11 @@ module.exports = { features: { postcss: false, }, + docs: { + autodocs: true, + }, + framework: { + name: '@storybook/react-vite', + options: {}, + }, } diff --git a/.storybook/themes.ts b/.storybook/themes.ts index da9d8136..e0fa9338 100644 --- a/.storybook/themes.ts +++ b/.storybook/themes.ts @@ -1,5 +1,5 @@ import { ThemeVars, create } from '@storybook/theming' -import { themeLight, themeDark, Theme } from '@lidofinance/theme' +import { themeLight, themeDark, Theme } from '../packages/theme' const constructor = (base: ThemeVars['base'], theme: Theme): ThemeVars => ({ base, diff --git a/.storybook/viewport.ts b/.storybook/viewport.ts index db1f3a74..f28a9a1f 100644 --- a/.storybook/viewport.ts +++ b/.storybook/viewport.ts @@ -1,4 +1,4 @@ -import { themeDefault } from '@lidofinance/theme' +import { themeDefault } from '../packages/theme' const { breakpointsMap } = themeDefault @@ -10,7 +10,7 @@ const themeViewports = Object.keys(breakpointsMap).reduce( styles: breakpointsMap[breakpoint], }, }), - {} + {}, ) export default { diff --git a/.stylelintrc.json b/.stylelintrc.json new file mode 100644 index 00000000..0eed88a3 --- /dev/null +++ b/.stylelintrc.json @@ -0,0 +1,13 @@ +{ + "extends": ["stylelint-config-standard"], + "rules": { + "custom-property-pattern": null, + "selector-class-pattern": null, + "property-no-unknown": [true, { "ignoreProperties": ["composes"] }], + "selector-pseudo-class-no-unknown": [ + true, + { "ignorePseudoClasses": ["global"] } + ] + }, + "ignoreFiles": ["node_modules/**/*", "dist/**/*"] +} diff --git a/.swcrc b/.swcrc deleted file mode 100644 index 3b5b7619..00000000 --- a/.swcrc +++ /dev/null @@ -1,138 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/swcrc", - "jsc": { - "parser": { - "syntax": "typescript", - "tsx": true, - "dts": true, - "decorators": false, - "dynamicImport": false - }, - "transform": { - "react": { - "pragma": "React.createElement", - "pragmaFrag": "React.Fragment", - "throwIfNamespace": true, - "development": false, - "useBuiltins": false - } - }, - "baseUrl": ".", - "paths": { - "@lidofinance/accordion": [ - "packages/accordion/index.ts" - ], - "@lidofinance/address": [ - "packages/address/index.ts" - ], - "@lidofinance/block": [ - "packages/block/index.ts" - ], - "@lidofinance/box": [ - "packages/box/index.ts" - ], - "@lidofinance/button": [ - "packages/button/index.ts" - ], - "@lidofinance/checkbox": [ - "packages/checkbox/index.ts" - ], - "@lidofinance/chip": [ - "packages/chip/index.ts" - ], - "@lidofinance/container": [ - "packages/container/index.ts" - ], - "@lidofinance/data-table": [ - "packages/data-table/index.ts" - ], - "@lidofinance/divider": [ - "packages/divider/index.ts" - ], - "@lidofinance/heading": [ - "packages/heading/index.tsx" - ], - "@lidofinance/hooks": [ - "packages/hooks/index.ts" - ], - "@lidofinance/icons": [ - "packages/icons/index.tsx" - ], - "@lidofinance/identicon": [ - "packages/identicon/index.ts" - ], - "@lidofinance/input": [ - "packages/input/index.ts" - ], - "@lidofinance/lido-logo": [ - "packages/lido-logo/index.ts" - ], - "@lidofinance/link": [ - "packages/link/index.ts" - ], - "@lidofinance/loaders": [ - "packages/loaders/index.ts" - ], - "@lidofinance/main-menu": [ - "packages/main-menu/index.ts" - ], - "@lidofinance/modal": [ - "packages/modal/index.ts" - ], - "@lidofinance/pagination": [ - "packages/pagination/index.ts" - ], - "@lidofinance/popover": [ - "packages/popover/index.ts" - ], - "@lidofinance/popup-menu": [ - "packages/popup-menu/index.ts" - ], - "@lidofinance/select": [ - "packages/select/index.ts" - ], - "@lidofinance/service-page": [ - "packages/service-page/index.ts" - ], - "@lidofinance/stack": [ - "packages/stack/index.ts" - ], - "@lidofinance/styled-system": [ - "packages/styled-system/index.ts" - ], - "@lidofinance/table": [ - "packages/table/index.ts" - ], - "@lidofinance/text": [ - "packages/text/index.ts" - ], - "@lidofinance/theme": [ - "packages/theme/index.ts" - ], - "@lidofinance/toast": [ - "packages/toast/index.ts" - ], - "@lidofinance/tooltip": [ - "packages/tooltip/index.ts" - ], - "@lidofinance/transition": [ - "packages/transition/index.ts" - ], - "@lidofinance/utils": [ - "packages/utils/index.ts" - ], - "@lidofinance/cookie-theme-toggler": [ - "packages/cookie-theme-toggler" - ], - "@lidofinance/content-theme": [ - "packages/content-theme" - ] - }, - "target": "es2019", - "loose": false, - "keepClassNames": true, - "externalHelpers": false - }, - "sourceMaps": true, - "minify": false -} \ No newline at end of file diff --git a/.swcrc.commonjs b/.swcrc.commonjs deleted file mode 100644 index bcd2c36d..00000000 --- a/.swcrc.commonjs +++ /dev/null @@ -1,152 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/swcrc", - "jsc": { - "parser": { - "syntax": "typescript", - "tsx": true, - "dts": true, - "decorators": false, - "dynamicImport": false - }, - "transform": { - "react": { - "pragma": "React.createElement", - "pragmaFrag": "React.Fragment", - "throwIfNamespace": true, - "development": false, - "useBuiltins": false - } - }, - "baseUrl": ".", - "paths": { - "@lidofinance/accordion": [ - "packages/accordion/index.ts" - ], - "@lidofinance/address": [ - "packages/address/index.ts" - ], - "@lidofinance/block": [ - "packages/block/index.ts" - ], - "@lidofinance/box": [ - "packages/box/index.ts" - ], - "@lidofinance/button": [ - "packages/button/index.ts" - ], - "@lidofinance/checkbox": [ - "packages/checkbox/index.ts" - ], - "@lidofinance/chip": [ - "packages/chip/index.ts" - ], - "@lidofinance/container": [ - "packages/container/index.ts" - ], - "@lidofinance/data-table": [ - "packages/data-table/index.ts" - ], - "@lidofinance/divider": [ - "packages/divider/index.ts" - ], - "@lidofinance/heading": [ - "packages/heading/index.tsx" - ], - "@lidofinance/hooks": [ - "packages/hooks/index.ts" - ], - "@lidofinance/icons": [ - "packages/icons/index.tsx" - ], - "@lidofinance/identicon": [ - "packages/identicon/index.ts" - ], - "@lidofinance/input": [ - "packages/input/index.ts" - ], - "@lidofinance/lido-logo": [ - "packages/lido-logo/index.ts" - ], - "@lidofinance/link": [ - "packages/link/index.ts" - ], - "@lidofinance/loaders": [ - "packages/loaders/index.ts" - ], - "@lidofinance/main-menu": [ - "packages/main-menu/index.ts" - ], - "@lidofinance/modal": [ - "packages/modal/index.ts" - ], - "@lidofinance/pagination": [ - "packages/pagination/index.ts" - ], - "@lidofinance/popover": [ - "packages/popover/index.ts" - ], - "@lidofinance/popup-menu": [ - "packages/popup-menu/index.ts" - ], - "@lidofinance/select": [ - "packages/select/index.ts" - ], - "@lidofinance/service-page": [ - "packages/service-page/index.ts" - ], - "@lidofinance/stack": [ - "packages/stack/index.ts" - ], - "@lidofinance/styled-system": [ - "packages/styled-system/index.ts" - ], - "@lidofinance/table": [ - "packages/table/index.ts" - ], - "@lidofinance/text": [ - "packages/text/index.ts" - ], - "@lidofinance/theme": [ - "packages/theme/index.ts" - ], - "@lidofinance/toast": [ - "packages/toast/index.ts" - ], - "@lidofinance/tooltip": [ - "packages/tooltip/index.ts" - ], - "@lidofinance/transition": [ - "packages/transition/index.ts" - ], - "@lidofinance/utils": [ - "packages/utils/index.ts" - ], - "@lidofinance/cookie-theme-toggler": [ - "packages/cookie-theme-toggler" - ], - "@lidofinance/content-theme": [ - "packages/content-theme" - ] - }, - "target": "es2019", - "loose": false, - "keepClassNames": true, - "externalHelpers": true - }, - "sourceMaps": true, - "minify": false, - "env": { - "targets": { - "chrome": "69", - "firefox": "68", - "safari": "13.1", - "node": "12" - }, - "mode": "entry", - "coreJs": "3.22" - }, - "exclude": "\\.(test|stories)\\.", - "module": { - "type": "commonjs" - } -} \ No newline at end of file diff --git a/.swcrc.esm b/.swcrc.esm deleted file mode 100644 index 70b12ea9..00000000 --- a/.swcrc.esm +++ /dev/null @@ -1,152 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/swcrc", - "jsc": { - "parser": { - "syntax": "typescript", - "tsx": true, - "dts": true, - "decorators": false, - "dynamicImport": false - }, - "transform": { - "react": { - "pragma": "React.createElement", - "pragmaFrag": "React.Fragment", - "throwIfNamespace": true, - "development": false, - "useBuiltins": false - } - }, - "baseUrl": ".", - "paths": { - "@lidofinance/accordion": [ - "packages/accordion/index.ts" - ], - "@lidofinance/address": [ - "packages/address/index.ts" - ], - "@lidofinance/block": [ - "packages/block/index.ts" - ], - "@lidofinance/box": [ - "packages/box/index.ts" - ], - "@lidofinance/button": [ - "packages/button/index.ts" - ], - "@lidofinance/checkbox": [ - "packages/checkbox/index.ts" - ], - "@lidofinance/chip": [ - "packages/chip/index.ts" - ], - "@lidofinance/container": [ - "packages/container/index.ts" - ], - "@lidofinance/data-table": [ - "packages/data-table/index.ts" - ], - "@lidofinance/divider": [ - "packages/divider/index.ts" - ], - "@lidofinance/heading": [ - "packages/heading/index.tsx" - ], - "@lidofinance/hooks": [ - "packages/hooks/index.ts" - ], - "@lidofinance/icons": [ - "packages/icons/index.tsx" - ], - "@lidofinance/identicon": [ - "packages/identicon/index.ts" - ], - "@lidofinance/input": [ - "packages/input/index.ts" - ], - "@lidofinance/lido-logo": [ - "packages/lido-logo/index.ts" - ], - "@lidofinance/link": [ - "packages/link/index.ts" - ], - "@lidofinance/loaders": [ - "packages/loaders/index.ts" - ], - "@lidofinance/main-menu": [ - "packages/main-menu/index.ts" - ], - "@lidofinance/modal": [ - "packages/modal/index.ts" - ], - "@lidofinance/pagination": [ - "packages/pagination/index.ts" - ], - "@lidofinance/popover": [ - "packages/popover/index.ts" - ], - "@lidofinance/popup-menu": [ - "packages/popup-menu/index.ts" - ], - "@lidofinance/select": [ - "packages/select/index.ts" - ], - "@lidofinance/service-page": [ - "packages/service-page/index.ts" - ], - "@lidofinance/stack": [ - "packages/stack/index.ts" - ], - "@lidofinance/styled-system": [ - "packages/styled-system/index.ts" - ], - "@lidofinance/table": [ - "packages/table/index.ts" - ], - "@lidofinance/text": [ - "packages/text/index.ts" - ], - "@lidofinance/theme": [ - "packages/theme/index.ts" - ], - "@lidofinance/toast": [ - "packages/toast/index.ts" - ], - "@lidofinance/tooltip": [ - "packages/tooltip/index.ts" - ], - "@lidofinance/transition": [ - "packages/transition/index.ts" - ], - "@lidofinance/utils": [ - "packages/utils/index.ts" - ], - "@lidofinance/cookie-theme-toggler": [ - "packages/cookie-theme-toggler" - ], - "@lidofinance/content-theme": [ - "packages/content-theme" - ] - }, - "target": "es2019", - "loose": false, - "keepClassNames": true, - "externalHelpers": true - }, - "sourceMaps": true, - "minify": false, - "env": { - "targets": { - "chrome": "69", - "firefox": "68", - "safari": "13.1", - "node": "12" - }, - "mode": "entry", - "coreJs": "3.22" - }, - "exclude": "\\.(test|stories)\\.", - "module": { - "type": "es6" - } -} \ No newline at end of file diff --git a/babel.config.js b/babel.config.js new file mode 100644 index 00000000..546eaedd --- /dev/null +++ b/babel.config.js @@ -0,0 +1,7 @@ +module.exports = { + presets: [ + '@babel/preset-env', + ['@babel/preset-react', { runtime: 'automatic' }], + '@babel/preset-typescript', + ], +} diff --git a/css-modules.d.ts b/css-modules.d.ts new file mode 100644 index 00000000..6c344ed3 --- /dev/null +++ b/css-modules.d.ts @@ -0,0 +1 @@ +declare module '*.module.css' diff --git a/jest.config.js b/jest.config.js index 36df2722..11764a31 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,29 +1,8 @@ +/** @type {import('ts-jest').JestConfigWithTsJest} */ module.exports = { - cacheDirectory: '.jest-cache', - coverageDirectory: '.jest-coverage', - // forked from default - // https://jestjs.io/docs/configuration#testmatch-arraystring - testMatch: [ - '**/__tests__/**/*.[jt]s', - '**/__tests__/**/*.[jt]sx', - '**/?(*.)+(spec|test).[jt]s', - '**/?(*.)+(spec|test).[jt]sx', - ], - collectCoverageFrom: ['packages/**/*'], - coverageReporters: ['html', 'text'], - coverageThreshold: { - global: { - branches: 100, - functions: 100, - lines: 100, - statements: 100, - }, - }, - transform: { - '^.+\\.((js|ts)x?)$': ['@swc/jest'], - }, + preset: 'ts-jest', + testEnvironment: 'jsdom', moduleNameMapper: { - '^@lidofinance/(.*)$': '/packages/$1/src', + '\\.(css|scss)$': 'identity-obj-proxy', }, - testEnvironment: 'jest-environment-jsdom', } diff --git a/package.json b/package.json index cf6ae780..7feca010 100644 --- a/package.json +++ b/package.json @@ -9,22 +9,26 @@ "bugs": { "url": "https://github.com/lidofinance/ui/issues" }, - "main": "dist/cjs/index.js", - "module": "dist/esm/index.js", - "types": "dist/types/index.d.ts", + "main": "dist/index.cjs.js", + "module": "dist/index.es.js", + "types": "dist/index.d.ts", "files": [ "dist" ], "scripts": { - "dev": "start-storybook -p 5555", - "build-storybook": "build-storybook", - "build": "zx ./scripts/build.cjs", + "dev": "storybook dev -p 5555", + "build-storybook": "storybook build", + "build": "zx scripts/build.mjs", "test": "jest", "test:coverage": "jest coverage", - "lint": "eslint .", - "lint:fix": "eslint --fix .", + "lint": "yarn lint:js && yarn lint:css", + "lint:js": "eslint .", + "lint:js:fix": "yarn lint:js --fix", + "lint:css": "stylelint '**/*.css'", + "lint:css:fix": "yarn lint:css --fix", + "format": "prettier -c '**/*.{js,mjs,cjs,jsx,ts,tsx,css}'", + "format:fix": "yarn format -w", "types": "tsc --noEmit", - "generate-swcrc": "zx ./scripts/generate-swcrc.cjs", "prepare": "husky install", "release": "semantic-release", "release:dry": "yarn release --dry-run", @@ -37,39 +41,37 @@ "styled-components": "^5.3.5" }, "dependencies": { - "@styled-system/should-forward-prop": "5.1.5", - "@swc/helpers": "^0.4.11", - "@swc/plugin-styled-components": "^1.2.10", + "classnames": "^2.3.2", "react-collapsed": "3.0.2", "react-jazzicon": "^1.0.4", "react-toastify": "7.0.4", "react-transition-group": "4", - "styled-system": "5.1.5", "ua-parser-js": "^1.0.35", "use-callback-ref": "1.2.5" }, "devDependencies": { + "@babel/preset-env": "^7.22.9", + "@babel/preset-react": "^7.22.5", + "@babel/preset-typescript": "^7.22.5", "@commitlint/cli": "13.1.0", "@commitlint/config-conventional": "13.1.0", + "@csstools/postcss-cascade-layers": "^4.0.0", + "@csstools/postcss-global-data": "^2.0.0", "@semantic-release/commit-analyzer": "^9.0.2", "@semantic-release/release-notes-generator": "^10.0.3", "@semrel-extra/npm": "^1.2.2", - "@storybook/addon-docs": "^6.5.10", - "@storybook/addon-essentials": "^6.5.10", - "@storybook/addons": "^6.5.10", - "@storybook/builder-webpack5": "^6.5.10", - "@storybook/cli": "^6.5.10", - "@storybook/manager-webpack5": "^6.5.10", - "@storybook/react": "^6.5.10", - "@storybook/storybook-deployer": "^2.8.12", - "@storybook/theming": "^6.5.10", + "@storybook/addon-docs": "^7.0.27", + "@storybook/addon-essentials": "^7.0.27", + "@storybook/addons": "^7.0.27", + "@storybook/cli": "^7.0.27", + "@storybook/react": "^7.0.27", + "@storybook/react-vite": "^7.0.27", + "@storybook/storybook-deployer": "^2.8.16", + "@storybook/theming": "^7.0.27", "@svgr/core": "5.5.0", "@svgr/plugin-jsx": "5.5.0", "@svgr/plugin-prettier": "5.5.0", "@svgr/plugin-svgo": "5.5.0", - "@swc/cli": "^0.1.57", - "@swc/core": "^1.2.245", - "@swc/jest": "^0.2.22", "@testing-library/react": "^13.3.0", "@types/jest": "^28.1.8", "@types/react": "18.0.17", @@ -80,35 +82,40 @@ "@types/styled-system": "^5.1.15", "@types/styled-system__should-forward-prop": "^5.1.2", "@types/ua-parser-js": "^0.7.36", - "@typescript-eslint/eslint-plugin": "^5.36.1", - "@typescript-eslint/parser": "^5.36.1", + "@typescript-eslint/eslint-plugin": "^6.1.0", + "@typescript-eslint/parser": "^6.1.0", + "@vitejs/plugin-react": "^4.0.3", + "babel-jest": "^29.6.1", "browserslist": "^4.21.3", - "conventional-changelog-conventionalcommits": "4.6.3", - "eslint": "7.32.0", - "eslint-config-prettier": "8.3.0", - "eslint-plugin-jsx-a11y": "6.4.1", - "eslint-plugin-prettier": "3.4.1", - "eslint-plugin-react": "7.24.0", - "eslint-plugin-react-hooks": "4.2.0", + "eslint": "8.45.0", + "eslint-plugin-jsx-a11y": "6.7.1", + "eslint-plugin-react": "7.32.2", + "eslint-plugin-react-hooks": "4.6.0", + "eslint-plugin-storybook": "^0.6.12", "husky": "^7.0.1", - "jest": "^29.0.1", + "identity-obj-proxy": "^3.0.0", + "jest": "^29.6.1", "jest-environment-jsdom": "^29.0.1", "jest-haste-map": "^29.0.1", "jest-resolve": "^29.0.1", "jest-runner": "^29.0.1", "jest-styled-components": "^7.1.1", - "lint-staged": "11.1.2", - "prettier": "2.3.2", + "postcss-custom-media": "^10.0.0", + "postcss-nesting": "^12.0.0", + "prettier": "~3.0.0", "react": "18.2.0", "react-dom": "18.2.0", "react-is": "18.2.0", - "rimraf": "3.0.2", + "react-test-renderer": "^18.2.0", "semantic-release": "^19.0.5", - "storybook-addon-swc": "^1.1.8", - "storybook-dark-mode": "^1.1.0", + "storybook": "^7.0.27", "styled-components": "^5.3.5", + "stylelint": "^15.10.2", + "stylelint-config-standard": "^34.0.0", + "ts-jest": "^29.1.1", "tslib": "2.3.1", - "typescript": "4.8", - "zx": "^7.0.8" + "typescript": "~4.9", + "vite": "^4.4.3", + "zx": "^7.2.3" } } diff --git a/packages/accordion/Accordion.module.css b/packages/accordion/Accordion.module.css new file mode 100644 index 00000000..664a90de --- /dev/null +++ b/packages/accordion/Accordion.module.css @@ -0,0 +1,70 @@ +.accordion { + margin: 0 0 var(--lido-space-sm) 0; + background: var(--lido-color-foreground); + border-radius: var(--lido-border-radius-xl); +} + +.summary { + cursor: pointer; + display: flex; + align-items: center; + outline: none; + min-height: 40px; + box-sizing: content-box; + padding: var(--lido-space-lg); + + @media (--lido-media-breakpoint-up-md) { + padding: var(--lido-space-lg) var(--lido-space-xxl); + } +} + +.title { + color: var(--lido-color-text); + flex-grow: 1; + font-weight: 800; + font-size: var(--lido-font-size-xs); + line-height: 1.6em; +} + +.content { + color: var(--lido-color-text-secondary); + font-weight: 400; + font-size: var(--lido-font-size-xxs); + line-height: 1.6em; + padding: var(--lido-space-lg); + padding-top: 0; + + @media (--lido-media-breakpoint-up-md) { + padding: var(--lido-space-xxl); + padding-top: 0; + } + + /* stylelint-disable no-descending-specificity */ + p, + ul, + ol { + margin: 0 0 1.6em; + + &:last-child { + margin-bottom: 0; + } + } + + ul, + ol { + padding: 0 0 0 1.5em; + } + /* stylelint-enable no-descending-specificity */ +} + +.arrow { + flex-shrink: 0; + margin: -2px; + margin-left: var(--lido-space-md); + fill: var(--lido-color-text-secondary); +} + +.arrowExpanded { + transition: transform var(--lido-transition-duration-norm) ease; + transform: rotate(180deg); +} diff --git a/packages/accordion/Accordion.stories.tsx b/packages/accordion/Accordion.stories.tsx index bdb10f86..3c824c4f 100644 --- a/packages/accordion/Accordion.stories.tsx +++ b/packages/accordion/Accordion.stories.tsx @@ -1,13 +1,12 @@ -import { Story, Meta } from '@storybook/react' -import { AccordionProps } from './types' -import Accordion from './Accordion' +import { StoryFn, Meta } from '@storybook/react' +import { Accordion, AccordionProps } from '.' export default { component: Accordion, title: 'Layout/Accordion', -} as Meta +} satisfies Meta -export const Basic: Story = (props) => ( +export const Basic: StoryFn = (props) => ( Liquid staking protocols allow users to earn staking rewards without locking assets or maintaining staking infrastructure. Users of these protocols can @@ -32,7 +31,7 @@ Basic.argTypes = { }, } -export const List: Story = () => ( +export const List: StoryFn = () => ( <>

diff --git a/packages/accordion/Accordion.tsx b/packages/accordion/Accordion.tsx index 38ff7b6d..837e2e6b 100644 --- a/packages/accordion/Accordion.tsx +++ b/packages/accordion/Accordion.tsx @@ -1,29 +1,37 @@ -import React, { ForwardedRef, forwardRef } from 'react' -import { AccordionProps } from './types' import { - AccordionStyle, - AccordionSummaryStyle, - AccordionTitleStyle, - AccordionArrowStyle, - AccordionContentStyle, -} from './AccordionStyles' + ComponentPropsWithoutRef, + ForwardedRef, + forwardRef, + ReactNode, +} from 'react' import { useExpanded } from './useExpanded' +import { ArrowBottom } from '../icons' +import styles from './Accordion.module.css' +import cn from 'classnames' -function Accordion(props: AccordionProps, ref?: ForwardedRef) { - const { defaultExpanded, summary, children, ...rest } = props - const { toggleProps, collapseProps, isExpanded } = useExpanded(props) - - return ( - - - {summary} - - -

- {children} -
- - ) +export type AccordionProps = ComponentPropsWithoutRef<'div'> & { + defaultExpanded?: boolean + summary: ReactNode } -export default forwardRef(Accordion) +export const Accordion = forwardRef( + (props: AccordionProps, ref?: ForwardedRef) => { + const { defaultExpanded, summary, children, className, ...rest } = props + const { toggleProps, collapseProps, isExpanded } = useExpanded(props) + + return ( +
+
+
{summary}
+ +
+
+
{children}
+
+
+ ) + }, +) +Accordion.displayName = 'Accordion' diff --git a/packages/accordion/AccordionStyles.tsx b/packages/accordion/AccordionStyles.tsx deleted file mode 100644 index 260bf4bb..00000000 --- a/packages/accordion/AccordionStyles.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import styled from 'styled-components' -import { ArrowBottom } from '@lidofinance/icons' - -export const AccordionStyle = styled.div` - margin: 0 0 ${({ theme }) => theme.spaceMap.sm}px 0; - background: var(--lido-color-foreground); - border-radius: ${({ theme }) => theme.borderRadiusesMap.xl}px; -` - -export const AccordionSummaryStyle = styled.div` - padding: ${({ theme }) => theme.spaceMap.lg}px - ${({ theme }) => theme.spaceMap.xxl}px; - cursor: pointer; - display: flex; - align-items: center; - outline: none; - min-height: 40px; - box-sizing: content-box; - - ${({ theme }) => theme.mediaQueries.md} { - padding: ${({ theme }) => theme.spaceMap.lg}px; - } -` - -export const AccordionTitleStyle = styled.div` - color: var(--lido-color-text); - flex-grow: 1; - font-weight: 800; - font-size: ${({ theme }) => theme.fontSizesMap.xs}px; - line-height: 1.6em; -` - -export const AccordionArrowStyle = styled(ArrowBottom)<{ $expanded: boolean }>` - flex-shrink: 0; - transform: rotate(${(props) => (props.$expanded ? 180 : 0)}deg); - transition: transform ${({ theme }) => theme.duration.norm} ease; - margin: -2px; - margin-left: ${({ theme }) => theme.spaceMap.md}px; - fill: var(--lido-color-textSecondary); -` - -export const AccordionContentStyle = styled.div` - color: var(--lido-color-textSecondary); - padding: ${({ theme }) => theme.spaceMap.xxl}px; - padding-top: 0; - font-weight: 400; - font-size: ${({ theme }) => theme.fontSizesMap.xxs}px; - line-height: 1.6em; - - ${({ theme }) => theme.mediaQueries.md} { - padding: ${({ theme }) => theme.spaceMap.lg}px; - padding-top: 0; - } - - p, - ul, - ol { - margin: 0 0 1.6em 0; - - &:last-child { - margin-bottom: 0; - } - } - - ul, - ol { - padding: 0 0 0 1.5em; - } -` diff --git a/packages/accordion/index.ts b/packages/accordion/index.ts index 0fa0ef46..5d69eaa8 100644 --- a/packages/accordion/index.ts +++ b/packages/accordion/index.ts @@ -1,2 +1 @@ -export { default as Accordion } from './Accordion' -export * from './types' +export * from './Accordion' diff --git a/packages/accordion/types.ts b/packages/accordion/types.ts deleted file mode 100644 index 5fd960c4..00000000 --- a/packages/accordion/types.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { LidoComponentProps } from '@lidofinance/utils' -import React from 'react' -export type { Theme } from '@lidofinance/theme' - -export type AccordionProps = LidoComponentProps< - 'div', - { - defaultExpanded?: boolean - summary: React.ReactNode - } -> diff --git a/packages/accordion/useExpanded.ts b/packages/accordion/useExpanded.ts index 95e2319a..d42a3f7a 100644 --- a/packages/accordion/useExpanded.ts +++ b/packages/accordion/useExpanded.ts @@ -1,5 +1,5 @@ import { useCallback, useEffect, useState } from 'react' -import { AccordionProps } from './types' +import { AccordionProps } from './Accordion' import useCollapse from 'react-collapsed' import { GetCollapsePropsOutput, @@ -21,7 +21,7 @@ export const useExpanded: UseExpanded = ({ defaultExpanded = false }) => { const handleToggle = useCallback( () => setExpanded((previous) => !previous), - [] + [], ) const { getToggleProps, getCollapseProps } = useCollapse({ isExpanded }) diff --git a/packages/address/Address.module.css b/packages/address/Address.module.css new file mode 100644 index 00000000..13500d9e --- /dev/null +++ b/packages/address/Address.module.css @@ -0,0 +1,17 @@ +.address { + position: relative; + display: inline-block; + font-weight: 400; +} + +.full { + position: absolute; + overflow: hidden; + inset: 0; + color: transparent; +} + +.trimmed { + user-select: none; + pointer-events: none; +} diff --git a/packages/address/Address.stories.tsx b/packages/address/Address.stories.tsx index c905a620..faac01fc 100644 --- a/packages/address/Address.stories.tsx +++ b/packages/address/Address.stories.tsx @@ -1,6 +1,5 @@ -import { Story, Meta } from '@storybook/react' -import { AddressProps } from './types' -import Address from './Address' +import { Meta, StoryFn } from '@storybook/react' +import { Address, AddressProps } from '.' export default { component: Address, @@ -14,6 +13,6 @@ export default { control: { type: 'range', min: 3, max: 21, step: 1 }, }, }, -} as Meta +} satisfies Meta -export const Basic: Story = (props) =>
+export const Basic: StoryFn = (props) =>
diff --git a/packages/address/Address.tsx b/packages/address/Address.tsx index 5cd5bc91..0b565e6c 100644 --- a/packages/address/Address.tsx +++ b/packages/address/Address.tsx @@ -1,21 +1,24 @@ -import React, { ForwardedRef, forwardRef } from 'react' -import { AddressProps } from './types' -import { - AddressStyle, - AddressFullStyle, - AddressTrimmedStyle, -} from './AddressStyles' +import { ComponentPropsWithoutRef, ForwardedRef, forwardRef } from 'react' import { trimAddress } from './trimAddress' +import styles from './Address.module.css' +import cn from 'classnames' -function Address(props: AddressProps, ref?: ForwardedRef) { - const { symbols = 3, address, ...rest } = props - - return ( - - {address} - {trimAddress(address, symbols)} - - ) +export type AddressProps = ComponentPropsWithoutRef<'div'> & { + address: string + symbols?: number } -export default forwardRef(Address) +export const Address = forwardRef( + ( + { symbols = 3, address, className, ...rest }: AddressProps, + ref?: ForwardedRef, + ) => { + return ( +
+ {address} + {trimAddress(address, symbols)} +
+ ) + }, +) +Address.displayName = 'Address' diff --git a/packages/address/AddressStyles.tsx b/packages/address/AddressStyles.tsx deleted file mode 100644 index e0836ce1..00000000 --- a/packages/address/AddressStyles.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import styled from 'styled-components' - -export const AddressStyle = styled.div` - position: relative; - display: inline-block; - font-weight: 400; -` - -export const AddressFullStyle = styled.span` - position: absolute; - overflow: hidden; - top: 0; - right: 0; - bottom: 0; - left: 0; - color: transparent; -` - -export const AddressTrimmedStyle = styled.span` - user-select: none; - pointer-events: none; -` diff --git a/packages/address/index.ts b/packages/address/index.ts index 82d55c03..653f3f8a 100644 --- a/packages/address/index.ts +++ b/packages/address/index.ts @@ -1,3 +1,2 @@ -export { default as Address } from './Address' +export * from './Address' export * from './trimAddress' -export * from './types' diff --git a/packages/address/types.ts b/packages/address/types.ts deleted file mode 100644 index 1ec4560f..00000000 --- a/packages/address/types.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { LidoComponentProps } from '@lidofinance/utils' - -export type AddressProps = LidoComponentProps< - 'div', - { - address: string - symbols?: number - } -> diff --git a/packages/addressBadge/AddressBadgeStyles.tsx b/packages/addressBadge/AddressBadge.module.css similarity index 56% rename from packages/addressBadge/AddressBadgeStyles.tsx rename to packages/addressBadge/AddressBadge.module.css index 93703672..cb3ab72f 100644 --- a/packages/addressBadge/AddressBadgeStyles.tsx +++ b/packages/addressBadge/AddressBadge.module.css @@ -1,16 +1,15 @@ -import { IdenticonBadge } from '@lidofinance/identicon' -import styled from 'styled-components' - -export const AddressBadgeStyle = styled(IdenticonBadge)` +.addressBadge { max-width: 100%; box-sizing: border-box; overflow: hidden; + & > * { flex-shrink: 0; } + & > :first-child { flex-shrink: 1; overflow: hidden; text-overflow: ellipsis; } -` +} diff --git a/packages/addressBadge/AddressBadge.stories.tsx b/packages/addressBadge/AddressBadge.stories.tsx index 17c74698..c0c10dce 100644 --- a/packages/addressBadge/AddressBadge.stories.tsx +++ b/packages/addressBadge/AddressBadge.stories.tsx @@ -1,6 +1,5 @@ -import { Story, Meta } from '@storybook/react' -import { AddressBadgeProps } from './types' -import AddressBadge from './AddressBadge' +import { StoryFn, Meta } from '@storybook/react' +import { AddressBadge, AddressBadgeProps } from '.' export default { component: AddressBadge, @@ -18,8 +17,8 @@ export default { control: { type: 'range', min: 3, max: 21, step: 1 }, }, }, -} as Meta +} satisfies Meta -export const Basic: Story = (props) => ( +export const Basic: StoryFn = (props) => ( ) diff --git a/packages/addressBadge/AddressBadge.tsx b/packages/addressBadge/AddressBadge.tsx index c095608c..97c00a05 100644 --- a/packages/addressBadge/AddressBadge.tsx +++ b/packages/addressBadge/AddressBadge.tsx @@ -1,22 +1,40 @@ -import React, { ForwardedRef, forwardRef } from 'react' -import { useBreakpoint } from '@lidofinance/hooks' -import { AddressBadgeStyle } from './AddressBadgeStyles' -import { AddressBadgeProps } from './types' +import { ForwardedRef, forwardRef } from 'react' +import { useBreakpoint } from '../hooks' +import cn from 'classnames' +import styles from './AddressBadge.module.css' +import { IdenticonBadge, IdenticonBadgeProps } from '../identicon' -function AddressBadge( - props: AddressBadgeProps, - ref?: ForwardedRef -) { - const { address, symbolsMobile = 3, symbolsDesktop = 6 } = props - const isMobile = useBreakpoint('md') - - return ( - - ) +export type AddressBadgeProps = Omit< + IdenticonBadgeProps, + 'address' | 'symbols' +> & { + address?: string + symbolsMobile?: number + symbolsDesktop?: number } -export default forwardRef(AddressBadge) +export const AddressBadge = forwardRef( + ( + { + address, + symbolsMobile = 3, + symbolsDesktop = 6, + className, + ...rest + }: AddressBadgeProps, + ref?: ForwardedRef, + ) => { + const isMobile = useBreakpoint('md') + + return ( + + ) + }, +) +AddressBadge.displayName = 'AddressBadge' diff --git a/packages/addressBadge/index.ts b/packages/addressBadge/index.ts index 694a7361..5cd93d37 100644 --- a/packages/addressBadge/index.ts +++ b/packages/addressBadge/index.ts @@ -1,2 +1 @@ -export { default as AddressBadge } from './AddressBadge' -export * from './types' +export * from './AddressBadge' diff --git a/packages/addressBadge/types.ts b/packages/addressBadge/types.ts deleted file mode 100644 index fa9c986e..00000000 --- a/packages/addressBadge/types.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { LidoComponentProps } from '@lidofinance/utils' - -export type AddressBadgeProps = LidoComponentProps< - 'div', - { - address?: string - symbolsMobile?: number - symbolsDesktop?: number - } -> diff --git a/packages/block/Block.module.css b/packages/block/Block.module.css new file mode 100644 index 00000000..b0c2e4bb --- /dev/null +++ b/packages/block/Block.module.css @@ -0,0 +1,38 @@ +.foreground { + background: var(--lido-color-foreground); + color: var(--lido-color-text-secondary); +} + +.background { + background: var(--lido-color-background); + color: var(--lido-color-text-secondary); +} + +.accent { + background: var(--lido-color-accent); + color: var(--lido-color-accent-contrast); +} + +.flat { + box-shadow: none; +} + +.shadow { + box-shadow: var(--lido-shadows-lg) var(--lido-color-shadow-light); +} + +.padding { + padding: var(--lido-space-lg); + + @media (--lido-media-breakpoint-up-md) { + padding: var(--lido-space-xxl); + } +} + +.block { + font-weight: 400; + font-size: var(--lido-font-size-xxs); + line-height: 1.6em; + border-radius: var(--lido-border-radius-xl); + margin: 0; +} diff --git a/packages/block/Block.stories.tsx b/packages/block/Block.stories.tsx index 3d9a0583..f0e296d6 100644 --- a/packages/block/Block.stories.tsx +++ b/packages/block/Block.stories.tsx @@ -1,6 +1,5 @@ -import { Story, Meta } from '@storybook/react' -import { BlockProps, BlockColor, BlockVariant } from './types' -import Block from './Block' +import { StoryFn, Meta } from '@storybook/react' +import { Block, BlockProps, BlockColor, BlockVariant } from '.' const getOptions = (enumObject: Record) => Object.values(enumObject).filter((value) => typeof value === 'string') @@ -24,6 +23,6 @@ export default { control: 'inline-radio', }, }, -} as Meta +} satisfies Meta -export const Basic: Story = (props) => +export const Basic: StoryFn = (props) => diff --git a/packages/block/Block.tsx b/packages/block/Block.tsx index a8029992..edf48c2e 100644 --- a/packages/block/Block.tsx +++ b/packages/block/Block.tsx @@ -1,24 +1,51 @@ -import React, { ForwardedRef, forwardRef } from 'react' -import { BlockStyle } from './BlockStyles' -import { BlockProps } from './types' +import { ComponentPropsWithoutRef, ForwardedRef, forwardRef } from 'react' +import cn from 'classnames' +import styles from './Block.module.css' -function Block(props: BlockProps, ref?: ForwardedRef) { - const { - color = 'foreground', - variant = 'flat', - paddingLess = false, - ...rest - } = props +export enum BlockVariant { + flat, + shadow, +} +export type BlockVariants = keyof typeof BlockVariant + +export enum BlockColor { + foreground, + background, + accent, +} +export type BlockColors = keyof typeof BlockColor - return ( - - ) +export type BlockProps = ComponentPropsWithoutRef<'div'> & { + color?: BlockColors + variant?: BlockVariants + paddingLess?: boolean } -export default forwardRef(Block) +export const Block = forwardRef( + ( + { + color = 'foreground', + variant = 'flat', + paddingLess = false, + className, + ...rest + }: BlockProps, + ref?: ForwardedRef, + ) => { + return ( +
+ ) + }, +) +Block.displayName = 'Block' diff --git a/packages/block/BlockStyles.tsx b/packages/block/BlockStyles.tsx deleted file mode 100644 index 58e7d4de..00000000 --- a/packages/block/BlockStyles.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import styled, { css } from 'styled-components' -import { Theme } from '@lidofinance/theme' -import { BlockVariants, BlockColors } from './types' - -type InjectedProps = { - $variant: BlockVariants - $color: BlockColors - $paddingLess: boolean - theme: Theme -} - -const colors = { - foreground: css` - background: var(--lido-color-foreground); - color: var(--lido-color-textSecondary); - `, - background: css` - background: var(--lido-color-background); - color: var(--lido-color-textSecondary); - `, - accent: css` - background: var(--lido-color-accent); - color: var(--lido-color-accentContrast); - `, -} - -const variants = { - flat: css` - box-shadow: none; - `, - shadow: css` - box-shadow: ${({ theme }) => theme.boxShadows.lg} - var(--lido-colors-shadowLight); - `, -} - -const paddings = css` - padding: ${({ theme }) => theme.spaceMap.xxl}px; - - ${({ theme }) => theme.mediaQueries.md} { - padding: ${({ theme }) => theme.spaceMap.lg}px; - } -` - -export const BlockStyle = styled.div` - font-weight: 400; - font-size: ${({ theme }) => theme.fontSizesMap.xxs}px; - line-height: 1.6em; - border-radius: ${({ theme }) => theme.borderRadiusesMap.xl}px; - margin: 0; - - ${({ $paddingLess }) => !$paddingLess && paddings} - - ${({ $variant }) => variants[$variant]} - ${({ $color }) => colors[$color]} -` diff --git a/packages/block/index.ts b/packages/block/index.ts index 71e233e7..d690ca0e 100644 --- a/packages/block/index.ts +++ b/packages/block/index.ts @@ -1,2 +1 @@ -export { default as Block } from './Block' -export * from './types' +export * from './Block' diff --git a/packages/block/types.tsx b/packages/block/types.tsx deleted file mode 100644 index c43d18cd..00000000 --- a/packages/block/types.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { LidoComponentProps } from '@lidofinance/utils' -export type { Theme } from '@lidofinance/theme' - -export enum BlockVariant { - flat, - shadow, -} -export type BlockVariants = keyof typeof BlockVariant - -export enum BlockColor { - foreground, - background, - accent, -} -export type BlockColors = keyof typeof BlockColor - -export type BlockProps = LidoComponentProps< - 'div', - { - color?: BlockColors - variant?: BlockVariants - paddingLess?: boolean - } -> diff --git a/packages/box/Box.stories.tsx b/packages/box/Box.stories.tsx deleted file mode 100644 index 5314add5..00000000 --- a/packages/box/Box.stories.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { Story, Meta } from '@storybook/react' -import Box from './Box' - -export default { - component: Box, - title: 'Styled System/Box', -} as Meta - -export const Basic: Story = (props) => ( - -) - -export const Breakpoints: Story = (props) => ( - - Example - -) diff --git a/packages/box/Box.tsx b/packages/box/Box.tsx deleted file mode 100644 index 307b57a6..00000000 --- a/packages/box/Box.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import styled from 'styled-components' -import { withStyledSystem } from '@lidofinance/styled-system' - -const Box = styled.div`` - -export default withStyledSystem(Box) diff --git a/packages/box/index.ts b/packages/box/index.ts deleted file mode 100644 index b1fc653c..00000000 --- a/packages/box/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as Box } from './Box' diff --git a/packages/button/Button.module.css b/packages/button/Button.module.css new file mode 100644 index 00000000..fb55a4b9 --- /dev/null +++ b/packages/button/Button.module.css @@ -0,0 +1,443 @@ +@layer loader, button; + +@layer button { + .button { + box-sizing: border-box; + margin: 0; + border: none; + outline: none; + white-space: nowrap; + overflow: hidden; + position: relative; + background: transparent; + font-family: inherit; + font-weight: 700; + + &::before { + content: ''; + position: absolute; + inset: 0; + pointer-events: none; + border-radius: inherit; + display: none; + } + + &:not(:disabled) { + cursor: pointer; + } + + &:disabled { + opacity: 0.5; + } + } + + .loading.button:disabled { + opacity: 1; + } + + .fullwidth { + width: 100%; + } + + .xxs { + line-height: 1em; + font-size: var(--lido-font-size-xxxs); + border-radius: var(--lido-border-radius-sm); + padding: 11px 16px; + min-width: 50px; + } + + .xxs.square { + padding: 11px; + min-width: 0; + } + + .xs { + line-height: 1em; + font-size: var(--lido-font-size-xxs); + border-radius: var(--lido-border-radius-sm); + padding: 10px 16px; + min-width: 60px; + } + + .xs.square { + padding: 10px; + min-width: 0; + } + + .sm { + line-height: 1em; + font-size: var(--lido-font-size-xs); + border-radius: var(--lido-border-radius-sm); + padding: 16px 24px; + min-width: 100px; + } + + .sm.square { + padding: 16px; + min-width: 0; + } + + .md { + line-height: 1em; + font-size: var(--lido-font-size-xs); + border-radius: var(--lido-border-radius-lg); + padding: 21px 44px; + min-width: 120px; + } + + .md.square { + padding: 21px; + min-width: 0; + } + + .lg { + line-height: 1em; + font-size: var(--lido-font-size-sm); + border-radius: var(--lido-border-radius-lg); + box-shadow: var(--lido-shadows-md) var(--lido-color-shadow-dark); + padding: 24px 64px; + min-width: 160px; + } + + .lg.square { + padding: 24px; + min-width: 0; + } + + @keyframes ripple { + to { + transform: scale(4); + opacity: 0; + } + } + + .ripple { + position: absolute; + border-radius: 50%; + transform: scale(0); + pointer-events: none; + animation: ripple 0.8s linear; + background-color: currentcolor; + opacity: 0.4; + } + + .filled { + transition: background-color var(--lido-transition-duration-fast) ease; + } + + .filled.primary { + color: var(--lido-color-primary-contrast); + background-color: var(--lido-color-primary); + + &:focus-visible, + &:not(:disabled):hover { + background-color: var(--lido-color-primary-hover); + } + } + + .filled.secondary { + color: var(--lido-color-secondary-contrast); + background-color: var(--lido-color-secondary); + + &:focus-visible, + &:not(:disabled):hover { + background-color: var(--lido-color-secondary-hover); + } + } + + .filled.warning { + color: var(--lido-color-warning-contrast); + background-color: var(--lido-color-warning); + + &:focus-visible, + &:not(:disabled):hover { + background-color: var(--lido-color-warning-hover); + } + } + + .filled.error { + color: var(--lido-color-error-contrast); + background-color: var(--lido-color-error); + + &:focus-visible, + &:not(:disabled):hover { + background-color: var(--lido-color-error-hover); + } + } + + .filled.success { + color: var(--lido-color-success-contrast); + background-color: var(--lido-color-success); + + &:focus-visible, + &:not(:disabled):hover { + background-color: var(--lido-color-success-hover); + } + } + + .outlined { + transition: + background-color var(--lido-transition-duration-fast) ease, + color var(--lido-transition-duration-fast) ease; + + &::before { + display: block; + } + } + + .outlined.primary { + color: var(--lido-color-primary); + + &::before { + border: 1px solid var(--lido-color-primary); + } + + &:focus-visible, + &:not(:disabled):hover { + color: var(--lido-color-primary-contrast); + background-color: var(--lido-color-primary-hover); + } + } + + .outlined.secondary { + color: var(--lido-color-secondary); + + &::before { + border: 1px solid var(--lido-color-secondary); + } + + &:focus-visible, + &:not(:disabled):hover { + color: var(--lido-color-secondary-contrast); + background-color: var(--lido-color-secondary-hover); + } + } + + .outlined.warning { + color: var(--lido-color-warning); + + &::before { + border: 1px solid var(--lido-color-warning); + } + + &:focus-visible, + &:not(:disabled):hover { + color: var(--lido-color-warning-contrast); + background-color: var(--lido-color-warning-hover); + } + } + + .outlined.error { + color: var(--lido-color-error); + + &::before { + border: 1px solid var(--lido-color-error); + } + + &:focus-visible, + &:not(:disabled):hover { + color: var(--lido-color-error-contrast); + background-color: var(--lido-color-error-hover); + } + } + + .outlined.success { + color: var(--lido-color-success); + + &::before { + border: 1px solid var(--lido-color-success); + } + + &:focus-visible, + &:not(:disabled):hover { + color: var(--lido-color-success-contrast); + background-color: var(--lido-color-success-hover); + } + } + + .text { + background-color: var(--lido-color-foreground); + + &::before { + display: block; + transition: opacity var(--lido-transition-duration-fast) ease; + opacity: 0; + } + + &:focus-visible, + &:not(:disabled):hover { + &::before { + opacity: 0.1; + } + } + } + + .text.primary { + color: var(--lido-color-primary); + + &::before { + background-color: var(--lido-color-primary); + } + } + + .text.secondary { + color: var(--lido-color-secondary); + + &::before { + background-color: var(--lido-color-secondary); + } + } + + .text.warning { + color: var(--lido-color-warning); + + &::before { + background-color: var(--lido-color-warning); + } + } + + .text.error { + color: var(--lido-color-error); + + &::before { + background-color: var(--lido-color-error); + } + } + + .text.success { + color: var(--lido-color-success); + + &::before { + background-color: var(--lido-color-success); + } + } + + .ghost { + &::before { + display: block; + transition: opacity var(--lido-transition-duration-fast) ease; + opacity: 0; + } + + &:focus-visible, + &:not(:disabled):hover { + &::before { + opacity: 0.1; + } + } + } + + .ghost.primary { + color: var(--lido-color-primary); + + &::before { + background-color: var(--lido-color-primary); + } + } + + .ghost.secondary { + color: var(--lido-color-secondary); + + &::before { + background-color: var(--lido-color-secondary); + } + } + + .ghost.warning { + color: var(--lido-color-warning); + + &::before { + background-color: var(--lido-color-warning); + } + } + + .ghost.error { + color: var(--lido-color-error); + + &::before { + background-color: var(--lido-color-error); + } + } + + .ghost.success { + color: var(--lido-color-success); + + &::before { + background-color: var(--lido-color-success); + } + } + + .translucent { + &::before { + display: block; + transition: opacity var(--lido-transition-duration-fast) ease; + opacity: 0.1; + } + + &:focus-visible, + &:not(:disabled):hover { + &::before { + opacity: 0.2; + } + } + } + + .translucent.primary { + color: var(--lido-color-primary); + + &::before { + background-color: var(--lido-color-primary); + } + } + + .translucent.secondary { + color: var(--lido-color-secondary); + + &::before { + background-color: var(--lido-color-secondary); + } + } + + .translucent.warning { + color: var(--lido-color-warning); + + &::before { + background-color: var(--lido-color-warning); + } + } + + .translucent.error { + color: var(--lido-color-error); + + &::before { + background-color: var(--lido-color-error); + } + } + + .translucent.success { + color: var(--lido-color-success); + + &::before { + background-color: var(--lido-color-success); + } + } + + .content { + position: relative; + pointer-events: none; + } + + .hidden { + visibility: hidden; + } + + .loader { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + color: currentcolor; + pointer-events: none; + } +} diff --git a/packages/button/Button.stories.tsx b/packages/button/Button.stories.tsx index a0c4fc08..62546558 100644 --- a/packages/button/Button.stories.tsx +++ b/packages/button/Button.stories.tsx @@ -1,14 +1,14 @@ -import { Story, Meta } from '@storybook/react' -import { Whitepaper } from '@lidofinance/icons' +import { StoryFn, Meta } from '@storybook/react' +import { Whitepaper } from '../icons' import { + Button, ButtonProps, ButtonIconProps, ButtonColor, ButtonSize, ButtonVariant, -} from './types' -import Button from './Button' -import ButtonIcon from './ButtonIcon' + ButtonIcon, +} from '.' const getOptions = (enumObject: Record) => Object.values(enumObject).filter((value) => typeof value === 'string') @@ -23,6 +23,7 @@ export default { disabled: false, fullwidth: false, loading: false, + square: false, children: 'Example', }, argTypes: { @@ -39,15 +40,15 @@ export default { control: 'inline-radio', }, }, -} as Meta +} satisfies Meta -export const Basic: Story = (props) => - + , ) expect(container.firstChild?.firstChild).toMatchSnapshot() diff --git a/packages/button/Button.tsx b/packages/button/Button.tsx index 7c0fe73b..97b29f4d 100644 --- a/packages/button/Button.tsx +++ b/packages/button/Button.tsx @@ -1,11 +1,8 @@ -import React, { ForwardedRef, forwardRef } from 'react' -import { - ButtonStyle, - ButtonContentStyle, - ButtonLoaderStyle, -} from './ButtonStyles' -import { ButtonProps } from './types' +import { ComponentPropsWithoutRef, ForwardedRef, forwardRef } from 'react' import { useRipple } from './useRipple' +import cn from 'classnames' +import styles from './Button.module.css' +import { Loader } from '../loaders' const loaderSizes = { xxs: 'small', @@ -15,44 +12,101 @@ const loaderSizes = { lg: 'medium', } as const -function Button(props: ButtonProps, ref?: ForwardedRef) { - const { - size = 'md', - variant = 'filled', - color = 'primary', - square = false, - fullwidth = false, - loading = false, - active = false, - onClick, - disabled, - children, - ...rest - } = props +export enum ButtonSize { + xxs, + xs, + sm, + md, + lg, +} +export type ButtonSizes = keyof typeof ButtonSize - const { handleClick, ripple } = useRipple(props) - const loaderSize = loaderSizes[size] +export enum ButtonVariant { + filled, + outlined, + text, + ghost, + translucent, +} +export type ButtonVariants = keyof typeof ButtonVariant - return ( - - {children} - {loading && } - {!active && ripple} - - ) +export enum ButtonColor { + primary, + secondary, + warning, + error, + success, } +export type ButtonColors = keyof typeof ButtonColor + +export type ButtonProps = ComponentPropsWithoutRef<'button'> & { + size?: ButtonSizes + variant?: ButtonVariants + color?: ButtonColors + fullwidth?: boolean + square?: boolean + loading?: boolean + active?: boolean + as?: never +} + +export const Button = forwardRef( + ( + { + size = 'md', + variant = 'filled', + color = 'primary', + square = false, + fullwidth = false, + loading = false, + active = false, + onClick, + disabled, + children, + className, + ...rest + }: ButtonProps, + ref?: ForwardedRef, + ) => { + const { handleClick, ripple } = useRipple({ onClick }) + const loaderSize = loaderSizes[size] -export default forwardRef(Button) + return ( + + ) + }, +) +Button.displayName = 'Button' diff --git a/packages/button/ButtonIcon.module.css b/packages/button/ButtonIcon.module.css new file mode 100644 index 00000000..b21d6a98 --- /dev/null +++ b/packages/button/ButtonIcon.module.css @@ -0,0 +1,29 @@ +.wrapper { + display: flex; + align-items: center; + justify-content: center; + min-height: 1em; + min-width: 1em; +} + +.icon { + line-height: 0; + flex-shrink: 0; + margin: -12px -6px; + + svg { + fill: currentcolor; + } + + *.square { + margin: -12px; + } +} + +.content { + margin-left: 10px; + + &:empty { + display: none; + } +} diff --git a/packages/button/ButtonIcon.tsx b/packages/button/ButtonIcon.tsx index a3f44b58..86d69400 100644 --- a/packages/button/ButtonIcon.tsx +++ b/packages/button/ButtonIcon.tsx @@ -1,27 +1,29 @@ -import React, { ForwardedRef, forwardRef } from 'react' -import { - ButtonWrapperStyle, - ButtonIconStyle, - ButtonContentStyle, -} from './ButtonIconStyles' -import { ButtonIconProps } from './types' -import Button from './Button' +import { ForwardedRef, forwardRef } from 'react' +import { Button, ButtonProps } from './Button' +import styles from './ButtonIcon.module.css' +import cn from 'classnames' -function ButtonIcon( - props: ButtonIconProps, - ref?: ForwardedRef -) { - const { icon, children, ...rest } = props - const hasNoChildren = !children - - return ( - - ) +export type ButtonIconProps = ButtonProps & { + icon: JSX.Element } -export default forwardRef(ButtonIcon) +export const ButtonIcon = forwardRef( + ( + { icon, children, ...rest }: ButtonIconProps, + ref?: ForwardedRef, + ) => { + const hasNoChildren = !children + + return ( + + ) + }, +) +ButtonIcon.displayName = 'ButtonIcon' diff --git a/packages/button/ButtonIconStyles.tsx b/packages/button/ButtonIconStyles.tsx deleted file mode 100644 index f5eb14e6..00000000 --- a/packages/button/ButtonIconStyles.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import styled from 'styled-components' - -export const ButtonWrapperStyle = styled.span` - display: flex; - align-items: center; - justify-content: center; - min-height: 1em; - min-width: 1em; -` - -export const ButtonIconStyle = styled.span<{ $square: boolean }>` - margin: ${({ $square }) => ($square ? '-12px' : '-12px -6px')}; - line-height: 0; - flex-shrink: 0; - - svg { - fill: currentColor; - } -` - -export const ButtonContentStyle = styled.span` - margin-left: 10px; - - :empty { - display: none; - } -` diff --git a/packages/button/ButtonStyles.tsx b/packages/button/ButtonStyles.tsx deleted file mode 100644 index 47140780..00000000 --- a/packages/button/ButtonStyles.tsx +++ /dev/null @@ -1,238 +0,0 @@ -import styled, { css, keyframes } from 'styled-components' -import { Theme } from '@lidofinance/theme' -import { Loader } from '@lidofinance/loaders' -import { ButtonColors, ButtonSizes, ButtonVariants } from './types' - -type InjectedProps = { - $color: ButtonColors - $size: ButtonSizes - $variant: ButtonVariants - $fullwidth: boolean - $square: boolean - $loading: boolean - $active: boolean - theme: Theme -} - -const sizes = { - xxs: css` - line-height: 1em; - font-size: ${({ theme }) => theme.fontSizesMap.xxxs}px; - border-radius: ${({ theme }) => theme.borderRadiusesMap.sm}px; - padding: ${({ $square }) => ($square ? '11px' : '11px 16px')}; - min-width: ${({ $square }) => ($square ? '0' : '50px')}; - `, - xs: css` - line-height: 1em; - font-size: ${({ theme }) => theme.fontSizesMap.xxs}px; - border-radius: ${({ theme }) => theme.borderRadiusesMap.sm}px; - padding: ${({ $square }) => ($square ? '10px' : '10px 16px')}; - min-width: ${({ $square }) => ($square ? '0' : '60px')}; - `, - sm: css` - line-height: 1em; - font-size: ${({ theme }) => theme.fontSizesMap.xs}px; - border-radius: ${({ theme }) => theme.borderRadiusesMap.sm}px; - padding: ${({ $square }) => ($square ? '16px' : '16px 24px')}; - min-width: ${({ $square }) => ($square ? '0' : '100px')}; - `, - md: css` - line-height: 1em; - font-size: ${({ theme }) => theme.fontSizesMap.xs}px; - border-radius: ${({ theme }) => theme.borderRadiusesMap.lg}px; - padding: ${({ $square }) => ($square ? '21px' : '21px 44px')}; - min-width: ${({ $square }) => ($square ? '0' : '120px')}; - `, - lg: css` - line-height: 1em; - font-size: ${({ theme }) => theme.fontSizesMap.sm}px; - border-radius: ${({ theme }) => theme.borderRadiusesMap.lg}px; - box-shadow: ${({ theme }) => theme.boxShadows.md} - var(--lido-color-shadowDark); - padding: ${({ $square }) => ($square ? '24px' : '24px 64px')}; - min-width: ${({ $square }) => ($square ? '0' : '160px')}; - `, -} - -const getMainColor = (props: InjectedProps) => { - const colorsMap = { - primary: `var(--lido-color-primary)`, - secondary: `var(--lido-color-secondary)`, - warning: `var(--lido-color-warning)`, - error: `var(--lido-color-error)`, - success: `var(--lido-color-success)`, - } - return colorsMap[props.$color] -} - -const getContrastColor = (props: InjectedProps) => { - const colorsMap = { - primary: `var(--lido-color-primaryContrast)`, - secondary: `var(--lido-color-secondaryContrast)`, - warning: `var(--lido-color-warningContrast)`, - error: `var(--lido-color-errorContrast)`, - success: `var(--lido-color-successContrast)`, - } - return colorsMap[props.$color] -} - -const getHoverColor = (props: InjectedProps) => { - const colorsMap = { - primary: `var(--lido-color-primaryHover)`, - secondary: `var(--lido-color-secondaryHover)`, - warning: `var(--lido-color-warningHover)`, - error: `var(--lido-color-errorHover)`, - success: `var(--lido-color-successHover)`, - } - return colorsMap[props.$color] -} - -const variants = { - filled: css` - color: ${getContrastColor}; - background-color: ${getMainColor}; - transition: background-color ${({ theme }) => theme.duration.fast} ease; - - :not(:disabled):hover, - :focus-visible { - background-color: ${getHoverColor}; - } - `, - outlined: css` - color: ${getMainColor}; - transition: background-color ${({ theme }) => theme.duration.fast} ease, - color ${({ theme }) => theme.duration.fast} ease; - - :before { - display: block; - border: 1px solid ${getMainColor}; - } - - :not(:disabled):hover, - :focus-visible { - background-color: ${getHoverColor}; - color: ${getContrastColor}; - } - `, - text: css` - color: ${getMainColor}; - background-color: var(--lido-color-foreground); - - :before { - display: block; - background-color: ${getMainColor}; - transition: opacity ${({ theme }) => theme.duration.fast} ease; - opacity: 0; - } - - :not(:disabled):hover, - :focus-visible { - :before { - opacity: 0.1; - } - } - `, - ghost: css` - color: ${getMainColor}; - - :before { - display: block; - background-color: ${getMainColor}; - transition: opacity ${({ theme }) => theme.duration.fast} ease; - opacity: 0; - } - - :not(:disabled):hover, - :focus-visible { - :before { - opacity: 0.1; - } - } - `, - translucent: css` - color: ${getMainColor}; - - :before { - display: block; - background-color: ${getMainColor}; - transition: opacity ${({ theme }) => theme.duration.fast} ease; - opacity: 0.1; - } - - :not(:disabled):hover, - :focus-visible { - :before { - opacity: 0.2; - } - } - `, -} - -export const ButtonStyle = styled.button` - box-sizing: border-box; - margin: 0; - border: none; - outline: none; - white-space: nowrap; - overflow: hidden; - position: relative; - background: transparent; - font-family: inherit; - font-weight: 700; - width: ${({ $fullwidth }) => ($fullwidth ? ' 100%' : 'auto')}; - - :before { - content: ''; - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - pointer-events: none; - border-radius: inherit; - display: none; - } - - :not(:disabled) { - cursor: pointer; - } - - :disabled { - opacity: ${({ $loading }) => ($loading ? 1 : 0.5)}; - } - - ${(props) => sizes[props.$size]} - ${(props) => variants[props.$variant]} -` - -export const ripple = keyframes` - to { - transform: scale(4); - opacity: 0; - } -` - -export const ButtonRippleStyle = styled.span` - position: absolute; - border-radius: 50%; - transform: scale(0); - pointer-events: none; - animation: ${ripple} 0.8s linear; - background-color: currentColor; - opacity: 0.4; -` - -export const ButtonContentStyle = styled.span<{ $hidden: boolean }>` - position: relative; - pointer-events: none; - visibility: ${({ $hidden }) => ($hidden ? 'hidden' : 'visible')}; -` - -export const ButtonLoaderStyle = styled(Loader)` - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - color: currentColor; - pointer-events: none; -` diff --git a/packages/button/__snapshots__/Button.test.tsx.snap b/packages/button/__snapshots__/Button.test.tsx.snap index 516f09d8..ade6ba68 100644 --- a/packages/button/__snapshots__/Button.test.tsx.snap +++ b/packages/button/__snapshots__/Button.test.tsx.snap @@ -1,66 +1,12 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`renders correctly 1`] = ` -.c0 { - box-sizing: border-box; - margin: 0; - border: none; - outline: none; - white-space: nowrap; - overflow: hidden; - position: relative; - background: transparent; - font-family: inherit; - font-weight: 700; - width: auto; - line-height: 1em; - font-size: 14px; - border-radius: 10px; - padding: 21px 44px; - min-width: 120px; - color: var(--lido-color-primaryContrast); - background-color: var(--lido-color-primary); - -webkit-transition: background-color 100ms ease; - transition: background-color 100ms ease; -} - -.c0:before { - content: ''; - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - pointer-events: none; - border-radius: inherit; - display: none; -} - -.c0:not(:disabled) { - cursor: pointer; -} - -.c0:disabled { - opacity: 0.5; -} - -.c0:not(:disabled):hover, -.c0:focus-visible { - background-color: var(--lido-color-primaryHover); -} - -.c1 { - position: relative; - pointer-events: none; - visibility: visible; -} - + ) + }, +) +Chip.displayName = 'Chip' diff --git a/packages/chip/ChipStyles.tsx b/packages/chip/ChipStyles.tsx deleted file mode 100644 index 217194e8..00000000 --- a/packages/chip/ChipStyles.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import styled, { css } from 'styled-components' -import { ChipVariants } from './types' - -type InjectedPropsTr = { - $variant: ChipVariants - $interactive: boolean -} - -const ChipVariantsStyle = { - positive: css` - background: var(--lido-color-success); - color: var(--lido-color-foreground); - `, - negative: css` - background: var(--lido-color-error); - color: var(--lido-color-foreground); - `, - gray: css` - background: var(--lido-color-shadowLight); - color: var(--lido-color-textSecondary); - `, - warning: css` - background: var(--lido-color-warning); - color: var(--lido-color-foreground); - `, -} - -export const ChipWrapperStyle = styled.div` - font-size: 14px; - line-height: 20px; - display: inline-flex; - max-width: 100%; - box-sizing: border-box; - align-items: center; - white-space: nowrap; - outline: none; - border-radius: ${({ theme }) => theme.borderRadiusesMap.xs}px; - padding: 0.2em 0.4em; - - cursor: ${({ $interactive }) => ($interactive ? 'pointer' : 'auto')}; - ${({ $variant }) => ChipVariantsStyle[$variant]} -` diff --git a/packages/chip/index.ts b/packages/chip/index.ts index 4e8e529b..1a33f6a1 100644 --- a/packages/chip/index.ts +++ b/packages/chip/index.ts @@ -1,2 +1 @@ -export { default as Chip } from './Chip' -export * from './types' +export * from './Chip' diff --git a/packages/chip/types.ts b/packages/chip/types.ts deleted file mode 100644 index 38395341..00000000 --- a/packages/chip/types.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { LidoComponentProps } from '@lidofinance/utils' -import React from 'react' -export type { Theme } from '@lidofinance/theme' - -export enum ChipVariant { - positive, - negative, - warning, - gray, -} - -export type ChipVariants = keyof typeof ChipVariant - -export type ChipProps = LidoComponentProps< - 'div', - { - wrapperRef?: React.RefObject - variant?: ChipVariants - } -> diff --git a/packages/container/Container.module.css b/packages/container/Container.module.css new file mode 100644 index 00000000..7a71aa1d --- /dev/null +++ b/packages/container/Container.module.css @@ -0,0 +1,23 @@ +.container { + box-sizing: border-box; + margin: 0 auto; + min-width: 320px; + width: 100%; + padding: 0 var(--lido-space-lg); + + @media (--lido-media-breakpoint-up-lg) { + padding: 0 var(--lido-space-xxl); + } +} + +.full { + max-width: 1424px; +} + +.content { + max-width: 960px; +} + +.tight { + max-width: 560px; +} diff --git a/packages/container/Container.stories.tsx b/packages/container/Container.stories.tsx index 1a3dbbc3..458d65c6 100644 --- a/packages/container/Container.stories.tsx +++ b/packages/container/Container.stories.tsx @@ -1,6 +1,5 @@ -import { Story, Meta } from '@storybook/react' -import { ContainerProps, ContainerSize } from './types' -import Container from './Container' +import { StoryFn, Meta } from '@storybook/react' +import { Container, ContainerProps, ContainerSize } from '.' import styled from 'styled-components' const getOptions = (enumObject: Record) => @@ -12,18 +11,18 @@ export default { parameters: { layout: 'fullscreen', }, -} as Meta +} satisfies Meta const StyledDiv = styled.div` height: 100px; background: var(--lido-color-foreground); - color: var(--lido-color-textSecondary); + color: var(--lido-color-text-secondary); display: flex; align-items: center; justify-content: center; ` -export const Base: Story = (props) => ( +export const Base: StoryFn = (props) => ( @@ -40,12 +39,12 @@ Base.argTypes = { }, } -export const PageLayout: Story = () => ( +export const PageLayout: StoryFn = () => ( <> - + Header - + ( Content - + Footer diff --git a/packages/container/Container.tsx b/packages/container/Container.tsx index 6a4922ff..bc59e12a 100644 --- a/packages/container/Container.tsx +++ b/packages/container/Container.tsx @@ -1,11 +1,36 @@ -import React, { ForwardedRef, forwardRef } from 'react' -import { ContainerStyle } from './ContainerStyles' -import { ContainerProps } from './types' +import { ComponentPropsWithoutRef, ForwardedRef, forwardRef } from 'react' +import styles from './Container.module.css' +import cn from 'classnames' -function Container(props: ContainerProps, ref?: ForwardedRef) { - const { size = 'full', ...rest } = props +export enum ContainerSize { + full, + content, + tight, +} +export type ContainerSizes = keyof typeof ContainerSize - return +export type ContainerProps = ComponentPropsWithoutRef<'div'> & { + size?: ContainerSizes } -export default forwardRef(Container) +export const Container = forwardRef( + ( + { size = 'full', className, children, ...rest }: ContainerProps, + ref?: ForwardedRef, + ) => { + return ( +
+ {children} +
+ ) + }, +) +Container.displayName = 'Container' diff --git a/packages/container/ContainerStyles.tsx b/packages/container/ContainerStyles.tsx deleted file mode 100644 index 936e6d17..00000000 --- a/packages/container/ContainerStyles.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import styled, { css } from 'styled-components' -import { ContainerSizes } from './types' - -const sizes = { - full: css` - max-width: 1424px; - `, - content: css` - max-width: 960px; - `, - tight: css` - max-width: 560px; - `, -} - -export const ContainerStyle = styled.div<{ $size: ContainerSizes }>` - box-sizing: border-box; - margin: 0 auto; - min-width: 320px; - width: 100%; - padding: 0 ${({ theme }) => theme.spaceMap.xxl}px; - - ${({ theme }) => theme.mediaQueries.lg} { - padding: 0 ${({ theme }) => theme.spaceMap.lg}px; - } - - ${({ $size }) => sizes[$size]} -` diff --git a/packages/container/index.ts b/packages/container/index.ts index af68d67e..fe8a50b1 100644 --- a/packages/container/index.ts +++ b/packages/container/index.ts @@ -1,2 +1 @@ -export { default as Container } from './Container' -export * from './types' +export * from './Container' diff --git a/packages/container/types.tsx b/packages/container/types.tsx deleted file mode 100644 index 6c6a7322..00000000 --- a/packages/container/types.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import { LidoComponentProps } from '@lidofinance/utils' -export type { Theme } from '@lidofinance/theme' - -export enum ContainerSize { - full, - content, - tight, -} -export type ContainerSizes = keyof typeof ContainerSize - -export type ContainerProps = LidoComponentProps< - 'div', - { - size?: ContainerSizes - } -> diff --git a/packages/content-theme/content-theme.tsx b/packages/content-theme/content-theme.tsx deleted file mode 100644 index 57471326..00000000 --- a/packages/content-theme/content-theme.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import React, { FC } from 'react' - -import { ContentThemeOnlyDark, ContentThemeOnlyLight } from './styles' - -import { ContentThemeProps } from './types' - -export const ContentTheme: FC = ( - props: ContentThemeProps -) => { - return ( - <> - {props.darkContent} - {props.lightContent} - - ) -} diff --git a/packages/content-theme/styles.tsx b/packages/content-theme/styles.tsx deleted file mode 100644 index f15defcc..00000000 --- a/packages/content-theme/styles.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import styled from 'styled-components' - -export const ContentThemeOnlyDark = styled.div` - display: var(--lido-color-darkDisplay, contents); -` - -export const ContentThemeOnlyLight = styled.div` - display: var(--lido-color-lightDisplay, contents); -` diff --git a/packages/content-theme/types.ts b/packages/content-theme/types.ts deleted file mode 100644 index 29e5379c..00000000 --- a/packages/content-theme/types.ts +++ /dev/null @@ -1,10 +0,0 @@ -import React from 'react' -import { LidoComponentProps } from '@lidofinance/utils' - -export type ContentThemeProps = LidoComponentProps< - 'div', - { - darkContent: React.ReactElement - lightContent: React.ReactElement - } -> diff --git a/packages/cookie-theme-toggler/cookie-theme-toggler.tsx b/packages/cookie-theme-toggler/cookie-theme-toggler.tsx deleted file mode 100644 index 89aa9834..00000000 --- a/packages/cookie-theme-toggler/cookie-theme-toggler.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import React, { FC } from 'react' -import { - CookieThemeTogglerStyle, - CookieThemeTogglerDarkIcon, - CookieThemeTogglerLightIcon, -} from './styles' -import { useThemeToggle } from '@lidofinance/theme' - -export const ThemeToggler: FC = () => { - const { toggleTheme } = useThemeToggle() - - return ( - - - - - ) -} diff --git a/packages/cookie-theme-toggler/index.ts b/packages/cookie-theme-toggler/index.ts deleted file mode 100644 index 74bb9deb..00000000 --- a/packages/cookie-theme-toggler/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './cookie-theme-toggler' diff --git a/packages/cookie-theme-toggler/styles.tsx b/packages/cookie-theme-toggler/styles.tsx deleted file mode 100644 index d60772b8..00000000 --- a/packages/cookie-theme-toggler/styles.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import styled from 'styled-components' -import { Button } from '@lidofinance/button' -import { Dark, Light } from '@lidofinance/icons' - -export const CookieThemeTogglerStyle = styled(Button).attrs({ - variant: 'text', - size: 'xs', -})` - display: inline-grid; - grid-template-areas: 'a'; - min-width: 0; - margin-left: ${({ theme }) => theme.spaceMap.sm}px; - line-height: 0; - font-size: 0; - padding: 0; - - width: 44px; - height: 44px; - // button element contains span as children container, we want to bypass it. - // by aiming explicit "span:first-child" we're verifying that this is what we are aiming for - // witout breaking our SVGs - & > span:first-child { - display: contents; - } -` - -export const CookieThemeTogglerLightIcon = styled(Light)` - grid-area: a; - align-self: center; - justify-self: center; - visibility: var(--lido-color-darkModeVisibility); -` - -export const CookieThemeTogglerDarkIcon = styled(Dark)` - grid-area: a; - align-self: center; - justify-self: center; - visibility: var(--lido-color-lightModeVisibility); -` diff --git a/packages/cookies-tooltip/cookies-tooltip.module.css b/packages/cookies-tooltip/cookies-tooltip.module.css new file mode 100644 index 00000000..667c82e4 --- /dev/null +++ b/packages/cookies-tooltip/cookies-tooltip.module.css @@ -0,0 +1,125 @@ +.link { + color: inherit; + text-decoration: underline !important; +} + +.wrap { + z-index: 999; + position: fixed; + display: flex; + align-items: center; + justify-content: center; + bottom: 0; + left: 0; + right: 0; + + @media (--lido-media-breakpoint-up-lg) { + bottom: 20px; + left: 20px; + right: 20px; + } +} + +.box { + display: flex; + flex-direction: column; + align-items: center; + padding: 16px 24px; + background-color: var(--lido-color-foreground); + box-shadow: 0 6px 32px rgb(0 0 0 / 8%); + border-radius: 0; + width: 100%; + + @media (--lido-media-breakpoint-up-lg) { + flex-direction: row; + border-radius: 20px; + width: auto; + } +} + +.iconWrap { + display: none; + + @media (--lido-media-breakpoint-up-lg) { + display: flex; + align-items: center; + justify-content: center; + margin-right: 24px; + width: 32px; + height: 32px; + border-radius: 8px; + } +} + +.text { + margin-right: 0; + margin-bottom: 8px; + font-weight: 500; + font-size: 14px; + line-height: 22px; + color: var(--lido-color-text-secondary); + width: 100%; + text-align: center; + + @media (--lido-media-breakpoint-up-lg) { + text-align: initial; + width: 340px; + margin-right: 8px; + margin-bottom: 0; + } +} + +.buttonWrap { + display: flex; +} + +.button { + font-weight: 800; + font-size: 12px; + line-height: 20px; + border-radius: 6px; + width: 112px; + height: 32px; + border: none; + outline: none; + cursor: pointer; + transition: + background-color ease 0.25s, + border-color ease 0.25s, + color ease 0.25s; + + &:not(:last-child) { + margin-right: 8px; + } + + &:hover { + background-color: #0e1621; + } + + &:active { + transform: translateY(1px); + } + + @media (--lido-media-breakpoint-up-lg) { + width: 72px; + } +} + +.buttonAllow { + background-color: var(--lido-color-text); + color: var(--lido-color-foreground); + + &:hover { + background-color: var(--lido-color-text-secondary); + } +} + +.buttonDecline { + background-color: var(--lido-color-foreground); + color: var(--lido-color-text); + border: 1px solid var(--lido-color-text); + + &:hover { + background-color: var(--lido-color-background); + } +} diff --git a/packages/cookies-tooltip/cookies-tooltip.stories.tsx b/packages/cookies-tooltip/cookies-tooltip.stories.tsx index 6ab4d2e8..3e25c99b 100644 --- a/packages/cookies-tooltip/cookies-tooltip.stories.tsx +++ b/packages/cookies-tooltip/cookies-tooltip.stories.tsx @@ -1,13 +1,12 @@ -import { Story, Meta } from '@storybook/react' - -import { Text } from '@lidofinance/text' -import { CookiesTooltip } from './cookies-tooltip' +import { StoryFn, Meta } from '@storybook/react' +import { Text } from '../text' +import { CookiesTooltip } from '.' export default { title: 'CookiesTooltip/Basic', -} as Meta +} satisfies Meta -export const Basic: Story = () => ( +export const Basic: StoryFn = () => ( <> CookiesTooltip component has an ability to detect a user choice in other diff --git a/packages/cookies-tooltip/cookies-tooltip.tsx b/packages/cookies-tooltip/cookies-tooltip.tsx index ab42fc45..dd9c3b66 100644 --- a/packages/cookies-tooltip/cookies-tooltip.tsx +++ b/packages/cookies-tooltip/cookies-tooltip.tsx @@ -1,22 +1,20 @@ -import React, { FC, useEffect, useState, useCallback } from 'react' -import { Cookie, CookieInverse } from '@lidofinance/icons' -import { ContentTheme } from '@lidofinance/content-theme' -import { getCrossDomainCookieClientSide } from '@lidofinance/utils' - import { - Wrap, - Box, - CookieIconWrap, - Text, - ButtonsWrap, - AllowButton, - DeclineButton, - Link, -} from './styles' + useEffect, + useState, + useCallback, + ComponentPropsWithoutRef, +} from 'react' +import { Cookie, CookieInverse } from '../icons' +import { ContentTheme } from '../theme/content-theme' +import { getCrossDomainCookieClientSide } from '../utils' import { allowCookies, declineCookies } from './utils' import { COOKIE_ALLOWED_KEY } from './constants' +import styles from './cookies-tooltip.module.css' +import cn from 'classnames' + +export type CookiesTooltipProps = ComponentPropsWithoutRef<'div'> -export const CookiesTooltip: FC = () => { +export const CookiesTooltip = ({ className, ...rest }: CookiesTooltipProps) => { const [isVisible, setVisibility] = useState(false) const checkCookieAllowedEarlier = useCallback(() => { @@ -43,41 +41,48 @@ export const CookiesTooltip: FC = () => { if (!isVisible) return <> return ( - - - +
+
+
} lightContent={} /> - - +
+
We use cookies to collect anonymous site visitation data to improve performance of our website. For more info, read our  - Privacy Notice - - - + Privacy Notice + +
+
+ +
+
+
) } - -export default CookiesTooltip diff --git a/packages/cookies-tooltip/index.ts b/packages/cookies-tooltip/index.ts index 3ef5391f..a16a5d6a 100644 --- a/packages/cookies-tooltip/index.ts +++ b/packages/cookies-tooltip/index.ts @@ -1,2 +1,2 @@ -export { default as CookiesTooltip } from './cookies-tooltip' +export * from './cookies-tooltip' export { migrationAllowCookieToCrossDomainCookieClientSide } from './utils' diff --git a/packages/cookies-tooltip/styles.tsx b/packages/cookies-tooltip/styles.tsx deleted file mode 100644 index 91e387ca..00000000 --- a/packages/cookies-tooltip/styles.tsx +++ /dev/null @@ -1,128 +0,0 @@ -import styled from 'styled-components' - -const ExternalLink = styled.a.attrs({ - target: '_blank', - rel: 'nofollow noopener', -})` - cursor: pointer; -` - -export const Wrap = styled.div` - z-index: 999; - position: fixed; - bottom: 20px; - left: 20px; - right: 20px; - display: flex; - align-items: center; - justify-content: center; - - ${({ theme }) => theme.mediaQueries.lg} { - bottom: 0; - left: 0; - right: 0; - } -` - -export const Box = styled.div` - display: flex; - align-items: center; - padding: 16px 24px; - border-radius: 20px; - background-color: var(--lido-color-foreground); - box-shadow: 0 6px 32px rgba(0, 0, 0, 0.08); - - ${({ theme }) => theme.mediaQueries.lg} { - flex-direction: column; - border-radius: 0; - width: 100%; - } -` - -export const CookieIconWrap = styled.div` - margin-right: 24px; - display: flex; - align-items: center; - justify-content: center; - width: 32px; - height: 32px; - border-radius: 8px; - - ${({ theme }) => theme.mediaQueries.lg} { - display: none; - } -` - -export const Text = styled.div` - margin-right: 8px; - font-weight: 500; - font-size: 14px; - line-height: 22px; - color: var(--lido-color-textSecondary); - width: 340px; - - ${({ theme }) => theme.mediaQueries.lg} { - margin-right: 0; - margin-bottom: 8px; - width: 100%; - text-align: center; - } -` - -export const ButtonsWrap = styled.div` - display: flex; -` - -const ButtonBasic = styled.button.attrs({ type: 'button' })` - font-weight: 800; - font-size: 12px; - line-height: 20px; - border-radius: 6px; - width: 72px; - height: 32px; - border: none; - outline: none; - cursor: pointer; - transition: background-color ease 0.25s, border-color ease 0.25s, - color ease 0.25s; - - &:not(:last-child) { - margin-right: 8px; - } - - &:hover { - background-color: #0e1621; - } - - &:active { - transform: translateY(1px); - } - - ${({ theme }) => theme.mediaQueries.lg} { - width: 112px; - } -` - -export const AllowButton = styled(ButtonBasic)` - background-color: var(--lido-color-text); - color: var(--lido-color-foreground); - - &:hover { - background-color: var(--lido-color-textSecondary); - } -` - -export const DeclineButton = styled(ButtonBasic)` - background-color: var(--lido-color-foreground); - color: var(--lido-color-text); - border: 1px solid var(--lido-color-text); - - &:hover { - background-color: var(--lido-color-background); - } -` - -export const Link = styled(ExternalLink)` - color: inherit; - text-decoration: underline !important; -` diff --git a/packages/cookies-tooltip/utils.ts b/packages/cookies-tooltip/utils.ts index 7994aa79..f831e89f 100644 --- a/packages/cookies-tooltip/utils.ts +++ b/packages/cookies-tooltip/utils.ts @@ -2,7 +2,7 @@ import { setCrossDomainCookieClientSide, getDomainCookieClientSide, removeCookiesClientSide, -} from '@lidofinance/utils' +} from '../utils' import { COOKIE_ALLOWED_KEY, COOKIE_VALUE_NO, @@ -18,7 +18,7 @@ export const declineCookies = (): void => { } export const migrationAllowCookieToCrossDomainCookieClientSide = ( - keyOldCookie: string + keyOldCookie: string, ) => { const old_cookie_value = getDomainCookieClientSide(keyOldCookie) diff --git a/packages/data-table/DataTable.module.css b/packages/data-table/DataTable.module.css new file mode 100644 index 00000000..0b9a3339 --- /dev/null +++ b/packages/data-table/DataTable.module.css @@ -0,0 +1,36 @@ +.row { + display: flex; + margin: var(--lido-space-md); + font-weight: 400; + font-size: var(--lido-font-size-xxs); + line-height: 1.6em; + + &:first-child { + margin-top: 0; + } + + &:last-child { + margin-bottom: 0; + } +} + +.title { + color: var(--lido-color-text-secondary); + flex-grow: 1; +} + +.value { + color: var(--lido-color-text); + text-align: right; + margin-left: var(--lido-space-xxl); + flex-grow: 1; + + &.highlight { + color: var(--lido-color-success); + } +} + +.question { + margin: -6px 0 -5px; + vertical-align: middle; +} diff --git a/packages/data-table/DataTable.stories.tsx b/packages/data-table/DataTable.stories.tsx index 4f6fcbe7..2b2f284b 100644 --- a/packages/data-table/DataTable.stories.tsx +++ b/packages/data-table/DataTable.stories.tsx @@ -1,6 +1,5 @@ -import { Story, Meta } from '@storybook/react' -import { DataTableProps, DataTableRowProps } from './types' -import DataTable, { DataTableRow } from './DataTable' +import { StoryFn, Meta } from '@storybook/react' +import { DataTable, DataTableRow, DataTableProps, DataTableRowProps } from '.' export default { component: DataTable, @@ -8,31 +7,30 @@ export default { parameters: { layout: 'centered', }, -} as Meta +} satisfies Meta -export const Base: Story> = - (props) => { - const { loading, ...rest } = props - - return ( -
- - - 100 LDO - - - $0.12 - - -
- ) - } +export const Base: StoryFn< + DataTableProps & Pick +> = ({ loading, ...rest }) => { + return ( +
+ + + 100 LDO + + + $0.12 + + +
+ ) +} Base.args = { loading: false, } -export const WithHint: Story = (props) => { +export const WithHint: StoryFn = (props) => { return (
@@ -48,7 +46,7 @@ export const WithHint: Story = (props) => { ) } -export const WithHighlighted: Story = (props) => { +export const WithHighlighted: StoryFn = (props) => { return (
diff --git a/packages/data-table/DataTable.tsx b/packages/data-table/DataTable.tsx index f8d48c93..e8a89a19 100644 --- a/packages/data-table/DataTable.tsx +++ b/packages/data-table/DataTable.tsx @@ -1,48 +1,61 @@ -import React, { ForwardedRef, forwardRef } from 'react' -import { InlineLoader } from '@lidofinance/loaders' -import { Tooltip } from '@lidofinance/tooltip' import { - DataTableStyle, - DataTableRowStyle, - DataTableTitleStyle, - DataTableValueStyle, - DataTableQuestionStyle, -} from './DataTableStyles' -import { DataTableProps, DataTableRowProps } from './types' + ComponentPropsWithoutRef, + ForwardedRef, + ReactNode, + forwardRef, +} from 'react' +import { InlineLoader } from '../loaders' +import { Tooltip } from '../tooltip' +import styles from './DataTable.module.css' +import cn from 'classnames' +import { Question } from '../icons' -function DataTable(props: DataTableProps, ref?: ForwardedRef) { - return -} +export type DataTableProps = ComponentPropsWithoutRef<'div'> + +export const DataTable = forwardRef( + (props: DataTableProps, ref?: ForwardedRef) => { + return
+ }, +) +DataTable.displayName = 'DataTable' -export default forwardRef(DataTable) +export type DataTableRowProps = ComponentPropsWithoutRef<'div'> & { + title: ReactNode + help?: ReactNode + loading?: boolean + highlight?: boolean +} -export const DataTableRow = forwardRef(function DataTableRow( - props: DataTableRowProps, - ref?: ForwardedRef -) { - const { - title, - loading = false, - highlight = false, - help, - children, - ...rest - } = props - const hasHelper = !!help +export const DataTableRow = forwardRef( + ( + { + title, + loading = false, + highlight = false, + help, + className, + children, + ...rest + }: DataTableRowProps, + ref?: ForwardedRef, + ) => { + const hasHelper = !!help - return ( - - - {title} - {hasHelper && ( - - - - )} - - - {loading ? : children} - - - ) -}) + return ( +
+
+ {title} + {hasHelper && ( + + + + )} +
+
+ {loading ? : children} +
+
+ ) + }, +) +DataTableRow.displayName = 'DataTableRow' diff --git a/packages/data-table/DataTableStyles.tsx b/packages/data-table/DataTableStyles.tsx deleted file mode 100644 index b3fe93e9..00000000 --- a/packages/data-table/DataTableStyles.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { Question } from '@lidofinance/icons' -import styled from 'styled-components' - -export const DataTableStyle = styled.div`` - -export const DataTableRowStyle = styled.div` - display: flex; - margin: ${({ theme }) => theme.spaceMap.md}px 0; - font-weight: 400; - font-size: ${({ theme }) => theme.fontSizesMap.xxs}px; - line-height: 1.6em; - - &:first-child { - margin-top: 0; - } - - &:last-child { - margin-bottom: 0; - } -` - -export const DataTableTitleStyle = styled.div` - color: var(--lido-color-textSecondary); - flex-grow: 1; -` - -export const DataTableValueStyle = styled.div<{ $highlight: boolean }>` - color: var( - --lido-color-${({ $highlight }) => ($highlight ? `success` : `text`)} - ); - text-align: right; - margin-left: ${({ theme }) => theme.spaceMap.xxl}px; - flex-grow: 1; -` - -export const DataTableQuestionStyle = styled(Question)` - margin: -6px 0 -5px 0; - vertical-align: middle; -` diff --git a/packages/data-table/index.ts b/packages/data-table/index.ts index 32cd8fe8..3f3f9752 100644 --- a/packages/data-table/index.ts +++ b/packages/data-table/index.ts @@ -1,3 +1 @@ export * from './DataTable' -export { default as DataTable } from './DataTable' -export * from './types' diff --git a/packages/data-table/types.tsx b/packages/data-table/types.tsx deleted file mode 100644 index 62b4b1ec..00000000 --- a/packages/data-table/types.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import { LidoComponentProps } from '@lidofinance/utils' -import React from 'react' -export type { Theme } from '@lidofinance/theme' - -export type DataTableProps = LidoComponentProps<'div'> - -export type DataTableRowProps = LidoComponentProps< - 'div', - { - title: React.ReactNode - help?: React.ReactNode - loading?: boolean - highlight?: boolean - } -> diff --git a/packages/divider/Divider.module.css b/packages/divider/Divider.module.css new file mode 100644 index 00000000..319249d7 --- /dev/null +++ b/packages/divider/Divider.module.css @@ -0,0 +1,61 @@ +.divider { + box-sizing: border-box; + list-style: none; + opacity: 0.1; + margin: 0; + padding: 0; + flex-shrink: 0; + flex-grow: 0; +} + +.horizontal { + border-top: 1px solid currentcolor; + width: 100%; + height: 0; + + &.xs { + margin: var(--lido-space-xs) 0; + } + + &.sm { + margin: var(--lido-space-sm) 0; + } + + &.md { + margin: var(--lido-space-md) 0; + } + + &.lg { + margin: var(--lido-space-lg) 0; + } + + &.xl { + margin: var(--lido-space-xl) 0; + } +} + +.vertical { + border-left: 1px solid currentcolor; + align-self: stretch; + width: 0; + + &.xs { + margin: 0 var(--lido-space-xs); + } + + &.sm { + margin: 0 var(--lido-space-sm); + } + + &.md { + margin: 0 var(--lido-space-md); + } + + &.lg { + margin: 0 var(--lido-space-lg); + } + + &.xl { + margin: 0 var(--lido-space-xl); + } +} diff --git a/packages/divider/Divider.stories.tsx b/packages/divider/Divider.stories.tsx index e6e38315..fb206e9e 100644 --- a/packages/divider/Divider.stories.tsx +++ b/packages/divider/Divider.stories.tsx @@ -1,6 +1,11 @@ -import { Story, Meta } from '@storybook/react' -import { DividerIndent, DividerProps, DividerType, DividerTypes } from './types' -import Divider from './Divider' +import { StoryFn, Meta } from '@storybook/react' +import { + Divider, + DividerIndent, + DividerProps, + DividerType, + DividerTypes, +} from '.' import styled from 'styled-components' const getOptions = (enumObject: Record) => @@ -23,14 +28,14 @@ export default { control: 'inline-radio', }, }, -} as Meta +} satisfies Meta const Wrapper = styled.div<{ $type?: DividerTypes }>` flex-direction: ${({ $type }) => ($type === 'vertical' ? 'row' : 'column')}; display: flex; ` -export const Base: Story = (props) => ( +export const Base: StoryFn = (props) => ( First diff --git a/packages/divider/Divider.tsx b/packages/divider/Divider.tsx index 8ee5c864..a4d775b4 100644 --- a/packages/divider/Divider.tsx +++ b/packages/divider/Divider.tsx @@ -1,11 +1,54 @@ -import React, { ForwardedRef, forwardRef } from 'react' -import { DividerStyle } from './DividerStyles' -import { DividerProps } from './types' +import { ComponentPropsWithoutRef, ForwardedRef, forwardRef } from 'react' +import cn from 'classnames' +import styles from './Divider.module.css' -function Divider(props: DividerProps, ref?: ForwardedRef) { - const { type = 'horizontal', indents, children, ...rest } = props +export enum DividerType { + vertical, + horizontal, +} +export type DividerTypes = keyof typeof DividerType + +export enum DividerIndent { + xs, + sm, + md, + lg, + xl, +} +export type DividerIndents = keyof typeof DividerIndent - return +export type DividerProps = ComponentPropsWithoutRef<'div'> & { + type?: DividerTypes + indents?: DividerIndents + children?: never } -export default forwardRef(Divider) +export const Divider = forwardRef( + ( + { + type = 'horizontal', + indents, + className, + children, + ...rest + }: DividerProps, + ref?: ForwardedRef, + ) => { + return ( +
+ ) + }, +) +Divider.displayName = 'Divider' diff --git a/packages/divider/DividerStyles.tsx b/packages/divider/DividerStyles.tsx deleted file mode 100644 index edfdafef..00000000 --- a/packages/divider/DividerStyles.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import styled, { css } from 'styled-components' -import { Theme } from '@lidofinance/theme' -import { DividerIndents, DividerTypes } from './types' - -type InjectedProps = { - $type: DividerTypes - $indents?: DividerIndents - theme: Theme -} - -const getIndents = ({ $indents, theme }: InjectedProps) => { - return $indents ? `${theme.spaceMap[$indents]}px` : '0' -} - -const types = { - horizontal: css` - border-top: 1px solid currentColor; - width: 100%; - height: 0; - margin: ${getIndents} 0; - `, - vertical: css` - border-left: 1px solid currentColor; - align-self: stretch; - width: 0; - margin: 0 ${getIndents}; - `, -} - -export const DividerStyle = styled.div` - box-sizing: border-box; - list-style: none; - opacity: 0.1; - margin: 0; - padding: 0; - flex-shrink: 0; - flex-grow: 0; - - ${(props) => types[props.$type]} -` diff --git a/packages/divider/index.ts b/packages/divider/index.ts index febfe614..1b872641 100644 --- a/packages/divider/index.ts +++ b/packages/divider/index.ts @@ -1,2 +1 @@ -export { default as Divider } from './Divider' -export * from './types' +export * from './Divider' diff --git a/packages/divider/types.tsx b/packages/divider/types.tsx deleted file mode 100644 index c398479f..00000000 --- a/packages/divider/types.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { LidoComponentProps } from '@lidofinance/utils' -export type { Theme } from '@lidofinance/theme' - -export enum DividerType { - vertical, - horizontal, -} -export type DividerTypes = keyof typeof DividerType - -export enum DividerIndent { - xs, - sm, - md, - lg, - xl, -} -export type DividerIndents = keyof typeof DividerIndent - -export type DividerProps = LidoComponentProps< - 'div', - { - type?: DividerTypes - indents?: DividerIndents - children?: never - } -> diff --git a/packages/heading/Heading.module.css b/packages/heading/Heading.module.css new file mode 100644 index 00000000..c65d35fc --- /dev/null +++ b/packages/heading/Heading.module.css @@ -0,0 +1,28 @@ +.sm { + font-size: var(--lido-font-size-xl); + line-height: 1.3em; +} + +.md { + font-size: var(--lido-font-size-xxl); + line-height: 1.3em; +} + +.lg { + font-size: var(--lido-font-size-xxxl); + line-height: 1.3em; +} + +.heading { + margin: 0; + padding: 0; + font-weight: 800; +} + +.heading.text { + color: var(--lido-color-text); +} + +.heading.secondary { + color: var(--lido-color-text-secondary); +} diff --git a/packages/heading/Heading.stories.tsx b/packages/heading/Heading.stories.tsx index feb344ca..94801547 100644 --- a/packages/heading/Heading.stories.tsx +++ b/packages/heading/Heading.stories.tsx @@ -1,6 +1,16 @@ -import { Story, Meta } from '@storybook/react' -import Heading, { H1, H2, H3 } from './Heading' -import { HeadingProps, HeadingColor, HeadingSize, HProps } from './types' +import { StoryFn, Meta } from '@storybook/react' +import { + Heading, + HeadingProps, + H1, + H1Props, + H2, + H2Props, + H3, + H3Props, + HeadingColor, + HeadingSize, +} from '.' const getOptions = (enumObject: Record) => Object.values(enumObject).filter((value) => typeof value === 'string') @@ -21,9 +31,9 @@ export default { control: 'inline-radio', }, }, -} as Meta +} satisfies Meta -export const Basic: Story = (props) => +export const Basic: StoryFn = (props) => Basic.args = { size: 'md', @@ -36,6 +46,6 @@ Basic.argTypes = { }, } -export const HeadingH1: Story> = (props) =>

-export const HeadingH2: Story> = (props) =>

-export const HeadingH3: Story> = (props) =>

+export const HeadingH1: StoryFn = (props) =>

+export const HeadingH2: StoryFn = (props) =>

+export const HeadingH3: StoryFn = (props) =>

diff --git a/packages/heading/Heading.tsx b/packages/heading/Heading.tsx index 31e6979f..75997421 100644 --- a/packages/heading/Heading.tsx +++ b/packages/heading/Heading.tsx @@ -1,34 +1,112 @@ -import React, { ForwardedRef, forwardRef } from 'react' -import { HeadingStyle, H1Style, H2Style, H3Style } from './HeadingStyles' -import { HeadingProps, HProps } from './types' +import { ComponentPropsWithoutRef, ForwardedRef, forwardRef } from 'react' +import cn from 'classnames' +import styles from './Heading.module.css' -function Heading(props: HeadingProps, ref?: ForwardedRef) { - const { size = 'md', color = 'text', ...rest } = props - return +export enum HeadingColor { + text, + secondary, } +export type HeadingColors = keyof typeof HeadingColor -export default forwardRef(Heading) +export enum HeadingSize { + sm, + md, + lg, +} +export type HeadingSizes = keyof typeof HeadingSize + +export type HeadingProps = ComponentPropsWithoutRef<'div'> & { + color?: HeadingColors + size?: HeadingSizes +} + +export const Heading = forwardRef( + ( + { size = 'md', color = 'text', ...rest }: HeadingProps, + ref?: ForwardedRef, + ) => { + return ( +
+ ) + }, +) +Heading.displayName = 'Heading' + +export type H1Props = ComponentPropsWithoutRef<'h1'> & { + color?: HeadingColors +} export const H1 = forwardRef(function H1( - props: HProps<'h1'>, - ref?: ForwardedRef + { color = 'text', children, ...rest }: H1Props, + ref?: ForwardedRef, ) { - const { color = 'text', ...rest } = props - return + return ( +

+ {children} +

+ ) }) +H1.displayName = 'H1' + +export type H2Props = ComponentPropsWithoutRef<'h2'> & { + color?: HeadingColors +} export const H2 = forwardRef(function H2( - props: HProps<'h2'>, - ref?: ForwardedRef + { color = 'text', children, ...rest }: H1Props, + ref?: ForwardedRef, ) { - const { color = 'text', ...rest } = props - return + return ( +

+ {children} +

+ ) }) +H2.displayName = 'H2' + +export type H3Props = ComponentPropsWithoutRef<'h3'> & { + color?: HeadingColors +} export const H3 = forwardRef(function H3( - props: HProps<'h3'>, - ref?: ForwardedRef + { color = 'text', children, ...rest }: H1Props, + ref?: ForwardedRef, ) { - const { color = 'text', ...rest } = props - return + return ( +

+ {children} +

+ ) }) +H3.displayName = 'H3' diff --git a/packages/heading/HeadingStyles.tsx b/packages/heading/HeadingStyles.tsx deleted file mode 100644 index 786d21c6..00000000 --- a/packages/heading/HeadingStyles.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import styled, { css } from 'styled-components' -import { Theme } from '@lidofinance/theme' -import { HeadingColors, HeadingSizes } from './types' - -export const sizes = { - sm: css` - font-size: ${({ theme }) => theme.fontSizesMap.xl}px; - line-height: 1.3em; - `, - md: css` - font-size: ${({ theme }) => theme.fontSizesMap.xxl}px; - line-height: 1.3em; - `, - lg: css` - font-size: ${({ theme }) => theme.fontSizesMap.xxxl}px; - line-height: 1.3em; - `, -} - -type InjectedProps = { - $color: HeadingColors - theme: Theme -} - -const getHeadingColor = (props: InjectedProps) => { - const colorsMap = { - text: `var(--lido-color-text)`, - secondary: `var(--lido-color-textSecondary)`, - } - return colorsMap[props.$color] -} - -const commonCSS = css` - margin: 0; - padding: 0; - font-weight: 800; - color: ${getHeadingColor}; -` - -export const HeadingStyle = styled.div` - ${commonCSS} - ${(props) => sizes[props.$size]} -` - -export const H1Style = styled.h1` - ${commonCSS} - ${sizes.lg} -` - -export const H2Style = styled.h2` - ${commonCSS} - ${sizes.md} -` - -export const H3Style = styled.h3` - ${commonCSS} - ${sizes.sm} -` diff --git a/packages/heading/index.tsx b/packages/heading/index.tsx index b3117d49..dd0dbbaa 100644 --- a/packages/heading/index.tsx +++ b/packages/heading/index.tsx @@ -1,3 +1 @@ export * from './Heading' -export { default as Heading } from './Heading' -export * from './types' diff --git a/packages/heading/types.ts b/packages/heading/types.ts deleted file mode 100644 index 36bfd2d9..00000000 --- a/packages/heading/types.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { LidoComponentProps } from '@lidofinance/utils' - -export enum HeadingColor { - text, - secondary, -} -export type HeadingColors = keyof typeof HeadingColor - -export enum HeadingSize { - sm, - md, - lg, -} -export type HeadingSizes = keyof typeof HeadingSize - -export type HeadingProps = LidoComponentProps< - 'div', - { - color?: HeadingColors - size?: HeadingSizes - } -> - -export type HProps = - LidoComponentProps< - T, - { - color?: HeadingColors - } - > diff --git a/packages/hooks/useBreakpoint.ts b/packages/hooks/useBreakpoint.ts index bfd6b16e..0f507b19 100644 --- a/packages/hooks/useBreakpoint.ts +++ b/packages/hooks/useBreakpoint.ts @@ -1,9 +1,9 @@ import { useEffect, useState } from 'react' -import { Theme } from '@lidofinance/theme' +import { Theme } from '../theme' import { useTheme } from 'styled-components' export const useBreakpoint = ( - breakpoint: keyof Theme['breakpointsMap'] + breakpoint: keyof Theme['breakpointsMap'], ): boolean => { const theme = useTheme() const maxWidth = theme.breakpointsMap[breakpoint].width diff --git a/packages/hooks/useEscape.ts b/packages/hooks/useEscape.ts index c87b7d51..902eec4a 100644 --- a/packages/hooks/useEscape.ts +++ b/packages/hooks/useEscape.ts @@ -7,7 +7,7 @@ export const useEscape = (callback?: () => void): void => { callback?.() } }, - [callback] + [callback], ) useEffect(() => { diff --git a/packages/hooks/useInterceptFocus.ts b/packages/hooks/useInterceptFocus.ts index 2db9a22d..c1981073 100644 --- a/packages/hooks/useInterceptFocus.ts +++ b/packages/hooks/useInterceptFocus.ts @@ -2,7 +2,7 @@ import { useCallback, useRef } from 'react' export const useInterceptFocus = (): [ (node: HTMLElement) => void, - () => void + () => void, ] => { const savedElement = useRef(null) diff --git a/packages/hooks/useLockScroll.ts b/packages/hooks/useLockScroll.ts index 82547abd..a1ea68b6 100644 --- a/packages/hooks/useLockScroll.ts +++ b/packages/hooks/useLockScroll.ts @@ -33,7 +33,7 @@ const getPaddingRight = (element: HTMLElement): number => { const setStyleProperty = ( element: HTMLElement, property: string, - value?: string + value?: string, ): void => { if (value) { element.style.setProperty(property, value) diff --git a/packages/hooks/useMergeRefs.ts b/packages/hooks/useMergeRefs.ts index 73e3cf8a..5cfa62fc 100644 --- a/packages/hooks/useMergeRefs.ts +++ b/packages/hooks/useMergeRefs.ts @@ -1,10 +1,10 @@ import { useMergeRefs as useCallbackMergeRefs } from 'use-callback-ref' -import React from 'react' +import { ForwardedRef, MutableRefObject } from 'react' export const useMergeRefs = ( - refs: (React.ForwardedRef | undefined)[] -): React.MutableRefObject => { + refs: (ForwardedRef | undefined)[], +): MutableRefObject => { return useCallbackMergeRefs( - refs.filter((ref): ref is React.ForwardedRef => !!ref) + refs.filter((ref): ref is ForwardedRef => !!ref), ) } diff --git a/packages/hooks/useOutsideClick.ts b/packages/hooks/useOutsideClick.ts index ee70964a..39f7e0ad 100644 --- a/packages/hooks/useOutsideClick.ts +++ b/packages/hooks/useOutsideClick.ts @@ -1,9 +1,9 @@ -import React, { useCallback, useEffect, useRef } from 'react' +import { RefObject, useCallback, useEffect, useRef } from 'react' export const useOutsideClick = ( - callback?: () => void + callback?: () => void, ): { - ref: React.RefObject + ref: RefObject } => { const ref = useRef(null) @@ -17,7 +17,7 @@ export const useOutsideClick = ( const isOutside = popover?.contains(target) === false if (isOutside) callback?.() }, - [callback] + [callback], ) useEffect(() => { diff --git a/packages/hooks/useSystemTheme.ts b/packages/hooks/useSystemTheme.ts index 0af93eea..1608dad0 100644 --- a/packages/hooks/useSystemTheme.ts +++ b/packages/hooks/useSystemTheme.ts @@ -1,6 +1,6 @@ import { useEffect, useState } from 'react' -import { ThemeName } from '@lidofinance/theme' +import { ThemeName } from '../theme' export const useSystemTheme = (): ThemeName | undefined => { const [systemTheme, setSystemTheme] = useState() diff --git a/packages/icons/Icon.stories.tsx b/packages/icons/Icon.stories.tsx index 28396c70..dd01128f 100644 --- a/packages/icons/Icon.stories.tsx +++ b/packages/icons/Icon.stories.tsx @@ -1,15 +1,15 @@ -import { Story, Meta } from '@storybook/react' +import { StoryFn, Meta } from '@storybook/react' import styled from 'styled-components' -import * as components from './index' +import * as components from '.' type IconVariants = keyof typeof components const iconKeys = Object.keys(components) as IconVariants[] export default { title: 'Images/Icons', -} as Meta +} satisfies Meta -export const Base: Story<{ color: string; type: IconVariants }> = (props) => { +export const Base: StoryFn<{ color: string; type: IconVariants }> = (props) => { const { color, type } = props const Component = components[type] @@ -48,7 +48,7 @@ const IconListTitle = styled.div` opacity: 0.5; ` -export const List: Story = () => ( +export const List: StoryFn = () => ( {iconKeys.map((componentName) => { const Icon = components[componentName] @@ -79,7 +79,7 @@ const SocialListItem = styled.div<{ $color: string }>` } ` -export const Social: Story = () => { +export const Social: StoryFn = () => { const { Facebook, Twitter, Linkedin, Email, Telegram } = components return ( @@ -103,7 +103,7 @@ export const Social: Story = () => { ) } -export const CryptoCurrencies: Story = () => { +export const CryptoCurrencies: StoryFn = () => { const { Eth, Weth, Steth, Wsteth, Beth, Ldo, Ldopl, Solana, Stsol, Terra } = components const iconKeys = Object.keys({ @@ -135,7 +135,7 @@ export const CryptoCurrencies: Story = () => { ) } -export const CryptoWallets: Story = () => { +export const CryptoWallets: StoryFn = () => { const { MetaMask, MetaMaskCircle, @@ -207,7 +207,7 @@ export const CryptoWallets: Story = () => { ) } -export const CryptoExchanges: Story = () => { +export const CryptoExchanges: StoryFn = () => { const { Uniswap, OneInch } = components const iconKeys = Object.keys({ Uniswap, diff --git a/packages/icons/converter/index.js b/packages/icons/converter/index.js index e768964e..7c2e77ce 100644 --- a/packages/icons/converter/index.js +++ b/packages/icons/converter/index.js @@ -57,7 +57,7 @@ const readFolder = async (folder) => { code: data.toString(), name: file.replace('.svg', ''), } - }) + }), ) } @@ -82,9 +82,9 @@ const convertFiles = async () => { plugins: [svgrSvgo, svgrJsx, svgrPrettier], template: require('./template.component.js'), }, - { componentName } + { componentName }, ) - }) + }), ) const content = indexTemplate(componentsCode) diff --git a/packages/icons/converter/template.component.js b/packages/icons/converter/template.component.js index 171a87f3..a06d27fd 100644 --- a/packages/icons/converter/template.component.js +++ b/packages/icons/converter/template.component.js @@ -1,7 +1,7 @@ module.exports = function template( { template }, opts, - { interfaces, componentName, props, jsx } + { interfaces, componentName, props, jsx }, ) { return template.ast` ${interfaces} diff --git a/packages/icons/index.tsx b/packages/icons/index.tsx index 0568cda4..cfd8e0f1 100644 --- a/packages/icons/index.tsx +++ b/packages/icons/index.tsx @@ -4,7 +4,7 @@ import React from 'react' export const Ambire = React.forwardRef(function Ambire( props: React.SVGProps, - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( ) - } + }, ) export const Blochainwallet = React.forwardRef(function Blochainwallet( props: React.SVGProps, - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( ) - } + }, ) export const LedgerCircle = React.forwardRef(function LedgerCircle( props: React.SVGProps, - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( ) - } + }, ) export const MathWalletCircle = React.forwardRef(function MathWalletCircle( props: React.SVGProps, - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( ) - } + }, ) export const MetaMaskCircle = React.forwardRef(function MetaMaskCircle( props: React.SVGProps, - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( ) - } + }, ) export const WalletConnect = React.forwardRef(function WalletConnect( props: React.SVGProps, - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( , - svgRef?: React.Ref + svgRef?: React.Ref, ) { return ( ) => Object.values(enumObject).filter((value) => typeof value === 'string') @@ -22,11 +22,13 @@ export default { control: { type: 'range', min: 4, max: 64, step: 4 }, }, }, -} as Meta +} satisfies Meta -export const Basic: Story = (props) => +export const Basic: StoryFn = (props) => ( + +) -export const Badge: Story = (props) => ( +export const Badge: StoryFn = (props) => ( ) diff --git a/packages/identicon/Identicon.tsx b/packages/identicon/Identicon.tsx index 0f57d5a1..372f4b42 100644 --- a/packages/identicon/Identicon.tsx +++ b/packages/identicon/Identicon.tsx @@ -1,17 +1,39 @@ -import React, { ForwardedRef, forwardRef } from 'react' -import { IdenticonProps } from './types' -import { IdenticonStyle } from './IdenticonStyles' +import { + ComponentPropsWithoutRef, + CSSProperties, + ForwardedRef, + forwardRef, +} from 'react' import Jazzicon, { jsNumberForAddress } from 'react-jazzicon' +import cn from 'classnames' +import styles from './Identicon.module.css' -function Identicon(props: IdenticonProps, ref?: ForwardedRef) { - const { diameter = 24, address, paperStyles, svgStyles, ...rest } = props - const iconProps = { diameter, paperStyles, svgStyles } - - return ( - - - - ) +export type IdenticonProps = ComponentPropsWithoutRef<'div'> & { + address: string + diameter?: number + paperStyles?: CSSProperties + svgStyles?: CSSProperties } -export default forwardRef(Identicon) +export const Identicon = forwardRef( + ( + { + diameter = 24, + address, + paperStyles, + svgStyles, + className, + ...rest + }: IdenticonProps, + ref?: ForwardedRef, + ) => { + const iconProps = { diameter, paperStyles, svgStyles } + + return ( +
+ +
+ ) + }, +) +Identicon.displayName = 'Identicon' diff --git a/packages/identicon/IdenticonBadge.module.css b/packages/identicon/IdenticonBadge.module.css new file mode 100644 index 00000000..87323747 --- /dev/null +++ b/packages/identicon/IdenticonBadge.module.css @@ -0,0 +1,26 @@ +/* export const IdenticonBadgeStyle = styled.div<{ $color: IdenticonBadgeColors }>` + + ${({ $color }) => colors[$color]} +` */ + +.identiconBadge { + border-radius: 1000px; + padding: 4px; + margin: 0 6px; + display: inline-flex; + align-items: center; +} + +.background { + background: var(--lido-color-background); + color: var(--lido-color-text-secondary); +} + +.accent { + background: var(--lido-color-accent-darken); + color: var(--lido-color-accent-contrast); +} + +.wrapper { + padding: 0 6px; +} diff --git a/packages/identicon/IdenticonBadge.tsx b/packages/identicon/IdenticonBadge.tsx index a789a256..bcf34f77 100644 --- a/packages/identicon/IdenticonBadge.tsx +++ b/packages/identicon/IdenticonBadge.tsx @@ -1,36 +1,55 @@ -import React, { ForwardedRef, forwardRef } from 'react' -import { Address } from '@lidofinance/address' -import { IdenticonBadgeProps } from './types' -import { IdenticonBadgeStyle, AddressWrapperStyle } from './IdenticonStyles' -import Identicon from './Identicon' +import { ForwardedRef, forwardRef } from 'react' +import { Address } from '../address' +import { Identicon, IdenticonProps } from './Identicon' +import cn from 'classnames' +import styles from './IdenticonBadge.module.css' -function IdenticonBadge( - props: IdenticonBadgeProps, - ref?: ForwardedRef -) { - const { - symbols = 3, - color = 'background', - diameter, - address, - paperStyles, - svgStyles, - ...rest - } = props - const identiconProps = { address, diameter, paperStyles, svgStyles } +export enum IdenticonBadgeColor { + background, + accent, +} +export type IdenticonBadgeColors = keyof typeof IdenticonBadgeColor - return ( - - {symbols > 0 ? ( - -
- - ) : ( - '' - )} - - - ) +export type IdenticonBadgeProps = IdenticonProps & { + symbols?: number + color?: IdenticonBadgeColors } -export default forwardRef(IdenticonBadge) +export const IdenticonBadge = forwardRef( + ( + { + symbols = 3, + color = 'background', + diameter, + address, + paperStyles, + svgStyles, + className, + ...rest + }: IdenticonBadgeProps, + ref?: ForwardedRef, + ) => { + const identiconProps = { address, diameter, paperStyles, svgStyles } + + return ( +
+ {symbols > 0 ? ( +
+
+
+ ) : ( + '' + )} + +
+ ) + }, +) +IdenticonBadge.displayName = 'IdenticonBadge' diff --git a/packages/identicon/IdenticonStyles.tsx b/packages/identicon/IdenticonStyles.tsx deleted file mode 100644 index 5cce7a77..00000000 --- a/packages/identicon/IdenticonStyles.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import styled, { css } from 'styled-components' -import { IdenticonBadgeColors } from './types' - -const colors = { - background: css` - background: var(--lido-color-background); - color: var(--lido-color-textSecondary); - `, - accent: css` - background: var(--lido-color-accentDarken); - color: var(--lido-color-accentContrast); - `, -} - -export const IdenticonBadgeStyle = styled.div<{ $color: IdenticonBadgeColors }>` - border-radius: 1000px; - padding: 4px; - margin: 0 6px; - display: inline-flex; - align-items: center; - - ${({ $color }) => colors[$color]} -` - -export const IdenticonStyle = styled.div` - border-radius: 1000px; - overflow: hidden; - line-height: 0; - display: inline-block; -` - -export const AddressWrapperStyle = styled.div` - padding: 0 6px; -` diff --git a/packages/identicon/declarations.d.ts b/packages/identicon/declarations.d.ts index dfc47d86..12980b3a 100644 --- a/packages/identicon/declarations.d.ts +++ b/packages/identicon/declarations.d.ts @@ -1,11 +1,13 @@ declare module 'react-jazzicon' { + import { CSSProperties, PureComponent } from 'react' + export function jsNumberForAddress(address: string): number - export default class Jazzicon extends React.PureComponent { + export default class Jazzicon extends PureComponent { props: { seed: number diameter: number - paperStyles?: React.CSSProperties - svgStyles?: React.CSSProperties + paperStyles?: CSSProperties + svgStyles?: CSSProperties } } } diff --git a/packages/identicon/index.ts b/packages/identicon/index.ts index 802ec433..a6a779ec 100644 --- a/packages/identicon/index.ts +++ b/packages/identicon/index.ts @@ -1,3 +1,2 @@ -export { default as IdenticonBadge } from './IdenticonBadge' -export { default as Identicon } from './Identicon' -export * from './types' +export * from './IdenticonBadge' +export * from './Identicon' diff --git a/packages/identicon/types.ts b/packages/identicon/types.ts deleted file mode 100644 index b083d01f..00000000 --- a/packages/identicon/types.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { LidoComponentProps } from '@lidofinance/utils' -import React from 'react' -export type { Theme } from '@lidofinance/theme' - -export type IdenticonProps = LidoComponentProps< - 'div', - { - address: string - diameter?: number - paperStyles?: React.CSSProperties - svgStyles?: React.CSSProperties - } -> - -export enum IdenticonBadgeColor { - background, - accent, -} -export type IdenticonBadgeColors = keyof typeof IdenticonBadgeColor - -export type IdenticonBadgeProps = { - symbols?: number - color?: IdenticonBadgeColors -} & IdenticonProps diff --git a/packages/index.ts b/packages/index.ts index b4217187..edb41c5b 100644 --- a/packages/index.ts +++ b/packages/index.ts @@ -2,7 +2,6 @@ export * from './accordion' export * from './address' export * from './addressBadge' export * from './block' -export * from './box' export * from './button' export * from './checkbox' export * from './chip' @@ -26,7 +25,6 @@ export * from './section' export * from './select' export * from './service-page' export * from './stack' -export * from './styled-system' export * from './table' export * from './text' export * from './theme' @@ -34,6 +32,4 @@ export * from './toast' export * from './tooltip' export * from './transition' export * from './utils' -export * from './cookie-theme-toggler' -export * from './content-theme' export * from './cookies-tooltip' diff --git a/packages/input/Input.module.css b/packages/input/Input.module.css new file mode 100644 index 00000000..f5efa651 --- /dev/null +++ b/packages/input/Input.module.css @@ -0,0 +1,365 @@ +.wrapper { + position: relative; + display: inline-flex; + align-items: stretch; + box-sizing: border-box; + cursor: text; + + &.disabled { + cursor: default; + } + + &.fullwidth { + width: 100%; + } +} + +.decorator { + flex-grow: 0; + flex-shrink: 0; + cursor: inherit; + display: flex; + align-items: center; + + &.left { + padding-right: 16px; + } + + &.right { + padding-left: 16px; + } +} + +.message { + margin-top: 6px; + left: 0; + max-width: 100%; + position: absolute; + top: 100%; + line-height: 1.6em; + font-weight: 400; + font-size: var(--lido-font-size-xxs); + border-radius: var(--lido-border-radius-sm); + padding: 6px 10px; + white-space: nowrap; + overflow: hidden; + box-sizing: border-box; + text-overflow: ellipsis; + z-index: 3; + + &.bordered { + margin-top: 5px; + left: -1px; + max-width: calc(100% + 2px); + } + + &.error { + background: var(--lido-color-error); + color: var(--lido-color-error-contrast); + box-shadow: var(--lido-shadows-sm) var(--lido-color-shadow-light); + } + + &.warning { + background: var(--lido-color-warning); + color: var(--lido-color-warning-contrast); + box-shadow: var(--lido-shadows-sm) var(--lido-color-shadow-light); + } + + &.success { + background: var(--lido-color-success); + color: var(--lido-color-success-contrast); + box-shadow: var(--lido-shadows-sm) var(--lido-color-shadow-light); + } +} + +.label { + position: absolute; + left: 0; + top: 50%; + font-size: 1em; + line-height: 1.25em; + margin: -0.625em 0 0; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + max-width: 100%; + color: inherit; + transform-origin: 0% 100%; + transition: transform var(--lido-transition-duration-fast) ease; + transition-property: transform, opacity, color; + transform: translateY(-14px) scale(0.75); + opacity: 1; + + &.colorDefault { + color: var(--lido-color-text-secondary); + } + + &.colorAccent { + color: var(--lido-color-accent-contrast-secondary); + } +} + +@layer content { + .content { + position: relative; + display: flex; + flex-grow: 1; + padding-left: 15px; + padding-right: 15px; + font-weight: 400; + font-size: var(--lido-font-size-xs); + border: 1px solid; + border-radius: var(--lido-border-radius-lg); + transition: border-color var(--lido-transition-duration-fast) ease; + } +} + +.content:not(.disabled) { + &:hover { + z-index: 1; + } + + &:focus-within { + z-index: 2; + border-color: var(--lido-color-border-active); + + .label { + color: var(--lido-color-primary); + opacity: 1; + } + } +} + +@layer content-color { + .content.colorDefault { + background: var(--lido-color-control-bg); + border-color: var(--lido-color-border); + color: var(--lido-color-text); + + &.disabled { + background: var(--lido-color-background); + } + + &:not(.disabled) { + &:hover { + border-color: var(--lido-color-border-hover); + } + } + } + + .content.colorAccent { + background: var(--lido-color-accent-control-bg); + border-color: var(--lido-color-accent-border); + color: var(--lido-color-accent-text); + + &.disabled { + background: var(--lido-color-control-bg); + } + + &:not(.disabled) { + &:hover { + border-color: var(--lido-color-accent-border-hover); + } + } + } +} + +@layer content-variant { + .content.variantDefault { + padding-top: 17px; + padding-bottom: 17px; + + & .decorator.left, + & .decorator.right { + margin-top: -17px; + margin-bottom: -17px; + } + } + + .content.variantSmall { + padding-top: 9px; + padding-bottom: 9px; + + & .decorator.left, + & .decorator.right { + padding-top: -9px; + padding-bottom: -9px; + } + } +} + +.content.active { + &, + &:hover, + &:focus-within { + z-index: 2; + border-color: var(--lido-color-border-active); + + /* stylelint-disable-next-line no-descending-specificity */ + .label { + color: var(--lido-color-primary); + opacity: 1; + } + } +} + +.content.warning { + &, + &:hover, + &:focus-within { + border-color: var(--lido-color-warning); + + /* stylelint-disable-next-line no-descending-specificity */ + .label { + color: var(--lido-color-warning); + } + } +} + +.content.error { + &, + &:hover, + &:focus-within { + border-color: var(--lido-color-error); + + /* stylelint-disable-next-line no-descending-specificity */ + .label { + color: var(--lido-color-error); + } + } +} + +.input { + top: 0; + width: 100%; + font-family: inherit; + font-weight: 400; + font-size: 1em; + line-height: 1.43em; + padding: 0; + border-radius: 0; + background: transparent; + box-shadow: none; + border: none; + outline: none; + position: relative; + + &::placeholder { + transition: opacity var(--lido-transition-duration-fast) ease; + } +} + +.input.labeled { + top: 8px; + + &:not(:focus):placeholder-shown { + & + .label { + transform: scale(1); + opacity: 0.5; + + &.colorDefault { + color: var(--lido-color-text-secondary); + } + + &.colorAccent { + color: var(--lido-color-accent-contrast-secondary); + } + } + + &::placeholder { + opacity: 0; + } + } +} + +.input.colorDefault { + color: var(--lido-color-text); + + &:disabled { + color: var(--lido-color-text-secondary); + } + + &::placeholder { + color: var(--lido-color-text-secondary); + } + + &:-webkit-autofill { + box-shadow: 0 0 0 100px var(--lido-color-control-bg) inset !important; + color: var(--lido-color-text) !important; + -webkit-text-fill-color: var(--lido-color-text) !important; + } + + &:-internal-autofill-selected { + color: var(--lido-color-text) !important; + -webkit-text-fill-color: var(--lido-color-text) !important; + } +} + +.input.colorAccent { + color: var(--lido-color-accent-text); + opacity: 1; + + &:disabled { + color: var(--lido-color-accent-text); + opacity: 0.5; + } + + &::placeholder { + color: var(--lido-color-accent-text); + opacity: 0.5; + } + + &:-webkit-autofill { + box-shadow: 0 0 0 100px var(--lido-color-accent-control-bg) inset !important; + color: var(--lido-color-accent-contrast) !important; + -webkit-text-fill-color: var(--lido-color-accent-contrast) !important; + } + + &:-internal-autofill-selected { + color: var(--lido-color-accent-contrast) !important; + -webkit-text-fill-color: var(--lido-color-accent-contrast) !important; + } +} + +.controlWrapper { + position: relative; + display: flex; + flex-grow: 1; +} + +.group { + display: inline-flex; + position: relative; + + &.fullwidth { + width: 100%; + } +} + +.groupContent { + display: flex; + width: 100%; + + & > .wrapper { + margin: 0 -1px 0 0; + + &:first-child .content { + border-top-right-radius: 0; + border-bottom-right-radius: 0; + } + + &:last-child .content { + margin-right: 0; + border-top-left-radius: 0; + border-bottom-left-radius: 0; + } + } +} + +.textareaLabel { + top: 10px; +} + +.textarea { + resize: none; +} diff --git a/packages/input/Input.stories.tsx b/packages/input/Input.stories.tsx index 8bbdca92..fe81bf88 100644 --- a/packages/input/Input.stories.tsx +++ b/packages/input/Input.stories.tsx @@ -1,14 +1,12 @@ -import { Story, Meta } from '@storybook/react' -import { Eth } from '@lidofinance/icons' -import { Block } from '@lidofinance/block' -import { Button } from '@lidofinance/button' -import { Identicon } from '@lidofinance/identicon' -import Input from './Input' +import { StoryFn, Meta } from '@storybook/react' import styled from 'styled-components' -import { InputProps, InputType, InputVariant, InputColor } from './types' import { useCallback, useState } from 'react' -import { ModalProps } from '../modal/types' -import Modal from '../modal/Modal' +import { Eth } from '../icons' +import { Block } from '../block' +import { Button } from '../button' +import { Identicon } from '../identicon' +import { Modal, ModalProps } from '../modal' +import { Input, InputProps, InputType, InputVariant, InputColor } from '.' const getOptions = (enumObject: Record) => Object.values(enumObject).filter((value) => typeof value === 'string') @@ -27,9 +25,9 @@ export default { table: { disable: true }, }, }, -} as Meta +} satisfies Meta -export const Basic: Story = (props) => +export const Basic: StoryFn = (props) => Basic.args = { placeholder: 'Amount', @@ -48,7 +46,7 @@ Basic.argTypes = { }, } -export const Small: Story = (props) => +export const Small: StoryFn = (props) => Small.args = { variant: 'small', @@ -62,7 +60,7 @@ Small.argTypes = { }, } -export const Label: Story = (props) => +export const Label: StoryFn = (props) => Label.args = { label: 'Email address', @@ -90,7 +88,7 @@ const MaxButton = () => ( ) -export const WithDecorators: Story = (props) => ( +export const WithDecorators: StoryFn = (props) => ( } rightDecorator={} @@ -108,7 +106,7 @@ WithDecorators.argTypes = { }, } -export const WithIdenticon: Story = (props) => { +export const WithIdenticon: StoryFn = (props) => { const [value, setValue] = useState('') return ( @@ -128,7 +126,7 @@ WithIdenticon.args = { placeholder: 'Ethereum address', } -export const WithButton: Story = (props) => ( +export const WithButton: StoryFn = (props) => ( = (props) => +export const WithError: StoryFn = (props) => WithError.args = { fullwidth: true, @@ -171,7 +169,7 @@ WithError.argTypes = { }, } -export const WithWarning: Story = (props) => +export const WithWarning: StoryFn = (props) => WithWarning.args = { fullwidth: true, @@ -186,7 +184,7 @@ const Success = styled.span` color: var(--lido-color-success); ` -export const WithSuccess: Story = (props) => ( +export const WithSuccess: StoryFn = (props) => ( Subscribed} {...props} /> ) @@ -198,7 +196,7 @@ WithSuccess.args = { 'Thank you for subscribing! We will notify you once we kick off our platform.', } -export const AccentColor: Story = (props) => { +export const AccentColor: StoryFn = (props) => { const [value, setValue] = useState('') return ( @@ -252,7 +250,7 @@ const useModal = (props: ModalProps) => { return { state, handleOpen, handleClose, handleBack } } -export const ErrorsOverlapCase: Story = (props) => { +export const ErrorsOverlapCase: StoryFn = (props) => { const { state, handleOpen, handleClose } = useModal(props) return ( diff --git a/packages/input/Input.tsx b/packages/input/Input.tsx index 162d08a2..ace95319 100644 --- a/packages/input/Input.tsx +++ b/packages/input/Input.tsx @@ -1,108 +1,134 @@ -import React, { ForwardedRef, forwardRef } from 'react' import { - InputWrapperStyle, - InputContentStyle, - InputControlWrapperStyle, - InputStyle, - InputLeftDecoratorStyle, - InputRightDecoratorStyle, - InputMessageStyle, -} from './InputStyles' -import { InputLabelStyle } from './LabelStyles' -import { InputProps } from './types' + ComponentPropsWithoutRef, + ForwardedRef, + ReactNode, + forwardRef, +} from 'react' +import { CommonProps, InputTypes } from './types' +import styles from './Input.module.css' +import cn from 'classnames' -function Input(props: InputProps, ref?: ForwardedRef) { - const { - label, - error, - warning, - success, - active = false, - fullwidth = false, - placeholder = ' ', - leftDecorator, - rightDecorator, - className, - style, - variant = 'default', - color = 'default', - wrapperRef, - children, - ...rest - } = props +export type InputProps = ComponentPropsWithoutRef<'input'> & + CommonProps & { + type?: InputTypes + leftDecorator?: ReactNode + rightDecorator?: ReactNode + } - const { id, disabled = false } = props - const wrapperProps = { className, style } +export const Input = forwardRef( + ( + { + id, + disabled = false, + label, + error, + warning, + success, + active = false, + fullwidth = false, + placeholder = ' ', + leftDecorator, + rightDecorator, + className, + style, + variant = 'default', + color = 'default', + wrapperRef, + children, + ...rest + }: InputProps, + ref?: ForwardedRef, + ) => { + const hasLabel = !!label && variant === 'default' - const hasLabel = !!label && variant === 'default' + const hasError = !!error + const hasErrorMessage = hasError && typeof error !== 'boolean' + const hasWarning = !hasError && !!warning // `error` overrides `warning` + const hasWarningMessage = hasWarning && typeof warning !== 'boolean' + const hasSuccess = !!success && !error + const hasSuccessMessage = hasSuccess && typeof success !== 'boolean' - const hasError = !!error - const hasErrorMessage = hasError && typeof error !== 'boolean' - const hasWarning = !hasError && !!warning // `error` overrides `warning` - const hasWarningMessage = hasWarning && typeof warning !== 'boolean' - const hasSuccess = !!success && !error - const hasSuccessMessage = hasSuccess && typeof success !== 'boolean' + const hasLeftDecorator = !!leftDecorator + const hasRightDecorator = !!rightDecorator - const hasLeftDecorator = !!leftDecorator - const hasRightDecorator = !!rightDecorator - - return ( - - - {hasLeftDecorator && ( - {leftDecorator} - )} - - - - {hasLabel && ( - {label} + + {hasLeftDecorator && ( + + {leftDecorator} + )} - - {hasRightDecorator && ( - {rightDecorator} - )} - +
+ + {hasLabel && ( + + {label} + + )} +
- {hasErrorMessage && ( - - {error} - - )} - {hasWarningMessage && ( - - {warning} - - )} - {hasSuccessMessage && ( - - {success} - - )} -
- ) -} + {hasRightDecorator && ( + + {rightDecorator} + + )} + -export default forwardRef(Input) + {hasErrorMessage && ( + + {error} + + )} + {hasWarningMessage && ( + + {warning} + + )} + {hasSuccessMessage && ( + + {success} + + )} + + ) + }, +) +Input.displayName = 'Input' diff --git a/packages/input/InputGroup.stories.tsx b/packages/input/InputGroup.stories.tsx index 81469754..8d5fed0b 100644 --- a/packages/input/InputGroup.stories.tsx +++ b/packages/input/InputGroup.stories.tsx @@ -1,7 +1,5 @@ -import { Story, Meta } from '@storybook/react' -import Input from './Input' -import InputGroup from './InputGroup' -import { InputGroupProps } from './types' +import { StoryFn, Meta } from '@storybook/react' +import { Input, InputGroup, InputGroupProps } from '.' export default { component: Input, @@ -9,9 +7,9 @@ export default { args: { fullwidth: false, }, -} as Meta +} satisfies Meta -export const Basic: Story = (props) => ( +export const Basic: StoryFn = (props) => ( diff --git a/packages/input/InputGroup.tsx b/packages/input/InputGroup.tsx index 241b5dab..dc10f778 100644 --- a/packages/input/InputGroup.tsx +++ b/packages/input/InputGroup.tsx @@ -1,28 +1,41 @@ -import React, { ForwardedRef, forwardRef } from 'react' -import { InputMessageStyle } from './InputStyles' -import { InputGroupStyle, InputGroupContentStyle } from './InputGroupStyles' -import { InputGroupProps } from './types' +import { + ComponentPropsWithoutRef, + ForwardedRef, + ReactNode, + forwardRef, +} from 'react' +import cn from 'classnames' +import styles from './Input.module.css' -function InputGroup( - props: InputGroupProps, - ref?: ForwardedRef -) { - const { fullwidth = false, error, success, children, ...rest } = props - - const hasError = !!error - const hasSuccess = !!success && !error - - return ( - - {children} - {hasError && ( - {error} - )} - {hasSuccess && ( - {success} - )} - - ) +export type InputGroupProps = ComponentPropsWithoutRef<'span'> & { + fullwidth?: boolean + error?: ReactNode + success?: ReactNode } -export default forwardRef(InputGroup) +export const InputGroup = forwardRef( + ( + { fullwidth = false, error, success, children, ...rest }: InputGroupProps, + ref?: ForwardedRef, + ) => { + const hasError = !!error + const hasSuccess = !!success && !error + + return ( + + {children} + {hasError && ( + {error} + )} + {hasSuccess && ( + {success} + )} + + ) + }, +) +InputGroup.displayName = 'InputGroup' diff --git a/packages/input/InputGroupStyles.ts b/packages/input/InputGroupStyles.ts deleted file mode 100644 index a2048aa0..00000000 --- a/packages/input/InputGroupStyles.ts +++ /dev/null @@ -1,32 +0,0 @@ -import styled from 'styled-components' -import { InputWrapperStyle, InputContentStyle } from './InputStyles' - -export const InputGroupStyle = styled.span<{ $fullwidth: boolean }>` - display: inline-flex; - position: relative; - width: ${({ $fullwidth }) => ($fullwidth ? '100%' : 'auto')}; -` - -export const InputGroupContentStyle = styled.span` - display: flex; - width: 100%; - - & > ${InputWrapperStyle} { - margin: 0 -1px 0 0; - - &:first-child { - & ${InputContentStyle} { - border-top-right-radius: 0; - border-bottom-right-radius: 0; - } - } - - &:last-child { - & ${InputContentStyle} { - margin-right: 0; - border-top-left-radius: 0; - border-bottom-left-radius: 0; - } - } - } -` diff --git a/packages/input/InputStyles.ts b/packages/input/InputStyles.ts deleted file mode 100644 index eb18faef..00000000 --- a/packages/input/InputStyles.ts +++ /dev/null @@ -1,314 +0,0 @@ -import styled, { css } from 'styled-components' -import { InputMessageVariants, InputVariants, InputColors } from './types' -import { - labelEmptyValueCSS, - labelFocusCSS, - labelErrorCSS, - InputLabelStyle, - labelWarningCSS, -} from './LabelStyles' - -const statesCSS = css` - &:hover { - z-index: 1; - } - - &:focus-within { - z-index: 2; - border-color: var(--lido-color-borderActive); - - ${InputLabelStyle} { - ${labelFocusCSS} - } - } -` - -const activeCSS = css` - &, - &:hover, - &:focus-within { - z-index: 2; - border-color: var(--lido-color-borderActive); - - ${InputLabelStyle} { - ${labelFocusCSS} - } - } -` - -const errorCSS = css` - &, - &:hover, - &:focus-within { - border-color: var(--lido-color-error); - - ${InputLabelStyle} { - ${labelErrorCSS} - } - } -` - -const warningCSS = css` - &, - &:hover, - &:focus-within { - border-color: var(--lido-color-warning); - - ${InputLabelStyle} { - ${labelWarningCSS} - } - } -` - -const wrapperColors = { - default: css<{ $disabled: boolean }>` - background: var(--lido-color-controlBg); - border-color: var(--lido-color-border); - color: var(--lido-color-text); - - ${({ $disabled }) => - $disabled - ? `background: var(--lido-color-background);` - : ` - &:hover { - border-color: var(--lido-color-borderHover); - } - `}; - `, - accent: css<{ $disabled: boolean }>` - background: var(--lido-color-accentControlBg); - border-color: var(--lido-color-accentBorder); - color: var(--lido-color-accentText); - - ${({ $disabled }) => - $disabled - ? `background: var(--lido-color-controlBg);` - : ` - &:hover { - border-color: var(--lido-color-accentBorderHover); - } - `}; - `, -} - -const decoratorCSS = css` - flex-grow: 0; - flex-shrink: 0; - cursor: inherit; - display: flex; - align-items: center; -` - -export const InputLeftDecoratorStyle = styled.span` - ${decoratorCSS} - padding-right: 16px; -` - -export const InputRightDecoratorStyle = styled.span` - ${decoratorCSS} - padding-left: 16px; -` - -export const InputWrapperStyle = styled.label<{ - $fullwidth: boolean - $disabled: boolean -}>` - position: relative; - display: inline-flex; - align-items: stretch; - box-sizing: border-box; - cursor: ${({ $disabled }) => ($disabled ? 'default' : 'text')}; - width: ${({ $fullwidth }) => ($fullwidth ? '100%' : 'auto')}; -` - -const contentVariants = { - default: css` - padding-top: 17px; - padding-bottom: 17px; - - & ${InputLeftDecoratorStyle}, & ${InputRightDecoratorStyle} { - margin-top: -17px; - margin-bottom: -17px; - } - `, - small: css` - padding-top: 9px; - padding-bottom: 9px; - - & ${InputLeftDecoratorStyle}, & ${InputRightDecoratorStyle} { - padding-top: -9px; - padding-bottom: -9px; - } - `, -} - -export const InputContentStyle = styled.span<{ - $error: boolean - $warning: boolean - $active: boolean - $disabled: boolean - $color: InputColors - $variant: InputVariants -}>` - position: relative; - display: flex; - flex-grow: 1; - padding-left: 15px; - padding-right: 15px; - font-weight: 400; - font-size: ${({ theme }) => theme.fontSizesMap.xs}px; - border: 1px solid; - border-radius: ${({ theme }) => theme.borderRadiusesMap.lg}px; - transition: border-color ${({ theme }) => theme.duration.fast} ease; - - ${({ $variant }) => contentVariants[$variant]}; - ${({ $color }) => wrapperColors[$color]}; - ${({ $disabled }) => ($disabled ? '' : statesCSS)}; - ${({ $active }) => ($active ? activeCSS : '')}; - ${({ $warning }) => ($warning ? warningCSS : '')}; - ${({ $error }) => ($error ? errorCSS : '')}; -` - -export const InputControlWrapperStyle = styled.div` - position: relative; - display: flex; - flex-grow: 1; -` - -const labeledCSS = css` - &:not(:focus):placeholder-shown { - & + ${InputLabelStyle} { - ${labelEmptyValueCSS} - } - - &::placeholder { - opacity: 0; - } - } -` - -const inputColors = { - default: css` - color: var(--lido-color-text); - - &:disabled { - color: var(--lido-color-textSecondary); - } - - &::placeholder { - color: var(--lido-color-textSecondary); - } - - &:-webkit-autofill { - box-shadow: 0 0 0 100px var(--lido-color-controlBg) inset !important; - color: var(--lido-color-text) !important; - -webkit-text-fill-color: var(--lido-color-text) !important; - } - - &:-internal-autofill-selected { - color: var(--lido-color-text) !important; - -webkit-text-fill-color: var(--lido-color-text) !important; - } - `, - accent: css` - color: var(--lido-color-accentText); - opacity: 1; - - &:disabled { - color: var(--lido-color-accentText); - opacity: 0.5; - } - - &::placeholder { - color: var(--lido-color-accentText); - opacity: 0.5; - } - - &:-webkit-autofill { - box-shadow: 0 0 0 100px var(--lido-color-accentControlBg) inset !important; - color: var(--lido-color-accentContrast) !important; - -webkit-text-fill-color: var(--lido-color-accentContrast) !important; - } - - &:-internal-autofill-selected { - color: var(--lido-color-accentContrast) !important; - -webkit-text-fill-color: var(--lido-color-accentContrast) !important; - } - `, -} - -export const InputStyle = styled.input<{ - $labeled: boolean - $color: InputColors -}>` - width: 100%; - font-family: inherit; - font-weight: 400; - font-size: 1em; - line-height: 1.43em; - padding: 0; - border-radius: 0; - background: transparent; - box-shadow: none; - border: none; - outline: none; - position: relative; - top: ${({ $labeled }) => ($labeled ? 8 : 0)}px; - - &::placeholder { - transition: opacity ${({ theme }) => theme.duration.fast} ease; - } - - ${({ $color }) => inputColors[$color]} - ${({ $labeled }) => ($labeled ? labeledCSS : '')} -` - -export const TextareaStyle = styled(InputStyle).attrs({ - as: 'textarea', -})` - resize: none; -` - -const messageVariants = { - error: css` - background: var(--lido-color-error); - color: var(--lido-color-errorContrast); - box-shadow: ${({ theme }) => theme.boxShadows.sm} - var(--lido-color-shadowLight); - `, - warning: css` - background: var(--lido-color-warning); - color: var(--lido-color-warningContrast); - box-shadow: ${({ theme }) => theme.boxShadows.sm} - var(--lido-color-shadowLight); - `, - success: css` - background: var(--lido-color-success); - color: var(--lido-color-successContrast); - box-shadow: ${({ theme }) => theme.boxShadows.sm} - var(--lido-color-shadowLight); - `, -} - -export const InputMessageStyle = styled.span<{ - $variant: InputMessageVariants - $bordered?: boolean -}>` - margin-top: ${({ $bordered }) => ($bordered ? 5 : 6)}px; - left: ${({ $bordered }) => ($bordered ? -1 : 0)}px; - position: absolute; - top: 100%; - line-height: 1.6em; - font-weight: 400; - font-size: ${({ theme }) => theme.fontSizesMap.xxs}px; - border-radius: ${({ theme }) => theme.borderRadiusesMap.sm}px; - padding: 6px 10px; - white-space: nowrap; - overflow: hidden; - box-sizing: border-box; - text-overflow: ellipsis; - max-width: ${({ $bordered }) => ($bordered ? 'calc(100% + 2px)' : '100%')}; - z-index: 3; - - ${({ $variant }) => messageVariants[$variant]} -` diff --git a/packages/input/LabelStyles.ts b/packages/input/LabelStyles.ts deleted file mode 100644 index 2195d196..00000000 --- a/packages/input/LabelStyles.ts +++ /dev/null @@ -1,60 +0,0 @@ -import styled, { css } from 'styled-components' -import { InputColors } from './types' - -const colors = { - default: css` - color: var(--lido-color-textSecondary); - `, - accent: css` - color: var(--lido-color-accentContrastSecondary); - `, -} - -export const labelEmptyValueCSS = css<{ $color: InputColors }>` - ${({ $color }) => colors[$color]} - - transform: scale(1); - opacity: 0.5; -` - -export const labelFilledValueCSS = css<{ $color: InputColors }>` - ${({ $color }) => colors[$color]} - - transform: translateY(-14px) scale(0.75); - opacity: 1; -` - -export const labelFocusCSS = css` - color: var(--lido-color-primary); - opacity: 1; -` - -export const labelErrorCSS = css` - color: var(--lido-color-error); -` - -export const labelWarningCSS = css` - color: var(--lido-color-warning); -` - -export const InputLabelStyle = styled.span` - position: absolute; - left: 0; - top: 50%; - font-size: 1em; - line-height: 1.25em; - margin: -0.625em 0 0 0; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - max-width: 100%; - color: inherit; - transform-origin: 0% 100%; - transition: transform ${({ theme }) => theme.duration.fast} ease; - transition-property: transform, opacity, color; - ${labelFilledValueCSS}; -` - -export const TextareaLabelStyle = styled(InputLabelStyle)` - top: 10px; -` diff --git a/packages/input/OptionsSlider.stories.tsx b/packages/input/OptionsSlider.stories.tsx index a58fb705..90a8730d 100644 --- a/packages/input/OptionsSlider.stories.tsx +++ b/packages/input/OptionsSlider.stories.tsx @@ -1,7 +1,6 @@ -import { Story, Meta } from '@storybook/react' -import { SliderInputProps } from './types' -import React, { useState } from 'react' -import OptionsSlider from './OptionsSlider' +import { StoryFn, Meta } from '@storybook/react' +import { useState } from 'react' +import { OptionsSlider, SliderInputProps } from '.' export default { component: OptionsSlider, @@ -17,15 +16,15 @@ export default { table: { disable: true }, }, }, -} as Meta +} satisfies Meta -export const Basic: Story = (props) => { +export const Basic: StoryFn = (props) => { const [value, setValue] = useState('weekly') return ( setValue(val as string)} + onChange={(val: string) => setValue(val)} options={[ { value: 'weekly', label: 'Weekly' }, { value: 'monthly', label: 'Monthly' }, diff --git a/packages/input/OptionsSlider.tsx b/packages/input/OptionsSlider.tsx index 103bedc4..6360db40 100644 --- a/packages/input/OptionsSlider.tsx +++ b/packages/input/OptionsSlider.tsx @@ -1,44 +1,61 @@ -import React from 'react' -import SliderInput from './SliderInput' -import { OptionsSliderInputProps } from './types' - -const OptionsSlider = ({ - options, - value, - onChange, -}: OptionsSliderInputProps): React.ReactElement => { - let sliderIndex = options.findIndex((option) => option.value === value) - - if (sliderIndex === -1) { - sliderIndex = 0 - } - - const max = options.length - 1 - - const getLabel = (optionIndex: number) => { - return options[optionIndex].label - } - - const handleSliderChange = (e: React.ChangeEvent) => { - const optionIndex = Number(e.target.value) - onChange(options[optionIndex].value, optionIndex) - } - - const labels = options.map(({ label }, index) => ({ value: index, label })) - return ( - - onChange(options[optionIndex].value, optionIndex) - } - /> - ) +import { ForwardedRef, ReactNode, forwardRef, ChangeEvent } from 'react' +import { SliderInput, SliderInputProps } from './SliderInput' + +export type SliderOptionValue = string | number + +export interface SliderOption { + value: SliderOptionValue + label: ReactNode } -export default OptionsSlider +export type OptionsSliderInputProps = Omit< + SliderInputProps, + 'value' | 'onChange' +> & { + options: [SliderOption, SliderOption, ...SliderOption[]] // this is declaration like T[] but with "at least 2 elements" constraint + value?: SliderOptionValue // if value is not provided, component should act isolated; initial value should be first option + onChange: (value: SliderOptionValue, valueIndex: number) => unknown +} + +export const OptionsSlider = forwardRef( + ( + { options, value, onChange, ...rest }: OptionsSliderInputProps, + ref: ForwardedRef, + ) => { + let sliderIndex = options.findIndex((option) => option.value === value) + + if (sliderIndex === -1) { + sliderIndex = 0 + } + + const max = options.length - 1 + + const getLabel = (optionIndex: number) => { + return options[optionIndex].label + } + + const handleSliderChange = (e: ChangeEvent) => { + const optionIndex = Number(e.target.value) + onChange(options[optionIndex].value, optionIndex) + } + + const labels = options.map(({ label }, index) => ({ value: index, label })) + return ( + + onChange(options[optionIndex].value, optionIndex) + } + ref={ref} + {...rest} + /> + ) + }, +) +OptionsSlider.displayName = 'OptionsSlider' diff --git a/packages/input/SliderInputStyles.ts b/packages/input/SliderInput.module.css similarity index 53% rename from packages/input/SliderInputStyles.ts rename to packages/input/SliderInput.module.css index caef7a98..ef8a792e 100644 --- a/packages/input/SliderInputStyles.ts +++ b/packages/input/SliderInput.module.css @@ -1,9 +1,7 @@ -import styled from 'styled-components' - -export const RangeInputSlider = styled.input.attrs({ type: 'range' })` +.range { width: 100%; background-color: transparent; - -webkit-appearance: none; + appearance: none; position: absolute; margin: 0; top: 56px; @@ -27,12 +25,14 @@ export const RangeInputSlider = styled.input.attrs({ type: 'range' })` margin-top: -8px; width: 18px; height: 18px; - background: #ffffff; - box-shadow: 0 0.5px 4px rgba(0, 0, 0, 0.12), 0 6px 13px rgba(0, 0, 0, 0.12); + background: #fff; + box-shadow: + 0 0.5px 4px rgb(0 0 0 / 12%), + 0 6px 13px rgb(0 0 0 / 12%); border: 0; border-radius: 50px; cursor: pointer; - -webkit-appearance: none; + appearance: none; } &::-moz-range-track { @@ -47,7 +47,7 @@ export const RangeInputSlider = styled.input.attrs({ type: 'range' })` &::-moz-range-thumb { width: 18px; height: 18px; - background: #ffffff; + background: #fff; border: 0; border-radius: 50px; cursor: pointer; @@ -60,82 +60,53 @@ export const RangeInputSlider = styled.input.attrs({ type: 'range' })` height: 2px; cursor: pointer; } -` +} -export const Track = styled.div<{ - fillPercentage: number - borderNone?: boolean -}>` - height: 1px; - position: absolute; - bottom: 0; - left: 0; - right: 0; - background: var(--lido-color-border); - - ${(props) => - props.borderNone && - ` - background: none; - `}; - - &:before { - content: ''; - position: absolute; - display: block; - height: 2px; - bottom: 0; - left: 0; - background: var(--lido-color-primary); - width: ${(props) => props.fillPercentage || 0}%; - } -` +.wrapper { + position: relative; +} -export const Slider = styled.div<{ - borderNone?: boolean -}>` +.slider { position: relative; height: 56px; padding: 16px 20px; box-sizing: border-box; + background: var(--lido-color-accent-control-bg); + overflow: hidden; +} - background: var(--lido-color-accentControlBg); - border-right: 1px solid var(--lido-color-border); - border-left: 1px solid var(--lido-color-border); - border-top: 1px solid var(--lido-color-border); - - ${(props) => - props.borderNone && - ` - border-right: none; - border-left: none; - border-top: none; - `}; - +.slider.border { + border: 1px solid var(--lido-color-border); border-radius: 10px; - overflow: hidden; -` +} -export const SliderWrapper = styled.div` - position: relative; -` +.track { + content: ''; + position: absolute; + display: block; + height: 2px; + bottom: 0; + left: 0; + width: 0; + background: var(--lido-color-primary); +} -export const LabelContainer = styled.div` +.labelContainer { display: flex; justify-content: space-between; margin-top: 8px; -` +} -export const Label = styled.span` +.label { font-style: normal; font-weight: 400; font-size: 12px; line-height: 20px; color: var(--lido-color-text); opacity: 0.5; -` +} -export const LabelButton = styled.button` +.labelButton { border: none; background-color: transparent; font-style: normal; @@ -145,4 +116,4 @@ export const LabelButton = styled.button` color: var(--lido-color-text); opacity: 0.5; cursor: pointer; -` +} diff --git a/packages/input/SliderInput.stories.tsx b/packages/input/SliderInput.stories.tsx index b603b296..f038de03 100644 --- a/packages/input/SliderInput.stories.tsx +++ b/packages/input/SliderInput.stories.tsx @@ -1,7 +1,6 @@ -import { Story, Meta } from '@storybook/react' -import { SliderInputProps } from './types' -import React, { useState } from 'react' -import SliderInput from './SliderInput' +import { StoryFn, Meta } from '@storybook/react' +import { useState } from 'react' +import { SliderInput, SliderInputProps } from '.' export default { component: SliderInput, @@ -10,6 +9,7 @@ export default { disabled: false, fullwidth: false, active: false, + borderNone: false, }, argTypes: { onChange: { @@ -17,9 +17,9 @@ export default { table: { disable: true }, }, }, -} as Meta +} satisfies Meta -export const Basic: Story = (props) => { +export const Basic: StoryFn = (props) => { const [value, setValue] = useState(100000) return ( String(val), - borderNone, - labels, -}: SliderInputProps): React.ReactElement { - const fillPercentage = ((value - min) / (max - min)) * 100 - const LabelComponent = onLabelClick ? LabelButton : Label - const createClickHandler = (value: number) => () => onLabelClick?.(value) +export interface ValueLabel { + value: number + label: ReactNode +} - return ( - - - {getLabel(value)} - - - - - {minLabel && ( - - {minLabel} - - )} - {labels?.map(({ value, label }) => ( - - {label} - - ))} - {maxLabel && ( - - {maxLabel} - - )} - - - ) +export type SliderInputProps = ComponentPropsWithoutRef<'input'> & { + value: number + onChange?: ChangeEventHandler + min?: number + max?: number + minLabel?: ReactNode + maxLabel?: ReactNode + step?: number + getLabel?: (value: number) => ReactNode + borderNone?: boolean + labels?: ValueLabel[] + onLabelClick?: (value: number) => unknown } -export default SliderInput +export const SliderInput = forwardRef( + ( + { + value, + onChange, + onLabelClick, + min = 0, + max = 100, + step = 1, + minLabel, + maxLabel, + getLabel = (val) => String(val), + borderNone, + labels, + className, + style, + ...rest + }: SliderInputProps, + ref: ForwardedRef, + ) => { + const fillPercentage = ((value - min) / (max - min)) * 100 + const createClickHandler = (value: number) => () => onLabelClick?.(value) + + return ( +
+
+ {getLabel(value)} +
+
+ +
+ {minLabel && + (onLabelClick ? ( + + ) : ( + {minLabel} + ))} + {labels?.map(({ value, label }) => + onLabelClick ? ( + + ) : ( + + {label} + + ), + )} + {maxLabel && + (onLabelClick ? ( + + ) : ( + {maxLabel} + ))} +
+
+ ) + }, +) +SliderInput.displayName = 'SliderInput' diff --git a/packages/input/Textarea.stories.tsx b/packages/input/Textarea.stories.tsx index 39c39e54..1237a1a5 100644 --- a/packages/input/Textarea.stories.tsx +++ b/packages/input/Textarea.stories.tsx @@ -1,6 +1,5 @@ -import { Story, Meta } from '@storybook/react' -import Textarea from './Textarea' -import { TextareaProps } from './types' +import { StoryFn, Meta } from '@storybook/react' +import { Textarea, TextareaProps } from '.' export default { component: Textarea, @@ -16,16 +15,18 @@ export default { table: { disable: true }, }, }, -} as Meta +} satisfies Meta -export const Basic: Story = (props) =>