vanilla-jui 1.5.45 → 1.6.1

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.5.45",
3
+ "version": "1.6.1",
4
4
  "private": false,
5
5
  "description": "A lightweight, reactive, and fully customizable vanilla JS UI library.",
6
6
  "keywords": [
@@ -29,7 +29,7 @@ import {
29
29
  import {
30
30
  createValidator,
31
31
  type ValidatorClassNameConfig,
32
- } from '../validation/validator.ts';
32
+ } from '../utilities/validator.ts';
33
33
  import { createLoading } from '../primitives/loading.ts';
34
34
  import { translate } from '../utilities/locale.ts';
35
35
 
@@ -18,7 +18,7 @@
18
18
  @import url('./tooltip.css');
19
19
  @import url('./swiper.css');
20
20
  @import url('./breadcrumb.css');
21
- @import url('./table.css');
21
+ /* @import url('./table.css'); */
22
22
  @import url('./pagination.css');
23
23
  @import url('./card.css');
24
24
  @import url('./menu.css');
@@ -712,4 +712,15 @@
712
712
  .j-editor .block {
713
713
  margin-block: var(--editor-block-space);
714
714
  }
715
+
716
+ .j-editor table {
717
+ text-indent: 0;
718
+ border-color: inherit;
719
+ border-collapse: collapse;
720
+
721
+ overflow-x: auto;
722
+ scrollbar-width: thin;
723
+ scrollbar-color: var(--ui-border) transparent;
724
+ display: block;
725
+ }
715
726
  }
package/src/css/form.css CHANGED
@@ -597,11 +597,12 @@
597
597
  }
598
598
 
599
599
  .input-inner-group
600
- .el-addon:first-child.is-text:has(.j-button:not(.is-icon)) {
600
+ .el-addon:first-child.inner-wrap:has(.j-button:not(.is-icon)) {
601
601
  left: var(--space);
602
602
  }
603
603
 
604
- .input-inner-group .el-addon:last-child.is-text:has(.j-button:not(.is-icon)) {
604
+ .input-inner-group
605
+ .el-addon:last-child.inner-wrap:has(.j-button:not(.is-icon)) {
605
606
  right: var(--space);
606
607
  }
607
608
 
@@ -610,14 +611,16 @@
610
611
  height: var(--font-size-xl);
611
612
  }
612
613
 
613
- .input-inner-group .el-addon.is-text {
614
+ .input-inner-group .el-addon.inner-wrap {
614
615
  display: flex;
615
616
  gap: var(--space);
616
617
  }
617
618
 
618
619
  /* status active */
619
620
  .input-group .j-button:not(:disabled):active,
621
+ .inner-group .j-input:focus,
620
622
  .input-group .j-input:focus-within,
623
+ .inner-group .j-input:focus-visible,
621
624
  .input-group select.j-select:focus {
622
625
  z-index: 1;
623
626
  }
@@ -59,8 +59,8 @@
59
59
  user-select: none;
60
60
  }
61
61
 
62
- [hidden],
63
- [aria-hidden='true'] {
62
+ /* [aria-hidden='true'], */
63
+ [hidden] {
64
64
  display: none;
65
65
  }
66
66
  }
@@ -57,6 +57,30 @@
57
57
  --radius-lg: 1.5rem;
58
58
  }
59
59
 
60
+ .j-editor table td,
61
+ .j-editor table th {
62
+ border-width: 1px;
63
+ border-style: solid;
64
+ border-color: var(--ui-border-subtle);
65
+ padding: 0.5rem 1rem;
66
+ text-align: left;
67
+ vertical-align: top;
68
+ }
69
+
70
+ .j-editor table thead th {
71
+ background: var(--ui-bg-subtle);
72
+ font-weight: 600;
73
+ white-space: nowrap;
74
+ }
75
+
76
+ .j-editor table tbody td {
77
+ min-width: 100px;
78
+ }
79
+
80
+ .j-editor table tbody tr:hover {
81
+ background: var(--ui-bg-subtle);
82
+ }
83
+
60
84
  .mobile .j-editor h1 {
61
85
  margin-bottom: 2rem;
62
86
  }
@@ -16,7 +16,7 @@
16
16
  @import url('./swiper.css');
17
17
  @import url('./breadcrumb.css');
18
18
  @import url('./loading.css');
19
- @import url('./table.css');
19
+ /* @import url('./table.css'); */
20
20
  @import url('./pagination.css');
21
21
  @import url('./menu.css');
22
22
  @import url('./toc.css');
@@ -15,7 +15,7 @@
15
15
 
16
16
  .j-tree-list .j-tree-list::before {
17
17
  top: 0;
18
- bottom: 10px;
18
+ bottom: 12px;
19
19
  left: 7px;
20
20
  width: 1px;
21
21
  background: var(--ui-border);
@@ -23,7 +23,7 @@
23
23
 
24
24
  .j-tree-list .j-tree-list > .j-tree-item::before {
25
25
  top: 13px;
26
- left: -11px;
26
+ left: -12px;
27
27
  width: 12px;
28
28
  height: 1px;
29
29
  background: var(--ui-border);
package/src/index.ts CHANGED
@@ -13,6 +13,8 @@ export * from './utilities/timer.ts';
13
13
  export * from './utilities/types.ts';
14
14
  export * from './utilities/state.ts';
15
15
  export * from './utilities/refs.ts';
16
+ export * from './utilities/validator.ts';
17
+ export * from './utilities/compress.ts';
16
18
  export * from './core/scheduler.ts';
17
19
  export * from './core/view.ts';
18
20
  export * from './core/motion.ts';
@@ -29,9 +31,6 @@ export * from './primitives/theme.ts';
29
31
  export * from './primitives/parabola.ts';
30
32
  export * from './primitives/sticky.ts';
31
33
 
32
- // Validation
33
- export * from './validation/validator.ts';
34
-
35
34
  // Components
36
35
  export * from './core/component.ts';
37
36
  export * from './components/toc.ts';
@@ -0,0 +1,167 @@
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;
package/src/css/table.css DELETED
@@ -1,12 +0,0 @@
1
- @layer components {
2
- table {
3
- text-indent: 0;
4
- border-color: inherit;
5
- border-collapse: collapse;
6
-
7
- overflow-x: auto;
8
- scrollbar-width: thin;
9
- scrollbar-color: var(--ui-border) transparent;
10
- display: block;
11
- }
12
- }
@@ -1,25 +0,0 @@
1
- @layer themes {
2
- table td,
3
- table th {
4
- border-width: 1px;
5
- border-style: solid;
6
- border-color: var(--ui-border-subtle);
7
- padding: 0.5rem 1rem;
8
- text-align: left;
9
- vertical-align: top;
10
- }
11
-
12
- table thead th {
13
- background: var(--ui-bg-subtle);
14
- font-weight: 600;
15
- white-space: nowrap;
16
- }
17
-
18
- table tbody td {
19
- min-width: 100px;
20
- }
21
-
22
- table tbody tr:hover {
23
- background: var(--ui-bg-subtle);
24
- }
25
- }
File without changes