vanilla-jui 1.7.42 → 1.7.43

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vanilla-jui",
3
- "version": "1.7.42",
3
+ "version": "1.7.43",
4
4
  "private": false,
5
5
  "description": "A lightweight, reactive, and fully customizable vanilla JS UI library.",
6
6
  "keywords": [
package/src/css/icon.css CHANGED
@@ -1,14 +1,39 @@
1
1
  @layer icon {
2
- svg {
2
+ /* svg {
3
3
  display: block;
4
+ } */
5
+
6
+ /* icon size */
7
+ [class^='icon-'] {
8
+ --icon-size: var(--font-size-lg);
9
+ width: var(--icon-size);
10
+ height: var(--icon-size);
11
+ }
12
+
13
+ .is-xs [class^='icon-'] {
14
+ --icon-size: var(--font-size-sm);
15
+ }
16
+
17
+ .is-sm [class^='icon-'] {
18
+ --icon-size: var(--font-size-md);
19
+ }
20
+
21
+ .is-md [class^='icon-'] {
22
+ --icon-size: var(--font-size-lg);
23
+ }
24
+
25
+ .is-lg [class^='icon-'] {
26
+ --icon-size: var(--font-size-xl);
27
+ }
28
+
29
+ .is-xl [class^='icon-'] {
30
+ --icon-size: var(--font-size-2xl);
4
31
  }
5
32
 
6
33
  /* close icon */
7
34
  .icon-close {
8
35
  position: relative;
9
36
  display: block;
10
- width: var(--font-size-xl);
11
- height: var(--font-size-xl);
12
37
  color: inherit;
13
38
  }
14
39
 
@@ -18,7 +43,7 @@
18
43
  position: absolute;
19
44
  top: 50%;
20
45
  left: 50%;
21
- width: 12px;
46
+ width: 100%;
22
47
  height: 1.5px;
23
48
  background: currentColor;
24
49
  border-radius: 1px;
@@ -109,7 +109,6 @@
109
109
  transparent 300%
110
110
  );
111
111
  pointer-events: none;
112
- user-select: none;
113
112
  }
114
113
 
115
114
  .dark .j-background-grid {
@@ -899,7 +898,6 @@
899
898
  background: var(--skeleton-bg, var(--ui-bg-muted));
900
899
  color: transparent;
901
900
  pointer-events: none;
902
- user-select: none;
903
901
  animation: animate-pulse 1.5s cubic-bezier(0.4, 0, 0.6, 1) infinite;
904
902
  }
905
903
 
@@ -964,7 +962,7 @@
964
962
  Popup
965
963
  ============================================ */
966
964
  .j-popup-layout {
967
- background: var(--ui-popup-bg);
965
+ background: var(--ui-popup);
968
966
  backdrop-filter: blur(4px);
969
967
  padding: 16px;
970
968
 
@@ -17,7 +17,6 @@
17
17
  display: flex;
18
18
  align-items: center;
19
19
  cursor: pointer;
20
- user-select: none;
21
20
  }
22
21
 
23
22
  .j-accordion.is-horizontal .accordion-header {
@@ -16,7 +16,6 @@
16
16
  display: inline-block;
17
17
  vertical-align: middle;
18
18
  overflow: hidden;
19
- user-select: none;
20
19
  object-fit: cover;
21
20
  }
22
21
 
@@ -1,9 +1,9 @@
1
1
  @layer components {
2
2
  .j-badge {
3
3
  gap: var(--badge-gap, 4px);
4
- padding: var(--badge-padding, 0 6px);
5
- min-width: var(--badge-min-width, 1.25rem);
6
- height: var(--badge-height, 1.25rem);
4
+ padding: var(--badge-padding, 0 4px);
5
+ min-width: var(--badge-min-width, 20px);
6
+ height: var(--badge-height, 20px);
7
7
  font-size: var(--badge-font-size, var(--font-size-sm));
8
8
  font-weight: 500;
9
9
  border-radius: 48px;
@@ -15,7 +15,6 @@
15
15
  display: inline-flex;
16
16
  justify-content: center;
17
17
  align-items: center;
18
- user-select: none;
19
18
  }
20
19
 
21
20
  .j-badge svg {
@@ -27,7 +26,6 @@
27
26
  }
28
27
 
29
28
  .j-badge.is-sm {
30
- --badge-padding: 0 4px;
31
29
  --badge-min-width: 1rem;
32
30
  --badge-height: 1rem;
33
31
  --badge-font-size: var(--font-size-xs);
@@ -7,7 +7,6 @@
7
7
  color: var(--ui-fg-muted);
8
8
 
9
9
  white-space: nowrap;
10
- user-select: none;
11
10
  }
12
11
 
13
12
  .j-breadcrumb > ol,
@@ -21,7 +21,6 @@
21
21
  flex-wrap: nowrap;
22
22
  white-space: nowrap;
23
23
  appearance: button;
24
- user-select: none;
25
24
  touch-action: manipulation;
26
25
  transition-property: box-shadow, opacity, transform;
27
26
  transition-duration: 0.15s;
@@ -163,9 +163,8 @@
163
163
  .j-editor u {
164
164
  text-decoration: underline;
165
165
  text-decoration-thickness: 4px;
166
-
167
- text-decoration-color: var(--tone-ring);
168
- text-underline-offset: 0.2em;
166
+ text-decoration-color: var(--ui-yellow);
167
+ text-underline-offset: 3px;
169
168
  }
170
169
 
171
170
  .j-editor s,
@@ -175,8 +174,8 @@
175
174
  }
176
175
 
177
176
  .j-editor mark {
178
- background: var(--tone-highlight);
179
- color: var(--tone-highlight-fg);
177
+ background: var(--ui-highlight);
178
+ color: var(--ui-highlight-fg);
180
179
  padding: 0.1em 0.3em;
181
180
  border-radius: 0.2em;
182
181
  }
@@ -344,7 +343,6 @@
344
343
 
345
344
  .j-editor ul[data-type='taskList'] li > label {
346
345
  flex: none;
347
- user-select: none;
348
346
  }
349
347
 
350
348
  .j-editor ul[data-type='taskList'] li div {
@@ -427,7 +425,7 @@
427
425
  border: 1px solid var(--editor-border-subtle);
428
426
  border-radius: var(--radius-sm);
429
427
  word-break: break-word;
430
- background: var(--ui-code-bg);
428
+ background: var(--ui-code);
431
429
  color: var(--ui-code-fg);
432
430
  }
433
431
 
@@ -133,7 +133,6 @@
133
133
  font-size: var(--font-size-md);
134
134
  padding: 8px;
135
135
  border-radius: var(--radius-md);
136
- user-select: none;
137
136
  }
