@allkit/use 0.0.1 → 0.0.3
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/dist/package.json +2 -2
- package/package.json +2 -2
- package/scripts/build.mjs +6 -3
- package/skills/SKILL.md +40 -0
- package/skills/examples/demo.tsx +83 -0
- package/skills/references/defineCtxState.md +61 -0
- package/skills/references/onMountedOrActivated.md +33 -0
- package/skills/references/useCtxState.md +36 -0
- package/skills/references/useEventListener.md +65 -0
- package/skills/references/usePageVisibility.md +45 -0
- package/skills/references/useScroll.md +90 -0
- /package/{skill → dist/skills}/SKILL.md +0 -0
- /package/{skill → dist/skills}/examples/demo.tsx +0 -0
- /package/{skill → dist/skills}/references/defineCtxState.md +0 -0
- /package/{skill → dist/skills}/references/onMountedOrActivated.md +0 -0
- /package/{skill → dist/skills}/references/useCtxState.md +0 -0
- /package/{skill → dist/skills}/references/useEventListener.md +0 -0
- /package/{skill → dist/skills}/references/usePageVisibility.md +0 -0
- /package/{skill → dist/skills}/references/useScroll.md +0 -0
package/dist/package.json
CHANGED
package/package.json
CHANGED
package/scripts/build.mjs
CHANGED
|
@@ -7,7 +7,7 @@ import fse from 'fs-extra'
|
|
|
7
7
|
import { build } from 'vite'
|
|
8
8
|
import { viteLibConfig } from '@allkit/vite-config'
|
|
9
9
|
|
|
10
|
-
const { outputFile } = fse
|
|
10
|
+
const { outputFile, copy } = fse
|
|
11
11
|
|
|
12
12
|
const require = createRequire(import.meta.url)
|
|
13
13
|
|
|
@@ -86,14 +86,17 @@ const buildAll = async () => {
|
|
|
86
86
|
// copy文件
|
|
87
87
|
// README.md
|
|
88
88
|
// 样式 index.css
|
|
89
|
-
|
|
89
|
+
// skills 目录
|
|
90
|
+
const copyFiles = async () => {
|
|
90
91
|
const markdown = createReadStream(resolve(__dirname, '../README.md'))
|
|
91
92
|
markdown.pipe(createWriteStream(resolve(__dirname, '../dist/README.md')))
|
|
93
|
+
|
|
94
|
+
await copy(resolve(__dirname, '../skills'), resolve(__dirname, '../dist/skills'))
|
|
92
95
|
}
|
|
93
96
|
|
|
94
97
|
const buildLib = async () => {
|
|
95
98
|
await buildAll()
|
|
96
|
-
copyFiles()
|
|
99
|
+
await copyFiles()
|
|
97
100
|
}
|
|
98
101
|
|
|
99
102
|
buildLib()
|
package/skills/SKILL.md
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: use
|
|
3
|
+
description: Vue 3 组合式 API 工具函数库,提供状态管理、事件监听和常用 UI 模式 / Vue 3 Composition API hooks library
|
|
4
|
+
author: allkit
|
|
5
|
+
category: vue
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# @allkit/use
|
|
9
|
+
|
|
10
|
+
Vue 3 组合式 API 工具函数库
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pnpm add @allkit/use
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Hooks
|
|
19
|
+
|
|
20
|
+
| Hook | Description |
|
|
21
|
+
|------|-------------|
|
|
22
|
+
| [defineCtxState](./references/defineCtxState.md) | 定义组件作用域共享数据 |
|
|
23
|
+
| [useCtxState](./references/useCtxState.md) | 获取共享数据 |
|
|
24
|
+
| [useEventListener](./references/useEventListener.md) | 事件监听 |
|
|
25
|
+
| [usePageVisibility](./references/usePageVisibility.md) | 页面可见性 |
|
|
26
|
+
| [useScroll](./references/useScroll.md) | 滚动监听 |
|
|
27
|
+
| [onMountedOrActivated](./references/onMountedOrActivated.md) | keep-alive 组件初始化 |
|
|
28
|
+
|
|
29
|
+
## Usage
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
import { defineCtxState, useCtxState, useEventListener } from '@allkit/use'
|
|
33
|
+
|
|
34
|
+
const [state, setState] = defineCtxState({
|
|
35
|
+
loading: false,
|
|
36
|
+
list: []
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
useEventListener('scroll', handleScroll)
|
|
40
|
+
```
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { defineComponent, ref, computed } from 'vue'
|
|
2
|
+
import {
|
|
3
|
+
defineCtxState,
|
|
4
|
+
useCtxState,
|
|
5
|
+
useEventListener,
|
|
6
|
+
usePageVisibility,
|
|
7
|
+
useScroll,
|
|
8
|
+
onMountedOrActivated
|
|
9
|
+
} from '@allkit/use'
|
|
10
|
+
|
|
11
|
+
interface UserListState {
|
|
12
|
+
loading: boolean
|
|
13
|
+
list: Array<{ id: number; name: string }>
|
|
14
|
+
pagination: { current: number; size: number; total: number }
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const useSearchForm = () => {
|
|
18
|
+
const [state, setState] = useCtxState<UserListState>()
|
|
19
|
+
|
|
20
|
+
const handleSearch = () => {
|
|
21
|
+
setState(s => {
|
|
22
|
+
s.loading = true
|
|
23
|
+
s.pagination.current = 1
|
|
24
|
+
})
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return () => (
|
|
28
|
+
<div className="search-form">
|
|
29
|
+
<button onClick={handleSearch} disabled={state.loading}>
|
|
30
|
+
{state.loading ? '搜索中...' : '搜索'}
|
|
31
|
+
</button>
|
|
32
|
+
</div>
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const useList = () => {
|
|
37
|
+
const [state, setState] = useCtxState<UserListState>()
|
|
38
|
+
|
|
39
|
+
return () => (
|
|
40
|
+
<div className="user-list">
|
|
41
|
+
{state.list.map(item => (
|
|
42
|
+
<div key={item.id}>{item.name}</div>
|
|
43
|
+
))}
|
|
44
|
+
</div>
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export default defineComponent({
|
|
49
|
+
setup() {
|
|
50
|
+
const [state, setState] = defineCtxState<UserListState>({
|
|
51
|
+
loading: false,
|
|
52
|
+
list: [],
|
|
53
|
+
pagination: { current: 1, size: 20, total: 0 }
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
const renderSearch = useSearchForm()
|
|
57
|
+
const renderList = useList()
|
|
58
|
+
|
|
59
|
+
useEventListener('resize', () => {
|
|
60
|
+
console.log('window resized')
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
const { onPageShow, onPageHidden } = usePageVisibility()
|
|
64
|
+
onPageShow(() => console.log('page show'))
|
|
65
|
+
onPageHidden(() => console.log('page hidden'))
|
|
66
|
+
|
|
67
|
+
const { y, arrivedState } = useScroll(window, { throttle: 100 })
|
|
68
|
+
const isBottom = computed(() => arrivedState.bottom)
|
|
69
|
+
|
|
70
|
+
onMountedOrActivated(() => {
|
|
71
|
+
console.log('mounted or activated')
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
return () => (
|
|
75
|
+
<div>
|
|
76
|
+
{renderSearch()}
|
|
77
|
+
{renderList()}
|
|
78
|
+
<p>scroll: {Math.round(y.value)}px</p>
|
|
79
|
+
<p>bottom: {isBottom.value ? 'yes' : 'no'}</p>
|
|
80
|
+
</div>
|
|
81
|
+
)
|
|
82
|
+
}
|
|
83
|
+
})
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# defineCtxState
|
|
2
|
+
|
|
3
|
+
定义当前组件作用域的共享数据,配合 `useCtxState` 在子 hooks 中获取。
|
|
4
|
+
|
|
5
|
+
## Type Signature
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
type DeepPartial<T> = {
|
|
9
|
+
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P]
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
type IStateTuple<T> = readonly [
|
|
13
|
+
Readonly<T>,
|
|
14
|
+
(state: DeepPartial<T> | ((newState: T) => void)) => void,
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
function defineCtxState<T extends Record<string, any>>(data: T): IStateTuple<T>
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Parameters
|
|
21
|
+
|
|
22
|
+
| Name | Type | Description |
|
|
23
|
+
|------|------|-------------|
|
|
24
|
+
| `data` | `T` | 需要共享的初始数据 |
|
|
25
|
+
|
|
26
|
+
## Returns
|
|
27
|
+
|
|
28
|
+
返回元组 `[state, setState]`:
|
|
29
|
+
- `state`: 共享数据的只读代理
|
|
30
|
+
- `setState`: 更新共享数据的方法
|
|
31
|
+
|
|
32
|
+
## Example
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
import { defineCtxState } from '@allkit/use'
|
|
36
|
+
|
|
37
|
+
const [state, setState] = defineCtxState({
|
|
38
|
+
listLoading: false,
|
|
39
|
+
userList: [],
|
|
40
|
+
pagination: {
|
|
41
|
+
current: 1,
|
|
42
|
+
size: 20,
|
|
43
|
+
total: 0
|
|
44
|
+
}
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
// 对象写法
|
|
48
|
+
setState({ listLoading: true })
|
|
49
|
+
|
|
50
|
+
// 函数写法
|
|
51
|
+
setState((state) => {
|
|
52
|
+
state.listLoading = true
|
|
53
|
+
state.pagination.current = 2
|
|
54
|
+
})
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Use Cases
|
|
58
|
+
|
|
59
|
+
- 功能模块入口页面需要与多个子 hooks 共享状态
|
|
60
|
+
- 避免通过 props 层层传递数据
|
|
61
|
+
- 实现 hooks 之间的数据通信
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# onMountedOrActivated
|
|
2
|
+
|
|
3
|
+
在 `mounted` 或 `activated` (keep-alive 缓存激活) 时执行回调。
|
|
4
|
+
|
|
5
|
+
## Type Signature
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
function onMountedOrActivated(hook: () => any): void
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Parameters
|
|
12
|
+
|
|
13
|
+
| Name | Type | Description |
|
|
14
|
+
|------|------|-------------|
|
|
15
|
+
| `hook` | `() => any` | 要执行的回调函数 |
|
|
16
|
+
|
|
17
|
+
## Example
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import { onMountedOrActivated } from '@allkit/use'
|
|
21
|
+
|
|
22
|
+
// 在 mounted 和每次 activated 时执行
|
|
23
|
+
onMountedOrActivated(() => {
|
|
24
|
+
console.log('组件已挂载或激活')
|
|
25
|
+
// 刷新数据等操作
|
|
26
|
+
})
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Use Cases
|
|
30
|
+
|
|
31
|
+
- keep-alive 缓存组件需要在每次激活时刷新数据
|
|
32
|
+
- 需要在 mounted 和 activated 时都执行的初始化逻辑
|
|
33
|
+
- 配合 `useEventListener` 实现事件监听的自动管理
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# useCtxState
|
|
2
|
+
|
|
3
|
+
获取当前组件作用域 `defineCtxState` 定义的共享数据。
|
|
4
|
+
|
|
5
|
+
## Type Signature
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
function useCtxState<T = Record<string, any>>(): IStateTuple<T>
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Returns
|
|
12
|
+
|
|
13
|
+
返回元组 `[state, setState]`,与 `defineCtxState` 相同。
|
|
14
|
+
|
|
15
|
+
## Example
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { useCtxState } from '@allkit/use'
|
|
19
|
+
|
|
20
|
+
export const useList = () => {
|
|
21
|
+
const [state, setState] = useCtxState<ListState>()
|
|
22
|
+
|
|
23
|
+
const handleReset = () => {
|
|
24
|
+
setState((state) => {
|
|
25
|
+
state.searchForm = {}
|
|
26
|
+
})
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return () => <div>{state.userList.length}</div>
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Notes
|
|
34
|
+
|
|
35
|
+
- 必须在 `setup()` 函数中调用
|
|
36
|
+
- 只能获取同一组件实例中 `defineCtxState` 定义的数据
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# useEventListener
|
|
2
|
+
|
|
3
|
+
事件监听,支持 Vue 缓存(keep-alive),页面销毁自动回收。
|
|
4
|
+
|
|
5
|
+
## Type Signature
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
type UseEventListenerOptions = {
|
|
9
|
+
target?: TargetRef
|
|
10
|
+
capture?: boolean
|
|
11
|
+
passive?: boolean
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function useEventListener<K extends keyof DocumentEventMap>(
|
|
15
|
+
type: K,
|
|
16
|
+
listener: (event: DocumentEventMap[K]) => void,
|
|
17
|
+
options?: UseEventListenerOptions,
|
|
18
|
+
): () => void
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Parameters
|
|
22
|
+
|
|
23
|
+
| Name | Type | Default | Description |
|
|
24
|
+
|------|------|---------|-------------|
|
|
25
|
+
| `type` | `string` | - | 事件类型 (scroll, resize, click 等) |
|
|
26
|
+
| `listener` | `EventListener` | - | 监听函数 |
|
|
27
|
+
| `options.target` | `TargetRef` | `window` | 监听目标 |
|
|
28
|
+
| `options.capture` | `boolean` | `false` | 是否捕获 |
|
|
29
|
+
| `options.passive` | `boolean` | `false` | 是否被动 |
|
|
30
|
+
|
|
31
|
+
## Returns
|
|
32
|
+
|
|
33
|
+
返回清理函数,调用后移除事件监听。
|
|
34
|
+
|
|
35
|
+
## Example
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
import { useEventListener } from '@allkit/use'
|
|
39
|
+
|
|
40
|
+
// 监听 window 滚动
|
|
41
|
+
const cleanup = useEventListener('scroll', () => {
|
|
42
|
+
console.log('scrolling')
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
// 监听指定元素
|
|
46
|
+
const boxRef = ref<HTMLElement>()
|
|
47
|
+
useEventListener('click', (e) => {
|
|
48
|
+
console.log('clicked', e)
|
|
49
|
+
}, { target: boxRef })
|
|
50
|
+
|
|
51
|
+
// 监听键盘事件
|
|
52
|
+
useEventListener('keydown', (e: KeyboardEvent) => {
|
|
53
|
+
if (e.key === 'Escape') {
|
|
54
|
+
// handle escape
|
|
55
|
+
}
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
// 组件卸载时自动清理
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Use Cases
|
|
62
|
+
|
|
63
|
+
- 监听 window/document 事件(resize、scroll、keydown 等)
|
|
64
|
+
- 监听 DOM 元素事件
|
|
65
|
+
- keep-alive 组件需要自动添加/移除事件监听
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# usePageVisibility
|
|
2
|
+
|
|
3
|
+
判断页面是否可见或不可见,用于处理用户切换标签页的场景。
|
|
4
|
+
|
|
5
|
+
## Type Signature
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
function usePageVisibility(): {
|
|
9
|
+
onPageShow: (cb: () => void) => void
|
|
10
|
+
onPageHidden: (cb: () => void) => void
|
|
11
|
+
}
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Returns
|
|
15
|
+
|
|
16
|
+
| Method | Description |
|
|
17
|
+
|--------|-------------|
|
|
18
|
+
| `onPageShow` | 页面显示时执行的回调 |
|
|
19
|
+
| `onPageHidden` | 页面隐藏时执行的回调 |
|
|
20
|
+
|
|
21
|
+
## Example
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import { usePageVisibility } from '@allkit/use'
|
|
25
|
+
|
|
26
|
+
const { onPageShow, onPageHidden } = usePageVisibility()
|
|
27
|
+
|
|
28
|
+
// 页面显示可见
|
|
29
|
+
onPageShow(() => {
|
|
30
|
+
console.log('页面可见')
|
|
31
|
+
// 恢复轮询、视频播放等
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
// 页面隐藏
|
|
35
|
+
onPageHidden(() => {
|
|
36
|
+
console.log('页面隐藏')
|
|
37
|
+
// 暂停轮询、视频播放等
|
|
38
|
+
})
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Use Cases
|
|
42
|
+
|
|
43
|
+
- 用户切换浏览器标签页时暂停/恢复操作
|
|
44
|
+
- 页面隐藏时停止轮询请求,显示时恢复
|
|
45
|
+
- 视频播放器在页面隐藏时自动暂停
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# useScroll
|
|
2
|
+
|
|
3
|
+
监听页面布局滚动事件,提供滚动位置、方向、状态等信息。
|
|
4
|
+
|
|
5
|
+
## Type Signature
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
interface UseScrollOptions {
|
|
9
|
+
throttle?: number
|
|
10
|
+
idle?: number
|
|
11
|
+
offset?: {
|
|
12
|
+
left?: number
|
|
13
|
+
right?: number
|
|
14
|
+
top?: number
|
|
15
|
+
bottom?: number
|
|
16
|
+
}
|
|
17
|
+
onScroll?: (e: Event) => void
|
|
18
|
+
onStop?: (e: Event) => void
|
|
19
|
+
eventListenerOptions?: AddEventListenerOptions
|
|
20
|
+
behavior?: ScrollBehavior
|
|
21
|
+
onError?: (error: unknown) => void
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function useScroll(
|
|
25
|
+
element: MaybeElementRef<MaybeElement>,
|
|
26
|
+
options?: UseScrollOptions
|
|
27
|
+
): {
|
|
28
|
+
x: ComputedRef<number>
|
|
29
|
+
y: ComputedRef<number>
|
|
30
|
+
isScrolling: Ref<boolean>
|
|
31
|
+
arrivedState: Reactive<{ left: boolean; right: boolean; top: boolean; bottom: boolean }>
|
|
32
|
+
directions: Reactive<{ left: boolean; right: boolean; top: boolean; bottom: boolean }>
|
|
33
|
+
measure: () => void
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Parameters
|
|
38
|
+
|
|
39
|
+
| Name | Type | Default | Description |
|
|
40
|
+
|------|------|---------|-------------|
|
|
41
|
+
| `element` | `MaybeElementRef` | - | 监听的元素,支持 window、document、HTMLElement 及 Ref |
|
|
42
|
+
| `options.throttle` | `number` | `0` | 节流时间 |
|
|
43
|
+
| `options.idle` | `number` | `200` | 滚动结束检测时间 |
|
|
44
|
+
| `options.offset` | `object` | `{}` | 边界偏移 |
|
|
45
|
+
| `options.onScroll` | `function` | - | 滚动时回调 |
|
|
46
|
+
| `options.onStop` | `function` | - | 滚动结束时回调 |
|
|
47
|
+
|
|
48
|
+
## Returns
|
|
49
|
+
|
|
50
|
+
| Property | Type | Description |
|
|
51
|
+
|----------|------|-------------|
|
|
52
|
+
| `x` | `ComputedRef<number>` | 水平滚动距离 |
|
|
53
|
+
| `y` | `ComputedRef<number>` | 垂直滚动距离 |
|
|
54
|
+
| `isScrolling` | `Ref<boolean>` | 是否正在滚动 |
|
|
55
|
+
| `arrivedState` | `Reactive` | 是否到达边界 `{ left, right, top, bottom }` |
|
|
56
|
+
| `directions` | `Reactive` | 滚动方向 `{ left, right, top, bottom }` |
|
|
57
|
+
| `measure` | `function` | 手动测量滚动状态 |
|
|
58
|
+
|
|
59
|
+
## Example
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
import { useScroll } from '@allkit/use'
|
|
63
|
+
|
|
64
|
+
// 监听 window 滚动
|
|
65
|
+
const { y, isScrolling, arrivedState } = useScroll(window)
|
|
66
|
+
|
|
67
|
+
// 监听指定元素
|
|
68
|
+
const containerRef = ref<HTMLElement>()
|
|
69
|
+
const scrollState = useScroll(containerRef, {
|
|
70
|
+
throttle: 100,
|
|
71
|
+
onScroll: (e) => {
|
|
72
|
+
console.log('scrolling')
|
|
73
|
+
},
|
|
74
|
+
onStop: (e) => {
|
|
75
|
+
console.log('scroll stopped')
|
|
76
|
+
}
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
// 判断是否滚动到底部
|
|
80
|
+
if (arrivedState.bottom) {
|
|
81
|
+
// 加载更多数据
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Use Cases
|
|
86
|
+
|
|
87
|
+
- 无限滚动列表,滚动到底部加载更多
|
|
88
|
+
- 吸顶导航栏,滚动超过一定距离显示
|
|
89
|
+
- 滚动进度指示器
|
|
90
|
+
- 虚拟列表滚动位置追踪
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|