Mercurial
diff .cms/js/menu.js @ 0:78edf6b517a0 draft
24.10
author | Coffee CMS <info@coffee-cms.ru> |
---|---|
date | Fri, 11 Oct 2024 22:40:23 +0000 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.cms/js/menu.js Fri Oct 11 22:40:23 2024 +0000 @@ -0,0 +1,426 @@ +document.addEventListener( "DOMContentLoaded", function( event ) { + + function _( str ) { + return __( str, "menu.mod.php" ); + } + + function select3( selects ) { + selects.forEach( function( select ) { + // именно keyup чтобы не пересекалось с input + select.querySelector( "input" ).addEventListener( "keyup", function( e ) { + api( { fn: "get_search_pages_list", search: this.value }, function( r ) { + if ( r.html ) { + select.querySelector( ".list-search" ).innerHTML = r.html; + select.querySelectorAll( ".list-search li" ).forEach( function( li ) { + li.addEventListener( "click", select3_li ); + } ); + } + } ); + } ); + // prevent close dropdown when click on input + select.querySelector( "input" ).addEventListener( "click", function( e ) { + e.stopPropagation(); + } ); + // show/hide dropdown list + select.querySelector( ".field-select" ).addEventListener( "click", function ( event ) { + event.stopPropagation(); + this.parentElement.classList.toggle( "open" ); + let list = event.currentTarget.nextElementSibling; + + // это можно убрать если переставить стили на родителя + list.classList.toggle( "open" ); + + if ( list.classList.contains( "open" ) ) { + let input = select.querySelector( "input" ); + input.focus(); + input.dispatchEvent( new Event( "keyup" ) ); + } + } ); + } ); + } + + // click selected item + function select3_li( e ) { + let id = this.getAttribute( "data-id" ); + let url = this.getAttribute( "data-url" ); + let title = this.innerText; + let input_title = this.closest( ".select-grid" ).querySelector( ".field-select" ); + let old_id = input_title.getAttribute( "data-old-id" ); + input_title.querySelector( ".value" ).innerText = title; + input_title.setAttribute( "data-id", id ); + // поменять url в поле ввода + let input_url = this.closest( ".menu-prop" ).querySelector( "input[name='url']" ); + input_url.value = url; + // поменять заголовок и ссылку вверху + let a = this.closest( ".item" ).querySelector( ":scope > a" ); + a.setAttribute( "href", cms.base_path + url ); + let replaced_title = this.closest( ".menu-prop" ).querySelector( `input[name="title"]` ).value; + if ( replaced_title ) title = replaced_title; + a.innerText = title; + if ( id === "0" ) { + input_url.removeAttribute( "disabled" ); + } else { + input_url.setAttribute( "disabled", true ); + } + // remove li except data-id=0 + Array.from( this.parentElement.children ).forEach( function( li ) { + if ( li.getAttribute( "data-id" ) !== "0" ) { + li.remove(); + } + } ); + // close dropdown list + input_title.click(); + // изменение + if ( old_id != id ) { + input_title.dispatchEvent( new Event( "input" ) ); + } + } + + api( { fn: "get_menu_items" }, set_menu_items ); + + function set_menu_items( r ) { + + document.querySelector( "#menu .menu-grid" ).innerHTML = r.list; + + // set parents for each menu item + document.querySelectorAll( "#menu [data-parent]" ).forEach( function( el ) { + el.nextElementSibling.insertAdjacentHTML( "beforeend", r.parents ); + let pid = el.getAttribute( "data-parent" ); + let parent = el.nextElementSibling.querySelector( `[value="${pid}"]` ).innerText.trim(); + el.querySelector( ".value" ).innerText = parent; + // remove self + let self = el.closest( "[data-item]" ).getAttribute( "data-item" ); + self = el.nextElementSibling.querySelector( `[value="${self}"]` ); + if ( self ) { + self.remove(); + } + } ); + + // selects + let selects = document.querySelectorAll( "#menu .select-grid" ); + select3( selects ); + + // Select + document.querySelectorAll( "#menu .area-select-grid .field-select-menu, #menu .parent-select-grid .field-select" ).forEach( function( select ) { + select.addEventListener( "click", function( e ) { + e.stopPropagation(); + this.parentElement.classList.toggle( "open" ); + + // это можно убрать если переставить стили на родителя + select.nextElementSibling.classList.toggle( "open" ); + } ); + } ); + // Option for menu + document.querySelectorAll( "#menu .area-select-grid .field-options .option" ).forEach( function( option ) { + option.addEventListener( "click", function( e ) { + let input = this.closest( ".area-select-grid" ).querySelector( ".field-select-menu" ); + let old_val = input.getAttribute( "data-menu-area" ); + let new_val = this.getAttribute( "value" ); + if ( old_val != new_val ) { + input.querySelector( ".value" ).innerText = this.innerText; + input.setAttribute( "data-menu-area", new_val ); + // Событие изменения + let event = new Event( "input" ); + input.dispatchEvent( event ); + } + //e.stopPropagation(); убираем чтобы закрылось автоматически + } ); + } ); + // Option for item + document.querySelectorAll( "#menu .parent-select-grid .field-options .option" ).forEach( function( option ) { + option.addEventListener( "click", parent_li_click ); + } ); + function parent_li_click( e ) { + let input = this.closest( ".parent-select-grid" ).querySelector( ".field-select" ); + let old_val = input.getAttribute( "data-parent" ); + let new_val = this.getAttribute( "value" ); + if ( old_val != new_val ) { + input.querySelector( ".value" ).innerText = this.innerText.trim(); + input.setAttribute( "data-parent", new_val ); + // Событие изменения + let event = new Event( "input" ); + input.dispatchEvent( event ); + } + //e.stopPropagation(); убираем чтобы закрылось автоматически + } + + // Toggle Menu Properties + let prop_buttons = document.querySelectorAll( "#menu .menu-buttons .prop" ); + prop_buttons.forEach( function( button ) { + button.addEventListener( "click", function( e ) { + e.stopPropagation(); + this.closest( "[data-item]" ).classList.toggle( "open" ); + } ); + } ); + + // Отлавливать изменения полей в меню чтобы показать Сохранить + document.querySelectorAll( "#menu .menu" ).forEach( function( menu ) { + menu.querySelectorAll( "input, .field-select-menu" ).forEach( function( input ) { + input.addEventListener( "input", function( event ) { + let menu = this.closest( ".menu" ); + menu.classList.add( "changed" ); + } ); + } ); + } ); + + // Отлавливать изменения полей в пунктах чтобы показать Сохранить + document.querySelectorAll( "#menu .item" ).forEach( function( item ) { + item.querySelectorAll( "input, .field-select" ).forEach( function( input ) { + input.addEventListener( "input", function( event ) { + let item = this.closest( ".item" ); + item.classList.add( "changed" ); + } ); + } ); + } ); + + // Save Properties + document.querySelectorAll( "#menu .menu-buttons .save" ).forEach( function( button ) { + button.addEventListener( "click", function( e ) { + e.stopPropagation(); + + let item = this.closest( "[data-item]" ); + let mid = item.getAttribute( "data-item" ); + + let area = item.querySelector( "[data-menu-area]" ); + if ( area ) { + area = area.getAttribute( "data-menu-area" ); + } + + let tag_title = item.querySelector( "[name='tag_title']" ); + if ( tag_title ) { + tag_title = tag_title.value; + } + + let url = item.querySelector( "[name='url']" ); + if ( url ) { + url = url.value; + } + + let id = item.querySelector( "[name='id']" ); + if ( id ) { + id = id.getAttribute( "data-id" ); + } + + let pid_el = item.querySelector( "[data-parent]" ); + let pid,old_pid; + if ( pid_el ) { + pid = pid_el.getAttribute( "data-parent" ); + old_pid = pid_el.getAttribute( "data-old-parent" ); + } + + let target = item.querySelector( "[name='targetblank']" ); + if ( target ) { + target = target.checked; + } + + let old_sort_el = item.querySelector( "[name='sort']" ); + let old_sort = old_sort_el.getAttribute( "data-sort" ); + + let data = { + fn: "save_menu_item", + mid: mid, + title: item.querySelector( "[name='title']" ).value, + tag_title: tag_title, + url: url, + id: id, + pid: pid, + old_pid: old_pid, + classes: item.querySelector( "[name='classes']" ).value, + sort: item.querySelector( "[name='sort']" ).value, + area: area, + target: target + } + document.querySelectorAll( `#menu [data-item]` ).forEach( function( el ) { + el.classList.remove( "last-edited" ); + } ); + api( data, function( r ) { + if ( r.ok == "false" ) { + notify( r.info_text, r.info_class, r.info_time ); + } + if ( r.ok == "true" ) { + item.classList.remove( "changed" ); + let item_with_childs = item.parentElement; + if ( r.list ) { + set_menu_items( r ); + } else if ( old_sort != data.sort ) { + // пересортировка имеет смысл если не было замены всего списка + // обновить сортировку для сравнения + old_sort_el.setAttribute( "data-sort", data.sort ); + let grid; + if ( data.area === null ) { + grid = item.closest( ".items-grid" ); + } else { + grid = item.closest( ".menu-grid" ); + } + let before = null; + for ( let n = 0; n < grid.children.length; n++ ) { + let node = grid.children[n]; + if ( node != item_with_childs ) { + let sort = node.querySelector( '[data-item] [name="sort"]' ).value; + if ( +data.sort < +sort ) { + before = node; + break; + } + } + }; + grid.insertBefore( item_with_childs, before ); + } + + // Замена title + let title; + if ( data.title != "" ) + title = data.title; + else + title = item.querySelector( ".select-grid .value" ).innerText; + item.querySelector( ":scope > a" ).innerText = title; + + // обновить родителей у подпунктов + // поскольку мог измениться title + // и только у строго дочерних, которых может и не быть + let childs_grid = item.nextElementSibling; + if ( childs_grid ) { + let childs_selector = childs_grid.querySelectorAll( ":scope > div > .item .parent-select-grid" ); + + if ( childs_selector ) childs_selector.forEach( function( grid ) { + let input = grid.querySelector( "[data-parent]" ); + let pid = input.getAttribute( "data-parent" ); + let options = grid.querySelector( ":scope > .field-options" ); + options.innerHTML = r.parents; + options.querySelectorAll( ".option" ).forEach( function( option ) { + option.addEventListener( "click", parent_li_click ); + } ); + // тайтл вычисляется выше + input.querySelector( ".value" ).innerText = title; + } ); + } + + // Last Edited Marker + setTimeout( function() { + document.querySelector( `#menu [data-item="${data.mid}"]` ).classList.add( "last-edited" ); + }, 200 ); + } + } ); + } ); + } ); + + // Delete Menu or Item + document.querySelectorAll( "#menu .menu-buttons .del" ).forEach( function( button ) { + button.addEventListener( "click", function( e ) { + e.stopPropagation(); + clear_highlite(); + let item = button.closest( ".item,.menu" ).parentNode; + item.classList.add( "will-be-deleted" ); + setTimeout( function() { + if ( ! confirm( __( "confirm_delete", "menu.mod.php" ) ) ) { + item.classList.remove( "will-be-deleted" ); + return; + } + let data = { + fn: "del_menu_item", + mid: button.closest( "[data-item]" ).getAttribute( "data-item" ) + }; + api( data, function( r ) { + if ( r.info_text ) { + notify( r.info_text, r.info_class, r.info_time ); + if ( r.info_class == "info-success" ) { + set_menu_items( r ); + } + } + } ); + }, 20 ); + } ); + } ); + + // Create Item + document.querySelectorAll( "#menu .main-main .append" ).forEach( function( button ) { + button.addEventListener( "click", modMenuCreate ); + } ); + + // Линия к родителю + let items = document.querySelectorAll( "#menu .item" ); + items.forEach( function( el ) { + el.addEventListener( "click", function( e ) { + // Если опускание мыши было не на нас, то игнорим + if ( mouse_down_el != this ) return; + mouse_down_el = null; + let highlited = this.classList.contains( "highlite-children" ); + clear_highlite(); + // Назначить классы + if ( ! highlited ) { + this.classList.add( "highlite-children" ); + this.closest( ".items-grid" ).classList.add( "highlite-parent" );; + } + } ); + } ); + // Опускание мыши + // Возможна ситуация когда нажатие мыши было на кнопке Свойства, + // а отпускание на плашке. В этом случае может появиться линия к родителю. + // Поэтому нужно записывать что опускание было на плашке или панельке кнопок. + let mouse_down_el; // элемент на котором было опускание мыши + items.forEach( function( el ) { + el.addEventListener( "mousedown", function( e ) { + // чтобы проигнорировать клики по дочерним + if ( e.target == this ) + mouse_down_el = this; + } ); + // див с кнопками можно тыкать + let mb = el.querySelector( ".menu-buttons" ); + if ( mb ) mb.addEventListener( "mousedown", function( e ) { + // чтобы проигнорировать клики по дочерним + if ( e.target == this ) + mouse_down_el = this.parentElement; + } ); + } ); + // Сбросить классы + function clear_highlite() { + document.querySelectorAll( "#menu .items-grid.highlite-parent" ).forEach( function( el ) { + el.classList.remove( "highlite-parent" ); + } ); + document.querySelectorAll( "#menu .item.highlite-children" ).forEach( function( el ) { + el.classList.remove( "highlite-children" ); + } ); + } + + } + + // Select + // Закрытие выпадающих списков при кликах вне их, а так же по ним + document.body.addEventListener( "click", function( e ) { + // это можно убрать если переставить стили на родителя + document.querySelectorAll( "#menu .field-options" ).forEach( function( list ) { + list.classList.remove( "open" ); + } ); + + document.querySelectorAll( "#menu .area-select-grid, #menu .select-grid, #menu .parent-select-grid" ).forEach( function( list ) { + list.classList.remove( "open" ); + } ); + } ); + + // Создание меню или пункта меню + function modMenuCreate( e ) { + e.stopPropagation(); + let pid = this.closest( "[data-item]" ).getAttribute( "data-item" ); + api( { fn : "create_menu_item", pid : pid }, function( r ) { + if ( r.info_text ) { + notify( r.info_text, r.info_class, r.info_time ); + if ( r.info_class == "info-success" ) { + set_menu_items( r ); + let item = document.querySelector( `#menu [data-item="${r.mid}"]` ); + item.classList.add( "open" ); + item.classList.add( "last-edited" ); + // Докрутить к новому элементу + item.scrollIntoView( { behavior: "smooth", block: "center" } ); + } + } + } ); + } + + // Create Menu + document.querySelector( "#menu .main-footer .create" ).addEventListener( "click", modMenuCreate ); + + // update page used in menu + document.body.addEventListener( "update_menu", function( e ) { + api( { fn: "get_menu_items" }, set_menu_items ); + } ); + +} );