@coffic/cosy-ui 0.8.24 → 0.8.26

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.
@@ -63,8 +63,8 @@ iPhoneWindow 组件模拟 iPhone 设备的外观,包含状态栏、时间显
63
63
  @emits
64
64
  -->
65
65
  <script lang="ts">
66
- import '../../style.ts';
67
- import { AlertDialog } from '../../index-vue.ts';
66
+ import '../../style';
67
+ import { AlertDialog } from '../../index-vue';
68
68
  import { ref, defineComponent } from 'vue';
69
69
  import iphoneFrame from './assets/iPhone 14 Pro - Deep Purple - Portrait.png';
70
70
  import StatusBarContent from './StatusBarContent.vue';
@@ -83,148 +83,168 @@ const mainContentWidthAspectRatio = 1179 / iphoneFrameWidth;
83
83
  // 比例-总高度
84
84
  const mainContentHeightAspectRatio = 2556 / iphoneFrameHeight;
85
85
  // 比例-状态栏高度
86
- const iphoneFrameStatusBarHeightAspectRatio = iphoneFrameStatusBarHeight / iphoneFrameHeight;
86
+ const iphoneFrameStatusBarHeightAspectRatio =
87
+ iphoneFrameStatusBarHeight / iphoneFrameHeight;
87
88
  // 比例-状态栏离上边框的距离
88
- const iphoneFrameStatusBarTopAspectRatio = iphoneFrameStatusBarTop / iphoneFrameHeight;
89
+ const iphoneFrameStatusBarTopAspectRatio =
90
+ iphoneFrameStatusBarTop / iphoneFrameHeight;
89
91
 
90
92
  // 预定义的高度选项
91
93
  type HeightOption = 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | '4xl' | '5xl';
92
94
 
93
95
  const heightClasses: Record<HeightOption, string> = {
94
- sm: 'cosy:h-64', // 256px
95
- md: 'cosy:h-80', // 320px
96
- lg: 'cosy:h-96', // 384px
97
- xl: 'cosy:h-[480px]', // 480px
98
- '2xl': 'cosy:h-[560px]', // 560px
99
- '3xl': 'cosy:h-[640px]', // 640px
100
- '4xl': 'cosy:h-[720px]', // 720px
101
- '5xl': 'cosy:h-[800px]', // 800px
96
+ sm: 'cosy:h-64', // 256px
97
+ md: 'cosy:h-80', // 320px
98
+ lg: 'cosy:h-96', // 384px
99
+ xl: 'cosy:h-[480px]', // 480px
100
+ '2xl': 'cosy:h-[560px]', // 560px
101
+ '3xl': 'cosy:h-[640px]', // 640px
102
+ '4xl': 'cosy:h-[720px]', // 720px
103
+ '5xl': 'cosy:h-[800px]', // 800px
102
104
  };
103
105
 
104
106
  export default defineComponent({
105
- name: 'iPhoneWindow',
106
- components: {
107
- AlertDialog,
108
- StatusBarContent,
107
+ name: 'iPhoneWindow',
108
+ components: {
109
+ AlertDialog,
110
+ StatusBarContent,
111
+ },
112
+ props: {
113
+ height: {
114
+ type: String as () => HeightOption,
115
+ default: 'lg',
116
+ validator: (value: string) => {
117
+ return Object.keys(heightClasses).includes(value);
118
+ },
109
119
  },
110
- props: {
111
- height: {
112
- type: String as () => HeightOption,
113
- default: 'lg',
114
- validator: (value: string) => {
115
- return Object.keys(heightClasses).includes(value);
116
- },
117
- },
118
- debug: {
119
- type: Boolean,
120
- default: false,
121
- },
122
- title: {
123
- type: String,
124
- default: '',
125
- },
126
- statusBarButtons: {
127
- type: Array,
128
- default: () => [],
129
- },
130
- withShadow: {
131
- type: Boolean,
132
- default: true,
133
- },
134
- showFrame: {
135
- type: Boolean,
136
- default: true,
137
- },
138
- backgroundColor: {
139
- type: String,
140
- default: '',
141
- },
120
+ debug: {
121
+ type: Boolean,
122
+ default: false,
142
123
  },
143
- setup(props) {
144
- const showAlertDialog = ref(false);
145
- const alertMessage = ref('');
146
-
147
- // 计算当前高度的缩放比例
148
- const getScaleRatio = () => {
149
- const heightValues = {
150
- sm: 256,
151
- md: 320,
152
- lg: 384,
153
- xl: 480,
154
- '2xl': 560,
155
- '3xl': 640,
156
- '4xl': 720,
157
- '5xl': 800,
158
- };
159
- const currentHeight = heightValues[props.height];
160
- // 基于特定高度计算缩放比例
161
- return currentHeight / 500;
162
- };
163
-
164
- return {
165
- showAlertDialog,
166
- alertMessage,
167
- iphoneFrame: (iphoneFrame as any).src || iphoneFrame,
168
- heightClasses,
169
- mainContentWidthAspectRatio,
170
- mainContentHeightAspectRatio,
171
- iphoneFrameWidth,
172
- iphoneFrameHeight,
173
- iphoneFrameStatusBarTop,
174
- iphoneFrameStatusBarHeight,
175
- iphoneFrameStatusBarHeightAspectRatio,
176
- iphoneFrameStatusBarTopAspectRatio,
177
- getScaleRatio,
178
- };
124
+ title: {
125
+ type: String,
126
+ default: '',
179
127
  },
128
+ statusBarButtons: {
129
+ type: Array,
130
+ default: () => [],
131
+ },
132
+ withShadow: {
133
+ type: Boolean,
134
+ default: true,
135
+ },
136
+ showFrame: {
137
+ type: Boolean,
138
+ default: true,
139
+ },
140
+ backgroundColor: {
141
+ type: String,
142
+ default: '',
143
+ },
144
+ },
145
+ setup(props) {
146
+ const showAlertDialog = ref(false);
147
+ const alertMessage = ref('');
148
+
149
+ // 计算当前高度的缩放比例
150
+ const getScaleRatio = () => {
151
+ const heightValues = {
152
+ sm: 256,
153
+ md: 320,
154
+ lg: 384,
155
+ xl: 480,
156
+ '2xl': 560,
157
+ '3xl': 640,
158
+ '4xl': 720,
159
+ '5xl': 800,
160
+ };
161
+ const currentHeight = heightValues[props.height];
162
+ // 基于特定高度计算缩放比例
163
+ return currentHeight / 500;
164
+ };
165
+
166
+ return {
167
+ showAlertDialog,
168
+ alertMessage,
169
+ iphoneFrame: (iphoneFrame as any).src || iphoneFrame,
170
+ heightClasses,
171
+ mainContentWidthAspectRatio,
172
+ mainContentHeightAspectRatio,
173
+ iphoneFrameWidth,
174
+ iphoneFrameHeight,
175
+ iphoneFrameStatusBarTop,
176
+ iphoneFrameStatusBarHeight,
177
+ iphoneFrameStatusBarHeightAspectRatio,
178
+ iphoneFrameStatusBarTopAspectRatio,
179
+ getScaleRatio,
180
+ };
181
+ },
180
182
  });
181
183
  </script>
182
184
 
183
185
  <template>
184
- <div :class="['cosy:relative', heightClasses[height]]" :style="{
185
- aspectRatio: `${iphoneFrameWidth}/${iphoneFrameHeight}`,
186
- // 调试模式,背景色为半透明的黄色
187
- backgroundColor: debug ? 'rgba(255, 255, 0, 0.3)' : 'transparent',
188
- }">
189
- <!-- iPhone 边框 -->
190
- <img v-if="showFrame" style="max-width: 100%; max-height: 100%;" :src="iphoneFrame" alt="iPhone frame" />
191
-
192
- <!-- 顶部状态栏 -->
193
- <div :style="{
194
- position: 'absolute',
195
- top: iphoneFrameStatusBarTopAspectRatio * 100 + '%',
196
- height: iphoneFrameStatusBarHeightAspectRatio * 100 + '%',
197
- width: mainContentWidthAspectRatio * 100 + '%',
198
- left: '50%',
199
- transform: 'translate(-50%, 0)',
200
- paddingLeft: '5%',
201
- paddingRight: '5%',
202
- // 调试模式,背景色为半透明的红色
203
- backgroundColor: debug ? 'rgba(255, 0, 0, 0.3)' : 'transparent',
204
- zIndex: 10,
205
- }">
206
- <StatusBarContent :scaleRatio="getScaleRatio()" />
207
- </div>
208
-
209
- <!-- 内容区域 -->
210
- <div class="cosy:inset-0 cosy:h-full" :style="{
211
- width: mainContentWidthAspectRatio * 100 + '%',
212
- height: mainContentHeightAspectRatio * 100 + '%',
213
- // 水平居中
214
- left: '50%',
215
- // 垂直居中
216
- top: '50%',
217
- transform: 'translate(-50%, -50%)',
218
- position: 'absolute',
219
- // 调试模式,背景色为半透明的蓝色
220
- backgroundColor: debug ? 'rgba(0, 0, 255, 0.3)' : 'transparent',
221
- zIndex: 10,
222
- }">
223
- <div :class="[debug ? 'cosy:bg-green-300/50' : '', 'cosy:h-full cosy:w-full cosy:overflow-hidden']">
224
- <slot />
225
- </div>
226
- </div>
186
+ <div
187
+ :class="['cosy:relative', heightClasses[height]]"
188
+ :style="{
189
+ aspectRatio: `${iphoneFrameWidth}/${iphoneFrameHeight}`,
190
+ // 调试模式,背景色为半透明的黄色
191
+ backgroundColor: debug ? 'rgba(255, 255, 0, 0.3)' : 'transparent',
192
+ }"
193
+ >
194
+ <!-- iPhone 边框 -->
195
+ <img
196
+ v-if="showFrame"
197
+ style="max-width: 100%; max-height: 100%"
198
+ :src="iphoneFrame"
199
+ alt="iPhone frame"
200
+ />
201
+
202
+ <!-- 顶部状态栏 -->
203
+ <div
204
+ :style="{
205
+ position: 'absolute',
206
+ top: iphoneFrameStatusBarTopAspectRatio * 100 + '%',
207
+ height: iphoneFrameStatusBarHeightAspectRatio * 100 + '%',
208
+ width: mainContentWidthAspectRatio * 100 + '%',
209
+ left: '50%',
210
+ transform: 'translate(-50%, 0)',
211
+ paddingLeft: '5%',
212
+ paddingRight: '5%',
213
+ // 调试模式,背景色为半透明的红色
214
+ backgroundColor: debug ? 'rgba(255, 0, 0, 0.3)' : 'transparent',
215
+ zIndex: 10,
216
+ }"
217
+ >
218
+ <StatusBarContent :scaleRatio="getScaleRatio()" />
219
+ </div>
220
+
221
+ <!-- 内容区域 -->
222
+ <div
223
+ class="cosy:inset-0 cosy:h-full"
224
+ :style="{
225
+ width: mainContentWidthAspectRatio * 100 + '%',
226
+ height: mainContentHeightAspectRatio * 100 + '%',
227
+ // 水平居中
228
+ left: '50%',
229
+ // 垂直居中
230
+ top: '50%',
231
+ transform: 'translate(-50%, -50%)',
232
+ position: 'absolute',
233
+ // 调试模式,背景色为半透明的蓝色
234
+ backgroundColor: debug ? 'rgba(0, 0, 255, 0.3)' : 'transparent',
235
+ zIndex: 10,
236
+ }"
237
+ >
238
+ <div
239
+ :class="[
240
+ debug ? 'cosy:bg-green-300/50' : '',
241
+ 'cosy:h-full cosy:w-full cosy:overflow-hidden',
242
+ ]"
243
+ >
244
+ <slot />
245
+ </div>
227
246
  </div>
247
+ </div>
228
248
 
229
- <AlertDialog v-model="showAlertDialog" :message="alertMessage" />
230
- </template>
249
+ <AlertDialog v-model="showAlertDialog" :message="alertMessage" />
250
+ </template>
@@ -1,50 +1,95 @@
1
1
  <template>
2
- <li
3
- class="cosy:mb-2 cosy:rounded-md cosy:bg-base-300 cosy:p-2 cosy:flex cosy:items-center cosy:gap-3 cosy:hover:bg-accent/10 cosy:relative cosy:overflow-hidden"
2
+ <ListItemRing
3
+ v-if="animationType === 'ring'"
4
+ :loading="loading"
5
+ :duration="duration"
4
6
  @click="$emit('click')"
5
7
  >
6
- <div
7
- v-if="loading"
8
- class="cosy:absolute cosy:left-0 cosy:top-0 cosy:h-full cosy:bg-accent/40 cosy:z-0 loading-bar"
9
- :style="{ animationDuration: duration + 'ms' }"
10
- ></div>
11
- <div
12
- class="cosy:relative cosy:z-10 cosy:w-full cosy:flex cosy:items-center cosy:gap-3"
13
- >
14
- <slot />
15
- </div>
16
- </li>
8
+ <slot />
9
+ </ListItemRing>
10
+
11
+ <ListItemIconLeft
12
+ v-else-if="animationType === 'icon-left'"
13
+ :loading="loading"
14
+ :duration="duration"
15
+ @click="$emit('click')"
16
+ >
17
+ <slot />
18
+ </ListItemIconLeft>
19
+
20
+ <ListItemIconRight
21
+ v-else-if="animationType === 'icon-right'"
22
+ :loading="loading"
23
+ :duration="duration"
24
+ @click="$emit('click')"
25
+ >
26
+ <slot />
27
+ </ListItemIconRight>
28
+
29
+ <ListItemBreath
30
+ v-else-if="animationType === 'breath'"
31
+ :loading="loading"
32
+ :duration="duration"
33
+ @click="$emit('click')"
34
+ >
35
+ <slot />
36
+ </ListItemBreath>
37
+
38
+ <ListItemPulse
39
+ v-else-if="animationType === 'pulse'"
40
+ :loading="loading"
41
+ :duration="duration"
42
+ @click="$emit('click')"
43
+ >
44
+ <slot />
45
+ </ListItemPulse>
46
+
47
+ <ListItemGlow
48
+ v-else-if="animationType === 'glow'"
49
+ :loading="loading"
50
+ :duration="duration"
51
+ @click="$emit('click')"
52
+ >
53
+ <slot />
54
+ </ListItemGlow>
55
+
56
+ <!-- 默认使用 ring 动画 -->
57
+ <ListItemRing
58
+ v-else
59
+ :loading="loading"
60
+ :duration="duration"
61
+ @click="$emit('click')"
62
+ >
63
+ <slot />
64
+ </ListItemRing>
17
65
  </template>
18
66
 
19
67
  <script setup lang="ts">
20
- import { defineProps, withDefaults, defineEmits } from 'vue';
68
+ import ListItemRing from './ListItemRing.vue';
69
+ import ListItemIconLeft from './ListItemIconLeft.vue';
70
+ import ListItemIconRight from './ListItemIconRight.vue';
71
+ import ListItemBreath from './ListItemBreath.vue';
72
+ import ListItemPulse from './ListItemPulse.vue';
73
+ import ListItemGlow from './ListItemGlow.vue';
21
74
 
22
75
  withDefaults(
23
76
  defineProps<{
24
77
  loading?: boolean;
25
- duration?: number; // 进度条动画时长,毫秒
78
+ duration?: number;
79
+ animationType?:
80
+ | 'ring'
81
+ | 'icon-left'
82
+ | 'icon-right'
83
+ | 'breath'
84
+ | 'pulse'
85
+ | 'glow';
26
86
  }>(),
27
87
  {
28
88
  loading: false,
29
- duration: 1500,
89
+ duration: undefined,
90
+ animationType: 'ring',
30
91
  }
31
92
  );
32
93
 
33
94
  defineEmits(['click']);
34
95
  </script>
35
-
36
- <style scoped>
37
- .loading-bar {
38
- width: 0%;
39
- height: 100%;
40
- animation: loading-bar-anim linear forwards;
41
- }
42
- @keyframes loading-bar-anim {
43
- 0% {
44
- width: 0%;
45
- }
46
- 100% {
47
- width: 100%;
48
- }
49
- }
50
- </style>
@@ -0,0 +1,70 @@
1
+ <template>
2
+ <li
3
+ :class="[
4
+ 'cosy:mb-2 cosy:rounded-md cosy:bg-base-300 cosy:p-2 cosy:flex cosy:items-center cosy:gap-3 cosy:hover:bg-accent/10 cosy:relative cosy:overflow-hidden',
5
+ loading && !duration ? 'breath-anim' : '',
6
+ ]"
7
+ @click="$emit('click')"
8
+ >
9
+ <div v-if="loading">
10
+ <template v-if="duration">
11
+ <div
12
+ class="cosy:absolute cosy:left-0 cosy:top-0 cosy:h-full cosy:bg-accent/40 cosy:z-0 loading-bar"
13
+ :style="{ animationDuration: duration + 'ms' }"
14
+ ></div>
15
+ </template>
16
+ </div>
17
+ <div
18
+ class="cosy:relative cosy:z-10 cosy:w-full cosy:flex cosy:items-center cosy:gap-3"
19
+ >
20
+ <slot />
21
+ </div>
22
+ </li>
23
+ </template>
24
+
25
+ <script setup lang="ts">
26
+ withDefaults(
27
+ defineProps<{
28
+ loading?: boolean;
29
+ duration?: number;
30
+ }>(),
31
+ {
32
+ loading: false,
33
+ duration: undefined,
34
+ }
35
+ );
36
+
37
+ defineEmits(['click']);
38
+ </script>
39
+
40
+ <style scoped>
41
+ /* 背景色呼吸动画 */
42
+ @keyframes breath {
43
+ 0%,
44
+ 100% {
45
+ background-color: rgb(55 65 81); /* base-300 等效颜色 */
46
+ }
47
+ 50% {
48
+ background-color: rgb(139 92 246 / 0.3); /* accent 色彩 */
49
+ }
50
+ }
51
+ .breath-anim {
52
+ animation: breath 2s ease-in-out infinite;
53
+ background-color: rgb(55 65 81); /* 确保有初始背景色 */
54
+ }
55
+
56
+ /* 进度条动画 */
57
+ .loading-bar {
58
+ width: 0%;
59
+ height: 100%;
60
+ animation: loading-bar-anim linear forwards;
61
+ }
62
+ @keyframes loading-bar-anim {
63
+ 0% {
64
+ width: 0%;
65
+ }
66
+ 100% {
67
+ width: 100%;
68
+ }
69
+ }
70
+ </style>
@@ -0,0 +1,75 @@
1
+ <template>
2
+ <li
3
+ :class="[
4
+ 'cosy:mb-2 cosy:rounded-md cosy:bg-base-300 cosy:p-2 cosy:flex cosy:items-center cosy:gap-3 cosy:hover:bg-accent/10 cosy:relative cosy:overflow-hidden',
5
+ loading && !duration ? 'glow-anim' : '',
6
+ ]"
7
+ @click="$emit('click')"
8
+ >
9
+ <div v-if="loading">
10
+ <template v-if="duration">
11
+ <div
12
+ class="cosy:absolute cosy:left-0 cosy:top-0 cosy:h-full cosy:bg-accent/40 cosy:z-0 loading-bar"
13
+ :style="{ animationDuration: duration + 'ms' }"
14
+ ></div>
15
+ </template>
16
+ </div>
17
+ <div
18
+ class="cosy:relative cosy:z-10 cosy:w-full cosy:flex cosy:items-center cosy:gap-3"
19
+ >
20
+ <slot />
21
+ </div>
22
+ </li>
23
+ </template>
24
+
25
+ <script setup lang="ts">
26
+ withDefaults(
27
+ defineProps<{
28
+ loading?: boolean;
29
+ duration?: number;
30
+ }>(),
31
+ {
32
+ loading: false,
33
+ duration: undefined,
34
+ }
35
+ );
36
+
37
+ defineEmits(['click']);
38
+ </script>
39
+
40
+ <style scoped>
41
+ /* 发光边框动画 */
42
+ @keyframes glow {
43
+ 0%,
44
+ 100% {
45
+ border: 1px solid rgb(139 92 246 / 0.3);
46
+ box-shadow: 0 0 5px rgb(139 92 246 / 0.3);
47
+ }
48
+ 50% {
49
+ border: 1px solid rgb(139 92 246 / 0.8);
50
+ box-shadow:
51
+ 0 0 20px rgb(139 92 246 / 0.6),
52
+ 0 0 30px rgb(139 92 246 / 0.4),
53
+ inset 0 0 10px rgb(139 92 246 / 0.2);
54
+ }
55
+ }
56
+ .glow-anim {
57
+ animation: glow 2s ease-in-out infinite;
58
+ border: 1px solid rgb(139 92 246 / 0.3); /* 确保有初始边框 */
59
+ }
60
+
61
+ /* 进度条动画 */
62
+ .loading-bar {
63
+ width: 0%;
64
+ height: 100%;
65
+ animation: loading-bar-anim linear forwards;
66
+ }
67
+ @keyframes loading-bar-anim {
68
+ 0% {
69
+ width: 0%;
70
+ }
71
+ 100% {
72
+ width: 100%;
73
+ }
74
+ }
75
+ </style>
@@ -0,0 +1,63 @@
1
+ <template>
2
+ <li
3
+ class="cosy:mb-2 cosy:rounded-md cosy:bg-base-300 cosy:p-2 cosy:flex cosy:items-center cosy:gap-3 cosy:hover:bg-accent/10 cosy:relative cosy:overflow-hidden"
4
+ @click="$emit('click')"
5
+ >
6
+ <div v-if="loading">
7
+ <template v-if="duration">
8
+ <div
9
+ class="cosy:absolute cosy:left-0 cosy:top-0 cosy:h-full cosy:bg-accent/40 cosy:z-0 loading-bar"
10
+ :style="{ animationDuration: duration + 'ms' }"
11
+ ></div>
12
+ </template>
13
+ </div>
14
+ <div
15
+ class="cosy:relative cosy:z-10 cosy:w-full cosy:flex cosy:items-center cosy:gap-3"
16
+ >
17
+ <!-- 前置 loading 图标 -->
18
+ <div
19
+ v-if="loading && !duration"
20
+ class="cosy:flex-shrink-0 cosy:transform-none"
21
+ style="transform: none !important"
22
+ >
23
+ <div
24
+ class="cosy:loading cosy:loading-spinner cosy:loading-sm cosy:text-accent"
25
+ ></div>
26
+ </div>
27
+
28
+ <slot />
29
+ </div>
30
+ </li>
31
+ </template>
32
+
33
+ <script setup lang="ts">
34
+ withDefaults(
35
+ defineProps<{
36
+ loading?: boolean;
37
+ duration?: number;
38
+ }>(),
39
+ {
40
+ loading: false,
41
+ duration: undefined,
42
+ }
43
+ );
44
+
45
+ defineEmits(['click']);
46
+ </script>
47
+
48
+ <style scoped>
49
+ /* 进度条动画 */
50
+ .loading-bar {
51
+ width: 0%;
52
+ height: 100%;
53
+ animation: loading-bar-anim linear forwards;
54
+ }
55
+ @keyframes loading-bar-anim {
56
+ 0% {
57
+ width: 0%;
58
+ }
59
+ 100% {
60
+ width: 100%;
61
+ }
62
+ }
63
+ </style>