lyrics

lyrics
git clone git://brookjeynes.dev/bjeynes/lyrics.git
Log | Files | Refs

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 }