yjernite HF Staff commited on
Commit
aeeb334
·
verified ·
1 Parent(s): 74761ca

Upload 3 files

Browse files
js/cards/ArtifactSummaryCard.js ADDED
@@ -0,0 +1,225 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // ArtifactSummaryCard.js - Summary card component for artifacts
2
+ import { areasData } from '../data/areas.js';
3
+ import { featuredArtifacts } from '../data/artifacts.js';
4
+
5
+ export function createArtifactSummaryCard(artifact, index) {
6
+ const { title, date, type, description, areaTags, subAreaTags, sourceUrl } = artifact;
7
+
8
+ // Type-based styling (simplified - just icon and text color)
9
+ const typeStyles = {
10
+ 'blog': {
11
+ icon: '📝',
12
+ textColor: 'text-blue-700'
13
+ },
14
+ 'paper': {
15
+ icon: '📄',
16
+ textColor: 'text-green-700'
17
+ },
18
+ 'dataset': {
19
+ icon: '📊',
20
+ textColor: 'text-purple-700'
21
+ },
22
+ 'space': {
23
+ icon: '🚀',
24
+ textColor: 'text-orange-700'
25
+ },
26
+ 'external': {
27
+ icon: '🔗',
28
+ textColor: 'text-gray-700'
29
+ }
30
+ };
31
+
32
+ const style = typeStyles[type] || typeStyles['external'];
33
+
34
+ // Use imported areas data
35
+ const areaData = areasData;
36
+
37
+ const primaryArea = areaTags[0];
38
+ const primaryAreaData = areaData[primaryArea];
39
+ const backgroundImage = primaryAreaData?.image || '';
40
+ const imageCredit = primaryAreaData?.imageAttribution || '';
41
+
42
+ const areaTagsHtml = areaTags.map(tag => {
43
+ const area = areaData[tag];
44
+ return area ? `<span class="inline-block px-2 py-1 text-xs rounded-full ${area.color}">${area.name}</span>` : '';
45
+ }).join('');
46
+
47
+ const subAreaTagsHtml = subAreaTags.map(subAreaKey => {
48
+ // Find the sub-area name and color by looking through all areas
49
+ let subAreaName = subAreaKey; // fallback to key if not found
50
+ let textColor = 'text-gray-700'; // fallback color
51
+
52
+ for (const areaKey in areaData) {
53
+ const area = areaData[areaKey];
54
+ if (area.subAreas && area.subAreas[subAreaKey]) {
55
+ const subArea = area.subAreas[subAreaKey];
56
+ subAreaName = typeof subArea === 'string' ? subArea : subArea.name;
57
+ // Extract just the text color from the sub-area color
58
+ if (typeof subArea === 'object' && subArea.color) {
59
+ const colorMatch = subArea.color.match(/text-(\w+)-(\d+)/);
60
+ if (colorMatch) {
61
+ textColor = `text-${colorMatch[1]}-700`; // Use consistent 700 shade
62
+ }
63
+ }
64
+ break;
65
+ }
66
+ }
67
+ return `<span class="inline-block px-2 py-0.5 text-xs bg-gray-200 ${textColor} rounded">${subAreaName}</span>`;
68
+ }).join('');
69
+
70
+ const cardId = `artifact-card-${index}`;
71
+
72
+ return `
73
+ <div class="flex-none w-80 h-60 border border-gray-200 rounded-lg overflow-hidden bg-white relative group hover:shadow-lg transition-shadow duration-200">
74
+ <!-- Background image -->
75
+ ${backgroundImage ? `
76
+ <div class="absolute inset-0 opacity-10">
77
+ <img src="images/${backgroundImage}" alt="" class="w-full h-full object-cover">
78
+ </div>
79
+ ` : ''}
80
+
81
+ <!-- Toggle indicator -->
82
+ <div class="absolute top-2 right-2 z-10">
83
+ <button class="toggle-btn w-6 h-6 bg-white bg-opacity-80 hover:bg-opacity-100 rounded-full flex items-center justify-center text-gray-600 hover:text-gray-800 transition-all shadow-sm" onclick="toggleCardView('${cardId}')">
84
+ <svg class="w-3 h-3 expand-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
85
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
86
+ </svg>
87
+ <svg class="w-3 h-3 collapse-icon hidden" fill="none" stroke="currentColor" viewBox="0 0 24 24">
88
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 15l7-7 7 7"></path>
89
+ </svg>
90
+ </button>
91
+ </div>
92
+
93
+ <!-- Content -->
94
+ <div class="relative p-4 h-full flex flex-col overflow-hidden" id="${cardId}">
95
+ <!-- Default view -->
96
+ <div class="default-view">
97
+ <!-- Header: Type and Date -->
98
+ <div class="flex justify-between items-start mb-3 pr-8">
99
+ <div class="flex items-center space-x-2">
100
+ <span class="text-lg">${style.icon}</span>
101
+ <span class="text-xs font-medium font-bold uppercase tracking-wide">${type}</span>
102
+ </div>
103
+ <span class="text-xs text-gray-600">${date}</span>
104
+ </div>
105
+
106
+ <!-- Title -->
107
+ <div class="mb-4 flex-grow min-h-0">
108
+ <h3 class="font-semibold text-gray-900 text-sm leading-tight line-clamp-3">${title}</h3>
109
+ </div>
110
+
111
+ <!-- Bottom section with tags and image -->
112
+ <div class="flex justify-between items-end">
113
+ <!-- Left: Tags -->
114
+ <div class="flex-1 mr-4">
115
+ <!-- Area Tags -->
116
+ <div class="flex flex-wrap gap-1 mb-2">
117
+ ${areaTagsHtml}
118
+ </div>
119
+
120
+ <!-- Sub-area Tags -->
121
+ ${subAreaTags.length > 0 ? `
122
+ <div class="flex flex-wrap gap-1">
123
+ ${subAreaTagsHtml}
124
+ </div>
125
+ ` : ''}
126
+ </div>
127
+
128
+ <!-- Right: Area image with credit on hover -->
129
+ ${backgroundImage ? `
130
+ <div class="relative group/image">
131
+ <div class="w-12 h-12 rounded-lg overflow-hidden bg-gray-100 flex items-center justify-center cursor-help" title="${imageCredit}">
132
+ <img src="images/${backgroundImage}" alt="${primaryAreaData.name}" class="w-full h-full object-cover opacity-80">
133
+ </div>
134
+ </div>
135
+ ` : ''}
136
+ </div>
137
+ </div>
138
+
139
+ <!-- Description view (hidden by default) -->
140
+ <div class="description-view hidden h-full flex flex-col min-h-0">
141
+ <!-- Title (single line with overflow) -->
142
+ <div class="mb-3 flex-shrink-0">
143
+ <h3 class="font-semibold text-gray-900 text-sm leading-tight truncate" title="${title}">${title}</h3>
144
+ </div>
145
+
146
+ <!-- Description (scrollable, takes remaining space) -->
147
+ <div class="flex-grow overflow-y-auto min-h-0">
148
+ <p class="text-xs text-gray-700 leading-relaxed">${description}</p>
149
+ </div>
150
+ </div>
151
+
152
+ <!-- URL link (always visible) -->
153
+ ${sourceUrl ? `
154
+ <div class="absolute bottom-2 right-2">
155
+ <a href="${sourceUrl}" target="_blank" rel="noopener noreferrer" class="text-gray-400 hover:text-blue-600 transition-colors">
156
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
157
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"></path>
158
+ </svg>
159
+ </a>
160
+ </div>
161
+ ` : ''}
162
+ </div>
163
+ </div>
164
+ `;
165
+ }
166
+
167
+ export function createArtifactCarousel(artifacts, containerId) {
168
+ const container = document.getElementById(containerId);
169
+ if (!container) return;
170
+
171
+ const cardsHtml = artifacts.map((artifact, index) => createArtifactSummaryCard(artifact, index)).join('');
172
+
173
+ container.innerHTML = `
174
+ <div class="relative">
175
+ <!-- Carousel container -->
176
+ <div class="flex overflow-x-auto scrollbar-hide space-x-4 pb-4" id="${containerId}-scroll">
177
+ ${cardsHtml}
178
+ </div>
179
+
180
+ <!-- Navigation arrows -->
181
+ <button class="absolute left-0 top-1/2 transform -translate-y-1/2 bg-white shadow-lg rounded-full p-2 hover:bg-gray-50 transition-colors z-10" onclick="scrollCarousel('${containerId}-scroll', -320)">
182
+ <svg class="w-4 h-4 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
183
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path>
184
+ </svg>
185
+ </button>
186
+ <button class="absolute right-0 top-1/2 transform -translate-y-1/2 bg-white shadow-lg rounded-full p-2 hover:bg-gray-50 transition-colors z-10" onclick="scrollCarousel('${containerId}-scroll', 320)">
187
+ <svg class="w-4 h-4 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
188
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
189
+ </svg>
190
+ </button>
191
+ </div>
192
+ `;
193
+ }
194
+
195
+
196
+ // Utility function for carousel navigation
197
+ window.scrollCarousel = function(containerId, scrollAmount) {
198
+ const container = document.getElementById(containerId);
199
+ if (container) {
200
+ container.scrollBy({ left: scrollAmount, behavior: 'smooth' });
201
+ }
202
+ };
203
+
204
+ // Add toggle function
205
+ window.toggleCardView = function(cardId) {
206
+ const card = document.getElementById(cardId);
207
+ const defaultView = card.querySelector('.default-view');
208
+ const descriptionView = card.querySelector('.description-view');
209
+ const expandIcon = card.parentElement.querySelector('.expand-icon');
210
+ const collapseIcon = card.parentElement.querySelector('.collapse-icon');
211
+
212
+ if (defaultView.classList.contains('hidden')) {
213
+ // Show default view
214
+ defaultView.classList.remove('hidden');
215
+ descriptionView.classList.add('hidden');
216
+ expandIcon.classList.remove('hidden');
217
+ collapseIcon.classList.add('hidden');
218
+ } else {
219
+ // Show description view
220
+ defaultView.classList.add('hidden');
221
+ descriptionView.classList.remove('hidden');
222
+ expandIcon.classList.add('hidden');
223
+ collapseIcon.classList.remove('hidden');
224
+ }
225
+ };
js/cards/HomeAreaCard.js ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // HomeAreaCard.js - Area card component for homepage
2
+ import { areasData } from '../data/areas.js';
3
+
4
+ export function createHomeAreaCard(id, title, description, openness, subAreas, imagePosition = 'left', imageAttribution = null, altText = null) {
5
+ const flexDirection = imagePosition === 'left' ? 'lg:flex-row' : 'lg:flex-row-reverse';
6
+
7
+ const subAreasList = subAreas.map(area => `<li>${area}</li>`).join('');
8
+
9
+ // Use provided alt text or fallback to title
10
+ const imgAlt = altText || title;
11
+
12
+ // Create attribution text if provided
13
+ const attribution = imageAttribution ?
14
+ `<p class="text-xs text-gray-500 mt-2 text-center">${imageAttribution}</p>` : '';
15
+
16
+ return `
17
+ <div id="${id}" class="bg-white rounded-lg shadow-sm overflow-hidden" style="min-height: 300px;">
18
+ <div class="flex flex-col ${flexDirection}">
19
+ <div class="lg:w-2/3 p-8">
20
+ <h2 class="text-2xl font-bold text-gray-900 mb-4">${title}</h2>
21
+ <p class="text-gray-700 mb-6">${description}</p>
22
+
23
+ ${openness ? `
24
+ <div class="mb-6 px-6 pt-4 pb-6 bg-gradient-to-r from-orange-50 to-yellow-50 border-l-4 border-orange-300 rounded-r-lg">
25
+ <p class="font-bold text-orange-900 mb-3">The Role of Openness</p>
26
+ <p class="text-orange-800 italic leading-relaxed">${openness}</p>
27
+ </div>
28
+ ` : ''}
29
+
30
+ <div class="mb-6">
31
+ <h3 class="text-lg font-semibold text-gray-900 mb-3">Sub-areas</h3>
32
+ <ul class="list-disc list-inside text-gray-700 space-y-1">
33
+ ${subAreasList}
34
+ </ul>
35
+ </div>
36
+ </div>
37
+ <div class="lg:w-1/3 bg-gray-100">
38
+ <div class="h-full flex flex-col items-center justify-center p-8">
39
+ <img src="images/${id}.png" alt="${imgAlt}" class="max-w-full max-h-full object-contain">
40
+ ${attribution}
41
+ </div>
42
+ </div>
43
+ </div>
44
+ </div>
45
+ `;
46
+ }
js/cards/ResourceCard.js ADDED
@@ -0,0 +1,130 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // cards/ResourceCard.js - Resource card component for SPA
2
+ import { areasData } from '../data/areas.js';
3
+
4
+ export function createResourceCard(resource) {
5
+ // Get the primary area for this resource
6
+ const primaryArea = areasData[resource.areaTags[0]];
7
+ const primaryColor = primaryArea?.primaryColor || 'gray';
8
+
9
+ // Format date
10
+ const formattedDate = new Date(resource.date).toLocaleDateString('en-US', {
11
+ year: 'numeric',
12
+ month: 'short',
13
+ day: 'numeric'
14
+ });
15
+
16
+ // Generate area tags
17
+ const areaTagsHtml = resource.areaTags.map(areaId => {
18
+ const area = areasData[areaId];
19
+ if (!area) return '';
20
+ return `<span class="px-2 py-1 rounded-full text-xs font-medium ${area.colors.medium}">${area.name}</span>`;
21
+ }).join('');
22
+
23
+ // Generate sub-area tags
24
+ const subAreaTagsHtml = resource.subAreaTags.map(subAreaKey => {
25
+ // Find the sub-area name and color by looking through all areas
26
+ let subAreaName = subAreaKey; // fallback to key if not found
27
+ let textColor = 'text-gray-700'; // fallback color
28
+
29
+ for (const areaKey in areasData) {
30
+ const area = areasData[areaKey];
31
+ if (area.subAreas && area.subAreas[subAreaKey]) {
32
+ const subArea = area.subAreas[subAreaKey];
33
+ subAreaName = typeof subArea === 'string' ? subArea : subArea.name;
34
+ // Extract just the text color from the sub-area color
35
+ if (typeof subArea === 'object' && subArea.color) {
36
+ const colorMatch = subArea.color.match(/text-(\w+)-(\d+)/);
37
+ if (colorMatch) {
38
+ textColor = `text-${colorMatch[1]}-700`; // Use consistent 700 shade
39
+ }
40
+ }
41
+ break;
42
+ }
43
+ }
44
+ return `<span class="inline-block px-2 py-0.5 text-xs bg-gray-200 ${textColor} rounded">${subAreaName}</span>`;
45
+ }).join('');
46
+
47
+ // Type badge
48
+ const typeBadge = resource.type === 'dataset' ? 'Dataset' : 'Tool';
49
+ const typeColor = resource.type === 'dataset' ? 'bg-blue-100 text-blue-800' : 'bg-green-100 text-green-800';
50
+
51
+ return `
52
+ <div class="resource-card bg-white rounded-lg shadow-sm border border-gray-200 overflow-hidden flex-shrink-0 relative" style="height: 600px; width: 600px;">
53
+ <!-- Top Section - Single view with all information -->
54
+ <div class="resource-card-top h-48 bg-gradient-to-br from-${primaryColor}-50 to-${primaryColor}-100 relative overflow-hidden">
55
+ <!-- Background image for top section -->
56
+ <div class="absolute inset-0 opacity-10">
57
+ <img src="images/${primaryArea?.image || 'ai.png'}" alt="" class="w-full h-full object-cover">
58
+ </div>
59
+
60
+ <div class="relative z-10 p-4 h-full flex flex-col">
61
+ <!-- Header with type, link, and date -->
62
+ <div class="flex items-center justify-between mb-3">
63
+ <div class="flex items-center gap-2">
64
+ <span class="px-2 py-1 rounded-full text-xs font-medium ${typeColor}">${typeBadge}</span>
65
+ <a href="${resource.url}" target="_blank" class="text-xs text-blue-600 hover:text-blue-800 flex items-center">
66
+ <svg class="w-3 h-3 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
67
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"></path>
68
+ </svg>
69
+ View Resource
70
+ </a>
71
+ </div>
72
+ <span class="text-xs text-gray-600">${formattedDate}</span>
73
+ </div>
74
+
75
+ <!-- Title -->
76
+ <h3 class="text-lg font-semibold text-gray-900 mb-3 leading-tight">${resource.title}</h3>
77
+
78
+ <!-- Description -->
79
+ <p class="text-sm text-gray-700 mb-3 flex-1 overflow-hidden" style="display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical;">
80
+ ${resource.description}
81
+ </p>
82
+
83
+ <!-- Footer with tags and area image -->
84
+ <div class="flex items-center justify-between">
85
+ <div class="flex flex-wrap gap-1 flex-1">
86
+ ${areaTagsHtml}
87
+ ${subAreaTagsHtml}
88
+ </div>
89
+ <div class="w-8 h-8 rounded-full bg-white bg-opacity-50 flex items-center justify-center ml-3 flex-shrink-0">
90
+ <img src="images/${primaryArea?.image || 'ai.png'}" alt="" class="w-6 h-6 rounded-full object-cover">
91
+ </div>
92
+ </div>
93
+ </div>
94
+ </div>
95
+
96
+ <!-- Bottom Section - 70% of height -->
97
+ <div class="resource-card-bottom relative" style="height: 420px;">
98
+ ${resource.embedUrl ? `
99
+ <!-- Iframe embed -->
100
+ <iframe
101
+ src="${resource.embedUrl}"
102
+ class="w-full h-full border-0"
103
+ loading="lazy"
104
+ title="${resource.title}"
105
+ ></iframe>
106
+ ` : `
107
+ <!-- Fallback with background image and text -->
108
+ <div class="w-full h-full relative overflow-hidden">
109
+ <div class="absolute inset-0 opacity-50">
110
+ <img src="images/${primaryArea?.image || 'ai.png'}" alt="" class="w-full h-full object-cover">
111
+ </div>
112
+ <div class="absolute inset-0 bg-black bg-opacity-20 flex items-center justify-center">
113
+ <div class="text-center text-white">
114
+ <div class="text-lg font-semibold mb-2">Iframe not available</div>
115
+ <a href="${resource.url}" target="_blank" class="text-sm text-blue-200 hover:text-blue-100 underline">
116
+ View on external site
117
+ </a>
118
+ </div>
119
+ </div>
120
+ </div>
121
+ `}
122
+ </div>
123
+ </div>
124
+ `;
125
+ }
126
+
127
+ export function initializeResourceCards() {
128
+ // Resource cards now have a single view, no toggle functionality needed
129
+ // This function is kept for consistency with the calling code
130
+ }