vanilla-jui 1.3.8 → 1.3.10
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.js +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/style.css +1 -1
- package/package.json +3 -2
- package/src/components/accordion.js +373 -0
- package/src/components/components.js +0 -0
- package/src/components/drop.js +437 -0
- package/src/components/flow.js +1133 -0
- package/src/components/form.js +585 -0
- package/src/components/icons.js +83 -0
- package/src/components/menu.js +422 -0
- package/src/components/modal.js +1174 -0
- package/src/components/offcanvas.js +332 -0
- package/src/components/pagination.js +536 -0
- package/src/components/parabola.js +295 -0
- package/src/components/sticky.js +271 -0
- package/src/components/swiper.js +1123 -0
- package/src/components/tabs.js +746 -0
- package/src/components/theme.js +353 -0
- package/src/components/toast.js +329 -0
- package/src/components/toc.js +226 -0
- package/src/components/tooltip.js +86 -0
- package/src/components/validator.js +586 -0
- package/src/core/Component.js +248 -0
- package/src/css/accordion.css +72 -0
- package/src/css/animation.css +177 -0
- package/src/css/avatar.css +62 -0
- package/src/css/badge.css +66 -0
- package/src/css/breadcrumb.css +106 -0
- package/src/css/button.css +296 -0
- package/src/css/card.css +151 -0
- package/src/css/color.css +1176 -0
- package/src/css/color_p3.css +1184 -0
- package/src/css/components.css +26 -0
- package/src/css/content.css +716 -0
- package/src/css/divider.css +9 -0
- package/src/css/drop.css +28 -0
- package/src/css/flow.css +86 -0
- package/src/css/font.css +23 -0
- package/src/css/form.css +919 -0
- package/src/css/gradient.css +211 -0
- package/src/css/icon.css +63 -0
- package/src/css/index.css +12 -0
- package/src/css/layout.css +782 -0
- package/src/css/loading.css +38 -0
- package/src/css/menu.css +564 -0
- package/src/css/modal.css +111 -0
- package/src/css/offcanvas.css +104 -0
- package/src/css/pagination.css +25 -0
- package/src/css/popup.css +69 -0
- package/src/css/skeleton.css +76 -0
- package/src/css/status.css +56 -0
- package/src/css/swiper.css +160 -0
- package/src/css/table.css +52 -0
- package/src/css/tabs.css +95 -0
- package/src/css/tag.css +98 -0
- package/src/css/theme-palette.css +59 -0
- package/src/css/tip.css +84 -0
- package/src/css/toast.css +154 -0
- package/src/css/toast.min.css +1 -0
- package/src/css/toc.css +38 -0
- package/src/css/tokens.css +1324 -0
- package/src/css/toolbar.css +115 -0
- package/src/css/tooltip.css +19 -0
- package/src/icons/index.js +30 -0
- package/src/index.js +45 -0
- package/src/locales/index.js +45 -0
- package/src/locales/index.min.js +1 -0
- package/src/utilities/browser.js +111 -0
- package/src/utilities/cache.js +159 -0
- package/src/utilities/core.js +502 -0
- package/src/utilities/debug.js +103 -0
- package/src/utilities/dom.js +313 -0
- package/src/utilities/events.js +69 -0
- package/src/utilities/http.js +30 -0
- package/src/utilities/legacy-dom.js +179 -0
- package/src/utilities/storage.js +46 -0
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import { createDeepStore, flushSync } from 'vanilla-signal';
|
|
2
|
+
|
|
3
|
+
import { createEventManager } from '../utilities/events.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 轻量级组件基类,集成 vanilla-signal 响应式状态和插件系统
|
|
7
|
+
* 为所有 UI 组件提供统一的状态管理、生命周期钩子和插件支持
|
|
8
|
+
*/
|
|
9
|
+
export default class Component {
|
|
10
|
+
/** 全局插件注册表,所有新创建的组件实例会自动安装这些插件 */
|
|
11
|
+
static globalPlugins = new Map();
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @param {Object} props - 组件初始属性配置
|
|
15
|
+
*/
|
|
16
|
+
constructor(props = {}) {
|
|
17
|
+
/** 组件属性配置对象 */
|
|
18
|
+
this.props = props || {};
|
|
19
|
+
|
|
20
|
+
/** DOM 引用容器,存储根元素及其他 DOM 节点引用 */
|
|
21
|
+
this.dom = { root: null };
|
|
22
|
+
|
|
23
|
+
/** 已安装的插件映射表 */
|
|
24
|
+
this.plugins = new Map();
|
|
25
|
+
|
|
26
|
+
/** 资源清理管理器,包含事件监听器和插件的清理函数 */
|
|
27
|
+
this.cleanup = { events: createEventManager(), plugins: new Map() };
|
|
28
|
+
|
|
29
|
+
/** 内部事件监听器注册表,用于生命周期和自定义事件 */
|
|
30
|
+
this._listeners = {
|
|
31
|
+
init: [], // 初始化完成事件
|
|
32
|
+
beforeUpdate: [], // 更新前事件
|
|
33
|
+
afterUpdate: [], // 更新后事件
|
|
34
|
+
destroy: [], // 销毁事件
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/** 响应式状态存储,使用 vanilla-signal 的深层响应式 store */
|
|
38
|
+
this.state = createDeepStore({});
|
|
39
|
+
|
|
40
|
+
/** 运行时状态标记 */
|
|
41
|
+
this.runtime = { destroyed: false };
|
|
42
|
+
|
|
43
|
+
/** 自动安装全局插件 */
|
|
44
|
+
this.installGlobalPlugins();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* 获取组件的根 DOM 元素
|
|
49
|
+
* @returns {HTMLElement|null} 根 DOM 元素或 null
|
|
50
|
+
*/
|
|
51
|
+
get root() {
|
|
52
|
+
return this.dom?.root || null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* 设置组件的根 DOM 元素
|
|
57
|
+
* @param {HTMLElement} value - 要设置的根 DOM 元素
|
|
58
|
+
*/
|
|
59
|
+
set root(value) {
|
|
60
|
+
if (!this.dom) this.dom = {};
|
|
61
|
+
this.dom.root = value;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* 安装实例插件
|
|
66
|
+
* 插件可以是函数形式 plugin(instance, options) 或对象形式 { install(instance, options) }
|
|
67
|
+
* 插件应返回清理函数或清理对象,在组件销毁时自动执行
|
|
68
|
+
* @param {Function|Object} plugin - 插件函数或包含 install 方法的插件对象
|
|
69
|
+
* @param {Object} [options] - 插件配置选项
|
|
70
|
+
* @returns {Component} 返回当前实例,支持链式调用
|
|
71
|
+
*/
|
|
72
|
+
use(plugin, options) {
|
|
73
|
+
if (!plugin) return this;
|
|
74
|
+
const cleanup =
|
|
75
|
+
typeof plugin === 'function'
|
|
76
|
+
? plugin(this, options)
|
|
77
|
+
: plugin.install?.(this, options);
|
|
78
|
+
this.cleanup.plugins.set(plugin, cleanup);
|
|
79
|
+
return this;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* 注册事件监听器
|
|
84
|
+
* 用于监听组件生命周期事件(init、beforeUpdate、afterUpdate、destroy)或自定义事件
|
|
85
|
+
* @param {string} event - 事件名称
|
|
86
|
+
* @param {Function} callback - 事件回调函数
|
|
87
|
+
* @returns {Component} 返回当前实例,支持链式调用
|
|
88
|
+
*/
|
|
89
|
+
on(event, callback) {
|
|
90
|
+
if (!this._listeners[event] || typeof callback !== 'function') return this;
|
|
91
|
+
this._listeners[event].push(callback);
|
|
92
|
+
return this;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* 移除事件监听器
|
|
97
|
+
* @param {string} event - 事件名称
|
|
98
|
+
* @param {Function} callback - 要移除的回调函数引用
|
|
99
|
+
* @returns {Component} 返回当前实例,支持链式调用
|
|
100
|
+
*/
|
|
101
|
+
off(event, callback) {
|
|
102
|
+
if (!this._listeners[event]) return this;
|
|
103
|
+
this._listeners[event] = this._listeners[event].filter(
|
|
104
|
+
(listener) => listener !== callback
|
|
105
|
+
);
|
|
106
|
+
return this;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* 安装全局插件到当前实例
|
|
111
|
+
* 遍历全局插件注册表并依次安装
|
|
112
|
+
* @private
|
|
113
|
+
*/
|
|
114
|
+
installGlobalPlugins() {
|
|
115
|
+
for (const plugin of Component.globalPlugins.values()) {
|
|
116
|
+
this.use(plugin);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* 注册全局插件
|
|
122
|
+
* 所有之后创建的组件实例都会自动安装此插件
|
|
123
|
+
* @static
|
|
124
|
+
* @param {string} name - 插件名称标识
|
|
125
|
+
* @param {Function|Object} plugin - 插件函数或插件对象
|
|
126
|
+
*/
|
|
127
|
+
static useGlobal(name, plugin) {
|
|
128
|
+
if (!name || !plugin) return;
|
|
129
|
+
Component.globalPlugins.set(name, plugin);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* 触发指定事件,执行所有注册的监听器
|
|
134
|
+
* @param {string} event - 事件名称
|
|
135
|
+
* @param {...*} args - 传递给监听器的参数
|
|
136
|
+
* @returns {Component} 返回当前实例,支持链式调用
|
|
137
|
+
*/
|
|
138
|
+
emit(event, ...args) {
|
|
139
|
+
const listeners = this._listeners[event];
|
|
140
|
+
if (!listeners) return this;
|
|
141
|
+
for (const listener of listeners) {
|
|
142
|
+
try {
|
|
143
|
+
listener(...args);
|
|
144
|
+
} catch {
|
|
145
|
+
// 忽略监听器执行错误,防止影响其他监听器
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return this;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* 初始化组件
|
|
153
|
+
* 合并传入的属性,调用 onInit 钩子(如果存在),并触发 init 事件
|
|
154
|
+
* @param {Object} [props={}] - 初始化属性配置
|
|
155
|
+
* @returns {Component} 返回当前实例,支持链式调用
|
|
156
|
+
* @throws {Error} 如果组件已被销毁则抛出异常
|
|
157
|
+
*/
|
|
158
|
+
init(props = {}) {
|
|
159
|
+
if (this.runtime.destroyed)
|
|
160
|
+
throw new Error('Component.init: instance destroyed');
|
|
161
|
+
if (props && typeof props === 'object' && !Array.isArray(props)) {
|
|
162
|
+
this.props = Object.assign({}, this.props, props);
|
|
163
|
+
}
|
|
164
|
+
if (typeof this.onInit === 'function') this.onInit(this.props);
|
|
165
|
+
this.emit('init', this.props);
|
|
166
|
+
return this;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* 批量更新响应式状态
|
|
171
|
+
* 支持两种调用方式:setState(key, value) 或 setState({ key1: value1, key2: value2 })
|
|
172
|
+
* 使用 flushSync 确保状态更新的同步性和批量处理
|
|
173
|
+
* @param {string|Object} keyOrPatch - 状态键名或包含多个键值对的补丁对象
|
|
174
|
+
* @param {*} [value] - 当第一个参数为字符串时的状态值
|
|
175
|
+
* @returns {Component} 返回当前实例,支持链式调用
|
|
176
|
+
* @throws {Error} 如果组件已被销毁或参数格式不正确则抛出异常
|
|
177
|
+
*/
|
|
178
|
+
setState(keyOrPatch = {}, value) {
|
|
179
|
+
if (this.runtime.destroyed)
|
|
180
|
+
throw new Error('Component.setState: instance destroyed');
|
|
181
|
+
|
|
182
|
+
const patch =
|
|
183
|
+
typeof keyOrPatch === 'string' && arguments.length > 1
|
|
184
|
+
? { [keyOrPatch]: value }
|
|
185
|
+
: keyOrPatch;
|
|
186
|
+
if (!patch || typeof patch !== 'object' || Array.isArray(patch)) {
|
|
187
|
+
throw new Error('Component.setState: expects a plain object patch.');
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
flushSync(() => {
|
|
191
|
+
for (const [k, v] of Object.entries(patch)) {
|
|
192
|
+
this.state[k] = v;
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
return this;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* 更新组件属性和触发更新生命周期
|
|
200
|
+
* 合并新的属性配置,触发 beforeUpdate 和 afterUpdate 事件
|
|
201
|
+
* 子类可以重写 onUpdate 方法实现自定义更新逻辑
|
|
202
|
+
* @param {Object} [propsPatch={}] - 要合并的属性补丁对象
|
|
203
|
+
* @param {Object} [options] - 更新选项
|
|
204
|
+
* @param {boolean} [options.force=false] - 是否强制更新(由子类处理)
|
|
205
|
+
* @returns {Component} 返回当前实例,支持链式调用
|
|
206
|
+
* @throws {Error} 如果组件已被销毁则抛出异常
|
|
207
|
+
*/
|
|
208
|
+
update(propsPatch = {}, { force = false } = {}) {
|
|
209
|
+
if (this.runtime.destroyed)
|
|
210
|
+
throw new Error('Component.update: instance destroyed');
|
|
211
|
+
if (propsPatch && typeof propsPatch === 'object') {
|
|
212
|
+
this.props = Object.assign({}, this.props, propsPatch);
|
|
213
|
+
}
|
|
214
|
+
this.emit('beforeUpdate', propsPatch, { force });
|
|
215
|
+
if (typeof this.onUpdate === 'function')
|
|
216
|
+
this.onUpdate(propsPatch, { force });
|
|
217
|
+
this.emit('afterUpdate', propsPatch, { force });
|
|
218
|
+
return this;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* 销毁组件实例
|
|
223
|
+
* 执行 onDestroy 钩子,触发 destroy 事件,清理所有插件和资源
|
|
224
|
+
* 这是组件生命周期的最后一步,销毁后实例不可再使用
|
|
225
|
+
*/
|
|
226
|
+
destroy() {
|
|
227
|
+
if (this.runtime.destroyed) return;
|
|
228
|
+
this.runtime = { destroyed: true };
|
|
229
|
+
|
|
230
|
+
if (typeof this.onDestroy === 'function') this.onDestroy();
|
|
231
|
+
this.emit('destroy');
|
|
232
|
+
|
|
233
|
+
// 执行所有插件的清理函数
|
|
234
|
+
for (const cleanup of this.cleanup.plugins.values()) {
|
|
235
|
+
try {
|
|
236
|
+
if (typeof cleanup === 'function') cleanup();
|
|
237
|
+
else if (cleanup && typeof cleanup.destroy === 'function')
|
|
238
|
+
cleanup.destroy();
|
|
239
|
+
} catch {
|
|
240
|
+
// 忽略清理函数执行错误
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
this.cleanup.plugins.clear();
|
|
244
|
+
this.cleanup.events.clear();
|
|
245
|
+
this.dom = { root: null };
|
|
246
|
+
this.state = null;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
@layer components {
|
|
2
|
+
.j-accordion {
|
|
3
|
+
--accordion-border: 1px solid var(--ui-border-subtle);
|
|
4
|
+
--accordion-icon-size: var(--font-size-lg);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.j-accordion .accordion-header {
|
|
8
|
+
display: flex;
|
|
9
|
+
align-items: center;
|
|
10
|
+
cursor: pointer;
|
|
11
|
+
padding: var(--accordion-header-padding,
|
|
12
|
+
calc(var(--space) * 2) calc(var(--space) * 3));
|
|
13
|
+
margin: var(--accordion-header-margin, 0);
|
|
14
|
+
border-bottom: var(--accordion-border);
|
|
15
|
+
font-size: var(--accordion-header-font-size, var(--font-size-md));
|
|
16
|
+
font-weight: var(--font-medium);
|
|
17
|
+
user-select: none;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.j-accordion .accordion-header:hover:not(.is-active) {
|
|
21
|
+
background: var(--accordion-header-hover-background, var(--ui-surface-hover));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.j-accordion .accordion-header.is-active {
|
|
25
|
+
border-color: transparent;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.j-accordion .accordion-header .header-title {
|
|
29
|
+
flex: 1;
|
|
30
|
+
text-align: left;
|
|
31
|
+
font-size: var(--accordion-title-font-size, var(--font-size-md));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.j-accordion .accordion-header.is-active .header-title {
|
|
35
|
+
font-weight: var(--font-bold);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.j-accordion .accordion-header .header-arrow {
|
|
39
|
+
display: flex;
|
|
40
|
+
justify-content: center;
|
|
41
|
+
align-items: center;
|
|
42
|
+
width: var(--accordion-icon-size);
|
|
43
|
+
height: var(--accordion-icon-size);
|
|
44
|
+
transition: transform 0.3s ease-out;
|
|
45
|
+
transform: rotate(0deg);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.j-accordion .accordion-header.is-active .header-arrow {
|
|
49
|
+
transform: rotate(180deg);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.j-accordion .accordion-panel {
|
|
53
|
+
display: none;
|
|
54
|
+
margin: 0;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.j-accordion .accordion-panel.is-active {
|
|
58
|
+
display: block;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.j-accordion .accordion-panel .panel-content {
|
|
62
|
+
margin: var(--accordion-panel-content-margin, 0);
|
|
63
|
+
padding: var(--accordion-panel-content-padding,
|
|
64
|
+
0 calc(var(--space) * 3) calc(var(--space) * 2));
|
|
65
|
+
border-bottom: var(--accordion-border);
|
|
66
|
+
font-size: var(--accordion-font-size, var(--font-size-md));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.j-accordion .accordion-panel:last-child .panel-content {
|
|
70
|
+
border-bottom: none;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
@layer animation {
|
|
2
|
+
/* none */
|
|
3
|
+
.animate-none {
|
|
4
|
+
animation: none;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
/* spin */
|
|
8
|
+
.animate-spin {
|
|
9
|
+
animation: animate-spin 1s linear infinite;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
@keyframes animate-spin {
|
|
13
|
+
to {
|
|
14
|
+
transform: rotate(360deg);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/* ping */
|
|
19
|
+
.animate-ping {
|
|
20
|
+
animation: animate-ping 1s cubic-bezier(0, 0, 0.2, 1) infinite;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
@keyframes animate-ping {
|
|
24
|
+
75%,
|
|
25
|
+
100% {
|
|
26
|
+
transform: scale(2);
|
|
27
|
+
opacity: 0;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/* pulse */
|
|
32
|
+
.animate-pulse {
|
|
33
|
+
animation: animate-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
@keyframes animate-pulse {
|
|
37
|
+
50% {
|
|
38
|
+
opacity: 0.5;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/* bounce */
|
|
43
|
+
.animate-bounce {
|
|
44
|
+
animation: animate-bounce 1s infinite;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
@keyframes animate-bounce {
|
|
48
|
+
0%,
|
|
49
|
+
100% {
|
|
50
|
+
transform: translateY(-25%);
|
|
51
|
+
animation-timing-function: cubic-bezier(0.8, 0, 1, 1);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
50% {
|
|
55
|
+
transform: translateY(0);
|
|
56
|
+
animation-timing-function: cubic-bezier(0, 0, 0.2, 1);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/* fade in */
|
|
61
|
+
.animate-fade-in {
|
|
62
|
+
animation: animate-fade-in 0.3s ease-in-out;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
@keyframes animate-fade-in {
|
|
66
|
+
0% {
|
|
67
|
+
opacity: 0;
|
|
68
|
+
transform: scale(0.8);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
100% {
|
|
72
|
+
opacity: 1;
|
|
73
|
+
transform: scale(1);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/* fade out */
|
|
78
|
+
.animate-fade-out {
|
|
79
|
+
animation: animate-fade-out 0.3s ease-in-out;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
@keyframes animate-fade-out {
|
|
83
|
+
0% {
|
|
84
|
+
opacity: 1;
|
|
85
|
+
transform: scale(1);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
100% {
|
|
89
|
+
opacity: 0;
|
|
90
|
+
transform: scale(0.8);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/* shake */
|
|
95
|
+
.animate-shake {
|
|
96
|
+
animation: animate-shake 0.3s ease-in-out;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
@keyframes animate-shake {
|
|
100
|
+
0%,
|
|
101
|
+
100% {
|
|
102
|
+
transform: translateX(0);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
25% {
|
|
106
|
+
transform: translateX(-10px);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
50% {
|
|
110
|
+
transform: translateX(10px);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
75% {
|
|
114
|
+
transform: translateX(-10px);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/* slide-to-top */
|
|
119
|
+
.animate-slide-to-top {
|
|
120
|
+
animation: animate-slide-to-top 0.3s ease-in-out;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
@keyframes animate-slide-to-top {
|
|
124
|
+
0% {
|
|
125
|
+
transform: translateY(0);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
100% {
|
|
129
|
+
transform: translateY(-100%);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/* slide-to-right */
|
|
134
|
+
.animate-slide-to-right {
|
|
135
|
+
animation: animate-slide-to-right 0.3s ease-in-out;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
@keyframes animate-slide-to-right {
|
|
139
|
+
0% {
|
|
140
|
+
transform: translateX(0);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
100% {
|
|
144
|
+
transform: translateX(100%);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/* slide-to-bottom */
|
|
149
|
+
.animate-slide-to-bottom {
|
|
150
|
+
animation: animate-slide-to-bottom 0.3s ease-in-out;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
@keyframes animate-slide-to-bottom {
|
|
154
|
+
0% {
|
|
155
|
+
transform: translateY(0);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
100% {
|
|
159
|
+
transform: translateY(100%);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/* slide-to-left */
|
|
164
|
+
.animate-slide-to-left {
|
|
165
|
+
animation: animate-slide-to-left 0.3s ease-in-out;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
@keyframes animate-slide-to-left {
|
|
169
|
+
0% {
|
|
170
|
+
transform: translateX(0);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
100% {
|
|
174
|
+
transform: translateX(-100%);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
@layer components {
|
|
2
|
+
.j-avatar {
|
|
3
|
+
--avatar-size: var(--el-height-md);
|
|
4
|
+
|
|
5
|
+
display: inline-block;
|
|
6
|
+
vertical-align: middle;
|
|
7
|
+
border-radius: var(--avatar-border-radius, var(--radius-md));
|
|
8
|
+
width: var(--avatar-size);
|
|
9
|
+
height: var(--avatar-size);
|
|
10
|
+
overflow: hidden;
|
|
11
|
+
user-select: none;
|
|
12
|
+
object-fit: cover;
|
|
13
|
+
box-shadow: var(--avatar-box-shadow, var(--shadow-sm));
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.j-avatar img,
|
|
17
|
+
.j-avatar svg {
|
|
18
|
+
margin: 0;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.j-avatar.is-sm {
|
|
22
|
+
--avatar-size: var(--el-height-2xs);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.j-avatar.is-lg {
|
|
26
|
+
--avatar-size: var(--el-height-lg);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.j-avatar.is-round {
|
|
30
|
+
border-radius: 0.5rem;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.j-avatar.is-text {
|
|
34
|
+
background-color: var(--tone-subtle);
|
|
35
|
+
color: var(--tone-solid);
|
|
36
|
+
font-weight: var(--font-medium);
|
|
37
|
+
display: flex;
|
|
38
|
+
justify-content: center;
|
|
39
|
+
align-items: center;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.j-avatar-group {
|
|
43
|
+
display: flex;
|
|
44
|
+
justify-content: center;
|
|
45
|
+
align-items: center;
|
|
46
|
+
flex-wrap: nowrap;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.j-avatar-group .j-avatar {
|
|
50
|
+
border: 1.5px solid var(--avatar-border-color, var(--ui-bg));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.j-avatar-group .j-avatar:not(:first-child) {
|
|
54
|
+
margin-inline-start: calc(var(--space) * -3);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.j-avatar-group .j-avatar:last-child {
|
|
58
|
+
font-size: var(--font-size-sm, 0.75rem);
|
|
59
|
+
background-color: var(--ui-bg);
|
|
60
|
+
box-shadow: var(--avatar-box-shadow, var(--shadow-sm));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
@layer components {
|
|
2
|
+
.j-badge {
|
|
3
|
+
display: inline-flex;
|
|
4
|
+
justify-content: center;
|
|
5
|
+
align-items: center;
|
|
6
|
+
padding: 0 var(--badge-padding, calc(var(--space) * 1.5));
|
|
7
|
+
min-width: var(--badge-min-width, 1.25rem);
|
|
8
|
+
height: var(--badge-height, 1.25rem);
|
|
9
|
+
line-height: var(--badge-line-height, var(--line-height-self));
|
|
10
|
+
font-size: var(--badge-font-size, var(--font-size-sm));
|
|
11
|
+
font-weight: var(--badge-font-weight, var(--font-medium));
|
|
12
|
+
border-radius: var(--badge-radius, 500px);
|
|
13
|
+
border: 1px solid var(--badge-border-color, var(--ui-border));
|
|
14
|
+
color: var(--badge-color, var(--ui-fg));
|
|
15
|
+
background-color: var(--badge-background, var(--ui-bg));
|
|
16
|
+
box-shadow: var(--badge-box-shadow, none);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.j-badge.is-sm {
|
|
20
|
+
--badge-padding: calc(var(--space) * 0.75);
|
|
21
|
+
--badge-min-width: 1rem;
|
|
22
|
+
--badge-height: 1rem;
|
|
23
|
+
--badge-font-size: 0.625rem;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.j-badge.is-contrast {
|
|
27
|
+
--badge-background: var(--ui-fg);
|
|
28
|
+
--badge-color: var(--ui-bg);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.j-badge.is-primary {
|
|
32
|
+
--badge-background: var(--tone-solid);
|
|
33
|
+
--badge-color: var(--tone-fg);
|
|
34
|
+
--badge-border-color: var(--tone-solid);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.j-badge.is-secondary {
|
|
38
|
+
--badge-background: var(--tone-soft);
|
|
39
|
+
--badge-color: var(--tone-text);
|
|
40
|
+
--badge-border-color: var(--tone-soft);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.j-badge.is-success {
|
|
44
|
+
--badge-background: var(--state-success);
|
|
45
|
+
--badge-color: var(--state-success-fg);
|
|
46
|
+
--badge-border-color: var(--state-success);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.j-badge.is-warning {
|
|
50
|
+
--badge-background: var(--state-warning);
|
|
51
|
+
--badge-color: var(--state-warning-fg);
|
|
52
|
+
--badge-border-color: var(--state-warning);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.j-badge.is-danger {
|
|
56
|
+
--badge-background: var(--state-danger);
|
|
57
|
+
--badge-color: var(--state-danger-fg);
|
|
58
|
+
--badge-border-color: var(--state-danger);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.j-badge.is-error {
|
|
62
|
+
--badge-border-color: var(--state-danger-subtle);
|
|
63
|
+
--badge-background: var(--state-danger-subtle);
|
|
64
|
+
--badge-color: var(--state-danger-text);
|
|
65
|
+
}
|
|
66
|
+
}
|