social-masonry 1.0.0 → 1.0.1

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/README.md CHANGED
@@ -1,291 +1,103 @@
1
1
  <div align="center">
2
- <img src="https://raw.githubusercontent.com/tkana-dev/social-masonry/main/assets/logo.svg" alt="Social Masonry" width="120" />
3
2
  <h1>Social Masonry</h1>
4
- <p><strong>Beautiful masonry layout for X (Twitter) and Instagram embeds</strong></p>
5
-
3
+ <p><strong>Masonry layout for X (Twitter) and Instagram embeds using official widgets</strong></p>
4
+
6
5
  [![npm version](https://img.shields.io/npm/v/social-masonry.svg)](https://www.npmjs.com/package/social-masonry)
7
6
  [![bundle size](https://img.shields.io/bundlephobia/minzip/social-masonry)](https://bundlephobia.com/package/social-masonry)
8
7
  [![license](https://img.shields.io/npm/l/social-masonry.svg)](https://github.com/tkana-dev/social-masonry/blob/main/LICENSE)
9
8
  [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue.svg)](https://www.typescriptlang.org/)
10
-
11
- <a href="https://social-masonry.dev">Demo</a> · <a href="#installation">Installation</a> · <a href="#usage">Usage</a> · <a href="#api">API</a>
12
9
  </div>
13
10
 
14
11
  ---
15
12
 
16
- <p align="center">
17
- <img src="https://raw.githubusercontent.com/tkana-dev/social-masonry/main/assets/demo.gif" alt="Demo" width="100%" />
18
- </p>
19
-
20
- ## ✨ Features
13
+ ## Features
21
14
 
22
- - 🎨 **Beautiful Cards** - Native-looking X and Instagram post cards with 5 design variants
23
- - **High Performance** - Virtual scrolling for smooth rendering of thousands of posts
24
- - 📱 **Responsive** - Adaptive column layouts that look great on any screen size
25
- - 🎭 **Themeable** - Light, dark, and auto themes with full customization
26
- - 🔄 **Infinite Scroll** - Built-in load more functionality
27
- - 🎯 **TypeScript** - Full type safety and excellent DX
28
- - ⚛️ **React Ready** - First-class React component with hooks
29
- - 🪶 **Lightweight** - ~8KB gzipped with zero dependencies
15
+ - **Official Widgets** - Uses Twitter's `widgets.js` and Instagram's `embed.js` for native embeds
16
+ - **Auto-sizing** - Embeds automatically adjust to their content height
17
+ - **Responsive** - Adaptive column layouts that look great on any screen size
18
+ - **Themeable** - Light and dark theme support for Twitter embeds
19
+ - **TypeScript** - Full type safety
20
+ - **React Ready** - First-class React component with ref support
21
+ - **Lightweight** - Minimal bundle size, widgets loaded on-demand
30
22
 
31
- ## 📦 Installation
23
+ ## Installation
32
24
 
33
25
  ```bash
34
- # npm
35
26
  npm install social-masonry
36
-
37
- # yarn
38
- yarn add social-masonry
39
-
40
- # pnpm
41
- pnpm add social-masonry
42
27
  ```
43
28
 
44
- ## 🚀 Quick Start
45
-
46
- ### Vanilla JavaScript
47
-
48
- ```javascript
49
- import { createSocialMasonry } from 'social-masonry';
50
- import 'social-masonry/styles';
51
-
52
- const masonry = createSocialMasonry({
53
- container: '#posts',
54
- posts: [
55
- {
56
- platform: 'twitter',
57
- id: '1',
58
- url: 'https://twitter.com/user/status/123',
59
- author: {
60
- username: 'johndoe',
61
- displayName: 'John Doe',
62
- avatarUrl: 'https://...',
63
- verified: true,
64
- },
65
- content: {
66
- text: 'Hello, world! 🌍',
67
- },
68
- metrics: {
69
- likes: 1234,
70
- retweets: 567,
71
- replies: 89,
72
- },
73
- createdAt: '2024-01-15T10:30:00Z',
74
- },
75
- // ... more posts
76
- ],
77
- });
78
-
79
- // Add more posts
80
- masonry.addPosts(newPosts);
81
-
82
- // Clean up
83
- masonry.destroy();
84
- ```
29
+ ## Quick Start
85
30
 
86
31
  ### React
87
32
 
88
33
  ```tsx
89
34
  import { SocialMasonry } from 'social-masonry/react';
90
- import 'social-masonry/styles';
91
35
 
92
36
  function App() {
93
- const posts = usePosts();
94
-
37
+ const posts = [
38
+ {
39
+ id: '1',
40
+ platform: 'twitter',
41
+ url: 'https://twitter.com/username/status/1234567890',
42
+ },
43
+ {
44
+ id: '2',
45
+ platform: 'instagram',
46
+ url: 'https://www.instagram.com/p/ABC123xyz/',
47
+ },
48
+ ];
49
+
95
50
  return (
96
51
  <SocialMasonry
97
52
  posts={posts}
98
53
  columns={[
99
- { columns: 4, minWidth: 1200 },
100
- { columns: 3, minWidth: 900 },
101
- { columns: 2, minWidth: 600 },
54
+ { columns: 4, minWidth: 1536 },
55
+ { columns: 3, minWidth: 1024 },
56
+ { columns: 2, minWidth: 640 },
102
57
  { columns: 1, minWidth: 0 },
103
58
  ]}
104
59
  gap={16}
105
- variant="elevated"
106
- theme="auto"
107
- onPostClick={(post) => window.open(post.url)}
60
+ theme="light"
108
61
  />
109
62
  );
110
63
  }
111
64
  ```
112
65
 
113
- ## 📖 Usage
114
-
115
- ### Post Types
66
+ ## API
116
67
 
117
- #### Twitter/X Post
68
+ ### SocialMasonry Props
118
69
 
119
- ```typescript
120
- const twitterPost: TwitterPost = {
121
- platform: 'twitter',
122
- id: 'unique-id',
123
- url: 'https://twitter.com/user/status/123',
124
- author: {
125
- username: 'johndoe',
126
- displayName: 'John Doe',
127
- avatarUrl: 'https://example.com/avatar.jpg',
128
- verified: true,
129
- },
130
- content: {
131
- text: 'This is my tweet! #awesome',
132
- html: 'This is my tweet! <a href="#">#awesome</a>', // Optional: pre-rendered HTML
133
- },
134
- media: [
135
- {
136
- type: 'image',
137
- url: 'https://example.com/image.jpg',
138
- aspectRatio: 16 / 9,
139
- },
140
- ],
141
- metrics: {
142
- likes: 1234,
143
- retweets: 567,
144
- replies: 89,
145
- views: 50000,
146
- },
147
- quotedPost: { /* nested TwitterPost */ }, // Optional
148
- createdAt: '2024-01-15T10:30:00Z',
149
- };
150
- ```
151
-
152
- #### Instagram Post
153
-
154
- ```typescript
155
- const instagramPost: InstagramPost = {
156
- platform: 'instagram',
157
- id: 'unique-id',
158
- url: 'https://instagram.com/p/ABC123',
159
- author: {
160
- username: 'janedoe',
161
- displayName: 'Jane Doe',
162
- avatarUrl: 'https://example.com/avatar.jpg',
163
- verified: false,
164
- },
165
- content: {
166
- caption: 'Beautiful sunset 🌅 #photography',
167
- },
168
- media: {
169
- type: 'image', // 'image' | 'video' | 'carousel'
170
- url: 'https://example.com/image.jpg',
171
- aspectRatio: 1, // Square
172
- carouselItems: [ /* for carousel type */ ],
173
- },
174
- metrics: {
175
- likes: 5678,
176
- comments: 123,
177
- },
178
- createdAt: '2024-01-15T10:30:00Z',
179
- };
180
- ```
70
+ | Prop | Type | Default | Description |
71
+ |------|------|---------|-------------|
72
+ | `posts` | `SocialPost[]` | `[]` | Array of posts to display |
73
+ | `columns` | `number \| ColumnConfig[]` | `3` | Number of columns or responsive config |
74
+ | `gap` | `number` | `16` | Gap between items in pixels |
75
+ | `theme` | `'light' \| 'dark'` | `'light'` | Theme for Twitter embeds |
76
+ | `className` | `string` | - | Custom class for container |
77
+ | `style` | `CSSProperties` | - | Custom styles for container |
78
+ | `onEmbedLoad` | `(post: SocialPost) => void` | - | Called when an embed loads |
79
+ | `onEmbedError` | `(post: SocialPost, error: Error) => void` | - | Called on embed error |
181
80
 
182
- ### Configuration Options
81
+ ### SocialPost Type
183
82
 
184
83
  ```typescript
185
- const masonry = createSocialMasonry({
186
- // Required
187
- container: '#posts', // Element or selector
188
- posts: [], // Array of SocialPost
189
-
190
- // Layout
191
- gap: 16, // Gap between cards (px)
192
- columns: [ // Responsive breakpoints
193
- { columns: 4, minWidth: 1200 },
194
- { columns: 3, minWidth: 900 },
195
- { columns: 2, minWidth: 600 },
196
- { columns: 1, minWidth: 0 },
197
- ],
198
- padding: 0, // Container padding
199
-
200
- // Animation
201
- animate: true,
202
- animationDuration: 300,
203
- easing: 'cubic-bezier(0.4, 0, 0.2, 1)',
204
-
205
- // Card Styling
206
- variant: 'default', // 'default' | 'minimal' | 'elevated' | 'bordered' | 'glass'
207
- theme: 'auto', // 'light' | 'dark' | 'auto'
208
- borderRadius: 12,
209
- hoverEffect: true,
210
-
211
- // Content
212
- showPlatformIcon: true,
213
- showAuthor: true,
214
- showMetrics: true,
215
- showTimestamp: true,
216
-
217
- // Custom Formatters
218
- formatDate: (date) => formatRelativeTime(date),
219
- formatNumber: (num) => formatNumber(num),
220
-
221
- // Image Loading
222
- imageLoading: 'lazy', // 'lazy' | 'eager'
223
- fallbackImage: 'https://...', // Fallback for broken images
224
-
225
- // Virtualization (for large lists)
226
- virtualization: {
227
- enabled: false,
228
- overscan: 3,
229
- estimatedItemHeight: 400,
230
- scrollContainer: null, // Default: window
231
- },
232
-
233
- // Infinite Scroll
234
- loadMoreThreshold: 500,
235
- showLoading: true,
236
- loadingElement: '<div>Loading...</div>',
237
- emptyMessage: 'No posts to display',
238
-
239
- // Events
240
- onPostClick: (post, event) => {},
241
- onAuthorClick: (post, event) => {},
242
- onMediaClick: (post, mediaIndex, event) => {},
243
- onLayoutComplete: (positions) => {},
244
- onLoadMore: async () => {},
245
- onImageError: (post, error) => {},
246
- });
84
+ interface SocialPost {
85
+ id?: string; // Optional unique identifier
86
+ platform: 'twitter' | 'instagram'; // Social platform
87
+ url: string; // Post URL
88
+ }
247
89
  ```
248
90
 
249
- ### Card Variants
250
-
251
- | Variant | Description |
252
- |---------|-------------|
253
- | `default` | Clean card with subtle border and shadow |
254
- | `minimal` | No background, perfect for embedding |
255
- | `elevated` | Floating card with prominent shadow |
256
- | `bordered` | Strong border, no shadow |
257
- | `glass` | Glassmorphism effect with blur |
258
-
259
- ### API Methods
91
+ ### ColumnConfig Type
260
92
 
261
93
  ```typescript
262
- // Add posts to the end
263
- masonry.addPosts(newPosts);
264
-
265
- // Replace all posts
266
- masonry.setPosts(posts);
267
-
268
- // Remove a post
269
- masonry.removePost(postId);
270
-
271
- // Update options
272
- masonry.setOptions({ theme: 'dark' });
273
-
274
- // Get current state
275
- const state = masonry.getLayoutState();
276
- const posts = masonry.getPosts();
277
-
278
- // Scroll to a post
279
- masonry.scrollToPost(postId, 'smooth');
280
-
281
- // Recalculate layout
282
- masonry.refresh();
283
-
284
- // Clean up
285
- masonry.destroy();
94
+ interface ColumnConfig {
95
+ columns: number; // Number of columns
96
+ minWidth: number; // Minimum container width for this config
97
+ }
286
98
  ```
287
99
 
288
- ### React Hooks
100
+ ### Ref Methods
289
101
 
290
102
  ```tsx
291
103
  import { useRef } from 'react';
@@ -293,87 +105,71 @@ import { SocialMasonry, SocialMasonryRef } from 'social-masonry/react';
293
105
 
294
106
  function App() {
295
107
  const masonryRef = useRef<SocialMasonryRef>(null);
296
-
108
+
297
109
  const handleAddPost = () => {
298
110
  masonryRef.current?.addPosts([newPost]);
299
111
  };
300
-
112
+
301
113
  return (
302
114
  <SocialMasonry
303
115
  ref={masonryRef}
304
116
  posts={posts}
305
- // ...
306
117
  />
307
118
  );
308
119
  }
309
120
  ```
310
121
 
311
- ## 🎨 Customization
312
-
313
- ### CSS Variables
314
-
315
- Override the default theme using CSS variables:
316
-
317
- ```css
318
- :root {
319
- /* Colors */
320
- --sm-bg: #ffffff;
321
- --sm-bg-hover: #f7f9fa;
322
- --sm-text: #0f1419;
323
- --sm-text-secondary: #536471;
324
- --sm-text-muted: #8b98a5;
325
- --sm-border: #eff3f4;
326
- --sm-link: #1d9bf0;
327
-
328
- /* Brand Colors */
329
- --sm-twitter-primary: #1d9bf0;
330
- --sm-instagram-primary: #e1306c;
331
-
332
- /* Shadows */
333
- --sm-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.04);
334
- --sm-shadow-md: 0 4px 12px rgba(0, 0, 0, 0.08);
335
- --sm-shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.12);
336
-
337
- /* Transitions */
338
- --sm-transition-fast: 150ms ease;
339
- --sm-transition-base: 200ms ease;
340
- }
341
- ```
122
+ Available ref methods:
342
123
 
343
- ### Custom Classes
124
+ | Method | Description |
125
+ |--------|-------------|
126
+ | `addPosts(posts)` | Add posts to the end |
127
+ | `setPosts(posts)` | Replace all posts |
128
+ | `removePost(id)` | Remove a post by ID |
129
+ | `refresh()` | Re-process embeds |
344
130
 
345
- Add custom classes to cards:
131
+ ## Supported URL Formats
346
132
 
347
- ```typescript
348
- createSocialMasonry({
349
- className: 'my-custom-card',
350
- // ...
351
- });
352
- ```
133
+ ### Twitter/X
134
+
135
+ - `https://twitter.com/username/status/1234567890`
136
+ - `https://x.com/username/status/1234567890`
353
137
 
354
- ## 📊 Performance
138
+ ### Instagram
355
139
 
356
- Social Masonry is optimized for performance:
140
+ - `https://www.instagram.com/p/ABC123xyz/`
141
+ - `https://www.instagram.com/reel/ABC123xyz/`
357
142
 
358
- - **Virtual Scrolling**: Only renders visible cards, enabling smooth scrolling with 10,000+ posts
359
- - **Efficient Layout**: Uses a columnar algorithm with O(n) complexity
360
- - **Smart Updates**: Batched DOM updates and position caching
361
- - **Lazy Loading**: Images load only when cards enter the viewport
143
+ ## Utilities
362
144
 
363
- Enable virtualization for large datasets:
145
+ The library exports utility functions for URL parsing:
364
146
 
365
147
  ```typescript
366
- createSocialMasonry({
367
- virtualization: {
368
- enabled: true,
369
- overscan: 3, // Extra items to render above/below viewport
370
- estimatedItemHeight: 400,
371
- },
372
- // ...
373
- });
148
+ import {
149
+ extractTweetId,
150
+ extractInstagramId,
151
+ detectPlatform,
152
+ } from 'social-masonry';
153
+
154
+ // Extract tweet ID from URL
155
+ extractTweetId('https://x.com/user/status/123456'); // '123456'
156
+
157
+ // Extract Instagram post ID
158
+ extractInstagramId('https://instagram.com/p/ABC123'); // 'ABC123'
159
+
160
+ // Detect platform from URL
161
+ detectPlatform('https://x.com/user/status/123'); // 'twitter'
162
+ detectPlatform('https://instagram.com/p/ABC'); // 'instagram'
374
163
  ```
375
164
 
376
- ## 🌐 Browser Support
165
+ ## How It Works
166
+
167
+ 1. **Script Loading**: Twitter's `widgets.js` and Instagram's `embed.js` are loaded on-demand when needed
168
+ 2. **Widget Creation**: Official APIs (`twttr.widgets.createTweet` and `instgrm.Embeds.process`) create native embeds
169
+ 3. **Masonry Layout**: Posts are distributed across columns using a simple round-robin algorithm
170
+ 4. **Auto-sizing**: Each embed automatically sizes to its content - no fixed heights
171
+
172
+ ## Browser Support
377
173
 
378
174
  | Browser | Version |
379
175
  |---------|---------|
@@ -382,13 +178,6 @@ createSocialMasonry({
382
178
  | Safari | 14+ |
383
179
  | Edge | 80+ |
384
180
 
385
- ## 📄 License
181
+ ## License
386
182
 
387
- MIT © [tkana_dev](https://github.com/tkana-dev)
388
-
389
- ---
390
-
391
- <div align="center">
392
- <p>If you find this project useful, please consider giving it a ⭐️</p>
393
- <a href="https://github.com/tkana-dev/social-masonry">GitHub</a> · <a href="https://www.npmjs.com/package/social-masonry">npm</a> · <a href="https://social-masonry.dev">Demo</a>
394
- </div>
183
+ MIT