Skip to content

Commit e9aa3a3

Browse files
authored
feat application ui (#3250)
1 parent 8def4f3 commit e9aa3a3

File tree

3 files changed

+159
-1
lines changed

3 files changed

+159
-1
lines changed

ui/src/directives/clickoutside.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import type { App } from 'vue'
2+
import { ClickOutside as vClickOutside } from 'element-plus'
3+
export default {
4+
install: (app: App) => {
5+
app.directive('click-outside', vClickOutside)
6+
}
7+
}
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
import { nextTick } from 'vue'
2+
3+
import { throttle } from 'lodash-unified'
4+
import { getScrollContainer } from 'element-plus/es/utils/index'
5+
import type { App } from 'vue'
6+
export const SCOPE = 'InfiniteScrollUP'
7+
export const CHECK_INTERVAL = 50
8+
export const DEFAULT_DELAY = 200
9+
export const DEFAULT_DISTANCE = 0
10+
11+
const attributes = {
12+
delay: {
13+
type: Number,
14+
default: DEFAULT_DELAY
15+
},
16+
distance: {
17+
type: Number,
18+
default: DEFAULT_DISTANCE
19+
},
20+
disabled: {
21+
type: Boolean,
22+
default: false
23+
},
24+
immediate: {
25+
type: Boolean,
26+
default: true
27+
}
28+
}
29+
30+
type Attrs = typeof attributes
31+
type ScrollOptions = { [K in keyof Attrs]: Attrs[K]['default'] }
32+
type InfiniteScrollCallback = () => void
33+
type InfiniteScrollEl = HTMLElement & {
34+
[SCOPE]: {
35+
container: HTMLElement | Window
36+
containerEl: HTMLElement
37+
instance: any
38+
delay: number // export for test
39+
lastScrollTop: number
40+
cb: InfiniteScrollCallback
41+
onScroll: () => void
42+
observer?: MutationObserver
43+
}
44+
}
45+
46+
const getScrollOptions = (el: HTMLElement, instance: any): ScrollOptions => {
47+
return Object.entries(attributes).reduce((acm: any, [name, option]) => {
48+
const { type, default: defaultValue } = option
49+
const attrVal: any = el.getAttribute(`infinite-scroll-up-${name}`)
50+
let value = instance[attrVal] ?? attrVal ?? defaultValue
51+
value = value === 'false' ? false : value
52+
value = type(value)
53+
acm[name] = Number.isNaN(value) ? defaultValue : value
54+
return acm
55+
}, {} as ScrollOptions)
56+
}
57+
58+
const destroyObserver = (el: InfiniteScrollEl) => {
59+
const { observer } = el[SCOPE]
60+
61+
if (observer) {
62+
observer.disconnect()
63+
delete el[SCOPE].observer
64+
}
65+
}
66+
67+
const handleScroll = (el: InfiniteScrollEl, cb: InfiniteScrollCallback) => {
68+
const { container, containerEl, instance, observer, lastScrollTop } = el[SCOPE]
69+
const { disabled } = getScrollOptions(el, instance)
70+
const { scrollTop } = containerEl
71+
72+
el[SCOPE].lastScrollTop = scrollTop
73+
74+
// trigger only if full check has done and not disabled and scroll down
75+
76+
if (observer || disabled || scrollTop > 0) return
77+
78+
if (scrollTop == 0) {
79+
cb.call(instance)
80+
}
81+
}
82+
83+
function checkFull(el: InfiniteScrollEl, cb: InfiniteScrollCallback) {
84+
const { containerEl, instance } = el[SCOPE]
85+
const { disabled } = getScrollOptions(el, instance)
86+
87+
if (disabled || containerEl.clientHeight == 0) return
88+
89+
if (containerEl.scrollTop <= 0) {
90+
cb.call(instance)
91+
} else {
92+
destroyObserver(el)
93+
}
94+
}
95+
96+
const InfiniteScroll = {
97+
async mounted(el: any, binding: any) {
98+
const { instance, value: cb } = binding
99+
100+
// ensure parentNode mounted
101+
await nextTick()
102+
103+
const { delay, immediate } = getScrollOptions(el, instance)
104+
const container = getScrollContainer(el, true)
105+
const containerEl = container === window ? document.documentElement : (container as HTMLElement)
106+
const onScroll = throttle(handleScroll.bind(null, el, cb), delay)
107+
108+
if (!container) return
109+
110+
el[SCOPE] = {
111+
instance,
112+
container,
113+
containerEl,
114+
delay,
115+
cb,
116+
onScroll,
117+
lastScrollTop: containerEl.scrollTop
118+
}
119+
120+
if (immediate) {
121+
const observer = new MutationObserver(throttle(checkFull.bind(null, el, cb), CHECK_INTERVAL))
122+
el[SCOPE].observer = observer
123+
observer.observe(el, { childList: true, subtree: true })
124+
checkFull(el, cb)
125+
}
126+
127+
container.addEventListener('scroll', onScroll)
128+
},
129+
unmounted(el: any) {
130+
if (!el[SCOPE]) return
131+
const { container, onScroll } = el[SCOPE]
132+
133+
container?.removeEventListener('scroll', onScroll)
134+
destroyObserver(el)
135+
},
136+
async updated(el: any) {
137+
if (!el[SCOPE]) {
138+
await nextTick()
139+
} else {
140+
const { containerEl, cb, observer } = el[SCOPE]
141+
if (containerEl.clientHeight && observer) {
142+
checkFull(el, cb)
143+
}
144+
}
145+
}
146+
}
147+
export default {
148+
install: (app: App) => {
149+
app.directive('infinite-scroll-up', InfiniteScroll)
150+
}
151+
}

ui/src/workflow/common/NodeCascader.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ const data = computed({
3434
},
3535
get: () => {
3636
return props.modelValue
37-
}
37+
},
3838
})
3939
const options = ref<Array<any>>([])
4040

0 commit comments

Comments
 (0)