diff --git a/.github/workflows/publish-canary.yml b/.github/workflows/publish-canary.yml index 61fa92bd..12dadca6 100644 --- a/.github/workflows/publish-canary.yml +++ b/.github/workflows/publish-canary.yml @@ -41,4 +41,4 @@ jobs: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} run: | echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc - yarn release:dry + yarn release diff --git a/.storybook/assets/Manrope-VariableFont_wght.ttf b/.storybook/assets/Manrope-VariableFont_wght.ttf new file mode 100644 index 00000000..f39ca39c Binary files /dev/null and b/.storybook/assets/Manrope-VariableFont_wght.ttf differ diff --git a/.storybook/preview.ts b/.storybook/preview.ts index 05ecff97..4cb71bd1 100644 --- a/.storybook/preview.ts +++ b/.storybook/preview.ts @@ -1,5 +1,6 @@ import { WithThemeProvider } from './components/WithThemeProvider.js' +import './styles/storybook.css' import '../styles/global.css' export const parameters = { diff --git a/.storybook/styles/normalize.css b/.storybook/styles/normalize.css new file mode 100644 index 00000000..b6eb8216 --- /dev/null +++ b/.storybook/styles/normalize.css @@ -0,0 +1,349 @@ +/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ + +/* Document + ========================================================================== */ + +/** + * 1. Correct the line height in all browsers. + * 2. Prevent adjustments of font size after orientation changes in iOS. + */ + + html { + line-height: 1.15; /* 1 */ + -webkit-text-size-adjust: 100%; /* 2 */ +} + +/* Sections + ========================================================================== */ + +/** + * Remove the margin in all browsers. + */ + +body { + margin: 0; +} + +/** + * Render the `main` element consistently in IE. + */ + +main { + display: block; +} + +/** + * Correct the font size and margin on `h1` elements within `section` and + * `article` contexts in Chrome, Firefox, and Safari. + */ + +h1 { + font-size: 2em; + margin: 0.67em 0; +} + +/* Grouping content + ========================================================================== */ + +/** + * 1. Add the correct box sizing in Firefox. + * 2. Show the overflow in Edge and IE. + */ + +hr { + box-sizing: content-box; /* 1 */ + height: 0; /* 1 */ + overflow: visible; /* 2 */ +} + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + +pre { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ +} + +/* Text-level semantics + ========================================================================== */ + +/** + * Remove the gray background on active links in IE 10. + */ + +a { + background-color: transparent; +} + +/** + * 1. Remove the bottom border in Chrome 57- + * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. + */ + +abbr[title] { + border-bottom: none; /* 1 */ + text-decoration: underline; /* 2 */ + text-decoration: underline dotted; /* 2 */ +} + +/** + * Add the correct font weight in Chrome, Edge, and Safari. + */ + +b, +strong { + font-weight: bolder; +} + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + +code, +kbd, +samp { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ +} + +/** + * Add the correct font size in all browsers. + */ + +small { + font-size: 80%; +} + +/** + * Prevent `sub` and `sup` elements from affecting the line height in + * all browsers. + */ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +/* Embedded content + ========================================================================== */ + +/** + * Remove the border on images inside links in IE 10. + */ + +img { + border-style: none; +} + +/* Forms + ========================================================================== */ + +/** + * 1. Change the font styles in all browsers. + * 2. Remove the margin in Firefox and Safari. + */ + +button, +input, +optgroup, +select, +textarea { + font-family: inherit; /* 1 */ + font-size: 100%; /* 1 */ + line-height: 1.15; /* 1 */ + margin: 0; /* 2 */ +} + +/** + * Show the overflow in IE. + * 1. Show the overflow in Edge. + */ + +button, +input { /* 1 */ + overflow: visible; +} + +/** + * Remove the inheritance of text transform in Edge, Firefox, and IE. + * 1. Remove the inheritance of text transform in Firefox. + */ + +button, +select { /* 1 */ + text-transform: none; +} + +/** + * Correct the inability to style clickable types in iOS and Safari. + */ + +button, +[type="button"], +[type="reset"], +[type="submit"] { + -webkit-appearance: button; +} + +/** + * Remove the inner border and padding in Firefox. + */ + +button::-moz-focus-inner, +[type="button"]::-moz-focus-inner, +[type="reset"]::-moz-focus-inner, +[type="submit"]::-moz-focus-inner { + border-style: none; + padding: 0; +} + +/** + * Restore the focus styles unset by the previous rule. + */ + +button:-moz-focusring, +[type="button"]:-moz-focusring, +[type="reset"]:-moz-focusring, +[type="submit"]:-moz-focusring { + outline: 1px dotted ButtonText; +} + +/** + * Correct the padding in Firefox. + */ + +fieldset { + padding: 0.35em 0.75em 0.625em; +} + +/** + * 1. Correct the text wrapping in Edge and IE. + * 2. Correct the color inheritance from `fieldset` elements in IE. + * 3. Remove the padding so developers are not caught out when they zero out + * `fieldset` elements in all browsers. + */ + +legend { + box-sizing: border-box; /* 1 */ + color: inherit; /* 2 */ + display: table; /* 1 */ + max-width: 100%; /* 1 */ + padding: 0; /* 3 */ + white-space: normal; /* 1 */ +} + +/** + * Add the correct vertical alignment in Chrome, Firefox, and Opera. + */ + +progress { + vertical-align: baseline; +} + +/** + * Remove the default vertical scrollbar in IE 10+. + */ + +textarea { + overflow: auto; +} + +/** + * 1. Add the correct box sizing in IE 10. + * 2. Remove the padding in IE 10. + */ + +[type="checkbox"], +[type="radio"] { + box-sizing: border-box; /* 1 */ + padding: 0; /* 2 */ +} + +/** + * Correct the cursor style of increment and decrement buttons in Chrome. + */ + +[type="number"]::-webkit-inner-spin-button, +[type="number"]::-webkit-outer-spin-button { + height: auto; +} + +/** + * 1. Correct the odd appearance in Chrome and Safari. + * 2. Correct the outline style in Safari. + */ + +[type="search"] { + -webkit-appearance: textfield; /* 1 */ + outline-offset: -2px; /* 2 */ +} + +/** + * Remove the inner padding in Chrome and Safari on macOS. + */ + +[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +/** + * 1. Correct the inability to style clickable types in iOS and Safari. + * 2. Change font properties to `inherit` in Safari. + */ + +::-webkit-file-upload-button { + -webkit-appearance: button; /* 1 */ + font: inherit; /* 2 */ +} + +/* Interactive + ========================================================================== */ + +/* + * Add the correct display in Edge, IE 10+, and Firefox. + */ + +details { + display: block; +} + +/* + * Add the correct display in all browsers. + */ + +summary { + display: list-item; +} + +/* Misc + ========================================================================== */ + +/** + * Add the correct display in IE 10+. + */ + +template { + display: none; +} + +/** + * Add the correct display in IE 10. + */ + +[hidden] { + display: none; +} \ No newline at end of file diff --git a/.storybook/styles/storybook.css b/.storybook/styles/storybook.css new file mode 100644 index 00000000..27c319b0 --- /dev/null +++ b/.storybook/styles/storybook.css @@ -0,0 +1,20 @@ +/* normalize.css */ +@import url('./normalize.css'); + +@font-face { + font-family: "Manrope"; + src: url("../assets/Manrope-VariableFont_wght.ttf"); +} + +/* generated via https://deploy-preview-15--upbeat-shirley-608546.netlify.app/perfect-ish-font-fallback/?font=Manrope */ +@font-face { + font-family: "Manrope-fallback"; + size-adjust: 102.91000000000001%; + ascent-override: 110%; + src: local("Arial"); +} + +html, +body { + font-family: "Manrope", "Manrope-fallback"; +} \ No newline at end of file diff --git a/README.md b/README.md index f5e0f864..3b14ce46 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,8 @@ Check out our Storybook at [https://ui.lido.fi](https://ui.lido.fi) +For release a new version of the library you need to create a commit with `!` like this - `feat!: UI v4` + ## Breaking Changes `useSystemTheme` hook will no longer return light theme as a fallback when system theme was not identified. A check needs to be added after upgrading. @@ -75,3 +77,12 @@ yarn dev Packages are automatically published to npm when you push to master. The publication is based on [semantic-release](https://github.com/semantic-release/semantic-release) and [@qiwi/multi-semantic-release](https://github.com/qiwi/multi-semantic-release). For correct version detection, please follow the [conventional commit format](https://www.conventionalcommits.org/en/v1.0.0/). + +## Typography styles +To use typography across your application, you need to import the provided CSS styles for typography. +How to Do It: +- Locate Your Main Application File (`_app.tsx`) +- Insert the following line at the top of your main application file to import the typography styles: +```tsx +import '@lidofinance/lido-ui/styles/typography.css'; +``` \ No newline at end of file diff --git a/package.json b/package.json index a6d6478b..e2a874cc 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "icons:convert": "node ./packages/icons/converter/index.cjs" }, "peerDependencies": { + "next": ">=12.0.0 <15.0.0", "react": "16 || 17 || 18", "react-dom": "16 || 17 || 18", "react-is": "16 || 17 || 18" @@ -67,6 +68,7 @@ "@semantic-release/commit-analyzer": "^9.0.2", "@semantic-release/release-notes-generator": "^10.0.3", "@semrel-extra/npm": "^1.2.2", + "@storybook/addon-actions": "^8.4.7", "@storybook/addon-docs": "^7.2.3", "@storybook/addon-essentials": "^7.2.3", "@storybook/addons": "^7.2.3", @@ -108,7 +110,11 @@ "jest-resolve": "^29.0.1", "jest-runner": "^29.0.1", "lint-staged": "11.1.2", + "next": "^14.0.0", "postcss": "^8.4.27", + "postcss-custom-media": "^11.0.5", + "postcss-loader": "^8.1.1", + "postcss-nested": "^7.0.2", "prettier": "^3.0.1", "react": "18.2.0", "react-dom": "18.2.0", diff --git a/packages/block/Block.module.css b/packages/block/Block.module.css deleted file mode 100644 index b0c2e4bb..00000000 --- a/packages/block/Block.module.css +++ /dev/null @@ -1,38 +0,0 @@ -.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 deleted file mode 100644 index f0e296d6..00000000 --- a/packages/block/Block.stories.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { StoryFn, Meta } from '@storybook/react' -import { Block, BlockProps, BlockColor, BlockVariant } from '.' - -const getOptions = (enumObject: Record) => - Object.values(enumObject).filter((value) => typeof value === 'string') - -export default { - component: Block, - title: 'Layout/Block', - args: { - children: 'Example content', - variant: 'flat', - color: 'foreground', - paddingLess: false, - }, - argTypes: { - variant: { - options: getOptions(BlockVariant), - control: 'inline-radio', - }, - color: { - options: getOptions(BlockColor), - control: 'inline-radio', - }, - }, -} satisfies Meta - -export const Basic: StoryFn = (props) => diff --git a/packages/button/Button.module.css b/packages/button/Button.module.css new file mode 100644 index 00000000..789e1af5 --- /dev/null +++ b/packages/button/Button.module.css @@ -0,0 +1,331 @@ +.button { + /* local var */ + --local-icon-padding: 4px; + --local-button-border-width: 0px; + + position: relative; + display: inline-flex; + box-sizing: border-box; + align-items: center; + justify-content: center; + padding: 0 + calc(var(--local-button-padding) - var(--local-button-border-width)); + height: var(--local-button-height); + border: none; + font-family: inherit; + text-decoration: none; + border-radius: var(--lido-border-radius-60); + overflow: hidden; + cursor: pointer; + + &:hover { + .arrow { + transform: translateX(3px); + transition: transform 0.3s; + } + } + + &:disabled { + cursor: not-allowed; + outline: none; + color: var(--lido-color-primary-20); + background-color: var(--lido-color-primary-8); + + &:hover { + color: var(--lido-color-primary-20); + background-color: var(--lido-color-primary-8); + + .arrow { + transform: translateX(0); + } + } + + .icon { + filter: grayscale(1); + opacity: 0.4; + border-color: var(--lido-color-borders-fog); + } + } + + &:not(:disabled):focus-visible { + outline: 2px solid var(--lido-color-borders-water-static); + outline-offset: 1px; + } + + &.size--l .icon, + &.size--xl .icon { + border-radius: 50%; + border: 1px solid var(--lido-color-borders-fog-reverse); + box-sizing: border-box; + } + + &.circle { + --local-icon-size: 40px; + --local-button-size: 40px; + + width: var(--local-button-size); + height: var(--local-button-size); + display: flex; + justify-content: center; + align-items: center; + padding: 0; + } +} + +.content { + flex: 1; + position: relative; + display: inline-flex; + align-items: center; + justify-content: center; + gap: var(--local-gap); +} + +.arrow { + display: block; + width: var(--local-arrow-size); + height: var(--local-arrow-size); + transition: transform 0.3s; + transform: translateX(0); +} + +.spacer { + display: none; + flex: 0 1 calc(var(--local-gap) + var(--local-icon-size)); + min-width: calc( + var(--local-button-spacer-min-width) - var(--local-button-padding) + ); +} + +.withIcon { + justify-content: space-between; + + .iconWrapper { + position: relative; + display: inline-flex; + flex: 0 0 auto; + width: calc(var(--local-icon-size) + var(--local-gap)); + height: var(--local-icon-size); + box-sizing: border-box; + } + + .content { + flex: 1 0 auto; + text-align: center; + } + + .spacer { + display: inline-block; + } + + .icon { + width: var(--local-icon-size); + height: var(--local-icon-size); + } +} + +/* Sizes */ +.size--s, +.size--m { + --local-button-height: 46px; + --local-button-padding: 24px; + --local-arrow-size: 20px; + --local-gap: 14px; + --local-button-spacer-min-width: 24px; + + &.withIcon { + --local-button-padding: 4px; + --local-icon-size: 38px; + } +} + +.size--l { + --local-button-height: 60px; + --local-button-padding: 30px; + --local-arrow-size: 24px; + --local-gap: 20px; + --local-button-spacer-min-width: 32px; + + &.withIcon { + --local-button-padding: 10px; + --local-icon-size: 40px; + } +} + +.size--xl, +.size--xxl { + --local-button-height: 70px; + --local-button-padding: 36px; + --local-arrow-size: 24px; + --local-gap: 20px; + --local-button-spacer-min-width: 32px; + + &.withIcon { + --local-button-padding: 10px; + --local-icon-size: 50px; + } +} + +/* Fonts */ +.normal { + font-size: var(--lido-font-size-control-not-adaptive); + font-weight: var(--lido-font-weight-regular); +} + +.semibold { + font-size: var(--lido-font-size-control-not-adaptive); + font-weight: var(--lido-font-weight-bold); + line-height: var(--lido-line-height-control-not-adaptive); +} + +.subhead { + font-size: var(--lido-font-size-subheader); + font-size: 22px; + font-weight: var(--lido-font-weight-light); + line-height: var(--lido-line-height-subheader); +} + +.description { + font-size: var(--lido-font-size-description); + font-weight: var(--lido-font-weight-regular); + line-height: var(--lido-line-height-description); +} + +/* Colors */ +.default { + color: var(--lido-color-primary-reverse); + font-weight: var(--lido-font-weight-bold); + background-color: var(--lido-color-primary); + transition: background-color 0.5s; + + &:hover { + background-color: var(--lido-color-accent-ocean-static); + transition: background-color 0.3s; + } +} + +.secondary { + color: var(--lido-color-white-static); + background-color: var(--lido-color-accent-ocean-static); + transition: background-color 0.5s; + + &:hover { + background-color: var(--lido-color-accent-sky-static); + transition: background-color 0.3s; + } +} + +.outline { + --local-button-border-width: 1px; + + color: var(--lido-color-primary); + background-color: transparent; + border: 1px solid var(--lido-color-borders-fog); + transition: + color 0.5s, + border-color 0.5s; + + &:hover { + color: var(--lido-color-accent-ocean-static); + border-color: var(--lido-color-accent-ocean-static); + transition: + color 0.3s, + border-color 0.3s; + } + + &:disabled { + color: var(--lido-color-primary-20); + border-color: var(--lido-color-primary-20); + background-color: transparent; + + &:hover { + background-color: transparent; + } + } + + & > * { + font-weight: var(--lido-font-weight-regular); + } +} + +.success { + background-color: var(--lido-color-accent-leaf-static); + color: var(--lido-color-white-static); + transition: background-color 0.5s; +} + +.transparent { + background-color: transparent; + color: var(--lido-color-primary); + outline: none; + + &:hover { + background-color: transparent; + } + + &:disabled { + background-color: transparent; + } +} + +/* Shapes */ +.circle { + --local-button-border-width: 1px; + + border-radius: 50%; + background-color: transparent; + color: var(--lido-color-primary); + border: 1px solid var(--lido-color-borders-fog); + transition: + color 0.5s, + border-color 0.5s; + + &:hover { + background-color: transparent; + color: var(--lido-color-accent-ocean-static); + border-color: var(--lido-color-accent-ocean-static); + transition: + color 0.3s, + border-color 0.3s; + } + + &:disabled { + color: var(--lido-color-primary-20); + border-color: var(--lido-color-primary-20); + background-color: transparent; + + &:hover { + background-color: transparent; + } + } + + /* Cirsle sizes */ + &.circle-s { + --local-button-size: 40px; + --local-icon-size: 38px; + } + + &.circle-m { + --local-button-size: 56px; + --local-icon-size: 46px; + } + + &.circle-l { + --local-button-size: 70px; + --local-icon-size: 58px; + } + + &.circle-xl { + --local-button-size: 86px; + --local-icon-size: 56px; + } + + &.circle-xxl { + --local-button-size: 110px; + --local-icon-size: 70px; + } + + &.transparent { + border: none; + } +} diff --git a/packages/button/Button.stories.tsx b/packages/button/Button.stories.tsx new file mode 100644 index 00000000..da0ba6fa --- /dev/null +++ b/packages/button/Button.stories.tsx @@ -0,0 +1,283 @@ +import { StoryFn, Meta } from '@storybook/react' +import { Button, ButtonProps } from './Button' +import { Eth } from '../icons' + +export default { + component: Button, + title: 'Buttons/Button', + args: { + children: 'Text', + color: 'default', + size: 'l', + shape: 'oval', + textStyle: 'semibold', + disabled: false, + icon: false, + loading: false, + withArrow: false, + loaderVariant: 'transparent', + }, + argTypes: { + onClick: { action: 'clicked' }, + color: { + options: ['default', 'secondary', 'outline', 'success', 'transparent'], + control: { type: 'radio' }, + }, + size: { + options: ['s', 'm', 'l', 'xl', 'xxl'], + control: { type: 'radio' }, + }, + shape: { + options: ['oval', 'circle'], + control: { type: 'radio' }, + }, + textStyle: { + options: ['normal', 'semibold', 'subhead', 'description'], + control: { type: 'radio' }, + }, + loaderVariant: { + options: ['transparent', 'opaque'], + control: { type: 'radio' }, + }, + loading: { + control: { type: 'boolean' }, + }, + icon: { + control: { type: 'boolean' }, + }, + withArrow: { + control: { type: 'boolean' }, + }, + }, + decorators: [ + (Story, context) => { + const icon = context.args.icon ? : null + return + }, + ], + tags: ['autodocs'], +} satisfies Meta + +export const Basic: StoryFn = (props) => + ))} + + +
+

Disabled Buttons

+
+ {colors.map((color) => ( + + ))} +
+
+ +
+

Loading Buttons

+
+ {colors.map((color) => ( + + ))} +
+
+ +
+

Buttons with Icon

+
+ {colors.map((color) => ( + + ))} +
+
+ +
+

Buttons with Arrow

+
+ {colors.map((color) => ( + + ))} +
+
+ +
+

Buttons with Icon and Arrow

+
+ {colors.map((color) => ( + + ))} +
+
+ + {colors.map((color) => ( +
+

Color: {color}

+
+ {sizes.map((size) => ( +
+

Size: {size}

+
+ {shapes.map((shape) => ( +
+
Shape: {shape}
+
+ {textStyles.map((textStyle, index) => ( + <> + {shape === 'circle' && index === 0 && ( + + )} + {shape !== 'circle' && ( + + )} + + ))} +
+
+ ))} +
+
+ ))} +
+
+ ))} + + ) +} + +AllStates.parameters = { + docs: { + description: { + story: 'Displays all possible Button states for easy review.', + }, + }, +} diff --git a/packages/button/Button.tsx b/packages/button/Button.tsx new file mode 100644 index 00000000..ecfcbc9e --- /dev/null +++ b/packages/button/Button.tsx @@ -0,0 +1,142 @@ +import { + cloneElement, + ForwardedRef, + forwardRef, + ComponentPropsWithoutRef, + ReactElement, + RefObject, +} from 'react' +import cn from 'classnames' +import styles from './Button.module.css' +import { DynamicLink } from '../links' +import { ArrowRight } from '../icons' +import { WaveLoader, WaveLoaderVariant } from './waveLoader/WaveLoader' + +export type ButtonProps = ( + | (ComponentPropsWithoutRef<'a'> & { + href: string + }) + | (ComponentPropsWithoutRef<'button'> & { + href?: never + }) +) & { + color?: ButtonColor + shape?: ButtonShape + icon?: ReactElement + withArrow?: boolean + size?: ButtonSize + loading?: boolean + textStyle?: ButtonTextStyle + loaderVariant?: WaveLoaderVariant +} + +export type ButtonSize = 's' | 'm' | 'l' | 'xl' | 'xxl' // "s" and "xxl" sizes are only for Circle shape +export type ButtonTextStyle = 'normal' | 'semibold' | 'subhead' | 'description' +export type ButtonColor = + | 'default' + | 'secondary' + | 'outline' + | 'success' + | 'transparent' +export type ButtonShape = 'oval' | 'circle' + +export const Button = forwardRef( + ( + { + color = 'default', + shape = 'oval', + icon, + href, + withArrow, + size = 'm', + className, + children, + loading, + textStyle = 'semibold', + loaderVariant, + ...rest + }: ButtonProps, + ref?: ForwardedRef, + ) => { + const isCircle = shape === 'circle' + + if (isCircle) { + icon + } + + const content = ( + <> + + {icon && ( + <> + {isCircle ? ( + <> + {cloneElement(icon, { + ...icon.props, + className: cn(styles.icon, icon.props.className), + })} + + ) : ( + + {cloneElement(icon, { + ...icon.props, + className: cn(styles.icon, icon.props.className), + })} + + )} + + )} + + <> + {!isCircle ? ( + <> + + {children} + {withArrow && } + + + + ) : ( + <>{withArrow && } + )} + + + ) + + const props = { + className: cn( + styles.button, + className, + styles[`size--${size}`], + styles[color], + styles[shape], + { + [styles.withIcon]: Boolean(icon), + [styles[`${shape}-${size}`]]: shape === 'circle', + [styles[shape]]: shape === 'circle', + }, + ), + children: content, + ...(rest as object), + } + + if (href) { + return ( + } + href={href} + {...props} + /> + ) + } + + return + ) + }, +) +NavigationButton.displayName = 'NavigationButton' diff --git a/packages/navigation/index.ts b/packages/navigation/index.ts new file mode 100644 index 00000000..f25c564b --- /dev/null +++ b/packages/navigation/index.ts @@ -0,0 +1 @@ +export * from './Navigation' diff --git a/packages/tag/Tag.module.css b/packages/tag/Tag.module.css new file mode 100644 index 00000000..ba30fb22 --- /dev/null +++ b/packages/tag/Tag.module.css @@ -0,0 +1,101 @@ +.tag { + position: relative; + width: fit-content; + display: flex; + justify-content: space-between; + align-items: center; + border-radius: var(--lido-border-radius-60); +} + +.variant--outline { + border: 1px solid var(--lido-color-borders-fog); + + &.color--accent { + border-color: var(--lido-color-borders-water-static); + color: var(--lido-color-accent-ocean-static); + } + + &.color--disabled { + border-color: var(--lido-color-borders-fog); + color: var(--lido-color-primary-20); + } + + &.color--negative { + border-color: var(--lido-color-accent-light-berry-static); + color: var(--lido-color-accent-berry-static); + } + + &.color--warning { + border-color: var(--lido-color-accent-light-coral-static); + color: var(--lido-color-accent-coral-static); + } + + &.color--positive { + border-color: var(--lido-color-accent-light-leaf-static); + color: var(--lido-color-accent-leaf-static); + } +} + +.variant--filled { + background-color: var(--lido-color-primary-6-static); + color: var(--lido-color-primary-72); + border: 1px solid var(--lido-color-borders-fog); + + &.color--accent { + background-color: var(--lido-color-accent-light-ocean-static); + color: var(--lido-color-accent-ocean-static); + border-color: transparent; + } + + &.color--disabled { + background-color: var(--lido-color-primary-5-static); + color: var(--lido-color-primary-20); + } + + &.color--negative { + background-color: var(--lido-color-accent-light-berry-static); + color: var(--lido-color-accent-berry-static); + border-color: transparent; + } + + &.color--warning { + background-color: var(--lido-color-accent-light-coral-static); + color: var(--lido-color-accent-coral-static); + border-color: transparent; + } + + &.color--positive { + background-color: var(--lido-color-accent-light-leaf-static); + color: var(--lido-color-accent-leaf-static); + border-color: transparent; + } +} + +.size--s { + padding: 6.5px 20px; +} + +.size--m { + padding: 10px 30px; +} + +.size--l { + padding: 17px 30px; +} + +.withOnClose { + padding-right: 14px; +} + +.withIcon { + padding-left: 8px; +} + +.icon { + display: flex; + align-items: center; +} + +.closeButton { + cursor: pointer; +} diff --git a/packages/tag/Tag.stories.tsx b/packages/tag/Tag.stories.tsx new file mode 100644 index 00000000..219d4252 --- /dev/null +++ b/packages/tag/Tag.stories.tsx @@ -0,0 +1,134 @@ +import type { Meta, StoryObj } from '@storybook/react' +import { Tag, TagColor, TagSize, TagVariant } from '.' +import { Eth } from '../icons' + +const meta: Meta = { + title: 'Layout/Tag', + component: Tag, + parameters: { + layout: 'centered', + }, + args: { + children: 'Text', + variant: 'outline', + size: 's', + color: 'default', + }, + argTypes: { + variant: { + options: ['outline', 'filled'], + control: { type: 'radio' }, + }, + color: { + options: [ + 'default', + 'accent', + 'disabled', + 'negative', + 'warning', + 'positive', + ], + control: { type: 'radio' }, + }, + size: { + options: ['s', 'm', 'l'], + control: { type: 'radio' }, + }, + icon: { + control: { type: 'boolean' }, + }, + onClose: { + control: { type: 'boolean' }, + }, + children: { + control: { type: 'text' }, + }, + }, + tags: ['autodocs'], +} satisfies Meta + +export default meta + +type Story = StoryObj + +const Icon = () => + +export const Basic: Story = {} + +export const AllStates: Story = { + render: () => { + const variants: TagVariant[] = ['outline', 'filled'] + const colors: TagColor[] = [ + 'default', + 'accent', + 'disabled', + 'negative', + 'warning', + 'positive', + ] + const sizes: TagSize[] = ['s', 'm', 'l'] + const iconOptions: boolean[] = [false, true] + const onCloseOptions: boolean[] = [false, true] + + return ( +
+ {variants.map((variant) => ( +
+

+ Variant: {variant.charAt(0).toUpperCase() + variant.slice(1)} +

+ {colors.map((color) => ( +
+

Color: {color.charAt(0).toUpperCase() + color.slice(1)}

+ {sizes.map((size) => ( +
+

Size: {size.toUpperCase()}

+
+ {iconOptions.map((hasIcon) => + onCloseOptions.map((hasOnClose) => ( + : undefined} + onClose={ + hasOnClose + ? () => { + console.log(1) + } + : undefined + } + > + {hasOnClose + ? 'Closable Tag' + : hasIcon + ? 'Tag with Icon' + : 'Tag'} + + )), + )} +
+
+ ))} +
+ ))} +
+ ))} +
+ ) + }, +} + +AllStates.parameters = { + docs: { + description: { + story: 'Displays all possible Tag states for easy review.', + }, + }, +} diff --git a/packages/tag/Tag.tsx b/packages/tag/Tag.tsx new file mode 100644 index 00000000..dc9bf099 --- /dev/null +++ b/packages/tag/Tag.tsx @@ -0,0 +1,85 @@ +import { + ComponentPropsWithoutRef, + ForwardedRef, + ReactNode, + forwardRef, +} from 'react' +import styles from './Tag.module.css' +import cn from 'classnames' +import { Close } from '../icons' + +export type TagProps = Omit, 'color'> & { + variant?: TagVariant + color?: TagColor + size?: TagSize + icon?: ReactNode + onClose?: () => void +} + +export type TagVariant = 'outline' | 'filled' +export type TagColor = + | 'default' + | 'accent' + | 'disabled' + | 'negative' + | 'warning' + | 'positive' +export type TagSize = 's' | 'm' | 'l' + +export const Tag = forwardRef( + ( + { + variant = 'outline', + color = 'default', + size = 's', + icon, + className, + children, + onClose, + ...rest + }: TagProps, + ref: ForwardedRef, + ) => { + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault() + onClose?.() + } + } + + return ( +
+ {icon} + + {children} + + {Boolean(onClose) && ( + + + + )} +
+ ) + }, +) +Tag.displayName = 'Tag' diff --git a/packages/tag/index.ts b/packages/tag/index.ts new file mode 100644 index 00000000..fd1a1e7e --- /dev/null +++ b/packages/tag/index.ts @@ -0,0 +1 @@ +export * from './Tag' diff --git a/packages/theme-css/block/Block.module.css b/packages/theme-css/block/Block.module.css new file mode 100644 index 00000000..bd22eb77 --- /dev/null +++ b/packages/theme-css/block/Block.module.css @@ -0,0 +1,22 @@ +.foreground { + background: var(--lido-color-foreground); + color: var(--lido-color-text-secondary); +} + +.background { + background: var(--lido-color-background); + color: var(--lido-color-text-secondary); +} + +.flat { + box-shadow: none; +} + +.block { + font-weight: 400; + font-size: var(--lido-font-size-description); + line-height: 1.6em; + border-radius: var(--lido-border-radius-4); + margin: 0; + padding: 10px 4px; +} diff --git a/packages/block/Block.tsx b/packages/theme-css/block/Block.tsx similarity index 73% rename from packages/block/Block.tsx rename to packages/theme-css/block/Block.tsx index edf48c2e..f6e09980 100644 --- a/packages/block/Block.tsx +++ b/packages/theme-css/block/Block.tsx @@ -4,32 +4,23 @@ import styles from './Block.module.css' 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 = ComponentPropsWithoutRef<'div'> & { color?: BlockColors variant?: BlockVariants - paddingLess?: boolean } export const Block = forwardRef( ( - { - color = 'foreground', - variant = 'flat', - paddingLess = false, - className, - ...rest - }: BlockProps, + { color = 'foreground', variant = 'flat', className, ...rest }: BlockProps, ref?: ForwardedRef, ) => { return ( @@ -37,10 +28,7 @@ export const Block = forwardRef( className={cn(styles.block, className, { [styles.foreground]: color === 'foreground', [styles.background]: color === 'background', - [styles.accent]: color === 'accent', [styles.flat]: variant === 'flat', - [styles.shadow]: variant === 'shadow', - [styles.padding]: !paddingLess, })} {...rest} ref={ref} diff --git a/packages/block/index.ts b/packages/theme-css/block/index.ts similarity index 100% rename from packages/block/index.ts rename to packages/theme-css/block/index.ts diff --git a/packages/theme-css/content-theme/content-theme.stories.tsx b/packages/theme-css/content-theme/content-theme.stories.tsx index 96b052d4..9f86e0ca 100644 --- a/packages/theme-css/content-theme/content-theme.stories.tsx +++ b/packages/theme-css/content-theme/content-theme.stories.tsx @@ -1,6 +1,6 @@ import { StoryFn, Meta } from '@storybook/react' -import { Block } from '../../block' import { ContentTheme } from './' +import { Block } from '../block' export default { title: 'Theme/Content Theme', diff --git a/packages/theme-css/theme-provider/theme-provider.stories.tsx b/packages/theme-css/theme-provider/theme-provider.stories.tsx index 0988e341..027bda09 100644 --- a/packages/theme-css/theme-provider/theme-provider.stories.tsx +++ b/packages/theme-css/theme-provider/theme-provider.stories.tsx @@ -1,6 +1,6 @@ import { StoryFn, Meta } from '@storybook/react' import { DarkThemeProvider, LightThemeProvider } from '.' -import { Block } from '../../block' +import { Block } from '../block' export default { title: 'Theme/Providers', diff --git a/packages/theme-css/theme-toggler/theme-toggler.stories.tsx b/packages/theme-css/theme-toggler/theme-toggler.stories.tsx index 7b9cf2bb..48dad96a 100644 --- a/packages/theme-css/theme-toggler/theme-toggler.stories.tsx +++ b/packages/theme-css/theme-toggler/theme-toggler.stories.tsx @@ -1,5 +1,5 @@ import { StoryFn, Meta } from '@storybook/react' -import { Block } from '../../block' +import { Block } from '../block' import { ContentTheme } from '../content-theme' import { ThemeToggler } from './' diff --git a/packages/theme-css/theme-toggler/theme-toggler.tsx b/packages/theme-css/theme-toggler/theme-toggler.tsx index 0a513da2..299d2af0 100644 --- a/packages/theme-css/theme-toggler/theme-toggler.tsx +++ b/packages/theme-css/theme-toggler/theme-toggler.tsx @@ -18,9 +18,6 @@ export const ThemeToggler = forwardRef( return (