@marvalt/shcoder 0.1.1 → 0.1.3

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/dist/index.esm.js CHANGED
@@ -35,6 +35,42 @@ function getApiUrl$1() {
35
35
  }
36
36
  // Initialize API URL
37
37
  apiUrl$1 = getApiUrl$1();
38
+ let staticDataProvider = null;
39
+ function setStaticDataProvider(provider) {
40
+ staticDataProvider = provider;
41
+ }
42
+ // Helper function to extract featured image from member data
43
+ function extractFeaturedImage(member) {
44
+ if (member.featured_image_urls) {
45
+ return member.featured_image_urls;
46
+ }
47
+ if (member._embedded?.['wp:featuredmedia']?.[0]) {
48
+ const featuredMedia = member._embedded['wp:featuredmedia'][0];
49
+ return {
50
+ thumbnail: featuredMedia.media_details?.sizes?.thumbnail ? {
51
+ url: featuredMedia.media_details.sizes.thumbnail.source_url,
52
+ width: featuredMedia.media_details.sizes.thumbnail.width,
53
+ height: featuredMedia.media_details.sizes.thumbnail.height,
54
+ } : undefined,
55
+ medium: featuredMedia.media_details?.sizes?.medium ? {
56
+ url: featuredMedia.media_details.sizes.medium.source_url,
57
+ width: featuredMedia.media_details.sizes.medium.width,
58
+ height: featuredMedia.media_details.sizes.medium.height,
59
+ } : undefined,
60
+ large: featuredMedia.media_details?.sizes?.large ? {
61
+ url: featuredMedia.media_details.sizes.large.source_url,
62
+ width: featuredMedia.media_details.sizes.large.width,
63
+ height: featuredMedia.media_details.sizes.large.height,
64
+ } : undefined,
65
+ full: {
66
+ url: featuredMedia.source_url || featuredMedia.media_details?.sizes?.full?.source_url || '',
67
+ width: featuredMedia.media_details?.width || 0,
68
+ height: featuredMedia.media_details?.height || 0,
69
+ },
70
+ };
71
+ }
72
+ return undefined;
73
+ }
38
74
  /**
39
75
  * Hook to fetch members based on shortcode attributes
40
76
  */
