lightnet 3.6.1 → 3.8.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 (53) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/README.md +23 -11
  3. package/__e2e__/fixtures/basics/node_modules/.bin/astro +7 -3
  4. package/__e2e__/fixtures/basics/node_modules/.bin/tailwind +5 -1
  5. package/__e2e__/fixtures/basics/node_modules/.bin/tailwindcss +5 -1
  6. package/__e2e__/fixtures/basics/node_modules/.bin/tsc +7 -3
  7. package/__e2e__/fixtures/basics/node_modules/.bin/tsserver +7 -3
  8. package/__e2e__/fixtures/basics/package.json +10 -7
  9. package/__tests__/utils/markdown.spec.ts +4 -0
  10. package/__tests__/utils/urls.spec.ts +27 -0
  11. package/exports/experimental-components.ts +1 -0
  12. package/package.json +19 -12
  13. package/src/astro-integration/integration.ts +6 -0
  14. package/src/components/CarouselSection.astro +182 -0
  15. package/src/components/CategoriesSection.astro +96 -47
  16. package/src/components/MediaGallery.astro +1 -1
  17. package/src/components/SearchInput.astro +1 -0
  18. package/src/components/SearchSection.astro +9 -4
  19. package/src/components/Section.astro +31 -17
  20. package/src/content/get-categories.ts +1 -1
  21. package/src/i18n/translations/README.md +1 -1
  22. package/src/i18n/translations/ar.yml +2 -0
  23. package/src/i18n/translations/bn.yml +2 -0
  24. package/src/i18n/translations/de.yml +2 -0
  25. package/src/i18n/translations/en.yml +12 -0
  26. package/src/i18n/translations/es.yml +2 -0
  27. package/src/i18n/translations/fi.yml +2 -0
  28. package/src/i18n/translations/fr.yml +2 -0
  29. package/src/i18n/translations/hi.yml +2 -0
  30. package/src/i18n/translations/pt.yml +2 -0
  31. package/src/i18n/translations/ru.yml +2 -0
  32. package/src/i18n/translations/uk.yml +2 -0
  33. package/src/i18n/translations/zh.yml +2 -0
  34. package/src/i18n/translations.ts +2 -0
  35. package/src/layouts/Page.astro +2 -3
  36. package/src/layouts/components/Header.astro +1 -4
  37. package/src/layouts/components/ViewTransition.astro +9 -0
  38. package/src/pages/api/versions.ts +7 -0
  39. package/src/pages/details-page/AudioDetailsPage.astro +4 -9
  40. package/src/pages/details-page/DefaultDetailsPage.astro +6 -6
  41. package/src/pages/details-page/components/AudioPanel.astro +64 -0
  42. package/src/pages/details-page/components/{main-details/AudioPlayer.astro → AudioPlayer.astro} +1 -1
  43. package/src/pages/details-page/components/ContentSection.astro +5 -2
  44. package/src/pages/details-page/components/DescriptionSection.astro +1 -1
  45. package/src/pages/details-page/components/MainDetailsSection.astro +8 -5
  46. package/src/pages/details-page/components/VideoDetailsSection.astro +1 -1
  47. package/src/pages/details-page/components/main-details/Authors.astro +2 -2
  48. package/src/pages/details-page/components/main-details/OpenButton.astro +3 -1
  49. package/src/pages/details-page/components/main-details/ShareButton.astro +21 -33
  50. package/src/pages/details-page/components/main-details/{Cover.astro → Thumbnail.astro} +14 -3
  51. package/src/pages/search-page/components/SearchList.tsx +0 -1
  52. package/src/pages/search-page/hooks/use-search.ts +2 -33
  53. package/src/utils/paths.ts +15 -1
@@ -6,7 +6,7 @@ type Props = {
6
6
  className?: string
7
7
  }
8
8
  const { src, className } = Astro.props
