Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

如何设计一个组件 #49

Open
ChelesteWang opened this issue Mar 13, 2022 · 1 comment
Open

如何设计一个组件 #49

ChelesteWang opened this issue Mar 13, 2022 · 1 comment

Comments

@ChelesteWang
Copy link
Owner

  1. 根据组件结构编写样式及布局
  2. 根据组件逻辑确定组件行为
  3. 根据组件交互,绑定事件
  4. 数据与逻辑分离,实现可复用
@ChelesteWang
Copy link
Owner Author

/* 二级级联选择器 */

class SelectorDasen {
    // 构造函数
    constructor(root, data, placeholder, size) {
        let tempDom;

        // 创建根标签
        let selectorRoot = document.createElement("div");
        selectorRoot.classList.add("dasen-selector");
        this.selectorRoot = selectorRoot;

        // 创建选择框标签
        tempDom = document.createElement("div");
        tempDom.classList.add("pointer");
        tempDom.addEventListener("click", () => SelectorDasen.switchActive(this.selectorRoot));
        let selectedLabel = document.createElement("label");
        this.placeholder = placeholder ? placeholder : "请选择";
        selectedLabel.innerHTML = this.placeholder;
        tempDom.appendChild(selectedLabel);
        selectorRoot.appendChild(tempDom);
        this.selectedLabel = selectedLabel;

        // 创建选项框标签
        let selectorOptions = document.createElement("div");
        selectorOptions.classList.add("options");
        selectorOptions.classList.add(size ? size : "middle");
        selectorRoot.appendChild(selectorOptions);
        this.selectorOptions = selectorOptions;

        // 创建一级选项标签
        let leftDom = document.createElement("div");
        leftDom.classList.add("left");
        selectorOptions.appendChild(leftDom);

        // 创建二级选项标签
        let rightDom = document.createElement("div");
        rightDom.classList.add("right");
        selectorOptions.appendChild(rightDom);

        // 创建关闭按钮
        let cancelBtn = document.createElement("div");
        cancelBtn.classList.add("cancel");
        cancelBtn.addEventListener("click", () => this.close());
        selectorOptions.appendChild(cancelBtn);

        // 创建每个一级选项及其下级内容
        this.supItems = [];
        this.subItems = [];
        let cnt = 0;
        for (const key in data) {
            let superTag = document.createElement("div");
            superTag.innerText = key;
            superTag.setAttribute("data-index", cnt.toString());
            superTag.addEventListener("click", (e) => SelectorDasen.tagClick(e, this));
            let subTagCon = document.createElement("div");
            subTagCon.classList.add("right-con");
            this.supItems.push(superTag);
            this.subItems.push({
                con: subTagCon,
                items: []
            });
            for (const index in data[key]) {
                let subTag = document.createElement("div");
                subTag.innerText = data[key][index];
                subTag.setAttribute("data-index", cnt.toString() + "-" + index.toString());
                subTag.addEventListener("click", (e) => SelectorDasen.tagClick(e, this));
                this.subItems[cnt].items.push(subTag);
                subTagCon.appendChild(subTag);
            }
            this.supItems[0].classList.add("active");
            this.subItems[0].con.classList.add("active");
            leftDom.appendChild(superTag);
            rightDom.appendChild(subTagCon);
            cnt++;
        }
        this.lastSupItemId = "0";
        this.lastSubItemId = "-";
        this.selectedId = "";
        this.selectedValue = "";

        // 挂载到页面
        document.getElementById(root).appendChild(selectorRoot);
        this.documentRootId = root;
    }
    // 原型方法
    select(id, value) {
        // 当用户选择了某一项时调用
        this.lastSubItemId = id;
        this.selectedValue = value;
        this.selectedId = id;
        this.selectedLabel.innerText = value;
        this.close();
    }
    resetScroll() {
        // 重置选择面板为当前选择的项
        let selectid = this.selectedId.split("-")[0];
        if (this.lastSupItemId !== selectid && selectid) {
            SelectorDasen.switchActive(this.supItems[this.lastSupItemId], this.supItems[selectid]);
            SelectorDasen.switchActive(this.subItems[this.lastSupItemId].con, this.subItems[selectid].con);
            this.lastSupItemId = selectid;
        }
        if (selectid) {
            this.selectorOptions.firstChild.scrollTop = this.supItems[selectid].offsetTop - 5;
        }
    }
    open() {
        // public: 打开选择面板
        this.selectorRoot.classList.add("active");
    }
    close() {
        // public: 关闭选择面板
        this.selectorRoot.classList.remove("active");
        this.resetScroll();
    }
    getValue() {
        // public: 获取当前选择的值
        return this.selectedValue;
    }
    getTag() {
        // public: 获取当前选择的值所属的一级标签
        let selectid = this.selectedId.split("-")[0];
        if (selectid) {
            return this.supItems[selectid].innerText;
        } else {
            return "";
        }
    }
    reset() {
        // public: 重置选择值
        if (this.selectedId) {
            let selectid = this.selectedId.split("-");
            SelectorDasen.switchActive(this.supItems[0], this.supItems[selectid[0]]);
            SelectorDasen.switchActive(this.subItems[0].con, this.subItems[selectid[0]].con);
            SelectorDasen.switchActive(this.subItems[selectid[0]].items[selectid[1]]);
            this.lastSupItemId = "0";
            this.lastSubItemId = "-";
            this.selectedId = "";
            this.selectedValue = "";
            this.selectedLabel.innerText = this.placeholder;
            this.selectorOptions.firstChild.scrollTop = 0;
        }
    }
    // 静态方法
    static tagClick(e, selector) {
        // 标签点击事件处理函数
        let supid = e.target.getAttribute("data-index");
        if (supid.indexOf("-") >= 0) {
            // 当点击的是二级标签
            if (selector.lastSubItemId !== supid) {
                let curid = supid;
                let subid = supid.split("-");
                let lastid = selector.lastSubItemId.split("-");
                supid = parseInt(subid[0]);
                subid = parseInt(subid[1]);
                if (lastid[0]) {
                    SelectorDasen.switchActive(
                        selector.subItems[supid].items[subid],
                        selector.subItems[parseInt(lastid[0])].items[parseInt(lastid[1])]
                    );
                } else {
                    SelectorDasen.switchActive(
                        selector.subItems[supid].items[subid]
                    );
                }
                selector.select(curid, e.target.innerText);
            }
        } else {
            // 当点击的是一级标签
            if (selector.lastSupItemId !== supid) {
                supid = parseInt(supid);
                SelectorDasen.switchActive(selector.supItems[selector.lastSupItemId], selector.supItems[supid]);
                SelectorDasen.switchActive(selector.subItems[selector.lastSupItemId].con, selector.subItems[supid].con);
                selector.lastSupItemId = supid;
            }
        }
    }
    static switchActive(dom, dom2) {
        // 切换 DOM 的 active 类名
        if (dom.classList.contains("active")) {
            dom.classList.remove("active");
        } else {
            dom.classList.add("active");
        }
        if (dom2) {
            SelectorDasen.switchActive(dom2);
        }
    }
}

// 代理,使得组件的属性和方法成为只读的,无法修改,修改静默失败
class Selector {
    constructor(root, data, placeholder, size) {
        let rawSelector = new SelectorDasen(root, data, placeholder, size);
        let proxy = new Proxy(rawSelector, {
            get(t, p) {
                let allowFun = ["open", "close", "getValue", "getTag", "reset"];
                if (allowFun.indexOf(p) >= 0) {
                    return t[p].bind(t);
                }
                return undefined;
            },
            set() {
                return false;
            }
        });
        return proxy;
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant