CN-1664 fix: 搜索组件添加操作符的选择

This commit is contained in:
刘洪洪
2024-06-04 18:06:00 +08:00
parent da4d3ca798
commit 521dcecfd7
5 changed files with 417 additions and 314 deletions

View File

@@ -45,6 +45,7 @@
@load="handleHintLoad" @load="handleHintLoad"
@select="handleSelect" @select="handleSelect"
@keydownEnter="handleEnter" @keydownEnter="handleEnter"
:columnList="columnList"
:hintParams="hintParams" :hintParams="hintParams"
:hintSearch="searchStr"></hint> :hintSearch="searchStr"></hint>
</div> </div>

View File

@@ -3,7 +3,7 @@
<div class="Hint" @click.stop> <div class="Hint" @click.stop>
<div class="hint__block"> <div class="hint__block">
<div class="hint__block-filter"> <div class="hint__block-filter">
<hint-info v-on="$listeners" :hintList="hintList" @select="onSelect" @keydownEnter="onKeydownEnter"></hint-info> <hint-info v-on="$listeners" :hintList="hintList" :columnList="columnList" @select="onSelect" @keydownEnter="onKeydownEnter"></hint-info>
</div> </div>
<div class="hint__block-helper"> <div class="hint__block-helper">
<helper-info :hintSearch="hintSearch"></helper-info> <helper-info :hintSearch="hintSearch"></helper-info>
@@ -20,7 +20,8 @@ export default {
name: 'Hint', name: 'Hint',
props: { props: {
hintList: [], hintList: [],
hintSearch: {} hintSearch: {},
columnList: []
}, },
components: { components: {
HintInfo, HintInfo,

View File

@@ -22,6 +22,9 @@ export default {
default () { default () {
return [] return []
} }
},
columnList: {
type: Array
} }
}, },
data () { data () {
@@ -52,8 +55,24 @@ export default {
const item = vm.hintList[vm.currentIndex] const item = vm.hintList[vm.currentIndex]
if (item) { if (item) {
this.$emit('keydownEnter', 'Enter') this.$emit('keydownEnter', 'Enter')
vm.handleSelect(item, vm.currentIndex, vm.hintList) const obj = vm.columnList.find(d => d.label === item.displayText)
vm.currentIndex = -1 // 如果选中label数组更新光标处在第一行。如果选择操作符光标位置不变
if (obj) {
vm.handleSelect(item, vm.currentIndex, vm.hintList)
const itemDom = document.getElementById('filterItem' + vm.currentIndex)
if (itemDom) {
itemDom.style.backgroundColor = ''
}
vm.$nextTick(() => {
vm.currentIndex = 1
const itemDom1 = document.getElementById('filterItem1')
if (itemDom1) {
itemDom1.style.backgroundColor = 'var(--el-fill-color-dark)'
}
})
} else {
vm.handleSelect(item, vm.currentIndex, vm.hintList)
}
} else { } else {
this.$emit('keydownEnter', null) this.$emit('keydownEnter', null)
} }
@@ -75,11 +94,7 @@ export default {
const itemDom = document.getElementById('filterItem' + realIndex) const itemDom = document.getElementById('filterItem' + realIndex)
if (itemDom) { if (itemDom) {
itemDom.style.backgroundColor = 'var(--el-fill-color-dark)' itemDom.style.backgroundColor = 'var(--el-fill-color-dark)'
if (realIndex >= 10) { itemDom.scrollIntoView(false)
dom.scrollTop = (realIndex - 10) * 26 + 16
} else {
dom.scrollTop = 0
}
} }
} else { } else {
const itemDom = document.getElementById('filterItem' + index) const itemDom = document.getElementById('filterItem' + index)
@@ -105,12 +120,7 @@ export default {
const itemDom = document.getElementById('filterItem' + realIndex) const itemDom = document.getElementById('filterItem' + realIndex)
if (itemDom) { if (itemDom) {
itemDom.style.backgroundColor = 'var(--el-fill-color-dark)' itemDom.style.backgroundColor = 'var(--el-fill-color-dark)'
if (realIndex >= 10) { itemDom.scrollIntoView(false)
// 26是24的行高+2px的margin16是两个标题总的margin
dom.scrollTop = (realIndex - 10) * 26 + 16
} else {
dom.scrollTop = 0
}
} }
} else { } else {
const itemDom = document.getElementById('filterItem' + index) const itemDom = document.getElementById('filterItem' + index)

View File

@@ -4,125 +4,134 @@ export default function (CodeMirror,{
keyboardDown, keyboardDown,
keyboardEnter keyboardEnter
}) { }) {
var HINT_ELEMENT_CLASS = 'CodeMirror-hint'
var HINT_ELEMENT_CLASS = "CodeMirror-hint"; var ACTIVE_HINT_ELEMENT_CLASS = 'CodeMirror-hint-active'
var ACTIVE_HINT_ELEMENT_CLASS = "CodeMirror-hint-active";
// This is the old interface, kept around for now to stay // This is the old interface, kept around for now to stay
// backwards-compatible. // backwards-compatible.
CodeMirror.showHint = function (cm, getHints, options) { CodeMirror.showHint = function (cm, getHints, options) {
if (!getHints) return cm.showHint(options); if (!getHints) return cm.showHint(options)
if (options && options.async) getHints.async = true; if (options && options.async) getHints.async = true
var newOpts = {hint: getHints}; var newOpts = { hint: getHints }
if (options) for (var prop in options) newOpts[prop] = options[prop]; if (options) for (var prop in options) newOpts[prop] = options[prop]
return cm.showHint(newOpts); return cm.showHint(newOpts)
}; }
CodeMirror.defineExtension("showHint", function (options) { CodeMirror.defineExtension('showHint', function (options) {
options = parseOptions(this, this.getCursor("start"), options); options = parseOptions(this, this.getCursor('start'), options)
var selections = this.listSelections() var selections = this.listSelections()
if (selections.length > 1) return; if (selections.length > 1) return
// By default, don't allow completion when something is selected. // By default, don't allow completion when something is selected.
// A hint function can have a `supportsSelection` property to // A hint function can have a `supportsSelection` property to
// indicate that it can handle selections. // indicate that it can handle selections.
if (this.somethingSelected()) { if (this.somethingSelected()) {
if (!options.hint.supportsSelection) return; if (!options.hint.supportsSelection) return
// Don't try with cross-line selections // Don't try with cross-line selections
for (var i = 0; i < selections.length; i++) for (var i = 0; i < selections.length; i++) {
if (selections[i].head.line != selections[i].anchor.line) return; if (selections[i].head.line != selections[i].anchor.line) return
}
} }
if (this.state.completionActive) this.state.completionActive.close(); if (this.state.completionActive) this.state.completionActive.close()
var completion = this.state.completionActive = new Completion(this, options); const completion = this.state.completionActive = new Completion(this, options)
if (!completion.options.hint) return; if (!completion.options.hint) return
CodeMirror.signal(this, "startCompletion", this); CodeMirror.signal(this, 'startCompletion', this)
completion.update(true); completion.update(true)
cb && cb({completion}) cb && cb({ completion })
}); })
CodeMirror.defineExtension("closeHint", function () { CodeMirror.defineExtension('closeHint', function () {
if (this.state.completionActive) this.state.completionActive.close() if (this.state.completionActive) this.state.completionActive.close()
}) })
function Completion(cm, options) { function Completion (cm, options) {
this.cm = cm; this.cm = cm
this.options = options; this.options = options
this.widget = null; this.widget = null
this.debounce = 0; this.debounce = 0
this.tick = 0; this.tick = 0
this.startPos = this.cm.getCursor("start"); this.startPos = this.cm.getCursor('start')
this.startLen = this.cm.getLine(this.startPos.line).length - this.cm.getSelection().length; this.startLen = this.cm.getLine(this.startPos.line).length - this.cm.getSelection().length
if (this.options.updateOnCursorActivity) { if (this.options.updateOnCursorActivity) {
var self = this; var self = this
cm.on("cursorActivity", this.activityFunc = function () { cm.on('cursorActivity', this.activityFunc = function () {
self.cursorActivity(); self.cursorActivity()
}); })
} }
} }
var requestAnimationFrame = window.requestAnimationFrame || function (fn) { var requestAnimationFrame = window.requestAnimationFrame || function (fn) {
return setTimeout(fn, 1000 / 60); return setTimeout(fn, 1000 / 60)
}; }
var cancelAnimationFrame = window.cancelAnimationFrame || clearTimeout; var cancelAnimationFrame = window.cancelAnimationFrame || clearTimeout
Completion.prototype = { Completion.prototype = {
close: function () { close: function () {
if (!this.active()) return; if (!this.active()) return
this.cm.state.completionActive = null; this.cm.state.completionActive = null
this.tick = null; this.tick = null
if (this.options.updateOnCursorActivity) { if (this.options.updateOnCursorActivity) {
this.cm.off("cursorActivity", this.activityFunc); this.cm.off('cursorActivity', this.activityFunc)
} }
if (this.widget && this.data) CodeMirror.signal(this.data, "close"); if (this.widget && this.data) CodeMirror.signal(this.data, 'close')
if (this.widget) this.widget.close(); if (this.widget) this.widget.close()
CodeMirror.signal(this.cm, "endCompletion", this.cm); CodeMirror.signal(this.cm, 'endCompletion', this.cm)
}, },
active: function () { active: function () {
return this.cm.state.completionActive == this; return this.cm.state.completionActive == this
}, },
pick: function (data, i) { pick: function (data, i) {
var completion = data.list[i], self = this; const completion = data.list[i]; const self = this
this.cm.operation(function () { this.cm.operation(function () {
if (completion.hint) if (completion.hint) {
completion.hint(self.cm, data, completion); completion.hint(self.cm, data, completion)
else } else if (completion.field) {
self.cm.replaceRange(getText(completion), completion.from || data.from, if (completion.text === 'in' || completion.text === 'like') {
completion.to || data.to, "complete"); completion.text = ' ' + completion.text
CodeMirror.signal(data, "pick", completion); }
self.cm.scrollIntoView(); let myString = self.cm.getValue().substring(data.from.ch, data.to.ch) + getText(completion)
}); if (completion.text === 'has') {
myString = getText(completion) + '(' + self.cm.getValue().substring(data.from.ch, data.to.ch) + ', ) '
}
self.cm.replaceRange(myString, data.from, data.to, 'complete')
} else {
self.cm.replaceRange(getText(completion), completion.from || data.from, completion.to || data.to, 'complete')
}
CodeMirror.signal(data, 'pick', completion)
self.cm.scrollIntoView()
})
if (this.options.closeOnPick) { if (this.options.closeOnPick) {
this.close(); this.close()
} }
}, },
cursorActivity: function () { cursorActivity: function () {
if (this.debounce) { if (this.debounce) {
cancelAnimationFrame(this.debounce); cancelAnimationFrame(this.debounce)
this.debounce = 0; this.debounce = 0
} }
var identStart = this.startPos; var identStart = this.startPos
if (this.data) { if (this.data) {
identStart = this.data.from; identStart = this.data.from
} }
var pos = this.cm.getCursor(), line = this.cm.getLine(pos.line); const pos = this.cm.getCursor(); const line = this.cm.getLine(pos.line)
if (pos.line != this.startPos.line || line.length - pos.ch != this.startLen - this.startPos.ch || if (pos.line != this.startPos.line || line.length - pos.ch != this.startLen - this.startPos.ch ||
pos.ch < identStart.ch || this.cm.somethingSelected() || pos.ch < identStart.ch || this.cm.somethingSelected() ||
(!pos.ch || this.options.closeCharacters.test(line.charAt(pos.ch - 1)))) { (!pos.ch || this.options.closeCharacters.test(line.charAt(pos.ch - 1)))) {
this.close(); this.close()
} else { } else {
var self = this; var self = this
this.debounce = requestAnimationFrame(function () { this.debounce = requestAnimationFrame(function () {
self.update(); self.update()
}); })
if (this.widget) this.widget.disable(); if (this.widget) this.widget.disable()
} }
}, },
@@ -135,397 +144,430 @@ export default function (CodeMirror,{
}, },
finishUpdate: function (data, first) { finishUpdate: function (data, first) {
if (this.data) CodeMirror.signal(this.data, "update"); if (this.data) CodeMirror.signal(this.data, 'update')
var picked = (this.widget && this.widget.picked) || (first && this.options.completeSingle); const picked = (this.widget && this.widget.picked) || (first && this.options.completeSingle)
if (this.widget) this.widget.close(); if (this.widget) this.widget.close()
this.data = data; this.data = data
if (data && data.list.length) { if (data && data.list.length) {
if (picked && data.list.length == 1) { if (picked && data.list.length == 1) {
this.pick(data, 0); this.pick(data, 0)
} else { } else {
this.widget = new Widget(this, data); this.widget = new Widget(this, data)
CodeMirror.signal(data, "shown"); CodeMirror.signal(data, 'shown')
} }
} }
} }
}; }
function parseOptions(cm, pos, options) { function parseOptions (cm, pos, options) {
var editor = cm.options.hintOptions; const editor = cm.options.hintOptions
var out = {}; const out = {}
for (var prop in defaultOptions) out[prop] = defaultOptions[prop]; for (var prop in defaultOptions) out[prop] = defaultOptions[prop]
if (editor) for (var prop in editor) if (editor) {
if (editor[prop] !== undefined) out[prop] = editor[prop]; for (var prop in editor) {
if (options) for (var prop in options) if (editor[prop] !== undefined) out[prop] = editor[prop]
if (options[prop] !== undefined) out[prop] = options[prop]; }
}
if (options) {
for (var prop in options) {
if (options[prop] !== undefined) out[prop] = options[prop]
}
}
if (out.hint.resolve) out.hint = out.hint.resolve(cm, pos) if (out.hint.resolve) out.hint = out.hint.resolve(cm, pos)
return out; return out
} }
function getText(completion) { function getText (completion) {
if (typeof completion == "string") return completion; if (typeof completion == 'string') {
else return completion.text; return completion
} else {
return completion.text
}
} }
function buildKeyMap(completion, handle) { function buildKeyMap (completion, handle) {
var baseMap = { var baseMap = {
Up: function () { Up: function () {
handle.moveFocus(-1); handle.moveFocus(-1)
}, },
Down: function () { Down: function () {
handle.moveFocus(1); handle.moveFocus(1)
}, },
PageUp: function () { PageUp: function () {
handle.moveFocus(-handle.menuSize() + 1, true); handle.moveFocus(-handle.menuSize() + 1, true)
}, },
PageDown: function () { PageDown: function () {
handle.moveFocus(handle.menuSize() - 1, true); handle.moveFocus(handle.menuSize() - 1, true)
}, },
Home: function () { Home: function () {
handle.setFocus(0); handle.setFocus(0)
}, },
End: function () { End: function () {
handle.setFocus(handle.length - 1); handle.setFocus(handle.length - 1)
}, },
// Enter: handle.pick, // Enter: handle.pick,
// Tab: handle.pick, // Tab: handle.pick,
Tab: keyboardEnter, Tab: keyboardEnter,
Enter: keyboardEnter, Enter: keyboardEnter,
Esc: handle.close Esc: handle.close
}; }
var mac = /Mac/.test(navigator.platform); var mac = /Mac/.test(navigator.platform)
if (mac) { if (mac) {
baseMap["Ctrl-P"] = function () { baseMap['Ctrl-P'] = function () {
handle.moveFocus(-1); handle.moveFocus(-1)
}; }
baseMap["Ctrl-N"] = function () { baseMap['Ctrl-N'] = function () {
handle.moveFocus(1); handle.moveFocus(1)
}; }
} }
var custom = completion.options.customKeys; var custom = completion.options.customKeys
var ourMap = custom ? {} : baseMap; var ourMap = custom ? {} : baseMap
function addBinding(key, val) { function addBinding (key, val) {
var bound; var bound
if (typeof val != "string") if (typeof val != 'string') {
bound = function (cm) { bound = function (cm) {
return val(cm, handle); return val(cm, handle)
}; }
// This mechanism is deprecated }// This mechanism is deprecated
else if (baseMap.hasOwnProperty(val)) else if (baseMap.hasOwnProperty(val)) {
bound = baseMap[val]; bound = baseMap[val]
else } else {
bound = val; bound = val
ourMap[key] = bound; }
ourMap[key] = bound
} }
if (custom) if (custom) {
for (var key in custom) if (custom.hasOwnProperty(key)) for (var key in custom) {
addBinding(key, custom[key]); if (custom.hasOwnProperty(key)) {
var extra = completion.options.extraKeys; addBinding(key, custom[key])
if (extra) }
for (var key in extra) if (extra.hasOwnProperty(key)) }
addBinding(key, extra[key]); }
return ourMap; var extra = completion.options.extraKeys
if (extra) {
for (var key in extra) {
if (extra.hasOwnProperty(key)) {
addBinding(key, extra[key])
}
}
}
return ourMap
} }
function getHintElement(hintsElement, el) { function getHintElement (hintsElement, el) {
while (el && el != hintsElement) { while (el && el != hintsElement) {
if (el.nodeName.toUpperCase() === "LI" && el.parentNode == hintsElement) return el; if (el.nodeName.toUpperCase() === 'LI' && el.parentNode == hintsElement) return el
el = el.parentNode; el = el.parentNode
} }
} }
function Widget(completion, data) { function Widget (completion, data) {
this.id = "cm-complete-" + Math.floor(Math.random(1e6)) this.id = 'cm-complete-' + Math.floor(Math.random(1e6))
this.completion = completion; this.completion = completion
this.data = data; this.data = data
this.picked = false; this.picked = false
var widget = this, cm = completion.cm; var widget = this, cm = completion.cm
var ownerDocument = cm.getInputField().ownerDocument; var ownerDocument = cm.getInputField().ownerDocument
var parentWindow = ownerDocument.defaultView || ownerDocument.parentWindow; var parentWindow = ownerDocument.defaultView || ownerDocument.parentWindow
var hints = this.hints = ownerDocument.createElement("ul"); var hints = this.hints = ownerDocument.createElement('ul')
// $(hints).append(` // $(hints).append(`
// <h1>lalallalla</h1> // <h1>lalallalla</h1>
// `) // `)
hints.setAttribute("role", "listbox") hints.setAttribute('role', 'listbox')
hints.setAttribute("aria-expanded", "true") hints.setAttribute('aria-expanded', 'true')
hints.id = this.id hints.id = this.id
var theme = completion.cm.options.theme; var theme = completion.cm.options.theme
hints.className = "CodeMirror-hints " + theme; hints.className = 'CodeMirror-hints ' + theme
this.selectedHint = data.selectedHint || 0; this.selectedHint = data.selectedHint || 0
var completions = data.list; var completions = data.list
for (var i = 0; i < completions.length; ++i) { for (var i = 0; i < completions.length; ++i) {
var elt = hints.appendChild(ownerDocument.createElement("li")), cur = completions[i]; var elt = hints.appendChild(ownerDocument.createElement('li')), cur = completions[i]
var className = HINT_ELEMENT_CLASS + (i != this.selectedHint ? "" : " " + ACTIVE_HINT_ELEMENT_CLASS); var className = HINT_ELEMENT_CLASS + (i != this.selectedHint ? '' : ' ' + ACTIVE_HINT_ELEMENT_CLASS)
if (cur.className != null) className = cur.className + " " + className; if (cur.className != null) className = cur.className + ' ' + className
elt.className = className; elt.className = className
if (i == this.selectedHint) elt.setAttribute("aria-selected", "true") if (i == this.selectedHint) elt.setAttribute('aria-selected', 'true')
elt.id = this.id + "-" + i elt.id = this.id + '-' + i
elt.setAttribute("role", "option") elt.setAttribute('role', 'option')
if (cur.render) cur.render(elt, data, cur); if (cur.render) {
else elt.appendChild(ownerDocument.createTextNode(cur.displayText || getText(cur))); cur.render(elt, data, cur)
elt.hintId = i; } else {
elt.appendChild(ownerDocument.createTextNode(cur.displayText || getText(cur)))
}
elt.hintId = i
} }
var container = completion.options.container || ownerDocument.body; var container = completion.options.container || ownerDocument.body
var pos = cm.cursorCoords(completion.options.alignWithWord ? data.from : null); var pos = cm.cursorCoords(completion.options.alignWithWord ? data.from : null)
var left = pos.left, top = pos.bottom, below = true; var left = pos.left, top = pos.bottom, below = true
var offsetLeft = 0, offsetTop = 0; var offsetLeft = 0, offsetTop = 0
if (container !== ownerDocument.body) { if (container !== ownerDocument.body) {
// We offset the cursor position because left and top are relative to the offsetParent's top left corner. // We offset the cursor position because left and top are relative to the offsetParent's top left corner.
var isContainerPositioned = ['absolute', 'relative', 'fixed'].indexOf(parentWindow.getComputedStyle(container).position) !== -1; var isContainerPositioned = ['absolute', 'relative', 'fixed'].indexOf(parentWindow.getComputedStyle(container).position) !== -1
var offsetParent = isContainerPositioned ? container : container.offsetParent; var offsetParent = isContainerPositioned ? container : container.offsetParent
var offsetParentPosition = offsetParent.getBoundingClientRect(); var offsetParentPosition = offsetParent.getBoundingClientRect()
var bodyPosition = ownerDocument.body.getBoundingClientRect(); var bodyPosition = ownerDocument.body.getBoundingClientRect()
offsetLeft = (offsetParentPosition.left - bodyPosition.left - offsetParent.scrollLeft); offsetLeft = (offsetParentPosition.left - bodyPosition.left - offsetParent.scrollLeft)
offsetTop = (offsetParentPosition.top - bodyPosition.top - offsetParent.scrollTop); offsetTop = (offsetParentPosition.top - bodyPosition.top - offsetParent.scrollTop)
} }
hints.style.left = (left - offsetLeft) + "px"; hints.style.left = (left - offsetLeft) + 'px'
hints.style.top = (top - offsetTop) + "px"; hints.style.top = (top - offsetTop) + 'px'
// todo 隐藏codemirror自带的提示 // todo 隐藏codemirror自带的提示
hints.style.display = 'none' hints.style.display = 'none'
// If we're at the edge of the screen, then we want the menu to appear on the left of the cursor. // If we're at the edge of the screen, then we want the menu to appear on the left of the cursor.
var winW = parentWindow.innerWidth || Math.max(ownerDocument.body.offsetWidth, ownerDocument.documentElement.offsetWidth); var winW = parentWindow.innerWidth || Math.max(ownerDocument.body.offsetWidth, ownerDocument.documentElement.offsetWidth)
var winH = parentWindow.innerHeight || Math.max(ownerDocument.body.offsetHeight, ownerDocument.documentElement.offsetHeight); var winH = parentWindow.innerHeight || Math.max(ownerDocument.body.offsetHeight, ownerDocument.documentElement.offsetHeight)
//不用默认的DOM 下拉提示 //不用默认的DOM 下拉提示
// container.appendChild(hints); // container.appendChild(hints);
hints.remove() hints.remove()
cm.getInputField().setAttribute("aria-autocomplete", "list") cm.getInputField().setAttribute('aria-autocomplete', 'list')
cm.getInputField().setAttribute("aria-owns", this.id) cm.getInputField().setAttribute('aria-owns', this.id)
cm.getInputField().setAttribute("aria-activedescendant", this.id + "-" + this.selectedHint) cm.getInputField().setAttribute('aria-activedescendant', this.id + '-' + this.selectedHint)
var box = completion.options.moveOnOverlap ? hints.getBoundingClientRect() : new DOMRect(); var box = completion.options.moveOnOverlap ? hints.getBoundingClientRect() : new DOMRect()
var scrolls = completion.options.paddingForScrollbar ? hints.scrollHeight > hints.clientHeight + 1 : false; var scrolls = completion.options.paddingForScrollbar ? hints.scrollHeight > hints.clientHeight + 1 : false
// Compute in the timeout to avoid reflow on init // Compute in the timeout to avoid reflow on init
var startScroll; var startScroll
setTimeout(function () { setTimeout(function () {
startScroll = cm.getScrollInfo(); startScroll = cm.getScrollInfo()
}); })
var overlapY = box.bottom - winH; var overlapY = box.bottom - winH
if (overlapY > 0) { if (overlapY > 0) {
var height = box.bottom - box.top, curTop = pos.top - (pos.bottom - box.top); var height = box.bottom - box.top, curTop = pos.top - (pos.bottom - box.top)
if (curTop - height > 0) { // Fits above cursor if (curTop - height > 0) { // Fits above cursor
hints.style.top = (top = pos.top - height - offsetTop) + "px"; hints.style.top = (top = pos.top - height - offsetTop) + 'px'
below = false; below = false
} else if (height > winH) { } else if (height > winH) {
hints.style.height = (winH - 5) + "px"; hints.style.height = (winH - 5) + 'px'
hints.style.top = (top = pos.bottom - box.top - offsetTop) + "px"; hints.style.top = (top = pos.bottom - box.top - offsetTop) + 'px'
var cursor = cm.getCursor(); var cursor = cm.getCursor()
if (data.from.ch != cursor.ch) { if (data.from.ch != cursor.ch) {
pos = cm.cursorCoords(cursor); pos = cm.cursorCoords(cursor)
hints.style.left = (left = pos.left - offsetLeft) + "px"; hints.style.left = (left = pos.left - offsetLeft) + 'px'
box = hints.getBoundingClientRect(); box = hints.getBoundingClientRect()
} }
} }
} }
var overlapX = box.right - winW; var overlapX = box.right - winW
if (scrolls) overlapX += cm.display.nativeBarWidth; if (scrolls) overlapX += cm.display.nativeBarWidth
if (overlapX > 0) { if (overlapX > 0) {
if (box.right - box.left > winW) { if (box.right - box.left > winW) {
hints.style.width = (winW - 5) + "px"; hints.style.width = (winW - 5) + 'px'
overlapX -= (box.right - box.left) - winW; overlapX -= (box.right - box.left) - winW
}
hints.style.left = (left = Math.max(pos.left - overlapX - offsetLeft, 0)) + 'px'
}
if (scrolls) {
for (var node = hints.firstChild; node; node = node.nextSibling) {
node.style.paddingRight = cm.display.nativeBarWidth + 'px'
} }
hints.style.left = (left = Math.max(pos.left - overlapX - offsetLeft, 0)) + "px";
} }
if (scrolls) for (var node = hints.firstChild; node; node = node.nextSibling)
node.style.paddingRight = cm.display.nativeBarWidth + "px"
// debugger // debugger
cm.addKeyMap(this.keyMap = buildKeyMap(completion, { cm.addKeyMap(this.keyMap = buildKeyMap(completion, {
moveFocus: function (n, avoidWrap) { moveFocus: function (n, avoidWrap) {
n === -1 ? keyboardUp && keyboardUp() : keyboardDown && keyboardDown() n === -1 ? keyboardUp && keyboardUp() : keyboardDown && keyboardDown()
widget.changeActive(widget.selectedHint + n, avoidWrap); widget.changeActive(widget.selectedHint + n, avoidWrap)
}, },
setFocus: function (n) { setFocus: function (n) {
widget.changeActive(n); widget.changeActive(n)
}, },
menuSize: function () { menuSize: function () {
return widget.screenAmount(); return widget.screenAmount()
}, },
length: completions.length, length: completions.length,
close: function () { close: function () {
completion.close(); completion.close()
}, },
pick: function () { pick: function () {
widget.pick(); widget.pick()
}, },
data: data data: data
})); }))
if (completion.options.closeOnUnfocus) { if (completion.options.closeOnUnfocus) {
var closingOnBlur; var closingOnBlur
cm.on("blur", this.onBlur = function () { cm.on('blur', this.onBlur = function () {
closingOnBlur = setTimeout(function () { closingOnBlur = setTimeout(function () {
completion.close(); completion.close()
}, 100); }, 100)
}); })
cm.on("focus", this.onFocus = function () { cm.on('focus', this.onFocus = function () {
clearTimeout(closingOnBlur); clearTimeout(closingOnBlur)
}); })
} }
cm.on("scroll", this.onScroll = function () { cm.on('scroll', this.onScroll = function () {
var curScroll = cm.getScrollInfo(), editor = cm.getWrapperElement().getBoundingClientRect(); var curScroll = cm.getScrollInfo(), editor = cm.getWrapperElement().getBoundingClientRect()
if (!startScroll) startScroll = cm.getScrollInfo(); if (!startScroll) startScroll = cm.getScrollInfo()
var newTop = top + startScroll.top - curScroll.top; var newTop = top + startScroll.top - curScroll.top
var point = newTop - (parentWindow.pageYOffset || (ownerDocument.documentElement || ownerDocument.body).scrollTop); var point = newTop - (parentWindow.pageYOffset || (ownerDocument.documentElement || ownerDocument.body).scrollTop)
if (!below) point += hints.offsetHeight; if (!below) point += hints.offsetHeight
if (point <= editor.top || point >= editor.bottom) return completion.close(); if (point <= editor.top || point >= editor.bottom) return completion.close()
hints.style.top = newTop + "px"; hints.style.top = newTop + 'px'
hints.style.left = (left + startScroll.left - curScroll.left) + "px"; hints.style.left = (left + startScroll.left - curScroll.left) + 'px'
}); })
CodeMirror.on(hints, "dblclick", function (e) { CodeMirror.on(hints, 'dblclick', function (e) {
var t = getHintElement(hints, e.target || e.srcElement); var t = getHintElement(hints, e.target || e.srcElement)
if (t && t.hintId != null) { if (t && t.hintId != null) {
widget.changeActive(t.hintId); widget.changeActive(t.hintId)
widget.pick(); widget.pick()
} }
}); })
CodeMirror.on(hints, "click", function (e) { CodeMirror.on(hints, 'click', function (e) {
var t = getHintElement(hints, e.target || e.srcElement); var t = getHintElement(hints, e.target || e.srcElement)
if (t && t.hintId != null) { if (t && t.hintId != null) {
widget.changeActive(t.hintId); widget.changeActive(t.hintId)
if (completion.options.completeOnSingleClick) widget.pick(); if (completion.options.completeOnSingleClick) widget.pick()
} }
}); })
CodeMirror.on(hints, "mousedown", function () { CodeMirror.on(hints, 'mousedown', function () {
setTimeout(function () { setTimeout(function () {
cm.focus(); cm.focus()
}, 20); }, 20)
}); })
// The first hint doesn't need to be scrolled to on init // The first hint doesn't need to be scrolled to on init
var selectedHintRange = this.getSelectedHintRange(); var selectedHintRange = this.getSelectedHintRange()
if (selectedHintRange.from !== 0 || selectedHintRange.to !== 0) { if (selectedHintRange.from !== 0 || selectedHintRange.to !== 0) {
this.scrollToActive(); this.scrollToActive()
} }
CodeMirror.signal(data, "select", completions[this.selectedHint], hints.childNodes[this.selectedHint]); CodeMirror.signal(data, 'select', completions[this.selectedHint], hints.childNodes[this.selectedHint])
return true; return true
} }
Widget.prototype = { Widget.prototype = {
close: function () { close: function () {
if (this.completion.widget != this) return; if (this.completion.widget != this) return
this.completion.widget = null; this.completion.widget = null
if (this.hints.parentNode) this.hints.parentNode.removeChild(this.hints); if (this.hints.parentNode) this.hints.parentNode.removeChild(this.hints)
this.completion.cm.removeKeyMap(this.keyMap); this.completion.cm.removeKeyMap(this.keyMap)
var input = this.completion.cm.getInputField() var input = this.completion.cm.getInputField()
input.removeAttribute("aria-activedescendant") input.removeAttribute('aria-activedescendant')
input.removeAttribute("aria-owns") input.removeAttribute('aria-owns')
var cm = this.completion.cm; var cm = this.completion.cm
if (this.completion.options.closeOnUnfocus) { if (this.completion.options.closeOnUnfocus) {
cm.off("blur", this.onBlur); cm.off('blur', this.onBlur)
cm.off("focus", this.onFocus); cm.off('focus', this.onFocus)
} }
cm.off("scroll", this.onScroll); cm.off('scroll', this.onScroll)
}, },
disable: function () { disable: function () {
this.completion.cm.removeKeyMap(this.keyMap); this.completion.cm.removeKeyMap(this.keyMap)
var widget = this; var widget = this
this.keyMap = { this.keyMap = {
Enter: function () { Enter: function () {
widget.picked = true; widget.picked = true
} }
}; }
this.completion.cm.addKeyMap(this.keyMap); this.completion.cm.addKeyMap(this.keyMap)
}, },
pick: function () { pick: function () {
this.completion.pick(this.data, this.selectedHint); this.completion.pick(this.data, this.selectedHint)
}, },
changeActive: function (i, avoidWrap) { changeActive: function (i, avoidWrap) {
if (i >= this.data.list.length) if (i >= this.data.list.length) {
i = avoidWrap ? this.data.list.length - 1 : 0; i = avoidWrap ? this.data.list.length - 1 : 0
else if (i < 0) } else if (i < 0) {
i = avoidWrap ? 0 : this.data.list.length - 1; i = avoidWrap ? 0 : this.data.list.length - 1
if (this.selectedHint == i) return;
var node = this.hints.childNodes[this.selectedHint];
if (node) {
node.className = node.className.replace(" " + ACTIVE_HINT_ELEMENT_CLASS, "");
node.removeAttribute("aria-selected")
} }
node = this.hints.childNodes[this.selectedHint = i]; if (this.selectedHint == i) return
node.className += " " + ACTIVE_HINT_ELEMENT_CLASS; var node = this.hints.childNodes[this.selectedHint]
node.setAttribute("aria-selected", "true") if (node) {
this.completion.cm.getInputField().setAttribute("aria-activedescendant", node.id) node.className = node.className.replace(' ' + ACTIVE_HINT_ELEMENT_CLASS, '')
node.removeAttribute('aria-selected')
}
node = this.hints.childNodes[this.selectedHint = i]
node.className += ' ' + ACTIVE_HINT_ELEMENT_CLASS
node.setAttribute('aria-selected', 'true')
this.completion.cm.getInputField().setAttribute('aria-activedescendant', node.id)
this.scrollToActive() this.scrollToActive()
CodeMirror.signal(this.data, "select", this.data.list[this.selectedHint], node); CodeMirror.signal(this.data, 'select', this.data.list[this.selectedHint], node)
}, },
scrollToActive: function () { scrollToActive: function () {
var selectedHintRange = this.getSelectedHintRange(); var selectedHintRange = this.getSelectedHintRange()
var node1 = this.hints.childNodes[selectedHintRange.from]; var node1 = this.hints.childNodes[selectedHintRange.from]
var node2 = this.hints.childNodes[selectedHintRange.to]; var node2 = this.hints.childNodes[selectedHintRange.to]
var firstNode = this.hints.firstChild; var firstNode = this.hints.firstChild
if (node1.offsetTop < this.hints.scrollTop) if (node1.offsetTop < this.hints.scrollTop) {
this.hints.scrollTop = node1.offsetTop - firstNode.offsetTop; this.hints.scrollTop = node1.offsetTop - firstNode.offsetTop
else if (node2.offsetTop + node2.offsetHeight > this.hints.scrollTop + this.hints.clientHeight) } else if (node2.offsetTop + node2.offsetHeight > this.hints.scrollTop + this.hints.clientHeight) {
this.hints.scrollTop = node2.offsetTop + node2.offsetHeight - this.hints.clientHeight + firstNode.offsetTop; this.hints.scrollTop = node2.offsetTop + node2.offsetHeight - this.hints.clientHeight + firstNode.offsetTop
}
}, },
screenAmount: function () { screenAmount: function () {
return Math.floor(this.hints.clientHeight / this.hints.firstChild.offsetHeight) || 1; return Math.floor(this.hints.clientHeight / this.hints.firstChild.offsetHeight) || 1
}, },
getSelectedHintRange: function () { getSelectedHintRange: function () {
var margin = this.completion.options.scrollMargin || 0; var margin = this.completion.options.scrollMargin || 0
return { return {
from: Math.max(0, this.selectedHint - margin), from: Math.max(0, this.selectedHint - margin),
to: Math.min(this.data.list.length - 1, this.selectedHint + margin), to: Math.min(this.data.list.length - 1, this.selectedHint + margin),
}; }
} }
}; }
function applicableHelpers(cm, helpers) { function applicableHelpers (cm, helpers) {
if (!cm.somethingSelected()) return helpers if (!cm.somethingSelected()) return helpers
var result = [] var result = []
for (var i = 0; i < helpers.length; i++) for (var i = 0; i < helpers.length; i++) {
if (helpers[i].supportsSelection) result.push(helpers[i]) if (helpers[i].supportsSelection) result.push(helpers[i])
}
return result return result
} }
function fetchHints(hint, cm, options, callback) { function fetchHints (hint, cm, options, callback) {
if (hint.async) { if (hint.async) {
hint(cm, callback, options) hint(cm, callback, options)
} else { } else {
var result = hint(cm, options) var result = hint(cm, options)
if (result && result.then) result.then(callback) if (result && result.then) {
else callback(result) result.then(callback)
} else {
callback(result)
}
} }
} }
function resolveAutoHints(cm, pos) { function resolveAutoHints (cm, pos) {
var helpers = cm.getHelpers(pos, "hint"), words var helpers = cm.getHelpers(pos, 'hint'), words
if (helpers.length) { if (helpers.length) {
var resolved = function (cm, callback, options) { var resolved = function (cm, callback, options) {
var app = applicableHelpers(cm, helpers); var app = applicableHelpers(cm, helpers)
function run(i) { function run (i) {
if (i == app.length) return callback(null) if (i == app.length) return callback(null)
fetchHints(app[i], cm, options, function (result) { fetchHints(app[i], cm, options, function (result) {
if (result && result.list.length > 0) callback(result) if (result && result.list.length > 0) {
else run(i + 1) callback(result)
} else {
run(i + 1)
}
}) })
} }
@@ -534,9 +576,9 @@ export default function (CodeMirror,{
resolved.async = true resolved.async = true
resolved.supportsSelection = true resolved.supportsSelection = true
return resolved return resolved
} else if (words = cm.getHelper(cm.getCursor(), "hintWords")) { } else if (words = cm.getHelper(cm.getCursor(), 'hintWords')) {
return function (cm) { return function (cm) {
return CodeMirror.hint.fromList(cm, {words: words}) return CodeMirror.hint.fromList(cm, { words: words })
} }
} else if (CodeMirror.hint.anyword) { } else if (CodeMirror.hint.anyword) {
return function (cm, options) { return function (cm, options) {
@@ -548,28 +590,35 @@ export default function (CodeMirror,{
} }
} }
CodeMirror.registerHelper("hint", "auto", { CodeMirror.registerHelper('hint', 'auto', {
resolve: resolveAutoHints resolve: resolveAutoHints
}); })
CodeMirror.registerHelper("hint", "fromList", function (cm, options) { CodeMirror.registerHelper('hint', 'fromList', function (cm, options) {
var cur = cm.getCursor(), token = cm.getTokenAt(cur) var cur = cm.getCursor(), token = cm.getTokenAt(cur)
var term, from = CodeMirror.Pos(cur.line, token.start), to = cur var term, from = CodeMirror.Pos(cur.line, token.start), to = cur
if (token.start < cur.ch && /\w/.test(token.string.charAt(cur.ch - token.start - 1))) { if (token.start < cur.ch && /\w/.test(token.string.charAt(cur.ch - token.start - 1))) {
term = token.string.substr(0, cur.ch - token.start) term = token.string.substr(0, cur.ch - token.start)
} else { } else {
term = "" term = ''
from = cur from = cur
} }
var found = []; var found = []
for (var i = 0; i < options.words.length; i++) { for (var i = 0; i < options.words.length; i++) {
var word = options.words[i]; var word = options.words[i]
if (word.slice(0, term.length) == term) if (word.slice(0, term.length) == term) {
found.push(word); found.push(word)
}
} }
if (found.length) return {list: found, from: from, to: to}; if (found.length) {
}); return {
list: found,
from: from,
to: to
}
}
})
CodeMirror.commands.autocomplete = CodeMirror.showHint; CodeMirror.commands.autocomplete = CodeMirror.showHint;

View File

@@ -8,16 +8,35 @@ export class Dataset {
this.sourceData = this.keepSourcedata(operatesList, funcDic, filtersList, operatesDic, operatorManual, fields, doc) this.sourceData = this.keepSourcedata(operatesList, funcDic, filtersList, operatesDic, operatorManual, fields, doc)
// 存储格式化的数据 // 存储格式化的数据
this.hintData = {} this.hintData = {}
this.columnList = JSON.parse(JSON.stringify(filtersList))
this.hintData.operatesList = this.formatOperates(operatesList) this.hintData.operatesList = this.formatOperates(operatesList)
this.hintData.filtersList = this.formatFilters(filtersList) this.hintData.filtersList = this.formatFilters(filtersList)
} }
getHintList (keyword, sqlKeywordsOptions = null, callback, completeFilter) { getHintList (keyword, sqlKeywordsOptions = null, callback, completeFilter) {
// 获取提示列表 // 获取提示列表
const operatesList = this.matchFilter(sqlKeywordsOptions || this.hintData.operatesList || [], keyword) let operatesList = this.matchFilter(sqlKeywordsOptions || this.hintData.operatesList || [], keyword)
const filtersList = this.matchFilter(this.hintData.filtersList || [], keyword) let filtersList = this.matchFilter(this.hintData.filtersList || [], keyword)
let fieldOperatesList = []
if (keyword) {
const obj = this.columnList.find(d => d.label === keyword)
if (obj) {
const operatorFunctions = obj.doc && obj.doc.constraints && obj.doc.constraints.operator_functions
fieldOperatesList = this.formatFieldOperates(operatorFunctions.split(','))
} else if (keyword === '=') {
fieldOperatesList = this.formatFieldOperates(['='])
filtersList = []
} else if (keyword === 'in') {
fieldOperatesList = this.formatFieldOperates(['in'])
operatesList = []
filtersList = []
} else if (keyword === 'like') {
fieldOperatesList = this.formatFieldOperates(['like'])
filtersList = []
}
}
const hintList = [...operatesList, ...filtersList] const hintList = [...operatesList, ...filtersList, ...fieldOperatesList]
callback && callback(operatesList, filtersList, hintList) callback && callback(operatesList, filtersList, hintList)
return hintList return hintList
} }
@@ -110,6 +129,29 @@ export class Dataset {
return results return results
} }
formatFieldOperates (list) {
// 格式化操作符,如=inlike
const tempTitle = {
type: 'abstract',
text: '',
displayText: i18n.global.t('overall.operators'),
className: 'divider hint-title',
hint (cm, callback, options) {
}
}
const results = list.map((item) => {
return {
text: item,
displayText: item,
className: 'filter-item el-dropdown-menu__item relative-item',
field: item
}
})
results.unshift(tempTitle)
return results
}
keepSourcedata (operatesList, funcDic, filtersList, operatesDic, operatorManual, fields, doc) { keepSourcedata (operatesList, funcDic, filtersList, operatesDic, operatorManual, fields, doc) {
// 初始化原始数据 方法(存放的是 全量原始数据) // 初始化原始数据 方法(存放的是 全量原始数据)
const sourceData = {} const sourceData = {}