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.
Files changed (71) hide show
  1. package/dist/index.d.ts +2927 -0
  2. package/dist/index.js +1 -1
  3. package/dist/index.umd.js +1 -1
  4. package/dist/style.css +1 -1
  5. package/package.json +4 -3
  6. package/src/components/accordion.ts +522 -0
  7. package/src/components/drop.ts +497 -0
  8. package/src/components/flow.ts +1579 -0
  9. package/src/components/form.ts +953 -0
  10. package/src/components/{icons.js → icons.ts} +24 -11
  11. package/src/components/menu.ts +615 -0
  12. package/src/components/{modal.js → modal.ts} +560 -237
  13. package/src/components/offcanvas.ts +496 -0
  14. package/src/components/{pagination.js → pagination.ts} +251 -76
  15. package/src/components/parabola.ts +335 -0
  16. package/src/components/{sticky.js → sticky.ts} +123 -98
  17. package/src/components/{swiper.js → swiper.ts} +510 -232
  18. package/src/components/tabs.ts +984 -0
  19. package/src/components/theme.ts +437 -0
  20. package/src/components/toast.ts +371 -0
  21. package/src/components/toc.ts +385 -0
  22. package/src/components/tooltip.ts +179 -0
  23. package/src/components/validator.ts +596 -0
  24. package/src/core/{Component.js → Component.ts} +134 -27
  25. package/src/css/animation.css +31 -1
  26. package/src/css/card.css +1 -1
  27. package/src/css/color.css +863 -1013
  28. package/src/css/color_p3.css +822 -975
  29. package/src/css/components.css +1 -0
  30. package/src/css/form.css +1 -1
  31. package/src/css/icon.css +17 -1
  32. package/src/css/index.css +2 -2
  33. package/src/css/menu.css +1 -1
  34. package/src/css/tabs.css +1 -0
  35. package/src/css/tokens.css +1 -1
  36. package/src/index.ts +37 -0
  37. package/src/locales/{index.js → index.ts} +1 -2
  38. package/src/types.d.ts +1 -0
  39. package/src/utilities/browser.ts +131 -0
  40. package/src/utilities/cache.ts +57 -0
  41. package/src/utilities/core.ts +614 -0
  42. package/src/utilities/dom.ts +468 -0
  43. package/src/utilities/events.ts +106 -0
  44. package/src/utilities/http.ts +35 -0
  45. package/src/utilities/storage.ts +46 -0
  46. package/src/components/accordion.js +0 -373
  47. package/src/components/drop.js +0 -437
  48. package/src/components/flow.js +0 -1133
  49. package/src/components/form.js +0 -585
  50. package/src/components/menu.js +0 -422
  51. package/src/components/offcanvas.js +0 -332
  52. package/src/components/parabola.js +0 -295
  53. package/src/components/tabs.js +0 -746
  54. package/src/components/theme.js +0 -353
  55. package/src/components/toast.js +0 -329
  56. package/src/components/toc.js +0 -226
  57. package/src/components/tooltip.js +0 -86
  58. package/src/components/validator.js +0 -586
  59. package/src/index.js +0 -45
  60. package/src/locales/index.min.js +0 -1
  61. package/src/utilities/browser.js +0 -111
  62. package/src/utilities/cache.js +0 -159
  63. package/src/utilities/core.js +0 -502
  64. package/src/utilities/debug.js +0 -103
  65. package/src/utilities/dom.js +0 -313
  66. package/src/utilities/events.js +0 -69
  67. package/src/utilities/http.js +0 -30
  68. package/src/utilities/legacy-dom.js +0 -179
  69. package/src/utilities/storage.js +0 -46
  70. /package/src/{components/components.js → css/themes/index.css} +0 -0
  71. /package/src/icons/{index.js → index.ts} +0 -0
@@ -24,3 +24,4 @@
24
24
  @import url('./toolbar.css');
25
25
  @import url('./menu.css');
26
26
  @import url('./toc.css');
