lightnet 2.18.1 → 3.0.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 (49) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/__e2e__/fixtures/basics/package.json +2 -2
  3. package/__e2e__/fixtures/basics/src/pages/[locale]/index.astro +7 -5
  4. package/exports/components.ts +5 -4
  5. package/exports/experimental-details-page.ts +12 -0
  6. package/package.json +1 -1
  7. package/src/astro-integration/config.ts +7 -0
  8. package/src/astro-integration/integration.ts +1 -1
  9. package/src/astro-integration/virtual.d.ts +5 -0
  10. package/src/astro-integration/vite-plugin-lightnet-config.ts +11 -1
  11. package/src/components/{CategoriesOverview.astro → CategoriesSection.astro} +1 -1
  12. package/src/components/{Gallery.astro → MediaGallery.astro} +6 -3
  13. package/src/components/MediaGallerySection.astro +27 -0
  14. package/src/components/{MediaItemList.astro → MediaList.astro} +18 -4
  15. package/src/components/Section.astro +29 -11
  16. package/src/{pages/details-page/components → components}/VideoPlayer.astro +40 -27
  17. package/src/i18n/translations/de.yml +5 -6
  18. package/src/i18n/translations/en.yml +5 -28
  19. package/src/i18n/translations/ru.yml +5 -6
  20. package/src/i18n/translations/uk.yml +5 -6
  21. package/src/i18n/translations.ts +3 -1
  22. package/src/layouts/Page.astro +2 -0
  23. package/src/pages/details-page/DefaultDetailsPage.astro +26 -0
  24. package/src/pages/details-page/DetailsPage.astro +16 -9
  25. package/src/pages/details-page/VideoDetailsPage.astro +19 -0
  26. package/src/pages/details-page/components/{Content.astro → ContentSection.astro} +3 -3
  27. package/src/pages/details-page/components/{Description.astro → DescriptionSection.astro} +3 -3
  28. package/src/pages/details-page/components/MainDetailsSection.astro +26 -0
  29. package/src/pages/details-page/components/MediaCollection.astro +4 -4
  30. package/src/pages/details-page/components/{MediaCollections.astro → MediaCollectionsSection.astro} +4 -4
  31. package/src/pages/details-page/components/MoreDetailsSection.astro +17 -0
  32. package/src/pages/details-page/components/VideoDetailsSection.astro +30 -0
  33. package/src/pages/details-page/components/{Authors.astro → main-details/Authors.astro} +3 -3
  34. package/src/pages/details-page/components/{Cover.astro → main-details/Cover.astro} +4 -4
  35. package/src/pages/details-page/components/{OpenButton.astro → main-details/OpenButton.astro} +6 -6
  36. package/src/pages/details-page/components/{ShareButton.astro → main-details/ShareButton.astro} +1 -1
  37. package/src/pages/details-page/components/main-details/Title.astro +28 -0
  38. package/src/pages/details-page/components/{details → more-details}/Categories.astro +3 -3
  39. package/src/pages/details-page/components/{details → more-details}/Languages.astro +6 -5
  40. package/src/pages/search-page/components/SearchFilter.tsx +3 -3
  41. package/src/pages/search-page/utils/search-translations.ts +3 -3
  42. package/exports/details-page.ts +0 -1
  43. package/src/pages/details-page/DefaultDetails.astro +0 -44
  44. package/src/pages/details-page/VideoDetails.astro +0 -43
  45. package/src/pages/details-page/components/SectionTitle.astro +0 -8
  46. package/src/pages/details-page/components/Title.astro +0 -18
  47. package/src/pages/details-page/components/details/Details.astro +0 -17
  48. /package/src/components/{Hero.astro → HeroSection.astro} +0 -0
  49. /package/src/pages/details-page/components/{details → more-details}/Label.astro +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,40 @@
1
1
  # lightnet
2
2
 
