spotify-now-playing-card 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/README.md +206 -0
- package/dist/SpotifyCard.d.ts +42 -0
- package/dist/SpotifyCard.d.ts.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.esm.js +44 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/index.js +49 -0
- package/dist/index.js.map +1 -0
- package/package.json +54 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Bojan Jagetic
|
|
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
|
+
|
package/README.md
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# Spotify Now Playing Card
|
|
2
|
+
|
|
3
|
+
A beautiful, customizable React component to display your currently playing Spotify track. Perfect for personal websites, portfolios, and blogs.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🎵 Displays currently playing track from Spotify
|
|
8
|
+
- 🎨 Fully customizable styling
|
|
9
|
+
- âš¡ Built with SWR for efficient data fetching
|
|
10
|
+
- 📱 Responsive design
|
|
11
|
+
- 🔄 Auto-refresh with configurable interval
|
|
12
|
+
- 🎯 Framework agnostic (works with Next.js, Create React App, etc.)
|
|
13
|
+
- 📘 Written in TypeScript with full type definitions
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install spotify-now-playing-card
|
|
19
|
+
# or
|
|
20
|
+
yarn add spotify-now-playing-card
|
|
21
|
+
# or
|
|
22
|
+
pnpm add spotify-now-playing-card
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Prerequisites
|
|
26
|
+
|
|
27
|
+
You need to set up a Spotify API endpoint that returns the currently playing track data. The component expects the API to return JSON in this format:
|
|
28
|
+
|
|
29
|
+
```json
|
|
30
|
+
{
|
|
31
|
+
"isPlaying": true,
|
|
32
|
+
"title": "Song Title",
|
|
33
|
+
"artist": "Artist Name",
|
|
34
|
+
"album": "Album Name",
|
|
35
|
+
"albumImageUrl": "https://...",
|
|
36
|
+
"songUrl": "https://open.spotify.com/track/..."
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
When not playing, the API should return:
|
|
41
|
+
```json
|
|
42
|
+
{
|
|
43
|
+
"isPlaying": false
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Setting up Spotify API
|
|
48
|
+
|
|
49
|
+
1. Go to [Spotify Developer Dashboard](https://developer.spotify.com/dashboard)
|
|
50
|
+
2. Create a new app
|
|
51
|
+
3. Get your `CLIENT_ID` and `CLIENT_SECRET`
|
|
52
|
+
4. Set redirect URI (e.g., `http://localhost:3000/api/spotify/callback`)
|
|
53
|
+
5. Get your refresh token (you can use tools like [this](https://github.com/spotify/web-api-auth-examples))
|
|
54
|
+
|
|
55
|
+
### Example API Endpoint (Next.js)
|
|
56
|
+
|
|
57
|
+
Create `pages/api/spotify.js`:
|
|
58
|
+
|
|
59
|
+
```javascript
|
|
60
|
+
import querystring from 'querystring'
|
|
61
|
+
import axios from 'axios'
|
|
62
|
+
|
|
63
|
+
const {
|
|
64
|
+
SPOTIFY_CLIENT_ID: client_id,
|
|
65
|
+
SPOTIFY_CLIENT_SECRET: client_secret,
|
|
66
|
+
SPOTIFY_REFRESH_TOKEN: refresh_token
|
|
67
|
+
} = process.env
|
|
68
|
+
|
|
69
|
+
const basic = Buffer.from(`${client_id}:${client_secret}`).toString('base64')
|
|
70
|
+
const NOW_PLAYING_ENDPOINT = 'https://api.spotify.com/v1/me/player/currently-playing'
|
|
71
|
+
const TOKEN_ENDPOINT = 'https://accounts.spotify.com/api/token'
|
|
72
|
+
|
|
73
|
+
const getAccessToken = async () => {
|
|
74
|
+
const response = await axios.post(
|
|
75
|
+
TOKEN_ENDPOINT,
|
|
76
|
+
querystring.stringify({
|
|
77
|
+
grant_type: 'refresh_token',
|
|
78
|
+
refresh_token
|
|
79
|
+
}),
|
|
80
|
+
{
|
|
81
|
+
headers: {
|
|
82
|
+
Authorization: `Basic ${basic}`,
|
|
83
|
+
'Content-Type': 'application/x-www-form-urlencoded'
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
)
|
|
87
|
+
return response.data
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export default async function handler(req, res) {
|
|
91
|
+
try {
|
|
92
|
+
const { access_token } = await getAccessToken()
|
|
93
|
+
const response = await axios.get(NOW_PLAYING_ENDPOINT, {
|
|
94
|
+
headers: {
|
|
95
|
+
Authorization: `Bearer ${access_token}`
|
|
96
|
+
}
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
if (
|
|
100
|
+
response.status === 204 ||
|
|
101
|
+
response.status > 400 ||
|
|
102
|
+
(response.data && response.data.currently_playing_type !== 'track')
|
|
103
|
+
) {
|
|
104
|
+
return res.status(200).json({ isPlaying: false })
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const data = {
|
|
108
|
+
isPlaying: response.data.is_playing,
|
|
109
|
+
title: response.data.item.name,
|
|
110
|
+
album: response.data.item.album.name,
|
|
111
|
+
artist: response.data.item.album.artists.map((artist) => artist.name).join(', '),
|
|
112
|
+
albumImageUrl: response.data.item.album.images[0].url,
|
|
113
|
+
songUrl: response.data.item.external_urls.spotify
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
res.status(200).json(data)
|
|
117
|
+
} catch (error) {
|
|
118
|
+
res.status(200).json({ isPlaying: false })
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Usage
|
|
124
|
+
|
|
125
|
+
### Basic Usage
|
|
126
|
+
|
|
127
|
+
```jsx
|
|
128
|
+
import { SpotifyCard } from 'spotify-now-playing-card'
|
|
129
|
+
|
|
130
|
+
function App() {
|
|
131
|
+
return <SpotifyCard apiUrl="/api/spotify" />
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### TypeScript Usage
|
|
136
|
+
|
|
137
|
+
The package includes full TypeScript definitions:
|
|
138
|
+
|
|
139
|
+
```tsx
|
|
140
|
+
import { SpotifyCard, SpotifyCardProps } from 'spotify-now-playing-card'
|
|
141
|
+
|
|
142
|
+
const MyComponent: React.FC = () => {
|
|
143
|
+
const cardProps: SpotifyCardProps = {
|
|
144
|
+
apiUrl: '/api/spotify',
|
|
145
|
+
showAlbum: true,
|
|
146
|
+
refreshInterval: 30000
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return <SpotifyCard {...cardProps} />
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### With Custom Styling
|
|
154
|
+
|
|
155
|
+
```jsx
|
|
156
|
+
import { SpotifyCard } from 'spotify-now-playing-card'
|
|
157
|
+
|
|
158
|
+
function App() {
|
|
159
|
+
return (
|
|
160
|
+
<SpotifyCard
|
|
161
|
+
apiUrl="/api/spotify"
|
|
162
|
+
className="my-custom-class"
|
|
163
|
+
styles={{
|
|
164
|
+
container: "bg-gray-900 rounded-xl",
|
|
165
|
+
title: "text-white text-lg",
|
|
166
|
+
artist: "text-gray-400"
|
|
167
|
+
}}
|
|
168
|
+
/>
|
|
169
|
+
)
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### All Props
|
|
174
|
+
|
|
175
|
+
| Prop | Type | Default | Description |
|
|
176
|
+
|------|------|---------|-------------|
|
|
177
|
+
| `apiUrl` | `string` | `'/api/spotify'` | API endpoint URL that returns Spotify data |
|
|
178
|
+
| `fallbackUrl` | `string` | `'https://open.spotify.com'` | URL to redirect when not playing |
|
|
179
|
+
| `className` | `string` | `''` | Additional CSS classes for container |
|
|
180
|
+
| `styles` | `object` | `{}` | Custom style overrides for different elements |
|
|
181
|
+
| `showAlbum` | `boolean` | `true` | Whether to show album name |
|
|
182
|
+
| `notPlayingText` | `string` | `'Currently not listening'` | Text when not playing |
|
|
183
|
+
| `refreshInterval` | `number` | `30000` | Auto-refresh interval in milliseconds |
|
|
184
|
+
|
|
185
|
+
### Styling
|
|
186
|
+
|
|
187
|
+
The component uses Tailwind CSS classes by default. Make sure you have Tailwind CSS installed and configured in your project.
|
|
188
|
+
|
|
189
|
+
If you're not using Tailwind, you can override all styles using the `styles` prop or `className` prop.
|
|
190
|
+
|
|
191
|
+
## Dependencies
|
|
192
|
+
|
|
193
|
+
- `react` (>=16.8.0)
|
|
194
|
+
- `react-dom` (>=16.8.0)
|
|
195
|
+
- `axios` (for API calls)
|
|
196
|
+
- `swr` (for data fetching)
|
|
197
|
+
- `react-icons` (for Spotify icon)
|
|
198
|
+
|
|
199
|
+
## License
|
|
200
|
+
|
|
201
|
+
MIT
|
|
202
|
+
|
|
203
|
+
## Contributing
|
|
204
|
+
|
|
205
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
206
|
+
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export interface SpotifyData {
|
|
3
|
+
isPlaying: boolean;
|
|
4
|
+
title?: string;
|
|
5
|
+
artist?: string;
|
|
6
|
+
album?: string;
|
|
7
|
+
albumImageUrl?: string;
|
|
8
|
+
songUrl?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface SpotifyCardStyles {
|
|
11
|
+
container?: string;
|
|
12
|
+
link?: string;
|
|
13
|
+
image?: string;
|
|
14
|
+
title?: string;
|
|
15
|
+
artist?: string;
|
|
16
|
+
album?: string;
|
|
17
|
+
icon?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface SpotifyCardProps {
|
|
20
|
+
/** The API endpoint URL that returns Spotify data */
|
|
21
|
+
apiUrl?: string;
|
|
22
|
+
/** URL to redirect when not playing (default: Spotify profile) */
|
|
23
|
+
fallbackUrl?: string;
|
|
24
|
+
/** Additional CSS classes for the card container */
|
|
25
|
+
className?: string;
|
|
26
|
+
/** Custom style overrides for different elements */
|
|
27
|
+
styles?: SpotifyCardStyles;
|
|
28
|
+
/** Whether to show album name (default: true) */
|
|
29
|
+
showAlbum?: boolean;
|
|
30
|
+
/** Text to show when not playing (default: "Currently not listening") */
|
|
31
|
+
notPlayingText?: string;
|
|
32
|
+
/** Auto-refresh interval in milliseconds (default: 30000) */
|
|
33
|
+
refreshInterval?: number;
|
|
34
|
+
/** Additional props to pass to the container div */
|
|
35
|
+
[key: string]: any;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* SpotifyCard - A React component to display currently playing Spotify track
|
|
39
|
+
*/
|
|
40
|
+
declare const SpotifyCard: React.FC<SpotifyCardProps>;
|
|
41
|
+
export default SpotifyCard;
|
|
42
|
+
//# sourceMappingURL=SpotifyCard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SpotifyCard.d.ts","sourceRoot":"","sources":["../src/SpotifyCard.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAOzB,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,OAAO,CAAA;IAClB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,iBAAiB;IAChC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,gBAAgB;IAC/B,qDAAqD;IACrD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,kEAAkE;IAClE,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,oDAAoD;IACpD,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,oDAAoD;IACpD,MAAM,CAAC,EAAE,iBAAiB,CAAA;IAC1B,iDAAiD;IACjD,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,yEAAyE;IACzE,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,6DAA6D;IAC7D,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,oDAAoD;IACpD,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;CACnB;AAED;;GAEG;AACH,QAAA,MAAM,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,gBAAgB,CAsF3C,CAAA;AAED,eAAe,WAAW,CAAA"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,eAAe,CAAA;AACtD,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AACvC,YAAY,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
2
|
+
import axios from 'axios';
|
|
3
|
+
import { SiSpotify } from 'react-icons/si';
|
|
4
|
+
import useSWR from 'swr';
|
|
5
|
+
|
|
6
|
+
const fetcher = (url) => axios.get(url).then(res => res.data);
|
|
7
|
+
/**
|
|
8
|
+
* SpotifyCard - A React component to display currently playing Spotify track
|
|
9
|
+
*/
|
|
10
|
+
const SpotifyCard = ({ apiUrl = '/api/spotify', fallbackUrl = 'https://open.spotify.com', className = '', styles = {}, showAlbum = true, notPlayingText = 'Currently not listening', refreshInterval = 30000, ...props }) => {
|
|
11
|
+
const { data, error } = useSWR(apiUrl, fetcher, {
|
|
12
|
+
refreshInterval: refreshInterval,
|
|
13
|
+
revalidateOnFocus: true
|
|
14
|
+
});
|
|
15
|
+
if (error) {
|
|
16
|
+
console.error('SpotifyCard error:', error);
|
|
17
|
+
}
|
|
18
|
+
const defaultStyles = {
|
|
19
|
+
container: 'text-slate-400 border border-neutral-800 rounded-lg w-full mx-5 my-2',
|
|
20
|
+
link: 'relative flex items-center space-x-2 py-4 pl-2 transition-shadow hover:shadow-md',
|
|
21
|
+
image: 'w-14 shadow-sm',
|
|
22
|
+
title: 'font-bold text-md w-48 lg:w-72 truncate',
|
|
23
|
+
artist: 'text-sm font-semibold',
|
|
24
|
+
album: 'text-xs font-semibold w-48 lg:w-72 truncate',
|
|
25
|
+
icon: 'absolute right-1.5 top-1.5'
|
|
26
|
+
};
|
|
27
|
+
const mergedStyles = {
|
|
28
|
+
container: `${defaultStyles.container} ${styles.container || ''} ${className}`,
|
|
29
|
+
link: `${defaultStyles.link} ${styles.link || ''}`,
|
|
30
|
+
image: `${defaultStyles.image} ${styles.image || ''}`,
|
|
31
|
+
title: `${defaultStyles.title} ${styles.title || ''}`,
|
|
32
|
+
artist: `${defaultStyles.artist} ${styles.artist || ''}`,
|
|
33
|
+
album: `${defaultStyles.album} ${styles.album || ''}`,
|
|
34
|
+
icon: `${defaultStyles.icon} ${styles.icon || ''}`
|
|
35
|
+
};
|
|
36
|
+
const isPlaying = data?.isPlaying;
|
|
37
|
+
const titleColor = isPlaying
|
|
38
|
+
? 'text-transparent bg-clip-text bg-gradient-to-r from-violet-500 via-blue-500 to-emerald-500'
|
|
39
|
+
: '';
|
|
40
|
+
return (jsx("div", { className: mergedStyles.container, ...props, children: jsxs("a", { target: '_blank', rel: 'noreferrer', href: isPlaying ? data?.songUrl : fallbackUrl, className: mergedStyles.link, children: [jsx("div", { className: 'w-15', children: isPlaying ? (jsx("img", { className: mergedStyles.image, src: data?.albumImageUrl, alt: data?.album || 'Album cover' })) : (jsx(SiSpotify, { size: 30, color: '#1ED760' })) }), jsxs("div", { className: 'flex-1', children: [jsx("p", { className: `${mergedStyles.title} ${titleColor}`, children: isPlaying ? data?.title : notPlayingText }), jsx("p", { className: mergedStyles.artist, children: isPlaying ? `Artist: ${data?.artist}` : 'Spotify' }), showAlbum && isPlaying && (jsxs("p", { className: mergedStyles.album, children: ["Album: ", data?.album] }))] }), isPlaying && (jsx("div", { className: mergedStyles.icon, children: jsx(SiSpotify, { size: 20, color: '#1ED760' }) }))] }) }));
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export { SpotifyCard, SpotifyCard as default };
|
|
44
|
+
//# sourceMappingURL=index.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.esm.js","sources":["../src/SpotifyCard.tsx"],"sourcesContent":[null],"names":["_jsx","_jsxs"],"mappings":"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"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
6
|
+
var axios = require('axios');
|
|
7
|
+
var si = require('react-icons/si');
|
|
8
|
+
var useSWR = require('swr');
|
|
9
|
+
|
|
10
|
+
const fetcher = (url) => axios.get(url).then(res => res.data);
|
|
11
|
+
/**
|
|
12
|
+
* SpotifyCard - A React component to display currently playing Spotify track
|
|
13
|
+
*/
|
|
14
|
+
const SpotifyCard = ({ apiUrl = '/api/spotify', fallbackUrl = 'https://open.spotify.com', className = '', styles = {}, showAlbum = true, notPlayingText = 'Currently not listening', refreshInterval = 30000, ...props }) => {
|
|
15
|
+
const { data, error } = useSWR(apiUrl, fetcher, {
|
|
16
|
+
refreshInterval: refreshInterval,
|
|
17
|
+
revalidateOnFocus: true
|
|
18
|
+
});
|
|
19
|
+
if (error) {
|
|
20
|
+
console.error('SpotifyCard error:', error);
|
|
21
|
+
}
|
|
22
|
+
const defaultStyles = {
|
|
23
|
+
container: 'text-slate-400 border border-neutral-800 rounded-lg w-full mx-5 my-2',
|
|
24
|
+
link: 'relative flex items-center space-x-2 py-4 pl-2 transition-shadow hover:shadow-md',
|
|
25
|
+
image: 'w-14 shadow-sm',
|
|
26
|
+
title: 'font-bold text-md w-48 lg:w-72 truncate',
|
|
27
|
+
artist: 'text-sm font-semibold',
|
|
28
|
+
album: 'text-xs font-semibold w-48 lg:w-72 truncate',
|
|
29
|
+
icon: 'absolute right-1.5 top-1.5'
|
|
30
|
+
};
|
|
31
|
+
const mergedStyles = {
|
|
32
|
+
container: `${defaultStyles.container} ${styles.container || ''} ${className}`,
|
|
33
|
+
link: `${defaultStyles.link} ${styles.link || ''}`,
|
|
34
|
+
image: `${defaultStyles.image} ${styles.image || ''}`,
|
|
35
|
+
title: `${defaultStyles.title} ${styles.title || ''}`,
|
|
36
|
+
artist: `${defaultStyles.artist} ${styles.artist || ''}`,
|
|
37
|
+
album: `${defaultStyles.album} ${styles.album || ''}`,
|
|
38
|
+
icon: `${defaultStyles.icon} ${styles.icon || ''}`
|
|
39
|
+
};
|
|
40
|
+
const isPlaying = data?.isPlaying;
|
|
41
|
+
const titleColor = isPlaying
|
|
42
|
+
? 'text-transparent bg-clip-text bg-gradient-to-r from-violet-500 via-blue-500 to-emerald-500'
|
|
43
|
+
: '';
|
|
44
|
+
return (jsxRuntime.jsx("div", { className: mergedStyles.container, ...props, children: jsxRuntime.jsxs("a", { target: '_blank', rel: 'noreferrer', href: isPlaying ? data?.songUrl : fallbackUrl, className: mergedStyles.link, children: [jsxRuntime.jsx("div", { className: 'w-15', children: isPlaying ? (jsxRuntime.jsx("img", { className: mergedStyles.image, src: data?.albumImageUrl, alt: data?.album || 'Album cover' })) : (jsxRuntime.jsx(si.SiSpotify, { size: 30, color: '#1ED760' })) }), jsxRuntime.jsxs("div", { className: 'flex-1', children: [jsxRuntime.jsx("p", { className: `${mergedStyles.title} ${titleColor}`, children: isPlaying ? data?.title : notPlayingText }), jsxRuntime.jsx("p", { className: mergedStyles.artist, children: isPlaying ? `Artist: ${data?.artist}` : 'Spotify' }), showAlbum && isPlaying && (jsxRuntime.jsxs("p", { className: mergedStyles.album, children: ["Album: ", data?.album] }))] }), isPlaying && (jsxRuntime.jsx("div", { className: mergedStyles.icon, children: jsxRuntime.jsx(si.SiSpotify, { size: 20, color: '#1ED760' }) }))] }) }));
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
exports.SpotifyCard = SpotifyCard;
|
|
48
|
+
exports.default = SpotifyCard;
|
|
49
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/SpotifyCard.tsx"],"sourcesContent":[null],"names":["_jsx","_jsxs","SiSpotify"],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "spotify-now-playing-card",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "A beautiful React component to display your currently playing Spotify track",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"module": "dist/index.esm.js",
|
|
8
|
+
"types": "dist/index.d.ts",
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"README.md"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "rollup -c",
|
|
15
|
+
"dev": "rollup -c -w",
|
|
16
|
+
"prepublishOnly": "npm run build"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"spotify",
|
|
20
|
+
"react",
|
|
21
|
+
"component",
|
|
22
|
+
"now-playing",
|
|
23
|
+
"music",
|
|
24
|
+
"card",
|
|
25
|
+
"widget"
|
|
26
|
+
],
|
|
27
|
+
"author": "Bojan Jagetic",
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "https://github.com/yourusername/spotify-now-playing-card.git"
|
|
32
|
+
},
|
|
33
|
+
"peerDependencies": {
|
|
34
|
+
"react": ">=16.8.0",
|
|
35
|
+
"react-dom": ">=16.8.0"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"axios": "^1.6.4",
|
|
39
|
+
"react-icons": "^4.3.1",
|
|
40
|
+
"swr": "^1.3.0"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@rollup/plugin-commonjs": "^25.0.7",
|
|
44
|
+
"@rollup/plugin-node-resolve": "^15.2.3",
|
|
45
|
+
"@rollup/plugin-typescript": "^11.1.5",
|
|
46
|
+
"@types/react": "^18.2.43",
|
|
47
|
+
"@types/react-dom": "^18.2.17",
|
|
48
|
+
"react": "^18.2.0",
|
|
49
|
+
"react-dom": "^18.2.0",
|
|
50
|
+
"rollup": "^4.6.1",
|
|
51
|
+
"tslib": "^2.8.1",
|
|
52
|
+
"typescript": "^5.3.3"
|
|
53
|
+
}
|
|
54
|
+
}
|