@neatui/vue 3.0.0 → 4.0.0
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 +1 -10
- package/package.json +5 -2
- package/rollup.config.js +42 -2
- package/src/components/basic/IDraggable@b.vue +1 -1
- package/src/components/basic/IFollowView.vue +2 -2
- package/src/components/basic/IScrollView.vue +441 -45
- package/src/components/basic/LayerView/Layer.vue +193 -42
- package/src/components/display/Calendar.vue +6 -6
- package/src/components/display/CalendarReg.vue +6 -6
- package/src/components/display/PhotoEditor.vue +2 -2
- package/src/components/display/TreeView.vue +4 -3
- package/src/components/form/Cascader.vue +3 -3
- package/src/components/form/DatePicker.vue +2 -2
- package/src/components/form/{DateRangePicker@v3.vue → DateRangePicker.vue} +2 -1
- package/src/components/form/{DateRangeView@v3.vue → DateRangeView.vue} +11 -11
- package/src/components/form/DateView.vue +461 -82
- package/src/components/form/ImgUpload.vue +3 -3
- package/src/components/form/{MoreSelect@v3.vue → MoreSelect.vue} +1 -1
- package/src/components/form/MoreSelectList.vue +1 -1
- package/src/components/form/{MoreSelectPanel@v3.vue → MoreSelectPanel.vue} +1 -1
- package/src/components/form/MoreSelectPicker.vue +3 -3
- package/src/components/form/MoreSelectTags.vue +1 -1
- package/src/components/form/PageMoreSelect.vue +1 -1
- package/src/components/form/PageSelect.vue +1 -1
- package/src/components/form/SearchMoreSelect.vue +1 -1
- package/src/components/form/{SearchSelect@v3.vue → SearchSelect.vue} +1 -1
- package/src/components/form/{Select@v3.vue → Select.vue} +1 -1
- package/src/components/form/SelectList.vue +1 -1
- package/src/components/form/SelectPicker.vue +3 -3
- package/src/components/form/SelectTree/{SelectTree@v1.vue → SelectTree.vue} +1 -1
- package/src/components/form/Switch.vue +2 -1
- package/src/components/form/TimeView.vue +6 -6
- package/src/components/form/Upload.vue +22 -11
- package/src/components/form/index.ts +8 -8
- package/src/components/loader/FormLoader/FormLoader.vue +124 -103
- package/src/components/loader/LayerLoader/LayerLoader.vue +6 -7
- package/src/components/loader/LimitLoader/{LimitLoader@v3.vue → LimitLoader.vue} +4 -4
- package/src/components/loader/LimitLoader/index.ts +1 -1
- package/src/components/loader/TableLoader/TableLoader.vue +7 -7
- package/src/components/loader/TableLoader/TableRender.vue +0 -1
- package/src/components/loader/ViewLoader/{ViewLoader@v1.vue → ViewLoader.vue} +124 -103
- package/src/components/loader/ViewLoader/index.ts +1 -1
- package/src/components/tools/FormDraftsView.vue +4 -4
- package/src/components/tools/FormVerifyView.vue +2 -2
- package/src/components/tools/MoreTools.vue +5 -5
- package/src/components/tools/Pagination@a.vue +4 -4
- package/src/components/tools/Pagination@b.vue +4 -4
- package/tsconfig.json +1 -2
- package/src/components/form/DateView@v2.vue +0 -386
- package/src/components/form/DateView@v3.vue +0 -608
- package/src/components/tools/MoreTools@v2.vue +0 -74
- /package/src/components/form/{Input@v3.vue → Input.vue} +0 -0
- /package/src/components/form/{TextArea.vue → Textarea.vue} +0 -0
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
NeatUI Vue component package for Vue 3 and Nuxt projects.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Nuxt users should use `@neatui/vue` directly.
|
|
6
6
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
@@ -71,15 +71,6 @@ const data = reactive({
|
|
|
71
71
|
</script>
|
|
72
72
|
```
|
|
73
73
|
|
|
74
|
-
## Migration From @neatui/nuxt
|
|
75
|
-
|
|
76
|
-
```diff
|
|
77
|
-
- import { Input, FormLoader, Pagination } from '@neatui/nuxt';
|
|
78
|
-
+ import { Input, FormLoader, Pagination } from '@neatui/vue';
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
`@neatui/nuxt` should be removed from new Nuxt projects.
|
|
82
|
-
|
|
83
74
|
## Documentation
|
|
84
75
|
|
|
85
76
|
[http://neatui.fekit.cn](http://neatui.fekit.cn)
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@neatui/vue",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"description": "basic component ui",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "xiaojunbo",
|
|
8
8
|
"peerDependencies": {
|
|
9
|
+
"pinia": "^3.0.3",
|
|
9
10
|
"vue": "^3.2.0",
|
|
10
11
|
"vue-router": "^4.0.0"
|
|
11
12
|
},
|
|
@@ -15,9 +16,11 @@
|
|
|
15
16
|
"devDependencies": {
|
|
16
17
|
"@rollup/plugin-typescript": "^11.1.6",
|
|
17
18
|
"@types/sortablejs": "^1.15.8",
|
|
19
|
+
"pinia": "^3.0.3",
|
|
18
20
|
"postcss-scss": "^4.0.9",
|
|
19
21
|
"rollup-plugin-scss": "^4.0.0",
|
|
20
22
|
"rollup-plugin-terser": "^7.0.2",
|
|
23
|
+
"sass": "^1.80.7",
|
|
21
24
|
"typescript": "^5.1.6",
|
|
22
25
|
"vue": "^3.5.18",
|
|
23
26
|
"vue-router": "^4.5.1"
|
|
@@ -31,7 +34,7 @@
|
|
|
31
34
|
"@fekit/scrollto": "^3.0.3",
|
|
32
35
|
"@fekit/toast": "^2.7.2",
|
|
33
36
|
"@fekit/utils": "^6.0.0",
|
|
34
|
-
"@neatui/css": "^
|
|
37
|
+
"@neatui/css": "^4.0.0",
|
|
35
38
|
"@rollup/plugin-commonjs": "^26.0.1",
|
|
36
39
|
"@rollup/plugin-node-resolve": "^15.2.3",
|
|
37
40
|
"@vue/compiler-sfc": "^3.4.30",
|
package/rollup.config.js
CHANGED
|
@@ -3,14 +3,40 @@ const commonjs = require('@rollup/plugin-commonjs');
|
|
|
3
3
|
const vue = require('rollup-plugin-vue');
|
|
4
4
|
const postcss = require('rollup-plugin-postcss');
|
|
5
5
|
const typescript = require('@rollup/plugin-typescript');
|
|
6
|
+
const ts = require('typescript');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const { pathToFileURL } = require('url');
|
|
6
9
|
const { terser } = require('rollup-plugin-terser');
|
|
7
10
|
const scss = require('postcss-scss');
|
|
8
11
|
|
|
12
|
+
const vueTypeScript = () => ({
|
|
13
|
+
name: 'vue-typescript',
|
|
14
|
+
transform(code, id) {
|
|
15
|
+
if (!/\.vue\?.*type=script.*lang\.ts$/.test(id)) return null;
|
|
16
|
+
|
|
17
|
+
const result = ts.transpileModule(code, {
|
|
18
|
+
fileName: id,
|
|
19
|
+
compilerOptions: {
|
|
20
|
+
target: ts.ScriptTarget.ESNext,
|
|
21
|
+
module: ts.ModuleKind.ESNext,
|
|
22
|
+
sourceMap: true
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
code: result.outputText,
|
|
28
|
+
map: result.sourceMapText ? JSON.parse(result.sourceMapText) : null
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
|
|
9
33
|
module.exports = {
|
|
10
34
|
input: 'src/components/loader/index.ts',
|
|
11
35
|
output: {
|
|
12
36
|
file: 'dist/bundle.js',
|
|
13
|
-
format: 'es'
|
|
37
|
+
format: 'es',
|
|
38
|
+
inlineDynamicImports: true,
|
|
39
|
+
sourcemap: true
|
|
14
40
|
},
|
|
15
41
|
plugins: [
|
|
16
42
|
resolve({
|
|
@@ -19,8 +45,22 @@ module.exports = {
|
|
|
19
45
|
commonjs(),
|
|
20
46
|
vue({
|
|
21
47
|
css: false,
|
|
22
|
-
compileTemplate: true
|
|
48
|
+
compileTemplate: true,
|
|
49
|
+
preprocessStyles: true,
|
|
50
|
+
preprocessOptions: {
|
|
51
|
+
scss: {
|
|
52
|
+
importers: [
|
|
53
|
+
{
|
|
54
|
+
findFileUrl(url) {
|
|
55
|
+
if (url !== '@neatui/css') return null;
|
|
56
|
+
return pathToFileURL(path.resolve(__dirname, '../css/neatui.scss'));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
]
|
|
60
|
+
}
|
|
61
|
+
}
|
|
23
62
|
}),
|
|
63
|
+
vueTypeScript(),
|
|
24
64
|
postcss({
|
|
25
65
|
extensions: ['.css', '.scss'],
|
|
26
66
|
extract: true,
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
v-for="(item, idx) in list || []"
|
|
5
5
|
:key="idx"
|
|
6
6
|
style="user-select: none"
|
|
7
|
-
class="drag-move-item b-solid
|
|
7
|
+
class="drag-move-item b-solid b-line b-xs n-sm"
|
|
8
8
|
:data-move-ing="state.__moveing"
|
|
9
9
|
@mousedown="fMoveSta($event, list, idx)"
|
|
10
10
|
@mousemove="fMoveIng($event, list, idx)"
|
|
@@ -18,11 +18,11 @@
|
|
|
18
18
|
>
|
|
19
19
|
<div ref="box" :ui-tips-box="state.pos" :class="tipBoxClass" :style="tipBoxStyle">
|
|
20
20
|
<div ui-hide=">mob" class="mob-follow-tips w-full n-ss" ui-flex="row xm">
|
|
21
|
-
<div class="w-
|
|
21
|
+
<div class="w-xs*s"></div>
|
|
22
22
|
<div>
|
|
23
23
|
<h5 class="bold">{{ title }}</h5>
|
|
24
24
|
</div>
|
|
25
|
-
<div class="w-
|
|
25
|
+
<div class="w-xs*s" ui-flex="row rm">
|
|
26
26
|
<button ui-btn="@a none s :square" @click="toggle">
|
|
27
27
|
<i class="icon icon-close"></i>
|
|
28
28
|
</button>
|
|
@@ -1,34 +1,64 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div
|
|
3
|
+
ref="root"
|
|
3
4
|
class="fekit-ipull-scroll"
|
|
4
5
|
:ui-scroll="`:y ${stable ? 'stable' : ''}`"
|
|
5
|
-
ref="root"
|
|
6
6
|
>
|
|
7
7
|
<div>
|
|
8
|
-
<div class="
|
|
9
|
-
<slot name="pull-head" :state="demo.store.pull">
|
|
10
|
-
<
|
|
11
|
-
<
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
8
|
+
<div v-if="pull" class="co-text o-mm" fekit-pullload-head="">
|
|
9
|
+
<slot name="pull-head" :state="demo.store.pull" :text="pullText()" :locale="words" :pullload="demo">
|
|
10
|
+
<slot v-if="demo.store.pull === 0" name="pull-idle" :state="demo.store.pull" :text="pullText('idle')" :locale="words" :pullload="demo">
|
|
11
|
+
<div ui-flex="row cm">
|
|
12
|
+
<i class="pulldown icon icon-pulldown"></i>
|
|
13
|
+
<span>{{ pullText("idle") }}</span>
|
|
14
|
+
</div>
|
|
15
|
+
</slot>
|
|
16
|
+
<slot v-else-if="demo.store.pull === 1" name="pull-ready" :state="demo.store.pull" :text="pullText('ready')" :locale="words" :pullload="demo">
|
|
17
|
+
<div ui-flex="row cm">
|
|
18
|
+
<i class="pulldown icon icon-pulldown toup"></i>
|
|
19
|
+
<span>{{ pullText("ready") }}</span>
|
|
20
|
+
</div>
|
|
21
|
+
</slot>
|
|
22
|
+
<slot v-else-if="demo.store.pull === 2" name="pull-loading" :state="demo.store.pull" :text="pullText('loading')" :locale="words" :pullload="demo">
|
|
23
|
+
<div ui-flex="row cm">
|
|
24
|
+
<span ui-load="@d"></span>
|
|
25
|
+
<span v-if="pullText('loading')">{{ pullText("loading") }}</span>
|
|
26
|
+
</div>
|
|
27
|
+
</slot>
|
|
28
|
+
<slot v-else-if="demo.store.pull === 3" name="pull-finished" :state="demo.store.pull" :text="pullText('finished')" :locale="words" :pullload="demo">
|
|
29
|
+
<div ui-flex="row cm">
|
|
30
|
+
<span>{{ pullText("finished") }}</span>
|
|
31
|
+
</div>
|
|
32
|
+
</slot>
|
|
21
33
|
</slot>
|
|
22
34
|
</div>
|
|
23
35
|
<slot></slot>
|
|
24
|
-
<div class="
|
|
25
|
-
<slot name="load-foot" :state="demo.store.load">
|
|
36
|
+
<div v-if="load" class="co-text o-mm my-ms" fekit-pullload-foot="">
|
|
37
|
+
<slot name="load-foot" :state="demo.store.load" :text="loadText()" :locale="words" :pullload="demo">
|
|
26
38
|
<transition name="fekit-pullload-fade" mode="out-in">
|
|
27
|
-
<
|
|
28
|
-
<
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
39
|
+
<slot v-if="demo.store.load === 2" name="load-loading" :state="demo.store.load" :text="loadText('loading')" :locale="words" :pullload="demo">
|
|
40
|
+
<div ui-flex="row cm">
|
|
41
|
+
<span ui-load="@d"></span>
|
|
42
|
+
<span v-if="loadText('loading')">{{ loadText("loading") }}</span>
|
|
43
|
+
</div>
|
|
44
|
+
</slot>
|
|
45
|
+
<slot v-else-if="demo.store.load === 3" name="load-over" :state="demo.store.load" :text="loadText('over')" :locale="words" :pullload="demo">
|
|
46
|
+
<div class="fekit-pullload-over" ui-flex="row cm">
|
|
47
|
+
<span>{{ loadText("over") }}</span>
|
|
48
|
+
</div>
|
|
49
|
+
</slot>
|
|
50
|
+
<slot v-else-if="demo.store.load === 1" name="load-loaded" :state="demo.store.load" :text="loadText('loaded')" :locale="words" :pullload="demo">
|
|
51
|
+
<div ui-flex="row cm">
|
|
52
|
+
<span v-if="loadText('loaded')">{{ loadText("loaded") }}</span>
|
|
53
|
+
<span v-else> </span>
|
|
54
|
+
</div>
|
|
55
|
+
</slot>
|
|
56
|
+
<slot v-else name="load-idle" :state="demo.store.load" :text="loadText('idle')" :locale="words" :pullload="demo">
|
|
57
|
+
<div ui-flex="row cm">
|
|
58
|
+
<span v-if="loadText('idle')">{{ loadText("idle") }}</span>
|
|
59
|
+
<span v-else> </span>
|
|
60
|
+
</div>
|
|
61
|
+
</slot>
|
|
32
62
|
</transition>
|
|
33
63
|
</slot>
|
|
34
64
|
</div>
|
|
@@ -36,8 +66,22 @@
|
|
|
36
66
|
</div>
|
|
37
67
|
</template>
|
|
38
68
|
<script setup lang="ts">
|
|
39
|
-
import { onMounted, onBeforeUnmount, ref } from "vue";
|
|
69
|
+
import { computed, nextTick, onMounted, onBeforeUnmount, ref, watch } from "vue";
|
|
40
70
|
const emits = defineEmits(["onpull", "onload"]);
|
|
71
|
+
const defaultLocale = {
|
|
72
|
+
pull: {
|
|
73
|
+
idle: "下拉刷新",
|
|
74
|
+
ready: "松开加载",
|
|
75
|
+
loading: "",
|
|
76
|
+
finished: "刷新完成",
|
|
77
|
+
},
|
|
78
|
+
load: {
|
|
79
|
+
idle: "",
|
|
80
|
+
loaded: "",
|
|
81
|
+
loading: "",
|
|
82
|
+
over: "没有更多了",
|
|
83
|
+
},
|
|
84
|
+
};
|
|
41
85
|
const props: any = defineProps({
|
|
42
86
|
pull: {
|
|
43
87
|
type: Boolean,
|
|
@@ -51,10 +95,264 @@ const props: any = defineProps({
|
|
|
51
95
|
type: Boolean,
|
|
52
96
|
default: false,
|
|
53
97
|
},
|
|
98
|
+
loadPos: {
|
|
99
|
+
type: [Number, String],
|
|
100
|
+
default: null,
|
|
101
|
+
},
|
|
102
|
+
loadPosition: {
|
|
103
|
+
type: [Number, String],
|
|
104
|
+
default: null,
|
|
105
|
+
},
|
|
106
|
+
loadOffset: {
|
|
107
|
+
type: [Number, String],
|
|
108
|
+
default: null,
|
|
109
|
+
},
|
|
110
|
+
initialLoad: {
|
|
111
|
+
type: [Boolean, String],
|
|
112
|
+
default: false,
|
|
113
|
+
},
|
|
114
|
+
locale: {
|
|
115
|
+
type: Object,
|
|
116
|
+
default: () => ({}),
|
|
117
|
+
},
|
|
54
118
|
});
|
|
55
119
|
const root: any = ref(null);
|
|
56
120
|
const demo: any = ref({ store: { pull: 0, load: 0 } });
|
|
57
121
|
let alive = true;
|
|
122
|
+
let removeLoadOffsetListener = () => {};
|
|
123
|
+
let checkingLoad = false;
|
|
124
|
+
let loadPending = false;
|
|
125
|
+
let initialPreloadDone = false;
|
|
126
|
+
let pullLoadListening = false;
|
|
127
|
+
let removePullLoadListener = () => {};
|
|
128
|
+
let resizeObserver: any = null;
|
|
129
|
+
let pullLoadReady: Promise<any> | null = null;
|
|
130
|
+
|
|
131
|
+
const words = computed(() => ({
|
|
132
|
+
pull: {
|
|
133
|
+
...defaultLocale.pull,
|
|
134
|
+
...(props.locale?.pull || {}),
|
|
135
|
+
},
|
|
136
|
+
load: {
|
|
137
|
+
...defaultLocale.load,
|
|
138
|
+
...(props.locale?.load || {}),
|
|
139
|
+
},
|
|
140
|
+
}));
|
|
141
|
+
|
|
142
|
+
const pullStatusMap: any = {
|
|
143
|
+
0: "idle",
|
|
144
|
+
1: "ready",
|
|
145
|
+
2: "loading",
|
|
146
|
+
3: "finished",
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
const loadStatusMap: any = {
|
|
150
|
+
0: "idle",
|
|
151
|
+
1: "loaded",
|
|
152
|
+
2: "loading",
|
|
153
|
+
3: "over",
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const pullText = (status = pullStatusMap[demo.value?.store?.pull] || "idle") => {
|
|
157
|
+
return words.value.pull?.[status] ?? "";
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
const loadText = (status = loadStatusMap[demo.value?.store?.load] || "idle") => {
|
|
161
|
+
return words.value.load?.[status] ?? "";
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
const numberValue = (value: any) => {
|
|
165
|
+
const num = parseFloat(String(value ?? "").trim());
|
|
166
|
+
return Number.isFinite(num) ? num : 0;
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
const hasValue = (value: any) => value !== null && value !== undefined && value !== "";
|
|
170
|
+
|
|
171
|
+
const boolValue = (value: any) => {
|
|
172
|
+
if (value === false || value === 0) return false;
|
|
173
|
+
const text = String(value ?? "").trim().toLowerCase();
|
|
174
|
+
return !["", "false", "0", "off", "no"].includes(text);
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
const clampRatio = (value: any) => {
|
|
178
|
+
const text = String(value ?? "").trim();
|
|
179
|
+
const ratio = text.endsWith("%") ? numberValue(text) / 100 : numberValue(text);
|
|
180
|
+
return Math.min(1, Math.max(0, ratio > 1 ? ratio / 100 : ratio));
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
const effectiveLoadPos = () => {
|
|
184
|
+
if (hasValue(props.loadPos)) return props.loadPos;
|
|
185
|
+
if (hasValue(props.loadPosition)) return props.loadPosition;
|
|
186
|
+
if (hasValue(props.loadOffset)) return null;
|
|
187
|
+
return 0.8;
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
const loadOffsetPx = (el: HTMLElement) => {
|
|
191
|
+
const text = String(props.loadOffset ?? "").trim();
|
|
192
|
+
const value = numberValue(text);
|
|
193
|
+
if (text.endsWith("%")) return Math.max(0, el.clientHeight * (value / 100));
|
|
194
|
+
return Math.max(0, value);
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
const isInitialLoadEnabled = () => {
|
|
198
|
+
return boolValue(props.initialLoad);
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
const shouldLoad = (el: HTMLElement) => {
|
|
202
|
+
const scrollable = el.scrollHeight - el.clientHeight;
|
|
203
|
+
if (scrollable <= 0) return false;
|
|
204
|
+
const loadPos = effectiveLoadPos();
|
|
205
|
+
if (hasValue(loadPos)) {
|
|
206
|
+
return el.scrollTop / scrollable >= clampRatio(loadPos);
|
|
207
|
+
}
|
|
208
|
+
return scrollable - el.scrollTop <= loadOffsetPx(el);
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
const shouldInitialPreload = (el: HTMLElement) => {
|
|
212
|
+
if (initialPreloadDone || !props.load || demo.value?.store?.load >= 2) return false;
|
|
213
|
+
if (!isInitialLoadEnabled()) return false;
|
|
214
|
+
return el.scrollHeight - el.clientHeight > 0;
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
const emitPull = async () => {
|
|
218
|
+
if (!props.pull) return;
|
|
219
|
+
emits("onpull", demo.value);
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
const emitLoad = async () => {
|
|
223
|
+
if (!props.load) {
|
|
224
|
+
demo.value?.loaded?.();
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
try {
|
|
228
|
+
emits("onload", demo.value);
|
|
229
|
+
} catch (error) {
|
|
230
|
+
resetLoad();
|
|
231
|
+
throw error;
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
const syncPullLoadHandlers = () => {
|
|
236
|
+
if (!demo.value) return;
|
|
237
|
+
demo.value.onpull = props.pull ? emitPull : null;
|
|
238
|
+
demo.value.onload = props.load ? emitLoad : null;
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
const checkLoad = () => {
|
|
242
|
+
const el = root.value;
|
|
243
|
+
if (!props.load || !el || loadPending || demo.value?.store?.pull === 2 || demo.value?.store?.load >= 2) return;
|
|
244
|
+
if (shouldInitialPreload(el)) {
|
|
245
|
+
initialPreloadDone = true;
|
|
246
|
+
triggerLoad();
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
if (shouldLoad(el)) {
|
|
250
|
+
triggerLoad();
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
const triggerLoad = () => {
|
|
255
|
+
if (loadPending || demo.value?.store?.load >= 2) return;
|
|
256
|
+
loadPending = true;
|
|
257
|
+
const run = () => {
|
|
258
|
+
if (!alive || !props.load || demo.value?.store?.load >= 2) {
|
|
259
|
+
loadPending = false;
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
if (typeof demo.value?.isload !== "function") {
|
|
263
|
+
loadPending = false;
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
demo.value.isload();
|
|
267
|
+
};
|
|
268
|
+
if (typeof window !== "undefined" && window.requestAnimationFrame) {
|
|
269
|
+
window.requestAnimationFrame(() => setTimeout(run, 0));
|
|
270
|
+
} else {
|
|
271
|
+
setTimeout(run, 0);
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
const requestCheckLoad = () => {
|
|
276
|
+
if (checkingLoad) return;
|
|
277
|
+
checkingLoad = true;
|
|
278
|
+
const run = () => {
|
|
279
|
+
checkingLoad = false;
|
|
280
|
+
checkLoad();
|
|
281
|
+
};
|
|
282
|
+
if (typeof window !== "undefined" && window.requestAnimationFrame) {
|
|
283
|
+
window.requestAnimationFrame(run);
|
|
284
|
+
} else {
|
|
285
|
+
setTimeout(run, 0);
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
const scheduleCheckLoad = async () => {
|
|
290
|
+
await nextTick();
|
|
291
|
+
if (!alive) return;
|
|
292
|
+
requestCheckLoad();
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
const removeLoadListener = () => {
|
|
296
|
+
removeLoadOffsetListener();
|
|
297
|
+
removeLoadOffsetListener = () => {};
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
const removePullListener = () => {
|
|
301
|
+
removePullLoadListener();
|
|
302
|
+
removePullLoadListener = () => {};
|
|
303
|
+
pullLoadListening = false;
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
const syncPullListener = () => {
|
|
307
|
+
const el = root.value;
|
|
308
|
+
if (!demo.value || !el) return;
|
|
309
|
+
if (!props.pull) {
|
|
310
|
+
removePullListener();
|
|
311
|
+
demo.value?.setcss?.(0);
|
|
312
|
+
if (demo.value?.store) {
|
|
313
|
+
demo.value.store.pull = 0;
|
|
314
|
+
}
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
if (pullLoadListening) return;
|
|
318
|
+
demo.value?.listen?.(el);
|
|
319
|
+
removePullLoadListener = typeof demo.value?.remove === "function" ? () => demo.value.remove() : () => {};
|
|
320
|
+
pullLoadListening = true;
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
const syncLoadListener = () => {
|
|
324
|
+
removeLoadListener();
|
|
325
|
+
const el = root.value;
|
|
326
|
+
if (!props.load) {
|
|
327
|
+
loadPending = false;
|
|
328
|
+
initialPreloadDone = false;
|
|
329
|
+
if (demo.value?.store) {
|
|
330
|
+
demo.value.store.load = 0;
|
|
331
|
+
}
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
if (!el) return;
|
|
335
|
+
|
|
336
|
+
const onChange = () => {
|
|
337
|
+
requestCheckLoad();
|
|
338
|
+
};
|
|
339
|
+
el.addEventListener("scroll", onChange, { passive: true });
|
|
340
|
+
el.addEventListener("touchmove", onChange, { passive: true });
|
|
341
|
+
removeLoadOffsetListener = () => {
|
|
342
|
+
el.removeEventListener("scroll", onChange);
|
|
343
|
+
el.removeEventListener("touchmove", onChange);
|
|
344
|
+
resizeObserver?.disconnect?.();
|
|
345
|
+
resizeObserver = null;
|
|
346
|
+
};
|
|
347
|
+
if (typeof ResizeObserver !== "undefined") {
|
|
348
|
+
resizeObserver = new ResizeObserver(onChange);
|
|
349
|
+
resizeObserver.observe(el);
|
|
350
|
+
if (el.firstElementChild) {
|
|
351
|
+
resizeObserver.observe(el.firstElementChild);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
scheduleCheckLoad();
|
|
355
|
+
};
|
|
58
356
|
|
|
59
357
|
const createPullLoad = async () => {
|
|
60
358
|
const { default: PullLoad, Easing } = await import("@fekit/pullload");
|
|
@@ -63,33 +361,42 @@ const createPullLoad = async () => {
|
|
|
63
361
|
friction: 0.3,
|
|
64
362
|
triggerDistance: 60,
|
|
65
363
|
loadingDistance: 50,
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
emits("onpull", demo.value);
|
|
70
|
-
},
|
|
71
|
-
}
|
|
72
|
-
: {}),
|
|
73
|
-
...(props.load
|
|
74
|
-
? {
|
|
75
|
-
onload: async () => {
|
|
76
|
-
emits("onload", demo.value);
|
|
77
|
-
},
|
|
78
|
-
}
|
|
79
|
-
: {}),
|
|
364
|
+
onpull: props.pull ? emitPull : null,
|
|
365
|
+
// IScrollView owns the scroll threshold, so onload is attached after listen().
|
|
366
|
+
onload: null,
|
|
80
367
|
});
|
|
81
368
|
};
|
|
82
369
|
|
|
370
|
+
const ensurePullLoad = async () => {
|
|
371
|
+
if (demo.value?.isload || demo.value?.ispull) return demo.value;
|
|
372
|
+
if (!pullLoadReady) {
|
|
373
|
+
pullLoadReady = createPullLoad()
|
|
374
|
+
.then((pullLoad) => {
|
|
375
|
+
if (!alive) {
|
|
376
|
+
pullLoad?.remove?.();
|
|
377
|
+
return null;
|
|
378
|
+
}
|
|
379
|
+
demo.value = pullLoad;
|
|
380
|
+
syncPullLoadHandlers();
|
|
381
|
+
return pullLoad;
|
|
382
|
+
})
|
|
383
|
+
.catch((error) => {
|
|
384
|
+
pullLoadReady = null;
|
|
385
|
+
throw error;
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
return pullLoadReady;
|
|
389
|
+
};
|
|
390
|
+
|
|
83
391
|
onMounted(async () => {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
pullLoad?.remove?.();
|
|
87
|
-
return;
|
|
392
|
+
if (props.pull || props.load) {
|
|
393
|
+
await ensurePullLoad();
|
|
88
394
|
}
|
|
89
395
|
|
|
90
|
-
demo.value = pullLoad;
|
|
91
396
|
if (root.value) {
|
|
92
|
-
|
|
397
|
+
syncPullLoadHandlers();
|
|
398
|
+
syncPullListener();
|
|
399
|
+
syncLoadListener();
|
|
93
400
|
if (props.pull) {
|
|
94
401
|
demo.value?.ispull?.();
|
|
95
402
|
}
|
|
@@ -97,14 +404,92 @@ onMounted(async () => {
|
|
|
97
404
|
});
|
|
98
405
|
onBeforeUnmount(() => {
|
|
99
406
|
alive = false;
|
|
100
|
-
|
|
407
|
+
removePullListener();
|
|
408
|
+
removeLoadListener();
|
|
409
|
+
pullLoadReady = null;
|
|
101
410
|
});
|
|
102
411
|
|
|
103
|
-
const pullload = () => {
|
|
412
|
+
const pullload = async () => {
|
|
413
|
+
if (!props.pull) return;
|
|
414
|
+
await ensurePullLoad();
|
|
104
415
|
demo.value?.ispull?.();
|
|
105
416
|
};
|
|
106
417
|
|
|
107
|
-
|
|
418
|
+
const loadmore = async () => {
|
|
419
|
+
if (!props.load || demo.value?.store?.load >= 2) return;
|
|
420
|
+
await ensurePullLoad();
|
|
421
|
+
demo.value?.isload?.();
|
|
422
|
+
};
|
|
423
|
+
|
|
424
|
+
const loaded = () => {
|
|
425
|
+
loadPending = false;
|
|
426
|
+
demo.value?.loaded?.();
|
|
427
|
+
};
|
|
428
|
+
|
|
429
|
+
const noMore = () => {
|
|
430
|
+
loadPending = false;
|
|
431
|
+
demo.value?.isover?.();
|
|
432
|
+
};
|
|
433
|
+
|
|
434
|
+
const resetLoad = () => {
|
|
435
|
+
loadPending = false;
|
|
436
|
+
if (demo.value?.store) {
|
|
437
|
+
demo.value.store.load = 0;
|
|
438
|
+
}
|
|
439
|
+
initialPreloadDone = false;
|
|
440
|
+
scheduleCheckLoad();
|
|
441
|
+
};
|
|
442
|
+
|
|
443
|
+
const scrollToTop = () => {
|
|
444
|
+
if (root.value) {
|
|
445
|
+
root.value.scrollTop = 0;
|
|
446
|
+
}
|
|
447
|
+
};
|
|
448
|
+
|
|
449
|
+
const pulled = () => {
|
|
450
|
+
demo.value?.pulled?.();
|
|
451
|
+
};
|
|
452
|
+
|
|
453
|
+
watch(
|
|
454
|
+
() => props.pull,
|
|
455
|
+
async () => {
|
|
456
|
+
if (props.pull) {
|
|
457
|
+
await ensurePullLoad();
|
|
458
|
+
}
|
|
459
|
+
syncPullLoadHandlers();
|
|
460
|
+
syncPullListener();
|
|
461
|
+
}
|
|
462
|
+
);
|
|
463
|
+
|
|
464
|
+
watch(
|
|
465
|
+
() => props.load,
|
|
466
|
+
async () => {
|
|
467
|
+
if (props.load) {
|
|
468
|
+
await ensurePullLoad();
|
|
469
|
+
}
|
|
470
|
+
syncPullLoadHandlers();
|
|
471
|
+
syncLoadListener();
|
|
472
|
+
}
|
|
473
|
+
);
|
|
474
|
+
|
|
475
|
+
watch(
|
|
476
|
+
() => [props.loadPos, props.loadPosition, props.loadOffset, props.initialLoad],
|
|
477
|
+
() => {
|
|
478
|
+
initialPreloadDone = false;
|
|
479
|
+
scheduleCheckLoad();
|
|
480
|
+
}
|
|
481
|
+
);
|
|
482
|
+
|
|
483
|
+
watch(
|
|
484
|
+
() => demo.value?.store?.load,
|
|
485
|
+
(load) => {
|
|
486
|
+
if (load !== 2) {
|
|
487
|
+
loadPending = false;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
);
|
|
491
|
+
|
|
492
|
+
defineExpose({ pullload, loadmore, loaded, noMore, resetLoad, scrollToTop, pulled, ex: () => demo.value });
|
|
108
493
|
</script>
|
|
109
494
|
|
|
110
495
|
<style lang="scss">
|
|
@@ -144,5 +529,16 @@ defineExpose({ pullload, ex: () => demo.value });
|
|
|
144
529
|
transform: rotate(180deg);
|
|
145
530
|
}
|
|
146
531
|
}
|
|
532
|
+
|
|
533
|
+
.fekit-pullload-over {
|
|
534
|
+
gap: 0.75em;
|
|
535
|
+
&::before,
|
|
536
|
+
&::after {
|
|
537
|
+
content: "";
|
|
538
|
+
width: 3em;
|
|
539
|
+
border-top: 1px solid currentColor;
|
|
540
|
+
opacity: 0.35;
|
|
541
|
+
}
|
|
542
|
+
}
|
|
147
543
|
}
|
|
148
544
|
</style>
|