├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── custom.md │ └── feature_request.md ├── .gitignore ├── .huskyrc ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── jest.config.js ├── lint-staged.config.js ├── package.json ├── packages ├── documentation │ ├── .gitignore │ ├── README.md │ ├── babel.config.js │ ├── docs │ │ ├── api.md │ │ ├── intro.md │ │ └── tutorial-basics │ │ │ ├── Header-stories.mdx │ │ │ ├── _category_.json │ │ │ ├── component-stories.mdx │ │ │ ├── image-stories.mdx │ │ │ ├── image-video-stories.mdx │ │ │ ├── playground.mdx │ │ │ ├── see-more.mdx │ │ │ └── video-stories.mdx │ ├── docusaurus.config.js │ ├── package.json │ ├── sidebars.js │ ├── src │ │ ├── components │ │ │ ├── ComponentStories.js │ │ │ ├── HeaderStories.js │ │ │ ├── HomepageFeatures.js │ │ │ ├── HomepageFeatures.module.css │ │ │ ├── ImageStories.js │ │ │ ├── ImageVideoStories.js │ │ │ ├── SeeMoreStories.js │ │ │ ├── VideoStories.js │ │ │ └── styles.css │ │ ├── css │ │ │ └── custom.css │ │ ├── pages │ │ │ ├── index.js │ │ │ ├── index.module.css │ │ │ └── markdown-page.md │ │ └── theme │ │ │ └── ReactLiveScope │ │ │ └── index.js │ └── static │ │ ├── .nojekyll │ │ └── img │ │ ├── 1.jpg │ │ ├── 2.jpg │ │ ├── 3.jpg │ │ ├── android-chrome-192x192.png │ │ ├── android-chrome-512x512.png │ │ ├── apple-touch-icon.png │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── favicon.ico │ │ ├── logo.jpg │ │ ├── logo.svg_ │ │ ├── site.webmanifest │ │ └── tutorial │ │ ├── docsVersionDropdown.png │ │ └── localeDropdown.png └── stories │ ├── README.md │ ├── package.json │ ├── rollup.config.js │ ├── src │ ├── Components │ │ ├── Actions │ │ │ ├── Actions.component.tsx │ │ │ ├── Actions.constants.ts │ │ │ ├── Actions.styles.css │ │ │ └── index.ts │ │ ├── CustomComponent │ │ │ ├── CustomComponents.component.tsx │ │ │ ├── CustomComponents.styles.css │ │ │ └── index.ts │ │ ├── Image │ │ │ ├── Image.component.tsx │ │ │ ├── Image.styles.css │ │ │ └── index.ts │ │ ├── Progress │ │ │ ├── Progress.component.tsx │ │ │ ├── index.ts │ │ │ └── progress.styles.css │ │ ├── ProgressBar │ │ │ ├── ProgressBar.component.tsx │ │ │ ├── ProgressBar.styles.css │ │ │ └── index.ts │ │ ├── SeeMore │ │ │ ├── SeeMore.component.tsx │ │ │ ├── SeeMore.styles.css │ │ │ └── index.ts │ │ ├── SeeMoreComponent │ │ │ ├── SeeMoreComponent.component.tsx │ │ │ ├── SeeMoreComponent.styles.css │ │ │ └── index.ts │ │ ├── SoundIcon │ │ │ ├── SoundIcon.component.tsx │ │ │ └── index.ts │ │ ├── Story │ │ │ ├── Story.component.tsx │ │ │ ├── Story.constants.ts │ │ │ ├── Story.styles.css │ │ │ └── index.ts │ │ ├── Video │ │ │ ├── Video.component.tsx │ │ │ ├── Video.styles.css │ │ │ └── index.ts │ │ └── index.ts │ ├── Contexts │ │ └── index.ts │ ├── Hooks │ │ └── index.ts │ ├── declerations.d.ts │ ├── index.tsx │ ├── styles.css │ ├── types.d.ts │ └── utilities.ts │ └── tsconfig.json ├── prettier.config.js ├── readme.md └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | end_of_line = lf 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | 15 | [.md] 16 | insert_final_newline = false 17 | trim_trailing_whitespace = false 18 | 19 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | **/dist/** 2 | **/node_modules/** 3 | 4 | /node_modules 5 | /dist 6 | /lib 7 | /deploy 8 | /public/ 9 | 10 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: [ 4 | 'eslint:recommended', 5 | 'plugin:jest/recommended', 6 | 'prettier', 7 | 'plugin:@typescript-eslint/recommended', 8 | ], 9 | plugins: ['@typescript-eslint', 'react', 'import'], 10 | rules: { 11 | indent: ['error', 2], 12 | 'linebreak-style': 1, 13 | }, 14 | parser: '@typescript-eslint/parser', 15 | parserOptions: { 16 | ecmaVersion: 2021, 17 | sourceType: 'module', 18 | ecmaFeatures: { 19 | jsx: true, 20 | useJSXTextNode: true, 21 | }, 22 | 23 | ignorePatterns: [ 24 | 'dist/**/*', // Ignore built files. 25 | ], 26 | }, 27 | env: { 28 | es6: true, 29 | browser: true, 30 | node: true, 31 | }, 32 | settings: { 33 | react: { 34 | version: 'detect', 35 | }, 36 | }, 37 | }; 38 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/custom.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Custom issue template 3 | about: Describe this issue template's purpose here. 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | logs/ 3 | pids/ 4 | lib-cov/ 5 | coverage/ 6 | jspm_packages/ 7 | build/Release/ 8 | node_modules/ 9 | bower_components/ 10 | coverage/ 11 | dist/ 12 | lib/ 13 | local/ 14 | reports/ 15 | Thumbs.db 16 | dll/ 17 | ~/ 18 | 19 | *.log 20 | npm-debug.log* 21 | *.pid 22 | *.seed 23 | .nyc_output 24 | .lock-wscript 25 | .npm 26 | .history 27 | .node_repl_history 28 | .cache-loader/ 29 | .eslintcache 30 | *.env 31 | .env 32 | .idea 33 | .vscode 34 | *.sublime-project 35 | *.sublime-workspaceg 36 | .DS_Store 37 | *.tgz 38 | *.zip 39 | .cache 40 | .history 41 | *.aes 42 | package-lock.json 43 | *.docz/** 44 | tags 45 | !tags/ 46 | tags.lock 47 | tags.temp 48 | 49 | -------------------------------------------------------------------------------- /.huskyrc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hannadrehman/stories-react/368e97e7cf64394da9f218b007e10079129ab5c3/.huskyrc -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | . 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # add contributing guidelines 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 hannad rehman 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 all 13 | 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 THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hannadrehman/stories-react/368e97e7cf64394da9f218b007e10079129ab5c3/jest.config.js -------------------------------------------------------------------------------- /lint-staged.config.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hannadrehman/stories-react/368e97e7cf64394da9f218b007e10079129ab5c3/lint-staged.config.js -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "workspaces": [ 4 | "packages/*" 5 | ], 6 | "version": "1.1.2", 7 | "description": "instagram style stories in react js", 8 | "scripts": { 9 | "start:docs": "cd ./packages/documentation/ && yarn start", 10 | "build:stories": "cd ./packages/stories/ && yarn build:watch", 11 | "build:docs": "cd ./packages/documentation/ && yarn build && yarn mv", 12 | "test": "yarn test", 13 | "lint": "eslint . --ext .ts", 14 | "install": "yarn install --frozen-lockfile" 15 | }, 16 | "keywords": [ 17 | "reactjs", 18 | "typescript", 19 | "instagram", 20 | "stories" 21 | ], 22 | "repository": "https://github.com/hannadrehman/stories-react/", 23 | "homepage": "https://hannadrehman.github.io/stories-react/", 24 | "bugs": "https://github.com/hannadrehman/stories-react/issues", 25 | "author": { 26 | "name": "Hannad Rehman", 27 | "email": "hannad63@gmail.com", 28 | "url": "https://hannadrehman.com" 29 | }, 30 | "license": "MIT", 31 | "devDependencies": { 32 | "@typescript-eslint/eslint-plugin": "^5.54.0", 33 | "@typescript-eslint/parser": "^5.54.0", 34 | "eslint": "^8.35.0", 35 | "eslint-config-prettier": "^8.6.0", 36 | "eslint-plugin-import": "^2.27.5", 37 | "eslint-plugin-jest": "^27.2.1", 38 | "eslint-plugin-prettier": "^4.2.1", 39 | "eslint-plugin-react": "^7.32.2", 40 | "jest": "^29.4.3", 41 | "prettier": "^2.8.4", 42 | "typescript": "^4.9.5" 43 | }, 44 | "dependencies": { 45 | "caniuse-lite": "^1.0.30001460" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /packages/documentation/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | 4 | /build 5 | # Generated files 6 | .docusaurus 7 | .cache-loader 8 | 9 | # Misc 10 | .DS_Store 11 | .env.local 12 | .env.development.local 13 | .env.test.local 14 | .env.production.local 15 | 16 | npm-debug.log* 17 | yarn-debug.log* 18 | yarn-error.log* 19 | -------------------------------------------------------------------------------- /packages/documentation/README.md: -------------------------------------------------------------------------------- 1 | # Website 2 | 3 | This website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator. 4 | 5 | ### Installation 6 | 7 | ``` 8 | $ yarn 9 | ``` 10 | 11 | ### Local Development 12 | 13 | ``` 14 | $ yarn start 15 | ``` 16 | 17 | This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server. 18 | 19 | ### Build 20 | 21 | ``` 22 | $ yarn build 23 | ``` 24 | 25 | This command generates static content into the `build` directory and can be served using any static contents hosting service. 26 | 27 | ### Deployment 28 | 29 | Using SSH: 30 | 31 | ``` 32 | $ USE_SSH=true yarn deploy 33 | ``` 34 | 35 | Not using SSH: 36 | 37 | ``` 38 | $ GIT_USER= yarn deploy 39 | ``` 40 | 41 | If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. 42 | -------------------------------------------------------------------------------- /packages/documentation/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')], 3 | }; 4 | -------------------------------------------------------------------------------- /packages/documentation/docs/api.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 3 3 | --- 4 | 5 | # Api 6 | 7 | 8 | ## Props 9 | 10 | | Property | Type | Default | Description | 11 | | ------------------------------| ------------------------------| --------------| --------------------------------------------------------------------------------------- | 12 | | `stories` | `IStoryObject[]` | `[]` | An array of story objects. description of `IStoryObject` is mentioned below | 13 | | `height` | `string` | `100%` | Height of story container | 14 | | `width` | `string` | `100%` | Width of story container | 15 | | `loop` | `boolean` | `false` | The last story loop to the first one and restart the stories | 16 | | `onStoryChange` | `function(index:number)` | `-` | Callback called when story changes | 17 | | `onStoriesStart` | `function` | `-` | Callback called when first story is rendered. it get called only once, | 18 | | `onAllStoriesEnd` | `function` | `-` | Callback called when last story gets completed. it will get called only once | 19 | | `currentIndex` | `number` | `-` | Current index of the story which should be selected first | 20 | | `defaultDuration` | `number` | `10000` | default duration in ms of stories if duration is not provided in the `IStoryObject` | 21 | | `classNames` | `IStoryClassNames` | `{}` | classnames to overide different sections of a story renderer | 22 | | `pauseStoryWhenInActiveWindow`| `boolean` | `true` | pauses story when window goes out of focus (user changes tab/minimizes browser etc | 23 | 24 | ## IStoryObject 25 | 26 | | Property | Type | Default | Description | 27 | | --------------------- |--------------------------------------| -------------| ------------------------------------------------------------| 28 | | `type` | `image , video , component` | `-` | type of story to render | 29 | | `url` | `string` | `-` | url of a story (image & video only) | 30 | | `duration` | `duration` | `10000` | duration in ms of the story | 31 | | `component` | `React Component` | `-` | custom component to render as a story | 32 | | `header` | `React Component` | `-` | header of a story | 33 | | `seeMore` | `string , boolean , React Component` | `true` | See more action text | 34 | | `seeMoreComponent` | `React Component` | `-` | see more component opens after clicking see more | 35 | | `onSeeMoreClick` | `function(index:number)` | `-` | Callback called when story see more is clicked | 36 | 37 | 38 | ## IStoryClassNames 39 | 40 | | Property | Type | Default | Description | 41 | | --------------------- |--------------------------------------| -------------| ------------------------------------------------------------| 42 | | `main` | `string` | `-` | classname for main container | 43 | | `progressContainer` | `string` | `-` | classname for progress line container | 44 | | `progressBarContainer`| `string` | `-` | classname for single progress bar box container | 45 | | `progressBar` | `string` | `-` | classname for progress bar | 46 | | `storyContainer` | `string` | `-` | classname for story container | 47 | 48 | ## Custom Component Story Props 49 | 50 | 51 | | Property | Type | | Description | 52 | | --------------------- |--------------------------------------| -------------| ------------------------------------------------------------| 53 | | `pause` | `function` | | call it to pause a story | 54 | | `resume` | `function` | | call it to resume a story | 55 | | `story` | `IStoryObject` | | current story properties | 56 | | `isPaused` | `boolean` | | state of a story | 57 | -------------------------------------------------------------------------------- /packages/documentation/docs/intro.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | --- 4 | 5 | # Intro 6 | 7 | Let's discover **React Instagram Stories** in less than **5 minutes**. 8 | 9 | ## Getting Started 10 | 11 | 12 | ## Install 13 | 14 | 15 | ```shell 16 | npm install stories-react 17 | ``` 18 | 19 | ## Usage 20 | 21 | 22 | ```jsx 23 | 24 | import Stories from 'stories-react'; 25 | 26 | const storySource = [ 27 | { 28 | type: "image", 29 | url: 'https://images.pexels.com/photos/9470805/pexels-photo-9470805.jpeg?w=300', 30 | duration: 5000 31 | }, 32 | { 33 | type: "video", 34 | url: 'https://images.pexels.com/photos/9733197/pexels-photo-9733197.jpeg?w=300', 35 | duration: 5000 36 | } 37 | ] 38 | 39 | function Profile(){ 40 | return ( 41 |
42 | 43 |
44 | ) 45 | } 46 | 47 | ``` 48 | -------------------------------------------------------------------------------- /packages/documentation/docs/tutorial-basics/Header-stories.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 6 3 | --- 4 | 5 | # Stories with Header 6 | 7 | import HeaderStories from '../../src/components/HeaderStories'; 8 | 9 | 10 | 11 | 12 | ```jsx 13 | 14 | import React from 'react'; 15 | import Stories from 'stories-react'; 16 | import 'stories-react/dist/index.css'; 17 | 18 | function Head(props) { 19 | return ( 20 |
30 |
31 | Avatar 41 |
42 |
50 |

59 | {props.name} 60 |

61 |

70 | {`${props.time} hours ago`} 71 |

72 |
73 |
74 | ); 75 | } 76 | 77 | export default function HeaderStories() { 78 | const stories = [ 79 | { 80 | type: 'image', 81 | url: 'https://images.pexels.com/photos/9470805/pexels-photo-9470805.jpeg?w=300', 82 | duration: 15000, 83 | header: ( 84 | 89 | ), 90 | }, 91 | { 92 | type: 'image', 93 | duration: 6000, 94 | url: 'https://images.pexels.com/photos/9733197/pexels-photo-9733197.jpeg?w=300', 95 | header: ( 96 | 101 | ), 102 | }, 103 | { 104 | duration: 7000, 105 | type: 'image, 106 | url: 'https://images.pexels.com/photos/10964888/pexels-photo-10964888.jpeg?w=300', 107 | header: ( 108 | 113 | ), 114 | }, 115 | ]; 116 | 117 | return ( 118 |
126 | 127 |
128 | ); 129 | } 130 | ' 131 | ``` 132 | -------------------------------------------------------------------------------- /packages/documentation/docs/tutorial-basics/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "position": 1, 3 | "label": "Basic Usage", 4 | "collapsible": false 5 | } 6 | -------------------------------------------------------------------------------- /packages/documentation/docs/tutorial-basics/component-stories.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 4 3 | --- 4 | 5 | # Component Stories 6 | 7 | import ComponentStories from '../../src/components/ComponentStories'; 8 | 9 | 10 | 11 | 12 | ```jsx 13 | 14 | import React from 'react'; 15 | import Stories from 'stories-react'; 16 | import 'stories-react/dist/index.css'; 17 | 18 | function Copy() { 19 | return ( 20 |

21 | Jaguar shark! So tell me - does it really exist? Checkmate... Eventually, 22 | you do plan to have dinosaurs on your dinosaur tour, right? Yeah, but your 23 | scientists were so preoccupied with whether or not they could, they didnt 24 | stop to think if they should. 25 |

26 | ); 27 | } 28 | 29 | function HelpText() { 30 | return ( 31 |
40 |

Hannad Rehman says:

41 | 42 |
43 | ); 44 | } 45 | 46 | function ComponentWithInteractions() { 47 | return ( 48 |
57 |

Component with interactions

58 |
61 | 62 |
63 |

64 | You need to add zindex >= 2 to any interaction u want in 65 | the component 66 |

67 | 85 |
86 | ); 87 | } 88 | 89 | function ComponentApi(props) { 90 | return ( 91 |
100 |

Component Api

101 |
102 |
103 |

A story can be paused and resumed too !

104 |
105 | 121 | 122 |
123 |
124 | 140 |
Make Sure button zIndex is >= 2
141 | 142 |
143 |         {JSON.stringify(props, null, 2)}
144 |       
145 |
Story object passed in props
146 |
147 | ); 148 | } 149 | 150 | export default function ComponentStories() { 151 | const stories = [ 152 | { 153 | type: 'component', 154 | duration: 9000, 155 | component: HelpText, 156 | }, 157 | { 158 | type: 'component', 159 | duration: 30000, 160 | component: ComponentWithInteractions, 161 | }, 162 | { 163 | duration: 9000, 164 | type: 'component', 165 | component: ComponentApi, 166 | }, 167 | ]; 168 | 169 | return ( 170 |
178 | 179 |
180 | ); 181 | } 182 | 183 | ``` 184 | -------------------------------------------------------------------------------- /packages/documentation/docs/tutorial-basics/image-stories.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | --- 4 | 5 | # Image Stories 6 | 7 | import ImageStories from '../../src/components/ImageStories'; 8 | 9 | 10 | 11 | 12 | ```jsx 13 | 14 | import React from 'react'; 15 | import Stories from 'stories-react'; 16 | import 'stories-react/dist/index.css'; 17 | 18 | export default function ImagesStories() { 19 | const stories = [ 20 | { 21 | type: 'image', 22 | url: 'https://images.pexels.com/photos/9470805/pexels-photo-9470805.jpeg?w=300', 23 | duration: 5000, 24 | }, 25 | { 26 | type: 'image', 27 | duration: 6000, 28 | url: 'https://images.pexels.com/photos/9733197/pexels-photo-9733197.jpeg?w=300', 29 | }, 30 | { 31 | duration: 7000, 32 | type: 'image', 33 | url: 'https://images.pexels.com/photos/9470805/pexels-photo-9470805.jpeg?w=300', 34 | }, 35 | ]; 36 | 37 | return ( 38 | 43 | ); 44 | } 45 | 46 | ``` 47 | -------------------------------------------------------------------------------- /packages/documentation/docs/tutorial-basics/image-video-stories.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 3 3 | --- 4 | 5 | # Image & Video Stories 6 | 7 | import ImageVideoStories from '../../src/components/ImageVideoStories'; 8 | 9 | 10 | 11 | 12 | ```jsx 13 | 14 | import React from 'react'; 15 | import Stories from 'stories-react'; 16 | import 'stories-react/dist/index.css'; 17 | 18 | export default function ImageVideoStories() { 19 | const stories = [ 20 | { 21 | type: 'image', 22 | duration: 6000, 23 | url: 'https://images.pexels.com/photos/9733197/pexels-photo-9733197.jpeg?w=300', 24 | }, 25 | { 26 | type: 'video', 27 | url: 'https://assets.mixkit.co/videos/preview/mixkit-man-dancing-under-changing-lights-1240-large.mp4', 28 | duration: 28000, 29 | }, 30 | { 31 | type: 'image', 32 | duration: 6000, 33 | url: 'https://images.pexels.com/photos/9733197/pexels-photo-9733197.jpeg?w=300', 34 | }, 35 | { 36 | type: 'video', 37 | url: 'https://assets.mixkit.co/videos/preview/mixkit-mother-with-her-little-daughter-eating-a-marshmallow-in-nature-39764-large.mp4', 38 | duration: 10000, 39 | }, 40 | { 41 | type: 'video', 42 | url: 'https://assets.mixkit.co/videos/preview/mixkit-family-walking-together-in-nature-39767-large.mp4', 43 | duration: 10000, 44 | }, 45 | 46 | { 47 | type: 'video', 48 | duration: 6000, 49 | url: 'https://assets.mixkit.co/videos/preview/mixkit-girl-in-neon-sign-1232-large.mp4', 50 | }, 51 | { 52 | duration: 7000, 53 | type: 'image', 54 | url: 'https://images.pexels.com/photos/9470805/pexels-photo-9470805.jpeg?w=300', 55 | }, 56 | ]; 57 | return ( 58 |
66 | 67 |
68 | ); 69 | } 70 | 71 | 72 | ``` 73 | -------------------------------------------------------------------------------- /packages/documentation/docs/tutorial-basics/playground.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 7 3 | --- 4 | 5 | # Playground 6 | 7 | 8 | ```jsx live 9 | function ImagesStories() { 10 | const stories = [ 11 | { 12 | type: 'image', 13 | url: 'https://images.pexels.com/photos/9470805/pexels-photo-9470805.jpeg?w=300', 14 | duration: 5000, 15 | }, 16 | { 17 | type: 'image', 18 | duration: 6000, 19 | url: 'https://images.pexels.com/photos/9733197/pexels-photo-9733197.jpeg?w=300', 20 | }, 21 | { 22 | duration: 7000, 23 | type: 'image', 24 | url: 'https://images.pexels.com/photos/10964888/pexels-photo-10964888.jpeg?w=300', 25 | }, 26 | ]; 27 | 28 | return ( 29 |
40 | 41 |

Change the code below and it will reflect in real time

42 |
43 | ); 44 | } 45 | ``` 46 | -------------------------------------------------------------------------------- /packages/documentation/docs/tutorial-basics/see-more.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 5 3 | --- 4 | 5 | # Stories with See More 6 | 7 | import SeeMoreStories from '../../src/components/SeeMoreStories'; 8 | 9 | 10 | 11 | 12 | ```jsx 13 | 14 | import React from 'react'; 15 | import Stories from 'stories-react'; 16 | import 'stories-react/dist/index.css'; 17 | 18 | function SeeMoreComponent() { 19 | return ( 20 |
29 |

Component opened from see more

30 |
33 | 34 |
35 |

36 | You need to add zindex >= 2 to any interaction u want in 37 | the component 38 |

39 | 57 |
58 | ); 59 | } 60 | 61 | export default function ImagesStories() { 62 | const stories = [ 63 | { 64 | type: 'image', 65 | url: 'https://images.pexels.com/photos/9470805/pexels-photo-9470805.jpeg?w=300', 66 | duration: 5000, 67 | seeMore: true, 68 | seeMoreComponent: SeeMoreComponent, 69 | }, 70 | { 71 | type: 'image', 72 | duration: 6000, 73 | url: 'https://images.pexels.com/photos/9733197/pexels-photo-9733197.jpeg?w=300', 74 | seeMore: 'Custom See More String', 75 | seeMoreComponent: SeeMoreComponent, 76 | }, 77 | { 78 | duration: 7000, 79 | type: 'image', 80 | url: 'https://images.pexels.com/photos/10964888/pexels-photo-10964888.jpeg?w=300', 81 | seeMore: ( 82 |
83 | 84 |
85 | ), 86 | seeMoreComponent: SeeMoreComponent, 87 | }, 88 | ]; 89 | 90 | return ( 91 |
99 | 100 |
101 | ); 102 | } 103 | 104 | ``` 105 | 106 | -------------------------------------------------------------------------------- /packages/documentation/docs/tutorial-basics/video-stories.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 2 3 | --- 4 | 5 | # Video Stories 6 | 7 | import VideoStories from '../../src/components/VideoStories'; 8 | 9 | 10 | 11 | 12 | ```jsx 13 | 14 | import React from 'react'; 15 | import Stories from 'stories-react'; 16 | import 'stories-react/dist/index.css'; 17 | 18 | export default function VideoStories() { 19 | const stories = [ 20 | { 21 | type: 'video', 22 | url: 'https://assets.mixkit.co/videos/preview/mixkit-man-dancing-under-changing-lights-1240-large.mp4', 23 | duration: 28000, 24 | }, 25 | { 26 | type: 'video', 27 | url: 'https://assets.mixkit.co/videos/preview/mixkit-mother-with-her-little-daughter-eating-a-marshmallow-in-nature-39764-large.mp4', 28 | duration: 10000, 29 | }, 30 | { 31 | type: 'video', 32 | url: 'https://assets.mixkit.co/videos/preview/mixkit-family-walking-together-in-nature-39767-large.mp4', 33 | duration: 10000, 34 | }, 35 | { 36 | type: 'video', 37 | duration: 6000, 38 | url: 'https://assets.mixkit.co/videos/preview/mixkit-girl-in-neon-sign-1232-large.mp4', 39 | }, 40 | { 41 | duration: 30000, 42 | type: 'video', 43 | url: 'https://assets.mixkit.co/videos/preview/mixkit-tree-with-yellow-flowers-1173-large.mp4', 44 | }, 45 | ]; 46 | 47 | return ( 48 |
56 | 57 |
58 | ); 59 | } 60 | ``` 61 | -------------------------------------------------------------------------------- /packages/documentation/docusaurus.config.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | // Note: type annotations allow type checking and IDEs autocompletion 3 | 4 | const lightCodeTheme = require('prism-react-renderer/themes/github'); 5 | const darkCodeTheme = require('prism-react-renderer/themes/dracula'); 6 | 7 | /** @type {import('@docusaurus/types').Config} */ 8 | const config = { 9 | title: 'React Stories', 10 | tagline: 'Add instagram like stories to your web project', 11 | url: 'https://hannadrehman.github.io/', 12 | baseUrl: '/stories-react/', 13 | onBrokenLinks: 'throw', 14 | onBrokenMarkdownLinks: 'warn', 15 | favicon: 'img/favicon.ico', 16 | organizationName: 'hannadrehman', // Usually your GitHub org/user name. 17 | projectName: 'stories-react', // Usually your repo name. 18 | deploymentBranch: 'gh-pages', 19 | plugins: [], 20 | presets: [ 21 | [ 22 | 'classic', 23 | /** @type {import('@docusaurus/preset-classic').Options} */ 24 | { 25 | docs: { 26 | sidebarPath: require.resolve('./sidebars.js'), 27 | // Please change this to your repo. 28 | editUrl: 29 | 'https://github.com/hannadrehman/stories-react/tree/main/packages/documentation/', 30 | }, 31 | theme: { 32 | customCss: [ 33 | require.resolve('./src/css/custom.css'), 34 | require.resolve('../stories/dist/index.css'), 35 | ], 36 | }, 37 | gtag: { 38 | trackingID: 'G-C9LG1XG1P1', 39 | anonymizeIP: true, 40 | }, 41 | }, 42 | ], 43 | ], 44 | themes: ['@docusaurus/theme-live-codeblock'], 45 | themeConfig: 46 | /** @type {import('@docusaurus/preset-classic').ThemeConfig} */ 47 | { 48 | colorMode: { 49 | defaultMode: 'light', 50 | disableSwitch: false, 51 | respectPrefersColorScheme: false, 52 | }, 53 | navbar: { 54 | title: 'React Stories', 55 | logo: { 56 | alt: 'React Stories', 57 | src: 'img/logo.jpg', 58 | }, 59 | items: [ 60 | { 61 | type: 'doc', 62 | docId: 'intro', 63 | position: 'left', 64 | label: 'Docs', 65 | }, 66 | { 67 | href: 'https://hannadrehman.com?ref=react-stories', 68 | label: 'Blog', 69 | position: 'left', 70 | 'aria-label': 'Hannad rehman Blog', 71 | }, 72 | 73 | { 74 | href: 'https://github.com/hannadrehman/stories-react', 75 | label: 'GitHub', 76 | position: 'right', 77 | 'aria-label': 'GitHub repository', 78 | }, 79 | ], 80 | }, 81 | footer: { 82 | style: 'dark', 83 | links: [ 84 | { 85 | title: 'Docs', 86 | items: [ 87 | { 88 | label: 'Tutorial', 89 | to: '/docs/intro', 90 | }, 91 | ], 92 | }, 93 | { 94 | title: 'Community', 95 | items: [], 96 | }, 97 | { 98 | title: 'More', 99 | items: [ 100 | { 101 | label: 'GitHub', 102 | href: 'https://github.com/hannadrehman/stories-react', 103 | }, 104 | ], 105 | }, 106 | ], 107 | copyright: `Copyright © ${new Date().getFullYear()} React Stories. Built with Docusaurus.`, 108 | }, 109 | prism: { 110 | theme: lightCodeTheme, 111 | darkTheme: darkCodeTheme, 112 | }, 113 | liveCodeBlock: { 114 | playgroundPosition: 'top', 115 | }, 116 | }, 117 | }; 118 | 119 | module.exports = config; 120 | -------------------------------------------------------------------------------- /packages/documentation/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "documentation", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "docusaurus": "docusaurus", 7 | "start": "docusaurus start", 8 | "build": "docusaurus build", 9 | "swizzle": "docusaurus swizzle", 10 | "deploy": "docusaurus deploy", 11 | "clear": "docusaurus clear", 12 | "serve": "docusaurus serve", 13 | "write-translations": "docusaurus write-translations", 14 | "write-heading-ids": "docusaurus write-heading-ids", 15 | "mv": "rm -rf ../../docs && cp -R ./build ../../docs && rm -rf ./build" 16 | }, 17 | "dependencies": { 18 | "@docusaurus/core": "2.3.1", 19 | "@docusaurus/plugin-google-gtag": "^2.3.1", 20 | "@docusaurus/preset-classic": "2.3.1", 21 | "@docusaurus/theme-live-codeblock": "^2.3.1", 22 | "@mdx-js/react": "^1.6.22", 23 | "clsx": "^1.2.1", 24 | "prism-react-renderer": "^1.3.5", 25 | "react": "^18.2.0", 26 | "react-dom": "^18.2.0" 27 | }, 28 | "browserslist": { 29 | "production": [ 30 | ">0.5%", 31 | "not dead", 32 | "not op_mini all" 33 | ], 34 | "development": [ 35 | "last 1 chrome version", 36 | "last 1 firefox version", 37 | "last 1 safari version" 38 | ] 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /packages/documentation/sidebars.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Creating a sidebar enables you to: 3 | - create an ordered group of docs 4 | - render a sidebar for each doc of that group 5 | - provide next/previous navigation 6 | 7 | The sidebars can be generated from the filesystem, or explicitly defined here. 8 | 9 | Create as many sidebars as you want. 10 | */ 11 | 12 | // @ts-check 13 | 14 | /** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ 15 | const sidebars = { 16 | // By default, Docusaurus generates a sidebar from the docs folder structure 17 | tutorialSidebar: [{ type: 'autogenerated', dirName: '.' }], 18 | 19 | // But you can create a sidebar manually 20 | /* 21 | tutorialSidebar: [ 22 | { 23 | type: 'category', 24 | label: 'Tutorial', 25 | items: ['hello'], 26 | }, 27 | ], 28 | */ 29 | }; 30 | 31 | module.exports = sidebars; 32 | -------------------------------------------------------------------------------- /packages/documentation/src/components/ComponentStories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Stories from 'stories-react'; 3 | import 'stories-react/dist/index.css'; 4 | 5 | function Copy() { 6 | return ( 7 |

8 | Jaguar shark! So tell me - does it really exist? Checkmate... Eventually, 9 | you do plan to have dinosaurs on your dinosaur tour, right? Yeah, but your 10 | scientists were so preoccupied with whether or not they could, they didnt 11 | stop to think if they should. 12 |

13 | ); 14 | } 15 | 16 | function HelpText() { 17 | return ( 18 |
27 |

Hannad Rehman says:

28 | 29 |
30 | ); 31 | } 32 | 33 | function ComponentWithInteractions() { 34 | return ( 35 |
44 |

Component with interactions

45 |
48 | 49 |
50 |

51 | You need to add zindex >= 2 to any interaction u want in 52 | the component 53 |

54 | 72 |
73 | ); 74 | } 75 | 76 | function ComponentApi(props) { 77 | return ( 78 |
87 |

Component Api

88 |
89 |
90 |

A story can be paused and resumed too !

91 |
92 | 108 | 109 |
110 |
111 | 127 |
Make Sure button zIndex is >= 2
128 |
135 | {JSON.stringify(props, null, 4)} 136 |
137 |
Story object passed in props
138 |
139 | ); 140 | } 141 | 142 | export default function ComponentStories() { 143 | const stories = [ 144 | { 145 | type: 'component', 146 | duration: 9000, 147 | component: HelpText, 148 | }, 149 | { 150 | type: 'component', 151 | duration: 30000, 152 | component: ComponentWithInteractions, 153 | }, 154 | { 155 | duration: 9000, 156 | type: 'component', 157 | component: ComponentApi, 158 | }, 159 | { 160 | type: 'component', 161 | duration: 9000, 162 | component: HelpText, 163 | }, 164 | ]; 165 | 166 | return ( 167 |
175 | 176 |
177 | ); 178 | } 179 | -------------------------------------------------------------------------------- /packages/documentation/src/components/HeaderStories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Stories from 'stories-react'; 3 | import 'stories-react/dist/index.css'; 4 | 5 | function Head(props) { 6 | return ( 7 |
17 |
18 | Avatar 28 |
29 |
34 |

42 | {props.name} 43 |

44 |

52 | {`${props.time} hours ago`} 53 |

54 |
55 |
56 | ); 57 | } 58 | 59 | export default function HeaderStories() { 60 | const stories = [ 61 | { 62 | type: 'image', 63 | url: 'https://images.pexels.com/photos/9470805/pexels-photo-9470805.jpeg?w=300', 64 | duration: 15000, 65 | header: ( 66 | 71 | ), 72 | }, 73 | { 74 | type: 'image', 75 | duration: 6000, 76 | url: 'https://images.pexels.com/photos/9733197/pexels-photo-9733197.jpeg?w=300', 77 | header: ( 78 | 83 | ), 84 | }, 85 | { 86 | duration: 7000, 87 | type: 'image', 88 | url: 'https://images.pexels.com/photos/10964888/pexels-photo-10964888.jpeg?w=300', 89 | header: ( 90 | 95 | ), 96 | }, 97 | { 98 | type: 'image', 99 | url: 'https://images.pexels.com/photos/10985425/pexels-photo-10985425.jpeg?w=300', 100 | duration: 15000, 101 | header: ( 102 | 107 | ), 108 | }, 109 | ]; 110 | 111 | return ( 112 |
120 | 121 |
122 | ); 123 | } 124 | -------------------------------------------------------------------------------- /packages/documentation/src/components/HomepageFeatures.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import clsx from 'clsx'; 3 | import styles from './HomepageFeatures.module.css'; 4 | 5 | const FeatureList = [ 6 | { 7 | title: 'Easy to Use', 8 | url: require('../../static/img/1.jpg').default, 9 | description: ( 10 | <> 11 | we designed it from the ground up to be easily usable in your website 12 | with clean and simple api. 13 | 14 | ), 15 | }, 16 | { 17 | title: 'Light weight', 18 | url: require('../../static/img/3.jpg').default, 19 | description: <>its just 3Kb gzipped, 20 | }, 21 | { 22 | title: 'Focus on What Matters', 23 | url: require('../../static/img/2.jpg').default, 24 | description: <>focus on your business logic, leave stories to us !, 25 | }, 26 | ]; 27 | 28 | function Feature({ url, title, description }) { 29 | return ( 30 |
31 |
32 |
33 | feature 39 |
40 |
41 |
42 |

{title}

43 |

{description}

44 |
45 |
46 | ); 47 | } 48 | 49 | export default function HomepageFeatures() { 50 | return ( 51 |
52 |
53 |
54 | {FeatureList.map((props, idx) => ( 55 | 56 | ))} 57 |
58 |
59 |
60 | ); 61 | } 62 | -------------------------------------------------------------------------------- /packages/documentation/src/components/HomepageFeatures.module.css: -------------------------------------------------------------------------------- 1 | .features { 2 | display: flex; 3 | align-items: center; 4 | padding: 2rem 0; 5 | width: 100%; 6 | } 7 | 8 | .featureImgBlock { 9 | height:200px; 10 | text-align: center; 11 | margin-bottom: 16px; 12 | } 13 | 14 | .featureSvg { 15 | height: 100%; 16 | } 17 | 18 | .flexCenter { 19 | display:flex; 20 | justify-content: center; 21 | } 22 | -------------------------------------------------------------------------------- /packages/documentation/src/components/ImageStories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Stories from 'stories-react'; 3 | import 'stories-react/dist/index.css'; 4 | import './styles.css'; 5 | 6 | export default function ImagesStories() { 7 | const stories = [ 8 | { 9 | type: 'image', 10 | url: 'https://images.pexels.com/photos/9470805/pexels-photo-9470805.jpeg?w=300', 11 | duration: 5000, 12 | }, 13 | { 14 | type: 'image', 15 | duration: 6000, 16 | url: 'https://images.pexels.com/photos/9733197/pexels-photo-9733197.jpeg?w=300', 17 | }, 18 | 19 | { 20 | duration: 7000, 21 | type: 'image', 22 | url: 'https://images.pexels.com/photos/10964888/pexels-photo-10964888.jpeg?w=300', 23 | }, 24 | { 25 | duration: 7000, 26 | type: 'image', 27 | url: 'https://images.pexels.com/photos/10985425/pexels-photo-10985425.jpeg?w=300', 28 | }, 29 | ]; 30 | 31 | return ( 32 |
40 | 52 |
53 | ); 54 | } 55 | -------------------------------------------------------------------------------- /packages/documentation/src/components/ImageVideoStories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Stories from 'stories-react'; 3 | import 'stories-react/dist/index.css'; 4 | 5 | export default function ImageVideoStories() { 6 | const stories = [ 7 | { 8 | type: 'image', 9 | duration: 6000, 10 | url: 'https://images.pexels.com/photos/9733197/pexels-photo-9733197.jpeg?w=300', 11 | }, 12 | { 13 | type: 'video', 14 | url: 'https://assets.mixkit.co/videos/preview/mixkit-man-dancing-under-changing-lights-1240-large.mp4', 15 | duration: 28000, 16 | }, 17 | { 18 | type: 'image', 19 | duration: 6000, 20 | url: 'https://images.pexels.com/photos/9733197/pexels-photo-9733197.jpeg?w=300', 21 | }, 22 | { 23 | type: 'video', 24 | url: 'https://assets.mixkit.co/videos/preview/mixkit-mother-with-her-little-daughter-eating-a-marshmallow-in-nature-39764-large.mp4', 25 | duration: 10000, 26 | }, 27 | { 28 | type: 'video', 29 | url: 'https://assets.mixkit.co/videos/preview/mixkit-family-walking-together-in-nature-39767-large.mp4', 30 | duration: 10000, 31 | }, 32 | 33 | { 34 | type: 'video', 35 | duration: 6000, 36 | url: 'https://assets.mixkit.co/videos/preview/mixkit-girl-in-neon-sign-1232-large.mp4', 37 | }, 38 | { 39 | duration: 7000, 40 | type: 'image', 41 | url: 'https://images.pexels.com/photos/9470805/pexels-photo-9470805.jpeg?w=300', 42 | }, 43 | ]; 44 | return ( 45 |
53 | 54 |
55 | ); 56 | } 57 | -------------------------------------------------------------------------------- /packages/documentation/src/components/SeeMoreStories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Stories from 'stories-react'; 3 | import 'stories-react/dist/index.css'; 4 | 5 | function SeeMoreComponent() { 6 | return ( 7 |
16 |

Component opened from see more

17 |
20 | 21 |
22 |

23 | You need to add zindex >= 2 to any interaction u want in 24 | the component 25 |

26 | 44 |
45 | ); 46 | } 47 | 48 | export default function SeeMoreStories() { 49 | const stories = [ 50 | { 51 | type: 'image', 52 | url: 'https://images.pexels.com/photos/9470805/pexels-photo-9470805.jpeg?w=300', 53 | duration: 5000, 54 | seeMore: true, 55 | seeMoreComponent: SeeMoreComponent, 56 | }, 57 | { 58 | type: 'image', 59 | duration: 6000, 60 | url: 'https://images.pexels.com/photos/9733197/pexels-photo-9733197.jpeg?w=300', 61 | seeMore: 'Custom See More String', 62 | seeMoreComponent: SeeMoreComponent, 63 | }, 64 | { 65 | duration: 7000, 66 | type: 'image', 67 | url: 'https://images.pexels.com/photos/10964888/pexels-photo-10964888.jpeg?w=300', 68 | seeMore: ( 69 |
70 | 71 |
72 | ), 73 | seeMoreComponent: SeeMoreComponent, 74 | }, 75 | { 76 | type: 'image', 77 | url: 'https://images.pexels.com/photos/10985425/pexels-photo-10985425.jpeg?w=300', 78 | duration: 5000, 79 | seeMore: true, 80 | seeMoreComponent: SeeMoreComponent, 81 | }, 82 | ]; 83 | 84 | return ( 85 |
93 | 94 |
95 | ); 96 | } 97 | -------------------------------------------------------------------------------- /packages/documentation/src/components/VideoStories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Stories from 'stories-react'; 3 | import 'stories-react/dist/index.css'; 4 | 5 | export default function VideoStories() { 6 | const stories = [ 7 | { 8 | type: 'video', 9 | url: 'https://assets.mixkit.co/videos/preview/mixkit-man-dancing-under-changing-lights-1240-large.mp4', 10 | duration: 28000, 11 | }, 12 | { 13 | type: 'video', 14 | url: 'https://assets.mixkit.co/videos/preview/mixkit-mother-with-her-little-daughter-eating-a-marshmallow-in-nature-39764-large.mp4', 15 | duration: 10000, 16 | }, 17 | { 18 | type: 'video', 19 | url: 'https://assets.mixkit.co/videos/preview/mixkit-family-walking-together-in-nature-39767-large.mp4', 20 | duration: 10000, 21 | }, 22 | { 23 | type: 'video', 24 | duration: 6000, 25 | url: 'https://assets.mixkit.co/videos/preview/mixkit-girl-in-neon-sign-1232-large.mp4', 26 | }, 27 | { 28 | duration: 30000, 29 | type: 'video', 30 | url: 'https://assets.mixkit.co/videos/preview/mixkit-tree-with-yellow-flowers-1173-large.mp4', 31 | }, 32 | ]; 33 | 34 | return ( 35 |
43 | 44 |
45 | ); 46 | } 47 | -------------------------------------------------------------------------------- /packages/documentation/src/components/styles.css: -------------------------------------------------------------------------------- 1 | .custom-container, 2 | .custom-progress-container, 3 | .custom-progress-bar-container, 4 | .custon-progress-bar, 5 | .custom-story-container { 6 | font-size: 1px; 7 | } 8 | 9 | -------------------------------------------------------------------------------- /packages/documentation/src/css/custom.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Any CSS included here will be global. The classic template 3 | * bundles Infima by default. Infima is a CSS framework designed to 4 | * work well for content-centric websites. 5 | */ 6 | 7 | /* You can override the default Infima variables here. */ 8 | :root { 9 | --ifm-color-primary: #25c2a0; 10 | --ifm-color-primary-dark: rgb(33, 175, 144); 11 | --ifm-color-primary-darker: rgb(31, 165, 136); 12 | --ifm-color-primary-darkest: rgb(26, 136, 112); 13 | --ifm-color-primary-light: rgb(70, 203, 174); 14 | --ifm-color-primary-lighter: rgb(102, 212, 189); 15 | --ifm-color-primary-lightest: rgb(146, 224, 208); 16 | --ifm-code-font-size: 95%; 17 | } 18 | 19 | .docusaurus-highlight-code-line { 20 | background-color: rgba(0, 0, 0, 0.1); 21 | display: block; 22 | margin: 0 calc(-1 * var(--ifm-pre-padding)); 23 | padding: 0 var(--ifm-pre-padding); 24 | } 25 | 26 | html[data-theme='dark'] .docusaurus-highlight-code-line { 27 | background-color: rgba(0, 0, 0, 0.3); 28 | } 29 | -------------------------------------------------------------------------------- /packages/documentation/src/pages/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import clsx from 'clsx'; 3 | import Layout from '@theme/Layout'; 4 | import Link from '@docusaurus/Link'; 5 | import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; 6 | import styles from './index.module.css'; 7 | import HomepageFeatures from '../components/HomepageFeatures'; 8 | 9 | function HomepageHeader() { 10 | const { siteConfig } = useDocusaurusContext(); 11 | return ( 12 |
13 |
14 |

{siteConfig.title}

15 |

{siteConfig.tagline}

16 |
17 | 21 | Tutorial - 5min ⏱️ 22 | 23 |
24 |
25 |
26 | ); 27 | } 28 | 29 | export default function Home() { 30 | const { siteConfig } = useDocusaurusContext(); 31 | return ( 32 | 36 | 37 |
38 | 39 |
40 |
41 | ); 42 | } 43 | -------------------------------------------------------------------------------- /packages/documentation/src/pages/index.module.css: -------------------------------------------------------------------------------- 1 | /** 2 | * CSS files with the .module.css suffix will be treated as CSS modules 3 | * and scoped locally. 4 | */ 5 | 6 | .heroBanner { 7 | padding: 4rem 0; 8 | text-align: center; 9 | position: relative; 10 | overflow: hidden; 11 | } 12 | 13 | @media screen and (max-width: 966px) { 14 | .heroBanner { 15 | padding: 2rem; 16 | } 17 | } 18 | 19 | .buttons { 20 | display: flex; 21 | align-items: center; 22 | justify-content: center; 23 | } 24 | -------------------------------------------------------------------------------- /packages/documentation/src/pages/markdown-page.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Markdown page example 3 | --- 4 | 5 | # Markdown page example 6 | 7 | You don't need React to write simple standalone pages. 8 | -------------------------------------------------------------------------------- /packages/documentation/src/theme/ReactLiveScope/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | import React from 'react'; 9 | import Stories from 'stories-react'; 10 | import storyStyles from 'stories-react'; 11 | 12 | // Add react-live imports you need here 13 | const ReactLiveScope = { 14 | React, 15 | ...React, 16 | Stories, 17 | storyStyles, 18 | }; 19 | 20 | export default ReactLiveScope; 21 | -------------------------------------------------------------------------------- /packages/documentation/static/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hannadrehman/stories-react/368e97e7cf64394da9f218b007e10079129ab5c3/packages/documentation/static/.nojekyll -------------------------------------------------------------------------------- /packages/documentation/static/img/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hannadrehman/stories-react/368e97e7cf64394da9f218b007e10079129ab5c3/packages/documentation/static/img/1.jpg -------------------------------------------------------------------------------- /packages/documentation/static/img/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hannadrehman/stories-react/368e97e7cf64394da9f218b007e10079129ab5c3/packages/documentation/static/img/2.jpg -------------------------------------------------------------------------------- /packages/documentation/static/img/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hannadrehman/stories-react/368e97e7cf64394da9f218b007e10079129ab5c3/packages/documentation/static/img/3.jpg -------------------------------------------------------------------------------- /packages/documentation/static/img/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hannadrehman/stories-react/368e97e7cf64394da9f218b007e10079129ab5c3/packages/documentation/static/img/android-chrome-192x192.png -------------------------------------------------------------------------------- /packages/documentation/static/img/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hannadrehman/stories-react/368e97e7cf64394da9f218b007e10079129ab5c3/packages/documentation/static/img/android-chrome-512x512.png -------------------------------------------------------------------------------- /packages/documentation/static/img/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hannadrehman/stories-react/368e97e7cf64394da9f218b007e10079129ab5c3/packages/documentation/static/img/apple-touch-icon.png -------------------------------------------------------------------------------- /packages/documentation/static/img/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hannadrehman/stories-react/368e97e7cf64394da9f218b007e10079129ab5c3/packages/documentation/static/img/favicon-16x16.png -------------------------------------------------------------------------------- /packages/documentation/static/img/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hannadrehman/stories-react/368e97e7cf64394da9f218b007e10079129ab5c3/packages/documentation/static/img/favicon-32x32.png -------------------------------------------------------------------------------- /packages/documentation/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hannadrehman/stories-react/368e97e7cf64394da9f218b007e10079129ab5c3/packages/documentation/static/img/favicon.ico -------------------------------------------------------------------------------- /packages/documentation/static/img/logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hannadrehman/stories-react/368e97e7cf64394da9f218b007e10079129ab5c3/packages/documentation/static/img/logo.jpg -------------------------------------------------------------------------------- /packages/documentation/static/img/logo.svg_: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /packages/documentation/static/img/site.webmanifest: -------------------------------------------------------------------------------- 1 | {"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} -------------------------------------------------------------------------------- /packages/documentation/static/img/tutorial/docsVersionDropdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hannadrehman/stories-react/368e97e7cf64394da9f218b007e10079129ab5c3/packages/documentation/static/img/tutorial/docsVersionDropdown.png -------------------------------------------------------------------------------- /packages/documentation/static/img/tutorial/localeDropdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hannadrehman/stories-react/368e97e7cf64394da9f218b007e10079129ab5c3/packages/documentation/static/img/tutorial/localeDropdown.png -------------------------------------------------------------------------------- /packages/stories/README.md: -------------------------------------------------------------------------------- 1 |

