@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.
- package/LICENSE +21 -0
- package/README.md +204 -0
- package/dist/index.cjs +6 -0
- package/dist/index.js +1085 -0
- package/package.json +57 -0
- package/src/ColorPicker.css +188 -0
- package/src/ColorPicker.wsx +416 -0
- package/src/ColorPickerUtils.ts +116 -0
- package/src/ReactiveCounter.css +304 -0
- package/src/ReactiveCounter.wsx +244 -0
- package/src/SimpleReactiveDemo.wsx +58 -0
- package/src/SvgDemo.wsx +241 -0
- package/src/SvgIcon.wsx +88 -0
- package/src/ThemeSwitcher.css +91 -0
- package/src/ThemeSwitcher.wsx +97 -0
- package/src/XyButton.css +257 -0
- package/src/XyButton.wsx +356 -0
- package/src/XyButtonGroup.css +30 -0
- package/src/XyButtonGroup.wsx +124 -0
- package/src/index.ts +17 -0
- package/src/jsx-inject.ts +2 -0
- package/src/types/wsx.d.ts +6 -0
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@wsxjs/wsx-base-components",
|
|
3
|
+
"version": "0.0.5",
|
|
4
|
+
"description": "Base UI components built with WSX Framework",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"require": "./dist/index.cjs"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"src",
|
|
17
|
+
"!**/__tests__",
|
|
18
|
+
"!**/test"
|
|
19
|
+
],
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@wsxjs/wsx-core": "0.0.5"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@typescript-eslint/eslint-plugin": "^8.37.0",
|
|
25
|
+
"@typescript-eslint/parser": "^8.37.0",
|
|
26
|
+
"eslint": "^9.31.0",
|
|
27
|
+
"http-server": "^14.1.1",
|
|
28
|
+
"tsup": "^8.0.0",
|
|
29
|
+
"typescript": "^5.0.0",
|
|
30
|
+
"vite": "^5.4.19",
|
|
31
|
+
"@wsxjs/eslint-plugin-wsx": "0.0.5",
|
|
32
|
+
"@wsxjs/wsx-vite-plugin": "0.0.5"
|
|
33
|
+
},
|
|
34
|
+
"keywords": [
|
|
35
|
+
"wsx",
|
|
36
|
+
"web-components",
|
|
37
|
+
"ui-components",
|
|
38
|
+
"typescript",
|
|
39
|
+
"jsx"
|
|
40
|
+
],
|
|
41
|
+
"author": "WSX Framework Team",
|
|
42
|
+
"license": "MIT",
|
|
43
|
+
"repository": {
|
|
44
|
+
"type": "git",
|
|
45
|
+
"url": "https://github.com/wsxjs/wsxjs.git",
|
|
46
|
+
"directory": "packages/base-components"
|
|
47
|
+
},
|
|
48
|
+
"scripts": {
|
|
49
|
+
"build": "vite build",
|
|
50
|
+
"build:dev": "NODE_ENV=development vite build",
|
|
51
|
+
"dev": "NODE_ENV=development vite build --watch",
|
|
52
|
+
"clean": "rm -rf dist",
|
|
53
|
+
"typecheck": "tsc --noEmit",
|
|
54
|
+
"lint": "eslint .",
|
|
55
|
+
"lint:fix": "eslint . --fix"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/* 通用颜色选择器样式 - 分离版本 */
|
|
2
|
+
|
|
3
|
+
/* 主容器 */
|
|
4
|
+
.color-section {
|
|
5
|
+
display: inline-block;
|
|
6
|
+
position: relative;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/* 弹出容器 */
|
|
10
|
+
.color-popover {
|
|
11
|
+
display: inline-block;
|
|
12
|
+
position: relative;
|
|
13
|
+
overflow: visible;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/* 颜色按钮 */
|
|
17
|
+
.color-btn {
|
|
18
|
+
display: inline-flex;
|
|
19
|
+
align-items: center;
|
|
20
|
+
justify-content: center;
|
|
21
|
+
width: 20px;
|
|
22
|
+
height: 20px;
|
|
23
|
+
border: 1px solid #e1e5e9;
|
|
24
|
+
border-radius: 3px;
|
|
25
|
+
background: var(--theme-color, #000);
|
|
26
|
+
color: transparent;
|
|
27
|
+
cursor: pointer;
|
|
28
|
+
transition: all 0.2s ease;
|
|
29
|
+
box-sizing: border-box;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.color-btn:hover:not(.disabled) {
|
|
33
|
+
border-color: #3f51b5;
|
|
34
|
+
box-shadow: 0 2px 8px rgba(63, 81, 181, 0.3);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.color-btn.disabled {
|
|
38
|
+
opacity: 0.5;
|
|
39
|
+
cursor: not-allowed;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.color-indicator {
|
|
43
|
+
font-size: 12px;
|
|
44
|
+
line-height: 1;
|
|
45
|
+
user-select: none;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/* 颜色面板 */
|
|
49
|
+
.color-panel {
|
|
50
|
+
position: absolute;
|
|
51
|
+
top: 100%;
|
|
52
|
+
left: 0;
|
|
53
|
+
z-index: 10000;
|
|
54
|
+
background: #fff;
|
|
55
|
+
border: 1px solid #e1e5e9;
|
|
56
|
+
border-radius: 6px;
|
|
57
|
+
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
|
|
58
|
+
padding: 8px;
|
|
59
|
+
margin-top: 4px;
|
|
60
|
+
box-sizing: border-box;
|
|
61
|
+
transform-origin: top left;
|
|
62
|
+
animation: colorPanelShow 0.2s ease-out;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
@keyframes colorPanelShow {
|
|
66
|
+
from {
|
|
67
|
+
opacity: 0;
|
|
68
|
+
transform: scale(0.9) translateY(-4px);
|
|
69
|
+
}
|
|
70
|
+
to {
|
|
71
|
+
opacity: 1;
|
|
72
|
+
transform: scale(1) translateY(0);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/* 颜色网格 */
|
|
77
|
+
.color-grid {
|
|
78
|
+
display: grid;
|
|
79
|
+
grid-template-columns: repeat(6, 1fr);
|
|
80
|
+
gap: 4px;
|
|
81
|
+
min-width: 160px;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/* 颜色立方体 */
|
|
85
|
+
.color-cube {
|
|
86
|
+
width: 20px;
|
|
87
|
+
height: 20px;
|
|
88
|
+
border: 1px solid #e1e5e9;
|
|
89
|
+
border-radius: 3px;
|
|
90
|
+
cursor: pointer;
|
|
91
|
+
transition: all 0.2s ease;
|
|
92
|
+
box-sizing: border-box;
|
|
93
|
+
background-color: var(--cube-color, #000);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.color-cube:hover {
|
|
97
|
+
border-color: #3f51b5;
|
|
98
|
+
transform: scale(1.1);
|
|
99
|
+
box-shadow: 0 2px 8px rgba(63, 81, 181, 0.4);
|
|
100
|
+
z-index: 1;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.color-cube:active {
|
|
104
|
+
transform: scale(0.95);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/* 自定义颜色选择器 */
|
|
108
|
+
.color-cube.custom-picker {
|
|
109
|
+
background: linear-gradient(
|
|
110
|
+
45deg,
|
|
111
|
+
#ff0000 0%,
|
|
112
|
+
#ff8000 14%,
|
|
113
|
+
#ffff00 28%,
|
|
114
|
+
#80ff00 42%,
|
|
115
|
+
#00ff00 57%,
|
|
116
|
+
#00ff80 71%,
|
|
117
|
+
#00ffff 85%,
|
|
118
|
+
#0080ff 100%
|
|
119
|
+
);
|
|
120
|
+
position: relative;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.color-cube.custom-picker::after {
|
|
124
|
+
content: "+";
|
|
125
|
+
position: absolute;
|
|
126
|
+
top: 50%;
|
|
127
|
+
left: 50%;
|
|
128
|
+
transform: translate(-50%, -50%);
|
|
129
|
+
color: #fff;
|
|
130
|
+
font-size: 12px;
|
|
131
|
+
font-weight: bold;
|
|
132
|
+
text-shadow: 0 0 2px rgba(0, 0, 0, 0.8);
|
|
133
|
+
pointer-events: none;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/* 主机元素状态 */
|
|
137
|
+
:host([disabled]) .color-btn {
|
|
138
|
+
opacity: 0.5;
|
|
139
|
+
cursor: not-allowed;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
:host([open]) .color-panel {
|
|
143
|
+
display: block;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
:host(:not([open])) .color-panel {
|
|
147
|
+
display: none;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/* 响应式设计 */
|
|
151
|
+
@media (max-width: 768px) {
|
|
152
|
+
.color-panel {
|
|
153
|
+
position: fixed;
|
|
154
|
+
top: auto;
|
|
155
|
+
left: 50%;
|
|
156
|
+
bottom: 10px;
|
|
157
|
+
transform: translateX(-50%);
|
|
158
|
+
width: 90vw;
|
|
159
|
+
max-width: 300px;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.color-grid {
|
|
163
|
+
grid-template-columns: repeat(8, 1fr);
|
|
164
|
+
gap: 6px;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.color-cube {
|
|
168
|
+
width: 24px;
|
|
169
|
+
height: 24px;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/* 深色模式支持 */
|
|
174
|
+
@media (prefers-color-scheme: dark) {
|
|
175
|
+
.color-panel {
|
|
176
|
+
background: #2d2d2d;
|
|
177
|
+
border-color: #404040;
|
|
178
|
+
color: #fff;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.color-btn {
|
|
182
|
+
border-color: #404040;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.color-cube {
|
|
186
|
+
border-color: #404040;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
/** @jsxImportSource @wsxjs/wsx-core */
|
|
2
|
+
/**
|
|
3
|
+
* 通用WSX颜色选择器 - Web Component示例
|
|
4
|
+
*
|
|
5
|
+
* 使用 .wsx 扩展名享受真正的TSX语法:
|
|
6
|
+
* - 继承EditorJSToolComponent
|
|
7
|
+
* - 真正的JSX语法支持
|
|
8
|
+
* - 支持外部CSS文件
|
|
9
|
+
* - 完全通用,不依赖特定UI库
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { WebComponent, autoRegister, createLogger } from "@wsxjs/wsx-core";
|
|
13
|
+
import styles from "./ColorPicker.css?inline";
|
|
14
|
+
import {
|
|
15
|
+
handleCSSVariables,
|
|
16
|
+
setDefaultColorCache,
|
|
17
|
+
getCustomColorCache,
|
|
18
|
+
setCustomColorCache,
|
|
19
|
+
throttle,
|
|
20
|
+
} from "./ColorPickerUtils";
|
|
21
|
+
import type { PluginType } from "./ColorPickerUtils";
|
|
22
|
+
|
|
23
|
+
const logger = createLogger("ColorPicker");
|
|
24
|
+
|
|
25
|
+
// 导出ColorPickerUtils方法 用于外部调用
|
|
26
|
+
export * from "./ColorPickerUtils";
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* 颜色选择器配置接口
|
|
30
|
+
*/
|
|
31
|
+
export interface ColorPickerConfig {
|
|
32
|
+
colorCollections?: string[];
|
|
33
|
+
onColorPicked?: (color: string) => void;
|
|
34
|
+
hasCustomPicker?: boolean;
|
|
35
|
+
defaultColor?: string;
|
|
36
|
+
pluginType?: PluginType;
|
|
37
|
+
disabled?: boolean;
|
|
38
|
+
api?: unknown; // EditorJS API
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* 默认颜色集合
|
|
43
|
+
*/
|
|
44
|
+
const DEFAULT_COLORS = [
|
|
45
|
+
"#ff1300",
|
|
46
|
+
"#EC7878",
|
|
47
|
+
"#9C27B0",
|
|
48
|
+
"#673AB7",
|
|
49
|
+
"#3F51B5",
|
|
50
|
+
"#0070FF",
|
|
51
|
+
"#03A9F4",
|
|
52
|
+
"#00BCD4",
|
|
53
|
+
"#4CAF50",
|
|
54
|
+
"#8BC34A",
|
|
55
|
+
"#CDDC39",
|
|
56
|
+
"#FFE500",
|
|
57
|
+
"#FFBF00",
|
|
58
|
+
"#FF9800",
|
|
59
|
+
"#795548",
|
|
60
|
+
"#9E9E9E",
|
|
61
|
+
"#5A5A5A",
|
|
62
|
+
"#FFF",
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* 颜色选择器组件 Web Component with JSX aka WSX
|
|
67
|
+
*/
|
|
68
|
+
@autoRegister({ tagName: "color-picker" })
|
|
69
|
+
export default class ColorPicker extends WebComponent {
|
|
70
|
+
private colorCollections: string[];
|
|
71
|
+
private onColorPicked?: (color: string) => void;
|
|
72
|
+
private hasCustomPicker: boolean;
|
|
73
|
+
private defaultColor: string;
|
|
74
|
+
private pluginType: PluginType;
|
|
75
|
+
private disabled: boolean;
|
|
76
|
+
|
|
77
|
+
// 状态
|
|
78
|
+
private selectedColor: string;
|
|
79
|
+
private customColor: string;
|
|
80
|
+
private isOpen: boolean;
|
|
81
|
+
|
|
82
|
+
// DOM引用
|
|
83
|
+
private colorBtn?: HTMLElement;
|
|
84
|
+
private colorPanel?: HTMLElement;
|
|
85
|
+
|
|
86
|
+
static get observedAttributes(): string[] {
|
|
87
|
+
return ["disabled", "selected-color", "open"];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
constructor(config: ColorPickerConfig = {}) {
|
|
91
|
+
super({
|
|
92
|
+
styles,
|
|
93
|
+
styleName: "base-color-picker",
|
|
94
|
+
...config,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// 初始化配置
|
|
98
|
+
this.colorCollections = config.colorCollections || DEFAULT_COLORS;
|
|
99
|
+
this.onColorPicked = config.onColorPicked;
|
|
100
|
+
this.hasCustomPicker = config.hasCustomPicker || false;
|
|
101
|
+
this.pluginType = config.pluginType || "text";
|
|
102
|
+
this.disabled = config.disabled || false;
|
|
103
|
+
|
|
104
|
+
// 初始化状态
|
|
105
|
+
this.defaultColor = handleCSSVariables(config.defaultColor || this.colorCollections[0]);
|
|
106
|
+
this.selectedColor = this.defaultColor;
|
|
107
|
+
this.customColor = getCustomColorCache(this.pluginType) || "";
|
|
108
|
+
this.isOpen = false;
|
|
109
|
+
|
|
110
|
+
logger.debug("ColorPicker initialized", {
|
|
111
|
+
colorCollections: this.colorCollections.length,
|
|
112
|
+
hasCustomPicker: this.hasCustomPicker,
|
|
113
|
+
pluginType: this.pluginType,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* 实现抽象方法:真正的JSX渲染!🎉
|
|
119
|
+
*/
|
|
120
|
+
render(): HTMLElement {
|
|
121
|
+
const colorButton = this.renderColorButton();
|
|
122
|
+
const colorPanel = this.isOpen ? this.renderColorPanel() : null;
|
|
123
|
+
|
|
124
|
+
return (
|
|
125
|
+
<section class="color-section">
|
|
126
|
+
<div class="color-popover">
|
|
127
|
+
{colorButton}
|
|
128
|
+
{colorPanel}
|
|
129
|
+
</div>
|
|
130
|
+
</section>
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* 渲染颜色按钮 - 真正的JSX!
|
|
136
|
+
*/
|
|
137
|
+
private renderColorButton(): HTMLElement {
|
|
138
|
+
return (
|
|
139
|
+
<xy-button
|
|
140
|
+
type="button"
|
|
141
|
+
class={`color-btn ${this.disabled ? "disabled" : ""}`}
|
|
142
|
+
style={`--theme-color: ${this.selectedColor}`}
|
|
143
|
+
disabled={this.disabled}
|
|
144
|
+
onClick={this.handleButtonClick}
|
|
145
|
+
ref={(el: HTMLButtonElement) => {
|
|
146
|
+
this.colorBtn = el;
|
|
147
|
+
}}
|
|
148
|
+
>
|
|
149
|
+
<span class="color-indicator">_</span>
|
|
150
|
+
</xy-button>
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* 渲染颜色面板 - 真正的JSX!
|
|
156
|
+
*/
|
|
157
|
+
private renderColorPanel(): HTMLElement {
|
|
158
|
+
return (
|
|
159
|
+
<div
|
|
160
|
+
class="color-panel"
|
|
161
|
+
onClick={this.handlePanelClick}
|
|
162
|
+
ref={(el: HTMLDivElement) => {
|
|
163
|
+
this.colorPanel = el;
|
|
164
|
+
}}
|
|
165
|
+
>
|
|
166
|
+
<div class="color-grid">
|
|
167
|
+
{this.hasCustomPicker ? this.renderCustomPicker() : null}
|
|
168
|
+
{this.renderColorButtons()}
|
|
169
|
+
</div>
|
|
170
|
+
</div>
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* 渲染自定义颜色选择器 - 真正的JSX!
|
|
176
|
+
*/
|
|
177
|
+
private renderCustomPicker(): HTMLElement {
|
|
178
|
+
return (
|
|
179
|
+
<xy-button
|
|
180
|
+
type="button"
|
|
181
|
+
class="color-cube custom-picker"
|
|
182
|
+
style={{ backgroundColor: this.customColor }}
|
|
183
|
+
onClick={this.handleCustomPickerClick}
|
|
184
|
+
title="自定义颜色"
|
|
185
|
+
/>
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* 渲染颜色按钮组 - 真正的JSX!
|
|
191
|
+
*/
|
|
192
|
+
private renderColorButtons(): HTMLElement[] {
|
|
193
|
+
return this.colorCollections.map((color) => (
|
|
194
|
+
<xy-button
|
|
195
|
+
key={color}
|
|
196
|
+
type="button"
|
|
197
|
+
class="color-cube"
|
|
198
|
+
style={`background-color: ${color}`}
|
|
199
|
+
data-color={color}
|
|
200
|
+
title={color}
|
|
201
|
+
onClick={() => this.handleColorSelect(color)}
|
|
202
|
+
/>
|
|
203
|
+
));
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* 处理主按钮点击
|
|
208
|
+
*/
|
|
209
|
+
private handleButtonClick = (event: Event): void => {
|
|
210
|
+
event.stopPropagation();
|
|
211
|
+
this.togglePanel();
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* 处理面板点击
|
|
216
|
+
*/
|
|
217
|
+
private handlePanelClick = (event: Event): void => {
|
|
218
|
+
event.stopPropagation();
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* 处理颜色选择
|
|
223
|
+
*/
|
|
224
|
+
private handleColorSelect = (color: string): void => {
|
|
225
|
+
const processedColor = handleCSSVariables(color);
|
|
226
|
+
this.setSelectedColor(processedColor);
|
|
227
|
+
this.closePanel();
|
|
228
|
+
|
|
229
|
+
// 缓存选择的颜色
|
|
230
|
+
setDefaultColorCache(processedColor, this.pluginType);
|
|
231
|
+
|
|
232
|
+
// 触发回调和事件
|
|
233
|
+
this.onColorPicked?.(processedColor);
|
|
234
|
+
this.dispatchEvent(
|
|
235
|
+
new CustomEvent("colorchange", {
|
|
236
|
+
detail: { color: processedColor },
|
|
237
|
+
bubbles: true,
|
|
238
|
+
composed: true,
|
|
239
|
+
})
|
|
240
|
+
);
|
|
241
|
+
|
|
242
|
+
logger.debug("Color selected", { color: processedColor });
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* 处理自定义颜色选择器点击
|
|
247
|
+
*/
|
|
248
|
+
private handleCustomPickerClick = (): void => {
|
|
249
|
+
const input = document.createElement("input");
|
|
250
|
+
input.type = "color";
|
|
251
|
+
input.value = this.customColor;
|
|
252
|
+
input.style.cssText = `
|
|
253
|
+
position: fixed;
|
|
254
|
+
left: -9999px;
|
|
255
|
+
opacity: 0;
|
|
256
|
+
pointer-events: none;
|
|
257
|
+
`;
|
|
258
|
+
|
|
259
|
+
const handleInput = throttle((e: Event) => {
|
|
260
|
+
const target = e.target as HTMLInputElement;
|
|
261
|
+
const color = handleCSSVariables(target.value);
|
|
262
|
+
|
|
263
|
+
this.setSelectedColor(color);
|
|
264
|
+
this.setCustomColor(color);
|
|
265
|
+
|
|
266
|
+
// 清理输入元素
|
|
267
|
+
document.body.removeChild(input);
|
|
268
|
+
|
|
269
|
+
// 触发回调
|
|
270
|
+
this.onColorPicked?.(color);
|
|
271
|
+
this.dispatchEvent(
|
|
272
|
+
new CustomEvent("colorchange", {
|
|
273
|
+
detail: { color },
|
|
274
|
+
bubbles: true,
|
|
275
|
+
composed: true,
|
|
276
|
+
})
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
logger.debug("Custom color selected", { color });
|
|
280
|
+
}, 30);
|
|
281
|
+
|
|
282
|
+
input.addEventListener("input", handleInput);
|
|
283
|
+
document.body.appendChild(input);
|
|
284
|
+
|
|
285
|
+
// 触发颜色选择器
|
|
286
|
+
requestAnimationFrame(() => {
|
|
287
|
+
input.focus();
|
|
288
|
+
input.click();
|
|
289
|
+
});
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* 切换面板显示状态
|
|
294
|
+
*/
|
|
295
|
+
private togglePanel(): void {
|
|
296
|
+
this.setOpen(!this.isOpen);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* 关闭面板
|
|
301
|
+
*/
|
|
302
|
+
private closePanel(): void {
|
|
303
|
+
this.setOpen(false);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* 设置选中的颜色
|
|
308
|
+
*/
|
|
309
|
+
private setSelectedColor(color: string): void {
|
|
310
|
+
this.selectedColor = color;
|
|
311
|
+
this.setAttr("selected-color", color);
|
|
312
|
+
this.updateColorButton();
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* 设置自定义颜色
|
|
317
|
+
*/
|
|
318
|
+
private setCustomColor(color: string): void {
|
|
319
|
+
this.customColor = color;
|
|
320
|
+
setCustomColorCache(color, this.pluginType);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* 设置面板开关状态
|
|
325
|
+
*/
|
|
326
|
+
private setOpen(open: boolean): void {
|
|
327
|
+
this.isOpen = open;
|
|
328
|
+
|
|
329
|
+
if (open) {
|
|
330
|
+
this.setAttr("open", "");
|
|
331
|
+
} else {
|
|
332
|
+
this.removeAttr("open");
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
this.rerender();
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* 更新颜色按钮样式
|
|
340
|
+
*/
|
|
341
|
+
private updateColorButton(): void {
|
|
342
|
+
if (this.colorBtn) {
|
|
343
|
+
this.colorBtn.style.setProperty("--theme-color", this.selectedColor);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* 组件连接到DOM后的初始化
|
|
349
|
+
*/
|
|
350
|
+
protected onConnected(): void {
|
|
351
|
+
// 绑定全局点击事件以关闭面板
|
|
352
|
+
document.addEventListener("click", this.handleDocumentClick);
|
|
353
|
+
|
|
354
|
+
logger.info("ColorPicker connected to DOM");
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* 组件从DOM断开时的清理
|
|
359
|
+
*/
|
|
360
|
+
protected onDisconnected(): void {
|
|
361
|
+
document.removeEventListener("click", this.handleDocumentClick);
|
|
362
|
+
|
|
363
|
+
logger.info("ColorPicker disconnected from DOM");
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* 属性变化处理
|
|
368
|
+
*/
|
|
369
|
+
protected onAttributeChanged(name: string, _oldValue: string, newValue: string): void {
|
|
370
|
+
switch (name) {
|
|
371
|
+
case "disabled":
|
|
372
|
+
this.disabled = newValue !== null;
|
|
373
|
+
this.rerender();
|
|
374
|
+
break;
|
|
375
|
+
case "selected-color":
|
|
376
|
+
if (newValue && newValue !== this.selectedColor) {
|
|
377
|
+
this.selectedColor = newValue;
|
|
378
|
+
this.updateColorButton();
|
|
379
|
+
}
|
|
380
|
+
break;
|
|
381
|
+
case "open":
|
|
382
|
+
this.isOpen = newValue !== null;
|
|
383
|
+
break;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* 处理文档点击以关闭面板
|
|
389
|
+
*/
|
|
390
|
+
private handleDocumentClick = (event: Event): void => {
|
|
391
|
+
if (this.isOpen && !this.contains(event.target as Node)) {
|
|
392
|
+
this.closePanel();
|
|
393
|
+
}
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* 公共API:获取当前选中的颜色
|
|
398
|
+
*/
|
|
399
|
+
public getSelectedColor(): string {
|
|
400
|
+
return this.selectedColor;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* 公共API:设置颜色
|
|
405
|
+
*/
|
|
406
|
+
public setColor(color: string): void {
|
|
407
|
+
this.setSelectedColor(handleCSSVariables(color));
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* 公共API:聚焦组件
|
|
412
|
+
*/
|
|
413
|
+
public focus(): void {
|
|
414
|
+
this.colorBtn?.focus();
|
|
415
|
+
}
|
|
416
|
+
}
|