vanilla-jui 1.3.11 → 1.4.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.
- package/dist/index.d.ts +2927 -0
- package/dist/index.js +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/style.css +1 -1
- package/package.json +4 -3
- package/src/components/accordion.ts +522 -0
- package/src/components/drop.ts +497 -0
- package/src/components/flow.ts +1579 -0
- package/src/components/form.ts +953 -0
- package/src/components/{icons.js → icons.ts} +24 -11
- package/src/components/menu.ts +615 -0
- package/src/components/{modal.js → modal.ts} +560 -237
- package/src/components/offcanvas.ts +496 -0
- package/src/components/{pagination.js → pagination.ts} +251 -76
- package/src/components/parabola.ts +335 -0
- package/src/components/{sticky.js → sticky.ts} +123 -98
- package/src/components/{swiper.js → swiper.ts} +510 -232
- package/src/components/tabs.ts +984 -0
- package/src/components/theme.ts +437 -0
- package/src/components/toast.ts +371 -0
- package/src/components/toc.ts +385 -0
- package/src/components/tooltip.ts +179 -0
- package/src/components/validator.ts +596 -0
- package/src/core/{Component.js → Component.ts} +134 -27
- package/src/css/animation.css +31 -1
- package/src/css/card.css +1 -1
- package/src/css/color.css +863 -1013
- package/src/css/color_p3.css +822 -975
- package/src/css/components.css +1 -0
- package/src/css/form.css +1 -1
- package/src/css/icon.css +17 -1
- package/src/css/index.css +2 -2
- package/src/css/menu.css +1 -1
- package/src/css/tabs.css +1 -0
- package/src/css/tokens.css +1 -1
- package/src/index.ts +37 -0
- package/src/locales/{index.js → index.ts} +1 -2
- package/src/types.d.ts +1 -0
- package/src/utilities/browser.ts +131 -0
- package/src/utilities/cache.ts +57 -0
- package/src/utilities/core.ts +614 -0
- package/src/utilities/dom.ts +468 -0
- package/src/utilities/events.ts +106 -0
- package/src/utilities/http.ts +35 -0
- package/src/utilities/storage.ts +46 -0
- package/src/components/accordion.js +0 -373
- package/src/components/drop.js +0 -437
- package/src/components/flow.js +0 -1133
- package/src/components/form.js +0 -585
- package/src/components/menu.js +0 -422
- package/src/components/offcanvas.js +0 -332
- package/src/components/parabola.js +0 -295
- package/src/components/tabs.js +0 -746
- package/src/components/theme.js +0 -353
- package/src/components/toast.js +0 -329
- package/src/components/toc.js +0 -226
- package/src/components/tooltip.js +0 -86
- package/src/components/validator.js +0 -586
- package/src/index.js +0 -45
- package/src/locales/index.min.js +0 -1
- package/src/utilities/browser.js +0 -111
- package/src/utilities/cache.js +0 -159
- package/src/utilities/core.js +0 -502
- package/src/utilities/debug.js +0 -103
- package/src/utilities/dom.js +0 -313
- package/src/utilities/events.js +0 -69
- package/src/utilities/http.js +0 -30
- package/src/utilities/legacy-dom.js +0 -179
- package/src/utilities/storage.js +0 -46
- /package/src/{components/components.js → css/themes/index.css} +0 -0
- /package/src/icons/{index.js → index.ts} +0 -0
package/src/utilities/browser.js
DELETED
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 判断当前环境是否为移动设备。
|
|
3
|
-
* @returns {boolean}
|
|
4
|
-
*/
|
|
5
|
-
export function isMobile() {
|
|
6
|
-
// 1. 优先使用现代 Client Hints API (如果浏览器支持)
|
|
7
|
-
// 这是目前最准确、最符合隐私规范的方式
|
|
8
|
-
if (
|
|
9
|
-
navigator.userAgentData &&
|
|
10
|
-
typeof navigator.userAgentData.mobile === 'boolean'
|
|
11
|
-
) {
|
|
12
|
-
return navigator.userAgentData.mobile;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const ua = navigator.userAgent || navigator.vendor || window.opera;
|
|
16
|
-
|
|
17
|
-
// 2. 核心 UA 正则匹配 (覆盖主流手机及移动端浏览器)
|
|
18
|
-
// 注意:这里包含了 Mobile, iPhone, Android 等,但排除了明确标识为 Tablet 的安卓设备
|
|
19
|
-
const mobileUaRegex =
|
|
20
|
-
/Android|iPhone|iPad|iPod|Mobile|Windows Phone|webOS|BlackBerry|IEMobile|Opera Mini/i;
|
|
21
|
-
const isMobileUa = mobileUaRegex.test(ua);
|
|
22
|
-
|
|
23
|
-
// 3. 排除平板设备 (Tablet)
|
|
24
|
-
// 安卓平板通常包含 "Android" 但不包含 "Mobile",或者明确包含 "Tablet"
|
|
25
|
-
const isTabletUa = /iPad|Android(?!.*Mobile)|Tablet/i.test(ua);
|
|
26
|
-
|
|
27
|
-
// 4. 触摸能力检测 (作为兜底和验证)
|
|
28
|
-
// 现代 PC 也可能有触摸屏,因此触摸仅作为辅助验证,不能单独作为判定标准
|
|
29
|
-
const hasTouchSupport =
|
|
30
|
-
'maxTouchPoints' in navigator
|
|
31
|
-
? navigator.maxTouchPoints > 0
|
|
32
|
-
: 'ontouchstart' in window;
|
|
33
|
-
|
|
34
|
-
// 5. 屏幕尺寸检测 (CSS 像素)
|
|
35
|
-
// 取宽高中较小的一边,防止横屏状态下宽度超过常规手机阈值
|
|
36
|
-
const screenWidth = Math.min(window.screen.width, window.screen.height);
|
|
37
|
-
const isSmallScreen = screenWidth <= 768;
|
|
38
|
-
|
|
39
|
-
// 6. 综合判定逻辑
|
|
40
|
-
// 必须是移动端 UA,且不是平板,并且具备小屏幕或触摸特征
|
|
41
|
-
// 这样能有效防止桌面浏览器修改 UA 导致的误判,以及 iPad 伪装成 Mac 的问题
|
|
42
|
-
if (isMobileUa && !isTabletUa) {
|
|
43
|
-
// 对于 iOS 设备,UA 匹配通常足够;对于 Android,需要屏幕尺寸辅助确认
|
|
44
|
-
if (
|
|
45
|
-
/iPhone|iPod|Windows Phone|webOS|BlackBerry|IEMobile|Opera Mini/i.test(ua)
|
|
46
|
-
) {
|
|
47
|
-
return true;
|
|
48
|
-
}
|
|
49
|
-
// Android 设备需结合屏幕尺寸或触摸能力,避免大屏安卓平板被误判
|
|
50
|
-
if (/Android/i.test(ua)) {
|
|
51
|
-
return isSmallScreen || hasTouchSupport;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// 7. 针对 iOS 13+ iPad 伪装成 Mac 的特殊兜底
|
|
56
|
-
// 如果 UA 显示为 Mac,但支持多点触控且屏幕较小,极大概率是 iPad
|
|
57
|
-
const isLikelyIPad =
|
|
58
|
-
/Macintosh|MacIntel/i.test(ua) && hasTouchSupport && isSmallScreen;
|
|
59
|
-
|
|
60
|
-
return isLikelyIPad;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* 复制文本到剪贴板。
|
|
65
|
-
*
|
|
66
|
-
* 优先使用 Clipboard API,不可用时降级到 textarea + execCommand。
|
|
67
|
-
* @param {*} text 需要复制的文本。
|
|
68
|
-
* @returns {Promise<boolean>} 是否复制成功。
|
|
69
|
-
*/
|
|
70
|
-
export async function copy(text) {
|
|
71
|
-
if (typeof window === 'undefined' || typeof document === 'undefined') {
|
|
72
|
-
return Promise.resolve(false);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const value = typeof text === 'string' ? text : String(text);
|
|
76
|
-
|
|
77
|
-
if (
|
|
78
|
-
navigator.clipboard &&
|
|
79
|
-
typeof navigator.clipboard.writeText === 'function'
|
|
80
|
-
) {
|
|
81
|
-
return navigator.clipboard
|
|
82
|
-
.writeText(value)
|
|
83
|
-
.then(() => true)
|
|
84
|
-
.catch(() => fallbackCopy(value));
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
return Promise.resolve(fallbackCopy(value));
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
function fallbackCopy(text) {
|
|
91
|
-
let textarea = null;
|
|
92
|
-
|
|
93
|
-
try {
|
|
94
|
-
textarea = document.createElement('textarea');
|
|
95
|
-
textarea.value = text;
|
|
96
|
-
textarea.setAttribute('readonly', '');
|
|
97
|
-
textarea.style.position = 'absolute';
|
|
98
|
-
textarea.style.left = '-9999px';
|
|
99
|
-
textarea.style.opacity = '0';
|
|
100
|
-
document.body.appendChild(textarea);
|
|
101
|
-
textarea.select();
|
|
102
|
-
textarea.setSelectionRange(0, text.length);
|
|
103
|
-
return !!document.execCommand('copy');
|
|
104
|
-
} catch {
|
|
105
|
-
return false;
|
|
106
|
-
} finally {
|
|
107
|
-
if (textarea && textarea.parentNode) {
|
|
108
|
-
textarea.parentNode.removeChild(textarea);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
package/src/utilities/cache.js
DELETED
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
import { isFunction, randomId } from './core.js';
|
|
2
|
-
|
|
3
|
-
const instances = new Map();
|
|
4
|
-
const timers = new Map();
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* 创建或获取带缓存的类实例。
|
|
8
|
-
*
|
|
9
|
-
* 支持按 cacheKey 缓存实例,并可通过 ttl 设置空闲释放时间。
|
|
10
|
-
* @template T
|
|
11
|
-
* @param {new (...args:any[])=>T} Class 构造函数。
|
|
12
|
-
* @param {any[]} [args=[]] 构造参数。
|
|
13
|
-
* @param {string} [cacheKey=""] 缓存键;为空时自动生成。
|
|
14
|
-
* @param {boolean} [isReset=false] 是否忽略已有缓存并重新创建。
|
|
15
|
-
* @param {number|null} [ttl=null] 空闲释放时间,单位毫秒。
|
|
16
|
-
* @returns {T} 实例代理。
|
|
17
|
-
*/
|
|
18
|
-
export const singleton = (
|
|
19
|
-
Class,
|
|
20
|
-
args = [],
|
|
21
|
-
cacheKey = '',
|
|
22
|
-
isReset = false,
|
|
23
|
-
ttl = null
|
|
24
|
-
) => {
|
|
25
|
-
if (!isFunction(Class)) {
|
|
26
|
-
throw new Error('First argument expects a class/constructor function.');
|
|
27
|
-
}
|
|
28
|
-
if (!Array.isArray(args)) {
|
|
29
|
-
throw new Error('Second argument must be an array of arguments.');
|
|
30
|
-
}
|
|
31
|
-
if (typeof cacheKey !== 'string') {
|
|
32
|
-
throw new Error('Third argument expects a string.');
|
|
33
|
-
}
|
|
34
|
-
if (typeof isReset !== 'boolean') {
|
|
35
|
-
throw new Error('Fourth argument expects a boolean.');
|
|
36
|
-
}
|
|
37
|
-
if (typeof ttl !== 'number' && ttl !== null) {
|
|
38
|
-
throw new Error('Fifth argument expects a number or null.');
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const key = cacheKey.trim() === '' ? randomId() : cacheKey;
|
|
42
|
-
|
|
43
|
-
if (instances.has(key) && !isReset) {
|
|
44
|
-
return instances.get(key).proxy;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const instance = new Class(...args);
|
|
48
|
-
const proxy = new Proxy(instance, {
|
|
49
|
-
get(target, name, receiver) {
|
|
50
|
-
if (ttl) refreshTimer(key, ttl);
|
|
51
|
-
return Reflect.get(target, name, receiver);
|
|
52
|
-
},
|
|
53
|
-
set(target, name, value, receiver) {
|
|
54
|
-
return Reflect.set(target, name, value, receiver);
|
|
55
|
-
},
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
instances.set(key, { instance, proxy });
|
|
59
|
-
return proxy;
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
function refreshTimer(key, ttl) {
|
|
63
|
-
if (timers.has(key)) {
|
|
64
|
-
clearTimeout(timers.get(key));
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const timeId = setTimeout(() => {
|
|
68
|
-
release(key);
|
|
69
|
-
}, ttl);
|
|
70
|
-
|
|
71
|
-
timers.set(key, timeId);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* 释放指定缓存实例。
|
|
76
|
-
*
|
|
77
|
-
* 若实例存在 destroy 方法,会先调用 destroy。
|
|
78
|
-
* @param {string} key 缓存键。
|
|
79
|
-
* @returns {void}
|
|
80
|
-
*/
|
|
81
|
-
export function release(key) {
|
|
82
|
-
if (instances.has(key)) {
|
|
83
|
-
const { instance } = instances.get(key);
|
|
84
|
-
|
|
85
|
-
if (typeof instance.destroy === 'function') {
|
|
86
|
-
try {
|
|
87
|
-
instance.destroy();
|
|
88
|
-
} catch {
|
|
89
|
-
throw new Error(
|
|
90
|
-
`release(): destroy() failed on instance with key "${key}".`
|
|
91
|
-
);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
instances.delete(key);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
if (timers.has(key)) {
|
|
99
|
-
clearTimeout(timers.get(key));
|
|
100
|
-
timers.delete(key);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* 释放 singleton 管理的全部实例。
|
|
106
|
-
* @returns {void}
|
|
107
|
-
*/
|
|
108
|
-
export function destroyAll() {
|
|
109
|
-
for (const { instance } of instances.values()) {
|
|
110
|
-
if (typeof instance.destroy === 'function') {
|
|
111
|
-
try {
|
|
112
|
-
instance.destroy();
|
|
113
|
-
} catch {}
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
instances.clear();
|
|
118
|
-
|
|
119
|
-
for (const timerId of timers.values()) {
|
|
120
|
-
clearTimeout(timerId);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
timers.clear();
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* 简易服务缓存容器。
|
|
128
|
-
*
|
|
129
|
-
* 用于按 key 缓存工厂创建的服务实例,并提供销毁能力。
|
|
130
|
-
*/
|
|
131
|
-
export const service = {
|
|
132
|
-
instances: new Map(),
|
|
133
|
-
|
|
134
|
-
get(key, factory) {
|
|
135
|
-
if (!this.instances.has(key)) {
|
|
136
|
-
this.instances.set(key, factory());
|
|
137
|
-
}
|
|
138
|
-
return this.instances.get(key);
|
|
139
|
-
},
|
|
140
|
-
|
|
141
|
-
destroy(key) {
|
|
142
|
-
if (this.instances.has(key)) {
|
|
143
|
-
const instance = this.instances.get(key);
|
|
144
|
-
if (instance.destroy) {
|
|
145
|
-
instance.destroy();
|
|
146
|
-
}
|
|
147
|
-
this.instances.delete(key);
|
|
148
|
-
}
|
|
149
|
-
},
|
|
150
|
-
|
|
151
|
-
destroyAll() {
|
|
152
|
-
for (const instance of this.instances.values()) {
|
|
153
|
-
if (instance.destroy) {
|
|
154
|
-
instance.destroy();
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
this.instances.clear();
|
|
158
|
-
},
|
|
159
|
-
};
|