27
+ @import url('./form.css');
package/src/css/form.css CHANGED
@@ -1,4 +1,4 @@
1
- @layer form {
1
+ @layer components {
2
2
 
3
3
  .j-form,
4
4
  .j-form .form-item,
package/src/css/icon.css CHANGED
@@ -3,6 +3,16 @@
3
3
  display: block;
4
4
  }
5
5
 
6
+ .j-loader {
7
+ display: flex;
8
+ justify-content: center;
9
+ align-items: center;
10
+ background: var(--ui-bg-subtle);
11
+ width: 100%;
12
+ height: 100%;
13
+ position: absolute;
14
+ }
15
+
6
16
  /* loader icon */
7
17
  .icon-loader {
8
18
  display: inline-block;
@@ -27,6 +37,7 @@
27
37
  color: inherit;
28
38
  cursor: pointer;
29
39
  }
40
+
30
41
  .icon-close::after,
31
42
  .icon-close::before {
32
43
  content: "";
@@ -40,15 +51,19 @@
40
51
  border-radius: 1px;
41
52
  transform-origin: center center;
42
53
  }
54
+
43
55
  .icon-close::before {
44
56
  transform: translate(-50%, -50%) rotate(45deg);
45
57
  }
58
+
46
59
  .icon-close::after {
47
60
  transform: translate(-50%, -50%) rotate(-45deg);
48
61
  }
62
+
49
63
  .icon-close:active {
50
64
  transform: translateY(1px);
51
65
  }
66
+
52
67
  .icon-close:focus-within {
53
68
  background-color: var(--tone-subtle);
54
69
  }
@@ -57,7 +72,8 @@
57
72
  .el-icon[data-action-close] {
58
73
  cursor: pointer;
59
74
  }
75
+
60
76
  .el-icon[data-action-close]:active {
61
77
  transform: scale(0.85);
62
78
  }
63
- }
79
+ }
package/src/css/index.css CHANGED
@@ -1,4 +1,4 @@
1
- @layer config, layout, content, icon, font, components, menu, form, animation, status;
1
+ @layer config, layout, content, icon, font, components, themes, animation, status;
2
2
 
3
3
  @import url('./tokens.css');
4
4
  /* @import url('./gradient.css'); */
@@ -6,7 +6,7 @@
6
6
  @import url('./icon.css');
7
7
  @import url('./font.css');
8
8
  @import url('./components.css');
9
- @import url('./form.css');
10
9
  @import url('./content.css');
10
+ @import url('./themes/index.css');
11
11
  @import url('./animation.css');
12
12
  @import url('./status.css');
