new-song.templ (9304B)
1 package newsong 2 3 import ( 4 "fmt" 5 "lyrics/internal/domain" 6 "lyrics/internal/server/ui/layouts/base" 7 "time" 8 ) 9 10 templ NewSong(params NewSongParams) { 11 @base.Base(base.BaseParams{Title: "new song", PageStylesheets: []string{"new-song.css"}}) { 12 <h1>Add a song</h1> 13 <form 14 class="song-form" 15 hx-post="/song/new" 16 hx-swap="none" 17 hx-disable-elt="#add-song-btn" 18 > 19 <fieldset x-data="{ showLatinTitle: false }"> 20 <legend>Title</legend> 21 <label> 22 title: 23 <input 24 name="song-title" 25 type="text" 26 pattern={ domain.NoTrailingOrLeadingSpaceRegex.String() } 27 required 28 @input="showLatinTitle = isNonAscii($event.target.value)" 29 /> 30 </label> 31 <label x-show="showLatinTitle"> 32 latin name: 33 <input 34 name="latin-title" 35 type="text" 36 :required="showLatinTitle" 37 pattern={ domain.IsValidSortNamePattern } 38 /> 39 </label> 40 </fieldset> 41 <fieldset 42 x-data="{ items: [{ id: 0, selected: false }], uid: 1, createAsSingle: false }" 43 > 44 <legend>Releases</legend> 45 <div class="releases-list-group"> 46 <ol 47 class="picker-list" 48 :class="createAsSingle && 'disabled-form'" 49 > 50 <template x-for="(item, i) in items" :key="item.id"> 51 <li 52 x-data="{ releaseId: '', releaseTitle: '' }" 53 x-init="htmx.process($el); initLucideIcons()" 54 > 55 <div class="picker-row"> 56 <div class="picker-main"> 57 <label x-show="!item.selected"> 58 <span>release:</span> 59 <input 60 type="search" 61 hx-get="/release/search?format=suggest" 62 hx-trigger="input changed delay:500ms" 63 hx-target="next .picker-options" 64 hx-swap="outerHTML" 65 name="release-title" 66 autocomplete="off" 67 :required="!item.selected && !createAsSingle" 68 :disabled="createAsSingle" 69 /> 70 </label> 71 <div class="picker-selected-item" x-show="item.selected"> 72 <strong x-text="releaseTitle"></strong> 73 <button 74 type="button" 75 class="-secondary" 76 @click="item.selected = false; releaseId = ''; releaseTitle = ''" 77 > 78 change 79 </button> 80 </div> 81 </div> 82 <div class="picker-actions"> 83 <button 84 type="button" 85 aria-label="Remove" 86 x-show="items.length > 1" 87 @click="items.splice(i, 1)" 88 > 89 <i data-lucide="x"></i> 90 </button> 91 </div> 92 </div> 93 <div x-show="!item.selected"> 94 <fieldset class="picker-options" role="radiogroup" x-show="false"></fieldset> 95 </div> 96 <input type="hidden" name="release-id" :value="releaseId"/> 97 </li> 98 </template> 99 </ol> 100 <button 101 class="-secondary" 102 type="button" 103 @click="items.push({ id: uid++, selected: false })" 104 :disabled="createAsSingle" 105 > 106 add additional release 107 </button> 108 </div> 109 <label 110 :class="items.find(item => item.selected) !== undefined && 'disabled-form'" 111 > 112 Create as single? 113 <input 114 name="create-as-single" 115 type="checkbox" 116 x-model="createAsSingle" 117 :disabled="items.find(item => item.selected) !== undefined" 118 /> 119 </label> 120 </fieldset> 121 <fieldset x-data="{ items: [{ id: 0 }], uid: 1 }"> 122 <legend>Artists</legend> 123 <ol class="picker-list"> 124 <template x-for="(item, i) in items" :key="item.id"> 125 <li 126 x-data="{ selected: false, artistId: '', artistName: '' }" 127 x-init="htmx.process($el); initLucideIcons()" 128 > 129 <div class="picker-row"> 130 <div class="picker-main"> 131 <label x-show="!selected"> 132 <span>artist:</span> 133 <input 134 type="search" 135 hx-get="/artist/search?format=suggest" 136 hx-trigger="input changed delay:500ms" 137 hx-target="next .picker-options" 138 hx-swap="outerHTML" 139 name="artist" 140 autocomplete="off" 141 :required="!selected" 142 /> 143 </label> 144 <div class="picker-selected-item" x-show="selected"> 145 <strong x-text="artistName"></strong> 146 <button 147 type="button" 148 class="-secondary" 149 @click="selected = false; artistId = ''; artistName = ''" 150 > 151 change 152 </button> 153 </div> 154 </div> 155 <div class="picker-actions"> 156 <button 157 type="button" 158 aria-label="Remove" 159 x-show="items.length > 1" 160 @click="items.splice(i, 1)" 161 > 162 <i data-lucide="x"></i> 163 </button> 164 <button 165 type="button" 166 aria-label="Move up" 167 x-show="selected && items.length > 1 && i >= 1" 168 @click="[items[i - 1], items[i]] = [items[i], items[i - 1]]" 169 > 170 <i data-lucide="chevron-up"></i> 171 </button> 172 <button 173 type="button" 174 aria-label="Move down" 175 x-show="selected && items.length > 1 && i + 1 < items.length" 176 @click="[items[i + 1], items[i]] = [items[i], items[i + 1]]" 177 > 178 <i data-lucide="chevron-down"></i> 179 </button> 180 </div> 181 </div> 182 <div x-show="!selected"> 183 <fieldset class="picker-options" role="radiogroup" x-show="false"></fieldset> 184 </div> 185 <input type="hidden" name="artist-id" :value="artistId"/> 186 </li> 187 </template> 188 </ol> 189 <button class="-secondary" type="button" @click="items.push({ id: uid++ })"> 190 add additional artist 191 </button> 192 </fieldset> 193 <fieldset x-data="{ items: [{ id: 0 }], uid: 1 }"> 194 <legend>Featuring</legend> 195 <ol class="picker-list"> 196 <template x-for="(item, i) in items" :key="item.id"> 197 <li 198 x-data="{ selected: false, artistId: '', artistName: '' }" 199 x-init="htmx.process($el); initLucideIcons()" 200 > 201 <div class="picker-row"> 202 <div class="picker-main"> 203 <label x-show="!selected"> 204 <span>artist:</span> 205 <input 206 type="search" 207 hx-get="/artist/search?format=suggest" 208 hx-trigger="input changed delay:500ms" 209 hx-target="next .picker-options" 210 hx-swap="outerHTML" 211 name="artist" 212 autocomplete="off" 213 /> 214 </label> 215 <div class="picker-selected-item" x-show="selected"> 216 <strong x-text="artistName"></strong> 217 <button 218 type="button" 219 class="-secondary" 220 @click="selected = false; artistId = ''; artistName = ''" 221 > 222 change 223 </button> 224 </div> 225 </div> 226 <div class="picker-actions"> 227 <button 228 type="button" 229 aria-label="Remove" 230 x-show="items.length > 1" 231 @click="items.splice(i, 1)" 232 > 233 <i data-lucide="x"></i> 234 </button> 235 <button 236 type="button" 237 aria-label="Move up" 238 x-show="selected && items.length > 1 && i >= 1" 239 @click="[items[i - 1], items[i]] = [items[i], items[i - 1]]" 240 > 241 <i data-lucide="chevron-up"></i> 242 </button> 243 <button 244 type="button" 245 aria-label="Move down" 246 x-show="selected && items.length > 1 && i + 1 < items.length" 247 @click="[items[i + 1], items[i]] = [items[i], items[i + 1]]" 248 > 249 <i data-lucide="chevron-down"></i> 250 </button> 251 </div> 252 </div> 253 <div x-show="!selected"> 254 <fieldset class="picker-options" role="radiogroup" x-show="false"></fieldset> 255 </div> 256 <input type="hidden" name="featuring-id" :value="artistId"/> 257 </li> 258 </template> 259 </ol> 260 <button class="-secondary" type="button" @click="items.push({ id: uid++ })"> 261 add additional artist 262 </button> 263 </fieldset> 264 <fieldset> 265 <legend>Genres</legend> 266 <div class="genre-options"> 267 for _, genre := range domain.Genres { 268 <label> 269 <input type="checkbox" name="genres" value={ string(genre) }/> 270 <span>{ string(genre) }</span> 271 </label> 272 } 273 </div> 274 </fieldset> 275 <fieldset> 276 <legend>Lyrics</legend> 277 <label> 278 lyrics: 279 <textarea name="lyrics" rows="16" spellcheck></textarea> 280 </label> 281 </fieldset> 282 <fieldset> 283 <legend>Release</legend> 284 <label> 285 release date: 286 <input 287 name="release-date" 288 type="date" 289 min="1000-01-01" 290 max={ fmt.Sprintf("%s", time.Now().AddDate(3, 0, 0).Format("2006-01-02")) } 291 required 292 /> 293 </label> 294 </fieldset> 295 <fieldset> 296 <legend>MusicBrainz</legend> 297 <label title="External identifier used for metadata lookups such as cover art."> 298 Recording ID (optional): 299 <input 300 name="musicbrainz-recording-id" 301 type="text" 302 pattern={ domain.IsValidMusicBrainzIDPattern } 303 placeholder="e32a4ae3-67d1-4541-84d8-de6ab2d7dec1" 304 /> 305 <i data-lucide="info"></i> 306 </label> 307 </fieldset> 308 <footer aria-label="Form actions"> 309 <button id="add-song-btn" class="-primary" type="submit">add song</button> 310 </footer> 311 </form> 312 <script src="/static/js/new-song.js" defer></script> 313 } 314 }