@coffic/cosy-ui 0.6.14 → 0.6.18

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.
@@ -47,11 +47,9 @@ BannerBox 组件是一个可定制的横幅容器,支持自定义背景、尺
47
47
  @prop {String} [title=''] - 横幅标题
48
48
  @prop {String} [description=''] - 横幅描述
49
49
  @prop {Array} [features=[]] - 特性列表,每项包含{emoji, title, link}
50
- @prop {Object} [customComponent=null] - 自定义组件
51
- @prop {Object} [customComponentProps={}] - 自定义组件的属性
52
50
 
53
51
  @slots
54
- @slot default - 横幅内容,当不使用title/description/features属性时显示
52
+ @slot default - 横幅内容
55
53
  -->
56
54
 
57
55
  <script lang="ts">
@@ -59,9 +57,12 @@ import { ref, onMounted, watch, onUnmounted, computed, defineComponent } from 'v
59
57
  import { RiDownloadLine } from '@remixicon/vue';
60
58
  import { toPng } from 'html-to-image';
61
59
  import FeatureCard from './FeatureCard.vue';
60
+ import DownloadButton from './DownloadButton.vue';
61
+ import { bgClasses } from './bgStyles';
62
+ import { sizePresets } from './sizePresets';
62
63
  import '../../style'
63
64
 