package/src/css/menu.css CHANGED
@@ -1,4 +1,4 @@
1
- @layer menu {
1
+ @layer components {
2
2
  .j-menu {
3
3
  width: 100%;
4
4
  white-space: nowrap;
package/src/css/tabs.css CHANGED
@@ -83,6 +83,7 @@
83
83
  display: none;
84
84
  margin: var(--tab-panel-item-margin, 0);
85
85
  position: relative;
86
+ min-height: 1.25em;
86
87
  }
87
88
 
88
89
  .j-tabs .tab-panel .panel-item.is-active {
@@ -78,7 +78,7 @@
78
78
  --columns-5xs: 12rem;
79
79
  --columns-4xs: 14rem;
80
80
  --columns-3xs: 16rem;
81
- --columns-2xs: 18px;
81
+ --columns-2xs: 18rem;
82
82
  --columns-xs: 20rem;
83
83
  --columns-sm: 24rem;
84
84
  --columns-md: 28rem;
package/src/index.ts ADDED
@@ -0,0 +1,37 @@
1
+ // CSS
2
+ import './css/index.css';
3
+
4
+ // Utilities
5
+ export * from './utilities/browser.ts';
6
+ export * from './utilities/cache.ts';
7
+ export * from './utilities/core.ts';
8
+ export * from './utilities/dom.ts';
9
+ export * from './utilities/events.ts';
10
+ export * from './utilities/http.ts';
11
+ export * from './utilities/storage.ts';
12
+
13
+ // Core
14
+ export { default as Component } from './core/Component.ts';
15
+
16
+ // Common Components
17
+ export * from './components/icons.ts';
18
+ export * from './components/theme.ts';
19
+ export * from './components/toast.ts';
20
+
21
+ // Components extending Component
22
+ // components with factory functions
23
+ export * from './components/form.ts';
24
+ export * from './components/validator.ts';
25
+ export * from './components/toc.ts';
26
+ export * from './components/sticky.ts';
27
+ export * from './components/flow.ts';
28
+ export * from './components/parabola.ts';
29
+ export * from './components/tabs.ts';
30
+ export * from './components/accordion.ts';
31
+ export * from './components/drop.ts';
32
+ export * from './components/tooltip.ts';
33
+ export * from './components/offcanvas.ts';
34
+ export * from './components/modal.ts';
35
+ export * from './components/pagination.ts';
36
+ export * from './components/swiper.ts';
37
+ export * from './components/menu.ts';
@@ -3,7 +3,6 @@ export default {
3
3
  b: 'Back',
4
4
  t: 'Customize',
5
5
  d: 'Customize your UI experience.',
6
- n: 'None',
7
6
  sm: 'Small',
8
7
  md: 'Medium',
9
8
  lg: 'Large',
@@ -32,7 +31,7 @@ export default {
32
31
  Yellow: '黄色',
33
32
  Orange: '橙色',
34
33
  Gold: '金色',
35
- n: '无',
34
+ None: '无',
36
35
  sm: '小',
37
36
  md: '中',
38
37
  lg: '大',
package/src/types.d.ts ADDED
@@ -0,0 +1 @@
1
+ declare module '*.css';
@@ -0,0 +1,131 @@
1
+ declare global {
2
+ interface Navigator {
3
+ userAgentData?: {
4
+ mobile: boolean;
5
+ brands: Array<{ brand: string; version: string }>;
6
+ platform: string;
7
+ getHighEntropyValues: (hints: string[]) => Promise<any>;
8
+ };
9
+ }
10
+ interface Window {
11
+ opera?: string;
12
+ }
13
+ }
14
+
15
+ /**
16
+ * 判断当前环境是否为移动设备。
17
+ * @returns {boolean}
18
+ */
19
+ export function isMobile(): boolean {
20
+ if (typeof window === 'undefined' || typeof navigator === 'undefined') {
21
+ return false;
22
+ }
23
+
24
+ // 1. 优先使用现代 Client Hints API
25
+ if (
26
+ navigator.userAgentData &&
27
+ typeof navigator.userAgentData.mobile === 'boolean'
28
+ ) {
29
+ return navigator.userAgentData.mobile;
30
+ }
31
+
32
+ // 提取 UA 字符串并做安全处理
33
+ const ua = navigator.userAgent || navigator.vendor || window.opera || '';
34
+
35
+ // 2. 核心 UA 正则匹配
36
+ const mobileUaRegex =
37
+ /Android|iPhone|iPad|iPod|Mobile|Windows Phone|webOS|BlackBerry|IEMobile|Opera Mini/i;
38
+ const isMobileUa = mobileUaRegex.test(ua);
39
+
40
+ // 3. 排除平板设备 (Tablet)
41
+ const isTabletUa = /iPad|Android(?!.*Mobile)|Tablet/i.test(ua);
42
+
43
+ // 4. 综合判定逻辑(针对 iOS 和 Android 分别处理)
44
+ if (isMobileUa && !isTabletUa) {
45
+ // 对于 iOS 设备,UA 匹配通常足够
46
+ if (
47
+ /iPhone|iPod|Windows Phone|webOS|BlackBerry|IEMobile|Opera Mini/i.test(ua)
48
+ ) {
49
+ return true;
50
+ }
51
+ // Android 设备需结合屏幕尺寸或触摸能力,避免大屏安卓平板被误判
52
+ if (/Android/i.test(ua)) {
53
+ const hasTouchSupport =
54
+ 'maxTouchPoints' in navigator
55
+ ? navigator.maxTouchPoints > 0
56
+ : 'ontouchstart' in window;
57
+ const screenWidth = Math.min(window.screen.width, window.screen.height);
58
+ return screenWidth <= 768 || hasTouchSupport;
59
+ }
60
+ }
61
+
62
+ // 5. 针对 iOS 13+ iPad 伪装成 Mac 的特殊兜底
63
+ const isLikelyIPad = /Macintosh|MacIntel/i.test(ua);
64
+ if (isLikelyIPad) {
65
+ const hasTouchSupport =
66
+ 'maxTouchPoints' in navigator
67
+ ? navigator.maxTouchPoints > 0
68
+ : 'ontouchstart' in window;
69
+ const screenWidth = Math.min(window.screen.width, window.screen.height);
70
+ return hasTouchSupport && screenWidth <= 768;
71
+ }
72
+
73
+ return false;
74
+ }
75
+
76
+ /**
77
+ * 复制文本到剪贴板。
78
+ *
79
+ * 优先使用 Clipboard API,不可用时降级到 textarea + execCommand。
80
+ * @param {unknown} text 需要复制的文本(接受任意类型,内部会安全转换为字符串)。
81
+ * @returns {Promise<boolean>} 是否复制成功。
82
+ */
83
+ export async function copy(text: unknown): Promise<boolean> {
84
+ if (typeof window === 'undefined' || typeof document === 'undefined') {
85
+ return Promise.resolve(false);
86
+ }
87
+
88
+ const value = String(text);
89
+
90
+ if (
91
+ navigator.clipboard &&
92
+ typeof navigator.clipboard.writeText === 'function'
93
+ ) {
94
+ try {
95
+ await navigator.clipboard.writeText(value);
96
+ return true;
97
+ } catch {
98
+ return fallbackCopy(value);
99
+ }
100
+ }
101
+
102
+ return fallbackCopy(value);
103
+ }
104
+
105
+ /**
106
+ * 降级复制方案:使用隐藏的 textarea 和 execCommand
107
+ * @param {string} text 需要复制的文本
108
+ * @returns {boolean} 是否复制成功
109
+ */
110
+ function fallbackCopy(text: string): boolean {
111
+ let textarea = null;
112
+
113
+ try {
114
+ textarea = document.createElement('textarea');
115
+ textarea.value = text;
116
+ textarea.setAttribute('readonly', '');
117
+ textarea.style.position = 'absolute';
118
+ textarea.style.left = '-9999px';
119
+ textarea.style.opacity = '0';
120
+ document.body.appendChild(textarea);
121
+ textarea.select();
122
+ textarea.setSelectionRange(0, text.length);
123
+ return document.execCommand('copy');
124
+ } catch {
125
+ return false;
126
+ } finally {
127
+ if (textarea && textarea.parentNode) {
128
+ textarea.parentNode.removeChild(textarea);
129
+ }
130
+ }
131
+ }
@@ -0,0 +1,57 @@
1
+ /**
2
+ * 定义可销毁服务的接口契约
3
+ */
4
+ export interface Destroyable {
5
+ destroy?: () => void;
6
+ }
7
+
8
+ /**
9
+ * 简易服务缓存容器。
10
+ * 用于按 key 缓存工厂创建的服务实例,并提供销毁能力。
11
+ */
12
+ export const service = (() => {
13
+ // 使用闭包封装 Map,避免 this 指向问题,实现私有化
14
+ const instances = new Map<string, Destroyable>();
15
+
16
+ return {
17
+ /**
18
+ * 获取服务实例。如果不存在,则通过工厂函数创建并缓存。
19
+ * @template T - 期望返回的服务类型(需继承自 Destroyable)
20
+ * @param {string} key - 服务的唯一标识
21
+ * @param {() => T} factory - 创建服务实例的工厂函数
22
+ * @returns {T} 服务实例
23
+ */
24
+ get<T extends Destroyable>(key: string, factory: () => T): T {
25
+ if (!instances.has(key)) {
26
+ instances.set(key, factory());
27
+ }
28
+ return instances.get(key) as T;
29
+ },
30
+
31
+ /**
32
+ * 销毁指定 key 的服务实例并从缓存中移除。
33
+ * @param {string} key - 服务的唯一标识
34
+ */
35
+ destroy(key: string): void {
36
+ const instance = instances.get(key);
37
+ if (instance) {
38
+ if (typeof instance.destroy === 'function') {
39
+ instance.destroy();
40
+ }
41
+ instances.delete(key);
42
+ }
43
+ },
44
+
45
+ /**
46
+ * 销毁所有缓存的服务实例并清空容器。
47
+ */
48
+ destroyAll(): void {
49
+ for (const instance of instances.values()) {
50
+ if (typeof instance.destroy === 'function') {
51
+ instance.destroy();
52
+ }
53
+ }
54
+ instances.clear();
55
+ },
56
+ };
57
+ })();