├── .eslintignore
├── .eslintrc
├── .github
└── workflows
│ ├── gh-pages.yml
│ └── publish.yml
├── .gitignore
├── .prettierrc
├── .prettierrc.json
├── LICENSE
├── README.md
├── config
└── CSSStub.ts
├── documentation
├── docs
│ ├── .vitepress
│ │ ├── components
│ │ │ ├── AudioPlayerRefExample.vue
│ │ │ ├── AudioPlayerWithRef.jsx
│ │ │ └── AudioPlayerWrapper.vue
│ │ ├── config.ts
│ │ ├── lib
│ │ │ └── Music.ts
│ │ ├── styles
│ │ │ └── app.css
│ │ └── theme
│ │ │ └── index.ts
│ ├── examples.md
│ ├── index.md
│ ├── public
│ │ ├── android-chrome-192x192.png
│ │ ├── android-chrome-512x512.png
│ │ ├── apple-touch-icon.png
│ │ ├── clarity.js
│ │ ├── favicon-16x16.png
│ │ ├── favicon-32x32.png
│ │ └── favicon.ico
│ └── sponsors.md
├── package-lock.json
└── package.json
├── jestconfig.json
├── package-lock.json
├── package.json
├── rollup.config.js
├── src
├── assets
│ └── loading.png
├── components
│ ├── AudioPlayer.tsx
│ ├── audioPlay.css
│ ├── core.interface.ts
│ └── index.ts
├── helpers
│ ├── icons
│ │ └── icons.ts
│ └── utils
│ │ ├── formatTime.ts
│ │ ├── getDeviceEventNames.ts
│ │ └── getRangeBox.ts
└── index.ts
├── tests
└── common.test.tsx
└── tsconfig.json
/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "root": true,
3 | "extends": ["prettier", "plugin:prettier/recommended", "eslint:recommended", "plugin:react/recommended", "plugin:@typescript-eslint/eslint-recommended", "plugin:@typescript-eslint/recommended"],
4 | "parser": "@typescript-eslint/parser",
5 | "plugins": ["@typescript-eslint", "prettier", "react", "react-hooks"],
6 | "rules": {
7 | "react-hooks/rules-of-hooks": "error",
8 | "react-hooks/exhaustive-deps": "warn",
9 | "@typescript-eslint/no-non-null-assertion": "off",
10 | "@typescript-eslint/ban-ts-comment": "off",
11 | "@typescript-eslint/no-explicit-any": "off"
12 | },
13 | "settings": {
14 | "react": {
15 | "version": "detect"
16 | }
17 | },
18 | "env": {
19 | "browser": true,
20 | "node": true
21 | },
22 | "globals": {
23 | "JSX": true
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/.github/workflows/gh-pages.yml:
--------------------------------------------------------------------------------
1 | name: Deploy Documentation Page
2 |
3 | on:
4 | push:
5 | branches:
6 | - doc-build
7 |
8 | jobs:
9 | deploy:
10 | runs-on: ubuntu-latest
11 |
12 | steps:
13 | - name: Checkout code
14 | uses: actions/checkout@v3
15 |
16 | - name: Setup Node.js
17 | uses: actions/setup-node@v3
18 | with:
19 | node-version: '18'
20 |
21 | - name: Install dependencies
22 | run: npm install
23 | working-directory: ./documentation
24 |
25 | - name: Build site
26 | run: npm run docs:build
27 | working-directory: ./documentation
28 |
29 | - name: Deploy to GitHub Pages
30 | uses: peaceiris/actions-gh-pages@v3
31 | with:
32 | github_token: ${{ secrets.GITHUB_TOKEN }}
33 | publish_dir: ./documentation/docs/.vitepress/dist
34 |
--------------------------------------------------------------------------------
/.github/workflows/publish.yml:
--------------------------------------------------------------------------------
1 | name: build
2 |
3 | on:
4 | release:
5 | types: [published]
6 |
7 | jobs:
8 | build:
9 | runs-on: ubuntu-latest
10 |
11 | steps:
12 | - name: Checkout
13 | uses: actions/checkout@v3
14 |
15 | - name: Setup Node
16 | uses: actions/setup-node@v2
17 | with:
18 | node-version: 16.x
19 | registry-url: https://registry.npmjs.org/
20 |
21 | - name: Install dependencies
22 | run: npm install --force
23 |
24 | - name: Build
25 | run: npm run build
26 |
27 | - name: Publish
28 | run: npm publish
29 |
30 | env:
31 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
32 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .idea
3 | dist
4 | yarn-error.log
5 | .vscode
6 | .parcel-cache
7 |
8 | /documentation/docs/.vitepress/cache
9 | /documentation/docs/docs
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "printWidth": 200,
4 | "proseWrap": "always",
5 | "tabWidth": 2,
6 | "useTabs": false,
7 | "trailingComma": "none",
8 | "bracketSpacing": true,
9 | "semi" : true
10 | }
--------------------------------------------------------------------------------
/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "bracketSpacing": true,
3 | "singleQuote": true,
4 | "trailingComma": "all",
5 | "tabWidth": 2,
6 | "semi": false,
7 | "printWidth": 120,
8 | "jsxSingleQuote": true,
9 | "endOfLine": "auto"
10 | }
11 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Shahidul Alam Riyad (riyaddecoder)
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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://www.npmjs.com/package/react-audio-play)
2 |
3 |
4 |
5 |
6 | ## react-audio-play
7 |
8 | `react-audio-play` is a simple, lightweight and customizable audio player npm package for React applications. It provides an easy-to-use interface to play audio files in your React components with
9 | minimal setup.
10 |
11 |
12 |
13 | ## Features
14 |
15 | - Play audio files in your React application with a single component.
16 | - Control playback with play, pause, stop, and volume adjustment functionality.
17 | - Display track progress with a customizable progress bar.
18 | - Trigger custom actions on audio events like onPlay, onPause, onStop, onEnd, etc.
19 | - Fully customizable appearance to match your application's design.
20 |
21 | ## Demo
22 |
23 | Check [examples](https://riyaddecoder.github.io/react-audio-play/examples.html)
24 |
25 | ## Installation
26 |
27 | You can install `react-audio-play` using npm or yarn:
28 |
29 | ```bash
30 | npm install react-audio-play
31 | ```
32 |
33 | or
34 |
35 | ```bash
36 | yarn add react-audio-play
37 | ```
38 |
39 | ## Usage
40 |
41 | To use `react-audio-play`, import the `AudioPlayer` component and provide the necessary props:
42 |
43 | ```js
44 | import React from 'react';
45 | import { AudioPlayer } from 'react-audio-play';
46 |
47 | const App = () => {
48 | return (
49 |
50 |
My Audio Player
51 |
52 |
53 | );
54 | };
55 |
56 | export default App;
57 | ```
58 |
59 | #### Keyboard shortcuts (When audio player focused)
60 |
61 | They can be turned off by setting `hasKeyBindings` prop to `false`
62 |
63 | | Key binding | Action |
64 | | ----------- | ------ |
65 | | Space | Play/Pause |
66 | | ← | Rewind |
67 | | → | Forward |
68 | | ↑ | Volume up |
69 | | ↓ | Volume down |
70 |
71 | ## Props
72 |
73 | `react-audio-play` accepts the following props:
74 |
75 | - `className` (string, optional): A CSS class name for styling the component.
76 | - `src` (string, required): The URL or file path of the audio file to be played.
77 | - `autoPlay` (boolean, optional): Set this to `true` to autoplay the audio. Default is `false`.
78 | - `preload` (string, optional): Specifies the preload behavior for the audio file. Possible values are:
79 | - `auto`: The audio data is loaded as soon as possible.
80 | - `metadata`: Only metadata (e.g., duration) is loaded.
81 | - `none`: No preloading. Audio data is only loaded when requested.
82 | - `loop` (boolean, optional): Set this to `true` to enable looping of the audio playback. Default is `false`.
83 | - `volume` (number, optional): The initial volume level of the audio, ranging from 0 to 100. Default is `100`.
84 | - `hasKeyBindings` (boolean, optional): Specifies whether the `AudioPlayer` component should enable keyboard shortcuts for volume control and seeking. Default is `true`.
85 | - `onPlay` (function, optional): Callback function to execute when the audio starts playing.
86 | - `onPause` (function, optional): Callback function to execute when the audio is paused.
87 | - `onEnd` (function, optional): Callback function to execute when the audio playback ends.
88 | - `onError` (function, optional): Callback function to execute if there's an error loading or playing the audio.
89 | - `backgroundColor` (string, optional): The background color of the audio player. Default is `#fff`.
90 | - `color` (string, optional): The text and icon color of the audio player. Default is `#566574`.
91 | - `sliderColor` (string, optional): The color of the progress slider. Default is `#007FFF`.
92 | - `volumePlacement` (string, optional): Specifies the placement of the volume controls. Possible values are `top` and `bottom`. Default is `top`.
93 | - `width` (string, optional): The width of the audio player. Use this prop to set the width of the player. For example, `"100%"`, `"300px"`, etc.
94 | - `style` (object, optional): An object containing additional inline styles for the component.
95 |
96 |
97 | ## Advanced Usage
98 |
99 | Starting with version `v1.0.4`, you can access certain actions of the `AudioPlayer` component programmatically using a `ref` with the following interface:
100 |
101 | - `play`: Function to start audio playback.
102 | - `pause`: Function to pause audio playback.
103 | - `stop`: Function to stop the audio playback.
104 | - `focus`: Function to focus on the audio player component.
105 |
106 | ## Example usage with ref (available from v1.0.4)
107 |
108 | [Live preview here](https://riyaddecoder.github.io/react-audio-play/examples.html#example-4-usage-with-ref-available-from-v1-0-4)
109 |
110 | ```js
111 | import { useRef } from 'react';
112 | import { AudioPlayer, AudioPlayerRef } from 'react-audio-play';
113 |
114 | function App() {
115 | const playerRef = useRef(null);
116 |
117 | const handlePlay = () => {
118 | playerRef.current?.play();
119 | };
120 |
121 | const handlePause = () => {
122 | playerRef.current?.pause();
123 | };
124 |
125 | const handleStop = () => {
126 | playerRef.current?.stop();
127 | };
128 |
129 | const handleFocus = () => {
130 | playerRef.current?.focus();
131 | };
132 |
133 | return (
134 |
135 |
136 |
Play
137 |
Pause
138 |
Stop
139 |
Focus
140 |
141 | );
142 | }
143 |
144 | ```
145 |
146 | ## Example with Custom Event Handling
147 |
148 | ```js
149 | import React from 'react';
150 | import { AudioPlayer } from 'react-audio-play';
151 |
152 | const App = () => {
153 | const handlePlay = () => {
154 | console.log('Audio started playing');
155 | };
156 |
157 | const handlePause = () => {
158 | console.log('Audio paused');
159 | };
160 |
161 | return (
162 |
163 |
My Audio Player
164 |
165 |
166 | );
167 | };
168 |
169 | export default App;
170 | ```
171 |
172 | ## Custom Styling
173 |
174 | You can easily customize the appearance of the audio player by applying your CSS styles to the AudioPlayer component or by overriding the default styles in your project's CSS. Check
175 | [examples](https://riyaddecoder.github.io/react-audio-play/examples.html#example-6-using-style-object)
176 |
177 | ## License
178 |
179 | This package is open-source and distributed under the MIT License. See the [LICENSE](https://github.com/riyaddecoder/react-audio-play/blob/master/LICENSE) file for details.
180 |
181 | ## Contribution
182 |
183 | Contributions are welcome! If you find any issues or have suggestions, feel free to create an issue or submit a pull request on the
184 | [GitHub repository](https://github.com/riyaddecoder/react-audio-play/). Let's build this package together!
185 |
186 |
187 |
188 | Enjoy using react-audio-play in your React applications, and we hope it enhances your audio experience with ease and flexibility. If you have any questions or need further assistance, don't hesitate
189 | to reach out to us! Happy coding! 🎶
190 |
--------------------------------------------------------------------------------
/config/CSSStub.ts:
--------------------------------------------------------------------------------
1 | module.exports = {};
2 |
--------------------------------------------------------------------------------
/documentation/docs/.vitepress/components/AudioPlayerRefExample.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
25 |
--------------------------------------------------------------------------------
/documentation/docs/.vitepress/components/AudioPlayerWithRef.jsx:
--------------------------------------------------------------------------------
1 | import React, { useRef } from 'react';
2 | import { AudioPlayer } from 'react-audio-play';
3 |
4 | export default function AudioPlayerWithRef(props) {
5 | const playerRef = useRef(null);
6 |
7 | const handlePlay = () => {
8 | playerRef.current?.play();
9 | };
10 |
11 | const handlePause = () => {
12 | playerRef.current?.pause();
13 | };
14 |
15 | const handleStop = () => {
16 | playerRef.current?.stop();
17 | };
18 |
19 | const handleFocus = () => {
20 | playerRef.current?.focus();
21 | };
22 |
23 | return (
24 |
25 |
26 |
Play
27 |
Pause
28 |
Stop
29 |
Focus
30 |
31 | );
32 | }
33 |
--------------------------------------------------------------------------------
/documentation/docs/.vitepress/components/AudioPlayerWrapper.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
89 |
90 |
--------------------------------------------------------------------------------
/documentation/docs/.vitepress/config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "vitepress";
2 |
3 | const basePath = "/react-audio-play";
4 | const ogDescription =
5 | "React-audio-play is a simple, lightweight and customizable audio player npm package for React applications";
6 | const ogImage = `${basePath}/android-chrome-192x192.png`;
7 | const ogTitle = "React Audio Play | A Simple Audio Player Package For ReactJS";
8 | const ogUrl = "https://riyaddecoder.github.io/react-audio-play";
9 |
10 | export default defineConfig({
11 | base: `${basePath}/`,
12 | title: `React Audio Play`,
13 | description:
14 | "react-audio-play is a simple, lightweight and customizable audio player npm package for React applications. It provides an easy-to-use interface to play audio files in your React components with minimal setup.",
15 |
16 | head: [
17 | [
18 | "link",
19 | {
20 | rel: "icon",
21 | type: "image/png",
22 | sizes: "16x16",
23 | href: `${basePath}/favicon-16x16.png`,
24 | },
25 | ],
26 | [
27 | "link",
28 | {
29 | rel: "icon",
30 | type: "image/png",
31 | sizes: "32x32",
32 | href: `${basePath}/favicon-32x32.png`,
33 | },
34 | ],
35 | [
36 | "link",
37 | {
38 | rel: "apple-touch-icon",
39 | sizes: "180x180",
40 | href: `${basePath}/apple-touch-icon.png`,
41 | },
42 | ],
43 | [
44 | "link",
45 | {
46 | rel: "icon",
47 | type: "image/png",
48 | sizes: "192x192",
49 | href: `${basePath}/android-chrome-192x192.png`,
50 | },
51 | ],
52 | [
53 | "link",
54 | {
55 | rel: "icon",
56 | type: "image/png",
57 | sizes: "512x512",
58 | href: `${basePath}/android-chrome-512x512.png`,
59 | },
60 | ],
61 | ["link", { rel: "preconnect", href: "https://fonts.googleapis.com" }],
62 | [
63 | "link",
64 | {
65 | rel: "preconnect",
66 | href: "https://fonts.gstatic.com",
67 | crossorigin: "true",
68 | },
69 | ],
70 | [
71 | "link",
72 | {
73 | rel: "preload",
74 | href: "https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=Manrope:wght@600&family=IBM+Plex+Mono:wght@400&display=swap",
75 | as: "style",
76 | },
77 | ],
78 | [
79 | "link",
80 | {
81 | rel: "stylesheet",
82 | href: "https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=Manrope:wght@600&family=IBM+Plex+Mono:wght@400&display=swap",
83 | },
84 | ],
85 | [
86 | "meta",
87 | {
88 | name: "keywords",
89 | content:
90 | "reactJs, React Package, React Audio Player, Audio Player For React, Simple Audio Player, Audio Player NPM, riyaddecoder",
91 | },
92 | ],
93 | [
94 | "meta",
95 | { name: "author", content: "Shahidul Alam Riyad aka riyaddecoder" },
96 | ],
97 |
98 | ["meta", { property: "og:title", content: ogTitle }],
99 | ["meta", { property: "og:description", content: ogDescription }],
100 | ["meta", { property: "og:image", content: ogImage }],
101 | ["meta", { property: "og:url", content: ogUrl }],
102 | ["meta", { property: "og:type", content: "npmPakcage" }],
103 | ["meta", { property: "og:site_name", content: "react-audio-play" }],
104 | ["meta", { name: "twitter:card", content: "REACT AUDIO PLAY" }],
105 | ["meta", { name: "twitter:site", content: "@riyaddecoder" }],
106 | [
107 | "meta",
108 | {
109 | name: "twitter:title",
110 | content: "React Audio Play | A Simple Audio Player Package For ReactJS",
111 | },
112 | ],
113 | [
114 | "meta",
115 | {
116 | name: "twitter:description",
117 | content:
118 | "react-audio-play is a simple, lightweight and customizable audio player npm package for React applications.",
119 | },
120 | ],
121 | [
122 | "meta",
123 | {
124 | name: "twitter:image",
125 | content: `${basePath}/android-chrome-192x192.png`,
126 | },
127 | ],
128 | [
129 | "script",
130 | {
131 | src: `${basePath}/clarity.js`,
132 | defer: "",
133 | },
134 | ],
135 | ],
136 |
137 | themeConfig: {
138 | logo: `/apple-touch-icon.png`,
139 |
140 | editLink: {
141 | pattern:
142 | "https://github.com/riyaddecoder/react-audio-play/edit/master/documentation/docs/:path",
143 | text: "Suggest changes to this page",
144 | },
145 |
146 | socialLinks: [
147 | { icon: "npm", link: "https://www.npmjs.com/package/react-audio-play" },
148 | { icon: "linkedin", link: "https://www.linkedin.com/in/riyaddecoder/" },
149 | { icon: "discord", link: "https://discord.gg/5THusG85" },
150 | {
151 | icon: "github",
152 | link: "https://github.com/riyaddecoder/react-audio-play",
153 | },
154 | ],
155 |
156 | footer: {
157 | message: `Released under the MIT License`,
158 | copyright:
159 | "Copyright © 2022-present Shahidul Alam Riyad ",
160 | },
161 |
162 | nav: [
163 | { text: "Examples", link: "/examples/", activeMatch: "/examples" },
164 | { text: "Sponsors", link: "/sponsors/", activeMatch: "/sponsors" },
165 | ],
166 |
167 | sidebar: {
168 | "/sponsors": [],
169 | "/": [
170 | {
171 | // text: "Guide",
172 | items: [
173 | {
174 | text: "Getting Started",
175 | link: "/",
176 | },
177 | {
178 | text: "Examples",
179 | link: "/examples",
180 | },
181 | ],
182 | },
183 | ],
184 | },
185 |
186 | outline: {
187 | level: [2, 3],
188 | },
189 | },
190 | transformPageData(pageData) {
191 | const canonicalUrl = `${ogUrl}/${pageData.relativePath}`
192 | .replace(/\/index\.md$/, "/")
193 | .replace(/\.md$/, "/");
194 | pageData.frontmatter.head ??= [];
195 | pageData.frontmatter.head.unshift(
196 | ["link", { rel: "canonical", href: canonicalUrl }],
197 | ["meta", { property: "og:title", content: pageData.title }]
198 | );
199 | return pageData;
200 | },
201 | });
202 |
--------------------------------------------------------------------------------
/documentation/docs/.vitepress/lib/Music.ts:
--------------------------------------------------------------------------------
1 | const MUSICS = [
2 | {
3 | url: 'https://github.com/riyaddecoder/audio-files/raw/master/Aurthohin-Anmone.mp3',
4 | title: 'Anmone - Aurthohin',
5 | sourceLink: 'https://youtu.be/OZZ_Qc2t49M?si=mXdMlU2pZVF-b5Mi'
6 | },
7 | {
8 | url: 'https://github.com/riyaddecoder/audio-files/raw/master/Kotha%20Koiyo%20Na%20-%20Coke%20Studio%20Bangla.mp3',
9 | title: 'Kotha koio na - Coke Studio',
10 | sourceLink: 'https://youtu.be/a7V9bUwc4cU?si=WyOopTUwhjZuLwZs'
11 | },
12 | {
13 | url: 'https://github.com/riyaddecoder/audio-files/raw/master/Anmone2-Aurthohin.mp3',
14 | title: 'Anmone 2 - Aurthohin',
15 | sourceLink: 'https://youtu.be/qWW4GBgQII8?si=e_ijRpo144ip0LT6'
16 | },
17 | {
18 | url: 'https://github.com/riyaddecoder/audio-files/raw/master/Poth%20Chola%20-%20Artcell.mp3',
19 | title: 'Poth Chola - Artcell',
20 | sourceLink: 'https://youtu.be/CKfhGvUPXkY?si=NSEmDY1T-gsUigSv'
21 | }
22 | ];
23 |
24 | export class Music {
25 | private currentMusic = MUSICS[0];
26 | private musicIndexes = new Set();
27 |
28 | nextMusic = () => {
29 | if (MUSICS.length <= this.musicIndexes.size) {
30 | this.musicIndexes.clear();
31 | }
32 | let number;
33 | do {
34 | number = Math.floor(Math.random() * MUSICS.length);
35 | } while (this.musicIndexes.has(number));
36 | this.currentMusic = MUSICS[number];
37 | this.musicIndexes.add(number);
38 | return MUSICS[number];
39 | };
40 |
41 | getCurrentMusic = () => {
42 | return this.currentMusic;
43 | };
44 | }
45 |
--------------------------------------------------------------------------------
/documentation/docs/.vitepress/styles/app.css:
--------------------------------------------------------------------------------
1 | .VPNavBar .vpi-social-npm {
2 | color: #cb3837;
3 | }
4 |
5 | .dark .VPNavBar .vpi-social-npm {
6 | color: #ff5150;
7 | }
8 |
9 | .VPNavBar .vpi-social-linkedin {
10 | color: #0a66c2;
11 | }
12 |
13 | .dark .VPNavBar .vpi-social-linkedin {
14 | color: #1e8eff;
15 | }
16 |
17 | .VPNavBar .vpi-social-discord {
18 | color: #5865f2;
19 | }
20 |
21 | .dark .VPNavBar .vpi-social-discord {
22 | color: #6c78ff;
23 | }
24 |
25 | .dark .VPNavBar .vpi-social-github {
26 | color: #fff;
27 | }
28 |
29 | .VPNavBar .vpi-social-github {
30 | color: #000;
31 | }
32 |
--------------------------------------------------------------------------------
/documentation/docs/.vitepress/theme/index.ts:
--------------------------------------------------------------------------------
1 | // docs/.vitepress/theme/index.js
2 | import DefaultTheme from 'vitepress/theme'
3 | import '../styles/app.css' // Import your custom CSS here
4 |
5 | export default {
6 | ...DefaultTheme,
7 | enhanceApp({ app }) {
8 | // You can add more custom logic here if needed
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/documentation/docs/examples.md:
--------------------------------------------------------------------------------
1 | # Example Usage
2 |
3 | Below are examples of how you can use the `AudioPlayer` component. Replace `path/to/audio.mp3` with the actual URL or path of your audio file.
4 |
5 | ## Example 1: Basic usage with default props
6 |
7 | ```js
8 | import { AudioPlayer } from "react-audio-play";
9 |
10 | export default function App() {
11 | return ;
12 | }
13 | ```
14 |
15 | Preview
16 |
17 |
18 |
19 | Music Source: {{music.getCurrentMusic().title}}
20 |
21 | ## Example 2: Looping audio, set the volume to 50% and volume control placement bottom
22 |
23 | ```js
24 | import { AudioPlayer } from "react-audio-play";
25 |
26 | export default function App() {
27 | return (
28 |
34 | );
35 | }
36 | ```
37 |
38 | Preview
39 |
40 |
41 |
42 | Music Source: {{music.getCurrentMusic().title}}
43 |
44 | ## Example 3: Using audio callbacks
45 |
46 | ```js
47 | import { AudioPlayer } from "react-audio-play";
48 |
49 | export default function App() {
50 | const handlePlay = () => {
51 | console.log("Audio started playing");
52 | };
53 |
54 | const handlePause = () => {
55 | console.log("Audio paused");
56 | };
57 |
58 | const handleEnd = () => {
59 | console.log("Audio ended");
60 | };
61 |
62 | const handleError = (event, errorMessage) => {
63 | console.log(errorMessage);
64 | };
65 |
66 | return (
67 |
74 | );
75 | }
76 | ```
77 |
78 | Preview
79 |
80 |
81 |
82 | Music Source: {{music.getCurrentMusic().title}}
83 |
84 | ## Example 4: Usage with ref (available from v1.0.4)
85 |
86 | ```tsx
87 | import { useRef } from 'react';
88 | import { AudioPlayer, AudioPlayerRef } from 'react-audio-play';
89 |
90 | function App() {
91 | const playerRef = useRef(null);
92 |
93 | const handlePlay = () => {
94 | playerRef.current?.play();
95 | };
96 |
97 | const handlePause = () => {
98 | playerRef.current?.pause();
99 | };
100 |
101 | const handleStop = () => {
102 | playerRef.current?.stop();
103 | };
104 |
105 | const handleFocus = () => {
106 | playerRef.current?.focus();
107 | };
108 |
109 | return (
110 |
111 |
112 |
Play
113 |
Pause
114 |
Stop
115 |
Focus
116 |
117 | );
118 | }
119 |
120 | ```
121 |
122 | Preview
123 |
124 |
125 |
126 | Music Source: {{music.getCurrentMusic().title}}
127 |
128 | ## Example 5: Darkmode using basic style props
129 |
130 | ```js
131 | import { AudioPlayer } from "react-audio-play";
132 |
133 | export default function App() {
134 | return (
135 |
141 | );
142 | }
143 | ```
144 |
145 | Preview
146 |
147 |
148 |
149 | Music Source: {{music.getCurrentMusic().title}}
150 |
151 | ## Example 6: Using Style Object
152 |
153 | ```js
154 | import { AudioPlayer } from "react-audio-play";
155 |
156 | export default function App() {
157 | return (
158 |
164 | );
165 | }
166 | ```
167 |
168 | Preview
169 |
170 |
171 |
172 | Music Source: {{music.getCurrentMusic().title}}
173 |
174 | ## Example 7: Using Custom CSS
175 |
176 | ::: code-group
177 |
178 | ```css [CSS]
179 | .custom-style.rap-container {
180 | background-color: #000000;
181 | background-image: linear-gradient(147deg, #000000 0%, #04619f 74%);
182 | color: aliceblue;
183 | }
184 |
185 | .custom-style.rap-container .rap-pp-icon path,
186 | .custom-style.rap-container .rap-volume-btn path {
187 | fill: white;
188 | }
189 |
190 | .custom-style.rap-container .rap-slider .rap-progress {
191 | background-color: #daecff;
192 | }
193 |
194 | .custom-style.rap-container .rap-volume .rap-volume-controls {
195 | background-color: #000000;
196 | background-image: linear-gradient(147deg, #000000 0%, #04619f 74%);
197 | }
198 |
199 | .custom-style.rap-container .rap-slider .rap-progress .rap-pin {
200 | background-color: #c3d5ff;
201 | box-shadow: 0 0 9px 7px #269eff52;
202 | }
203 |
204 | .custom-style.rap-container svg.rap-pp-icon:hover,
205 | .custom-style.rap-container .rap-volume-btn svg:hover {
206 | filter: drop-shadow(0px 0px 6px rgba(255, 255, 255, 0.9));
207 | }
208 | ```
209 |
210 | ```js [TSX]
211 | import { AudioPlayer } from "react-audio-play";
212 |
213 | export default function App() {
214 | return ;
215 | }
216 | ```
217 |
218 | :::
219 |
220 | ::: warning
221 | Use a wrapper class to avoid CSS override issues. Ex: `.custom-style`
222 | :::
223 |
224 | Preview
225 |
226 |
227 |
228 | Music Source: {{music.getCurrentMusic().title}}
229 |
230 | ## Example 8: More Playing With CSS
231 |
232 | ::: code-group
233 |
234 | ```css [CSS]
235 | .custom-style.rap-container {
236 | background-color: #e4e4e4;
237 | color: #566574;
238 | border-radius: 20px;
239 | }
240 |
241 | .custom-style.rap-container .rap-slider .rap-progress {
242 | background-color: #959595;
243 | }
244 |
245 | .custom-style.rap-container .rap-slider .rap-progress .rap-pin {
246 | background-color: #566574;
247 | height: 18px;
248 | width: 18px;
249 | border-radius: 10px;
250 | }
251 |
252 | .custom-style.rap-container .rap-controls .rap-slider .rap-progress .rap-pin {
253 | top: -5px;
254 | }
255 |
256 | .custom-style.rap-container .rap-controls .rap-slider {
257 | height: 8px;
258 | border-radius: 4px;
259 | }
260 |
261 | .custom-style.rap-container .rap-volume .rap-volume-btn.rap-volume-open path {
262 | fill: #000;
263 | }
264 |
265 | .custom-style.rap-container .rap-volume .rap-volume-controls {
266 | background-color: #e4e4e4;
267 | }
268 |
269 | .custom-style.rap-container .rap-volume .rap-volume-controls .rap-slider,
270 | .custom-style.rap-container
271 | .rap-volume
272 | .rap-volume-controls
273 | .rap-slider
274 | .rap-progress {
275 | width: 8px;
276 | }
277 | ```
278 |
279 | ```js [TSX]
280 | import { AudioPlayer } from "react-audio-play";
281 |
282 | export default function App() {
283 | return ;
284 | }
285 | ```
286 |
287 | :::
288 |
289 | ::: warning
290 | Use a wrapper class to avoid CSS override issues. Ex: `.custom-style`
291 | :::
292 |
293 | Preview
294 |
295 |
296 |
297 | Music Source: {{music.getCurrentMusic().title}}
298 |
299 |
306 |
--------------------------------------------------------------------------------
/documentation/docs/index.md:
--------------------------------------------------------------------------------
1 | # Getting Started
2 |
3 | ## Installation
4 |
5 | Welcome to the Audio Player documentation. To get started, follow the steps below:
6 |
7 | 1. **Install the package using npm:**
8 |
9 | ```bash
10 | npm install react-audio-play
11 | ```
12 |
13 | 2. **Alternatively, install with yarn:**
14 |
15 | ```bash
16 | yarn add react-audio-play
17 | ```
18 |
19 | 3. **Import and use the `AudioPlayer` component in your project:**
20 |
21 | ```js
22 | import { AudioPlayer } from 'react-audio-play';
23 |
24 | export default function App() {
25 | return ;
26 | }
27 | ```
28 |
29 | 4. **Customize the appearance and functionality as needed.**
30 |
31 | ## Props
32 |
33 | The **AudioPlayer** component accepts the following props:
34 |
35 | | Prop | Type | Description |
36 | | --------------- | -------------------- | --------------------------------------------------------------------------------------------- |
37 | | `className` | string, optional | A CSS class name for styling the component. |
38 | | `src` | string, required | The URL or file path of the audio file to be played. |
39 | | `autoPlay` | boolean, optional | Set to `true` to autoplay the audio (default: `false`). |
40 | | `preload` | string, optional | `auto` - The audio data is loaded as soon as possible. `metadata` - Only metadata (e.g., duration) is loaded. `none` - No preloading. Audio data is only loaded when requested. |
41 | | `loop` | boolean, optional | Set to `true` to loop the audio playback (default: `false`). |
42 | | `volume` | number, optional | The initial volume level (0 to 100) of the audio (default: `100`). |
43 | | `hasKeyBindings`| boolean, optional | Specifies whether the `AudioPlayer` component should enable keyboard shortcuts for volume control and seeking (default: `true`). |
44 | | `onPlay` | function, optional | Callback function to execute when the audio starts playing. |
45 | | `onPause` | function, optional | Callback function to execute when the audio is paused. |
46 | | `onEnd` | function, optional | Callback function to execute when the audio playback ends. |
47 | | `onError` | function, optional | Callback function to execute if there’s an error loading or playing the audio. |
48 | | `backgroundColor`| string, optional | Set the background color of the audio player (default: `#fff`). |
49 | | `color` | string, optional | The text and icon color of the audio player (default: `#566574`). |
50 | | `sliderColor` | string, optional | The color of the progress slider (default: `#007FFF`). |
51 | | `volumePlacement`| string, optional | Control where the volume controls are located (`top | bottom`) (default: `top`). |
52 | | `width` | string, optional | The width of the audio player. For example, `"100%"`, `"300px"`, etc. |
53 | | `style` | object, optional | An object containing additional inline styles for the component. |
54 |
55 |
56 | ## Advanced Usage
57 |
58 | Starting with version `v1.0.4`, you can access certain actions of the `AudioPlayer` component programmatically using a `ref` with the following interface:
59 |
60 | - `play`: Function to start audio playback.
61 | - `pause`: Function to pause audio playback.
62 | - `stop`: Function to stop the audio playback.
63 | - `focus`: Function to focus on the audio player component.
64 |
65 | ::: info
66 | [Example Here](/examples.html#example-4-usage-with-ref-available-from-v1-0-4)
67 | :::
68 |
69 | ## Keyboard Shortcuts
70 |
71 | Below are the keyboard shortcuts available when the audio player is focused. They can be turned off by setting the `hasKeyBindings` prop to `false`.
72 |
73 | | Key | Action |
74 | | ----- | ------------- |
75 | | Space | Play/Pause |
76 | | ← | Rewind |
77 | | → | Forward |
78 | | ↑ | Volume up |
79 | | ↓ | Volume down |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/documentation/docs/public/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/riyaddecoder/react-audio-play/8353163064a1d6bcce4f44c791913a81462620c6/documentation/docs/public/android-chrome-192x192.png
--------------------------------------------------------------------------------
/documentation/docs/public/android-chrome-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/riyaddecoder/react-audio-play/8353163064a1d6bcce4f44c791913a81462620c6/documentation/docs/public/android-chrome-512x512.png
--------------------------------------------------------------------------------
/documentation/docs/public/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/riyaddecoder/react-audio-play/8353163064a1d6bcce4f44c791913a81462620c6/documentation/docs/public/apple-touch-icon.png
--------------------------------------------------------------------------------
/documentation/docs/public/clarity.js:
--------------------------------------------------------------------------------
1 | (function (c, l, a, r, i, t, y) {
2 | c[a] =
3 | c[a] ||
4 | function () {
5 | (c[a].q = c[a].q || []).push(arguments);
6 | };
7 | t = l.createElement(r);
8 | t.async = 1;
9 | t.src = 'https://www.clarity.ms/tag/' + i;
10 | y = l.getElementsByTagName(r)[0];
11 | y.parentNode.insertBefore(t, y);
12 | })(window, document, 'clarity', 'script', 'itd5tbp46u');
13 |
--------------------------------------------------------------------------------
/documentation/docs/public/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/riyaddecoder/react-audio-play/8353163064a1d6bcce4f44c791913a81462620c6/documentation/docs/public/favicon-16x16.png
--------------------------------------------------------------------------------
/documentation/docs/public/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/riyaddecoder/react-audio-play/8353163064a1d6bcce4f44c791913a81462620c6/documentation/docs/public/favicon-32x32.png
--------------------------------------------------------------------------------
/documentation/docs/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/riyaddecoder/react-audio-play/8353163064a1d6bcce4f44c791913a81462620c6/documentation/docs/public/favicon.ico
--------------------------------------------------------------------------------
/documentation/docs/sponsors.md:
--------------------------------------------------------------------------------
1 | # Sponsors
2 |
3 | Currently, there are no sponsors.
4 |
5 | However, if you'd like to support the project, you can contribute by buying me a cup of tea! Your support helps keep the project running and allows for continuous improvement.
6 |
7 | [Buy me a cup of tea](https://buymeacoffee.com/riyaddecoder)
8 |
9 | Thank you for your generosity and support!
10 |
--------------------------------------------------------------------------------
/documentation/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "documentation",
3 | "lockfileVersion": 3,
4 | "requires": true,
5 | "packages": {
6 | "": {
7 | "dependencies": {
8 | "react": "^18.3.1",
9 | "react-audio-play": "^1.0.4",
10 | "react-dom": "^18.3.1"
11 | },
12 | "devDependencies": {
13 | "@types/express": "^4.17.21",
14 | "vitepress": "^1.3.4"
15 | }
16 | },
17 | "node_modules/@algolia/autocomplete-core": {
18 | "version": "1.9.3",
19 | "dev": true,
20 | "license": "MIT",
21 | "dependencies": {
22 | "@algolia/autocomplete-plugin-algolia-insights": "1.9.3",
23 | "@algolia/autocomplete-shared": "1.9.3"
24 | }
25 | },
26 | "node_modules/@algolia/autocomplete-plugin-algolia-insights": {
27 | "version": "1.9.3",
28 | "dev": true,
29 | "license": "MIT",
30 | "dependencies": {
31 | "@algolia/autocomplete-shared": "1.9.3"
32 | },
33 | "peerDependencies": {
34 | "search-insights": ">= 1 < 3"
35 | }
36 | },
37 | "node_modules/@algolia/autocomplete-preset-algolia": {
38 | "version": "1.9.3",
39 | "dev": true,
40 | "license": "MIT",
41 | "dependencies": {
42 | "@algolia/autocomplete-shared": "1.9.3"
43 | },
44 | "peerDependencies": {
45 | "@algolia/client-search": ">= 4.9.1 < 6",
46 | "algoliasearch": ">= 4.9.1 < 6"
47 | }
48 | },
49 | "node_modules/@algolia/autocomplete-shared": {
50 | "version": "1.9.3",
51 | "dev": true,
52 | "license": "MIT",
53 | "peerDependencies": {
54 | "@algolia/client-search": ">= 4.9.1 < 6",
55 | "algoliasearch": ">= 4.9.1 < 6"
56 | }
57 | },
58 | "node_modules/@algolia/cache-browser-local-storage": {
59 | "version": "4.24.0",
60 | "dev": true,
61 | "license": "MIT",
62 | "dependencies": {
63 | "@algolia/cache-common": "4.24.0"
64 | }
65 | },
66 | "node_modules/@algolia/cache-common": {
67 | "version": "4.24.0",
68 | "dev": true,
69 | "license": "MIT"
70 | },
71 | "node_modules/@algolia/cache-in-memory": {
72 | "version": "4.24.0",
73 | "dev": true,
74 | "license": "MIT",
75 | "dependencies": {
76 | "@algolia/cache-common": "4.24.0"
77 | }
78 | },
79 | "node_modules/@algolia/client-account": {
80 | "version": "4.24.0",
81 | "dev": true,
82 | "license": "MIT",
83 | "dependencies": {
84 | "@algolia/client-common": "4.24.0",
85 | "@algolia/client-search": "4.24.0",
86 | "@algolia/transporter": "4.24.0"
87 | }
88 | },
89 | "node_modules/@algolia/client-account/node_modules/@algolia/client-common": {
90 | "version": "4.24.0",
91 | "dev": true,
92 | "license": "MIT",
93 | "dependencies": {
94 | "@algolia/requester-common": "4.24.0",
95 | "@algolia/transporter": "4.24.0"
96 | }
97 | },
98 | "node_modules/@algolia/client-account/node_modules/@algolia/client-search": {
99 | "version": "4.24.0",
100 | "dev": true,
101 | "license": "MIT",
102 | "dependencies": {
103 | "@algolia/client-common": "4.24.0",
104 | "@algolia/requester-common": "4.24.0",
105 | "@algolia/transporter": "4.24.0"
106 | }
107 | },
108 | "node_modules/@algolia/client-analytics": {
109 | "version": "4.24.0",
110 | "dev": true,
111 | "license": "MIT",
112 | "dependencies": {
113 | "@algolia/client-common": "4.24.0",
114 | "@algolia/client-search": "4.24.0",
115 | "@algolia/requester-common": "4.24.0",
116 | "@algolia/transporter": "4.24.0"
117 | }
118 | },
119 | "node_modules/@algolia/client-analytics/node_modules/@algolia/client-common": {
120 | "version": "4.24.0",
121 | "dev": true,
122 | "license": "MIT",
123 | "dependencies": {
124 | "@algolia/requester-common": "4.24.0",
125 | "@algolia/transporter": "4.24.0"
126 | }
127 | },
128 | "node_modules/@algolia/client-analytics/node_modules/@algolia/client-search": {
129 | "version": "4.24.0",
130 | "dev": true,
131 | "license": "MIT",
132 | "dependencies": {
133 | "@algolia/client-common": "4.24.0",
134 | "@algolia/requester-common": "4.24.0",
135 | "@algolia/transporter": "4.24.0"
136 | }
137 | },
138 | "node_modules/@algolia/client-common": {
139 | "version": "5.8.1",
140 | "dev": true,
141 | "license": "MIT",
142 | "peer": true,
143 | "engines": {
144 | "node": ">= 14.0.0"
145 | }
146 | },
147 | "node_modules/@algolia/client-personalization": {
148 | "version": "4.24.0",
149 | "dev": true,
150 | "license": "MIT",
151 | "dependencies": {
152 | "@algolia/client-common": "4.24.0",
153 | "@algolia/requester-common": "4.24.0",
154 | "@algolia/transporter": "4.24.0"
155 | }
156 | },
157 | "node_modules/@algolia/client-personalization/node_modules/@algolia/client-common": {
158 | "version": "4.24.0",
159 | "dev": true,
160 | "license": "MIT",
161 | "dependencies": {
162 | "@algolia/requester-common": "4.24.0",
163 | "@algolia/transporter": "4.24.0"
164 | }
165 | },
166 | "node_modules/@algolia/client-search": {
167 | "version": "5.8.1",
168 | "dev": true,
169 | "license": "MIT",
170 | "peer": true,
171 | "dependencies": {
172 | "@algolia/client-common": "5.8.1",
173 | "@algolia/requester-browser-xhr": "5.8.1",
174 | "@algolia/requester-fetch": "5.8.1",
175 | "@algolia/requester-node-http": "5.8.1"
176 | },
177 | "engines": {
178 | "node": ">= 14.0.0"
179 | }
180 | },
181 | "node_modules/@algolia/logger-common": {
182 | "version": "4.24.0",
183 | "dev": true,
184 | "license": "MIT"
185 | },
186 | "node_modules/@algolia/logger-console": {
187 | "version": "4.24.0",
188 | "dev": true,
189 | "license": "MIT",
190 | "dependencies": {
191 | "@algolia/logger-common": "4.24.0"
192 | }
193 | },
194 | "node_modules/@algolia/recommend": {
195 | "version": "4.24.0",
196 | "dev": true,
197 | "license": "MIT",
198 | "dependencies": {
199 | "@algolia/cache-browser-local-storage": "4.24.0",
200 | "@algolia/cache-common": "4.24.0",
201 | "@algolia/cache-in-memory": "4.24.0",
202 | "@algolia/client-common": "4.24.0",
203 | "@algolia/client-search": "4.24.0",
204 | "@algolia/logger-common": "4.24.0",
205 | "@algolia/logger-console": "4.24.0",
206 | "@algolia/requester-browser-xhr": "4.24.0",
207 | "@algolia/requester-common": "4.24.0",
208 | "@algolia/requester-node-http": "4.24.0",
209 | "@algolia/transporter": "4.24.0"
210 | }
211 | },
212 | "node_modules/@algolia/recommend/node_modules/@algolia/client-common": {
213 | "version": "4.24.0",
214 | "dev": true,
215 | "license": "MIT",
216 | "dependencies": {
217 | "@algolia/requester-common": "4.24.0",
218 | "@algolia/transporter": "4.24.0"
219 | }
220 | },
221 | "node_modules/@algolia/recommend/node_modules/@algolia/client-search": {
222 | "version": "4.24.0",
223 | "dev": true,
224 | "license": "MIT",
225 | "dependencies": {
226 | "@algolia/client-common": "4.24.0",
227 | "@algolia/requester-common": "4.24.0",
228 | "@algolia/transporter": "4.24.0"
229 | }
230 | },
231 | "node_modules/@algolia/recommend/node_modules/@algolia/requester-browser-xhr": {
232 | "version": "4.24.0",
233 | "dev": true,
234 | "license": "MIT",
235 | "dependencies": {
236 | "@algolia/requester-common": "4.24.0"
237 | }
238 | },
239 | "node_modules/@algolia/recommend/node_modules/@algolia/requester-node-http": {
240 | "version": "4.24.0",
241 | "dev": true,
242 | "license": "MIT",
243 | "dependencies": {
244 | "@algolia/requester-common": "4.24.0"
245 | }
246 | },
247 | "node_modules/@algolia/requester-browser-xhr": {
248 | "version": "5.8.1",
249 | "dev": true,
250 | "license": "MIT",
251 | "peer": true,
252 | "dependencies": {
253 | "@algolia/client-common": "5.8.1"
254 | },
255 | "engines": {
256 | "node": ">= 14.0.0"
257 | }
258 | },
259 | "node_modules/@algolia/requester-common": {
260 | "version": "4.24.0",
261 | "dev": true,
262 | "license": "MIT"
263 | },
264 | "node_modules/@algolia/requester-fetch": {
265 | "version": "5.8.1",
266 | "dev": true,
267 | "license": "MIT",
268 | "peer": true,
269 | "dependencies": {
270 | "@algolia/client-common": "5.8.1"
271 | },
272 | "engines": {
273 | "node": ">= 14.0.0"
274 | }
275 | },
276 | "node_modules/@algolia/requester-node-http": {
277 | "version": "5.8.1",
278 | "dev": true,
279 | "license": "MIT",
280 | "peer": true,
281 | "dependencies": {
282 | "@algolia/client-common": "5.8.1"
283 | },
284 | "engines": {
285 | "node": ">= 14.0.0"
286 | }
287 | },
288 | "node_modules/@algolia/transporter": {
289 | "version": "4.24.0",
290 | "dev": true,
291 | "license": "MIT",
292 | "dependencies": {
293 | "@algolia/cache-common": "4.24.0",
294 | "@algolia/logger-common": "4.24.0",
295 | "@algolia/requester-common": "4.24.0"
296 | }
297 | },
298 | "node_modules/@babel/helper-string-parser": {
299 | "version": "7.25.7",
300 | "dev": true,
301 | "license": "MIT",
302 | "engines": {
303 | "node": ">=6.9.0"
304 | }
305 | },
306 | "node_modules/@babel/helper-validator-identifier": {
307 | "version": "7.25.7",
308 | "dev": true,
309 | "license": "MIT",
310 | "engines": {
311 | "node": ">=6.9.0"
312 | }
313 | },
314 | "node_modules/@babel/parser": {
315 | "version": "7.25.8",
316 | "dev": true,
317 | "license": "MIT",
318 | "dependencies": {
319 | "@babel/types": "^7.25.8"
320 | },
321 | "bin": {
322 | "parser": "bin/babel-parser.js"
323 | },
324 | "engines": {
325 | "node": ">=6.0.0"
326 | }
327 | },
328 | "node_modules/@babel/types": {
329 | "version": "7.25.8",
330 | "dev": true,
331 | "license": "MIT",
332 | "dependencies": {
333 | "@babel/helper-string-parser": "^7.25.7",
334 | "@babel/helper-validator-identifier": "^7.25.7",
335 | "to-fast-properties": "^2.0.0"
336 | },
337 | "engines": {
338 | "node": ">=6.9.0"
339 | }
340 | },
341 | "node_modules/@docsearch/css": {
342 | "version": "3.6.2",
343 | "dev": true,
344 | "license": "MIT"
345 | },
346 | "node_modules/@docsearch/js": {
347 | "version": "3.6.2",
348 | "dev": true,
349 | "license": "MIT",
350 | "dependencies": {
351 | "@docsearch/react": "3.6.2",
352 | "preact": "^10.0.0"
353 | }
354 | },
355 | "node_modules/@docsearch/react": {
356 | "version": "3.6.2",
357 | "dev": true,
358 | "license": "MIT",
359 | "dependencies": {
360 | "@algolia/autocomplete-core": "1.9.3",
361 | "@algolia/autocomplete-preset-algolia": "1.9.3",
362 | "@docsearch/css": "3.6.2",
363 | "algoliasearch": "^4.19.1"
364 | },
365 | "peerDependencies": {
366 | "@types/react": ">= 16.8.0 < 19.0.0",
367 | "react": ">= 16.8.0 < 19.0.0",
368 | "react-dom": ">= 16.8.0 < 19.0.0",
369 | "search-insights": ">= 1 < 3"
370 | },
371 | "peerDependenciesMeta": {
372 | "@types/react": {
373 | "optional": true
374 | },
375 | "react": {
376 | "optional": true
377 | },
378 | "react-dom": {
379 | "optional": true
380 | },
381 | "search-insights": {
382 | "optional": true
383 | }
384 | }
385 | },
386 | "node_modules/@esbuild/linux-x64": {
387 | "version": "0.21.5",
388 | "cpu": [
389 | "x64"
390 | ],
391 | "dev": true,
392 | "license": "MIT",
393 | "optional": true,
394 | "os": [
395 | "linux"
396 | ],
397 | "engines": {
398 | "node": ">=12"
399 | }
400 | },
401 | "node_modules/@jridgewell/sourcemap-codec": {
402 | "version": "1.5.0",
403 | "dev": true,
404 | "license": "MIT"
405 | },
406 | "node_modules/@rollup/rollup-linux-x64-gnu": {
407 | "version": "4.24.0",
408 | "cpu": [
409 | "x64"
410 | ],
411 | "dev": true,
412 | "license": "MIT",
413 | "optional": true,
414 | "os": [
415 | "linux"
416 | ]
417 | },
418 | "node_modules/@rollup/rollup-linux-x64-musl": {
419 | "version": "4.24.0",
420 | "cpu": [
421 | "x64"
422 | ],
423 | "dev": true,
424 | "license": "MIT",
425 | "optional": true,
426 | "os": [
427 | "linux"
428 | ]
429 | },
430 | "node_modules/@shikijs/core": {
431 | "version": "1.22.0",
432 | "dev": true,
433 | "license": "MIT",
434 | "dependencies": {
435 | "@shikijs/engine-javascript": "1.22.0",
436 | "@shikijs/engine-oniguruma": "1.22.0",
437 | "@shikijs/types": "1.22.0",
438 | "@shikijs/vscode-textmate": "^9.3.0",
439 | "@types/hast": "^3.0.4",
440 | "hast-util-to-html": "^9.0.3"
441 | }
442 | },
443 | "node_modules/@shikijs/engine-javascript": {
444 | "version": "1.22.0",
445 | "dev": true,
446 | "license": "MIT",
447 | "dependencies": {
448 | "@shikijs/types": "1.22.0",
449 | "@shikijs/vscode-textmate": "^9.3.0",
450 | "oniguruma-to-js": "0.4.3"
451 | }
452 | },
453 | "node_modules/@shikijs/engine-oniguruma": {
454 | "version": "1.22.0",
455 | "dev": true,
456 | "license": "MIT",
457 | "dependencies": {
458 | "@shikijs/types": "1.22.0",
459 | "@shikijs/vscode-textmate": "^9.3.0"
460 | }
461 | },
462 | "node_modules/@shikijs/transformers": {
463 | "version": "1.22.0",
464 | "dev": true,
465 | "license": "MIT",
466 | "dependencies": {
467 | "shiki": "1.22.0"
468 | }
469 | },
470 | "node_modules/@shikijs/types": {
471 | "version": "1.22.0",
472 | "dev": true,
473 | "license": "MIT",
474 | "dependencies": {
475 | "@shikijs/vscode-textmate": "^9.3.0",
476 | "@types/hast": "^3.0.4"
477 | }
478 | },
479 | "node_modules/@shikijs/vscode-textmate": {
480 | "version": "9.3.0",
481 | "dev": true,
482 | "license": "MIT"
483 | },
484 | "node_modules/@types/body-parser": {
485 | "version": "1.19.5",
486 | "dev": true,
487 | "license": "MIT",
488 | "dependencies": {
489 | "@types/connect": "*",
490 | "@types/node": "*"
491 | }
492 | },
493 | "node_modules/@types/connect": {
494 | "version": "3.4.38",
495 | "dev": true,
496 | "license": "MIT",
497 | "dependencies": {
498 | "@types/node": "*"
499 | }
500 | },
501 | "node_modules/@types/estree": {
502 | "version": "1.0.6",
503 | "dev": true,
504 | "license": "MIT"
505 | },
506 | "node_modules/@types/express": {
507 | "version": "4.17.21",
508 | "dev": true,
509 | "license": "MIT",
510 | "dependencies": {
511 | "@types/body-parser": "*",
512 | "@types/express-serve-static-core": "^4.17.33",
513 | "@types/qs": "*",
514 | "@types/serve-static": "*"
515 | }
516 | },
517 | "node_modules/@types/express-serve-static-core": {
518 | "version": "4.19.6",
519 | "dev": true,
520 | "license": "MIT",
521 | "dependencies": {
522 | "@types/node": "*",
523 | "@types/qs": "*",
524 | "@types/range-parser": "*",
525 | "@types/send": "*"
526 | }
527 | },
528 | "node_modules/@types/hast": {
529 | "version": "3.0.4",
530 | "dev": true,
531 | "license": "MIT",
532 | "dependencies": {
533 | "@types/unist": "*"
534 | }
535 | },
536 | "node_modules/@types/http-errors": {
537 | "version": "2.0.4",
538 | "dev": true,
539 | "license": "MIT"
540 | },
541 | "node_modules/@types/linkify-it": {
542 | "version": "5.0.0",
543 | "dev": true,
544 | "license": "MIT"
545 | },
546 | "node_modules/@types/markdown-it": {
547 | "version": "14.1.2",
548 | "dev": true,
549 | "license": "MIT",
550 | "dependencies": {
551 | "@types/linkify-it": "^5",
552 | "@types/mdurl": "^2"
553 | }
554 | },
555 | "node_modules/@types/mdast": {
556 | "version": "4.0.4",
557 | "dev": true,
558 | "license": "MIT",
559 | "dependencies": {
560 | "@types/unist": "*"
561 | }
562 | },
563 | "node_modules/@types/mdurl": {
564 | "version": "2.0.0",
565 | "dev": true,
566 | "license": "MIT"
567 | },
568 | "node_modules/@types/mime": {
569 | "version": "1.3.5",
570 | "dev": true,
571 | "license": "MIT"
572 | },
573 | "node_modules/@types/node": {
574 | "version": "22.7.5",
575 | "dev": true,
576 | "license": "MIT",
577 | "dependencies": {
578 | "undici-types": "~6.19.2"
579 | }
580 | },
581 | "node_modules/@types/qs": {
582 | "version": "6.9.16",
583 | "dev": true,
584 | "license": "MIT"
585 | },
586 | "node_modules/@types/range-parser": {
587 | "version": "1.2.7",
588 | "dev": true,
589 | "license": "MIT"
590 | },
591 | "node_modules/@types/send": {
592 | "version": "0.17.4",
593 | "dev": true,
594 | "license": "MIT",
595 | "dependencies": {
596 | "@types/mime": "^1",
597 | "@types/node": "*"
598 | }
599 | },
600 | "node_modules/@types/serve-static": {
601 | "version": "1.15.7",
602 | "dev": true,
603 | "license": "MIT",
604 | "dependencies": {
605 | "@types/http-errors": "*",
606 | "@types/node": "*",
607 | "@types/send": "*"
608 | }
609 | },
610 | "node_modules/@types/unist": {
611 | "version": "3.0.3",
612 | "dev": true,
613 | "license": "MIT"
614 | },
615 | "node_modules/@types/web-bluetooth": {
616 | "version": "0.0.20",
617 | "dev": true,
618 | "license": "MIT"
619 | },
620 | "node_modules/@ungap/structured-clone": {
621 | "version": "1.2.0",
622 | "dev": true,
623 | "license": "ISC"
624 | },
625 | "node_modules/@vitejs/plugin-vue": {
626 | "version": "5.1.4",
627 | "dev": true,
628 | "license": "MIT",
629 | "engines": {
630 | "node": "^18.0.0 || >=20.0.0"
631 | },
632 | "peerDependencies": {
633 | "vite": "^5.0.0",
634 | "vue": "^3.2.25"
635 | }
636 | },
637 | "node_modules/@vue/compiler-core": {
638 | "version": "3.5.12",
639 | "dev": true,
640 | "license": "MIT",
641 | "dependencies": {
642 | "@babel/parser": "^7.25.3",
643 | "@vue/shared": "3.5.12",
644 | "entities": "^4.5.0",
645 | "estree-walker": "^2.0.2",
646 | "source-map-js": "^1.2.0"
647 | }
648 | },
649 | "node_modules/@vue/compiler-dom": {
650 | "version": "3.5.12",
651 | "dev": true,
652 | "license": "MIT",
653 | "dependencies": {
654 | "@vue/compiler-core": "3.5.12",
655 | "@vue/shared": "3.5.12"
656 | }
657 | },
658 | "node_modules/@vue/compiler-sfc": {
659 | "version": "3.5.12",
660 | "dev": true,
661 | "license": "MIT",
662 | "dependencies": {
663 | "@babel/parser": "^7.25.3",
664 | "@vue/compiler-core": "3.5.12",
665 | "@vue/compiler-dom": "3.5.12",
666 | "@vue/compiler-ssr": "3.5.12",
667 | "@vue/shared": "3.5.12",
668 | "estree-walker": "^2.0.2",
669 | "magic-string": "^0.30.11",
670 | "postcss": "^8.4.47",
671 | "source-map-js": "^1.2.0"
672 | }
673 | },
674 | "node_modules/@vue/compiler-ssr": {
675 | "version": "3.5.12",
676 | "dev": true,
677 | "license": "MIT",
678 | "dependencies": {
679 | "@vue/compiler-dom": "3.5.12",
680 | "@vue/shared": "3.5.12"
681 | }
682 | },
683 | "node_modules/@vue/devtools-api": {
684 | "version": "7.4.6",
685 | "dev": true,
686 | "license": "MIT",
687 | "dependencies": {
688 | "@vue/devtools-kit": "^7.4.6"
689 | }
690 | },
691 | "node_modules/@vue/devtools-kit": {
692 | "version": "7.4.6",
693 | "dev": true,
694 | "license": "MIT",
695 | "dependencies": {
696 | "@vue/devtools-shared": "^7.4.6",
697 | "birpc": "^0.2.17",
698 | "hookable": "^5.5.3",
699 | "mitt": "^3.0.1",
700 | "perfect-debounce": "^1.0.0",
701 | "speakingurl": "^14.0.1",
702 | "superjson": "^2.2.1"
703 | }
704 | },
705 | "node_modules/@vue/devtools-shared": {
706 | "version": "7.4.6",
707 | "dev": true,
708 | "license": "MIT",
709 | "dependencies": {
710 | "rfdc": "^1.4.1"
711 | }
712 | },
713 | "node_modules/@vue/reactivity": {
714 | "version": "3.5.12",
715 | "dev": true,
716 | "license": "MIT",
717 | "dependencies": {
718 | "@vue/shared": "3.5.12"
719 | }
720 | },
721 | "node_modules/@vue/runtime-core": {
722 | "version": "3.5.12",
723 | "dev": true,
724 | "license": "MIT",
725 | "dependencies": {
726 | "@vue/reactivity": "3.5.12",
727 | "@vue/shared": "3.5.12"
728 | }
729 | },
730 | "node_modules/@vue/runtime-dom": {
731 | "version": "3.5.12",
732 | "dev": true,
733 | "license": "MIT",
734 | "dependencies": {
735 | "@vue/reactivity": "3.5.12",
736 | "@vue/runtime-core": "3.5.12",
737 | "@vue/shared": "3.5.12",
738 | "csstype": "^3.1.3"
739 | }
740 | },
741 | "node_modules/@vue/server-renderer": {
742 | "version": "3.5.12",
743 | "dev": true,
744 | "license": "MIT",
745 | "dependencies": {
746 | "@vue/compiler-ssr": "3.5.12",
747 | "@vue/shared": "3.5.12"
748 | },
749 | "peerDependencies": {
750 | "vue": "3.5.12"
751 | }
752 | },
753 | "node_modules/@vue/shared": {
754 | "version": "3.5.12",
755 | "dev": true,
756 | "license": "MIT"
757 | },
758 | "node_modules/@vueuse/core": {
759 | "version": "11.1.0",
760 | "dev": true,
761 | "license": "MIT",
762 | "dependencies": {
763 | "@types/web-bluetooth": "^0.0.20",
764 | "@vueuse/metadata": "11.1.0",
765 | "@vueuse/shared": "11.1.0",
766 | "vue-demi": ">=0.14.10"
767 | },
768 | "funding": {
769 | "url": "https://github.com/sponsors/antfu"
770 | }
771 | },
772 | "node_modules/@vueuse/core/node_modules/vue-demi": {
773 | "version": "0.14.10",
774 | "dev": true,
775 | "hasInstallScript": true,
776 | "license": "MIT",
777 | "bin": {
778 | "vue-demi-fix": "bin/vue-demi-fix.js",
779 | "vue-demi-switch": "bin/vue-demi-switch.js"
780 | },
781 | "engines": {
782 | "node": ">=12"
783 | },
784 | "funding": {
785 | "url": "https://github.com/sponsors/antfu"
786 | },
787 | "peerDependencies": {
788 | "@vue/composition-api": "^1.0.0-rc.1",
789 | "vue": "^3.0.0-0 || ^2.6.0"
790 | },
791 | "peerDependenciesMeta": {
792 | "@vue/composition-api": {
793 | "optional": true
794 | }
795 | }
796 | },
797 | "node_modules/@vueuse/integrations": {
798 | "version": "11.1.0",
799 | "dev": true,
800 | "license": "MIT",
801 | "dependencies": {
802 | "@vueuse/core": "11.1.0",
803 | "@vueuse/shared": "11.1.0",
804 | "vue-demi": ">=0.14.10"
805 | },
806 | "funding": {
807 | "url": "https://github.com/sponsors/antfu"
808 | },
809 | "peerDependencies": {
810 | "async-validator": "^4",
811 | "axios": "^1",
812 | "change-case": "^5",
813 | "drauu": "^0.4",
814 | "focus-trap": "^7",
815 | "fuse.js": "^7",
816 | "idb-keyval": "^6",
817 | "jwt-decode": "^4",
818 | "nprogress": "^0.2",
819 | "qrcode": "^1.5",
820 | "sortablejs": "^1",
821 | "universal-cookie": "^7"
822 | },
823 | "peerDependenciesMeta": {
824 | "async-validator": {
825 | "optional": true
826 | },
827 | "axios": {
828 | "optional": true
829 | },
830 | "change-case": {
831 | "optional": true
832 | },
833 | "drauu": {
834 | "optional": true
835 | },
836 | "focus-trap": {
837 | "optional": true
838 | },
839 | "fuse.js": {
840 | "optional": true
841 | },
842 | "idb-keyval": {
843 | "optional": true
844 | },
845 | "jwt-decode": {
846 | "optional": true
847 | },
848 | "nprogress": {
849 | "optional": true
850 | },
851 | "qrcode": {
852 | "optional": true
853 | },
854 | "sortablejs": {
855 | "optional": true
856 | },
857 | "universal-cookie": {
858 | "optional": true
859 | }
860 | }
861 | },
862 | "node_modules/@vueuse/integrations/node_modules/vue-demi": {
863 | "version": "0.14.10",
864 | "dev": true,
865 | "hasInstallScript": true,
866 | "license": "MIT",
867 | "bin": {
868 | "vue-demi-fix": "bin/vue-demi-fix.js",
869 | "vue-demi-switch": "bin/vue-demi-switch.js"
870 | },
871 | "engines": {
872 | "node": ">=12"
873 | },
874 | "funding": {
875 | "url": "https://github.com/sponsors/antfu"
876 | },
877 | "peerDependencies": {
878 | "@vue/composition-api": "^1.0.0-rc.1",
879 | "vue": "^3.0.0-0 || ^2.6.0"
880 | },
881 | "peerDependenciesMeta": {
882 | "@vue/composition-api": {
883 | "optional": true
884 | }
885 | }
886 | },
887 | "node_modules/@vueuse/metadata": {
888 | "version": "11.1.0",
889 | "dev": true,
890 | "license": "MIT",
891 | "funding": {
892 | "url": "https://github.com/sponsors/antfu"
893 | }
894 | },
895 | "node_modules/@vueuse/shared": {
896 | "version": "11.1.0",
897 | "dev": true,
898 | "license": "MIT",
899 | "dependencies": {
900 | "vue-demi": ">=0.14.10"
901 | },
902 | "funding": {
903 | "url": "https://github.com/sponsors/antfu"
904 | }
905 | },
906 | "node_modules/@vueuse/shared/node_modules/vue-demi": {
907 | "version": "0.14.10",
908 | "dev": true,
909 | "hasInstallScript": true,
910 | "license": "MIT",
911 | "bin": {
912 | "vue-demi-fix": "bin/vue-demi-fix.js",
913 | "vue-demi-switch": "bin/vue-demi-switch.js"
914 | },
915 | "engines": {
916 | "node": ">=12"
917 | },
918 | "funding": {
919 | "url": "https://github.com/sponsors/antfu"
920 | },
921 | "peerDependencies": {
922 | "@vue/composition-api": "^1.0.0-rc.1",
923 | "vue": "^3.0.0-0 || ^2.6.0"
924 | },
925 | "peerDependenciesMeta": {
926 | "@vue/composition-api": {
927 | "optional": true
928 | }
929 | }
930 | },
931 | "node_modules/algoliasearch": {
932 | "version": "4.24.0",
933 | "dev": true,
934 | "license": "MIT",
935 | "dependencies": {
936 | "@algolia/cache-browser-local-storage": "4.24.0",
937 | "@algolia/cache-common": "4.24.0",
938 | "@algolia/cache-in-memory": "4.24.0",
939 | "@algolia/client-account": "4.24.0",
940 | "@algolia/client-analytics": "4.24.0",
941 | "@algolia/client-common": "4.24.0",
942 | "@algolia/client-personalization": "4.24.0",
943 | "@algolia/client-search": "4.24.0",
944 | "@algolia/logger-common": "4.24.0",
945 | "@algolia/logger-console": "4.24.0",
946 | "@algolia/recommend": "4.24.0",
947 | "@algolia/requester-browser-xhr": "4.24.0",
948 | "@algolia/requester-common": "4.24.0",
949 | "@algolia/requester-node-http": "4.24.0",
950 | "@algolia/transporter": "4.24.0"
951 | }
952 | },
953 | "node_modules/algoliasearch/node_modules/@algolia/client-common": {
954 | "version": "4.24.0",
955 | "dev": true,
956 | "license": "MIT",
957 | "dependencies": {
958 | "@algolia/requester-common": "4.24.0",
959 | "@algolia/transporter": "4.24.0"
960 | }
961 | },
962 | "node_modules/algoliasearch/node_modules/@algolia/client-search": {
963 | "version": "4.24.0",
964 | "dev": true,
965 | "license": "MIT",
966 | "dependencies": {
967 | "@algolia/client-common": "4.24.0",
968 | "@algolia/requester-common": "4.24.0",
969 | "@algolia/transporter": "4.24.0"
970 | }
971 | },
972 | "node_modules/algoliasearch/node_modules/@algolia/requester-browser-xhr": {
973 | "version": "4.24.0",
974 | "dev": true,
975 | "license": "MIT",
976 | "dependencies": {
977 | "@algolia/requester-common": "4.24.0"
978 | }
979 | },
980 | "node_modules/algoliasearch/node_modules/@algolia/requester-node-http": {
981 | "version": "4.24.0",
982 | "dev": true,
983 | "license": "MIT",
984 | "dependencies": {
985 | "@algolia/requester-common": "4.24.0"
986 | }
987 | },
988 | "node_modules/birpc": {
989 | "version": "0.2.19",
990 | "dev": true,
991 | "license": "MIT",
992 | "funding": {
993 | "url": "https://github.com/sponsors/antfu"
994 | }
995 | },
996 | "node_modules/ccount": {
997 | "version": "2.0.1",
998 | "dev": true,
999 | "license": "MIT",
1000 | "funding": {
1001 | "type": "github",
1002 | "url": "https://github.com/sponsors/wooorm"
1003 | }
1004 | },
1005 | "node_modules/character-entities-html4": {
1006 | "version": "2.1.0",
1007 | "dev": true,
1008 | "license": "MIT",
1009 | "funding": {
1010 | "type": "github",
1011 | "url": "https://github.com/sponsors/wooorm"
1012 | }
1013 | },
1014 | "node_modules/character-entities-legacy": {
1015 | "version": "3.0.0",
1016 | "dev": true,
1017 | "license": "MIT",
1018 | "funding": {
1019 | "type": "github",
1020 | "url": "https://github.com/sponsors/wooorm"
1021 | }
1022 | },
1023 | "node_modules/comma-separated-tokens": {
1024 | "version": "2.0.3",
1025 | "dev": true,
1026 | "license": "MIT",
1027 | "funding": {
1028 | "type": "github",
1029 | "url": "https://github.com/sponsors/wooorm"
1030 | }
1031 | },
1032 | "node_modules/copy-anything": {
1033 | "version": "3.0.5",
1034 | "dev": true,
1035 | "license": "MIT",
1036 | "dependencies": {
1037 | "is-what": "^4.1.8"
1038 | },
1039 | "engines": {
1040 | "node": ">=12.13"
1041 | },
1042 | "funding": {
1043 | "url": "https://github.com/sponsors/mesqueeb"
1044 | }
1045 | },
1046 | "node_modules/csstype": {
1047 | "version": "3.1.3",
1048 | "dev": true,
1049 | "license": "MIT"
1050 | },
1051 | "node_modules/dequal": {
1052 | "version": "2.0.3",
1053 | "dev": true,
1054 | "license": "MIT",
1055 | "engines": {
1056 | "node": ">=6"
1057 | }
1058 | },
1059 | "node_modules/devlop": {
1060 | "version": "1.1.0",
1061 | "dev": true,
1062 | "license": "MIT",
1063 | "dependencies": {
1064 | "dequal": "^2.0.0"
1065 | },
1066 | "funding": {
1067 | "type": "github",
1068 | "url": "https://github.com/sponsors/wooorm"
1069 | }
1070 | },
1071 | "node_modules/entities": {
1072 | "version": "4.5.0",
1073 | "dev": true,
1074 | "license": "BSD-2-Clause",
1075 | "engines": {
1076 | "node": ">=0.12"
1077 | },
1078 | "funding": {
1079 | "url": "https://github.com/fb55/entities?sponsor=1"
1080 | }
1081 | },
1082 | "node_modules/esbuild": {
1083 | "version": "0.21.5",
1084 | "dev": true,
1085 | "hasInstallScript": true,
1086 | "license": "MIT",
1087 | "bin": {
1088 | "esbuild": "bin/esbuild"
1089 | },
1090 | "engines": {
1091 | "node": ">=12"
1092 | },
1093 | "optionalDependencies": {
1094 | "@esbuild/aix-ppc64": "0.21.5",
1095 | "@esbuild/android-arm": "0.21.5",
1096 | "@esbuild/android-arm64": "0.21.5",
1097 | "@esbuild/android-x64": "0.21.5",
1098 | "@esbuild/darwin-arm64": "0.21.5",
1099 | "@esbuild/darwin-x64": "0.21.5",
1100 | "@esbuild/freebsd-arm64": "0.21.5",
1101 | "@esbuild/freebsd-x64": "0.21.5",
1102 | "@esbuild/linux-arm": "0.21.5",
1103 | "@esbuild/linux-arm64": "0.21.5",
1104 | "@esbuild/linux-ia32": "0.21.5",
1105 | "@esbuild/linux-loong64": "0.21.5",
1106 | "@esbuild/linux-mips64el": "0.21.5",
1107 | "@esbuild/linux-ppc64": "0.21.5",
1108 | "@esbuild/linux-riscv64": "0.21.5",
1109 | "@esbuild/linux-s390x": "0.21.5",
1110 | "@esbuild/linux-x64": "0.21.5",
1111 | "@esbuild/netbsd-x64": "0.21.5",
1112 | "@esbuild/openbsd-x64": "0.21.5",
1113 | "@esbuild/sunos-x64": "0.21.5",
1114 | "@esbuild/win32-arm64": "0.21.5",
1115 | "@esbuild/win32-ia32": "0.21.5",
1116 | "@esbuild/win32-x64": "0.21.5"
1117 | }
1118 | },
1119 | "node_modules/estree-walker": {
1120 | "version": "2.0.2",
1121 | "dev": true,
1122 | "license": "MIT"
1123 | },
1124 | "node_modules/focus-trap": {
1125 | "version": "7.6.0",
1126 | "dev": true,
1127 | "license": "MIT",
1128 | "dependencies": {
1129 | "tabbable": "^6.2.0"
1130 | }
1131 | },
1132 | "node_modules/hast-util-to-html": {
1133 | "version": "9.0.3",
1134 | "dev": true,
1135 | "license": "MIT",
1136 | "dependencies": {
1137 | "@types/hast": "^3.0.0",
1138 | "@types/unist": "^3.0.0",
1139 | "ccount": "^2.0.0",
1140 | "comma-separated-tokens": "^2.0.0",
1141 | "hast-util-whitespace": "^3.0.0",
1142 | "html-void-elements": "^3.0.0",
1143 | "mdast-util-to-hast": "^13.0.0",
1144 | "property-information": "^6.0.0",
1145 | "space-separated-tokens": "^2.0.0",
1146 | "stringify-entities": "^4.0.0",
1147 | "zwitch": "^2.0.4"
1148 | },
1149 | "funding": {
1150 | "type": "opencollective",
1151 | "url": "https://opencollective.com/unified"
1152 | }
1153 | },
1154 | "node_modules/hast-util-whitespace": {
1155 | "version": "3.0.0",
1156 | "dev": true,
1157 | "license": "MIT",
1158 | "dependencies": {
1159 | "@types/hast": "^3.0.0"
1160 | },
1161 | "funding": {
1162 | "type": "opencollective",
1163 | "url": "https://opencollective.com/unified"
1164 | }
1165 | },
1166 | "node_modules/hookable": {
1167 | "version": "5.5.3",
1168 | "dev": true,
1169 | "license": "MIT"
1170 | },
1171 | "node_modules/html-void-elements": {
1172 | "version": "3.0.0",
1173 | "dev": true,
1174 | "license": "MIT",
1175 | "funding": {
1176 | "type": "github",
1177 | "url": "https://github.com/sponsors/wooorm"
1178 | }
1179 | },
1180 | "node_modules/is-what": {
1181 | "version": "4.1.16",
1182 | "dev": true,
1183 | "license": "MIT",
1184 | "engines": {
1185 | "node": ">=12.13"
1186 | },
1187 | "funding": {
1188 | "url": "https://github.com/sponsors/mesqueeb"
1189 | }
1190 | },
1191 | "node_modules/js-tokens": {
1192 | "version": "4.0.0",
1193 | "license": "MIT"
1194 | },
1195 | "node_modules/loose-envify": {
1196 | "version": "1.4.0",
1197 | "license": "MIT",
1198 | "dependencies": {
1199 | "js-tokens": "^3.0.0 || ^4.0.0"
1200 | },
1201 | "bin": {
1202 | "loose-envify": "cli.js"
1203 | }
1204 | },
1205 | "node_modules/magic-string": {
1206 | "version": "0.30.12",
1207 | "dev": true,
1208 | "license": "MIT",
1209 | "dependencies": {
1210 | "@jridgewell/sourcemap-codec": "^1.5.0"
1211 | }
1212 | },
1213 | "node_modules/mark.js": {
1214 | "version": "8.11.1",
1215 | "dev": true,
1216 | "license": "MIT"
1217 | },
1218 | "node_modules/mdast-util-to-hast": {
1219 | "version": "13.2.0",
1220 | "dev": true,
1221 | "license": "MIT",
1222 | "dependencies": {
1223 | "@types/hast": "^3.0.0",
1224 | "@types/mdast": "^4.0.0",
1225 | "@ungap/structured-clone": "^1.0.0",
1226 | "devlop": "^1.0.0",
1227 | "micromark-util-sanitize-uri": "^2.0.0",
1228 | "trim-lines": "^3.0.0",
1229 | "unist-util-position": "^5.0.0",
1230 | "unist-util-visit": "^5.0.0",
1231 | "vfile": "^6.0.0"
1232 | },
1233 | "funding": {
1234 | "type": "opencollective",
1235 | "url": "https://opencollective.com/unified"
1236 | }
1237 | },
1238 | "node_modules/micromark-util-character": {
1239 | "version": "2.1.0",
1240 | "dev": true,
1241 | "funding": [
1242 | {
1243 | "type": "GitHub Sponsors",
1244 | "url": "https://github.com/sponsors/unifiedjs"
1245 | },
1246 | {
1247 | "type": "OpenCollective",
1248 | "url": "https://opencollective.com/unified"
1249 | }
1250 | ],
1251 | "license": "MIT",
1252 | "dependencies": {
1253 | "micromark-util-symbol": "^2.0.0",
1254 | "micromark-util-types": "^2.0.0"
1255 | }
1256 | },
1257 | "node_modules/micromark-util-encode": {
1258 | "version": "2.0.0",
1259 | "dev": true,
1260 | "funding": [
1261 | {
1262 | "type": "GitHub Sponsors",
1263 | "url": "https://github.com/sponsors/unifiedjs"
1264 | },
1265 | {
1266 | "type": "OpenCollective",
1267 | "url": "https://opencollective.com/unified"
1268 | }
1269 | ],
1270 | "license": "MIT"
1271 | },
1272 | "node_modules/micromark-util-sanitize-uri": {
1273 | "version": "2.0.0",
1274 | "dev": true,
1275 | "funding": [
1276 | {
1277 | "type": "GitHub Sponsors",
1278 | "url": "https://github.com/sponsors/unifiedjs"
1279 | },
1280 | {
1281 | "type": "OpenCollective",
1282 | "url": "https://opencollective.com/unified"
1283 | }
1284 | ],
1285 | "license": "MIT",
1286 | "dependencies": {
1287 | "micromark-util-character": "^2.0.0",
1288 | "micromark-util-encode": "^2.0.0",
1289 | "micromark-util-symbol": "^2.0.0"
1290 | }
1291 | },
1292 | "node_modules/micromark-util-symbol": {
1293 | "version": "2.0.0",
1294 | "dev": true,
1295 | "funding": [
1296 | {
1297 | "type": "GitHub Sponsors",
1298 | "url": "https://github.com/sponsors/unifiedjs"
1299 | },
1300 | {
1301 | "type": "OpenCollective",
1302 | "url": "https://opencollective.com/unified"
1303 | }
1304 | ],
1305 | "license": "MIT"
1306 | },
1307 | "node_modules/micromark-util-types": {
1308 | "version": "2.0.0",
1309 | "dev": true,
1310 | "funding": [
1311 | {
1312 | "type": "GitHub Sponsors",
1313 | "url": "https://github.com/sponsors/unifiedjs"
1314 | },
1315 | {
1316 | "type": "OpenCollective",
1317 | "url": "https://opencollective.com/unified"
1318 | }
1319 | ],
1320 | "license": "MIT"
1321 | },
1322 | "node_modules/minisearch": {
1323 | "version": "7.1.0",
1324 | "dev": true,
1325 | "license": "MIT"
1326 | },
1327 | "node_modules/mitt": {
1328 | "version": "3.0.1",
1329 | "dev": true,
1330 | "license": "MIT"
1331 | },
1332 | "node_modules/nanoid": {
1333 | "version": "3.3.7",
1334 | "dev": true,
1335 | "funding": [
1336 | {
1337 | "type": "github",
1338 | "url": "https://github.com/sponsors/ai"
1339 | }
1340 | ],
1341 | "license": "MIT",
1342 | "bin": {
1343 | "nanoid": "bin/nanoid.cjs"
1344 | },
1345 | "engines": {
1346 | "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
1347 | }
1348 | },
1349 | "node_modules/oniguruma-to-js": {
1350 | "version": "0.4.3",
1351 | "dev": true,
1352 | "license": "MIT",
1353 | "dependencies": {
1354 | "regex": "^4.3.2"
1355 | },
1356 | "funding": {
1357 | "url": "https://github.com/sponsors/antfu"
1358 | }
1359 | },
1360 | "node_modules/perfect-debounce": {
1361 | "version": "1.0.0",
1362 | "dev": true,
1363 | "license": "MIT"
1364 | },
1365 | "node_modules/picocolors": {
1366 | "version": "1.1.0",
1367 | "dev": true,
1368 | "license": "ISC"
1369 | },
1370 | "node_modules/postcss": {
1371 | "version": "8.4.47",
1372 | "dev": true,
1373 | "funding": [
1374 | {
1375 | "type": "opencollective",
1376 | "url": "https://opencollective.com/postcss/"
1377 | },
1378 | {
1379 | "type": "tidelift",
1380 | "url": "https://tidelift.com/funding/github/npm/postcss"
1381 | },
1382 | {
1383 | "type": "github",
1384 | "url": "https://github.com/sponsors/ai"
1385 | }
1386 | ],
1387 | "license": "MIT",
1388 | "dependencies": {
1389 | "nanoid": "^3.3.7",
1390 | "picocolors": "^1.1.0",
1391 | "source-map-js": "^1.2.1"
1392 | },
1393 | "engines": {
1394 | "node": "^10 || ^12 || >=14"
1395 | }
1396 | },
1397 | "node_modules/preact": {
1398 | "version": "10.24.3",
1399 | "dev": true,
1400 | "license": "MIT",
1401 | "funding": {
1402 | "type": "opencollective",
1403 | "url": "https://opencollective.com/preact"
1404 | }
1405 | },
1406 | "node_modules/property-information": {
1407 | "version": "6.5.0",
1408 | "dev": true,
1409 | "license": "MIT",
1410 | "funding": {
1411 | "type": "github",
1412 | "url": "https://github.com/sponsors/wooorm"
1413 | }
1414 | },
1415 | "node_modules/react": {
1416 | "version": "18.3.1",
1417 | "license": "MIT",
1418 | "dependencies": {
1419 | "loose-envify": "^1.1.0"
1420 | },
1421 | "engines": {
1422 | "node": ">=0.10.0"
1423 | }
1424 | },
1425 | "node_modules/react-audio-play": {
1426 | "version": "1.0.4",
1427 | "resolved": "https://registry.npmjs.org/react-audio-play/-/react-audio-play-1.0.4.tgz",
1428 | "integrity": "sha512-2xOOQb0VDEqac6fvjdNrbYH4LjJoCxfzDh6cryCTdJusDJ1Th92Wg4oF6QdO0ZR/YbSlmns3T4PnM94KU2QiFw==",
1429 | "peerDependencies": {
1430 | "react": ">=17"
1431 | }
1432 | },
1433 | "node_modules/react-dom": {
1434 | "version": "18.3.1",
1435 | "license": "MIT",
1436 | "dependencies": {
1437 | "loose-envify": "^1.1.0",
1438 | "scheduler": "^0.23.2"
1439 | },
1440 | "peerDependencies": {
1441 | "react": "^18.3.1"
1442 | }
1443 | },
1444 | "node_modules/regex": {
1445 | "version": "4.3.3",
1446 | "dev": true,
1447 | "license": "MIT"
1448 | },
1449 | "node_modules/rfdc": {
1450 | "version": "1.4.1",
1451 | "dev": true,
1452 | "license": "MIT"
1453 | },
1454 | "node_modules/rollup": {
1455 | "version": "4.24.0",
1456 | "dev": true,
1457 | "license": "MIT",
1458 | "dependencies": {
1459 | "@types/estree": "1.0.6"
1460 | },
1461 | "bin": {
1462 | "rollup": "dist/bin/rollup"
1463 | },
1464 | "engines": {
1465 | "node": ">=18.0.0",
1466 | "npm": ">=8.0.0"
1467 | },
1468 | "optionalDependencies": {
1469 | "@rollup/rollup-android-arm-eabi": "4.24.0",
1470 | "@rollup/rollup-android-arm64": "4.24.0",
1471 | "@rollup/rollup-darwin-arm64": "4.24.0",
1472 | "@rollup/rollup-darwin-x64": "4.24.0",
1473 | "@rollup/rollup-linux-arm-gnueabihf": "4.24.0",
1474 | "@rollup/rollup-linux-arm-musleabihf": "4.24.0",
1475 | "@rollup/rollup-linux-arm64-gnu": "4.24.0",
1476 | "@rollup/rollup-linux-arm64-musl": "4.24.0",
1477 | "@rollup/rollup-linux-powerpc64le-gnu": "4.24.0",
1478 | "@rollup/rollup-linux-riscv64-gnu": "4.24.0",
1479 | "@rollup/rollup-linux-s390x-gnu": "4.24.0",
1480 | "@rollup/rollup-linux-x64-gnu": "4.24.0",
1481 | "@rollup/rollup-linux-x64-musl": "4.24.0",
1482 | "@rollup/rollup-win32-arm64-msvc": "4.24.0",
1483 | "@rollup/rollup-win32-ia32-msvc": "4.24.0",
1484 | "@rollup/rollup-win32-x64-msvc": "4.24.0",
1485 | "fsevents": "~2.3.2"
1486 | }
1487 | },
1488 | "node_modules/scheduler": {
1489 | "version": "0.23.2",
1490 | "license": "MIT",
1491 | "dependencies": {
1492 | "loose-envify": "^1.1.0"
1493 | }
1494 | },
1495 | "node_modules/search-insights": {
1496 | "version": "2.17.2",
1497 | "dev": true,
1498 | "license": "MIT",
1499 | "peer": true
1500 | },
1501 | "node_modules/shiki": {
1502 | "version": "1.22.0",
1503 | "dev": true,
1504 | "license": "MIT",
1505 | "dependencies": {
1506 | "@shikijs/core": "1.22.0",
1507 | "@shikijs/engine-javascript": "1.22.0",
1508 | "@shikijs/engine-oniguruma": "1.22.0",
1509 | "@shikijs/types": "1.22.0",
1510 | "@shikijs/vscode-textmate": "^9.3.0",
1511 | "@types/hast": "^3.0.4"
1512 | }
1513 | },
1514 | "node_modules/source-map-js": {
1515 | "version": "1.2.1",
1516 | "dev": true,
1517 | "license": "BSD-3-Clause",
1518 | "engines": {
1519 | "node": ">=0.10.0"
1520 | }
1521 | },
1522 | "node_modules/space-separated-tokens": {
1523 | "version": "2.0.2",
1524 | "dev": true,
1525 | "license": "MIT",
1526 | "funding": {
1527 | "type": "github",
1528 | "url": "https://github.com/sponsors/wooorm"
1529 | }
1530 | },
1531 | "node_modules/speakingurl": {
1532 | "version": "14.0.1",
1533 | "dev": true,
1534 | "license": "BSD-3-Clause",
1535 | "engines": {
1536 | "node": ">=0.10.0"
1537 | }
1538 | },
1539 | "node_modules/stringify-entities": {
1540 | "version": "4.0.4",
1541 | "dev": true,
1542 | "license": "MIT",
1543 | "dependencies": {
1544 | "character-entities-html4": "^2.0.0",
1545 | "character-entities-legacy": "^3.0.0"
1546 | },
1547 | "funding": {
1548 | "type": "github",
1549 | "url": "https://github.com/sponsors/wooorm"
1550 | }
1551 | },
1552 | "node_modules/superjson": {
1553 | "version": "2.2.1",
1554 | "dev": true,
1555 | "license": "MIT",
1556 | "dependencies": {
1557 | "copy-anything": "^3.0.2"
1558 | },
1559 | "engines": {
1560 | "node": ">=16"
1561 | }
1562 | },
1563 | "node_modules/tabbable": {
1564 | "version": "6.2.0",
1565 | "dev": true,
1566 | "license": "MIT"
1567 | },
1568 | "node_modules/to-fast-properties": {
1569 | "version": "2.0.0",
1570 | "dev": true,
1571 | "license": "MIT",
1572 | "engines": {
1573 | "node": ">=4"
1574 | }
1575 | },
1576 | "node_modules/trim-lines": {
1577 | "version": "3.0.1",
1578 | "dev": true,
1579 | "license": "MIT",
1580 | "funding": {
1581 | "type": "github",
1582 | "url": "https://github.com/sponsors/wooorm"
1583 | }
1584 | },
1585 | "node_modules/undici-types": {
1586 | "version": "6.19.8",
1587 | "dev": true,
1588 | "license": "MIT"
1589 | },
1590 | "node_modules/unist-util-is": {
1591 | "version": "6.0.0",
1592 | "dev": true,
1593 | "license": "MIT",
1594 | "dependencies": {
1595 | "@types/unist": "^3.0.0"
1596 | },
1597 | "funding": {
1598 | "type": "opencollective",
1599 | "url": "https://opencollective.com/unified"
1600 | }
1601 | },
1602 | "node_modules/unist-util-position": {
1603 | "version": "5.0.0",
1604 | "dev": true,
1605 | "license": "MIT",
1606 | "dependencies": {
1607 | "@types/unist": "^3.0.0"
1608 | },
1609 | "funding": {
1610 | "type": "opencollective",
1611 | "url": "https://opencollective.com/unified"
1612 | }
1613 | },
1614 | "node_modules/unist-util-stringify-position": {
1615 | "version": "4.0.0",
1616 | "dev": true,
1617 | "license": "MIT",
1618 | "dependencies": {
1619 | "@types/unist": "^3.0.0"
1620 | },
1621 | "funding": {
1622 | "type": "opencollective",
1623 | "url": "https://opencollective.com/unified"
1624 | }
1625 | },
1626 | "node_modules/unist-util-visit": {
1627 | "version": "5.0.0",
1628 | "dev": true,
1629 | "license": "MIT",
1630 | "dependencies": {
1631 | "@types/unist": "^3.0.0",
1632 | "unist-util-is": "^6.0.0",
1633 | "unist-util-visit-parents": "^6.0.0"
1634 | },
1635 | "funding": {
1636 | "type": "opencollective",
1637 | "url": "https://opencollective.com/unified"
1638 | }
1639 | },
1640 | "node_modules/unist-util-visit-parents": {
1641 | "version": "6.0.1",
1642 | "dev": true,
1643 | "license": "MIT",
1644 | "dependencies": {
1645 | "@types/unist": "^3.0.0",
1646 | "unist-util-is": "^6.0.0"
1647 | },
1648 | "funding": {
1649 | "type": "opencollective",
1650 | "url": "https://opencollective.com/unified"
1651 | }
1652 | },
1653 | "node_modules/vfile": {
1654 | "version": "6.0.3",
1655 | "dev": true,
1656 | "license": "MIT",
1657 | "dependencies": {
1658 | "@types/unist": "^3.0.0",
1659 | "vfile-message": "^4.0.0"
1660 | },
1661 | "funding": {
1662 | "type": "opencollective",
1663 | "url": "https://opencollective.com/unified"
1664 | }
1665 | },
1666 | "node_modules/vfile-message": {
1667 | "version": "4.0.2",
1668 | "dev": true,
1669 | "license": "MIT",
1670 | "dependencies": {
1671 | "@types/unist": "^3.0.0",
1672 | "unist-util-stringify-position": "^4.0.0"
1673 | },
1674 | "funding": {
1675 | "type": "opencollective",
1676 | "url": "https://opencollective.com/unified"
1677 | }
1678 | },
1679 | "node_modules/vite": {
1680 | "version": "5.4.9",
1681 | "dev": true,
1682 | "license": "MIT",
1683 | "dependencies": {
1684 | "esbuild": "^0.21.3",
1685 | "postcss": "^8.4.43",
1686 | "rollup": "^4.20.0"
1687 | },
1688 | "bin": {
1689 | "vite": "bin/vite.js"
1690 | },
1691 | "engines": {
1692 | "node": "^18.0.0 || >=20.0.0"
1693 | },
1694 | "funding": {
1695 | "url": "https://github.com/vitejs/vite?sponsor=1"
1696 | },
1697 | "optionalDependencies": {
1698 | "fsevents": "~2.3.3"
1699 | },
1700 | "peerDependencies": {
1701 | "@types/node": "^18.0.0 || >=20.0.0",
1702 | "less": "*",
1703 | "lightningcss": "^1.21.0",
1704 | "sass": "*",
1705 | "sass-embedded": "*",
1706 | "stylus": "*",
1707 | "sugarss": "*",
1708 | "terser": "^5.4.0"
1709 | },
1710 | "peerDependenciesMeta": {
1711 | "@types/node": {
1712 | "optional": true
1713 | },
1714 | "less": {
1715 | "optional": true
1716 | },
1717 | "lightningcss": {
1718 | "optional": true
1719 | },
1720 | "sass": {
1721 | "optional": true
1722 | },
1723 | "sass-embedded": {
1724 | "optional": true
1725 | },
1726 | "stylus": {
1727 | "optional": true
1728 | },
1729 | "sugarss": {
1730 | "optional": true
1731 | },
1732 | "terser": {
1733 | "optional": true
1734 | }
1735 | }
1736 | },
1737 | "node_modules/vitepress": {
1738 | "version": "1.4.1",
1739 | "dev": true,
1740 | "license": "MIT",
1741 | "dependencies": {
1742 | "@docsearch/css": "^3.6.2",
1743 | "@docsearch/js": "^3.6.2",
1744 | "@shikijs/core": "^1.22.0",
1745 | "@shikijs/transformers": "^1.22.0",
1746 | "@shikijs/types": "^1.22.0",
1747 | "@types/markdown-it": "^14.1.2",
1748 | "@vitejs/plugin-vue": "^5.1.4",
1749 | "@vue/devtools-api": "^7.4.6",
1750 | "@vue/shared": "^3.5.12",
1751 | "@vueuse/core": "^11.1.0",
1752 | "@vueuse/integrations": "^11.1.0",
1753 | "focus-trap": "^7.6.0",
1754 | "mark.js": "8.11.1",
1755 | "minisearch": "^7.1.0",
1756 | "shiki": "^1.22.0",
1757 | "vite": "^5.4.8",
1758 | "vue": "^3.5.12"
1759 | },
1760 | "bin": {
1761 | "vitepress": "bin/vitepress.js"
1762 | },
1763 | "peerDependencies": {
1764 | "markdown-it-mathjax3": "^4",
1765 | "postcss": "^8"
1766 | },
1767 | "peerDependenciesMeta": {
1768 | "markdown-it-mathjax3": {
1769 | "optional": true
1770 | },
1771 | "postcss": {
1772 | "optional": true
1773 | }
1774 | }
1775 | },
1776 | "node_modules/vue": {
1777 | "version": "3.5.12",
1778 | "dev": true,
1779 | "license": "MIT",
1780 | "dependencies": {
1781 | "@vue/compiler-dom": "3.5.12",
1782 | "@vue/compiler-sfc": "3.5.12",
1783 | "@vue/runtime-dom": "3.5.12",
1784 | "@vue/server-renderer": "3.5.12",
1785 | "@vue/shared": "3.5.12"
1786 | },
1787 | "peerDependencies": {
1788 | "typescript": "*"
1789 | },
1790 | "peerDependenciesMeta": {
1791 | "typescript": {
1792 | "optional": true
1793 | }
1794 | }
1795 | },
1796 | "node_modules/zwitch": {
1797 | "version": "2.0.4",
1798 | "dev": true,
1799 | "license": "MIT",
1800 | "funding": {
1801 | "type": "github",
1802 | "url": "https://github.com/sponsors/wooorm"
1803 | }
1804 | }
1805 | }
1806 | }
1807 |
--------------------------------------------------------------------------------
/documentation/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "scripts": {
3 | "docs:dev": "vitepress dev docs",
4 | "docs:build": "vitepress build docs",
5 | "docs:serve": "vitepress serve docs"
6 | },
7 | "type": "module",
8 | "devDependencies": {
9 | "@types/express": "^4.17.21",
10 | "vitepress": "^1.3.4"
11 | },
12 | "dependencies": {
13 | "react": "^18.3.1",
14 | "react-audio-play": "^1.0.4",
15 | "react-dom": "^18.3.1"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/jestconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "transform": {
3 | "^.+\\.(t|j)sx?$": "ts-jest"
4 | },
5 | "testRegex": "(/tests/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$",
6 | "moduleFileExtensions": ["ts", "tsx", "js", "jsx", "json", "node"],
7 | "testEnvironment": "jsdom",
8 | "moduleNameMapper": {
9 | "^.+\\.(css|less)$": "/config/CSSStub.ts"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-audio-play",
3 | "version": "1.0.4",
4 | "description": "React audio player component",
5 | "homepage": "https://riyaddecoder.github.io/react-audio-play/",
6 | "main": "dist/cjs/index.js",
7 | "module": "dist/esm/index.js",
8 | "types": "dist/types.d.ts",
9 | "files": [
10 | "dist",
11 | "LICENSE",
12 | "README.md"
13 | ],
14 | "scripts": {
15 | "prepare": "npm run build",
16 | "prepublishOnly": "npm test && npm run prettier && npm run lint",
17 | "build": "rollup -c --bundleConfigAsCjs",
18 | "lint": "eslint \"{**/*,*}.{js,ts,jsx,tsx}\"",
19 | "prettier": "prettier --write \"{src,tests,example/src}/**/*.{js,ts,jsx,tsx}\"",
20 | "test": "jest --config jestconfig.json"
21 | },
22 | "keywords": [
23 | "best audio player for react",
24 | "simple audio player",
25 | "audio player for react",
26 | "react audio player",
27 | "react-audio",
28 | "react-audio-player",
29 | "audio-player",
30 | "typescripts"
31 | ],
32 | "author": "Shahidul Alam Riyad (riyaddecoder)",
33 | "license": "MIT",
34 | "devDependencies": {
35 | "@rollup/plugin-commonjs": "^25.0.3",
36 | "@rollup/plugin-image": "^3.0.2",
37 | "@rollup/plugin-node-resolve": "^15.1.0",
38 | "@rollup/plugin-terser": "^0.4.3",
39 | "@rollup/plugin-typescript": "^11.1.2",
40 | "@testing-library/react": "^14.0.0",
41 | "@types/jest": "^29.5.3",
42 | "@types/react": "^18.0.14",
43 | "@typescript-eslint/eslint-plugin": "^6.1.0",
44 | "@typescript-eslint/parser": "^6.1.0",
45 | "eslint": "^8.45.0",
46 | "eslint-config-prettier": "^8.8.0",
47 | "eslint-plugin-prettier": "^5.0.0",
48 | "eslint-plugin-react": "^7.33.0",
49 | "eslint-plugin-react-hooks": "^4.6.0",
50 | "jest": "^29.6.1",
51 | "jest-canvas-mock": "^2.5.2",
52 | "jest-environment-jsdom": "^29.6.1",
53 | "prettier": "^3.0.0",
54 | "react": "^18.2.0",
55 | "react-dom": "^18.2.0",
56 | "rollup": "^3.27.2",
57 | "rollup-plugin-dts": "^5.3.0",
58 | "rollup-plugin-peer-deps-external": "^2.2.4",
59 | "rollup-plugin-styles": "^4.0.0",
60 | "ts-jest": "^29.1.1",
61 | "tslib": "^2.6.0",
62 | "typescript": "^5.1.6"
63 | },
64 | "repository": {
65 | "type": "git",
66 | "url": "git+https://github.com/riyaddecoder/react-audio-play"
67 | },
68 | "peerDependencies": {
69 | "react": ">=17"
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | import dts from 'rollup-plugin-dts';
2 | import terser from '@rollup/plugin-terser';
3 | import commonjs from '@rollup/plugin-commonjs';
4 | import resolve from '@rollup/plugin-node-resolve';
5 | import typescript from '@rollup/plugin-typescript';
6 | import peerDepsExternal from 'rollup-plugin-peer-deps-external';
7 | import styles from 'rollup-plugin-styles';
8 | import image from '@rollup/plugin-image';
9 |
10 | // eslint-disable-next-line @typescript-eslint/no-var-requires
11 | const packageJson = require('./package.json');
12 |
13 | export default [
14 | {
15 | input: 'src/index.ts',
16 | output: [
17 | {
18 | file: packageJson.main,
19 | format: 'cjs',
20 | sourcemap: true
21 | },
22 | {
23 | file: packageJson.module,
24 | format: 'esm',
25 | sourcemap: true
26 | }
27 | ],
28 | plugins: [peerDepsExternal(), resolve(), styles(), commonjs(), typescript({ tsconfig: './tsconfig.json' }), terser(), image()],
29 | external: ['react', 'react-dom', 'styled-components']
30 | },
31 | {
32 | input: 'src/index.ts',
33 | output: [{ file: 'dist/types.d.ts', format: 'es' }],
34 | plugins: [dts.default(), styles()]
35 | }
36 | ];
37 |
--------------------------------------------------------------------------------
/src/assets/loading.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/riyaddecoder/react-audio-play/8353163064a1d6bcce4f44c791913a81462620c6/src/assets/loading.png
--------------------------------------------------------------------------------
/src/components/AudioPlayer.tsx:
--------------------------------------------------------------------------------
1 | import React, { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react';
2 | import { AudioInterface, AudioPlayerRef } from './core.interface';
3 | import { iconPaths } from '../helpers/icons/icons';
4 | import { formatTime } from '../helpers/utils/formatTime';
5 | import { getRangeBox } from '../helpers/utils/getRangeBox';
6 | import getDeviceEventNames from '../helpers/utils/getDeviceEventNames';
7 | import './audioPlay.css';
8 |
9 | export const AudioPlayer = forwardRef(
10 | (
11 | {
12 | autoPlay = false,
13 | className = '',
14 | src,
15 | loop = false,
16 | preload = 'auto',
17 | backgroundColor,
18 | color,
19 | width,
20 | style,
21 | sliderColor,
22 | volume = 100,
23 | volumePlacement = 'top',
24 | hasKeyBindings = true,
25 | onPlay,
26 | onPause,
27 | onEnd,
28 | onError
29 | },
30 | ref
31 | ) => {
32 | const wrapperRef = useRef(null);
33 | const audioRef = useRef(null);
34 | const currentlyDragged = useRef(null);
35 | const rewindPin = useRef(null);
36 | const volumePin = useRef(null);
37 | const [canPlay, setCanPlay] = useState(preload === 'none');
38 | const [isPlaying, setIsPlaying] = useState(false);
39 | const [progressBarPercent, setProgressBarPercent] = useState(0);
40 | const [currentTime, setCurrentTime] = useState('0:00');
41 | const [totalTime, setTotalTime] = useState('--:--');
42 | const [volumeOpen, setVolumeOpen] = useState(false);
43 | const [volumeProgress, setVolumeProgress] = useState(100);
44 | const [speakerIcon, setSpeakerIcon] = useState(getVolumePath(volume));
45 | const [coefficient, setCoefficient] = useState(0);
46 | const [hasError, setHasError] = useState(false);
47 |
48 | useEffect(() => {
49 | handleReload();
50 | // eslint-disable-next-line react-hooks/exhaustive-deps
51 | }, [src]);
52 |
53 | useEffect(() => {
54 | if (audioRef.current?.duration && audioRef.current.duration !== Infinity) {
55 | setTotalTime(formatTime(audioRef.current.duration));
56 | }
57 | }, [audioRef.current?.duration]);
58 |
59 | useEffect(() => {
60 | if (!isNaN(volume)) {
61 | const tempVol = volume > 100 ? 100 : volume < 0 ? 0 : volume;
62 | setVolumeProgress(tempVol);
63 | if (audioRef.current) {
64 | audioRef.current.volume = tempVol / 100;
65 | }
66 | }
67 | }, [volume]);
68 |
69 | useImperativeHandle(ref, () => ({
70 | play: () => {
71 | play();
72 | },
73 | pause: () => {
74 | pause();
75 | },
76 | stop: () => {
77 | stop();
78 | },
79 | focus: () => {
80 | focus();
81 | }
82 | }));
83 |
84 | const getTotalDuration = () => {
85 | if (!audioRef.current) {
86 | return 0;
87 | }
88 | return audioRef.current.duration !== Infinity ? audioRef.current.duration : audioRef.current.buffered.end(0);
89 | };
90 |
91 | const handleCanPlay = () => {
92 | setCanPlay(true);
93 | };
94 |
95 | const handleReload = () => {
96 | if (audioRef.current) {
97 | setIsPlaying(false);
98 | setTotalTime('--:--');
99 | setCanPlay(false);
100 | setHasError(false);
101 | audioRef.current.src = src;
102 | audioRef.current.load();
103 | }
104 | };
105 |
106 | const handleOnError = (event: React.SyntheticEvent) => {
107 | setCanPlay(true);
108 | setHasError(true);
109 | if (onError) {
110 | const mediaError = (event.target as HTMLAudioElement).error;
111 | let errorMessage = 'An unknown error occurred.';
112 |
113 | if (mediaError?.code) {
114 | switch (mediaError?.code) {
115 | case mediaError.MEDIA_ERR_ABORTED:
116 | errorMessage = 'The media playback was aborted.';
117 | break;
118 | case mediaError.MEDIA_ERR_NETWORK:
119 | errorMessage = 'A network error caused the media to fail.';
120 | break;
121 | case mediaError.MEDIA_ERR_DECODE:
122 | errorMessage = 'The media playback was aborted due to a decoding error.';
123 | break;
124 | case mediaError.MEDIA_ERR_SRC_NOT_SUPPORTED:
125 | errorMessage = 'The media source format is not supported.';
126 | break;
127 | default:
128 | errorMessage = 'An unknown error occurred.';
129 | break;
130 | }
131 | }
132 |
133 | onError(event, errorMessage);
134 | }
135 | };
136 |
137 | const handleEnded = () => {
138 | setIsPlaying(false);
139 | if (audioRef.current) {
140 | audioRef.current.currentTime = 0;
141 | if (totalTime === '--:--') {
142 | handleReload();
143 | }
144 | if (onEnd) {
145 | onEnd();
146 | }
147 | }
148 | };
149 |
150 | const handleUpdateVolume = () => {
151 | if (audioRef.current) {
152 | setVolumeProgress(audioRef.current.volume * 100);
153 | if (audioRef.current.volume >= 0.5) {
154 | setSpeakerIcon(iconPaths.fullVolume);
155 | } else if (audioRef.current.volume < 0.5 && audioRef.current.volume > 0.05) {
156 | setSpeakerIcon(iconPaths.midVolume);
157 | } else if (audioRef.current.volume <= 0.05) {
158 | setSpeakerIcon(iconPaths.lowVolume);
159 | }
160 | }
161 | };
162 |
163 | const handleUpdateProgress = () => {
164 | if (audioRef.current) {
165 | const current = audioRef.current.currentTime;
166 | const percent = (current / getTotalDuration()) * 100;
167 | setProgressBarPercent(percent);
168 | setCurrentTime(formatTime(current));
169 | }
170 | };
171 |
172 | const handleLoadedMetaData = () => {
173 | if (audioRef.current?.duration && audioRef.current?.duration !== Infinity) {
174 | setTotalTime(formatTime(audioRef.current.duration ?? 0));
175 | const currentTime = audioRef.current.duration * coefficient;
176 | audioRef.current.currentTime = currentTime;
177 | }
178 | };
179 |
180 | function getVolumePath(volumeLevel: number) {
181 | const MIN_VOLUME = 0;
182 | const MAX_VOLUME = 100;
183 |
184 | volumeLevel = isNaN(volumeLevel) ? 100 : Math.max(MIN_VOLUME, Math.min(volumeLevel, MAX_VOLUME));
185 |
186 | if (volumeLevel >= 50) {
187 | return iconPaths.fullVolume;
188 | } else if (volumeLevel > 5) {
189 | return iconPaths.midVolume;
190 | }
191 |
192 | return iconPaths.lowVolume;
193 | }
194 |
195 | const togglePlay = () => {
196 | if (audioRef.current) {
197 | if (preload === 'none' && !audioRef.current.duration) {
198 | setCanPlay(false);
199 | }
200 |
201 | if (audioRef.current.paused) {
202 | play();
203 | } else {
204 | pause();
205 | }
206 | }
207 | };
208 |
209 | const play = () => {
210 | if (hasError) {
211 | handleReload();
212 | } else {
213 | audioRef.current?.play();
214 | setIsPlaying(true);
215 | if (onPlay) {
216 | onPlay();
217 | }
218 | }
219 | };
220 |
221 | const pause = () => {
222 | audioRef.current?.pause();
223 | setIsPlaying(false);
224 | if (onPause) {
225 | onPause();
226 | }
227 | };
228 |
229 | const stop = () => {
230 | if (audioRef.current) {
231 | audioRef.current.pause();
232 | setIsPlaying(false);
233 | audioRef.current.currentTime = 0;
234 | }
235 | };
236 |
237 | const focus = () => {
238 | wrapperRef.current?.focus();
239 | };
240 |
241 | const inRange = (event: MouseEvent | TouchEvent | React.MouseEvent) => {
242 | const rangeBox = getRangeBox(event, currentlyDragged.current);
243 | const rect = rangeBox.getBoundingClientRect();
244 | const direction = rangeBox.dataset.direction;
245 | if (direction === 'horizontal') {
246 | const clientX = 'touches' in event ? event.touches[0].clientX : event.clientX;
247 | if (clientX - rect.left < 0 || clientX - rect.right > 0) return false;
248 | } else {
249 | const min = rect.top;
250 | const max = min + rangeBox.offsetHeight;
251 | const clientY = 'touches' in event ? event.touches[0].clientY : event.clientY;
252 | if (clientY < min || clientY > max) return false;
253 | }
254 | return true;
255 | };
256 |
257 | function getCoefficient(event: MouseEvent | TouchEvent | React.MouseEvent) {
258 | const slider = getRangeBox(event, currentlyDragged.current);
259 | const rect = slider.getBoundingClientRect();
260 | let K = 0;
261 |
262 | if (slider.dataset.direction === 'horizontal') {
263 | const clientX = 'touches' in event ? event.touches[0].clientX : event.clientX;
264 | const offsetX = clientX - rect.left;
265 | const width = slider.clientWidth;
266 | K = offsetX / width;
267 | } else if (slider.dataset.direction === 'vertical') {
268 | const clientY = 'touches' in event ? event.touches[0].clientY : event.clientY;
269 | const height = slider.clientHeight;
270 | const offsetY = clientY - rect.top;
271 | K = 1 - offsetY / height;
272 | }
273 |
274 | return K;
275 | }
276 |
277 | const rewind = (event: MouseEvent | TouchEvent | React.MouseEvent) => {
278 | if (inRange(event) && audioRef.current) {
279 | if (preload === 'none' && !audioRef.current.duration) {
280 | setCanPlay(false);
281 | audioRef.current.load();
282 | setCoefficient(getCoefficient(event));
283 | } else if (audioRef.current.duration) {
284 | audioRef.current.currentTime = getTotalDuration() * getCoefficient(event);
285 | }
286 | }
287 | };
288 |
289 | const changeVolume = (event: MouseEvent | TouchEvent | React.MouseEvent) => {
290 | if (inRange(event) && audioRef.current) {
291 | audioRef.current.volume = getCoefficient(event);
292 | }
293 | };
294 |
295 | const handleRewindDragging = () => {
296 | currentlyDragged.current = rewindPin.current;
297 | const events = getDeviceEventNames();
298 | window.addEventListener(events.move, rewind, false);
299 |
300 | window.addEventListener(
301 | events.up,
302 | () => {
303 | currentlyDragged.current = null;
304 | window.removeEventListener(events.move, rewind, false);
305 | },
306 | { once: true }
307 | );
308 | };
309 |
310 | const handleVolumeDragging = () => {
311 | currentlyDragged.current = volumePin.current;
312 | const events = getDeviceEventNames();
313 |
314 | window.addEventListener(events.move, changeVolume, false);
315 |
316 | window.addEventListener(
317 | events.up,
318 | () => {
319 | currentlyDragged.current = null;
320 | window.removeEventListener(events.move, changeVolume, false);
321 | },
322 | false
323 | );
324 | };
325 |
326 | const adjustAudioTime = (percentage: number) => {
327 | if (audioRef.current) {
328 | const currentTime = audioRef.current.currentTime + getTotalDuration() * (percentage / 100);
329 | audioRef.current.currentTime = Math.min(currentTime, getTotalDuration());
330 | }
331 | };
332 |
333 | const adjustVolume = (delta: number) => {
334 | if (audioRef.current) {
335 | audioRef.current.volume = Math.max(0, Math.min(1, audioRef.current.volume + delta));
336 | }
337 | };
338 |
339 | const handleKeyPress = (event: React.KeyboardEvent) => {
340 | if (!hasKeyBindings) {
341 | return;
342 | }
343 | event.preventDefault();
344 | switch (event.key) {
345 | case 'ArrowLeft':
346 | adjustAudioTime(-5);
347 | break;
348 | case 'ArrowRight':
349 | adjustAudioTime(5);
350 | break;
351 | case 'ArrowUp':
352 | adjustVolume(0.05);
353 | break;
354 | case 'ArrowDown':
355 | adjustVolume(-0.05);
356 | break;
357 | case ' ':
358 | togglePlay();
359 | break;
360 | default:
361 | //Nothing to do
362 | break;
363 | }
364 | };
365 |
366 | const handleOnPlay = () => {
367 | setIsPlaying(true);
368 | };
369 |
370 | return (
371 |
383 | {hasError && (
384 |
385 |
386 |
390 |
391 |
392 | )}
393 | {!canPlay && !hasError && (
394 |
397 | )}
398 | {canPlay && !hasError && (
399 |
togglePlay()}>
400 |
401 |
402 |
403 |
404 | )}
405 |
406 |
407 |
{currentTime}
408 |
426 | {totalTime !== '--:--' &&
{totalTime} }
427 |
428 |
429 |
430 |
setVolumeOpen((vol) => !vol)}>
431 |
432 |
433 |
434 |
435 |
436 |
{
439 | e.preventDefault();
440 | e.stopPropagation();
441 | }}
442 | >
443 |
461 |
462 |
setVolumeOpen(false)}>
463 |
464 |
465 |
466 |
479 |
480 |
481 |
482 | );
483 | }
484 | );
485 |
486 | AudioPlayer.displayName = 'AudioPlayer';
487 |
--------------------------------------------------------------------------------
/src/components/audioPlay.css:
--------------------------------------------------------------------------------
1 | .rap-container {
2 | max-width: 400px;
3 | height: 56px;
4 | box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.07);
5 | display: flex;
6 | justify-content: space-between;
7 | align-items: center;
8 | padding-left: 24px;
9 | padding-right: 24px;
10 | border-radius: 4px;
11 | user-select: none;
12 | -webkit-user-select: none;
13 | background-color: #fff;
14 | color: #55606e;
15 | }
16 | .rap-container:focus {
17 | outline: none;
18 | }
19 | .rap-container .rap-current-time,
20 | .rap-container .rap-total-time {
21 | min-width: 40px;
22 | }
23 | .rap-container .rap-total-time {
24 | text-align: right;
25 | }
26 | .rap-container .rap-pp-button {
27 | cursor: pointer;
28 | }
29 | .rap-container .rap-spinner {
30 | width: 18px;
31 | height: 18px;
32 | background-image: url('../assets/loading.png');
33 | background-size: cover;
34 | background-repeat: no-repeat;
35 | animation: rapSpin 0.4s linear infinite;
36 | }
37 | .rap-container .rap-slider {
38 | flex-grow: 1;
39 | background-color: #d8d8d8;
40 | cursor: pointer;
41 | position: relative;
42 | }
43 | .rap-container .rap-slider .rap-progress {
44 | background-color: #007fff;
45 | border-radius: inherit;
46 | position: absolute;
47 | pointer-events: none;
48 | }
49 | .rap-container .rap-slider .rap-progress .rap-pin {
50 | height: 16px;
51 | width: 16px;
52 | border-radius: 8px;
53 | background-color: #007fff;
54 | position: absolute;
55 | pointer-events: all;
56 | box-shadow: 0px 1px 1px 0px rgba(0, 0, 0, 0.32);
57 | }
58 | .rap-container .rap-controls {
59 | font-family: 'Roboto', sans-serif;
60 | font-size: 16px;
61 | line-height: 18px;
62 | display: flex;
63 | flex-grow: 1;
64 | justify-content: space-between;
65 | align-items: center;
66 | margin-left: 24px;
67 | margin-right: 24px;
68 | }
69 | .rap-container .rap-controls .rap-slider {
70 | margin-left: 12px;
71 | margin-right: 12px;
72 | border-radius: 2px;
73 | height: 4px;
74 | }
75 | .rap-container .rap-controls .rap-slider .rap-progress {
76 | width: 0;
77 | height: 100%;
78 | }
79 | .rap-container .rap-controls .rap-slider .rap-progress .rap-pin {
80 | right: -8px;
81 | top: -6px;
82 | }
83 | .rap-container .rap-controls span {
84 | cursor: default;
85 | }
86 | .rap-container .rap-volume {
87 | position: relative;
88 | }
89 | .rap-container .rap-volume .rap-volume-btn {
90 | cursor: pointer;
91 | }
92 | .rap-container .rap-volume .rap-volume-controls {
93 | width: 30px;
94 | height: 135px;
95 | background-color: rgba(0, 0, 0, 0.62);
96 | border-radius: 7px;
97 | position: absolute;
98 | left: -3px;
99 | bottom: 52px;
100 | flex-direction: column;
101 | align-items: center;
102 | display: flex;
103 | z-index: 1;
104 | }
105 | .rap-container .rap-volume .rap-hidden {
106 | display: none;
107 | }
108 | .rap-container .rap-volume .rap-vol-placement-top {
109 | position: absolute;
110 | left: 0;
111 | top: 32px;
112 | }
113 |
114 | .rap-container .rap-volume .rap-vol-placement-bottom {
115 | position: absolute;
116 | left: 0;
117 | top: 230px;
118 | }
119 |
120 | .rap-container .rap-volume .rap-volume-controls .rap-slider {
121 | margin-top: 12px;
122 | margin-bottom: 12px;
123 | width: 6px;
124 | border-radius: 3px;
125 | }
126 | .rap-container .rap-volume .rap-volume-controls .rap-slider .rap-progress {
127 | bottom: 0;
128 | height: 100%;
129 | width: 6px;
130 | }
131 | .rap-container .rap-volume .rap-volume-controls .rap-slider .rap-progress .rap-pin {
132 | left: -5px;
133 | top: -8px;
134 | }
135 |
136 | .rap-container svg {
137 | display: block;
138 | }
139 |
140 | .rap-backdrop {
141 | position: fixed;
142 | width: 100vw;
143 | height: 100vh;
144 | top: 0;
145 | left: 0;
146 | }
147 |
148 | @keyframes rapSpin {
149 | from {
150 | transform: rotateZ(0);
151 | }
152 | to {
153 | transform: rotateZ(1turn);
154 | }
155 | }
156 |
--------------------------------------------------------------------------------
/src/components/core.interface.ts:
--------------------------------------------------------------------------------
1 | export interface AudioInterface {
2 | autoPlay?: boolean;
3 | className?: string;
4 | src: string;
5 | loop?: boolean;
6 | preload?: 'auto' | 'metadata' | 'none';
7 | backgroundColor?: string;
8 | color?: string;
9 | width?: number | string;
10 | style?: React.CSSProperties;
11 | sliderColor?: string;
12 | volume?: number;
13 | volumePlacement?: 'top' | 'bottom';
14 | hasKeyBindings?: boolean;
15 | onPlay?: () => void;
16 | onPause?: () => void;
17 | onEnd?: () => void;
18 | onError?: (event: React.SyntheticEvent, errorMessage: string) => void;
19 | }
20 |
21 | export interface AudioPlayerRef {
22 | play: () => void;
23 | pause: () => void;
24 | stop: () => void;
25 | focus: () => void;
26 | }
27 |
--------------------------------------------------------------------------------
/src/components/index.ts:
--------------------------------------------------------------------------------
1 | export * from './AudioPlayer';
2 | export * from './core.interface';
3 |
--------------------------------------------------------------------------------
/src/helpers/icons/icons.ts:
--------------------------------------------------------------------------------
1 | export const iconPaths = {
2 | fullVolume:
3 | 'M14.667 0v2.747c3.853 1.146 6.666 4.72 6.666 8.946 0 4.227-2.813 7.787-6.666 8.934v2.76C20 22.173 24 17.4 24 11.693 24 5.987 20 1.213 14.667 0zM18 11.693c0-2.36-1.333-4.386-3.333-5.373v10.707c2-.947 3.333-2.987 3.333-5.334zm-18-4v8h5.333L12 22.36V1.027L5.333 7.693H0z',
4 | midVolume: 'M0 7.667v8h5.333L12 22.333V1L5.333 7.667M17.333 11.373C17.333 9.013 16 6.987 14 6v10.707c2-.947 3.333-2.987 3.333-5.334z',
5 | lowVolume: 'M0 7.667v8h5.333L12 22.333V1L5.333 7.667',
6 | pause: 'M0 0h6v24H0zM12 0h6v24h-6z',
7 | play: 'M18 12L0 24V0'
8 | };
9 |
--------------------------------------------------------------------------------
/src/helpers/utils/formatTime.ts:
--------------------------------------------------------------------------------
1 | export const formatTime = (time: number) => {
2 | const min = Math.floor(time / 60);
3 | const sec = Math.floor(time % 60);
4 | return min + ':' + (sec < 10 ? '0' + sec : sec);
5 | };
6 |
--------------------------------------------------------------------------------
/src/helpers/utils/getDeviceEventNames.ts:
--------------------------------------------------------------------------------
1 | type moveEvents = 'touchmove' | 'mousemove';
2 | type upEvents = 'touchend' | 'mouseup';
3 |
4 | interface returnProp {
5 | move: moveEvents;
6 | up: upEvents;
7 | }
8 |
9 | const getDeviceEventNames = (): returnProp => {
10 | if (isTouchDevice()) {
11 | return {
12 | move: 'touchmove',
13 | up: 'touchend'
14 | };
15 | }
16 | return {
17 | move: 'mousemove',
18 | up: 'mouseup'
19 | };
20 | };
21 |
22 | const isTouchDevice = () => {
23 | return 'ontouchstart' in window || navigator.maxTouchPoints;
24 | };
25 |
26 | export default getDeviceEventNames;
27 |
--------------------------------------------------------------------------------
/src/helpers/utils/getRangeBox.ts:
--------------------------------------------------------------------------------
1 | function isDraggable(el: HTMLElement) {
2 | return Array.from(el.classList).indexOf('rap-pin') !== -1;
3 | }
4 |
5 | export const getRangeBox = (event: MouseEvent | TouchEvent | React.MouseEvent, currentDragElement: HTMLDivElement | null) => {
6 | let rangeBox = event.target as HTMLElement;
7 | if (event.type === 'click' && isDraggable(rangeBox) && rangeBox?.parentElement?.parentElement) {
8 | rangeBox = rangeBox.parentElement.parentElement;
9 | }
10 | if (event.type === 'mousemove' && currentDragElement?.parentElement?.parentElement) {
11 | rangeBox = currentDragElement.parentElement.parentElement;
12 | }
13 | return rangeBox;
14 | };
15 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './components';
2 |
--------------------------------------------------------------------------------
/tests/common.test.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { render } from '@testing-library/react';
3 |
4 | import 'jest-canvas-mock';
5 |
6 | import { AudioPlayer } from '../src';
7 |
8 | beforeAll(() => {
9 | Object.defineProperty(HTMLMediaElement.prototype, 'load', {
10 | configurable: true,
11 | value: jest.fn() // Mock implementation of the load method
12 | });
13 | });
14 |
15 | describe('Common render', () => {
16 | it('renders without crashing', () => {
17 | render( );
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "esModuleInterop": true,
4 | "strict": true,
5 | "skipLibCheck": true,
6 | "jsx": "react",
7 | "module": "ESNext",
8 | "declaration": true,
9 | "declarationDir": "types",
10 | "sourceMap": true,
11 | "outDir": "dist",
12 | "moduleResolution": "node",
13 | "emitDeclarationOnly": true,
14 | "allowSyntheticDefaultImports": true,
15 | "forceConsistentCasingInFileNames": true
16 | },
17 | "exclude": ["dist", "node_modules", "src/**/*.test.tsx", "src/**/*.stories.tsx"],
18 | "include": ["**/*.ts"]
19 | }
20 |
--------------------------------------------------------------------------------