Skip to content

Commit

Permalink
Merge branch 'main' into feature/media-viewer
Browse files Browse the repository at this point in the history
  • Loading branch information
Illawind committed Jan 14, 2025
2 parents 262424a + 3eb1ec9 commit 64d65f3
Show file tree
Hide file tree
Showing 24 changed files with 859 additions and 553 deletions.
6 changes: 4 additions & 2 deletions .env.sample
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#NUXT_PUBLIC_SITE_URL=
#NUXT_PUBLIC_SITE_NAME=
NUXT_PUBLIC_SITE_URL=http://localhost:3000
NUXT_PUBLIC_SITE_ENVIRONMENT=development

# API
#NUXT_PUBLIC_API_URL=
Expand All @@ -18,7 +20,7 @@ XILOFONE_BASE_URL=https://xilofone.rezo-zero.com
XILOFONE_USERNAME=
XILOFONE_PASSWORD=
XILOFONE_FILE_ID=
XILOFONE_OUTPUT=assets/locales/
XILOFONE_OUTPUT=i18n/locales/

# SENTRY
#NUXT_PUBLIC_SENTRY_DSN=
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
ARG NODE_VERSION=20.18.0
ARG NODE_VERSION=22.13.0
ARG NGINX_VERSION=1.27.2
ARG UID=1000

Expand Down
1 change: 0 additions & 1 deletion assets/locales/nuxt.fr.json

This file was deleted.

27 changes: 27 additions & 0 deletions components/atoms/VTime/ENLocale.stories.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<script lang="ts" setup>
const { locale } = useI18n()
locale.value = 'en'
</script>

