katalyst-navigation 1.8.4 → 2.0.0
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/README.md +11 -2
- data/app/assets/builds/katalyst/navigation.esm.js +207 -109
- data/app/assets/builds/katalyst/navigation.js +207 -109
- data/app/assets/builds/katalyst/navigation.min.js +1 -1
- data/app/assets/builds/katalyst/navigation.min.js.map +1 -1
- data/app/assets/images/katalyst/navigation/icons/button.svg +1 -0
- data/app/assets/images/katalyst/navigation/icons/collapse.svg +3 -0
- data/app/assets/images/katalyst/navigation/icons/edit.svg +1 -0
- data/app/assets/images/katalyst/navigation/icons/expand.svg +3 -0
- data/app/assets/images/katalyst/navigation/icons/heading.svg +1 -0
- data/app/assets/images/katalyst/navigation/icons/hidden.svg +1 -0
- data/app/assets/images/katalyst/navigation/icons/indent.svg +3 -0
- data/app/assets/images/katalyst/navigation/icons/link.svg +1 -0
- data/app/assets/images/katalyst/navigation/icons/outdent.svg +4 -0
- data/app/assets/images/katalyst/navigation/icons/remove.svg +1 -0
- data/app/assets/stylesheets/katalyst/_navigation.scss +3 -0
- data/app/assets/stylesheets/katalyst/navigation/editor.css +175 -0
- data/app/assets/stylesheets/katalyst/navigation/icons.css +54 -0
- data/app/assets/stylesheets/katalyst/navigation/{editor/_status-bar.scss → status-bar.css} +30 -34
- data/app/assets/stylesheets/katalyst/navigation.css +3 -0
- data/app/components/katalyst/navigation/editor/base_component.rb +0 -6
- data/app/components/katalyst/navigation/editor/item_component.html.erb +26 -16
- data/app/components/katalyst/navigation/editor/item_component.rb +2 -3
- data/app/components/katalyst/navigation/editor/item_editor_component.rb +16 -19
- data/app/components/katalyst/navigation/editor/new_item_component.html.erb +15 -14
- data/app/components/katalyst/navigation/editor/new_item_component.rb +7 -10
- data/app/components/katalyst/navigation/editor/new_items_component.html.erb +41 -2
- data/app/components/katalyst/navigation/editor/new_items_component.rb +0 -2
- data/app/components/katalyst/navigation/editor/row_component.html.erb +2 -1
- data/app/components/katalyst/navigation/editor/status_bar_component.rb +9 -11
- data/app/components/katalyst/navigation/editor/table_component.html.erb +0 -6
- data/app/components/katalyst/navigation/editor/table_component.rb +12 -14
- data/app/components/katalyst/navigation/editor_component.html.erb +7 -0
- data/app/components/katalyst/navigation/editor_component.rb +13 -16
- data/app/controllers/katalyst/navigation/items_controller.rb +1 -1
- data/app/controllers/katalyst/navigation/menus_controller.rb +1 -1
- data/app/javascript/navigation/application.js +8 -3
- data/app/javascript/navigation/editor/item.js +1 -1
- data/app/javascript/navigation/editor/item_editor_controller.js +54 -0
- data/app/javascript/navigation/editor/list_controller.js +11 -103
- data/app/javascript/navigation/editor/new_items_controller.js +145 -0
- data/app/views/katalyst/navigation/items/_button.html.erb +24 -28
- data/app/views/katalyst/navigation/items/_form.html.erb +6 -0
- data/app/views/katalyst/navigation/items/_form_errors.html.erb +1 -1
- data/app/views/katalyst/navigation/items/_heading.html.erb +12 -14
- data/app/views/katalyst/navigation/items/_link.html.erb +24 -26
- data/app/views/katalyst/navigation/items/edit.html.erb +40 -2
- data/app/views/katalyst/navigation/items/edit.turbo_stream.erb +2 -0
- data/app/views/katalyst/navigation/items/update.turbo_stream.erb +0 -1
- data/app/views/katalyst/navigation/menus/show.html.erb +0 -1
- data/lib/katalyst/navigation/engine.rb +0 -1
- metadata +21 -28
- data/app/assets/stylesheets/katalyst/navigation/editor/_icon.scss +0 -17
- data/app/assets/stylesheets/katalyst/navigation/editor/_index.scss +0 -145
- data/app/assets/stylesheets/katalyst/navigation/editor/_item-actions.scss +0 -93
- data/app/assets/stylesheets/katalyst/navigation/editor/_item-rules.scss +0 -19
- data/app/assets/stylesheets/katalyst/navigation/editor/_new-items.scss +0 -67
- data/app/javascript/navigation/editor/new_item_controller.js +0 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c156092dfddc81eae2c946543d921bc2da92d7a65b4ac1ce1186715104420d53
|
4
|
+
data.tar.gz: 29732312e198d827da22b4555f2f1a1ef86313cdc877ca0dc74e2761be24a032
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3784055b26abc2a204eeed9089d14551b322ad8df9f1758f3525f7cf297878175c24376d680a95c9dcea0f2e841ad5421789e9f9fe7190257afbd7ed12799022
|
7
|
+
data.tar.gz: 3ee6d69f1e7aadcea6ce1c8dd9626378ff3abae3933b1adf6480cb029793062bd45b7c1b5a045015326cccbe3cc8ec306b7c66e0f98402853b450badc935bd5c
|
data/README.md
CHANGED
@@ -23,7 +23,7 @@ rake katalyst_navigation:install:migrations
|
|
23
23
|
```
|
24
24
|
|
25
25
|
Add the Gem's javascript and CSS to your build pipeline. This assumes that
|
26
|
-
you're using `
|
26
|
+
you're using `propshaft` and `importmaps` to manage your assets.
|
27
27
|
|
28
28
|
```javascript
|
29
29
|
// app/javascript/controllers/application.js
|
@@ -32,7 +32,16 @@ import navigation from "@katalyst/navigation";
|
|
32
32
|
application.load(navigation);
|
33
33
|
```
|
34
34
|
|
35
|
-
|
35
|
+
Import the styles as css:
|
36
|
+
|
37
|
+
```css
|
38
|
+
@import "/katalyst/navigation.css";
|
39
|
+
```
|
40
|
+
|
41
|
+
Or, if you're using `dartsass-rails`:
|
42
|
+
|
43
|
+
```scss
|
44
|
+
// app/assets/stylesheets/application.scss
|
36
45
|
@use "katalyst/navigation";
|
37
46
|
```
|
38
47
|
|
@@ -264,7 +264,7 @@ class Item {
|
|
264
264
|
*/
|
265
265
|
function createChildrenList(node) {
|
266
266
|
const childrenList = document.createElement("ol");
|
267
|
-
childrenList.
|
267
|
+
childrenList.toggleAttribute("hidden", true);
|
268
268
|
|
269
269
|
childrenList.dataset[`navigationChildren`] = "";
|
270
270
|
|
@@ -744,11 +744,59 @@ class ItemController extends Controller {
|
|
744
744
|
}
|
745
745
|
}
|
746
746
|
|
747
|
-
class
|
747
|
+
class ItemEditorController extends Controller {
|
748
|
+
static targets = ["dialog"];
|
749
|
+
|
748
750
|
connect() {
|
749
|
-
this.
|
751
|
+
this.element.addEventListener("turbo:submit-end", this.onSubmit);
|
752
|
+
}
|
753
|
+
|
754
|
+
disconnect() {
|
755
|
+
this.element.removeEventListener("turbo:submit-end", this.onSubmit);
|
756
|
+
}
|
757
|
+
|
758
|
+
dismiss() {
|
759
|
+
if (!this.dialogTarget) return;
|
760
|
+
if (!this.dialogTarget.open) this.dialogTarget.close();
|
761
|
+
|
762
|
+
if (!("itemPersisted" in this.dialogTarget.dataset)) {
|
763
|
+
this.#removeTargetItem();
|
764
|
+
}
|
765
|
+
|
766
|
+
this.element.removeAttribute("src");
|
767
|
+
this.dialogTarget.remove();
|
768
|
+
}
|
769
|
+
|
770
|
+
dialogTargetConnected(dialog) {
|
771
|
+
dialog.showModal();
|
750
772
|
}
|
751
773
|
|
774
|
+
onSubmit = (event) => {
|
775
|
+
if (event.detail.success) {
|
776
|
+
this.dialogTarget.close();
|
777
|
+
this.element.removeAttribute("src");
|
778
|
+
this.dialogTarget.remove();
|
779
|
+
}
|
780
|
+
};
|
781
|
+
|
782
|
+
noop() {}
|
783
|
+
|
784
|
+
#removeTargetItem() {
|
785
|
+
const el = document.getElementById(this.dialogTarget.dataset.itemId);
|
786
|
+
const item = new Item(el.closest("[data-navigation-item]"));
|
787
|
+
const list = item.node.parentElement;
|
788
|
+
|
789
|
+
item.node.remove();
|
790
|
+
|
791
|
+
this.dispatch("reindex", {
|
792
|
+
target: list,
|
793
|
+
bubbles: true,
|
794
|
+
prefix: "navigation",
|
795
|
+
});
|
796
|
+
}
|
797
|
+
}
|
798
|
+
|
799
|
+
class ListController extends Controller {
|
752
800
|
/**
|
753
801
|
* When the user starts a drag within the list, set the item's dataTransfer
|
754
802
|
* properties to indicate that it's being dragged and update its style.
|
@@ -772,11 +820,6 @@ class ListController extends Controller {
|
|
772
820
|
* When the user drags an item over another item in the last, swap the
|
773
821
|
* dragging item with the item under the cursor.
|
774
822
|
*
|
775
|
-
* As a special case, if the item is dragged over placeholder space at the end
|
776
|
-
* of the list, move the item to the bottom of the list instead. This allows
|
777
|
-
* users to hit the list element more easily when adding new items to an empty
|
778
|
-
* list.
|
779
|
-
*
|
780
823
|
* @param event {DragEvent}
|
781
824
|
*/
|
782
825
|
dragover(event) {
|
@@ -790,53 +833,7 @@ class ListController extends Controller {
|
|
790
833
|
}
|
791
834
|
|
792
835
|
/**
|
793
|
-
* When the user
|
794
|
-
* represent the new item. Note that we can't access the drag data
|
795
|
-
* until drop, so we assume that this is our template item for now.
|
796
|
-
*
|
797
|
-
* Users can cancel the drag by dragging the item out of the list or by
|
798
|
-
* pressing escape. Both are handled by `cancelDrag`.
|
799
|
-
*
|
800
|
-
* @param event {DragEvent}
|
801
|
-
*/
|
802
|
-
dragenter(event) {
|
803
|
-
event.preventDefault();
|
804
|
-
|
805
|
-
// Safari doesn't support relatedTarget, so we count enter/leave pairs
|
806
|
-
this.enterCount++;
|
807
|
-
|
808
|
-
if (copyAllowed(event) && !this.dragItem) {
|
809
|
-
const item = document.createElement("li");
|
810
|
-
item.dataset.dragging = "";
|
811
|
-
item.dataset.newItem = "";
|
812
|
-
this.element.appendChild(item);
|
813
|
-
}
|
814
|
-
}
|
815
|
-
|
816
|
-
/**
|
817
|
-
* When the user drags the item out of the list, remove the placeholder.
|
818
|
-
* This allows users to cancel the drag by dragging the item out of the list.
|
819
|
-
*
|
820
|
-
* @param event {DragEvent}
|
821
|
-
*/
|
822
|
-
dragleave(event) {
|
823
|
-
// Safari doesn't support relatedTarget, so we count enter/leave pairs
|
824
|
-
// https://bugs.webkit.org/show_bug.cgi?id=66547
|
825
|
-
this.enterCount--;
|
826
|
-
|
827
|
-
if (
|
828
|
-
this.enterCount <= 0 &&
|
829
|
-
this.dragItem.dataset.hasOwnProperty("newItem")
|
830
|
-
) {
|
831
|
-
this.cancelDrag(event);
|
832
|
-
}
|
833
|
-
}
|
834
|
-
|
835
|
-
/**
|
836
|
-
* When the user drops an item into the list, end the drag and reindex the list.
|
837
|
-
*
|
838
|
-
* If the item is a new item, we replace the placeholder with the template
|
839
|
-
* item data from the dataTransfer API.
|
836
|
+
* When the user drops an item, end the drag and reindex the list.
|
840
837
|
*
|
841
838
|
* @param event {DragEvent}
|
842
839
|
*/
|
@@ -849,18 +846,6 @@ class ListController extends Controller {
|
|
849
846
|
delete item.dataset.dragging;
|
850
847
|
swap(dropTarget(event.target), item);
|
851
848
|
|
852
|
-
if (item.dataset.hasOwnProperty("newItem")) {
|
853
|
-
const placeholder = item;
|
854
|
-
const template = document.createElement("template");
|
855
|
-
template.innerHTML = event.dataTransfer.getData("text/html");
|
856
|
-
item = template.content.querySelector("li");
|
857
|
-
|
858
|
-
this.element.replaceChild(item, placeholder);
|
859
|
-
requestAnimationFrame(() =>
|
860
|
-
item.querySelector("[role='button'][value='edit']").click(),
|
861
|
-
);
|
862
|
-
}
|
863
|
-
|
864
849
|
this.dispatch("drop", {
|
865
850
|
target: item,
|
866
851
|
bubbles: true,
|
@@ -869,15 +854,13 @@ class ListController extends Controller {
|
|
869
854
|
}
|
870
855
|
|
871
856
|
/**
|
872
|
-
* End an in-progress drag
|
873
|
-
*
|
857
|
+
* End an in-progress drag by resetting the item's style and restoring its
|
858
|
+
* original position in the list.
|
874
859
|
*/
|
875
860
|
dragend() {
|
876
861
|
const item = this.dragItem;
|
877
862
|
|
878
|
-
if (
|
879
|
-
item.remove();
|
880
|
-
} else {
|
863
|
+
if (item) {
|
881
864
|
delete item.dataset.dragging;
|
882
865
|
this.reset();
|
883
866
|
}
|
@@ -901,7 +884,7 @@ class ListController extends Controller {
|
|
901
884
|
}
|
902
885
|
|
903
886
|
/**
|
904
|
-
* Swaps two list items.
|
887
|
+
* Swaps two list items.
|
905
888
|
*
|
906
889
|
* @param target the target element to swap with
|
907
890
|
* @param item the item the user is dragging
|
@@ -910,51 +893,162 @@ function swap(target, item) {
|
|
910
893
|
if (!target) return;
|
911
894
|
if (target === item) return;
|
912
895
|
|
913
|
-
|
914
|
-
|
915
|
-
|
916
|
-
|
917
|
-
|
918
|
-
target.insertAdjacentElement("afterend", item);
|
919
|
-
}
|
920
|
-
}
|
921
|
-
|
922
|
-
if (target.nodeName === "OL") {
|
923
|
-
target.appendChild(item);
|
896
|
+
const positionComparison = target.compareDocumentPosition(item);
|
897
|
+
if (positionComparison & Node.DOCUMENT_POSITION_FOLLOWING) {
|
898
|
+
target.insertAdjacentElement("beforebegin", item);
|
899
|
+
} else if (positionComparison & Node.DOCUMENT_POSITION_PRECEDING) {
|
900
|
+
target.insertAdjacentElement("afterend", item);
|
924
901
|
}
|
925
902
|
}
|
926
903
|
|
927
|
-
/**
|
928
|
-
* Returns true if the event supports copy or copy move.
|
929
|
-
*
|
930
|
-
* Chrome and Firefox use copy, but Safari only supports copyMove.
|
931
|
-
*/
|
932
|
-
function copyAllowed(event) {
|
933
|
-
return (
|
934
|
-
event.dataTransfer.effectAllowed === "copy" ||
|
935
|
-
event.dataTransfer.effectAllowed === "copyMove"
|
936
|
-
);
|
937
|
-
}
|
938
|
-
|
939
904
|
/**
|
940
905
|
* Given an event target, return the closest drop target, if any.
|
941
906
|
*/
|
942
907
|
function dropTarget(e) {
|
943
|
-
return (
|
944
|
-
e &&
|
945
|
-
(e.closest("[data-controller='navigation--editor--list'] > *") ||
|
946
|
-
e.closest("[data-controller='navigation--editor--list']"))
|
947
|
-
);
|
908
|
+
return e && e.closest("[data-controller='navigation--editor--list'] > *");
|
948
909
|
}
|
949
910
|
|
950
|
-
|
951
|
-
static targets = ["template"];
|
911
|
+
const EDGE_AREA = 24;
|
952
912
|
|
953
|
-
|
954
|
-
|
913
|
+
class NewItemsController extends Controller {
|
914
|
+
static targets = ["inline"];
|
955
915
|
|
956
|
-
|
957
|
-
|
916
|
+
connect() {
|
917
|
+
this.form.addEventListener("mousemove", this.move);
|
918
|
+
}
|
919
|
+
|
920
|
+
disconnect() {
|
921
|
+
this.form?.removeEventListener("mousemove", this.move);
|
922
|
+
delete this.currentItem;
|
923
|
+
}
|
924
|
+
|
925
|
+
open(e) {
|
926
|
+
e.preventDefault();
|
927
|
+
this.dialog.showModal();
|
928
|
+
}
|
929
|
+
|
930
|
+
close(e) {
|
931
|
+
e.preventDefault();
|
932
|
+
this.dialog.close();
|
933
|
+
}
|
934
|
+
|
935
|
+
noop(e) {}
|
936
|
+
|
937
|
+
/**
|
938
|
+
* Add the selected item to the DOM at the current position or the end of the list.
|
939
|
+
*/
|
940
|
+
add(e) {
|
941
|
+
e.preventDefault();
|
942
|
+
|
943
|
+
const template = e.target.querySelector("template");
|
944
|
+
const item = template.content.querySelector("li").cloneNode(true);
|
945
|
+
const target = this.currentItem;
|
946
|
+
|
947
|
+
if (target) {
|
948
|
+
target.insertAdjacentElement("beforebegin", item);
|
949
|
+
} else {
|
950
|
+
this.list.insertAdjacentElement("beforeend", item);
|
951
|
+
}
|
952
|
+
|
953
|
+
this.toggleInline(false);
|
954
|
+
this.dialog.close();
|
955
|
+
|
956
|
+
requestAnimationFrame(() => {
|
957
|
+
item.querySelector(`[value="edit"]`).click();
|
958
|
+
});
|
959
|
+
}
|
960
|
+
|
961
|
+
morph(e) {
|
962
|
+
e.preventDefault();
|
963
|
+
this.dialog.close();
|
964
|
+
}
|
965
|
+
|
966
|
+
move = (e) => {
|
967
|
+
if (this.isOverInlineTarget(e)) return;
|
968
|
+
if (this.dialog.open) return;
|
969
|
+
|
970
|
+
const target = this.getCurrentItem(e);
|
971
|
+
|
972
|
+
// return if we're already showing this item
|
973
|
+
if (this.currentItem === target) return;
|
974
|
+
|
975
|
+
// hide the button if it's already visible
|
976
|
+
if (this.currentItem) this.toggleInline(false);
|
977
|
+
|
978
|
+
this.currentItem = target;
|
979
|
+
|
980
|
+
// clear any previously set timer
|
981
|
+
if (this.timer) clearTimeout(this.timer);
|
982
|
+
|
983
|
+
// show the button after a debounce pause
|
984
|
+
this.timer = setTimeout(() => {
|
985
|
+
delete this.timer;
|
986
|
+
this.toggleInline();
|
987
|
+
}, 100);
|
988
|
+
};
|
989
|
+
|
990
|
+
toggleInline(show = !!this.currentItem) {
|
991
|
+
if (show) {
|
992
|
+
this.inlineTarget.style.top = `${this.currentItem.offsetTop}px`;
|
993
|
+
this.inlineTarget.toggleAttribute("hidden", false);
|
994
|
+
} else {
|
995
|
+
this.inlineTarget.toggleAttribute("hidden", true);
|
996
|
+
}
|
997
|
+
}
|
998
|
+
|
999
|
+
get dialog() {
|
1000
|
+
return this.element.querySelector("dialog");
|
1001
|
+
}
|
1002
|
+
|
1003
|
+
/**
|
1004
|
+
* @returns {HTMLFormElement}
|
1005
|
+
*/
|
1006
|
+
get form() {
|
1007
|
+
return this.element.closest("form");
|
1008
|
+
}
|
1009
|
+
|
1010
|
+
/**
|
1011
|
+
* @returns {HTMLUListElement,null}
|
1012
|
+
*/
|
1013
|
+
get list() {
|
1014
|
+
return this.form.querySelector(
|
1015
|
+
`[data-controller="navigation--editor--list"]`,
|
1016
|
+
);
|
1017
|
+
}
|
1018
|
+
|
1019
|
+
/**
|
1020
|
+
* @param {MouseEvent} e
|
1021
|
+
* @returns {HTMLLIElement,null}
|
1022
|
+
*/
|
1023
|
+
getCurrentItem(e) {
|
1024
|
+
const item = document.elementFromPoint(e.clientX, e.clientY).closest("li");
|
1025
|
+
if (!item) return null;
|
1026
|
+
|
1027
|
+
const bounds = item.getBoundingClientRect();
|
1028
|
+
|
1029
|
+
// check X for center(ish) mouse position
|
1030
|
+
if (e.clientX < bounds.left + bounds.width / 2 - 2 * EDGE_AREA) return null;
|
1031
|
+
if (e.clientX > bounds.left + bounds.width / 2 + 2 * EDGE_AREA) return null;
|
1032
|
+
|
1033
|
+
// check Y for hits on this item or it's next sibling
|
1034
|
+
if (e.clientY - bounds.y <= EDGE_AREA) {
|
1035
|
+
return item;
|
1036
|
+
} else if (bounds.y + bounds.height - e.clientY <= EDGE_AREA) {
|
1037
|
+
return item.nextElementSibling;
|
1038
|
+
} else {
|
1039
|
+
return null;
|
1040
|
+
}
|
1041
|
+
}
|
1042
|
+
|
1043
|
+
/**
|
1044
|
+
* @param {MouseEvent} e
|
1045
|
+
* @returns {Boolean} true when the target of the event is the floating button
|
1046
|
+
*/
|
1047
|
+
isOverInlineTarget(e) {
|
1048
|
+
return (
|
1049
|
+
this.inlineTarget ===
|
1050
|
+
document.elementFromPoint(e.clientX, e.clientY).closest("div")
|
1051
|
+
);
|
958
1052
|
}
|
959
1053
|
}
|
960
1054
|
|
@@ -995,13 +1089,17 @@ const Definitions = [
|
|
995
1089
|
identifier: "navigation--editor--item",
|
996
1090
|
controllerConstructor: ItemController,
|
997
1091
|
},
|
1092
|
+
{
|
1093
|
+
identifier: "navigation--editor--item-editor",
|
1094
|
+
controllerConstructor: ItemEditorController,
|
1095
|
+
},
|
998
1096
|
{
|
999
1097
|
identifier: "navigation--editor--list",
|
1000
1098
|
controllerConstructor: ListController,
|
1001
1099
|
},
|
1002
1100
|
{
|
1003
|
-
identifier: "navigation--editor--new-
|
1004
|
-
controllerConstructor:
|
1101
|
+
identifier: "navigation--editor--new-items",
|
1102
|
+
controllerConstructor: NewItemsController,
|
1005
1103
|
},
|
1006
1104
|
{
|
1007
1105
|
identifier: "navigation--editor--status-bar",
|