feat: extract shop from mp/shop — initial libreshop/shop
Some checks failed
Build and publish / build (push) Failing after 19s
Some checks failed
Build and publish / build (push) Failing after 19s
Source moved verbatim from mp/shop/ on 2026-04-29; mp was the first concrete adapter consuming the libreshop toolkit. Builds and publishes git.librete.ch/libreshop/shop on every main / v* push via the standard .gitea/workflows/build.yml shared across libreshop components.
This commit is contained in:
62
components/FeatureModule.vue
Normal file
62
components/FeatureModule.vue
Normal file
@@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<section class="feature-module py-16 lg:py-24">
|
||||
<div class="xl:container mx-auto px-6">
|
||||
<div class="grid lg:grid-cols-2 gap-8 lg:gap-16 items-center">
|
||||
<!-- Image -->
|
||||
<div :class="imageRight ? 'lg:order-2' : ''">
|
||||
<img
|
||||
v-if="image"
|
||||
:src="image"
|
||||
:alt="headline"
|
||||
class="rounded-xl shadow-md w-full object-cover aspect-[4/3]"
|
||||
loading="lazy"
|
||||
/>
|
||||
<div v-else class="rounded-xl bg-gray-200 w-full aspect-[4/3] flex items-center justify-center">
|
||||
<span class="text-gray-400 text-sm">Bild folgt</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Content -->
|
||||
<div class="lg:w-4/5">
|
||||
<p v-if="eyebrow" class="text-sm uppercase tracking-widest opacity-60 mb-2">
|
||||
{{ eyebrow }}
|
||||
</p>
|
||||
<h2 class="font-display text-3xl lg:text-4xl font-bold mb-4">
|
||||
{{ headline }}
|
||||
</h2>
|
||||
<p v-if="subtitle" class="text-xl lg:text-2xl opacity-80 mb-6">
|
||||
{{ subtitle }}
|
||||
</p>
|
||||
<div class="prose prose-lg max-w-none text-gray-700 leading-relaxed" v-html="formattedBody"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const props = defineProps<{
|
||||
eyebrow?: string;
|
||||
headline: string;
|
||||
subtitle?: string;
|
||||
body: string;
|
||||
image?: string;
|
||||
imageRight?: boolean;
|
||||
}>();
|
||||
|
||||
// Clean up multi-line template strings (remove extra whitespace from indentation)
|
||||
const formattedBody = computed(() => {
|
||||
return props.body
|
||||
.split("\n")
|
||||
.map((line) => line.trim())
|
||||
.join(" ")
|
||||
.replace(/\s+/g, " ")
|
||||
.trim();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.feature-module :deep(strong) {
|
||||
@apply font-semibold text-gray-900;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user