closes #1 by adding headshot feature
This commit is contained in:
		| @@ -16,8 +16,45 @@ body { | |||||||
|   font-family: $font-garamond; |   font-family: $font-garamond; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | .wompum-radial-grid { | ||||||
|  |   @apply absolute w-full h-full top-0 left-0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .narrator-image-container { | ||||||
|  |   @apply relative; | ||||||
|  |  | ||||||
|  |   .wompum-radial-grid { | ||||||
|  |     @apply absolute inset-0 w-full h-full; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .narrator-headshot { | ||||||
|  |   @apply w-full rounded-full object-cover relative z-10 border-2 border-white; | ||||||
|  |   aspect-ratio: 1; | ||||||
|  | } | ||||||
|  |  | ||||||
| .interviewer-question { | .interviewer-question { | ||||||
|   font-style: italic; |   font-style: italic; | ||||||
|   color: #444; |   color: #444; | ||||||
|   margin-left: -1rem; |   margin-left: -1rem; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Narrator Image Component | ||||||
|  | .narrator { | ||||||
|  |   &__container { | ||||||
|  |     @apply relative; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   &__frame { | ||||||
|  |     @apply relative p-4; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   &__image { | ||||||
|  |     @apply w-full rounded-full object-cover relative z-10 bg-white text-center border-4 border-white grid place-items-center; | ||||||
|  |     aspect-ratio: 1; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   &__wompum { | ||||||
|  |     @apply absolute inset-0 w-full h-full; | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -7,6 +7,7 @@ approved: 2025-03-20 | |||||||
| summary: "Drawing on many ancestral traditions and the experience of her own body, Asia Dorsey learns and teaches the pattern language of a healthy gut." | summary: "Drawing on many ancestral traditions and the experience of her own body, Asia Dorsey learns and teaches the pattern language of a healthy gut." | ||||||
| location: "Denver CO" | location: "Denver CO" | ||||||
| tags: [ancestors, food, health, indigeneity] | tags: [ancestors, food, health, indigeneity] | ||||||
|  | headshot: "/headshots/placeholder-headshot.png" | ||||||
| links: | links: | ||||||
|   - text: "Bugs Bones & Botany" |   - text: "Bugs Bones & Botany" | ||||||
|     url: "https://www.bonesbugsandbotany.com" |     url: "https://www.bonesbugsandbotany.com" | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ date: 2024-10-10 | |||||||
| approved: 2024-10-11 | approved: 2024-10-11 | ||||||
| summary: "After widespread resistance to codes of conduct in open-source software communities, Coraline Ada Ehmke's Contributor Covenant became the most popular code of conduct in the ecosystem." | summary: "After widespread resistance to codes of conduct in open-source software communities, Coraline Ada Ehmke's Contributor Covenant became the most popular code of conduct in the ecosystem." | ||||||
| location: "Chicago, USA" | location: "Chicago, USA" | ||||||
|  | headshot: "/headshots/placeholder-headshot.png" | ||||||
| tags: [code of conduct, dispute resolution, gender, open source, organizations, software] | tags: [code of conduct, dispute resolution, gender, open source, organizations, software] | ||||||
| --- | --- | ||||||
|  |  | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ date: 2024-10-29 | |||||||
| approved: 2024-12-16 | approved: 2024-12-16 | ||||||
| summary: "Good Market is a digital commons for enterprises that prioritize people and the planet over profit. It enables communities to set and enforce their own standards for doing business." | summary: "Good Market is a digital commons for enterprises that prioritize people and the planet over profit. It enables communities to set and enforce their own standards for doing business." | ||||||
| location: "Sri Lanka / United States" | location: "Sri Lanka / United States" | ||||||
|  | headshot: "/headshots/placeholder-headshot.png" | ||||||
| tags: [economics, organizations, ecology, standards] | tags: [economics, organizations, ecology, standards] | ||||||
| --- | --- | ||||||
|  |  | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ date: 2025-02-04 | |||||||
| approved: 2025-02-11 | approved: 2025-02-11 | ||||||
| summary: "Constructed languages, or conlangs, are the basis of a hobby, a science, and a community that now occupies a small corner of the entertainment industry." | summary: "Constructed languages, or conlangs, are the basis of a hobby, a science, and a community that now occupies a small corner of the entertainment industry." | ||||||
| location: "Wellington, NZ" | location: "Wellington, NZ" | ||||||
|  | headshot: "/headshots/placeholder-headshot.png" | ||||||
| tags: [fiction, gender, language, open source, software] | tags: [fiction, gender, language, open source, software] | ||||||
| --- | --- | ||||||
|  |  | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ date: 2024-12-13 | |||||||
| approved: 2025-03-04 | approved: 2025-03-04 | ||||||
| summary: "A diplomat for Bangladesh describes the role of protocol in high-profile international visits and treaty negotiations." | summary: "A diplomat for Bangladesh describes the role of protocol in high-profile international visits and treaty negotiations." | ||||||
| location: "Dhaka, Bangladesh" | location: "Dhaka, Bangladesh" | ||||||
|  | headshot: "/headshots/placeholder-headshot.png" | ||||||
| tags: [diplomacy, government, friendship] | tags: [diplomacy, government, friendship] | ||||||
| --- | --- | ||||||
|  |  | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ date: 2024-12-06 | |||||||
| approved: 2025-04-01 | approved: 2025-04-01 | ||||||
| summary: "A career-long journey to build online social networks that cannot be controlled by a single company, culminating with the ActivityPub standard." | summary: "A career-long journey to build online social networks that cannot be controlled by a single company, culminating with the ActivityPub standard." | ||||||
| location: "Montreal, Canada" | location: "Montreal, Canada" | ||||||
|  | headshot: "/headshots/placeholder-headshot.png" | ||||||
| links: | links: | ||||||
|   - text: Website |   - text: Website | ||||||
|     url: https://evanp.me/ |     url: https://evanp.me/ | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ date: 2024-11-04 | |||||||
| approved: 2024-12-02 | approved: 2024-12-02 | ||||||
| summary: "As a sport often played with no referees, ultimate frisbee has developed a strong set of norms for addressing conflict and self-governing." | summary: "As a sport often played with no referees, ultimate frisbee has developed a strong set of norms for addressing conflict and self-governing." | ||||||
| location: "East Greenbush, NY USA" | location: "East Greenbush, NY USA" | ||||||
|  | headshot: "/headshots/placeholder-headshot.png" | ||||||
| tags: [frisbee, sports, organizations, dispute resolution] | tags: [frisbee, sports, organizations, dispute resolution] | ||||||
| --- | --- | ||||||
|  |  | ||||||
|   | |||||||
| @@ -6,6 +6,13 @@ | |||||||
|  |  | ||||||
|       <div class="flex gap-4"> |       <div class="flex gap-4"> | ||||||
|         <aside class="lg:sticky lg:top-0 lg:h-screen lg:overflow-y-auto lg:w-1/3 p-4 font-iosevka"> |         <aside class="lg:sticky lg:top-0 lg:h-screen lg:overflow-y-auto lg:w-1/3 p-4 font-iosevka"> | ||||||
|  |           {{ if .Params.headshot }} | ||||||
|  |           <div class="narrator__container w-48 mb-2" data-text="{{ .Params.narrator }}"> | ||||||
|  |             <div class="narrator__frame"> | ||||||
|  |               <img src="{{ .Params.headshot }}" alt="Photo of {{ .Params.narrator }}" class="narrator__image" loading="lazy"> | ||||||
|  |             </div> | ||||||
|  |           </div> | ||||||
|  |           {{ end }} | ||||||
|           <p><strong>Date:</strong> <time datetime="{{ .Date.Format " 2006-01-02" }}">{{ .Date.Format "January 2, 2006" }}</time></p> |           <p><strong>Date:</strong> <time datetime="{{ .Date.Format " 2006-01-02" }}">{{ .Date.Format "January 2, 2006" }}</time></p> | ||||||
|           <p><strong>Narrator:</strong> <a href="{{ "/narrator/" | relLangURL }}{{ .Params.narrator | urlize }}">{{ .Params.narrator }}</a></p> |           <p><strong>Narrator:</strong> <a href="{{ "/narrator/" | relLangURL }}{{ .Params.narrator | urlize }}">{{ .Params.narrator }}</a></p> | ||||||
|           <p><strong>Facilitator:</strong> <a href="{{ "/facilitator/" | relLangURL }}{{ .Params.facilitator | urlize }}">{{ .Params.facilitator }}</a></p> |           <p><strong>Facilitator:</strong> <a href="{{ "/facilitator/" | relLangURL }}{{ .Params.facilitator | urlize }}">{{ .Params.facilitator }}</a></p> | ||||||
|   | |||||||
							
								
								
									
										
											BIN
										
									
								
								static/headshots/placeholder-headshot.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								static/headshots/placeholder-headshot.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 1.9 MiB | 
| @@ -152,6 +152,93 @@ class ArticleGrid extends WompumGrid { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | class RadialWompumGrid { | ||||||
|  |     constructor(element, size = 10) { | ||||||
|  |         this.element = element; // Remove .parentElement | ||||||
|  |         this.segments = size; | ||||||
|  |         this.colorCalculator = new ColorCalculator(); | ||||||
|  |                 this.sigil = Sigil.generate(this.element.dataset.text || ''); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     createSegment(index) { | ||||||
|  |         const angle = (360 / this.segments); | ||||||
|  |         // Add a small gap by reducing the arc angle | ||||||
|  |         const gap = 2; // degrees of gap | ||||||
|  |         const startAngle = (index * angle) + (gap / 2); | ||||||
|  |         const endAngle = ((index + 1) * angle) - (gap / 2); | ||||||
|  |         const outerRadius = 50; | ||||||
|  |         const innerRadius = 0; | ||||||
|  |  | ||||||
|  |         const start = this.polarToCartesian(startAngle, outerRadius); | ||||||
|  |         const end = this.polarToCartesian(endAngle, outerRadius); | ||||||
|  |         const innerStart = this.polarToCartesian(startAngle, innerRadius); | ||||||
|  |         const innerEnd = this.polarToCartesian(endAngle, innerRadius); | ||||||
|  |  | ||||||
|  |         const largeArcFlag = (angle - gap) > 180 ? 1 : 0; | ||||||
|  |  | ||||||
|  |         const d = [ | ||||||
|  |             `M ${start.x} ${start.y}`, | ||||||
|  |             `A ${outerRadius} ${outerRadius} 0 ${largeArcFlag} 1 ${end.x} ${end.y}`, | ||||||
|  |             `L ${innerEnd.x} ${innerEnd.y}`, | ||||||
|  |             `A ${innerRadius} ${innerRadius} 0 ${largeArcFlag} 0 ${innerStart.x} ${innerStart.y}`, | ||||||
|  |             'Z' | ||||||
|  |         ].join(' '); | ||||||
|  |  | ||||||
|  |         const path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); | ||||||
|  |         path.setAttribute('d', d); | ||||||
|  |          | ||||||
|  |         const sigilDigit = this.getSigilDigit(index); | ||||||
|  |         // Pass empty array for influences since we don't need adjacent segments | ||||||
|  |         const color = this.colorCalculator.getColor(sigilDigit, []); | ||||||
|  |         path.setAttribute('fill', color); | ||||||
|  |          | ||||||
|  |         return path; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     polarToCartesian(angle, radius) { | ||||||
|  |         const radian = (angle - 90) * Math.PI / 180.0; | ||||||
|  |         return { | ||||||
|  |             x: 50 + (radius * Math.cos(radian)), | ||||||
|  |             y: 50 + (radius * Math.sin(radian)) | ||||||
|  |         }; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     getSigilDigit(position) { | ||||||
|  |         if (!this.sigil || !this.sigil.length) return 0; | ||||||
|  |         return this.sigil[position % this.sigil.length]; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     getInfluences(index) { | ||||||
|  |         const prev = (index - 1 + this.segments) % this.segments; | ||||||
|  |         const next = (index + 1) % this.segments; | ||||||
|  |         return [ | ||||||
|  |             this.getSigilDigit(prev), | ||||||
|  |             this.getSigilDigit(next) | ||||||
|  |         ]; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     createSVG() { | ||||||
|  |         const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); | ||||||
|  |         svg.setAttribute('viewBox', '0 0 100 100'); | ||||||
|  |         svg.setAttribute('class', 'wompum-radial-grid'); | ||||||
|  |  | ||||||
|  |         for (let i = 0; i < this.segments; i++) { | ||||||
|  |             svg.appendChild(this.createSegment(i)); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return svg; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     init() { | ||||||
|  |         if (!this.sigil || !this.sigil.length) { | ||||||
|  |             console.error('No sigil generated from text:', this.element.getAttribute('data-text')); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |         const svg = this.createSVG(); | ||||||
|  |         this.element.insertBefore(svg, this.element.firstChild); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| // Initialize grids | // Initialize grids | ||||||
| document.addEventListener('DOMContentLoaded', () => { | document.addEventListener('DOMContentLoaded', () => { | ||||||
|     // Initialize basic grids |     // Initialize basic grids | ||||||
| @@ -172,4 +259,10 @@ document.addEventListener('DOMContentLoaded', () => { | |||||||
|         const grid = new ArticleGrid(element, metadata); |         const grid = new ArticleGrid(element, metadata); | ||||||
|         grid.init(); |         grid.init(); | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|  |     // Initialize radial grids for profile images | ||||||
|  |     document.querySelectorAll('.narrator__container').forEach(element => { | ||||||
|  |         const grid = new RadialWompumGrid(element); | ||||||
|  |         grid.init(); | ||||||
|  |     }); | ||||||
| }); | }); | ||||||
		Reference in New Issue
	
	Block a user