weifuwu 0.38.1 → 0.38.2
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/README.md
CHANGED
|
@@ -32,7 +32,7 @@ npm install weifuwu
|
|
|
32
32
|
| `weifuwu/client` | **ErrorBoundary** | 错误边界组件 | createApp |
|
|
33
33
|
| `weifuwu/client` | **confirm** | Promise 化确认对话框 | createApp |
|
|
34
34
|
| `weifuwu/client** | **lockScroll/trapFocus** | 滚动锁定 / 焦点陷阱工具 | — |
|
|
35
|
-
| `weifuwu/components` | **
|
|
35
|
+
| `weifuwu/components` | **38 个组件** | Button/Table/Modal/Toast/... | weifuwu/client |
|
|
36
36
|
| `weifuwu/layout` | **CSS 布局** | 35 个布局原语 + 72 个主题 Token | — |
|
|
37
37
|
|
|
38
38
|
---
|
|
@@ -1246,7 +1246,7 @@ import type { RouterOptions } from 'weifuwu/client'
|
|
|
1246
1246
|
|
|
1247
1247
|
# 组件库 (`weifuwu/components`)
|
|
1248
1248
|
|
|
1249
|
-
|
|
1249
|
+
38 个 HTML 原语组件。每个是 `(props, ctx) => VNode` 纯函数,引用 `--wf-*` CSS 变量做主题。
|
|
1250
1250
|
|
|
1251
1251
|
```ts
|
|
1252
1252
|
import { Button, Input, Table, Modal, Toast } from 'weifuwu/components'
|
|
@@ -1295,6 +1295,7 @@ import 'weifuwu/components/style.css'
|
|
|
1295
1295
|
| StatCard | `StatCard` | `title`, `value`, `trend`, `icon`, `variant` | 统计卡片 |
|
|
1296
1296
|
| PageHeader | `PageHeader` | `title`, `subtitle`, `actions`, `onBack`, `breadcrumb` | 页面标题 |
|
|
1297
1297
|
| Img | `Img` | `src`, `alt`, `fallback`, `lazy`, `fit` | 图片(含 fallback) |
|
|
1298
|
+
| InView | `InView` | `once`, `threshold`, `rootMargin`, `placeholder`, `onEnter` | 进入视窗后懒加载内容 |
|
|
1298
1299
|
|
|
1299
1300
|
### 数据反馈
|
|
1300
1301
|
|
package/dist/client/index.js
CHANGED
|
@@ -237,6 +237,7 @@ function patchValue(parent, oldNode, oldInput, newInput, ctx) {
|
|
|
237
237
|
if (oldNode && oldNode.nodeType === 1) {
|
|
238
238
|
patchProps(oldNode, oldV.props, newV.props);
|
|
239
239
|
patchChildren(oldNode, oldV, newV, ctx);
|
|
240
|
+
if (oldV._cleanup) newV._cleanup = oldV._cleanup;
|
|
240
241
|
} else if (oldNode) {
|
|
241
242
|
callRefCleanup(oldInput);
|
|
242
243
|
const node = renderValue(newInput, ctx);
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* weifuwu/components — InView
|
|
3
|
+
*
|
|
4
|
+
* 进入视窗组件。通过 IntersectionObserver 监听元素是否进入视窗,
|
|
5
|
+
* 实现内容懒加载:只有用户滚动到该区域时才渲染 children。
|
|
6
|
+
*
|
|
7
|
+
* 用法:
|
|
8
|
+
* <InView>
|
|
9
|
+
* <ExpensiveComponent />
|
|
10
|
+
* </InView>
|
|
11
|
+
*
|
|
12
|
+
* <InView once={false} rootMargin="200px">
|
|
13
|
+
* <img src="large.jpg" />
|
|
14
|
+
* </InView>
|
|
15
|
+
*/
|
|
16
|
+
import type { Component } from '../../client/vnode.ts';
|
|
17
|
+
export interface InViewProps {
|
|
18
|
+
/** 是否只触发一次(默认 true,进入后一直显示) */
|
|
19
|
+
once?: boolean;
|
|
20
|
+
/** IntersectionObserver threshold(默认 0) */
|
|
21
|
+
threshold?: number;
|
|
22
|
+
/** IntersectionObserver rootMargin(默认 '0px') */
|
|
23
|
+
rootMargin?: string;
|
|
24
|
+
/** 进入视窗前渲染的占位内容(默认轻量占位块) */
|
|
25
|
+
placeholder?: any;
|
|
26
|
+
/** 进入视窗回调 */
|
|
27
|
+
onEnter?: () => void;
|
|
28
|
+
/** 懒加载内容 */
|
|
29
|
+
children?: any;
|
|
30
|
+
}
|
|
31
|
+
export declare const InView: Component<InViewProps>;
|
|
@@ -79,3 +79,5 @@ export { Skeleton } from './Skeleton/Skeleton.ts';
|
|
|
79
79
|
export type { SkeletonProps, SkeletonVariant } from './Skeleton/Skeleton.ts';
|
|
80
80
|
export { Img } from './Img/Img.ts';
|
|
81
81
|
export type { ImgProps } from './Img/Img.ts';
|
|
82
|
+
export { InView } from './InView/InView.ts';
|
|
83
|
+
export type { InViewProps } from './InView/InView.ts';
|
package/dist/components/index.js
CHANGED
|
@@ -1023,6 +1023,50 @@ var Img = (props) => {
|
|
|
1023
1023
|
}
|
|
1024
1024
|
return h("img", imgProps);
|
|
1025
1025
|
};
|
|
1026
|
+
|
|
1027
|
+
// src/components/InView/InView.ts
|
|
1028
|
+
var InView = (props, ctx) => {
|
|
1029
|
+
const {
|
|
1030
|
+
once = true,
|
|
1031
|
+
threshold = 0,
|
|
1032
|
+
rootMargin = "0px",
|
|
1033
|
+
placeholder,
|
|
1034
|
+
onEnter,
|
|
1035
|
+
children
|
|
1036
|
+
} = props;
|
|
1037
|
+
const $ = ctx.ui.$;
|
|
1038
|
+
if (!ctx.ui.ready) {
|
|
1039
|
+
$.inView = false;
|
|
1040
|
+
}
|
|
1041
|
+
const sentinelRef = (el) => {
|
|
1042
|
+
if (!el) return;
|
|
1043
|
+
if ($.inView && once) return;
|
|
1044
|
+
const io = new IntersectionObserver(
|
|
1045
|
+
(entries) => {
|
|
1046
|
+
if (entries[0]?.isIntersecting) {
|
|
1047
|
+
$.inView = true;
|
|
1048
|
+
onEnter?.();
|
|
1049
|
+
if (once) io.disconnect();
|
|
1050
|
+
} else if (!once) {
|
|
1051
|
+
$.inView = false;
|
|
1052
|
+
}
|
|
1053
|
+
},
|
|
1054
|
+
{ threshold, rootMargin }
|
|
1055
|
+
);
|
|
1056
|
+
io.observe(el);
|
|
1057
|
+
return () => {
|
|
1058
|
+
io.disconnect();
|
|
1059
|
+
};
|
|
1060
|
+
};
|
|
1061
|
+
if ($.inView) {
|
|
1062
|
+
return h("div", { class: "wf-inview wf-inview--loaded" }, children);
|
|
1063
|
+
}
|
|
1064
|
+
const placeholderEl = placeholder !== void 0 ? placeholder : h("div", { class: "wf-inview-placeholder" });
|
|
1065
|
+
return h("div", {
|
|
1066
|
+
class: "wf-inview wf-inview--pending",
|
|
1067
|
+
ref: sentinelRef
|
|
1068
|
+
}, placeholderEl);
|
|
1069
|
+
};
|
|
1026
1070
|
export {
|
|
1027
1071
|
Accordion,
|
|
1028
1072
|
Alert,
|
|
@@ -1040,6 +1084,7 @@ export {
|
|
|
1040
1084
|
FileUpload,
|
|
1041
1085
|
Form,
|
|
1042
1086
|
Img,
|
|
1087
|
+
InView,
|
|
1043
1088
|
Input,
|
|
1044
1089
|
Loading,
|
|
1045
1090
|
Modal,
|
|
@@ -2003,3 +2003,29 @@
|
|
|
2003
2003
|
width: 60%;
|
|
2004
2004
|
}
|
|
2005
2005
|
|
|
2006
|
+
/* weifuwu/components — InView */
|
|
2007
|
+
|
|
2008
|
+
.wf-inview {
|
|
2009
|
+
width: 100%;
|
|
2010
|
+
}
|
|
2011
|
+
|
|
2012
|
+
.wf-inview--loaded {
|
|
2013
|
+
/* 加载后无额外样式,让 children 自然布局 */;
|
|
2014
|
+
}
|
|
2015
|
+
|
|
2016
|
+
.wf-inview-placeholder {
|
|
2017
|
+
display: flex;
|
|
2018
|
+
align-items: center;
|
|
2019
|
+
justify-content: center;
|
|
2020
|
+
min-height: 100px;
|
|
2021
|
+
background: var(--wf-color-bg-secondary, #f9fafb);
|
|
2022
|
+
border-radius: var(--wf-radius-md, 8px);
|
|
2023
|
+
border: 1px dashed var(--wf-color-border, #e5e7eb);
|
|
2024
|
+
color: var(--wf-color-text-tertiary, #9ca3af);
|
|
2025
|
+
font-size: var(--wf-font-size-sm, 13px);
|
|
2026
|
+
}
|
|
2027
|
+
|
|
2028
|
+
.wf-inview-placeholder::after {
|
|
2029
|
+
content: '滚动到此处加载内容';
|
|
2030
|
+
}
|
|
2031
|
+
|