lightnet 4.5.2 → 4.6.0

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.
Files changed (34) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/package.json +13 -13
  3. package/src/components/CategoriesSection.astro +68 -72
  4. package/src/components/HeroSection.astro +23 -29
  5. package/src/components/HighlightSection.astro +22 -28
  6. package/src/components/MediaGallerySection.astro +52 -58
  7. package/src/components/MediaList.astro +56 -58
  8. package/src/components/Section.astro +19 -21
  9. package/src/components/VideoPlayer.astro +55 -57
  10. package/src/layouts/MarkdownPage.astro +2 -1
  11. package/src/layouts/Page.astro +4 -8
  12. package/src/layouts/components/footer/Footer.astro +33 -36
  13. package/src/layouts/components/footer/LanguageSelectionMenu.astro +9 -9
  14. package/src/layouts/components/header/LanguageSelectionMenu.astro +10 -12
  15. package/src/layouts/components/header/PageNavigation.astro +26 -30
  16. package/src/layouts/components/header/PageTitle.astro +15 -20
  17. package/src/layouts/components/meta/Favicon.astro +3 -5
  18. package/src/layouts/components/meta/ViewTransition.astro +0 -1
  19. package/src/pages/404Route.astro +2 -1
  20. package/src/pages/details-page/DetailsPageRoute.astro +3 -5
  21. package/src/pages/details-page/components/AudioPanel.astro +6 -8
  22. package/src/pages/details-page/components/AudioPlayer.astro +28 -30
  23. package/src/pages/details-page/components/ContentSection.astro +37 -41
  24. package/src/pages/details-page/components/DescriptionSection.astro +9 -11
  25. package/src/pages/details-page/components/MainDetailsSection.astro +1 -3
  26. package/src/pages/details-page/components/MediaCollectionsSection.astro +3 -5
  27. package/src/pages/details-page/components/VideoDetailsSection.astro +1 -3
  28. package/src/pages/details-page/components/main-details/Authors.astro +5 -7
  29. package/src/pages/details-page/components/main-details/EditButton.astro +3 -2
  30. package/src/pages/details-page/components/main-details/OpenButton.astro +3 -5
  31. package/src/pages/details-page/components/main-details/ShareButton.astro +3 -2
  32. package/src/pages/details-page/components/more-details/Categories.astro +18 -20
  33. package/src/pages/details-page/components/more-details/Languages.astro +10 -12
  34. package/src/utils/verify-schema.ts +2 -2
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # lightnet
2
2
 