<template>
<NuxtStory>
<NuxtStoryVariant title="Single">
<VTime datetime="2023-06-28T22:00:00+02:00" />
</NuxtStoryVariant>
<NuxtStoryVariant title="Single (with minutes)">
<VTime datetime="2023-06-28T22:15:00+02:00" />
</NuxtStoryVariant>
<NuxtStoryVariant title="Range (multi times / same day)">
<VTime :datetime="['2023-06-28T19:30:00+02:00', '2023-06-28T22:30:00+02:00']" />
</NuxtStoryVariant>
<NuxtStoryVariant title="Range (multi days / same month)">
<VTime :datetime="['2023-06-28T22:30:00+02:00', '2023-06-29T22:30:00+02:00']" />
</NuxtStoryVariant>
<NuxtStoryVariant title="Range (multi days / multi months)">
<VTime :datetime="['2023-06-28T22:30:00+02:00', '2023-07-12T22:30:00+02:00']" />
</NuxtStoryVariant>
<NuxtStoryVariant title="Range (multi years)">
<VTime :datetime="['2023-06-28T22:30:00+02:00', '2024-07-12T22:30:00+02:00']" />
</NuxtStoryVariant>
</NuxtStory>
</template>
2 changes: 1 addition & 1 deletion components/molecules/VRoadizAlert/VRoadizAlert.vue
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ const { themeClass } = useThemeProvider({ preferredTheme: 'dark' })
<VMarkdown
:content="alert.content"
/>
<VLinkButton
<VRoadizLinkButton
:label="alert.linkLabel"
:reference="linkReference"
:url="alert.linkExternalUrl"
Expand Down
6 changes: 3 additions & 3 deletions components/molecules/VRoadizLink/Default.stories.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const currentBaseUrl = computed(() => (window?.origin ? joinURL(window.origin, '
<NuxtStoryVariant title="Url prop">
<VRoadizLink
label="External link with url"
url="https:google.com"
url="https://google.com"
/>
<VRoadizLink
label="Internal link with url"
Expand All @@ -34,15 +34,15 @@ const currentBaseUrl = computed(() => (window?.origin ? joinURL(window.origin, '
</ClientOnly>
</NuxtStoryVariant>
<NuxtStoryVariant title="Link without label">
<VRoadizLink url="https:google.com" />
<VRoadizLink url="https://google.com" />
<VRoadizLink url="/page-test" />
<VRoadizLink :url="siteUrlConfigPath" />
<ClientOnly>
<VRoadizLink :url="currentBaseUrl" />
</ClientOnly>
</NuxtStoryVariant>
<NuxtStoryVariant title="Slot label">
<VRoadizLink url="https:google.com">
<VRoadizLink url="https://google.com">
External Link
</VRoadizLink>
<VRoadizLink url="/page-test">
Expand Down
25 changes: 25 additions & 0 deletions components/molecules/VRoadizLink/Schemes.stories.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<template>
<NuxtStory>
<NuxtStoryVariant title="Mailto">
<VRoadizLink
label="Link"
url="mailto:[email protected]"
/>
</NuxtStoryVariant>
<NuxtStoryVariant title="Script">
<VRoadizLink
label="Link"
url="javascript:alert('Hello')"
/>
</NuxtStoryVariant>
<NuxtStoryVariant title="Phone">
<VRoadizLink
label="Link"
url="tel:+33123456789"
/>
</NuxtStoryVariant>
</NuxtStory>
</template>

<script setup>
</script>
57 changes: 33 additions & 24 deletions components/molecules/VRoadizLink/VRoadizLink.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { h, type PropType } from 'vue'
import type { NuxtLinkProps } from '#app/components/nuxt-link'
import { NuxtLink } from '#components'
import type { ReachableItem } from '~/types/app'
import { isInternalUrl } from '~/utils/url'
export const vRoadizLinkProps = {
label: [String, Boolean],
Expand All @@ -21,47 +20,57 @@ export default defineComponent({
setup(props, { attrs, slots }) {
const reference = computed(() => {
if (!props.reference) return
return Array.isArray(props.reference) ? props.reference[0] : props.reference
})
const url = computed(() => {
return props.url || reference.value?.url || props.document?.relativePath
return Array.isArray(props.reference) ? props.reference[0] : props.reference
})
const runtimeConfig = useRuntimeConfig()
const siteUrl = runtimeConfig?.public?.site.url
const isInternal = computed(() => isInternalUrl(url.value, siteUrl))
const isExternal = computed(() => !!url.value && !isInternal.value)
const isDownload = computed(() => !!url.value && !isExternal.value && !!props.document?.relativePath)
const attributes = computed(() => {
const mergedAttrs = { ...attrs, ...props.nuxtLinkProps }
if (!url.value) return mergedAttrs
const defaultAttrs = { ...attrs, ...props.nuxtLinkProps }
if (isDownload.value) {
Object.assign(mergedAttrs, {
// Download
if (props.document?.relativePath) {
return {
...defaultAttrs,
href: useRoadizDocumentUrl(props.document?.relativePath),
target: attrs?.target || '_blank',
rel: attrs?.rel || 'noopener',
rel: attrs?.rel || 'noopener noreferrer',
download: '',
})
}
}
else if (isExternal.value) {
Object.assign(mergedAttrs, {
// External link
if (props.url && isExternalUrl(props.url, siteUrl)) {
return {
...defaultAttrs,
href: props.url,
target: attrs?.target || '_blank',
rel: attrs?.rel || 'noopener',
})
rel: attrs?.rel || 'noopener noreferrer',
}
}
else if (isInternal.value) {
// Internal link
const internalUrl = props.url && isInternalURL(props.url, siteUrl) ? props.url : reference.value && isInternalURL(reference.value.url, siteUrl) ? reference.value.url : undefined
if (internalUrl) {
// Prevent NuxtLink to add rel attrs if it is absolute internal url
Object.assign(mergedAttrs, {
to: url.value?.replace(siteUrl, ''), // Force relative path
})
return {
...defaultAttrs,
to: internalUrl?.replace(siteUrl, ''), // Force relative path
}
}
// Other kind of links (mailto, tel, javascript, etc.)
if (props.url) {
return {
...defaultAttrs,
to: props.url,
}
}
return mergedAttrs
return defaultAttrs
})
return () => {
Expand Down
7 changes: 7 additions & 0 deletions components/organisms/VFooter/VFooter.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<!-- <script setup lang="ts"></script> -->

<template>
<footer id="footer" />
</template>

<!-- <style lang="scss" module></style> -->
14 changes: 14 additions & 0 deletions components/organisms/VTopBar/Default.stories.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<template>
<NuxtStory
layout="fullscreen"
:class="$style.root"
>
<VTopBar />
</NuxtStory>
</template>

<style lang="scss" module>
.root {
height: 600vh;
}
</style>
61 changes: 61 additions & 0 deletions components/organisms/VTopBar/VTopBar.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<script lang="ts" setup>
import { VRoadizLink } from '#components'
const { isHomePage, homePagePath } = await useHomePage()
// Scroll behavior
const { isHidden, isOnPageTop } = useTopBarScroll()
// Style
const $style = useCssModule()
const rootClasses = computed(() => {
return [
$style.root,
isHomePage.value && $style['root--home'],
isOnPageTop.value && $style['root--page-top'],
isHidden.value && $style['root--hidden'],
]
})
</script>

<template>
<div
:class="rootClasses"
>
<component
:is="isHomePage ? 'div' : VRoadizLink"
:aria-label="isHomePage ? undefined : $t('back_home')"
:class="$style['logo-wrapper']"
:url="isHomePage ? undefined : homePagePath"
>
Logo
</component>
</div>
</template>

<style lang="scss" module>
@use 'assets/scss/functions/rem' as *;
@use 'assets/scss/functions/ease' as *;
.root {
position: sticky;
z-index: 10;
top: 0;
height: rem(120);
display: flex;
align-items: center;
justify-content: space-between;
padding-inline: rem(32);
transition:
background-color 0.4s ease(out-quad),
translate 0.4s ease(out-quad);
&--hidden {
translate: 0 -100%;
}
&:not(.root--page-top) {
background-color: #ccc;
}
}
</style>
6 changes: 3 additions & 3 deletions composables/use-home-page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import { joinURL } from 'ufo'

export async function useHomePage() {
const { homeItem } = await useCommonContent()
const route = useRoute()

const isHomePage = computed(() => {
const url = homeItem.value?.url
const currentPageUrl = useCurrentPage().value.webResponse?.item?.url

if (url) return route.path === url
if (url) return currentPageUrl === url

// If there is an error then maybe the API could not be reached.
// Therefore, the common content will be empty.
Expand All @@ -16,7 +16,7 @@ export async function useHomePage() {
const locales: string[] = $i18n?.localeCodes?.value || []

// test `/` or `/{locale}` depending on the current locale and the i18n route strategy from Nuxt i18n or Roadiz
return route.path === '/' || locales.map(locale => joinURL('/', locale)).includes(joinURL('/', route.path))
return currentPageUrl === '/' || (currentPageUrl && locales.map(locale => joinURL('/', locale)).includes(joinURL('/', currentPageUrl)))
})

const homePagePath = computed(() => {
Expand Down
19 changes: 19 additions & 0 deletions composables/use-i18n-cookie.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { localeCodes, nuxtI18nOptions } from '#build/i18n.options.mjs'

// Workaround for useCookieLocale() returning empty string
// @see https://github.com/nuxt-modules/i18n/issues/2975
export function useI18nCookie(): Ref<string> {
const locale: Ref<string> = ref('')
const detect = nuxtI18nOptions.detectBrowserLanguage

if (detect && detect.useCookie) {
const cookieKey = detect.cookieKey
const code = useCookie<string>(cookieKey).value ?? null

if (code && localeCodes.includes(code)) {
locale.value = code
}
}

return locale
}
26 changes: 26 additions & 0 deletions composables/use-roadiz-detect-browser-language.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import type { RoadizAlternateLink } from '@roadiz/types'
import { nuxtI18nOptions } from '#build/i18n.options.mjs'

export async function useRoadizDetectBrowserLanguage({ locale, alternateLinks }: { locale: string, alternateLinks: RoadizAlternateLink[] }) {
const { detectBrowserLanguage } = nuxtI18nOptions

if (!detectBrowserLanguage) return

const isRootPath = useRoute().path === '/' || alternateLinks.some(link => link.url === '/')

if (!isRootPath && detectBrowserLanguage.redirectOn === 'root') return

const cookieLocale = useI18nCookie().value

if (cookieLocale && detectBrowserLanguage.alwaysRedirect !== true) return

const { $i18n } = useNuxtApp()
const browserLocale = useBrowserLocale()
const preferredLocale = detectBrowserLanguage.useCookie ? (cookieLocale || browserLocale) : browserLocale

if (preferredLocale && preferredLocale !== locale && $i18n.locales.value.find(availableLocale => availableLocale.code === preferredLocale)) {
const alternateLink = alternateLinks.find(link => link.locale === preferredLocale)

if (alternateLink) await navigateTo(alternateLink.url, { replace: true, external: true })
}
}
3 changes: 3 additions & 0 deletions constants/i18n.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const I18N_LOCALES = ['fr'] as const
export const I18N_DEFAULT_LOCALE = 'fr'
export const I18N_DEFAULT_TIMEZONE = 'Europe/Paris'
15 changes: 6 additions & 9 deletions i18n.config.ts → i18n/i18n.config.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import type { I18nOptions } from 'vue-i18n'
import type { DateTimeFormat } from '@intlify/core-base'
import { I18N_DEFAULT_TIMEZONE, I18N_LOCALES } from '~/constants/i18n'

export const I18N_LOCALES = ['fr']
export const I18N_DEFAULT_LOCALE = 'fr'
export const I18N_DEFAULT_TIMEZONE = 'Europe/Paris'

// defineI18nConfig() does not work, therefore we need to type the config object
export default {
export default defineI18nConfig(() => ({
datetimeFormats: I18N_LOCALES.reduce(
(acc, cur) => ({
...acc,
Expand Down Expand Up @@ -71,6 +67,7 @@ export default {
},
},
}),
{},
{} as Record<typeof I18N_LOCALES[number], DateTimeFormat>,
),
} satisfies I18nOptions
}),
)
File renamed without changes.
2 changes: 2 additions & 0 deletions i18n/locales/nuxt.fr.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{
}
Loading

0 comments on commit 64d65f3

Please sign in to comment.