lerobot/robot-learning-tutorial
508
1---2// @ts-ignore - types provided by Astro at runtime3import ResponsiveImage from "./ResponsiveImage.astro";4 5interface ImageItem {6 /** Source image imported via astro:assets */7 src: any;8 /** Alt text for accessibility */9 alt: string;10 /** Individual caption for this image */11 caption?: string;12 /** Optional individual image ID for referencing */13 id?: string;14 /** Enable zoom on this specific image (defaults to parent zoomable setting) */15 zoomable?: boolean;16 /** Enable download on this specific image (defaults to parent downloadable setting) */17 downloadable?: boolean;18}19 20interface Props {21 /** Array of images to display */22 images: ImageItem[];23 /** Global caption for the entire figure */24 caption?: string;25 /** Layout mode: number of columns or 'auto' for responsive */26 layout?: "2-column" | "3-column" | "4-column" | "auto";27 /** Enable medium-zoom behavior on all images (can be overridden per image) */28 zoomable?: boolean;29 /** Show download buttons on all images (can be overridden per image) */30 downloadable?: boolean;31 /** Optional class to apply on the wrapper */32 class?: string;33 /** Optional global ID for the multi-image figure */34 id?: string;35}36 37const {38 images,39 caption,40 layout = "3-column",41 zoomable = false,42 downloadable = false,43 class: className,44 id,45} = Astro.props as Props;46 47const hasCaptionSlot = Astro.slots.has("caption");48const hasCaption =49 hasCaptionSlot || (typeof caption === "string" && caption.length > 0);50const uid = `mi_${Math.random().toString(36).slice(2)}`;51 52// Generate CSS grid columns based on layout53const getGridColumns = () => {54 switch (layout) {55 case "2-column":56 return "repeat(2, 1fr)";57 case "3-column":58 return "repeat(3, 1fr)";59 case "4-column":60 return "repeat(4, 1fr)";61 case "auto":62 return "repeat(auto-fit, minmax(200px, 1fr))";63 default:64 return "repeat(3, 1fr)";65 }66};67 68const gridColumns = getGridColumns();69---70 71<div72 class={`multi-image ${className || ""}`}73 data-mi-root={uid}74 data-layout={layout}75 {id}76>77 {78 hasCaption ? (79 <figure class="multi-image-figure">80 <div81 class="multi-image-grid"82 style={`grid-template-columns: ${gridColumns}`}83 >84 {images.map((image, index) => (85 <div class="multi-image-item">86 <ResponsiveImage87 src={image.src}88 alt={image.alt}89 zoomable={image.zoomable ?? zoomable}90 downloadable={91 image.downloadable ?? downloadable92 }93 class="multi-image-img"94 />95 {image.caption && (96 <div class="multi-image-subcaption">97 {image.caption}98 </div>99 )}100 {image.id && (101 <span102 id={image.id}103 style="position: absolute;"104 />105 )}106 </div>107 ))}108 </div>109 <figcaption class="multi-image-caption">110 {hasCaptionSlot ? (111 <slot name="caption" />112 ) : (113 caption && <span set:html={caption} />114 )}115 </figcaption>116 </figure>117 ) : (118 <div119 class="multi-image-grid"120 style={`grid-template-columns: ${gridColumns}`}121 >122 {images.map((image, index) => (123 <div class="multi-image-item">124 <ResponsiveImage125 src={image.src}126 alt={image.alt}127 zoomable={image.zoomable ?? zoomable}128 downloadable={image.downloadable ?? downloadable}129 class="multi-image-img"130 />131 {image.caption && (132 <div class="multi-image-subcaption">133 {image.caption}134 </div>135 )}136 {image.id && (137 <span id={image.id} style="position: absolute;" />138 )}139 </div>140 ))}141 </div>142 )143 }144</div>145 146<style>147 .multi-image {148 margin: var(--block-spacing-y) 0;149 }150 151 .multi-image-figure {152 margin: 0;153 }154 155 .multi-image-grid {156 display: grid;157 gap: 1rem;158 align-items: start;159 }160 161 .multi-image-item {162 display: flex;163 flex-direction: column;164 text-align: center;165 }166 167 .multi-image-item :global(.ri-root) {168 margin: 0;169 }170 171 .multi-image-item :global(figure) {172 margin: 0;173 }174 175 .multi-image-img {176 width: 100%;177 height: auto;178 object-fit: contain;179 }180 181 .multi-image-subcaption {182 font-size: 0.85rem;183 color: var(--muted-color);184 margin-top: 0.5rem;185 line-height: 1.4;186 }187 188 .multi-image-caption {189 text-align: left;190 font-size: 0.9rem;191 color: var(--muted-color);192 margin-top: 1rem;193 line-height: 1.4;194 }195 196 /* Responsive behavior */197 @media (max-width: 768px) {198 .multi-image-grid[style*="repeat(3, 1fr)"],199 .multi-image-grid[style*="repeat(4, 1fr)"] {200 grid-template-columns: 1fr !important;201 gap: 1.5rem;202 }203 204 .multi-image-grid[style*="repeat(2, 1fr)"] {205 grid-template-columns: 1fr !important;206 gap: 1.5rem;207 }208 }209 210 @media (min-width: 769px) and (max-width: 1024px) {211 .multi-image-grid[style*="repeat(4, 1fr)"] {212 grid-template-columns: repeat(2, 1fr) !important;213 }214 }215 216 /* Equal height images when desired */217 .multi-image[data-layout*="column"] .multi-image-item :global(img) {218 height: 200px;219 object-fit: contain;220 }221 222 /* Auto layout gets flexible heights */223 .multi-image[data-layout="auto"] .multi-image-item :global(img) {224 height: auto;225 }226 227 /* Ensure images maintain aspect ratio */228 .multi-image-item :global(img) {229 max-width: 100%;230 display: block;231 margin: 0 auto;232 }233</style>234 