oxy-uni-ui 2.0.0 → 2.1.1
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/attributes.json +1 -1
- package/components/common/abstracts/variable.scss +449 -271
- package/components/common/util.ts +25 -0
- package/components/composables/useDynamicVirtualScroll.ts +80 -0
- package/components/composables/useVirtualScroll.ts +40 -14
- package/components/oxy-action-sheet/index.scss +8 -8
- package/components/oxy-backtop/index.scss +3 -3
- package/components/oxy-badge/index.scss +2 -2
- package/components/oxy-button/index.scss +5 -8
- package/components/oxy-calendar/index.scss +10 -10
- package/components/oxy-calendar/oxy-calendar.vue +3 -3
- package/components/oxy-calendar-view/monthPanel/index.scss +4 -5
- package/components/oxy-calendar-view/monthPanel/month-panel.vue +72 -37
- package/components/oxy-calendar-view/monthPanel/types.ts +43 -1
- package/components/oxy-calendar-view/types.ts +1 -1
- package/components/oxy-calendar-view/utils.ts +12 -1
- package/components/oxy-calendar-view/year/index.scss +1 -1
- package/components/oxy-calendar-view/yearPanel/index.scss +3 -3
- package/components/oxy-calendar-view/yearPanel/types.ts +36 -2
- package/components/oxy-calendar-view/yearPanel/year-panel.vue +64 -45
- package/components/oxy-card/index.scss +4 -4
- package/components/oxy-cell/index.scss +2 -2
- package/components/oxy-cell-group/index.scss +2 -2
- package/components/oxy-checkbox/index.scss +75 -220
- package/components/oxy-checkbox-group/index.scss +2 -2
- package/components/oxy-col-picker/index.scss +3 -3
- package/components/oxy-collapse/index.scss +1 -1
- package/components/oxy-collapse-item/index.scss +2 -2
- package/components/oxy-corner/index.scss +4 -4
- package/components/oxy-count-to/oxy-count-to.vue +3 -3
- package/components/oxy-count-to/types.ts +1 -1
- package/components/oxy-date-strip-item/index.scss +4 -4
- package/components/oxy-datetime-picker/index.scss +5 -5
- package/components/oxy-datetime-picker/types.ts +1 -1
- package/components/oxy-datetime-picker-view/types.ts +2 -2
- package/components/oxy-drop-menu/index.scss +3 -3
- package/components/oxy-drop-menu-item/index.scss +2 -2
- package/components/oxy-fab/index.scss +1 -5
- package/components/oxy-file-list/index.scss +22 -22
- package/components/oxy-footer/index.scss +2 -2
- package/components/oxy-footer/oxy-footer.vue +2 -3
- package/components/oxy-form-item/index.scss +0 -5
- package/components/oxy-grid/oxy-grid.vue +1 -1
- package/components/oxy-grid-item/index.scss +1 -1
- package/components/oxy-guidance/index.scss +15 -15
- package/components/oxy-img/index.scss +2 -2
- package/components/oxy-img-cropper/index.scss +14 -16
- package/components/oxy-img-lazy/index.scss +0 -1
- package/components/oxy-index-anchor/index.scss +5 -5
- package/components/oxy-index-bar/index.scss +3 -3
- package/components/oxy-input/index.scss +2 -2
- package/components/oxy-input-number/index.scss +21 -3
- package/components/oxy-input-number/oxy-input-number.vue +9 -1
- package/components/oxy-keyboard/index.scss +3 -3
- package/components/oxy-link/index.scss +11 -10
- package/components/oxy-loading/index.scss +1 -1
- package/components/oxy-loadmore/index.scss +1 -1
- package/components/oxy-long-press-menu/index.scss +2 -2
- package/components/oxy-message-box/index.scss +10 -10
- package/components/oxy-message-box/oxy-message-box.vue +4 -5
- package/components/oxy-message-box/types.ts +0 -5
- package/components/oxy-navbar/index.scss +1 -1
- package/components/oxy-navbar/oxy-navbar.vue +2 -3
- package/components/oxy-pagination/index.scss +5 -4
- package/components/oxy-password-input/index.scss +4 -4
- package/components/oxy-picker/index.scss +14 -14
- package/components/oxy-picker/types.ts +1 -1
- package/components/oxy-picker-view/index.scss +2 -2
- package/components/oxy-picker-view/oxy-picker-view.vue +8 -5
- package/components/oxy-picker-view/types.ts +2 -2
- package/components/oxy-popover/index.scss +8 -8
- package/components/oxy-popup/index.scss +4 -4
- package/components/oxy-progress/index.scss +3 -3
- package/components/oxy-radio/index.scss +26 -16
- package/components/oxy-radio-group/index.scss +2 -3
- package/components/oxy-rich-text/index.scss +20 -24
- package/components/oxy-rich-text/mp-html/card/card.vue +3 -3
- package/components/oxy-rich-text/mp-html/mp-html.d.ts +2 -0
- package/components/oxy-rich-text/mp-html/mp-html.vue +6 -5
- package/components/oxy-rich-text/mp-html/node/node.vue +25 -2
- package/components/oxy-rich-text/mp-html/parser.js +6 -6
- package/components/oxy-rich-text/oxy-rich-text.vue +31 -8
- package/components/oxy-search/index.scss +6 -6
- package/components/oxy-segmented/index.scss +14 -15
- package/components/oxy-select/index.scss +117 -69
- package/components/oxy-select/oxy-select.vue +24 -11
- package/components/oxy-select-picker/index.scss +2 -2
- package/components/oxy-sidebar-item/index.scss +22 -13
- package/components/oxy-skeleton/index.scss +1 -1
- package/components/oxy-slider/index.scss +8 -9
- package/components/oxy-sort-button/index.scss +3 -5
- package/components/oxy-splitter/index.scss +19 -0
- package/components/oxy-splitter/oxy-splitter.vue +409 -0
- package/components/oxy-splitter/types.ts +75 -0
- package/components/oxy-splitter-panel/index.scss +366 -0
- package/components/oxy-splitter-panel/oxy-splitter-panel.vue +432 -0
- package/components/oxy-splitter-panel/types.ts +63 -0
- package/components/oxy-step/index.scss +13 -13
- package/components/oxy-stream-render/oxy-stream-render.vue +230 -4
- package/components/oxy-swiper-nav/index.scss +5 -5
- package/components/oxy-switch/index.scss +5 -5
- package/components/oxy-tab/index.scss +8 -2
- package/components/oxy-tabbar/index.scss +5 -5
- package/components/oxy-tabbar/oxy-tabbar.vue +3 -3
- package/components/oxy-table/index.scss +5 -6
- package/components/oxy-table-col/index.scss +4 -5
- package/components/oxy-tabs/index.scss +16 -14
- package/components/oxy-tag/index.scss +111 -36
- package/components/oxy-text/index.scss +1 -1
- package/components/oxy-textarea/index.scss +3 -7
- package/components/oxy-toast/index.scss +1 -1
- package/components/oxy-tooltip/index.scss +1 -1
- package/components/oxy-tree/index.scss +35 -15
- package/components/oxy-tree/oxy-tree.vue +113 -2
- package/components/oxy-tree/types.ts +1 -0
- package/components/oxy-upload/index.scss +20 -20
- package/components/oxy-video-preview/index.scss +3 -3
- package/components/oxy-virtual-scroll/index.scss +2 -2
- package/components/oxy-voice-player/index.scss +104 -75
- package/components/oxy-watermark/index.scss +1 -1
- package/global.d.ts +2 -0
- package/package.json +1 -1
- package/tags.json +1 -1
- package/web-types.json +1 -1
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<view :id="splitterId" :class="rootClass" :style="customStyle">
|
|
3
|
+
<slot />
|
|
4
|
+
</view>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script lang="ts">
|
|
8
|
+
export default {
|
|
9
|
+
name: 'oxy-splitter',
|
|
10
|
+
options: {
|
|
11
|
+
addGlobalClass: true,
|
|
12
|
+
virtualHost: true,
|
|
13
|
+
styleIsolation: 'shared'
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
</script>
|
|
17
|
+
|
|
18
|
+
<script lang="ts" setup>
|
|
19
|
+
import { computed, getCurrentInstance, nextTick, onMounted, onUnmounted, provide, ref, watch } from 'vue'
|
|
20
|
+
import { getRect, isDef, isNumber, isString, unitConvert, uuid } from '../common/util'
|
|
21
|
+
import { SPLITTER_KEY, splitterProps, type SplitterCollapseType, type SplitterPanelSize, type SplitterPanelState, type SplitterExpose } from './types'
|
|
22
|
+
|
|
23
|
+
const props = defineProps(splitterProps)
|
|
24
|
+
const emit = defineEmits(['resize-start', 'resize', 'resize-end', 'collapse'])
|
|
25
|
+
|
|
26
|
+
const { proxy } = getCurrentInstance() as any
|
|
27
|
+
|
|
28
|
+
const splitterId = ref<string>(`splitter-${uuid()}`)
|
|
29
|
+
const containerSize = ref<number>(0)
|
|
30
|
+
const panels = ref<SplitterPanelState[]>([])
|
|
31
|
+
const percentSizes = ref<number[]>([])
|
|
32
|
+
const movingIndex = ref<number>(-1)
|
|
33
|
+
const lazyOffset = ref<number>(0)
|
|
34
|
+
|
|
35
|
+
const cacheSizes = ref<number[]>([])
|
|
36
|
+
const cacheCollapsedSize = ref<number[]>([])
|
|
37
|
+
const confirmedMovingIndex = ref<number>(-1)
|
|
38
|
+
|
|
39
|
+
const rootClass = computed(() => `oxy-splitter oxy-splitter--${props.layout} ${props.customClass}`)
|
|
40
|
+
|
|
41
|
+
const pxSizes = computed(() => {
|
|
42
|
+
return percentSizes.value.map((item) => item * containerSize.value)
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
const getContainerSize = () => containerSize.value
|
|
46
|
+
|
|
47
|
+
const isPercentSize = (size: SplitterPanelSize | undefined): size is string => isString(size) && size.trim().endsWith('%')
|
|
48
|
+
|
|
49
|
+
const getPercentValue = (size: string) => {
|
|
50
|
+
const value = Number.parseFloat(size)
|
|
51
|
+
return Number.isFinite(value) ? value / 100 : 0
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const getPixelValue = (size: SplitterPanelSize) => {
|
|
55
|
+
return unitConvert(size, containerSize.value)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const normalizePanelPercent = (size?: SplitterPanelSize) => {
|
|
59
|
+
if (!isDef(size)) {
|
|
60
|
+
return undefined
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (!containerSize.value) {
|
|
64
|
+
return undefined
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (isPercentSize(size)) {
|
|
68
|
+
return getPercentValue(size)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return getPixelValue(size) / containerSize.value
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const normalizePercentList = (percentList: Array<number | undefined>) => {
|
|
75
|
+
const list = [...percentList]
|
|
76
|
+
const undefinedCount = list.filter((item) => !isDef(item)).length
|
|
77
|
+
const definedTotal = list.reduce<number>((total, item) => total + (item ?? 0), 0)
|
|
78
|
+
|
|
79
|
+
if (definedTotal > 1 || undefinedCount === 0) {
|
|
80
|
+
const total = list.reduce<number>((sum, item) => sum + (item ?? 0), 0)
|
|
81
|
+
if (total > 0) {
|
|
82
|
+
return list.map((item) => (item ?? 0) / total)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const equal = list.length ? 1 / list.length : 0
|
|
86
|
+
return list.map(() => equal)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const fill = undefinedCount ? (1 - definedTotal) / undefinedCount : 0
|
|
90
|
+
return list.map((item) => (isDef(item) ? item : fill))
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const syncPercentSizes = () => {
|
|
94
|
+
if (!panels.value.length) {
|
|
95
|
+
percentSizes.value = []
|
|
96
|
+
return
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (!containerSize.value) {
|
|
100
|
+
const equal = 1 / panels.value.length
|
|
101
|
+
percentSizes.value = panels.value.map(() => equal)
|
|
102
|
+
return
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const rawPercent = panels.value.map((panel) => normalizePanelPercent(panel.size))
|
|
106
|
+
percentSizes.value = normalizePercentList(rawPercent)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const getPanelSize = (index: number) => {
|
|
110
|
+
return pxSizes.value[index] || 0
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const toPx = (value: SplitterPanelSize | undefined, defaultValue: number) => {
|
|
114
|
+
if (!isDef(value)) {
|
|
115
|
+
return defaultValue
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (isPercentSize(value)) {
|
|
119
|
+
return getPercentValue(value) * containerSize.value
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return getPixelValue(value)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const getLimitSize = (size: SplitterPanelSize | undefined, defaultValue: number) => {
|
|
126
|
+
if (!containerSize.value) {
|
|
127
|
+
return defaultValue
|
|
128
|
+
}
|
|
129
|
+
return toPx(size, defaultValue)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const clamp = (value: number, min: number, max: number) => {
|
|
133
|
+
return Math.min(Math.max(value, min), max)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const isDraggable = (index: number) => {
|
|
137
|
+
const panel = panels.value[index]
|
|
138
|
+
const nextPanel = panels.value[index + 1]
|
|
139
|
+
return Boolean(panel?.resizable && nextPanel?.resizable)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const registerPanel = (panel: SplitterPanelState) => {
|
|
143
|
+
panels.value.push(panel)
|
|
144
|
+
nextTick(syncPercentSizes)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const unregisterPanel = (uid: number) => {
|
|
148
|
+
const index = panels.value.findIndex((item) => item.uid === uid)
|
|
149
|
+
if (index < 0) {
|
|
150
|
+
return
|
|
151
|
+
}
|
|
152
|
+
panels.value.splice(index, 1)
|
|
153
|
+
nextTick(syncPercentSizes)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const updatePanel = (uid: number, patch: Partial<SplitterPanelState>) => {
|
|
157
|
+
const panel = panels.value.find((item) => item.uid === uid)
|
|
158
|
+
if (!panel) {
|
|
159
|
+
return
|
|
160
|
+
}
|
|
161
|
+
Object.assign(panel, patch)
|
|
162
|
+
nextTick(syncPercentSizes)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const getPanelIndex = (uid: number) => {
|
|
166
|
+
return panels.value.findIndex((item) => item.uid === uid)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const movePanelSize = (index: number, offset: number) => {
|
|
170
|
+
const panel = panels.value[index]
|
|
171
|
+
const nextPanel = panels.value[index + 1]
|
|
172
|
+
if (!panel || !nextPanel) {
|
|
173
|
+
return
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const panelSize = cacheSizes.value[index] || 0
|
|
177
|
+
const nextSize = cacheSizes.value[index + 1] || 0
|
|
178
|
+
const panelMin = getLimitSize(panel.min, 0)
|
|
179
|
+
const panelMax = getLimitSize(panel.max, Number.MAX_SAFE_INTEGER)
|
|
180
|
+
const nextMin = getLimitSize(nextPanel.min, 0)
|
|
181
|
+
const nextMax = getLimitSize(nextPanel.max, Number.MAX_SAFE_INTEGER)
|
|
182
|
+
|
|
183
|
+
const minOffset = -Math.min(panelSize - panelMin, nextMax - nextSize)
|
|
184
|
+
const maxOffset = Math.min(nextSize - nextMin, panelMax - panelSize)
|
|
185
|
+
const mergedOffset = clamp(offset, minOffset, maxOffset)
|
|
186
|
+
|
|
187
|
+
const nextPanelSize = nextSize - mergedOffset
|
|
188
|
+
panel.size = panelSize + mergedOffset
|
|
189
|
+
nextPanel.size = nextPanelSize
|
|
190
|
+
|
|
191
|
+
syncPercentSizes()
|
|
192
|
+
emit('resize', index, [panel.size, nextPanel.size])
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const onMoveStart = (index: number) => {
|
|
196
|
+
cacheSizes.value = [...pxSizes.value]
|
|
197
|
+
confirmedMovingIndex.value = -1
|
|
198
|
+
movingIndex.value = index
|
|
199
|
+
lazyOffset.value = 0
|
|
200
|
+
emit('resize-start', index)
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const onMoving = (index: number, offset: number) => {
|
|
204
|
+
if (movingIndex.value < 0) {
|
|
205
|
+
return
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const currentPanelSize = getPanelSize(index)
|
|
209
|
+
const nextPanelSize = getPanelSize(index + 1)
|
|
210
|
+
|
|
211
|
+
if (confirmedMovingIndex.value === -1) {
|
|
212
|
+
const isCurrentZero = currentPanelSize === 0
|
|
213
|
+
const isNextZero = nextPanelSize === 0
|
|
214
|
+
if (isCurrentZero || isNextZero) {
|
|
215
|
+
confirmedMovingIndex.value = isCurrentZero ? index - 1 : index + 1
|
|
216
|
+
cacheSizes.value = [...pxSizes.value]
|
|
217
|
+
} else {
|
|
218
|
+
confirmedMovingIndex.value = index
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const mergedIndex = confirmedMovingIndex.value
|
|
223
|
+
if (mergedIndex < 0) {
|
|
224
|
+
return
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (props.lazy) {
|
|
228
|
+
lazyOffset.value = offset
|
|
229
|
+
return
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
movePanelSize(mergedIndex, offset)
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const onMoveEnd = (index: number) => {
|
|
236
|
+
if (movingIndex.value < 0) {
|
|
237
|
+
return
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (props.lazy) {
|
|
241
|
+
movePanelSize(confirmedMovingIndex.value > -1 ? confirmedMovingIndex.value : index, lazyOffset.value)
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
lazyOffset.value = 0
|
|
245
|
+
movingIndex.value = -1
|
|
246
|
+
confirmedMovingIndex.value = -1
|
|
247
|
+
cacheSizes.value = []
|
|
248
|
+
emit('resize-end', index)
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const onCollapse = (index: number, type: SplitterCollapseType) => {
|
|
252
|
+
const panel = panels.value[index]
|
|
253
|
+
const nextPanel = panels.value[index + 1]
|
|
254
|
+
if (!panel || !nextPanel) {
|
|
255
|
+
return
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const panelSize = getPanelSize(index)
|
|
259
|
+
const nextPanelSize = getPanelSize(index + 1)
|
|
260
|
+
const panelCollapsed = panelSize === 0
|
|
261
|
+
const nextPanelCollapsed = nextPanelSize === 0
|
|
262
|
+
|
|
263
|
+
// 交互约定:
|
|
264
|
+
// 只要任一侧已折叠,点击任意折叠按钮都优先执行“还原已折叠侧”
|
|
265
|
+
if (panelCollapsed || nextPanelCollapsed) {
|
|
266
|
+
if (panelCollapsed) {
|
|
267
|
+
const panelMin = getLimitSize(panel.min, 0)
|
|
268
|
+
const panelMax = getLimitSize(panel.max, Number.MAX_SAFE_INTEGER)
|
|
269
|
+
const cached = cacheCollapsedSize.value[index]
|
|
270
|
+
const fallback = panelMin > 0 ? panelMin : nextPanelSize / 2
|
|
271
|
+
const restore = clamp(cached || fallback, panelMin, Math.min(panelMax, nextPanelSize))
|
|
272
|
+
|
|
273
|
+
if (restore > 0) {
|
|
274
|
+
panel.size = restore
|
|
275
|
+
nextPanel.size = nextPanelSize - restore
|
|
276
|
+
}
|
|
277
|
+
} else {
|
|
278
|
+
const nextMin = getLimitSize(nextPanel.min, 0)
|
|
279
|
+
const nextMax = getLimitSize(nextPanel.max, Number.MAX_SAFE_INTEGER)
|
|
280
|
+
const cached = cacheCollapsedSize.value[index]
|
|
281
|
+
const fallback = nextMin > 0 ? nextMin : panelSize / 2
|
|
282
|
+
const restore = clamp(cached || fallback, nextMin, Math.min(nextMax, panelSize))
|
|
283
|
+
|
|
284
|
+
if (restore > 0) {
|
|
285
|
+
panel.size = panelSize - restore
|
|
286
|
+
nextPanel.size = restore
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
syncPercentSizes()
|
|
291
|
+
emit('collapse', index, type)
|
|
292
|
+
return
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const isStart = type === 'start'
|
|
296
|
+
const collapseSize = isStart ? panelSize : nextPanelSize
|
|
297
|
+
const expandSize = isStart ? nextPanelSize : panelSize
|
|
298
|
+
const collapsed = collapseSize === 0
|
|
299
|
+
|
|
300
|
+
if (!collapsed) {
|
|
301
|
+
cacheCollapsedSize.value[index] = collapseSize
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if (collapsed) {
|
|
305
|
+
const mergedSize = cacheCollapsedSize.value[index] || 0
|
|
306
|
+
const targetSize = getLimitSize(isStart ? panel.max : nextPanel.max, Number.MAX_SAFE_INTEGER)
|
|
307
|
+
|
|
308
|
+
if (targetSize <= mergedSize) {
|
|
309
|
+
return
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
if (expandSize < mergedSize) {
|
|
313
|
+
return
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
if (isStart) {
|
|
317
|
+
panel.size = mergedSize
|
|
318
|
+
nextPanel.size = expandSize - mergedSize
|
|
319
|
+
} else {
|
|
320
|
+
panel.size = expandSize - mergedSize
|
|
321
|
+
nextPanel.size = mergedSize
|
|
322
|
+
}
|
|
323
|
+
} else {
|
|
324
|
+
if (isStart) {
|
|
325
|
+
panel.size = 0
|
|
326
|
+
nextPanel.size = expandSize + collapseSize
|
|
327
|
+
} else {
|
|
328
|
+
panel.size = expandSize + collapseSize
|
|
329
|
+
nextPanel.size = 0
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
syncPercentSizes()
|
|
334
|
+
emit('collapse', index, type)
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const refresh = async () => {
|
|
338
|
+
try {
|
|
339
|
+
const rect = await getRect(`#${splitterId.value}`, false, proxy)
|
|
340
|
+
const nextSize = props.layout === 'horizontal' ? Number(rect.width) : Number(rect.height)
|
|
341
|
+
if (isNumber(nextSize) && Number.isFinite(nextSize) && nextSize > 0) {
|
|
342
|
+
containerSize.value = nextSize
|
|
343
|
+
syncPercentSizes()
|
|
344
|
+
}
|
|
345
|
+
} catch (error) {
|
|
346
|
+
// empty
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
const onWindowResize = () => {
|
|
351
|
+
refresh()
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
watch(
|
|
355
|
+
() => panels.value.length,
|
|
356
|
+
() => {
|
|
357
|
+
nextTick(refresh)
|
|
358
|
+
}
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
watch(
|
|
362
|
+
() => props.layout,
|
|
363
|
+
() => {
|
|
364
|
+
nextTick(refresh)
|
|
365
|
+
}
|
|
366
|
+
)
|
|
367
|
+
|
|
368
|
+
provide(SPLITTER_KEY, {
|
|
369
|
+
layout: computed(() => props.layout),
|
|
370
|
+
lazy: computed(() => props.lazy),
|
|
371
|
+
panels,
|
|
372
|
+
pxSizes,
|
|
373
|
+
movingIndex,
|
|
374
|
+
lazyOffset,
|
|
375
|
+
registerPanel,
|
|
376
|
+
unregisterPanel,
|
|
377
|
+
updatePanel,
|
|
378
|
+
getPanelIndex,
|
|
379
|
+
getPanelSize,
|
|
380
|
+
getContainerSize,
|
|
381
|
+
isDraggable,
|
|
382
|
+
onMoveStart,
|
|
383
|
+
onMoving,
|
|
384
|
+
onMoveEnd,
|
|
385
|
+
onCollapse,
|
|
386
|
+
refresh
|
|
387
|
+
})
|
|
388
|
+
|
|
389
|
+
onMounted(() => {
|
|
390
|
+
nextTick(refresh)
|
|
391
|
+
// #ifdef H5
|
|
392
|
+
window.addEventListener('resize', onWindowResize)
|
|
393
|
+
// #endif
|
|
394
|
+
})
|
|
395
|
+
|
|
396
|
+
onUnmounted(() => {
|
|
397
|
+
// #ifdef H5
|
|
398
|
+
window.removeEventListener('resize', onWindowResize)
|
|
399
|
+
// #endif
|
|
400
|
+
})
|
|
401
|
+
|
|
402
|
+
defineExpose<SplitterExpose>({
|
|
403
|
+
refresh
|
|
404
|
+
})
|
|
405
|
+
</script>
|
|
406
|
+
|
|
407
|
+
<style lang="scss" scoped>
|
|
408
|
+
@import './index.scss';
|
|
409
|
+
</style>
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { ComputedRef, ComponentPublicInstance, ExtractPropTypes, InjectionKey, Ref } from 'vue'
|
|
2
|
+
import { baseProps, makeBooleanProp, makeStringProp } from '../common/props'
|
|
3
|
+
|
|
4
|
+
export type SplitterLayout = 'horizontal' | 'vertical'
|
|
5
|
+
|
|
6
|
+
export type SplitterPanelSize = number | string
|
|
7
|
+
|
|
8
|
+
export type SplitterCollapsible = boolean
|
|
9
|
+
|
|
10
|
+
export type SplitterCollapseType = 'start' | 'end'
|
|
11
|
+
|
|
12
|
+
export type SplitterPanelState = {
|
|
13
|
+
/** panel 唯一标识 */
|
|
14
|
+
uid: number
|
|
15
|
+
/** 当前 size,支持 number(px) / string(px|rpx|%) */
|
|
16
|
+
size?: SplitterPanelSize
|
|
17
|
+
/** 最小尺寸 */
|
|
18
|
+
min?: SplitterPanelSize
|
|
19
|
+
/** 最大尺寸 */
|
|
20
|
+
max?: SplitterPanelSize
|
|
21
|
+
/** 是否允许拖拽 */
|
|
22
|
+
resizable: boolean
|
|
23
|
+
/** 是否支持折叠 */
|
|
24
|
+
collapsible: SplitterCollapsible
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export type SplitterProvide = {
|
|
28
|
+
layout: ComputedRef<SplitterLayout>
|
|
29
|
+
lazy: ComputedRef<boolean>
|
|
30
|
+
panels: Ref<SplitterPanelState[]>
|
|
31
|
+
pxSizes: ComputedRef<number[]>
|
|
32
|
+
movingIndex: Ref<number>
|
|
33
|
+
lazyOffset: Ref<number>
|
|
34
|
+
registerPanel: (panel: SplitterPanelState) => void
|
|
35
|
+
unregisterPanel: (uid: number) => void
|
|
36
|
+
updatePanel: (uid: number, patch: Partial<SplitterPanelState>) => void
|
|
37
|
+
getPanelIndex: (uid: number) => number
|
|
38
|
+
getPanelSize: (index: number) => number
|
|
39
|
+
getContainerSize: () => number
|
|
40
|
+
isDraggable: (index: number) => boolean
|
|
41
|
+
onMoveStart: (index: number) => void
|
|
42
|
+
onMoving: (index: number, offset: number) => void
|
|
43
|
+
onMoveEnd: (index: number) => void
|
|
44
|
+
onCollapse: (index: number, type: SplitterCollapseType) => void
|
|
45
|
+
refresh: () => void
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export const SPLITTER_KEY: InjectionKey<SplitterProvide> = Symbol('oxy-splitter')
|
|
49
|
+
|
|
50
|
+
export const splitterProps = {
|
|
51
|
+
...baseProps,
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* 分隔方向
|
|
55
|
+
* `horizontal` 为左右布局,`vertical` 为上下布局
|
|
56
|
+
*/
|
|
57
|
+
layout: makeStringProp<SplitterLayout>('horizontal'),
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* 是否启用懒更新
|
|
61
|
+
* 拖拽中仅更新分割线,松手后再提交尺寸
|
|
62
|
+
*/
|
|
63
|
+
lazy: makeBooleanProp(false)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export type SplitterProps = ExtractPropTypes<typeof splitterProps>
|
|
67
|
+
|
|
68
|
+
export type SplitterExpose = {
|
|
69
|
+
/**
|
|
70
|
+
* 手动刷新尺寸(例如在 popup 打开后调用)
|
|
71
|
+
*/
|
|
72
|
+
refresh: () => void
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export type SplitterInstance = ComponentPublicInstance<SplitterProps, SplitterExpose>
|