@wsxjs/wsx-base-components 0.0.19 → 0.0.20

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wsxjs/wsx-base-components",
3
- "version": "0.0.19",
3
+ "version": "0.0.20",
4
4
  "description": "Base UI components built with WSXJS",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -19,8 +19,8 @@
19
19
  ],
20
20
  "dependencies": {
21
21
  "prismjs": "^1.30.0",
22
- "@wsxjs/wsx-logger": "0.0.19",
23
- "@wsxjs/wsx-core": "0.0.19"
22
+ "@wsxjs/wsx-core": "0.0.20",
23
+ "@wsxjs/wsx-logger": "0.0.20"
24
24
  },
25
25
  "devDependencies": {
26
26
  "@types/prismjs": "^1.26.5",
@@ -33,8 +33,8 @@
33
33
  "typescript": "^5.0.0",
34
34
  "vite": "^5.4.19",
35
35
  "vitest": "^2.1.8",
36
- "@wsxjs/eslint-plugin-wsx": "0.0.19",
37
- "@wsxjs/wsx-vite-plugin": "0.0.19"
36
+ "@wsxjs/eslint-plugin-wsx": "0.0.20",
37
+ "@wsxjs/wsx-vite-plugin": "0.0.20"
38
38
  },
39
39
  "keywords": [
40
40
  "wsx",
package/src/CodeBlock.wsx CHANGED
@@ -5,7 +5,7 @@
5
5
  */
6
6
  import { LightComponent, autoRegister, state } from "@wsxjs/wsx-core";
7
7
  import type { CodeSegment, CodeBlockConfig } from "./CodeBlock.types";
8
- import styles from "./CodeBlock.css?inline";
8
+
9
9
  // 导入 Prism.js 主题 CSS
10
10
  import "prismjs/themes/prism-tomorrow.css";
11
11
  // 静态导入 Prism.js 核心
@@ -32,7 +32,7 @@ import "prismjs/components/prism-cpp"; // 依赖 c
32
32
  // 依赖其他语言的语言(必须在依赖之后)
33
33
  import "prismjs/components/prism-tsx"; // 依赖 typescript 和 jsx
34
34
 
35
- @autoRegister({ tagName: "code-block" })
35
+ @autoRegister({ tagName: "wsx-code-block" })
36
36
  export default class CodeBlock extends LightComponent {
37
37
  static get observedAttributes(): string[] {
38
38
  return ["code", "title", "language", "show-copy", "show-try-online", "try-online-url"];
@@ -51,20 +51,6 @@ export default class CodeBlock extends LightComponent {
51
51
  private codeElements: HTMLElement[] = [];
52
52
  private isHighlighting: boolean = false; // 防止重复高亮
53
53
 
54
- constructor() {
55
- super({ styles });
56
- }
57
-
58
- protected onConnected() {
59
- // 从属性初始化(使用 @state 会自动触发重渲染)
60
- this.code = this.getAttribute("code") || "";
61
- this.codeTitle = this.getAttribute("title") || "";
62
- this.language = this.getAttribute("language") || "typescript";
63
- this.showCopy = this.getAttribute("show-copy") !== "false";
64
- this.showTryOnline = this.getAttribute("show-try-online") === "true";
65
- this.tryOnlineUrl = this.getAttribute("try-online-url") || "";
66
- }
67
-
68
54
  protected onRendered() {
69
55
  // 在渲染完成后应用语法高亮(防止重复高亮)
70
56
  if (!this.isHighlighting) {
@@ -72,40 +58,6 @@ export default class CodeBlock extends LightComponent {
72
58
  }
73
59
  }
74
60
 
75
- protected onAttributeChanged(name: string, _oldValue: string, newValue: string) {
76
- // 只在组件已连接时处理属性变化,避免在 render() 阶段触发重渲染
77
- if (!this.connected) {
78
- return;
79
- }
80
-
81
- switch (name) {
82
- case "code":
83
- this.code = newValue || "";
84
- // @state 会自动触发重渲染,onRendered 会在渲染完成后调用 highlightCode()
85
- break;
86
- case "title":
87
- this.codeTitle = newValue || "";
88
- // @state 会自动触发重渲染
89
- break;
90
- case "language":
91
- this.language = newValue || "typescript";
92
- // @state 会自动触发重渲染,onRendered 会在渲染完成后调用 highlightCode()
93
- break;
94
- case "show-copy":
95
- this.showCopy = newValue !== "false";
96
- // @state 会自动触发重渲染
97
- break;
98
- case "show-try-online":
99
- this.showTryOnline = newValue === "true";
100
- // @state 会自动触发重渲染
101
- break;
102
- case "try-online-url":
103
- this.tryOnlineUrl = newValue || "";
104
- // @state 会自动触发重渲染
105
- break;
106
- }
107
- }
108
-
109
61
  /**
110
62
  * 通过方法设置配置(用于编程式使用)
111
63
  */
@@ -16,7 +16,7 @@ export type { NavItem, ResponsiveNavConfig };
16
16
 
17
17
  @autoRegister({ tagName: "wsx-responsive-nav" })
18
18
  export default class ResponsiveNav extends WebComponent {
19
- private navConfig: ResponsiveNavConfig = { items: [] };
19
+ @state private navigation: ResponsiveNavConfig = { items: [] };
20
20
  /** 移动端菜单是否打开 */
21
21
  @state private isMobileMenuOpen: boolean = false;
22
22
  /** 可见的导航项索引 */
@@ -41,7 +41,7 @@ export default class ResponsiveNav extends WebComponent {
41
41
  });
42
42
  // 如果通过构造函数传递配置,使用它;否则在 onConnected 时从属性读取
43
43
  if (config) {
44
- this.navConfig = {
44
+ this.navigation = {
45
45
  mobileBreakpoint: 768,
46
46
  autoOverflow: true,
47
47
  ...config,
@@ -52,11 +52,11 @@ export default class ResponsiveNav extends WebComponent {
52
52
 
53
53
  render(): HTMLElement {
54
54
  // 确保配置已初始化
55
- if (!this.navConfig.items || this.navConfig.items.length === 0) {
55
+ if (!this.navigation?.items || this.navigation?.items.length === 0) {
56
56
  return <nav class="responsive-nav"></nav>;
57
57
  }
58
58
 
59
- const hiddenItems = this.hiddenItemIndices.map((index) => this.navConfig.items[index]);
59
+ const hiddenItems = this.hiddenItemIndices.map((index) => this.navigation.items[index]);
60
60
 
61
61
  return (
62
62
  <nav class="responsive-nav">
@@ -64,13 +64,13 @@ export default class ResponsiveNav extends WebComponent {
64
64
  {/* 品牌 */}
65
65
  <div class="nav-brand">
66
66
  <slot name="brand-icon">
67
- {this.navConfig.brandIcon &&
68
- typeof this.navConfig.brandIcon === "string" && (
69
- <span class="nav-brand-icon">{this.navConfig.brandIcon}</span>
67
+ {this.navigation.brandIcon &&
68
+ typeof this.navigation.brandIcon === "string" && (
69
+ <span class="nav-brand-icon">{this.navigation.brandIcon}</span>
70
70
  )}
71
71
  </slot>
72
- {this.navConfig.brand && (
73
- <span class="nav-brand-text">{this.navConfig.brand}</span>
72
+ {this.navigation.brand && (
73
+ <span class="nav-brand-text">{this.navigation.brand}</span>
74
74
  )}
75
75
  </div>
76
76
 
@@ -78,7 +78,7 @@ export default class ResponsiveNav extends WebComponent {
78
78
  {!this.isMobile && (
79
79
  <div ref={(el) => (this.navMenuElement = el)} class="nav-menu">
80
80
  {/* 渲染所有项,但隐藏不在 visibleItemIndices 中的项 */}
81
- {this.navConfig.items.map((item, index) => {
81
+ {this.navigation.items.map((item, index) => {
82
82
  const isVisible = this.visibleItemIndices.includes(index);
83
83
  return (
84
84
  <wsx-link
@@ -104,7 +104,7 @@ export default class ResponsiveNav extends WebComponent {
104
104
  })}
105
105
 
106
106
  {/* Overflow 菜单 */}
107
- {this.navConfig.autoOverflow &&
107
+ {this.navigation.autoOverflow &&
108
108
  hiddenItems.length > 0 &&
109
109
  !this.isMobile && (
110
110
  <div class="nav-overflow">
@@ -140,9 +140,9 @@ export default class ResponsiveNav extends WebComponent {
140
140
  )}
141
141
 
142
142
  {/* 右侧操作项(始终显示,包括移动端) */}
143
- {this.navConfig.actionTags && (
143
+ {this.navigation.actionTags && (
144
144
  <div class="nav-actions">
145
- {this.navConfig.actionTags.map((tag, index) => {
145
+ {this.navigation.actionTags.map((tag, index) => {
146
146
  // 使用动态标签名创建元素
147
147
  const TagName = tag as keyof HTMLElementTagNameMap;
148
148
  return (
@@ -173,7 +173,7 @@ export default class ResponsiveNav extends WebComponent {
173
173
  {/* 移动端菜单 */}
174
174
  {this.isMobile && (
175
175
  <div class={`nav-mobile-menu ${this.isMobileMenuOpen ? "open" : ""}`}>
176
- {this.navConfig.items
176
+ {this.navigation.items
177
177
  .filter((item) => !item.hideOnMobile)
178
178
  .map((item, index) => (
179
179
  <wsx-link
@@ -258,10 +258,10 @@ export default class ResponsiveNav extends WebComponent {
258
258
  * 使用 OverflowDetector 来最大化可见项数量
259
259
  */
260
260
  private updateVisibleItems = (): void => {
261
- if (!this.navConfig.autoOverflow || this.isMobile || !this.navMenuElement) {
261
+ if (!this.navigation.autoOverflow || this.isMobile || !this.navMenuElement) {
262
262
  // 如果不是移动端且 autoOverflow 关闭,显示所有项
263
263
  if (!this.isMobile) {
264
- const allIndices = this.navConfig.items.map((_, index) => index);
264
+ const allIndices = this.navigation.items.map((_, index) => index);
265
265
  if (
266
266
  JSON.stringify(allIndices.sort()) !==
267
267
  JSON.stringify(this.visibleItemIndices.sort())
@@ -277,7 +277,7 @@ export default class ResponsiveNav extends WebComponent {
277
277
  // 确保所有导航项元素都已获取
278
278
  // 先渲染所有项(隐藏的项也需要渲染以获取宽度)
279
279
  const allItems: HTMLElement[] = [];
280
- for (let i = 0; i < this.navConfig.items.length; i++) {
280
+ for (let i = 0; i < this.navigation.items.length; i++) {
281
281
  // 尝试从已渲染的元素中获取
282
282
  let itemElement = this.navItemsElements[i];
283
283
  if (!itemElement) {
@@ -296,7 +296,7 @@ export default class ResponsiveNav extends WebComponent {
296
296
  // 如果元素还不存在,创建一个临时元素来测量宽度
297
297
  // 这通常发生在首次渲染时
298
298
  const tempElement = document.createElement("wsx-link");
299
- tempElement.textContent = this.navConfig.items[i].label;
299
+ tempElement.textContent = this.navigation.items[i].label;
300
300
  tempElement.style.visibility = "hidden";
301
301
  tempElement.style.position = "absolute";
302
302
  document.body.appendChild(tempElement);
@@ -363,7 +363,7 @@ export default class ResponsiveNav extends WebComponent {
363
363
  * 检查是否为移动端
364
364
  */
365
365
  private checkMobile = (): void => {
366
- const isMobile = window.innerWidth <= (this.navConfig.mobileBreakpoint || 768);
366
+ const isMobile = window.innerWidth <= (this.navigation?.mobileBreakpoint || 768);
367
367
  if (isMobile !== this.isMobile) {
368
368
  this.isMobile = isMobile;
369
369
  this.rerender();
@@ -375,21 +375,12 @@ export default class ResponsiveNav extends WebComponent {
375
375
  */
376
376
  protected onConnected(): void {
377
377
  // 如果配置未初始化,从属性读取
378
- if (!this.navConfig.items || this.navConfig.items.length === 0) {
379
- const configAttr = this.getAttribute("config");
380
- if (configAttr) {
381
- try {
382
- const parsedConfig = JSON.parse(configAttr) as ResponsiveNavConfig;
383
- this.navConfig = {
384
- mobileBreakpoint: 768,
385
- autoOverflow: true,
386
- ...parsedConfig,
387
- };
388
- this.visibleItemIndices = parsedConfig.items.map((_, index) => index);
389
- this.rerender();
390
- } catch (error) {
391
- console.error("Failed to parse ResponsiveNav config:", error);
392
- }
378
+ if (!this.navigation?.items || this.navigation?.items.length === 0) {
379
+ try {
380
+ this.visibleItemIndices = this.navigation.items.map((_, index) => index);
381
+ this.rerender();
382
+ } catch (error) {
383
+ console.error("Failed to parse ResponsiveNav config:", error);
393
384
  }
394
385
  }
395
386
 
@@ -401,7 +392,7 @@ export default class ResponsiveNav extends WebComponent {
401
392
  if (attempts > 10) return; // 最多尝试10次
402
393
  setTimeout(() => {
403
394
  if (this.navMenuElement) {
404
- const hasAllElements = this.navConfig.items.every(
395
+ const hasAllElements = this.navigation.items.every(
405
396
  (_, index) => this.navItemsElements[index]
406
397
  );
407
398
  if (hasAllElements || attempts > 5) {
package/src/SvgIcon.wsx CHANGED
@@ -1,19 +1,19 @@
1
1
  /** @jsxImportSource @wsxjs/wsx-core */
2
- import { WebComponent, autoRegister } from "@wsxjs/wsx-core";
2
+ import { WebComponent, state, autoRegister } from "@wsxjs/wsx-core";
3
3
  import { createLogger } from "@wsxjs/wsx-logger";
4
4
 
5
5
  const logger = createLogger("SvgIcon");
6
6
 
7
7
  @autoRegister({ tagName: "svg-icon" })
8
8
  export default class SvgIcon extends WebComponent {
9
- constructor() {
10
- super();
11
- }
9
+ @state private size: number = 24;
10
+ @state private color: string = "currentColor";
11
+ @state private name: string = "star";
12
12
 
13
13
  render() {
14
- const size = this.getAttribute("size") || "24";
15
- const color = this.getAttribute("color") || "currentColor";
16
- const name = this.getAttribute("name") || "star";
14
+ const size = this.size || "24";
15
+ const color = this.color || "currentColor";
16
+ const name = this.name || "star";
17
17
 
18
18
  // Different icon paths based on name
19
19
  const iconPaths = {
@@ -51,13 +51,13 @@ export default class SvgIcon extends WebComponent {
51
51
  }
52
52
 
53
53
  private handleClick = (event: Event) => {
54
- logger.debug("SVG icon clicked", { name: this.getAttribute("name") });
54
+ logger.debug("SVG icon clicked", { name: this.name });
55
55
 
56
56
  // Dispatch custom event
57
57
  this.dispatchEvent(
58
58
  new CustomEvent("icon-click", {
59
59
  detail: {
60
- name: this.getAttribute("name"),
60
+ name: this.name,
61
61
  originalEvent: event,
62
62
  },
63
63
  bubbles: true,