3
+ ## 3.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - [#234](https://github.com/LightNetDev/LightNet/pull/234) [`1cb0dc0`](https://github.com/LightNetDev/LightNet/commit/1cb0dc0ef2d7a3ecb5d2a37a36f2f2e8fa97a756) Thanks [@smn-cds](https://github.com/smn-cds)! - Renames custom details pages prop `slug` to `mediaId`.
8
+
9
+ - [#234](https://github.com/LightNetDev/LightNet/pull/234) [`1cb0dc0`](https://github.com/LightNetDev/LightNet/commit/1cb0dc0ef2d7a3ecb5d2a37a36f2f2e8fa97a756) Thanks [@smn-cds](https://github.com/smn-cds)! - Streamline handling of components. Make sure all components including a section are suffixed with `Section`.
10
+
11
+ - Removes `Gallery` export
12
+ - Adds `MediaGallerySection` that includes the section component
13
+ - Renames `Hero` to `HeroSection`
14
+ - Renames `CategoriesOverview` to `CategoriesSection`
15
+ - Renames `MediaItemList` to `MediaList`
16
+
17
+ - [#234](https://github.com/LightNetDev/LightNet/pull/234) [`1cb0dc0`](https://github.com/LightNetDev/LightNet/commit/1cb0dc0ef2d7a3ecb5d2a37a36f2f2e8fa97a756) Thanks [@smn-cds](https://github.com/smn-cds)! - Improve Section
18
+
19
+ - rename maxWidth values: `full` => `wide`, `prose` => `narrow`
20
+ - add marginTop settings: `none`, `sm`, `lg`
21
+
22
+ ### Minor Changes
23
+
24
+ - [#234](https://github.com/LightNetDev/LightNet/pull/234) [`1cb0dc0`](https://github.com/LightNetDev/LightNet/commit/1cb0dc0ef2d7a3ecb5d2a37a36f2f2e8fa97a756) Thanks [@smn-cds](https://github.com/smn-cds)! - Exports experimental details page components
25
+
26
+ - [#234](https://github.com/LightNetDev/LightNet/pull/234) [`1cb0dc0`](https://github.com/LightNetDev/LightNet/commit/1cb0dc0ef2d7a3ecb5d2a37a36f2f2e8fa97a756) Thanks [@smn-cds](https://github.com/smn-cds)! - Accept undefined media items on Gallery and List component.
27
+
28
+ - [#234](https://github.com/LightNetDev/LightNet/pull/234) [`1cb0dc0`](https://github.com/LightNetDev/LightNet/commit/1cb0dc0ef2d7a3ecb5d2a37a36f2f2e8fa97a756) Thanks [@smn-cds](https://github.com/smn-cds)! - Add VideoPlayer component
29
+
30
+ - [#234](https://github.com/LightNetDev/LightNet/pull/234) [`1cb0dc0`](https://github.com/LightNetDev/LightNet/commit/1cb0dc0ef2d7a3ecb5d2a37a36f2f2e8fa97a756) Thanks [@smn-cds](https://github.com/smn-cds)! - Add support for custom head components.
31
+
32
+ ## 2.18.2
33
+
34
+ ### Patch Changes
35
+
36
+ - [#232](https://github.com/LightNetDev/LightNet/pull/232) [`1ab5c4e`](https://github.com/LightNetDev/LightNet/commit/1ab5c4ea5d081d05f99f9e668e05e8cfd23a890d) Thanks [@smn-cds](https://github.com/smn-cds)! - Simplify plural rules.
37
+
3
38
  ## 2.18.1
4
39
 
5
40
  ### Patch Changes
@@ -6,9 +6,9 @@
6
6
  "dependencies": {
7
7
  "@astrojs/react": "^4.2.1",
8
8
  "@astrojs/tailwind": "^6.0.0",
9
- "@lightnet/decap-admin": "^2.4.2",
9
+ "@lightnet/decap-admin": "^3.0.0",
10
10
  "astro": "^5.5.3",
11
- "lightnet": "^2.18.0",
11
+ "lightnet": "^3.0.0",
12
12
  "react": "^19.0.0",
13
13
  "react-dom": "^19.0.0",
14
14
  "sharp": "^0.33.5",
@@ -1,14 +1,16 @@
1
1
  ---
2
2
  export { getLocalePaths as getStaticPaths } from "lightnet/i18n"
3
3
 
4
- import { Gallery, Page, Section } from "lightnet/components"
4
+ import { MediaGallerySection, Page } from "lightnet/components"
5
5
  import { getMediaItems } from "lightnet/content"
6
6
 
7
- const allItems = await getMediaItems({})
7
+ const allItems = await getMediaItems()
8
8
  ---
9
9
 
10
10
  <Page>
11
- <Section title={Astro.locals.i18n.t("home.all-items")}>
12
- <Gallery items={allItems} layout="book" />
13
- </Section>
11
+ <MediaGallerySection
12
+ title={Astro.locals.i18n.t("home.all-items")}
13
+ items={allItems}
14
+ layout="book"
15
+ />
14
16
  </Page>
@@ -1,9 +1,10 @@
1
- export { default as CategoriesOverview } from "../src/components/CategoriesOverview.astro"
2
- export { default as Gallery } from "../src/components/Gallery.astro"
3
- export { default as Hero } from "../src/components/Hero.astro"
1
+ export { default as CategoriesSection } from "../src/components/CategoriesSection.astro"
2
+ export { default as HeroSection } from "../src/components/HeroSection.astro"
4
3
  export { default as HighlightSection } from "../src/components/HighlightSection.astro"
5
4
  export { default as Icon } from "../src/components/Icon"
6
- export { default as MediaItemList } from "../src/components/MediaItemList.astro"
5
+ export { default as MediaGallerySection } from "../src/components/MediaGallerySection.astro"
6
+ export { default as MediaList } from "../src/components/MediaList.astro"
7
7
  export { default as Section } from "../src/components/Section.astro"
8
+ export { default as VideoPlayer } from "../src/components/VideoPlayer.astro"
8
9
  export { default as MarkdownPage } from "../src/layouts/MarkdownPage.astro"
9
10
  export { default as Page } from "../src/layouts/Page.astro"
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Details page components are still experimental.
3
+ * We might break the API without updating the major version.
4
+ */
5
+ export { default as ContentSection } from "../src/pages/details-page/components/ContentSection.astro"
6
+ export { default as DescriptionSection } from "../src/pages/details-page/components/DescriptionSection.astro"
7
+ export { default as OpenButton } from "../src/pages/details-page/components/main-details/OpenButton.astro"
8
+ export { default as ShareButton } from "../src/pages/details-page/components/main-details/ShareButton.astro"
9
+ export { default as MainDetailsSection } from "../src/pages/details-page/components/MainDetailsSection.astro"
10
+ export { default as MediaCollectionsSection } from "../src/pages/details-page/components/MediaCollectionsSection.astro"
11
+ export { default as MoreDetailsSection } from "../src/pages/details-page/components/MoreDetailsSection.astro"
12
+ export { default as VideoDetailsSection } from "../src/pages/details-page/components/VideoDetailsSection.astro"
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "lightnet",
3
3
  "type": "module",
4
4
  "license": "MIT",
5
- "version": "2.18.1",
5
+ "version": "3.0.0",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "https://github.com/LightNetDev/lightnet",
@@ -179,6 +179,13 @@ export const configSchema = z.object({
179
179
  * - Ensure that only trusted and necessary domains are included in this list.
180
180
  */
181
181
  internalDomains: z.array(z.string()).default([]),
182
+ /**
183
+ * Path to an Astro component to be added to the HTML head element of all pages.
184
+ * For example use this if you need to add an analytics script to every page.
185
+ *
186
+ * @example "./src/components/MyHeadTag.astro"
187
+ */
188
+ headComponent: z.string().optional(),
182
189
  /**
183
190
  * Configure search page behavior
184
191
  */
@@ -51,7 +51,7 @@ export function lightnet(lightnetConfig: LightnetConfig): AstroIntegration {
51
51
  })
52
52
 
53
53
  injectRoute({
54
- pattern: "/[locale]/media/[slug]",
54
+ pattern: "/[locale]/media/[mediaId]",
55
55
  entrypoint: "lightnet/pages/DetailsPage.astro",
56
56
  prerender: true,
57
57
  })
@@ -12,3 +12,8 @@ declare module "virtual:lightnet/project-context" {
12
12
  const context: import("./project-context").ProjectContext
13
13
  export default context
14
14
  }
15
+
16
+ declare module "virtual:lightnet/components/CustomHeadComponent" {
17
+ const CustomHeadComponent: ((props: Record<string, any>) => any) | undefined
18
+ export default CustomHeadComponent
19
+ }
@@ -8,8 +8,14 @@ import { type LightnetConfig } from "./config"
8
8
  const CONFIG = "virtual:lightnet/config"
9
9
  const LOGO = "virtual:lightnet/logo"
10
10
  const PROJECT_CONTEXT = "virtual:lightnet/project-context"
11
+ const CUSTOM_HEAD_COMPONENT = "virtual:lightnet/components/CustomHeadComponent"
11
12
 
12
- const VIRTUAL_MODULES = [CONFIG, LOGO, PROJECT_CONTEXT] as const
13
+ const VIRTUAL_MODULES = [
14
+ CONFIG,
15
+ LOGO,
16
+ PROJECT_CONTEXT,
17
+ CUSTOM_HEAD_COMPONENT,
18
+ ] as const
13
19
 
14
20
  export function vitePluginLightnetConfig(
15
21
  config: LightnetConfig,
@@ -46,6 +52,10 @@ export function vitePluginLightnetConfig(
46
52
  : "export default undefined;"
47
53
  case PROJECT_CONTEXT:
48
54
  return `export default ${JSON.stringify({ root, srcDir, site })}`
55
+ case CUSTOM_HEAD_COMPONENT:
56
+ return config.headComponent
57
+ ? `export { default } from ${resolveFilePath(config.headComponent)};`
58
+ : "export default undefined;"
49
59
  }
50
60
  },
51
61
  }
@@ -15,7 +15,7 @@ const categories = await getCategories(currentLocale, t)
15
15
 
16
16
  {
17
17
  categories.length && (
18
- <Section title={title ?? t("ln.category", { count: categories.length })}>
18
+ <Section title={title ?? t("ln.categories")}>
19
19
  <ul class="flex w-full flex-wrap gap-2 sm:gap-3">
20
20
  {categories.map((category) => (
21
21
  <li class="flex max-w-56 grow">
@@ -6,7 +6,7 @@ import { getMediaTypes } from "../content/get-media-types"
6
6
  import { detailsPagePath } from "../utils/paths"
7
7
  import Icon from "./Icon"
8
8
 
9
- type GalleryItem = {
9
+ type MediaItem = {
10
10
  id: string
11
11
  data: {
12
12
  title: string
@@ -25,11 +25,14 @@ const types = Object.fromEntries(
25
25
  )
26
26
 
27
27
  interface Props {
28
- items: GalleryItem[]
28
+ items: (MediaItem | undefined)[]
29
29
  layout: "book" | "video" | "portrait" | "landscape"
30
30
  }
31
31
 
32
- const { items, layout } = Astro.props
32
+ const { items: itemsInput, layout } = Astro.props
33
+ // We allow users to pass undefined values because
34
+ // this is in the return type of getEntry
35
+ const items = itemsInput.filter((item) => !!item)
33
36
  ---
34
37
 
35
38
  {
@@ -0,0 +1,27 @@
1
+ ---
2
+ import type { ImageMetadata } from "astro"
3
+
4
+ import MediaGallery from "./MediaGallery.astro"
5
+ import Section from "./Section.astro"
6
+
7
+ type MediaItem = {
8
+ id: string
9
+ data: {
10
+ title: string
11
+ type: { id: string }
12
+ image: ImageMetadata
13
+ }
14
+ }
15
+
16
+ interface Props {
17
+ title?: string
18
+ items: (MediaItem | undefined)[]
19
+ layout: "book" | "video" | "portrait" | "landscape"
20
+ }
21
+
22
+ const { title, items, layout } = Astro.props
23
+ ---
24
+
25
+ <Section title={title}>
26
+ <MediaGallery items={items} layout={layout} />
27
+ </Section>
@@ -1,16 +1,30 @@
1
1
  ---
2
+ import type { ImageMetadata } from "astro"
2
3
  import { Image } from "astro:assets"
3
4
 
4
- import type { MediaItemEntry } from "../content/content-schema-internal"
5
5
  import { getMediaTypes } from "../content/get-media-types"
6
6
  import { detailsPagePath } from "../utils/paths"
7
7
  import Icon from "./Icon"
8
8
 
9
+ type MediaItem = {
10
+ id: string
11
+ disabled?: boolean
12
+ data: {
13
+ title: string
14
+ language: string
15
+ authors?: string[]
16
+ type: { id: string }
17
+ image: ImageMetadata
18
+ }
19
+ }
20
+
9
21
  interface Props {
10
- items: (MediaItemEntry & { disabled?: boolean })[]
22
+ items: (MediaItem | undefined)[]
11
23
  }
12
24
 
13
- const { items } = Astro.props
25
+ // We allow users to pass undefined values because
26
+ // this is in the return type of getEntry
27
+ const items = Astro.props.items.filter((item) => !!item)
14
28
 
15
29
  const { t, direction } = Astro.locals.i18n
16
30
 
@@ -38,7 +52,7 @@ const mediaTypes = Object.fromEntries(
38
52
  id: item.id,
39
53
  })
40
54
  }
41
- class="group flex overflow-hidden py-4 transition-colors ease-in-out md:rounded-sm md:py-8 md:hover:bg-gray-100"
55
+ class="group flex overflow-hidden py-4 transition-colors ease-in-out md:rounded-sm md:py-6 md:hover:bg-gray-100"
42
56
  class:list={
43
57
  item.disabled ? "pointer-events-none cursor-default opacity-50" : ""
44
58
  }
@@ -6,15 +6,21 @@ interface Props {
6
6
  */
7
7
  id?: string
8
8
  /**
9
- * Maximum width of the section container.
9
+ * Maximum width of the section container. This limits how wide the section grows
10
+ * on bigger screens. After reaching this width the section will be centered horizontally.
10
11
  *
11
- * - "full" takes the full available width of the LightNet page. This is not equal to the screen width.
12
- * For example the full width typically is used on the homepage.
13
- * - "prose" width ensures text is well readable. This is the width used on the details page.
12
+ * - "wide" (1280 px) is the maximum homepage width.
13
+ * - "narrow" (768 px) is the maximum details page width. Optimized for good text readability.
14
14
  *
15
- * @default "full"
15
+ * @default "wide"
16
16
  */
17
- maxWidth?: "full" | "prose"
17
+ maxWidth?: "wide" | "narrow"
18
+ /**
19
+ * Gap to the component above.
20
+ *
21
+ * @default "lg"
22
+ */
23
+ marginTop?: "sm" | "lg" | "none"
18
24
  /**
19
25
  * Title on top of the section.
20
26
  */
@@ -25,17 +31,29 @@ interface Props {
25
31
  className?: string
26
32
  }
27
33
 
28
- const { id, maxWidth = "full", title, className } = Astro.props
34
+ const {
35
+ id,
36
+ maxWidth = "wide",
37
+ marginTop = "lg",
38
+ title,
39
+ className,
40
+ } = Astro.props
29
41
 
30
42
  const maxWidths = {
31
- full: "max-w-screen-xl",
32
- prose: "max-w-screen-md",
43
+ wide: "max-w-screen-xl",
44
+ narrow: "max-w-screen-md",
45
+ }
46
+
47
+ const marginTopValues = {
48
+ none: "",
49
+ sm: "mt-16 md:mt-20",
50
+ lg: "mt-24 md:mt-28",
33
51
  }
34
52
  ---
35
53
 
36
54
  <section
37
- class="mx-auto mt-24 px-4 md:mt-28 md:px-8"
38
- class:list={[maxWidths[maxWidth], className]}
55
+ class="mx-auto px-4 md:px-8"
56
+ class:list={[maxWidths[maxWidth], marginTopValues[marginTop], className]}
39
57
  id={id}
40
58
  >
41
59
  {
@@ -3,9 +3,18 @@ import type { ImageMetadata } from "astro"
3
3
  import { getImage } from "astro:assets"
4
4
 
5
5
  interface Props {
6
+ /**
7
+ * Url of the video. This can be YouTube, vimeo, or a mp4 file.
8
+ */
6
9
  url: string
7
- title: string
8
- image: ImageMetadata
10
+ /**
11
+ * Title attribute of the video. Used for screen readers.
12
+ */
13
+ title?: string
14
+ /**
15
+ * Poster image to use for the mp4 video player.
16
+ */
17
+ image?: ImageMetadata
9
18
  }
10
19
 
11
20
  const { url, title, image: imageMetadata } = Astro.props
@@ -45,34 +54,38 @@ async function parseUrl(urlToParse: string): Promise<{
45
54
 
46
55
  // https://domain.com/video.mp4
47
56
  if (url.pathname.endsWith(".mp4")) {
48
- const image = (await getImage({ src: imageMetadata, format: "webp" })).src
57
+ const image =
58
+ imageMetadata &&
59
+ (await getImage({ src: imageMetadata, format: "webp" })).src
49
60
  return { host: "mp4", id: url.toString(), image }
50
61
  }
51
62
  throw Error(`Unsupported video url: ${urlToParse}`)
52
63
  }
53
64
  ---
54
65
 
55
- {
56
- host === "youtube" ? (
57
- <iframe
58
- class="h-full w-full"
59
- src={`https://www.youtube-nocookie.com/embed/${id}`}
60
- title={title}
61
- allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
62
- referrerpolicy="strict-origin-when-cross-origin"
63
- allowfullscreen
64
- />
65
- ) : host === "vimeo" ? (
66
- <iframe
67
- class="h-full w-full"
68
- src={`https://player.vimeo.com/video/${id}`}
69
- allow="autoplay; fullscreen; picture-in-picture"
70
- allowfullscreen
71
- />
72
- ) : host === "mp4" ? (
73
- <video class="h-full w-full" controls preload="auto" poster={image}>
74
- <source src={id} type="video/mp4" />
75
- Your browser does not support the video tag.
76
- </video>
77
- ) : null
78
- }
66
+ <div class="aspect-video w-full overflow-hidden bg-black md:rounded-lg">
67
+ {
68
+ host === "youtube" ? (
69
+ <iframe
70
+ class="h-full w-full"
71
+ src={`https://www.youtube-nocookie.com/embed/${id}`}
72
+ title={title}
73
+ allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
74
+ referrerpolicy="strict-origin-when-cross-origin"
75
+ allowfullscreen
76
+ />
77
+ ) : host === "vimeo" ? (
78
+ <iframe
79
+ class="h-full w-full"
80
+ title={title}
81
+ src={`https://player.vimeo.com/video/${id}`}
82
+ allow="autoplay; fullscreen; picture-in-picture"
83
+ allowfullscreen
84
+ />
85
+ ) : host === "mp4" ? (
86
+ <video class="h-full w-full" controls preload="auto" poster={image}>
87
+ <source src={id} type="video/mp4" />
88
+ </video>
89
+ ) : null
90
+ }
91
+ </div>
@@ -1,11 +1,10 @@
1
1
  ln.404.go-to-the-home-page: Zurück zur Startseite
2
2
  ln.404.page-not-found: Seite nicht gefunden
3
- ln.category_other: Kategorien
4
- ln.category_one: Kategorie
5
- ln.language_one: Sprache
6
- ln.language_other: Sprachen
7
- ln.type_one: Typ
8
- ln.type_other: Typen
3
+ ln.categories: Kategorien
4
+ ln.category: Kategorie
5
+ ln.language: Sprache
6
+ ln.languages: Sprachen
7
+ ln.type: Typ
9
8
  ln.external-link: Externer link
10
9
  ln.details.open: Öffnen
11
10
  ln.details.part-of-collection: Teil der Sammlung
@@ -20,56 +20,33 @@ ln.home.title: Home
20
20
 
21
21
  # Label for a single media item category.
22
22
  #
23
- # Define the plural forms for different quantities as follows:
24
- # - ln.category_one : for a singular item (e.g., "1 item")
25
- # - ln.category_two : for exactly two items
26
- # - ln.category_few : for a few items (language-specific rules)
27
- # - ln.category_many : for many items (language-specific rules)
28
- # - ln.category_other: for all other cases
29
- #
30
- # For guidance on the correct plural forms for your language,
31
- # use this tool: https://jsfiddle.net/6bpxsgd4
32
- #
33
23
  # English: Category
34
24
  # Used on: https://sk8-ministries.pages.dev/en/media
35
- ln.category_one: Category
25
+ ln.category: Category
36
26
 
37
27
  # Label for multiple media item categories.
38
28
  #
39
29
  # English: Categories
40
30
  # Used on: https://sk8-ministries.pages.dev/en/media/how-to-360-flip--en/
41
- ln.category_other: Categories
31
+ ln.categories: Categories
42
32
 
43
33
  # Label for a single language.
44
34
  #
45
- # Define the plural forms for different quantities as follows:
46
- # - ln.language_one : for a singular item (e.g., "1 item")
47
- # - ln.language_two : for exactly two items
48
- # - ln.language_few : for a few items (language-specific rules)
49
- # - ln.language_many : for many items (language-specific rules)
50
- # - ln.language_other: for all other cases
51
- #
52
- # For guidance on the correct plural forms for your language,
53
- # use this tool: https://jsfiddle.net/6bpxsgd4
54
- #
55
35
  # English: Language
56
36
  # Used on: https://sk8-ministries.pages.dev/en/media
57
- ln.language_one: Language
37
+ ln.language: Language
58
38
 
59
39
  # Label for multiple languages.
60
40
  #
61
41
  # English: Languages
62
42
  # Used on: https://sk8-ministries.pages.dev/en/media/how-to-kickflip--en/
63
- ln.language_other: Languages
43
+ ln.languages: Languages
64
44
 
65
45
  # Label for a single media type.
66
46
  #
67
47
  # English: Type
68
48
  # Used on: https://sk8-ministries.pages.dev/en/media
69
- ln.type_one: Type
70
-
71
- # English: Types
72
- ln.type_other: Types
49
+ ln.type: Type
73
50
 
74
51
  # Accessibility label for an external link.
75
52
  # This is only "visible" to a screen-reader.
@@ -1,12 +1,11 @@
1
1
  ln.header.open-main-menu: Открыть главное меню
2
2
  ln.header.select-language: Выберите язык
3
3
  ln.home.title: Главная
4
- ln.category_one: Категория
5
- ln.category_other: Категории
6
- ln.language_one: Язык
7
- ln.language_other: Языки
8
- ln.type_one: Тип
9
- ln.type_other: Типы
4
+ ln.category: Категория
5
+ ln.categories: Категории
6
+ ln.language: Язык
7
+ ln.languages: Языки
8
+ ln.type: Тип
10
9
  ln.external-link: Внешняя ссылка
11
10
  ln.search.title: Поиск
12
11
  ln.search.placeholder: Поиск по названию, автору, описанию...
@@ -1,12 +1,11 @@
1
1
  ln.header.open-main-menu: Відкрити головне меню
2
2
  ln.header.select-language: Оберіть мову
3
3
  ln.home.title: Головна
4
- ln.category_one: Категорія
5
- ln.category_other: Категорії
6
- ln.language_one: Мова
7
- ln.language_other: Мови
8
- ln.type_one: Тип
9
- ln.type_other: Типи
4
+ ln.category: Категорія
5
+ ln.categories: Категорії
6
+ ln.language: Мова
7
+ ln.languages: Мови
8
+ ln.type: Тип
10
9
  ln.external-link: Зовнішнє посилання
11
10
  ln.search.title: Пошук
12
11
  ln.search.placeholder: Пошук за назвою, автором, описом...
@@ -51,9 +51,11 @@ export type LightNetTranslationKey =
51
51
  | "ln.404.go-to-the-home-page"
52
52
  | "ln.404.page-not-found"
53
53
  | "ln.category"
54
+ | "ln.categories"
54
55
  | "ln.language"
56
+ | "ln.languages"
55
57
  | "ln.external-link"
56
- | "ln.type_one"
58
+ | "ln.type"
57
59
  | "ln.details.open"
58
60
  | "ln.details.share"
59
61
  | "ln.details.part-of-collection"
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  import { ClientRouter } from "astro:transitions"
3
+ import CustomHeadComponent from "virtual:lightnet/components/CustomHeadComponent"
3
4
  import config from "virtual:lightnet/config"
4
5
 
5
6
  import { resolveLanguage } from "../i18n/resolve-language"
@@ -25,6 +26,7 @@ const language = resolveLanguage(currentLocale)
25
26
  <head>
26
27
  <meta charset="UTF-8" />
27
28
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
29
+ {CustomHeadComponent && <CustomHeadComponent />}
28
30
  <title>{title ? `${title} | ${configTitle}` : configTitle}</title>
29
31
  {description && <meta name="description" content={description} />}
30
32
  {config.manifest && <link rel="manifest" href={config.manifest} />}
@@ -0,0 +1,26 @@
1
+ ---
2
+ import ContentSection from "./components/ContentSection.astro"
3
+ import DescriptionSection from "./components/DescriptionSection.astro"
4
+ import OpenButton from "./components/main-details/OpenButton.astro"
5
+ import ShareButton from "./components/main-details/ShareButton.astro"
6
+ import MainDetailsSection from "./components/MainDetailsSection.astro"
7
+ import MediaCollectionsSection from "./components/MediaCollectionsSection.astro"
8
+ import MoreDetailsSection from "./components/MoreDetailsSection.astro"
9
+
10
+ export type Props = {
11
+ mediaId: string
12
+ coverStyle?: "default" | "book"
13
+ openActionLabel?: string
14
+ }
15
+
16
+ const { mediaId, coverStyle, openActionLabel = "ln.details.open" } = Astro.props
17
+ ---
18
+
19
+ <MainDetailsSection mediaId={mediaId} coverStyle={coverStyle}>
20
+ <OpenButton mediaId={mediaId} openActionLabel={openActionLabel} />
21
+ <ShareButton />
22
+ </MainDetailsSection>
23
+ <DescriptionSection mediaId={mediaId} />
24
+ <ContentSection mediaId={mediaId} />
25
+ <MediaCollectionsSection mediaId={mediaId} />
26
+ <MoreDetailsSection mediaId={mediaId} />
@@ -8,18 +8,18 @@ import { getMediaType } from "../../content/get-media-types"
8
8
  import { resolveLocales } from "../../i18n/resolve-locales"
9
9
  import Page from "../../layouts/Page.astro"
10
10
  import { markdownToText } from "../../utils/markdown"
11
- import DefaultDetails from "./DefaultDetails.astro"
12
- import VideoDetails from "./VideoDetails.astro"
11
+ import DefaultDetailsPage from "./DefaultDetailsPage.astro"
12
+ import VideoDetailsPage from "./VideoDetailsPage.astro"
13
13
 
14
14
  export const getStaticPaths = (async () => {
15
15
  const mediaItems = await getMediaItems()
16
16
  return resolveLocales(config).flatMap((locale) =>
17
- mediaItems.map(({ id: slug }) => ({ params: { slug, locale } })),
17
+ mediaItems.map(({ id: mediaId }) => ({ params: { mediaId, locale } })),
18
18
  )
19
19
  }) satisfies GetStaticPaths
20
20
 
21
- const { slug } = Astro.params
22
- const mediaItem = (await getMediaItem(Astro.params.slug)).data
21
+ const { mediaId } = Astro.params
22
+ const mediaItem = (await getMediaItem(Astro.params.mediaId)).data
23
23
 
24
24
  const {
25
25
  data: { detailsPage },
@@ -31,7 +31,6 @@ if (detailsPage?.layout === "custom") {
31
31
  const d = import.meta.glob("/src/details-pages/*.astro")
32
32
  const customDetailsImport = d[
33
33
  `/src/details-pages/${detailsPage.customComponent}`
34
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
35
34
  ] as () => Promise<any>
36
35
  if (!customDetailsImport) {
37
36
  throw new AstroError(
@@ -47,7 +46,15 @@ if (detailsPage?.layout === "custom") {
47
46
  title={mediaItem.title}
48
47
  description={markdownToText(mediaItem.description)}
49
48
  >
50
- {layout === "default" && <DefaultDetails slug={slug} {...detailsPage} />}
51
- {layout === "video" && <VideoDetails slug={slug} {...detailsPage} />}
52
- {CustomDetails && <CustomDetails slug={slug} {...detailsPage} />}
49
+ {
50
+ layout === "default" && (
51
+ <DefaultDetailsPage mediaId={mediaId} {...detailsPage} />
52
+ )
53
+ }
54
+ {
55
+ layout === "video" && (
56
+ <VideoDetailsPage mediaId={mediaId} {...detailsPage} />
57
+ )
58
+ }
59
+ {CustomDetails && <CustomDetails mediaId={mediaId} {...detailsPage} />}
53
60
  </Page>
@@ -0,0 +1,19 @@
1
+ ---
2
+ import ContentSection from "./components/ContentSection.astro"
3
+ import DescriptionSection from "./components/DescriptionSection.astro"
4
+ import MediaCollectionsSection from "./components/MediaCollectionsSection.astro"
5
+ import MoreDetailsSection from "./components/MoreDetailsSection.astro"
6
+ import VideoDetailsSection from "./components/VideoDetailsSection.astro"
7
+
8
+ export type Props = {
9
+ mediaId: string
10
+ }
11
+
12
+ const { mediaId } = Astro.props
13
+ ---
14
+
15
+ <VideoDetailsSection mediaId={mediaId} />
16
+ <DescriptionSection mediaId={mediaId} />
17
+ <ContentSection mediaId={mediaId} />
18
+ <MediaCollectionsSection mediaId={mediaId} />
19
+ <MoreDetailsSection mediaId={mediaId} />
@@ -7,10 +7,10 @@ import {
7
7
  } from "../utils/create-content-metadata"
8
8
 
9
9
  interface Props {
10
- slug: string
10
+ mediaId: string
11
11
  }
12
12
 
13
- const item = await getMediaItem(Astro.props.slug)
13
+ const item = await getMediaItem(Astro.props.mediaId)
14
14
 
15
15
  if (item.data.content.length < 2) {
16
16
  return
@@ -31,7 +31,7 @@ const { t, direction } = Astro.locals.i18n
31
31
  ---
32
32
 
33
33
  <ol
34
- class="mx-auto mt-20 max-w-screen-md overflow-hidden bg-gray-200 md:mt-24 md:rounded-xl"
34
+ class="mx-auto mt-16 max-w-screen-md overflow-hidden bg-gray-200 md:mt-20 md:rounded-xl"
35
35
  >
36
36
  {
37
37
  content.map(
@@ -4,9 +4,9 @@ import { resolveLanguage } from "../../../i18n/resolve-language"
4
4
  import { markdownToHtml } from "../../../utils/markdown"
5
5
 
6
6
  interface Props {
7
- slug: string
7
+ mediaId: string
8
8
  }
9
- const item = await getMediaItem(Astro.props.slug)
9
+ const item = await getMediaItem(Astro.props.mediaId)
10
10
 
11
11
  const { description, language } = item.data
12
12
 
@@ -17,7 +17,7 @@ const { direction, code: lang } = resolveLanguage(language)
17
17
  description && (
18
18
  <div
19
19
  dir={direction}
20
- class="prose prose-gray mx-auto mt-10 max-w-screen-md px-4 sm:mt-14 md:px-8"
20
+ class="prose prose-gray mx-auto mt-16 max-w-screen-md px-4 md:mt-20 md:px-8"
21
21
  set:html={await markdownToHtml(description)}
22
22
  lang={lang}
23
23
  id="description"
@@ -0,0 +1,26 @@
1
+ ---
2
+ import Authors from "./main-details/Authors.astro"
3
+ import Cover from "./main-details/Cover.astro"
4
+ import Title from "./main-details/Title.astro"
5
+
6
+ export type Props = {
7
+ mediaId: string
8
+ coverStyle?: "default" | "book"
9
+ }
10
+
11
+ const { mediaId, coverStyle = "default" } = Astro.props
12
+ ---
13
+
14
+ <div
15
+ class="mx-auto mt-10 flex max-w-screen-md flex-col items-center gap-8 px-4 sm:mt-20 sm:flex-row sm:items-start sm:gap-14 md:px-8"
16
+ >
17
+ <Cover mediaId={mediaId} style={coverStyle} />
18
+ <div class="flex flex-col items-center sm:items-start">
19
+ <Title className="text-center sm:text-start" mediaId={mediaId} />
20
+ <Authors className="text-center sm:text-start" mediaId={mediaId} />
21
+
22
+ <div class="mt-10 flex flex-col justify-center gap-4 sm:justify-start">
23
+ <slot />
24
+ </div>
25
+ </div>
26
+ </div>
@@ -2,7 +2,7 @@
2
2
  import { AstroError } from "astro/errors"
3
3
  import { getEntry } from "astro:content"
4
4
 
5
- import MediaItemList from "../../../components/MediaItemList.astro"
5
+ import MediaList from "../../../components/MediaList.astro"
6
6
  import { getMediaItems } from "../../../content/get-media-items"
7
7
  import { queryMediaItems } from "../../../content/query-media-items"
8
8
 
@@ -29,11 +29,11 @@ const t = Astro.locals.i18n.t
29
29
  ---
30
30
 
31
31
  <section class="mx-auto mt-16 max-w-screen-md px-4 md:mt-20 md:px-8">
32
- <h2 class="mb-3 text-xs font-bold uppercase text-gray-600">
32
+ <h2 class="mb-1 text-xs font-bold uppercase text-gray-600 md:mb-2">
33
33
  {t("ln.details.part-of-collection")}
34
34
  </h2>
35
- <h3 class="mb-2 text-lg font-bold text-gray-700">
35
+ <h3 class="text-lg font-bold text-gray-700">
36
36
  {t(collection.data.label)}
37
37
  </h3>
38
- <MediaItemList items={items} />
38
+ <MediaList items={items} />
39
39
  </section>
@@ -3,11 +3,11 @@ import { getMediaItem } from "../../../content/get-media-items"
3
3
  import MediaCollection from "./MediaCollection.astro"
4
4
 
5
5
  interface Props {
6
- slug: string
6
+ mediaId: string
7
7
  }
8
8
 
9
- const { slug } = Astro.props
10
- const item = await getMediaItem(slug)
9
+ const { mediaId } = Astro.props
10
+ const item = await getMediaItem(mediaId)
11
11
 
12
12
  const collections = item.data.collections?.map(
13
13
  ({ collection }) => collection.id,
@@ -16,6 +16,6 @@ const collections = item.data.collections?.map(
16
16
 
17
17
  {
18
18
  collections?.map((id) => (
19
- <MediaCollection collectionId={id} disableItem={slug} />
19
+ <MediaCollection collectionId={id} disableItem={mediaId} />
20
20
  ))
21
21
  }
@@ -0,0 +1,17 @@
1
+ ---
2
+ import Categories from "./more-details/Categories.astro"
3
+ import Languages from "./more-details/Languages.astro"
4
+
5
+ interface Props {
6
+ mediaId: string
7
+ }
8
+
9
+ const { mediaId } = Astro.props
10
+ ---
11
+
12
+ <div class="mx-auto mt-16 max-w-screen-md px-4 md:mt-20 md:px-8" id="details">
13
+ <div class="flex flex-col gap-4">
14
+ <Languages mediaId={mediaId} />
15
+ <Categories mediaId={mediaId} />
16
+ </div>
17
+ </div>
@@ -0,0 +1,30 @@
1
+ ---
2
+ import VideoPlayer from "../../../components/VideoPlayer.astro"
3
+ import { getMediaItem } from "../../../content/get-media-items"
4
+ import Authors from "./main-details/Authors.astro"
5
+ import ShareButton from "./main-details/ShareButton.astro"
6
+ import Title from "./main-details/Title.astro"
7
+
8
+ export type Props = {
9
+ mediaId: string
10
+ }
11
+
12
+ const { mediaId } = Astro.props
13
+
14
+ const item = await getMediaItem(mediaId)
15
+ ---
16
+
17
+ <div class="mx-auto max-w-screen-md md:mt-8 md:px-8">
18
+ <VideoPlayer
19
+ url={item.data.content[0].url!}
20
+ title={item.data.title}
21
+ image={item.data.image}
22
+ />
23
+ </div>
24
+ <div
25
+ class="mx-auto mt-6 flex max-w-screen-md flex-col items-start px-4 md:mt-10 md:px-8"
26
+ >
27
+ <Title mediaId={mediaId} />
28
+ <Authors mediaId={mediaId} />
29
+ <ShareButton className="mt-6 md:mt-10" />
30
+ </div>
@@ -1,12 +1,12 @@
1
1
  ---
2
- import { getMediaItem } from "../../../content/get-media-items"
2
+ import { getMediaItem } from "../../../../content/get-media-items"
3
3
 
4
4
  interface Props {
5
- slug: string
5
+ mediaId: string
6
6
  className?: string
7
7
  }
8
8
 
9
- const item = await getMediaItem(Astro.props.slug)
9
+ const item = await getMediaItem(Astro.props.mediaId)
10
10
  const authors = item.data.authors?.join(", ")
11
11
  ---
12
12
 
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  import { Image } from "astro:assets"
3
3
 
4
- import { getMediaItem } from "../../../content/get-media-items"
4
+ import { getMediaItem } from "../../../../content/get-media-items"
5
5
 
6
6
  interface Props {
7
- slug: string
7
+ mediaId: string
8
8
  style?: "default" | "book"
9
9
  }
10
- const { slug, style = "default" } = Astro.props
10
+ const { mediaId, style = "default" } = Astro.props
11
11
 
12
- const item = await getMediaItem(slug)
12
+ const item = await getMediaItem(mediaId)
13
13
  const image = item.data.image
14
14
  const isPortraitImage = image.height > image.width
15
15
  ---
@@ -1,14 +1,14 @@
1
1
  ---
2
- import Icon from "../../../components/Icon"
3
- import { getMediaItem } from "../../../content/get-media-items"
4
- import { createContentMetadata } from "../utils/create-content-metadata"
2
+ import Icon from "../../../../components/Icon"
3
+ import { getMediaItem } from "../../../../content/get-media-items"
4
+ import { createContentMetadata } from "../../utils/create-content-metadata"
5
5
 
6
6
  interface Props {
7
- slug: string
7
+ mediaId: string
8
8
  openActionLabel: string
9
9
  }
10
- const { slug, openActionLabel } = Astro.props
11
- const item = await getMediaItem(slug)
10
+ const { mediaId, openActionLabel } = Astro.props
11
+ const item = await getMediaItem(mediaId)
12
12
  const content = createContentMetadata(item.data.content[0])
13
13
  ---
14
14
 
@@ -1,5 +1,5 @@
1
1
  ---
2
- import Icon from "../../../components/Icon"
2
+ import Icon from "../../../../components/Icon"
3
3
 
4
4
  interface Props {
5
5
  className?: string
@@ -0,0 +1,28 @@
1
+ ---
2
+ import { getMediaItem } from "../../../../content/get-media-items"
3
+
4
+ interface Props {
5
+ mediaId: string
6
+ className?: string
7
+ }
8
+
9
+ const {
10
+ data: { title, language },
11
+ } = await getMediaItem(Astro.props.mediaId)
12
+
13
+ const titleSize = {
14
+ md: "text-3xl sm:text-4xl",
15
+ xl: "text-4xl sm:text-5xl",
16
+ }
17
+ ---
18
+
19
+ <h1
20
+ class="font-bold"
21
+ lang={language}
22
+ class:list={[
23
+ titleSize[title.length > 20 ? "md" : "xl"],
24
+ Astro.props.className,
25
+ ]}
26
+ >
27
+ {title}
28
+ </h1>
@@ -5,10 +5,10 @@ import { searchPagePath } from "../../../../utils/paths"
5
5
  import Label from "./Label.astro"
6
6
 
7
7
  interface Props {
8
- slug: string
8
+ mediaId: string
9
9
  }
10
10
 
11
- const item = await getMediaItem(Astro.props.slug)
11
+ const item = await getMediaItem(Astro.props.mediaId)
12
12
 
13
13
  const categories = item.data.categories?.map(({ id }) => id)
14
14
  const { t } = Astro.locals.i18n
@@ -17,7 +17,7 @@ const { t } = Astro.locals.i18n
17
17
  {
18
18
  !!categories?.length && (
19
19
  <div>
20
- <Label>{t("ln.category", { count: categories.length })}</Label>
20
+ <Label>{t("ln.categories")}</Label>
21
21
  <ul class="flex flex-wrap gap-2">
22
22
  {categories.map(async (category) => (
23
23
  <li class="flex rounded-lg bg-gray-200 px-4 py-1 text-gray-500 hover:bg-gray-300">
@@ -8,15 +8,16 @@ import { detailsPagePath } from "../../../../utils/paths"
8
8
  import Label from "./Label.astro"
9
9
 
10
10
  interface Props {
11
- slug: string
11
+ mediaId: string
12
12
  }
13
13
 
14
- const item = await getMediaItem(Astro.props.slug)
14
+ const item = await getMediaItem(Astro.props.mediaId)
15
15
 
16
- const { slug } = Astro.props
16
+ const { mediaId } = Astro.props
17
17
  const translations = (await getMediaItems())
18
18
  .filter(
19
- (entry) => entry.data.commonId === item.data.commonId && entry.id !== slug,
19
+ (entry) =>
20
+ entry.data.commonId === item.data.commonId && entry.id !== mediaId,
20
21
  )
21
22
  .sort((a, b) => a.data.language.localeCompare(b.data.language))
22
23
 
@@ -24,7 +25,7 @@ const { t } = Astro.locals.i18n
24
25
  ---
25
26
 
26
27
  <div>
27
- <Label>{t("ln.language", { count: translations.length + 1 })}</Label>
28
+ <Label>{translations.length ? t("ln.languages") : t("ln.language")}</Label>
28
29
  <ul class="flex flex-wrap gap-2">
29
30
  <li class="py-1 pe-2 text-gray-800">
30
31
  {resolveTranslatedLanguage(item.data.language, t).name}
@@ -111,7 +111,7 @@ export default function SearchFilter({
111
111
  <div className="mb-8 grid grid-cols-1 gap-2 sm:grid-cols-3 sm:gap-6 md:mb-10">
112
112
  {languageFilterEnabled && (
113
113
  <Select
114
- label={t("ln.language_one")}
114
+ label={t("ln.language")}
115
115
  initialValue={language}
116
116
  valueChange={(val) => setLanguage(val)}
117
117
  options={[
@@ -126,7 +126,7 @@ export default function SearchFilter({
126
126
 
127
127
  {typesFilterEnabled && (
128
128
  <Select
129
- label={t("ln.type_one")}
129
+ label={t("ln.type")}
130
130
  initialValue={type}
131
131
  valueChange={(val) => setType(val)}
132
132
  options={[
@@ -138,7 +138,7 @@ export default function SearchFilter({
138
138
 
139
139
  {categoriesFilterEnabled && (
140
140
  <Select
141
- label={t("ln.category_one")}
141
+ label={t("ln.category")}
142
142
  initialValue={category}
143
143
  valueChange={(val) => setCategory(val)}
144
144
  options={[
@@ -1,12 +1,12 @@
1
1
  const translationKeys = [
2
2
  "ln.search.no-results",
3
3
  "ln.search.title",
4
- "ln.language_one",
4
+ "ln.language",
5
5
  "ln.search.placeholder",
6
6
  "ln.search.all-languages",
7
- "ln.type_one",
7
+ "ln.type",
8
8
  "ln.search.all-types",
9
- "ln.category_one",
9
+ "ln.category",
10
10
  "ln.search.all-categories",
11
11
  "ln.search.more-results",
12
12
  ] as const
@@ -1 +0,0 @@
1
- export { default as MediaCollections } from "../src/pages/details-page/components/MediaCollections.astro"
@@ -1,44 +0,0 @@
1
- ---
2
- import Authors from "./components/Authors.astro"
3
- import Content from "./components/Content.astro"
4
- import Cover from "./components/Cover.astro"
5
- import Description from "./components/Description.astro"
6
- import Details from "./components/details/Details.astro"
7
- import MediaCollections from "./components/MediaCollections.astro"
8
- import OpenButton from "./components/OpenButton.astro"
9
- import ShareButton from "./components/ShareButton.astro"
10
- import Title from "./components/Title.astro"
11
-
12
- export type Props = {
13
- slug: string
14
- coverStyle?: "default" | "book"
15
- openActionLabel?: string
16
- }
17
-
18
- const {
19
- slug,
20
- coverStyle = "default",
21
- openActionLabel = "ln.details.open",
22
- } = Astro.props
23
- ---
24
-
25
- <div
26
- class="mx-auto mt-10 flex max-w-screen-md flex-col items-center gap-8 px-4 sm:mt-20 sm:flex-row sm:items-start sm:gap-14 md:px-8"
27
- >
28
- <Cover slug={slug} style={coverStyle} />
29
- <div class="flex flex-col items-center sm:items-start">
30
- <Title className="text-center sm:text-start" slug={slug} />
31
- <Authors className="text-center sm:text-start" slug={slug} />
32
-
33
- <div
34
- class="mt-8 flex flex-col justify-center gap-4 sm:justify-start md:mt-14"
35
- >
36
- <OpenButton slug={slug} openActionLabel={openActionLabel} />
37
- <ShareButton />
38
- </div>
39
- </div>
40
- </div>
41
- <Description slug={slug} />
42
- <Content slug={slug} />
43
- <MediaCollections slug={slug} />
44
- <Details slug={slug} />
@@ -1,43 +0,0 @@
1
- ---
2
- import { getMediaItem } from "../../content/get-media-items"
3
- import Authors from "./components/Authors.astro"
4
- import Content from "./components/Content.astro"
5
- import Description from "./components/Description.astro"
6
- import Details from "./components/details/Details.astro"
7
- import MediaCollections from "./components/MediaCollections.astro"
8
- import ShareButton from "./components/ShareButton.astro"
9
- import Title from "./components/Title.astro"
10
- import VideoPlayer from "./components/VideoPlayer.astro"
11
-
12
- export type Props = {
13
- slug: string
14
- }
15
-
16
- const { slug } = Astro.props
17
-
18
- const item = await getMediaItem(slug)
19
- ---
20
-
21
- <div class="mx-auto max-w-screen-md md:px-8">
22
- <div
23
- class="aspect-video w-full overflow-hidden bg-black md:mt-8 md:rounded-lg"
24
- >
25
- <VideoPlayer
26
- url={item.data.content[0].url!}
27
- title={item.data.title}
28
- image={item.data.image}
29
- />
30
- </div>
31
- </div>
32
- <div
33
- class="mx-auto mt-12 flex max-w-screen-md flex-col items-start px-4 md:mt-14 md:px-8"
34
- >
35
- <Title slug={slug} />
36
- <Authors slug={slug} />
37
- <ShareButton className="mt-8" />
38
- </div>
39
-
40
- <Description slug={slug} />
41
- <Content slug={slug} />
42
- <MediaCollections slug={slug} />
43
- <Details slug={slug} />
@@ -1,8 +0,0 @@
1
- ---
2
- interface Props {
3
- text: string
4
- }
5
- const { text } = Astro.props
6
- ---
7
-
8
- <h2 class="mb-2 text-xs font-bold uppercase text-gray-600">{text}</h2>
@@ -1,18 +0,0 @@
1
- ---
2
- import { getMediaItem } from "../../../content/get-media-items"
3
-
4
- interface Props {
5
- slug: string
6
- className?: string
7
- }
8
-
9
- const item = await getMediaItem(Astro.props.slug)
10
- ---
11
-
12
- <h1
13
- class="text-4xl font-bold sm:text-5xl"
14
- lang={item.data.language}
15
- class:list={Astro.props.className}
16
- >
17
- {item.data.title}
18
- </h1>
@@ -1,17 +0,0 @@
1
- ---
2
- import Categories from "./Categories.astro"
3
- import Languages from "./Languages.astro"
4
-
5
- interface Props {
6
- slug: string
7
- }
8
-
9
- const { slug } = Astro.props
10
- ---
11
-
12
- <div class="mx-auto mt-20 max-w-screen-md px-4 sm:mt-24 md:px-8" id="details">
13
- <div class="flex flex-col gap-4">
14
- <Languages slug={slug} />
15
- <Categories slug={slug} />
16
- </div>
17
- </div>