138
137
 
139
138
  .j-radio label:not(:has(input[type='radio']:disabled)):hover,
@@ -200,7 +199,6 @@
200
199
  justify-content: center;
201
200
  align-items: center;
202
201
  text-align: center;
203
- user-select: none;
204
202
  transition-property: box-shadow, color, background-color;
205
203
  transition-duration: 0.2s;
206
204
  transition-timing-function: ease;
@@ -460,11 +458,6 @@
460
458
  border-radius: var(--radius-md);
461
459
  }
462
460
 
463
- .input-group .el-addon .icon-close,
464
- .input-inner-group .el-addon .icon-close {
465
- width: var(--input-height);
466
- }
467
-
468
461
  .input-group > *:not(:first-child) {
469
462
  margin-left: -1px;
470
463
  }
@@ -551,7 +544,6 @@
551
544
  .input-inner-group .el-addon > .el-text {
552
545
  display: flex;
553
546
  align-items: center;
554
- user-select: none;
555
547
  padding: 0 8px;
556
548
  font-size: var(--font-size-md);
557
549
  color: var(--ui-fg-muted);
@@ -13,7 +13,6 @@
13
13
  scroll-behavior: smooth;
14
14
  -ms-overflow-style: none;
15
15
  scrollbar-width: none;
16
- user-select: none;
17
16
  }
18
17
 
19
18
  .j-menu::-webkit-scrollbar {
@@ -468,7 +467,6 @@
468
467
  padding: 4px 8px;
469
468
  text-decoration: none;
470
469
  color: var(--ui-fg-muted);
471
- user-select: none;
472
470
  border-radius: var(--radius-sm);
473
471
  transition: none;
474
472
  }
@@ -1,6 +1,6 @@
1
1
  @layer components {
2
2
  .j-offcanvas-overlay {
3
- background: var(--ui-popup-bg);
3
+ background: var(--ui-popup);
4
4
 
5
5
  position: fixed;
6
6
  top: 0;
@@ -8,7 +8,6 @@
8
8
  display: block;
9
9
  overflow: hidden;
10
10
  touch-action: pan-y;
11
- user-select: none;
12
11
  width: 100%;
13
12
  min-height: var(--swiper-min-height, 120px);
14
13
  border-radius: var(--radius-md);
@@ -30,7 +29,6 @@
30
29
  overflow: hidden;
31
30
  color: inherit;
32
31
  text-decoration: none;
33
- user-select: none;
34
32
  width: var(--swiper-slide-width, 100%);
35
33
  min-width: 100%;
36
34
  aspect-ratio: inherit;
@@ -118,7 +118,6 @@
118
118
  width: fit-content;
119
119
  text-align: center;
120
120
 
121
- user-select: none;
122
121
  pointer-events: none;
123
122
  }
124
123
 
@@ -9,7 +9,6 @@
9
9
 
10
10
  position: relative;
11
11
  width: fit-content;
12
- user-select: none;
13
12
  }
