「MediaWiki:MobileUI.js」の版間の差分

編集の要約なし
編集の要約なし
タグ: モバイル編集 モバイルウェブ編集
編集の要約なし
タグ: モバイル編集 モバイルウェブ編集
3行目: 3行目:
/**
/**
  * @typedef MediaWiki
  * @typedef MediaWiki
  * @prop {readonly Map<string, any>} config
  * @prop {readonly Map<string, never>} config
  */
  */


39行目: 39行目:
  * @prop {string | string[]} [class]
  * @prop {string | string[]} [class]
  * @prop {MenuDataItem[]} items
  * @prop {MenuDataItem[]} items
*/
/**
* @typedef UserMenuItem
* @prop {string} text
* @prop {string} title
* @prop {readonly string[]} iconClass
  */
  */


44行目: 51行目:
  * @typedef MobileUI
  * @typedef MobileUI
  * @prop {MenuDataGroup[]} leftGroups
  * @prop {MenuDataGroup[]} leftGroups
  * @prop {MenuDataGroup[]} rightGroups
  * @prop {readonly UserMenuItem[]} userMenuItems
  */
  */


276行目: 283行目:


     /**
     /**
     * @returns {HTMLBodyElement | null}
     * @returns {HTMLUListElement | null}
    */
    static get authenticatedBody() {
        return /** @type {HTMLBodyElement} */ (
            window.document
                .getElementsByClassName("is-authenticated")
                .item(0)
        );
    }
 
    /**
    * @returns {HTMLFormElement}
    */
    static get headerElement() {
        return /** @type {HTMLFormElement} */ (
            window.document
                .getElementsByClassName("header")
                .item(0)
        );
    }
 
    /**
    * @returns {HTMLAnchorElement | null}
    */
    static get userButton() {
        return /** @type {HTMLAnchorElement} */ (
            MobileUIBuilder
                .headerElement
                .getElementsByClassName("user-button")
                .item(0)
        );
    }
 
    /**
    * @returns {HTMLButtonElement | null}
     */
     */
     static get rightMenuButtonElement() {
     static get userMenuElement() {
         return /** @type {HTMLButtonElement} */ (
         return window
             window.document
             .document
                .getElementById(MobileUIBuilder.rightMenuButtonID)
            .querySelector(".minerva-user-menu > ul.toggle-list__list");
        );
     }
     }


334行目: 306行目:


     /**
     /**
     * @returns {this}
    * @param {UserMenuItem} item
     * @returns {HTMLLIElement}
     */
     */
     assembleRightMenuButton() {
     createUserMenuItemElement(item) {
         if (!this.wgUserId) return this;
         const li = window.document.createElement("li");
         if (!MobileUIBuilder.authenticatedBody) return this;
         li.classList.add("toggle-list-item");
        if (MobileUIBuilder.rightMenuButtonElement) return this;


         const userButton = MobileUIBuilder.userButton;
         const a = window.document.createElement("a");
         if (userButton && !userButton.text.trim()) {
         a.classList.add("toggle-list-item__anchor");
            userButton.parentElement.classList.add(MobileUIBuilder.hiddenClass);
        a.href = [this.wgScriptPath, item.title].join("/");
         }
         li.append(a);


         const button = window.document.createElement("button");
         const outerSpan = window.document.createElement("span");
         button.id = MobileUIBuilder.rightMenuButtonID;
         outerSpan.classList.add("toggle-list-item__icon", "mw-ui-icon", "mw-ui-icon-before", ...item.iconClass);
        button.classList.add(
         a.append(outerSpan);
            "mw-ui-icon",
            "mw-ui-icon-element",
            "mobile-ui-icon-puzzle",
         );


         button.addEventListener("click", function (event) {
         const innerSpan = window.document.createElement("span");
            event.preventDefault();
        innerSpan.classList.add("toggle-list-item__label");
            event.stopPropagation();
        innerSpan.textContent = item.text;
            this.blur();
        outerSpan.append(innerSpan);


            const body = MobileUIBuilder.authenticatedBody;
        return li;
            if (!body) return;
    }
            if (body.classList.contains(MobileUIBuilder.navigationEnabledClass)) return;


            body.classList.add(
    /**
                MobileUIBuilder.navigationEnabledClass,
    * @param {readonly UserMenuItem[]} items
                "secondary-navigation-enabled",
    * @returns {this}
            );
    */
         });
    extendUserMenu(items) {
         const menu = MobileUIBuilder.userMenuElement;


         const buttonWrapper = window.document.createElement("div");
         if (menu) {
        buttonWrapper.append(button);
            const lastItem = menu.lastChild;
        MobileUIBuilder.headerElement.append(buttonWrapper);
            if (lastItem) {
                for (const item of items) {
                    const li = this.createUserMenuItemElement(item);
                    menu.insertBefore(li, lastItem);
                }
            }
        }


         return this;
         return this;
382行目: 356行目:
         return this
         return this
             .setNotArticleClass()
             .setNotArticleClass()
             .assembleRightMenuButton();
             ;
     }
     }


     /**
     /**
     * @param {MobileUI} mobileUI
     * @param {MobileUI} mobileUI
    * @param {HTMLElement} leftMenuContainer
     * @param {HTMLDivElement} leftMenu
     * @param {HTMLDivElement} leftMenu
     * @returns {this}
     * @returns {this}
     */
     */
     modify(mobileUI, leftMenuContainer, leftMenu) {
     modify(mobileUI, leftMenu) {
         const firstGroup = leftMenu.getElementsByTagName("ul").item(0);
         const firstGroup = leftMenu.getElementsByTagName("ul").item(0);
         if (firstGroup) {
         if (firstGroup) {
399行目: 372行目:
         const customLeftGroups = this.menuDataGroups(mobileUI.leftGroups);
         const customLeftGroups = this.menuDataGroups(mobileUI.leftGroups);
         leftMenu.prepend(...customLeftGroups);
         leftMenu.prepend(...customLeftGroups);
        const viewport = leftMenuContainer.parentElement;
        const rightMenuContainer = window.document.createElement("nav");
        rightMenuContainer.id = "mobile-ui-page-right";
        rightMenuContainer.classList.add(...leftMenuContainer.classList);
        viewport.insertBefore(rightMenuContainer, leftMenuContainer);
        const rightMenu = window.document.createElement("div");
        rightMenu.classList.add(...leftMenu.classList);
        rightMenuContainer.appendChild(rightMenu);
        const rightGroups = this.menuDataGroups(mobileUI.rightGroups);
        rightMenu.append(...rightGroups);


         return this;
         return this;
422行目: 381行目:
     */
     */
     build(mobileUI) {
     build(mobileUI) {
        if (mobileUI.userMenuItems) {
            this.extendUserMenu(mobileUI.userMenuItems);
        }
         const leftMenuContainer = window.document.getElementById("mw-mf-page-left");
         const leftMenuContainer = window.document.getElementById("mw-mf-page-left");


429行目: 392行目:


         if (MobileUIBuilder.leftMenuElement) {
         if (MobileUIBuilder.leftMenuElement) {
             this.modify(mobileUI, leftMenuContainer, leftMenu);
             this.modify(mobileUI, leftMenu);
             return this;
             return this;
         }
         }
440行目: 403行目:
                     if (classList && classList.contains(MobileUIBuilder.menuClass)) {
                     if (classList && classList.contains(MobileUIBuilder.menuClass)) {
                         observer.disconnect();
                         observer.disconnect();
                         this.modify(mobileUI, leftMenuContainer, maybeLeftMenu);
                         this.modify(mobileUI, maybeLeftMenu);
                         return;
                         return;
                     }
                     }