cbvirtua 1.0.7 → 1.0.9

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 ADDED
@@ -0,0 +1,270 @@
1
+ # vue-presetup
2
+
3
+ > 仅适用于 Vue3
4
+
5
+ vue-presetup 可以将指定的 Vue 组件进行提前加载,并在组件“准备好”后再进行下一步操作,
6
+
7
+ 如从当前路由 /foo 跳转至 /bar 时,可以先提前在 /foo 页面加载 /bar 对应的组件,随后再进行路由切换
8
+
9
+ ## 安装
10
+
11
+ 通过 npm 安装
12
+
13
+ ```shell
14
+ npm install vue-presetup -S
15
+ ```
16
+
17
+ 通过 yarn 安装
18
+
19
+ ```shell
20
+ yarn add vue-presetup -S
21
+ ```
22
+
23
+ 通过 pnpm 安装
24
+
25
+ ```shell
26
+ pnpm install vue-presetup -S
27
+ ```
28
+
29
+ ## 基础使用示例
30
+
31
+ 有一个简单的使用示例,这个示例是摘自 [playground](./playground) 中的
32
+
33
+ 1. 有以下的路由配置
34
+
35
+ **router.ts**
36
+
37
+ ```ts
38
+ import { createRouter, createWebHashHistory } from 'vue-router'
39
+ import Foo from './views/Foo.vue'
40
+ import Bar from './views/Bar.vue'
41
+
42
+ const router = createRouter({
43
+ history: createWebHashHistory(),
44
+ routes: [
45
+ {
46
+ path: '/',
47
+ redirect: '/foo'
48
+ },
49
+ {
50
+ path: '/foo',
51
+ component: Foo
52
+ },
53
+ {
54
+ path: '/bar',
55
+ component: Bar
56
+ }
57
+ ]
58
+ })
59
+
60
+ export default router
61
+ ```
62
+
63
+ 2. 在 App.vue 使用路由并放置 vue-presetup 需要的载体
64
+
65
+ **App.vue**
66
+
67
+ ```vue
68
+ <script setup lang="ts">
69
+ import { PresetupView } from 'vue-presetup'
70
+ </script>
71
+
72
+ <template>
73
+ <RouterView v-slot="{ Component }">
74
+ <PresetupView :active="Component"></PresetupView>
75
+ </RouterView>
76
+ </template>
77
+ ```
78
+
79
+ 3. 在可能被提前加载的 /bar 组件需要做一些反馈,来告知当前组件什么时候准备好(实际情况可能是首屏数据加载好了啥的)
80
+
81
+ **Bar.vue**
82
+
83
+ ```vue
84
+ <script lang="ts" setup>
85
+ import { ref } from 'vue'
86
+ import { useContext } from 'vue-presetup'
87
+
88
+ const context = useContext()
89
+
90
+ window.setTimeout(() => {
91
+ // 在 1500ms 后,这个组件准备好了!
92
+ context.resolve()
93
+ }, 1500)
94
+ </script>
95
+
96
+ <template>
97
+ <div class="Bar">
98
+ <h1>Bar.vue</h1>
99
+ </div>
100
+ </template>
101
+ ```
102
+
103
+ 4. 接下来,就可以实现在 /foo 中提前加载 /bar 对应的组件
104
+
105
+ **Foo.bar**
106
+
107
+ ```vue
108
+ <script lang="ts" setup>
109
+ import { ref } from 'vue'
110
+ import { useSetupComponent } from 'vue-presetup'
111
+ import router from '../router'
112
+ import Bar from './Bar.vue'
113
+
114
+ const { setupComponent } = useSetupComponent()
115
+
116
+ const go = async () => {
117
+ // 等待组件加载完成
118
+ await setupComponent(Bar)
119
+ router.push('/bar')
120
+ }
121
+ </script>
122
+
123
+ <template>
124
+ <div class="Foo">
125
+ <h1>Foo.vue</h1>
126
+ <button @click="go">Go</ElButton>
127
+ </div>
128
+ </template>
129
+ ```
130
+
131
+ ## 与 Transition、KeepAlive 一起使用
132
+
133
+ 编辑上述示例中 **App.vue**
134
+
135
+ ```vue
136
+ <script> /* ... */ </script>
137
+ <template>
138
+ <RouterView v-slot="{ Component }">
139
+ <PresetupView :active="Component" v-slot="{ Component: Component2 }">
140
+ <Transition name="fade" mode="out-in">
141
+ <KeepAlive include="foo">
142
+ <component :is="Component2"></component>
143
+ </KeepAlive>
144
+ </Transition>
145
+ </PresetupView>
146
+ </RouterView>
147
+ </template>
148
+ ```
149
+
150
+ ## API
151
+
152
+ ### PresetupView
153
+
154
+ Type: Component
155
+
156
+ Props:
157
+
158
+ + `active`: 期望渲染的 VNode
159
+
160
+ ### useSetupComponent
161
+
162
+ Type: Hook
163
+
164
+ Return: { setupComponent }
165
+
166
+ > 这是一个 Vue3 Hook,因此只能在 setup 函数中使用
167
+
168
+ ```vue
169
+ <script setup lang="ts">
170
+ import { useSetupComponent } from 'vue-presetup'
171
+ import Bar from './Bar.vue'
172
+
173
+ const { setupComponent } = useSetupComponent()
174
+
175
+ const next = async () => {
176
+ await setupComponent(Bar, /* props */)
177
+ }
178
+ </script>
179
+ ```
180
+
181
+ #### setupComponent
182
+
183
+ Params:
184
+
185
+ + Component
186
+
187
+ 需要加载的组件对象,这个组件必须有 `name` 属性,否则会收到一个被 reject 的 Promise;
188
+
189
+ 同一组件(相同的 `name` ,后同)可预加载的数量是有限的(一个),在后面预加载的组件会覆盖之前已加载过的同一组件。
190
+
191
+ + props
192
+
193
+ 传递给组件的参数,在 Vue 组件中是区分 Props 和 Attrs 的,因此在组件内部接收这个参数时候需要声明 Props,否则会给 Vue 理解为 Attrs,如下面这个示例,在 Comp.vue 中,p1,p2 在组件的 props 中,p3 是 attrs 中:
194
+
195
+ Comp.vue
196
+
197
+ ```vue
198
+ <script setup lang="ts">
199
+ const props = deineProps(['p1', 'p2'])
200
+ </script>
201
+ ```
202
+
203
+ Foo.vue
204
+
205
+ ```vue
206
+ <script setup lang="ts">
207
+ import { useSetupComponent } from 'vue-presetup'
208
+ import Comp from './Comp.vue'
209
+
210
+ const { setupComponent } = useSetupComponent()
211
+
212
+ const next = async () => {
213
+ await setupComponent(Comp, {
214
+ p1: 1,
215
+ p2: 2,
216
+ p3: 3
217
+ })
218
+ }
219
+ </script>
220
+ ```
221
+
222
+ ### useContext
223
+
224
+ Type: Hook
225
+
226
+ Return Context
227
+
228
+ > 这是一个 Vue3 Hook,因此只能在 setup 函数中使用
229
+
230
+ 用在可能会被预加载的组件中,如基础使用示例的 Bar.vue
231
+
232
+ #### Context
233
+
234
+ ```ts
235
+ interface UseContextResult {
236
+ // 在当前组件“准备好”调用,以告知预加载完成。如当前组件不是通过预加载,此方法什么都不会发生。
237
+ resolve: Callback
238
+ // 在当前组件中预加载发生错误时候调用。如当前组件不是通过预加载,此方法什么都不会发生。
239
+ reject: Callback
240
+ // 获取在 setupComponent 中传递的参数。如当前组件不是通过预加载,此方法没有办法获取任何值,除非你提供了 defaultValue
241
+ get<R = unknown>(key: string): R | undefined
242
+ get<R = unknown>(key: string, defaultValue: R): R
243
+ }
244
+ ```
245
+
246
+ ### removeComponent
247
+
248
+ 将一个已预加载的组件移除
249
+
250
+ Params:
251
+
252
+ + `name`: 被移除组件的 name
253
+
254
+ ```vue
255
+ <script setup lang="ts">
256
+ import { useSetupComponent, removeComponent } from 'vue-presetup'
257
+ import Bar from './Bar.vue'
258
+
259
+ const { setupComponent } = useSetupComponent()
260
+
261
+ const next = async () => {
262
+ await setupComponent(Bar, /* props */)
263
+ removeComponent(Bar.name)
264
+ }
265
+ </script>
266
+ ```
267
+
268
+ ## License
269
+
270
+ [MIT](https://github.com/haiya6/vite-plugin-html-resolve-alias/blob/main/LICENSE)
package/asy.js ADDED
@@ -0,0 +1,32 @@
1
+ async function asyncTaskWithCb(cb) {
2
+ let err, user, savedTask, notification;
3
+
4
+ [ err, user ] = await to(UserModel.findById(1));
5
+ if(!user) return cb('No user found');
6
+
7
+ [ err, savedTask ] = await to(TaskModel({userId: user.id, name: 'Demo Task'}));
8
+ if(err) return cb('Error occurred while saving task');
9
+
10
+ if(user.notificationsEnabled) {
11
+ [ err ] = await to(NotificationService.sendNotification(user.id, 'Task Created'));
12
+ if(err) return cb('Error while sending notification');
13
+ }
14
+
15
+ if(savedTask.assignedUser.id !== user.id) {
16
+ [ err, notification ] = await to(NotificationService.sendNotification(savedTask.assignedUser.id, 'Task was created for you'));
17
+ if(err) return cb('Error while sending notification');
18
+ }
19
+
20
+ cb(null, savedTask);
21
+ }
22
+
23
+ function to(promise, errorExt) {
24
+ return promise
25
+ .then(function (data) { return [null, data]; }) // 执行成功,返回数组第一项为 null。第二个是结果。
26
+ .catch(function (err) {
27
+ if (errorExt) {
28
+ Object.assign(err, errorExt);
29
+ }
30
+ return [err, undefined]; // 执行失败,返回数组第一项为错误信息,第二项为 undefined
31
+ });
32
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cbvirtua",
3
- "version": "1.0.7",
3
+ "version": "1.0.9",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/pre/index.js ADDED
@@ -0,0 +1,93 @@
1
+ import { h, render, defineComponent, getCurrentInstance } from 'vue';
2
+ const COMPONENT_KEPT_ALIVE = 1 << 9;
3
+ const CONTEXT_KEY = Symbol('VUE_PRESETUP_CONTEXT_KEY');
4
+ const NOOP = () => { };
5
+ const cache = new Map();
6
+ export const PresetupView = defineComponent({
7
+ name: 'PresetupView',
8
+ props: {
9
+ active: {
10
+ type: Object
11
+ }
12
+ },
13
+ setup(props, { slots }) {
14
+ const instance = getCurrentInstance();
15
+ // @ts-expect-error
16
+ instance.ctx.activate = (vnode, container, anchor) => {
17
+ container.insertBefore(vnode.component.subTree.el, anchor);
18
+ };
19
+ return () => {
20
+ const { active } = props;
21
+ const normalizeSlot = (vnode) => {
22
+ if (slots.default) {
23
+ return slots.default({ Component: vnode });
24
+ }
25
+ return vnode;
26
+ };
27
+ if (!active)
28
+ return null;
29
+ const name = typeof active.type === 'object' ? active.type.name : '';
30
+ if (!name || !cache.has(name))
31
+ return normalizeSlot(active);
32
+ const data = cache.get(name);
33
+ cache.delete(name);
34
+ return normalizeSlot(data.vnode);
35
+ };
36
+ }
37
+ });
38
+ export function useContext() {
39
+ const instance = getCurrentInstance();
40
+ if (!instance)
41
+ console.warn(`[vue-presetup] useContext() called without active instance.`);
42
+ // @ts-expect-error
43
+ const context = instance?.vnode?.[CONTEXT_KEY] ?? {};
44
+ const get = (key, defaultValue) => {
45
+ const props = context?.props ?? {};
46
+ if ((key in props) && props[key] !== void 0)
47
+ return props[key];
48
+ else
49
+ return defaultValue;
50
+ };
51
+ return {
52
+ resolve: context?.resolve ?? NOOP,
53
+ reject: context?.resolve ?? NOOP,
54
+ get
55
+ };
56
+ }
57
+ export function useSetupComponent() {
58
+ const instance = getCurrentInstance();
59
+ if (!instance)
60
+ throw new Error(`[vue-presetup] useSetupComponent() called without active instance.`);
61
+ return {
62
+ setupComponent(component, props = {}) {
63
+ if (!component.name)
64
+ return Promise.reject(new Error(`[vue-presetup] The component must have \`name\` attribute.`));
65
+ return new Promise((resolve, reject) => {
66
+ const container = window.document.createElement('div');
67
+ const vnode = h(component, props);
68
+ // bind appContext
69
+ vnode.appContext = instance.appContext;
70
+ // bind presetup context
71
+ const context = { props, resolve, reject };
72
+ // @ts-expect-error
73
+ vnode[CONTEXT_KEY] = context;
74
+ if (cache.has(component.name))
75
+ removeComponent(component.name);
76
+ cache.set(component.name, { vnode, container });
77
+ render(vnode, container);
78
+ vnode.shapeFlag |= COMPONENT_KEPT_ALIVE;
79
+ });
80
+ }
81
+ };
82
+ }
83
+ export function removeComponent(name) {
84
+ const data = cache.get(name);
85
+ if (!data) {
86
+ if (process.env.NODE_ENV === 'development') {
87
+ console.warn(`[vue-presetup] No component with name \`${name}\` found.`);
88
+ }
89
+ return;
90
+ }
91
+ render(null, data.container);
92
+ cache.delete(name);
93
+ }
@@ -0,0 +1,69 @@
1
+ @sides: {
2
+ top: bottom;
3
+ bottom: top;
4
+ left: right;
5
+ right: left;
6
+ }
7
+ @N: .van-popper;
8
+ @{N} {
9
+ --van-arrow-size: 10px;
10
+ --van-popper-content-bg: #fff;
11
+ --van-popper-border: 1px solid #ebedf0;
12
+ border-radius: 4px;
13
+ color: #000;
14
+ background-color: var(--van-popper-content-bg);
15
+ padding: 10px 12px;
16
+ border: var(--van-popper-border);
17
+ position: absolute;
18
+ white-space: nowrap;
19
+ transition: opacity .3s;
20
+ font-size: 13px;
21
+ z-index: 1000;
22
+ width: max-content;
23
+
24
+ &__arrow {
25
+ position: absolute;
26
+ width: 10px;
27
+ height: 10px;
28
+ background: var(--van-popper-content-bg);
29
+ transform: rotate(45deg);
30
+ }
31
+
32
+ each(@sides, {
33
+ &[data-side^='@{value}'] {
34
+ @{N}__arrow {
35
+ @{key}: -5px;
36
+ }
37
+ @{N}__arrow {
38
+ border-@{key}: var(--van-popper-border);
39
+ }
40
+ @{N}__arrow when (@value =top) {
41
+ border-right: var(--van-popper-border);
42
+ }
43
+ @{N}__arrow when (@value =bottom) {
44
+ border-left: var(--van-popper-border);
45
+ }
46
+ @{N}__arrow when (@value =left) {
47
+ border-top: var(--van-popper-border);
48
+ }
49
+ @{N}__arrow when (@value =right) {
50
+ border-bottom: var(--van-popper-border);
51
+ }
52
+ }
53
+ });
54
+
55
+ &.is-dark {
56
+ --van-popper-content-bg: #000;
57
+ color: #fff;
58
+ border: none;
59
+
60
+ @{N}__arrow {
61
+ border-color: transparent;
62
+ }
63
+ }
64
+ }
65
+
66
+ .van-popper-fade-enter-from,
67
+ .van-popper-fade-leave-to {
68
+ opacity: 0;
69
+ }
@@ -0,0 +1,125 @@
1
+ @import '../../style/themes/index';
2
+ @import '../../style/mixins/index';
3
+ @import './mixin';
4
+
5
+ @row-prefix-cls: ~'@{ant-prefix}-row';
6
+ @col-prefix-cls: ~'@{ant-prefix}-col';
7
+
8
+ // Grid system
9
+ .@{row-prefix-cls} {
10
+ display: flex;
11
+ flex-flow: row wrap;
12
+
13
+ &::before,
14
+ &::after {
15
+ display: flex;
16
+ }
17
+
18
+ // No wrap of flex
19
+ &-no-wrap {
20
+ flex-wrap: nowrap;
21
+ }
22
+ }
23
+
24
+ // x轴原点
25
+ .@{row-prefix-cls}-start {
26
+ justify-content: flex-start;
27
+ }
28
+
29
+ // x轴居中
30
+ .@{row-prefix-cls}-center {
31
+ justify-content: center;
32
+ }
33
+
34
+ // x轴反方向
35
+ .@{row-prefix-cls}-end {
36
+ justify-content: flex-end;
37
+ }
38
+
39
+ // x轴平分
40
+ .@{row-prefix-cls}-space-between {
41
+ justify-content: space-between;
42
+ }
43
+
44
+ // x轴有间隔地平分
45
+ .@{row-prefix-cls}-space-around {
46
+ justify-content: space-around;
47
+ }
48
+
49
+ // 顶部对齐
50
+ .@{row-prefix-cls}-top {
51
+ align-items: flex-start;
52
+ }
53
+
54
+ // 居中对齐
55
+ .@{row-prefix-cls}-middle {
56
+ align-items: center;
57
+ }
58
+
59
+ // 底部对齐
60
+ .@{row-prefix-cls}-bottom {
61
+ align-items: flex-end;
62
+ }
63
+
64
+ .@{col-prefix-cls} {
65
+ position: relative;
66
+ max-width: 100%;
67
+ // Prevent columns from collapsing when empty
68
+ min-height: 1px;
69
+ }
70
+
71
+ .make-grid();
72
+
73
+ // Extra small grid
74
+ //
75
+ // Columns, offsets, pushes, and pulls for extra small devices like
76
+ // smartphones.
77
+
78
+ .make-grid(-xs);
79
+
80
+ // Small grid
81
+ //
82
+ // Columns, offsets, pushes, and pulls for the small device range, from phones
83
+ // to tablets.
84
+
85
+ @media (min-width: @screen-sm-min) {
86
+ .make-grid(-sm);
87
+ }
88
+
89
+ // Medium grid
90
+ //
91
+ // Columns, offsets, pushes, and pulls for the desktop device range.
92
+
93
+ @media (min-width: @screen-md-min) {
94
+ .make-grid(-md);
95
+ }
96
+
97
+ // Large grid
98
+ //
99
+ // Columns, offsets, pushes, and pulls for the large desktop device range.
100
+
101
+ @media (min-width: @screen-lg-min) {
102
+ .make-grid(-lg);
103
+ }
104
+
105
+ // Extra Large grid
106
+ //
107
+ // Columns, offsets, pushes, and pulls for the full hd device range.
108
+
109
+ @media (min-width: @screen-xl-min) {
110
+ .make-grid(-xl);
111
+ }
112
+
113
+ // Extra Extra Large grid
114
+ //
115
+ // Columns, offsets, pushes, and pulls for the full hd device range.
116
+
117
+ @media (min-width: @screen-xxl-min) {
118
+ .make-grid(-xxl);
119
+ }
120
+
121
+ @media (min-width: @screen-xxxl-min) {
122
+ .make-grid(-xxxl);
123
+ }
124
+
125
+ @import './rtl';
@@ -0,0 +1,2 @@
1
+ import '../../style/index.less';
2
+ import './index.less';
@@ -0,0 +1,53 @@
1
+ @import '../../style/mixins/index';
2
+
3
+ // mixins for grid system
4
+ // ------------------------
5
+
6
+ .loop-grid-columns(@index, @class) when (@index > 0) {
7
+ .@{ant-prefix}-col@{class}-@{index} {
8
+ display: block;
9
+ flex: 0 0 percentage((@index / @grid-columns));
10
+ max-width: percentage((@index / @grid-columns));
11
+ }
12
+ .@{ant-prefix}-col@{class}-push-@{index} {
13
+ left: percentage((@index / @grid-columns));
14
+ }
15
+ .@{ant-prefix}-col@{class}-pull-@{index} {
16
+ right: percentage((@index / @grid-columns));
17
+ }
18
+ .@{ant-prefix}-col@{class}-offset-@{index} {
19
+ margin-left: percentage((@index / @grid-columns));
20
+ }
21
+ .@{ant-prefix}-col@{class}-order-@{index} {
22
+ order: @index;
23
+ }
24
+ .loop-grid-columns((@index - 1), @class);
25
+ }
26
+
27
+ .loop-grid-columns(@index, @class) when (@index = 0) {
28
+ .@{ant-prefix}-col@{class}-@{index} {
29
+ display: none;
30
+ }
31
+ .@{ant-prefix}-col-push-@{index} {
32
+ left: auto;
33
+ }
34
+ .@{ant-prefix}-col-pull-@{index} {
35
+ right: auto;
36
+ }
37
+ .@{ant-prefix}-col@{class}-push-@{index} {
38
+ left: auto;
39
+ }
40
+ .@{ant-prefix}-col@{class}-pull-@{index} {
41
+ right: auto;
42
+ }
43
+ .@{ant-prefix}-col@{class}-offset-@{index} {
44
+ margin-left: 0;
45
+ }
46
+ .@{ant-prefix}-col@{class}-order-@{index} {
47
+ order: 0;
48
+ }
49
+ }
50
+
51
+ .make-grid(@class: ~'') {
52
+ .loop-grid-columns(@grid-columns, @class);
53
+ }