katalyst-content 2.1.2 → 2.1.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/content.esm.js +110 -30
- data/app/assets/builds/katalyst/content.js +110 -30
- data/app/assets/builds/katalyst/content.min.js +1 -1
- data/app/assets/builds/katalyst/content.min.js.map +1 -1
- data/app/assets/stylesheets/katalyst/content/editor/_item-actions.scss +1 -0
- data/app/assets/stylesheets/katalyst/content/editor/_new-items.scss +1 -1
- data/app/components/katalyst/content/editor/row_component.rb +0 -3
- data/app/components/katalyst/content/editor/table_component.rb +1 -0
- data/app/components/katalyst/content/editor_component.html.erb +1 -1
- data/app/javascript/content/editor/list_controller.js +112 -31
- data/lib/katalyst/content/engine.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a9443ad6721368461db387b1eb3124dcf477cbe38f4c773799545e567959fb2e
|
|
4
|
+
data.tar.gz: d2b50d341d690488ddbf388147814b4261dcf9592acd2782933ac843be480e5d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3a459cae9de0384663d867cc5c5595b12329578cc434263d9858e69a6f29757305f25d76baae4601913bb97875f55accb4e84740803fd345622cc2198401a250
|
|
7
|
+
data.tar.gz: d610313d97457fa1506fdb6c8fc2d82a31a1b30295e0800e49c951e72168dfd8d7f685907d4f99177b441964993c8757deef60b5f4d90588fc2391fccec2bbed
|
|
@@ -737,6 +737,19 @@ class ItemController extends Controller {
|
|
|
737
737
|
}
|
|
738
738
|
|
|
739
739
|
class ListController extends Controller {
|
|
740
|
+
connect() {
|
|
741
|
+
this.enterCount = 0;
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
/**
|
|
745
|
+
* When the user starts a drag within the list, set the item's dataTransfer
|
|
746
|
+
* properties to indicate that it's being dragged and update its style.
|
|
747
|
+
*
|
|
748
|
+
* We delay setting the dataset property until the next animation frame
|
|
749
|
+
* so that the style updates can be computed before the drag begins.
|
|
750
|
+
*
|
|
751
|
+
* @param event {DragEvent}
|
|
752
|
+
*/
|
|
740
753
|
dragstart(event) {
|
|
741
754
|
if (this.element !== event.target.parentElement) return;
|
|
742
755
|
|
|
@@ -744,51 +757,86 @@ class ListController extends Controller {
|
|
|
744
757
|
event.dataTransfer.effectAllowed = "move";
|
|
745
758
|
|
|
746
759
|
// update element style after drag has begun
|
|
747
|
-
|
|
760
|
+
requestAnimationFrame(() => (target.dataset.dragging = ""));
|
|
748
761
|
}
|
|
749
762
|
|
|
763
|
+
/**
|
|
764
|
+
* When the user drags an item over another item in the last, swap the
|
|
765
|
+
* dragging item with the item under the cursor.
|
|
766
|
+
*
|
|
767
|
+
* As a special case, if the item is dragged over placeholder space at the end
|
|
768
|
+
* of the list, move the item to the bottom of the list instead. This allows
|
|
769
|
+
* users to hit the list element more easily when adding new items to an empty
|
|
770
|
+
* list.
|
|
771
|
+
*
|
|
772
|
+
* @param event {DragEvent}
|
|
773
|
+
*/
|
|
750
774
|
dragover(event) {
|
|
751
|
-
const item = this.dragItem
|
|
775
|
+
const item = this.dragItem;
|
|
752
776
|
if (!item) return;
|
|
753
777
|
|
|
754
|
-
swap(
|
|
778
|
+
swap(dropTarget(event.target), item);
|
|
755
779
|
|
|
756
780
|
event.preventDefault();
|
|
757
781
|
return true;
|
|
758
782
|
}
|
|
759
783
|
|
|
784
|
+
/**
|
|
785
|
+
* When the user drags an item into the list, create a placeholder item to
|
|
786
|
+
* represent the new item. Note that we can't access the drag data
|
|
787
|
+
* until drop, so we assume that this is our template item for now.
|
|
788
|
+
*
|
|
789
|
+
* Users can cancel the drag by dragging the item out of the list or by
|
|
790
|
+
* pressing escape. Both are handled by `cancelDrag`.
|
|
791
|
+
*
|
|
792
|
+
* @param event {DragEvent}
|
|
793
|
+
*/
|
|
760
794
|
dragenter(event) {
|
|
761
795
|
event.preventDefault();
|
|
762
796
|
|
|
763
|
-
|
|
797
|
+
// Safari doesn't support relatedTarget, so we count enter/leave pairs
|
|
798
|
+
this.enterCount++;
|
|
799
|
+
|
|
800
|
+
if (copyAllowed(event) && !this.dragItem) {
|
|
764
801
|
const item = document.createElement("li");
|
|
765
802
|
item.dataset.dragging = "";
|
|
766
803
|
item.dataset.newItem = "";
|
|
767
|
-
this.element.
|
|
804
|
+
this.element.appendChild(item);
|
|
768
805
|
}
|
|
769
806
|
}
|
|
770
807
|
|
|
808
|
+
/**
|
|
809
|
+
* When the user drags the item out of the list, remove the placeholder.
|
|
810
|
+
* This allows users to cancel the drag by dragging the item out of the list.
|
|
811
|
+
*
|
|
812
|
+
* @param event {DragEvent}
|
|
813
|
+
*/
|
|
771
814
|
dragleave(event) {
|
|
772
|
-
|
|
773
|
-
|
|
815
|
+
// Safari doesn't support relatedTarget, so we count enter/leave pairs
|
|
816
|
+
// https://bugs.webkit.org/show_bug.cgi?id=66547
|
|
817
|
+
this.enterCount--;
|
|
774
818
|
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
// remove item if it's a new item
|
|
779
|
-
if (item.dataset.hasOwnProperty("newItem")) {
|
|
780
|
-
item.remove();
|
|
819
|
+
if (this.enterCount <= 0 && this.dragItem.dataset.hasOwnProperty("newItem")) {
|
|
820
|
+
this.cancelDrag(event);
|
|
781
821
|
}
|
|
782
822
|
}
|
|
783
823
|
|
|
824
|
+
/**
|
|
825
|
+
* When the user drops an item into the list, end the drag and reindex the list.
|
|
826
|
+
*
|
|
827
|
+
* If the item is a new item, we replace the placeholder with the template
|
|
828
|
+
* item data from the dataTransfer API.
|
|
829
|
+
*
|
|
830
|
+
* @param event {DragEvent}
|
|
831
|
+
*/
|
|
784
832
|
drop(event) {
|
|
785
|
-
let item = this.dragItem
|
|
833
|
+
let item = this.dragItem;
|
|
786
834
|
|
|
787
835
|
if (!item) return;
|
|
788
836
|
|
|
789
837
|
event.preventDefault();
|
|
790
838
|
delete item.dataset.dragging;
|
|
791
|
-
swap(
|
|
839
|
+
swap(dropTarget(event.target), item);
|
|
792
840
|
|
|
793
841
|
if (item.dataset.hasOwnProperty("newItem")) {
|
|
794
842
|
const placeholder = item;
|
|
@@ -797,42 +845,52 @@ class ListController extends Controller {
|
|
|
797
845
|
item = template.content.querySelector("li");
|
|
798
846
|
|
|
799
847
|
this.element.replaceChild(item, placeholder);
|
|
800
|
-
|
|
848
|
+
requestAnimationFrame(() =>
|
|
801
849
|
item.querySelector("[role='button'][value='edit']").click()
|
|
802
850
|
);
|
|
803
851
|
}
|
|
804
852
|
|
|
805
|
-
this.dispatch("drop", {
|
|
853
|
+
this.dispatch("drop", {target: item, bubbles: true, prefix: "content"});
|
|
806
854
|
}
|
|
807
855
|
|
|
856
|
+
/**
|
|
857
|
+
* End an in-progress drag. If the item is a new item, remove it, otherwise
|
|
858
|
+
* reset the item's style and restore its original position in the list.
|
|
859
|
+
*/
|
|
808
860
|
dragend() {
|
|
809
|
-
const item = this.dragItem
|
|
810
|
-
if (!item) return;
|
|
861
|
+
const item = this.dragItem;
|
|
811
862
|
|
|
812
|
-
|
|
813
|
-
|
|
863
|
+
if (!item) ; else if (item.dataset.hasOwnProperty("newItem")) {
|
|
864
|
+
item.remove();
|
|
865
|
+
} else {
|
|
866
|
+
delete item.dataset.dragging;
|
|
867
|
+
this.reset();
|
|
868
|
+
}
|
|
814
869
|
}
|
|
815
870
|
|
|
816
|
-
|
|
817
|
-
return this.
|
|
871
|
+
get isDragging() {
|
|
872
|
+
return !!this.dragItem;
|
|
818
873
|
}
|
|
819
874
|
|
|
820
|
-
|
|
821
|
-
return (
|
|
822
|
-
e.closest("[data-controller='content--editor--list'] > *") ||
|
|
823
|
-
e.closest("[data-controller='content--editor--list']")
|
|
824
|
-
);
|
|
875
|
+
get dragItem() {
|
|
876
|
+
return this.element.querySelector("[data-dragging]");
|
|
825
877
|
}
|
|
826
878
|
|
|
827
879
|
reindex() {
|
|
828
|
-
this.dispatch("reindex", {
|
|
880
|
+
this.dispatch("reindex", {bubbles: true, prefix: "content"});
|
|
829
881
|
}
|
|
830
882
|
|
|
831
883
|
reset() {
|
|
832
|
-
this.dispatch("reset", {
|
|
884
|
+
this.dispatch("reset", {bubbles: true, prefix: "content"});
|
|
833
885
|
}
|
|
834
886
|
}
|
|
835
887
|
|
|
888
|
+
/**
|
|
889
|
+
* Swaps two list items. If target is a list, the item is appended.
|
|
890
|
+
*
|
|
891
|
+
* @param target the target element to swap with
|
|
892
|
+
* @param item the item the user is dragging
|
|
893
|
+
*/
|
|
836
894
|
function swap(target, item) {
|
|
837
895
|
if (!target) return;
|
|
838
896
|
if (target === item) return;
|
|
@@ -851,6 +909,28 @@ function swap(target, item) {
|
|
|
851
909
|
}
|
|
852
910
|
}
|
|
853
911
|
|
|
912
|
+
/**
|
|
913
|
+
* Returns true if the event supports copy or copy move.
|
|
914
|
+
*
|
|
915
|
+
* Chrome and Firefox use copy, but Safari only supports copyMove.
|
|
916
|
+
*/
|
|
917
|
+
function copyAllowed(event) {
|
|
918
|
+
return (
|
|
919
|
+
event.dataTransfer.effectAllowed === "copy" ||
|
|
920
|
+
event.dataTransfer.effectAllowed === "copyMove"
|
|
921
|
+
);
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
/**
|
|
925
|
+
* Given an event target, return the closest drop target, if any.
|
|
926
|
+
*/
|
|
927
|
+
function dropTarget(e) {
|
|
928
|
+
return e && (
|
|
929
|
+
e.closest("[data-controller='content--editor--list'] > *") ||
|
|
930
|
+
e.closest("[data-controller='content--editor--list']")
|
|
931
|
+
);
|
|
932
|
+
}
|
|
933
|
+
|
|
854
934
|
class NewItemController extends Controller {
|
|
855
935
|
static targets = ["template"];
|
|
856
936
|
|
|
@@ -737,6 +737,19 @@ class ItemController extends Controller {
|
|
|
737
737
|
}
|
|
738
738
|
|
|
739
739
|
class ListController extends Controller {
|
|
740
|
+
connect() {
|
|
741
|
+
this.enterCount = 0;
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
/**
|
|
745
|
+
* When the user starts a drag within the list, set the item's dataTransfer
|
|
746
|
+
* properties to indicate that it's being dragged and update its style.
|
|
747
|
+
*
|
|
748
|
+
* We delay setting the dataset property until the next animation frame
|
|
749
|
+
* so that the style updates can be computed before the drag begins.
|
|
750
|
+
*
|
|
751
|
+
* @param event {DragEvent}
|
|
752
|
+
*/
|
|
740
753
|
dragstart(event) {
|
|
741
754
|
if (this.element !== event.target.parentElement) return;
|
|
742
755
|
|
|
@@ -744,51 +757,86 @@ class ListController extends Controller {
|
|
|
744
757
|
event.dataTransfer.effectAllowed = "move";
|
|
745
758
|
|
|
746
759
|
// update element style after drag has begun
|
|
747
|
-
|
|
760
|
+
requestAnimationFrame(() => (target.dataset.dragging = ""));
|
|
748
761
|
}
|
|
749
762
|
|
|
763
|
+
/**
|
|
764
|
+
* When the user drags an item over another item in the last, swap the
|
|
765
|
+
* dragging item with the item under the cursor.
|
|
766
|
+
*
|
|
767
|
+
* As a special case, if the item is dragged over placeholder space at the end
|
|
768
|
+
* of the list, move the item to the bottom of the list instead. This allows
|
|
769
|
+
* users to hit the list element more easily when adding new items to an empty
|
|
770
|
+
* list.
|
|
771
|
+
*
|
|
772
|
+
* @param event {DragEvent}
|
|
773
|
+
*/
|
|
750
774
|
dragover(event) {
|
|
751
|
-
const item = this.dragItem
|
|
775
|
+
const item = this.dragItem;
|
|
752
776
|
if (!item) return;
|
|
753
777
|
|
|
754
|
-
swap(
|
|
778
|
+
swap(dropTarget(event.target), item);
|
|
755
779
|
|
|
756
780
|
event.preventDefault();
|
|
757
781
|
return true;
|
|
758
782
|
}
|
|
759
783
|
|
|
784
|
+
/**
|
|
785
|
+
* When the user drags an item into the list, create a placeholder item to
|
|
786
|
+
* represent the new item. Note that we can't access the drag data
|
|
787
|
+
* until drop, so we assume that this is our template item for now.
|
|
788
|
+
*
|
|
789
|
+
* Users can cancel the drag by dragging the item out of the list or by
|
|
790
|
+
* pressing escape. Both are handled by `cancelDrag`.
|
|
791
|
+
*
|
|
792
|
+
* @param event {DragEvent}
|
|
793
|
+
*/
|
|
760
794
|
dragenter(event) {
|
|
761
795
|
event.preventDefault();
|
|
762
796
|
|
|
763
|
-
|
|
797
|
+
// Safari doesn't support relatedTarget, so we count enter/leave pairs
|
|
798
|
+
this.enterCount++;
|
|
799
|
+
|
|
800
|
+
if (copyAllowed(event) && !this.dragItem) {
|
|
764
801
|
const item = document.createElement("li");
|
|
765
802
|
item.dataset.dragging = "";
|
|
766
803
|
item.dataset.newItem = "";
|
|
767
|
-
this.element.
|
|
804
|
+
this.element.appendChild(item);
|
|
768
805
|
}
|
|
769
806
|
}
|
|
770
807
|
|
|
808
|
+
/**
|
|
809
|
+
* When the user drags the item out of the list, remove the placeholder.
|
|
810
|
+
* This allows users to cancel the drag by dragging the item out of the list.
|
|
811
|
+
*
|
|
812
|
+
* @param event {DragEvent}
|
|
813
|
+
*/
|
|
771
814
|
dragleave(event) {
|
|
772
|
-
|
|
773
|
-
|
|
815
|
+
// Safari doesn't support relatedTarget, so we count enter/leave pairs
|
|
816
|
+
// https://bugs.webkit.org/show_bug.cgi?id=66547
|
|
817
|
+
this.enterCount--;
|
|
774
818
|
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
// remove item if it's a new item
|
|
779
|
-
if (item.dataset.hasOwnProperty("newItem")) {
|
|
780
|
-
item.remove();
|
|
819
|
+
if (this.enterCount <= 0 && this.dragItem.dataset.hasOwnProperty("newItem")) {
|
|
820
|
+
this.cancelDrag(event);
|
|
781
821
|
}
|
|
782
822
|
}
|
|
783
823
|
|
|
824
|
+
/**
|
|
825
|
+
* When the user drops an item into the list, end the drag and reindex the list.
|
|
826
|
+
*
|
|
827
|
+
* If the item is a new item, we replace the placeholder with the template
|
|
828
|
+
* item data from the dataTransfer API.
|
|
829
|
+
*
|
|
830
|
+
* @param event {DragEvent}
|
|
831
|
+
*/
|
|
784
832
|
drop(event) {
|
|
785
|
-
let item = this.dragItem
|
|
833
|
+
let item = this.dragItem;
|
|
786
834
|
|
|
787
835
|
if (!item) return;
|
|
788
836
|
|
|
789
837
|
event.preventDefault();
|
|
790
838
|
delete item.dataset.dragging;
|
|
791
|
-
swap(
|
|
839
|
+
swap(dropTarget(event.target), item);
|
|
792
840
|
|
|
793
841
|
if (item.dataset.hasOwnProperty("newItem")) {
|
|
794
842
|
const placeholder = item;
|
|
@@ -797,42 +845,52 @@ class ListController extends Controller {
|
|
|
797
845
|
item = template.content.querySelector("li");
|
|
798
846
|
|
|
799
847
|
this.element.replaceChild(item, placeholder);
|
|
800
|
-
|
|
848
|
+
requestAnimationFrame(() =>
|
|
801
849
|
item.querySelector("[role='button'][value='edit']").click()
|
|
802
850
|
);
|
|
803
851
|
}
|
|
804
852
|
|
|
805
|
-
this.dispatch("drop", {
|
|
853
|
+
this.dispatch("drop", {target: item, bubbles: true, prefix: "content"});
|
|
806
854
|
}
|
|
807
855
|
|
|
856
|
+
/**
|
|
857
|
+
* End an in-progress drag. If the item is a new item, remove it, otherwise
|
|
858
|
+
* reset the item's style and restore its original position in the list.
|
|
859
|
+
*/
|
|
808
860
|
dragend() {
|
|
809
|
-
const item = this.dragItem
|
|
810
|
-
if (!item) return;
|
|
861
|
+
const item = this.dragItem;
|
|
811
862
|
|
|
812
|
-
|
|
813
|
-
|
|
863
|
+
if (!item) ; else if (item.dataset.hasOwnProperty("newItem")) {
|
|
864
|
+
item.remove();
|
|
865
|
+
} else {
|
|
866
|
+
delete item.dataset.dragging;
|
|
867
|
+
this.reset();
|
|
868
|
+
}
|
|
814
869
|
}
|
|
815
870
|
|
|
816
|
-
|
|
817
|
-
return this.
|
|
871
|
+
get isDragging() {
|
|
872
|
+
return !!this.dragItem;
|
|
818
873
|
}
|
|
819
874
|
|
|
820
|
-
|
|
821
|
-
return (
|
|
822
|
-
e.closest("[data-controller='content--editor--list'] > *") ||
|
|
823
|
-
e.closest("[data-controller='content--editor--list']")
|
|
824
|
-
);
|
|
875
|
+
get dragItem() {
|
|
876
|
+
return this.element.querySelector("[data-dragging]");
|
|
825
877
|
}
|
|
826
878
|
|
|
827
879
|
reindex() {
|
|
828
|
-
this.dispatch("reindex", {
|
|
880
|
+
this.dispatch("reindex", {bubbles: true, prefix: "content"});
|
|
829
881
|
}
|
|
830
882
|
|
|
831
883
|
reset() {
|
|
832
|
-
this.dispatch("reset", {
|
|
884
|
+
this.dispatch("reset", {bubbles: true, prefix: "content"});
|
|
833
885
|
}
|
|
834
886
|
}
|
|
835
887
|
|
|
888
|
+
/**
|
|
889
|
+
* Swaps two list items. If target is a list, the item is appended.
|
|
890
|
+
*
|
|
891
|
+
* @param target the target element to swap with
|
|
892
|
+
* @param item the item the user is dragging
|
|
893
|
+
*/
|
|
836
894
|
function swap(target, item) {
|
|
837
895
|
if (!target) return;
|
|
838
896
|
if (target === item) return;
|
|
@@ -851,6 +909,28 @@ function swap(target, item) {
|
|
|
851
909
|
}
|
|
852
910
|
}
|
|
853
911
|
|
|
912
|
+
/**
|
|
913
|
+
* Returns true if the event supports copy or copy move.
|
|
914
|
+
*
|
|
915
|
+
* Chrome and Firefox use copy, but Safari only supports copyMove.
|
|
916
|
+
*/
|
|
917
|
+
function copyAllowed(event) {
|
|
918
|
+
return (
|
|
919
|
+
event.dataTransfer.effectAllowed === "copy" ||
|
|
920
|
+
event.dataTransfer.effectAllowed === "copyMove"
|
|
921
|
+
);
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
/**
|
|
925
|
+
* Given an event target, return the closest drop target, if any.
|
|
926
|
+
*/
|
|
927
|
+
function dropTarget(e) {
|
|
928
|
+
return e && (
|
|
929
|
+
e.closest("[data-controller='content--editor--list'] > *") ||
|
|
930
|
+
e.closest("[data-controller='content--editor--list']")
|
|
931
|
+
);
|
|
932
|
+
}
|
|
933
|
+
|
|
854
934
|
class NewItemController extends Controller {
|
|
855
935
|
static targets = ["template"];
|
|
856
936
|
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{Controller as t}from"@hotwired/stimulus";import"trix";class e{static comparator(t,e){return t.index-e.index}constructor(t){this.node=t}get itemId(){return this.node.dataset.contentItemId}get#t(){return this.node.querySelector('input[name$="[id]"]')}set itemId(t){this.itemId!==t&&(this.node.dataset.contentItemId=`${t}`,this.#t.value=`${t}`)}get depth(){return parseInt(this.node.dataset.contentDepth)||0}get#e(){return this.node.querySelector('input[name$="[depth]"]')}set depth(t){this.depth!==t&&(this.node.dataset.contentDepth=`${t}`,this.#e.value=`${t}`)}get index(){return parseInt(this.node.dataset.contentIndex)}get#n(){return this.node.querySelector('input[name$="[index]"]')}set index(t){this.index!==t&&(this.node.dataset.contentIndex=`${t}`,this.#n.value=`${t}`)}get isLayout(){return this.node.hasAttribute("data-content-layout")}get previousItem(){let t=this.node.previousElementSibling;if(t)return new e(t)}get nextItem(){let t=this.node.nextElementSibling;if(t)return new e(t)}hasCollapsedDescendants(){let t=this.#i;return!!t&&t.children.length>0}hasExpandedDescendants(){let t=this.nextItem;return!!t&&t.depth>this.depth}traverse(t){const e=this.#a;t(this),this.#s(t),e.forEach((e=>e.#s(t)))}#s(t){this.hasCollapsedDescendants()&&this.#r.forEach((e=>{t(e),e.#s(t)}))}collapseChild(t){this.#i.appendChild(t.node)}collapse(){let t=this.#i;t||(t=function(t){const e=document.createElement("ol");return e.setAttribute("class","hidden"),e.dataset.contentChildren="",t.appendChild(e),e}(this.node)),this.#a.forEach((e=>t.appendChild(e.node)))}expand(){this.hasCollapsedDescendants()&&Array.from(this.#i.children).reverse().forEach((t=>{this.node.insertAdjacentElement("afterend",t)}))}toggleRule(t,e=!1){this.node.dataset.hasOwnProperty(t)&&!e&&delete this.node.dataset[t],!this.node.dataset.hasOwnProperty(t)&&e&&(this.node.dataset[t]=""),"denyDrag"===t&&(this.node.hasAttribute("draggable")||e||this.node.setAttribute("draggable","true"),this.node.hasAttribute("draggable")&&e&&this.node.removeAttribute("draggable"))}hasItemIdChanged(){return!(this.#t.value===this.itemId)}updateAfterChange(){this.itemId=this.#t.value,this.#n.value=this.index,this.#e.value=this.depth}get#i(){return this.node.querySelector(":scope > [data-content-children]")}get#a(){const t=[];let e=this.nextItem;for(;e&&e.depth>this.depth;)t.push(e),e=e.nextItem;return t}get#r(){return this.hasCollapsedDescendants()?Array.from(this.#i.children).map((t=>new e(t))):[]}}class n{constructor(t){this.node=t}get items(){return t=this.node.querySelectorAll("[data-content-index]"),Array.from(t).map((t=>new e(t)));var t}get state(){const t=this.node.querySelectorAll("li input[type=hidden]");return Array.from(t).map((t=>t.value)).join("/")}reindex(){this.items.map(((t,e)=>t.index=e))}reset(){this.items.sort(e.comparator).forEach((t=>{this.node.appendChild(t.node)}))}}class i{static rules=["denyDeNest","denyNest","denyCollapse","denyExpand","denyRemove","denyDrag","denyEdit"];constructor(t=!1){this.debug=t?(...t)=>console.log(...t):()=>{}}normalize(t){this.firstItemDepthZero(t),this.depthMustBeSet(t),this.itemCannotHaveInvalidDepth(t),this.parentMustBeLayout(t),this.parentCannotHaveExpandedAndCollapsedChildren(t)}update(t){this.rules={},this.parentsCannotDeNest(t),this.rootsCannotDeNest(t),this.onlyLastItemCanDeNest(t),this.nestingNeedsParent(t),this.leavesCannotCollapse(t),this.needHiddenItemsToExpand(t),this.parentsCannotBeDeleted(t),this.parentsCannotBeDragged(t),i.rules.forEach((e=>{t.toggleRule(e,!!this.rules[e])}))}firstItemDepthZero(t){0===t.index&&0!==t.depth&&(this.debug(`enforce depth on item ${t.index}: ${t.depth} => 0`),t.depth=0)}depthMustBeSet(t){(isNaN(t.depth)||t.depth<0)&&(this.debug(`unset depth on item ${t.index}: => 0`),t.depth=0)}itemCannotHaveInvalidDepth(t){const e=t.previousItem;e&&e.depth<t.depth-1&&(this.debug(`invalid depth on item ${t.index}: ${t.depth} => ${e.depth+1}`),t.depth=e.depth+1)}parentMustBeLayout(t){const e=t.previousItem;e&&e.depth<t.depth&&!e.isLayout&&(this.debug(`invalid parent for item ${t.index}: ${t.depth} => ${e.depth}`),t.depth=e.depth)}parentCannotHaveExpandedAndCollapsedChildren(t){t.hasCollapsedDescendants()&&t.hasExpandedDescendants()&&(this.debug(`expanding collapsed children of item ${t.index}`),t.expand())}parentsCannotDeNest(t){t.hasExpandedDescendants()&&this.#o("denyDeNest")}rootsCannotDeNest(t){0===t.depth&&this.#o("denyDeNest")}onlyLastItemCanDeNest(t){const e=t.nextItem;e&&e.depth===t.depth&&!t.isLayout&&this.#o("denyDeNest")}leavesCannotCollapse(t){t.hasExpandedDescendants()||this.#o("denyCollapse")}needHiddenItemsToExpand(t){t.hasCollapsedDescendants()||this.#o("denyExpand")}nestingNeedsParent(t){const e=t.previousItem;e?e.depth<t.depth?this.#o("denyNest"):e.depth!==t.depth||e.isLayout||this.#o("denyNest"):this.#o("denyNest")}parentsCannotBeDeleted(t){t.itemId&&!t.hasExpandedDescendants()||this.#o("denyRemove")}parentsCannotBeDragged(t){t.hasExpandedDescendants()&&this.#o("denyDrag")}#o(t){this.rules[t]=!0}}function a(t){return new e(t.target.closest("[data-content-item]"))}function s(t,e){if(t&&t!==e){if("LI"===t.nodeName){const n=t.compareDocumentPosition(e);n&Node.DOCUMENT_POSITION_FOLLOWING?t.insertAdjacentElement("beforebegin",e):n&Node.DOCUMENT_POSITION_PRECEDING&&t.insertAdjacentElement("afterend",e)}"OL"===t.nodeName&&t.appendChild(e)}}const r=window.Trix;r.config.blockAttributes.heading4={tagName:"h4",terminal:!0,breakOnReturn:!0,group:!1},delete r.config.blockAttributes.heading1;r.config.toolbar.getDefaultHTML=()=>{const{lang:t}=r.config;return`\n<div class="trix-button-row">\n <span class="trix-button-group trix-button-group--text-tools" data-trix-button-group="text-tools">\n <button type="button" class="trix-button trix-button--icon trix-button--icon-bold" data-trix-attribute="bold" data-trix-key="b" title="${t.bold}" tabindex="-1">${t.bold}</button>\n <button type="button" class="trix-button trix-button--icon trix-button--icon-italic" data-trix-attribute="italic" data-trix-key="i" title="${t.italic}" tabindex="-1">${t.italic}</button>\n <button type="button" class="trix-button trix-button--icon trix-button--icon-strike" data-trix-attribute="strike" title="${t.strike}" tabindex="-1">${t.strike}</button>\n <button type="button" class="trix-button trix-button--icon trix-button--icon-link" data-trix-attribute="href" data-trix-action="link" data-trix-key="k" title="${t.link}" tabindex="-1">${t.link}</button>\n </span>\n <span class="trix-button-group trix-button-group--block-tools" data-trix-button-group="block-tools">\n <button type="button" class="trix-button trix-button--icon trix-button--icon-heading-1" data-trix-attribute="heading4" title="${t.heading1}" tabindex="-1">${t.heading1}</button>\n <button type="button" class="trix-button trix-button--icon trix-button--icon-quote" data-trix-attribute="quote" title="${t.quote}" tabindex="-1">${t.quote}</button>\n <button type="button" class="trix-button trix-button--icon trix-button--icon-code" data-trix-attribute="code" title="${t.code}" tabindex="-1">${t.code}</button>\n <button type="button" class="trix-button trix-button--icon trix-button--icon-bullet-list" data-trix-attribute="bullet" title="${t.bullets}" tabindex="-1">${t.bullets}</button>\n <button type="button" class="trix-button trix-button--icon trix-button--icon-number-list" data-trix-attribute="number" title="${t.numbers}" tabindex="-1">${t.numbers}</button>\n <button type="button" class="trix-button trix-button--icon trix-button--icon-decrease-nesting-level" data-trix-action="decreaseNestingLevel" title="${t.outdent}" tabindex="-1">${t.outdent}</button>\n <button type="button" class="trix-button trix-button--icon trix-button--icon-increase-nesting-level" data-trix-action="increaseNestingLevel" title="${t.indent}" tabindex="-1">${t.indent}</button>\n </span>\n <span class="trix-button-group trix-button-group--file-tools" data-trix-button-group="file-tools">\n <button type="button" class="trix-button trix-button--icon trix-button--icon-attach" data-trix-action="attachFiles" title="${t.attachFiles}" tabindex="-1">${t.attachFiles}</button>\n </span>\n <span class="trix-button-group-spacer"></span>\n <span class="trix-button-group trix-button-group--history-tools" data-trix-button-group="history-tools">\n <button type="button" class="trix-button trix-button--icon trix-button--icon-undo" data-trix-action="undo" data-trix-key="z" title="${t.undo}" tabindex="-1">${t.undo}</button>\n <button type="button" class="trix-button trix-button--icon trix-button--icon-redo" data-trix-action="redo" data-trix-key="shift+z" title="${t.redo}" tabindex="-1">${t.redo}</button>\n </span>\n</div>\n<div class="trix-dialogs" data-trix-dialogs>\n <div class="trix-dialog trix-dialog--link" data-trix-dialog="href" data-trix-dialog-attribute="href">\n <div class="trix-dialog__link-fields">\n <input type="text" name="href" pattern="(https?|mailto:|tel:|/|#).*?" class="trix-input trix-input--dialog" placeholder="${t.urlPlaceholder}" aria-label="${t.url}" required data-trix-input>\n <div class="trix-button-group">\n <input type="button" class="trix-button trix-button--dialog" value="${t.link}" data-trix-method="setAttribute">\n <input type="button" class="trix-button trix-button--dialog" value="${t.unlink}" data-trix-method="removeAttribute">\n </div>\n </div>\n </div>\n</div>\n`},document.querySelectorAll("trix-toolbar").forEach((t=>{t.innerHTML=r.config.toolbar.getDefaultHTML()}));const o=[{identifier:"content--editor--container",controllerConstructor:class extends t{static targets=["container"];connect(){this.state=this.container.state,this.reindex()}get container(){return new n(this.containerTarget)}reindex(){this.container.reindex(),this.#d()}reset(){this.container.reset()}drop(t){this.container.reindex();const e=a(t),n=e.previousItem;let i=0;i=void 0===n?-e.depth:n.isLayout&&e.nextItem&&e.nextItem.depth>n.depth?n.depth-e.depth+1:n.depth-e.depth,e.traverse((t=>{t.depth+=i})),this.#d(),t.preventDefault()}remove(t){a(t).node.remove(),this.#d(),t.preventDefault()}nest(t){a(t).traverse((t=>{t.depth+=1})),this.#d(),t.preventDefault()}deNest(t){a(t).traverse((t=>{t.depth-=1})),this.#d(),t.preventDefault()}collapse(t){a(t).collapse(),this.#d(),t.preventDefault()}expand(t){a(t).expand(),this.#d(),t.preventDefault()}#d(){this.updateRequested=!0,setTimeout((()=>{if(!this.updateRequested)return;this.updateRequested=!1;const t=new i;this.container.items.forEach((e=>t.normalize(e))),this.container.items.forEach((e=>t.update(e))),this.#l()}),0)}#l(){this.dispatch("change",{bubbles:!0,prefix:"content",detail:{dirty:this.#u()}})}#u(){return this.container.state!==this.state}}},{identifier:"content--editor--item",controllerConstructor:class extends t{get item(){return new e(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:"content"})}}},{identifier:"content--editor--list",controllerConstructor:class extends t{dragstart(t){if(this.element!==t.target.parentElement)return;const e=t.target;t.dataTransfer.effectAllowed="move",setTimeout((()=>e.dataset.dragging=""))}dragover(t){const e=this.dragItem();if(e)return s(this.dropTarget(t.target),e),t.preventDefault(),!0}dragenter(t){if(t.preventDefault(),"copy"===t.dataTransfer.effectAllowed&&!this.dragItem()){const t=document.createElement("li");t.dataset.dragging="",t.dataset.newItem="",this.element.prepend(t)}}dragleave(t){const e=this.dragItem(),n=this.dropTarget(t.relatedTarget);e&&!n&&e.dataset.hasOwnProperty("newItem")&&e.remove()}drop(t){let e=this.dragItem();if(e){if(t.preventDefault(),delete e.dataset.dragging,s(this.dropTarget(t.target),e),e.dataset.hasOwnProperty("newItem")){const n=e,i=document.createElement("template");i.innerHTML=t.dataTransfer.getData("text/html"),e=i.content.querySelector("li"),this.element.replaceChild(e,n),setTimeout((()=>e.querySelector("[role='button'][value='edit']").click()))}this.dispatch("drop",{target:e,bubbles:!0,prefix:"content"})}}dragend(){const t=this.dragItem();t&&(delete t.dataset.dragging,this.reset())}dragItem(){return this.element.querySelector("[data-dragging]")}dropTarget(t){return t.closest("[data-controller='content--editor--list'] > *")||t.closest("[data-controller='content--editor--list']")}reindex(){this.dispatch("reindex",{bubbles:!0,prefix:"content"})}reset(){this.dispatch("reset",{bubbles:!0,prefix:"content"})}}},{identifier:"content--editor--new-item",controllerConstructor:class extends t{static targets=["template"];dragstart(t){this.element===t.target&&(t.dataTransfer.setData("text/html",this.templateTarget.innerHTML),t.dataTransfer.effectAllowed="copy")}}},{identifier:"content--editor--status-bar",controllerConstructor:class extends t{connect(){this.versionState=this.element.dataset.state}change(t){t.detail&&t.detail.hasOwnProperty("dirty")&&this.update(t.detail)}update({dirty:t}){this.element.dataset.state=t?"dirty":this.versionState}}},{identifier:"content--editor--trix",controllerConstructor:class extends t{trixInitialize(t){}}}];export{o as default};
|
|
1
|
+
import{Controller as t}from"@hotwired/stimulus";import"trix";class e{static comparator(t,e){return t.index-e.index}constructor(t){this.node=t}get itemId(){return this.node.dataset.contentItemId}get#t(){return this.node.querySelector('input[name$="[id]"]')}set itemId(t){this.itemId!==t&&(this.node.dataset.contentItemId=`${t}`,this.#t.value=`${t}`)}get depth(){return parseInt(this.node.dataset.contentDepth)||0}get#e(){return this.node.querySelector('input[name$="[depth]"]')}set depth(t){this.depth!==t&&(this.node.dataset.contentDepth=`${t}`,this.#e.value=`${t}`)}get index(){return parseInt(this.node.dataset.contentIndex)}get#n(){return this.node.querySelector('input[name$="[index]"]')}set index(t){this.index!==t&&(this.node.dataset.contentIndex=`${t}`,this.#n.value=`${t}`)}get isLayout(){return this.node.hasAttribute("data-content-layout")}get previousItem(){let t=this.node.previousElementSibling;if(t)return new e(t)}get nextItem(){let t=this.node.nextElementSibling;if(t)return new e(t)}hasCollapsedDescendants(){let t=this.#i;return!!t&&t.children.length>0}hasExpandedDescendants(){let t=this.nextItem;return!!t&&t.depth>this.depth}traverse(t){const e=this.#a;t(this),this.#s(t),e.forEach((e=>e.#s(t)))}#s(t){this.hasCollapsedDescendants()&&this.#r.forEach((e=>{t(e),e.#s(t)}))}collapseChild(t){this.#i.appendChild(t.node)}collapse(){let t=this.#i;t||(t=function(t){const e=document.createElement("ol");return e.setAttribute("class","hidden"),e.dataset.contentChildren="",t.appendChild(e),e}(this.node)),this.#a.forEach((e=>t.appendChild(e.node)))}expand(){this.hasCollapsedDescendants()&&Array.from(this.#i.children).reverse().forEach((t=>{this.node.insertAdjacentElement("afterend",t)}))}toggleRule(t,e=!1){this.node.dataset.hasOwnProperty(t)&&!e&&delete this.node.dataset[t],!this.node.dataset.hasOwnProperty(t)&&e&&(this.node.dataset[t]=""),"denyDrag"===t&&(this.node.hasAttribute("draggable")||e||this.node.setAttribute("draggable","true"),this.node.hasAttribute("draggable")&&e&&this.node.removeAttribute("draggable"))}hasItemIdChanged(){return!(this.#t.value===this.itemId)}updateAfterChange(){this.itemId=this.#t.value,this.#n.value=this.index,this.#e.value=this.depth}get#i(){return this.node.querySelector(":scope > [data-content-children]")}get#a(){const t=[];let e=this.nextItem;for(;e&&e.depth>this.depth;)t.push(e),e=e.nextItem;return t}get#r(){return this.hasCollapsedDescendants()?Array.from(this.#i.children).map((t=>new e(t))):[]}}class n{constructor(t){this.node=t}get items(){return t=this.node.querySelectorAll("[data-content-index]"),Array.from(t).map((t=>new e(t)));var t}get state(){const t=this.node.querySelectorAll("li input[type=hidden]");return Array.from(t).map((t=>t.value)).join("/")}reindex(){this.items.map(((t,e)=>t.index=e))}reset(){this.items.sort(e.comparator).forEach((t=>{this.node.appendChild(t.node)}))}}class i{static rules=["denyDeNest","denyNest","denyCollapse","denyExpand","denyRemove","denyDrag","denyEdit"];constructor(t=!1){this.debug=t?(...t)=>console.log(...t):()=>{}}normalize(t){this.firstItemDepthZero(t),this.depthMustBeSet(t),this.itemCannotHaveInvalidDepth(t),this.parentMustBeLayout(t),this.parentCannotHaveExpandedAndCollapsedChildren(t)}update(t){this.rules={},this.parentsCannotDeNest(t),this.rootsCannotDeNest(t),this.onlyLastItemCanDeNest(t),this.nestingNeedsParent(t),this.leavesCannotCollapse(t),this.needHiddenItemsToExpand(t),this.parentsCannotBeDeleted(t),this.parentsCannotBeDragged(t),i.rules.forEach((e=>{t.toggleRule(e,!!this.rules[e])}))}firstItemDepthZero(t){0===t.index&&0!==t.depth&&(this.debug(`enforce depth on item ${t.index}: ${t.depth} => 0`),t.depth=0)}depthMustBeSet(t){(isNaN(t.depth)||t.depth<0)&&(this.debug(`unset depth on item ${t.index}: => 0`),t.depth=0)}itemCannotHaveInvalidDepth(t){const e=t.previousItem;e&&e.depth<t.depth-1&&(this.debug(`invalid depth on item ${t.index}: ${t.depth} => ${e.depth+1}`),t.depth=e.depth+1)}parentMustBeLayout(t){const e=t.previousItem;e&&e.depth<t.depth&&!e.isLayout&&(this.debug(`invalid parent for item ${t.index}: ${t.depth} => ${e.depth}`),t.depth=e.depth)}parentCannotHaveExpandedAndCollapsedChildren(t){t.hasCollapsedDescendants()&&t.hasExpandedDescendants()&&(this.debug(`expanding collapsed children of item ${t.index}`),t.expand())}parentsCannotDeNest(t){t.hasExpandedDescendants()&&this.#o("denyDeNest")}rootsCannotDeNest(t){0===t.depth&&this.#o("denyDeNest")}onlyLastItemCanDeNest(t){const e=t.nextItem;e&&e.depth===t.depth&&!t.isLayout&&this.#o("denyDeNest")}leavesCannotCollapse(t){t.hasExpandedDescendants()||this.#o("denyCollapse")}needHiddenItemsToExpand(t){t.hasCollapsedDescendants()||this.#o("denyExpand")}nestingNeedsParent(t){const e=t.previousItem;e?e.depth<t.depth?this.#o("denyNest"):e.depth!==t.depth||e.isLayout||this.#o("denyNest"):this.#o("denyNest")}parentsCannotBeDeleted(t){t.itemId&&!t.hasExpandedDescendants()||this.#o("denyRemove")}parentsCannotBeDragged(t){t.hasExpandedDescendants()&&this.#o("denyDrag")}#o(t){this.rules[t]=!0}}function a(t){return new e(t.target.closest("[data-content-item]"))}function s(t,e){if(t&&t!==e){if("LI"===t.nodeName){const n=t.compareDocumentPosition(e);n&Node.DOCUMENT_POSITION_FOLLOWING?t.insertAdjacentElement("beforebegin",e):n&Node.DOCUMENT_POSITION_PRECEDING&&t.insertAdjacentElement("afterend",e)}"OL"===t.nodeName&&t.appendChild(e)}}function r(t){return t&&(t.closest("[data-controller='content--editor--list'] > *")||t.closest("[data-controller='content--editor--list']"))}const o=window.Trix;o.config.blockAttributes.heading4={tagName:"h4",terminal:!0,breakOnReturn:!0,group:!1},delete o.config.blockAttributes.heading1;o.config.toolbar.getDefaultHTML=()=>{const{lang:t}=o.config;return`\n<div class="trix-button-row">\n <span class="trix-button-group trix-button-group--text-tools" data-trix-button-group="text-tools">\n <button type="button" class="trix-button trix-button--icon trix-button--icon-bold" data-trix-attribute="bold" data-trix-key="b" title="${t.bold}" tabindex="-1">${t.bold}</button>\n <button type="button" class="trix-button trix-button--icon trix-button--icon-italic" data-trix-attribute="italic" data-trix-key="i" title="${t.italic}" tabindex="-1">${t.italic}</button>\n <button type="button" class="trix-button trix-button--icon trix-button--icon-strike" data-trix-attribute="strike" title="${t.strike}" tabindex="-1">${t.strike}</button>\n <button type="button" class="trix-button trix-button--icon trix-button--icon-link" data-trix-attribute="href" data-trix-action="link" data-trix-key="k" title="${t.link}" tabindex="-1">${t.link}</button>\n </span>\n <span class="trix-button-group trix-button-group--block-tools" data-trix-button-group="block-tools">\n <button type="button" class="trix-button trix-button--icon trix-button--icon-heading-1" data-trix-attribute="heading4" title="${t.heading1}" tabindex="-1">${t.heading1}</button>\n <button type="button" class="trix-button trix-button--icon trix-button--icon-quote" data-trix-attribute="quote" title="${t.quote}" tabindex="-1">${t.quote}</button>\n <button type="button" class="trix-button trix-button--icon trix-button--icon-code" data-trix-attribute="code" title="${t.code}" tabindex="-1">${t.code}</button>\n <button type="button" class="trix-button trix-button--icon trix-button--icon-bullet-list" data-trix-attribute="bullet" title="${t.bullets}" tabindex="-1">${t.bullets}</button>\n <button type="button" class="trix-button trix-button--icon trix-button--icon-number-list" data-trix-attribute="number" title="${t.numbers}" tabindex="-1">${t.numbers}</button>\n <button type="button" class="trix-button trix-button--icon trix-button--icon-decrease-nesting-level" data-trix-action="decreaseNestingLevel" title="${t.outdent}" tabindex="-1">${t.outdent}</button>\n <button type="button" class="trix-button trix-button--icon trix-button--icon-increase-nesting-level" data-trix-action="increaseNestingLevel" title="${t.indent}" tabindex="-1">${t.indent}</button>\n </span>\n <span class="trix-button-group trix-button-group--file-tools" data-trix-button-group="file-tools">\n <button type="button" class="trix-button trix-button--icon trix-button--icon-attach" data-trix-action="attachFiles" title="${t.attachFiles}" tabindex="-1">${t.attachFiles}</button>\n </span>\n <span class="trix-button-group-spacer"></span>\n <span class="trix-button-group trix-button-group--history-tools" data-trix-button-group="history-tools">\n <button type="button" class="trix-button trix-button--icon trix-button--icon-undo" data-trix-action="undo" data-trix-key="z" title="${t.undo}" tabindex="-1">${t.undo}</button>\n <button type="button" class="trix-button trix-button--icon trix-button--icon-redo" data-trix-action="redo" data-trix-key="shift+z" title="${t.redo}" tabindex="-1">${t.redo}</button>\n </span>\n</div>\n<div class="trix-dialogs" data-trix-dialogs>\n <div class="trix-dialog trix-dialog--link" data-trix-dialog="href" data-trix-dialog-attribute="href">\n <div class="trix-dialog__link-fields">\n <input type="text" name="href" pattern="(https?|mailto:|tel:|/|#).*?" class="trix-input trix-input--dialog" placeholder="${t.urlPlaceholder}" aria-label="${t.url}" required data-trix-input>\n <div class="trix-button-group">\n <input type="button" class="trix-button trix-button--dialog" value="${t.link}" data-trix-method="setAttribute">\n <input type="button" class="trix-button trix-button--dialog" value="${t.unlink}" data-trix-method="removeAttribute">\n </div>\n </div>\n </div>\n</div>\n`},document.querySelectorAll("trix-toolbar").forEach((t=>{t.innerHTML=o.config.toolbar.getDefaultHTML()}));const d=[{identifier:"content--editor--container",controllerConstructor:class extends t{static targets=["container"];connect(){this.state=this.container.state,this.reindex()}get container(){return new n(this.containerTarget)}reindex(){this.container.reindex(),this.#d()}reset(){this.container.reset()}drop(t){this.container.reindex();const e=a(t),n=e.previousItem;let i=0;i=void 0===n?-e.depth:n.isLayout&&e.nextItem&&e.nextItem.depth>n.depth?n.depth-e.depth+1:n.depth-e.depth,e.traverse((t=>{t.depth+=i})),this.#d(),t.preventDefault()}remove(t){a(t).node.remove(),this.#d(),t.preventDefault()}nest(t){a(t).traverse((t=>{t.depth+=1})),this.#d(),t.preventDefault()}deNest(t){a(t).traverse((t=>{t.depth-=1})),this.#d(),t.preventDefault()}collapse(t){a(t).collapse(),this.#d(),t.preventDefault()}expand(t){a(t).expand(),this.#d(),t.preventDefault()}#d(){this.updateRequested=!0,setTimeout((()=>{if(!this.updateRequested)return;this.updateRequested=!1;const t=new i;this.container.items.forEach((e=>t.normalize(e))),this.container.items.forEach((e=>t.update(e))),this.#l()}),0)}#l(){this.dispatch("change",{bubbles:!0,prefix:"content",detail:{dirty:this.#u()}})}#u(){return this.container.state!==this.state}}},{identifier:"content--editor--item",controllerConstructor:class extends t{get item(){return new e(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:"content"})}}},{identifier:"content--editor--list",controllerConstructor:class extends t{connect(){this.enterCount=0}dragstart(t){if(this.element!==t.target.parentElement)return;const e=t.target;t.dataTransfer.effectAllowed="move",requestAnimationFrame((()=>e.dataset.dragging=""))}dragover(t){const e=this.dragItem;if(e)return s(r(t.target),e),t.preventDefault(),!0}dragenter(t){if(t.preventDefault(),this.enterCount++,function(t){return"copy"===t.dataTransfer.effectAllowed||"copyMove"===t.dataTransfer.effectAllowed}(t)&&!this.dragItem){const t=document.createElement("li");t.dataset.dragging="",t.dataset.newItem="",this.element.appendChild(t)}}dragleave(t){this.enterCount--,this.enterCount<=0&&this.dragItem.dataset.hasOwnProperty("newItem")&&this.cancelDrag(t)}drop(t){let e=this.dragItem;if(e){if(t.preventDefault(),delete e.dataset.dragging,s(r(t.target),e),e.dataset.hasOwnProperty("newItem")){const n=e,i=document.createElement("template");i.innerHTML=t.dataTransfer.getData("text/html"),e=i.content.querySelector("li"),this.element.replaceChild(e,n),requestAnimationFrame((()=>e.querySelector("[role='button'][value='edit']").click()))}this.dispatch("drop",{target:e,bubbles:!0,prefix:"content"})}}dragend(){const t=this.dragItem;t&&(t.dataset.hasOwnProperty("newItem")?t.remove():(delete t.dataset.dragging,this.reset()))}get isDragging(){return!!this.dragItem}get dragItem(){return this.element.querySelector("[data-dragging]")}reindex(){this.dispatch("reindex",{bubbles:!0,prefix:"content"})}reset(){this.dispatch("reset",{bubbles:!0,prefix:"content"})}}},{identifier:"content--editor--new-item",controllerConstructor:class extends t{static targets=["template"];dragstart(t){this.element===t.target&&(t.dataTransfer.setData("text/html",this.templateTarget.innerHTML),t.dataTransfer.effectAllowed="copy")}}},{identifier:"content--editor--status-bar",controllerConstructor:class extends t{connect(){this.versionState=this.element.dataset.state}change(t){t.detail&&t.detail.hasOwnProperty("dirty")&&this.update(t.detail)}update({dirty:t}){this.element.dataset.state=t?"dirty":this.versionState}}},{identifier:"content--editor--trix",controllerConstructor:class extends t{trixInitialize(t){}}}];export{d as default};
|
|
2
2
|
//# sourceMappingURL=content.min.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"content.min.js","sources":["../../../javascript/content/editor/item.js","../../../javascript/content/editor/container.js","../../../javascript/content/editor/rules_engine.js","../../../javascript/content/editor/container_controller.js","../../../javascript/content/editor/list_controller.js","../../../javascript/content/editor/trix_controller.js","../../../javascript/content/application.js","../../../javascript/content/editor/item_controller.js","../../../javascript/content/editor/new_item_controller.js","../../../javascript/content/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-content-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[`contentItemId`];\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[`contentItemId`] = `${id}`;\n this.#itemIdInput.value = `${id}`;\n }\n\n /**\n * @returns {number} logical nesting depth of node in container\n */\n get depth() {\n return parseInt(this.node.dataset[`contentDepth`]) || 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[`contentDepth`] = `${depth}`;\n this.#depthInput.value = `${depth}`;\n }\n\n /**\n * @returns {number} logical index of node in container (pre-order traversal)\n */\n get index() {\n return parseInt(this.node.dataset[`contentIndex`]);\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[`contentIndex`] = `${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 * Move the given item into this element's hidden children list.\n * Assumes the list already exists.\n *\n * @param item {Item}\n */\n collapseChild(item) {\n this.#childrenListElement.appendChild(item.node);\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 container.\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-content-children]\n */\n get #childrenListElement() {\n return this.node.querySelector(`:scope > [data-content-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-content-index]\n * @returns {Element} ol[data-content-children]\n */\nfunction createChildrenList(node) {\n const childrenList = document.createElement(\"ol\");\n childrenList.setAttribute(\"class\", \"hidden\");\n\n // if objectType is \"rich-content\" set richContentChildren as a data attribute\n childrenList.dataset[`contentChildren`] = \"\";\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 Container {\n /**\n * @param node {Element} content editor list\n */\n constructor(node) {\n this.node = node;\n }\n\n /**\n * @return {Item[]} an ordered list of all items in the container\n */\n get items() {\n return createItemList(this.node.querySelectorAll(\"[data-content-index]\"));\n }\n\n /**\n * @return {String} a serialized description of the structure of the container\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(debug = false) {\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.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.onlyLastItemCanDeNest(item);\n this.nestingNeedsParent(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 * 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 * De-nesting an item that has siblings would make it a container.\n *\n * @param {Item} item\n */\n onlyLastItemCanDeNest(item) {\n const next = item.nextItem;\n if (next && next.depth === item.depth && !item.isLayout)\n 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 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 Container from \"./container\";\nimport RulesEngine from \"./rules_engine\";\n\nexport default class ContainerController extends Controller {\n static targets = [\"container\"];\n\n connect() {\n this.state = this.container.state;\n\n this.reindex();\n }\n\n get container() {\n return new Container(this.containerTarget);\n }\n\n reindex() {\n this.container.reindex();\n this.#update();\n }\n\n reset() {\n this.container.reset();\n }\n\n drop(event) {\n this.container.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 (\n previous.isLayout &&\n item.nextItem &&\n item.nextItem.depth > previous.depth\n ) {\n // if previous is a layout and 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();\n this.container.items.forEach((item) => engine.normalize(item));\n this.container.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: \"content\",\n detail: { dirty: this.#isDirty() },\n });\n }\n\n #isDirty() {\n return this.container.state !== this.state;\n }\n}\n\nfunction getEventItem(event) {\n return new Item(event.target.closest(\"[data-content-item]\"));\n}\n","import { Controller } from \"@hotwired/stimulus\";\n\nexport default class ListController extends Controller {\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 setTimeout(() => (target.dataset.dragging = \"\"));\n }\n\n dragover(event) {\n const item = this.dragItem();\n if (!item) return;\n\n swap(this.dropTarget(event.target), item);\n\n event.preventDefault();\n return true;\n }\n\n dragenter(event) {\n event.preventDefault();\n\n if (event.dataTransfer.effectAllowed === \"copy\" && !this.dragItem()) {\n const item = document.createElement(\"li\");\n item.dataset.dragging = \"\";\n item.dataset.newItem = \"\";\n this.element.prepend(item);\n }\n }\n\n dragleave(event) {\n const item = this.dragItem();\n const related = this.dropTarget(event.relatedTarget);\n\n // ignore if item is not set or we're moving into a valid drop target\n if (!item || related) return;\n\n // remove item if it's a new item\n if (item.dataset.hasOwnProperty(\"newItem\")) {\n item.remove();\n }\n }\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(this.dropTarget(event.target), item);\n\n if (item.dataset.hasOwnProperty(\"newItem\")) {\n const placeholder = item;\n const template = document.createElement(\"template\");\n template.innerHTML = event.dataTransfer.getData(\"text/html\");\n item = template.content.querySelector(\"li\");\n\n this.element.replaceChild(item, placeholder);\n setTimeout(() =>\n item.querySelector(\"[role='button'][value='edit']\").click()\n );\n }\n\n this.dispatch(\"drop\", { target: item, bubbles: true, prefix: \"content\" });\n }\n\n dragend() {\n const item = this.dragItem();\n if (!item) return;\n\n delete item.dataset.dragging;\n this.reset();\n }\n\n dragItem() {\n return this.element.querySelector(\"[data-dragging]\");\n }\n\n dropTarget(e) {\n return (\n e.closest(\"[data-controller='content--editor--list'] > *\") ||\n e.closest(\"[data-controller='content--editor--list']\")\n );\n }\n\n reindex() {\n this.dispatch(\"reindex\", { bubbles: true, prefix: \"content\" });\n }\n\n reset() {\n this.dispatch(\"reset\", { bubbles: true, prefix: \"content\" });\n }\n}\n\nfunction swap(target, item) {\n if (!target) return;\n if (target === item) return;\n\n if (target.nodeName === \"LI\") {\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 if (target.nodeName === \"OL\") {\n target.appendChild(item);\n }\n}\n","import { Controller } from \"@hotwired/stimulus\";\nimport \"trix\";\n\n// Note, action_text 7.1.2 changes how Trix is bundled and loaded. This\n// seems to have broken the default export from trix. This is a workaround\n// that relies on the backwards compatibility of the old export to window.Trix.\nconst Trix = window.Trix;\n\n// Stimulus controller doesn't do anything, but having one ensures that trix\n// will be lazy loaded when a trix-editor is added to the dom.\nexport default class TrixController extends Controller {\n trixInitialize(e) {\n // noop, useful as an extension point for registering behaviour on load\n }\n}\n\n// Add H4 as an acceptable tag\nTrix.config.blockAttributes[\"heading4\"] = {\n tagName: \"h4\",\n terminal: true,\n breakOnReturn: true,\n group: false,\n};\n\n// Remove H1 from trix list of acceptable tags\ndelete Trix.config.blockAttributes.heading1;\n\n/**\n * Allow users to enter path and fragment URIs which the input[type=url] browser\n * input does not permit. Uses a permissive regex pattern which is not suitable\n * for untrusted use cases.\n */\nconst LINK_PATTERN = \"(https?|mailto:|tel:|/|#).*?\";\n\n/**\n * Customize default toolbar:\n *\n * * headings: h4 instead of h1\n * * links: use type=text instead of type=url\n *\n * @returns {String} toolbar html fragment\n */\nTrix.config.toolbar.getDefaultHTML = () => {\n const { lang } = Trix.config;\n return `\n<div class=\"trix-button-row\">\n <span class=\"trix-button-group trix-button-group--text-tools\" data-trix-button-group=\"text-tools\">\n <button type=\"button\" class=\"trix-button trix-button--icon trix-button--icon-bold\" data-trix-attribute=\"bold\" data-trix-key=\"b\" title=\"${lang.bold}\" tabindex=\"-1\">${lang.bold}</button>\n <button type=\"button\" class=\"trix-button trix-button--icon trix-button--icon-italic\" data-trix-attribute=\"italic\" data-trix-key=\"i\" title=\"${lang.italic}\" tabindex=\"-1\">${lang.italic}</button>\n <button type=\"button\" class=\"trix-button trix-button--icon trix-button--icon-strike\" data-trix-attribute=\"strike\" title=\"${lang.strike}\" tabindex=\"-1\">${lang.strike}</button>\n <button type=\"button\" class=\"trix-button trix-button--icon trix-button--icon-link\" data-trix-attribute=\"href\" data-trix-action=\"link\" data-trix-key=\"k\" title=\"${lang.link}\" tabindex=\"-1\">${lang.link}</button>\n </span>\n <span class=\"trix-button-group trix-button-group--block-tools\" data-trix-button-group=\"block-tools\">\n <button type=\"button\" class=\"trix-button trix-button--icon trix-button--icon-heading-1\" data-trix-attribute=\"heading4\" title=\"${lang.heading1}\" tabindex=\"-1\">${lang.heading1}</button>\n <button type=\"button\" class=\"trix-button trix-button--icon trix-button--icon-quote\" data-trix-attribute=\"quote\" title=\"${lang.quote}\" tabindex=\"-1\">${lang.quote}</button>\n <button type=\"button\" class=\"trix-button trix-button--icon trix-button--icon-code\" data-trix-attribute=\"code\" title=\"${lang.code}\" tabindex=\"-1\">${lang.code}</button>\n <button type=\"button\" class=\"trix-button trix-button--icon trix-button--icon-bullet-list\" data-trix-attribute=\"bullet\" title=\"${lang.bullets}\" tabindex=\"-1\">${lang.bullets}</button>\n <button type=\"button\" class=\"trix-button trix-button--icon trix-button--icon-number-list\" data-trix-attribute=\"number\" title=\"${lang.numbers}\" tabindex=\"-1\">${lang.numbers}</button>\n <button type=\"button\" class=\"trix-button trix-button--icon trix-button--icon-decrease-nesting-level\" data-trix-action=\"decreaseNestingLevel\" title=\"${lang.outdent}\" tabindex=\"-1\">${lang.outdent}</button>\n <button type=\"button\" class=\"trix-button trix-button--icon trix-button--icon-increase-nesting-level\" data-trix-action=\"increaseNestingLevel\" title=\"${lang.indent}\" tabindex=\"-1\">${lang.indent}</button>\n </span>\n <span class=\"trix-button-group trix-button-group--file-tools\" data-trix-button-group=\"file-tools\">\n <button type=\"button\" class=\"trix-button trix-button--icon trix-button--icon-attach\" data-trix-action=\"attachFiles\" title=\"${lang.attachFiles}\" tabindex=\"-1\">${lang.attachFiles}</button>\n </span>\n <span class=\"trix-button-group-spacer\"></span>\n <span class=\"trix-button-group trix-button-group--history-tools\" data-trix-button-group=\"history-tools\">\n <button type=\"button\" class=\"trix-button trix-button--icon trix-button--icon-undo\" data-trix-action=\"undo\" data-trix-key=\"z\" title=\"${lang.undo}\" tabindex=\"-1\">${lang.undo}</button>\n <button type=\"button\" class=\"trix-button trix-button--icon trix-button--icon-redo\" data-trix-action=\"redo\" data-trix-key=\"shift+z\" title=\"${lang.redo}\" tabindex=\"-1\">${lang.redo}</button>\n </span>\n</div>\n<div class=\"trix-dialogs\" data-trix-dialogs>\n <div class=\"trix-dialog trix-dialog--link\" data-trix-dialog=\"href\" data-trix-dialog-attribute=\"href\">\n <div class=\"trix-dialog__link-fields\">\n <input type=\"text\" name=\"href\" pattern=\"${LINK_PATTERN}\" class=\"trix-input trix-input--dialog\" placeholder=\"${lang.urlPlaceholder}\" aria-label=\"${lang.url}\" required data-trix-input>\n <div class=\"trix-button-group\">\n <input type=\"button\" class=\"trix-button trix-button--dialog\" value=\"${lang.link}\" data-trix-method=\"setAttribute\">\n <input type=\"button\" class=\"trix-button trix-button--dialog\" value=\"${lang.unlink}\" data-trix-method=\"removeAttribute\">\n </div>\n </div>\n </div>\n</div>\n`;\n};\n\n/**\n * If the <trix-editor> element is in the HTML when Trix loads, then Trix will have already injected the toolbar content\n * before our code gets a chance to run. Fix that now.\n *\n * Note: in Trix 2 this is likely to no longer be necessary.\n */\ndocument.querySelectorAll(\"trix-toolbar\").forEach((e) => {\n e.innerHTML = Trix.config.toolbar.getDefaultHTML();\n});\n","import ContainerController from \"./editor/container_controller\";\nimport ItemController from \"./editor/item_controller\";\nimport ListController from \"./editor/list_controller\";\nimport NewItemController from \"./editor/new_item_controller\";\nimport StatusBarController from \"./editor/status_bar_controller\";\nimport TrixController from \"./editor/trix_controller\";\n\nconst Definitions = [\n {\n identifier: \"content--editor--container\",\n controllerConstructor: ContainerController,\n },\n {\n identifier: \"content--editor--item\",\n controllerConstructor: ItemController,\n },\n {\n identifier: \"content--editor--list\",\n controllerConstructor: ListController,\n },\n {\n identifier: \"content--editor--new-item\",\n controllerConstructor: NewItemController,\n },\n {\n identifier: \"content--editor--status-bar\",\n controllerConstructor: StatusBarController,\n },\n {\n identifier: \"content--editor--trix\",\n controllerConstructor: TrixController,\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: \"content\" });\n }\n}\n","import { Controller } from \"@hotwired/stimulus\";\n\nexport default class NewItemController extends Controller {\n static targets = [\"template\"];\n\n dragstart(event) {\n if (this.element !== event.target) return;\n\n event.dataTransfer.setData(\"text/html\", this.templateTarget.innerHTML);\n event.dataTransfer.effectAllowed = \"copy\";\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 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","collapseChild","appendChild","collapse","listElement","document","createElement","setAttribute","createChildrenList","child","expand","Array","from","reverse","insertAdjacentElement","toggleRule","rule","deny","hasOwnProperty","removeAttribute","hasItemIdChanged","updateAfterChange","descendants","push","map","Container","items","nodes","querySelectorAll","state","inputs","e","join","reindex","reset","sort","RulesEngine","static","debug","args","console","log","normalize","firstItemDepthZero","depthMustBeSet","itemCannotHaveInvalidDepth","parentMustBeLayout","parentCannotHaveExpandedAndCollapsedChildren","update","rules","parentsCannotDeNest","rootsCannotDeNest","onlyLastItemCanDeNest","nestingNeedsParent","leavesCannotCollapse","needHiddenItemsToExpand","parentsCannotBeDeleted","parentsCannotBeDragged","isNaN","previous","next","getEventItem","event","target","closest","swap","nodeName","positionComparison","compareDocumentPosition","Node","DOCUMENT_POSITION_FOLLOWING","DOCUMENT_POSITION_PRECEDING","Trix","window","config","blockAttributes","tagName","terminal","breakOnReturn","group","heading1","toolbar","getDefaultHTML","lang","bold","italic","strike","link","quote","code","bullets","numbers","outdent","indent","attachFiles","undo","redo","urlPlaceholder","url","unlink","innerHTML","Definitions","identifier","controllerConstructor","Controller","connect","container","containerTarget","drop","delta","undefined","preventDefault","remove","nest","deNest","updateRequested","setTimeout","engine","notifyChange","dispatch","bubbles","prefix","detail","dirty","isDirty","li","ol","element","dragstart","parentElement","dataTransfer","effectAllowed","dragging","dragover","dragItem","dropTarget","dragenter","newItem","prepend","dragleave","related","relatedTarget","placeholder","template","getData","content","replaceChild","click","dragend","setData","templateTarget","versionState","change","trixInitialize"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"content.min.js","sources":["../../../javascript/content/editor/item.js","../../../javascript/content/editor/container.js","../../../javascript/content/editor/rules_engine.js","../../../javascript/content/editor/container_controller.js","../../../javascript/content/editor/list_controller.js","../../../javascript/content/editor/trix_controller.js","../../../javascript/content/application.js","../../../javascript/content/editor/item_controller.js","../../../javascript/content/editor/new_item_controller.js","../../../javascript/content/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-content-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[`contentItemId`];\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[`contentItemId`] = `${id}`;\n this.#itemIdInput.value = `${id}`;\n }\n\n /**\n * @returns {number} logical nesting depth of node in container\n */\n get depth() {\n return parseInt(this.node.dataset[`contentDepth`]) || 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[`contentDepth`] = `${depth}`;\n this.#depthInput.value = `${depth}`;\n }\n\n /**\n * @returns {number} logical index of node in container (pre-order traversal)\n */\n get index() {\n return parseInt(this.node.dataset[`contentIndex`]);\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[`contentIndex`] = `${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 * Move the given item into this element's hidden children list.\n * Assumes the list already exists.\n *\n * @param item {Item}\n */\n collapseChild(item) {\n this.#childrenListElement.appendChild(item.node);\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 container.\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-content-children]\n */\n get #childrenListElement() {\n return this.node.querySelector(`:scope > [data-content-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-content-index]\n * @returns {Element} ol[data-content-children]\n */\nfunction createChildrenList(node) {\n const childrenList = document.createElement(\"ol\");\n childrenList.setAttribute(\"class\", \"hidden\");\n\n // if objectType is \"rich-content\" set richContentChildren as a data attribute\n childrenList.dataset[`contentChildren`] = \"\";\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 Container {\n /**\n * @param node {Element} content editor list\n */\n constructor(node) {\n this.node = node;\n }\n\n /**\n * @return {Item[]} an ordered list of all items in the container\n */\n get items() {\n return createItemList(this.node.querySelectorAll(\"[data-content-index]\"));\n }\n\n /**\n * @return {String} a serialized description of the structure of the container\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(debug = false) {\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.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.onlyLastItemCanDeNest(item);\n this.nestingNeedsParent(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 * 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 * De-nesting an item that has siblings would make it a container.\n *\n * @param {Item} item\n */\n onlyLastItemCanDeNest(item) {\n const next = item.nextItem;\n if (next && next.depth === item.depth && !item.isLayout)\n 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 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 Container from \"./container\";\nimport RulesEngine from \"./rules_engine\";\n\nexport default class ContainerController extends Controller {\n static targets = [\"container\"];\n\n connect() {\n this.state = this.container.state;\n\n this.reindex();\n }\n\n get container() {\n return new Container(this.containerTarget);\n }\n\n reindex() {\n this.container.reindex();\n this.#update();\n }\n\n reset() {\n this.container.reset();\n }\n\n drop(event) {\n this.container.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 (\n previous.isLayout &&\n item.nextItem &&\n item.nextItem.depth > previous.depth\n ) {\n // if previous is a layout and 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();\n this.container.items.forEach((item) => engine.normalize(item));\n this.container.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: \"content\",\n detail: { dirty: this.#isDirty() },\n });\n }\n\n #isDirty() {\n return this.container.state !== this.state;\n }\n}\n\nfunction getEventItem(event) {\n return new Item(event.target.closest(\"[data-content-item]\"));\n}\n","import {Controller} from \"@hotwired/stimulus\";\n\nexport default class ListController extends Controller {\n connect() {\n this.enterCount = 0;\n }\n\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 * As a special case, if the item is dragged over placeholder space at the end\n * of the list, move the item to the bottom of the list instead. This allows\n * users to hit the list element more easily when adding new items to an empty\n * list.\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 drags an item into the list, create a placeholder item to\n * represent the new item. Note that we can't access the drag data\n * until drop, so we assume that this is our template item for now.\n *\n * Users can cancel the drag by dragging the item out of the list or by\n * pressing escape. Both are handled by `cancelDrag`.\n *\n * @param event {DragEvent}\n */\n dragenter(event) {\n event.preventDefault();\n\n // Safari doesn't support relatedTarget, so we count enter/leave pairs\n this.enterCount++;\n\n if (copyAllowed(event) && !this.dragItem) {\n const item = document.createElement(\"li\");\n item.dataset.dragging = \"\";\n item.dataset.newItem = \"\";\n this.element.appendChild(item);\n }\n }\n\n /**\n * When the user drags the item out of the list, remove the placeholder.\n * This allows users to cancel the drag by dragging the item out of the list.\n *\n * @param event {DragEvent}\n */\n dragleave(event) {\n // Safari doesn't support relatedTarget, so we count enter/leave pairs\n // https://bugs.webkit.org/show_bug.cgi?id=66547\n this.enterCount--;\n\n if (this.enterCount <= 0 && this.dragItem.dataset.hasOwnProperty(\"newItem\")) {\n this.cancelDrag(event);\n }\n }\n\n /**\n * When the user drops an item into the list, end the drag and reindex the list.\n *\n * If the item is a new item, we replace the placeholder with the template\n * item data from the dataTransfer API.\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 if (item.dataset.hasOwnProperty(\"newItem\")) {\n const placeholder = item;\n const template = document.createElement(\"template\");\n template.innerHTML = event.dataTransfer.getData(\"text/html\");\n item = template.content.querySelector(\"li\");\n\n this.element.replaceChild(item, placeholder);\n requestAnimationFrame(() =>\n item.querySelector(\"[role='button'][value='edit']\").click()\n );\n }\n\n this.dispatch(\"drop\", {target: item, bubbles: true, prefix: \"content\"});\n }\n\n /**\n * End an in-progress drag. If the item is a new item, remove it, otherwise\n * reset the item's style and restore its original position in the list.\n */\n dragend() {\n const item = this.dragItem;\n\n if (!item) {\n } else if (item.dataset.hasOwnProperty(\"newItem\")) {\n item.remove();\n } else {\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: \"content\"});\n }\n\n reset() {\n this.dispatch(\"reset\", {bubbles: true, prefix: \"content\"});\n }\n}\n\n/**\n * Swaps two list items. If target is a list, the item is appended.\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 if (target.nodeName === \"LI\") {\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 if (target.nodeName === \"OL\") {\n target.appendChild(item);\n }\n}\n\n/**\n * Returns true if the event supports copy or copy move.\n *\n * Chrome and Firefox use copy, but Safari only supports copyMove.\n */\nfunction copyAllowed(event) {\n return (\n event.dataTransfer.effectAllowed === \"copy\" ||\n event.dataTransfer.effectAllowed === \"copyMove\"\n );\n}\n\n/**\n * Given an event target, return the closest drop target, if any.\n */\nfunction dropTarget(e) {\n return e && (\n e.closest(\"[data-controller='content--editor--list'] > *\") ||\n e.closest(\"[data-controller='content--editor--list']\")\n );\n}\n","import { Controller } from \"@hotwired/stimulus\";\nimport \"trix\";\n\n// Note, action_text 7.1.2 changes how Trix is bundled and loaded. This\n// seems to have broken the default export from trix. This is a workaround\n// that relies on the backwards compatibility of the old export to window.Trix.\nconst Trix = window.Trix;\n\n// Stimulus controller doesn't do anything, but having one ensures that trix\n// will be lazy loaded when a trix-editor is added to the dom.\nexport default class TrixController extends Controller {\n trixInitialize(e) {\n // noop, useful as an extension point for registering behaviour on load\n }\n}\n\n// Add H4 as an acceptable tag\nTrix.config.blockAttributes[\"heading4\"] = {\n tagName: \"h4\",\n terminal: true,\n breakOnReturn: true,\n group: false,\n};\n\n// Remove H1 from trix list of acceptable tags\ndelete Trix.config.blockAttributes.heading1;\n\n/**\n * Allow users to enter path and fragment URIs which the input[type=url] browser\n * input does not permit. Uses a permissive regex pattern which is not suitable\n * for untrusted use cases.\n */\nconst LINK_PATTERN = \"(https?|mailto:|tel:|/|#).*?\";\n\n/**\n * Customize default toolbar:\n *\n * * headings: h4 instead of h1\n * * links: use type=text instead of type=url\n *\n * @returns {String} toolbar html fragment\n */\nTrix.config.toolbar.getDefaultHTML = () => {\n const { lang } = Trix.config;\n return `\n<div class=\"trix-button-row\">\n <span class=\"trix-button-group trix-button-group--text-tools\" data-trix-button-group=\"text-tools\">\n <button type=\"button\" class=\"trix-button trix-button--icon trix-button--icon-bold\" data-trix-attribute=\"bold\" data-trix-key=\"b\" title=\"${lang.bold}\" tabindex=\"-1\">${lang.bold}</button>\n <button type=\"button\" class=\"trix-button trix-button--icon trix-button--icon-italic\" data-trix-attribute=\"italic\" data-trix-key=\"i\" title=\"${lang.italic}\" tabindex=\"-1\">${lang.italic}</button>\n <button type=\"button\" class=\"trix-button trix-button--icon trix-button--icon-strike\" data-trix-attribute=\"strike\" title=\"${lang.strike}\" tabindex=\"-1\">${lang.strike}</button>\n <button type=\"button\" class=\"trix-button trix-button--icon trix-button--icon-link\" data-trix-attribute=\"href\" data-trix-action=\"link\" data-trix-key=\"k\" title=\"${lang.link}\" tabindex=\"-1\">${lang.link}</button>\n </span>\n <span class=\"trix-button-group trix-button-group--block-tools\" data-trix-button-group=\"block-tools\">\n <button type=\"button\" class=\"trix-button trix-button--icon trix-button--icon-heading-1\" data-trix-attribute=\"heading4\" title=\"${lang.heading1}\" tabindex=\"-1\">${lang.heading1}</button>\n <button type=\"button\" class=\"trix-button trix-button--icon trix-button--icon-quote\" data-trix-attribute=\"quote\" title=\"${lang.quote}\" tabindex=\"-1\">${lang.quote}</button>\n <button type=\"button\" class=\"trix-button trix-button--icon trix-button--icon-code\" data-trix-attribute=\"code\" title=\"${lang.code}\" tabindex=\"-1\">${lang.code}</button>\n <button type=\"button\" class=\"trix-button trix-button--icon trix-button--icon-bullet-list\" data-trix-attribute=\"bullet\" title=\"${lang.bullets}\" tabindex=\"-1\">${lang.bullets}</button>\n <button type=\"button\" class=\"trix-button trix-button--icon trix-button--icon-number-list\" data-trix-attribute=\"number\" title=\"${lang.numbers}\" tabindex=\"-1\">${lang.numbers}</button>\n <button type=\"button\" class=\"trix-button trix-button--icon trix-button--icon-decrease-nesting-level\" data-trix-action=\"decreaseNestingLevel\" title=\"${lang.outdent}\" tabindex=\"-1\">${lang.outdent}</button>\n <button type=\"button\" class=\"trix-button trix-button--icon trix-button--icon-increase-nesting-level\" data-trix-action=\"increaseNestingLevel\" title=\"${lang.indent}\" tabindex=\"-1\">${lang.indent}</button>\n </span>\n <span class=\"trix-button-group trix-button-group--file-tools\" data-trix-button-group=\"file-tools\">\n <button type=\"button\" class=\"trix-button trix-button--icon trix-button--icon-attach\" data-trix-action=\"attachFiles\" title=\"${lang.attachFiles}\" tabindex=\"-1\">${lang.attachFiles}</button>\n </span>\n <span class=\"trix-button-group-spacer\"></span>\n <span class=\"trix-button-group trix-button-group--history-tools\" data-trix-button-group=\"history-tools\">\n <button type=\"button\" class=\"trix-button trix-button--icon trix-button--icon-undo\" data-trix-action=\"undo\" data-trix-key=\"z\" title=\"${lang.undo}\" tabindex=\"-1\">${lang.undo}</button>\n <button type=\"button\" class=\"trix-button trix-button--icon trix-button--icon-redo\" data-trix-action=\"redo\" data-trix-key=\"shift+z\" title=\"${lang.redo}\" tabindex=\"-1\">${lang.redo}</button>\n </span>\n</div>\n<div class=\"trix-dialogs\" data-trix-dialogs>\n <div class=\"trix-dialog trix-dialog--link\" data-trix-dialog=\"href\" data-trix-dialog-attribute=\"href\">\n <div class=\"trix-dialog__link-fields\">\n <input type=\"text\" name=\"href\" pattern=\"${LINK_PATTERN}\" class=\"trix-input trix-input--dialog\" placeholder=\"${lang.urlPlaceholder}\" aria-label=\"${lang.url}\" required data-trix-input>\n <div class=\"trix-button-group\">\n <input type=\"button\" class=\"trix-button trix-button--dialog\" value=\"${lang.link}\" data-trix-method=\"setAttribute\">\n <input type=\"button\" class=\"trix-button trix-button--dialog\" value=\"${lang.unlink}\" data-trix-method=\"removeAttribute\">\n </div>\n </div>\n </div>\n</div>\n`;\n};\n\n/**\n * If the <trix-editor> element is in the HTML when Trix loads, then Trix will have already injected the toolbar content\n * before our code gets a chance to run. Fix that now.\n *\n * Note: in Trix 2 this is likely to no longer be necessary.\n */\ndocument.querySelectorAll(\"trix-toolbar\").forEach((e) => {\n e.innerHTML = Trix.config.toolbar.getDefaultHTML();\n});\n","import ContainerController from \"./editor/container_controller\";\nimport ItemController from \"./editor/item_controller\";\nimport ListController from \"./editor/list_controller\";\nimport NewItemController from \"./editor/new_item_controller\";\nimport StatusBarController from \"./editor/status_bar_controller\";\nimport TrixController from \"./editor/trix_controller\";\n\nconst Definitions = [\n {\n identifier: \"content--editor--container\",\n controllerConstructor: ContainerController,\n },\n {\n identifier: \"content--editor--item\",\n controllerConstructor: ItemController,\n },\n {\n identifier: \"content--editor--list\",\n controllerConstructor: ListController,\n },\n {\n identifier: \"content--editor--new-item\",\n controllerConstructor: NewItemController,\n },\n {\n identifier: \"content--editor--status-bar\",\n controllerConstructor: StatusBarController,\n },\n {\n identifier: \"content--editor--trix\",\n controllerConstructor: TrixController,\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: \"content\" });\n }\n}\n","import { Controller } from \"@hotwired/stimulus\";\n\nexport default class NewItemController extends Controller {\n static targets = [\"template\"];\n\n dragstart(event) {\n if (this.element !== event.target) return;\n\n event.dataTransfer.setData(\"text/html\", this.templateTarget.innerHTML);\n event.dataTransfer.effectAllowed = \"copy\";\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 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","collapseChild","appendChild","collapse","listElement","document","createElement","setAttribute","createChildrenList","child","expand","Array","from","reverse","insertAdjacentElement","toggleRule","rule","deny","hasOwnProperty","removeAttribute","hasItemIdChanged","updateAfterChange","descendants","push","map","Container","items","nodes","querySelectorAll","state","inputs","e","join","reindex","reset","sort","RulesEngine","static","debug","args","console","log","normalize","firstItemDepthZero","depthMustBeSet","itemCannotHaveInvalidDepth","parentMustBeLayout","parentCannotHaveExpandedAndCollapsedChildren","update","rules","parentsCannotDeNest","rootsCannotDeNest","onlyLastItemCanDeNest","nestingNeedsParent","leavesCannotCollapse","needHiddenItemsToExpand","parentsCannotBeDeleted","parentsCannotBeDragged","isNaN","previous","next","getEventItem","event","target","closest","swap","nodeName","positionComparison","compareDocumentPosition","Node","DOCUMENT_POSITION_FOLLOWING","DOCUMENT_POSITION_PRECEDING","dropTarget","Trix","window","config","blockAttributes","tagName","terminal","breakOnReturn","group","heading1","toolbar","getDefaultHTML","lang","bold","italic","strike","link","quote","code","bullets","numbers","outdent","indent","attachFiles","undo","redo","urlPlaceholder","url","unlink","innerHTML","Definitions","identifier","controllerConstructor","Controller","connect","container","containerTarget","drop","delta","undefined","preventDefault","remove","nest","deNest","updateRequested","setTimeout","engine","notifyChange","dispatch","bubbles","prefix","detail","dirty","isDirty","li","ol","element","enterCount","dragstart","parentElement","dataTransfer","effectAllowed","requestAnimationFrame","dragging","dragover","dragItem","dragenter","copyAllowed","newItem","dragleave","cancelDrag","placeholder","template","getData","content","replaceChild","click","dragend","isDragging","setData","templateTarget","versionState","change","trixInitialize"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<%= form_with(model: container, url:, scope:, **html_attributes) do %>
|
|
2
2
|
<%# Hidden input ensures that if the container is empty then the controller receives an empty array. %>
|
|
3
3
|
<input type="hidden" name="<%= attributes_scope %>[id]">
|
|
4
|
-
<%= render
|
|
4
|
+
<%= render errors %>
|
|
5
5
|
|
|
6
6
|
<%= render Katalyst::Content::Editor::TableComponent.new(container:) do |list| %>
|
|
7
7
|
<%= container.draft_items.each { |item| list.with_item(item) } %>
|
|
@@ -1,6 +1,19 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {Controller} from "@hotwired/stimulus";
|
|
2
2
|
|
|
3
3
|
export default class ListController extends Controller {
|
|
4
|
+
connect() {
|
|
5
|
+
this.enterCount = 0;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* When the user starts a drag within the list, set the item's dataTransfer
|
|
10
|
+
* properties to indicate that it's being dragged and update its style.
|
|
11
|
+
*
|
|
12
|
+
* We delay setting the dataset property until the next animation frame
|
|
13
|
+
* so that the style updates can be computed before the drag begins.
|
|
14
|
+
*
|
|
15
|
+
* @param event {DragEvent}
|
|
16
|
+
*/
|
|
4
17
|
dragstart(event) {
|
|
5
18
|
if (this.element !== event.target.parentElement) return;
|
|
6
19
|
|
|
@@ -8,51 +21,86 @@ export default class ListController extends Controller {
|
|
|
8
21
|
event.dataTransfer.effectAllowed = "move";
|
|
9
22
|
|
|
10
23
|
// update element style after drag has begun
|
|
11
|
-
|
|
24
|
+
requestAnimationFrame(() => (target.dataset.dragging = ""));
|
|
12
25
|
}
|
|
13
26
|
|
|
27
|
+
/**
|
|
28
|
+
* When the user drags an item over another item in the last, swap the
|
|
29
|
+
* dragging item with the item under the cursor.
|
|
30
|
+
*
|
|
31
|
+
* As a special case, if the item is dragged over placeholder space at the end
|
|
32
|
+
* of the list, move the item to the bottom of the list instead. This allows
|
|
33
|
+
* users to hit the list element more easily when adding new items to an empty
|
|
34
|
+
* list.
|
|
35
|
+
*
|
|
36
|
+
* @param event {DragEvent}
|
|
37
|
+
*/
|
|
14
38
|
dragover(event) {
|
|
15
|
-
const item = this.dragItem
|
|
39
|
+
const item = this.dragItem;
|
|
16
40
|
if (!item) return;
|
|
17
41
|
|
|
18
|
-
swap(
|
|
42
|
+
swap(dropTarget(event.target), item);
|
|
19
43
|
|
|
20
44
|
event.preventDefault();
|
|
21
45
|
return true;
|
|
22
46
|
}
|
|
23
47
|
|
|
48
|
+
/**
|
|
49
|
+
* When the user drags an item into the list, create a placeholder item to
|
|
50
|
+
* represent the new item. Note that we can't access the drag data
|
|
51
|
+
* until drop, so we assume that this is our template item for now.
|
|
52
|
+
*
|
|
53
|
+
* Users can cancel the drag by dragging the item out of the list or by
|
|
54
|
+
* pressing escape. Both are handled by `cancelDrag`.
|
|
55
|
+
*
|
|
56
|
+
* @param event {DragEvent}
|
|
57
|
+
*/
|
|
24
58
|
dragenter(event) {
|
|
25
59
|
event.preventDefault();
|
|
26
60
|
|
|
27
|
-
|
|
61
|
+
// Safari doesn't support relatedTarget, so we count enter/leave pairs
|
|
62
|
+
this.enterCount++;
|
|
63
|
+
|
|
64
|
+
if (copyAllowed(event) && !this.dragItem) {
|
|
28
65
|
const item = document.createElement("li");
|
|
29
66
|
item.dataset.dragging = "";
|
|
30
67
|
item.dataset.newItem = "";
|
|
31
|
-
this.element.
|
|
68
|
+
this.element.appendChild(item);
|
|
32
69
|
}
|
|
33
70
|
}
|
|
34
71
|
|
|
72
|
+
/**
|
|
73
|
+
* When the user drags the item out of the list, remove the placeholder.
|
|
74
|
+
* This allows users to cancel the drag by dragging the item out of the list.
|
|
75
|
+
*
|
|
76
|
+
* @param event {DragEvent}
|
|
77
|
+
*/
|
|
35
78
|
dragleave(event) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
// ignore if item is not set or we're moving into a valid drop target
|
|
40
|
-
if (!item || related) return;
|
|
79
|
+
// Safari doesn't support relatedTarget, so we count enter/leave pairs
|
|
80
|
+
// https://bugs.webkit.org/show_bug.cgi?id=66547
|
|
81
|
+
this.enterCount--;
|
|
41
82
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
item.remove();
|
|
83
|
+
if (this.enterCount <= 0 && this.dragItem.dataset.hasOwnProperty("newItem")) {
|
|
84
|
+
this.cancelDrag(event);
|
|
45
85
|
}
|
|
46
86
|
}
|
|
47
87
|
|
|
88
|
+
/**
|
|
89
|
+
* When the user drops an item into the list, end the drag and reindex the list.
|
|
90
|
+
*
|
|
91
|
+
* If the item is a new item, we replace the placeholder with the template
|
|
92
|
+
* item data from the dataTransfer API.
|
|
93
|
+
*
|
|
94
|
+
* @param event {DragEvent}
|
|
95
|
+
*/
|
|
48
96
|
drop(event) {
|
|
49
|
-
let item = this.dragItem
|
|
97
|
+
let item = this.dragItem;
|
|
50
98
|
|
|
51
99
|
if (!item) return;
|
|
52
100
|
|
|
53
101
|
event.preventDefault();
|
|
54
102
|
delete item.dataset.dragging;
|
|
55
|
-
swap(
|
|
103
|
+
swap(dropTarget(event.target), item);
|
|
56
104
|
|
|
57
105
|
if (item.dataset.hasOwnProperty("newItem")) {
|
|
58
106
|
const placeholder = item;
|
|
@@ -61,42 +109,53 @@ export default class ListController extends Controller {
|
|
|
61
109
|
item = template.content.querySelector("li");
|
|
62
110
|
|
|
63
111
|
this.element.replaceChild(item, placeholder);
|
|
64
|
-
|
|
112
|
+
requestAnimationFrame(() =>
|
|
65
113
|
item.querySelector("[role='button'][value='edit']").click()
|
|
66
114
|
);
|
|
67
115
|
}
|
|
68
116
|
|
|
69
|
-
this.dispatch("drop", {
|
|
117
|
+
this.dispatch("drop", {target: item, bubbles: true, prefix: "content"});
|
|
70
118
|
}
|
|
71
119
|
|
|
120
|
+
/**
|
|
121
|
+
* End an in-progress drag. If the item is a new item, remove it, otherwise
|
|
122
|
+
* reset the item's style and restore its original position in the list.
|
|
123
|
+
*/
|
|
72
124
|
dragend() {
|
|
73
|
-
const item = this.dragItem
|
|
74
|
-
if (!item) return;
|
|
125
|
+
const item = this.dragItem;
|
|
75
126
|
|
|
76
|
-
|
|
77
|
-
|
|
127
|
+
if (!item) {
|
|
128
|
+
} else if (item.dataset.hasOwnProperty("newItem")) {
|
|
129
|
+
item.remove();
|
|
130
|
+
} else {
|
|
131
|
+
delete item.dataset.dragging;
|
|
132
|
+
this.reset();
|
|
133
|
+
}
|
|
78
134
|
}
|
|
79
135
|
|
|
80
|
-
|
|
81
|
-
return this.
|
|
136
|
+
get isDragging() {
|
|
137
|
+
return !!this.dragItem;
|
|
82
138
|
}
|
|
83
139
|
|
|
84
|
-
|
|
85
|
-
return (
|
|
86
|
-
e.closest("[data-controller='content--editor--list'] > *") ||
|
|
87
|
-
e.closest("[data-controller='content--editor--list']")
|
|
88
|
-
);
|
|
140
|
+
get dragItem() {
|
|
141
|
+
return this.element.querySelector("[data-dragging]");
|
|
89
142
|
}
|
|
90
143
|
|
|
91
144
|
reindex() {
|
|
92
|
-
this.dispatch("reindex", {
|
|
145
|
+
this.dispatch("reindex", {bubbles: true, prefix: "content"});
|
|
93
146
|
}
|
|
94
147
|
|
|
95
148
|
reset() {
|
|
96
|
-
this.dispatch("reset", {
|
|
149
|
+
this.dispatch("reset", {bubbles: true, prefix: "content"});
|
|
97
150
|
}
|
|
98
151
|
}
|
|
99
152
|
|
|
153
|
+
/**
|
|
154
|
+
* Swaps two list items. If target is a list, the item is appended.
|
|
155
|
+
*
|
|
156
|
+
* @param target the target element to swap with
|
|
157
|
+
* @param item the item the user is dragging
|
|
158
|
+
*/
|
|
100
159
|
function swap(target, item) {
|
|
101
160
|
if (!target) return;
|
|
102
161
|
if (target === item) return;
|
|
@@ -114,3 +173,25 @@ function swap(target, item) {
|
|
|
114
173
|
target.appendChild(item);
|
|
115
174
|
}
|
|
116
175
|
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Returns true if the event supports copy or copy move.
|
|
179
|
+
*
|
|
180
|
+
* Chrome and Firefox use copy, but Safari only supports copyMove.
|
|
181
|
+
*/
|
|
182
|
+
function copyAllowed(event) {
|
|
183
|
+
return (
|
|
184
|
+
event.dataTransfer.effectAllowed === "copy" ||
|
|
185
|
+
event.dataTransfer.effectAllowed === "copyMove"
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Given an event target, return the closest drop target, if any.
|
|
191
|
+
*/
|
|
192
|
+
function dropTarget(e) {
|
|
193
|
+
return e && (
|
|
194
|
+
e.closest("[data-controller='content--editor--list'] > *") ||
|
|
195
|
+
e.closest("[data-controller='content--editor--list']")
|
|
196
|
+
);
|
|
197
|
+
}
|
|
@@ -28,7 +28,7 @@ module Katalyst
|
|
|
28
28
|
initializer "katalyst-content.importmap", before: "importmap" do |app|
|
|
29
29
|
if app.config.respond_to?(:importmap)
|
|
30
30
|
app.config.importmap.paths << root.join("config/importmap.rb")
|
|
31
|
-
app.config.importmap.cache_sweepers << root.join("app/assets/
|
|
31
|
+
app.config.importmap.cache_sweepers << root.join("app/assets/builds")
|
|
32
32
|
end
|
|
33
33
|
end
|
|
34
34
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: katalyst-content
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.1.
|
|
4
|
+
version: 2.1.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Katalyst Interactive
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2023-12-
|
|
11
|
+
date: 2023-12-19 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: active_storage_validations
|