wip: ESLint Rule against loss of reactivity + prep for string-only locale files
This commit is contained in:
parent
d2020cafec
commit
3c26238ca4
7 changed files with 251 additions and 223 deletions
|
|
@ -2,23 +2,46 @@
|
|||
import HomeSectionWrapper from "@/components/molecules/HomeSectionWrapper.vue";
|
||||
import { useLocale } from "@/i18n";
|
||||
import { GREETINGS, type Greeting } from "@/i18n/greeting";
|
||||
import type { Locale } from "@/i18n/locale";
|
||||
import type * as i18n from "@/i18n/locale";
|
||||
import { VILANTIC_ID_TO_FLAG } from "@/i18n/vilantic";
|
||||
import { randomElement } from "@/utils/random";
|
||||
import { computed } from "vue";
|
||||
import flakkaImg from "@/assets/flakka.png";
|
||||
|
||||
interface SectionConfig {
|
||||
id: keyof i18n.Locale["home"]["sections"];
|
||||
image?: keyof typeof imagesI18n.value;
|
||||
}
|
||||
|
||||
const SECTION_CONFIGS = [
|
||||
{ id: "whatIsViossa", image: "viossaFlag" },
|
||||
{ id: "historyOfViossa", image: "viossaFlag" },
|
||||
{ id: "community" },
|
||||
] as const satisfies SectionConfig[];
|
||||
|
||||
const locale = useLocale();
|
||||
const homeI18n = computed(() => locale.value.home);
|
||||
|
||||
interface ImageI18n {
|
||||
src: string;
|
||||
metadata: i18n.Image;
|
||||
}
|
||||
|
||||
const imagesI18n = computed(() => {
|
||||
const imagesI18n = homeI18n.value.images;
|
||||
|
||||
return {
|
||||
viossaFlag: { src: flakkaImg, metadata: imagesI18n.viossaFlag },
|
||||
} as const satisfies Record<string, ImageI18n>;
|
||||
});
|
||||
|
||||
const greeting: Greeting = randomElement(GREETINGS);
|
||||
|
||||
const SECTION_ORDER = [
|
||||
"whatIsViossa",
|
||||
"historyOfViossa",
|
||||
"community",
|
||||
] as const satisfies (keyof Locale["home"]["sections"])[];
|
||||
|
||||
const sections = computed(() =>
|
||||
SECTION_ORDER.map((id) => locale.value.home.sections[id]),
|
||||
const sectionsI18n = computed(() =>
|
||||
SECTION_CONFIGS.map(({ id, image }: SectionConfig) => ({
|
||||
text: homeI18n.value.sections[id],
|
||||
image: image && imagesI18n.value[image],
|
||||
})),
|
||||
);
|
||||
</script>
|
||||
|
||||
|
|
@ -46,12 +69,12 @@ const sections = computed(() =>
|
|||
|
||||
<section class="section container">
|
||||
<HomeSectionWrapper
|
||||
v-for="(section, index) in sections"
|
||||
v-for="(sectioni18n, index) in sectionsI18n"
|
||||
:key="index"
|
||||
:title="section.title"
|
||||
:text="section.text"
|
||||
:image="section.image?.src"
|
||||
:alt="section.image?.alt"
|
||||
:title="sectioni18n.text.title"
|
||||
:text="sectioni18n.text.body"
|
||||
:image="sectioni18n.image?.src"
|
||||
:alt="sectioni18n.image?.metadata.alt"
|
||||
:reverse="index % 2 !== 0" />
|
||||
</section>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -3,30 +3,56 @@ import LearningResourceWrapper, {
|
|||
type ResourceButton,
|
||||
} from "@/components/molecules/LearningResourceWrapper.vue";
|
||||
import { useLocale, type CompileLocale } from "@/i18n";
|
||||
import type { Locale } from "@/i18n/locale";
|
||||
import type * as i18n from "@/i18n/locale";
|
||||
import { ignore } from "@/utils/ignore";
|
||||
import { computed } from "vue";
|
||||
import discordImg from "@/assets/discord.png";
|
||||
|
||||
interface ResourceConfig {
|
||||
id: keyof i18n.Locale["resources"]["resources"];
|
||||
image?: keyof typeof imagesI18n.value;
|
||||
}
|
||||
|
||||
const RESOURCE_CONFIGS = [
|
||||
{ id: "discord", image: "discordLogo" },
|
||||
] as const satisfies ResourceConfig[];
|
||||
|
||||
const locale = useLocale();
|
||||
|
||||
const resourceIdToResource = computed(() => locale.value.resources.resources);
|
||||
const pageI18n = computed(() => locale.value.resources);
|
||||
|
||||
const RESOURCE_ORDER = [
|
||||
"discord",
|
||||
] as const satisfies (keyof Locale["resources"]["resources"])[];
|
||||
interface ImageI18n {
|
||||
src: string;
|
||||
metadata: i18n.Image;
|
||||
}
|
||||
|
||||
const resources = computed(() =>
|
||||
RESOURCE_ORDER.map((id) => [id, resourceIdToResource.value[id]] as const),
|
||||
const imagesI18n = computed(() => {
|
||||
const imagesI18n = pageI18n.value.images;
|
||||
|
||||
return {
|
||||
discordLogo: { src: discordImg, metadata: imagesI18n.discordLogo },
|
||||
} as const satisfies Record<string, ImageI18n>;
|
||||
});
|
||||
|
||||
const resourcesI18n = computed(() =>
|
||||
RESOURCE_CONFIGS.map(
|
||||
({ id, image }: ResourceConfig) =>
|
||||
({
|
||||
id,
|
||||
text: pageI18n.value.resources[id],
|
||||
image: image && imagesI18n.value[image],
|
||||
}) as const,
|
||||
),
|
||||
);
|
||||
|
||||
const computeButtons = (
|
||||
id: keyof CompileLocale<Locale>["resources"]["resources"],
|
||||
id: keyof CompileLocale<i18n.Locale>["resources"]["resources"],
|
||||
): ResourceButton[] => {
|
||||
// will warn us if a new variant is added that isn't handled, and so we should add a switch
|
||||
// once we have a switch statement, this won't be needed as that will check for exhaustiveness
|
||||
ignore<"discord">(id);
|
||||
|
||||
const buttons = resourceIdToResource.value[id].buttons;
|
||||
const buttons = pageI18n.value.resources[id].buttons;
|
||||
return [
|
||||
{
|
||||
link: {
|
||||
|
|
@ -58,13 +84,18 @@ const computeButtons = (
|
|||
|
||||
<section class="section container">
|
||||
<LearningResourceWrapper
|
||||
v-for="([resourceId, resource], index) in resources"
|
||||
v-for="(resourceI18n, index) in resourcesI18n"
|
||||
:key="index"
|
||||
:title="resource.title"
|
||||
:subtitle="resource.subtitle"
|
||||
:desc="resource.desc"
|
||||
:image="resource.image ?? undefined"
|
||||
:buttons="computeButtons(resourceId)" />
|
||||
:title="resourceI18n.text.title"
|
||||
:subtitle="resourceI18n.text.subtitle"
|
||||
:desc="resourceI18n.text.desc"
|
||||
:image="
|
||||
resourceI18n.image && {
|
||||
src: resourceI18n.image.src,
|
||||
alt: resourceI18n.image.metadata.alt,
|
||||
}
|
||||
"
|
||||
:buttons="computeButtons(resourceI18n.id)" />
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue