@cyberpunk-vue/theme-chalk 0.1.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.
@@ -0,0 +1,434 @@
1
+ // CpSwitch 组件样式
2
+ // 赛博朋克/机甲风格 - 升级版
3
+
4
+ @use '../common/var' as *;
5
+ @use '../mixins/mixins' as *;
6
+
7
+ // Switch 变量
8
+ $switch-sizes: (
9
+ sm: (
10
+ height: 20px, // 稍微增高以容纳细节
11
+ width: 36px,
12
+ thumb: 16px,
13
+ clip: 4px,
14
+ font-size: 10px,
15
+ padding: 2px,
16
+ ),
17
+ md: (
18
+ height: 24px,
19
+ width: 44px,
20
+ thumb: 20px,
21
+ clip: 5px,
22
+ font-size: 12px,
23
+ padding: 2px,
24
+ ),
25
+ lg: (
26
+ height: 28px,
27
+ width: 56px,
28
+ thumb: 24px,
29
+ clip: 6px,
30
+ font-size: 13px,
31
+ padding: 2px,
32
+ ),
33
+ );
34
+
35
+ @include b(switch) {
36
+ // 基础样式
37
+ position: relative;
38
+ display: inline-flex;
39
+ align-items: center;
40
+ cursor: pointer;
41
+ user-select: none;
42
+ vertical-align: middle;
43
+
44
+ // CSS 变量
45
+ --cp-switch-active-color: var(--cp-color-primary);
46
+ --cp-switch-active-color-light: var(--cp-color-primary-light);
47
+ --cp-switch-inactive-color: var(--cp-border);
48
+ --cp-switch-bg-color: rgba(20, 20, 25, 0.8);
49
+ --cp-switch-text-color: var(--cp-text-inverse);
50
+
51
+ // ===== 隐藏原生 input =====
52
+ @include e(input) {
53
+ position: absolute;
54
+ width: 0;
55
+ height: 0;
56
+ opacity: 0;
57
+ margin: 0;
58
+ }
59
+
60
+ // ===== 轨道 =====
61
+ @include e(track) {
62
+ position: relative;
63
+ display: flex;
64
+ align-items: center;
65
+ background: var(--cp-switch-bg-color);
66
+ border: 1px solid var(--cp-switch-inactive-color);
67
+ border-radius: 2px; // 轻微圆角配合切角
68
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
69
+ overflow: hidden;
70
+
71
+ // 纹理叠加
72
+ &::before {
73
+ content: '';
74
+ position: absolute;
75
+ top: 0;
76
+ left: 0;
77
+ width: 100%;
78
+ height: 100%;
79
+ background-image: repeating-linear-gradient(
80
+ 45deg,
81
+ transparent,
82
+ transparent 2px,
83
+ rgba(255, 255, 255, 0.03) 2px,
84
+ rgba(255, 255, 255, 0.03) 4px
85
+ );
86
+ z-index: 0;
87
+ }
88
+
89
+ // 装饰性细节 (角落光标)
90
+ &::after {
91
+ content: '';
92
+ position: absolute;
93
+ top: -1px;
94
+ right: -1px;
95
+ width: 6px;
96
+ height: 6px;
97
+ background: transparent;
98
+ border-top: 1px solid var(--cp-switch-inactive-color);
99
+ border-right: 1px solid var(--cp-switch-inactive-color);
100
+ clip-path: polygon(0 0, 100% 0, 100% 100%, 0 0); // 三角形
101
+ z-index: 2;
102
+ transition: all 0.3s ease;
103
+ }
104
+ }
105
+
106
+ // ===== Wrapper (Fit Text 占位) =====
107
+ // 仅在 fit-text 模式下有内容撑开
108
+ @include e(wrapper) {
109
+ display: flex;
110
+ align-items: center;
111
+ justify-content: center;
112
+ opacity: 0;
113
+ visibility: hidden;
114
+ height: 100%;
115
+ pointer-events: none;
116
+
117
+ .wrapper-text {
118
+ padding: 0 8px; // 边距
119
+ font-family: 'Rajdhani', sans-serif;
120
+ font-weight: 600;
121
+ white-space: nowrap;
122
+
123
+ // 同时渲染两个文本,取最宽的一个撑开容器
124
+ &.inactive {
125
+ display: none; // 如果不想都显示,至少要让最宽的那个生效。
126
+ // 简单起见,如果 fit-text 开启,我们让 .active 和 .inactive 叠加?
127
+ // 为了简单,这里简单处理:并排会导致过宽。
128
+ // 应该 absolute 叠加取最大宽,或者 JS 计算。
129
+ // CSS 方案:grid 布局叠加。
130
+ }
131
+ }
132
+ }
133
+
134
+ @include when(fit-text) {
135
+ width: auto;
136
+ min-width: unset;
137
+
138
+ .cp-switch__track {
139
+ min-width: 44px; // 最小宽度
140
+ width: auto;
141
+ }
142
+
143
+ .cp-switch__wrapper {
144
+ display: grid;
145
+ grid-template-areas: "content";
146
+
147
+ .wrapper-text {
148
+ grid-area: content;
149
+ display: block !important;
150
+ visibility: hidden; // 占位
151
+ }
152
+ }
153
+
154
+ // Fit Text 模式下滑块和文字的定位
155
+ // 需重新计算 thumb 位置。
156
+ // 由于布局变了,Thumb 不再是 absolute left/right,而是...
157
+ // 实际上 Fit Text 最好的方式是 Thumb 也在流中,或者依然 absolute。
158
+ // 依然 absolute,但 left/right 变为 flex 的两端对齐?
159
+ // 保持 fixed thumb width 是最简单的。
160
+ }
161
+
162
+ // ===== 滑块 =====
163
+ @include e(thumb) {
164
+ position: absolute;
165
+ top: 1px; // border 1px
166
+ left: 1px;
167
+ display: flex;
168
+ align-items: center;
169
+ justify-content: center;
170
+ background: linear-gradient(135deg, #3c3c41 0%, #2a2a2e 100%);
171
+ border: 1px solid rgba(255, 255, 255, 0.2);
172
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
173
+ transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); // 弹性动画
174
+ z-index: 10;
175
+ color: var(--cp-text-muted);
176
+
177
+ // 机甲纹理
178
+ &::before {
179
+ content: '';
180
+ position: absolute;
181
+ width: 40%;
182
+ height: 2px;
183
+ background: rgba(0, 0, 0, 0.5);
184
+ border-radius: 1px;
185
+ box-shadow: 0 1px 0 rgba(255, 255, 255, 0.1);
186
+ }
187
+
188
+ // 专属 Loading Core
189
+ .cp-switch__loading-core {
190
+ display: flex;
191
+ align-items: center;
192
+ justify-content: center;
193
+ width: 100%;
194
+ height: 100%;
195
+ color: inherit;
196
+
197
+ .loading-svg {
198
+ width: 80%;
199
+ height: 80%;
200
+ }
201
+
202
+ .outer-ring {
203
+ transform-origin: center;
204
+ animation: cp-switch-spin 1.5s linear infinite;
205
+ }
206
+
207
+ .inner-core {
208
+ transform-origin: center;
209
+ animation: cp-switch-pulse 1s ease-in-out infinite alternate;
210
+ }
211
+ }
212
+ }
213
+
214
+ // ===== 内嵌文字 =====
215
+ @include e(active-text) {
216
+ position: absolute;
217
+ top: 0;
218
+ height: 100%;
219
+ display: flex;
220
+ align-items: center;
221
+ font-family: 'Rajdhani', sans-serif;
222
+ font-weight: 600;
223
+ transition: opacity 0.2s ease;
224
+ pointer-events: none;
225
+ z-index: 1;
226
+ white-space: nowrap;
227
+
228
+ .text-inner {
229
+ display: block;
230
+ line-height: 1;
231
+ }
232
+ }
233
+
234
+ @include e(inactive-text) {
235
+ position: absolute;
236
+ top: 0;
237
+ height: 100%;
238
+ display: flex;
239
+ align-items: center;
240
+ font-family: 'Rajdhani', sans-serif;
241
+ font-weight: 600;
242
+ transition: opacity 0.2s ease;
243
+ pointer-events: none;
244
+ z-index: 1;
245
+ white-space: nowrap;
246
+
247
+ .text-inner {
248
+ display: block;
249
+ line-height: 1;
250
+ }
251
+ }
252
+
253
+ @include e(active-text) {
254
+ left: 0;
255
+ padding-left: 6px; // 初始 padding
256
+ color: var(--cp-switch-text-color);
257
+ opacity: 0;
258
+ justify-content: flex-start;
259
+ width: calc(100% - 20px); // 减去 thumb 宽度的近似值
260
+ }
261
+
262
+ @include e(inactive-text) {
263
+ right: 0;
264
+ padding-right: 6px;
265
+ color: var(--cp-text-muted);
266
+ opacity: 1;
267
+ justify-content: flex-end;
268
+ width: calc(100% - 20px);
269
+ }
270
+
271
+ // ===== 尺寸 =====
272
+ @each $size-name, $size-config in $switch-sizes {
273
+ @include m(#{$size-name}) {
274
+ $height: map-get($size-config, height);
275
+ $width: map-get($size-config, width);
276
+ $thumb-size: map-get($size-config, thumb);
277
+ $padding: map-get($size-config, padding);
278
+
279
+ // 基础固定宽度模式
280
+ &:not(.is-fit-text) {
281
+ .cp-switch__track {
282
+ height: $height;
283
+ width: $width;
284
+ }
285
+ }
286
+
287
+ // Fit Text 模式
288
+ &.is-fit-text {
289
+ .cp-switch__track {
290
+ height: $height;
291
+ // width 由内容撑开
292
+ }
293
+
294
+ // 文字边距调整
295
+ .cp-switch__active-text,
296
+ .cp-switch__inactive-text,
297
+ .wrapper-text {
298
+ width: 100%;
299
+ // 左右预留足够空间防止被 Thumb 遮挡 (Thumb宽度 + 间距)
300
+ padding: 0 calc(#{$thumb-size} + 8px);
301
+ box-sizing: border-box;
302
+ }
303
+
304
+ // wrapper-text 不需要 width 100%,它需要自然撑开
305
+ .wrapper-text {
306
+ width: auto;
307
+ }
308
+
309
+ .cp-switch__active-text { left: 0; }
310
+ .cp-switch__inactive-text { right: 0; }
311
+
312
+ // 选中时的位置修正
313
+ &.is-checked {
314
+ .cp-switch__thumb {
315
+ // 使用 left: auto, right: 1px 不支持 transition left -> right
316
+ // 所以必须通过 calc(100% - size)
317
+ left: calc(100% - #{$thumb-size} - 2px);
318
+ }
319
+ }
320
+ }
321
+
322
+ .cp-switch__track {
323
+ clip-path: polygon(
324
+ map-get($size-config, clip) 0,
325
+ 100% 0,
326
+ 100% calc(100% - #{map-get($size-config, clip)}),
327
+ calc(100% - #{map-get($size-config, clip)}) 100%,
328
+ 0 100%,
329
+ 0 map-get($size-config, clip)
330
+ );
331
+ }
332
+
333
+ .cp-switch__thumb {
334
+ width: $thumb-size;
335
+ height: $thumb-size;
336
+ clip-path: polygon(
337
+ 3px 0, 100% 0, 100% calc(100% - 3px), calc(100% - 3px) 100%, 0 100%, 0 3px
338
+ );
339
+
340
+ // 机甲纹理调整
341
+ @if $size-name == 'sm' { &::before { height: 1px; } }
342
+ }
343
+
344
+ .cp-switch__active-text,
345
+ .cp-switch__inactive-text,
346
+ .wrapper-text {
347
+ font-size: map-get($size-config, font-size);
348
+ }
349
+
350
+ // 基础模式的 Thumb 位置
351
+ &:not(.is-fit-text) {
352
+ &.is-checked {
353
+ .cp-switch__thumb {
354
+ left: calc(#{$width} - #{$thumb-size} - 2px);
355
+ }
356
+ }
357
+ }
358
+ }
359
+ }
360
+
361
+ // ===== 选中状态 =====
362
+ @include when(checked) {
363
+ .cp-switch__track {
364
+ background: rgba(0, 0, 0, 0.4); // 半透明深色底
365
+ border-color: var(--cp-switch-active-color);
366
+ box-shadow:
367
+ 0 0 10px var(--cp-switch-active-color-light);
368
+
369
+ &::after { // 角落装饰
370
+ border-color: var(--cp-switch-active-color);
371
+ box-shadow: 0 0 5px var(--cp-switch-active-color);
372
+ background: var(--cp-switch-active-color); // 实心
373
+ }
374
+
375
+ // 纹理变色 - 使用 CSS 滤镜实现染色
376
+ &::before {
377
+ background-image: repeating-linear-gradient(
378
+ 45deg,
379
+ transparent,
380
+ transparent 2px,
381
+ var(--cp-switch-active-color-light) 2px,
382
+ var(--cp-switch-active-color-light) 4px
383
+ );
384
+ }
385
+ }
386
+
387
+ .cp-switch__thumb {
388
+ background: var(--cp-switch-active-color);
389
+ border-color: rgba(255, 255, 255, 0.8);
390
+ box-shadow: 0 0 12px var(--cp-switch-active-color); // 强发光
391
+ // Loading core 颜色 - 使用深色以在亮色背景上保持可见
392
+ color: rgba(0, 0, 0, 0.7);
393
+
394
+ // Loading 动画添加发光效果增强可见度
395
+ .cp-switch__loading-core {
396
+ filter: drop-shadow(0 0 2px rgba(255, 255, 255, 0.9));
397
+ }
398
+
399
+ &::before { // 纹理
400
+ background: rgba(255, 255, 255, 0.6);
401
+ box-shadow: 0 1px 0 rgba(0, 0, 0, 0.2);
402
+ }
403
+ }
404
+
405
+ .cp-switch__active-text { opacity: 1; }
406
+ .cp-switch__inactive-text { opacity: 0; }
407
+ }
408
+
409
+ // ===== Loading 状态 =====
410
+ @include when(loading) {
411
+ cursor: wait;
412
+ .cp-switch__thumb {
413
+ &::before { display: none; } // 隐藏横杠显示 Loading
414
+ }
415
+ }
416
+
417
+ // ===== 禁用状态 =====
418
+ @include when(disabled) {
419
+ opacity: 0.6;
420
+ cursor: not-allowed;
421
+ filter: grayscale(0.8);
422
+ }
423
+ }
424
+
425
+ // 动画定义
426
+ @keyframes cp-switch-spin {
427
+ from { transform: rotate(0deg); }
428
+ to { transform: rotate(360deg); }
429
+ }
430
+
431
+ @keyframes cp-switch-pulse {
432
+ from { opacity: 0.5; transform: scale(0.8); }
433
+ to { opacity: 1; transform: scale(1.1); }
434
+ }