Better error/succes handling in JS according to what the backend is returning
- Added "js" to the list of included file extensions in .air.toml. - Added new JavaScript files to the HTML templates. - Improved error handling in custom.js for htmx requests.
- commit
- 877125334a83c80a3f56c7473d7f3e40367d9f42
- author
- bnema · · 2023-09-22 08:00 UTC
- parent
- 6ccaf5c
- signature
- unsigned
7 files · +221 -121
M .air.toml +1 -1
| @@ -14,7 +14,7 @@ tmp_dir = "tmp" | |||
| 14 | 14 | follow_symlink = false | |
| 15 | 15 | full_bin = "./tmp/main" | |
| 16 | 16 | include_dir = [] | |
| 17 | - | include_ext = ["go", "tpl", "tmpl", "html", "gohtml", "css"] | |
| 17 | + | include_ext = ["go", "tpl", "tmpl", "html", "gohtml", "css", "js"] | |
| 18 | 18 | include_file = [] | |
| 19 | 19 | kill_delay = "0s" | |
| 20 | 20 | log = "build-errors.log" | |
| @@ -14,7 +14,7 @@ tmp_dir = "tmp" | |||||
| 14 | follow_symlink = false | 14 | follow_symlink = false | ||
| 15 | full_bin = "./tmp/main" | 15 | full_bin = "./tmp/main" | ||
| 16 | include_dir = [] | 16 | include_dir = [] | ||
| 17 | - | include_ext = ["go", "tpl", "tmpl", "html", "gohtml", "css"] | 17 | + | include_ext = ["go", "tpl", "tmpl", "html", "gohtml", "css", "js"] |
| 18 | include_file = [] | 18 | include_file = [] | ||
| 19 | kill_delay = "0s" | 19 | kill_delay = "0s" | ||
| 20 | log = "build-errors.log" | 20 | log = "build-errors.log" | ||
M internal/gotemplate/models/html/fragments/header.gohtml +4 -3
| @@ -4,8 +4,9 @@ | |||
| 4 | 4 | <title>{{ .header.meta.title }}</title> | |
| 5 | 5 | <link rel="stylesheet" href="/assets/css/tailwind.css" /> | |
| 6 | 6 | <link rel="stylesheet" href="/libs/iconoir.css" /> | |
| 7 | - | <script src="/libs/alpine.min.js" defer></script> | |
| 8 | 7 | <script src="/libs/htmx.min.js"></script> | |
| 8 | + | <script src="/libs/htmx-response-targets.js"></script> | |
| 9 | + | <script src="/libs/htmx-debug.js"></script> | |
| 9 | 10 | <script src="/libs/htmx-sse.js"></script> | |
| 10 | - | <script src="/assets/js/custom.js" defer></script> | |
| 11 | - | </head>⏎̸ | |
| 11 | + | <script defer src="/assets/js/custom.js"></script> | |
| 12 | + | </head> | |
| @@ -4,8 +4,9 @@ | |||||
| 4 | <title>{{ .header.meta.title }}</title> | 4 | <title>{{ .header.meta.title }}</title> | ||
| 5 | <link rel="stylesheet" href="/assets/css/tailwind.css" /> | 5 | <link rel="stylesheet" href="/assets/css/tailwind.css" /> | ||
| 6 | <link rel="stylesheet" href="/libs/iconoir.css" /> | 6 | <link rel="stylesheet" href="/libs/iconoir.css" /> | ||
| 7 | - | <script src="/libs/alpine.min.js" defer></script> | |||
| 8 | <script src="/libs/htmx.min.js"></script> | 7 | <script src="/libs/htmx.min.js"></script> | ||
| 8 | + | <script src="/libs/htmx-response-targets.js"></script> | |||
| 9 | + | <script src="/libs/htmx-debug.js"></script> | |||
| 9 | <script src="/libs/htmx-sse.js"></script> | 10 | <script src="/libs/htmx-sse.js"></script> | ||
| 10 | - | <script src="/assets/js/custom.js" defer></script> | 11 | + | <script defer src="/assets/js/custom.js"></script> |
| 11 | - | </head>⏎̸ | 12 | + | </head> |
M internal/gotemplate/models/html/fragments/imagelist.gohtml +17 -41
| @@ -1,4 +1,4 @@ | |||
| 1 | - | <div class="flex flex-col p-4"> | |
| 1 | + | <div class="flex flex-col p-4" hx-ext="response-targets"> | |
| 2 | 2 | <h1 class="text-lg md:text-xl font-bold">Images</h1> | |
| 3 | 3 | <div class="flex font-bold border-t-2 align-baseline py-2"> | |
| 4 | 4 | <div class="w-2/4">Name</div> | |
| @@ -7,49 +7,25 @@ | |||
| 7 | 7 | <div class="w-1/4">Action</div> | |
| 8 | 8 | </div> | |
| 9 | 9 | {{ range.Images }} | |
| 10 | - | <div | |
| 11 | - | x-data="{ success: false, loading: false, showError: false, errorMessage: '' }" | |
| 12 | - | > | |
| 13 | - | <div | |
| 14 | - | id="img-{{.ShortImgID}}" | |
| 15 | - | class="flex py-2 align-baseline justify-center" | |
| 16 | - | hx-boost="true" | |
| 17 | - | > | |
| 18 | - | <div class="w-2/4"> | |
| 19 | - | <span title="{{.ImageSummary.ID}}">{{.Name}}</span> | |
| 20 | - | </div> | |
| 21 | - | <div class="w-2/4">{{.CreatedStr}}</div> | |
| 22 | - | <div class="w-1/6">{{.SizeStr}}</div> | |
| 23 | - | ||
| 24 | - | <button | |
| 25 | - | id="remove-button-img-{{.ShortImgID}}" | |
| 26 | - | x-bind:class="{ 'bg-green-500': success, 'bg-red-500': !success, 'text-white': true, 'px-2': true, 'py-1': true, 'rounded': true }" | |
| 27 | - | @click="loading = true" | |
| 28 | - | hx-trigger="click" | |
| 29 | - | hx-delete="/htmx/image-manager/delete/{{.ShortImgID}}" | |
| 30 | - | hx-swap="outerHTML swap:outerHTML" | |
| 31 | - | hx-target="#img-{{.ShortImgID}}" | |
| 32 | - | hx-confirm="Are you sure you want to delete this image?" | |
| 33 | - | hx-select="button" | |
| 34 | - | hx-boost="true" | |
| 35 | - | x-on:htmx:after-swap="success = true; loading = false; error = false" | |
| 36 | - | x-on:htmx:responseError="showError = true; errorMessage = (event.detail && event.detail.xhr && event.detail.xhr.responseText) ? event.detail.xhr.responseText : 'An unknown error occurred.'" | |
| 37 | - | > | |
| 38 | - | <span x-show="!loading && !success">Remove</span> | |
| 39 | - | <span x-show="loading">Loading...</span> | |
| 40 | - | <span x-show="success">Success</span> | |
| 41 | - | </button> | |
| 10 | + | <div id="img-{{.ShortImgID}}" class="flex py-2 align-baseline justify-center"> | |
| 11 | + | <div class="w-2/4"> | |
| 12 | + | <span title="{{.ImageSummary.ID}}">{{.Name}}</span> | |
| 42 | 13 | </div> | |
| 43 | - | ||
| 44 | - | <div | |
| 45 | - | x-show="showError" | |
| 46 | - | data-error-type="image" | |
| 47 | - | class="error-display-div bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative" | |
| 14 | + | <div class="w-2/4">{{.CreatedStr}}</div> | |
| 15 | + | <div class="w-1/6">{{.SizeStr}}</div> | |
| 16 | + | <button | |
| 17 | + | id="remove-button-img-{{.ShortImgID}}" | |
| 18 | + | hx-delete="/htmx/image-manager/delete/{{.ShortImgID}}" | |
| 19 | + | hx-target="#img-{{.ShortImgID}}" | |
| 20 | + | hx-target-*="#error-message-img" | |
| 21 | + | hx-swap="innerHTML" | |
| 22 | + | hx-confirm="Are you sure you want to delete this container image?" | |
| 23 | + | class="w-1/4 bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded-md" | |
| 48 | 24 | > | |
| 49 | - | <p x-text="errorMessage"></p> | |
| 50 | - | </div> | |
| 25 | + | Delete | |
| 26 | + | </button> | |
| 51 | 27 | </div> | |
| 52 | - | ||
| 28 | + | <div id="error-message-img" class="mt-2 text-red-500" display="none"></div> | |
| 53 | 29 | {{ else }} | |
| 54 | 30 | <div class="mt-2">No images found.</div> | |
| 55 | 31 | {{ end }} | |
| @@ -1,4 +1,4 @@ | |||||
| 1 | - | <div class="flex flex-col p-4"> | 1 | + | <div class="flex flex-col p-4" hx-ext="response-targets"> |
| 2 | <h1 class="text-lg md:text-xl font-bold">Images</h1> | 2 | <h1 class="text-lg md:text-xl font-bold">Images</h1> | ||
| 3 | <div class="flex font-bold border-t-2 align-baseline py-2"> | 3 | <div class="flex font-bold border-t-2 align-baseline py-2"> | ||
| 4 | <div class="w-2/4">Name</div> | 4 | <div class="w-2/4">Name</div> | ||
| @@ -7,49 +7,25 @@ | |||||
| 7 | <div class="w-1/4">Action</div> | 7 | <div class="w-1/4">Action</div> | ||
| 8 | </div> | 8 | </div> | ||
| 9 | {{ range.Images }} | 9 | {{ range.Images }} | ||
| 10 | - | <div | 10 | + | <div id="img-{{.ShortImgID}}" class="flex py-2 align-baseline justify-center"> |
| 11 | - | x-data="{ success: false, loading: false, showError: false, errorMessage: '' }" | 11 | + | <div class="w-2/4"> |
| 12 | - | > | 12 | + | <span title="{{.ImageSummary.ID}}">{{.Name}}</span> |
| 13 | - | <div | |||
| 14 | - | id="img-{{.ShortImgID}}" | |||
| 15 | - | class="flex py-2 align-baseline justify-center" | |||
| 16 | - | hx-boost="true" | |||
| 17 | - | > | |||
| 18 | - | <div class="w-2/4"> | |||
| 19 | - | <span title="{{.ImageSummary.ID}}">{{.Name}}</span> | |||
| 20 | - | </div> | |||
| 21 | - | <div class="w-2/4">{{.CreatedStr}}</div> | |||
| 22 | - | <div class="w-1/6">{{.SizeStr}}</div> | |||
| 23 | - | ||||
| 24 | - | <button | |||
| 25 | - | id="remove-button-img-{{.ShortImgID}}" | |||
| 26 | - | x-bind:class="{ 'bg-green-500': success, 'bg-red-500': !success, 'text-white': true, 'px-2': true, 'py-1': true, 'rounded': true }" | |||
| 27 | - | @click="loading = true" | |||
| 28 | - | hx-trigger="click" | |||
| 29 | - | hx-delete="/htmx/image-manager/delete/{{.ShortImgID}}" | |||
| 30 | - | hx-swap="outerHTML swap:outerHTML" | |||
| 31 | - | hx-target="#img-{{.ShortImgID}}" | |||
| 32 | - | hx-confirm="Are you sure you want to delete this image?" | |||
| 33 | - | hx-select="button" | |||
| 34 | - | hx-boost="true" | |||
| 35 | - | x-on:htmx:after-swap="success = true; loading = false; error = false" | |||
| 36 | - | x-on:htmx:responseError="showError = true; errorMessage = (event.detail && event.detail.xhr && event.detail.xhr.responseText) ? event.detail.xhr.responseText : 'An unknown error occurred.'" | |||
| 37 | - | > | |||
| 38 | - | <span x-show="!loading && !success">Remove</span> | |||
| 39 | - | <span x-show="loading">Loading...</span> | |||
| 40 | - | <span x-show="success">Success</span> | |||
| 41 | - | </button> | |||
| 42 | </div> | 13 | </div> | ||
| 43 | - | 14 | + | <div class="w-2/4">{{.CreatedStr}}</div> | |
| 44 | - | <div | 15 | + | <div class="w-1/6">{{.SizeStr}}</div> |
| 45 | - | x-show="showError" | 16 | + | <button |
| 46 | - | data-error-type="image" | 17 | + | id="remove-button-img-{{.ShortImgID}}" |
| 47 | - | class="error-display-div bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative" | 18 | + | hx-delete="/htmx/image-manager/delete/{{.ShortImgID}}" |
| 19 | + | hx-target="#img-{{.ShortImgID}}" | |||
| 20 | + | hx-target-*="#error-message-img" | |||
| 21 | + | hx-swap="innerHTML" | |||
| 22 | + | hx-confirm="Are you sure you want to delete this container image?" | |||
| 23 | + | class="w-1/4 bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded-md" | |||
| 48 | > | 24 | > | ||
| 49 | - | <p x-text="errorMessage"></p> | 25 | + | Delete |
| 50 | - | </div> | 26 | + | </button> |
| 51 | </div> | 27 | </div> | ||
| 52 | - | 28 | + | <div id="error-message-img" class="mt-2 text-red-500" display="none"></div> | |
| 53 | {{ else }} | 29 | {{ else }} | ||
| 54 | <div class="mt-2">No images found.</div> | 30 | <div class="mt-2">No images found.</div> | ||
| 55 | {{ end }} | 31 | {{ end }} | ||
M internal/webui/public/assets/css/tailwind.css +39 -63
| @@ -541,8 +541,38 @@ video { | |||
| 541 | 541 | --tw-backdrop-sepia: ; | |
| 542 | 542 | } | |
| 543 | 543 | ||
| 544 | - | .relative { | |
| 545 | - | position: relative; | |
| 544 | + | .container { | |
| 545 | + | width: 100%; | |
| 546 | + | } | |
| 547 | + | ||
| 548 | + | @media (min-width: 640px) { | |
| 549 | + | .container { | |
| 550 | + | max-width: 640px; | |
| 551 | + | } | |
| 552 | + | } | |
| 553 | + | ||
| 554 | + | @media (min-width: 768px) { | |
| 555 | + | .container { | |
| 556 | + | max-width: 768px; | |
| 557 | + | } | |
| 558 | + | } | |
| 559 | + | ||
| 560 | + | @media (min-width: 1024px) { | |
| 561 | + | .container { | |
| 562 | + | max-width: 1024px; | |
| 563 | + | } | |
| 564 | + | } | |
| 565 | + | ||
| 566 | + | @media (min-width: 1280px) { | |
| 567 | + | .container { | |
| 568 | + | max-width: 1280px; | |
| 569 | + | } | |
| 570 | + | } | |
| 571 | + | ||
| 572 | + | @media (min-width: 1536px) { | |
| 573 | + | .container { | |
| 574 | + | max-width: 1536px; | |
| 575 | + | } | |
| 546 | 576 | } | |
| 547 | 577 | ||
| 548 | 578 | .my-2 { | |
| @@ -643,22 +673,6 @@ video { | |||
| 643 | 673 | flex-grow: 1; | |
| 644 | 674 | } | |
| 645 | 675 | ||
| 646 | - | .scale-100 { | |
| 647 | - | --tw-scale-x: 1; | |
| 648 | - | --tw-scale-y: 1; | |
| 649 | - | transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); | |
| 650 | - | } | |
| 651 | - | ||
| 652 | - | .scale-90 { | |
| 653 | - | --tw-scale-x: .9; | |
| 654 | - | --tw-scale-y: .9; | |
| 655 | - | transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); | |
| 656 | - | } | |
| 657 | - | ||
| 658 | - | .transform { | |
| 659 | - | transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); | |
| 660 | - | } | |
| 661 | - | ||
| 662 | 676 | .flex-row { | |
| 663 | 677 | flex-direction: row; | |
| 664 | 678 | } | |
| @@ -732,11 +746,6 @@ video { | |||
| 732 | 746 | border-color: rgb(209 213 219 / var(--tw-border-opacity)); | |
| 733 | 747 | } | |
| 734 | 748 | ||
| 735 | - | .border-red-400 { | |
| 736 | - | --tw-border-opacity: 1; | |
| 737 | - | border-color: rgb(248 113 113 / var(--tw-border-opacity)); | |
| 738 | - | } | |
| 739 | - | ||
| 740 | 749 | .bg-gordonmascot-hblue { | |
| 741 | 750 | --tw-bg-opacity: 1; | |
| 742 | 751 | background-color: rgb(1 30 92 / var(--tw-bg-opacity)); | |
| @@ -757,11 +766,6 @@ video { | |||
| 757 | 766 | background-color: rgb(34 197 94 / var(--tw-bg-opacity)); | |
| 758 | 767 | } | |
| 759 | 768 | ||
| 760 | - | .bg-red-100 { | |
| 761 | - | --tw-bg-opacity: 1; | |
| 762 | - | background-color: rgb(254 226 226 / var(--tw-bg-opacity)); | |
| 763 | - | } | |
| 764 | - | ||
| 765 | 769 | .bg-red-500 { | |
| 766 | 770 | --tw-bg-opacity: 1; | |
| 767 | 771 | background-color: rgb(239 68 68 / var(--tw-bg-opacity)); | |
| @@ -813,11 +817,6 @@ video { | |||
| 813 | 817 | padding-bottom: 0.5rem; | |
| 814 | 818 | } | |
| 815 | 819 | ||
| 816 | - | .py-3 { | |
| 817 | - | padding-top: 0.75rem; | |
| 818 | - | padding-bottom: 0.75rem; | |
| 819 | - | } | |
| 820 | - | ||
| 821 | 820 | .align-baseline { | |
| 822 | 821 | vertical-align: baseline; | |
| 823 | 822 | } | |
| @@ -879,9 +878,9 @@ video { | |||
| 879 | 878 | color: rgb(31 41 55 / var(--tw-text-opacity)); | |
| 880 | 879 | } | |
| 881 | 880 | ||
| 882 | - | .text-red-700 { | |
| 881 | + | .text-red-500 { | |
| 883 | 882 | --tw-text-opacity: 1; | |
| 884 | - | color: rgb(185 28 28 / var(--tw-text-opacity)); | |
| 883 | + | color: rgb(239 68 68 / var(--tw-text-opacity)); | |
| 885 | 884 | } | |
| 886 | 885 | ||
| 887 | 886 | .text-red-800 { | |
| @@ -903,14 +902,6 @@ video { | |||
| 903 | 902 | text-decoration-line: underline; | |
| 904 | 903 | } | |
| 905 | 904 | ||
| 906 | - | .opacity-0 { | |
| 907 | - | opacity: 0; | |
| 908 | - | } | |
| 909 | - | ||
| 910 | - | .opacity-100 { | |
| 911 | - | opacity: 1; | |
| 912 | - | } | |
| 913 | - | ||
| 914 | 905 | .shadow { | |
| 915 | 906 | --tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1); | |
| 916 | 907 | --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color); | |
| @@ -922,26 +913,6 @@ video { | |||
| 922 | 913 | outline-offset: 2px; | |
| 923 | 914 | } | |
| 924 | 915 | ||
| 925 | - | .transition { | |
| 926 | - | transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter; | |
| 927 | - | transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; | |
| 928 | - | transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter; | |
| 929 | - | transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); | |
| 930 | - | transition-duration: 150ms; | |
| 931 | - | } | |
| 932 | - | ||
| 933 | - | .duration-300 { | |
| 934 | - | transition-duration: 300ms; | |
| 935 | - | } | |
| 936 | - | ||
| 937 | - | .ease-in { | |
| 938 | - | transition-timing-function: cubic-bezier(0.4, 0, 1, 1); | |
| 939 | - | } | |
| 940 | - | ||
| 941 | - | .ease-out { | |
| 942 | - | transition-timing-function: cubic-bezier(0, 0, 0.2, 1); | |
| 943 | - | } | |
| 944 | - | ||
| 945 | 916 | /* Import IBM Plex Fonts */ | |
| 946 | 917 | ||
| 947 | 918 | @font-face { | |
| @@ -1062,6 +1033,11 @@ video { | |||
| 1062 | 1033 | margin-right: 8px; | |
| 1063 | 1034 | } | |
| 1064 | 1035 | ||
| 1036 | + | .hover\:bg-red-700:hover { | |
| 1037 | + | --tw-bg-opacity: 1; | |
| 1038 | + | background-color: rgb(185 28 28 / var(--tw-bg-opacity)); | |
| 1039 | + | } | |
| 1040 | + | ||
| 1065 | 1041 | @media (min-width: 640px) { | |
| 1066 | 1042 | .sm\:h-24 { | |
| 1067 | 1043 | height: 6rem; | |
| @@ -541,8 +541,38 @@ video { | |||||
| 541 | --tw-backdrop-sepia: ; | 541 | --tw-backdrop-sepia: ; | ||
| 542 | } | 542 | } | ||
| 543 | 543 | ||||
| 544 | - | .relative { | 544 | + | .container { |
| 545 | - | position: relative; | 545 | + | width: 100%; |
| 546 | + | } | |||
| 547 | + | ||||
| 548 | + | @media (min-width: 640px) { | |||
| 549 | + | .container { | |||
| 550 | + | max-width: 640px; | |||
| 551 | + | } | |||
| 552 | + | } | |||
| 553 | + | ||||
| 554 | + | @media (min-width: 768px) { | |||
| 555 | + | .container { | |||
| 556 | + | max-width: 768px; | |||
| 557 | + | } | |||
| 558 | + | } | |||
| 559 | + | ||||
| 560 | + | @media (min-width: 1024px) { | |||
| 561 | + | .container { | |||
| 562 | + | max-width: 1024px; | |||
| 563 | + | } | |||
| 564 | + | } | |||
| 565 | + | ||||
| 566 | + | @media (min-width: 1280px) { | |||
| 567 | + | .container { | |||
| 568 | + | max-width: 1280px; | |||
| 569 | + | } | |||
| 570 | + | } | |||
| 571 | + | ||||
| 572 | + | @media (min-width: 1536px) { | |||
| 573 | + | .container { | |||
| 574 | + | max-width: 1536px; | |||
| 575 | + | } | |||
| 546 | } | 576 | } | ||
| 547 | 577 | ||||
| 548 | .my-2 { | 578 | .my-2 { | ||
| @@ -643,22 +673,6 @@ video { | |||||
| 643 | flex-grow: 1; | 673 | flex-grow: 1; | ||
| 644 | } | 674 | } | ||
| 645 | 675 | ||||
| 646 | - | .scale-100 { | |||
| 647 | - | --tw-scale-x: 1; | |||
| 648 | - | --tw-scale-y: 1; | |||
| 649 | - | transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); | |||
| 650 | - | } | |||
| 651 | - | ||||
| 652 | - | .scale-90 { | |||
| 653 | - | --tw-scale-x: .9; | |||
| 654 | - | --tw-scale-y: .9; | |||
| 655 | - | transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); | |||
| 656 | - | } | |||
| 657 | - | ||||
| 658 | - | .transform { | |||
| 659 | - | transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); | |||
| 660 | - | } | |||
| 661 | - | ||||
| 662 | .flex-row { | 676 | .flex-row { | ||
| 663 | flex-direction: row; | 677 | flex-direction: row; | ||
| 664 | } | 678 | } | ||
| @@ -732,11 +746,6 @@ video { | |||||
| 732 | border-color: rgb(209 213 219 / var(--tw-border-opacity)); | 746 | border-color: rgb(209 213 219 / var(--tw-border-opacity)); | ||
| 733 | } | 747 | } | ||
| 734 | 748 | ||||
| 735 | - | .border-red-400 { | |||
| 736 | - | --tw-border-opacity: 1; | |||
| 737 | - | border-color: rgb(248 113 113 / var(--tw-border-opacity)); | |||
| 738 | - | } | |||
| 739 | - | ||||
| 740 | .bg-gordonmascot-hblue { | 749 | .bg-gordonmascot-hblue { | ||
| 741 | --tw-bg-opacity: 1; | 750 | --tw-bg-opacity: 1; | ||
| 742 | background-color: rgb(1 30 92 / var(--tw-bg-opacity)); | 751 | background-color: rgb(1 30 92 / var(--tw-bg-opacity)); | ||
| @@ -757,11 +766,6 @@ video { | |||||
| 757 | background-color: rgb(34 197 94 / var(--tw-bg-opacity)); | 766 | background-color: rgb(34 197 94 / var(--tw-bg-opacity)); | ||
| 758 | } | 767 | } | ||
| 759 | 768 | ||||
| 760 | - | .bg-red-100 { | |||
| 761 | - | --tw-bg-opacity: 1; | |||
| 762 | - | background-color: rgb(254 226 226 / var(--tw-bg-opacity)); | |||
| 763 | - | } | |||
| 764 | - | ||||
| 765 | .bg-red-500 { | 769 | .bg-red-500 { | ||
| 766 | --tw-bg-opacity: 1; | 770 | --tw-bg-opacity: 1; | ||
| 767 | background-color: rgb(239 68 68 / var(--tw-bg-opacity)); | 771 | background-color: rgb(239 68 68 / var(--tw-bg-opacity)); | ||
| @@ -813,11 +817,6 @@ video { | |||||
| 813 | padding-bottom: 0.5rem; | 817 | padding-bottom: 0.5rem; | ||
| 814 | } | 818 | } | ||
| 815 | 819 | ||||
| 816 | - | .py-3 { | |||
| 817 | - | padding-top: 0.75rem; | |||
| 818 | - | padding-bottom: 0.75rem; | |||
| 819 | - | } | |||
| 820 | - | ||||
| 821 | .align-baseline { | 820 | .align-baseline { | ||
| 822 | vertical-align: baseline; | 821 | vertical-align: baseline; | ||
| 823 | } | 822 | } | ||
| @@ -879,9 +878,9 @@ video { | |||||
| 879 | color: rgb(31 41 55 / var(--tw-text-opacity)); | 878 | color: rgb(31 41 55 / var(--tw-text-opacity)); | ||
| 880 | } | 879 | } | ||
| 881 | 880 | ||||
| 882 | - | .text-red-700 { | 881 | + | .text-red-500 { |
| 883 | --tw-text-opacity: 1; | 882 | --tw-text-opacity: 1; | ||
| 884 | - | color: rgb(185 28 28 / var(--tw-text-opacity)); | 883 | + | color: rgb(239 68 68 / var(--tw-text-opacity)); |
| 885 | } | 884 | } | ||
| 886 | 885 | ||||
| 887 | .text-red-800 { | 886 | .text-red-800 { | ||
| @@ -903,14 +902,6 @@ video { | |||||
| 903 | text-decoration-line: underline; | 902 | text-decoration-line: underline; | ||
| 904 | } | 903 | } | ||
| 905 | 904 | ||||
| 906 | - | .opacity-0 { | |||
| 907 | - | opacity: 0; | |||
| 908 | - | } | |||
| 909 | - | ||||
| 910 | - | .opacity-100 { | |||
| 911 | - | opacity: 1; | |||
| 912 | - | } | |||
| 913 | - | ||||
| 914 | .shadow { | 905 | .shadow { | ||
| 915 | --tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1); | 906 | --tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1); | ||
| 916 | --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color); | 907 | --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color); | ||
| @@ -922,26 +913,6 @@ video { | |||||
| 922 | outline-offset: 2px; | 913 | outline-offset: 2px; | ||
| 923 | } | 914 | } | ||
| 924 | 915 | ||||
| 925 | - | .transition { | |||
| 926 | - | transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter; | |||
| 927 | - | transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; | |||
| 928 | - | transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter; | |||
| 929 | - | transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); | |||
| 930 | - | transition-duration: 150ms; | |||
| 931 | - | } | |||
| 932 | - | ||||
| 933 | - | .duration-300 { | |||
| 934 | - | transition-duration: 300ms; | |||
| 935 | - | } | |||
| 936 | - | ||||
| 937 | - | .ease-in { | |||
| 938 | - | transition-timing-function: cubic-bezier(0.4, 0, 1, 1); | |||
| 939 | - | } | |||
| 940 | - | ||||
| 941 | - | .ease-out { | |||
| 942 | - | transition-timing-function: cubic-bezier(0, 0, 0.2, 1); | |||
| 943 | - | } | |||
| 944 | - | ||||
| 945 | /* Import IBM Plex Fonts */ | 916 | /* Import IBM Plex Fonts */ | ||
| 946 | 917 | ||||
| 947 | @font-face { | 918 | @font-face { | ||
| @@ -1062,6 +1033,11 @@ video { | |||||
| 1062 | margin-right: 8px; | 1033 | margin-right: 8px; | ||
| 1063 | } | 1034 | } | ||
| 1064 | 1035 | ||||
| 1036 | + | .hover\:bg-red-700:hover { | |||
| 1037 | + | --tw-bg-opacity: 1; | |||
| 1038 | + | background-color: rgb(185 28 28 / var(--tw-bg-opacity)); | |||
| 1039 | + | } | |||
| 1040 | + | ||||
| 1065 | @media (min-width: 640px) { | 1041 | @media (min-width: 640px) { | ||
| 1066 | .sm\:h-24 { | 1042 | .sm\:h-24 { | ||
| 1067 | height: 6rem; | 1043 | height: 6rem; | ||
M internal/webui/public/assets/js/custom.js +27 -13
| @@ -1,15 +1,29 @@ | |||
| 1 | - | document.addEventListener('DOMContentLoaded', function() { | |
| 2 | - | document.body.addEventListener('htmx:responseError', function(event) { | |
| 3 | - | const errorDetail = event.detail; | |
| 4 | - | const errorType = errorDetail && errorDetail.xhr ? errorDetail.xhr.getResponseHeader("X-Error-Type") : 'generic'; // Fallback to 'generic' if not set | |
| 5 | - | const errorDiv = document.querySelector(`div[data-error-type="${errorType}"]`); | |
| 6 | - | if (errorDiv) { | |
| 7 | - | if (errorDetail && errorDetail.xhr && errorDetail.xhr.responseText) { | |
| 8 | - | errorDiv.innerHTML = errorDetail.xhr.responseText; | |
| 9 | - | } else { | |
| 10 | - | errorDiv.textContent = 'An unknown error occurred.'; | |
| 11 | - | } | |
| 12 | - | errorDiv.style.display = 'block'; // Make it visible | |
| 1 | + | // htmx.logAll(); | |
| 2 | + | document.addEventListener('htmx:afterRequest', function(event) { | |
| 3 | + | ||
| 4 | + | // Find the error message element | |
| 5 | + | var errorMessageElement = document.getElementById('error-message-img'); | |
| 6 | + | ||
| 7 | + | // Find the image div element by ID (you need to get this ID dynamically) | |
| 8 | + | var imageDivID = 'img-' + event.detail.elt.id.replace('img-', ''); | |
| 9 | + | var imageDivElement = document.getElementById(imageDivID); | |
| 10 | + | console.log(imageDivElement); | |
| 11 | + | ||
| 12 | + | // If it's an error, we display and hide the error message | |
| 13 | + | if (event.detail.xhr.status !== 200) { | |
| 14 | + | if (errorMessageElement && errorMessageElement.textContent.trim() !== "") { | |
| 15 | + | // Set a timeout to hide it | |
| 16 | + | setTimeout(function() { | |
| 17 | + | errorMessageElement.style.display = 'none'; // or set it to empty | |
| 18 | + | errorMessageElement.textContent = ''; // clear the text content | |
| 19 | + | }, 7000); // 3000 milliseconds = 3 seconds | |
| 13 | 20 | } | |
| 14 | - | }); | |
| 21 | + | } else { | |
| 22 | + | // If it's a success, we hide the image div | |
| 23 | + | if (imageDivElement) { | |
| 24 | + | setTimeout(function() { | |
| 25 | + | imageDivElement.style.display = 'none'; // Hide the div | |
| 26 | + | }, 3000); // 3000 milliseconds = 3 seconds | |
| 27 | + | } | |
| 28 | + | } | |
| 15 | 29 | }); | |
| @@ -1,15 +1,29 @@ | |||||
| 1 | - | document.addEventListener('DOMContentLoaded', function() { | 1 | + | // htmx.logAll(); |
| 2 | - | document.body.addEventListener('htmx:responseError', function(event) { | 2 | + | document.addEventListener('htmx:afterRequest', function(event) { |
| 3 | - | const errorDetail = event.detail; | 3 | + | |
| 4 | - | const errorType = errorDetail && errorDetail.xhr ? errorDetail.xhr.getResponseHeader("X-Error-Type") : 'generic'; // Fallback to 'generic' if not set | 4 | + | // Find the error message element |
| 5 | - | const errorDiv = document.querySelector(`div[data-error-type="${errorType}"]`); | 5 | + | var errorMessageElement = document.getElementById('error-message-img'); |
| 6 | - | if (errorDiv) { | 6 | + | |
| 7 | - | if (errorDetail && errorDetail.xhr && errorDetail.xhr.responseText) { | 7 | + | // Find the image div element by ID (you need to get this ID dynamically) |
| 8 | - | errorDiv.innerHTML = errorDetail.xhr.responseText; | 8 | + | var imageDivID = 'img-' + event.detail.elt.id.replace('img-', ''); |
| 9 | - | } else { | 9 | + | var imageDivElement = document.getElementById(imageDivID); |
| 10 | - | errorDiv.textContent = 'An unknown error occurred.'; | 10 | + | console.log(imageDivElement); |
| 11 | - | } | 11 | + | |
| 12 | - | errorDiv.style.display = 'block'; // Make it visible | 12 | + | // If it's an error, we display and hide the error message |
| 13 | + | if (event.detail.xhr.status !== 200) { | |||
| 14 | + | if (errorMessageElement && errorMessageElement.textContent.trim() !== "") { | |||
| 15 | + | // Set a timeout to hide it | |||
| 16 | + | setTimeout(function() { | |||
| 17 | + | errorMessageElement.style.display = 'none'; // or set it to empty | |||
| 18 | + | errorMessageElement.textContent = ''; // clear the text content | |||
| 19 | + | }, 7000); // 3000 milliseconds = 3 seconds | |||
| 13 | } | 20 | } | ||
| 14 | - | }); | 21 | + | } else { |
| 22 | + | // If it's a success, we hide the image div | |||
| 23 | + | if (imageDivElement) { | |||
| 24 | + | setTimeout(function() { | |||
| 25 | + | imageDivElement.style.display = 'none'; // Hide the div | |||
| 26 | + | }, 3000); // 3000 milliseconds = 3 seconds | |||
| 27 | + | } | |||
| 28 | + | } | |||
| 15 | }); | 29 | }); | ||
A internal/webui/public/libs/htmx-debug.js +11 -0
| @@ -0,0 +1,11 @@ | |||
| 1 | + | htmx.defineExtension('debug', { | |
| 2 | + | onEvent: function (name, evt) { | |
| 3 | + | if (console.debug) { | |
| 4 | + | console.debug(name, evt); | |
| 5 | + | } else if (console) { | |
| 6 | + | console.log("DEBUG:", name, evt); | |
| 7 | + | } else { | |
| 8 | + | throw "NO CONSOLE SUPPORTED" | |
| 9 | + | } | |
| 10 | + | } | |
| 11 | + | }); | |
| @@ -0,0 +1,11 @@ | |||||
| 1 | + | htmx.defineExtension('debug', { | |||
| 2 | + | onEvent: function (name, evt) { | |||
| 3 | + | if (console.debug) { | |||
| 4 | + | console.debug(name, evt); | |||
| 5 | + | } else if (console) { | |||
| 6 | + | console.log("DEBUG:", name, evt); | |||
| 7 | + | } else { | |||
| 8 | + | throw "NO CONSOLE SUPPORTED" | |||
| 9 | + | } | |||
| 10 | + | } | |||
| 11 | + | }); | |||
A internal/webui/public/libs/htmx-response-targets.js +122 -0
| @@ -0,0 +1,122 @@ | |||
| 1 | + | (function(){ | |
| 2 | + | ||
| 3 | + | /** @type {import("../htmx").HtmxInternalApi} */ | |
| 4 | + | var api; | |
| 5 | + | ||
| 6 | + | var attrPrefix = 'hx-target-'; | |
| 7 | + | ||
| 8 | + | /** | |
| 9 | + | * @param {HTMLElement} elt | |
| 10 | + | * @param {number} respCode | |
| 11 | + | * @returns {HTMLElement | null} | |
| 12 | + | */ | |
| 13 | + | function getRespCodeTarget(elt, respCodeNumber) { | |
| 14 | + | if (!elt || !respCodeNumber) return null; | |
| 15 | + | ||
| 16 | + | var respCode = respCodeNumber.toString(); | |
| 17 | + | ||
| 18 | + | // '*' is the original syntax, as the obvious character for a wildcard. | |
| 19 | + | // The 'x' alternative was added for maximum compatibility with HTML | |
| 20 | + | // templating engines, due to ambiguity around which characters are | |
| 21 | + | // supported in HTML attributes. | |
| 22 | + | // | |
| 23 | + | // Start with the most specific possible attribute and generalize from | |
| 24 | + | // there. | |
| 25 | + | var attrPossibilities = [ | |
| 26 | + | respCode, | |
| 27 | + | ||
| 28 | + | respCode.substr(0, 2) + '*', | |
| 29 | + | respCode.substr(0, 2) + 'x', | |
| 30 | + | ||
| 31 | + | respCode.substr(0, 1) + '*', | |
| 32 | + | respCode.substr(0, 1) + 'x', | |
| 33 | + | respCode.substr(0, 1) + '**', | |
| 34 | + | respCode.substr(0, 1) + 'xx', | |
| 35 | + | ||
| 36 | + | '*', | |
| 37 | + | 'x', | |
| 38 | + | '***', | |
| 39 | + | 'xxx', | |
| 40 | + | ]; | |
| 41 | + | ||
| 42 | + | for (var i = 0; i < attrPossibilities.length; i++) { | |
| 43 | + | var attr = attrPrefix + attrPossibilities[i]; | |
| 44 | + | var attrValue = api.getClosestAttributeValue(elt, attr); | |
| 45 | + | if (attrValue) { | |
| 46 | + | if (attrValue === "this") { | |
| 47 | + | return api.findThisElement(elt, attr); | |
| 48 | + | } else { | |
| 49 | + | return api.querySelectorExt(elt, attrValue); | |
| 50 | + | } | |
| 51 | + | } | |
| 52 | + | } | |
| 53 | + | ||
| 54 | + | return null; | |
| 55 | + | } | |
| 56 | + | ||
| 57 | + | /** @param {Event} evt */ | |
| 58 | + | function handleErrorFlag(evt) { | |
| 59 | + | if (evt.detail.isError) { | |
| 60 | + | if (htmx.config.responseTargetUnsetsError) { | |
| 61 | + | evt.detail.isError = false; | |
| 62 | + | } | |
| 63 | + | } else if (htmx.config.responseTargetSetsError) { | |
| 64 | + | evt.detail.isError = true; | |
| 65 | + | } | |
| 66 | + | } | |
| 67 | + | ||
| 68 | + | htmx.defineExtension('response-targets', { | |
| 69 | + | ||
| 70 | + | /** @param {import("../htmx").HtmxInternalApi} apiRef */ | |
| 71 | + | init: function (apiRef) { | |
| 72 | + | api = apiRef; | |
| 73 | + | ||
| 74 | + | if (htmx.config.responseTargetUnsetsError === undefined) { | |
| 75 | + | htmx.config.responseTargetUnsetsError = true; | |
| 76 | + | } | |
| 77 | + | if (htmx.config.responseTargetSetsError === undefined) { | |
| 78 | + | htmx.config.responseTargetSetsError = false; | |
| 79 | + | } | |
| 80 | + | if (htmx.config.responseTargetPrefersExisting === undefined) { | |
| 81 | + | htmx.config.responseTargetPrefersExisting = false; | |
| 82 | + | } | |
| 83 | + | if (htmx.config.responseTargetPrefersRetargetHeader === undefined) { | |
| 84 | + | htmx.config.responseTargetPrefersRetargetHeader = true; | |
| 85 | + | } | |
| 86 | + | }, | |
| 87 | + | ||
| 88 | + | /** | |
| 89 | + | * @param {string} name | |
| 90 | + | * @param {Event} evt | |
| 91 | + | */ | |
| 92 | + | onEvent: function (name, evt) { | |
| 93 | + | if (name === "htmx:beforeSwap" && | |
| 94 | + | evt.detail.xhr && | |
| 95 | + | evt.detail.xhr.status !== 200) { | |
| 96 | + | if (evt.detail.target) { | |
| 97 | + | if (htmx.config.responseTargetPrefersExisting) { | |
| 98 | + | evt.detail.shouldSwap = true; | |
| 99 | + | handleErrorFlag(evt); | |
| 100 | + | return true; | |
| 101 | + | } | |
| 102 | + | if (htmx.config.responseTargetPrefersRetargetHeader && | |
| 103 | + | evt.detail.xhr.getAllResponseHeaders().match(/HX-Retarget:/i)) { | |
| 104 | + | evt.detail.shouldSwap = true; | |
| 105 | + | handleErrorFlag(evt); | |
| 106 | + | return true; | |
| 107 | + | } | |
| 108 | + | } | |
| 109 | + | if (!evt.detail.requestConfig) { | |
| 110 | + | return true; | |
| 111 | + | } | |
| 112 | + | var target = getRespCodeTarget(evt.detail.requestConfig.elt, evt.detail.xhr.status); | |
| 113 | + | if (target) { | |
| 114 | + | handleErrorFlag(evt); | |
| 115 | + | evt.detail.shouldSwap = true; | |
| 116 | + | evt.detail.target = target; | |
| 117 | + | } | |
| 118 | + | return true; | |
| 119 | + | } | |
| 120 | + | } | |
| 121 | + | }); | |
| 122 | + | })(); | |
| @@ -0,0 +1,122 @@ | |||||
| 1 | + | (function(){ | |||
| 2 | + | ||||
| 3 | + | /** @type {import("../htmx").HtmxInternalApi} */ | |||
| 4 | + | var api; | |||
| 5 | + | ||||
| 6 | + | var attrPrefix = 'hx-target-'; | |||
| 7 | + | ||||
| 8 | + | /** | |||
| 9 | + | * @param {HTMLElement} elt | |||
| 10 | + | * @param {number} respCode | |||
| 11 | + | * @returns {HTMLElement | null} | |||
| 12 | + | */ | |||
| 13 | + | function getRespCodeTarget(elt, respCodeNumber) { | |||
| 14 | + | if (!elt || !respCodeNumber) return null; | |||
| 15 | + | ||||
| 16 | + | var respCode = respCodeNumber.toString(); | |||
| 17 | + | ||||
| 18 | + | // '*' is the original syntax, as the obvious character for a wildcard. | |||
| 19 | + | // The 'x' alternative was added for maximum compatibility with HTML | |||
| 20 | + | // templating engines, due to ambiguity around which characters are | |||
| 21 | + | // supported in HTML attributes. | |||
| 22 | + | // | |||
| 23 | + | // Start with the most specific possible attribute and generalize from | |||
| 24 | + | // there. | |||
| 25 | + | var attrPossibilities = [ | |||
| 26 | + | respCode, | |||
| 27 | + | ||||
| 28 | + | respCode.substr(0, 2) + '*', | |||
| 29 | + | respCode.substr(0, 2) + 'x', | |||
| 30 | + | ||||
| 31 | + | respCode.substr(0, 1) + '*', | |||
| 32 | + | respCode.substr(0, 1) + 'x', | |||
| 33 | + | respCode.substr(0, 1) + '**', | |||
| 34 | + | respCode.substr(0, 1) + 'xx', | |||
| 35 | + | ||||
| 36 | + | '*', | |||
| 37 | + | 'x', | |||
| 38 | + | '***', | |||
| 39 | + | 'xxx', | |||
| 40 | + | ]; | |||
| 41 | + | ||||
| 42 | + | for (var i = 0; i < attrPossibilities.length; i++) { | |||
| 43 | + | var attr = attrPrefix + attrPossibilities[i]; | |||
| 44 | + | var attrValue = api.getClosestAttributeValue(elt, attr); | |||
| 45 | + | if (attrValue) { | |||
| 46 | + | if (attrValue === "this") { | |||
| 47 | + | return api.findThisElement(elt, attr); | |||
| 48 | + | } else { | |||
| 49 | + | return api.querySelectorExt(elt, attrValue); | |||
| 50 | + | } | |||
| 51 | + | } | |||
| 52 | + | } | |||
| 53 | + | ||||
| 54 | + | return null; | |||
| 55 | + | } | |||
| 56 | + | ||||
| 57 | + | /** @param {Event} evt */ | |||
| 58 | + | function handleErrorFlag(evt) { | |||
| 59 | + | if (evt.detail.isError) { | |||
| 60 | + | if (htmx.config.responseTargetUnsetsError) { | |||
| 61 | + | evt.detail.isError = false; | |||
| 62 | + | } | |||
| 63 | + | } else if (htmx.config.responseTargetSetsError) { | |||
| 64 | + | evt.detail.isError = true; | |||
| 65 | + | } | |||
| 66 | + | } | |||
| 67 | + | ||||
| 68 | + | htmx.defineExtension('response-targets', { | |||
| 69 | + | ||||
| 70 | + | /** @param {import("../htmx").HtmxInternalApi} apiRef */ | |||
| 71 | + | init: function (apiRef) { | |||
| 72 | + | api = apiRef; | |||
| 73 | + | ||||
| 74 | + | if (htmx.config.responseTargetUnsetsError === undefined) { | |||
| 75 | + | htmx.config.responseTargetUnsetsError = true; | |||
| 76 | + | } | |||
| 77 | + | if (htmx.config.responseTargetSetsError === undefined) { | |||
| 78 | + | htmx.config.responseTargetSetsError = false; | |||
| 79 | + | } | |||
| 80 | + | if (htmx.config.responseTargetPrefersExisting === undefined) { | |||
| 81 | + | htmx.config.responseTargetPrefersExisting = false; | |||
| 82 | + | } | |||
| 83 | + | if (htmx.config.responseTargetPrefersRetargetHeader === undefined) { | |||
| 84 | + | htmx.config.responseTargetPrefersRetargetHeader = true; | |||
| 85 | + | } | |||
| 86 | + | }, | |||
| 87 | + | ||||
| 88 | + | /** | |||
| 89 | + | * @param {string} name | |||
| 90 | + | * @param {Event} evt | |||
| 91 | + | */ | |||
| 92 | + | onEvent: function (name, evt) { | |||
| 93 | + | if (name === "htmx:beforeSwap" && | |||
| 94 | + | evt.detail.xhr && | |||
| 95 | + | evt.detail.xhr.status !== 200) { | |||
| 96 | + | if (evt.detail.target) { | |||
| 97 | + | if (htmx.config.responseTargetPrefersExisting) { | |||
| 98 | + | evt.detail.shouldSwap = true; | |||
| 99 | + | handleErrorFlag(evt); | |||
| 100 | + | return true; | |||
| 101 | + | } | |||
| 102 | + | if (htmx.config.responseTargetPrefersRetargetHeader && | |||
| 103 | + | evt.detail.xhr.getAllResponseHeaders().match(/HX-Retarget:/i)) { | |||
| 104 | + | evt.detail.shouldSwap = true; | |||
| 105 | + | handleErrorFlag(evt); | |||
| 106 | + | return true; | |||
| 107 | + | } | |||
| 108 | + | } | |||
| 109 | + | if (!evt.detail.requestConfig) { | |||
| 110 | + | return true; | |||
| 111 | + | } | |||
| 112 | + | var target = getRespCodeTarget(evt.detail.requestConfig.elt, evt.detail.xhr.status); | |||
| 113 | + | if (target) { | |||
| 114 | + | handleErrorFlag(evt); | |||
| 115 | + | evt.detail.shouldSwap = true; | |||
| 116 | + | evt.detail.target = target; | |||
| 117 | + | } | |||
| 118 | + | return true; | |||
| 119 | + | } | |||
| 120 | + | } | |||
| 121 | + | }); | |||
| 122 | + | })(); | |||