@@ -43,15 +79,39 @@ function useMembers(attrs) {
43
79
  const [loading, setLoading] = useState(true);
44
80
  const [error, setError] = useState(null);
45
81
  useEffect(() => {
46
- if (!apiUrl$1) {
47
- setError(new Error('WordPress API URL not configured'));
48
- setLoading(false);
49
- return;
50
- }
51
82
  async function fetchMembers() {
52
83
  try {
53
84
  setLoading(true);
54
85
  setError(null);
86
+ // Use static data if available (preferred method)
87
+ if (staticDataProvider) {
88
+ const provider = staticDataProvider();
89
+ const filtered = provider.filterMembers({
90
+ category: attrs.tax || attrs.category,
91
+ role: attrs.role,
92
+ limit: attrs.limit ? parseInt(attrs.limit, 10) : undefined,
93
+ orderby: attrs.orderby || 'date',
94
+ order: attrs.order || 'desc',
95
+ });
96
+ // Process members to match Member interface
97
+ const processedMembers = filtered.map((member) => {
98
+ const processed = {
99
+ ...member,
100
+ member_meta: member.member_meta || {},
101
+ featured_image_urls: extractFeaturedImage(member),
102
+ };
103
+ return processed;
104
+ });
105
+ setMembers(processedMembers);
106
+ setLoading(false);
107
+ return;
108
+ }
109
+ // Fallback to API (should not happen in production with static data)
110
+ if (!apiUrl$1) {
111
+ setError(new Error('WordPress API URL not configured and no static data provider available'));
112
+ setLoading(false);
113
+ return;
114
+ }
55
115
  // Build query params from shortcode attributes
56
116
  const params = {
57
117
  per_page: (attrs.limit ? parseInt(attrs.limit, 10) : 100).toString(),
@@ -84,33 +144,8 @@ function useMembers(attrs) {
84
144
  const processed = {
85
145
  ...member,
86
146
  member_meta: member.member_meta || {},
147
+ featured_image_urls: extractFeaturedImage(member),
87
148
  };
88
- // Extract featured image from _embedded
89
- if (member._embedded && member._embedded['wp:featuredmedia'] && member._embedded['wp:featuredmedia'][0]) {
90
- const featuredMedia = member._embedded['wp:featuredmedia'][0];
91
- processed.featured_image_urls = {
92
- thumbnail: featuredMedia.media_details?.sizes?.thumbnail ? {
93
- url: featuredMedia.media_details.sizes.thumbnail.source_url,
94
- width: featuredMedia.media_details.sizes.thumbnail.width,
95
- height: featuredMedia.media_details.sizes.thumbnail.height,
96
- } : undefined,
97
- medium: featuredMedia.media_details?.sizes?.medium ? {
98
- url: featuredMedia.media_details.sizes.medium.source_url,
99
- width: featuredMedia.media_details.sizes.medium.width,
100
- height: featuredMedia.media_details.sizes.medium.height,
101
- } : undefined,
102
- large: featuredMedia.media_details?.sizes?.large ? {
103
- url: featuredMedia.media_details.sizes.large.source_url,
104
- width: featuredMedia.media_details.sizes.large.width,
105
- height: featuredMedia.media_details.sizes.large.height,
106
- } : undefined,
107
- full: {
108
- url: featuredMedia.source_url || featuredMedia.media_details?.sizes?.full?.source_url || '',
109
- width: featuredMedia.media_details?.width || 0,
110
- height: featuredMedia.media_details?.height || 0,
111
- },
112
- };
113
- }
114
149
  return processed;
115
150
  });
116
151
  setMembers(processedMembers);
@@ -135,7 +170,7 @@ function useMember(id) {
135
170
  const [loading, setLoading] = useState(true);
136
171
  const [error, setError] = useState(null);
137
172
  useEffect(() => {
138
- if (!apiUrl$1 || !id) {
173
+ if (!id) {
139
174
  setLoading(false);
140
175
  return;
141
176
  }
@@ -143,6 +178,31 @@ function useMember(id) {
143
178
  try {
144
179
  setLoading(true);
145
180
  setError(null);
181
+ // Use static data if available (preferred method)
182
+ if (staticDataProvider) {
183
+ const provider = staticDataProvider();
184
+ const found = provider.getMemberById(id);
185
+ if (found) {
186
+ // Process member to match Member interface
187
+ const processed = {
188
+ ...found,
189
+ member_meta: found.member_meta || {},
190
+ featured_image_urls: extractFeaturedImage(found),
191
+ };
192
+ setMember(processed);
193
+ }
194
+ else {
195
+ setMember(null);
196
+ }
197
+ setLoading(false);
198
+ return;
199
+ }
200
+ // Fallback to API (should not happen in production with static data)
201
+ if (!apiUrl$1) {
202
+ setError(new Error('WordPress API URL not configured and no static data provider available'));
203
+ setLoading(false);
204
+ return;
205
+ }
146
206
  const response = await fetch(`${apiUrl$1}/wp/v2/member/${id}?_embed=true`);
147
207
  if (!response.ok) {
148
208
  throw new Error(`Failed to fetch member: ${response.statusText}`);
@@ -152,33 +212,8 @@ function useMember(id) {
152
212
  const processed = {
153
213
  ...data,
154
214
  member_meta: data.member_meta || {},
215
+ featured_image_urls: extractFeaturedImage(data),
155
216
  };
156
- // Extract featured image from _embedded
157
- if (data._embedded && data._embedded['wp:featuredmedia'] && data._embedded['wp:featuredmedia'][0]) {
158
- const featuredMedia = data._embedded['wp:featuredmedia'][0];
159
- processed.featured_image_urls = {
160
- thumbnail: featuredMedia.media_details?.sizes?.thumbnail ? {
161
- url: featuredMedia.media_details.sizes.thumbnail.source_url,
162
- width: featuredMedia.media_details.sizes.thumbnail.width,
163
- height: featuredMedia.media_details.sizes.thumbnail.height,
164
- } : undefined,
165
- medium: featuredMedia.media_details?.sizes?.medium ? {
166
- url: featuredMedia.media_details.sizes.medium.source_url,
167
- width: featuredMedia.media_details.sizes.medium.width,
168
- height: featuredMedia.media_details.sizes.medium.height,
169
- } : undefined,
170
- large: featuredMedia.media_details?.sizes?.large ? {
171
- url: featuredMedia.media_details.sizes.large.source_url,
172
- width: featuredMedia.media_details.sizes.large.width,
173
- height: featuredMedia.media_details.sizes.large.height,
174
- } : undefined,
175
- full: {
176
- url: featuredMedia.source_url || featuredMedia.media_details?.sizes?.full?.source_url || '',
177
- width: featuredMedia.media_details?.width || 0,
178
- height: featuredMedia.media_details?.height || 0,
179
- },
180
- };
181
- }
182
217
  setMember(processed);
183
218
  }
184
219
  catch (err) {
@@ -1705,6 +1740,37 @@ const MemberCard = ({ id, className = '' }) => {
1705
1740
  return (jsxRuntimeExports.jsxs("div", { className: `member-card ${className}`, children: [imageUrl && (jsxRuntimeExports.jsx("div", { className: "member-card-image", children: jsxRuntimeExports.jsx("img", { src: imageUrl, alt: fullName, className: "w-full h-auto rounded-lg" }) })), jsxRuntimeExports.jsxs("div", { className: "member-card-content", children: [jsxRuntimeExports.jsx("h3", { className: "member-card-name", children: fullName }), meta.member_role && jsxRuntimeExports.jsx("p", { className: "member-card-role", children: meta.member_role }), meta.business_name && jsxRuntimeExports.jsx("p", { className: "member-card-company", children: meta.business_name }), meta.tagline && jsxRuntimeExports.jsx("p", { className: "member-card-tagline", children: meta.tagline }), meta.member_bio && (jsxRuntimeExports.jsx("div", { className: "member-card-bio", dangerouslySetInnerHTML: { __html: meta.member_bio } })), meta.website && (jsxRuntimeExports.jsx("a", { href: meta.website, target: "_blank", rel: "noopener noreferrer", className: "member-card-website", children: "Visit Website" }))] })] }));
1706
1741
  };
1707
1742
 
1743
+ /**
1744
+ * Cloudflare Images variant URL helper for member card images.
1745
+ * Only Cloudflare URLs (imagedelivery.net) are supported; WordPress URLs are not used.
1746
+ */
1747
+ function isCloudflareImageUrl(url) {
1748
+ if (!url)
1749
+ return false;
1750
+ try {
1751
+ const u = new URL(url);
1752
+ return u.hostname.toLowerCase().includes('imagedelivery.net');
1753
+ }
1754
+ catch {
1755
+ return false;
1756
+ }
1757
+ }
1758
+ /**
1759
+ * Append Cloudflare Images variant (e.g. w=720,h=1280 for 9:16) to the base URL.
1760
+ * Returns the original url if it is not a Cloudflare URL.
1761
+ */
1762
+ function getCloudflareVariantUrl(url, options) {
1763
+ if (!isCloudflareImageUrl(url))
1764
+ return url;
1765
+ if (/\/w=\d+/.test(url) || /\/(public|thumbnail|banner|avatar)/.test(url)) {
1766
+ return url;
1767
+ }
1768
+ const { width, height } = options;
1769
+ const base = url.endsWith('/') ? url.slice(0, -1) : url;
1770
+ const variant = `w=${Math.max(1, Math.floor(width))}` + (`,h=${Math.max(1, Math.floor(height))}` );
1771
+ return `${base}/${variant}`;
1772
+ }
1773
+
1708
1774
  /**
1709
1775
  * MemberCardsGrid component - displays multiple members in a grid
1710
1776
  * Usage: [member_cards tax="leadership" cols="3" image="true"]
@@ -1737,12 +1803,16 @@ const MemberCardsGrid = ({ tax, category, role, cols = '3', image = 'true', limi
1737
1803
  }[colsNum] || 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3';
1738
1804
  return (jsxRuntimeExports.jsx("div", { className: `member-cards-grid grid ${gridCols} gap-6 ${className}`, children: members.map((member) => {
1739
1805
  const meta = member.member_meta || {};
1740
- const imageUrl = showImage
1806
+ const rawImageUrl = showImage
1741
1807
  ? (member.featured_image_urls?.medium?.url || member.featured_image_urls?.full?.url)
1742
1808
  : null;
1809
+ // Only use Cloudflare URLs; request 9:16 at 720x1280 for sharp display
1810
+ const imageUrl = rawImageUrl && isCloudflareImageUrl(rawImageUrl)
1811
+ ? getCloudflareVariantUrl(rawImageUrl, { width: 720, height: 1280 })
1812
+ : null;
1743
1813
  const fullName = [meta.members_firstname, meta.members_last_name].filter(Boolean).join(' ')
1744
1814
  || member.title.rendered;
1745
- return (jsxRuntimeExports.jsxs("div", { className: "member-card bg-white rounded-lg shadow-md overflow-hidden", children: [imageUrl && (jsxRuntimeExports.jsx("div", { className: "member-card-image", children: jsxRuntimeExports.jsx("img", { src: imageUrl, alt: fullName, className: "w-full h-48 object-cover" }) })), jsxRuntimeExports.jsxs("div", { className: "member-card-content p-6", children: [jsxRuntimeExports.jsx("h3", { className: "member-card-name text-xl font-bold mb-2", children: fullName }), meta.member_role && (jsxRuntimeExports.jsx("p", { className: "member-card-role text-gray-600 mb-2", children: meta.member_role })), meta.business_name && (jsxRuntimeExports.jsx("p", { className: "member-card-company text-sm text-gray-500 mb-2", children: meta.business_name })), meta.tagline && (jsxRuntimeExports.jsx("p", { className: "member-card-tagline text-sm italic mb-3", children: meta.tagline })), meta.member_pitch && (jsxRuntimeExports.jsx("p", { className: "member-card-pitch text-sm mb-3 line-clamp-3", children: meta.member_pitch })), meta.website && (jsxRuntimeExports.jsx("a", { href: meta.website, target: "_blank", rel: "noopener noreferrer", className: "member-card-website text-blue-600 hover:underline text-sm", children: "Visit Website \u2192" }))] })] }, member.id));
1815
+ return (jsxRuntimeExports.jsxs("div", { className: "member-card bg-white rounded-lg shadow-md overflow-hidden", children: [imageUrl && (jsxRuntimeExports.jsx("div", { className: "member-card-image aspect-[9/16] w-full overflow-hidden bg-slate-100", children: jsxRuntimeExports.jsx("img", { src: imageUrl, alt: fullName, className: "h-full w-full object-cover" }) })), jsxRuntimeExports.jsxs("div", { className: "member-card-content p-6", children: [jsxRuntimeExports.jsx("h3", { className: "member-card-name text-xl font-bold mb-2", children: fullName }), meta.member_role && (jsxRuntimeExports.jsx("p", { className: "member-card-role text-gray-600 mb-2", children: meta.member_role })), meta.business_name && (jsxRuntimeExports.jsx("p", { className: "member-card-company text-sm text-gray-500 mb-2", children: meta.business_name })), meta.tagline && (jsxRuntimeExports.jsx("p", { className: "member-card-tagline text-sm italic mb-3", children: meta.tagline })), meta.member_pitch && (jsxRuntimeExports.jsx("p", { className: "member-card-pitch text-sm mb-3 line-clamp-3", children: meta.member_pitch })), meta.website && (jsxRuntimeExports.jsx("a", { href: meta.website, target: "_blank", rel: "noopener noreferrer", className: "member-card-website text-blue-600 hover:underline text-sm", children: "Visit Website \u2192" }))] })] }, member.id));
1746
1816
  }) }));
1747
1817
  };
1748
1818
 
@@ -1878,5 +1948,5 @@ const TestimonialSlideshow = ({ tax, category, forPostId, limit = '5', interval
1878
1948
  return (jsxRuntimeExports.jsxs("div", { className: `testimonial-slideshow relative ${className}`, children: [jsxRuntimeExports.jsxs("div", { className: "testimonial-slideshow-slide bg-white rounded-lg shadow-md p-8", children: [showContent === 'true' && (jsxRuntimeExports.jsx("div", { className: "testimonial-slideshow-content text-xl mb-6 italic", dangerouslySetInnerHTML: { __html: currentTestimonial.content.rendered } })), jsxRuntimeExports.jsxs("div", { className: "testimonial-slideshow-author", children: [showName === 'true' && (jsxRuntimeExports.jsx("p", { className: "testimonial-slideshow-name text-lg font-bold", children: meta.submitter_name || 'Anonymous' })), showRole === 'true' && meta.submitter_role && (jsxRuntimeExports.jsx("p", { className: "testimonial-slideshow-role text-sm text-gray-600", children: meta.submitter_role })), showBusinessName === 'true' && meta.submitter_business_name && (jsxRuntimeExports.jsx("p", { className: "testimonial-slideshow-business text-sm text-gray-500", children: meta.submitter_business_name })), showCompanyUrl === 'true' && meta.submitter_company && (jsxRuntimeExports.jsx("a", { href: meta.submitter_company, target: "_blank", rel: "noopener noreferrer", className: "testimonial-slideshow-company text-sm text-blue-600 hover:underline", children: meta.submitter_company }))] }), showKudos === 'true' && meta.kudos && meta.kudos.length > 0 && (jsxRuntimeExports.jsx("div", { className: "testimonial-slideshow-kudos mt-4 flex gap-2 justify-center", children: meta.kudos.map((kudo, index) => (jsxRuntimeExports.jsx("span", { className: "text-2xl", title: kudo, children: kudo }, index))) }))] }), testimonials.length > 1 && (jsxRuntimeExports.jsx("div", { className: "testimonial-slideshow-dots flex justify-center gap-2 mt-6", children: testimonials.map((_, index) => (jsxRuntimeExports.jsx("button", { onClick: () => setCurrentIndex(index), className: `w-3 h-3 rounded-full transition-colors ${index === currentIndex ? 'bg-blue-600' : 'bg-gray-300'}`, "aria-label": `Go to slide ${index + 1}` }, index))) }))] }));
1879
1949
  };
1880
1950
 
1881
- export { MemberCard, MemberCardsGrid, MemberSlideshow, TestimonialCard, TestimonialCardsGrid, TestimonialSlideshow, setWordPressApiUrl, useMember, useMembers, useTestimonial, useTestimonials };
1951
+ export { MemberCard, MemberCardsGrid, MemberSlideshow, TestimonialCard, TestimonialCardsGrid, TestimonialSlideshow, setStaticDataProvider, setWordPressApiUrl, useMember, useMembers, useTestimonial, useTestimonials };
1882
1952
  //# sourceMappingURL=index.esm.js.map