UNPKG

omii-ui

Version:

使用OMII开发的WebComponents库,样式采用bootstrap。可以作为标准的esmodule使用

156 lines (147 loc) 3.99 kB
const { h, classNames, createRef } = omii; import { computePosition, flip, shift, offset } from "@floating-ui/dom"; import uiBase from "../uiBase"; import { getCSSStyleSheets } from "../css"; import css from "./index.scss"; //当前显示的下拉关上 let $visibleDropDown = null; document.addEventListener("click", (evt) => { if ($visibleDropDown) $visibleDropDown.hide(); }); export default class extends uiBase { static css = [ () => getCSSStyleSheets("reboot", "utilities", "transitions", "dropdown"), css, ]; static defaultProps = { /** * // | 'top' // | 'top-start' // | 'top-end' // | 'right' // | 'right-start' // | 'right-end' // | 'bottom' // | 'bottom-start' // | 'bottom-end' // | 'left' // | 'left-start' // | 'left-end'; */ placement: "bottom", strategy: "absolute", trigger: "click", offset: 0, menu: null, reference: null, //哪個對象? }; static propTypes = { placement: String, strategy: String, trigger: String, offset: Number, }; #menuRef = createRef(); get $menu() { return this.#menuRef.current; } #slotRef = createRef(); get #$slot() { return this.#slotRef.current; } get #$reference() { let assignedElements = this.#$slot.assignedElements(); if (assignedElements.length === 0) { let { reference } = this.$props; if (typeof reference == "string") { return this.getRootNode()?.querySelector(reference); } else { //支持使用creatRef绑定的对象 return reference?.current ?? reference; } } else { return assignedElements.at(0); } } setPosition() { let { placement, offset: _offset, strategy } = this.$props; computePosition(this.#$reference, this.$menu, { strategy, placement, middleware: [flip(), shift(), offset(_offset)], }).then(({ x, y, placement }) => { Object.assign(this.$menu.style, { left: `${x}px`, top: `${y}px`, }); }); } #visible = false; show() { this.#visible = true; this.#$slot.focus(); this.#$slot.setAttribute("aria-expanded", true); this.#$slot.classList.add("show"); Object.assign(this.$menu.style, { display: "block", position: this.$props.strategy, }); $visibleDropDown = this; this.setPosition(); } hide() { this.#visible = false; this.#$slot.classList.remove("show"); this.#$slot.setAttribute("aria-expanded", "false"); $visibleDropDown = null; Object.assign(this.$menu.style, { display: "none", }); } toggle() { if (this.#visible) this.hide(); else { this.show(); } } installed() { const { trigger } = this.$props; if (trigger == "click") { this.#$reference.addEventListener("click", (evt) => { if ($visibleDropDown && $visibleDropDown != this) $visibleDropDown.hide(); evt.preventDefault(); evt.stopImmediatePropagation(); this.toggle(); }); } else if (trigger == "focus") { this.#$reference.addEventListener("focus", (evt) => { if ($visibleDropDown && $visibleDropDown != this) $visibleDropDown.hide(); evt.preventDefault(); evt.stopImmediatePropagation(); this.show(); }); this.#$reference.addEventListener("blur", (evt) => { evt.preventDefault(); evt.stopImmediatePropagation(); this.hide(); }); } this.addEventListener("click", (evt) => { evt.stopImmediatePropagation(); }); } render(props) { let { menu } = this.$props; return ( <> <slot ref={this.#slotRef}></slot> <slot name="menu" ref={this.#menuRef}> {menu} </slot> </> ); } }