diff --git a/CHANGELOG.md b/CHANGELOG.md index e14f0bc..501d608 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,33 @@ --> +## 0.1.0-alpha.2 (12 February, 2021) + +### Features + +- Added `ModalCloseButton`, `ModalHeader`, `ModalBody` and `ModalFooter` components +- Added `Container` component +- Added `Image` component +- Added `Badge` component +- Added `Code` component +- Added `Kbd` component +- Added `Tag` component + +### Changes + +- Added color types for `Alert`, `Button`, `Checkbox`, `Link` +- Adjusted Alert text color +- Added export for theme color scales +- Updated and added additional prop documentation +- Moved icon button base style into theme +- The close button's icon can now be overridden +- Button line-height changed from `1` to `1.2` + +### Bug Fixes + +- Fixed modal positioning +- Fixed typos in types + ## 0.1.0-alpha.1 (8 February, 2021) ### Features diff --git a/package-lock.json b/package-lock.json index 4149c47..e0dc4ab 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@spicy-ui/core", - "version": "0.1.0-alpha.1", + "version": "0.1.0-alpha.2", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -4204,13 +4204,13 @@ "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "4.14.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.14.2.tgz", - "integrity": "sha512-uMGfG7GFYK/nYutK/iqYJv6K/Xuog/vrRRZX9aEP4Zv1jsYXuvFUMDFLhUnc8WFv3D2R5QhNQL3VYKmvLS5zsQ==", + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.15.0.tgz", + "integrity": "sha512-DJgdGZW+8CFUTz5C/dnn4ONcUm2h2T0itWD85Ob5/V27Ndie8hUoX5HKyGssvR8sUMkAIlUc/AMK67Lqa3kBIQ==", "dev": true, "requires": { - "@typescript-eslint/experimental-utils": "4.14.2", - "@typescript-eslint/scope-manager": "4.14.2", + "@typescript-eslint/experimental-utils": "4.15.0", + "@typescript-eslint/scope-manager": "4.15.0", "debug": "^4.1.1", "functional-red-black-tree": "^1.0.1", "lodash": "^4.17.15", @@ -4219,6 +4219,135 @@ "tsutils": "^3.17.1" }, "dependencies": { + "@nodelib/fs.stat": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz", + "integrity": "sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q==", + "dev": true + }, + "@typescript-eslint/experimental-utils": { + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.15.0.tgz", + "integrity": "sha512-V4vaDWvxA2zgesg4KPgEGiomWEBpJXvY4ZX34Y3qxK8LUm5I87L+qGIOTd9tHZOARXNRt9pLbblSKiYBlGMawg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.3", + "@typescript-eslint/scope-manager": "4.15.0", + "@typescript-eslint/types": "4.15.0", + "@typescript-eslint/typescript-estree": "4.15.0", + "eslint-scope": "^5.0.0", + "eslint-utils": "^2.0.0" + } + }, + "@typescript-eslint/scope-manager": { + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.15.0.tgz", + "integrity": "sha512-CSNBZnCC2jEA/a+pR9Ljh8Y+5TY5qgbPz7ICEk9WCpSEgT6Pi7H2RIjxfrrbUXvotd6ta+i27sssKEH8Azm75g==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.15.0", + "@typescript-eslint/visitor-keys": "4.15.0" + } + }, + "@typescript-eslint/types": { + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.15.0.tgz", + "integrity": "sha512-su4RHkJhS+iFwyqyXHcS8EGPlUVoC+XREfy5daivjLur9JP8GhvTmDipuRpcujtGC4M+GYhUOJCPDE3rC5NJrg==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.15.0.tgz", + "integrity": "sha512-jG6xTmcNbi6xzZq0SdWh7wQ9cMb2pqXaUp6bUZOMsIlu5aOlxGxgE/t6L/gPybybQGvdguajXGkZKSndZJpksA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.15.0", + "@typescript-eslint/visitor-keys": "4.15.0", + "debug": "^4.1.1", + "globby": "^11.0.1", + "is-glob": "^4.0.1", + "semver": "^7.3.2", + "tsutils": "^3.17.1" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.15.0.tgz", + "integrity": "sha512-RnDtJwOwFucWFAMjG3ghCG/ikImFJFEg20DI7mn4pHEx3vC48lIAoyjhffvfHmErRDboUPC7p9Z2il4CLb7qxA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.15.0", + "eslint-visitor-keys": "^2.0.0" + } + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "fast-glob": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz", + "integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.0", + "merge2": "^1.3.0", + "micromatch": "^4.0.2", + "picomatch": "^2.2.1" + } + }, + "glob-parent": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "globby": { + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.2.tgz", + "integrity": "sha512-2ZThXDvvV8fYFRVIxnrMQBipZQDr7MxKAmQK1vujaj9/7eF0efG7BPUKJ7jP7G5SLF37xKDXvO4S/KKLj/Z0og==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" + } + }, + "ignore": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "dev": true + }, "semver": { "version": "7.3.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", @@ -4257,15 +4386,131 @@ } }, "@typescript-eslint/parser": { - "version": "4.14.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.14.2.tgz", - "integrity": "sha512-ipqSP6EuUsMu3E10EZIApOJgWSpcNXeKZaFeNKQyzqxnQl8eQCbV+TSNsl+s2GViX2d18m1rq3CWgnpOxDPgHg==", + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.15.0.tgz", + "integrity": "sha512-L6Dtbq8Bc7g2aZwnIBETpmUa9XDKCMzKVwAArnGp5Mn7PRNFjf3mUzq8UeBjL3K8t311hvevnyqXAMSmxO8Gpg==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "4.14.2", - "@typescript-eslint/types": "4.14.2", - "@typescript-eslint/typescript-estree": "4.14.2", + "@typescript-eslint/scope-manager": "4.15.0", + "@typescript-eslint/types": "4.15.0", + "@typescript-eslint/typescript-estree": "4.15.0", "debug": "^4.1.1" + }, + "dependencies": { + "@nodelib/fs.stat": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz", + "integrity": "sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q==", + "dev": true + }, + "@typescript-eslint/scope-manager": { + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.15.0.tgz", + "integrity": "sha512-CSNBZnCC2jEA/a+pR9Ljh8Y+5TY5qgbPz7ICEk9WCpSEgT6Pi7H2RIjxfrrbUXvotd6ta+i27sssKEH8Azm75g==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.15.0", + "@typescript-eslint/visitor-keys": "4.15.0" + } + }, + "@typescript-eslint/types": { + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.15.0.tgz", + "integrity": "sha512-su4RHkJhS+iFwyqyXHcS8EGPlUVoC+XREfy5daivjLur9JP8GhvTmDipuRpcujtGC4M+GYhUOJCPDE3rC5NJrg==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.15.0.tgz", + "integrity": "sha512-jG6xTmcNbi6xzZq0SdWh7wQ9cMb2pqXaUp6bUZOMsIlu5aOlxGxgE/t6L/gPybybQGvdguajXGkZKSndZJpksA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.15.0", + "@typescript-eslint/visitor-keys": "4.15.0", + "debug": "^4.1.1", + "globby": "^11.0.1", + "is-glob": "^4.0.1", + "semver": "^7.3.2", + "tsutils": "^3.17.1" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.15.0.tgz", + "integrity": "sha512-RnDtJwOwFucWFAMjG3ghCG/ikImFJFEg20DI7mn4pHEx3vC48lIAoyjhffvfHmErRDboUPC7p9Z2il4CLb7qxA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.15.0", + "eslint-visitor-keys": "^2.0.0" + } + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, + "fast-glob": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz", + "integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.0", + "merge2": "^1.3.0", + "micromatch": "^4.0.2", + "picomatch": "^2.2.1" + } + }, + "glob-parent": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "globby": { + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.2.tgz", + "integrity": "sha512-2ZThXDvvV8fYFRVIxnrMQBipZQDr7MxKAmQK1vujaj9/7eF0efG7BPUKJ7jP7G5SLF37xKDXvO4S/KKLj/Z0og==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" + } + }, + "ignore": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "dev": true + }, + "semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } } }, "@typescript-eslint/scope-manager": { @@ -17070,6 +17315,12 @@ "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==", "dev": true }, + "react-lorem-ipsum": { + "version": "1.4.9", + "resolved": "https://registry.npmjs.org/react-lorem-ipsum/-/react-lorem-ipsum-1.4.9.tgz", + "integrity": "sha512-maYf2KTltui/Fnxrq2ttoKnpIm9JO6j5aYeNvCjEPCKGVMJwWNFL75Nb9hgXnMQGHTHSR4KBSIUnCPD3CzSh3g==", + "dev": true + }, "react-popper": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-2.2.4.tgz", @@ -20270,9 +20521,9 @@ } }, "typescript": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.3.tgz", - "integrity": "sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.5.tgz", + "integrity": "sha512-6OSu9PTIzmn9TCDiovULTnET6BgXtDYL4Gg4szY+cGsc3JP1dQL8qvE8kShTRx1NIw4Q9IBHlwODjkjWEtMUyA==", "dev": true }, "ua-parser-js": { diff --git a/package.json b/package.json index b5b8520..57161ad 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@spicy-ui/core", - "version": "0.1.0-alpha.1", + "version": "0.1.0-alpha.2", "description": "A themable and extensible React UI library, ready to use out of the box", "keywords": [ "react", @@ -61,8 +61,8 @@ "@storybook/react": "^6.1.17", "@types/react": "^17.0.1", "@types/react-dom": "^17.0.0", - "@typescript-eslint/eslint-plugin": "^4.14.2", - "@typescript-eslint/parser": "^4.14.2", + "@typescript-eslint/eslint-plugin": "^4.15.0", + "@typescript-eslint/parser": "^4.15.0", "eslint": "^7.19.0", "eslint-config-airbnb-typescript": "^12.3.1", "eslint-plugin-jest": "^24.1.3", @@ -71,12 +71,13 @@ "react": "^17.0.1", "react-dom": "^17.0.1", "react-json-view": "^1.21.1", + "react-lorem-ipsum": "^1.4.9", "react-uid": "^2.3.1", "rimraf": "^3.0.2", "styled-components": "^5.2.1", "tsdx": "^0.14.1", "tslib": "^2.1.0", - "typescript": "^4.1.3" + "typescript": "^4.1.5" }, "peerDependencies": { "react": ">=17.0.0", diff --git a/src/components/Alert/Alert.stories.tsx b/src/components/Alert/Alert.stories.tsx index 5da7743..06a748b 100644 --- a/src/components/Alert/Alert.stories.tsx +++ b/src/components/Alert/Alert.stories.tsx @@ -8,7 +8,11 @@ export default { component: Alert, } as Meta; -export const Simple: Story = (props) => A alert; +export const Simple: Story = (props) => ( + + This is a alert + +); export const Colors: Story = () => ( diff --git a/src/components/Alert/Alert.tsx b/src/components/Alert/Alert.tsx index 21f6a22..85bdc23 100644 --- a/src/components/Alert/Alert.tsx +++ b/src/components/Alert/Alert.tsx @@ -1,10 +1,15 @@ import * as React from 'react'; import { SxProps, useComponentStyles } from '../../system'; +import { ColorScales } from '../../theme'; +import { LiteralUnion } from '../../types'; import { Box } from '../Box'; +export type AlertColors = ColorScales; + export interface AlertProps extends SxProps { children?: React.ReactNode; - color?: string; + /** Color of the alert. */ + color?: LiteralUnion; } export const Alert = React.forwardRef((props, ref) => { diff --git a/src/components/Avatar/Avatar.tsx b/src/components/Avatar/Avatar.tsx index 8d21234..45e5c55 100644 --- a/src/components/Avatar/Avatar.tsx +++ b/src/components/Avatar/Avatar.tsx @@ -44,15 +44,15 @@ export interface AvatarProps extends SxProps { getInitials?: (name: string) => string; /** The avatars fallback icon when the src is not loaded or specified. */ icon?: React.ReactElement; - /** Name of the person thee avatar represents. */ + /** Name of the person the avatar represents. */ name?: string; /** If `true` the Avatar will show a border around it. */ showBorder?: boolean; - /** Avatar size. */ - size?: LiteralUnion; /** Image url of the Avatar. */ src?: string; - /** Avatar variant. */ + /** Size of the avatar. */ + size?: LiteralUnion; + /** Variant of the avatar. */ variant?: LiteralUnion; } diff --git a/src/components/Avatar/AvatarGroup.tsx b/src/components/Avatar/AvatarGroup.tsx index 90c42e2..c42a397 100644 --- a/src/components/Avatar/AvatarGroup.tsx +++ b/src/components/Avatar/AvatarGroup.tsx @@ -1,3 +1,4 @@ +import { SpaceProps } from '@spicy-ui/styled-system'; import * as React from 'react'; import { SxProps, useComponentStyles } from '../../system'; import { Box } from '../Box'; @@ -9,10 +10,10 @@ export interface AvatarGroupProps extends SxProps { borderColor?: string; /** Maximum number of avatars to show. */ max?: number; + /** Spacing between avatars. */ + spacing?: SpaceProps['margin']; /** Size of all avatars. */ size?: AvatarProps['size']; - /** Spacing between avatars. */ - spacing?: string; /** Variant of all avatars. */ variant?: AvatarProps['variant']; } @@ -39,16 +40,18 @@ export const AvatarGroup = React.forwardRef((p +{overflow} )} - {reversedVisible.map((avatar, index) => { - const isFirst = index === 0; - return React.cloneElement(avatar, { - mr: isFirst ? 0 : spacing, + {reversedVisible.map((avatar, index) => + React.cloneElement(avatar, { + showBorder: true, + sx: { + mr: index === 0 ? 0 : spacing, + ...(borderColor ? { borderColor } : {}), + ...avatar.props.sx, + }, size, variant, - sx: { ...(borderColor ? { borderColor } : {}), ...avatar.props.sx }, - showBorder: true, - }); - })} + }), + )} ); }); diff --git a/src/components/Badge/Badge.stories.tsx b/src/components/Badge/Badge.stories.tsx new file mode 100644 index 0000000..a9bd739 --- /dev/null +++ b/src/components/Badge/Badge.stories.tsx @@ -0,0 +1,72 @@ +import { Meta, Story } from '@storybook/react'; +import * as React from 'react'; +import { uid } from 'react-uid'; +import { Badge, BadgeProps, BadgeVariants } from '..'; +import { BadgeColors } from './Badge'; + +const badgeColors: BadgeColors[] = [ + 'blueGray', + 'coolGray', + 'gray', + 'trueGray', + 'warmGray', + 'red', + 'orange', + 'amber', + 'yellow', + 'lime', + 'green', + 'emerald', + 'teal', + 'cyan', + 'lightBlue', + 'blue', + 'indigo', + 'violet', + 'purple', + 'fuchsia', + 'pink', + 'rose', + 'whiteAlpha', + 'blackAlpha', +]; + +const badgeVariants: BadgeVariants[] = ['outline', 'solid', 'subtle']; + +export default { + title: 'Badge', + component: Badge, +} as Meta; + +export const Simple: Story = (props) => Success; + +export const Colors: Story = (props) => ( + + + + + {badgeVariants.map((variant, idx) => ( + + ))} + + + + {badgeColors.map((color, idx) => ( + + + {badgeVariants.map((variant, idy) => ( + + ))} + + ))} + +
  + {variant} +
