@4verburga/alpine-spanishplus 1.6.6

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.
@@ -0,0 +1,49 @@
1
+ <template>
2
+ <section>
3
+ <p class="message">
4
+ This page could not be found
5
+ </p>
6
+
7
+ <p class="status">
8
+ 404
9
+ </p>
10
+
11
+ <NuxtLink to="/">
12
+ Back to homepage
13
+ <Icon name="ph:arrow-right" />
14
+ </NuxtLink>
15
+ </section>
16
+ </template>
17
+
18
+ <style scoped lang="ts">
19
+ css({
20
+ section: {
21
+ minHeight: '50vh',
22
+ display: 'flex',
23
+ flexDirection: 'column',
24
+ alignItems: 'center',
25
+ justifyContent: 'center',
26
+ '.message': {
27
+ fontSize: '{text.3xl.fontSize}',
28
+ lineHeight: '{text.3xl.lineHeight}',
29
+ fontWeight: '{fontWeight.medium}',
30
+ },
31
+ '.status': {
32
+ text: '6xl',
33
+ my: '{space.12}'
34
+ },
35
+ a: {
36
+ position: 'relative',
37
+ display: 'flex',
38
+ alignItems: 'center',
39
+ '.icon': {
40
+ position: 'absolute',
41
+ right: 'calc(0px - {space.1})',
42
+ width: '{space.5}',
43
+ height: '{space.5}',
44
+ transform: 'translateX(100%)',
45
+ },
46
+ }
47
+ }
48
+ })
49
+ </style>
@@ -0,0 +1,64 @@
1
+ <script lang="ts" setup>
2
+ const { navigation } = useContent()
3
+
4
+ const emits = defineEmits(['linkClick'])
5
+
6
+ function handleClick() {
7
+ emits('linkClick')
8
+ }
9
+ </script>
10
+
11
+ <template>
12
+ <nav>
13
+ <ul>
14
+ <li
15
+ v-for="link of navigation"
16
+ :key="link._path"
17
+ >
18
+ <NuxtLink
19
+ :to="link._path"
20
+ @click="handleClick"
21
+ >
22
+ <span class="underline-fx" />
23
+ {{ link.title }}
24
+ </NuxtLink>
25
+ </li>
26
+ </ul>
27
+ </nav>
28
+ </template>
29
+
30
+ <style scoped lang="ts">
31
+ css({
32
+ nav: {
33
+ ul: {
34
+ display: 'flex',
35
+ flexDirection: 'column',
36
+ width: '100%',
37
+ justifyContent: 'center',
38
+ gap: '{space.4}',
39
+ '@sm': {
40
+ flexDirection: 'row',
41
+ gap: '{space.8}',
42
+ },
43
+ a: {
44
+ position: 'relative',
45
+ '&.router-link-active': {
46
+ color: '{color.primary.500}'
47
+ },
48
+ '.underline-fx': {
49
+ position: 'absolute',
50
+ bottom: '-4px',
51
+ width: 0,
52
+ height: '1px',
53
+ backgroundColor: 'currentColor',
54
+ transition: 'width 200ms ease-in-out',
55
+ 'a:hover &&': {
56
+ width: '100%'
57
+ }
58
+ }
59
+ },
60
+
61
+ }
62
+ }
63
+ })
64
+ </style>
@@ -0,0 +1,70 @@
1
+ <script setup lang="ts">
2
+ const props = defineProps({
3
+ socials: {
4
+ type: Object,
5
+ default: () => {}
6
+ }
7
+ })
8
+
9
+ const builtInSocials = ['twitter', 'facebook', 'instagram', 'youtube', 'github', 'medium']
10
+
11
+ const icons = computed<any>(() => {
12
+ return Object.entries(props.socials)
13
+ .map(([key, value]) => {
14
+ if (typeof value === 'object') {
15
+ return value
16
+ } else if (typeof value === 'string' && value && builtInSocials.includes(key)) {
17
+ return {
18
+ href: `https://${key}.com/${value}`,
19
+ icon: `uil:${key}`,
20
+ label: value
21
+ }
22
+ } else {
23
+ return null
24
+ }
25
+ })
26
+ .filter(Boolean)
27
+ })
28
+
29
+ const getRel = (icon:any) => {
30
+ const base = ['noopener', 'noreferrer']
31
+ if (icon.rel) {
32
+ base.push(icon.rel)
33
+ }
34
+ return base.join(' ')
35
+ }
36
+ </script>
37
+
38
+ <template>
39
+ <!-- eslint-disable-next-line vue/no-multiple-template-root -->
40
+ <NuxtLink
41
+ v-for="icon in icons"
42
+ :key="icon.label"
43
+ :rel="getRel(icon)"
44
+ :title="icon.label"
45
+ :aria-label="icon.label"
46
+ :href="icon.href"
47
+ target="_blank"
48
+ >
49
+ <Icon v-if="icon.icon" :name="icon.icon" />
50
+ </NuxtLink>
51
+ </template>
52
+
53
+ <style scoped lang="ts">
54
+ css({
55
+ a: {
56
+ '--social-icon-size': '24px',
57
+ width: 'var(--social-icon-size)',
58
+ height: 'var(--social-icon-size)',
59
+ display: 'flex',
60
+ ':hover': {
61
+ // TODO: why prose links is not yellow, should be primary
62
+ color: '{color.primary.500}',
63
+ },
64
+ svg: {
65
+ width: '100%',
66
+ height: '100%',
67
+ }
68
+ }
69
+ })
70
+ </style>
@@ -0,0 +1,122 @@
1
+ <script setup lang="ts">
2
+ import { useContentPreview } from '#imports'
3
+
4
+ type Article = {
5
+ _path: string
6
+ title: string
7
+ date: string
8
+ description: string
9
+ badges?: { bg: string, text: string, content: string }[]
10
+ }
11
+
12
+ const props = defineProps({
13
+ article: {
14
+ type: Object,
15
+ required: true,
16
+ validator: (value: Article) => {
17
+ if (value?._path && value.title) { return true }
18
+ return false
19
+ }
20
+ },
21
+ featured: {
22
+ type: Boolean,
23
+ default: false
24
+ }
25
+ })
26
+
27
+ const id = computed(() => {
28
+ // @ts-ignore
29
+ return (process.dev || useContentPreview()?.isEnabled()) ? props.article?._id : undefined
30
+ })
31
+ </script>
32
+
33
+ <template>
34
+ <article
35
+ v-if="article._path && article.title"
36
+ :class="{ 'layout': featured }"
37
+
38
+ >
39
+ <div class="content">
40
+ <NuxtLink
41
+ :to="article._path"
42
+ class="headline"
43
+ >
44
+ <h1>
45
+ {{ article.title }}
46
+ </h1>
47
+ </NuxtLink>
48
+ <time>
49
+ {{ formatDate(article.date) }}
50
+ </time>
51
+ </div>
52
+ </article>
53
+ </template>
54
+
55
+ <style scoped lang="ts">
56
+ css({
57
+ article: {
58
+ display: 'flex',
59
+ flexDirection: 'column',
60
+ gap: '{space.4}',
61
+ '&.featured': {
62
+ '@md': {
63
+ flexDirection: 'row',
64
+ gap: '{space.8}',
65
+ }
66
+ },
67
+ img: {
68
+ width: '100%',
69
+ aspectRatio: '16 / 9',
70
+ objectFit: 'cover',
71
+ borderRadius: '{radii.md}',
72
+ },
73
+ '.image': {
74
+ flex: 1,
75
+ div: {
76
+ position: 'absolute',
77
+ display: 'flex',
78
+ flexWrap: true,
79
+ gap: '{space.2}',
80
+ marginTop: '{space.2}',
81
+ marginLeft: '{space.2}',
82
+ span: {
83
+ padding: '{space.1}',
84
+ borderRadius: '{radii.sm}',
85
+ text: 'xs',
86
+ fontWeight: 700
87
+ }
88
+ }
89
+ },
90
+ '.content': {
91
+ display: 'flex',
92
+ flexDirection: 'column',
93
+ flex: 1,
94
+ '.headline': {
95
+ text: '2xl',
96
+ marginBottom: '{space.2}',
97
+ fontWeight: '{fontWeight.semibold}',
98
+ lineClamp: 2,
99
+ '.featured &&': {
100
+ text: '4xl',
101
+ lineClamp: 3,
102
+ },
103
+ },
104
+ '.description': {
105
+ marginBottom: '{space.4}',
106
+ lineClamp: 2,
107
+ '.featured &&': {
108
+ lineClamp: 4,
109
+ }
110
+ },
111
+ time: {
112
+ text: 'sm',
113
+ // TODO: add secondary color token
114
+ color: '{color.gray.500}',
115
+ '@dark': {
116
+ color: '{color.gray.500}',
117
+ }
118
+ }
119
+ },
120
+ }
121
+ })
122
+ </style>
@@ -0,0 +1,70 @@
1
+ <script setup lang="ts">
2
+ import { withTrailingSlash } from 'ufo'
3
+ import ArticleIndexEntry from './ArticleIndexEntry.vue';
4
+
5
+ const props = defineProps({
6
+ path: {
7
+ type: String,
8
+ default: 'articles'
9
+ }
10
+ })
11
+
12
+ // @ts-ignore
13
+ const { data: _articles } = await useAsyncData(props.path, async () => await queryContent(withTrailingSlash(props.path)).sort({ date: -1 }).find())
14
+
15
+ // create new fields year and month
16
+ // const articles = computed(() => _articles.value || [])
17
+ const articles = computed(() => _articles.value.map((article) => {
18
+ const date = new Date(article.date)
19
+ article.year = date.getFullYear()
20
+ article.month = date.getMonth()
21
+ return article
22
+ }) || [])
23
+
24
+
25
+ </script>
26
+
27
+ <template>
28
+ <!-- TODO: group the outputs of article._path on each year and month -->
29
+ <ArticleIndexEntry v-for="(article, index) in articles" :key="index" :article="article" />
30
+ <!-- <d>DEBUG: articulo de indice 0 {{articles[0]}}</d> -->
31
+
32
+ </template>
33
+
34
+ <style scoped lang="ts">
35
+ css({
36
+ '.articles-list': {
37
+ '@sm': {
38
+ px: '{space.12}',
39
+ },
40
+ '@md': {
41
+ px: 0,
42
+ },
43
+ '.featured': {
44
+ my: '{space.12}',
45
+ '@md': {
46
+ my: '{space.8}',
47
+ }
48
+ },
49
+ '.layout': {
50
+ display: 'grid',
51
+ gridTemplateColumns: 'repeat(1, minmax(0, 1fr))',
52
+ gap: '{space.12}',
53
+ '@md': {
54
+ gridTemplateColumns: 'repeat(2, minmax(0, 1fr))',
55
+ gap: '{space.8}',
56
+ },
57
+ '@lg': {
58
+ gridTemplateColumns: 'repeat(3, minmax(0, 1fr))',
59
+ },
60
+ }
61
+ },
62
+ '.tour': {
63
+ minHeight: '30vh',
64
+ display: 'flex',
65
+ flexDirection: 'column',
66
+ alignItems: 'center',
67
+ justifyContent: 'center',
68
+ }
69
+ })
70
+ </style>
@@ -0,0 +1,72 @@
1
+ <script setup lang="ts">
2
+ import { withTrailingSlash } from 'ufo'
3
+
4
+ const props = defineProps({
5
+ path: {
6
+ type: String,
7
+ default: 'articles'
8
+ }
9
+ })
10
+
11
+ // @ts-ignore
12
+ const { data: _articles } = await useAsyncData(props.path, async () => await queryContent(withTrailingSlash(props.path)).sort({ date: -1 }).find())
13
+
14
+ const articles = computed(() => _articles.value || [])
15
+ </script>
16
+
17
+ <template>
18
+ <div v-if="articles?.length" class="articles-list">
19
+ <div class="featured">
20
+ <ArticlesListItem :article="articles[0]" :featured="true" />
21
+ </div>
22
+ <div class="layout">
23
+ <ArticlesListItem v-for="(article, index) in articles.slice(1)" :key="index" :article="article" />
24
+ </div>
25
+ </div>
26
+ <div v-else class="tour">
27
+ <p>Seems like there are no articles yet.</p>
28
+ <p>
29
+ You can start by
30
+ <!-- eslint-disable-next-line -->
31
+ <ProseA href="https://alpine.nuxt.space/articles/write-articles">creating</ProseA> one in the <ProseCodeInline>articles</ProseCodeInline> folder.
32
+ </p>
33
+ </div>
34
+ </template>
35
+
36
+ <style scoped lang="ts">
37
+ css({
38
+ '.articles-list': {
39
+ '@sm': {
40
+ px: '{space.12}',
41
+ },
42
+ '@md': {
43
+ px: 0,
44
+ },
45
+ '.featured': {
46
+ my: '{space.12}',
47
+ '@md': {
48
+ my: '{space.8}',
49
+ }
50
+ },
51
+ '.layout': {
52
+ display: 'grid',
53
+ gridTemplateColumns: 'repeat(1, minmax(0, 1fr))',
54
+ gap: '{space.12}',
55
+ '@md': {
56
+ gridTemplateColumns: 'repeat(2, minmax(0, 1fr))',
57
+ gap: '{space.8}',
58
+ },
59
+ '@lg': {
60
+ gridTemplateColumns: 'repeat(3, minmax(0, 1fr))',
61
+ },
62
+ }
63
+ },
64
+ '.tour': {
65
+ minHeight: '30vh',
66
+ display: 'flex',
67
+ flexDirection: 'column',
68
+ alignItems: 'center',
69
+ justifyContent: 'center',
70
+ }
71
+ })
72
+ </style>
@@ -0,0 +1,149 @@
1
+ <script setup lang="ts">
2
+ import { useContentPreview } from '#imports'
3
+
4
+ type Article = {
5
+ _path: string
6
+ title: string
7
+ date: string
8
+ description: string
9
+ badges?: { bg: string, text: string, content: string }[]
10
+ }
11
+
12
+ const props = defineProps({
13
+ article: {
14
+ type: Object,
15
+ required: true,
16
+ validator: (value: Article) => {
17
+ if (value?._path && value.title) { return true }
18
+ return false
19
+ }
20
+ },
21
+ featured: {
22
+ type: Boolean,
23
+ default: false
24
+ }
25
+ })
26
+
27
+ const id = computed(() => {
28
+ // @ts-ignore
29
+ return (process.dev || useContentPreview()?.isEnabled()) ? props.article?._id : undefined
30
+ })
31
+ </script>
32
+
33
+ <template>
34
+ <article
35
+ v-if="article._path && article.title"
36
+ :class="{ 'featured': featured }"
37
+ :data-content-id="id"
38
+ >
39
+ <div v-if="article.cover" class="image">
40
+ <div v-if="article?.badges">
41
+ <span
42
+ v-for="(badge, index) in article.badges"
43
+ :key="index"
44
+ :style="{
45
+ backgroundColor: badge?.bg || 'rgba(0, 0, 0, 0.3)',
46
+ color: badge?.color || 'white'
47
+ }"
48
+ >
49
+ {{ typeof badge === 'string' ? badge : badge.content }}
50
+ </span>
51
+ </div>
52
+ <NuxtLink :to="article._path">
53
+ <NuxtImg
54
+ :src="article.cover"
55
+ :alt="article.title"
56
+ width="16"
57
+ height="9"
58
+ />
59
+ </NuxtLink>
60
+ </div>
61
+
62
+ <div class="content">
63
+ <NuxtLink
64
+ :to="article._path"
65
+ class="headline"
66
+ >
67
+ <h1>
68
+ {{ article.title }}
69
+ </h1>
70
+ </NuxtLink>
71
+
72
+ <p class="description">
73
+ {{ article.description }}
74
+ </p>
75
+ <time>
76
+ {{ formatDate(article.date) }}
77
+ </time>
78
+ </div>
79
+ </article>
80
+ </template>
81
+
82
+ <style scoped lang="ts">
83
+ css({
84
+ article: {
85
+ display: 'flex',
86
+ flexDirection: 'column',
87
+ gap: '{space.4}',
88
+ '&.featured': {
89
+ '@md': {
90
+ flexDirection: 'row',
91
+ gap: '{space.8}',
92
+ }
93
+ },
94
+ img: {
95
+ width: '100%',
96
+ aspectRatio: '16 / 9',
97
+ objectFit: 'cover',
98
+ borderRadius: '{radii.md}',
99
+ },
100
+ '.image': {
101
+ flex: 1,
102
+ div: {
103
+ position: 'absolute',
104
+ display: 'flex',
105
+ flexWrap: true,
106
+ gap: '{space.2}',
107
+ marginTop: '{space.2}',
108
+ marginLeft: '{space.2}',
109
+ span: {
110
+ padding: '{space.1}',
111
+ borderRadius: '{radii.sm}',
112
+ text: 'xs',
113
+ fontWeight: 700
114
+ }
115
+ }
116
+ },
117
+ '.content': {
118
+ display: 'flex',
119
+ flexDirection: 'column',
120
+ flex: 1,
121
+ '.headline': {
122
+ text: '2xl',
123
+ marginBottom: '{space.2}',
124
+ fontWeight: '{fontWeight.semibold}',
125
+ lineClamp: 2,
126
+ '.featured &&': {
127
+ text: '4xl',
128
+ lineClamp: 3,
129
+ },
130
+ },
131
+ '.description': {
132
+ marginBottom: '{space.4}',
133
+ lineClamp: 2,
134
+ '.featured &&': {
135
+ lineClamp: 4,
136
+ }
137
+ },
138
+ time: {
139
+ text: 'sm',
140
+ // TODO: add secondary color token
141
+ color: '{color.gray.500}',
142
+ '@dark': {
143
+ color: '{color.gray.500}',
144
+ }
145
+ }
146
+ },
147
+ }
148
+ })
149
+ </style>