@live-change/vue3-components 0.9.201 → 0.9.203
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 +27 -0
- package/package.json +4 -3
- package/view/ReactiveRangeViewer.vue +207 -0
- package/view/index.js +4 -2
package/README.md
CHANGED
|
@@ -60,6 +60,33 @@ const synchronizedEvent = synchronized({
|
|
|
60
60
|
const editable = synchronizedEvent.value
|
|
61
61
|
```
|
|
62
62
|
|
|
63
|
+
Przykład użycia `synchronizedList` (fragment z `rcstreamer`):
|
|
64
|
+
|
|
65
|
+
```javascript
|
|
66
|
+
import { synchronizedList } from '@live-change/vue3-components'
|
|
67
|
+
|
|
68
|
+
const synchronizedAccessesList = synchronizedList({
|
|
69
|
+
source: accesses,
|
|
70
|
+
update: accessControlApi.updateSessionOrUserAndObjectOwnedAccess,
|
|
71
|
+
delete: accessControlApi.resetSessionOrUserAndObjectOwnedAccess,
|
|
72
|
+
identifiers: { object, objectType },
|
|
73
|
+
objectIdentifiers: ({ to, sessionOrUser, sessionOrUserType }) => ({
|
|
74
|
+
access: to, sessionOrUser, sessionOrUserType, object, objectType
|
|
75
|
+
}),
|
|
76
|
+
recursive: true
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
const synchronizedAccesses = synchronizedAccessesList.value
|
|
80
|
+
await synchronizedAccessesList.delete(synchronizedAccesses.value[0])
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
`synchronizedList` jest przeznaczony do edycji list elementów (np. role dostępów), gdzie:
|
|
84
|
+
|
|
85
|
+
- `source` jest tablicą z `live(...)`,
|
|
86
|
+
- każdy element ma stabilne `id`,
|
|
87
|
+
- `identifiers` przekazuje kontekst wspólny dla listy,
|
|
88
|
+
- `objectIdentifiers` mapuje identyfikatory pojedynczego elementu pod akcje backendu.
|
|
89
|
+
|
|
63
90
|
### Formularze: `DefinedForm`, `CommandForm`, `FormBind`
|
|
64
91
|
|
|
65
92
|
- **`DefinedForm`** – renderuje formularz na podstawie definicji (np. definicji akcji)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@live-change/vue3-components",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.203",
|
|
4
4
|
"description": "Live Change Framework - vue components",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -21,11 +21,12 @@
|
|
|
21
21
|
},
|
|
22
22
|
"homepage": "https://github.com/live-change/live-change-stack",
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@live-change/vue3-ssr": "^0.9.
|
|
24
|
+
"@live-change/vue3-ssr": "^0.9.203",
|
|
25
|
+
"@vueuse/core": "^12.3.0",
|
|
25
26
|
"debug": "^4.3.4",
|
|
26
27
|
"mitt": "3.0.1",
|
|
27
28
|
"vue": "^3.5.12",
|
|
28
29
|
"vue3-scroll-border": "0.1.7"
|
|
29
30
|
},
|
|
30
|
-
"gitHead": "
|
|
31
|
+
"gitHead": "4d4ce413fe747da2c398eb4ad378e37cc81874b3"
|
|
31
32
|
}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<component :is="tag" ref="rootEl">
|
|
3
|
+
<div
|
|
4
|
+
v-if="showPlaceholder"
|
|
5
|
+
class="reactive-range-viewer-placeholder"
|
|
6
|
+
:style="{ minHeight: `${placeholderHeight}px` }"
|
|
7
|
+
/>
|
|
8
|
+
<RangeViewer
|
|
9
|
+
v-else-if="currentBuckets"
|
|
10
|
+
:key="viewerKey"
|
|
11
|
+
v-bind="forwardedViewerProps"
|
|
12
|
+
:buckets="currentBuckets"
|
|
13
|
+
v-on="forwardedListeners"
|
|
14
|
+
>
|
|
15
|
+
<template v-for="(_, slotName) in slots" #[slotName]="slotProps">
|
|
16
|
+
<slot :name="slotName" v-bind="slotProps" />
|
|
17
|
+
</template>
|
|
18
|
+
</RangeViewer>
|
|
19
|
+
</component>
|
|
20
|
+
</template>
|
|
21
|
+
|
|
22
|
+
<script setup>
|
|
23
|
+
import { computed, onBeforeUnmount, ref, useAttrs, useSlots, watch } from 'vue'
|
|
24
|
+
import { useElementSize } from '@vueuse/core'
|
|
25
|
+
import { rangeBuckets } from '@live-change/vue3-ssr'
|
|
26
|
+
import RangeViewer from './RangeViewer.vue'
|
|
27
|
+
|
|
28
|
+
const props = defineProps({
|
|
29
|
+
pathFunction: {
|
|
30
|
+
type: Function,
|
|
31
|
+
required: true
|
|
32
|
+
},
|
|
33
|
+
sourceKey: {
|
|
34
|
+
default: undefined
|
|
35
|
+
},
|
|
36
|
+
preserveHeightOnReload: {
|
|
37
|
+
type: Boolean,
|
|
38
|
+
default: false
|
|
39
|
+
},
|
|
40
|
+
reloadOnPathFunctionChange: {
|
|
41
|
+
type: Boolean,
|
|
42
|
+
default: false
|
|
43
|
+
},
|
|
44
|
+
bucketSize: {
|
|
45
|
+
type: Number,
|
|
46
|
+
default: 20
|
|
47
|
+
},
|
|
48
|
+
initialPosition: {
|
|
49
|
+
type: String,
|
|
50
|
+
default: undefined
|
|
51
|
+
},
|
|
52
|
+
softClose: {
|
|
53
|
+
type: Boolean,
|
|
54
|
+
default: false
|
|
55
|
+
},
|
|
56
|
+
canLoadTop: {
|
|
57
|
+
type: Boolean,
|
|
58
|
+
default: true
|
|
59
|
+
},
|
|
60
|
+
canDropTop: {
|
|
61
|
+
type: Boolean,
|
|
62
|
+
default: false
|
|
63
|
+
},
|
|
64
|
+
canLoadBottom: {
|
|
65
|
+
type: Boolean,
|
|
66
|
+
default: true
|
|
67
|
+
},
|
|
68
|
+
canDropBottom: {
|
|
69
|
+
type: Boolean,
|
|
70
|
+
default: false
|
|
71
|
+
},
|
|
72
|
+
loadTopSensorSize: {
|
|
73
|
+
type: String,
|
|
74
|
+
default: '500px'
|
|
75
|
+
},
|
|
76
|
+
loadBottomSensorSize: {
|
|
77
|
+
type: String,
|
|
78
|
+
default: '500px'
|
|
79
|
+
},
|
|
80
|
+
dropTopSensorSize: {
|
|
81
|
+
type: String,
|
|
82
|
+
default: '5000px'
|
|
83
|
+
},
|
|
84
|
+
dropBottomSensorSize: {
|
|
85
|
+
type: String,
|
|
86
|
+
default: '5000px'
|
|
87
|
+
},
|
|
88
|
+
loadTopDelay: {
|
|
89
|
+
type: Number,
|
|
90
|
+
default: 200
|
|
91
|
+
},
|
|
92
|
+
loadBottomDelay: {
|
|
93
|
+
type: Number,
|
|
94
|
+
default: 200
|
|
95
|
+
},
|
|
96
|
+
dropTopDelay: {
|
|
97
|
+
type: Number,
|
|
98
|
+
default: 200
|
|
99
|
+
},
|
|
100
|
+
dropBottomDelay: {
|
|
101
|
+
type: Number,
|
|
102
|
+
default: 200
|
|
103
|
+
},
|
|
104
|
+
frozen: {
|
|
105
|
+
type: Boolean,
|
|
106
|
+
default: false
|
|
107
|
+
},
|
|
108
|
+
tag: {
|
|
109
|
+
type: String,
|
|
110
|
+
default: 'div'
|
|
111
|
+
}
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
const attrs = useAttrs()
|
|
115
|
+
const slots = useSlots()
|
|
116
|
+
|
|
117
|
+
const rootEl = ref(null)
|
|
118
|
+
const { height } = useElementSize(rootEl)
|
|
119
|
+
|
|
120
|
+
const currentBuckets = ref(null)
|
|
121
|
+
const showPlaceholder = ref(false)
|
|
122
|
+
const placeholderHeight = ref(0)
|
|
123
|
+
const viewerKey = ref(0)
|
|
124
|
+
let currentReloadToken = 0
|
|
125
|
+
|
|
126
|
+
const forwardedViewerProps = computed(() => ({
|
|
127
|
+
bucketSize: props.bucketSize,
|
|
128
|
+
initialPosition: props.initialPosition,
|
|
129
|
+
softClose: props.softClose,
|
|
130
|
+
canLoadTop: props.canLoadTop,
|
|
131
|
+
canDropTop: props.canDropTop,
|
|
132
|
+
canLoadBottom: props.canLoadBottom,
|
|
133
|
+
canDropBottom: props.canDropBottom,
|
|
134
|
+
loadTopSensorSize: props.loadTopSensorSize,
|
|
135
|
+
loadBottomSensorSize: props.loadBottomSensorSize,
|
|
136
|
+
dropTopSensorSize: props.dropTopSensorSize,
|
|
137
|
+
dropBottomSensorSize: props.dropBottomSensorSize,
|
|
138
|
+
loadTopDelay: props.loadTopDelay,
|
|
139
|
+
loadBottomDelay: props.loadBottomDelay,
|
|
140
|
+
dropTopDelay: props.dropTopDelay,
|
|
141
|
+
dropBottomDelay: props.dropBottomDelay,
|
|
142
|
+
frozen: props.frozen,
|
|
143
|
+
tag: props.tag
|
|
144
|
+
}))
|
|
145
|
+
|
|
146
|
+
const forwardedListeners = computed(() => {
|
|
147
|
+
const listeners = {}
|
|
148
|
+
for(const [key, value] of Object.entries(attrs)) {
|
|
149
|
+
if(key.startsWith('on')) listeners[key] = value
|
|
150
|
+
}
|
|
151
|
+
return listeners
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
async function buildBuckets() {
|
|
155
|
+
return await rangeBuckets(
|
|
156
|
+
(range, p) => props.pathFunction(range, p),
|
|
157
|
+
{
|
|
158
|
+
bucketSize: props.bucketSize,
|
|
159
|
+
initialPosition: props.initialPosition,
|
|
160
|
+
softClose: props.softClose
|
|
161
|
+
}
|
|
162
|
+
)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async function reloadBuckets() {
|
|
166
|
+
const token = ++currentReloadToken
|
|
167
|
+
if(props.preserveHeightOnReload) {
|
|
168
|
+
placeholderHeight.value = Math.max(height.value || 0, 1)
|
|
169
|
+
showPlaceholder.value = true
|
|
170
|
+
}
|
|
171
|
+
const newBuckets = await buildBuckets()
|
|
172
|
+
if(token !== currentReloadToken) {
|
|
173
|
+
newBuckets.dispose?.()
|
|
174
|
+
return
|
|
175
|
+
}
|
|
176
|
+
currentBuckets.value?.dispose?.()
|
|
177
|
+
currentBuckets.value = newBuckets
|
|
178
|
+
viewerKey.value++
|
|
179
|
+
showPlaceholder.value = false
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
watch(
|
|
183
|
+
() => props.sourceKey,
|
|
184
|
+
async () => {
|
|
185
|
+
await reloadBuckets()
|
|
186
|
+
},
|
|
187
|
+
{ immediate: true }
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
watch(
|
|
191
|
+
() => props.pathFunction,
|
|
192
|
+
async () => {
|
|
193
|
+
if(!props.reloadOnPathFunctionChange) return
|
|
194
|
+
await reloadBuckets()
|
|
195
|
+
}
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
onBeforeUnmount(() => {
|
|
199
|
+
currentBuckets.value?.dispose?.()
|
|
200
|
+
})
|
|
201
|
+
</script>
|
|
202
|
+
|
|
203
|
+
<style scoped>
|
|
204
|
+
.reactive-range-viewer-placeholder {
|
|
205
|
+
width: 100%;
|
|
206
|
+
}
|
|
207
|
+
</style>
|
package/view/index.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import ScrollLoader from "./ScrollLoader.vue"
|
|
2
2
|
import VisibleArea from "./VisibleArea.vue"
|
|
3
3
|
import RangeViewer from "./RangeViewer.vue"
|
|
4
|
+
import ReactiveRangeViewer from "./ReactiveRangeViewer.vue"
|
|
4
5
|
|
|
5
|
-
export { ScrollLoader, VisibleArea, RangeViewer }
|
|
6
|
+
export { ScrollLoader, VisibleArea, RangeViewer, ReactiveRangeViewer }
|
|
6
7
|
|
|
7
8
|
function registerViewComponents(app) {
|
|
8
9
|
app.component("scroll-loader", ScrollLoader)
|
|
9
|
-
app.component("visible-area", VisibleArea)
|
|
10
|
+
app.component("visible-area", VisibleArea)
|
|
11
|
+
app.component("reactive-range-viewer", ReactiveRangeViewer)
|
|
10
12
|
}
|
|
11
13
|
|
|
12
14
|
export { registerViewComponents }
|