+ {color} + + + Badge + +
+); diff --git a/src/components/Badge/Badge.tsx b/src/components/Badge/Badge.tsx new file mode 100644 index 0000000..62b9869 --- /dev/null +++ b/src/components/Badge/Badge.tsx @@ -0,0 +1,37 @@ +import * as React from 'react'; +import { SxProps, useComponentStyles } from '../../system'; +import { ColorScales } from '../../theme'; +import { LiteralUnion } from '../../types'; +import { Box } from '../Box'; + +export type BadgeColors = ColorScales; + +export type BadgeVariants = 'outline' | 'solid' | 'subtle'; + +export interface BadgeProps extends SxProps { + children?: React.ReactNode; + as?: string | React.ComponentType; + /** Color of the badge. */ + color?: LiteralUnion; + /** Variant of the badge. */ + variant?: LiteralUnion; +} + +export const Badge = React.forwardRef((props, ref) => { + const { children, sx, as, color, variant, ...rest } = props; + + const styles = useComponentStyles('Badge', props); + + return ( + + {children} + + ); +}); + +Badge.defaultProps = { + color: 'gray', + variant: 'subtle', +}; + +Badge.displayName = 'Badge'; diff --git a/src/components/Badge/index.ts b/src/components/Badge/index.ts new file mode 100644 index 0000000..9c8edca --- /dev/null +++ b/src/components/Badge/index.ts @@ -0,0 +1 @@ +export * from './Badge'; diff --git a/src/components/Button/Button.stories.tsx b/src/components/Button/Button.stories.tsx index c762d05..b8c6a10 100644 --- a/src/components/Button/Button.stories.tsx +++ b/src/components/Button/Button.stories.tsx @@ -3,9 +3,9 @@ import * as React from 'react'; import { HiOutlineChat, HiOutlineCog } from 'react-icons/hi'; import { uid } from 'react-uid'; import { useTheme } from 'styled-components'; -import { Button, ButtonProps, ButtonVariants, Stack } from '..'; +import { Button, ButtonProps, ButtonColors, ButtonVariants, Stack } from '..'; -const buttonColors = [ +const buttonColors: ButtonColors[] = [ 'blueGray', 'coolGray', 'gray', @@ -40,7 +40,6 @@ export default { } as Meta; export const Simple: Story = (props) => ; -Simple.args = { color: 'blue' }; export const Colors: Story = (props) => ( diff --git a/src/components/Button/Button.tsx b/src/components/Button/Button.tsx index f9edd31..d4a9af3 100644 --- a/src/components/Button/Button.tsx +++ b/src/components/Button/Button.tsx @@ -1,6 +1,7 @@ import { SpaceProps } from '@spicy-ui/styled-system'; import * as React from 'react'; import { SxProps, useComponentStyles } from '../../system'; +import { ColorScales } from '../../theme'; import { LiteralUnion } from '../../types'; import { Box } from '../Box'; import { Spinner } from '../Spinner'; @@ -24,6 +25,8 @@ const iconStyles = { }, }; +export type ButtonColors = ColorScales; + export type ButtonSizes = 'xs' | 'sm' | 'md' | 'lg'; export type ButtonVariants = 'filled' | 'outlined' | 'ghost' | 'link' | 'unstyled'; @@ -32,7 +35,7 @@ export interface ButtonProps extends React.ButtonHTMLAttributes; /** Space between the button icon and label. */ - iconSpacing?: SpaceProps['margin']; + iconSpacing?: string; /** Icon shown before the button's label. */ iconBefore?: React.ReactElement; /** Icon shown after the button's label. */ @@ -51,8 +54,8 @@ export interface ButtonProps extends React.ButtonHTMLAttributes; /** Size of the button. */ size?: LiteralUnion; /** Variant style of the button. */ @@ -94,7 +97,7 @@ export const Button = React.forwardRef((props, r {...rest} > {iconBefore && !isLoading && ( - + {iconBefore} )} @@ -115,7 +118,7 @@ export const Button = React.forwardRef((props, r )} {iconAfter && !isLoading && ( - + {iconAfter} )} diff --git a/src/components/Button/IconButton.tsx b/src/components/Button/IconButton.tsx index e940566..121ae04 100644 --- a/src/components/Button/IconButton.tsx +++ b/src/components/Button/IconButton.tsx @@ -1,17 +1,22 @@ import * as React from 'react'; +import { useComponentStyles } from '../../system'; import { Button, ButtonProps } from './Button'; export interface IconButtonProps extends Omit { + /** Icon shown inside the button. */ icon?: React.ReactElement; + /** If `true`, the icon button will be rounded. */ isRound?: boolean; } export const IconButton = React.forwardRef((props, ref) => { - const { sx, children, icon, isRound, ...rest } = props; + const { children, sx, icon, isRound, ...rest } = props; + + const styles = useComponentStyles('IconButton', props); return ( - ); diff --git a/src/components/Checkbox/Checkbox.stories.tsx b/src/components/Checkbox/Checkbox.stories.tsx index 5cf5394..f7d05d0 100644 --- a/src/components/Checkbox/Checkbox.stories.tsx +++ b/src/components/Checkbox/Checkbox.stories.tsx @@ -1,9 +1,9 @@ import { Meta, Story } from '@storybook/react'; import * as React from 'react'; import { uid } from 'react-uid'; -import { Checkbox, CheckboxProps, Stack } from '..'; +import { Checkbox, CheckboxColors, CheckboxProps, Stack } from '..'; -const checkboxColors = [ +const checkboxColors: CheckboxColors[] = [ 'blueGray', 'coolGray', 'gray', diff --git a/src/components/Checkbox/Checkbox.tsx b/src/components/Checkbox/Checkbox.tsx index b08b349..c2e8075 100644 --- a/src/components/Checkbox/Checkbox.tsx +++ b/src/components/Checkbox/Checkbox.tsx @@ -1,10 +1,13 @@ import * as React from 'react'; import { useComponentStyles } from '../../system'; +import { ColorScales } from '../../theme'; import { LiteralUnion } from '../../types'; import { Box } from '../Box'; import { Text } from '../Text'; -type CheckboxSizes = 'xs' | 'sm' | 'md' | 'lg'; +export type CheckboxColors = ColorScales; + +export type CheckboxSizes = 'xs' | 'sm' | 'md' | 'lg'; export interface CheckboxProps extends Omit, 'width' | 'height' | 'size'> { /** Checkbox label. */ @@ -13,8 +16,8 @@ export interface CheckboxProps extends Omit; /** Size of the checkbox. */ size?: LiteralUnion; } diff --git a/src/components/CloseButton/CloseButton.tsx b/src/components/CloseButton/CloseButton.tsx index 9e0af5b..b08cc58 100644 --- a/src/components/CloseButton/CloseButton.tsx +++ b/src/components/CloseButton/CloseButton.tsx @@ -2,10 +2,15 @@ import * as React from 'react'; import { HiX } from 'react-icons/hi'; import { IconButton, IconButtonProps } from '../Button'; -export interface CloseButtonProps extends IconButtonProps {} +export interface CloseButtonProps extends IconButtonProps { + /** Icon shown inside the close button. */ + icon?: React.ReactElement; +} -export const CloseButton = React.forwardRef((props, ref) => ( - } variant="ghost" color="blackAlpha" {...props} /> -)); +export const CloseButton = React.forwardRef((props, ref) => { + const { icon = , ...rest } = props; + + return ; +}); CloseButton.displayName = 'CloseButton'; diff --git a/src/components/Code/Code.stories.tsx b/src/components/Code/Code.stories.tsx new file mode 100644 index 0000000..f5bc47a --- /dev/null +++ b/src/components/Code/Code.stories.tsx @@ -0,0 +1,72 @@ +import { Meta, Story } from '@storybook/react'; +import * as React from 'react'; +import { uid } from 'react-uid'; +import { Code, CodeProps } from '..'; +import { CodeColors, CodeVariants } from './Code'; + +const codeColors: CodeColors[] = [ + 'blueGray', + 'coolGray', + 'gray', + 'trueGray', + 'warmGray', + 'red', + 'orange', + 'amber', + 'yellow', + 'lime', + 'green', + 'emerald', + 'teal', + 'cyan', + 'lightBlue', + 'blue', + 'indigo', + 'violet', + 'purple', + 'fuchsia', + 'pink', + 'rose', + 'whiteAlpha', + 'blackAlpha', +]; + +const codeVariants: CodeVariants[] = ['outline', 'solid', 'subtle']; + +export default { + title: 'Code', + component: Code, +} as Meta; + +export const Simple: Story = (props) => console.log('hello world!'); + +export const Colors: Story = (props) => ( +
+ + + + {codeVariants.map((variant, idx) => ( + + ))} + + + + {codeColors.map((color, idx) => ( + + + {codeVariants.map((variant, idy) => ( + + ))} + + ))} + +
  + {variant} +
+ {color} + + + console.log('hello world!') + +
+); diff --git a/src/components/Code/Code.tsx b/src/components/Code/Code.tsx new file mode 100644 index 0000000..447d42f --- /dev/null +++ b/src/components/Code/Code.tsx @@ -0,0 +1,37 @@ +import * as React from 'react'; +import { SxProps, useComponentStyles } from '../../system'; +import { ColorScales } from '../../theme'; +import { LiteralUnion } from '../../types'; +import { Box } from '../Box'; + +export type CodeColors = ColorScales; + +export type CodeVariants = 'outline' | 'solid' | 'subtle'; + +export interface CodeProps extends SxProps { + children?: React.ReactNode; + as?: string | React.ComponentType; + /** Color of the code element. */ + color?: LiteralUnion; + /** Variant of the code element. */ + variant?: LiteralUnion; +} + +export const Code = React.forwardRef((props, ref) => { + const { children, sx, as, color, variant, ...rest } = props; + + const styles = useComponentStyles('Code', props); + + return ( + + {children} + + ); +}); + +Code.defaultProps = { + color: 'gray', + variant: 'subtle', +}; + +Code.displayName = 'Code'; diff --git a/src/components/Code/index.ts b/src/components/Code/index.ts new file mode 100644 index 0000000..c3aa944 --- /dev/null +++ b/src/components/Code/index.ts @@ -0,0 +1 @@ +export * from './Code'; diff --git a/src/components/Container/Container.stories.tsx b/src/components/Container/Container.stories.tsx new file mode 100644 index 0000000..16ad5fe --- /dev/null +++ b/src/components/Container/Container.stories.tsx @@ -0,0 +1,25 @@ +import { Meta, Story } from '@storybook/react'; +import * as React from 'react'; +import { Container, ContainerProps, Box } from '..'; + +export default { + title: 'Container', + component: Container, +} as Meta; + +export const Simple: Story = (props) => ( + + + Container inner + + +); + +export const Centered: Story = (props) => ( + + + Container inner + + +); +Centered.args = { isCentered: true }; diff --git a/src/components/Container/Container.tsx b/src/components/Container/Container.tsx new file mode 100644 index 0000000..dc1d601 --- /dev/null +++ b/src/components/Container/Container.tsx @@ -0,0 +1,24 @@ +import * as React from 'react'; +import { useComponentStyles } from '../../system'; +import { Box, BoxProps } from '../Box'; + +export interface ContainerProps extends BoxProps { + children?: React.ReactNode; + as?: string | React.ComponentType; + /** If `true`, container will center its children. */ + isCentered?: boolean; +} + +export const Container = React.forwardRef((props, ref) => { + const { sx, as, children, isCentered, ...rest } = props; + + const styles = useComponentStyles('Container', props); + + return ( + + {children} + + ); +}); + +Container.displayName = 'Container'; diff --git a/src/components/Container/index.ts b/src/components/Container/index.ts new file mode 100644 index 0000000..8a1103f --- /dev/null +++ b/src/components/Container/index.ts @@ -0,0 +1 @@ +export * from './Container'; diff --git a/src/components/Heading/Heading.tsx b/src/components/Heading/Heading.tsx index faf4e26..e5f7ca2 100644 --- a/src/components/Heading/Heading.tsx +++ b/src/components/Heading/Heading.tsx @@ -7,7 +7,6 @@ export type HeadingVariant = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'; export interface HeadingProps extends AllSystemProps, SxProps { color?: string; - /** Stack children. */ children?: React.ReactNode; /** Text variant. */ variant?: LiteralUnion; diff --git a/src/components/Image/Image.stories.tsx b/src/components/Image/Image.stories.tsx new file mode 100644 index 0000000..ac6ae58 --- /dev/null +++ b/src/components/Image/Image.stories.tsx @@ -0,0 +1,27 @@ +import { Meta, Story } from '@storybook/react'; +import * as React from 'react'; +import { Image, ImageProps, Spinner } from '..'; + +export default { + title: 'Image', + component: Image, +} as Meta; + +export const Simple: Story = (props) => ; +Simple.args = { + src: 'https://source.unsplash.com/random/256x256?mountain', + alt: 'Random unsplash image', +}; + +export const Fallback: Story = (props) => ; +Fallback.args = { + src: 'https://source.unsplash.com/random/256x256?city', + fallbackSrc: 'https://via.placeholder.com/256', + alt: 'Random unsplash image', +}; + +export const FallbackComponent: Story = (props) => } />; +FallbackComponent.args = { + src: 'https://source.unsplash.com/random/256x256?forest', + alt: 'Random unsplash image', +}; diff --git a/src/components/Image/Image.tsx b/src/components/Image/Image.tsx new file mode 100644 index 0000000..2cb4a1b --- /dev/null +++ b/src/components/Image/Image.tsx @@ -0,0 +1,39 @@ +import * as React from 'react'; +import { useImage } from '../../hooks/useImage'; +import { SxProps, useComponentStyles } from '../../system'; +import { Box } from '../Box'; + +export interface ImageProps extends React.ImgHTMLAttributes, SxProps { + /** Fallback component for the image. This is shown when the image is loading. */ + fallback?: React.ReactElement; + /** Fallback src image. If you intend to use this instead of a component, it's advised to use a data src. */ + fallbackSrc?: string; + /** Image src. */ + src?: string; +} + +export const Image = React.forwardRef((props, ref) => { + const { sx, crossOrigin, fallback, fallbackSrc, src, ...rest } = props; + + const { status } = useImage(src, crossOrigin); + + const styles = useComponentStyles('Image', props); + + const shared = { + ref, + sx: styles, + ...rest, + }; + + if (status !== 'loaded') { + if (fallback) { + return fallback; + } + + return ; + } + + return ; +}); + +Image.displayName = 'Image'; diff --git a/src/components/Image/index.ts b/src/components/Image/index.ts new file mode 100644 index 0000000..4bbac90 --- /dev/null +++ b/src/components/Image/index.ts @@ -0,0 +1 @@ +export * from './Image'; diff --git a/src/components/Kbd/Kbd.stories.tsx b/src/components/Kbd/Kbd.stories.tsx new file mode 100644 index 0000000..d5116f7 --- /dev/null +++ b/src/components/Kbd/Kbd.stories.tsx @@ -0,0 +1,14 @@ +import { Meta, Story } from '@storybook/react'; +import * as React from 'react'; +import { Text, Kbd, KbdProps } from '..'; + +export default { + title: 'Kbd', + component: Kbd, +} as Meta; + +export const Simple: Story = (props) => ( + + ctrl + shift + v + +); diff --git a/src/components/Kbd/Kbd.tsx b/src/components/Kbd/Kbd.tsx new file mode 100644 index 0000000..10fe284 --- /dev/null +++ b/src/components/Kbd/Kbd.tsx @@ -0,0 +1,22 @@ +import * as React from 'react'; +import { SxProps, useComponentStyles } from '../../system'; +import { Box } from '../Box'; + +export interface KbdProps extends SxProps { + children?: React.ReactNode; + as?: string | React.ComponentType; +} + +export const Kbd = React.forwardRef((props, ref) => { + const { children, sx, as, ...rest } = props; + + const styles = useComponentStyles('Kbd', props); + + return ( + + {children} + + ); +}); + +Kbd.displayName = 'Kbd'; diff --git a/src/components/Kbd/index.ts b/src/components/Kbd/index.ts new file mode 100644 index 0000000..904df7e --- /dev/null +++ b/src/components/Kbd/index.ts @@ -0,0 +1 @@ +export * from './Kbd'; diff --git a/src/components/Link/Link.tsx b/src/components/Link/Link.tsx index a289466..2e2d2e2 100644 --- a/src/components/Link/Link.tsx +++ b/src/components/Link/Link.tsx @@ -2,14 +2,18 @@ import { sfp } from '@spicy-ui/styled-system'; import * as React from 'react'; import styled from 'styled-components'; import { getComponentStyles, sxMixin, SxProps } from '../../system'; +import { ColorScales } from '../../theme'; +import { LiteralUnion } from '../../types'; + +export type LinkColors = ColorScales; export type LinkUnderlineBehaviour = 'default' | 'none' | 'hover'; export interface LinkProps extends SxProps, Partial> { /** Color of the link. */ - color?: string; + color?: LiteralUnion; /** Hover color of the link. */ - hoverColor?: string; + hoverColor?: LiteralUnion; /** Set to `true` to disable the link. */ isDisabled?: boolean; /** Set to `true` to add external `rel` tags. */ diff --git a/src/components/Modal/Modal.stories.tsx b/src/components/Modal/Modal.stories.tsx index 5148a01..21e6a3a 100644 --- a/src/components/Modal/Modal.stories.tsx +++ b/src/components/Modal/Modal.stories.tsx @@ -1,10 +1,15 @@ import { Meta, Story } from '@storybook/react'; import * as React from 'react'; -import { Box, Button, Modal, ModalProps } from '..'; +import { loremIpsum } from 'react-lorem-ipsum'; +import { uid } from 'react-uid'; +import { Button, Modal, ModalBody, ModalCloseButton, ModalFooter, ModalHeader, ModalProps, Stack, Text } from '..'; + +const lorem = loremIpsum({ p: 2 }).map((str, i) => {str}); export default { title: 'Modal', component: Modal, + subcomponents: { ModalHeader, ModalBody, ModalFooter, ModalCloseButton }, } as Meta; export const Simple: Story = (props) => { @@ -15,11 +20,45 @@ export const Simple: Story = (props) => { setIsOpen(false)}> - - - + setIsOpen(false)} /> + Modal header + + {lorem} + + + + + ); }; Simple.args = { closeOnOverlayClick: true }; + +export const AsAlertDialog: Story = (props) => { + const [isOpen, setIsOpen] = React.useState(false); + + const cancelRef = React.useRef(null); + + return ( + <> + + + setIsOpen(false)}> + Delete product + Are you sure you want to delete this product? + + + + + + + ); +}; +AsAlertDialog.args = { closeOnOverlayClick: false }; diff --git a/src/components/Modal/ModalBody.tsx b/src/components/Modal/ModalBody.tsx new file mode 100644 index 0000000..49389c1 --- /dev/null +++ b/src/components/Modal/ModalBody.tsx @@ -0,0 +1,19 @@ +import * as React from 'react'; +import { SxProps, useComponentStyles } from '../../system'; +import { Box } from '../Box'; + +export interface ModalBodyProps extends SxProps {} + +export const ModalBody: React.FC = (props) => { + const { sx, children, ...rest } = props; + + const styles = useComponentStyles('ModalBody', props); + + return ( + + {children} + + ); +}; + +ModalBody.displayName = 'ModalBody'; diff --git a/src/components/Modal/ModalCloseButton.tsx b/src/components/Modal/ModalCloseButton.tsx new file mode 100644 index 0000000..d853b47 --- /dev/null +++ b/src/components/Modal/ModalCloseButton.tsx @@ -0,0 +1,15 @@ +import * as React from 'react'; +import { useComponentStyles } from '../../system'; +import { CloseButton, CloseButtonProps } from '../CloseButton'; + +export interface ModalCloseButtonProps extends CloseButtonProps {} + +export const ModalCloseButton: React.FC = (props) => { + const { sx, children, ...rest } = props; + + const styles = useComponentStyles('ModalCloseButton', props); + + return ; +}; + +ModalCloseButton.displayName = 'ModalCloseButton'; diff --git a/src/components/Modal/ModalFooter.tsx b/src/components/Modal/ModalFooter.tsx new file mode 100644 index 0000000..ba52954 --- /dev/null +++ b/src/components/Modal/ModalFooter.tsx @@ -0,0 +1,19 @@ +import * as React from 'react'; +import { SxProps, useComponentStyles } from '../../system'; +import { Box } from '../Box'; + +export interface ModalFooterProps extends SxProps {} + +export const ModalFooter: React.FC = (props) => { + const { sx, children, ...rest } = props; + + const styles = useComponentStyles('ModalFooter', props); + + return ( + + {children} + + ); +}; + +ModalFooter.displayName = 'ModalFooter'; diff --git a/src/components/Modal/ModalHeader.tsx b/src/components/Modal/ModalHeader.tsx new file mode 100644 index 0000000..0436894 --- /dev/null +++ b/src/components/Modal/ModalHeader.tsx @@ -0,0 +1,19 @@ +import * as React from 'react'; +import { SxProps, useComponentStyles } from '../../system'; +import { Box } from '../Box'; + +export interface ModalHeaderProps extends SxProps {} + +export const ModalHeader: React.FC = (props) => { + const { sx, children, ...rest } = props; + + const styles = useComponentStyles('ModalHeader', props); + + return ( + + {children} + + ); +}; + +ModalHeader.displayName = 'ModalHeader'; diff --git a/src/components/Modal/index.ts b/src/components/Modal/index.ts index cb89ee1..2be75b9 100644 --- a/src/components/Modal/index.ts +++ b/src/components/Modal/index.ts @@ -1 +1,5 @@ export * from './Modal'; +export * from './ModalBody'; +export * from './ModalCloseButton'; +export * from './ModalFooter'; +export * from './ModalHeader'; diff --git a/src/components/Select/Select.stories.tsx b/src/components/Select/Select.stories.tsx index 8e291a3..2b1f06e 100644 --- a/src/components/Select/Select.stories.tsx +++ b/src/components/Select/Select.stories.tsx @@ -77,6 +77,7 @@ export const SearchableAsync = (props: any) => { return (