| 1 | <%
|
|---|
| 2 | // Track editor modal — mobile-first rewrite.
|
|---|
| 3 | //
|
|---|
| 4 | // Visual model:
|
|---|
| 5 | // Mobile (<720px): bottom-sheet that slides up from below, max 92vh,
|
|---|
| 6 | // rounded top corners only, drag-handle bar at top.
|
|---|
| 7 | // Desktop (≥720px): centered modal max-width 520px, fully-rounded.
|
|---|
| 8 | //
|
|---|
| 9 | // Self-contained styling under the .te-* namespace — no dependency on
|
|---|
| 10 | // the .ax-* admin primitives. Same visual language though: paper bg,
|
|---|
| 11 | // rule borders, accent for active states.
|
|---|
| 12 | //
|
|---|
| 13 | // Exposes window.openTrackEditor({ id, onSaved? }) — called by the
|
|---|
| 14 | // "✎ Bewerken" buttons on admin-audio. Fetches /admin/audio/api/:id,
|
|---|
| 15 | // renders the form with current values + an inline <audio> preview,
|
|---|
| 16 | // and PATCHes via /admin/audio/api/:id on save.
|
|---|
| 17 |
|
|---|
| 18 | const _csrf = (typeof csrfToken !== 'undefined' && csrfToken) || '';
|
|---|
| 19 | %>
|
|---|
| 20 |
|
|---|
| 21 | <style>
|
|---|
| 22 | /* ─────────────────────────────────────────────────────────────────
|
|---|
| 23 | Track editor modal — namespace .te-*
|
|---|
| 24 | ───────────────────────────────────────────────────────────────── */
|
|---|
| 25 |
|
|---|
| 26 | .te-backdrop {
|
|---|
| 27 | position: fixed; inset: 0;
|
|---|
| 28 | /* Above bottom-tab nav (z=1050) and audio-player sheet (z=1100) so the
|
|---|
| 29 | editor always wins. */
|
|---|
| 30 | z-index: 9000;
|
|---|
| 31 | background: rgba(0, 0, 0, 0.55);
|
|---|
| 32 | -webkit-backdrop-filter: blur(4px);
|
|---|
| 33 | backdrop-filter: blur(4px);
|
|---|
| 34 | display: flex;
|
|---|
| 35 | /* Mobile: dock to bottom; desktop overrides to center via media query */
|
|---|
| 36 | align-items: flex-end;
|
|---|
| 37 | justify-content: center;
|
|---|
| 38 | animation: te-fade 160ms ease-out;
|
|---|
| 39 | }
|
|---|
| 40 | @keyframes te-fade { from { opacity: 0; } to { opacity: 1; } }
|
|---|
| 41 |
|
|---|
| 42 | .te-modal {
|
|---|
| 43 | background: var(--paper);
|
|---|
| 44 | width: 100%;
|
|---|
| 45 | max-height: 92vh;
|
|---|
| 46 | /* Bottom-sheet shape on mobile — only top corners rounded */
|
|---|
| 47 | border-radius: 16px 16px 0 0;
|
|---|
| 48 | border: 1px solid var(--rule);
|
|---|
| 49 | border-bottom: 0;
|
|---|
| 50 | box-shadow: 0 -8px 30px rgba(0,0,0,0.4);
|
|---|
| 51 | display: flex; flex-direction: column;
|
|---|
| 52 | overflow: hidden;
|
|---|
| 53 | animation: te-slide-up 220ms cubic-bezier(.2,.9,.3,1);
|
|---|
| 54 | }
|
|---|
| 55 | @keyframes te-slide-up {
|
|---|
| 56 | from { transform: translateY(100%); }
|
|---|
| 57 | to { transform: translateY(0); }
|
|---|
| 58 | }
|
|---|
| 59 |
|
|---|
| 60 | /* Desktop: center the modal as a card */
|
|---|
| 61 | @media (min-width: 720px) {
|
|---|
| 62 | .te-backdrop { align-items: center; padding: 1rem; }
|
|---|
| 63 | .te-modal {
|
|---|
| 64 | max-width: 520px;
|
|---|
| 65 | border-radius: 14px;
|
|---|
| 66 | border: 1px solid var(--rule);
|
|---|
| 67 | max-height: calc(100vh - 2rem);
|
|---|
| 68 | box-shadow: 0 20px 60px rgba(0,0,0,0.4);
|
|---|
| 69 | animation: te-fade 220ms ease-out;
|
|---|
| 70 | }
|
|---|
| 71 | }
|
|---|
| 72 |
|
|---|
| 73 | /* Drag-handle bar at top of mobile sheet */
|
|---|
| 74 | .te-handle {
|
|---|
| 75 | width: 100%; padding: 0.6rem 0;
|
|---|
| 76 | display: flex; justify-content: center;
|
|---|
| 77 | flex-shrink: 0;
|
|---|
| 78 | }
|
|---|
| 79 | .te-handle-bar {
|
|---|
| 80 | width: 36px; height: 4px;
|
|---|
| 81 | background: var(--rule);
|
|---|
| 82 | border-radius: 99px;
|
|---|
| 83 | }
|
|---|
| 84 | @media (min-width: 720px) { .te-handle { display: none; } }
|
|---|
| 85 |
|
|---|
| 86 | .te-header {
|
|---|
| 87 | display: flex; align-items: center; justify-content: space-between;
|
|---|
| 88 | padding: 0.5rem 1.25rem 1rem;
|
|---|
| 89 | border-bottom: 1px solid var(--rule);
|
|---|
| 90 | flex-shrink: 0;
|
|---|
| 91 | }
|
|---|
| 92 | @media (min-width: 720px) { .te-header { padding: 1rem 1.25rem; } }
|
|---|
| 93 | .te-header h3 {
|
|---|
| 94 | font-family: var(--font-display, serif);
|
|---|
| 95 | margin: 0; font-size: 1.15rem; font-weight: 600;
|
|---|
| 96 | }
|
|---|
| 97 | .te-close {
|
|---|
| 98 | width: 36px; height: 36px;
|
|---|
| 99 | display: inline-flex; align-items: center; justify-content: center;
|
|---|
| 100 | border: 0; border-radius: 8px;
|
|---|
| 101 | background: transparent;
|
|---|
| 102 | color: var(--ink-muted, var(--ink-soft));
|
|---|
| 103 | font-size: 1.5rem; line-height: 1;
|
|---|
| 104 | cursor: pointer;
|
|---|
| 105 | transition: background 120ms, color 120ms;
|
|---|
| 106 | }
|
|---|
| 107 | .te-close:hover { background: var(--paper-2); color: var(--ink); }
|
|---|
| 108 |
|
|---|
| 109 | .te-body {
|
|---|
| 110 | padding: 1rem 1.25rem;
|
|---|
| 111 | overflow-y: auto;
|
|---|
| 112 | flex: 1;
|
|---|
| 113 | -webkit-overflow-scrolling: touch;
|
|---|
| 114 | display: flex; flex-direction: column; gap: 1rem;
|
|---|
| 115 | }
|
|---|
| 116 |
|
|---|
| 117 | /* ── Inline preview player ─────────────────────────────────── */
|
|---|
| 118 | .te-preview {
|
|---|
| 119 | display: flex; align-items: center; gap: 0.75rem;
|
|---|
| 120 | padding: 0.75rem;
|
|---|
| 121 | background: var(--paper-2);
|
|---|
| 122 | border: 1px solid var(--rule);
|
|---|
| 123 | border-radius: 10px;
|
|---|
| 124 | }
|
|---|
| 125 | .te-preview-cover {
|
|---|
| 126 | width: 56px; height: 56px;
|
|---|
| 127 | border-radius: 8px; overflow: hidden;
|
|---|
| 128 | background: var(--paper);
|
|---|
| 129 | display: inline-flex; align-items: center; justify-content: center;
|
|---|
| 130 | flex-shrink: 0;
|
|---|
| 131 | font-size: 1.4rem; color: var(--accent);
|
|---|
| 132 | }
|
|---|
| 133 | .te-preview-cover img { width: 100%; height: 100%; object-fit: cover; }
|
|---|
| 134 | .te-preview-meta {
|
|---|
| 135 | flex: 1; min-width: 0;
|
|---|
| 136 | display: flex; flex-direction: column; gap: 0.25rem;
|
|---|
| 137 | }
|
|---|
| 138 | .te-preview-title {
|
|---|
| 139 | font-weight: 600; font-size: 0.95rem;
|
|---|
| 140 | overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|---|
| 141 | }
|
|---|
| 142 | .te-preview-sub {
|
|---|
| 143 | font-size: 0.78rem;
|
|---|
| 144 | color: var(--ink-muted, var(--ink-soft));
|
|---|
| 145 | }
|
|---|
| 146 | .te-preview-play {
|
|---|
| 147 | width: 44px; height: 44px;
|
|---|
| 148 | border: 1px solid var(--rule);
|
|---|
| 149 | border-radius: 50%;
|
|---|
| 150 | background: var(--paper);
|
|---|
| 151 | color: var(--ink);
|
|---|
| 152 | font-size: 1.1rem;
|
|---|
| 153 | cursor: pointer;
|
|---|
| 154 | display: inline-flex; align-items: center; justify-content: center;
|
|---|
| 155 | flex-shrink: 0;
|
|---|
| 156 | transition: border-color 120ms, color 120ms, background 120ms;
|
|---|
| 157 | }
|
|---|
| 158 | .te-preview-play:hover { border-color: var(--accent); color: var(--accent); }
|
|---|
| 159 | .te-preview-play.is-playing {
|
|---|
| 160 | background: var(--accent); color: white; border-color: var(--accent);
|
|---|
| 161 | }
|
|---|
| 162 |
|
|---|
| 163 | /* ── Form fields ───────────────────────────────────────────── */
|
|---|
| 164 | .te-form { display: flex; flex-direction: column; gap: 0.85rem; }
|
|---|
| 165 | .te-row { display: grid; gap: 0.85rem; }
|
|---|
| 166 | .te-row-2 { grid-template-columns: 1fr 1fr; }
|
|---|
| 167 | @media (max-width: 480px) { .te-row-2 { grid-template-columns: 1fr; } }
|
|---|
| 168 |
|
|---|
| 169 | .te-field { display: flex; flex-direction: column; gap: 0.35rem; min-width: 0; }
|
|---|
| 170 | .te-field > span {
|
|---|
| 171 | font-size: 0.8rem; font-weight: 600;
|
|---|
| 172 | color: var(--ink-soft);
|
|---|
| 173 | display: flex; align-items: baseline; gap: 0.4rem;
|
|---|
| 174 | }
|
|---|
| 175 | .te-field > span small {
|
|---|
| 176 | font-weight: 400; color: var(--ink-muted); font-size: 0.95em;
|
|---|
| 177 | }
|
|---|
| 178 | .te-required { color: #dc2626; }
|
|---|
| 179 | .te-field input[type="text"],
|
|---|
| 180 | .te-field input[type="number"],
|
|---|
| 181 | .te-field input[type="url"] {
|
|---|
| 182 | width: 100%; box-sizing: border-box;
|
|---|
| 183 | padding: 0.6rem 0.75rem; min-height: 44px;
|
|---|
| 184 | border: 1px solid var(--rule); border-radius: 6px;
|
|---|
| 185 | background: var(--paper-2); color: var(--ink);
|
|---|
| 186 | font-family: var(--font-ui, system-ui), sans-serif;
|
|---|
| 187 | font-size: 0.95rem;
|
|---|
| 188 | -webkit-appearance: none; appearance: none;
|
|---|
| 189 | transition: border-color 120ms;
|
|---|
| 190 | }
|
|---|
| 191 | .te-field input:focus {
|
|---|
| 192 | outline: 2px solid var(--accent);
|
|---|
| 193 | outline-offset: -1px;
|
|---|
| 194 | border-color: var(--accent);
|
|---|
| 195 | }
|
|---|
| 196 |
|
|---|
| 197 | /* ── Credit field with © insert button ──────────────────────────── */
|
|---|
| 198 | .te-credit-row { display: flex; gap: 0.4rem; align-items: stretch; }
|
|---|
| 199 | .te-credit-row input { flex: 1; min-width: 0; }
|
|---|
| 200 | .te-sym-btn {
|
|---|
| 201 | flex: 0 0 auto;
|
|---|
| 202 | width: 44px; min-height: 44px;
|
|---|
| 203 | border: 1px solid var(--rule); border-radius: 6px;
|
|---|
| 204 | background: var(--paper-2); color: var(--ink);
|
|---|
| 205 | font-size: 1.1rem; cursor: pointer;
|
|---|
| 206 | transition: border-color 120ms, background 120ms;
|
|---|
| 207 | }
|
|---|
| 208 | .te-sym-btn:hover { border-color: var(--accent); }
|
|---|
| 209 |
|
|---|
| 210 | /* ── Cover field with thumb + button + URL ─────────────────── */
|
|---|
| 211 | .te-cover-row {
|
|---|
| 212 | display: grid;
|
|---|
| 213 | grid-template-columns: 80px 1fr;
|
|---|
| 214 | gap: 0.75rem;
|
|---|
| 215 | align-items: start;
|
|---|
| 216 | }
|
|---|
| 217 | .te-cover-thumb {
|
|---|
| 218 | width: 80px; height: 80px;
|
|---|
| 219 | border-radius: 10px;
|
|---|
| 220 | border: 1px dashed var(--rule);
|
|---|
| 221 | background: var(--paper-2);
|
|---|
| 222 | display: flex; align-items: center; justify-content: center;
|
|---|
| 223 | overflow: hidden;
|
|---|
| 224 | cursor: pointer;
|
|---|
| 225 | transition: border-color 120ms;
|
|---|
| 226 | }
|
|---|
| 227 | .te-cover-thumb:hover, .te-cover-thumb.is-dragover { border-color: var(--accent); border-style: solid; }
|
|---|
| 228 | .te-cover-thumb img { width: 100%; height: 100%; object-fit: cover; }
|
|---|
| 229 | .te-cover-empty {
|
|---|
| 230 | font-size: 1.6rem; opacity: 0.5;
|
|---|
| 231 | }
|
|---|
| 232 | .te-cover-actions {
|
|---|
| 233 | display: flex; flex-direction: column; gap: 0.5rem;
|
|---|
| 234 | min-width: 0;
|
|---|
| 235 | }
|
|---|
| 236 | .te-cover-btn-row { display: flex; gap: 0.4rem; flex-wrap: wrap; }
|
|---|
| 237 | .te-cover-btn {
|
|---|
| 238 | display: inline-flex; align-items: center; gap: 0.35rem;
|
|---|
| 239 | padding: 0.45rem 0.75rem;
|
|---|
| 240 | border: 1px solid var(--rule); border-radius: 6px;
|
|---|
| 241 | background: var(--paper-2); color: var(--ink);
|
|---|
| 242 | font-family: var(--font-ui, system-ui); font-size: 0.85rem;
|
|---|
| 243 | cursor: pointer;
|
|---|
| 244 | transition: border-color 120ms;
|
|---|
| 245 | }
|
|---|
| 246 | .te-cover-btn:hover { border-color: var(--accent); }
|
|---|
| 247 | .te-cover-btn-remove:hover { color: #dc2626; border-color: #dc2626; }
|
|---|
| 248 | .te-cover-status {
|
|---|
| 249 | font-size: 0.78rem;
|
|---|
| 250 | color: var(--ink-muted, var(--ink-soft));
|
|---|
| 251 | min-height: 1em;
|
|---|
| 252 | }
|
|---|
| 253 | .te-cover-status.is-error { color: #dc2626; }
|
|---|
| 254 | .te-cover-status.is-ok { color: #2a9d5e; }
|
|---|
| 255 |
|
|---|
| 256 | /* ── Footer (sticky) ───────────────────────────────────────── */
|
|---|
| 257 | .te-footer {
|
|---|
| 258 | display: flex; align-items: center; gap: 0.5rem;
|
|---|
| 259 | padding: 0.85rem 1.25rem;
|
|---|
| 260 | border-top: 1px solid var(--rule);
|
|---|
| 261 | background: var(--paper);
|
|---|
| 262 | flex-shrink: 0;
|
|---|
| 263 | }
|
|---|
| 264 | .te-footer-spacer { flex: 1; }
|
|---|
| 265 | .te-btn {
|
|---|
| 266 | display: inline-flex; align-items: center; justify-content: center; gap: 0.35rem;
|
|---|
| 267 | padding: 0.6rem 1.1rem; min-height: 44px;
|
|---|
| 268 | border: 1px solid var(--rule); border-radius: 8px;
|
|---|
| 269 | background: var(--paper-2); color: var(--ink);
|
|---|
| 270 | font-family: var(--font-ui, system-ui); font-size: 0.9rem; font-weight: 500;
|
|---|
| 271 | cursor: pointer;
|
|---|
| 272 | transition: border-color 120ms, background 120ms;
|
|---|
| 273 | -webkit-tap-highlight-color: transparent;
|
|---|
| 274 | }
|
|---|
| 275 | .te-btn:hover { border-color: var(--accent); }
|
|---|
| 276 | .te-btn:disabled { opacity: 0.5; cursor: not-allowed; }
|
|---|
| 277 | .te-btn-primary {
|
|---|
| 278 | background: var(--accent); color: white; border-color: var(--accent);
|
|---|
| 279 | }
|
|---|
| 280 | .te-btn-primary:hover { opacity: 0.92; }
|
|---|
| 281 |
|
|---|
| 282 | /* Lock body scroll while modal is open */
|
|---|
| 283 | body.te-modal-open { overflow: hidden; }
|
|---|
| 284 | </style>
|
|---|
| 285 |
|
|---|
| 286 | <%# Het script van deze pagina staat in assets/js/mod/track-editor.js (shaer-bqr). %>
|
|---|