9
- if (!src.endsWith(".mp3")) {
9
+ if (!src.toLowerCase().endsWith(".mp3")) {
10
10
  throw new AstroError(
11
11
  `Unsupported audio file ${src}`,
12
12
  "To fix the issue, reference a mp3 file.",
@@ -8,11 +8,14 @@ import {
8
8
 
9
9
  interface Props {
10
10
  mediaId: string
11
+ minimumItems?: number
11
12
  }
12
13
 
13
- const item = await getMediaItem(Astro.props.mediaId)
14
+ const { mediaId, minimumItems = 2 } = Astro.props
14
15
 
15
- if (item.data.content.length < 2) {
16
+ const item = await getMediaItem(mediaId)
17
+
18
+ if (item.data.content.length < minimumItems) {
16
19
  return
17
20
  }
18
21
 
@@ -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-16 max-w-screen-md px-4 md:mt-20 md:px-8"
20
+ class="prose prose-gray mx-auto mt-8 max-w-screen-md px-4 md:mt-12 md:px-8"
21
21
  set:html={await markdownToHtml(description)}
22
22
  lang={lang}
23
23
  id="description"
@@ -1,23 +1,26 @@
1
1
  ---
2
2
  import Authors from "./main-details/Authors.astro"
3
- import Cover from "./main-details/Cover.astro"
3
+ import ShareButton from "./main-details/ShareButton.astro"
4
+ import Thumbnail from "./main-details/Thumbnail.astro"
4
5
  import Title from "./main-details/Title.astro"
5
6
 
6
7
  export type Props = {
7
8
  mediaId: string
8
- coverStyle?: "default" | "book"
9
+ thumbnailStyle?: "default" | "book"
10
+ thumbnailSize?: "md" | "lg"
9
11
  }
10
12
 
11
- const { mediaId, coverStyle = "default" } = Astro.props
13
+ const { mediaId, thumbnailStyle, thumbnailSize } = Astro.props
12
14
  ---
13
15
 
14
16
  <div
15
17
  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
18
  >
17
- <Cover mediaId={mediaId} style={coverStyle} />
19
+ <Thumbnail size={thumbnailSize} mediaId={mediaId} style={thumbnailStyle} />
18
20
  <div class="flex w-full grow flex-col items-center sm:items-start">
19
21
  <Title className="text-center sm:text-start" mediaId={mediaId} />
20
- <Authors className="text-center sm:text-start" mediaId={mediaId} />
22
+ <Authors className="mt-2" mediaId={mediaId} />
23
+ <ShareButton className="mt-3" />
21
24
  <slot />
22
25
  </div>
23
26
  </div>
@@ -26,5 +26,5 @@ const item = await getMediaItem(mediaId)
26
26
  >
27
27
  <Title mediaId={mediaId} />
28
28
  <Authors mediaId={mediaId} />
29
- <ShareButton className="mt-6 md:mt-10" />
29
+ <ShareButton className="mt-3" />
30
30
  </div>
@@ -12,8 +12,8 @@ const authors = item.data.authors?.join(", ")
12
12
 
13
13
  {
14
14
  authors && (
15
- <p class="mt-2 text-xl sm:text-2xl" class:list={Astro.props.className}>
15
+ <div class="text-xl sm:text-2xl" class:list={Astro.props.className}>
16
16
  {authors}
17
- </p>
17
+ </div>
18
18
  )
19
19
  }
@@ -6,8 +6,9 @@ import { createContentMetadata } from "../../utils/create-content-metadata"
6
6
  interface Props {
7
7
  mediaId: string
8
8
  openActionLabel: string
9
+ className?: string
9
10
  }
10
- const { mediaId, openActionLabel } = Astro.props
11
+ const { mediaId, openActionLabel, className } = Astro.props
11
12
  const item = await getMediaItem(mediaId)
12
13
  const content = createContentMetadata(item.data.content[0])
13
14
  ---
@@ -17,6 +18,7 @@ const content = createContentMetadata(item.data.content[0])
17
18
  href={content.url}
18
19
  target={content.target}
19
20
  hreflang={item.data.language}
21
+ class:list={[className]}
20
22
  >
21
23
  {
22
24
  content.isExternal && (
@@ -7,7 +7,7 @@ interface Props {
7
7
  ---
8
8
 
9
9
  <button
10
- class="flex cursor-pointer items-center justify-center gap-2 rounded-2xl bg-gray-200 px-6 py-3 font-bold uppercase text-gray-600 shadow-sm hover:bg-gray-300"
10
+ class="flex cursor-pointer items-center gap-2 font-bold text-gray-700 underline"
11
11
  class:list={[Astro.props.className]}
12
12
  id="share-btn"
13
13
  ><Icon className="mdi--share" ariaLabel="" />
@@ -22,37 +22,25 @@ interface Props {
22
22
  </div>
23
23
  </div>
24
24
  <script>
25
- document.addEventListener("astro:after-swap", () => {
26
- initShareButton()
27
- })
28
- initShareButton()
29
-
30
- function initShareButton() {
31
- const btn = document.querySelector("#share-btn")
32
- if (!btn) {
33
- return
25
+ const btn = document.querySelector("#share-btn")
26
+ btn?.addEventListener("click", () => {
27
+ if (navigator.share) {
28
+ navigator
29
+ .share({
30
+ url: window.location.href,
31
+ })
32
+ .catch((e) => console.debug("Could not share", e))
33
+ } else {
34
+ navigator.clipboard
35
+ .writeText(window.location.href)
36
+ .then(() => {
37
+ const toast = document.querySelector<HTMLElement>("#share-success")!
38
+ toast.style.opacity = "100%"
39
+ setTimeout(() => {
40
+ toast.style.opacity = "0%"
41
+ }, 3000)
42
+ })
43
+ .catch((error) => console.log("Error copying URL to clipboard:", error))
34
44
  }
35
- btn?.addEventListener("click", () => {
36
- if (navigator.share) {
37
- navigator
38
- .share({
39
- url: window.location.href,
40
- })
41
- .catch((e) => console.debug("Could not share", e))
42
- } else {
43
- navigator.clipboard
44
- .writeText(window.location.href)
45
- .then(() => {
46
- const toast = document.querySelector<HTMLElement>("#share-success")!
47
- toast.style.opacity = "100%"
48
- setTimeout(() => {
49
- toast.style.opacity = "0%"
50
- }, 3000)
51
- })
52
- .catch((error) =>
53
- console.log("Error copying URL to clipboard:", error),
54
- )
55
- }
56
- })
57
- }
45
+ })
58
46
  </script>
@@ -6,12 +6,23 @@ import { getMediaItem } from "../../../../content/get-media-items"
6
6
  interface Props {
7
7
  mediaId: string
8
8
  style?: "default" | "book"
9
+ size?: "md" | "lg"
9
10
  }
10
- const { mediaId, style = "default" } = Astro.props
11
+ const { mediaId, style = "default", size = "lg" } = Astro.props
11
12
 
12
13
  const item = await getMediaItem(mediaId)
13
14
  const image = item.data.image
14
15
  const isPortraitImage = image.height > image.width
16
+ const imageSizes = {
17
+ md: {
18
+ css: isPortraitImage ? "h-52 w-auto" : "h-auto w-52",
19
+ query: "13rem",
20
+ },
21
+ lg: {
22
+ css: isPortraitImage ? "h-52 w-auto sm:h-72" : "h-auto w-52 sm:w-72",
23
+ query: "(max-width: 640px) 13rem, 18rem",
24
+ },
25
+ }
15
26
  ---
16
27
 
17
28
  <div
@@ -19,10 +30,10 @@ const isPortraitImage = image.height > image.width
19
30
  class:list={style === "book" ? "rounded-sm" : "rounded-md"}
20
31
  >
21
32
  <Image
22
- class:list={isPortraitImage ? "h-52 w-auto sm:h-72" : "h-auto w-52 sm:w-72"}
33
+ class:list={imageSizes[size].css}
23
34
  alt=""
24
35
  widths={[256, 512, 768, 1024]}
25
- sizes="(max-width: 640px) 13rem, 18rem"
36
+ sizes={imageSizes[size].query}
26
37
  src={image}
27
38
  quality="high"
28
39
  loading="eager"
@@ -33,7 +33,6 @@ export default function SearchList({
33
33
  const listRef = useRef<HTMLDivElement | null>(null)
34
34
  const [rowHeight, setRowHeight] = useState(256)
35
35
  const { results, isLoading } = useSearch({
36
- currentLocale,
37
36
  categories,
38
37
  languages,
39
38
  mediaTypes,
@@ -4,29 +4,13 @@ import { useEffect, useMemo, useRef, useState } from "react"
4
4
  import type { SearchItem, SearchResponse } from "../../api/search-response"
5
5
  import { observeSearchQuery, type SearchQuery } from "../utils/search-query"
6
6
 
7
- declare global {
8
- interface Window {
9
- lnSearchState?: {
10
- fuse: Fuse<SearchItem>
11
- items: SearchItem[]
12
- locale?: string
13
- }
14
- }
15
- }
16
-
17
7
  interface Context {
18
8
  categories: Record<string, string>
19
9
  mediaTypes: Record<string, { name: string }>
20
10
  languages: Record<string, { name: string }>
21
- currentLocale?: string
22
11
  }
23
12
 
24
- export function useSearch({
25
- currentLocale,
26
- categories,
27
- mediaTypes,
28
- languages,
29
- }: Context) {
13
+ export function useSearch({ categories, mediaTypes, languages }: Context) {
30
14
  const fuse = useRef<Fuse<SearchItem>>(undefined)
31
15
  const [allItems, setAllItems] = useState<SearchItem[]>([])
32
16
  const [isLoading, setIsLoading] = useState(true)
@@ -83,27 +67,12 @@ export function useSearch({
83
67
  ignoreLocation: true,
84
68
  })
85
69
  setAllItems(items)
86
- window.lnSearchState = {
87
- locale: currentLocale,
88
- items,
89
- fuse: fuse.current,
90
- }
91
70
  } catch (error) {
92
71
  console.error(error)
93
72
  }
94
73
  setIsLoading(false)
95
74
  }
96
- // try restore old search index only if
97
- // locale is still the same because we add translated values to the
98
- // search index
99
- const { lnSearchState } = window
100
- if (lnSearchState && lnSearchState.locale === currentLocale) {
101
- fuse.current = lnSearchState.fuse
102
- setAllItems(lnSearchState.items)
103
- setIsLoading(false)
104
- } else {
105
- fetchData()
106
- }
75
+ fetchData()
107
76
  return removeSearchQueryObserver
108
77
  }, [])
109
78
 
@@ -21,12 +21,26 @@ export function detailsPagePath(
21
21
  */
22
22
  export function searchPagePath(
23
23
  locale: string | undefined,
24
- filter?: { category: string },
24
+ filter?: {
25
+ category?: string
26
+ language?: string
27
+ search?: string
28
+ type?: string
29
+ },
25
30
  ) {
26
31
  const searchParams = new URLSearchParams()
27
32
  if (filter?.category) {
28
33
  searchParams.append("category", filter.category)
29
34
  }
35
+ if (filter?.language) {
36
+ searchParams.append("language", filter.language)
37
+ }
38
+ if (filter?.search) {
39
+ searchParams.append("search", filter.search)
40
+ }
41
+ if (filter?.type) {
42
+ searchParams.append("type", filter.type)
43
+ }
30
44
  const query = searchParams.size ? `?${searchParams.toString()}` : ""
31
45
  return `/${locale}/media${query}`
32
46
  }