@whitesev/pops
Version:
弹窗库,包含了alert、confirm、prompt、drawer、folder、loading、iframe、panel、tooltip、searchSuggestion、rightClickMenu组件
824 lines (816 loc) • 31.7 kB
text/typescript
import { PopsCommonCSSClassName } from "../../config/CommonCSSClassName";
import { GlobalConfig } from "../../config/GlobalConfig";
import { EventEmiter } from "../../event/EventEmiter";
import { PopsElementHandler } from "../../handler/PopsElementHandler";
import { PopsHandler } from "../../handler/PopsHandler";
import { PopsCSS } from "../../PopsCSS";
import type { EventMap } from "../../types/EventEmitter";
import type { PopsType } from "../../types/main";
import { popsDOMUtils } from "../../utils/PopsDOMUtils";
import { PopsSafeUtils } from "../../utils/PopsSafeUtils";
import { popsUtils } from "../../utils/PopsUtils";
import { PopsSearchSuggestionDefaultConfig } from "./defaultConfig";
import type { PopsSearchSuggestionConfig, PopsSearchSuggestionData } from "./types/index";
export const PopsSearchSuggestion = {
init<T>(__config__: PopsSearchSuggestionConfig<T>) {
const guid = popsUtils.getRandomGUID();
// 设置当前类型
const popsType: PopsType = "searchSuggestion";
let config = PopsSearchSuggestionDefaultConfig();
config = popsUtils.assign(config, GlobalConfig.getGlobalConfig());
config = popsUtils.assign(config, __config__);
// 如果$inputTarget为空,则根据$target
if (config.$inputTarget == null) {
config.$inputTarget = config.$target as HTMLInputElement;
}
const emitter = config.emitter ?? new EventEmiter<EventMap>(popsType);
const { $shadowContainer, $shadowRoot } = PopsHandler.handlerShadow(config);
PopsHandler.handleInit($shadowRoot, [
{
name: "index",
css: PopsCSS.index,
},
{
name: "anim",
css: PopsCSS.anim,
},
{
name: "common",
css: PopsCSS.common,
},
{
name: "skeleton",
css: PopsCSS.skeletonCSS,
},
]);
// 添加自定义style
PopsElementHandler.addStyle($shadowRoot, config.style);
// 添加自定义浅色style
PopsElementHandler.addLightStyle($shadowRoot, config.lightStyle);
// 添加自定义深色style
PopsElementHandler.addDarkStyle($shadowRoot, config.darkStyle);
/**
* 监听器的默认配置
*/
const defaultListenerOption: AddEventListenerOptions = {
capture: true,
passive: true,
};
const SearchSuggestion = {
emitter: emitter,
/**
* 当前的环境,可以是document,可以是shadowroot,默认是document
*/
selfDocument: config.$selfDocument,
$el: {
/** 根元素 */
root: null as any as HTMLElement,
/**
* 包裹ul的容器元素
*/
$dropdownWrapper: null as any as HTMLElement,
/** ul元素 */
$dropdownContainer: null as any as HTMLUListElement,
/**
* 箭头元素
*/
$arrow: null as any as HTMLDivElement,
/** 动态更新CSS */
$dynamicCSS: null as any as HTMLStyleElement,
},
$evt: {
offInputChangeEvtHandler: [] as ((...args: any[]) => any)[],
},
$data: {
/** 是否结果为空 */
isEmpty: true,
/**
* 存储在元素上的操作的键名
*/
storeNodeHandlerKey: "data-SearchSuggestion",
},
/**
* 初始化
* @param $parent 父元素
* @example
* .init();
* .setAllEvent();
*/
init($parent = document.body || document.documentElement) {
SearchSuggestion.initEl();
SearchSuggestion.update(SearchSuggestion.getData());
SearchSuggestion.updateStyleSheet();
SearchSuggestion.hide();
$shadowRoot.appendChild(SearchSuggestion.$el.root);
$parent.appendChild($shadowContainer);
},
/**
* 初始化元素变量
*/
initEl() {
SearchSuggestion.$el.root = SearchSuggestion.createSearchSelectElement();
Reflect.set(SearchSuggestion.$el.root, this.$data.storeNodeHandlerKey, SearchSuggestion);
SearchSuggestion.$el.$dynamicCSS =
SearchSuggestion.$el.root.querySelector<HTMLStyleElement>("style[data-dynamic]")!;
SearchSuggestion.$el.$dropdownWrapper = SearchSuggestion.$el.root.querySelector<HTMLElement>(
`.pops-${popsType}-search-suggestion-dropdown-wrapper`
)!;
SearchSuggestion.$el.$dropdownContainer = SearchSuggestion.$el.root.querySelector<HTMLUListElement>(
`ul.pops-${popsType}-search-suggestion-dropdown-container`
)!;
SearchSuggestion.$el.$arrow = SearchSuggestion.$el.root.querySelector<HTMLDivElement>(
`.pops-${popsType}-search-suggestion-arrow`
)!;
},
/**
* 获取数据
*/
getData(): PopsSearchSuggestionData<T>[] {
return typeof config.data === "function" ? config.data() : config.data;
},
/**
* 更新数据
* @param data 数据
*/
setData(data: PopsSearchSuggestionData<T>[]) {
(<PopsSearchSuggestionConfig<T>["data"]>config.data) = data;
},
/**
* 获取显示出搜索建议框的html
*/
createSearchSelectElement() {
const $el = popsDOMUtils.createElement(
"div",
{
className: `pops pops-${popsType}-search-suggestion`,
innerHTML: /*html*/ `
<style type="text/css">
.pops-${popsType}-animation{
-moz-animation: searchSelectFalIn 0.5s 1 linear;
-webkit-animation: searchSelectFalIn 0.5s 1 linear;
-o-animation: searchSelectFalIn 0.5s 1 linear;
-ms-animation: searchSelectFalIn 0.5s 1 linear;
}
</style>
<style type="text/css">
.pops-${popsType}-search-suggestion-arrow{
--suggestion-arrow-box-shadow-left-color: rgba(0, 0, 0, 0.24);
--suggestion-arrow-box-shadow-right-color: rgba(0, 0, 0, 0.12);
--suggestion-arrow--after-color: rgb(78, 78, 78);
--suggestion-arrow--after-bg-color: rgb(255, 255, 255, var(--pops-bg-opacity));
--suggestion-arrow--after-width: 10px;
--suggestion-arrow--after-height: 10px;
}
.pops-${popsType}-search-suggestion-arrow{
position: absolute;
top: 100%;
left: 50%;
overflow: hidden;
width: 100%;
height: 12.5px;
transform: translateX(-50%);
}
.pops-${popsType}-search-suggestion-arrow::after{
position: absolute;
top: 0;
left: 50%;
width: var(--suggestion-arrow--after-width);
height: var(--suggestion-arrow--after-height);
background: var(--suggestion-arrow--after-bg-color);
color: var(--suggestion-arrow--after-color);
box-shadow:
0 1px 7px var(--suggestion-arrow-box-shadow-left-color),
0 1px 7px var(--suggestion-arrow-box-shadow-right-color);
content: "";
transform: translateX(-50%) translateY(-50%) rotate(45deg);
}
.pops-${popsType}-search-suggestion[data-popper-placement^="top"] .pops-${popsType}-search-suggestion-arrow{
position: absolute;
top: 100%;
left: 50%;
overflow: hidden;
width: 100%;
height: 12.5px;
transform: translateX(-50%);
}
.pops-${popsType}-search-suggestion[data-popper-placement^="top"] .pops-${popsType}-search-suggestion-arrow::after{
position: absolute;
top: 0;
left: 50%;
width: var(--suggestion-arrow--after-width);
height: var(--suggestion-arrow--after-height);
background: var(--suggestion-arrow--after-bg-color);
box-shadow:
0 1px 7px var(--suggestion-arrow-box-shadow-left-color),
0 1px 7px var(--suggestion-arrow-box-shadow-right-color);
content: "";
transform: translateX(-50%) translateY(-50%) rotate(45deg);
}
.pops-${popsType}-search-suggestion[data-popper-placement^="bottom"] .pops-${popsType}-search-suggestion-arrow{
top: -12.5px;
left: 50%;
transform: translateX(-50%);
}
.pops-${popsType}-search-suggestion[data-popper-placement^="bottom"] .pops-${popsType}-search-suggestion-arrow::after{
position: absolute;
top: 100%;
left: 50%;
content: "";
}
</style>
<style type="text/css" data-dynamic="true">
${SearchSuggestion.getDynamicCSS()}
</style>
<style>
.el-zoom-in-top-animation{
--el-transition-md-fade: transform 0.3s cubic-bezier(0.23, 1, 0.32, 1),
opacity 0.3s cubic-bezier(0.23, 1, 0.32, 1);
transition: var(--el-transition-md-fade);
transform-origin: center top;
}
.el-zoom-in-top-animation[data-popper-placement^="top"] {
transform-origin: center bottom;
}
.el-zoom-in-top-animation-hide{
opacity: 0;
transform: scaleY(0);
}
.el-zoom-in-top-animation-show{
opacity: 1;
transform: scaleY(1);
}
</style>
<style type="text/css" data-user-css>
${config.style || ""}
</style>
<div class="pops-${popsType}-search-suggestion-dropdown-wrapper">
<ul class="pops-${popsType}-search-suggestion-dropdown-container ${PopsCommonCSSClassName.userSelectNone}">${
config.toSearhNotResultHTML
}</ul>
</div>
<!-- 箭头 -->
${config.useArrow ? /*html*/ `<div class="pops-${popsType}-search-suggestion-arrow"></div>` : ""}
`,
},
{
"data-guid": guid,
"type-value": popsType,
}
);
if (config.className !== "" && config.className != null) {
popsDOMUtils.addClassName($el, config.className);
}
if (config.isAnimation) {
popsDOMUtils.addClassName($el, `pops-${popsType}-animation`);
}
if (config.useFoldAnimation) {
popsDOMUtils.addClassName($el, "el-zoom-in-top-animation");
}
return $el;
},
/**
* 动态获取CSS
*/
getDynamicCSS() {
return /*css*/ `
.pops-${popsType}-search-suggestion{
--search-suggestion-bg-color: #ffffff;
--search-suggestion-box-shadow-color: rgb(0 0 0 / 20%);
--search-suggestion-item-color: #515a6e;
--search-suggestion-item-none-color: #8e8e8e;
--search-suggestion-item-is-hover-bg-color: #f5f7fa;
--search-suggestion-item-is-select-bg-color: #409eff;
}
.pops-${popsType}-search-suggestion{
border: initial;
overflow: initial;
position: ${config.isAbsolute ? "absolute" : "fixed"};
z-index: ${PopsHandler.getTargerOrFunctionValue(config.zIndex)};
}
.pops-${popsType}-search-suggestion-dropdown-wrapper{
max-height: ${config.maxHeight};
border-radius: 4px;
box-shadow: 0 1px 6px var(--search-suggestion-box-shadow-color);
background-color: var(--search-suggestion-bg-color);
padding: 5px 0;
overflow-x: hidden;
overflow-y: auto;
}
.pops-${popsType}-search-suggestion-dropdown-wrapper ul.pops-${popsType}-search-suggestion-dropdown-container{
box-sizing: border-box;
}
// 建议框在上面时
.pops-${popsType}-search-suggestion[data-top-reverse] ul.pops-${popsType}-search-suggestion-dropdown-container{
display: flex;
flex-direction: column-reverse;
}
.pops-${popsType}-search-suggestion[data-top-reverse] ul.pops-${popsType}-search-suggestion-dropdown-container li{
flex-shrink: 0;
}
ul.pops-${popsType}-search-suggestion-dropdown-container li{
padding: 7px;
margin: 0;
clear: both;
color: var(--search-suggestion-item-color);
font-size: 14px;
list-style: none;
cursor: pointer;
transition: background .2s ease-in-out;
overflow: hidden;
text-overflow: ellipsis;
width: 100%;
}
ul.pops-${popsType}-search-suggestion-dropdown-container li[data-none]{
text-align: center;
font-size: 12px;
color: var(--search-suggestion-item-none-color);
}
ul.pops-${popsType}-search-suggestion-dropdown-container li:not([data-none]):hover{
background-color: var(--search-suggestion-item-is-hover-bg-color);
}
(prefers-color-scheme: dark){
.pops-${popsType}-search-suggestion{
--search-suggestion-bg-color: #1d1e1f;
--search-suggestion-item-color: #cfd3d4;
--search-suggestion-item-is-hover-bg-color: rgba(175, 175, 175, .1);
}
}
`;
},
/**
* 获取data-value值
* @param data 数据项
*/
getItemDataValue(data: PopsSearchSuggestionData<T>) {
return data;
},
/**
* 获取显示出搜索建议框的每一项的html
* @param dataItem 当前项的值
* @param dateItemIndex 当前项的下标
*/
createSearchItemLiElement(dataItem: PopsSearchSuggestionData<T>, dateItemIndex: number) {
const dataValue = SearchSuggestion.getItemDataValue(dataItem);
const $li = popsDOMUtils.createElement("li", {
className: `pops-${popsType}-search-suggestion-dropdown-item`,
"data-index": dateItemIndex,
"data-value": dataValue,
});
Reflect.set($li, "data-index", dateItemIndex);
Reflect.set($li, "data-value", dataValue);
// 项内容
const $itemInner = dataItem.itemView(dataItem, $li, config);
if (typeof $itemInner === "string") {
PopsSafeUtils.setSafeHTML($li, $itemInner);
} else {
popsDOMUtils.append($li, $itemInner);
}
// 删除按钮
const enableDeleteButton = dataItem.enableDeleteButton;
if (typeof enableDeleteButton === "boolean" && enableDeleteButton) {
const $deleteIcon = SearchSuggestion.createItemDeleteIcon();
popsDOMUtils.append($li, $deleteIcon);
}
popsDOMUtils.addClassName($li, PopsCommonCSSClassName.flexCenter);
popsDOMUtils.addClassName($li, PopsCommonCSSClassName.flexYCenter);
return $li;
},
/**
* 设置搜索建议框每一项的点击事件
* @param $searchItem 当前项的元素
*/
setSearchItemClickEvent($searchItem: HTMLLIElement) {
popsDOMUtils.on(
$searchItem,
"click",
async (event) => {
popsDOMUtils.preventEvent(event);
const $click = event.target as HTMLLIElement;
const data = SearchSuggestion.getData();
const dataItem = Reflect.get($searchItem, "data-value") as PopsSearchSuggestionData<T>;
const isDelete = Boolean($click.closest(`.pops-${popsType}-delete-icon`));
if (isDelete) {
// 删除
if (typeof dataItem.deleteButtonClickCallback === "function") {
const result = await dataItem.deleteButtonClickCallback(event, $searchItem, dataItem, config);
if (typeof result === "boolean" && result) {
data.splice(data.indexOf(dataItem), 1);
popsDOMUtils.remove($searchItem);
}
}
if (!SearchSuggestion.$el.$dropdownContainer.children.length) {
// 全删完了
SearchSuggestion.clear();
}
SearchSuggestion.updateStyleSheet();
} else {
// 点击选择项
if (typeof dataItem.clickCallback === "function") {
const result = await dataItem.clickCallback(event, $searchItem, dataItem, config);
if (typeof result === "boolean" && result) {
if (
config.$inputTarget instanceof HTMLInputElement ||
config.$inputTarget instanceof HTMLTextAreaElement
) {
config.$inputTarget.value = String(dataItem.value);
}
}
}
}
},
{
capture: true,
}
);
},
/**
* 设置搜索建议框每一项的选中事件
* @param $li 每一项元素
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
setSearchItemSelectEvent($li: HTMLLIElement) {
// TODO
},
/**
* 监听输入框内容改变
*/
setInputChangeEvent(option: AddEventListenerOptions = defaultListenerOption) {
// 必须是input或者textarea才有input事件
if (!(config.$inputTarget instanceof HTMLInputElement || config.$inputTarget instanceof HTMLTextAreaElement)) {
return;
}
// 是input输入框
// 禁用输入框自动提示
config.$inputTarget.setAttribute("autocomplete", "off");
// 内容改变事件
const listenerHandler = popsDOMUtils.onInput(
config.$inputTarget,
async () => {
const data = SearchSuggestion.getData();
const queryDataResult = await config.inputTargetChangeRefreshShowDataCallback(
config.$inputTarget.value,
data,
config
);
SearchSuggestion.update(queryDataResult);
SearchSuggestion.updateStyleSheet();
},
option
);
SearchSuggestion.$evt.offInputChangeEvtHandler.push(listenerHandler.off);
},
/**
* 移除输入框内容改变的监听
*/
removeInputChangeEvent() {
for (let index = 0; index < SearchSuggestion.$evt.offInputChangeEvtHandler.length; index++) {
const handler = SearchSuggestion.$evt.offInputChangeEvtHandler[index];
handler();
SearchSuggestion.$evt.offInputChangeEvtHandler.splice(index, 1);
index--;
}
},
/**
* 显示搜索建议框的事件
*/
showEvent() {
SearchSuggestion.updateStyleSheet();
if (config.toHideWithNotResult) {
if (SearchSuggestion.$data.isEmpty) {
SearchSuggestion.hide(true);
} else {
SearchSuggestion.show();
}
} else {
SearchSuggestion.show();
}
},
/**
* 设置显示搜索建议框的事件
*/
setShowEvent(option: AddEventListenerOptions = defaultListenerOption) {
/* 焦点|点击事件*/
if (config.followPosition === "target") {
popsDOMUtils.on([config.$target], ["focus", "click"], SearchSuggestion.showEvent, option);
} else if (config.followPosition === "input") {
popsDOMUtils.on([config.$inputTarget], ["focus", "click"], SearchSuggestion.showEvent, option);
} else if (config.followPosition === "inputCursor") {
popsDOMUtils.on([config.$inputTarget], ["focus", "click", "input"], SearchSuggestion.showEvent, option);
} else {
throw new Error("未知followPosition:" + config.followPosition);
}
},
/**
* 移除显示搜索建议框的事件
*/
removeShowEvent(option: AddEventListenerOptions = defaultListenerOption) {
/* 焦点|点击事件*/
popsDOMUtils.off([config.$target, config.$inputTarget], ["focus", "click"], SearchSuggestion.showEvent, option);
// 内容改变事件
popsDOMUtils.off([config.$inputTarget], ["input"], SearchSuggestion.showEvent, option);
},
/**
* 隐藏搜索建议框的事件
* @param event
*/
hideEvent(event: PointerEvent | MouseEvent) {
if (event.target instanceof Node) {
if ($shadowContainer.contains(event.target)) {
// 点击在shadow上的
return;
}
if (config.$target.contains(event.target)) {
// 点击在目标元素内
return;
}
if (config.$inputTarget.contains(event.target)) {
// 点击在目标input内
return;
}
SearchSuggestion.hide(true);
}
},
/**
* 设置隐藏搜索建议框的事件
*/
setHideEvent(option: AddEventListenerOptions = defaultListenerOption) {
// 全局点击事件
// 全局触摸屏点击事件
if (Array.isArray(SearchSuggestion.selfDocument)) {
SearchSuggestion.selfDocument.forEach(($checkParent) => {
popsDOMUtils.on($checkParent, ["click", "touchstart"], SearchSuggestion.hideEvent, option);
});
} else {
popsDOMUtils.on(SearchSuggestion.selfDocument, ["click", "touchstart"], SearchSuggestion.hideEvent, option);
}
},
/**
* 移除隐藏搜索建议框的事件
*/
removeHideEvent(option: AddEventListenerOptions = defaultListenerOption) {
if (Array.isArray(SearchSuggestion.selfDocument)) {
SearchSuggestion.selfDocument.forEach(($checkParent) => {
popsDOMUtils.off($checkParent, ["click", "touchstart"], SearchSuggestion.hideEvent, option);
});
} else {
popsDOMUtils.off(SearchSuggestion.selfDocument, ["click", "touchstart"], SearchSuggestion.hideEvent, option);
}
},
/**
* 设置所有监听,包括(input值改变、全局点击判断显示/隐藏建议框)
*/
setAllEvent(option: AddEventListenerOptions = defaultListenerOption) {
SearchSuggestion.setInputChangeEvent(option);
SearchSuggestion.setHideEvent(option);
SearchSuggestion.setShowEvent(option);
},
/**
* 移除所有监听
*/
removeAllEvent(option: AddEventListenerOptions = defaultListenerOption) {
SearchSuggestion.removeInputChangeEvent();
SearchSuggestion.removeHideEvent(option);
SearchSuggestion.removeShowEvent(option);
},
/**
* 获取删除按钮的html
*/
createItemDeleteIcon(size = 16, fill = "#bababa") {
const $svg = popsDOMUtils.parseTextToDOM(/*html*/ `
<svg class="pops-${popsType}-delete-icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" fill="${fill}">
<path d="M512 883.2A371.2 371.2 0 1 0 140.8 512 371.2 371.2 0 0 0 512 883.2z m0 64a435.2 435.2 0 1 1 435.2-435.2 435.2 435.2 0 0 1-435.2 435.2z"></path>
<path d="M557.056 512l122.368 122.368a31.744 31.744 0 1 1-45.056 45.056L512 557.056l-122.368 122.368a31.744 31.744 0 1 1-45.056-45.056L466.944 512 344.576 389.632a31.744 31.744 0 1 1 45.056-45.056L512 466.944l122.368-122.368a31.744 31.744 0 1 1 45.056 45.056z"></path>
</svg>
`);
return $svg;
},
/**
* 设置当前正在搜索中的提示
*/
setPromptsInSearch() {
const $isSearching = popsDOMUtils.createElement("li", {
className: `pops-${popsType}-search-suggestion-dropdown-searching-item`,
innerHTML: config.searchingTip,
});
SearchSuggestion.addItem($isSearching);
},
/**
* 移除正在搜索中的提示
*/
removePromptsInSearch() {
popsDOMUtils.remove(
SearchSuggestion.$el.$dropdownContainer.querySelector<HTMLLIElement>(
`li.pops-${popsType}-search-suggestion-dropdown-searching-item`
)
);
},
/**
* 更新搜索建议框的位置(top、left)
* 因为目标元素可能是动态隐藏的
* @param target 目标元素
* @param checkPositonAgain 是否在更新位置信息后检测更新位置信息,默认true
*/
changeHintULElementPosition(target = config.$target ?? config.$inputTarget, checkPositonAgain: boolean = true) {
let targetRect: DOMRect | null = null;
if (config.followPosition === "inputCursor") {
targetRect = popsDOMUtils.getTextBoundingRect(
config.$inputTarget,
config.$inputTarget.selectionStart || 0,
config.$inputTarget.selectionEnd || 0,
false
);
} else {
targetRect = config.isAbsolute ? popsDOMUtils.offset(target) : target.getBoundingClientRect();
}
if (targetRect == null) {
return;
}
// 文档最大高度
let documentHeight = document.documentElement.clientHeight;
if (config.isAbsolute) {
// 绝对定位
documentHeight = popsDOMUtils.height(document);
}
// 文档最大宽度
const documentWidth = popsDOMUtils.width(document);
// 箭头
const arrowHeight = config.useArrow ? popsDOMUtils.height(SearchSuggestion.$el.$arrow) : 0;
let position = config.position;
if (config.position === "auto") {
// 需目标高度+搜索建议框高度大于文档高度,则显示在上面
const targetBottom = targetRect.bottom;
// 容器整体的高度
const searchSuggestionContainerHeight =
popsDOMUtils.height(SearchSuggestion.$el.$dropdownWrapper) + arrowHeight;
if (targetBottom + searchSuggestionContainerHeight > documentHeight) {
// 在上面
position = "top";
} else {
// 在下面
position = "bottom";
}
}
if (position === "top") {
// 在上面
if (config.positionTopToReverse) {
// 自动翻转
SearchSuggestion.$el.root.setAttribute("data-top-reverse", "true");
}
if (config.useFoldAnimation) {
// 翻转折叠
SearchSuggestion.$el.root.setAttribute("data-popper-placement", "top");
}
const bottom = documentHeight - targetRect.top + config.topDistance + arrowHeight;
SearchSuggestion.$el.root.style.top = "";
SearchSuggestion.$el.root.style.bottom = bottom + "px";
} else if (position === "bottom") {
// 在下面
if (config.useFoldAnimation) {
SearchSuggestion.$el.root.setAttribute("data-popper-placement", "bottom-center");
}
const top = targetRect.height + targetRect.top + config.topDistance + arrowHeight;
SearchSuggestion.$el.root.removeAttribute("data-top-reverse");
SearchSuggestion.$el.root.style.bottom = "";
SearchSuggestion.$el.root.style.top = top + "px";
}
let left = targetRect.left;
const hintUIWidth = popsDOMUtils.width(SearchSuggestion.$el.$dropdownWrapper);
if (hintUIWidth > documentWidth) {
// 超出宽度
left = left + documentWidth - hintUIWidth;
}
SearchSuggestion.$el.root.style.left = left + "px";
// 如果更新前在下面的话且高度超出了屏幕
// 这时候会有滚动条,会造成位置偏移
// 更新后的位置却在上面,这时候的位置信息不对齐
// 需重新更新位置
// 此情况一般是config.position === "auto"
if (checkPositonAgain) {
SearchSuggestion.changeHintULElementPosition(target, !checkPositonAgain);
}
},
/**
* 更新搜索建议框的width
* 因为目标元素可能是动态隐藏的
* @param target 目标元素
*/
changeHintULElementWidth(target = config.$target ?? config.$inputTarget) {
const targetRect = target.getBoundingClientRect();
if (config.followTargetWidth) {
SearchSuggestion.$el.$dropdownWrapper.style.width = targetRect.width + "px";
} else {
SearchSuggestion.$el.$dropdownWrapper.style.width = config.width;
}
},
/**
* 动态更新CSS
*/
updateDynamicCSS() {
const cssText = SearchSuggestion.getDynamicCSS();
PopsSafeUtils.setSafeHTML(SearchSuggestion.$el.$dynamicCSS, cssText);
},
/**
* 数据项的数量改变时调用
*
* - 更新css
* - 更新建议框的宽度
* - 更新建议框的位置
*/
updateStyleSheet() {
// 更新z-index
SearchSuggestion.updateDynamicCSS();
// 更新宽度
SearchSuggestion.changeHintULElementWidth();
// 更新位置
SearchSuggestion.changeHintULElementPosition();
},
/**
* 添加搜索结果元素
* @param $item 项元素
*/
addItem($item: HTMLElement | DocumentFragment) {
SearchSuggestion.$el.$dropdownContainer.appendChild($item);
},
/**
* 更新页面显示的搜索结果
* @param updateData
*/
update(updateData: PopsSearchSuggestionData<T>[] = []) {
if (!Array.isArray(updateData)) {
throw new TypeError("传入的数据不是数组");
}
const data = updateData;
// 清空已有的搜索结果
if (data.length) {
SearchSuggestion.$data.isEmpty = false;
if (config.toHideWithNotResult) {
SearchSuggestion.show();
}
SearchSuggestion.clear(true);
// 添加进ul中
const fragment = document.createDocumentFragment();
data.forEach((item, index) => {
const $item = SearchSuggestion.createSearchItemLiElement(item, index);
SearchSuggestion.setSearchItemClickEvent($item);
SearchSuggestion.setSearchItemSelectEvent($item);
fragment.appendChild($item);
});
SearchSuggestion.addItem(fragment);
} else {
// 清空
SearchSuggestion.clear();
}
},
/**
* 清空当前的搜索结果并显示无结果
* @param [onlyClearView=false] 是否仅清空元素,默认false
*/
clear(onlyClearView: boolean = false) {
PopsSafeUtils.setSafeHTML(SearchSuggestion.$el.$dropdownContainer, "");
if (onlyClearView) {
return;
}
SearchSuggestion.$data.isEmpty = true;
let $noResult;
if (typeof config.toSearhNotResultHTML === "string") {
$noResult = popsDOMUtils.parseTextToDOM(config.toSearhNotResultHTML);
} else {
$noResult = config.toSearhNotResultHTML();
}
SearchSuggestion.addItem($noResult);
if (config.toHideWithNotResult) {
SearchSuggestion.hide();
}
},
/**
* 隐藏搜索建议框
* @param useAnimationToHide 是否使用动画隐藏
*/
hide(useAnimationToHide: boolean = false) {
if (config.useFoldAnimation) {
if (!useAnimationToHide) {
// 去掉动画
popsDOMUtils.removeClassName(SearchSuggestion.$el.root, "el-zoom-in-top-animation");
}
popsDOMUtils.addClassName(SearchSuggestion.$el.root, "el-zoom-in-top-animation");
popsDOMUtils.addClassName(SearchSuggestion.$el.root, "el-zoom-in-top-animation-hide");
popsDOMUtils.removeClassName(SearchSuggestion.$el.root, "el-zoom-in-top-animation-show");
} else {
SearchSuggestion.$el.root.style.display = "none";
}
},
/**
* 显示搜索建议框
*/
show() {
SearchSuggestion.$el.root.style.display = "";
if (config.useFoldAnimation) {
popsDOMUtils.addClassName(SearchSuggestion.$el.root, "el-zoom-in-top-animation");
popsDOMUtils.removeClassName(SearchSuggestion.$el.root, "el-zoom-in-top-animation-hide");
popsDOMUtils.addClassName(SearchSuggestion.$el.root, "el-zoom-in-top-animation-show");
}
},
};
return SearchSuggestion;
},
};