stories-react

2 |

A React component For Instagram like stories

3 | 4 |

Homepage 5 | 6 |

7 | 8 | version 9 | 10 |
11 | 22 | 30 | 38 | 39 |

40 | hero 41 |

42 | 43 | # Install 44 | 45 | ```sh 46 | npm install --save stories-react 47 | ``` 48 | 49 | # Demo 50 | 51 | You can find working demo [here](https://hannadrehman.github.io/stories-react/) 52 | 53 | # Usage 54 | 55 | ```jsx 56 | import React from 'react'; 57 | import Stories from 'stories-react'; 58 | import 'stories-react/dist/index.css'; 59 | 60 | export default function ImagesStories() { 61 | const stories = [ 62 | { 63 | type: 'image', 64 | url: 'https://images.pexels.com/photos/9470805/pexels-photo-9470805.jpeg?w=300', 65 | duration: 5000, 66 | }, 67 | { 68 | type: 'image', 69 | duration: 6000, 70 | url: 'https://images.pexels.com/photos/9733197/pexels-photo-9733197.jpeg?w=300', 71 | }, 72 | { 73 | duration: 7000, 74 | type: 'image', 75 | url: 'https://images.pexels.com/photos/9470805/pexels-photo-9470805.jpeg?w=300', 76 | }, 77 | ]; 78 | 79 | return ; 80 | } 81 | ``` 82 | 83 | ## Props 84 | 85 | | Property | Type | Default | Description | 86 | | ------------------------------ | ------------------------ | ------- | ----------------------------------------------------------------------------------- | 87 | | `stories` | `IStoryObject[]` | `[]` | An array of story objects. description of `IStoryObject` is mentioned below | 88 | | `height` | `string` | `100%` | Height of story container | 89 | | `width` | `string` | `100%` | Width of story container | 90 | | `onStoryChange` | `function(index:number)` | `-` | Callback called when story changes | 91 | | `onStoriesStart` | `function` | `-` | Callback called when first story is rendered. it get called only once, | 92 | | `onAllStoriesEnd` | `function` | `-` | Callback called when last story gets completed. it will get called only once | 93 | | `currentIndex` | `number` | `-` | Current index of the story which should be selected first | 94 | | `defaultDuration` | `number` | `10000` | default duration in ms of stories if duration is not provided in the `IStoryObject` | 95 | | `classNames` | `IStoryClassNames` | `{}` | classnames to overide different sections of a story renderer | 96 | | `pauseStoryWhenInActiveWindow` | `boolean` | `true` | pauses story when window goes out of focus (user changes tab/minimizes browser etc | 97 | 98 | ## IStoryObject 99 | 100 | | Property | Type | Default | Description | 101 | | ------------------ | ------------------------------------ | ------- | ------------------------------------------------ | 102 | | `type` | `image , video , component` | `-` | type of story to render | 103 | | `url` | `string` | `-` | url of a story (image & video only) | 104 | | `duration` | `duration` | `10000` | duration in ms of the story | 105 | | `component` | `React Component` | `-` | custom component to render as a story | 106 | | `header` | `React Component` | `-` | header of a story | 107 | | `seeMore` | `string , boolean , React Component` | `true` | See more action text | 108 | | `seeMoreComponent` | `React Component` | `-` | see more component opens after clicking see more | 109 | | `onSeeMoreClick` | `function(index:number)` | `-` | Callback called when story see more is clicked | 110 | 111 | ## IStoryClassNames 112 | 113 | | Property | Type | Default | Description | 114 | | ---------------------- | -------- | ------- | ----------------------------------------------- | 115 | | `main` | `string` | `-` | classname for main container | 116 | | `progressContainer` | `string` | `-` | classname for progress line container | 117 | | `progressBarContainer` | `string` | `-` | classname for single progress bar box container | 118 | | `progressBar` | `string` | `-` | classname for progress bar | 119 | | `storyContainer` | `string` | `-` | classname for story container | 120 | 121 | ## Custom Component Story Props 122 | 123 | | Property | Type | | Description | 124 | | ---------- | -------------- | --- | ------------------------- | 125 | | `pause` | `function` | | call it to pause a story | 126 | | `resume` | `function` | | call it to resume a story | 127 | | `story` | `IStoryObject` | | current story properties | 128 | | `isPaused` | `boolean` | | state of a story | 129 | -------------------------------------------------------------------------------- /packages/stories/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "stories-react", 3 | "version": "1.2.0", 4 | "description": "instagram style stories in react js", 5 | "main": "dist/index.js", 6 | "module": "dist/index.esm.js", 7 | "typings": "dist/index.d.ts", 8 | "scripts": { 9 | "test": "yarn test", 10 | "build": "rollup -c", 11 | "build:watch": "yarn build -w" 12 | }, 13 | "keywords": [ 14 | "reactjs", 15 | "typescript", 16 | "instagram", 17 | "stories" 18 | ], 19 | "repository": "https://github.com/hannadrehman/stories-react/", 20 | "homepage": "https://hannadrehman.github.io/stories-react/", 21 | "bugs": "https://github.com/hannadrehman/stories-react/issues", 22 | "author": { 23 | "name": "Hannad Rehman", 24 | "email": "hannad63@gmail.com", 25 | "url": "https://hannadrehman.com" 26 | }, 27 | "license": "MIT", 28 | "peerDependencies": { 29 | "react": "^18.2.0", 30 | "react-dom": "^18.2.0" 31 | }, 32 | "devDependencies": { 33 | "@babel/preset-typescript": "^7.21.0", 34 | "@rollup/plugin-commonjs": "^24.0.1", 35 | "@rollup/plugin-node-resolve": "^15.0.1", 36 | "@types/jest": "^29.4.0", 37 | "@types/node": "^18.14.6", 38 | "@types/react": "^18.0.28", 39 | "@types/react-dom": "^18.0.11", 40 | "cssnano": "^5.1.15", 41 | "postcss-preset-env": "^8.0.1", 42 | "rollup": "^3.18.0", 43 | "rollup-plugin-css-only": "^4.3.0", 44 | "rollup-plugin-peer-deps-external": "^2.2.4", 45 | "rollup-plugin-postcss": "^4.0.2", 46 | "rollup-plugin-terser": "^7.0.2", 47 | "rollup-plugin-typescript2": "^0.34.1", 48 | "typescript": "^4.9.5" 49 | }, 50 | "dependencies": {} 51 | } 52 | -------------------------------------------------------------------------------- /packages/stories/rollup.config.js: -------------------------------------------------------------------------------- 1 | const peerDepsExternal = require('rollup-plugin-peer-deps-external'); 2 | const resolve = require('@rollup/plugin-node-resolve'); 3 | const commonjs = require('@rollup/plugin-commonjs'); 4 | const typescript = require('rollup-plugin-typescript2'); 5 | const { terser } = require('rollup-plugin-terser'); 6 | const postcss = require('rollup-plugin-postcss'); 7 | const postcssPresetEnv = require('postcss-preset-env'); 8 | const cssnano = require('cssnano'); 9 | const packageJson = require('./package.json'); 10 | 11 | module.exports = { 12 | input: 'src/index.tsx', 13 | output: [ 14 | { 15 | file: packageJson.main, 16 | format: 'cjs', 17 | sourcemap: true, 18 | }, 19 | { 20 | file: packageJson.module, 21 | format: 'esm', 22 | sourcemap: true, 23 | }, 24 | ], 25 | plugins: [ 26 | peerDepsExternal(), 27 | resolve(), 28 | commonjs(), 29 | typescript({ 30 | tsconfig: './tsconfig.json', 31 | }), 32 | postcss({ 33 | extensions: ['.css'], 34 | modules: true, 35 | extract: true, 36 | minimize: true, 37 | plugins: [postcssPresetEnv({ stage: 0 }), cssnano()], 38 | }), 39 | terser({ 40 | output: { 41 | comments: false, 42 | }, 43 | }), 44 | ], 45 | }; 46 | -------------------------------------------------------------------------------- /packages/stories/src/Components/Actions/Actions.component.tsx: -------------------------------------------------------------------------------- 1 | import { Fragment, useRef, useState } from 'react'; 2 | import * as CONSTANTS from './Actions.constants'; 3 | import styles from './Actions.styles.css'; 4 | 5 | interface IActionsProps { 6 | onNextClick: () => void; 7 | onPrevClick: () => void; 8 | onPause: () => void; 9 | onResume: () => void; 10 | } 11 | 12 | type IActionEvent = React.MouseEvent | React.TouchEvent; 13 | 14 | export function Actions({ 15 | onNextClick, 16 | onPrevClick, 17 | onPause, 18 | onResume, 19 | }: IActionsProps) { 20 | const [isStoryPaused, setIsStoryPaused] = useState(false); 21 | //adding pause timer because we want to debouce pause interaction 22 | //because mouse down is called with mouse up immediately 23 | const pauseTimerRef = useRef(null); 24 | 25 | function handlePause(event: IActionEvent) { 26 | event.stopPropagation(); 27 | event.preventDefault(); 28 | clearTimeout(pauseTimerRef.current); 29 | 30 | // delay this transaction 31 | pauseTimerRef.current = setTimeout(() => { 32 | onPause(); 33 | setIsStoryPaused(true); 34 | }, 200); 35 | } 36 | 37 | function handleInteractions(region: string, event: IActionEvent) { 38 | event.stopPropagation(); 39 | event.preventDefault(); 40 | 41 | //clear any pending timeout 42 | clearTimeout(pauseTimerRef.current); 43 | if (isStoryPaused) { 44 | onResume(); 45 | setIsStoryPaused(false); 46 | return; 47 | } 48 | onResume(); 49 | if (region == CONSTANTS.EVENT_REGION.LEFT) { 50 | onPrevClick(); 51 | return; 52 | } 53 | onNextClick(); 54 | } 55 | 56 | function getEvents(region: string) { 57 | return { 58 | onMouseUp: (e: React.MouseEvent) => handleInteractions(region, e), 59 | onTouchEnd: (e: React.TouchEvent) => handleInteractions(region, e), 60 | onTouchStart: (e: React.TouchEvent) => handlePause(e), 61 | onMouseDown: (e: React.MouseEvent) => handlePause(e), 62 | }; 63 | } 64 | 65 | return ( 66 | 67 |
71 |
75 | 76 | ); 77 | } 78 | -------------------------------------------------------------------------------- /packages/stories/src/Components/Actions/Actions.constants.ts: -------------------------------------------------------------------------------- 1 | export const EVENT_TYPE = Object.freeze({ 2 | MOUSE_DOWN: 'mouseDown', 3 | MOUSE_UP: 'mouseUp', 4 | TOUCH_START: 'touchStart', 5 | TOUCH_END: 'touchEnd', 6 | }); 7 | 8 | export const EVENT_REGION = Object.freeze({ 9 | LEFT: 'left', 10 | RIGHT: 'right', 11 | }); 12 | -------------------------------------------------------------------------------- /packages/stories/src/Components/Actions/Actions.styles.css: -------------------------------------------------------------------------------- 1 | .left { 2 | position: absolute; 3 | width: 50%; 4 | top: 0; 5 | bottom: 0; 6 | height: 100%; 7 | z-index: 1; 8 | left: 0; 9 | 10 | } 11 | 12 | .right { 13 | position: absolute; 14 | width: 50%; 15 | top: 0; 16 | bottom: 0; 17 | height: 100%; 18 | z-index: 1; 19 | right: 0; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /packages/stories/src/Components/Actions/index.ts: -------------------------------------------------------------------------------- 1 | import { Actions } from './Actions.component'; 2 | 3 | export { Actions }; 4 | -------------------------------------------------------------------------------- /packages/stories/src/Components/CustomComponent/CustomComponents.component.tsx: -------------------------------------------------------------------------------- 1 | import styles from './CustomComponents.styles.css'; 2 | import { IStoryComponentProps } from '../../types'; 3 | 4 | export function CustomComponent(props: IStoryComponentProps) { 5 | return ( 6 |
7 | 13 |
14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /packages/stories/src/Components/CustomComponent/CustomComponents.styles.css: -------------------------------------------------------------------------------- 1 | .component { 2 | width: 100%; 3 | height: 100%; 4 | } 5 | 6 | -------------------------------------------------------------------------------- /packages/stories/src/Components/CustomComponent/index.ts: -------------------------------------------------------------------------------- 1 | import { CustomComponent } from './CustomComponents.component'; 2 | 3 | export { CustomComponent }; 4 | -------------------------------------------------------------------------------- /packages/stories/src/Components/Image/Image.component.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react'; 2 | import { IStoryComponentProps } from '../../types'; 3 | import styles from './Image.styles.css'; 4 | 5 | export function Image(props: IStoryComponentProps) { 6 | useEffect(() => { 7 | props.onPause(); 8 | }, []); 9 | 10 | function handleLoadImage() { 11 | //set timeout is done because there is an inconsitancy in safari and other browser 12 | //on when to call useEffect 13 | setTimeout(() => { 14 | props.onResume(); 15 | }, 4); 16 | } 17 | 18 | return ( 19 | story 25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /packages/stories/src/Components/Image/Image.styles.css: -------------------------------------------------------------------------------- 1 | .image { 2 | height: 100%; 3 | width: 100%; 4 | object-fit: cover; 5 | } 6 | -------------------------------------------------------------------------------- /packages/stories/src/Components/Image/index.ts: -------------------------------------------------------------------------------- 1 | import { Image } from './Image.component'; 2 | 3 | export { Image }; 4 | -------------------------------------------------------------------------------- /packages/stories/src/Components/Progress/Progress.component.tsx: -------------------------------------------------------------------------------- 1 | import { useStoriesContext } from '../../Hooks'; 2 | import { ProgressBar } from '../ProgressBar'; 3 | import { IStoryIndexedObject } from '../../types'; 4 | import styles from './progress.styles.css'; 5 | 6 | interface IProgressProps { 7 | activeStoryIndex: number; 8 | isPaused: boolean; 9 | } 10 | 11 | export function Progress(props: IProgressProps) { 12 | const { stories, classNames } = useStoriesContext(); 13 | return ( 14 |
18 | {stories.map((story: IStoryIndexedObject) => ( 19 | 26 | ))} 27 |
28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /packages/stories/src/Components/Progress/index.ts: -------------------------------------------------------------------------------- 1 | import { Progress } from './Progress.component'; 2 | 3 | export { Progress }; 4 | -------------------------------------------------------------------------------- /packages/stories/src/Components/Progress/progress.styles.css: -------------------------------------------------------------------------------- 1 | .wrapper{ 2 | position: absolute; 3 | left: 0; 4 | right: 0; 5 | top: 0; 6 | height: 2px; 7 | display: grid; 8 | grid-gap: 4px; 9 | padding: 4px; 10 | } 11 | -------------------------------------------------------------------------------- /packages/stories/src/Components/ProgressBar/ProgressBar.component.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef, useState } from 'react'; 2 | import * as hooks from '../../Hooks/'; 3 | import { IStoryIndexedObject } from '../../types'; 4 | import styles from './ProgressBar.styles.css'; 5 | 6 | interface IProgressBarProps { 7 | hasStoryPassed: boolean; 8 | isActive: boolean; 9 | story: IStoryIndexedObject; 10 | isPaused: boolean; 11 | } 12 | 13 | let barWidth = 0; // declaring it here to avoid variable creating in a loop. this will improve memory utilization. 14 | let step = 0.1; 15 | 16 | export function ProgressBar(props: IProgressBarProps) { 17 | const { defaultDuration, classNames } = hooks.useStoriesContext(); 18 | const barRef = useRef(null); 19 | const barWrapperRef = useRef(null); 20 | const [shouldAnimate, setShouldAnimate] = useState(false); 21 | 22 | //set animations 23 | useEffect(() => { 24 | if (props.isPaused || !props.isActive) { 25 | setShouldAnimate(false); 26 | return; 27 | } 28 | if (props.isActive) { 29 | setShouldAnimate(true); 30 | return; 31 | } 32 | setShouldAnimate(false); 33 | }, [props.isActive, props.isPaused]); 34 | 35 | useEffect(() => { 36 | if (!barRef.current) { 37 | return; 38 | } 39 | if (props.hasStoryPassed) { 40 | barRef.current.style.width = `${barWrapperRef?.current?.offsetWidth}px`; 41 | return; 42 | } 43 | barRef.current.style.width = '0px'; 44 | }, [props.hasStoryPassed, props.isActive]); 45 | 46 | hooks.useAnimationFrame((time: number) => { 47 | if (!barRef.current || !barWrapperRef.current) { 48 | return; 49 | } 50 | barWidth = 51 | Number( 52 | (barRef.current.style.width || '1px').slice( 53 | 0, 54 | barRef.current.style.width.length - 2, 55 | ), 56 | ) || 0; 57 | 58 | if (barWidth > barWrapperRef.current.offsetWidth) { 59 | setShouldAnimate(false); 60 | return; 61 | } 62 | 63 | step = 64 | barWrapperRef?.current?.offsetWidth / 65 | ((props.story.duration || defaultDuration) / time); 66 | barRef.current.style.width = `${barWidth + step}px`; 67 | }, shouldAnimate); 68 | 69 | return ( 70 |
74 |
78 |
79 | ); 80 | } 81 | -------------------------------------------------------------------------------- /packages/stories/src/Components/ProgressBar/ProgressBar.styles.css: -------------------------------------------------------------------------------- 1 | .wrapper { 2 | position: relative; 3 | background-color: #6a6a6a; 4 | border-radius: 2px; 5 | height:2px; 6 | } 7 | 8 | .bar { 9 | width: 0; 10 | position: absolute; 11 | left: 0; 12 | right: 0; 13 | top: 0; 14 | bottom: 0; 15 | border-radius: 2px; 16 | background-color: #eae8e8; 17 | height:2px; 18 | } 19 | -------------------------------------------------------------------------------- /packages/stories/src/Components/ProgressBar/index.ts: -------------------------------------------------------------------------------- 1 | import { ProgressBar } from './ProgressBar.component'; 2 | 3 | export { ProgressBar }; 4 | -------------------------------------------------------------------------------- /packages/stories/src/Components/SeeMore/SeeMore.component.tsx: -------------------------------------------------------------------------------- 1 | import { IStoryIndexedObject } from '../../types'; 2 | import styles from './SeeMore.styles.css'; 3 | 4 | interface Iprops { 5 | story: IStoryIndexedObject; 6 | onSeeMoreClick: () => void; 7 | } 8 | export function SeeMore(props: Iprops) { 9 | function getSeeMore() { 10 | const seeMore = props.story.seeMore; 11 | const typeOfSeeMore = typeof seeMore; 12 | if (['string', 'boolean'].includes(typeOfSeeMore)) { 13 | const seeMoreText = typeOfSeeMore === 'string' ? seeMore : 'See More'; 14 | return ( 15 |
16 | ^ 17 |

{seeMoreText}

18 |
19 | ); 20 | } 21 | if (typeOfSeeMore === 'function') { 22 | return ; 23 | } 24 | return props.story.seeMore; 25 | } 26 | 27 | function handleSeeMore() { 28 | props.onSeeMoreClick(); 29 | } 30 | 31 | if (!props.story.seeMore) { 32 | return null; 33 | } 34 | 35 | return ( 36 | 43 | ); 44 | } 45 | -------------------------------------------------------------------------------- /packages/stories/src/Components/SeeMore/SeeMore.styles.css: -------------------------------------------------------------------------------- 1 | .seeMoreWrapper { 2 | position:absolute; 3 | bottom:24px; 4 | left:0; 5 | right:0; 6 | display:flex; 7 | justify-content:center; 8 | width: 100%; 9 | outline: none; 10 | background-color: inherit; 11 | border:none; 12 | outline:none; 13 | z-index: 2; 14 | cursor: pointer; 15 | } 16 | 17 | .defaultSeeMore{ 18 | display:flex; 19 | width: 100%; 20 | flex-direction: column; 21 | justify-content: center; 22 | align-items:center; 23 | color:white; 24 | font-weight: 600; 25 | font-size: 14px; 26 | line-height: 0.8; 27 | } 28 | 29 | .defaultSeeMore p { 30 | margin:0; 31 | padding:0; 32 | } 33 | 34 | -------------------------------------------------------------------------------- /packages/stories/src/Components/SeeMore/index.ts: -------------------------------------------------------------------------------- 1 | import { SeeMore } from './SeeMore.component'; 2 | 3 | export { SeeMore }; 4 | -------------------------------------------------------------------------------- /packages/stories/src/Components/SeeMoreComponent/SeeMoreComponent.component.tsx: -------------------------------------------------------------------------------- 1 | import { IStoryIndexedObject } from '../../types'; 2 | import styles from './SeeMoreComponent.styles.css'; 3 | 4 | interface IProps { 5 | story: IStoryIndexedObject; 6 | onClose: () => void; 7 | } 8 | 9 | export function SeeMoreComponent(props: IProps) { 10 | function getSeeMoreComponent() { 11 | if (typeof props.story.seeMoreComponent === 'function') { 12 | return ; 13 | } 14 | return props.story.seeMoreComponent; 15 | } 16 | if (!props.story.seeMore) { 17 | return null; 18 | } 19 | if (!props.story.seeMoreComponent) { 20 | return null; 21 | } 22 | 23 | return ( 24 |
25 | 28 | {getSeeMoreComponent()} 29 |
30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /packages/stories/src/Components/SeeMoreComponent/SeeMoreComponent.styles.css: -------------------------------------------------------------------------------- 1 | .seeMoreComponentWrapper { 2 | background-color: white; 3 | max-height: 100%; 4 | overflow-y: auto; 5 | position: absolute; 6 | left:0; 7 | right:0; 8 | z-index:3; 9 | animation: up 200ms ease-in-out; 10 | animation-fill-mode: forwards; 11 | top: 0; 12 | bottom: 0; 13 | } 14 | 15 | .closeIcon { 16 | position: absolute; 17 | top:0; 18 | padding:16px; 19 | outline: none; 20 | background-color: inherit; 21 | border:none; 22 | outline:none; 23 | cursor: pointer; 24 | font-size: 16px; 25 | right: 0; 26 | top: 0; 27 | background: transparent; 28 | 29 | } 30 | 31 | @keyframes up { 32 | from {transform: translateY(10%); } 33 | to{transform: translateY(0);} 34 | } 35 | 36 | -------------------------------------------------------------------------------- /packages/stories/src/Components/SeeMoreComponent/index.ts: -------------------------------------------------------------------------------- 1 | import { SeeMoreComponent } from './SeeMoreComponent.component'; 2 | 3 | export { SeeMoreComponent }; 4 | -------------------------------------------------------------------------------- /packages/stories/src/Components/SoundIcon/SoundIcon.component.tsx: -------------------------------------------------------------------------------- 1 | interface IProps { 2 | type: string; 3 | } 4 | 5 | export function SoundIcon(props: IProps) { 6 | if (props.type === 'off') { 7 | return ( 8 | 15 | 16 | 17 | ); 18 | } 19 | 20 | return ( 21 | 28 | 29 | 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /packages/stories/src/Components/SoundIcon/index.ts: -------------------------------------------------------------------------------- 1 | import { SoundIcon } from './SoundIcon.component'; 2 | 3 | export { SoundIcon }; 4 | -------------------------------------------------------------------------------- /packages/stories/src/Components/Story/Story.component.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef, useState } from 'react'; 2 | import * as CONSTANTS from './Story.constants'; 3 | import styles from './Story.styles.css'; 4 | import { IStoryComponentProps } from '../../types'; 5 | import { Image } from '../Image'; 6 | import { Video } from '../Video'; 7 | import { CustomComponent } from '../CustomComponent'; 8 | import { SeeMore } from '../SeeMore'; 9 | import { SeeMoreComponent } from '../SeeMoreComponent'; 10 | import * as hooks from '../../Hooks'; 11 | 12 | export function Story(props: IStoryComponentProps) { 13 | const [showSeeMoreComponent, setShowSeeMoreComponent] = useState(false); 14 | const { classNames } = hooks.useStoriesContext(); 15 | 16 | useEffect(() => { 17 | setShowSeeMoreComponent(false); 18 | }, [props.story]); 19 | 20 | function getStory() { 21 | if (props.story.type === CONSTANTS.STORY_TYPES.IMAGE) { 22 | return ; 23 | } 24 | if (props.story.type === CONSTANTS.STORY_TYPES.VIDEO) { 25 | return