64
- interface Feature {
65
+ export interface IFeature {
65
66
  emoji: string;
66
67
  title: string;
67
68
  link?: string;
@@ -71,7 +72,8 @@ export default defineComponent({
71
72
  name: 'BannerBox',
72
73
  components: {
73
74
  RiDownloadLine,
74
- FeatureCard
75
+ FeatureCard,
76
+ DownloadButton
75
77
  },
76
78
  props: {
77
79
  displayMode: {
@@ -92,19 +94,9 @@ export default defineComponent({
92
94
  default: ''
93
95
  },
94
96
  features: {
95
- type: Array as () => Feature[],
97
+ type: Array as () => IFeature[],
96
98
  default: () => []
97
99
  },
98
- // 自定义组件
99
- customComponent: {
100
- type: Object,
101
- default: null
102
- },
103
- // 自定义组件的属性
104
- customComponentProps: {
105
- type: Object,
106
- default: () => ({})
107
- }
108
100
  },
109
101
  setup(props) {
110
102
  const componentRef = ref<HTMLElement | null>(null);
@@ -112,19 +104,6 @@ export default defineComponent({
112
104
  const isLoadedFromStorage = ref(false);
113
105
  const selectedBgIndex = ref(props.backgroundClassIndex);
114
106
 
115
- const sizePresets = [
116
- { name: 'Default', width: 'cosy:w-full', height: 'cosy:h-full' },
117
- { name: 'Square', width: 'cosy:w-[600px]', height: 'cosy:h-[600px]' },
118
- { name: 'Landscape', width: 'cosy:w-[800px]', height: 'cosy:h-[450px]' },
119
- { name: 'Portrait', width: 'cosy:w-[450px]', height: 'cosy:h-[800px]' },
120
- { name: 'Wide', width: 'cosy:w-[1200px]', height: 'cosy:h-[675px]' },
121
- { name: 'Banner', width: 'cosy:w-[1200px]', height: 'cosy:h-[300px]' },
122
- { name: '1280 × 800', width: 'cosy:w-[1280px]', height: 'cosy:h-[800px]' },
123
- { name: '1440 × 900', width: 'cosy:w-[1440px]', height: 'cosy:h-[900px]' },
124
- { name: '2560 × 1600', width: 'cosy:w-[2560px]', height: 'cosy:h-[1600px]' },
125
- { name: '2880 × 1800', width: 'cosy:w-[2880px]', height: 'cosy:h-[1800px]' },
126
- ];
127
-
128
107
  const selectedSize = ref(sizePresets[0]);
129
108
 
130
109
  const toggleDropdown = () => {
@@ -213,56 +192,6 @@ export default defineComponent({
213
192
  }
214
193
  };
215
194
 
216
- const bgClasses = [
217
- 'cosy:bg-gradient-to-b cosy:from-blue-100/50 cosy:to-blue-200/50 dark:cosy:from-blue-500/10 dark:cosy:to-blue-200/10',
218
- 'cosy:bg-gradient-to-b cosy:from-blue-200/50 cosy:to-purple-200/50 dark:cosy:from-blue-500/10 dark:cosy:to-purple-200/10',
219
- 'cosy:bg-gradient-to-b cosy:from-yellow-200/50 cosy:to-green-200/50 dark:cosy:from-yellow-500/10 dark:cosy:to-green-200/10',
220
- 'cosy:bg-gradient-to-b cosy:from-teal-200/50 cosy:to-blue-200/50 dark:cosy:from-teal-500/10 dark:cosy:to-blue-200/10',
221
- 'cosy:bg-gradient-to-b cosy:from-pink-200/50 cosy:to-indigo-200/20 dark:cosy:from-pink-500/10 dark:cosy:to-indigo-200/10',
222
- 'cosy:bg-gradient-to-b cosy:from-red-200/50 cosy:to-orange-200/50 dark:cosy:from-red-500/10 dark:cosy:to-orange-200/10',
223
- 'cosy:bg-gradient-to-b cosy:from-orange-200/50 cosy:to-yellow-200/50 dark:cosy:from-orange-500/10 dark:cosy:to-yellow-200/10',
224
- 'cosy:bg-gradient-to-b cosy:from-green-200/50 cosy:to-teal-200/50 dark:cosy:from-green-500/10 dark:cosy:to-teal-200/10',
225
-
226
- // 不透明的背景
227
- 'cosy:bg-gradient-to-b cosy:from-blue-100 cosy:to-blue-200 dark:cosy:from-blue-500 dark:cosy:to-blue-200',
228
- 'cosy:bg-gradient-to-b cosy:from-blue-200 cosy:to-purple-200 dark:cosy:from-blue-500 dark:cosy:to-purple-200',
229
- 'cosy:bg-gradient-to-b cosy:from-yellow-200 cosy:to-green-200 dark:cosy:from-yellow-500 dark:cosy:to-green-200',
230
- 'cosy:bg-gradient-to-b cosy:from-teal-200 cosy:to-blue-200 dark:cosy:from-teal-500 dark:cosy:to-blue-200',
231
- 'cosy:bg-gradient-to-b cosy:from-pink-200 cosy:to-red-200 dark:cosy:from-pink-500 dark:cosy:to-red-200',
232
- 'cosy:bg-gradient-to-b cosy:from-red-200 cosy:to-orange-200 dark:cosy:from-red-500 dark:cosy:to-orange-200',
233
- 'cosy:bg-gradient-to-b cosy:from-orange-200 cosy:to-yellow-200 dark:cosy:from-orange-500 dark:cosy:to-yellow-200',
234
- 'cosy:bg-gradient-to-b cosy:from-green-200 cosy:to-teal-200 dark:cosy:from-green-500 dark:cosy:to-teal-200',
235
-
236
- // 不透明的深色背景
237
- 'cosy:bg-gradient-to-b cosy:from-blue-900 cosy:to-blue-200 dark:cosy:from-blue-900 dark:cosy:to-blue-200',
238
- 'cosy:bg-gradient-to-b cosy:from-blue-900 cosy:to-purple-200 dark:cosy:from-blue-900 dark:cosy:to-purple-200',
239
- 'cosy:bg-gradient-to-b cosy:from-yellow-900 cosy:to-green-200 dark:cosy:from-yellow-900 dark:cosy:to-green-200',
240
- 'cosy:bg-gradient-to-b cosy:from-teal-900 cosy:to-blue-200 dark:cosy:from-teal-900 dark:cosy:to-blue-200',
241
- 'cosy:bg-gradient-to-b cosy:from-pink-900 cosy:to-red-200 dark:cosy:from-pink-900 dark:cosy:to-red-200',
242
- 'cosy:bg-gradient-to-b cosy:from-red-900 cosy:to-orange-200 dark:cosy:from-red-900 dark:cosy:to-orange-200',
243
- 'cosy:bg-gradient-to-b cosy:from-orange-900 cosy:to-yellow-200 dark:cosy:from-orange-900 dark:cosy:to-yellow-200',
244
- 'cosy:bg-gradient-to-b cosy:from-green-900 cosy:to-teal-900 dark:cosy:from-green-900 dark:cosy:to-teal-900',
245
- // 不透明的渐变背景
246
- 'cosy:bg-gradient-to-br cosy:from-emerald-400 cosy:to-cyan-400 dark:cosy:from-emerald-600 dark:cosy:to-cyan-600',
247
- 'cosy:bg-gradient-to-br cosy:from-violet-400 cosy:to-fuchsia-400 dark:cosy:from-violet-600 dark:cosy:to-fuchsia-600',
248
- 'cosy:bg-gradient-to-br cosy:from-amber-400 cosy:to-orange-400 dark:cosy:from-amber-600 dark:cosy:to-orange-600',
249
- 'cosy:bg-gradient-to-br cosy:from-rose-400 cosy:to-pink-400 dark:cosy:from-rose-600 dark:cosy:to-pink-600',
250
- 'cosy:bg-gradient-to-br cosy:from-sky-400 cosy:to-indigo-400 dark:cosy:from-sky-600 dark:cosy:to-indigo-600',
251
- 'cosy:bg-gradient-to-br cosy:from-lime-400 cosy:to-emerald-400 dark:cosy:from-lime-600 dark:cosy:to-emerald-600',
252
- 'cosy:bg-gradient-to-br cosy:from-purple-400 cosy:to-indigo-400 dark:cosy:from-purple-600 dark:cosy:to-indigo-600',
253
- 'cosy:bg-gradient-to-br cosy:from-blue-400 cosy:to-violet-400 dark:cosy:from-blue-600 dark:cosy:to-violet-600',
254
-
255
- // 纯色背景
256
- 'cosy:bg-emerald-400 dark:cosy:bg-emerald-600',
257
- 'cosy:bg-violet-400 dark:cosy:bg-violet-600',
258
- 'cosy:bg-amber-400 dark:cosy:bg-amber-600',
259
- 'cosy:bg-rose-400 dark:cosy:bg-rose-600',
260
- 'cosy:bg-sky-400 dark:cosy:bg-sky-600',
261
- 'cosy:bg-lime-400 dark:cosy:bg-lime-600',
262
- 'cosy:bg-purple-400 dark:cosy:bg-purple-600',
263
- 'cosy:bg-blue-400 dark:cosy:bg-blue-600'
264
- ];
265
-
266
195
  const getBackgroundClass = (): string => {
267
196
  return bgClasses[selectedBgIndex.value % bgClasses.length];
268
197
  }
@@ -280,9 +209,6 @@ export default defineComponent({
280
209
  window.removeEventListener('bannerBoxSizeChange', handleSizeChange);
281
210
  });
282
211
 
283
- // 是否显示Banner内容
284
- const showBannerContent = computed(() => props.title !== '' || props.description !== '' || props.features.length > 0 || props.customComponent !== null);
285
-
286
212
  // 计算下载按钮是否显示及其样式类
287
213
  const downloadButtonStyles = computed(() => {
288
214
  switch (props.displayMode) {
@@ -320,8 +246,8 @@ export default defineComponent({
320
246
  downloadAsImage,
321
247
  getBackgroundClass,
322
248
  clearStoredSize,
323
- showBannerContent,
324
- downloadButtonStyles
249
+ downloadButtonStyles,
250
+ bgClasses
325
251
  };
326
252
  }
327
253
  });
@@ -329,88 +255,25 @@ export default defineComponent({
329
255
 
330
256
  <template>
331
257
  <div class="cosy:relative cosy:w-full cosy:rounded-2xl cosy:max-w-7xl cosy:mx-auto">
332
- <!-- Add size indicator -->
258
+ <!-- Size indicator -->
333
259
  <div v-if="isLoadedFromStorage"
334
260
  class="cosy:absolute cosy:top-4 cosy:right-4 cosy:bg-yellow-500/30 cosy:backdrop-blur-sm cosy:px-3 cosy:py-1 cosy:rounded-lg cosy:text-sm cosy:text-white">
335
261
  {{ selectedSize.name }}
336
262
  </div>
337
263
 
338
264
  <!-- Download button with dropdown menu -->
339
- <div v-if="downloadButtonStyles.show" class="cosy:absolute cosy:top-4 cosy:left-4"
340
- :class="downloadButtonStyles.classes">
341
- <div class="cosy:relative" data-dropdown>
342
- <button
343
- class="cosy:bg-yellow-500/30 cosy:backdrop-blur-sm cosy:p-2 cosy:rounded-lg hover:cosy:bg-yellow-500/40"
344
- @click="toggleDropdown">
345
- <RiDownloadLine class="cosy:w-6 cosy:h-6 cosy:text-white" />
346
- </button>
347
- <!-- Size selection dropdown -->
348
- <div v-if="isDropdownOpen"
349
- class="cosy:absolute cosy:left-0 cosy:mt-2 cosy:w-96 cosy:bg-white dark:cosy:bg-gray-800 cosy:rounded-lg cosy:shadow-lg cosy:py-2 cosy:z-50">
350
- <!-- Component size presets -->
351
- <div class="cosy:px-4 cosy:py-2 cosy:border-b cosy:border-gray-200 dark:cosy:border-gray-700">
352
- <div class="cosy:grid cosy:grid-cols-3 cosy:gap-2">
353
- <button v-for="preset in sizePresets" :key="preset.name" :class="[
354
- 'cosy:p-2 cosy:text-left cosy:rounded cosy:text-sm',
355
- selectedSize.name === preset.name
356
- ? 'cosy:bg-yellow-500/30 cosy:text-yellow-900 dark:cosy:text-yellow-100'
357
- : 'hover:cosy:bg-gray-100 dark:hover:cosy:bg-gray-700'
358
- ]" @click="selectedSize = preset">
359
- <div class="cosy:flex cosy:flex-col">
360
- <span class="cosy:font-medium">{{ preset.name }}</span>
361
- <span class="cosy:text-xs cosy:text-gray-500 dark:cosy:text-gray-400">
362
- {{ preset.width.replace('cosy:w-[', '').replace(']', '') }}
363
- </span>
364
- </div>
365
- </button>
366
- <!-- Clear size button -->
367
- <button
368
- class="cosy:p-2 cosy:text-left cosy:rounded cosy:text-sm hover:cosy:bg-gray-100 dark:hover:cosy:bg-gray-700"
369
- @click="clearStoredSize">
370
- <div class="cosy:flex cosy:flex-col">
371
- <span
372
- class="cosy:font-medium cosy:text-red-600 dark:cosy:text-red-400">清除记住的尺寸</span>
373
- <span class="cosy:text-xs cosy:text-gray-500 dark:cosy:text-gray-400">重置为默认尺寸</span>
374
- </div>
375
- </button>
376
- </div>
377
- </div>
378
- <!-- Background settings -->
379
- <div class="cosy:px-4 cosy:py-2 cosy:border-b cosy:border-gray-200 dark:cosy:border-gray-700">
380
- <div class="cosy:mt-2">
381
- <div class="cosy:grid cosy:grid-cols-8 cosy:gap-2">
382
- <button v-for="(_, index) in bgClasses" :key="index" :class="[
383
- bgClasses[index],
384
- 'cosy:w-8 cosy:h-8 cosy:rounded-lg cosy:border-2',
385
- selectedBgIndex === index ? 'cosy:border-yellow-500' : 'cosy:border-transparent'
386
- ]" @click="selectedBgIndex = index" />
387
- </div>
388
- </div>
389
- </div>
390
- <!-- Size options -->
391
- <div class="cosy:p-4">
392
- <button
393
- class="cosy:w-full cosy:p-2 cosy:text-center cosy:rounded hover:cosy:bg-gray-100 dark:hover:cosy:bg-gray-700"
394
- @click="downloadAsImage()">
395
- <div class="cosy:flex cosy:items-center cosy:justify-center cosy:gap-2">
396
- <RiDownloadLine class="cosy:w-4 cosy:h-4" />
397
- <span class="cosy:font-medium">下载图片</span>
398
- </div>
399
- </button>
400
- </div>
401
- </div>
402
- </div>
403
- </div>
265
+ <DownloadButton :displayMode="displayMode" :isLoadedFromStorage="isLoadedFromStorage"
266
+ :selectedSize="selectedSize" :selectedBgIndex="selectedBgIndex" @update:selectedSize="selectedSize = $event"
267
+ @update:selectedBgIndex="selectedBgIndex = $event" @clear-stored-size="clearStoredSize"
268
+ @download-image="downloadAsImage" />
404
269
 
405
270
  <div ref="componentRef" class="cosy:flex cosy:p-8 cosy:rounded-2xl cosy:shadow" :class="[
406
271
  getBackgroundClass(),
407
272
  selectedSize.width,
408
273
  selectedSize.height
409
274
  ]">
410
- <!-- Smart Banner Content (when banner prop is provided) -->
411
- <div v-if="showBannerContent" class="cosy:py-16 cosy:px-8 cosy:text-center cosy:w-full cosy:rounded-2xl"
412
- data-type="smart-banner">
413
- <h2 class="cosy:text-4xl cosy:mb-4">
275
+ <div class="cosy:py-16 cosy:px-8 cosy:text-center cosy:w-full cosy:rounded-2xl" data-type="smart-banner">
276
+ <h2 v-if="title.length > 0" class="cosy:text-4xl cosy:mb-4">
414
277
  {{ title }}
415
278
  </h2>
416
279
 
@@ -418,18 +281,16 @@ export default defineComponent({
418
281
  {{ description }}
419
282
  </p>
420
283
 
421
- <div class="cosy:flex cosy:flex-row cosy:justify-center cosy:gap-8 cosy:mx-auto cosy:w-full cosy:mt-24">
284
+ <div v-if="features.length > 0"
285
+ class="cosy:flex cosy:flex-row cosy:justify-center cosy:gap-8 cosy:mx-auto cosy:w-full cosy:mt-24">
422
286
  <FeatureCard v-for="feature in features" :key="feature.title" :emoji="feature.emoji"
423
287
  :title="feature.title" :link="feature.link" />
424
288
  </div>
425
289
 
426
- <div class="cosy:mt-12">
427
- <component :is="customComponent" v-if="customComponent" v-bind="customComponentProps" />
290
+ <div :class="{ 'cosy:mt-12': title.length > 0 || description.length > 0 || features.length > 0 }">
291
+ <slot />
428
292
  </div>
429
293
  </div>
430
-
431
- <!-- Default slot for custom content (when banner prop is not provided) -->
432
- <slot v-if="!showBannerContent" />
433
294
  </div>
434
295
  </div>
435
296
  </template>
@@ -0,0 +1,202 @@
1
+ <!--
2
+ @component DownloadButton
3
+
4
+ @description
5
+ DownloadButton 组件提供了一个下载按钮,带有可展开的下拉菜单,用于调整尺寸、背景和下载图片。
6
+
7
+ @usage
8
+ 基本用法:
9
+ ```vue
10
+ <DownloadButton
11
+ :displayMode="'hover'"
12
+ :isLoadedFromStorage="true"
13
+ :selectedSize="selectedSize"
14
+ :selectedBgIndex="selectedBgIndex"
15
+ :sizePresets="sizePresets"
16
+ :bgClasses="bgClasses"
17
+ @update:selectedSize="selectedSize = $event"
18
+ @update:selectedBgIndex="selectedBgIndex = $event"
19
+ @clear-stored-size="clearStoredSize"
20
+ @download-image="downloadAsImage"
21
+ />
22
+ ```
23
+
24
+ @props
25
+ @prop {String} [displayMode='hover'] - 下载按钮显示模式:'always'(总是显示),'hover'(悬停显示),'never'(不显示)
26
+ @prop {Boolean} [isLoadedFromStorage=false] - 是否已从存储中加载尺寸
27
+ @prop {Object} [selectedSize] - 当前选中的尺寸预设
28
+ @prop {Number} [selectedBgIndex=0] - 当前选中的背景样式索引
29
+ @prop {Array} [sizePresets] - 尺寸预设列表
30
+ @prop {Array} [bgClasses] - 背景样式类列表
31
+
32
+ @emits
33
+ @emit update:selectedSize - 当选择新的尺寸预设时触发
34
+ @emit update:selectedBgIndex - 当选择新的背景样式时触发
35
+ @emit clear-stored-size - 当清除存储的尺寸时触发
36
+ @emit download-image - 当请求下载图片时触发
37
+ -->
38
+
39
+ <script lang="ts">
40
+ import { ref, computed, defineComponent } from 'vue';
41
+ import { RiDownloadLine } from '@remixicon/vue';
42
+ import { bgClasses } from './bgStyles';
43
+ import { sizePresets, type SizePreset } from './sizePresets';
44
+ import '../../style'
45
+
46
+ export default defineComponent({
47
+ name: 'DownloadButton',
48
+ components: {
49
+ RiDownloadLine
50
+ },
51
+ props: {
52
+ displayMode: {
53
+ type: String,
54
+ default: 'hover',
55
+ validator: (value: string) => ['always', 'hover', 'never'].includes(value)
56
+ },
57
+ isLoadedFromStorage: {
58
+ type: Boolean,
59
+ default: false
60
+ },
61
+ selectedSize: {
62
+ type: Object as () => SizePreset,
63
+ required: true
64
+ },
65
+ selectedBgIndex: {
66
+ type: Number,
67
+ default: 0
68
+ }
69
+ },
70
+ emits: ['update:selectedSize', 'update:selectedBgIndex', 'clear-stored-size', 'download-image'],
71
+ setup(props, { emit }) {
72
+ const isDropdownOpen = ref(false);
73
+
74
+ const toggleDropdown = () => {
75
+ isDropdownOpen.value = !isDropdownOpen.value;
76
+ };
77
+
78
+ // 计算下载按钮是否显示及其样式类
79
+ const downloadButtonStyles = computed(() => {
80
+ switch (props.displayMode) {
81
+ case 'always':
82
+ return {
83
+ show: true,
84
+ classes: 'cosy:opacity-100'
85
+ };
86
+ case 'hover':
87
+ return {
88
+ show: true,
89
+ classes: 'cosy:opacity-0 cosy:hover:opacity-100 cosy:transition-opacity'
90
+ };
91
+ case 'never':
92
+ return {
93
+ show: false,
94
+ classes: ''
95
+ };
96
+ default:
97
+ return {
98
+ show: true,
99
+ classes: 'cosy:opacity-0 cosy:hover:opacity-100 cosy:transition-opacity'
100
+ };
101
+ }
102
+ });
103
+
104
+ const selectSize = (size: SizePreset) => {
105
+ emit('update:selectedSize', size);
106
+ };
107
+
108
+ const selectBackground = (index: number) => {
109
+ emit('update:selectedBgIndex', index);
110
+ };
111
+
112
+ const clearStoredSize = () => {
113
+ emit('clear-stored-size');
114
+ isDropdownOpen.value = false;
115
+ };
116
+
117
+ const downloadImage = () => {
118
+ emit('download-image');
119
+ isDropdownOpen.value = false;
120
+ };
121
+
122
+ return {
123
+ isDropdownOpen,
124
+ toggleDropdown,
125
+ downloadButtonStyles,
126
+ selectSize,
127
+ selectBackground,
128
+ clearStoredSize,
129
+ downloadImage,
130
+ bgClasses,
131
+ sizePresets
132
+ };
133
+ }
134
+ });
135
+ </script>
136
+
137
+ <template>
138
+ <div v-if="downloadButtonStyles.show" class="cosy:absolute cosy:top-4 cosy:left-4"
139
+ :class="downloadButtonStyles.classes">
140
+ <div class="cosy:relative" data-dropdown>
141
+ <button
142
+ class="cosy:bg-yellow-500/30 cosy:backdrop-blur-sm cosy:p-2 cosy:rounded-lg hover:cosy:bg-yellow-500/40"
143
+ @click="toggleDropdown">
144
+ <RiDownloadLine class="cosy:w-6 cosy:h-6 cosy:text-white" />
145
+ </button>
146
+ <!-- Size selection dropdown -->
147
+ <div v-if="isDropdownOpen"
148
+ class="cosy:absolute cosy:left-0 cosy:mt-2 cosy:w-96 cosy:bg-white dark:cosy:bg-gray-800 cosy:rounded-lg cosy:shadow-lg cosy:py-2 cosy:z-50">
149
+ <!-- Component size presets -->
150
+ <div class="cosy:px-4 cosy:py-2 cosy:border-b cosy:border-gray-200 dark:cosy:border-gray-700">
151
+ <div class="cosy:grid cosy:grid-cols-3 cosy:gap-2">
152
+ <button v-for="preset in sizePresets" :key="preset.name" :class="[
153
+ 'cosy:p-2 cosy:text-left cosy:rounded cosy:text-sm',
154
+ selectedSize.name === preset.name
155
+ ? 'cosy:bg-yellow-500/30 cosy:text-yellow-900 dark:cosy:text-yellow-100'
156
+ : 'hover:cosy:bg-gray-100 dark:hover:cosy:bg-gray-700'
157
+ ]" @click="selectSize(preset)">
158
+ <div class="cosy:flex cosy:flex-col">
159
+ <span class="cosy:font-medium">{{ preset.name }}</span>
160
+ <span class="cosy:text-xs cosy:text-gray-500 dark:cosy:text-gray-400">
161
+ {{ preset.width.replace('cosy:w-[', '').replace(']', '') }}
162
+ </span>
163
+ </div>
164
+ </button>
165
+ <!-- Clear size button -->
166
+ <button
167
+ class="cosy:p-2 cosy:text-left cosy:rounded cosy:text-sm hover:cosy:bg-gray-100 dark:hover:cosy:bg-gray-700"
168
+ @click="clearStoredSize">
169
+ <div class="cosy:flex cosy:flex-col">
170
+ <span class="cosy:font-medium cosy:text-red-600 dark:cosy:text-red-400">清除记住的尺寸</span>
171
+ <span class="cosy:text-xs cosy:text-gray-500 dark:cosy:text-gray-400">重置为默认尺寸</span>
172
+ </div>
173
+ </button>
174
+ </div>
175
+ </div>
176
+ <!-- Background settings -->
177
+ <div class="cosy:px-4 cosy:py-2 cosy:border-b cosy:border-gray-200 dark:cosy:border-gray-700">
178
+ <div class="cosy:mt-2">
179
+ <div class="cosy:grid cosy:grid-cols-8 cosy:gap-2">
180
+ <button v-for="(className, index) in bgClasses" :key="index" :class="[
181
+ className,
182
+ 'cosy:w-8 cosy:h-8 cosy:rounded-lg cosy:border-2',
183
+ selectedBgIndex === index ? 'cosy:border-yellow-500' : 'cosy:border-transparent'
184
+ ]" @click="selectBackground(index)" />
185
+ </div>
186
+ </div>
187
+ </div>
188
+ <!-- Size options -->
189
+ <div class="cosy:p-4">
190
+ <button
191
+ class="cosy:w-full cosy:p-2 cosy:text-center cosy:rounded hover:cosy:bg-gray-100 dark:hover:cosy:bg-gray-700"
192
+ @click="downloadImage">
193
+ <div class="cosy:flex cosy:items-center cosy:justify-center cosy:gap-2">
194
+ <RiDownloadLine class="cosy:w-4 cosy:h-4" />
195
+ <span class="cosy:font-medium">下载图片</span>
196
+ </div>
197
+ </button>
198
+ </div>
199
+ </div>
200
+ </div>
201
+ </div>
202
+ </template>
@@ -0,0 +1,34 @@
1
+ <!--
2
+ @component BannerBox.DisplayModeAlways
3
+
4
+ @description
5
+ BannerBox 组件的"总是显示下载按钮"模式示例。
6
+
7
+ @usage
8
+ ```vue
9
+ <BannerBoxExamples.DisplayModeAlways />
10
+ ```
11
+ -->
12
+
13
+ <script lang="ts">
14
+ import '../../app.css'
15
+ import { defineComponent } from 'vue'
16
+ import BannerBox from './BannerBox.vue'
17
+
18
+ export default defineComponent({
19
+ name: 'BannerBoxDisplayModeAlwaysExample',
20
+ components: {
21
+ BannerBox
22
+ }
23
+ })
24
+ </script>
25
+
26
+ <template>
27
+ <div>
28
+ <BannerBox displayMode="always" :backgroundClassIndex="1">
29
+ <div class="cosy:flex cosy:items-center cosy:justify-center cosy:min-h-[150px]">
30
+ <p>下载按钮始终可见</p>
31
+ </div>
32
+ </BannerBox>
33
+ </div>
34
+ </template>
@@ -0,0 +1,34 @@
1
+ <!--
2
+ @component BannerBox.DisplayModeHover
3
+
4
+ @description
5
+ BannerBox 组件的"悬停时显示下载按钮"模式示例,这是默认行为。
6
+
7
+ @usage
8
+ ```vue
9
+ <BannerBoxExamples.DisplayModeHover />
10
+ ```
11
+ -->
12
+
13
+ <script lang="ts">
14
+ import '../../app.css'
15
+ import { defineComponent } from 'vue'
16
+ import BannerBox from './BannerBox.vue'
17
+
18
+ export default defineComponent({
19
+ name: 'BannerBoxDisplayModeHoverExample',
20
+ components: {
21
+ BannerBox
22
+ }
23
+ })
24
+ </script>
25
+
26
+ <template>
27
+ <div>
28
+ <BannerBox displayMode="hover" :backgroundClassIndex="2">
29
+ <div class="cosy:flex cosy:items-center cosy:justify-center cosy:min-h-[150px]">
30
+ <p>鼠标悬停时显示下载按钮</p>
31
+ </div>
32
+ </BannerBox>
33
+ </div>
34
+ </template>
@@ -0,0 +1,34 @@
1
+ <!--
2
+ @component BannerBox.DisplayModeNever
3
+
4
+ @description
5
+ BannerBox 组件的"不显示下载按钮"模式示例。
6
+
7
+ @usage
8
+ ```vue
9
+ <BannerBoxExamples.DisplayModeNever />
10
+ ```
11
+ -->
12
+
13
+ <script lang="ts">
14
+ import '../../app.css'
15
+ import { defineComponent } from 'vue'
16
+ import BannerBox from './BannerBox.vue'
17
+
18
+ export default defineComponent({
19
+ name: 'BannerBoxDisplayModeNeverExample',
20
+ components: {
21
+ BannerBox
22
+ }
23
+ })
24
+ </script>
25
+
26
+ <template>
27
+ <div>
28
+ <BannerBox displayMode="never" :backgroundClassIndex="3">
29
+ <div class="cosy:flex cosy:items-center cosy:justify-center cosy:min-h-[150px]">
30
+ <p>不显示下载按钮</p>
31
+ </div>
32
+ </BannerBox>
33
+ </div>
34
+ </template>
@@ -1,30 +1,3 @@
1
- <template>
2
- <component :is="link ? 'a' : 'div'" :href="link || undefined" :target="link ? '_blank' : undefined"
3
- :rel="link ? 'noopener noreferrer' : undefined" :class="[
4
- 'card bg-base-100/10 backdrop-blur-lg p-8 transition-all duration-300 hover:-translate-y-1 shadow-lg',
5
- {
6
- 'hover:bg-primary/15 cursor-pointer': link,
7
- 'cursor-default': !link
8
- }
9
- ]">
10
- <div class="card-body p-0">
11
- <div class="mb-4">
12
- <component :is="icon" v-if="icon" class="text-4xl text-base-content" />
13
- <component :is="getPresetIcon" v-else-if="presetIcon" class="text-4xl text-base-content" />
14
- <div v-else class="text-4xl text-base-content">
15
- {{ emoji }}
16
- </div>
17
- </div>
18
- <h3 class="card-title text-lg font-medium text-base-content">
19
- {{ title }}
20
- </h3>
21
- <p v-if="description" class="text-base-content/70">
22
- {{ description }}
23
- </p>
24
- </div>
25
- </component>
26
- </template>
27
-
28
1
  <script setup lang="ts">
29
2
  import { computed, type Component } from 'vue';
30
3
  import {
@@ -187,4 +160,31 @@ const getPresetIcon = computed(() => {
187
160
  if (!props.presetIcon) return null;
188
161
  return presetIcons[props.presetIcon];
189
162
  });
190
- </script>
163
+ </script>
164
+
165
+ <template>
166
+ <component :is="link ? 'a' : 'div'" :href="link || undefined" :target="link ? '_blank' : undefined"
167
+ :rel="link ? 'noopener noreferrer' : undefined" :class="[
168
+ 'cosy:card cosy:no-underline cosy:bg-base-100/10 cosy:backdrop-blur-lg cosy:p-8 cosy:transition-all cosy:duration-300 cosy:hover:-translate-y-1 cosy:shadow-lg',
169
+ {
170
+ 'cosy:hover:bg-primary/15 cosy:cursor-pointer': link,
171
+ 'cosy:cursor-default': !link
172
+ }
173
+ ]">
174
+ <div class="card-body cosy:p-0">
175
+ <div class="cosy:mb-4">
176
+ <component :is="icon" v-if="icon" class="cosy:text-4xl cosy:text-base-content" />
177
+ <component :is="getPresetIcon" v-else-if="presetIcon" class="cosy:text-4xl cosy:text-base-content" />
178
+ <div v-else class="cosy:text-4xl cosy:text-base-content">
179
+ {{ emoji }}
180
+ </div>
181
+ </div>
182
+ <h3 class="card-title cosy:text-lg cosy:font-medium cosy:text-base-content">
183
+ {{ title }}
184
+ </h3>
185
+ <p v-if="description" class="cosy:text-base-content/70">
186
+ {{ description }}
187
+ </p>
188
+ </div>
189
+ </component>
190
+ </template>