3
+ ## 4.6.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#449](https://github.com/LightNetDev/LightNet/pull/449) [`b4b1ba9`](https://github.com/LightNetDev/LightNet/commit/b4b1ba9e591af8f6870f2b3eb04029820a008393) - Support passing Markdown frontmatter to Page props.
8
+
9
+ ### Patch Changes
10
+
11
+ - [#449](https://github.com/LightNetDev/LightNet/pull/449) [`b4b1ba9`](https://github.com/LightNetDev/LightNet/commit/b4b1ba9e591af8f6870f2b3eb04029820a008393) - Make globe icon in footer language selection menu clickable.
12
+
13
+ - [#449](https://github.com/LightNetDev/LightNet/pull/449) [`b4b1ba9`](https://github.com/LightNetDev/LightNet/commit/b4b1ba9e591af8f6870f2b3eb04029820a008393) - Update dependencies
14
+
3
15
  ## 4.5.2
4
16
 
5
17
  ### Patch Changes
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "LightNet makes it easy to run your own digital media library.",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
- "version": "4.5.2",
6
+ "version": "4.6.0",
7
7
  "repository": {
8
8
  "type": "git",
9
9
  "url": "https://github.com/LightNetDev/lightnet",
@@ -52,29 +52,29 @@
52
52
  },
53
53
  "dependencies": {
54
54
  "@astrojs/react": "^5.0.7",
55
- "@iconify-json/lucide": "^1.2.116",
55
+ "@iconify-json/lucide": "^1.2.130",
56
56
  "@iconify-json/mdi": "^1.2.3",
57
57
  "@iconify/tailwind": "^1.2.0",
58
58
  "@tailwindcss/typography": "^0.5.20",
59
- "@tanstack/react-virtual": "^3.14.5",
60
- "autoprefixer": "^10.5.2",
59
+ "@tanstack/react-virtual": "^3.14.11",
60
+ "autoprefixer": "^10.5.5",
61
61
  "embla-carousel": "^8.6.0",
62
62
  "embla-carousel-wheel-gestures": "^8.1.0",
63
- "fuse.js": "^7.4.2",
64
- "i18next": "^26.3.4",
65
- "lucide-react": "^1.23.0",
66
- "marked": "^18.0.5",
67
- "postcss": "^8.5.16",
63
+ "fuse.js": "^7.5.0",
64
+ "i18next": "^26.4.2",
65
+ "lucide-react": "^1.43.0",
66
+ "marked": "^18.0.12",
67
+ "postcss": "^8.5.28",
68
68
  "postcss-load-config": "^6.0.1",
69
69
  "yaml": "^2.9.0"
70
70
  },
71
71
  "devDependencies": {
72
- "@playwright/test": "^1.61.1",
73
- "@types/react": "^19.2.17",
72
+ "@internal/e2e-test-utils": "^0.0.1",
73
+ "@playwright/test": "^1.63.0",
74
+ "@types/react": "^19.2.18",
74
75
  "astro": "^6.4.8",
75
76
  "typescript": "^6.0.3",
76
- "vitest": "^4.1.9",
77
- "@internal/e2e-test-utils": "^0.0.1"
77
+ "vitest": "^5.0.0"
78
78
  },
79
79
  "engines": {
80
80
  "node": ">=22"
@@ -29,96 +29,37 @@ function getImage({ image, id }: Category) {
29
29
  }
30
30
  ---
31
31
 
32
- {
33
- !!categories.length && layout === "grid" && (
34
- <Section {...props} title={resolvedTitle}>
35
- <ol class="grid grid-cols-2 items-end justify-between gap-4 sm:grid-cols-3 md:grid-cols-4 md:gap-8 lg:grid-cols-5">
36
- {categories.map((category) => (
37
- <li class="h-full">
38
- <a
39
- href={searchPagePath(currentLocale, { category: category.id })}
40
- class="group flex h-full flex-col gap-3"
41
- >
42
- <div
43
- class="relative overflow-hidden rounded-2xl shadow-sm outline-2 outline-gray-400 transition-colors duration-75 ease-in-out sm:group-hover:outline"
44
- class:list={[
45
- !category.image && "h-full min-h-28 w-full bg-gray-300",
46
- ]}
47
- >
48
- {category.image && (
49
- <Image
50
- class="h-full w-full bg-gray-500 object-contain"
51
- src={getImage(category)}
52
- alt=""
53
- widths={[128, 256, 388, 512, 768]}
54
- sizes={
55
- "(max-width: 640px) calc(calc(100vw - 3rem ) / 2), " +
56
- "(max-width: 768px) calc(calc(100vw - 4rem ) / 3), " +
57
- "(max-width: 1024px) calc(calc(100vw - 10rem ) / 4), " +
58
- "(max-width: 1280px) calc(calc(100vw - 12rem ) / 5), " +
59
- "217px"
60
- }
61
- />
62
- )}
63
-
64
- <div
65
- class="absolute start-0 top-0 flex h-full w-full flex-col justify-end bg-gradient-to-t p-4 text-gray-50"
66
- class:list={[
67
- category.image
68
- ? "from-black/80 via-black/30 via-50% to-transparent"
69
- : "from-black/35 to-transparent",
70
- ]}
71
- >
72
- <span class="line-clamp-3 select-none text-balance font-bold">
73
- {category.labelText}
74
- </span>
75
- </div>
76
- </div>
77
- </a>
78
- </li>
79
- ))}
80
- </ol>
81
- </Section>
82
- )
83
- }
84
- {
85
- !!categories.length && layout === "carousel" && (
86
- <CarouselSection {...props} title={resolvedTitle}>
32
+ {!!categories.length && layout === "grid" && (
33
+ <Section {...props} title={resolvedTitle}>
34
+ <ol class="grid grid-cols-2 items-end justify-between gap-4 sm:grid-cols-3 md:grid-cols-4 md:gap-8 lg:grid-cols-5">
87
35
  {categories.map((category) => (
88
- <li
89
- class="carousel-item--narrow"
90
- role="group"
91
- aria-roledescription="slide"
92
- class:list={[
93
- /** Category cards without images should take the full height of other cards */
94
- category.image ? "h-full" : "self-stretch",
95
- ]}
96
- >
36
+ <li class="h-full">
97
37
  <a
98
38
  href={searchPagePath(currentLocale, { category: category.id })}
99
39
  class="group flex h-full flex-col gap-3"
100
40
  >
101
41
  <div
102
- class="relative overflow-hidden rounded-2xl shadow-sm outline-2 outline-gray-400 transition-all duration-75 ease-in-out sm:group-hover:outline"
42
+ class="relative overflow-hidden rounded-2xl shadow-sm outline-2 outline-gray-400 transition-colors duration-75 ease-in-out sm:group-hover:outline"
103
43
  class:list={[
104
- !category.image && "min-h-28 w-full flex-1 bg-gray-300",
44
+ !category.image && "h-full min-h-28 w-full bg-gray-300",
105
45
  ]}
106
46
  >
107
47
  {category.image && (
108
48
  <Image
109
- class="h-full w-full bg-gray-300 object-contain"
49
+ class="h-full w-full bg-gray-500 object-contain"
110
50
  src={getImage(category)}
111
51
  alt=""
112
52
  widths={[128, 256, 388, 512, 768]}
113
53
  sizes={
114
- "(max-width: 640px) calc(calc(100vw - 3rem ) / 2.3), " +
115
- "(max-width: 768px) calc(calc(100vw - 5rem ) / 3), " +
54
+ "(max-width: 640px) calc(calc(100vw - 3rem ) / 2), " +
55
+ "(max-width: 768px) calc(calc(100vw - 4rem ) / 3), " +
116
56
  "(max-width: 1024px) calc(calc(100vw - 10rem ) / 4), " +
117
57
  "(max-width: 1280px) calc(calc(100vw - 12rem ) / 5), " +
118
58
  "217px"
119
59
  }
120
60
  />
121
61
  )}
62
+
122
63
  <div
123
64
  class="absolute start-0 top-0 flex h-full w-full flex-col justify-end bg-gradient-to-t p-4 text-gray-50"
124
65
  class:list={[
@@ -135,6 +76,61 @@ function getImage({ image, id }: Category) {
135
76
  </a>
136
77
  </li>
137
78
  ))}
138
- </CarouselSection>
139
- )
140
- }
79
+ </ol>
80
+ </Section>
81
+ )}
82
+ {!!categories.length && layout === "carousel" && (
83
+ <CarouselSection {...props} title={resolvedTitle}>
84
+ {categories.map((category) => (
85
+ <li
86
+ class="carousel-item--narrow"
87
+ role="group"
88
+ aria-roledescription="slide"
89
+ class:list={[
90
+ /** Category cards without images should take the full height of other cards */
91
+ category.image ? "h-full" : "self-stretch",
92
+ ]}
93
+ >
94
+ <a
95
+ href={searchPagePath(currentLocale, { category: category.id })}
96
+ class="group flex h-full flex-col gap-3"
97
+ >
98
+ <div
99
+ class="relative overflow-hidden rounded-2xl shadow-sm outline-2 outline-gray-400 transition-all duration-75 ease-in-out sm:group-hover:outline"
100
+ class:list={[
101
+ !category.image && "min-h-28 w-full flex-1 bg-gray-300",
102
+ ]}
103
+ >
104
+ {category.image && (
105
+ <Image
106
+ class="h-full w-full bg-gray-300 object-contain"
107
+ src={getImage(category)}
108
+ alt=""
109
+ widths={[128, 256, 388, 512, 768]}
110
+ sizes={
111
+ "(max-width: 640px) calc(calc(100vw - 3rem ) / 2.3), " +
112
+ "(max-width: 768px) calc(calc(100vw - 5rem ) / 3), " +
113
+ "(max-width: 1024px) calc(calc(100vw - 10rem ) / 4), " +
114
+ "(max-width: 1280px) calc(calc(100vw - 12rem ) / 5), " +
115
+ "217px"
116
+ }
117
+ />
118
+ )}
119
+ <div
120
+ class="absolute start-0 top-0 flex h-full w-full flex-col justify-end bg-gradient-to-t p-4 text-gray-50"
121
+ class:list={[
122
+ category.image
123
+ ? "from-black/80 via-black/30 via-50% to-transparent"
124
+ : "from-black/35 to-transparent",
125
+ ]}
126
+ >
127
+ <span class="line-clamp-3 select-none text-balance font-bold">
128
+ {category.labelText}
129
+ </span>
130
+ </div>
131
+ </div>
132
+ </a>
133
+ </li>
134
+ ))}
135
+ </CarouselSection>
136
+ )}
@@ -60,35 +60,29 @@ const subtitleSizes = {
60
60
  !disableOverlay && "bg-gradient-radial from-black/30 to-black/40",
61
61
  ]}
62
62
  >
63
- {
64
- title && (
65
- <h1
66
- class="max-w-screen-md text-balance font-bold tracking-tight transition-transform duration-1000 group-hover:scale-[102%]"
67
- class:list={[titleSizes[titleSize], titleClass]}
68
- >
69
- {title}
70
- </h1>
71
- )
72
- }
73
- {
74
- subtitle && (
75
- <p
76
- class="mt-1 max-w-screen-sm text-balance font-bold md:mt-2"
77
- class:list={[
78
- "sm:text-lg md:text-2xl",
79
- subtitleSizes[subtitleSize],
80
- subtitleClass,
81
- ]}
82
- >
83
- {subtitle}
84
- </p>
85
- )
86
- }
87
- {
88
- showSearch && (
89
- <SearchInput className="mt-6 max-w-sm md:mt-10 md:max-w-md" />
90
- )
91
- }
63
+ {title && (
64
+ <h1
65
+ class="max-w-screen-md text-balance font-bold tracking-tight transition-transform duration-1000 group-hover:scale-[102%]"
66
+ class:list={[titleSizes[titleSize], titleClass]}
67
+ >
68
+ {title}
69
+ </h1>
70
+ )}
71
+ {subtitle && (
72
+ <p
73
+ class="mt-1 max-w-screen-sm text-balance font-bold md:mt-2"
74
+ class:list={[
75
+ "sm:text-lg md:text-2xl",
76
+ subtitleSizes[subtitleSize],
77
+ subtitleClass,
78
+ ]}
79
+ >
80
+ {subtitle}
81
+ </p>
82
+ )}
83
+ {showSearch && (
84
+ <SearchInput className="mt-6 max-w-sm md:mt-10 md:max-w-md" />
85
+ )}
92
86
  <slot />
93
87
  </div>
94
88
  </div>
@@ -34,34 +34,28 @@ const iconDirectionClass =
34
34
  class="aspect-[4/3] w-full shrink-0 object-cover md:w-1/2 xl:aspect-video"
35
35
  />
36
36
  <div class="my-16 max-w-screen-sm px-4 md:px-8 lg:my-24 xl:px-16">
37
- {
38
- title && (
39
- <h2
40
- class="mb-4 text-balance text-2xl font-bold sm:mb-8 sm:text-3xl"
41
- class:list={titleClass}
42
- >
43
- {title}
44
- </h2>
45
- )
46
- }
47
- {
48
- text && (
49
- <p class="mb-10 text-lg sm:mb-12" class:list={textClass}>
50
- {text}
51
- </p>
52
- )
53
- }
54
- {
55
- link && (
56
- <a
57
- class="inline-flex items-center justify-center gap-2 rounded-2xl bg-primary px-6 py-3 text-sm font-bold text-gray-50 shadow-sm hover:bg-primary/85 hover:text-gray-100"
58
- href={link.href}
59
- >
60
- {link.text}
61
- <ArrowRightIcon className={iconDirectionClass} />
62
- </a>
63
- )
64
- }
37
+ {title && (
38
+ <h2
39
+ class="mb-4 text-balance text-2xl font-bold sm:mb-8 sm:text-3xl"
40
+ class:list={titleClass}
41
+ >
42
+ {title}
43
+ </h2>
44
+ )}
45
+ {text && (
46
+ <p class="mb-10 text-lg sm:mb-12" class:list={textClass}>
47
+ {text}
48
+ </p>
49
+ )}
50
+ {link && (
51
+ <a
52
+ class="inline-flex items-center justify-center gap-2 rounded-2xl bg-primary px-6 py-3 text-sm font-bold text-gray-50 shadow-sm hover:bg-primary/85 hover:text-gray-100"
53
+ href={link.href}
54
+ >
55
+ {link.text}
56
+ <ArrowRightIcon className={iconDirectionClass} />
57
+ </a>
58
+ )}
65
59
  <slot />
66
60
  </div>
67
61
  </div>
@@ -96,66 +96,19 @@ const gridLayouts = {
96
96
  } as const
97
97
  ---
98
98
 
99
- {
100
- layout === "grid" && (
101
- <Section {...sectionProps}>
102
- <ol
103
- class="grid items-end justify-between gap-4 md:gap-8"
104
- class:list={gridLayouts[resolvedItemWidth]}
105
- >
106
- {items.map((item) => {
107
- const coverImageStyle = types[item.data.type.id].coverImageStyle
108
- return (
109
- <li>
110
- <a
111
- href={detailsPagePath(currentLocale, item)}
112
- class="group flex flex-col gap-3"
113
- >
114
- <CoverImageDecorator
115
- style={coverImageStyle}
116
- className="outline-2 outline-gray-400 transition-all duration-75 ease-in-out sm:group-hover:outline"
117
- >
118
- <Image
119
- class="h-full w-full object-cover"
120
- class:list={[
121
- coverImageStyle === "video" && "absolute top-0",
122
- ]}
123
- src={item.data.image}
124
- alt=""
125
- widths={[128, 256, 512, 768]}
126
- sizes={imageSizes[resolvedItemWidth]}
127
- />
128
- </CoverImageDecorator>
129
-
130
- <span class="line-clamp-2 h-12 text-balance text-sm font-bold text-gray-700">
131
- <Icon
132
- className={`${types[item.data.type.id].icon} me-2 align-bottom`}
133
- ariaLabel={types[item.data.type.id].labelText}
134
- />
135
- {item.data.title}
136
- </span>
137
- </a>
138
- </li>
139
- )
140
- })}
141
- </ol>
142
- </Section>
143
- )
144
- }
145
- {
146
- layout === "carousel" && (
147
- <CarouselSection {...sectionProps}>
99
+ {layout === "grid" && (
100
+ <Section {...sectionProps}>
101
+ <ol
102
+ class="grid items-end justify-between gap-4 md:gap-8"
103
+ class:list={gridLayouts[resolvedItemWidth]}
104
+ >
148
105
  {items.map((item) => {
149
106
  const coverImageStyle = types[item.data.type.id].coverImageStyle
150
107
  return (
151
- <li
152
- class={`carousel-item--${resolvedItemWidth} h-full`}
153
- role="group"
154
- aria-roledescription="slide"
155
- >
108
+ <li>
156
109
  <a
157
110
  href={detailsPagePath(currentLocale, item)}
158
- class="group flex h-full flex-col justify-end gap-3"
111
+ class="group flex flex-col gap-3"
159
112
  >
160
113
  <CoverImageDecorator
161
114
  style={coverImageStyle}
@@ -163,6 +116,7 @@ const gridLayouts = {
163
116
  >
164
117
  <Image
165
118
  class="h-full w-full object-cover"
119
+ class:list={[coverImageStyle === "video" && "absolute top-0"]}
166
120
  src={item.data.image}
167
121
  alt=""
168
122
  widths={[128, 256, 512, 768]}
@@ -181,6 +135,46 @@ const gridLayouts = {
181
135
  </li>
182
136
  )
183
137
  })}
184
- </CarouselSection>
185
- )
186
- }
138
+ </ol>
139
+ </Section>
140
+ )}
141
+ {layout === "carousel" && (
142
+ <CarouselSection {...sectionProps}>
143
+ {items.map((item) => {
144
+ const coverImageStyle = types[item.data.type.id].coverImageStyle
145
+ return (
146
+ <li
147
+ class={`carousel-item--${resolvedItemWidth} h-full`}
148
+ role="group"
149
+ aria-roledescription="slide"
150
+ >
151
+ <a
152
+ href={detailsPagePath(currentLocale, item)}
153
+ class="group flex h-full flex-col justify-end gap-3"
154
+ >
155
+ <CoverImageDecorator
156
+ style={coverImageStyle}
157
+ className="outline-2 outline-gray-400 transition-all duration-75 ease-in-out sm:group-hover:outline"
158
+ >
159
+ <Image
160
+ class="h-full w-full object-cover"
161
+ src={item.data.image}
162
+ alt=""
163
+ widths={[128, 256, 512, 768]}
164
+ sizes={imageSizes[resolvedItemWidth]}
165
+ />
166
+ </CoverImageDecorator>
167
+
168
+ <span class="line-clamp-2 h-12 text-balance text-sm font-bold text-gray-700">
169
+ <Icon
170
+ className={`${types[item.data.type.id].icon} me-2 align-bottom`}
171
+ ariaLabel={types[item.data.type.id].labelText}
172
+ />
173
+ {item.data.title}
174
+ </span>
175
+ </a>
176
+ </li>
177
+ )
178
+ })}
179
+ </CarouselSection>
180
+ )}
@@ -47,65 +47,63 @@ const iconDirectionClass = direction === "rtl" ? "scale-x-[-1]" : ""
47
47
  ---
48
48
 
49
49
  <ol class="divide-y divide-gray-200">
50
- {
51
- items.map((item) => (
52
- <li lang={item.data.language}>
53
- <a
54
- href={
55
- item.disabled
56
- ? "javascript:void(0)"
57
- : detailsPagePath(currentLocale, {
58
- id: item.id,
59
- })
60
- }
61
- class="group flex overflow-hidden py-4 transition-colors ease-in-out md:rounded-sm md:py-6 md:hover:bg-gray-100"
62
- class:list={
63
- item.disabled ? "pointer-events-none cursor-default opacity-50" : ""
64
- }
65
- tabindex={item.disabled ? -1 : 0}
66
- >
67
- <div class="flex h-32 w-32 shrink-0 flex-col items-start justify-center">
68
- <CoverImageDecorator
69
- style={mediaTypes[item.data.type.id].coverImageStyle}
70
- >
71
- <Image
72
- class="max-h-32 w-auto max-w-32 object-cover"
73
- class:list={[
74
- mediaTypes[item.data.type.id].coverImageStyle === "video" &&
75
- "aspect-video",
76
- ]}
77
- src={item.data.image}
78
- width={256}
79
- alt=""
80
- />
81
- </CoverImageDecorator>
82
- </div>
83
-
84
- <div
85
- class="ms-4 flex grow flex-col justify-center sm:ms-6"
86
- lang={item.data.language}
50
+ {items.map((item) => (
51
+ <li lang={item.data.language}>
52
+ <a
53
+ href={
54
+ item.disabled
55
+ ? "javascript:void(0)"
56
+ : detailsPagePath(currentLocale, {
57
+ id: item.id,
58
+ })
59
+ }
60
+ class="group flex overflow-hidden py-4 transition-colors ease-in-out md:rounded-sm md:py-6 md:hover:bg-gray-100"
61
+ class:list={
62
+ item.disabled ? "pointer-events-none cursor-default opacity-50" : ""
63
+ }
64
+ tabindex={item.disabled ? -1 : 0}
65
+ >
66
+ <div class="flex h-32 w-32 shrink-0 flex-col items-start justify-center">
67
+ <CoverImageDecorator
68
+ style={mediaTypes[item.data.type.id].coverImageStyle}
87
69
  >
88
- <p class="mb-1 line-clamp-3 text-balance font-bold text-gray-700 md:mb-3">
89
- <Icon
90
- className={`${mediaTypes[item.data.type.id].icon} me-2 align-bottom text-2xl text-gray-700`}
91
- ariaLabel={mediaTypes[item.data.type.id].labelText}
92
- />
93
- <span>{item.data.title}</span>
94
- </p>
95
- <div class="mb-3 flex flex-col flex-wrap items-start gap-2 text-balance md:flex-row md:items-center md:gap-3">
96
- {!!item.data.authors?.length && (
97
- <p class="mb-1 md:mb-0 md:text-base">
98
- {item.data.authors.join(", ")}
99
- </p>
100
- )}
101
- </div>
70
+ <Image
71
+ class="max-h-32 w-auto max-w-32 object-cover"
72
+ class:list={[
73
+ mediaTypes[item.data.type.id].coverImageStyle === "video" &&
74
+ "aspect-video",
75
+ ]}
76
+ src={item.data.image}
77
+ width={256}
78
+ alt=""
79
+ />
80
+ </CoverImageDecorator>
81
+ </div>
82
+
83
+ <div
84
+ class="ms-4 flex grow flex-col justify-center sm:ms-6"
85
+ lang={item.data.language}
86
+ >
87
+ <p class="mb-1 line-clamp-3 text-balance font-bold text-gray-700 md:mb-3">
88
+ <Icon
89
+ className={`${mediaTypes[item.data.type.id].icon} me-2 align-bottom text-2xl text-gray-700`}
90
+ ariaLabel={mediaTypes[item.data.type.id].labelText}
91
+ />
92
+ <span>{item.data.title}</span>
93
+ </p>
94
+ <div class="mb-3 flex flex-col flex-wrap items-start gap-2 text-balance md:flex-row md:items-center md:gap-3">
95
+ {!!item.data.authors?.length && (
96
+ <p class="mb-1 md:mb-0 md:text-base">
97
+ {item.data.authors.join(", ")}
98
+ </p>
99
+ )}
102
100
  </div>
101
+ </div>
103
102
 
104
- <ChevronRightIcon
105
- className={`my-auto me-4 ms-2 hidden shrink-0 text-gray-300 sm:block md:group-hover:text-primary ${iconDirectionClass}`}
106
- />
107
- </a>
108
- </li>
109
- ))
110
- }
103
+ <ChevronRightIcon
104
+ className={`my-auto me-4 ms-2 hidden shrink-0 text-gray-300 sm:block md:group-hover:text-primary ${iconDirectionClass}`}
105
+ />
106
+ </a>
107
+ </li>
108
+ ))}
111
109
  </ol>
@@ -74,26 +74,24 @@ const iconDirectionClass =
74
74
  id={id}
75
75
  aria-label={title}
76
76
  >
77
- {
78
- title && (
79
- <h2
80
- class="mb-10 text-balance text-2xl font-bold text-gray-700 sm:mb-12 sm:text-3xl"
81
- class:list={[titleClass]}
82
- >
83
- {titleHref ? (
84
- <a href={titleHref} class="group flex items-center">
85
- {title}
86
- <ChevronRightIcon
87
- size="2rem"
88
- strokeWidth="3px"
89
- className={`transition-colors duration-75 ease-in-out group-hover:text-primary ${iconDirectionClass}`}
90
- />
91
- </a>
92
- ) : (
93
- <>{title}</>
94
- )}
95
- </h2>
96
- )
97
- }
77
+ {title && (
78
+ <h2
79
+ class="mb-10 text-balance text-2xl font-bold text-gray-700 sm:mb-12 sm:text-3xl"
80
+ class:list={[titleClass]}
81
+ >
82
+ {titleHref ? (
83
+ <a href={titleHref} class="group flex items-center">
84
+ {title}
85
+ <ChevronRightIcon
86
+ size="2rem"
87
+ strokeWidth="3px"
88
+ className={`transition-colors duration-75 ease-in-out group-hover:text-primary ${iconDirectionClass}`}
89
+ />
90
+ </a>
91
+ ) : (
92
+ <>{title}</>
93
+ )}
94
+ </h2>
95
+ )}
98
96
  <slot />
99
97
  </section>