├── .babelrc.json ├── .env ├── .github ├── FUNDING.yml └── workflows │ ├── ci.yml │ ├── publish.yml │ └── storybook.yml ├── .gitignore ├── .storybook ├── main.js ├── preview-head.html └── preview.js ├── LICENSE ├── README.md ├── package-lock.json ├── package.json ├── src ├── Card │ ├── index.tsx │ └── styles.module.scss ├── CryptoCard │ ├── CryptoChart │ │ ├── config.tsx │ │ ├── index.tsx │ │ └── styles.module.scss │ ├── IconLine │ │ ├── index.tsx │ │ └── styles.module.scss │ ├── TitleLine │ │ ├── index.tsx │ │ └── styles.module.scss │ ├── index.tsx │ └── styles.module.scss ├── DropdownCard │ ├── Arrow │ │ ├── ArrowDown │ │ │ └── index.js │ │ ├── ArrowUp │ │ │ └── index.js │ │ ├── index.js │ │ └── styles.scss │ ├── NameSurname │ │ ├── index.js │ │ └── styles.scss │ ├── ToggledCard │ │ ├── index.js │ │ └── styles.scss │ ├── index.js │ └── styles.scss ├── FlippingCard │ ├── FlippingCardBack │ │ ├── index.tsx │ │ └── styles.module.scss │ ├── FlippingCardFront │ │ ├── index.tsx │ │ └── styles.module.scss │ ├── index.tsx │ └── styles.module.scss ├── NewsHeaderCard │ ├── NewsHeader │ │ ├── index.tsx │ │ └── styles.module.scss │ ├── NewsHeaderBackground │ │ ├── index.tsx │ │ └── styles.module.scss │ ├── index.tsx │ └── styles.module.scss ├── PaymentCard │ ├── Chip │ │ ├── index.tsx │ │ └── styles.module.scss │ ├── IssuerIcon │ │ ├── index.tsx │ │ └── styles.module.scss │ ├── PaymentCardBack │ │ ├── index.tsx │ │ └── styles.module.scss │ ├── PaymentCardBackground │ │ ├── index.tsx │ │ └── styles.module.scss │ ├── PaymentCardDate │ │ ├── index.tsx │ │ └── styles.module.scss │ ├── PaymentCardName │ │ ├── index.tsx │ │ └── styles.module.scss │ ├── PaymentCardNumber │ │ ├── index.tsx │ │ └── styles.module.scss │ ├── index.tsx │ ├── styles.module.scss │ └── svg │ │ ├── spiral.svg │ │ ├── triangles.svg │ │ └── world-map.svg ├── ProductCard │ ├── PriceTag │ │ ├── index.tsx │ │ └── styles.module.scss │ ├── ProductCardDescription │ │ ├── index.tsx │ │ └── styles.module.scss │ ├── ProductCardGallery │ │ ├── GalleryToggle │ │ │ ├── index.tsx │ │ │ └── styles.module.scss │ │ ├── index.tsx │ │ └── styles.module.scss │ ├── index.tsx │ └── styles.module.scss ├── RecipeCard │ ├── RecipeCardContent │ │ ├── index.tsx │ │ └── styles.module.scss │ ├── RecipeCardDesc │ │ ├── index.tsx │ │ └── styles.module.scss │ ├── index.tsx │ └── styles.module.scss ├── TaggedContentCard │ ├── CardContent │ │ ├── index.tsx │ │ └── styles.module.scss │ ├── CardOverlay │ │ ├── index.tsx │ │ └── styles.module.scss │ ├── index.tsx │ └── styles.module.scss ├── UserCard │ ├── UserCardAvatar │ │ ├── index.tsx │ │ └── styles.module.scss │ ├── UserCardBody │ │ ├── index.tsx │ │ └── styles.module.scss │ ├── UserCardHeader │ │ ├── index.tsx │ │ └── styles.module.scss │ ├── UserCardStats │ │ ├── index.tsx │ │ └── styles.module.scss │ ├── index.tsx │ └── styles.module.scss ├── __snapshots__ │ └── storybook.test.ts.snap ├── index.tsx ├── stories │ ├── Card.stories.tsx │ ├── CryptoCard.stories.tsx │ ├── Demo.module.scss │ ├── Demo.stories.tsx │ ├── FlippingCard.stories.tsx │ ├── NewsHeaderCard.stories.tsx │ ├── PaymentCard.stories.tsx │ ├── ProductCard.stories.tsx │ ├── RecipeCard.stories.tsx │ ├── TaggedContentCard.stories.tsx │ └── UserCard.stories.tsx ├── storybook.test.ts └── typings.d.ts ├── tsconfig.json └── webpack.config.ts /.babelrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "sourceType": "unambiguous", 3 | "presets": [ 4 | [ 5 | "@babel/preset-env", 6 | { 7 | "targets": { 8 | "chrome": 100 9 | } 10 | } 11 | ], 12 | "@babel/preset-typescript", 13 | "@babel/preset-react" 14 | ], 15 | "plugins": [] 16 | } -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | SKIP_PREFLIGHT_CHECK=true -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: nukeop -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI job 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | test-and-build: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - name: Checkout 10 | uses: actions/checkout@v2 11 | 12 | - name: Setup node 13 | uses: actions/setup-node@v2.1.5 14 | with: 15 | node-version: 16 16 | 17 | - name: npm ci 18 | run: npm ci 19 | 20 | - name: Run tests 21 | run: npm test 22 | 23 | - name: Build 24 | run: npm run build -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Build and publish 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | workflow_dispatch: 8 | 9 | jobs: 10 | build-and-publish: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v2 15 | 16 | - name: Setup node 17 | uses: actions/setup-node@v2.1.5 18 | with: 19 | node-version: 16 20 | 21 | - name: npm ci 22 | run: npm ci 23 | 24 | - name: Build 25 | run: npm run build 26 | 27 | - name: Publish 28 | uses: JS-DevTools/npm-publish@v1 29 | with: 30 | token: ${{ secrets.NPM_TOKEN }} -------------------------------------------------------------------------------- /.github/workflows/storybook.yml: -------------------------------------------------------------------------------- 1 | name: Publish stories 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | paths: ["stories/**", "src/components/**"] 8 | workflow_dispatch: 9 | 10 | jobs: 11 | build-and-deploy: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v2 16 | 17 | - name: Setup node 18 | uses: actions/setup-node@v2.1.5 19 | with: 20 | node-version: 16 21 | 22 | - name: npm ci 23 | run: npm ci 24 | 25 | - name: Build Storybook 26 | run: npm run build-storybook 27 | 28 | - name: Build and deploy to gh-pages branch 29 | uses: JamesIves/github-pages-deploy-action@4.1.5 30 | with: 31 | branch: gh-pages 32 | folder: storybook-static -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | 25 | dist 26 | storybook-static -------------------------------------------------------------------------------- /.storybook/main.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "stories": ["../src/**/*.stories.@(js|jsx|ts|tsx)"], 3 | "addons": ["@storybook/addon-links", "@storybook/addon-essentials", 4 | //"@storybook/preset-create-react-app", 5 | '@storybook/preset-scss', "@storybook/addon-mdx-gfm"], 6 | webpackFinal: async config => { 7 | config.module.rules.find(({ 8 | test 9 | }) => test.test('.svg')).test = /\.(ico|jpg|jpeg|png|apng|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)(\?.*)?$/; 10 | config.module.rules.unshift({ 11 | test: /\.svg$/, 12 | use: "raw-loader" 13 | }); 14 | return config; 15 | }, 16 | framework: { 17 | name: "@storybook/react-webpack5", 18 | options: {} 19 | } 20 | }; -------------------------------------------------------------------------------- /.storybook/preview-head.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.storybook/preview.js: -------------------------------------------------------------------------------- 1 | export const parameters = { 2 | actions: { argTypesRegex: "^on[A-Z].*" }, 3 | backgrounds: { 4 | default: 'light', 5 | values: [ 6 | { 7 | name: 'light', 8 | value: '#ABDCFF' 9 | }, 10 | { 11 | name: 'dark', 12 | value: '#141e30' 13 | } 14 | ], 15 | }, 16 | controls: { 17 | matchers: { 18 | color: /(background|color)$/i, 19 | date: /Date$/, 20 | }, 21 | }, 22 | options: { 23 | storySort: { 24 | order: ['Demo', 'Cards'] 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2018 nukeop 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-ui-cards ![CI](https://github.com/nukeop/react-ui-cards/actions/workflows/ci.yml/badge.svg) 2 | 3 | Various stylish card components for React. 4 | Focused on being ready to use with little modifications. Supply your data and you're good to go. 5 | 6 | ## Live demo 7 | https://nukeop.github.io/react-ui-cards 8 | 9 | ## npm 10 | https://www.npmjs.com/package/react-ui-cards 11 | 12 | ## Installation and usage example 13 | Minified and gzipped bundle size: 52kb. 14 | ```bash 15 | $ npm install --save react-ui-cards 16 | ``` 17 | 18 | ```typescript 19 | import { UserCard } from 'react-ui-cards'; 20 | 21 | export const Example = () => 29 | ``` 30 | 31 | ## Development 32 | ### Getting started 33 | After cloning the repo, run the following commands to start Storybook in development mode with hot reload: 34 | 35 | ```shell 36 | $ npm install 37 | $ npm run storybook 38 | ``` 39 | 40 | ### Running tests 41 | The repo is configured so that it generates snapshot tests from Storybook stories. To run them, use: 42 | 43 | ```shell 44 | $ npm test 45 | ``` 46 | 47 | ### Contributing 48 | All contributions are welcome. You can either add something to existing cards, or create a completely new card type. 49 | 50 | ## Card types 51 | In addition to what's listed for each card type, all cards can take the following additional props: 52 | 53 | - `className` to apply additional classes to the card 54 | - `float` (boolean) to enable animation on hover 55 | - Other props are passed down to the enclosing div element so you can e.g. add custom callbacks for onClick and other events. 56 | 57 | ### User cards 58 | Cards representing users or people. 59 | 60 | | attribute | type | description | 61 | |--------------|-------------|---------------------------------------------------------------------------------------------------------| 62 | | href | string/null | optional url the card will link to when clicked | 63 | | header | string | url to the image that will be displayed in the upper part of the card | 64 | | avatar | string | url to the image that will be displayed in the center of the card | 65 | | name | string | user's name | 66 | | positionName | string | user's role | 67 | | stats | array/null | an optional array of objects with attributes 'name' and 'value' - this is displayed in the card's footer | 68 | 69 | ![User card](https://i.imgur.com/2QMNcjY.png) 70 | 71 | ### Product cards 72 | Cards representing products available for purchase. 73 | 74 | | attribute | type | description | 75 | |-------------|-----------------|---------------------------------------------------------------| 76 | | photos | array | array of urls to photos of the product | 77 | | price | string | the price that will be displayed in the upper lefthand corner | 78 | | productName | string | name of the product | 79 | | description | string | a short description of the product | 80 | | rating | integer | rating of the product (0-5). Not implemented yet. | 81 | | url | string | url the button will link to | 82 | | buttonText | string/null | optional button text | 83 | 84 | ![Product card](https://i.imgur.com/qDCMzwV.png) 85 | 86 | ### Tagged content cards 87 | Cards showcasing any content that can be described with a single thumbnail and a list of tags. 88 | 89 | | attribute | type | description | 90 | |-------------|-------------|-----------------------------------------------------------------------------------------------------------------------| 91 | | href | string/null | optional url the card will link to when clicked | 92 | | thumbnail | string | url to the image that will be displayed in the center part of the card and as the background | 93 | | title | string | title of the card | 94 | | description | string | short description of the content, try to not exceed one line | 95 | | tags | array/null | an array of strings that will be converted to pill-style tags and displayed in the lower righthand corner of the card | 96 | 97 | ![Tagged content card](https://i.imgur.com/SyakUBF.png) 98 | 99 | ### Flipping card 100 | A card with a front and back side. Flips over on hover. Any React component can be displayed on either side. 101 | 102 | This card is a bit different than the other ones - it has no attributes. To define it, you have to create a nested structure and pass your content as children: 103 | 104 | ```javascript 105 | 106 | 107 | Content that will be displayed on the back of the card 108 | 109 | 110 | Content that will be displayed on the front of the card 111 | 112 | 113 | ``` 114 | 115 | See demo page for an example in action. 116 | 117 | ### Recipe card 118 | Cards you can use to show dishes' names, time they take to cook and how big the portions are. Heart sign can be used as "Add to favorite" button. 119 | 120 | | attribute | type | description | 121 | |-------------|--------------|------------------------------------------------------------------------| 122 | | href | string/null | optional url the card will link to when clicked | 123 | | thumbnail | string | url to the image that will be displayed at the top of other elements | 124 | | title | string | name of the dish | 125 | | time | string | amount of time it will take to prepare the dish | 126 | | servings | string | estimation of portions' size | 127 | 128 | ![Recipe card](https://i.imgur.com/PfE6Cf0.png) 129 | 130 | 131 | 132 | ### News header card 133 | Cards you can use on the news site, shows the title, author and date it was published. Click on image to see the exact news. 134 | 135 | | attribute | type | description | 136 | |-----------|-------------|-----------------------------------------------------------| 137 | | href | string/null | optional url the card will link to when clicked | 138 | | thumbnail | string | url to the image that will be displayed in the background | 139 | | author | string | author of aricle linked in href | 140 | | date | string | date of publishing | 141 | | tags | string | optional. array of string to be rendered as tags | 142 | 143 | ![News header card](https://i.imgur.com/3fZKPyS.png) 144 | 145 | ### Cryptocurrency card 146 | Cards that show cryptocurrency-related data, including a chart. Can be used for showing other data, such as fiat currency or stock market prices. 147 | 148 | | attribute | type | description | 149 | |-------------------|-------------------|----------------------------------------------------------------------------------| 150 | | currencyName | string | Name of the cryptocurrency | 151 | | currencyPrice | string | Current price, displayed as provided, which means you have to format it yourself | 152 | | icon | node | Icon representing the currency, it's displayed at 24x24 px | 153 | | currencyShortName | string | Symbol such as BTC, ETH, etc | 154 | | trend | string | Recent price change. Just as with the price, no formatting is performed | 155 | | trendDirection | integer(-1, 0, 1) | Indicates whether the trend is positive, negative, or if there's no change | 156 | | chartColor | string | Color of the line on the chart | 157 | | chartData | array\[integer\] | An array of integer values to be displayed on the chart | 158 | 159 | ![Cryptocurrency card](http://i.imgur.com/7yGpFnw.png) 160 | 161 | ### Payment card 162 | Card that represents a credit card, a debit card, or other similar cards. Flips on hover to reveal the back. 163 | 164 | | attribute | type | description | 165 | |-------------------|----------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------| 166 | | background | string | Valid CSS background string. If not provided, a default background is used. | 167 | | backgroundPattern | string/null | Lets you select one of the patterns to display in the background. Can be either "worldMap", "triangles", or "spiral". Null results in no pattern being displayed | 168 | | issuerIcon | string/element | Card issuer icon. Either a link to the image with an icon, or a React element to render | 169 | | number | string | Payment card number | 170 | | date | string | The "valid thru" date | 171 | | name | string | Name displayed on the card | 172 | | cvv | string | CVV code displayed on the back of the card | 173 | 174 | ![Payment cards](http://i.imgur.com/0Nb9K1B.png) 175 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-ui-cards", 3 | "version": "3.1.0", 4 | "description": "Various card components for React", 5 | "main": "dist/index.js", 6 | "files": [ 7 | "dist", 8 | "README.md" 9 | ], 10 | "scripts": { 11 | "start": "react-scripts start", 12 | "build": "webpack --progress", 13 | "test": "react-scripts test", 14 | "eject": "react-scripts eject", 15 | "storybook": "storybook dev -p 6006", 16 | "build-storybook": "storybook build" 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "git+https://github.com/nukeop/react-ui-cards.git" 21 | }, 22 | "keywords": [ 23 | "react", 24 | "card", 25 | "component", 26 | "ui" 27 | ], 28 | "author": "nukeop", 29 | "license": "MIT", 30 | "dependencies": { 31 | "@storybook/addon-storyshots": "^7.0.0-rc.8", 32 | "@storybook/preset-scss": "^1.0.3", 33 | "@testing-library/jest-dom": "^5.11.4", 34 | "@testing-library/react": "^11.1.0", 35 | "@testing-library/user-event": "^12.1.10", 36 | "@types/jest": "^26.0.15", 37 | "chart.js": "^2.7.2", 38 | "classnames": "^2.2.6", 39 | "css-loader": "^5.2.7", 40 | "raw-loader": "^4.0.2", 41 | "react": "^17.0.2", 42 | "react-chartjs-2": "^2.7.2", 43 | "react-dom": "^17.0.2", 44 | "react-fontawesome": "^1.6.1", 45 | "react-scripts": "5.0.1", 46 | "sass": "^1.6.0", 47 | "sass-loader": "^10.2.0", 48 | "style-loader": "^2.0.0", 49 | "ts-loader": "^8.3.0", 50 | "web-vitals": "^1.0.1", 51 | "webpack": "^5.76.3", 52 | "webpack-node-externals": "^3.0.0" 53 | }, 54 | "eslintConfig": { 55 | "extends": [ 56 | "react-app", 57 | "react-app/jest" 58 | ], 59 | "overrides": [ 60 | { 61 | "files": [ 62 | "**/*.stories.*" 63 | ], 64 | "rules": { 65 | "import/no-anonymous-default-export": "off" 66 | } 67 | } 68 | ] 69 | }, 70 | "browserslist": { 71 | "production": [ 72 | ">0.2%", 73 | "not dead", 74 | "not op_mini all" 75 | ], 76 | "development": [ 77 | "last 1 chrome version", 78 | "last 1 firefox version", 79 | "last 1 safari version" 80 | ] 81 | }, 82 | "devDependencies": { 83 | "@babel/cli": "^7.16.0", 84 | "@babel/preset-env": "^7.20.2", 85 | "@babel/preset-react": "^7.18.6", 86 | "@babel/preset-typescript": "^7.21.0", 87 | "@storybook/addon-actions": "^7.0.0-rc.8", 88 | "@storybook/addon-essentials": "^7.0.0-rc.8", 89 | "@storybook/addon-links": "^7.0.0-rc.8", 90 | "@storybook/addon-mdx-gfm": "^7.0.0-rc.8", 91 | "@storybook/node-logger": "^7.0.0-rc.8", 92 | "@storybook/preset-create-react-app": "^7.0.0-rc.8", 93 | "@storybook/react": "^7.0.0-rc.8", 94 | "@storybook/react-webpack5": "^7.0.0-rc.8", 95 | "@types/node": "^12.20.36", 96 | "@types/react": "^17.0.0", 97 | "@types/react-dom": "^17.0.0", 98 | "@types/react-fontawesome": "^1.6.5", 99 | "@types/webpack": "^5.28.0", 100 | "@types/webpack-node-externals": "^2.5.3", 101 | "cross-env": "^7.0.3", 102 | "storybook": "^7.0.0-rc.8", 103 | "ts-node": "^10.4.0", 104 | "typescript": "^4.4.4", 105 | "webpack-cli": "^5.0.1" 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/Card/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import classnames from 'classnames'; 3 | 4 | import styles from './styles.module.scss'; 5 | 6 | export type CardProps = { 7 | className?: string; 8 | float?: boolean; 9 | }; 10 | 11 | const Card: React.FC = ({ 12 | className, 13 | float, 14 | children, 15 | ...rest 16 | }) => { 17 | 18 | return
25 | {children} 26 |
; 27 | }; 28 | 29 | export default Card; 30 | -------------------------------------------------------------------------------- /src/Card/styles.module.scss: -------------------------------------------------------------------------------- 1 | .card { 2 | position: relative; 3 | max-width: 100%; 4 | width: 250px; 5 | min-width: 250px; 6 | min-height: 350px; 7 | display: flex; 8 | flex-flow: column; 9 | background-color: #ecf0f1; 10 | border-radius: 0.25rem; 11 | margin: 1rem; 12 | user-select: none; 13 | transition: 0.25s; 14 | 15 | &.float:hover { 16 | transform: translateY(-8px); 17 | box-shadow: 0 10px 10px -10px rgba(#7f8c8d, 1); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/CryptoCard/CryptoChart/config.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-anonymous-default-export */ 2 | export default { 3 | datasets: { 4 | borderColor: 'rgba(241, 196, 15, 1)', 5 | borderCapStyle: 'round', 6 | borderJoinStyle: 'round', 7 | lineTension: 0.4, 8 | xAxisId: 'x-0', 9 | yAxisID: 'y-0', 10 | pointRadius: 0, 11 | pointHoverRadius: 0, 12 | fill: true, 13 | showLine: true 14 | }, 15 | options: { 16 | legend: { 17 | display: false 18 | }, 19 | tooltip: { 20 | enabled: false 21 | }, 22 | scales: { 23 | yAxes: [ 24 | { 25 | id: 'y-0', 26 | display: false, 27 | type: 'linear', 28 | gridLines: { 29 | display: false 30 | } 31 | } 32 | ], 33 | xAxes: [ 34 | { 35 | id: 'x-0', 36 | display: false, 37 | gridLines: { 38 | display: false 39 | } 40 | } 41 | ] 42 | }, 43 | responsive: true 44 | } 45 | 46 | }; 47 | -------------------------------------------------------------------------------- /src/CryptoCard/CryptoChart/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Line } from 'react-chartjs-2'; 3 | 4 | import config from './config'; 5 | import styles from './styles.module.scss'; 6 | 7 | export type CryptoChartProps = { 8 | chartColor?: string; 9 | chartData: number[]; 10 | } 11 | 12 | const CryptoChart: React.FC = ({ 13 | chartColor, 14 | chartData 15 | }) => { 16 | let datasets = [{ 17 | ...config.datasets, 18 | data: chartData 19 | }]; 20 | 21 | let data = { 22 | datasets, 23 | labels: Object.keys(chartData) 24 | }; 25 | 26 | const dataFun = (canvas: HTMLCanvasElement) => { 27 | const ctx = canvas?.getContext('2d'); 28 | const gradient = ctx?.createLinearGradient(50, 0, 50, canvas.height * 1.1); 29 | gradient?.addColorStop(0, 'rgba(236, 240, 241, 0.1)'); 30 | gradient?.addColorStop(1, 'rgba(236, 240, 241, 0)'); 31 | 32 | let firstDataset = data.datasets[0]; 33 | firstDataset.borderColor = chartColor ?? firstDataset.borderColor; 34 | // @ts-ignore 35 | firstDataset.backgroundColor = gradient; 36 | 37 | return data; 38 | }; 39 | 40 | return ( 41 |
42 | 47 |
48 | ); 49 | } 50 | 51 | export default CryptoChart; 52 | -------------------------------------------------------------------------------- /src/CryptoCard/CryptoChart/styles.module.scss: -------------------------------------------------------------------------------- 1 | .crypto-chart { 2 | position: absolute; 3 | bottom: 0; 4 | right: 0; 5 | left: 0; 6 | top: 40%; 7 | display: flex; 8 | width: 100%; 9 | 10 | canvas { 11 | height: 100% !important; 12 | width: 100% !important; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/CryptoCard/IconLine/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import cx from 'classnames'; 3 | 4 | import styles from './styles.module.scss'; 5 | 6 | export type IconLineProps = { 7 | icon: React.ReactNode; 8 | currencyShortName: string; 9 | trend: string; 10 | trendDirection: -1 | 0 | 1; 11 | }; 12 | 13 | const IconLine: React.FC = ({ 14 | icon, 15 | currencyShortName, 16 | trend, 17 | trendDirection 18 | }) => ( 19 |
20 |
21 |
22 | {icon} 23 |
24 |
25 | {currencyShortName} 26 |
27 |
28 |
29 |
0 }, 32 | { [`${styles.yellow}`]: trendDirection === 0 }, 33 | { [`${styles.red}`]: trendDirection < 0 } 34 | )}> 35 | {trend} 36 | {' '} 37 | {trendDirection > 0 38 | ? '▲' 39 | : trendDirection < 0 40 | ? '▼' 41 | : '='} 42 |
43 |
44 |
45 | ) 46 | 47 | export default IconLine; 48 | -------------------------------------------------------------------------------- /src/CryptoCard/IconLine/styles.module.scss: -------------------------------------------------------------------------------- 1 | .crypto-card-icon-line { 2 | display: flex; 3 | flex-flow: row; 4 | justify-content: space-between; 5 | align-items: center; 6 | font-size: 18px; 7 | 8 | .left, 9 | .right, 10 | .crypto-short-name { 11 | display: flex; 12 | flex-flow: row; 13 | flex: 1 1 auto; 14 | align-items: center; 15 | } 16 | 17 | .left { 18 | justify-content: flex-start; 19 | color: rgba(#fff, 0.4); 20 | } 21 | 22 | .right { 23 | justify-content: flex-end; 24 | } 25 | 26 | .crypto-icon { 27 | display: flex; 28 | height: 24px; 29 | 30 | img { 31 | height: 100%; 32 | } 33 | } 34 | 35 | .crypto-short-name { 36 | height: 100%; 37 | margin-left: 0.5em; 38 | } 39 | 40 | .trend { 41 | &.green { 42 | color: #2ecc71; 43 | } 44 | 45 | &.yellow { 46 | color: #f1c40f; 47 | } 48 | 49 | &.red { 50 | color: #e74c3c; 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/CryptoCard/TitleLine/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import styles from './styles.module.scss'; 4 | 5 | export type TitleLineProps = { 6 | currencyName: string, 7 | currencyPrice: string 8 | } 9 | 10 | const TitleLine: React.FC = ({ 11 | currencyName, 12 | currencyPrice 13 | }) => ( 14 |
15 |
{currencyName}
16 |
{currencyPrice}
17 |
18 | ) 19 | 20 | export default TitleLine; 21 | -------------------------------------------------------------------------------- /src/CryptoCard/TitleLine/styles.module.scss: -------------------------------------------------------------------------------- 1 | .crypto-card-title-line { 2 | display: flex; 3 | flex-flow: row; 4 | justify-content: space-between; 5 | align-items: center; 6 | margin-bottom: 0.5rem; 7 | font-size: 20px; 8 | 9 | color: #FFF; 10 | } 11 | -------------------------------------------------------------------------------- /src/CryptoCard/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import cx from 'classnames'; 3 | 4 | import Card from '../Card'; 5 | import TitleLine, { TitleLineProps } from './TitleLine'; 6 | import IconLine, { IconLineProps } from './IconLine'; 7 | import CryptoChart, { CryptoChartProps } from './CryptoChart'; 8 | 9 | import styles from './styles.module.scss'; 10 | 11 | export type CryptoCardProps = { 12 | className?: string; 13 | currencyName: TitleLineProps['currencyName']; 14 | currencyPrice: TitleLineProps['currencyPrice']; 15 | icon: IconLineProps['icon']; 16 | currencyShortName: IconLineProps['currencyShortName']; 17 | trend: IconLineProps['trend']; 18 | trendDirection: IconLineProps['trendDirection']; 19 | chartColor?: CryptoChartProps['chartColor']; 20 | chartData: CryptoChartProps['chartData']; 21 | }; 22 | 23 | const CryptoCard: React.FC = ({ 24 | className, 25 | currencyName, 26 | currencyPrice, 27 | icon, 28 | currencyShortName, 29 | trend, 30 | trendDirection, 31 | chartColor, 32 | chartData, 33 | ...rest 34 | }) => ( 35 | 41 | 44 | 49 | 50 | 53 | 54 | ) 55 | 56 | export default CryptoCard; 57 | 58 | //Designed by Triyandi Saputra 59 | //https://dribbble.com/shots/4071699-Crypt-App-Dark-Version 60 | -------------------------------------------------------------------------------- /src/CryptoCard/styles.module.scss: -------------------------------------------------------------------------------- 1 | .crypto-card { 2 | display: flex; 3 | 4 | background: rgba(#3f4c6b, 0.35); 5 | 6 | min-width: auto; 7 | min-height: auto; 8 | width: 24rem; 9 | height: 14rem; 10 | font-family: Roboto; 11 | 12 | padding: 1rem; 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/DropdownCard/Arrow/ArrowDown/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import FontAwesome from 'react-fontawesome'; 3 | 4 | class ArrowDown extends React.Component { 5 | render(){ 6 | return( 7 | 8 | 9 | 10 | ); 11 | } 12 | } 13 | export default ArrowDown; 14 | -------------------------------------------------------------------------------- /src/DropdownCard/Arrow/ArrowUp/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import FontAwesome from 'react-fontawesome'; 3 | 4 | class ArrowUp extends React.Component { 5 | render(){ 6 | return( 7 | 8 | 9 | 10 | ); 11 | } 12 | } 13 | export default ArrowUp; 14 | -------------------------------------------------------------------------------- /src/DropdownCard/Arrow/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Card from '../../Card'; 3 | import PropTypes from 'prop-types'; 4 | 5 | import ArrowUp from './ArrowUp'; 6 | import ArrowDown from './ArrowDown'; 7 | 8 | import styles from './styles.scss'; 9 | 10 | class Arrow extends React.Component { 11 | constructor(props){ 12 | super(props); 13 | } 14 | 15 | render(){ 16 | let{ 17 | onClick, 18 | position, 19 | message 20 | } = this.props; 21 | 22 | return ( 23 | 24 | { 25 | position === false 26 | ? { message } 27 | : { message } 28 | } 29 | 30 | ); 31 | } 32 | } 33 | 34 | 35 | export default Arrow; 36 | -------------------------------------------------------------------------------- /src/DropdownCard/Arrow/styles.scss: -------------------------------------------------------------------------------- 1 | .toggle-root{ 2 | width: 100%; 3 | } 4 | -------------------------------------------------------------------------------- /src/DropdownCard/NameSurname/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import styles from './styles.scss'; 4 | 5 | class NameSurname extends React.Component { 6 | render(){ 7 | let{ 8 | name, 9 | surname, 10 | bgColor 11 | } = this.props; 12 | 13 | return( 14 |
20 | 21 |
22 | {name} 23 |
24 | 25 |
26 | {surname} 27 |
28 | 29 |
30 | ); 31 | } 32 | } 33 | export default NameSurname; 34 | -------------------------------------------------------------------------------- /src/DropdownCard/NameSurname/styles.scss: -------------------------------------------------------------------------------- 1 | .NSroot{ 2 | display: flex; 3 | justify-content: center; 4 | align-items: center; 5 | height: 3rem; 6 | border-radius: 30px 5px 0 0; 7 | 8 | .name{ 9 | padding-right: 0.5rem; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/DropdownCard/ToggledCard/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import Card from '../../Card'; 4 | 5 | import styles from './styles.scss'; 6 | 7 | class ToggledCard extends React.Component { 8 | constructor(props){ 9 | super(props); 10 | } 11 | render(){ 12 | let{ 13 | email, 14 | phone, 15 | info, 16 | bgColor 17 | } = this.props; 18 | 19 | return( 20 |
23 | 24 |
25 | { email } 26 |
27 | 28 |
29 | { phone } 30 |
31 | 32 |
33 | { info } 34 |
35 | 36 |
37 | ); 38 | } 39 | }; 40 | 41 | export default ToggledCard; 42 | -------------------------------------------------------------------------------- /src/DropdownCard/ToggledCard/styles.scss: -------------------------------------------------------------------------------- 1 | .toggled-root{ 2 | height: 10rem; 3 | border-radius: 0 0 5px 5px; 4 | display: flex; 5 | flex-direction: column; 6 | justify-content: space-evenly; 7 | align-items: center; 8 | } 9 | -------------------------------------------------------------------------------- /src/DropdownCard/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import cx from 'classnames'; 3 | 4 | import Card from '../Card'; 5 | import NameSurname from './NameSurname'; 6 | import Arrow from './Arrow'; 7 | import ToggledCard from './ToggledCard'; 8 | 9 | import styles from './styles.scss'; 10 | 11 | class DropdownCard extends React.Component{ 12 | constructor(props){ 13 | super(props); 14 | this.state = { 15 | arrowToggled : false 16 | }; 17 | } 18 | 19 | toggleArrow() { 20 | this.setState(prevState => { 21 | return { arrowToggled: !prevState.arrowToggled }; 22 | }); 23 | } 24 | 25 | render(){ 26 | let{ 27 | className, 28 | name, 29 | surname, 30 | email, 31 | phone, 32 | info, 33 | messageOnToggled, 34 | messageOnUntoggled, 35 | buttonColor, 36 | bgColor, 37 | ...other 38 | } = this.props; 39 | 40 | return( 41 | 47 | 52 | 53 | { 54 | this.state.arrowToggled === true 55 | ? 56 | 68 | 69 | 75 | 76 | : 88 | } 89 | 90 | 91 | ); 92 | } 93 | } 94 | export default DropdownCard; 95 | -------------------------------------------------------------------------------- /src/DropdownCard/styles.scss: -------------------------------------------------------------------------------- 1 | .dropdown-card { 2 | display: flex; 3 | margin-bottom: 10rem; 4 | min-height: 3rem; 5 | background-color: rgba(0, 0, 0, 0); 6 | 7 | .button { 8 | display: flex; 9 | border-style: none; 10 | width: 100%; 11 | transition: .4s; 12 | 13 | &:hover { 14 | transform: scale(1.1); 15 | border-radius: 50px; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/FlippingCard/FlippingCardBack/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import styles from './styles.module.scss'; 4 | 5 | const FlippingCardBack: React.FC = ({ children }) => ( 6 |
7 | {children} 8 |
9 | ) 10 | 11 | export default FlippingCardBack; 12 | -------------------------------------------------------------------------------- /src/FlippingCard/FlippingCardBack/styles.module.scss: -------------------------------------------------------------------------------- 1 | .back { 2 | position: absolute; 3 | width: 100%; 4 | height: 100%; 5 | 6 | backface-visibility: hidden; 7 | top: 0; 8 | left: 0; 9 | transform: rotateY(180deg); 10 | border-radius: 0.25rem; 11 | overflow: hidden; 12 | } 13 | -------------------------------------------------------------------------------- /src/FlippingCard/FlippingCardFront/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import styles from './styles.module.scss'; 4 | 5 | const FlippingCardFront: React.FC = ({ children }) => ( 6 |
7 | {children} 8 |
9 | ) 10 | 11 | export default FlippingCardFront; 12 | -------------------------------------------------------------------------------- /src/FlippingCard/FlippingCardFront/styles.module.scss: -------------------------------------------------------------------------------- 1 | .front { 2 | position: absolute; 3 | width: 100%; 4 | height: 100%; 5 | 6 | backface-visibility: hidden; 7 | top: 0; 8 | left: 0; 9 | z-index: 2; 10 | transform: rotateY(0deg); 11 | border-radius: 0.25rem; 12 | overflow: hidden; 13 | } 14 | -------------------------------------------------------------------------------- /src/FlippingCard/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import cx from 'classnames'; 3 | 4 | import Card from '../Card'; 5 | import styles from './styles.module.scss'; 6 | 7 | export type FlippingCardProps = { 8 | className?: string, 9 | }; 10 | 11 | const FlippingCard: React.FC = ({ 12 | className, 13 | children, 14 | ...rest 15 | }) => ( 16 | 23 |
24 | {children} 25 |
26 |
27 | ) 28 | 29 | export default FlippingCard; 30 | -------------------------------------------------------------------------------- /src/FlippingCard/styles.module.scss: -------------------------------------------------------------------------------- 1 | .flipping-card-container { 2 | perspective: 1000px; 3 | background-color: transparent; 4 | transition: 0.5s; 5 | 6 | &:hover { 7 | .flipping-card { 8 | transform: rotateY(180deg); 9 | } 10 | } 11 | } 12 | 13 | .flipping-card { 14 | position: absolute; 15 | width: 100%; 16 | height: 100%; 17 | 18 | perspective: 1000px; 19 | transition: 0.5s; 20 | transform-style: preserve-3d; 21 | } 22 | -------------------------------------------------------------------------------- /src/NewsHeaderCard/NewsHeader/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import styles from './styles.module.scss'; 4 | 5 | export type NewsHeaderProps = { 6 | title: string; 7 | author: string; 8 | date: string; 9 | tags?: (string | (() => React.ReactNode))[]; 10 | }; 11 | 12 | const NewsHeader: React.FC = ({ 13 | title, 14 | author, 15 | date, 16 | tags 17 | }) => ( 18 |
19 | 20 |
21 | {title} 22 |
23 | 24 |
25 |
26 | {author} 27 |
28 | 29 |
30 | {date} 31 |
32 |
33 | 34 |
35 | {tags && tags.map((tag, i) => { 36 | return ( 37 | 38 | {typeof tag === "function" ? tag() : tag} 39 | 40 | ); 41 | })} 42 |
43 | 44 |
45 | ) 46 | 47 | export default NewsHeader; 48 | -------------------------------------------------------------------------------- /src/NewsHeaderCard/NewsHeader/styles.module.scss: -------------------------------------------------------------------------------- 1 | .news-header{ 2 | position: absolute; 3 | bottom: 0; 4 | flex-flow: row; 5 | color: #FAFAFA; 6 | width: 100%; 7 | padding: 1rem; 8 | box-sizing: border-box; 9 | z-index: 2; 10 | text-decoration: none; 11 | 12 | div{ 13 | display: flex; 14 | flex-flow: row; 15 | align-items: center; 16 | border: none; 17 | white-space: nowrap; 18 | overflow: hidden; 19 | text-overflow: ellipsis; 20 | } 21 | 22 | .title{ 23 | padding-bottom: 0.75rem; 24 | display: block; 25 | justify-content: center; 26 | font-size: 1.5rem; 27 | } 28 | 29 | .bottom{ 30 | justify-content: space-between; 31 | } 32 | 33 | .tags { 34 | display: flex; 35 | justify-content: flex-end; 36 | width: 100%; 37 | margin-top: 0.25rem; 38 | 39 | .tag { 40 | margin: 0 0.5rem; 41 | padding: 0.25rem 0.75rem; 42 | background-color: rgba(255, 255, 255, 0.35); 43 | border-radius: 2rem; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/NewsHeaderCard/NewsHeaderBackground/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import styles from './styles.module.scss'; 4 | 5 | export type NewsHeaderBackgroundProps = { 6 | thumbnail: string; 7 | }; 8 | 9 | const NewsHeaderBackground: React.FC = ({ thumbnail }) => ( 10 |
11 |
12 |
13 |
14 |
15 |
16 | ) 17 | 18 | export default NewsHeaderBackground; 19 | -------------------------------------------------------------------------------- /src/NewsHeaderCard/NewsHeaderBackground/styles.module.scss: -------------------------------------------------------------------------------- 1 | .news-header-background{ 2 | display: flex; 3 | flex-flow: column; 4 | justify-content: flex-start; 5 | align-items: center; 6 | overflow: hidden; 7 | position: absolute; 8 | top: 0; 9 | left: 0; 10 | right: 0; 11 | bottom: 0; 12 | background-color: black; 13 | z-index: 1; 14 | 15 | .overlay { 16 | position: absolute; 17 | top: 0; 18 | bottom: 0; 19 | left: 0; 20 | right: 0; 21 | background-image: linear-gradient(0deg, rgba(0, 0, 0, 0.9) 0%, rgba(0, 0, 0, 0.2) 35%, rgba(0, 0, 0, 0.0) 100%); 22 | pointer-events : none; 23 | z-index: 10; 24 | } 25 | 26 | .thumbnail{ 27 | display: flex; 28 | justify-content: center; 29 | align-items: center; 30 | width: 100%; 31 | transition: 0.4s; 32 | 33 | &:hover { 34 | transform: scale(1.1); 35 | opacity: 0.75; 36 | } 37 | } 38 | 39 | div{ 40 | width: 100%; 41 | height: 100%; 42 | background-size: cover; 43 | background-position: center; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/NewsHeaderCard/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import cx from 'classnames'; 3 | 4 | import Card from '../Card'; 5 | import NewsHeader, { NewsHeaderProps } from './NewsHeader'; 6 | import NewsHeaderBackground, { NewsHeaderBackgroundProps } from './NewsHeaderBackground'; 7 | 8 | import styles from './styles.module.scss'; 9 | 10 | export type NewsHeaderCardProps = { 11 | className?: string; 12 | href: string; 13 | hrefTarget?: string; 14 | title: NewsHeaderProps['title']; 15 | author: NewsHeaderProps['author']; 16 | date: NewsHeaderProps['date']; 17 | tags?: NewsHeaderProps['tags']; 18 | thumbnail: NewsHeaderBackgroundProps['thumbnail']; 19 | }; 20 | 21 | const NewsHeaderCard: React.FC = ({ 22 | className, 23 | href, 24 | hrefTarget = "_self", 25 | title, 26 | author, 27 | date, 28 | tags, 29 | thumbnail, 30 | ...rest 31 | }) => ( 32 | 33 | 40 | 41 | 44 | 45 | 51 | 52 | 53 | ) 54 | 55 | export default NewsHeaderCard; 56 | -------------------------------------------------------------------------------- /src/NewsHeaderCard/styles.module.scss: -------------------------------------------------------------------------------- 1 | .news-header-card{ 2 | position: relative; 3 | display: flex; 4 | flex-flow: column; 5 | width:450px; 6 | height: 100%; 7 | } 8 | 9 | -------------------------------------------------------------------------------- /src/PaymentCard/Chip/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import styles from './styles.module.scss'; 4 | 5 | const Chip: React.FC = () => ( 6 |
7 | 8 |
9 | ); 10 | 11 | export default Chip; 12 | -------------------------------------------------------------------------------- /src/PaymentCard/Chip/styles.module.scss: -------------------------------------------------------------------------------- 1 | .chip-container { 2 | position: relative; 3 | display: flex; 4 | flex-flow: row; 5 | justify-content: flex-start; 6 | width: 100%; 7 | } 8 | 9 | .chip { 10 | position: relative; 11 | border-radius: 0.5rem; 12 | width: 3rem; 13 | height: 2.25rem; 14 | background: linear-gradient(135deg, #ffb347, #ffcc33); 15 | border: 2px solid rgba(#2c3e50, 0.2); 16 | margin-left: 2rem; 17 | z-index: 10; 18 | } 19 | -------------------------------------------------------------------------------- /src/PaymentCard/IssuerIcon/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import styles from './styles.module.scss'; 4 | 5 | export type IssuerIconProps = { 6 | icon?: string | React.ReactNode; 7 | } 8 | 9 | const IssuerIcon: React.FC = ({ 10 | icon 11 | }) => { 12 | return ( 13 |
14 | { 15 | icon === undefined 16 | ? null 17 | : typeof icon === 'string' 18 | ? ( 19 | 20 | ) 21 | : icon 22 | } 23 |
24 | ); 25 | } 26 | 27 | export default IssuerIcon; 28 | -------------------------------------------------------------------------------- /src/PaymentCard/IssuerIcon/styles.module.scss: -------------------------------------------------------------------------------- 1 | .issuer-icon { 2 | position: relative; 3 | z-index: 10; 4 | 5 | display: flex; 6 | align-items: flex-start; 7 | flex-flow: row; 8 | justify-content: flex-end; 9 | 10 | box-sizing: border-box; 11 | width: 100%; 12 | padding: 1.5rem 1.5rem 0; 13 | 14 | img { 15 | width: 20%; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/PaymentCard/PaymentCardBack/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import styles from './styles.module.scss'; 4 | 5 | export type PaymentCardBackProps = { 6 | cvv: string | number; 7 | }; 8 | 9 | const PaymentCardBack: React.FC = ({ 10 | cvv 11 | }) => ( 12 |
13 | 14 |
15 | {cvv} 16 |
17 |
18 | ) 19 | 20 | export default PaymentCardBack; 21 | -------------------------------------------------------------------------------- /src/PaymentCard/PaymentCardBack/styles.module.scss: -------------------------------------------------------------------------------- 1 | .payment-card-back { 2 | position: relative; 3 | 4 | display: flex; 5 | flex-flow: column; 6 | 7 | width: 100%; 8 | height: 100%; 9 | 10 | .magnetic-stripe { 11 | width: 100%; 12 | height: 3rem; 13 | margin-top: 1rem; 14 | 15 | background: #2c3e50; 16 | } 17 | 18 | .signature-field { 19 | display: flex; 20 | align-items: flex-end; 21 | flex-flow: row; 22 | justify-content: flex-end; 23 | 24 | height: 2rem; 25 | margin-top: 0.5rem; 26 | margin-right: 6rem; 27 | margin-left: 0.5rem; 28 | 29 | background-image: repeating-linear-gradient(180deg, #f1c40f, #f1c40f 3px, #ecf0f1 3px, #ecf0f1 6px); 30 | } 31 | 32 | .cvv { 33 | color: #2c3e50; 34 | 35 | font-family: 'OCR A Std'; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/PaymentCard/PaymentCardBackground/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import classnames from 'classnames'; 3 | 4 | import triangles from '../svg/triangles.svg'; 5 | import spiral from '../svg/spiral.svg'; 6 | import styles from './styles.module.scss'; 7 | 8 | const patterns = { 9 | triangles, 10 | spiral 11 | }; 12 | 13 | const defaultBackground = 'linear-gradient( 135deg, #FF9D6C 10%, #BB4E75 100%)'; 14 | 15 | export type PaymentCardBackgroundProps = { 16 | background?: string, 17 | backgroundPattern?: keyof typeof patterns | null; 18 | } 19 | 20 | const PaymentCardBackground: React.FC = ({ 21 | background, 22 | backgroundPattern 23 | }) => { 24 | let pattern = backgroundPattern === undefined 25 | ? 'spiral' : backgroundPattern; 26 | 27 | return ( 28 |
30 |
36 |
37 | ); 38 | } 39 | 40 | export default PaymentCardBackground; 41 | -------------------------------------------------------------------------------- /src/PaymentCard/PaymentCardBackground/styles.module.scss: -------------------------------------------------------------------------------- 1 | .payment-card-background { 2 | position: absolute; 3 | top: 0; 4 | bottom: 0; 5 | left: 0; 6 | right: 0; 7 | 8 | .svg-background { 9 | position: relative; 10 | width: 100%; 11 | height: 100%; 12 | 13 | &.triangles>svg { 14 | opacity: 0.4; 15 | top: -25%; 16 | } 17 | 18 | &.worldMap>svg { 19 | top: -40%; 20 | opacity: 0.6; 21 | } 22 | 23 | &.spiral>svg { 24 | top: 0; 25 | height: 100%; 26 | width: auto; 27 | opacity: 0.5; 28 | } 29 | } 30 | 31 | svg { 32 | position: relative; 33 | width: 100%; 34 | fill: #ecf0f1; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/PaymentCard/PaymentCardDate/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import styles from './styles.module.scss'; 4 | 5 | export type PaymentCardDateProps = { 6 | date: string 7 | }; 8 | 9 | const PaymentCardDate: React.FC = ({ 10 | date 11 | }) => ( 12 |
13 |
Valid dates
14 |
{date}
15 |
16 | ) 17 | 18 | export default PaymentCardDate; 19 | -------------------------------------------------------------------------------- /src/PaymentCard/PaymentCardDate/styles.module.scss: -------------------------------------------------------------------------------- 1 | .payment-card-date { 2 | position: relative; 3 | z-index: 10; 4 | 5 | display: flex; 6 | flex-flow: column; 7 | 8 | color: #ecf0f1; 9 | 10 | .valid-date { 11 | padding-left: 6.5rem; 12 | 13 | text-transform: uppercase; 14 | 15 | font-size: 10px; 16 | font-weight: bold; 17 | } 18 | 19 | .to-date { 20 | padding-left: 6.5rem; 21 | 22 | text-shadow: 0px 1px 3px rgba(#2c3e50, 0.7); 23 | 24 | font-family: 'OCR A Std'; 25 | font-size: 14px; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/PaymentCard/PaymentCardName/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import styles from './styles.module.scss'; 4 | 5 | export type PaymentCardNameProps = { 6 | name: string; 7 | } 8 | 9 | const PaymentCardName: React.FC = ({ 10 | name 11 | }) => ( 12 |
13 | {name} 14 |
15 | ) 16 | 17 | export default PaymentCardName; 18 | -------------------------------------------------------------------------------- /src/PaymentCard/PaymentCardName/styles.module.scss: -------------------------------------------------------------------------------- 1 | .payment-card-name { 2 | position: relative; 3 | color: #ecf0f1; 4 | z-index: 10; 5 | font-family: 'OCR A Std'; 6 | text-transform: uppercase; 7 | text-shadow: 0px 1px 3px rgba(#2c3e50, 0.7); 8 | margin-left: 1.3rem; 9 | margin-top: 0.5rem; 10 | font-size: 14px; 11 | } 12 | -------------------------------------------------------------------------------- /src/PaymentCard/PaymentCardNumber/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import styles from './styles.module.scss'; 4 | 5 | export type PaymentCardNumberProps = { 6 | number: string; 7 | } 8 | 9 | const PaymentCardNumber: React.FC = ({ 10 | number 11 | }) => ( 12 |
13 | {number} 14 |
15 | ) 16 | 17 | export default PaymentCardNumber; 18 | -------------------------------------------------------------------------------- /src/PaymentCard/PaymentCardNumber/styles.module.scss: -------------------------------------------------------------------------------- 1 | .payment-card-number { 2 | position: relative; 3 | z-index: 10; 4 | 5 | display: flex; 6 | align-items: center; 7 | flex-flow: column; 8 | justify-content: center; 9 | margin-top: 1rem; 10 | 11 | letter-spacing: 0.4px; 12 | 13 | color: #ecf0f1; 14 | 15 | font-family: 'OCR A Std'; 16 | font-size: 21px; 17 | opacity: 0.85; 18 | text-shadow: 0px 1px 3px rgba(#2c3e50, 0.7); 19 | } 20 | -------------------------------------------------------------------------------- /src/PaymentCard/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import cx from 'classnames'; 3 | 4 | import FlippingCard from '../FlippingCard'; 5 | import FlippingCardFront from '../FlippingCard/FlippingCardFront'; 6 | import FlippingCardBack from '../FlippingCard/FlippingCardBack'; 7 | import PaymentCardBackground, { PaymentCardBackgroundProps } from './PaymentCardBackground'; 8 | import IssuerIcon, { IssuerIconProps } from './IssuerIcon'; 9 | import PaymentCardNumber, { PaymentCardNumberProps } from './PaymentCardNumber'; 10 | import Chip from './Chip'; 11 | import PaymentCardDate, { PaymentCardDateProps } from './PaymentCardDate'; 12 | import PaymentCardName, { PaymentCardNameProps } from './PaymentCardName'; 13 | import PaymentCardBack, { PaymentCardBackProps } from './PaymentCardBack'; 14 | import styles from './styles.module.scss'; 15 | 16 | export type PaymentCardProps = { 17 | className?: string; 18 | background?: PaymentCardBackgroundProps['background']; 19 | backgroundPattern?: PaymentCardBackgroundProps['backgroundPattern']; 20 | issuerIcon: IssuerIconProps['icon']; 21 | number: PaymentCardNumberProps['number']; 22 | date: PaymentCardDateProps['date']; 23 | name: PaymentCardNameProps['name']; 24 | cvv: PaymentCardBackProps['cvv']; 25 | } 26 | 27 | const PaymentCard: React.FC = ({ 28 | className, 29 | background, 30 | backgroundPattern, 31 | issuerIcon, 32 | number, 33 | date, 34 | name, 35 | cvv, 36 | ...rest 37 | }) => ( 38 | 45 | 46 | 49 | 51 | 52 | 54 | 56 | 58 | 59 | 60 | 61 | 64 | 66 | 67 | 68 | ) 69 | 70 | export default PaymentCard; 71 | -------------------------------------------------------------------------------- /src/PaymentCard/styles.module.scss: -------------------------------------------------------------------------------- 1 | .payment-card { 2 | width: 21rem; 3 | min-width: 21rem; 4 | max-width: 21rem; 5 | height: 13.5rem; 6 | min-height: 13.5rem; 7 | max-height: 13.5rem; 8 | 9 | .front { 10 | display: flex; 11 | flex-flow: column; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/PaymentCard/svg/spiral.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/PaymentCard/svg/triangles.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/ProductCard/PriceTag/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import styles from './styles.module.scss'; 4 | 5 | export type PriceTagProps = { 6 | price: string; 7 | } 8 | 9 | const PriceTag: React.FC = ({ price }) => ( 10 |
11 | {price} 12 |
13 | ) 14 | 15 | export default PriceTag; 16 | -------------------------------------------------------------------------------- /src/ProductCard/PriceTag/styles.module.scss: -------------------------------------------------------------------------------- 1 | .price-tag { 2 | position: absolute; 3 | background-color: rgba(#2c3e50, 0.95); 4 | color: #ecf0f1; 5 | border-radius: 0.25rem; 6 | padding: 0.125rem 0.5rem; 7 | top: 1rem; 8 | left: 1rem; 9 | } 10 | -------------------------------------------------------------------------------- /src/ProductCard/ProductCardDescription/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import styles from './styles.module.scss'; 4 | 5 | export type ProductCardDescriptionProps = { 6 | productName: string; 7 | description: string; 8 | buttonText?: string; 9 | url: string; 10 | isButtonActive?: boolean; 11 | }; 12 | 13 | const ProductCardDescription: React.FC = ({ 14 | productName, 15 | description, 16 | buttonText, 17 | url, 18 | isButtonActive = true 19 | }) => ( 20 |
21 |
{productName}
22 |

{description}

23 | {buttonText && 24 | } 29 |
30 | ); 31 | 32 | export default ProductCardDescription; 33 | -------------------------------------------------------------------------------- /src/ProductCard/ProductCardDescription/styles.module.scss: -------------------------------------------------------------------------------- 1 | .product-card-description-box { 2 | display: flex; 3 | flex-flow: column; 4 | justify-content: flex-start; 5 | 6 | flex: 0 0 auto; 7 | height: 50%; 8 | padding: 1.5rem 2rem; 9 | box-sizing: border-box; 10 | background-color: #ecf0f1; 11 | 12 | .row { 13 | display: flex; 14 | flex-flow: row; 15 | margin-top: 0.5rem; 16 | } 17 | } 18 | 19 | .product-card-name { 20 | text-transform: uppercase; 21 | font-size: 1.5rem; 22 | letter-spacing: 3px; 23 | color: #2c3e50; 24 | } 25 | 26 | .product-card-description { 27 | color: #2c3e50; 28 | font-size: 0.75rem; 29 | letter-spacing: 2px; 30 | } 31 | 32 | .buy-button { 33 | padding: 0.75rem 1.5rem; 34 | font-size: 0.75rem; 35 | text-decoration: none; 36 | text-transform: uppercase; 37 | letter-spacing: 3px; 38 | color: #ecf0f1; 39 | background-color: #1abc9c; 40 | border-radius: 0.25rem; 41 | transition: 0.25s; 42 | 43 | &:hover, 44 | &:focus, 45 | &:active { 46 | background-color: #16a085; 47 | } 48 | } 49 | 50 | .buy-button-disabled { 51 | padding: 0.75rem 1.5rem; 52 | font-size: 0.75rem; 53 | text-decoration: none; 54 | text-transform: uppercase; 55 | letter-spacing: 3px; 56 | color: rgb(245, 230, 230); 57 | background-color: #cccccc; 58 | border-radius: 0.25rem; 59 | transition: 0.25s; 60 | cursor: default; 61 | } 62 | -------------------------------------------------------------------------------- /src/ProductCard/ProductCardGallery/GalleryToggle/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import cx from 'classnames'; 3 | 4 | import styles from './styles.module.scss'; 5 | 6 | export type GalleryToggleProps = { 7 | icon: string; 8 | action: React.MouseEventHandler; 9 | left?: boolean; 10 | right?: boolean; 11 | }; 12 | 13 | const GalleryToggle: React.FC = ({ 14 | icon, 15 | action, 16 | left=false, 17 | right=false 18 | }) => ( 19 |
29 | {icon} 30 |
31 | ) 32 | 33 | export default GalleryToggle; 34 | -------------------------------------------------------------------------------- /src/ProductCard/ProductCardGallery/GalleryToggle/styles.module.scss: -------------------------------------------------------------------------------- 1 | .gallery-toggle { 2 | position: absolute; 3 | display: flex; 4 | flex-flow: column; 5 | justify-content: center; 6 | align-items: center; 7 | height: 100%; 8 | width: 2rem; 9 | transition: 0.25s; 10 | color: rgba(#ecf0f1, 0.5); 11 | background-color: rgba(#2c3e50, 0.5); 12 | opacity: 0; 13 | cursor: pointer; 14 | 15 | &.left { 16 | left: 0; 17 | border-radius: 0.25rem 0 0 0; 18 | } 19 | 20 | &.right { 21 | right: 0; 22 | border-radius: 0 0.25rem 0 0; 23 | } 24 | 25 | &:hover { 26 | opacity: 1; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/ProductCard/ProductCardGallery/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import cx from 'classnames'; 3 | 4 | import GalleryToggle from './GalleryToggle'; 5 | import styles from './styles.module.scss'; 6 | 7 | export type ProductCardGalleryProps = { 8 | photos: string[]; 9 | } 10 | 11 | const ProductCardGallery: React.FC = ({ photos }) => { 12 | const [activeItem, setActiveItem] = useState(0); 13 | 14 | const rotateRight = () => { 15 | let n = photos.length; 16 | setActiveItem((((activeItem + 1) % n) + n) % n); 17 | } 18 | 19 | const rotateLeft = () => { 20 | let n = photos.length; 21 | setActiveItem((((activeItem - 1) % n) + n) % n); 22 | } 23 | 24 | return ( 25 |
28 | { 29 | photos.map((photo, i) => ( 30 |
39 | )) 40 | } 41 | 46 | 51 |
52 | ); 53 | } 54 | 55 | export default ProductCardGallery; 56 | -------------------------------------------------------------------------------- /src/ProductCard/ProductCardGallery/styles.module.scss: -------------------------------------------------------------------------------- 1 | .product-card-gallery { 2 | position: relative; 3 | display: flex; 4 | flex-flow: row; 5 | flex: 0 0 auto; 6 | height: 50%; 7 | overflow: hidden; 8 | 9 | .gallery-item { 10 | position: absolute; 11 | width: 100%; 12 | height:100%; 13 | 14 | background-size: cover; 15 | background-repeat: no-repeat; 16 | background-position: center; 17 | border-radius: 0.25rem 0.25rem 0 0; 18 | opacity: 0; 19 | 20 | transition: 1s; 21 | } 22 | .active { 23 | width: 100%; 24 | opacity: 1; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/ProductCard/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import cx from 'classnames'; 3 | 4 | import Card from '../Card'; 5 | import ProductCardDescription, { ProductCardDescriptionProps } from './ProductCardDescription'; 6 | import ProductCardGallery, { ProductCardGalleryProps } from './ProductCardGallery'; 7 | import PriceTag, { PriceTagProps } from './PriceTag'; 8 | 9 | import styles from './styles.module.scss'; 10 | 11 | export type ProductCardProps = { 12 | className?: string; 13 | photos: ProductCardGalleryProps['photos']; 14 | price: PriceTagProps['price']; 15 | productName: ProductCardDescriptionProps['productName']; 16 | description: ProductCardDescriptionProps['description']; 17 | buttonText?: ProductCardDescriptionProps['buttonText']; 18 | url: ProductCardDescriptionProps['url']; 19 | } 20 | 21 | const ProductCard: React.FC = ({ 22 | className, 23 | photos, 24 | price, 25 | productName, 26 | description, 27 | buttonText, 28 | url, 29 | ...rest 30 | }) => ( 31 | 38 | 39 | 40 | 45 | 46 | ); 47 | 48 | export default ProductCard; 49 | -------------------------------------------------------------------------------- /src/ProductCard/styles.module.scss: -------------------------------------------------------------------------------- 1 | .product-card { 2 | display: flex; 3 | flex-flow: column; 4 | 5 | width: 300px; 6 | height: 450px; 7 | 8 | box-shadow: 0 10px 30px rgba(#2c3e50, 0.5); 9 | } 10 | -------------------------------------------------------------------------------- /src/RecipeCard/RecipeCardContent/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import styles from './styles.module.scss'; 4 | 5 | export type RecipeCardContentProps = { 6 | thumbnail: string; 7 | href: string; 8 | }; 9 | 10 | const RecipeCardContent: React.FC = ({ 11 | thumbnail, 12 | href 13 | }) =>( 14 | 18 |
19 |
20 |
21 |
22 | ) 23 | 24 | export default RecipeCardContent; 25 | -------------------------------------------------------------------------------- /src/RecipeCard/RecipeCardContent/styles.module.scss: -------------------------------------------------------------------------------- 1 | .recipe-card-content{ 2 | display: flex; 3 | flex-flow:column; 4 | justify-content: flex-start; 5 | align-items: center; 6 | 7 | .thumbnail{ 8 | display:flex; 9 | justify-content:center; 10 | align-items:center; 11 | width: 100%; 12 | 13 | div { 14 | width:100%; 15 | height:300px; 16 | background-size: cover; 17 | background-position: center; 18 | 19 | &:hover{ 20 | opacity:0.7; 21 | } 22 | } 23 | } 24 | } 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/RecipeCard/RecipeCardDesc/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import FontAwesome from 'react-fontawesome'; 3 | 4 | import styles from './styles.module.scss'; 5 | 6 | export type RecipeCardDescProps = { 7 | title: string; 8 | time: string; 9 | servings: string; 10 | likeCallback: React.MouseEventHandler; 11 | }; 12 | 13 | const RecipeCardDesc: React.FC = ({ 14 | title, 15 | time, 16 | servings, 17 | likeCallback 18 | }) =>
19 |
20 | {title} 21 |
22 | 26 | 27 | 28 |
29 | 30 | {time} 31 |
32 |
33 | 34 | {servings} 35 |
36 |
; 37 | 38 | 39 | export default RecipeCardDesc; 40 | -------------------------------------------------------------------------------- /src/RecipeCard/RecipeCardDesc/styles.module.scss: -------------------------------------------------------------------------------- 1 | .recipe-card-desc { 2 | display: flex; 3 | flex-flow: row; 4 | justify-content: center; 5 | color: rgba(0, 0, 0, 0.75); 6 | 7 | 8 | div, 9 | a { 10 | display: flex; 11 | flex-flow: row; 12 | justify-content: center; 13 | align-items: center; 14 | padding: 1rem; 15 | flex: 1 1 auto; 16 | border: none; 17 | white-space: nowrap; 18 | overflow: hidden; 19 | text-overflow: ellipsis; 20 | 21 | &:hover { 22 | background: #F8BBD0; 23 | } 24 | } 25 | 26 | .title { 27 | display: inline; 28 | text-align: center; 29 | } 30 | 31 | a:hover { 32 | cursor: pointer; 33 | } 34 | 35 | .title { 36 | font-size: 1.25rem; 37 | } 38 | 39 | span.fa:not(.fa-heart-o) { 40 | margin-right: 0.25rem; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/RecipeCard/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import cx from 'classnames'; 3 | 4 | import Card from '../Card'; 5 | import RecipeCardContent, { RecipeCardContentProps } from './RecipeCardContent'; 6 | import RecipeCardDesc, { RecipeCardDescProps } from './RecipeCardDesc'; 7 | 8 | import styles from './styles.module.scss'; 9 | 10 | export type RecipeCardProps = { 11 | className?: string; 12 | href: RecipeCardContentProps['href']; 13 | thumbnail: RecipeCardContentProps['thumbnail']; 14 | likeCallback: RecipeCardDescProps['likeCallback']; 15 | title: RecipeCardDescProps['title']; 16 | time: RecipeCardDescProps['time']; 17 | servings: RecipeCardDescProps['servings']; 18 | }; 19 | 20 | const RecipeCard: React.FC = ({ 21 | className, 22 | href, 23 | thumbnail, 24 | likeCallback, 25 | title, 26 | time, 27 | servings, 28 | ...rest 29 | }) => ( 30 | 37 | 40 | 45 | 46 | ) 47 | 48 | export default RecipeCard; 49 | -------------------------------------------------------------------------------- /src/RecipeCard/styles.module.scss: -------------------------------------------------------------------------------- 1 | .recipe-card{ 2 | position:relative; 3 | display:flex; 4 | flex-flow:column; 5 | width: 450px; 6 | overflow: hidden; 7 | 8 | font-size: 1rem; 9 | background-color:#FCE4EC; 10 | color:#546E7A; 11 | } 12 | 13 | a.recipe-card-link{ 14 | display:flex; 15 | text-decoration:none; 16 | } 17 | -------------------------------------------------------------------------------- /src/TaggedContentCard/CardContent/index.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable jsx-a11y/alt-text */ 2 | import React from 'react'; 3 | 4 | import styles from './styles.module.scss'; 5 | 6 | export type CardContentProps = { 7 | thumbnail: string; 8 | title: string; 9 | description: string; 10 | tags: (string | (() => React.ReactNode))[]; 11 | }; 12 | 13 | const CardContent: React.FC = ({ 14 | thumbnail, 15 | title, 16 | description, 17 | tags 18 | }) => ( 19 |
22 |
23 | 24 |
25 | 26 |
27 |
28 | {title} 29 |
30 |
31 | {description} 32 |
33 |
34 | {tags.map((tag, i) => ( 35 | 39 | {typeof tag === "function" ? tag() : tag} 40 | 41 | ))} 42 |
43 |
44 |
45 | ) 46 | 47 | export default CardContent; 48 | -------------------------------------------------------------------------------- /src/TaggedContentCard/CardContent/styles.module.scss: -------------------------------------------------------------------------------- 1 | .card-content { 2 | display: flex; 3 | flex-flow: column; 4 | justify-content: flex-start; 5 | align-items: center; 6 | width: 100%; 7 | height: 100%; 8 | z-index: 10; 9 | 10 | .thumbnail { 11 | display: flex; 12 | height: 50%; 13 | width: 100%; 14 | justify-content: center; 15 | align-items: center; 16 | padding: 2rem 0 0 0; 17 | flex: 1 1 auto; 18 | 19 | img { 20 | width: auto; 21 | height: 100%; 22 | object-fit: cover; 23 | box-shadow: 0 16px 24px 2px rgba(#2c3e50,0.24), 0 6px 30px 5px rgba(#2c3e50,0.22), 0 8px 10px -5px rgba(#2c3e50,0.4) 24 | } 25 | } 26 | 27 | .text { 28 | display: flex; 29 | flex-flow: column; 30 | justify-content: center; 31 | width: 100%; 32 | padding: 1rem; 33 | margin-top: 1rem; 34 | box-sizing: border-box; 35 | border-top: 1px solid rgba(#ecf0f1, 0.2); 36 | background-color: rgba(0, 0, 0, 0.2); 37 | color: #ecf0f1; 38 | 39 | .description { 40 | color: rgba(#ecf0f1, 0.8); 41 | font-size: 0.875rem; 42 | } 43 | } 44 | 45 | .tags { 46 | display: flex; 47 | justify-content: flex-end; 48 | width: 100%; 49 | 50 | .tag { 51 | margin: 0 0.5rem; 52 | padding: 0.25rem 0.75rem; 53 | background-color: rgba(0, 0, 0, 0.5); 54 | border-radius: 2rem; 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/TaggedContentCard/CardOverlay/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import styles from './styles.module.scss'; 4 | 5 | export type CardOverlayProps = { 6 | thumbnail: string; 7 | }; 8 | 9 | const CardOverlay: React.FC = ({ thumbnail }) => ( 10 |
13 |
18 |
19 | ); 20 | 21 | export default CardOverlay; 22 | -------------------------------------------------------------------------------- /src/TaggedContentCard/CardOverlay/styles.module.scss: -------------------------------------------------------------------------------- 1 | .card-overlay { 2 | position: absolute; 3 | width: 100%; 4 | height: 100%; 5 | overflow: hidden; 6 | border-radius: 0.25rem; 7 | 8 | .overlay { 9 | position: absolute; 10 | width: 100%; 11 | height: 100%; 12 | background-repeat: no-repeat; 13 | background-position: center; 14 | background-size: cover; 15 | filter: blur(20px) brightness(0.75); 16 | backface-visibility: hidden; 17 | perspective: 1000; 18 | transform: translate3d(0,0,0); 19 | transform: translateZ(0); 20 | will-change: transform; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/TaggedContentCard/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import cx from 'classnames'; 3 | 4 | import Card from '../Card'; 5 | import CardContent, { CardContentProps } from './CardContent'; 6 | import CardOverlay from './CardOverlay'; 7 | import styles from './styles.module.scss'; 8 | 9 | export type TaggedContentCardProps = { 10 | className?: string; 11 | href: string; 12 | thumbnail: CardContentProps['thumbnail']; 13 | title: CardContentProps['title']; 14 | description: CardContentProps['description']; 15 | tags: CardContentProps['tags']; 16 | }; 17 | 18 | const TaggedContentCard: React.FC = ({ 19 | className, 20 | href, 21 | thumbnail, 22 | title, 23 | description, 24 | tags, 25 | ...rest 26 | }) => ( 27 | 28 | 35 | 37 | 42 | 43 | 44 | ); 45 | 46 | export default TaggedContentCard; 47 | -------------------------------------------------------------------------------- /src/TaggedContentCard/styles.module.scss: -------------------------------------------------------------------------------- 1 | a.card-link { 2 | display: flex; 3 | text-decoration: none; 4 | } 5 | 6 | .tagged-content-card { 7 | position: relative; 8 | display: flex; 9 | flex-flow: column; 10 | 11 | width: 420px; 12 | height: 350px; 13 | 14 | background-color: #2c3e50; 15 | transition: 0.5s; 16 | 17 | &:hover { 18 | transform: scale(1.1); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/UserCard/UserCardAvatar/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import styles from './styles.module.scss'; 4 | 5 | export type UserCardAvatarProps = { 6 | src: string; 7 | }; 8 | 9 | const UserCardAvatar: React.FC = ({ src }) => 10 | ( 11 |
14 | ) 15 | 16 | export default UserCardAvatar; 17 | -------------------------------------------------------------------------------- /src/UserCard/UserCardAvatar/styles.module.scss: -------------------------------------------------------------------------------- 1 | .user-card-avatar { 2 | position: absolute; 3 | display: flex; 4 | flex: 0 0 auto; 5 | height: 6rem; 6 | width: 6rem; 7 | top: calc(50% - 3rem); 8 | left: calc(50% - 3rem); 9 | 10 | background-size: cover; 11 | background-repeat: no-repeat; 12 | background-position: center; 13 | border-radius: 100%; 14 | z-index: 10; 15 | } 16 | -------------------------------------------------------------------------------- /src/UserCard/UserCardBody/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import styles from './styles.module.scss'; 4 | 5 | export type UserCardBodyProps = { 6 | name: string; 7 | positionName: string; 8 | } 9 | 10 | const UserCardBody: React.FC = ({ name, positionName, children }) => ( 11 |
14 |
15 | {name} 16 |
17 |
18 | {positionName} 19 |
20 | {children} 21 |
22 | ) 23 | 24 | export default UserCardBody; 25 | -------------------------------------------------------------------------------- /src/UserCard/UserCardBody/styles.module.scss: -------------------------------------------------------------------------------- 1 | .user-card-body { 2 | padding: 1rem; 3 | padding-top: 4rem; 4 | 5 | .user-card-name { 6 | display: flex; 7 | justify-content: center; 8 | font-size: 1.25rem; 9 | margin-bottom: 0.25rem; 10 | color: #2c3e50; 11 | } 12 | 13 | .user-card-position-name { 14 | display: flex; 15 | justify-content: center; 16 | font-size: 0.75rem; 17 | color: #7f8c8d; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/UserCard/UserCardHeader/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import styles from './styles.module.scss'; 4 | 5 | export type UserCardHeaderProps = { 6 | src: string; 7 | }; 8 | 9 | const UserCardHeader: React.FC = ({ src }) => ( 10 |
14 |
15 | ) 16 | 17 | export default UserCardHeader; 18 | -------------------------------------------------------------------------------- /src/UserCard/UserCardHeader/styles.module.scss: -------------------------------------------------------------------------------- 1 | .user-card-header { 2 | position: relative; 3 | display: flex; 4 | flex: 0 0 auto; 5 | height: 50%; 6 | 7 | background-size: cover; 8 | background-repeat: no-repeat; 9 | background-position: center; 10 | border-radius: 0.25rem 0.25rem 0 0; 11 | } 12 | -------------------------------------------------------------------------------- /src/UserCard/UserCardStats/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import styles from './styles.module.scss'; 4 | 5 | export type UserCardStatsProps = { 6 | stats?: { 7 | name: string; 8 | value: number | string; 9 | }[]; 10 | } 11 | 12 | const UserCardStats: React.FC = ({ stats }) => ( 13 |
16 | {stats?.map((stat) => { 17 | return ( 18 |
22 |
23 | {stat.value} 24 |
25 |
26 | {stat.name} 27 |
28 |
29 | ); 30 | })} 31 |
32 | ) 33 | 34 | export default UserCardStats; 35 | -------------------------------------------------------------------------------- /src/UserCard/UserCardStats/styles.module.scss: -------------------------------------------------------------------------------- 1 | .user-card-stats { 2 | display: flex; 3 | flex-flow: row; 4 | justify-content: center; 5 | 6 | font-size: 0.75rem; 7 | } 8 | 9 | .stat-container { 10 | border-top: 2px solid rgba(#7f8c8d, 0.2); 11 | border-right: 2px solid rgba(#7f8c8d, 0.2); 12 | min-width: 4rem; 13 | padding: 0.25rem; 14 | 15 | &:last-child { 16 | border-right: none; 17 | } 18 | } 19 | 20 | .stat-name { 21 | display: flex; 22 | flex-flow: row; 23 | justify-content: center; 24 | color: #7f8c8d; 25 | } 26 | 27 | .stat-value { 28 | display: flex; 29 | flex-flow: row; 30 | justify-content: center; 31 | color: #2c3e50; 32 | font-weight: bold; 33 | } 34 | -------------------------------------------------------------------------------- /src/UserCard/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import Card, { CardProps } from '../Card'; 4 | import UserCardHeader, { UserCardHeaderProps } from './UserCardHeader'; 5 | import UserCardAvatar, { UserCardAvatarProps } from './UserCardAvatar'; 6 | import UserCardBody, { UserCardBodyProps } from './UserCardBody'; 7 | import UserCardStats, { UserCardStatsProps } from './UserCardStats'; 8 | 9 | import styles from './styles.module.scss'; 10 | 11 | export type UserCardProps = CardProps & { 12 | href: string; 13 | header: UserCardHeaderProps['src']; 14 | avatar: UserCardAvatarProps['src']; 15 | name: UserCardBodyProps['name']; 16 | positionName: UserCardBodyProps['positionName']; 17 | stats?: UserCardStatsProps['stats']; 18 | }; 19 | 20 | const UserCard: React.FC = ({ 21 | className, 22 | href, 23 | header, 24 | avatar, 25 | name, 26 | positionName, 27 | stats, 28 | children, 29 | ...rest 30 | }) => 34 | 38 | 41 | 44 | 48 | {children} 49 | 50 | { 51 | stats !== undefined 52 | && 55 | } 56 | 57 | ; 58 | 59 | export default UserCard; -------------------------------------------------------------------------------- /src/UserCard/styles.module.scss: -------------------------------------------------------------------------------- 1 | a.card-link { 2 | display: flex; 3 | text-decoration: none; 4 | } 5 | -------------------------------------------------------------------------------- /src/__snapshots__/storybook.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Storyshots Cards/Base card Custom Class 1`] = ` 4 | Array [ 5 | , 15 |
18 | Card with custom className 19 |
, 20 | ] 21 | `; 22 | 23 | exports[`Storyshots Cards/Base card Example 1 1`] = ` 24 |
27 | Card content 28 |
29 | `; 30 | 31 | exports[`Storyshots Cards/Base card Floating Card 1`] = ` 32 |
35 | Hover over me 36 |
37 | `; 38 | 39 | exports[`Storyshots Cards/Crypto card Example 1 1`] = ` 40 |
43 |
46 |
49 | Bitcoin 50 |
51 |
54 | $8850.32 55 |
56 |
57 |
60 |
63 |
66 | 69 |
70 |
73 | BTC 74 |
75 |
76 |
79 |
82 | (8.54%) $563.47 83 | 84 | ▲ 85 |
86 |
87 |
88 |
91 |
92 | `; 93 | 94 | exports[`Storyshots Cards/Crypto card Example 2 1`] = ` 95 |
98 |
101 |
104 | Ethereum 105 |
106 |
109 | $766.12 110 |
111 |
112 |
115 |
118 |
121 | 124 |
125 |
128 | ETH 129 |
130 |
131 |
134 |
137 | (7.32%) $14.94 138 | 139 | ▲ 140 |
141 |
142 |
143 |
146 |
147 | `; 148 | 149 | exports[`Storyshots Cards/Crypto card Example 3 1`] = ` 150 |
153 |
156 |
159 | Litecoin 160 |
161 |
164 | $137.92 165 |
166 |
167 |
170 |
173 |
176 | 179 |
180 |
183 | LTC 184 |
185 |
186 |
189 |
192 | (-5.12%) $6.34 193 | 194 | ▼ 195 |
196 |
197 |
198 |
201 |
202 | `; 203 | 204 | exports[`Storyshots Cards/Flipping card Example 1 1`] = ` 205 |
208 |
211 |
214 |
225 |
226 |
229 |
240 |
241 |
242 |
243 | `; 244 | 245 | exports[`Storyshots Cards/Flipping card Example 2 1`] = ` 246 |
249 |
252 |
255 |
266 |
267 |
270 |
281 |
282 |
283 |
284 | `; 285 | 286 | exports[`Storyshots Cards/Flipping card Example 3 1`] = ` 287 |
290 |
293 |
296 |
307 |
308 |
311 |
322 |
323 |
324 |
325 | `; 326 | 327 | exports[`Storyshots Cards/News header card Example 1 1`] = ` 328 | 332 |
335 |
338 |
341 |
344 |
351 |
352 |
353 |
356 |
359 | Polish mountaineers on the top 360 |
361 |
364 |
367 | Daily Sport 368 |
369 |
372 | Feb 2, 2018 373 |
374 |
375 |
378 | 381 | nature 382 | 383 | 386 | photo 387 | 388 | 391 | 392 | component 393 | 394 | 395 |
396 |
397 |
398 |
399 | `; 400 | 401 | exports[`Storyshots Cards/News header card Example 2 1`] = ` 402 | 406 |
409 |
412 |
415 |
418 |
425 |
426 |
427 |
430 |
433 | Most poisonous dishes you must avoid 434 |
435 |
438 |
441 | Daily Health 442 |
443 |
446 | Jan 2, 2018 447 |
448 |
449 |
452 |
453 |
454 |
455 | `; 456 | 457 | exports[`Storyshots Cards/News header card Example 3 1`] = ` 458 | 462 |
465 |
468 |
471 |
474 |
481 |
482 |
483 |
486 |
489 | Cool looking test tubes 490 |
491 |
494 |
497 | Daily Science 498 |
499 |
502 | Mar 2, 2018 503 |
504 |
505 |
508 |
509 |
510 |
511 | `; 512 | 513 | exports[`Storyshots Cards/Payment card Example 1 1`] = ` 514 |
517 |
520 |
523 |
531 |
539 |
540 |
543 | 546 |
547 |
550 | 553 |
554 |
557 | 4445 8509 0753 1365 558 |
559 |
562 |
565 | Valid dates 566 |
567 |
570 | 12/18 571 |
572 |
573 |
576 | Jean Pierre Polnareff 577 |
578 |
579 |
582 |
590 |
598 |
599 |
602 | 605 |
608 | 611 | 213 612 | 613 |
614 |
615 |
616 |
617 |
618 | `; 619 | 620 | exports[`Storyshots Cards/Payment card Example 2 1`] = ` 621 |
624 |
627 |
630 |
638 |
646 |
647 |
650 | 653 |
654 |
657 | 660 |
661 |
664 | 3421 7270 0864 956 665 |
666 |
669 |
672 | Valid dates 673 |
674 |
677 | 02/22 678 |
679 |
680 |
683 | Jotaro Kujo 684 |
685 |
686 |
689 |
697 |
705 |
706 |
709 | 712 |
715 | 718 | 880 719 | 720 |
721 |
722 |
723 |
724 |
725 | `; 726 | 727 | exports[`Storyshots Cards/Product card Example 1 1`] = ` 728 |
731 |
734 |
742 |
750 |
758 |
762 | > 763 |
764 |
768 | < 769 |
770 |
771 |
774 | $99 775 |
776 |
779 |
782 | Headphones 783 |
784 |

787 | Donec lectus nulla, molestie aliquam nisl vitae, tempor placerat magna. Morbi dignissim in felis vel aliquet. 788 |

789 | 799 |
800 |
801 | `; 802 | 803 | exports[`Storyshots Cards/Product card Example 2 1`] = ` 804 |
807 |
810 |
818 |
826 |
834 |
838 | > 839 |
840 |
844 | < 845 |
846 |
847 |
850 | $1.50 851 |
852 |
855 |
858 | Cupcake 859 |
860 |

863 | Nullam velit leo, pulvinar non metus feugiat, pharetra ornare enim. Vivamus ac quam a purus venenatis fringilla. 864 |

865 |
866 |
867 | `; 868 | 869 | exports[`Storyshots Cards/Recipe card Example 1 1`] = ` 870 |
873 | 877 |
880 |
887 |
888 |
889 |
892 |
895 | Fluffy pancakes 896 |
897 | 900 | 904 | 905 |
908 | 912 | 0:30 913 |
914 |
917 | 921 | 3-5 922 |
923 |
924 |
925 | `; 926 | 927 | exports[`Storyshots Cards/Recipe card Example 2 1`] = ` 928 |
931 | 935 |
938 |
945 |
946 |
947 |
950 |
953 | Cupcakes 954 |
955 | 958 | 962 | 963 |
966 | 970 | 1:30 971 |
972 |
975 | 979 | 4-6 980 |
981 |
982 |
983 | `; 984 | 985 | exports[`Storyshots Cards/Recipe card Example 3 1`] = ` 986 |
989 | 993 |
996 |
1003 |
1004 |
1005 |
1008 |
1011 | Macarons 1012 |
1013 | 1016 | 1020 | 1021 |
1024 | 1028 | 2:00 1029 |
1030 |
1033 | 1037 | 7-10 1038 |
1039 |
1040 |
1041 | `; 1042 | 1043 | exports[`Storyshots Cards/Tagged content card Example 1 1`] = ` 1044 | 1048 |
1051 |
1054 |
1062 |
1063 |
1066 |
1069 | 1072 |
1073 |
1076 |
1079 | Cheesecake 1080 |
1081 |
1084 | a cake with raspberries 1085 |
1086 |
1089 | 1092 | food 1093 | 1094 | 1097 | cake 1098 | 1099 | 1102 | fruits 1103 | 1104 |
1105 |
1106 |
1107 |
1108 |
1109 | `; 1110 | 1111 | exports[`Storyshots Cards/Tagged content card Example 2 1`] = ` 1112 | 1116 |
1119 |
1122 |
1130 |
1131 |
1134 |
1137 | 1140 |
1141 |
1144 |
1147 | Cupcakes 1148 |
1149 |
1152 | a bite-sized cake 1153 |
1154 |
1157 | 1160 | food 1161 | 1162 | 1165 | cake 1166 | 1167 | 1170 | icing 1171 | 1172 |
1173 |
1174 |
1175 |
1176 |
1177 | `; 1178 | 1179 | exports[`Storyshots Cards/Tagged content card Example 3 1`] = ` 1180 | 1184 |
1187 |
1190 |
1198 |
1199 |
1202 |
1205 | 1208 |
1209 |
1212 |
1215 | Burger 1216 |
1217 |
1220 | the cornerstone of every nutritious breakfast 1221 |
1222 |
1225 | 1228 | food 1229 | 1230 | 1233 | sandwich 1234 | 1235 | 1238 | meat 1239 | 1240 |
1241 |
1242 |
1243 |
1244 |
1245 | `; 1246 | 1247 | exports[`Storyshots Cards/User card Example 1 1`] = ` 1248 | 1252 |
1255 |
1263 |
1271 |
1274 |
1277 | Justin Case 1278 |
1279 |
1282 | Software Developer 1283 |
1284 |
1285 |
1288 |
1291 |
1294 | 21 1295 |
1296 |
1299 | followers 1300 |
1301 |
1302 |
1305 |
1308 | 37 1309 |
1310 |
1313 | following 1314 |
1315 |
1316 |
1319 |
1322 | 117 1323 |
1324 |
1327 | posts 1328 |
1329 |
1330 |
1331 |
1332 |
1333 | `; 1334 | 1335 | exports[`Storyshots Cards/User card Example 2 1`] = ` 1336 | 1340 |
1343 |
1351 |
1359 |
1362 |
1365 | Frank Hepsfield 1366 |
1367 |
1370 | Software Engineering Manager 1371 |
1372 |
1373 |
1374 |
1375 | `; 1376 | 1377 | exports[`Storyshots Cards/User card Example 3 1`] = ` 1378 | 1382 |
1385 |
1393 |
1401 |
1404 |
1407 | Joseph Cheps 1408 |
1409 |
1412 | Firmware Engineer 1413 |
1414 |
1415 |
1418 |
1421 |
1424 | 365 1425 |
1426 |
1429 | commits 1430 |
1431 |
1432 |
1435 |
1438 | 110 1439 |
1440 |
1443 | stars 1444 |
1445 |
1446 |
1449 |
1452 | 54 1453 |
1454 |
1457 | repositories 1458 |
1459 |
1460 |
1461 |
1462 |
1463 | `; 1464 | 1465 | exports[`Storyshots Demo Demo 1`] = ` 1466 |
1469 | 1483 |

1484 | Use the links on the left to see the other components. 1485 |

1486 |
1489 | 1493 |
1496 |
1504 |
1512 |
1515 |
1518 | Justin Case 1519 |
1520 |
1523 | Software Developer 1524 |
1525 |
1526 |
1529 |
1532 |
1535 | 21 1536 |
1537 |
1540 | followers 1541 |
1542 |
1543 |
1546 |
1549 | 37 1550 |
1551 |
1554 | following 1555 |
1556 |
1557 |
1560 |
1563 | 117 1564 |
1565 |
1568 | posts 1569 |
1570 |
1571 |
1572 |
1573 |
1574 | 1578 |
1581 |
1589 |
1597 |
1600 |
1603 | Frank Hepsfield 1604 |
1605 |
1608 | Software Engineering Manager 1609 |
1610 |
1611 |
1612 |
1613 | 1617 | 1700 | `; 1701 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import UserCard from './UserCard'; 2 | import ProductCard from './ProductCard'; 3 | import TaggedContentCard from './TaggedContentCard'; 4 | import FlippingCard from './FlippingCard'; 5 | import FlippingCardFront from './FlippingCard/FlippingCardFront'; 6 | import FlippingCardBack from './FlippingCard/FlippingCardBack'; 7 | import PaymentCard from './PaymentCard'; 8 | import RecipeCard from './RecipeCard'; 9 | import NewsHeaderCard from './NewsHeaderCard'; 10 | import CryptoCard from './CryptoCard'; 11 | 12 | export { 13 | UserCard, 14 | ProductCard, 15 | TaggedContentCard, 16 | FlippingCard, 17 | FlippingCardFront, 18 | FlippingCardBack, 19 | PaymentCard, 20 | RecipeCard, 21 | NewsHeaderCard, 22 | CryptoCard, 23 | }; 24 | -------------------------------------------------------------------------------- /src/stories/Card.stories.tsx: -------------------------------------------------------------------------------- 1 | import Card from "../Card" 2 | 3 | export default { 4 | title: 'Cards/Base card' 5 | }; 6 | 7 | export const Example1 = () => 8 | Card content 9 | 10 | 11 | export const FloatingCard = () => 12 | Hover over me 13 | 14 | 15 | export const CustomClass = () => <> 16 | 26 | 27 | Card with custom className 28 | 29 | -------------------------------------------------------------------------------- /src/stories/CryptoCard.stories.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable jsx-a11y/alt-text */ 2 | import CryptoCard from "../CryptoCard"; 3 | 4 | export default { 5 | title: "Cards/Crypto card", 6 | component: CryptoCard, 7 | parameters: { 8 | backgrounds: { default: 'dark' } 9 | } 10 | } 11 | 12 | export const Example1 = () => } 16 | currencyShortName='BTC' 17 | trend='(8.54%) $563.47' 18 | trendDirection={1} 19 | chartData={[9200, 5720, 8100, 6734, 7054, 7832, 6421, 7383, 8697, 8850]} 20 | />; 21 | 22 | export const Example2 = () => } 26 | currencyShortName='ETH' 27 | trend='(7.32%) $14.94' 28 | trendDirection={1} 29 | chartData={[760, 500, 800, 670, 820, 620, 796, 766]} 30 | chartColor='#9b59b6' 31 | />; 32 | 33 | export const Example3 = () => } 37 | currencyShortName='LTC' 38 | trend='(-5.12%) $6.34' 39 | trendDirection={-1} 40 | chartData={[90, 120, 115, 85, 75, 92, 70, 101, 111, 137]} 41 | chartColor='#ecf0f1' 42 | />; -------------------------------------------------------------------------------- /src/stories/Demo.module.scss: -------------------------------------------------------------------------------- 1 | .demo { 2 | display: flex; 3 | flex-flow: column; 4 | 5 | .links { 6 | display: flex; 7 | flex-flow: column; 8 | justify-content: center; 9 | align-items: center; 10 | font-size: 24px; 11 | 12 | a { 13 | padding: 1em 2em; 14 | border-radius: 1em; 15 | color: #ecf0f1; 16 | background: rgba(#9b59b6, 0.8); 17 | margin: 1rem; 18 | } 19 | } 20 | 21 | p { 22 | text-align: center; 23 | font-size: 30px; 24 | } 25 | 26 | .cards { 27 | display: flex; 28 | flex-flow: row wrap; 29 | justify-content: space-between; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/stories/Demo.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import UserCard from "../UserCard"; 4 | import styles from './Demo.module.scss'; 5 | 6 | export default { 7 | title: 'Demo' 8 | } 9 | 10 | export const Demo = () =>
11 | 15 | 16 |

17 | Use the links on the left to see the other components. 18 |

19 | 20 |
21 | 43 | 44 | 52 | 53 | 75 |
76 |
; -------------------------------------------------------------------------------- /src/stories/FlippingCard.stories.tsx: -------------------------------------------------------------------------------- 1 | import FlippingCard from "../FlippingCard"; 2 | import FlippingCardBack from "../FlippingCard/FlippingCardBack"; 3 | import FlippingCardFront from "../FlippingCard/FlippingCardFront"; 4 | 5 | export default { 6 | title: 'Cards/Flipping card', 7 | component: FlippingCard, 8 | }; 9 | 10 | export const Example1 = () => 11 | 12 |
20 |
21 |
22 | 23 |
31 |
32 |
33 |
; 34 | 35 | export const Example2 = () => 36 | 37 |
45 |
46 |
47 | 48 |
56 |
57 |
58 |
; 59 | 60 | export const Example3 = () => 61 | 62 |
70 |
71 |
72 | 73 |
81 |
82 |
83 |
; -------------------------------------------------------------------------------- /src/stories/NewsHeaderCard.stories.tsx: -------------------------------------------------------------------------------- 1 | import NewsHeaderCard from "../NewsHeaderCard"; 2 | 3 | export default { 4 | title: "Cards/News header card", 5 | component: NewsHeaderCard 6 | } 7 | 8 | export const Example1 = () => (component)]} 15 | /> 16 | 17 | export const Example2 = () => 25 | 26 | export const Example3 = () => ; -------------------------------------------------------------------------------- /src/stories/PaymentCard.stories.tsx: -------------------------------------------------------------------------------- 1 | import PaymentCard from "../PaymentCard"; 2 | 3 | export default { 4 | title: 'Cards/Payment card', 5 | component: PaymentCard, 6 | }; 7 | 8 | export const Example1 = () => ; 16 | 17 | export const Example2 = () => ; 26 | -------------------------------------------------------------------------------- /src/stories/ProductCard.stories.tsx: -------------------------------------------------------------------------------- 1 | import ProductCard from "../ProductCard"; 2 | 3 | export default { 4 | title: "Cards/Product card", 5 | component: ProductCard, 6 | }; 7 | 8 | export const Example1 = () => ; 20 | 21 | export const Example2 = () => -------------------------------------------------------------------------------- /src/stories/RecipeCard.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import RecipeCard from "../RecipeCard"; 3 | 4 | export default { 5 | title: "Cards/Recipe card", 6 | component: RecipeCard, 7 | argTypes: { 8 | onLike: { action: 'Added to favourites' } 9 | } 10 | } 11 | 12 | type StoryProps = { 13 | onLike: React.MouseEventHandler; 14 | } 15 | 16 | export const Example1 = ({ onLike }: StoryProps) => ; 24 | 25 | export const Example2 = ({ onLike }: StoryProps) => ; 33 | 34 | export const Example3 = ({ onLike }: StoryProps) => ; -------------------------------------------------------------------------------- /src/stories/TaggedContentCard.stories.tsx: -------------------------------------------------------------------------------- 1 | import TaggedContentCard from "../TaggedContentCard"; 2 | 3 | export default { 4 | title: 'Cards/Tagged content card', 5 | component: TaggedContentCard 6 | } 7 | 8 | export const Example1 = () => ; 19 | 20 | export const Example2 = () => ; 31 | 32 | export const Example3 = () => ; -------------------------------------------------------------------------------- /src/stories/UserCard.stories.tsx: -------------------------------------------------------------------------------- 1 | import UserCard from "../UserCard"; 2 | 3 | export default { 4 | title: 'Cards/User card', 5 | component: UserCard 6 | } 7 | 8 | export const Example1 = () => 30 | 31 | export const Example2 = () => 39 | 40 | export const Example3 = () => 62 | 63 | -------------------------------------------------------------------------------- /src/storybook.test.ts: -------------------------------------------------------------------------------- 1 | import initStoryshots from '@storybook/addon-storyshots'; 2 | 3 | jest.mock('react-chartjs-2', () => ({ 4 | Line: () => null, 5 | })); 6 | 7 | initStoryshots(); -------------------------------------------------------------------------------- /src/typings.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.svg' { 2 | import * as React from 'react'; 3 | 4 | export const ReactComponent: React.FunctionComponent & { title?: string }>; 7 | 8 | const src: string; 9 | export default src; 10 | } 11 | 12 | declare module "*.module.scss" { 13 | const classes: { readonly [key: string]: string }; 14 | export default classes; 15 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "noFallthroughCasesInSwitch": true, 16 | "module": "esnext", 17 | "moduleResolution": "node", 18 | "resolveJsonModule": true, 19 | "isolatedModules": true, 20 | "noEmit": true, 21 | "jsx": "react-jsx", 22 | "declaration": true, 23 | "sourceMap": true, 24 | "outDir": "dist" 25 | }, 26 | "ts-node": { 27 | "compilerOptions": { 28 | "module": "CommonJS" 29 | } 30 | }, 31 | "include": [ 32 | "src" 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /webpack.config.ts: -------------------------------------------------------------------------------- 1 | import * as path from "path"; 2 | import * as webpack from "webpack"; 3 | import nodeExternals from 'webpack-node-externals'; 4 | 5 | const SRC_DIR = path.resolve(__dirname, "src"); 6 | const DIST_DIR = path.resolve(__dirname, "dist"); 7 | 8 | const config: webpack.Configuration = { 9 | mode: "production", 10 | entry: path.resolve(SRC_DIR, "index.tsx"), 11 | output: { 12 | path: DIST_DIR, 13 | filename: "index.js", 14 | library: "reactUiCards", 15 | libraryTarget: "umd" 16 | }, 17 | devtool: "source-map", 18 | resolve: { 19 | extensions: [".ts", ".tsx", ".js"], 20 | }, 21 | module: { 22 | rules: [ 23 | { 24 | test: /\.tsx?/, 25 | loader: "ts-loader", 26 | options: { 27 | compilerOptions: { 28 | noEmit: false 29 | } 30 | }, 31 | exclude: /node_modules/, 32 | }, 33 | { 34 | test: /.scss$/, 35 | use: [ 36 | "style-loader", 37 | { 38 | loader: "css-loader", 39 | options: { 40 | importLoaders: 1, 41 | modules: { 42 | auto: true, 43 | localIdentName: "[path][name]__[local]--[hash:base64:5]" 44 | }, 45 | }, 46 | }, 47 | "sass-loader", 48 | ], 49 | }, 50 | { 51 | test: /\.svg$/, 52 | use: "raw-loader", 53 | }, 54 | ], 55 | }, 56 | externals: [nodeExternals()], 57 | target: "web", 58 | }; 59 | 60 | export default config; 61 | --------------------------------------------------------------------------------