@wsxjs/wsx-base-components 0.0.5

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.
@@ -0,0 +1,257 @@
1
+ /* XyButton Component Styles - Migrated from xy-button.js */
2
+ :host {
3
+ position: relative;
4
+ display: inline-flex;
5
+ padding: 0.25em 0.625em;
6
+ box-sizing: border-box;
7
+ vertical-align: middle;
8
+ line-height: 1.8;
9
+ overflow: hidden;
10
+ align-items: center;
11
+ justify-content: center;
12
+ font-size: 14px;
13
+ color: var(--fontColor, #333);
14
+ border-radius: var(--borderRadius, 0.25em);
15
+ background: var(--fontColor, #333);
16
+ transition:
17
+ background 0.3s,
18
+ box-shadow 0.3s,
19
+ border-color 0.3s,
20
+ color 0.3s;
21
+ }
22
+
23
+ /* Size variants - 现代设计系统标准 */
24
+ :host([size="xxxs"]) {
25
+ padding: 0.03125em 0.0625em;
26
+ font-size: 6px;
27
+ line-height: 1;
28
+ min-width: 10px;
29
+ min-height: 8px; /* 极致微小设计 */
30
+ border-radius: 1px;
31
+ }
32
+
33
+ :host([size="xxs"]) {
34
+ padding: 0.0625em 0.125em;
35
+ font-size: 8px;
36
+ line-height: 1.1;
37
+ min-width: 14px;
38
+ min-height: 12px; /* 极致紧凑设计 */
39
+ border-radius: 2px;
40
+ }
41
+
42
+ :host([size="xs"]) {
43
+ padding: 0.125em 0.25em;
44
+ font-size: 10px;
45
+ line-height: 1.2;
46
+ min-width: 18px;
47
+ min-height: 16px; /* 真正的紧凑设计 */
48
+ border-radius: 3px;
49
+ }
50
+
51
+ :host([size="sm"]) {
52
+ padding: 0.25em 0.5em;
53
+ font-size: 12px;
54
+ line-height: 1.4;
55
+ min-width: 24px;
56
+ min-height: 20px;
57
+ border-radius: 4px;
58
+ }
59
+
60
+ :host([size="md"]) {
61
+ padding: 0.375em 0.75em;
62
+ font-size: 14px;
63
+ line-height: 1.5;
64
+ min-width: 32px;
65
+ min-height: 28px;
66
+ border-radius: 6px;
67
+ }
68
+
69
+ :host([size="lg"]) {
70
+ padding: 0.5em 1em;
71
+ font-size: 14px;
72
+ line-height: 1.6;
73
+ min-width: 40px;
74
+ min-height: 32px;
75
+ border-radius: 6px;
76
+ }
77
+
78
+ :host([size="xl"]) {
79
+ padding: 0.625em 1.25em;
80
+ font-size: 16px;
81
+ line-height: 1.6;
82
+ min-width: 48px;
83
+ min-height: 40px;
84
+ border-radius: 8px;
85
+ }
86
+
87
+ :host([shape="circle"]) {
88
+ border-radius: 50%;
89
+ }
90
+
91
+ :host([disabled]),
92
+ :host([loading]) {
93
+ pointer-events: none;
94
+ opacity: 0.6;
95
+ }
96
+
97
+ :host([block]) {
98
+ display: flex;
99
+ }
100
+
101
+ :host([disabled]:not([variant])) {
102
+ background: rgba(0, 0, 0, 0.1);
103
+ }
104
+
105
+ :host([disabled]) .btn,
106
+ :host([loading]) .btn {
107
+ cursor: not-allowed;
108
+ pointer-events: all;
109
+ }
110
+
111
+ :host(:not([variant="primary"]):not([variant="danger"]):not([disabled]):hover),
112
+ :host(:not([variant="primary"]):not([variant="danger"]):focus-within),
113
+ :host([variant="flat"][focus]) {
114
+ color: var(--themeColor, #42b983);
115
+ border-color: var(--themeColor, #42b983);
116
+ }
117
+
118
+ :host(:not([variant="primary"]):not([variant="danger"])) .btn::after {
119
+ background-image: radial-gradient(circle, var(--themeColor, #42b983) 10%, transparent 10.01%);
120
+ }
121
+
122
+ :host([variant="primary"]) {
123
+ color: #fff;
124
+ background: var(--themeBackground, var(--themeColor, #42b983));
125
+ }
126
+
127
+ :host([variant="danger"]) {
128
+ color: #fff;
129
+ background: var(--themeBackground, var(--dangerColor, #ff7875));
130
+ }
131
+
132
+ :host([variant="dashed"]) {
133
+ border-style: dashed;
134
+ }
135
+
136
+ :host([variant="flat"]),
137
+ :host([variant="primary"]),
138
+ :host([variant="danger"]) {
139
+ border: 0;
140
+ padding: calc(0.25em + 1px) calc(0.625em + 1px);
141
+ }
142
+
143
+ :host([variant="flat"]) .btn::before {
144
+ content: "";
145
+ position: absolute;
146
+ background: var(--themeColor, #42b983);
147
+ pointer-events: none;
148
+ left: 0;
149
+ right: 0;
150
+ top: 0;
151
+ bottom: 0;
152
+ opacity: 0;
153
+ transition: 0.3s;
154
+ }
155
+
156
+ :host([variant="flat"]:not([disabled]):hover) .btn::before {
157
+ opacity: 0.1;
158
+ }
159
+
160
+ :host(:not([disabled]):hover) {
161
+ z-index: 1;
162
+ }
163
+
164
+ :host([variant="flat"]:focus-within) .btn:before,
165
+ :host([variant="flat"][focus]) .btn:before {
166
+ opacity: 0.2;
167
+ }
168
+
169
+ .btn {
170
+ background: none;
171
+ outline: 0;
172
+ border: 0;
173
+ position: absolute;
174
+ left: 0;
175
+ top: 0;
176
+ width: 100%;
177
+ height: 100%;
178
+ padding: 0;
179
+ user-select: none;
180
+ cursor: unset;
181
+ }
182
+
183
+ .loading {
184
+ margin-right: 0.35em;
185
+ }
186
+
187
+ ::-moz-focus-inner {
188
+ border: 0;
189
+ }
190
+
191
+ .btn::before {
192
+ content: "";
193
+ display: block;
194
+ position: absolute;
195
+ width: 100%;
196
+ height: 100%;
197
+ left: 0;
198
+ top: 0;
199
+ transition: 0.2s;
200
+ background: #fff;
201
+ opacity: 0;
202
+ }
203
+
204
+ :host(:not([disabled]):active) .btn::before {
205
+ opacity: 0.2;
206
+ }
207
+
208
+ .btn::after {
209
+ content: "";
210
+ display: block;
211
+ position: absolute;
212
+ width: 100%;
213
+ height: 100%;
214
+ left: var(--x, 0);
215
+ top: var(--y, 0);
216
+ pointer-events: none;
217
+ background-image: radial-gradient(circle, #fff 10%, transparent 10.01%);
218
+ background-repeat: no-repeat;
219
+ background-position: 50%;
220
+ transform: translate(-50%, -50%) scale(10);
221
+ opacity: 0;
222
+ transition:
223
+ transform 0.3s,
224
+ opacity 0.8s;
225
+ }
226
+
227
+ .btn:not([disabled]):active::after {
228
+ transform: translate(-50%, -50%) scale(0);
229
+ opacity: 0.3;
230
+ transition: 0s;
231
+ }
232
+
233
+ .icon {
234
+ margin-right: 0.35em;
235
+ transition: none;
236
+ }
237
+
238
+ :host(:empty) .icon {
239
+ margin: auto;
240
+ }
241
+
242
+ :host(:empty) {
243
+ padding: 0.65em;
244
+ }
245
+
246
+ :host([type="flat"]:empty),
247
+ :host([type="primary"]:empty) {
248
+ padding: calc(0.65em + 1px);
249
+ }
250
+
251
+ ::slotted(.icon) {
252
+ transition: none;
253
+ }
254
+
255
+ :host([href]) {
256
+ cursor: pointer;
257
+ }
@@ -0,0 +1,356 @@
1
+ /** @jsxImportSource @wsxjs/wsx-core */
2
+ /**
3
+ * XyButton WSX Component - 迁移自xy-button.js
4
+ *
5
+ * 特性:
6
+ * - 支持多种按钮类型:primary、danger、flat、dashed
7
+ * - 支持disabled、loading、toggle状态
8
+ * - 支持icon显示和href链接
9
+ * - 波纹点击效果
10
+ * - 完整的样式系统和主题变量
11
+ */
12
+
13
+ import { WebComponent, autoRegister } from "@wsxjs/wsx-core";
14
+ import styles from "./XyButton.css?inline";
15
+
16
+ export interface XyButtonConfig {
17
+ disabled?: boolean;
18
+ icon?: string;
19
+ loading?: boolean;
20
+ href?: string;
21
+ type?: "button" | "submit" | "reset";
22
+ target?: string;
23
+ rel?: string;
24
+ download?: string;
25
+ toggle?: boolean;
26
+ checked?: boolean;
27
+ variant?: "primary" | "danger" | "flat" | "dashed" | "text";
28
+ shape?: "circle";
29
+ block?: boolean;
30
+ size?: "xxxs" | "xxs" | "xs" | "sm" | "md" | "lg" | "xl";
31
+ }
32
+
33
+ @autoRegister({ tagName: "xy-button" })
34
+ export default class XyButton extends WebComponent {
35
+ // 状态属性
36
+ private disabled: boolean = false;
37
+ private loading: boolean = false;
38
+ private toggle: boolean = false;
39
+ private checked: boolean = false;
40
+ private icon: string | null = null;
41
+ private href: string | null = null;
42
+ private type: "button" | "submit" | "reset" | null = null;
43
+ private target: string = "_blank";
44
+ private rel: string | null = null;
45
+ private download: string | null = null;
46
+ private variant: string = "flat";
47
+ private size: string = "md";
48
+ // DOM引用
49
+ private btnElement?: HTMLButtonElement | HTMLAnchorElement;
50
+ private iconElement?: HTMLElement;
51
+ private loadingElement?: HTMLElement;
52
+
53
+ static get observedAttributes(): string[] {
54
+ return [
55
+ "disabled",
56
+ "icon",
57
+ "loading",
58
+ "href",
59
+ "type",
60
+ "target",
61
+ "rel",
62
+ "download",
63
+ "toggle",
64
+ "checked",
65
+ "variant",
66
+ "shape",
67
+ "block",
68
+ "size",
69
+ ];
70
+ }
71
+
72
+ constructor(config: XyButtonConfig = {}) {
73
+ super({
74
+ styles,
75
+ styleName: "xy-button",
76
+ ...config,
77
+ });
78
+
79
+ // 初始化配置
80
+ this.disabled = config.disabled || false;
81
+ this.loading = config.loading || false;
82
+ this.toggle = config.toggle || false;
83
+ this.checked = config.checked || false;
84
+ this.icon = config.icon || null;
85
+ this.href = config.href || null;
86
+ this.type = (config.type as "button" | "submit" | "reset") || null;
87
+ this.target = config.target || "_blank";
88
+ this.rel = config.rel || null;
89
+ this.download = config.download || null;
90
+ this.variant = config.variant || "flat";
91
+ this.size = config.size || "md";
92
+ }
93
+
94
+ render(): HTMLElement {
95
+ const isLink = !!this.href;
96
+
97
+ const linkElement = (
98
+ <a
99
+ href={this.href}
100
+ target={this.target}
101
+ rel={this.rel || undefined}
102
+ download={this.download || undefined}
103
+ className="btn"
104
+ onMouseDown={this.handleMouseDown}
105
+ onKeyDown={this.handleKeyDown}
106
+ ref={(el: HTMLAnchorElement) => {
107
+ this.btnElement = el;
108
+ }}
109
+ >
110
+ {this.renderContent()}
111
+ </a>
112
+ );
113
+
114
+ const buttonElement = (
115
+ <button
116
+ type={this.type || "button"}
117
+ disabled={this.disabled}
118
+ className="btn"
119
+ onMouseDown={this.handleMouseDown}
120
+ onKeyDown={this.handleKeyDown}
121
+ ref={(el: HTMLButtonElement) => {
122
+ this.btnElement = el;
123
+ }}
124
+ >
125
+ {this.renderContent()}
126
+ </button>
127
+ );
128
+
129
+ return <div className="xy-button-container">{isLink ? linkElement : buttonElement}</div>;
130
+ }
131
+
132
+ private renderContent(): (HTMLElement | string)[] {
133
+ const content: (HTMLElement | string)[] = [];
134
+
135
+ // 渲染loading状态
136
+ if (this.loading) {
137
+ content.push(
138
+ <div
139
+ className="loading"
140
+ ref={(el: HTMLElement) => {
141
+ this.loadingElement = el;
142
+ }}
143
+ >
144
+ Loading...
145
+ </div>
146
+ );
147
+ }
148
+
149
+ // 渲染icon
150
+ if (!this.loading && this.icon && this.icon !== "null") {
151
+ content.push(
152
+ <div
153
+ className="icon"
154
+ data-icon={this.icon}
155
+ ref={(el: HTMLElement) => {
156
+ this.iconElement = el;
157
+ }}
158
+ >
159
+ {this.icon}
160
+ </div>
161
+ );
162
+ }
163
+
164
+ // 渲染slot内容
165
+ content.push(<slot />);
166
+
167
+ return content;
168
+ }
169
+
170
+ /**
171
+ * 处理鼠标按下事件 - 波纹效果
172
+ */
173
+ private handleMouseDown = (event: MouseEvent): void => {
174
+ if (this.disabled) return;
175
+
176
+ const rect = this.getBoundingClientRect();
177
+ const x = event.clientX - rect.left;
178
+ const y = event.clientY - rect.top;
179
+
180
+ this.style.setProperty("--x", `${x}px`);
181
+ this.style.setProperty("--y", `${y}px`);
182
+ };
183
+
184
+ /**
185
+ * 处理键盘事件
186
+ */
187
+ private handleKeyDown = (event: KeyboardEvent): void => {
188
+ if (event.code === "Enter" || event.code === "Space") {
189
+ // Enter
190
+ event.stopPropagation();
191
+ }
192
+ };
193
+
194
+ /**
195
+ * 处理点击事件
196
+ */
197
+ private handleClick = (): void => {
198
+ if (this.toggle && !this.disabled) {
199
+ this.checked = !this.checked;
200
+ if (this.checked) {
201
+ this.setAttr("checked", "");
202
+ } else {
203
+ this.removeAttr("checked");
204
+ }
205
+ }
206
+ };
207
+
208
+ /**
209
+ * 组件连接到DOM后的初始化
210
+ */
211
+ protected onConnected(): void {
212
+ this.addEventListener("click", this.handleClick);
213
+ }
214
+
215
+ /**
216
+ * 组件从DOM断开时的清理
217
+ */
218
+ protected onDisconnected(): void {
219
+ this.removeEventListener("click", this.handleClick);
220
+ }
221
+
222
+ /**
223
+ * 属性变化处理
224
+ */
225
+ protected onAttributeChanged(
226
+ name: string,
227
+ oldValue: string | null,
228
+ newValue: string | null
229
+ ): void {
230
+ switch (name) {
231
+ case "disabled":
232
+ this.disabled = newValue !== null;
233
+ this.updateButtonState();
234
+ break;
235
+ case "loading":
236
+ this.loading = newValue !== null;
237
+ this.updateButtonState();
238
+ this.rerender();
239
+ break;
240
+ case "icon":
241
+ this.icon = newValue;
242
+ this.rerender();
243
+ break;
244
+ case "href":
245
+ this.href = newValue;
246
+ this.rerender();
247
+ break;
248
+ case "type":
249
+ this.type = newValue as "button" | "submit" | "reset" | null;
250
+ this.updateButtonState();
251
+ break;
252
+ case "target":
253
+ this.target = newValue || "_blank";
254
+ break;
255
+ case "rel":
256
+ this.rel = newValue;
257
+ break;
258
+ case "download":
259
+ this.download = newValue;
260
+ break;
261
+ case "toggle":
262
+ this.toggle = newValue !== null;
263
+ break;
264
+ case "checked":
265
+ this.checked = newValue !== null;
266
+ break;
267
+ case "variant":
268
+ this.variant = newValue || "flat";
269
+ break;
270
+ case "size":
271
+ this.size = newValue || "md";
272
+ break;
273
+ }
274
+ }
275
+
276
+ /**
277
+ * 更新按钮状态
278
+ */
279
+ private updateButtonState(): void {
280
+ if (!this.btnElement) return;
281
+
282
+ if (this.disabled || this.loading) {
283
+ this.btnElement.setAttribute("disabled", "disabled");
284
+ if (this.href && this.btnElement instanceof HTMLAnchorElement) {
285
+ this.btnElement.removeAttribute("href");
286
+ }
287
+ } else {
288
+ this.btnElement.removeAttribute("disabled");
289
+ if (this.href && this.btnElement instanceof HTMLAnchorElement) {
290
+ this.btnElement.href = this.href;
291
+ }
292
+ }
293
+
294
+ if (this.type && this.btnElement instanceof HTMLButtonElement) {
295
+ this.btnElement.type = this.type;
296
+ }
297
+ }
298
+
299
+ /**
300
+ * 公共API:聚焦按钮
301
+ */
302
+ public focus(): void {
303
+ this.btnElement?.focus();
304
+ }
305
+
306
+ /**
307
+ * 公共API:获取/设置属性
308
+ */
309
+ public get isDisabled(): boolean {
310
+ return this.disabled;
311
+ }
312
+
313
+ public set isDisabled(value: boolean) {
314
+ if (value) {
315
+ this.setAttr("disabled", "");
316
+ } else {
317
+ this.removeAttr("disabled");
318
+ }
319
+ }
320
+
321
+ public get isLoading(): boolean {
322
+ return this.loading;
323
+ }
324
+
325
+ public set isLoading(value: boolean) {
326
+ if (value) {
327
+ this.setAttr("loading", "");
328
+ } else {
329
+ this.removeAttr("loading");
330
+ }
331
+ }
332
+
333
+ public get isChecked(): boolean {
334
+ return this.checked;
335
+ }
336
+
337
+ public set isChecked(value: boolean) {
338
+ if (value) {
339
+ this.setAttr("checked", "");
340
+ } else {
341
+ this.removeAttr("checked");
342
+ }
343
+ }
344
+
345
+ public get buttonIcon(): string | null {
346
+ return this.icon;
347
+ }
348
+
349
+ public set buttonIcon(value: string | null) {
350
+ if (value) {
351
+ this.setAttr("icon", value);
352
+ } else {
353
+ this.removeAttr("icon");
354
+ }
355
+ }
356
+ }
@@ -0,0 +1,30 @@
1
+ /* XyButtonGroup Component Styles - Migrated from xy-button.js */
2
+ :host {
3
+ display: inline-flex;
4
+ }
5
+
6
+ ::slotted(xy-button:not(:first-of-type):not(:last-of-type)) {
7
+ border-radius: 0;
8
+ }
9
+
10
+ ::slotted(xy-button) {
11
+ margin: 0 !important;
12
+ }
13
+
14
+ ::slotted(xy-button:not(:first-of-type)) {
15
+ margin-left: -1px !important;
16
+ }
17
+
18
+ ::slotted(xy-button[type]:not([type="dashed"]):not(:first-of-type)) {
19
+ margin-left: 1px !important;
20
+ }
21
+
22
+ ::slotted(xy-button:first-of-type) {
23
+ border-top-right-radius: 0;
24
+ border-bottom-right-radius: 0px;
25
+ }
26
+
27
+ ::slotted(xy-button:last-of-type) {
28
+ border-top-left-radius: 0;
29
+ border-bottom-left-radius: 0;
30
+ }