katalyst-navigation 2.0.0 → 2.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/assets/builds/katalyst/navigation.esm.js +14 -6
- data/app/assets/builds/katalyst/navigation.js +14 -6
- data/app/assets/builds/katalyst/navigation.min.js +1 -1
- data/app/assets/builds/katalyst/navigation.min.js.map +1 -1
- data/app/components/katalyst/navigation/editor/new_items_component.html.erb +7 -7
- data/app/components/katalyst/navigation/editor/status_bar_component.rb +1 -1
- data/app/javascript/navigation/editor/item_editor_controller.js +8 -3
- data/app/javascript/navigation/editor/new_items_controller.js +8 -3
- data/app/views/katalyst/navigation/items/edit.html.erb +6 -7
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2b96b9a427a224910ad753e84158ca628519e0c1f0ab24b6a43b9c4662876570
|
4
|
+
data.tar.gz: 24134aa48c7d42f25b61e6c4cfc48c81dbfad06e031062ec2dffeda516531599
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 02c91e663245f3346894472feacd6f439caafe45fe68886104640cefcaf4e93748a6330953b93300f0042f46ec5e4a80761e50e04c9371fa066e40a7fa1b3b9e
|
7
|
+
data.tar.gz: 6a0f14085da0103e1afa38a0c4876fb6ea9282fd7a4bd9cbfe500de1fe7343b774d9d296bb9fe0a2bb6b5baee11b614f121327e007edf00d14afaa144a97af07
|
@@ -755,6 +755,10 @@ class ItemEditorController extends Controller {
|
|
755
755
|
this.element.removeEventListener("turbo:submit-end", this.onSubmit);
|
756
756
|
}
|
757
757
|
|
758
|
+
click(e) {
|
759
|
+
if (e.target.tagName === "DIALOG") this.dismiss();
|
760
|
+
}
|
761
|
+
|
758
762
|
dismiss() {
|
759
763
|
if (!this.dialogTarget) return;
|
760
764
|
if (!this.dialogTarget.open) this.dialogTarget.close();
|
@@ -772,15 +776,16 @@ class ItemEditorController extends Controller {
|
|
772
776
|
}
|
773
777
|
|
774
778
|
onSubmit = (event) => {
|
775
|
-
if (
|
779
|
+
if (
|
780
|
+
event.detail.success &&
|
781
|
+
"closeDialog" in event.detail.formSubmission?.submitter?.dataset
|
782
|
+
) {
|
776
783
|
this.dialogTarget.close();
|
777
784
|
this.element.removeAttribute("src");
|
778
785
|
this.dialogTarget.remove();
|
779
786
|
}
|
780
787
|
};
|
781
788
|
|
782
|
-
noop() {}
|
783
|
-
|
784
789
|
#removeTargetItem() {
|
785
790
|
const el = document.getElementById(this.dialogTarget.dataset.itemId);
|
786
791
|
const item = new Item(el.closest("[data-navigation-item]"));
|
@@ -922,6 +927,10 @@ class NewItemsController extends Controller {
|
|
922
927
|
delete this.currentItem;
|
923
928
|
}
|
924
929
|
|
930
|
+
click(e) {
|
931
|
+
if (e.target.tagName === "DIALOG") this.close(e);
|
932
|
+
}
|
933
|
+
|
925
934
|
open(e) {
|
926
935
|
e.preventDefault();
|
927
936
|
this.dialog.showModal();
|
@@ -932,20 +941,19 @@ class NewItemsController extends Controller {
|
|
932
941
|
this.dialog.close();
|
933
942
|
}
|
934
943
|
|
935
|
-
noop(e) {}
|
936
|
-
|
937
944
|
/**
|
938
945
|
* Add the selected item to the DOM at the current position or the end of the list.
|
939
946
|
*/
|
940
947
|
add(e) {
|
941
948
|
e.preventDefault();
|
942
949
|
|
943
|
-
const template = e.target.querySelector("template");
|
950
|
+
const template = e.target.closest("li").querySelector("template");
|
944
951
|
const item = template.content.querySelector("li").cloneNode(true);
|
945
952
|
const target = this.currentItem;
|
946
953
|
|
947
954
|
if (target) {
|
948
955
|
target.insertAdjacentElement("beforebegin", item);
|
956
|
+
new Item(item).depth = new Item(target).depth;
|
949
957
|
} else {
|
950
958
|
this.list.insertAdjacentElement("beforeend", item);
|
951
959
|
}
|
@@ -755,6 +755,10 @@ class ItemEditorController extends Controller {
|
|
755
755
|
this.element.removeEventListener("turbo:submit-end", this.onSubmit);
|
756
756
|
}
|
757
757
|
|
758
|
+
click(e) {
|
759
|
+
if (e.target.tagName === "DIALOG") this.dismiss();
|
760
|
+
}
|
761
|
+
|
758
762
|
dismiss() {
|
759
763
|
if (!this.dialogTarget) return;
|
760
764
|
if (!this.dialogTarget.open) this.dialogTarget.close();
|
@@ -772,15 +776,16 @@ class ItemEditorController extends Controller {
|
|
772
776
|
}
|
773
777
|
|
774
778
|
onSubmit = (event) => {
|
775
|
-
if (
|
779
|
+
if (
|
780
|
+
event.detail.success &&
|
781
|
+
"closeDialog" in event.detail.formSubmission?.submitter?.dataset
|
782
|
+
) {
|
776
783
|
this.dialogTarget.close();
|
777
784
|
this.element.removeAttribute("src");
|
778
785
|
this.dialogTarget.remove();
|
779
786
|
}
|
780
787
|
};
|
781
788
|
|
782
|
-
noop() {}
|
783
|
-
|
784
789
|
#removeTargetItem() {
|
785
790
|
const el = document.getElementById(this.dialogTarget.dataset.itemId);
|
786
791
|
const item = new Item(el.closest("[data-navigation-item]"));
|
@@ -922,6 +927,10 @@ class NewItemsController extends Controller {
|
|
922
927
|
delete this.currentItem;
|
923
928
|
}
|
924
929
|
|
930
|
+
click(e) {
|
931
|
+
if (e.target.tagName === "DIALOG") this.close(e);
|
932
|
+
}
|
933
|
+
|
925
934
|
open(e) {
|
926
935
|
e.preventDefault();
|
927
936
|
this.dialog.showModal();
|
@@ -932,20 +941,19 @@ class NewItemsController extends Controller {
|
|
932
941
|
this.dialog.close();
|
933
942
|
}
|
934
943
|
|
935
|
-
noop(e) {}
|
936
|
-
|
937
944
|
/**
|
938
945
|
* Add the selected item to the DOM at the current position or the end of the list.
|
939
946
|
*/
|
940
947
|
add(e) {
|
941
948
|
e.preventDefault();
|
942
949
|
|
943
|
-
const template = e.target.querySelector("template");
|
950
|
+
const template = e.target.closest("li").querySelector("template");
|
944
951
|
const item = template.content.querySelector("li").cloneNode(true);
|
945
952
|
const target = this.currentItem;
|
946
953
|
|
947
954
|
if (target) {
|
948
955
|
target.insertAdjacentElement("beforebegin", item);
|
956
|
+
new Item(item).depth = new Item(target).depth;
|
949
957
|
} else {
|
950
958
|
this.list.insertAdjacentElement("beforeend", item);
|
951
959
|
}
|
@@ -1,2 +1,2 @@
|
|
1
|
-
import{Controller as e}from"@hotwired/stimulus";class t{static comparator(e,t){return e.index-t.index}constructor(e){this.node=e}get itemId(){return this.node.dataset.navigationItemId}get#e(){return this.node.querySelector('input[name$="[id]"]')}set itemId(e){this.itemId!==e&&(this.node.dataset.navigationItemId=`${e}`,this.#e.value=`${e}`)}get depth(){return parseInt(this.node.dataset.navigationDepth)||0}get#t(){return this.node.querySelector('input[name$="[depth]"]')}set depth(e){this.depth!==e&&(this.node.dataset.navigationDepth=`${e}`,this.#t.value=`${e}`)}get index(){return parseInt(this.node.dataset.navigationIndex)}get#n(){return this.node.querySelector('input[name$="[index]"]')}set index(e){this.index!==e&&(this.node.dataset.navigationIndex=`${e}`,this.#n.value=`${e}`)}get isLayout(){return this.node.hasAttribute("data-content-layout")}get previousItem(){let e=this.node.previousElementSibling;if(e)return new t(e)}get nextItem(){let e=this.node.nextElementSibling;if(e)return new t(e)}hasCollapsedDescendants(){let e=this.#i;return!!e&&e.children.length>0}hasExpandedDescendants(){let e=this.nextItem;return!!e&&e.depth>this.depth}traverse(e){const t=this.#s;e(this),this.#a(e),t.forEach((t=>t.#a(e)))}#a(e){this.hasCollapsedDescendants()&&this.#r.forEach((t=>{e(t),t.#a(e)}))}collapse(){let e=this.#i;e||(e=function(e){const t=document.createElement("ol");return t.toggleAttribute("hidden",!0),t.dataset.navigationChildren="",e.appendChild(t),t}(this.node)),this.#s.forEach((t=>e.appendChild(t.node)))}expand(){this.hasCollapsedDescendants()&&Array.from(this.#i.children).reverse().forEach((e=>{this.node.insertAdjacentElement("afterend",e)}))}toggleRule(e,t=!1){this.node.dataset.hasOwnProperty(e)&&!t&&delete this.node.dataset[e],!this.node.dataset.hasOwnProperty(e)&&t&&(this.node.dataset[e]=""),"denyDrag"===e&&(this.node.hasAttribute("draggable")||t||this.node.setAttribute("draggable","true"),this.node.hasAttribute("draggable")&&t&&this.node.removeAttribute("draggable"))}hasItemIdChanged(){return!(this.#e.value===this.itemId)}updateAfterChange(){this.itemId=this.#e.value,this.#n.value=this.index,this.#t.value=this.depth}get#i(){return this.node.querySelector(":scope > [data-navigation-children]")}get#s(){const e=[];let t=this.nextItem;for(;t&&t.depth>this.depth;)e.push(t),t=t.nextItem;return e}get#r(){return this.hasCollapsedDescendants()?Array.from(this.#i.children).map((e=>new t(e))):[]}}class n{constructor(e){this.node=e}get items(){return e=this.node.querySelectorAll("[data-navigation-index]"),Array.from(e).map((e=>new t(e)));var e}get state(){const e=this.node.querySelectorAll("li input[type=hidden]");return Array.from(e).map((e=>e.value)).join("/")}reindex(){this.items.map(((e,t)=>e.index=t))}reset(){this.items.sort(t.comparator).forEach((e=>{this.node.appendChild(e.node)}))}}class i{static rules=["denyDeNest","denyNest","denyCollapse","denyExpand","denyRemove","denyDrag","denyEdit"];constructor(e=null,t=!1){this.maxDepth=e,this.debug=t?(...e)=>console.log(...e):()=>{}}normalize(e){this.firstItemDepthZero(e),this.depthMustBeSet(e),this.itemCannotHaveInvalidDepth(e),this.itemCannotExceedDepthLimit(e),this.parentMustBeLayout(e),this.parentCannotHaveExpandedAndCollapsedChildren(e)}update(e){this.rules={},this.parentsCannotDeNest(e),this.rootsCannotDeNest(e),this.nestingNeedsParent(e),this.nestingCannotExceedMaxDepth(e),this.leavesCannotCollapse(e),this.needHiddenItemsToExpand(e),this.parentsCannotBeDeleted(e),this.parentsCannotBeDragged(e),i.rules.forEach((t=>{e.toggleRule(t,!!this.rules[t])}))}firstItemDepthZero(e){0===e.index&&0!==e.depth&&(this.debug(`enforce depth on item ${e.index}: ${e.depth} => 0`),e.depth=0)}depthMustBeSet(e){(isNaN(e.depth)||e.depth<0)&&(this.debug(`unset depth on item ${e.index}: => 0`),e.depth=0)}itemCannotHaveInvalidDepth(e){const t=e.previousItem;t&&t.depth<e.depth-1&&(this.debug(`invalid depth on item ${e.index}: ${e.depth} => ${t.depth+1}`),e.depth=t.depth+1)}itemCannotExceedDepthLimit(e){this.maxDepth>0&&this.maxDepth<=e.depth&&(e.depth=this.maxDepth-1)}parentMustBeLayout(e){const t=e.previousItem;t&&t.depth<e.depth&&!t.isLayout&&(this.debug(`invalid parent for item ${e.index}: ${e.depth} => ${t.depth}`),e.depth=t.depth)}parentCannotHaveExpandedAndCollapsedChildren(e){e.hasCollapsedDescendants()&&e.hasExpandedDescendants()&&(this.debug(`expanding collapsed children of item ${e.index}`),e.expand())}parentsCannotDeNest(e){e.hasExpandedDescendants()&&this.#d("denyDeNest")}rootsCannotDeNest(e){0===e.depth&&this.#d("denyDeNest")}leavesCannotCollapse(e){e.hasExpandedDescendants()||this.#d("denyCollapse")}needHiddenItemsToExpand(e){e.hasCollapsedDescendants()||this.#d("denyExpand")}nestingNeedsParent(e){const t=e.previousItem;t?t.depth<e.depth?this.#d("denyNest"):t.depth!==e.depth||t.isLayout||this.#d("denyNest"):this.#d("denyNest")}nestingCannotExceedMaxDepth(e){this.maxDepth>0&&this.maxDepth<=e.depth+1&&this.#d("denyNest")}parentsCannotBeDeleted(e){e.itemId&&!e.hasExpandedDescendants()||this.#d("denyRemove")}parentsCannotBeDragged(e){e.hasExpandedDescendants()&&this.#d("denyDrag")}#d(e){this.rules[e]=!0}}function s(e){return new t(e.target.closest("[data-navigation-item]"))}function a(e,t){if(!e)return;if(e===t)return;const n=e.compareDocumentPosition(t);n&Node.DOCUMENT_POSITION_FOLLOWING?e.insertAdjacentElement("beforebegin",t):n&Node.DOCUMENT_POSITION_PRECEDING&&e.insertAdjacentElement("afterend",t)}function r(e){return e&&e.closest("[data-controller='navigation--editor--list'] > *")}const d=[{identifier:"navigation--editor--menu",controllerConstructor:class extends e{static targets=["menu"];static values={maxDepth:Number};connect(){this.state=this.menu.state,this.reindex()}get menu(){return new n(this.menuTarget)}reindex(){this.menu.reindex(),this.#o()}reset(){this.menu.reset()}drop(e){this.menu.reindex();const t=s(e),n=t.previousItem;let i=0;i=void 0===n?-t.depth:t.nextItem&&t.nextItem.depth>n.depth?n.depth-t.depth+1:n.depth-t.depth,t.traverse((e=>{e.depth+=i})),this.#o(),e.preventDefault()}remove(e){s(e).node.remove(),this.#o(),e.preventDefault()}nest(e){s(e).traverse((e=>{e.depth+=1})),this.#o(),e.preventDefault()}deNest(e){s(e).traverse((e=>{e.depth-=1})),this.#o(),e.preventDefault()}collapse(e){s(e).collapse(),this.#o(),e.preventDefault()}expand(e){s(e).expand(),this.#o(),e.preventDefault()}#o(){this.updateRequested=!0,setTimeout((()=>{if(!this.updateRequested)return;this.updateRequested=!1;const e=new i(this.maxDepthValue);this.menu.items.forEach((t=>e.normalize(t))),this.menu.items.forEach((t=>e.update(t))),this.#h()}),0)}#h(){this.dispatch("change",{bubbles:!0,prefix:"navigation",detail:{dirty:this.#l()}})}#l(){return this.menu.state!==this.state}}},{identifier:"navigation--editor--item",controllerConstructor:class extends e{get item(){return new t(this.li)}get ol(){return this.element.closest("ol")}get li(){return this.element.closest("li")}connect(){this.element.dataset.hasOwnProperty("delete")?this.remove():this.item.index>=0?this.item.hasItemIdChanged()&&(this.item.updateAfterChange(),this.reindex()):this.reindex()}remove(){this.ol,this.li.remove(),this.reindex()}reindex(){this.dispatch("reindex",{bubbles:!0,prefix:"navigation"})}}},{identifier:"navigation--editor--item-editor",controllerConstructor:class extends e{static targets=["dialog"];connect(){this.element.addEventListener("turbo:submit-end",this.onSubmit)}disconnect(){this.element.removeEventListener("turbo:submit-end",this.onSubmit)}dismiss(){this.dialogTarget&&(this.dialogTarget.open||this.dialogTarget.close(),"itemPersisted"in this.dialogTarget.dataset||this.#p(),this.element.removeAttribute("src"),this.dialogTarget.remove())}dialogTargetConnected(e){e.showModal()}onSubmit=e=>{e.detail.success&&(this.dialogTarget.close(),this.element.removeAttribute("src"),this.dialogTarget.remove())};noop(){}#p(){const e=document.getElementById(this.dialogTarget.dataset.itemId),n=new t(e.closest("[data-navigation-item]")),i=n.node.parentElement;n.node.remove(),this.dispatch("reindex",{target:i,bubbles:!0,prefix:"navigation"})}}},{identifier:"navigation--editor--list",controllerConstructor:class extends e{dragstart(e){if(this.element!==e.target.parentElement)return;const t=e.target;e.dataTransfer.effectAllowed="move",requestAnimationFrame((()=>t.dataset.dragging=""))}dragover(e){const t=this.dragItem;if(t)return a(r(e.target),t),e.preventDefault(),!0}drop(e){let t=this.dragItem;t&&(e.preventDefault(),delete t.dataset.dragging,a(r(e.target),t),this.dispatch("drop",{target:t,bubbles:!0,prefix:"navigation"}))}dragend(){const e=this.dragItem;e&&(delete e.dataset.dragging,this.reset())}get isDragging(){return!!this.dragItem}get dragItem(){return this.element.querySelector("[data-dragging]")}reindex(){this.dispatch("reindex",{bubbles:!0,prefix:"navigation"})}reset(){this.dispatch("reset",{bubbles:!0,prefix:"navigation"})}}},{identifier:"navigation--editor--new-items",controllerConstructor:class extends e{static targets=["inline"];connect(){this.form.addEventListener("mousemove",this.move)}disconnect(){this.form?.removeEventListener("mousemove",this.move),delete this.currentItem}open(e){e.preventDefault(),this.dialog.showModal()}close(e){e.preventDefault(),this.dialog.close()}noop(e){}add(e){e.preventDefault();const t=e.target.querySelector("template").content.querySelector("li").cloneNode(!0),n=this.currentItem;n?n.insertAdjacentElement("beforebegin",t):this.list.insertAdjacentElement("beforeend",t),this.toggleInline(!1),this.dialog.close(),requestAnimationFrame((()=>{t.querySelector('[value="edit"]').click()}))}morph(e){e.preventDefault(),this.dialog.close()}move=e=>{if(this.isOverInlineTarget(e))return;if(this.dialog.open)return;const t=this.getCurrentItem(e);this.currentItem!==t&&(this.currentItem&&this.toggleInline(!1),this.currentItem=t,this.timer&&clearTimeout(this.timer),this.timer=setTimeout((()=>{delete this.timer,this.toggleInline()}),100))};toggleInline(e=!!this.currentItem){e?(this.inlineTarget.style.top=`${this.currentItem.offsetTop}px`,this.inlineTarget.toggleAttribute("hidden",!1)):this.inlineTarget.toggleAttribute("hidden",!0)}get dialog(){return this.element.querySelector("dialog")}get form(){return this.element.closest("form")}get list(){return this.form.querySelector('[data-controller="navigation--editor--list"]')}getCurrentItem(e){const t=document.elementFromPoint(e.clientX,e.clientY).closest("li");if(!t)return null;const n=t.getBoundingClientRect();return e.clientX<n.left+n.width/2-48||e.clientX>n.left+n.width/2+48?null:e.clientY-n.y<=24?t:n.y+n.height-e.clientY<=24?t.nextElementSibling:null}isOverInlineTarget(e){return this.inlineTarget===document.elementFromPoint(e.clientX,e.clientY).closest("div")}}},{identifier:"navigation--editor--status-bar",controllerConstructor:class extends e{connect(){this.versionState=this.element.dataset.state}morph(e){e.target===this.element&&(this.versionState=this.element.dataset.state,this.update({dirty:!1}))}change(e){e.detail&&e.detail.hasOwnProperty("dirty")&&this.update(e.detail)}update({dirty:e}){this.element.dataset.state=e?"dirty":this.versionState}}}];export{d as default};
|
1
|
+
import{Controller as e}from"@hotwired/stimulus";class t{static comparator(e,t){return e.index-t.index}constructor(e){this.node=e}get itemId(){return this.node.dataset.navigationItemId}get#e(){return this.node.querySelector('input[name$="[id]"]')}set itemId(e){this.itemId!==e&&(this.node.dataset.navigationItemId=`${e}`,this.#e.value=`${e}`)}get depth(){return parseInt(this.node.dataset.navigationDepth)||0}get#t(){return this.node.querySelector('input[name$="[depth]"]')}set depth(e){this.depth!==e&&(this.node.dataset.navigationDepth=`${e}`,this.#t.value=`${e}`)}get index(){return parseInt(this.node.dataset.navigationIndex)}get#n(){return this.node.querySelector('input[name$="[index]"]')}set index(e){this.index!==e&&(this.node.dataset.navigationIndex=`${e}`,this.#n.value=`${e}`)}get isLayout(){return this.node.hasAttribute("data-content-layout")}get previousItem(){let e=this.node.previousElementSibling;if(e)return new t(e)}get nextItem(){let e=this.node.nextElementSibling;if(e)return new t(e)}hasCollapsedDescendants(){let e=this.#i;return!!e&&e.children.length>0}hasExpandedDescendants(){let e=this.nextItem;return!!e&&e.depth>this.depth}traverse(e){const t=this.#s;e(this),this.#a(e),t.forEach((t=>t.#a(e)))}#a(e){this.hasCollapsedDescendants()&&this.#d.forEach((t=>{e(t),t.#a(e)}))}collapse(){let e=this.#i;e||(e=function(e){const t=document.createElement("ol");return t.toggleAttribute("hidden",!0),t.dataset.navigationChildren="",e.appendChild(t),t}(this.node)),this.#s.forEach((t=>e.appendChild(t.node)))}expand(){this.hasCollapsedDescendants()&&Array.from(this.#i.children).reverse().forEach((e=>{this.node.insertAdjacentElement("afterend",e)}))}toggleRule(e,t=!1){this.node.dataset.hasOwnProperty(e)&&!t&&delete this.node.dataset[e],!this.node.dataset.hasOwnProperty(e)&&t&&(this.node.dataset[e]=""),"denyDrag"===e&&(this.node.hasAttribute("draggable")||t||this.node.setAttribute("draggable","true"),this.node.hasAttribute("draggable")&&t&&this.node.removeAttribute("draggable"))}hasItemIdChanged(){return!(this.#e.value===this.itemId)}updateAfterChange(){this.itemId=this.#e.value,this.#n.value=this.index,this.#t.value=this.depth}get#i(){return this.node.querySelector(":scope > [data-navigation-children]")}get#s(){const e=[];let t=this.nextItem;for(;t&&t.depth>this.depth;)e.push(t),t=t.nextItem;return e}get#d(){return this.hasCollapsedDescendants()?Array.from(this.#i.children).map((e=>new t(e))):[]}}class n{constructor(e){this.node=e}get items(){return e=this.node.querySelectorAll("[data-navigation-index]"),Array.from(e).map((e=>new t(e)));var e}get state(){const e=this.node.querySelectorAll("li input[type=hidden]");return Array.from(e).map((e=>e.value)).join("/")}reindex(){this.items.map(((e,t)=>e.index=t))}reset(){this.items.sort(t.comparator).forEach((e=>{this.node.appendChild(e.node)}))}}class i{static rules=["denyDeNest","denyNest","denyCollapse","denyExpand","denyRemove","denyDrag","denyEdit"];constructor(e=null,t=!1){this.maxDepth=e,this.debug=t?(...e)=>console.log(...e):()=>{}}normalize(e){this.firstItemDepthZero(e),this.depthMustBeSet(e),this.itemCannotHaveInvalidDepth(e),this.itemCannotExceedDepthLimit(e),this.parentMustBeLayout(e),this.parentCannotHaveExpandedAndCollapsedChildren(e)}update(e){this.rules={},this.parentsCannotDeNest(e),this.rootsCannotDeNest(e),this.nestingNeedsParent(e),this.nestingCannotExceedMaxDepth(e),this.leavesCannotCollapse(e),this.needHiddenItemsToExpand(e),this.parentsCannotBeDeleted(e),this.parentsCannotBeDragged(e),i.rules.forEach((t=>{e.toggleRule(t,!!this.rules[t])}))}firstItemDepthZero(e){0===e.index&&0!==e.depth&&(this.debug(`enforce depth on item ${e.index}: ${e.depth} => 0`),e.depth=0)}depthMustBeSet(e){(isNaN(e.depth)||e.depth<0)&&(this.debug(`unset depth on item ${e.index}: => 0`),e.depth=0)}itemCannotHaveInvalidDepth(e){const t=e.previousItem;t&&t.depth<e.depth-1&&(this.debug(`invalid depth on item ${e.index}: ${e.depth} => ${t.depth+1}`),e.depth=t.depth+1)}itemCannotExceedDepthLimit(e){this.maxDepth>0&&this.maxDepth<=e.depth&&(e.depth=this.maxDepth-1)}parentMustBeLayout(e){const t=e.previousItem;t&&t.depth<e.depth&&!t.isLayout&&(this.debug(`invalid parent for item ${e.index}: ${e.depth} => ${t.depth}`),e.depth=t.depth)}parentCannotHaveExpandedAndCollapsedChildren(e){e.hasCollapsedDescendants()&&e.hasExpandedDescendants()&&(this.debug(`expanding collapsed children of item ${e.index}`),e.expand())}parentsCannotDeNest(e){e.hasExpandedDescendants()&&this.#r("denyDeNest")}rootsCannotDeNest(e){0===e.depth&&this.#r("denyDeNest")}leavesCannotCollapse(e){e.hasExpandedDescendants()||this.#r("denyCollapse")}needHiddenItemsToExpand(e){e.hasCollapsedDescendants()||this.#r("denyExpand")}nestingNeedsParent(e){const t=e.previousItem;t?t.depth<e.depth?this.#r("denyNest"):t.depth!==e.depth||t.isLayout||this.#r("denyNest"):this.#r("denyNest")}nestingCannotExceedMaxDepth(e){this.maxDepth>0&&this.maxDepth<=e.depth+1&&this.#r("denyNest")}parentsCannotBeDeleted(e){e.itemId&&!e.hasExpandedDescendants()||this.#r("denyRemove")}parentsCannotBeDragged(e){e.hasExpandedDescendants()&&this.#r("denyDrag")}#r(e){this.rules[e]=!0}}function s(e){return new t(e.target.closest("[data-navigation-item]"))}function a(e,t){if(!e)return;if(e===t)return;const n=e.compareDocumentPosition(t);n&Node.DOCUMENT_POSITION_FOLLOWING?e.insertAdjacentElement("beforebegin",t):n&Node.DOCUMENT_POSITION_PRECEDING&&e.insertAdjacentElement("afterend",t)}function d(e){return e&&e.closest("[data-controller='navigation--editor--list'] > *")}const r=[{identifier:"navigation--editor--menu",controllerConstructor:class extends e{static targets=["menu"];static values={maxDepth:Number};connect(){this.state=this.menu.state,this.reindex()}get menu(){return new n(this.menuTarget)}reindex(){this.menu.reindex(),this.#o()}reset(){this.menu.reset()}drop(e){this.menu.reindex();const t=s(e),n=t.previousItem;let i=0;i=void 0===n?-t.depth:t.nextItem&&t.nextItem.depth>n.depth?n.depth-t.depth+1:n.depth-t.depth,t.traverse((e=>{e.depth+=i})),this.#o(),e.preventDefault()}remove(e){s(e).node.remove(),this.#o(),e.preventDefault()}nest(e){s(e).traverse((e=>{e.depth+=1})),this.#o(),e.preventDefault()}deNest(e){s(e).traverse((e=>{e.depth-=1})),this.#o(),e.preventDefault()}collapse(e){s(e).collapse(),this.#o(),e.preventDefault()}expand(e){s(e).expand(),this.#o(),e.preventDefault()}#o(){this.updateRequested=!0,setTimeout((()=>{if(!this.updateRequested)return;this.updateRequested=!1;const e=new i(this.maxDepthValue);this.menu.items.forEach((t=>e.normalize(t))),this.menu.items.forEach((t=>e.update(t))),this.#h()}),0)}#h(){this.dispatch("change",{bubbles:!0,prefix:"navigation",detail:{dirty:this.#l()}})}#l(){return this.menu.state!==this.state}}},{identifier:"navigation--editor--item",controllerConstructor:class extends e{get item(){return new t(this.li)}get ol(){return this.element.closest("ol")}get li(){return this.element.closest("li")}connect(){this.element.dataset.hasOwnProperty("delete")?this.remove():this.item.index>=0?this.item.hasItemIdChanged()&&(this.item.updateAfterChange(),this.reindex()):this.reindex()}remove(){this.ol,this.li.remove(),this.reindex()}reindex(){this.dispatch("reindex",{bubbles:!0,prefix:"navigation"})}}},{identifier:"navigation--editor--item-editor",controllerConstructor:class extends e{static targets=["dialog"];connect(){this.element.addEventListener("turbo:submit-end",this.onSubmit)}disconnect(){this.element.removeEventListener("turbo:submit-end",this.onSubmit)}click(e){"DIALOG"===e.target.tagName&&this.dismiss()}dismiss(){this.dialogTarget&&(this.dialogTarget.open||this.dialogTarget.close(),"itemPersisted"in this.dialogTarget.dataset||this.#p(),this.element.removeAttribute("src"),this.dialogTarget.remove())}dialogTargetConnected(e){e.showModal()}onSubmit=e=>{e.detail.success&&"closeDialog"in e.detail.formSubmission?.submitter?.dataset&&(this.dialogTarget.close(),this.element.removeAttribute("src"),this.dialogTarget.remove())};#p(){const e=document.getElementById(this.dialogTarget.dataset.itemId),n=new t(e.closest("[data-navigation-item]")),i=n.node.parentElement;n.node.remove(),this.dispatch("reindex",{target:i,bubbles:!0,prefix:"navigation"})}}},{identifier:"navigation--editor--list",controllerConstructor:class extends e{dragstart(e){if(this.element!==e.target.parentElement)return;const t=e.target;e.dataTransfer.effectAllowed="move",requestAnimationFrame((()=>t.dataset.dragging=""))}dragover(e){const t=this.dragItem;if(t)return a(d(e.target),t),e.preventDefault(),!0}drop(e){let t=this.dragItem;t&&(e.preventDefault(),delete t.dataset.dragging,a(d(e.target),t),this.dispatch("drop",{target:t,bubbles:!0,prefix:"navigation"}))}dragend(){const e=this.dragItem;e&&(delete e.dataset.dragging,this.reset())}get isDragging(){return!!this.dragItem}get dragItem(){return this.element.querySelector("[data-dragging]")}reindex(){this.dispatch("reindex",{bubbles:!0,prefix:"navigation"})}reset(){this.dispatch("reset",{bubbles:!0,prefix:"navigation"})}}},{identifier:"navigation--editor--new-items",controllerConstructor:class extends e{static targets=["inline"];connect(){this.form.addEventListener("mousemove",this.move)}disconnect(){this.form?.removeEventListener("mousemove",this.move),delete this.currentItem}click(e){"DIALOG"===e.target.tagName&&this.close(e)}open(e){e.preventDefault(),this.dialog.showModal()}close(e){e.preventDefault(),this.dialog.close()}add(e){e.preventDefault();const n=e.target.closest("li").querySelector("template").content.querySelector("li").cloneNode(!0),i=this.currentItem;i?(i.insertAdjacentElement("beforebegin",n),new t(n).depth=new t(i).depth):this.list.insertAdjacentElement("beforeend",n),this.toggleInline(!1),this.dialog.close(),requestAnimationFrame((()=>{n.querySelector('[value="edit"]').click()}))}morph(e){e.preventDefault(),this.dialog.close()}move=e=>{if(this.isOverInlineTarget(e))return;if(this.dialog.open)return;const t=this.getCurrentItem(e);this.currentItem!==t&&(this.currentItem&&this.toggleInline(!1),this.currentItem=t,this.timer&&clearTimeout(this.timer),this.timer=setTimeout((()=>{delete this.timer,this.toggleInline()}),100))};toggleInline(e=!!this.currentItem){e?(this.inlineTarget.style.top=`${this.currentItem.offsetTop}px`,this.inlineTarget.toggleAttribute("hidden",!1)):this.inlineTarget.toggleAttribute("hidden",!0)}get dialog(){return this.element.querySelector("dialog")}get form(){return this.element.closest("form")}get list(){return this.form.querySelector('[data-controller="navigation--editor--list"]')}getCurrentItem(e){const t=document.elementFromPoint(e.clientX,e.clientY).closest("li");if(!t)return null;const n=t.getBoundingClientRect();return e.clientX<n.left+n.width/2-48||e.clientX>n.left+n.width/2+48?null:e.clientY-n.y<=24?t:n.y+n.height-e.clientY<=24?t.nextElementSibling:null}isOverInlineTarget(e){return this.inlineTarget===document.elementFromPoint(e.clientX,e.clientY).closest("div")}}},{identifier:"navigation--editor--status-bar",controllerConstructor:class extends e{connect(){this.versionState=this.element.dataset.state}morph(e){e.target===this.element&&(this.versionState=this.element.dataset.state,this.update({dirty:!1}))}change(e){e.detail&&e.detail.hasOwnProperty("dirty")&&this.update(e.detail)}update({dirty:e}){this.element.dataset.state=e?"dirty":this.versionState}}}];export{r as default};
|
2
2
|
//# sourceMappingURL=navigation.min.js.map
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"navigation.min.js","sources":["../../../javascript/navigation/editor/item.js","../../../javascript/navigation/editor/menu.js","../../../javascript/navigation/editor/rules-engine.js","../../../javascript/navigation/editor/menu_controller.js","../../../javascript/navigation/editor/list_controller.js","../../../javascript/navigation/application.js","../../../javascript/navigation/editor/item_controller.js","../../../javascript/navigation/editor/item_editor_controller.js","../../../javascript/navigation/editor/new_items_controller.js","../../../javascript/navigation/editor/status_bar_controller.js"],"sourcesContent":["export default class Item {\n /**\n * Sort items by their index.\n *\n * @param a {Item}\n * @param b {Item}\n * @returns {number}\n */\n static comparator(a, b) {\n return a.index - b.index;\n }\n\n /**\n * @param node {Element} li[data-navigation-index]\n */\n constructor(node) {\n this.node = node;\n }\n\n /**\n * @returns {String} id of the node's item (from data attributes)\n */\n get itemId() {\n return this.node.dataset[`navigationItemId`];\n }\n\n get #itemIdInput() {\n return this.node.querySelector(`input[name$=\"[id]\"]`);\n }\n\n /**\n * @param itemId {String} id\n */\n set itemId(id) {\n if (this.itemId === id) return;\n\n this.node.dataset[`navigationItemId`] = `${id}`;\n this.#itemIdInput.value = `${id}`;\n }\n\n /**\n * @returns {number} logical nesting depth of node in menu\n */\n get depth() {\n return parseInt(this.node.dataset[`navigationDepth`]) || 0;\n }\n\n get #depthInput() {\n return this.node.querySelector(`input[name$=\"[depth]\"]`);\n }\n\n /**\n * @param depth {number} depth >= 0\n */\n set depth(depth) {\n if (this.depth === depth) return;\n\n this.node.dataset[`navigationDepth`] = `${depth}`;\n this.#depthInput.value = `${depth}`;\n }\n\n /**\n * @returns {number} logical index of node in menu (pre-order traversal)\n */\n get index() {\n return parseInt(this.node.dataset[`navigationIndex`]);\n }\n\n get #indexInput() {\n return this.node.querySelector(`input[name$=\"[index]\"]`);\n }\n\n /**\n * @param index {number} index >= 0\n */\n set index(index) {\n if (this.index === index) return;\n\n this.node.dataset[`navigationIndex`] = `${index}`;\n this.#indexInput.value = `${index}`;\n }\n\n /**\n * @returns {boolean} true if this item can have children\n */\n get isLayout() {\n return this.node.hasAttribute(\"data-content-layout\");\n }\n\n /**\n * @returns {Item} nearest neighbour (index - 1)\n */\n get previousItem() {\n let sibling = this.node.previousElementSibling;\n if (sibling) return new Item(sibling);\n }\n\n /**\n * @returns {Item} nearest neighbour (index + 1)\n */\n get nextItem() {\n let sibling = this.node.nextElementSibling;\n if (sibling) return new Item(sibling);\n }\n\n /**\n * @returns {boolean} true if this item has any collapsed children\n */\n hasCollapsedDescendants() {\n let childrenList = this.#childrenListElement;\n return !!childrenList && childrenList.children.length > 0;\n }\n\n /**\n * @returns {boolean} true if this item has any expanded children\n */\n hasExpandedDescendants() {\n let sibling = this.nextItem;\n return !!sibling && sibling.depth > this.depth;\n }\n\n /**\n * Recursively traverse the node and its descendants.\n *\n * @callback {Item}\n */\n traverse(callback) {\n // capture descendants before traversal in case of side-effects\n // specifically, setting depth affects calculation\n const expanded = this.#expandedDescendants;\n\n callback(this);\n this.#traverseCollapsed(callback);\n expanded.forEach((item) => item.#traverseCollapsed(callback));\n }\n\n /**\n * Recursively traverse the node's collapsed descendants, if any.\n *\n * @callback {Item}\n */\n #traverseCollapsed(callback) {\n if (!this.hasCollapsedDescendants()) return;\n\n this.#collapsedDescendants.forEach((item) => {\n callback(item);\n item.#traverseCollapsed(callback);\n });\n }\n\n /**\n * Collapses visible (logical) children into this element's hidden children\n * list, creating it if it doesn't already exist.\n */\n collapse() {\n let listElement = this.#childrenListElement;\n\n if (!listElement) listElement = createChildrenList(this.node);\n\n this.#expandedDescendants.forEach((child) =>\n listElement.appendChild(child.node),\n );\n }\n\n /**\n * Moves any collapsed children back into the parent menu.\n */\n expand() {\n if (!this.hasCollapsedDescendants()) return;\n\n Array.from(this.#childrenListElement.children)\n .reverse()\n .forEach((node) => {\n this.node.insertAdjacentElement(\"afterend\", node);\n });\n }\n\n /**\n * Sets the state of a given rule on the target node.\n *\n * @param rule {String}\n * @param deny {boolean}\n */\n toggleRule(rule, deny = false) {\n if (this.node.dataset.hasOwnProperty(rule) && !deny) {\n delete this.node.dataset[rule];\n }\n if (!this.node.dataset.hasOwnProperty(rule) && deny) {\n this.node.dataset[rule] = \"\";\n }\n\n if (rule === \"denyDrag\") {\n if (!this.node.hasAttribute(\"draggable\") && !deny) {\n this.node.setAttribute(\"draggable\", \"true\");\n }\n if (this.node.hasAttribute(\"draggable\") && deny) {\n this.node.removeAttribute(\"draggable\");\n }\n }\n }\n\n /**\n * Detects turbo item changes by comparing the dataset id with the input\n */\n hasItemIdChanged() {\n return !(this.#itemIdInput.value === this.itemId);\n }\n\n /**\n * Updates inputs, in case they don't match the data values, e.g., when the\n * nested inputs have been hot-swapped by turbo with data from the server.\n *\n * Updates itemId from input as that is the canonical source.\n */\n updateAfterChange() {\n this.itemId = this.#itemIdInput.value;\n this.#indexInput.value = this.index;\n this.#depthInput.value = this.depth;\n }\n\n /**\n * Finds the dom container for storing collapsed (hidden) children, if present.\n *\n * @returns {Element} ol[data-navigation-children]\n */\n get #childrenListElement() {\n return this.node.querySelector(`:scope > [data-navigation-children]`);\n }\n\n /**\n * @returns {Item[]} all items that follow this element that have a greater depth.\n */\n get #expandedDescendants() {\n const descendants = [];\n\n let sibling = this.nextItem;\n while (sibling && sibling.depth > this.depth) {\n descendants.push(sibling);\n sibling = sibling.nextItem;\n }\n\n return descendants;\n }\n\n /**\n * @returns {Item[]} all items directly contained inside this element's hidden children element.\n */\n get #collapsedDescendants() {\n if (!this.hasCollapsedDescendants()) return [];\n\n return Array.from(this.#childrenListElement.children).map(\n (node) => new Item(node),\n );\n }\n}\n\n/**\n * Finds or creates a dom container for storing collapsed (hidden) children.\n *\n * @param node {Element} li[data-navigation-index]\n * @returns {Element} ol[data-navigation-children]\n */\nfunction createChildrenList(node) {\n const childrenList = document.createElement(\"ol\");\n childrenList.toggleAttribute(\"hidden\", true);\n\n childrenList.dataset[`navigationChildren`] = \"\";\n\n node.appendChild(childrenList);\n\n return childrenList;\n}\n","import Item from \"./item\";\n\n/**\n * @param nodes {NodeList}\n * @returns {Item[]}\n */\nfunction createItemList(nodes) {\n return Array.from(nodes).map((node) => new Item(node));\n}\n\nexport default class Menu {\n /**\n * @param node {Element} navigation editor list\n */\n constructor(node) {\n this.node = node;\n }\n\n /**\n * @return {Item[]} an ordered list of all items in the menu\n */\n get items() {\n return createItemList(\n this.node.querySelectorAll(\"[data-navigation-index]\"),\n );\n }\n\n /**\n * @return {String} a serialized description of the structure of the menu\n */\n get state() {\n const inputs = this.node.querySelectorAll(\"li input[type=hidden]\");\n return Array.from(inputs)\n .map((e) => e.value)\n .join(\"/\");\n }\n\n /**\n * Set the index of items based on their current position.\n */\n reindex() {\n this.items.map((item, index) => (item.index = index));\n }\n\n /**\n * Resets the order of items to their defined index.\n * Useful after an aborted drag.\n */\n reset() {\n this.items.sort(Item.comparator).forEach((item) => {\n this.node.appendChild(item.node);\n });\n }\n}\n","export default class RulesEngine {\n static rules = [\n \"denyDeNest\",\n \"denyNest\",\n \"denyCollapse\",\n \"denyExpand\",\n \"denyRemove\",\n \"denyDrag\",\n \"denyEdit\",\n ];\n\n constructor(maxDepth = null, debug = false) {\n this.maxDepth = maxDepth;\n if (debug) {\n this.debug = (...args) => console.log(...args);\n } else {\n this.debug = () => {};\n }\n }\n\n /**\n * Enforce structural rules to ensure that the given item is currently in a\n * valid state.\n *\n * @param {Item} item\n */\n normalize(item) {\n // structural rules enforce a valid tree structure\n this.firstItemDepthZero(item);\n this.depthMustBeSet(item);\n this.itemCannotHaveInvalidDepth(item);\n this.itemCannotExceedDepthLimit(item);\n this.parentMustBeLayout(item);\n this.parentCannotHaveExpandedAndCollapsedChildren(item);\n }\n\n /**\n * Apply rules to the given item to determine what operations are permitted.\n *\n * @param {Item} item\n */\n update(item) {\n this.rules = {};\n\n // behavioural rules define what the user is allowed to do\n this.parentsCannotDeNest(item);\n this.rootsCannotDeNest(item);\n this.nestingNeedsParent(item);\n this.nestingCannotExceedMaxDepth(item);\n this.leavesCannotCollapse(item);\n this.needHiddenItemsToExpand(item);\n this.parentsCannotBeDeleted(item);\n this.parentsCannotBeDragged(item);\n\n RulesEngine.rules.forEach((rule) => {\n item.toggleRule(rule, !!this.rules[rule]);\n });\n }\n\n /**\n * First item can't have a parent, so its depth should always be 0\n */\n firstItemDepthZero(item) {\n if (item.index === 0 && item.depth !== 0) {\n this.debug(`enforce depth on item ${item.index}: ${item.depth} => 0`);\n\n item.depth = 0;\n }\n }\n\n /**\n * Every item should have a non-negative depth set.\n *\n * @param {Item} item\n */\n depthMustBeSet(item) {\n if (isNaN(item.depth) || item.depth < 0) {\n this.debug(`unset depth on item ${item.index}: => 0`);\n\n item.depth = 0;\n }\n }\n\n /**\n * Depth must increase stepwise.\n *\n * @param {Item} item\n */\n itemCannotHaveInvalidDepth(item) {\n const previous = item.previousItem;\n if (previous && previous.depth < item.depth - 1) {\n this.debug(\n `invalid depth on item ${item.index}: ${item.depth} => ${\n previous.depth + 1\n }`,\n );\n\n item.depth = previous.depth + 1;\n }\n }\n\n /**\n * Depth must not exceed menu's depth limit.\n *\n * @param {Item} item\n */\n itemCannotExceedDepthLimit(item) {\n if (this.maxDepth > 0 && this.maxDepth <= item.depth) {\n // Note: this change can cause an issue where the previous item is treated\n // like a parent even though it no longer has children. This is because\n // items are processed in order. This issue does not seem worth solving\n // as it only occurs if the max depth is altered. The issue can be worked\n // around by saving the menu.\n item.depth = this.maxDepth - 1;\n }\n }\n\n /**\n * Parent item, if any, must be a layout.\n *\n * @param {Item} item\n */\n parentMustBeLayout(item) {\n // if we're the first child, make sure our parent is a layout\n // if we're a sibling, we know the previous item is valid so we must be too\n const previous = item.previousItem;\n if (previous && previous.depth < item.depth && !previous.isLayout) {\n this.debug(\n `invalid parent for item ${item.index}: ${item.depth} => ${previous.depth}`,\n );\n\n item.depth = previous.depth;\n }\n }\n\n /**\n * If a parent has expanded and collapsed children, expand.\n *\n * @param {Item} item\n */\n parentCannotHaveExpandedAndCollapsedChildren(item) {\n if (item.hasCollapsedDescendants() && item.hasExpandedDescendants()) {\n this.debug(`expanding collapsed children of item ${item.index}`);\n\n item.expand();\n }\n }\n\n /**\n * De-nesting an item would create a gap of 2 between itself and its children\n *\n * @param {Item} item\n */\n parentsCannotDeNest(item) {\n if (item.hasExpandedDescendants()) this.#deny(\"denyDeNest\");\n }\n\n /**\n * Item depth can't go below 0.\n *\n * @param {Item} item\n */\n rootsCannotDeNest(item) {\n if (item.depth === 0) this.#deny(\"denyDeNest\");\n }\n\n /**\n * If an item doesn't have children it can't be collapsed.\n *\n * @param {Item} item\n */\n leavesCannotCollapse(item) {\n if (!item.hasExpandedDescendants()) this.#deny(\"denyCollapse\");\n }\n\n /**\n * If an item doesn't have any hidden descendants then it can't be expanded.\n *\n * @param {Item} item\n */\n needHiddenItemsToExpand(item) {\n if (!item.hasCollapsedDescendants()) this.#deny(\"denyExpand\");\n }\n\n /**\n * An item can't be nested (indented) if it doesn't have a valid parent.\n *\n * @param {Item} item\n */\n nestingNeedsParent(item) {\n const previous = item.previousItem;\n // no previous, so cannot nest\n if (!previous) this.#deny(\"denyNest\");\n // previous is too shallow, nesting would increase depth too much\n else if (previous.depth < item.depth) this.#deny(\"denyNest\");\n // new parent is not a layout\n else if (previous.depth === item.depth && !previous.isLayout)\n this.#deny(\"denyNest\");\n }\n\n /**\n * An item can't be nested (indented) if doing so would exceed the max depth.\n *\n * @param {Item} item\n */\n nestingCannotExceedMaxDepth(item) {\n if (this.maxDepth > 0 && this.maxDepth <= item.depth + 1) {\n this.#deny(\"denyNest\");\n }\n }\n\n /**\n * An item can't be deleted if it has visible children.\n *\n * @param {Item} item\n */\n parentsCannotBeDeleted(item) {\n if (!item.itemId || item.hasExpandedDescendants()) this.#deny(\"denyRemove\");\n }\n\n /**\n * Items cannot be dragged if they have visible children.\n *\n * @param {Item} item\n */\n parentsCannotBeDragged(item) {\n if (item.hasExpandedDescendants()) this.#deny(\"denyDrag\");\n }\n\n /**\n * Record a deny.\n *\n * @param rule {String}\n */\n #deny(rule) {\n this.rules[rule] = true;\n }\n}\n","import { Controller } from \"@hotwired/stimulus\";\n\nimport Item from \"./item\";\nimport Menu from \"./menu\";\nimport RulesEngine from \"./rules-engine\";\n\nexport default class MenuController extends Controller {\n static targets = [\"menu\"];\n static values = {\n maxDepth: Number,\n };\n\n // Caution: connect is called on attachment, but also on morph/render\n connect() {\n this.state = this.menu.state;\n\n this.reindex();\n }\n\n get menu() {\n return new Menu(this.menuTarget);\n }\n\n reindex() {\n this.menu.reindex();\n this.#update();\n }\n\n reset() {\n this.menu.reset();\n }\n\n drop(event) {\n this.menu.reindex(); // set indexes before calculating previous\n\n const item = getEventItem(event);\n const previous = item.previousItem;\n\n let delta = 0;\n if (previous === undefined) {\n // if previous does not exist, set depth to 0\n delta = -item.depth;\n } else if (item.nextItem && item.nextItem.depth > previous.depth) {\n // if next is a child of previous, make item a child of previous\n delta = previous.depth - item.depth + 1;\n } else {\n // otherwise, make item a sibling of previous\n delta = previous.depth - item.depth;\n }\n\n item.traverse((child) => {\n child.depth += delta;\n });\n\n this.#update();\n event.preventDefault();\n }\n\n remove(event) {\n const item = getEventItem(event);\n\n item.node.remove();\n\n this.#update();\n event.preventDefault();\n }\n\n nest(event) {\n const item = getEventItem(event);\n\n item.traverse((child) => {\n child.depth += 1;\n });\n\n this.#update();\n event.preventDefault();\n }\n\n deNest(event) {\n const item = getEventItem(event);\n\n item.traverse((child) => {\n child.depth -= 1;\n });\n\n this.#update();\n event.preventDefault();\n }\n\n collapse(event) {\n const item = getEventItem(event);\n\n item.collapse();\n\n this.#update();\n event.preventDefault();\n }\n\n expand(event) {\n const item = getEventItem(event);\n\n item.expand();\n\n this.#update();\n event.preventDefault();\n }\n\n /**\n * Re-apply rules to items to enable/disable appropriate actions.\n */\n #update() {\n // debounce requests to ensure that we only update once per tick\n this.updateRequested = true;\n setTimeout(() => {\n if (!this.updateRequested) return;\n\n this.updateRequested = false;\n const engine = new RulesEngine(this.maxDepthValue);\n this.menu.items.forEach((item) => engine.normalize(item));\n this.menu.items.forEach((item) => engine.update(item));\n\n this.#notifyChange();\n }, 0);\n }\n\n #notifyChange() {\n this.dispatch(\"change\", {\n bubbles: true,\n prefix: \"navigation\",\n detail: { dirty: this.#isDirty() },\n });\n }\n\n #isDirty() {\n return this.menu.state !== this.state;\n }\n}\n\nfunction getEventItem(event) {\n return new Item(event.target.closest(\"[data-navigation-item]\"));\n}\n","import { Controller } from \"@hotwired/stimulus\";\n\nexport default class ListController extends Controller {\n /**\n * When the user starts a drag within the list, set the item's dataTransfer\n * properties to indicate that it's being dragged and update its style.\n *\n * We delay setting the dataset property until the next animation frame\n * so that the style updates can be computed before the drag begins.\n *\n * @param event {DragEvent}\n */\n dragstart(event) {\n if (this.element !== event.target.parentElement) return;\n\n const target = event.target;\n event.dataTransfer.effectAllowed = \"move\";\n\n // update element style after drag has begun\n requestAnimationFrame(() => (target.dataset.dragging = \"\"));\n }\n\n /**\n * When the user drags an item over another item in the last, swap the\n * dragging item with the item under the cursor.\n *\n * @param event {DragEvent}\n */\n dragover(event) {\n const item = this.dragItem;\n if (!item) return;\n\n swap(dropTarget(event.target), item);\n\n event.preventDefault();\n return true;\n }\n\n /**\n * When the user drops an item, end the drag and reindex the list.\n *\n * @param event {DragEvent}\n */\n drop(event) {\n let item = this.dragItem;\n\n if (!item) return;\n\n event.preventDefault();\n delete item.dataset.dragging;\n swap(dropTarget(event.target), item);\n\n this.dispatch(\"drop\", {\n target: item,\n bubbles: true,\n prefix: \"navigation\",\n });\n }\n\n /**\n * End an in-progress drag by resetting the item's style and restoring its\n * original position in the list.\n */\n dragend() {\n const item = this.dragItem;\n\n if (item) {\n delete item.dataset.dragging;\n this.reset();\n }\n }\n\n get isDragging() {\n return !!this.dragItem;\n }\n\n get dragItem() {\n return this.element.querySelector(\"[data-dragging]\");\n }\n\n reindex() {\n this.dispatch(\"reindex\", { bubbles: true, prefix: \"navigation\" });\n }\n\n reset() {\n this.dispatch(\"reset\", { bubbles: true, prefix: \"navigation\" });\n }\n}\n\n/**\n * Swaps two list items.\n *\n * @param target the target element to swap with\n * @param item the item the user is dragging\n */\nfunction swap(target, item) {\n if (!target) return;\n if (target === item) return;\n\n const positionComparison = target.compareDocumentPosition(item);\n if (positionComparison & Node.DOCUMENT_POSITION_FOLLOWING) {\n target.insertAdjacentElement(\"beforebegin\", item);\n } else if (positionComparison & Node.DOCUMENT_POSITION_PRECEDING) {\n target.insertAdjacentElement(\"afterend\", item);\n }\n}\n\n/**\n * Given an event target, return the closest drop target, if any.\n */\nfunction dropTarget(e) {\n return e && e.closest(\"[data-controller='navigation--editor--list'] > *\");\n}\n","import MenuController from \"./editor/menu_controller\";\nimport ItemController from \"./editor/item_controller\";\nimport ItemEditorController from \"./editor/item_editor_controller\";\nimport ListController from \"./editor/list_controller\";\nimport NewItemsController from \"./editor/new_items_controller\";\nimport StatusBarController from \"./editor/status_bar_controller\";\n\nconst Definitions = [\n {\n identifier: \"navigation--editor--menu\",\n controllerConstructor: MenuController,\n },\n {\n identifier: \"navigation--editor--item\",\n controllerConstructor: ItemController,\n },\n {\n identifier: \"navigation--editor--item-editor\",\n controllerConstructor: ItemEditorController,\n },\n {\n identifier: \"navigation--editor--list\",\n controllerConstructor: ListController,\n },\n {\n identifier: \"navigation--editor--new-items\",\n controllerConstructor: NewItemsController,\n },\n {\n identifier: \"navigation--editor--status-bar\",\n controllerConstructor: StatusBarController,\n },\n];\n\nexport { Definitions as default };\n","import { Controller } from \"@hotwired/stimulus\";\nimport Item from \"./item\";\n\nexport default class ItemController extends Controller {\n get item() {\n return new Item(this.li);\n }\n\n get ol() {\n return this.element.closest(\"ol\");\n }\n\n get li() {\n return this.element.closest(\"li\");\n }\n\n connect() {\n if (this.element.dataset.hasOwnProperty(\"delete\")) {\n this.remove();\n }\n // if index is not already set, re-index will set it\n else if (!(this.item.index >= 0)) {\n this.reindex();\n }\n // if item has been replaced via turbo, re-index will run the rules engine\n // update our depth and index with values from the li's data attributes\n else if (this.item.hasItemIdChanged()) {\n this.item.updateAfterChange();\n this.reindex();\n }\n }\n\n remove() {\n // capture ol\n const ol = this.ol;\n // remove self from dom\n this.li.remove();\n // reindex ol\n this.reindex();\n }\n\n reindex() {\n this.dispatch(\"reindex\", { bubbles: true, prefix: \"navigation\" });\n }\n}\n","import { Controller } from \"@hotwired/stimulus\";\nimport Item from \"./item\";\n\nexport default class ItemEditorController extends Controller {\n static targets = [\"dialog\"];\n\n connect() {\n this.element.addEventListener(\"turbo:submit-end\", this.onSubmit);\n }\n\n disconnect() {\n this.element.removeEventListener(\"turbo:submit-end\", this.onSubmit);\n }\n\n dismiss() {\n if (!this.dialogTarget) return;\n if (!this.dialogTarget.open) this.dialogTarget.close();\n\n if (!(\"itemPersisted\" in this.dialogTarget.dataset)) {\n this.#removeTargetItem();\n }\n\n this.element.removeAttribute(\"src\");\n this.dialogTarget.remove();\n }\n\n dialogTargetConnected(dialog) {\n dialog.showModal();\n }\n\n onSubmit = (event) => {\n if (event.detail.success) {\n this.dialogTarget.close();\n this.element.removeAttribute(\"src\");\n this.dialogTarget.remove();\n }\n };\n\n noop() {}\n\n #removeTargetItem() {\n const el = document.getElementById(this.dialogTarget.dataset.itemId);\n const item = new Item(el.closest(\"[data-navigation-item]\"));\n const list = item.node.parentElement;\n\n item.node.remove();\n\n this.dispatch(\"reindex\", {\n target: list,\n bubbles: true,\n prefix: \"navigation\",\n });\n }\n}\n","import { Controller } from \"@hotwired/stimulus\";\n\nconst EDGE_AREA = 24;\n\nexport default class NewItemsController extends Controller {\n static targets = [\"inline\"];\n\n connect() {\n this.form.addEventListener(\"mousemove\", this.move);\n }\n\n disconnect() {\n this.form?.removeEventListener(\"mousemove\", this.move);\n delete this.currentItem;\n }\n\n open(e) {\n e.preventDefault();\n this.dialog.showModal();\n }\n\n close(e) {\n e.preventDefault();\n this.dialog.close();\n }\n\n noop(e) {}\n\n /**\n * Add the selected item to the DOM at the current position or the end of the list.\n */\n add(e) {\n e.preventDefault();\n\n const template = e.target.querySelector(\"template\");\n const item = template.content.querySelector(\"li\").cloneNode(true);\n const target = this.currentItem;\n\n if (target) {\n target.insertAdjacentElement(\"beforebegin\", item);\n } else {\n this.list.insertAdjacentElement(\"beforeend\", item);\n }\n\n this.toggleInline(false);\n this.dialog.close();\n\n requestAnimationFrame(() => {\n item.querySelector(`[value=\"edit\"]`).click();\n });\n }\n\n morph(e) {\n e.preventDefault();\n this.dialog.close();\n }\n\n move = (e) => {\n if (this.isOverInlineTarget(e)) return;\n if (this.dialog.open) return;\n\n const target = this.getCurrentItem(e);\n\n // return if we're already showing this item\n if (this.currentItem === target) return;\n\n // hide the button if it's already visible\n if (this.currentItem) this.toggleInline(false);\n\n this.currentItem = target;\n\n // clear any previously set timer\n if (this.timer) clearTimeout(this.timer);\n\n // show the button after a debounce pause\n this.timer = setTimeout(() => {\n delete this.timer;\n this.toggleInline();\n }, 100);\n };\n\n toggleInline(show = !!this.currentItem) {\n if (show) {\n this.inlineTarget.style.top = `${this.currentItem.offsetTop}px`;\n this.inlineTarget.toggleAttribute(\"hidden\", false);\n } else {\n this.inlineTarget.toggleAttribute(\"hidden\", true);\n }\n }\n\n get dialog() {\n return this.element.querySelector(\"dialog\");\n }\n\n /**\n * @returns {HTMLFormElement}\n */\n get form() {\n return this.element.closest(\"form\");\n }\n\n /**\n * @returns {HTMLUListElement,null}\n */\n get list() {\n return this.form.querySelector(\n `[data-controller=\"navigation--editor--list\"]`,\n );\n }\n\n /**\n * @param {MouseEvent} e\n * @returns {HTMLLIElement,null}\n */\n getCurrentItem(e) {\n const item = document.elementFromPoint(e.clientX, e.clientY).closest(\"li\");\n if (!item) return null;\n\n const bounds = item.getBoundingClientRect();\n\n // check X for center(ish) mouse position\n if (e.clientX < bounds.left + bounds.width / 2 - 2 * EDGE_AREA) return null;\n if (e.clientX > bounds.left + bounds.width / 2 + 2 * EDGE_AREA) return null;\n\n // check Y for hits on this item or it's next sibling\n if (e.clientY - bounds.y <= EDGE_AREA) {\n return item;\n } else if (bounds.y + bounds.height - e.clientY <= EDGE_AREA) {\n return item.nextElementSibling;\n } else {\n return null;\n }\n }\n\n /**\n * @param {MouseEvent} e\n * @returns {Boolean} true when the target of the event is the floating button\n */\n isOverInlineTarget(e) {\n return (\n this.inlineTarget ===\n document.elementFromPoint(e.clientX, e.clientY).closest(\"div\")\n );\n }\n}\n","import { Controller } from \"@hotwired/stimulus\";\n\nexport default class StatusBarController extends Controller {\n connect() {\n // cache the version's state in the controller on connect\n this.versionState = this.element.dataset.state;\n }\n\n morph(e) {\n if (e.target !== this.element) return;\n\n this.versionState = this.element.dataset.state;\n this.update({ dirty: false });\n }\n\n change(e) {\n if (e.detail && e.detail.hasOwnProperty(\"dirty\")) {\n this.update(e.detail);\n }\n }\n\n update({ dirty }) {\n if (dirty) {\n this.element.dataset.state = \"dirty\";\n } else {\n this.element.dataset.state = this.versionState;\n }\n }\n}\n"],"names":["Item","comparator","a","b","index","constructor","node","this","itemId","dataset","itemIdInput","querySelector","id","value","depth","parseInt","depthInput","indexInput","isLayout","hasAttribute","previousItem","sibling","previousElementSibling","nextItem","nextElementSibling","hasCollapsedDescendants","childrenList","childrenListElement","children","length","hasExpandedDescendants","traverse","callback","expanded","expandedDescendants","traverseCollapsed","forEach","item","collapsedDescendants","collapse","listElement","document","createElement","toggleAttribute","appendChild","createChildrenList","child","expand","Array","from","reverse","insertAdjacentElement","toggleRule","rule","deny","hasOwnProperty","setAttribute","removeAttribute","hasItemIdChanged","updateAfterChange","descendants","push","map","Menu","items","nodes","querySelectorAll","state","inputs","e","join","reindex","reset","sort","RulesEngine","static","maxDepth","debug","args","console","log","normalize","firstItemDepthZero","depthMustBeSet","itemCannotHaveInvalidDepth","itemCannotExceedDepthLimit","parentMustBeLayout","parentCannotHaveExpandedAndCollapsedChildren","update","rules","parentsCannotDeNest","rootsCannotDeNest","nestingNeedsParent","nestingCannotExceedMaxDepth","leavesCannotCollapse","needHiddenItemsToExpand","parentsCannotBeDeleted","parentsCannotBeDragged","isNaN","previous","getEventItem","event","target","closest","swap","positionComparison","compareDocumentPosition","Node","DOCUMENT_POSITION_FOLLOWING","DOCUMENT_POSITION_PRECEDING","dropTarget","Definitions","identifier","controllerConstructor","Controller","Number","connect","menu","menuTarget","drop","delta","undefined","preventDefault","remove","nest","deNest","updateRequested","setTimeout","engine","maxDepthValue","notifyChange","dispatch","bubbles","prefix","detail","dirty","isDirty","li","ol","element","addEventListener","onSubmit","disconnect","removeEventListener","dismiss","dialogTarget","open","close","removeTargetItem","dialogTargetConnected","dialog","showModal","success","noop","el","getElementById","list","parentElement","dragstart","dataTransfer","effectAllowed","requestAnimationFrame","dragging","dragover","dragItem","dragend","isDragging","form","move","currentItem","add","content","cloneNode","toggleInline","click","morph","isOverInlineTarget","getCurrentItem","timer","clearTimeout","show","inlineTarget","style","top","offsetTop","elementFromPoint","clientX","clientY","bounds","getBoundingClientRect","left","width","y","height","versionState","change"],"mappings":"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"}
|
1
|
+
{"version":3,"file":"navigation.min.js","sources":["../../../javascript/navigation/editor/item.js","../../../javascript/navigation/editor/menu.js","../../../javascript/navigation/editor/rules-engine.js","../../../javascript/navigation/editor/menu_controller.js","../../../javascript/navigation/editor/list_controller.js","../../../javascript/navigation/application.js","../../../javascript/navigation/editor/item_controller.js","../../../javascript/navigation/editor/item_editor_controller.js","../../../javascript/navigation/editor/new_items_controller.js","../../../javascript/navigation/editor/status_bar_controller.js"],"sourcesContent":["export default class Item {\n /**\n * Sort items by their index.\n *\n * @param a {Item}\n * @param b {Item}\n * @returns {number}\n */\n static comparator(a, b) {\n return a.index - b.index;\n }\n\n /**\n * @param node {Element} li[data-navigation-index]\n */\n constructor(node) {\n this.node = node;\n }\n\n /**\n * @returns {String} id of the node's item (from data attributes)\n */\n get itemId() {\n return this.node.dataset[`navigationItemId`];\n }\n\n get #itemIdInput() {\n return this.node.querySelector(`input[name$=\"[id]\"]`);\n }\n\n /**\n * @param itemId {String} id\n */\n set itemId(id) {\n if (this.itemId === id) return;\n\n this.node.dataset[`navigationItemId`] = `${id}`;\n this.#itemIdInput.value = `${id}`;\n }\n\n /**\n * @returns {number} logical nesting depth of node in menu\n */\n get depth() {\n return parseInt(this.node.dataset[`navigationDepth`]) || 0;\n }\n\n get #depthInput() {\n return this.node.querySelector(`input[name$=\"[depth]\"]`);\n }\n\n /**\n * @param depth {number} depth >= 0\n */\n set depth(depth) {\n if (this.depth === depth) return;\n\n this.node.dataset[`navigationDepth`] = `${depth}`;\n this.#depthInput.value = `${depth}`;\n }\n\n /**\n * @returns {number} logical index of node in menu (pre-order traversal)\n */\n get index() {\n return parseInt(this.node.dataset[`navigationIndex`]);\n }\n\n get #indexInput() {\n return this.node.querySelector(`input[name$=\"[index]\"]`);\n }\n\n /**\n * @param index {number} index >= 0\n */\n set index(index) {\n if (this.index === index) return;\n\n this.node.dataset[`navigationIndex`] = `${index}`;\n this.#indexInput.value = `${index}`;\n }\n\n /**\n * @returns {boolean} true if this item can have children\n */\n get isLayout() {\n return this.node.hasAttribute(\"data-content-layout\");\n }\n\n /**\n * @returns {Item} nearest neighbour (index - 1)\n */\n get previousItem() {\n let sibling = this.node.previousElementSibling;\n if (sibling) return new Item(sibling);\n }\n\n /**\n * @returns {Item} nearest neighbour (index + 1)\n */\n get nextItem() {\n let sibling = this.node.nextElementSibling;\n if (sibling) return new Item(sibling);\n }\n\n /**\n * @returns {boolean} true if this item has any collapsed children\n */\n hasCollapsedDescendants() {\n let childrenList = this.#childrenListElement;\n return !!childrenList && childrenList.children.length > 0;\n }\n\n /**\n * @returns {boolean} true if this item has any expanded children\n */\n hasExpandedDescendants() {\n let sibling = this.nextItem;\n return !!sibling && sibling.depth > this.depth;\n }\n\n /**\n * Recursively traverse the node and its descendants.\n *\n * @callback {Item}\n */\n traverse(callback) {\n // capture descendants before traversal in case of side-effects\n // specifically, setting depth affects calculation\n const expanded = this.#expandedDescendants;\n\n callback(this);\n this.#traverseCollapsed(callback);\n expanded.forEach((item) => item.#traverseCollapsed(callback));\n }\n\n /**\n * Recursively traverse the node's collapsed descendants, if any.\n *\n * @callback {Item}\n */\n #traverseCollapsed(callback) {\n if (!this.hasCollapsedDescendants()) return;\n\n this.#collapsedDescendants.forEach((item) => {\n callback(item);\n item.#traverseCollapsed(callback);\n });\n }\n\n /**\n * Collapses visible (logical) children into this element's hidden children\n * list, creating it if it doesn't already exist.\n */\n collapse() {\n let listElement = this.#childrenListElement;\n\n if (!listElement) listElement = createChildrenList(this.node);\n\n this.#expandedDescendants.forEach((child) =>\n listElement.appendChild(child.node),\n );\n }\n\n /**\n * Moves any collapsed children back into the parent menu.\n */\n expand() {\n if (!this.hasCollapsedDescendants()) return;\n\n Array.from(this.#childrenListElement.children)\n .reverse()\n .forEach((node) => {\n this.node.insertAdjacentElement(\"afterend\", node);\n });\n }\n\n /**\n * Sets the state of a given rule on the target node.\n *\n * @param rule {String}\n * @param deny {boolean}\n */\n toggleRule(rule, deny = false) {\n if (this.node.dataset.hasOwnProperty(rule) && !deny) {\n delete this.node.dataset[rule];\n }\n if (!this.node.dataset.hasOwnProperty(rule) && deny) {\n this.node.dataset[rule] = \"\";\n }\n\n if (rule === \"denyDrag\") {\n if (!this.node.hasAttribute(\"draggable\") && !deny) {\n this.node.setAttribute(\"draggable\", \"true\");\n }\n if (this.node.hasAttribute(\"draggable\") && deny) {\n this.node.removeAttribute(\"draggable\");\n }\n }\n }\n\n /**\n * Detects turbo item changes by comparing the dataset id with the input\n */\n hasItemIdChanged() {\n return !(this.#itemIdInput.value === this.itemId);\n }\n\n /**\n * Updates inputs, in case they don't match the data values, e.g., when the\n * nested inputs have been hot-swapped by turbo with data from the server.\n *\n * Updates itemId from input as that is the canonical source.\n */\n updateAfterChange() {\n this.itemId = this.#itemIdInput.value;\n this.#indexInput.value = this.index;\n this.#depthInput.value = this.depth;\n }\n\n /**\n * Finds the dom container for storing collapsed (hidden) children, if present.\n *\n * @returns {Element} ol[data-navigation-children]\n */\n get #childrenListElement() {\n return this.node.querySelector(`:scope > [data-navigation-children]`);\n }\n\n /**\n * @returns {Item[]} all items that follow this element that have a greater depth.\n */\n get #expandedDescendants() {\n const descendants = [];\n\n let sibling = this.nextItem;\n while (sibling && sibling.depth > this.depth) {\n descendants.push(sibling);\n sibling = sibling.nextItem;\n }\n\n return descendants;\n }\n\n /**\n * @returns {Item[]} all items directly contained inside this element's hidden children element.\n */\n get #collapsedDescendants() {\n if (!this.hasCollapsedDescendants()) return [];\n\n return Array.from(this.#childrenListElement.children).map(\n (node) => new Item(node),\n );\n }\n}\n\n/**\n * Finds or creates a dom container for storing collapsed (hidden) children.\n *\n * @param node {Element} li[data-navigation-index]\n * @returns {Element} ol[data-navigation-children]\n */\nfunction createChildrenList(node) {\n const childrenList = document.createElement(\"ol\");\n childrenList.toggleAttribute(\"hidden\", true);\n\n childrenList.dataset[`navigationChildren`] = \"\";\n\n node.appendChild(childrenList);\n\n return childrenList;\n}\n","import Item from \"./item\";\n\n/**\n * @param nodes {NodeList}\n * @returns {Item[]}\n */\nfunction createItemList(nodes) {\n return Array.from(nodes).map((node) => new Item(node));\n}\n\nexport default class Menu {\n /**\n * @param node {Element} navigation editor list\n */\n constructor(node) {\n this.node = node;\n }\n\n /**\n * @return {Item[]} an ordered list of all items in the menu\n */\n get items() {\n return createItemList(\n this.node.querySelectorAll(\"[data-navigation-index]\"),\n );\n }\n\n /**\n * @return {String} a serialized description of the structure of the menu\n */\n get state() {\n const inputs = this.node.querySelectorAll(\"li input[type=hidden]\");\n return Array.from(inputs)\n .map((e) => e.value)\n .join(\"/\");\n }\n\n /**\n * Set the index of items based on their current position.\n */\n reindex() {\n this.items.map((item, index) => (item.index = index));\n }\n\n /**\n * Resets the order of items to their defined index.\n * Useful after an aborted drag.\n */\n reset() {\n this.items.sort(Item.comparator).forEach((item) => {\n this.node.appendChild(item.node);\n });\n }\n}\n","export default class RulesEngine {\n static rules = [\n \"denyDeNest\",\n \"denyNest\",\n \"denyCollapse\",\n \"denyExpand\",\n \"denyRemove\",\n \"denyDrag\",\n \"denyEdit\",\n ];\n\n constructor(maxDepth = null, debug = false) {\n this.maxDepth = maxDepth;\n if (debug) {\n this.debug = (...args) => console.log(...args);\n } else {\n this.debug = () => {};\n }\n }\n\n /**\n * Enforce structural rules to ensure that the given item is currently in a\n * valid state.\n *\n * @param {Item} item\n */\n normalize(item) {\n // structural rules enforce a valid tree structure\n this.firstItemDepthZero(item);\n this.depthMustBeSet(item);\n this.itemCannotHaveInvalidDepth(item);\n this.itemCannotExceedDepthLimit(item);\n this.parentMustBeLayout(item);\n this.parentCannotHaveExpandedAndCollapsedChildren(item);\n }\n\n /**\n * Apply rules to the given item to determine what operations are permitted.\n *\n * @param {Item} item\n */\n update(item) {\n this.rules = {};\n\n // behavioural rules define what the user is allowed to do\n this.parentsCannotDeNest(item);\n this.rootsCannotDeNest(item);\n this.nestingNeedsParent(item);\n this.nestingCannotExceedMaxDepth(item);\n this.leavesCannotCollapse(item);\n this.needHiddenItemsToExpand(item);\n this.parentsCannotBeDeleted(item);\n this.parentsCannotBeDragged(item);\n\n RulesEngine.rules.forEach((rule) => {\n item.toggleRule(rule, !!this.rules[rule]);\n });\n }\n\n /**\n * First item can't have a parent, so its depth should always be 0\n */\n firstItemDepthZero(item) {\n if (item.index === 0 && item.depth !== 0) {\n this.debug(`enforce depth on item ${item.index}: ${item.depth} => 0`);\n\n item.depth = 0;\n }\n }\n\n /**\n * Every item should have a non-negative depth set.\n *\n * @param {Item} item\n */\n depthMustBeSet(item) {\n if (isNaN(item.depth) || item.depth < 0) {\n this.debug(`unset depth on item ${item.index}: => 0`);\n\n item.depth = 0;\n }\n }\n\n /**\n * Depth must increase stepwise.\n *\n * @param {Item} item\n */\n itemCannotHaveInvalidDepth(item) {\n const previous = item.previousItem;\n if (previous && previous.depth < item.depth - 1) {\n this.debug(\n `invalid depth on item ${item.index}: ${item.depth} => ${\n previous.depth + 1\n }`,\n );\n\n item.depth = previous.depth + 1;\n }\n }\n\n /**\n * Depth must not exceed menu's depth limit.\n *\n * @param {Item} item\n */\n itemCannotExceedDepthLimit(item) {\n if (this.maxDepth > 0 && this.maxDepth <= item.depth) {\n // Note: this change can cause an issue where the previous item is treated\n // like a parent even though it no longer has children. This is because\n // items are processed in order. This issue does not seem worth solving\n // as it only occurs if the max depth is altered. The issue can be worked\n // around by saving the menu.\n item.depth = this.maxDepth - 1;\n }\n }\n\n /**\n * Parent item, if any, must be a layout.\n *\n * @param {Item} item\n */\n parentMustBeLayout(item) {\n // if we're the first child, make sure our parent is a layout\n // if we're a sibling, we know the previous item is valid so we must be too\n const previous = item.previousItem;\n if (previous && previous.depth < item.depth && !previous.isLayout) {\n this.debug(\n `invalid parent for item ${item.index}: ${item.depth} => ${previous.depth}`,\n );\n\n item.depth = previous.depth;\n }\n }\n\n /**\n * If a parent has expanded and collapsed children, expand.\n *\n * @param {Item} item\n */\n parentCannotHaveExpandedAndCollapsedChildren(item) {\n if (item.hasCollapsedDescendants() && item.hasExpandedDescendants()) {\n this.debug(`expanding collapsed children of item ${item.index}`);\n\n item.expand();\n }\n }\n\n /**\n * De-nesting an item would create a gap of 2 between itself and its children\n *\n * @param {Item} item\n */\n parentsCannotDeNest(item) {\n if (item.hasExpandedDescendants()) this.#deny(\"denyDeNest\");\n }\n\n /**\n * Item depth can't go below 0.\n *\n * @param {Item} item\n */\n rootsCannotDeNest(item) {\n if (item.depth === 0) this.#deny(\"denyDeNest\");\n }\n\n /**\n * If an item doesn't have children it can't be collapsed.\n *\n * @param {Item} item\n */\n leavesCannotCollapse(item) {\n if (!item.hasExpandedDescendants()) this.#deny(\"denyCollapse\");\n }\n\n /**\n * If an item doesn't have any hidden descendants then it can't be expanded.\n *\n * @param {Item} item\n */\n needHiddenItemsToExpand(item) {\n if (!item.hasCollapsedDescendants()) this.#deny(\"denyExpand\");\n }\n\n /**\n * An item can't be nested (indented) if it doesn't have a valid parent.\n *\n * @param {Item} item\n */\n nestingNeedsParent(item) {\n const previous = item.previousItem;\n // no previous, so cannot nest\n if (!previous) this.#deny(\"denyNest\");\n // previous is too shallow, nesting would increase depth too much\n else if (previous.depth < item.depth) this.#deny(\"denyNest\");\n // new parent is not a layout\n else if (previous.depth === item.depth && !previous.isLayout)\n this.#deny(\"denyNest\");\n }\n\n /**\n * An item can't be nested (indented) if doing so would exceed the max depth.\n *\n * @param {Item} item\n */\n nestingCannotExceedMaxDepth(item) {\n if (this.maxDepth > 0 && this.maxDepth <= item.depth + 1) {\n this.#deny(\"denyNest\");\n }\n }\n\n /**\n * An item can't be deleted if it has visible children.\n *\n * @param {Item} item\n */\n parentsCannotBeDeleted(item) {\n if (!item.itemId || item.hasExpandedDescendants()) this.#deny(\"denyRemove\");\n }\n\n /**\n * Items cannot be dragged if they have visible children.\n *\n * @param {Item} item\n */\n parentsCannotBeDragged(item) {\n if (item.hasExpandedDescendants()) this.#deny(\"denyDrag\");\n }\n\n /**\n * Record a deny.\n *\n * @param rule {String}\n */\n #deny(rule) {\n this.rules[rule] = true;\n }\n}\n","import { Controller } from \"@hotwired/stimulus\";\n\nimport Item from \"./item\";\nimport Menu from \"./menu\";\nimport RulesEngine from \"./rules-engine\";\n\nexport default class MenuController extends Controller {\n static targets = [\"menu\"];\n static values = {\n maxDepth: Number,\n };\n\n // Caution: connect is called on attachment, but also on morph/render\n connect() {\n this.state = this.menu.state;\n\n this.reindex();\n }\n\n get menu() {\n return new Menu(this.menuTarget);\n }\n\n reindex() {\n this.menu.reindex();\n this.#update();\n }\n\n reset() {\n this.menu.reset();\n }\n\n drop(event) {\n this.menu.reindex(); // set indexes before calculating previous\n\n const item = getEventItem(event);\n const previous = item.previousItem;\n\n let delta = 0;\n if (previous === undefined) {\n // if previous does not exist, set depth to 0\n delta = -item.depth;\n } else if (item.nextItem && item.nextItem.depth > previous.depth) {\n // if next is a child of previous, make item a child of previous\n delta = previous.depth - item.depth + 1;\n } else {\n // otherwise, make item a sibling of previous\n delta = previous.depth - item.depth;\n }\n\n item.traverse((child) => {\n child.depth += delta;\n });\n\n this.#update();\n event.preventDefault();\n }\n\n remove(event) {\n const item = getEventItem(event);\n\n item.node.remove();\n\n this.#update();\n event.preventDefault();\n }\n\n nest(event) {\n const item = getEventItem(event);\n\n item.traverse((child) => {\n child.depth += 1;\n });\n\n this.#update();\n event.preventDefault();\n }\n\n deNest(event) {\n const item = getEventItem(event);\n\n item.traverse((child) => {\n child.depth -= 1;\n });\n\n this.#update();\n event.preventDefault();\n }\n\n collapse(event) {\n const item = getEventItem(event);\n\n item.collapse();\n\n this.#update();\n event.preventDefault();\n }\n\n expand(event) {\n const item = getEventItem(event);\n\n item.expand();\n\n this.#update();\n event.preventDefault();\n }\n\n /**\n * Re-apply rules to items to enable/disable appropriate actions.\n */\n #update() {\n // debounce requests to ensure that we only update once per tick\n this.updateRequested = true;\n setTimeout(() => {\n if (!this.updateRequested) return;\n\n this.updateRequested = false;\n const engine = new RulesEngine(this.maxDepthValue);\n this.menu.items.forEach((item) => engine.normalize(item));\n this.menu.items.forEach((item) => engine.update(item));\n\n this.#notifyChange();\n }, 0);\n }\n\n #notifyChange() {\n this.dispatch(\"change\", {\n bubbles: true,\n prefix: \"navigation\",\n detail: { dirty: this.#isDirty() },\n });\n }\n\n #isDirty() {\n return this.menu.state !== this.state;\n }\n}\n\nfunction getEventItem(event) {\n return new Item(event.target.closest(\"[data-navigation-item]\"));\n}\n","import { Controller } from \"@hotwired/stimulus\";\n\nexport default class ListController extends Controller {\n /**\n * When the user starts a drag within the list, set the item's dataTransfer\n * properties to indicate that it's being dragged and update its style.\n *\n * We delay setting the dataset property until the next animation frame\n * so that the style updates can be computed before the drag begins.\n *\n * @param event {DragEvent}\n */\n dragstart(event) {\n if (this.element !== event.target.parentElement) return;\n\n const target = event.target;\n event.dataTransfer.effectAllowed = \"move\";\n\n // update element style after drag has begun\n requestAnimationFrame(() => (target.dataset.dragging = \"\"));\n }\n\n /**\n * When the user drags an item over another item in the last, swap the\n * dragging item with the item under the cursor.\n *\n * @param event {DragEvent}\n */\n dragover(event) {\n const item = this.dragItem;\n if (!item) return;\n\n swap(dropTarget(event.target), item);\n\n event.preventDefault();\n return true;\n }\n\n /**\n * When the user drops an item, end the drag and reindex the list.\n *\n * @param event {DragEvent}\n */\n drop(event) {\n let item = this.dragItem;\n\n if (!item) return;\n\n event.preventDefault();\n delete item.dataset.dragging;\n swap(dropTarget(event.target), item);\n\n this.dispatch(\"drop\", {\n target: item,\n bubbles: true,\n prefix: \"navigation\",\n });\n }\n\n /**\n * End an in-progress drag by resetting the item's style and restoring its\n * original position in the list.\n */\n dragend() {\n const item = this.dragItem;\n\n if (item) {\n delete item.dataset.dragging;\n this.reset();\n }\n }\n\n get isDragging() {\n return !!this.dragItem;\n }\n\n get dragItem() {\n return this.element.querySelector(\"[data-dragging]\");\n }\n\n reindex() {\n this.dispatch(\"reindex\", { bubbles: true, prefix: \"navigation\" });\n }\n\n reset() {\n this.dispatch(\"reset\", { bubbles: true, prefix: \"navigation\" });\n }\n}\n\n/**\n * Swaps two list items.\n *\n * @param target the target element to swap with\n * @param item the item the user is dragging\n */\nfunction swap(target, item) {\n if (!target) return;\n if (target === item) return;\n\n const positionComparison = target.compareDocumentPosition(item);\n if (positionComparison & Node.DOCUMENT_POSITION_FOLLOWING) {\n target.insertAdjacentElement(\"beforebegin\", item);\n } else if (positionComparison & Node.DOCUMENT_POSITION_PRECEDING) {\n target.insertAdjacentElement(\"afterend\", item);\n }\n}\n\n/**\n * Given an event target, return the closest drop target, if any.\n */\nfunction dropTarget(e) {\n return e && e.closest(\"[data-controller='navigation--editor--list'] > *\");\n}\n","import MenuController from \"./editor/menu_controller\";\nimport ItemController from \"./editor/item_controller\";\nimport ItemEditorController from \"./editor/item_editor_controller\";\nimport ListController from \"./editor/list_controller\";\nimport NewItemsController from \"./editor/new_items_controller\";\nimport StatusBarController from \"./editor/status_bar_controller\";\n\nconst Definitions = [\n {\n identifier: \"navigation--editor--menu\",\n controllerConstructor: MenuController,\n },\n {\n identifier: \"navigation--editor--item\",\n controllerConstructor: ItemController,\n },\n {\n identifier: \"navigation--editor--item-editor\",\n controllerConstructor: ItemEditorController,\n },\n {\n identifier: \"navigation--editor--list\",\n controllerConstructor: ListController,\n },\n {\n identifier: \"navigation--editor--new-items\",\n controllerConstructor: NewItemsController,\n },\n {\n identifier: \"navigation--editor--status-bar\",\n controllerConstructor: StatusBarController,\n },\n];\n\nexport { Definitions as default };\n","import { Controller } from \"@hotwired/stimulus\";\nimport Item from \"./item\";\n\nexport default class ItemController extends Controller {\n get item() {\n return new Item(this.li);\n }\n\n get ol() {\n return this.element.closest(\"ol\");\n }\n\n get li() {\n return this.element.closest(\"li\");\n }\n\n connect() {\n if (this.element.dataset.hasOwnProperty(\"delete\")) {\n this.remove();\n }\n // if index is not already set, re-index will set it\n else if (!(this.item.index >= 0)) {\n this.reindex();\n }\n // if item has been replaced via turbo, re-index will run the rules engine\n // update our depth and index with values from the li's data attributes\n else if (this.item.hasItemIdChanged()) {\n this.item.updateAfterChange();\n this.reindex();\n }\n }\n\n remove() {\n // capture ol\n const ol = this.ol;\n // remove self from dom\n this.li.remove();\n // reindex ol\n this.reindex();\n }\n\n reindex() {\n this.dispatch(\"reindex\", { bubbles: true, prefix: \"navigation\" });\n }\n}\n","import { Controller } from \"@hotwired/stimulus\";\nimport Item from \"./item\";\n\nexport default class ItemEditorController extends Controller {\n static targets = [\"dialog\"];\n\n connect() {\n this.element.addEventListener(\"turbo:submit-end\", this.onSubmit);\n }\n\n disconnect() {\n this.element.removeEventListener(\"turbo:submit-end\", this.onSubmit);\n }\n\n click(e) {\n if (e.target.tagName === \"DIALOG\") this.dismiss();\n }\n\n dismiss() {\n if (!this.dialogTarget) return;\n if (!this.dialogTarget.open) this.dialogTarget.close();\n\n if (!(\"itemPersisted\" in this.dialogTarget.dataset)) {\n this.#removeTargetItem();\n }\n\n this.element.removeAttribute(\"src\");\n this.dialogTarget.remove();\n }\n\n dialogTargetConnected(dialog) {\n dialog.showModal();\n }\n\n onSubmit = (event) => {\n if (\n event.detail.success &&\n \"closeDialog\" in event.detail.formSubmission?.submitter?.dataset\n ) {\n this.dialogTarget.close();\n this.element.removeAttribute(\"src\");\n this.dialogTarget.remove();\n }\n };\n\n #removeTargetItem() {\n const el = document.getElementById(this.dialogTarget.dataset.itemId);\n const item = new Item(el.closest(\"[data-navigation-item]\"));\n const list = item.node.parentElement;\n\n item.node.remove();\n\n this.dispatch(\"reindex\", {\n target: list,\n bubbles: true,\n prefix: \"navigation\",\n });\n }\n}\n","import { Controller } from \"@hotwired/stimulus\";\n\nimport Item from \"./item\";\n\nconst EDGE_AREA = 24;\n\nexport default class NewItemsController extends Controller {\n static targets = [\"inline\"];\n\n connect() {\n this.form.addEventListener(\"mousemove\", this.move);\n }\n\n disconnect() {\n this.form?.removeEventListener(\"mousemove\", this.move);\n delete this.currentItem;\n }\n\n click(e) {\n if (e.target.tagName === \"DIALOG\") this.close(e);\n }\n\n open(e) {\n e.preventDefault();\n this.dialog.showModal();\n }\n\n close(e) {\n e.preventDefault();\n this.dialog.close();\n }\n\n /**\n * Add the selected item to the DOM at the current position or the end of the list.\n */\n add(e) {\n e.preventDefault();\n\n const template = e.target.closest(\"li\").querySelector(\"template\");\n const item = template.content.querySelector(\"li\").cloneNode(true);\n const target = this.currentItem;\n\n if (target) {\n target.insertAdjacentElement(\"beforebegin\", item);\n new Item(item).depth = new Item(target).depth;\n } else {\n this.list.insertAdjacentElement(\"beforeend\", item);\n }\n\n this.toggleInline(false);\n this.dialog.close();\n\n requestAnimationFrame(() => {\n item.querySelector(`[value=\"edit\"]`).click();\n });\n }\n\n morph(e) {\n e.preventDefault();\n this.dialog.close();\n }\n\n move = (e) => {\n if (this.isOverInlineTarget(e)) return;\n if (this.dialog.open) return;\n\n const target = this.getCurrentItem(e);\n\n // return if we're already showing this item\n if (this.currentItem === target) return;\n\n // hide the button if it's already visible\n if (this.currentItem) this.toggleInline(false);\n\n this.currentItem = target;\n\n // clear any previously set timer\n if (this.timer) clearTimeout(this.timer);\n\n // show the button after a debounce pause\n this.timer = setTimeout(() => {\n delete this.timer;\n this.toggleInline();\n }, 100);\n };\n\n toggleInline(show = !!this.currentItem) {\n if (show) {\n this.inlineTarget.style.top = `${this.currentItem.offsetTop}px`;\n this.inlineTarget.toggleAttribute(\"hidden\", false);\n } else {\n this.inlineTarget.toggleAttribute(\"hidden\", true);\n }\n }\n\n get dialog() {\n return this.element.querySelector(\"dialog\");\n }\n\n /**\n * @returns {HTMLFormElement}\n */\n get form() {\n return this.element.closest(\"form\");\n }\n\n /**\n * @returns {HTMLUListElement,null}\n */\n get list() {\n return this.form.querySelector(\n `[data-controller=\"navigation--editor--list\"]`,\n );\n }\n\n /**\n * @param {MouseEvent} e\n * @returns {HTMLLIElement,null}\n */\n getCurrentItem(e) {\n const item = document.elementFromPoint(e.clientX, e.clientY).closest(\"li\");\n if (!item) return null;\n\n const bounds = item.getBoundingClientRect();\n\n // check X for center(ish) mouse position\n if (e.clientX < bounds.left + bounds.width / 2 - 2 * EDGE_AREA) return null;\n if (e.clientX > bounds.left + bounds.width / 2 + 2 * EDGE_AREA) return null;\n\n // check Y for hits on this item or it's next sibling\n if (e.clientY - bounds.y <= EDGE_AREA) {\n return item;\n } else if (bounds.y + bounds.height - e.clientY <= EDGE_AREA) {\n return item.nextElementSibling;\n } else {\n return null;\n }\n }\n\n /**\n * @param {MouseEvent} e\n * @returns {Boolean} true when the target of the event is the floating button\n */\n isOverInlineTarget(e) {\n return (\n this.inlineTarget ===\n document.elementFromPoint(e.clientX, e.clientY).closest(\"div\")\n );\n }\n}\n","import { Controller } from \"@hotwired/stimulus\";\n\nexport default class StatusBarController extends Controller {\n connect() {\n // cache the version's state in the controller on connect\n this.versionState = this.element.dataset.state;\n }\n\n morph(e) {\n if (e.target !== this.element) return;\n\n this.versionState = this.element.dataset.state;\n this.update({ dirty: false });\n }\n\n change(e) {\n if (e.detail && e.detail.hasOwnProperty(\"dirty\")) {\n this.update(e.detail);\n }\n }\n\n update({ dirty }) {\n if (dirty) {\n this.element.dataset.state = \"dirty\";\n } else {\n this.element.dataset.state = this.versionState;\n }\n }\n}\n"],"names":["Item","comparator","a","b","index","constructor","node","this","itemId","dataset","itemIdInput","querySelector","id","value","depth","parseInt","depthInput","indexInput","isLayout","hasAttribute","previousItem","sibling","previousElementSibling","nextItem","nextElementSibling","hasCollapsedDescendants","childrenList","childrenListElement","children","length","hasExpandedDescendants","traverse","callback","expanded","expandedDescendants","traverseCollapsed","forEach","item","collapsedDescendants","collapse","listElement","document","createElement","toggleAttribute","appendChild","createChildrenList","child","expand","Array","from","reverse","insertAdjacentElement","toggleRule","rule","deny","hasOwnProperty","setAttribute","removeAttribute","hasItemIdChanged","updateAfterChange","descendants","push","map","Menu","items","nodes","querySelectorAll","state","inputs","e","join","reindex","reset","sort","RulesEngine","static","maxDepth","debug","args","console","log","normalize","firstItemDepthZero","depthMustBeSet","itemCannotHaveInvalidDepth","itemCannotExceedDepthLimit","parentMustBeLayout","parentCannotHaveExpandedAndCollapsedChildren","update","rules","parentsCannotDeNest","rootsCannotDeNest","nestingNeedsParent","nestingCannotExceedMaxDepth","leavesCannotCollapse","needHiddenItemsToExpand","parentsCannotBeDeleted","parentsCannotBeDragged","isNaN","previous","getEventItem","event","target","closest","swap","positionComparison","compareDocumentPosition","Node","DOCUMENT_POSITION_FOLLOWING","DOCUMENT_POSITION_PRECEDING","dropTarget","Definitions","identifier","controllerConstructor","Controller","Number","connect","menu","menuTarget","drop","delta","undefined","preventDefault","remove","nest","deNest","updateRequested","setTimeout","engine","maxDepthValue","notifyChange","dispatch","bubbles","prefix","detail","dirty","isDirty","li","ol","element","addEventListener","onSubmit","disconnect","removeEventListener","click","tagName","dismiss","dialogTarget","open","close","removeTargetItem","dialogTargetConnected","dialog","showModal","success","formSubmission","submitter","el","getElementById","list","parentElement","dragstart","dataTransfer","effectAllowed","requestAnimationFrame","dragging","dragover","dragItem","dragend","isDragging","form","move","currentItem","add","content","cloneNode","toggleInline","morph","isOverInlineTarget","getCurrentItem","timer","clearTimeout","show","inlineTarget","style","top","offsetTop","elementFromPoint","clientX","clientY","bounds","getBoundingClientRect","left","width","y","height","versionState","change"],"mappings":"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"}
|
@@ -18,8 +18,10 @@
|
|
18
18
|
<span class="visually-hidden">Add item here</span>
|
19
19
|
</button>
|
20
20
|
</div>
|
21
|
-
<dialog id="navigation--editor--new-items-dialog"
|
22
|
-
|
21
|
+
<dialog id="navigation--editor--new-items-dialog"
|
22
|
+
class="modal"
|
23
|
+
data-action="click->navigation--editor--new-items#click">
|
24
|
+
<article class="flow">
|
23
25
|
<header class="repel" data-nowrap>
|
24
26
|
<h2>Add item</h2>
|
25
27
|
<button class="button"
|
@@ -31,11 +33,9 @@
|
|
31
33
|
</button>
|
32
34
|
</header>
|
33
35
|
<main>
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
</ul>
|
38
|
-
<% end %>
|
36
|
+
<ul role="list" class="items-list">
|
37
|
+
<%= render Katalyst::Navigation::Editor::NewItemComponent.with_collection(items) %>
|
38
|
+
</ul>
|
39
39
|
</main>
|
40
40
|
</article>
|
41
41
|
</dialog>
|
@@ -24,7 +24,7 @@ module Katalyst
|
|
24
24
|
def actions
|
25
25
|
tag.menu do
|
26
26
|
concat action(:discard, class: "button", data: { text_button: "" })
|
27
|
-
concat action(:revert, class: "button", data: { text_button: "" })
|
27
|
+
concat action(:revert, class: "button", data: { text_button: "" })
|
28
28
|
concat action(:save, class: "button", data: { ghost_button: "" })
|
29
29
|
concat action(:publish, class: "button")
|
30
30
|
end
|
@@ -12,6 +12,10 @@ export default class ItemEditorController extends Controller {
|
|
12
12
|
this.element.removeEventListener("turbo:submit-end", this.onSubmit);
|
13
13
|
}
|
14
14
|
|
15
|
+
click(e) {
|
16
|
+
if (e.target.tagName === "DIALOG") this.dismiss();
|
17
|
+
}
|
18
|
+
|
15
19
|
dismiss() {
|
16
20
|
if (!this.dialogTarget) return;
|
17
21
|
if (!this.dialogTarget.open) this.dialogTarget.close();
|
@@ -29,15 +33,16 @@ export default class ItemEditorController extends Controller {
|
|
29
33
|
}
|
30
34
|
|
31
35
|
onSubmit = (event) => {
|
32
|
-
if (
|
36
|
+
if (
|
37
|
+
event.detail.success &&
|
38
|
+
"closeDialog" in event.detail.formSubmission?.submitter?.dataset
|
39
|
+
) {
|
33
40
|
this.dialogTarget.close();
|
34
41
|
this.element.removeAttribute("src");
|
35
42
|
this.dialogTarget.remove();
|
36
43
|
}
|
37
44
|
};
|
38
45
|
|
39
|
-
noop() {}
|
40
|
-
|
41
46
|
#removeTargetItem() {
|
42
47
|
const el = document.getElementById(this.dialogTarget.dataset.itemId);
|
43
48
|
const item = new Item(el.closest("[data-navigation-item]"));
|
@@ -1,5 +1,7 @@
|
|
1
1
|
import { Controller } from "@hotwired/stimulus";
|
2
2
|
|
3
|
+
import Item from "./item";
|
4
|
+
|
3
5
|
const EDGE_AREA = 24;
|
4
6
|
|
5
7
|
export default class NewItemsController extends Controller {
|
@@ -14,6 +16,10 @@ export default class NewItemsController extends Controller {
|
|
14
16
|
delete this.currentItem;
|
15
17
|
}
|
16
18
|
|
19
|
+
click(e) {
|
20
|
+
if (e.target.tagName === "DIALOG") this.close(e);
|
21
|
+
}
|
22
|
+
|
17
23
|
open(e) {
|
18
24
|
e.preventDefault();
|
19
25
|
this.dialog.showModal();
|
@@ -24,20 +30,19 @@ export default class NewItemsController extends Controller {
|
|
24
30
|
this.dialog.close();
|
25
31
|
}
|
26
32
|
|
27
|
-
noop(e) {}
|
28
|
-
|
29
33
|
/**
|
30
34
|
* Add the selected item to the DOM at the current position or the end of the list.
|
31
35
|
*/
|
32
36
|
add(e) {
|
33
37
|
e.preventDefault();
|
34
38
|
|
35
|
-
const template = e.target.querySelector("template");
|
39
|
+
const template = e.target.closest("li").querySelector("template");
|
36
40
|
const item = template.content.querySelector("li").cloneNode(true);
|
37
41
|
const target = this.currentItem;
|
38
42
|
|
39
43
|
if (target) {
|
40
44
|
target.insertAdjacentElement("beforebegin", item);
|
45
|
+
new Item(item).depth = new Item(target).depth;
|
41
46
|
} else {
|
42
47
|
this.list.insertAdjacentElement("beforeend", item);
|
43
48
|
}
|
@@ -5,19 +5,18 @@
|
|
5
5
|
<dialog class="modal"
|
6
6
|
data-navigation--editor--item-editor-target="dialog"
|
7
7
|
data-action="
|
8
|
-
click->navigation--editor--item-editor#
|
8
|
+
click->navigation--editor--item-editor#click
|
9
9
|
close->navigation--editor--item-editor#dismiss
|
10
10
|
"
|
11
11
|
data-item-id="<%= dom_id(item_editor.item) %>"
|
12
12
|
<%= "data-item-persisted" if item_editor.item.persisted? %>>
|
13
|
-
<article class="flow"
|
13
|
+
<article class="flow">
|
14
14
|
<header class="repel" data-nowrap>
|
15
15
|
<h2><%= item_editor.title %></h2>
|
16
|
-
<button
|
17
|
-
formmethod="dialog"
|
18
|
-
class="button"
|
16
|
+
<button class="button"
|
19
17
|
data-button-padding="tight"
|
20
|
-
data-text-button
|
18
|
+
data-text-button
|
19
|
+
data-action="navigation--editor--item-editor#dismiss">
|
21
20
|
<icon aria-hidden="true" class="icon" data-icon="close"> </icon>
|
22
21
|
<span class="visually-hidden">Close</span>
|
23
22
|
</button>
|
@@ -26,7 +25,7 @@
|
|
26
25
|
<%= render item_editor %>
|
27
26
|
</main>
|
28
27
|
<footer class="actions" data-reverse>
|
29
|
-
<button form="<%= item_editor.id %>" class="button">
|
28
|
+
<button form="<%= item_editor.id %>" class="button" data-close-dialog>
|
30
29
|
Done
|
31
30
|
</button>
|
32
31
|
<button form="<%= item_editor.id %>"
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: katalyst-navigation
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Katalyst Interactive
|
8
8
|
bindir: bin
|
9
9
|
cert_chain: []
|
10
|
-
date:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: katalyst-html-attributes
|
@@ -165,7 +165,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
165
165
|
- !ruby/object:Gem::Version
|
166
166
|
version: '0'
|
167
167
|
requirements: []
|
168
|
-
rubygems_version: 3.6.
|
168
|
+
rubygems_version: 3.6.7
|
169
169
|
specification_version: 4
|
170
170
|
summary: Navigation generator and editor
|
171
171
|
test_files: []
|