@sugarat/theme 0.5.10 → 0.5.12-beta.0
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/node.d.ts +2 -4
- package/node.js +206 -156
- package/node.mjs +205 -154
- package/package.json +10 -14
- package/src/components/Alert.vue +308 -0
- package/src/components/Avatar.vue +66 -0
- package/src/components/BlogAlert.vue +4 -4
- package/src/components/BlogApp.vue +4 -0
- package/src/components/BlogArticleAnalyze.vue +70 -32
- package/src/components/BlogButtonAfterArticle.vue +4 -4
- package/src/components/BlogFriendLink.vue +54 -27
- package/src/components/BlogHomeInfo.vue +1 -3
- package/src/components/BlogHomeTags.vue +8 -14
- package/src/components/BlogHotArticle.vue +3 -3
- package/src/components/BlogImagePreview.vue +3 -3
- package/src/components/BlogList.vue +7 -8
- package/src/components/BlogRecommendArticle.vue +3 -3
- package/src/components/Button.vue +165 -0
- package/src/components/Carousel.vue +254 -0
- package/src/components/CarouselItem.vue +154 -0
- package/src/components/Image.vue +34 -0
- package/src/components/ImageViewer.vue +406 -0
- package/src/components/Pagination.vue +397 -0
- package/src/components/Tag.vue +163 -0
- package/src/components/UserWorks.vue +19 -22
- package/src/composables/config/blog.ts +6 -1
- package/src/composables/config/index.ts +2 -2
- package/src/index.ts +10 -17
- package/src/node.ts +0 -3
- package/src/styles/el-base.css +340 -0
- package/src/utils/client/index.ts +17 -0
- package/src/utils/node/mdPlugins.ts +1 -1
- package/src/utils/node/theme.ts +5 -2
- package/src/utils/node/vitePlugins.ts +51 -18
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { computed, onMounted, onUnmounted, provide, ref, watch } from 'vue'
|
|
3
|
+
|
|
4
|
+
const props = defineProps({
|
|
5
|
+
height: { type: String, default: '300px' },
|
|
6
|
+
initialIndex: { type: Number, default: 0 },
|
|
7
|
+
trigger: { type: String, default: 'hover' },
|
|
8
|
+
autoplay: { type: Boolean, default: true },
|
|
9
|
+
interval: { type: Number, default: 3000 },
|
|
10
|
+
indicatorPosition: { type: String, default: '' },
|
|
11
|
+
arrow: { type: String, default: 'hover' },
|
|
12
|
+
type: { type: String, default: '' },
|
|
13
|
+
loop: { type: Boolean, default: true },
|
|
14
|
+
direction: { type: String, default: 'horizontal' },
|
|
15
|
+
pauseOnHover: { type: Boolean, default: true },
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
const activeIndex = ref(props.initialIndex)
|
|
19
|
+
const items = ref<any[]>([])
|
|
20
|
+
const timer = ref<any>(null)
|
|
21
|
+
|
|
22
|
+
function addItem(item: any) {
|
|
23
|
+
items.value.push(item)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function removeItem(uid: number) {
|
|
27
|
+
const index = items.value.findIndex(item => item.uid === uid)
|
|
28
|
+
if (index > -1)
|
|
29
|
+
items.value.splice(index, 1)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
provide('carousel', {
|
|
33
|
+
addItem,
|
|
34
|
+
removeItem,
|
|
35
|
+
activeIndex,
|
|
36
|
+
items,
|
|
37
|
+
type: computed(() => props.type),
|
|
38
|
+
direction: computed(() => props.direction),
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
function startTimer() {
|
|
42
|
+
if (typeof window === 'undefined')
|
|
43
|
+
return
|
|
44
|
+
if (props.interval <= 0 || !props.autoplay || timer.value)
|
|
45
|
+
return
|
|
46
|
+
timer.value = setInterval(playSlides, props.interval)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function pauseTimer() {
|
|
50
|
+
if (timer.value) {
|
|
51
|
+
clearInterval(timer.value)
|
|
52
|
+
timer.value = null
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function playSlides() {
|
|
57
|
+
if (activeIndex.value < items.value.length - 1) {
|
|
58
|
+
activeIndex.value++
|
|
59
|
+
}
|
|
60
|
+
else if (props.loop) {
|
|
61
|
+
activeIndex.value = 0
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function setActiveItem(index: number) {
|
|
66
|
+
activeIndex.value = index
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function prev() {
|
|
70
|
+
setActiveItem(activeIndex.value > 0 ? activeIndex.value - 1 : items.value.length - 1)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function next() {
|
|
74
|
+
setActiveItem(activeIndex.value < items.value.length - 1 ? activeIndex.value + 1 : 0)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function handleIndicatorClick(index: number) {
|
|
78
|
+
activeIndex.value = index
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const hover = ref(false)
|
|
82
|
+
|
|
83
|
+
function handleMouseEnter() {
|
|
84
|
+
hover.value = true
|
|
85
|
+
if (props.pauseOnHover)
|
|
86
|
+
pauseTimer()
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function handleMouseLeave() {
|
|
90
|
+
hover.value = false
|
|
91
|
+
if (props.pauseOnHover)
|
|
92
|
+
startTimer()
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
watch(() => props.autoplay, (val) => {
|
|
96
|
+
val ? startTimer() : pauseTimer()
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
onMounted(() => {
|
|
100
|
+
startTimer()
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
onUnmounted(() => {
|
|
104
|
+
pauseTimer()
|
|
105
|
+
})
|
|
106
|
+
</script>
|
|
107
|
+
|
|
108
|
+
<template>
|
|
109
|
+
<div
|
|
110
|
+
class="sugar-carousel"
|
|
111
|
+
:class="{ 'sugar-carousel--card': type === 'card' }"
|
|
112
|
+
@mouseenter="handleMouseEnter"
|
|
113
|
+
@mouseleave="handleMouseLeave"
|
|
114
|
+
>
|
|
115
|
+
<div class="sugar-carousel__container" :style="{ height }">
|
|
116
|
+
<slot />
|
|
117
|
+
<transition name="carousel-arrow-left">
|
|
118
|
+
<button
|
|
119
|
+
v-if="arrow !== 'never' && items.length > 1"
|
|
120
|
+
v-show="(arrow === 'always' || hover) && (loop || activeIndex > 0)"
|
|
121
|
+
type="button"
|
|
122
|
+
class="sugar-carousel__arrow sugar-carousel__arrow--left"
|
|
123
|
+
@click.stop="prev"
|
|
124
|
+
>
|
|
125
|
+
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em"><path fill="currentColor" d="M609.408 149.376 277.76 489.6a32 32 0 0 0 0 44.672l331.648 340.352a29.12 29.12 0 0 0 41.728 0 30.592 30.592 0 0 0 0-42.752L339.264 511.936l311.872-319.872a30.592 30.592 0 0 0 0-42.688 29.12 29.12 0 0 0-41.728 0z" /></svg>
|
|
126
|
+
</button>
|
|
127
|
+
</transition>
|
|
128
|
+
<transition name="carousel-arrow-right">
|
|
129
|
+
<button
|
|
130
|
+
v-if="arrow !== 'never' && items.length > 1"
|
|
131
|
+
v-show="(arrow === 'always' || hover) && (loop || activeIndex < items.length - 1)"
|
|
132
|
+
type="button"
|
|
133
|
+
class="sugar-carousel__arrow sugar-carousel__arrow--right"
|
|
134
|
+
@click.stop="next"
|
|
135
|
+
>
|
|
136
|
+
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em"><path fill="currentColor" d="M340.864 149.312a30.592 30.592 0 0 0 0 42.752L652.736 512 340.864 831.872a30.592 30.592 0 0 0 0 42.752 29.12 29.12 0 0 0 41.728 0L714.24 534.336a32 32 0 0 0 0-44.672L382.592 149.376a29.12 29.12 0 0 0-41.728 0z" /></svg>
|
|
137
|
+
</button>
|
|
138
|
+
</transition>
|
|
139
|
+
</div>
|
|
140
|
+
|
|
141
|
+
<ul v-if="indicatorPosition !== 'none' && items.length > 1" class="sugar-carousel__indicators">
|
|
142
|
+
<li
|
|
143
|
+
v-for="(item, index) in items"
|
|
144
|
+
:key="index"
|
|
145
|
+
class="sugar-carousel__indicator"
|
|
146
|
+
:class="{ 'is-active': index === activeIndex }"
|
|
147
|
+
@mouseenter="trigger === 'hover' && setActiveItem(index)"
|
|
148
|
+
@click.stop="handleIndicatorClick(index)"
|
|
149
|
+
>
|
|
150
|
+
<button class="sugar-carousel__button" />
|
|
151
|
+
</li>
|
|
152
|
+
</ul>
|
|
153
|
+
</div>
|
|
154
|
+
</template>
|
|
155
|
+
|
|
156
|
+
<style lang="scss" scoped>
|
|
157
|
+
.sugar-carousel {
|
|
158
|
+
position: relative;
|
|
159
|
+
overflow: hidden; // simplified
|
|
160
|
+
&:hover {
|
|
161
|
+
.sugar-carousel__arrow {
|
|
162
|
+
display: inline-flex;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.sugar-carousel__container {
|
|
168
|
+
position: relative;
|
|
169
|
+
height: 300px;
|
|
170
|
+
overflow: hidden;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.sugar-carousel__indicators {
|
|
174
|
+
position: absolute;
|
|
175
|
+
list-style: none;
|
|
176
|
+
bottom: 0;
|
|
177
|
+
left: 50%;
|
|
178
|
+
transform: translateX(-50%);
|
|
179
|
+
margin: 0;
|
|
180
|
+
padding: 0;
|
|
181
|
+
z-index: 20;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
.sugar-carousel__indicator {
|
|
185
|
+
display: inline-block;
|
|
186
|
+
background-color: transparent;
|
|
187
|
+
padding: 12px 4px;
|
|
188
|
+
cursor: pointer;
|
|
189
|
+
|
|
190
|
+
&.is-active button {
|
|
191
|
+
opacity: 1;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.sugar-carousel__button {
|
|
196
|
+
display: block;
|
|
197
|
+
opacity: 0.48;
|
|
198
|
+
width: 30px;
|
|
199
|
+
height: 2px;
|
|
200
|
+
background-color: #d3dce6; // element-plus default like
|
|
201
|
+
border: none;
|
|
202
|
+
outline: none;
|
|
203
|
+
padding: 0;
|
|
204
|
+
margin: 0;
|
|
205
|
+
cursor: pointer;
|
|
206
|
+
transition: .3s;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.sugar-carousel__arrow {
|
|
210
|
+
border: none;
|
|
211
|
+
outline: none;
|
|
212
|
+
padding: 0;
|
|
213
|
+
margin: 0;
|
|
214
|
+
height: 36px;
|
|
215
|
+
width: 36px;
|
|
216
|
+
cursor: pointer;
|
|
217
|
+
transition: .3s;
|
|
218
|
+
border-radius: 50%;
|
|
219
|
+
background-color: rgba(31, 45, 61, .11);
|
|
220
|
+
color: #fff;
|
|
221
|
+
position: absolute;
|
|
222
|
+
top: 50%;
|
|
223
|
+
z-index: 20;
|
|
224
|
+
transform: translateY(-50%);
|
|
225
|
+
text-align: center;
|
|
226
|
+
font-size: 12px;
|
|
227
|
+
display: inline-flex;
|
|
228
|
+
justify-content: center;
|
|
229
|
+
align-items: center;
|
|
230
|
+
|
|
231
|
+
&:hover {
|
|
232
|
+
background-color: rgba(31, 45, 61, .23);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
&--left {
|
|
236
|
+
left: 16px;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
&--right {
|
|
240
|
+
right: 16px;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
.carousel-arrow-left-enter-from,
|
|
244
|
+
.carousel-arrow-left-leave-to {
|
|
245
|
+
opacity: 0;
|
|
246
|
+
transform: translateY(-50%) translateX(-10px);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
.carousel-arrow-right-enter-from,
|
|
250
|
+
.carousel-arrow-right-leave-to {
|
|
251
|
+
opacity: 0;
|
|
252
|
+
transform: translateY(-50%) translateX(10px);
|
|
253
|
+
}
|
|
254
|
+
</style>
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { computed, getCurrentInstance, inject, onMounted, onUnmounted } from 'vue'
|
|
3
|
+
|
|
4
|
+
const instance = getCurrentInstance()
|
|
5
|
+
const uid = instance?.uid
|
|
6
|
+
|
|
7
|
+
const carousel = inject('carousel') as any
|
|
8
|
+
const { activeIndex, items, type, addItem, removeItem } = carousel
|
|
9
|
+
|
|
10
|
+
const index = computed(() => {
|
|
11
|
+
return items.value.findIndex((item: any) => item.uid === uid)
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
const isActive = computed(() => index.value === activeIndex.value)
|
|
15
|
+
|
|
16
|
+
// Card logic
|
|
17
|
+
const CARD_SCALE = 0.83
|
|
18
|
+
|
|
19
|
+
const cardStyle = computed(() => {
|
|
20
|
+
if (type.value !== 'card') {
|
|
21
|
+
// Normal mode: simplified translate
|
|
22
|
+
// const isPrev = index.value === activeIndex.value - 1 || (activeIndex.value === 0 && index.value === items.value.length - 1)
|
|
23
|
+
// const isNext = index.value === activeIndex.value + 1 || (activeIndex.value === items.value.length - 1 && index.value === 0)
|
|
24
|
+
|
|
25
|
+
// Simple show/hide for normal mode or use CSS transition
|
|
26
|
+
// Better: use transform
|
|
27
|
+
// const offset = index.value - activeIndex.value
|
|
28
|
+
// Handle loop wrap
|
|
29
|
+
// ... too complex to implement full logic in one go.
|
|
30
|
+
// Let's rely on simple absolute positioning and z-index for normal fade/slide
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
transform: `translateX(${(index.value - activeIndex.value) * 100}%)`,
|
|
34
|
+
zIndex: isActive.value ? 2 : 1,
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Card Mode
|
|
39
|
+
// const parentWidth = instance?.parent?.vnode?.el?.offsetWidth || 0 // Need parent width
|
|
40
|
+
// Actually simpler:
|
|
41
|
+
// Active: translate(0) scale(1)
|
|
42
|
+
// Prev: translate(-50%) scale(0.83)
|
|
43
|
+
// Next: translate(50%) scale(0.83)
|
|
44
|
+
// Others: hidden or further
|
|
45
|
+
|
|
46
|
+
const active = activeIndex.value
|
|
47
|
+
const count = items.value.length
|
|
48
|
+
const idx = index.value
|
|
49
|
+
|
|
50
|
+
// Logic from Element Plus (simplified)
|
|
51
|
+
const processIndex = (index: number, activeIndex: number, length: number) => {
|
|
52
|
+
if (activeIndex === 0 && index === length - 1)
|
|
53
|
+
return -1
|
|
54
|
+
if (activeIndex === length - 1 && index === 0)
|
|
55
|
+
return length
|
|
56
|
+
if (index < activeIndex - 1 && activeIndex - index >= length / 2)
|
|
57
|
+
return length + 1
|
|
58
|
+
if (index > activeIndex + 1 && index - activeIndex >= length / 2)
|
|
59
|
+
return -2
|
|
60
|
+
return index
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const calculatedIndex = processIndex(idx, active, count)
|
|
64
|
+
|
|
65
|
+
const inStage = Math.round(Math.abs(calculatedIndex - active)) <= 1
|
|
66
|
+
|
|
67
|
+
const translate = (() => {
|
|
68
|
+
if (calculatedIndex === active)
|
|
69
|
+
return 0
|
|
70
|
+
if (Math.abs(calculatedIndex - active) > 1)
|
|
71
|
+
return 0 // Should be hidden really
|
|
72
|
+
return calculatedIndex > active ? '50%' : '-50%'
|
|
73
|
+
})()
|
|
74
|
+
|
|
75
|
+
const scale = calculatedIndex === active ? 1 : CARD_SCALE
|
|
76
|
+
const zIndex = calculatedIndex === active ? 10 : 0 // Simplified
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
transform: `translateX(${translate}) scale(${scale})`,
|
|
80
|
+
zIndex,
|
|
81
|
+
opacity: inStage ? 1 : 0, // Hide others
|
|
82
|
+
display: inStage ? 'block' : 'none', // optimize
|
|
83
|
+
}
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
onMounted(() => {
|
|
87
|
+
addItem({ uid })
|
|
88
|
+
})
|
|
89
|
+
onUnmounted(() => {
|
|
90
|
+
removeItem(uid)
|
|
91
|
+
})
|
|
92
|
+
</script>
|
|
93
|
+
|
|
94
|
+
<template>
|
|
95
|
+
<div
|
|
96
|
+
v-if="type !== 'card' || cardStyle.display !== 'none'"
|
|
97
|
+
class="sugar-carousel__item"
|
|
98
|
+
:class="{
|
|
99
|
+
'is-active': isActive,
|
|
100
|
+
'is-in-stage': type === 'card' && cardStyle.opacity === 1,
|
|
101
|
+
'sugar-carousel__item--card': type === 'card',
|
|
102
|
+
}"
|
|
103
|
+
:style="cardStyle"
|
|
104
|
+
>
|
|
105
|
+
<div v-if="type === 'card'" v-show="!isActive" class="sugar-carousel__mask" />
|
|
106
|
+
<slot />
|
|
107
|
+
</div>
|
|
108
|
+
</template>
|
|
109
|
+
|
|
110
|
+
<style lang="scss" scoped>
|
|
111
|
+
.sugar-carousel__item {
|
|
112
|
+
position: absolute;
|
|
113
|
+
top: 0;
|
|
114
|
+
left: 0;
|
|
115
|
+
width: 100%;
|
|
116
|
+
height: 100%;
|
|
117
|
+
display: inline-block;
|
|
118
|
+
overflow: hidden;
|
|
119
|
+
z-index: 0;
|
|
120
|
+
transition: transform 0.4s ease-in-out;
|
|
121
|
+
background-color: transparent; // Default
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.sugar-carousel__item--card {
|
|
125
|
+
width: 50%;
|
|
126
|
+
left: 25%; // Center it initially?
|
|
127
|
+
// Actually if we use translateX 50% or -50%, it's relative to item width.
|
|
128
|
+
// Element Plus Card:
|
|
129
|
+
// Item width is 50%.
|
|
130
|
+
// Active item is at translateX(containerWidth/4).
|
|
131
|
+
// Prev item at translateX(0).
|
|
132
|
+
// Next item at translateX(containerWidth/2).
|
|
133
|
+
|
|
134
|
+
// My simplified CSS logic:
|
|
135
|
+
// left: 25% (centers the 50% width item in container)
|
|
136
|
+
// active: translate(0) -> centered
|
|
137
|
+
// prev: translate(-50% of item) -> -25% of container? No, -50% of 50% is -25% width.
|
|
138
|
+
// left 25% - 25% = 0% (Left aligned)
|
|
139
|
+
// next: translate(50% of item) -> +25% of container.
|
|
140
|
+
// left 25% + 25% = 50% (Right aligned)
|
|
141
|
+
// This works!
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.sugar-carousel__mask {
|
|
145
|
+
position: absolute;
|
|
146
|
+
width: 100%;
|
|
147
|
+
height: 100%;
|
|
148
|
+
top: 0;
|
|
149
|
+
left: 0;
|
|
150
|
+
background-color: #fff;
|
|
151
|
+
opacity: 0.24;
|
|
152
|
+
transition: .2s;
|
|
153
|
+
}
|
|
154
|
+
</style>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
defineProps({
|
|
3
|
+
src: { type: String, default: '' },
|
|
4
|
+
alt: { type: String, default: '' },
|
|
5
|
+
loading: { type: String, default: 'eager' },
|
|
6
|
+
})
|
|
7
|
+
</script>
|
|
8
|
+
|
|
9
|
+
<template>
|
|
10
|
+
<div class="sugar-image">
|
|
11
|
+
<img
|
|
12
|
+
:src="src"
|
|
13
|
+
:alt="alt"
|
|
14
|
+
:loading="loading as any"
|
|
15
|
+
class="sugar-image__inner"
|
|
16
|
+
>
|
|
17
|
+
</div>
|
|
18
|
+
</template>
|
|
19
|
+
|
|
20
|
+
<style lang="scss" scoped>
|
|
21
|
+
.sugar-image {
|
|
22
|
+
position: relative;
|
|
23
|
+
display: inline-block;
|
|
24
|
+
overflow: hidden;
|
|
25
|
+
|
|
26
|
+
&__inner {
|
|
27
|
+
width: 100%;
|
|
28
|
+
height: 100%;
|
|
29
|
+
vertical-align: top;
|
|
30
|
+
object-fit: inherit;
|
|
31
|
+
cursor: pointer;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
</style>
|