14
13
 
15
14
  .j-tooltip.is-reverse {
@@ -84,9 +84,11 @@
84
84
  --ui-raised: #ffffff;
85
85
 
86
86
  --ui-reverse: #000;
87
- --ui-popup-bg: rgba(0, 0, 0, 0.0625);
88
- --ui-code-bg: #f3f4f6;
87
+ --ui-popup: rgba(0, 0, 0, 0.0625);
88
+ --ui-code: #f3f4f6;
89
89
  --ui-code-fg: #374151;
90
+ --ui-highlight: #fef08a;
91
+ --ui-highlight-fg: #854d0e;
90
92
 
91
93
  /* --- Fixed Theme Colors (15 themes) --- */
92
94
  --ui-gray: #374151;
@@ -154,9 +156,6 @@
154
156
  --tone-fg: #ffffff;
155
157
  --tone-ring: rgba(99, 102, 241, 0.4);
156
158
 
157
- --tone-highlight: #fef08a;
158
- --tone-highlight-fg: #854d0e;
159
-
160
159
  /* Typography */
161
160
  --tone-heading: #111827;
162
161
  --tone-subtitle: #374151;
@@ -250,9 +249,11 @@
250
249
  --ui-raised: #151b24;
251
250
 
252
251
  --ui-reverse: #fff;
253
- --ui-popup-bg: rgba(0, 0, 0, 0.0375);
254
- --ui-code-bg: #181a1b;
252
+ --ui-popup: rgba(0, 0, 0, 0.0375);
253
+ --ui-code: #181a1b;
255
254
  --ui-code-fg: #e5e7eb;
255
+ --ui-highlight: #fbbf24;
256
+ --ui-highlight-fg: #78350f;
256
257
 
257
258
  /* --- Fixed Theme Colors (15 themes) --- */
258
259
  --ui-gray: #d1d5db;
@@ -319,8 +320,6 @@
319
320
  --tone-text-hover: #c7d2fe;
320
321
  --tone-fg: #1e1b4b;
321
322
  --tone-ring: rgba(129, 140, 248, 0.4);
322
- --tone-highlight: #fbbf24;
323
- --tone-highlight-fg: #78350f;
324
323
 
325
324
  /* Typography */
326
325
  --tone-heading: #f9fafb;
package/src/index.ts CHANGED
@@ -18,7 +18,6 @@ export * from './utilities/config.ts';
18
18
  export * from './utilities/state.ts';
19
19
  export * from './utilities/refs.ts';
20
20
  export * from './utilities/validator.ts';
21
- export * from './utilities/compress.ts';
22
21
  export * from './core/scheduler.ts';
23
22
  export * from './core/view.ts';
24
23
  export * from './core/motion.ts';
@@ -1,167 +0,0 @@
1
- export interface CompressOptions {
2
- /** 压缩质量,范围 0-1,默认 0.7 */
3
- quality?: number;
4
- /** 目标宽度(像素),不指定则按原图比例缩放 */
5
- width?: number;
6
- /** 目标高度(像素),不指定则按原图比例缩放 */
7
- height?: number;
8
- }
9
-
10
- export interface ImgCompressInstance {
11
- /**
12
- * 将图片路径转换为压缩后的 base64 数据
13
- * @param path - 图片文件路径或 base64 字符串
14
- * @param callback - 压缩完成后的回调函数,接收 base64 字符串作为参数
15
- */
16
- canvasDataURL: (path: string, callback: (base64: string) => void) => void;
17
-
18
- /**
19
- * 压缩文件对象
20
- * @param file - 文件对象(Blob 或 File)
21
- * @param callback - 压缩完成后的回调函数,接收 base64 字符串作为参数
22
- */
23
- photoCompress: (file: Blob, callback: (base64: string) => void) => void;
24
-
25
- /**
26
- * 将 base64 字符串转换为 Blob 对象
27
- * @param urlData - base64 数据(包含 data:image 前缀)
28
- * @returns Blob 对象,如果解析失败则返回 undefined
29
- */
30
- convertBase64UrlToBlob: (urlData: string) => Blob | undefined;
31
- }
32
-
33
- /** 单例缓存 */
34
- let singletonInstance: ImgCompressInstance | null = null;
35
-
36
- /**
37
- * 将 base64 字符串转换为 Blob 对象
38
- * @param urlData - base64 数据(包含 data:image 前缀)
39
- * @returns Blob 对象,如果解析失败则返回 undefined
40
- */
41
- function convertBase64UrlToBlob(urlData: string): Blob | undefined {
42
- const arr = urlData.split(',');
43
- const mimeMatch = arr[0].match(/:(.*?);/);
44
- if (!mimeMatch) return;
45
-
46
- const mime = mimeMatch[1];
47
- const binaryString = atob(arr[1]);
48
- const length = binaryString.length;
49
- const uint8Array = new Uint8Array(length);
50
-
51
- for (let i = 0; i < length; i++) {
52
- uint8Array[i] = binaryString.charCodeAt(i);
53
- }
54
-
55
- return new Blob([uint8Array], { type: mime });
56
- }
57
-
58
- /**
59
- * 创建压缩实例的核心工厂函数
60
- */
61
- function createCompressInstance(
62
- options?: CompressOptions
63
- ): ImgCompressInstance {
64
- const { quality = 0.7, width, height } = options ?? {};
65
- const config = { quality, width, height };
66
-
67
- return {
68
- canvasDataURL(path: string, callback: (base64: string) => void): void {
69
- const img = new Image();
70
- img.src = path;
71
-
72
- img.onload = (e: Event): void => {
73
- const target = e.target as HTMLImageElement;
74
- const { width: originalWidth, height: originalHeight } = target;
75
- const scale = originalWidth / originalHeight;
76
-
77
- const w = config.width ?? originalWidth;
78
- const h = config.height ?? w / scale;
79
-
80
- const canvas = document.createElement('canvas');
81
- const ctx = canvas.getContext('2d');
82
-
83
- canvas.width = w;
84
- canvas.height = h;
85
-
86
- ctx?.drawImage(target, 0, 0, w, h);
87
-
88
- const base64 = canvas.toDataURL('image/jpeg', config.quality);
89
- callback(base64);
90
- };
91
- },
92
-
93
- photoCompress(file: Blob, callback: (base64: string) => void): void {
94
- const reader = new FileReader();
95
-
96
- reader.readAsDataURL(file);
97
-
98
- reader.onload = (event: ProgressEvent<FileReader>): void => {
99
- const result = event.target?.result as string;
100
- this.canvasDataURL(result, callback);
101
- };
102
- },
103
-
104
- convertBase64UrlToBlob,
105
- };
106
- }
107
-
108
- // ==================== public api ====================
109
-
110
- /**
111
- * 创建图片压缩实例
112
- * @param options - 压缩配置选项
113
- * @param singleton - 是否返回单例,默认 true
114
- * @returns 图片压缩实例
115
- * @example
116
- * ```ts
117
- * // 获取单例实例(默认)
118
- * const imgzip = createImgCompress({ quality: 0.8, width: 800 });
119
- *
120
- * // 创建新实例
121
- * const imgzip2 = createImgCompress({ quality: 0.9 }, false);
122
- * ```
123
- */
124
- function createImgCompress(
125
- options?: CompressOptions,
126
- singleton: boolean = true
127
- ): ImgCompressInstance {
128
- if (singleton && singletonInstance) {
129
- return singletonInstance;
130
- }
131
-
132
- const instance = createCompressInstance(options);
133
-
134
- if (singleton) {
135
- singletonInstance = instance;
136
- }
137
-
138
- return instance;
139
- }
140
-
141
- /**
142
- * 获取单例实例(如果不存在则创建)
143
- * @param options - 压缩配置选项(仅在首次创建时生效)
144
- * @returns 单例实例
145
- * @example
146
- * ```ts
147
- * const imgzip = getImgCompressInstance({ quality: 0.8 });
148
- * ```
149
- */
150
- function getImgCompressInstance(
151
- options?: CompressOptions
152
- ): ImgCompressInstance {
153
- if (!singletonInstance) {
154
- singletonInstance = createCompressInstance(options);
155
- }
156
- return singletonInstance;
157
- }
158
-
159
- /**
160
- * 重置单例实例(用于测试或重新配置)
161
- */
162
- function resetImgCompressInstance(): void {
163
- singletonInstance = null;
164
- }
165
-
166
- export { createImgCompress, getImgCompressInstance, resetImgCompressInstance };
167
- export default createImgCompress;