lightnet 3.12.2 → 4.0.1
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.
- package/CHANGELOG.md +55 -0
- package/exports/content.ts +7 -2
- package/exports/i18n.ts +0 -1
- package/exports/index.ts +1 -5
- package/exports/utils.ts +0 -1
- package/package.json +26 -12
- package/src/astro-integration/config.ts +60 -49
- package/src/astro-integration/integration.ts +13 -24
- package/src/astro-integration/tailwind.ts +86 -0
- package/src/astro-integration/validators/validate-inline-translations.ts +51 -0
- package/src/astro-integration/validators/validate-languages.ts +39 -0
- package/src/astro-integration/virtual.d.ts +8 -6
- package/src/astro-integration/vite-plugin-lightnet-config.ts +29 -9
- package/src/components/CarouselSection.astro +7 -11
- package/src/components/CategoriesSection.astro +2 -2
- package/src/components/HighlightSection.astro +4 -7
- package/src/components/Icon.tsx +2 -2
- package/src/components/MediaGallerySection.astro +88 -68
- package/src/components/MediaList.astro +9 -7
- package/src/components/SearchInput.astro +7 -4
- package/src/components/Section.astro +7 -5
- package/src/components/VideoPlayer.astro +2 -3
- package/src/content/content-schema.ts +129 -142
- package/src/content/get-categories.ts +52 -28
- package/src/content/get-languages.ts +29 -8
- package/src/content/get-media-collections.ts +43 -0
- package/src/content/get-media-types.ts +41 -7
- package/src/content/query-media-items.ts +23 -13
- package/src/i18n/bcp-47.ts +8 -0
- package/src/i18n/get-locale-paths.ts +1 -3
- package/src/i18n/locals.d.ts +21 -3
- package/src/i18n/locals.ts +18 -11
- package/src/i18n/resolve-current-locale.ts +18 -0
- package/src/i18n/resolve-language.ts +10 -5
- package/src/i18n/translate-map.ts +70 -0
- package/src/i18n/translate.ts +68 -47
- package/src/layouts/Page.astro +5 -3
- package/src/layouts/components/LanguagePicker.astro +22 -17
- package/src/layouts/components/Menu.astro +2 -5
- package/src/layouts/components/MenuItem.astro +1 -1
- package/src/layouts/components/PageNavigation.astro +29 -29
- package/src/layouts/components/PageTitle.astro +23 -7
- package/src/pages/404Route.astro +2 -1
- package/src/pages/RootRoute.astro +6 -1
- package/src/pages/details-page/DefaultDetailsPage.astro +9 -2
- package/src/pages/details-page/DetailsPageRoute.astro +1 -2
- package/src/pages/details-page/components/AudioPanel.astro +7 -3
- package/src/pages/details-page/components/AudioPlayer.astro +2 -2
- package/src/pages/details-page/components/ContentSection.astro +67 -44
- package/src/pages/details-page/components/MediaCollection.astro +8 -4
- package/src/pages/details-page/components/MediaCollectionsSection.astro +3 -6
- package/src/pages/details-page/components/main-details/EditButton.astro +22 -10
- package/src/pages/details-page/components/main-details/OpenButton.astro +17 -12
- package/src/pages/details-page/components/main-details/ShareButton.astro +3 -2
- package/src/pages/details-page/components/more-details/Categories.astro +5 -3
- package/src/pages/details-page/components/more-details/Languages.astro +12 -7
- package/src/pages/details-page/utils/create-content-metadata.ts +24 -9
- package/src/pages/details-page/utils/get-translations.ts +6 -0
- package/src/pages/search-page/components/LoadingSkeleton.tsx +6 -5
- package/src/pages/search-page/components/SearchFilter.astro +10 -21
- package/src/pages/search-page/components/SearchFilter.tsx +2 -2
- package/src/pages/search-page/components/SearchList.astro +10 -7
- package/src/pages/search-page/components/SearchListItem.tsx +5 -4
- package/src/pages/search-page/hooks/use-search.ts +5 -2
- package/src/utils/lazy.ts +20 -0
- package/src/utils/paths.ts +40 -3
- package/src/utils/urls.ts +1 -2
- package/src/utils/verify-schema.ts +12 -10
- package/tailwind.config.ts +1 -25
- package/__e2e__/admin.spec.ts +0 -20
- package/__e2e__/basics-fixture.ts +0 -77
- package/__e2e__/fixtures/basics/astro.config.mjs +0 -40
- package/__e2e__/fixtures/basics/node_modules/.bin/astro +0 -21
- package/__e2e__/fixtures/basics/node_modules/.bin/tailwind +0 -21
- package/__e2e__/fixtures/basics/node_modules/.bin/tailwindcss +0 -21
- package/__e2e__/fixtures/basics/node_modules/.bin/tsc +0 -21
- package/__e2e__/fixtures/basics/node_modules/.bin/tsserver +0 -21
- package/__e2e__/fixtures/basics/package.json +0 -21
- package/__e2e__/fixtures/basics/public/favicon.svg +0 -1
- package/__e2e__/fixtures/basics/public/files/example.pdf +0 -0
- package/__e2e__/fixtures/basics/src/assets/logo.png +0 -0
- package/__e2e__/fixtures/basics/src/content/categories/christian-living.json +0 -3
- package/__e2e__/fixtures/basics/src/content/categories/teens.json +0 -3
- package/__e2e__/fixtures/basics/src/content/categories/theology.json +0 -3
- package/__e2e__/fixtures/basics/src/content/media/faithful-freestyle--en.json +0 -13
- package/__e2e__/fixtures/basics/src/content/media/how-to-kickflip--de.json +0 -12
- package/__e2e__/fixtures/basics/src/content/media/images/cover.jpg +0 -0
- package/__e2e__/fixtures/basics/src/content/media/images/how-to-kickflip--en.webp +0 -0
- package/__e2e__/fixtures/basics/src/content/media/skate-sounds--en.json +0 -15
- package/__e2e__/fixtures/basics/src/content/media-collections/how-to-articles.json +0 -3
- package/__e2e__/fixtures/basics/src/content/media-types/audio.json +0 -7
- package/__e2e__/fixtures/basics/src/content/media-types/book.json +0 -9
- package/__e2e__/fixtures/basics/src/content/media-types/video.json +0 -7
- package/__e2e__/fixtures/basics/src/content.config.ts +0 -3
- package/__e2e__/fixtures/basics/src/pages/[locale]/index.astro +0 -16
- package/__e2e__/fixtures/basics/src/translations/de.yml +0 -9
- package/__e2e__/fixtures/basics/src/translations/en.yml +0 -9
- package/__e2e__/fixtures/basics/tailwind.config.mjs +0 -8
- package/__e2e__/global.teardown.ts +0 -5
- package/__e2e__/homepage.spec.ts +0 -123
- package/__e2e__/search.spec.ts +0 -14
- package/__tests__/pages/details-page/create-content-metadata.spec.ts +0 -135
- package/__tests__/utils/markdown.spec.ts +0 -74
- package/__tests__/utils/urls.spec.ts +0 -27
- package/playwright.config.ts +0 -31
- package/src/astro-integration/project-context.ts +0 -5
- package/src/content/compare-media-collection-items.ts +0 -24
- package/src/i18n/resolve-default-locale.ts +0 -19
- package/src/i18n/resolve-locales.ts +0 -5
- package/src/pages/details-page/utils/get-collection-items.ts +0 -29
- package/vitest.config.js +0 -20
|
@@ -3,25 +3,32 @@ import { fileURLToPath } from "node:url"
|
|
|
3
3
|
|
|
4
4
|
import type { AstroConfig, AstroIntegrationLogger, ViteUserConfig } from "astro"
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import type { ExtendedLightnetConfig } from "./config"
|
|
7
7
|
|
|
8
8
|
const CONFIG = "virtual:lightnet/config"
|
|
9
9
|
const LOGO = "virtual:lightnet/logo"
|
|
10
|
-
const PROJECT_CONTEXT = "virtual:lightnet/project-context"
|
|
11
10
|
const CUSTOM_HEAD = "virtual:lightnet/components/CustomHead"
|
|
12
11
|
const CUSTOM_FOOTER = "virtual:lightnet/components/CustomFooter"
|
|
12
|
+
const MEDIA_ITEM_EDIT_BUTTON_CONTROLLER =
|
|
13
|
+
"virtual:lightnet/components/media-item-edit-button-controller"
|
|
14
|
+
|
|
15
|
+
const TRANSLATION_RUNTIME_MODULES = [
|
|
16
|
+
fileURLToPath(new URL("../i18n/translations.ts", import.meta.url)),
|
|
17
|
+
fileURLToPath(new URL("../i18n/translate.ts", import.meta.url)),
|
|
18
|
+
fileURLToPath(new URL("../i18n/locals.ts", import.meta.url)),
|
|
19
|
+
]
|
|
13
20
|
|
|
14
21
|
const VIRTUAL_MODULES = [
|
|
15
22
|
CONFIG,
|
|
16
23
|
LOGO,
|
|
17
|
-
PROJECT_CONTEXT,
|
|
18
24
|
CUSTOM_HEAD,
|
|
19
25
|
CUSTOM_FOOTER,
|
|
26
|
+
MEDIA_ITEM_EDIT_BUTTON_CONTROLLER,
|
|
20
27
|
] as const
|
|
21
28
|
|
|
22
29
|
export function vitePluginLightnetConfig(
|
|
23
|
-
config:
|
|
24
|
-
{ root
|
|
30
|
+
config: ExtendedLightnetConfig,
|
|
31
|
+
{ root }: Pick<AstroConfig, "root">,
|
|
25
32
|
logger: AstroIntegrationLogger,
|
|
26
33
|
): NonNullable<ViteUserConfig["plugins"]>[number] {
|
|
27
34
|
const resolveFilePath = (id: string) =>
|
|
@@ -33,14 +40,27 @@ export function vitePluginLightnetConfig(
|
|
|
33
40
|
const module = VIRTUAL_MODULES.find((m) => m === id)
|
|
34
41
|
if (module) return `\0${module}`
|
|
35
42
|
},
|
|
36
|
-
handleHotUpdate({ file, server }) {
|
|
43
|
+
handleHotUpdate({ file, modules, server }) {
|
|
37
44
|
const srcPath = resolve(fileURLToPath(root), "src/translations/")
|
|
38
45
|
if (
|
|
39
46
|
(file.endsWith(".yml") || file.endsWith(".yaml")) &&
|
|
40
47
|
file.startsWith(srcPath)
|
|
41
48
|
) {
|
|
49
|
+
const affectedModules = [
|
|
50
|
+
...modules,
|
|
51
|
+
...TRANSLATION_RUNTIME_MODULES.flatMap((id) => {
|
|
52
|
+
const module = server.moduleGraph.getModuleById(id)
|
|
53
|
+
return module ? [module] : []
|
|
54
|
+
}),
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
for (const module of affectedModules) {
|
|
58
|
+
server.moduleGraph.invalidateModule(module)
|
|
59
|
+
}
|
|
60
|
+
|
|
42
61
|
logger.info(`Update translations ${file.slice(srcPath.length)}`)
|
|
43
|
-
server.
|
|
62
|
+
server.ws.send({ type: "full-reload" })
|
|
63
|
+
return []
|
|
44
64
|
}
|
|
45
65
|
},
|
|
46
66
|
load(id): string | undefined {
|
|
@@ -52,8 +72,6 @@ export function vitePluginLightnetConfig(
|
|
|
52
72
|
return config.logo
|
|
53
73
|
? `import logo from ${resolveFilePath(config.logo.src)}; export default logo;`
|
|
54
74
|
: "export default undefined;"
|
|
55
|
-
case PROJECT_CONTEXT:
|
|
56
|
-
return `export default ${JSON.stringify({ root, srcDir, site })}`
|
|
57
75
|
case CUSTOM_HEAD:
|
|
58
76
|
return config.headComponent
|
|
59
77
|
? `export { default } from ${resolveFilePath(config.headComponent)};`
|
|
@@ -62,6 +80,8 @@ export function vitePluginLightnetConfig(
|
|
|
62
80
|
return config.footerComponent
|
|
63
81
|
? `export { default } from ${resolveFilePath(config.footerComponent)};`
|
|
64
82
|
: "export default undefined;"
|
|
83
|
+
case MEDIA_ITEM_EDIT_BUTTON_CONTROLLER:
|
|
84
|
+
return "export default undefined;"
|
|
65
85
|
}
|
|
66
86
|
},
|
|
67
87
|
}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
---
|
|
2
|
-
import
|
|
2
|
+
import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react"
|
|
3
|
+
|
|
3
4
|
import Section, { type Props as SectionProps } from "./Section.astro"
|
|
4
5
|
|
|
5
6
|
type Props = SectionProps
|
|
6
7
|
|
|
7
8
|
const { titleClass = "", ...props } = Astro.props
|
|
8
9
|
const { t, direction } = Astro.locals.i18n
|
|
10
|
+
const iconDirectionClass = direction === "rtl" ? "scale-x-[-1]" : ""
|
|
9
11
|
---
|
|
10
12
|
|
|
11
13
|
<Section {...props} titleClass={"!mb-8 sm:!-mb-1 " + titleClass}>
|
|
@@ -15,22 +17,16 @@ const { t, direction } = Astro.locals.i18n
|
|
|
15
17
|
data-button-prev
|
|
16
18
|
aria-label={t("ln.previous")}
|
|
17
19
|
class="flex h-9 w-9 items-center justify-center rounded-full border-2 border-gray-200 bg-gray-200 text-gray-600 transition-colors ease-in-out hover:border-gray-400 disabled:bg-transparent disabled:text-gray-300 disabled:hover:border-gray-200"
|
|
18
|
-
><Icon
|
|
19
|
-
className="mdi--chevron-left"
|
|
20
|
-
ariaLabel=""
|
|
21
|
-
flipIcon={direction === "rtl"}
|
|
22
|
-
/></button
|
|
23
20
|
>
|
|
21
|
+
<ChevronLeftIcon className={iconDirectionClass} />
|
|
22
|
+
</button>
|
|
24
23
|
<button
|
|
25
24
|
data-button-next
|
|
26
25
|
aria-label={t("ln.next")}
|
|
27
26
|
class="flex h-9 w-9 items-center justify-center rounded-full border-2 border-gray-200 bg-gray-200 text-gray-600 transition-colors ease-in-out hover:border-gray-400 disabled:bg-transparent disabled:text-gray-300 disabled:hover:border-gray-200"
|
|
28
|
-
><Icon
|
|
29
|
-
className="mdi--chevron-right"
|
|
30
|
-
ariaLabel=""
|
|
31
|
-
flipIcon={direction === "rtl"}
|
|
32
|
-
/></button
|
|
33
27
|
>
|
|
28
|
+
<ChevronRightIcon className={iconDirectionClass} />
|
|
29
|
+
</button>
|
|
34
30
|
</div>
|
|
35
31
|
|
|
36
32
|
<div data-carousel class="-m-4 overflow-hidden p-4">
|
|
@@ -12,10 +12,10 @@ type Props = Omit<SectionProps, "maxWidth"> & {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
const { title, layout = "carousel", ...props } = Astro.props
|
|
15
|
-
const { t, currentLocale } = Astro.locals.i18n
|
|
15
|
+
const { t, tMap, currentLocale } = Astro.locals.i18n
|
|
16
16
|
const resolvedTitle = title ?? t("ln.categories")
|
|
17
17
|
|
|
18
|
-
const categories = await getUsedCategories(currentLocale,
|
|
18
|
+
const categories = await getUsedCategories(currentLocale, tMap)
|
|
19
19
|
type Category = (typeof categories)[number]
|
|
20
20
|
|
|
21
21
|
function getImage({ image, id }: Category) {
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
import type { ImageMetadata } from "astro"
|
|
3
3
|
import { Image } from "astro:assets"
|
|
4
|
-
|
|
5
|
-
import Icon from "./Icon"
|
|
4
|
+
import { ArrowRightIcon } from "lucide-react"
|
|
6
5
|
|
|
7
6
|
interface Props {
|
|
8
7
|
image: ImageMetadata
|
|
@@ -17,6 +16,8 @@ interface Props {
|
|
|
17
16
|
|
|
18
17
|
const { image, id, title, text, link, className, titleClass, textClass } =
|
|
19
18
|
Astro.props
|
|
19
|
+
const iconDirectionClass =
|
|
20
|
+
Astro.locals.i18n.direction === "rtl" ? "scale-x-[-1]" : ""
|
|
20
21
|
---
|
|
21
22
|
|
|
22
23
|
<section
|
|
@@ -57,11 +58,7 @@ const { image, id, title, text, link, className, titleClass, textClass } =
|
|
|
57
58
|
href={link.href}
|
|
58
59
|
>
|
|
59
60
|
{link.text}
|
|
60
|
-
<
|
|
61
|
-
flipIcon={Astro.locals.i18n.direction === "rtl"}
|
|
62
|
-
className="mdi--arrow-right"
|
|
63
|
-
ariaLabel=""
|
|
64
|
-
/>
|
|
61
|
+
<ArrowRightIcon className={iconDirectionClass} />
|
|
65
62
|
</a>
|
|
66
63
|
)
|
|
67
64
|
}
|
package/src/components/Icon.tsx
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Icon Component.
|
|
3
3
|
*
|
|
4
|
-
* @param className containing a
|
|
4
|
+
* @param className containing a lucide icon name prefixed with 'lucide--'
|
|
5
5
|
* @param ariaLabel accessibility label to be added e.g. for people using a screen reader. Empty string will hide the icon from a screen reader.
|
|
6
6
|
* @param flipIcon if set to true this will mirror the icon along its x-axis. Useful for RTL layouts.
|
|
7
|
-
* @see https://
|
|
7
|
+
* @see https://lucide.dev/icons/ for available icons
|
|
8
8
|
* @returns icon
|
|
9
9
|
*/
|
|
10
10
|
export default function Icon({
|
|
@@ -9,8 +9,6 @@ import CoverImageDecorator from "./CoverImageDecorator"
|
|
|
9
9
|
import Icon from "./Icon"
|
|
10
10
|
import Section, { type Props as SectionProps } from "./Section.astro"
|
|
11
11
|
|
|
12
|
-
type ItemStyle = "book" | "video" | "portrait" | "landscape"
|
|
13
|
-
|
|
14
12
|
type MediaItem = {
|
|
15
13
|
id: string
|
|
16
14
|
data: {
|
|
@@ -22,23 +20,28 @@ type MediaItem = {
|
|
|
22
20
|
|
|
23
21
|
type Props = Omit<SectionProps, "maxWidth"> & {
|
|
24
22
|
items: (MediaItem | undefined)[]
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
itemWidth?: "infer" | "narrow" | "wide"
|
|
24
|
+
layout?: "grid" | "carousel"
|
|
27
25
|
}
|
|
28
26
|
|
|
29
27
|
const {
|
|
30
28
|
items: itemsInput,
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
itemWidth = "infer",
|
|
30
|
+
layout = "carousel",
|
|
33
31
|
...sectionProps
|
|
34
32
|
} = Astro.props
|
|
35
33
|
|
|
36
|
-
const
|
|
34
|
+
const { currentLocale, tMap } = Astro.locals.i18n
|
|
37
35
|
|
|
38
36
|
const types = Object.fromEntries(
|
|
39
37
|
(await getMediaTypes()).map((type) => [
|
|
40
38
|
type.id,
|
|
41
|
-
{
|
|
39
|
+
{
|
|
40
|
+
...type.data,
|
|
41
|
+
labelText: tMap(type.data.label, {
|
|
42
|
+
path: ["media-types", type.id, "label"],
|
|
43
|
+
}),
|
|
44
|
+
},
|
|
42
45
|
]),
|
|
43
46
|
)
|
|
44
47
|
|
|
@@ -46,14 +49,26 @@ const types = Object.fromEntries(
|
|
|
46
49
|
// this is in the return type of getEntry
|
|
47
50
|
const items = itemsInput.filter((item) => !!item)
|
|
48
51
|
|
|
49
|
-
const
|
|
50
|
-
{
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
52
|
+
const { landscapeCount, portraitCount } = items.slice(0, 10).reduce(
|
|
53
|
+
(acc, item) => {
|
|
54
|
+
const { width, height } = item.data.image
|
|
55
|
+
if (width > height) {
|
|
56
|
+
acc.landscapeCount += 1
|
|
57
|
+
}
|
|
58
|
+
if (height > width) {
|
|
59
|
+
acc.portraitCount += 1
|
|
60
|
+
}
|
|
61
|
+
return acc
|
|
62
|
+
},
|
|
63
|
+
{ landscapeCount: 0, portraitCount: 0 },
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
const resolvedItemWidth =
|
|
67
|
+
itemWidth === "infer"
|
|
68
|
+
? landscapeCount > portraitCount
|
|
69
|
+
? "wide"
|
|
70
|
+
: "narrow"
|
|
71
|
+
: itemWidth
|
|
57
72
|
|
|
58
73
|
const imageSizes = {
|
|
59
74
|
narrow:
|
|
@@ -84,23 +99,68 @@ const gridLayouts = {
|
|
|
84
99
|
narrow: "grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5",
|
|
85
100
|
wide: "grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4",
|
|
86
101
|
} as const
|
|
87
|
-
|
|
88
|
-
const coverImageStyle =
|
|
89
|
-
(["book", "video"] as const).find((style) => itemStyle === style) ?? "default"
|
|
90
102
|
---
|
|
91
103
|
|
|
92
104
|
{
|
|
93
|
-
|
|
105
|
+
layout === "grid" && (
|
|
94
106
|
<Section {...sectionProps}>
|
|
95
107
|
<ol
|
|
96
108
|
class="grid items-end justify-between gap-4 md:gap-8"
|
|
97
|
-
class:list={gridLayouts[
|
|
109
|
+
class:list={gridLayouts[resolvedItemWidth]}
|
|
98
110
|
>
|
|
99
|
-
{items.map((item) =>
|
|
100
|
-
|
|
111
|
+
{items.map((item) => {
|
|
112
|
+
const coverImageStyle = types[item.data.type.id].coverImageStyle
|
|
113
|
+
return (
|
|
114
|
+
<li>
|
|
115
|
+
<a
|
|
116
|
+
href={detailsPagePath(currentLocale, item)}
|
|
117
|
+
class="group flex flex-col gap-3"
|
|
118
|
+
>
|
|
119
|
+
<CoverImageDecorator
|
|
120
|
+
style={coverImageStyle}
|
|
121
|
+
className="outline-2 outline-gray-400 transition-all duration-75 ease-in-out sm:group-hover:outline"
|
|
122
|
+
>
|
|
123
|
+
<Image
|
|
124
|
+
class="h-full w-full object-cover"
|
|
125
|
+
class:list={[
|
|
126
|
+
coverImageStyle === "video" && "absolute top-0",
|
|
127
|
+
]}
|
|
128
|
+
src={item.data.image}
|
|
129
|
+
alt=""
|
|
130
|
+
widths={[128, 256, 512, 768]}
|
|
131
|
+
sizes={imageSizes[resolvedItemWidth]}
|
|
132
|
+
/>
|
|
133
|
+
</CoverImageDecorator>
|
|
134
|
+
|
|
135
|
+
<span class="line-clamp-2 h-12 text-balance text-sm font-bold text-gray-700">
|
|
136
|
+
<Icon
|
|
137
|
+
className={`${types[item.data.type.id].icon} me-2 align-bottom`}
|
|
138
|
+
ariaLabel={types[item.data.type.id].labelText}
|
|
139
|
+
/>
|
|
140
|
+
{item.data.title}
|
|
141
|
+
</span>
|
|
142
|
+
</a>
|
|
143
|
+
</li>
|
|
144
|
+
)
|
|
145
|
+
})}
|
|
146
|
+
</ol>
|
|
147
|
+
</Section>
|
|
148
|
+
)
|
|
149
|
+
}
|
|
150
|
+
{
|
|
151
|
+
layout === "carousel" && (
|
|
152
|
+
<CarouselSection {...sectionProps}>
|
|
153
|
+
{items.map((item) => {
|
|
154
|
+
const coverImageStyle = types[item.data.type.id].coverImageStyle
|
|
155
|
+
return (
|
|
156
|
+
<li
|
|
157
|
+
class={`carousel-item--${resolvedItemWidth} h-full`}
|
|
158
|
+
role="group"
|
|
159
|
+
aria-roledescription="slide"
|
|
160
|
+
>
|
|
101
161
|
<a
|
|
102
|
-
href={detailsPagePath(
|
|
103
|
-
class="group flex flex-col gap-3"
|
|
162
|
+
href={detailsPagePath(currentLocale, item)}
|
|
163
|
+
class="group flex h-full flex-col justify-end gap-3"
|
|
104
164
|
>
|
|
105
165
|
<CoverImageDecorator
|
|
106
166
|
style={coverImageStyle}
|
|
@@ -108,11 +168,10 @@ const coverImageStyle =
|
|
|
108
168
|
>
|
|
109
169
|
<Image
|
|
110
170
|
class="h-full w-full object-cover"
|
|
111
|
-
class:list={[itemStyle === "video" && "absolute top-0"]}
|
|
112
171
|
src={item.data.image}
|
|
113
172
|
alt=""
|
|
114
173
|
widths={[128, 256, 512, 768]}
|
|
115
|
-
sizes={imageSizes[
|
|
174
|
+
sizes={imageSizes[resolvedItemWidth]}
|
|
116
175
|
/>
|
|
117
176
|
</CoverImageDecorator>
|
|
118
177
|
|
|
@@ -125,47 +184,8 @@ const coverImageStyle =
|
|
|
125
184
|
</span>
|
|
126
185
|
</a>
|
|
127
186
|
</li>
|
|
128
|
-
)
|
|
129
|
-
|
|
130
|
-
</Section>
|
|
131
|
-
)
|
|
132
|
-
}
|
|
133
|
-
{
|
|
134
|
-
viewLayout === "carousel" && (
|
|
135
|
-
<CarouselSection {...sectionProps}>
|
|
136
|
-
{items.map((item) => (
|
|
137
|
-
<li
|
|
138
|
-
class={`carousel-item--${itemWidth} h-full`}
|
|
139
|
-
role="group"
|
|
140
|
-
aria-roledescription="slide"
|
|
141
|
-
>
|
|
142
|
-
<a
|
|
143
|
-
href={detailsPagePath(Astro.currentLocale, item)}
|
|
144
|
-
class="group flex h-full flex-col justify-end gap-3"
|
|
145
|
-
>
|
|
146
|
-
<CoverImageDecorator
|
|
147
|
-
style={coverImageStyle}
|
|
148
|
-
className="outline-2 outline-gray-400 transition-all duration-75 ease-in-out sm:group-hover:outline"
|
|
149
|
-
>
|
|
150
|
-
<Image
|
|
151
|
-
class="h-full w-full object-cover"
|
|
152
|
-
src={item.data.image}
|
|
153
|
-
alt=""
|
|
154
|
-
widths={[128, 256, 512, 768]}
|
|
155
|
-
sizes={imageSizes[itemWidth]}
|
|
156
|
-
/>
|
|
157
|
-
</CoverImageDecorator>
|
|
158
|
-
|
|
159
|
-
<span class="line-clamp-2 h-12 text-balance text-sm font-bold text-gray-700">
|
|
160
|
-
<Icon
|
|
161
|
-
className={`${types[item.data.type.id].icon} me-2 align-bottom`}
|
|
162
|
-
ariaLabel={types[item.data.type.id].labelText}
|
|
163
|
-
/>
|
|
164
|
-
{item.data.title}
|
|
165
|
-
</span>
|
|
166
|
-
</a>
|
|
167
|
-
</li>
|
|
168
|
-
))}
|
|
187
|
+
)
|
|
188
|
+
})}
|
|
169
189
|
</CarouselSection>
|
|
170
190
|
)
|
|
171
191
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
import type { ImageMetadata } from "astro"
|
|
3
3
|
import { Image } from "astro:assets"
|
|
4
|
+
import { ChevronRightIcon } from "lucide-react"
|
|
4
5
|
|
|
5
6
|
import { getMediaTypes } from "../content/get-media-types"
|
|
6
7
|
import { detailsPagePath } from "../utils/paths"
|
|
@@ -27,19 +28,22 @@ interface Props {
|
|
|
27
28
|
// this is in the return type of getEntry
|
|
28
29
|
const items = Astro.props.items.filter((item) => !!item)
|
|
29
30
|
|
|
30
|
-
const {
|
|
31
|
+
const { direction, currentLocale, tMap } = Astro.locals.i18n
|
|
31
32
|
|
|
32
33
|
const mediaTypes = Object.fromEntries(
|
|
33
34
|
(await getMediaTypes()).map((type) => [
|
|
34
35
|
type.id,
|
|
35
36
|
{
|
|
36
37
|
id: type.id,
|
|
37
|
-
labelText:
|
|
38
|
+
labelText: tMap(type.data.label, {
|
|
39
|
+
path: ["media-types", type.id, "label"],
|
|
40
|
+
}),
|
|
38
41
|
icon: type.data.icon,
|
|
39
42
|
coverImageStyle: type.data.coverImageStyle,
|
|
40
43
|
},
|
|
41
44
|
]),
|
|
42
45
|
)
|
|
46
|
+
const iconDirectionClass = direction === "rtl" ? "scale-x-[-1]" : ""
|
|
43
47
|
---
|
|
44
48
|
|
|
45
49
|
<ol class="divide-y divide-gray-200">
|
|
@@ -50,7 +54,7 @@ const mediaTypes = Object.fromEntries(
|
|
|
50
54
|
href={
|
|
51
55
|
item.disabled
|
|
52
56
|
? "javascript:void(0)"
|
|
53
|
-
: detailsPagePath(
|
|
57
|
+
: detailsPagePath(currentLocale, {
|
|
54
58
|
id: item.id,
|
|
55
59
|
})
|
|
56
60
|
}
|
|
@@ -97,10 +101,8 @@ const mediaTypes = Object.fromEntries(
|
|
|
97
101
|
</div>
|
|
98
102
|
</div>
|
|
99
103
|
|
|
100
|
-
<
|
|
101
|
-
className=
|
|
102
|
-
flipIcon={direction === "rtl"}
|
|
103
|
-
ariaLabel=""
|
|
104
|
+
<ChevronRightIcon
|
|
105
|
+
className={`my-auto me-4 ms-2 hidden shrink-0 text-gray-300 sm:block md:group-hover:text-primary ${iconDirectionClass}`}
|
|
104
106
|
/>
|
|
105
107
|
</a>
|
|
106
108
|
</li>
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
---
|
|
2
|
-
import
|
|
2
|
+
import { SearchIcon } from "lucide-react"
|
|
3
|
+
|
|
4
|
+
import { searchPagePath } from "../utils/paths"
|
|
3
5
|
|
|
4
6
|
type Props = {
|
|
5
7
|
className?: string
|
|
6
8
|
}
|
|
7
9
|
|
|
8
|
-
const { t } = Astro.locals.i18n
|
|
10
|
+
const { t, currentLocale } = Astro.locals.i18n
|
|
9
11
|
---
|
|
10
12
|
|
|
11
13
|
<form
|
|
12
|
-
action={
|
|
14
|
+
action={searchPagePath(currentLocale)}
|
|
13
15
|
method="get"
|
|
14
16
|
role="search"
|
|
15
17
|
class="group flex w-full rounded-2xl shadow-sm outline outline-2 outline-offset-2 outline-transparent transition-all ease-in-out group-focus-within:outline-gray-400"
|
|
@@ -24,8 +26,9 @@ const { t } = Astro.locals.i18n
|
|
|
24
26
|
/>
|
|
25
27
|
<button
|
|
26
28
|
type="submit"
|
|
29
|
+
aria-label={t("ln.search.title")}
|
|
27
30
|
class="flex items-center rounded-e-2xl border border-gray-100/95 bg-gray-800 px-4 py-3 text-gray-50 hover:bg-gray-950 hover:text-gray-300"
|
|
28
31
|
>
|
|
29
|
-
<
|
|
32
|
+
<SearchIcon />
|
|
30
33
|
</button>
|
|
31
34
|
</form>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
import
|
|
2
|
+
import { ChevronRightIcon } from "lucide-react"
|
|
3
3
|
|
|
4
4
|
export interface Props {
|
|
5
5
|
/**
|
|
@@ -64,6 +64,8 @@ const marginTopValues = {
|
|
|
64
64
|
sm: "mt-16 md:mt-20",
|
|
65
65
|
lg: "mt-24 md:mt-28",
|
|
66
66
|
}
|
|
67
|
+
const iconDirectionClass =
|
|
68
|
+
Astro.locals.i18n.direction === "rtl" ? "scale-x-[-1]" : ""
|
|
67
69
|
---
|
|
68
70
|
|
|
69
71
|
<section
|
|
@@ -81,10 +83,10 @@ const marginTopValues = {
|
|
|
81
83
|
{titleHref ? (
|
|
82
84
|
<a href={titleHref} class="group flex items-center">
|
|
83
85
|
{title}
|
|
84
|
-
<
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
86
|
+
<ChevronRightIcon
|
|
87
|
+
size="2rem"
|
|
88
|
+
strokeWidth="3px"
|
|
89
|
+
className={`transition-colors duration-75 ease-in-out group-hover:text-primary ${iconDirectionClass}`}
|
|
88
90
|
/>
|
|
89
91
|
</a>
|
|
90
92
|
) : (
|
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
import type { ImageMetadata } from "astro"
|
|
3
3
|
import { getImage } from "astro:assets"
|
|
4
4
|
import { Image } from "astro:assets"
|
|
5
|
-
|
|
6
|
-
import Icon from "./Icon"
|
|
5
|
+
import { ExternalLinkIcon } from "lucide-react"
|
|
7
6
|
|
|
8
7
|
interface Props {
|
|
9
8
|
/**
|
|
@@ -146,7 +145,7 @@ async function parseUrl(
|
|
|
146
145
|
</span>
|
|
147
146
|
</div>
|
|
148
147
|
<div class="absolute bottom-0 end-0 start-0 z-20 flex items-center gap-2 rounded bg-black/70 px-4 py-4 text-sm font-medium text-white backdrop-blur md:text-sm">
|
|
149
|
-
<
|
|
148
|
+
<ExternalLinkIcon className="shrink-0" />
|
|
150
149
|
{t("ln.external-link")}
|
|
151
150
|
</div>
|
|
152
151
|
</a>
|